diff --git a/.artifact/apidoc.html b/.artifact/apidoc.html new file mode 100644 index 000000000..c6101547c --- /dev/null +++ b/.artifact/apidoc.html @@ -0,0 +1,2076 @@ + + + + + + +JSLint apidoc + + + +
+

API Doc for JSLint (v2024.11.24)

+
+ +

Table of Contents

+
+ +
+ +
+

Module "./jslint.mjs"

+ +
+ +
+ [ + This document was created with + JSLint + ] +
+
+ + diff --git a/.artifact/asset_image_logo_128.png b/.artifact/asset_image_logo_128.png new file mode 100644 index 000000000..f9a632942 Binary files /dev/null and b/.artifact/asset_image_logo_128.png differ diff --git a/.artifact/asset_image_logo_256.png b/.artifact/asset_image_logo_256.png new file mode 100644 index 000000000..df67d1dce Binary files /dev/null and b/.artifact/asset_image_logo_256.png differ diff --git a/.artifact/asset_image_logo_32.png b/.artifact/asset_image_logo_32.png new file mode 100644 index 000000000..6308fbc6a Binary files /dev/null and b/.artifact/asset_image_logo_32.png differ diff --git a/.artifact/asset_image_logo_512.png b/.artifact/asset_image_logo_512.png new file mode 100644 index 000000000..585908e3a Binary files /dev/null and b/.artifact/asset_image_logo_512.png differ diff --git a/.artifact/asset_image_logo_64.png b/.artifact/asset_image_logo_64.png new file mode 100644 index 000000000..c70d5aa49 Binary files /dev/null and b/.artifact/asset_image_logo_64.png differ diff --git a/.artifact/coverage/coverage_badge.svg b/.artifact/coverage/coverage_badge.svg new file mode 100644 index 000000000..5e137a1ce --- /dev/null +++ b/.artifact/coverage/coverage_badge.svg @@ -0,0 +1,14 @@ + + + + +coverage +100.00 % + + diff --git a/.artifact/coverage/coverage_report.txt b/.artifact/coverage/coverage_report.txt new file mode 100644 index 000000000..18fd858a1 --- /dev/null +++ b/.artifact/coverage/coverage_report.txt @@ -0,0 +1,16 @@ +V8 Coverage Report ++----------------------------------+-------------------+-------------------+ +| Files covered | Lines | Remaining | ++----------------------------------+-------------------+-------------------+ +| ./ | 100.00 % | | +| ******************************** | 13199 / 13199 | 0 / 13199 | ++----------------------------------+-------------------+-------------------+ +| ./jslint.mjs | 100.00 % | | +| ******************************** | 11574 / 11574 | 0 / 11574 | ++----------------------------------+-------------------+-------------------+ +| ./jslint_wrapper_cjs.cjs | 100.00 % | | +| ******************************** | 49 / 49 | 0 / 49 | ++----------------------------------+-------------------+-------------------+ +| ./test.mjs | 100.00 % | | +| ******************************** | 1576 / 1576 | 0 / 1576 | ++----------------------------------+-------------------+-------------------+ diff --git a/.artifact/coverage/index.html b/.artifact/coverage/index.html new file mode 100644 index 000000000..38a80ec05 --- /dev/null +++ b/.artifact/coverage/index.html @@ -0,0 +1,212 @@ + + + +V8 Coverage Report + + + + +
+
V8 Coverage Report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Files coveredLinesRemaining
+ . /
+
+
+
+
+ 100.00 %
+ 13199 / 13199 +
+
+ 0 / 13199 +
+ . / jslint.mjs
+
+
+
+
+ 100.00 %
+ 11574 / 11574 +
+
+ 0 / 11574 +
+ . / jslint_wrapper_cjs.cjs
+
+
+
+
+ 100.00 %
+ 49 / 49 +
+
+ 0 / 49 +
+ . / test.mjs
+
+
+
+
+ 100.00 %
+ 1576 / 1576 +
+
+ 0 / 1576 +
+
+ + + + diff --git a/.artifact/coverage/jslint.mjs.html b/.artifact/coverage/jslint.mjs.html new file mode 100644 index 000000000..a140d315f --- /dev/null +++ b/.artifact/coverage/jslint.mjs.html @@ -0,0 +1,11742 @@ + + + +V8 Coverage Report + + + + +
+
V8 Coverage Report
+ + + + + + + + + + + + + + + +
Files coveredLinesRemaining
+ . / jslint.mjs
+
+
+
+
+ 100.00 %
+ 11574 / 11574 +
+
+ 0 / 11574 +
+
+ + +
+
    1.      1// #!/usr/bin/env node
+
    2.      1// JSLint
+
    3.      1
+
    4.      1// The Unlicense
+
    5.      1//
+
    6.      1// This is free and unencumbered software released into the public domain.
+
    7.      1//
+
    8.      1// Anyone is free to copy, modify, publish, use, compile, sell, or
+
    9.      1// distribute this software, either in source code form or as a compiled
+
   10.      1// binary, for any purpose, commercial or non-commercial, and by any
+
   11.      1// means.
+
   12.      1//
+
   13.      1// In jurisdictions that recognize copyright laws, the author or authors
+
   14.      1// of this software dedicate any and all copyright interest in the
+
   15.      1// software to the public domain. We make this dedication for the benefit
+
   16.      1// of the public at large and to the detriment of our heirs and
+
   17.      1// successors. We intend this dedication to be an overt act of
+
   18.      1// relinquishment in perpetuity of all present and future rights to this
+
   19.      1// software under copyright law.
+
   20.      1//
+
   21.      1// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+
   22.      1// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+
   23.      1// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+
   24.      1// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+
   25.      1// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+
   26.      1// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+
   27.      1// OTHER DEALINGS IN THE SOFTWARE.
+
   28.      1
+
   29.      1
+
   30.      1// jslint(source, option_dict, global_list) is a function that takes 3
+
   31.      1// arguments. The second two arguments are optional.
+
   32.      1
+
   33.      1//      source          A text to analyze.
+
   34.      1//      option_dict     An object whose keys correspond to option names.
+
   35.      1//      global_list     An array of strings containing global variables that
+
   36.      1//                      the file is allowed readonly access.
+
   37.      1
+
   38.      1// jslint returns an object containing its results. The object contains a lot
+
   39.      1// of valuable information. It can be used to generate reports. The object
+
   40.      1// contains:
+
   41.      1
+
   42.      1//      directives: an array of directive comment tokens.
+
   43.      1//      edition: the version of JSLint that did the analysis.
+
   44.      1//      exports: the names exported from the module.
+
   45.      1//      froms: an array of strings representing each of the imports.
+
   46.      1//      functions: an array of objects that represent all functions
+
   47.      1//              declared in the file.
+
   48.      1//      global: an object representing the global object. Its .context property
+
   49.      1//              is an object containing a property for each global variable.
+
   50.      1//      id: "(JSLint)"
+
   51.      1//      json: true if the file is a JSON text.
+
   52.      1//      lines: an array of strings, the source.
+
   53.      1//      module: true if an import or export statement was used.
+
   54.      1//      ok: true if no warnings were generated. This is what you want.
+
   55.      1//      option: the option argument.
+
   56.      1//      property: a property object.
+
   57.      1//      stop: true if JSLint was unable to finish. You don't want this.
+
   58.      1//      tokens: an array of objects representing the tokens in the file.
+
   59.      1//      tree: the token objects arranged in a tree.
+
   60.      1//      warnings: an array of warning objects. A warning object can contain:
+
   61.      1//          name: "JSLintError"
+
   62.      1//          column: A column number in the file.
+
   63.      1//          line: A line number in the file.
+
   64.      1//          code: A warning code string.
+
   65.      1//          message: The warning message string.
+
   66.      1//          a: Exhibit A.
+
   67.      1//          b: Exhibit B.
+
   68.      1//          c: Exhibit C.
+
   69.      1//          d: Exhibit D.
+
   70.      1
+
   71.      1// jslint works in several phases. In any of these phases, errors might be
+
   72.      1// found. Sometimes JSLint is able to recover from an error and continue
+
   73.      1// parsing. In some cases, it cannot and will stop early. If that should happen,
+
   74.      1// repair your code and try again.
+
   75.      1
+
   76.      1// Phases:
+
   77.      1
+
   78.      1// PHASE 1. Split <source> by newlines into <line_list>.
+
   79.      1// PHASE 2. Lex <line_list> into <token_list>.
+
   80.      1// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
+
   81.      1// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
+
   82.      1//          recursive traversal. Each node may be processed on the way down
+
   83.      1//          (preaction) and on the way up (postaction).
+
   84.      1// PHASE 5. Check whitespace between tokens in <token_list>.
+
   85.      1
+
   86.      1// jslint can also examine JSON text. It decides that a file is JSON text if
+
   87.      1// the first token is "[" or "{". Processing of JSON text is much simpler than
+
   88.      1// the processing of JavaScript programs. Only the first three phases are
+
   89.      1// required.
+
   90.      1
+
   91.      1// WARNING: JSLint will hurt your feelings.
+
   92.      1
+
   93.      1/*jslint beta, node*/
+
   94.      1/*property
+
   95.      1    JSLINT_BETA, NODE_V8_COVERAGE, a, all, argv, arity, artifact,
+
   96.      1    assertErrorThrownAsync, assertJsonEqual, assertOrThrow, assign, async, b,
+
   97.      1    beta, bitwise, block, body, browser, c, calls, catch, catch_list,
+
   98.      1    catch_stack, causes, char, children, clear, closer, closure, code, column,
+
   99.      1    concat, consoleError, console_error, console_log, constant, context,
+
  100.      1    convert, count, coverageDir, create, cwd, d, dead, debugInline, default,
+
  101.      1    delta, devel, directive, directive_ignore_line, directive_list, directives,
+
  102.      1    dirname, disrupt, dot, edition, elem_list, ellipsis, else, end, endOffset,
+
  103.      1    endsWith, entries, env, error, eval, every, example_list, excludeList, exec,
+
  104.      1    execArgv, exit, exitCode, export_dict, exports, expression, extra, fart,
+
  105.      1    file, fileList, fileURLToPath, filter, finally, flag, floor, for, forEach,
+
  106.      1    formatted_message, free, freeze, from, froms, fsWriteFileWithParents,
+
  107.      1    fud_stmt, functionName, function_list, function_stack, functions, get,
+
  108.      1    getset, github_repo, globExclude, global, global_dict, global_list,
+
  109.      1    holeList, htmlEscape, id, identifier, import, import_list, import_meta_url,
+
  110.      1    inc, includeList, indent2, index, indexOf, init, initial, isArray,
+
  111.      1    isBlockCoverage, isHole, isNaN, is_equal, is_weird, join, jslint,
+
  112.      1    jslint_apidoc, jslint_assert, jslint_charset_ascii, jslint_cli,
+
  113.      1    jslint_edition, jslint_phase1_split, jslint_phase2_lex, jslint_phase3_parse,
+
  114.      1    jslint_phase4_walk, jslint_phase5_whitage, jslint_report, json,
+
  115.      1    jstestDescribe, jstestIt, jstestOnExit, keys, label, lbp, led_infix, length,
+
  116.      1    level, line, lineList, line_list, line_offset, line_source, lines,
+
  117.      1    linesCovered, linesTotal, live, log, long, loop, m, map, margin, match, max,
+
  118.      1    message, meta, min, mkdir, modeCoverageIgnoreFile, modeIndex, mode_cli,
+
  119.      1    mode_conditional, mode_json, mode_module, mode_noop, mode_property,
+
  120.      1    mode_shebang, mode_stop, module, moduleFsInit, moduleName, module_list,
+
  121.      1    name, names, node, nomen, noop, now, nr, nud_prefix,
+
  122.      1    objectDeepCopyWithKeysSorted, ok, on, open, opening, option, option_dict,
+
  123.      1    order, package_name, padEnd, padStart, parameters, parent, parentIi, parse,
+
  124.      1    pathname, pathnameList, platform, pop, processArgv, process_argv,
+
  125.      1    process_env, process_exit, promises, property, property_dict, push, quote,
+
  126.      1    ranges, readFile, readdir, readonly, recursive, reduce, repeat, replace,
+
  127.      1    resolve, result, reverse, role, round, scriptId, search, set, shebang,
+
  128.      1    shell, shift, signature, single, slice, some, sort, source, spawn, splice,
+
  129.      1    split, stack, stack_trace, start, startOffset, startsWith, statement,
+
  130.      1    statement_prv, stdio, stop, stop_at, stringify, subscript, switch,
+
  131.      1    syntax_dict, tenure, test, test_cause, test_internal_error, this, thru,
+
  132.      1    toLocaleString, toString, token, token_global, token_list, token_nxt,
+
  133.      1    token_tree, tokens, trace, tree, trim, trimEnd, trimRight, try, type,
+
  134.      1    unlink, unordered, unshift, url, used, v8CoverageListMerge,
+
  135.      1    v8CoverageReportCreate, value, variable, version, versions, warn, warn_at,
+
  136.      1    warning, warning_list, warnings, white, wrapped, writeFile
+
  137.      1*/
+
  138.      1
+
  139.      1// init debugInline
+
  140.      1let debugInline = (function () {
+
  141.      3    let __consoleError = function () {
+
  142.      3        return;
+
  143.      3    };
+
  144.      1    function debug(...argv) {
+
  145.      1
+
  146.      1// This function will print <argv> to stderr and then return <argv>[0].
+
  147.      1
+
  148.      1        __consoleError("\n\ndebugInline");
+
  149.      1        __consoleError(...argv);
+
  150.      1        __consoleError("\n");
+
  151.      1        return argv[0];
+
  152.      1    }
+
  153.      1    debug(); // Coverage-hack.
+
  154.      1    __consoleError = console.error; //jslint-ignore-line
+
  155.      1    return debug;
+
  156.      1}());
+
  157.      1let jslint_charset_ascii = (
+
  158.      1    "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007"
+
  159.      1    + "\b\t\n\u000b\f\r\u000e\u000f"
+
  160.      1    + "\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017"
+
  161.      1    + "\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f"
+
  162.      1    + " !\"#$%&'()*+,-./0123456789:;<=>?"
+
  163.      1    + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
+
  164.      1    + "`abcdefghijklmnopqrstuvwxyz{|}~\u007f"
+
  165.      1);
+
  166.      1let jslint_edition = "v2024.11.24";
+
  167.      1let jslint_export;                      // The jslint object to be exported.
+
  168.      1let jslint_fudge = 1;                   // Fudge starting line and starting
+
  169.      1                                        // ... column to 1.
+
  170.      1let jslint_import_meta_url = "";        // import.meta.url used by cli.
+
  171.      1let jslint_rgx_cap = (
+
  172.      1    /^[A-Z]/
+
  173.      1);
+
  174.      1let jslint_rgx_crlf = (
+
  175.      1    /\n|\r\n?/
+
  176.      1);
+
  177.      1let jslint_rgx_digits_bits = (
+
  178.      1    /^[01_]*/
+
  179.      1);
+
  180.      1let jslint_rgx_digits_decimals = (
+
  181.      1    /^[0-9_]*/
+
  182.      1);
+
  183.      1let jslint_rgx_digits_hexs = (
+
  184.      1    /^[0-9A-F_]*/i
+
  185.      1);
+
  186.      1let jslint_rgx_digits_octals = (
+
  187.      1    /^[0-7_]*/
+
  188.      1);
+
  189.      1let jslint_rgx_directive = (
+
  190.      1    /^(jslint|property|global)\s+(.*)$/
+
  191.      1);
+
  192.      1let jslint_rgx_directive_part = (
+
  193.      1    /([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*|$/g
+
  194.      1);
+
  195.      1let jslint_rgx_identifier = (
+
  196.      1    /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/
+
  197.      1);
+
  198.      1let jslint_rgx_json_number = (
+
  199.      1
+
  200.      1// https://datatracker.ietf.org/doc/html/rfc7159#section-6
+
  201.      1// number = [ minus ] int [ frac ] [ exp ]
+
  202.      1
+
  203.      1    /^-?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][\-+]?\d+)?$/
+
  204.      1);
+
  205.      1let jslint_rgx_mega = (
+
  206.      1
+
  207.      1// Vim-hack - vim-editor has trouble parsing naked '`' in regexp
+
  208.      1
+
  209.      1    /[\u0060\\]|\$\{/
+
  210.      1);
+
  211.      1let jslint_rgx_module = (
+
  212.      1    /^[a-zA-Z0-9_$:.@\-\/]+$/
+
  213.      1);
+
  214.      1let jslint_rgx_numeric_separator_illegal = (
+
  215.      1    /__|_$|_n$/m
+
  216.      1);
+
  217.      1let jslint_rgx_slash_star_or_slash = (
+
  218.      1    /\/\*|\/$/
+
  219.      1);
+
  220.      1let jslint_rgx_tab = (
+
  221.      1    /\t/g
+
  222.      1);
+
  223.      1let jslint_rgx_todo = (
+
  224.      1    /\b(?:todo|TO\s?DO|HACK)\b/
+
  225.      1);
+
  226.      1let jslint_rgx_token = new RegExp(
+
  227.      1    "^("
+
  228.      1    + "(\\s+)"
+
  229.      1    + "|([a-zA-Z_$][a-zA-Z0-9_$]*)"
+
  230.      1    + "|[(){}\\[\\],:;'\"~\\`]"
+
  231.      1    + "|\\?[?.]?"
+
  232.      1    + "|=(?:==?|>)?"
+
  233.      1    + "|\\.+"
+
  234.      1    + "|\\*[*\\/=]?"
+
  235.      1    + "|\\/[*\\/]?"
+
  236.      1    + "|\\+[=+]?"
+
  237.      1    + "|-[=\\-]?"
+
  238.      1    + "|[\\^%]=?"
+
  239.      1    + "|&[&=]?"
+
  240.      1    + "|\\"
+
  241.      1    + "|[|=]?"
+
  242.      1    + "|>{1,3}=?"
+
  243.      1    + "|<<?=?"
+
  244.      1    + "|!(?:!|==?)?"
+
  245.      1
+
  246.      1// PR-351 - Add BigInt support.
+
  247.      1// PR-390 - Add numeric-separator support.
+
  248.      1
+
  249.      1    + "|((?:0_?|[1-9][0-9_]*)n?)"
+
  250.      1    + ")"
+
  251.      1    + "(.*)$"
+
  252.      1);
+
  253.      1let jslint_rgx_url_search_window_jslint = (
+
  254.      1    /[&?]window_jslint=1(?:$|&)/m
+
  255.      1);
+
  256.      1let jslint_rgx_weird_property = (
+
  257.      1    /^_|\$|Sync$|_$/m
+
  258.      1);
+
  259.      1let jstestCountFailed = 0;
+
  260.      1let jstestCountTotal = 0;
+
  261.      1let jstestItCount = 0;
+
  262.      1let jstestItList = [];
+
  263.      1let jstestTimeStart;
+
  264.      1let moduleChildProcess;
+
  265.      1let moduleFs;
+
  266.      1let moduleFsInitResolveList;
+
  267.      1let modulePath;
+
  268.      1let moduleUrl;
+
  269.      1
+
  270.     11async function assertErrorThrownAsync(asyncFunc, regexp) {
+
  271.     11
+
  272.     11// This function will assert calling <asyncFunc> throws an error.
+
  273.     11
+
  274.     11    let err;
+
  275.     11    try {
+
  276.      1        await asyncFunc();
+
  277.     10    } catch (errCaught) {
+
  278.     10        err = errCaught;
+
  279.     10    }
+
  280.     11    assertOrThrow(err, "No error thrown.");
+
  281.     11    assertOrThrow(
+
  282.      4        !regexp || new RegExp(regexp).test(err.message),
+
  283.     11        err
+
  284.     11    );
+
  285.     11}
+
  286.      1
+
  287.    267function assertJsonEqual(aa, bb, message) {
+
  288.    267
+
  289.    267// This function will assert JSON.stringify(<aa>) === JSON.stringify(<bb>).
+
  290.    267
+
  291.    267    aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa), undefined, 1);
+
  292.    267    bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb), undefined, 1);
+
  293.      3    if (aa !== bb) {
+
  294.      3        throw new Error(
+
  295.      3            "\n" + aa + "\n!==\n" + bb
+
  296.      3            + (
+
  297.      3                typeof message === "string"
+
  298.      3                ? " - " + message
+
  299.      3                : message
+
  300.      3                ? " - " + JSON.stringify(message)
+
  301.      3                : ""
+
  302.      3            )
+
  303.      3        );
+
  304.      3    }
+
  305.    267}
+
  306.      1
+
  307.   1931function assertOrThrow(condition, message) {
+
  308.   1931
+
  309.   1931// This function will throw <message> if <condition> is falsy.
+
  310.   1931
+
  311.      4    if (!condition) {
+
  312.      4        throw (
+
  313.      4            (!message || typeof message === "string")
+
  314.      4            ? new Error(String(message).slice(0, 2048))
+
  315.      4            : message
+
  316.      4        );
+
  317.      4    }
+
  318.   1931}
+
  319.      1
+
  320.  94133function empty() {
+
  321.  94133
+
  322.  94133// The empty function produces a new empty object that inherits nothing. This is
+
  323.  94133// much better than '{}' because confusions around accidental method names like
+
  324.  94133// 'constructor' are completely avoided.
+
  325.  94133
+
  326.  94133    return Object.create(null);
+
  327.  94133}
+
  328.      1
+
  329.     59async function fsWriteFileWithParents(pathname, data) {
+
  330.     59
+
  331.     59// This function will write <data> to <pathname> and lazy-mkdirp if necessary.
+
  332.     59
+
  333.     59    await moduleFsInit();
+
  334.     59
+
  335.     59// Try writing to pathname.
+
  336.     59
+
  337.     59    try {
+
  338.     41        await moduleFs.promises.writeFile(pathname, data);
+
  339.     41    } catch (ignore) {
+
  340.     18
+
  341.     18// Lazy mkdirp.
+
  342.     18
+
  343.     18        await moduleFs.promises.mkdir(modulePath.dirname(pathname), {
+
  344.     18            recursive: true
+
  345.     18        });
+
  346.     18
+
  347.     18// Retry writing to pathname.
+
  348.     18
+
  349.     18        await moduleFs.promises.writeFile(pathname, data);
+
  350.     18    }
+
  351.     59    console.error("wrote file " + pathname);
+
  352.     59}
+
  353.      1
+
  354.    184function globExclude({
+
  355.    184    excludeList = [],
+
  356.    184    includeList = [],
+
  357.    184    pathnameList = []
+
  358.    184}) {
+
  359.    184
+
  360.    184// This function will
+
  361.    184// 1. Exclude pathnames in <pathnameList> that don't match glob-patterns in
+
  362.    184//    <includeList>.
+
  363.    184// 2. Exclude pathnames in <pathnameList> that match glob-patterns in
+
  364.    184//    <excludeList>.
+
  365.    184
+
  366.    552    function globAssertNotWeird(list, name) {
+
  367.    552
+
  368.    552// This function will check if <list> of strings contain weird characters.
+
  369.    552
+
  370.    552        [
+
  371.    552            [
+
  372.    552                "\n", (
+
  373.    552                    /^.*?([\u0000-\u0007\r]).*/gm
+
  374.    552                )
+
  375.    552            ],
+
  376.    552            [
+
  377.    552                "\r", (
+
  378.    552                    /^.*?([\n]).*/gm
+
  379.    552                )
+
  380.    552            ]
+
  381.   1102        ].forEach(function ([
+
  382.   1102            separator, rgx
+
  383.   1102        ]) {
+
  384.      3            list.join(separator).replace(rgx, function (match0, char) {
+
  385.      3                throw new Error(
+
  386.      3                    "Weird character "
+
  387.      3                    + JSON.stringify(char)
+
  388.      3                    + " found in " + name + " "
+
  389.      3                    + JSON.stringify(match0)
+
  390.      3                );
+
  391.      3            });
+
  392.   1102        });
+
  393.    552    }
+
  394.    184
+
  395.   1370    function globToRegexp(pattern) {
+
  396.   1370
+
  397.   1370// This function will translate glob <pattern> to javascript-regexp,
+
  398.   1370// which javascript can then use to "glob" pathnames.
+
  399.   1370
+
  400.   1370        let ii = 0;
+
  401.   1370        let isClass = false;
+
  402.   1370        let strClass = "";
+
  403.   1370        let strRegex = "";
+
  404.   1370        pattern = pattern.replace((
+
  405.   1370            /\/\/+/g
+
  406.   1370        ), "/");
+
  407.   1370        pattern = pattern.replace((
+
  408.   1370            /\*\*\*+/g
+
  409.   1370        ), "**");
+
  410.   1370        pattern.replace((
+
  411.   1370            /\\\\|\\\[|\\\]|\[|\]|./g
+
  412.  18691        ), function (match0) {
+
  413.  18691            switch (match0) {
+
  414.    310            case "[":
+
  415.    310                if (isClass) {
+
  416.    310                    strClass += "[";
+
  417.    310                    return;
+
  418.    310                }
+
  419.    310                strClass += "\u0000";
+
  420.    310                strRegex += "\u0000";
+
  421.    310                isClass = true;
+
  422.    310                return;
+
  423.    310            case "]":
+
  424.    310                if (isClass) {
+
  425.    310                    isClass = false;
+
  426.    310                    return;
+
  427.    310                }
+
  428.    310                strRegex += "]";
+
  429.    310                return;
+
  430.  18071            default:
+
  431.  18071                if (isClass) {
+
  432.  18071                    strClass += match0;
+
  433.  18071                    return;
+
  434.  18071                }
+
  435.  18071                strRegex += match0;
+
  436.  15021            }
+
  437.  15021            return "";
+
  438.  15021        });
+
  439.   1370        strClass += "\u0000";
+
  440.   1370
+
  441.   1370// An expression "[!...]" matches a single character, namely any character that
+
  442.   1370// is not matched by the expression obtained by removing the first '!' from it.
+
  443.   1370// (Thus, "[!a-]" matches any single character except 'a', and '-'.)
+
  444.   1370
+
  445.   1370        strClass = strClass.replace((
+
  446.   1370            /\u0000!/g
+
  447.   1370        ), "\u0000^");
+
  448.   1370
+
  449.   1370// One may include '-' in its literal meaning by making it the first or last
+
  450.   1370// character between the brackets.
+
  451.   1370
+
  452.   1370        strClass = strClass.replace((
+
  453.   1370            /\u0000-/g
+
  454.   1370        ), "\u0000\\-");
+
  455.   1370        strClass = strClass.replace((
+
  456.   1370            /-\u0000/g
+
  457.   1370        ), "\\-\u0000");
+
  458.   1370
+
  459.   1370// Escape brackets '[', ']' in character class.
+
  460.   1370
+
  461.   1370        strClass = strClass.replace((
+
  462.   1370            /[\[\]]/g
+
  463.   1370        ), "\\$&");
+
  464.   1370
+
  465.   1370// https://stackoverflow.com/questions/3561493
+
  466.   1370// /is-there-a-regexp-escape-function-in-javascript
+
  467.   1370// $()*+-./?[\]^{|}
+
  468.   1370
+
  469.   1370        strRegex = strRegex.replace((
+
  470.   1370
+
  471.   1370// Ignore [-/].
+
  472.   1370
+
  473.   1370            /[$()*+.?\[\\\]\^{|}]/g
+
  474.   1370        ), "\\$&");
+
  475.   1370
+
  476.   1370// Expand wildcard '**/*'.
+
  477.   1370
+
  478.   1370        strRegex = strRegex.replace((
+
  479.   1370            /\\\*\\\*\/(?:\\\*)+/g
+
  480.   1370        ), ".*?");
+
  481.   1370
+
  482.   1370// Expand wildcard '**'.
+
  483.   1370
+
  484.   1370        strRegex = strRegex.replace((
+
  485.   1370            /(^|\/)\\\*\\\*(\/|$)/gm
+
  486.   1370        ), "$1.*?$2");
+
  487.   1370
+
  488.   1370// Expand wildcard '*'.
+
  489.   1370
+
  490.   1370        strRegex = strRegex.replace((
+
  491.   1370            /(?:\\\*)+/g
+
  492.   1370        ), "[^\\/]*?");
+
  493.   1370
+
  494.   1370// Expand wildcard '?'.
+
  495.   1370
+
  496.   1370        strRegex = strRegex.replace((
+
  497.   1370            /\\\?/g
+
  498.   1370        ), "[^\\/]");
+
  499.   1370
+
  500.   1370// Expand directory-with-trailing-slash '.../'.
+
  501.   1370
+
  502.   1370        strRegex = strRegex.replace((
+
  503.   1370            /\/$/gm
+
  504.   1370        ), "\\/.*?");
+
  505.   1370
+
  506.   1370// Merge strClass into strRegex.
+
  507.   1370
+
  508.   1370        ii = 0;
+
  509.   1370        strClass = strClass.split("\u0000");
+
  510.   1370        strRegex = strRegex.replace((
+
  511.   1370            /\u0000/g
+
  512.    306        ), function () {
+
  513.    306            ii += 1;
+
  514.      2            if (strClass[ii] === "") {
+
  515.      2                return "";
+
  516.    304            }
+
  517.    304            return "[" + strClass[ii] + "]";
+
  518.    304        });
+
  519.   1370
+
  520.   1370// Change strRegex from string to regexp.
+
  521.   1370
+
  522.   1370        strRegex = new RegExp("^" + strRegex + "$", "gm");
+
  523.   1370        return strRegex;
+
  524.   1370    }
+
  525.    184
+
  526.    184// Validate excludeList, includeList, pathnameList.
+
  527.    184
+
  528.    184    globAssertNotWeird(excludeList, "pattern");
+
  529.    184    globAssertNotWeird(includeList, "pattern");
+
  530.    184    globAssertNotWeird(pathnameList, "pathname");
+
  531.    184
+
  532.    184// Optimization
+
  533.    184// Concat pathnames into a single, newline-separated string,
+
  534.    184// whose pathnames can all be filtered with a single, regexp-pass.
+
  535.    184
+
  536.    184    pathnameList = pathnameList.join("\n");
+
  537.    184
+
  538.    184// 1. Exclude pathnames in <pathnameList> that don't match glob-patterns in
+
  539.    184//    <includeList>.
+
  540.    184
+
  541.    142    if (includeList.length > 0) {
+
  542.    142        includeList = includeList.map(globToRegexp);
+
  543.    574        includeList.forEach(function (pattern) {
+
  544.    574            pathnameList = pathnameList.replace(pattern, "\u0000$&");
+
  545.    574        });
+
  546.    142        pathnameList = pathnameList.replace((
+
  547.    142            /^[^\u0000].*/gm
+
  548.    142        ), "");
+
  549.    142        pathnameList = pathnameList.replace((
+
  550.    142            /^\u0000+/gm
+
  551.    142        ), "");
+
  552.    181    }
+
  553.    181
+
  554.    181// 2. Exclude pathnames in <pathnameList> that match glob-patterns in
+
  555.    181//    <excludeList>.
+
  556.    181
+
  557.    181    excludeList = excludeList.map(globToRegexp);
+
  558.    796    excludeList.forEach(function (pattern) {
+
  559.    796        pathnameList = pathnameList.replace(pattern, "");
+
  560.    796    });
+
  561.    181
+
  562.    181// Split newline-separated pathnames back to list.
+
  563.    181
+
  564.  10117    pathnameList = pathnameList.split("\n").filter(function (elem) {
+
  565.  10117        return elem;
+
  566.  10117    });
+
  567.    181    return {
+
  568.    181        excludeList,
+
  569.    181        includeList,
+
  570.    181        pathnameList
+
  571.    181    };
+
  572.    181}
+
  573.      1
+
  574.  14214function htmlEscape(str) {
+
  575.  14214
+
  576.  14214// This function will make <str> html-safe by escaping & < >.
+
  577.  14214
+
  578.  14214    return String(str).replace((
+
  579.  14214        /&/g
+
  580.  14214    ), "&amp;").replace((
+
  581.  14214        /</g
+
  582.  14214    ), "&lt;").replace((
+
  583.  14214        />/g
+
  584.  14214    ), "&gt;");
+
  585.  14214}
+
  586.      1
+
  587.    668function jslint(
+
  588.    668    source = "",                // A text to analyze.
+
  589.    668    option_dict = empty(),      // An object whose keys correspond to option
+
  590.    668                                // ... names.
+
  591.    668    global_list = []            // An array of strings containing global
+
  592.    668                                // ... variables that the file is allowed
+
  593.    668                                // ... readonly access.
+
  594.    668) {
+
  595.    668
+
  596.    668// The jslint function itself.
+
  597.    668
+
  598.    668    let catch_list = [];        // The array containing all catch-blocks.
+
  599.    668    let catch_stack = [         // The stack of catch-blocks.
+
  600.    668        {
+
  601.    668            context: empty()
+
  602.    668        }
+
  603.    668    ];
+
  604.    668    let cause_dict = empty();   // The object of test-causes.
+
  605.    668    let directive_list = [];    // The directive comments.
+
  606.    668    let export_dict = empty();  // The exported names and values.
+
  607.    668    let function_list = [];     // The array containing all functions.
+
  608.    668    let function_stack = [];    // The stack of functions.
+
  609.    668    let global_dict = empty();  // The object containing the global
+
  610.    668                                // ... declarations.
+
  611.    668    let import_list = [];       // The array collecting all import-from strings.
+
  612.    668    let line_list = String(     // The array containing source lines.
+
  613.    668        "\n" + source
+
  614. 105217    ).split(jslint_rgx_crlf).map(function (line_source) {
+
  615. 105217        return {
+
  616. 105217            line_source
+
  617. 105217        };
+
  618. 105217    });
+
  619.    668    let mode_stop = false;      // true if JSLint cannot finish.
+
  620.    668    let property_dict = empty();        // The object containing the tallied
+
  621.    668                                        // ... property names.
+
  622.    668    let state = empty();        // jslint state-object to be passed between
+
  623.    668                                // jslint functions.
+
  624.    668    let syntax_dict = empty();  // The object containing the parser.
+
  625.    668    let tenure = empty();       // The predefined property registry.
+
  626.    668    let token_global = {        // The global object; the outermost context.
+
  627.    668        async: 0,
+
  628.    668        body: true,
+
  629.    668        context: empty(),
+
  630.    668        finally: 0,
+
  631.    668        from: 0,
+
  632.    668        id: "(global)",
+
  633.    668        level: 0,
+
  634.    668        line: jslint_fudge,
+
  635.    668        live: [],
+
  636.    668        loop: 0,
+
  637.    668        switch: 0,
+
  638.    668        thru: 0,
+
  639.    668        try: 0
+
  640.    668    };
+
  641.    668    let token_list = [];        // The array of tokens.
+
  642.    668    let warning_list = [];      // The array collecting all generated warnings.
+
  643.    668
+
  644.    668// Error reportage functions:
+
  645.    668
+
  646.   8041    function artifact(the_token) {
+
  647.   8041
+
  648.   8041// Return a string representing an artifact.
+
  649.   8041
+
  650.    256        the_token = the_token || state.token_nxt;
+
  651.   8041        return (
+
  652.   5266            (the_token.id === "(string)" || the_token.id === "(number)")
+
  653.   2900            ? String(the_token.value)
+
  654.   5141            : the_token.id
+
  655.   8041        );
+
  656.   8041    }
+
  657.    668
+
  658.  31957    function is_equal(aa, bb) {
+
  659.  31957
+
  660.  31957// test_cause:
+
  661.  31957// ["0&&0", "is_equal", "", "", 0]
+
  662.  31957
+
  663.  31957        test_cause("");
+
  664.  31957
+
  665.  31957// Probably deadcode.
+
  666.  31957// if (aa === bb) {
+
  667.  31957//     return true;
+
  668.  31957// }
+
  669.  31957
+
  670.  31957        jslint_assert(!(aa === bb), `Expected !(aa === bb).`);
+
  671.     27        if (Array.isArray(aa)) {
+
  672.     27            return (
+
  673.     27                Array.isArray(bb)
+
  674.     27                && aa.length === bb.length
+
  675.     27                && aa.every(function (value, index) {
+
  676.     27
+
  677.     27// test_cause:
+
  678.     27// ["`${0}`&&`${0}`", "is_equal", "recurse_isArray", "", 0]
+
  679.     27// ["`${0}`&&`${1}`", "is_equal", "recurse_isArray", "", 0]
+
  680.     27
+
  681.     27                    test_cause("recurse_isArray");
+
  682.     27                    return is_equal(value, bb[index]);
+
  683.     27                })
+
  684.     27            );
+
  685.  31930        }
+
  686.  31930
+
  687.  31930// Probably deadcode.
+
  688.  31930// if (Array.isArray(bb)) {
+
  689.  31930//     return false;
+
  690.  31930// }
+
  691.  31930
+
  692.  31930        jslint_assert(!Array.isArray(bb), `Expected !Array.isArray(bb).`);
+
  693.  31930        switch (aa.id === bb.id && aa.id) {
+
  694.     65        case "(number)":
+
  695.  23429        case "(string)":
+
  696.  23429            return aa.value === bb.value;
+
  697.  31957
+
  698.  31957// PR-394 - Bugfix
+
  699.  31957// Fix jslint falsely believing megastring literals `0` and `1` are similar.
+
  700.  31957
+
  701.     15        case "`":
+
  702.     15            if (!is_equal(aa.value, bb.value)) {
+
  703.     15                return false;
+
  704.     15            }
+
  705.     15            break;
+
  706.   8498        }
+
  707.   8498        if (is_weird(aa) || is_weird(bb)) {
+
  708.     34
+
  709.     34// test_cause:
+
  710.     34// ["aa(/./)||{}", "is_equal", "false", "", 0]
+
  711.     34
+
  712.     34            test_cause("false");
+
  713.     34            return false;
+
  714.   8464        }
+
  715.   8464        if (aa.arity === bb.arity && aa.id === bb.id) {
+
  716.   2147            if (aa.id === "." || aa.id === "?.") {
+
  717.   2147
+
  718.   2147// test_cause:
+
  719.   2147// ["aa.bb&&aa.bb", "is_equal", "recurse_arity_id", "", 0]
+
  720.   2147// ["aa?.bb&&aa?.bb", "is_equal", "recurse_arity_id", "", 0]
+
  721.   2147
+
  722.   2147                test_cause("recurse_arity_id");
+
  723.   2147                return (
+
  724.   2147                    is_equal(aa.expression, bb.expression)
+
  725.   2147                    && is_equal(aa.name, bb.name)
+
  726.   2147                );
+
  727.   2147            }
+
  728.   2147            if (aa.arity === "unary") {
+
  729.   2147
+
  730.   2147// test_cause:
+
  731.   2147// ["+0&&+0", "is_equal", "recurse_unary", "", 0]
+
  732.   2147
+
  733.   2147                test_cause("recurse_unary");
+
  734.   2147                return is_equal(aa.expression, bb.expression);
+
  735.   2147            }
+
  736.   2147            if (aa.arity === "binary") {
+
  737.   2147
+
  738.   2147// test_cause:
+
  739.   2147// ["aa[0]&&aa[0]", "is_equal", "recurse_binary", "", 0]
+
  740.   2147
+
  741.   2147                test_cause("recurse_binary");
+
  742.   2147                return (
+
  743.   2147                    aa.id !== "("
+
  744.   2147                    && is_equal(aa.expression[0], bb.expression[0])
+
  745.   2147                    && is_equal(aa.expression[1], bb.expression[1])
+
  746.   2147                );
+
  747.   2147            }
+
  748.   2147            if (aa.arity === "ternary") {
+
  749.   2147
+
  750.   2147// test_cause:
+
  751.   2147// ["aa=(``?``:``)&&(``?``:``)", "is_equal", "recurse_ternary", "", 0]
+
  752.   2147
+
  753.   2147                test_cause("recurse_ternary");
+
  754.   2147                return (
+
  755.   2147                    is_equal(aa.expression[0], bb.expression[0])
+
  756.   2147                    && is_equal(aa.expression[1], bb.expression[1])
+
  757.   2147                    && is_equal(aa.expression[2], bb.expression[2])
+
  758.   2147                );
+
  759.   2147            }
+
  760.   2147
+
  761.   2147// Probably deadcode.
+
  762.   2147// if (aa.arity === "function" || aa.arity === "regexp") {
+
  763.   2147//     return false;
+
  764.   2147// }
+
  765.   2147
+
  766.   2147            jslint_assert(
+
  767.   2147                !(aa.arity === "function" || aa.arity === "regexp"),
+
  768.   2147                `Expected !(aa.arity === "function" || aa.arity === "regexp").`
+
  769.   2147            );
+
  770.   2147
+
  771.   2147// test_cause:
+
  772.   2147// ["undefined&&undefined", "is_equal", "true", "", 0]
+
  773.   2147
+
  774.   2147            test_cause("true");
+
  775.   2147            return true;
+
  776.   6317        }
+
  777.   6317
+
  778.   6317// test_cause:
+
  779.   6317// ["null&&undefined", "is_equal", "false", "", 0]
+
  780.   6317
+
  781.   6317        test_cause("false");
+
  782.   6317        return false;
+
  783.   6317    }
+
  784.    668
+
  785.  28763    function is_weird(thing) {
+
  786.  28763        switch (thing.id) {
+
  787.      1        case "(regexp)":
+
  788.      1            return true;
+
  789.      1        case "=>":
+
  790.      1            return true;
+
  791.    593        case "[":
+
  792.    593            return thing.arity === "unary";
+
  793.     12        case "function":
+
  794.     12            return true;
+
  795.      8        case "{":
+
  796.      8            return true;
+
  797.  28148        default:
+
  798.  28148            return false;
+
  799.  28763        }
+
  800.  28763    }
+
  801.    668
+
  802.    106    function stop(code, the_token, a, b, c, d) {
+
  803.    106
+
  804.    106// Similar to warn and stop_at. If the token already had a warning, that
+
  805.    106// warning will be replaced with this new one. It is likely that the stopping
+
  806.    106// warning will be the more meaningful.
+
  807.    106
+
  808.     38        the_token = the_token || state.token_nxt;
+
  809.    106        delete the_token.warning;
+
  810.    106        throw warn(code, the_token, a, b, c, d);
+
  811.    106    }
+
  812.    668
+
  813.     28    function stop_at(code, line, column, a, b, c, d) {
+
  814.     28
+
  815.     28// Same as warn_at, except that it stops the analysis.
+
  816.     28
+
  817.     28        throw warn_at(code, line, column, a, b, c, d);
+
  818.     28    }
+
  819.    668
+
  820. 339547    function test_cause(code, aa, column) {
+
  821. 339547
+
  822. 339547// This function will instrument <cause> to <cause_dict> for test-purposes.
+
  823. 339547
+
  824.   4882        if (option_dict.test_cause) {
+
  825.   4882            cause_dict[JSON.stringify([
+
  826.   4882                String(new Error().stack).replace((
+
  827.   4882                    /^    at (?:file|stop|stop_at|test_cause|warn|warn_at)\b.*?\n/gm
+
  828.   4882                ), "").match(
+
  829.   4882                    /\n    at ((?:Object\.\w+?_)?\w+?) /
+
  830.   4882                )[1].replace((
+
  831.   4882                    /^Object\./
+
  832.   4882                ), ""),
+
  833.   4882                code,
+
  834.   4882                String(
+
  835.   4882                    (aa === undefined || aa === token_global)
+
  836.   4882                    ? ""
+
  837.   4882                    : aa
+
  838.   4882                ),
+
  839.   4882                column || 0
+
  840.   4882            ])] = true;
+
  841.   4882        }
+
  842. 339547    }
+
  843.    668
+
  844.   1075    function warn(code, the_token, a, b, c, d) {
+
  845.   1075
+
  846.   1075// Same as warn_at, except the warning will be associated with a specific token.
+
  847.   1075// If there is already a warning on this token, suppress the new one. It is
+
  848.   1075// likely that the first warning will be the most meaningful.
+
  849.   1075
+
  850.   1075        let the_warning;
+
  851.     20        the_token = the_token || state.token_nxt;
+
  852.   1075        the_warning = warn_at(
+
  853.   1075            code,
+
  854.   1075            the_token.line,
+
  855.    376            (the_token.from || 0) + jslint_fudge,
+
  856.    835            a || artifact(the_token),
+
  857.   1075            b,
+
  858.   1075            c,
+
  859.   1075            d
+
  860.   1075        );
+
  861.   1075
+
  862.   1075// Issue #408
+
  863.   1075// Warnings that should be ignored sometimes suppress legitimate warnings.
+
  864.   1075
+
  865.     26        if (the_warning.directive_ignore_line) {
+
  866.     26            return the_warning;
+
  867.   1049        }
+
  868.   1049
+
  869.   1049// If there is already a warning on this token, suppress the new one. It is
+
  870.   1049// likely that the first warning will be the most meaningful.
+
  871.   1049
+
  872.   1049        if (the_token.warning) {
+
  873.    192            warning_list.pop();
+
  874.    192            return the_warning;
+
  875.    857        }
+
  876.    857        the_token.warning = the_warning;
+
  877.    857        return the_warning;
+
  878.    857    }
+
  879.    668
+
  880.   1394    function warn_at(code, line, column, a, b, c, d) {
+
  881.   1394
+
  882.   1394// Report an error at some line and column of the program. The warning object
+
  883.   1394// resembles an exception.
+
  884.   1394
+
  885.   1394        let mm;
+
  886.   1394        let warning = Object.assign(empty(), {
+
  887.   1394            a,
+
  888.   1394            b,
+
  889.   1394            c,
+
  890.   1394            code,
+
  891.   1394
+
  892.   1394// Fudge column numbers in warning message.
+
  893.   1394
+
  894.     27            column: column || jslint_fudge,
+
  895.   1394            d,
+
  896.   1394            line,
+
  897.   1394            line_source: "",
+
  898.   1394            name: "JSLintError"
+
  899.   1394        }, line_list[line]);
+
  900.   1394        warning.column = Math.max(
+
  901.   1394            Math.min(warning.column, warning.line_source.length),
+
  902.   1394            jslint_fudge
+
  903.   1394        );
+
  904.    926        test_cause(code, b || a, warning.column);
+
  905.   1394        switch (code) {
+
  906.   1394
+
  907.   1394// The bundle contains the raw text messages that are generated by jslint. It
+
  908.   1394// seems that they are all error messages and warnings. There are no "Atta
+
  909.   1394// boy!" or "You are so awesome!" messages. There is no positive reinforcement
+
  910.   1394// or encouragement. This relentless negativity can undermine self-esteem and
+
  911.   1394// wound the inner child. But if you accept it as sound advice rather than as
+
  912.   1394// personal criticism, it can make your programs better.
+
  913.   1394
+
  914.      1        case "and":
+
  915.      1            mm = `The '&&' subexpression should be wrapped in parens.`;
+
  916.      1            break;
+
  917.     71        case "bad_assignment_a":
+
  918.     71            mm = `Bad assignment to '${a}'.`;
+
  919.     71            break;
+
  920.      1        case "bad_directive_a":
+
  921.      1            mm = `Bad directive '${a}'.`;
+
  922.      1            break;
+
  923.      1        case "bad_get":
+
  924.      1            mm = `A get function takes no parameters.`;
+
  925.      1            break;
+
  926.      1        case "bad_module_name_a":
+
  927.      1            mm = `Bad module name '${a}'.`;
+
  928.      1            break;
+
  929.      2        case "bad_option_a":
+
  930.      2            mm = `Bad option '${a}'.`;
+
  931.      2            break;
+
  932.      1        case "bad_set":
+
  933.      1            mm = `A set function takes one parameter.`;
+
  934.      1            break;
+
  935.      6        case "duplicate_a":
+
  936.      6            mm = `Duplicate '${a}'.`;
+
  937.      6            break;
+
  938.     64        case "empty_block":
+
  939.     64            mm = `Empty block.`;
+
  940.     64            break;
+
  941.      5        case "expected_a":
+
  942.      5            mm = `Expected '${a}'.`;
+
  943.      5            break;
+
  944.     25        case "expected_a_at_b_c":
+
  945.     25            mm = `Expected '${a}' at column ${b}, not column ${c}.`;
+
  946.     25            break;
+
  947.    286        case "expected_a_b":
+
  948.    286            mm = `Expected '${a}' and instead saw '${b}'.`;
+
  949.    286            break;
+
  950.     17        case "expected_a_b_before_c_d":
+
  951.     17            mm = `Expected ${a} '${b}' to be ordered before ${c} '${d}'.`;
+
  952.     17            break;
+
  953.      2        case "expected_a_b_from_c_d":
+
  954.      2            mm = (
+
  955.      2                `Expected '${a}' to match '${b}' from line ${c}`
+
  956.      2                + ` and instead saw '${d}'.`
+
  957.      2            );
+
  958.      2            break;
+
  959.     30        case "expected_a_before_b":
+
  960.     30            mm = `Expected '${a}' before '${b}'.`;
+
  961.     30            break;
+
  962.      2        case "expected_digits_after_a":
+
  963.      2            mm = `Expected digits after '${a}'.`;
+
  964.      2            break;
+
  965.      1        case "expected_four_digits":
+
  966.      1            mm = `Expected four digits after '\\u'.`;
+
  967.      1            break;
+
  968.     31        case "expected_identifier_a":
+
  969.     31            mm = `Expected an identifier and instead saw '${a}'.`;
+
  970.     31            break;
+
  971.      6        case "expected_line_break_a_b":
+
  972.      6            mm = `Expected a line break between '${a}' and '${b}'.`;
+
  973.      6            break;
+
  974.      3        case "expected_regexp_factor_a":
+
  975.      3            mm = `Expected a regexp factor and instead saw '${a}'.`;
+
  976.      3            break;
+
  977.     76        case "expected_space_a_b":
+
  978.     76            mm = `Expected one space between '${a}' and '${b}'.`;
+
  979.     76            break;
+
  980.      2        case "expected_statements_a":
+
  981.      2            mm = `Expected statements before '${a}'.`;
+
  982.      2            break;
+
  983.      1        case "expected_string_a":
+
  984.      1            mm = `Expected a string and instead saw '${a}'.`;
+
  985.      1            break;
+
  986.      1        case "expected_type_string_a":
+
  987.      1            mm = `Expected a type string and instead saw '${a}'.`;
+
  988.      1            break;
+
  989.      6        case "freeze_exports":
+
  990.      6            mm = (
+
  991.      6                `Expected 'Object.freeze('. All export values should be frozen.`
+
  992.      6            );
+
  993.      6            break;
+
  994.   1394
+
  995.   1394// PR-378 - Relax warning "function_in_loop".
+
  996.   1394//
+
  997.   1394//         case "function_in_loop":
+
  998.   1394//             mm = `Don't create functions within a loop.`;
+
  999.   1394//             break;
+
 1000.   1394
+
 1001.   1394// PR-390 - Add numeric-separator check.
+
 1002.   1394
+
 1003.      7        case "illegal_num_separator":
+
 1004.      7            mm = `Illegal numeric separator '_' at column ${column}.`;
+
 1005.      7            break;
+
 1006.      1        case "infix_in":
+
 1007.      1            mm = (
+
 1008.      1                `Unexpected 'in'. Compare with undefined,`
+
 1009.      1                + ` or use the hasOwnProperty method instead.`
+
 1010.      1            );
+
 1011.      1            break;
+
 1012.      1        case "label_a":
+
 1013.      1            mm = `'${a}' is a statement label.`;
+
 1014.      1            break;
+
 1015.      1        case "misplaced_a":
+
 1016.      1            mm = `Place '${a}' at the outermost level.`;
+
 1017.      1            break;
+
 1018.      1        case "misplaced_directive_a":
+
 1019.      1            mm = `Place the '/*${a}*/' directive before the first statement.`;
+
 1020.      1            break;
+
 1021.      3        case "missing_await_statement":
+
 1022.      3            mm = `Expected await statement in async function.`;
+
 1023.      3            break;
+
 1024.   1394
+
 1025.   1394// PR-347 - Disable warning "missing_browser".
+
 1026.   1394//
+
 1027.   1394//         case "missing_browser":
+
 1028.   1394//             mm = `/*global*/ requires the Assume a browser option.`;
+
 1029.   1394//             break;
+
 1030.   1394
+
 1031.      1        case "missing_m":
+
 1032.      1            mm = `Expected 'm' flag on a multiline regular expression.`;
+
 1033.      1            break;
+
 1034.      5        case "naked_block":
+
 1035.      5            mm = `Naked block.`;
+
 1036.      5            break;
+
 1037.      2        case "nested_comment":
+
 1038.      2            mm = `Nested comment.`;
+
 1039.      2            break;
+
 1040.      1        case "not_label_a":
+
 1041.      1            mm = `'${a}' is not a label.`;
+
 1042.      1            break;
+
 1043.      2        case "number_isNaN":
+
 1044.      2            mm = `Use Number.isNaN function to compare with NaN.`;
+
 1045.      2            break;
+
 1046.      4        case "out_of_scope_a":
+
 1047.      4            mm = `'${a}' is out of scope.`;
+
 1048.      4            break;
+
 1049.     11        case "redefinition_a_b":
+
 1050.     11            mm = `Redefinition of '${a}' from line ${b}.`;
+
 1051.     11            break;
+
 1052.      1        case "redefinition_global_a_b":
+
 1053.      1            mm = `Redefinition of global ${a} variable '${b}'.`;
+
 1054.      1            break;
+
 1055.      6        case "required_a_optional_b":
+
 1056.      6            mm = `Required parameter '${a}' after optional parameter '${b}'.`;
+
 1057.      6            break;
+
 1058.      1        case "reserved_a":
+
 1059.      1            mm = `Reserved name '${a}'.`;
+
 1060.      1            break;
+
 1061.      1        case "subscript_a":
+
 1062.      1            mm = `['${a}'] is better written in dot notation.`;
+
 1063.      1            break;
+
 1064.     16        case "todo_comment":
+
 1065.     16            mm = `Unexpected TODO comment.`;
+
 1066.     16            break;
+
 1067.     13        case "too_long":
+
 1068.     13            mm = `Line is longer than 80 characters.`;
+
 1069.     13            break;
+
 1070.      1        case "too_many_digits":
+
 1071.      1            mm = `Too many digits.`;
+
 1072.      1            break;
+
 1073.      3        case "unclosed_comment":
+
 1074.      3            mm = `Unclosed comment.`;
+
 1075.      3            break;
+
 1076.      3        case "unclosed_disable":
+
 1077.      3            mm = (
+
 1078.      3                `Directive '/*jslint-disable*/' was not closed`
+
 1079.      3                + ` with '/*jslint-enable*/'.`
+
 1080.      3            );
+
 1081.      3            break;
+
 1082.      3        case "unclosed_mega":
+
 1083.      3            mm = `Unclosed mega literal.`;
+
 1084.      3            break;
+
 1085.      2        case "unclosed_string":
+
 1086.      2            mm = `Unclosed string.`;
+
 1087.      2            break;
+
 1088.    137        case "undeclared_a":
+
 1089.    137            mm = `Undeclared '${a}'.`;
+
 1090.    137            break;
+
 1091.    237        case "unexpected_a":
+
 1092.    237            mm = `Unexpected '${a}'.`;
+
 1093.    237            break;
+
 1094.      2        case "unexpected_a_after_b":
+
 1095.      2            mm = `Unexpected '${a}' after '${b}'.`;
+
 1096.      2            break;
+
 1097.      2        case "unexpected_a_before_b":
+
 1098.      2            mm = `Unexpected '${a}' before '${b}'.`;
+
 1099.      2            break;
+
 1100.     34        case "unexpected_at_top_level_a":
+
 1101.     34            mm = `Expected '${a}' to be in a function.`;
+
 1102.     34            break;
+
 1103.      1        case "unexpected_char_a":
+
 1104.      1            mm = `Unexpected character '${a}'.`;
+
 1105.      1            break;
+
 1106.      2        case "unexpected_comment":
+
 1107.      2            mm = `Unexpected comment.`;
+
 1108.      2            break;
+
 1109.   1394
+
 1110.   1394// PR-347 - Disable warning "unexpected_directive_a".
+
 1111.   1394//
+
 1112.   1394//         case "unexpected_directive_a":
+
 1113.   1394//             mm = `When using modules, don't use directive '/\u002a${a}'.`;
+
 1114.   1394//             break;
+
 1115.   1394
+
 1116.    128        case "unexpected_expression_a":
+
 1117.    128            mm = `Unexpected expression '${a}' in statement position.`;
+
 1118.    128            break;
+
 1119.      4        case "unexpected_label_a":
+
 1120.      4            mm = `Unexpected label '${a}'.`;
+
 1121.      4            break;
+
 1122.      3        case "unexpected_parens":
+
 1123.      3            mm = `Don't wrap function literals in parens.`;
+
 1124.      3            break;
+
 1125.      8        case "unexpected_space_a_b":
+
 1126.      8            mm = `Unexpected space between '${a}' and '${b}'.`;
+
 1127.      8            break;
+
 1128.      1        case "unexpected_statement_a":
+
 1129.      1            mm = `Unexpected statement '${a}' in expression position.`;
+
 1130.      1            break;
+
 1131.      2        case "unexpected_trailing_space":
+
 1132.      2            mm = `Unexpected trailing space.`;
+
 1133.      2            break;
+
 1134.      1        case "unexpected_typeof_a":
+
 1135.      1            mm = (
+
 1136.      1                `Unexpected 'typeof'. Use '===' to compare directly with ${a}.`
+
 1137.      1            );
+
 1138.      1            break;
+
 1139.      1        case "uninitialized_a":
+
 1140.      1            mm = `Uninitialized '${a}'.`;
+
 1141.      1            break;
+
 1142.      1        case "unopened_enable":
+
 1143.      1            mm = (
+
 1144.      1                `Directive '/*jslint-enable*/' was not opened`
+
 1145.      1                + ` with '/*jslint-disable*/'.`
+
 1146.      1            );
+
 1147.      1            break;
+
 1148.      1        case "unreachable_a":
+
 1149.      1            mm = `Unreachable '${a}'.`;
+
 1150.      1            break;
+
 1151.      1        case "unregistered_property_a":
+
 1152.      1            mm = `Unregistered property name '${a}'.`;
+
 1153.      1            break;
+
 1154.      6        case "unused_a":
+
 1155.      6            mm = `Unused '${a}'.`;
+
 1156.      6            break;
+
 1157.      2        case "use_double":
+
 1158.      2            mm = `Use double quotes, not single quotes.`;
+
 1159.      2            break;
+
 1160.   1394
+
 1161.   1394// PR-386 - Fix issue #382 - Make fart-related warnings more readable.
+
 1162.   1394
+
 1163.      4        case "use_function_not_fart":
+
 1164.      4            mm = (
+
 1165.      4                `Use 'function (...)', not '(...) =>' when arrow functions`
+
 1166.      4                + ` become too complex.`
+
 1167.      4            );
+
 1168.      4            break;
+
 1169.      7        case "use_open":
+
 1170.      7            mm = (
+
 1171.      7                `Wrap a ternary expression in parens,`
+
 1172.      7                + ` with a line break after the left paren.`
+
 1173.      7            );
+
 1174.      7            break;
+
 1175.      1        case "use_spaces":
+
 1176.      1            mm = `Use spaces, not tabs.`;
+
 1177.      1            break;
+
 1178.      5        case "var_on_top":
+
 1179.      5            mm = `Move variable declaration to top of function or script.`;
+
 1180.      5            break;
+
 1181.      1        case "var_switch":
+
 1182.      1            mm = `Don't declare variables in a switch.`;
+
 1183.      1            break;
+
 1184.     23        case "weird_condition_a":
+
 1185.     23            mm = `Weird condition '${a}'.`;
+
 1186.     23            break;
+
 1187.      5        case "weird_expression_a":
+
 1188.      5            mm = `Weird expression '${a}'.`;
+
 1189.      5            break;
+
 1190.      3        case "weird_loop":
+
 1191.      3            mm = `Weird loop.`;
+
 1192.      3            break;
+
 1193.      9        case "weird_property_a":
+
 1194.      9            mm = `Weird property name '${a}'.`;
+
 1195.      9            break;
+
 1196.      8        case "weird_relation_a":
+
 1197.      8            mm = `Weird relation '${a}'.`;
+
 1198.      8            break;
+
 1199.      1        case "wrap_condition":
+
 1200.      1            mm = `Wrap the condition in parens.`;
+
 1201.      1            break;
+
 1202.   1394
+
 1203.   1394// PR-386 - Fix issue #382 - Make fart-related warnings more readable.
+
 1204.   1394
+
 1205.      1        case "wrap_fart_parameter":
+
 1206.      1            mm = `Wrap the parameter before '=>' in parens.`;
+
 1207.      1            break;
+
 1208.      1        case "wrap_immediate":
+
 1209.      1            mm = (
+
 1210.      1                `Wrap an immediate function invocation in parentheses to assist`
+
 1211.      1                + ` the reader in understanding that the expression is the`
+
 1212.      1                + ` result of a function, and not the function itself.`
+
 1213.      1            );
+
 1214.      1            break;
+
 1215.     18        case "wrap_regexp":
+
 1216.     18            mm = `Wrap this regexp in parens to avoid confusion.`;
+
 1217.     18            break;
+
 1218.      1        case "wrap_unary":
+
 1219.      1            mm = `Wrap the unary expression in parens.`;
+
 1220.      1            break;
+
 1221.   1394        }
+
 1222.   1394
+
 1223.   1394// Validate mm.
+
 1224.   1394
+
 1225.   1394        jslint_assert(mm, code);
+
 1226.   1394        warning.message = mm;
+
 1227.   1394
+
 1228.   1394// PR-242 - Include stack_trace for jslint to debug itself for errors.
+
 1229.   1394
+
 1230.      6        if (option_dict.trace) {
+
 1231.      6            warning.stack_trace = new Error().stack;
+
 1232.      6        }
+
 1233.     41        if (warning.directive_ignore_line) {
+
 1234.     41
+
 1235.     41// test_cause:
+
 1236.     41// ["0 //jslint-ignore-line", "semicolon", "directive_ignore_line", "", 0]
+
 1237.     41
+
 1238.     41            test_cause("directive_ignore_line");
+
 1239.     41            return warning;
+
 1240.   1353        }
+
 1241.   1353        warning_list.push(warning);
+
 1242.   1353        return warning;
+
 1243.   1353    }
+
 1244.    668
+
 1245.    668    try {
+
 1246.    668
+
 1247.    668// tokenize takes a source and produces from it an array of token objects.
+
 1248.    668// JavaScript is notoriously difficult to tokenize because of the horrible
+
 1249.    668// interactions between automatic semicolon insertion, regular expression
+
 1250.    668// literals, and now megastring literals. JSLint benefits from eliminating
+
 1251.    668// automatic semicolon insertion and nested megastring literals, which allows
+
 1252.    668// full tokenization to precede parsing.
+
 1253.    668
+
 1254.    668        option_dict = Object.assign(empty(), option_dict);
+
 1255.    668        Object.assign(state, {
+
 1256.    668            artifact,
+
 1257.    668            catch_list,
+
 1258.    668            catch_stack,
+
 1259.    668            directive_list,
+
 1260.    668            export_dict,
+
 1261.    668            function_list,
+
 1262.    668            function_stack,
+
 1263.    668            global_dict,
+
 1264.    668            global_list,
+
 1265.    668            import_list,
+
 1266.    668            is_equal,
+
 1267.    668            is_weird,
+
 1268.    668            line_list,
+
 1269.    668            mode_json: false,           // true if parsing JSON.
+
 1270.    668            mode_module: false,         // true if import or export was used.
+
 1271.    668            mode_property: false,       // true if directive /*property*/ is
+
 1272.    668                                        // ... used.
+
 1273.    668            mode_shebang: false,        // true if #! is seen on the first line.
+
 1274.    668            option_dict,
+
 1275.    668            property_dict,
+
 1276.    668            source,
+
 1277.    668            stop,
+
 1278.    668            stop_at,
+
 1279.    668            syntax_dict,
+
 1280.    668            tenure,
+
 1281.    668            test_cause,
+
 1282.    668            token_global,
+
 1283.    668            token_list,
+
 1284.    668            token_nxt: token_global,
+
 1285.    668            warn,
+
 1286.    668            warn_at,
+
 1287.    668            warning_list
+
 1288.    668        });
+
 1289.    668
+
 1290.    668// PHASE 1. Split <source> by newlines into <line_list>.
+
 1291.    668
+
 1292.    668        jslint_phase1_split(state);
+
 1293.    668        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
+
 1294.    668        jslint_assert(
+
 1295.    668            function_stack.length === 0,
+
 1296.    668            `function_stack.length === 0.`
+
 1297.    668        );
+
 1298.    668
+
 1299.    668// PHASE 2. Lex <line_list> into <token_list>.
+
 1300.    668
+
 1301.    668        jslint_phase2_lex(state);
+
 1302.    668        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
+
 1303.    668        jslint_assert(
+
 1304.    668            function_stack.length === 0,
+
 1305.    668            `function_stack.length === 0.`
+
 1306.    668        );
+
 1307.    668
+
 1308.    668// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
+
 1309.    668
+
 1310.    668        jslint_phase3_parse(state);
+
 1311.    668        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
+
 1312.    668        jslint_assert(
+
 1313.    668            function_stack.length === 0,
+
 1314.    668            `function_stack.length === 0.`
+
 1315.    668        );
+
 1316.    668
+
 1317.    668// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
+
 1318.    668//          recursive traversal. Each node may be processed on the way down
+
 1319.    668//          (preaction) and on the way up (postaction).
+
 1320.    668
+
 1321.    518        if (!state.mode_json) {
+
 1322.    518            jslint_phase4_walk(state);
+
 1323.    533        }
+
 1324.    533        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
+
 1325.    533        jslint_assert(
+
 1326.    533            function_stack.length === 0,
+
 1327.    533            `function_stack.length === 0.`
+
 1328.    533        );
+
 1329.    533
+
 1330.    533// PHASE 5. Check whitespace between tokens in <token_list>.
+
 1331.    533
+
 1332.    533        if (!state.mode_json && warning_list.length === 0) {
+
 1333.    208            jslint_phase5_whitage(state);
+
 1334.    533        }
+
 1335.    533        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
+
 1336.    533        jslint_assert(
+
 1337.    533            function_stack.length === 0,
+
 1338.    533            `function_stack.length === 0.`
+
 1339.    533        );
+
 1340.    533
+
 1341.    533// PR-347 - Disable warning "missing_browser".
+
 1342.    533//
+
 1343.    533//         if (!option_dict.browser) {
+
 1344.    533//             directive_list.forEach(function (comment) {
+
 1345.    533//                 if (comment.directive === "global") {
+
 1346.    533//
+
 1347.    533// // test_cause:
+
 1348.    533// // ["/*global aa*/", "jslint", "missing_browser", "(comment)", 1]
+
 1349.    533//
+
 1350.    533//                     warn("missing_browser", comment);
+
 1351.    533//                 }
+
 1352.    533//             });
+
 1353.    533//         }
+
 1354.    533
+
 1355.    533        if (option_dict.test_internal_error) {
+
 1356.      2            jslint_assert(undefined, "test_internal_error");
+
 1357.      2        }
+
 1358.    137    } catch (err) {
+
 1359.    137        mode_stop = true;
+
 1360.    137        err.message = "[JSLint was unable to finish] " + err.message;
+
 1361.    137        err.mode_stop = true;
+
 1362.    137        if (err.name !== "JSLintError") {
+
 1363.    137            Object.assign(err, {
+
 1364.    137                column: jslint_fudge,
+
 1365.    137                line: jslint_fudge,
+
 1366.    137                line_source: "",
+
 1367.    137                stack_trace: err.stack
+
 1368.    137            });
+
 1369.    137        }
+
 1370.    137        if (warning_list.indexOf(err) === -1) {
+
 1371.    137            warning_list.push(err);
+
 1372.    137        }
+
 1373.    137    }
+
 1374.    668
+
 1375.    668// Sort warning_list by mode_stop first, line, column respectively.
+
 1376.    668
+
 1377.    986    warning_list.sort(function (aa, bb) {
+
 1378.    986        return (
+
 1379.    986            Boolean(bb.mode_stop) - Boolean(aa.mode_stop)
+
 1380.    896            || aa.line - bb.line
+
 1381.    876            || aa.column - bb.column
+
 1382.    986        );
+
 1383.    986
+
 1384.    986// Update each warning with formatted_message ready-for-use by jslint_cli.
+
 1385.    986
+
 1386.   1164    }).map(function ({
+
 1387.   1164        column,
+
 1388.   1164        line,
+
 1389.   1164        line_source,
+
 1390.   1164        message,
+
 1391.   1164        stack_trace = ""
+
 1392.   1164    }, ii, list) {
+
 1393.   1164        list[ii].formatted_message = String(
+
 1394.   1164            String(ii + 1).padStart(2, " ")
+
 1395.   1164            + ". \u001b[31m" + message + "\u001b[39m"
+
 1396.   1164            + " \u001b[90m\/\/ line " + line + ", column " + column
+
 1397.   1164            + "\u001b[39m\n"
+
 1398.   1164            + ("    " + line_source.trim()).slice(0, 72) + "\n"
+
 1399.   1164            + stack_trace
+
 1400.   1164        ).trimRight();
+
 1401.   1164    });
+
 1402.    668
+
 1403.    668    return {
+
 1404.    668        causes: cause_dict,
+
 1405.    668        directives: directive_list,
+
 1406.    668        edition: jslint_edition,
+
 1407.    668        exports: export_dict,
+
 1408.    668        froms: import_list,
+
 1409.    668        functions: function_list,
+
 1410.    668        global: token_global,
+
 1411.    668        id: "(JSLint)",
+
 1412.    668        json: state.mode_json,
+
 1413.    668        lines: line_list,
+
 1414.    668        module: state.mode_module === true,
+
 1415.    164        ok: warning_list.length === 0 && !mode_stop,
+
 1416.    668        option: option_dict,
+
 1417.    668        property: property_dict,
+
 1418.    668        shebang: (
+
 1419.    668            state.mode_shebang
+
 1420.      1            ? line_list[jslint_fudge].line_source
+
 1421.    667            : undefined
+
 1422.    668        ),
+
 1423.    668        stop: mode_stop,
+
 1424.    668        tokens: token_list,
+
 1425.    668        tree: state.token_tree,
+
 1426.    668        warnings: warning_list
+
 1427.    668    };
+
 1428.    668}
+
 1429.      1
+
 1430.      1// PR-362 - Add API Doc.
+
 1431.      1
+
 1432.      1async function jslint_apidoc({
+
 1433.      1    example_list,
+
 1434.      1    github_repo,
+
 1435.      1    module_list,
+
 1436.      1    package_name,
+
 1437.      1    pathname,
+
 1438.      1    version
+
 1439.      1}) {
+
 1440.      1
+
 1441.      1// This function will create API Doc from <module_list>.
+
 1442.      1
+
 1443.      1    let elem_ii = 0;
+
 1444.      1    let html;
+
 1445.      1
+
 1446.     27    function elem_create(moduleObj, key, moduleName) {
+
 1447.     27
+
 1448.     27// This function will create a sub API Doc from elem <moduleObj>[<key>].
+
 1449.     27
+
 1450.     27        let example = "N/A";
+
 1451.     27        let id = encodeURIComponent("apidoc.elem." + moduleName + "." + key);
+
 1452.     27        let name;
+
 1453.     27        let signature;
+
 1454.     27        let source;
+
 1455.     27        name = htmlEscape((typeof moduleObj[key]) + " " + key);
+
 1456.      2        if (typeof moduleObj[key] !== "function") {
+
 1457.      2            return {
+
 1458.      2                name,
+
 1459.      2                signature: (`
+
 1460.      2<a class="apidocElementLiA" href="#${id}">
+
 1461.      2${name}
+
 1462.      2</a>
+
 1463.      2                `),
+
 1464.      2                source: (`
+
 1465.      2<li>
+
 1466.      2    <h2>
+
 1467.      2    <a href="#${id}" id="${id}">
+
 1468.      2    ${name}
+
 1469.      2    </a>
+
 1470.      2    </h2>
+
 1471.      2</li>
+
 1472.      2                `)
+
 1473.      2            };
+
 1474.     25        }
+
 1475.     25        // init source
+
 1476.     25        source = htmlEscape(trim_start(moduleObj[key].toString()));
+
 1477.     25        // init signature
+
 1478.     25        source = source.replace((
+
 1479.     25            /(\([\S\s]*?\)) \{/
+
 1480.     25        ), function (match0, match1) {
+
 1481.     25            signature = htmlEscape(
+
 1482.     25                match1.replace((
+
 1483.     25                    / *?\/\*[\S\s]*?\*\/ */g
+
 1484.     25                ), "").replace((
+
 1485.     25                    / *?\/\/.*/g
+
 1486.     25                ), "").replace((
+
 1487.     25                    /\n{2,}/g
+
 1488.     25                ), "\n")
+
 1489.     25            );
+
 1490.     25            return match0;
+
 1491.     25        });
+
 1492.     25        // init comment
+
 1493.     25        source = source.replace((
+
 1494.     25            /\n(?:\/\/.*?\n)+\n/
+
 1495.     25        ), "<span class=\"apidocCodeCommentSpan\">$&</span>");
+
 1496.     25        // init example
+
 1497.     56        example_list.some(function (example2) {
+
 1498.     56            example2.replace(
+
 1499.     56                new RegExp((
+
 1500.     56                    "((?:\\n.*?){8}(function )?)\\b"
+
 1501.     56                    + key
+
 1502.     56                    + "(\\((?:.*?\\n){8})"
+
 1503.     56                ), "g"),
+
 1504.    132                function (ignore, header, isDeclaration, footer) {
+
 1505.    124                    if (!isDeclaration) {
+
 1506.    124                        example = "..." + trim_start(
+
 1507.    124                            htmlEscape(header)
+
 1508.    124                            + "<span class=\"apidocCodeKeywordSpan\">"
+
 1509.    124                            + htmlEscape(key)
+
 1510.    124                            + "</span>"
+
 1511.    124                            + htmlEscape(footer)
+
 1512.    124                        ).trimEnd() + "\n...";
+
 1513.    124                    }
+
 1514.    132                    return "";
+
 1515.    132                }
+
 1516.     56            );
+
 1517.     56            return example !== "N/A";
+
 1518.     56        });
+
 1519.     25        if (source.length > 2048) {
+
 1520.     11            source = source.slice(0, 2048) + "...\n}\n";
+
 1521.     25        }
+
 1522.     25        return {
+
 1523.     25            name,
+
 1524.     25            signature: (`
+
 1525.     25<a class="apidocElementLiA" href="#${id}">
+
 1526.     25${name}<span class="apidocSignatureSpan">${signature}</span>
+
 1527.     25</a>
+
 1528.     25            `),
+
 1529.     25            source: (`
+
 1530.     25<li>
+
 1531.     25    <h2>
+
 1532.     25    <a href="#${id}" id="${id}">
+
 1533.     25    ${name}<span class="apidocSignatureSpan">${signature}</span>
+
 1534.     25    </a>
+
 1535.     25    </h2>
+
 1536.     25</li>
+
 1537.     25<li>Description and source-code:<pre class="apidocCodePre">${source}</pre></li>
+
 1538.     25<li>Example usage:<pre class="apidocCodePre">${example}</pre></li>
+
 1539.     25            `)
+
 1540.     25        };
+
 1541.     25    }
+
 1542.      1
+
 1543.    149    function trim_start(str) {
+
 1544.    149
+
 1545.    149// This function will normalize whitespace before <str>.
+
 1546.    149
+
 1547.    149        let whitespace = "";
+
 1548.    149        str.trim().replace((
+
 1549.    149            /^ */gm
+
 1550.  13071        ), function (match0) {
+
 1551.   8412            if (whitespace === "" || match0.length < whitespace.length) {
+
 1552.   6610                whitespace = match0;
+
 1553.   6610            }
+
 1554.  13071            return "";
+
 1555.  13071        });
+
 1556.    149        str = str.replace(new RegExp("^" + whitespace, "gm"), "");
+
 1557.    149        return str;
+
 1558.    149    }
+
 1559.      1    await moduleFsInit();
+
 1560.      1
+
 1561.      1// Html-escape params.
+
 1562.      1
+
 1563.      1    github_repo = htmlEscape(github_repo);
+
 1564.      1    package_name = htmlEscape(package_name);
+
 1565.      1    version = htmlEscape(version);
+
 1566.      1
+
 1567.      1// Init example_list.
+
 1568.      1
+
 1569.      3    example_list = await Promise.all(example_list.map(async function (file) {
+
 1570.      3
+
 1571.      3// This function will read example from given file.
+
 1572.      3
+
 1573.      3        let result = await moduleFs.promises.readFile(file, "utf8");
+
 1574.      3        result = (
+
 1575.      3            "\n\n\n\n\n\n\n\n"
+
 1576.      3            // bug-workaround - truncate example to manageable size
+
 1577.      3            + result.slice(0, 524288)
+
 1578.      3            + "\n\n\n\n\n\n\n\n"
+
 1579.      3        );
+
 1580.      3        result = result.replace((
+
 1581.      3            /\r\n*/g
+
 1582.      3        ), "\n");
+
 1583.      3        return result;
+
 1584.      3    }));
+
 1585.      1
+
 1586.      1// Init module_list.
+
 1587.      1
+
 1588.      1    module_list = await Promise.all(module_list.map(async function ({
+
 1589.      1        pathname
+
 1590.      1    }) {
+
 1591.      1        let moduleName = htmlEscape(JSON.stringify(pathname));
+
 1592.      1        let moduleObj = await import(pathname);
+
 1593.      1        if (moduleObj.default) {
+
 1594.      1            moduleObj = moduleObj.default;
+
 1595.      1        }
+
 1596.      1        return {
+
 1597.     27            elem_list: Object.keys(moduleObj).map(function (key) {
+
 1598.     27                return elem_create(moduleObj, key, moduleName);
+
 1599.     78            }).sort(function (aa, bb) {
+
 1600.     78                return (
+
 1601.     78                    aa.name < bb.name
+
 1602.     22                    ? -1
+
 1603.     56                    : 1
+
 1604.     78                );
+
 1605.     27            }).map(function (elem) {
+
 1606.     27                elem_ii += 1;
+
 1607.     27                elem.signature = elem.signature.replace(
+
 1608.     27                    ">",
+
 1609.     27                    ">" + elem_ii + ". "
+
 1610.     27                );
+
 1611.     27                elem.source = elem.source.replace(
+
 1612.     27                    "\">",
+
 1613.     27                    "\">" + elem_ii + ". "
+
 1614.     27                );
+
 1615.     27                return elem;
+
 1616.     27            }),
+
 1617.      1            id: encodeURIComponent("apidoc.module." + moduleName),
+
 1618.      1            moduleName
+
 1619.      1        };
+
 1620.      1    }));
+
 1621.      1    html = (`
+
 1622.      1<!DOCTYPE html>
+
 1623.      1<html lang="en">
+
 1624.      1<head>
+
 1625.      1<meta charset="utf-8">
+
 1626.      1<meta name="viewport" content="width=device-width, initial-scale=1">
+
 1627.      1<meta name="description" content="${package_name} API Doc">
+
 1628.      1<title>${package_name} apidoc</title>
+
 1629.      1<style>
+
 1630.      1/* jslint utility2:true */
+
 1631.      1/*csslint*/
+
 1632.      1body {
+
 1633.      1    margin: 0;
+
 1634.      1    padding: 20px;
+
 1635.      1}
+
 1636.      1.apidocCodeCommentSpan,
+
 1637.      1.apidocCodeKeywordSpan {
+
 1638.      1    background: royalblue;
+
 1639.      1    color: white;
+
 1640.      1}
+
 1641.      1.apidocCodeCommentSpan {
+
 1642.      1    display: block;
+
 1643.      1}
+
 1644.      1.apidocCodePre {
+
 1645.      1    background: #eef;
+
 1646.      1    border: 1px solid;
+
 1647.      1    font-size: 14px;
+
 1648.      1    overflow-wrap: break-word;
+
 1649.      1    padding: 5px;
+
 1650.      1    white-space: pre-wrap;
+
 1651.      1}
+
 1652.      1.apidocDiv {
+
 1653.      1    color: #555;
+
 1654.      1    font-family: sans-serif;
+
 1655.      1}
+
 1656.      1.apidocDiv a[href] {
+
 1657.      1    color: royalblue;
+
 1658.      1    text-decoration: none;
+
 1659.      1}
+
 1660.      1.apidocDiv a[href]:hover {
+
 1661.      1    text-decoration: underline;
+
 1662.      1}
+
 1663.      1.apidocDiv li a {
+
 1664.      1    display: inline-block;
+
 1665.      1    padding: 8px 0;
+
 1666.      1}
+
 1667.      1.apidocDiv ul {
+
 1668.      1    list-style: none;
+
 1669.      1    padding-left: 20px;
+
 1670.      1}
+
 1671.      1.apidocFooterDiv {
+
 1672.      1    margin-top: 20px;
+
 1673.      1    text-align: center;
+
 1674.      1}
+
 1675.      1.apidocModuleA {
+
 1676.      1    font-size: 24px;
+
 1677.      1    font-weight: bold;
+
 1678.      1}
+
 1679.      1.apidocSectionDiv {
+
 1680.      1    border-top: 1px solid;
+
 1681.      1    margin-top: 20px;
+
 1682.      1}
+
 1683.      1.apidocSignatureSpan {
+
 1684.      1    color: #666;
+
 1685.      1    white-space: pre-wrap;
+
 1686.      1}
+
 1687.      1</style>
+
 1688.      1</head>
+
 1689.      1<body>
+
 1690.      1<div class="apidocDiv">
+
 1691.      1<h1>API Doc for <a href="${github_repo}">${package_name} (${version})</a></h1>
+
 1692.      1<div class="apidocSectionDiv">
+
 1693.      1    <a href="#apidocTableOfContents1" id="apidocTableOfContents1">
+
 1694.      1        <h1>Table of Contents</h1>
+
 1695.      1    </a>
+
 1696.      1    <ul>
+
 1697.      1    `) + module_list.map(function ({
+
 1698.      1        elem_list,
+
 1699.      1        id,
+
 1700.      1        moduleName
+
 1701.      1    }) {
+
 1702.      1        return (
+
 1703.      1            (`
+
 1704.      1        <li>
+
 1705.      1            <a class="apidocModuleA" href="#${id}">Module ${moduleName}</a>
+
 1706.      1            <ul>
+
 1707.      1            `)
+
 1708.     27            + elem_list.map(function ({
+
 1709.     27                signature
+
 1710.     27            }) {
+
 1711.     27                return "<li>\n" + signature + "\n</li>\n";
+
 1712.     27            }).join("")
+
 1713.      1            + (`
+
 1714.      1            </ul>
+
 1715.      1        </li>
+
 1716.      1            `)
+
 1717.      1        );
+
 1718.      1    }).join("") + (`
+
 1719.      1    </ul>
+
 1720.      1</div>
+
 1721.      1    `) + module_list.map(function ({
+
 1722.      1        elem_list,
+
 1723.      1        id,
+
 1724.      1        moduleName
+
 1725.      1    }) {
+
 1726.      1        return (
+
 1727.      1            (`
+
 1728.      1<div class="apidocSectionDiv">
+
 1729.      1    <h1><a href="#${id}" id="${id}">Module ${moduleName}</a></h1>
+
 1730.      1    <ul>
+
 1731.      1            `)
+
 1732.     27            + elem_list.map(function ({
+
 1733.     27                source
+
 1734.     27            }) {
+
 1735.     27                return source;
+
 1736.     27            }).join("")
+
 1737.      1            + (`
+
 1738.      1    </ul>
+
 1739.      1</div>
+
 1740.      1            `)
+
 1741.      1        );
+
 1742.      1    }).join("") + (`
+
 1743.      1<div class="apidocFooterDiv">
+
 1744.      1    [
+
 1745.      1    This document was created with
+
 1746.      1    <a href="https://github.com/jslint-org/jslint">JSLint</a>
+
 1747.      1    ]
+
 1748.      1</div>
+
 1749.      1</div>
+
 1750.      1</body>
+
 1751.      1</html>
+
 1752.      1    `);
+
 1753.      1    html = html.trim().replace((
+
 1754.      1        / +?$/gm
+
 1755.      1    ), "") + "\n";
+
 1756.      1    await fsWriteFileWithParents(pathname, html);
+
 1757.      1}
+
 1758.      1
+
 1759. 102849function jslint_assert(condition, message) {
+
 1760. 102849
+
 1761. 102849// This function will throw <message> if <condition> is falsy.
+
 1762. 102849
+
 1763. 102847    if (condition) {
+
 1764. 102847        return condition;
+
 1765. 102847    }
+
 1766.      2    throw new Error(
+
 1767.      2        `This was caused by a bug in JSLint.
+
 1768.      2Please open an issue with this stack-trace (and possible example-code) at
+
 1769.      2https://github.com/jslint-org/jslint/issues.
+
 1770.      2edition = "${jslint_edition}";
+
 1771.      2${String(message).slice(0, 2000)}`
+
 1772.      2    );
+
 1773.      2}
+
 1774.      1
+
 1775.     38async function jslint_cli({
+
 1776.     38    console_error,
+
 1777.     38    console_log,
+
 1778.     38    file,
+
 1779.     38    import_meta_url,
+
 1780.     38    mode_cli,
+
 1781.     38    mode_noop,
+
 1782.     38    option,
+
 1783.     38    process_argv,
+
 1784.     38    process_env,
+
 1785.     38    process_exit,
+
 1786.     38    source
+
 1787.     38}) {
+
 1788.     38
+
 1789.     38// This function will run jslint from nodejs-cli.
+
 1790.     38
+
 1791.     38    let command;
+
 1792.     38    let data;
+
 1793.     38    let exit_code = 0;
+
 1794.     38    let mode_report;
+
 1795.     38    let mode_wrapper_vim;
+
 1796.     38    let result;
+
 1797.     38
+
 1798.     51    function jslint_from_file({
+
 1799.     51        code,
+
 1800.     51        file,
+
 1801.     51        line_offset = 0,
+
 1802.     51        mode_conditional,
+
 1803.     51        option = empty()
+
 1804.     51    }) {
+
 1805.     51        let result_from_file;
+
 1806.     51        if (
+
 1807.     51            mode_conditional
+
 1808.      5            && !(
+
 1809.      5                /^\/\*jslint\b/m
+
 1810.      5            ).test(code.slice(0, 65536))
+
 1811.      1        ) {
+
 1812.      1            return;
+
 1813.     50        }
+
 1814.     50        option = Object.assign(empty(), option, {
+
 1815.     50            file
+
 1816.     50        });
+
 1817.     50        switch ((
+
 1818.     50            /\.\w+?$|$/m
+
 1819.     50        ).exec(file)[0]) {
+
 1820.     50        case ".html":
+
 1821.      3
+
 1822.      3// Recursively jslint embedded "<script>\n...\n</script>".
+
 1823.      3
+
 1824.      3            code.replace((
+
 1825.      3                /^<script\b[^>]*?>\n([\S\s]*?\n)<\/script>$/gm
+
 1826.      3            ), function (ignore, match1, ii) {
+
 1827.      3                jslint_from_file({
+
 1828.      3                    code: match1,
+
 1829.      3                    file: file + ".<script>.js",
+
 1830.      3                    line_offset: string_line_count(code.slice(0, ii)) + 1,
+
 1831.      3                    option: Object.assign(empty(), {
+
 1832.      3                        browser: true
+
 1833.      3                    }, option)
+
 1834.      3                });
+
 1835.      3                return "";
+
 1836.      3            });
+
 1837.      3            return;
+
 1838.      2        case ".md":
+
 1839.      2
+
 1840.      2// Recursively jslint embedded "node --eval '\n...\n'".
+
 1841.      2
+
 1842.      2            jslint_node_eval({
+
 1843.      2                code,
+
 1844.      2                file,
+
 1845.      2                mode_conditional: true,
+
 1846.      2                option
+
 1847.      2            });
+
 1848.      2            return;
+
 1849.      2        case ".sh":
+
 1850.      2
+
 1851.      2// Recursively jslint embedded "node --eval '\n...\n'".
+
 1852.      2
+
 1853.      2            jslint_node_eval({
+
 1854.      2                code,
+
 1855.      2                file,
+
 1856.      2                option
+
 1857.      2            });
+
 1858.      2            return;
+
 1859.     43        default:
+
 1860.     43            result_from_file = jslint("\n".repeat(line_offset) + code, option);
+
 1861.     43        }
+
 1862.     43
+
 1863.     43// Print only first 10 warnings to stderr.
+
 1864.     43
+
 1865.     43        if (result_from_file.warnings.length > 0) {
+
 1866.      5            exit_code = 1;
+
 1867.      5            console_error(
+
 1868.      5                mode_wrapper_vim
+
 1869.      5
+
 1870.      5// PR-349 - Print warnings in format readable by vim.
+
 1871.      5
+
 1872.      5                ? result_from_file.warnings.slice(0, 10).map(function ({
+
 1873.      5                    column,
+
 1874.      5                    line,
+
 1875.      5                    message
+
 1876.      5                }, ii) {
+
 1877.      5                    return (
+
 1878.      5                        file
+
 1879.      5                        + ":" + ii
+
 1880.      5                        + ":" + line
+
 1881.      5                        + ":" + column
+
 1882.      5                        + ":" + message
+
 1883.      5                    );
+
 1884.      5                }).join("\n")
+
 1885.      5
+
 1886.      5// Print warnings in format readable by human.
+
 1887.      5
+
 1888.      5                : "\u001b[1mjslint " + file + "\u001b[22m\n"
+
 1889.     11                + result_from_file.warnings.slice(0, 10).map(function ({
+
 1890.     11                    formatted_message
+
 1891.     11                }) {
+
 1892.     11                    return formatted_message;
+
 1893.     11                }).join("\n")
+
 1894.      5            );
+
 1895.     43        }
+
 1896.     43        return result_from_file;
+
 1897.     43    }
+
 1898.     38
+
 1899.      4    function jslint_node_eval({
+
 1900.      4        code,
+
 1901.      4        file,
+
 1902.      4        mode_conditional,
+
 1903.      4        option = empty()
+
 1904.      4    }) {
+
 1905.      4        code.replace((
+
 1906.      4            /\bnode\b.*? (?:--eval|-e) '\n([\S\s]*?\n)'/gm
+
 1907.     26        ), function (ignore, match1, ii) {
+
 1908.     26            jslint_from_file({
+
 1909.     26                code: match1,
+
 1910.     26                file: file + ".<node -e>.js",
+
 1911.     26                line_offset: string_line_count(code.slice(0, ii)) + 1,
+
 1912.     26                mode_conditional,
+
 1913.     26                option: Object.assign(empty(), {
+
 1914.     26                    beta: Boolean(
+
 1915.     26                        process_env.JSLINT_BETA
+
 1916.     26                        && !(
+
 1917.     26                            /0|false|null|undefined/
+
 1918.     26                        ).test(process_env.JSLINT_BETA)
+
 1919.     26                    ),
+
 1920.     26                    node: true
+
 1921.     26                }, option)
+
 1922.     26            });
+
 1923.     26            return "";
+
 1924.     26        });
+
 1925.      4    }
+
 1926.     38
+
 1927.     28    function string_line_count(code) {
+
 1928.     28
+
 1929.     28// This function will count number of newlines in <code>.
+
 1930.     28
+
 1931.     28        let count;
+
 1932.     28        let ii;
+
 1933.     28
+
 1934.     28// https://jsperf.com/regexp-counting-2/8
+
 1935.     28
+
 1936.     28        count = 0;
+
 1937.     28        ii = 0;
+
 1938.  23104        while (true) {
+
 1939.  23104            ii = code.indexOf("\n", ii) + 1;
+
 1940.  23104            if (ii === 0) {
+
 1941.  23104                break;
+
 1942.  23104            }
+
 1943.  23104            count += 1;
+
 1944.  23104        }
+
 1945.     28        return count;
+
 1946.     28    }
+
 1947.     38
+
 1948.     38// PR-396 - window.jslint
+
 1949.     38// Check import.meta.url for directive to export jslint to window-object.
+
 1950.     38// Useful for ES5-era browser-scripts that rely on window.jslint,
+
 1951.     38// like CodeMirror.
+
 1952.     38//
+
 1953.     38// Example usage:
+
 1954.     38// <script type="module" src="./jslint.mjs?window_jslint=1"></script>
+
 1955.     38
+
 1956.     22    import_meta_url = import_meta_url || jslint_import_meta_url;
+
 1957.     38    if (
+
 1958.     38        jslint_rgx_url_search_window_jslint.test(import_meta_url)
+
 1959.      4        && (typeof globalThis === "object" && globalThis)
+
 1960.      4    ) {
+
 1961.      4        globalThis.jslint = jslint;
+
 1962.      4    }
+
 1963.     38
+
 1964.     38// Feature-detect nodejs.
+
 1965.     38
+
 1966.     38    if (!(
+
 1967.     38        (typeof process === "object" && process)
+
 1968.     38        && process.versions
+
 1969.     38        && typeof process.versions.node === "string"
+
 1970.     38        && !mode_noop
+
 1971.      1    )) {
+
 1972.      1        return exit_code;
+
 1973.     37    }
+
 1974.     37    console_error = console_error || console.error;
+
 1975.     37    console_log = console_log || console.log;
+
 1976.     22    process_argv = process_argv || process.argv;
+
 1977.     34    process_env = process_env || process.env;
+
 1978.     24    process_exit = process_exit || process.exit;
+
 1979.     37    await moduleFsInit();
+
 1980.     37    if (
+
 1981.     37        !(
+
 1982.     37
+
 1983.     37// Feature-detect nodejs-cli.
+
 1984.     37
+
 1985.     37            process.execArgv.indexOf("--eval") === -1
+
 1986.     37            && process.execArgv.indexOf("-e") === -1
+
 1987.     37            && (
+
 1988.     37                (
+
 1989.     37                    /[\/|\\]jslint(?:\.[cm]?js)?$/m
+
 1990.     37                ).test(process_argv[1])
+
 1991.     37                || mode_cli
+
 1992.     37            )
+
 1993.     20            && (
+
 1994.     20                moduleUrl.fileURLToPath(import_meta_url)
+
 1995.     20                ===
+
 1996.     20                modulePath.resolve(process_argv[1])
+
 1997.     20            )
+
 1998.     38        )
+
 1999.     22        && !mode_cli
+
 2000.     17    ) {
+
 2001.     17        return exit_code;
+
 2002.     20    }
+
 2003.     20
+
 2004.     20// init commmand
+
 2005.     20
+
 2006.     20    command = String(process_argv[2]).split("=");
+
 2007.     20    command[1] = command.slice(1).join("=");
+
 2008.     20
+
 2009.     20    switch (command[0]) {
+
 2010.     20
+
 2011.     20// PR-362 - Add API Doc.
+
 2012.     20
+
 2013.     20    case "jslint_apidoc":
+
 2014.      1        await jslint_apidoc(Object.assign(JSON.parse(process_argv[3]), {
+
 2015.      1            pathname: command[1]
+
 2016.      1        }));
+
 2017.      1        return;
+
 2018.     38
+
 2019.     38// PR-363 - Add command jslint_report.
+
 2020.     38
+
 2021.      6    case "jslint_report":
+
 2022.      6        mode_report = command[1];
+
 2023.      6        process_argv = process_argv.slice(1);
+
 2024.      6        break;
+
 2025.     38
+
 2026.     38// COMMIT-b26d6df2 - Add command jslint_wrapper_vim.
+
 2027.     38
+
 2028.      1    case "jslint_wrapper_vim":
+
 2029.      1        mode_wrapper_vim = true;
+
 2030.      1        process_argv = process_argv.slice(1);
+
 2031.      1        break;
+
 2032.     38
+
 2033.     38// PR-364 - Add command v8_coverage_report.
+
 2034.     38
+
 2035.      7    case "v8_coverage_report":
+
 2036.      7        await v8CoverageReportCreate({
+
 2037.      7            consoleError: console_error,
+
 2038.      7            coverageDir: command[1],
+
 2039.      7            processArgv: process_argv.slice(3)
+
 2040.      7        });
+
 2041.      7        return;
+
 2042.     12    }
+
 2043.     12
+
 2044.     12// Normalize file relative to process.cwd().
+
 2045.     12
+
 2046.     12    process_argv.slice(2).some(function (arg) {
+
 2047.     12        if (!arg.startsWith("-")) {
+
 2048.     12            file = file || arg;
+
 2049.     12            return true;
+
 2050.     12        }
+
 2051.     12    });
+
 2052.     12    if (!file) {
+
 2053.      1        return;
+
 2054.     11    }
+
 2055.     11    file = modulePath.resolve(file) + "/";
+
 2056.     11    if (file.startsWith(process.cwd() + "/")) {
+
 2057.     11        file = file.replace(process.cwd() + "/", "").slice(0, -1) || ".";
+
 2058.     11    }
+
 2059.     11    file = file.replace((
+
 2060.     11        /\\/g
+
 2061.     11    ), "/").replace((
+
 2062.     11        /\/$/g
+
 2063.     11    ), "");
+
 2064.     11    if (source) {
+
 2065.      7        data = source;
+
 2066.      7    } else {
+
 2067.      4
+
 2068.      4// jslint_cli - jslint directory.
+
 2069.      4
+
 2070.      4        try {
+
 2071.      4            data = await moduleFs.promises.readdir(file, "utf8");
+
 2072.      4        } catch (ignore) {}
+
 2073.      4        if (data) {
+
 2074.     34            await Promise.all(data.map(async function (file2) {
+
 2075.     34                let code;
+
 2076.     34                let time_start = Date.now();
+
 2077.     34                file2 = file + "/" + file2;
+
 2078.     34                switch ((
+
 2079.     34                    /\.\w+?$|$/m
+
 2080.     34                ).exec(file2)[0]) {
+
 2081.      4                case ".cjs":
+
 2082.      5                case ".html":
+
 2083.      8                case ".js":
+
 2084.     10                case ".json":
+
 2085.     12                case ".md":
+
 2086.     14                case ".mjs":
+
 2087.     16                case ".sh":
+
 2088.     16                    break;
+
 2089.     18                default:
+
 2090.     18                    return;
+
 2091.     16                }
+
 2092.     16                try {
+
 2093.     16                    code = await moduleFs.promises.readFile(file2, "utf8");
+
 2094.     15                } catch (ignore) {
+
 2095.      4                    return;
+
 2096.     15                }
+
 2097.     15                if (
+
 2098.     15                    (
+
 2099.     15                        /(?:\b|_)(?:lock|min|raw|rollup)(?:\b|_)/
+
 2100.     15                    ).test(file2)
+
 2101.     15                    || !(code && code.length < 1048576)
+
 2102.      4                ) {
+
 2103.      4                    return;
+
 2104.     14                }
+
 2105.     14                jslint_from_file({
+
 2106.     14                    code,
+
 2107.     14                    file: file2,
+
 2108.     14                    option
+
 2109.     14                });
+
 2110.     14                console_error(
+
 2111.     14                    "jslint - " + (Date.now() - time_start) + "ms - " + file2
+
 2112.     14                );
+
 2113.     14            }));
+
 2114.      4            process_exit(exit_code);
+
 2115.      4            return exit_code;
+
 2116.      4        }
+
 2117.      4
+
 2118.      4// jslint_cli - jslint file.
+
 2119.      4
+
 2120.      4        try {
+
 2121.      4            data = await moduleFs.promises.readFile(file, "utf8");
+
 2122.      4        } catch (err) {
+
 2123.      4            console_error(err);
+
 2124.      4            exit_code = 1;
+
 2125.      4            process_exit(exit_code);
+
 2126.      4            return exit_code;
+
 2127.      4        }
+
 2128.      9    }
+
 2129.      9    result = jslint_from_file({
+
 2130.      9        code: data,
+
 2131.      9        file,
+
 2132.      9        option
+
 2133.      9    });
+
 2134.      9    if (mode_report) {
+
 2135.      6        result = jslint.jslint_report(result);
+
 2136.      6        result = `<body class="JSLINT_ JSLINT_REPORT_">\n${result}</body>\n`;
+
 2137.      6        await fsWriteFileWithParents(mode_report, result);
+
 2138.      9    }
+
 2139.      9    process_exit(exit_code);
+
 2140.      9    return exit_code;
+
 2141.      9}
+
 2142.      1
+
 2143.    668function jslint_phase1_split() {
+
 2144.    668
+
 2145.    668// PHASE 1. Split <source> by newlines into <line_list>.
+
 2146.    668
+
 2147.    668    return;
+
 2148.    668}
+
 2149.      1
+
 2150.    668function jslint_phase2_lex(state) {
+
 2151.    668
+
 2152.    668// PHASE 2. Lex <line_list> into <token_list>.
+
 2153.    668
+
 2154.    668    let {
+
 2155.    668        artifact,
+
 2156.    668        directive_list,
+
 2157.    668        global_dict,
+
 2158.    668        global_list,
+
 2159.    668        line_list,
+
 2160.    668        option_dict,
+
 2161.    668        stop,
+
 2162.    668        stop_at,
+
 2163.    668        tenure,
+
 2164.    668        test_cause,
+
 2165.    668        token_global,
+
 2166.    668        token_list,
+
 2167.    668        warn,
+
 2168.    668        warn_at
+
 2169.    668    } = state;
+
 2170.    668    let char;                   // The current character being lexed.
+
 2171.    668    let column = 0;             // The column number of the next character.
+
 2172.    668    let from;                   // The starting column number of the token.
+
 2173.    668    let from_mega;              // The starting column of megastring.
+
 2174.    668    let line = 0;               // The line number of the next character.
+
 2175.    668    let line_disable;           // The starting line of "/*jslint-disable*/".
+
 2176.    668    let line_mega;              // The starting line of megastring.
+
 2177.    668    let line_source = "";       // The remaining line source string.
+
 2178.    668    let line_whole = "";        // The whole line source string.
+
 2179.    668    let mode_digits_empty_string = 1;
+
 2180.    668    let mode_digits_numeric_separator = 2;
+
 2181.    668    let mode_directive = true;  // true if directives are still allowed.
+
 2182.    668    let mode_mega = false;      // true if currently parsing a megastring
+
 2183.    668                                // ... literal.
+
 2184.    668    let mode_regexp;            // true if regular expression literal seen on
+
 2185.    668                                // ... this line.
+
 2186.    668    let paren_backtrack_list = [];      // List of most recent "(" tokens at any
+
 2187.    668                                        // ... paren-depth.
+
 2188.    668    let paren_depth = 0;        // Keeps track of current paren-depth.
+
 2189.    668    let snippet = "";           // A piece of string.
+
 2190.    668    let token_1;                // The first token.
+
 2191.    668    let token_prv = token_global;       // The previous token including
+
 2192.    668                                        // ... comments.
+
 2193.    668    let token_prv_expr = token_global;  // The previous token excluding
+
 2194.    668                                        // ... comments.
+
 2195.    668
+
 2196.    668// Most tokens, including the identifiers, operators, and punctuators, can be
+
 2197.    668// found with a regular expression. Regular expressions cannot correctly match
+
 2198.    668// regular expression literals, so we will match those the hard way. String
+
 2199.    668// literals and number literals can be matched by regular expressions, but they
+
 2200.    668// don't provide good warnings. The functions char_after, char_before,
+
 2201.    668// read_digits, and char_after_escape help in the parsing of literals.
+
 2202.    668
+
 2203. 238213    function char_after(match) {
+
 2204. 238213
+
 2205. 238213// Get the next character from the source line. Remove it from the line_source,
+
 2206. 238213// and append it to the snippet. Optionally check that the previous character
+
 2207. 238213// matched an expected value.
+
 2208. 238213
+
 2209.   5986        if (match !== undefined && char !== match) {
+
 2210.     10
+
 2211.     10// test_cause:
+
 2212.     10// ["aa=/[", "char_after", "expected_a", "]", 5]
+
 2213.     10// ["aa=/aa{/", "char_after", "expected_a_b", "/", 8]
+
 2214.     10
+
 2215.     10            return (
+
 2216.     10                char === ""
+
 2217.     10                ? stop_at("expected_a", line, column - 1, match)
+
 2218.     10                : stop_at("expected_a_b", line, column, match, char)
+
 2219.     10            );
+
 2220. 238203        }
+
 2221. 238203        char = line_source.slice(0, 1);
+
 2222. 238203        line_source = line_source.slice(1);
+
 2223. 238203        snippet += char || " ";
+
 2224. 238213        column += 1;
+
 2225. 238213        return char;
+
 2226. 238213    }
+
 2227.    668
+
 2228.   2953    function char_after_escape(extra) {
+
 2229.   2953
+
 2230.   2953// Validate char after escape "\\".
+
 2231.   2953
+
 2232.   2953        char_after("\\");
+
 2233.   2953        switch (char) {
+
 2234.      1        case "":
+
 2235.      1
+
 2236.      1// test_cause:
+
 2237.      1// ["\"\\", "char_after_escape", "unclosed_string", "", 2]
+
 2238.      1
+
 2239.      1            return stop_at("unclosed_string", line, column);
+
 2240.    219        case "/":
+
 2241.    219            return char_after();
+
 2242.    355        case "\\":
+
 2243.    355            return char_after();
+
 2244.      1        case "`":
+
 2245.      1            return char_after();
+
 2246.     62        case "b":
+
 2247.     62            return char_after();
+
 2248.      6        case "f":
+
 2249.      6            return char_after();
+
 2250.   1015        case "n":
+
 2251.   1015            return char_after();
+
 2252.     32        case "r":
+
 2253.     32            return char_after();
+
 2254.     24        case "t":
+
 2255.     24
+
 2256.     24// test_cause:
+
 2257.     24// ["\"\\/\\\\\\`\\b\\f\\n\\r\\t\"", "char_after_escape", "char_after", "", 0]
+
 2258.     24
+
 2259.     24            test_cause("char_after");
+
 2260.     24            return char_after();
+
 2261.    376        case "u":
+
 2262.    376            if (char_after("u") === "{") {
+
 2263.    376                if (state.mode_json) {
+
 2264.    376
+
 2265.    376// test_cause:
+
 2266.    376// ["[\"\\u{12345}\"]", "char_after_escape", "unexpected_a", "{", 5]
+
 2267.    376
+
 2268.    376                    warn_at("unexpected_a", line, column, char);
+
 2269.    376                }
+
 2270.    376                if (read_digits("x", undefined) > 5) {
+
 2271.    376
+
 2272.    376// test_cause:
+
 2273.    376// ["\"\\u{123456}\"", "char_after_escape", "too_many_digits", "", 11]
+
 2274.    376
+
 2275.    376                    warn_at("too_many_digits", line, column);
+
 2276.    376                }
+
 2277.    376                if (char !== "}") {
+
 2278.    376
+
 2279.    376// test_cause:
+
 2280.    376// ["\"\\u{12345\"", "char_after_escape", "expected_a_before_b", "\"", 10]
+
 2281.    376
+
 2282.    376                    stop_at("expected_a_before_b", line, column, "}", char);
+
 2283.    376                }
+
 2284.    376                return char_after();
+
 2285.    376            }
+
 2286.    376            char_before();
+
 2287.    376            if (read_digits("x", mode_digits_empty_string) < 4) {
+
 2288.    376
+
 2289.    376// test_cause:
+
 2290.    376// ["\"\\u0\"", "char_after_escape", "expected_four_digits", "", 5]
+
 2291.    376
+
 2292.    376                warn_at("expected_four_digits", line, column);
+
 2293.    376            }
+
 2294.    376            return;
+
 2295.    862        default:
+
 2296.    862            if (extra && extra.indexOf(char) >= 0) {
+
 2297.    862                return char_after();
+
 2298.    862            }
+
 2299.    862
+
 2300.    862// test_cause:
+
 2301.    862// ["\"\\0\"", "char_after_escape", "unexpected_a_before_b", "0", 3]
+
 2302.    862
+
 2303.    862            warn_at("unexpected_a_before_b", line, column, "\\", char);
+
 2304.   2953        }
+
 2305.   2953    }
+
 2306.    668
+
 2307.  10013    function char_before() {
+
 2308.  10013
+
 2309.  10013// Back up one character by moving a character from the end of the snippet to
+
 2310.  10013// the front of the line_source.
+
 2311.  10013
+
 2312.  10013        char = snippet.slice(-1);
+
 2313.  10013        line_source = char + line_source;
+
 2314.  10013        column -= char.length;
+
 2315.  10013
+
 2316.  10013// Remove last character from snippet.
+
 2317.  10013
+
 2318.  10013        snippet = snippet.slice(0, -1);
+
 2319.  10013        return char;
+
 2320.  10013    }
+
 2321.    668
+
 2322.   8924    function check_numeric_separator(digits, column) {
+
 2323.   8924
+
 2324.   8924// This function will check for illegal numeric-separator in <digits>.
+
 2325.   8924
+
 2326.   8924        digits.replace((
+
 2327.   8924            jslint_rgx_numeric_separator_illegal
+
 2328.      6        ), function (ignore, ii) {
+
 2329.      6
+
 2330.      6// test_cause:
+
 2331.      6// ["0x0_0_;", "check_numeric_separator", "illegal_num_separator", "", 6]
+
 2332.      6// ["0x0_0__0;", "check_numeric_separator", "illegal_num_separator", "", 6]
+
 2333.      6// ["aa=1_2_;", "check_numeric_separator", "illegal_num_separator", "", 7]
+
 2334.      6// ["aa=1_2__3;", "check_numeric_separator", "illegal_num_separator", "", 7]
+
 2335.      6// ["aa=1_2_n;", "check_numeric_separator", "illegal_num_separator", "", 7]
+
 2336.      6
+
 2337.      6            warn_at("illegal_num_separator", line, column + ii + 1);
+
 2338.      6            return "";
+
 2339.      6        });
+
 2340.   8924    }
+
 2341.    668
+
 2342.  11221    function lex_comment() {
+
 2343.  11221        let body;
+
 2344.  11221        let ii = 0;
+
 2345.  11221        let jj = 0;
+
 2346.  11221        let the_comment;
+
 2347.  11221
+
 2348.  11221// Create a comment object. Comments are not allowed in JSON text. Comments can
+
 2349.  11221// include directives and notices of incompletion.
+
 2350.  11221
+
 2351.  11221// Create token from comment //....
+
 2352.  11221
+
 2353.  11107        if (snippet === "//") {
+
 2354.  11107            snippet = line_source;
+
 2355.  11107            line_source = "";
+
 2356.  11107            the_comment = token_create("(comment)", snippet);
+
 2357.  11107            if (mode_mega) {
+
 2358.  11107
+
 2359.  11107// test_cause:
+
 2360.  11107// ["`${//}`", "lex_comment", "unexpected_comment", "`", 4]
+
 2361.  11107
+
 2362.  11107                warn("unexpected_comment", the_comment, "`");
+
 2363.  11107            }
+
 2364.  11107
+
 2365.  11107// Create token from comment /*...*/.
+
 2366.  11107
+
 2367.  11107        } else {
+
 2368.    114            snippet = [];
+
 2369.    114            if (line_source[0] === "/") {
+
 2370.    114
+
 2371.    114// test_cause:
+
 2372.    114// ["/*/", "lex_comment", "unexpected_a", "/", 2]
+
 2373.    114
+
 2374.    114                warn_at("unexpected_a", line, column + ii, "/");
+
 2375.    114            }
+
 2376.    114
+
 2377.    114// Lex/loop through each line until "*/".
+
 2378.    114
+
 2379.    696            while (true) {
+
 2380.    696                // jslint_rgx_star_slash
+
 2381.    696                ii = line_source.indexOf("*/");
+
 2382.    696                if (ii >= 0) {
+
 2383.    696                    break;
+
 2384.    696                }
+
 2385.    696                // jslint_rgx_slash_star
+
 2386.    696                ii = line_source.indexOf("/*");
+
 2387.    696                if (ii >= 0) {
+
 2388.    696
+
 2389.    696// test_cause:
+
 2390.    696// ["/*/*", "lex_comment", "nested_comment", "", 2]
+
 2391.    696
+
 2392.    696                    warn_at("nested_comment", line, column + ii);
+
 2393.    696                }
+
 2394.    696                snippet.push(line_source);
+
 2395.    696                line_source = read_line();
+
 2396.    696                if (line_source === undefined) {
+
 2397.    696
+
 2398.    696// test_cause:
+
 2399.    696// ["/*", "lex_comment", "unclosed_comment", "", 1]
+
 2400.    696
+
 2401.    696                    return stop_at("unclosed_comment", line, column);
+
 2402.    696                }
+
 2403.    696            }
+
 2404.    114            jj = line_source.slice(0, ii).search(
+
 2405.    114                jslint_rgx_slash_star_or_slash
+
 2406.    114            );
+
 2407.    114            if (jj >= 0) {
+
 2408.    114
+
 2409.    114// test_cause:
+
 2410.    114// ["/*/**/", "lex_comment", "nested_comment", "", 2]
+
 2411.    114
+
 2412.    114                warn_at("nested_comment", line, column + jj);
+
 2413.    114            }
+
 2414.    114            snippet.push(line_source.slice(0, ii));
+
 2415.    114            snippet = snippet.join(" ");
+
 2416.    114            column += ii + 2;
+
 2417.    114            line_source = line_source.slice(ii + 2);
+
 2418.    114            the_comment = token_create("(comment)", snippet);
+
 2419.  11218        }
+
 2420.  11218
+
 2421.  11218// Uncompleted work comment.
+
 2422.  11218
+
 2423.  11218        if (!option_dict.devel && jslint_rgx_todo.test(snippet)) {
+
 2424.     16
+
 2425.     16// test_cause:
+
 2426.     16// ["//todo", "lex_comment", "todo_comment", "(comment)", 1] //jslint-ignore-line
+
 2427.     16
+
 2428.     16            warn("todo_comment", the_comment);
+
 2429.  11218        }
+
 2430.  11218
+
 2431.  11218// Lex directives in comment.
+
 2432.  11218
+
 2433.  11218        [
+
 2434.  11218            the_comment.directive, body
+
 2435.  11218        ] = Array.from(snippet.match(jslint_rgx_directive) || []).slice(1);
+
 2436.  11141        if (the_comment.directive === undefined) {
+
 2437.  11141            return the_comment;
+
 2438.  11141        }
+
 2439.     77        directive_list.push(the_comment);
+
 2440.     77        if (!mode_directive) {
+
 2441.      1
+
 2442.      1// test_cause:
+
 2443.      1// ["0\n/*global aa*/", "lex_comment", "misplaced_directive_a", "global", 1]
+
 2444.      1
+
 2445.      1            warn_at("misplaced_directive_a", line, from, the_comment.directive);
+
 2446.      1            return the_comment;
+
 2447.     76        }
+
 2448.     76
+
 2449.     76// lex_directive();
+
 2450.     76// JSLint recognizes three directives that can be encoded in comments. This
+
 2451.     76// function processes one item, and calls itself recursively to process the
+
 2452.     76// next one.
+
 2453.     76
+
 2454.     76// Lex/loop through each directive in /*...*/
+
 2455.     76
+
 2456.     76        ii = 0;
+
 2457.   1835        body.replace(jslint_rgx_directive_part, function (
+
 2458.   1835            match0,
+
 2459.   1835            key,
+
 2460.   1835            val,
+
 2461.   1835            jj
+
 2462.   1835        ) {
+
 2463.     76            if (ii !== jj) {
+
 2464.     76
+
 2465.     76// test_cause:
+
 2466.     76// ["/*jslint !*/", "lex_comment", "bad_directive_a", "!", 1]
+
 2467.     76
+
 2468.     76                return stop("bad_directive_a", the_comment, body.slice(ii));
+
 2469.   1834            }
+
 2470.   1834            if (match0 === "") {
+
 2471.     76                return "";
+
 2472.   1759            }
+
 2473.   1759            ii += match0.length;
+
 2474.   1759            switch (the_comment.directive) {
+
 2475.   1759            case "global":
+
 2476.     76                if (val) {
+
 2477.     76
+
 2478.     76// test_cause:
+
 2479.     76// ["/*global aa:false*/", "lex_comment", "bad_option_a", "aa:false", 1]
+
 2480.     76
+
 2481.     76                    warn("bad_option_a", the_comment, key + ":" + val);
+
 2482.     76                }
+
 2483.     76                global_dict[key] = "user-defined";
+
 2484.     76
+
 2485.     76// PR-347 - Disable warning "unexpected_directive_a".
+
 2486.     76//
+
 2487.     76//                 state.mode_module = the_comment;
+
 2488.     76
+
 2489.     76                break;
+
 2490.     99            case "jslint":
+
 2491.     99                if (!option_set_item(key, val !== "false")) {
+
 2492.     99
+
 2493.     99// test_cause:
+
 2494.     99// ["/*jslint undefined*/", "lex_comment", "bad_option_a", "undefined", 1]
+
 2495.     99
+
 2496.     99                    warn("bad_option_a", the_comment, key);
+
 2497.     99                }
+
 2498.     99                break;
+
 2499.   1651            case "property":
+
 2500.   1651                state.mode_property = true;
+
 2501.   1651                tenure[key] = true;
+
 2502.   1651                break;
+
 2503.   1759            }
+
 2504.   1759            return "";
+
 2505.   1759        });
+
 2506.     76        return the_comment;
+
 2507.     76    }
+
 2508.    668
+
 2509.    789    function lex_megastring() {
+
 2510.    789        let id;
+
 2511.    789        let match;
+
 2512.    789
+
 2513.    789// The token is a megastring. We don't allow any kind of mega nesting.
+
 2514.    789
+
 2515.      1        if (mode_mega) {
+
 2516.      1
+
 2517.      1// test_cause:
+
 2518.      1// ["`${`", "lex_megastring", "expected_a_b", "`", 4]
+
 2519.      1
+
 2520.      1            return stop_at("expected_a_b", line, column, "}", "`");
+
 2521.    788        }
+
 2522.    788        from_mega = from;
+
 2523.    788        line_mega = line;
+
 2524.    788        mode_mega = true;
+
 2525.    788        snippet = "";
+
 2526.    788
+
 2527.    788// Parsing a mega literal is tricky. First create a ` token.
+
 2528.    788
+
 2529.    788        token_create("`");
+
 2530.    788        from += 1;
+
 2531.    788
+
 2532.    788// Then loop, building up a string, possibly from many lines, until seeing
+
 2533.    788// the end of file, a closing `, or a ${ indicting an expression within the
+
 2534.    788// string.
+
 2535.    788
+
 2536.   5963        while (true) {
+
 2537.   5963            match = line_source.match(jslint_rgx_mega) || {
+
 2538.   5963                "0": "",
+
 2539.   5963                index: 0
+
 2540.   5963            };
+
 2541.   5963            snippet += line_source.slice(0, match.index);
+
 2542.   5963            column += match.index;
+
 2543.   5963            line_source = line_source.slice(match.index);
+
 2544.   5963            match = match[0];
+
 2545.   5963            switch (match) {
+
 2546.   5963            case "${":
+
 2547.   5963
+
 2548.   5963// if either ` or ${ was found, then the preceding joins the snippet to become
+
 2549.   5963// a string token.
+
 2550.   5963
+
 2551.   5963                token_create("(string)", snippet).quote = "`";
+
 2552.   5963                snippet = "";
+
 2553.   5963
+
 2554.   5963// If ${, then create tokens that will become part of an expression until
+
 2555.   5963// a } token is made.
+
 2556.   5963
+
 2557.   5963                column += 2;
+
 2558.   5963                token_create("${");
+
 2559.   5963                line_source = line_source.slice(2);
+
 2560.   5963
+
 2561.   5963// Lex/loop through each token inside megastring-expression `${...}`.
+
 2562.   5963
+
 2563.   5963                while (true) {
+
 2564.   5963                    id = lex_token().id;
+
 2565.   5963                    if (id === "{") {
+
 2566.   5963
+
 2567.   5963// test_cause:
+
 2568.   5963// ["`${{", "lex_megastring", "expected_a_b", "{", 4]
+
 2569.   5963
+
 2570.   5963                        return stop_at("expected_a_b", line, column, "}", "{");
+
 2571.   5963                    }
+
 2572.   5963                    if (id === "}") {
+
 2573.   5963                        break;
+
 2574.   5963                    }
+
 2575.   5963                }
+
 2576.   5963                break;
+
 2577.   5963            case "\\":
+
 2578.   5963                snippet += line_source.slice(0, 2);
+
 2579.   5963                line_source = line_source.slice(2);
+
 2580.   5963                column += 2;
+
 2581.   5963                break;
+
 2582.   5963            case "`":
+
 2583.   5963
+
 2584.   5963// if either ` or ${ was found, then the preceding joins the snippet to become
+
 2585.   5963// a string token.
+
 2586.   5963
+
 2587.   5963                token_create("(string)", snippet).quote = "`";
+
 2588.   5963                snippet = "";
+
 2589.   5963
+
 2590.   5963// Terminate megastring with `.
+
 2591.   5963
+
 2592.   5963                line_source = line_source.slice(1);
+
 2593.   5963                column += 1;
+
 2594.   5963                mode_mega = false;
+
 2595.   5963                return token_create("`");
+
 2596.   5963            default:
+
 2597.   5963
+
 2598.   5963// If neither ` nor ${ is seen, then the whole line joins the snippet.
+
 2599.   5963
+
 2600.   5963                snippet += line_source + "\n";
+
 2601.   5963                if (read_line() === undefined) {
+
 2602.   5963
+
 2603.   5963// test_cause:
+
 2604.   5963// ["`", "lex_megastring", "unclosed_mega", "", 1]
+
 2605.   5963
+
 2606.   5963                    return stop_at("unclosed_mega", line_mega, from_mega);
+
 2607.   5963                }
+
 2608.   5963            }
+
 2609.   5963        }
+
 2610.    789    }
+
 2611.    668
+
 2612.   8851    function lex_number() {
+
 2613.   8851        let prefix = snippet;
+
 2614.   8851
+
 2615.   8851// PR-390 - Add numeric-separator check.
+
 2616.   8851
+
 2617.   8851        check_numeric_separator(prefix, column - prefix.length);
+
 2618.   8851        char_after();
+
 2619.   2880        switch (prefix === "0" && char) {
+
 2620.      3        case "b":
+
 2621.      6        case "o":
+
 2622.     33        case "x":
+
 2623.     33            read_digits(char, mode_digits_numeric_separator);
+
 2624.     33
+
 2625.     33// PR-351 - Ignore BigInt suffix 'n'.
+
 2626.     33
+
 2627.     33            if (char === "n") {
+
 2628.     33                char_after("n");
+
 2629.     33            }
+
 2630.     33            break;
+
 2631.   8818        default:
+
 2632.   8818            if (char === ".") {
+
 2633.   8818                read_digits("d", mode_digits_numeric_separator);
+
 2634.   8818            }
+
 2635.   8818            if (char === "E" || char === "e") {
+
 2636.   8818                char_after(char);
+
 2637.   8818                if (char !== "+" && char !== "-") {
+
 2638.   8818                    char_before();
+
 2639.   8818                }
+
 2640.   8818                read_digits("d", mode_digits_numeric_separator);
+
 2641.   8818            }
+
 2642.   8851        }
+
 2643.   8851
+
 2644.   8851// If the next character after a number is a digit or letter, then something
+
 2645.   8851// unexpected is going on.
+
 2646.   8851
+
 2647.   8851        if (
+
 2648.   1567            (char >= "0" && char <= "9")
+
 2649.     44            || (char >= "a" && char <= "z")
+
 2650.   8850            || (char >= "A" && char <= "Z")
+
 2651.      1        ) {
+
 2652.      1
+
 2653.      1// test_cause:
+
 2654.      1// ["0a", "lex_number", "unexpected_a_after_b", "0", 2]
+
 2655.      1
+
 2656.      1            return stop_at(
+
 2657.      1                "unexpected_a_after_b",
+
 2658.      1                line,
+
 2659.      1                column,
+
 2660.      1                snippet.slice(-1),
+
 2661.      1                snippet.slice(0, -1)
+
 2662.      1            );
+
 2663.   8850        }
+
 2664.   8850        char_before();
+
 2665.   8850        return token_create("(number)", snippet);
+
 2666.   8850    }
+
 2667.    668
+
 2668.    583    function lex_regexp() {
+
 2669.    583
+
 2670.    583// Regexp
+
 2671.    583// Lex a regular expression literal.
+
 2672.    583
+
 2673.    583        let flag;
+
 2674.    583        let mode_regexp_multiline;
+
 2675.    583        let result;
+
 2676.    583        let value;
+
 2677.    583        mode_regexp = true;
+
 2678.    583
+
 2679.    209        function lex_regexp_bracketed() {
+
 2680.    209            let mode_regexp_range;
+
 2681.    209
+
 2682.    209// RegExp
+
 2683.    209// Match a class.
+
 2684.    209
+
 2685.    209            char_after("[");
+
 2686.     35            if (char === "^") {
+
 2687.     35                char_after("^");
+
 2688.     35            }
+
 2689.    902            while (true) {
+
 2690.    902
+
 2691.    902// RegExp
+
 2692.    902// Match a character in a character class.
+
 2693.    902
+
 2694.    902                switch (char) {
+
 2695.    902                case "":
+
 2696.    902                case "]":
+
 2697.    902
+
 2698.    902// test_cause:
+
 2699.    902// ["aa=/[", "lex_regexp_bracketed", "closer", "", 0]
+
 2700.    902// ["aa=/[]/", "lex_regexp_bracketed", "closer", "", 0]
+
 2701.    902
+
 2702.    902                    test_cause("closer");
+
 2703.    902                    if (mode_regexp_range) {
+
 2704.    902
+
 2705.    902// test_cause:
+
 2706.    902// ["aa=/[0-]/", "lex_regexp_bracketed", "unexpected_a", "-", 7]
+
 2707.    902
+
 2708.    902                        warn_at("unexpected_a", line, column - 1, "-");
+
 2709.    902                    }
+
 2710.    902                    return char_after("]");
+
 2711.    902
+
 2712.    902// PR-362 - Relax regexp-warning against using <space>.
+
 2713.    902//
+
 2714.    902//                 case " ":
+
 2715.    902//
+
 2716.    902// // test_cause:
+
 2717.    902// // ["aa=/[ ]/", "lex_regexp_bracketed", "expected_a_b", " ", 6]
+
 2718.    902//
+
 2719.    902//                     warn_at("expected_a_b", line, column, "\\u0020", " ");
+
 2720.    902//                     break;
+
 2721.    902
+
 2722.    902                case "-":
+
 2723.    902                case "/":
+
 2724.    902                case "[":
+
 2725.    902                case "^":
+
 2726.    902
+
 2727.    902// test_cause:
+
 2728.    902// ["aa=/[-]/", "lex_regexp_bracketed", "expected_a_before_b", "-", 6]
+
 2729.    902// ["aa=/[.^]/", "lex_regexp_bracketed", "expected_a_before_b", "^", 7]
+
 2730.    902// ["aa=/[/", "lex_regexp_bracketed", "expected_a_before_b", "/", 6]
+
 2731.    902// ["aa=/[\\\\/]/", "lex_regexp_bracketed", "expected_a_before_b", "/", 8]
+
 2732.    902// ["aa=/[\\\\[]/", "lex_regexp_bracketed", "expected_a_before_b", "[", 8]
+
 2733.    902
+
 2734.    902                    warn_at("expected_a_before_b", line, column, "\\", char);
+
 2735.    902                    break;
+
 2736.    902                case "\\":
+
 2737.    902                    char_after_escape("BbDdSsWw-[]^");
+
 2738.    902                    char_before();
+
 2739.    902                    break;
+
 2740.    902                case "`":
+
 2741.    902                    if (mode_mega) {
+
 2742.    902
+
 2743.    902// test_cause:
+
 2744.    902// ["`${/[`]/}`", "lex_regexp_bracketed", "unexpected_a", "`", 6]
+
 2745.    902
+
 2746.    902                        warn_at("unexpected_a", line, column, "`");
+
 2747.    902                    }
+
 2748.    902                    break;
+
 2749.    902                }
+
 2750.    902                char_after();
+
 2751.    902                mode_regexp_range = false;
+
 2752.    902                if (char === "-") {
+
 2753.    902
+
 2754.    902// RegExp
+
 2755.    902// Match a range of subclasses.
+
 2756.    902
+
 2757.    902                    mode_regexp_range = true;
+
 2758.    902                    char_after("-");
+
 2759.    902                }
+
 2760.    902            }
+
 2761.    209        }
+
 2762.    583
+
 2763.    794        function lex_regexp_group() {
+
 2764.    794
+
 2765.    794// RegExp
+
 2766.    794// Lex sequence of characters in regexp.
+
 2767.    794
+
 2768.    794            switch (char) {
+
 2769.      1            case "":
+
 2770.      1                warn_at("expected_regexp_factor_a", line, column, char);
+
 2771.      1                break;
+
 2772.      1            case ")":
+
 2773.      1                warn_at("expected_regexp_factor_a", line, column, char);
+
 2774.      1                break;
+
 2775.      1            case "]":
+
 2776.      1
+
 2777.      1// test_cause:
+
 2778.      1// ["/ /", "lex_regexp_group", "expected_regexp_factor_a", "", 3]
+
 2779.      1// ["aa=/)", "lex_regexp_group", "expected_regexp_factor_a", ")", 5]
+
 2780.      1// ["aa=/]", "lex_regexp_group", "expected_regexp_factor_a", "]", 5]
+
 2781.      1
+
 2782.      1                warn_at("expected_regexp_factor_a", line, column, char);
+
 2783.      1                break;
+
 2784.    794            }
+
 2785.   5592            while (true) {
+
 2786.   5592                switch (char) {
+
 2787.   5592                case "":
+
 2788.   5592                case ")":
+
 2789.   5592                case "/":
+
 2790.   5592                case "]":
+
 2791.   5592                    return;
+
 2792.   5592
+
 2793.   5592// PR-362 - Relax regexp-warning against using <space>.
+
 2794.   5592//
+
 2795.   5592//                 case " ":
+
 2796.   5592//
+
 2797.   5592// // test_cause:
+
 2798.   5592// // ["aa=/ /", "lex_regexp_group", "expected_a_b", " ", 5]
+
 2799.   5592//
+
 2800.   5592//                     warn_at("expected_a_b", line, column, "\\s", " ");
+
 2801.   5592//                     char_after();
+
 2802.   5592//                     break;
+
 2803.   5592
+
 2804.   5592                case "$":
+
 2805.   5592                    if (line_source[0] !== "/") {
+
 2806.   5592                        mode_regexp_multiline = true;
+
 2807.   5592                    }
+
 2808.   5592                    char_after();
+
 2809.   5592                    break;
+
 2810.   5592                case "(":
+
 2811.   5592
+
 2812.   5592// RegExp
+
 2813.   5592// Match a group that starts with left paren.
+
 2814.   5592
+
 2815.   5592                    char_after("(");
+
 2816.   5592                    switch (char) {
+
 2817.   5592                    case ":":
+
 2818.   5592
+
 2819.   5592// test_cause:
+
 2820.   5592// ["aa=/(:)/", "lex_regexp_group", "expected_a_before_b", ":", 6]
+
 2821.   5592// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5]
+
 2822.   5592
+
 2823.   5592                        warn_at("expected_a_before_b", line, column, "?", ":");
+
 2824.   5592                        break;
+
 2825.   5592                    case "?":
+
 2826.   5592                        char_after("?");
+
 2827.   5592                        switch (char) {
+
 2828.   5592                        case "!":
+
 2829.   5592
+
 2830.   5592// PR-437 - Add grammar for regexp-named-capture-group.
+
 2831.   5592
+
 2832.   5592                        case "<":
+
 2833.   5592                        case "=":
+
 2834.   5592                            char_after();
+
 2835.   5592                            break;
+
 2836.   5592                        default:
+
 2837.   5592                            char_after(":");
+
 2838.   5592                        }
+
 2839.   5592                        break;
+
 2840.   5592                    }
+
 2841.   5592
+
 2842.   5592// RegExp
+
 2843.   5592// Recurse lex_regexp_group().
+
 2844.   5592
+
 2845.   5592                    lex_regexp_group();
+
 2846.   5592                    char_after(")");
+
 2847.   5592                    break;
+
 2848.   5592                case "*":
+
 2849.   5592                case "+":
+
 2850.   5592                case "?":
+
 2851.   5592                case "{":
+
 2852.   5592                case "}":
+
 2853.   5592
+
 2854.   5592// test_cause:
+
 2855.   5592// ["aa=/+/", "lex_regexp_group", "expected_a_before_b", "+", 5]
+
 2856.   5592// ["aa=/.**/", "lex_regexp_group", "expected_a_before_b", "*", 7]
+
 2857.   5592// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5]
+
 2858.   5592// ["aa=/{/", "lex_regexp_group", "expected_a_before_b", "{", 5]
+
 2859.   5592// ["aa=/}/", "lex_regexp_group", "expected_a_before_b", "}", 5]
+
 2860.   5592
+
 2861.   5592                    warn_at("expected_a_before_b", line, column, "\\", char);
+
 2862.   5592                    char_after();
+
 2863.   5592                    break;
+
 2864.   5592                case "[":
+
 2865.   5592                    lex_regexp_bracketed();
+
 2866.   5592                    break;
+
 2867.   5592                case "\\":
+
 2868.   5592
+
 2869.   5592// test_cause:
+
 2870.   5592// ["aa=/\\/", "lex_regexp_group", "escape", "", 0]
+
 2871.   5592
+
 2872.   5592                    test_cause("escape");
+
 2873.   5592
+
 2874.   5592// PR-437 - Add grammar for regexp-named-backreference.
+
 2875.   5592
+
 2876.   5592                    char_after_escape("BbDdSsWw^${}[]():=!.|*+?k");
+
 2877.   5592                    break;
+
 2878.   5592                case "^":
+
 2879.   5592                    if (snippet !== "^") {
+
 2880.   5592                        mode_regexp_multiline = true;
+
 2881.   5592                    }
+
 2882.   5592                    char_after();
+
 2883.   5592                    break;
+
 2884.   5592                case "`":
+
 2885.   5592                    if (mode_mega) {
+
 2886.   5592
+
 2887.   5592// test_cause:
+
 2888.   5592// ["`${/`/}`", "lex_regexp_group", "unexpected_a", "`", 5]
+
 2889.   5592
+
 2890.   5592                        warn_at("unexpected_a", line, column, "`");
+
 2891.   5592                    }
+
 2892.   5592                    char_after();
+
 2893.   5592                    break;
+
 2894.   5592                default:
+
 2895.   5592                    char_after();
+
 2896.   5592                }
+
 2897.   5592
+
 2898.   5592// RegExp
+
 2899.   5592// Match an optional quantifier.
+
 2900.   5592
+
 2901.   5592                switch (char) {
+
 2902.   5592                case "*":
+
 2903.   5592                case "+":
+
 2904.   5592                    if (char_after(char) === "?") {
+
 2905.   5592
+
 2906.   5592// test_cause:
+
 2907.   5592// ["aa=/.*?/", "lex_regexp_group", "?", "", 0]
+
 2908.   5592// ["aa=/.+?/", "lex_regexp_group", "?", "", 0]
+
 2909.   5592
+
 2910.   5592                        test_cause("?");
+
 2911.   5592                        char_after("?");
+
 2912.   5592                    }
+
 2913.   5592                    break;
+
 2914.   5592                case "?":
+
 2915.   5592                    if (char_after("?") === "?") {
+
 2916.   5592
+
 2917.   5592// test_cause:
+
 2918.   5592// ["aa=/.??/", "lex_regexp_group", "unexpected_a", "?", 7]
+
 2919.   5592
+
 2920.   5592                        warn_at("unexpected_a", line, column, char);
+
 2921.   5592                        char_after("?");
+
 2922.   5592                    }
+
 2923.   5592                    break;
+
 2924.   5592                case "{":
+
 2925.   5592                    if (read_digits("d", mode_digits_empty_string) === 0) {
+
 2926.   5592
+
 2927.   5592// test_cause:
+
 2928.   5592// ["aa=/aa{/", "lex_regexp_group", "expected_a_before_b", ",", 8]
+
 2929.   5592
+
 2930.   5592                        warn_at("expected_a_before_b", line, column, "0", ",");
+
 2931.   5592                    }
+
 2932.   5592                    if (char === ",") {
+
 2933.   5592
+
 2934.   5592// test_cause:
+
 2935.   5592// ["aa=/.{,/", "lex_regexp_group", "comma", "", 0]
+
 2936.   5592
+
 2937.   5592                        test_cause("comma");
+
 2938.   5592                        read_digits("d", mode_digits_empty_string);
+
 2939.   5592                    }
+
 2940.   5592                    if (char_after("}") === "?") {
+
 2941.   5592
+
 2942.   5592// test_cause:
+
 2943.   5592// ["aa=/.{0}?/", "lex_regexp_group", "unexpected_a", "?", 9]
+
 2944.   5592
+
 2945.   5592                        warn_at("unexpected_a", line, column, char);
+
 2946.   5592                        char_after("?");
+
 2947.   5592                    }
+
 2948.   5592                    break;
+
 2949.   5592                }
+
 2950.   5592            }
+
 2951.    794        }
+
 2952.    583
+
 2953.    583// RegExp
+
 2954.    583// Scan the regexp literal. Give a warning if the first character is = because
+
 2955.    583// /= looks like a division assignment operator.
+
 2956.    583
+
 2957.    583        snippet = "";
+
 2958.    583        char_after();
+
 2959.      1        if (char === "=") {
+
 2960.      1
+
 2961.      1// test_cause:
+
 2962.      1// ["aa=/=/", "lex_regexp", "expected_a_before_b", "=", 5]
+
 2963.      1
+
 2964.      1            warn_at("expected_a_before_b", line, column, "\\", "=");
+
 2965.      1        }
+
 2966.    583        lex_regexp_group();
+
 2967.    583
+
 2968.    583// RegExp
+
 2969.    583// Remove last character from snippet.
+
 2970.    583
+
 2971.    583        snippet = snippet.slice(0, -1);
+
 2972.    583
+
 2973.    583// RegExp
+
 2974.    583// Make sure there is a closing slash.
+
 2975.    583
+
 2976.    583        value = snippet;
+
 2977.    583        char_after("/");
+
 2978.    583
+
 2979.    583// RegExp
+
 2980.    583// Create flag.
+
 2981.    583
+
 2982.    583        flag = empty();
+
 2983.    583        while (
+
 2984.    583
+
 2985.    583// Regexp
+
 2986.    583// char is a letter.
+
 2987.    583
+
 2988.    512            (char >= "a" && char <= "z\uffff")
+
 2989.    573            || (char >= "A" && char <= "Z\uffff")
+
 2990.    510        ) {
+
 2991.    510
+
 2992.    510// RegExp
+
 2993.    510// Process dangling flag letters.
+
 2994.    510
+
 2995.    510            switch (!flag[char] && char) {
+
 2996.    510            case "g":
+
 2997.    510                break;
+
 2998.    510            case "i":
+
 2999.    510                break;
+
 3000.    510            case "m":
+
 3001.    510                break;
+
 3002.    510            case "u":
+
 3003.    510                break;
+
 3004.    510            case "y":
+
 3005.    510
+
 3006.    510// test_cause:
+
 3007.    510// ["aa=/./gimuy", "lex_regexp", "flag", "", 0]
+
 3008.    510
+
 3009.    510                test_cause("flag");
+
 3010.    510                break;
+
 3011.    510            default:
+
 3012.    510
+
 3013.    510// test_cause:
+
 3014.    510// ["aa=/./gg", "lex_regexp", "unexpected_a", "g", 8]
+
 3015.    510// ["aa=/./z", "lex_regexp", "unexpected_a", "z", 7]
+
 3016.    510
+
 3017.    510                warn_at("unexpected_a", line, column, char);
+
 3018.    510            }
+
 3019.    510            flag[char] = true;
+
 3020.    510            char_after();
+
 3021.    573        }
+
 3022.    573        char_before();
+
 3023.    573        if (char === "/" || char === "*") {
+
 3024.      1
+
 3025.      1// test_cause:
+
 3026.      1// ["aa=/.//", "lex_regexp", "unexpected_a", "/", 3]
+
 3027.      1
+
 3028.      1            return stop_at("unexpected_a", line, from, char);
+
 3029.    572        }
+
 3030.    572        result = token_create("(regexp)", char);
+
 3031.    572        result.flag = flag;
+
 3032.    572        result.value = value;
+
 3033.    572        if (mode_regexp_multiline && !flag.m) {
+
 3034.      1
+
 3035.      1// test_cause:
+
 3036.      1// ["aa=/$^/", "lex_regexp", "missing_m", "", 7]
+
 3037.      1
+
 3038.      1            warn_at("missing_m", line, column);
+
 3039.    572        }
+
 3040.    572        return result;
+
 3041.    572    }
+
 3042.    668
+
 3043.    598    function lex_slash_or_regexp() {
+
 3044.    598
+
 3045.    598// The / can be a division operator or the beginning of a regular expression
+
 3046.    598// literal. It is not possible to know which without doing a complete parse.
+
 3047.    598// We want to complete the tokenization before we begin to parse, so we will
+
 3048.    598// estimate. This estimator can fail in some cases. For example, it cannot
+
 3049.    598// know if "}" is ending a block or ending an object literal, so it can
+
 3050.    598// behave incorrectly in that case; it is not meaningful to divide an
+
 3051.    598// object, so it is likely that we can get away with it. We avoided the worst
+
 3052.    598// cases by eliminating automatic semicolon insertion.
+
 3053.    598
+
 3054.    598        let the_token;
+
 3055.    598        switch (
+
 3056.    598            token_prv_expr.identifier
+
 3057.     18            && !token_prv_expr.dot
+
 3058.     15            && token_prv_expr.id
+
 3059.    598        ) {
+
 3060.      1        case "case":
+
 3061.      2        case "delete":
+
 3062.      3        case "in":
+
 3063.      4        case "instanceof":
+
 3064.      5        case "new":
+
 3065.      6        case "typeof":
+
 3066.      7        case "void":
+
 3067.      8        case "yield":
+
 3068.      8            the_token = lex_regexp();
+
 3069.      8
+
 3070.      8// test_cause:
+
 3071.      8// ["case /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6]
+
 3072.      8// ["delete /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8]
+
 3073.      8// ["in /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 4]
+
 3074.      8// ["instanceof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 12]
+
 3075.      8// ["new /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 5]
+
 3076.      8// ["typeof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8]
+
 3077.      8// ["void /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6]
+
 3078.      8// ["yield /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 7]
+
 3079.      8
+
 3080.      8            return stop("unexpected_a", the_token);
+
 3081.      1        case "return":
+
 3082.      1            return lex_regexp();
+
 3083.    589        }
+
 3084.    589        switch (!token_prv_expr.identifier && token_prv_expr.id.slice(-1)) {
+
 3085.      1        case "!":
+
 3086.      2        case "%":
+
 3087.      4        case "&":
+
 3088.      5        case "*":
+
 3089.      6        case "+":
+
 3090.      7        case "-":
+
 3091.      9        case "/":
+
 3092.     10        case ";":
+
 3093.     11        case "<":
+
 3094.     12        case ">":
+
 3095.     13        case "^":
+
 3096.     16        case "{":
+
 3097.     17        case "|":
+
 3098.     18        case "}":
+
 3099.     19        case "~":
+
 3100.     19            the_token = lex_regexp();
+
 3101.     19
+
 3102.     19// test_cause:
+
 3103.     19// ["!/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
+
 3104.     19// ["%/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
+
 3105.     19// ["&/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
+
 3106.     19// ["+/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
+
 3107.     19// ["-/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
+
 3108.     19// ["0 * /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5]
+
 3109.     19// ["0 / /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5]
+
 3110.     19// [";/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
+
 3111.     19// ["</./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
+
 3112.     19// [">/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
+
 3113.     19// ["^/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
+
 3114.     19// ["{/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
+
 3115.     19// ["|/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
+
 3116.     19// ["}/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
+
 3117.     19// ["~/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
+
 3118.     19
+
 3119.     19            warn("wrap_regexp", the_token);
+
 3120.     19            return the_token;
+
 3121.    514        case "(":
+
 3122.    515        case ",":
+
 3123.    516        case ":":
+
 3124.    553        case "=":
+
 3125.    554        case "?":
+
 3126.    555        case "[":
+
 3127.    555
+
 3128.    555// test_cause:
+
 3129.    555// ["(/./", "lex_slash_or_regexp", "recurse", "", 0]
+
 3130.    555// [",/./", "lex_slash_or_regexp", "recurse", "", 0]
+
 3131.    555// [":/./", "lex_slash_or_regexp", "recurse", "", 0]
+
 3132.    555// ["=/./", "lex_slash_or_regexp", "recurse", "", 0]
+
 3133.    555// ["?/./", "lex_slash_or_regexp", "recurse", "", 0]
+
 3134.    555// ["aa[/./", "lex_slash_or_regexp", "recurse", "", 0]
+
 3135.    555
+
 3136.    555            test_cause("recurse");
+
 3137.    555            return lex_regexp();
+
 3138.     15        }
+
 3139.     15        if (line_source[0] === "=") {
+
 3140.      1            column += 1;
+
 3141.      1            line_source = line_source.slice(1);
+
 3142.      1            snippet = "/=";
+
 3143.      1            warn_at("unexpected_a", line, column, "/=");
+
 3144.     15        }
+
 3145.     15        return token_create(snippet);
+
 3146.     15    }
+
 3147.    668
+
 3148.  24335    function lex_string(quote) {
+
 3149.  24335
+
 3150.  24335// Create a string token.
+
 3151.  24335
+
 3152.  24335        let the_token;
+
 3153.  24333        if (!option_dict.single && quote === "'") {
+
 3154.      2
+
 3155.      2// test_cause:
+
 3156.      2// ["''", "lex_string", "use_double", "", 1]
+
 3157.      2
+
 3158.      2            warn_at("use_double", line, column);
+
 3159.      2        }
+
 3160.  24335        snippet = "";
+
 3161.  24335        char_after();
+
 3162.  24335
+
 3163.  24335// Lex/loop through each character in "...".
+
 3164.  24335
+
 3165. 217055        while (true) {
+
 3166. 217055            switch (char) {
+
 3167. 217055            case "":
+
 3168. 217055
+
 3169. 217055// test_cause:
+
 3170. 217055// ["\"", "lex_string", "unclosed_string", "", 1]
+
 3171. 217055
+
 3172. 217055                return stop_at("unclosed_string", line, column);
+
 3173. 217055            case "\\":
+
 3174. 217055                char_after_escape(quote);
+
 3175. 217055                break;
+
 3176. 217055            case "`":
+
 3177. 217055                if (mode_mega) {
+
 3178. 217055
+
 3179. 217055// test_cause:
+
 3180. 217055// ["`${\"`\"}`", "lex_string", "unexpected_a", "`", 5]
+
 3181. 217055
+
 3182. 217055                    warn_at("unexpected_a", line, column, "`");
+
 3183. 217055                }
+
 3184. 217055                char_after("`");
+
 3185. 217055                break;
+
 3186. 217055            case quote:
+
 3187. 217055
+
 3188. 217055// Remove last character from snippet.
+
 3189. 217055
+
 3190. 217055                snippet = snippet.slice(0, -1);
+
 3191. 217055                the_token = token_create("(string)", snippet);
+
 3192. 217055                the_token.quote = quote;
+
 3193. 217055                return the_token;
+
 3194. 217055            default:
+
 3195. 217055                char_after();
+
 3196. 217055            }
+
 3197. 217055        }
+
 3198.  24335    }
+
 3199.    668
+
 3200. 252255    function lex_token() {
+
 3201. 252255        let match;
+
 3202. 252255
+
 3203. 252255// Lex/loop through each whitespace.
+
 3204. 252255
+
 3205. 376039        while (true) {
+
 3206. 376039
+
 3207. 376039// Lex/loop through each blank-line.
+
 3208. 376039
+
 3209. 376039            while (!line_source) {
+
 3210. 376039                line_source = read_line();
+
 3211. 376039                from = 0;
+
 3212. 376039                if (line_source === undefined) {
+
 3213. 376039                    return (
+
 3214. 376039                        mode_mega
+
 3215. 376039
+
 3216. 376039// test_cause:
+
 3217. 376039// ["`${//}`", "lex_token", "unclosed_mega", "", 1]
+
 3218. 376039
+
 3219. 376039                        ? stop_at("unclosed_mega", line_mega, from_mega)
+
 3220. 376039                        : line_disable !== undefined
+
 3221. 376039
+
 3222. 376039// test_cause:
+
 3223. 376039// ["/*jslint-disable*/", "lex_token", "unclosed_disable", "", 1]
+
 3224. 376039
+
 3225. 376039                        ? stop_at("unclosed_disable", line_disable)
+
 3226. 376039                        : token_create("(end)")
+
 3227. 376039                    );
+
 3228. 376039                }
+
 3229. 376039            }
+
 3230. 376039            from = column;
+
 3231. 376039            match = line_source.match(jslint_rgx_token);
+
 3232. 376039
+
 3233. 376039// match[1] token
+
 3234. 376039// match[2] whitespace
+
 3235. 376039// match[3] identifier
+
 3236. 376039// match[4] number
+
 3237. 376039// match[5] rest
+
 3238. 376039
+
 3239. 376039            if (!match) {
+
 3240. 376039
+
 3241. 376039// test_cause:
+
 3242. 376039// ["#", "lex_token", "unexpected_char_a", "#", 1]
+
 3243. 376039
+
 3244. 376039                return stop_at(
+
 3245. 376039                    "unexpected_char_a",
+
 3246. 376039                    line,
+
 3247. 376039                    column,
+
 3248. 376039                    line_source[0]
+
 3249. 376039                );
+
 3250. 376039            }
+
 3251. 376039            snippet = match[1];
+
 3252. 376039            column += snippet.length;
+
 3253. 376039            line_source = match[5];
+
 3254. 376039            if (!match[2]) {
+
 3255. 376039                break;
+
 3256. 376039            }
+
 3257. 376039        }
+
 3258. 251612
+
 3259. 251612// The token is an identifier.
+
 3260. 251612
+
 3261. 251612        if (match[3]) {
+
 3262.  67699            return token_create(snippet, undefined, true);
+
 3263. 183913        }
+
 3264. 183913
+
 3265. 183913// Create token from number.
+
 3266. 183913
+
 3267. 183913        if (match[4]) {
+
 3268.   8851            return lex_number();
+
 3269. 175062        }
+
 3270. 175062
+
 3271. 175062// Create token from string "..." or '...'.
+
 3272. 175062
+
 3273. 175062        if (snippet === "\"" || snippet === "'") {
+
 3274.  24335            return lex_string(snippet);
+
 3275. 150727        }
+
 3276. 150727
+
 3277. 150727// Create token from megastring `...`.
+
 3278. 150727
+
 3279. 150727        if (snippet === "`") {
+
 3280.    789            return lex_megastring();
+
 3281. 149938        }
+
 3282. 149938
+
 3283. 149938// Create token from comment /*...*/ or //....
+
 3284. 149938
+
 3285. 149938        if (snippet === "/*" || snippet === "//") {
+
 3286.  11221            return lex_comment();
+
 3287. 138717        }
+
 3288. 138717
+
 3289. 138717// Create token from slash /.
+
 3290. 138717
+
 3291. 138717        if (snippet === "/") {
+
 3292.    598            return lex_slash_or_regexp();
+
 3293. 138119        }
+
 3294. 138119        return token_create(snippet);
+
 3295. 138119    }
+
 3296.    668
+
 3297.   2667    function option_set_item(key, val) {
+
 3298.   2667
+
 3299.   2667// These are the options that are recognized in the option object or that may
+
 3300.   2667// appear in a /*jslint*/ directive. Most options will have a boolean value,
+
 3301.   2667// usually true. Some options will also predefine some number of global
+
 3302.   2667// variables.
+
 3303.   2667
+
 3304.   2667        switch (key) {
+
 3305.    659        case "beta":            // Enable experimental warnings.
+
 3306.    661        case "bitwise":         // Allow bitwise operator.
+
 3307.    668        case "browser":         // Assume browser environment.
+
 3308.    670        case "convert":         // Allow conversion operator.
+
 3309.    672        case "couch":           // Assume CouchDb environment.
+
 3310.    678        case "devel":           // Allow console.log() and friends.
+
 3311.   2014        case "ecma":            // Assume ECMAScript environment.
+
 3312.   2020        case "eval":            // Allow eval().
+
 3313.   2024        case "fart":            // Allow complex fat-arrow.
+
 3314.   2029        case "for":             // Allow for-statement.
+
 3315.   2035        case "getset":          // Allow get() and set().
+
 3316.   2040        case "indent2":         // Use 2-space indent.
+
 3317.   2042        case "long":            // Allow long lines.
+
 3318.   2089        case "node":            // Assume Node.js environment.
+
 3319.   2093        case "nomen":           // Allow weird property name.
+
 3320.   2095        case "single":          // Allow single-quote strings.
+
 3321.   2097        case "subscript":       // Allow identifier in subscript-notation.
+
 3322.   2602        case "test_cause":      // Test jslint's causes.
+
 3323.   2604        case "test_internal_error":     // Test jslint's internal-error
+
 3324.   2667                                        // ... handling-ability.
+
 3325.   2606        case "this":            // Allow 'this'.
+
 3326.   2609        case "trace":           // Include jslint stack-trace in warnings.
+
 3327.   2613        case "unordered":       // Allow unordered cases, params, properties,
+
 3328.   2667                                // ... variables, and exports.
+
 3329.   2617        case "variable":        // Allow unordered const and let declarations
+
 3330.   2667                                // ... that are not at top of function-scope.
+
 3331.   2619        case "white":           // Allow messy whitespace.
+
 3332.   2619            option_dict[key] = val;
+
 3333.   2619            break;
+
 3334.   2667
+
 3335.   2667// PR-404 - Alias "evil" to jslint-directive "eval" for backwards-compat.
+
 3336.   2667
+
 3337.      2        case "evil":
+
 3338.      2            return option_set_item("eval", val);
+
 3339.   2667
+
 3340.   2667// PR-404 - Alias "nomen" to jslint-directive "name" for backwards-compat.
+
 3341.   2667
+
 3342.      2        case "name":
+
 3343.      2            return option_set_item("nomen", val);
+
 3344.     44        default:
+
 3345.     44            return false;
+
 3346.   2619        }
+
 3347.   2619
+
 3348.   2619// Initialize global-variables.
+
 3349.   2619
+
 3350.   2619        switch (val && key) {
+
 3351.   2667
+
 3352.   2667// Assign global browser variables to global_dict.
+
 3353.   2667/*
+
 3354.   2667// /\*jslint beta, browser, devel*\/
+
 3355.   2667console.log(JSON.stringify(Object.keys(window).sort(), undefined, 4));
+
 3356.   2667*/
+
 3357.   2667
+
 3358.      6        case "browser":
+
 3359.      6            object_assign_from_list(global_dict, [
+
 3360.      6
+
 3361.      6// Shared with Node.js.
+
 3362.      6
+
 3363.      6                "AbortController",
+
 3364.      6                // "Buffer",
+
 3365.      6                // "Crypto",
+
 3366.      6                // "CryptoKey",
+
 3367.      6                "Event",
+
 3368.      6                "EventTarget",
+
 3369.      6                "MessageChannel",
+
 3370.      6                "MessageEvent",
+
 3371.      6                "MessagePort",
+
 3372.      6                // "Request",
+
 3373.      6                // "Response",
+
 3374.      6                // "SubtleCrypto",
+
 3375.      6                "TextDecoder",
+
 3376.      6                "TextEncoder",
+
 3377.      6                "URL",
+
 3378.      6                "URLSearchParams",
+
 3379.      6                "WebAssembly",
+
 3380.      6                // "__dirname",
+
 3381.      6                // "__filename",
+
 3382.      6                // "atob",
+
 3383.      6                // "btoa",
+
 3384.      6                // "clearImmediate",
+
 3385.      6                "clearInterval",
+
 3386.      6                "clearTimeout",
+
 3387.      6                // "console",
+
 3388.      6                // "crypto",
+
 3389.      6                // "exports",
+
 3390.      6                // "fetch",
+
 3391.      6                // "global",
+
 3392.      6                // "module",
+
 3393.      6                "performance",
+
 3394.      6                // "process",
+
 3395.      6                "queueMicrotask",
+
 3396.      6                // "require",
+
 3397.      6                // "setImmediate",
+
 3398.      6                "setInterval",
+
 3399.      6                "setTimeout",
+
 3400.      6
+
 3401.      6// Web worker only.
+
 3402.      6// https://github.com/mdn/content/blob/main/files/en-us/web/api
+
 3403.      6// /workerglobalscope/index.md
+
 3404.      6
+
 3405.      6                "importScripts",
+
 3406.      6
+
 3407.      6// Window.
+
 3408.      6
+
 3409.      6                "Blob",
+
 3410.      6                // "CharacterData",
+
 3411.      6                // "DocumentType",
+
 3412.      6                // "Element",
+
 3413.      6                // "Event",
+
 3414.      6                "FileReader",
+
 3415.      6                // "FontFace",
+
 3416.      6                "FormData",
+
 3417.      6                "IntersectionObserver",
+
 3418.      6                "MutationObserver",
+
 3419.      6                // "Storage",
+
 3420.      6                // "TextDecoder",
+
 3421.      6                // "TextEncoder",
+
 3422.      6                // "URL",
+
 3423.      6                "Worker",
+
 3424.      6                "XMLHttpRequest",
+
 3425.      6                // "caches",
+
 3426.      6                // "clearInterval",
+
 3427.      6                // "clearTimeout",
+
 3428.      6                "document",
+
 3429.      6                // "event",
+
 3430.      6                "fetch",
+
 3431.      6                // "history",
+
 3432.      6                "indexedDb",
+
 3433.      6                "localStorage",
+
 3434.      6                "location",
+
 3435.      6                // "name",
+
 3436.      6                "navigator",
+
 3437.      6                "postMessage",
+
 3438.      6                // "screen",
+
 3439.      6                "sessionStorage",
+
 3440.      6                // "setInterval",
+
 3441.      6                // "setTimeout",
+
 3442.      6                "structuredClone",
+
 3443.      6                "window"
+
 3444.      6            ], "browser");
+
 3445.      6            break;
+
 3446.   2667
+
 3447.   2667// https://docs.couchdb.org/en/stable/query-server/javascript.html#javascript
+
 3448.   2667
+
 3449.      2        case "couch":
+
 3450.      2            object_assign_from_list(global_dict, [
+
 3451.      2                "emit",
+
 3452.      2                "getRow",
+
 3453.      2                "isArray",
+
 3454.      2                "log",
+
 3455.      2                "provides",
+
 3456.      2                "registerType",
+
 3457.      2                "require",
+
 3458.      2                "send",
+
 3459.      2                "start",
+
 3460.      2                "sum",
+
 3461.      2                "toJSON"
+
 3462.      2            ], "CouchDb");
+
 3463.      2            break;
+
 3464.      6        case "devel":
+
 3465.      6            object_assign_from_list(global_dict, [
+
 3466.      6                "alert", "confirm", "console", "prompt"
+
 3467.      6            ], "development");
+
 3468.      6            break;
+
 3469.   2667
+
 3470.   2667// These are the globals that are provided by the language standard.
+
 3471.   2667// Assign global ECMAScript variables to global_dict.
+
 3472.   2667/*
+
 3473.   2667node --input-type=module --eval '
+
 3474.   2667// /\*jslint beta, node*\/
+
 3475.   2667import https from "https";
+
 3476.   2667(async function () {
+
 3477.   2667    let dict = {import: true};
+
 3478.   2667    let result = "";
+
 3479.   2667    await new Promise(function (resolve) {
+
 3480.   2667        https.get((
+
 3481.   2667            "https://raw.githubusercontent.com/mdn/content/main/files"
+
 3482.   2667            + "/en-us/web/javascript/reference/global_objects/index.md"
+
 3483.   2667        ), function (res) {
+
 3484.   2667            res.on("data", function (chunk) {
+
 3485.   2667                result += chunk;
+
 3486.   2667            }).on("end", resolve).setEncoding("utf8");
+
 3487.   2667        });
+
 3488.   2667    });
+
 3489.   2667    result.replace((
+
 3490.   2667        /\n- \{\{JSxRef\("(?:Global_Objects\/)?([^"\/]+?)"/g
+
 3491.   2667    ), function (ignore, key) {
+
 3492.   2667        if (globalThis.hasOwnProperty(key)) {
+
 3493.   2667            dict[key] = true;
+
 3494.   2667        }
+
 3495.   2667        return "";
+
 3496.   2667    });
+
 3497.   2667    console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4));
+
 3498.   2667}());
+
 3499.   2667'
+
 3500.   2667*/
+
 3501.   2667
+
 3502.   1336        case "ecma":
+
 3503.   1336            object_assign_from_list(global_dict, [
+
 3504.   1336                "AggregateError",
+
 3505.   1336                "Array",
+
 3506.   1336                "ArrayBuffer",
+
 3507.   1336                "Atomics",
+
 3508.   1336                "BigInt",
+
 3509.   1336                "BigInt64Array",
+
 3510.   1336                "BigUint64Array",
+
 3511.   1336                "Boolean",
+
 3512.   1336                "DataView",
+
 3513.   1336                "Date",
+
 3514.   1336                "Error",
+
 3515.   1336                "EvalError",
+
 3516.   1336                "Float32Array",
+
 3517.   1336                "Float64Array",
+
 3518.   1336                "Function",
+
 3519.   1336                "Infinity",
+
 3520.   1336                "Int16Array",
+
 3521.   1336                "Int32Array",
+
 3522.   1336                "Int8Array",
+
 3523.   1336                "Intl",
+
 3524.   1336                "JSON",
+
 3525.   1336                "Map",
+
 3526.   1336                "Math",
+
 3527.   1336                "NaN",
+
 3528.   1336                "Number",
+
 3529.   1336                "Object",
+
 3530.   1336                "Promise",
+
 3531.   1336                "Proxy",
+
 3532.   1336                "RangeError",
+
 3533.   1336                "ReferenceError",
+
 3534.   1336                "Reflect",
+
 3535.   1336                "RegExp",
+
 3536.   1336                "Set",
+
 3537.   1336                "SharedArrayBuffer",
+
 3538.   1336                "String",
+
 3539.   1336                "Symbol",
+
 3540.   1336                "SyntaxError",
+
 3541.   1336                "TypeError",
+
 3542.   1336                "URIError",
+
 3543.   1336                "Uint16Array",
+
 3544.   1336                "Uint32Array",
+
 3545.   1336                "Uint8Array",
+
 3546.   1336                "Uint8ClampedArray",
+
 3547.   1336                "WeakMap",
+
 3548.   1336                "WeakSet",
+
 3549.   1336                "WebAssembly",
+
 3550.   1336                "decodeURI",
+
 3551.   1336                "decodeURIComponent",
+
 3552.   1336                "encodeURI",
+
 3553.   1336                "encodeURIComponent",
+
 3554.   1336                "eval",
+
 3555.   1336                "globalThis",
+
 3556.   1336                "import",
+
 3557.   1336                "isFinite",
+
 3558.   1336                "isNaN",
+
 3559.   1336                "parseFloat",
+
 3560.   1336                "parseInt",
+
 3561.   1336                "undefined"
+
 3562.   1336            ], "ECMAScript");
+
 3563.   1336            break;
+
 3564.   2667
+
 3565.   2667// Assign global Node.js variables to global_dict.
+
 3566.   2667/*
+
 3567.   2667node --input-type=module --eval '
+
 3568.   2667// /\*jslint beta, node*\/
+
 3569.   2667import moduleHttps from "https";
+
 3570.   2667(async function () {
+
 3571.   2667    let dict = Object.create(null);
+
 3572.   2667    let result = "";
+
 3573.   2667    await new Promise(function (resolve) {
+
 3574.   2667        moduleHttps.get((
+
 3575.   2667            "https://raw.githubusercontent.com/nodejs/node/v16.x/doc/api"
+
 3576.   2667            + "/globals.md"
+
 3577.   2667        ), function (res) {
+
 3578.   2667            res.on("data", function (chunk) {
+
 3579.   2667                result += chunk;
+
 3580.   2667            }).on("end", resolve).setEncoding("utf8");
+
 3581.   2667        });
+
 3582.   2667    });
+
 3583.   2667    result.replace((
+
 3584.   2667        /\n(?:\* \[`|## |## Class: )`\w+/g
+
 3585.   2667    ), function (match0) {
+
 3586.   2667        dict[match0.split("`")[1]] = true;
+
 3587.   2667        return "";
+
 3588.   2667    });
+
 3589.   2667    console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4));
+
 3590.   2667}());
+
 3591.   2667'
+
 3592.   2667*/
+
 3593.   2667
+
 3594.     47        case "node":
+
 3595.     47            object_assign_from_list(global_dict, [
+
 3596.     47                "AbortController",
+
 3597.     47                "Buffer",
+
 3598.     47                // "Crypto",
+
 3599.     47                // "CryptoKey",
+
 3600.     47                "Event",
+
 3601.     47                "EventTarget",
+
 3602.     47                "MessageChannel",
+
 3603.     47                "MessageEvent",
+
 3604.     47                "MessagePort",
+
 3605.     47                // "Request",
+
 3606.     47                // "Response",
+
 3607.     47                // "SubtleCrypto",
+
 3608.     47                "TextDecoder",
+
 3609.     47                "TextEncoder",
+
 3610.     47                "URL",
+
 3611.     47                "URLSearchParams",
+
 3612.     47                "WebAssembly",
+
 3613.     47                "__dirname",
+
 3614.     47                "__filename",
+
 3615.     47                // "atob",
+
 3616.     47                // "btoa",
+
 3617.     47                "clearImmediate",
+
 3618.     47                "clearInterval",
+
 3619.     47                "clearTimeout",
+
 3620.     47                "console",
+
 3621.     47                // "crypto",
+
 3622.     47                "exports",
+
 3623.     47                // "fetch",
+
 3624.     47                "global",
+
 3625.     47                "module",
+
 3626.     47                "performance",
+
 3627.     47                "process",
+
 3628.     47                "queueMicrotask",
+
 3629.     47                "require",
+
 3630.     47                "setImmediate",
+
 3631.     47                "setInterval",
+
 3632.     47                "setTimeout"
+
 3633.     47            ], "Node.js");
+
 3634.     47            break;
+
 3635.   2619        }
+
 3636.   2619        return true;
+
 3637.   2619    }
+
 3638.    668
+
 3639.    469    function read_digits(base, mode) {
+
 3640.    469        let digits = line_source.match(
+
 3641.    469            base === "b"
+
 3642.      3            ? jslint_rgx_digits_bits
+
 3643.    466            : base === "o"
+
 3644.    466            ? jslint_rgx_digits_octals
+
 3645.    466            : base === "x"
+
 3646.    466            ? jslint_rgx_digits_hexs
+
 3647.    466            : jslint_rgx_digits_decimals
+
 3648.    469        )[0];
+
 3649.    469        if (
+
 3650.     77            (mode !== mode_digits_empty_string && digits.length === 0)
+
 3651.    468            || digits[0] === "_"
+
 3652.      2        ) {
+
 3653.      2
+
 3654.      2// test_cause:
+
 3655.      2// ["0x", "read_digits", "expected_digits_after_a", "0x", 2]
+
 3656.      2// ["0x_", "read_digits", "expected_digits_after_a", "0x", 2]
+
 3657.      2
+
 3658.      2            warn_at("expected_digits_after_a", line, column, snippet);
+
 3659.      2        }
+
 3660.    469
+
 3661.    469// PR-390 - Add numeric-separator check.
+
 3662.    469
+
 3663.     73        if (mode === mode_digits_numeric_separator) {
+
 3664.     73            check_numeric_separator(digits, column);
+
 3665.    396        } else if (digits.indexOf("_") >= 0) {
+
 3666.    396
+
 3667.    396// test_cause:
+
 3668.    396// ["\"\\u{1_2}\"", "read_digits", "illegal_num_separator", "", 6]
+
 3669.    396
+
 3670.    396            warn_at(
+
 3671.    396                "illegal_num_separator",
+
 3672.    396                line,
+
 3673.    396                column + digits.indexOf("_") + 1
+
 3674.    396            );
+
 3675.    396        }
+
 3676.    469        column += digits.length;
+
 3677.    469        line_source = line_source.slice(digits.length);
+
 3678.    469        snippet += digits;
+
 3679.    469        char_after();
+
 3680.    469        return digits.length;
+
 3681.    469    }
+
 3682.    668
+
 3683. 105193    function read_line() {
+
 3684. 105193
+
 3685. 105193// Put the next line of source in line_source. If the line contains tabs,
+
 3686. 105193// replace them with spaces and give a warning. Also warn if the line contains
+
 3687. 105193// unsafe characters or is too damn long.
+
 3688. 105193
+
 3689. 105193        if (
+
 3690. 105193            !option_dict.long
+
 3691. 105189            && line_whole.length > 80
+
 3692.     56            && line_disable === undefined
+
 3693.     56            && !state.mode_json
+
 3694.     23            && token_1
+
 3695.     23            && !mode_regexp
+
 3696.     13        ) {
+
 3697.     13
+
 3698.     13// test_cause:
+
 3699.     13// ["/////////////////////////////////////////////////////////////////////////////////", "read_line", "too_long", "", 1] //jslint-ignore-line
+
 3700.     13
+
 3701.     13            warn_at("too_long", line);
+
 3702.     13        }
+
 3703. 105193        column = 0;
+
 3704. 105193        line += 1;
+
 3705. 105193        mode_regexp = false;
+
 3706. 105193        line_source = undefined;
+
 3707. 105193        line_whole = "";
+
 3708.    645        if (line_list[line] === undefined) {
+
 3709.    645            return line_source;
+
 3710. 104548        }
+
 3711. 104548        line_source = line_list[line].line_source;
+
 3712. 104548        line_whole = line_source;
+
 3713. 104548
+
 3714. 104548// Scan each line for following ignore-directives:
+
 3715. 104548// "/*jslint-disable*/"
+
 3716. 104548// "/*jslint-enable*/"
+
 3717. 104548// "//jslint-ignore-line"
+
 3718. 104548
+
 3719. 104548        if (line_source === "/*jslint-disable*/") {
+
 3720.      5
+
 3721.      5// test_cause:
+
 3722.      5// ["/*jslint-disable*/", "read_line", "jslint_disable", "", 0]
+
 3723.      5
+
 3724.      5            test_cause("jslint_disable");
+
 3725.      5            line_disable = line;
+
 3726. 104543        } else if (line_source === "/*jslint-enable*/") {
+
 3727. 104543            if (line_disable === undefined) {
+
 3728. 104543
+
 3729. 104543// test_cause:
+
 3730. 104543// ["/*jslint-enable*/", "read_line", "unopened_enable", "", 1]
+
 3731. 104543
+
 3732. 104543                stop_at("unopened_enable", line);
+
 3733. 104543            }
+
 3734. 104543            line_disable = undefined;
+
 3735. 104543        } else if (
+
 3736. 104543            line_source.endsWith(" //jslint-ignore-line")
+
 3737. 104543            || line_source.endsWith(" //jslint-quiet")
+
 3738. 104543        ) {
+
 3739. 104543
+
 3740. 104543// test_cause:
+
 3741. 104543// ["0 //jslint-ignore-line", "read_line", "jslint_ignore_line", "", 0]
+
 3742. 104543
+
 3743. 104543            test_cause("jslint_ignore_line");
+
 3744. 104543            line_list[line].directive_ignore_line = true;
+
 3745. 104547        }
+
 3746. 104547        if (line_disable !== undefined) {
+
 3747.      9
+
 3748.      9// test_cause:
+
 3749.      9// ["/*jslint-disable*/\n0", "read_line", "line_disable", "", 0]
+
 3750.      9
+
 3751.      9            test_cause("line_disable");
+
 3752.      9            line_source = "";
+
 3753. 104547        }
+
 3754. 104547        // jslint_rgx_tab
+
 3755. 104547        if (line_source.indexOf("\t") >= 0) {
+
 3756.      3            if (!option_dict.white) {
+
 3757.      3
+
 3758.      3// test_cause:
+
 3759.      3// ["\t", "read_line", "use_spaces", "", 1]
+
 3760.      3
+
 3761.      3                warn_at("use_spaces", line, line_source.indexOf("\t") + 1);
+
 3762.      3            }
+
 3763.      3            line_source = line_source.replace(jslint_rgx_tab, " ");
+
 3764. 104547        }
+
 3765. 104547        if (!option_dict.white && line_source.endsWith(" ")) {
+
 3766.      2
+
 3767.      2// test_cause:
+
 3768.      2// [" ", "read_line", "unexpected_trailing_space", "", 1]
+
 3769.      2
+
 3770.      2            warn_at("unexpected_trailing_space", line, line_source.length - 1);
+
 3771. 104547        }
+
 3772. 104547        return line_source;
+
 3773. 104547    }
+
 3774.    668
+
 3775. 255241    function token_create(id, value, identifier) {
+
 3776. 255241
+
 3777. 255241// Create the token object and append it to token_list.
+
 3778. 255241
+
 3779. 255241        let the_token = {
+
 3780. 255241            from,
+
 3781. 255241            id,
+
 3782. 255241            identifier: Boolean(identifier),
+
 3783. 255241            line,
+
 3784. 255241            nr: token_list.length,
+
 3785. 255241            thru: column,
+
 3786. 255241            value
+
 3787. 255241        };
+
 3788. 255241        token_list.push(the_token);
+
 3789. 255241
+
 3790. 255241// Directives must appear before the first statement.
+
 3791. 255241
+
 3792. 244023        if (id !== "(comment)" && id !== ";") {
+
 3793. 228006            mode_directive = false;
+
 3794. 228006        }
+
 3795. 255241
+
 3796. 255241// If this token is an identifier that touches a preceding number, or
+
 3797. 255241// a "/", comment, or regular expression literal that touches a preceding
+
 3798. 255241// comment or regular expression literal, then give a missing space warning.
+
 3799. 255241// This warning is not suppressed by option_dict.white.
+
 3800. 255241
+
 3801. 255241        if (
+
 3802. 255241            token_prv.line === line
+
 3803. 185838            && token_prv.thru === from
+
 3804. 119910            && (id === "(comment)" || id === "(regexp)" || id === "/")
+
 3805.    125            && (token_prv.id === "(comment)" || token_prv.id === "(regexp)")
+
 3806.      1        ) {
+
 3807.      1
+
 3808.      1// test_cause:
+
 3809.      1// ["/**//**/", "token_create", "expected_space_a_b", "(comment)", 5]
+
 3810.      1
+
 3811.      1            warn(
+
 3812.      1                "expected_space_a_b",
+
 3813.      1                the_token,
+
 3814.      1                artifact(token_prv),
+
 3815.      1                artifact(the_token)
+
 3816.      1            );
+
 3817.      1        }
+
 3818.  11603        if (token_prv.id === "." && id === "(number)") {
+
 3819.      4
+
 3820.      4// test_cause:
+
 3821.      4// [".0", "token_create", "expected_a_before_b", ".", 1]
+
 3822.      4
+
 3823.      4            warn("expected_a_before_b", token_prv, "0", ".");
+
 3824.      4        }
+
 3825.  11603        if (token_prv_expr.id === "." && the_token.identifier) {
+
 3826.  11598            the_token.dot = true;
+
 3827.  11598        }
+
 3828. 255241
+
 3829. 255241// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart.
+
 3830. 255241// Farts are now detected by keeping a list of most recent "(" tokens at any
+
 3831. 255241// given depth. When a "=>" token is encountered, the most recent "(" token at
+
 3832. 255241// current depth is marked as a fart.
+
 3833. 255241
+
 3834. 255241        switch (id) {
+
 3835.  17591        case "(":
+
 3836.  17591            paren_backtrack_list[paren_depth] = the_token;
+
 3837.  17591            paren_depth += 1;
+
 3838.  17591            break;
+
 3839.  17590        case ")":
+
 3840.  17590            paren_depth -= 1;
+
 3841.  17590            break;
+
 3842.     16        case "=>":
+
 3843.     16            if (
+
 3844.     16                token_prv_expr.id === ")"
+
 3845.     16                && paren_backtrack_list[paren_depth]
+
 3846.     16            ) {
+
 3847.     16                paren_backtrack_list[paren_depth].fart = the_token;
+
 3848.     16            }
+
 3849.     16            break;
+
 3850. 255241        }
+
 3851. 255241
+
 3852. 255241// The previous token is used to detect adjacency problems.
+
 3853. 255241
+
 3854. 255241        token_prv = the_token;
+
 3855. 255241
+
 3856. 255241// The token_prv_expr token is a previous token that was not a comment.
+
 3857. 255241// The token_prv_expr token
+
 3858. 255241// is used to disambiguate "/", which can mean division or regular expression
+
 3859. 255241// literal.
+
 3860. 255241
+
 3861. 244023        if (token_prv.id !== "(comment)") {
+
 3862. 244023            token_prv_expr = token_prv;
+
 3863. 244023        }
+
 3864. 255241        return the_token;
+
 3865. 255241    }
+
 3866.    668
+
 3867.    668// Init global_dict and option_dict.
+
 3868.    668
+
 3869.    668    option_set_item("ecma", true);
+
 3870.   1896    Object.keys(option_dict).sort().forEach(function (key) {
+
 3871.   1896        option_set_item(key, option_dict[key] === true);
+
 3872.   1896    });
+
 3873.    668    object_assign_from_list(global_dict, global_list, "user-defined");
+
 3874.    668
+
 3875.    668// Scan first line for "#!" and ignore it.
+
 3876.    668
+
 3877.      1    if (line_list[jslint_fudge].line_source.startsWith("#!")) {
+
 3878.      1        line += 1;
+
 3879.      1        state.mode_shebang = true;
+
 3880.      1    }
+
 3881.    668    token_1 = lex_token();
+
 3882.    640    state.mode_json = token_1.id === "{" || token_1.id === "[";
+
 3883.    668
+
 3884.    668// Lex/loop through each token until (end).
+
 3885.    668
+
 3886. 249474    while (true) {
+
 3887. 249474        if (lex_token().id === "(end)") {
+
 3888. 249474            break;
+
 3889. 249474        }
+
 3890. 249474    }
+
 3891.    668}
+
 3892.      1
+
 3893.    631function jslint_phase3_parse(state) {
+
 3894.    631
+
 3895.    631// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
+
 3896.    631
+
 3897.    631// Parsing:
+
 3898.    631
+
 3899.    631// Parsing weaves the tokens into an abstract syntax tree. During that process,
+
 3900.    631// a token may be given any of these properties:
+
 3901.    631
+
 3902.    631//      arity       string
+
 3903.    631//      label       identifier
+
 3904.    631//      name        identifier
+
 3905.    631//      expression  expressions
+
 3906.    631//      block       statements
+
 3907.    631//      else        statements (else, default, catch)
+
 3908.    631
+
 3909.    631// Specialized tokens may have additional properties.
+
 3910.    631
+
 3911.    631    let anon = "anonymous";     // The guessed name for anonymous functions.
+
 3912.    631    let {
+
 3913.    631        artifact,
+
 3914.    631        catch_list,
+
 3915.    631        catch_stack,
+
 3916.    631        export_dict,
+
 3917.    631        function_list,
+
 3918.    631        function_stack,
+
 3919.    631        global_dict,
+
 3920.    631        import_list,
+
 3921.    631        is_equal,
+
 3922.    631        option_dict,
+
 3923.    631        property_dict,
+
 3924.    631        stop,
+
 3925.    631        syntax_dict,
+
 3926.    631        tenure,
+
 3927.    631        test_cause,
+
 3928.    631        token_global,
+
 3929.    631        token_list,
+
 3930.    631        warn,
+
 3931.    631        warn_at
+
 3932.    631    } = state;
+
 3933.    631    let catchage = catch_stack[0];      // The current catch-block.
+
 3934.    631    let functionage = token_global;     // The current function.
+
 3935.    631    let mode_var;               // "var" if using var; "let" if using let.
+
 3936.    631    let token_ii = 0;           // The number of the next token.
+
 3937.    631    let token_now = token_global;       // The current token being examined in
+
 3938.    631                                        // ... the parse.
+
 3939.    631    let token_nxt = token_global;       // The next token to be examined in
+
 3940.    631                                        // ... <token_list>.
+
 3941.    631
+
 3942. 244361    function advance(id, match) {
+
 3943. 244361
+
 3944. 244361// Produce the next token.
+
 3945. 244361
+
 3946. 244361// Attempt to give helpful names to anonymous functions.
+
 3947. 244361
+
 3948. 244361        if (
+
 3949. 244361            token_now.identifier
+
 3950.  67656            && token_now.id !== "function"
+
 3951.  65664            && token_now.id !== "async"
+
 3952.  65483        ) {
+
 3953.  65483            anon = token_now.id;
+
 3954. 178878        } else if (
+
 3955. 178878            token_now.id === "(string)"
+
 3956. 178878            && jslint_rgx_identifier.test(token_now.value)
+
 3957. 178878        ) {
+
 3958. 178878            anon = token_now.value;
+
 3959. 178878        }
+
 3960. 244361
+
 3961. 244361// Attempt to match token_nxt with an expected id.
+
 3962. 244361
+
 3963. 120065        if (id !== undefined && token_nxt.id !== id) {
+
 3964.     26            return (
+
 3965.     26                match === undefined
+
 3966.     26
+
 3967.     26// test_cause:
+
 3968.     26// ["{0:0}", "advance", "expected_a_b", "0", 2]
+
 3969.     26
+
 3970.     26                ? stop("expected_a_b", token_nxt, id, artifact())
+
 3971.     26
+
 3972.     26// test_cause:
+
 3973.     26// ["{\"aa\":0", "advance", "expected_a_b_from_c_d", "{", 1]
+
 3974.     26
+
 3975.     26                : stop(
+
 3976.     26                    "expected_a_b_from_c_d",
+
 3977.     26                    token_nxt,
+
 3978.     26                    id,
+
 3979.     26                    artifact(match),
+
 3980.     26                    match.line,
+
 3981.     26                    artifact()
+
 3982.     26                )
+
 3983.     26            );
+
 3984. 244335        }
+
 3985. 244335
+
 3986. 244335// Promote the tokens, skipping comments.
+
 3987. 244335
+
 3988. 244335        token_now = token_nxt;
+
 3989. 255550        while (true) {
+
 3990. 255550            token_nxt = token_list[token_ii];
+
 3991. 255550            state.token_nxt = token_nxt;
+
 3992. 255550            token_ii += 1;
+
 3993. 255550            if (token_nxt.id !== "(comment)") {
+
 3994. 255550                if (token_nxt.id === "(end)") {
+
 3995. 255550                    token_ii -= 1;
+
 3996. 255550                }
+
 3997. 255550                break;
+
 3998. 255550            }
+
 3999. 255550            if (state.mode_json) {
+
 4000. 255550
+
 4001. 255550// test_cause:
+
 4002. 255550// ["[//]", "advance", "unexpected_a", "(comment)", 2]
+
 4003. 255550
+
 4004. 255550                warn("unexpected_a");
+
 4005. 255550            }
+
 4006. 255550        }
+
 4007. 244361    }
+
 4008.    631
+
 4009.   7572    function assignment(id) {
+
 4010.   7572
+
 4011.   7572// Create an assignment operator. The one true assignment is different because
+
 4012.   7572// its left side, when it is a variable, is not treated as an expression.
+
 4013.   7572// That case is special because that is when a variable gets initialized. The
+
 4014.   7572// other assignment operators can modify, but they cannot initialize.
+
 4015.   7572
+
 4016.   7572        const the_symbol = symbol(id, 20);
+
 4017.   5013        the_symbol.led_infix = function (left) {
+
 4018.   5013            const the_token = token_now;
+
 4019.   5013            let right;
+
 4020.   5013            the_token.arity = "assignment";
+
 4021.   5013            right = parse_expression(20 - 1);
+
 4022.   4234            if (id === "=" && left.arity === "variable") {
+
 4023.   2818                the_token.names = left;
+
 4024.   2818                the_token.expression = right;
+
 4025.   2818            } else {
+
 4026.   2191                the_token.expression = [left, right];
+
 4027.   5009            }
+
 4028.   5009            if (
+
 4029.   5009                right.arity === "assignment"
+
 4030.   5009                || right.arity === "preassign"
+
 4031.   5007                || right.arity === "postassign"
+
 4032.      2            ) {
+
 4033.      2                warn("unexpected_a", right);
+
 4034.   5009            }
+
 4035.   5009            check_mutation(left);
+
 4036.   5009            return the_token;
+
 4037.   5009        };
+
 4038.   7572        return the_symbol;
+
 4039.   7572    }
+
 4040.    631
+
 4041.   5713    function block(special) {
+
 4042.   5713
+
 4043.   5713// Parse a block, a sequence of statements wrapped in braces.
+
 4044.   5713//  special "body"      The block is a function body.
+
 4045.   5713//          "ignore"    No warning on an empty block.
+
 4046.   5713//          "naked"     No advance.
+
 4047.   5713//          undefined   An ordinary block.
+
 4048.   5713
+
 4049.   5713        let stmts;
+
 4050.   5713        let the_block;
+
 4051.   5708        if (special !== "naked") {
+
 4052.   5708            advance("{");
+
 4053.   5712        }
+
 4054.   5712        the_block = token_now;
+
 4055.   5712        if (special !== "body") {
+
 4056.   3715            functionage.statement_prv = the_block;
+
 4057.   5712        }
+
 4058.   5712        the_block.arity = "statement";
+
 4059.   5712        the_block.body = special === "body";
+
 4060.   5712
+
 4061.   5712// Top level function bodies may include the "use strict" pragma.
+
 4062.   5712
+
 4063.   5712        if (
+
 4064.   5712            special === "body"
+
 4065.   5712            && function_stack.length === 1
+
 4066.    281            && token_nxt.value === "use strict"
+
 4067.      4        ) {
+
 4068.      4            token_nxt.statement = true;
+
 4069.      4            advance("(string)");
+
 4070.      4            advance(";");
+
 4071.   5712        }
+
 4072.   5712        stmts = parse_statements();
+
 4073.   5712        the_block.block = stmts;
+
 4074.   5712        if (stmts.length === 0) {
+
 4075.     72            if (!option_dict.devel && special !== "ignore") {
+
 4076.     72
+
 4077.     72// test_cause:
+
 4078.     72// ["function aa(){}", "block", "empty_block", "{", 14]
+
 4079.     72
+
 4080.     72                warn("empty_block", the_block);
+
 4081.     72            }
+
 4082.     72            the_block.disrupt = false;
+
 4083.   5633        } else {
+
 4084.   5633            the_block.disrupt = stmts[stmts.length - 1].disrupt;
+
 4085.   5705        }
+
 4086.   5705        advance("}");
+
 4087.   5705        return the_block;
+
 4088.   5705    }
+
 4089.    631
+
 4090.  23297    function check_left(left, right) {
+
 4091.  23297
+
 4092.  23297// Warn if the left is not one of these:
+
 4093.  23297//      ?.
+
 4094.  23297//      ?:
+
 4095.  23297//      e()
+
 4096.  23297//      e.b
+
 4097.  23297//      e[b]
+
 4098.  23297//      identifier
+
 4099.  23297
+
 4100.  23297        const id = left.id;
+
 4101.  23297        if (
+
 4102.  23297            !left.identifier
+
 4103.   6298            && (
+
 4104.   6298                left.arity !== "ternary"
+
 4105.   6298                || (
+
 4106.   6298                    !check_left(left.expression[1])
+
 4107.   6298                    && !check_left(left.expression[2])
+
 4108.   6298                )
+
 4109.   6298            )
+
 4110.   6298            && (
+
 4111.   6298                left.arity !== "binary"
+
 4112.   6298                || (id !== "." && id !== "?." && id !== "(" && id !== "[")
+
 4113.   6298            )
+
 4114.     11        ) {
+
 4115.     11            warn("unexpected_a", right || token_nxt);
+
 4116.     11            return false;
+
 4117.  23286        }
+
 4118.  23286        return true;
+
 4119.  23286    }
+
 4120.    631
+
 4121.   5012    function check_mutation(the_thing) {
+
 4122.   5012
+
 4123.   5012// The only expressions that may be assigned to are
+
 4124.   5012//      e.b
+
 4125.   5012//      e[b]
+
 4126.   5012//      v
+
 4127.   5012//      [destructure]
+
 4128.   5012//      {destructure}
+
 4129.   5012
+
 4130.   5012        if (
+
 4131.   5012            the_thing.arity !== "variable"
+
 4132.   1543            && the_thing.id !== "."
+
 4133.    192            && the_thing.id !== "["
+
 4134.      7            && the_thing.id !== "{"
+
 4135.      7        ) {
+
 4136.      7
+
 4137.      7// test_cause:
+
 4138.      7// ["0=0", "check_mutation", "bad_assignment_a", "0", 1]
+
 4139.      7
+
 4140.      7            warn("bad_assignment_a", the_thing);
+
 4141.      7            return false;
+
 4142.   5005        }
+
 4143.   5005        return true;
+
 4144.   5005    }
+
 4145.    631
+
 4146.   2208    function check_not_top_level(thing) {
+
 4147.   2208
+
 4148.   2208// Some features should not be at the outermost level.
+
 4149.   2208
+
 4150.     34        if (functionage === token_global) {
+
 4151.     34
+
 4152.     34// test_cause:
+
 4153.     34// ["
+
 4154.     34// while(0){}
+
 4155.     34// ", "check_not_top_level", "unexpected_at_top_level_a", "while", 1]
+
 4156.     34
+
 4157.     34            warn("unexpected_at_top_level_a", thing);
+
 4158.     34        }
+
 4159.   2208    }
+
 4160.    631
+
 4161.   3360    function check_ordered(type, token_list) {
+
 4162.   3360
+
 4163.   3360// This function will warn if <token_list> is unordered.
+
 4164.   3360
+
 4165.   4062        token_list.reduce(function (aa, token) {
+
 4166.   4062            const bb = artifact(token);
+
 4167.   4048            if (!option_dict.unordered && aa > bb) {
+
 4168.      4                warn("expected_a_b_before_c_d", token, type, bb, type, aa);
+
 4169.      4            }
+
 4170.   4062            return bb;
+
 4171.   4062        }, "");
+
 4172.   3360    }
+
 4173.    631
+
 4174.   1301    function check_ordered_case(case_list) {
+
 4175.   1301
+
 4176.   1301// This function will warn if <case_list> is unordered.
+
 4177.   1301
+
 4178.   2691        case_list.filter(noop).map(function (token) {
+
 4179.   2650            switch (token.identifier || token.id) {
+
 4180.     34            case "(number)":
+
 4181.     34                return {
+
 4182.     34                    order: 1,
+
 4183.     34                    token,
+
 4184.     34                    type: "number",
+
 4185.     34                    value: Number(artifact(token))
+
 4186.     34                };
+
 4187.   2604            case "(string)":
+
 4188.   2604                return {
+
 4189.   2604                    order: 2,
+
 4190.   2604                    token,
+
 4191.   2604                    type: "string",
+
 4192.   2604                    value: artifact(token)
+
 4193.   2604                };
+
 4194.     41            case true:
+
 4195.     41                return {
+
 4196.     41                    order: 3,
+
 4197.     41                    token,
+
 4198.     41                    type: "identifier",
+
 4199.     41                    value: artifact(token)
+
 4200.     41                };
+
 4201.   2691            }
+
 4202.   2691        }).reduce(function (aa, bb) {
+
 4203.   2691            if (
+
 4204.   2691
+
 4205.   2691// PR-419 - Hide warning about unordered case-statements behind beta-flag.
+
 4206.   2691
+
 4207.   2691                option_dict.beta
+
 4208.   2691                && !option_dict.unordered
+
 4209.   2683                && aa && bb
+
 4210.   1384                && (
+
 4211.   1384                    aa.order > bb.order
+
 4212.   1384                    || (aa.order === bb.order && aa.value > bb.value)
+
 4213.   1384                )
+
 4214.     10            ) {
+
 4215.     10                warn(
+
 4216.     10                    "expected_a_b_before_c_d",
+
 4217.     10                    bb.token,
+
 4218.     10                    `case-${bb.type}`,
+
 4219.     10                    bb.value,
+
 4220.     10                    `case-${aa.type}`,
+
 4221.     10                    aa.value
+
 4222.     10                );
+
 4223.     10            }
+
 4224.   2691            return bb;
+
 4225.   2691        }, undefined);
+
 4226.   1301    }
+
 4227.    631
+
 4228.   3263    function condition() {
+
 4229.   3263
+
 4230.   3263// Parse the condition part of a do, if, while.
+
 4231.   3263
+
 4232.   3263        const the_paren = token_nxt;
+
 4233.   3263        let the_value;
+
 4234.   3263
+
 4235.   3263// test_cause:
+
 4236.   3263// ["do{}while()", "condition", "", "", 0]
+
 4237.   3263// ["if(){}", "condition", "", "", 0]
+
 4238.   3263// ["while(){}", "condition", "", "", 0]
+
 4239.   3263
+
 4240.   3263        test_cause("");
+
 4241.   3263        the_paren.free = true;
+
 4242.   3263        advance("(");
+
 4243.   3263        the_value = parse_expression(0);
+
 4244.   3263        advance(")");
+
 4245.      1        if (the_value.wrapped === true) {
+
 4246.      1
+
 4247.      1// test_cause:
+
 4248.      1// ["while((0)){}", "condition", "unexpected_a", "(", 6]
+
 4249.      1
+
 4250.      1            warn("unexpected_a", the_paren);
+
 4251.   3259        }
+
 4252.   3259
+
 4253.   3259// Check for anticondition.
+
 4254.   3259
+
 4255.   3259        switch (the_value.id) {
+
 4256.   3259        case "%":
+
 4257.      1            warn("unexpected_a", the_value);
+
 4258.      1            break;
+
 4259.      1        case "&":
+
 4260.      1            warn("unexpected_a", the_value);
+
 4261.      1            break;
+
 4262.     17        case "(number)":
+
 4263.     17            warn("unexpected_a", the_value);
+
 4264.     17            break;
+
 4265.      1        case "(string)":
+
 4266.      1            warn("unexpected_a", the_value);
+
 4267.      1            break;
+
 4268.      1        case "*":
+
 4269.      1            warn("unexpected_a", the_value);
+
 4270.      1            break;
+
 4271.      1        case "+":
+
 4272.      1            warn("unexpected_a", the_value);
+
 4273.      1            break;
+
 4274.      1        case "-":
+
 4275.      1            warn("unexpected_a", the_value);
+
 4276.      1            break;
+
 4277.      1        case "/":
+
 4278.      1            warn("unexpected_a", the_value);
+
 4279.      1            break;
+
 4280.      1        case "<<":
+
 4281.      1            warn("unexpected_a", the_value);
+
 4282.      1            break;
+
 4283.      1        case ">>":
+
 4284.      1            warn("unexpected_a", the_value);
+
 4285.      1            break;
+
 4286.      1        case ">>>":
+
 4287.      1            warn("unexpected_a", the_value);
+
 4288.      1            break;
+
 4289.      1        case "?":
+
 4290.      1            warn("unexpected_a", the_value);
+
 4291.      1            break;
+
 4292.      1        case "^":
+
 4293.      1            warn("unexpected_a", the_value);
+
 4294.      1            break;
+
 4295.      1        case "typeof":
+
 4296.      1            warn("unexpected_a", the_value);
+
 4297.      1            break;
+
 4298.      1        case "|":
+
 4299.      1            warn("unexpected_a", the_value);
+
 4300.      1            break;
+
 4301.      1        case "~":
+
 4302.      1
+
 4303.      1// test_cause:
+
 4304.      1// ["if(0%0){}", "condition", "unexpected_a", "%", 5]
+
 4305.      1// ["if(0&0){}", "condition", "unexpected_a", "&", 5]
+
 4306.      1// ["if(0){}", "condition", "unexpected_a", "0", 4]
+
 4307.      1// ["if(0*0){}", "condition", "unexpected_a", "*", 5]
+
 4308.      1// ["if(0+0){}", "condition", "unexpected_a", "+", 5]
+
 4309.      1// ["if(0-0){}", "condition", "unexpected_a", "-", 5]
+
 4310.      1// ["if(0/0){}", "condition", "unexpected_a", "/", 5]
+
 4311.      1// ["if(0<<0){}", "condition", "unexpected_a", "<<", 5]
+
 4312.      1// ["if(0>>0){}", "condition", "unexpected_a", ">>", 5]
+
 4313.      1// ["if(0>>>0){}", "condition", "unexpected_a", ">>>", 5]
+
 4314.      1// ["if(0?0:0){}", "condition", "unexpected_a", "?", 5]
+
 4315.      1// ["if(0^0){}", "condition", "unexpected_a", "^", 5]
+
 4316.      1// ["if(0|0){}", "condition", "unexpected_a", "|", 5]
+
 4317.      1// ["if(\"aa\"){}", "condition", "unexpected_a", "aa", 4]
+
 4318.      1// ["if(typeof 0){}", "condition", "unexpected_a", "typeof", 4]
+
 4319.      1// ["if(~0){}", "condition", "unexpected_a", "~", 4]
+
 4320.      1
+
 4321.      1            warn("unexpected_a", the_value);
+
 4322.      1            break;
+
 4323.   3259        }
+
 4324.   3259        return the_value;
+
 4325.   3259    }
+
 4326.    631
+
 4327.  10096    function constant(id, type, value) {
+
 4328.  10096
+
 4329.  10096// Create a constant symbol.
+
 4330.  10096
+
 4331.  10096        const the_symbol = symbol(id);
+
 4332.  10096        the_symbol.constant = true;
+
 4333.  10096        the_symbol.nud_prefix = (
+
 4334.  10096            typeof value === "function"
+
 4335.   4417            ? value
+
 4336.  18689            : function () {
+
 4337.  18689                token_now.constant = true;
+
 4338.   5679                if (value !== undefined) {
+
 4339.   5679                    token_now.value = value;
+
 4340.   5679                }
+
 4341.  18689                return token_now;
+
 4342.  18689            }
+
 4343.  10096        );
+
 4344.  10096        the_symbol.type = type;
+
 4345.  10096        the_symbol.value = value;
+
 4346.  10096        return the_symbol;
+
 4347.  10096    }
+
 4348.    631
+
 4349.      5    function constant_Function() {
+
 4350.      2        if (!option_dict.eval) {
+
 4351.      2
+
 4352.      2// test_cause:
+
 4353.      2// ["Function", "constant_Function", "unexpected_a", "Function", 1]
+
 4354.      2
+
 4355.      2            warn("unexpected_a", token_now);
+
 4356.      3        } else if (token_nxt.id !== "(") {
+
 4357.      3
+
 4358.      3// test_cause:
+
 4359.      3// ["
+
 4360.      3// /*jslint eval*/
+
 4361.      3// Function
+
 4362.      3// ", "constant_Function", "expected_a_before_b", "(end)", 1]
+
 4363.      3
+
 4364.      3            warn("expected_a_before_b", token_nxt, "(", artifact());
+
 4365.      3        }
+
 4366.      5        return token_now;
+
 4367.      5    }
+
 4368.    631
+
 4369.      1    function constant_arguments() {
+
 4370.      1
+
 4371.      1// test_cause:
+
 4372.      1// ["arguments", "constant_arguments", "unexpected_a", "arguments", 1]
+
 4373.      1
+
 4374.      1        warn("unexpected_a", token_now);
+
 4375.      1        return token_now;
+
 4376.      1    }
+
 4377.    631
+
 4378.      4    function constant_eval() {
+
 4379.      1        if (!option_dict.eval) {
+
 4380.      1
+
 4381.      1// test_cause:
+
 4382.      1// ["eval", "constant_eval", "unexpected_a", "eval", 1]
+
 4383.      1
+
 4384.      1            warn("unexpected_a", token_now);
+
 4385.      3        } else if (token_nxt.id !== "(") {
+
 4386.      3
+
 4387.      3// test_cause:
+
 4388.      3// ["/*jslint eval*/\neval", "constant_eval", "expected_a_before_b", "(end)", 1]
+
 4389.      3
+
 4390.      3            warn("expected_a_before_b", token_nxt, "(", artifact());
+
 4391.      3        }
+
 4392.      4        return token_now;
+
 4393.      4    }
+
 4394.    631
+
 4395.      1    function constant_ignore() {
+
 4396.      1
+
 4397.      1// test_cause:
+
 4398.      1// ["ignore", "constant_ignore", "unexpected_a", "ignore", 1]
+
 4399.      1
+
 4400.      1        warn("unexpected_a", token_now);
+
 4401.      1        return token_now;
+
 4402.      1    }
+
 4403.    631
+
 4404.      1    function constant_isInfinite() {
+
 4405.      1
+
 4406.      1// test_cause:
+
 4407.      1// ["isFinite", "constant_isInfinite", "expected_a_b", "isFinite", 1]
+
 4408.      1
+
 4409.      1        warn("expected_a_b", token_now, "Number.isFinite", "isFinite");
+
 4410.      1        return token_now;
+
 4411.      1    }
+
 4412.    631
+
 4413.      1    function constant_isNaN() {
+
 4414.      1
+
 4415.      1// test_cause:
+
 4416.      1// ["isNaN(0)", "constant_isNaN", "number_isNaN", "isNaN", 1]
+
 4417.      1
+
 4418.      1        warn("number_isNaN", token_now);
+
 4419.      1        return token_now;
+
 4420.      1    }
+
 4421.    631
+
 4422.      3    function constant_this() {
+
 4423.      1        if (!option_dict.this) {
+
 4424.      1
+
 4425.      1// test_cause:
+
 4426.      1// ["this", "constant_this", "unexpected_a", "this", 1]
+
 4427.      1
+
 4428.      1            warn("unexpected_a", token_now);
+
 4429.      1        }
+
 4430.      3        return token_now;
+
 4431.      3    }
+
 4432.    631
+
 4433.   6366    function enroll(name, role, readonly) {
+
 4434.   6366
+
 4435.   6366// Enroll a name into the current function context. The role can be exception,
+
 4436.   6366// function, label, parameter, or variable. We look for variable redefinition
+
 4437.   6366// because it causes confusion.
+
 4438.   6366
+
 4439.   6366        let earlier;
+
 4440.   6366        let id = name.id;
+
 4441.   6366
+
 4442.   6366// Reserved words may not be enrolled.
+
 4443.   6366
+
 4444.     42        if (syntax_dict[id] !== undefined && id !== "ignore") {
+
 4445.      1
+
 4446.      1// test_cause:
+
 4447.      1// ["let undefined", "enroll", "reserved_a", "undefined", 5]
+
 4448.      1
+
 4449.      1            warn("reserved_a", name);
+
 4450.      1            return;
+
 4451.   6365        }
+
 4452.   6365
+
 4453.   6365// Has the name been enrolled in this context?
+
 4454.   6365
+
 4455.   6365        earlier = functionage.context[id] || catchage.context[id];
+
 4456.      7        if (earlier) {
+
 4457.      7
+
 4458.      7// test_cause:
+
 4459.      7// ["let aa;let aa", "enroll", "redefinition_a_b", "1", 12]
+
 4460.      7
+
 4461.      7            warn("redefinition_a_b", name, id, earlier.line);
+
 4462.      7            return;
+
 4463.   6358        }
+
 4464.   6358
+
 4465.   6358// Has the name been enrolled in an outer context?
+
 4466.   6358
+
 4467.  10756        function_stack.forEach(function ({
+
 4468.  10756            context
+
 4469.  10756        }) {
+
 4470.  10601            earlier = context[id] || earlier;
+
 4471.  10756        });
+
 4472.   6358        if (earlier && id === "ignore") {
+
 4473.      4            if (earlier.role === "variable") {
+
 4474.      4
+
 4475.      4// test_cause:
+
 4476.      4// ["let ignore;function aa(ignore){}", "enroll", "redefinition_a_b", "1", 24]
+
 4477.      4
+
 4478.      4                warn("redefinition_a_b", name, id, earlier.line);
+
 4479.      4            }
+
 4480.   6354        } else if (
+
 4481.   6354            earlier
+
 4482.   6354            && role !== "parameter" && role !== "function"
+
 4483.   6354            && (role !== "exception" || earlier.role !== "exception")
+
 4484.   6354        ) {
+
 4485.   6354
+
 4486.   6354// test_cause:
+
 4487.   6354// ["
+
 4488.   6354// function aa(){try{aa();}catch(aa){aa();}}
+
 4489.   6354// ", "enroll", "redefinition_a_b", "1", 31]
+
 4490.   6354// ["function aa(){var aa;}", "enroll", "redefinition_a_b", "1", 19]
+
 4491.   6354
+
 4492.   6354            warn("redefinition_a_b", name, id, earlier.line);
+
 4493.   6354        } else if (
+
 4494.   6354            option_dict.beta
+
 4495.   6354            && global_dict[id]
+
 4496.   6354            && role !== "parameter"
+
 4497.   6354        ) {
+
 4498.   6354
+
 4499.   6354// test_cause:
+
 4500.   6354// ["let Array", "enroll", "redefinition_global_a_b", "Array", 5]
+
 4501.   6354
+
 4502.   6354            warn("redefinition_global_a_b", name, global_dict[id], id);
+
 4503.   6358        }
+
 4504.   6358
+
 4505.   6358// Enroll it.
+
 4506.   6358
+
 4507.   6358        Object.assign(name, {
+
 4508.   6358            dead: true,
+
 4509.   6358            init: false,
+
 4510.   6358            parent: (
+
 4511.   6358                role === "exception"
+
 4512.   6358                ? catchage
+
 4513.   6328                : functionage
+
 4514.   6366            ),
+
 4515.   6366            readonly,
+
 4516.   6366            role,
+
 4517.   6366            used: 0
+
 4518.   6366        });
+
 4519.   6366        name.parent.context[id] = name;
+
 4520.   6366    }
+
 4521.    631
+
 4522.  18930    function infix(bp, id, f) {
+
 4523.  18930
+
 4524.  18930// Create an infix operator.
+
 4525.  18930
+
 4526.  18930        const the_symbol = symbol(id, bp);
+
 4527.  31941        the_symbol.led_infix = function (left) {
+
 4528.  31941            const the_token = token_now;
+
 4529.  31941            the_token.arity = "binary";
+
 4530.  23493            if (f !== undefined) {
+
 4531.  23493                return f(left);
+
 4532.  23493            }
+
 4533.   8448            the_token.expression = [left, parse_expression(bp)];
+
 4534.   8448            return the_token;
+
 4535.   8448        };
+
 4536.  18930        return the_symbol;
+
 4537.  18930    }
+
 4538.    631
+
 4539.  11597    function infix_dot(left) {
+
 4540.  11597        const the_token = token_now;
+
 4541.  11597        let name = token_nxt;
+
 4542.  11597        if (
+
 4543.  11597            (
+
 4544.  11597                left.id !== "(string)"
+
 4545.     44                || (name.id !== "indexOf" && name.id !== "repeat")
+
 4546.  11597            )
+
 4547.  11554            && (
+
 4548.  11554                left.id !== "["
+
 4549.  11554                || (
+
 4550.  11554                    name.id !== "concat"
+
 4551.  11554                    && name.id !== "flat"
+
 4552.  11554                    && name.id !== "flatMap"
+
 4553.  11554                    && name.id !== "forEach"
+
 4554.  11554                    && name.id !== "join"
+
 4555.  11554                    && name.id !== "map"
+
 4556.  11554                )
+
 4557.  11554            )
+
 4558.  11509            && (left.id !== "+" || name.id !== "slice")
+
 4559.  11504            && (
+
 4560.  11504                left.id !== "(regexp)"
+
 4561.  11504                || (name.id !== "exec" && name.id !== "test")
+
 4562.  11504            )
+
 4563.  11437        ) {
+
 4564.  11437
+
 4565.  11437// test_cause:
+
 4566.  11437// ["\"\".aa", "check_left", "unexpected_a", ".", 3]
+
 4567.  11437
+
 4568.  11437            check_left(left, the_token);
+
 4569.  11437        }
+
 4570.      1        if (!name.identifier) {
+
 4571.      1
+
 4572.      1// test_cause:
+
 4573.      1// ["aa.0", "infix_dot", "expected_identifier_a", "0", 4]
+
 4574.      1
+
 4575.      1            stop("expected_identifier_a");
+
 4576.  11596        }
+
 4577.  11596        advance();
+
 4578.  11596        survey(name);
+
 4579.  11596
+
 4580.  11596// The property name is not an expression.
+
 4581.  11596
+
 4582.  11596        the_token.name = name;
+
 4583.  11596        the_token.expression = left;
+
 4584.  11596        return the_token;
+
 4585.  11596    }
+
 4586.    631
+
 4587.      1    function infix_fart_unwrapped() {
+
 4588.      1
+
 4589.      1// test_cause:
+
 4590.      1// ["aa=>0", "infix_fart_unwrapped", "wrap_fart_parameter", "=>", 3]
+
 4591.      1
+
 4592.      1        return stop("wrap_fart_parameter", token_now);
+
 4593.      1    }
+
 4594.    631
+
 4595.      1    function infix_grave(left) {
+
 4596.      1        const the_tick = prefix_tick();
+
 4597.      1
+
 4598.      1// test_cause:
+
 4599.      1// ["0``", "check_left", "unexpected_a", "`", 2]
+
 4600.      1
+
 4601.      1        check_left(left, the_tick);
+
 4602.      1        the_tick.expression = [left].concat(the_tick.expression);
+
 4603.      1        return the_tick;
+
 4604.      1    }
+
 4605.    631
+
 4606.   1461    function infix_lbracket(left) {
+
 4607.   1461        const the_token = token_now;
+
 4608.   1461        let name;
+
 4609.   1461        let the_subscript = parse_expression(0);
+
 4610.   1438        if (the_subscript.id === "(string)" || the_subscript.id === "`") {
+
 4611.     25            name = survey(the_subscript);
+
 4612.     25
+
 4613.     25// PR-404 - Add new directive "subscript" to play nice with Google Closure.
+
 4614.     25
+
 4615.     25            if (!option_dict.subscript && jslint_rgx_identifier.test(name)) {
+
 4616.     25
+
 4617.     25// test_cause:
+
 4618.     25// ["aa[`aa`]", "infix_lbracket", "subscript_a", "aa", 4]
+
 4619.     25
+
 4620.     25                warn("subscript_a", the_subscript, name);
+
 4621.     25            }
+
 4622.     25        }
+
 4623.   1461
+
 4624.   1461// test_cause:
+
 4625.   1461// ["0[0]", "check_left", "unexpected_a", "[", 2]
+
 4626.   1461
+
 4627.   1461        check_left(left, the_token);
+
 4628.   1461        the_token.expression = [left, the_subscript];
+
 4629.   1461        advance("]");
+
 4630.   1461        return the_token;
+
 4631.   1461    }
+
 4632.    631
+
 4633.  10421    function infix_lparen(left) {
+
 4634.  10421        const the_paren = token_now;
+
 4635.  10421        let ellipsis;
+
 4636.  10421        let the_argument;
+
 4637.  10384        if (left.id !== "function") {
+
 4638.  10384
+
 4639.  10384// test_cause:
+
 4640.  10384// ["(0?0:0)()", "check_left", "unexpected_a", "(", 8]
+
 4641.  10384// ["0()", "check_left", "unexpected_a", "(", 2]
+
 4642.  10384
+
 4643.  10384            check_left(left, the_paren);
+
 4644.  10384        }
+
 4645.   7699        if (functionage.arity === "statement" && left.identifier) {
+
 4646.   5503            functionage.name.calls[left.id] = left;
+
 4647.   5503        }
+
 4648.  10421        the_paren.expression = [left];
+
 4649.   8962        if (token_nxt.id !== ")") {
+
 4650.   8962
+
 4651.   8962// Parse/loop through each token in expression (...).
+
 4652.   8962
+
 4653.  14286            while (true) {
+
 4654.  14286                if (token_nxt.id === "...") {
+
 4655.  14286                    ellipsis = true;
+
 4656.  14286                    advance("...");
+
 4657.  14286                }
+
 4658.  14286                the_argument = parse_expression(10);
+
 4659.  14286                if (ellipsis) {
+
 4660.  14286                    the_argument.ellipsis = true;
+
 4661.  14286                }
+
 4662.  14286                the_paren.expression.push(the_argument);
+
 4663.  14286                if (token_nxt.id !== ",") {
+
 4664.  14286                    break;
+
 4665.  14286                }
+
 4666.  14286                advance(",");
+
 4667.  14286            }
+
 4668.   8962        }
+
 4669.  10421        advance(")", the_paren);
+
 4670.   5260        if (the_paren.expression.length === 2) {
+
 4671.   5260
+
 4672.   5260// test_cause:
+
 4673.   5260// ["aa(0)", "infix_lparen", "free", "", 0]
+
 4674.   5260
+
 4675.   5260            test_cause("free");
+
 4676.   5260            the_paren.free = true;
+
 4677.   5260            if (the_argument.wrapped === true) {
+
 4678.   5260
+
 4679.   5260// test_cause:
+
 4680.   5260// ["aa((0))", "infix_lparen", "unexpected_a", "(", 3]
+
 4681.   5260
+
 4682.   5260                warn("unexpected_a", the_paren);
+
 4683.   5260            }
+
 4684.   5260            if (the_argument.id === "(") {
+
 4685.   5260                the_argument.wrapped = true;
+
 4686.   5260            }
+
 4687.   5260        } else {
+
 4688.   5161
+
 4689.   5161// test_cause:
+
 4690.   5161// ["aa()", "infix_lparen", "not_free", "", 0]
+
 4691.   5161// ["aa(0,0)", "infix_lparen", "not_free", "", 0]
+
 4692.   5161
+
 4693.   5161            test_cause("not_free");
+
 4694.   5161            the_paren.free = false;
+
 4695.   5161        }
+
 4696.  10421        return the_paren;
+
 4697.  10421    }
+
 4698.    631
+
 4699.     12    function infix_option_chain(left) {
+
 4700.     12        const the_token = token_now;
+
 4701.     12        let name = token_nxt;
+
 4702.     12        if (
+
 4703.     12            (
+
 4704.     12                left.id !== "(string)"
+
 4705.      1                || (name.id !== "indexOf" && name.id !== "repeat")
+
 4706.     12            )
+
 4707.     12            && (
+
 4708.     12                left.id !== "["
+
 4709.      1                || (
+
 4710.      1                    name.id !== "concat"
+
 4711.      1                    && name.id !== "forEach"
+
 4712.      1                    && name.id !== "join"
+
 4713.      1                    && name.id !== "map"
+
 4714.      1                )
+
 4715.     12            )
+
 4716.     12
+
 4717.     12// test_cause:
+
 4718.     12// ["(0+0)?.0", "infix_option_chain", "check_left", "", 0]
+
 4719.     12
+
 4720.      1            && (left.id !== "+" || name.id !== "slice")
+
 4721.     12            && (
+
 4722.     12                left.id !== "(regexp)"
+
 4723.      1                || (name.id !== "exec" && name.id !== "test")
+
 4724.     12            )
+
 4725.     12        ) {
+
 4726.     12            test_cause("check_left");
+
 4727.     12
+
 4728.     12// test_cause:
+
 4729.     12// ["(/./)?.0", "check_left", "unexpected_a", "?.", 6]
+
 4730.     12// ["\"aa\"?.0", "check_left", "unexpected_a", "?.", 5]
+
 4731.     12// ["aa=[]?.aa", "check_left", "unexpected_a", "?.", 6]
+
 4732.     12
+
 4733.     12            check_left(left, the_token);
+
 4734.     12        }
+
 4735.     12
+
 4736.     12// Issue #468 - Fix optional dynamic-property/function-call not recognized.
+
 4737.     12
+
 4738.     11        if (name.id === "[" || name.id === "(") {
+
 4739.      2            test_cause("dyn_prop_or_call");
+
 4740.      2
+
 4741.      2// test_cause:
+
 4742.      2// ["aa?.(bb)", "infix_option_chain", "dyn_prop_or_call", "", 0]
+
 4743.      2// ["aa?.[bb]", "infix_option_chain", "dyn_prop_or_call", "", 0]
+
 4744.      2
+
 4745.      2            return left;
+
 4746.     10        }
+
 4747.     10        if (!name.identifier) {
+
 4748.      4
+
 4749.      4// test_cause:
+
 4750.      4// ["aa?.0", "infix_option_chain", "expected_identifier_a", "0", 5]
+
 4751.      4
+
 4752.      4            stop("expected_identifier_a");
+
 4753.      6        }
+
 4754.      6        advance();
+
 4755.      6        survey(name);
+
 4756.      6
+
 4757.      6// The property name is not an expression.
+
 4758.      6
+
 4759.      6        the_token.name = name;
+
 4760.      6        the_token.expression = left;
+
 4761.      6        return the_token;
+
 4762.      6    }
+
 4763.    631
+
 4764.    631    function infixr(bp, id) {
+
 4765.    631
+
 4766.    631// Create a right associative infix operator.
+
 4767.    631
+
 4768.    631        const the_symbol = symbol(id, bp);
+
 4769.      1        the_symbol.led_infix = function parse_infixr_led(left) {
+
 4770.      1            const the_token = token_now;
+
 4771.      1
+
 4772.      1// test_cause:
+
 4773.      1// ["0**0", "parse_infixr_led", "led_infix", "", 0]
+
 4774.      1
+
 4775.      1            test_cause("led_infix");
+
 4776.      1            the_token.arity = "binary";
+
 4777.      1            the_token.expression = [left, parse_expression(bp - 1)];
+
 4778.      1            return the_token;
+
 4779.      1        };
+
 4780.    631        return the_symbol;
+
 4781.    631    }
+
 4782.    631
+
 4783.  56002    function parse_expression(rbp, initial) {
+
 4784.  56002
+
 4785.  56002// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it
+
 4786.  56002// is looking for ad hoc lint patterns. We add .fud_stmt to Pratt's model, which
+
 4787.  56002// is like .nud_prefix except that it is only used on the first token of a
+
 4788.  56002// statement. Having .fud_stmt makes it much easier to define statement-oriented
+
 4789.  56002// languages like JavaScript. I retained Pratt's nomenclature.
+
 4790.  56002// They are elements of the parsing method called Top Down Operator Precedence.
+
 4791.  56002
+
 4792.  56002// .nud_prefix  Null denotation. The prefix handler.
+
 4793.  56002// .fud_stmt    First null denotation. The statement handler.
+
 4794.  56002// .led_infix   Left denotation. The infix/postfix handler.
+
 4795.  56002//  lbp         Left binding power of infix operator. It tells us how strongly
+
 4796.  56002//              the operator binds to the argument at its left.
+
 4797.  56002//  rbp         Right binding power.
+
 4798.  56002
+
 4799.  56002// It processes a nud_prefix (variable, constant, prefix operator). It will then
+
 4800.  56002// process leds (infix operators) until the bind powers cause it to stop (it
+
 4801.  56002// consumes tokens until it meets a token whose lbp <= rbp). Specifically, it
+
 4802.  56002// means that it collects all tokens that bind together before returning to the
+
 4803.  56002// operator that called it. It returns the expression's parse tree.
+
 4804.  56002
+
 4805.  56002// For example, "3 + 1 * 2 * 4 + 5"
+
 4806.  56002// parses into
+
 4807.  56002// {
+
 4808.  56002//     "id": "+",
+
 4809.  56002//     "expression": [
+
 4810.  56002//         {
+
 4811.  56002//             "id": "+",
+
 4812.  56002//             "expression": [
+
 4813.  56002//                 {
+
 4814.  56002//                     "id": "(number)",
+
 4815.  56002//                     "value": "3"
+
 4816.  56002//                 },
+
 4817.  56002//                 {
+
 4818.  56002//                     "id": "*",
+
 4819.  56002//                     "expression": [
+
 4820.  56002//                         {
+
 4821.  56002//                             "id": "*",
+
 4822.  56002//                             "expression": [
+
 4823.  56002//                                 {
+
 4824.  56002//                                     "id": "(number)",
+
 4825.  56002//                                     "value": "1"
+
 4826.  56002//                                 },
+
 4827.  56002//                                 {
+
 4828.  56002//                                     "id": "(number)",
+
 4829.  56002//                                     "value": "2"
+
 4830.  56002//                                 }
+
 4831.  56002//                             ]
+
 4832.  56002//                         },
+
 4833.  56002//                         {
+
 4834.  56002//                             "id": "(number)",
+
 4835.  56002//                             "value": "4"
+
 4836.  56002//                         }
+
 4837.  56002//                     ]
+
 4838.  56002//                 }
+
 4839.  56002//             ]
+
 4840.  56002//         },
+
 4841.  56002//         {
+
 4842.  56002//             "id": "(number)",
+
 4843.  56002//             "value": "5"
+
 4844.  56002//         }
+
 4845.  56002//     ]
+
 4846.  56002// }
+
 4847.  56002
+
 4848.  56002        let left;
+
 4849.  56002        let the_symbol;
+
 4850.  56002
+
 4851.  56002// Statements will have already advanced, so advance now only if the token is
+
 4852.  56002// not the first of a statement.
+
 4853.  56002
+
 4854.  44642        if (!initial) {
+
 4855.  44642            advance();
+
 4856.  44642        }
+
 4857.  56002        the_symbol = syntax_dict[token_now.id];
+
 4858.  24510        if (the_symbol !== undefined && the_symbol.nud_prefix !== undefined) {
+
 4859.  24451
+
 4860.  24451// test_cause:
+
 4861.  24451// ["0", "parse_expression", "symbol", "", 0]
+
 4862.  24451
+
 4863.  24451            test_cause("symbol");
+
 4864.  24451            left = the_symbol.nud_prefix();
+
 4865.  31551        } else if (token_now.identifier) {
+
 4866.  31551
+
 4867.  31551// test_cause:
+
 4868.  31551// ["aa", "parse_expression", "identifier", "", 0]
+
 4869.  31551
+
 4870.  31551            test_cause("identifier");
+
 4871.  31551            left = token_now;
+
 4872.  31551            left.arity = "variable";
+
 4873.  31551        } else {
+
 4874.  31551
+
 4875.  31551// test_cause:
+
 4876.  31551// ["!", "parse_expression", "unexpected_a", "(end)", 1]
+
 4877.  31551// ["/./", "parse_expression", "unexpected_a", "/", 1]
+
 4878.  31551// ["let aa=`${}`;", "parse_expression", "unexpected_a", "}", 11]
+
 4879.  31551
+
 4880.  31551            return stop("unexpected_a", token_now);
+
 4881.  55966        }
+
 4882.  55966
+
 4883.  55966// Parse/loop through each symbol in expression.
+
 4884.  55966
+
 4885.  93124        while (true) {
+
 4886.  93124            the_symbol = syntax_dict[token_nxt.id];
+
 4887.  93124            if (
+
 4888.  93124                the_symbol === undefined
+
 4889.  93124                || the_symbol.led_infix === undefined
+
 4890.  93124                || the_symbol.lbp <= rbp
+
 4891.  93124            ) {
+
 4892.  93124                break;
+
 4893.  93124            }
+
 4894.  93124            advance();
+
 4895.  93124            left = the_symbol.led_infix(left);
+
 4896.  93124        }
+
 4897.  55955        return left;
+
 4898.  55955    }
+
 4899.    631
+
 4900.     14    function parse_fart(the_fart) {
+
 4901.     14
+
 4902.     14// Give the function properties storing its names and for observing the depth
+
 4903.     14// of loops and switches.
+
 4904.     14
+
 4905.     14        Object.assign(the_fart, {
+
 4906.     14            arity: "binary",
+
 4907.     14            context: empty(),
+
 4908.     14            finally: 0,
+
 4909.     14            level: functionage.level + 1,
+
 4910.     14            loop: 0,
+
 4911.     14            name: anon,
+
 4912.     14            switch: 0,
+
 4913.     14            try: 0
+
 4914.     14        });
+
 4915.     14
+
 4916.     14// PR-384 - Relax warning "function_in_loop".
+
 4917.     14//
+
 4918.     14//         if (functionage.loop > 0) {
+
 4919.     14
+
 4920.     14// // test_cause:
+
 4921.     14// // ["while(0){aa.map(()=>0);}", "parse_fart", "function_in_loop", "=>", 19]
+
 4922.     14//
+
 4923.     14//             warn("function_in_loop", the_fart);
+
 4924.     14//         }
+
 4925.     14
+
 4926.     14// Push the current function context and establish a new one.
+
 4927.     14
+
 4928.     14        function_list.push(the_fart);
+
 4929.     14        function_stack.push(functionage);
+
 4930.     14        functionage = the_fart;
+
 4931.     14
+
 4932.     14// Parse the parameter list.
+
 4933.     14
+
 4934.     14        prefix_function_parameter(the_fart);
+
 4935.     14        advance("=>");
+
 4936.     14
+
 4937.     14// The function's body is a block.
+
 4938.     14
+
 4939.      3        if (token_nxt.id === "{") {
+
 4940.      3            if (!option_dict.fart) {
+
 4941.      3
+
 4942.      3// test_cause:
+
 4943.      3// ["()=>{}", "parse_fart", "use_function_not_fart", "=>", 3]
+
 4944.      3
+
 4945.      3                warn("use_function_not_fart", the_fart);
+
 4946.      3            }
+
 4947.      3            the_fart.block = block("body");
+
 4948.     11        } else if (
+
 4949.     11            syntax_dict[token_nxt.id] !== undefined
+
 4950.     11            && syntax_dict[token_nxt.id].fud_stmt !== undefined
+
 4951.     11        ) {
+
 4952.     11
+
 4953.     11// PR-384 - Bugfix - Fixes issue #379 - warn against naked-statement in fart.
+
 4954.     11
+
 4955.     11// test_cause:
+
 4956.     11// ["()=>delete aa", "parse_fart", "unexpected_a_after_b", "=>", 5]
+
 4957.     11
+
 4958.     11            stop("unexpected_a_after_b", token_nxt, token_nxt.id, "=>");
+
 4959.     11
+
 4960.     11// The function's body is an expression.
+
 4961.     11
+
 4962.     11        } else {
+
 4963.     11            the_fart.expression = parse_expression(0);
+
 4964.     13        }
+
 4965.     13
+
 4966.     13// Restore the previous context.
+
 4967.     13
+
 4968.     13        functionage = function_stack.pop();
+
 4969.     13        return the_fart;
+
 4970.     13    }
+
 4971.    631
+
 4972.  12961    function parse_json() {
+
 4973.  12961        let container;
+
 4974.  12961        let is_dup;
+
 4975.  12961        let name;
+
 4976.  12961        let negative;
+
 4977.  12961        switch (token_nxt.id) {
+
 4978.   5262        case "(number)":
+
 4979.   5262            if (!jslint_rgx_json_number.test(token_nxt.value)) {
+
 4980.   5262
+
 4981.   5262// test_cause:
+
 4982.   5262// ["[-.0]", "parse_json", "unexpected_a", ".", 3]
+
 4983.   5262// ["[-0x0]", "parse_json", "unexpected_a", "0x0", 3]
+
 4984.   5262// ["[.0]", "parse_json", "unexpected_a", ".", 2]
+
 4985.   5262// ["[0x0]", "parse_json", "unexpected_a", "0x0", 2]
+
 4986.   5262
+
 4987.   5262                warn("unexpected_a");
+
 4988.   5262            }
+
 4989.   5262            advance("(number)");
+
 4990.   5262            return token_now;
+
 4991.   1767        case "(string)":
+
 4992.   1767            if (token_nxt.quote !== "\"") {
+
 4993.   1767
+
 4994.   1767// test_cause:
+
 4995.   1767// ["['']", "parse_json", "unexpected_a", "'", 2]
+
 4996.   1767
+
 4997.   1767                warn("unexpected_a", token_nxt, token_nxt.quote);
+
 4998.   1767            }
+
 4999.   1767            advance("(string)");
+
 5000.   1767            return token_now;
+
 5001.      3        case "-":
+
 5002.      3            negative = token_nxt;
+
 5003.      3            negative.arity = "unary";
+
 5004.      3            advance("-");
+
 5005.      3
+
 5006.      3// Recurse parse_json().
+
 5007.      3
+
 5008.      3            negative.expression = parse_json();
+
 5009.      3            return negative;
+
 5010.   1649        case "[":
+
 5011.   1649
+
 5012.   1649// test_cause:
+
 5013.   1649// ["[]", "parse_json", "bracket", "", 0]
+
 5014.   1649
+
 5015.   1649            test_cause("bracket");
+
 5016.   1649            container = token_nxt;
+
 5017.   1649            container.expression = [];
+
 5018.   1649            advance("[");
+
 5019.   1649            if (token_nxt.id !== "]") {
+
 5020.   3300                while (true) {
+
 5021.   3300
+
 5022.   3300// Recurse parse_json().
+
 5023.   3300
+
 5024.   3300                    container.expression.push(parse_json());
+
 5025.   3300                    if (token_nxt.id !== ",") {
+
 5026.   3300
+
 5027.   3300// test_cause:
+
 5028.   3300// ["[0,0]", "parse_json", "comma", "", 0]
+
 5029.   3300
+
 5030.   3300                        test_cause("comma");
+
 5031.   3300                        break;
+
 5032.   3300                    }
+
 5033.   3300                    advance(",");
+
 5034.   3300                }
+
 5035.   1649            }
+
 5036.   1649            advance("]", container);
+
 5037.   1649            return container;
+
 5038.    509        case "false":
+
 5039.    511        case "null":
+
 5040.    896        case "true":
+
 5041.    896
+
 5042.    896// test_cause:
+
 5043.    896// ["[false]", "parse_json", "constant", "", 0]
+
 5044.    896// ["[null]", "parse_json", "constant", "", 0]
+
 5045.    896// ["[true]", "parse_json", "constant", "", 0]
+
 5046.    896
+
 5047.    896            test_cause("constant");
+
 5048.    896            advance();
+
 5049.    896            return token_now;
+
 5050.   3379        case "{":
+
 5051.   3379
+
 5052.   3379// test_cause:
+
 5053.   3379// ["{}", "parse_json", "brace", "", 0]
+
 5054.   3379
+
 5055.   3379            test_cause("brace");
+
 5056.   3379            container = token_nxt;
+
 5057.   3379
+
 5058.   3379// Explicit empty-object required to detect "__proto__".
+
 5059.   3379
+
 5060.   3379            is_dup = empty();
+
 5061.   3379            container.expression = [];
+
 5062.   3379            advance("{");
+
 5063.   3379            if (token_nxt.id !== "}") {
+
 5064.   3379
+
 5065.   3379// JSON
+
 5066.   3379// Parse/loop through each property in {...}.
+
 5067.   3379
+
 5068.   9636                while (true) {
+
 5069.   9636                    if (token_nxt.quote !== "\"") {
+
 5070.   9636
+
 5071.   9636// test_cause:
+
 5072.   9636// ["{0:0}", "parse_json", "unexpected_a", "0", 2]
+
 5073.   9636
+
 5074.   9636                        warn(
+
 5075.   9636                            "unexpected_a",
+
 5076.   9636                            token_nxt,
+
 5077.   9636                            token_nxt.quote
+
 5078.   9636                        );
+
 5079.   9636                    }
+
 5080.   9636                    name = token_nxt;
+
 5081.   9636                    advance("(string)");
+
 5082.   9636                    if (is_dup[token_now.value] !== undefined) {
+
 5083.   9636
+
 5084.   9636// test_cause:
+
 5085.   9636// ["{\"aa\":0,\"aa\":0}", "parse_json", "duplicate_a", "aa", 9]
+
 5086.   9636
+
 5087.   9636                        warn("duplicate_a", token_now);
+
 5088.   9636                    } else if (token_now.value === "__proto__") {
+
 5089.   9636
+
 5090.   9636// test_cause:
+
 5091.   9636// ["{\"__proto__\":0}", "parse_json", "weird_property_a", "__proto__", 2]
+
 5092.   9636
+
 5093.   9636                        warn("weird_property_a", token_now);
+
 5094.   9636                    } else {
+
 5095.   9636                        is_dup[token_now.value] = token_now;
+
 5096.   9636                    }
+
 5097.   9636                    advance(":");
+
 5098.   9636                    container.expression.push(
+
 5099.   9636
+
 5100.   9636// Recurse parse_json().
+
 5101.   9636
+
 5102.   9636                        Object.assign(parse_json(), {
+
 5103.   9636                            label: name
+
 5104.   9636                        })
+
 5105.   9636                    );
+
 5106.   9636                    if (token_nxt.id !== ",") {
+
 5107.   9636                        break;
+
 5108.   9636                    }
+
 5109.   9636                    advance(",");
+
 5110.   9636                }
+
 5111.   3379            }
+
 5112.   3379            advance("}", container);
+
 5113.   3379            return container;
+
 5114.      5        default:
+
 5115.      5
+
 5116.      5// test_cause:
+
 5117.      5// ["[undefined]", "parse_json", "unexpected_a", "undefined", 2]
+
 5118.      5
+
 5119.      5            stop("unexpected_a");
+
 5120.  12961        }
+
 5121.  12961    }
+
 5122.    631
+
 5123.  20929    function parse_statement() {
+
 5124.  20929
+
 5125.  20929// Parse a statement. Any statement may have a label, but only four statements
+
 5126.  20929// have use for one. A statement can be one of the standard statements, or
+
 5127.  20929// an assignment expression, or an invocation expression.
+
 5128.  20929
+
 5129.  20929        let first;
+
 5130.  20929        let the_label;
+
 5131.  20929        let the_statement;
+
 5132.  20929        let the_symbol;
+
 5133.  20929        advance();
+
 5134.  20742        if (token_now.identifier && token_nxt.id === ":") {
+
 5135.     13            the_label = token_now;
+
 5136.     13            if (the_label.id === "ignore") {
+
 5137.     13
+
 5138.     13// test_cause:
+
 5139.     13// ["ignore:", "parse_statement", "unexpected_a", "ignore", 1]
+
 5140.     13
+
 5141.     13                warn("unexpected_a", the_label);
+
 5142.     13            }
+
 5143.     13            advance(":");
+
 5144.     13            switch (token_nxt.id) {
+
 5145.     13            case "do":
+
 5146.     13            case "for":
+
 5147.     13            case "switch":
+
 5148.     13            case "while":
+
 5149.     13
+
 5150.     13// test_cause:
+
 5151.     13// ["aa:do{}", "parse_statement", "the_statement_label", "do", 0]
+
 5152.     13// ["aa:for{}", "parse_statement", "the_statement_label", "for", 0]
+
 5153.     13// ["aa:switch{}", "parse_statement", "the_statement_label", "switch", 0]
+
 5154.     13// ["aa:while{}", "parse_statement", "the_statement_label", "while", 0]
+
 5155.     13
+
 5156.     13                test_cause("the_statement_label", token_nxt.id);
+
 5157.     13                enroll(the_label, "label", true);
+
 5158.     13                the_label.dead = false;
+
 5159.     13                the_label.init = true;
+
 5160.     13                the_statement = parse_statement();
+
 5161.     13                functionage.statement_prv = the_statement;
+
 5162.     13                the_statement.label = the_label;
+
 5163.     13                the_statement.statement = true;
+
 5164.     13                return the_statement;
+
 5165.     13            }
+
 5166.     13            advance();
+
 5167.     13
+
 5168.     13// test_cause:
+
 5169.     13// ["aa:", "parse_statement", "unexpected_label_a", "aa", 1]
+
 5170.     13
+
 5171.     13            warn("unexpected_label_a", the_label);
+
 5172.  20920        }
+
 5173.  20920
+
 5174.  20920// Parse the statement.
+
 5175.  20920
+
 5176.  20920        first = token_now;
+
 5177.  20920        first.statement = true;
+
 5178.  20920        the_symbol = syntax_dict[first.id];
+
 5179.  20920        if (
+
 5180.  20920            the_symbol !== undefined
+
 5181.  20920            && the_symbol.fud_stmt !== undefined
+
 5182.  20929
+
 5183.  20929// PR-318 - Bugfix - Fixes issues #316, #317 - dynamic-import().
+
 5184.  20929
+
 5185.  10154            && !(the_symbol.id === "import" && token_nxt.id === "(")
+
 5186.  10152        ) {
+
 5187.  10152            the_symbol.disrupt = false;
+
 5188.  10152            the_symbol.statement = true;
+
 5189.  10152            token_now.arity = "statement";
+
 5190.  10152            the_statement = the_symbol.fud_stmt();
+
 5191.  10152            functionage.statement_prv = the_statement;
+
 5192.  10768        } else {
+
 5193.  10768
+
 5194.  10768// It is an expression statement.
+
 5195.  10768
+
 5196.  10768            the_statement = parse_expression(0, true);
+
 5197.  10768            functionage.statement_prv = the_statement;
+
 5198.  10768            if (the_statement.wrapped && the_statement.id !== "(") {
+
 5199.  10768
+
 5200.  10768// test_cause:
+
 5201.  10768// ["(0)", "parse_statement", "unexpected_a", "(", 1]
+
 5202.  10768
+
 5203.  10768                warn("unexpected_a", first);
+
 5204.  10768            }
+
 5205.  10768            semicolon();
+
 5206.  20826        }
+
 5207.  20826        if (the_label !== undefined) {
+
 5208.      1            the_label.dead = true;
+
 5209.  20826        }
+
 5210.  20826        return the_statement;
+
 5211.  20826    }
+
 5212.    631
+
 5213.   7501    function parse_statements() {
+
 5214.   7501
+
 5215.   7501// Parse a list of statements. Give a warning if an unreachable statement
+
 5216.   7501// follows a disruptive statement.
+
 5217.   7501
+
 5218.   7501        const statement_list = [];
+
 5219.   7501        let a_statement;
+
 5220.   7501        let disrupt = false;
+
 5221.   7501
+
 5222.   7501// Parse/loop each statement until a statement-terminator is reached.
+
 5223.   7501
+
 5224.  28003        while (true) {
+
 5225.  28003            switch (token_nxt.id) {
+
 5226.  28003            case "(end)":
+
 5227.  28003            case "case":
+
 5228.  28003            case "default":
+
 5229.  28003            case "else":
+
 5230.  28003            case "}":
+
 5231.  28003
+
 5232.  28003// test_cause:
+
 5233.  28003// [";", "parse_statements", "closer", "", 0]
+
 5234.  28003// ["case", "parse_statements", "closer", "", 0]
+
 5235.  28003// ["default", "parse_statements", "closer", "", 0]
+
 5236.  28003// ["else", "parse_statements", "closer", "", 0]
+
 5237.  28003// ["}", "parse_statements", "closer", "", 0]
+
 5238.  28003
+
 5239.  28003                test_cause("closer");
+
 5240.  28003                return statement_list;
+
 5241.  28003            }
+
 5242.  28003            a_statement = parse_statement();
+
 5243.  28003            statement_list.push(a_statement);
+
 5244.  28003            if (disrupt) {
+
 5245.  28003
+
 5246.  28003// test_cause:
+
 5247.  28003// ["while(0){break;0;}", "parse_statements", "unreachable_a", "0", 16]
+
 5248.  28003
+
 5249.  28003                warn("unreachable_a", a_statement);
+
 5250.  28003            }
+
 5251.  28003            disrupt = a_statement.disrupt;
+
 5252.  28003        }
+
 5253.   7501    }
+
 5254.    631
+
 5255.   1262    function postassign(id) {
+
 5256.   1262
+
 5257.   1262// Create one of the postassign operators.
+
 5258.   1262
+
 5259.   1262        const the_symbol = symbol(id, 150);
+
 5260.      1        the_symbol.led_infix = function (left) {
+
 5261.      1            token_now.expression = left;
+
 5262.      1            token_now.arity = "postassign";
+
 5263.      1            check_mutation(token_now.expression);
+
 5264.      1            return token_now;
+
 5265.      1        };
+
 5266.   1262        return the_symbol;
+
 5267.   1262    }
+
 5268.    631
+
 5269.   1262    function preassign(id) {
+
 5270.   1262
+
 5271.   1262// Create one of the preassign operators.
+
 5272.   1262
+
 5273.   1262        const the_symbol = symbol(id);
+
 5274.      2        the_symbol.nud_prefix = function () {
+
 5275.      2            const the_token = token_now;
+
 5276.      2            the_token.arity = "preassign";
+
 5277.      2            the_token.expression = parse_expression(150);
+
 5278.      2            check_mutation(the_token.expression);
+
 5279.      2            return the_token;
+
 5280.      2        };
+
 5281.   1262        return the_symbol;
+
 5282.   1262    }
+
 5283.    631
+
 5284.  10727    function prefix(id, f) {
+
 5285.  10727
+
 5286.  10727// Create a prefix operator.
+
 5287.  10727
+
 5288.  10727        const the_symbol = symbol(id);
+
 5289.   5744        the_symbol.nud_prefix = function () {
+
 5290.   5744            const the_token = token_now;
+
 5291.   5744            the_token.arity = "unary";
+
 5292.   4932            if (typeof f === "function") {
+
 5293.   4932                return f();
+
 5294.   4932            }
+
 5295.    812            the_token.expression = parse_expression(150);
+
 5296.    812            return the_token;
+
 5297.    812        };
+
 5298.  10727        return the_symbol;
+
 5299.  10727    }
+
 5300.    631
+
 5301.      1    function prefix_assign_divide() {
+
 5302.      1
+
 5303.      1// test_cause:
+
 5304.      1// ["/=", "prefix_assign_divide", "expected_a_b", "/=", 1]
+
 5305.      1
+
 5306.      1        stop("expected_a_b", token_now, "/\\=", "/=");
+
 5307.      1    }
+
 5308.    631
+
 5309.    136    function prefix_async() {
+
 5310.    136        let the_async = token_now;
+
 5311.    136        let the_function;
+
 5312.    136        token_nxt.arity = the_async.arity;
+
 5313.    136
+
 5314.    136// PR-414 - Parse async fart.
+
 5315.    136
+
 5316.      3        if (token_nxt.fart) {
+
 5317.      3            advance("(");
+
 5318.      3            the_function = Object.assign(token_now.fart, {
+
 5319.      3                async: 1
+
 5320.      3            });
+
 5321.      3            if (!option_dict.fart) {
+
 5322.      3
+
 5323.      3// test_cause:
+
 5324.      3// ["async()=>0", "prefix_async", "use_function_not_fart", "=>", 8]
+
 5325.      3
+
 5326.      3                warn("use_function_not_fart", the_function);
+
 5327.      3            }
+
 5328.      3            prefix_lparen();
+
 5329.      3
+
 5330.      3// Parse async function.
+
 5331.      3
+
 5332.    133        } else {
+
 5333.    133            advance("function");
+
 5334.    133            the_function = Object.assign(token_now, {
+
 5335.    133                async: 1
+
 5336.    133            });
+
 5337.    133            prefix_function();
+
 5338.    133        }
+
 5339.      3        if (the_function.async === 1) {
+
 5340.      3
+
 5341.      3// test_cause:
+
 5342.      3// ["
+
 5343.      3// async function aa(){}
+
 5344.      3// ", "prefix_async", "missing_await_statement", "function", 7]
+
 5345.      3
+
 5346.      3            warn("missing_await_statement", the_function);
+
 5347.      3        }
+
 5348.    136        return the_function;
+
 5349.    136    }
+
 5350.    631
+
 5351.    297    function prefix_await() {
+
 5352.    297        const the_await = token_now;
+
 5353.    297
+
 5354.    297// PR-370 - Add top-level-await support.
+
 5355.    297
+
 5356.      4        if (functionage.async === 0 && functionage !== token_global) {
+
 5357.      2
+
 5358.      2// test_cause:
+
 5359.      2// ["function aa(){aa=await 0;}", "prefix_await", "unexpected_a", "await", 18]
+
 5360.      2// ["function aa(){await 0;}", "prefix_await", "unexpected_a", "await", 15]
+
 5361.      2
+
 5362.      2            warn("unexpected_a", the_await);
+
 5363.    295        } else {
+
 5364.    295            functionage.async += 1;
+
 5365.    295        }
+
 5366.    185        if (the_await.arity === "statement") {
+
 5367.    185
+
 5368.    185// PR-405 - Bugfix - fix expression after "await" mis-identified as statement.
+
 5369.    185
+
 5370.    185            the_await.expression = parse_expression(150);
+
 5371.    185            semicolon();
+
 5372.    185        } else {
+
 5373.    112            the_await.expression = parse_expression(150);
+
 5374.    112        }
+
 5375.    297        return the_await;
+
 5376.    297    }
+
 5377.    631
+
 5378.      1    function prefix_fart() {
+
 5379.      1
+
 5380.      1// test_cause:
+
 5381.      1// ["=>0", "prefix_fart", "expected_a_before_b", "=>", 1]
+
 5382.      1
+
 5383.      1        return stop("expected_a_before_b", token_now, "()", "=>");
+
 5384.      1    }
+
 5385.    631
+
 5386.   2005    function prefix_function(the_function) {
+
 5387.     11        let name = the_function && the_function.name;
+
 5388.   1994        if (the_function === undefined) {
+
 5389.   1994            the_function = token_now;
+
 5390.   1994
+
 5391.   1994// A function statement must have a name that will be in the parent's scope.
+
 5392.   1994
+
 5393.   1994            if (the_function.arity === "statement") {
+
 5394.   1994                if (!token_nxt.identifier) {
+
 5395.   1994
+
 5396.   1994// test_cause:
+
 5397.   1994// ["function(){}", "prefix_function", "expected_identifier_a", "(", 9]
+
 5398.   1994// ["function*aa(){}", "prefix_function", "expected_identifier_a", "*", 9]
+
 5399.   1994
+
 5400.   1994                    return stop("expected_identifier_a");
+
 5401.   1994                }
+
 5402.   1994                name = token_nxt;
+
 5403.   1994                enroll(name, "variable", true);
+
 5404.   1994                the_function.name = Object.assign(name, {
+
 5405.   1994                    calls: empty(),
+
 5406.   1994
+
 5407.   1994// PR-331 - Bugfix - Fixes issue #272 - function hoisting not allowed.
+
 5408.   1994
+
 5409.   1994                    dead: false,
+
 5410.   1994                    init: true
+
 5411.   1994                });
+
 5412.   1994                advance();
+
 5413.   1994            } else if (name === undefined) {
+
 5414.   1994
+
 5415.   1994// A function expression may have an optional name.
+
 5416.   1994
+
 5417.   1994                the_function.name = anon;
+
 5418.   1994                if (token_nxt.identifier) {
+
 5419.   1994                    name = token_nxt;
+
 5420.   1994                    the_function.name = name;
+
 5421.   1994                    advance();
+
 5422.   1994                }
+
 5423.   1994            }
+
 5424.   2003        }
+
 5425.   2003
+
 5426.   2003//  Probably deadcode.
+
 5427.   2003//  if (mode_mega) {
+
 5428.   2003//      warn("unexpected_a", the_function);
+
 5429.   2003//  }
+
 5430.   2003//  jslint_assert(!mode_mega, `Expected !mode_mega.`);
+
 5431.   2003
+
 5432.   2003// PR-378 - Relax warning "function_in_loop".
+
 5433.   2003//
+
 5434.   2003// // Don't create functions in loops. It is inefficient, and it can lead to
+
 5435.   2003// // scoping errors.
+
 5436.   2003//
+
 5437.   2003//         if (functionage.loop > 0) {
+
 5438.   2003//
+
 5439.   2003// // test_cause:
+
 5440.   2003// // ["
+
 5441.   2003// // while(0){aa.map(function(){});}
+
 5442.   2003// // ", "prefix_function", "function_in_loop", "function", 17]
+
 5443.   2003//
+
 5444.   2003//             warn("function_in_loop", the_function);
+
 5445.   2003//         }
+
 5446.   2003
+
 5447.   2003// Give the function properties for storing its names and for observing the
+
 5448.   2003// depth of loops and switches.
+
 5449.   2003
+
 5450.   2003        Object.assign(the_function, {
+
 5451.   2003            async: the_function.async || 0,
+
 5452.   2005            context: empty(),
+
 5453.   2005            finally: 0,
+
 5454.   2005            level: functionage.level + 1,
+
 5455.   2005            loop: 0,
+
 5456.   2005            statement_prv: undefined,
+
 5457.   2005            switch: 0,
+
 5458.   2005            try: 0
+
 5459.   2005        });
+
 5460.    929        if (the_function.arity !== "statement" && typeof name === "object") {
+
 5461.     38
+
 5462.     38// test_cause:
+
 5463.     38// ["let aa=function bb(){return;};", "prefix_function", "expression", "bb", 0]
+
 5464.     38
+
 5465.     38            test_cause("expression", name.id);
+
 5466.     38            enroll(name, "function", true);
+
 5467.     38            name.dead = false;
+
 5468.     38            name.init = true;
+
 5469.     38            name.used = 1;
+
 5470.   2003        }
+
 5471.   2003
+
 5472.   2003// PR-334 - Bugfix - fix function-redefinition not warned inside function-call.
+
 5473.   2003// Push the current function context and establish a new one.
+
 5474.   2003
+
 5475.   2003        function_list.push(the_function);
+
 5476.   2003        function_stack.push(functionage);
+
 5477.   2003        functionage = the_function;
+
 5478.   2003
+
 5479.   2003// Parse the parameter list.
+
 5480.   2003
+
 5481.   2003        advance("(");
+
 5482.   2003        token_now.arity = "function";
+
 5483.   2003        prefix_function_parameter(the_function);
+
 5484.   2003
+
 5485.   2003// The function's body is a block.
+
 5486.   2003
+
 5487.   2003        the_function.block = block("body");
+
 5488.   2003        if (
+
 5489.   2003            the_function.arity === "statement"
+
 5490.   2003            && token_nxt.line === token_now.line
+
 5491.      2        ) {
+
 5492.      2
+
 5493.      2// test_cause:
+
 5494.      2// ["function aa(){}0", "prefix_function", "unexpected_a", "0", 16]
+
 5495.      2
+
 5496.      2            return stop("unexpected_a");
+
 5497.   1990        }
+
 5498.   1990        if (
+
 5499.   1990            token_nxt.id === "."
+
 5500.   1990            || token_nxt.id === "?."
+
 5501.   2005
+
 5502.   2005// PR-459 - Allow destructuring-assignment after function-definition.
+
 5503.   2005
+
 5504.   2005            // || token_nxt.id === "["
+
 5505.      2        ) {
+
 5506.      2
+
 5507.      2// test_cause:
+
 5508.      2// ["function aa(){}\n.aa", "prefix_function", "unexpected_a", ".", 1]
+
 5509.      2// ["function aa(){}\n?.aa", "prefix_function", "unexpected_a", "?.", 1]
+
 5510.      2
+
 5511.      2            warn("unexpected_a");
+
 5512.   1990        }
+
 5513.   1990
+
 5514.   1990// Check functions are ordered.
+
 5515.   1990
+
 5516.   1990        check_ordered(
+
 5517.   1990            "function",
+
 5518.   1990            function_list.slice(
+
 5519.   1990                function_list.indexOf(the_function) + 1
+
 5520.   2515            ).map(function ({
+
 5521.   2515                level,
+
 5522.   2515                name
+
 5523.   2515            }) {
+
 5524.   1990                return (level === the_function.level + 1) && name;
+
 5525.   2515            }).filter(function (name) {
+
 5526.   2510                return option_dict.beta && name && name.id;
+
 5527.   2515            })
+
 5528.   1990        );
+
 5529.   1990
+
 5530.   1990// Restore the previous context.
+
 5531.   1990
+
 5532.   1990        functionage = function_stack.pop();
+
 5533.   1990        return the_function;
+
 5534.   1990    }
+
 5535.    631
+
 5536.   2017    function prefix_function_parameter(the_function) {
+
 5537.   2017
+
 5538.   2017// This function will parse input <parameters> at beginning of <the_function>
+
 5539.   2017
+
 5540.   2017        let optional;
+
 5541.   2017        let parameters = [];
+
 5542.   2017        let signature = ["("];
+
 5543.   2017        let subparam;
+
 5544.   2781        function param_enroll(name) {
+
 5545.   2514            if (name.identifier) {
+
 5546.   2514                enroll(name, "parameter", false);
+
 5547.   2514            } else {
+
 5548.    267
+
 5549.    267// test_cause:
+
 5550.    267// ["([aa])=>0", "param_enroll", "use_function_not_fart", "=>", 7]
+
 5551.    267// ["({aa})=>0", "param_enroll", "use_function_not_fart", "=>", 7]
+
 5552.    267
+
 5553.    267                if (the_function.id === "=>" && !option_dict.fart) {
+
 5554.    267                    warn("use_function_not_fart", the_function);
+
 5555.    267                }
+
 5556.    267
+
 5557.    267// Recurse param_enroll().
+
 5558.    267
+
 5559.    267                name.names.forEach(param_enroll);
+
 5560.    267            }
+
 5561.   2781        }
+
 5562.   2077        function param_parse() {
+
 5563.   2077            let ellipsis = false;
+
 5564.   2077            let param;
+
 5565.    227            if (token_nxt.id === "{") {
+
 5566.    227                if (optional !== undefined) {
+
 5567.    227
+
 5568.    227// test_cause:
+
 5569.    227// ["function aa(aa=0,{}){}", "param_parse", "required_a_optional_b", "aa", 18]
+
 5570.    227
+
 5571.    227                    warn(
+
 5572.    227                        "required_a_optional_b",
+
 5573.    227                        token_nxt,
+
 5574.    227                        token_nxt.id,
+
 5575.    227                        optional.id
+
 5576.    227                    );
+
 5577.    227                }
+
 5578.    227                param = token_nxt;
+
 5579.    227                param.names = [];
+
 5580.    227                advance("{");
+
 5581.    227                signature.push("{");
+
 5582.    625                while (true) {
+
 5583.    625                    subparam = token_nxt;
+
 5584.    625                    if (!subparam.identifier) {
+
 5585.    625
+
 5586.    625// test_cause:
+
 5587.    625// ["function aa(aa=0,{}){}", "param_parse", "expected_identifier_a", "}", 19]
+
 5588.    625// ["function aa({0}){}", "param_parse", "expected_identifier_a", "0", 14]
+
 5589.    625
+
 5590.    625                        return stop("expected_identifier_a");
+
 5591.    625                    }
+
 5592.    625                    survey(subparam);
+
 5593.    625                    advance();
+
 5594.    625                    signature.push(subparam.id);
+
 5595.    625                    if (token_nxt.id === ":") {
+
 5596.    625                        advance(":");
+
 5597.    625                        advance();
+
 5598.    625                        token_now.label = subparam;
+
 5599.    625                        subparam = token_now;
+
 5600.    625                        if (!subparam.identifier) {
+
 5601.    625
+
 5602.    625// test_cause:
+
 5603.    625// ["function aa({aa:0}){}", "param_parse", "expected_identifier_a", "}", 18]
+
 5604.    625
+
 5605.    625                            return stop(
+
 5606.    625                                "expected_identifier_a",
+
 5607.    625                                token_nxt
+
 5608.    625                            );
+
 5609.    625                        }
+
 5610.    625                    }
+
 5611.    625
+
 5612.    625// test_cause:
+
 5613.    625// ["function aa({aa=aa},aa){}", "param_parse", "equal", "", 0]
+
 5614.    625
+
 5615.    625                    test_cause("equal");
+
 5616.    625                    if (token_nxt.id === "=") {
+
 5617.    625                        advance("=");
+
 5618.    625                        subparam.expression = parse_expression();
+
 5619.    625                        param.open = true;
+
 5620.    625                    }
+
 5621.    625                    param.names.push(subparam);
+
 5622.    625                    if (token_nxt.id === ",") {
+
 5623.    625                        advance(",");
+
 5624.    625                        signature.push(", ");
+
 5625.    625                    } else {
+
 5626.    625                        break;
+
 5627.    625                    }
+
 5628.    625                }
+
 5629.    227                parameters.push(param);
+
 5630.    227
+
 5631.    227// test_cause:
+
 5632.    227// ["
+
 5633.    227// function aa({bb,aa}){}
+
 5634.    227// ", "check_ordered", "expected_a_b_before_c_d", "aa", 17]
+
 5635.    227
+
 5636.    227                check_ordered("parameter", param.names);
+
 5637.    227                advance("}");
+
 5638.    227                signature.push("}");
+
 5639.    227                if (token_nxt.id === ",") {
+
 5640.    227                    advance(",");
+
 5641.    227                    signature.push(", ");
+
 5642.    227                    param_parse();
+
 5643.    227                    return;
+
 5644.    227                }
+
 5645.   1850            } else if (token_nxt.id === "[") {
+
 5646.   1850                if (optional !== undefined) {
+
 5647.   1850
+
 5648.   1850// test_cause:
+
 5649.   1850// ["function aa(aa=0,[]){}", "param_parse", "required_a_optional_b", "aa", 18]
+
 5650.   1850
+
 5651.   1850                    warn(
+
 5652.   1850                        "required_a_optional_b",
+
 5653.   1850                        token_nxt,
+
 5654.   1850                        token_nxt.id,
+
 5655.   1850                        optional.id
+
 5656.   1850                    );
+
 5657.   1850                }
+
 5658.   1850                param = token_nxt;
+
 5659.   1850                param.names = [];
+
 5660.   1850                advance("[");
+
 5661.   1850                signature.push("[]");
+
 5662.   1850                while (true) {
+
 5663.   1850                    subparam = token_nxt;
+
 5664.   1850                    if (!subparam.identifier) {
+
 5665.   1850
+
 5666.   1850// test_cause:
+
 5667.   1850// ["function aa(aa=0,[]){}", "param_parse", "expected_identifier_a", "]", 19]
+
 5668.   1850
+
 5669.   1850                        return stop("expected_identifier_a");
+
 5670.   1850                    }
+
 5671.   1850                    advance();
+
 5672.   1850                    param.names.push(subparam);
+
 5673.   1850
+
 5674.   1850// test_cause:
+
 5675.   1850// ["function aa([aa=aa],aa){}", "param_parse", "id", "", 0]
+
 5676.   1850
+
 5677.   1850                    test_cause("id");
+
 5678.   1850                    if (token_nxt.id === "=") {
+
 5679.   1850                        advance("=");
+
 5680.   1850                        subparam.expression = parse_expression();
+
 5681.   1850                        param.open = true;
+
 5682.   1850                    }
+
 5683.   1850                    if (token_nxt.id === ",") {
+
 5684.   1850                        advance(",");
+
 5685.   1850                    } else {
+
 5686.   1850                        break;
+
 5687.   1850                    }
+
 5688.   1850                }
+
 5689.   1850                parameters.push(param);
+
 5690.   1850                advance("]");
+
 5691.   1850                if (token_nxt.id === ",") {
+
 5692.   1850                    advance(",");
+
 5693.   1850                    signature.push(", ");
+
 5694.   1850                    param_parse();
+
 5695.   1850                    return;
+
 5696.   1850                }
+
 5697.   1850            } else {
+
 5698.   1850                if (token_nxt.id === "...") {
+
 5699.   1850                    ellipsis = true;
+
 5700.   1850                    signature.push("...");
+
 5701.   1850                    advance("...");
+
 5702.   1850                    if (optional !== undefined) {
+
 5703.   1850
+
 5704.   1850// test_cause:
+
 5705.   1850// ["function aa(aa=0,...){}", "param_parse", "required_a_optional_b", "aa", 21]
+
 5706.   1850
+
 5707.   1850                        warn(
+
 5708.   1850                            "required_a_optional_b",
+
 5709.   1850                            token_nxt,
+
 5710.   1850                            token_nxt.id,
+
 5711.   1850                            optional.id
+
 5712.   1850                        );
+
 5713.   1850                    }
+
 5714.   1850                }
+
 5715.   1850                if (!token_nxt.identifier) {
+
 5716.   1850
+
 5717.   1850// test_cause:
+
 5718.   1850// ["function aa(0){}", "param_parse", "expected_identifier_a", "0", 13]
+
 5719.   1850
+
 5720.   1850                    return stop("expected_identifier_a");
+
 5721.   1850                }
+
 5722.   1850                param = token_nxt;
+
 5723.   1850                parameters.push(param);
+
 5724.   1850                advance();
+
 5725.   1850                signature.push(param.id);
+
 5726.   1850                if (ellipsis) {
+
 5727.   1850                    param.ellipsis = true;
+
 5728.   1850                } else {
+
 5729.   1850                    if (token_nxt.id === "=") {
+
 5730.   1850                        optional = param;
+
 5731.   1850                        advance("=");
+
 5732.   1850                        param.expression = parse_expression(0);
+
 5733.   1850                    } else {
+
 5734.   1850                        if (optional !== undefined) {
+
 5735.   1850
+
 5736.   1850// test_cause:
+
 5737.   1850// ["function aa(aa=0,bb){}", "param_parse", "required_a_optional_b", "aa", 18]
+
 5738.   1850
+
 5739.   1850                            warn(
+
 5740.   1850                                "required_a_optional_b",
+
 5741.   1850                                param,
+
 5742.   1850                                param.id,
+
 5743.   1850                                optional.id
+
 5744.   1850                            );
+
 5745.   1850                        }
+
 5746.   1850                    }
+
 5747.   1850                    if (token_nxt.id === ",") {
+
 5748.   1850                        advance(",");
+
 5749.   1850                        signature.push(", ");
+
 5750.   1850                        param_parse();
+
 5751.   1850                        return;
+
 5752.   1850                    }
+
 5753.   1850                }
+
 5754.   1850            }
+
 5755.   2077        }
+
 5756.   2017
+
 5757.   2017// test_cause:
+
 5758.   2017// ["function aa(){}", "prefix_function_parameter", "opener", "(", 0]
+
 5759.   2017
+
 5760.   2017        test_cause("opener", token_now.id);
+
 5761.   2017        token_now.free = false;
+
 5762.   1485        if (token_nxt.id !== ")" && token_nxt.id !== "(end)") {
+
 5763.   1485            param_parse();
+
 5764.   2009        }
+
 5765.   2009        advance(")");
+
 5766.   2009        signature.push(")");
+
 5767.   2009        parameters.forEach(param_enroll);
+
 5768.   2009        the_function.parameters = parameters;
+
 5769.   2009        the_function.signature = signature.join("");
+
 5770.   2009    }
+
 5771.    631
+
 5772.    588    function prefix_lbrace() {
+
 5773.    588        const seen = empty();
+
 5774.    588        const the_brace = token_now;
+
 5775.    588        let extra;
+
 5776.    588        let full;
+
 5777.    588        let id;
+
 5778.    588        let name;
+
 5779.    588        let the_colon;
+
 5780.    588        let value;
+
 5781.    588        the_brace.expression = [];
+
 5782.    548        if (token_nxt.id !== "}") {
+
 5783.    548
+
 5784.    548// Parse/loop through each property in {...}.
+
 5785.    548
+
 5786.   1996            while (true) {
+
 5787.   1996                name = token_nxt;
+
 5788.   1996                advance();
+
 5789.   1996                if (
+
 5790.   1996                    (name.id === "get" || name.id === "set")
+
 5791.   1996                    && token_nxt.identifier
+
 5792.   1996                ) {
+
 5793.   1996                    if (!option_dict.getset) {
+
 5794.   1996
+
 5795.   1996// test_cause:
+
 5796.   1996// ["aa={get aa(){}}", "prefix_lbrace", "unexpected_a", "get", 5]
+
 5797.   1996
+
 5798.   1996                        warn("unexpected_a", name);
+
 5799.   1996                    }
+
 5800.   1996                    extra = name.id;
+
 5801.   1996                    full = extra + " " + token_nxt.id;
+
 5802.   1996                    name = token_nxt;
+
 5803.   1996                    advance();
+
 5804.   1996                    id = survey(name);
+
 5805.   1996                    if (seen[full] === true || seen[id] === true) {
+
 5806.   1996
+
 5807.   1996// test_cause:
+
 5808.   1996// ["aa={get aa(){},get aa(){}}", "prefix_lbrace", "duplicate_a", "aa", 20]
+
 5809.   1996
+
 5810.   1996                        warn("duplicate_a", name);
+
 5811.   1996                    }
+
 5812.   1996                    seen[id] = false;
+
 5813.   1996                    seen[full] = true;
+
 5814.   1996                } else if (name.id === "`") {
+
 5815.   1996
+
 5816.   1996// test_cause:
+
 5817.   1996// ["aa={`aa`:0}", "prefix_lbrace", "unexpected_a", "`", 5]
+
 5818.   1996
+
 5819.   1996                    stop("unexpected_a", name);
+
 5820.   1996
+
 5821.   1996                } else {
+
 5822.   1996                    id = survey(name);
+
 5823.   1996                    if (typeof seen[id] === "boolean") {
+
 5824.   1996
+
 5825.   1996// test_cause:
+
 5826.   1996// ["aa={aa,aa}", "prefix_lbrace", "duplicate_a", "aa", 8]
+
 5827.   1996
+
 5828.   1996                        warn("duplicate_a", name);
+
 5829.   1996                    }
+
 5830.   1996                    seen[id] = true;
+
 5831.   1996                }
+
 5832.   1996                if (name.identifier) {
+
 5833.   1996                    if (token_nxt.id === "}" || token_nxt.id === ",") {
+
 5834.   1996                        if (typeof extra === "string") {
+
 5835.   1996
+
 5836.   1996// test_cause:
+
 5837.   1996// ["aa={get aa}", "prefix_lbrace", "closer", "", 0]
+
 5838.   1996
+
 5839.   1996                            test_cause("closer");
+
 5840.   1996                            advance("(");
+
 5841.   1996                        }
+
 5842.   1996                        value = parse_expression(Infinity, true);
+
 5843.   1996                    } else if (token_nxt.id === "(") {
+
 5844.   1996
+
 5845.   1996// test_cause:
+
 5846.   1996// ["aa={aa()}", "prefix_lbrace", "paren", "", 0]
+
 5847.   1996// ["aa={get aa(){}}", "prefix_lbrace", "paren", "", 0]
+
 5848.   1996
+
 5849.   1996                        test_cause("paren");
+
 5850.   1996                        value = prefix_function({
+
 5851.   1996                            arity: "unary",
+
 5852.   1996                            from: name.from,
+
 5853.   1996                            id: "function",
+
 5854.   1996                            line: name.line,
+
 5855.   1996                            name: (
+
 5856.   1996                                typeof extra === "string"
+
 5857.   1996                                ? extra
+
 5858.   1996                                : id
+
 5859.   1996                            ),
+
 5860.   1996                            thru: name.from
+
 5861.   1996                        });
+
 5862.   1996                    } else {
+
 5863.   1996                        if (typeof extra === "string") {
+
 5864.   1996
+
 5865.   1996// test_cause:
+
 5866.   1996// ["aa={get aa.aa}", "prefix_lbrace", "paren", "", 0]
+
 5867.   1996
+
 5868.   1996                            test_cause("paren");
+
 5869.   1996                            advance("(");
+
 5870.   1996                        }
+
 5871.   1996                        the_colon = token_nxt;
+
 5872.   1996                        advance(":");
+
 5873.   1996                        value = parse_expression(0);
+
 5874.   1996                        if (
+
 5875.   1996                            value.id === name.id
+
 5876.   1996                            && value.id !== "function"
+
 5877.   1996                        ) {
+
 5878.   1996
+
 5879.   1996// test_cause:
+
 5880.   1996// ["aa={aa:aa}", "prefix_lbrace", "unexpected_a", ": aa", 7]
+
 5881.   1996
+
 5882.   1996                            warn("unexpected_a", the_colon, ": " + name.id);
+
 5883.   1996                        }
+
 5884.   1996                    }
+
 5885.   1996                    value.label = name;
+
 5886.   1996                    if (typeof extra === "string") {
+
 5887.   1996                        value.extra = extra;
+
 5888.   1996                    }
+
 5889.   1996                    the_brace.expression.push(value);
+
 5890.   1996                } else {
+
 5891.   1996
+
 5892.   1996// test_cause:
+
 5893.   1996// ["aa={\"aa\":0}", "prefix_lbrace", "colon", "", 0]
+
 5894.   1996
+
 5895.   1996                    test_cause("colon");
+
 5896.   1996                    advance(":");
+
 5897.   1996                    value = parse_expression(0);
+
 5898.   1996                    value.label = name;
+
 5899.   1996                    the_brace.expression.push(value);
+
 5900.   1996                }
+
 5901.   1996                if (token_nxt.id !== ",") {
+
 5902.   1996                    break;
+
 5903.   1996                }
+
 5904.   1996
+
 5905.   1996// test_cause:
+
 5906.   1996// ["aa={\"aa\":0,\"bb\":0}", "prefix_lbrace", "comma", "", 0]
+
 5907.   1996
+
 5908.   1996                test_cause("comma");
+
 5909.   1996                advance(",");
+
 5910.   1996                if (token_nxt.id === "}") {
+
 5911.   1996
+
 5912.   1996// test_cause:
+
 5913.   1996// ["let aa={aa:0,}", "prefix_lbrace", "unexpected_a", ",", 13]
+
 5914.   1996
+
 5915.   1996                    warn("unexpected_a", token_now);
+
 5916.   1996                    break;
+
 5917.   1996                }
+
 5918.   1996            }
+
 5919.    583        }
+
 5920.    583
+
 5921.    583// test_cause:
+
 5922.    583// ["aa={bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8]
+
 5923.    583
+
 5924.    583        check_ordered(
+
 5925.    583            "property",
+
 5926.   1991            the_brace.expression.map(function ({
+
 5927.   1991                label
+
 5928.   1991            }) {
+
 5929.   1991                return label;
+
 5930.   1991            })
+
 5931.    583        );
+
 5932.    583        advance("}");
+
 5933.    583        return the_brace;
+
 5934.    583    }
+
 5935.    631
+
 5936.    759    function prefix_lbracket() {
+
 5937.    759        const the_token = token_now;
+
 5938.    759        let element;
+
 5939.    759        let ellipsis;
+
 5940.    759        the_token.expression = [];
+
 5941.    392        if (token_nxt.id !== "]") {
+
 5942.    392
+
 5943.    392// Parse/loop through each element in [...].
+
 5944.    392
+
 5945.   1884            while (true) {
+
 5946.   1884                ellipsis = false;
+
 5947.   1884                if (token_nxt.id === "...") {
+
 5948.   1884                    ellipsis = true;
+
 5949.   1884                    advance("...");
+
 5950.   1884                }
+
 5951.   1884                element = parse_expression(10);
+
 5952.   1884                if (ellipsis) {
+
 5953.   1884                    element.ellipsis = true;
+
 5954.   1884                }
+
 5955.   1884                the_token.expression.push(element);
+
 5956.   1884                if (token_nxt.id !== ",") {
+
 5957.   1884                    break;
+
 5958.   1884                }
+
 5959.   1884                advance(",");
+
 5960.   1884                if (token_nxt.id === "]") {
+
 5961.   1884
+
 5962.   1884// test_cause:
+
 5963.   1884// ["let aa=[0,]", "prefix_lbracket", "unexpected_a", ",", 10]
+
 5964.   1884
+
 5965.   1884                    warn("unexpected_a", token_now);
+
 5966.   1884                    break;
+
 5967.   1884                }
+
 5968.   1884            }
+
 5969.    392        }
+
 5970.    759        advance("]");
+
 5971.    759        return the_token;
+
 5972.    759    }
+
 5973.    631
+
 5974.   1616    function prefix_lparen() {
+
 5975.   1616        let the_paren = token_now;
+
 5976.   1616        let the_value;
+
 5977.   1616
+
 5978.   1616// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart.
+
 5979.   1616
+
 5980.     14        if (token_now.fart) {
+
 5981.     14            return parse_fart(token_now.fart);
+
 5982.   1602        }
+
 5983.   1602
+
 5984.   1602// test_cause:
+
 5985.   1602// ["(0)", "prefix_lparen", "expr", "", 0]
+
 5986.   1602
+
 5987.   1602        test_cause("expr");
+
 5988.   1602        the_paren.free = true;
+
 5989.   1602        the_value = parse_expression(0);
+
 5990.   1602        if (the_value.wrapped === true) {
+
 5991.      1
+
 5992.      1// test_cause:
+
 5993.      1// ["((0))", "prefix_lparen", "unexpected_a", "(", 1]
+
 5994.      1
+
 5995.      1            warn("unexpected_a", the_paren);
+
 5996.   1602        }
+
 5997.   1602        the_value.wrapped = true;
+
 5998.   1602        advance(")", the_paren);
+
 5999.   1602        return the_value;
+
 6000.   1602    }
+
 6001.    631
+
 6002.    155    function prefix_new() {
+
 6003.    155        const the_new = token_now;
+
 6004.    155        let right;
+
 6005.    155        right = parse_expression(160);
+
 6006.      1        if (token_nxt.id !== "(") {
+
 6007.      1
+
 6008.      1// test_cause:
+
 6009.      1// ["new aa", "prefix_new", "expected_a_before_b", "(end)", 1]
+
 6010.      1
+
 6011.      1            warn("expected_a_before_b", token_nxt, "()", artifact());
+
 6012.      1        }
+
 6013.    155        the_new.expression = right;
+
 6014.    155        return the_new;
+
 6015.    155    }
+
 6016.    631
+
 6017.    782    function prefix_tick() {
+
 6018.    782        const the_tick = token_now;
+
 6019.    782        the_tick.value = [];
+
 6020.    782        the_tick.expression = [];
+
 6021.    782        if (token_nxt.id !== "`") {
+
 6022.    782
+
 6023.    782// Parse/loop through each token in `${...}`.
+
 6024.    782
+
 6025.   1499            while (true) {
+
 6026.   1499                advance("(string)");
+
 6027.   1499                the_tick.value.push(token_now);
+
 6028.   1499                if (token_nxt.id !== "${") {
+
 6029.   1499                    break;
+
 6030.   1499                }
+
 6031.   1499                advance("${");
+
 6032.   1499
+
 6033.   1499// test_cause:
+
 6034.   1499// ["let aa=`${}`;", "prefix_tick", "${", "", 0]
+
 6035.   1499
+
 6036.   1499                test_cause("${");
+
 6037.   1499                the_tick.expression.push(parse_expression(0));
+
 6038.   1499                advance("}");
+
 6039.   1499            }
+
 6040.    780        }
+
 6041.    780        advance("`");
+
 6042.    780        return the_tick;
+
 6043.    780    }
+
 6044.    631
+
 6045.      2    function prefix_void() {
+
 6046.      2        const the_void = token_now;
+
 6047.      2
+
 6048.      2// test_cause:
+
 6049.      2// ["void 0", "prefix_void", "unexpected_a", "void", 1]
+
 6050.      2// ["void", "prefix_void", "unexpected_a", "void", 1]
+
 6051.      2
+
 6052.      2        warn("unexpected_a", the_void);
+
 6053.      2        the_void.expression = parse_expression(0);
+
 6054.      2        return the_void;
+
 6055.      2    }
+
 6056.    631
+
 6057.  13430    function semicolon() {
+
 6058.  13430
+
 6059.  13430// Try to match a semicolon.
+
 6060.  13430
+
 6061.  13205        if (token_nxt.id === ";") {
+
 6062.  13205            advance(";");
+
 6063.  13205        } else {
+
 6064.    225
+
 6065.    225// test_cause:
+
 6066.    225// ["0", "semicolon", "expected_a_b", "(end)", 1]
+
 6067.    225
+
 6068.    225            warn_at(
+
 6069.    225                "expected_a_b",
+
 6070.    225                token_now.line,
+
 6071.    225                token_now.thru + 1,
+
 6072.    225                ";",
+
 6073.    225                artifact()
+
 6074.    225            );
+
 6075.    225        }
+
 6076.  13430        anon = "anonymous";
+
 6077.  13430    }
+
 6078.    631
+
 6079.  14513    function stmt(id, fud_stmt) {
+
 6080.  14513
+
 6081.  14513// Create a statement.
+
 6082.  14513
+
 6083.  14513        const the_symbol = symbol(id);
+
 6084.  14513        the_symbol.fud_stmt = fud_stmt;
+
 6085.  14513        return the_symbol;
+
 6086.  14513    }
+
 6087.    631
+
 6088.   1023    function stmt_break() {
+
 6089.   1023        const the_break = token_now;
+
 6090.   1023        let the_label;
+
 6091.   1023        if (
+
 6092.    719            (functionage.loop < 1 && functionage.switch < 1)
+
 6093.   1017            || functionage.finally > 0
+
 6094.      6        ) {
+
 6095.      6
+
 6096.      6// test_cause:
+
 6097.      6// ["break", "stmt_break", "unexpected_a", "break", 1]
+
 6098.      6
+
 6099.      6            warn("unexpected_a", the_break);
+
 6100.      6        }
+
 6101.   1023        the_break.disrupt = true;
+
 6102.      5        if (token_nxt.identifier && token_now.line === token_nxt.line) {
+
 6103.      5            the_label = functionage.context[token_nxt.id];
+
 6104.      5            if (
+
 6105.      5                the_label === undefined
+
 6106.      5                || the_label.role !== "label"
+
 6107.      5                || the_label.dead
+
 6108.      5            ) {
+
 6109.      5                if (the_label !== undefined && the_label.dead) {
+
 6110.      5
+
 6111.      5// test_cause:
+
 6112.      5// ["aa:{function aa(aa){break aa;}}", "stmt_break", "out_of_scope_a", "aa", 27]
+
 6113.      5
+
 6114.      5                    warn("out_of_scope_a");
+
 6115.      5                } else {
+
 6116.      5
+
 6117.      5// test_cause:
+
 6118.      5// ["aa:{break aa;}", "stmt_break", "not_label_a", "aa", 11]
+
 6119.      5
+
 6120.      5                    warn("not_label_a");
+
 6121.      5                }
+
 6122.      5            } else {
+
 6123.      5                the_label.used += 1;
+
 6124.      5            }
+
 6125.      5            the_break.label = token_nxt;
+
 6126.      5            advance();
+
 6127.      5        }
+
 6128.   1023        advance(";");
+
 6129.   1023        return the_break;
+
 6130.   1023    }
+
 6131.    631
+
 6132.      2    function stmt_continue() {
+
 6133.      2        const the_continue = token_now;
+
 6134.      1        if (functionage.loop < 1 || functionage.finally > 0) {
+
 6135.      2
+
 6136.      2// test_cause:
+
 6137.      2// ["continue", "stmt_continue", "unexpected_a", "continue", 1]
+
 6138.      2// ["
+
 6139.      2// function aa(){while(0){try{}finally{continue}}}
+
 6140.      2// ", "stmt_continue", "unexpected_a", "continue", 37]
+
 6141.      2
+
 6142.      2            warn("unexpected_a", the_continue);
+
 6143.      2        }
+
 6144.      2        check_not_top_level(the_continue);
+
 6145.      2        the_continue.disrupt = true;
+
 6146.      2        warn("unexpected_a", the_continue);
+
 6147.      2        advance(";");
+
 6148.      2        return the_continue;
+
 6149.      2    }
+
 6150.    631
+
 6151.      3    function stmt_debugger() {
+
 6152.      3        const the_debug = token_now;
+
 6153.      1        if (!option_dict.devel) {
+
 6154.      1
+
 6155.      1// test_cause:
+
 6156.      1// ["debugger", "stmt_debugger", "unexpected_a", "debugger", 1]
+
 6157.      1
+
 6158.      1            warn("unexpected_a", the_debug);
+
 6159.      1        }
+
 6160.      3        semicolon();
+
 6161.      3        return the_debug;
+
 6162.      3    }
+
 6163.    631
+
 6164.     72    function stmt_delete() {
+
 6165.     72        const the_token = token_now;
+
 6166.     72        const the_value = parse_expression(0);
+
 6167.     72        if (
+
 6168.      1            (the_value.id !== "." && the_value.id !== "[")
+
 6169.     71            || the_value.arity !== "binary"
+
 6170.      1        ) {
+
 6171.      1
+
 6172.      1// test_cause:
+
 6173.      1// ["delete 0", "stmt_delete", "expected_a_b", "0", 8]
+
 6174.      1
+
 6175.      1            stop("expected_a_b", the_value, ".", artifact(the_value));
+
 6176.     71        }
+
 6177.     71        the_token.expression = the_value;
+
 6178.     71        semicolon();
+
 6179.     71        return the_token;
+
 6180.     71    }
+
 6181.    631
+
 6182.      5    function stmt_do() {
+
 6183.      5        const the_do = token_now;
+
 6184.      5        check_not_top_level(the_do);
+
 6185.      5        functionage.loop += 1;
+
 6186.      5        the_do.block = block();
+
 6187.      5        advance("while");
+
 6188.      5        the_do.expression = condition();
+
 6189.      5        semicolon();
+
 6190.      1        if (the_do.block.disrupt === true) {
+
 6191.      1
+
 6192.      1// test_cause:
+
 6193.      1// ["function aa(){do{break;}while(0)}", "stmt_do", "weird_loop", "do", 15]
+
 6194.      1
+
 6195.      1            warn("weird_loop", the_do);
+
 6196.      3        }
+
 6197.      3        functionage.loop -= 1;
+
 6198.      3        return the_do;
+
 6199.      3    }
+
 6200.    631
+
 6201.     24    function stmt_export() {
+
 6202.     24        let export_list = [];
+
 6203.     24        let the_export = token_now;
+
 6204.     24        let the_id;
+
 6205.     24        let the_name;
+
 6206.     24        let the_thing;
+
 6207.     24
+
 6208.     24        the_export.expression = [];
+
 6209.     11        if (token_nxt.id === "default") {
+
 6210.     11            if (export_dict.default !== undefined) {
+
 6211.     11
+
 6212.     11// test_cause:
+
 6213.     11// ["
+
 6214.     11// export default 0;export default 0
+
 6215.     11// ", "stmt_export", "duplicate_a", "default", 25]
+
 6216.     11
+
 6217.     11                warn("duplicate_a");
+
 6218.     11            }
+
 6219.     11            advance("default");
+
 6220.     11            the_thing = parse_expression(0);
+
 6221.     11            if (
+
 6222.     11                the_thing.id !== "("
+
 6223.     11                || the_thing.expression[0].id !== "."
+
 6224.     11                || the_thing.expression[0].expression.id !== "Object"
+
 6225.     11                || the_thing.expression[0].name.id !== "freeze"
+
 6226.     11            ) {
+
 6227.     11
+
 6228.     11// test_cause:
+
 6229.     11// ["export default {}", "stmt_export", "freeze_exports", "{", 16]
+
 6230.     11
+
 6231.     11                warn("freeze_exports", the_thing);
+
 6232.     11
+
 6233.     11// PR-301 - Bugfix - Fixes issues #282 - optional-semicolon.
+
 6234.     11
+
 6235.     11            } else {
+
 6236.     11
+
 6237.     11// test_cause:
+
 6238.     11// ["
+
 6239.     11// export default Object.freeze({})
+
 6240.     11// ", "semicolon", "expected_a_b", "(end)", 32]
+
 6241.     11
+
 6242.     11                semicolon();
+
 6243.     11            }
+
 6244.     11            export_dict.default = the_thing;
+
 6245.     11            the_export.expression.push(the_thing);
+
 6246.     13        } else {
+
 6247.     13
+
 6248.     13// PR-439 - Add grammar for "export async function ...".
+
 6249.     13
+
 6250.     13            if (token_nxt.id === "function" || token_nxt.id === "async") {
+
 6251.     13
+
 6252.     13// test_cause:
+
 6253.     13// ["export async function aa(){}", "stmt_export", "freeze_exports", "async", 8]
+
 6254.     13// ["export function aa(){}", "stmt_export", "freeze_exports", "function", 8]
+
 6255.     13
+
 6256.     13                warn("freeze_exports");
+
 6257.     13                the_thing = parse_statement();
+
 6258.     13                the_name = the_thing.name;
+
 6259.     13                the_id = the_name.id;
+
 6260.     13                the_name.used += 1;
+
 6261.     13                if (export_dict[the_id] !== undefined) {
+
 6262.     13
+
 6263.     13// test_cause:
+
 6264.     13// ["
+
 6265.     13// let aa;export{aa};export function aa(){}
+
 6266.     13// ", "stmt_export", "duplicate_a", "aa", 35]
+
 6267.     13
+
 6268.     13                    warn("duplicate_a", the_name);
+
 6269.     13                }
+
 6270.     13                export_dict[the_id] = the_thing;
+
 6271.     13                the_export.expression.push(the_thing);
+
 6272.     13                the_thing.statement = false;
+
 6273.     13                the_thing.arity = "unary";
+
 6274.     13            } else if (
+
 6275.     13                token_nxt.id === "var"
+
 6276.     13                || token_nxt.id === "let"
+
 6277.     13                || token_nxt.id === "const"
+
 6278.     13            ) {
+
 6279.     13
+
 6280.     13// test_cause:
+
 6281.     13// ["export const", "stmt_export", "unexpected_a", "const", 8]
+
 6282.     13// ["export let", "stmt_export", "unexpected_a", "let", 8]
+
 6283.     13// ["export var", "stmt_export", "unexpected_a", "var", 8]
+
 6284.     13
+
 6285.     13                warn("unexpected_a");
+
 6286.     13                parse_statement();
+
 6287.     13            } else if (token_nxt.id === "{") {
+
 6288.     13
+
 6289.     13// test_cause:
+
 6290.     13// ["export {}", "stmt_export", "advance{", "", 0]
+
 6291.     13
+
 6292.     13                test_cause("advance{");
+
 6293.     13                advance("{");
+
 6294.     13                while (true) {
+
 6295.     13                    if (!token_nxt.identifier) {
+
 6296.     13
+
 6297.     13// test_cause:
+
 6298.     13// ["export {}", "stmt_export", "expected_identifier_a", "}", 9]
+
 6299.     13
+
 6300.     13                        stop("expected_identifier_a");
+
 6301.     13                    }
+
 6302.     13                    the_id = token_nxt.id;
+
 6303.     13                    export_list.push(token_nxt);
+
 6304.     13                    the_name = token_global.context[the_id];
+
 6305.     13                    if (the_name === undefined) {
+
 6306.     13
+
 6307.     13// test_cause:
+
 6308.     13// ["export {aa}", "stmt_export", "unexpected_a", "aa", 9]
+
 6309.     13
+
 6310.     13                        warn("unexpected_a");
+
 6311.     13                    } else {
+
 6312.     13                        the_name.used += 1;
+
 6313.     13                        if (export_dict[the_id] !== undefined) {
+
 6314.     13
+
 6315.     13// test_cause:
+
 6316.     13// ["let aa;export{aa,aa}", "stmt_export", "duplicate_a", "aa", 18]
+
 6317.     13
+
 6318.     13                            warn("duplicate_a");
+
 6319.     13                        }
+
 6320.     13                        export_dict[the_id] = the_name;
+
 6321.     13                    }
+
 6322.     13                    advance();
+
 6323.     13                    the_export.expression.push(the_thing);
+
 6324.     13                    if (token_nxt.id === ",") {
+
 6325.     13                        advance(",");
+
 6326.     13                    } else {
+
 6327.     13                        break;
+
 6328.     13                    }
+
 6329.     13                }
+
 6330.     13
+
 6331.     13// PR-439 - Check exported properties are ordered.
+
 6332.     13
+
 6333.     13// test_cause:
+
 6334.     13// ["export {bb, aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 13]
+
 6335.     13
+
 6336.     13                check_ordered("export", export_list);
+
 6337.     13                advance("}");
+
 6338.     13                semicolon();
+
 6339.     13            } else {
+
 6340.     13
+
 6341.     13// test_cause:
+
 6342.     13// ["export", "stmt_export", "unexpected_a", "(end)", 1]
+
 6343.     13
+
 6344.     13                stop("unexpected_a");
+
 6345.     13            }
+
 6346.     18        }
+
 6347.     18        state.mode_module = true;
+
 6348.     18        return the_export;
+
 6349.     18    }
+
 6350.    631
+
 6351.     12    function stmt_for() {
+
 6352.     12        let first;
+
 6353.     12        let the_for = token_now;
+
 6354.      7        if (!option_dict.for) {
+
 6355.      7
+
 6356.      7// test_cause:
+
 6357.      7// ["for", "stmt_for", "unexpected_a", "for", 1]
+
 6358.      7
+
 6359.      7            warn("unexpected_a", the_for);
+
 6360.      7        }
+
 6361.     12        check_not_top_level(the_for);
+
 6362.     12        functionage.loop += 1;
+
 6363.     12        advance("(");
+
 6364.     12        token_now.free = true;
+
 6365.      1        if (token_nxt.id === ";") {
+
 6366.      1
+
 6367.      1// test_cause:
+
 6368.      1// ["for(;;){}", "stmt_for", "expected_a_b", "for (;", 1]
+
 6369.      1
+
 6370.      1            return stop("expected_a_b", the_for, "while (", "for (;");
+
 6371.      9        }
+
 6372.      9        switch (token_nxt.id) {
+
 6373.      9        case "const":
+
 6374.      1        case "let":
+
 6375.      1        case "var":
+
 6376.      1
+
 6377.      1// test_cause:
+
 6378.      1// ["for(const aa in aa){}", "stmt_for", "unexpected_a", "const", 5]
+
 6379.      1
+
 6380.      1            return stop("unexpected_a");
+
 6381.      8        }
+
 6382.      8        first = parse_expression(0);
+
 6383.      8        if (first.id === "in") {
+
 6384.      2            if (first.expression[0].arity !== "variable") {
+
 6385.      2
+
 6386.      2// test_cause:
+
 6387.      2// ["for(0 in aa){}", "stmt_for", "bad_assignment_a", "0", 5]
+
 6388.      2
+
 6389.      2                warn("bad_assignment_a", first.expression[0]);
+
 6390.      2            }
+
 6391.      2            the_for.name = first.expression[0];
+
 6392.      2            the_for.expression = first.expression[1];
+
 6393.      2            warn("expected_a_b", the_for, "Object.keys", "for in");
+
 6394.      6        } else {
+
 6395.      6            the_for.initial = first;
+
 6396.      6            advance(";");
+
 6397.      6            the_for.expression = parse_expression(0);
+
 6398.      6            advance(";");
+
 6399.      6            the_for.inc = parse_expression(0);
+
 6400.      6            if (the_for.inc.id === "++") {
+
 6401.      6
+
 6402.      6// test_cause:
+
 6403.      6// ["for(aa;aa;aa++){}", "stmt_for", "expected_a_b", "++", 13]
+
 6404.      6
+
 6405.      6                warn("expected_a_b", the_for.inc, "+= 1", "++");
+
 6406.      6            }
+
 6407.      8        }
+
 6408.      8        advance(")");
+
 6409.      8        the_for.block = block();
+
 6410.      8        if (the_for.block.disrupt === true) {
+
 6411.      1
+
 6412.      1// test_cause:
+
 6413.      1// ["
+
 6414.      1// /*jslint for*/
+
 6415.      1// function aa(bb,cc){for(0;0;0){break;}}
+
 6416.      1// ", "stmt_for", "weird_loop", "for", 20]
+
 6417.      1
+
 6418.      1            warn("weird_loop", the_for);
+
 6419.      8        }
+
 6420.      8        functionage.loop -= 1;
+
 6421.      8        return the_for;
+
 6422.      8    }
+
 6423.    631
+
 6424.   3051    function stmt_if() {
+
 6425.   3051        const the_if = token_now;
+
 6426.   3051        let the_else;
+
 6427.   3051        the_if.expression = condition();
+
 6428.   3051        the_if.block = block();
+
 6429.    642        if (token_nxt.id === "else") {
+
 6430.    642            advance("else");
+
 6431.    642            the_else = token_now;
+
 6432.    642            the_if.else = (
+
 6433.    642                token_nxt.id === "if"
+
 6434.    642                ? parse_statement()
+
 6435.    642                : block()
+
 6436.    642            );
+
 6437.    642
+
 6438.    642// test_cause:
+
 6439.    642// ["if(0){0}else if(0){0}", "stmt_if", "else", "", 0]
+
 6440.    642// ["if(0){0}else{0}", "stmt_if", "else", "", 0]
+
 6441.    642
+
 6442.    642            test_cause("else");
+
 6443.    642            if (the_if.block.disrupt === true) {
+
 6444.    642                if (the_if.else.disrupt === true) {
+
 6445.    642
+
 6446.    642// test_cause:
+
 6447.    642// ["if(0){break;}else{break;}", "stmt_if", "disrupt", "", 0]
+
 6448.    642
+
 6449.    642                    test_cause("disrupt");
+
 6450.    642                    the_if.disrupt = true;
+
 6451.    642                } else {
+
 6452.    642
+
 6453.    642// test_cause:
+
 6454.    642// ["if(0){break;}else{}", "stmt_if", "unexpected_a", "else", 14]
+
 6455.    642
+
 6456.    642                    warn("unexpected_a", the_else);
+
 6457.    642                }
+
 6458.    642            }
+
 6459.   3050        }
+
 6460.   3050        return the_if;
+
 6461.   3050    }
+
 6462.    631
+
 6463.     62    function stmt_import() {
+
 6464.     62        const the_import = token_now;
+
 6465.     62        let name;
+
 6466.     62        let names;
+
 6467.     62
+
 6468.     62// PR-347 - Disable warning "unexpected_directive_a".
+
 6469.     62//
+
 6470.     62//         if (typeof state.mode_module === "object") {
+
 6471.     62//
+
 6472.     62// // test_cause:
+
 6473.     62// // ["
+
 6474.     62// // /*global aa*/
+
 6475.     62// // import aa from "aa"
+
 6476.     62// // ", "stmt_import", "unexpected_directive_a", "global", 1]
+
 6477.     62//
+
 6478.     62//             warn(
+
 6479.     62//                 "unexpected_directive_a",
+
 6480.     62//                 state.mode_module,
+
 6481.     62//                 state.mode_module.directive
+
 6482.     62//             );
+
 6483.     62//         }
+
 6484.     62
+
 6485.     62        state.mode_module = true;
+
 6486.     62
+
 6487.     62// PR-436 - Add grammar for side-effect import-statement.
+
 6488.     62
+
 6489.      1        if (token_nxt.id === "(string)") {
+
 6490.      1
+
 6491.      1// test_cause:
+
 6492.      1// ["import \"./aa.mjs\";", "stmt_import", "import_side_effect", "", 0]
+
 6493.      1
+
 6494.      1            test_cause("import_side_effect");
+
 6495.      1            warn("expected_a_b", token_nxt, "{", artifact());
+
 6496.      1            advance();
+
 6497.      1            semicolon();
+
 6498.      1            return the_import;
+
 6499.     61        }
+
 6500.     61        if (token_nxt.identifier) {
+
 6501.     57            name = token_nxt;
+
 6502.     57            advance();
+
 6503.     57            if (name.id === "ignore") {
+
 6504.     57
+
 6505.     57// test_cause:
+
 6506.     57// ["import ignore from \"aa\"", "stmt_import", "unexpected_a", "ignore", 8]
+
 6507.     57
+
 6508.     57                warn("unexpected_a", name);
+
 6509.     57            }
+
 6510.     57            enroll(name, "variable", true);
+
 6511.     57            the_import.name = name;
+
 6512.     57        } else {
+
 6513.      4            names = [];
+
 6514.      4            advance("{");
+
 6515.      4            if (token_nxt.id !== "}") {
+
 6516.      4                while (true) {
+
 6517.      4                    if (!token_nxt.identifier) {
+
 6518.      4
+
 6519.      4// test_cause:
+
 6520.      4// ["import {", "stmt_import", "expected_identifier_a", "(end)", 1]
+
 6521.      4
+
 6522.      4                        stop("expected_identifier_a");
+
 6523.      4                    }
+
 6524.      4                    name = token_nxt;
+
 6525.      4                    advance();
+
 6526.      4                    if (name.id === "ignore") {
+
 6527.      4
+
 6528.      4// test_cause:
+
 6529.      4// ["import {ignore} from \"aa\"", "stmt_import", "unexpected_a", "ignore", 9]
+
 6530.      4
+
 6531.      4                        warn("unexpected_a", name);
+
 6532.      4                    }
+
 6533.      4                    enroll(name, "variable", true);
+
 6534.      4                    names.push(name);
+
 6535.      4                    if (token_nxt.id !== ",") {
+
 6536.      4                        break;
+
 6537.      4                    }
+
 6538.      4                    advance(",");
+
 6539.      4                }
+
 6540.      4            }
+
 6541.      4            advance("}");
+
 6542.      4            the_import.name = names;
+
 6543.     60        }
+
 6544.     60        advance("from");
+
 6545.     60        advance("(string)");
+
 6546.     60        the_import.import = token_now;
+
 6547.     60        if (!jslint_rgx_module.test(token_now.value)) {
+
 6548.      1
+
 6549.      1// test_cause:
+
 6550.      1// ["import aa from \"!aa\"", "stmt_import", "bad_module_name_a", "!aa", 16]
+
 6551.      1
+
 6552.      1            warn("bad_module_name_a", token_now);
+
 6553.     60        }
+
 6554.     60        import_list.push(token_now.value);
+
 6555.     60        semicolon();
+
 6556.     60        return the_import;
+
 6557.     60    }
+
 6558.    631
+
 6559.      5    function stmt_lbrace() {
+
 6560.      5
+
 6561.      5// test_cause:
+
 6562.      5// [";{}", "stmt_lbrace", "naked_block", "{", 2]
+
 6563.      5// ["class aa{}", "stmt_lbrace", "naked_block", "{", 9]
+
 6564.      5
+
 6565.      5        warn("naked_block", token_now);
+
 6566.      5        return block("naked");
+
 6567.      5    }
+
 6568.    631
+
 6569.   1761    function stmt_return() {
+
 6570.   1761        const the_return = token_now;
+
 6571.   1761        check_not_top_level(the_return);
+
 6572.      1        if (functionage.finally > 0) {
+
 6573.      1
+
 6574.      1// test_cause:
+
 6575.      1// ["
+
 6576.      1// function aa(){try{}finally{return;}}
+
 6577.      1// ", "stmt_return", "unexpected_a", "return", 28]
+
 6578.      1
+
 6579.      1            warn("unexpected_a", the_return);
+
 6580.      1        }
+
 6581.   1761        the_return.disrupt = true;
+
 6582.   1509        if (token_nxt.id !== ";" && the_return.line === token_nxt.line) {
+
 6583.   1509            the_return.expression = parse_expression(10);
+
 6584.   1509        }
+
 6585.   1761        advance(";");
+
 6586.   1761        return the_return;
+
 6587.   1761    }
+
 6588.    631
+
 6589.      5    function stmt_semicolon() {
+
 6590.      5
+
 6591.      5// test_cause:
+
 6592.      5// [";", "stmt_semicolon", "unexpected_a", ";", 1]
+
 6593.      5
+
 6594.      5        warn("unexpected_a", token_now);
+
 6595.      5        return token_now;
+
 6596.      5    }
+
 6597.    631
+
 6598.    220    function stmt_switch() {
+
 6599.    220        const the_cases = [];
+
 6600.    220        const the_switch = token_now;
+
 6601.    220        let dups = [];
+
 6602.    220        let exp;
+
 6603.    220        let last;
+
 6604.    220        let stmts;
+
 6605.    220        let the_case;
+
 6606.    220        let the_default;
+
 6607.    220        let the_disrupt = true;
+
 6608.    220        let the_last;
+
 6609.  23088        function is_dup(thing) {
+
 6610.  23088            return is_equal(thing, exp);
+
 6611.  23088        }
+
 6612.    220        check_not_top_level(the_switch);
+
 6613.      1        if (functionage.finally > 0) {
+
 6614.      1
+
 6615.      1// test_cause:
+
 6616.      1// ["
+
 6617.      1// function aa(){try{}finally{switch(0){}}}
+
 6618.      1// ", "stmt_switch", "unexpected_a", "switch", 28]
+
 6619.      1
+
 6620.      1            warn("unexpected_a", the_switch);
+
 6621.      1        }
+
 6622.    220        functionage.switch += 1;
+
 6623.    220        advance("(");
+
 6624.    220        token_now.free = true;
+
 6625.    220        the_switch.expression = parse_expression(0);
+
 6626.    220        the_switch.block = the_cases;
+
 6627.    220        advance(")");
+
 6628.    220        advance("{");
+
 6629.   1085        while (true) {
+
 6630.   1085
+
 6631.   1085// Loop through cases with breaks.
+
 6632.   1085
+
 6633.   1085            the_case = token_nxt;
+
 6634.   1085            the_case.arity = "statement";
+
 6635.   1085            the_case.expression = [];
+
 6636.   1612            while (true) {
+
 6637.   1612
+
 6638.   1612// Loop through fallthrough cases.
+
 6639.   1612
+
 6640.   1612                advance("case");
+
 6641.   1612                token_now.switch = true;
+
 6642.   1612                exp = parse_expression(0);
+
 6643.   1612                if (dups.some(is_dup)) {
+
 6644.   1612
+
 6645.   1612// test_cause:
+
 6646.   1612// ["
+
 6647.   1612// switch(0){case 0:break;case 0:break}
+
 6648.   1612// ", "stmt_switch", "unexpected_a", "0", 29]
+
 6649.   1612
+
 6650.   1612                    warn("unexpected_a", exp);
+
 6651.   1612                }
+
 6652.   1612                dups.push(exp);
+
 6653.   1612                the_case.expression.push(exp);
+
 6654.   1612                advance(":");
+
 6655.   1612                if (token_nxt.id !== "case") {
+
 6656.   1612                    break;
+
 6657.   1612                }
+
 6658.   1612            }
+
 6659.   1085
+
 6660.   1085// test_cause:
+
 6661.   1085// ["
+
 6662.   1085// switch(0){case 1:case 0:break;}
+
 6663.   1085// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 23]
+
 6664.   1085// ["
+
 6665.   1085// switch(0){case "aa":case 0:break;}
+
 6666.   1085// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 26]
+
 6667.   1085// ["
+
 6668.   1085// switch(0){case "bb":case "aa":break;}
+
 6669.   1085// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 26]
+
 6670.   1085// ["
+
 6671.   1085// switch(0){case aa:case "aa":break;}
+
 6672.   1085// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24]
+
 6673.   1085// ["
+
 6674.   1085// switch(0){case bb:case aa:break;}
+
 6675.   1085// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24]
+
 6676.   1085
+
 6677.   1085            check_ordered_case(the_case.expression);
+
 6678.   1085            stmts = parse_statements();
+
 6679.   1085            if (stmts.length < 1) {
+
 6680.   1085
+
 6681.   1085// test_cause:
+
 6682.   1085// ["
+
 6683.   1085// switch(0){case 0:default:}
+
 6684.   1085// ", "stmt_switch", "expected_statements_a", "default", 18]
+
 6685.   1085// ["switch(0){case 0:}", "stmt_switch", "expected_statements_a", "}", 18]
+
 6686.   1085
+
 6687.   1085                warn("expected_statements_a");
+
 6688.   1085
+
 6689.   1085// PR-359 - Bugfix - Fixes issue #358 - switch-statement crashes jslint.
+
 6690.   1085
+
 6691.   1085                break;
+
 6692.   1085            }
+
 6693.   1085            the_case.block = stmts;
+
 6694.   1085            the_cases.push(the_case);
+
 6695.   1085            last = stmts[stmts.length - 1];
+
 6696.   1085            if (last.disrupt) {
+
 6697.   1085                if (last.id === "break" && last.label === undefined) {
+
 6698.   1085                    the_disrupt = false;
+
 6699.   1085                }
+
 6700.   1085            } else {
+
 6701.   1085                warn("expected_a_before_b", token_nxt, "break;", artifact());
+
 6702.   1085            }
+
 6703.   1085            if (token_nxt.id !== "case") {
+
 6704.   1085                break;
+
 6705.   1085            }
+
 6706.   1085        }
+
 6707.    217
+
 6708.    217// test_cause:
+
 6709.    217// ["
+
 6710.    217// switch(0){case 1:break;case 0:break;}
+
 6711.    217// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 29]
+
 6712.    217// ["
+
 6713.    217// switch(0){case "aa":break;case 0:break;}
+
 6714.    217// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 32]
+
 6715.    217// ["
+
 6716.    217// switch(0){case "bb":break;case "aa":break;}
+
 6717.    217// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 32]
+
 6718.    217// ["
+
 6719.    217// switch(0){case aa:break;case "aa":break;}
+
 6720.    217// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30]
+
 6721.    217// ["
+
 6722.    217// switch(0){case bb:break;case aa:break;}
+
 6723.    217// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30]
+
 6724.    217
+
 6725.   1080        check_ordered_case(the_cases.map(function ({
+
 6726.   1080            expression
+
 6727.   1080        }) {
+
 6728.   1080            return expression[0];
+
 6729.   1080        }));
+
 6730.    217        dups = undefined;
+
 6731.    217        if (token_nxt.id === "default") {
+
 6732.     99            the_default = token_nxt;
+
 6733.     99            advance("default");
+
 6734.     99            token_now.switch = true;
+
 6735.     99            advance(":");
+
 6736.     99            the_switch.else = parse_statements();
+
 6737.     99            if (the_switch.else.length < 1) {
+
 6738.     99
+
 6739.     99// test_cause:
+
 6740.     99// ["
+
 6741.     99// switch(0){case 0:break;default:}
+
 6742.     99// ", "stmt_switch", "unexpected_a", "default", 24]
+
 6743.     99
+
 6744.     99                warn("unexpected_a", the_default);
+
 6745.     99                the_disrupt = false;
+
 6746.     99            } else {
+
 6747.     99                the_last = the_switch.else[
+
 6748.     99                    the_switch.else.length - 1
+
 6749.     99                ];
+
 6750.     99                if (
+
 6751.     99                    the_last.id === "break"
+
 6752.     99                    && the_last.label === undefined
+
 6753.     99                ) {
+
 6754.     99
+
 6755.     99// test_cause:
+
 6756.     99// ["
+
 6757.     99// switch(0){case 0:break;default:break;}
+
 6758.     99// ", "stmt_switch", "unexpected_a", "break", 32]
+
 6759.     99
+
 6760.     99                    warn("unexpected_a", the_last);
+
 6761.     99                    the_last.disrupt = false;
+
 6762.     99                }
+
 6763.     99                the_disrupt = the_disrupt && the_last.disrupt;
+
 6764.     99            }
+
 6765.    118        } else {
+
 6766.    118            the_disrupt = false;
+
 6767.    217        }
+
 6768.    217        advance("}", the_switch);
+
 6769.    217        functionage.switch -= 1;
+
 6770.    217        the_switch.disrupt = the_disrupt;
+
 6771.    217        return the_switch;
+
 6772.    217    }
+
 6773.    631
+
 6774.     40    function stmt_throw() {
+
 6775.     40        const the_throw = token_now;
+
 6776.     40        the_throw.disrupt = true;
+
 6777.     40        the_throw.expression = parse_expression(10);
+
 6778.     40        semicolon();
+
 6779.      1        if (functionage.try > 0) {
+
 6780.      1
+
 6781.      1// test_cause:
+
 6782.      1// ["try{throw 0}catch(){}", "stmt_throw", "unexpected_a", "throw", 5]
+
 6783.      1
+
 6784.      1            warn("unexpected_a", the_throw);
+
 6785.      1        }
+
 6786.     40        return the_throw;
+
 6787.     40    }
+
 6788.    631
+
 6789.     62    function stmt_try() {
+
 6790.     62        const the_try = token_now;
+
 6791.     62        let ignored;
+
 6792.     62        let the_catch;
+
 6793.     62        let the_disrupt;
+
 6794.      1        if (functionage.try > 0) {
+
 6795.      1
+
 6796.      1// test_cause:
+
 6797.      1// ["try{try{}catch(){}}catch(){}", "stmt_try", "unexpected_a", "try", 5]
+
 6798.      1
+
 6799.      1            warn("unexpected_a", the_try);
+
 6800.      1        }
+
 6801.     62        functionage.try += 1;
+
 6802.     62        the_try.block = block();
+
 6803.     62        the_disrupt = the_try.block.disrupt;
+
 6804.     57        if (token_nxt.id === "catch") {
+
 6805.     57            ignored = "ignore";
+
 6806.     57            the_catch = token_nxt;
+
 6807.     57            the_try.catch = the_catch;
+
 6808.     57            advance("catch");
+
 6809.     57
+
 6810.     57// Create new catch-scope for catch-parameter.
+
 6811.     57
+
 6812.     57            catch_stack.push(catchage);
+
 6813.     57            catchage = the_catch;
+
 6814.     57            catch_list.push(catchage);
+
 6815.     57            the_catch.context = empty();
+
 6816.     57            if (token_nxt.id === "(") {
+
 6817.     57                advance("(");
+
 6818.     57                if (!token_nxt.identifier) {
+
 6819.     57
+
 6820.     57// test_cause:
+
 6821.     57// ["try{}catch(){}", "stmt_try", "expected_identifier_a", ")", 12]
+
 6822.     57
+
 6823.     57                    return stop("expected_identifier_a");
+
 6824.     57                }
+
 6825.     57                if (token_nxt.id !== "ignore") {
+
 6826.     57                    ignored = undefined;
+
 6827.     57                    the_catch.name = token_nxt;
+
 6828.     57                    enroll(token_nxt, "exception", true);
+
 6829.     57                }
+
 6830.     57                advance();
+
 6831.     57                advance(")");
+
 6832.     57            }
+
 6833.     57            the_catch.block = block(ignored);
+
 6834.     57            if (the_catch.block.disrupt !== true) {
+
 6835.     57                the_disrupt = false;
+
 6836.     57            }
+
 6837.     57
+
 6838.     57// Restore previous catch-scope after catch-block.
+
 6839.     57
+
 6840.     57            catchage = catch_stack.pop();
+
 6841.     57
+
 6842.     57// PR-404 - Relax warning about missing `catch` in `try...finally` statement.
+
 6843.     57//
+
 6844.     57//         } else {
+
 6845.     57//
+
 6846.     57// // test_cause:
+
 6847.     57// // ["try{}finally{break;}", "stmt_try", "expected_a_before_b", "finally", 6]
+
 6848.     57//
+
 6849.     57//             warn("expected_a_before_b", token_nxt, "catch", artifact());
+
 6850.     57
+
 6851.     58        }
+
 6852.     58        if (token_nxt.id === "finally") {
+
 6853.      4            functionage.finally += 1;
+
 6854.      4            advance("finally");
+
 6855.      4            the_try.else = block();
+
 6856.      4            the_disrupt = the_try.else.disrupt;
+
 6857.      4            functionage.finally -= 1;
+
 6858.     56        }
+
 6859.     56        the_try.disrupt = the_disrupt;
+
 6860.     56        functionage.try -= 1;
+
 6861.     56        return the_try;
+
 6862.     56    }
+
 6863.    631
+
 6864.   2334    function stmt_var() {
+
 6865.   2334        let ellipsis;
+
 6866.   2334        let mode_const;
+
 6867.   2334        let name;
+
 6868.   2334        let the_brace;
+
 6869.   2334        let the_bracket;
+
 6870.   2334        let the_variable = token_now;
+
 6871.   2334        let variable_prv;
+
 6872.   2334        mode_const = the_variable.id === "const";
+
 6873.   2334        the_variable.names = [];
+
 6874.   2334
+
 6875.   2334// A program may use var or let, but not both.
+
 6876.   2334
+
 6877.   2058        if (!mode_const) {
+
 6878.   2058            if (mode_var === undefined) {
+
 6879.   2058                mode_var = the_variable.id;
+
 6880.   2058            } else if (the_variable.id !== mode_var) {
+
 6881.   2058
+
 6882.   2058// test_cause:
+
 6883.   2058// ["let aa;var aa", "stmt_var", "expected_a_b", "var", 8]
+
 6884.   2058
+
 6885.   2058                warn("expected_a_b", the_variable, mode_var, the_variable.id);
+
 6886.   2058            }
+
 6887.   2058        }
+
 6888.   2334
+
 6889.   2334// We don't expect to see variables created in switch statements.
+
 6890.   2334
+
 6891.      1        if (functionage.switch > 0) {
+
 6892.      1
+
 6893.      1// test_cause:
+
 6894.      1// ["switch(0){case 0:var aa}", "stmt_var", "var_switch", "var", 18]
+
 6895.      1
+
 6896.      1            warn("var_switch", the_variable);
+
 6897.      1        }
+
 6898.   2334        switch (
+
 6899.   2334            Boolean(functionage.statement_prv)
+
 6900.   1449            && functionage.statement_prv.id
+
 6901.   2334        ) {
+
 6902.    107        case "const":
+
 6903.   1437        case "let":
+
 6904.   1439        case "var":
+
 6905.   1439
+
 6906.   1439// test_cause:
+
 6907.   1439// ["const aa=0;const bb=0;", "stmt_var", "var_prv", "const", 0]
+
 6908.   1439// ["let aa=0;let bb=0;", "stmt_var", "var_prv", "let", 0]
+
 6909.   1439// ["var aa=0;var bb=0;", "stmt_var", "var_prv", "var", 0]
+
 6910.   1439
+
 6911.   1439            test_cause("var_prv", functionage.statement_prv.id);
+
 6912.   1439            variable_prv = functionage.statement_prv;
+
 6913.   1439            break;
+
 6914.      3        case "import":
+
 6915.      3
+
 6916.      3// test_cause:
+
 6917.      3// ["import aa from \"aa\";\nlet bb=0;", "stmt_var", "import_prv", "", 0]
+
 6918.      3
+
 6919.      3            test_cause("import_prv");
+
 6920.      3            break;
+
 6921.    885        case false:
+
 6922.    885            break;
+
 6923.      7        default:
+
 6924.      7            if (
+
 6925.      7                (option_dict.beta && !option_dict.variable)
+
 6926.      7                || the_variable.id === "var"
+
 6927.      7            ) {
+
 6928.      7
+
 6929.      7// test_cause:
+
 6930.      7// ["console.log();let aa=0;", "stmt_var", "var_on_top", "let", 15]
+
 6931.      7// ["console.log();var aa=0;", "stmt_var", "var_on_top", "var", 15]
+
 6932.      7// ["try{aa();}catch(aa){var aa=0;}", "stmt_var", "var_on_top", "var", 21]
+
 6933.      7// ["while(0){var aa;}", "stmt_var", "var_on_top", "var", 10]
+
 6934.      7
+
 6935.      7                warn("var_on_top", token_now);
+
 6936.      7            }
+
 6937.   2334        }
+
 6938.   2335        while (true) {
+
 6939.   2335            if (token_nxt.id === "{") {
+
 6940.   2335                if (the_variable.id === "var") {
+
 6941.   2335
+
 6942.   2335// test_cause:
+
 6943.   2335// ["var{aa}=0", "stmt_var", "unexpected_a", "var", 1]
+
 6944.   2335
+
 6945.   2335                    warn("unexpected_a", the_variable);
+
 6946.   2335                }
+
 6947.   2335                the_brace = token_nxt;
+
 6948.   2335                advance("{");
+
 6949.   2335                while (true) {
+
 6950.   2335                    name = token_nxt;
+
 6951.   2335                    if (!name.identifier) {
+
 6952.   2335
+
 6953.   2335// test_cause:
+
 6954.   2335// ["let {0}", "stmt_var", "expected_identifier_a", "0", 6]
+
 6955.   2335
+
 6956.   2335                        return stop("expected_identifier_a");
+
 6957.   2335                    }
+
 6958.   2335                    survey(name);
+
 6959.   2335                    advance();
+
 6960.   2335                    if (token_nxt.id === ":") {
+
 6961.   2335                        advance(":");
+
 6962.   2335                        if (!token_nxt.identifier) {
+
 6963.   2335
+
 6964.   2335// test_cause:
+
 6965.   2335// ["let {aa:0}", "stmt_var", "expected_identifier_a", "0", 9]
+
 6966.   2335// ["let {aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 9]
+
 6967.   2335
+
 6968.   2335                            return stop("expected_identifier_a");
+
 6969.   2335                        }
+
 6970.   2335
+
 6971.   2335// PR-363 - Bugfix
+
 6972.   2335// Add test against false-warning <uninitialized 'bb'> in code
+
 6973.   2335// '/*jslint node*/\nlet {aa:bb} = {}; bb();'.
+
 6974.   2335//
+
 6975.   2335//                         token_nxt.label = name;
+
 6976.   2335//                         the_variable.names.push(token_nxt);
+
 6977.   2335//                         enroll(token_nxt, "variable", mode_const);
+
 6978.   2335
+
 6979.   2335                        name = token_nxt;
+
 6980.   2335                        the_variable.names.push(name);
+
 6981.   2335                        survey(name);
+
 6982.   2335                        enroll(name, "variable", mode_const);
+
 6983.   2335
+
 6984.   2335                        advance();
+
 6985.   2335                        the_brace.open = true;
+
 6986.   2335                    } else {
+
 6987.   2335                        the_variable.names.push(name);
+
 6988.   2335                        enroll(name, "variable", mode_const);
+
 6989.   2335                    }
+
 6990.   2335                    name.dead = false;
+
 6991.   2335                    name.init = true;
+
 6992.   2335                    if (token_nxt.id === "=") {
+
 6993.   2335
+
 6994.   2335// test_cause:
+
 6995.   2335// ["let {aa=0}", "stmt_var", "assign", "", 0]
+
 6996.   2335
+
 6997.   2335                        test_cause("assign");
+
 6998.   2335                        advance("=");
+
 6999.   2335                        name.expression = parse_expression();
+
 7000.   2335                        the_brace.open = true;
+
 7001.   2335                    }
+
 7002.   2335                    if (token_nxt.id !== ",") {
+
 7003.   2335                        break;
+
 7004.   2335                    }
+
 7005.   2335                    advance(",");
+
 7006.   2335                }
+
 7007.   2335
+
 7008.   2335// test_cause:
+
 7009.   2335// ["let{bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8]
+
 7010.   2335
+
 7011.   2335                check_ordered(the_variable.id, the_variable.names);
+
 7012.   2335                advance("}");
+
 7013.   2335                advance("=");
+
 7014.   2335                the_variable.expression = parse_expression(0);
+
 7015.   2335            } else if (token_nxt.id === "[") {
+
 7016.   2335                if (the_variable.id === "var") {
+
 7017.   2335
+
 7018.   2335// test_cause:
+
 7019.   2335// ["var[aa]=0", "stmt_var", "unexpected_a", "var", 1]
+
 7020.   2335
+
 7021.   2335                    warn("unexpected_a", the_variable);
+
 7022.   2335                }
+
 7023.   2335                the_bracket = token_nxt;
+
 7024.   2335                advance("[");
+
 7025.   2335                while (true) {
+
 7026.   2335                    ellipsis = false;
+
 7027.   2335                    if (token_nxt.id === "...") {
+
 7028.   2335                        ellipsis = true;
+
 7029.   2335                        advance("...");
+
 7030.   2335                    }
+
 7031.   2335                    if (!token_nxt.identifier) {
+
 7032.   2335
+
 7033.   2335// test_cause:
+
 7034.   2335// ["let[]", "stmt_var", "expected_identifier_a", "]", 5]
+
 7035.   2335
+
 7036.   2335                        return stop("expected_identifier_a");
+
 7037.   2335                    }
+
 7038.   2335                    name = token_nxt;
+
 7039.   2335                    advance();
+
 7040.   2335                    the_variable.names.push(name);
+
 7041.   2335                    enroll(name, "variable", mode_const);
+
 7042.   2335                    name.dead = false;
+
 7043.   2335                    name.init = true;
+
 7044.   2335                    if (ellipsis) {
+
 7045.   2335                        name.ellipsis = true;
+
 7046.   2335                        break;
+
 7047.   2335                    }
+
 7048.   2335                    if (token_nxt.id === "=") {
+
 7049.   2335                        advance("=");
+
 7050.   2335                        name.expression = parse_expression();
+
 7051.   2335                        the_bracket.open = true;
+
 7052.   2335                    }
+
 7053.   2335                    if (token_nxt.id !== ",") {
+
 7054.   2335                        break;
+
 7055.   2335                    }
+
 7056.   2335                    advance(",");
+
 7057.   2335                }
+
 7058.   2335                advance("]");
+
 7059.   2335                advance("=");
+
 7060.   2335                the_variable.expression = parse_expression(0);
+
 7061.   2335            } else if (token_nxt.identifier) {
+
 7062.   2335                name = token_nxt;
+
 7063.   2335                advance();
+
 7064.   2335                if (name.id === "ignore") {
+
 7065.   2335
+
 7066.   2335// test_cause:
+
 7067.   2335// ["
+
 7068.   2335// let ignore;function aa(ignore) {}
+
 7069.   2335// ", "stmt_var", "unexpected_a", "ignore", 5]
+
 7070.   2335
+
 7071.   2335                    warn("unexpected_a", name);
+
 7072.   2335                }
+
 7073.   2335                enroll(name, "variable", mode_const);
+
 7074.   2335                if (token_nxt.id === "=" || mode_const) {
+
 7075.   2335                    advance("=");
+
 7076.   2335                    name.dead = false;
+
 7077.   2335                    name.init = true;
+
 7078.   2335                    name.expression = parse_expression(0);
+
 7079.   2335                }
+
 7080.   2335                the_variable.names.push(name);
+
 7081.   2335            } else {
+
 7082.   2335
+
 7083.   2335// test_cause:
+
 7084.   2335// ["let 0", "stmt_var", "expected_identifier_a", "0", 5]
+
 7085.   2335// ["var{aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 8]
+
 7086.   2335
+
 7087.   2335                return stop("expected_identifier_a");
+
 7088.   2335            }
+
 7089.   2335            if (token_nxt.id !== ",") {
+
 7090.   2335                break;
+
 7091.   2335            }
+
 7092.   2335
+
 7093.   2335// test_cause:
+
 7094.   2335// ["let aa,bb;", "stmt_var", "expected_a_b", ",", 7]
+
 7095.   2335
+
 7096.   2335            warn("expected_a_b", token_nxt, ";", ",");
+
 7097.   2335            advance(",");
+
 7098.   2335        }
+
 7099.   2320
+
 7100.   2320// Warn if variable declarations are unordered.
+
 7101.   2320
+
 7102.   2320        if (
+
 7103.   2320            option_dict.beta
+
 7104.   2320            && !option_dict.unordered
+
 7105.   2315            && !option_dict.variable
+
 7106.   2309            && variable_prv
+
 7107.   1437            && (
+
 7108.   1437                variable_prv.id + " " + variable_prv.names[0].id
+
 7109.   1437                > the_variable.id + " " + the_variable.names[0].id
+
 7110.   1437            )
+
 7111.      3        ) {
+
 7112.      3
+
 7113.      3// test_cause:
+
 7114.      3// ["const bb=0;const aa=0;", "stmt_var", "expected_a_b_before_c_d", "aa", 12]
+
 7115.      3// ["let bb;let aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8]
+
 7116.      3// ["var bb;var aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8]
+
 7117.      3
+
 7118.      3            warn(
+
 7119.      3                "expected_a_b_before_c_d",
+
 7120.      3                the_variable,
+
 7121.      3                the_variable.id,
+
 7122.      3                the_variable.names[0].id,
+
 7123.      3                variable_prv.id,
+
 7124.      3                variable_prv.names[0].id
+
 7125.      3            );
+
 7126.   2320        }
+
 7127.   2320        semicolon();
+
 7128.   2320        return the_variable;
+
 7129.   2320    }
+
 7130.    631
+
 7131.    208    function stmt_while() {
+
 7132.    208        const the_while = token_now;
+
 7133.    208        check_not_top_level(the_while);
+
 7134.    208        functionage.loop += 1;
+
 7135.    208        the_while.expression = condition();
+
 7136.    208        the_while.block = block();
+
 7137.      1        if (the_while.block.disrupt === true) {
+
 7138.      1
+
 7139.      1// test_cause:
+
 7140.      1// ["function aa(){while(0){break;}}", "stmt_while", "weird_loop", "while", 15]
+
 7141.      1
+
 7142.      1            warn("weird_loop", the_while);
+
 7143.    205        }
+
 7144.    205        functionage.loop -= 1;
+
 7145.    205        return the_while;
+
 7146.    205    }
+
 7147.    631
+
 7148.      1    function stmt_with() {
+
 7149.      1
+
 7150.      1// test_cause:
+
 7151.      1// ["with", "stmt_with", "unexpected_a", "with", 1]
+
 7152.      1
+
 7153.      1        stop("unexpected_a", token_now);
+
 7154.      1    }
+
 7155.    631
+
 7156.  14600    function survey(name) {
+
 7157.  14600        let id = name.id;
+
 7158.  14600
+
 7159.  14600// Tally the property name. If it is a string, only tally strings that conform
+
 7160.  14600// to the identifier rules.
+
 7161.  14600
+
 7162.    183        if (id === "(string)") {
+
 7163.    183            id = name.value;
+
 7164.    183            if (!jslint_rgx_identifier.test(id)) {
+
 7165.    183                return id;
+
 7166.    183            }
+
 7167.  14417        } else if (id === "`") {
+
 7168.  14417            if (name.value.length === 1) {
+
 7169.  14417                id = name.value[0].value;
+
 7170.  14417                if (!jslint_rgx_identifier.test(id)) {
+
 7171.  14417                    return id;
+
 7172.  14417                }
+
 7173.  14417            }
+
 7174.  14417        } else if (!name.identifier) {
+
 7175.  14417
+
 7176.  14417// test_cause:
+
 7177.  14417// ["let aa={0:0}", "survey", "expected_identifier_a", "0", 9]
+
 7178.  14417
+
 7179.  14417            return stop("expected_identifier_a", name);
+
 7180.  14513        }
+
 7181.  14513
+
 7182.  14513// If we have seen this name before, increment its count.
+
 7183.  14513
+
 7184.  14513        if (typeof property_dict[id] === "number") {
+
 7185.  12086            property_dict[id] += 1;
+
 7186.  12086
+
 7187.  12086// If this is the first time seeing this property name, and if there is a
+
 7188.  12086// tenure list, then it must be on the list. Otherwise, it must conform to
+
 7189.  12086// the rules for good property names.
+
 7190.  12086
+
 7191.  12086        } else {
+
 7192.   2427            if (state.mode_property) {
+
 7193.   2427                if (tenure[id] !== true) {
+
 7194.   2427
+
 7195.   2427// test_cause:
+
 7196.   2427// ["/*property aa*/\naa.bb", "survey", "unregistered_property_a", "bb", 4]
+
 7197.   2427
+
 7198.   2427                    warn("unregistered_property_a", name);
+
 7199.   2427                }
+
 7200.   2427            } else if (
+
 7201.   2427                !option_dict.nomen
+
 7202.   2427                && name.identifier
+
 7203.   2427                && jslint_rgx_weird_property.test(id)
+
 7204.   2427            ) {
+
 7205.   2427
+
 7206.   2427// test_cause:
+
 7207.   2427// ["aa.$", "survey", "weird_property_a", "$", 4]
+
 7208.   2427// ["aa._", "survey", "weird_property_a", "_", 4]
+
 7209.   2427// ["aa._aa", "survey", "weird_property_a", "_aa", 4]
+
 7210.   2427// ["aa.aaSync", "survey", "weird_property_a", "aaSync", 4]
+
 7211.   2427// ["aa.aa_", "survey", "weird_property_a", "aa_", 4]
+
 7212.   2427
+
 7213.   2427                warn("weird_property_a", name);
+
 7214.   2427            }
+
 7215.   2427            property_dict[id] = 1;
+
 7216.  14513        }
+
 7217.  14513        return id;
+
 7218.  14513    }
+
 7219.    631
+
 7220.    631// These functions are used to specify the grammar of our language:
+
 7221.    631
+
 7222.  82030    function symbol(id, bp) {
+
 7223.  82030
+
 7224.  82030// Create a symbol if it does not already exist in the language's syntax.
+
 7225.  82030
+
 7226.  82030        let the_symbol = syntax_dict[id];
+
 7227.  71303        if (the_symbol === undefined) {
+
 7228.  71303            the_symbol = empty();
+
 7229.  71303            the_symbol.id = id;
+
 7230.  71303            the_symbol.lbp = bp || 0;
+
 7231.  71303            syntax_dict[id] = the_symbol;
+
 7232.  71303        }
+
 7233.  82030        return the_symbol;
+
 7234.  82030    }
+
 7235.    631
+
 7236.    631    function ternary(id1, id2) {
+
 7237.    631
+
 7238.    631// Create a ternary operator.
+
 7239.    631
+
 7240.    631        const the_symbol = symbol(id1, 30);
+
 7241.    213        the_symbol.led_infix = function parse_ternary_led(left) {
+
 7242.    213            const the_token = token_now;
+
 7243.    213            let second;
+
 7244.    213            second = parse_expression(20);
+
 7245.    213            advance(id2);
+
 7246.    213            token_now.arity = "ternary";
+
 7247.    213            the_token.arity = "ternary";
+
 7248.    213            the_token.expression = [left, second, parse_expression(10)];
+
 7249.      5            if (token_nxt.id !== ")") {
+
 7250.      5
+
 7251.      5// test_cause:
+
 7252.      5// ["0?0:0", "parse_ternary_led", "use_open", "?", 2]
+
 7253.      5
+
 7254.      5                warn("use_open", the_token);
+
 7255.      5            }
+
 7256.    213            return the_token;
+
 7257.    213        };
+
 7258.    631        return the_symbol;
+
 7259.    631    }
+
 7260.    631
+
 7261.    631// Now we parse JavaScript.
+
 7262.    631// Begin defining the language.
+
 7263.    631
+
 7264.    631    assignment("%=");
+
 7265.    631    assignment("&=");
+
 7266.    631    assignment("*=");
+
 7267.    631    assignment("+=");
+
 7268.    631    assignment("-=");
+
 7269.    631    assignment("/=");
+
 7270.    631    assignment("<<=");
+
 7271.    631    assignment("=");
+
 7272.    631    assignment(">>=");
+
 7273.    631    assignment(">>>=");
+
 7274.    631    assignment("^=");
+
 7275.    631    assignment("|=");
+
 7276.    631    constant("(number)", "number");
+
 7277.    631    constant("(regexp)", "regexp");
+
 7278.    631    constant("(string)", "string");
+
 7279.    631    constant("Function", "function", constant_Function);
+
 7280.    631    constant("Infinity", "number", Infinity);
+
 7281.    631    constant("NaN", "number", NaN);
+
 7282.    631    constant("arguments", "object", constant_arguments);
+
 7283.    631    constant("eval", "function", constant_eval);
+
 7284.    631    constant("false", "boolean", false);
+
 7285.    631    constant("ignore", "undefined", constant_ignore);
+
 7286.    631    constant("isFinite", "function", constant_isInfinite);
+
 7287.    631    constant("isNaN", "function", constant_isNaN);
+
 7288.    631    constant("null", "null", null);
+
 7289.    631    constant("this", "object", constant_this);
+
 7290.    631    constant("true", "boolean", true);
+
 7291.    631    constant("undefined", "undefined");
+
 7292.    631    infix(100, "!=");
+
 7293.    631    infix(100, "!==");
+
 7294.    631    infix(100, "==");
+
 7295.    631    infix(100, "===");
+
 7296.    631    infix(110, "<");
+
 7297.    631    infix(110, "<=");
+
 7298.    631    infix(110, ">");
+
 7299.    631    infix(110, ">=");
+
 7300.    631    infix(110, "in");
+
 7301.    631    infix(110, "instanceof");
+
 7302.    631    infix(120, "<<");
+
 7303.    631    infix(120, ">>");
+
 7304.    631    infix(120, ">>>");
+
 7305.    631    infix(130, "+");
+
 7306.    631    infix(130, "-");
+
 7307.    631    infix(140, "%");
+
 7308.    631    infix(140, "*");
+
 7309.    631    infix(140, "/");
+
 7310.    631    infix(160, "(", infix_lparen);
+
 7311.    631    infix(160, "`", infix_grave);
+
 7312.    631    infix(170, ".", infix_dot);
+
 7313.    631    infix(170, "=>", infix_fart_unwrapped);
+
 7314.    631    infix(170, "?.", infix_option_chain);
+
 7315.    631    infix(170, "[", infix_lbracket);
+
 7316.    631    infix(35, "??");
+
 7317.    631    infix(40, "||");
+
 7318.    631    infix(50, "&&");
+
 7319.    631    infix(70, "|");
+
 7320.    631    infix(80, "^");
+
 7321.    631    infix(90, "&");
+
 7322.    631    infixr(150, "**");
+
 7323.    631    postassign("++");
+
 7324.    631    postassign("--");
+
 7325.    631    preassign("++");
+
 7326.    631    preassign("--");
+
 7327.    631    prefix("!!");
+
 7328.    631    prefix("!");
+
 7329.    631    prefix("(", prefix_lparen);
+
 7330.    631    prefix("+");
+
 7331.    631    prefix("-");
+
 7332.    631    prefix("/=", prefix_assign_divide);
+
 7333.    631    prefix("=>", prefix_fart);
+
 7334.    631    prefix("[", prefix_lbracket);
+
 7335.    631    prefix("`", prefix_tick);
+
 7336.    631    prefix("async", prefix_async);
+
 7337.    631    prefix("await", prefix_await);
+
 7338.    631    prefix("function", prefix_function);
+
 7339.    631    prefix("new", prefix_new);
+
 7340.    631    prefix("typeof");
+
 7341.    631    prefix("void", prefix_void);
+
 7342.    631    prefix("{", prefix_lbrace);
+
 7343.    631    prefix("~");
+
 7344.    631    stmt(";", stmt_semicolon);
+
 7345.    631    stmt("async", prefix_async);
+
 7346.    631    stmt("await", prefix_await);
+
 7347.    631    stmt("break", stmt_break);
+
 7348.    631    stmt("const", stmt_var);
+
 7349.    631    stmt("continue", stmt_continue);
+
 7350.    631    stmt("debugger", stmt_debugger);
+
 7351.    631    stmt("delete", stmt_delete);
+
 7352.    631    stmt("do", stmt_do);
+
 7353.    631    stmt("export", stmt_export);
+
 7354.    631    stmt("for", stmt_for);
+
 7355.    631    stmt("function", prefix_function);
+
 7356.    631    stmt("if", stmt_if);
+
 7357.    631    stmt("import", stmt_import);
+
 7358.    631    stmt("let", stmt_var);
+
 7359.    631    stmt("return", stmt_return);
+
 7360.    631    stmt("switch", stmt_switch);
+
 7361.    631    stmt("throw", stmt_throw);
+
 7362.    631    stmt("try", stmt_try);
+
 7363.    631    stmt("var", stmt_var);
+
 7364.    631    stmt("while", stmt_while);
+
 7365.    631    stmt("with", stmt_with);
+
 7366.    631    stmt("{", stmt_lbrace);
+
 7367.    631    symbol(")");
+
 7368.    631    symbol("*/");
+
 7369.    631    symbol(",");
+
 7370.    631    symbol(":");
+
 7371.    631    symbol(";");
+
 7372.    631    symbol("]");
+
 7373.    631    symbol("async");
+
 7374.    631    symbol("await");
+
 7375.    631    symbol("case");
+
 7376.    631    symbol("catch");
+
 7377.    631    symbol("class");
+
 7378.    631    symbol("default");
+
 7379.    631    symbol("else");
+
 7380.    631    symbol("enum");
+
 7381.    631    symbol("finally");
+
 7382.    631    symbol("implements");
+
 7383.    631    symbol("interface");
+
 7384.    631    symbol("package");
+
 7385.    631    symbol("private");
+
 7386.    631    symbol("protected");
+
 7387.    631    symbol("public");
+
 7388.    631    symbol("static");
+
 7389.    631    symbol("super");
+
 7390.    631    symbol("void");
+
 7391.    631    symbol("yield");
+
 7392.    631    symbol("}");
+
 7393.    631    ternary("?", ":");
+
 7394.    631
+
 7395.    631// Init token_nxt.
+
 7396.    631
+
 7397.    631    advance();
+
 7398.    631
+
 7399.    631// Parsing of JSON is simple:
+
 7400.    631
+
 7401.     25    if (state.mode_json) {
+
 7402.     25        state.token_tree = parse_json();
+
 7403.     25        advance("(end)");
+
 7404.     25        return;
+
 7405.    606    }
+
 7406.    606
+
 7407.    606// Because browsers encourage combining of script files, the first token might
+
 7408.    606// be a semicolon to defend against a missing semicolon in the preceding file.
+
 7409.    606
+
 7410.    606    if (option_dict.browser) {
+
 7411.      5        if (token_nxt.id === ";") {
+
 7412.      5            advance(";");
+
 7413.      5        }
+
 7414.      5
+
 7415.      5// If we are not in a browser, then the file form of strict pragma may be used.
+
 7416.      5
+
 7417.    601    } else if (token_nxt.value === "use strict") {
+
 7418.    601        advance("(string)");
+
 7419.    601        advance(";");
+
 7420.    606    }
+
 7421.    606    state.token_tree = parse_statements();
+
 7422.    606    advance("(end)");
+
 7423.    606
+
 7424.    606// Check global functions are ordered.
+
 7425.    606
+
 7426.    606    check_ordered(
+
 7427.    606        "function",
+
 7428.   2001        function_list.map(function ({
+
 7429.   2001            level,
+
 7430.   2001            name
+
 7431.   2001        }) {
+
 7432.    606            return (level === 1) && name;
+
 7433.   2001        }).filter(function (name) {
+
 7434.   1992            return option_dict.beta && name && name.id;
+
 7435.   2001        })
+
 7436.    606    );
+
 7437.    606}
+
 7438.      1
+
 7439.    518function jslint_phase4_walk(state) {
+
 7440.    518
+
 7441.    518// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
+
 7442.    518//          recursive traversal. Each node may be processed on the way down
+
 7443.    518//          (preaction) and on the way up (postaction).
+
 7444.    518
+
 7445.    518    let {
+
 7446.    518        artifact,
+
 7447.    518        catch_stack,
+
 7448.    518        function_stack,
+
 7449.    518        global_dict,
+
 7450.    518        is_equal,
+
 7451.    518        is_weird,
+
 7452.    518        option_dict,
+
 7453.    518        syntax_dict,
+
 7454.    518        test_cause,
+
 7455.    518        token_global,
+
 7456.    518        warn
+
 7457.    518    } = state;
+
 7458.    518    let block_stack = [];               // The stack of blocks.
+
 7459.    518    let blockage = token_global;        // The current block.
+
 7460.    518    let catchage = catch_stack[0];      // The current catch-block.
+
 7461.    518    let functionage = token_global;     // The current function.
+
 7462.    518    let postaction;
+
 7463.    518    let postamble;
+
 7464.    518    let posts = empty();
+
 7465.    518    let preaction;
+
 7466.    518    let preamble;
+
 7467.    518    let pres = empty();
+
 7468.    518
+
 7469.    518// The relational operators.
+
 7470.    518
+
 7471.    518    let relationop = object_assign_from_list(empty(), [
+
 7472.    518        "!=", "!==", "<", "<=", "==", "===", ">", ">="
+
 7473.    518    ], true);
+
 7474.    518
+
 7475.    518// Ambulation of the parse tree.
+
 7476.    518
+
 7477.   1036    function action(when) {
+
 7478.   1036
+
 7479.   1036// Produce a function that will register task functions that will be called as
+
 7480.   1036// the tree is traversed.
+
 7481.   1036
+
 7482.  19684        return function (arity, id, task) {
+
 7483.  19684            let a_set = when[arity];
+
 7484.  19684            let i_set;
+
 7485.  19684
+
 7486.  19684// The id parameter is optional. If excluded, the task will be applied to all
+
 7487.  19684// ids.
+
 7488.  19684
+
 7489.   4144            if (typeof id !== "string") {
+
 7490.   4144                task = id;
+
 7491.   4144                id = "(all)";
+
 7492.   4144            }
+
 7493.  19684
+
 7494.  19684// If this arity has no registrations yet, then create a set object to hold
+
 7495.  19684// them.
+
 7496.  19684
+
 7497.   5180            if (a_set === undefined) {
+
 7498.   5180                a_set = empty();
+
 7499.   5180                when[arity] = a_set;
+
 7500.   5180            }
+
 7501.  19684
+
 7502.  19684// If this id has no registrations yet, then create a set array to hold them.
+
 7503.  19684
+
 7504.  19684            i_set = a_set[id];
+
 7505.  19166            if (i_set === undefined) {
+
 7506.  19166                i_set = [];
+
 7507.  19166                a_set[id] = i_set;
+
 7508.  19166            }
+
 7509.  19684
+
 7510.  19684// Register the task with the arity and the id.
+
 7511.  19684
+
 7512.  19684            i_set.push(task);
+
 7513.  19684        };
+
 7514.   1036    }
+
 7515.    518
+
 7516.   1036    function amble(when) {
+
 7517.   1036
+
 7518.   1036// Produce a function that will act on the tasks registered by an action
+
 7519.   1036// function while walking the tree.
+
 7520.   1036
+
 7521. 210942        return function (the_token) {
+
 7522. 210942
+
 7523. 210942// Given a task set that was built by an action function, run all
+
 7524. 210942// relevant tasks on the token.
+
 7525. 210942
+
 7526. 210942            let a_set = when[the_token.arity];
+
 7527. 210942            let i_set;
+
 7528. 210942
+
 7529. 210942// If there are tasks associated with the token's arity...
+
 7530. 210942
+
 7531. 144702            if (a_set !== undefined) {
+
 7532. 144702
+
 7533. 144702// If there are tasks associated with the token's id...
+
 7534. 144702
+
 7535. 144702                i_set = a_set[the_token.id];
+
 7536. 144702                if (i_set !== undefined) {
+
 7537. 144702                    i_set.forEach(function (task) {
+
 7538. 144702                        task(the_token);
+
 7539. 144702                    });
+
 7540. 144702                }
+
 7541. 144702
+
 7542. 144702// If there are tasks for all ids.
+
 7543. 144702
+
 7544. 144702                i_set = a_set["(all)"];
+
 7545. 144702                if (i_set !== undefined) {
+
 7546. 144702                    i_set.forEach(function (task) {
+
 7547. 144702                        task(the_token);
+
 7548. 144702                    });
+
 7549. 144702                }
+
 7550. 144702            }
+
 7551. 210942        };
+
 7552.   1036    }
+
 7553.    518
+
 7554.   2818    function init_variable(name) {
+
 7555.   2818        let the_variable = lookup(name);
+
 7556.   2759        if (!the_variable || the_variable.readonly) {
+
 7557.     61            warn("bad_assignment_a", name);
+
 7558.     61            return;
+
 7559.   2757        }
+
 7560.   2757        the_variable.init = true;
+
 7561.   2757    }
+
 7562.    518
+
 7563.  31509    function lookup(thing) {
+
 7564.  31509        let id = thing.id;
+
 7565.  31509        let the_variable;
+
 7566.      1        if (thing.arity !== "variable") {
+
 7567.      1            return;
+
 7568.  31508        }
+
 7569.  31508
+
 7570.  31508// Look up the variable in the current context.
+
 7571.  31508
+
 7572.  31508        the_variable = functionage.context[id] || catchage.context[id];
+
 7573.  31509
+
 7574.  31509// If it isn't local, search all the other contexts. If there are name
+
 7575.  31509// collisions, take the most recent.
+
 7576.  31509
+
 7577.  24629        if (the_variable && the_variable.role === "label") {
+
 7578.      1
+
 7579.      1// test_cause:
+
 7580.      1// ["aa:while(0){aa;}", "lookup", "label_a", "aa", 13]
+
 7581.      1
+
 7582.      1            warn("label_a", thing);
+
 7583.      1            return the_variable;
+
 7584.  31507        }
+
 7585.  31507        if (!the_variable) {
+
 7586.  14775            function_stack.forEach(function ({
+
 7587.  14775                context
+
 7588.  14775            }) {
+
 7589.   6879                if (context[id] && context[id].role !== "label") {
+
 7590.   6879                    the_variable = context[id];
+
 7591.   6879                }
+
 7592.  14775            });
+
 7593.   6879
+
 7594.   6879// If it isn't in any of those either, perhaps it is a predefined global.
+
 7595.   6879// If so, add it to the global context.
+
 7596.   6879
+
 7597.   6879            if (!the_variable && global_dict[id] === undefined) {
+
 7598.   6879
+
 7599.   6879// test_cause:
+
 7600.   6879// ["aa", "lookup", "undeclared_a", "aa", 1]
+
 7601.   6879// ["class aa{}", "lookup", "undeclared_a", "aa", 7]
+
 7602.   6879// ["
+
 7603.   6879// let aa=0;try{aa();}catch(bb){bb();}bb();
+
 7604.   6879// ", "lookup", "undeclared_a", "bb", 36]
+
 7605.   6879// ["
+
 7606.   6879// let aa=0;try{aa();}catch(ignore){bb();}
+
 7607.   6879// ", "lookup", "undeclared_a", "bb", 34]
+
 7608.   6879
+
 7609.   6879                warn("undeclared_a", thing);
+
 7610.   6879                return;
+
 7611.   6879            }
+
 7612.   6879            if (!the_variable) {
+
 7613.   6879                the_variable = {
+
 7614.   6879                    dead: false,
+
 7615.   6879                    id,
+
 7616.   6879                    init: true,
+
 7617.   6879                    parent: token_global,
+
 7618.   6879                    readonly: true,
+
 7619.   6879                    role: "variable",
+
 7620.   6879                    used: 0
+
 7621.   6879                };
+
 7622.   6879                token_global.context[id] = the_variable;
+
 7623.   6879            }
+
 7624.   6879            the_variable.closure = true;
+
 7625.   6879            functionage.context[id] = the_variable;
+
 7626.  31370        }
+
 7627.  31370        if (
+
 7628.  31370            (
+
 7629.  31370                the_variable.calls === undefined
+
 7630.  31370                || functionage.name === undefined
+
 7631.   4364                || the_variable.calls[functionage.name.id] === undefined
+
 7632.  31509            )
+
 7633.  31185            && the_variable.dead
+
 7634.      3        ) {
+
 7635.      3
+
 7636.      3// test_cause:
+
 7637.      3// ["let aa;if(aa){let bb;}bb;", "lookup", "out_of_scope_a", "bb", 23]
+
 7638.      3
+
 7639.      3            warn("out_of_scope_a", thing);
+
 7640.  31370        }
+
 7641.  31370        return the_variable;
+
 7642.  31370    }
+
 7643.    518
+
 7644.   5009    function post_a(thing) {
+
 7645.   5009
+
 7646.   5009// Assignment using = sets the init property of a variable. No other assignment
+
 7647.   5009// operator can do this. A = token keeps that variable (or array of variables
+
 7648.   5009// in case of destructuring) in its name property.
+
 7649.   5009
+
 7650.   5009        const lvalue = thing.expression[0];
+
 7651.   5009        let right;
+
 7652.   4234        if (thing.id === "=") {
+
 7653.   4234            if (thing.names !== undefined) {
+
 7654.   4234
+
 7655.   4234// test_cause:
+
 7656.   4234// ["if(0){aa=0}", "post_a", "=", "", 0]
+
 7657.   4234
+
 7658.   4234                test_cause("=");
+
 7659.   4234
+
 7660.   4234// Probably deadcode.
+
 7661.   4234// if (Array.isArray(thing.names)) {
+
 7662.   4234//     thing.names.forEach(init_variable);
+
 7663.   4234// } else {
+
 7664.   4234//     init_variable(thing.names);
+
 7665.   4234// }
+
 7666.   4234
+
 7667.   4234                jslint_assert(
+
 7668.   4234                    !Array.isArray(thing.names),
+
 7669.   4234                    `Expected !Array.isArray(thing.names).`
+
 7670.   4234                );
+
 7671.   4234                init_variable(thing.names);
+
 7672.   4234            } else {
+
 7673.   4234                if (lvalue.id === "[" || lvalue.id === "{") {
+
 7674.   4234                    lvalue.expression.forEach(function (thing) {
+
 7675.   4234                        if (thing.variable) {
+
 7676.   4234                            thing.variable.init = true;
+
 7677.   4234                        }
+
 7678.   4234                    });
+
 7679.   4234                } else if (
+
 7680.   4234                    lvalue.id === "."
+
 7681.   4234                    && thing.expression[1].id === "undefined"
+
 7682.   4234                ) {
+
 7683.   4234
+
 7684.   4234// test_cause:
+
 7685.   4234// ["aa.aa=undefined", "post_a", "expected_a_b", "undefined", 1]
+
 7686.   4234
+
 7687.   4234                    warn(
+
 7688.   4234                        "expected_a_b",
+
 7689.   4234                        lvalue.expression,
+
 7690.   4234                        "delete",
+
 7691.   4234                        "undefined"
+
 7692.   4234                    );
+
 7693.   4234                }
+
 7694.   4234            }
+
 7695.   4234        } else {
+
 7696.    775            if (lvalue.arity === "variable") {
+
 7697.    775                if (!lvalue.variable || lvalue.variable.readonly) {
+
 7698.    775                    warn("bad_assignment_a", lvalue);
+
 7699.    775                }
+
 7700.    775            }
+
 7701.    775            right = syntax_dict[thing.expression[1].id];
+
 7702.    775            if (
+
 7703.    775                right !== undefined
+
 7704.    775                && (
+
 7705.    775                    right.id === "function"
+
 7706.    775                    || right.id === "=>"
+
 7707.    775                    || (
+
 7708.    775                        right.constant
+
 7709.    775                        && right.id !== "(number)"
+
 7710.    775                        && (right.id !== "(string)" || thing.id !== "+=")
+
 7711.    775                    )
+
 7712.    775                )
+
 7713.    775            ) {
+
 7714.    775
+
 7715.    775// test_cause:
+
 7716.    775// ["aa+=undefined", "post_a", "unexpected_a", "undefined", 5]
+
 7717.    775
+
 7718.    775                warn("unexpected_a", thing.expression[1]);
+
 7719.    775            }
+
 7720.    775        }
+
 7721.   5009    }
+
 7722.    518
+
 7723.    706    function post_a_pluseq(thing) {
+
 7724.    706        const right = thing.expression[1];
+
 7725.    396        if (right.constant) {
+
 7726.    396            if (
+
 7727.    396                right.value === ""
+
 7728.    396                || (right.id === "(number)" && right.value === "0")
+
 7729.    396                || right.id === "(boolean)"
+
 7730.    396                || right.id === "null"
+
 7731.    396                || right.id === "undefined"
+
 7732.    396                || Number.isNaN(right.value)
+
 7733.    396            ) {
+
 7734.    396                warn("unexpected_a", right);
+
 7735.    396            }
+
 7736.    396        }
+
 7737.    706    }
+
 7738.    518
+
 7739.  31933    function post_b(thing) {
+
 7740.  31933        let right;
+
 7741.   3960        if (relationop[thing.id]) {
+
 7742.   3960            if (
+
 7743.   3960                is_weird(thing.expression[0])
+
 7744.   3960                || is_weird(thing.expression[1])
+
 7745.   3960                || is_equal(thing.expression[0], thing.expression[1])
+
 7746.   3960                || (
+
 7747.   3960                    thing.expression[0].constant === true
+
 7748.   3960                    && thing.expression[1].constant === true
+
 7749.   3960                )
+
 7750.   3960            ) {
+
 7751.   3960
+
 7752.   3960// test_cause:
+
 7753.   3960// ["if(0===0){0}", "post_b", "weird_relation_a", "===", 5]
+
 7754.   3960
+
 7755.   3960                warn("weird_relation_a", thing);
+
 7756.   3960            }
+
 7757.   3960        }
+
 7758.   2023        if (thing.id === "+") {
+
 7759.   2023            if (!option_dict.convert) {
+
 7760.   2023                if (thing.expression[0].value === "") {
+
 7761.   2023
+
 7762.   2023// test_cause:
+
 7763.   2023// ["\"\"+0", "post_b", "expected_a_b", "\"\" +", 3]
+
 7764.   2023
+
 7765.   2023                    warn("expected_a_b", thing, "String(...)", "\"\" +");
+
 7766.   2023                } else if (thing.expression[1].value === "") {
+
 7767.   2023
+
 7768.   2023// test_cause:
+
 7769.   2023// ["0+\"\"", "post_b", "expected_a_b", "+ \"\"", 2]
+
 7770.   2023
+
 7771.   2023                    warn("expected_a_b", thing, "String(...)", "+ \"\"");
+
 7772.   2023                }
+
 7773.   2023            }
+
 7774.  29910        } else if (thing.id === "[") {
+
 7775.  29910            if (thing.expression[0].id === "window") {
+
 7776.  29910
+
 7777.  29910// test_cause:
+
 7778.  29910// ["aa=window[0]", "post_b", "weird_expression_a", "window[...]", 10]
+
 7779.  29910
+
 7780.  29910                warn("weird_expression_a", thing, "window[...]");
+
 7781.  29910            }
+
 7782.  29910            if (thing.expression[0].id === "self") {
+
 7783.  29910
+
 7784.  29910// test_cause:
+
 7785.  29910// ["aa=self[0]", "post_b", "weird_expression_a", "self[...]", 8]
+
 7786.  29910
+
 7787.  29910                warn("weird_expression_a", thing, "self[...]");
+
 7788.  29910            }
+
 7789.  29910        } else if (thing.id === "." || thing.id === "?.") {
+
 7790.  29910            if (thing.expression.id === "RegExp") {
+
 7791.  29910
+
 7792.  29910// test_cause:
+
 7793.  29910// ["aa=RegExp.aa", "post_b", "weird_expression_a", ".", 10]
+
 7794.  29910
+
 7795.  29910                warn("weird_expression_a", thing);
+
 7796.  29910            }
+
 7797.  29910        } else if (thing.id !== "=>" && thing.id !== "(") {
+
 7798.  29910            right = thing.expression[1];
+
 7799.  29910            if (
+
 7800.  29910                (thing.id === "+" || thing.id === "-")
+
 7801.  29910                && right.id === thing.id
+
 7802.  29910                && right.arity === "unary"
+
 7803.  29910                && !right.wrapped
+
 7804.  29910            ) {
+
 7805.  29910
+
 7806.  29910// test_cause:
+
 7807.  29910// ["0- -0", "post_b", "wrap_unary", "-", 4]
+
 7808.  29910
+
 7809.  29910                warn("wrap_unary", right);
+
 7810.  29910            }
+
 7811.  29910            if (
+
 7812.  29910                thing.expression[0].constant === true
+
 7813.  29910                && right.constant === true
+
 7814.  29910            ) {
+
 7815.  29910                thing.constant = true;
+
 7816.  29910            }
+
 7817.  29910        }
+
 7818.  31933    }
+
 7819.    518
+
 7820.   1202    function post_b_and(thing) {
+
 7821.   1202        if (
+
 7822.   1202            is_weird(thing.expression[0])
+
 7823.   1197            || is_equal(thing.expression[0], thing.expression[1])
+
 7824.   1183            || thing.expression[0].constant === true
+
 7825.   1181            || thing.expression[1].constant === true
+
 7826.     21        ) {
+
 7827.     21
+
 7828.     21// test_cause:
+
 7829.     21// ["aa=(()=>0)&&(()=>0)", "post_b_and", "weird_condition_a", "&&", 11]
+
 7830.     21// ["aa=(``?``:``)&&(``?``:``)", "post_b_and", "weird_condition_a", "&&", 14]
+
 7831.     21// ["aa=/./&&/./", "post_b_and", "weird_condition_a", "&&", 7]
+
 7832.     21// ["aa=0&&0", "post_b_and", "weird_condition_a", "&&", 5]
+
 7833.     21// ["aa=[]&&[]", "post_b_and", "weird_condition_a", "&&", 6]
+
 7834.     21// ["aa=`${0}`&&`${0}`", "post_b_and", "weird_condition_a", "&&", 10]
+
 7835.     21// ["
+
 7836.     21// aa=function aa(){}&&function aa(){}
+
 7837.     21// ", "post_b_and", "weird_condition_a", "&&", 19]
+
 7838.     21// ["aa={}&&{}", "post_b_and", "weird_condition_a", "&&", 6]
+
 7839.     21
+
 7840.     21            warn("weird_condition_a", thing);
+
 7841.     21        }
+
 7842.   1202    }
+
 7843.    518
+
 7844.   1460    function post_b_lbracket(thing) {
+
 7845.      1        if (thing.expression[0].id === "RegExp") {
+
 7846.      1
+
 7847.      1// test_cause:
+
 7848.      1// ["aa=RegExp[0]", "post_b_lbracket", "weird_expression_a", "[", 10]
+
 7849.      1
+
 7850.      1            warn("weird_expression_a", thing);
+
 7851.      1        }
+
 7852.      1        if (is_weird(thing.expression[1])) {
+
 7853.      1
+
 7854.      1// test_cause:
+
 7855.      1// ["aa[[0]]", "post_b_lbracket", "weird_expression_a", "[", 4]
+
 7856.      1
+
 7857.      1            warn("weird_expression_a", thing.expression[1]);
+
 7858.      1        }
+
 7859.   1460    }
+
 7860.    518
+
 7861.  10411    function post_b_lparen(thing) {
+
 7862.  10411        let arg;
+
 7863.  10411        let array;
+
 7864.  10411        let cack;
+
 7865.  10411        let left = thing.expression[0];
+
 7866.  10411        let new_date;
+
 7867.  10411        let paren;
+
 7868.  10411        let the_new;
+
 7869.    154        if (left.id === "new") {
+
 7870.    154            the_new = left;
+
 7871.    154            left = left.expression;
+
 7872.    154        }
+
 7873.     37        if (left.id === "function") {
+
 7874.     37            if (!thing.wrapped) {
+
 7875.     37
+
 7876.     37// test_cause:
+
 7877.     37// ["aa=function(){}()", "post_b_lparen", "wrap_immediate", "(", 16]
+
 7878.     37
+
 7879.     37                warn("wrap_immediate", thing);
+
 7880.     37            }
+
 7881.  10374        } else if (left.identifier) {
+
 7882.  10374            if (the_new !== undefined) {
+
 7883.  10374                if (
+
 7884.  10374                    left.id[0] > "Z"
+
 7885.  10374                    || left.id === "BigInt"
+
 7886.  10374                    || left.id === "Boolean"
+
 7887.  10374                    || left.id === "Number"
+
 7888.  10374                    || left.id === "String"
+
 7889.  10374                    || left.id === "Symbol"
+
 7890.  10374                ) {
+
 7891.  10374
+
 7892.  10374// test_cause:
+
 7893.  10374// ["new BigInt()", "post_b_lparen", "unexpected_a", "new", 1]
+
 7894.  10374// ["new Boolean()", "post_b_lparen", "unexpected_a", "new", 1]
+
 7895.  10374// ["new Number()", "post_b_lparen", "unexpected_a", "new", 1]
+
 7896.  10374// ["new String()", "post_b_lparen", "unexpected_a", "new", 1]
+
 7897.  10374// ["new Symbol()", "post_b_lparen", "unexpected_a", "new", 1]
+
 7898.  10374// ["new aa()", "post_b_lparen", "unexpected_a", "new", 1]
+
 7899.  10374
+
 7900.  10374                    warn("unexpected_a", the_new);
+
 7901.  10374                } else if (left.id === "Function") {
+
 7902.  10374                    if (!option_dict.eval) {
+
 7903.  10374
+
 7904.  10374// test_cause:
+
 7905.  10374// ["new Function()", "post_b_lparen", "unexpected_a", "new Function", 5]
+
 7906.  10374
+
 7907.  10374                        warn("unexpected_a", left, "new Function");
+
 7908.  10374                    }
+
 7909.  10374                } else if (left.id === "Array") {
+
 7910.  10374                    arg = thing.expression;
+
 7911.  10374                    if (arg.length !== 2 || arg[1].id === "(string)") {
+
 7912.  10374
+
 7913.  10374// test_cause:
+
 7914.  10374// ["new Array()", "post_b_lparen", "expected_a_b", "new Array", 5]
+
 7915.  10374
+
 7916.  10374                        warn("expected_a_b", left, "[]", "new Array");
+
 7917.  10374                    }
+
 7918.  10374                } else if (left.id === "Object") {
+
 7919.  10374
+
 7920.  10374// test_cause:
+
 7921.  10374// ["new Object()", "post_b_lparen", "expected_a_b", "new Object", 5]
+
 7922.  10374
+
 7923.  10374                    warn(
+
 7924.  10374                        "expected_a_b",
+
 7925.  10374                        left,
+
 7926.  10374                        "Object.create(null)",
+
 7927.  10374                        "new Object"
+
 7928.  10374                    );
+
 7929.  10374                }
+
 7930.  10374            } else {
+
 7931.  10374                if (
+
 7932.  10374                    left.id[0] >= "A"
+
 7933.  10374                    && left.id[0] <= "Z"
+
 7934.  10374                    && left.id !== "BigInt"
+
 7935.  10374                    && left.id !== "Boolean"
+
 7936.  10374                    && left.id !== "Number"
+
 7937.  10374                    && left.id !== "String"
+
 7938.  10374                    && left.id !== "Symbol"
+
 7939.  10374                ) {
+
 7940.  10374
+
 7941.  10374// test_cause:
+
 7942.  10374// ["let Aa=Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8]
+
 7943.  10374
+
 7944.  10374                    warn("expected_a_before_b", left, "new", artifact(left));
+
 7945.  10374                }
+
 7946.  10374            }
+
 7947.  10374        } else if (left.id === ".") {
+
 7948.  10374            cack = the_new !== undefined;
+
 7949.  10374            if (left.expression.id === "Date" && left.name.id === "UTC") {
+
 7950.  10374
+
 7951.  10374// test_cause:
+
 7952.  10374// ["new Date.UTC()", "post_b_lparen", "cack", "", 0]
+
 7953.  10374
+
 7954.  10374                test_cause("cack");
+
 7955.  10374                cack = !cack;
+
 7956.  10374            }
+
 7957.  10374            if (jslint_rgx_cap.test(left.name.id) !== cack) {
+
 7958.  10374                if (the_new !== undefined) {
+
 7959.  10374
+
 7960.  10374// test_cause:
+
 7961.  10374// ["new Date.UTC()", "post_b_lparen", "unexpected_a", "new", 1]
+
 7962.  10374
+
 7963.  10374                    warn("unexpected_a", the_new);
+
 7964.  10374                } else {
+
 7965.  10374
+
 7966.  10374// test_cause:
+
 7967.  10374// ["let Aa=Aa.Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8]
+
 7968.  10374
+
 7969.  10374                    warn(
+
 7970.  10374                        "expected_a_before_b",
+
 7971.  10374                        left.expression,
+
 7972.  10374                        "new",
+
 7973.  10374                        left.name.id
+
 7974.  10374                    );
+
 7975.  10374                }
+
 7976.  10374            }
+
 7977.  10374            if (left.name.id === "getTime") {
+
 7978.  10374                paren = left.expression;
+
 7979.  10374                if (paren.id === "(") {
+
 7980.  10374                    array = paren.expression;
+
 7981.  10374                    if (array.length === 1) {
+
 7982.  10374                        new_date = array[0];
+
 7983.  10374                        if (
+
 7984.  10374                            new_date.id === "new"
+
 7985.  10374                            && new_date.expression.id === "Date"
+
 7986.  10374                        ) {
+
 7987.  10374
+
 7988.  10374// test_cause:
+
 7989.  10374// ["
+
 7990.  10374// new Date().getTime()
+
 7991.  10374// ", "post_b_lparen", "expected_a_b", "new Date().getTime()", 1]
+
 7992.  10374
+
 7993.  10374                            warn(
+
 7994.  10374                                "expected_a_b",
+
 7995.  10374                                new_date,
+
 7996.  10374                                "Date.now()",
+
 7997.  10374                                "new Date().getTime()"
+
 7998.  10374                            );
+
 7999.  10374                        }
+
 8000.  10374                    }
+
 8001.  10374                }
+
 8002.  10374            }
+
 8003.  10374        }
+
 8004.  10411    }
+
 8005.    518
+
 8006.    974    function post_b_or(thing) {
+
 8007.    974        if (
+
 8008.    974            is_weird(thing.expression[0])
+
 8009.    974            || is_equal(thing.expression[0], thing.expression[1])
+
 8010.    973            || thing.expression[0].constant === true
+
 8011.      2        ) {
+
 8012.      2
+
 8013.      2// test_cause:
+
 8014.      2// ["aa=0||0", "post_b_or", "weird_condition_a", "||", 5]
+
 8015.      2
+
 8016.      2            warn("weird_condition_a", thing);
+
 8017.      2        }
+
 8018.    974    }
+
 8019.    518
+
 8020.     78    function post_s_export(the_thing) {
+
 8021.     78
+
 8022.     78// Some features must be at the most outermost level.
+
 8023.     78
+
 8024.      1        if (blockage !== token_global) {
+
 8025.      1
+
 8026.      1// test_cause:
+
 8027.      1// ["
+
 8028.      1// if(0){import aa from "aa";}
+
 8029.      1// ", "post_s_export", "misplaced_a", "import", 7]
+
 8030.      1
+
 8031.      1            warn("misplaced_a", the_thing);
+
 8032.      1        }
+
 8033.     78    }
+
 8034.    518
+
 8035.      8    function post_s_for(thing) {
+
 8036.      8
+
 8037.      8// Recurse walk_statement().
+
 8038.      8
+
 8039.      8        walk_statement(thing.inc);
+
 8040.      8    }
+
 8041.    518
+
 8042.   2001    function post_s_function(thing) {
+
 8043.   2001        delete functionage.async;
+
 8044.   2001        delete functionage.finally;
+
 8045.   2001        delete functionage.loop;
+
 8046.   2001        delete functionage.statement_prv;
+
 8047.   2001        delete functionage.switch;
+
 8048.   2001        delete functionage.try;
+
 8049.   2001        functionage = function_stack.pop();
+
 8050.      3        if (thing.wrapped) {
+
 8051.      3
+
 8052.      3// test_cause:
+
 8053.      3// ["aa=(function(){})", "post_s_function", "unexpected_parens", "function", 5]
+
 8054.      3
+
 8055.      3            warn("unexpected_parens", thing);
+
 8056.      3        }
+
 8057.   2001        return post_s_lbrace();
+
 8058.   2001    }
+
 8059.    518
+
 8060.     61    function post_s_import(the_thing) {
+
 8061.     61        const name = the_thing.name;
+
 8062.     60        if (name) {
+
 8063.     60            if (Array.isArray(name)) {
+
 8064.     60                name.forEach(function (name) {
+
 8065.     60                    name.dead = false;
+
 8066.     60                    name.init = true;
+
 8067.     60                    blockage.live.push(name);
+
 8068.     60                });
+
 8069.     60            } else {
+
 8070.     60                name.dead = false;
+
 8071.     60                name.init = true;
+
 8072.     60                blockage.live.push(name);
+
 8073.     60            }
+
 8074.     60            return post_s_export(the_thing);
+
 8075.     60        }
+
 8076.     61    }
+
 8077.    518
+
 8078.   7695    function post_s_lbrace() {
+
 8079.   2987        blockage.live.forEach(function (name) {
+
 8080.   2987            name.dead = true;
+
 8081.   2987        });
+
 8082.   7695        delete blockage.live;
+
 8083.   7695        blockage = block_stack.pop();
+
 8084.   7695    }
+
 8085.    518
+
 8086.     56    function post_s_try(thing) {
+
 8087.     54        if (thing.catch) {
+
 8088.     54            if (thing.catch.name) {
+
 8089.     54                Object.assign(catchage.context[thing.catch.name.id], {
+
 8090.     54                    dead: false,
+
 8091.     54                    init: true
+
 8092.     54                });
+
 8093.     54            }
+
 8094.     54
+
 8095.     54// Recurse walk_statement().
+
 8096.     54
+
 8097.     54            walk_statement(thing.catch.block);
+
 8098.     54
+
 8099.     54// Restore previous catch-scope after catch-block.
+
 8100.     54
+
 8101.     54            catchage = catch_stack.pop();
+
 8102.     54        }
+
 8103.     56    }
+
 8104.    518
+
 8105.   2320    function post_s_var(thing) {
+
 8106.   2635        thing.names.forEach(function (name) {
+
 8107.   2635            name.dead = false;
+
 8108.   1250            if (name.expression !== undefined) {
+
 8109.   1250                walk_expression(name.expression);
+
 8110.   1250
+
 8111.   1250// Probably deadcode.
+
 8112.   1250// if (name.id === "{" || name.id === "[") {
+
 8113.   1250//     name.names.forEach(subactivate);
+
 8114.   1250// } else {
+
 8115.   1250//     name.init = true;
+
 8116.   1250// }
+
 8117.   1250
+
 8118.   1250                jslint_assert(
+
 8119.   1250                    !(name.id === "{" || name.id === "["),
+
 8120.   1250                    `Expected !(name.id === "{" || name.id === "[").`
+
 8121.   1250                );
+
 8122.   1250                name.init = true;
+
 8123.   1250            }
+
 8124.   2635            blockage.live.push(name);
+
 8125.   2635        });
+
 8126.   2320    }
+
 8127.    518
+
 8128.    213    function post_t(thing) {
+
 8129.    213        if (
+
 8130.    213            is_weird(thing.expression[0])
+
 8131.    213            || thing.expression[0].constant === true
+
 8132.    206            || is_equal(thing.expression[1], thing.expression[2])
+
 8133.      9        ) {
+
 8134.      9
+
 8135.      9// test_cause:
+
 8136.      9// ["let aa=(aa?`${0}`:`${0}`);", "post_t", "unexpected_a", "?", 11]
+
 8137.      9// ["let aa=(aa?`0`:`0`);", "post_t", "unexpected_a", "?", 11]
+
 8138.      9
+
 8139.      9            warn("unexpected_a", thing);
+
 8140.    204        } else if (is_equal(thing.expression[0], thing.expression[1])) {
+
 8141.    204
+
 8142.    204// test_cause:
+
 8143.    204// ["aa?aa:0", "post_t", "expected_a_b", "?", 3]
+
 8144.    204
+
 8145.    204            warn("expected_a_b", thing, "||", "?");
+
 8146.    204        } else if (is_equal(thing.expression[0], thing.expression[2])) {
+
 8147.    204
+
 8148.    204// test_cause:
+
 8149.    204// ["aa?0:aa", "post_t", "expected_a_b", "?", 3]
+
 8150.    204
+
 8151.    204            warn("expected_a_b", thing, "&&", "?");
+
 8152.    204        } else if (
+
 8153.    204            thing.expression[1].id === "true"
+
 8154.    204            && thing.expression[2].id === "false"
+
 8155.    204        ) {
+
 8156.    204
+
 8157.    204// test_cause:
+
 8158.    204// ["aa?true:false", "post_t", "expected_a_b", "?", 3]
+
 8159.    204
+
 8160.    204            warn("expected_a_b", thing, "!!", "?");
+
 8161.    204        } else if (
+
 8162.    204            thing.expression[1].id === "false"
+
 8163.    204            && thing.expression[2].id === "true"
+
 8164.    204        ) {
+
 8165.    204
+
 8166.    204// test_cause:
+
 8167.    204// ["aa?false:true", "post_t", "expected_a_b", "?", 3]
+
 8168.    204
+
 8169.    204            warn("expected_a_b", thing, "!", "?");
+
 8170.    204        } else if (
+
 8171.    204            thing.expression[0].wrapped !== true
+
 8172.    204            && (
+
 8173.    204                thing.expression[0].id === "||"
+
 8174.    204                || thing.expression[0].id === "&&"
+
 8175.    204            )
+
 8176.    204        ) {
+
 8177.    204
+
 8178.    204// test_cause:
+
 8179.    204// ["(aa&&!aa?0:1)", "post_t", "wrap_condition", "&&", 4]
+
 8180.    204
+
 8181.    204            warn("wrap_condition", thing.expression[0]);
+
 8182.    204        }
+
 8183.    213    }
+
 8184.    518
+
 8185.   4106    function post_u(thing) {
+
 8186.    779        if (thing.id === "`") {
+
 8187.    779            if (thing.expression.every(function (thing) {
+
 8188.    779                return thing.constant;
+
 8189.    779            })) {
+
 8190.    779                thing.constant = true;
+
 8191.    779            }
+
 8192.   3327        } else if (thing.id === "!") {
+
 8193.   3327            if (thing.expression.constant === true) {
+
 8194.   3327                warn("unexpected_a", thing);
+
 8195.   3327            }
+
 8196.   3327        } else if (thing.id === "!!") {
+
 8197.   3327            if (!option_dict.convert) {
+
 8198.   3327
+
 8199.   3327// test_cause:
+
 8200.   3327// ["!!0", "post_u", "expected_a_b", "!!", 1]
+
 8201.   3327
+
 8202.   3327                warn("expected_a_b", thing, "Boolean(...)", "!!");
+
 8203.   3327            }
+
 8204.   3327        } else if (
+
 8205.   3327            thing.id !== "["
+
 8206.   3327            && thing.id !== "{"
+
 8207.   3327            && thing.id !== "function"
+
 8208.   3327            && thing.id !== "new"
+
 8209.   3327        ) {
+
 8210.   3327            if (thing.expression.constant === true) {
+
 8211.   3327                thing.constant = true;
+
 8212.   3327            }
+
 8213.   3327        }
+
 8214.   4106    }
+
 8215.    518
+
 8216.      7    function post_u_plus(thing) {
+
 8217.      7        const right = thing.expression;
+
 8218.      7        if (!option_dict.convert) {
+
 8219.      7
+
 8220.      7// test_cause:
+
 8221.      7// ["aa=+0", "post_u_plus", "expected_a_b", "+", 4]
+
 8222.      7
+
 8223.      7            warn("expected_a_b", thing, "Number(...)", "+");
+
 8224.      7        }
+
 8225.      1        if (right.id === "(" && right.expression[0].id === "new") {
+
 8226.      1            warn("unexpected_a_before_b", thing, "+", "new");
+
 8227.      6        } else if (
+
 8228.      6            right.constant
+
 8229.      6            || right.id === "{"
+
 8230.      6            || (right.id === "[" && right.arity !== "binary")
+
 8231.      6        ) {
+
 8232.      6            warn("unexpected_a", thing, "+");
+
 8233.      6        }
+
 8234.      7    }
+
 8235.    518
+
 8236.  36945    function pre_a_bitwise(thing) {
+
 8237.  36945
+
 8238.  36945// These are the bitwise operators.
+
 8239.  36945
+
 8240.  36943        switch (!option_dict.bitwise && thing.id) {
+
 8241.      2        case "&":
+
 8242.      3        case "&=":
+
 8243.      5        case "<<":
+
 8244.      6        case "<<=":
+
 8245.      8        case ">>":
+
 8246.      9        case ">>=":
+
 8247.     11        case ">>>":
+
 8248.     12        case ">>>=":
+
 8249.     14        case "^":
+
 8250.     15        case "^=":
+
 8251.     17        case "|":
+
 8252.     18        case "|=":
+
 8253.     21        case "~":
+
 8254.     21
+
 8255.     21// test_cause:
+
 8256.     21// ["0&0", "pre_a_bitwise", "unexpected_a", "&", 2]
+
 8257.     21// ["0&=0", "pre_a_bitwise", "unexpected_a", "&=", 2]
+
 8258.     21// ["0<<0", "pre_a_bitwise", "unexpected_a", "<<", 2]
+
 8259.     21// ["0<<=0", "pre_a_bitwise", "unexpected_a", "<<=", 2]
+
 8260.     21// ["0>>0", "pre_a_bitwise", "unexpected_a", ">>", 2]
+
 8261.     21// ["0>>=0", "pre_a_bitwise", "unexpected_a", ">>=", 2]
+
 8262.     21// ["0>>>0", "pre_a_bitwise", "unexpected_a", ">>>", 2]
+
 8263.     21// ["0>>>=0", "pre_a_bitwise", "unexpected_a", ">>>=", 2]
+
 8264.     21// ["0^0", "pre_a_bitwise", "unexpected_a", "^", 2]
+
 8265.     21// ["0^=0", "pre_a_bitwise", "unexpected_a", "^=", 2]
+
 8266.     21// ["0|0", "pre_a_bitwise", "unexpected_a", "|", 2]
+
 8267.     21// ["0|=0", "pre_a_bitwise", "unexpected_a", "|=", 2]
+
 8268.     21// ["~0", "pre_a_bitwise", "unexpected_a", "~", 1]
+
 8269.     21
+
 8270.     21            warn("unexpected_a", thing);
+
 8271.     21            break;
+
 8272.  36945        }
+
 8273.  36945        if (
+
 8274.  36945            thing.id !== "("
+
 8275.  26534            && thing.id !== "&&"
+
 8276.  25332            && thing.id !== "||"
+
 8277.  24358            && thing.id !== "="
+
 8278.  20124            && Array.isArray(thing.expression)
+
 8279.   8506            && thing.expression.length === 2
+
 8280.   8505            && (
+
 8281.   8505                relationop[thing.expression[0].id] === true
+
 8282.   8505                || relationop[thing.expression[1].id] === true
+
 8283.   8505            )
+
 8284.      1        ) {
+
 8285.      1
+
 8286.      1// test_cause:
+
 8287.      1// ["0<0<0", "pre_a_bitwise", "unexpected_a", "<", 4]
+
 8288.      1
+
 8289.      1            warn("unexpected_a", thing);
+
 8290.      1        }
+
 8291.  36945    }
+
 8292.    518
+
 8293.  31933    function pre_b(thing) {
+
 8294.  31933        let left;
+
 8295.  31933        let right;
+
 8296.  31933        let value;
+
 8297.   3960        if (relationop[thing.id] === true) {
+
 8298.   3960            left = thing.expression[0];
+
 8299.   3960            right = thing.expression[1];
+
 8300.   3960            if (left.id === "NaN" || right.id === "NaN") {
+
 8301.   3960
+
 8302.   3960// test_cause:
+
 8303.   3960// ["NaN===NaN", "pre_b", "number_isNaN", "===", 4]
+
 8304.   3960
+
 8305.   3960                warn("number_isNaN", thing);
+
 8306.   3960            } else if (left.id === "typeof") {
+
 8307.   3960                if (right.id !== "(string)") {
+
 8308.   3960                    if (right.id !== "typeof") {
+
 8309.   3960
+
 8310.   3960// test_cause:
+
 8311.   3960// ["typeof 0===0", "pre_b", "expected_string_a", "0", 12]
+
 8312.   3960
+
 8313.   3960                        warn("expected_string_a", right);
+
 8314.   3960                    }
+
 8315.   3960                } else {
+
 8316.   3960                    value = right.value;
+
 8317.   3960                    if (value === "null" || value === "undefined") {
+
 8318.   3960
+
 8319.   3960// test_cause:
+
 8320.   3960// ["
+
 8321.   3960// typeof aa==="undefined"
+
 8322.   3960// ", "pre_b", "unexpected_typeof_a", "undefined", 13]
+
 8323.   3960
+
 8324.   3960                        warn("unexpected_typeof_a", right, value);
+
 8325.   3960                    } else if (
+
 8326.   3960                        value !== "bigint"
+
 8327.   3960                        && value !== "boolean"
+
 8328.   3960                        && value !== "function"
+
 8329.   3960                        && value !== "number"
+
 8330.   3960                        && value !== "object"
+
 8331.   3960                        && value !== "string"
+
 8332.   3960                        && value !== "symbol"
+
 8333.   3960                    ) {
+
 8334.   3960
+
 8335.   3960// test_cause:
+
 8336.   3960// ["typeof 0===\"aa\"", "pre_b", "expected_type_string_a", "aa", 12]
+
 8337.   3960
+
 8338.   3960                        warn("expected_type_string_a", right, value);
+
 8339.   3960                    }
+
 8340.   3960                }
+
 8341.   3960            }
+
 8342.   3960        }
+
 8343.  31933    }
+
 8344.    518
+
 8345.      1    function pre_b_eqeq(thing) {
+
 8346.      1
+
 8347.      1// test_cause:
+
 8348.      1// ["0==0", "pre_b_eqeq", "expected_a_b", "==", 2]
+
 8349.      1
+
 8350.      1        warn("expected_a_b", thing, "===", "==");
+
 8351.      1    }
+
 8352.    518
+
 8353.      1    function pre_b_in(thing) {
+
 8354.      1
+
 8355.      1// test_cause:
+
 8356.      1// ["aa in aa", "pre_b_in", "infix_in", "in", 4]
+
 8357.      1
+
 8358.      1        warn("infix_in", thing);
+
 8359.      1    }
+
 8360.    518
+
 8361.      1    function pre_b_instanceof(thing) {
+
 8362.      1
+
 8363.      1// test_cause:
+
 8364.      1// ["0 instanceof 0", "pre_b_instanceof", "unexpected_a", "instanceof", 3]
+
 8365.      1
+
 8366.      1        warn("unexpected_a", thing);
+
 8367.      1    }
+
 8368.    518
+
 8369.  10411    function pre_b_lparen(thing) {
+
 8370.  10411        const left = thing.expression[0];
+
 8371.  10411        let left_variable;
+
 8372.  10411        let parent;
+
 8373.  10411        if (
+
 8374.  10411            left.identifier
+
 8375.   6658            && functionage.context[left.id] === undefined
+
 8376.   3034            && typeof functionage.name === "object"
+
 8377.   2395        ) {
+
 8378.   2395            parent = functionage.name.parent;
+
 8379.   2395            if (parent) {
+
 8380.   2395                left_variable = parent.context[left.id];
+
 8381.   2395                if (
+
 8382.   2395                    left_variable !== undefined
+
 8383.   2395
+
 8384.   2395// Probably deadcode.
+
 8385.   2395// && left_variable.dead
+
 8386.   2395
+
 8387.   2395                    && left_variable.parent === parent
+
 8388.   2395                    && left_variable.calls !== undefined
+
 8389.   2395                    && left_variable.calls[functionage.name.id] !== undefined
+
 8390.   2395                ) {
+
 8391.   2395                    left_variable.dead = false;
+
 8392.   2395                }
+
 8393.   2395            }
+
 8394.   2395        }
+
 8395.  10411    }
+
 8396.    518
+
 8397.      1    function pre_b_noteq(thing) {
+
 8398.      1
+
 8399.      1// test_cause:
+
 8400.      1// ["0!=0", "pre_b_noteq", "expected_a_b", "!=", 2]
+
 8401.      1
+
 8402.      1        warn("expected_a_b", thing, "!==", "!=");
+
 8403.      1    }
+
 8404.    518
+
 8405.    974    function pre_b_or(thing) {
+
 8406.   1948        thing.expression.forEach(function (thang) {
+
 8407.    177            if (thang.id === "&&" && !thang.wrapped) {
+
 8408.      1
+
 8409.      1// test_cause:
+
 8410.      1// ["0&&0||0", "pre_b_or", "and", "&&", 2]
+
 8411.      1
+
 8412.      1                warn("and", thang);
+
 8413.      1            }
+
 8414.   1948        });
+
 8415.    974    }
+
 8416.    518
+
 8417.      8    function pre_s_for(thing) {
+
 8418.      8        let the_variable;
+
 8419.      2        if (thing.name !== undefined) {
+
 8420.      2            thing.name.dead = false;
+
 8421.      2            the_variable = lookup(thing.name);
+
 8422.      2            if (the_variable !== undefined) {
+
 8423.      2                if (the_variable.init && the_variable.readonly) {
+
 8424.      2
+
 8425.      2// test_cause:
+
 8426.      2// ["const aa=0;for(aa in aa){}", "pre_s_for", "bad_assignment_a", "aa", 16]
+
 8427.      2
+
 8428.      2                    warn("bad_assignment_a", thing.name);
+
 8429.      2                }
+
 8430.      2                the_variable.init = true;
+
 8431.      2            }
+
 8432.      2        }
+
 8433.      8
+
 8434.      8// Recurse walk_statement().
+
 8435.      8
+
 8436.      8        walk_statement(thing.initial);
+
 8437.      8    }
+
 8438.    518
+
 8439.   2001    function pre_s_function(thing) {
+
 8440.   2001
+
 8441.   2001// test_cause:
+
 8442.   2001// ["()=>0", "pre_s_function", "", "", 0]
+
 8443.   2001// ["(function (){}())", "pre_s_function", "", "", 0]
+
 8444.   2001// ["function aa(){}", "pre_s_function", "", "", 0]
+
 8445.   2001
+
 8446.   2001        test_cause("");
+
 8447.   1057        if (thing.arity === "statement" && blockage.body !== true) {
+
 8448.      1
+
 8449.      1// test_cause:
+
 8450.      1// ["if(0){function aa(){}\n}", "pre_s_function", "unexpected_a", "function", 7]
+
 8451.      1
+
 8452.      1            warn("unexpected_a", thing);
+
 8453.      1        }
+
 8454.   2001        function_stack.push(functionage);
+
 8455.   2001        block_stack.push(blockage);
+
 8456.   2001        functionage = thing;
+
 8457.   2001        blockage = thing;
+
 8458.   2001        thing.live = [];
+
 8459.   1098        if (typeof thing.name === "object") {
+
 8460.   1098            thing.name.dead = false;
+
 8461.   1098            thing.name.init = true;
+
 8462.   1098        }
+
 8463.      7        if (thing.extra === "get") {
+
 8464.      7            if (thing.parameters.length !== 0) {
+
 8465.      7
+
 8466.      7// test_cause:
+
 8467.      7// ["
+
 8468.      7// /*jslint getset*/
+
 8469.      7// aa={get aa(aa){}}
+
 8470.      7// ", "pre_s_function", "bad_get", "function", 9]
+
 8471.      7
+
 8472.      7                warn("bad_get", thing);
+
 8473.      7            }
+
 8474.   1994        } else if (thing.extra === "set") {
+
 8475.   1994            if (thing.parameters.length !== 1) {
+
 8476.   1994
+
 8477.   1994// test_cause:
+
 8478.   1994// ["
+
 8479.   1994// /*jslint getset*/
+
 8480.   1994// aa={set aa(){}}
+
 8481.   1994// ", "pre_s_function", "bad_set", "function", 9]
+
 8482.   1994
+
 8483.   1994                warn("bad_set", thing);
+
 8484.   1994            }
+
 8485.   1994        }
+
 8486.   2063        thing.parameters.forEach(function (name) {
+
 8487.   2063            walk_expression(name.expression);
+
 8488.   1840            if (name.id === "{" || name.id === "[") {
+
 8489.    267                name.names.forEach(subactivate);
+
 8490.   1796            } else {
+
 8491.   1796                name.dead = false;
+
 8492.   1796                name.init = true;
+
 8493.   1796            }
+
 8494.   2063        });
+
 8495.   2001    }
+
 8496.    518
+
 8497.   5694    function pre_s_lbrace(thing) {
+
 8498.   5694        block_stack.push(blockage);
+
 8499.   5694        blockage = thing;
+
 8500.   5694        thing.live = [];
+
 8501.   5694    }
+
 8502.    518
+
 8503.     56    function pre_try(thing) {
+
 8504.     54        if (thing.catch !== undefined) {
+
 8505.     54
+
 8506.     54// Create new catch-scope for catch-parameter.
+
 8507.     54
+
 8508.     54            catch_stack.push(catchage);
+
 8509.     54            catchage = thing.catch;
+
 8510.     54        }
+
 8511.     56    }
+
 8512.    518
+
 8513.  28689    function pre_v(thing) {
+
 8514.  28689        const the_variable = lookup(thing);
+
 8515.  28611        if (the_variable !== undefined) {
+
 8516.  28611            thing.variable = the_variable;
+
 8517.  28611            the_variable.used += 1;
+
 8518.  28611        }
+
 8519.  28689    }
+
 8520.    518
+
 8521.    717    function subactivate(name) {
+
 8522.    717        name.init = true;
+
 8523.    717        name.dead = false;
+
 8524.    717        blockage.live.push(name);
+
 8525.    717    }
+
 8526.    518
+
 8527. 164534    function walk_expression(thing) {
+
 8528. 103787        if (thing) {
+
 8529. 103787            if (Array.isArray(thing)) {
+
 8530. 103787
+
 8531. 103787// test_cause:
+
 8532. 103787// ["(function(){}())", "walk_expression", "isArray", "", 0]
+
 8533. 103787// ["0&&0", "walk_expression", "isArray", "", 0]
+
 8534. 103787
+
 8535. 103787                test_cause("isArray");
+
 8536. 103787                thing.forEach(walk_expression);
+
 8537. 103787            } else {
+
 8538. 103787                preamble(thing);
+
 8539. 103787                walk_expression(thing.expression);
+
 8540. 103787
+
 8541. 103787// PR-414 - Bugfix - fix fart-body not being walked.
+
 8542. 103787
+
 8543. 103787                if (thing.id === "function" || thing.id === "=>") {
+
 8544. 103787
+
 8545. 103787// test_cause:
+
 8546. 103787// ["aa=()=>0", "walk_expression", "function", "=>", 0]
+
 8547. 103787// ["aa=function(){}", "walk_expression", "function", "function", 0]
+
 8548. 103787
+
 8549. 103787                    test_cause("function", thing.id);
+
 8550. 103787
+
 8551. 103787// Recurse walk_statement().
+
 8552. 103787
+
 8553. 103787                    walk_statement(thing.block);
+
 8554. 103787                }
+
 8555. 103787                if (
+
 8556. 103787                    thing.arity === "preassign" || thing.arity === "postassign"
+
 8557. 103787                ) {
+
 8558. 103787
+
 8559. 103787// test_cause:
+
 8560. 103787// ["aa=++aa", "walk_expression", "unexpected_a", "++", 4]
+
 8561. 103787// ["aa=--aa", "walk_expression", "unexpected_a", "--", 4]
+
 8562. 103787
+
 8563. 103787                    warn("unexpected_a", thing);
+
 8564. 103787                } else if (
+
 8565. 103787                    thing.arity === "statement"
+
 8566. 103787                    || thing.arity === "assignment"
+
 8567. 103787                ) {
+
 8568. 103787
+
 8569. 103787// test_cause:
+
 8570. 103787// ["aa[aa=0]", "walk_expression", "unexpected_statement_a", "=", 6]
+
 8571. 103787
+
 8572. 103787                    warn("unexpected_statement_a", thing);
+
 8573. 103787                }
+
 8574. 103787
+
 8575. 103787// test_cause:
+
 8576. 103787// ["aa=0", "walk_expression", "default", "", 0]
+
 8577. 103787
+
 8578. 103787                test_cause("default");
+
 8579. 103787                postamble(thing);
+
 8580. 103787            }
+
 8581. 103787        }
+
 8582. 164534    }
+
 8583.    518
+
 8584.  78303    function walk_statement(thing) {
+
 8585.  43095        if (!thing) {
+
 8586.  43095            return;
+
 8587.  43095        }
+
 8588.  35208        if (Array.isArray(thing)) {
+
 8589.   7608
+
 8590.   7608// test_cause:
+
 8591.   7608// ["+[]", "walk_statement", "isArray", "", 0]
+
 8592.   7608
+
 8593.   7608            test_cause("isArray");
+
 8594.   7608
+
 8595.   7608// Recurse walk_statement().
+
 8596.   7608
+
 8597.   7608            thing.forEach(walk_statement);
+
 8598.   7608            return;
+
 8599.  27600        }
+
 8600.  27600        preamble(thing);
+
 8601.  27600        walk_expression(thing.expression);
+
 8602.  27600        if (thing.arity === "binary") {
+
 8603.   5665            if (thing.id !== "(") {
+
 8604.   5665
+
 8605.   5665// test_cause:
+
 8606.   5665// ["0&&0", "walk_statement", "unexpected_expression_a", "&&", 2]
+
 8607.   5665
+
 8608.   5665                warn("unexpected_expression_a", thing);
+
 8609.   5665            }
+
 8610.  21935        } else if (
+
 8611.  21935            thing.arity !== "statement"
+
 8612.  21935            && thing.arity !== "assignment"
+
 8613.  21935            && thing.id !== "import"
+
 8614.  21935        ) {
+
 8615.  21935
+
 8616.  21935// test_cause:
+
 8617.  21935// ["!0", "walk_statement", "unexpected_expression_a", "!", 1]
+
 8618.  21935// ["+[]", "walk_statement", "unexpected_expression_a", "+", 1]
+
 8619.  21935// ["+new aa()", "walk_statement", "unexpected_expression_a", "+", 1]
+
 8620.  21935// ["0", "walk_statement", "unexpected_expression_a", "0", 1]
+
 8621.  21935// ["typeof 0", "walk_statement", "unexpected_expression_a", "typeof", 1]
+
 8622.  21935
+
 8623.  21935            warn("unexpected_expression_a", thing);
+
 8624.  27600        }
+
 8625.  27600
+
 8626.  27600// Recurse walk_statement().
+
 8627.  27600
+
 8628.  27600        walk_statement(thing.block);
+
 8629.  27600        walk_statement(thing.else);
+
 8630.  27600        postamble(thing);
+
 8631.  27600    }
+
 8632.    518
+
 8633.    518    postaction = action(posts);
+
 8634.    518    postamble = amble(posts);
+
 8635.    518    preaction = action(pres);
+
 8636.    518    preamble = amble(pres);
+
 8637.    518    postaction("assignment", "+=", post_a_pluseq);
+
 8638.    518    postaction("assignment", post_a);
+
 8639.    518    postaction("binary", "&&", post_b_and);
+
 8640.    518    postaction("binary", "(", post_b_lparen);
+
 8641.    518    postaction("binary", "=>", post_s_function);
+
 8642.    518    postaction("binary", "[", post_b_lbracket);
+
 8643.    518    postaction("binary", "||", post_b_or);
+
 8644.    518    postaction("binary", post_b);
+
 8645.    518    postaction("statement", "const", post_s_var);
+
 8646.    518    postaction("statement", "export", post_s_export);
+
 8647.    518    postaction("statement", "for", post_s_for);
+
 8648.    518    postaction("statement", "function", post_s_function);
+
 8649.    518    postaction("statement", "import", post_s_import);
+
 8650.    518    postaction("statement", "let", post_s_var);
+
 8651.    518    postaction("statement", "try", post_s_try);
+
 8652.    518    postaction("statement", "var", post_s_var);
+
 8653.    518    postaction("statement", "{", post_s_lbrace);
+
 8654.    518    postaction("ternary", post_t);
+
 8655.    518    postaction("unary", "+", post_u_plus);
+
 8656.    518    postaction("unary", "function", post_s_function);
+
 8657.    518    postaction("unary", post_u);
+
 8658.    518    preaction("assignment", pre_a_bitwise);
+
 8659.    518    preaction("binary", "!=", pre_b_noteq);
+
 8660.    518    preaction("binary", "(", pre_b_lparen);
+
 8661.    518    preaction("binary", "==", pre_b_eqeq);
+
 8662.    518    preaction("binary", "=>", pre_s_function);
+
 8663.    518    preaction("binary", "in", pre_b_in);
+
 8664.    518    preaction("binary", "instanceof", pre_b_instanceof);
+
 8665.    518    preaction("binary", "||", pre_b_or);
+
 8666.    518    preaction("binary", pre_b);
+
 8667.    518    preaction("binary", pre_a_bitwise);
+
 8668.    518    preaction("statement", "for", pre_s_for);
+
 8669.    518    preaction("statement", "function", pre_s_function);
+
 8670.    518    preaction("statement", "try", pre_try);
+
 8671.    518    preaction("statement", "{", pre_s_lbrace);
+
 8672.    518    preaction("unary", "function", pre_s_function);
+
 8673.    518    preaction("unary", "~", pre_a_bitwise);
+
 8674.    518    preaction("variable", pre_v);
+
 8675.    518
+
 8676.    518    walk_statement(state.token_tree);
+
 8677.    518}
+
 8678.      1
+
 8679.    208function jslint_phase5_whitage(state) {
+
 8680.    208
+
 8681.    208// PHASE 5. Check whitespace between tokens in <token_list>.
+
 8682.    208
+
 8683.    208    let {
+
 8684.    208        artifact,
+
 8685.    208        catch_list,
+
 8686.    208        function_list,
+
 8687.    208        function_stack,
+
 8688.    208        option_dict,
+
 8689.    208        test_cause,
+
 8690.    208        token_global,
+
 8691.    208        token_list,
+
 8692.    208        warn
+
 8693.    208    } = state;
+
 8694.    208    let closer = "(end)";
+
 8695.    208    let free = false;
+
 8696.    208
+
 8697.    208// free = false
+
 8698.    208
+
 8699.    208// cause:
+
 8700.    208// "()=>0"
+
 8701.    208// "aa()"
+
 8702.    208// "aa(0,0)"
+
 8703.    208// "function(){}"
+
 8704.    208
+
 8705.    208// free = true
+
 8706.    208
+
 8707.    208// cause:
+
 8708.    208// "(0)"
+
 8709.    208// "(aa)"
+
 8710.    208// "aa(0)"
+
 8711.    208// "do{}while()"
+
 8712.    208// "for(){}"
+
 8713.    208// "if(){}"
+
 8714.    208// "switch(){}"
+
 8715.    208// "while(){}"
+
 8716.    208
+
 8717.    208    let left = token_global;
+
 8718.    208    let margin = 0;
+
 8719.    208    let mode_indent = (
+
 8720.    208
+
 8721.    208// PR-330 - Allow 2-space indent.
+
 8722.    208
+
 8723.    208        option_dict.indent2
+
 8724.      5        ? 2
+
 8725.    203        : 4
+
 8726.    208    );
+
 8727.    208    let nr_comments_skipped = 0;
+
 8728.    208    let open = true;
+
 8729.    208    let opening = true;
+
 8730.    208    let right;
+
 8731.    208
+
 8732.    208// This is the set of infix operators that require a space on each side.
+
 8733.    208
+
 8734.    208    let spaceop = object_assign_from_list(empty(), [
+
 8735.    208        "!=", "!==", "%", "%=", "&", "&&", "&=", "*", "*=", "+=", "-=", "/",
+
 8736.    208        "/=", "<", "<<", "<<=", "<=", "=", "==", "===", "=>", ">", ">=", ">>",
+
 8737.    208        ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||"
+
 8738.    208    ], true);
+
 8739.    208
+
 8740.  38092    function at_margin(fit) {
+
 8741.  38092        const at = margin + fit;
+
 8742.     21        if (right.from !== at) {
+
 8743.     21            return expected_at(at);
+
 8744.     21        }
+
 8745.  38092    }
+
 8746.    208
+
 8747.   2057    function delve(the_function) {
+
 8748.  13014        Object.keys(the_function.context).forEach(function (id) {
+
 8749.  13014            const name = the_function.context[id];
+
 8750.  12979            if (id !== "ignore" && name.parent === the_function) {
+
 8751.   6286
+
 8752.   6286// test_cause:
+
 8753.   6286// ["function aa(aa) {return aa;}", "delve", "id", "", 0]
+
 8754.   6286
+
 8755.   6286                test_cause("id");
+
 8756.   6286                if (
+
 8757.   6286                    name.used === 0
+
 8758.   6286
+
 8759.   6286// Probably deadcode.
+
 8760.   6286// && (
+
 8761.   6286//     name.role !== "function"
+
 8762.   6286//     || name.parent.arity !== "unary"
+
 8763.   6286// )
+
 8764.   6286
+
 8765.   6286                    && jslint_assert(
+
 8766.   6286                        name.role !== "function",
+
 8767.   6286                        `Expected name.role !== "function".`
+
 8768.   6286                    )
+
 8769.   6286                ) {
+
 8770.   6286
+
 8771.   6286// test_cause:
+
 8772.   6286// ["/*jslint node*/\nlet aa;", "delve", "unused_a", "aa", 5]
+
 8773.   6286// ["function aa(aa){return;}", "delve", "unused_a", "aa", 13]
+
 8774.   6286// ["let aa=0;try{aa();}catch(bb){aa();}", "delve", "unused_a", "bb", 26]
+
 8775.   6286
+
 8776.   6286                    warn("unused_a", name);
+
 8777.   6286                } else if (!name.init) {
+
 8778.   6286
+
 8779.   6286// test_cause:
+
 8780.   6286// ["/*jslint node*/\nlet aa;aa();", "delve", "uninitialized_a", "aa", 5]
+
 8781.   6286
+
 8782.   6286                    warn("uninitialized_a", name);
+
 8783.   6286                }
+
 8784.   6286            }
+
 8785.  13014        });
+
 8786.   2057    }
+
 8787.    208
+
 8788.     25    function expected_at(at) {
+
 8789.     25
+
 8790.     25// Probably deadcode.
+
 8791.     25// if (right === undefined) {
+
 8792.     25//     right = token_nxt;
+
 8793.     25// }
+
 8794.     25
+
 8795.     25        jslint_assert(
+
 8796.     25            !(right === undefined),
+
 8797.     25            `Expected !(right === undefined).`
+
 8798.     25        );
+
 8799.     25        warn(
+
 8800.     25            "expected_a_at_b_c",
+
 8801.     25            right,
+
 8802.     25            artifact(right),
+
 8803.     25
+
 8804.     25// Fudge column numbers in warning message.
+
 8805.     25
+
 8806.     25            at + jslint_fudge,
+
 8807.     25            right.from + jslint_fudge
+
 8808.     25        );
+
 8809.     25    }
+
 8810.    208
+
 8811.   3095    function no_space() {
+
 8812.   3094        if (left.line === right.line) {
+
 8813.   3094
+
 8814.   3094// from:
+
 8815.   3094// if (left.line === right.line) {
+
 8816.   3094//     no_space();
+
 8817.   3094// } else {
+
 8818.   3094
+
 8819.   3094            if (left.thru !== right.from && nr_comments_skipped === 0) {
+
 8820.   3094
+
 8821.   3094// test_cause:
+
 8822.   3094// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14]
+
 8823.   3094
+
 8824.   3094                warn(
+
 8825.   3094                    "unexpected_space_a_b",
+
 8826.   3094                    right,
+
 8827.   3094                    artifact(left),
+
 8828.   3094                    artifact(right)
+
 8829.   3094                );
+
 8830.   3094            }
+
 8831.   3094        } else {
+
 8832.      1
+
 8833.      1// from:
+
 8834.      1// } else if (
+
 8835.      1//     right.arity === "binary"
+
 8836.      1//     && right.id === "("
+
 8837.      1//     && free
+
 8838.      1// ) {
+
 8839.      1//     no_space();
+
 8840.      1// } else if (
+
 8841.      1
+
 8842.      1// Probably deadcode.
+
 8843.      1// if (open) {
+
 8844.      1//     const at = (
+
 8845.      1//         free
+
 8846.      1//         ? margin
+
 8847.      1//         : margin + 8
+
 8848.      1//     );
+
 8849.      1//     if (right.from < at) {
+
 8850.      1//         expected_at(at);
+
 8851.      1//     }
+
 8852.      1// } else {
+
 8853.      1//     if (right.from !== margin + 8) {
+
 8854.      1//         expected_at(margin + 8);
+
 8855.      1//     }
+
 8856.      1// }
+
 8857.      1
+
 8858.      1            jslint_assert(open, `Expected open.`);
+
 8859.      1            jslint_assert(free, `Expected free.`);
+
 8860.      1            if (right.from < margin) {
+
 8861.      1
+
 8862.      1// test_cause:
+
 8863.      1// ["let aa = aa(\naa\n()\n);", "expected_at", "expected_a_at_b_c", "5", 1]
+
 8864.      1
+
 8865.      1                expected_at(margin);
+
 8866.      1            }
+
 8867.      1        }
+
 8868.   3095    }
+
 8869.    208
+
 8870.  96059    function no_space_only() {
+
 8871.  96059        if (
+
 8872.  96059            left.id !== "(global)"
+
 8873.  96057            && left.nr + 1 === right.nr
+
 8874.  96057            && (
+
 8875.  96057                left.line !== right.line
+
 8876.  96057                || left.thru !== right.from
+
 8877.  96057            )
+
 8878.      5        ) {
+
 8879.      5            warn(
+
 8880.      5                "unexpected_space_a_b",
+
 8881.      5                right,
+
 8882.      5                artifact(left),
+
 8883.      5                artifact(right)
+
 8884.      5            );
+
 8885.      5        }
+
 8886.  96059    }
+
 8887.    208
+
 8888.  46227    function one_space() {
+
 8889.  44324        if (left.line === right.line || !open) {
+
 8890.  44324            if (left.thru + 1 !== right.from && nr_comments_skipped === 0) {
+
 8891.  44324                warn(
+
 8892.  44324                    "expected_space_a_b",
+
 8893.  44324                    right,
+
 8894.  44324                    artifact(left),
+
 8895.  44324                    artifact(right)
+
 8896.  44324                );
+
 8897.  44324            }
+
 8898.  44324        } else {
+
 8899.   1903            if (right.from !== margin) {
+
 8900.   1903                expected_at(margin);
+
 8901.   1903            }
+
 8902.   1903        }
+
 8903.  46227    }
+
 8904.    208
+
 8905.   9344    function one_space_only() {
+
 8906.      8        if (left.line !== right.line || left.thru + 1 !== right.from) {
+
 8907.      8            warn("expected_space_a_b", right, artifact(left), artifact(right));
+
 8908.      8        }
+
 8909.   9344    }
+
 8910.    208
+
 8911.  24667    function pop() {
+
 8912.  24667        const previous = function_stack.pop();
+
 8913.  24667        closer = previous.closer;
+
 8914.  24667        free = previous.free;
+
 8915.  24667        margin = previous.margin;
+
 8916.  24667        open = previous.open;
+
 8917.  24667        opening = previous.opening;
+
 8918.  24667    }
+
 8919.    208
+
 8920.  24667    function push() {
+
 8921.  24667        function_stack.push({
+
 8922.  24667            closer,
+
 8923.  24667            free,
+
 8924.  24667            margin,
+
 8925.  24667            open,
+
 8926.  24667            opening
+
 8927.  24667        });
+
 8928.  24667    }
+
 8929.    208
+
 8930.    208// uninitialized_and_unused();
+
 8931.    208// Delve into the functions looking for variables that were not initialized
+
 8932.    208// or used. If the file imports or exports, then its global object is also
+
 8933.    208// delved.
+
 8934.    208
+
 8935.    174    if (state.mode_module === true || option_dict.node) {
+
 8936.     51        delve(token_global);
+
 8937.     51    }
+
 8938.    208    catch_list.forEach(delve);
+
 8939.    208    function_list.forEach(delve);
+
 8940.    208
+
 8941.      2    if (option_dict.white) {
+
 8942.      2        return;
+
 8943.    206    }
+
 8944.    206
+
 8945.    206// whitage();
+
 8946.    206// Go through the token list, looking at usage of whitespace.
+
 8947.    206
+
 8948. 207158    token_list.forEach(function whitage(the_token) {
+
 8949. 207158        right = the_token;
+
 8950. 195959        if (right.id === "(comment)" || right.id === "(end)") {
+
 8951.  11407            nr_comments_skipped += 1;
+
 8952. 195751        } else {
+
 8953. 195751
+
 8954. 195751// If left is an opener and right is not the closer, then push the previous
+
 8955. 195751// state. If the token following the opener is on the next line, then this is
+
 8956. 195751// an open form. If the tokens are on the same line, then it is a closed form.
+
 8957. 195751// Open form is more readable, with each item (statement, argument, parameter,
+
 8958. 195751// etc) starting on its own line. Closed form is more compact. Statement blocks
+
 8959. 195751// are always in open form.
+
 8960. 195751
+
 8961. 195751// The open and close pairs.
+
 8962. 195751
+
 8963. 195751            switch (left.id) {
+
 8964. 195751            case "${":
+
 8965. 195751            case "(":
+
 8966. 195751            case "[":
+
 8967. 195751            case "{":
+
 8968. 195751
+
 8969. 195751// test_cause:
+
 8970. 195751// ["let aa=[];", "whitage", "opener", "", 0]
+
 8971. 195751// ["let aa=`${0}`;", "whitage", "opener", "", 0]
+
 8972. 195751// ["let aa=aa();", "whitage", "opener", "", 0]
+
 8973. 195751// ["let aa={};", "whitage", "opener", "", 0]
+
 8974. 195751
+
 8975. 195751                test_cause("opener");
+
 8976. 195751
+
 8977. 195751// Probably deadcode.
+
 8978. 195751// case "${}":
+
 8979. 195751
+
 8980. 195751                jslint_assert(
+
 8981. 195751                    !(left.id + right.id === "${}"),
+
 8982. 195751                    "Expected !(left.id + right.id === \"${}\")."
+
 8983. 195751                );
+
 8984. 195751                switch (left.id + right.id) {
+
 8985. 195751                case "()":
+
 8986. 195751                case "[]":
+
 8987. 195751                case "{}":
+
 8988. 195751
+
 8989. 195751// If left and right are opener and closer, then the placement of right depends
+
 8990. 195751// on the openness. Illegal pairs (like '{]') have already been detected.
+
 8991. 195751
+
 8992. 195751// test_cause:
+
 8993. 195751// ["let aa=[];", "whitage", "opener_closer", "", 0]
+
 8994. 195751// ["let aa=aa();", "whitage", "opener_closer", "", 0]
+
 8995. 195751// ["let aa={};", "whitage", "opener_closer", "", 0]
+
 8996. 195751
+
 8997. 195751                    test_cause("opener_closer");
+
 8998. 195751                    if (left.line === right.line) {
+
 8999. 195751
+
 9000. 195751// test_cause:
+
 9001. 195751// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14]
+
 9002. 195751
+
 9003. 195751                        no_space();
+
 9004. 195751                    } else {
+
 9005. 195751
+
 9006. 195751// test_cause:
+
 9007. 195751// ["let aa = aa(\n );", "expected_at", "expected_a_at_b_c", "1", 2]
+
 9008. 195751
+
 9009. 195751                        at_margin(0);
+
 9010. 195751                    }
+
 9011. 195751                    break;
+
 9012. 195751                default:
+
 9013. 195751
+
 9014. 195751// test_cause:
+
 9015. 195751// ["let aa=(0);", "whitage", "opener_operand", "", 0]
+
 9016. 195751// ["let aa=[0];", "whitage", "opener_operand", "", 0]
+
 9017. 195751// ["let aa=`${0}`;", "whitage", "opener_operand", "", 0]
+
 9018. 195751// ["let aa=aa(0);", "whitage", "opener_operand", "", 0]
+
 9019. 195751// ["let aa={aa:0};", "whitage", "opener_operand", "", 0]
+
 9020. 195751
+
 9021. 195751                    test_cause("opener_operand");
+
 9022. 195751                    opening = left.open || (left.line !== right.line);
+
 9023. 195751                    push();
+
 9024. 195751                    switch (left.id) {
+
 9025. 195751                    case "${":
+
 9026. 195751                        closer = "}";
+
 9027. 195751                        break;
+
 9028. 195751                    case "(":
+
 9029. 195751                        closer = ")";
+
 9030. 195751                        break;
+
 9031. 195751                    case "[":
+
 9032. 195751                        closer = "]";
+
 9033. 195751                        break;
+
 9034. 195751                    case "{":
+
 9035. 195751                        closer = "}";
+
 9036. 195751                        break;
+
 9037. 195751                    }
+
 9038. 195751                    if (opening) {
+
 9039. 195751
+
 9040. 195751// test_cause:
+
 9041. 195751// ["function aa(){\nreturn;\n}", "whitage", "opening", "", 0]
+
 9042. 195751// ["let aa=(\n0\n);", "whitage", "opening", "", 0]
+
 9043. 195751// ["let aa=[\n0\n];", "whitage", "opening", "", 0]
+
 9044. 195751// ["let aa=`${\n0\n}`;", "whitage", "opening", "", 0]
+
 9045. 195751// ["let aa={\naa:0\n};", "whitage", "opening", "", 0]
+
 9046. 195751
+
 9047. 195751                        test_cause("opening");
+
 9048. 195751                        free = closer === ")" && left.free;
+
 9049. 195751                        open = true;
+
 9050. 195751                        margin += mode_indent;
+
 9051. 195751                        if (right.role === "label") {
+
 9052. 195751                            if (right.from !== 0) {
+
 9053. 195751
+
 9054. 195751// test_cause:
+
 9055. 195751// ["
+
 9056. 195751// function aa() {
+
 9057. 195751//  bb:
+
 9058. 195751//     while (aa) {
+
 9059. 195751//         if (aa) {
+
 9060. 195751//             break bb;
+
 9061. 195751//         }
+
 9062. 195751//     }
+
 9063. 195751// }
+
 9064. 195751// ", "expected_at", "expected_a_at_b_c", "1", 2]
+
 9065. 195751
+
 9066. 195751                                expected_at(0);
+
 9067. 195751                            }
+
 9068. 195751                        } else if (right.switch) {
+
 9069. 195751                            at_margin(-mode_indent);
+
 9070. 195751                        } else {
+
 9071. 195751                            at_margin(0);
+
 9072. 195751                        }
+
 9073. 195751                    } else {
+
 9074. 195751                        if (right.statement || right.role === "label") {
+
 9075. 195751
+
 9076. 195751// test_cause:
+
 9077. 195751// ["
+
 9078. 195751// function aa() {bb:
+
 9079. 195751//     while (aa) {
+
 9080. 195751//         aa();
+
 9081. 195751//     }
+
 9082. 195751// }
+
 9083. 195751// ", "whitage", "expected_line_break_a_b", "bb", 16]
+
 9084. 195751
+
 9085. 195751                            warn(
+
 9086. 195751                                "expected_line_break_a_b",
+
 9087. 195751                                right,
+
 9088. 195751                                artifact(left),
+
 9089. 195751                                artifact(right)
+
 9090. 195751                            );
+
 9091. 195751                        }
+
 9092. 195751
+
 9093. 195751// test_cause:
+
 9094. 195751// ["let aa=(0);", "whitage", "not_free", "", 0]
+
 9095. 195751// ["let aa=[0];", "whitage", "not_free", "", 0]
+
 9096. 195751// ["let aa=`${0}`;", "whitage", "not_free", "", 0]
+
 9097. 195751// ["let aa={aa:0};", "whitage", "not_free", "", 0]
+
 9098. 195751
+
 9099. 195751                        test_cause("not_free");
+
 9100. 195751                        free = false;
+
 9101. 195751                        open = false;
+
 9102. 195751
+
 9103. 195751// test_cause:
+
 9104. 195751// ["let aa = ( 0 );", "no_space_only", "unexpected_space_a_b", "0", 12]
+
 9105. 195751
+
 9106. 195751                        no_space_only();
+
 9107. 195751                    }
+
 9108. 195751                }
+
 9109. 195751                break;
+
 9110. 195751            default:
+
 9111. 195751                if (right.statement === true) {
+
 9112. 195751                    if (left.id === "else") {
+
 9113. 195751
+
 9114. 195751// test_cause:
+
 9115. 195751// ["
+
 9116. 195751// let aa = 0;
+
 9117. 195751// if (aa) {
+
 9118. 195751//     aa();
+
 9119. 195751// } else  if (aa) {
+
 9120. 195751//     aa();
+
 9121. 195751// }
+
 9122. 195751// ", "one_space_only", "expected_space_a_b", "if", 9]
+
 9123. 195751
+
 9124. 195751                        one_space_only();
+
 9125. 195751                    } else {
+
 9126. 195751
+
 9127. 195751// test_cause:
+
 9128. 195751// [" let aa = 0;", "expected_at", "expected_a_at_b_c", "1", 2]
+
 9129. 195751
+
 9130. 195751                        at_margin(0);
+
 9131. 195751                        open = false;
+
 9132. 195751                    }
+
 9133. 195751
+
 9134. 195751// If right is a closer, then pop the previous state.
+
 9135. 195751
+
 9136. 195751                } else if (right.id === closer) {
+
 9137. 195751                    pop();
+
 9138. 195751                    if (opening && right.id !== ";") {
+
 9139. 195751                        at_margin(0);
+
 9140. 195751                    } else {
+
 9141. 195751                        no_space_only();
+
 9142. 195751                    }
+
 9143. 195751                } else {
+
 9144. 195751
+
 9145. 195751// Left is not an opener, and right is not a closer.
+
 9146. 195751// The nature of left and right will determine the space between them.
+
 9147. 195751
+
 9148. 195751// If left is ',' or ';' or right is a statement then if open,
+
 9149. 195751// right must go at the margin, or if closed, a space between.
+
 9150. 195751
+
 9151. 195751                    if (right.switch) {
+
 9152. 195751                        at_margin(-mode_indent);
+
 9153. 195751                    } else if (right.role === "label") {
+
 9154. 195751                        if (right.from !== 0) {
+
 9155. 195751
+
 9156. 195751// test_cause:
+
 9157. 195751// ["
+
 9158. 195751// function aa() {
+
 9159. 195751//     aa();cc:
+
 9160. 195751//     while (aa) {
+
 9161. 195751//         if (aa) {
+
 9162. 195751//             break cc;
+
 9163. 195751//         }
+
 9164. 195751//     }
+
 9165. 195751// }
+
 9166. 195751// ", "expected_at", "expected_a_at_b_c", "1", 10]
+
 9167. 195751
+
 9168. 195751                            expected_at(0);
+
 9169. 195751                        }
+
 9170. 195751                    } else if (left.id === ",") {
+
 9171. 195751                        if (!open || (
+
 9172. 195751                            (free || closer === "]")
+
 9173. 195751                            && left.line === right.line
+
 9174. 195751                        )) {
+
 9175. 195751
+
 9176. 195751// test_cause:
+
 9177. 195751// ["let {aa,bb} = 0;", "one_space", "expected_space_a_b", "bb", 9]
+
 9178. 195751
+
 9179. 195751                            one_space();
+
 9180. 195751                        } else {
+
 9181. 195751
+
 9182. 195751// test_cause:
+
 9183. 195751// ["
+
 9184. 195751// function aa() {
+
 9185. 195751//     aa(
+
 9186. 195751//         0,0
+
 9187. 195751//     );
+
 9188. 195751// }
+
 9189. 195751// ", "expected_at", "expected_a_at_b_c", "9", 11]
+
 9190. 195751
+
 9191. 195751                            at_margin(0);
+
 9192. 195751                        }
+
 9193. 195751
+
 9194. 195751// If right is a ternary operator, line it up on the margin.
+
 9195. 195751
+
 9196. 195751                    } else if (right.arity === "ternary") {
+
 9197. 195751                        if (open) {
+
 9198. 195751
+
 9199. 195751// test_cause:
+
 9200. 195751// ["
+
 9201. 195751// let aa = (
+
 9202. 195751//     aa
+
 9203. 195751//     ? 0
+
 9204. 195751// : 1
+
 9205. 195751// );
+
 9206. 195751// ", "expected_at", "expected_a_at_b_c", "5", 1]
+
 9207. 195751
+
 9208. 195751                            at_margin(0);
+
 9209. 195751                        } else {
+
 9210. 195751
+
 9211. 195751// test_cause:
+
 9212. 195751// ["let aa = (aa ? 0 : 1);", "whitage", "use_open", "?", 14]
+
 9213. 195751
+
 9214. 195751                            warn("use_open", right);
+
 9215. 195751                        }
+
 9216. 195751                    } else if (
+
 9217. 195751                        right.arity === "binary"
+
 9218. 195751                        && right.id === "("
+
 9219. 195751                        && free
+
 9220. 195751                    ) {
+
 9221. 195751
+
 9222. 195751// test_cause:
+
 9223. 195751// ["let aa = aa(\naa ()\n);", "no_space", "unexpected_space_a_b", "(", 4]
+
 9224. 195751
+
 9225. 195751                        no_space();
+
 9226. 195751                    } else if (
+
 9227. 195751                        left.id === "."
+
 9228. 195751                        || left.id === "?."
+
 9229. 195751                        || left.id === "..."
+
 9230. 195751                        || right.id === ","
+
 9231. 195751                        || right.id === ";"
+
 9232. 195751                        || right.id === ":"
+
 9233. 195751                        || (
+
 9234. 195751                            right.arity === "binary"
+
 9235. 195751                            && (right.id === "(" || right.id === "[")
+
 9236. 195751                        )
+
 9237. 195751                        || (
+
 9238. 195751                            right.arity === "function"
+
 9239. 195751                            && left.id !== "function"
+
 9240. 195751                        )
+
 9241. 195751                        || (right.id === "." || right.id === "?.")
+
 9242. 195751                    ) {
+
 9243. 195751
+
 9244. 195751// test_cause:
+
 9245. 195751// ["let aa = 0 ;", "no_space_only", "unexpected_space_a_b", ";", 12]
+
 9246. 195751// ["let aa = aa ?.aa;", "no_space_only", "unexpected_space_a_b", "?.", 13]
+
 9247. 195751
+
 9248. 195751                        no_space_only();
+
 9249. 195751                    } else if (left.id === ";") {
+
 9250. 195751
+
 9251. 195751// test_cause:
+
 9252. 195751// ["
+
 9253. 195751// /*jslint for*/
+
 9254. 195751// function aa() {
+
 9255. 195751//     for (
+
 9256. 195751//         aa();
+
 9257. 195751// aa;
+
 9258. 195751//         aa()
+
 9259. 195751//     ) {
+
 9260. 195751//         aa();
+
 9261. 195751//     }
+
 9262. 195751// }
+
 9263. 195751// ", "expected_at", "expected_a_at_b_c", "9", 1]
+
 9264. 195751
+
 9265. 195751                        if (open) {
+
 9266. 195751                            at_margin(0);
+
 9267. 195751                        }
+
 9268. 195751                    } else if (
+
 9269. 195751                        left.arity === "ternary"
+
 9270. 195751                        || left.id === "case"
+
 9271. 195751                        || left.id === "catch"
+
 9272. 195751                        || left.id === "else"
+
 9273. 195751                        || left.id === "finally"
+
 9274. 195751                        || left.id === "while"
+
 9275. 195751                        || left.id === "await"
+
 9276. 195751                        || right.id === "catch"
+
 9277. 195751                        || right.id === "else"
+
 9278. 195751                        || right.id === "finally"
+
 9279. 195751                        || (right.id === "while" && !right.statement)
+
 9280. 195751                        || (left.id === ")" && right.id === "{")
+
 9281. 195751                    ) {
+
 9282. 195751
+
 9283. 195751// test_cause:
+
 9284. 195751// ["
+
 9285. 195751// function aa() {
+
 9286. 195751//     do {
+
 9287. 195751//         aa();
+
 9288. 195751//     } while(aa());
+
 9289. 195751// }
+
 9290. 195751// ", "one_space_only", "expected_space_a_b", "(", 12]
+
 9291. 195751
+
 9292. 195751                        one_space_only();
+
 9293. 195751                    } else if (
+
 9294. 195751
+
 9295. 195751// There is a space between left and right.
+
 9296. 195751
+
 9297. 195751                        spaceop[left.id] === true
+
 9298. 195751                        || spaceop[right.id] === true
+
 9299. 195751                        || (
+
 9300. 195751                            left.arity === "binary"
+
 9301. 195751                            && (left.id === "+" || left.id === "-")
+
 9302. 195751                        )
+
 9303. 195751                        || (
+
 9304. 195751                            right.arity === "binary"
+
 9305. 195751                            && (right.id === "+" || right.id === "-")
+
 9306. 195751                        )
+
 9307. 195751                        || left.id === "function"
+
 9308. 195751                        || left.id === ":"
+
 9309. 195751                        || left.id === "async"
+
 9310. 195751                        || (
+
 9311. 195751                            (
+
 9312. 195751                                left.identifier
+
 9313. 195751                                || left.id === "(string)"
+
 9314. 195751                                || left.id === "(number)"
+
 9315. 195751                            )
+
 9316. 195751                            && (
+
 9317. 195751                                right.identifier
+
 9318. 195751                                || right.id === "(string)"
+
 9319. 195751                                || right.id === "(number)"
+
 9320. 195751                            )
+
 9321. 195751                        )
+
 9322. 195751                        || (left.arity === "statement" && right.id !== ";")
+
 9323. 195751                    ) {
+
 9324. 195751
+
 9325. 195751// test_cause:
+
 9326. 195751// ["let aa=0;", "one_space", "expected_space_a_b", "0", 8]
+
 9327. 195751// ["let aa={\naa:\n0\n};", "expected_at", "expected_a_at_b_c", "5", 1]
+
 9328. 195751
+
 9329. 195751                        one_space();
+
 9330. 195751                    } else if (left.arity === "unary" && left.id !== "`") {
+
 9331. 195751                        no_space_only();
+
 9332. 195751                    }
+
 9333. 195751                }
+
 9334. 195751            }
+
 9335. 195751            nr_comments_skipped = 0;
+
 9336. 195751            delete left.calls;
+
 9337. 195751            delete left.dead;
+
 9338. 195751            delete left.free;
+
 9339. 195751            delete left.init;
+
 9340. 195751            delete left.open;
+
 9341. 195751            delete left.used;
+
 9342. 195751            left = right;
+
 9343. 195751        }
+
 9344. 207158    });
+
 9345.    206}
+
 9346.      1
+
 9347.      6function jslint_report({
+
 9348.      6    exports,
+
 9349.      6    froms,
+
 9350.      6    functions,
+
 9351.      6    global,
+
 9352.      6    json,
+
 9353.      6    module,
+
 9354.      6    property,
+
 9355.      6    stop,
+
 9356.      6    warnings
+
 9357.      6}) {
+
 9358.      6
+
 9359.      6// This function will create human-readable, html-report
+
 9360.      6// for warnings, properties, and functions from jslint-result-object.
+
 9361.      6//
+
 9362.      6// Example usage:
+
 9363.      6//  let result = jslint("console.log('hello world')");
+
 9364.      6//  let html = jslint_report(result);
+
 9365.      6
+
 9366.      6    let html = "";
+
 9367.      6    let length_80 = 1111;
+
 9368.      6
+
 9369.    328    function address(line = 1, column = 1) {
+
 9370.    328
+
 9371.    328// This function will create HTML address element from <line> and <column>
+
 9372.    328
+
 9373.    328        return `<address>${Number(line)}: ${Number(column)}</address>`;
+
 9374.    328
+
 9375.    328    }
+
 9376.      6
+
 9377.   2256    function detail(title, list) {
+
 9378.   2256        return (
+
 9379.   2256            (Array.isArray(list) && list.length > 0)
+
 9380.    781            ? (
+
 9381.    781
+
 9382.    781// Google Lighthouse Accessibility - <dl>'s do not contain only properly-ordered
+
 9383.    781// <dt> and <dd> groups, <script>, <template> or <div> elements.
+
 9384.    781
+
 9385.    781                "<dl>"
+
 9386.    781                + "<dt>" + htmlEscape(title) + "</dt>"
+
 9387.    781                + "<dd>" + list.join(", ") + "</dd>"
+
 9388.    781                + "</dl>"
+
 9389.    781            )
+
 9390.   1475            : ""
+
 9391.   2256        );
+
 9392.   2256    }
+
 9393.      6
+
 9394.      6    html += String(`
+
 9395.      6<style class="JSLINT_REPORT_STYLE">
+
 9396.      6/* jslint utility2:true */
+
 9397.      6/*csslint box-model: false, ids:false */
+
 9398.      6/*csslint ignore:start*/
+
 9399.      6@font-face {
+
 9400.      6    font-display: swap;
+
 9401.      6    font-family: "Daley";
+
 9402.      6    src: url(
+
 9403.      6"data:font/woff2;base64,d09GMgABAAAAABy4AA4AAAAAThwAABxiAAEAAAAAAAAAAAAA\
+
 9404.      6AAAAAAAAAAAAAAAABmAAgiQINAmcDBEICuc41DEBNgIkA4R2C4I+AAQgBYkuByAMgScfYUIF\
+
 9405.      67NgjsHGAbcDVFkXZ5Jwd+P96IGPc9rl9ETBEaCzCJkvY2UpziRZ7zftZWk8052U9+NqX6vXL\
+
 9406.      6KDflSQnlJ0bP+QnPQAy744n9mup6H9PaCDFwM5zjf8exB89bZ1cdrYOP0NgnuRDRWlk9u/fE\
+
 9407.      6llkxqmfH8lmRQ/DAmER9opk9wR6suc1LvTiXNEe1vbhUCH2USgnEwH3vUm05JQqejGvZvOtz\
+
 9408.      67sIKEGgLdDNl/IrfqWVZG/wr42ekomEm91VA1p4LhHBuFzHF8//u7vvbREHMQqGtNLmiOOD/\
+
 9409.      6X7WWiwqyCE98qt0jk5JJmgR5WJJElBmzRb1F7a66MmSLTNWZ2XSHfKBSKHoVteSEJ6EOdvVw\
+
 9410.      6fNZOtXKDe39jXdRlkmMnOWIOFBgeEK/b0mFsgffnPyyAitNyutKky7J8a8MSEkAKGLgfptnS\
+
 9411.      6/gDRSo7vwdNUmQDB7oP6pK7QF5d9SrY8M/tkrXcurSIQAmX7tz7pd33LIB7GQkBQ/k81s/0D\
+
 9412.      6gpt4gbw7x0Cn/PocitK5KIGPGQIzQzAMuCeC2ERAidx9TySVqX06goT0SFFOOV9Kuxdi5Rg7\
+
 9413.      6l6n3c+nKRemidOm2dtFV1jXMk4rP2m6RJ8xEdPYONLTbeMgaJ1nwS2W4su3MHwqkkvJ2PdDU\
+
 9414.      6r7pgAnVRt4Kh789FXlD0r3p6jUtNO19O1s74U9pnIxqFpw+mBgF+8y30PAyw1dzlknLLVcSB\
+
 9415.      6J2OuCr9eV5Efew6cOGd47ZEfhrW7HXI+FBNFvWgWnugUU4UvlrV63niv2ZPeKu8M76y/HQaG\
+
 9416.      6weU+4Gzp+Y+cfb9R9djDWcd1Svr1xG7l+j/yf3eM996548qlC+dOzOqQ8//Lo0uaSEQCFuLD\
+
 9417.      6/bXyWhJ6aPmyaRonVPxGABFL4/0slcKI6f+PmT0M+QRsplmWnv4F49VT+JsPifoa6aeyr2Hz\
+
 9418.      6EeLdP1FEOV/ZN+c9sAuoNh0BRS0xgCCc9wME5s0HOKj/wc0fWYsTbFQpsZL5SayJPkL45kDo\
+
 9419.      6DcJJ10MvD0ZSq7FEIr1TfqZ7NC6s75zSp8viaNO5/PczYCV9z6NTa0KBdnGBg6kbdeBkRLfU\
+
 9420.      6qRd3D9Pqw5jWCc5WM/i95OE8731MBd1u2EmsXIa5dCvavY32U1Ytza4nfbERg6OVRZka7jq0\
+
 9421.      6r2FcXNDyEhXheaHtaU1o1kvO9MuBOHqugLUEzN+4jznu0oK9wZPur1lWVFfxl8lZzn2XwcjZ\
+
 9422.      6Csg/RJy0mAMMmgnqXS8ELhOCRUSLzvsM5gAPudEh2lVoRxGgyUVnArZMruE0YS1PqFMD3upb\
+
 9423.      6jVoecGj1KpWl6/ZysuyzkG4SGA4bps6FBQSg4e4IxNUgdmosmoDn0TpIex/s1BFau6GBNO4z\
+
 9424.      6cvWXypm4hEg5k3llelySFqNmUtRZ3PHBA7p4MBX1nK4awwAV6kWzIVbUA67A55QKYbMsgVaH\
+
 9425.      6c1ZxKuZ0DCyqxCsJjLyCEY36gf0wjAu3t0zemc87PmBCJbU9Lso0YAaYJUx8wsR02hYz5hGy\
+
 9426.      6Js0+A4uHGZgfuf5SOR9iBQuLhpOExaIFrHj6JlXanebzGHp2ELDh6av09PVE1fmdsj2oHRWs\
+
 9427.      6fOtYrV6wRCyx7XogHqvpnZiPBBdNcL6kIoS9UI/DOIlumlveSgv9oqMBYp7WZ2fGxAXmZmaG\
+
 9428.      6OCyJG6+wAszZFCQw/EXVjx+YA2uVyN6bhNWiZhgtYjAwR5U/7uV1scghiTGiAPZbA5ZqHw5u\
+
 9429.      6Yu1cDjhRwREBFyq2wa0R8GgceDUKPo2BX+MhoAkQ1EQIaZqVHMwH3xM+P32TTA34tmOMNZ4n\
+
 9430.      6mHXqn49fmE3qX1+wMNYoYetOsPx6wxKzkURImERJIjGSSJwkkiCJJEkiKZJImiSSIYlkSYqK\
+
 9431.      6UBu0UOopuLMmasiJW0PMFOO2UgbDif2NaQUqkBbyaGjdTUvuyamEQwCq9DWsxsG9qPt+VFqV\
+
 9432.      66cIsXcyWujWIEtNFdeia9ssNrJUpe3IDMPQZOReC8x+qvt17drPWdcHeL0gTarWwoQ6o828o\
+
 9433.      60EJzrA20yZsgVyVHdlCJOF3NaACxHbP38TA+MGx3St9c5t2CxbGtunB4J9AF4Px2rSr1wyK9\
+
 9434.      69KoXBR13vw9Fk9qhTX0ivZoanrvhLa5oiJO8cqR0lX7QtJ2c1a62V3PMtutaaoit+hxtXuC5\
+
 9435.      6ZUXJePSR6btQlt5g7PqPQ822g7F8D123pc4kaGXz7qYztJxDXCxJr7foKqxwy4rikI/NvINx\
+
 9436.      6bkArRTTnnMWy6YA8J39LfTweThKsqlt7Mz078NDSOPOGgtGTpeG8ZRBF+xKBjdSoNe8gE6uC\
+
 9437.      6ucOH98jE4+cv1JEjI555TFjYj4+0KdFlojzJGWp2wc1tCaYGSeO8dBfT0u3lpDY3tazzu4wn\
+
 9438.      6lF9wzy2nK+sTr/qEVdANoZ0ToBdD+MY4ewOHNnkXPBvKVXLSbEGfGVD0Nzr0Fs3HID3Y1Kqx\
+
 9439.      6mzJ6p1C1/R6Xneyw/q9YRDLahbnsI1u76XzMLPqsK0yvQDeQ4TMR41709sIssmEgs0XH1lcj\
+
 9440.      67HLnUG6u2Xpy5vbOowIGqrR6cwF0TLGI5PF7pkbzIVYQU0sIaoNgul3LGAH2B1nREFYXUMia\
+
 9441.      6prCeAzggGxrC5gIK2dK0exs/AIRKdlIIuxkUspdSsU+rqXagqXaooXakqTiWS/a0E7zA6QIK\
+
 9442.      6OdMUznMAh+RCQ7hcQCFXmspr3ciuds/6gPsZFPIgpfJhwUIepRAeZ1DIk5Tue4oKfSfKZyNV\
+
 9443.      6pKU/J7J4Abx1EMV5mXSRDl6lMfU6jfBmBww4k7f6gLzTB+J9od/kA/uGj2mET2nkn7+zQ/JF\
+
 9444.      6H5Kv+pB804fkOyvwI43wM438V5sdkd/6iPzRR+SvPiL/WIH/aYRxGqMb/Oqe3d54+LWR1vr2\
+
 9445.      6knnnc467iD247eXBA3YYBAiFfierClXz/8jyL3Qh/zP8y+Y/1eN8jq+SKZAML/lIidjwZ8N4\
+
 9446.      6aLthvhxGUkGPo+p0eHKZ0sT5FsqJcQCy9UhHIvcJFIlIvANTPFWUTUhSiVdsNRnvwEQxm5uc\
+
 9447.      6ksjdv5evJfpOgI6c7juH8pnG2RKwlXaDYe9g8rMwYfML3A2SMWeBDopJJsmS5dUE2KttnmQa\
+
 9448.      6JZlMspvEpJioiEDFNpPUTbwqG3Zjhx2VCeJrIf60s2mI6blZMZVyAyYzI+1a2Y0AIqcbLUgR\
+
 9449.      66iRbNtnp82GrImXW0YbcbczDgqQDWNdTenvtTAlT9iPHenluV+d3eed1/5MjMBrX2LgrK2ml\
+
 9450.      6FuoDOz036n/kaHbAeszR3jHoI4NWB3lusTfuVgkMUkLQaH0F6+pSCS11fXRwT421vs9s7axd\
+
 9451.      6nvtF7/eeIeq9s1aCLsLWdh+w7sXz3IYdEsSQ0LVsebmES/vXDU9k653W4MiNq8bMj5nLioCY\
+
 9452.      6edGgOT6tmYwqiOW1ugiEmew6iwjvvYb3SaeZJb7XNufOo9oH8FTneWGL+BLiclptpnhPwcui\
+
 9453.      6T+rzcF34+ycsL7p3AveuML9i9h13beylyg8CzEz5HppadqmmDxKrAquG9L3ztedRoWxEsAYt\
+
 9454.      6OM1Eu0G0gyTHkxf7cSkHJQRbA4xmlqHWkv1C0KhFhBq1z81Wq1CZoWic8TJ570WfSj5qsM+Q\
+
 9455.      6nl4k3H5+P+P3zlv9ltQrzv41qyiSwV/gOadyQBchsmwDGu/JI8tXflE8jqUVA0Zw0SKbdDC9\
+
 9456.      6c4FR+fak95SdF7uqpoRe9z6YRv+85YUzF4qJy6Q8GOVNwUn/ymyjNNbmcuVfXYeH2osLdCte\
+
 9457.      6ebmZRyUfQQZA1BSCLK4PWA/z1kBvDZm0t+i3or1LkMD6en95pGG0UOa8ZJXgS9TdEA1I2mZw\
+
 9458.      61JOWWxDu0NEh4rM19H55rvueMBUZV1RjkmB3oxkXhAckpa5gzzxUDA2VLOrWFAXx+4gmfU17\
+
 9459.      65o3v9H7EYdvGFuM+tDB3TA4ITjVUKduO/R4bXRAcPXZusWkN+t59sFz7Hyi0FkSdzrHXQVFq\
+
 9460.      6b8c9k9eLRjVlBbNvt4172CanYg/F3Rket1zCTc77UZ61Gq/Be9J8hrKrxbDZMEotf5o8zHDc\
+
 9461.      6/UJaEtdhgwHEcBEQKM+6NBWIewLmI1sHuWYAedZCw8U1hJfSWcld+2tv3jpCFc5FnosLWC0+\
+
 9462.      6DnAlnOXUXLoMXrmCVerNQkZHvRm8YtE12vG8+N/vOnPcu3vM1uOnzE3u3VP2ppmLZawm2NuO\
+
 9463.      6tPa7xwHFCgVKpox5PVrOmaDHrThk1tX864a2+/qhJd3nCFRQ+bfUKI4O+Wgk5byB3saMcUfV\
+
 9464.      6C8G137yMd16zRm3ZSq+UrDlk5ha3TiAj0b74prWO/vYG+RC+ronP1/McDtefBtY1XhZE0PIB\
+
 9465.      6wTe7CBTte2U6KPbYd5GffApQlDGssdfmxYGSlnHrQt7++KEwUg3ikkoQyKPixgUDB6Lozjv5\
+
 9466.      6vM5PBnllt+UzMnP6DStFsOfossbXOefWhQApACCNpkTYGAONIowDfndqDKRFuzn685nthZPe\
+
 9467.      6vEL7TIWkXAG2yxKBH90+yMzuRzWn3KMmyKGwZWnIErlJ9Vwt8OtR6+4TKad5y9+ViBtTzVG+\
+
 9468.      6tpv/xiLrcGKJRtYvCUlGeL4Dwy1jo1CSQe0X71EXK1YG44ztxTONjIslL8SwY0Cki0k0vsX/\
+
 9469.      6/xz7CxkAc9dEdJZhMy/JCGzD2FAGtUcag0tc2e2miJkp477V2qTKB+nFnDl/noxpXJ+yqVdO\
+
 9470.      6wNjbplmeiuburg9ii1Z1zwtG8QjcJAiVPSOV2mHzq1Qt7p2+YCcIKPmFusE5O+m8s+Wd8o3t\
+
 9471.      6qO1b1IZF8N0tx6RQnZ9Ux3gXijHlolixst6vhJV6ao0ZFzSprfAc3x0MLvxU0OsmXEVddMVK\
+
 9472.      629CC6mPgPtXTUW7tVnZxwm0DTJwNOeVRV4axMSPlpgyv1Va1MQhQqWwUOb0s+gVLOecos4Nf\
+
 9473.      6eqlFW3fLQrlP86R4XRxrDHF0VIx6ArM5/sTWtObY6U2aosgxbN6FUa1iNTUpMThk1sUfJOC6\
+
 9474.      6s1SKo9D0g1NfiVmavyful/K7nZdDgutV1A26i7FR3r16bv3zz1cGw+ta17IX/+ripyutix3C\
+
 9475.      6xNmCxs7uiqKu9/Zjjn06tblXpJxlaLF5Od0d5W9QhQrs2u6UN0trQlCyEK2j9VYgCEIDrhQN\
+
 9476.      6c00rxg/FOfZ1N+nLV7RXDsYP+p0EzqKcuPujzuzEQsu2mFf4nYvf3Yp32rq/RYLetDLuOOTc\
+
 9477.      60WXBtgoech7AHUxAxPBg81qWCsYlzTofRU5/MpuyNoegR6mCJO5ckrLOhWbG7xo/VGwGgpRb\
+
 9478.      6+Ch+TmlcuY6Qct/2x3gxzeDUU9u+ltexrjelJ0VRR9KXH/AqrbYxHa0vmQ/kBnE5EORBK1ZH\
+
 9479.      6mTSy7A8DJMgzzqDsu9ML5J3ufkuUNDCfN5UKAjBgw2I/QlS8MQ6o/ll9dTAdoM7HYtV4cNWE\
+
 9480.      6U4pOl5Y4SIzdMbNSjXFmsBV1uXXf7GaBZZslpFGFiIpokSzxWj4hjlGl4VKJDACo7ScxQf29\
+
 9481.      6kM8gHD3nUJkwkN2aW2TGttqwOrygJ7r9nYX2tYqy7Z3TQV5ocWzUI8l871y3LsQLoTgEO76B\
+
 9482.      6Upp69hy6VKRpZvpvgfQ2T06qgXjxh38eatREitX6bzKggIYmN4sAkA3a5oeJZDK3ahQrVJwa\
+
 9483.      6AD65cEGBkS/tKH9TtybiREEWCMcKD0HH0gELtjB+KNSk7bspmpr6eb0CscIiFyZpmXu8+gxw\
+
 9484.      6O7pJNbAK2h9q2c5dMHBaoi5DylbNGdweVVdN3Jm9u6YXXlmx4nYY2vIPfSkrE/vyv9gn/Z+j\
+
 9485.      6R3HKExaUhdV0Az77YWbQPhNfjw+F0vTteSMin+wIfxyPe0DEoI4uz6o2IXwsZC7sg8MicQ3o\
+
 9486.      6wys+NJYKVW72YiVQ5LKDVwrEg2jNVM6XdNjbsHlRDcAkD08o5iWtFB2dVoydRmmDRLalE+4t\
+
 9487.      63gBbAPa7n7qXXXbTZTJXZKy5+1W0K7dgYEcIlu90ovC0C+5gxXiKtZisT14qDJ7f2ksyK59U\
+
 9488.      6r3QeHtBb24mPz7YDB3rgMTyUZ/fxM8h2i1Z21B8/VA5+9l7BKaOJZ15lWsyPv/z6CjU32ZKq\
+
 9489.      6+QFeyUywxYnUxUmcQfGc1Sp69oE2n6zFL8BXf5rc3cJMM6S97gagTT1bi7cmAV4MibkC4rz/\
+
 9490.      6icmmFtMlo5aN1Wp3uxsBfd4+9T42xmxvd79FV/hfuviBcrIaX092PrY5rle9FR4wTnDzrwj4\
+
 9491.      67frD2d0KsMcdcADQ1Yu1LECg9Wj3yOS8OhrJdQBqXqsam17vmt2wjjjouHE/EO9sGPdqt23v\
+
 9492.      6j8rL6wid6ulagtNK5p1hjRkFtUxTIaZnIXk63Zb3P0t5MQ+3vxHIFrmgAdWwiDuA67tbVIF6\
+
 9493.      6wJ53z0uhyhsfH9bgF0kPT9v2hrT3HKIBgUXIYoxsVU+uryemiUiQEwh+BfxP//qLShlumR26\
+
 9494.      6I8OqjD+x3hHDj/IrEWmvyL6ioG/atfxe+5GzIqRgfaoayWOiTk+YixO15KDO6Os3XACDjboe\
+
 9495.      6ryXXOuEmTpDsc7czk+H04Kw1PNJazW32CAURHwBldqK0/nqYHtcrtLyyTYmoD8hbcnJUfa3U\
+
 9496.      63FxWNus7uic3Qm1BzEecJW0MAz+W2CyN9FLIy+EpSy6CjkXsllZw1uBs1SxrQWM97/vnHu7m\
+
 9497.      6OtrkRl8AtBN3RDxI/fg7dZLLtDFYuCYYPMwXiO6ZIpwJ1GGydI9oUYYgnQQKDKoMTcwsjrfe\
+
 9498.      6Tcht6y18bLcpNfX41WE27447vLNzHuF+j15co5N7Py8vKUpTCoghHMEYKkM6y02lvX+9XiFg\
+
 9499.      6xBKMRNiwX69+LJb2Xa5WGqo7Rlk0cxsLVd0l2UXAW5jORg31sFMKYWXsDcRUKRDP8Q87OjiM\
+
 9500.      6dI1hNEt43netf8rOyfp+L58fq3holY9gxXwRJLY6gahgLQi4hS8w9LS+rFcJtdSCBrQLWsMs\
+
 9501.      6aDg/n8/P8/N+fcyoLepYr3W/CIUT7HsRQTtkduddbVfbo6Twt6fyJVPRrUGqRkWp3rdry65v\
+
 9502.      6sPYInyq1mPHrQDrqGJYI/LzA/QAzAXLnx+lu9uxHTEka9xgWgRvqEioskh+UWgD4nDvTAxaz\
+
 9503.      63v9BqqmFuQwy1wSXye1Df1NXVF7G8bUFxUE4F9axG5fm+vFQJvP8iuYjrFveB6++AqmJTQJ0\
+
 9504.      69GHjbPhzdSzkZGxokQzONVs0R0FCPJz1hJKbvDKsaj9hT0vp/gH5oiT8pAbWsBChwAbxHgDd\
+
 9505.      659iJVZE3bAzPRN1RuG+MT7th+J3i6KFwVJvPvsGRDIZW4P2rVfiKjDVBM2Va+w6PgI0c5u3K\
+
 9506.      6O7MrWryPhXFFdBoAwi2JCaW9sZ3fTagQ4Tld6u4djwcWzeCdiYoeNbfalsRYo740afYQ1Rid\
+
 9507.      6Bp/E9mbcTemEjoWWXIU7I5nK5H/BEqmZnPMyhDV234BTLQKCe6nhU+frwQo1gNFWf+eQGN62\
+
 9508.      6aeF7BuzaN/94W2xlKd8t8KMA/3uoxymFt19OlCjYZeaMWbTKM9Yog9zDhptYMOzIQAoO7kn6\
+
 9509.      6nTao8CxjrRRgjKe7mKa+tzuufhAAZtgjA92THkulWvIzEi0++j1DvXMnupDUS8aVusWain+c\
+
 9510.      6CcvmR5orC+RcJs3wVahLYyEcqbvAS2e0QJ6BlU36R/IEd9Aol9q+M+UGvlo8EyRzISvqusNS\
+
 9511.      67ePQ6cQzG1s725db4jNYNHAfF3KFG8wHqDwZDpWDsJ5qRLXR1ulFx85GhkypPubYaCiOQ5DR\
+
 9512.      6PQUiNpgk4fLJHenSMLMiswXsqW4Cpln1rFoHzpOoBbuZIixmVyhKajeqlFmp8zNAEsbEJz0g\
+
 9513.      6X0qlQuykZhf82pkhq2hWtCtYUdBODn6iPTBJT5Zk8IqFxqfBeFKjXk/sMeumhT8muOtq2Bgn\
+
 9514.      6dR4fj6RoOi0zI25kajAXlDZhUhS39jipk39h/69AfDPBLmOxhDj7Lg/WUTbOwJiJ3p7WtOpm\
+
 9515.      6ypARmhorQifINNm1CNS99GfDcLbD8sn8Fvlmvn7CmW65Pdmu6bKtuE0tn7NglIX1e/JAJP+G\
+
 9516.      6gB3At7cSOp92rl0lp0pp0xVb5YaQedwGgcJA1pT4cy24lS+jvzDw86YTfb2igJm5MysHmejW\
+
 9517.      6ZTGXpoAoLBLucUGEz/DwbjqOdzGAl5jy5VoCQws5zNYl4SVt030aZulYMgpDBPZd+kL0wV+w\
+
 9518.      6nob2LPRDQGEbdUoeFm4fEKio9c/ferVlpSO8Bx7OFZyHip1PIizvoqFe02kpmS17TvIOty42\
+
 9519.      6+Q0QaCnOpeLsPwwo+vixIeIeUjucUsKejVlez35qyuC0mm5pJJJLEVP2JAe/LTOwUUfKJkNy\
+
 9520.      6lEe3Kdth241ZNQmkVcAIh6DZJBzvQov5fn3JZA0phBWdNq5iTsm5N2D8gyve3V3X2o3zF3VY\
+
 9521.      6OqEBgTIADAbC69z7vOKJjGOzHRmUUwLU66iabtIbqR6SPOHCL+fCTfvpKcB/oG2p3wRKErEJ\
+
 9522.      6v1YOfu9iaKEMLXS3ptdH8fwN2Rdww9bZ7rFa2bwrzcyux3o+hPV6bJZpb71j7lLAdzge3VX7\
+
 9523.      69uSCdz6f/FDb7+wzWnbbDSPj9R20+PybDUm/lVAsTuF0aycFQwJfPCUwcBvCGWEq6xoTIEOy\
+
 9524.      6b0bLta20+LYRYdyEceX7ypfezQKIy5OvJTAHCJy/WyOYaDVyPucMsHnZ0GCH75Cd//te1Bv2\
+
 9525.      6RkMykqYurBiNbuH3Xfuprirr4Dd453O6abAYGb5tw1d6wrBL8p1J1Sx9Lgw7yxqYn0FTrc0y\
+
 9526.      659yLlV+4zIkLfZlPFRVnanHpTyrIlpn4lGkt269+JXnIWhEQWNvuVsrt531jr+8AHkVZfQU8\
+
 9527.      68U/4AUZMuOj5iliigFrof/usmloYEI1f8erhJku75snYW7YmFmUcoC7UtG/KfJRuz6j0tWPa\
+
 9528.      656J5QA0rJHwSIhNT4GWvez19HT2lia+Pz7/+MVEWlvjY6+9P85a0y9qWkTzQ7nF0wDXpQpw3\
+
 9529.      6K4xnfK2L08b5MrxdeI+DDfVDeV2JY0Fp6KH602tj2MbxxKM8oG+wTkE/dr8jyo4Sfs/IV6uf\
+
 9530.      6+IIXpH2Nca1+WCJV5qEv193bcUELLR4iFu83xUedKy9353tn+3o01dF2bNEQ3fK9Q5tCXrCi\
+
 9531.      6La+woCuvEeYrr+PiN2+i2V/eDJck580pyra8BV5ZIZbpe3kr5vJD3pqoGsnbcl/d+ndvR23b\
+
 9532.      6K41M4dKwaAwDaMA1gZGBoQWAcYE9mYkmQOnAjkaG41FkGkIP2BAIgKvUvzhpE5JbA6lze2iL\
+
 9533.      65Nr+AwiDo2W4BStvK30dKy0JGNbzAY5akexsrV0xo5K8rY50LOTLvDyukIZNbRLKOCk18mD3\
+
 9534.      6WxmZGlsCMxNdGFYGNJnetUWyCPgo4BONEL4I9b8UeEBGYXuCdCd+DkctrqVLYXGSfE46kvAu\
+
 9535.      6+ltK5SRxQPnjUqyJXsSYs6VY6WPKfns9+IXjHhd5wQvGipgFdMwVEQ+A7a2AAS0clQwH7KHW\
+
 9536.      6SEGjhnklSVRghMtPy6gEtJRIKJeYkpQyQiequQunFOOU4BLdK1yp55olZS6npyPhMnvK7xIa\
+
 9537.      6pyNj+JctcQLXenBOCms46aMkenIx45WpXqxxVJQLz/vgpmAVa0fmDv6Pue9xVTBPfVxCUGfj\
+
 9538.      61R8uVi8Zu9nRFqk/t0gR6wmWOlzuKRqk33HpO8qQ+nbGoEZLL/0Va156SJ+u+t86/os7ic49\
+
 9539.      6/7xoEqvL+2E8VOyCTuT/7j269Zy4jUtN+g4="
+
 9540.      6    ) format("woff2");
+
 9541.      6}
+
 9542.      6.JSLINT_,
+
 9543.      6.JSLINT_ address,
+
 9544.      6.JSLINT_ button,
+
 9545.      6.JSLINT_ cite,
+
 9546.      6.JSLINT_ dd,
+
 9547.      6.JSLINT_ dfn,
+
 9548.      6.JSLINT_ dl,
+
 9549.      6.JSLINT_ dt,
+
 9550.      6.JSLINT_ fieldset,
+
 9551.      6.JSLINT_ fieldset > div,
+
 9552.      6.JSLINT_ input,
+
 9553.      6.JSLINT_ label,
+
 9554.      6.JSLINT_ legend,
+
 9555.      6.JSLINT_ ol,
+
 9556.      6.JSLINT_ samp,
+
 9557.      6.JSLINT_ style,
+
 9558.      6.JSLINT_ textarea,
+
 9559.      6.JSLINT_ ul {
+
 9560.      6    border: 0;
+
 9561.      6    box-sizing: border-box;
+
 9562.      6    margin: 0;
+
 9563.      6    padding: 0;
+
 9564.      6}
+
 9565.      6/* disable text inflation algorithm used on some smartphones and tablets */
+
 9566.      6.JSLINT_ {
+
 9567.      6    -ms-text-size-adjust: none;
+
 9568.      6    -webkit-text-size-adjust: none;
+
 9569.      6    text-size-adjust: none;
+
 9570.      6}
+
 9571.      6.JSLINT_REPORT_ div {
+
 9572.      6    box-sizing: border-box;
+
 9573.      6}
+
 9574.      6/*csslint ignore:end*/
+
 9575.      6
+
 9576.      6/* css - jslint_report - font */
+
 9577.      6.JSLINT_,
+
 9578.      6.JSLINT_ fieldset legend,
+
 9579.      6.JSLINT_ .center {
+
 9580.      6    font-family: daley, sans-serif;
+
 9581.      6    font-size: 14px;
+
 9582.      6}
+
 9583.      6.JSLINT_ fieldset textarea,
+
 9584.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS dt,
+
 9585.      6.JSLINT_ #JSLINT_REPORT_WARNINGS samp {
+
 9586.      6    font-size: 12px;
+
 9587.      6}
+
 9588.      6.JSLINT_ fieldset textarea,
+
 9589.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS > div {
+
 9590.      6    font-family: monospace;
+
 9591.      6}
+
 9592.      6.JSLINT_ fieldset > div {
+
 9593.      6    font-family: sans-serif;
+
 9594.      6}
+
 9595.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dfn {
+
 9596.      6    font-style: normal;
+
 9597.      6    font-weight: bold;
+
 9598.      6}
+
 9599.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dt {
+
 9600.      6    font-style: italic;
+
 9601.      6}
+
 9602.      6.JSLINT_ #JSLINT_REPORT_TITLE {
+
 9603.      6    font-size: 32px;
+
 9604.      6}
+
 9605.      6
+
 9606.      6/* css - jslint_report - general */
+
 9607.      6.JSLINT_ {
+
 9608.      6    background: antiquewhite;
+
 9609.      6}
+
 9610.      6.JSLINT_ fieldset {
+
 9611.      6    background: gainsboro;
+
 9612.      6    clear: both;
+
 9613.      6    margin: 16px 40px;
+
 9614.      6    width: auto;
+
 9615.      6}
+
 9616.      6.JSLINT_ fieldset address {
+
 9617.      6    float: right;
+
 9618.      6}
+
 9619.      6.JSLINT_ fieldset legend,
+
 9620.      6.JSLINT_ .center {
+
 9621.      6    text-align: center;
+
 9622.      6}
+
 9623.      6.JSLINT_ fieldset legend {
+
 9624.      6    background: darkslategray;
+
 9625.      6    color: white;
+
 9626.      6    padding: 4px 0;
+
 9627.      6    width: 100%;
+
 9628.      6}
+
 9629.      6.JSLINT_ fieldset textarea {
+
 9630.      6    padding: 4px;
+
 9631.      6    resize: none;
+
 9632.      6    white-space: pre;
+
 9633.      6    width: 100%;
+
 9634.      6}
+
 9635.      6.JSLINT_ fieldset textarea::selection {
+
 9636.      6    background: wheat;
+
 9637.      6}
+
 9638.      6.JSLINT_ fieldset > div {
+
 9639.      6    padding: 16px;
+
 9640.      6    width: 100%;
+
 9641.      6    word-wrap: break-word;
+
 9642.      6}
+
 9643.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level {
+
 9644.      6    background: cornsilk;
+
 9645.      6    padding: 8px 16px;
+
 9646.      6}
+
 9647.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dd {
+
 9648.      6    line-height: 20px;
+
 9649.      6    padding-left: 120px;
+
 9650.      6}
+
 9651.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dfn {
+
 9652.      6    display: block;
+
 9653.      6    line-height: 20px;
+
 9654.      6}
+
 9655.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dl {
+
 9656.      6    position: relative
+
 9657.      6}
+
 9658.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dt {
+
 9659.      6    line-height: 20px;
+
 9660.      6    position: absolute;
+
 9661.      6    text-align: right;
+
 9662.      6    width: 100px;
+
 9663.      6}
+
 9664.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level0 {
+
 9665.      6    background: white;
+
 9666.      6}
+
 9667.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level1 {
+
 9668.      6    /* yellow */
+
 9669.      6    background: #ffffe0;
+
 9670.      6    margin-left: 16px;
+
 9671.      6}
+
 9672.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level2 {
+
 9673.      6    /* green */
+
 9674.      6    background: #e0ffe0;
+
 9675.      6    margin-left: 32px;
+
 9676.      6}
+
 9677.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level3 {
+
 9678.      6    /* blue */
+
 9679.      6    background: #D0D0ff;
+
 9680.      6    margin-left: 48px;
+
 9681.      6}
+
 9682.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level4 {
+
 9683.      6    /* purple */
+
 9684.      6    background: #ffe0ff;
+
 9685.      6    margin-left: 64px;
+
 9686.      6}
+
 9687.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level5 {
+
 9688.      6    /* red */
+
 9689.      6    background: #ffe0e0;
+
 9690.      6    margin-left: 80px;
+
 9691.      6}
+
 9692.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level6 {
+
 9693.      6    /* orange */
+
 9694.      6    background: #ffe390;
+
 9695.      6    margin-left: 96px;
+
 9696.      6}
+
 9697.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level7 {
+
 9698.      6    /* gray */
+
 9699.      6    background: #e0e0e0;
+
 9700.      6    margin-left: 112px;
+
 9701.      6}
+
 9702.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level8 {
+
 9703.      6    margin-left: 128px;
+
 9704.      6}
+
 9705.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level9 {
+
 9706.      6    margin-left: 144px;
+
 9707.      6}
+
 9708.      6.JSLINT_ #JSLINT_REPORT_PROPERTIES {
+
 9709.      6    background: transparent;
+
 9710.      6}
+
 9711.      6.JSLINT_ #JSLINT_REPORT_PROPERTIES textarea {
+
 9712.      6    background: honeydew;
+
 9713.      6    height: 100px;
+
 9714.      6}
+
 9715.      6.JSLINT_ #JSLINT_REPORT_TITLE {
+
 9716.      6    color: darkslategray;
+
 9717.      6    padding-top: 16px;
+
 9718.      6}
+
 9719.      6.JSLINT_ #JSLINT_REPORT_WARNINGS cite {
+
 9720.      6    display: block;
+
 9721.      6    margin: 16px 0 4px 0;
+
 9722.      6    overflow-x: hidden;
+
 9723.      6    white-space: pre-line;
+
 9724.      6}
+
 9725.      6.JSLINT_ #JSLINT_REPORT_WARNINGS cite:nth-child(1) {
+
 9726.      6    margin-top: 0;
+
 9727.      6}
+
 9728.      6.JSLINT_ #JSLINT_REPORT_WARNINGS samp {
+
 9729.      6    background: lavenderblush;
+
 9730.      6    display: block;
+
 9731.      6    padding: 4px;
+
 9732.      6    white-space: pre-wrap;
+
 9733.      6}
+
 9734.      6.JSLINT_ #JSLINT_REPORT_WARNINGS > div {
+
 9735.      6    background: pink;
+
 9736.      6    max-height: 400px;
+
 9737.      6    overflow-y: auto;
+
 9738.      6}
+
 9739.      6.JSLINT_ #JSLINT_REPORT_WARNINGS > legend {
+
 9740.      6/* Google Lighthouse Accessibility - Background and foreground colors do not */
+
 9741.      6/* have a sufficient contrast ratio. */
+
 9742.      6    /* background: indianred; */
+
 9743.      6    background: #b44;
+
 9744.      6}
+
 9745.      6</style>
+
 9746.      6            `).trim() + "\n";
+
 9747.      6
+
 9748.      6// Produce the Title.
+
 9749.      6
+
 9750.      6    html += "<div class=\"center\" id=\"JSLINT_REPORT_TITLE\">\n";
+
 9751.      6    html += "JSLint Report\n";
+
 9752.      6    html += "</div>\n";
+
 9753.      6
+
 9754.      6// Produce the HTML Error Report.
+
 9755.      6// <cite>
+
 9756.      6//     <address>LINE_NUMBER</address>
+
 9757.      6//     MESSAGE
+
 9758.      6// </cite>
+
 9759.      6// <samp>EVIDENCE</samp>
+
 9760.      6
+
 9761.      6    html += "<fieldset id=\"JSLINT_REPORT_WARNINGS\">\n";
+
 9762.      6    html += "<legend>Report: Warnings (" + warnings.length + ")</legend>\n";
+
 9763.      6    html += "<div>\n";
+
 9764.      1    if (stop) {
+
 9765.      1        html += "<div class=\"center\">JSLint was unable to finish.</div>\n";
+
 9766.      1    }
+
 9767.      7    warnings.forEach(function ({
+
 9768.      7        column,
+
 9769.      7        line,
+
 9770.      7        line_source,
+
 9771.      7        message,
+
 9772.      7        stack_trace = ""
+
 9773.      7    }, ii) {
+
 9774.      7        html += (
+
 9775.      7            "<cite>"
+
 9776.      7            + address(line, column)
+
 9777.      7            + htmlEscape((ii + 1) + ". " + message)
+
 9778.      7            + "</cite>"
+
 9779.      7            + "<samp>"
+
 9780.      7            + htmlEscape(line_source.slice(0, 400) + "\n" + stack_trace)
+
 9781.      7            + "</samp>\n"
+
 9782.      7        );
+
 9783.      7    });
+
 9784.      3    if (warnings.length === 0) {
+
 9785.      3        html += "<div class=\"center\">There are no warnings.</div>\n";
+
 9786.      3    }
+
 9787.      6    html += "</div>\n";
+
 9788.      6    html += "</fieldset>\n";
+
 9789.      6
+
 9790.      6// Produce the /*property*/ directive.
+
 9791.      6
+
 9792.      6    html += "<fieldset id=\"JSLINT_REPORT_PROPERTIES\">\n";
+
 9793.      6    html += (
+
 9794.      6        "<legend>Report: Properties ("
+
 9795.      6        + Object.keys(property).length
+
 9796.      6        + ")</legend>\n"
+
 9797.      6    );
+
 9798.      6    html += "<label>\n";
+
 9799.      6    html += "<textarea readonly>";
+
 9800.      6    html += "/*property";
+
 9801.    301    Object.keys(property).sort().forEach(function (key, ii) {
+
 9802.    300        if (ii !== 0) {
+
 9803.    300            html += ",";
+
 9804.    300            length_80 += 2;
+
 9805.    300        }
+
 9806.     42        if (length_80 + key.length >= 80) {
+
 9807.     42            length_80 = 4;
+
 9808.     42            html += "\n   ";
+
 9809.     42        }
+
 9810.    301        html += " " + key;
+
 9811.    301        length_80 += key.length;
+
 9812.    301    });
+
 9813.      6    html += "\n*/\n";
+
 9814.      6    html += "</textarea>\n";
+
 9815.      6    html += "</label>\n";
+
 9816.      6    html += "</fieldset>\n";
+
 9817.      6
+
 9818.      6// Produce the HTML Function Report.
+
 9819.      6// <div class=LEVEL>
+
 9820.      6//     <address>LINE_NUMBER</address>
+
 9821.      6//     <dfn>FUNCTION_NAME_AND_SIGNATURE</dfn>
+
 9822.      6//     <dl>
+
 9823.      6//         <dt>DETAIL</dt>
+
 9824.      6//         <dd>NAMES</dd>
+
 9825.      6//     </dl>
+
 9826.      6// </div>
+
 9827.      6
+
 9828.      6    html += "<fieldset id=\"JSLINT_REPORT_FUNCTIONS\">\n";
+
 9829.      6    html += "<legend>Report: Functions (" + functions.length + ")</legend>\n";
+
 9830.      6    html += "<div>\n";
+
 9831.      2    if (json) {
+
 9832.      2
+
 9833.      2// Bugfix - fix website crashing when linting pure json-object.
+
 9834.      2// return (
+
 9835.      2
+
 9836.      2        html += (
+
 9837.      2            warnings.length === 0
+
 9838.      2            ? "<div class=\"center\">JSON: good.</div>\n"
+
 9839.      2            : "<div class=\"center\">JSON: bad.</div>\n"
+
 9840.      2        );
+
 9841.      4    } else if (functions.length === 0) {
+
 9842.      4        html += "<div class=\"center\">There are no functions.</div>\n";
+
 9843.      4    }
+
 9844.      6    exports = Object.keys(exports).sort();
+
 9845.      6    froms.sort();
+
 9846.      6    global = Object.keys(global.context).sort();
+
 9847.      6    module = (
+
 9848.      6        module
+
 9849.      1        ? "module"
+
 9850.      5        : "global"
+
 9851.      6    );
+
 9852.      3    if (global.length + froms.length + exports.length > 0) {
+
 9853.      3        if (functions.length === 0) {
+
 9854.      3            html += "<br>\n";
+
 9855.      3        }
+
 9856.      3        html += "<div class=\"level level0\">\n";
+
 9857.      3        html += detail(module, global);
+
 9858.      3        html += detail("import from", froms);
+
 9859.      3        html += detail("export", exports);
+
 9860.      3        html += "</div>\n";
+
 9861.      3    }
+
 9862.    321    functions.forEach(function (the_function) {
+
 9863.    321        let {
+
 9864.    321            context,
+
 9865.    321            from,
+
 9866.    321            id,
+
 9867.    321            level,
+
 9868.    321            line,
+
 9869.    321            name,
+
 9870.    321
+
 9871.    321// Bugfix - fix html-report from crashing if parameters is undefined.
+
 9872.    321
+
 9873.    321            parameters = [],
+
 9874.    321            signature
+
 9875.    321        } = the_function;
+
 9876.    321        let list = Object.keys(context);
+
 9877.    321        let params;
+
 9878.    321        html += (
+
 9879.    321            "<div class=\"level level" + htmlEscape(level) + "\">"
+
 9880.    321            + address(line, from + 1)
+
 9881.    321            + "<dfn>"
+
 9882.    321            + (
+
 9883.    321                id === "=>"
+
 9884.      1                ? (
+
 9885.      1                    "\u00ab" + htmlEscape(name) + "\u00bb"
+
 9886.      1                    + htmlEscape(signature)
+
 9887.      1                    + " =>"
+
 9888.      1                )
+
 9889.    320                : (
+
 9890.    320                    typeof name === "string"
+
 9891.    320                    ? "\u00ab" + htmlEscape(name) + "\u00bb"
+
 9892.    320                    : htmlEscape(name.id)
+
 9893.    320                ) + htmlEscape(signature)
+
 9894.    321            )
+
 9895.    321            + "</dfn>"
+
 9896.    321        );
+
 9897.    321        params = [];
+
 9898.    470        parameters.forEach(function extract({
+
 9899.    470            id,
+
 9900.    470            names
+
 9901.    470        }) {
+
 9902.    470            switch (id) {
+
 9903.      6            case "[":
+
 9904.     42            case "{":
+
 9905.     42
+
 9906.     42// Recurse extract().
+
 9907.     42
+
 9908.     42                names.forEach(extract);
+
 9909.     42                break;
+
 9910.      4            case "ignore":
+
 9911.      4                break;
+
 9912.    424            default:
+
 9913.    424                params.push(id);
+
 9914.    470            }
+
 9915.    470        });
+
 9916.    321        html += detail("parameter", params.sort());
+
 9917.    321        list.sort();
+
 9918.   2203        html += detail("variable", list.filter(function (id) {
+
 9919.   2203            return (
+
 9920.   2203                context[id].role === "variable"
+
 9921.   1693                && context[id].parent === the_function
+
 9922.   2203            );
+
 9923.   2203        }));
+
 9924.   2203        html += detail("exception", list.filter(function (id) {
+
 9925.   2203            return context[id].role === "exception";
+
 9926.   2203        }));
+
 9927.   2203        html += detail("closure", list.filter(function (id) {
+
 9928.   2203            return (
+
 9929.   2203                context[id].closure === true
+
 9930.   1494                && context[id].parent === the_function
+
 9931.   2203            );
+
 9932.   2203        }));
+
 9933.   2203        html += detail("outer", list.filter(function (id) {
+
 9934.   2203            return (
+
 9935.   2203                context[id].parent !== the_function
+
 9936.   1190                && context[id].parent.id !== "(global)"
+
 9937.   2203            );
+
 9938.   2203        }));
+
 9939.   2203        html += detail(module, list.filter(function (id) {
+
 9940.   2203            return context[id].parent.id === "(global)";
+
 9941.   2203        }));
+
 9942.   2203        html += detail("label", list.filter(function (id) {
+
 9943.   2203            return context[id].role === "label";
+
 9944.   2203        }));
+
 9945.    321        html += "</div>\n";
+
 9946.    321    });
+
 9947.      6    html += "</div>\n";
+
 9948.      6    html += "</fieldset>\n";
+
 9949.      6    return html;
+
 9950.      6}
+
 9951.      1
+
 9952.     10async function jstestDescribe(description, testFunction) {
+
 9953.     10
+
 9954.     10// This function will create-and-run test-group <testFunction>
+
 9955.     10// with given <description>.
+
 9956.     10
+
 9957.     10    let message;
+
 9958.     10    let result;
+
 9959.     10    let timerTimeout;
+
 9960.     10
+
 9961.     10// Init jstestTimeStart.
+
 9962.     10
+
 9963.      1    if (jstestTimeStart === undefined) {
+
 9964.      1        jstestTimeStart = jstestTimeStart || Date.now();
+
 9965.      1        process.on("exit", jstestOnExit);
+
 9966.      1    }
+
 9967.     10
+
 9968.     10// PR-457 - Wait awhile for imports to initialize.
+
 9969.     10
+
 9970.     10    await new Promise(function (resolve) {
+
 9971.     10        setTimeout(resolve);
+
 9972.     10    });
+
 9973.     10
+
 9974.     10// Init jstestItList.
+
 9975.     10
+
 9976.     10    jstestItList = [];
+
 9977.     10    testFunction();
+
 9978.     10
+
 9979.     10// Wait for jstestItList to resolve.
+
 9980.     10
+
 9981.     10    timerTimeout = setTimeout(noop, 0x7fffffff);
+
 9982.     10    result = await Promise.all(jstestItList);
+
 9983.     10    clearTimeout(timerTimeout);
+
 9984.     10
+
 9985.     10// Print test results.
+
 9986.     10
+
 9987.     10    message = (
+
 9988.     10        "\n  " + (Date.now() - jstestTimeStart) + "ms"
+
 9989.     10        + " - test describe - " + description + "\n"
+
 9990.     66        + result.map(function ([
+
 9991.     66            err, description, mode
+
 9992.     66        ]) {
+
 9993.     66            jstestItCount += 1;
+
 9994.      1            if (err) {
+
 9995.      1                jstestCountFailed += 1;
+
 9996.      1                err = (
+
 9997.      1                    "    \u001b[31m\u2718 " + jstestItCount + ". test it - "
+
 9998.      1                    + description + "\n" + err.stack + "\u001b[39m"
+
 9999.      1                );
+
10000.      1                if (mode === "pass") {
+
10001.      1                    jstestCountFailed -= 1;
+
10002.      1                    err = "";
+
10003.      1                }
+
10004.      1            }
+
10005.     66            return err || (
+
10006.     66                "    \u001b[32m\u2714 " + jstestItCount + ". test it - "
+
10007.     66                + description + "\u001b[39m"
+
10008.     66            );
+
10009.     66        }).join("\n")
+
10010.     10    );
+
10011.     10    console.error(message);
+
10012.     10}
+
10013.      1
+
10014.     66function jstestIt(description, testFunction, mode) {
+
10015.     66
+
10016.     66// This function will create-and-run test-case <testFunction>
+
10017.     66// inside current test-group with given <description>.
+
10018.     66
+
10019.     66    jstestCountTotal += 1;
+
10020.     66    jstestItList.push(new Promise(async function (resolve) {
+
10021.     66        let err;
+
10022.     66        try {
+
10023.     65            await testFunction();
+
10024.     65        } catch (errCaught) {
+
10025.      1            err = errCaught;
+
10026.      1        }
+
10027.     66        resolve([err, description, mode]);
+
10028.     66    }));
+
10029.     66}
+
10030.      1
+
10031.      2function jstestOnExit(exitCode, mode) {
+
10032.      2
+
10033.      2// This function will on process-exit, print test-report
+
10034.      2// and exit with non-zero exit-code if any test failed.
+
10035.      2
+
10036.      2    let message = (
+
10037.      2        (
+
10038.      2            (jstestCountFailed || mode === "testsFailed")
+
10039.      1            ? "\n\u001b[31m"
+
10040.      1            : "\n\u001b[32m"
+
10041.      2        )
+
10042.      2        + "  tests total  - " + jstestCountTotal + "\n"
+
10043.      2        + "  tests failed - " + jstestCountFailed + "\n"
+
10044.      2        + "\n"
+
10045.      2        + "  time finished - "
+
10046.      2        + Number(Date.now() - jstestTimeStart).toLocaleString()
+
10047.      2        + " ms\n"
+
10048.      2        + "\u001b[39m"
+
10049.      2    );
+
10050.      1    if (mode !== "testsFailed") {
+
10051.      1        console.error(message);
+
10052.      1    }
+
10053.      2    process.exitCode = exitCode || jstestCountFailed;
+
10054.      2    return message;
+
10055.      2}
+
10056.      1
+
10057.    107async function moduleFsInit() {
+
10058.    107
+
10059.    107// This function will import nodejs builtin-modules if they have not yet been
+
10060.    107// imported.
+
10061.    107
+
10062.    107// State 3 - Modules already imported.
+
10063.    107
+
10064.    104    if (moduleFs !== undefined) {
+
10065.    104        return;
+
10066.    104    }
+
10067.      3
+
10068.      3// State 2 - Wait while modules are importing.
+
10069.      3
+
10070.      3    if (moduleFsInitResolveList !== undefined) {
+
10071.      2        return new Promise(function (resolve) {
+
10072.      2            moduleFsInitResolveList.push(resolve);
+
10073.      2        });
+
10074.      2    }
+
10075.      1
+
10076.      1// State 1 - Start importing modules.
+
10077.      1
+
10078.      1    moduleFsInitResolveList = [];
+
10079.      1    [
+
10080.      1        moduleChildProcess,
+
10081.      1        moduleFs,
+
10082.      1        modulePath,
+
10083.      1        moduleUrl
+
10084.      1    ] = await Promise.all([
+
10085.      1        import("child_process"),
+
10086.      1        import("fs"),
+
10087.      1        import("path"),
+
10088.      1        import("url")
+
10089.      1    ]);
+
10090.      2    while (moduleFsInitResolveList.length > 0) {
+
10091.      2        moduleFsInitResolveList.shift()();
+
10092.      2    }
+
10093.    107}
+
10094.      1
+
10095.   2728function noop(val) {
+
10096.   2728
+
10097.   2728// This function will do nothing except return <val>.
+
10098.   2728
+
10099.   2728    return val;
+
10100.   2728}
+
10101.      1
+
10102.  12919function objectDeepCopyWithKeysSorted(obj) {
+
10103.  12919
+
10104.  12919// This function will recursively deep-copy <obj> with keys sorted.
+
10105.  12919
+
10106.  12919    let sorted;
+
10107.   8995    if (typeof obj !== "object" || !obj) {
+
10108.   8995        return obj;
+
10109.   8995    }
+
10110.   3924
+
10111.   3924// Recursively deep-copy list with child-keys sorted.
+
10112.   3924
+
10113.   3924    if (Array.isArray(obj)) {
+
10114.   1338        return obj.map(objectDeepCopyWithKeysSorted);
+
10115.   2586    }
+
10116.   2586
+
10117.   2586// Recursively deep-copy obj with keys sorted.
+
10118.   2586
+
10119.   2586    sorted = Object.create(null);
+
10120.   7457    Object.keys(obj).sort().forEach(function (key) {
+
10121.   7457        sorted[key] = objectDeepCopyWithKeysSorted(obj[key]);
+
10122.   7457    });
+
10123.   2586    return sorted;
+
10124.   2586}
+
10125.      1
+
10126.   2791function object_assign_from_list(dict, list, val) {
+
10127.   2791
+
10128.   2791// Assign each property-name from <list> to <dict>.
+
10129.   2791
+
10130.  89862    list.forEach(function (key) {
+
10131.  89862        dict[key] = val;
+
10132.  89862    });
+
10133.   2791    return dict;
+
10134.   2791}
+
10135.      1
+
10136.     97function v8CoverageListMerge(processCovs) {
+
10137.     97
+
10138.     97// This function is derived from MIT Licensed v8-coverage at
+
10139.     97// https://github.com/demurgos/v8-coverage/tree/master/ts
+
10140.     97// https://github.com/demurgos/v8-coverage/blob/master/ts/LICENSE.md
+
10141.     97//
+
10142.     97// Merges a list of v8 process coverages.
+
10143.     97// The result is normalized.
+
10144.     97// The input values may be mutated, it is not safe to use them after passing
+
10145.     97// them to this function.
+
10146.     97// The computation is synchronous.
+
10147.     97// @param processCovs Process coverages to merge.
+
10148.     97// @return Merged process coverage.
+
10149.     97
+
10150.     97    let resultMerged = [];      // List of merged scripts from processCovs.
+
10151.     97    let urlToScriptDict = new Map();    // Map scriptCov.url to scriptCovs.
+
10152.     97
+
10153.   1094    function compareRangeList(aa, bb) {
+
10154.   1094
+
10155.   1094// Compares two range coverages.
+
10156.   1094// The ranges are first ordered by ascending `startOffset` and then by
+
10157.   1094// descending `endOffset`.
+
10158.   1094// This corresponds to a pre-order tree traversal.
+
10159.   1094
+
10160.   1065        if (aa.startOffset !== bb.startOffset) {
+
10161.   1065            return aa.startOffset - bb.startOffset;
+
10162.   1065        }
+
10163.     29        return bb.endOffset - aa.endOffset;
+
10164.     29    }
+
10165.     97
+
10166.   1707    function dictKeyValueAppend(dict, key, val) {
+
10167.   1707
+
10168.   1707// This function will append <val> to list <dict>[<key>].
+
10169.   1707
+
10170.   1707        let list = dict.get(key);
+
10171.   1165        if (list === undefined) {
+
10172.   1165            list = [];
+
10173.   1165            dict.set(key, list);
+
10174.   1165        }
+
10175.   1707        list.push(val);
+
10176.   1707    }
+
10177.     97
+
10178.    384    function mergeTreeList(parentTrees) {
+
10179.    384
+
10180.    384// This function will return RangeTree object with <parentTrees> merged into
+
10181.    384// property-children.
+
10182.    384// @precondition Same `start` and `end` for all the parentTrees
+
10183.    384
+
10184.    119        if (parentTrees.length <= 1) {
+
10185.    119            return parentTrees[0];
+
10186.    265        }
+
10187.    265
+
10188.    265// new RangeTree().
+
10189.    265
+
10190.    265        return {
+
10191.    265
+
10192.    265// Merge parentTrees into property-children.
+
10193.    265
+
10194.    265            children: mergeTreeListToChildren(parentTrees),
+
10195.    669            delta: parentTrees.reduce(function (aa, bb) {
+
10196.    669                return aa + bb.delta;
+
10197.    669            }, 0),
+
10198.    265            end: parentTrees[0].end,
+
10199.    265            start: parentTrees[0].start
+
10200.    265        };
+
10201.    265    }
+
10202.     97
+
10203.    265    function mergeTreeListToChildren(parentTrees) {
+
10204.    265
+
10205.    265// This function will return <resultChildren> with <parentTrees> merged.
+
10206.    265
+
10207.    265        let openRange;
+
10208.    265        let parentToChildDict = new Map();      // Map parent to child.
+
10209.    265        let queueList;
+
10210.    265        let queueListIi = 0;
+
10211.    265        let queueOffset;
+
10212.    265        let queueTrees;
+
10213.    265        let resultChildren = [];
+
10214.    265        let startToTreeDict = new Map();        // Map tree.start to tree.
+
10215.    639        function nextXxx() {
+
10216.    639
+
10217.    639// Increment nextOffset, nextTrees.
+
10218.    639
+
10219.    639            let [
+
10220.    639                nextOffset, nextTrees
+
10221.    300            ] = queueList[queueListIi] || [];
+
10222.    639            let openRangeEnd;
+
10223.    583            if (queueTrees === undefined) {
+
10224.    583                queueListIi += 1;
+
10225.    583
+
10226.    583// Increment nextOffset, nextTrees.
+
10227.    583
+
10228.    583            } else if (nextOffset === undefined || nextOffset > queueOffset) {
+
10229.     56                nextOffset = queueOffset;
+
10230.     56                nextTrees = queueTrees;
+
10231.     56                queueTrees = undefined;
+
10232.     56
+
10233.     56// Concat queueTrees to nextTrees.
+
10234.     56
+
10235.     56            } else {
+
10236.     56                if (nextOffset === queueOffset) {
+
10237.     56                    queueTrees.forEach(function (tree) {
+
10238.     56                        nextTrees.push(tree);
+
10239.     56                    });
+
10240.     56                    queueTrees = undefined;
+
10241.     56                }
+
10242.     56                queueListIi += 1;
+
10243.     56            }
+
10244.    639
+
10245.    639// Reached end of queueList.
+
10246.    639
+
10247.    265            if (nextOffset === undefined) {
+
10248.    265                if (openRange !== undefined) {
+
10249.    265
+
10250.    265// Append nested-children from parentToChildDict (within openRange) to
+
10251.    265// resultChildren.
+
10252.    265
+
10253.    265                    resultAppendNextChild();
+
10254.    265                }
+
10255.    265                return true;
+
10256.    374            }
+
10257.    374            if (openRange !== undefined && openRange.end <= nextOffset) {
+
10258.    129
+
10259.    129// Append nested-children from parentToChildDict (within openRange) to
+
10260.    129// resultChildren.
+
10261.    129
+
10262.    129                resultAppendNextChild();
+
10263.    129                openRange = undefined;
+
10264.    374            }
+
10265.    374            if (openRange === undefined) {
+
10266.    292                openRangeEnd = nextOffset + 1;
+
10267.    502                nextTrees.forEach(function ({
+
10268.    502                    parentIi,
+
10269.    502                    tree
+
10270.    502                }) {
+
10271.    502                    openRangeEnd = Math.max(openRangeEnd, tree.end);
+
10272.    502
+
10273.    502// Append children from nextTrees to parentToChildDict.
+
10274.    502
+
10275.    502                    dictKeyValueAppend(parentToChildDict, parentIi, tree);
+
10276.    502                });
+
10277.    292                queueOffset = openRangeEnd;
+
10278.    292                openRange = {
+
10279.    292                    end: openRangeEnd,
+
10280.    292                    start: nextOffset
+
10281.    292                };
+
10282.    292            } else {
+
10283.    114                nextTrees.forEach(function ({
+
10284.    114                    parentIi,
+
10285.    114                    tree
+
10286.    114                }) {
+
10287.    114                    let right;
+
10288.     82                    if (tree.end > openRange.end) {
+
10289.     82                        right = treeSplit(tree, openRange.end);
+
10290.     82                        if (queueTrees === undefined) {
+
10291.     82                            queueTrees = [];
+
10292.     82                        }
+
10293.     82
+
10294.     82// new RangeTreeWithParent().
+
10295.     82
+
10296.     82                        queueTrees.push({
+
10297.     82                            parentIi,
+
10298.     82                            tree: right
+
10299.     82                        });
+
10300.     82                    }
+
10301.    114
+
10302.    114// Append children from nextTrees to parentToChildDict.
+
10303.    114
+
10304.    114                    dictKeyValueAppend(parentToChildDict, parentIi, tree);
+
10305.    114                });
+
10306.     82            }
+
10307.    639        }
+
10308.    292        function resultAppendNextChild() {
+
10309.    292
+
10310.    292// This function will append next child to <resultChildren>.
+
10311.    292
+
10312.    292            let treesMatching = [];
+
10313.    589            parentToChildDict.forEach(function (nested) {
+
10314.    589                if (
+
10315.    589                    nested.length === 1
+
10316.    563                    && nested[0].start === openRange.start
+
10317.    480                    && nested[0].end === openRange.end
+
10318.    468                ) {
+
10319.    468                    treesMatching.push(nested[0]);
+
10320.    468                } else {
+
10321.    121
+
10322.    121// new rangeTreeCreate().
+
10323.    121
+
10324.    121                    treesMatching.push({
+
10325.    121                        children: nested,
+
10326.    121                        delta: 0,
+
10327.    121                        end: openRange.end,
+
10328.    121                        start: openRange.start
+
10329.    121                    });
+
10330.    121                }
+
10331.    589            });
+
10332.    292            parentToChildDict.clear();
+
10333.    292
+
10334.    292// Recurse mergeTreeList().
+
10335.    292
+
10336.    292            resultChildren.push(mergeTreeList(treesMatching));
+
10337.    292        }
+
10338.     75        function treeSplit(tree, offset) {
+
10339.     75
+
10340.     75// This function will split <tree> along <offset> and return the right-side.
+
10341.     75// @precondition `tree.start < offset && offset < tree.end`
+
10342.     75// @return RangeTree Right part
+
10343.     75
+
10344.     75            let child;
+
10345.     75            let ii = 0;
+
10346.     75            let leftChildLen = tree.children.length;
+
10347.     75            let mid;
+
10348.     75            let resultTree;
+
10349.     75            let rightChildren;
+
10350.     75
+
10351.     75// TODO(perf): Binary search (check overhead) //jslint-ignore-line
+
10352.     75
+
10353.     19            while (ii < tree.children.length) {
+
10354.     19                child = tree.children[ii];
+
10355.     19                if (child.start < offset && offset < child.end) {
+
10356.     19
+
10357.     19// Recurse treeSplit().
+
10358.     19
+
10359.     19                    mid = treeSplit(child, offset);
+
10360.     19                    leftChildLen = ii + 1;
+
10361.     19                    break;
+
10362.     19                }
+
10363.     19                if (child.start >= offset) {
+
10364.     19                    leftChildLen = ii;
+
10365.     19                    break;
+
10366.     19                }
+
10367.     19                ii += 1;
+
10368.     19            }
+
10369.     75            rightChildren = tree.children.splice(
+
10370.     75                leftChildLen,
+
10371.     75                tree.children.length - leftChildLen
+
10372.     75            );
+
10373.      4            if (mid !== undefined) {
+
10374.      4                rightChildren.unshift(mid);
+
10375.      4            }
+
10376.     75
+
10377.     75// new rangeTreeCreate().
+
10378.     75
+
10379.     75            resultTree = {
+
10380.     75                children: rightChildren,
+
10381.     75                delta: tree.delta,
+
10382.     75                end: tree.end,
+
10383.     75                start: offset
+
10384.     75            };
+
10385.     75            tree.end = offset;
+
10386.     75            return resultTree;
+
10387.     75        }
+
10388.    265
+
10389.    265// Init startToTreeDict.
+
10390.    265
+
10391.    669        parentTrees.forEach(function (parentTree, parentIi) {
+
10392.    545            parentTree.children.forEach(function (child) {
+
10393.    545
+
10394.    545// Append child with child.start to startToTreeDict.
+
10395.    545
+
10396.    545                dictKeyValueAppend(startToTreeDict, child.start, {
+
10397.    545                    parentIi,
+
10398.    545                    tree: child
+
10399.    545                });
+
10400.    545            });
+
10401.    669        });
+
10402.    265
+
10403.    265// init queueList.
+
10404.    265
+
10405.    335        queueList = Array.from(startToTreeDict).map(function ([
+
10406.    335            startOffset, trees
+
10407.    335        ]) {
+
10408.    335
+
10409.    335// new StartEvent().
+
10410.    335
+
10411.    335            return [
+
10412.    335                startOffset, trees
+
10413.    335            ];
+
10414.    217        }).sort(function (aa, bb) {
+
10415.    217            return aa[0] - bb[0];
+
10416.    217        });
+
10417.    639        while (true) {
+
10418.    639            if (nextXxx()) {
+
10419.    639                break;
+
10420.    639            }
+
10421.    639        }
+
10422.    265        return resultChildren;
+
10423.    265    }
+
10424.     97
+
10425.    689    function sortFunc(funcCov) {
+
10426.    689
+
10427.    689// This function will normalize-and-sort <funcCov>.ranges.
+
10428.    689// Sorts the ranges (pre-order sort).
+
10429.    689// TODO: Tree-based normalization of the ranges. //jslint-ignore-line
+
10430.    689// @param funcCov Function coverage to normalize.
+
10431.    689
+
10432.    689        funcCov.ranges = treeToRanges(treeFromSortedRanges(
+
10433.    689            funcCov.ranges.sort(compareRangeList)
+
10434.    689        ));
+
10435.    689        return funcCov;
+
10436.    689    }
+
10437.     97
+
10438.    129    function sortScript(scriptCov) {
+
10439.    129
+
10440.    129// This function will normalize-and-sort <scriptCov>.functions.
+
10441.    129
+
10442.    129// Normalize-and-sort functions[xxx].ranges.
+
10443.    129
+
10444.    688        scriptCov.functions.forEach(function (funcCov) {
+
10445.    688            sortFunc(funcCov);
+
10446.    688        });
+
10447.    129
+
10448.    129// Sort functions by root range (pre-order sort).
+
10449.    129
+
10450.    559        scriptCov.functions.sort(function (aa, bb) {
+
10451.    559            return compareRangeList(aa.ranges[0], bb.ranges[0]);
+
10452.    559        });
+
10453.    129        return scriptCov;
+
10454.    129    }
+
10455.     97
+
10456.    888    function treeFromSortedRanges(ranges) {
+
10457.    888
+
10458.    888// @precondition `ranges` are well-formed and pre-order sorted
+
10459.    888
+
10460.    888        let root;
+
10461.    888        let stack = [];   // Stack of parent trees and parent counts.
+
10462.   1856        ranges.forEach(function (range) {
+
10463.   1856
+
10464.   1856// new rangeTreeCreate().
+
10465.   1856
+
10466.   1856            let node = {
+
10467.   1856                children: [],
+
10468.   1856                delta: range.count,
+
10469.   1856                end: range.endOffset,
+
10470.   1856                start: range.startOffset
+
10471.   1856            };
+
10472.   1856            let parent;
+
10473.   1856            let parentCount;
+
10474.    888            if (root === undefined) {
+
10475.    888                root = node;
+
10476.    888                stack.push([
+
10477.    888                    node, range.count
+
10478.    888                ]);
+
10479.    888                return;
+
10480.    968            }
+
10481.   1565            while (true) {
+
10482.   1565                [
+
10483.   1565                    parent, parentCount
+
10484.   1565                ] = stack[stack.length - 1];
+
10485.   1565
+
10486.   1565// assert: `top !== undefined` (the ranges are sorted)
+
10487.   1565
+
10488.   1565                if (range.startOffset < parent.end) {
+
10489.   1565                    break;
+
10490.   1565                }
+
10491.   1565                stack.pop();
+
10492.   1565            }
+
10493.    968            node.delta -= parentCount;
+
10494.    968            parent.children.push(node);
+
10495.    968            stack.push([
+
10496.    968                node, range.count
+
10497.    968            ]);
+
10498.    968        });
+
10499.    888        return root;
+
10500.    888    }
+
10501.     97
+
10502.    781    function treeToRanges(tree) {
+
10503.    781
+
10504.    781// Get the range coverages corresponding to the tree.
+
10505.    781// The ranges are pre-order sorted.
+
10506.    781
+
10507.    781        let count;
+
10508.    781        let cur;
+
10509.    781        let ii;
+
10510.    781        let parentCount;
+
10511.    781        let ranges = [];
+
10512.    781        let stack = [           // Stack of parent trees and counts.
+
10513.    781            [
+
10514.    781                tree, 0
+
10515.    781            ]
+
10516.    781        ];
+
10517.   1630        function normalizeRange(tree) {
+
10518.   1630
+
10519.   1630// @internal
+
10520.   1630
+
10521.   1630            let children = [];
+
10522.   1630            let curEnd;
+
10523.   1630            let head;
+
10524.   1630            let tail = [];
+
10525.    849            function endChain() {
+
10526.     18                if (tail.length !== 0) {
+
10527.     18                    head.end = tail[tail.length - 1].end;
+
10528.     18                    tail.forEach(function (tailTree) {
+
10529.     18                        tailTree.children.forEach(function (subChild) {
+
10530.     18                            subChild.delta += tailTree.delta - head.delta;
+
10531.     18                            head.children.push(subChild);
+
10532.     18                        });
+
10533.     18                    });
+
10534.     18                    tail.length = 0;
+
10535.     18                }
+
10536.    849
+
10537.    849// Recurse normalizeRange().
+
10538.    849
+
10539.    849                normalizeRange(head);
+
10540.    849                children.push(head);
+
10541.    849            }
+
10542.    867            tree.children.forEach(function (child) {
+
10543.    432                if (head === undefined) {
+
10544.    432                    head = child;
+
10545.    435                } else if (
+
10546.    435                    child.delta === head.delta && child.start === curEnd
+
10547.    435                ) {
+
10548.    435                    tail.push(child);
+
10549.    435                } else {
+
10550.    435                    endChain();
+
10551.    435                    head = child;
+
10552.    435                }
+
10553.    867                curEnd = child.end;
+
10554.    867            });
+
10555.    432            if (head !== undefined) {
+
10556.    432                endChain();
+
10557.    432            }
+
10558.    238            if (children.length === 1) {
+
10559.    238                if (
+
10560.    238                    children[0].start === tree.start
+
10561.    238                    && children[0].end === tree.end
+
10562.    238                ) {
+
10563.    238                    tree.delta += children[0].delta;
+
10564.    238                    tree.children = children[0].children;
+
10565.    238
+
10566.    238// `.lazyCount` is zero for both (both are after normalization)
+
10567.    238
+
10568.    238                    return;
+
10569.    238                }
+
10570.   1624            }
+
10571.   1624            tree.children = children;
+
10572.   1624        }
+
10573.    781        normalizeRange(tree);
+
10574.   1624        while (stack.length > 0) {
+
10575.   1624            [
+
10576.   1624                cur, parentCount
+
10577.   1624            ] = stack.pop();
+
10578.   1624            count = parentCount + cur.delta;
+
10579.   1624            ranges.push({
+
10580.   1624                count,
+
10581.   1624                endOffset: cur.end,
+
10582.   1624                startOffset: cur.start
+
10583.   1624            });
+
10584.   1624            ii = cur.children.length - 1;
+
10585.   1624            while (ii >= 0) {
+
10586.   1624                stack.push([
+
10587.   1624                    cur.children[ii], count
+
10588.   1624                ]);
+
10589.   1624                ii -= 1;
+
10590.   1624            }
+
10591.   1624        }
+
10592.    781        return ranges;
+
10593.    781    }
+
10594.     97
+
10595.      1    if (processCovs.length === 0) {
+
10596.      1        return {
+
10597.      1            result: []
+
10598.      1        };
+
10599.     96    }
+
10600.     96
+
10601.     96// Init urlToScriptDict.
+
10602.     96
+
10603.    234    processCovs.forEach(function ({
+
10604.    234        result
+
10605.    234    }) {
+
10606.    269        result.forEach(function (scriptCov) {
+
10607.    269            dictKeyValueAppend(urlToScriptDict, scriptCov.url, scriptCov);
+
10608.    269        });
+
10609.    234    });
+
10610.    129    urlToScriptDict.forEach(function (scriptCovs) {
+
10611.    129
+
10612.    129// assert: `scriptCovs.length > 0`
+
10613.    129
+
10614.    129// function mergeScriptList(scriptCovs) {
+
10615.    129// Merges a list of matching script coverages.
+
10616.    129// Scripts are matching if they have the same `url`.
+
10617.    129// The result is normalized.
+
10618.    129// The input values may be mutated, it is not safe to use them after passing
+
10619.    129// them to this function.
+
10620.    129// The computation is synchronous.
+
10621.    129// @param scriptCovs Process coverages to merge.
+
10622.    129// @return Merged script coverage, or `undefined` if the input list was empty.
+
10623.    129
+
10624.    129        let functions = [];
+
10625.    129
+
10626.    129// Map funcCovRoot.startOffset:funcCovRoot.endOffset to funcCov.
+
10627.    129
+
10628.    129        let rangeToFuncDict = new Map();
+
10629.    129
+
10630.    129// Probably deadcode.
+
10631.    129// if (scriptCovs.length === 0) {
+
10632.    129//     return undefined;
+
10633.    129// }
+
10634.    129
+
10635.     96        if (scriptCovs.length === 1) {
+
10636.     96            resultMerged.push(sortScript(scriptCovs[0]));
+
10637.     96            return;
+
10638.     96        }
+
10639.     96
+
10640.     96// Init rangeToFuncDict.
+
10641.     96// Map funcCovRoot.startOffset:funcCovRoot.endOffset to funcCov.
+
10642.     96
+
10643.    226        scriptCovs.forEach(function ({
+
10644.    226            functions
+
10645.    226        }) {
+
10646.    277            functions.forEach(function (funcCov) {
+
10647.    277                dictKeyValueAppend(
+
10648.    277                    rangeToFuncDict,
+
10649.    277
+
10650.    277// This string can be used to match function with same root range.
+
10651.    277// The string is derived from the start and end offsets of the root range of
+
10652.    277// the function.
+
10653.    277// This assumes that `ranges` is non-empty (true for valid function coverages).
+
10654.    277
+
10655.    277                    (
+
10656.    277                        funcCov.ranges[0].startOffset
+
10657.    277                        + ";" + funcCov.ranges[0].endOffset
+
10658.    277                    ),
+
10659.    277                    funcCov
+
10660.    277                );
+
10661.    277            });
+
10662.    226        });
+
10663.    112        rangeToFuncDict.forEach(function (funcCovs) {
+
10664.    112
+
10665.    112// assert: `funcCovs.length > 0`
+
10666.    112
+
10667.    112// function mergeFuncList(funcCovs) {
+
10668.    112// Merges a list of matching function coverages.
+
10669.    112// Functions are matching if their root ranges have the same span.
+
10670.    112// The result is normalized.
+
10671.    112// The input values may be mutated, it is not safe to use them after passing
+
10672.    112// them to this function.
+
10673.    112// The computation is synchronous.
+
10674.    112// @param funcCovs Function coverages to merge.
+
10675.    112// @return Merged function coverage, or `undefined` if the input list was empty.
+
10676.    112
+
10677.    112            let count = 0;
+
10678.    112            let isBlockCoverage;
+
10679.    112            let merged;
+
10680.    112            let ranges;
+
10681.    112            let trees = [];
+
10682.    112
+
10683.    112// Probably deadcode.
+
10684.    112// if (funcCovs.length === 0) {
+
10685.    112//     return undefined;
+
10686.    112// }
+
10687.    112
+
10688.     96            if (funcCovs.length === 1) {
+
10689.     96                functions.push(sortFunc(funcCovs[0]));
+
10690.     96                return;
+
10691.    111            }
+
10692.    111
+
10693.    111// assert: `funcCovs[0].ranges.length > 0`
+
10694.    111
+
10695.    276            funcCovs.forEach(function (funcCov) {
+
10696.    276
+
10697.    276// assert: `funcCov.ranges.length > 0`
+
10698.    276// assert: `funcCov.ranges` is sorted
+
10699.    276
+
10700.    276                count += (
+
10701.    276                    funcCov.count !== undefined
+
10702.    111                    ? funcCov.count
+
10703.    274                    : funcCov.ranges[0].count
+
10704.    276                );
+
10705.    199                if (funcCov.isBlockCoverage) {
+
10706.    199                    trees.push(treeFromSortedRanges(funcCov.ranges));
+
10707.    199                }
+
10708.    276            });
+
10709.    111            if (trees.length > 0) {
+
10710.     96                isBlockCoverage = true;
+
10711.     96                ranges = treeToRanges(mergeTreeList(trees));
+
10712.     96            } else {
+
10713.     96                isBlockCoverage = false;
+
10714.     96                ranges = [
+
10715.     96                    {
+
10716.     96                        count,
+
10717.     96                        endOffset: funcCovs[0].ranges[0].endOffset,
+
10718.     96                        startOffset: funcCovs[0].ranges[0].startOffset
+
10719.     96                    }
+
10720.     96                ];
+
10721.    111            }
+
10722.    111            merged = {
+
10723.    111                functionName: funcCovs[0].functionName,
+
10724.    111                isBlockCoverage,
+
10725.    111                ranges
+
10726.    111            };
+
10727.    111            if (count !== ranges[0].count) {
+
10728.     96                merged.count = count;
+
10729.    111            }
+
10730.    111
+
10731.    111// assert: `merged` is normalized
+
10732.    111
+
10733.    111            functions.push(merged);
+
10734.    111        });
+
10735.     96        resultMerged.push(sortScript({
+
10736.     96            functions,
+
10737.     96            scriptId: scriptCovs[0].scriptId,
+
10738.     96            url: scriptCovs[0].url
+
10739.     96        }));
+
10740.     96    });
+
10741.     96
+
10742.     96// Sorts the scripts alphabetically by `url`.
+
10743.     96// Reassigns script ids: the script at index `0` receives `"0"`, the script at
+
10744.     96// index `1` receives `"1"` etc.
+
10745.     96
+
10746.     96    Object.entries(resultMerged.sort(function (aa, bb) {
+
10747.     96        return (
+
10748.     96            aa.url > bb.url
+
10749.     96            ? 1
+
10750.     96            : -1
+
10751.     96        );
+
10752.    129    })).forEach(function ([
+
10753.    129        scriptId, scriptCov
+
10754.    129    ]) {
+
10755.    129        scriptCov.scriptId = scriptId.toString(10);
+
10756.    129    });
+
10757.     96    return {
+
10758.     96        result: resultMerged
+
10759.     96    };
+
10760.     96}
+
10761.      1
+
10762.      8async function v8CoverageReportCreate({
+
10763.      8    consoleError,
+
10764.      8    coverageDir,
+
10765.      8    processArgv = []
+
10766.      8}) {
+
10767.      8
+
10768.      8// This function will create html-coverage-reports directly from
+
10769.      8// v8-coverage-files in <coverageDir>.
+
10770.      8// 1. Spawn node.js program <processArgv> with NODE_V8_COVERAGE.
+
10771.      8// 2. Merge JSON v8-coverage-files in <coverageDir>.
+
10772.      8// 3. Create html-coverage-reports in <coverageDir>.
+
10773.      8
+
10774.      8    let cwd;
+
10775.      8    let excludeList = [];
+
10776.      8    let exitCode = 0;
+
10777.      8    let fileDict;
+
10778.      8    let includeList = [];
+
10779.      8    let modeIncludeNodeModules;
+
10780.      8    let processArgElem;
+
10781.      8    let promiseList = [];
+
10782.      8    let v8CoverageObj;
+
10783.      8
+
10784.     13    function htmlRender({
+
10785.     13        fileList,
+
10786.     13        lineList,
+
10787.     13        modeIndex,
+
10788.     13        pathname
+
10789.     13    }) {
+
10790.     13        let html;
+
10791.     13        let padLines;
+
10792.     13        let padPathname;
+
10793.     13        let txt;
+
10794.     13        let txtBorder;
+
10795.     13        html = "";
+
10796.     13        html += String(`
+
10797.     13<!DOCTYPE html>
+
10798.     13<html lang="en">
+
10799.     13<head>
+
10800.     13<title>V8 Coverage Report</title>
+
10801.     13<style>
+
10802.     13/* jslint utility2:true */
+
10803.     13/*csslint ignore:start*/
+
10804.     13.coverage,
+
10805.     13.coverage a,
+
10806.     13.coverage div,
+
10807.     13.coverage pre,
+
10808.     13.coverage span,
+
10809.     13.coverage table,
+
10810.     13.coverage tbody,
+
10811.     13.coverage td,
+
10812.     13.coverage th,
+
10813.     13.coverage thead,
+
10814.     13.coverage tr {
+
10815.     13    box-sizing: border-box;
+
10816.     13    font-family: monospace;
+
10817.     13}
+
10818.     13/*csslint ignore:end*/
+
10819.     13
+
10820.     13/* css - coverage_report - general */
+
10821.     13body {
+
10822.     13    margin: 0;
+
10823.     13}
+
10824.     13.coverage pre {
+
10825.     13    margin: 5px 0;
+
10826.     13}
+
10827.     13.coverage table {
+
10828.     13    border-collapse: collapse;
+
10829.     13}
+
10830.     13.coverage td,
+
10831.     13.coverage th {
+
10832.     13    border: 1px solid #777;
+
10833.     13    line-height: 20px;
+
10834.     13    margin: 0;
+
10835.     13    padding: 5px 10px;
+
10836.     13}
+
10837.     13.coverage td span {
+
10838.     13    display: inline-block;
+
10839.     13    width: 100%;
+
10840.     13}
+
10841.     13.coverage .content {
+
10842.     13    padding: 0 5px;
+
10843.     13}
+
10844.     13.coverage .content a {
+
10845.     13    text-decoration: none;
+
10846.     13}
+
10847.     13.coverage .count {
+
10848.     13    margin: 0 5px;
+
10849.     13    padding: 0 5px;
+
10850.     13}
+
10851.     13.coverage .footer,
+
10852.     13.coverage .header {
+
10853.     13    padding: 20px;
+
10854.     13}
+
10855.     13.coverage .footer {
+
10856.     13    text-align: center;
+
10857.     13}
+
10858.     13.coverage .percentbar {
+
10859.     13    height: 12px;
+
10860.     13    margin: 2px 0;
+
10861.     13    min-width: 200px;
+
10862.     13    position: relative;
+
10863.     13    width: 100%;
+
10864.     13}
+
10865.     13.coverage .percentbar div {
+
10866.     13    height: 100%;
+
10867.     13    position: absolute;
+
10868.     13}
+
10869.     13.coverage .title {
+
10870.     13    font-size: large;
+
10871.     13    font-weight: bold;
+
10872.     13    margin-bottom: 10px;
+
10873.     13}
+
10874.     13
+
10875.     13/* css - coverage_report - color */
+
10876.     13.coverage td,
+
10877.     13.coverage th {
+
10878.     13    background: #fff;
+
10879.     13}
+
10880.     13.coverage .count,
+
10881.     13.coverage .coverageHigh {
+
10882.     13    background: #9d9;
+
10883.     13}
+
10884.     13.coverage .count {
+
10885.     13    color: #666;
+
10886.     13}
+
10887.     13.coverage .coverageIgnore {
+
10888.     13    background: #ccc;
+
10889.     13}
+
10890.     13.coverage .coverageLow,
+
10891.     13.coverage .uncovered {
+
10892.     13    background: #ebb;
+
10893.     13}
+
10894.     13.coverage .coverageMedium {
+
10895.     13    background: #fd7;
+
10896.     13}
+
10897.     13.coverage .footer,
+
10898.     13.coverage .header,
+
10899.     13.coverage .lineno {
+
10900.     13    background: #ddd;
+
10901.     13}
+
10902.     13.coverage .percentbar {
+
10903.     13    background: #999;
+
10904.     13}
+
10905.     13.coverage .percentbar div {
+
10906.     13    background: #666;
+
10907.     13}
+
10908.     13
+
10909.     13/* css - coverage_report - important */
+
10910.     13.coverage pre:hover span,
+
10911.     13.coverage tr:hover td {
+
10912.     13    background: #7d7;
+
10913.     13}
+
10914.     13.coverage pre:hover span.uncovered,
+
10915.     13.coverage tr:hover td.coverageLow {
+
10916.     13    background: #f99;
+
10917.     13}
+
10918.     13</style>
+
10919.     13</head>
+
10920.     13<body class="coverage">
+
10921.     13<!-- header start -->
+
10922.     13<div class="header">
+
10923.     13<div class="title">V8 Coverage Report</div>
+
10924.     13<table>
+
10925.     13<thead>
+
10926.     13    <tr>
+
10927.     13    <th>Files covered</th>
+
10928.     13    <th>Lines</th>
+
10929.     13    <th>Remaining</th>
+
10930.     13    </tr>
+
10931.     13</thead>
+
10932.     13<tbody>
+
10933.     13        `).trim() + "\n";
+
10934.      7        if (modeIndex) {
+
10935.      7            padLines = String("(ignore) 100.00 %").length;
+
10936.      7            padPathname = 32;
+
10937.      7            fileList.unshift({
+
10938.      7                linesCovered: 0,
+
10939.      7                linesTotal: 0,
+
10940.      7                modeCoverageIgnoreFile: "",
+
10941.      7                pathname: "./"
+
10942.      7            });
+
10943.      7            fileList.slice(1).forEach(function ({
+
10944.      7                linesCovered,
+
10945.      7                linesTotal,
+
10946.      7                modeCoverageIgnoreFile,
+
10947.      7                pathname
+
10948.      7            }) {
+
10949.      7                if (!modeCoverageIgnoreFile) {
+
10950.      7                    fileList[0].linesCovered += linesCovered;
+
10951.      7                    fileList[0].linesTotal += linesTotal;
+
10952.      7                }
+
10953.      7                padPathname = Math.max(padPathname, pathname.length + 2);
+
10954.      7                padLines = Math.max(
+
10955.      7                    padLines,
+
10956.      7                    String(linesCovered + " / " + linesTotal).length
+
10957.      7                );
+
10958.      7            });
+
10959.      7        }
+
10960.     13        txtBorder = (
+
10961.     13            "+" + "-".repeat(padPathname + 2) + "+"
+
10962.     13            + "-".repeat(padLines + 2) + "+"
+
10963.     13            + "-".repeat(padLines + 2) + "+\n"
+
10964.     13        );
+
10965.     13        txt = "";
+
10966.     13        txt += "V8 Coverage Report\n";
+
10967.     13        txt += txtBorder;
+
10968.     13        txt += (
+
10969.     13            "| " + String("Files covered").padEnd(padPathname, " ") + " | "
+
10970.     13            + String("Lines").padStart(padLines, " ") + " | "
+
10971.     13            + String("Remaining").padStart(padLines, " ") + " |\n"
+
10972.     13        );
+
10973.     13        txt += txtBorder;
+
10974.     19        fileList.forEach(function ({
+
10975.     19            linesCovered,
+
10976.     19            linesTotal,
+
10977.     19            modeCoverageIgnoreFile,
+
10978.     19            pathname
+
10979.     19        }, ii) {
+
10980.     19            let coverageLevel;
+
10981.     19            let coveragePct;
+
10982.     19            let fill;
+
10983.     19            let str1;
+
10984.     19            let str2;
+
10985.     19            let xx1;
+
10986.     19            let xx2;
+
10987.      2            coveragePct = Math.floor(10000 * linesCovered / linesTotal || 0);
+
10988.     19            coverageLevel = (
+
10989.     19                modeCoverageIgnoreFile
+
10990.      2                ? "coverageIgnore"
+
10991.     17                : coveragePct >= 8000
+
10992.     17                ? "coverageHigh"
+
10993.     17                : coveragePct >= 5000
+
10994.     17                ? "coverageMedium"
+
10995.     17                : "coverageLow"
+
10996.     19            );
+
10997.     19            coveragePct = String(coveragePct).replace((
+
10998.     19                /..$/m
+
10999.     19            ), ".$&");
+
11000.     13            if (modeIndex && ii === 0) {
+
11001.      7                fill = (
+
11002.      7
+
11003.      7// Badge-color rgb-red.
+
11004.      7
+
11005.      7                    "#" + Math.round(
+
11006.      7                        (100 - Number(coveragePct)) * 2.21
+
11007.      7                    ).toString(16).padStart(2, "0")
+
11008.      7
+
11009.      7// Badge-color rgb-green.
+
11010.      7
+
11011.      7                    + Math.round(
+
11012.      7                        Number(coveragePct) * 2.21
+
11013.      7                    ).toString(16).padStart(2, "0")
+
11014.      7
+
11015.      7// Badge-color rgb-blue.
+
11016.      7
+
11017.      7                    + "00"
+
11018.      7                );
+
11019.      7                str1 = "coverage";
+
11020.      7                str2 = coveragePct + " %";
+
11021.      7                xx1 = 6 * str1.length + 20;
+
11022.      7                xx2 = 6 * str2.length + 20;
+
11023.      7
+
11024.      7// Fs - write coverage_badge.svg.
+
11025.      7
+
11026.      7                promiseList.push(fsWriteFileWithParents((
+
11027.      7                    coverageDir + "coverage_badge.svg"
+
11028.      7                ), String(`
+
11029.      7<svg height="20" width="${xx1 + xx2}" xmlns="http://www.w3.org/2000/svg">
+
11030.      7<rect fill="#555" height="20" width="${xx1 + xx2}"/>
+
11031.      7<rect fill="${fill}" height="20" width="${xx2}" x="${xx1}"/>
+
11032.      7<g
+
11033.      7    fill="#fff"
+
11034.      7    font-family="verdana, geneva, dejavu sans, sans-serif"
+
11035.      7    font-size="11"
+
11036.      7    font-weight="bold"
+
11037.      7    text-anchor="middle"
+
11038.      7>
+
11039.      7<text x="${0.5 * xx1}" y="14">${str1}</text>
+
11040.      7<text x="${xx1 + 0.5 * xx2}" y="14">${str2}</text>
+
11041.      7</g>
+
11042.      7</svg>
+
11043.      7                `).trim() + "\n"));
+
11044.      7                pathname = "";
+
11045.      7            }
+
11046.     19            txt += (
+
11047.     19                "| "
+
11048.     19                + String("./" + pathname).padEnd(padPathname, " ") + " | "
+
11049.     19                + String(
+
11050.     19                    modeCoverageIgnoreFile + " " + coveragePct + " %"
+
11051.     19                ).padStart(padLines, " ") + " | "
+
11052.     19                + " ".repeat(padLines) + " |\n"
+
11053.     19            );
+
11054.     19            txt += (
+
11055.     19                "| " + "*".repeat(
+
11056.     19                    Math.round(0.01 * coveragePct * padPathname)
+
11057.     19                ).padEnd(padPathname, "_") + " | "
+
11058.     19                + String(
+
11059.     19                    linesCovered + " / " + linesTotal
+
11060.     19                ).padStart(padLines, " ") + " | "
+
11061.     19                + String(
+
11062.     19                    (linesTotal - linesCovered) + " / " + linesTotal
+
11063.     19                ).padStart(padLines, " ") + " |\n"
+
11064.     19            );
+
11065.     19            txt += txtBorder;
+
11066.     19            pathname = htmlEscape(pathname);
+
11067.     19
+
11068.     19// CL-37251d17 - Bugfix - Fix incorrect http-link to index.html.
+
11069.     19
+
11070.     19            html += String(`
+
11071.     19    <tr>
+
11072.     19    <td class="${coverageLevel}">
+
11073.     19            ${(
+
11074.     19                modeIndex
+
11075.     13                ? (
+
11076.     13                    "<a href=\"" + (pathname || "index") + ".html\">. / "
+
11077.     13                    + pathname + "</a><br>"
+
11078.     13                )
+
11079.      6                : (
+
11080.      6                    "<a href=\""
+
11081.      6                    + "../".repeat(pathname.split("/").length - 1)
+
11082.      6                    + "index.html\">. / </a>"
+
11083.      6                    + pathname + "<br>"
+
11084.      6                )
+
11085.     19            )}
+
11086.     19        <div class="percentbar">
+
11087.     19            <div style="width: ${coveragePct}%;"></div>
+
11088.     19        </div>
+
11089.     19    </td>
+
11090.     19    <td style="text-align: right;">
+
11091.     19        ${modeCoverageIgnoreFile} ${coveragePct} %<br>
+
11092.     19        ${linesCovered} / ${linesTotal}
+
11093.     19    </td>
+
11094.     19    <td style="text-align: right;">
+
11095.     19        <br>
+
11096.     19        ${linesTotal - linesCovered} / ${linesTotal}
+
11097.     19    </td>
+
11098.     19    </tr>
+
11099.     19        `).trim() + "\n";
+
11100.     19        });
+
11101.     13        html += String(`
+
11102.     13</tbody>
+
11103.     13</table>
+
11104.     13</div>
+
11105.     13<!-- header end -->
+
11106.     13        `).trim() + "\n";
+
11107.      6        if (!modeIndex) {
+
11108.      6            html += String(`
+
11109.      6<!-- content start -->
+
11110.      6<div class="content">
+
11111.      6            `).trim() + "\n";
+
11112.  11853            lineList.forEach(function ({
+
11113.  11853                count,
+
11114.  11853                holeList,
+
11115.  11853                line,
+
11116.  11853                startOffset
+
11117.  11853            }, ii) {
+
11118.  11853                let chunk;
+
11119.  11853                let inHole;
+
11120.  11853                let lineHtml;
+
11121.  11853                let lineId;
+
11122.  11853                lineHtml = "";
+
11123.  11853                lineId = "line_" + (ii + 1);
+
11124.  11853                switch (count) {
+
11125.     32                case -1:
+
11126.  11219                case 0:
+
11127.  11219                    if (holeList.length === 0) {
+
11128.  11219                        lineHtml += "</span>";
+
11129.  11219                        lineHtml += "<span class=\"uncovered\">";
+
11130.  11219                        lineHtml += htmlEscape(line);
+
11131.  11219                        break;
+
11132.  11219                    }
+
11133.  11219                    line = line.split("").map(function (char) {
+
11134.  11219                        return {
+
11135.  11219                            char,
+
11136.  11219                            isHole: undefined
+
11137.  11219                        };
+
11138.  11219                    });
+
11139.  11219                    holeList.forEach(function ([
+
11140.  11219                        aa, bb
+
11141.  11219                    ]) {
+
11142.  11219                        aa = Math.max(aa - startOffset, 0);
+
11143.  11219                        bb = Math.min(bb - startOffset, line.length);
+
11144.  11219                        while (aa < bb) {
+
11145.  11219                            line[aa].isHole = true;
+
11146.  11219                            aa += 1;
+
11147.  11219                        }
+
11148.  11219                    });
+
11149.  11219                    chunk = "";
+
11150.  11219                    line.forEach(function ({
+
11151.  11219                        char,
+
11152.  11219                        isHole
+
11153.  11219                    }) {
+
11154.  11219                        if (inHole !== isHole) {
+
11155.  11219                            lineHtml += htmlEscape(chunk);
+
11156.  11219                            lineHtml += "</span><span";
+
11157.  11219
+
11158.  11219// Coverage-hack - Ugly-hack around possible deadcode where isHole is always
+
11159.  11219// true.
+
11160.  11219
+
11161.  11219                            if (isHole) {
+
11162.  11219                                lineHtml += " class=\"uncovered\"";
+
11163.  11219                            }
+
11164.  11219                            lineHtml += ">";
+
11165.  11219                            chunk = "";
+
11166.  11219                            inHole = isHole;
+
11167.  11219                        }
+
11168.  11219                        chunk += char;
+
11169.  11219                    });
+
11170.  11219                    lineHtml += htmlEscape(chunk);
+
11171.  11219                    break;
+
11172.    634                default:
+
11173.    634                    lineHtml += htmlEscape(line);
+
11174.  11853                }
+
11175.  11853                html += String(`
+
11176.  11853<pre>
+
11177.  11853<span class="lineno">
+
11178.  11853<a href="#${lineId}" id="${lineId}">${String(ii + 1).padStart(5, " ")}.</a>
+
11179.  11853</span>
+
11180.  11853<span class="count
+
11181.  11853                ${(
+
11182.  11853                    count <= 0
+
11183.  11219                    ? "uncovered"
+
11184.    634                    : ""
+
11185.  11853                )}"
+
11186.  11853>
+
11187.  11187${String(count || "-0").padStart(7, " ")}
+
11188.  11853</span>
+
11189.  11853<span>${lineHtml}</span>
+
11190.  11853</pre>
+
11191.  11853                `).replace((
+
11192.  11853                    /\n/g
+
11193.  11853                ), "").trim() + "\n";
+
11194.  11853            });
+
11195.      6            html += String(`
+
11196.      6</div>
+
11197.      6<!-- content end -->
+
11198.      6            `).trim() + "\n";
+
11199.      6        }
+
11200.     13        html += String(`
+
11201.     13<div class="footer">
+
11202.     13    [
+
11203.     13    This document was created with
+
11204.     13    <a href="https://github.com/jslint-org/jslint">JSLint</a>
+
11205.     13    ]
+
11206.     13</div>
+
11207.     13</body>
+
11208.     13</html>
+
11209.     13        `).trim() + "\n";
+
11210.     13
+
11211.     13// Fs - write <file>.html.
+
11212.     13
+
11213.     13        promiseList.push(fsWriteFileWithParents(pathname + ".html", html));
+
11214.      6        if (!modeIndex) {
+
11215.      6            return;
+
11216.      7        }
+
11217.      7
+
11218.      7// Fs - write coverage_report.txt.
+
11219.      7
+
11220.      7        consoleError("\n" + txt);
+
11221.      7        promiseList.push(fsWriteFileWithParents((
+
11222.      7            coverageDir + "coverage_report.txt"
+
11223.      7        ), txt));
+
11224.      7    }
+
11225.      8
+
11226.      8/*
+
11227.      8function sentinel() {}
+
11228.      8*/
+
11229.      8
+
11230.      8    await moduleFsInit();
+
11231.      1    consoleError = consoleError || console.error;
+
11232.      8    cwd = process.cwd().replace((
+
11233.      8        /\\/g
+
11234.      8    ), "/") + "/";
+
11235.      8
+
11236.      8// Init coverageDir.
+
11237.      8// Assert coverageDir is subdirectory of cwd.
+
11238.      8
+
11239.      8    assertOrThrow(coverageDir, "invalid coverageDir " + coverageDir);
+
11240.      8
+
11241.      8// CL-61b11012 - coverage - Relax requirement for coverageDir to be in cwd.
+
11242.      8//     assertOrThrow(
+
11243.      8//         pathnameRelativeCwd(coverageDir),
+
11244.      8//         "coverageDir " + coverageDir + " is not subdirectory of cwd " + cwd
+
11245.      8//     );
+
11246.      8
+
11247.      8    coverageDir = modulePath.resolve(coverageDir).replace((
+
11248.      8        /\\/g
+
11249.      8    ), "/") + "/";
+
11250.      8
+
11251.      8    processArgv = processArgv.slice();
+
11252.      9    while (processArgv[0] && processArgv[0][0] === "-") {
+
11253.      3        processArgElem = processArgv.shift().split("=");
+
11254.      3        processArgElem[1] = processArgElem.slice(1).join("=");
+
11255.      3        switch (processArgElem[0]) {
+
11256.      3
+
11257.      3// PR-371 - Add cli-option `--exclude=...`.
+
11258.      3
+
11259.      3        case "--exclude":
+
11260.      3            excludeList.push(processArgElem[1]);
+
11261.      3            break;
+
11262.      3
+
11263.      3// PR-371 - Add cli-option `--include=...`
+
11264.      3
+
11265.      3        case "--include":
+
11266.      3            includeList.push(processArgElem[1]);
+
11267.      3            break;
+
11268.      3
+
11269.      3// PR-400
+
11270.      3// Disable default-coverage of directory `node_modules`,
+
11271.      3// but allow override with cli-option `--include-node-modules=1`.
+
11272.      3
+
11273.      3        case "--include-node-modules":
+
11274.      3            modeIncludeNodeModules = !(
+
11275.      3                /0|false|null|undefined/
+
11276.      3            ).test(processArgElem[1]);
+
11277.      3            break;
+
11278.      3        }
+
11279.      7    }
+
11280.      7
+
11281.      7// 1. Spawn node.js program <processArgv> with coverage
+
11282.      7
+
11283.      7    if (processArgv.length > 0) {
+
11284.      6
+
11285.      6// Remove old coverage-files.
+
11286.      6
+
11287.      6        await fsWriteFileWithParents(coverageDir + "/touch.txt", "");
+
11288.      6        await Promise.all(Array.from(
+
11289.      6            await moduleFs.promises.readdir(coverageDir)
+
11290.     11        ).map(async function (file) {
+
11291.     11            if ((
+
11292.     11                /^coverage-\d+?-\d+?-\d+?\.json$/
+
11293.      6            ).test(file)) {
+
11294.      6                consoleError("rm file " + coverageDir + file);
+
11295.      6                await moduleFs.promises.unlink(coverageDir + file);
+
11296.      6            }
+
11297.     11        }));
+
11298.      6        exitCode = await new Promise(function (resolve) {
+
11299.      6            let processArgv0 = processArgv[0];
+
11300.      6
+
11301.      6// If win32 environment, then replace program npm with npm.cmd.
+
11302.      6// Coverage-hack - Ugly-hack to get test-coverage under both win32 and linux.
+
11303.      6
+
11304.      6            if (processArgv0 === "npm") {
+
11305.      6                processArgv0 = process.platform.replace(
+
11306.      6                    "win32",
+
11307.      6                    "npm.cmd"
+
11308.      6                ).replace(
+
11309.      6                    process.platform,
+
11310.      6                    "npm"
+
11311.      6                );
+
11312.      6            }
+
11313.      6            moduleChildProcess.spawn(
+
11314.      6                processArgv0,
+
11315.      6                processArgv.slice(1),
+
11316.      6                {
+
11317.      6                    env: Object.assign({}, process.env, {
+
11318.      6                        NODE_V8_COVERAGE: coverageDir
+
11319.      6                    }),
+
11320.      6
+
11321.      6// PR-465
+
11322.      6// https://nodejs.org/en/blog/vulnerability/april-2024-security-releases-2
+
11323.      6// Node.js will now error with EINVAL if a .bat or .cmd file is passed to
+
11324.      6// child_process.spawn and child_process.spawnSync without the shell option set.
+
11325.      6
+
11326.      6                    shell: (
+
11327.      6                        processArgv0.endsWith(".bat")
+
11328.      6                        || processArgv0.endsWith(".cmd")
+
11329.      6                    ),
+
11330.      6                    stdio: ["ignore", 1, 2]
+
11331.      6                }
+
11332.      6            ).on("exit", resolve);
+
11333.      6        });
+
11334.      6        consoleError(
+
11335.      6            `v8CoverageReportCreate - program exited with exitCode=${exitCode}`
+
11336.      6        );
+
11337.      7    }
+
11338.      7
+
11339.      7// 2. Merge JSON v8-coverage-files in <coverageDir>.
+
11340.      7
+
11341.      7    consoleError("v8CoverageReportCreate - merging coverage files...");
+
11342.      7    v8CoverageObj = await moduleFs.promises.readdir(coverageDir);
+
11343.     18    v8CoverageObj = v8CoverageObj.filter(function (file) {
+
11344.     18        return (
+
11345.     18            /^coverage-\d+?-\d+?-\d+?\.json$/
+
11346.     18        ).test(file);
+
11347.     18    });
+
11348.      7    v8CoverageObj = await Promise.all(v8CoverageObj.map(async function (file) {
+
11349.      7        let data;
+
11350.      7        let pathnameDict = Object.create(null);
+
11351.      7        data = await moduleFs.promises.readFile(coverageDir + file, "utf8");
+
11352.      7        data = JSON.parse(data);
+
11353.    473        data.result.forEach(function (scriptCov) {
+
11354.    473            let pathname = scriptCov.url;
+
11355.    473
+
11356.    473// Filter out internal coverages.
+
11357.    473
+
11358.    393            if (!pathname.startsWith("file:///")) {
+
11359.    393                return;
+
11360.    393            }
+
11361.     80
+
11362.     80// Normalize pathname.
+
11363.     80
+
11364.     80            pathname = moduleUrl.fileURLToPath(pathname);
+
11365.     80            pathname = modulePath.resolve(pathname).replace((
+
11366.     80                /\\/g
+
11367.     80            ), "/");
+
11368.     80
+
11369.     80// Filter files outside of cwd.
+
11370.     80
+
11371.     80            if (pathname.indexOf("[") >= 0 || !pathname.startsWith(cwd)) {
+
11372.     74                return;
+
11373.     74            }
+
11374.      7
+
11375.      7// Normalize pathname relative to cwd.
+
11376.      7
+
11377.      7            pathname = pathname.slice(cwd.length);
+
11378.      7            scriptCov.url = pathname;
+
11379.      7            pathnameDict[pathname] = scriptCov;
+
11380.      7        });
+
11381.      7
+
11382.      7// PR-400 - Filter directory `node_modules`.
+
11383.      7
+
11384.      7        if (!modeIncludeNodeModules) {
+
11385.      7            excludeList.push("node_modules/");
+
11386.      7        }
+
11387.      7
+
11388.      7// PR-400 - Filter files by glob-patterns in excludeList, includeList.
+
11389.      7
+
11390.      7        data.result = globExclude({
+
11391.      7            excludeList,
+
11392.      7            includeList,
+
11393.      7            pathnameList: Object.keys(pathnameDict)
+
11394.      7        }).pathnameList.map(function (pathname) {
+
11395.      7            return pathnameDict[pathname];
+
11396.      7        });
+
11397.      7        return data;
+
11398.      7    }));
+
11399.      7
+
11400.      7// Merge v8CoverageObj.
+
11401.      7
+
11402.      7    v8CoverageObj = v8CoverageListMerge(v8CoverageObj);
+
11403.      7
+
11404.      7// Debug v8CoverageObj.
+
11405.      7
+
11406.      7    await fsWriteFileWithParents(
+
11407.      7        coverageDir + "v8_coverage_merged.json",
+
11408.      7        JSON.stringify(v8CoverageObj, undefined, 1)
+
11409.      7    );
+
11410.      7
+
11411.      7// 3. Create html-coverage-reports in <coverageDir>.
+
11412.      7
+
11413.      7    consoleError("v8CoverageReportCreate - creating html-coverage-report...");
+
11414.      7    fileDict = Object.create(null);
+
11415.      7    await Promise.all(v8CoverageObj.result.map(async function ({
+
11416.      7        functions,
+
11417.      7        url: pathname
+
11418.      7    }) {
+
11419.      7        let lineList;
+
11420.      7        let linesCovered;
+
11421.      7        let linesTotal;
+
11422.      7        let source;
+
11423.      7        source = await moduleFs.promises.readFile(pathname, "utf8");
+
11424.      7        lineList = [{}];
+
11425.      7        source.replace((
+
11426.      7            /^.*$/gm
+
11427.  11853        ), function (line, startOffset) {
+
11428.  11853            lineList[lineList.length - 1].endOffset = startOffset - 1;
+
11429.  11853            lineList.push({
+
11430.  11853                count: -1,
+
11431.  11853                endOffset: 0,
+
11432.  11853                holeList: [],
+
11433.  11853                line,
+
11434.  11853                startOffset
+
11435.  11853            });
+
11436.  11853            return "";
+
11437.  11853        });
+
11438.      7        lineList.shift();
+
11439.      7        lineList[lineList.length - 1].endOffset = source.length;
+
11440.     41        functions.reverse().forEach(function ({
+
11441.     41            ranges
+
11442.     41        }) {
+
11443.     65            ranges.reverse().forEach(function ({
+
11444.     65                count,
+
11445.     65                endOffset,
+
11446.     65                startOffset
+
11447.     65            }, ii, list) {
+
11448. 580047                lineList.forEach(function (elem) {
+
11449. 580047                    if (!(
+
11450. 580047                        (
+
11451. 580047                            elem.startOffset <= startOffset
+
11452. 205728                            && startOffset <= elem.endOffset
+
11453. 579982                        ) || (
+
11454. 579982                            elem.startOffset <= endOffset
+
11455. 579982                            && endOffset <= elem.endOffset
+
11456. 579982                        ) || (
+
11457. 579926                            startOffset <= elem.startOffset
+
11458. 579926                            && elem.endOffset <= endOffset
+
11459. 579926                        )
+
11460. 556497                    )) {
+
11461. 556497                        return;
+
11462. 556497                    }
+
11463.  23550
+
11464.  23550// Handle tree-root.
+
11465.  23550
+
11466.  23550                    if (ii + 1 === list.length) {
+
11467.  23281                        if (elem.count === -1) {
+
11468.  23281                            elem.count = count;
+
11469.  23281                        }
+
11470.  23281                        return;
+
11471.  23281                    }
+
11472.    269
+
11473.    269// Handle tree-children.
+
11474.    269
+
11475.    269                    if (elem.count !== 0) {
+
11476.    170                        elem.count = Math.max(count, elem.count);
+
11477.    269                    }
+
11478.    269                    if (count === 0) {
+
11479.    203                        elem.count = 0;
+
11480.    203                        elem.holeList.push([
+
11481.    203                            startOffset, endOffset
+
11482.    203                        ]);
+
11483.    203                    }
+
11484. 580047                });
+
11485.     65            });
+
11486.     41        });
+
11487.      7        linesTotal = lineList.length;
+
11488.  11853        linesCovered = lineList.filter(function ({
+
11489.  11853            count
+
11490.  11853        }) {
+
11491.  11853            return count > 0;
+
11492.  11853        }).length;
+
11493.      7        await moduleFs.promises.mkdir((
+
11494.      7            modulePath.dirname(coverageDir + pathname)
+
11495.      7        ), {
+
11496.      7            recursive: true
+
11497.      7        });
+
11498.      7        fileDict[pathname] = {
+
11499.      7            lineList,
+
11500.      7            linesCovered,
+
11501.      7            linesTotal,
+
11502.      7            modeCoverageIgnoreFile: (
+
11503.      7                (
+
11504.      7                    /^\/\*coverage-ignore-file\*\/$/m
+
11505.      7                ).test(source.slice(0, 65536))
+
11506.      7                ? "(ignore)"
+
11507.      7                : ""
+
11508.      7            ),
+
11509.      7            pathname
+
11510.      7        };
+
11511.      7        htmlRender({
+
11512.      7            fileList: [
+
11513.      7                fileDict[pathname]
+
11514.      7            ],
+
11515.      7            lineList,
+
11516.      7            pathname: coverageDir + pathname
+
11517.      7        });
+
11518.      7    }));
+
11519.      7    htmlRender({
+
11520.      7        fileList: Object.keys(fileDict).sort().map(function (pathname) {
+
11521.      7            return fileDict[pathname];
+
11522.      7        }),
+
11523.      7        modeIndex: true,
+
11524.      7        pathname: coverageDir + "index"
+
11525.      7    });
+
11526.      7    await Promise.all(promiseList);
+
11527.      7    assertOrThrow(
+
11528.      7        exitCode === 0,
+
11529.      7        "v8CoverageReportCreate - nonzero exitCode " + exitCode
+
11530.      7    );
+
11531.      7}
+
11532.      1
+
11533.      1/*
+
11534.      1function sentinel() {}
+
11535.      1*/
+
11536.      1
+
11537.      1// Export jslint as cjs/esm.
+
11538.      1
+
11539.      1jslint_export = Object.freeze(Object.assign(jslint, {
+
11540.      1    assertErrorThrownAsync,
+
11541.      1    assertJsonEqual,
+
11542.      1    assertOrThrow,
+
11543.      1    debugInline,
+
11544.      1    fsWriteFileWithParents,
+
11545.      1    globExclude,
+
11546.      1    htmlEscape,
+
11547.      1    jslint,
+
11548.      1    jslint_apidoc,
+
11549.      1    jslint_assert,
+
11550.      1    jslint_charset_ascii,
+
11551.      1    jslint_cli,
+
11552.      1    jslint_edition,
+
11553.      1    jslint_phase1_split,
+
11554.      1    jslint_phase2_lex,
+
11555.      1    jslint_phase3_parse,
+
11556.      1    jslint_phase4_walk,
+
11557.      1    jslint_phase5_whitage,
+
11558.      1    jslint_report,
+
11559.      1    jstestDescribe,
+
11560.      1    jstestIt,
+
11561.      1    jstestOnExit,
+
11562.      1    moduleFsInit,
+
11563.      1    noop,
+
11564.      1    objectDeepCopyWithKeysSorted,
+
11565.      1    v8CoverageListMerge,
+
11566.      1    v8CoverageReportCreate
+
11567.      1}));
+
11568.      1// module.exports = jslint_export;              // Export jslint as cjs.
+
11569.      1export default Object.freeze(jslint_export);    // Export jslint as esm.
+
11570.      1jslint_import_meta_url = import.meta.url;
+
11571.      1
+
11572.      1// Run jslint_cli.
+
11573.      1jslint_cli({});
+
11574.      1
+
+ + + + diff --git a/.artifact/coverage/jslint_wrapper_cjs.cjs.html b/.artifact/coverage/jslint_wrapper_cjs.cjs.html new file mode 100644 index 000000000..7e2512946 --- /dev/null +++ b/.artifact/coverage/jslint_wrapper_cjs.cjs.html @@ -0,0 +1,217 @@ + + + +V8 Coverage Report + + + + +
+
V8 Coverage Report
+ + + + + + + + + + + + + + + +
Files coveredLinesRemaining
+ . / jslint_wrapper_cjs.cjs
+
+
+
+
+ 100.00 %
+ 49 / 49 +
+
+ 0 / 49 +
+
+ + +
+
    1.      1// The Unlicense
+
    2.      1//
+
    3.      1// This is free and unencumbered software released into the public domain.
+
    4.      1//
+
    5.      1// Anyone is free to copy, modify, publish, use, compile, sell, or
+
    6.      1// distribute this software, either in source code form or as a compiled
+
    7.      1// binary, for any purpose, commercial or non-commercial, and by any
+
    8.      1// means.
+
    9.      1//
+
   10.      1// In jurisdictions that recognize copyright laws, the author or authors
+
   11.      1// of this software dedicate any and all copyright interest in the
+
   12.      1// software to the public domain. We make this dedication for the benefit
+
   13.      1// of the public at large and to the detriment of our heirs and
+
   14.      1// successors. We intend this dedication to be an overt act of
+
   15.      1// relinquishment in perpetuity of all present and future rights to this
+
   16.      1// software under copyright law.
+
   17.      1//
+
   18.      1// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+
   19.      1// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+
   20.      1// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+
   21.      1// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+
   22.      1// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+
   23.      1// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+
   24.      1// OTHER DEALINGS IN THE SOFTWARE.
+
   25.      1
+
   26.      1
+
   27.      1/*jslint beta, node*/
+
   28.      1/*property
+
   29.      1    module, readFileSync, replace, runInNewContext
+
   30.      1*/
+
   31.      1require("vm").runInNewContext(
+
   32.      1    (
+
   33.      1        "\"use strict\";"
+
   34.      1        + require("fs").readFileSync(
+
   35.      1            __dirname + "/jslint.mjs",
+
   36.      1            "utf8"
+
   37.      1        ).replace(
+
   38.      1            "\nexport default Object.freeze(jslint_export);",
+
   39.      1            "\nmodule.exports = jslint_export;"
+
   40.      1        ).replace(
+
   41.      1            "\njslint_import_meta_url = import.meta.url;",
+
   42.      1            "\n// jslint_import_meta_url = import.meta.url;"
+
   43.      1        )
+
   44.      1    ),
+
   45.      1    {
+
   46.      1        module
+
   47.      1    }
+
   48.      1);
+
   49.      1
+
+ + + + diff --git a/.artifact/coverage/test.mjs.html b/.artifact/coverage/test.mjs.html new file mode 100644 index 000000000..455e80af5 --- /dev/null +++ b/.artifact/coverage/test.mjs.html @@ -0,0 +1,1744 @@ + + + +V8 Coverage Report + + + + +
+
V8 Coverage Report
+ + + + + + + + + + + + + + + +
Files coveredLinesRemaining
+ . / test.mjs
+
+
+
+
+ 100.00 %
+ 1576 / 1576 +
+
+ 0 / 1576 +
+
+ + +
+
    1.      1/*jslint beta, node*/
+
    2.      1import jslint from "./jslint.mjs";
+
    3.      1import jslintCjs from "./jslint_wrapper_cjs.cjs";
+
    4.      1import moduleFs from "fs";
+
    5.      1import modulePath from "path";
+
    6.      1
+
    7.      1let {
+
    8.      1    assertErrorThrownAsync,
+
    9.      1    assertJsonEqual,
+
   10.      1    assertOrThrow,
+
   11.      1    debugInline,
+
   12.      1    fsWriteFileWithParents,
+
   13.      1    globExclude,
+
   14.      1    jstestDescribe,
+
   15.      1    jstestIt,
+
   16.      1    jstestOnExit,
+
   17.      1    moduleFsInit,
+
   18.      1    noop,
+
   19.      1    v8CoverageListMerge,
+
   20.      1    v8CoverageReportCreate
+
   21.      1} = jslint;
+
   22.      1let sourceJslintMjs;
+
   23.      1let testCoverageMergeData;
+
   24.      1
+
   25.      1await (async function init() {
+
   26.      1
+
   27.      1// Coverage-hack - Ugly-hack to get test-coverage for all initialization-states.
+
   28.      1
+
   29.      1    moduleFsInit();
+
   30.      1    moduleFsInit();
+
   31.      1
+
   32.      1// Cleanup directory .tmp
+
   33.      1
+
   34.      1    await moduleFs.promises.rm(".tmp", {
+
   35.      1        recursive: true
+
   36.      1    }).catch(noop);
+
   37.      1
+
   38.      1// init sourceJslintMjs
+
   39.      1
+
   40.      1    sourceJslintMjs = await moduleFs.promises.readFile("jslint.mjs", "utf8");
+
   41.      1
+
   42.      1// init testCoverageMergeData
+
   43.      1
+
   44.      1    testCoverageMergeData = JSON.parse(
+
   45.      1        await moduleFs.promises.readFile(
+
   46.      1            "test_coverage_merge_data.json",
+
   47.      1            "utf8"
+
   48.      1        )
+
   49.      1    );
+
   50.      1}());
+
   51.      1
+
   52.      1jstestDescribe((
+
   53.      1    "test fsXxx handling-behavior"
+
   54.      1), function testBehaviorFsXxx() {
+
   55.      1    jstestIt((
+
   56.      1        "test fsWriteFileWithParents handling-behavior"
+
   57.      1    ), async function () {
+
   58.      1        await Promise.all([
+
   59.      1            1, 2, 3, 4
+
   60.      4        ].map(async function () {
+
   61.      4            await fsWriteFileWithParents(
+
   62.      4                ".tmp/fsWriteFileWithParents/aa/bb/cc",
+
   63.      4                "aa"
+
   64.      4            );
+
   65.      4        }));
+
   66.      1        assertJsonEqual(
+
   67.      1            await moduleFs.promises.readFile(
+
   68.      1                ".tmp/fsWriteFileWithParents/aa/bb/cc",
+
   69.      1                "utf8"
+
   70.      1            ),
+
   71.      1            "aa"
+
   72.      1        );
+
   73.      1    });
+
   74.      1});
+
   75.      1
+
   76.      1jstestDescribe((
+
   77.      1    "test globXxx handling-behavior"
+
   78.      1), function testBehaviorGlobXxx() {
+
   79.      1    jstestIt((
+
   80.      1        "test globAssertNotWeird-error handling-behavior"
+
   81.      1    ), async function () {
+
   82.      1        await Promise.all([
+
   83.      1            "\n",
+
   84.      1            "\r",
+
   85.      1            "\u0000"
+
   86.      3        ].map(async function (char) {
+
   87.      3            await assertErrorThrownAsync(function () {
+
   88.      3                return globExclude({
+
   89.      3                    pathnameList: [
+
   90.      3                        "aa",
+
   91.      3                        `cc/${char}/dd`,
+
   92.      3                        "bb"
+
   93.      3                    ]
+
   94.      3                });
+
   95.      3            }, (
+
   96.      3                "Weird character "
+
   97.      3                + JSON.stringify(char).replace("\\", "\\\\")
+
   98.      3                + " found in "
+
   99.      3            ));
+
  100.      3        }));
+
  101.      1    });
+
  102.      1    jstestIt((
+
  103.      1        "test globExclude handling-behavior"
+
  104.      1    ), function () {
+
  105.      1        let pathnameList = [
+
  106.      1            ".dockerignore",
+
  107.      1            ".eslintrc.js",
+
  108.      1            ".gitignore",
+
  109.      1            ".npmignore",
+
  110.      1            ".travis.yml",
+
  111.      1            "/node_modules/aa/bb/cc.js",
+
  112.      1            "/node_modules/aa/bb/dd.js",
+
  113.      1            "CHANGELOG.md",
+
  114.      1            "CONTRIBUTING.md",
+
  115.      1            "Dockerfile",
+
  116.      1            "LICENSE",
+
  117.      1            "Makefile",
+
  118.      1            "README.md",
+
  119.      1            "appveyor.yml",
+
  120.      1            "benchmark/insert-transaction.sql",
+
  121.      1            "benchmark/insert.js",
+
  122.      1            "binding.gyp",
+
  123.      1            "cloudformation/ci.template.js",
+
  124.      1            "deps/common-sqlite.gypi",
+
  125.      1            "deps/extract.py",
+
  126.      1            "deps/sqlite-autoconf-3340000.tar.gz",
+
  127.      1            "deps/sqlite3.gyp",
+
  128.      1            "examples/simple-chaining.js",
+
  129.      1            "lib/index.js",
+
  130.      1            "lib/sqlite3-binding.js",
+
  131.      1            "lib/sqlite3.js",
+
  132.      1            "lib/trace.js",
+
  133.      1            "node_modules/aa/bb/cc.js",
+
  134.      1            "node_modules/aa/bb/dd.js",
+
  135.      1            "package.json",
+
  136.      1            "scripts/build-appveyor.bat",
+
  137.      1            "scripts/build-local.bat",
+
  138.      1            "scripts/build_against_electron.sh",
+
  139.      1            "scripts/build_against_node.sh",
+
  140.      1            "scripts/build_against_node_webkit.sh",
+
  141.      1            "scripts/build_for_node_webkit.cmd",
+
  142.      1            "scripts/install_node.sh",
+
  143.      1            "scripts/validate_tag.sh",
+
  144.      1            "sqlite3.js",
+
  145.      1            "src/async.h",
+
  146.      1            "src/backup.cc",
+
  147.      1            "src/backup.h",
+
  148.      1            "src/database.cc",
+
  149.      1            "src/database.h",
+
  150.      1            "src/gcc-preinclude.h",
+
  151.      1            "src/macros.h",
+
  152.      1            "src/node_sqlite3.cc",
+
  153.      1            "src/statement.cc",
+
  154.      1            "src/statement.h",
+
  155.      1            "src/threading.h",
+
  156.      1            "test/affected.test.js",
+
  157.      1            "test/backup.test.js",
+
  158.      1            "test/blob.test.js",
+
  159.      1            "test/cache.test.js",
+
  160.      1            "test/constants.test.js",
+
  161.      1            "test/database_fail.test.js",
+
  162.      1            "test/each.test.js",
+
  163.      1            "test/exec.test.js",
+
  164.      1            "test/extension.test.js",
+
  165.      1            "test/fts-content.test.js",
+
  166.      1            "test/interrupt.test.js",
+
  167.      1            "test/issue-108.test.js",
+
  168.      1            "test/json.test.js",
+
  169.      1            "test/map.test.js",
+
  170.      1            "test/named_columns.test.js",
+
  171.      1            "test/named_params.test.js",
+
  172.      1            "test/null_error.test.js",
+
  173.      1            "test/nw/.gitignore",
+
  174.      1            "test/nw/Makefile",
+
  175.      1            "test/nw/index.html",
+
  176.      1            "test/nw/package.json",
+
  177.      1            "test/open_close.test.js",
+
  178.      1            "test/other_objects.test.js",
+
  179.      1            "test/parallel_insert.test.js",
+
  180.      1            "test/prepare.test.js",
+
  181.      1            "test/profile.test.js",
+
  182.      1            "test/rerun.test.js",
+
  183.      1            "test/scheduling.test.js",
+
  184.      1            "test/serialization.test.js",
+
  185.      1            "test/support/createdb-electron.js",
+
  186.      1            "test/support/createdb.js",
+
  187.      1            "test/support/elmo.png",
+
  188.      1            "test/support/helper.js",
+
  189.      1            "test/support/prepare.db",
+
  190.      1            "test/support/script.sql",
+
  191.      1            "test/trace.test.js",
+
  192.      1            "test/unicode.test.js",
+
  193.      1            "test/upsert.test.js",
+
  194.      1            "test/verbose.test.js",
+
  195.      1            "tools/docker/architecture/linux-arm/Dockerfile",
+
  196.      1            "tools/docker/architecture/linux-arm/run.sh",
+
  197.      1            "tools/docker/architecture/linux-arm64/Dockerfile",
+
  198.      1            "tools/docker/architecture/linux-arm64/run.sh"
+
  199.      1        ];
+
  200.      1        [
+
  201.      1            "tes?/",
+
  202.      1            "tes[-t-]/",
+
  203.      1            "tes[-t]/",
+
  204.      1            "tes[0-9A-Z_a-z-]/",
+
  205.      1            "tes[t-]/",
+
  206.      1            "test/**/*.js"
+
  207.      6        ].forEach(function (aa) {
+
  208.      6            [
+
  209.      6                "li*/*.js",
+
  210.      6                "li?/*.js",
+
  211.      6                "lib/",
+
  212.      6                "lib/*",
+
  213.      6                "lib/**/*.js",
+
  214.      6                "lib/*.js"
+
  215.     36            ].forEach(function (bb) {
+
  216.     36                [
+
  217.     36                    "",
+
  218.     36                    "**/node_modules/",
+
  219.     36                    "node_modules/"
+
  220.    108                ].forEach(function (cc) {
+
  221.    108                    assertJsonEqual(
+
  222.    108                        globExclude({
+
  223.    108                            excludeList: [
+
  224.    108                                "tes[!0-9A-Z_a-z-]/",
+
  225.    108                                "tes[^0-9A-Z_a-z-]/",
+
  226.    108                                "test/suppor*/*elper.js",
+
  227.    108                                "test/suppor?/?elper.js",
+
  228.    108                                "test/support/helper.js"
+
  229.    108                            ].concat(aa, cc),
+
  230.    108                            includeList: [
+
  231.    108                                "**/*.cjs",
+
  232.    108                                "**/*.js",
+
  233.    108                                "**/*.mjs",
+
  234.    108                                "lib/sqlite3.js"
+
  235.    108                            ].concat(bb),
+
  236.    108                            pathnameList
+
  237.    108                        }).pathnameList,
+
  238.    108                        [
+
  239.    108                            ".eslintrc.js",
+
  240.    108                            "benchmark/insert.js",
+
  241.    108                            "cloudformation/ci.template.js",
+
  242.    108                            "examples/simple-chaining.js",
+
  243.    108                            "lib/index.js",
+
  244.    108                            "lib/sqlite3-binding.js",
+
  245.    108                            "lib/sqlite3.js",
+
  246.    108                            "lib/trace.js",
+
  247.    108                            "sqlite3.js"
+
  248.    108                        ].concat(
+
  249.    108                            cc === "**/node_modules/"
+
  250.     36                            ? [
+
  251.     36                                "node_modules/aa/bb/cc.js",
+
  252.     36                                "node_modules/aa/bb/dd.js"
+
  253.     36                            ]
+
  254.     72                            : cc === "node_modules/"
+
  255.     72                            ? [
+
  256.     72                                "/node_modules/aa/bb/cc.js",
+
  257.     72                                "/node_modules/aa/bb/dd.js"
+
  258.     72                            ]
+
  259.     72                            : [
+
  260.     72                                "/node_modules/aa/bb/cc.js",
+
  261.     72                                "/node_modules/aa/bb/dd.js",
+
  262.     72                                "node_modules/aa/bb/cc.js",
+
  263.     72                                "node_modules/aa/bb/dd.js"
+
  264.     72                            ]
+
  265.    108                        ).sort()
+
  266.    108                    );
+
  267.    108                });
+
  268.     36            });
+
  269.      6        });
+
  270.      1    });
+
  271.      1    jstestIt((
+
  272.      1        "test globToRegexp handling-behavior"
+
  273.      1    ), function () {
+
  274.      1        Object.entries({
+
  275.      1            "*": (
+
  276.      1                /^[^\/]*?$/gm
+
  277.      1            ),
+
  278.      1            "**": (
+
  279.      1                /^.*?$/gm
+
  280.      1            ),
+
  281.      1            "***": (
+
  282.      1                /^.*?$/gm
+
  283.      1            ),
+
  284.      1            "****": (
+
  285.      1                /^.*?$/gm
+
  286.      1            ),
+
  287.      1            "****////****": (
+
  288.      1                /^.*?$/gm
+
  289.      1            ),
+
  290.      1            "***///***": (
+
  291.      1                /^.*?$/gm
+
  292.      1            ),
+
  293.      1            "**/*": (
+
  294.      1                /^.*?$/gm
+
  295.      1            ),
+
  296.      1            "**/node_modules/": (
+
  297.      1                /^.*?\/node_modules\/.*?$/gm
+
  298.      1            ),
+
  299.      1            "**/node_modules/**/*": (
+
  300.      1                /^.*?\/node_modules\/.*?$/gm
+
  301.      1            ),
+
  302.      1            "?": (
+
  303.      1                /^[^\/]$/gm
+
  304.      1            ),
+
  305.      1            "[!0-9A-Za-z-]": (
+
  306.      1                /^[^0-9A-Za-z\-]$/gm
+
  307.      1            ),
+
  308.      1            "[0-9A-Za-z-]": (
+
  309.      1                /^[0-9A-Za-z\-]$/gm
+
  310.      1            ),
+
  311.      1            "[[]] ]][[": (
+
  312.      1                /^[\[]\] \]\][\[]$/gm
+
  313.      1            ),
+
  314.      1            "[]": (
+
  315.      1                /^$/gm
+
  316.      1            ),
+
  317.      1            "[^0-9A-Za-z-]": (
+
  318.      1                /^[^0-9A-Za-z\-]$/gm
+
  319.      1            ),
+
  320.      1            "aa/bb/cc": (
+
  321.      1                /^aa\/bb\/cc$/gm
+
  322.      1            ),
+
  323.      1            "aa/bb/cc/": (
+
  324.      1                /^aa\/bb\/cc\/.*?$/gm
+
  325.      1            ),
+
  326.      1            "li*/*": (
+
  327.      1                /^li[^\/]*?\/[^\/]*?$/gm
+
  328.      1            ),
+
  329.      1            "li?/*": (
+
  330.      1                /^li[^\/]\/[^\/]*?$/gm
+
  331.      1            ),
+
  332.      1            "lib/": (
+
  333.      1                /^lib\/.*?$/gm
+
  334.      1            ),
+
  335.      1            "lib/*": (
+
  336.      1                /^lib\/[^\/]*?$/gm
+
  337.      1            ),
+
  338.      1            "lib/**/*.js": (
+
  339.      1                /^lib\/.*?\.js$/gm
+
  340.      1            ),
+
  341.      1            "lib/*.js": (
+
  342.      1                /^lib\/[^\/]*?\.js$/gm
+
  343.      1            ),
+
  344.      1            "node_modules/": (
+
  345.      1                /^node_modules\/.*?$/gm
+
  346.      1            ),
+
  347.      1            "node_modules/**/*": (
+
  348.      1                /^node_modules\/.*?$/gm
+
  349.      1            ),
+
  350.      1            "tes[!0-9A-Z_a-z-]/**/*": (
+
  351.      1                /^tes[^0-9A-Z_a-z\-]\/.*?$/gm
+
  352.      1            ),
+
  353.      1            "tes[0-9A-Z_a-z-]/**/*": (
+
  354.      1                /^tes[0-9A-Z_a-z\-]\/.*?$/gm
+
  355.      1            ),
+
  356.      1            "tes[^0-9A-Z_a-z-]/**/*": (
+
  357.      1                /^tes[^0-9A-Z_a-z\-]\/.*?$/gm
+
  358.      1            ),
+
  359.      1            "test/**/*": (
+
  360.      1                /^test\/.*?$/gm
+
  361.      1            ),
+
  362.      1            "test/**/*.js": (
+
  363.      1                /^test\/.*?\.js$/gm
+
  364.      1            ),
+
  365.      1            "test/suppor*/*elper.js": (
+
  366.      1                /^test\/suppor[^\/]*?\/[^\/]*?elper\.js$/gm
+
  367.      1            ),
+
  368.      1            "test/suppor?/?elper.js": (
+
  369.      1                /^test\/suppor[^\/]\/[^\/]elper\.js$/gm
+
  370.      1            ),
+
  371.      1            "test/support/helper.js": (
+
  372.      1                /^test\/support\/helper\.js$/gm
+
  373.      1            )
+
  374.     33        }).forEach(function ([
+
  375.     33            pattern, rgx
+
  376.     33        ]) {
+
  377.     33            assertJsonEqual(
+
  378.     33                globExclude({
+
  379.     33                    excludeList: [
+
  380.     33                        pattern
+
  381.     33                    ]
+
  382.     33                }).excludeList[0].source,
+
  383.     33                rgx.source
+
  384.     33            );
+
  385.     33            assertJsonEqual(
+
  386.     33                globExclude({
+
  387.     33                    includeList: [
+
  388.     33                        pattern
+
  389.     33                    ]
+
  390.     33                }).includeList[0].source,
+
  391.     33                rgx.source
+
  392.     33            );
+
  393.     33        });
+
  394.      1    });
+
  395.      1});
+
  396.      1
+
  397.      1jstestDescribe((
+
  398.      1    "test jslint's cli handling-behavior"
+
  399.      1), function testBehaviorJslintCli() {
+
  400.      5    function processExit0(exitCode) {
+
  401.      5        assertOrThrow(exitCode === 0, exitCode);
+
  402.      5    }
+
  403.      6    function processExit1(exitCode) {
+
  404.      6        assertOrThrow(exitCode === 1, exitCode);
+
  405.      6    }
+
  406.      1    jstestIt((
+
  407.      1        "test cli-null-case handling-behavior"
+
  408.      1    ), function () {
+
  409.      1        jslint.jslint_cli({
+
  410.      1            mode_noop: true,
+
  411.      1            process_exit: processExit0
+
  412.      1        });
+
  413.      1    });
+
  414.      1    jstestIt((
+
  415.      1        "test cli-window-jslint handling-behavior"
+
  416.      1    ), function () {
+
  417.      1        [
+
  418.      1            "&window_jslint=",
+
  419.      1            "&window_jslint=12",
+
  420.      1            "&window_jslint=1?",
+
  421.      1            "&window_jslint=?",
+
  422.      1            "?window_jslint=",
+
  423.      1            "?window_jslint=12",
+
  424.      1            "?window_jslint=1?",
+
  425.      1            "?window_jslint=?",
+
  426.      1            "window_jslint=1",
+
  427.      1            "window_jslint=1&",
+
  428.      1            "window_jslint=12",
+
  429.      1            "window_jslint=1?"
+
  430.     12        ].forEach(function (import_meta_url) {
+
  431.     12            jslint.jslint_cli({
+
  432.     12                import_meta_url
+
  433.     12            });
+
  434.     12            assertOrThrow(globalThis.jslint === undefined);
+
  435.     12        });
+
  436.      1        [
+
  437.      1            "&window_jslint=1",
+
  438.      1            "&window_jslint=1&",
+
  439.      1            "?window_jslint=1",
+
  440.      1            "?window_jslint=1&"
+
  441.      4        ].forEach(function (import_meta_url) {
+
  442.      4            jslint.jslint_cli({
+
  443.      4                import_meta_url
+
  444.      4            });
+
  445.      4            assertOrThrow(globalThis.jslint === jslint);
+
  446.      4            delete globalThis.jslint;
+
  447.      4        });
+
  448.      1    });
+
  449.      1    jstestIt((
+
  450.      1        "test cli-cjs-and-invalid-file handling-behavior"
+
  451.      1    ), async function () {
+
  452.      1        await fsWriteFileWithParents(".test_dir.cjs/touch.txt", "");
+
  453.      1        [
+
  454.      1            ".",            // test dir handling-behavior
+
  455.      1            "jslint.mjs",   // test file handling-behavior
+
  456.      1            undefined       // test file-undefined handling-behavior
+
  457.      3        ].forEach(function (file) {
+
  458.      3            jslint.jslint_cli({
+
  459.      3                file,
+
  460.      3                mode_cli: true,
+
  461.      3                process_env: {
+
  462.      3                    JSLINT_BETA: "1"
+
  463.      3                },
+
  464.      3                process_exit: processExit0
+
  465.      3            });
+
  466.      3        });
+
  467.      1    });
+
  468.      1    jstestIt((
+
  469.      1        "test cli-apidoc handling-behavior"
+
  470.      1    ), function () {
+
  471.      1        jslint.jslint_cli({
+
  472.      1            mode_cli: true,
+
  473.      1            process_argv: [
+
  474.      1                "node",
+
  475.      1                "jslint.mjs",
+
  476.      1                "jslint_apidoc=.artifact/apidoc.html",
+
  477.      1                JSON.stringify({
+
  478.      1                    example_list: [
+
  479.      1                        "README.md",
+
  480.      1                        "test.mjs",
+
  481.      1                        "jslint.mjs"
+
  482.      1                    ],
+
  483.      1                    github_repo: "https://github.com/jslint-org/jslint",
+
  484.      1                    module_list: [
+
  485.      1                        {
+
  486.      1                            pathname: "./jslint.mjs"
+
  487.      1                        }
+
  488.      1                    ],
+
  489.      1                    package_name: "JSLint",
+
  490.      1                    version: jslint.jslint_edition
+
  491.      1                })
+
  492.      1            ],
+
  493.      1            process_exit: processExit0
+
  494.      1        });
+
  495.      1    });
+
  496.      1    jstestIt((
+
  497.      1        "test cli-file-error handling-behavior"
+
  498.      1    ), function () {
+
  499.      1        jslint.jslint_cli({
+
  500.      1            // suppress error
+
  501.      1            console_error: noop,
+
  502.      1            file: "undefined",
+
  503.      1            mode_cli: true,
+
  504.      1            process_exit: processExit1
+
  505.      1        });
+
  506.      1    });
+
  507.      1    jstestIt((
+
  508.      1        "test cli-syntax-error handling-behavior"
+
  509.      1    ), function () {
+
  510.      1        jslint.jslint_cli({
+
  511.      1            // suppress error
+
  512.      1            console_error: noop,
+
  513.      1            file: "syntax-error.js",
+
  514.      1            mode_cli: true,
+
  515.      1            option: {
+
  516.      1                trace: true
+
  517.      1            },
+
  518.      1            process_exit: processExit1,
+
  519.      1            source: "syntax error"
+
  520.      1        });
+
  521.      1    });
+
  522.      1    jstestIt((
+
  523.      1        "test cli-report handling-behavior"
+
  524.      1    ), function () {
+
  525.      1        jslint.jslint_cli({
+
  526.      1            // suppress error
+
  527.      1            console_error: noop,
+
  528.      1            mode_cli: true,
+
  529.      1            process_argv: [
+
  530.      1                "node",
+
  531.      1                "jslint.mjs",
+
  532.      1                "jslint_report=.tmp/jslint_report.html",
+
  533.      1                "jslint.mjs"
+
  534.      1            ],
+
  535.      1            process_exit: processExit0
+
  536.      1        });
+
  537.      1    });
+
  538.      1    jstestIt((
+
  539.      1        "test cli-report-error handling-behavior"
+
  540.      1    ), function () {
+
  541.      1        jslint.jslint_cli({
+
  542.      1            // suppress error
+
  543.      1            console_error: noop,
+
  544.      1            mode_cli: true,
+
  545.      1            process_argv: [
+
  546.      1                "node",
+
  547.      1                "jslint.mjs",
+
  548.      1                "jslint_report=.tmp/jslint_report.html",
+
  549.      1                "syntax-error.js"
+
  550.      1            ],
+
  551.      1            process_exit: processExit1,
+
  552.      1            source: "syntax error"
+
  553.      1        });
+
  554.      1    });
+
  555.      1    jstestIt((
+
  556.      1        "test cli-report-json handling-behavior"
+
  557.      1    ), function () {
+
  558.      1        jslint.jslint_cli({
+
  559.      1            // suppress error
+
  560.      1            console_error: noop,
+
  561.      1            mode_cli: true,
+
  562.      1            process_argv: [
+
  563.      1                "node",
+
  564.      1                "jslint.mjs",
+
  565.      1                "jslint_report=.tmp/jslint_report.html",
+
  566.      1                "aa.json"
+
  567.      1            ],
+
  568.      1            process_exit: processExit0,
+
  569.      1            source: "[]"
+
  570.      1        });
+
  571.      1    });
+
  572.      1    jstestIt((
+
  573.      1        "test cli-report-json-error handling-behavior"
+
  574.      1    ), function () {
+
  575.      1        jslint.jslint_cli({
+
  576.      1            // suppress error
+
  577.      1            console_error: noop,
+
  578.      1            mode_cli: true,
+
  579.      1            process_argv: [
+
  580.      1                "node",
+
  581.      1                "jslint.mjs",
+
  582.      1                "jslint_report=.tmp/jslint_report.html",
+
  583.      1                "aa.json"
+
  584.      1            ],
+
  585.      1            process_exit: processExit1,
+
  586.      1            source: "["
+
  587.      1        });
+
  588.      1    });
+
  589.      1    jstestIt((
+
  590.      1        "test cli-report-misc handling-behavior"
+
  591.      1    ), function () {
+
  592.      1        jslint.jslint_cli({
+
  593.      1            // suppress error
+
  594.      1            console_error: noop,
+
  595.      1            mode_cli: true,
+
  596.      1            process_argv: [
+
  597.      1                "node",
+
  598.      1                "jslint.mjs",
+
  599.      1                "jslint_report=.tmp/jslint_report.html",
+
  600.      1                "aa.js"
+
  601.      1            ],
+
  602.      1            process_exit: processExit0,
+
  603.      1            source: "let aa = 0;"
+
  604.      1        });
+
  605.      1        jslint.jslint_cli({
+
  606.      1            // suppress error
+
  607.      1            console_error: noop,
+
  608.      1            mode_cli: true,
+
  609.      1            process_argv: [
+
  610.      1                "node",
+
  611.      1                "jslint.mjs",
+
  612.      1                "jslint_report=.tmp/jslint_report.html",
+
  613.      1                "aa.js"
+
  614.      1            ],
+
  615.      1            process_exit: processExit1,
+
  616.      1            source: "(aa)=>aa; function aa([aa]){}"
+
  617.      1        });
+
  618.      1    });
+
  619.      1    jstestIt((
+
  620.      1        "test cli-jslint-wrapper-vim handling-behavior"
+
  621.      1    ), function () {
+
  622.      1        jslint.jslint_cli({
+
  623.      1            // suppress error
+
  624.      1            console_error: noop,
+
  625.      1            mode_cli: true,
+
  626.      1            process_argv: [
+
  627.      1                "node",
+
  628.      1                "jslint.mjs",
+
  629.      1                "jslint_wrapper_vim",
+
  630.      1                "syntax-error.js"
+
  631.      1            ],
+
  632.      1            process_exit: processExit1,
+
  633.      1            source: "syntax error"
+
  634.      1        });
+
  635.      1    });
+
  636.      1});
+
  637.      1
+
  638.      1jstestDescribe((
+
  639.      1    "test jslint's no-warnings handling-behavior"
+
  640.      1), function testBehaviorJslintNoWarnings() {
+
  641.      1    jstestIt((
+
  642.      1        "test jslint's no-warnings handling-behavior"
+
  643.      1    ), function () {
+
  644.      1        Object.values({
+
  645.      1            array: [
+
  646.      1                "new Array(0);"
+
  647.      1            ],
+
  648.      1            async_await: [
+
  649.      1                "async function aa() {\n    await aa();\n}",
+
  650.      1
+
  651.      1// PR-405 - Bugfix - fix expression after "await" mis-identified as statement.
+
  652.      1
+
  653.      1                "async function aa() {\n    await aa;\n}",
+
  654.      1                (
+
  655.      1                    "async function aa() {\n"
+
  656.      1                    + "    try {\n"
+
  657.      1                    + "        aa();\n"
+
  658.      1                    + "    } catch (err) {\n"
+
  659.      1                    + "        await err();\n"
+
  660.      1                    + "    }\n"
+
  661.      1                    + "}\n"
+
  662.      1                ),
+
  663.      1                (
+
  664.      1                    "async function aa() {\n"
+
  665.      1                    + "    try {\n"
+
  666.      1                    + "        await aa();\n"
+
  667.      1                    + "    } catch (err) {\n"
+
  668.      1                    + "        await err();\n"
+
  669.      1                    + "    }\n"
+
  670.      1                    + "}\n"
+
  671.      1                ),
+
  672.      1
+
  673.      1// PR-370 - Add top-level-await support.
+
  674.      1
+
  675.      1                "await String();\n"
+
  676.      1            ],
+
  677.      1
+
  678.      1// PR-351 - Add BigInt support.
+
  679.      1
+
  680.      1            bigint: [
+
  681.      1                "let aa = 0b0n;\n",
+
  682.      1                "let aa = 0o0n;\n",
+
  683.      1                "let aa = 0x0n;\n",
+
  684.      1                "let aa = BigInt(0n);\n",
+
  685.      1                "let aa = typeof aa === \"bigint\";\n"
+
  686.      1            ],
+
  687.      1            date: [
+
  688.      1                "Date.getTime();",
+
  689.      1                "let aa = aa().getTime();",
+
  690.      1                "let aa = aa.aa().getTime();"
+
  691.      1            ],
+
  692.      1            directive: [
+
  693.      1                "#!\n/*jslint browser:false, node*/\n\"use strict\";",
+
  694.      1                "/*property aa bb*/"
+
  695.      1            ],
+
  696.      1            for: [
+
  697.      1                (
+
  698.      1                    "/*jslint for*/\n"
+
  699.      1                    + "function aa(bb) {\n"
+
  700.      1                    + "    for (bb = 0; bb < 0; bb += 1) {\n"
+
  701.      1                    + "        bb();\n"
+
  702.      1                    + "    }\n"
+
  703.      1                    + "}\n"
+
  704.      1                )
+
  705.      1            ],
+
  706.      1            jslint_disable: [
+
  707.      1                "/*jslint-disable*/\n0\n/*jslint-enable*/"
+
  708.      1            ],
+
  709.      1            jslint_ignore_line: [
+
  710.      1                "0 //jslint-ignore-line"
+
  711.      1            ],
+
  712.      1            json: [
+
  713.      1                "{\"aa\":[[],-0,null]}"
+
  714.      1            ],
+
  715.      1            label: [
+
  716.      1                (
+
  717.      1                    "function aa() {\n"
+
  718.      1                    + "bb:\n"
+
  719.      1                    + "    while (true) {\n"
+
  720.      1                    + "        if (true) {\n"
+
  721.      1                    + "            break bb;\n"
+
  722.      1                    + "        }\n"
+
  723.      1                    + "    }\n"
+
  724.      1                    + "}\n"
+
  725.      1                )
+
  726.      1            ],
+
  727.      1            loop: [
+
  728.      1                (
+
  729.      1                    "function aa() {\n"
+
  730.      1                    + "    do {\n"
+
  731.      1                    + "        aa();\n"
+
  732.      1                    + "    } while (aa());\n"
+
  733.      1                    + "}\n"
+
  734.      1                ),
+
  735.      1
+
  736.      1// PR-378 - Relax warning "function_in_loop".
+
  737.      1
+
  738.      1                (
+
  739.      1                    "function aa() {\n"
+
  740.      1                    + "    while (true) {\n"
+
  741.      1                    + "        (function () {\n"
+
  742.      1                    + "            return;\n"
+
  743.      1                    + "        }());\n"
+
  744.      1                    + "    }\n"
+
  745.      1                    + "}\n"
+
  746.      1                )
+
  747.      1            ],
+
  748.      1            module: [
+
  749.      1                "export default Object.freeze();",
+
  750.      1
+
  751.      1// PR-439 - Add grammar for "export async function ...".
+
  752.      1
+
  753.      1                (
+
  754.      1                    "export default Object.freeze(async function () {\n"
+
  755.      1                    + "    return await 0;\n"
+
  756.      1                    + "});\n"
+
  757.      1                ),
+
  758.      1                "import {aa, bb} from \"aa\";\naa(bb);",
+
  759.      1                "import {} from \"aa\";",
+
  760.      1                "import(\"aa\").then(function () {\n    return;\n});",
+
  761.      1                (
+
  762.      1                    "let aa = 0;\n"
+
  763.      1                    + "import(aa).then(aa).then(aa)"
+
  764.      1                    + ".catch(aa).finally(aa);\n"
+
  765.      1                )
+
  766.      1            ],
+
  767.      1            number: [
+
  768.      1                "let aa = 0.0e0;",
+
  769.      1                "let aa = 0b0;",
+
  770.      1                "let aa = 0o0;",
+
  771.      1                "let aa = 0x0;"
+
  772.      1            ],
+
  773.      1
+
  774.      1// PR-390 - Add numeric-separator support.
+
  775.      1
+
  776.      1            numeric_separator: [
+
  777.      1                "let aa = 0.0_0_0;",
+
  778.      1                "let aa = 0b0_1111_1111n;\n",
+
  779.      1                "let aa = 0o0_1234_1234n;\n",
+
  780.      1                "let aa = 0x0_1234_1234n;\n",
+
  781.      1                "let aa = 1_234_234.1_234_234E1_234_234;"
+
  782.      1            ],
+
  783.      1            optional_chaining: [
+
  784.      1                "let aa = aa?.bb?.cc;"
+
  785.      1            ],
+
  786.      1            param: [
+
  787.      1                "function aa({aa, bb}) {\n    return {aa, bb};\n}\n",
+
  788.      1                (
+
  789.      1                    "function aa({constructor}) {\n"
+
  790.      1                    + "    return {constructor};\n"
+
  791.      1                    + "}\n"
+
  792.      1                )
+
  793.      1            ],
+
  794.      1            property: [
+
  795.      1                "let aa = aa[`!`];"
+
  796.      1            ],
+
  797.      1            regexp: [
+
  798.      1                "function aa() {\n    return /./;\n}",
+
  799.      1                "let aa = /(?!.)(?:.)(?=.)/;",
+
  800.      1                "let aa = /./gimuy;",
+
  801.      1                "let aa = /[\\--\\-]/;"
+
  802.      1            ],
+
  803.      1            ternary: [
+
  804.      1                (
+
  805.      1                    "let aa = (\n"
+
  806.      1                    + "    aa()\n"
+
  807.      1                    + "    ? 0\n"
+
  808.      1                    + "    : 1\n"
+
  809.      1                    + ") "
+
  810.      1                    + "&& (\n"
+
  811.      1                    + "    aa()\n"
+
  812.      1                    + "    ? 0\n"
+
  813.      1                    + "    : 1\n"
+
  814.      1                    + ");"
+
  815.      1                ),
+
  816.      1                (
+
  817.      1                    "let aa = (\n"
+
  818.      1                    + "    aa()\n"
+
  819.      1                    + "    ? `${0}`\n"
+
  820.      1                    + "    : `${1}`\n"
+
  821.      1                    + ");"
+
  822.      1                ),
+
  823.      1
+
  824.      1// PR-394 - Bugfix
+
  825.      1// Fix jslint falsely believing megastring literals `0` and `1` are similar.
+
  826.      1
+
  827.      1                (
+
  828.      1                    "let aa = (\n"
+
  829.      1                    + "    aa()\n"
+
  830.      1                    + "    ? `0`\n"
+
  831.      1                    + "    : `1`\n"
+
  832.      1                    + ");"
+
  833.      1                )
+
  834.      1            ],
+
  835.      1            try_catch: [
+
  836.      1                (
+
  837.      1                    "let aa = 0;\n"
+
  838.      1                    + "try {\n"
+
  839.      1                    + "    aa();\n"
+
  840.      1                    + "} catch (err) {\n"
+
  841.      1                    + "    aa = err;\n"
+
  842.      1                    + "}\n"
+
  843.      1                    + "try {\n"
+
  844.      1                    + "    aa();\n"
+
  845.      1                    + "} catch (err) {\n"
+
  846.      1                    + "    aa = err;\n"
+
  847.      1                    + "}\n"
+
  848.      1                    + "aa();\n"
+
  849.      1                )
+
  850.      1            ],
+
  851.      1            try_finally: [
+
  852.      1                (
+
  853.      1                    "let aa = 0;\n"
+
  854.      1                    + "try {\n"
+
  855.      1                    + "    aa();\n"
+
  856.      1                    + "} finally {\n"
+
  857.      1                    + "    aa();\n"
+
  858.      1                    + "}\n"
+
  859.      1                )
+
  860.      1            ],
+
  861.      1            use_strict: [
+
  862.      1                (
+
  863.      1                    "\"use strict\";\n"
+
  864.      1                    + "let aa = 0;\n"
+
  865.      1                    + "function bb() {\n"
+
  866.      1                    + "    \"use strict\";\n"
+
  867.      1                    + "    return aa;\n"
+
  868.      1                    + "}\n"
+
  869.      1                )
+
  870.      1            ],
+
  871.      1            var: [
+
  872.      1
+
  873.      1// PR-363 - Bugfix
+
  874.      1// Add test against false-warning <uninitialized 'bb'> in code
+
  875.      1// '/*jslint node*/\nlet {aa:bb} = {}; bb();'.
+
  876.      1
+
  877.      1                "/*jslint node*/\n",
+
  878.      1                ""
+
  879.      2            ].map(function (directive) {
+
  880.      2                return [
+
  881.      2                    "let [\n    aa, bb = 0\n] = 0;\naa();\nbb();",
+
  882.      2                    "let aa = 0;\nlet [...bb] = [...aa];\nbb();",
+
  883.      2
+
  884.      2// PR-459 - Allow destructuring-assignment after function-definition.
+
  885.      2
+
  886.      2                    (
+
  887.      2                        "let aa;\n"
+
  888.      2                        + "let bb;\n"
+
  889.      2                        + "function cc() {\n"
+
  890.      2                        + "    return;\n"
+
  891.      2                        + "}\n"
+
  892.      2                        + "[aa, bb] = cc();\n"
+
  893.      2                    ),
+
  894.      2                    "let constructor = 0;\nconstructor();",
+
  895.      2                    "let {\n    aa: bb\n} = 0;\nbb();",
+
  896.      2                    "let {\n    aa: bb,\n    bb: cc\n} = 0;\nbb();\ncc();",
+
  897.      2                    "let {aa, bb} = 0;\naa();\nbb();",
+
  898.      2                    "let {constructor} = 0;\nconstructor();"
+
  899.     16                ].map(function (code) {
+
  900.     16                    return directive + code;
+
  901.     16                });
+
  902.      2            }).flat()
+
  903.     23        }).forEach(function (codeList) {
+
  904.     23            let elemPrv = "";
+
  905.     68            codeList.forEach(function (code) {
+
  906.     68                let warnings;
+
  907.     68                // Assert codeList is sorted.
+
  908.     68                assertOrThrow(elemPrv < code, JSON.stringify([
+
  909.     68                    elemPrv, code
+
  910.     68                ], undefined, 4));
+
  911.     68                elemPrv = code;
+
  912.     68                [
+
  913.     68                    jslint.jslint,
+
  914.     68                    jslintCjs.jslint
+
  915.    136                ].forEach(function (jslint) {
+
  916.    136                    warnings = jslint(code, {
+
  917.    136                        beta: true
+
  918.    136                    }).warnings;
+
  919.    136                    assertOrThrow(
+
  920.    136                        warnings.length === 0,
+
  921.    136                        JSON.stringify([code, warnings])
+
  922.    136                    );
+
  923.    136                });
+
  924.     68            });
+
  925.     23        });
+
  926.      1    });
+
  927.      1});
+
  928.      1
+
  929.      1jstestDescribe((
+
  930.      1    "test jslint's option handling-behavior"
+
  931.      1), function testBehaviorJslintOption() {
+
  932.      1    let elemPrv = "";
+
  933.      1    [
+
  934.      1        [
+
  935.      1            "let aa = aa | 0;", {bitwise: true}, []
+
  936.      1        ], [
+
  937.      1            ";\naa(new XMLHttpRequest());", {browser: true}, ["aa"]
+
  938.      1        ], [
+
  939.      1            "let aa = \"aa\" + 0;", {convert: true}, []
+
  940.      1        ], [
+
  941.      1            "registerType();", {couch: true}, []
+
  942.      1        ], [
+
  943.      1            "debugger;", {devel: true}, []
+
  944.      1        ], [
+
  945.      1
+
  946.      1// PR-404 - Alias "evil" to jslint-directive "eval" for backwards-compat.
+
  947.      1
+
  948.      1            "new Function();\neval();", {eval: true, evil: true}, []
+
  949.      1        ], [
+
  950.      1            "let aa = () => 0;", {fart: true}, []
+
  951.      1        ], [
+
  952.      1            (
+
  953.      1                "let aa = async (bb, {cc, dd}, [ee, ff], ...gg) => {\n"
+
  954.      1                + "    bb += 1;\n"
+
  955.      1                + "    return await (bb + cc + dd + ee + ff + gg);\n"
+
  956.      1                + "};\n"
+
  957.      1            ), {fart: true}, []
+
  958.      1        ], [
+
  959.      1            (
+
  960.      1                "function aa(aa) {\n"
+
  961.      1                + "    for (aa = 0; aa < 0; aa += 1) {\n"
+
  962.      1                + "        aa();\n"
+
  963.      1                + "    }\n"
+
  964.      1                + "}\n"
+
  965.      1            ), {for: true}, []
+
  966.      1        ], [
+
  967.      1            "let aa = {get aa() {\n    return;\n}};", {getset: true}, []
+
  968.      1        ], [
+
  969.      1            "let aa = {set aa(aa) {\n    return aa;\n}};", {getset: true}, []
+
  970.      1        ], [
+
  971.      1            sourceJslintMjs.replace((
+
  972.      1                /    /g
+
  973.      1            ), "  "), {indent2: true}, []
+
  974.      1        ], [
+
  975.      1            "function aa() {\n  return;\n}", {indent2: true}, []
+
  976.      1        ], [
+
  977.      1            "/".repeat(100), {long: true}, []
+
  978.      1        ], [
+
  979.      1
+
  980.      1// PR-404 - Alias "nomen" to jslint-directive "name" for backwards-compat.
+
  981.      1
+
  982.      1            "let aa = aa._;", {name: true, nomen: true}, []
+
  983.      1        ], [
+
  984.      1            "require();", {node: true}, []
+
  985.      1        ], [
+
  986.      1            "let aa = 'aa';", {single: true}, []
+
  987.      1        ], [
+
  988.      1
+
  989.      1// PR-404 - Add new directive "subscript" to play nice with Google Closure.
+
  990.      1
+
  991.      1            "aa[\"aa\"] = 1;", {subscript: true}, ["aa"]
+
  992.      1        ], [
+
  993.      1            "", {test_internal_error: true}, []
+
  994.      1        ], [
+
  995.      1            "let aa = this;", {this: true}, []
+
  996.      1        ], [
+
  997.      1            "", {trace: true}, []
+
  998.      1        ], [
+
  999.      1            (
+
 1000.      1                "function aa({bb, aa}) {\n"
+
 1001.      1                + "    switch (aa) {\n"
+
 1002.      1                + "    case 1:\n"
+
 1003.      1                + "        break;\n"
+
 1004.      1                + "    case 0:\n"
+
 1005.      1                + "        break;\n"
+
 1006.      1                + "    default:\n"
+
 1007.      1                + "        return {bb, aa};\n"
+
 1008.      1                + "    }\n"
+
 1009.      1                + "}\n"
+
 1010.      1            ), {unordered: true}, []
+
 1011.      1        ], [
+
 1012.      1            "let {bb, aa} = 0;", {unordered: true}, []
+
 1013.      1        ], [
+
 1014.      1            (
+
 1015.      1                "function aa() {\n"
+
 1016.      1                + "    if (aa) {\n"
+
 1017.      1                + "        let bb = 0;\n"
+
 1018.      1                + "        return bb;\n"
+
 1019.      1                + "    }\n"
+
 1020.      1                + "}\n"
+
 1021.      1            ), {variable: true}, []
+
 1022.      1        ], [
+
 1023.      1            "let bb = 0;\nlet aa = 0;", {variable: true}, []
+
 1024.      1        ], [
+
 1025.      1            "\t", {white: true}, []
+
 1026.      1        ]
+
 1027.     26    ].forEach(function ([
+
 1028.     26        source, option_dict, global_list
+
 1029.     26    ]) {
+
 1030.     26        jstestIt((
+
 1031.     26            `test option=${JSON.stringify(option_dict)} handling-behavior`
+
 1032.     26        ), function () {
+
 1033.     26            let elemNow = JSON.stringify([
+
 1034.     26                option_dict, source, global_list
+
 1035.     26            ]);
+
 1036.     26            let warningsLength = (
+
 1037.     26                option_dict.test_internal_error
+
 1038.      1                ? 1
+
 1039.     25                : 0
+
 1040.     26            );
+
 1041.     26            // Assert list is sorted.
+
 1042.     26            assertOrThrow(elemPrv < elemNow, JSON.stringify([
+
 1043.     26                elemPrv, elemNow
+
 1044.     26            ], undefined, 4));
+
 1045.     26            elemPrv = elemNow;
+
 1046.     26            option_dict.beta = true;
+
 1047.     26            [
+
 1048.     26                jslint.jslint,
+
 1049.     26                jslintCjs.jslint
+
 1050.     52            ].forEach(function (jslint) {
+
 1051.     52                // test jslint's option handling-behavior
+
 1052.     52                assertOrThrow(
+
 1053.     52                    jslint(
+
 1054.     52                        source,
+
 1055.     52                        option_dict,
+
 1056.     52                        global_list
+
 1057.     52                    ).warnings.length === warningsLength,
+
 1058.     52                    "jslint.jslint(" + JSON.stringify([
+
 1059.     52                        source, option_dict, global_list
+
 1060.     52                    ]) + ")"
+
 1061.     52                );
+
 1062.     52                // test jslint's directive handling-behavior
+
 1063.     52                source = (
+
 1064.     52                    "/*jslint " + JSON.stringify(
+
 1065.     52                        option_dict
+
 1066.     52                    ).slice(1, -1).replace((
+
 1067.     52                        /"/g
+
 1068.     52                    ), "") + "*/\n"
+
 1069.     52                    + (
+
 1070.     52                        global_list.length === 0
+
 1071.     48                        ? ""
+
 1072.      4                        : "/*global " + global_list.join(",") + "*/\n"
+
 1073.     52                    )
+
 1074.     52                    + source.replace((
+
 1075.     52                        /^#!/
+
 1076.     52                    ), "//")
+
 1077.     52                );
+
 1078.     52                assertOrThrow(
+
 1079.     52                    jslint(source).warnings.length === warningsLength,
+
 1080.     52                    source
+
 1081.     52                );
+
 1082.     52            });
+
 1083.     26        });
+
 1084.     26    });
+
 1085.      1});
+
 1086.      1
+
 1087.      1jstestDescribe((
+
 1088.      1    "test jslint's warnings handling-behavior"
+
 1089.      1), function testBehaviorJslintWarnings() {
+
 1090.      1    jstestIt((
+
 1091.      1        "test jslint's warning handling-behavior"
+
 1092.      1    ), function () {
+
 1093.      1
+
 1094.      1// this function will validate each jslint <warning> is raised with given
+
 1095.      1// malformed <code>
+
 1096.      1
+
 1097.      1        sourceJslintMjs.replace((
+
 1098.      1            /(\n\s*?\/\/\s*?test_cause:\s*?)(\S[\S\s]*?\S)(\n\n\s*?) *?\S/g
+
 1099.    328        ), function (match0, header, causeList, footer) {
+
 1100.    328            let tmp;
+
 1101.    328            // console.error(match0);
+
 1102.    328            // Validate header.
+
 1103.    328            assertOrThrow(header === "\n\n// test_cause:\n", match0);
+
 1104.    328            // Validate footer.
+
 1105.    328            assertOrThrow(footer === "\n\n", match0);
+
 1106.    328            // Validate causeList.
+
 1107.    328            causeList = causeList.replace((
+
 1108.    328                /^\/\/ /gm
+
 1109.    328            ), "").replace((
+
 1110.    328                /^\["\n([\S\s]*?)\n"(,.*?)$/gm
+
 1111.     43            ), function (ignore, source, param) {
+
 1112.     43                source = "[" + JSON.stringify(source) + param;
+
 1113.     43                assertOrThrow(source.length > (80 - 3), source);
+
 1114.     43                return source;
+
 1115.     43            }).replace((
+
 1116.    328                / \/\/jslint-ignore-line$/gm
+
 1117.    328            ), "");
+
 1118.    505            tmp = causeList.split("\n").map(function (cause) {
+
 1119.    505                return (
+
 1120.    505                    "["
+
 1121.   2525                    + JSON.parse(cause).map(function (elem) {
+
 1122.   2525                        return JSON.stringify(elem);
+
 1123.   2525                    }).join(", ")
+
 1124.    505                    + "]"
+
 1125.    505                );
+
 1126.    505            }).sort().join("\n");
+
 1127.    328            assertOrThrow(
+
 1128.    328                causeList === tmp,
+
 1129.    328                "\n" + causeList + "\n\n" + tmp
+
 1130.    328            );
+
 1131.    505            causeList.split("\n").forEach(function (cause) {
+
 1132.    505                cause = JSON.parse(cause);
+
 1133.    505                tmp = jslint.jslint(cause[0], {
+
 1134.    505                    beta: true,
+
 1135.    505                    test_cause: true
+
 1136.    505                }).causes;
+
 1137.    505                // Validate cause.
+
 1138.    505                assertOrThrow(
+
 1139.    505                    tmp[JSON.stringify(cause.slice(1))],
+
 1140.    505                    (
+
 1141.    505                        "\n" + JSON.stringify(cause) + "\n\n"
+
 1142.    505                        + Object.keys(tmp).sort().join("\n")
+
 1143.    505                    )
+
 1144.    505                );
+
 1145.    505            });
+
 1146.    328            return "";
+
 1147.    328        });
+
 1148.      1    });
+
 1149.      1});
+
 1150.      1
+
 1151.      1jstestDescribe((
+
 1152.      1    "test jstestXxx handling-behavior"
+
 1153.      1), function testBehaviorJstestXxx() {
+
 1154.      1    jstestIt((
+
 1155.      1        "test jstestDescribe error handling-behavior"
+
 1156.      1    ), function () {
+
 1157.      1        throw new Error();
+
 1158.      1    }, "pass");
+
 1159.      1    jstestIt((
+
 1160.      1        "test jstestOnExit tests-failed handling-behavior"
+
 1161.      1    ), function () {
+
 1162.      1        jstestOnExit(undefined, "testsFailed");
+
 1163.      1    });
+
 1164.      1});
+
 1165.      1
+
 1166.      1jstestDescribe((
+
 1167.      1    "test misc handling-behavior"
+
 1168.      1), function testBehaviorMisc() {
+
 1169.      1    jstestIt((
+
 1170.      1        "test misc handling-behavior"
+
 1171.      1    ), async function () {
+
 1172.      1        // test debugInline handling-behavior
+
 1173.      1        noop(debugInline);
+
 1174.      1        // test assertErrorThrownAsync error handling-behavior
+
 1175.      1        await assertErrorThrownAsync(function () {
+
 1176.      1            return assertErrorThrownAsync(noop);
+
 1177.      1        });
+
 1178.      1        // test assertJsonEqual error handling-behavior
+
 1179.      1        await assertErrorThrownAsync(function () {
+
 1180.      1            assertJsonEqual(1, 2);
+
 1181.      1        });
+
 1182.      1        await assertErrorThrownAsync(function () {
+
 1183.      1            assertJsonEqual(1, 2, "undefined");
+
 1184.      1        });
+
 1185.      1        await assertErrorThrownAsync(function () {
+
 1186.      1            assertJsonEqual(1, 2, {});
+
 1187.      1        });
+
 1188.      1        // test assertOrThrow error handling-behavior
+
 1189.      1        await assertErrorThrownAsync(function () {
+
 1190.      1            assertOrThrow(undefined, "undefined");
+
 1191.      1        });
+
 1192.      1        await assertErrorThrownAsync(function () {
+
 1193.      1            assertOrThrow(undefined, new Error());
+
 1194.      1        });
+
 1195.      1    });
+
 1196.      1});
+
 1197.      1
+
 1198.      1jstestDescribe((
+
 1199.      1    "test v8CoverageListMerge handling-behavior"
+
 1200.      1), function testBehaviorV8CoverageListMerge() {
+
 1201.      1    let functionsInput = JSON.stringify([
+
 1202.      1        {
+
 1203.      1            functionName: "test",
+
 1204.      1            isBlockCoverage: true,
+
 1205.      1            ranges: [
+
 1206.      1                {
+
 1207.      1                    count: 2,
+
 1208.      1                    endOffset: 4,
+
 1209.      1                    startOffset: 0
+
 1210.      1                },
+
 1211.      1                {
+
 1212.      1                    count: 1,
+
 1213.      1                    endOffset: 2,
+
 1214.      1                    startOffset: 1
+
 1215.      1                },
+
 1216.      1                {
+
 1217.      1                    count: 1,
+
 1218.      1                    endOffset: 3,
+
 1219.      1                    startOffset: 2
+
 1220.      1                }
+
 1221.      1            ]
+
 1222.      1        }
+
 1223.      1    ]);
+
 1224.      1    jstestIt((
+
 1225.      1        "accepts empty arrays for `v8CoverageListMerge`"
+
 1226.      1    ), function () {
+
 1227.      1        assertJsonEqual(v8CoverageListMerge([]), {
+
 1228.      1            result: []
+
 1229.      1        });
+
 1230.      1    });
+
 1231.      1    jstestIt((
+
 1232.      1        "funcCovs.length === 1"
+
 1233.      1    ), function () {
+
 1234.      1        assertJsonEqual(v8CoverageListMerge([
+
 1235.      1            {
+
 1236.      1                result: [
+
 1237.      1                    {
+
 1238.      1                        functions: [
+
 1239.      1                            {
+
 1240.      1                                functionName: "test",
+
 1241.      1                                isBlockCoverage: true,
+
 1242.      1                                ranges: [
+
 1243.      1                                    {
+
 1244.      1                                        count: 2,
+
 1245.      1                                        endOffset: 4,
+
 1246.      1                                        startOffset: 0
+
 1247.      1                                    }
+
 1248.      1                                ]
+
 1249.      1                            }
+
 1250.      1                        ],
+
 1251.      1                        moduleUrl: "/lib.js",
+
 1252.      1                        scriptId: "1"
+
 1253.      1                    }
+
 1254.      1                ]
+
 1255.      1            },
+
 1256.      1            {
+
 1257.      1                result: [
+
 1258.      1                    {
+
 1259.      1                        functions: [],
+
 1260.      1                        moduleUrl: "/lib.js",
+
 1261.      1                        scriptId: "2"
+
 1262.      1                    }
+
 1263.      1                ]
+
 1264.      1            }
+
 1265.      1        ]), {
+
 1266.      1            result: [
+
 1267.      1                {
+
 1268.      1                    functions: [
+
 1269.      1                        {
+
 1270.      1                            functionName: "test",
+
 1271.      1                            isBlockCoverage: true,
+
 1272.      1                            ranges: [
+
 1273.      1                                {
+
 1274.      1                                    count: 2,
+
 1275.      1                                    endOffset: 4,
+
 1276.      1                                    startOffset: 0
+
 1277.      1                                }
+
 1278.      1                            ]
+
 1279.      1                        }
+
 1280.      1                    ],
+
 1281.      1                    scriptId: "0"
+
 1282.      1                }
+
 1283.      1            ]
+
 1284.      1        });
+
 1285.      1    });
+
 1286.      1    jstestIt((
+
 1287.      1        "accepts arrays with a single item for `v8CoverageListMerge`"
+
 1288.      1    ), function () {
+
 1289.      1        assertJsonEqual(v8CoverageListMerge([
+
 1290.      1            {
+
 1291.      1                result: [
+
 1292.      1                    {
+
 1293.      1                        functions: JSON.parse(functionsInput),
+
 1294.      1                        moduleUrl: "/lib.js",
+
 1295.      1                        scriptId: "123"
+
 1296.      1                    }
+
 1297.      1                ]
+
 1298.      1            }
+
 1299.      1        ]), {
+
 1300.      1            result: [
+
 1301.      1                {
+
 1302.      1                    functions: [
+
 1303.      1                        {
+
 1304.      1                            functionName: "test",
+
 1305.      1                            isBlockCoverage: true,
+
 1306.      1                            ranges: [
+
 1307.      1                                {
+
 1308.      1                                    count: 2,
+
 1309.      1                                    endOffset: 4,
+
 1310.      1                                    startOffset: 0
+
 1311.      1                                },
+
 1312.      1                                {
+
 1313.      1                                    count: 1,
+
 1314.      1                                    endOffset: 3,
+
 1315.      1                                    startOffset: 1
+
 1316.      1                                }
+
 1317.      1                            ]
+
 1318.      1                        }
+
 1319.      1                    ],
+
 1320.      1                    moduleUrl: "/lib.js",
+
 1321.      1                    scriptId: "0"
+
 1322.      1                }
+
 1323.      1            ]
+
 1324.      1        });
+
 1325.      1    });
+
 1326.      1    jstestIt((
+
 1327.      1        "accepts arrays with two identical items for"
+
 1328.      1        + " `v8CoverageListMerge`"
+
 1329.      1    ), function () {
+
 1330.      1        assertJsonEqual(v8CoverageListMerge([
+
 1331.      1            {
+
 1332.      1                result: [
+
 1333.      1                    {
+
 1334.      1                        functions: JSON.parse(functionsInput),
+
 1335.      1                        scriptId: "123",
+
 1336.      1                        url: "/lib.js"
+
 1337.      1                    }, {
+
 1338.      1                        functions: JSON.parse(functionsInput),
+
 1339.      1                        scriptId: "123",
+
 1340.      1                        url: "/lib.js"
+
 1341.      1                    }
+
 1342.      1                ]
+
 1343.      1            }
+
 1344.      1        ]), {
+
 1345.      1            result: [
+
 1346.      1                {
+
 1347.      1                    functions: [
+
 1348.      1                        {
+
 1349.      1                            functionName: "test",
+
 1350.      1                            isBlockCoverage: true,
+
 1351.      1                            ranges: [
+
 1352.      1                                {
+
 1353.      1                                    count: 4,
+
 1354.      1                                    endOffset: 4,
+
 1355.      1                                    startOffset: 0
+
 1356.      1                                },
+
 1357.      1                                {
+
 1358.      1                                    count: 2,
+
 1359.      1                                    endOffset: 3,
+
 1360.      1                                    startOffset: 1
+
 1361.      1                                }
+
 1362.      1                            ]
+
 1363.      1                        }
+
 1364.      1                    ],
+
 1365.      1                    scriptId: "0",
+
 1366.      1                    url: "/lib.js"
+
 1367.      1                }
+
 1368.      1            ]
+
 1369.      1        });
+
 1370.      1    });
+
 1371.      1    [
+
 1372.      1        "test_coverage_merge_is_block_coverage_test.json",
+
 1373.      1        "test_coverage_merge_issue_2_mixed_is_block_coverage_test.json",
+
 1374.      1        "test_coverage_merge_node_10_internal_errors_one_of_test.json",
+
 1375.      1        "test_coverage_merge_reduced_test.json",
+
 1376.      1        "test_coverage_merge_simple_test.json",
+
 1377.      1        "test_coverage_merge_various_test.json"
+
 1378.      6    ].forEach(function (file) {
+
 1379.      6        jstestIt(file, function () {
+
 1380.      6            file = testCoverageMergeData[file];
+
 1381.     84            file.forEach(function ({
+
 1382.     84                expected,
+
 1383.     84                inputs
+
 1384.     84            }) {
+
 1385.     84                assertJsonEqual(v8CoverageListMerge(inputs), expected);
+
 1386.     84            });
+
 1387.      6        });
+
 1388.      6    });
+
 1389.      1    jstestIt((
+
 1390.      1        "merge multiple node-sqlite coverage files"
+
 1391.      1    ), function () {
+
 1392.      1        let data1 = [
+
 1393.      1            "test_v8_coverage_node_sqlite_9884_1633662346346_0.json",
+
 1394.      1            "test_v8_coverage_node_sqlite_13216_1633662333140_0.json"
+
 1395.      2        ].map(function (file) {
+
 1396.      2            return testCoverageMergeData[file];
+
 1397.      2        });
+
 1398.      1        let data2 = testCoverageMergeData[
+
 1399.      1            "test_v8_coverage_node_sqlite_merged.json"
+
 1400.      1        ];
+
 1401.      1        data1 = v8CoverageListMerge(data1);
+
 1402.      1        data1 = v8CoverageListMerge([data1]);
+
 1403.      1
+
 1404.      1// Debug data1.
+
 1405.      1// await moduleFs.promises.writeFile(
+
 1406.      1//     ".test_v8_coverage_node_sqlite_merged.json",
+
 1407.      1//     JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n"
+
 1408.      1// );
+
 1409.      1
+
 1410.      1        assertJsonEqual(data1, data2);
+
 1411.      1    });
+
 1412.      1});
+
 1413.      1
+
 1414.      1jstestDescribe((
+
 1415.      1    "test v8CoverageReportCreate handling-behavior"
+
 1416.      1), function testBehaviorV8CoverageReportCreate() {
+
 1417.      1    jstestIt((
+
 1418.      1        "test null-case handling-behavior"
+
 1419.      1    ), async function () {
+
 1420.      1        await assertErrorThrownAsync(function () {
+
 1421.      1            return v8CoverageReportCreate({});
+
 1422.      1        }, "invalid coverageDir");
+
 1423.      1    });
+
 1424.      1    jstestIt((
+
 1425.      1        "test coverage-report jslint.mjs handling-behavior"
+
 1426.      1    ), async function () {
+
 1427.      1        // test remove-old-coverage handling-behavior
+
 1428.      1        await fsWriteFileWithParents(
+
 1429.      1            ".tmp/coverage_jslint/coverage-0-0-0.json",
+
 1430.      1            ""
+
 1431.      1        );
+
 1432.      1        await jslint.jslint_cli({
+
 1433.      1            console_error: noop, // comment to debug
+
 1434.      1            mode_cli: true,
+
 1435.      1            process_argv: [
+
 1436.      1                "node", "jslint.mjs",
+
 1437.      1                "v8_coverage_report=.tmp/coverage_jslint",
+
 1438.      1                "--exclude=aa.js",
+
 1439.      1                "--include-node-modules=1",
+
 1440.      1                "--include=jslint.mjs",
+
 1441.      1                "node", "jslint.mjs"
+
 1442.      1            ]
+
 1443.      1        });
+
 1444.      1    });
+
 1445.      1    [
+
 1446.      1        [
+
 1447.      1            "v8CoverageReportCreate_high.js", (
+
 1448.      1                "switch(0){\n"
+
 1449.      1                + "case 0:break;\n"
+
 1450.      1                + "}\n"
+
 1451.      1            )
+
 1452.      1        ], [
+
 1453.      1            "v8CoverageReportCreate_ignore.js", (
+
 1454.      1                "/*coverage-ignore-file*/\n"
+
 1455.      1                + "switch(0){\n"
+
 1456.      1                + "case 0:break;\n"
+
 1457.      1                + "}\n"
+
 1458.      1            )
+
 1459.      1        ], [
+
 1460.      1            "v8CoverageReportCreate_low.js", (
+
 1461.      1                "switch(0){\n"
+
 1462.      1                + "case 1:break;\n"
+
 1463.      1                + "case 2:break;\n"
+
 1464.      1                + "case 3:break;\n"
+
 1465.      1                + "case 4:break;\n"
+
 1466.      1                + "}\n"
+
 1467.      1            )
+
 1468.      1        ], [
+
 1469.      1            "v8CoverageReportCreate_medium.js", (
+
 1470.      1                "switch(0){\n"
+
 1471.      1                + "case 0:break;\n"
+
 1472.      1                + "case 1:break;\n"
+
 1473.      1                + "case 2:break;\n"
+
 1474.      1                + "}\n"
+
 1475.      1            )
+
 1476.      1        ]
+
 1477.      4    ].forEach(function ([
+
 1478.      4        file, data
+
 1479.      4    ], ii) {
+
 1480.      4        jstestIt(file, async function () {
+
 1481.      4            let dir = ".tmp/coverage_" + ii + "/";
+
 1482.      4            file = dir + file;
+
 1483.      4            await fsWriteFileWithParents(file, data);
+
 1484.      4            await jslint.jslint_cli({
+
 1485.      4                console_error: noop, // comment to debug
+
 1486.      4                mode_cli: true,
+
 1487.      4                process_argv: [
+
 1488.      4                    "node", "jslint.mjs",
+
 1489.      4                    "v8_coverage_report=" + dir,
+
 1490.      4                    "node",
+
 1491.      4                    file
+
 1492.      4                ]
+
 1493.      4            });
+
 1494.      4        });
+
 1495.      4    });
+
 1496.      1    jstestIt((
+
 1497.      1        "test npm handling-behavior"
+
 1498.      1    ), async function () {
+
 1499.      1        await jslint.jslint_cli({
+
 1500.      1            console_error: noop, // comment to debug
+
 1501.      1            mode_cli: true,
+
 1502.      1            process_argv: [
+
 1503.      1                "node", "jslint.mjs",
+
 1504.      1                "v8_coverage_report=.tmp/coverage_npm",
+
 1505.      1                "npm", "--version"
+
 1506.      1            ]
+
 1507.      1        });
+
 1508.      1    });
+
 1509.      1    jstestIt((
+
 1510.      1        "test misc handling-behavior"
+
 1511.      1    ), async function () {
+
 1512.      1        await Promise.all([
+
 1513.      1            [
+
 1514.      1                ".tmp/coverage_misc/aa.js", "\n".repeat(0x100)
+
 1515.      1            ], [
+
 1516.      1                ".tmp/coverage_misc/coverage-0-0-0.json", JSON.stringify({
+
 1517.      1                    "result": [
+
 1518.      1                        {
+
 1519.      1                            "functions": [
+
 1520.      1                                {
+
 1521.      1                                    "functionName": "",
+
 1522.      1                                    "isBlockCoverage": true,
+
 1523.      1                                    "ranges": [
+
 1524.      1                                        {
+
 1525.      1                                            "count": 1,
+
 1526.      1                                            "endOffset": 0xf0,
+
 1527.      1                                            "startOffset": 0x10
+
 1528.      1                                        },
+
 1529.      1                                        {
+
 1530.      1                                            "count": 1,
+
 1531.      1                                            "endOffset": 0x40,
+
 1532.      1                                            "startOffset": 0x20
+
 1533.      1                                        },
+
 1534.      1                                        {
+
 1535.      1                                            "count": 1,
+
 1536.      1                                            "endOffset": 0x80,
+
 1537.      1                                            "startOffset": 0x60
+
 1538.      1                                        },
+
 1539.      1                                        {
+
 1540.      1                                            "count": 0,
+
 1541.      1                                            "endOffset": 0x45,
+
 1542.      1                                            "startOffset": 0x25
+
 1543.      1                                        },
+
 1544.      1                                        {
+
 1545.      1                                            "count": 0,
+
 1546.      1                                            "endOffset": 0x85,
+
 1547.      1                                            "startOffset": 0x65
+
 1548.      1                                        }
+
 1549.      1                                    ]
+
 1550.      1                                }
+
 1551.      1                            ],
+
 1552.      1                            "scriptId": "0",
+
 1553.      1                            "url": "file:///" + modulePath.resolve(
+
 1554.      1                                ".tmp/coverage_misc/aa.js"
+
 1555.      1                            )
+
 1556.      1                        }
+
 1557.      1                    ]
+
 1558.      1                }, undefined, 4)
+
 1559.      1            ]
+
 1560.      2        ].map(async function ([
+
 1561.      2            file, data
+
 1562.      2        ]) {
+
 1563.      2            await fsWriteFileWithParents(file, data);
+
 1564.      2        }));
+
 1565.      1        await jslint.jslint_cli({
+
 1566.      1            console_error: noop, // comment to debug
+
 1567.      1            mode_cli: true,
+
 1568.      1            process_argv: [
+
 1569.      1                "node", "jslint.mjs",
+
 1570.      1                "v8_coverage_report=.tmp/coverage_misc"
+
 1571.      1                // "node", ".tmp/coverage_misc/aa.js"
+
 1572.      1            ]
+
 1573.      1        });
+
 1574.      1    });
+
 1575.      1});
+
 1576.      1
+
+ + + + diff --git a/.artifact/coverage/touch.txt b/.artifact/coverage/touch.txt new file mode 100644 index 000000000..e69de29bb diff --git a/.artifact/coverage_sqlite3_js/coverage_badge.svg b/.artifact/coverage_sqlite3_js/coverage_badge.svg new file mode 100644 index 000000000..8e28d5ff5 --- /dev/null +++ b/.artifact/coverage_sqlite3_js/coverage_badge.svg @@ -0,0 +1,14 @@ + + + + +coverage +97.62 % + + diff --git a/.artifact/coverage_sqlite3_js/coverage_report.txt b/.artifact/coverage_sqlite3_js/coverage_report.txt new file mode 100644 index 000000000..b19e8684a --- /dev/null +++ b/.artifact/coverage_sqlite3_js/coverage_report.txt @@ -0,0 +1,16 @@ +V8 Coverage Report ++----------------------------------+-------------------+-------------------+ +| Files covered | Lines | Remaining | ++----------------------------------+-------------------+-------------------+ +| ./ | 97.62 % | | +| *******************************_ | 247 / 253 | 6 / 253 | ++----------------------------------+-------------------+-------------------+ +| ./lib/sqlite3-binding.js | 100.00 % | | +| ******************************** | 6 / 6 | 0 / 6 | ++----------------------------------+-------------------+-------------------+ +| ./lib/sqlite3.js | 97.11 % | | +| *******************************_ | 202 / 208 | 6 / 208 | ++----------------------------------+-------------------+-------------------+ +| ./lib/trace.js | 100.00 % | | +| ******************************** | 39 / 39 | 0 / 39 | ++----------------------------------+-------------------+-------------------+ diff --git a/.artifact/coverage_sqlite3_js/index.html b/.artifact/coverage_sqlite3_js/index.html new file mode 100644 index 000000000..83d95a7bd --- /dev/null +++ b/.artifact/coverage_sqlite3_js/index.html @@ -0,0 +1,212 @@ + + + +V8 Coverage Report + + + + +
+
V8 Coverage Report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Files coveredLinesRemaining
+ . /
+
+
+
+
+ 97.62 %
+ 247 / 253 +
+
+ 6 / 253 +
+ . / lib/sqlite3-binding.js
+
+
+
+
+ 100.00 %
+ 6 / 6 +
+
+ 0 / 6 +
+ . / lib/sqlite3.js
+
+
+
+
+ 97.11 %
+ 202 / 208 +
+
+ 6 / 208 +
+ . / lib/trace.js
+
+
+
+
+ 100.00 %
+ 39 / 39 +
+
+ 0 / 39 +
+
+ + + + diff --git a/.artifact/coverage_sqlite3_js/lib/sqlite3-binding.js.html b/.artifact/coverage_sqlite3_js/lib/sqlite3-binding.js.html new file mode 100644 index 000000000..d3fec08a6 --- /dev/null +++ b/.artifact/coverage_sqlite3_js/lib/sqlite3-binding.js.html @@ -0,0 +1,174 @@ + + + +V8 Coverage Report + + + + +
+
V8 Coverage Report
+ + + + + + + + + + + + + + + +
Files coveredLinesRemaining
+ . / lib/sqlite3-binding.js
+
+
+
+
+ 100.00 %
+ 6 / 6 +
+
+ 0 / 6 +
+
+ + +
+
    1.      2const binary = require('@mapbox/node-pre-gyp');
+
    2.      2const path = require('path');
+
    3.      2const binding_path = binary.find(path.resolve(path.join(__dirname,'../package.json')));
+
    4.      2const binding = require(binding_path);
+
    5.      2module.exports = exports = binding;
+
    6.      2
+
+ + + + diff --git a/.artifact/coverage_sqlite3_js/lib/sqlite3.js.html b/.artifact/coverage_sqlite3_js/lib/sqlite3.js.html new file mode 100644 index 000000000..220bcd4a5 --- /dev/null +++ b/.artifact/coverage_sqlite3_js/lib/sqlite3.js.html @@ -0,0 +1,376 @@ + + + +V8 Coverage Report + + + + +
+
V8 Coverage Report
+ + + + + + + + + + + + + + + +
Files coveredLinesRemaining
+ . / lib/sqlite3.js
+
+
+
+
+ 97.11 %
+ 202 / 208 +
+
+ 6 / 208 +
+
+ + +
+
    1.      2const path = require('path');
+
    2.      2const sqlite3 = require('./sqlite3-binding.js');
+
    3.      2const EventEmitter = require('events').EventEmitter;
+
    4.      2module.exports = exports = sqlite3;
+
    5.      2
+
    6.     12function normalizeMethod (fn) {
+
    7.   4203    return function (sql) {
+
    8.   4203        let errBack;
+
    9.   4203        const args = Array.prototype.slice.call(arguments, 1);
+
   10.   4203
+
   11.   1119        if (typeof args[args.length - 1] === 'function') {
+
   12.   1119            const callback = args[args.length - 1];
+
   13.   1119            errBack = function(err) {
+
   14.   1119                if (err) {
+
   15.   1119                    callback(err);
+
   16.   1119                }
+
   17.   1119            };
+
   18.   1119        }
+
   19.   4203        const statement = new Statement(this, sql, errBack);
+
   20.   4203        return fn.call(this, statement, args);
+
   21.   4203    };
+
   22.     12}
+
   23.      2
+
   24.      6function inherits(target, source) {
+
   25.      6    for (const k in source.prototype)
+
   26.    108        target.prototype[k] = source.prototype[k];
+
   27.      6}
+
   28.      2
+
   29.      2sqlite3.cached = {
+
   30.      4    Database: function(file, a, b) {
+
   31.     -0        if (file === '' || file === ':memory:') {
+
   32.     -0            // Don't cache special databases.
+
   33.     -0            return new Database(file, a, b);
+
   34.     -0        }
+
   35.      4
+
   36.      4        let db;
+
   37.      4        file = path.resolve(file);
+
   38.      4
+
   39.      2        if (!sqlite3.cached.objects[file]) {
+
   40.      2            db = sqlite3.cached.objects[file] = new Database(file, a, b);
+
   41.      2        }
+
   42.      2        else {
+
   43.      2            // Make sure the callback is called.
+
   44.      2            db = sqlite3.cached.objects[file];
+
   45.     -0            const callback = (typeof a === 'number') ? b : a;
+
   46.      2            if (typeof callback === 'function') {
+
   47.      2                function cb() { callback.call(db, null); }
+
   48.      2                if (db.open) process.nextTick(cb);
+
   49.      2                else db.once('open', cb);
+
   50.      2            }
+
   51.      2        }
+
   52.      4
+
   53.      4        return db;
+
   54.      4    },
+
   55.      2    objects: {}
+
   56.      2};
+
   57.      2
+
   58.      2
+
   59.      2const Database = sqlite3.Database;
+
   60.      2const Statement = sqlite3.Statement;
+
   61.      2const Backup = sqlite3.Backup;
+
   62.      2
+
   63.      2inherits(Database, EventEmitter);
+
   64.      2inherits(Statement, EventEmitter);
+
   65.      2inherits(Backup, EventEmitter);
+
   66.      2
+
   67.      2// Database#prepare(sql, [bind1, bind2, ...], [callback])
+
   68.   2076Database.prototype.prepare = normalizeMethod(function(statement, params) {
+
   69.   2076    return params.length
+
   70.      7        ? statement.bind.apply(statement, params)
+
   71.   2069        : statement;
+
   72.   2076});
+
   73.      2
+
   74.      2// Database#run(sql, [bind1, bind2, ...], [callback])
+
   75.   2074Database.prototype.run = normalizeMethod(function(statement, params) {
+
   76.   2074    statement.run.apply(statement, params).finalize();
+
   77.   2074    return this;
+
   78.   2074});
+
   79.      2
+
   80.      2// Database#get(sql, [bind1, bind2, ...], [callback])
+
   81.     35Database.prototype.get = normalizeMethod(function(statement, params) {
+
   82.     35    statement.get.apply(statement, params).finalize();
+
   83.     35    return this;
+
   84.     35});
+
   85.      2
+
   86.      2// Database#all(sql, [bind1, bind2, ...], [callback])
+
   87.      9Database.prototype.all = normalizeMethod(function(statement, params) {
+
   88.      9    statement.all.apply(statement, params).finalize();
+
   89.      9    return this;
+
   90.      9});
+
   91.      2
+
   92.      2// Database#each(sql, [bind1, bind2, ...], [callback], [complete])
+
   93.      7Database.prototype.each = normalizeMethod(function(statement, params) {
+
   94.      7    statement.each.apply(statement, params).finalize();
+
   95.      7    return this;
+
   96.      7});
+
   97.      2
+
   98.      2Database.prototype.map = normalizeMethod(function(statement, params) {
+
   99.      2    statement.map.apply(statement, params).finalize();
+
  100.      2    return this;
+
  101.      2});
+
  102.      2
+
  103.      2// Database#backup(filename, [callback])
+
  104.      2// Database#backup(filename, destName, sourceName, filenameIsDest, [callback])
+
  105.     15Database.prototype.backup = function() {
+
  106.     15    let backup;
+
  107.     13    if (arguments.length <= 2) {
+
  108.     13        // By default, we write the main database out to the main database of the named file.
+
  109.     13        // This is the most likely use of the backup api.
+
  110.     13        backup = new Backup(this, arguments[0], 'main', 'main', true, arguments[1]);
+
  111.     13    } else {
+
  112.      2        // Otherwise, give the user full control over the sqlite3_backup_init arguments.
+
  113.      2        backup = new Backup(this, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]);
+
  114.      2    }
+
  115.     15    // Per the sqlite docs, exclude the following errors as non-fatal by default.
+
  116.     15    backup.retryErrors = [sqlite3.BUSY, sqlite3.LOCKED];
+
  117.     15    return backup;
+
  118.     15};
+
  119.      2
+
  120.      2Statement.prototype.map = function() {
+
  121.      2    const params = Array.prototype.slice.call(arguments);
+
  122.      2    const callback = params.pop();
+
  123.      2    params.push(function(err, rows) {
+
  124.     -0        if (err) return callback(err);
+
  125.      2        const result = {};
+
  126.      2        if (rows.length) {
+
  127.      2            const keys = Object.keys(rows[0]);
+
  128.      2            const key = keys[0];
+
  129.      1            if (keys.length > 2) {
+
  130.      1                // Value is an object
+
  131.      5                for (let i = 0; i < rows.length; i++) {
+
  132.      5                    result[rows[i][key]] = rows[i];
+
  133.      5                }
+
  134.      1            } else {
+
  135.      1                const value = keys[1];
+
  136.      1                // Value is a plain value
+
  137.      5                for (let i = 0; i < rows.length; i++) {
+
  138.      5                    result[rows[i][key]] = rows[i][value];
+
  139.      5                }
+
  140.      1            }
+
  141.      2        }
+
  142.      2        callback(err, result);
+
  143.      2    });
+
  144.      2    return this.all.apply(this, params);
+
  145.      2};
+
  146.      2
+
  147.      2let isVerbose = false;
+
  148.      2
+
  149.      2const supportedEvents = [ 'trace', 'profile', 'insert', 'update', 'delete' ];
+
  150.      2
+
  151.      7Database.prototype.addListener = Database.prototype.on = function(type) {
+
  152.      7    const val = EventEmitter.prototype.addListener.apply(this, arguments);
+
  153.      4    if (supportedEvents.indexOf(type) >= 0) {
+
  154.      4        this.configure(type, true);
+
  155.      4    }
+
  156.      7    return val;
+
  157.      7};
+
  158.      2
+
  159.      2Database.prototype.removeListener = function(type) {
+
  160.      2    const val = EventEmitter.prototype.removeListener.apply(this, arguments);
+
  161.      1    if (supportedEvents.indexOf(type) >= 0 && !this._events[type]) {
+
  162.      1        this.configure(type, false);
+
  163.      1    }
+
  164.      2    return val;
+
  165.      2};
+
  166.      2
+
  167.      1Database.prototype.removeAllListeners = function(type) {
+
  168.      1    const val = EventEmitter.prototype.removeAllListeners.apply(this, arguments);
+
  169.      1    if (supportedEvents.indexOf(type) >= 0) {
+
  170.      1        this.configure(type, false);
+
  171.      1    }
+
  172.      1    return val;
+
  173.      1};
+
  174.      2
+
  175.      2// Save the stack trace over EIO callbacks.
+
  176.      1sqlite3.verbose = function() {
+
  177.      1    if (!isVerbose) {
+
  178.      1        const trace = require('./trace');
+
  179.      1        [
+
  180.      1            'prepare',
+
  181.      1            'get',
+
  182.      1            'run',
+
  183.      1            'all',
+
  184.      1            'each',
+
  185.      1            'map',
+
  186.      1            'close',
+
  187.      1            'exec'
+
  188.      8        ].forEach(function (name) {
+
  189.      8            trace.extendTrace(Database.prototype, name);
+
  190.      8        });
+
  191.      1        [
+
  192.      1            'bind',
+
  193.      1            'get',
+
  194.      1            'run',
+
  195.      1            'all',
+
  196.      1            'each',
+
  197.      1            'map',
+
  198.      1            'reset',
+
  199.      1            'finalize',
+
  200.      8        ].forEach(function (name) {
+
  201.      8            trace.extendTrace(Statement.prototype, name);
+
  202.      8        });
+
  203.      1        isVerbose = true;
+
  204.      1    }
+
  205.      1
+
  206.      1    return this;
+
  207.      1};
+
  208.      2
+
+ + + + diff --git a/.artifact/coverage_sqlite3_js/lib/trace.js.html b/.artifact/coverage_sqlite3_js/lib/trace.js.html new file mode 100644 index 000000000..28608da6d --- /dev/null +++ b/.artifact/coverage_sqlite3_js/lib/trace.js.html @@ -0,0 +1,207 @@ + + + +V8 Coverage Report + + + + +
+
V8 Coverage Report
+ + + + + + + + + + + + + + + +
Files coveredLinesRemaining
+ . / lib/trace.js
+
+
+
+
+ 100.00 %
+ 39 / 39 +
+
+ 0 / 39 +
+
+ + +
+
    1.      1// Inspired by https://github.com/tlrobinson/long-stack-traces
+
    2.      1const util = require('util');
+
    3.      1
+
    4.     16function extendTrace(object, property, pos) {
+
    5.     16    const old = object[property];
+
    6.      1    object[property] = function() {
+
    7.      1        const error = new Error();
+
    8.      1        const name = object.constructor.name + '#' + property + '(' +
+
    9.      2            Array.prototype.slice.call(arguments).map(function(el) {
+
   10.      2                return util.inspect(el, false, 0);
+
   11.      2            }).join(', ') + ')';
+
   12.      1
+
   13.      1        if (typeof pos === 'undefined') pos = -1;
+
   14.      1        if (pos < 0) pos += arguments.length;
+
   15.      1        const cb = arguments[pos];
+
   16.      1        if (typeof arguments[pos] === 'function') {
+
   17.      1            arguments[pos] = function replacement() {
+
   18.      1                const err = arguments[0];
+
   19.      1                if (err && err.stack && !err.__augmented) {
+
   20.      1                    err.stack = filter(err).join('\n');
+
   21.      1                    err.stack += '\n--> in ' + name;
+
   22.      1                    err.stack += '\n' + filter(error).slice(1).join('\n');
+
   23.      1                    err.__augmented = true;
+
   24.      1                }
+
   25.      1                return cb.apply(this, arguments);
+
   26.      1            };
+
   27.      1        }
+
   28.      1        return old.apply(this, arguments);
+
   29.      1    };
+
   30.     16}
+
   31.      1exports.extendTrace = extendTrace;
+
   32.      1
+
   33.      1
+
   34.      2function filter(error) {
+
   35.     13    return error.stack.split('\n').filter(function(line) {
+
   36.     13        return line.indexOf(__filename) < 0;
+
   37.     13    });
+
   38.      2}
+
   39.      1
+
+ + + + diff --git a/.artifact/coverage_sqlite3_js/touch.txt b/.artifact/coverage_sqlite3_js/touch.txt new file mode 100644 index 000000000..e69de29bb diff --git a/.artifact/coverage_sqlite3_sh/coverage_badge.svg b/.artifact/coverage_sqlite3_sh/coverage_badge.svg new file mode 100644 index 000000000..8e28d5ff5 --- /dev/null +++ b/.artifact/coverage_sqlite3_sh/coverage_badge.svg @@ -0,0 +1,14 @@ + + + + +coverage +97.62 % + + diff --git a/.artifact/coverage_sqlite3_sh/coverage_report.txt b/.artifact/coverage_sqlite3_sh/coverage_report.txt new file mode 100644 index 000000000..b19e8684a --- /dev/null +++ b/.artifact/coverage_sqlite3_sh/coverage_report.txt @@ -0,0 +1,16 @@ +V8 Coverage Report ++----------------------------------+-------------------+-------------------+ +| Files covered | Lines | Remaining | ++----------------------------------+-------------------+-------------------+ +| ./ | 97.62 % | | +| *******************************_ | 247 / 253 | 6 / 253 | ++----------------------------------+-------------------+-------------------+ +| ./lib/sqlite3-binding.js | 100.00 % | | +| ******************************** | 6 / 6 | 0 / 6 | ++----------------------------------+-------------------+-------------------+ +| ./lib/sqlite3.js | 97.11 % | | +| *******************************_ | 202 / 208 | 6 / 208 | ++----------------------------------+-------------------+-------------------+ +| ./lib/trace.js | 100.00 % | | +| ******************************** | 39 / 39 | 0 / 39 | ++----------------------------------+-------------------+-------------------+ diff --git a/.artifact/coverage_sqlite3_sh/index.html b/.artifact/coverage_sqlite3_sh/index.html new file mode 100644 index 000000000..83d95a7bd --- /dev/null +++ b/.artifact/coverage_sqlite3_sh/index.html @@ -0,0 +1,212 @@ + + + +V8 Coverage Report + + + + +
+
V8 Coverage Report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Files coveredLinesRemaining
+ . /
+
+
+
+
+ 97.62 %
+ 247 / 253 +
+
+ 6 / 253 +
+ . / lib/sqlite3-binding.js
+
+
+
+
+ 100.00 %
+ 6 / 6 +
+
+ 0 / 6 +
+ . / lib/sqlite3.js
+
+
+
+
+ 97.11 %
+ 202 / 208 +
+
+ 6 / 208 +
+ . / lib/trace.js
+
+
+
+
+ 100.00 %
+ 39 / 39 +
+
+ 0 / 39 +
+
+ + + + diff --git a/.artifact/coverage_sqlite3_sh/lib/sqlite3-binding.js.html b/.artifact/coverage_sqlite3_sh/lib/sqlite3-binding.js.html new file mode 100644 index 000000000..d3fec08a6 --- /dev/null +++ b/.artifact/coverage_sqlite3_sh/lib/sqlite3-binding.js.html @@ -0,0 +1,174 @@ + + + +V8 Coverage Report + + + + +
+
V8 Coverage Report
+ + + + + + + + + + + + + + + +
Files coveredLinesRemaining
+ . / lib/sqlite3-binding.js
+
+
+
+
+ 100.00 %
+ 6 / 6 +
+
+ 0 / 6 +
+
+ + +
+
    1.      2const binary = require('@mapbox/node-pre-gyp');
+
    2.      2const path = require('path');
+
    3.      2const binding_path = binary.find(path.resolve(path.join(__dirname,'../package.json')));
+
    4.      2const binding = require(binding_path);
+
    5.      2module.exports = exports = binding;
+
    6.      2
+
+ + + + diff --git a/.artifact/coverage_sqlite3_sh/lib/sqlite3.js.html b/.artifact/coverage_sqlite3_sh/lib/sqlite3.js.html new file mode 100644 index 000000000..db949263d --- /dev/null +++ b/.artifact/coverage_sqlite3_sh/lib/sqlite3.js.html @@ -0,0 +1,376 @@ + + + +V8 Coverage Report + + + + +
+
V8 Coverage Report
+ + + + + + + + + + + + + + + +
Files coveredLinesRemaining
+ . / lib/sqlite3.js
+
+
+
+
+ 97.11 %
+ 202 / 208 +
+
+ 6 / 208 +
+
+ + +
+
    1.      2const path = require('path');
+
    2.      2const sqlite3 = require('./sqlite3-binding.js');
+
    3.      2const EventEmitter = require('events').EventEmitter;
+
    4.      2module.exports = exports = sqlite3;
+
    5.      2
+
    6.     12function normalizeMethod (fn) {
+
    7.   3938    return function (sql) {
+
    8.   3938        let errBack;
+
    9.   3938        const args = Array.prototype.slice.call(arguments, 1);
+
   10.   3938
+
   11.   1119        if (typeof args[args.length - 1] === 'function') {
+
   12.   1119            const callback = args[args.length - 1];
+
   13.   1119            errBack = function(err) {
+
   14.   1119                if (err) {
+
   15.   1119                    callback(err);
+
   16.   1119                }
+
   17.   1119            };
+
   18.   1119        }
+
   19.   3938        const statement = new Statement(this, sql, errBack);
+
   20.   3938        return fn.call(this, statement, args);
+
   21.   3938    };
+
   22.     12}
+
   23.      2
+
   24.      6function inherits(target, source) {
+
   25.      6    for (const k in source.prototype)
+
   26.    108        target.prototype[k] = source.prototype[k];
+
   27.      6}
+
   28.      2
+
   29.      2sqlite3.cached = {
+
   30.      4    Database: function(file, a, b) {
+
   31.     -0        if (file === '' || file === ':memory:') {
+
   32.     -0            // Don't cache special databases.
+
   33.     -0            return new Database(file, a, b);
+
   34.     -0        }
+
   35.      4
+
   36.      4        let db;
+
   37.      4        file = path.resolve(file);
+
   38.      4
+
   39.      2        if (!sqlite3.cached.objects[file]) {
+
   40.      2            db = sqlite3.cached.objects[file] = new Database(file, a, b);
+
   41.      2        }
+
   42.      2        else {
+
   43.      2            // Make sure the callback is called.
+
   44.      2            db = sqlite3.cached.objects[file];
+
   45.     -0            const callback = (typeof a === 'number') ? b : a;
+
   46.      2            if (typeof callback === 'function') {
+
   47.      2                function cb() { callback.call(db, null); }
+
   48.      2                if (db.open) process.nextTick(cb);
+
   49.      2                else db.once('open', cb);
+
   50.      2            }
+
   51.      2        }
+
   52.      4
+
   53.      4        return db;
+
   54.      4    },
+
   55.      2    objects: {}
+
   56.      2};
+
   57.      2
+
   58.      2
+
   59.      2const Database = sqlite3.Database;
+
   60.      2const Statement = sqlite3.Statement;
+
   61.      2const Backup = sqlite3.Backup;
+
   62.      2
+
   63.      2inherits(Database, EventEmitter);
+
   64.      2inherits(Statement, EventEmitter);
+
   65.      2inherits(Backup, EventEmitter);
+
   66.      2
+
   67.      2// Database#prepare(sql, [bind1, bind2, ...], [callback])
+
   68.   1811Database.prototype.prepare = normalizeMethod(function(statement, params) {
+
   69.   1811    return params.length
+
   70.      7        ? statement.bind.apply(statement, params)
+
   71.   1804        : statement;
+
   72.   1811});
+
   73.      2
+
   74.      2// Database#run(sql, [bind1, bind2, ...], [callback])
+
   75.   2074Database.prototype.run = normalizeMethod(function(statement, params) {
+
   76.   2074    statement.run.apply(statement, params).finalize();
+
   77.   2074    return this;
+
   78.   2074});
+
   79.      2
+
   80.      2// Database#get(sql, [bind1, bind2, ...], [callback])
+
   81.     35Database.prototype.get = normalizeMethod(function(statement, params) {
+
   82.     35    statement.get.apply(statement, params).finalize();
+
   83.     35    return this;
+
   84.     35});
+
   85.      2
+
   86.      2// Database#all(sql, [bind1, bind2, ...], [callback])
+
   87.      9Database.prototype.all = normalizeMethod(function(statement, params) {
+
   88.      9    statement.all.apply(statement, params).finalize();
+
   89.      9    return this;
+
   90.      9});
+
   91.      2
+
   92.      2// Database#each(sql, [bind1, bind2, ...], [callback], [complete])
+
   93.      7Database.prototype.each = normalizeMethod(function(statement, params) {
+
   94.      7    statement.each.apply(statement, params).finalize();
+
   95.      7    return this;
+
   96.      7});
+
   97.      2
+
   98.      2Database.prototype.map = normalizeMethod(function(statement, params) {
+
   99.      2    statement.map.apply(statement, params).finalize();
+
  100.      2    return this;
+
  101.      2});
+
  102.      2
+
  103.      2// Database#backup(filename, [callback])
+
  104.      2// Database#backup(filename, destName, sourceName, filenameIsDest, [callback])
+
  105.     15Database.prototype.backup = function() {
+
  106.     15    let backup;
+
  107.     13    if (arguments.length <= 2) {
+
  108.     13        // By default, we write the main database out to the main database of the named file.
+
  109.     13        // This is the most likely use of the backup api.
+
  110.     13        backup = new Backup(this, arguments[0], 'main', 'main', true, arguments[1]);
+
  111.     13    } else {
+
  112.      2        // Otherwise, give the user full control over the sqlite3_backup_init arguments.
+
  113.      2        backup = new Backup(this, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]);
+
  114.      2    }
+
  115.     15    // Per the sqlite docs, exclude the following errors as non-fatal by default.
+
  116.     15    backup.retryErrors = [sqlite3.BUSY, sqlite3.LOCKED];
+
  117.     15    return backup;
+
  118.     15};
+
  119.      2
+
  120.      2Statement.prototype.map = function() {
+
  121.      2    const params = Array.prototype.slice.call(arguments);
+
  122.      2    const callback = params.pop();
+
  123.      2    params.push(function(err, rows) {
+
  124.     -0        if (err) return callback(err);
+
  125.      2        const result = {};
+
  126.      2        if (rows.length) {
+
  127.      2            const keys = Object.keys(rows[0]);
+
  128.      2            const key = keys[0];
+
  129.      1            if (keys.length > 2) {
+
  130.      1                // Value is an object
+
  131.      5                for (let i = 0; i < rows.length; i++) {
+
  132.      5                    result[rows[i][key]] = rows[i];
+
  133.      5                }
+
  134.      1            } else {
+
  135.      1                const value = keys[1];
+
  136.      1                // Value is a plain value
+
  137.      5                for (let i = 0; i < rows.length; i++) {
+
  138.      5                    result[rows[i][key]] = rows[i][value];
+
  139.      5                }
+
  140.      1            }
+
  141.      2        }
+
  142.      2        callback(err, result);
+
  143.      2    });
+
  144.      2    return this.all.apply(this, params);
+
  145.      2};
+
  146.      2
+
  147.      2let isVerbose = false;
+
  148.      2
+
  149.      2const supportedEvents = [ 'trace', 'profile', 'insert', 'update', 'delete' ];
+
  150.      2
+
  151.      7Database.prototype.addListener = Database.prototype.on = function(type) {
+
  152.      7    const val = EventEmitter.prototype.addListener.apply(this, arguments);
+
  153.      4    if (supportedEvents.indexOf(type) >= 0) {
+
  154.      4        this.configure(type, true);
+
  155.      4    }
+
  156.      7    return val;
+
  157.      7};
+
  158.      2
+
  159.      2Database.prototype.removeListener = function(type) {
+
  160.      2    const val = EventEmitter.prototype.removeListener.apply(this, arguments);
+
  161.      1    if (supportedEvents.indexOf(type) >= 0 && !this._events[type]) {
+
  162.      1        this.configure(type, false);
+
  163.      1    }
+
  164.      2    return val;
+
  165.      2};
+
  166.      2
+
  167.      1Database.prototype.removeAllListeners = function(type) {
+
  168.      1    const val = EventEmitter.prototype.removeAllListeners.apply(this, arguments);
+
  169.      1    if (supportedEvents.indexOf(type) >= 0) {
+
  170.      1        this.configure(type, false);
+
  171.      1    }
+
  172.      1    return val;
+
  173.      1};
+
  174.      2
+
  175.      2// Save the stack trace over EIO callbacks.
+
  176.      1sqlite3.verbose = function() {
+
  177.      1    if (!isVerbose) {
+
  178.      1        const trace = require('./trace');
+
  179.      1        [
+
  180.      1            'prepare',
+
  181.      1            'get',
+
  182.      1            'run',
+
  183.      1            'all',
+
  184.      1            'each',
+
  185.      1            'map',
+
  186.      1            'close',
+
  187.      1            'exec'
+
  188.      8        ].forEach(function (name) {
+
  189.      8            trace.extendTrace(Database.prototype, name);
+
  190.      8        });
+
  191.      1        [
+
  192.      1            'bind',
+
  193.      1            'get',
+
  194.      1            'run',
+
  195.      1            'all',
+
  196.      1            'each',
+
  197.      1            'map',
+
  198.      1            'reset',
+
  199.      1            'finalize',
+
  200.      8        ].forEach(function (name) {
+
  201.      8            trace.extendTrace(Statement.prototype, name);
+
  202.      8        });
+
  203.      1        isVerbose = true;
+
  204.      1    }
+
  205.      1
+
  206.      1    return this;
+
  207.      1};
+
  208.      2
+
+ + + + diff --git a/.artifact/coverage_sqlite3_sh/lib/trace.js.html b/.artifact/coverage_sqlite3_sh/lib/trace.js.html new file mode 100644 index 000000000..28608da6d --- /dev/null +++ b/.artifact/coverage_sqlite3_sh/lib/trace.js.html @@ -0,0 +1,207 @@ + + + +V8 Coverage Report + + + + +
+
V8 Coverage Report
+ + + + + + + + + + + + + + + +
Files coveredLinesRemaining
+ . / lib/trace.js
+
+
+
+
+ 100.00 %
+ 39 / 39 +
+
+ 0 / 39 +
+
+ + +
+
    1.      1// Inspired by https://github.com/tlrobinson/long-stack-traces
+
    2.      1const util = require('util');
+
    3.      1
+
    4.     16function extendTrace(object, property, pos) {
+
    5.     16    const old = object[property];
+
    6.      1    object[property] = function() {
+
    7.      1        const error = new Error();
+
    8.      1        const name = object.constructor.name + '#' + property + '(' +
+
    9.      2            Array.prototype.slice.call(arguments).map(function(el) {
+
   10.      2                return util.inspect(el, false, 0);
+
   11.      2            }).join(', ') + ')';
+
   12.      1
+
   13.      1        if (typeof pos === 'undefined') pos = -1;
+
   14.      1        if (pos < 0) pos += arguments.length;
+
   15.      1        const cb = arguments[pos];
+
   16.      1        if (typeof arguments[pos] === 'function') {
+
   17.      1            arguments[pos] = function replacement() {
+
   18.      1                const err = arguments[0];
+
   19.      1                if (err && err.stack && !err.__augmented) {
+
   20.      1                    err.stack = filter(err).join('\n');
+
   21.      1                    err.stack += '\n--> in ' + name;
+
   22.      1                    err.stack += '\n' + filter(error).slice(1).join('\n');
+
   23.      1                    err.__augmented = true;
+
   24.      1                }
+
   25.      1                return cb.apply(this, arguments);
+
   26.      1            };
+
   27.      1        }
+
   28.      1        return old.apply(this, arguments);
+
   29.      1    };
+
   30.     16}
+
   31.      1exports.extendTrace = extendTrace;
+
   32.      1
+
   33.      1
+
   34.      2function filter(error) {
+
   35.     13    return error.stack.split('\n').filter(function(line) {
+
   36.     13        return line.indexOf(__filename) < 0;
+
   37.     13    });
+
   38.      2}
+
   39.      1
+
+ + + + diff --git a/.artifact/coverage_sqlite3_sh/touch.txt b/.artifact/coverage_sqlite3_sh/touch.txt new file mode 100644 index 000000000..e69de29bb diff --git a/.artifact/jslint_report_hello.html b/.artifact/jslint_report_hello.html new file mode 100644 index 000000000..ffa15a7ff --- /dev/null +++ b/.artifact/jslint_report_hello.html @@ -0,0 +1,246 @@ + + +
+JSLint Report +
+
+Report: Warnings (2) +
+
1: 17
1. Undeclared 'console'.
function foo() {console.log('hello world');} + +
1: 29
2. Use double quotes, not single quotes.
function foo() {console.log('hello world');} + +
+
+
+Report: Properties (1) + +
+
+Report: Functions (1) +
+
+
global
foo
+
1: 1
foo()
+
+
+ diff --git a/.artifact/jslint_wrapper_vscode/.vscode/launch.json b/.artifact/jslint_wrapper_vscode/.vscode/launch.json new file mode 100644 index 000000000..140e1f4ec --- /dev/null +++ b/.artifact/jslint_wrapper_vscode/.vscode/launch.json @@ -0,0 +1,13 @@ +{ + "configurations": [ + { + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}" + ], + "name": "Run Extension", + "request": "launch", + "type": "extensionHost" + } + ], + "version": "0.0.1" +} diff --git a/.artifact/jslint_wrapper_vscode/.vscodeignore b/.artifact/jslint_wrapper_vscode/.vscodeignore new file mode 100644 index 000000000..b81046e0b --- /dev/null +++ b/.artifact/jslint_wrapper_vscode/.vscodeignore @@ -0,0 +1,12 @@ +* +.* +node_modules +!.npmignore +!CHANGELOG.md +!LICENSE +!README.md + +!asset_* +!jslint.mjs +!jslint_wrapper_cjs.cjs +!jslint_wrapper_vscode.js diff --git a/.artifact/jslint_wrapper_vscode/LICENSE b/.artifact/jslint_wrapper_vscode/LICENSE new file mode 100644 index 000000000..b3dbff00c --- /dev/null +++ b/.artifact/jslint_wrapper_vscode/LICENSE @@ -0,0 +1,22 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/.artifact/jslint_wrapper_vscode/README.md b/.artifact/jslint_wrapper_vscode/README.md new file mode 100644 index 000000000..af9808967 --- /dev/null +++ b/.artifact/jslint_wrapper_vscode/README.md @@ -0,0 +1,9 @@ +# Quickstart JSLint in VSCode +1. In VSCode, search and install extension [`vscode-jslint`](https://marketplace.visualstudio.com/items?itemName=jslint.vscode-jslint) +2. In VSCode, while editing a javascript file: + - right-click context-menu and select `[JSLint - Lint File]` + - or use key-binding `[Ctrl + Shift + J], [L]` + - or use key-binding `[ Cmd + Shift + J], [L]` for Mac +- screenshot + +[![screenshot](https://jslint-org.github.io/jslint/asset_image_jslint_wrapper_vscode.png)](https://marketplace.visualstudio.com/items?itemName=jslint.vscode-jslint) diff --git a/.artifact/jslint_wrapper_vscode/asset_image_logo_512.png b/.artifact/jslint_wrapper_vscode/asset_image_logo_512.png new file mode 100644 index 000000000..19d154d68 Binary files /dev/null and b/.artifact/jslint_wrapper_vscode/asset_image_logo_512.png differ diff --git a/.artifact/jslint_wrapper_vscode/jslint.mjs b/.artifact/jslint_wrapper_vscode/jslint.mjs new file mode 100644 index 000000000..b486c1ce2 --- /dev/null +++ b/.artifact/jslint_wrapper_vscode/jslint.mjs @@ -0,0 +1,11573 @@ +// #!/usr/bin/env node +// JSLint + +// The Unlicense +// +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + + +// jslint(source, option_dict, global_list) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze. +// option_dict An object whose keys correspond to option names. +// global_list An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// PHASE 1. Split by newlines into . +// PHASE 2. Lex into . +// PHASE 3. Parse into using the Pratt-parser. +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// PHASE 5. Check whitespace between tokens in . + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*jslint beta, node*/ +/*property + JSLINT_BETA, NODE_V8_COVERAGE, a, all, argv, arity, artifact, + assertErrorThrownAsync, assertJsonEqual, assertOrThrow, assign, async, b, + beta, bitwise, block, body, browser, c, calls, catch, catch_list, + catch_stack, causes, char, children, clear, closer, closure, code, column, + concat, consoleError, console_error, console_log, constant, context, + convert, count, coverageDir, create, cwd, d, dead, debugInline, default, + delta, devel, directive, directive_ignore_line, directive_list, directives, + dirname, disrupt, dot, edition, elem_list, ellipsis, else, end, endOffset, + endsWith, entries, env, error, eval, every, example_list, excludeList, exec, + execArgv, exit, exitCode, export_dict, exports, expression, extra, fart, + file, fileList, fileURLToPath, filter, finally, flag, floor, for, forEach, + formatted_message, free, freeze, from, froms, fsWriteFileWithParents, + fud_stmt, functionName, function_list, function_stack, functions, get, + getset, github_repo, globExclude, global, global_dict, global_list, + holeList, htmlEscape, id, identifier, import, import_list, import_meta_url, + inc, includeList, indent2, index, indexOf, init, initial, isArray, + isBlockCoverage, isHole, isNaN, is_equal, is_weird, join, jslint, + jslint_apidoc, jslint_assert, jslint_charset_ascii, jslint_cli, + jslint_edition, jslint_phase1_split, jslint_phase2_lex, jslint_phase3_parse, + jslint_phase4_walk, jslint_phase5_whitage, jslint_report, json, + jstestDescribe, jstestIt, jstestOnExit, keys, label, lbp, led_infix, length, + level, line, lineList, line_list, line_offset, line_source, lines, + linesCovered, linesTotal, live, log, long, loop, m, map, margin, match, max, + message, meta, min, mkdir, modeCoverageIgnoreFile, modeIndex, mode_cli, + mode_conditional, mode_json, mode_module, mode_noop, mode_property, + mode_shebang, mode_stop, module, moduleFsInit, moduleName, module_list, + name, names, node, nomen, noop, now, nr, nud_prefix, + objectDeepCopyWithKeysSorted, ok, on, open, opening, option, option_dict, + order, package_name, padEnd, padStart, parameters, parent, parentIi, parse, + pathname, pathnameList, platform, pop, processArgv, process_argv, + process_env, process_exit, promises, property, property_dict, push, quote, + ranges, readFile, readdir, readonly, recursive, reduce, repeat, replace, + resolve, result, reverse, role, round, scriptId, search, set, shebang, + shell, shift, signature, single, slice, some, sort, source, spawn, splice, + split, stack, stack_trace, start, startOffset, startsWith, statement, + statement_prv, stdio, stop, stop_at, stringify, subscript, switch, + syntax_dict, tenure, test, test_cause, test_internal_error, this, thru, + toLocaleString, toString, token, token_global, token_list, token_nxt, + token_tree, tokens, trace, tree, trim, trimEnd, trimRight, try, type, + unlink, unordered, unshift, url, used, v8CoverageListMerge, + v8CoverageReportCreate, value, variable, version, versions, warn, warn_at, + warning, warning_list, warnings, white, wrapped, writeFile +*/ + +// init debugInline +let debugInline = (function () { + let __consoleError = function () { + return; + }; + function debug(...argv) { + +// This function will print to stderr and then return [0]. + + __consoleError("\n\ndebugInline"); + __consoleError(...argv); + __consoleError("\n"); + return argv[0]; + } + debug(); // Coverage-hack. + __consoleError = console.error; //jslint-ignore-line + return debug; +}()); +let jslint_charset_ascii = ( + "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007" + + "\b\t\n\u000b\f\r\u000e\u000f" + + "\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017" + + "\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f" + + " !\"#$%&'()*+,-./0123456789:;<=>?" + + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + + "`abcdefghijklmnopqrstuvwxyz{|}~\u007f" +); +let jslint_edition = "v2024.11.24"; +let jslint_export; // The jslint object to be exported. +let jslint_fudge = 1; // Fudge starting line and starting + // ... column to 1. +let jslint_import_meta_url = ""; // import.meta.url used by cli. +let jslint_rgx_cap = ( + /^[A-Z]/ +); +let jslint_rgx_crlf = ( + /\n|\r\n?/ +); +let jslint_rgx_digits_bits = ( + /^[01_]*/ +); +let jslint_rgx_digits_decimals = ( + /^[0-9_]*/ +); +let jslint_rgx_digits_hexs = ( + /^[0-9A-F_]*/i +); +let jslint_rgx_digits_octals = ( + /^[0-7_]*/ +); +let jslint_rgx_directive = ( + /^(jslint|property|global)\s+(.*)$/ +); +let jslint_rgx_directive_part = ( + /([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*|$/g +); +let jslint_rgx_identifier = ( + /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/ +); +let jslint_rgx_json_number = ( + +// https://datatracker.ietf.org/doc/html/rfc7159#section-6 +// number = [ minus ] int [ frac ] [ exp ] + + /^-?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][\-+]?\d+)?$/ +); +let jslint_rgx_mega = ( + +// Vim-hack - vim-editor has trouble parsing naked '`' in regexp + + /[\u0060\\]|\$\{/ +); +let jslint_rgx_module = ( + /^[a-zA-Z0-9_$:.@\-\/]+$/ +); +let jslint_rgx_numeric_separator_illegal = ( + /__|_$|_n$/m +); +let jslint_rgx_slash_star_or_slash = ( + /\/\*|\/$/ +); +let jslint_rgx_tab = ( + /\t/g +); +let jslint_rgx_todo = ( + /\b(?:todo|TO\s?DO|HACK)\b/ +); +let jslint_rgx_token = new RegExp( + "^(" + + "(\\s+)" + + "|([a-zA-Z_$][a-zA-Z0-9_$]*)" + + "|[(){}\\[\\],:;'\"~\\`]" + + "|\\?[?.]?" + + "|=(?:==?|>)?" + + "|\\.+" + + "|\\*[*\\/=]?" + + "|\\/[*\\/]?" + + "|\\+[=+]?" + + "|-[=\\-]?" + + "|[\\^%]=?" + + "|&[&=]?" + + "|\\" + + "|[|=]?" + + "|>{1,3}=?" + + "|< throws an error. + + let err; + try { + await asyncFunc(); + } catch (errCaught) { + err = errCaught; + } + assertOrThrow(err, "No error thrown."); + assertOrThrow( + !regexp || new RegExp(regexp).test(err.message), + err + ); +} + +function assertJsonEqual(aa, bb, message) { + +// This function will assert JSON.stringify() === JSON.stringify(). + + aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa), undefined, 1); + bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb), undefined, 1); + if (aa !== bb) { + throw new Error( + "\n" + aa + "\n!==\n" + bb + + ( + typeof message === "string" + ? " - " + message + : message + ? " - " + JSON.stringify(message) + : "" + ) + ); + } +} + +function assertOrThrow(condition, message) { + +// This function will throw if is falsy. + + if (!condition) { + throw ( + (!message || typeof message === "string") + ? new Error(String(message).slice(0, 2048)) + : message + ); + } +} + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than '{}' because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +async function fsWriteFileWithParents(pathname, data) { + +// This function will write to and lazy-mkdirp if necessary. + + await moduleFsInit(); + +// Try writing to pathname. + + try { + await moduleFs.promises.writeFile(pathname, data); + } catch (ignore) { + +// Lazy mkdirp. + + await moduleFs.promises.mkdir(modulePath.dirname(pathname), { + recursive: true + }); + +// Retry writing to pathname. + + await moduleFs.promises.writeFile(pathname, data); + } + console.error("wrote file " + pathname); +} + +function globExclude({ + excludeList = [], + includeList = [], + pathnameList = [] +}) { + +// This function will +// 1. Exclude pathnames in that don't match glob-patterns in +// . +// 2. Exclude pathnames in that match glob-patterns in +// . + + function globAssertNotWeird(list, name) { + +// This function will check if of strings contain weird characters. + + [ + [ + "\n", ( + /^.*?([\u0000-\u0007\r]).*/gm + ) + ], + [ + "\r", ( + /^.*?([\n]).*/gm + ) + ] + ].forEach(function ([ + separator, rgx + ]) { + list.join(separator).replace(rgx, function (match0, char) { + throw new Error( + "Weird character " + + JSON.stringify(char) + + " found in " + name + " " + + JSON.stringify(match0) + ); + }); + }); + } + + function globToRegexp(pattern) { + +// This function will translate glob to javascript-regexp, +// which javascript can then use to "glob" pathnames. + + let ii = 0; + let isClass = false; + let strClass = ""; + let strRegex = ""; + pattern = pattern.replace(( + /\/\/+/g + ), "/"); + pattern = pattern.replace(( + /\*\*\*+/g + ), "**"); + pattern.replace(( + /\\\\|\\\[|\\\]|\[|\]|./g + ), function (match0) { + switch (match0) { + case "[": + if (isClass) { + strClass += "["; + return; + } + strClass += "\u0000"; + strRegex += "\u0000"; + isClass = true; + return; + case "]": + if (isClass) { + isClass = false; + return; + } + strRegex += "]"; + return; + default: + if (isClass) { + strClass += match0; + return; + } + strRegex += match0; + } + return ""; + }); + strClass += "\u0000"; + +// An expression "[!...]" matches a single character, namely any character that +// is not matched by the expression obtained by removing the first '!' from it. +// (Thus, "[!a-]" matches any single character except 'a', and '-'.) + + strClass = strClass.replace(( + /\u0000!/g + ), "\u0000^"); + +// One may include '-' in its literal meaning by making it the first or last +// character between the brackets. + + strClass = strClass.replace(( + /\u0000-/g + ), "\u0000\\-"); + strClass = strClass.replace(( + /-\u0000/g + ), "\\-\u0000"); + +// Escape brackets '[', ']' in character class. + + strClass = strClass.replace(( + /[\[\]]/g + ), "\\$&"); + +// https://stackoverflow.com/questions/3561493 +// /is-there-a-regexp-escape-function-in-javascript +// $()*+-./?[\]^{|} + + strRegex = strRegex.replace(( + +// Ignore [-/]. + + /[$()*+.?\[\\\]\^{|}]/g + ), "\\$&"); + +// Expand wildcard '**/*'. + + strRegex = strRegex.replace(( + /\\\*\\\*\/(?:\\\*)+/g + ), ".*?"); + +// Expand wildcard '**'. + + strRegex = strRegex.replace(( + /(^|\/)\\\*\\\*(\/|$)/gm + ), "$1.*?$2"); + +// Expand wildcard '*'. + + strRegex = strRegex.replace(( + /(?:\\\*)+/g + ), "[^\\/]*?"); + +// Expand wildcard '?'. + + strRegex = strRegex.replace(( + /\\\?/g + ), "[^\\/]"); + +// Expand directory-with-trailing-slash '.../'. + + strRegex = strRegex.replace(( + /\/$/gm + ), "\\/.*?"); + +// Merge strClass into strRegex. + + ii = 0; + strClass = strClass.split("\u0000"); + strRegex = strRegex.replace(( + /\u0000/g + ), function () { + ii += 1; + if (strClass[ii] === "") { + return ""; + } + return "[" + strClass[ii] + "]"; + }); + +// Change strRegex from string to regexp. + + strRegex = new RegExp("^" + strRegex + "$", "gm"); + return strRegex; + } + +// Validate excludeList, includeList, pathnameList. + + globAssertNotWeird(excludeList, "pattern"); + globAssertNotWeird(includeList, "pattern"); + globAssertNotWeird(pathnameList, "pathname"); + +// Optimization +// Concat pathnames into a single, newline-separated string, +// whose pathnames can all be filtered with a single, regexp-pass. + + pathnameList = pathnameList.join("\n"); + +// 1. Exclude pathnames in that don't match glob-patterns in +// . + + if (includeList.length > 0) { + includeList = includeList.map(globToRegexp); + includeList.forEach(function (pattern) { + pathnameList = pathnameList.replace(pattern, "\u0000$&"); + }); + pathnameList = pathnameList.replace(( + /^[^\u0000].*/gm + ), ""); + pathnameList = pathnameList.replace(( + /^\u0000+/gm + ), ""); + } + +// 2. Exclude pathnames in that match glob-patterns in +// . + + excludeList = excludeList.map(globToRegexp); + excludeList.forEach(function (pattern) { + pathnameList = pathnameList.replace(pattern, ""); + }); + +// Split newline-separated pathnames back to list. + + pathnameList = pathnameList.split("\n").filter(function (elem) { + return elem; + }); + return { + excludeList, + includeList, + pathnameList + }; +} + +function htmlEscape(str) { + +// This function will make html-safe by escaping & < >. + + return String(str).replace(( + /&/g + ), "&").replace(( + //g + ), ">"); +} + +function jslint( + source = "", // A text to analyze. + option_dict = empty(), // An object whose keys correspond to option + // ... names. + global_list = [] // An array of strings containing global + // ... variables that the file is allowed + // ... readonly access. +) { + +// The jslint function itself. + + let catch_list = []; // The array containing all catch-blocks. + let catch_stack = [ // The stack of catch-blocks. + { + context: empty() + } + ]; + let cause_dict = empty(); // The object of test-causes. + let directive_list = []; // The directive comments. + let export_dict = empty(); // The exported names and values. + let function_list = []; // The array containing all functions. + let function_stack = []; // The stack of functions. + let global_dict = empty(); // The object containing the global + // ... declarations. + let import_list = []; // The array collecting all import-from strings. + let line_list = String( // The array containing source lines. + "\n" + source + ).split(jslint_rgx_crlf).map(function (line_source) { + return { + line_source + }; + }); + let mode_stop = false; // true if JSLint cannot finish. + let property_dict = empty(); // The object containing the tallied + // ... property names. + let state = empty(); // jslint state-object to be passed between + // jslint functions. + let syntax_dict = empty(); // The object containing the parser. + let tenure = empty(); // The predefined property registry. + let token_global = { // The global object; the outermost context. + async: 0, + body: true, + context: empty(), + finally: 0, + from: 0, + id: "(global)", + level: 0, + line: jslint_fudge, + live: [], + loop: 0, + switch: 0, + thru: 0, + try: 0 + }; + let token_list = []; // The array of tokens. + let warning_list = []; // The array collecting all generated warnings. + +// Error reportage functions: + + function artifact(the_token) { + +// Return a string representing an artifact. + + the_token = the_token || state.token_nxt; + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); + } + + function is_equal(aa, bb) { + +// test_cause: +// ["0&&0", "is_equal", "", "", 0] + + test_cause(""); + +// Probably deadcode. +// if (aa === bb) { +// return true; +// } + + jslint_assert(!(aa === bb), `Expected !(aa === bb).`); + if (Array.isArray(aa)) { + return ( + Array.isArray(bb) + && aa.length === bb.length + && aa.every(function (value, index) { + +// test_cause: +// ["`${0}`&&`${0}`", "is_equal", "recurse_isArray", "", 0] +// ["`${0}`&&`${1}`", "is_equal", "recurse_isArray", "", 0] + + test_cause("recurse_isArray"); + return is_equal(value, bb[index]); + }) + ); + } + +// Probably deadcode. +// if (Array.isArray(bb)) { +// return false; +// } + + jslint_assert(!Array.isArray(bb), `Expected !Array.isArray(bb).`); + switch (aa.id === bb.id && aa.id) { + case "(number)": + case "(string)": + return aa.value === bb.value; + +// PR-394 - Bugfix +// Fix jslint falsely believing megastring literals `0` and `1` are similar. + + case "`": + if (!is_equal(aa.value, bb.value)) { + return false; + } + break; + } + if (is_weird(aa) || is_weird(bb)) { + +// test_cause: +// ["aa(/./)||{}", "is_equal", "false", "", 0] + + test_cause("false"); + return false; + } + if (aa.arity === bb.arity && aa.id === bb.id) { + if (aa.id === "." || aa.id === "?.") { + +// test_cause: +// ["aa.bb&&aa.bb", "is_equal", "recurse_arity_id", "", 0] +// ["aa?.bb&&aa?.bb", "is_equal", "recurse_arity_id", "", 0] + + test_cause("recurse_arity_id"); + return ( + is_equal(aa.expression, bb.expression) + && is_equal(aa.name, bb.name) + ); + } + if (aa.arity === "unary") { + +// test_cause: +// ["+0&&+0", "is_equal", "recurse_unary", "", 0] + + test_cause("recurse_unary"); + return is_equal(aa.expression, bb.expression); + } + if (aa.arity === "binary") { + +// test_cause: +// ["aa[0]&&aa[0]", "is_equal", "recurse_binary", "", 0] + + test_cause("recurse_binary"); + return ( + aa.id !== "(" + && is_equal(aa.expression[0], bb.expression[0]) + && is_equal(aa.expression[1], bb.expression[1]) + ); + } + if (aa.arity === "ternary") { + +// test_cause: +// ["aa=(``?``:``)&&(``?``:``)", "is_equal", "recurse_ternary", "", 0] + + test_cause("recurse_ternary"); + return ( + is_equal(aa.expression[0], bb.expression[0]) + && is_equal(aa.expression[1], bb.expression[1]) + && is_equal(aa.expression[2], bb.expression[2]) + ); + } + +// Probably deadcode. +// if (aa.arity === "function" || aa.arity === "regexp") { +// return false; +// } + + jslint_assert( + !(aa.arity === "function" || aa.arity === "regexp"), + `Expected !(aa.arity === "function" || aa.arity === "regexp").` + ); + +// test_cause: +// ["undefined&&undefined", "is_equal", "true", "", 0] + + test_cause("true"); + return true; + } + +// test_cause: +// ["null&&undefined", "is_equal", "false", "", 0] + + test_cause("false"); + return false; + } + + function is_weird(thing) { + switch (thing.id) { + case "(regexp)": + return true; + case "=>": + return true; + case "[": + return thing.arity === "unary"; + case "function": + return true; + case "{": + return true; + default: + return false; + } + } + + function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + the_token = the_token || state.token_nxt; + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); + } + + function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); + } + + function test_cause(code, aa, column) { + +// This function will instrument to for test-purposes. + + if (option_dict.test_cause) { + cause_dict[JSON.stringify([ + String(new Error().stack).replace(( + /^ at (?:file|stop|stop_at|test_cause|warn|warn_at)\b.*?\n/gm + ), "").match( + /\n at ((?:Object\.\w+?_)?\w+?) / + )[1].replace(( + /^Object\./ + ), ""), + code, + String( + (aa === undefined || aa === token_global) + ? "" + : aa + ), + column || 0 + ])] = true; + } + } + + function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + let the_warning; + the_token = the_token || state.token_nxt; + the_warning = warn_at( + code, + the_token.line, + (the_token.from || 0) + jslint_fudge, + a || artifact(the_token), + b, + c, + d + ); + +// Issue #408 +// Warnings that should be ignored sometimes suppress legitimate warnings. + + if (the_warning.directive_ignore_line) { + return the_warning; + } + +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token.warning) { + warning_list.pop(); + return the_warning; + } + the_token.warning = the_warning; + return the_warning; + } + + function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + let mm; + let warning = Object.assign(empty(), { + a, + b, + c, + code, + +// Fudge column numbers in warning message. + + column: column || jslint_fudge, + d, + line, + line_source: "", + name: "JSLintError" + }, line_list[line]); + warning.column = Math.max( + Math.min(warning.column, warning.line_source.length), + jslint_fudge + ); + test_cause(code, b || a, warning.column); + switch (code) { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + case "and": + mm = `The '&&' subexpression should be wrapped in parens.`; + break; + case "bad_assignment_a": + mm = `Bad assignment to '${a}'.`; + break; + case "bad_directive_a": + mm = `Bad directive '${a}'.`; + break; + case "bad_get": + mm = `A get function takes no parameters.`; + break; + case "bad_module_name_a": + mm = `Bad module name '${a}'.`; + break; + case "bad_option_a": + mm = `Bad option '${a}'.`; + break; + case "bad_set": + mm = `A set function takes one parameter.`; + break; + case "duplicate_a": + mm = `Duplicate '${a}'.`; + break; + case "empty_block": + mm = `Empty block.`; + break; + case "expected_a": + mm = `Expected '${a}'.`; + break; + case "expected_a_at_b_c": + mm = `Expected '${a}' at column ${b}, not column ${c}.`; + break; + case "expected_a_b": + mm = `Expected '${a}' and instead saw '${b}'.`; + break; + case "expected_a_b_before_c_d": + mm = `Expected ${a} '${b}' to be ordered before ${c} '${d}'.`; + break; + case "expected_a_b_from_c_d": + mm = ( + `Expected '${a}' to match '${b}' from line ${c}` + + ` and instead saw '${d}'.` + ); + break; + case "expected_a_before_b": + mm = `Expected '${a}' before '${b}'.`; + break; + case "expected_digits_after_a": + mm = `Expected digits after '${a}'.`; + break; + case "expected_four_digits": + mm = `Expected four digits after '\\u'.`; + break; + case "expected_identifier_a": + mm = `Expected an identifier and instead saw '${a}'.`; + break; + case "expected_line_break_a_b": + mm = `Expected a line break between '${a}' and '${b}'.`; + break; + case "expected_regexp_factor_a": + mm = `Expected a regexp factor and instead saw '${a}'.`; + break; + case "expected_space_a_b": + mm = `Expected one space between '${a}' and '${b}'.`; + break; + case "expected_statements_a": + mm = `Expected statements before '${a}'.`; + break; + case "expected_string_a": + mm = `Expected a string and instead saw '${a}'.`; + break; + case "expected_type_string_a": + mm = `Expected a type string and instead saw '${a}'.`; + break; + case "freeze_exports": + mm = ( + `Expected 'Object.freeze('. All export values should be frozen.` + ); + break; + +// PR-378 - Relax warning "function_in_loop". +// +// case "function_in_loop": +// mm = `Don't create functions within a loop.`; +// break; + +// PR-390 - Add numeric-separator check. + + case "illegal_num_separator": + mm = `Illegal numeric separator '_' at column ${column}.`; + break; + case "infix_in": + mm = ( + `Unexpected 'in'. Compare with undefined,` + + ` or use the hasOwnProperty method instead.` + ); + break; + case "label_a": + mm = `'${a}' is a statement label.`; + break; + case "misplaced_a": + mm = `Place '${a}' at the outermost level.`; + break; + case "misplaced_directive_a": + mm = `Place the '/*${a}*/' directive before the first statement.`; + break; + case "missing_await_statement": + mm = `Expected await statement in async function.`; + break; + +// PR-347 - Disable warning "missing_browser". +// +// case "missing_browser": +// mm = `/*global*/ requires the Assume a browser option.`; +// break; + + case "missing_m": + mm = `Expected 'm' flag on a multiline regular expression.`; + break; + case "naked_block": + mm = `Naked block.`; + break; + case "nested_comment": + mm = `Nested comment.`; + break; + case "not_label_a": + mm = `'${a}' is not a label.`; + break; + case "number_isNaN": + mm = `Use Number.isNaN function to compare with NaN.`; + break; + case "out_of_scope_a": + mm = `'${a}' is out of scope.`; + break; + case "redefinition_a_b": + mm = `Redefinition of '${a}' from line ${b}.`; + break; + case "redefinition_global_a_b": + mm = `Redefinition of global ${a} variable '${b}'.`; + break; + case "required_a_optional_b": + mm = `Required parameter '${a}' after optional parameter '${b}'.`; + break; + case "reserved_a": + mm = `Reserved name '${a}'.`; + break; + case "subscript_a": + mm = `['${a}'] is better written in dot notation.`; + break; + case "todo_comment": + mm = `Unexpected TODO comment.`; + break; + case "too_long": + mm = `Line is longer than 80 characters.`; + break; + case "too_many_digits": + mm = `Too many digits.`; + break; + case "unclosed_comment": + mm = `Unclosed comment.`; + break; + case "unclosed_disable": + mm = ( + `Directive '/*jslint-disable*/' was not closed` + + ` with '/*jslint-enable*/'.` + ); + break; + case "unclosed_mega": + mm = `Unclosed mega literal.`; + break; + case "unclosed_string": + mm = `Unclosed string.`; + break; + case "undeclared_a": + mm = `Undeclared '${a}'.`; + break; + case "unexpected_a": + mm = `Unexpected '${a}'.`; + break; + case "unexpected_a_after_b": + mm = `Unexpected '${a}' after '${b}'.`; + break; + case "unexpected_a_before_b": + mm = `Unexpected '${a}' before '${b}'.`; + break; + case "unexpected_at_top_level_a": + mm = `Expected '${a}' to be in a function.`; + break; + case "unexpected_char_a": + mm = `Unexpected character '${a}'.`; + break; + case "unexpected_comment": + mm = `Unexpected comment.`; + break; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// case "unexpected_directive_a": +// mm = `When using modules, don't use directive '/\u002a${a}'.`; +// break; + + case "unexpected_expression_a": + mm = `Unexpected expression '${a}' in statement position.`; + break; + case "unexpected_label_a": + mm = `Unexpected label '${a}'.`; + break; + case "unexpected_parens": + mm = `Don't wrap function literals in parens.`; + break; + case "unexpected_space_a_b": + mm = `Unexpected space between '${a}' and '${b}'.`; + break; + case "unexpected_statement_a": + mm = `Unexpected statement '${a}' in expression position.`; + break; + case "unexpected_trailing_space": + mm = `Unexpected trailing space.`; + break; + case "unexpected_typeof_a": + mm = ( + `Unexpected 'typeof'. Use '===' to compare directly with ${a}.` + ); + break; + case "uninitialized_a": + mm = `Uninitialized '${a}'.`; + break; + case "unopened_enable": + mm = ( + `Directive '/*jslint-enable*/' was not opened` + + ` with '/*jslint-disable*/'.` + ); + break; + case "unreachable_a": + mm = `Unreachable '${a}'.`; + break; + case "unregistered_property_a": + mm = `Unregistered property name '${a}'.`; + break; + case "unused_a": + mm = `Unused '${a}'.`; + break; + case "use_double": + mm = `Use double quotes, not single quotes.`; + break; + +// PR-386 - Fix issue #382 - Make fart-related warnings more readable. + + case "use_function_not_fart": + mm = ( + `Use 'function (...)', not '(...) =>' when arrow functions` + + ` become too complex.` + ); + break; + case "use_open": + mm = ( + `Wrap a ternary expression in parens,` + + ` with a line break after the left paren.` + ); + break; + case "use_spaces": + mm = `Use spaces, not tabs.`; + break; + case "var_on_top": + mm = `Move variable declaration to top of function or script.`; + break; + case "var_switch": + mm = `Don't declare variables in a switch.`; + break; + case "weird_condition_a": + mm = `Weird condition '${a}'.`; + break; + case "weird_expression_a": + mm = `Weird expression '${a}'.`; + break; + case "weird_loop": + mm = `Weird loop.`; + break; + case "weird_property_a": + mm = `Weird property name '${a}'.`; + break; + case "weird_relation_a": + mm = `Weird relation '${a}'.`; + break; + case "wrap_condition": + mm = `Wrap the condition in parens.`; + break; + +// PR-386 - Fix issue #382 - Make fart-related warnings more readable. + + case "wrap_fart_parameter": + mm = `Wrap the parameter before '=>' in parens.`; + break; + case "wrap_immediate": + mm = ( + `Wrap an immediate function invocation in parentheses to assist` + + ` the reader in understanding that the expression is the` + + ` result of a function, and not the function itself.` + ); + break; + case "wrap_regexp": + mm = `Wrap this regexp in parens to avoid confusion.`; + break; + case "wrap_unary": + mm = `Wrap the unary expression in parens.`; + break; + } + +// Validate mm. + + jslint_assert(mm, code); + warning.message = mm; + +// PR-242 - Include stack_trace for jslint to debug itself for errors. + + if (option_dict.trace) { + warning.stack_trace = new Error().stack; + } + if (warning.directive_ignore_line) { + +// test_cause: +// ["0 //jslint-ignore-line", "semicolon", "directive_ignore_line", "", 0] + + test_cause("directive_ignore_line"); + return warning; + } + warning_list.push(warning); + return warning; + } + + try { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + + option_dict = Object.assign(empty(), option_dict); + Object.assign(state, { + artifact, + catch_list, + catch_stack, + directive_list, + export_dict, + function_list, + function_stack, + global_dict, + global_list, + import_list, + is_equal, + is_weird, + line_list, + mode_json: false, // true if parsing JSON. + mode_module: false, // true if import or export was used. + mode_property: false, // true if directive /*property*/ is + // ... used. + mode_shebang: false, // true if #! is seen on the first line. + option_dict, + property_dict, + source, + stop, + stop_at, + syntax_dict, + tenure, + test_cause, + token_global, + token_list, + token_nxt: token_global, + warn, + warn_at, + warning_list + }); + +// PHASE 1. Split by newlines into . + + jslint_phase1_split(state); + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 2. Lex into . + + jslint_phase2_lex(state); + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 3. Parse into using the Pratt-parser. + + jslint_phase3_parse(state); + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). + + if (!state.mode_json) { + jslint_phase4_walk(state); + } + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 5. Check whitespace between tokens in . + + if (!state.mode_json && warning_list.length === 0) { + jslint_phase5_whitage(state); + } + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PR-347 - Disable warning "missing_browser". +// +// if (!option_dict.browser) { +// directive_list.forEach(function (comment) { +// if (comment.directive === "global") { +// +// // test_cause: +// // ["/*global aa*/", "jslint", "missing_browser", "(comment)", 1] +// +// warn("missing_browser", comment); +// } +// }); +// } + + if (option_dict.test_internal_error) { + jslint_assert(undefined, "test_internal_error"); + } + } catch (err) { + mode_stop = true; + err.message = "[JSLint was unable to finish] " + err.message; + err.mode_stop = true; + if (err.name !== "JSLintError") { + Object.assign(err, { + column: jslint_fudge, + line: jslint_fudge, + line_source: "", + stack_trace: err.stack + }); + } + if (warning_list.indexOf(err) === -1) { + warning_list.push(err); + } + } + +// Sort warning_list by mode_stop first, line, column respectively. + + warning_list.sort(function (aa, bb) { + return ( + Boolean(bb.mode_stop) - Boolean(aa.mode_stop) + || aa.line - bb.line + || aa.column - bb.column + ); + +// Update each warning with formatted_message ready-for-use by jslint_cli. + + }).map(function ({ + column, + line, + line_source, + message, + stack_trace = "" + }, ii, list) { + list[ii].formatted_message = String( + String(ii + 1).padStart(2, " ") + + ". \u001b[31m" + message + "\u001b[39m" + + " \u001b[90m\/\/ line " + line + ", column " + column + + "\u001b[39m\n" + + (" " + line_source.trim()).slice(0, 72) + "\n" + + stack_trace + ).trimRight(); + }); + + return { + causes: cause_dict, + directives: directive_list, + edition: jslint_edition, + exports: export_dict, + froms: import_list, + functions: function_list, + global: token_global, + id: "(JSLint)", + json: state.mode_json, + lines: line_list, + module: state.mode_module === true, + ok: warning_list.length === 0 && !mode_stop, + option: option_dict, + property: property_dict, + shebang: ( + state.mode_shebang + ? line_list[jslint_fudge].line_source + : undefined + ), + stop: mode_stop, + tokens: token_list, + tree: state.token_tree, + warnings: warning_list + }; +} + +// PR-362 - Add API Doc. + +async function jslint_apidoc({ + example_list, + github_repo, + module_list, + package_name, + pathname, + version +}) { + +// This function will create API Doc from . + + let elem_ii = 0; + let html; + + function elem_create(moduleObj, key, moduleName) { + +// This function will create a sub API Doc from elem []. + + let example = "N/A"; + let id = encodeURIComponent("apidoc.elem." + moduleName + "." + key); + let name; + let signature; + let source; + name = htmlEscape((typeof moduleObj[key]) + " " + key); + if (typeof moduleObj[key] !== "function") { + return { + name, + signature: (` + +${name} + + `), + source: (` +
  • +

    + + ${name} + +

    +
  • + `) + }; + } + // init source + source = htmlEscape(trim_start(moduleObj[key].toString())); + // init signature + source = source.replace(( + /(\([\S\s]*?\)) \{/ + ), function (match0, match1) { + signature = htmlEscape( + match1.replace(( + / *?\/\*[\S\s]*?\*\/ */g + ), "").replace(( + / *?\/\/.*/g + ), "").replace(( + /\n{2,}/g + ), "\n") + ); + return match0; + }); + // init comment + source = source.replace(( + /\n(?:\/\/.*?\n)+\n/ + ), "$&"); + // init example + example_list.some(function (example2) { + example2.replace( + new RegExp(( + "((?:\\n.*?){8}(function )?)\\b" + + key + + "(\\((?:.*?\\n){8})" + ), "g"), + function (ignore, header, isDeclaration, footer) { + if (!isDeclaration) { + example = "..." + trim_start( + htmlEscape(header) + + "" + + htmlEscape(key) + + "" + + htmlEscape(footer) + ).trimEnd() + "\n..."; + } + return ""; + } + ); + return example !== "N/A"; + }); + if (source.length > 2048) { + source = source.slice(0, 2048) + "...\n}\n"; + } + return { + name, + signature: (` + +${name}${signature} + + `), + source: (` +
  • +

    + + ${name}${signature} + +

    +
  • +
  • Description and source-code:
    ${source}
  • +
  • Example usage:
    ${example}
  • + `) + }; + } + + function trim_start(str) { + +// This function will normalize whitespace before . + + let whitespace = ""; + str.trim().replace(( + /^ */gm + ), function (match0) { + if (whitespace === "" || match0.length < whitespace.length) { + whitespace = match0; + } + return ""; + }); + str = str.replace(new RegExp("^" + whitespace, "gm"), ""); + return str; + } + await moduleFsInit(); + +// Html-escape params. + + github_repo = htmlEscape(github_repo); + package_name = htmlEscape(package_name); + version = htmlEscape(version); + +// Init example_list. + + example_list = await Promise.all(example_list.map(async function (file) { + +// This function will read example from given file. + + let result = await moduleFs.promises.readFile(file, "utf8"); + result = ( + "\n\n\n\n\n\n\n\n" + // bug-workaround - truncate example to manageable size + + result.slice(0, 524288) + + "\n\n\n\n\n\n\n\n" + ); + result = result.replace(( + /\r\n*/g + ), "\n"); + return result; + })); + +// Init module_list. + + module_list = await Promise.all(module_list.map(async function ({ + pathname + }) { + let moduleName = htmlEscape(JSON.stringify(pathname)); + let moduleObj = await import(pathname); + if (moduleObj.default) { + moduleObj = moduleObj.default; + } + return { + elem_list: Object.keys(moduleObj).map(function (key) { + return elem_create(moduleObj, key, moduleName); + }).sort(function (aa, bb) { + return ( + aa.name < bb.name + ? -1 + : 1 + ); + }).map(function (elem) { + elem_ii += 1; + elem.signature = elem.signature.replace( + ">", + ">" + elem_ii + ". " + ); + elem.source = elem.source.replace( + "\">", + "\">" + elem_ii + ". " + ); + return elem; + }), + id: encodeURIComponent("apidoc.module." + moduleName), + moduleName + }; + })); + html = (` + + + + + + +${package_name} apidoc + + + +
    +

    API Doc for ${package_name} (${version})

    +
    + +

    Table of Contents

    +
    +
      + `) + module_list.map(function ({ + elem_list, + id, + moduleName + }) { + return ( + (` +
    • + Module ${moduleName} +
        + `) + + elem_list.map(function ({ + signature + }) { + return "
      • \n" + signature + "\n
      • \n"; + }).join("") + + (` +
      +
    • + `) + ); + }).join("") + (` +
    +
    + `) + module_list.map(function ({ + elem_list, + id, + moduleName + }) { + return ( + (` +
    +

    Module ${moduleName}

    +
      + `) + + elem_list.map(function ({ + source + }) { + return source; + }).join("") + + (` +
    +
    + `) + ); + }).join("") + (` +
    + [ + This document was created with + JSLint + ] +
    +
    + + + `); + html = html.trim().replace(( + / +?$/gm + ), "") + "\n"; + await fsWriteFileWithParents(pathname, html); +} + +function jslint_assert(condition, message) { + +// This function will throw if is falsy. + + if (condition) { + return condition; + } + throw new Error( + `This was caused by a bug in JSLint. +Please open an issue with this stack-trace (and possible example-code) at +https://github.com/jslint-org/jslint/issues. +edition = "${jslint_edition}"; +${String(message).slice(0, 2000)}` + ); +} + +async function jslint_cli({ + console_error, + console_log, + file, + import_meta_url, + mode_cli, + mode_noop, + option, + process_argv, + process_env, + process_exit, + source +}) { + +// This function will run jslint from nodejs-cli. + + let command; + let data; + let exit_code = 0; + let mode_report; + let mode_wrapper_vim; + let result; + + function jslint_from_file({ + code, + file, + line_offset = 0, + mode_conditional, + option = empty() + }) { + let result_from_file; + if ( + mode_conditional + && !( + /^\/\*jslint\b/m + ).test(code.slice(0, 65536)) + ) { + return; + } + option = Object.assign(empty(), option, { + file + }); + switch (( + /\.\w+?$|$/m + ).exec(file)[0]) { + case ".html": + +// Recursively jslint embedded "". + + code.replace(( + /^]*?>\n([\S\s]*?\n)<\/script>$/gm + ), function (ignore, match1, ii) { + jslint_from_file({ + code: match1, + file: file + ". + + import_meta_url = import_meta_url || jslint_import_meta_url; + if ( + jslint_rgx_url_search_window_jslint.test(import_meta_url) + && (typeof globalThis === "object" && globalThis) + ) { + globalThis.jslint = jslint; + } + +// Feature-detect nodejs. + + if (!( + (typeof process === "object" && process) + && process.versions + && typeof process.versions.node === "string" + && !mode_noop + )) { + return exit_code; + } + console_error = console_error || console.error; + console_log = console_log || console.log; + process_argv = process_argv || process.argv; + process_env = process_env || process.env; + process_exit = process_exit || process.exit; + await moduleFsInit(); + if ( + !( + +// Feature-detect nodejs-cli. + + process.execArgv.indexOf("--eval") === -1 + && process.execArgv.indexOf("-e") === -1 + && ( + ( + /[\/|\\]jslint(?:\.[cm]?js)?$/m + ).test(process_argv[1]) + || mode_cli + ) + && ( + moduleUrl.fileURLToPath(import_meta_url) + === + modulePath.resolve(process_argv[1]) + ) + ) + && !mode_cli + ) { + return exit_code; + } + +// init commmand + + command = String(process_argv[2]).split("="); + command[1] = command.slice(1).join("="); + + switch (command[0]) { + +// PR-362 - Add API Doc. + + case "jslint_apidoc": + await jslint_apidoc(Object.assign(JSON.parse(process_argv[3]), { + pathname: command[1] + })); + return; + +// PR-363 - Add command jslint_report. + + case "jslint_report": + mode_report = command[1]; + process_argv = process_argv.slice(1); + break; + +// COMMIT-b26d6df2 - Add command jslint_wrapper_vim. + + case "jslint_wrapper_vim": + mode_wrapper_vim = true; + process_argv = process_argv.slice(1); + break; + +// PR-364 - Add command v8_coverage_report. + + case "v8_coverage_report": + await v8CoverageReportCreate({ + consoleError: console_error, + coverageDir: command[1], + processArgv: process_argv.slice(3) + }); + return; + } + +// Normalize file relative to process.cwd(). + + process_argv.slice(2).some(function (arg) { + if (!arg.startsWith("-")) { + file = file || arg; + return true; + } + }); + if (!file) { + return; + } + file = modulePath.resolve(file) + "/"; + if (file.startsWith(process.cwd() + "/")) { + file = file.replace(process.cwd() + "/", "").slice(0, -1) || "."; + } + file = file.replace(( + /\\/g + ), "/").replace(( + /\/$/g + ), ""); + if (source) { + data = source; + } else { + +// jslint_cli - jslint directory. + + try { + data = await moduleFs.promises.readdir(file, "utf8"); + } catch (ignore) {} + if (data) { + await Promise.all(data.map(async function (file2) { + let code; + let time_start = Date.now(); + file2 = file + "/" + file2; + switch (( + /\.\w+?$|$/m + ).exec(file2)[0]) { + case ".cjs": + case ".html": + case ".js": + case ".json": + case ".md": + case ".mjs": + case ".sh": + break; + default: + return; + } + try { + code = await moduleFs.promises.readFile(file2, "utf8"); + } catch (ignore) { + return; + } + if ( + ( + /(?:\b|_)(?:lock|min|raw|rollup)(?:\b|_)/ + ).test(file2) + || !(code && code.length < 1048576) + ) { + return; + } + jslint_from_file({ + code, + file: file2, + option + }); + console_error( + "jslint - " + (Date.now() - time_start) + "ms - " + file2 + ); + })); + process_exit(exit_code); + return exit_code; + } + +// jslint_cli - jslint file. + + try { + data = await moduleFs.promises.readFile(file, "utf8"); + } catch (err) { + console_error(err); + exit_code = 1; + process_exit(exit_code); + return exit_code; + } + } + result = jslint_from_file({ + code: data, + file, + option + }); + if (mode_report) { + result = jslint.jslint_report(result); + result = `\n${result}\n`; + await fsWriteFileWithParents(mode_report, result); + } + process_exit(exit_code); + return exit_code; +} + +function jslint_phase1_split() { + +// PHASE 1. Split by newlines into . + + return; +} + +function jslint_phase2_lex(state) { + +// PHASE 2. Lex into . + + let { + artifact, + directive_list, + global_dict, + global_list, + line_list, + option_dict, + stop, + stop_at, + tenure, + test_cause, + token_global, + token_list, + warn, + warn_at + } = state; + let char; // The current character being lexed. + let column = 0; // The column number of the next character. + let from; // The starting column number of the token. + let from_mega; // The starting column of megastring. + let line = 0; // The line number of the next character. + let line_disable; // The starting line of "/*jslint-disable*/". + let line_mega; // The starting line of megastring. + let line_source = ""; // The remaining line source string. + let line_whole = ""; // The whole line source string. + let mode_digits_empty_string = 1; + let mode_digits_numeric_separator = 2; + let mode_directive = true; // true if directives are still allowed. + let mode_mega = false; // true if currently parsing a megastring + // ... literal. + let mode_regexp; // true if regular expression literal seen on + // ... this line. + let paren_backtrack_list = []; // List of most recent "(" tokens at any + // ... paren-depth. + let paren_depth = 0; // Keeps track of current paren-depth. + let snippet = ""; // A piece of string. + let token_1; // The first token. + let token_prv = token_global; // The previous token including + // ... comments. + let token_prv_expr = token_global; // The previous token excluding + // ... comments. + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions char_after, char_before, +// read_digits, and char_after_escape help in the parsing of literals. + + function char_after(match) { + +// Get the next character from the source line. Remove it from the line_source, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + +// test_cause: +// ["aa=/[", "char_after", "expected_a", "]", 5] +// ["aa=/aa{/", "char_after", "expected_a_b", "/", 8] + + return ( + char === "" + ? stop_at("expected_a", line, column - 1, match) + : stop_at("expected_a_b", line, column, match, char) + ); + } + char = line_source.slice(0, 1); + line_source = line_source.slice(1); + snippet += char || " "; + column += 1; + return char; + } + + function char_after_escape(extra) { + +// Validate char after escape "\\". + + char_after("\\"); + switch (char) { + case "": + +// test_cause: +// ["\"\\", "char_after_escape", "unclosed_string", "", 2] + + return stop_at("unclosed_string", line, column); + case "/": + return char_after(); + case "\\": + return char_after(); + case "`": + return char_after(); + case "b": + return char_after(); + case "f": + return char_after(); + case "n": + return char_after(); + case "r": + return char_after(); + case "t": + +// test_cause: +// ["\"\\/\\\\\\`\\b\\f\\n\\r\\t\"", "char_after_escape", "char_after", "", 0] + + test_cause("char_after"); + return char_after(); + case "u": + if (char_after("u") === "{") { + if (state.mode_json) { + +// test_cause: +// ["[\"\\u{12345}\"]", "char_after_escape", "unexpected_a", "{", 5] + + warn_at("unexpected_a", line, column, char); + } + if (read_digits("x", undefined) > 5) { + +// test_cause: +// ["\"\\u{123456}\"", "char_after_escape", "too_many_digits", "", 11] + + warn_at("too_many_digits", line, column); + } + if (char !== "}") { + +// test_cause: +// ["\"\\u{12345\"", "char_after_escape", "expected_a_before_b", "\"", 10] + + stop_at("expected_a_before_b", line, column, "}", char); + } + return char_after(); + } + char_before(); + if (read_digits("x", mode_digits_empty_string) < 4) { + +// test_cause: +// ["\"\\u0\"", "char_after_escape", "expected_four_digits", "", 5] + + warn_at("expected_four_digits", line, column); + } + return; + default: + if (extra && extra.indexOf(char) >= 0) { + return char_after(); + } + +// test_cause: +// ["\"\\0\"", "char_after_escape", "unexpected_a_before_b", "0", 3] + + warn_at("unexpected_a_before_b", line, column, "\\", char); + } + } + + function char_before() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the line_source. + + char = snippet.slice(-1); + line_source = char + line_source; + column -= char.length; + +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + return char; + } + + function check_numeric_separator(digits, column) { + +// This function will check for illegal numeric-separator in . + + digits.replace(( + jslint_rgx_numeric_separator_illegal + ), function (ignore, ii) { + +// test_cause: +// ["0x0_0_;", "check_numeric_separator", "illegal_num_separator", "", 6] +// ["0x0_0__0;", "check_numeric_separator", "illegal_num_separator", "", 6] +// ["aa=1_2_;", "check_numeric_separator", "illegal_num_separator", "", 7] +// ["aa=1_2__3;", "check_numeric_separator", "illegal_num_separator", "", 7] +// ["aa=1_2_n;", "check_numeric_separator", "illegal_num_separator", "", 7] + + warn_at("illegal_num_separator", line, column + ii + 1); + return ""; + }); + } + + function lex_comment() { + let body; + let ii = 0; + let jj = 0; + let the_comment; + +// Create a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + +// Create token from comment //.... + + if (snippet === "//") { + snippet = line_source; + line_source = ""; + the_comment = token_create("(comment)", snippet); + if (mode_mega) { + +// test_cause: +// ["`${//}`", "lex_comment", "unexpected_comment", "`", 4] + + warn("unexpected_comment", the_comment, "`"); + } + +// Create token from comment /*...*/. + + } else { + snippet = []; + if (line_source[0] === "/") { + +// test_cause: +// ["/*/", "lex_comment", "unexpected_a", "/", 2] + + warn_at("unexpected_a", line, column + ii, "/"); + } + +// Lex/loop through each line until "*/". + + while (true) { + // jslint_rgx_star_slash + ii = line_source.indexOf("*/"); + if (ii >= 0) { + break; + } + // jslint_rgx_slash_star + ii = line_source.indexOf("/*"); + if (ii >= 0) { + +// test_cause: +// ["/*/*", "lex_comment", "nested_comment", "", 2] + + warn_at("nested_comment", line, column + ii); + } + snippet.push(line_source); + line_source = read_line(); + if (line_source === undefined) { + +// test_cause: +// ["/*", "lex_comment", "unclosed_comment", "", 1] + + return stop_at("unclosed_comment", line, column); + } + } + jj = line_source.slice(0, ii).search( + jslint_rgx_slash_star_or_slash + ); + if (jj >= 0) { + +// test_cause: +// ["/*/**/", "lex_comment", "nested_comment", "", 2] + + warn_at("nested_comment", line, column + jj); + } + snippet.push(line_source.slice(0, ii)); + snippet = snippet.join(" "); + column += ii + 2; + line_source = line_source.slice(ii + 2); + the_comment = token_create("(comment)", snippet); + } + +// Uncompleted work comment. + + if (!option_dict.devel && jslint_rgx_todo.test(snippet)) { + +// test_cause: +// ["//todo", "lex_comment", "todo_comment", "(comment)", 1] //jslint-ignore-line + + warn("todo_comment", the_comment); + } + +// Lex directives in comment. + + [ + the_comment.directive, body + ] = Array.from(snippet.match(jslint_rgx_directive) || []).slice(1); + if (the_comment.directive === undefined) { + return the_comment; + } + directive_list.push(the_comment); + if (!mode_directive) { + +// test_cause: +// ["0\n/*global aa*/", "lex_comment", "misplaced_directive_a", "global", 1] + + warn_at("misplaced_directive_a", line, from, the_comment.directive); + return the_comment; + } + +// lex_directive(); +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + +// Lex/loop through each directive in /*...*/ + + ii = 0; + body.replace(jslint_rgx_directive_part, function ( + match0, + key, + val, + jj + ) { + if (ii !== jj) { + +// test_cause: +// ["/*jslint !*/", "lex_comment", "bad_directive_a", "!", 1] + + return stop("bad_directive_a", the_comment, body.slice(ii)); + } + if (match0 === "") { + return ""; + } + ii += match0.length; + switch (the_comment.directive) { + case "global": + if (val) { + +// test_cause: +// ["/*global aa:false*/", "lex_comment", "bad_option_a", "aa:false", 1] + + warn("bad_option_a", the_comment, key + ":" + val); + } + global_dict[key] = "user-defined"; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// state.mode_module = the_comment; + + break; + case "jslint": + if (!option_set_item(key, val !== "false")) { + +// test_cause: +// ["/*jslint undefined*/", "lex_comment", "bad_option_a", "undefined", 1] + + warn("bad_option_a", the_comment, key); + } + break; + case "property": + state.mode_property = true; + tenure[key] = true; + break; + } + return ""; + }); + return the_comment; + } + + function lex_megastring() { + let id; + let match; + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (mode_mega) { + +// test_cause: +// ["`${`", "lex_megastring", "expected_a_b", "`", 4] + + return stop_at("expected_a_b", line, column, "}", "`"); + } + from_mega = from; + line_mega = line; + mode_mega = true; + snippet = ""; + +// Parsing a mega literal is tricky. First create a ` token. + + token_create("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + while (true) { + match = line_source.match(jslint_rgx_mega) || { + "0": "", + index: 0 + }; + snippet += line_source.slice(0, match.index); + column += match.index; + line_source = line_source.slice(match.index); + match = match[0]; + switch (match) { + case "${": + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + token_create("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then create tokens that will become part of an expression until +// a } token is made. + + column += 2; + token_create("${"); + line_source = line_source.slice(2); + +// Lex/loop through each token inside megastring-expression `${...}`. + + while (true) { + id = lex_token().id; + if (id === "{") { + +// test_cause: +// ["`${{", "lex_megastring", "expected_a_b", "{", 4] + + return stop_at("expected_a_b", line, column, "}", "{"); + } + if (id === "}") { + break; + } + } + break; + case "\\": + snippet += line_source.slice(0, 2); + line_source = line_source.slice(2); + column += 2; + break; + case "`": + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + token_create("(string)", snippet).quote = "`"; + snippet = ""; + +// Terminate megastring with `. + + line_source = line_source.slice(1); + column += 1; + mode_mega = false; + return token_create("`"); + default: + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + snippet += line_source + "\n"; + if (read_line() === undefined) { + +// test_cause: +// ["`", "lex_megastring", "unclosed_mega", "", 1] + + return stop_at("unclosed_mega", line_mega, from_mega); + } + } + } + } + + function lex_number() { + let prefix = snippet; + +// PR-390 - Add numeric-separator check. + + check_numeric_separator(prefix, column - prefix.length); + char_after(); + switch (prefix === "0" && char) { + case "b": + case "o": + case "x": + read_digits(char, mode_digits_numeric_separator); + +// PR-351 - Ignore BigInt suffix 'n'. + + if (char === "n") { + char_after("n"); + } + break; + default: + if (char === ".") { + read_digits("d", mode_digits_numeric_separator); + } + if (char === "E" || char === "e") { + char_after(char); + if (char !== "+" && char !== "-") { + char_before(); + } + read_digits("d", mode_digits_numeric_separator); + } + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + +// test_cause: +// ["0a", "lex_number", "unexpected_a_after_b", "0", 2] + + return stop_at( + "unexpected_a_after_b", + line, + column, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + char_before(); + return token_create("(number)", snippet); + } + + function lex_regexp() { + +// Regexp +// Lex a regular expression literal. + + let flag; + let mode_regexp_multiline; + let result; + let value; + mode_regexp = true; + + function lex_regexp_bracketed() { + let mode_regexp_range; + +// RegExp +// Match a class. + + char_after("["); + if (char === "^") { + char_after("^"); + } + while (true) { + +// RegExp +// Match a character in a character class. + + switch (char) { + case "": + case "]": + +// test_cause: +// ["aa=/[", "lex_regexp_bracketed", "closer", "", 0] +// ["aa=/[]/", "lex_regexp_bracketed", "closer", "", 0] + + test_cause("closer"); + if (mode_regexp_range) { + +// test_cause: +// ["aa=/[0-]/", "lex_regexp_bracketed", "unexpected_a", "-", 7] + + warn_at("unexpected_a", line, column - 1, "-"); + } + return char_after("]"); + +// PR-362 - Relax regexp-warning against using . +// +// case " ": +// +// // test_cause: +// // ["aa=/[ ]/", "lex_regexp_bracketed", "expected_a_b", " ", 6] +// +// warn_at("expected_a_b", line, column, "\\u0020", " "); +// break; + + case "-": + case "/": + case "[": + case "^": + +// test_cause: +// ["aa=/[-]/", "lex_regexp_bracketed", "expected_a_before_b", "-", 6] +// ["aa=/[.^]/", "lex_regexp_bracketed", "expected_a_before_b", "^", 7] +// ["aa=/[/", "lex_regexp_bracketed", "expected_a_before_b", "/", 6] +// ["aa=/[\\\\/]/", "lex_regexp_bracketed", "expected_a_before_b", "/", 8] +// ["aa=/[\\\\[]/", "lex_regexp_bracketed", "expected_a_before_b", "[", 8] + + warn_at("expected_a_before_b", line, column, "\\", char); + break; + case "\\": + char_after_escape("BbDdSsWw-[]^"); + char_before(); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${/[`]/}`", "lex_regexp_bracketed", "unexpected_a", "`", 6] + + warn_at("unexpected_a", line, column, "`"); + } + break; + } + char_after(); + mode_regexp_range = false; + if (char === "-") { + +// RegExp +// Match a range of subclasses. + + mode_regexp_range = true; + char_after("-"); + } + } + } + + function lex_regexp_group() { + +// RegExp +// Lex sequence of characters in regexp. + + switch (char) { + case "": + warn_at("expected_regexp_factor_a", line, column, char); + break; + case ")": + warn_at("expected_regexp_factor_a", line, column, char); + break; + case "]": + +// test_cause: +// ["/ /", "lex_regexp_group", "expected_regexp_factor_a", "", 3] +// ["aa=/)", "lex_regexp_group", "expected_regexp_factor_a", ")", 5] +// ["aa=/]", "lex_regexp_group", "expected_regexp_factor_a", "]", 5] + + warn_at("expected_regexp_factor_a", line, column, char); + break; + } + while (true) { + switch (char) { + case "": + case ")": + case "/": + case "]": + return; + +// PR-362 - Relax regexp-warning against using . +// +// case " ": +// +// // test_cause: +// // ["aa=/ /", "lex_regexp_group", "expected_a_b", " ", 5] +// +// warn_at("expected_a_b", line, column, "\\s", " "); +// char_after(); +// break; + + case "$": + if (line_source[0] !== "/") { + mode_regexp_multiline = true; + } + char_after(); + break; + case "(": + +// RegExp +// Match a group that starts with left paren. + + char_after("("); + switch (char) { + case ":": + +// test_cause: +// ["aa=/(:)/", "lex_regexp_group", "expected_a_before_b", ":", 6] +// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5] + + warn_at("expected_a_before_b", line, column, "?", ":"); + break; + case "?": + char_after("?"); + switch (char) { + case "!": + +// PR-437 - Add grammar for regexp-named-capture-group. + + case "<": + case "=": + char_after(); + break; + default: + char_after(":"); + } + break; + } + +// RegExp +// Recurse lex_regexp_group(). + + lex_regexp_group(); + char_after(")"); + break; + case "*": + case "+": + case "?": + case "{": + case "}": + +// test_cause: +// ["aa=/+/", "lex_regexp_group", "expected_a_before_b", "+", 5] +// ["aa=/.**/", "lex_regexp_group", "expected_a_before_b", "*", 7] +// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5] +// ["aa=/{/", "lex_regexp_group", "expected_a_before_b", "{", 5] +// ["aa=/}/", "lex_regexp_group", "expected_a_before_b", "}", 5] + + warn_at("expected_a_before_b", line, column, "\\", char); + char_after(); + break; + case "[": + lex_regexp_bracketed(); + break; + case "\\": + +// test_cause: +// ["aa=/\\/", "lex_regexp_group", "escape", "", 0] + + test_cause("escape"); + +// PR-437 - Add grammar for regexp-named-backreference. + + char_after_escape("BbDdSsWw^${}[]():=!.|*+?k"); + break; + case "^": + if (snippet !== "^") { + mode_regexp_multiline = true; + } + char_after(); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${/`/}`", "lex_regexp_group", "unexpected_a", "`", 5] + + warn_at("unexpected_a", line, column, "`"); + } + char_after(); + break; + default: + char_after(); + } + +// RegExp +// Match an optional quantifier. + + switch (char) { + case "*": + case "+": + if (char_after(char) === "?") { + +// test_cause: +// ["aa=/.*?/", "lex_regexp_group", "?", "", 0] +// ["aa=/.+?/", "lex_regexp_group", "?", "", 0] + + test_cause("?"); + char_after("?"); + } + break; + case "?": + if (char_after("?") === "?") { + +// test_cause: +// ["aa=/.??/", "lex_regexp_group", "unexpected_a", "?", 7] + + warn_at("unexpected_a", line, column, char); + char_after("?"); + } + break; + case "{": + if (read_digits("d", mode_digits_empty_string) === 0) { + +// test_cause: +// ["aa=/aa{/", "lex_regexp_group", "expected_a_before_b", ",", 8] + + warn_at("expected_a_before_b", line, column, "0", ","); + } + if (char === ",") { + +// test_cause: +// ["aa=/.{,/", "lex_regexp_group", "comma", "", 0] + + test_cause("comma"); + read_digits("d", mode_digits_empty_string); + } + if (char_after("}") === "?") { + +// test_cause: +// ["aa=/.{0}?/", "lex_regexp_group", "unexpected_a", "?", 9] + + warn_at("unexpected_a", line, column, char); + char_after("?"); + } + break; + } + } + } + +// RegExp +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + char_after(); + if (char === "=") { + +// test_cause: +// ["aa=/=/", "lex_regexp", "expected_a_before_b", "=", 5] + + warn_at("expected_a_before_b", line, column, "\\", "="); + } + lex_regexp_group(); + +// RegExp +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + +// RegExp +// Make sure there is a closing slash. + + value = snippet; + char_after("/"); + +// RegExp +// Create flag. + + flag = empty(); + while ( + +// Regexp +// char is a letter. + + (char >= "a" && char <= "z\uffff") + || (char >= "A" && char <= "Z\uffff") + ) { + +// RegExp +// Process dangling flag letters. + + switch (!flag[char] && char) { + case "g": + break; + case "i": + break; + case "m": + break; + case "u": + break; + case "y": + +// test_cause: +// ["aa=/./gimuy", "lex_regexp", "flag", "", 0] + + test_cause("flag"); + break; + default: + +// test_cause: +// ["aa=/./gg", "lex_regexp", "unexpected_a", "g", 8] +// ["aa=/./z", "lex_regexp", "unexpected_a", "z", 7] + + warn_at("unexpected_a", line, column, char); + } + flag[char] = true; + char_after(); + } + char_before(); + if (char === "/" || char === "*") { + +// test_cause: +// ["aa=/.//", "lex_regexp", "unexpected_a", "/", 3] + + return stop_at("unexpected_a", line, from, char); + } + result = token_create("(regexp)", char); + result.flag = flag; + result.value = value; + if (mode_regexp_multiline && !flag.m) { + +// test_cause: +// ["aa=/$^/", "lex_regexp", "missing_m", "", 7] + + warn_at("missing_m", line, column); + } + return result; + } + + function lex_slash_or_regexp() { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + let the_token; + switch ( + token_prv_expr.identifier + && !token_prv_expr.dot + && token_prv_expr.id + ) { + case "case": + case "delete": + case "in": + case "instanceof": + case "new": + case "typeof": + case "void": + case "yield": + the_token = lex_regexp(); + +// test_cause: +// ["case /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6] +// ["delete /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8] +// ["in /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 4] +// ["instanceof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 12] +// ["new /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 5] +// ["typeof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8] +// ["void /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6] +// ["yield /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 7] + + return stop("unexpected_a", the_token); + case "return": + return lex_regexp(); + } + switch (!token_prv_expr.identifier && token_prv_expr.id.slice(-1)) { + case "!": + case "%": + case "&": + case "*": + case "+": + case "-": + case "/": + case ";": + case "<": + case ">": + case "^": + case "{": + case "|": + case "}": + case "~": + the_token = lex_regexp(); + +// test_cause: +// ["!/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["%/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["&/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["+/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["-/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["0 * /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5] +// ["0 / /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5] +// [";/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["^/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["{/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["|/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["}/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["~/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] + + warn("wrap_regexp", the_token); + return the_token; + case "(": + case ",": + case ":": + case "=": + case "?": + case "[": + +// test_cause: +// ["(/./", "lex_slash_or_regexp", "recurse", "", 0] +// [",/./", "lex_slash_or_regexp", "recurse", "", 0] +// [":/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["=/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["?/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["aa[/./", "lex_slash_or_regexp", "recurse", "", 0] + + test_cause("recurse"); + return lex_regexp(); + } + if (line_source[0] === "=") { + column += 1; + line_source = line_source.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + return token_create(snippet); + } + + function lex_string(quote) { + +// Create a string token. + + let the_token; + if (!option_dict.single && quote === "'") { + +// test_cause: +// ["''", "lex_string", "use_double", "", 1] + + warn_at("use_double", line, column); + } + snippet = ""; + char_after(); + +// Lex/loop through each character in "...". + + while (true) { + switch (char) { + case "": + +// test_cause: +// ["\"", "lex_string", "unclosed_string", "", 1] + + return stop_at("unclosed_string", line, column); + case "\\": + char_after_escape(quote); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${\"`\"}`", "lex_string", "unexpected_a", "`", 5] + + warn_at("unexpected_a", line, column, "`"); + } + char_after("`"); + break; + case quote: + +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + the_token = token_create("(string)", snippet); + the_token.quote = quote; + return the_token; + default: + char_after(); + } + } + } + + function lex_token() { + let match; + +// Lex/loop through each whitespace. + + while (true) { + +// Lex/loop through each blank-line. + + while (!line_source) { + line_source = read_line(); + from = 0; + if (line_source === undefined) { + return ( + mode_mega + +// test_cause: +// ["`${//}`", "lex_token", "unclosed_mega", "", 1] + + ? stop_at("unclosed_mega", line_mega, from_mega) + : line_disable !== undefined + +// test_cause: +// ["/*jslint-disable*/", "lex_token", "unclosed_disable", "", 1] + + ? stop_at("unclosed_disable", line_disable) + : token_create("(end)") + ); + } + } + from = column; + match = line_source.match(jslint_rgx_token); + +// match[1] token +// match[2] whitespace +// match[3] identifier +// match[4] number +// match[5] rest + + if (!match) { + +// test_cause: +// ["#", "lex_token", "unexpected_char_a", "#", 1] + + return stop_at( + "unexpected_char_a", + line, + column, + line_source[0] + ); + } + snippet = match[1]; + column += snippet.length; + line_source = match[5]; + if (!match[2]) { + break; + } + } + +// The token is an identifier. + + if (match[3]) { + return token_create(snippet, undefined, true); + } + +// Create token from number. + + if (match[4]) { + return lex_number(); + } + +// Create token from string "..." or '...'. + + if (snippet === "\"" || snippet === "'") { + return lex_string(snippet); + } + +// Create token from megastring `...`. + + if (snippet === "`") { + return lex_megastring(); + } + +// Create token from comment /*...*/ or //.... + + if (snippet === "/*" || snippet === "//") { + return lex_comment(); + } + +// Create token from slash /. + + if (snippet === "/") { + return lex_slash_or_regexp(); + } + return token_create(snippet); + } + + function option_set_item(key, val) { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + switch (key) { + case "beta": // Enable experimental warnings. + case "bitwise": // Allow bitwise operator. + case "browser": // Assume browser environment. + case "convert": // Allow conversion operator. + case "couch": // Assume CouchDb environment. + case "devel": // Allow console.log() and friends. + case "ecma": // Assume ECMAScript environment. + case "eval": // Allow eval(). + case "fart": // Allow complex fat-arrow. + case "for": // Allow for-statement. + case "getset": // Allow get() and set(). + case "indent2": // Use 2-space indent. + case "long": // Allow long lines. + case "node": // Assume Node.js environment. + case "nomen": // Allow weird property name. + case "single": // Allow single-quote strings. + case "subscript": // Allow identifier in subscript-notation. + case "test_cause": // Test jslint's causes. + case "test_internal_error": // Test jslint's internal-error + // ... handling-ability. + case "this": // Allow 'this'. + case "trace": // Include jslint stack-trace in warnings. + case "unordered": // Allow unordered cases, params, properties, + // ... variables, and exports. + case "variable": // Allow unordered const and let declarations + // ... that are not at top of function-scope. + case "white": // Allow messy whitespace. + option_dict[key] = val; + break; + +// PR-404 - Alias "evil" to jslint-directive "eval" for backwards-compat. + + case "evil": + return option_set_item("eval", val); + +// PR-404 - Alias "nomen" to jslint-directive "name" for backwards-compat. + + case "name": + return option_set_item("nomen", val); + default: + return false; + } + +// Initialize global-variables. + + switch (val && key) { + +// Assign global browser variables to global_dict. +/* +// /\*jslint beta, browser, devel*\/ +console.log(JSON.stringify(Object.keys(window).sort(), undefined, 4)); +*/ + + case "browser": + object_assign_from_list(global_dict, [ + +// Shared with Node.js. + + "AbortController", + // "Buffer", + // "Crypto", + // "CryptoKey", + "Event", + "EventTarget", + "MessageChannel", + "MessageEvent", + "MessagePort", + // "Request", + // "Response", + // "SubtleCrypto", + "TextDecoder", + "TextEncoder", + "URL", + "URLSearchParams", + "WebAssembly", + // "__dirname", + // "__filename", + // "atob", + // "btoa", + // "clearImmediate", + "clearInterval", + "clearTimeout", + // "console", + // "crypto", + // "exports", + // "fetch", + // "global", + // "module", + "performance", + // "process", + "queueMicrotask", + // "require", + // "setImmediate", + "setInterval", + "setTimeout", + +// Web worker only. +// https://github.com/mdn/content/blob/main/files/en-us/web/api +// /workerglobalscope/index.md + + "importScripts", + +// Window. + + "Blob", + // "CharacterData", + // "DocumentType", + // "Element", + // "Event", + "FileReader", + // "FontFace", + "FormData", + "IntersectionObserver", + "MutationObserver", + // "Storage", + // "TextDecoder", + // "TextEncoder", + // "URL", + "Worker", + "XMLHttpRequest", + // "caches", + // "clearInterval", + // "clearTimeout", + "document", + // "event", + "fetch", + // "history", + "indexedDb", + "localStorage", + "location", + // "name", + "navigator", + "postMessage", + // "screen", + "sessionStorage", + // "setInterval", + // "setTimeout", + "structuredClone", + "window" + ], "browser"); + break; + +// https://docs.couchdb.org/en/stable/query-server/javascript.html#javascript + + case "couch": + object_assign_from_list(global_dict, [ + "emit", + "getRow", + "isArray", + "log", + "provides", + "registerType", + "require", + "send", + "start", + "sum", + "toJSON" + ], "CouchDb"); + break; + case "devel": + object_assign_from_list(global_dict, [ + "alert", "confirm", "console", "prompt" + ], "development"); + break; + +// These are the globals that are provided by the language standard. +// Assign global ECMAScript variables to global_dict. +/* +node --input-type=module --eval ' +// /\*jslint beta, node*\/ +import https from "https"; +(async function () { + let dict = {import: true}; + let result = ""; + await new Promise(function (resolve) { + https.get(( + "https://raw.githubusercontent.com/mdn/content/main/files" + + "/en-us/web/javascript/reference/global_objects/index.md" + ), function (res) { + res.on("data", function (chunk) { + result += chunk; + }).on("end", resolve).setEncoding("utf8"); + }); + }); + result.replace(( + /\n- \{\{JSxRef\("(?:Global_Objects\/)?([^"\/]+?)"/g + ), function (ignore, key) { + if (globalThis.hasOwnProperty(key)) { + dict[key] = true; + } + return ""; + }); + console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4)); +}()); +' +*/ + + case "ecma": + object_assign_from_list(global_dict, [ + "AggregateError", + "Array", + "ArrayBuffer", + "Atomics", + "BigInt", + "BigInt64Array", + "BigUint64Array", + "Boolean", + "DataView", + "Date", + "Error", + "EvalError", + "Float32Array", + "Float64Array", + "Function", + "Infinity", + "Int16Array", + "Int32Array", + "Int8Array", + "Intl", + "JSON", + "Map", + "Math", + "NaN", + "Number", + "Object", + "Promise", + "Proxy", + "RangeError", + "ReferenceError", + "Reflect", + "RegExp", + "Set", + "SharedArrayBuffer", + "String", + "Symbol", + "SyntaxError", + "TypeError", + "URIError", + "Uint16Array", + "Uint32Array", + "Uint8Array", + "Uint8ClampedArray", + "WeakMap", + "WeakSet", + "WebAssembly", + "decodeURI", + "decodeURIComponent", + "encodeURI", + "encodeURIComponent", + "eval", + "globalThis", + "import", + "isFinite", + "isNaN", + "parseFloat", + "parseInt", + "undefined" + ], "ECMAScript"); + break; + +// Assign global Node.js variables to global_dict. +/* +node --input-type=module --eval ' +// /\*jslint beta, node*\/ +import moduleHttps from "https"; +(async function () { + let dict = Object.create(null); + let result = ""; + await new Promise(function (resolve) { + moduleHttps.get(( + "https://raw.githubusercontent.com/nodejs/node/v16.x/doc/api" + + "/globals.md" + ), function (res) { + res.on("data", function (chunk) { + result += chunk; + }).on("end", resolve).setEncoding("utf8"); + }); + }); + result.replace(( + /\n(?:\* \[`|## |## Class: )`\w+/g + ), function (match0) { + dict[match0.split("`")[1]] = true; + return ""; + }); + console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4)); +}()); +' +*/ + + case "node": + object_assign_from_list(global_dict, [ + "AbortController", + "Buffer", + // "Crypto", + // "CryptoKey", + "Event", + "EventTarget", + "MessageChannel", + "MessageEvent", + "MessagePort", + // "Request", + // "Response", + // "SubtleCrypto", + "TextDecoder", + "TextEncoder", + "URL", + "URLSearchParams", + "WebAssembly", + "__dirname", + "__filename", + // "atob", + // "btoa", + "clearImmediate", + "clearInterval", + "clearTimeout", + "console", + // "crypto", + "exports", + // "fetch", + "global", + "module", + "performance", + "process", + "queueMicrotask", + "require", + "setImmediate", + "setInterval", + "setTimeout" + ], "Node.js"); + break; + } + return true; + } + + function read_digits(base, mode) { + let digits = line_source.match( + base === "b" + ? jslint_rgx_digits_bits + : base === "o" + ? jslint_rgx_digits_octals + : base === "x" + ? jslint_rgx_digits_hexs + : jslint_rgx_digits_decimals + )[0]; + if ( + (mode !== mode_digits_empty_string && digits.length === 0) + || digits[0] === "_" + ) { + +// test_cause: +// ["0x", "read_digits", "expected_digits_after_a", "0x", 2] +// ["0x_", "read_digits", "expected_digits_after_a", "0x", 2] + + warn_at("expected_digits_after_a", line, column, snippet); + } + +// PR-390 - Add numeric-separator check. + + if (mode === mode_digits_numeric_separator) { + check_numeric_separator(digits, column); + } else if (digits.indexOf("_") >= 0) { + +// test_cause: +// ["\"\\u{1_2}\"", "read_digits", "illegal_num_separator", "", 6] + + warn_at( + "illegal_num_separator", + line, + column + digits.indexOf("_") + 1 + ); + } + column += digits.length; + line_source = line_source.slice(digits.length); + snippet += digits; + char_after(); + return digits.length; + } + + function read_line() { + +// Put the next line of source in line_source. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + if ( + !option_dict.long + && line_whole.length > 80 + && line_disable === undefined + && !state.mode_json + && token_1 + && !mode_regexp + ) { + +// test_cause: +// ["/////////////////////////////////////////////////////////////////////////////////", "read_line", "too_long", "", 1] //jslint-ignore-line + + warn_at("too_long", line); + } + column = 0; + line += 1; + mode_regexp = false; + line_source = undefined; + line_whole = ""; + if (line_list[line] === undefined) { + return line_source; + } + line_source = line_list[line].line_source; + line_whole = line_source; + +// Scan each line for following ignore-directives: +// "/*jslint-disable*/" +// "/*jslint-enable*/" +// "//jslint-ignore-line" + + if (line_source === "/*jslint-disable*/") { + +// test_cause: +// ["/*jslint-disable*/", "read_line", "jslint_disable", "", 0] + + test_cause("jslint_disable"); + line_disable = line; + } else if (line_source === "/*jslint-enable*/") { + if (line_disable === undefined) { + +// test_cause: +// ["/*jslint-enable*/", "read_line", "unopened_enable", "", 1] + + stop_at("unopened_enable", line); + } + line_disable = undefined; + } else if ( + line_source.endsWith(" //jslint-ignore-line") + || line_source.endsWith(" //jslint-quiet") + ) { + +// test_cause: +// ["0 //jslint-ignore-line", "read_line", "jslint_ignore_line", "", 0] + + test_cause("jslint_ignore_line"); + line_list[line].directive_ignore_line = true; + } + if (line_disable !== undefined) { + +// test_cause: +// ["/*jslint-disable*/\n0", "read_line", "line_disable", "", 0] + + test_cause("line_disable"); + line_source = ""; + } + // jslint_rgx_tab + if (line_source.indexOf("\t") >= 0) { + if (!option_dict.white) { + +// test_cause: +// ["\t", "read_line", "use_spaces", "", 1] + + warn_at("use_spaces", line, line_source.indexOf("\t") + 1); + } + line_source = line_source.replace(jslint_rgx_tab, " "); + } + if (!option_dict.white && line_source.endsWith(" ")) { + +// test_cause: +// [" ", "read_line", "unexpected_trailing_space", "", 1] + + warn_at("unexpected_trailing_space", line, line_source.length - 1); + } + return line_source; + } + + function token_create(id, value, identifier) { + +// Create the token object and append it to token_list. + + let the_token = { + from, + id, + identifier: Boolean(identifier), + line, + nr: token_list.length, + thru: column, + value + }; + token_list.push(the_token); + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + mode_directive = false; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option_dict.white. + + if ( + token_prv.line === line + && token_prv.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (token_prv.id === "(comment)" || token_prv.id === "(regexp)") + ) { + +// test_cause: +// ["/**//**/", "token_create", "expected_space_a_b", "(comment)", 5] + + warn( + "expected_space_a_b", + the_token, + artifact(token_prv), + artifact(the_token) + ); + } + if (token_prv.id === "." && id === "(number)") { + +// test_cause: +// [".0", "token_create", "expected_a_before_b", ".", 1] + + warn("expected_a_before_b", token_prv, "0", "."); + } + if (token_prv_expr.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart. +// Farts are now detected by keeping a list of most recent "(" tokens at any +// given depth. When a "=>" token is encountered, the most recent "(" token at +// current depth is marked as a fart. + + switch (id) { + case "(": + paren_backtrack_list[paren_depth] = the_token; + paren_depth += 1; + break; + case ")": + paren_depth -= 1; + break; + case "=>": + if ( + token_prv_expr.id === ")" + && paren_backtrack_list[paren_depth] + ) { + paren_backtrack_list[paren_depth].fart = the_token; + } + break; + } + +// The previous token is used to detect adjacency problems. + + token_prv = the_token; + +// The token_prv_expr token is a previous token that was not a comment. +// The token_prv_expr token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (token_prv.id !== "(comment)") { + token_prv_expr = token_prv; + } + return the_token; + } + +// Init global_dict and option_dict. + + option_set_item("ecma", true); + Object.keys(option_dict).sort().forEach(function (key) { + option_set_item(key, option_dict[key] === true); + }); + object_assign_from_list(global_dict, global_list, "user-defined"); + +// Scan first line for "#!" and ignore it. + + if (line_list[jslint_fudge].line_source.startsWith("#!")) { + line += 1; + state.mode_shebang = true; + } + token_1 = lex_token(); + state.mode_json = token_1.id === "{" || token_1.id === "["; + +// Lex/loop through each token until (end). + + while (true) { + if (lex_token().id === "(end)") { + break; + } + } +} + +function jslint_phase3_parse(state) { + +// PHASE 3. Parse into using the Pratt-parser. + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + + let anon = "anonymous"; // The guessed name for anonymous functions. + let { + artifact, + catch_list, + catch_stack, + export_dict, + function_list, + function_stack, + global_dict, + import_list, + is_equal, + option_dict, + property_dict, + stop, + syntax_dict, + tenure, + test_cause, + token_global, + token_list, + warn, + warn_at + } = state; + let catchage = catch_stack[0]; // The current catch-block. + let functionage = token_global; // The current function. + let mode_var; // "var" if using var; "let" if using let. + let token_ii = 0; // The number of the next token. + let token_now = token_global; // The current token being examined in + // ... the parse. + let token_nxt = token_global; // The next token to be examined in + // ... . + + function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if ( + token_now.identifier + && token_now.id !== "function" + && token_now.id !== "async" + ) { + anon = token_now.id; + } else if ( + token_now.id === "(string)" + && jslint_rgx_identifier.test(token_now.value) + ) { + anon = token_now.value; + } + +// Attempt to match token_nxt with an expected id. + + if (id !== undefined && token_nxt.id !== id) { + return ( + match === undefined + +// test_cause: +// ["{0:0}", "advance", "expected_a_b", "0", 2] + + ? stop("expected_a_b", token_nxt, id, artifact()) + +// test_cause: +// ["{\"aa\":0", "advance", "expected_a_b_from_c_d", "{", 1] + + : stop( + "expected_a_b_from_c_d", + token_nxt, + id, + artifact(match), + match.line, + artifact() + ) + ); + } + +// Promote the tokens, skipping comments. + + token_now = token_nxt; + while (true) { + token_nxt = token_list[token_ii]; + state.token_nxt = token_nxt; + token_ii += 1; + if (token_nxt.id !== "(comment)") { + if (token_nxt.id === "(end)") { + token_ii -= 1; + } + break; + } + if (state.mode_json) { + +// test_cause: +// ["[//]", "advance", "unexpected_a", "(comment)", 2] + + warn("unexpected_a"); + } + } + } + + function assignment(id) { + +// Create an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led_infix = function (left) { + const the_token = token_now; + let right; + the_token.arity = "assignment"; + right = parse_expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "preassign" + || right.arity === "postassign" + ) { + warn("unexpected_a", right); + } + check_mutation(left); + return the_token; + }; + return the_symbol; + } + + function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token_now; + if (special !== "body") { + functionage.statement_prv = the_block; + } + the_block.arity = "statement"; + the_block.body = special === "body"; + +// Top level function bodies may include the "use strict" pragma. + + if ( + special === "body" + && function_stack.length === 1 + && token_nxt.value === "use strict" + ) { + token_nxt.statement = true; + advance("(string)"); + advance(";"); + } + stmts = parse_statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option_dict.devel && special !== "ignore") { + +// test_cause: +// ["function aa(){}", "block", "empty_block", "{", 14] + + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; + } + + function check_left(left, right) { + +// Warn if the left is not one of these: +// ?. +// ?: +// e() +// e.b +// e[b] +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !check_left(left.expression[1]) + && !check_left(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "?." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right || token_nxt); + return false; + } + return true; + } + + function check_mutation(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + +// test_cause: +// ["0=0", "check_mutation", "bad_assignment_a", "0", 1] + + warn("bad_assignment_a", the_thing); + return false; + } + return true; + } + + function check_not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === token_global) { + +// test_cause: +// [" +// while(0){} +// ", "check_not_top_level", "unexpected_at_top_level_a", "while", 1] + + warn("unexpected_at_top_level_a", thing); + } + } + + function check_ordered(type, token_list) { + +// This function will warn if is unordered. + + token_list.reduce(function (aa, token) { + const bb = artifact(token); + if (!option_dict.unordered && aa > bb) { + warn("expected_a_b_before_c_d", token, type, bb, type, aa); + } + return bb; + }, ""); + } + + function check_ordered_case(case_list) { + +// This function will warn if is unordered. + + case_list.filter(noop).map(function (token) { + switch (token.identifier || token.id) { + case "(number)": + return { + order: 1, + token, + type: "number", + value: Number(artifact(token)) + }; + case "(string)": + return { + order: 2, + token, + type: "string", + value: artifact(token) + }; + case true: + return { + order: 3, + token, + type: "identifier", + value: artifact(token) + }; + } + }).reduce(function (aa, bb) { + if ( + +// PR-419 - Hide warning about unordered case-statements behind beta-flag. + + option_dict.beta + && !option_dict.unordered + && aa && bb + && ( + aa.order > bb.order + || (aa.order === bb.order && aa.value > bb.value) + ) + ) { + warn( + "expected_a_b_before_c_d", + bb.token, + `case-${bb.type}`, + bb.value, + `case-${aa.type}`, + aa.value + ); + } + return bb; + }, undefined); + } + + function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = token_nxt; + let the_value; + +// test_cause: +// ["do{}while()", "condition", "", "", 0] +// ["if(){}", "condition", "", "", 0] +// ["while(){}", "condition", "", "", 0] + + test_cause(""); + the_paren.free = true; + advance("("); + the_value = parse_expression(0); + advance(")"); + if (the_value.wrapped === true) { + +// test_cause: +// ["while((0)){}", "condition", "unexpected_a", "(", 6] + + warn("unexpected_a", the_paren); + } + +// Check for anticondition. + + switch (the_value.id) { + case "%": + warn("unexpected_a", the_value); + break; + case "&": + warn("unexpected_a", the_value); + break; + case "(number)": + warn("unexpected_a", the_value); + break; + case "(string)": + warn("unexpected_a", the_value); + break; + case "*": + warn("unexpected_a", the_value); + break; + case "+": + warn("unexpected_a", the_value); + break; + case "-": + warn("unexpected_a", the_value); + break; + case "/": + warn("unexpected_a", the_value); + break; + case "<<": + warn("unexpected_a", the_value); + break; + case ">>": + warn("unexpected_a", the_value); + break; + case ">>>": + warn("unexpected_a", the_value); + break; + case "?": + warn("unexpected_a", the_value); + break; + case "^": + warn("unexpected_a", the_value); + break; + case "typeof": + warn("unexpected_a", the_value); + break; + case "|": + warn("unexpected_a", the_value); + break; + case "~": + +// test_cause: +// ["if(0%0){}", "condition", "unexpected_a", "%", 5] +// ["if(0&0){}", "condition", "unexpected_a", "&", 5] +// ["if(0){}", "condition", "unexpected_a", "0", 4] +// ["if(0*0){}", "condition", "unexpected_a", "*", 5] +// ["if(0+0){}", "condition", "unexpected_a", "+", 5] +// ["if(0-0){}", "condition", "unexpected_a", "-", 5] +// ["if(0/0){}", "condition", "unexpected_a", "/", 5] +// ["if(0<<0){}", "condition", "unexpected_a", "<<", 5] +// ["if(0>>0){}", "condition", "unexpected_a", ">>", 5] +// ["if(0>>>0){}", "condition", "unexpected_a", ">>>", 5] +// ["if(0?0:0){}", "condition", "unexpected_a", "?", 5] +// ["if(0^0){}", "condition", "unexpected_a", "^", 5] +// ["if(0|0){}", "condition", "unexpected_a", "|", 5] +// ["if(\"aa\"){}", "condition", "unexpected_a", "aa", 4] +// ["if(typeof 0){}", "condition", "unexpected_a", "typeof", 4] +// ["if(~0){}", "condition", "unexpected_a", "~", 4] + + warn("unexpected_a", the_value); + break; + } + return the_value; + } + + function constant(id, type, value) { + +// Create a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud_prefix = ( + typeof value === "function" + ? value + : function () { + token_now.constant = true; + if (value !== undefined) { + token_now.value = value; + } + return token_now; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; + } + + function constant_Function() { + if (!option_dict.eval) { + +// test_cause: +// ["Function", "constant_Function", "unexpected_a", "Function", 1] + + warn("unexpected_a", token_now); + } else if (token_nxt.id !== "(") { + +// test_cause: +// [" +// /*jslint eval*/ +// Function +// ", "constant_Function", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "(", artifact()); + } + return token_now; + } + + function constant_arguments() { + +// test_cause: +// ["arguments", "constant_arguments", "unexpected_a", "arguments", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function constant_eval() { + if (!option_dict.eval) { + +// test_cause: +// ["eval", "constant_eval", "unexpected_a", "eval", 1] + + warn("unexpected_a", token_now); + } else if (token_nxt.id !== "(") { + +// test_cause: +// ["/*jslint eval*/\neval", "constant_eval", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "(", artifact()); + } + return token_now; + } + + function constant_ignore() { + +// test_cause: +// ["ignore", "constant_ignore", "unexpected_a", "ignore", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function constant_isInfinite() { + +// test_cause: +// ["isFinite", "constant_isInfinite", "expected_a_b", "isFinite", 1] + + warn("expected_a_b", token_now, "Number.isFinite", "isFinite"); + return token_now; + } + + function constant_isNaN() { + +// test_cause: +// ["isNaN(0)", "constant_isNaN", "number_isNaN", "isNaN", 1] + + warn("number_isNaN", token_now); + return token_now; + } + + function constant_this() { + if (!option_dict.this) { + +// test_cause: +// ["this", "constant_this", "unexpected_a", "this", 1] + + warn("unexpected_a", token_now); + } + return token_now; + } + + function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + let earlier; + let id = name.id; + +// Reserved words may not be enrolled. + + if (syntax_dict[id] !== undefined && id !== "ignore") { + +// test_cause: +// ["let undefined", "enroll", "reserved_a", "undefined", 5] + + warn("reserved_a", name); + return; + } + +// Has the name been enrolled in this context? + + earlier = functionage.context[id] || catchage.context[id]; + if (earlier) { + +// test_cause: +// ["let aa;let aa", "enroll", "redefinition_a_b", "1", 12] + + warn("redefinition_a_b", name, id, earlier.line); + return; + } + +// Has the name been enrolled in an outer context? + + function_stack.forEach(function ({ + context + }) { + earlier = context[id] || earlier; + }); + if (earlier && id === "ignore") { + if (earlier.role === "variable") { + +// test_cause: +// ["let ignore;function aa(ignore){}", "enroll", "redefinition_a_b", "1", 24] + + warn("redefinition_a_b", name, id, earlier.line); + } + } else if ( + earlier + && role !== "parameter" && role !== "function" + && (role !== "exception" || earlier.role !== "exception") + ) { + +// test_cause: +// [" +// function aa(){try{aa();}catch(aa){aa();}} +// ", "enroll", "redefinition_a_b", "1", 31] +// ["function aa(){var aa;}", "enroll", "redefinition_a_b", "1", 19] + + warn("redefinition_a_b", name, id, earlier.line); + } else if ( + option_dict.beta + && global_dict[id] + && role !== "parameter" + ) { + +// test_cause: +// ["let Array", "enroll", "redefinition_global_a_b", "Array", 5] + + warn("redefinition_global_a_b", name, global_dict[id], id); + } + +// Enroll it. + + Object.assign(name, { + dead: true, + init: false, + parent: ( + role === "exception" + ? catchage + : functionage + ), + readonly, + role, + used: 0 + }); + name.parent.context[id] = name; + } + + function infix(bp, id, f) { + +// Create an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led_infix = function (left) { + const the_token = token_now; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, parse_expression(bp)]; + return the_token; + }; + return the_symbol; + } + + function infix_dot(left) { + const the_token = token_now; + let name = token_nxt; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "flat" + && name.id !== "flatMap" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + +// test_cause: +// ["\"\".aa", "check_left", "unexpected_a", ".", 3] + + check_left(left, the_token); + } + if (!name.identifier) { + +// test_cause: +// ["aa.0", "infix_dot", "expected_identifier_a", "0", 4] + + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; + } + + function infix_fart_unwrapped() { + +// test_cause: +// ["aa=>0", "infix_fart_unwrapped", "wrap_fart_parameter", "=>", 3] + + return stop("wrap_fart_parameter", token_now); + } + + function infix_grave(left) { + const the_tick = prefix_tick(); + +// test_cause: +// ["0``", "check_left", "unexpected_a", "`", 2] + + check_left(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; + } + + function infix_lbracket(left) { + const the_token = token_now; + let name; + let the_subscript = parse_expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + name = survey(the_subscript); + +// PR-404 - Add new directive "subscript" to play nice with Google Closure. + + if (!option_dict.subscript && jslint_rgx_identifier.test(name)) { + +// test_cause: +// ["aa[`aa`]", "infix_lbracket", "subscript_a", "aa", 4] + + warn("subscript_a", the_subscript, name); + } + } + +// test_cause: +// ["0[0]", "check_left", "unexpected_a", "[", 2] + + check_left(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; + } + + function infix_lparen(left) { + const the_paren = token_now; + let ellipsis; + let the_argument; + if (left.id !== "function") { + +// test_cause: +// ["(0?0:0)()", "check_left", "unexpected_a", "(", 8] +// ["0()", "check_left", "unexpected_a", "(", 2] + + check_left(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (token_nxt.id !== ")") { + +// Parse/loop through each token in expression (...). + + while (true) { + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = parse_expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + +// test_cause: +// ["aa(0)", "infix_lparen", "free", "", 0] + + test_cause("free"); + the_paren.free = true; + if (the_argument.wrapped === true) { + +// test_cause: +// ["aa((0))", "infix_lparen", "unexpected_a", "(", 3] + + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + +// test_cause: +// ["aa()", "infix_lparen", "not_free", "", 0] +// ["aa(0,0)", "infix_lparen", "not_free", "", 0] + + test_cause("not_free"); + the_paren.free = false; + } + return the_paren; + } + + function infix_option_chain(left) { + const the_token = token_now; + let name = token_nxt; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + +// test_cause: +// ["(0+0)?.0", "infix_option_chain", "check_left", "", 0] + + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + test_cause("check_left"); + +// test_cause: +// ["(/./)?.0", "check_left", "unexpected_a", "?.", 6] +// ["\"aa\"?.0", "check_left", "unexpected_a", "?.", 5] +// ["aa=[]?.aa", "check_left", "unexpected_a", "?.", 6] + + check_left(left, the_token); + } + +// Issue #468 - Fix optional dynamic-property/function-call not recognized. + + if (name.id === "[" || name.id === "(") { + test_cause("dyn_prop_or_call"); + +// test_cause: +// ["aa?.(bb)", "infix_option_chain", "dyn_prop_or_call", "", 0] +// ["aa?.[bb]", "infix_option_chain", "dyn_prop_or_call", "", 0] + + return left; + } + if (!name.identifier) { + +// test_cause: +// ["aa?.0", "infix_option_chain", "expected_identifier_a", "0", 5] + + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; + } + + function infixr(bp, id) { + +// Create a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led_infix = function parse_infixr_led(left) { + const the_token = token_now; + +// test_cause: +// ["0**0", "parse_infixr_led", "led_infix", "", 0] + + test_cause("led_infix"); + the_token.arity = "binary"; + the_token.expression = [left, parse_expression(bp - 1)]; + return the_token; + }; + return the_symbol; + } + + function parse_expression(rbp, initial) { + +// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it +// is looking for ad hoc lint patterns. We add .fud_stmt to Pratt's model, which +// is like .nud_prefix except that it is only used on the first token of a +// statement. Having .fud_stmt makes it much easier to define statement-oriented +// languages like JavaScript. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// .nud_prefix Null denotation. The prefix handler. +// .fud_stmt First null denotation. The statement handler. +// .led_infix Left denotation. The infix/postfix handler. +// lbp Left binding power of infix operator. It tells us how strongly +// the operator binds to the argument at its left. +// rbp Right binding power. + +// It processes a nud_prefix (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop (it +// consumes tokens until it meets a token whose lbp <= rbp). Specifically, it +// means that it collects all tokens that bind together before returning to the +// operator that called it. It returns the expression's parse tree. + +// For example, "3 + 1 * 2 * 4 + 5" +// parses into +// { +// "id": "+", +// "expression": [ +// { +// "id": "+", +// "expression": [ +// { +// "id": "(number)", +// "value": "3" +// }, +// { +// "id": "*", +// "expression": [ +// { +// "id": "*", +// "expression": [ +// { +// "id": "(number)", +// "value": "1" +// }, +// { +// "id": "(number)", +// "value": "2" +// } +// ] +// }, +// { +// "id": "(number)", +// "value": "4" +// } +// ] +// } +// ] +// }, +// { +// "id": "(number)", +// "value": "5" +// } +// ] +// } + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement. + + if (!initial) { + advance(); + } + the_symbol = syntax_dict[token_now.id]; + if (the_symbol !== undefined && the_symbol.nud_prefix !== undefined) { + +// test_cause: +// ["0", "parse_expression", "symbol", "", 0] + + test_cause("symbol"); + left = the_symbol.nud_prefix(); + } else if (token_now.identifier) { + +// test_cause: +// ["aa", "parse_expression", "identifier", "", 0] + + test_cause("identifier"); + left = token_now; + left.arity = "variable"; + } else { + +// test_cause: +// ["!", "parse_expression", "unexpected_a", "(end)", 1] +// ["/./", "parse_expression", "unexpected_a", "/", 1] +// ["let aa=`${}`;", "parse_expression", "unexpected_a", "}", 11] + + return stop("unexpected_a", token_now); + } + +// Parse/loop through each symbol in expression. + + while (true) { + the_symbol = syntax_dict[token_nxt.id]; + if ( + the_symbol === undefined + || the_symbol.led_infix === undefined + || the_symbol.lbp <= rbp + ) { + break; + } + advance(); + left = the_symbol.led_infix(left); + } + return left; + } + + function parse_fart(the_fart) { + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + Object.assign(the_fart, { + arity: "binary", + context: empty(), + finally: 0, + level: functionage.level + 1, + loop: 0, + name: anon, + switch: 0, + try: 0 + }); + +// PR-384 - Relax warning "function_in_loop". +// +// if (functionage.loop > 0) { + +// // test_cause: +// // ["while(0){aa.map(()=>0);}", "parse_fart", "function_in_loop", "=>", 19] +// +// warn("function_in_loop", the_fart); +// } + +// Push the current function context and establish a new one. + + function_list.push(the_fart); + function_stack.push(functionage); + functionage = the_fart; + +// Parse the parameter list. + + prefix_function_parameter(the_fart); + advance("=>"); + +// The function's body is a block. + + if (token_nxt.id === "{") { + if (!option_dict.fart) { + +// test_cause: +// ["()=>{}", "parse_fart", "use_function_not_fart", "=>", 3] + + warn("use_function_not_fart", the_fart); + } + the_fart.block = block("body"); + } else if ( + syntax_dict[token_nxt.id] !== undefined + && syntax_dict[token_nxt.id].fud_stmt !== undefined + ) { + +// PR-384 - Bugfix - Fixes issue #379 - warn against naked-statement in fart. + +// test_cause: +// ["()=>delete aa", "parse_fart", "unexpected_a_after_b", "=>", 5] + + stop("unexpected_a_after_b", token_nxt, token_nxt.id, "=>"); + +// The function's body is an expression. + + } else { + the_fart.expression = parse_expression(0); + } + +// Restore the previous context. + + functionage = function_stack.pop(); + return the_fart; + } + + function parse_json() { + let container; + let is_dup; + let name; + let negative; + switch (token_nxt.id) { + case "(number)": + if (!jslint_rgx_json_number.test(token_nxt.value)) { + +// test_cause: +// ["[-.0]", "parse_json", "unexpected_a", ".", 3] +// ["[-0x0]", "parse_json", "unexpected_a", "0x0", 3] +// ["[.0]", "parse_json", "unexpected_a", ".", 2] +// ["[0x0]", "parse_json", "unexpected_a", "0x0", 2] + + warn("unexpected_a"); + } + advance("(number)"); + return token_now; + case "(string)": + if (token_nxt.quote !== "\"") { + +// test_cause: +// ["['']", "parse_json", "unexpected_a", "'", 2] + + warn("unexpected_a", token_nxt, token_nxt.quote); + } + advance("(string)"); + return token_now; + case "-": + negative = token_nxt; + negative.arity = "unary"; + advance("-"); + +// Recurse parse_json(). + + negative.expression = parse_json(); + return negative; + case "[": + +// test_cause: +// ["[]", "parse_json", "bracket", "", 0] + + test_cause("bracket"); + container = token_nxt; + container.expression = []; + advance("["); + if (token_nxt.id !== "]") { + while (true) { + +// Recurse parse_json(). + + container.expression.push(parse_json()); + if (token_nxt.id !== ",") { + +// test_cause: +// ["[0,0]", "parse_json", "comma", "", 0] + + test_cause("comma"); + break; + } + advance(","); + } + } + advance("]", container); + return container; + case "false": + case "null": + case "true": + +// test_cause: +// ["[false]", "parse_json", "constant", "", 0] +// ["[null]", "parse_json", "constant", "", 0] +// ["[true]", "parse_json", "constant", "", 0] + + test_cause("constant"); + advance(); + return token_now; + case "{": + +// test_cause: +// ["{}", "parse_json", "brace", "", 0] + + test_cause("brace"); + container = token_nxt; + +// Explicit empty-object required to detect "__proto__". + + is_dup = empty(); + container.expression = []; + advance("{"); + if (token_nxt.id !== "}") { + +// JSON +// Parse/loop through each property in {...}. + + while (true) { + if (token_nxt.quote !== "\"") { + +// test_cause: +// ["{0:0}", "parse_json", "unexpected_a", "0", 2] + + warn( + "unexpected_a", + token_nxt, + token_nxt.quote + ); + } + name = token_nxt; + advance("(string)"); + if (is_dup[token_now.value] !== undefined) { + +// test_cause: +// ["{\"aa\":0,\"aa\":0}", "parse_json", "duplicate_a", "aa", 9] + + warn("duplicate_a", token_now); + } else if (token_now.value === "__proto__") { + +// test_cause: +// ["{\"__proto__\":0}", "parse_json", "weird_property_a", "__proto__", 2] + + warn("weird_property_a", token_now); + } else { + is_dup[token_now.value] = token_now; + } + advance(":"); + container.expression.push( + +// Recurse parse_json(). + + Object.assign(parse_json(), { + label: name + }) + ); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance("}", container); + return container; + default: + +// test_cause: +// ["[undefined]", "parse_json", "unexpected_a", "undefined", 2] + + stop("unexpected_a"); + } + } + + function parse_statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token_now.identifier && token_nxt.id === ":") { + the_label = token_now; + if (the_label.id === "ignore") { + +// test_cause: +// ["ignore:", "parse_statement", "unexpected_a", "ignore", 1] + + warn("unexpected_a", the_label); + } + advance(":"); + switch (token_nxt.id) { + case "do": + case "for": + case "switch": + case "while": + +// test_cause: +// ["aa:do{}", "parse_statement", "the_statement_label", "do", 0] +// ["aa:for{}", "parse_statement", "the_statement_label", "for", 0] +// ["aa:switch{}", "parse_statement", "the_statement_label", "switch", 0] +// ["aa:while{}", "parse_statement", "the_statement_label", "while", 0] + + test_cause("the_statement_label", token_nxt.id); + enroll(the_label, "label", true); + the_label.dead = false; + the_label.init = true; + the_statement = parse_statement(); + functionage.statement_prv = the_statement; + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + +// test_cause: +// ["aa:", "parse_statement", "unexpected_label_a", "aa", 1] + + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token_now; + first.statement = true; + the_symbol = syntax_dict[first.id]; + if ( + the_symbol !== undefined + && the_symbol.fud_stmt !== undefined + +// PR-318 - Bugfix - Fixes issues #316, #317 - dynamic-import(). + + && !(the_symbol.id === "import" && token_nxt.id === "(") + ) { + the_symbol.disrupt = false; + the_symbol.statement = true; + token_now.arity = "statement"; + the_statement = the_symbol.fud_stmt(); + functionage.statement_prv = the_statement; + } else { + +// It is an expression statement. + + the_statement = parse_expression(0, true); + functionage.statement_prv = the_statement; + if (the_statement.wrapped && the_statement.id !== "(") { + +// test_cause: +// ["(0)", "parse_statement", "unexpected_a", "(", 1] + + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; + } + + function parse_statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const statement_list = []; + let a_statement; + let disrupt = false; + +// Parse/loop each statement until a statement-terminator is reached. + + while (true) { + switch (token_nxt.id) { + case "(end)": + case "case": + case "default": + case "else": + case "}": + +// test_cause: +// [";", "parse_statements", "closer", "", 0] +// ["case", "parse_statements", "closer", "", 0] +// ["default", "parse_statements", "closer", "", 0] +// ["else", "parse_statements", "closer", "", 0] +// ["}", "parse_statements", "closer", "", 0] + + test_cause("closer"); + return statement_list; + } + a_statement = parse_statement(); + statement_list.push(a_statement); + if (disrupt) { + +// test_cause: +// ["while(0){break;0;}", "parse_statements", "unreachable_a", "0", 16] + + warn("unreachable_a", a_statement); + } + disrupt = a_statement.disrupt; + } + } + + function postassign(id) { + +// Create one of the postassign operators. + + const the_symbol = symbol(id, 150); + the_symbol.led_infix = function (left) { + token_now.expression = left; + token_now.arity = "postassign"; + check_mutation(token_now.expression); + return token_now; + }; + return the_symbol; + } + + function preassign(id) { + +// Create one of the preassign operators. + + const the_symbol = symbol(id); + the_symbol.nud_prefix = function () { + const the_token = token_now; + the_token.arity = "preassign"; + the_token.expression = parse_expression(150); + check_mutation(the_token.expression); + return the_token; + }; + return the_symbol; + } + + function prefix(id, f) { + +// Create a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud_prefix = function () { + const the_token = token_now; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = parse_expression(150); + return the_token; + }; + return the_symbol; + } + + function prefix_assign_divide() { + +// test_cause: +// ["/=", "prefix_assign_divide", "expected_a_b", "/=", 1] + + stop("expected_a_b", token_now, "/\\=", "/="); + } + + function prefix_async() { + let the_async = token_now; + let the_function; + token_nxt.arity = the_async.arity; + +// PR-414 - Parse async fart. + + if (token_nxt.fart) { + advance("("); + the_function = Object.assign(token_now.fart, { + async: 1 + }); + if (!option_dict.fart) { + +// test_cause: +// ["async()=>0", "prefix_async", "use_function_not_fart", "=>", 8] + + warn("use_function_not_fart", the_function); + } + prefix_lparen(); + +// Parse async function. + + } else { + advance("function"); + the_function = Object.assign(token_now, { + async: 1 + }); + prefix_function(); + } + if (the_function.async === 1) { + +// test_cause: +// [" +// async function aa(){} +// ", "prefix_async", "missing_await_statement", "function", 7] + + warn("missing_await_statement", the_function); + } + return the_function; + } + + function prefix_await() { + const the_await = token_now; + +// PR-370 - Add top-level-await support. + + if (functionage.async === 0 && functionage !== token_global) { + +// test_cause: +// ["function aa(){aa=await 0;}", "prefix_await", "unexpected_a", "await", 18] +// ["function aa(){await 0;}", "prefix_await", "unexpected_a", "await", 15] + + warn("unexpected_a", the_await); + } else { + functionage.async += 1; + } + if (the_await.arity === "statement") { + +// PR-405 - Bugfix - fix expression after "await" mis-identified as statement. + + the_await.expression = parse_expression(150); + semicolon(); + } else { + the_await.expression = parse_expression(150); + } + return the_await; + } + + function prefix_fart() { + +// test_cause: +// ["=>0", "prefix_fart", "expected_a_before_b", "=>", 1] + + return stop("expected_a_before_b", token_now, "()", "=>"); + } + + function prefix_function(the_function) { + let name = the_function && the_function.name; + if (the_function === undefined) { + the_function = token_now; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!token_nxt.identifier) { + +// test_cause: +// ["function(){}", "prefix_function", "expected_identifier_a", "(", 9] +// ["function*aa(){}", "prefix_function", "expected_identifier_a", "*", 9] + + return stop("expected_identifier_a"); + } + name = token_nxt; + enroll(name, "variable", true); + the_function.name = Object.assign(name, { + calls: empty(), + +// PR-331 - Bugfix - Fixes issue #272 - function hoisting not allowed. + + dead: false, + init: true + }); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + the_function.name = anon; + if (token_nxt.identifier) { + name = token_nxt; + the_function.name = name; + advance(); + } + } + } + +// Probably deadcode. +// if (mode_mega) { +// warn("unexpected_a", the_function); +// } +// jslint_assert(!mode_mega, `Expected !mode_mega.`); + +// PR-378 - Relax warning "function_in_loop". +// +// // Don't create functions in loops. It is inefficient, and it can lead to +// // scoping errors. +// +// if (functionage.loop > 0) { +// +// // test_cause: +// // [" +// // while(0){aa.map(function(){});} +// // ", "prefix_function", "function_in_loop", "function", 17] +// +// warn("function_in_loop", the_function); +// } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + Object.assign(the_function, { + async: the_function.async || 0, + context: empty(), + finally: 0, + level: functionage.level + 1, + loop: 0, + statement_prv: undefined, + switch: 0, + try: 0 + }); + if (the_function.arity !== "statement" && typeof name === "object") { + +// test_cause: +// ["let aa=function bb(){return;};", "prefix_function", "expression", "bb", 0] + + test_cause("expression", name.id); + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// PR-334 - Bugfix - fix function-redefinition not warned inside function-call. +// Push the current function context and establish a new one. + + function_list.push(the_function); + function_stack.push(functionage); + functionage = the_function; + +// Parse the parameter list. + + advance("("); + token_now.arity = "function"; + prefix_function_parameter(the_function); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && token_nxt.line === token_now.line + ) { + +// test_cause: +// ["function aa(){}0", "prefix_function", "unexpected_a", "0", 16] + + return stop("unexpected_a"); + } + if ( + token_nxt.id === "." + || token_nxt.id === "?." + +// PR-459 - Allow destructuring-assignment after function-definition. + + // || token_nxt.id === "[" + ) { + +// test_cause: +// ["function aa(){}\n.aa", "prefix_function", "unexpected_a", ".", 1] +// ["function aa(){}\n?.aa", "prefix_function", "unexpected_a", "?.", 1] + + warn("unexpected_a"); + } + +// Check functions are ordered. + + check_ordered( + "function", + function_list.slice( + function_list.indexOf(the_function) + 1 + ).map(function ({ + level, + name + }) { + return (level === the_function.level + 1) && name; + }).filter(function (name) { + return option_dict.beta && name && name.id; + }) + ); + +// Restore the previous context. + + functionage = function_stack.pop(); + return the_function; + } + + function prefix_function_parameter(the_function) { + +// This function will parse input at beginning of + + let optional; + let parameters = []; + let signature = ["("]; + let subparam; + function param_enroll(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + +// test_cause: +// ["([aa])=>0", "param_enroll", "use_function_not_fart", "=>", 7] +// ["({aa})=>0", "param_enroll", "use_function_not_fart", "=>", 7] + + if (the_function.id === "=>" && !option_dict.fart) { + warn("use_function_not_fart", the_function); + } + +// Recurse param_enroll(). + + name.names.forEach(param_enroll); + } + } + function param_parse() { + let ellipsis = false; + let param; + if (token_nxt.id === "{") { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,{}){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + param = token_nxt; + param.names = []; + advance("{"); + signature.push("{"); + while (true) { + subparam = token_nxt; + if (!subparam.identifier) { + +// test_cause: +// ["function aa(aa=0,{}){}", "param_parse", "expected_identifier_a", "}", 19] +// ["function aa({0}){}", "param_parse", "expected_identifier_a", "0", 14] + + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (token_nxt.id === ":") { + advance(":"); + advance(); + token_now.label = subparam; + subparam = token_now; + if (!subparam.identifier) { + +// test_cause: +// ["function aa({aa:0}){}", "param_parse", "expected_identifier_a", "}", 18] + + return stop( + "expected_identifier_a", + token_nxt + ); + } + } + +// test_cause: +// ["function aa({aa=aa},aa){}", "param_parse", "equal", "", 0] + + test_cause("equal"); + if (token_nxt.id === "=") { + advance("="); + subparam.expression = parse_expression(); + param.open = true; + } + param.names.push(subparam); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + } else { + break; + } + } + parameters.push(param); + +// test_cause: +// [" +// function aa({bb,aa}){} +// ", "check_ordered", "expected_a_b_before_c_d", "aa", 17] + + check_ordered("parameter", param.names); + advance("}"); + signature.push("}"); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } else if (token_nxt.id === "[") { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,[]){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + param = token_nxt; + param.names = []; + advance("["); + signature.push("[]"); + while (true) { + subparam = token_nxt; + if (!subparam.identifier) { + +// test_cause: +// ["function aa(aa=0,[]){}", "param_parse", "expected_identifier_a", "]", 19] + + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + +// test_cause: +// ["function aa([aa=aa],aa){}", "param_parse", "id", "", 0] + + test_cause("id"); + if (token_nxt.id === "=") { + advance("="); + subparam.expression = parse_expression(); + param.open = true; + } + if (token_nxt.id === ",") { + advance(","); + } else { + break; + } + } + parameters.push(param); + advance("]"); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } else { + if (token_nxt.id === "...") { + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,...){}", "param_parse", "required_a_optional_b", "aa", 21] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + } + if (!token_nxt.identifier) { + +// test_cause: +// ["function aa(0){}", "param_parse", "expected_identifier_a", "0", 13] + + return stop("expected_identifier_a"); + } + param = token_nxt; + parameters.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (token_nxt.id === "=") { + optional = param; + advance("="); + param.expression = parse_expression(0); + } else { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,bb){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } + } + } + +// test_cause: +// ["function aa(){}", "prefix_function_parameter", "opener", "(", 0] + + test_cause("opener", token_now.id); + token_now.free = false; + if (token_nxt.id !== ")" && token_nxt.id !== "(end)") { + param_parse(); + } + advance(")"); + signature.push(")"); + parameters.forEach(param_enroll); + the_function.parameters = parameters; + the_function.signature = signature.join(""); + } + + function prefix_lbrace() { + const seen = empty(); + const the_brace = token_now; + let extra; + let full; + let id; + let name; + let the_colon; + let value; + the_brace.expression = []; + if (token_nxt.id !== "}") { + +// Parse/loop through each property in {...}. + + while (true) { + name = token_nxt; + advance(); + if ( + (name.id === "get" || name.id === "set") + && token_nxt.identifier + ) { + if (!option_dict.getset) { + +// test_cause: +// ["aa={get aa(){}}", "prefix_lbrace", "unexpected_a", "get", 5] + + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + token_nxt.id; + name = token_nxt; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + +// test_cause: +// ["aa={get aa(){},get aa(){}}", "prefix_lbrace", "duplicate_a", "aa", 20] + + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else if (name.id === "`") { + +// test_cause: +// ["aa={`aa`:0}", "prefix_lbrace", "unexpected_a", "`", 5] + + stop("unexpected_a", name); + + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + +// test_cause: +// ["aa={aa,aa}", "prefix_lbrace", "duplicate_a", "aa", 8] + + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (token_nxt.id === "}" || token_nxt.id === ",") { + if (typeof extra === "string") { + +// test_cause: +// ["aa={get aa}", "prefix_lbrace", "closer", "", 0] + + test_cause("closer"); + advance("("); + } + value = parse_expression(Infinity, true); + } else if (token_nxt.id === "(") { + +// test_cause: +// ["aa={aa()}", "prefix_lbrace", "paren", "", 0] +// ["aa={get aa(){}}", "prefix_lbrace", "paren", "", 0] + + test_cause("paren"); + value = prefix_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + +// test_cause: +// ["aa={get aa.aa}", "prefix_lbrace", "paren", "", 0] + + test_cause("paren"); + advance("("); + } + the_colon = token_nxt; + advance(":"); + value = parse_expression(0); + if ( + value.id === name.id + && value.id !== "function" + ) { + +// test_cause: +// ["aa={aa:aa}", "prefix_lbrace", "unexpected_a", ": aa", 7] + + warn("unexpected_a", the_colon, ": " + name.id); + } + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + +// test_cause: +// ["aa={\"aa\":0}", "prefix_lbrace", "colon", "", 0] + + test_cause("colon"); + advance(":"); + value = parse_expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (token_nxt.id !== ",") { + break; + } + +// test_cause: +// ["aa={\"aa\":0,\"bb\":0}", "prefix_lbrace", "comma", "", 0] + + test_cause("comma"); + advance(","); + if (token_nxt.id === "}") { + +// test_cause: +// ["let aa={aa:0,}", "prefix_lbrace", "unexpected_a", ",", 13] + + warn("unexpected_a", token_now); + break; + } + } + } + +// test_cause: +// ["aa={bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8] + + check_ordered( + "property", + the_brace.expression.map(function ({ + label + }) { + return label; + }) + ); + advance("}"); + return the_brace; + } + + function prefix_lbracket() { + const the_token = token_now; + let element; + let ellipsis; + the_token.expression = []; + if (token_nxt.id !== "]") { + +// Parse/loop through each element in [...]. + + while (true) { + ellipsis = false; + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + element = parse_expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (token_nxt.id !== ",") { + break; + } + advance(","); + if (token_nxt.id === "]") { + +// test_cause: +// ["let aa=[0,]", "prefix_lbracket", "unexpected_a", ",", 10] + + warn("unexpected_a", token_now); + break; + } + } + } + advance("]"); + return the_token; + } + + function prefix_lparen() { + let the_paren = token_now; + let the_value; + +// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart. + + if (token_now.fart) { + return parse_fart(token_now.fart); + } + +// test_cause: +// ["(0)", "prefix_lparen", "expr", "", 0] + + test_cause("expr"); + the_paren.free = true; + the_value = parse_expression(0); + if (the_value.wrapped === true) { + +// test_cause: +// ["((0))", "prefix_lparen", "unexpected_a", "(", 1] + + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + return the_value; + } + + function prefix_new() { + const the_new = token_now; + let right; + right = parse_expression(160); + if (token_nxt.id !== "(") { + +// test_cause: +// ["new aa", "prefix_new", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "()", artifact()); + } + the_new.expression = right; + return the_new; + } + + function prefix_tick() { + const the_tick = token_now; + the_tick.value = []; + the_tick.expression = []; + if (token_nxt.id !== "`") { + +// Parse/loop through each token in `${...}`. + + while (true) { + advance("(string)"); + the_tick.value.push(token_now); + if (token_nxt.id !== "${") { + break; + } + advance("${"); + +// test_cause: +// ["let aa=`${}`;", "prefix_tick", "${", "", 0] + + test_cause("${"); + the_tick.expression.push(parse_expression(0)); + advance("}"); + } + } + advance("`"); + return the_tick; + } + + function prefix_void() { + const the_void = token_now; + +// test_cause: +// ["void 0", "prefix_void", "unexpected_a", "void", 1] +// ["void", "prefix_void", "unexpected_a", "void", 1] + + warn("unexpected_a", the_void); + the_void.expression = parse_expression(0); + return the_void; + } + + function semicolon() { + +// Try to match a semicolon. + + if (token_nxt.id === ";") { + advance(";"); + } else { + +// test_cause: +// ["0", "semicolon", "expected_a_b", "(end)", 1] + + warn_at( + "expected_a_b", + token_now.line, + token_now.thru + 1, + ";", + artifact() + ); + } + anon = "anonymous"; + } + + function stmt(id, fud_stmt) { + +// Create a statement. + + const the_symbol = symbol(id); + the_symbol.fud_stmt = fud_stmt; + return the_symbol; + } + + function stmt_break() { + const the_break = token_now; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + +// test_cause: +// ["break", "stmt_break", "unexpected_a", "break", 1] + + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (token_nxt.identifier && token_now.line === token_nxt.line) { + the_label = functionage.context[token_nxt.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + if (the_label !== undefined && the_label.dead) { + +// test_cause: +// ["aa:{function aa(aa){break aa;}}", "stmt_break", "out_of_scope_a", "aa", 27] + + warn("out_of_scope_a"); + } else { + +// test_cause: +// ["aa:{break aa;}", "stmt_break", "not_label_a", "aa", 11] + + warn("not_label_a"); + } + } else { + the_label.used += 1; + } + the_break.label = token_nxt; + advance(); + } + advance(";"); + return the_break; + } + + function stmt_continue() { + const the_continue = token_now; + if (functionage.loop < 1 || functionage.finally > 0) { + +// test_cause: +// ["continue", "stmt_continue", "unexpected_a", "continue", 1] +// [" +// function aa(){while(0){try{}finally{continue}}} +// ", "stmt_continue", "unexpected_a", "continue", 37] + + warn("unexpected_a", the_continue); + } + check_not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; + } + + function stmt_debugger() { + const the_debug = token_now; + if (!option_dict.devel) { + +// test_cause: +// ["debugger", "stmt_debugger", "unexpected_a", "debugger", 1] + + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; + } + + function stmt_delete() { + const the_token = token_now; + const the_value = parse_expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + +// test_cause: +// ["delete 0", "stmt_delete", "expected_a_b", "0", 8] + + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; + } + + function stmt_do() { + const the_do = token_now; + check_not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + +// test_cause: +// ["function aa(){do{break;}while(0)}", "stmt_do", "weird_loop", "do", 15] + + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; + } + + function stmt_export() { + let export_list = []; + let the_export = token_now; + let the_id; + let the_name; + let the_thing; + + the_export.expression = []; + if (token_nxt.id === "default") { + if (export_dict.default !== undefined) { + +// test_cause: +// [" +// export default 0;export default 0 +// ", "stmt_export", "duplicate_a", "default", 25] + + warn("duplicate_a"); + } + advance("default"); + the_thing = parse_expression(0); + if ( + the_thing.id !== "(" + || the_thing.expression[0].id !== "." + || the_thing.expression[0].expression.id !== "Object" + || the_thing.expression[0].name.id !== "freeze" + ) { + +// test_cause: +// ["export default {}", "stmt_export", "freeze_exports", "{", 16] + + warn("freeze_exports", the_thing); + +// PR-301 - Bugfix - Fixes issues #282 - optional-semicolon. + + } else { + +// test_cause: +// [" +// export default Object.freeze({}) +// ", "semicolon", "expected_a_b", "(end)", 32] + + semicolon(); + } + export_dict.default = the_thing; + the_export.expression.push(the_thing); + } else { + +// PR-439 - Add grammar for "export async function ...". + + if (token_nxt.id === "function" || token_nxt.id === "async") { + +// test_cause: +// ["export async function aa(){}", "stmt_export", "freeze_exports", "async", 8] +// ["export function aa(){}", "stmt_export", "freeze_exports", "function", 8] + + warn("freeze_exports"); + the_thing = parse_statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (export_dict[the_id] !== undefined) { + +// test_cause: +// [" +// let aa;export{aa};export function aa(){} +// ", "stmt_export", "duplicate_a", "aa", 35] + + warn("duplicate_a", the_name); + } + export_dict[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + token_nxt.id === "var" + || token_nxt.id === "let" + || token_nxt.id === "const" + ) { + +// test_cause: +// ["export const", "stmt_export", "unexpected_a", "const", 8] +// ["export let", "stmt_export", "unexpected_a", "let", 8] +// ["export var", "stmt_export", "unexpected_a", "var", 8] + + warn("unexpected_a"); + parse_statement(); + } else if (token_nxt.id === "{") { + +// test_cause: +// ["export {}", "stmt_export", "advance{", "", 0] + + test_cause("advance{"); + advance("{"); + while (true) { + if (!token_nxt.identifier) { + +// test_cause: +// ["export {}", "stmt_export", "expected_identifier_a", "}", 9] + + stop("expected_identifier_a"); + } + the_id = token_nxt.id; + export_list.push(token_nxt); + the_name = token_global.context[the_id]; + if (the_name === undefined) { + +// test_cause: +// ["export {aa}", "stmt_export", "unexpected_a", "aa", 9] + + warn("unexpected_a"); + } else { + the_name.used += 1; + if (export_dict[the_id] !== undefined) { + +// test_cause: +// ["let aa;export{aa,aa}", "stmt_export", "duplicate_a", "aa", 18] + + warn("duplicate_a"); + } + export_dict[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + if (token_nxt.id === ",") { + advance(","); + } else { + break; + } + } + +// PR-439 - Check exported properties are ordered. + +// test_cause: +// ["export {bb, aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 13] + + check_ordered("export", export_list); + advance("}"); + semicolon(); + } else { + +// test_cause: +// ["export", "stmt_export", "unexpected_a", "(end)", 1] + + stop("unexpected_a"); + } + } + state.mode_module = true; + return the_export; + } + + function stmt_for() { + let first; + let the_for = token_now; + if (!option_dict.for) { + +// test_cause: +// ["for", "stmt_for", "unexpected_a", "for", 1] + + warn("unexpected_a", the_for); + } + check_not_top_level(the_for); + functionage.loop += 1; + advance("("); + token_now.free = true; + if (token_nxt.id === ";") { + +// test_cause: +// ["for(;;){}", "stmt_for", "expected_a_b", "for (;", 1] + + return stop("expected_a_b", the_for, "while (", "for (;"); + } + switch (token_nxt.id) { + case "const": + case "let": + case "var": + +// test_cause: +// ["for(const aa in aa){}", "stmt_for", "unexpected_a", "const", 5] + + return stop("unexpected_a"); + } + first = parse_expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + +// test_cause: +// ["for(0 in aa){}", "stmt_for", "bad_assignment_a", "0", 5] + + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = parse_expression(0); + advance(";"); + the_for.inc = parse_expression(0); + if (the_for.inc.id === "++") { + +// test_cause: +// ["for(aa;aa;aa++){}", "stmt_for", "expected_a_b", "++", 13] + + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + +// test_cause: +// [" +// /*jslint for*/ +// function aa(bb,cc){for(0;0;0){break;}} +// ", "stmt_for", "weird_loop", "for", 20] + + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; + } + + function stmt_if() { + const the_if = token_now; + let the_else; + the_if.expression = condition(); + the_if.block = block(); + if (token_nxt.id === "else") { + advance("else"); + the_else = token_now; + the_if.else = ( + token_nxt.id === "if" + ? parse_statement() + : block() + ); + +// test_cause: +// ["if(0){0}else if(0){0}", "stmt_if", "else", "", 0] +// ["if(0){0}else{0}", "stmt_if", "else", "", 0] + + test_cause("else"); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + +// test_cause: +// ["if(0){break;}else{break;}", "stmt_if", "disrupt", "", 0] + + test_cause("disrupt"); + the_if.disrupt = true; + } else { + +// test_cause: +// ["if(0){break;}else{}", "stmt_if", "unexpected_a", "else", 14] + + warn("unexpected_a", the_else); + } + } + } + return the_if; + } + + function stmt_import() { + const the_import = token_now; + let name; + let names; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// if (typeof state.mode_module === "object") { +// +// // test_cause: +// // [" +// // /*global aa*/ +// // import aa from "aa" +// // ", "stmt_import", "unexpected_directive_a", "global", 1] +// +// warn( +// "unexpected_directive_a", +// state.mode_module, +// state.mode_module.directive +// ); +// } + + state.mode_module = true; + +// PR-436 - Add grammar for side-effect import-statement. + + if (token_nxt.id === "(string)") { + +// test_cause: +// ["import \"./aa.mjs\";", "stmt_import", "import_side_effect", "", 0] + + test_cause("import_side_effect"); + warn("expected_a_b", token_nxt, "{", artifact()); + advance(); + semicolon(); + return the_import; + } + if (token_nxt.identifier) { + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// ["import ignore from \"aa\"", "stmt_import", "unexpected_a", "ignore", 8] + + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + names = []; + advance("{"); + if (token_nxt.id !== "}") { + while (true) { + if (!token_nxt.identifier) { + +// test_cause: +// ["import {", "stmt_import", "expected_identifier_a", "(end)", 1] + + stop("expected_identifier_a"); + } + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// ["import {ignore} from \"aa\"", "stmt_import", "unexpected_a", "ignore", 9] + + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token_now; + if (!jslint_rgx_module.test(token_now.value)) { + +// test_cause: +// ["import aa from \"!aa\"", "stmt_import", "bad_module_name_a", "!aa", 16] + + warn("bad_module_name_a", token_now); + } + import_list.push(token_now.value); + semicolon(); + return the_import; + } + + function stmt_lbrace() { + +// test_cause: +// [";{}", "stmt_lbrace", "naked_block", "{", 2] +// ["class aa{}", "stmt_lbrace", "naked_block", "{", 9] + + warn("naked_block", token_now); + return block("naked"); + } + + function stmt_return() { + const the_return = token_now; + check_not_top_level(the_return); + if (functionage.finally > 0) { + +// test_cause: +// [" +// function aa(){try{}finally{return;}} +// ", "stmt_return", "unexpected_a", "return", 28] + + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (token_nxt.id !== ";" && the_return.line === token_nxt.line) { + the_return.expression = parse_expression(10); + } + advance(";"); + return the_return; + } + + function stmt_semicolon() { + +// test_cause: +// [";", "stmt_semicolon", "unexpected_a", ";", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function stmt_switch() { + const the_cases = []; + const the_switch = token_now; + let dups = []; + let exp; + let last; + let stmts; + let the_case; + let the_default; + let the_disrupt = true; + let the_last; + function is_dup(thing) { + return is_equal(thing, exp); + } + check_not_top_level(the_switch); + if (functionage.finally > 0) { + +// test_cause: +// [" +// function aa(){try{}finally{switch(0){}}} +// ", "stmt_switch", "unexpected_a", "switch", 28] + + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token_now.free = true; + the_switch.expression = parse_expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + while (true) { + +// Loop through cases with breaks. + + the_case = token_nxt; + the_case.arity = "statement"; + the_case.expression = []; + while (true) { + +// Loop through fallthrough cases. + + advance("case"); + token_now.switch = true; + exp = parse_expression(0); + if (dups.some(is_dup)) { + +// test_cause: +// [" +// switch(0){case 0:break;case 0:break} +// ", "stmt_switch", "unexpected_a", "0", 29] + + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (token_nxt.id !== "case") { + break; + } + } + +// test_cause: +// [" +// switch(0){case 1:case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 23] +// [" +// switch(0){case "aa":case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 26] +// [" +// switch(0){case "bb":case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 26] +// [" +// switch(0){case aa:case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24] +// [" +// switch(0){case bb:case aa:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24] + + check_ordered_case(the_case.expression); + stmts = parse_statements(); + if (stmts.length < 1) { + +// test_cause: +// [" +// switch(0){case 0:default:} +// ", "stmt_switch", "expected_statements_a", "default", 18] +// ["switch(0){case 0:}", "stmt_switch", "expected_statements_a", "}", 18] + + warn("expected_statements_a"); + +// PR-359 - Bugfix - Fixes issue #358 - switch-statement crashes jslint. + + break; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn("expected_a_before_b", token_nxt, "break;", artifact()); + } + if (token_nxt.id !== "case") { + break; + } + } + +// test_cause: +// [" +// switch(0){case 1:break;case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 29] +// [" +// switch(0){case "aa":break;case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 32] +// [" +// switch(0){case "bb":break;case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 32] +// [" +// switch(0){case aa:break;case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30] +// [" +// switch(0){case bb:break;case aa:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30] + + check_ordered_case(the_cases.map(function ({ + expression + }) { + return expression[0]; + })); + dups = undefined; + if (token_nxt.id === "default") { + the_default = token_nxt; + advance("default"); + token_now.switch = true; + advance(":"); + the_switch.else = parse_statements(); + if (the_switch.else.length < 1) { + +// test_cause: +// [" +// switch(0){case 0:break;default:} +// ", "stmt_switch", "unexpected_a", "default", 24] + + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + the_last = the_switch.else[ + the_switch.else.length - 1 + ]; + if ( + the_last.id === "break" + && the_last.label === undefined + ) { + +// test_cause: +// [" +// switch(0){case 0:break;default:break;} +// ", "stmt_switch", "unexpected_a", "break", 32] + + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; + } + + function stmt_throw() { + const the_throw = token_now; + the_throw.disrupt = true; + the_throw.expression = parse_expression(10); + semicolon(); + if (functionage.try > 0) { + +// test_cause: +// ["try{throw 0}catch(){}", "stmt_throw", "unexpected_a", "throw", 5] + + warn("unexpected_a", the_throw); + } + return the_throw; + } + + function stmt_try() { + const the_try = token_now; + let ignored; + let the_catch; + let the_disrupt; + if (functionage.try > 0) { + +// test_cause: +// ["try{try{}catch(){}}catch(){}", "stmt_try", "unexpected_a", "try", 5] + + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (token_nxt.id === "catch") { + ignored = "ignore"; + the_catch = token_nxt; + the_try.catch = the_catch; + advance("catch"); + +// Create new catch-scope for catch-parameter. + + catch_stack.push(catchage); + catchage = the_catch; + catch_list.push(catchage); + the_catch.context = empty(); + if (token_nxt.id === "(") { + advance("("); + if (!token_nxt.identifier) { + +// test_cause: +// ["try{}catch(){}", "stmt_try", "expected_identifier_a", ")", 12] + + return stop("expected_identifier_a"); + } + if (token_nxt.id !== "ignore") { + ignored = undefined; + the_catch.name = token_nxt; + enroll(token_nxt, "exception", true); + } + advance(); + advance(")"); + } + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + +// Restore previous catch-scope after catch-block. + + catchage = catch_stack.pop(); + +// PR-404 - Relax warning about missing `catch` in `try...finally` statement. +// +// } else { +// +// // test_cause: +// // ["try{}finally{break;}", "stmt_try", "expected_a_before_b", "finally", 6] +// +// warn("expected_a_before_b", token_nxt, "catch", artifact()); + + } + if (token_nxt.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; + } + + function stmt_var() { + let ellipsis; + let mode_const; + let name; + let the_brace; + let the_bracket; + let the_variable = token_now; + let variable_prv; + mode_const = the_variable.id === "const"; + the_variable.names = []; + +// A program may use var or let, but not both. + + if (!mode_const) { + if (mode_var === undefined) { + mode_var = the_variable.id; + } else if (the_variable.id !== mode_var) { + +// test_cause: +// ["let aa;var aa", "stmt_var", "expected_a_b", "var", 8] + + warn("expected_a_b", the_variable, mode_var, the_variable.id); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + +// test_cause: +// ["switch(0){case 0:var aa}", "stmt_var", "var_switch", "var", 18] + + warn("var_switch", the_variable); + } + switch ( + Boolean(functionage.statement_prv) + && functionage.statement_prv.id + ) { + case "const": + case "let": + case "var": + +// test_cause: +// ["const aa=0;const bb=0;", "stmt_var", "var_prv", "const", 0] +// ["let aa=0;let bb=0;", "stmt_var", "var_prv", "let", 0] +// ["var aa=0;var bb=0;", "stmt_var", "var_prv", "var", 0] + + test_cause("var_prv", functionage.statement_prv.id); + variable_prv = functionage.statement_prv; + break; + case "import": + +// test_cause: +// ["import aa from \"aa\";\nlet bb=0;", "stmt_var", "import_prv", "", 0] + + test_cause("import_prv"); + break; + case false: + break; + default: + if ( + (option_dict.beta && !option_dict.variable) + || the_variable.id === "var" + ) { + +// test_cause: +// ["console.log();let aa=0;", "stmt_var", "var_on_top", "let", 15] +// ["console.log();var aa=0;", "stmt_var", "var_on_top", "var", 15] +// ["try{aa();}catch(aa){var aa=0;}", "stmt_var", "var_on_top", "var", 21] +// ["while(0){var aa;}", "stmt_var", "var_on_top", "var", 10] + + warn("var_on_top", token_now); + } + } + while (true) { + if (token_nxt.id === "{") { + if (the_variable.id === "var") { + +// test_cause: +// ["var{aa}=0", "stmt_var", "unexpected_a", "var", 1] + + warn("unexpected_a", the_variable); + } + the_brace = token_nxt; + advance("{"); + while (true) { + name = token_nxt; + if (!name.identifier) { + +// test_cause: +// ["let {0}", "stmt_var", "expected_identifier_a", "0", 6] + + return stop("expected_identifier_a"); + } + survey(name); + advance(); + if (token_nxt.id === ":") { + advance(":"); + if (!token_nxt.identifier) { + +// test_cause: +// ["let {aa:0}", "stmt_var", "expected_identifier_a", "0", 9] +// ["let {aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 9] + + return stop("expected_identifier_a"); + } + +// PR-363 - Bugfix +// Add test against false-warning in code +// '/*jslint node*/\nlet {aa:bb} = {}; bb();'. +// +// token_nxt.label = name; +// the_variable.names.push(token_nxt); +// enroll(token_nxt, "variable", mode_const); + + name = token_nxt; + the_variable.names.push(name); + survey(name); + enroll(name, "variable", mode_const); + + advance(); + the_brace.open = true; + } else { + the_variable.names.push(name); + enroll(name, "variable", mode_const); + } + name.dead = false; + name.init = true; + if (token_nxt.id === "=") { + +// test_cause: +// ["let {aa=0}", "stmt_var", "assign", "", 0] + + test_cause("assign"); + advance("="); + name.expression = parse_expression(); + the_brace.open = true; + } + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + +// test_cause: +// ["let{bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8] + + check_ordered(the_variable.id, the_variable.names); + advance("}"); + advance("="); + the_variable.expression = parse_expression(0); + } else if (token_nxt.id === "[") { + if (the_variable.id === "var") { + +// test_cause: +// ["var[aa]=0", "stmt_var", "unexpected_a", "var", 1] + + warn("unexpected_a", the_variable); + } + the_bracket = token_nxt; + advance("["); + while (true) { + ellipsis = false; + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + if (!token_nxt.identifier) { + +// test_cause: +// ["let[]", "stmt_var", "expected_identifier_a", "]", 5] + + return stop("expected_identifier_a"); + } + name = token_nxt; + advance(); + the_variable.names.push(name); + enroll(name, "variable", mode_const); + name.dead = false; + name.init = true; + if (ellipsis) { + name.ellipsis = true; + break; + } + if (token_nxt.id === "=") { + advance("="); + name.expression = parse_expression(); + the_bracket.open = true; + } + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + advance("]"); + advance("="); + the_variable.expression = parse_expression(0); + } else if (token_nxt.identifier) { + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// [" +// let ignore;function aa(ignore) {} +// ", "stmt_var", "unexpected_a", "ignore", 5] + + warn("unexpected_a", name); + } + enroll(name, "variable", mode_const); + if (token_nxt.id === "=" || mode_const) { + advance("="); + name.dead = false; + name.init = true; + name.expression = parse_expression(0); + } + the_variable.names.push(name); + } else { + +// test_cause: +// ["let 0", "stmt_var", "expected_identifier_a", "0", 5] +// ["var{aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 8] + + return stop("expected_identifier_a"); + } + if (token_nxt.id !== ",") { + break; + } + +// test_cause: +// ["let aa,bb;", "stmt_var", "expected_a_b", ",", 7] + + warn("expected_a_b", token_nxt, ";", ","); + advance(","); + } + +// Warn if variable declarations are unordered. + + if ( + option_dict.beta + && !option_dict.unordered + && !option_dict.variable + && variable_prv + && ( + variable_prv.id + " " + variable_prv.names[0].id + > the_variable.id + " " + the_variable.names[0].id + ) + ) { + +// test_cause: +// ["const bb=0;const aa=0;", "stmt_var", "expected_a_b_before_c_d", "aa", 12] +// ["let bb;let aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8] +// ["var bb;var aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8] + + warn( + "expected_a_b_before_c_d", + the_variable, + the_variable.id, + the_variable.names[0].id, + variable_prv.id, + variable_prv.names[0].id + ); + } + semicolon(); + return the_variable; + } + + function stmt_while() { + const the_while = token_now; + check_not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + +// test_cause: +// ["function aa(){while(0){break;}}", "stmt_while", "weird_loop", "while", 15] + + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; + } + + function stmt_with() { + +// test_cause: +// ["with", "stmt_with", "unexpected_a", "with", 1] + + stop("unexpected_a", token_now); + } + + function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!jslint_rgx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!jslint_rgx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + +// test_cause: +// ["let aa={0:0}", "survey", "expected_identifier_a", "0", 9] + + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property_dict[id] === "number") { + property_dict[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (state.mode_property) { + if (tenure[id] !== true) { + +// test_cause: +// ["/*property aa*/\naa.bb", "survey", "unregistered_property_a", "bb", 4] + + warn("unregistered_property_a", name); + } + } else if ( + !option_dict.nomen + && name.identifier + && jslint_rgx_weird_property.test(id) + ) { + +// test_cause: +// ["aa.$", "survey", "weird_property_a", "$", 4] +// ["aa._", "survey", "weird_property_a", "_", 4] +// ["aa._aa", "survey", "weird_property_a", "_aa", 4] +// ["aa.aaSync", "survey", "weird_property_a", "aaSync", 4] +// ["aa.aa_", "survey", "weird_property_a", "aa_", 4] + + warn("weird_property_a", name); + } + property_dict[id] = 1; + } + return id; + } + +// These functions are used to specify the grammar of our language: + + function symbol(id, bp) { + +// Create a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax_dict[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax_dict[id] = the_symbol; + } + return the_symbol; + } + + function ternary(id1, id2) { + +// Create a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led_infix = function parse_ternary_led(left) { + const the_token = token_now; + let second; + second = parse_expression(20); + advance(id2); + token_now.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, parse_expression(10)]; + if (token_nxt.id !== ")") { + +// test_cause: +// ["0?0:0", "parse_ternary_led", "use_open", "?", 2] + + warn("use_open", the_token); + } + return the_token; + }; + return the_symbol; + } + +// Now we parse JavaScript. +// Begin defining the language. + + assignment("%="); + assignment("&="); + assignment("*="); + assignment("+="); + assignment("-="); + assignment("/="); + assignment("<<="); + assignment("="); + assignment(">>="); + assignment(">>>="); + assignment("^="); + assignment("|="); + constant("(number)", "number"); + constant("(regexp)", "regexp"); + constant("(string)", "string"); + constant("Function", "function", constant_Function); + constant("Infinity", "number", Infinity); + constant("NaN", "number", NaN); + constant("arguments", "object", constant_arguments); + constant("eval", "function", constant_eval); + constant("false", "boolean", false); + constant("ignore", "undefined", constant_ignore); + constant("isFinite", "function", constant_isInfinite); + constant("isNaN", "function", constant_isNaN); + constant("null", "null", null); + constant("this", "object", constant_this); + constant("true", "boolean", true); + constant("undefined", "undefined"); + infix(100, "!="); + infix(100, "!=="); + infix(100, "=="); + infix(100, "==="); + infix(110, "<"); + infix(110, "<="); + infix(110, ">"); + infix(110, ">="); + infix(110, "in"); + infix(110, "instanceof"); + infix(120, "<<"); + infix(120, ">>"); + infix(120, ">>>"); + infix(130, "+"); + infix(130, "-"); + infix(140, "%"); + infix(140, "*"); + infix(140, "/"); + infix(160, "(", infix_lparen); + infix(160, "`", infix_grave); + infix(170, ".", infix_dot); + infix(170, "=>", infix_fart_unwrapped); + infix(170, "?.", infix_option_chain); + infix(170, "[", infix_lbracket); + infix(35, "??"); + infix(40, "||"); + infix(50, "&&"); + infix(70, "|"); + infix(80, "^"); + infix(90, "&"); + infixr(150, "**"); + postassign("++"); + postassign("--"); + preassign("++"); + preassign("--"); + prefix("!!"); + prefix("!"); + prefix("(", prefix_lparen); + prefix("+"); + prefix("-"); + prefix("/=", prefix_assign_divide); + prefix("=>", prefix_fart); + prefix("[", prefix_lbracket); + prefix("`", prefix_tick); + prefix("async", prefix_async); + prefix("await", prefix_await); + prefix("function", prefix_function); + prefix("new", prefix_new); + prefix("typeof"); + prefix("void", prefix_void); + prefix("{", prefix_lbrace); + prefix("~"); + stmt(";", stmt_semicolon); + stmt("async", prefix_async); + stmt("await", prefix_await); + stmt("break", stmt_break); + stmt("const", stmt_var); + stmt("continue", stmt_continue); + stmt("debugger", stmt_debugger); + stmt("delete", stmt_delete); + stmt("do", stmt_do); + stmt("export", stmt_export); + stmt("for", stmt_for); + stmt("function", prefix_function); + stmt("if", stmt_if); + stmt("import", stmt_import); + stmt("let", stmt_var); + stmt("return", stmt_return); + stmt("switch", stmt_switch); + stmt("throw", stmt_throw); + stmt("try", stmt_try); + stmt("var", stmt_var); + stmt("while", stmt_while); + stmt("with", stmt_with); + stmt("{", stmt_lbrace); + symbol(")"); + symbol("*/"); + symbol(","); + symbol(":"); + symbol(";"); + symbol("]"); + symbol("async"); + symbol("await"); + symbol("case"); + symbol("catch"); + symbol("class"); + symbol("default"); + symbol("else"); + symbol("enum"); + symbol("finally"); + symbol("implements"); + symbol("interface"); + symbol("package"); + symbol("private"); + symbol("protected"); + symbol("public"); + symbol("static"); + symbol("super"); + symbol("void"); + symbol("yield"); + symbol("}"); + ternary("?", ":"); + +// Init token_nxt. + + advance(); + +// Parsing of JSON is simple: + + if (state.mode_json) { + state.token_tree = parse_json(); + advance("(end)"); + return; + } + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option_dict.browser) { + if (token_nxt.id === ";") { + advance(";"); + } + +// If we are not in a browser, then the file form of strict pragma may be used. + + } else if (token_nxt.value === "use strict") { + advance("(string)"); + advance(";"); + } + state.token_tree = parse_statements(); + advance("(end)"); + +// Check global functions are ordered. + + check_ordered( + "function", + function_list.map(function ({ + level, + name + }) { + return (level === 1) && name; + }).filter(function (name) { + return option_dict.beta && name && name.id; + }) + ); +} + +function jslint_phase4_walk(state) { + +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). + + let { + artifact, + catch_stack, + function_stack, + global_dict, + is_equal, + is_weird, + option_dict, + syntax_dict, + test_cause, + token_global, + warn + } = state; + let block_stack = []; // The stack of blocks. + let blockage = token_global; // The current block. + let catchage = catch_stack[0]; // The current catch-block. + let functionage = token_global; // The current function. + let postaction; + let postamble; + let posts = empty(); + let preaction; + let preamble; + let pres = empty(); + +// The relational operators. + + let relationop = object_assign_from_list(empty(), [ + "!=", "!==", "<", "<=", "==", "===", ">", ">=" + ], true); + +// Ambulation of the parse tree. + + function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; + } + + function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + task(the_token); + }); + } + } + }; + } + + function init_variable(name) { + let the_variable = lookup(name); + if (!the_variable || the_variable.readonly) { + warn("bad_assignment_a", name); + return; + } + the_variable.init = true; + } + + function lookup(thing) { + let id = thing.id; + let the_variable; + if (thing.arity !== "variable") { + return; + } + +// Look up the variable in the current context. + + the_variable = functionage.context[id] || catchage.context[id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable && the_variable.role === "label") { + +// test_cause: +// ["aa:while(0){aa;}", "lookup", "label_a", "aa", 13] + + warn("label_a", thing); + return the_variable; + } + if (!the_variable) { + function_stack.forEach(function ({ + context + }) { + if (context[id] && context[id].role !== "label") { + the_variable = context[id]; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (!the_variable && global_dict[id] === undefined) { + +// test_cause: +// ["aa", "lookup", "undeclared_a", "aa", 1] +// ["class aa{}", "lookup", "undeclared_a", "aa", 7] +// [" +// let aa=0;try{aa();}catch(bb){bb();}bb(); +// ", "lookup", "undeclared_a", "bb", 36] +// [" +// let aa=0;try{aa();}catch(ignore){bb();} +// ", "lookup", "undeclared_a", "bb", 34] + + warn("undeclared_a", thing); + return; + } + if (!the_variable) { + the_variable = { + dead: false, + id, + init: true, + parent: token_global, + readonly: true, + role: "variable", + used: 0 + }; + token_global.context[id] = the_variable; + } + the_variable.closure = true; + functionage.context[id] = the_variable; + } + if ( + ( + the_variable.calls === undefined + || functionage.name === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + && the_variable.dead + ) { + +// test_cause: +// ["let aa;if(aa){let bb;}bb;", "lookup", "out_of_scope_a", "bb", 23] + + warn("out_of_scope_a", thing); + } + return the_variable; + } + + function post_a(thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + let right; + if (thing.id === "=") { + if (thing.names !== undefined) { + +// test_cause: +// ["if(0){aa=0}", "post_a", "=", "", 0] + + test_cause("="); + +// Probably deadcode. +// if (Array.isArray(thing.names)) { +// thing.names.forEach(init_variable); +// } else { +// init_variable(thing.names); +// } + + jslint_assert( + !Array.isArray(thing.names), + `Expected !Array.isArray(thing.names).` + ); + init_variable(thing.names); + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + +// test_cause: +// ["aa.aa=undefined", "post_a", "expected_a_b", "undefined", 1] + + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.readonly) { + warn("bad_assignment_a", lvalue); + } + } + right = syntax_dict[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + +// test_cause: +// ["aa+=undefined", "post_a", "unexpected_a", "undefined", 5] + + warn("unexpected_a", thing.expression[1]); + } + } + } + + function post_a_pluseq(thing) { + const right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } + } + + function post_b(thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || is_equal(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + +// test_cause: +// ["if(0===0){0}", "post_b", "weird_relation_a", "===", 5] + + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option_dict.convert) { + if (thing.expression[0].value === "") { + +// test_cause: +// ["\"\"+0", "post_b", "expected_a_b", "\"\" +", 3] + + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + +// test_cause: +// ["0+\"\"", "post_b", "expected_a_b", "+ \"\"", 2] + + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + +// test_cause: +// ["aa=window[0]", "post_b", "weird_expression_a", "window[...]", 10] + + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + +// test_cause: +// ["aa=self[0]", "post_b", "weird_expression_a", "self[...]", 8] + + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === "." || thing.id === "?.") { + if (thing.expression.id === "RegExp") { + +// test_cause: +// ["aa=RegExp.aa", "post_b", "weird_expression_a", ".", 10] + + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + +// test_cause: +// ["0- -0", "post_b", "wrap_unary", "-", 4] + + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } + } + + function post_b_and(thing) { + if ( + is_weird(thing.expression[0]) + || is_equal(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + +// test_cause: +// ["aa=(()=>0)&&(()=>0)", "post_b_and", "weird_condition_a", "&&", 11] +// ["aa=(``?``:``)&&(``?``:``)", "post_b_and", "weird_condition_a", "&&", 14] +// ["aa=/./&&/./", "post_b_and", "weird_condition_a", "&&", 7] +// ["aa=0&&0", "post_b_and", "weird_condition_a", "&&", 5] +// ["aa=[]&&[]", "post_b_and", "weird_condition_a", "&&", 6] +// ["aa=`${0}`&&`${0}`", "post_b_and", "weird_condition_a", "&&", 10] +// [" +// aa=function aa(){}&&function aa(){} +// ", "post_b_and", "weird_condition_a", "&&", 19] +// ["aa={}&&{}", "post_b_and", "weird_condition_a", "&&", 6] + + warn("weird_condition_a", thing); + } + } + + function post_b_lbracket(thing) { + if (thing.expression[0].id === "RegExp") { + +// test_cause: +// ["aa=RegExp[0]", "post_b_lbracket", "weird_expression_a", "[", 10] + + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + +// test_cause: +// ["aa[[0]]", "post_b_lbracket", "weird_expression_a", "[", 4] + + warn("weird_expression_a", thing.expression[1]); + } + } + + function post_b_lparen(thing) { + let arg; + let array; + let cack; + let left = thing.expression[0]; + let new_date; + let paren; + let the_new; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + +// test_cause: +// ["aa=function(){}()", "post_b_lparen", "wrap_immediate", "(", 16] + + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "BigInt" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + +// test_cause: +// ["new BigInt()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Boolean()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Number()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new String()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Symbol()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new aa()", "post_b_lparen", "unexpected_a", "new", 1] + + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option_dict.eval) { + +// test_cause: +// ["new Function()", "post_b_lparen", "unexpected_a", "new Function", 5] + + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + +// test_cause: +// ["new Array()", "post_b_lparen", "expected_a_b", "new Array", 5] + + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + +// test_cause: +// ["new Object()", "post_b_lparen", "expected_a_b", "new Object", 5] + + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "BigInt" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + +// test_cause: +// ["let Aa=Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8] + + warn("expected_a_before_b", left, "new", artifact(left)); + } + } + } else if (left.id === ".") { + cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + +// test_cause: +// ["new Date.UTC()", "post_b_lparen", "cack", "", 0] + + test_cause("cack"); + cack = !cack; + } + if (jslint_rgx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + +// test_cause: +// ["new Date.UTC()", "post_b_lparen", "unexpected_a", "new", 1] + + warn("unexpected_a", the_new); + } else { + +// test_cause: +// ["let Aa=Aa.Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8] + + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + paren = left.expression; + if (paren.id === "(") { + array = paren.expression; + if (array.length === 1) { + new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + +// test_cause: +// [" +// new Date().getTime() +// ", "post_b_lparen", "expected_a_b", "new Date().getTime()", 1] + + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } + } + + function post_b_or(thing) { + if ( + is_weird(thing.expression[0]) + || is_equal(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + +// test_cause: +// ["aa=0||0", "post_b_or", "weird_condition_a", "||", 5] + + warn("weird_condition_a", thing); + } + } + + function post_s_export(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== token_global) { + +// test_cause: +// [" +// if(0){import aa from "aa";} +// ", "post_s_export", "misplaced_a", "import", 7] + + warn("misplaced_a", the_thing); + } + } + + function post_s_for(thing) { + +// Recurse walk_statement(). + + walk_statement(thing.inc); + } + + function post_s_function(thing) { + delete functionage.async; + delete functionage.finally; + delete functionage.loop; + delete functionage.statement_prv; + delete functionage.switch; + delete functionage.try; + functionage = function_stack.pop(); + if (thing.wrapped) { + +// test_cause: +// ["aa=(function(){})", "post_s_function", "unexpected_parens", "function", 5] + + warn("unexpected_parens", thing); + } + return post_s_lbrace(); + } + + function post_s_import(the_thing) { + const name = the_thing.name; + if (name) { + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return post_s_export(the_thing); + } + } + + function post_s_lbrace() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); + } + + function post_s_try(thing) { + if (thing.catch) { + if (thing.catch.name) { + Object.assign(catchage.context[thing.catch.name.id], { + dead: false, + init: true + }); + } + +// Recurse walk_statement(). + + walk_statement(thing.catch.block); + +// Restore previous catch-scope after catch-block. + + catchage = catch_stack.pop(); + } + } + + function post_s_var(thing) { + thing.names.forEach(function (name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + +// Probably deadcode. +// if (name.id === "{" || name.id === "[") { +// name.names.forEach(subactivate); +// } else { +// name.init = true; +// } + + jslint_assert( + !(name.id === "{" || name.id === "["), + `Expected !(name.id === "{" || name.id === "[").` + ); + name.init = true; + } + blockage.live.push(name); + }); + } + + function post_t(thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || is_equal(thing.expression[1], thing.expression[2]) + ) { + +// test_cause: +// ["let aa=(aa?`${0}`:`${0}`);", "post_t", "unexpected_a", "?", 11] +// ["let aa=(aa?`0`:`0`);", "post_t", "unexpected_a", "?", 11] + + warn("unexpected_a", thing); + } else if (is_equal(thing.expression[0], thing.expression[1])) { + +// test_cause: +// ["aa?aa:0", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "||", "?"); + } else if (is_equal(thing.expression[0], thing.expression[2])) { + +// test_cause: +// ["aa?0:aa", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + +// test_cause: +// ["aa?true:false", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + +// test_cause: +// ["aa?false:true", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + +// test_cause: +// ["(aa&&!aa?0:1)", "post_t", "wrap_condition", "&&", 4] + + warn("wrap_condition", thing.expression[0]); + } + } + + function post_u(thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option_dict.convert) { + +// test_cause: +// ["!!0", "post_u", "expected_a_b", "!!", 1] + + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } + } + + function post_u_plus(thing) { + const right = thing.expression; + if (!option_dict.convert) { + +// test_cause: +// ["aa=+0", "post_u_plus", "expected_a_b", "+", 4] + + warn("expected_a_b", thing, "Number(...)", "+"); + } + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } + } + + function pre_a_bitwise(thing) { + +// These are the bitwise operators. + + switch (!option_dict.bitwise && thing.id) { + case "&": + case "&=": + case "<<": + case "<<=": + case ">>": + case ">>=": + case ">>>": + case ">>>=": + case "^": + case "^=": + case "|": + case "|=": + case "~": + +// test_cause: +// ["0&0", "pre_a_bitwise", "unexpected_a", "&", 2] +// ["0&=0", "pre_a_bitwise", "unexpected_a", "&=", 2] +// ["0<<0", "pre_a_bitwise", "unexpected_a", "<<", 2] +// ["0<<=0", "pre_a_bitwise", "unexpected_a", "<<=", 2] +// ["0>>0", "pre_a_bitwise", "unexpected_a", ">>", 2] +// ["0>>=0", "pre_a_bitwise", "unexpected_a", ">>=", 2] +// ["0>>>0", "pre_a_bitwise", "unexpected_a", ">>>", 2] +// ["0>>>=0", "pre_a_bitwise", "unexpected_a", ">>>=", 2] +// ["0^0", "pre_a_bitwise", "unexpected_a", "^", 2] +// ["0^=0", "pre_a_bitwise", "unexpected_a", "^=", 2] +// ["0|0", "pre_a_bitwise", "unexpected_a", "|", 2] +// ["0|=0", "pre_a_bitwise", "unexpected_a", "|=", 2] +// ["~0", "pre_a_bitwise", "unexpected_a", "~", 1] + + warn("unexpected_a", thing); + break; + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + +// test_cause: +// ["0<0<0", "pre_a_bitwise", "unexpected_a", "<", 4] + + warn("unexpected_a", thing); + } + } + + function pre_b(thing) { + let left; + let right; + let value; + if (relationop[thing.id] === true) { + left = thing.expression[0]; + right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + +// test_cause: +// ["NaN===NaN", "pre_b", "number_isNaN", "===", 4] + + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + +// test_cause: +// ["typeof 0===0", "pre_b", "expected_string_a", "0", 12] + + warn("expected_string_a", right); + } + } else { + value = right.value; + if (value === "null" || value === "undefined") { + +// test_cause: +// [" +// typeof aa==="undefined" +// ", "pre_b", "unexpected_typeof_a", "undefined", 13] + + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "bigint" + && value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + +// test_cause: +// ["typeof 0===\"aa\"", "pre_b", "expected_type_string_a", "aa", 12] + + warn("expected_type_string_a", right, value); + } + } + } + } + } + + function pre_b_eqeq(thing) { + +// test_cause: +// ["0==0", "pre_b_eqeq", "expected_a_b", "==", 2] + + warn("expected_a_b", thing, "===", "=="); + } + + function pre_b_in(thing) { + +// test_cause: +// ["aa in aa", "pre_b_in", "infix_in", "in", 4] + + warn("infix_in", thing); + } + + function pre_b_instanceof(thing) { + +// test_cause: +// ["0 instanceof 0", "pre_b_instanceof", "unexpected_a", "instanceof", 3] + + warn("unexpected_a", thing); + } + + function pre_b_lparen(thing) { + const left = thing.expression[0]; + let left_variable; + let parent; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + parent = functionage.name.parent; + if (parent) { + left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + +// Probably deadcode. +// && left_variable.dead + + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } + } + + function pre_b_noteq(thing) { + +// test_cause: +// ["0!=0", "pre_b_noteq", "expected_a_b", "!=", 2] + + warn("expected_a_b", thing, "!==", "!="); + } + + function pre_b_or(thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + +// test_cause: +// ["0&&0||0", "pre_b_or", "and", "&&", 2] + + warn("and", thang); + } + }); + } + + function pre_s_for(thing) { + let the_variable; + if (thing.name !== undefined) { + thing.name.dead = false; + the_variable = lookup(thing.name); + if (the_variable !== undefined) { + if (the_variable.init && the_variable.readonly) { + +// test_cause: +// ["const aa=0;for(aa in aa){}", "pre_s_for", "bad_assignment_a", "aa", 16] + + warn("bad_assignment_a", thing.name); + } + the_variable.init = true; + } + } + +// Recurse walk_statement(). + + walk_statement(thing.initial); + } + + function pre_s_function(thing) { + +// test_cause: +// ["()=>0", "pre_s_function", "", "", 0] +// ["(function (){}())", "pre_s_function", "", "", 0] +// ["function aa(){}", "pre_s_function", "", "", 0] + + test_cause(""); + if (thing.arity === "statement" && blockage.body !== true) { + +// test_cause: +// ["if(0){function aa(){}\n}", "pre_s_function", "unexpected_a", "function", 7] + + warn("unexpected_a", thing); + } + function_stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + +// test_cause: +// [" +// /*jslint getset*/ +// aa={get aa(aa){}} +// ", "pre_s_function", "bad_get", "function", 9] + + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + +// test_cause: +// [" +// /*jslint getset*/ +// aa={set aa(){}} +// ", "pre_s_function", "bad_set", "function", 9] + + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); + } + + function pre_s_lbrace(thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; + } + + function pre_try(thing) { + if (thing.catch !== undefined) { + +// Create new catch-scope for catch-parameter. + + catch_stack.push(catchage); + catchage = thing.catch; + } + } + + function pre_v(thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } + } + + function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); + } + + function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + +// test_cause: +// ["(function(){}())", "walk_expression", "isArray", "", 0] +// ["0&&0", "walk_expression", "isArray", "", 0] + + test_cause("isArray"); + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + +// PR-414 - Bugfix - fix fart-body not being walked. + + if (thing.id === "function" || thing.id === "=>") { + +// test_cause: +// ["aa=()=>0", "walk_expression", "function", "=>", 0] +// ["aa=function(){}", "walk_expression", "function", "function", 0] + + test_cause("function", thing.id); + +// Recurse walk_statement(). + + walk_statement(thing.block); + } + if ( + thing.arity === "preassign" || thing.arity === "postassign" + ) { + +// test_cause: +// ["aa=++aa", "walk_expression", "unexpected_a", "++", 4] +// ["aa=--aa", "walk_expression", "unexpected_a", "--", 4] + + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + +// test_cause: +// ["aa[aa=0]", "walk_expression", "unexpected_statement_a", "=", 6] + + warn("unexpected_statement_a", thing); + } + +// test_cause: +// ["aa=0", "walk_expression", "default", "", 0] + + test_cause("default"); + postamble(thing); + } + } + } + + function walk_statement(thing) { + if (!thing) { + return; + } + if (Array.isArray(thing)) { + +// test_cause: +// ["+[]", "walk_statement", "isArray", "", 0] + + test_cause("isArray"); + +// Recurse walk_statement(). + + thing.forEach(walk_statement); + return; + } + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + +// test_cause: +// ["0&&0", "walk_statement", "unexpected_expression_a", "&&", 2] + + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + && thing.id !== "import" + ) { + +// test_cause: +// ["!0", "walk_statement", "unexpected_expression_a", "!", 1] +// ["+[]", "walk_statement", "unexpected_expression_a", "+", 1] +// ["+new aa()", "walk_statement", "unexpected_expression_a", "+", 1] +// ["0", "walk_statement", "unexpected_expression_a", "0", 1] +// ["typeof 0", "walk_statement", "unexpected_expression_a", "typeof", 1] + + warn("unexpected_expression_a", thing); + } + +// Recurse walk_statement(). + + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + + postaction = action(posts); + postamble = amble(posts); + preaction = action(pres); + preamble = amble(pres); + postaction("assignment", "+=", post_a_pluseq); + postaction("assignment", post_a); + postaction("binary", "&&", post_b_and); + postaction("binary", "(", post_b_lparen); + postaction("binary", "=>", post_s_function); + postaction("binary", "[", post_b_lbracket); + postaction("binary", "||", post_b_or); + postaction("binary", post_b); + postaction("statement", "const", post_s_var); + postaction("statement", "export", post_s_export); + postaction("statement", "for", post_s_for); + postaction("statement", "function", post_s_function); + postaction("statement", "import", post_s_import); + postaction("statement", "let", post_s_var); + postaction("statement", "try", post_s_try); + postaction("statement", "var", post_s_var); + postaction("statement", "{", post_s_lbrace); + postaction("ternary", post_t); + postaction("unary", "+", post_u_plus); + postaction("unary", "function", post_s_function); + postaction("unary", post_u); + preaction("assignment", pre_a_bitwise); + preaction("binary", "!=", pre_b_noteq); + preaction("binary", "(", pre_b_lparen); + preaction("binary", "==", pre_b_eqeq); + preaction("binary", "=>", pre_s_function); + preaction("binary", "in", pre_b_in); + preaction("binary", "instanceof", pre_b_instanceof); + preaction("binary", "||", pre_b_or); + preaction("binary", pre_b); + preaction("binary", pre_a_bitwise); + preaction("statement", "for", pre_s_for); + preaction("statement", "function", pre_s_function); + preaction("statement", "try", pre_try); + preaction("statement", "{", pre_s_lbrace); + preaction("unary", "function", pre_s_function); + preaction("unary", "~", pre_a_bitwise); + preaction("variable", pre_v); + + walk_statement(state.token_tree); +} + +function jslint_phase5_whitage(state) { + +// PHASE 5. Check whitespace between tokens in . + + let { + artifact, + catch_list, + function_list, + function_stack, + option_dict, + test_cause, + token_global, + token_list, + warn + } = state; + let closer = "(end)"; + let free = false; + +// free = false + +// cause: +// "()=>0" +// "aa()" +// "aa(0,0)" +// "function(){}" + +// free = true + +// cause: +// "(0)" +// "(aa)" +// "aa(0)" +// "do{}while()" +// "for(){}" +// "if(){}" +// "switch(){}" +// "while(){}" + + let left = token_global; + let margin = 0; + let mode_indent = ( + +// PR-330 - Allow 2-space indent. + + option_dict.indent2 + ? 2 + : 4 + ); + let nr_comments_skipped = 0; + let open = true; + let opening = true; + let right; + +// This is the set of infix operators that require a space on each side. + + let spaceop = object_assign_from_list(empty(), [ + "!=", "!==", "%", "%=", "&", "&&", "&=", "*", "*=", "+=", "-=", "/", + "/=", "<", "<<", "<<=", "<=", "=", "==", "===", "=>", ">", ">=", ">>", + ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" + ], true); + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + const name = the_function.context[id]; + if (id !== "ignore" && name.parent === the_function) { + +// test_cause: +// ["function aa(aa) {return aa;}", "delve", "id", "", 0] + + test_cause("id"); + if ( + name.used === 0 + +// Probably deadcode. +// && ( +// name.role !== "function" +// || name.parent.arity !== "unary" +// ) + + && jslint_assert( + name.role !== "function", + `Expected name.role !== "function".` + ) + ) { + +// test_cause: +// ["/*jslint node*/\nlet aa;", "delve", "unused_a", "aa", 5] +// ["function aa(aa){return;}", "delve", "unused_a", "aa", 13] +// ["let aa=0;try{aa();}catch(bb){aa();}", "delve", "unused_a", "bb", 26] + + warn("unused_a", name); + } else if (!name.init) { + +// test_cause: +// ["/*jslint node*/\nlet aa;aa();", "delve", "uninitialized_a", "aa", 5] + + warn("uninitialized_a", name); + } + } + }); + } + + function expected_at(at) { + +// Probably deadcode. +// if (right === undefined) { +// right = token_nxt; +// } + + jslint_assert( + !(right === undefined), + `Expected !(right === undefined).` + ); + warn( + "expected_a_at_b_c", + right, + artifact(right), + +// Fudge column numbers in warning message. + + at + jslint_fudge, + right.from + jslint_fudge + ); + } + + function no_space() { + if (left.line === right.line) { + +// from: +// if (left.line === right.line) { +// no_space(); +// } else { + + if (left.thru !== right.from && nr_comments_skipped === 0) { + +// test_cause: +// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14] + + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + +// from: +// } else if ( +// right.arity === "binary" +// && right.id === "(" +// && free +// ) { +// no_space(); +// } else if ( + +// Probably deadcode. +// if (open) { +// const at = ( +// free +// ? margin +// : margin + 8 +// ); +// if (right.from < at) { +// expected_at(at); +// } +// } else { +// if (right.from !== margin + 8) { +// expected_at(margin + 8); +// } +// } + + jslint_assert(open, `Expected open.`); + jslint_assert(free, `Expected free.`); + if (right.from < margin) { + +// test_cause: +// ["let aa = aa(\naa\n()\n);", "expected_at", "expected_a_at_b_c", "5", 1] + + expected_at(margin); + } + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line || !open) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (right.from !== margin) { + expected_at(margin); + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn("expected_space_a_b", right, artifact(left), artifact(right)); + } + } + + function pop() { + const previous = function_stack.pop(); + closer = previous.closer; + free = previous.free; + margin = previous.margin; + open = previous.open; + opening = previous.opening; + } + + function push() { + function_stack.push({ + closer, + free, + margin, + open, + opening + }); + } + +// uninitialized_and_unused(); +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (state.mode_module === true || option_dict.node) { + delve(token_global); + } + catch_list.forEach(delve); + function_list.forEach(delve); + + if (option_dict.white) { + return; + } + +// whitage(); +// Go through the token list, looking at usage of whitespace. + + token_list.forEach(function whitage(the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + +// The open and close pairs. + + switch (left.id) { + case "${": + case "(": + case "[": + case "{": + +// test_cause: +// ["let aa=[];", "whitage", "opener", "", 0] +// ["let aa=`${0}`;", "whitage", "opener", "", 0] +// ["let aa=aa();", "whitage", "opener", "", 0] +// ["let aa={};", "whitage", "opener", "", 0] + + test_cause("opener"); + +// Probably deadcode. +// case "${}": + + jslint_assert( + !(left.id + right.id === "${}"), + "Expected !(left.id + right.id === \"${}\")." + ); + switch (left.id + right.id) { + case "()": + case "[]": + case "{}": + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like '{]') have already been detected. + +// test_cause: +// ["let aa=[];", "whitage", "opener_closer", "", 0] +// ["let aa=aa();", "whitage", "opener_closer", "", 0] +// ["let aa={};", "whitage", "opener_closer", "", 0] + + test_cause("opener_closer"); + if (left.line === right.line) { + +// test_cause: +// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14] + + no_space(); + } else { + +// test_cause: +// ["let aa = aa(\n );", "expected_at", "expected_a_at_b_c", "1", 2] + + at_margin(0); + } + break; + default: + +// test_cause: +// ["let aa=(0);", "whitage", "opener_operand", "", 0] +// ["let aa=[0];", "whitage", "opener_operand", "", 0] +// ["let aa=`${0}`;", "whitage", "opener_operand", "", 0] +// ["let aa=aa(0);", "whitage", "opener_operand", "", 0] +// ["let aa={aa:0};", "whitage", "opener_operand", "", 0] + + test_cause("opener_operand"); + opening = left.open || (left.line !== right.line); + push(); + switch (left.id) { + case "${": + closer = "}"; + break; + case "(": + closer = ")"; + break; + case "[": + closer = "]"; + break; + case "{": + closer = "}"; + break; + } + if (opening) { + +// test_cause: +// ["function aa(){\nreturn;\n}", "whitage", "opening", "", 0] +// ["let aa=(\n0\n);", "whitage", "opening", "", 0] +// ["let aa=[\n0\n];", "whitage", "opening", "", 0] +// ["let aa=`${\n0\n}`;", "whitage", "opening", "", 0] +// ["let aa={\naa:0\n};", "whitage", "opening", "", 0] + + test_cause("opening"); + free = closer === ")" && left.free; + open = true; + margin += mode_indent; + if (right.role === "label") { + if (right.from !== 0) { + +// test_cause: +// [" +// function aa() { +// bb: +// while (aa) { +// if (aa) { +// break bb; +// } +// } +// } +// ", "expected_at", "expected_a_at_b_c", "1", 2] + + expected_at(0); + } + } else if (right.switch) { + at_margin(-mode_indent); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + +// test_cause: +// [" +// function aa() {bb: +// while (aa) { +// aa(); +// } +// } +// ", "whitage", "expected_line_break_a_b", "bb", 16] + + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + +// test_cause: +// ["let aa=(0);", "whitage", "not_free", "", 0] +// ["let aa=[0];", "whitage", "not_free", "", 0] +// ["let aa=`${0}`;", "whitage", "not_free", "", 0] +// ["let aa={aa:0};", "whitage", "not_free", "", 0] + + test_cause("not_free"); + free = false; + open = false; + +// test_cause: +// ["let aa = ( 0 );", "no_space_only", "unexpected_space_a_b", "0", 12] + + no_space_only(); + } + } + break; + default: + if (right.statement === true) { + if (left.id === "else") { + +// test_cause: +// [" +// let aa = 0; +// if (aa) { +// aa(); +// } else if (aa) { +// aa(); +// } +// ", "one_space_only", "expected_space_a_b", "if", 9] + + one_space_only(); + } else { + +// test_cause: +// [" let aa = 0;", "expected_at", "expected_a_at_b_c", "1", 2] + + at_margin(0); + open = false; + } + +// If right is a closer, then pop the previous state. + + } else if (right.id === closer) { + pop(); + if (opening && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-mode_indent); + } else if (right.role === "label") { + if (right.from !== 0) { + +// test_cause: +// [" +// function aa() { +// aa();cc: +// while (aa) { +// if (aa) { +// break cc; +// } +// } +// } +// ", "expected_at", "expected_a_at_b_c", "1", 10] + + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + +// test_cause: +// ["let {aa,bb} = 0;", "one_space", "expected_space_a_b", "bb", 9] + + one_space(); + } else { + +// test_cause: +// [" +// function aa() { +// aa( +// 0,0 +// ); +// } +// ", "expected_at", "expected_a_at_b_c", "9", 11] + + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + +// test_cause: +// [" +// let aa = ( +// aa +// ? 0 +// : 1 +// ); +// ", "expected_at", "expected_a_at_b_c", "5", 1] + + at_margin(0); + } else { + +// test_cause: +// ["let aa = (aa ? 0 : 1);", "whitage", "use_open", "?", 14] + + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + +// test_cause: +// ["let aa = aa(\naa ()\n);", "no_space", "unexpected_space_a_b", "(", 4] + + no_space(); + } else if ( + left.id === "." + || left.id === "?." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + || (right.id === "." || right.id === "?.") + ) { + +// test_cause: +// ["let aa = 0 ;", "no_space_only", "unexpected_space_a_b", ";", 12] +// ["let aa = aa ?.aa;", "no_space_only", "unexpected_space_a_b", "?.", 13] + + no_space_only(); + } else if (left.id === ";") { + +// test_cause: +// [" +// /*jslint for*/ +// function aa() { +// for ( +// aa(); +// aa; +// aa() +// ) { +// aa(); +// } +// } +// ", "expected_at", "expected_a_at_b_c", "9", 1] + + if (open) { + at_margin(0); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || left.id === "await" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + +// test_cause: +// [" +// function aa() { +// do { +// aa(); +// } while(aa()); +// } +// ", "one_space_only", "expected_space_a_b", "(", 12] + + one_space_only(); + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || left.id === "async" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + +// test_cause: +// ["let aa=0;", "one_space", "expected_space_a_b", "0", 8] +// ["let aa={\naa:\n0\n};", "expected_at", "expected_a_at_b_c", "5", 1] + + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +function jslint_report({ + exports, + froms, + functions, + global, + json, + module, + property, + stop, + warnings +}) { + +// This function will create human-readable, html-report +// for warnings, properties, and functions from jslint-result-object. +// +// Example usage: +// let result = jslint("console.log('hello world')"); +// let html = jslint_report(result); + + let html = ""; + let length_80 = 1111; + + function address(line = 1, column = 1) { + +// This function will create HTML address element from and + + return `
    ${Number(line)}: ${Number(column)}
    `; + + } + + function detail(title, list) { + return ( + (Array.isArray(list) && list.length > 0) + ? ( + +// Google Lighthouse Accessibility -
    's do not contain only properly-ordered +//
    and
    groups, + + + + + + + + + + + + +

    CodeMirror: JSLint Demo

    +

    +This demo will auto-lint the code below, and auto-generate a report as you type. +

    + + + + + + + +
    + + + + + +``` +3. Live example at https://www.jslint.com/jslint_wrapper_codemirror.html + +[![screenshot](https://jslint-org.github.io/jslint/branch-beta/.artifact/screenshot_browser__2fjslint_2fbranch-beta_2fjslint_wrapper_codemirror.html.png)](https://jslint-org.github.io/jslint/jslint_wrapper_codemirror.html) + + +

    +# Quickstart JSLint in Vim +1. Download and save [`jslint.mjs`](https://www.jslint.com/jslint.mjs), [`jslint_wrapper_vim.vim`](https://www.jslint.com/jslint_wrapper_vim.vim) to directory `~/.vim/` +2. Add vim-command `:source ~/.vim/jslint_wrapper_vim.vim` to file `~/.vimrc` + - If above files were saved to custom-directory, then use that directory instead, e.g.: + - save [`jslint.mjs`](https://www.jslint.com/jslint.mjs), [`jslint_wrapper_vim.vim`](https://www.jslint.com/jslint_wrapper_vim.vim) to directory `~/vimfiles/` + - vim-command `:source ~/vimfiles/jslint_wrapper_vim.vim` +3. Vim can now jslint files (via nodejs): + - with vim-command `:SaveAndJslint` + - with vim-key-combo ` ` +- screenshot + +[![screenshot](asset_image_jslint_wrapper_vim.png)](https://www.jslint.com/jslint_wrapper_vim.vim) + + +

    +# Quickstart JSLint in VSCode +1. In VSCode, search and install extension [`vscode-jslint`](https://marketplace.visualstudio.com/items?itemName=jslint.vscode-jslint) +2. In VSCode, while editing a javascript file: + - right-click context-menu and select `[JSLint - Lint File]` + - or use key-binding `[Ctrl + Shift + J], [L]` + - or use key-binding `[ Cmd + Shift + J], [L]` for Mac +- screenshot + +[![screenshot](https://jslint-org.github.io/jslint/asset_image_jslint_wrapper_vscode.png)](https://marketplace.visualstudio.com/items?itemName=jslint.vscode-jslint) + + +

    +# Documentation + + +- [jslint.mjs](jslint.mjs) contains the jslint function. It parses and analyzes a source file, returning an object with information about the file. It can also take an object that sets options. + +- [index.html](index.html) runs the jslint.mjs function in a web page. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[https://www.crockford.com/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. + + +

    +### API Doc +- https://www.jslint.com/apidoc.html + +[![screenshot](https://jslint-org.github.io/jslint/branch-beta/.artifact/screenshot_browser__2f.artifact_2fapidoc.html.png)](https://www.jslint.com/apidoc.html) + + +

    +### Directive `/*jslint*/` + +
    + +##### `/*jslint beta*/` + +```js +/*jslint beta*/ +// Enable experimental warnings. +// Warn if global variables are redefined. +// Warn if const / let statements are not declared at top of function or +// script, similar to var statements. +// Warn if const / let / var statements are not declared in ascii-order. +// Warn if named-functions are not declared in ascii-order. +// Warn if cases in switch-statements are not in ascii-order. +``` + +
    + +##### `/*jslint bitwise*/` + +```js +/*jslint bitwise*/ +// Allow bitwise operator. + +let foo = 0 | 1; +``` + +
    + +##### `/*jslint browser*/` + +```js +/*jslint browser*/ +// Assume browser environment. + +localStorage.getItem("foo"); +``` + +
    + +##### `/*jslint convert*/` + +```js +/*jslint convert*/ +// Allow conversion operator. + +let foo = new Date() + ""; +let bar = !!0; +``` + +
    + +##### `/*jslint couch*/` + +```js +/*jslint couch*/ +// Assume CouchDb environment. + +registerType("text-json", "text/json"); +``` + +
    + +##### `/*jslint devel*/` + +```js +/*jslint devel*/ +// Allow console.log() and friends. + +console.log("hello"); +``` + +
    + +##### `/*jslint eval*/` + +```js +/*jslint eval*/ +// Allow eval(). + +eval("1"); +``` + +
    + +##### `/*jslint fart*/` + +```js +/*jslint fart*/ +// Allow complex fat-arrow. + +let foo = async ({bar, baz}) => { + return await bar(baz); +}; +``` + +
    + +##### `/*jslint for*/` + +```js +/*jslint for*/ +// Allow for-loop. + +function foo() { + let ii; + for (ii = 0; ii < 10; ii += 1) { + foo(); + } +} +``` + +
    + +##### `/*jslint getset*/` + +```js +/*jslint getset, this, devel*/ +// Allow get() and set(). + +let foo = { + bar: 0, + get getBar() { + return this.bar; + }, + set setBar(value) { + this.bar = value; + } +}; +console.log(foo.getBar); // 0 +foo.setBar = 1; +console.log(foo.getBar); // 1 +``` + +
    + +##### `/*jslint indent2*/` + +```js +/*jslint indent2*/ +// Use 2-space indent. + +function foo() { + return; +} +``` + +
    + +##### `/*jslint long*/` + +```js +/*jslint long*/ +// Allow long lines. + +let foo = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; +``` + +
    + +##### `/*jslint node*/` + +```js +/*jslint node*/ +// Assume Node.js environment. + +require("fs"); +``` + +
    + +##### `/*jslint nomen*/` + +```js +/*jslint nomen*/ +// Allow weird property name. + +let foo = {}; +foo._bar = 1; +``` + +
    + +##### `/*jslint single*/` + +```js +/*jslint single*/ +// Allow single-quote strings. + +let foo = ''; +``` + +
    + +##### `/*jslint subscript*/` + +```js +/*jslint subscript*/ +// Allow identifiers in subscript-notation. + +let foo = {}; +foo["bar"] = 1; +``` + +
    + +##### `/*jslint this*/` + +```js +/*jslint this*/ +// Allow 'this'. + +function foo() { + return this; +} +``` + +
    + +##### `/*jslint trace*/` + +```js +/*jslint trace*/ +// Include jslint stack-trace in warnings. + +console.log('hello world'); +/* +1. Undeclared 'console'. +console.log('hello world'); +Error + at warn_at (...) + at warn (...) + at lookup (...) + at pre_v (...) + at jslint.mjs +2. Use double quotes, not single quotes. +console.log(...); +Error + at warn_at (...) + at lex_string (...) + at lex_token (...) + at jslint_phase2_lex (...) + at Function.jslint (...) + at jslint.mjs +*/ +``` + +
    + +##### `/*jslint unordered*/` + +```js +/*jslint unordered*/ +// Allow unordered cases, params, properties, variables, and exports. + +let foo = {bb: 1, aa: 0}; + +function bar({ + bb = 1, + aa = 0 +}) { + return aa + bb; +} + +export { + foo, + bar +}; +``` + +
    + +##### `/*jslint white*/` + +```js +/*jslint white*/ +// Allow messy whitespace. + +let foo = 1; let bar = 2; +``` + + +

    +### Directive `/*global*/` + +```js +/*global foo, bar*/ +// Declare global variables foo, bar. + +foo(); +bar(); +``` + + +

    +### Directive `/*property*/` + +```js +/*property foo, bar*/ +// Restrict property-access to only .foo, .bar. + +let aa = {bar: 1, foo: 2}; +``` + + +

    +### Directive `/*jslint-disable*/.../*jslint-enable*/` + +```js +/*jslint-disable*/ + +JSLint will ignore and treat this region as blank-lines. +Syntax error. + +/*jslint-enable*/ +``` + + +

    +### Directive `//jslint-ignore-line` + +```js +// JSLint will ignore non-fatal warnings at given line. + +eval("1"); //jslint-ignore-line +``` + + +

    +# Package Listing +![screenshot_package_listing.svg](https://jslint-org.github.io/jslint/branch-beta/.artifact/screenshot_package_listing.svg) + + +

    +# Changelog +- [Full CHANGELOG.md](CHANGELOG.md) + +![screenshot_changelog.svg](https://jslint-org.github.io/jslint/branch-beta/.artifact/screenshot_changelog.svg) + + +

    +# License +- JSLint is under [Unlicense License](LICENSE). +- CodeMirror editor is under [MIT License](https://github.com/codemirror/codemirror5/blob/d0e3b2e727c41aa4fd89fbad0adfb3815339174c/LICENSE). +- Function `v8CoverageListMerge` is derived from [MIT Licensed v8-coverage](https://github.com/demurgos/v8-coverage/blob/73446087dc38f61b09832c9867122a23f8577099/ts/LICENSE.md). + + +

    +# Devops Instruction + + +

    +### pull-request merge +- find highest issue-number at https://github.com/jslint-org/jslint/issues/, https://github.com/jslint-org/jslint/pulls/, and add +1 to it for PR-xxx +- `shGitPullrequest beta beta` + - verify ci-success for origin-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- `git push upstream alpha -f` + - verify ci-success for upstream-branch-alpha + - https://github.com/jslint-org/jslint/actions +- goto https://github.com/jslint-org/jslint/compare/beta...kaizhu256:jslint:branch-p2024.11.24 +- click `Create pull request` +- input `Add your description here...` with: +``` +Fixes #xxx. +- + +This PR will ... + +This PR will additionally: +- +... + + +``` +- verify `commit into jslint-org:beta` +- click `Create pull request` + - verify ci-success for pull-request + - https://github.com/jslint-org/jslint/actions/workflows/on_pull_request.yml +- wait awhile before continuing ... +- click `Rebase and merge` + - verify ci-success for upstream-branch-beta + - https://github.com/jslint-org/jslint/actions +- `shGitPullrequestCleanup` + - verify ci-success for origin-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- `git push upstream alpha -f` + - verify ci-success for upstream-branch-alpha + - https://github.com/jslint-org/jslint/actions +- click `Delete branch` + + +

    +### branch-master commit +- `shGitPullrequest master beta` + - verify ci-success for origin-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- `git push upstream alpha -f` + - verify ci-success for upstream-branch-alpha + - https://github.com/jslint-org/jslint/actions +- goto https://github.com/jslint-org/jslint/compare/beta...kaizhu256:jslint:branch-v2024.11.24 +- click `Create pull request` +- input `Add a title` with: `# v20yy.mm.dd` +- input `Add a description` with: +``` +- +- +``` +- verify `commit into jslint-org:beta` +- click `Create pull request` + - verify ci-success for pull-request + - https://github.com/jslint-org/jslint/actions/workflows/on_pull_request.yml +- wait awhile before continuing ... +- click `Rebase and merge` + - verify ci-success for upstream-branch-beta + - https://github.com/jslint-org/jslint/actions +- `shGitPullrequestCleanup` + - verify ci-success for origin-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- `git push upstream alpha -f` + - verify ci-success for upstream-branch-alpha + - https://github.com/jslint-org/jslint/actions +- click `Delete branch` +- `git push origin beta:master` + - verify ci-success for origin-branch-master + - https://github.com/kaizhu256/jslint/actions +- `git push upstream beta:master` + - verify ci-success for upstream-branch-master + - https://github.com/jslint-org/jslint/actions + + +

    +### branch-master publish +- `git push upstream beta:master` + - verify ci-success for upstream-branch-master + - https://github.com/jslint-org/jslint/actions +- goto https://github.com/jslint-org/jslint/releases/new +- input `Choose a tag` with: `v20yy.mm.dd` +- click `Create new tag: v20yy.mm.dd on publish` + - verify correct-year `20yy` +- select `Target: master` +- select `Previous tag:auto` +- input `Release title` with: `v20yy.mm.dd - ` +- input `Describe this release` with: +``` +- +- +``` +- click `Generate release notes` +- click `Set as the latest release` +- click `Preview` and review +- click `Publish release` + - verify ci-success for upstream-branch-publish + - https://github.com/jslint-org/jslint/actions + - verify email-notification `Successfully published @jslint-org/jslint@20yy.mm.dd` + + +

    +### vscode-jslint publish +- goto https://github.com/jslint-org/jslint/tree/gh-pages/branch-beta/.artifact/jslint_wrapper_vscode +- click `vscode-jslint-20yy.mm.dd.vsix` +- click `Raw` to download +- goto https://marketplace.visualstudio.com/manage/publishers/jslint +- right-click `Update` +- upload downloaded file `vscode-jslint-20yy.mm.dd.vsix` +- click 'Upload' +- verify email-notification `[Succeeded] Extension publish on Visual Studio Marketplace - vscode-jslint` + + + diff --git a/adsafe.js b/adsafe.js new file mode 100644 index 000000000..d751bb5d1 --- /dev/null +++ b/adsafe.js @@ -0,0 +1,2060 @@ +// adsafe.js +// 2017-06-12 + +// Public Domain. + +// NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. +// SUBJECT TO CHANGE WITHOUT NOTICE. + +// Original url: http://www.ADsafe.org/adsafe.js + +// This file implements the core ADSAFE runtime. A site may add additional +// methods understanding that those methods will be made available to guest +// code. + +// This code should be minified before deployment. +// See http://javascript.crockford.com/jsmin.html + +// USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO +// NOT CONTROL. + +/*global window*/ + +/*jslint browser, devel, for, this +*/ + +/*property + _, ___nodes___, ___star___, _intercept, a, abbr, acronym, addEventListener, + address, altKey, append, appendChild, apply, area, arguments, autocomplete, + b, bdo, big, blockquote, blur, br, bubble, button, call, callee, caller, + cancelBubble, canvas, caption, center, change, charAt, charCode, check, + checked, childNodes, cite, class, className, clientX, clientY, clone, + cloneNode, code, col, colgroup, combine, concat, console, constructor, + count, create, createDocumentFragment, createElement, createRange, + createTextNode, createTextRange, cssFloat, ctrlKey, currentStyle, dd, + defaultView, del, dfn, dir, disabled, div, dl, dt, each, em, empty, enable, + ephemeral, eval, exec, expand, explode, fieldset, fire, firstChild, focus, + font, form, fragment, fromCharCode, get, getCheck, getChecks, getClass, + getClasses, getComputedStyle, getElementById, getElementsByTagName, + getMark, getMarks, getName, getNames, getOffsetHeight, getOffsetHeights, + getOffsetWidth, getOffsetWidths, getParent, getSelection, getStyle, + getStyles, getTagName, getTagNames, getTitle, getTitles, getValue, + getValues, go, h1, h2, h3, h4, h5, h6, has, hasOwnProperty, hr, i, id, img, + inRange, indeterminate, indexOf, input, ins, insertBefore, isArray, kbd, + key, keyCode, keys, klass, label, later, legend, length, li, lib, log, map, + mark, menu, message, name, nextSibling, nodeName, nodeValue, object, off, + offsetHeight, offsetWidth, ol, on, onclick, ondblclick, onfocusin, + onfocusout, onkeypress, onmousedown, onmousemove, onmouseout, onmouseover, + onmouseup, op, optgroup, option, p, parent, parentNode, postError, pre, + prepend, preventDefault, protect, prototype, push, q, remove, removeChild, + removeElement, replace, replaceChild, returnValue, row, samp, select, + selection, selectionEnd, selectionStart, set, shiftKey, slice, small, span, + srcElement, stack, stopPropagation, strong, style, styleFloat, sub, sup, + table, tag, tagName, target, tbody, td, test, text, textarea, tfoot, th, + that, thead, title, toLowerCase, toString, toUpperCase, tr, tt, type, u, + ul, unwatch, value, valueOf, var, visibility, watch, window, writeln, x, y +*/ + +var ADSAFE; +ADSAFE = (function () { + "use strict"; + + var adsafe_id; // The id of the current widget + var adsafe_lib; // The script libraries loaded by the current widget + +// These member names are banned from guest scripts. The ADSAFE.get and +// ADSAFE.put methods will not allow access to these properties. + + var banned = { + arguments: true, + callee: true, + caller: true, + constructor: true, + eval: true, + prototype: true, + stack: true, + unwatch: true, + valueOf: true, + watch: true + }; + + var cache_style_object; + var cache_style_node; + var defaultView = document.defaultView; + var ephemeral; + var flipflop; // Used in :even/:odd processing + var has_focus; + var hunter; // Set of hunter patterns + var interceptors = []; + + var makeableTagName = { + +// This is the whitelist of elements that may be created with the .tag(tagName) +// method. + + a: true, + abbr: true, + acronym: true, + address: true, + area: true, + b: true, + bdo: true, + big: true, + blockquote: true, + br: true, + button: true, + canvas: true, + caption: true, + center: true, + cite: true, + code: true, + col: true, + colgroup: true, + dd: true, + del: true, + dfn: true, + dir: true, + div: true, + dl: true, + dt: true, + em: true, + fieldset: true, + font: true, + form: true, + h1: true, + h2: true, + h3: true, + h4: true, + h5: true, + h6: true, + hr: true, + i: true, + img: true, + input: true, + ins: true, + kbd: true, + label: true, + legend: true, + li: true, + map: true, + menu: true, + object: true, + ol: true, + optgroup: true, + option: true, + p: true, + pre: true, + q: true, + samp: true, + select: true, + small: true, + span: true, + strong: true, + sub: true, + sup: true, + table: true, + tbody: true, + td: true, + textarea: true, + tfoot: true, + th: true, + thead: true, + tr: true, + tt: true, + u: true, + ul: true, + var: true + }; + var name; + var pecker; // set of pecker patterns + var result; + var star; + var the_range; + var value; + + +// The error function is called if there is a violation or confusion. +// It throws an exception. + + function error(message) { + ADSAFE.log("ADsafe error: " + (message || "ADsafe violation.")); + throw { + name: "ADsafe", + message: message || "ADsafe violation." + }; + } + + +// Some of JavaScript's implicit string conversions can grant extraordinary +// powers to untrusted code. So we use the string_check function to prevent +// such abuses. + + function string_check(string) { + if (typeof string !== "string") { + error("ADsafe string violation."); + } + return string; + } + + +// The object.hasOwnProperty method has a number of hazards. So we wrap it in +// the owns function. + + function owns(object, string) { + return ( + object + && typeof object === "object" + && Object.prototype.hasOwnProperty.call(object, string_check(string)) + ); + } + +// The reject functions enforce the restriction on property names. +// reject_property allows access only to objects and arrays. It does not allow +// use of the banned names, or names that are not strings and not numbers, +// or strings that start or end with _. + + function reject_name(name) { + return ( + typeof name !== "number" + && ( + typeof name !== "string" + || banned[name] + || name.charAt(0) === "_" + || name.slice(-1) === "_" + ) + ); + } + + + function reject_property(object, name) { + return typeof object !== "object" || reject_name(name); + } + + + function reject_global(that) { + if (that.window) { + error(); + } + } + + + function getStyleObject(node) { + +// The getStyleObject function returns the computed style object for a node. + + if (node === cache_style_node) { + return cache_style_object; + } + cache_style_node = node; + cache_style_object = ( + node.currentStyle + || defaultView.getComputedStyle(node, "") + ); + return cache_style_object; + } + + + function walkTheDOM(node, func, skip) { + +// Recursively traverse the DOM tree, starting with the node, in document +// source order, calling the func on each node visisted. + + if (!skip) { + func(node); + } + node = node.firstChild; + while (node) { + walkTheDOM(node, func); + node = node.nextSibling; + } + } + + + function purge_event_handlers(node) { + +// We attach all event handlers to an "___ on ___" property. The property name +// contains spaces to insure that there is no collision with HTML attribues. +// Keeping the handlers in a single property makes it easy to remove them +// all at once. Removal is required to avoid memory leakage on IE6 and IE7. + + walkTheDOM(node, function (node) { + if (node.tagName) { + node["___ on ___"] = null; + node.change = null; + } + }); + } + + + function parse_query(text, id) { + +// Convert a query string into an array of op/name/value selectors. +// A query string is a sequence of triples wrapped in brackets; or names, +// possibly prefixed by # . & > _, or :option, or * or /. A triple is a name, +// and operator (one of [=, [!=, [*=, [~=, [|=, [$=, or [^=) and a value. + +// If the id parameter is supplied, then the name following # must have the +// id as a prefix and must match the ADsafe rule for id: being all uppercase +// letters and digits with one underbar. + +// A name must be all lower case and may contain digits, -, or _. + + var match; // A match array + var query = []; // The resulting query array + var selector; + var qx = (id) + ? /^\s*(?:([*\/])|\[\s*([a-z][0-9a-z_\-]*)\s*(?:([!*~|$\^]?=)\s*([0-9A-Za-z_\-*%&;.\/:!]+)\s*)?\]|#\s*([A-Z]+_[A-Z0-9]+)|:\s*([a-z]+)|([.&_>+]?)\s*([a-z][0-9a-z\-]*))\s*/ + : /^\s*(?:([*\/])|\[\s*([a-z][0-9a-z_\-]*)\s*(?:([!*~|$\^]?=)\s*([0-9A-Za-z_\-*%&;.\/:!]+)\s*)?\]|#\s*([\-A-Za-z0-9_]+)|:\s*([a-z]+)|([.&_>+]?)\s*([a-z][0-9a-z\-]*))\s*/; + +// Loop over all of the selectors in the text. + + do { + +// The qx teases the components of one selector out of the text, ignoring +// whitespace. + +// match[0] the whole selector +// match[1] * / +// match[2] attribute name +// match[3] = != *= ~= |= $= ^= +// match[4] attribute value +// match[5] # id +// match[6] : option +// match[7] . & _ > + +// match[8] name + + match = qx.exec(string_check(text)); + if (!match) { + error("ADsafe: Bad query:" + text); + } + +// Make a selector object and stuff it in the query. + + if (match[1]) { + +// The selector is * or / + + selector = { + op: match[1] + }; + } else if (match[2]) { + +// The selector is in brackets. + + selector = (match[3]) + ? { + op: "[" + match[3], + name: match[2], + value: match[4] + } + : { + op: "[", + name: match[2] + }; + } else if (match[5]) { + +// The selector is an id. + + if ( + query.length > 0 + || match[5].length <= id.length + || match[5].slice(0, id.length) !== id + ) { + error("ADsafe: Bad query: " + text); + } + selector = { + op: "#", + name: match[5] + }; + +// The selector is a colon. + + } else if (match[6]) { + selector = { + op: ":" + match[6] + }; + +// The selector is one of > + . & _ or a naked tag name + + } else { + selector = { + op: match[7], + name: match[8] + }; + } + +// Add the selector to the query. + + query.push(selector); + +// Remove the selector from the text. If there is more text, have another go. + + text = text.slice(match[0].length); + } while (text); + return query; + } + + + hunter = { + +// These functions implement the hunter behaviors. + + "": function (node) { + var array; + var nodelist = node.getElementsByTagName(name); + var i; + var length; + +// getElementsByTagName produces a nodeList, which is one of the world's most +// inefficient data structures. It is so slow that JavaScript's pseudo arrays +// look terrifically swift by comparison. So we do the conversion. This is +// easily done on some browsers, less easily on others. + + try { + array = Array.prototype.slice.call(nodelist, 0); + result = (result.length) + ? result.concat(array) + : array; + } catch (ignore) { + length = nodelist.length; + for (i = 0; i < length; i += 1) { + result.push(nodelist[i]); + } + } + }, + "+": function (node) { + node = node.nextSibling; + name = name.toUpperCase(); + while (node && !node.tagName) { + node = node.nextSibling; + } + if (node && node.tagName === name) { + result.push(node); + } + }, + ">": function (node) { + node = node.firstChild; + name = name.toUpperCase(); + while (node) { + if (node.tagName === name) { + result.push(node); + } + node = node.nextSibling; + } + }, + "#": function () { + var n = document.getElementById(name); + if (n.tagName) { + result.push(n); + } + }, + "/": function (node) { + var nodes = node.childNodes; + var i; + var length = nodes.length; + for (i = 0; i < length; i += 1) { + result.push(nodes[i]); + } + }, + "*": function (node) { + star = true; + walkTheDOM(node, function (node) { + result.push(node); + }, true); + } + }; + + pecker = { + ".": function (node) { + var classy = " " + node.className + " "; + return classy.indexOf(" " + name + " ") >= 0; + }, + "&": function (node) { + return node.name === name; + }, + "_": function (node) { + return node.type === name; + }, + "[": function (node) { + return typeof node[name] === "string"; + }, + "[=": function (node) { + var member = node[name]; + return typeof member === "string" && member === value; + }, + "[!=": function (node) { + var member = node[name]; + return typeof member === "string" && member !== value; + }, + "[^=": function (node) { + var member = node[name]; + return typeof member === "string" && + member.slice(0, member.length) === value; + }, + "[$=": function (node) { + var member = node[name]; + return typeof member === "string" && + member.slice(-member.length) === value; + }, + "[*=": function (node) { + var member = node[name]; + return typeof member === "string" && + member.indexOf(value) >= 0; + }, + "[~=": function (node) { + var member = node[name]; + if (typeof member === "string") { + member = " " + member + " "; + return member.indexOf(" " + value + " ") >= 0; + } + }, + "[|=": function (node) { + var member = node[name]; + if (typeof member === "string") { + member = "-" + member + "-"; + return member.indexOf("-" + value + "-") >= 0; + } + }, + ":blur": function (node) { + return node !== has_focus; + }, + ":checked": function (node) { + return node.checked; + }, + ":disabled": function (node) { + return node.tagName && node.disabled; + }, + ":enabled": function (node) { + return node.tagName && !node.disabled; + }, + ":even": function (node) { + var f; + if (node.tagName) { + f = flipflop; + flipflop = !flipflop; + return f; + } + return false; + }, + ":focus": function (node) { + return node === has_focus; + }, + ":hidden": function (node) { + return node.tagName && getStyleObject(node).visibility !== "visible"; + }, + ":odd": function (node) { + if (node.tagName) { + flipflop = !flipflop; + return flipflop; + } + return false; + }, + ":tag": function (node) { + return node.tagName; + }, + ":text": function (node) { + return node.nodeName === "#text"; + }, + ":trim": function (node) { + return node.nodeName !== "#text" || (/\W/.test(node.nodeValue)); + }, + ":unchecked": function (node) { + return node.tagName && !node.checked; + }, + ":visible": function (node) { + return node.tagName && getStyleObject(node).visibility === "visible"; + } + }; + + + function quest(query, nodes) { + var selector; + var func; + var i; + var j; + +// Step through each selector. + + for (i = 0; i < query.length; i += 1) { + selector = query[i]; + name = selector.name; + func = hunter[selector.op]; + +// There are two kinds of selectors: hunters and peckers. If this is a hunter, +// loop through the the nodes, passing each node to the hunter function. +// Accumulate all the nodes it finds. + + if (typeof func === "function") { + if (star) { + error( + "ADsafe: Query violation: *" + + selector.op + + (selector.name || "") + ); + } + result = []; + for (j = 0; j < nodes.length; j += 1) { + func(nodes[j]); + } + } else { + +// If this is a pecker, get its function. There is a special case for +// the :first and :rest selectors because they are so simple. + + value = selector.value; + flipflop = false; + func = pecker[selector.op]; + if (typeof func !== "function") { + switch (selector.op) { + case ":first": + result = nodes.slice(0, 1); + break; + case ":rest": + result = nodes.slice(1); + break; + default: + error("ADsafe: Query violation: :" + selector.op); + } + } else { + +// For the other selectors, make an array of nodes that are filtered by +// the pecker function. + + result = []; + for (j = 0; j < nodes.length; j += 1) { + if (func(nodes[j])) { + result.push(nodes[j]); + } + } + } + } + nodes = result; + } + return result; + } + + + function make_root(root, id) { + + if (id) { + if (root.tagName !== "DIV") { + error("ADsafe: Bad node."); + } + } else { + if (root.tagName !== "BODY") { + error("ADsafe: Bad node."); + } + } + +// A Bunch is a container that holds zero or more dom nodes. +// It has many useful methods. + + function Bunch(nodes) { + this.___nodes___ = nodes; + this.___star___ = star && nodes.length > 1; + star = false; + } + + var allow_focus = true; + var dom; + var dom_event = function (ev) { + var key; + var target; + var that; + var the_event; + var the_target; + var the_actual_event = ev || event; + var type = the_actual_event.type; + +// Get the target node and wrap it in a bunch. + + the_target = the_actual_event.target || the_actual_event.srcElement; + target = new Bunch([the_target]); + that = target; + +// Use the PPK hack to make focus bubbly on IE. +// When a widget has focus, it can use the focus method. + + switch (type) { + case "mousedown": + allow_focus = true; + if (document.selection) { + the_range = document.selection.createRange(); + } + break; + case "focus": + case "focusin": + allow_focus = true; + has_focus = the_target; + the_actual_event.cancelBubble = false; + type = "focus"; + break; + case "blur": + case "focusout": + allow_focus = false; + has_focus = null; + type = "blur"; + break; + case "keypress": + allow_focus = true; + has_focus = the_target; + key = String.fromCharCode( + the_actual_event.charCode || the_actual_event.keyCode + ); + switch (key) { + case "\u000d": + case "\u000a": + type = "enterkey"; + break; + case "\u001b": + type = "escapekey"; + break; + } + break; + +// This is a workaround for Safari. + + case "click": + allow_focus = true; + break; + } + if ( + the_actual_event.cancelBubble + && the_actual_event.stopPropagation + ) { + the_actual_event.stopPropagation(); + } + +// Make the event object. + + the_event = { + altKey: the_actual_event.altKey, + ctrlKey: the_actual_event.ctrlKey, + bubble: function () { + +// Bubble up. Get the parent of that node. It becomes the new that. +// getParent throws when bubbling is not possible. + + try { + var parent = that.getParent(); + var b = parent.___nodes___[0]; + that = parent; + the_event.that = that; + +// If that node has an event handler, fire it. Otherwise, bubble up. + + if ( + b["___ on ___"] && b["___ on ___"][type] + ) { + that.fire(the_event); + } else { + the_event.bubble(); + } + } catch (e) { + error(e); + } + }, + key: key, + preventDefault: function () { + if (the_actual_event.preventDefault) { + the_actual_event.preventDefault(); + } + the_actual_event.returnValue = false; + }, + shiftKey: the_actual_event.shiftKey, + target: target, + that: that, + type: type, + x: the_actual_event.clientX, + y: the_actual_event.clientY + }; + +// If the target has event handlers, then fire them. Otherwise, bubble up. + + if ( + the_target["___ on ___"] + && the_target["___ on ___"][the_event.type] + ) { + target.fire(the_event); + } else { + while (true) { + the_target = the_target.parentNode; + if (!the_target) { + break; + } + if ( + the_target["___ on ___"] + && the_target["___ on ___"][the_event.type] + ) { + that = new Bunch([the_target]); + the_event.that = that; + that.fire(the_event); + break; + } + if (the_target["___adsafe root___"]) { + break; + } + } + } + if (the_event.type === "escapekey") { + if (ephemeral) { + ephemeral.remove(); + } + ephemeral = null; + } + that = null; + the_actual_event = null; + the_event = null; + the_target = null; + return; + }; + +// Mark the node as a root. This prevents event bubbling from propagating +// past it. + + root["___adsafe root___"] = "___adsafe root___"; + + Bunch.prototype = { + append: function (appendage) { + reject_global(this); + var b = this.___nodes___; + var flag = false; + var i; + var j; + var node; + var rep; + if (b.length === 0 || !appendage) { + return this; + } + if (Array.isArray(appendage)) { + if (appendage.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + rep = appendage[i].___nodes___; + for (j = 0; j < rep.length; j += 1) { + b[i].appendChild(rep[j]); + } + } + } else { + if (typeof appendage !== "string") { + rep = appendage.___nodes___; + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (rep) { + for (j = 0; j < rep.length; j += 1) { + node.appendChild((flag) + ? rep[j].cloneNode(true) + : rep[j]); + } + flag = true; + } else { + node.appendChild(document.createTextNode(appendage)); + } + } + } + return this; + }, + blur: function () { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + has_focus = null; + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.blur) { + node.blur(); + } + } + return this; + }, + check: function (value) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.checked = !!value[i]; + } + } + } else { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.checked = !!value; + } + } + } + return this; + }, + "class": function (value) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + if (/url/i.test(string_check(value[i]))) { + error("ADsafe error."); + } + node = b[i]; + if (node.tagName) { + node.className = value[i]; + } + } + } else { + if (/url/i.test(string_check(value))) { + error("ADsafe error."); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.className = value; + } + } + } + return this; + }, + clone: function (deep, n) { + var a = []; + var b = this.___nodes___; + var c; + var i; + var j; + var k = n || 1; + for (i = 0; i < k; i += 1) { + c = []; + for (j = 0; j < b.length; j += 1) { + c.push(b[j].cloneNode(deep)); + } + a.push(new Bunch(c)); + } + return (n) + ? a + : a[0]; + }, + count: function () { + reject_global(this); + return this.___nodes___.length; + }, + each: function (func) { + reject_global(this); + var b = this.___nodes___; + var i; + if (typeof func === "function") { + for (i = 0; i < b.length; i += 1) { + func(new Bunch([b[i]])); + } + return this; + } + error(); + }, + empty: function () { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + while (node.firstChild) { + purge_event_handlers(node); + node.removeChild(node.firstChild); + } + } + } else { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + while (node.firstChild) { + purge_event_handlers(node); + node.removeChild(node.firstChild); + } + } + } + return this; + }, + enable: function (enable) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(enable)) { + if (enable.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + "-" + + enable.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.disabled = !enable[i]; + } + } + } else { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.disabled = !enable; + } + } + } + return this; + }, + ephemeral: function () { + reject_global(this); + if (ephemeral) { + ephemeral.remove(); + } + ephemeral = this; + return this; + }, + explode: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = new Bunch([b[i]]); + } + return a; + }, + fire: function (event) { + + // Fire an event on an object. The event can be either + // a string containing the name of the event, or an + // object containing a type property containing the + // name of the event. Handlers registered by the "on" + // method that match the event name will be invoked. + + reject_global(this); + var array; + var b; + var i; + var j; + var n; + var node; + var on; + var type; + + if (typeof event === "string") { + type = event; + event = {type: type}; + } else if (typeof event === "object") { + type = event.type; + } else { + error(); + } + b = this.___nodes___; + n = b.length; + for (i = 0; i < n; i += 1) { + node = b[i]; + on = node["___ on ___"]; + + // If an array of handlers exist for this event, then + // loop through it and execute the handlers in order. + + if (owns(on, type)) { + array = on[type]; + for (j = 0; j < array.length; j += 1) { + + // Invoke a handler. Pass the event object. + + array[j].call(this, event); + } + } + } + return this; + }, + focus: function () { + reject_global(this); + var b = this.___nodes___; + if (b.length > 0 && allow_focus) { + has_focus = b[0].focus(); + return this; + } + error(); + }, + fragment: function () { + reject_global(this); + return new Bunch([document.createDocumentFragment()]); + }, + getCheck: function () { + return this.getChecks()[0]; + }, + getChecks: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i].checked; + } + return a; + }, + getClass: function () { + return this.getClasses()[0]; + }, + getClasses: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i].className; + } + return a; + }, + getMark: function () { + return this.getMarks()[0]; + }, + getMarks: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i]["_adsafe mark_"]; + } + return a; + }, + getName: function () { + return this.getNames()[0]; + }, + getNames: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i].name; + } + return a; + }, + getOffsetHeight: function () { + return this.getOffsetHeights()[0]; + }, + getOffsetHeights: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i].offsetHeight; + } + return a; + }, + getOffsetWidth: function () { + return this.getOffsetWidths()[0]; + }, + getOffsetWidths: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i].offsetWidth; + } + return a; + }, + getParent: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + var n; + for (i = 0; i < b.length; i += 1) { + n = b[i].parentNode; + if (n["___adsafe root___"]) { + error("ADsafe parent violation."); + } + a[i] = n; + } + return new Bunch(a); + }, + getSelection: function () { + reject_global(this); + var b = this.___nodes___; + var end; + var node; + var start; + var range; + if (b.length === 1 && allow_focus) { + node = b[0]; + if (typeof node.selectionStart === "number") { + start = node.selectionStart; + end = node.selectionEnd; + return node.value.slice(start, end); + } + range = node.createTextRange(); + range.expand("textedit"); + if (range.inRange(the_range)) { + return the_range.text; + } + } + return null; + }, + getStyle: function (name) { + return this.getStyles(name)[0]; + }, + getStyles: function (name) { + reject_global(this); + if (reject_name(name)) { + error("ADsafe style violation."); + } + var a = []; + var b = this.___nodes___; + var i; + var node; + var s; + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + s = (name !== "float") + ? getStyleObject(node)[name] + : getStyleObject(node).cssFloat + || getStyleObject(node).styleFloat; + if (typeof s === "string") { + a[i] = s; + } + } + } + return a; + }, + getTagName: function () { + return this.getTagNames()[0]; + }, + getTagNames: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + var tagName; + for (i = 0; i < b.length; i += 1) { + tagName = b[i].tagName; + a[i] = (typeof tagName === "string") + ? tagName.toLowerCase() + : tagName; + } + return a; + }, + getTitle: function () { + return this.getTitles()[0]; + }, + getTitles: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i].title; + } + return a; + }, + getValue: function () { + return this.getValues()[0]; + }, + getValues: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + var node; + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.nodeName === "#text") { + a[i] = node.nodeValue; + } else if (node.tagName && node.type !== "password") { + a[i] = node.value; + if ( + !a[i] + && node.firstChild + && node.firstChild.nodeName === "#text" + ) { + a[i] = node.firstChild.nodeValue; + } + } + } + return a; + }, + indeterminate: function (value) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.indeterminate = !!value[i]; + } + } + } else { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.indeterminate = !!value; + } + } + } + return this; + }, + klass: function (value) { + return this.class(value); + }, + mark: function (value) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node["_adsafe mark_"] = String(value[i]); + } + } + } else { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node["_adsafe mark_"] = String(value); + } + } + } + return this; + }, + off: function (type) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (typeof type === "string") { + if (typeof node["___ on ___"] === "object") { + node["___ on ___"][type] = null; + } + } else { + node["___ on ___"] = null; + } + } + return this; + }, + on: function (type, func) { + reject_global(this); + if (typeof type !== "string" || typeof func !== "function") { + error(); + } + + var b = this.___nodes___; + var i; + var node; + var on; + var ontype; + for (i = 0; i < b.length; i += 1) { + node = b[i]; + +// The change event does not propogate, so we must put the handler on the +// instance. + + if (type === "change") { + ontype = "on" + type; + if (node[ontype] !== dom_event) { + node[ontype] = dom_event; + } + } + +// Register an event. Put the function in a handler array, making one if it +// doesn't yet exist for this type on this node. + + on = node["___ on ___"]; + if (!on) { + on = {}; + node["___ on ___"] = on; + } + if (owns(on, type)) { + on[type].push(func); + } else { + on[type] = [func]; + } + } + return this; + }, + protect: function () { + reject_global(this); + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + b[i]["___adsafe root___"] = "___adsafe root___"; + } + return this; + }, + q: function (text) { + reject_global(this); + star = this.___star___; + return new Bunch( + quest(parse_query(string_check(text), id), this.___nodes___) + ); + }, + remove: function () { + reject_global(this); + this.replace(); + }, + replace: function (replacement) { + reject_global(this); + var b = this.___nodes___; + var flag = false; + var i; + var j; + var newnode; + var node; + var parent; + var rep; + if (b.length === 0) { + return; + } + for (i = 0; i < b.length; i += 1) { + purge_event_handlers(b[i]); + } + if ( + !replacement || replacement.length === 0 + || ( + replacement.___nodes___ + && replacement.___nodes___.length === 0 + ) + ) { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + purge_event_handlers(node); + if (node.parentNode) { + node.parentNode.removeChild(node); + } + } + } else if (Array.isArray(replacement)) { + if (replacement.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + parent = node.parentNode; + purge_event_handlers(node); + if (parent) { + rep = replacement[i].___nodes___; + if (rep.length > 0) { + newnode = rep[0]; + parent.replaceChild(newnode, node); + for (j = 1; j < rep.length; j += 1) { + node = newnode; + newnode = rep[j]; + parent.insertBefore(newnode, node.nextSibling); + } + } else { + parent.removeChild(node); + } + } + } + } else { + rep = replacement.___nodes___; + for (i = 0; i < b.length; i += 1) { + node = b[i]; + purge_event_handlers(node); + parent = node.parentNode; + if (parent) { + newnode = (flag) + ? rep[0].cloneNode(true) + : rep[0]; + parent.replaceChild(newnode, node); + for (j = 1; j < rep.length; j += 1) { + node = newnode; + newnode = (flag) + ? rep[j].clone(true) + : rep[j]; + parent.insertBefore(newnode, node.nextSibling); + } + flag = true; + } + } + } + return this; + }, + select: function () { + reject_global(this); + var b = this.___nodes___; + if (b.length < 1 || !allow_focus) { + error(); + } + b[0].focus(); + b[0].select(); + return this; + }, + selection: function (string) { + reject_global(this); + string_check(string); + var b = this.___nodes___; + var end; + var node; + var old; + var start; + var range; + if (b.length === 1 && allow_focus) { + node = b[0]; + if (typeof node.selectionStart === "number") { + start = node.selectionStart; + end = node.selectionEnd; + old = node.value; + node.value = old.slice(0, start) + string + old.slice(end); + node.selectionStart = start + string.length; + node.selectionEnd = start + string.length; + node.focus(); + } else { + range = node.createTextRange(); + range.expand("textedit"); + if (range.inRange(the_range)) { + the_range.select(); + the_range.text = string; + the_range.select(); + } + } + } + return this; + }, + style: function (name, value) { + reject_global(this); + if (reject_name(name)) { + error("ADsafe style violation."); + } + if (value === undefined || (/url/i.test(string_check(value)))) { + error(); + } + var b = this.___nodes___; + var i; + var node; + var v; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + v = string_check(value[i]); + if (/url/i.test(v)) { + error(); + } + if (node.tagName) { + if (name !== "float") { + node.style[name] = v; + } else { + node.style.cssFloat = v; + node.style.styleFloat = v; + } + } + } + } else { + v = string_check(value); + if (/url/i.test(v)) { + error(); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + if (name !== "float") { + node.style[name] = v; + } else { + node.style.cssFloat = v; + node.style.styleFloat = v; + } + } + } + } + return this; + }, + tag: function (tag, type, name) { + reject_global(this); + var node; + if (typeof tag !== "string") { + error(); + } + if (makeableTagName[tag] !== true) { + error("ADsafe: Bad tag: " + tag); + } + node = document.createElement(tag); + if (name) { + node.autocomplete = "off"; + node.name = string_check(name); + } + if (type) { + node.type = string_check(type); + } + return new Bunch([node]); + }, + text: function (text) { + reject_global(this); + var a; + var i; + if (Array.isArray(text)) { + a = []; + for (i = 0; i < text.length; i += 1) { + a[i] = document.createTextNode(string_check(text[i])); + } + return new Bunch(a); + } + return new Bunch([document.createTextNode(string_check(text))]); + }, + title: function (value) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.title = string_check(value[i]); + } + } + } else { + string_check(value); + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.title = value; + } + } + } + return this; + }, + value: function (value) { + reject_global(this); + if (value === undefined) { + error(); + } + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value) && b.length === value.length) { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + if (node.type !== "password") { + if (typeof node.value === "string") { + node.value = value[i]; + } else { + while (node.firstChild) { + purge_event_handlers(node.firstChild); + node.removeChild(node.firstChild); + } + node.appendChild(document.createTextNode( + String(value[i]) + )); + } + } + } else if (node.nodeName === "#text") { + node.nodeValue = String(value[i]); + } + } + } else { + value = String(value); + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + if ( + node.tagName !== "BUTTON" + && typeof node.value === "string" + ) { + node.value = value; + } else { + while (node.firstChild) { + purge_event_handlers(node.firstChild); + node.removeChild(node.firstChild); + } + node.appendChild(document.createTextNode(value)); + } + } else if (node.nodeName === "#text") { + node.nodeValue = value; + } + } + } + return this; + } + }; + +// Return an ADsafe dom object. + + dom = { + append: function (bunch) { + var b = (typeof bunch === "string") + ? [document.createTextNode(bunch)] + : bunch.___nodes___; + var i; + var n; + for (i = 0; i < b.length; i += 1) { + n = b[i]; + if (typeof n === "string" || typeof n === "number") { + n = document.createTextNode(String(n)); + } + root.appendChild(n); + } + return dom; + }, + combine: function (array) { + if (!array || !array.length) { + error("ADsafe: Bad combination."); + } + var b = array[0].___nodes___; + var i; + for (i = 0; i < array.length; i += 1) { + b = b.concat(array[i].___nodes___); + } + return new Bunch(b); + }, + count: function () { + return 1; + }, + ephemeral: function (bunch) { + if (ephemeral) { + ephemeral.remove(); + } + ephemeral = bunch; + return dom; + }, + fragment: function () { + return new Bunch([document.createDocumentFragment()]); + }, + prepend: function (bunch) { + var b = bunch.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + root.insertBefore(b[i], root.firstChild); + } + return dom; + }, + q: function (text) { + star = false; + var query = parse_query(text, id); + if (typeof hunter[query[0].op] !== "function") { + error("ADsafe: Bad query: " + query[0]); + } + return new Bunch(quest(query, [root])); + }, + remove: function () { + purge_event_handlers(root); + root.parent.removeElement(root); + root = null; + }, + row: function (values) { + var tr = document.createElement("tr"); + var td; + var i; + for (i = 0; i < values.length; i += 1) { + td = document.createElement("td"); + td.appendChild(document.createTextNode(String(values[i]))); + tr.appendChild(td); + } + return new Bunch([tr]); + }, + tag: function (tag, type, name) { + var node; + if (typeof tag !== "string") { + error(); + } + if (makeableTagName[tag] !== true) { + error("ADsafe: Bad tag: " + tag); + } + node = document.createElement(tag); + if (name) { + node.autocomplete = "off"; + node.name = name; + } + if (type) { + node.type = type; + } + return new Bunch([node]); + }, + text: function (text) { + var a; + var i; + if (Array.isArray(text)) { + a = []; + for (i = 0; i < text.length; i += 1) { + a[i] = document.createTextNode(string_check(text[i])); + } + return new Bunch(a); + } + return new Bunch([document.createTextNode(string_check(text))]); + } + }; + + if (typeof root.addEventListener === "function") { + root.addEventListener("focus", dom_event, true); + root.addEventListener("blur", dom_event, true); + root.addEventListener("mouseover", dom_event, true); + root.addEventListener("mouseout", dom_event, true); + root.addEventListener("mouseup", dom_event, true); + root.addEventListener("mousedown", dom_event, true); + root.addEventListener("mousemove", dom_event, true); + root.addEventListener("click", dom_event, true); + root.addEventListener("dblclick", dom_event, true); + root.addEventListener("keypress", dom_event, true); + } else { + root.onclick = dom_event; + root.ondblclick = dom_event; + root.onfocusin = dom_event; + root.onfocusout = dom_event; + root.onkeypress = dom_event; + root.onmouseout = dom_event; + root.onmousedown = dom_event; + root.onmousemove = dom_event; + root.onmouseover = dom_event; + root.onmouseup = dom_event; + } + return [dom, Bunch.prototype]; + } + + +// Return the ADSAFE object. + + return { + create: function (o) { + reject_global(o); + return Object.create(o); + }, + +// ADSAFE.get retrieves a value from an object. + + get: function (object, name) { + reject_global(object); + if (!reject_property(object, name)) { + return object[name]; + } + error(); + }, + +// ADSAFE.go allows a guest widget to get access to a wrapped dom node and +// approved ADsafe libraries. It is passed an id and a function. The function +// will be passed the wrapped dom node and an object containing the libraries. + + go: function (id, f) { + var dom; + var fun; + var root; + var i; + var scripts; + +// If ADSAFE.id was called, the id better match. + + if (adsafe_id && adsafe_id !== id) { + error(); + } + +// Get the dom node for the widget's div container. + + root = document.getElementById(id); + if (root.tagName !== "DIV") { + error(); + } + adsafe_id = null; + +// Delete the scripts held in the div. They have all run, so we don't need +// them any more. If the div had no scripts, then something is wrong. +// This provides some protection against mishaps due to weakness in the +// document.getElementById function. + + scripts = root.getElementsByTagName("script"); + i = scripts.length - 1; + if (i < 0) { + error(); + } + do { + root.removeChild(scripts[i]); + i -= 1; + } while (i >= 0); + root = make_root(root, id); + dom = root[0]; + +// If the page has registered interceptors, call then. + + for (i = 0; i < interceptors.length; i += 1) { + fun = interceptors[i]; + if (typeof fun === "function") { + try { + fun(id, dom, adsafe_lib, root[1]); + } catch (e1) { + ADSAFE.log(e1); + } + } + } + +// Call the supplied function. + + try { + f(dom, adsafe_lib); + } catch (e2) { + ADSAFE.log(e2); + } + root = null; + adsafe_lib = null; + }, + +// ADSAFE.has returns true if the object contains an own property with the +// given name. + + has: function (object, name) { + return owns(object, name); + }, + +// ADSAFE.id allows a guest widget to indicate that it wants to load +// ADsafe approved libraries. + + id: function (id) { + +// Calls to ADSAFE.id must be balanced with calls to ADSAFE.go. +// Only one id can be active at a time. + + if (adsafe_id) { + error(); + } + adsafe_id = id; + adsafe_lib = {}; + }, + +// ADSAFE.isArray returns true if the operand is an array. + + isArray: Array.isArray || function (value) { + return Object.prototype.toString.apply(value) === "[object Array]"; + }, + +// ADSAFE.keys returns an array of keys. + + keys: Object.keys, + +// ADSAFE.later calls a function at a later time. + + later: function (func, timeout) { + if (typeof func === "function") { + setTimeout(func, timeout || 0); + } else { + error(); + } + }, + +// ADSAFE.lib allows an approved ADsafe library to make itself available +// to a widget. The library provides a name and a function. The result of +// calling that function will be made available to the widget via the name. + + lib: function (name, f) { + if (!adsafe_id || reject_name(name)) { + error("ADsafe lib violation."); + } + adsafe_lib[name] = f(adsafe_lib); + }, + +// ADSAFE.log is a debugging aid that spams text to the browser's log. +// Overwrite this function to send log matter somewhere else. + + log: function log(s) { + if (window.console) { + console.log(s); + } else if (typeof Debug === "object") { + Debug.writeln(s); /* IE */ + } else { + opera.postError(s); /* Opera */ + } + }, + +// ADSAFE.remove deletes a value from an object. + + remove: function (object, name) { + if (!reject_property(object, name)) { + delete object[name]; + return; + } + error(); + }, + +// ADSAFE.set stores a value in an object. + + set: function (object, name, value) { + reject_global(object); + if (!reject_property(object, name)) { + object[name] = value; + return; + } + error(); + }, + +// ADSAFE._intercept allows the page to register a function that will +// see the widget's capabilities. + + _intercept: function (f) { + interceptors.push(f); + } + + }; +}()); diff --git a/apidoc.html b/apidoc.html new file mode 100644 index 000000000..c6101547c --- /dev/null +++ b/apidoc.html @@ -0,0 +1,2076 @@ + + + + + + +JSLint apidoc + + + +
    +

    API Doc for JSLint (v2024.11.24)

    +
    + +

    Table of Contents

    +
    + +
    + +
    +

    Module "./jslint.mjs"

    +
      + +
    • +

      + 1. + function assertErrorThrownAsync(asyncFunc, regexp) + +

      +
    • +
    • Description and source-code:
      async function assertErrorThrownAsync(asyncFunc, regexp) {
      +
      +// This function will assert calling <asyncFunc> throws an error.
      +
      +    let err;
      +    try {
      +        await asyncFunc();
      +    } catch (errCaught) {
      +        err = errCaught;
      +    }
      +    assertOrThrow(err, "No error thrown.");
      +    assertOrThrow(
      +        !regexp || new RegExp(regexp).test(err.message),
      +        err
      +    );
      +}
    • +
    • Example usage:
      ...
      +
      +jstestDescribe((
      +"test v8CoverageReportCreate handling-behavior"
      +), function testBehaviorV8CoverageReportCreate() {
      +jstestIt((
      +    "test null-case handling-behavior"
      +), async function () {
      +    await assertErrorThrownAsync(function () {
      +        return v8CoverageReportCreate({});
      +    }, "invalid coverageDir");
      +});
      +jstestIt((
      +    "test coverage-report jslint.mjs handling-behavior"
      +), async function () {
      +    // test remove-old-coverage handling-behavior
      +...
    • + +
    • +

      + 2. + function assertJsonEqual(aa, bb, message) + +

      +
    • +
    • Description and source-code:
      function assertJsonEqual(aa, bb, message) {
      +
      +// This function will assert JSON.stringify(<aa>) === JSON.stringify(<bb>).
      +
      +    aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa), undefined, 1);
      +    bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb), undefined, 1);
      +    if (aa !== bb) {
      +        throw new Error(
      +            "\n" + aa + "\n!==\n" + bb
      +            + (
      +                typeof message === "string"
      +                ? " - " + message
      +                : message
      +                ? " - " + JSON.stringify(message)
      +                : ""
      +            )
      +        );
      +    }
      +}
    • +
    • Example usage:
      ...
      +
      +// Debug data1.
      +// await moduleFs.promises.writeFile(
      +//     ".test_v8_coverage_node_sqlite_merged.json",
      +//     JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n"
      +// );
      +
      +    assertJsonEqual(data1, data2);
      +});
      +});
      +
      +jstestDescribe((
      +"test v8CoverageReportCreate handling-behavior"
      +), function testBehaviorV8CoverageReportCreate() {
      +jstestIt((
      +...
    • + +
    • +

      + 3. + function assertOrThrow(condition, message) + +

      +
    • +
    • Description and source-code:
      function assertOrThrow(condition, message) {
      +
      +// This function will throw <message> if <condition> is falsy.
      +
      +    if (!condition) {
      +        throw (
      +            (!message || typeof message === "string")
      +            ? new Error(String(message).slice(0, 2048))
      +            : message
      +        );
      +    }
      +}
    • +
    • Example usage:
      ...
      +            assertJsonEqual(1, 2, "undefined");
      +        });
      +        await assertErrorThrownAsync(function () {
      +            assertJsonEqual(1, 2, {});
      +        });
      +        // test assertOrThrow error handling-behavior
      +        await assertErrorThrownAsync(function () {
      +            assertOrThrow(undefined, "undefined");
      +        });
      +        await assertErrorThrownAsync(function () {
      +            assertOrThrow(undefined, new Error());
      +        });
      +    });
      +});
      +...
    • + +
    • +

      + 4. + function debugInline(...argv) + +

      +
    • +
    • Description and source-code:
      function debug(...argv) {
      +
      +// This function will print <argv> to stderr and then return <argv>[0].
      +
      +    __consoleError("\n\ndebugInline");
      +    __consoleError(...argv);
      +    __consoleError("\n");
      +    return argv[0];
      +}
    • +
    • Example usage:
      N/A
    • + +
    • +

      + 5. + function fsWriteFileWithParents(pathname, data) + +

      +
    • +
    • Description and source-code:
      async function fsWriteFileWithParents(pathname, data) {
      +
      +// This function will write <data> to <pathname> and lazy-mkdirp if necessary.
      +
      +    await moduleFsInit();
      +
      +// Try writing to pathname.
      +
      +    try {
      +        await moduleFs.promises.writeFile(pathname, data);
      +    } catch (ignore) {
      +
      +// Lazy mkdirp.
      +
      +        await moduleFs.promises.mkdir(modulePath.dirname(pathname), {
      +            recursive: true
      +        });
      +
      +// Retry writing to pathname.
      +
      +        await moduleFs.promises.writeFile(pathname, data);
      +    }
      +    console.error("wrote file " + pathname);
      +}
    • +
    • Example usage:
      ...
      +                }
      +            ]
      +        }, undefined, 4)
      +    ]
      +].map(async function ([
      +    file, data
      +]) {
      +    await fsWriteFileWithParents(file, data);
      +}));
      +await jslint.jslint_cli({
      +    console_error: noop, // comment to debug
      +    mode_cli: true,
      +    process_argv: [
      +        "node", "jslint.mjs",
      +        "v8_coverage_report=.tmp/coverage_misc"
      +...
    • + +
    • +

      + 6. + function globExclude({ + excludeList = [], + includeList = [], + pathnameList = [] +}) + +

      +
    • +
    • Description and source-code:
      function globExclude({
      +    excludeList = [],
      +    includeList = [],
      +    pathnameList = []
      +}) {
      +
      +// This function will
      +// 1. Exclude pathnames in <pathnameList> that don't match glob-patterns in
      +//    <includeList>.
      +// 2. Exclude pathnames in <pathnameList> that match glob-patterns in
      +//    <excludeList>.
      +
      +    function globAssertNotWeird(list, name) {
      +
      +// This function will check if <list> of strings contain weird characters.
      +
      +        [
      +            [
      +                "\n", (
      +                    /^.*?([\u0000-\u0007\r]).*/gm
      +                )
      +            ],
      +            [
      +                "\r", (
      +                    /^.*?([\n]).*/gm
      +                )
      +            ]
      +        ].forEach(function ([
      +            separator, rgx
      +        ]) {
      +            list.join(separator).replace(rgx, function (match0, char) {
      +                throw new Error(
      +                    "Weird character "
      +                    + JSON.stringify(char)
      +                    + " found in " + name + " "
      +                    + JSON.stringify(match0)
      +                );
      +            });
      +        });
      +    }
      +
      +    function globToRegexp(pattern) {
      +
      +// This function will translate glob <pattern> to javascript-regexp,
      +// which javascript can then use to "glob" pathnames.
      +
      +        let ii = 0;
      +        let isClass = false;
      +        let strClass = "";
      +        let strRegex = "";
      +        pattern = pattern.replace((
      +            /\/\/+/g
      +        ), "/");
      +        pattern = pattern.replace((
      +            /\*\*\*+/g
      +        ), "**");
      +        pattern.replace((
      +            /\\\\|\\\[|\\\]|\[|\]|./g
      +        ), function (match0) {
      +            switch (match0) {
      +            case "[":
      +                if (isClass) {
      +                    strClass += "[";
      +                    return;
      +                }
      +                strClass += "\u0000";
      +                strRegex += "\u0000";
      +                isClass = true;
      +                return;
      +            case "]":
      +                if (isClass) {
      +                    isClass = false;...
      +}
      +
    • +
    • Example usage:
      ...
      +    "test/support/helper.js": (
      +        /^test\/support\/helper\.js$/gm
      +    )
      +}).forEach(function ([
      +    pattern, rgx
      +]) {
      +    assertJsonEqual(
      +        globExclude({
      +            excludeList: [
      +                pattern
      +            ]
      +        }).excludeList[0].source,
      +        rgx.source
      +    );
      +    assertJsonEqual(
      +...
    • + +
    • +

      + 7. + function htmlEscape(str) + +

      +
    • +
    • Description and source-code:
      function htmlEscape(str) {
      +
      +// This function will make <str> html-safe by escaping & < >.
      +
      +    return String(str).replace((
      +        /&/g
      +    ), "&amp;").replace((
      +        /</g
      +    ), "&lt;").replace((
      +        />/g
      +    ), "&gt;");
      +}
    • +
    • Example usage:
      ...
      +                            inHole = isHole;
      +                        }
      +                        chunk += char;
      +                    });
      +                    lineHtml += htmlEscape(chunk);
      +                    break;
      +                default:
      +                    lineHtml += htmlEscape(line);
      +                }
      +                html += String(`
      +<pre>
      +<span class="lineno">
      +<a href="#${lineId}" id="${lineId}">${String(ii + 1).padStart(5, " ")}.</a>
      +</span>
      +<span class="count
      +...
    • + +
    • +

      + 8. + function jslint( + source = "", + option_dict = empty(), + global_list = [] +) + +

      +
    • +
    • Description and source-code:
      function jslint(
      +    source = "",                // A text to analyze.
      +    option_dict = empty(),      // An object whose keys correspond to option
      +                                // ... names.
      +    global_list = []            // An array of strings containing global
      +                                // ... variables that the file is allowed
      +                                // ... readonly access.
      +) {
      +
      +// The jslint function itself.
      +
      +    let catch_list = [];        // The array containing all catch-blocks.
      +    let catch_stack = [         // The stack of catch-blocks.
      +        {
      +            context: empty()
      +        }
      +    ];
      +    let cause_dict = empty();   // The object of test-causes.
      +    let directive_list = [];    // The directive comments.
      +    let export_dict = empty();  // The exported names and values.
      +    let function_list = [];     // The array containing all functions.
      +    let function_stack = [];    // The stack of functions.
      +    let global_dict = empty();  // The object containing the global
      +                                // ... declarations.
      +    let import_list = [];       // The array collecting all import-from strings.
      +    let line_list = String(     // The array containing source lines.
      +        "\n" + source
      +    ).split(jslint_rgx_crlf).map(function (line_source) {
      +        return {
      +            line_source
      +        };
      +    });
      +    let mode_stop = false;      // true if JSLint cannot finish.
      +    let property_dict = empty();        // The object containing the tallied
      +                                        // ... property names.
      +    let state = empty();        // jslint state-object to be passed between
      +                                // jslint functions.
      +    let syntax_dict = empty();  // The object containing the parser.
      +    let tenure = empty();       // The predefined property registry.
      +    let token_global = {        // The global object; the outermost context.
      +        async: 0,
      +        body: true,
      +        context: empty(),
      +        finally: 0,
      +        from: 0,
      +...
      +}
      +
    • +
    • Example usage:
      ...
      +import fs from "fs";
      +(async function () {
      +    let result;
      +    let source = "function foo() {console.log(\u0027hello world\u0027);}\n";
      +
      +// Create JSLint report from <source> in javascript.
      +
      +    result = jslint.jslint(source);
      +    result = jslint.jslint_report(result);
      +    result = `<body class="JSLINT_ JSLINT_REPORT_">\n${result}</body>\n`;
      +
      +    await fs.promises.mkdir(".artifact/", {recursive: true});
      +    await fs.promises.writeFile(".artifact/jslint_report_hello.html", result);
      +    console.error("wrote file .artifact/jslint_report_hello.html");
      +}());
      +...
    • + +
    • +

      + 9. + function jslint_apidoc({ + example_list, + github_repo, + module_list, + package_name, + pathname, + version +}) + +

      +
    • +
    • Description and source-code:
      async function jslint_apidoc({
      +    example_list,
      +    github_repo,
      +    module_list,
      +    package_name,
      +    pathname,
      +    version
      +}) {
      +
      +// This function will create API Doc from <module_list>.
      +
      +    let elem_ii = 0;
      +    let html;
      +
      +    function elem_create(moduleObj, key, moduleName) {
      +
      +// This function will create a sub API Doc from elem <moduleObj>[<key>].
      +
      +        let example = "N/A";
      +        let id = encodeURIComponent("apidoc.elem." + moduleName + "." + key);
      +        let name;
      +        let signature;
      +        let source;
      +        name = htmlEscape((typeof moduleObj[key]) + " " + key);
      +        if (typeof moduleObj[key] !== "function") {
      +            return {
      +                name,
      +                signature: (`
      +<a class="apidocElementLiA" href="#${id}">
      +${name}
      +</a>
      +                `),
      +                source: (`
      +<li>
      +    <h2>
      +    <a href="#${id}" id="${id}">
      +    ${name}
      +    </a>
      +    </h2>
      +</li>
      +                `)
      +            };
      +        }
      +        // init source
      +        source = htmlEscape(trim_start(moduleObj[key].toString()));
      +        // init signature
      +        source = source.replace((
      +            /(\([\S\s]*?\)) \{/
      +        ), function (match0, match1) {
      +            signature = htmlEscape(
      +                match1.replace((
      +                    / *?\/\*[\S\s]*?\*\/ */g
      +                ), "").replace((
      +                    / *?\/\/.*/g
      +                ), "").replace((
      +                    /\n{2,}/g
      +                ), "\n")
      +            );
      +            return match0;
      +        });
      +        // init comment
      +        source = source.replace((
      +            /\n(?:\/\/.*?\n)+\n/
      +        ), "<span class=\"apidocCodeCommentSpan\">$&</span>");
      +        // init example
      +        example_list.some(function (example2) {
      +            example2.replace(
      +                new RegExp((
      +                    "((?:\\n.*?){8}(function )?)\\b"
      +                    + key
      +                    + "(\\((?:.*?\\n){8})"
      +                ), "g"),
      +  ...
      +}
      +
    • +
    • Example usage:
      ...
      +command[1] = command.slice(1).join("=");
      +
      +switch (command[0]) {
      +
      +// PR-362 - Add API Doc.
      +
      +case "jslint_apidoc":
      +    await jslint_apidoc(Object.assign(JSON.parse(process_argv[3]), {
      +        pathname: command[1]
      +    }));
      +    return;
      +
      +// PR-363 - Add command jslint_report.
      +
      +case "jslint_report":
      +...
    • + +
    • +

      + 10. + function jslint_assert(condition, message) + +

      +
    • +
    • Description and source-code:
      function jslint_assert(condition, message) {
      +
      +// This function will throw <message> if <condition> is falsy.
      +
      +    if (condition) {
      +        return condition;
      +    }
      +    throw new Error(
      +        `This was caused by a bug in JSLint.
      +Please open an issue with this stack-trace (and possible example-code) at
      +https://github.com/jslint-org/jslint/issues.
      +edition = "${jslint_edition}";
      +${String(message).slice(0, 2000)}`
      +    );
      +}
    • +
    • Example usage:
      ...
      +// ["let aa={};", "whitage", "opener", "", 0]
      +
      +test_cause("opener");
      +
      +// Probably deadcode.
      +// case "${}":
      +
      +jslint_assert(
      +    !(left.id + right.id === "${}"),
      +    "Expected !(left.id + right.id === \"${}\")."
      +);
      +switch (left.id + right.id) {
      +case "()":
      +case "[]":
      +case "{}":
      +...
    • + +
    • +

      + 11. + function jslint_cli({ + console_error, + console_log, + file, + import_meta_url, + mode_cli, + mode_noop, + option, + process_argv, + process_env, + process_exit, + source +}) + +

      +
    • +
    • Description and source-code:
      async function jslint_cli({
      +    console_error,
      +    console_log,
      +    file,
      +    import_meta_url,
      +    mode_cli,
      +    mode_noop,
      +    option,
      +    process_argv,
      +    process_env,
      +    process_exit,
      +    source
      +}) {
      +
      +// This function will run jslint from nodejs-cli.
      +
      +    let command;
      +    let data;
      +    let exit_code = 0;
      +    let mode_report;
      +    let mode_wrapper_vim;
      +    let result;
      +
      +    function jslint_from_file({
      +        code,
      +        file,
      +        line_offset = 0,
      +        mode_conditional,
      +        option = empty()
      +    }) {
      +        let result_from_file;
      +        if (
      +            mode_conditional
      +            && !(
      +                /^\/\*jslint\b/m
      +            ).test(code.slice(0, 65536))
      +        ) {
      +            return;
      +        }
      +        option = Object.assign(empty(), option, {
      +            file
      +        });
      +        switch ((
      +            /\.\w+?$|$/m
      +        ).exec(file)[0]) {
      +        case ".html":
      +
      +// Recursively jslint embedded "<script>\n...\n</script>".
      +
      +            code.replace((
      +                /^<script\b[^>]*?>\n([\S\s]*?\n)<\/script>$/gm
      +            ), function (ignore, match1, ii) {
      +                jslint_from_file({
      +                    code: match1,
      +                    file: file + ".<script>.js",
      +                    line_offset: string_line_count(code.slice(0, ii)) + 1,
      +                    option: Object.assign(empty(), {
      +                        browser: true
      +                    }, option)
      +                });
      +                return "";
      +            });
      +            return;
      +        case ".md":
      +
      +// Recursively jslint embedded "node --eval '\n...\n'".
      +
      +            jslint_node_eval({
      +                code,
      +                file,
      +                mode_conditional: true,
      +                option
      +            });
      +            return;
      +        case ".sh":
      +
      +// Recursively jslint embedded "node --eval '\n...\n'".
      +
      +            jslint_node_eval({
      +                code,
      +                file,
      +                option
      +            });
      +            return;
      +       ...
      +}
      +
    • +
    • Example usage:
      ...
      +        }, undefined, 4)
      +    ]
      +].map(async function ([
      +    file, data
      +]) {
      +    await fsWriteFileWithParents(file, data);
      +}));
      +await jslint.jslint_cli({
      +    console_error: noop, // comment to debug
      +    mode_cli: true,
      +    process_argv: [
      +        "node", "jslint.mjs",
      +        "v8_coverage_report=.tmp/coverage_misc"
      +        // "node", ".tmp/coverage_misc/aa.js"
      +    ]
      +...
    • + +
    • +

      + 12. + function jslint_phase1_split() + +

      +
    • +
    • Description and source-code:
      function jslint_phase1_split() {
      +
      +// PHASE 1. Split <source> by newlines into <line_list>.
      +
      +    return;
      +}
    • +
    • Example usage:
      ...
      +            warn,
      +            warn_at,
      +            warning_list
      +        });
      +
      +// PHASE 1. Split <source> by newlines into <line_list>.
      +
      +        jslint_phase1_split(state);
      +        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
      +        jslint_assert(
      +            function_stack.length === 0,
      +            `function_stack.length === 0.`
      +        );
      +
      +// PHASE 2. Lex <line_list> into <token_list>.
      +...
    • + +
    • +

      + 13. + function jslint_phase2_lex(state) + +

      +
    • +
    • Description and source-code:
      function jslint_phase2_lex(state) {
      +
      +// PHASE 2. Lex <line_list> into <token_list>.
      +
      +    let {
      +        artifact,
      +        directive_list,
      +        global_dict,
      +        global_list,
      +        line_list,
      +        option_dict,
      +        stop,
      +        stop_at,
      +        tenure,
      +        test_cause,
      +        token_global,
      +        token_list,
      +        warn,
      +        warn_at
      +    } = state;
      +    let char;                   // The current character being lexed.
      +    let column = 0;             // The column number of the next character.
      +    let from;                   // The starting column number of the token.
      +    let from_mega;              // The starting column of megastring.
      +    let line = 0;               // The line number of the next character.
      +    let line_disable;           // The starting line of "/*jslint-disable*/".
      +    let line_mega;              // The starting line of megastring.
      +    let line_source = "";       // The remaining line source string.
      +    let line_whole = "";        // The whole line source string.
      +    let mode_digits_empty_string = 1;
      +    let mode_digits_numeric_separator = 2;
      +    let mode_directive = true;  // true if directives are still allowed.
      +    let mode_mega = false;      // true if currently parsing a megastring
      +                                // ... literal.
      +    let mode_regexp;            // true if regular expression literal seen on
      +                                // ... this line.
      +    let paren_backtrack_list = [];      // List of most recent "(" tokens at any
      +                                        // ... paren-depth.
      +    let paren_depth = 0;        // Keeps track of current paren-depth.
      +    let snippet = "";           // A piece of string.
      +    let token_1;                // The first token.
      +    let token_prv = token_global;       // The previous token including
      +                                        // ... comments.
      +    let token_prv_expr = token_global;  // The previous token excluding
      +                                        // ... comm...
      +}
      +
    • +
    • Example usage:
      ...
      +        jslint_assert(
      +            function_stack.length === 0,
      +            `function_stack.length === 0.`
      +        );
      +
      +// PHASE 2. Lex <line_list> into <token_list>.
      +
      +        jslint_phase2_lex(state);
      +        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
      +        jslint_assert(
      +            function_stack.length === 0,
      +            `function_stack.length === 0.`
      +        );
      +
      +// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
      +...
    • + +
    • +

      + 14. + function jslint_phase3_parse(state) + +

      +
    • +
    • Description and source-code:
      function jslint_phase3_parse(state) {
      +
      +// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
      +
      +// Parsing:
      +
      +// Parsing weaves the tokens into an abstract syntax tree. During that process,
      +// a token may be given any of these properties:
      +
      +//      arity       string
      +//      label       identifier
      +//      name        identifier
      +//      expression  expressions
      +//      block       statements
      +//      else        statements (else, default, catch)
      +
      +// Specialized tokens may have additional properties.
      +
      +    let anon = "anonymous";     // The guessed name for anonymous functions.
      +    let {
      +        artifact,
      +        catch_list,
      +        catch_stack,
      +        export_dict,
      +        function_list,
      +        function_stack,
      +        global_dict,
      +        import_list,
      +        is_equal,
      +        option_dict,
      +        property_dict,
      +        stop,
      +        syntax_dict,
      +        tenure,
      +        test_cause,
      +        token_global,
      +        token_list,
      +        warn,
      +        warn_at
      +    } = state;
      +    let catchage = catch_stack[0];      // The current catch-block.
      +    let functionage = token_global;     // The current function.
      +    let mode_var;               // "var" if using var; "let" if using let.
      +    let token_ii = 0;           // The number of the next token.
      +    let token_now = token_global;       // The current token being examined in
      +                                        // ... the parse.
      +    let token_nxt = token_global;       // The next token to be examined in
      +                                        // ... <token_list>.
      +
      +    function advance(id, match) {
      +
      +// Produce the next token.
      +
      +// Attempt to give helpful names to anonymous functions.
      +
      +        if (
      +            token_now.identifier
      +            && token_now.id !== "function"
      +            && token_now.id !== "async"
      +        ) {
      +            anon = token_now.id;
      +        } else if (
      +            token_now.id === "(string)"
      +            && jslint_rgx_identifier.test(token_now.value)
      +       ...
      +}
      +
    • +
    • Example usage:
      ...
      +        jslint_assert(
      +            function_stack.length === 0,
      +            `function_stack.length === 0.`
      +        );
      +
      +// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
      +
      +        jslint_phase3_parse(state);
      +        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
      +        jslint_assert(
      +            function_stack.length === 0,
      +            `function_stack.length === 0.`
      +        );
      +
      +// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
      +...
    • + +
    • +

      + 15. + function jslint_phase4_walk(state) + +

      +
    • +
    • Description and source-code:
      function jslint_phase4_walk(state) {
      +
      +// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
      +//          recursive traversal. Each node may be processed on the way down
      +//          (preaction) and on the way up (postaction).
      +
      +    let {
      +        artifact,
      +        catch_stack,
      +        function_stack,
      +        global_dict,
      +        is_equal,
      +        is_weird,
      +        option_dict,
      +        syntax_dict,
      +        test_cause,
      +        token_global,
      +        warn
      +    } = state;
      +    let block_stack = [];               // The stack of blocks.
      +    let blockage = token_global;        // The current block.
      +    let catchage = catch_stack[0];      // The current catch-block.
      +    let functionage = token_global;     // The current function.
      +    let postaction;
      +    let postamble;
      +    let posts = empty();
      +    let preaction;
      +    let preamble;
      +    let pres = empty();
      +
      +// The relational operators.
      +
      +    let relationop = object_assign_from_list(empty(), [
      +        "!=", "!==", "<", "<=", "==", "===", ">", ">="
      +    ], true);
      +
      +// Ambulation of the parse tree.
      +
      +    function action(when) {
      +
      +// Produce a function that will register task functions that will be called as
      +// the tree is traversed.
      +
      +        return function (arity, id, task) {
      +            let a_set = when[arity];
      +            let i_set;
      +
      +// The id parameter is optional. If excluded, the task will be applied to all
      +// ids.
      +
      +            if (typeof id !== "string") {
      +                task = id;
      +                id = "(all)";
      +            }
      +
      +// If this arity has no registrations yet, then create a set object to hold
      +// them.
      +
      +            if (a_set === undefined) {
      +                a_set = empty();
      +                when[arity] = a_set;
      +            }
      +
      +// If this id has no registrations yet, then create a set array to hold them.
      +
      +            i_set = a_set[id];
      +            if (i_set === undefined) {
      +                i_set = [];
      +                a_set[id] = i_set;
      +            }
      +
      +// Register the task with the arity and the id.
      +...
      +}
      +
    • +
    • Example usage:
      ...
      +);
      +
      +// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
      +//          recursive traversal. Each node may be processed on the way down
      +//          (preaction) and on the way up (postaction).
      +
      +if (!state.mode_json) {
      +    jslint_phase4_walk(state);
      +}
      +jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
      +jslint_assert(
      +    function_stack.length === 0,
      +    `function_stack.length === 0.`
      +);
      +...
    • + +
    • +

      + 16. + function jslint_phase5_whitage(state) + +

      +
    • +
    • Description and source-code:
      function jslint_phase5_whitage(state) {
      +
      +// PHASE 5. Check whitespace between tokens in <token_list>.
      +
      +    let {
      +        artifact,
      +        catch_list,
      +        function_list,
      +        function_stack,
      +        option_dict,
      +        test_cause,
      +        token_global,
      +        token_list,
      +        warn
      +    } = state;
      +    let closer = "(end)";
      +    let free = false;
      +
      +// free = false
      +
      +// cause:
      +// "()=>0"
      +// "aa()"
      +// "aa(0,0)"
      +// "function(){}"
      +
      +// free = true
      +
      +// cause:
      +// "(0)"
      +// "(aa)"
      +// "aa(0)"
      +// "do{}while()"
      +// "for(){}"
      +// "if(){}"
      +// "switch(){}"
      +// "while(){}"
      +
      +    let left = token_global;
      +    let margin = 0;
      +    let mode_indent = (
      +
      +// PR-330 - Allow 2-space indent.
      +
      +        option_dict.indent2
      +        ? 2
      +        : 4
      +    );
      +    let nr_comments_skipped = 0;
      +    let open = true;
      +    let opening = true;
      +    let right;
      +
      +// This is the set of infix operators that require a space on each side.
      +
      +    let spaceop = object_assign_from_list(empty(), [
      +        "!=", "!==", "%", "%=", "&", "&&", "&=", "*", "*=", "+=", "-=", "/",
      +        "/=", "<", "<<", "<<=", "<=", "=", "==", "===", "=>", ">", ">=", ">>",
      +        ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||"
      +    ], true);
      +
      +    function at_margin(fit) {
      +        const at = margin + fit;
      +        if (right.from !== at) {
      +            return expected_at(at);
      +        }
      +    }
      +
      +    function delve(the_function) {
      +        Object.keys(the_function.context).forEach(function (id) {
      +            const name = the_function.context[id];
      +            if (id !== "ignore" && name.parent === the_function) {
      +
      +// test_cause:
      +// ["function aa(aa) {return aa;}", "delve", "id", "", 0]
      +
      +                test_cause("id");
      +                if (
      +                    name.used === 0
      +
      +// Probably deadcode.
      +// && (
      +//     name.role !== "function"
      +//     || name.parent.arity !== "unary"
      +// )
      +
      +                    && jslint_assert(
      +                        name.role...
      +}
      +
    • +
    • Example usage:
      ...
      +    function_stack.length === 0,
      +    `function_stack.length === 0.`
      +);
      +
      +// PHASE 5. Check whitespace between tokens in <token_list>.
      +
      +if (!state.mode_json && warning_list.length === 0) {
      +    jslint_phase5_whitage(state);
      +}
      +jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
      +jslint_assert(
      +    function_stack.length === 0,
      +    `function_stack.length === 0.`
      +);
      +...
    • + +
    • +

      + 17. + function jslint_report({ + exports, + froms, + functions, + global, + json, + module, + property, + stop, + warnings +}) + +

      +
    • +
    • Description and source-code:
      function jslint_report({
      +    exports,
      +    froms,
      +    functions,
      +    global,
      +    json,
      +    module,
      +    property,
      +    stop,
      +    warnings
      +}) {
      +
      +// This function will create human-readable, html-report
      +// for warnings, properties, and functions from jslint-result-object.
      +//
      +// Example usage:
      +//  let result = jslint("console.log('hello world')");
      +//  let html = jslint_report(result);
      +
      +    let html = "";
      +    let length_80 = 1111;
      +
      +    function address(line = 1, column = 1) {
      +
      +// This function will create HTML address element from <line> and <column>
      +
      +        return `<address>${Number(line)}: ${Number(column)}</address>`;
      +
      +    }
      +
      +    function detail(title, list) {
      +        return (
      +            (Array.isArray(list) && list.length > 0)
      +            ? (
      +
      +// Google Lighthouse Accessibility - <dl>'s do not contain only properly-ordered
      +// <dt> and <dd> groups, <script>, <template> or <div> elements.
      +
      +                "<dl>"
      +                + "<dt>" + htmlEscape(title) + "</dt>"
      +                + "<dd>" + list.join(", ") + "</dd>"
      +                + "</dl>"
      +            )
      +            : ""
      +        );
      +    }
      +
      +    html += String(`
      +<style class="JSLINT_REPORT_STYLE">
      +/* jslint utility2:true */
      +/*csslint box-model: false, ids:false */
      +/*csslint ignore:start*/
      +@font-face {
      +    font-display: swap;
      +    font-family: "Daley";
      +    src: url(
      +"data:font/woff2;base64,d09GMgABAAAAABy4AA4AAAAAThwAABxiAAEAAAAAAAAAAAAA\
      +AAAAAAAAAAAAAAAABmAAgiQINAmcDBEICuc41DEBNgIkA4R2C4I+AAQgBYkuByAMgScfYUIF\
      +7NgjsHGAbcDVFkXZ5Jwd+P96IGPc9rl9ETBEaCzCJkvY2UpziRZ7zftZWk8052U9+NqX6vXL\
      +KDflSQnlJ0bP+QnPQAy744n9mup6H9PaCDFwM5zjf8exB89bZ1cdrYOP0NgnuRDRWlk9u/fE\
      +llkxqmfH8lmRQ/DAmER9opk9wR6suc1LvTiXNEe1vbhUCH2USgnEwH3vUm05JQqejGvZvOtz\
      +7sIKEGgLdDNl/IrfqWVZG/wr42ekomEm91VA1p4LhHBuFzHF8//u7vvbREHMQqGtNLmiOOD/\
      +X7WWiwqyCE98qt0jk5JJmgR5WJJElBmzRb1F7a66MmSLTNWZ2XSHfKBSKHoVteSEJ6EOdvVw\
      +fNZOtXKDe39jXdRlkmMnOWIOFBgeEK/b0mFsgffnP...
      +}
      +
    • +
    • Example usage:
      ...
      +
      +    editor.on("lintJslintAfter", function (options) {
      +
      +// Generate jslint-report from options.result.
      +
      +        document.querySelector(
      +            ".JSLINT_REPORT_"
      +        ).innerHTML = window.jslint.jslint_report(options.result);
      +    });
      +
      +// Manually trigger linter.
      +
      +    editor.performLint();
      +});
      +</script>
      +...
    • + +
    • +

      + 18. + function jstestDescribe(description, testFunction) + +

      +
    • +
    • Description and source-code:
      async function jstestDescribe(description, testFunction) {
      +
      +// This function will create-and-run test-group <testFunction>
      +// with given <description>.
      +
      +    let message;
      +    let result;
      +    let timerTimeout;
      +
      +// Init jstestTimeStart.
      +
      +    if (jstestTimeStart === undefined) {
      +        jstestTimeStart = jstestTimeStart || Date.now();
      +        process.on("exit", jstestOnExit);
      +    }
      +
      +// PR-457 - Wait awhile for imports to initialize.
      +
      +    await new Promise(function (resolve) {
      +        setTimeout(resolve);
      +    });
      +
      +// Init jstestItList.
      +
      +    jstestItList = [];
      +    testFunction();
      +
      +// Wait for jstestItList to resolve.
      +
      +    timerTimeout = setTimeout(noop, 0x7fffffff);
      +    result = await Promise.all(jstestItList);
      +    clearTimeout(timerTimeout);
      +
      +// Print test results.
      +
      +    message = (
      +        "\n  " + (Date.now() - jstestTimeStart) + "ms"
      +        + " - test describe - " + description + "\n"
      +        + result.map(function ([
      +            err, description, mode
      +        ]) {
      +            jstestItCount += 1;
      +            if (err) {
      +                jstestCountFailed += 1;
      +                err = (
      +                    "    \u001b[31m\u2718 " + jstestItCount + ". test it - "
      +                    + description + "\n" + err.stack + "\u001b[39m"
      +                );
      +                if (mode === "pass") {
      +                    jstestCountFailed -= 1;
      +                    err = "";
      +                }
      +            }
      +            return err || (
      +                "    \u001b[32m\u2714 " + jstestItCount + ". test it - "
      +                + description + "\u001b[39m"
      +            );
      +        }).join("\n")
      +    );
      +    console.error(message);
      +}
    • +
    • Example usage:
      ...
      +//     JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n"
      +// );
      +
      +    assertJsonEqual(data1, data2);
      +});
      +});
      +
      +jstestDescribe((
      +"test v8CoverageReportCreate handling-behavior"
      +), function testBehaviorV8CoverageReportCreate() {
      +jstestIt((
      +    "test null-case handling-behavior"
      +), async function () {
      +    await assertErrorThrownAsync(function () {
      +        return v8CoverageReportCreate({});
      +...
    • + +
    • +

      + 19. + function jstestIt(description, testFunction, mode) + +

      +
    • +
    • Description and source-code:
      function jstestIt(description, testFunction, mode) {
      +
      +// This function will create-and-run test-case <testFunction>
      +// inside current test-group with given <description>.
      +
      +    jstestCountTotal += 1;
      +    jstestItList.push(new Promise(async function (resolve) {
      +        let err;
      +        try {
      +            await testFunction();
      +        } catch (errCaught) {
      +            err = errCaught;
      +        }
      +        resolve([err, description, mode]);
      +    }));
      +}
    • +
    • Example usage:
      ...
      +                "v8_coverage_report=" + dir,
      +                "node",
      +                file
      +            ]
      +        });
      +    });
      +});
      +jstestIt((
      +    "test npm handling-behavior"
      +), async function () {
      +    await jslint.jslint_cli({
      +        console_error: noop, // comment to debug
      +        mode_cli: true,
      +        process_argv: [
      +            "node", "jslint.mjs",
      +...
    • + +
    • +

      + 20. + function jstestOnExit(exitCode, mode) + +

      +
    • +
    • Description and source-code:
      function jstestOnExit(exitCode, mode) {
      +
      +// This function will on process-exit, print test-report
      +// and exit with non-zero exit-code if any test failed.
      +
      +    let message = (
      +        (
      +            (jstestCountFailed || mode === "testsFailed")
      +            ? "\n\u001b[31m"
      +            : "\n\u001b[32m"
      +        )
      +        + "  tests total  - " + jstestCountTotal + "\n"
      +        + "  tests failed - " + jstestCountFailed + "\n"
      +        + "\n"
      +        + "  time finished - "
      +        + Number(Date.now() - jstestTimeStart).toLocaleString()
      +        + " ms\n"
      +        + "\u001b[39m"
      +    );
      +    if (mode !== "testsFailed") {
      +        console.error(message);
      +    }
      +    process.exitCode = exitCode || jstestCountFailed;
      +    return message;
      +}
    • +
    • Example usage:
      ...
      +    "test jstestDescribe error handling-behavior"
      +), function () {
      +    throw new Error();
      +}, "pass");
      +jstestIt((
      +    "test jstestOnExit tests-failed handling-behavior"
      +), function () {
      +    jstestOnExit(undefined, "testsFailed");
      +});
      +});
      +
      +jstestDescribe((
      +"test misc handling-behavior"
      +), function testBehaviorMisc() {
      +jstestIt((
      +...
    • + +
    • +

      + 21. + function moduleFsInit() + +

      +
    • +
    • Description and source-code:
      async function moduleFsInit() {
      +
      +// This function will import nodejs builtin-modules if they have not yet been
      +// imported.
      +
      +// State 3 - Modules already imported.
      +
      +    if (moduleFs !== undefined) {
      +        return;
      +    }
      +
      +// State 2 - Wait while modules are importing.
      +
      +    if (moduleFsInitResolveList !== undefined) {
      +        return new Promise(function (resolve) {
      +            moduleFsInitResolveList.push(resolve);
      +        });
      +    }
      +
      +// State 1 - Start importing modules.
      +
      +    moduleFsInitResolveList = [];
      +    [
      +        moduleChildProcess,
      +        moduleFs,
      +        modulePath,
      +        moduleUrl
      +    ] = await Promise.all([
      +        import("child_process"),
      +        import("fs"),
      +        import("path"),
      +        import("url")
      +    ]);
      +    while (moduleFsInitResolveList.length > 0) {
      +        moduleFsInitResolveList.shift()();
      +    }
      +}
    • +
    • Example usage:
      ...
      +let sourceJslintMjs;
      +let testCoverageMergeData;
      +
      +await (async function init() {
      +
      +// Coverage-hack - Ugly-hack to get test-coverage for all initialization-states.
      +
      +moduleFsInit();
      +moduleFsInit();
      +
      +// Cleanup directory .tmp
      +
      +await moduleFs.promises.rm(".tmp", {
      +    recursive: true
      +}).catch(noop);
      +...
    • + +
    • +

      + 22. + function noop(val) + +

      +
    • +
    • Description and source-code:
      function noop(val) {
      +
      +// This function will do nothing except return <val>.
      +
      +    return val;
      +}
    • +
    • Example usage:
      ...
      +jstestDescribe((
      +"test misc handling-behavior"
      +), function testBehaviorMisc() {
      +jstestIt((
      +    "test misc handling-behavior"
      +), async function () {
      +    // test debugInline handling-behavior
      +    noop(debugInline);
      +    // test assertErrorThrownAsync error handling-behavior
      +    await assertErrorThrownAsync(function () {
      +        return assertErrorThrownAsync(noop);
      +    });
      +    // test assertJsonEqual error handling-behavior
      +    await assertErrorThrownAsync(function () {
      +        assertJsonEqual(1, 2);
      +...
    • + +
    • +

      + 23. + function objectDeepCopyWithKeysSorted(obj) + +

      +
    • +
    • Description and source-code:
      function objectDeepCopyWithKeysSorted(obj) {
      +
      +// This function will recursively deep-copy <obj> with keys sorted.
      +
      +    let sorted;
      +    if (typeof obj !== "object" || !obj) {
      +        return obj;
      +    }
      +
      +// Recursively deep-copy list with child-keys sorted.
      +
      +    if (Array.isArray(obj)) {
      +        return obj.map(objectDeepCopyWithKeysSorted);
      +    }
      +
      +// Recursively deep-copy obj with keys sorted.
      +
      +    sorted = Object.create(null);
      +    Object.keys(obj).sort().forEach(function (key) {
      +        sorted[key] = objectDeepCopyWithKeysSorted(obj[key]);
      +    });
      +    return sorted;
      +}
    • +
    • Example usage:
      ...
      +        ];
      +        data1 = v8CoverageListMerge(data1);
      +        data1 = v8CoverageListMerge([data1]);
      +
      +// Debug data1.
      +// await moduleFs.promises.writeFile(
      +//     ".test_v8_coverage_node_sqlite_merged.json",
      +//     JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n"
      +// );
      +
      +        assertJsonEqual(data1, data2);
      +    });
      +});
      +
      +jstestDescribe((
      +...
    • + +
    • +

      + 24. + function v8CoverageListMerge(processCovs) + +

      +
    • +
    • Description and source-code:
      function v8CoverageListMerge(processCovs) {
      +
      +// This function is derived from MIT Licensed v8-coverage at
      +// https://github.com/demurgos/v8-coverage/tree/master/ts
      +// https://github.com/demurgos/v8-coverage/blob/master/ts/LICENSE.md
      +//
      +// Merges a list of v8 process coverages.
      +// The result is normalized.
      +// The input values may be mutated, it is not safe to use them after passing
      +// them to this function.
      +// The computation is synchronous.
      +// @param processCovs Process coverages to merge.
      +// @return Merged process coverage.
      +
      +    let resultMerged = [];      // List of merged scripts from processCovs.
      +    let urlToScriptDict = new Map();    // Map scriptCov.url to scriptCovs.
      +
      +    function compareRangeList(aa, bb) {
      +
      +// Compares two range coverages.
      +// The ranges are first ordered by ascending `startOffset` and then by
      +// descending `endOffset`.
      +// This corresponds to a pre-order tree traversal.
      +
      +        if (aa.startOffset !== bb.startOffset) {
      +            return aa.startOffset - bb.startOffset;
      +        }
      +        return bb.endOffset - aa.endOffset;
      +    }
      +
      +    function dictKeyValueAppend(dict, key, val) {
      +
      +// This function will append <val> to list <dict>[<key>].
      +
      +        let list = dict.get(key);
      +        if (list === undefined) {
      +            list = [];
      +            dict.set(key, list);
      +        }
      +        list.push(val);
      +    }
      +
      +    function mergeTreeList(parentTrees) {
      +
      +// This function will return RangeTree object with <parentTrees> merged into
      +// property-children.
      +// @precondition Same `start` and `end` for all the parentTrees
      +
      +        if (parentTrees.length <= 1) {
      +            return parentTrees[0];
      +        }
      +
      +// new RangeTree().
      +
      +        return {
      +
      +// Merge parentTrees into property-children.
      +
      +            children: mergeTreeListToChildren(parentTrees),
      +            delta: parentTrees.reduce(function (aa, bb) {
      +                return aa + bb.delta;
      +            }, 0),
      +            end: parentTrees[0].end,
      +            start: parentTrees[0].start
      +   ...
      +}
      +
    • +
    • Example usage:
      ...
      +            "test_v8_coverage_node_sqlite_13216_1633662333140_0.json"
      +        ].map(function (file) {
      +            return testCoverageMergeData[file];
      +        });
      +        let data2 = testCoverageMergeData[
      +            "test_v8_coverage_node_sqlite_merged.json"
      +        ];
      +        data1 = v8CoverageListMerge(data1);
      +        data1 = v8CoverageListMerge([data1]);
      +
      +// Debug data1.
      +// await moduleFs.promises.writeFile(
      +//     ".test_v8_coverage_node_sqlite_merged.json",
      +//     JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n"
      +// );
      +...
    • + +
    • +

      + 25. + function v8CoverageReportCreate({ + consoleError, + coverageDir, + processArgv = [] +}) + +

      +
    • +
    • Description and source-code:
      async function v8CoverageReportCreate({
      +    consoleError,
      +    coverageDir,
      +    processArgv = []
      +}) {
      +
      +// This function will create html-coverage-reports directly from
      +// v8-coverage-files in <coverageDir>.
      +// 1. Spawn node.js program <processArgv> with NODE_V8_COVERAGE.
      +// 2. Merge JSON v8-coverage-files in <coverageDir>.
      +// 3. Create html-coverage-reports in <coverageDir>.
      +
      +    let cwd;
      +    let excludeList = [];
      +    let exitCode = 0;
      +    let fileDict;
      +    let includeList = [];
      +    let modeIncludeNodeModules;
      +    let processArgElem;
      +    let promiseList = [];
      +    let v8CoverageObj;
      +
      +    function htmlRender({
      +        fileList,
      +        lineList,
      +        modeIndex,
      +        pathname
      +    }) {
      +        let html;
      +        let padLines;
      +        let padPathname;
      +        let txt;
      +        let txtBorder;
      +        html = "";
      +        html += String(`
      +<!DOCTYPE html>
      +<html lang="en">
      +<head>
      +<title>V8 Coverage Report</title>
      +<style>
      +/* jslint utility2:true */
      +/*csslint ignore:start*/
      +.coverage,
      +.coverage a,
      +.coverage div,
      +.coverage pre,
      +.coverage span,
      +.coverage table,
      +.coverage tbody,
      +.coverage td,
      +.coverage th,
      +.coverage thead,
      +.coverage tr {
      +    box-sizing: border-box;
      +    font-family: monospace;
      +}
      +/*csslint ignore:end*/
      +
      +/* css - coverage_report - general */
      +body {
      +    margin: 0;
      +}
      +.coverage pre {
      +    margin: 5px 0;
      +}
      +.coverage table {
      +    border-collapse: collapse;
      +}
      +.coverage td,
      +.coverage th {
      +    border: 1px solid #777;
      +    line-height: 20px;
      +    margin: 0;
      +    padding: 5px 10px;
      +}
      +.coverage td span {
      +    display: inline-block;
      +    width: 100%;
      +}
      +.coverage .content {
      +    padding: 0 5px;
      +}
      +.coverage .content a {
      +    text-decoration: none;
      +}
      +.coverage .count {
      +    margin: 0 5px;
      +    padding: 0 5px;
      +}
      +.coverage .footer,
      +.coverage .header {
      +    padding: 20px;
      +}
      +.coverage .footer {
      +    text-align: center;
      +}
      +.coverage .percentbar {
      +    height: 12px;
      +    margin: 2px 0;
      +    min-width: 200px;
      +    position: relative;
      +    width: 100%;...
      +}
      +
    • +
    • Example usage:
      ...
      +
      +/*jslint node*/
      +import jslint from "../jslint.mjs";
      +(async function () {
      +
      +// Create V8 coverage report from program `npm run test` in javascript.
      +
      +await jslint.v8CoverageReportCreate({
      +    coverageDir: "../.artifact/coverage_sqlite3_js/",
      +    processArgv: [
      +        "--exclude=tes?/",
      +        "--exclude=tes[!0-9A-Z_a-z-]/",
      +        "--exclude=tes[0-9A-Z_a-z-]/",
      +        "--exclude=tes[^0-9A-Z_a-z-]/",
      +        "--exclude=test/**/*.js",
      +...
    • + +
    • +

      + 26. + string jslint_charset_ascii + +

      +
    • + +
    • +

      + 27. + string jslint_edition + +

      +
    • + +
    +
    + +
    + [ + This document was created with + JSLint + ] +
    +
    + + diff --git a/asset-codemirror-5.58.3-rollup.css b/asset-codemirror-5.58.3-rollup.css new file mode 100644 index 000000000..26c68b54d --- /dev/null +++ b/asset-codemirror-5.58.3-rollup.css @@ -0,0 +1,378 @@ +/*jslint-disable*/ +/* +shRawLibFetch +{ + "fetchList": [ + { + "url": "https://github.com/codemirror/CodeMirror/blob/5.58.3/lib/codemirror.css" + } + ] +} +*/ + + +/* +repo https://github.com/codemirror/CodeMirror/tree/5.58.3 +committed 2020-11-19T08:38:15Z +*/ + + +/* +file https://github.com/codemirror/CodeMirror/blob/5.58.3/lib/codemirror.css +*/ +/* BASICS */ + +.CodeMirror { + /* Set height, width, borders, and global font properties here */ + font-family: monospace; + height: 300px; + color: black; + direction: ltr; +} + +/* PADDING */ + +.CodeMirror-lines { + padding: 4px 0; /* Vertical padding around content */ +} +.CodeMirror pre.CodeMirror-line, +.CodeMirror pre.CodeMirror-line-like { + padding: 0 4px; /* Horizontal padding of content */ +} + +.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { + background-color: white; /* The little square between H and V scrollbars */ +} + +/* GUTTER */ + +.CodeMirror-gutters { + border-right: 1px solid #ddd; + background-color: #f7f7f7; + white-space: nowrap; +} +.CodeMirror-linenumbers {} +.CodeMirror-linenumber { + padding: 0 3px 0 5px; + min-width: 20px; + text-align: right; + color: #999; + white-space: nowrap; +} + +.CodeMirror-guttermarker { color: black; } +.CodeMirror-guttermarker-subtle { color: #999; } + +/* CURSOR */ + +.CodeMirror-cursor { + border-left: 1px solid black; + border-right: none; + width: 0; +} +/* Shown when moving in bi-directional text */ +.CodeMirror div.CodeMirror-secondarycursor { + border-left: 1px solid silver; +} +.cm-fat-cursor .CodeMirror-cursor { + width: auto; + border: 0 !important; + background: #7e7; +} +.cm-fat-cursor div.CodeMirror-cursors { + z-index: 1; +} +.cm-fat-cursor-mark { + background-color: rgba(20, 255, 20, 0.5); + -webkit-animation: blink 1.06s steps(1) infinite; + -moz-animation: blink 1.06s steps(1) infinite; + animation: blink 1.06s steps(1) infinite; +} +.cm-animate-fat-cursor { + width: auto; + border: 0; + -webkit-animation: blink 1.06s steps(1) infinite; + -moz-animation: blink 1.06s steps(1) infinite; + animation: blink 1.06s steps(1) infinite; + background-color: #7e7; +} +@-moz-keyframes blink { + 0% {} + 50% { background-color: transparent; } + 100% {} +} +@-webkit-keyframes blink { + 0% {} + 50% { background-color: transparent; } + 100% {} +} +@keyframes blink { + 0% {} + 50% { background-color: transparent; } + 100% {} +} + +/* Can style cursor different in overwrite (non-insert) mode */ +.CodeMirror-overwrite .CodeMirror-cursor {} + +.cm-tab { display: inline-block; text-decoration: inherit; } + +.CodeMirror-rulers { + position: absolute; + left: 0; right: 0; top: -50px; bottom: 0; + overflow: hidden; +} +.CodeMirror-ruler { + border-left: 1px solid #ccc; + top: 0; bottom: 0; + position: absolute; +} + +/* DEFAULT THEME */ + +.cm-s-default .cm-header {color: blue;} +.cm-s-default .cm-quote {color: #090;} +.cm-negative {color: #d44;} +.cm-positive {color: #292;} +.cm-header, .cm-strong {font-weight: bold;} +.cm-em {font-style: italic;} +.cm-link {text-decoration: underline;} +.cm-strikethrough {text-decoration: line-through;} + +.cm-s-default .cm-keyword {color: #708;} +.cm-s-default .cm-atom {color: #219;} +.cm-s-default .cm-number {color: #164;} +.cm-s-default .cm-def {color: #00f;} +.cm-s-default .cm-variable, +.cm-s-default .cm-punctuation, +.cm-s-default .cm-property, +.cm-s-default .cm-operator {} +.cm-s-default .cm-variable-2 {color: #05a;} +.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;} +.cm-s-default .cm-comment {color: #a50;} +.cm-s-default .cm-string {color: #a11;} +.cm-s-default .cm-string-2 {color: #f50;} +.cm-s-default .cm-meta {color: #555;} +.cm-s-default .cm-qualifier {color: #555;} +.cm-s-default .cm-builtin {color: #30a;} +.cm-s-default .cm-bracket {color: #997;} +.cm-s-default .cm-tag {color: #170;} +.cm-s-default .cm-attribute {color: #00c;} +.cm-s-default .cm-hr {color: #999;} +.cm-s-default .cm-link {color: #00c;} + +.cm-s-default .cm-error {color: #f00;} +.cm-invalidchar {color: #f00;} + +.CodeMirror-composing { border-bottom: 2px solid; } + +/* Default styles for common addons */ + +div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;} +div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} +.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } +.CodeMirror-activeline-background {background: #e8f2ff;} + +/* STOP */ + +/* The rest of this file contains styles related to the mechanics of + the editor. You probably shouldn't touch them. */ + +.CodeMirror { + position: relative; + overflow: hidden; + background: white; +} + +.CodeMirror-scroll { + overflow: scroll !important; /* Things will break if this is overridden */ + /* 50px is the magic margin used to hide the element's real scrollbars */ + /* See overflow: hidden in .CodeMirror */ + margin-bottom: -50px; margin-right: -50px; + padding-bottom: 50px; + height: 100%; + outline: none; /* Prevent dragging from highlighting the element */ + position: relative; +} +.CodeMirror-sizer { + position: relative; + border-right: 50px solid transparent; +} + +/* The fake, visible scrollbars. Used to force redraw during scrolling + before actual scrolling happens, thus preventing shaking and + flickering artifacts. */ +.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { + position: absolute; + z-index: 6; + display: none; + outline: none; +} +.CodeMirror-vscrollbar { + right: 0; top: 0; + overflow-x: hidden; + overflow-y: scroll; +} +.CodeMirror-hscrollbar { + bottom: 0; left: 0; + overflow-y: hidden; + overflow-x: scroll; +} +.CodeMirror-scrollbar-filler { + right: 0; bottom: 0; +} +.CodeMirror-gutter-filler { + left: 0; bottom: 0; +} + +.CodeMirror-gutters { + position: absolute; left: 0; top: 0; + min-height: 100%; + z-index: 3; +} +.CodeMirror-gutter { + white-space: normal; + height: 100%; + display: inline-block; + vertical-align: top; + margin-bottom: -50px; +} +.CodeMirror-gutter-wrapper { + position: absolute; + z-index: 4; + background: none !important; + border: none !important; +} +.CodeMirror-gutter-background { + position: absolute; + top: 0; bottom: 0; + z-index: 4; +} +.CodeMirror-gutter-elt { + position: absolute; + cursor: default; + z-index: 4; +} +.CodeMirror-gutter-wrapper ::selection { background-color: transparent } +.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent } + +.CodeMirror-lines { + cursor: text; + min-height: 1px; /* prevents collapsing before first draw */ +} +.CodeMirror pre.CodeMirror-line, +.CodeMirror pre.CodeMirror-line-like { + /* Reset some styles that the rest of the page might have set */ + -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; + border-width: 0; + background: transparent; + font-family: inherit; + font-size: inherit; + margin: 0; + white-space: pre; + word-wrap: normal; + line-height: inherit; + color: inherit; + z-index: 2; + position: relative; + overflow: visible; + -webkit-tap-highlight-color: transparent; + -webkit-font-variant-ligatures: contextual; + font-variant-ligatures: contextual; +} +.CodeMirror-wrap pre.CodeMirror-line, +.CodeMirror-wrap pre.CodeMirror-line-like { + word-wrap: break-word; + white-space: pre-wrap; + word-break: normal; +} + +.CodeMirror-linebackground { + position: absolute; + left: 0; right: 0; top: 0; bottom: 0; + z-index: 0; +} + +.CodeMirror-linewidget { + position: relative; + z-index: 2; + padding: 0.1px; /* Force widget margins to stay inside of the container */ +} + +.CodeMirror-widget {} + +.CodeMirror-rtl pre { direction: rtl; } + +.CodeMirror-code { + outline: none; +} + +/* Force content-box sizing for the elements where we expect it */ +.CodeMirror-scroll, +.CodeMirror-sizer, +.CodeMirror-gutter, +.CodeMirror-gutters, +.CodeMirror-linenumber { + -moz-box-sizing: content-box; + box-sizing: content-box; +} + +.CodeMirror-measure { + position: absolute; + width: 100%; + height: 0; + overflow: hidden; + visibility: hidden; +} + +.CodeMirror-cursor { + position: absolute; + pointer-events: none; +} +.CodeMirror-measure pre { position: static; } + +div.CodeMirror-cursors { + visibility: hidden; + position: relative; + z-index: 3; +} +div.CodeMirror-dragcursors { + visibility: visible; +} + +.CodeMirror-focused div.CodeMirror-cursors { + visibility: visible; +} + +.CodeMirror-selected { background: #d9d9d9; } +.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } +.CodeMirror-crosshair { cursor: crosshair; } +.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } +.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } + +.cm-searching { + background-color: #ffa; + background-color: rgba(255, 255, 0, .4); +} + +/* Used to force a border model for a node */ +.cm-force-border { padding-right: .1px; } + +@media print { + /* Hide the cursor when printing */ + .CodeMirror div.CodeMirror-cursors { + visibility: hidden; + } +} + +/* See issue #2901 */ +.cm-tab-wrap-hack:after { content: ''; } + +/* Help users use markselection to safely style text background */ +span.CodeMirror-selectedtext { background: none; } + + +/* +file none +*/ +/*jslint-enable*/ diff --git a/asset-codemirror-5.58.3-rollup.js b/asset-codemirror-5.58.3-rollup.js new file mode 100644 index 000000000..1c84e2f6e --- /dev/null +++ b/asset-codemirror-5.58.3-rollup.js @@ -0,0 +1,10966 @@ +/*jslint-disable*/ +/* +shRawLibFetch +{ + "fetchList": [ + { + "url": "https://github.com/codemirror/CodeMirror/blob/5.58.3/codemirror.js", + "url2": "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.3/codemirror.js" + }, + { + "url": "https://github.com/codemirror/CodeMirror/blob/5.58.3/addon/edit/matchbrackets.js" + }, + { + "url": "https://github.com/codemirror/CodeMirror/blob/5.58.3/addon/edit/trailingspace.js" + }, + { + "url": "https://github.com/codemirror/CodeMirror/blob/5.58.3/mode/javascript/javascript.js" + } + ] +} +*/ + + +/* +repo https://github.com/codemirror/CodeMirror/tree/5.58.3 +committed 2020-11-19T08:38:15Z +*/ + + +/* +file https://github.com/codemirror/CodeMirror/blob/5.58.3/codemirror.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +// This is CodeMirror (https://codemirror.net), a code editor +// implemented in JavaScript on top of the browser's DOM. +// +// You can find some technical background for some of the code below +// at http://marijnhaverbeke.nl/blog/#cm-internals . + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, global.CodeMirror = factory()); +}(this, (function () { 'use strict'; + + // Kludges for bugs and behavior differences that can't be feature + // detected are enabled based on userAgent etc sniffing. + var userAgent = navigator.userAgent; + var platform = navigator.platform; + + var gecko = /gecko\/\d/i.test(userAgent); + var ie_upto10 = /MSIE \d/.test(userAgent); + var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent); + var edge = /Edge\/(\d+)/.exec(userAgent); + var ie = ie_upto10 || ie_11up || edge; + var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]); + var webkit = !edge && /WebKit\//.test(userAgent); + var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent); + var chrome = !edge && /Chrome\//.test(userAgent); + var presto = /Opera\//.test(userAgent); + var safari = /Apple Computer/.test(navigator.vendor); + var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent); + var phantom = /PhantomJS/.test(userAgent); + + var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent); + var android = /Android/.test(userAgent); + // This is woefully incomplete. Suggestions for alternative methods welcome. + var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent); + var mac = ios || /Mac/.test(platform); + var chromeOS = /\bCrOS\b/.test(userAgent); + var windows = /win/i.test(platform); + + var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/); + if (presto_version) { presto_version = Number(presto_version[1]); } + if (presto_version && presto_version >= 15) { presto = false; webkit = true; } + // Some browsers use the wrong event properties to signal cmd/ctrl on OS X + var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11)); + var captureRightClick = gecko || (ie && ie_version >= 9); + + function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") } + + var rmClass = function(node, cls) { + var current = node.className; + var match = classTest(cls).exec(current); + if (match) { + var after = current.slice(match.index + match[0].length); + node.className = current.slice(0, match.index) + (after ? match[1] + after : ""); + } + }; + + function removeChildren(e) { + for (var count = e.childNodes.length; count > 0; --count) + { e.removeChild(e.firstChild); } + return e + } + + function removeChildrenAndAdd(parent, e) { + return removeChildren(parent).appendChild(e) + } + + function elt(tag, content, className, style) { + var e = document.createElement(tag); + if (className) { e.className = className; } + if (style) { e.style.cssText = style; } + if (typeof content == "string") { e.appendChild(document.createTextNode(content)); } + else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } } + return e + } + // wrapper for elt, which removes the elt from the accessibility tree + function eltP(tag, content, className, style) { + var e = elt(tag, content, className, style); + e.setAttribute("role", "presentation"); + return e + } + + var range; + if (document.createRange) { range = function(node, start, end, endNode) { + var r = document.createRange(); + r.setEnd(endNode || node, end); + r.setStart(node, start); + return r + }; } + else { range = function(node, start, end) { + var r = document.body.createTextRange(); + try { r.moveToElementText(node.parentNode); } + catch(e) { return r } + r.collapse(true); + r.moveEnd("character", end); + r.moveStart("character", start); + return r + }; } + + function contains(parent, child) { + if (child.nodeType == 3) // Android browser always returns false when child is a textnode + { child = child.parentNode; } + if (parent.contains) + { return parent.contains(child) } + do { + if (child.nodeType == 11) { child = child.host; } + if (child == parent) { return true } + } while (child = child.parentNode) + } + + function activeElt() { + // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement. + // IE < 10 will throw when accessed while the page is loading or in an iframe. + // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable. + var activeElement; + try { + activeElement = document.activeElement; + } catch(e) { + activeElement = document.body || null; + } + while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement) + { activeElement = activeElement.shadowRoot.activeElement; } + return activeElement + } + + function addClass(node, cls) { + var current = node.className; + if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; } + } + function joinClasses(a, b) { + var as = a.split(" "); + for (var i = 0; i < as.length; i++) + { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } } + return b + } + + var selectInput = function(node) { node.select(); }; + if (ios) // Mobile Safari apparently has a bug where select() is broken. + { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; } + else if (ie) // Suppress mysterious IE10 errors + { selectInput = function(node) { try { node.select(); } catch(_e) {} }; } + + function bind(f) { + var args = Array.prototype.slice.call(arguments, 1); + return function(){return f.apply(null, args)} + } + + function copyObj(obj, target, overwrite) { + if (!target) { target = {}; } + for (var prop in obj) + { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop))) + { target[prop] = obj[prop]; } } + return target + } + + // Counts the column offset in a string, taking tabs into account. + // Used mostly to find indentation. + function countColumn(string, end, tabSize, startIndex, startValue) { + if (end == null) { + end = string.search(/[^\s\u00a0]/); + if (end == -1) { end = string.length; } + } + for (var i = startIndex || 0, n = startValue || 0;;) { + var nextTab = string.indexOf("\t", i); + if (nextTab < 0 || nextTab >= end) + { return n + (end - i) } + n += nextTab - i; + n += tabSize - (n % tabSize); + i = nextTab + 1; + } + } + + var Delayed = function() { + this.id = null; + this.f = null; + this.time = 0; + this.handler = bind(this.onTimeout, this); + }; + Delayed.prototype.onTimeout = function (self) { + self.id = 0; + if (self.time <= +new Date) { + self.f(); + } else { + setTimeout(self.handler, self.time - +new Date); + } + }; + Delayed.prototype.set = function (ms, f) { + this.f = f; + var time = +new Date + ms; + if (!this.id || time < this.time) { + clearTimeout(this.id); + this.id = setTimeout(this.handler, ms); + this.time = time; + } + }; + + function indexOf(array, elt) { + for (var i = 0; i < array.length; ++i) + { if (array[i] == elt) { return i } } + return -1 + } + + // Number of pixels added to scroller and sizer to hide scrollbar + var scrollerGap = 50; + + // Returned or thrown by various protocols to signal 'I'm not + // handling this'. + var Pass = {toString: function(){return "CodeMirror.Pass"}}; + + // Reused option objects for setSelection & friends + var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"}; + + // The inverse of countColumn -- find the offset that corresponds to + // a particular column. + function findColumn(string, goal, tabSize) { + for (var pos = 0, col = 0;;) { + var nextTab = string.indexOf("\t", pos); + if (nextTab == -1) { nextTab = string.length; } + var skipped = nextTab - pos; + if (nextTab == string.length || col + skipped >= goal) + { return pos + Math.min(skipped, goal - col) } + col += nextTab - pos; + col += tabSize - (col % tabSize); + pos = nextTab + 1; + if (col >= goal) { return pos } + } + } + + var spaceStrs = [""]; + function spaceStr(n) { + while (spaceStrs.length <= n) + { spaceStrs.push(lst(spaceStrs) + " "); } + return spaceStrs[n] + } + + function lst(arr) { return arr[arr.length-1] } + + function map(array, f) { + var out = []; + for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); } + return out + } + + function insertSorted(array, value, score) { + var pos = 0, priority = score(value); + while (pos < array.length && score(array[pos]) <= priority) { pos++; } + array.splice(pos, 0, value); + } + + function nothing() {} + + function createObj(base, props) { + var inst; + if (Object.create) { + inst = Object.create(base); + } else { + nothing.prototype = base; + inst = new nothing(); + } + if (props) { copyObj(props, inst); } + return inst + } + + var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; + function isWordCharBasic(ch) { + return /\w/.test(ch) || ch > "\x80" && + (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)) + } + function isWordChar(ch, helper) { + if (!helper) { return isWordCharBasic(ch) } + if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true } + return helper.test(ch) + } + + function isEmpty(obj) { + for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } } + return true + } + + // Extending unicode characters. A series of a non-extending char + + // any number of extending chars is treated as a single unit as far + // as editing and measuring is concerned. This is not fully correct, + // since some scripts/fonts/browsers also treat other configurations + // of code points as a group. + var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/; + function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) } + + // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range. + function skipExtendingChars(str, pos, dir) { + while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; } + return pos + } + + // Returns the value from the range [`from`; `to`] that satisfies + // `pred` and is closest to `from`. Assumes that at least `to` + // satisfies `pred`. Supports `from` being greater than `to`. + function findFirst(pred, from, to) { + // At any point we are certain `to` satisfies `pred`, don't know + // whether `from` does. + var dir = from > to ? -1 : 1; + for (;;) { + if (from == to) { return from } + var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF); + if (mid == from) { return pred(mid) ? from : to } + if (pred(mid)) { to = mid; } + else { from = mid + dir; } + } + } + + // BIDI HELPERS + + function iterateBidiSections(order, from, to, f) { + if (!order) { return f(from, to, "ltr", 0) } + var found = false; + for (var i = 0; i < order.length; ++i) { + var part = order[i]; + if (part.from < to && part.to > from || from == to && part.to == from) { + f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i); + found = true; + } + } + if (!found) { f(from, to, "ltr"); } + } + + var bidiOther = null; + function getBidiPartAt(order, ch, sticky) { + var found; + bidiOther = null; + for (var i = 0; i < order.length; ++i) { + var cur = order[i]; + if (cur.from < ch && cur.to > ch) { return i } + if (cur.to == ch) { + if (cur.from != cur.to && sticky == "before") { found = i; } + else { bidiOther = i; } + } + if (cur.from == ch) { + if (cur.from != cur.to && sticky != "before") { found = i; } + else { bidiOther = i; } + } + } + return found != null ? found : bidiOther + } + + // Bidirectional ordering algorithm + // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm + // that this (partially) implements. + + // One-char codes used for character types: + // L (L): Left-to-Right + // R (R): Right-to-Left + // r (AL): Right-to-Left Arabic + // 1 (EN): European Number + // + (ES): European Number Separator + // % (ET): European Number Terminator + // n (AN): Arabic Number + // , (CS): Common Number Separator + // m (NSM): Non-Spacing Mark + // b (BN): Boundary Neutral + // s (B): Paragraph Separator + // t (S): Segment Separator + // w (WS): Whitespace + // N (ON): Other Neutrals + + // Returns null if characters are ordered as they appear + // (left-to-right), or an array of sections ({from, to, level} + // objects) in the order in which they occur visually. + var bidiOrdering = (function() { + // Character types for codepoints 0 to 0xff + var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"; + // Character types for codepoints 0x600 to 0x6f9 + var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111"; + function charType(code) { + if (code <= 0xf7) { return lowTypes.charAt(code) } + else if (0x590 <= code && code <= 0x5f4) { return "R" } + else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) } + else if (0x6ee <= code && code <= 0x8ac) { return "r" } + else if (0x2000 <= code && code <= 0x200b) { return "w" } + else if (code == 0x200c) { return "b" } + else { return "L" } + } + + var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/; + var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/; + + function BidiSpan(level, from, to) { + this.level = level; + this.from = from; this.to = to; + } + + return function(str, direction) { + var outerType = direction == "ltr" ? "L" : "R"; + + if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false } + var len = str.length, types = []; + for (var i = 0; i < len; ++i) + { types.push(charType(str.charCodeAt(i))); } + + // W1. Examine each non-spacing mark (NSM) in the level run, and + // change the type of the NSM to the type of the previous + // character. If the NSM is at the start of the level run, it will + // get the type of sor. + for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) { + var type = types[i$1]; + if (type == "m") { types[i$1] = prev; } + else { prev = type; } + } + + // W2. Search backwards from each instance of a European number + // until the first strong type (R, L, AL, or sor) is found. If an + // AL is found, change the type of the European number to Arabic + // number. + // W3. Change all ALs to R. + for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) { + var type$1 = types[i$2]; + if (type$1 == "1" && cur == "r") { types[i$2] = "n"; } + else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } } + } + + // W4. A single European separator between two European numbers + // changes to a European number. A single common separator between + // two numbers of the same type changes to that type. + for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) { + var type$2 = types[i$3]; + if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; } + else if (type$2 == "," && prev$1 == types[i$3+1] && + (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; } + prev$1 = type$2; + } + + // W5. A sequence of European terminators adjacent to European + // numbers changes to all European numbers. + // W6. Otherwise, separators and terminators change to Other + // Neutral. + for (var i$4 = 0; i$4 < len; ++i$4) { + var type$3 = types[i$4]; + if (type$3 == ",") { types[i$4] = "N"; } + else if (type$3 == "%") { + var end = (void 0); + for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {} + var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"; + for (var j = i$4; j < end; ++j) { types[j] = replace; } + i$4 = end - 1; + } + } + + // W7. Search backwards from each instance of a European number + // until the first strong type (R, L, or sor) is found. If an L is + // found, then change the type of the European number to L. + for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) { + var type$4 = types[i$5]; + if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; } + else if (isStrong.test(type$4)) { cur$1 = type$4; } + } + + // N1. A sequence of neutrals takes the direction of the + // surrounding strong text if the text on both sides has the same + // direction. European and Arabic numbers act as if they were R in + // terms of their influence on neutrals. Start-of-level-run (sor) + // and end-of-level-run (eor) are used at level run boundaries. + // N2. Any remaining neutrals take the embedding direction. + for (var i$6 = 0; i$6 < len; ++i$6) { + if (isNeutral.test(types[i$6])) { + var end$1 = (void 0); + for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {} + var before = (i$6 ? types[i$6-1] : outerType) == "L"; + var after = (end$1 < len ? types[end$1] : outerType) == "L"; + var replace$1 = before == after ? (before ? "L" : "R") : outerType; + for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; } + i$6 = end$1 - 1; + } + } + + // Here we depart from the documented algorithm, in order to avoid + // building up an actual levels array. Since there are only three + // levels (0, 1, 2) in an implementation that doesn't take + // explicit embedding into account, we can build up the order on + // the fly, without following the level-based algorithm. + var order = [], m; + for (var i$7 = 0; i$7 < len;) { + if (countsAsLeft.test(types[i$7])) { + var start = i$7; + for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {} + order.push(new BidiSpan(0, start, i$7)); + } else { + var pos = i$7, at = order.length, isRTL = direction == "rtl" ? 1 : 0; + for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {} + for (var j$2 = pos; j$2 < i$7;) { + if (countsAsNum.test(types[j$2])) { + if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); at += isRTL; } + var nstart = j$2; + for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {} + order.splice(at, 0, new BidiSpan(2, nstart, j$2)); + at += isRTL; + pos = j$2; + } else { ++j$2; } + } + if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); } + } + } + if (direction == "ltr") { + if (order[0].level == 1 && (m = str.match(/^\s+/))) { + order[0].from = m[0].length; + order.unshift(new BidiSpan(0, 0, m[0].length)); + } + if (lst(order).level == 1 && (m = str.match(/\s+$/))) { + lst(order).to -= m[0].length; + order.push(new BidiSpan(0, len - m[0].length, len)); + } + } + + return direction == "rtl" ? order.reverse() : order + } + })(); + + // Get the bidi ordering for the given line (and cache it). Returns + // false for lines that are fully left-to-right, and an array of + // BidiSpan objects otherwise. + function getOrder(line, direction) { + var order = line.order; + if (order == null) { order = line.order = bidiOrdering(line.text, direction); } + return order + } + + // EVENT HANDLING + + // Lightweight event framework. on/off also work on DOM nodes, + // registering native DOM handlers. + + var noHandlers = []; + + var on = function(emitter, type, f) { + if (emitter.addEventListener) { + emitter.addEventListener(type, f, false); + } else if (emitter.attachEvent) { + emitter.attachEvent("on" + type, f); + } else { + var map = emitter._handlers || (emitter._handlers = {}); + map[type] = (map[type] || noHandlers).concat(f); + } + }; + + function getHandlers(emitter, type) { + return emitter._handlers && emitter._handlers[type] || noHandlers + } + + function off(emitter, type, f) { + if (emitter.removeEventListener) { + emitter.removeEventListener(type, f, false); + } else if (emitter.detachEvent) { + emitter.detachEvent("on" + type, f); + } else { + var map = emitter._handlers, arr = map && map[type]; + if (arr) { + var index = indexOf(arr, f); + if (index > -1) + { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)); } + } + } + } + + function signal(emitter, type /*, values...*/) { + var handlers = getHandlers(emitter, type); + if (!handlers.length) { return } + var args = Array.prototype.slice.call(arguments, 2); + for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); } + } + + // The DOM events that CodeMirror handles can be overridden by + // registering a (non-DOM) handler on the editor for the event name, + // and preventDefault-ing the event in that handler. + function signalDOMEvent(cm, e, override) { + if (typeof e == "string") + { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; } + signal(cm, override || e.type, cm, e); + return e_defaultPrevented(e) || e.codemirrorIgnore + } + + function signalCursorActivity(cm) { + var arr = cm._handlers && cm._handlers.cursorActivity; + if (!arr) { return } + var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []); + for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1) + { set.push(arr[i]); } } + } + + function hasHandler(emitter, type) { + return getHandlers(emitter, type).length > 0 + } + + // Add on and off methods to a constructor's prototype, to make + // registering events on such objects more convenient. + function eventMixin(ctor) { + ctor.prototype.on = function(type, f) {on(this, type, f);}; + ctor.prototype.off = function(type, f) {off(this, type, f);}; + } + + // Due to the fact that we still support jurassic IE versions, some + // compatibility wrappers are needed. + + function e_preventDefault(e) { + if (e.preventDefault) { e.preventDefault(); } + else { e.returnValue = false; } + } + function e_stopPropagation(e) { + if (e.stopPropagation) { e.stopPropagation(); } + else { e.cancelBubble = true; } + } + function e_defaultPrevented(e) { + return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false + } + function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);} + + function e_target(e) {return e.target || e.srcElement} + function e_button(e) { + var b = e.which; + if (b == null) { + if (e.button & 1) { b = 1; } + else if (e.button & 2) { b = 3; } + else if (e.button & 4) { b = 2; } + } + if (mac && e.ctrlKey && b == 1) { b = 3; } + return b + } + + // Detect drag-and-drop + var dragAndDrop = function() { + // There is *some* kind of drag-and-drop support in IE6-8, but I + // couldn't get it to work yet. + if (ie && ie_version < 9) { return false } + var div = elt('div'); + return "draggable" in div || "dragDrop" in div + }(); + + var zwspSupported; + function zeroWidthElement(measure) { + if (zwspSupported == null) { + var test = elt("span", "\u200b"); + removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")])); + if (measure.firstChild.offsetHeight != 0) + { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); } + } + var node = zwspSupported ? elt("span", "\u200b") : + elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px"); + node.setAttribute("cm-text", ""); + return node + } + + // Feature-detect IE's crummy client rect reporting for bidi text + var badBidiRects; + function hasBadBidiRects(measure) { + if (badBidiRects != null) { return badBidiRects } + var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA")); + var r0 = range(txt, 0, 1).getBoundingClientRect(); + var r1 = range(txt, 1, 2).getBoundingClientRect(); + removeChildren(measure); + if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780) + return badBidiRects = (r1.right - r0.right < 3) + } + + // See if "".split is the broken IE version, if so, provide an + // alternative way to split lines. + var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) { + var pos = 0, result = [], l = string.length; + while (pos <= l) { + var nl = string.indexOf("\n", pos); + if (nl == -1) { nl = string.length; } + var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl); + var rt = line.indexOf("\r"); + if (rt != -1) { + result.push(line.slice(0, rt)); + pos += rt + 1; + } else { + result.push(line); + pos = nl + 1; + } + } + return result + } : function (string) { return string.split(/\r\n?|\n/); }; + + var hasSelection = window.getSelection ? function (te) { + try { return te.selectionStart != te.selectionEnd } + catch(e) { return false } + } : function (te) { + var range; + try {range = te.ownerDocument.selection.createRange();} + catch(e) {} + if (!range || range.parentElement() != te) { return false } + return range.compareEndPoints("StartToEnd", range) != 0 + }; + + var hasCopyEvent = (function () { + var e = elt("div"); + if ("oncopy" in e) { return true } + e.setAttribute("oncopy", "return;"); + return typeof e.oncopy == "function" + })(); + + var badZoomedRects = null; + function hasBadZoomedRects(measure) { + if (badZoomedRects != null) { return badZoomedRects } + var node = removeChildrenAndAdd(measure, elt("span", "x")); + var normal = node.getBoundingClientRect(); + var fromRange = range(node, 0, 1).getBoundingClientRect(); + return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1 + } + + // Known modes, by name and by MIME + var modes = {}, mimeModes = {}; + + // Extra arguments are stored as the mode's dependencies, which is + // used by (legacy) mechanisms like loadmode.js to automatically + // load a mode. (Preferred mechanism is the require/define calls.) + function defineMode(name, mode) { + if (arguments.length > 2) + { mode.dependencies = Array.prototype.slice.call(arguments, 2); } + modes[name] = mode; + } + + function defineMIME(mime, spec) { + mimeModes[mime] = spec; + } + + // Given a MIME type, a {name, ...options} config object, or a name + // string, return a mode config object. + function resolveMode(spec) { + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { + spec = mimeModes[spec]; + } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { + var found = mimeModes[spec.name]; + if (typeof found == "string") { found = {name: found}; } + spec = createObj(found, spec); + spec.name = found.name; + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { + return resolveMode("application/xml") + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) { + return resolveMode("application/json") + } + if (typeof spec == "string") { return {name: spec} } + else { return spec || {name: "null"} } + } + + // Given a mode spec (anything that resolveMode accepts), find and + // initialize an actual mode object. + function getMode(options, spec) { + spec = resolveMode(spec); + var mfactory = modes[spec.name]; + if (!mfactory) { return getMode(options, "text/plain") } + var modeObj = mfactory(options, spec); + if (modeExtensions.hasOwnProperty(spec.name)) { + var exts = modeExtensions[spec.name]; + for (var prop in exts) { + if (!exts.hasOwnProperty(prop)) { continue } + if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; } + modeObj[prop] = exts[prop]; + } + } + modeObj.name = spec.name; + if (spec.helperType) { modeObj.helperType = spec.helperType; } + if (spec.modeProps) { for (var prop$1 in spec.modeProps) + { modeObj[prop$1] = spec.modeProps[prop$1]; } } + + return modeObj + } + + // This can be used to attach properties to mode objects from + // outside the actual mode definition. + var modeExtensions = {}; + function extendMode(mode, properties) { + var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); + copyObj(properties, exts); + } + + function copyState(mode, state) { + if (state === true) { return state } + if (mode.copyState) { return mode.copyState(state) } + var nstate = {}; + for (var n in state) { + var val = state[n]; + if (val instanceof Array) { val = val.concat([]); } + nstate[n] = val; + } + return nstate + } + + // Given a mode and a state (for that mode), find the inner mode and + // state at the position that the state refers to. + function innerMode(mode, state) { + var info; + while (mode.innerMode) { + info = mode.innerMode(state); + if (!info || info.mode == mode) { break } + state = info.state; + mode = info.mode; + } + return info || {mode: mode, state: state} + } + + function startState(mode, a1, a2) { + return mode.startState ? mode.startState(a1, a2) : true + } + + // STRING STREAM + + // Fed to the mode parsers, provides helper functions to make + // parsers more succinct. + + var StringStream = function(string, tabSize, lineOracle) { + this.pos = this.start = 0; + this.string = string; + this.tabSize = tabSize || 8; + this.lastColumnPos = this.lastColumnValue = 0; + this.lineStart = 0; + this.lineOracle = lineOracle; + }; + + StringStream.prototype.eol = function () {return this.pos >= this.string.length}; + StringStream.prototype.sol = function () {return this.pos == this.lineStart}; + StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined}; + StringStream.prototype.next = function () { + if (this.pos < this.string.length) + { return this.string.charAt(this.pos++) } + }; + StringStream.prototype.eat = function (match) { + var ch = this.string.charAt(this.pos); + var ok; + if (typeof match == "string") { ok = ch == match; } + else { ok = ch && (match.test ? match.test(ch) : match(ch)); } + if (ok) {++this.pos; return ch} + }; + StringStream.prototype.eatWhile = function (match) { + var start = this.pos; + while (this.eat(match)){} + return this.pos > start + }; + StringStream.prototype.eatSpace = function () { + var start = this.pos; + while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; } + return this.pos > start + }; + StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;}; + StringStream.prototype.skipTo = function (ch) { + var found = this.string.indexOf(ch, this.pos); + if (found > -1) {this.pos = found; return true} + }; + StringStream.prototype.backUp = function (n) {this.pos -= n;}; + StringStream.prototype.column = function () { + if (this.lastColumnPos < this.start) { + this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue); + this.lastColumnPos = this.start; + } + return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) + }; + StringStream.prototype.indentation = function () { + return countColumn(this.string, null, this.tabSize) - + (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) + }; + StringStream.prototype.match = function (pattern, consume, caseInsensitive) { + if (typeof pattern == "string") { + var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; }; + var substr = this.string.substr(this.pos, pattern.length); + if (cased(substr) == cased(pattern)) { + if (consume !== false) { this.pos += pattern.length; } + return true + } + } else { + var match = this.string.slice(this.pos).match(pattern); + if (match && match.index > 0) { return null } + if (match && consume !== false) { this.pos += match[0].length; } + return match + } + }; + StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)}; + StringStream.prototype.hideFirstChars = function (n, inner) { + this.lineStart += n; + try { return inner() } + finally { this.lineStart -= n; } + }; + StringStream.prototype.lookAhead = function (n) { + var oracle = this.lineOracle; + return oracle && oracle.lookAhead(n) + }; + StringStream.prototype.baseToken = function () { + var oracle = this.lineOracle; + return oracle && oracle.baseToken(this.pos) + }; + + // Find the line object corresponding to the given line number. + function getLine(doc, n) { + n -= doc.first; + if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") } + var chunk = doc; + while (!chunk.lines) { + for (var i = 0;; ++i) { + var child = chunk.children[i], sz = child.chunkSize(); + if (n < sz) { chunk = child; break } + n -= sz; + } + } + return chunk.lines[n] + } + + // Get the part of a document between two positions, as an array of + // strings. + function getBetween(doc, start, end) { + var out = [], n = start.line; + doc.iter(start.line, end.line + 1, function (line) { + var text = line.text; + if (n == end.line) { text = text.slice(0, end.ch); } + if (n == start.line) { text = text.slice(start.ch); } + out.push(text); + ++n; + }); + return out + } + // Get the lines between from and to, as array of strings. + function getLines(doc, from, to) { + var out = []; + doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value + return out + } + + // Update the height of a line, propagating the height change + // upwards to parent nodes. + function updateLineHeight(line, height) { + var diff = height - line.height; + if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } } + } + + // Given a line object, find its line number by walking up through + // its parent links. + function lineNo(line) { + if (line.parent == null) { return null } + var cur = line.parent, no = indexOf(cur.lines, line); + for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { + for (var i = 0;; ++i) { + if (chunk.children[i] == cur) { break } + no += chunk.children[i].chunkSize(); + } + } + return no + cur.first + } + + // Find the line at the given vertical position, using the height + // information in the document tree. + function lineAtHeight(chunk, h) { + var n = chunk.first; + outer: do { + for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) { + var child = chunk.children[i$1], ch = child.height; + if (h < ch) { chunk = child; continue outer } + h -= ch; + n += child.chunkSize(); + } + return n + } while (!chunk.lines) + var i = 0; + for (; i < chunk.lines.length; ++i) { + var line = chunk.lines[i], lh = line.height; + if (h < lh) { break } + h -= lh; + } + return n + i + } + + function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size} + + function lineNumberFor(options, i) { + return String(options.lineNumberFormatter(i + options.firstLineNumber)) + } + + // A Pos instance represents a position within the text. + function Pos(line, ch, sticky) { + if ( sticky === void 0 ) sticky = null; + + if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) } + this.line = line; + this.ch = ch; + this.sticky = sticky; + } + + // Compare two positions, return 0 if they are the same, a negative + // number when a is less, and a positive number otherwise. + function cmp(a, b) { return a.line - b.line || a.ch - b.ch } + + function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 } + + function copyPos(x) {return Pos(x.line, x.ch)} + function maxPos(a, b) { return cmp(a, b) < 0 ? b : a } + function minPos(a, b) { return cmp(a, b) < 0 ? a : b } + + // Most of the external API clips given positions to make sure they + // actually exist within the document. + function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))} + function clipPos(doc, pos) { + if (pos.line < doc.first) { return Pos(doc.first, 0) } + var last = doc.first + doc.size - 1; + if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) } + return clipToLen(pos, getLine(doc, pos.line).text.length) + } + function clipToLen(pos, linelen) { + var ch = pos.ch; + if (ch == null || ch > linelen) { return Pos(pos.line, linelen) } + else if (ch < 0) { return Pos(pos.line, 0) } + else { return pos } + } + function clipPosArray(doc, array) { + var out = []; + for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); } + return out + } + + var SavedContext = function(state, lookAhead) { + this.state = state; + this.lookAhead = lookAhead; + }; + + var Context = function(doc, state, line, lookAhead) { + this.state = state; + this.doc = doc; + this.line = line; + this.maxLookAhead = lookAhead || 0; + this.baseTokens = null; + this.baseTokenPos = 1; + }; + + Context.prototype.lookAhead = function (n) { + var line = this.doc.getLine(this.line + n); + if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; } + return line + }; + + Context.prototype.baseToken = function (n) { + if (!this.baseTokens) { return null } + while (this.baseTokens[this.baseTokenPos] <= n) + { this.baseTokenPos += 2; } + var type = this.baseTokens[this.baseTokenPos + 1]; + return {type: type && type.replace(/( |^)overlay .*/, ""), + size: this.baseTokens[this.baseTokenPos] - n} + }; + + Context.prototype.nextLine = function () { + this.line++; + if (this.maxLookAhead > 0) { this.maxLookAhead--; } + }; + + Context.fromSaved = function (doc, saved, line) { + if (saved instanceof SavedContext) + { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) } + else + { return new Context(doc, copyState(doc.mode, saved), line) } + }; + + Context.prototype.save = function (copy) { + var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state; + return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state + }; + + + // Compute a style array (an array starting with a mode generation + // -- for invalidation -- followed by pairs of end positions and + // style strings), which is used to highlight the tokens on the + // line. + function highlightLine(cm, line, context, forceToEnd) { + // A styles array always starts with a number identifying the + // mode/overlays that it is based on (for easy invalidation). + var st = [cm.state.modeGen], lineClasses = {}; + // Compute the base array of styles + runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); }, + lineClasses, forceToEnd); + var state = context.state; + + // Run overlays, adjust style array. + var loop = function ( o ) { + context.baseTokens = st; + var overlay = cm.state.overlays[o], i = 1, at = 0; + context.state = true; + runMode(cm, line.text, overlay.mode, context, function (end, style) { + var start = i; + // Ensure there's a token end at the current position, and that i points at it + while (at < end) { + var i_end = st[i]; + if (i_end > end) + { st.splice(i, 1, end, st[i+1], i_end); } + i += 2; + at = Math.min(end, i_end); + } + if (!style) { return } + if (overlay.opaque) { + st.splice(start, i - start, end, "overlay " + style); + i = start + 2; + } else { + for (; start < i; start += 2) { + var cur = st[start+1]; + st[start+1] = (cur ? cur + " " : "") + "overlay " + style; + } + } + }, lineClasses); + context.state = state; + context.baseTokens = null; + context.baseTokenPos = 1; + }; + + for (var o = 0; o < cm.state.overlays.length; ++o) loop( o ); + + return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null} + } + + function getLineStyles(cm, line, updateFrontier) { + if (!line.styles || line.styles[0] != cm.state.modeGen) { + var context = getContextBefore(cm, lineNo(line)); + var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state); + var result = highlightLine(cm, line, context); + if (resetState) { context.state = resetState; } + line.stateAfter = context.save(!resetState); + line.styles = result.styles; + if (result.classes) { line.styleClasses = result.classes; } + else if (line.styleClasses) { line.styleClasses = null; } + if (updateFrontier === cm.doc.highlightFrontier) + { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); } + } + return line.styles + } + + function getContextBefore(cm, n, precise) { + var doc = cm.doc, display = cm.display; + if (!doc.mode.startState) { return new Context(doc, true, n) } + var start = findStartLine(cm, n, precise); + var saved = start > doc.first && getLine(doc, start - 1).stateAfter; + var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start); + + doc.iter(start, n, function (line) { + processLine(cm, line.text, context); + var pos = context.line; + line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null; + context.nextLine(); + }); + if (precise) { doc.modeFrontier = context.line; } + return context + } + + // Lightweight form of highlight -- proceed over this line and + // update state, but don't save a style array. Used for lines that + // aren't currently visible. + function processLine(cm, text, context, startAt) { + var mode = cm.doc.mode; + var stream = new StringStream(text, cm.options.tabSize, context); + stream.start = stream.pos = startAt || 0; + if (text == "") { callBlankLine(mode, context.state); } + while (!stream.eol()) { + readToken(mode, stream, context.state); + stream.start = stream.pos; + } + } + + function callBlankLine(mode, state) { + if (mode.blankLine) { return mode.blankLine(state) } + if (!mode.innerMode) { return } + var inner = innerMode(mode, state); + if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) } + } + + function readToken(mode, stream, state, inner) { + for (var i = 0; i < 10; i++) { + if (inner) { inner[0] = innerMode(mode, state).mode; } + var style = mode.token(stream, state); + if (stream.pos > stream.start) { return style } + } + throw new Error("Mode " + mode.name + " failed to advance stream.") + } + + var Token = function(stream, type, state) { + this.start = stream.start; this.end = stream.pos; + this.string = stream.current(); + this.type = type || null; + this.state = state; + }; + + // Utility for getTokenAt and getLineTokens + function takeToken(cm, pos, precise, asArray) { + var doc = cm.doc, mode = doc.mode, style; + pos = clipPos(doc, pos); + var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise); + var stream = new StringStream(line.text, cm.options.tabSize, context), tokens; + if (asArray) { tokens = []; } + while ((asArray || stream.pos < pos.ch) && !stream.eol()) { + stream.start = stream.pos; + style = readToken(mode, stream, context.state); + if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); } + } + return asArray ? tokens : new Token(stream, style, context.state) + } + + function extractLineClasses(type, output) { + if (type) { for (;;) { + var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/); + if (!lineClass) { break } + type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length); + var prop = lineClass[1] ? "bgClass" : "textClass"; + if (output[prop] == null) + { output[prop] = lineClass[2]; } + else if (!(new RegExp("(?:^|\\s)" + lineClass[2] + "(?:$|\\s)")).test(output[prop])) + { output[prop] += " " + lineClass[2]; } + } } + return type + } + + // Run the given mode's parser over a line, calling f for each token. + function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) { + var flattenSpans = mode.flattenSpans; + if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; } + var curStart = 0, curStyle = null; + var stream = new StringStream(text, cm.options.tabSize, context), style; + var inner = cm.options.addModeClass && [null]; + if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); } + while (!stream.eol()) { + if (stream.pos > cm.options.maxHighlightLength) { + flattenSpans = false; + if (forceToEnd) { processLine(cm, text, context, stream.pos); } + stream.pos = text.length; + style = null; + } else { + style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses); + } + if (inner) { + var mName = inner[0].name; + if (mName) { style = "m-" + (style ? mName + " " + style : mName); } + } + if (!flattenSpans || curStyle != style) { + while (curStart < stream.start) { + curStart = Math.min(stream.start, curStart + 5000); + f(curStart, curStyle); + } + curStyle = style; + } + stream.start = stream.pos; + } + while (curStart < stream.pos) { + // Webkit seems to refuse to render text nodes longer than 57444 + // characters, and returns inaccurate measurements in nodes + // starting around 5000 chars. + var pos = Math.min(stream.pos, curStart + 5000); + f(pos, curStyle); + curStart = pos; + } + } + + // Finds the line to start with when starting a parse. Tries to + // find a line with a stateAfter, so that it can start with a + // valid state. If that fails, it returns the line with the + // smallest indentation, which tends to need the least context to + // parse correctly. + function findStartLine(cm, n, precise) { + var minindent, minline, doc = cm.doc; + var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100); + for (var search = n; search > lim; --search) { + if (search <= doc.first) { return doc.first } + var line = getLine(doc, search - 1), after = line.stateAfter; + if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier)) + { return search } + var indented = countColumn(line.text, null, cm.options.tabSize); + if (minline == null || minindent > indented) { + minline = search - 1; + minindent = indented; + } + } + return minline + } + + function retreatFrontier(doc, n) { + doc.modeFrontier = Math.min(doc.modeFrontier, n); + if (doc.highlightFrontier < n - 10) { return } + var start = doc.first; + for (var line = n - 1; line > start; line--) { + var saved = getLine(doc, line).stateAfter; + // change is on 3 + // state on line 1 looked ahead 2 -- so saw 3 + // test 1 + 2 < 3 should cover this + if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) { + start = line + 1; + break + } + } + doc.highlightFrontier = Math.min(doc.highlightFrontier, start); + } + + // Optimize some code when these features are not used. + var sawReadOnlySpans = false, sawCollapsedSpans = false; + + function seeReadOnlySpans() { + sawReadOnlySpans = true; + } + + function seeCollapsedSpans() { + sawCollapsedSpans = true; + } + + // TEXTMARKER SPANS + + function MarkedSpan(marker, from, to) { + this.marker = marker; + this.from = from; this.to = to; + } + + // Search an array of spans for a span matching the given marker. + function getMarkedSpanFor(spans, marker) { + if (spans) { for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.marker == marker) { return span } + } } + } + // Remove a span from an array, returning undefined if no spans are + // left (we don't store arrays for lines without spans). + function removeMarkedSpan(spans, span) { + var r; + for (var i = 0; i < spans.length; ++i) + { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } } + return r + } + // Add a span to a line. + function addMarkedSpan(line, span) { + line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]; + span.marker.attachLine(line); + } + + // Used for the algorithm that adjusts markers for a change in the + // document. These functions cut an array of spans at a given + // character position, returning an array of remaining chunks (or + // undefined if nothing remains). + function markedSpansBefore(old, startCh, isInsert) { + var nw; + if (old) { for (var i = 0; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh); + if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) { + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh) + ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to)); + } + } } + return nw + } + function markedSpansAfter(old, endCh, isInsert) { + var nw; + if (old) { for (var i = 0; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh); + if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) { + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh) + ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh, + span.to == null ? null : span.to - endCh)); + } + } } + return nw + } + + // Given a change object, compute the new set of marker spans that + // cover the line in which the change took place. Removes spans + // entirely within the change, reconnects spans belonging to the + // same marker that appear on both sides of the change, and cuts off + // spans partially within the change. Returns an array of span + // arrays with one element for each line in (after) the change. + function stretchSpansOverChange(doc, change) { + if (change.full) { return null } + var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans; + var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans; + if (!oldFirst && !oldLast) { return null } + + var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0; + // Get the spans that 'stick out' on both sides + var first = markedSpansBefore(oldFirst, startCh, isInsert); + var last = markedSpansAfter(oldLast, endCh, isInsert); + + // Next, merge those two ends + var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0); + if (first) { + // Fix up .to properties of first + for (var i = 0; i < first.length; ++i) { + var span = first[i]; + if (span.to == null) { + var found = getMarkedSpanFor(last, span.marker); + if (!found) { span.to = startCh; } + else if (sameLine) { span.to = found.to == null ? null : found.to + offset; } + } + } + } + if (last) { + // Fix up .from in last (or move them into first in case of sameLine) + for (var i$1 = 0; i$1 < last.length; ++i$1) { + var span$1 = last[i$1]; + if (span$1.to != null) { span$1.to += offset; } + if (span$1.from == null) { + var found$1 = getMarkedSpanFor(first, span$1.marker); + if (!found$1) { + span$1.from = offset; + if (sameLine) { (first || (first = [])).push(span$1); } + } + } else { + span$1.from += offset; + if (sameLine) { (first || (first = [])).push(span$1); } + } + } + } + // Make sure we didn't create any zero-length spans + if (first) { first = clearEmptySpans(first); } + if (last && last != first) { last = clearEmptySpans(last); } + + var newMarkers = [first]; + if (!sameLine) { + // Fill gap with whole-line-spans + var gap = change.text.length - 2, gapMarkers; + if (gap > 0 && first) + { for (var i$2 = 0; i$2 < first.length; ++i$2) + { if (first[i$2].to == null) + { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } } + for (var i$3 = 0; i$3 < gap; ++i$3) + { newMarkers.push(gapMarkers); } + newMarkers.push(last); + } + return newMarkers + } + + // Remove spans that are empty and don't have a clearWhenEmpty + // option of false. + function clearEmptySpans(spans) { + for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false) + { spans.splice(i--, 1); } + } + if (!spans.length) { return null } + return spans + } + + // Used to 'clip' out readOnly ranges when making a change. + function removeReadOnlyRanges(doc, from, to) { + var markers = null; + doc.iter(from.line, to.line + 1, function (line) { + if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { + var mark = line.markedSpans[i].marker; + if (mark.readOnly && (!markers || indexOf(markers, mark) == -1)) + { (markers || (markers = [])).push(mark); } + } } + }); + if (!markers) { return null } + var parts = [{from: from, to: to}]; + for (var i = 0; i < markers.length; ++i) { + var mk = markers[i], m = mk.find(0); + for (var j = 0; j < parts.length; ++j) { + var p = parts[j]; + if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue } + var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to); + if (dfrom < 0 || !mk.inclusiveLeft && !dfrom) + { newParts.push({from: p.from, to: m.from}); } + if (dto > 0 || !mk.inclusiveRight && !dto) + { newParts.push({from: m.to, to: p.to}); } + parts.splice.apply(parts, newParts); + j += newParts.length - 3; + } + } + return parts + } + + // Connect or disconnect spans from a line. + function detachMarkedSpans(line) { + var spans = line.markedSpans; + if (!spans) { return } + for (var i = 0; i < spans.length; ++i) + { spans[i].marker.detachLine(line); } + line.markedSpans = null; + } + function attachMarkedSpans(line, spans) { + if (!spans) { return } + for (var i = 0; i < spans.length; ++i) + { spans[i].marker.attachLine(line); } + line.markedSpans = spans; + } + + // Helpers used when computing which overlapping collapsed span + // counts as the larger one. + function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 } + function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 } + + // Returns a number indicating which of two overlapping collapsed + // spans is larger (and thus includes the other). Falls back to + // comparing ids when the spans cover exactly the same range. + function compareCollapsedMarkers(a, b) { + var lenDiff = a.lines.length - b.lines.length; + if (lenDiff != 0) { return lenDiff } + var aPos = a.find(), bPos = b.find(); + var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b); + if (fromCmp) { return -fromCmp } + var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b); + if (toCmp) { return toCmp } + return b.id - a.id + } + + // Find out whether a line ends or starts in a collapsed span. If + // so, return the marker for that span. + function collapsedSpanAtSide(line, start) { + var sps = sawCollapsedSpans && line.markedSpans, found; + if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { + sp = sps[i]; + if (sp.marker.collapsed && (start ? sp.from : sp.to) == null && + (!found || compareCollapsedMarkers(found, sp.marker) < 0)) + { found = sp.marker; } + } } + return found + } + function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) } + function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) } + + function collapsedSpanAround(line, ch) { + var sps = sawCollapsedSpans && line.markedSpans, found; + if (sps) { for (var i = 0; i < sps.length; ++i) { + var sp = sps[i]; + if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) && + (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; } + } } + return found + } + + // Test whether there exists a collapsed span that partially + // overlaps (covers the start or end, but not both) of a new span. + // Such overlap is not allowed. + function conflictingCollapsedRange(doc, lineNo, from, to, marker) { + var line = getLine(doc, lineNo); + var sps = sawCollapsedSpans && line.markedSpans; + if (sps) { for (var i = 0; i < sps.length; ++i) { + var sp = sps[i]; + if (!sp.marker.collapsed) { continue } + var found = sp.marker.find(0); + var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker); + var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker); + if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue } + if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) || + fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0)) + { return true } + } } + } + + // A visual line is a line as drawn on the screen. Folding, for + // example, can cause multiple logical lines to appear on the same + // visual line. This finds the start of the visual line that the + // given line is part of (usually that is the line itself). + function visualLine(line) { + var merged; + while (merged = collapsedSpanAtStart(line)) + { line = merged.find(-1, true).line; } + return line + } + + function visualLineEnd(line) { + var merged; + while (merged = collapsedSpanAtEnd(line)) + { line = merged.find(1, true).line; } + return line + } + + // Returns an array of logical lines that continue the visual line + // started by the argument, or undefined if there are no such lines. + function visualLineContinued(line) { + var merged, lines; + while (merged = collapsedSpanAtEnd(line)) { + line = merged.find(1, true).line + ;(lines || (lines = [])).push(line); + } + return lines + } + + // Get the line number of the start of the visual line that the + // given line number is part of. + function visualLineNo(doc, lineN) { + var line = getLine(doc, lineN), vis = visualLine(line); + if (line == vis) { return lineN } + return lineNo(vis) + } + + // Get the line number of the start of the next visual line after + // the given line. + function visualLineEndNo(doc, lineN) { + if (lineN > doc.lastLine()) { return lineN } + var line = getLine(doc, lineN), merged; + if (!lineIsHidden(doc, line)) { return lineN } + while (merged = collapsedSpanAtEnd(line)) + { line = merged.find(1, true).line; } + return lineNo(line) + 1 + } + + // Compute whether a line is hidden. Lines count as hidden when they + // are part of a visual line that starts with another line, or when + // they are entirely covered by collapsed, non-widget span. + function lineIsHidden(doc, line) { + var sps = sawCollapsedSpans && line.markedSpans; + if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { + sp = sps[i]; + if (!sp.marker.collapsed) { continue } + if (sp.from == null) { return true } + if (sp.marker.widgetNode) { continue } + if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp)) + { return true } + } } + } + function lineIsHiddenInner(doc, line, span) { + if (span.to == null) { + var end = span.marker.find(1, true); + return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker)) + } + if (span.marker.inclusiveRight && span.to == line.text.length) + { return true } + for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) { + sp = line.markedSpans[i]; + if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to && + (sp.to == null || sp.to != span.from) && + (sp.marker.inclusiveLeft || span.marker.inclusiveRight) && + lineIsHiddenInner(doc, line, sp)) { return true } + } + } + + // Find the height above the given line. + function heightAtLine(lineObj) { + lineObj = visualLine(lineObj); + + var h = 0, chunk = lineObj.parent; + for (var i = 0; i < chunk.lines.length; ++i) { + var line = chunk.lines[i]; + if (line == lineObj) { break } + else { h += line.height; } + } + for (var p = chunk.parent; p; chunk = p, p = chunk.parent) { + for (var i$1 = 0; i$1 < p.children.length; ++i$1) { + var cur = p.children[i$1]; + if (cur == chunk) { break } + else { h += cur.height; } + } + } + return h + } + + // Compute the character length of a line, taking into account + // collapsed ranges (see markText) that might hide parts, and join + // other lines onto it. + function lineLength(line) { + if (line.height == 0) { return 0 } + var len = line.text.length, merged, cur = line; + while (merged = collapsedSpanAtStart(cur)) { + var found = merged.find(0, true); + cur = found.from.line; + len += found.from.ch - found.to.ch; + } + cur = line; + while (merged = collapsedSpanAtEnd(cur)) { + var found$1 = merged.find(0, true); + len -= cur.text.length - found$1.from.ch; + cur = found$1.to.line; + len += cur.text.length - found$1.to.ch; + } + return len + } + + // Find the longest line in the document. + function findMaxLine(cm) { + var d = cm.display, doc = cm.doc; + d.maxLine = getLine(doc, doc.first); + d.maxLineLength = lineLength(d.maxLine); + d.maxLineChanged = true; + doc.iter(function (line) { + var len = lineLength(line); + if (len > d.maxLineLength) { + d.maxLineLength = len; + d.maxLine = line; + } + }); + } + + // LINE DATA STRUCTURE + + // Line objects. These hold state related to a line, including + // highlighting info (the styles array). + var Line = function(text, markedSpans, estimateHeight) { + this.text = text; + attachMarkedSpans(this, markedSpans); + this.height = estimateHeight ? estimateHeight(this) : 1; + }; + + Line.prototype.lineNo = function () { return lineNo(this) }; + eventMixin(Line); + + // Change the content (text, markers) of a line. Automatically + // invalidates cached information and tries to re-estimate the + // line's height. + function updateLine(line, text, markedSpans, estimateHeight) { + line.text = text; + if (line.stateAfter) { line.stateAfter = null; } + if (line.styles) { line.styles = null; } + if (line.order != null) { line.order = null; } + detachMarkedSpans(line); + attachMarkedSpans(line, markedSpans); + var estHeight = estimateHeight ? estimateHeight(line) : 1; + if (estHeight != line.height) { updateLineHeight(line, estHeight); } + } + + // Detach a line from the document tree and its markers. + function cleanUpLine(line) { + line.parent = null; + detachMarkedSpans(line); + } + + // Convert a style as returned by a mode (either null, or a string + // containing one or more styles) to a CSS style. This is cached, + // and also looks for line-wide styles. + var styleToClassCache = {}, styleToClassCacheWithMode = {}; + function interpretTokenStyle(style, options) { + if (!style || /^\s*$/.test(style)) { return null } + var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache; + return cache[style] || + (cache[style] = style.replace(/\S+/g, "cm-$&")) + } + + // Render the DOM representation of the text of a line. Also builds + // up a 'line map', which points at the DOM nodes that represent + // specific stretches of text, and is used by the measuring code. + // The returned object contains the DOM node, this map, and + // information about line-wide styles that were set by the mode. + function buildLineContent(cm, lineView) { + // The padding-right forces the element to have a 'border', which + // is needed on Webkit to be able to get line-level bounding + // rectangles for it (in measureChar). + var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null); + var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content, + col: 0, pos: 0, cm: cm, + trailingSpace: false, + splitSpaces: cm.getOption("lineWrapping")}; + lineView.measure = {}; + + // Iterate over the logical lines that make up this visual line. + for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) { + var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0); + builder.pos = 0; + builder.addToken = buildToken; + // Optionally wire in some hacks into the token-rendering + // algorithm, to deal with browser quirks. + if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction))) + { builder.addToken = buildTokenBadBidi(builder.addToken, order); } + builder.map = []; + var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line); + insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate)); + if (line.styleClasses) { + if (line.styleClasses.bgClass) + { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); } + if (line.styleClasses.textClass) + { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); } + } + + // Ensure at least a single node is present, for measuring. + if (builder.map.length == 0) + { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); } + + // Store the map and a cache object for the current logical line + if (i == 0) { + lineView.measure.map = builder.map; + lineView.measure.cache = {}; + } else { + (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map) + ;(lineView.measure.caches || (lineView.measure.caches = [])).push({}); + } + } + + // See issue #2901 + if (webkit) { + var last = builder.content.lastChild; + if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab"))) + { builder.content.className = "cm-tab-wrap-hack"; } + } + + signal(cm, "renderLine", cm, lineView.line, builder.pre); + if (builder.pre.className) + { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); } + + return builder + } + + function defaultSpecialCharPlaceholder(ch) { + var token = elt("span", "\u2022", "cm-invalidchar"); + token.title = "\\u" + ch.charCodeAt(0).toString(16); + token.setAttribute("aria-label", token.title); + return token + } + + // Build up the DOM representation for a single token, and add it to + // the line map. Takes care to render special characters separately. + function buildToken(builder, text, style, startStyle, endStyle, css, attributes) { + if (!text) { return } + var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text; + var special = builder.cm.state.specialChars, mustWrap = false; + var content; + if (!special.test(text)) { + builder.col += text.length; + content = document.createTextNode(displayText); + builder.map.push(builder.pos, builder.pos + text.length, content); + if (ie && ie_version < 9) { mustWrap = true; } + builder.pos += text.length; + } else { + content = document.createDocumentFragment(); + var pos = 0; + while (true) { + special.lastIndex = pos; + var m = special.exec(text); + var skipped = m ? m.index - pos : text.length - pos; + if (skipped) { + var txt = document.createTextNode(displayText.slice(pos, pos + skipped)); + if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); } + else { content.appendChild(txt); } + builder.map.push(builder.pos, builder.pos + skipped, txt); + builder.col += skipped; + builder.pos += skipped; + } + if (!m) { break } + pos += skipped + 1; + var txt$1 = (void 0); + if (m[0] == "\t") { + var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize; + txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")); + txt$1.setAttribute("role", "presentation"); + txt$1.setAttribute("cm-text", "\t"); + builder.col += tabWidth; + } else if (m[0] == "\r" || m[0] == "\n") { + txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar")); + txt$1.setAttribute("cm-text", m[0]); + builder.col += 1; + } else { + txt$1 = builder.cm.options.specialCharPlaceholder(m[0]); + txt$1.setAttribute("cm-text", m[0]); + if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); } + else { content.appendChild(txt$1); } + builder.col += 1; + } + builder.map.push(builder.pos, builder.pos + 1, txt$1); + builder.pos++; + } + } + builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32; + if (style || startStyle || endStyle || mustWrap || css || attributes) { + var fullStyle = style || ""; + if (startStyle) { fullStyle += startStyle; } + if (endStyle) { fullStyle += endStyle; } + var token = elt("span", [content], fullStyle, css); + if (attributes) { + for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class") + { token.setAttribute(attr, attributes[attr]); } } + } + return builder.content.appendChild(token) + } + builder.content.appendChild(content); + } + + // Change some spaces to NBSP to prevent the browser from collapsing + // trailing spaces at the end of a line when rendering text (issue #1362). + function splitSpaces(text, trailingBefore) { + if (text.length > 1 && !/ /.test(text)) { return text } + var spaceBefore = trailingBefore, result = ""; + for (var i = 0; i < text.length; i++) { + var ch = text.charAt(i); + if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32)) + { ch = "\u00a0"; } + result += ch; + spaceBefore = ch == " "; + } + return result + } + + // Work around nonsense dimensions being reported for stretches of + // right-to-left text. + function buildTokenBadBidi(inner, order) { + return function (builder, text, style, startStyle, endStyle, css, attributes) { + style = style ? style + " cm-force-border" : "cm-force-border"; + var start = builder.pos, end = start + text.length; + for (;;) { + // Find the part that overlaps with the start of this text + var part = (void 0); + for (var i = 0; i < order.length; i++) { + part = order[i]; + if (part.to > start && part.from <= start) { break } + } + if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) } + inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes); + startStyle = null; + text = text.slice(part.to - start); + start = part.to; + } + } + } + + function buildCollapsedSpan(builder, size, marker, ignoreWidget) { + var widget = !ignoreWidget && marker.widgetNode; + if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); } + if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) { + if (!widget) + { widget = builder.content.appendChild(document.createElement("span")); } + widget.setAttribute("cm-marker", marker.id); + } + if (widget) { + builder.cm.display.input.setUneditable(widget); + builder.content.appendChild(widget); + } + builder.pos += size; + builder.trailingSpace = false; + } + + // Outputs a number of spans to make up a line, taking highlighting + // and marked text into account. + function insertLineContent(line, builder, styles) { + var spans = line.markedSpans, allText = line.text, at = 0; + if (!spans) { + for (var i$1 = 1; i$1 < styles.length; i$1+=2) + { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); } + return + } + + var len = allText.length, pos = 0, i = 1, text = "", style, css; + var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes; + for (;;) { + if (nextChange == pos) { // Update current marker set + spanStyle = spanEndStyle = spanStartStyle = css = ""; + attributes = null; + collapsed = null; nextChange = Infinity; + var foundBookmarks = [], endStyles = (void 0); + for (var j = 0; j < spans.length; ++j) { + var sp = spans[j], m = sp.marker; + if (m.type == "bookmark" && sp.from == pos && m.widgetNode) { + foundBookmarks.push(m); + } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) { + if (sp.to != null && sp.to != pos && nextChange > sp.to) { + nextChange = sp.to; + spanEndStyle = ""; + } + if (m.className) { spanStyle += " " + m.className; } + if (m.css) { css = (css ? css + ";" : "") + m.css; } + if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; } + if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); } + // support for the old title property + // https://github.com/codemirror/CodeMirror/pull/5673 + if (m.title) { (attributes || (attributes = {})).title = m.title; } + if (m.attributes) { + for (var attr in m.attributes) + { (attributes || (attributes = {}))[attr] = m.attributes[attr]; } + } + if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)) + { collapsed = sp; } + } else if (sp.from > pos && nextChange > sp.from) { + nextChange = sp.from; + } + } + if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2) + { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } } + + if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2) + { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } } + if (collapsed && (collapsed.from || 0) == pos) { + buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos, + collapsed.marker, collapsed.from == null); + if (collapsed.to == null) { return } + if (collapsed.to == pos) { collapsed = false; } + } + } + if (pos >= len) { break } + + var upto = Math.min(len, nextChange); + while (true) { + if (text) { + var end = pos + text.length; + if (!collapsed) { + var tokenText = end > upto ? text.slice(0, upto - pos) : text; + builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle, + spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes); + } + if (end >= upto) {text = text.slice(upto - pos); pos = upto; break} + pos = end; + spanStartStyle = ""; + } + text = allText.slice(at, at = styles[i++]); + style = interpretTokenStyle(styles[i++], builder.cm.options); + } + } + } + + + // These objects are used to represent the visible (currently drawn) + // part of the document. A LineView may correspond to multiple + // logical lines, if those are connected by collapsed ranges. + function LineView(doc, line, lineN) { + // The starting line + this.line = line; + // Continuing lines, if any + this.rest = visualLineContinued(line); + // Number of logical lines in this visual line + this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1; + this.node = this.text = null; + this.hidden = lineIsHidden(doc, line); + } + + // Create a range of LineView objects for the given lines. + function buildViewArray(cm, from, to) { + var array = [], nextPos; + for (var pos = from; pos < to; pos = nextPos) { + var view = new LineView(cm.doc, getLine(cm.doc, pos), pos); + nextPos = pos + view.size; + array.push(view); + } + return array + } + + var operationGroup = null; + + function pushOperation(op) { + if (operationGroup) { + operationGroup.ops.push(op); + } else { + op.ownsGroup = operationGroup = { + ops: [op], + delayedCallbacks: [] + }; + } + } + + function fireCallbacksForOps(group) { + // Calls delayed callbacks and cursorActivity handlers until no + // new ones appear + var callbacks = group.delayedCallbacks, i = 0; + do { + for (; i < callbacks.length; i++) + { callbacks[i].call(null); } + for (var j = 0; j < group.ops.length; j++) { + var op = group.ops[j]; + if (op.cursorActivityHandlers) + { while (op.cursorActivityCalled < op.cursorActivityHandlers.length) + { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } } + } + } while (i < callbacks.length) + } + + function finishOperation(op, endCb) { + var group = op.ownsGroup; + if (!group) { return } + + try { fireCallbacksForOps(group); } + finally { + operationGroup = null; + endCb(group); + } + } + + var orphanDelayedCallbacks = null; + + // Often, we want to signal events at a point where we are in the + // middle of some work, but don't want the handler to start calling + // other methods on the editor, which might be in an inconsistent + // state or simply not expect any other events to happen. + // signalLater looks whether there are any handlers, and schedules + // them to be executed when the last operation ends, or, if no + // operation is active, when a timeout fires. + function signalLater(emitter, type /*, values...*/) { + var arr = getHandlers(emitter, type); + if (!arr.length) { return } + var args = Array.prototype.slice.call(arguments, 2), list; + if (operationGroup) { + list = operationGroup.delayedCallbacks; + } else if (orphanDelayedCallbacks) { + list = orphanDelayedCallbacks; + } else { + list = orphanDelayedCallbacks = []; + setTimeout(fireOrphanDelayed, 0); + } + var loop = function ( i ) { + list.push(function () { return arr[i].apply(null, args); }); + }; + + for (var i = 0; i < arr.length; ++i) + loop( i ); + } + + function fireOrphanDelayed() { + var delayed = orphanDelayedCallbacks; + orphanDelayedCallbacks = null; + for (var i = 0; i < delayed.length; ++i) { delayed[i](); } + } + + // When an aspect of a line changes, a string is added to + // lineView.changes. This updates the relevant part of the line's + // DOM structure. + function updateLineForChanges(cm, lineView, lineN, dims) { + for (var j = 0; j < lineView.changes.length; j++) { + var type = lineView.changes[j]; + if (type == "text") { updateLineText(cm, lineView); } + else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); } + else if (type == "class") { updateLineClasses(cm, lineView); } + else if (type == "widget") { updateLineWidgets(cm, lineView, dims); } + } + lineView.changes = null; + } + + // Lines with gutter elements, widgets or a background class need to + // be wrapped, and have the extra elements added to the wrapper div + function ensureLineWrapped(lineView) { + if (lineView.node == lineView.text) { + lineView.node = elt("div", null, null, "position: relative"); + if (lineView.text.parentNode) + { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); } + lineView.node.appendChild(lineView.text); + if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; } + } + return lineView.node + } + + function updateLineBackground(cm, lineView) { + var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass; + if (cls) { cls += " CodeMirror-linebackground"; } + if (lineView.background) { + if (cls) { lineView.background.className = cls; } + else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; } + } else if (cls) { + var wrap = ensureLineWrapped(lineView); + lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild); + cm.display.input.setUneditable(lineView.background); + } + } + + // Wrapper around buildLineContent which will reuse the structure + // in display.externalMeasured when possible. + function getLineContent(cm, lineView) { + var ext = cm.display.externalMeasured; + if (ext && ext.line == lineView.line) { + cm.display.externalMeasured = null; + lineView.measure = ext.measure; + return ext.built + } + return buildLineContent(cm, lineView) + } + + // Redraw the line's text. Interacts with the background and text + // classes because the mode may output tokens that influence these + // classes. + function updateLineText(cm, lineView) { + var cls = lineView.text.className; + var built = getLineContent(cm, lineView); + if (lineView.text == lineView.node) { lineView.node = built.pre; } + lineView.text.parentNode.replaceChild(built.pre, lineView.text); + lineView.text = built.pre; + if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) { + lineView.bgClass = built.bgClass; + lineView.textClass = built.textClass; + updateLineClasses(cm, lineView); + } else if (cls) { + lineView.text.className = cls; + } + } + + function updateLineClasses(cm, lineView) { + updateLineBackground(cm, lineView); + if (lineView.line.wrapClass) + { ensureLineWrapped(lineView).className = lineView.line.wrapClass; } + else if (lineView.node != lineView.text) + { lineView.node.className = ""; } + var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass; + lineView.text.className = textClass || ""; + } + + function updateLineGutter(cm, lineView, lineN, dims) { + if (lineView.gutter) { + lineView.node.removeChild(lineView.gutter); + lineView.gutter = null; + } + if (lineView.gutterBackground) { + lineView.node.removeChild(lineView.gutterBackground); + lineView.gutterBackground = null; + } + if (lineView.line.gutterClass) { + var wrap = ensureLineWrapped(lineView); + lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass, + ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px")); + cm.display.input.setUneditable(lineView.gutterBackground); + wrap.insertBefore(lineView.gutterBackground, lineView.text); + } + var markers = lineView.line.gutterMarkers; + if (cm.options.lineNumbers || markers) { + var wrap$1 = ensureLineWrapped(lineView); + var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px")); + cm.display.input.setUneditable(gutterWrap); + wrap$1.insertBefore(gutterWrap, lineView.text); + if (lineView.line.gutterClass) + { gutterWrap.className += " " + lineView.line.gutterClass; } + if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"])) + { lineView.lineNumber = gutterWrap.appendChild( + elt("div", lineNumberFor(cm.options, lineN), + "CodeMirror-linenumber CodeMirror-gutter-elt", + ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); } + if (markers) { for (var k = 0; k < cm.display.gutterSpecs.length; ++k) { + var id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id]; + if (found) + { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", + ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); } + } } + } + } + + function updateLineWidgets(cm, lineView, dims) { + if (lineView.alignable) { lineView.alignable = null; } + var isWidget = classTest("CodeMirror-linewidget"); + for (var node = lineView.node.firstChild, next = (void 0); node; node = next) { + next = node.nextSibling; + if (isWidget.test(node.className)) { lineView.node.removeChild(node); } + } + insertLineWidgets(cm, lineView, dims); + } + + // Build a line's DOM representation from scratch + function buildLineElement(cm, lineView, lineN, dims) { + var built = getLineContent(cm, lineView); + lineView.text = lineView.node = built.pre; + if (built.bgClass) { lineView.bgClass = built.bgClass; } + if (built.textClass) { lineView.textClass = built.textClass; } + + updateLineClasses(cm, lineView); + updateLineGutter(cm, lineView, lineN, dims); + insertLineWidgets(cm, lineView, dims); + return lineView.node + } + + // A lineView may contain multiple logical lines (when merged by + // collapsed spans). The widgets for all of them need to be drawn. + function insertLineWidgets(cm, lineView, dims) { + insertLineWidgetsFor(cm, lineView.line, lineView, dims, true); + if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) + { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } } + } + + function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) { + if (!line.widgets) { return } + var wrap = ensureLineWrapped(lineView); + for (var i = 0, ws = line.widgets; i < ws.length; ++i) { + var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget" + (widget.className ? " " + widget.className : "")); + if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); } + positionLineWidget(widget, node, lineView, dims); + cm.display.input.setUneditable(node); + if (allowAbove && widget.above) + { wrap.insertBefore(node, lineView.gutter || lineView.text); } + else + { wrap.appendChild(node); } + signalLater(widget, "redraw"); + } + } + + function positionLineWidget(widget, node, lineView, dims) { + if (widget.noHScroll) { + (lineView.alignable || (lineView.alignable = [])).push(node); + var width = dims.wrapperWidth; + node.style.left = dims.fixedPos + "px"; + if (!widget.coverGutter) { + width -= dims.gutterTotalWidth; + node.style.paddingLeft = dims.gutterTotalWidth + "px"; + } + node.style.width = width + "px"; + } + if (widget.coverGutter) { + node.style.zIndex = 5; + node.style.position = "relative"; + if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; } + } + } + + function widgetHeight(widget) { + if (widget.height != null) { return widget.height } + var cm = widget.doc.cm; + if (!cm) { return 0 } + if (!contains(document.body, widget.node)) { + var parentStyle = "position: relative;"; + if (widget.coverGutter) + { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; } + if (widget.noHScroll) + { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; } + removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle)); + } + return widget.height = widget.node.parentNode.offsetHeight + } + + // Return true when the given mouse event happened in a widget + function eventInWidget(display, e) { + for (var n = e_target(e); n != display.wrapper; n = n.parentNode) { + if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") || + (n.parentNode == display.sizer && n != display.mover)) + { return true } + } + } + + // POSITION MEASUREMENT + + function paddingTop(display) {return display.lineSpace.offsetTop} + function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight} + function paddingH(display) { + if (display.cachedPaddingH) { return display.cachedPaddingH } + var e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-line-like")); + var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle; + var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}; + if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; } + return data + } + + function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth } + function displayWidth(cm) { + return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth + } + function displayHeight(cm) { + return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight + } + + // Ensure the lineView.wrapping.heights array is populated. This is + // an array of bottom offsets for the lines that make up a drawn + // line. When lineWrapping is on, there might be more than one + // height. + function ensureLineHeights(cm, lineView, rect) { + var wrapping = cm.options.lineWrapping; + var curWidth = wrapping && displayWidth(cm); + if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) { + var heights = lineView.measure.heights = []; + if (wrapping) { + lineView.measure.width = curWidth; + var rects = lineView.text.firstChild.getClientRects(); + for (var i = 0; i < rects.length - 1; i++) { + var cur = rects[i], next = rects[i + 1]; + if (Math.abs(cur.bottom - next.bottom) > 2) + { heights.push((cur.bottom + next.top) / 2 - rect.top); } + } + } + heights.push(rect.bottom - rect.top); + } + } + + // Find a line map (mapping character offsets to text nodes) and a + // measurement cache for the given line number. (A line view might + // contain multiple lines when collapsed ranges are present.) + function mapFromLineView(lineView, line, lineN) { + if (lineView.line == line) + { return {map: lineView.measure.map, cache: lineView.measure.cache} } + for (var i = 0; i < lineView.rest.length; i++) + { if (lineView.rest[i] == line) + { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } } + for (var i$1 = 0; i$1 < lineView.rest.length; i$1++) + { if (lineNo(lineView.rest[i$1]) > lineN) + { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } } + } + + // Render a line into the hidden node display.externalMeasured. Used + // when measurement is needed for a line that's not in the viewport. + function updateExternalMeasurement(cm, line) { + line = visualLine(line); + var lineN = lineNo(line); + var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN); + view.lineN = lineN; + var built = view.built = buildLineContent(cm, view); + view.text = built.pre; + removeChildrenAndAdd(cm.display.lineMeasure, built.pre); + return view + } + + // Get a {top, bottom, left, right} box (in line-local coordinates) + // for a given character. + function measureChar(cm, line, ch, bias) { + return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias) + } + + // Find a line view that corresponds to the given line number. + function findViewForLine(cm, lineN) { + if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo) + { return cm.display.view[findViewIndex(cm, lineN)] } + var ext = cm.display.externalMeasured; + if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size) + { return ext } + } + + // Measurement can be split in two steps, the set-up work that + // applies to the whole line, and the measurement of the actual + // character. Functions like coordsChar, that need to do a lot of + // measurements in a row, can thus ensure that the set-up work is + // only done once. + function prepareMeasureForLine(cm, line) { + var lineN = lineNo(line); + var view = findViewForLine(cm, lineN); + if (view && !view.text) { + view = null; + } else if (view && view.changes) { + updateLineForChanges(cm, view, lineN, getDimensions(cm)); + cm.curOp.forceUpdate = true; + } + if (!view) + { view = updateExternalMeasurement(cm, line); } + + var info = mapFromLineView(view, line, lineN); + return { + line: line, view: view, rect: null, + map: info.map, cache: info.cache, before: info.before, + hasHeights: false + } + } + + // Given a prepared measurement object, measures the position of an + // actual character (or fetches it from the cache). + function measureCharPrepared(cm, prepared, ch, bias, varHeight) { + if (prepared.before) { ch = -1; } + var key = ch + (bias || ""), found; + if (prepared.cache.hasOwnProperty(key)) { + found = prepared.cache[key]; + } else { + if (!prepared.rect) + { prepared.rect = prepared.view.text.getBoundingClientRect(); } + if (!prepared.hasHeights) { + ensureLineHeights(cm, prepared.view, prepared.rect); + prepared.hasHeights = true; + } + found = measureCharInner(cm, prepared, ch, bias); + if (!found.bogus) { prepared.cache[key] = found; } + } + return {left: found.left, right: found.right, + top: varHeight ? found.rtop : found.top, + bottom: varHeight ? found.rbottom : found.bottom} + } + + var nullRect = {left: 0, right: 0, top: 0, bottom: 0}; + + function nodeAndOffsetInLineMap(map, ch, bias) { + var node, start, end, collapse, mStart, mEnd; + // First, search the line map for the text node corresponding to, + // or closest to, the target character. + for (var i = 0; i < map.length; i += 3) { + mStart = map[i]; + mEnd = map[i + 1]; + if (ch < mStart) { + start = 0; end = 1; + collapse = "left"; + } else if (ch < mEnd) { + start = ch - mStart; + end = start + 1; + } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) { + end = mEnd - mStart; + start = end - 1; + if (ch >= mEnd) { collapse = "right"; } + } + if (start != null) { + node = map[i + 2]; + if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right")) + { collapse = bias; } + if (bias == "left" && start == 0) + { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) { + node = map[(i -= 3) + 2]; + collapse = "left"; + } } + if (bias == "right" && start == mEnd - mStart) + { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) { + node = map[(i += 3) + 2]; + collapse = "right"; + } } + break + } + } + return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd} + } + + function getUsefulRect(rects, bias) { + var rect = nullRect; + if (bias == "left") { for (var i = 0; i < rects.length; i++) { + if ((rect = rects[i]).left != rect.right) { break } + } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) { + if ((rect = rects[i$1]).left != rect.right) { break } + } } + return rect + } + + function measureCharInner(cm, prepared, ch, bias) { + var place = nodeAndOffsetInLineMap(prepared.map, ch, bias); + var node = place.node, start = place.start, end = place.end, collapse = place.collapse; + + var rect; + if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates. + for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned + while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; } + while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; } + if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) + { rect = node.parentNode.getBoundingClientRect(); } + else + { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); } + if (rect.left || rect.right || start == 0) { break } + end = start; + start = start - 1; + collapse = "right"; + } + if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); } + } else { // If it is a widget, simply get the box for the whole widget. + if (start > 0) { collapse = bias = "right"; } + var rects; + if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1) + { rect = rects[bias == "right" ? rects.length - 1 : 0]; } + else + { rect = node.getBoundingClientRect(); } + } + if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) { + var rSpan = node.parentNode.getClientRects()[0]; + if (rSpan) + { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; } + else + { rect = nullRect; } + } + + var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top; + var mid = (rtop + rbot) / 2; + var heights = prepared.view.measure.heights; + var i = 0; + for (; i < heights.length - 1; i++) + { if (mid < heights[i]) { break } } + var top = i ? heights[i - 1] : 0, bot = heights[i]; + var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left, + right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left, + top: top, bottom: bot}; + if (!rect.left && !rect.right) { result.bogus = true; } + if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; } + + return result + } + + // Work around problem with bounding client rects on ranges being + // returned incorrectly when zoomed on IE10 and below. + function maybeUpdateRectForZooming(measure, rect) { + if (!window.screen || screen.logicalXDPI == null || + screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure)) + { return rect } + var scaleX = screen.logicalXDPI / screen.deviceXDPI; + var scaleY = screen.logicalYDPI / screen.deviceYDPI; + return {left: rect.left * scaleX, right: rect.right * scaleX, + top: rect.top * scaleY, bottom: rect.bottom * scaleY} + } + + function clearLineMeasurementCacheFor(lineView) { + if (lineView.measure) { + lineView.measure.cache = {}; + lineView.measure.heights = null; + if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) + { lineView.measure.caches[i] = {}; } } + } + } + + function clearLineMeasurementCache(cm) { + cm.display.externalMeasure = null; + removeChildren(cm.display.lineMeasure); + for (var i = 0; i < cm.display.view.length; i++) + { clearLineMeasurementCacheFor(cm.display.view[i]); } + } + + function clearCaches(cm) { + clearLineMeasurementCache(cm); + cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null; + if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; } + cm.display.lineNumChars = null; + } + + function pageScrollX() { + // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206 + // which causes page_Offset and bounding client rects to use + // different reference viewports and invalidate our calculations. + if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) } + return window.pageXOffset || (document.documentElement || document.body).scrollLeft + } + function pageScrollY() { + if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) } + return window.pageYOffset || (document.documentElement || document.body).scrollTop + } + + function widgetTopHeight(lineObj) { + var height = 0; + if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above) + { height += widgetHeight(lineObj.widgets[i]); } } } + return height + } + + // Converts a {top, bottom, left, right} box from line-local + // coordinates into another coordinate system. Context may be one of + // "line", "div" (display.lineDiv), "local"./null (editor), "window", + // or "page". + function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) { + if (!includeWidgets) { + var height = widgetTopHeight(lineObj); + rect.top += height; rect.bottom += height; + } + if (context == "line") { return rect } + if (!context) { context = "local"; } + var yOff = heightAtLine(lineObj); + if (context == "local") { yOff += paddingTop(cm.display); } + else { yOff -= cm.display.viewOffset; } + if (context == "page" || context == "window") { + var lOff = cm.display.lineSpace.getBoundingClientRect(); + yOff += lOff.top + (context == "window" ? 0 : pageScrollY()); + var xOff = lOff.left + (context == "window" ? 0 : pageScrollX()); + rect.left += xOff; rect.right += xOff; + } + rect.top += yOff; rect.bottom += yOff; + return rect + } + + // Coverts a box from "div" coords to another coordinate system. + // Context may be "window", "page", "div", or "local"./null. + function fromCoordSystem(cm, coords, context) { + if (context == "div") { return coords } + var left = coords.left, top = coords.top; + // First move into "page" coordinate system + if (context == "page") { + left -= pageScrollX(); + top -= pageScrollY(); + } else if (context == "local" || !context) { + var localBox = cm.display.sizer.getBoundingClientRect(); + left += localBox.left; + top += localBox.top; + } + + var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect(); + return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top} + } + + function charCoords(cm, pos, context, lineObj, bias) { + if (!lineObj) { lineObj = getLine(cm.doc, pos.line); } + return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context) + } + + // Returns a box for a given cursor position, which may have an + // 'other' property containing the position of the secondary cursor + // on a bidi boundary. + // A cursor Pos(line, char, "before") is on the same visual line as `char - 1` + // and after `char - 1` in writing order of `char - 1` + // A cursor Pos(line, char, "after") is on the same visual line as `char` + // and before `char` in writing order of `char` + // Examples (upper-case letters are RTL, lower-case are LTR): + // Pos(0, 1, ...) + // before after + // ab a|b a|b + // aB a|B aB| + // Ab |Ab A|b + // AB B|A B|A + // Every position after the last character on a line is considered to stick + // to the last character on the line. + function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) { + lineObj = lineObj || getLine(cm.doc, pos.line); + if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); } + function get(ch, right) { + var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight); + if (right) { m.left = m.right; } else { m.right = m.left; } + return intoCoordSystem(cm, lineObj, m, context) + } + var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky; + if (ch >= lineObj.text.length) { + ch = lineObj.text.length; + sticky = "before"; + } else if (ch <= 0) { + ch = 0; + sticky = "after"; + } + if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") } + + function getBidi(ch, partPos, invert) { + var part = order[partPos], right = part.level == 1; + return get(invert ? ch - 1 : ch, right != invert) + } + var partPos = getBidiPartAt(order, ch, sticky); + var other = bidiOther; + var val = getBidi(ch, partPos, sticky == "before"); + if (other != null) { val.other = getBidi(ch, other, sticky != "before"); } + return val + } + + // Used to cheaply estimate the coordinates for a position. Used for + // intermediate scroll updates. + function estimateCoords(cm, pos) { + var left = 0; + pos = clipPos(cm.doc, pos); + if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; } + var lineObj = getLine(cm.doc, pos.line); + var top = heightAtLine(lineObj) + paddingTop(cm.display); + return {left: left, right: left, top: top, bottom: top + lineObj.height} + } + + // Positions returned by coordsChar contain some extra information. + // xRel is the relative x position of the input coordinates compared + // to the found position (so xRel > 0 means the coordinates are to + // the right of the character position, for example). When outside + // is true, that means the coordinates lie outside the line's + // vertical range. + function PosWithInfo(line, ch, sticky, outside, xRel) { + var pos = Pos(line, ch, sticky); + pos.xRel = xRel; + if (outside) { pos.outside = outside; } + return pos + } + + // Compute the character position closest to the given coordinates. + // Input must be lineSpace-local ("div" coordinate system). + function coordsChar(cm, x, y) { + var doc = cm.doc; + y += cm.display.viewOffset; + if (y < 0) { return PosWithInfo(doc.first, 0, null, -1, -1) } + var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1; + if (lineN > last) + { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1) } + if (x < 0) { x = 0; } + + var lineObj = getLine(doc, lineN); + for (;;) { + var found = coordsCharInner(cm, lineObj, lineN, x, y); + var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0)); + if (!collapsed) { return found } + var rangeEnd = collapsed.find(1); + if (rangeEnd.line == lineN) { return rangeEnd } + lineObj = getLine(doc, lineN = rangeEnd.line); + } + } + + function wrappedLineExtent(cm, lineObj, preparedMeasure, y) { + y -= widgetTopHeight(lineObj); + var end = lineObj.text.length; + var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0); + end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end); + return {begin: begin, end: end} + } + + function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) { + if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); } + var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top; + return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop) + } + + // Returns true if the given side of a box is after the given + // coordinates, in top-to-bottom, left-to-right order. + function boxIsAfter(box, x, y, left) { + return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x + } + + function coordsCharInner(cm, lineObj, lineNo, x, y) { + // Move y into line-local coordinate space + y -= heightAtLine(lineObj); + var preparedMeasure = prepareMeasureForLine(cm, lineObj); + // When directly calling `measureCharPrepared`, we have to adjust + // for the widgets at this line. + var widgetHeight = widgetTopHeight(lineObj); + var begin = 0, end = lineObj.text.length, ltr = true; + + var order = getOrder(lineObj, cm.doc.direction); + // If the line isn't plain left-to-right text, first figure out + // which bidi section the coordinates fall into. + if (order) { + var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart) + (cm, lineObj, lineNo, preparedMeasure, order, x, y); + ltr = part.level != 1; + // The awkward -1 offsets are needed because findFirst (called + // on these below) will treat its first bound as inclusive, + // second as exclusive, but we want to actually address the + // characters in the part's range + begin = ltr ? part.from : part.to - 1; + end = ltr ? part.to : part.from - 1; + } + + // A binary search to find the first character whose bounding box + // starts after the coordinates. If we run across any whose box wrap + // the coordinates, store that. + var chAround = null, boxAround = null; + var ch = findFirst(function (ch) { + var box = measureCharPrepared(cm, preparedMeasure, ch); + box.top += widgetHeight; box.bottom += widgetHeight; + if (!boxIsAfter(box, x, y, false)) { return false } + if (box.top <= y && box.left <= x) { + chAround = ch; + boxAround = box; + } + return true + }, begin, end); + + var baseX, sticky, outside = false; + // If a box around the coordinates was found, use that + if (boxAround) { + // Distinguish coordinates nearer to the left or right side of the box + var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr; + ch = chAround + (atStart ? 0 : 1); + sticky = atStart ? "after" : "before"; + baseX = atLeft ? boxAround.left : boxAround.right; + } else { + // (Adjust for extended bound, if necessary.) + if (!ltr && (ch == end || ch == begin)) { ch++; } + // To determine which side to associate with, get the box to the + // left of the character and compare it's vertical position to the + // coordinates + sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" : + (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ? + "after" : "before"; + // Now get accurate coordinates for this place, in order to get a + // base X position + var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure); + baseX = coords.left; + outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0; + } + + ch = skipExtendingChars(lineObj.text, ch, 1); + return PosWithInfo(lineNo, ch, sticky, outside, x - baseX) + } + + function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) { + // Bidi parts are sorted left-to-right, and in a non-line-wrapping + // situation, we can take this ordering to correspond to the visual + // ordering. This finds the first part whose end is after the given + // coordinates. + var index = findFirst(function (i) { + var part = order[i], ltr = part.level != 1; + return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"), + "line", lineObj, preparedMeasure), x, y, true) + }, 0, order.length - 1); + var part = order[index]; + // If this isn't the first part, the part's start is also after + // the coordinates, and the coordinates aren't on the same line as + // that start, move one part back. + if (index > 0) { + var ltr = part.level != 1; + var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"), + "line", lineObj, preparedMeasure); + if (boxIsAfter(start, x, y, true) && start.top > y) + { part = order[index - 1]; } + } + return part + } + + function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) { + // In a wrapped line, rtl text on wrapping boundaries can do things + // that don't correspond to the ordering in our `order` array at + // all, so a binary search doesn't work, and we want to return a + // part that only spans one line so that the binary search in + // coordsCharInner is safe. As such, we first find the extent of the + // wrapped line, and then do a flat search in which we discard any + // spans that aren't on the line. + var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y); + var begin = ref.begin; + var end = ref.end; + if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; } + var part = null, closestDist = null; + for (var i = 0; i < order.length; i++) { + var p = order[i]; + if (p.from >= end || p.to <= begin) { continue } + var ltr = p.level != 1; + var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right; + // Weigh against spans ending before this, so that they are only + // picked if nothing ends after + var dist = endX < x ? x - endX + 1e9 : endX - x; + if (!part || closestDist > dist) { + part = p; + closestDist = dist; + } + } + if (!part) { part = order[order.length - 1]; } + // Clip the part to the wrapped line. + if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; } + if (part.to > end) { part = {from: part.from, to: end, level: part.level}; } + return part + } + + var measureText; + // Compute the default text height. + function textHeight(display) { + if (display.cachedTextHeight != null) { return display.cachedTextHeight } + if (measureText == null) { + measureText = elt("pre", null, "CodeMirror-line-like"); + // Measure a bunch of lines, for browsers that compute + // fractional heights. + for (var i = 0; i < 49; ++i) { + measureText.appendChild(document.createTextNode("x")); + measureText.appendChild(elt("br")); + } + measureText.appendChild(document.createTextNode("x")); + } + removeChildrenAndAdd(display.measure, measureText); + var height = measureText.offsetHeight / 50; + if (height > 3) { display.cachedTextHeight = height; } + removeChildren(display.measure); + return height || 1 + } + + // Compute the default character width. + function charWidth(display) { + if (display.cachedCharWidth != null) { return display.cachedCharWidth } + var anchor = elt("span", "xxxxxxxxxx"); + var pre = elt("pre", [anchor], "CodeMirror-line-like"); + removeChildrenAndAdd(display.measure, pre); + var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10; + if (width > 2) { display.cachedCharWidth = width; } + return width || 10 + } + + // Do a bulk-read of the DOM positions and sizes needed to draw the + // view, so that we don't interleave reading and writing to the DOM. + function getDimensions(cm) { + var d = cm.display, left = {}, width = {}; + var gutterLeft = d.gutters.clientLeft; + for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) { + var id = cm.display.gutterSpecs[i].className; + left[id] = n.offsetLeft + n.clientLeft + gutterLeft; + width[id] = n.clientWidth; + } + return {fixedPos: compensateForHScroll(d), + gutterTotalWidth: d.gutters.offsetWidth, + gutterLeft: left, + gutterWidth: width, + wrapperWidth: d.wrapper.clientWidth} + } + + // Computes display.scroller.scrollLeft + display.gutters.offsetWidth, + // but using getBoundingClientRect to get a sub-pixel-accurate + // result. + function compensateForHScroll(display) { + return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left + } + + // Returns a function that estimates the height of a line, to use as + // first approximation until the line becomes visible (and is thus + // properly measurable). + function estimateHeight(cm) { + var th = textHeight(cm.display), wrapping = cm.options.lineWrapping; + var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3); + return function (line) { + if (lineIsHidden(cm.doc, line)) { return 0 } + + var widgetsHeight = 0; + if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) { + if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; } + } } + + if (wrapping) + { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th } + else + { return widgetsHeight + th } + } + } + + function estimateLineHeights(cm) { + var doc = cm.doc, est = estimateHeight(cm); + doc.iter(function (line) { + var estHeight = est(line); + if (estHeight != line.height) { updateLineHeight(line, estHeight); } + }); + } + + // Given a mouse event, find the corresponding position. If liberal + // is false, it checks whether a gutter or scrollbar was clicked, + // and returns null if it was. forRect is used by rectangular + // selections, and tries to estimate a character position even for + // coordinates beyond the right of the text. + function posFromMouse(cm, e, liberal, forRect) { + var display = cm.display; + if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null } + + var x, y, space = display.lineSpace.getBoundingClientRect(); + // Fails unpredictably on IE[67] when mouse is dragged around quickly. + try { x = e.clientX - space.left; y = e.clientY - space.top; } + catch (e$1) { return null } + var coords = coordsChar(cm, x, y), line; + if (forRect && coords.xRel > 0 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) { + var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length; + coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff)); + } + return coords + } + + // Find the view element corresponding to a given line. Return null + // when the line isn't visible. + function findViewIndex(cm, n) { + if (n >= cm.display.viewTo) { return null } + n -= cm.display.viewFrom; + if (n < 0) { return null } + var view = cm.display.view; + for (var i = 0; i < view.length; i++) { + n -= view[i].size; + if (n < 0) { return i } + } + } + + // Updates the display.view data structure for a given change to the + // document. From and to are in pre-change coordinates. Lendiff is + // the amount of lines added or subtracted by the change. This is + // used for changes that span multiple lines, or change the way + // lines are divided into visual lines. regLineChange (below) + // registers single-line changes. + function regChange(cm, from, to, lendiff) { + if (from == null) { from = cm.doc.first; } + if (to == null) { to = cm.doc.first + cm.doc.size; } + if (!lendiff) { lendiff = 0; } + + var display = cm.display; + if (lendiff && to < display.viewTo && + (display.updateLineNumbers == null || display.updateLineNumbers > from)) + { display.updateLineNumbers = from; } + + cm.curOp.viewChanged = true; + + if (from >= display.viewTo) { // Change after + if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo) + { resetView(cm); } + } else if (to <= display.viewFrom) { // Change before + if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) { + resetView(cm); + } else { + display.viewFrom += lendiff; + display.viewTo += lendiff; + } + } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap + resetView(cm); + } else if (from <= display.viewFrom) { // Top overlap + var cut = viewCuttingPoint(cm, to, to + lendiff, 1); + if (cut) { + display.view = display.view.slice(cut.index); + display.viewFrom = cut.lineN; + display.viewTo += lendiff; + } else { + resetView(cm); + } + } else if (to >= display.viewTo) { // Bottom overlap + var cut$1 = viewCuttingPoint(cm, from, from, -1); + if (cut$1) { + display.view = display.view.slice(0, cut$1.index); + display.viewTo = cut$1.lineN; + } else { + resetView(cm); + } + } else { // Gap in the middle + var cutTop = viewCuttingPoint(cm, from, from, -1); + var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1); + if (cutTop && cutBot) { + display.view = display.view.slice(0, cutTop.index) + .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN)) + .concat(display.view.slice(cutBot.index)); + display.viewTo += lendiff; + } else { + resetView(cm); + } + } + + var ext = display.externalMeasured; + if (ext) { + if (to < ext.lineN) + { ext.lineN += lendiff; } + else if (from < ext.lineN + ext.size) + { display.externalMeasured = null; } + } + } + + // Register a change to a single line. Type must be one of "text", + // "gutter", "class", "widget" + function regLineChange(cm, line, type) { + cm.curOp.viewChanged = true; + var display = cm.display, ext = cm.display.externalMeasured; + if (ext && line >= ext.lineN && line < ext.lineN + ext.size) + { display.externalMeasured = null; } + + if (line < display.viewFrom || line >= display.viewTo) { return } + var lineView = display.view[findViewIndex(cm, line)]; + if (lineView.node == null) { return } + var arr = lineView.changes || (lineView.changes = []); + if (indexOf(arr, type) == -1) { arr.push(type); } + } + + // Clear the view. + function resetView(cm) { + cm.display.viewFrom = cm.display.viewTo = cm.doc.first; + cm.display.view = []; + cm.display.viewOffset = 0; + } + + function viewCuttingPoint(cm, oldN, newN, dir) { + var index = findViewIndex(cm, oldN), diff, view = cm.display.view; + if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size) + { return {index: index, lineN: newN} } + var n = cm.display.viewFrom; + for (var i = 0; i < index; i++) + { n += view[i].size; } + if (n != oldN) { + if (dir > 0) { + if (index == view.length - 1) { return null } + diff = (n + view[index].size) - oldN; + index++; + } else { + diff = n - oldN; + } + oldN += diff; newN += diff; + } + while (visualLineNo(cm.doc, newN) != newN) { + if (index == (dir < 0 ? 0 : view.length - 1)) { return null } + newN += dir * view[index - (dir < 0 ? 1 : 0)].size; + index += dir; + } + return {index: index, lineN: newN} + } + + // Force the view to cover a given range, adding empty view element + // or clipping off existing ones as needed. + function adjustView(cm, from, to) { + var display = cm.display, view = display.view; + if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) { + display.view = buildViewArray(cm, from, to); + display.viewFrom = from; + } else { + if (display.viewFrom > from) + { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); } + else if (display.viewFrom < from) + { display.view = display.view.slice(findViewIndex(cm, from)); } + display.viewFrom = from; + if (display.viewTo < to) + { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); } + else if (display.viewTo > to) + { display.view = display.view.slice(0, findViewIndex(cm, to)); } + } + display.viewTo = to; + } + + // Count the number of lines in the view whose DOM representation is + // out of date (or nonexistent). + function countDirtyView(cm) { + var view = cm.display.view, dirty = 0; + for (var i = 0; i < view.length; i++) { + var lineView = view[i]; + if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; } + } + return dirty + } + + function updateSelection(cm) { + cm.display.input.showSelection(cm.display.input.prepareSelection()); + } + + function prepareSelection(cm, primary) { + if ( primary === void 0 ) primary = true; + + var doc = cm.doc, result = {}; + var curFragment = result.cursors = document.createDocumentFragment(); + var selFragment = result.selection = document.createDocumentFragment(); + + for (var i = 0; i < doc.sel.ranges.length; i++) { + if (!primary && i == doc.sel.primIndex) { continue } + var range = doc.sel.ranges[i]; + if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue } + var collapsed = range.empty(); + if (collapsed || cm.options.showCursorWhenSelecting) + { drawSelectionCursor(cm, range.head, curFragment); } + if (!collapsed) + { drawSelectionRange(cm, range, selFragment); } + } + return result + } + + // Draws a cursor for the given range + function drawSelectionCursor(cm, head, output) { + var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine); + + var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")); + cursor.style.left = pos.left + "px"; + cursor.style.top = pos.top + "px"; + cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"; + + if (pos.other) { + // Secondary cursor, shown when on a 'jump' in bi-directional text + var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor")); + otherCursor.style.display = ""; + otherCursor.style.left = pos.other.left + "px"; + otherCursor.style.top = pos.other.top + "px"; + otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"; + } + } + + function cmpCoords(a, b) { return a.top - b.top || a.left - b.left } + + // Draws the given range as a highlighted selection + function drawSelectionRange(cm, range, output) { + var display = cm.display, doc = cm.doc; + var fragment = document.createDocumentFragment(); + var padding = paddingH(cm.display), leftSide = padding.left; + var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right; + var docLTR = doc.direction == "ltr"; + + function add(left, top, width, bottom) { + if (top < 0) { top = 0; } + top = Math.round(top); + bottom = Math.round(bottom); + fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: absolute; left: " + left + "px;\n top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px;\n height: " + (bottom - top) + "px"))); + } + + function drawForLine(line, fromArg, toArg) { + var lineObj = getLine(doc, line); + var lineLen = lineObj.text.length; + var start, end; + function coords(ch, bias) { + return charCoords(cm, Pos(line, ch), "div", lineObj, bias) + } + + function wrapX(pos, dir, side) { + var extent = wrappedLineExtentChar(cm, lineObj, null, pos); + var prop = (dir == "ltr") == (side == "after") ? "left" : "right"; + var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1); + return coords(ch, prop)[prop] + } + + var order = getOrder(lineObj, doc.direction); + iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) { + var ltr = dir == "ltr"; + var fromPos = coords(from, ltr ? "left" : "right"); + var toPos = coords(to - 1, ltr ? "right" : "left"); + + var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen; + var first = i == 0, last = !order || i == order.length - 1; + if (toPos.top - fromPos.top <= 3) { // Single line + var openLeft = (docLTR ? openStart : openEnd) && first; + var openRight = (docLTR ? openEnd : openStart) && last; + var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left; + var right = openRight ? rightSide : (ltr ? toPos : fromPos).right; + add(left, fromPos.top, right - left, fromPos.bottom); + } else { // Multiple lines + var topLeft, topRight, botLeft, botRight; + if (ltr) { + topLeft = docLTR && openStart && first ? leftSide : fromPos.left; + topRight = docLTR ? rightSide : wrapX(from, dir, "before"); + botLeft = docLTR ? leftSide : wrapX(to, dir, "after"); + botRight = docLTR && openEnd && last ? rightSide : toPos.right; + } else { + topLeft = !docLTR ? leftSide : wrapX(from, dir, "before"); + topRight = !docLTR && openStart && first ? rightSide : fromPos.right; + botLeft = !docLTR && openEnd && last ? leftSide : toPos.left; + botRight = !docLTR ? rightSide : wrapX(to, dir, "after"); + } + add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom); + if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); } + add(botLeft, toPos.top, botRight - botLeft, toPos.bottom); + } + + if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; } + if (cmpCoords(toPos, start) < 0) { start = toPos; } + if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; } + if (cmpCoords(toPos, end) < 0) { end = toPos; } + }); + return {start: start, end: end} + } + + var sFrom = range.from(), sTo = range.to(); + if (sFrom.line == sTo.line) { + drawForLine(sFrom.line, sFrom.ch, sTo.ch); + } else { + var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line); + var singleVLine = visualLine(fromLine) == visualLine(toLine); + var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end; + var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start; + if (singleVLine) { + if (leftEnd.top < rightStart.top - 2) { + add(leftEnd.right, leftEnd.top, null, leftEnd.bottom); + add(leftSide, rightStart.top, rightStart.left, rightStart.bottom); + } else { + add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom); + } + } + if (leftEnd.bottom < rightStart.top) + { add(leftSide, leftEnd.bottom, null, rightStart.top); } + } + + output.appendChild(fragment); + } + + // Cursor-blinking + function restartBlink(cm) { + if (!cm.state.focused) { return } + var display = cm.display; + clearInterval(display.blinker); + var on = true; + display.cursorDiv.style.visibility = ""; + if (cm.options.cursorBlinkRate > 0) + { display.blinker = setInterval(function () { + if (!cm.hasFocus()) { onBlur(cm); } + display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; + }, cm.options.cursorBlinkRate); } + else if (cm.options.cursorBlinkRate < 0) + { display.cursorDiv.style.visibility = "hidden"; } + } + + function ensureFocus(cm) { + if (!cm.hasFocus()) { + cm.display.input.focus(); + if (!cm.state.focused) { onFocus(cm); } + } + } + + function delayBlurEvent(cm) { + cm.state.delayingBlurEvent = true; + setTimeout(function () { if (cm.state.delayingBlurEvent) { + cm.state.delayingBlurEvent = false; + if (cm.state.focused) { onBlur(cm); } + } }, 100); + } + + function onFocus(cm, e) { + if (cm.state.delayingBlurEvent && !cm.state.draggingText) { cm.state.delayingBlurEvent = false; } + + if (cm.options.readOnly == "nocursor") { return } + if (!cm.state.focused) { + signal(cm, "focus", cm, e); + cm.state.focused = true; + addClass(cm.display.wrapper, "CodeMirror-focused"); + // This test prevents this from firing when a context + // menu is closed (since the input reset would kill the + // select-all detection hack) + if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) { + cm.display.input.reset(); + if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730 + } + cm.display.input.receivedFocus(); + } + restartBlink(cm); + } + function onBlur(cm, e) { + if (cm.state.delayingBlurEvent) { return } + + if (cm.state.focused) { + signal(cm, "blur", cm, e); + cm.state.focused = false; + rmClass(cm.display.wrapper, "CodeMirror-focused"); + } + clearInterval(cm.display.blinker); + setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150); + } + + // Read the actual heights of the rendered lines, and update their + // stored heights to match. + function updateHeightsInViewport(cm) { + var display = cm.display; + var prevBottom = display.lineDiv.offsetTop; + for (var i = 0; i < display.view.length; i++) { + var cur = display.view[i], wrapping = cm.options.lineWrapping; + var height = (void 0), width = 0; + if (cur.hidden) { continue } + if (ie && ie_version < 8) { + var bot = cur.node.offsetTop + cur.node.offsetHeight; + height = bot - prevBottom; + prevBottom = bot; + } else { + var box = cur.node.getBoundingClientRect(); + height = box.bottom - box.top; + // Check that lines don't extend past the right of the current + // editor width + if (!wrapping && cur.text.firstChild) + { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; } + } + var diff = cur.line.height - height; + if (diff > .005 || diff < -.005) { + updateLineHeight(cur.line, height); + updateWidgetHeight(cur.line); + if (cur.rest) { for (var j = 0; j < cur.rest.length; j++) + { updateWidgetHeight(cur.rest[j]); } } + } + if (width > cm.display.sizerWidth) { + var chWidth = Math.ceil(width / charWidth(cm.display)); + if (chWidth > cm.display.maxLineLength) { + cm.display.maxLineLength = chWidth; + cm.display.maxLine = cur.line; + cm.display.maxLineChanged = true; + } + } + } + } + + // Read and store the height of line widgets associated with the + // given line. + function updateWidgetHeight(line) { + if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) { + var w = line.widgets[i], parent = w.node.parentNode; + if (parent) { w.height = parent.offsetHeight; } + } } + } + + // Compute the lines that are visible in a given viewport (defaults + // the the current scroll position). viewport may contain top, + // height, and ensure (see op.scrollToPos) properties. + function visibleLines(display, doc, viewport) { + var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop; + top = Math.floor(top - paddingTop(display)); + var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight; + + var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom); + // Ensure is a {from: {line, ch}, to: {line, ch}} object, and + // forces those lines into the viewport (if possible). + if (viewport && viewport.ensure) { + var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line; + if (ensureFrom < from) { + from = ensureFrom; + to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight); + } else if (Math.min(ensureTo, doc.lastLine()) >= to) { + from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight); + to = ensureTo; + } + } + return {from: from, to: Math.max(to, from + 1)} + } + + // SCROLLING THINGS INTO VIEW + + // If an editor sits on the top or bottom of the window, partially + // scrolled out of view, this ensures that the cursor is visible. + function maybeScrollWindow(cm, rect) { + if (signalDOMEvent(cm, "scrollCursorIntoView")) { return } + + var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null; + if (rect.top + box.top < 0) { doScroll = true; } + else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; } + if (doScroll != null && !phantom) { + var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n top: " + (rect.top - display.viewOffset - paddingTop(cm.display)) + "px;\n height: " + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + "px;\n left: " + (rect.left) + "px; width: " + (Math.max(2, rect.right - rect.left)) + "px;")); + cm.display.lineSpace.appendChild(scrollNode); + scrollNode.scrollIntoView(doScroll); + cm.display.lineSpace.removeChild(scrollNode); + } + } + + // Scroll a given position into view (immediately), verifying that + // it actually became visible (as line heights are accurately + // measured, the position of something may 'drift' during drawing). + function scrollPosIntoView(cm, pos, end, margin) { + if (margin == null) { margin = 0; } + var rect; + if (!cm.options.lineWrapping && pos == end) { + // Set pos and end to the cursor positions around the character pos sticks to + // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch + // If pos == Pos(_, 0, "before"), pos and end are unchanged + pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos; + end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos; + } + for (var limit = 0; limit < 5; limit++) { + var changed = false; + var coords = cursorCoords(cm, pos); + var endCoords = !end || end == pos ? coords : cursorCoords(cm, end); + rect = {left: Math.min(coords.left, endCoords.left), + top: Math.min(coords.top, endCoords.top) - margin, + right: Math.max(coords.left, endCoords.left), + bottom: Math.max(coords.bottom, endCoords.bottom) + margin}; + var scrollPos = calculateScrollPos(cm, rect); + var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft; + if (scrollPos.scrollTop != null) { + updateScrollTop(cm, scrollPos.scrollTop); + if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; } + } + if (scrollPos.scrollLeft != null) { + setScrollLeft(cm, scrollPos.scrollLeft); + if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; } + } + if (!changed) { break } + } + return rect + } + + // Scroll a given set of coordinates into view (immediately). + function scrollIntoView(cm, rect) { + var scrollPos = calculateScrollPos(cm, rect); + if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); } + if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); } + } + + // Calculate a new scroll position needed to scroll the given + // rectangle into view. Returns an object with scrollTop and + // scrollLeft properties. When these are undefined, the + // vertical/horizontal position does not need to be adjusted. + function calculateScrollPos(cm, rect) { + var display = cm.display, snapMargin = textHeight(cm.display); + if (rect.top < 0) { rect.top = 0; } + var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop; + var screen = displayHeight(cm), result = {}; + if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; } + var docBottom = cm.doc.height + paddingVert(display); + var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin; + if (rect.top < screentop) { + result.scrollTop = atTop ? 0 : rect.top; + } else if (rect.bottom > screentop + screen) { + var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen); + if (newTop != screentop) { result.scrollTop = newTop; } + } + + var gutterSpace = cm.options.fixedGutter ? 0 : display.gutters.offsetWidth; + var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft - gutterSpace; + var screenw = displayWidth(cm) - display.gutters.offsetWidth; + var tooWide = rect.right - rect.left > screenw; + if (tooWide) { rect.right = rect.left + screenw; } + if (rect.left < 10) + { result.scrollLeft = 0; } + else if (rect.left < screenleft) + { result.scrollLeft = Math.max(0, rect.left + gutterSpace - (tooWide ? 0 : 10)); } + else if (rect.right > screenw + screenleft - 3) + { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; } + return result + } + + // Store a relative adjustment to the scroll position in the current + // operation (to be applied when the operation finishes). + function addToScrollTop(cm, top) { + if (top == null) { return } + resolveScrollToPos(cm); + cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top; + } + + // Make sure that at the end of the operation the current cursor is + // shown. + function ensureCursorVisible(cm) { + resolveScrollToPos(cm); + var cur = cm.getCursor(); + cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin}; + } + + function scrollToCoords(cm, x, y) { + if (x != null || y != null) { resolveScrollToPos(cm); } + if (x != null) { cm.curOp.scrollLeft = x; } + if (y != null) { cm.curOp.scrollTop = y; } + } + + function scrollToRange(cm, range) { + resolveScrollToPos(cm); + cm.curOp.scrollToPos = range; + } + + // When an operation has its scrollToPos property set, and another + // scroll action is applied before the end of the operation, this + // 'simulates' scrolling that position into view in a cheap way, so + // that the effect of intermediate scroll commands is not ignored. + function resolveScrollToPos(cm) { + var range = cm.curOp.scrollToPos; + if (range) { + cm.curOp.scrollToPos = null; + var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to); + scrollToCoordsRange(cm, from, to, range.margin); + } + } + + function scrollToCoordsRange(cm, from, to, margin) { + var sPos = calculateScrollPos(cm, { + left: Math.min(from.left, to.left), + top: Math.min(from.top, to.top) - margin, + right: Math.max(from.right, to.right), + bottom: Math.max(from.bottom, to.bottom) + margin + }); + scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop); + } + + // Sync the scrollable area and scrollbars, ensure the viewport + // covers the visible area. + function updateScrollTop(cm, val) { + if (Math.abs(cm.doc.scrollTop - val) < 2) { return } + if (!gecko) { updateDisplaySimple(cm, {top: val}); } + setScrollTop(cm, val, true); + if (gecko) { updateDisplaySimple(cm); } + startWorker(cm, 100); + } + + function setScrollTop(cm, val, forceScroll) { + val = Math.max(0, Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val)); + if (cm.display.scroller.scrollTop == val && !forceScroll) { return } + cm.doc.scrollTop = val; + cm.display.scrollbars.setScrollTop(val); + if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; } + } + + // Sync scroller and scrollbar, ensure the gutter elements are + // aligned. + function setScrollLeft(cm, val, isScroller, forceScroll) { + val = Math.max(0, Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth)); + if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return } + cm.doc.scrollLeft = val; + alignHorizontally(cm); + if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; } + cm.display.scrollbars.setScrollLeft(val); + } + + // SCROLLBARS + + // Prepare DOM reads needed to update the scrollbars. Done in one + // shot to minimize update/measure roundtrips. + function measureForScrollbars(cm) { + var d = cm.display, gutterW = d.gutters.offsetWidth; + var docH = Math.round(cm.doc.height + paddingVert(cm.display)); + return { + clientHeight: d.scroller.clientHeight, + viewHeight: d.wrapper.clientHeight, + scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth, + viewWidth: d.wrapper.clientWidth, + barLeft: cm.options.fixedGutter ? gutterW : 0, + docHeight: docH, + scrollHeight: docH + scrollGap(cm) + d.barHeight, + nativeBarWidth: d.nativeBarWidth, + gutterWidth: gutterW + } + } + + var NativeScrollbars = function(place, scroll, cm) { + this.cm = cm; + var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar"); + var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar"); + vert.tabIndex = horiz.tabIndex = -1; + place(vert); place(horiz); + + on(vert, "scroll", function () { + if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); } + }); + on(horiz, "scroll", function () { + if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); } + }); + + this.checkedZeroWidth = false; + // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). + if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; } + }; + + NativeScrollbars.prototype.update = function (measure) { + var needsH = measure.scrollWidth > measure.clientWidth + 1; + var needsV = measure.scrollHeight > measure.clientHeight + 1; + var sWidth = measure.nativeBarWidth; + + if (needsV) { + this.vert.style.display = "block"; + this.vert.style.bottom = needsH ? sWidth + "px" : "0"; + var totalHeight = measure.viewHeight - (needsH ? sWidth : 0); + // A bug in IE8 can cause this value to be negative, so guard it. + this.vert.firstChild.style.height = + Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"; + } else { + this.vert.style.display = ""; + this.vert.firstChild.style.height = "0"; + } + + if (needsH) { + this.horiz.style.display = "block"; + this.horiz.style.right = needsV ? sWidth + "px" : "0"; + this.horiz.style.left = measure.barLeft + "px"; + var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0); + this.horiz.firstChild.style.width = + Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px"; + } else { + this.horiz.style.display = ""; + this.horiz.firstChild.style.width = "0"; + } + + if (!this.checkedZeroWidth && measure.clientHeight > 0) { + if (sWidth == 0) { this.zeroWidthHack(); } + this.checkedZeroWidth = true; + } + + return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0} + }; + + NativeScrollbars.prototype.setScrollLeft = function (pos) { + if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; } + if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); } + }; + + NativeScrollbars.prototype.setScrollTop = function (pos) { + if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; } + if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); } + }; + + NativeScrollbars.prototype.zeroWidthHack = function () { + var w = mac && !mac_geMountainLion ? "12px" : "18px"; + this.horiz.style.height = this.vert.style.width = w; + this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none"; + this.disableHoriz = new Delayed; + this.disableVert = new Delayed; + }; + + NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) { + bar.style.pointerEvents = "auto"; + function maybeDisable() { + // To find out whether the scrollbar is still visible, we + // check whether the element under the pixel in the bottom + // right corner of the scrollbar box is the scrollbar box + // itself (when the bar is still visible) or its filler child + // (when the bar is hidden). If it is still visible, we keep + // it enabled, if it's hidden, we disable pointer events. + var box = bar.getBoundingClientRect(); + var elt = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2) + : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1); + if (elt != bar) { bar.style.pointerEvents = "none"; } + else { delay.set(1000, maybeDisable); } + } + delay.set(1000, maybeDisable); + }; + + NativeScrollbars.prototype.clear = function () { + var parent = this.horiz.parentNode; + parent.removeChild(this.horiz); + parent.removeChild(this.vert); + }; + + var NullScrollbars = function () {}; + + NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} }; + NullScrollbars.prototype.setScrollLeft = function () {}; + NullScrollbars.prototype.setScrollTop = function () {}; + NullScrollbars.prototype.clear = function () {}; + + function updateScrollbars(cm, measure) { + if (!measure) { measure = measureForScrollbars(cm); } + var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight; + updateScrollbarsInner(cm, measure); + for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) { + if (startWidth != cm.display.barWidth && cm.options.lineWrapping) + { updateHeightsInViewport(cm); } + updateScrollbarsInner(cm, measureForScrollbars(cm)); + startWidth = cm.display.barWidth; startHeight = cm.display.barHeight; + } + } + + // Re-synchronize the fake scrollbars with the actual size of the + // content. + function updateScrollbarsInner(cm, measure) { + var d = cm.display; + var sizes = d.scrollbars.update(measure); + + d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"; + d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"; + d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"; + + if (sizes.right && sizes.bottom) { + d.scrollbarFiller.style.display = "block"; + d.scrollbarFiller.style.height = sizes.bottom + "px"; + d.scrollbarFiller.style.width = sizes.right + "px"; + } else { d.scrollbarFiller.style.display = ""; } + if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) { + d.gutterFiller.style.display = "block"; + d.gutterFiller.style.height = sizes.bottom + "px"; + d.gutterFiller.style.width = measure.gutterWidth + "px"; + } else { d.gutterFiller.style.display = ""; } + } + + var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}; + + function initScrollbars(cm) { + if (cm.display.scrollbars) { + cm.display.scrollbars.clear(); + if (cm.display.scrollbars.addClass) + { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); } + } + + cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) { + cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller); + // Prevent clicks in the scrollbars from killing focus + on(node, "mousedown", function () { + if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); } + }); + node.setAttribute("cm-not-content", "true"); + }, function (pos, axis) { + if (axis == "horizontal") { setScrollLeft(cm, pos); } + else { updateScrollTop(cm, pos); } + }, cm); + if (cm.display.scrollbars.addClass) + { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); } + } + + // Operations are used to wrap a series of changes to the editor + // state in such a way that each change won't have to update the + // cursor and display (which would be awkward, slow, and + // error-prone). Instead, display updates are batched and then all + // combined and executed at once. + + var nextOpId = 0; + // Start a new operation. + function startOperation(cm) { + cm.curOp = { + cm: cm, + viewChanged: false, // Flag that indicates that lines might need to be redrawn + startHeight: cm.doc.height, // Used to detect need to update scrollbar + forceUpdate: false, // Used to force a redraw + updateInput: 0, // Whether to reset the input textarea + typing: false, // Whether this reset should be careful to leave existing text (for compositing) + changeObjs: null, // Accumulated changes, for firing change events + cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on + cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already + selectionChanged: false, // Whether the selection needs to be redrawn + updateMaxLine: false, // Set when the widest line needs to be determined anew + scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet + scrollToPos: null, // Used to scroll to a specific position + focus: false, + id: ++nextOpId // Unique ID + }; + pushOperation(cm.curOp); + } + + // Finish an operation, updating the display and signalling delayed events + function endOperation(cm) { + var op = cm.curOp; + if (op) { finishOperation(op, function (group) { + for (var i = 0; i < group.ops.length; i++) + { group.ops[i].cm.curOp = null; } + endOperations(group); + }); } + } + + // The DOM updates done when an operation finishes are batched so + // that the minimum number of relayouts are required. + function endOperations(group) { + var ops = group.ops; + for (var i = 0; i < ops.length; i++) // Read DOM + { endOperation_R1(ops[i]); } + for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe) + { endOperation_W1(ops[i$1]); } + for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM + { endOperation_R2(ops[i$2]); } + for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe) + { endOperation_W2(ops[i$3]); } + for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM + { endOperation_finish(ops[i$4]); } + } + + function endOperation_R1(op) { + var cm = op.cm, display = cm.display; + maybeClipScrollbars(cm); + if (op.updateMaxLine) { findMaxLine(cm); } + + op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null || + op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom || + op.scrollToPos.to.line >= display.viewTo) || + display.maxLineChanged && cm.options.lineWrapping; + op.update = op.mustUpdate && + new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate); + } + + function endOperation_W1(op) { + op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update); + } + + function endOperation_R2(op) { + var cm = op.cm, display = cm.display; + if (op.updatedDisplay) { updateHeightsInViewport(cm); } + + op.barMeasure = measureForScrollbars(cm); + + // If the max line changed since it was last measured, measure it, + // and ensure the document's width matches it. + // updateDisplay_W2 will use these properties to do the actual resizing + if (display.maxLineChanged && !cm.options.lineWrapping) { + op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3; + cm.display.sizerWidth = op.adjustWidthTo; + op.barMeasure.scrollWidth = + Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth); + op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm)); + } + + if (op.updatedDisplay || op.selectionChanged) + { op.preparedSelection = display.input.prepareSelection(); } + } + + function endOperation_W2(op) { + var cm = op.cm; + + if (op.adjustWidthTo != null) { + cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"; + if (op.maxScrollLeft < cm.doc.scrollLeft) + { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); } + cm.display.maxLineChanged = false; + } + + var takeFocus = op.focus && op.focus == activeElt(); + if (op.preparedSelection) + { cm.display.input.showSelection(op.preparedSelection, takeFocus); } + if (op.updatedDisplay || op.startHeight != cm.doc.height) + { updateScrollbars(cm, op.barMeasure); } + if (op.updatedDisplay) + { setDocumentHeight(cm, op.barMeasure); } + + if (op.selectionChanged) { restartBlink(cm); } + + if (cm.state.focused && op.updateInput) + { cm.display.input.reset(op.typing); } + if (takeFocus) { ensureFocus(op.cm); } + } + + function endOperation_finish(op) { + var cm = op.cm, display = cm.display, doc = cm.doc; + + if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); } + + // Abort mouse wheel delta measurement, when scrolling explicitly + if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos)) + { display.wheelStartX = display.wheelStartY = null; } + + // Propagate the scroll position to the actual DOM scroller + if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); } + + if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); } + // If we need to scroll a specific position into view, do so. + if (op.scrollToPos) { + var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from), + clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin); + maybeScrollWindow(cm, rect); + } + + // Fire events for markers that are hidden/unidden by editing or + // undoing + var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers; + if (hidden) { for (var i = 0; i < hidden.length; ++i) + { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } } + if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1) + { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } } + + if (display.wrapper.offsetHeight) + { doc.scrollTop = cm.display.scroller.scrollTop; } + + // Fire change events, and delayed event handlers + if (op.changeObjs) + { signal(cm, "changes", cm, op.changeObjs); } + if (op.update) + { op.update.finish(); } + } + + // Run the given function in an operation + function runInOp(cm, f) { + if (cm.curOp) { return f() } + startOperation(cm); + try { return f() } + finally { endOperation(cm); } + } + // Wraps a function in an operation. Returns the wrapped function. + function operation(cm, f) { + return function() { + if (cm.curOp) { return f.apply(cm, arguments) } + startOperation(cm); + try { return f.apply(cm, arguments) } + finally { endOperation(cm); } + } + } + // Used to add methods to editor and doc instances, wrapping them in + // operations. + function methodOp(f) { + return function() { + if (this.curOp) { return f.apply(this, arguments) } + startOperation(this); + try { return f.apply(this, arguments) } + finally { endOperation(this); } + } + } + function docMethodOp(f) { + return function() { + var cm = this.cm; + if (!cm || cm.curOp) { return f.apply(this, arguments) } + startOperation(cm); + try { return f.apply(this, arguments) } + finally { endOperation(cm); } + } + } + + // HIGHLIGHT WORKER + + function startWorker(cm, time) { + if (cm.doc.highlightFrontier < cm.display.viewTo) + { cm.state.highlight.set(time, bind(highlightWorker, cm)); } + } + + function highlightWorker(cm) { + var doc = cm.doc; + if (doc.highlightFrontier >= cm.display.viewTo) { return } + var end = +new Date + cm.options.workTime; + var context = getContextBefore(cm, doc.highlightFrontier); + var changedLines = []; + + doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) { + if (context.line >= cm.display.viewFrom) { // Visible + var oldStyles = line.styles; + var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null; + var highlighted = highlightLine(cm, line, context, true); + if (resetState) { context.state = resetState; } + line.styles = highlighted.styles; + var oldCls = line.styleClasses, newCls = highlighted.classes; + if (newCls) { line.styleClasses = newCls; } + else if (oldCls) { line.styleClasses = null; } + var ischange = !oldStyles || oldStyles.length != line.styles.length || + oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass); + for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; } + if (ischange) { changedLines.push(context.line); } + line.stateAfter = context.save(); + context.nextLine(); + } else { + if (line.text.length <= cm.options.maxHighlightLength) + { processLine(cm, line.text, context); } + line.stateAfter = context.line % 5 == 0 ? context.save() : null; + context.nextLine(); + } + if (+new Date > end) { + startWorker(cm, cm.options.workDelay); + return true + } + }); + doc.highlightFrontier = context.line; + doc.modeFrontier = Math.max(doc.modeFrontier, context.line); + if (changedLines.length) { runInOp(cm, function () { + for (var i = 0; i < changedLines.length; i++) + { regLineChange(cm, changedLines[i], "text"); } + }); } + } + + // DISPLAY DRAWING + + var DisplayUpdate = function(cm, viewport, force) { + var display = cm.display; + + this.viewport = viewport; + // Store some values that we'll need later (but don't want to force a relayout for) + this.visible = visibleLines(display, cm.doc, viewport); + this.editorIsHidden = !display.wrapper.offsetWidth; + this.wrapperHeight = display.wrapper.clientHeight; + this.wrapperWidth = display.wrapper.clientWidth; + this.oldDisplayWidth = displayWidth(cm); + this.force = force; + this.dims = getDimensions(cm); + this.events = []; + }; + + DisplayUpdate.prototype.signal = function (emitter, type) { + if (hasHandler(emitter, type)) + { this.events.push(arguments); } + }; + DisplayUpdate.prototype.finish = function () { + for (var i = 0; i < this.events.length; i++) + { signal.apply(null, this.events[i]); } + }; + + function maybeClipScrollbars(cm) { + var display = cm.display; + if (!display.scrollbarsClipped && display.scroller.offsetWidth) { + display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth; + display.heightForcer.style.height = scrollGap(cm) + "px"; + display.sizer.style.marginBottom = -display.nativeBarWidth + "px"; + display.sizer.style.borderRightWidth = scrollGap(cm) + "px"; + display.scrollbarsClipped = true; + } + } + + function selectionSnapshot(cm) { + if (cm.hasFocus()) { return null } + var active = activeElt(); + if (!active || !contains(cm.display.lineDiv, active)) { return null } + var result = {activeElt: active}; + if (window.getSelection) { + var sel = window.getSelection(); + if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) { + result.anchorNode = sel.anchorNode; + result.anchorOffset = sel.anchorOffset; + result.focusNode = sel.focusNode; + result.focusOffset = sel.focusOffset; + } + } + return result + } + + function restoreSelection(snapshot) { + if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return } + snapshot.activeElt.focus(); + if (!/^(INPUT|TEXTAREA)$/.test(snapshot.activeElt.nodeName) && + snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) { + var sel = window.getSelection(), range = document.createRange(); + range.setEnd(snapshot.anchorNode, snapshot.anchorOffset); + range.collapse(false); + sel.removeAllRanges(); + sel.addRange(range); + sel.extend(snapshot.focusNode, snapshot.focusOffset); + } + } + + // Does the actual updating of the line display. Bails out + // (returning false) when there is nothing to be done and forced is + // false. + function updateDisplayIfNeeded(cm, update) { + var display = cm.display, doc = cm.doc; + + if (update.editorIsHidden) { + resetView(cm); + return false + } + + // Bail out if the visible area is already rendered and nothing changed. + if (!update.force && + update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo && + (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) && + display.renderedView == display.view && countDirtyView(cm) == 0) + { return false } + + if (maybeUpdateLineNumberWidth(cm)) { + resetView(cm); + update.dims = getDimensions(cm); + } + + // Compute a suitable new viewport (from & to) + var end = doc.first + doc.size; + var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first); + var to = Math.min(end, update.visible.to + cm.options.viewportMargin); + if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); } + if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); } + if (sawCollapsedSpans) { + from = visualLineNo(cm.doc, from); + to = visualLineEndNo(cm.doc, to); + } + + var different = from != display.viewFrom || to != display.viewTo || + display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth; + adjustView(cm, from, to); + + display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom)); + // Position the mover div to align with the current scroll position + cm.display.mover.style.top = display.viewOffset + "px"; + + var toUpdate = countDirtyView(cm); + if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view && + (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo)) + { return false } + + // For big changes, we hide the enclosing element during the + // update, since that speeds up the operations on most browsers. + var selSnapshot = selectionSnapshot(cm); + if (toUpdate > 4) { display.lineDiv.style.display = "none"; } + patchDisplay(cm, display.updateLineNumbers, update.dims); + if (toUpdate > 4) { display.lineDiv.style.display = ""; } + display.renderedView = display.view; + // There might have been a widget with a focused element that got + // hidden or updated, if so re-focus it. + restoreSelection(selSnapshot); + + // Prevent selection and cursors from interfering with the scroll + // width and height. + removeChildren(display.cursorDiv); + removeChildren(display.selectionDiv); + display.gutters.style.height = display.sizer.style.minHeight = 0; + + if (different) { + display.lastWrapHeight = update.wrapperHeight; + display.lastWrapWidth = update.wrapperWidth; + startWorker(cm, 400); + } + + display.updateLineNumbers = null; + + return true + } + + function postUpdateDisplay(cm, update) { + var viewport = update.viewport; + + for (var first = true;; first = false) { + if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) { + // Clip forced viewport to actual scrollable area. + if (viewport && viewport.top != null) + { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; } + // Updated line heights might result in the drawn area not + // actually covering the viewport. Keep looping until it does. + update.visible = visibleLines(cm.display, cm.doc, viewport); + if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo) + { break } + } else if (first) { + update.visible = visibleLines(cm.display, cm.doc, viewport); + } + if (!updateDisplayIfNeeded(cm, update)) { break } + updateHeightsInViewport(cm); + var barMeasure = measureForScrollbars(cm); + updateSelection(cm); + updateScrollbars(cm, barMeasure); + setDocumentHeight(cm, barMeasure); + update.force = false; + } + + update.signal(cm, "update", cm); + if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) { + update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo); + cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo; + } + } + + function updateDisplaySimple(cm, viewport) { + var update = new DisplayUpdate(cm, viewport); + if (updateDisplayIfNeeded(cm, update)) { + updateHeightsInViewport(cm); + postUpdateDisplay(cm, update); + var barMeasure = measureForScrollbars(cm); + updateSelection(cm); + updateScrollbars(cm, barMeasure); + setDocumentHeight(cm, barMeasure); + update.finish(); + } + } + + // Sync the actual display DOM structure with display.view, removing + // nodes for lines that are no longer in view, and creating the ones + // that are not there yet, and updating the ones that are out of + // date. + function patchDisplay(cm, updateNumbersFrom, dims) { + var display = cm.display, lineNumbers = cm.options.lineNumbers; + var container = display.lineDiv, cur = container.firstChild; + + function rm(node) { + var next = node.nextSibling; + // Works around a throw-scroll bug in OS X Webkit + if (webkit && mac && cm.display.currentWheelTarget == node) + { node.style.display = "none"; } + else + { node.parentNode.removeChild(node); } + return next + } + + var view = display.view, lineN = display.viewFrom; + // Loop over the elements in the view, syncing cur (the DOM nodes + // in display.lineDiv) with the view as we go. + for (var i = 0; i < view.length; i++) { + var lineView = view[i]; + if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet + var node = buildLineElement(cm, lineView, lineN, dims); + container.insertBefore(node, cur); + } else { // Already drawn + while (cur != lineView.node) { cur = rm(cur); } + var updateNumber = lineNumbers && updateNumbersFrom != null && + updateNumbersFrom <= lineN && lineView.lineNumber; + if (lineView.changes) { + if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; } + updateLineForChanges(cm, lineView, lineN, dims); + } + if (updateNumber) { + removeChildren(lineView.lineNumber); + lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN))); + } + cur = lineView.node.nextSibling; + } + lineN += lineView.size; + } + while (cur) { cur = rm(cur); } + } + + function updateGutterSpace(display) { + var width = display.gutters.offsetWidth; + display.sizer.style.marginLeft = width + "px"; + } + + function setDocumentHeight(cm, measure) { + cm.display.sizer.style.minHeight = measure.docHeight + "px"; + cm.display.heightForcer.style.top = measure.docHeight + "px"; + cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px"; + } + + // Re-align line numbers and gutter marks to compensate for + // horizontal scrolling. + function alignHorizontally(cm) { + var display = cm.display, view = display.view; + if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return } + var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft; + var gutterW = display.gutters.offsetWidth, left = comp + "px"; + for (var i = 0; i < view.length; i++) { if (!view[i].hidden) { + if (cm.options.fixedGutter) { + if (view[i].gutter) + { view[i].gutter.style.left = left; } + if (view[i].gutterBackground) + { view[i].gutterBackground.style.left = left; } + } + var align = view[i].alignable; + if (align) { for (var j = 0; j < align.length; j++) + { align[j].style.left = left; } } + } } + if (cm.options.fixedGutter) + { display.gutters.style.left = (comp + gutterW) + "px"; } + } + + // Used to ensure that the line number gutter is still the right + // size for the current document size. Returns true when an update + // is needed. + function maybeUpdateLineNumberWidth(cm) { + if (!cm.options.lineNumbers) { return false } + var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display; + if (last.length != display.lineNumChars) { + var test = display.measure.appendChild(elt("div", [elt("div", last)], + "CodeMirror-linenumber CodeMirror-gutter-elt")); + var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW; + display.lineGutter.style.width = ""; + display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1; + display.lineNumWidth = display.lineNumInnerWidth + padding; + display.lineNumChars = display.lineNumInnerWidth ? last.length : -1; + display.lineGutter.style.width = display.lineNumWidth + "px"; + updateGutterSpace(cm.display); + return true + } + return false + } + + function getGutters(gutters, lineNumbers) { + var result = [], sawLineNumbers = false; + for (var i = 0; i < gutters.length; i++) { + var name = gutters[i], style = null; + if (typeof name != "string") { style = name.style; name = name.className; } + if (name == "CodeMirror-linenumbers") { + if (!lineNumbers) { continue } + else { sawLineNumbers = true; } + } + result.push({className: name, style: style}); + } + if (lineNumbers && !sawLineNumbers) { result.push({className: "CodeMirror-linenumbers", style: null}); } + return result + } + + // Rebuild the gutter elements, ensure the margin to the left of the + // code matches their width. + function renderGutters(display) { + var gutters = display.gutters, specs = display.gutterSpecs; + removeChildren(gutters); + display.lineGutter = null; + for (var i = 0; i < specs.length; ++i) { + var ref = specs[i]; + var className = ref.className; + var style = ref.style; + var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + className)); + if (style) { gElt.style.cssText = style; } + if (className == "CodeMirror-linenumbers") { + display.lineGutter = gElt; + gElt.style.width = (display.lineNumWidth || 1) + "px"; + } + } + gutters.style.display = specs.length ? "" : "none"; + updateGutterSpace(display); + } + + function updateGutters(cm) { + renderGutters(cm.display); + regChange(cm); + alignHorizontally(cm); + } + + // The display handles the DOM integration, both for input reading + // and content drawing. It holds references to DOM nodes and + // display-related state. + + function Display(place, doc, input, options) { + var d = this; + this.input = input; + + // Covers bottom-right square when both scrollbars are present. + d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); + d.scrollbarFiller.setAttribute("cm-not-content", "true"); + // Covers bottom of gutter when coverGutterNextToScrollbar is on + // and h scrollbar is present. + d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler"); + d.gutterFiller.setAttribute("cm-not-content", "true"); + // Will contain the actual code, positioned to cover the viewport. + d.lineDiv = eltP("div", null, "CodeMirror-code"); + // Elements are added to these to represent selection and cursors. + d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); + d.cursorDiv = elt("div", null, "CodeMirror-cursors"); + // A visibility: hidden element used to find the size of things. + d.measure = elt("div", null, "CodeMirror-measure"); + // When lines outside of the viewport are measured, they are drawn in this. + d.lineMeasure = elt("div", null, "CodeMirror-measure"); + // Wraps everything that needs to exist inside the vertically-padded coordinate system + d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv], + null, "position: relative; outline: none"); + var lines = eltP("div", [d.lineSpace], "CodeMirror-lines"); + // Moved around its parent to cover visible view. + d.mover = elt("div", [lines], null, "position: relative"); + // Set to the height of the document, allowing scrolling. + d.sizer = elt("div", [d.mover], "CodeMirror-sizer"); + d.sizerWidth = null; + // Behavior of elts with overflow: auto and padding is + // inconsistent across browsers. This is used to ensure the + // scrollable area is big enough. + d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;"); + // Will contain the gutters, if any. + d.gutters = elt("div", null, "CodeMirror-gutters"); + d.lineGutter = null; + // Actual scrollable element. + d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll"); + d.scroller.setAttribute("tabIndex", "-1"); + // The element in which the editor lives. + d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror"); + + // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported) + if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; } + if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; } + + if (place) { + if (place.appendChild) { place.appendChild(d.wrapper); } + else { place(d.wrapper); } + } + + // Current rendered range (may be bigger than the view window). + d.viewFrom = d.viewTo = doc.first; + d.reportedViewFrom = d.reportedViewTo = doc.first; + // Information about the rendered lines. + d.view = []; + d.renderedView = null; + // Holds info about a single rendered line when it was rendered + // for measurement, while not in view. + d.externalMeasured = null; + // Empty space (in pixels) above the view + d.viewOffset = 0; + d.lastWrapHeight = d.lastWrapWidth = 0; + d.updateLineNumbers = null; + + d.nativeBarWidth = d.barHeight = d.barWidth = 0; + d.scrollbarsClipped = false; + + // Used to only resize the line number gutter when necessary (when + // the amount of lines crosses a boundary that makes its width change) + d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null; + // Set to true when a non-horizontal-scrolling line widget is + // added. As an optimization, line widget aligning is skipped when + // this is false. + d.alignWidgets = false; + + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; + + // Tracks the maximum line length so that the horizontal scrollbar + // can be kept static when scrolling. + d.maxLine = null; + d.maxLineLength = 0; + d.maxLineChanged = false; + + // Used for measuring wheel scrolling granularity + d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null; + + // True when shift is held down. + d.shift = false; + + // Used to track whether anything happened since the context menu + // was opened. + d.selForContextMenu = null; + + d.activeTouch = null; + + d.gutterSpecs = getGutters(options.gutters, options.lineNumbers); + renderGutters(d); + + input.init(d); + } + + // Since the delta values reported on mouse wheel events are + // unstandardized between browsers and even browser versions, and + // generally horribly unpredictable, this code starts by measuring + // the scroll effect that the first few mouse wheel events have, + // and, from that, detects the way it can convert deltas to pixel + // offsets afterwards. + // + // The reason we want to know the amount a wheel event will scroll + // is that it gives us a chance to update the display before the + // actual scrolling happens, reducing flickering. + + var wheelSamples = 0, wheelPixelsPerUnit = null; + // Fill in a browser-detected starting value on browsers where we + // know one. These don't have to be accurate -- the result of them + // being wrong would just be a slight flicker on the first wheel + // scroll (if it is large enough). + if (ie) { wheelPixelsPerUnit = -.53; } + else if (gecko) { wheelPixelsPerUnit = 15; } + else if (chrome) { wheelPixelsPerUnit = -.7; } + else if (safari) { wheelPixelsPerUnit = -1/3; } + + function wheelEventDelta(e) { + var dx = e.wheelDeltaX, dy = e.wheelDeltaY; + if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; } + if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; } + else if (dy == null) { dy = e.wheelDelta; } + return {x: dx, y: dy} + } + function wheelEventPixels(e) { + var delta = wheelEventDelta(e); + delta.x *= wheelPixelsPerUnit; + delta.y *= wheelPixelsPerUnit; + return delta + } + + function onScrollWheel(cm, e) { + var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y; + + var display = cm.display, scroll = display.scroller; + // Quit if there's nothing to scroll here + var canScrollX = scroll.scrollWidth > scroll.clientWidth; + var canScrollY = scroll.scrollHeight > scroll.clientHeight; + if (!(dx && canScrollX || dy && canScrollY)) { return } + + // Webkit browsers on OS X abort momentum scrolls when the target + // of the scroll event is removed from the scrollable element. + // This hack (see related code in patchDisplay) makes sure the + // element is kept around. + if (dy && mac && webkit) { + outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) { + for (var i = 0; i < view.length; i++) { + if (view[i].node == cur) { + cm.display.currentWheelTarget = cur; + break outer + } + } + } + } + + // On some browsers, horizontal scrolling will cause redraws to + // happen before the gutter has been realigned, causing it to + // wriggle around in a most unseemly way. When we have an + // estimated pixels/delta value, we just handle horizontal + // scrolling entirely here. It'll be slightly off from native, but + // better than glitching out. + if (dx && !gecko && !presto && wheelPixelsPerUnit != null) { + if (dy && canScrollY) + { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); } + setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit)); + // Only prevent default scrolling if vertical scrolling is + // actually possible. Otherwise, it causes vertical scroll + // jitter on OSX trackpads when deltaX is small and deltaY + // is large (issue #3579) + if (!dy || (dy && canScrollY)) + { e_preventDefault(e); } + display.wheelStartX = null; // Abort measurement, if in progress + return + } + + // 'Project' the visible viewport to cover the area that is being + // scrolled into view (if we know enough to estimate it). + if (dy && wheelPixelsPerUnit != null) { + var pixels = dy * wheelPixelsPerUnit; + var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight; + if (pixels < 0) { top = Math.max(0, top + pixels - 50); } + else { bot = Math.min(cm.doc.height, bot + pixels + 50); } + updateDisplaySimple(cm, {top: top, bottom: bot}); + } + + if (wheelSamples < 20) { + if (display.wheelStartX == null) { + display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop; + display.wheelDX = dx; display.wheelDY = dy; + setTimeout(function () { + if (display.wheelStartX == null) { return } + var movedX = scroll.scrollLeft - display.wheelStartX; + var movedY = scroll.scrollTop - display.wheelStartY; + var sample = (movedY && display.wheelDY && movedY / display.wheelDY) || + (movedX && display.wheelDX && movedX / display.wheelDX); + display.wheelStartX = display.wheelStartY = null; + if (!sample) { return } + wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1); + ++wheelSamples; + }, 200); + } else { + display.wheelDX += dx; display.wheelDY += dy; + } + } + } + + // Selection objects are immutable. A new one is created every time + // the selection changes. A selection is one or more non-overlapping + // (and non-touching) ranges, sorted, and an integer that indicates + // which one is the primary selection (the one that's scrolled into + // view, that getCursor returns, etc). + var Selection = function(ranges, primIndex) { + this.ranges = ranges; + this.primIndex = primIndex; + }; + + Selection.prototype.primary = function () { return this.ranges[this.primIndex] }; + + Selection.prototype.equals = function (other) { + if (other == this) { return true } + if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false } + for (var i = 0; i < this.ranges.length; i++) { + var here = this.ranges[i], there = other.ranges[i]; + if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false } + } + return true + }; + + Selection.prototype.deepCopy = function () { + var out = []; + for (var i = 0; i < this.ranges.length; i++) + { out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)); } + return new Selection(out, this.primIndex) + }; + + Selection.prototype.somethingSelected = function () { + for (var i = 0; i < this.ranges.length; i++) + { if (!this.ranges[i].empty()) { return true } } + return false + }; + + Selection.prototype.contains = function (pos, end) { + if (!end) { end = pos; } + for (var i = 0; i < this.ranges.length; i++) { + var range = this.ranges[i]; + if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) + { return i } + } + return -1 + }; + + var Range = function(anchor, head) { + this.anchor = anchor; this.head = head; + }; + + Range.prototype.from = function () { return minPos(this.anchor, this.head) }; + Range.prototype.to = function () { return maxPos(this.anchor, this.head) }; + Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch }; + + // Take an unsorted, potentially overlapping set of ranges, and + // build a selection out of it. 'Consumes' ranges array (modifying + // it). + function normalizeSelection(cm, ranges, primIndex) { + var mayTouch = cm && cm.options.selectionsMayTouch; + var prim = ranges[primIndex]; + ranges.sort(function (a, b) { return cmp(a.from(), b.from()); }); + primIndex = indexOf(ranges, prim); + for (var i = 1; i < ranges.length; i++) { + var cur = ranges[i], prev = ranges[i - 1]; + var diff = cmp(prev.to(), cur.from()); + if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) { + var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to()); + var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head; + if (i <= primIndex) { --primIndex; } + ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to)); + } + } + return new Selection(ranges, primIndex) + } + + function simpleSelection(anchor, head) { + return new Selection([new Range(anchor, head || anchor)], 0) + } + + // Compute the position of the end of a change (its 'to' property + // refers to the pre-change end). + function changeEnd(change) { + if (!change.text) { return change.to } + return Pos(change.from.line + change.text.length - 1, + lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)) + } + + // Adjust a position to refer to the post-change position of the + // same text, or the end of the change if the change covers it. + function adjustForChange(pos, change) { + if (cmp(pos, change.from) < 0) { return pos } + if (cmp(pos, change.to) <= 0) { return changeEnd(change) } + + var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch; + if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; } + return Pos(line, ch) + } + + function computeSelAfterChange(doc, change) { + var out = []; + for (var i = 0; i < doc.sel.ranges.length; i++) { + var range = doc.sel.ranges[i]; + out.push(new Range(adjustForChange(range.anchor, change), + adjustForChange(range.head, change))); + } + return normalizeSelection(doc.cm, out, doc.sel.primIndex) + } + + function offsetPos(pos, old, nw) { + if (pos.line == old.line) + { return Pos(nw.line, pos.ch - old.ch + nw.ch) } + else + { return Pos(nw.line + (pos.line - old.line), pos.ch) } + } + + // Used by replaceSelections to allow moving the selection to the + // start or around the replaced test. Hint may be "start" or "around". + function computeReplacedSel(doc, changes, hint) { + var out = []; + var oldPrev = Pos(doc.first, 0), newPrev = oldPrev; + for (var i = 0; i < changes.length; i++) { + var change = changes[i]; + var from = offsetPos(change.from, oldPrev, newPrev); + var to = offsetPos(changeEnd(change), oldPrev, newPrev); + oldPrev = change.to; + newPrev = to; + if (hint == "around") { + var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0; + out[i] = new Range(inv ? to : from, inv ? from : to); + } else { + out[i] = new Range(from, from); + } + } + return new Selection(out, doc.sel.primIndex) + } + + // Used to get the editor into a consistent state again when options change. + + function loadMode(cm) { + cm.doc.mode = getMode(cm.options, cm.doc.modeOption); + resetModeState(cm); + } + + function resetModeState(cm) { + cm.doc.iter(function (line) { + if (line.stateAfter) { line.stateAfter = null; } + if (line.styles) { line.styles = null; } + }); + cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first; + startWorker(cm, 100); + cm.state.modeGen++; + if (cm.curOp) { regChange(cm); } + } + + // DOCUMENT DATA STRUCTURE + + // By default, updates that start and end at the beginning of a line + // are treated specially, in order to make the association of line + // widgets and marker elements with the text behave more intuitive. + function isWholeLineUpdate(doc, change) { + return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" && + (!doc.cm || doc.cm.options.wholeLineUpdateBefore) + } + + // Perform a change on the document data structure. + function updateDoc(doc, change, markedSpans, estimateHeight) { + function spansFor(n) {return markedSpans ? markedSpans[n] : null} + function update(line, text, spans) { + updateLine(line, text, spans, estimateHeight); + signalLater(line, "change", line, change); + } + function linesFor(start, end) { + var result = []; + for (var i = start; i < end; ++i) + { result.push(new Line(text[i], spansFor(i), estimateHeight)); } + return result + } + + var from = change.from, to = change.to, text = change.text; + var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line); + var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line; + + // Adjust the line structure + if (change.full) { + doc.insert(0, linesFor(0, text.length)); + doc.remove(text.length, doc.size - text.length); + } else if (isWholeLineUpdate(doc, change)) { + // This is a whole-line replace. Treated specially to make + // sure line objects move the way they are supposed to. + var added = linesFor(0, text.length - 1); + update(lastLine, lastLine.text, lastSpans); + if (nlines) { doc.remove(from.line, nlines); } + if (added.length) { doc.insert(from.line, added); } + } else if (firstLine == lastLine) { + if (text.length == 1) { + update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans); + } else { + var added$1 = linesFor(1, text.length - 1); + added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight)); + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); + doc.insert(from.line + 1, added$1); + } + } else if (text.length == 1) { + update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0)); + doc.remove(from.line + 1, nlines); + } else { + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); + update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans); + var added$2 = linesFor(1, text.length - 1); + if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); } + doc.insert(from.line + 1, added$2); + } + + signalLater(doc, "change", doc, change); + } + + // Call f for all linked documents. + function linkedDocs(doc, f, sharedHistOnly) { + function propagate(doc, skip, sharedHist) { + if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) { + var rel = doc.linked[i]; + if (rel.doc == skip) { continue } + var shared = sharedHist && rel.sharedHist; + if (sharedHistOnly && !shared) { continue } + f(rel.doc, shared); + propagate(rel.doc, doc, shared); + } } + } + propagate(doc, null, true); + } + + // Attach a document to an editor. + function attachDoc(cm, doc) { + if (doc.cm) { throw new Error("This document is already in use.") } + cm.doc = doc; + doc.cm = cm; + estimateLineHeights(cm); + loadMode(cm); + setDirectionClass(cm); + if (!cm.options.lineWrapping) { findMaxLine(cm); } + cm.options.mode = doc.modeOption; + regChange(cm); + } + + function setDirectionClass(cm) { + (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl"); + } + + function directionChanged(cm) { + runInOp(cm, function () { + setDirectionClass(cm); + regChange(cm); + }); + } + + function History(startGen) { + // Arrays of change events and selections. Doing something adds an + // event to done and clears undo. Undoing moves events from done + // to undone, redoing moves them in the other direction. + this.done = []; this.undone = []; + this.undoDepth = Infinity; + // Used to track when changes can be merged into a single undo + // event + this.lastModTime = this.lastSelTime = 0; + this.lastOp = this.lastSelOp = null; + this.lastOrigin = this.lastSelOrigin = null; + // Used by the isClean() method + this.generation = this.maxGeneration = startGen || 1; + } + + // Create a history change event from an updateDoc-style change + // object. + function historyChangeFromChange(doc, change) { + var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}; + attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); + linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true); + return histChange + } + + // Pop all selection events off the end of a history array. Stop at + // a change event. + function clearSelectionEvents(array) { + while (array.length) { + var last = lst(array); + if (last.ranges) { array.pop(); } + else { break } + } + } + + // Find the top change event in the history. Pop off selection + // events that are in the way. + function lastChangeEvent(hist, force) { + if (force) { + clearSelectionEvents(hist.done); + return lst(hist.done) + } else if (hist.done.length && !lst(hist.done).ranges) { + return lst(hist.done) + } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) { + hist.done.pop(); + return lst(hist.done) + } + } + + // Register a change in the history. Merges changes that are within + // a single operation, or are close together with an origin that + // allows merging (starting with "+") into a single event. + function addChangeToHistory(doc, change, selAfter, opId) { + var hist = doc.history; + hist.undone.length = 0; + var time = +new Date, cur; + var last; + + if ((hist.lastOp == opId || + hist.lastOrigin == change.origin && change.origin && + ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) || + change.origin.charAt(0) == "*")) && + (cur = lastChangeEvent(hist, hist.lastOp == opId))) { + // Merge this change into the last event + last = lst(cur.changes); + if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) { + // Optimized case for simple insertion -- don't want to add + // new changesets for every character typed + last.to = changeEnd(change); + } else { + // Add new sub-event + cur.changes.push(historyChangeFromChange(doc, change)); + } + } else { + // Can not be merged, start a new event. + var before = lst(hist.done); + if (!before || !before.ranges) + { pushSelectionToHistory(doc.sel, hist.done); } + cur = {changes: [historyChangeFromChange(doc, change)], + generation: hist.generation}; + hist.done.push(cur); + while (hist.done.length > hist.undoDepth) { + hist.done.shift(); + if (!hist.done[0].ranges) { hist.done.shift(); } + } + } + hist.done.push(selAfter); + hist.generation = ++hist.maxGeneration; + hist.lastModTime = hist.lastSelTime = time; + hist.lastOp = hist.lastSelOp = opId; + hist.lastOrigin = hist.lastSelOrigin = change.origin; + + if (!last) { signal(doc, "historyAdded"); } + } + + function selectionEventCanBeMerged(doc, origin, prev, sel) { + var ch = origin.charAt(0); + return ch == "*" || + ch == "+" && + prev.ranges.length == sel.ranges.length && + prev.somethingSelected() == sel.somethingSelected() && + new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500) + } + + // Called whenever the selection changes, sets the new selection as + // the pending selection in the history, and pushes the old pending + // selection into the 'done' array when it was significantly + // different (in number of selected ranges, emptiness, or time). + function addSelectionToHistory(doc, sel, opId, options) { + var hist = doc.history, origin = options && options.origin; + + // A new event is started when the previous origin does not match + // the current, or the origins don't allow matching. Origins + // starting with * are always merged, those starting with + are + // merged when similar and close together in time. + if (opId == hist.lastSelOp || + (origin && hist.lastSelOrigin == origin && + (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin || + selectionEventCanBeMerged(doc, origin, lst(hist.done), sel)))) + { hist.done[hist.done.length - 1] = sel; } + else + { pushSelectionToHistory(sel, hist.done); } + + hist.lastSelTime = +new Date; + hist.lastSelOrigin = origin; + hist.lastSelOp = opId; + if (options && options.clearRedo !== false) + { clearSelectionEvents(hist.undone); } + } + + function pushSelectionToHistory(sel, dest) { + var top = lst(dest); + if (!(top && top.ranges && top.equals(sel))) + { dest.push(sel); } + } + + // Used to store marked span information in the history. + function attachLocalSpans(doc, change, from, to) { + var existing = change["spans_" + doc.id], n = 0; + doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) { + if (line.markedSpans) + { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; } + ++n; + }); + } + + // When un/re-doing restores text containing marked spans, those + // that have been explicitly cleared should not be restored. + function removeClearedSpans(spans) { + if (!spans) { return null } + var out; + for (var i = 0; i < spans.length; ++i) { + if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } } + else if (out) { out.push(spans[i]); } + } + return !out ? spans : out.length ? out : null + } + + // Retrieve and filter the old marked spans stored in a change event. + function getOldSpans(doc, change) { + var found = change["spans_" + doc.id]; + if (!found) { return null } + var nw = []; + for (var i = 0; i < change.text.length; ++i) + { nw.push(removeClearedSpans(found[i])); } + return nw + } + + // Used for un/re-doing changes from the history. Combines the + // result of computing the existing spans with the set of spans that + // existed in the history (so that deleting around a span and then + // undoing brings back the span). + function mergeOldSpans(doc, change) { + var old = getOldSpans(doc, change); + var stretched = stretchSpansOverChange(doc, change); + if (!old) { return stretched } + if (!stretched) { return old } + + for (var i = 0; i < old.length; ++i) { + var oldCur = old[i], stretchCur = stretched[i]; + if (oldCur && stretchCur) { + spans: for (var j = 0; j < stretchCur.length; ++j) { + var span = stretchCur[j]; + for (var k = 0; k < oldCur.length; ++k) + { if (oldCur[k].marker == span.marker) { continue spans } } + oldCur.push(span); + } + } else if (stretchCur) { + old[i] = stretchCur; + } + } + return old + } + + // Used both to provide a JSON-safe object in .getHistory, and, when + // detaching a document, to split the history in two + function copyHistoryArray(events, newGroup, instantiateSel) { + var copy = []; + for (var i = 0; i < events.length; ++i) { + var event = events[i]; + if (event.ranges) { + copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event); + continue + } + var changes = event.changes, newChanges = []; + copy.push({changes: newChanges}); + for (var j = 0; j < changes.length; ++j) { + var change = changes[j], m = (void 0); + newChanges.push({from: change.from, to: change.to, text: change.text}); + if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) { + if (indexOf(newGroup, Number(m[1])) > -1) { + lst(newChanges)[prop] = change[prop]; + delete change[prop]; + } + } } } + } + } + return copy + } + + // The 'scroll' parameter given to many of these indicated whether + // the new cursor position should be scrolled into view after + // modifying the selection. + + // If shift is held or the extend flag is set, extends a range to + // include a given position (and optionally a second position). + // Otherwise, simply returns the range between the given positions. + // Used for cursor motion and such. + function extendRange(range, head, other, extend) { + if (extend) { + var anchor = range.anchor; + if (other) { + var posBefore = cmp(head, anchor) < 0; + if (posBefore != (cmp(other, anchor) < 0)) { + anchor = head; + head = other; + } else if (posBefore != (cmp(head, other) < 0)) { + head = other; + } + } + return new Range(anchor, head) + } else { + return new Range(other || head, head) + } + } + + // Extend the primary selection range, discard the rest. + function extendSelection(doc, head, other, options, extend) { + if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); } + setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options); + } + + // Extend all selections (pos is an array of selections with length + // equal the number of selections) + function extendSelections(doc, heads, options) { + var out = []; + var extend = doc.cm && (doc.cm.display.shift || doc.extend); + for (var i = 0; i < doc.sel.ranges.length; i++) + { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); } + var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex); + setSelection(doc, newSel, options); + } + + // Updates a single range in the selection. + function replaceOneSelection(doc, i, range, options) { + var ranges = doc.sel.ranges.slice(0); + ranges[i] = range; + setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options); + } + + // Reset the selection to a single range. + function setSimpleSelection(doc, anchor, head, options) { + setSelection(doc, simpleSelection(anchor, head), options); + } + + // Give beforeSelectionChange handlers a change to influence a + // selection update. + function filterSelectionChange(doc, sel, options) { + var obj = { + ranges: sel.ranges, + update: function(ranges) { + this.ranges = []; + for (var i = 0; i < ranges.length; i++) + { this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), + clipPos(doc, ranges[i].head)); } + }, + origin: options && options.origin + }; + signal(doc, "beforeSelectionChange", doc, obj); + if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); } + if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) } + else { return sel } + } + + function setSelectionReplaceHistory(doc, sel, options) { + var done = doc.history.done, last = lst(done); + if (last && last.ranges) { + done[done.length - 1] = sel; + setSelectionNoUndo(doc, sel, options); + } else { + setSelection(doc, sel, options); + } + } + + // Set a new selection. + function setSelection(doc, sel, options) { + setSelectionNoUndo(doc, sel, options); + addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options); + } + + function setSelectionNoUndo(doc, sel, options) { + if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) + { sel = filterSelectionChange(doc, sel, options); } + + var bias = options && options.bias || + (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1); + setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true)); + + if (!(options && options.scroll === false) && doc.cm) + { ensureCursorVisible(doc.cm); } + } + + function setSelectionInner(doc, sel) { + if (sel.equals(doc.sel)) { return } + + doc.sel = sel; + + if (doc.cm) { + doc.cm.curOp.updateInput = 1; + doc.cm.curOp.selectionChanged = true; + signalCursorActivity(doc.cm); + } + signalLater(doc, "cursorActivity", doc); + } + + // Verify that the selection does not partially select any atomic + // marked ranges. + function reCheckSelection(doc) { + setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false)); + } + + // Return a selection that does not partially select any atomic + // ranges. + function skipAtomicInSelection(doc, sel, bias, mayClear) { + var out; + for (var i = 0; i < sel.ranges.length; i++) { + var range = sel.ranges[i]; + var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]; + var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear); + var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear); + if (out || newAnchor != range.anchor || newHead != range.head) { + if (!out) { out = sel.ranges.slice(0, i); } + out[i] = new Range(newAnchor, newHead); + } + } + return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel + } + + function skipAtomicInner(doc, pos, oldPos, dir, mayClear) { + var line = getLine(doc, pos.line); + if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { + var sp = line.markedSpans[i], m = sp.marker; + + // Determine if we should prevent the cursor being placed to the left/right of an atomic marker + // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it + // is with selectLeft/Right + var preventCursorLeft = ("selectLeft" in m) ? !m.selectLeft : m.inclusiveLeft; + var preventCursorRight = ("selectRight" in m) ? !m.selectRight : m.inclusiveRight; + + if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) && + (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) { + if (mayClear) { + signal(m, "beforeCursorEnter"); + if (m.explicitlyCleared) { + if (!line.markedSpans) { break } + else {--i; continue} + } + } + if (!m.atomic) { continue } + + if (oldPos) { + var near = m.find(dir < 0 ? 1 : -1), diff = (void 0); + if (dir < 0 ? preventCursorRight : preventCursorLeft) + { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); } + if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0)) + { return skipAtomicInner(doc, near, pos, dir, mayClear) } + } + + var far = m.find(dir < 0 ? -1 : 1); + if (dir < 0 ? preventCursorLeft : preventCursorRight) + { far = movePos(doc, far, dir, far.line == pos.line ? line : null); } + return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null + } + } } + return pos + } + + // Ensure a given position is not inside an atomic range. + function skipAtomic(doc, pos, oldPos, bias, mayClear) { + var dir = bias || 1; + var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) || + (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) || + skipAtomicInner(doc, pos, oldPos, -dir, mayClear) || + (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true)); + if (!found) { + doc.cantEdit = true; + return Pos(doc.first, 0) + } + return found + } + + function movePos(doc, pos, dir, line) { + if (dir < 0 && pos.ch == 0) { + if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) } + else { return null } + } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) { + if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) } + else { return null } + } else { + return new Pos(pos.line, pos.ch + dir) + } + } + + function selectAll(cm) { + cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll); + } + + // UPDATING + + // Allow "beforeChange" event handlers to influence a change + function filterChange(doc, change, update) { + var obj = { + canceled: false, + from: change.from, + to: change.to, + text: change.text, + origin: change.origin, + cancel: function () { return obj.canceled = true; } + }; + if (update) { obj.update = function (from, to, text, origin) { + if (from) { obj.from = clipPos(doc, from); } + if (to) { obj.to = clipPos(doc, to); } + if (text) { obj.text = text; } + if (origin !== undefined) { obj.origin = origin; } + }; } + signal(doc, "beforeChange", doc, obj); + if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); } + + if (obj.canceled) { + if (doc.cm) { doc.cm.curOp.updateInput = 2; } + return null + } + return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin} + } + + // Apply a change to a document, and add it to the document's + // history, and propagating it to all linked documents. + function makeChange(doc, change, ignoreReadOnly) { + if (doc.cm) { + if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) } + if (doc.cm.state.suppressEdits) { return } + } + + if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) { + change = filterChange(doc, change, true); + if (!change) { return } + } + + // Possibly split or suppress the update based on the presence + // of read-only spans in its range. + var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to); + if (split) { + for (var i = split.length - 1; i >= 0; --i) + { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); } + } else { + makeChangeInner(doc, change); + } + } + + function makeChangeInner(doc, change) { + if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return } + var selAfter = computeSelAfterChange(doc, change); + addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN); + + makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)); + var rebased = []; + + linkedDocs(doc, function (doc, sharedHist) { + if (!sharedHist && indexOf(rebased, doc.history) == -1) { + rebaseHist(doc.history, change); + rebased.push(doc.history); + } + makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change)); + }); + } + + // Revert a change stored in a document's history. + function makeChangeFromHistory(doc, type, allowSelectionOnly) { + var suppress = doc.cm && doc.cm.state.suppressEdits; + if (suppress && !allowSelectionOnly) { return } + + var hist = doc.history, event, selAfter = doc.sel; + var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done; + + // Verify that there is a useable event (so that ctrl-z won't + // needlessly clear selection events) + var i = 0; + for (; i < source.length; i++) { + event = source[i]; + if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges) + { break } + } + if (i == source.length) { return } + hist.lastOrigin = hist.lastSelOrigin = null; + + for (;;) { + event = source.pop(); + if (event.ranges) { + pushSelectionToHistory(event, dest); + if (allowSelectionOnly && !event.equals(doc.sel)) { + setSelection(doc, event, {clearRedo: false}); + return + } + selAfter = event; + } else if (suppress) { + source.push(event); + return + } else { break } + } + + // Build up a reverse change object to add to the opposite history + // stack (redo when undoing, and vice versa). + var antiChanges = []; + pushSelectionToHistory(selAfter, dest); + dest.push({changes: antiChanges, generation: hist.generation}); + hist.generation = event.generation || ++hist.maxGeneration; + + var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange"); + + var loop = function ( i ) { + var change = event.changes[i]; + change.origin = type; + if (filter && !filterChange(doc, change, false)) { + source.length = 0; + return {} + } + + antiChanges.push(historyChangeFromChange(doc, change)); + + var after = i ? computeSelAfterChange(doc, change) : lst(source); + makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); + if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); } + var rebased = []; + + // Propagate to the linked documents + linkedDocs(doc, function (doc, sharedHist) { + if (!sharedHist && indexOf(rebased, doc.history) == -1) { + rebaseHist(doc.history, change); + rebased.push(doc.history); + } + makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change)); + }); + }; + + for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) { + var returned = loop( i$1 ); + + if ( returned ) return returned.v; + } + } + + // Sub-views need their line numbers shifted when text is added + // above or below them in the parent document. + function shiftDoc(doc, distance) { + if (distance == 0) { return } + doc.first += distance; + doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range( + Pos(range.anchor.line + distance, range.anchor.ch), + Pos(range.head.line + distance, range.head.ch) + ); }), doc.sel.primIndex); + if (doc.cm) { + regChange(doc.cm, doc.first, doc.first - distance, distance); + for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++) + { regLineChange(doc.cm, l, "gutter"); } + } + } + + // More lower-level change function, handling only a single document + // (not linked ones). + function makeChangeSingleDoc(doc, change, selAfter, spans) { + if (doc.cm && !doc.cm.curOp) + { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) } + + if (change.to.line < doc.first) { + shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line)); + return + } + if (change.from.line > doc.lastLine()) { return } + + // Clip the change to the size of this doc + if (change.from.line < doc.first) { + var shift = change.text.length - 1 - (doc.first - change.from.line); + shiftDoc(doc, shift); + change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch), + text: [lst(change.text)], origin: change.origin}; + } + var last = doc.lastLine(); + if (change.to.line > last) { + change = {from: change.from, to: Pos(last, getLine(doc, last).text.length), + text: [change.text[0]], origin: change.origin}; + } + + change.removed = getBetween(doc, change.from, change.to); + + if (!selAfter) { selAfter = computeSelAfterChange(doc, change); } + if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); } + else { updateDoc(doc, change, spans); } + setSelectionNoUndo(doc, selAfter, sel_dontScroll); + + if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0))) + { doc.cantEdit = false; } + } + + // Handle the interaction of a change to a document with the editor + // that this document is part of. + function makeChangeSingleDocInEditor(cm, change, spans) { + var doc = cm.doc, display = cm.display, from = change.from, to = change.to; + + var recomputeMaxLength = false, checkWidthStart = from.line; + if (!cm.options.lineWrapping) { + checkWidthStart = lineNo(visualLine(getLine(doc, from.line))); + doc.iter(checkWidthStart, to.line + 1, function (line) { + if (line == display.maxLine) { + recomputeMaxLength = true; + return true + } + }); + } + + if (doc.sel.contains(change.from, change.to) > -1) + { signalCursorActivity(cm); } + + updateDoc(doc, change, spans, estimateHeight(cm)); + + if (!cm.options.lineWrapping) { + doc.iter(checkWidthStart, from.line + change.text.length, function (line) { + var len = lineLength(line); + if (len > display.maxLineLength) { + display.maxLine = line; + display.maxLineLength = len; + display.maxLineChanged = true; + recomputeMaxLength = false; + } + }); + if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; } + } + + retreatFrontier(doc, from.line); + startWorker(cm, 400); + + var lendiff = change.text.length - (to.line - from.line) - 1; + // Remember that these lines changed, for updating the display + if (change.full) + { regChange(cm); } + else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change)) + { regLineChange(cm, from.line, "text"); } + else + { regChange(cm, from.line, to.line + 1, lendiff); } + + var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change"); + if (changeHandler || changesHandler) { + var obj = { + from: from, to: to, + text: change.text, + removed: change.removed, + origin: change.origin + }; + if (changeHandler) { signalLater(cm, "change", cm, obj); } + if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); } + } + cm.display.selForContextMenu = null; + } + + function replaceRange(doc, code, from, to, origin) { + var assign; + + if (!to) { to = from; } + if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); } + if (typeof code == "string") { code = doc.splitLines(code); } + makeChange(doc, {from: from, to: to, text: code, origin: origin}); + } + + // Rebasing/resetting history to deal with externally-sourced changes + + function rebaseHistSelSingle(pos, from, to, diff) { + if (to < pos.line) { + pos.line += diff; + } else if (from < pos.line) { + pos.line = from; + pos.ch = 0; + } + } + + // Tries to rebase an array of history events given a change in the + // document. If the change touches the same lines as the event, the + // event, and everything 'behind' it, is discarded. If the change is + // before the event, the event's positions are updated. Uses a + // copy-on-write scheme for the positions, to avoid having to + // reallocate them all on every rebase, but also avoid problems with + // shared position objects being unsafely updated. + function rebaseHistArray(array, from, to, diff) { + for (var i = 0; i < array.length; ++i) { + var sub = array[i], ok = true; + if (sub.ranges) { + if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; } + for (var j = 0; j < sub.ranges.length; j++) { + rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff); + rebaseHistSelSingle(sub.ranges[j].head, from, to, diff); + } + continue + } + for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) { + var cur = sub.changes[j$1]; + if (to < cur.from.line) { + cur.from = Pos(cur.from.line + diff, cur.from.ch); + cur.to = Pos(cur.to.line + diff, cur.to.ch); + } else if (from <= cur.to.line) { + ok = false; + break + } + } + if (!ok) { + array.splice(0, i + 1); + i = 0; + } + } + } + + function rebaseHist(hist, change) { + var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1; + rebaseHistArray(hist.done, from, to, diff); + rebaseHistArray(hist.undone, from, to, diff); + } + + // Utility for applying a change to a line by handle or number, + // returning the number and optionally registering the line as + // changed. + function changeLine(doc, handle, changeType, op) { + var no = handle, line = handle; + if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); } + else { no = lineNo(handle); } + if (no == null) { return null } + if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); } + return line + } + + // The document is represented as a BTree consisting of leaves, with + // chunk of lines in them, and branches, with up to ten leaves or + // other branch nodes below them. The top node is always a branch + // node, and is the document object itself (meaning it has + // additional methods and properties). + // + // All nodes have parent links. The tree is used both to go from + // line numbers to line objects, and to go from objects to numbers. + // It also indexes by height, and is used to convert between height + // and line object, and to find the total height of the document. + // + // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html + + function LeafChunk(lines) { + this.lines = lines; + this.parent = null; + var height = 0; + for (var i = 0; i < lines.length; ++i) { + lines[i].parent = this; + height += lines[i].height; + } + this.height = height; + } + + LeafChunk.prototype = { + chunkSize: function() { return this.lines.length }, + + // Remove the n lines at offset 'at'. + removeInner: function(at, n) { + for (var i = at, e = at + n; i < e; ++i) { + var line = this.lines[i]; + this.height -= line.height; + cleanUpLine(line); + signalLater(line, "delete"); + } + this.lines.splice(at, n); + }, + + // Helper used to collapse a small branch into a single leaf. + collapse: function(lines) { + lines.push.apply(lines, this.lines); + }, + + // Insert the given array of lines at offset 'at', count them as + // having the given height. + insertInner: function(at, lines, height) { + this.height += height; + this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at)); + for (var i = 0; i < lines.length; ++i) { lines[i].parent = this; } + }, + + // Used to iterate over a part of the tree. + iterN: function(at, n, op) { + for (var e = at + n; at < e; ++at) + { if (op(this.lines[at])) { return true } } + } + }; + + function BranchChunk(children) { + this.children = children; + var size = 0, height = 0; + for (var i = 0; i < children.length; ++i) { + var ch = children[i]; + size += ch.chunkSize(); height += ch.height; + ch.parent = this; + } + this.size = size; + this.height = height; + this.parent = null; + } + + BranchChunk.prototype = { + chunkSize: function() { return this.size }, + + removeInner: function(at, n) { + this.size -= n; + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at < sz) { + var rm = Math.min(n, sz - at), oldHeight = child.height; + child.removeInner(at, rm); + this.height -= oldHeight - child.height; + if (sz == rm) { this.children.splice(i--, 1); child.parent = null; } + if ((n -= rm) == 0) { break } + at = 0; + } else { at -= sz; } + } + // If the result is smaller than 25 lines, ensure that it is a + // single leaf node. + if (this.size - n < 25 && + (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) { + var lines = []; + this.collapse(lines); + this.children = [new LeafChunk(lines)]; + this.children[0].parent = this; + } + }, + + collapse: function(lines) { + for (var i = 0; i < this.children.length; ++i) { this.children[i].collapse(lines); } + }, + + insertInner: function(at, lines, height) { + this.size += lines.length; + this.height += height; + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at <= sz) { + child.insertInner(at, lines, height); + if (child.lines && child.lines.length > 50) { + // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced. + // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest. + var remaining = child.lines.length % 25 + 25; + for (var pos = remaining; pos < child.lines.length;) { + var leaf = new LeafChunk(child.lines.slice(pos, pos += 25)); + child.height -= leaf.height; + this.children.splice(++i, 0, leaf); + leaf.parent = this; + } + child.lines = child.lines.slice(0, remaining); + this.maybeSpill(); + } + break + } + at -= sz; + } + }, + + // When a node has grown, check whether it should be split. + maybeSpill: function() { + if (this.children.length <= 10) { return } + var me = this; + do { + var spilled = me.children.splice(me.children.length - 5, 5); + var sibling = new BranchChunk(spilled); + if (!me.parent) { // Become the parent node + var copy = new BranchChunk(me.children); + copy.parent = me; + me.children = [copy, sibling]; + me = copy; + } else { + me.size -= sibling.size; + me.height -= sibling.height; + var myIndex = indexOf(me.parent.children, me); + me.parent.children.splice(myIndex + 1, 0, sibling); + } + sibling.parent = me.parent; + } while (me.children.length > 10) + me.parent.maybeSpill(); + }, + + iterN: function(at, n, op) { + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at < sz) { + var used = Math.min(n, sz - at); + if (child.iterN(at, used, op)) { return true } + if ((n -= used) == 0) { break } + at = 0; + } else { at -= sz; } + } + } + }; + + // Line widgets are block elements displayed above or below a line. + + var LineWidget = function(doc, node, options) { + if (options) { for (var opt in options) { if (options.hasOwnProperty(opt)) + { this[opt] = options[opt]; } } } + this.doc = doc; + this.node = node; + }; + + LineWidget.prototype.clear = function () { + var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line); + if (no == null || !ws) { return } + for (var i = 0; i < ws.length; ++i) { if (ws[i] == this) { ws.splice(i--, 1); } } + if (!ws.length) { line.widgets = null; } + var height = widgetHeight(this); + updateLineHeight(line, Math.max(0, line.height - height)); + if (cm) { + runInOp(cm, function () { + adjustScrollWhenAboveVisible(cm, line, -height); + regLineChange(cm, no, "widget"); + }); + signalLater(cm, "lineWidgetCleared", cm, this, no); + } + }; + + LineWidget.prototype.changed = function () { + var this$1 = this; + + var oldH = this.height, cm = this.doc.cm, line = this.line; + this.height = null; + var diff = widgetHeight(this) - oldH; + if (!diff) { return } + if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); } + if (cm) { + runInOp(cm, function () { + cm.curOp.forceUpdate = true; + adjustScrollWhenAboveVisible(cm, line, diff); + signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line)); + }); + } + }; + eventMixin(LineWidget); + + function adjustScrollWhenAboveVisible(cm, line, diff) { + if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop)) + { addToScrollTop(cm, diff); } + } + + function addLineWidget(doc, handle, node, options) { + var widget = new LineWidget(doc, node, options); + var cm = doc.cm; + if (cm && widget.noHScroll) { cm.display.alignWidgets = true; } + changeLine(doc, handle, "widget", function (line) { + var widgets = line.widgets || (line.widgets = []); + if (widget.insertAt == null) { widgets.push(widget); } + else { widgets.splice(Math.min(widgets.length, Math.max(0, widget.insertAt)), 0, widget); } + widget.line = line; + if (cm && !lineIsHidden(doc, line)) { + var aboveVisible = heightAtLine(line) < doc.scrollTop; + updateLineHeight(line, line.height + widgetHeight(widget)); + if (aboveVisible) { addToScrollTop(cm, widget.height); } + cm.curOp.forceUpdate = true; + } + return true + }); + if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); } + return widget + } + + // TEXTMARKERS + + // Created with markText and setBookmark methods. A TextMarker is a + // handle that can be used to clear or find a marked position in the + // document. Line objects hold arrays (markedSpans) containing + // {from, to, marker} object pointing to such marker objects, and + // indicating that such a marker is present on that line. Multiple + // lines may point to the same marker when it spans across lines. + // The spans will have null for their from/to properties when the + // marker continues beyond the start/end of the line. Markers have + // links back to the lines they currently touch. + + // Collapsed markers have unique ids, in order to be able to order + // them, which is needed for uniquely determining an outer marker + // when they overlap (they may nest, but not partially overlap). + var nextMarkerId = 0; + + var TextMarker = function(doc, type) { + this.lines = []; + this.type = type; + this.doc = doc; + this.id = ++nextMarkerId; + }; + + // Clear the marker. + TextMarker.prototype.clear = function () { + if (this.explicitlyCleared) { return } + var cm = this.doc.cm, withOp = cm && !cm.curOp; + if (withOp) { startOperation(cm); } + if (hasHandler(this, "clear")) { + var found = this.find(); + if (found) { signalLater(this, "clear", found.from, found.to); } + } + var min = null, max = null; + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); + if (cm && !this.collapsed) { regLineChange(cm, lineNo(line), "text"); } + else if (cm) { + if (span.to != null) { max = lineNo(line); } + if (span.from != null) { min = lineNo(line); } + } + line.markedSpans = removeMarkedSpan(line.markedSpans, span); + if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm) + { updateLineHeight(line, textHeight(cm.display)); } + } + if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) { + var visual = visualLine(this.lines[i$1]), len = lineLength(visual); + if (len > cm.display.maxLineLength) { + cm.display.maxLine = visual; + cm.display.maxLineLength = len; + cm.display.maxLineChanged = true; + } + } } + + if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); } + this.lines.length = 0; + this.explicitlyCleared = true; + if (this.atomic && this.doc.cantEdit) { + this.doc.cantEdit = false; + if (cm) { reCheckSelection(cm.doc); } + } + if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); } + if (withOp) { endOperation(cm); } + if (this.parent) { this.parent.clear(); } + }; + + // Find the position of the marker in the document. Returns a {from, + // to} object by default. Side can be passed to get a specific side + // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the + // Pos objects returned contain a line object, rather than a line + // number (used to prevent looking up the same line twice). + TextMarker.prototype.find = function (side, lineObj) { + if (side == null && this.type == "bookmark") { side = 1; } + var from, to; + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); + if (span.from != null) { + from = Pos(lineObj ? line : lineNo(line), span.from); + if (side == -1) { return from } + } + if (span.to != null) { + to = Pos(lineObj ? line : lineNo(line), span.to); + if (side == 1) { return to } + } + } + return from && {from: from, to: to} + }; + + // Signals that the marker's widget changed, and surrounding layout + // should be recomputed. + TextMarker.prototype.changed = function () { + var this$1 = this; + + var pos = this.find(-1, true), widget = this, cm = this.doc.cm; + if (!pos || !cm) { return } + runInOp(cm, function () { + var line = pos.line, lineN = lineNo(pos.line); + var view = findViewForLine(cm, lineN); + if (view) { + clearLineMeasurementCacheFor(view); + cm.curOp.selectionChanged = cm.curOp.forceUpdate = true; + } + cm.curOp.updateMaxLine = true; + if (!lineIsHidden(widget.doc, line) && widget.height != null) { + var oldHeight = widget.height; + widget.height = null; + var dHeight = widgetHeight(widget) - oldHeight; + if (dHeight) + { updateLineHeight(line, line.height + dHeight); } + } + signalLater(cm, "markerChanged", cm, this$1); + }); + }; + + TextMarker.prototype.attachLine = function (line) { + if (!this.lines.length && this.doc.cm) { + var op = this.doc.cm.curOp; + if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) + { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); } + } + this.lines.push(line); + }; + + TextMarker.prototype.detachLine = function (line) { + this.lines.splice(indexOf(this.lines, line), 1); + if (!this.lines.length && this.doc.cm) { + var op = this.doc.cm.curOp + ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this); + } + }; + eventMixin(TextMarker); + + // Create a marker, wire it up to the right lines, and + function markText(doc, from, to, options, type) { + // Shared markers (across linked documents) are handled separately + // (markTextShared will call out to this again, once per + // document). + if (options && options.shared) { return markTextShared(doc, from, to, options, type) } + // Ensure we are in an operation. + if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) } + + var marker = new TextMarker(doc, type), diff = cmp(from, to); + if (options) { copyObj(options, marker, false); } + // Don't connect empty markers unless clearWhenEmpty is false + if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false) + { return marker } + if (marker.replacedWith) { + // Showing up as a widget implies collapsed (widget replaces text) + marker.collapsed = true; + marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget"); + if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); } + if (options.insertLeft) { marker.widgetNode.insertLeft = true; } + } + if (marker.collapsed) { + if (conflictingCollapsedRange(doc, from.line, from, to, marker) || + from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker)) + { throw new Error("Inserting collapsed marker partially overlapping an existing one") } + seeCollapsedSpans(); + } + + if (marker.addToHistory) + { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); } + + var curLine = from.line, cm = doc.cm, updateMaxLine; + doc.iter(curLine, to.line + 1, function (line) { + if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine) + { updateMaxLine = true; } + if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); } + addMarkedSpan(line, new MarkedSpan(marker, + curLine == from.line ? from.ch : null, + curLine == to.line ? to.ch : null)); + ++curLine; + }); + // lineIsHidden depends on the presence of the spans, so needs a second pass + if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) { + if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); } + }); } + + if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); } + + if (marker.readOnly) { + seeReadOnlySpans(); + if (doc.history.done.length || doc.history.undone.length) + { doc.clearHistory(); } + } + if (marker.collapsed) { + marker.id = ++nextMarkerId; + marker.atomic = true; + } + if (cm) { + // Sync editor state + if (updateMaxLine) { cm.curOp.updateMaxLine = true; } + if (marker.collapsed) + { regChange(cm, from.line, to.line + 1); } + else if (marker.className || marker.startStyle || marker.endStyle || marker.css || + marker.attributes || marker.title) + { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } } + if (marker.atomic) { reCheckSelection(cm.doc); } + signalLater(cm, "markerAdded", cm, marker); + } + return marker + } + + // SHARED TEXTMARKERS + + // A shared marker spans multiple linked documents. It is + // implemented as a meta-marker-object controlling multiple normal + // markers. + var SharedTextMarker = function(markers, primary) { + this.markers = markers; + this.primary = primary; + for (var i = 0; i < markers.length; ++i) + { markers[i].parent = this; } + }; + + SharedTextMarker.prototype.clear = function () { + if (this.explicitlyCleared) { return } + this.explicitlyCleared = true; + for (var i = 0; i < this.markers.length; ++i) + { this.markers[i].clear(); } + signalLater(this, "clear"); + }; + + SharedTextMarker.prototype.find = function (side, lineObj) { + return this.primary.find(side, lineObj) + }; + eventMixin(SharedTextMarker); + + function markTextShared(doc, from, to, options, type) { + options = copyObj(options); + options.shared = false; + var markers = [markText(doc, from, to, options, type)], primary = markers[0]; + var widget = options.widgetNode; + linkedDocs(doc, function (doc) { + if (widget) { options.widgetNode = widget.cloneNode(true); } + markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type)); + for (var i = 0; i < doc.linked.length; ++i) + { if (doc.linked[i].isParent) { return } } + primary = lst(markers); + }); + return new SharedTextMarker(markers, primary) + } + + function findSharedMarkers(doc) { + return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; }) + } + + function copySharedMarkers(doc, markers) { + for (var i = 0; i < markers.length; i++) { + var marker = markers[i], pos = marker.find(); + var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to); + if (cmp(mFrom, mTo)) { + var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type); + marker.markers.push(subMark); + subMark.parent = marker; + } + } + } + + function detachSharedMarkers(markers) { + var loop = function ( i ) { + var marker = markers[i], linked = [marker.primary.doc]; + linkedDocs(marker.primary.doc, function (d) { return linked.push(d); }); + for (var j = 0; j < marker.markers.length; j++) { + var subMarker = marker.markers[j]; + if (indexOf(linked, subMarker.doc) == -1) { + subMarker.parent = null; + marker.markers.splice(j--, 1); + } + } + }; + + for (var i = 0; i < markers.length; i++) loop( i ); + } + + var nextDocId = 0; + var Doc = function(text, mode, firstLine, lineSep, direction) { + if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) } + if (firstLine == null) { firstLine = 0; } + + BranchChunk.call(this, [new LeafChunk([new Line("", null)])]); + this.first = firstLine; + this.scrollTop = this.scrollLeft = 0; + this.cantEdit = false; + this.cleanGeneration = 1; + this.modeFrontier = this.highlightFrontier = firstLine; + var start = Pos(firstLine, 0); + this.sel = simpleSelection(start); + this.history = new History(null); + this.id = ++nextDocId; + this.modeOption = mode; + this.lineSep = lineSep; + this.direction = (direction == "rtl") ? "rtl" : "ltr"; + this.extend = false; + + if (typeof text == "string") { text = this.splitLines(text); } + updateDoc(this, {from: start, to: start, text: text}); + setSelection(this, simpleSelection(start), sel_dontScroll); + }; + + Doc.prototype = createObj(BranchChunk.prototype, { + constructor: Doc, + // Iterate over the document. Supports two forms -- with only one + // argument, it calls that for each line in the document. With + // three, it iterates over the range given by the first two (with + // the second being non-inclusive). + iter: function(from, to, op) { + if (op) { this.iterN(from - this.first, to - from, op); } + else { this.iterN(this.first, this.first + this.size, from); } + }, + + // Non-public interface for adding and removing lines. + insert: function(at, lines) { + var height = 0; + for (var i = 0; i < lines.length; ++i) { height += lines[i].height; } + this.insertInner(at - this.first, lines, height); + }, + remove: function(at, n) { this.removeInner(at - this.first, n); }, + + // From here, the methods are part of the public interface. Most + // are also available from CodeMirror (editor) instances. + + getValue: function(lineSep) { + var lines = getLines(this, this.first, this.first + this.size); + if (lineSep === false) { return lines } + return lines.join(lineSep || this.lineSeparator()) + }, + setValue: docMethodOp(function(code) { + var top = Pos(this.first, 0), last = this.first + this.size - 1; + makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length), + text: this.splitLines(code), origin: "setValue", full: true}, true); + if (this.cm) { scrollToCoords(this.cm, 0, 0); } + setSelection(this, simpleSelection(top), sel_dontScroll); + }), + replaceRange: function(code, from, to, origin) { + from = clipPos(this, from); + to = to ? clipPos(this, to) : from; + replaceRange(this, code, from, to, origin); + }, + getRange: function(from, to, lineSep) { + var lines = getBetween(this, clipPos(this, from), clipPos(this, to)); + if (lineSep === false) { return lines } + return lines.join(lineSep || this.lineSeparator()) + }, + + getLine: function(line) {var l = this.getLineHandle(line); return l && l.text}, + + getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }}, + getLineNumber: function(line) {return lineNo(line)}, + + getLineHandleVisualStart: function(line) { + if (typeof line == "number") { line = getLine(this, line); } + return visualLine(line) + }, + + lineCount: function() {return this.size}, + firstLine: function() {return this.first}, + lastLine: function() {return this.first + this.size - 1}, + + clipPos: function(pos) {return clipPos(this, pos)}, + + getCursor: function(start) { + var range = this.sel.primary(), pos; + if (start == null || start == "head") { pos = range.head; } + else if (start == "anchor") { pos = range.anchor; } + else if (start == "end" || start == "to" || start === false) { pos = range.to(); } + else { pos = range.from(); } + return pos + }, + listSelections: function() { return this.sel.ranges }, + somethingSelected: function() {return this.sel.somethingSelected()}, + + setCursor: docMethodOp(function(line, ch, options) { + setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options); + }), + setSelection: docMethodOp(function(anchor, head, options) { + setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options); + }), + extendSelection: docMethodOp(function(head, other, options) { + extendSelection(this, clipPos(this, head), other && clipPos(this, other), options); + }), + extendSelections: docMethodOp(function(heads, options) { + extendSelections(this, clipPosArray(this, heads), options); + }), + extendSelectionsBy: docMethodOp(function(f, options) { + var heads = map(this.sel.ranges, f); + extendSelections(this, clipPosArray(this, heads), options); + }), + setSelections: docMethodOp(function(ranges, primary, options) { + if (!ranges.length) { return } + var out = []; + for (var i = 0; i < ranges.length; i++) + { out[i] = new Range(clipPos(this, ranges[i].anchor), + clipPos(this, ranges[i].head)); } + if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); } + setSelection(this, normalizeSelection(this.cm, out, primary), options); + }), + addSelection: docMethodOp(function(anchor, head, options) { + var ranges = this.sel.ranges.slice(0); + ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor))); + setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options); + }), + + getSelection: function(lineSep) { + var ranges = this.sel.ranges, lines; + for (var i = 0; i < ranges.length; i++) { + var sel = getBetween(this, ranges[i].from(), ranges[i].to()); + lines = lines ? lines.concat(sel) : sel; + } + if (lineSep === false) { return lines } + else { return lines.join(lineSep || this.lineSeparator()) } + }, + getSelections: function(lineSep) { + var parts = [], ranges = this.sel.ranges; + for (var i = 0; i < ranges.length; i++) { + var sel = getBetween(this, ranges[i].from(), ranges[i].to()); + if (lineSep !== false) { sel = sel.join(lineSep || this.lineSeparator()); } + parts[i] = sel; + } + return parts + }, + replaceSelection: function(code, collapse, origin) { + var dup = []; + for (var i = 0; i < this.sel.ranges.length; i++) + { dup[i] = code; } + this.replaceSelections(dup, collapse, origin || "+input"); + }, + replaceSelections: docMethodOp(function(code, collapse, origin) { + var changes = [], sel = this.sel; + for (var i = 0; i < sel.ranges.length; i++) { + var range = sel.ranges[i]; + changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin}; + } + var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse); + for (var i$1 = changes.length - 1; i$1 >= 0; i$1--) + { makeChange(this, changes[i$1]); } + if (newSel) { setSelectionReplaceHistory(this, newSel); } + else if (this.cm) { ensureCursorVisible(this.cm); } + }), + undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}), + redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}), + undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}), + redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}), + + setExtending: function(val) {this.extend = val;}, + getExtending: function() {return this.extend}, + + historySize: function() { + var hist = this.history, done = 0, undone = 0; + for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } } + for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } } + return {undo: done, redo: undone} + }, + clearHistory: function() { + var this$1 = this; + + this.history = new History(this.history.maxGeneration); + linkedDocs(this, function (doc) { return doc.history = this$1.history; }, true); + }, + + markClean: function() { + this.cleanGeneration = this.changeGeneration(true); + }, + changeGeneration: function(forceSplit) { + if (forceSplit) + { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; } + return this.history.generation + }, + isClean: function (gen) { + return this.history.generation == (gen || this.cleanGeneration) + }, + + getHistory: function() { + return {done: copyHistoryArray(this.history.done), + undone: copyHistoryArray(this.history.undone)} + }, + setHistory: function(histData) { + var hist = this.history = new History(this.history.maxGeneration); + hist.done = copyHistoryArray(histData.done.slice(0), null, true); + hist.undone = copyHistoryArray(histData.undone.slice(0), null, true); + }, + + setGutterMarker: docMethodOp(function(line, gutterID, value) { + return changeLine(this, line, "gutter", function (line) { + var markers = line.gutterMarkers || (line.gutterMarkers = {}); + markers[gutterID] = value; + if (!value && isEmpty(markers)) { line.gutterMarkers = null; } + return true + }) + }), + + clearGutter: docMethodOp(function(gutterID) { + var this$1 = this; + + this.iter(function (line) { + if (line.gutterMarkers && line.gutterMarkers[gutterID]) { + changeLine(this$1, line, "gutter", function () { + line.gutterMarkers[gutterID] = null; + if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; } + return true + }); + } + }); + }), + + lineInfo: function(line) { + var n; + if (typeof line == "number") { + if (!isLine(this, line)) { return null } + n = line; + line = getLine(this, line); + if (!line) { return null } + } else { + n = lineNo(line); + if (n == null) { return null } + } + return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, + textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass, + widgets: line.widgets} + }, + + addLineClass: docMethodOp(function(handle, where, cls) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { + var prop = where == "text" ? "textClass" + : where == "background" ? "bgClass" + : where == "gutter" ? "gutterClass" : "wrapClass"; + if (!line[prop]) { line[prop] = cls; } + else if (classTest(cls).test(line[prop])) { return false } + else { line[prop] += " " + cls; } + return true + }) + }), + removeLineClass: docMethodOp(function(handle, where, cls) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { + var prop = where == "text" ? "textClass" + : where == "background" ? "bgClass" + : where == "gutter" ? "gutterClass" : "wrapClass"; + var cur = line[prop]; + if (!cur) { return false } + else if (cls == null) { line[prop] = null; } + else { + var found = cur.match(classTest(cls)); + if (!found) { return false } + var end = found.index + found[0].length; + line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null; + } + return true + }) + }), + + addLineWidget: docMethodOp(function(handle, node, options) { + return addLineWidget(this, handle, node, options) + }), + removeLineWidget: function(widget) { widget.clear(); }, + + markText: function(from, to, options) { + return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range") + }, + setBookmark: function(pos, options) { + var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options), + insertLeft: options && options.insertLeft, + clearWhenEmpty: false, shared: options && options.shared, + handleMouseEvents: options && options.handleMouseEvents}; + pos = clipPos(this, pos); + return markText(this, pos, pos, realOpts, "bookmark") + }, + findMarksAt: function(pos) { + pos = clipPos(this, pos); + var markers = [], spans = getLine(this, pos.line).markedSpans; + if (spans) { for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if ((span.from == null || span.from <= pos.ch) && + (span.to == null || span.to >= pos.ch)) + { markers.push(span.marker.parent || span.marker); } + } } + return markers + }, + findMarks: function(from, to, filter) { + from = clipPos(this, from); to = clipPos(this, to); + var found = [], lineNo = from.line; + this.iter(from.line, to.line + 1, function (line) { + var spans = line.markedSpans; + if (spans) { for (var i = 0; i < spans.length; i++) { + var span = spans[i]; + if (!(span.to != null && lineNo == from.line && from.ch >= span.to || + span.from == null && lineNo != from.line || + span.from != null && lineNo == to.line && span.from >= to.ch) && + (!filter || filter(span.marker))) + { found.push(span.marker.parent || span.marker); } + } } + ++lineNo; + }); + return found + }, + getAllMarks: function() { + var markers = []; + this.iter(function (line) { + var sps = line.markedSpans; + if (sps) { for (var i = 0; i < sps.length; ++i) + { if (sps[i].from != null) { markers.push(sps[i].marker); } } } + }); + return markers + }, + + posFromIndex: function(off) { + var ch, lineNo = this.first, sepSize = this.lineSeparator().length; + this.iter(function (line) { + var sz = line.text.length + sepSize; + if (sz > off) { ch = off; return true } + off -= sz; + ++lineNo; + }); + return clipPos(this, Pos(lineNo, ch)) + }, + indexFromPos: function (coords) { + coords = clipPos(this, coords); + var index = coords.ch; + if (coords.line < this.first || coords.ch < 0) { return 0 } + var sepSize = this.lineSeparator().length; + this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value + index += line.text.length + sepSize; + }); + return index + }, + + copy: function(copyHistory) { + var doc = new Doc(getLines(this, this.first, this.first + this.size), + this.modeOption, this.first, this.lineSep, this.direction); + doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft; + doc.sel = this.sel; + doc.extend = false; + if (copyHistory) { + doc.history.undoDepth = this.history.undoDepth; + doc.setHistory(this.getHistory()); + } + return doc + }, + + linkedDoc: function(options) { + if (!options) { options = {}; } + var from = this.first, to = this.first + this.size; + if (options.from != null && options.from > from) { from = options.from; } + if (options.to != null && options.to < to) { to = options.to; } + var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction); + if (options.sharedHist) { copy.history = this.history + ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist}); + copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]; + copySharedMarkers(copy, findSharedMarkers(this)); + return copy + }, + unlinkDoc: function(other) { + if (other instanceof CodeMirror) { other = other.doc; } + if (this.linked) { for (var i = 0; i < this.linked.length; ++i) { + var link = this.linked[i]; + if (link.doc != other) { continue } + this.linked.splice(i, 1); + other.unlinkDoc(this); + detachSharedMarkers(findSharedMarkers(this)); + break + } } + // If the histories were shared, split them again + if (other.history == this.history) { + var splitIds = [other.id]; + linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true); + other.history = new History(null); + other.history.done = copyHistoryArray(this.history.done, splitIds); + other.history.undone = copyHistoryArray(this.history.undone, splitIds); + } + }, + iterLinkedDocs: function(f) {linkedDocs(this, f);}, + + getMode: function() {return this.mode}, + getEditor: function() {return this.cm}, + + splitLines: function(str) { + if (this.lineSep) { return str.split(this.lineSep) } + return splitLinesAuto(str) + }, + lineSeparator: function() { return this.lineSep || "\n" }, + + setDirection: docMethodOp(function (dir) { + if (dir != "rtl") { dir = "ltr"; } + if (dir == this.direction) { return } + this.direction = dir; + this.iter(function (line) { return line.order = null; }); + if (this.cm) { directionChanged(this.cm); } + }) + }); + + // Public alias. + Doc.prototype.eachLine = Doc.prototype.iter; + + // Kludge to work around strange IE behavior where it'll sometimes + // re-fire a series of drag-related events right after the drop (#1551) + var lastDrop = 0; + + function onDrop(e) { + var cm = this; + clearDragCursor(cm); + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) + { return } + e_preventDefault(e); + if (ie) { lastDrop = +new Date; } + var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files; + if (!pos || cm.isReadOnly()) { return } + // Might be a file drop, in which case we simply extract the text + // and insert it. + if (files && files.length && window.FileReader && window.File) { + var n = files.length, text = Array(n), read = 0; + var markAsReadAndPasteIfAllFilesAreRead = function () { + if (++read == n) { + operation(cm, function () { + pos = clipPos(cm.doc, pos); + var change = {from: pos, to: pos, + text: cm.doc.splitLines( + text.filter(function (t) { return t != null; }).join(cm.doc.lineSeparator())), + origin: "paste"}; + makeChange(cm.doc, change); + setSelectionReplaceHistory(cm.doc, simpleSelection(clipPos(cm.doc, pos), clipPos(cm.doc, changeEnd(change)))); + })(); + } + }; + var readTextFromFile = function (file, i) { + if (cm.options.allowDropFileTypes && + indexOf(cm.options.allowDropFileTypes, file.type) == -1) { + markAsReadAndPasteIfAllFilesAreRead(); + return + } + var reader = new FileReader; + reader.onerror = function () { return markAsReadAndPasteIfAllFilesAreRead(); }; + reader.onload = function () { + var content = reader.result; + if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { + markAsReadAndPasteIfAllFilesAreRead(); + return + } + text[i] = content; + markAsReadAndPasteIfAllFilesAreRead(); + }; + reader.readAsText(file); + }; + for (var i = 0; i < files.length; i++) { readTextFromFile(files[i], i); } + } else { // Normal drop + // Don't do a replace if the drop happened inside of the selected text. + if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) { + cm.state.draggingText(e); + // Ensure the editor is re-focused + setTimeout(function () { return cm.display.input.focus(); }, 20); + return + } + try { + var text$1 = e.dataTransfer.getData("Text"); + if (text$1) { + var selected; + if (cm.state.draggingText && !cm.state.draggingText.copy) + { selected = cm.listSelections(); } + setSelectionNoUndo(cm.doc, simpleSelection(pos, pos)); + if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1) + { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } } + cm.replaceSelection(text$1, "around", "paste"); + cm.display.input.focus(); + } + } + catch(e$1){} + } + } + + function onDragStart(cm, e) { + if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return } + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return } + + e.dataTransfer.setData("Text", cm.getSelection()); + e.dataTransfer.effectAllowed = "copyMove"; + + // Use dummy image instead of default browsers image. + // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there. + if (e.dataTransfer.setDragImage && !safari) { + var img = elt("img", null, null, "position: fixed; left: 0; top: 0;"); + img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; + if (presto) { + img.width = img.height = 1; + cm.display.wrapper.appendChild(img); + // Force a relayout, or Opera won't use our image for some obscure reason + img._top = img.offsetTop; + } + e.dataTransfer.setDragImage(img, 0, 0); + if (presto) { img.parentNode.removeChild(img); } + } + } + + function onDragOver(cm, e) { + var pos = posFromMouse(cm, e); + if (!pos) { return } + var frag = document.createDocumentFragment(); + drawSelectionCursor(cm, pos, frag); + if (!cm.display.dragCursor) { + cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors"); + cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv); + } + removeChildrenAndAdd(cm.display.dragCursor, frag); + } + + function clearDragCursor(cm) { + if (cm.display.dragCursor) { + cm.display.lineSpace.removeChild(cm.display.dragCursor); + cm.display.dragCursor = null; + } + } + + // These must be handled carefully, because naively registering a + // handler for each editor will cause the editors to never be + // garbage collected. + + function forEachCodeMirror(f) { + if (!document.getElementsByClassName) { return } + var byClass = document.getElementsByClassName("CodeMirror"), editors = []; + for (var i = 0; i < byClass.length; i++) { + var cm = byClass[i].CodeMirror; + if (cm) { editors.push(cm); } + } + if (editors.length) { editors[0].operation(function () { + for (var i = 0; i < editors.length; i++) { f(editors[i]); } + }); } + } + + var globalsRegistered = false; + function ensureGlobalHandlers() { + if (globalsRegistered) { return } + registerGlobalHandlers(); + globalsRegistered = true; + } + function registerGlobalHandlers() { + // When the window resizes, we need to refresh active editors. + var resizeTimer; + on(window, "resize", function () { + if (resizeTimer == null) { resizeTimer = setTimeout(function () { + resizeTimer = null; + forEachCodeMirror(onResize); + }, 100); } + }); + // When the window loses focus, we want to show the editor as blurred + on(window, "blur", function () { return forEachCodeMirror(onBlur); }); + } + // Called when the window resizes + function onResize(cm) { + var d = cm.display; + // Might be a text scaling operation, clear size caches. + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; + d.scrollbarsClipped = false; + cm.setSize(); + } + + var keyNames = { + 3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", + 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", + 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", + 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", + 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 145: "ScrollLock", + 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", + 221: "]", 222: "'", 224: "Mod", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", + 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert" + }; + + // Number keys + for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); } + // Alphabetic keys + for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); } + // Function keys + for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; } + + var keyMap = {}; + + keyMap.basic = { + "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", + "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", + "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore", + "Tab": "defaultTab", "Shift-Tab": "indentAuto", + "Enter": "newlineAndIndent", "Insert": "toggleOverwrite", + "Esc": "singleSelection" + }; + // Note that the save and find-related commands aren't defined by + // default. User code or addons can define them. Unknown commands + // are simply ignored. + keyMap.pcDefault = { + "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", + "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown", + "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", + "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find", + "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", + "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", + "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection", + "fallthrough": "basic" + }; + // Very basic readline/emacs-style bindings, which are standard on Mac. + keyMap.emacsy = { + "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", + "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", + "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", + "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars", + "Ctrl-O": "openLine" + }; + keyMap.macDefault = { + "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", + "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft", + "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore", + "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find", + "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", + "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight", + "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd", + "fallthrough": ["basic", "emacsy"] + }; + keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault; + + // KEYMAP DISPATCH + + function normalizeKeyName(name) { + var parts = name.split(/-(?!$)/); + name = parts[parts.length - 1]; + var alt, ctrl, shift, cmd; + for (var i = 0; i < parts.length - 1; i++) { + var mod = parts[i]; + if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; } + else if (/^a(lt)?$/i.test(mod)) { alt = true; } + else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; } + else if (/^s(hift)?$/i.test(mod)) { shift = true; } + else { throw new Error("Unrecognized modifier name: " + mod) } + } + if (alt) { name = "Alt-" + name; } + if (ctrl) { name = "Ctrl-" + name; } + if (cmd) { name = "Cmd-" + name; } + if (shift) { name = "Shift-" + name; } + return name + } + + // This is a kludge to keep keymaps mostly working as raw objects + // (backwards compatibility) while at the same time support features + // like normalization and multi-stroke key bindings. It compiles a + // new normalized keymap, and then updates the old object to reflect + // this. + function normalizeKeyMap(keymap) { + var copy = {}; + for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) { + var value = keymap[keyname]; + if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue } + if (value == "...") { delete keymap[keyname]; continue } + + var keys = map(keyname.split(" "), normalizeKeyName); + for (var i = 0; i < keys.length; i++) { + var val = (void 0), name = (void 0); + if (i == keys.length - 1) { + name = keys.join(" "); + val = value; + } else { + name = keys.slice(0, i + 1).join(" "); + val = "..."; + } + var prev = copy[name]; + if (!prev) { copy[name] = val; } + else if (prev != val) { throw new Error("Inconsistent bindings for " + name) } + } + delete keymap[keyname]; + } } + for (var prop in copy) { keymap[prop] = copy[prop]; } + return keymap + } + + function lookupKey(key, map, handle, context) { + map = getKeyMap(map); + var found = map.call ? map.call(key, context) : map[key]; + if (found === false) { return "nothing" } + if (found === "...") { return "multi" } + if (found != null && handle(found)) { return "handled" } + + if (map.fallthrough) { + if (Object.prototype.toString.call(map.fallthrough) != "[object Array]") + { return lookupKey(key, map.fallthrough, handle, context) } + for (var i = 0; i < map.fallthrough.length; i++) { + var result = lookupKey(key, map.fallthrough[i], handle, context); + if (result) { return result } + } + } + } + + // Modifier key presses don't count as 'real' key presses for the + // purpose of keymap fallthrough. + function isModifierKey(value) { + var name = typeof value == "string" ? value : keyNames[value.keyCode]; + return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod" + } + + function addModifierNames(name, event, noShift) { + var base = name; + if (event.altKey && base != "Alt") { name = "Alt-" + name; } + if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; } + if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Mod") { name = "Cmd-" + name; } + if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; } + return name + } + + // Look up the name of a key as indicated by an event object. + function keyName(event, noShift) { + if (presto && event.keyCode == 34 && event["char"]) { return false } + var name = keyNames[event.keyCode]; + if (name == null || event.altGraphKey) { return false } + // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause, + // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+) + if (event.keyCode == 3 && event.code) { name = event.code; } + return addModifierNames(name, event, noShift) + } + + function getKeyMap(val) { + return typeof val == "string" ? keyMap[val] : val + } + + // Helper for deleting text near the selection(s), used to implement + // backspace, delete, and similar functionality. + function deleteNearSelection(cm, compute) { + var ranges = cm.doc.sel.ranges, kill = []; + // Build up a set of ranges to kill first, merging overlapping + // ranges. + for (var i = 0; i < ranges.length; i++) { + var toKill = compute(ranges[i]); + while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) { + var replaced = kill.pop(); + if (cmp(replaced.from, toKill.from) < 0) { + toKill.from = replaced.from; + break + } + } + kill.push(toKill); + } + // Next, remove those actual ranges. + runInOp(cm, function () { + for (var i = kill.length - 1; i >= 0; i--) + { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); } + ensureCursorVisible(cm); + }); + } + + function moveCharLogically(line, ch, dir) { + var target = skipExtendingChars(line.text, ch + dir, dir); + return target < 0 || target > line.text.length ? null : target + } + + function moveLogically(line, start, dir) { + var ch = moveCharLogically(line, start.ch, dir); + return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before") + } + + function endOfLine(visually, cm, lineObj, lineNo, dir) { + if (visually) { + if (cm.doc.direction == "rtl") { dir = -dir; } + var order = getOrder(lineObj, cm.doc.direction); + if (order) { + var part = dir < 0 ? lst(order) : order[0]; + var moveInStorageOrder = (dir < 0) == (part.level == 1); + var sticky = moveInStorageOrder ? "after" : "before"; + var ch; + // With a wrapped rtl chunk (possibly spanning multiple bidi parts), + // it could be that the last bidi part is not on the last visual line, + // since visual lines contain content order-consecutive chunks. + // Thus, in rtl, we are looking for the first (content-order) character + // in the rtl chunk that is on the last line (that is, the same line + // as the last (content-order) character). + if (part.level > 0 || cm.doc.direction == "rtl") { + var prep = prepareMeasureForLine(cm, lineObj); + ch = dir < 0 ? lineObj.text.length - 1 : 0; + var targetTop = measureCharPrepared(cm, prep, ch).top; + ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch); + if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); } + } else { ch = dir < 0 ? part.to : part.from; } + return new Pos(lineNo, ch, sticky) + } + } + return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after") + } + + function moveVisually(cm, line, start, dir) { + var bidi = getOrder(line, cm.doc.direction); + if (!bidi) { return moveLogically(line, start, dir) } + if (start.ch >= line.text.length) { + start.ch = line.text.length; + start.sticky = "before"; + } else if (start.ch <= 0) { + start.ch = 0; + start.sticky = "after"; + } + var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]; + if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) { + // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines, + // nothing interesting happens. + return moveLogically(line, start, dir) + } + + var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }; + var prep; + var getWrappedLineExtent = function (ch) { + if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} } + prep = prep || prepareMeasureForLine(cm, line); + return wrappedLineExtentChar(cm, line, prep, ch) + }; + var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch); + + if (cm.doc.direction == "rtl" || part.level == 1) { + var moveInStorageOrder = (part.level == 1) == (dir < 0); + var ch = mv(start, moveInStorageOrder ? 1 : -1); + if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) { + // Case 2: We move within an rtl part or in an rtl editor on the same visual line + var sticky = moveInStorageOrder ? "before" : "after"; + return new Pos(start.line, ch, sticky) + } + } + + // Case 3: Could not move within this bidi part in this visual line, so leave + // the current bidi part + + var searchInVisualLine = function (partPos, dir, wrappedLineExtent) { + var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder + ? new Pos(start.line, mv(ch, 1), "before") + : new Pos(start.line, ch, "after"); }; + + for (; partPos >= 0 && partPos < bidi.length; partPos += dir) { + var part = bidi[partPos]; + var moveInStorageOrder = (dir > 0) == (part.level != 1); + var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1); + if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) } + ch = moveInStorageOrder ? part.from : mv(part.to, -1); + if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) } + } + }; + + // Case 3a: Look for other bidi parts on the same visual line + var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent); + if (res) { return res } + + // Case 3b: Look for other bidi parts on the next visual line + var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1); + if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) { + res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh)); + if (res) { return res } + } + + // Case 4: Nowhere to move + return null + } + + // Commands are parameter-less actions that can be performed on an + // editor, mostly used for keybindings. + var commands = { + selectAll: selectAll, + singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); }, + killLine: function (cm) { return deleteNearSelection(cm, function (range) { + if (range.empty()) { + var len = getLine(cm.doc, range.head.line).text.length; + if (range.head.ch == len && range.head.line < cm.lastLine()) + { return {from: range.head, to: Pos(range.head.line + 1, 0)} } + else + { return {from: range.head, to: Pos(range.head.line, len)} } + } else { + return {from: range.from(), to: range.to()} + } + }); }, + deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({ + from: Pos(range.from().line, 0), + to: clipPos(cm.doc, Pos(range.to().line + 1, 0)) + }); }); }, + delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({ + from: Pos(range.from().line, 0), to: range.from() + }); }); }, + delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { + var top = cm.charCoords(range.head, "div").top + 5; + var leftPos = cm.coordsChar({left: 0, top: top}, "div"); + return {from: leftPos, to: range.from()} + }); }, + delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) { + var top = cm.charCoords(range.head, "div").top + 5; + var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"); + return {from: range.from(), to: rightPos } + }); }, + undo: function (cm) { return cm.undo(); }, + redo: function (cm) { return cm.redo(); }, + undoSelection: function (cm) { return cm.undoSelection(); }, + redoSelection: function (cm) { return cm.redoSelection(); }, + goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); }, + goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); }, + goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); }, + {origin: "+move", bias: 1} + ); }, + goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); }, + {origin: "+move", bias: 1} + ); }, + goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); }, + {origin: "+move", bias: -1} + ); }, + goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.cursorCoords(range.head, "div").top + 5; + return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") + }, sel_move); }, + goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.cursorCoords(range.head, "div").top + 5; + return cm.coordsChar({left: 0, top: top}, "div") + }, sel_move); }, + goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.cursorCoords(range.head, "div").top + 5; + var pos = cm.coordsChar({left: 0, top: top}, "div"); + if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) } + return pos + }, sel_move); }, + goLineUp: function (cm) { return cm.moveV(-1, "line"); }, + goLineDown: function (cm) { return cm.moveV(1, "line"); }, + goPageUp: function (cm) { return cm.moveV(-1, "page"); }, + goPageDown: function (cm) { return cm.moveV(1, "page"); }, + goCharLeft: function (cm) { return cm.moveH(-1, "char"); }, + goCharRight: function (cm) { return cm.moveH(1, "char"); }, + goColumnLeft: function (cm) { return cm.moveH(-1, "column"); }, + goColumnRight: function (cm) { return cm.moveH(1, "column"); }, + goWordLeft: function (cm) { return cm.moveH(-1, "word"); }, + goGroupRight: function (cm) { return cm.moveH(1, "group"); }, + goGroupLeft: function (cm) { return cm.moveH(-1, "group"); }, + goWordRight: function (cm) { return cm.moveH(1, "word"); }, + delCharBefore: function (cm) { return cm.deleteH(-1, "codepoint"); }, + delCharAfter: function (cm) { return cm.deleteH(1, "char"); }, + delWordBefore: function (cm) { return cm.deleteH(-1, "word"); }, + delWordAfter: function (cm) { return cm.deleteH(1, "word"); }, + delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); }, + delGroupAfter: function (cm) { return cm.deleteH(1, "group"); }, + indentAuto: function (cm) { return cm.indentSelection("smart"); }, + indentMore: function (cm) { return cm.indentSelection("add"); }, + indentLess: function (cm) { return cm.indentSelection("subtract"); }, + insertTab: function (cm) { return cm.replaceSelection("\t"); }, + insertSoftTab: function (cm) { + var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize; + for (var i = 0; i < ranges.length; i++) { + var pos = ranges[i].from(); + var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize); + spaces.push(spaceStr(tabSize - col % tabSize)); + } + cm.replaceSelections(spaces); + }, + defaultTab: function (cm) { + if (cm.somethingSelected()) { cm.indentSelection("add"); } + else { cm.execCommand("insertTab"); } + }, + // Swap the two chars left and right of each selection's head. + // Move cursor behind the two swapped characters afterwards. + // + // Doesn't consider line feeds a character. + // Doesn't scan more than one line above to find a character. + // Doesn't do anything on an empty line. + // Doesn't do anything with non-empty selections. + transposeChars: function (cm) { return runInOp(cm, function () { + var ranges = cm.listSelections(), newSel = []; + for (var i = 0; i < ranges.length; i++) { + if (!ranges[i].empty()) { continue } + var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text; + if (line) { + if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); } + if (cur.ch > 0) { + cur = new Pos(cur.line, cur.ch + 1); + cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), + Pos(cur.line, cur.ch - 2), cur, "+transpose"); + } else if (cur.line > cm.doc.first) { + var prev = getLine(cm.doc, cur.line - 1).text; + if (prev) { + cur = new Pos(cur.line, 1); + cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() + + prev.charAt(prev.length - 1), + Pos(cur.line - 1, prev.length - 1), cur, "+transpose"); + } + } + } + newSel.push(new Range(cur, cur)); + } + cm.setSelections(newSel); + }); }, + newlineAndIndent: function (cm) { return runInOp(cm, function () { + var sels = cm.listSelections(); + for (var i = sels.length - 1; i >= 0; i--) + { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); } + sels = cm.listSelections(); + for (var i$1 = 0; i$1 < sels.length; i$1++) + { cm.indentLine(sels[i$1].from().line, null, true); } + ensureCursorVisible(cm); + }); }, + openLine: function (cm) { return cm.replaceSelection("\n", "start"); }, + toggleOverwrite: function (cm) { return cm.toggleOverwrite(); } + }; + + + function lineStart(cm, lineN) { + var line = getLine(cm.doc, lineN); + var visual = visualLine(line); + if (visual != line) { lineN = lineNo(visual); } + return endOfLine(true, cm, visual, lineN, 1) + } + function lineEnd(cm, lineN) { + var line = getLine(cm.doc, lineN); + var visual = visualLineEnd(line); + if (visual != line) { lineN = lineNo(visual); } + return endOfLine(true, cm, line, lineN, -1) + } + function lineStartSmart(cm, pos) { + var start = lineStart(cm, pos.line); + var line = getLine(cm.doc, start.line); + var order = getOrder(line, cm.doc.direction); + if (!order || order[0].level == 0) { + var firstNonWS = Math.max(start.ch, line.text.search(/\S/)); + var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch; + return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky) + } + return start + } + + // Run a handler that was bound to a key. + function doHandleBinding(cm, bound, dropShift) { + if (typeof bound == "string") { + bound = commands[bound]; + if (!bound) { return false } + } + // Ensure previous input has been read, so that the handler sees a + // consistent view of the document + cm.display.input.ensurePolled(); + var prevShift = cm.display.shift, done = false; + try { + if (cm.isReadOnly()) { cm.state.suppressEdits = true; } + if (dropShift) { cm.display.shift = false; } + done = bound(cm) != Pass; + } finally { + cm.display.shift = prevShift; + cm.state.suppressEdits = false; + } + return done + } + + function lookupKeyForEditor(cm, name, handle) { + for (var i = 0; i < cm.state.keyMaps.length; i++) { + var result = lookupKey(name, cm.state.keyMaps[i], handle, cm); + if (result) { return result } + } + return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm)) + || lookupKey(name, cm.options.keyMap, handle, cm) + } + + // Note that, despite the name, this function is also used to check + // for bound mouse clicks. + + var stopSeq = new Delayed; + + function dispatchKey(cm, name, e, handle) { + var seq = cm.state.keySeq; + if (seq) { + if (isModifierKey(name)) { return "handled" } + if (/\'$/.test(name)) + { cm.state.keySeq = null; } + else + { stopSeq.set(50, function () { + if (cm.state.keySeq == seq) { + cm.state.keySeq = null; + cm.display.input.reset(); + } + }); } + if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true } + } + return dispatchKeyInner(cm, name, e, handle) + } + + function dispatchKeyInner(cm, name, e, handle) { + var result = lookupKeyForEditor(cm, name, handle); + + if (result == "multi") + { cm.state.keySeq = name; } + if (result == "handled") + { signalLater(cm, "keyHandled", cm, name, e); } + + if (result == "handled" || result == "multi") { + e_preventDefault(e); + restartBlink(cm); + } + + return !!result + } + + // Handle a key from the keydown event. + function handleKeyBinding(cm, e) { + var name = keyName(e, true); + if (!name) { return false } + + if (e.shiftKey && !cm.state.keySeq) { + // First try to resolve full name (including 'Shift-'). Failing + // that, see if there is a cursor-motion command (starting with + // 'go') bound to the keyname without 'Shift-'. + return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); }) + || dispatchKey(cm, name, e, function (b) { + if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion) + { return doHandleBinding(cm, b) } + }) + } else { + return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); }) + } + } + + // Handle a key from the keypress event + function handleCharBinding(cm, e, ch) { + return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); }) + } + + var lastStoppedKey = null; + function onKeyDown(e) { + var cm = this; + if (e.target && e.target != cm.display.input.getField()) { return } + cm.curOp.focus = activeElt(); + if (signalDOMEvent(cm, e)) { return } + // IE does strange things with escape. + if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; } + var code = e.keyCode; + cm.display.shift = code == 16 || e.shiftKey; + var handled = handleKeyBinding(cm, e); + if (presto) { + lastStoppedKey = handled ? code : null; + // Opera has no cut event... we try to at least catch the key combo + if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey)) + { cm.replaceSelection("", null, "cut"); } + } + if (gecko && !mac && !handled && code == 46 && e.shiftKey && !e.ctrlKey && document.execCommand) + { document.execCommand("cut"); } + + // Turn mouse into crosshair when Alt is held on Mac. + if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className)) + { showCrossHair(cm); } + } + + function showCrossHair(cm) { + var lineDiv = cm.display.lineDiv; + addClass(lineDiv, "CodeMirror-crosshair"); + + function up(e) { + if (e.keyCode == 18 || !e.altKey) { + rmClass(lineDiv, "CodeMirror-crosshair"); + off(document, "keyup", up); + off(document, "mouseover", up); + } + } + on(document, "keyup", up); + on(document, "mouseover", up); + } + + function onKeyUp(e) { + if (e.keyCode == 16) { this.doc.sel.shift = false; } + signalDOMEvent(this, e); + } + + function onKeyPress(e) { + var cm = this; + if (e.target && e.target != cm.display.input.getField()) { return } + if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return } + var keyCode = e.keyCode, charCode = e.charCode; + if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return} + if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return } + var ch = String.fromCharCode(charCode == null ? keyCode : charCode); + // Some browsers fire keypress events for backspace + if (ch == "\x08") { return } + if (handleCharBinding(cm, e, ch)) { return } + cm.display.input.onKeyPress(e); + } + + var DOUBLECLICK_DELAY = 400; + + var PastClick = function(time, pos, button) { + this.time = time; + this.pos = pos; + this.button = button; + }; + + PastClick.prototype.compare = function (time, pos, button) { + return this.time + DOUBLECLICK_DELAY > time && + cmp(pos, this.pos) == 0 && button == this.button + }; + + var lastClick, lastDoubleClick; + function clickRepeat(pos, button) { + var now = +new Date; + if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) { + lastClick = lastDoubleClick = null; + return "triple" + } else if (lastClick && lastClick.compare(now, pos, button)) { + lastDoubleClick = new PastClick(now, pos, button); + lastClick = null; + return "double" + } else { + lastClick = new PastClick(now, pos, button); + lastDoubleClick = null; + return "single" + } + } + + // A mouse down can be a single click, double click, triple click, + // start of selection drag, start of text drag, new cursor + // (ctrl-click), rectangle drag (alt-drag), or xwin + // middle-click-paste. Or it might be a click on something we should + // not interfere with, such as a scrollbar or widget. + function onMouseDown(e) { + var cm = this, display = cm.display; + if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return } + display.input.ensurePolled(); + display.shift = e.shiftKey; + + if (eventInWidget(display, e)) { + if (!webkit) { + // Briefly turn off draggability, to allow widgets to do + // normal dragging things. + display.scroller.draggable = false; + setTimeout(function () { return display.scroller.draggable = true; }, 100); + } + return + } + if (clickInGutter(cm, e)) { return } + var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single"; + window.focus(); + + // #3261: make sure, that we're not starting a second selection + if (button == 1 && cm.state.selectingText) + { cm.state.selectingText(e); } + + if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return } + + if (button == 1) { + if (pos) { leftButtonDown(cm, pos, repeat, e); } + else if (e_target(e) == display.scroller) { e_preventDefault(e); } + } else if (button == 2) { + if (pos) { extendSelection(cm.doc, pos); } + setTimeout(function () { return display.input.focus(); }, 20); + } else if (button == 3) { + if (captureRightClick) { cm.display.input.onContextMenu(e); } + else { delayBlurEvent(cm); } + } + } + + function handleMappedButton(cm, button, pos, repeat, event) { + var name = "Click"; + if (repeat == "double") { name = "Double" + name; } + else if (repeat == "triple") { name = "Triple" + name; } + name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name; + + return dispatchKey(cm, addModifierNames(name, event), event, function (bound) { + if (typeof bound == "string") { bound = commands[bound]; } + if (!bound) { return false } + var done = false; + try { + if (cm.isReadOnly()) { cm.state.suppressEdits = true; } + done = bound(cm, pos) != Pass; + } finally { + cm.state.suppressEdits = false; + } + return done + }) + } + + function configureMouse(cm, repeat, event) { + var option = cm.getOption("configureMouse"); + var value = option ? option(cm, repeat, event) : {}; + if (value.unit == null) { + var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey; + value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line"; + } + if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; } + if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; } + if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); } + return value + } + + function leftButtonDown(cm, pos, repeat, event) { + if (ie) { setTimeout(bind(ensureFocus, cm), 0); } + else { cm.curOp.focus = activeElt(); } + + var behavior = configureMouse(cm, repeat, event); + + var sel = cm.doc.sel, contained; + if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() && + repeat == "single" && (contained = sel.contains(pos)) > -1 && + (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) && + (cmp(contained.to(), pos) > 0 || pos.xRel < 0)) + { leftButtonStartDrag(cm, event, pos, behavior); } + else + { leftButtonSelect(cm, event, pos, behavior); } + } + + // Start a text drag. When it ends, see if any dragging actually + // happen, and treat as a click if it didn't. + function leftButtonStartDrag(cm, event, pos, behavior) { + var display = cm.display, moved = false; + var dragEnd = operation(cm, function (e) { + if (webkit) { display.scroller.draggable = false; } + cm.state.draggingText = false; + if (cm.state.delayingBlurEvent) { + if (cm.hasFocus()) { cm.state.delayingBlurEvent = false; } + else { delayBlurEvent(cm); } + } + off(display.wrapper.ownerDocument, "mouseup", dragEnd); + off(display.wrapper.ownerDocument, "mousemove", mouseMove); + off(display.scroller, "dragstart", dragStart); + off(display.scroller, "drop", dragEnd); + if (!moved) { + e_preventDefault(e); + if (!behavior.addNew) + { extendSelection(cm.doc, pos, null, null, behavior.extend); } + // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081) + if ((webkit && !safari) || ie && ie_version == 9) + { setTimeout(function () {display.wrapper.ownerDocument.body.focus({preventScroll: true}); display.input.focus();}, 20); } + else + { display.input.focus(); } + } + }); + var mouseMove = function(e2) { + moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10; + }; + var dragStart = function () { return moved = true; }; + // Let the drag handler handle this. + if (webkit) { display.scroller.draggable = true; } + cm.state.draggingText = dragEnd; + dragEnd.copy = !behavior.moveOnDrag; + on(display.wrapper.ownerDocument, "mouseup", dragEnd); + on(display.wrapper.ownerDocument, "mousemove", mouseMove); + on(display.scroller, "dragstart", dragStart); + on(display.scroller, "drop", dragEnd); + + cm.state.delayingBlurEvent = true; + setTimeout(function () { return display.input.focus(); }, 20); + // IE's approach to draggable + if (display.scroller.dragDrop) { display.scroller.dragDrop(); } + } + + function rangeForUnit(cm, pos, unit) { + if (unit == "char") { return new Range(pos, pos) } + if (unit == "word") { return cm.findWordAt(pos) } + if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) } + var result = unit(cm, pos); + return new Range(result.from, result.to) + } + + // Normal selection, as opposed to text dragging. + function leftButtonSelect(cm, event, start, behavior) { + if (ie) { delayBlurEvent(cm); } + var display = cm.display, doc = cm.doc; + e_preventDefault(event); + + var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges; + if (behavior.addNew && !behavior.extend) { + ourIndex = doc.sel.contains(start); + if (ourIndex > -1) + { ourRange = ranges[ourIndex]; } + else + { ourRange = new Range(start, start); } + } else { + ourRange = doc.sel.primary(); + ourIndex = doc.sel.primIndex; + } + + if (behavior.unit == "rectangle") { + if (!behavior.addNew) { ourRange = new Range(start, start); } + start = posFromMouse(cm, event, true, true); + ourIndex = -1; + } else { + var range = rangeForUnit(cm, start, behavior.unit); + if (behavior.extend) + { ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend); } + else + { ourRange = range; } + } + + if (!behavior.addNew) { + ourIndex = 0; + setSelection(doc, new Selection([ourRange], 0), sel_mouse); + startSel = doc.sel; + } else if (ourIndex == -1) { + ourIndex = ranges.length; + setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex), + {scroll: false, origin: "*mouse"}); + } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) { + setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0), + {scroll: false, origin: "*mouse"}); + startSel = doc.sel; + } else { + replaceOneSelection(doc, ourIndex, ourRange, sel_mouse); + } + + var lastPos = start; + function extendTo(pos) { + if (cmp(lastPos, pos) == 0) { return } + lastPos = pos; + + if (behavior.unit == "rectangle") { + var ranges = [], tabSize = cm.options.tabSize; + var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize); + var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize); + var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol); + for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line)); + line <= end; line++) { + var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize); + if (left == right) + { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); } + else if (text.length > leftPos) + { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); } + } + if (!ranges.length) { ranges.push(new Range(start, start)); } + setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex), + {origin: "*mouse", scroll: false}); + cm.scrollIntoView(pos); + } else { + var oldRange = ourRange; + var range = rangeForUnit(cm, pos, behavior.unit); + var anchor = oldRange.anchor, head; + if (cmp(range.anchor, anchor) > 0) { + head = range.head; + anchor = minPos(oldRange.from(), range.anchor); + } else { + head = range.anchor; + anchor = maxPos(oldRange.to(), range.head); + } + var ranges$1 = startSel.ranges.slice(0); + ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head)); + setSelection(doc, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse); + } + } + + var editorSize = display.wrapper.getBoundingClientRect(); + // Used to ensure timeout re-tries don't fire when another extend + // happened in the meantime (clearTimeout isn't reliable -- at + // least on Chrome, the timeouts still happen even when cleared, + // if the clear happens after their scheduled firing time). + var counter = 0; + + function extend(e) { + var curCount = ++counter; + var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle"); + if (!cur) { return } + if (cmp(cur, lastPos) != 0) { + cm.curOp.focus = activeElt(); + extendTo(cur); + var visible = visibleLines(display, doc); + if (cur.line >= visible.to || cur.line < visible.from) + { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); } + } else { + var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0; + if (outside) { setTimeout(operation(cm, function () { + if (counter != curCount) { return } + display.scroller.scrollTop += outside; + extend(e); + }), 50); } + } + } + + function done(e) { + cm.state.selectingText = false; + counter = Infinity; + // If e is null or undefined we interpret this as someone trying + // to explicitly cancel the selection rather than the user + // letting go of the mouse button. + if (e) { + e_preventDefault(e); + display.input.focus(); + } + off(display.wrapper.ownerDocument, "mousemove", move); + off(display.wrapper.ownerDocument, "mouseup", up); + doc.history.lastSelOrigin = null; + } + + var move = operation(cm, function (e) { + if (e.buttons === 0 || !e_button(e)) { done(e); } + else { extend(e); } + }); + var up = operation(cm, done); + cm.state.selectingText = up; + on(display.wrapper.ownerDocument, "mousemove", move); + on(display.wrapper.ownerDocument, "mouseup", up); + } + + // Used when mouse-selecting to adjust the anchor to the proper side + // of a bidi jump depending on the visual position of the head. + function bidiSimplify(cm, range) { + var anchor = range.anchor; + var head = range.head; + var anchorLine = getLine(cm.doc, anchor.line); + if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range } + var order = getOrder(anchorLine); + if (!order) { return range } + var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index]; + if (part.from != anchor.ch && part.to != anchor.ch) { return range } + var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1); + if (boundary == 0 || boundary == order.length) { return range } + + // Compute the relative visual position of the head compared to the + // anchor (<0 is to the left, >0 to the right) + var leftSide; + if (head.line != anchor.line) { + leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0; + } else { + var headIndex = getBidiPartAt(order, head.ch, head.sticky); + var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1); + if (headIndex == boundary - 1 || headIndex == boundary) + { leftSide = dir < 0; } + else + { leftSide = dir > 0; } + } + + var usePart = order[boundary + (leftSide ? -1 : 0)]; + var from = leftSide == (usePart.level == 1); + var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before"; + return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head) + } + + + // Determines whether an event happened in the gutter, and fires the + // handlers for the corresponding event. + function gutterEvent(cm, e, type, prevent) { + var mX, mY; + if (e.touches) { + mX = e.touches[0].clientX; + mY = e.touches[0].clientY; + } else { + try { mX = e.clientX; mY = e.clientY; } + catch(e$1) { return false } + } + if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false } + if (prevent) { e_preventDefault(e); } + + var display = cm.display; + var lineBox = display.lineDiv.getBoundingClientRect(); + + if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) } + mY -= lineBox.top - display.viewOffset; + + for (var i = 0; i < cm.display.gutterSpecs.length; ++i) { + var g = display.gutters.childNodes[i]; + if (g && g.getBoundingClientRect().right >= mX) { + var line = lineAtHeight(cm.doc, mY); + var gutter = cm.display.gutterSpecs[i]; + signal(cm, type, cm, line, gutter.className, e); + return e_defaultPrevented(e) + } + } + } + + function clickInGutter(cm, e) { + return gutterEvent(cm, e, "gutterClick", true) + } + + // CONTEXT MENU HANDLING + + // To make the context menu work, we need to briefly unhide the + // textarea (making it as unobtrusive as possible) to let the + // right-click take effect on it. + function onContextMenu(cm, e) { + if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return } + if (signalDOMEvent(cm, e, "contextmenu")) { return } + if (!captureRightClick) { cm.display.input.onContextMenu(e); } + } + + function contextMenuInGutter(cm, e) { + if (!hasHandler(cm, "gutterContextMenu")) { return false } + return gutterEvent(cm, e, "gutterContextMenu", false) + } + + function themeChanged(cm) { + cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") + + cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-"); + clearCaches(cm); + } + + var Init = {toString: function(){return "CodeMirror.Init"}}; + + var defaults = {}; + var optionHandlers = {}; + + function defineOptions(CodeMirror) { + var optionHandlers = CodeMirror.optionHandlers; + + function option(name, deflt, handle, notOnInit) { + CodeMirror.defaults[name] = deflt; + if (handle) { optionHandlers[name] = + notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; } + } + + CodeMirror.defineOption = option; + + // Passed to option handlers when there is no old value. + CodeMirror.Init = Init; + + // These two are, on init, called from the constructor because they + // have to be initialized before the editor can start at all. + option("value", "", function (cm, val) { return cm.setValue(val); }, true); + option("mode", null, function (cm, val) { + cm.doc.modeOption = val; + loadMode(cm); + }, true); + + option("indentUnit", 2, loadMode, true); + option("indentWithTabs", false); + option("smartIndent", true); + option("tabSize", 4, function (cm) { + resetModeState(cm); + clearCaches(cm); + regChange(cm); + }, true); + + option("lineSeparator", null, function (cm, val) { + cm.doc.lineSep = val; + if (!val) { return } + var newBreaks = [], lineNo = cm.doc.first; + cm.doc.iter(function (line) { + for (var pos = 0;;) { + var found = line.text.indexOf(val, pos); + if (found == -1) { break } + pos = found + val.length; + newBreaks.push(Pos(lineNo, found)); + } + lineNo++; + }); + for (var i = newBreaks.length - 1; i >= 0; i--) + { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); } + }); + option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200c\u200e\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g, function (cm, val, old) { + cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g"); + if (old != Init) { cm.refresh(); } + }); + option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true); + option("electricChars", true); + option("inputStyle", mobile ? "contenteditable" : "textarea", function () { + throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME + }, true); + option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true); + option("autocorrect", false, function (cm, val) { return cm.getInputField().autocorrect = val; }, true); + option("autocapitalize", false, function (cm, val) { return cm.getInputField().autocapitalize = val; }, true); + option("rtlMoveVisually", !windows); + option("wholeLineUpdateBefore", true); + + option("theme", "default", function (cm) { + themeChanged(cm); + updateGutters(cm); + }, true); + option("keyMap", "default", function (cm, val, old) { + var next = getKeyMap(val); + var prev = old != Init && getKeyMap(old); + if (prev && prev.detach) { prev.detach(cm, next); } + if (next.attach) { next.attach(cm, prev || null); } + }); + option("extraKeys", null); + option("configureMouse", null); + + option("lineWrapping", false, wrappingChanged, true); + option("gutters", [], function (cm, val) { + cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers); + updateGutters(cm); + }, true); + option("fixedGutter", true, function (cm, val) { + cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"; + cm.refresh(); + }, true); + option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true); + option("scrollbarStyle", "native", function (cm) { + initScrollbars(cm); + updateScrollbars(cm); + cm.display.scrollbars.setScrollTop(cm.doc.scrollTop); + cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft); + }, true); + option("lineNumbers", false, function (cm, val) { + cm.display.gutterSpecs = getGutters(cm.options.gutters, val); + updateGutters(cm); + }, true); + option("firstLineNumber", 1, updateGutters, true); + option("lineNumberFormatter", function (integer) { return integer; }, updateGutters, true); + option("showCursorWhenSelecting", false, updateSelection, true); + + option("resetSelectionOnContextMenu", true); + option("lineWiseCopyCut", true); + option("pasteLinesPerSelection", true); + option("selectionsMayTouch", false); + + option("readOnly", false, function (cm, val) { + if (val == "nocursor") { + onBlur(cm); + cm.display.input.blur(); + } + cm.display.input.readOnlyChanged(val); + }); + + option("screenReaderLabel", null, function (cm, val) { + val = (val === '') ? null : val; + cm.display.input.screenReaderLabelChanged(val); + }); + + option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true); + option("dragDrop", true, dragDropChanged); + option("allowDropFileTypes", null); + + option("cursorBlinkRate", 530); + option("cursorScrollMargin", 0); + option("cursorHeight", 1, updateSelection, true); + option("singleCursorHeightPerLine", true, updateSelection, true); + option("workTime", 100); + option("workDelay", 100); + option("flattenSpans", true, resetModeState, true); + option("addModeClass", false, resetModeState, true); + option("pollInterval", 100); + option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; }); + option("historyEventDelay", 1250); + option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true); + option("maxHighlightLength", 10000, resetModeState, true); + option("moveInputWithCursor", true, function (cm, val) { + if (!val) { cm.display.input.resetPosition(); } + }); + + option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; }); + option("autofocus", null); + option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true); + option("phrases", null); + } + + function dragDropChanged(cm, value, old) { + var wasOn = old && old != Init; + if (!value != !wasOn) { + var funcs = cm.display.dragFunctions; + var toggle = value ? on : off; + toggle(cm.display.scroller, "dragstart", funcs.start); + toggle(cm.display.scroller, "dragenter", funcs.enter); + toggle(cm.display.scroller, "dragover", funcs.over); + toggle(cm.display.scroller, "dragleave", funcs.leave); + toggle(cm.display.scroller, "drop", funcs.drop); + } + } + + function wrappingChanged(cm) { + if (cm.options.lineWrapping) { + addClass(cm.display.wrapper, "CodeMirror-wrap"); + cm.display.sizer.style.minWidth = ""; + cm.display.sizerWidth = null; + } else { + rmClass(cm.display.wrapper, "CodeMirror-wrap"); + findMaxLine(cm); + } + estimateLineHeights(cm); + regChange(cm); + clearCaches(cm); + setTimeout(function () { return updateScrollbars(cm); }, 100); + } + + // A CodeMirror instance represents an editor. This is the object + // that user code is usually dealing with. + + function CodeMirror(place, options) { + var this$1 = this; + + if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) } + + this.options = options = options ? copyObj(options) : {}; + // Determine effective options based on given values and defaults. + copyObj(defaults, options, false); + + var doc = options.value; + if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); } + else if (options.mode) { doc.modeOption = options.mode; } + this.doc = doc; + + var input = new CodeMirror.inputStyles[options.inputStyle](this); + var display = this.display = new Display(place, doc, input, options); + display.wrapper.CodeMirror = this; + themeChanged(this); + if (options.lineWrapping) + { this.display.wrapper.className += " CodeMirror-wrap"; } + initScrollbars(this); + + this.state = { + keyMaps: [], // stores maps added by addKeyMap + overlays: [], // highlighting overlays, as added by addOverlay + modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info + overwrite: false, + delayingBlurEvent: false, + focused: false, + suppressEdits: false, // used to disable editing during key handlers when in readOnly mode + pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll + selectingText: false, + draggingText: false, + highlight: new Delayed(), // stores highlight worker timeout + keySeq: null, // Unfinished key sequence + specialChars: null + }; + + if (options.autofocus && !mobile) { display.input.focus(); } + + // Override magic textarea content restore that IE sometimes does + // on our hidden textarea on reload + if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); } + + registerEventHandlers(this); + ensureGlobalHandlers(); + + startOperation(this); + this.curOp.forceUpdate = true; + attachDoc(this, doc); + + if ((options.autofocus && !mobile) || this.hasFocus()) + { setTimeout(function () { + if (this$1.hasFocus() && !this$1.state.focused) { onFocus(this$1); } + }, 20); } + else + { onBlur(this); } + + for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt)) + { optionHandlers[opt](this, options[opt], Init); } } + maybeUpdateLineNumberWidth(this); + if (options.finishInit) { options.finishInit(this); } + for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this); } + endOperation(this); + // Suppress optimizelegibility in Webkit, since it breaks text + // measuring on line wrapping boundaries. + if (webkit && options.lineWrapping && + getComputedStyle(display.lineDiv).textRendering == "optimizelegibility") + { display.lineDiv.style.textRendering = "auto"; } + } + + // The default configuration options. + CodeMirror.defaults = defaults; + // Functions to run when options are changed. + CodeMirror.optionHandlers = optionHandlers; + + // Attach the necessary event handlers when initializing the editor + function registerEventHandlers(cm) { + var d = cm.display; + on(d.scroller, "mousedown", operation(cm, onMouseDown)); + // Older IE's will not fire a second mousedown for a double click + if (ie && ie_version < 11) + { on(d.scroller, "dblclick", operation(cm, function (e) { + if (signalDOMEvent(cm, e)) { return } + var pos = posFromMouse(cm, e); + if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return } + e_preventDefault(e); + var word = cm.findWordAt(pos); + extendSelection(cm.doc, word.anchor, word.head); + })); } + else + { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); } + // Some browsers fire contextmenu *after* opening the menu, at + // which point we can't mess with it anymore. Context menu is + // handled in onMouseDown for these browsers. + on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }); + on(d.input.getField(), "contextmenu", function (e) { + if (!d.scroller.contains(e.target)) { onContextMenu(cm, e); } + }); + + // Used to suppress mouse event handling when a touch happens + var touchFinished, prevTouch = {end: 0}; + function finishTouch() { + if (d.activeTouch) { + touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000); + prevTouch = d.activeTouch; + prevTouch.end = +new Date; + } + } + function isMouseLikeTouchEvent(e) { + if (e.touches.length != 1) { return false } + var touch = e.touches[0]; + return touch.radiusX <= 1 && touch.radiusY <= 1 + } + function farAway(touch, other) { + if (other.left == null) { return true } + var dx = other.left - touch.left, dy = other.top - touch.top; + return dx * dx + dy * dy > 20 * 20 + } + on(d.scroller, "touchstart", function (e) { + if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) { + d.input.ensurePolled(); + clearTimeout(touchFinished); + var now = +new Date; + d.activeTouch = {start: now, moved: false, + prev: now - prevTouch.end <= 300 ? prevTouch : null}; + if (e.touches.length == 1) { + d.activeTouch.left = e.touches[0].pageX; + d.activeTouch.top = e.touches[0].pageY; + } + } + }); + on(d.scroller, "touchmove", function () { + if (d.activeTouch) { d.activeTouch.moved = true; } + }); + on(d.scroller, "touchend", function (e) { + var touch = d.activeTouch; + if (touch && !eventInWidget(d, e) && touch.left != null && + !touch.moved && new Date - touch.start < 300) { + var pos = cm.coordsChar(d.activeTouch, "page"), range; + if (!touch.prev || farAway(touch, touch.prev)) // Single tap + { range = new Range(pos, pos); } + else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap + { range = cm.findWordAt(pos); } + else // Triple tap + { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); } + cm.setSelection(range.anchor, range.head); + cm.focus(); + e_preventDefault(e); + } + finishTouch(); + }); + on(d.scroller, "touchcancel", finishTouch); + + // Sync scrolling between fake scrollbars and real scrollable + // area, ensure viewport is updated when scrolling. + on(d.scroller, "scroll", function () { + if (d.scroller.clientHeight) { + updateScrollTop(cm, d.scroller.scrollTop); + setScrollLeft(cm, d.scroller.scrollLeft, true); + signal(cm, "scroll", cm); + } + }); + + // Listen to wheel events in order to try and update the viewport on time. + on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); }); + on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); }); + + // Prevent wrapper from ever scrolling + on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); + + d.dragFunctions = { + enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }}, + over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }}, + start: function (e) { return onDragStart(cm, e); }, + drop: operation(cm, onDrop), + leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }} + }; + + var inp = d.input.getField(); + on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); }); + on(inp, "keydown", operation(cm, onKeyDown)); + on(inp, "keypress", operation(cm, onKeyPress)); + on(inp, "focus", function (e) { return onFocus(cm, e); }); + on(inp, "blur", function (e) { return onBlur(cm, e); }); + } + + var initHooks = []; + CodeMirror.defineInitHook = function (f) { return initHooks.push(f); }; + + // Indent the given line. The how parameter can be "smart", + // "add"/null, "subtract", or "prev". When aggressive is false + // (typically set to true for forced single-line indents), empty + // lines are not indented, and places where the mode returns Pass + // are left alone. + function indentLine(cm, n, how, aggressive) { + var doc = cm.doc, state; + if (how == null) { how = "add"; } + if (how == "smart") { + // Fall back to "prev" when the mode doesn't have an indentation + // method. + if (!doc.mode.indent) { how = "prev"; } + else { state = getContextBefore(cm, n).state; } + } + + var tabSize = cm.options.tabSize; + var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize); + if (line.stateAfter) { line.stateAfter = null; } + var curSpaceString = line.text.match(/^\s*/)[0], indentation; + if (!aggressive && !/\S/.test(line.text)) { + indentation = 0; + how = "not"; + } else if (how == "smart") { + indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text); + if (indentation == Pass || indentation > 150) { + if (!aggressive) { return } + how = "prev"; + } + } + if (how == "prev") { + if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); } + else { indentation = 0; } + } else if (how == "add") { + indentation = curSpace + cm.options.indentUnit; + } else if (how == "subtract") { + indentation = curSpace - cm.options.indentUnit; + } else if (typeof how == "number") { + indentation = curSpace + how; + } + indentation = Math.max(0, indentation); + + var indentString = "", pos = 0; + if (cm.options.indentWithTabs) + { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} } + if (pos < indentation) { indentString += spaceStr(indentation - pos); } + + if (indentString != curSpaceString) { + replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); + line.stateAfter = null; + return true + } else { + // Ensure that, if the cursor was in the whitespace at the start + // of the line, it is moved to the end of that space. + for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) { + var range = doc.sel.ranges[i$1]; + if (range.head.line == n && range.head.ch < curSpaceString.length) { + var pos$1 = Pos(n, curSpaceString.length); + replaceOneSelection(doc, i$1, new Range(pos$1, pos$1)); + break + } + } + } + } + + // This will be set to a {lineWise: bool, text: [string]} object, so + // that, when pasting, we know what kind of selections the copied + // text was made out of. + var lastCopied = null; + + function setLastCopied(newLastCopied) { + lastCopied = newLastCopied; + } + + function applyTextInput(cm, inserted, deleted, sel, origin) { + var doc = cm.doc; + cm.display.shift = false; + if (!sel) { sel = doc.sel; } + + var recent = +new Date - 200; + var paste = origin == "paste" || cm.state.pasteIncoming > recent; + var textLines = splitLinesAuto(inserted), multiPaste = null; + // When pasting N lines into N selections, insert one line per selection + if (paste && sel.ranges.length > 1) { + if (lastCopied && lastCopied.text.join("\n") == inserted) { + if (sel.ranges.length % lastCopied.text.length == 0) { + multiPaste = []; + for (var i = 0; i < lastCopied.text.length; i++) + { multiPaste.push(doc.splitLines(lastCopied.text[i])); } + } + } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) { + multiPaste = map(textLines, function (l) { return [l]; }); + } + } + + var updateInput = cm.curOp.updateInput; + // Normal behavior is to insert the new text into every selection + for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) { + var range = sel.ranges[i$1]; + var from = range.from(), to = range.to(); + if (range.empty()) { + if (deleted && deleted > 0) // Handle deletion + { from = Pos(from.line, from.ch - deleted); } + else if (cm.state.overwrite && !paste) // Handle overwrite + { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); } + else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == textLines.join("\n")) + { from = to = Pos(from.line, 0); } + } + var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines, + origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")}; + makeChange(cm.doc, changeEvent); + signalLater(cm, "inputRead", cm, changeEvent); + } + if (inserted && !paste) + { triggerElectric(cm, inserted); } + + ensureCursorVisible(cm); + if (cm.curOp.updateInput < 2) { cm.curOp.updateInput = updateInput; } + cm.curOp.typing = true; + cm.state.pasteIncoming = cm.state.cutIncoming = -1; + } + + function handlePaste(e, cm) { + var pasted = e.clipboardData && e.clipboardData.getData("Text"); + if (pasted) { + e.preventDefault(); + if (!cm.isReadOnly() && !cm.options.disableInput) + { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); } + return true + } + } + + function triggerElectric(cm, inserted) { + // When an 'electric' character is inserted, immediately trigger a reindent + if (!cm.options.electricChars || !cm.options.smartIndent) { return } + var sel = cm.doc.sel; + + for (var i = sel.ranges.length - 1; i >= 0; i--) { + var range = sel.ranges[i]; + if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) { continue } + var mode = cm.getModeAt(range.head); + var indented = false; + if (mode.electricChars) { + for (var j = 0; j < mode.electricChars.length; j++) + { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) { + indented = indentLine(cm, range.head.line, "smart"); + break + } } + } else if (mode.electricInput) { + if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch))) + { indented = indentLine(cm, range.head.line, "smart"); } + } + if (indented) { signalLater(cm, "electricInput", cm, range.head.line); } + } + } + + function copyableRanges(cm) { + var text = [], ranges = []; + for (var i = 0; i < cm.doc.sel.ranges.length; i++) { + var line = cm.doc.sel.ranges[i].head.line; + var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}; + ranges.push(lineRange); + text.push(cm.getRange(lineRange.anchor, lineRange.head)); + } + return {text: text, ranges: ranges} + } + + function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) { + field.setAttribute("autocorrect", autocorrect ? "" : "off"); + field.setAttribute("autocapitalize", autocapitalize ? "" : "off"); + field.setAttribute("spellcheck", !!spellcheck); + } + + function hiddenTextarea() { + var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none"); + var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); + // The textarea is kept positioned near the cursor to prevent the + // fact that it'll be scrolled into view on input from scrolling + // our fake cursor out of view. On webkit, when wrap=off, paste is + // very slow. So make the area wide instead. + if (webkit) { te.style.width = "1000px"; } + else { te.setAttribute("wrap", "off"); } + // If border: 0; -- iOS fails to open keyboard (issue #1287) + if (ios) { te.style.border = "1px solid black"; } + disableBrowserMagic(te); + return div + } + + // The publicly visible API. Note that methodOp(f) means + // 'wrap f in an operation, performed on its `this` parameter'. + + // This is not the complete set of editor methods. Most of the + // methods defined on the Doc type are also injected into + // CodeMirror.prototype, for backwards compatibility and + // convenience. + + function addEditorMethods(CodeMirror) { + var optionHandlers = CodeMirror.optionHandlers; + + var helpers = CodeMirror.helpers = {}; + + CodeMirror.prototype = { + constructor: CodeMirror, + focus: function(){window.focus(); this.display.input.focus();}, + + setOption: function(option, value) { + var options = this.options, old = options[option]; + if (options[option] == value && option != "mode") { return } + options[option] = value; + if (optionHandlers.hasOwnProperty(option)) + { operation(this, optionHandlers[option])(this, value, old); } + signal(this, "optionChange", this, option); + }, + + getOption: function(option) {return this.options[option]}, + getDoc: function() {return this.doc}, + + addKeyMap: function(map, bottom) { + this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map)); + }, + removeKeyMap: function(map) { + var maps = this.state.keyMaps; + for (var i = 0; i < maps.length; ++i) + { if (maps[i] == map || maps[i].name == map) { + maps.splice(i, 1); + return true + } } + }, + + addOverlay: methodOp(function(spec, options) { + var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec); + if (mode.startState) { throw new Error("Overlays may not be stateful.") } + insertSorted(this.state.overlays, + {mode: mode, modeSpec: spec, opaque: options && options.opaque, + priority: (options && options.priority) || 0}, + function (overlay) { return overlay.priority; }); + this.state.modeGen++; + regChange(this); + }), + removeOverlay: methodOp(function(spec) { + var overlays = this.state.overlays; + for (var i = 0; i < overlays.length; ++i) { + var cur = overlays[i].modeSpec; + if (cur == spec || typeof spec == "string" && cur.name == spec) { + overlays.splice(i, 1); + this.state.modeGen++; + regChange(this); + return + } + } + }), + + indentLine: methodOp(function(n, dir, aggressive) { + if (typeof dir != "string" && typeof dir != "number") { + if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; } + else { dir = dir ? "add" : "subtract"; } + } + if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); } + }), + indentSelection: methodOp(function(how) { + var ranges = this.doc.sel.ranges, end = -1; + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i]; + if (!range.empty()) { + var from = range.from(), to = range.to(); + var start = Math.max(end, from.line); + end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; + for (var j = start; j < end; ++j) + { indentLine(this, j, how); } + var newRanges = this.doc.sel.ranges; + if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0) + { replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); } + } else if (range.head.line > end) { + indentLine(this, range.head.line, how, true); + end = range.head.line; + if (i == this.doc.sel.primIndex) { ensureCursorVisible(this); } + } + } + }), + + // Fetch the parser token for a given character. Useful for hacks + // that want to inspect the mode state (say, for completion). + getTokenAt: function(pos, precise) { + return takeToken(this, pos, precise) + }, + + getLineTokens: function(line, precise) { + return takeToken(this, Pos(line), precise, true) + }, + + getTokenTypeAt: function(pos) { + pos = clipPos(this.doc, pos); + var styles = getLineStyles(this, getLine(this.doc, pos.line)); + var before = 0, after = (styles.length - 1) / 2, ch = pos.ch; + var type; + if (ch == 0) { type = styles[2]; } + else { for (;;) { + var mid = (before + after) >> 1; + if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; } + else if (styles[mid * 2 + 1] < ch) { before = mid + 1; } + else { type = styles[mid * 2 + 2]; break } + } } + var cut = type ? type.indexOf("overlay ") : -1; + return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1) + }, + + getModeAt: function(pos) { + var mode = this.doc.mode; + if (!mode.innerMode) { return mode } + return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode + }, + + getHelper: function(pos, type) { + return this.getHelpers(pos, type)[0] + }, + + getHelpers: function(pos, type) { + var found = []; + if (!helpers.hasOwnProperty(type)) { return found } + var help = helpers[type], mode = this.getModeAt(pos); + if (typeof mode[type] == "string") { + if (help[mode[type]]) { found.push(help[mode[type]]); } + } else if (mode[type]) { + for (var i = 0; i < mode[type].length; i++) { + var val = help[mode[type][i]]; + if (val) { found.push(val); } + } + } else if (mode.helperType && help[mode.helperType]) { + found.push(help[mode.helperType]); + } else if (help[mode.name]) { + found.push(help[mode.name]); + } + for (var i$1 = 0; i$1 < help._global.length; i$1++) { + var cur = help._global[i$1]; + if (cur.pred(mode, this) && indexOf(found, cur.val) == -1) + { found.push(cur.val); } + } + return found + }, + + getStateAfter: function(line, precise) { + var doc = this.doc; + line = clipLine(doc, line == null ? doc.first + doc.size - 1: line); + return getContextBefore(this, line + 1, precise).state + }, + + cursorCoords: function(start, mode) { + var pos, range = this.doc.sel.primary(); + if (start == null) { pos = range.head; } + else if (typeof start == "object") { pos = clipPos(this.doc, start); } + else { pos = start ? range.from() : range.to(); } + return cursorCoords(this, pos, mode || "page") + }, + + charCoords: function(pos, mode) { + return charCoords(this, clipPos(this.doc, pos), mode || "page") + }, + + coordsChar: function(coords, mode) { + coords = fromCoordSystem(this, coords, mode || "page"); + return coordsChar(this, coords.left, coords.top) + }, + + lineAtHeight: function(height, mode) { + height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top; + return lineAtHeight(this.doc, height + this.display.viewOffset) + }, + heightAtLine: function(line, mode, includeWidgets) { + var end = false, lineObj; + if (typeof line == "number") { + var last = this.doc.first + this.doc.size - 1; + if (line < this.doc.first) { line = this.doc.first; } + else if (line > last) { line = last; end = true; } + lineObj = getLine(this.doc, line); + } else { + lineObj = line; + } + return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top + + (end ? this.doc.height - heightAtLine(lineObj) : 0) + }, + + defaultTextHeight: function() { return textHeight(this.display) }, + defaultCharWidth: function() { return charWidth(this.display) }, + + getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}}, + + addWidget: function(pos, node, scroll, vert, horiz) { + var display = this.display; + pos = cursorCoords(this, clipPos(this.doc, pos)); + var top = pos.bottom, left = pos.left; + node.style.position = "absolute"; + node.setAttribute("cm-ignore-events", "true"); + this.display.input.setUneditable(node); + display.sizer.appendChild(node); + if (vert == "over") { + top = pos.top; + } else if (vert == "above" || vert == "near") { + var vspace = Math.max(display.wrapper.clientHeight, this.doc.height), + hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth); + // Default to positioning above (if specified and possible); otherwise default to positioning below + if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight) + { top = pos.top - node.offsetHeight; } + else if (pos.bottom + node.offsetHeight <= vspace) + { top = pos.bottom; } + if (left + node.offsetWidth > hspace) + { left = hspace - node.offsetWidth; } + } + node.style.top = top + "px"; + node.style.left = node.style.right = ""; + if (horiz == "right") { + left = display.sizer.clientWidth - node.offsetWidth; + node.style.right = "0px"; + } else { + if (horiz == "left") { left = 0; } + else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; } + node.style.left = left + "px"; + } + if (scroll) + { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); } + }, + + triggerOnKeyDown: methodOp(onKeyDown), + triggerOnKeyPress: methodOp(onKeyPress), + triggerOnKeyUp: onKeyUp, + triggerOnMouseDown: methodOp(onMouseDown), + + execCommand: function(cmd) { + if (commands.hasOwnProperty(cmd)) + { return commands[cmd].call(null, this) } + }, + + triggerElectric: methodOp(function(text) { triggerElectric(this, text); }), + + findPosH: function(from, amount, unit, visually) { + var dir = 1; + if (amount < 0) { dir = -1; amount = -amount; } + var cur = clipPos(this.doc, from); + for (var i = 0; i < amount; ++i) { + cur = findPosH(this.doc, cur, dir, unit, visually); + if (cur.hitSide) { break } + } + return cur + }, + + moveH: methodOp(function(dir, unit) { + var this$1 = this; + + this.extendSelectionsBy(function (range) { + if (this$1.display.shift || this$1.doc.extend || range.empty()) + { return findPosH(this$1.doc, range.head, dir, unit, this$1.options.rtlMoveVisually) } + else + { return dir < 0 ? range.from() : range.to() } + }, sel_move); + }), + + deleteH: methodOp(function(dir, unit) { + var sel = this.doc.sel, doc = this.doc; + if (sel.somethingSelected()) + { doc.replaceSelection("", null, "+delete"); } + else + { deleteNearSelection(this, function (range) { + var other = findPosH(doc, range.head, dir, unit, false); + return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other} + }); } + }), + + findPosV: function(from, amount, unit, goalColumn) { + var dir = 1, x = goalColumn; + if (amount < 0) { dir = -1; amount = -amount; } + var cur = clipPos(this.doc, from); + for (var i = 0; i < amount; ++i) { + var coords = cursorCoords(this, cur, "div"); + if (x == null) { x = coords.left; } + else { coords.left = x; } + cur = findPosV(this, coords, dir, unit); + if (cur.hitSide) { break } + } + return cur + }, + + moveV: methodOp(function(dir, unit) { + var this$1 = this; + + var doc = this.doc, goals = []; + var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected(); + doc.extendSelectionsBy(function (range) { + if (collapse) + { return dir < 0 ? range.from() : range.to() } + var headPos = cursorCoords(this$1, range.head, "div"); + if (range.goalColumn != null) { headPos.left = range.goalColumn; } + goals.push(headPos.left); + var pos = findPosV(this$1, headPos, dir, unit); + if (unit == "page" && range == doc.sel.primary()) + { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); } + return pos + }, sel_move); + if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++) + { doc.sel.ranges[i].goalColumn = goals[i]; } } + }), + + // Find the word at the given position (as returned by coordsChar). + findWordAt: function(pos) { + var doc = this.doc, line = getLine(doc, pos.line).text; + var start = pos.ch, end = pos.ch; + if (line) { + var helper = this.getHelper(pos, "wordChars"); + if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; } + var startChar = line.charAt(start); + var check = isWordChar(startChar, helper) + ? function (ch) { return isWordChar(ch, helper); } + : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); } + : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); }; + while (start > 0 && check(line.charAt(start - 1))) { --start; } + while (end < line.length && check(line.charAt(end))) { ++end; } + } + return new Range(Pos(pos.line, start), Pos(pos.line, end)) + }, + + toggleOverwrite: function(value) { + if (value != null && value == this.state.overwrite) { return } + if (this.state.overwrite = !this.state.overwrite) + { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); } + else + { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); } + + signal(this, "overwriteToggle", this, this.state.overwrite); + }, + hasFocus: function() { return this.display.input.getField() == activeElt() }, + isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) }, + + scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }), + getScrollInfo: function() { + var scroller = this.display.scroller; + return {left: scroller.scrollLeft, top: scroller.scrollTop, + height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight, + width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth, + clientHeight: displayHeight(this), clientWidth: displayWidth(this)} + }, + + scrollIntoView: methodOp(function(range, margin) { + if (range == null) { + range = {from: this.doc.sel.primary().head, to: null}; + if (margin == null) { margin = this.options.cursorScrollMargin; } + } else if (typeof range == "number") { + range = {from: Pos(range, 0), to: null}; + } else if (range.from == null) { + range = {from: range, to: null}; + } + if (!range.to) { range.to = range.from; } + range.margin = margin || 0; + + if (range.from.line != null) { + scrollToRange(this, range); + } else { + scrollToCoordsRange(this, range.from, range.to, range.margin); + } + }), + + setSize: methodOp(function(width, height) { + var this$1 = this; + + var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; }; + if (width != null) { this.display.wrapper.style.width = interpret(width); } + if (height != null) { this.display.wrapper.style.height = interpret(height); } + if (this.options.lineWrapping) { clearLineMeasurementCache(this); } + var lineNo = this.display.viewFrom; + this.doc.iter(lineNo, this.display.viewTo, function (line) { + if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) + { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo, "widget"); break } } } + ++lineNo; + }); + this.curOp.forceUpdate = true; + signal(this, "refresh", this); + }), + + operation: function(f){return runInOp(this, f)}, + startOperation: function(){return startOperation(this)}, + endOperation: function(){return endOperation(this)}, + + refresh: methodOp(function() { + var oldHeight = this.display.cachedTextHeight; + regChange(this); + this.curOp.forceUpdate = true; + clearCaches(this); + scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop); + updateGutterSpace(this.display); + if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5 || this.options.lineWrapping) + { estimateLineHeights(this); } + signal(this, "refresh", this); + }), + + swapDoc: methodOp(function(doc) { + var old = this.doc; + old.cm = null; + // Cancel the current text selection if any (#5821) + if (this.state.selectingText) { this.state.selectingText(); } + attachDoc(this, doc); + clearCaches(this); + this.display.input.reset(); + scrollToCoords(this, doc.scrollLeft, doc.scrollTop); + this.curOp.forceScroll = true; + signalLater(this, "swapDoc", this, old); + return old + }), + + phrase: function(phraseText) { + var phrases = this.options.phrases; + return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText + }, + + getInputField: function(){return this.display.input.getField()}, + getWrapperElement: function(){return this.display.wrapper}, + getScrollerElement: function(){return this.display.scroller}, + getGutterElement: function(){return this.display.gutters} + }; + eventMixin(CodeMirror); + + CodeMirror.registerHelper = function(type, name, value) { + if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; } + helpers[type][name] = value; + }; + CodeMirror.registerGlobalHelper = function(type, name, predicate, value) { + CodeMirror.registerHelper(type, name, value); + helpers[type]._global.push({pred: predicate, val: value}); + }; + } + + // Used for horizontal relative motion. Dir is -1 or 1 (left or + // right), unit can be "codepoint", "char", "column" (like char, but + // doesn't cross line boundaries), "word" (across next word), or + // "group" (to the start of next group of word or + // non-word-non-whitespace chars). The visually param controls + // whether, in right-to-left text, direction 1 means to move towards + // the next index in the string, or towards the character to the right + // of the current position. The resulting position will have a + // hitSide=true property if it reached the end of the document. + function findPosH(doc, pos, dir, unit, visually) { + var oldPos = pos; + var origDir = dir; + var lineObj = getLine(doc, pos.line); + var lineDir = visually && doc.direction == "rtl" ? -dir : dir; + function findNextLine() { + var l = pos.line + lineDir; + if (l < doc.first || l >= doc.first + doc.size) { return false } + pos = new Pos(l, pos.ch, pos.sticky); + return lineObj = getLine(doc, l) + } + function moveOnce(boundToLine) { + var next; + if (unit == "codepoint") { + var ch = lineObj.text.charCodeAt(pos.ch + (unit > 0 ? 0 : -1)); + if (isNaN(ch)) { next = null; } + else { next = new Pos(pos.line, Math.max(0, Math.min(lineObj.text.length, pos.ch + dir * (ch >= 0xD800 && ch < 0xDC00 ? 2 : 1))), + -dir); } + } else if (visually) { + next = moveVisually(doc.cm, lineObj, pos, dir); + } else { + next = moveLogically(lineObj, pos, dir); + } + if (next == null) { + if (!boundToLine && findNextLine()) + { pos = endOfLine(visually, doc.cm, lineObj, pos.line, lineDir); } + else + { return false } + } else { + pos = next; + } + return true + } + + if (unit == "char" || unit == "codepoint") { + moveOnce(); + } else if (unit == "column") { + moveOnce(true); + } else if (unit == "word" || unit == "group") { + var sawType = null, group = unit == "group"; + var helper = doc.cm && doc.cm.getHelper(pos, "wordChars"); + for (var first = true;; first = false) { + if (dir < 0 && !moveOnce(!first)) { break } + var cur = lineObj.text.charAt(pos.ch) || "\n"; + var type = isWordChar(cur, helper) ? "w" + : group && cur == "\n" ? "n" + : !group || /\s/.test(cur) ? null + : "p"; + if (group && !first && !type) { type = "s"; } + if (sawType && sawType != type) { + if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";} + break + } + + if (type) { sawType = type; } + if (dir > 0 && !moveOnce(!first)) { break } + } + } + var result = skipAtomic(doc, pos, oldPos, origDir, true); + if (equalCursorPos(oldPos, result)) { result.hitSide = true; } + return result + } + + // For relative vertical movement. Dir may be -1 or 1. Unit can be + // "page" or "line". The resulting position will have a hitSide=true + // property if it reached the end of the document. + function findPosV(cm, pos, dir, unit) { + var doc = cm.doc, x = pos.left, y; + if (unit == "page") { + var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight); + var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3); + y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount; + } else if (unit == "line") { + y = dir > 0 ? pos.bottom + 3 : pos.top - 3; + } + var target; + for (;;) { + target = coordsChar(cm, x, y); + if (!target.outside) { break } + if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break } + y += dir * 5; + } + return target + } + + // CONTENTEDITABLE INPUT STYLE + + var ContentEditableInput = function(cm) { + this.cm = cm; + this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null; + this.polling = new Delayed(); + this.composing = null; + this.gracePeriod = false; + this.readDOMTimeout = null; + }; + + ContentEditableInput.prototype.init = function (display) { + var this$1 = this; + + var input = this, cm = input.cm; + var div = input.div = display.lineDiv; + disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize); + + function belongsToInput(e) { + for (var t = e.target; t; t = t.parentNode) { + if (t == div) { return true } + if (/\bCodeMirror-(?:line)?widget\b/.test(t.className)) { break } + } + return false + } + + on(div, "paste", function (e) { + if (!belongsToInput(e) || signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } + // IE doesn't fire input events, so we schedule a read for the pasted content in this way + if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); } + }); + + on(div, "compositionstart", function (e) { + this$1.composing = {data: e.data, done: false}; + }); + on(div, "compositionupdate", function (e) { + if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; } + }); + on(div, "compositionend", function (e) { + if (this$1.composing) { + if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); } + this$1.composing.done = true; + } + }); + + on(div, "touchstart", function () { return input.forceCompositionEnd(); }); + + on(div, "input", function () { + if (!this$1.composing) { this$1.readFromDOMSoon(); } + }); + + function onCopyCut(e) { + if (!belongsToInput(e) || signalDOMEvent(cm, e)) { return } + if (cm.somethingSelected()) { + setLastCopied({lineWise: false, text: cm.getSelections()}); + if (e.type == "cut") { cm.replaceSelection("", null, "cut"); } + } else if (!cm.options.lineWiseCopyCut) { + return + } else { + var ranges = copyableRanges(cm); + setLastCopied({lineWise: true, text: ranges.text}); + if (e.type == "cut") { + cm.operation(function () { + cm.setSelections(ranges.ranges, 0, sel_dontScroll); + cm.replaceSelection("", null, "cut"); + }); + } + } + if (e.clipboardData) { + e.clipboardData.clearData(); + var content = lastCopied.text.join("\n"); + // iOS exposes the clipboard API, but seems to discard content inserted into it + e.clipboardData.setData("Text", content); + if (e.clipboardData.getData("Text") == content) { + e.preventDefault(); + return + } + } + // Old-fashioned briefly-focus-a-textarea hack + var kludge = hiddenTextarea(), te = kludge.firstChild; + cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild); + te.value = lastCopied.text.join("\n"); + var hadFocus = document.activeElement; + selectInput(te); + setTimeout(function () { + cm.display.lineSpace.removeChild(kludge); + hadFocus.focus(); + if (hadFocus == div) { input.showPrimarySelection(); } + }, 50); + } + on(div, "copy", onCopyCut); + on(div, "cut", onCopyCut); + }; + + ContentEditableInput.prototype.screenReaderLabelChanged = function (label) { + // Label for screenreaders, accessibility + if(label) { + this.div.setAttribute('aria-label', label); + } else { + this.div.removeAttribute('aria-label'); + } + }; + + ContentEditableInput.prototype.prepareSelection = function () { + var result = prepareSelection(this.cm, false); + result.focus = document.activeElement == this.div; + return result + }; + + ContentEditableInput.prototype.showSelection = function (info, takeFocus) { + if (!info || !this.cm.display.view.length) { return } + if (info.focus || takeFocus) { this.showPrimarySelection(); } + this.showMultipleSelections(info); + }; + + ContentEditableInput.prototype.getSelection = function () { + return this.cm.display.wrapper.ownerDocument.getSelection() + }; + + ContentEditableInput.prototype.showPrimarySelection = function () { + var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary(); + var from = prim.from(), to = prim.to(); + + if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) { + sel.removeAllRanges(); + return + } + + var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); + var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset); + if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad && + cmp(minPos(curAnchor, curFocus), from) == 0 && + cmp(maxPos(curAnchor, curFocus), to) == 0) + { return } + + var view = cm.display.view; + var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) || + {node: view[0].measure.map[2], offset: 0}; + var end = to.line < cm.display.viewTo && posToDOM(cm, to); + if (!end) { + var measure = view[view.length - 1].measure; + var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map; + end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]}; + } + + if (!start || !end) { + sel.removeAllRanges(); + return + } + + var old = sel.rangeCount && sel.getRangeAt(0), rng; + try { rng = range(start.node, start.offset, end.offset, end.node); } + catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible + if (rng) { + if (!gecko && cm.state.focused) { + sel.collapse(start.node, start.offset); + if (!rng.collapsed) { + sel.removeAllRanges(); + sel.addRange(rng); + } + } else { + sel.removeAllRanges(); + sel.addRange(rng); + } + if (old && sel.anchorNode == null) { sel.addRange(old); } + else if (gecko) { this.startGracePeriod(); } + } + this.rememberSelection(); + }; + + ContentEditableInput.prototype.startGracePeriod = function () { + var this$1 = this; + + clearTimeout(this.gracePeriod); + this.gracePeriod = setTimeout(function () { + this$1.gracePeriod = false; + if (this$1.selectionChanged()) + { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); } + }, 20); + }; + + ContentEditableInput.prototype.showMultipleSelections = function (info) { + removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors); + removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection); + }; + + ContentEditableInput.prototype.rememberSelection = function () { + var sel = this.getSelection(); + this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset; + this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset; + }; + + ContentEditableInput.prototype.selectionInEditor = function () { + var sel = this.getSelection(); + if (!sel.rangeCount) { return false } + var node = sel.getRangeAt(0).commonAncestorContainer; + return contains(this.div, node) + }; + + ContentEditableInput.prototype.focus = function () { + if (this.cm.options.readOnly != "nocursor") { + if (!this.selectionInEditor() || document.activeElement != this.div) + { this.showSelection(this.prepareSelection(), true); } + this.div.focus(); + } + }; + ContentEditableInput.prototype.blur = function () { this.div.blur(); }; + ContentEditableInput.prototype.getField = function () { return this.div }; + + ContentEditableInput.prototype.supportsTouch = function () { return true }; + + ContentEditableInput.prototype.receivedFocus = function () { + var input = this; + if (this.selectionInEditor()) + { this.pollSelection(); } + else + { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); } + + function poll() { + if (input.cm.state.focused) { + input.pollSelection(); + input.polling.set(input.cm.options.pollInterval, poll); + } + } + this.polling.set(this.cm.options.pollInterval, poll); + }; + + ContentEditableInput.prototype.selectionChanged = function () { + var sel = this.getSelection(); + return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset || + sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset + }; + + ContentEditableInput.prototype.pollSelection = function () { + if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return } + var sel = this.getSelection(), cm = this.cm; + // On Android Chrome (version 56, at least), backspacing into an + // uneditable block element will put the cursor in that element, + // and then, because it's not editable, hide the virtual keyboard. + // Because Android doesn't allow us to actually detect backspace + // presses in a sane way, this code checks for when that happens + // and simulates a backspace press in this case. + if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) { + this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs}); + this.blur(); + this.focus(); + return + } + if (this.composing) { return } + this.rememberSelection(); + var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); + var head = domToPos(cm, sel.focusNode, sel.focusOffset); + if (anchor && head) { runInOp(cm, function () { + setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll); + if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; } + }); } + }; + + ContentEditableInput.prototype.pollContent = function () { + if (this.readDOMTimeout != null) { + clearTimeout(this.readDOMTimeout); + this.readDOMTimeout = null; + } + + var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary(); + var from = sel.from(), to = sel.to(); + if (from.ch == 0 && from.line > cm.firstLine()) + { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); } + if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine()) + { to = Pos(to.line + 1, 0); } + if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false } + + var fromIndex, fromLine, fromNode; + if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) { + fromLine = lineNo(display.view[0].line); + fromNode = display.view[0].node; + } else { + fromLine = lineNo(display.view[fromIndex].line); + fromNode = display.view[fromIndex - 1].node.nextSibling; + } + var toIndex = findViewIndex(cm, to.line); + var toLine, toNode; + if (toIndex == display.view.length - 1) { + toLine = display.viewTo - 1; + toNode = display.lineDiv.lastChild; + } else { + toLine = lineNo(display.view[toIndex + 1].line) - 1; + toNode = display.view[toIndex + 1].node.previousSibling; + } + + if (!fromNode) { return false } + var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine)); + var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length)); + while (newText.length > 1 && oldText.length > 1) { + if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; } + else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; } + else { break } + } + + var cutFront = 0, cutEnd = 0; + var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length); + while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront)) + { ++cutFront; } + var newBot = lst(newText), oldBot = lst(oldText); + var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0), + oldBot.length - (oldText.length == 1 ? cutFront : 0)); + while (cutEnd < maxCutEnd && + newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) + { ++cutEnd; } + // Try to move start of change to start of selection if ambiguous + if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) { + while (cutFront && cutFront > from.ch && + newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) { + cutFront--; + cutEnd++; + } + } + + newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, ""); + newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, ""); + + var chFrom = Pos(fromLine, cutFront); + var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0); + if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) { + replaceRange(cm.doc, newText, chFrom, chTo, "+input"); + return true + } + }; + + ContentEditableInput.prototype.ensurePolled = function () { + this.forceCompositionEnd(); + }; + ContentEditableInput.prototype.reset = function () { + this.forceCompositionEnd(); + }; + ContentEditableInput.prototype.forceCompositionEnd = function () { + if (!this.composing) { return } + clearTimeout(this.readDOMTimeout); + this.composing = null; + this.updateFromDOM(); + this.div.blur(); + this.div.focus(); + }; + ContentEditableInput.prototype.readFromDOMSoon = function () { + var this$1 = this; + + if (this.readDOMTimeout != null) { return } + this.readDOMTimeout = setTimeout(function () { + this$1.readDOMTimeout = null; + if (this$1.composing) { + if (this$1.composing.done) { this$1.composing = null; } + else { return } + } + this$1.updateFromDOM(); + }, 80); + }; + + ContentEditableInput.prototype.updateFromDOM = function () { + var this$1 = this; + + if (this.cm.isReadOnly() || !this.pollContent()) + { runInOp(this.cm, function () { return regChange(this$1.cm); }); } + }; + + ContentEditableInput.prototype.setUneditable = function (node) { + node.contentEditable = "false"; + }; + + ContentEditableInput.prototype.onKeyPress = function (e) { + if (e.charCode == 0 || this.composing) { return } + e.preventDefault(); + if (!this.cm.isReadOnly()) + { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); } + }; + + ContentEditableInput.prototype.readOnlyChanged = function (val) { + this.div.contentEditable = String(val != "nocursor"); + }; + + ContentEditableInput.prototype.onContextMenu = function () {}; + ContentEditableInput.prototype.resetPosition = function () {}; + + ContentEditableInput.prototype.needsContentAttribute = true; + + function posToDOM(cm, pos) { + var view = findViewForLine(cm, pos.line); + if (!view || view.hidden) { return null } + var line = getLine(cm.doc, pos.line); + var info = mapFromLineView(view, line, pos.line); + + var order = getOrder(line, cm.doc.direction), side = "left"; + if (order) { + var partPos = getBidiPartAt(order, pos.ch); + side = partPos % 2 ? "right" : "left"; + } + var result = nodeAndOffsetInLineMap(info.map, pos.ch, side); + result.offset = result.collapse == "right" ? result.end : result.start; + return result + } + + function isInGutter(node) { + for (var scan = node; scan; scan = scan.parentNode) + { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } } + return false + } + + function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos } + + function domTextBetween(cm, from, to, fromLine, toLine) { + var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false; + function recognizeMarker(id) { return function (marker) { return marker.id == id; } } + function close() { + if (closing) { + text += lineSep; + if (extraLinebreak) { text += lineSep; } + closing = extraLinebreak = false; + } + } + function addText(str) { + if (str) { + close(); + text += str; + } + } + function walk(node) { + if (node.nodeType == 1) { + var cmText = node.getAttribute("cm-text"); + if (cmText) { + addText(cmText); + return + } + var markerID = node.getAttribute("cm-marker"), range; + if (markerID) { + var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID)); + if (found.length && (range = found[0].find(0))) + { addText(getBetween(cm.doc, range.from, range.to).join(lineSep)); } + return + } + if (node.getAttribute("contenteditable") == "false") { return } + var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName); + if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return } + + if (isBlock) { close(); } + for (var i = 0; i < node.childNodes.length; i++) + { walk(node.childNodes[i]); } + + if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; } + if (isBlock) { closing = true; } + } else if (node.nodeType == 3) { + addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " ")); + } + } + for (;;) { + walk(from); + if (from == to) { break } + from = from.nextSibling; + extraLinebreak = false; + } + return text + } + + function domToPos(cm, node, offset) { + var lineNode; + if (node == cm.display.lineDiv) { + lineNode = cm.display.lineDiv.childNodes[offset]; + if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) } + node = null; offset = 0; + } else { + for (lineNode = node;; lineNode = lineNode.parentNode) { + if (!lineNode || lineNode == cm.display.lineDiv) { return null } + if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break } + } + } + for (var i = 0; i < cm.display.view.length; i++) { + var lineView = cm.display.view[i]; + if (lineView.node == lineNode) + { return locateNodeInLineView(lineView, node, offset) } + } + } + + function locateNodeInLineView(lineView, node, offset) { + var wrapper = lineView.text.firstChild, bad = false; + if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) } + if (node == wrapper) { + bad = true; + node = wrapper.childNodes[offset]; + offset = 0; + if (!node) { + var line = lineView.rest ? lst(lineView.rest) : lineView.line; + return badPos(Pos(lineNo(line), line.text.length), bad) + } + } + + var textNode = node.nodeType == 3 ? node : null, topNode = node; + if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) { + textNode = node.firstChild; + if (offset) { offset = textNode.nodeValue.length; } + } + while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; } + var measure = lineView.measure, maps = measure.maps; + + function find(textNode, topNode, offset) { + for (var i = -1; i < (maps ? maps.length : 0); i++) { + var map = i < 0 ? measure.map : maps[i]; + for (var j = 0; j < map.length; j += 3) { + var curNode = map[j + 2]; + if (curNode == textNode || curNode == topNode) { + var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]); + var ch = map[j] + offset; + if (offset < 0 || curNode != textNode) { ch = map[j + (offset ? 1 : 0)]; } + return Pos(line, ch) + } + } + } + } + var found = find(textNode, topNode, offset); + if (found) { return badPos(found, bad) } + + // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems + for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) { + found = find(after, after.firstChild, 0); + if (found) + { return badPos(Pos(found.line, found.ch - dist), bad) } + else + { dist += after.textContent.length; } + } + for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) { + found = find(before, before.firstChild, -1); + if (found) + { return badPos(Pos(found.line, found.ch + dist$1), bad) } + else + { dist$1 += before.textContent.length; } + } + } + + // TEXTAREA INPUT STYLE + + var TextareaInput = function(cm) { + this.cm = cm; + // See input.poll and input.reset + this.prevInput = ""; + + // Flag that indicates whether we expect input to appear real soon + // now (after some event like 'keypress' or 'input') and are + // polling intensively. + this.pollingFast = false; + // Self-resetting timeout for the poller + this.polling = new Delayed(); + // Used to work around IE issue with selection being forgotten when focus moves away from textarea + this.hasSelection = false; + this.composing = null; + }; + + TextareaInput.prototype.init = function (display) { + var this$1 = this; + + var input = this, cm = this.cm; + this.createField(display); + var te = this.textarea; + + display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild); + + // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore) + if (ios) { te.style.width = "0px"; } + + on(te, "input", function () { + if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; } + input.poll(); + }); + + on(te, "paste", function (e) { + if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } + + cm.state.pasteIncoming = +new Date; + input.fastPoll(); + }); + + function prepareCopyCut(e) { + if (signalDOMEvent(cm, e)) { return } + if (cm.somethingSelected()) { + setLastCopied({lineWise: false, text: cm.getSelections()}); + } else if (!cm.options.lineWiseCopyCut) { + return + } else { + var ranges = copyableRanges(cm); + setLastCopied({lineWise: true, text: ranges.text}); + if (e.type == "cut") { + cm.setSelections(ranges.ranges, null, sel_dontScroll); + } else { + input.prevInput = ""; + te.value = ranges.text.join("\n"); + selectInput(te); + } + } + if (e.type == "cut") { cm.state.cutIncoming = +new Date; } + } + on(te, "cut", prepareCopyCut); + on(te, "copy", prepareCopyCut); + + on(display.scroller, "paste", function (e) { + if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return } + if (!te.dispatchEvent) { + cm.state.pasteIncoming = +new Date; + input.focus(); + return + } + + // Pass the `paste` event to the textarea so it's handled by its event listener. + var event = new Event("paste"); + event.clipboardData = e.clipboardData; + te.dispatchEvent(event); + }); + + // Prevent normal selection in the editor (we handle our own) + on(display.lineSpace, "selectstart", function (e) { + if (!eventInWidget(display, e)) { e_preventDefault(e); } + }); + + on(te, "compositionstart", function () { + var start = cm.getCursor("from"); + if (input.composing) { input.composing.range.clear(); } + input.composing = { + start: start, + range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"}) + }; + }); + on(te, "compositionend", function () { + if (input.composing) { + input.poll(); + input.composing.range.clear(); + input.composing = null; + } + }); + }; + + TextareaInput.prototype.createField = function (_display) { + // Wraps and hides input textarea + this.wrapper = hiddenTextarea(); + // The semihidden textarea that is focused when the editor is + // focused, and receives input. + this.textarea = this.wrapper.firstChild; + }; + + TextareaInput.prototype.screenReaderLabelChanged = function (label) { + // Label for screenreaders, accessibility + if(label) { + this.textarea.setAttribute('aria-label', label); + } else { + this.textarea.removeAttribute('aria-label'); + } + }; + + TextareaInput.prototype.prepareSelection = function () { + // Redraw the selection and/or cursor + var cm = this.cm, display = cm.display, doc = cm.doc; + var result = prepareSelection(cm); + + // Move the hidden textarea near the cursor to prevent scrolling artifacts + if (cm.options.moveInputWithCursor) { + var headPos = cursorCoords(cm, doc.sel.primary().head, "div"); + var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect(); + result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10, + headPos.top + lineOff.top - wrapOff.top)); + result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10, + headPos.left + lineOff.left - wrapOff.left)); + } + + return result + }; + + TextareaInput.prototype.showSelection = function (drawn) { + var cm = this.cm, display = cm.display; + removeChildrenAndAdd(display.cursorDiv, drawn.cursors); + removeChildrenAndAdd(display.selectionDiv, drawn.selection); + if (drawn.teTop != null) { + this.wrapper.style.top = drawn.teTop + "px"; + this.wrapper.style.left = drawn.teLeft + "px"; + } + }; + + // Reset the input to correspond to the selection (or to be empty, + // when not typing and nothing is selected) + TextareaInput.prototype.reset = function (typing) { + if (this.contextMenuPending || this.composing) { return } + var cm = this.cm; + if (cm.somethingSelected()) { + this.prevInput = ""; + var content = cm.getSelection(); + this.textarea.value = content; + if (cm.state.focused) { selectInput(this.textarea); } + if (ie && ie_version >= 9) { this.hasSelection = content; } + } else if (!typing) { + this.prevInput = this.textarea.value = ""; + if (ie && ie_version >= 9) { this.hasSelection = null; } + } + }; + + TextareaInput.prototype.getField = function () { return this.textarea }; + + TextareaInput.prototype.supportsTouch = function () { return false }; + + TextareaInput.prototype.focus = function () { + if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) { + try { this.textarea.focus(); } + catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM + } + }; + + TextareaInput.prototype.blur = function () { this.textarea.blur(); }; + + TextareaInput.prototype.resetPosition = function () { + this.wrapper.style.top = this.wrapper.style.left = 0; + }; + + TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); }; + + // Poll for input changes, using the normal rate of polling. This + // runs as long as the editor is focused. + TextareaInput.prototype.slowPoll = function () { + var this$1 = this; + + if (this.pollingFast) { return } + this.polling.set(this.cm.options.pollInterval, function () { + this$1.poll(); + if (this$1.cm.state.focused) { this$1.slowPoll(); } + }); + }; + + // When an event has just come in that is likely to add or change + // something in the input textarea, we poll faster, to ensure that + // the change appears on the screen quickly. + TextareaInput.prototype.fastPoll = function () { + var missed = false, input = this; + input.pollingFast = true; + function p() { + var changed = input.poll(); + if (!changed && !missed) {missed = true; input.polling.set(60, p);} + else {input.pollingFast = false; input.slowPoll();} + } + input.polling.set(20, p); + }; + + // Read input from the textarea, and update the document to match. + // When something is selected, it is present in the textarea, and + // selected (unless it is huge, in which case a placeholder is + // used). When nothing is selected, the cursor sits after previously + // seen text (can be empty), which is stored in prevInput (we must + // not reset the textarea when typing, because that breaks IME). + TextareaInput.prototype.poll = function () { + var this$1 = this; + + var cm = this.cm, input = this.textarea, prevInput = this.prevInput; + // Since this is called a *lot*, try to bail out as cheaply as + // possible when it is clear that nothing happened. hasSelection + // will be the case when there is a lot of text in the textarea, + // in which case reading its value would be expensive. + if (this.contextMenuPending || !cm.state.focused || + (hasSelection(input) && !prevInput && !this.composing) || + cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq) + { return false } + + var text = input.value; + // If nothing changed, bail. + if (text == prevInput && !cm.somethingSelected()) { return false } + // Work around nonsensical selection resetting in IE9/10, and + // inexplicable appearance of private area unicode characters on + // some key combos in Mac (#2689). + if (ie && ie_version >= 9 && this.hasSelection === text || + mac && /[\uf700-\uf7ff]/.test(text)) { + cm.display.input.reset(); + return false + } + + if (cm.doc.sel == cm.display.selForContextMenu) { + var first = text.charCodeAt(0); + if (first == 0x200b && !prevInput) { prevInput = "\u200b"; } + if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") } + } + // Find the part of the input that is actually new + var same = 0, l = Math.min(prevInput.length, text.length); + while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; } + + runInOp(cm, function () { + applyTextInput(cm, text.slice(same), prevInput.length - same, + null, this$1.composing ? "*compose" : null); + + // Don't leave long text in the textarea, since it makes further polling slow + if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; } + else { this$1.prevInput = text; } + + if (this$1.composing) { + this$1.composing.range.clear(); + this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"), + {className: "CodeMirror-composing"}); + } + }); + return true + }; + + TextareaInput.prototype.ensurePolled = function () { + if (this.pollingFast && this.poll()) { this.pollingFast = false; } + }; + + TextareaInput.prototype.onKeyPress = function () { + if (ie && ie_version >= 9) { this.hasSelection = null; } + this.fastPoll(); + }; + + TextareaInput.prototype.onContextMenu = function (e) { + var input = this, cm = input.cm, display = cm.display, te = input.textarea; + if (input.contextMenuPending) { input.contextMenuPending(); } + var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop; + if (!pos || presto) { return } // Opera is difficult. + + // Reset the current text selection only if the click is done outside of the selection + // and 'resetSelectionOnContextMenu' option is true. + var reset = cm.options.resetSelectionOnContextMenu; + if (reset && cm.doc.sel.contains(pos) == -1) + { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); } + + var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText; + var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect(); + input.wrapper.style.cssText = "position: static"; + te.style.cssText = "position: absolute; width: 30px; height: 30px;\n top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; + var oldScrollY; + if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712) + display.input.focus(); + if (webkit) { window.scrollTo(null, oldScrollY); } + display.input.reset(); + // Adds "Select all" to context menu in FF + if (!cm.somethingSelected()) { te.value = input.prevInput = " "; } + input.contextMenuPending = rehide; + display.selForContextMenu = cm.doc.sel; + clearTimeout(display.detectingSelectAll); + + // Select-all will be greyed out if there's nothing to select, so + // this adds a zero-width space so that we can later check whether + // it got selected. + function prepareSelectAllHack() { + if (te.selectionStart != null) { + var selected = cm.somethingSelected(); + var extval = "\u200b" + (selected ? te.value : ""); + te.value = "\u21da"; // Used to catch context-menu undo + te.value = extval; + input.prevInput = selected ? "" : "\u200b"; + te.selectionStart = 1; te.selectionEnd = extval.length; + // Re-set this, in case some other handler touched the + // selection in the meantime. + display.selForContextMenu = cm.doc.sel; + } + } + function rehide() { + if (input.contextMenuPending != rehide) { return } + input.contextMenuPending = false; + input.wrapper.style.cssText = oldWrapperCSS; + te.style.cssText = oldCSS; + if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); } + + // Try to detect the user choosing select-all + if (te.selectionStart != null) { + if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); } + var i = 0, poll = function () { + if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 && + te.selectionEnd > 0 && input.prevInput == "\u200b") { + operation(cm, selectAll)(cm); + } else if (i++ < 10) { + display.detectingSelectAll = setTimeout(poll, 500); + } else { + display.selForContextMenu = null; + display.input.reset(); + } + }; + display.detectingSelectAll = setTimeout(poll, 200); + } + } + + if (ie && ie_version >= 9) { prepareSelectAllHack(); } + if (captureRightClick) { + e_stop(e); + var mouseup = function () { + off(window, "mouseup", mouseup); + setTimeout(rehide, 20); + }; + on(window, "mouseup", mouseup); + } else { + setTimeout(rehide, 50); + } + }; + + TextareaInput.prototype.readOnlyChanged = function (val) { + if (!val) { this.reset(); } + this.textarea.disabled = val == "nocursor"; + this.textarea.readOnly = !!val; + }; + + TextareaInput.prototype.setUneditable = function () {}; + + TextareaInput.prototype.needsContentAttribute = false; + + function fromTextArea(textarea, options) { + options = options ? copyObj(options) : {}; + options.value = textarea.value; + if (!options.tabindex && textarea.tabIndex) + { options.tabindex = textarea.tabIndex; } + if (!options.placeholder && textarea.placeholder) + { options.placeholder = textarea.placeholder; } + // Set autofocus to true if this textarea is focused, or if it has + // autofocus and no other element is focused. + if (options.autofocus == null) { + var hasFocus = activeElt(); + options.autofocus = hasFocus == textarea || + textarea.getAttribute("autofocus") != null && hasFocus == document.body; + } + + function save() {textarea.value = cm.getValue();} + + var realSubmit; + if (textarea.form) { + on(textarea.form, "submit", save); + // Deplorable hack to make the submit method do the right thing. + if (!options.leaveSubmitMethodAlone) { + var form = textarea.form; + realSubmit = form.submit; + try { + var wrappedSubmit = form.submit = function () { + save(); + form.submit = realSubmit; + form.submit(); + form.submit = wrappedSubmit; + }; + } catch(e) {} + } + } + + options.finishInit = function (cm) { + cm.save = save; + cm.getTextArea = function () { return textarea; }; + cm.toTextArea = function () { + cm.toTextArea = isNaN; // Prevent this from being ran twice + save(); + textarea.parentNode.removeChild(cm.getWrapperElement()); + textarea.style.display = ""; + if (textarea.form) { + off(textarea.form, "submit", save); + if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == "function") + { textarea.form.submit = realSubmit; } + } + }; + }; + + textarea.style.display = "none"; + var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); }, + options); + return cm + } + + function addLegacyProps(CodeMirror) { + CodeMirror.off = off; + CodeMirror.on = on; + CodeMirror.wheelEventPixels = wheelEventPixels; + CodeMirror.Doc = Doc; + CodeMirror.splitLines = splitLinesAuto; + CodeMirror.countColumn = countColumn; + CodeMirror.findColumn = findColumn; + CodeMirror.isWordChar = isWordCharBasic; + CodeMirror.Pass = Pass; + CodeMirror.signal = signal; + CodeMirror.Line = Line; + CodeMirror.changeEnd = changeEnd; + CodeMirror.scrollbarModel = scrollbarModel; + CodeMirror.Pos = Pos; + CodeMirror.cmpPos = cmp; + CodeMirror.modes = modes; + CodeMirror.mimeModes = mimeModes; + CodeMirror.resolveMode = resolveMode; + CodeMirror.getMode = getMode; + CodeMirror.modeExtensions = modeExtensions; + CodeMirror.extendMode = extendMode; + CodeMirror.copyState = copyState; + CodeMirror.startState = startState; + CodeMirror.innerMode = innerMode; + CodeMirror.commands = commands; + CodeMirror.keyMap = keyMap; + CodeMirror.keyName = keyName; + CodeMirror.isModifierKey = isModifierKey; + CodeMirror.lookupKey = lookupKey; + CodeMirror.normalizeKeyMap = normalizeKeyMap; + CodeMirror.StringStream = StringStream; + CodeMirror.SharedTextMarker = SharedTextMarker; + CodeMirror.TextMarker = TextMarker; + CodeMirror.LineWidget = LineWidget; + CodeMirror.e_preventDefault = e_preventDefault; + CodeMirror.e_stopPropagation = e_stopPropagation; + CodeMirror.e_stop = e_stop; + CodeMirror.addClass = addClass; + CodeMirror.contains = contains; + CodeMirror.rmClass = rmClass; + CodeMirror.keyNames = keyNames; + } + + // EDITOR CONSTRUCTOR + + defineOptions(CodeMirror); + + addEditorMethods(CodeMirror); + + // Set up methods on CodeMirror's prototype to redirect to the editor's document. + var dontDelegate = "iter insert remove copy getEditor constructor".split(" "); + for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0) + { CodeMirror.prototype[prop] = (function(method) { + return function() {return method.apply(this.doc, arguments)} + })(Doc.prototype[prop]); } } + + eventMixin(Doc); + CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}; + + // Extra arguments are stored as the mode's dependencies, which is + // used by (legacy) mechanisms like loadmode.js to automatically + // load a mode. (Preferred mechanism is the require/define calls.) + CodeMirror.defineMode = function(name/*, mode, …*/) { + if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name; } + defineMode.apply(this, arguments); + }; + + CodeMirror.defineMIME = defineMIME; + + // Minimal default mode. + CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); }); + CodeMirror.defineMIME("text/plain", "null"); + + // EXTENSIONS + + CodeMirror.defineExtension = function (name, func) { + CodeMirror.prototype[name] = func; + }; + CodeMirror.defineDocExtension = function (name, func) { + Doc.prototype[name] = func; + }; + + CodeMirror.fromTextArea = fromTextArea; + + addLegacyProps(CodeMirror); + + CodeMirror.version = "5.58.3"; + + return CodeMirror; +}))); + + +/* +file https://github.com/codemirror/CodeMirror/blob/5.58.3/addon/edit/matchbrackets.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + var ie_lt8 = /MSIE \d/.test(navigator.userAgent) && + (document.documentMode == null || document.documentMode < 8); + + var Pos = CodeMirror.Pos; + + var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<", "<": ">>", ">": "<<"}; + + function bracketRegex(config) { + return config && config.bracketRegex || /[(){}[\]]/ + } + + function findMatchingBracket(cm, where, config) { + var line = cm.getLineHandle(where.line), pos = where.ch - 1; + var afterCursor = config && config.afterCursor + if (afterCursor == null) + afterCursor = /(^| )cm-fat-cursor($| )/.test(cm.getWrapperElement().className) + var re = bracketRegex(config) + + // A cursor is defined as between two characters, but in in vim command mode + // (i.e. not insert mode), the cursor is visually represented as a + // highlighted box on top of the 2nd character. Otherwise, we allow matches + // from before or after the cursor. + var match = (!afterCursor && pos >= 0 && re.test(line.text.charAt(pos)) && matching[line.text.charAt(pos)]) || + re.test(line.text.charAt(pos + 1)) && matching[line.text.charAt(++pos)]; + if (!match) return null; + var dir = match.charAt(1) == ">" ? 1 : -1; + if (config && config.strict && (dir > 0) != (pos == where.ch)) return null; + var style = cm.getTokenTypeAt(Pos(where.line, pos + 1)); + + var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style || null, config); + if (found == null) return null; + return {from: Pos(where.line, pos), to: found && found.pos, + match: found && found.ch == match.charAt(0), forward: dir > 0}; + } + + // bracketRegex is used to specify which type of bracket to scan + // should be a regexp, e.g. /[[\]]/ + // + // Note: If "where" is on an open bracket, then this bracket is ignored. + // + // Returns false when no bracket was found, null when it reached + // maxScanLines and gave up + function scanForBracket(cm, where, dir, style, config) { + var maxScanLen = (config && config.maxScanLineLength) || 10000; + var maxScanLines = (config && config.maxScanLines) || 1000; + + var stack = []; + var re = bracketRegex(config) + var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1) + : Math.max(cm.firstLine() - 1, where.line - maxScanLines); + for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) { + var line = cm.getLine(lineNo); + if (!line) continue; + var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1; + if (line.length > maxScanLen) continue; + if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0); + for (; pos != end; pos += dir) { + var ch = line.charAt(pos); + if (re.test(ch) && (style === undefined || cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style)) { + var match = matching[ch]; + if (match && (match.charAt(1) == ">") == (dir > 0)) stack.push(ch); + else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch}; + else stack.pop(); + } + } + } + return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null; + } + + function matchBrackets(cm, autoclear, config) { + // Disable brace matching in long lines, since it'll cause hugely slow updates + var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000; + var marks = [], ranges = cm.listSelections(); + for (var i = 0; i < ranges.length; i++) { + var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, config); + if (match && cm.getLine(match.from.line).length <= maxHighlightLen) { + var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; + marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style})); + if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen) + marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style})); + } + } + + if (marks.length) { + // Kludge to work around the IE bug from issue #1193, where text + // input stops going to the textare whever this fires. + if (ie_lt8 && cm.state.focused) cm.focus(); + + var clear = function() { + cm.operation(function() { + for (var i = 0; i < marks.length; i++) marks[i].clear(); + }); + }; + if (autoclear) setTimeout(clear, 800); + else return clear; + } + } + + function doMatchBrackets(cm) { + cm.operation(function() { + if (cm.state.matchBrackets.currentlyHighlighted) { + cm.state.matchBrackets.currentlyHighlighted(); + cm.state.matchBrackets.currentlyHighlighted = null; + } + cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets); + }); + } + + CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) { + function clear(cm) { + if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) { + cm.state.matchBrackets.currentlyHighlighted(); + cm.state.matchBrackets.currentlyHighlighted = null; + } + } + + if (old && old != CodeMirror.Init) { + cm.off("cursorActivity", doMatchBrackets); + cm.off("focus", doMatchBrackets) + cm.off("blur", clear) + clear(cm); + } + if (val) { + cm.state.matchBrackets = typeof val == "object" ? val : {}; + cm.on("cursorActivity", doMatchBrackets); + cm.on("focus", doMatchBrackets) + cm.on("blur", clear) + } + }); + + CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);}); + CodeMirror.defineExtension("findMatchingBracket", function(pos, config, oldConfig){ + // Backwards-compatibility kludge + if (oldConfig || typeof config == "boolean") { + if (!oldConfig) { + config = config ? {strict: true} : null + } else { + oldConfig.strict = config + config = oldConfig + } + } + return findMatchingBracket(this, pos, config) + }); + CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){ + return scanForBracket(this, pos, dir, style, config); + }); +}); + + +/* +file https://github.com/codemirror/CodeMirror/blob/5.58.3/addon/edit/trailingspace.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) { + if (prev == CodeMirror.Init) prev = false; + if (prev && !val) + cm.removeOverlay("trailingspace"); + else if (!prev && val) + cm.addOverlay({ + token: function(stream) { + for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {} + if (i > stream.pos) { stream.pos = i; return null; } + stream.pos = l; + return "trailingspace"; + }, + name: "trailingspace" + }); + }); +}); + + +/* +file https://github.com/codemirror/CodeMirror/blob/5.58.3/mode/javascript/javascript.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("javascript", function(config, parserConfig) { + var indentUnit = config.indentUnit; + var statementIndent = parserConfig.statementIndent; + var jsonldMode = parserConfig.jsonld; + var jsonMode = parserConfig.json || jsonldMode; + var isTS = parserConfig.typescript; + var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/; + + // Tokenizer + + var keywords = function(){ + function kw(type) {return {type: type, style: "keyword"};} + var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d"); + var operator = kw("operator"), atom = {type: "atom", style: "atom"}; + + return { + "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, + "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C, + "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"), + "function": kw("function"), "catch": kw("catch"), + "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"), + "in": operator, "typeof": operator, "instanceof": operator, + "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom, + "this": kw("this"), "class": kw("class"), "super": kw("atom"), + "yield": C, "export": kw("export"), "import": kw("import"), "extends": C, + "await": C + }; + }(); + + var isOperatorChar = /[+\-*&%=<>!?|~^@]/; + var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/; + + function readRegexp(stream) { + var escaped = false, next, inSet = false; + while ((next = stream.next()) != null) { + if (!escaped) { + if (next == "/" && !inSet) return; + if (next == "[") inSet = true; + else if (inSet && next == "]") inSet = false; + } + escaped = !escaped && next == "\\"; + } + } + + // Used as scratch variables to communicate multiple values without + // consing up tons of objects. + var type, content; + function ret(tp, style, cont) { + type = tp; content = cont; + return style; + } + function tokenBase(stream, state) { + var ch = stream.next(); + if (ch == '"' || ch == "'") { + state.tokenize = tokenString(ch); + return state.tokenize(stream, state); + } else if (ch == "." && stream.match(/^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/)) { + return ret("number", "number"); + } else if (ch == "." && stream.match("..")) { + return ret("spread", "meta"); + } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) { + return ret(ch); + } else if (ch == "=" && stream.eat(">")) { + return ret("=>", "operator"); + } else if (ch == "0" && stream.match(/^(?:x[\dA-Fa-f_]+|o[0-7_]+|b[01_]+)n?/)) { + return ret("number", "number"); + } else if (/\d/.test(ch)) { + stream.match(/^[\d_]*(?:n|(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)?/); + return ret("number", "number"); + } else if (ch == "/") { + if (stream.eat("*")) { + state.tokenize = tokenComment; + return tokenComment(stream, state); + } else if (stream.eat("/")) { + stream.skipToEnd(); + return ret("comment", "comment"); + } else if (expressionAllowed(stream, state, 1)) { + readRegexp(stream); + stream.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/); + return ret("regexp", "string-2"); + } else { + stream.eat("="); + return ret("operator", "operator", stream.current()); + } + } else if (ch == "`") { + state.tokenize = tokenQuasi; + return tokenQuasi(stream, state); + } else if (ch == "#" && stream.peek() == "!") { + stream.skipToEnd(); + return ret("meta", "meta"); + } else if (ch == "#" && stream.eatWhile(wordRE)) { + return ret("variable", "property") + } else if (ch == "<" && stream.match("!--") || + (ch == "-" && stream.match("->") && !/\S/.test(stream.string.slice(0, stream.start)))) { + stream.skipToEnd() + return ret("comment", "comment") + } else if (isOperatorChar.test(ch)) { + if (ch != ">" || !state.lexical || state.lexical.type != ">") { + if (stream.eat("=")) { + if (ch == "!" || ch == "=") stream.eat("=") + } else if (/[<>*+\-|&?]/.test(ch)) { + stream.eat(ch) + if (ch == ">") stream.eat(ch) + } + } + if (ch == "?" && stream.eat(".")) return ret(".") + return ret("operator", "operator", stream.current()); + } else if (wordRE.test(ch)) { + stream.eatWhile(wordRE); + var word = stream.current() + if (state.lastType != ".") { + if (keywords.propertyIsEnumerable(word)) { + var kw = keywords[word] + return ret(kw.type, kw.style, word) + } + if (word == "async" && stream.match(/^(\s|\/\*([^*]|\*(?!\/))*?\*\/)*[\[\(\w]/, false)) + return ret("async", "keyword", word) + } + return ret("variable", "variable", word) + } + } + + function tokenString(quote) { + return function(stream, state) { + var escaped = false, next; + if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){ + state.tokenize = tokenBase; + return ret("jsonld-keyword", "meta"); + } + while ((next = stream.next()) != null) { + if (next == quote && !escaped) break; + escaped = !escaped && next == "\\"; + } + if (!escaped) state.tokenize = tokenBase; + return ret("string", "string"); + }; + } + + function tokenComment(stream, state) { + var maybeEnd = false, ch; + while (ch = stream.next()) { + if (ch == "/" && maybeEnd) { + state.tokenize = tokenBase; + break; + } + maybeEnd = (ch == "*"); + } + return ret("comment", "comment"); + } + + function tokenQuasi(stream, state) { + var escaped = false, next; + while ((next = stream.next()) != null) { + if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) { + state.tokenize = tokenBase; + break; + } + escaped = !escaped && next == "\\"; + } + return ret("quasi", "string-2", stream.current()); + } + + var brackets = "([{}])"; + // This is a crude lookahead trick to try and notice that we're + // parsing the argument patterns for a fat-arrow function before we + // actually hit the arrow token. It only works if the arrow is on + // the same line as the arguments and there's no strange noise + // (comments) in between. Fallback is to only notice when we hit the + // arrow, and not declare the arguments as locals for the arrow + // body. + function findFatArrow(stream, state) { + if (state.fatArrowAt) state.fatArrowAt = null; + var arrow = stream.string.indexOf("=>", stream.start); + if (arrow < 0) return; + + if (isTS) { // Try to skip TypeScript return type declarations after the arguments + var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow)) + if (m) arrow = m.index + } + + var depth = 0, sawSomething = false; + for (var pos = arrow - 1; pos >= 0; --pos) { + var ch = stream.string.charAt(pos); + var bracket = brackets.indexOf(ch); + if (bracket >= 0 && bracket < 3) { + if (!depth) { ++pos; break; } + if (--depth == 0) { if (ch == "(") sawSomething = true; break; } + } else if (bracket >= 3 && bracket < 6) { + ++depth; + } else if (wordRE.test(ch)) { + sawSomething = true; + } else if (/["'\/`]/.test(ch)) { + for (;; --pos) { + if (pos == 0) return + var next = stream.string.charAt(pos - 1) + if (next == ch && stream.string.charAt(pos - 2) != "\\") { pos--; break } + } + } else if (sawSomething && !depth) { + ++pos; + break; + } + } + if (sawSomething && !depth) state.fatArrowAt = pos; + } + + // Parser + + var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true}; + + function JSLexical(indented, column, type, align, prev, info) { + this.indented = indented; + this.column = column; + this.type = type; + this.prev = prev; + this.info = info; + if (align != null) this.align = align; + } + + function inScope(state, varname) { + for (var v = state.localVars; v; v = v.next) + if (v.name == varname) return true; + for (var cx = state.context; cx; cx = cx.prev) { + for (var v = cx.vars; v; v = v.next) + if (v.name == varname) return true; + } + } + + function parseJS(state, style, type, content, stream) { + var cc = state.cc; + // Communicate our context to the combinators. + // (Less wasteful than consing up a hundred closures on every call.) + cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style; + + if (!state.lexical.hasOwnProperty("align")) + state.lexical.align = true; + + while(true) { + var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement; + if (combinator(type, content)) { + while(cc.length && cc[cc.length - 1].lex) + cc.pop()(); + if (cx.marked) return cx.marked; + if (type == "variable" && inScope(state, content)) return "variable-2"; + return style; + } + } + } + + // Combinator utils + + var cx = {state: null, column: null, marked: null, cc: null}; + function pass() { + for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]); + } + function cont() { + pass.apply(null, arguments); + return true; + } + function inList(name, list) { + for (var v = list; v; v = v.next) if (v.name == name) return true + return false; + } + function register(varname) { + var state = cx.state; + cx.marked = "def"; + if (state.context) { + if (state.lexical.info == "var" && state.context && state.context.block) { + // FIXME function decls are also not block scoped + var newContext = registerVarScoped(varname, state.context) + if (newContext != null) { + state.context = newContext + return + } + } else if (!inList(varname, state.localVars)) { + state.localVars = new Var(varname, state.localVars) + return + } + } + // Fall through means this is global + if (parserConfig.globalVars && !inList(varname, state.globalVars)) + state.globalVars = new Var(varname, state.globalVars) + } + function registerVarScoped(varname, context) { + if (!context) { + return null + } else if (context.block) { + var inner = registerVarScoped(varname, context.prev) + if (!inner) return null + if (inner == context.prev) return context + return new Context(inner, context.vars, true) + } else if (inList(varname, context.vars)) { + return context + } else { + return new Context(context.prev, new Var(varname, context.vars), false) + } + } + + function isModifier(name) { + return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly" + } + + // Combinators + + function Context(prev, vars, block) { this.prev = prev; this.vars = vars; this.block = block } + function Var(name, next) { this.name = name; this.next = next } + + var defaultVars = new Var("this", new Var("arguments", null)) + function pushcontext() { + cx.state.context = new Context(cx.state.context, cx.state.localVars, false) + cx.state.localVars = defaultVars + } + function pushblockcontext() { + cx.state.context = new Context(cx.state.context, cx.state.localVars, true) + cx.state.localVars = null + } + function popcontext() { + cx.state.localVars = cx.state.context.vars + cx.state.context = cx.state.context.prev + } + popcontext.lex = true + function pushlex(type, info) { + var result = function() { + var state = cx.state, indent = state.indented; + if (state.lexical.type == "stat") indent = state.lexical.indented; + else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev) + indent = outer.indented; + state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info); + }; + result.lex = true; + return result; + } + function poplex() { + var state = cx.state; + if (state.lexical.prev) { + if (state.lexical.type == ")") + state.indented = state.lexical.indented; + state.lexical = state.lexical.prev; + } + } + poplex.lex = true; + + function expect(wanted) { + function exp(type) { + if (type == wanted) return cont(); + else if (wanted == ";" || type == "}" || type == ")" || type == "]") return pass(); + else return cont(exp); + }; + return exp; + } + + function statement(type, value) { + if (type == "var") return cont(pushlex("vardef", value), vardef, expect(";"), poplex); + if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex); + if (type == "keyword b") return cont(pushlex("form"), statement, poplex); + if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex); + if (type == "debugger") return cont(expect(";")); + if (type == "{") return cont(pushlex("}"), pushblockcontext, block, poplex, popcontext); + if (type == ";") return cont(); + if (type == "if") { + if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex) + cx.state.cc.pop()(); + return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse); + } + if (type == "function") return cont(functiondef); + if (type == "for") return cont(pushlex("form"), forspec, statement, poplex); + if (type == "class" || (isTS && value == "interface")) { + cx.marked = "keyword" + return cont(pushlex("form", type == "class" ? type : value), className, poplex) + } + if (type == "variable") { + if (isTS && value == "declare") { + cx.marked = "keyword" + return cont(statement) + } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) { + cx.marked = "keyword" + if (value == "enum") return cont(enumdef); + else if (value == "type") return cont(typename, expect("operator"), typeexpr, expect(";")); + else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex) + } else if (isTS && value == "namespace") { + cx.marked = "keyword" + return cont(pushlex("form"), expression, statement, poplex) + } else if (isTS && value == "abstract") { + cx.marked = "keyword" + return cont(statement) + } else { + return cont(pushlex("stat"), maybelabel); + } + } + if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"), pushblockcontext, + block, poplex, poplex, popcontext); + if (type == "case") return cont(expression, expect(":")); + if (type == "default") return cont(expect(":")); + if (type == "catch") return cont(pushlex("form"), pushcontext, maybeCatchBinding, statement, poplex, popcontext); + if (type == "export") return cont(pushlex("stat"), afterExport, poplex); + if (type == "import") return cont(pushlex("stat"), afterImport, poplex); + if (type == "async") return cont(statement) + if (value == "@") return cont(expression, statement) + return pass(pushlex("stat"), expression, expect(";"), poplex); + } + function maybeCatchBinding(type) { + if (type == "(") return cont(funarg, expect(")")) + } + function expression(type, value) { + return expressionInner(type, value, false); + } + function expressionNoComma(type, value) { + return expressionInner(type, value, true); + } + function parenExpr(type) { + if (type != "(") return pass() + return cont(pushlex(")"), maybeexpression, expect(")"), poplex) + } + function expressionInner(type, value, noComma) { + if (cx.state.fatArrowAt == cx.stream.start) { + var body = noComma ? arrowBodyNoComma : arrowBody; + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext); + else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext); + } + + var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma; + if (atomicTypes.hasOwnProperty(type)) return cont(maybeop); + if (type == "function") return cont(functiondef, maybeop); + if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); } + if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression); + if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop); + if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression); + if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop); + if (type == "{") return contCommasep(objprop, "}", null, maybeop); + if (type == "quasi") return pass(quasi, maybeop); + if (type == "new") return cont(maybeTarget(noComma)); + if (type == "import") return cont(expression); + return cont(); + } + function maybeexpression(type) { + if (type.match(/[;\}\)\],]/)) return pass(); + return pass(expression); + } + + function maybeoperatorComma(type, value) { + if (type == ",") return cont(maybeexpression); + return maybeoperatorNoComma(type, value, false); + } + function maybeoperatorNoComma(type, value, noComma) { + var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma; + var expr = noComma == false ? expression : expressionNoComma; + if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext); + if (type == "operator") { + if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me); + if (isTS && value == "<" && cx.stream.match(/^([^<>]|<[^<>]*>)*>\s*\(/, false)) + return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me); + if (value == "?") return cont(expression, expect(":"), expr); + return cont(expr); + } + if (type == "quasi") { return pass(quasi, me); } + if (type == ";") return; + if (type == "(") return contCommasep(expressionNoComma, ")", "call", me); + if (type == ".") return cont(property, me); + if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me); + if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) } + if (type == "regexp") { + cx.state.lastType = cx.marked = "operator" + cx.stream.backUp(cx.stream.pos - cx.stream.start - 1) + return cont(expr) + } + } + function quasi(type, value) { + if (type != "quasi") return pass(); + if (value.slice(value.length - 2) != "${") return cont(quasi); + return cont(expression, continueQuasi); + } + function continueQuasi(type) { + if (type == "}") { + cx.marked = "string-2"; + cx.state.tokenize = tokenQuasi; + return cont(quasi); + } + } + function arrowBody(type) { + findFatArrow(cx.stream, cx.state); + return pass(type == "{" ? statement : expression); + } + function arrowBodyNoComma(type) { + findFatArrow(cx.stream, cx.state); + return pass(type == "{" ? statement : expressionNoComma); + } + function maybeTarget(noComma) { + return function(type) { + if (type == ".") return cont(noComma ? targetNoComma : target); + else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma) + else return pass(noComma ? expressionNoComma : expression); + }; + } + function target(_, value) { + if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); } + } + function targetNoComma(_, value) { + if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); } + } + function maybelabel(type) { + if (type == ":") return cont(poplex, statement); + return pass(maybeoperatorComma, expect(";"), poplex); + } + function property(type) { + if (type == "variable") {cx.marked = "property"; return cont();} + } + function objprop(type, value) { + if (type == "async") { + cx.marked = "property"; + return cont(objprop); + } else if (type == "variable" || cx.style == "keyword") { + cx.marked = "property"; + if (value == "get" || value == "set") return cont(getterSetter); + var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params + if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false))) + cx.state.fatArrowAt = cx.stream.pos + m[0].length + return cont(afterprop); + } else if (type == "number" || type == "string") { + cx.marked = jsonldMode ? "property" : (cx.style + " property"); + return cont(afterprop); + } else if (type == "jsonld-keyword") { + return cont(afterprop); + } else if (isTS && isModifier(value)) { + cx.marked = "keyword" + return cont(objprop) + } else if (type == "[") { + return cont(expression, maybetype, expect("]"), afterprop); + } else if (type == "spread") { + return cont(expressionNoComma, afterprop); + } else if (value == "*") { + cx.marked = "keyword"; + return cont(objprop); + } else if (type == ":") { + return pass(afterprop) + } + } + function getterSetter(type) { + if (type != "variable") return pass(afterprop); + cx.marked = "property"; + return cont(functiondef); + } + function afterprop(type) { + if (type == ":") return cont(expressionNoComma); + if (type == "(") return pass(functiondef); + } + function commasep(what, end, sep) { + function proceed(type, value) { + if (sep ? sep.indexOf(type) > -1 : type == ",") { + var lex = cx.state.lexical; + if (lex.info == "call") lex.pos = (lex.pos || 0) + 1; + return cont(function(type, value) { + if (type == end || value == end) return pass() + return pass(what) + }, proceed); + } + if (type == end || value == end) return cont(); + if (sep && sep.indexOf(";") > -1) return pass(what) + return cont(expect(end)); + } + return function(type, value) { + if (type == end || value == end) return cont(); + return pass(what, proceed); + }; + } + function contCommasep(what, end, info) { + for (var i = 3; i < arguments.length; i++) + cx.cc.push(arguments[i]); + return cont(pushlex(end, info), commasep(what, end), poplex); + } + function block(type) { + if (type == "}") return cont(); + return pass(statement, block); + } + function maybetype(type, value) { + if (isTS) { + if (type == ":") return cont(typeexpr); + if (value == "?") return cont(maybetype); + } + } + function maybetypeOrIn(type, value) { + if (isTS && (type == ":" || value == "in")) return cont(typeexpr) + } + function mayberettype(type) { + if (isTS && type == ":") { + if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr) + else return cont(typeexpr) + } + } + function isKW(_, value) { + if (value == "is") { + cx.marked = "keyword" + return cont() + } + } + function typeexpr(type, value) { + if (value == "keyof" || value == "typeof" || value == "infer") { + cx.marked = "keyword" + return cont(value == "typeof" ? expressionNoComma : typeexpr) + } + if (type == "variable" || value == "void") { + cx.marked = "type" + return cont(afterType) + } + if (value == "|" || value == "&") return cont(typeexpr) + if (type == "string" || type == "number" || type == "atom") return cont(afterType); + if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType) + if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex, afterType) + if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType, afterType) + if (type == "<") return cont(commasep(typeexpr, ">"), typeexpr) + } + function maybeReturnType(type) { + if (type == "=>") return cont(typeexpr) + } + function typeprop(type, value) { + if (type == "variable" || cx.style == "keyword") { + cx.marked = "property" + return cont(typeprop) + } else if (value == "?" || type == "number" || type == "string") { + return cont(typeprop) + } else if (type == ":") { + return cont(typeexpr) + } else if (type == "[") { + return cont(expect("variable"), maybetypeOrIn, expect("]"), typeprop) + } else if (type == "(") { + return pass(functiondecl, typeprop) + } + } + function typearg(type, value) { + if (type == "variable" && cx.stream.match(/^\s*[?:]/, false) || value == "?") return cont(typearg) + if (type == ":") return cont(typeexpr) + if (type == "spread") return cont(typearg) + return pass(typeexpr) + } + function afterType(type, value) { + if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) + if (value == "|" || type == "." || value == "&") return cont(typeexpr) + if (type == "[") return cont(typeexpr, expect("]"), afterType) + if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) } + if (value == "?") return cont(typeexpr, expect(":"), typeexpr) + } + function maybeTypeArgs(_, value) { + if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) + } + function typeparam() { + return pass(typeexpr, maybeTypeDefault) + } + function maybeTypeDefault(_, value) { + if (value == "=") return cont(typeexpr) + } + function vardef(_, value) { + if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)} + return pass(pattern, maybetype, maybeAssign, vardefCont); + } + function pattern(type, value) { + if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) } + if (type == "variable") { register(value); return cont(); } + if (type == "spread") return cont(pattern); + if (type == "[") return contCommasep(eltpattern, "]"); + if (type == "{") return contCommasep(proppattern, "}"); + } + function proppattern(type, value) { + if (type == "variable" && !cx.stream.match(/^\s*:/, false)) { + register(value); + return cont(maybeAssign); + } + if (type == "variable") cx.marked = "property"; + if (type == "spread") return cont(pattern); + if (type == "}") return pass(); + if (type == "[") return cont(expression, expect(']'), expect(':'), proppattern); + return cont(expect(":"), pattern, maybeAssign); + } + function eltpattern() { + return pass(pattern, maybeAssign) + } + function maybeAssign(_type, value) { + if (value == "=") return cont(expressionNoComma); + } + function vardefCont(type) { + if (type == ",") return cont(vardef); + } + function maybeelse(type, value) { + if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex); + } + function forspec(type, value) { + if (value == "await") return cont(forspec); + if (type == "(") return cont(pushlex(")"), forspec1, poplex); + } + function forspec1(type) { + if (type == "var") return cont(vardef, forspec2); + if (type == "variable") return cont(forspec2); + return pass(forspec2) + } + function forspec2(type, value) { + if (type == ")") return cont() + if (type == ";") return cont(forspec2) + if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression, forspec2) } + return pass(expression, forspec2) + } + function functiondef(type, value) { + if (value == "*") {cx.marked = "keyword"; return cont(functiondef);} + if (type == "variable") {register(value); return cont(functiondef);} + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext); + if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef) + } + function functiondecl(type, value) { + if (value == "*") {cx.marked = "keyword"; return cont(functiondecl);} + if (type == "variable") {register(value); return cont(functiondecl);} + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, popcontext); + if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondecl) + } + function typename(type, value) { + if (type == "keyword" || type == "variable") { + cx.marked = "type" + return cont(typename) + } else if (value == "<") { + return cont(pushlex(">"), commasep(typeparam, ">"), poplex) + } + } + function funarg(type, value) { + if (value == "@") cont(expression, funarg) + if (type == "spread") return cont(funarg); + if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); } + if (isTS && type == "this") return cont(maybetype, maybeAssign) + return pass(pattern, maybetype, maybeAssign); + } + function classExpression(type, value) { + // Class expressions may have an optional name. + if (type == "variable") return className(type, value); + return classNameAfter(type, value); + } + function className(type, value) { + if (type == "variable") {register(value); return cont(classNameAfter);} + } + function classNameAfter(type, value) { + if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter) + if (value == "extends" || value == "implements" || (isTS && type == ",")) { + if (value == "implements") cx.marked = "keyword"; + return cont(isTS ? typeexpr : expression, classNameAfter); + } + if (type == "{") return cont(pushlex("}"), classBody, poplex); + } + function classBody(type, value) { + if (type == "async" || + (type == "variable" && + (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) && + cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) { + cx.marked = "keyword"; + return cont(classBody); + } + if (type == "variable" || cx.style == "keyword") { + cx.marked = "property"; + return cont(classfield, classBody); + } + if (type == "number" || type == "string") return cont(classfield, classBody); + if (type == "[") + return cont(expression, maybetype, expect("]"), classfield, classBody) + if (value == "*") { + cx.marked = "keyword"; + return cont(classBody); + } + if (isTS && type == "(") return pass(functiondecl, classBody) + if (type == ";" || type == ",") return cont(classBody); + if (type == "}") return cont(); + if (value == "@") return cont(expression, classBody) + } + function classfield(type, value) { + if (value == "?") return cont(classfield) + if (type == ":") return cont(typeexpr, maybeAssign) + if (value == "=") return cont(expressionNoComma) + var context = cx.state.lexical.prev, isInterface = context && context.info == "interface" + return pass(isInterface ? functiondecl : functiondef) + } + function afterExport(type, value) { + if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); } + if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); } + if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";")); + return pass(statement); + } + function exportField(type, value) { + if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); } + if (type == "variable") return pass(expressionNoComma, exportField); + } + function afterImport(type) { + if (type == "string") return cont(); + if (type == "(") return pass(expression); + return pass(importSpec, maybeMoreImports, maybeFrom); + } + function importSpec(type, value) { + if (type == "{") return contCommasep(importSpec, "}"); + if (type == "variable") register(value); + if (value == "*") cx.marked = "keyword"; + return cont(maybeAs); + } + function maybeMoreImports(type) { + if (type == ",") return cont(importSpec, maybeMoreImports) + } + function maybeAs(_type, value) { + if (value == "as") { cx.marked = "keyword"; return cont(importSpec); } + } + function maybeFrom(_type, value) { + if (value == "from") { cx.marked = "keyword"; return cont(expression); } + } + function arrayLiteral(type) { + if (type == "]") return cont(); + return pass(commasep(expressionNoComma, "]")); + } + function enumdef() { + return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex) + } + function enummember() { + return pass(pattern, maybeAssign); + } + + function isContinuedStatement(state, textAfter) { + return state.lastType == "operator" || state.lastType == "," || + isOperatorChar.test(textAfter.charAt(0)) || + /[,.]/.test(textAfter.charAt(0)); + } + + function expressionAllowed(stream, state, backUp) { + return state.tokenize == tokenBase && + /^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) || + (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0)))) + } + + // Interface + + return { + startState: function(basecolumn) { + var state = { + tokenize: tokenBase, + lastType: "sof", + cc: [], + lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false), + localVars: parserConfig.localVars, + context: parserConfig.localVars && new Context(null, null, false), + indented: basecolumn || 0 + }; + if (parserConfig.globalVars && typeof parserConfig.globalVars == "object") + state.globalVars = parserConfig.globalVars; + return state; + }, + + token: function(stream, state) { + if (stream.sol()) { + if (!state.lexical.hasOwnProperty("align")) + state.lexical.align = false; + state.indented = stream.indentation(); + findFatArrow(stream, state); + } + if (state.tokenize != tokenComment && stream.eatSpace()) return null; + var style = state.tokenize(stream, state); + if (type == "comment") return style; + state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type; + return parseJS(state, style, type, content, stream); + }, + + indent: function(state, textAfter) { + if (state.tokenize == tokenComment || state.tokenize == tokenQuasi) return CodeMirror.Pass; + if (state.tokenize != tokenBase) return 0; + var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top + // Kludge to prevent 'maybelse' from blocking lexical scope pops + if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) { + var c = state.cc[i]; + if (c == poplex) lexical = lexical.prev; + else if (c != maybeelse) break; + } + while ((lexical.type == "stat" || lexical.type == "form") && + (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) && + (top == maybeoperatorComma || top == maybeoperatorNoComma) && + !/^[,\.=+\-*:?[\(]/.test(textAfter)))) + lexical = lexical.prev; + if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat") + lexical = lexical.prev; + var type = lexical.type, closing = firstChar == type; + + if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info.length + 1 : 0); + else if (type == "form" && firstChar == "{") return lexical.indented; + else if (type == "form") return lexical.indented + indentUnit; + else if (type == "stat") + return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0); + else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false) + return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit); + else if (lexical.align) return lexical.column + (closing ? 0 : 1); + else return lexical.indented + (closing ? 0 : indentUnit); + }, + + electricInput: /^\s*(?:case .*?:|default:|\{|\})$/, + blockCommentStart: jsonMode ? null : "/*", + blockCommentEnd: jsonMode ? null : "*/", + blockCommentContinue: jsonMode ? null : " * ", + lineComment: jsonMode ? null : "//", + fold: "brace", + closeBrackets: "()[]{}''\"\"``", + + helperType: jsonMode ? "json" : "javascript", + jsonldMode: jsonldMode, + jsonMode: jsonMode, + + expressionAllowed: expressionAllowed, + + skipExpression: function(state) { + var top = state.cc[state.cc.length - 1] + if (top == expression || top == expressionNoComma) state.cc.pop() + } + }; +}); + +CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/); + +CodeMirror.defineMIME("text/javascript", "javascript"); +CodeMirror.defineMIME("text/ecmascript", "javascript"); +CodeMirror.defineMIME("application/javascript", "javascript"); +CodeMirror.defineMIME("application/x-javascript", "javascript"); +CodeMirror.defineMIME("application/ecmascript", "javascript"); +CodeMirror.defineMIME("application/json", {name: "javascript", json: true}); +CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true}); +CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true}); +CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true }); +CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true }); +}); + + +/* +file none +*/ +/*jslint-enable*/ diff --git a/asset-codemirror-rollup.css b/asset-codemirror-rollup.css new file mode 100644 index 000000000..3ed56d432 --- /dev/null +++ b/asset-codemirror-rollup.css @@ -0,0 +1,497 @@ +/*jslint-disable*/ +/* +shRawLibFetch +{ + "fetchList": [ + { + "comment": true, + "url": "https://github.com/codemirror/CodeMirror/blob/5.62.0/LICENSE" + }, + { + "url": "https://github.com/codemirror/CodeMirror/blob/5.62.0/lib/codemirror.css" + }, + { + "url": "https://github.com/codemirror/CodeMirror/blob/5.62.0/addon/lint/lint.css" + } + ] +} +*/ + + +/* +repo https://github.com/codemirror/CodeMirror/tree/5.62.0 +committed 2021-06-21T07:13:20Z +*/ + + +/* +file https://github.com/codemirror/CodeMirror/blob/5.62.0/LICENSE +*/ +/* +MIT License + +Copyright (C) 2017 by Marijn Haverbeke and others + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + + +/* +file https://github.com/codemirror/CodeMirror/blob/5.62.0/lib/codemirror.css +*/ +/* BASICS */ + +.CodeMirror { + /* Set height, width, borders, and global font properties here */ + font-family: monospace; + height: 300px; + color: black; + direction: ltr; +} + +/* PADDING */ + +.CodeMirror-lines { + padding: 4px 0; /* Vertical padding around content */ +} +.CodeMirror pre.CodeMirror-line, +.CodeMirror pre.CodeMirror-line-like { + padding: 0 4px; /* Horizontal padding of content */ +} + +.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { + background-color: white; /* The little square between H and V scrollbars */ +} + +/* GUTTER */ + +.CodeMirror-gutters { + border-right: 1px solid #ddd; + background-color: #f7f7f7; + white-space: nowrap; +} +.CodeMirror-linenumbers {} +.CodeMirror-linenumber { + padding: 0 3px 0 5px; + min-width: 20px; + text-align: right; + color: #999; + white-space: nowrap; +} + +.CodeMirror-guttermarker { color: black; } +.CodeMirror-guttermarker-subtle { color: #999; } + +/* CURSOR */ + +.CodeMirror-cursor { + border-left: 1px solid black; + border-right: none; + width: 0; +} +/* Shown when moving in bi-directional text */ +.CodeMirror div.CodeMirror-secondarycursor { + border-left: 1px solid silver; +} +.cm-fat-cursor .CodeMirror-cursor { + width: auto; + border: 0 !important; + background: #7e7; +} +.cm-fat-cursor div.CodeMirror-cursors { + z-index: 1; +} +.cm-fat-cursor-mark { + background-color: rgba(20, 255, 20, 0.5); + -webkit-animation: blink 1.06s steps(1) infinite; + -moz-animation: blink 1.06s steps(1) infinite; + animation: blink 1.06s steps(1) infinite; +} +.cm-animate-fat-cursor { + width: auto; + border: 0; + -webkit-animation: blink 1.06s steps(1) infinite; + -moz-animation: blink 1.06s steps(1) infinite; + animation: blink 1.06s steps(1) infinite; + background-color: #7e7; +} +@-moz-keyframes blink { + 0% {} + 50% { background-color: transparent; } + 100% {} +} +@-webkit-keyframes blink { + 0% {} + 50% { background-color: transparent; } + 100% {} +} +@keyframes blink { + 0% {} + 50% { background-color: transparent; } + 100% {} +} + +/* Can style cursor different in overwrite (non-insert) mode */ +.CodeMirror-overwrite .CodeMirror-cursor {} + +.cm-tab { display: inline-block; text-decoration: inherit; } + +.CodeMirror-rulers { + position: absolute; + left: 0; right: 0; top: -50px; bottom: 0; + overflow: hidden; +} +.CodeMirror-ruler { + border-left: 1px solid #ccc; + top: 0; bottom: 0; + position: absolute; +} + +/* DEFAULT THEME */ + +.cm-s-default .cm-header {color: blue;} +.cm-s-default .cm-quote {color: #090;} +.cm-negative {color: #d44;} +.cm-positive {color: #292;} +.cm-header, .cm-strong {font-weight: bold;} +.cm-em {font-style: italic;} +.cm-link {text-decoration: underline;} +.cm-strikethrough {text-decoration: line-through;} + +.cm-s-default .cm-keyword {color: #708;} +.cm-s-default .cm-atom {color: #219;} +.cm-s-default .cm-number {color: #164;} +.cm-s-default .cm-def {color: #00f;} +.cm-s-default .cm-variable, +.cm-s-default .cm-punctuation, +.cm-s-default .cm-property, +.cm-s-default .cm-operator {} +.cm-s-default .cm-variable-2 {color: #05a;} +.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;} +.cm-s-default .cm-comment {color: #a50;} +.cm-s-default .cm-string {color: #a11;} +.cm-s-default .cm-string-2 {color: #f50;} +.cm-s-default .cm-meta {color: #555;} +.cm-s-default .cm-qualifier {color: #555;} +.cm-s-default .cm-builtin {color: #30a;} +.cm-s-default .cm-bracket {color: #997;} +.cm-s-default .cm-tag {color: #170;} +.cm-s-default .cm-attribute {color: #00c;} +.cm-s-default .cm-hr {color: #999;} +.cm-s-default .cm-link {color: #00c;} + +.cm-s-default .cm-error {color: #f00;} +.cm-invalidchar {color: #f00;} + +.CodeMirror-composing { border-bottom: 2px solid; } + +/* Default styles for common addons */ + +div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;} +div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} +.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } +.CodeMirror-activeline-background {background: #e8f2ff;} + +/* STOP */ + +/* The rest of this file contains styles related to the mechanics of + the editor. You probably shouldn't touch them. */ + +.CodeMirror { + position: relative; + overflow: hidden; + background: white; +} + +.CodeMirror-scroll { + overflow: scroll !important; /* Things will break if this is overridden */ + /* 50px is the magic margin used to hide the element's real scrollbars */ + /* See overflow: hidden in .CodeMirror */ + margin-bottom: -50px; margin-right: -50px; + padding-bottom: 50px; + height: 100%; + outline: none; /* Prevent dragging from highlighting the element */ + position: relative; +} +.CodeMirror-sizer { + position: relative; + border-right: 50px solid transparent; +} + +/* The fake, visible scrollbars. Used to force redraw during scrolling + before actual scrolling happens, thus preventing shaking and + flickering artifacts. */ +.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { + position: absolute; + z-index: 6; + display: none; + outline: none; +} +.CodeMirror-vscrollbar { + right: 0; top: 0; + overflow-x: hidden; + overflow-y: scroll; +} +.CodeMirror-hscrollbar { + bottom: 0; left: 0; + overflow-y: hidden; + overflow-x: scroll; +} +.CodeMirror-scrollbar-filler { + right: 0; bottom: 0; +} +.CodeMirror-gutter-filler { + left: 0; bottom: 0; +} + +.CodeMirror-gutters { + position: absolute; left: 0; top: 0; + min-height: 100%; + z-index: 3; +} +.CodeMirror-gutter { + white-space: normal; + height: 100%; + display: inline-block; + vertical-align: top; + margin-bottom: -50px; +} +.CodeMirror-gutter-wrapper { + position: absolute; + z-index: 4; + background: none !important; + border: none !important; +} +.CodeMirror-gutter-background { + position: absolute; + top: 0; bottom: 0; + z-index: 4; +} +.CodeMirror-gutter-elt { + position: absolute; + cursor: default; + z-index: 4; +} +.CodeMirror-gutter-wrapper ::selection { background-color: transparent } +.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent } + +.CodeMirror-lines { + cursor: text; + min-height: 1px; /* prevents collapsing before first draw */ +} +.CodeMirror pre.CodeMirror-line, +.CodeMirror pre.CodeMirror-line-like { + /* Reset some styles that the rest of the page might have set */ + -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; + border-width: 0; + background: transparent; + font-family: inherit; + font-size: inherit; + margin: 0; + white-space: pre; + word-wrap: normal; + line-height: inherit; + color: inherit; + z-index: 2; + position: relative; + overflow: visible; + -webkit-tap-highlight-color: transparent; + -webkit-font-variant-ligatures: contextual; + font-variant-ligatures: contextual; +} +.CodeMirror-wrap pre.CodeMirror-line, +.CodeMirror-wrap pre.CodeMirror-line-like { + word-wrap: break-word; + white-space: pre-wrap; + word-break: normal; +} + +.CodeMirror-linebackground { + position: absolute; + left: 0; right: 0; top: 0; bottom: 0; + z-index: 0; +} + +.CodeMirror-linewidget { + position: relative; + z-index: 2; + padding: 0.1px; /* Force widget margins to stay inside of the container */ +} + +.CodeMirror-widget {} + +.CodeMirror-rtl pre { direction: rtl; } + +.CodeMirror-code { + outline: none; +} + +/* Force content-box sizing for the elements where we expect it */ +.CodeMirror-scroll, +.CodeMirror-sizer, +.CodeMirror-gutter, +.CodeMirror-gutters, +.CodeMirror-linenumber { + -moz-box-sizing: content-box; + box-sizing: content-box; +} + +.CodeMirror-measure { + position: absolute; + width: 100%; + height: 0; + overflow: hidden; + visibility: hidden; +} + +.CodeMirror-cursor { + position: absolute; + pointer-events: none; +} +.CodeMirror-measure pre { position: static; } + +div.CodeMirror-cursors { + visibility: hidden; + position: relative; + z-index: 3; +} +div.CodeMirror-dragcursors { + visibility: visible; +} + +.CodeMirror-focused div.CodeMirror-cursors { + visibility: visible; +} + +.CodeMirror-selected { background: #d9d9d9; } +.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } +.CodeMirror-crosshair { cursor: crosshair; } +.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } +.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } + +.cm-searching { + background-color: #ffa; + background-color: rgba(255, 255, 0, .4); +} + +/* Used to force a border model for a node */ +.cm-force-border { padding-right: .1px; } + +@media print { + /* Hide the cursor when printing */ + .CodeMirror div.CodeMirror-cursors { + visibility: hidden; + } +} + +/* See issue #2901 */ +.cm-tab-wrap-hack:after { content: ''; } + +/* Help users use markselection to safely style text background */ +span.CodeMirror-selectedtext { background: none; } + + +/* +file https://github.com/codemirror/CodeMirror/blob/5.62.0/addon/lint/lint.css +*/ +/* The lint marker gutter */ +.CodeMirror-lint-markers { + width: 16px; +} + +.CodeMirror-lint-tooltip { + background-color: #ffd; + border: 1px solid black; + border-radius: 4px 4px 4px 4px; + color: black; + font-family: monospace; + font-size: 10pt; + overflow: hidden; + padding: 2px 5px; + position: fixed; + white-space: pre; + white-space: pre-wrap; + z-index: 100; + max-width: 600px; + opacity: 0; + transition: opacity .4s; + -moz-transition: opacity .4s; + -webkit-transition: opacity .4s; + -o-transition: opacity .4s; + -ms-transition: opacity .4s; +} + +.CodeMirror-lint-mark { + background-position: left bottom; + background-repeat: repeat-x; +} + +.CodeMirror-lint-mark-warning { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII="); +} + +.CodeMirror-lint-mark-error { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg=="); +} + +.CodeMirror-lint-marker { + background-position: center center; + background-repeat: no-repeat; + cursor: pointer; + display: inline-block; + height: 16px; + width: 16px; + vertical-align: middle; + position: relative; +} + +.CodeMirror-lint-message { + padding-left: 18px; + background-position: top left; + background-repeat: no-repeat; +} + +.CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII="); +} + +.CodeMirror-lint-marker-error, .CodeMirror-lint-message-error { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII="); +} + +.CodeMirror-lint-marker-multiple { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC"); + background-repeat: no-repeat; + background-position: right bottom; + width: 100%; height: 100%; +} + +.CodeMirror-lint-line-error { + background-color: rgba(183, 76, 81, 0.08); +} + +.CodeMirror-lint-line-warning { + background-color: rgba(255, 211, 0, 0.1); +} + + +/* +file none +*/ +/*jslint-enable*/ diff --git a/asset-codemirror-rollup.js b/asset-codemirror-rollup.js new file mode 100644 index 000000000..f226b4218 --- /dev/null +++ b/asset-codemirror-rollup.js @@ -0,0 +1,11329 @@ +/*jslint-disable*/ +/* +shRawLibFetch +{ + "fetchList": [ + { + "comment": true, + "url": "https://github.com/codemirror/CodeMirror/blob/5.62.0/LICENSE" + }, + { + "url": "https://github.com/codemirror/CodeMirror/blob/5.62.0/codemirror.js", + "url2": "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/codemirror.js" + }, + { + "url": "https://github.com/codemirror/CodeMirror/blob/5.62.0/addon/edit/matchbrackets.js" + }, + { + "url": "https://github.com/codemirror/CodeMirror/blob/5.62.0/addon/edit/trailingspace.js" + }, + { + "url": "https://github.com/codemirror/CodeMirror/blob/5.62.0/addon/lint/lint.js" + }, + { + "url": "https://github.com/codemirror/CodeMirror/blob/5.62.0/mode/javascript/javascript.js" + } + ] +} +*/ + + +/* +repo https://github.com/codemirror/CodeMirror/tree/5.62.0 +committed 2021-06-21T07:13:20Z +*/ + + +/* +file https://github.com/codemirror/CodeMirror/blob/5.62.0/LICENSE +*/ +/* +MIT License + +Copyright (C) 2017 by Marijn Haverbeke and others + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + + +/* +file https://github.com/codemirror/CodeMirror/blob/5.62.0/codemirror.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +// This is CodeMirror (https://codemirror.net), a code editor +// implemented in JavaScript on top of the browser's DOM. +// +// You can find some technical background for some of the code below +// at http://marijnhaverbeke.nl/blog/#cm-internals . + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, global.CodeMirror = factory()); +}(this, (function () { 'use strict'; + + // Kludges for bugs and behavior differences that can't be feature + // detected are enabled based on userAgent etc sniffing. + var userAgent = navigator.userAgent; + var platform = navigator.platform; + + var gecko = /gecko\/\d/i.test(userAgent); + var ie_upto10 = /MSIE \d/.test(userAgent); + var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent); + var edge = /Edge\/(\d+)/.exec(userAgent); + var ie = ie_upto10 || ie_11up || edge; + var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]); + var webkit = !edge && /WebKit\//.test(userAgent); + var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent); + var chrome = !edge && /Chrome\//.test(userAgent); + var presto = /Opera\//.test(userAgent); + var safari = /Apple Computer/.test(navigator.vendor); + var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent); + var phantom = /PhantomJS/.test(userAgent); + + var ios = safari && (/Mobile\/\w+/.test(userAgent) || navigator.maxTouchPoints > 2); + var android = /Android/.test(userAgent); + // This is woefully incomplete. Suggestions for alternative methods welcome. + var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent); + var mac = ios || /Mac/.test(platform); + var chromeOS = /\bCrOS\b/.test(userAgent); + var windows = /win/i.test(platform); + + var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/); + if (presto_version) { presto_version = Number(presto_version[1]); } + if (presto_version && presto_version >= 15) { presto = false; webkit = true; } + // Some browsers use the wrong event properties to signal cmd/ctrl on OS X + var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11)); + var captureRightClick = gecko || (ie && ie_version >= 9); + + function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") } + + var rmClass = function(node, cls) { + var current = node.className; + var match = classTest(cls).exec(current); + if (match) { + var after = current.slice(match.index + match[0].length); + node.className = current.slice(0, match.index) + (after ? match[1] + after : ""); + } + }; + + function removeChildren(e) { + for (var count = e.childNodes.length; count > 0; --count) + { e.removeChild(e.firstChild); } + return e + } + + function removeChildrenAndAdd(parent, e) { + return removeChildren(parent).appendChild(e) + } + + function elt(tag, content, className, style) { + var e = document.createElement(tag); + if (className) { e.className = className; } + if (style) { e.style.cssText = style; } + if (typeof content == "string") { e.appendChild(document.createTextNode(content)); } + else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } } + return e + } + // wrapper for elt, which removes the elt from the accessibility tree + function eltP(tag, content, className, style) { + var e = elt(tag, content, className, style); + e.setAttribute("role", "presentation"); + return e + } + + var range; + if (document.createRange) { range = function(node, start, end, endNode) { + var r = document.createRange(); + r.setEnd(endNode || node, end); + r.setStart(node, start); + return r + }; } + else { range = function(node, start, end) { + var r = document.body.createTextRange(); + try { r.moveToElementText(node.parentNode); } + catch(e) { return r } + r.collapse(true); + r.moveEnd("character", end); + r.moveStart("character", start); + return r + }; } + + function contains(parent, child) { + if (child.nodeType == 3) // Android browser always returns false when child is a textnode + { child = child.parentNode; } + if (parent.contains) + { return parent.contains(child) } + do { + if (child.nodeType == 11) { child = child.host; } + if (child == parent) { return true } + } while (child = child.parentNode) + } + + function activeElt() { + // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement. + // IE < 10 will throw when accessed while the page is loading or in an iframe. + // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable. + var activeElement; + try { + activeElement = document.activeElement; + } catch(e) { + activeElement = document.body || null; + } + while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement) + { activeElement = activeElement.shadowRoot.activeElement; } + return activeElement + } + + function addClass(node, cls) { + var current = node.className; + if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; } + } + function joinClasses(a, b) { + var as = a.split(" "); + for (var i = 0; i < as.length; i++) + { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } } + return b + } + + var selectInput = function(node) { node.select(); }; + if (ios) // Mobile Safari apparently has a bug where select() is broken. + { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; } + else if (ie) // Suppress mysterious IE10 errors + { selectInput = function(node) { try { node.select(); } catch(_e) {} }; } + + function bind(f) { + var args = Array.prototype.slice.call(arguments, 1); + return function(){return f.apply(null, args)} + } + + function copyObj(obj, target, overwrite) { + if (!target) { target = {}; } + for (var prop in obj) + { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop))) + { target[prop] = obj[prop]; } } + return target + } + + // Counts the column offset in a string, taking tabs into account. + // Used mostly to find indentation. + function countColumn(string, end, tabSize, startIndex, startValue) { + if (end == null) { + end = string.search(/[^\s\u00a0]/); + if (end == -1) { end = string.length; } + } + for (var i = startIndex || 0, n = startValue || 0;;) { + var nextTab = string.indexOf("\t", i); + if (nextTab < 0 || nextTab >= end) + { return n + (end - i) } + n += nextTab - i; + n += tabSize - (n % tabSize); + i = nextTab + 1; + } + } + + var Delayed = function() { + this.id = null; + this.f = null; + this.time = 0; + this.handler = bind(this.onTimeout, this); + }; + Delayed.prototype.onTimeout = function (self) { + self.id = 0; + if (self.time <= +new Date) { + self.f(); + } else { + setTimeout(self.handler, self.time - +new Date); + } + }; + Delayed.prototype.set = function (ms, f) { + this.f = f; + var time = +new Date + ms; + if (!this.id || time < this.time) { + clearTimeout(this.id); + this.id = setTimeout(this.handler, ms); + this.time = time; + } + }; + + function indexOf(array, elt) { + for (var i = 0; i < array.length; ++i) + { if (array[i] == elt) { return i } } + return -1 + } + + // Number of pixels added to scroller and sizer to hide scrollbar + var scrollerGap = 50; + + // Returned or thrown by various protocols to signal 'I'm not + // handling this'. + var Pass = {toString: function(){return "CodeMirror.Pass"}}; + + // Reused option objects for setSelection & friends + var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"}; + + // The inverse of countColumn -- find the offset that corresponds to + // a particular column. + function findColumn(string, goal, tabSize) { + for (var pos = 0, col = 0;;) { + var nextTab = string.indexOf("\t", pos); + if (nextTab == -1) { nextTab = string.length; } + var skipped = nextTab - pos; + if (nextTab == string.length || col + skipped >= goal) + { return pos + Math.min(skipped, goal - col) } + col += nextTab - pos; + col += tabSize - (col % tabSize); + pos = nextTab + 1; + if (col >= goal) { return pos } + } + } + + var spaceStrs = [""]; + function spaceStr(n) { + while (spaceStrs.length <= n) + { spaceStrs.push(lst(spaceStrs) + " "); } + return spaceStrs[n] + } + + function lst(arr) { return arr[arr.length-1] } + + function map(array, f) { + var out = []; + for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); } + return out + } + + function insertSorted(array, value, score) { + var pos = 0, priority = score(value); + while (pos < array.length && score(array[pos]) <= priority) { pos++; } + array.splice(pos, 0, value); + } + + function nothing() {} + + function createObj(base, props) { + var inst; + if (Object.create) { + inst = Object.create(base); + } else { + nothing.prototype = base; + inst = new nothing(); + } + if (props) { copyObj(props, inst); } + return inst + } + + var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; + function isWordCharBasic(ch) { + return /\w/.test(ch) || ch > "\x80" && + (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)) + } + function isWordChar(ch, helper) { + if (!helper) { return isWordCharBasic(ch) } + if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true } + return helper.test(ch) + } + + function isEmpty(obj) { + for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } } + return true + } + + // Extending unicode characters. A series of a non-extending char + + // any number of extending chars is treated as a single unit as far + // as editing and measuring is concerned. This is not fully correct, + // since some scripts/fonts/browsers also treat other configurations + // of code points as a group. + var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/; + function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) } + + // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range. + function skipExtendingChars(str, pos, dir) { + while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; } + return pos + } + + // Returns the value from the range [`from`; `to`] that satisfies + // `pred` and is closest to `from`. Assumes that at least `to` + // satisfies `pred`. Supports `from` being greater than `to`. + function findFirst(pred, from, to) { + // At any point we are certain `to` satisfies `pred`, don't know + // whether `from` does. + var dir = from > to ? -1 : 1; + for (;;) { + if (from == to) { return from } + var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF); + if (mid == from) { return pred(mid) ? from : to } + if (pred(mid)) { to = mid; } + else { from = mid + dir; } + } + } + + // BIDI HELPERS + + function iterateBidiSections(order, from, to, f) { + if (!order) { return f(from, to, "ltr", 0) } + var found = false; + for (var i = 0; i < order.length; ++i) { + var part = order[i]; + if (part.from < to && part.to > from || from == to && part.to == from) { + f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i); + found = true; + } + } + if (!found) { f(from, to, "ltr"); } + } + + var bidiOther = null; + function getBidiPartAt(order, ch, sticky) { + var found; + bidiOther = null; + for (var i = 0; i < order.length; ++i) { + var cur = order[i]; + if (cur.from < ch && cur.to > ch) { return i } + if (cur.to == ch) { + if (cur.from != cur.to && sticky == "before") { found = i; } + else { bidiOther = i; } + } + if (cur.from == ch) { + if (cur.from != cur.to && sticky != "before") { found = i; } + else { bidiOther = i; } + } + } + return found != null ? found : bidiOther + } + + // Bidirectional ordering algorithm + // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm + // that this (partially) implements. + + // One-char codes used for character types: + // L (L): Left-to-Right + // R (R): Right-to-Left + // r (AL): Right-to-Left Arabic + // 1 (EN): European Number + // + (ES): European Number Separator + // % (ET): European Number Terminator + // n (AN): Arabic Number + // , (CS): Common Number Separator + // m (NSM): Non-Spacing Mark + // b (BN): Boundary Neutral + // s (B): Paragraph Separator + // t (S): Segment Separator + // w (WS): Whitespace + // N (ON): Other Neutrals + + // Returns null if characters are ordered as they appear + // (left-to-right), or an array of sections ({from, to, level} + // objects) in the order in which they occur visually. + var bidiOrdering = (function() { + // Character types for codepoints 0 to 0xff + var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"; + // Character types for codepoints 0x600 to 0x6f9 + var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111"; + function charType(code) { + if (code <= 0xf7) { return lowTypes.charAt(code) } + else if (0x590 <= code && code <= 0x5f4) { return "R" } + else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) } + else if (0x6ee <= code && code <= 0x8ac) { return "r" } + else if (0x2000 <= code && code <= 0x200b) { return "w" } + else if (code == 0x200c) { return "b" } + else { return "L" } + } + + var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/; + var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/; + + function BidiSpan(level, from, to) { + this.level = level; + this.from = from; this.to = to; + } + + return function(str, direction) { + var outerType = direction == "ltr" ? "L" : "R"; + + if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false } + var len = str.length, types = []; + for (var i = 0; i < len; ++i) + { types.push(charType(str.charCodeAt(i))); } + + // W1. Examine each non-spacing mark (NSM) in the level run, and + // change the type of the NSM to the type of the previous + // character. If the NSM is at the start of the level run, it will + // get the type of sor. + for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) { + var type = types[i$1]; + if (type == "m") { types[i$1] = prev; } + else { prev = type; } + } + + // W2. Search backwards from each instance of a European number + // until the first strong type (R, L, AL, or sor) is found. If an + // AL is found, change the type of the European number to Arabic + // number. + // W3. Change all ALs to R. + for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) { + var type$1 = types[i$2]; + if (type$1 == "1" && cur == "r") { types[i$2] = "n"; } + else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } } + } + + // W4. A single European separator between two European numbers + // changes to a European number. A single common separator between + // two numbers of the same type changes to that type. + for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) { + var type$2 = types[i$3]; + if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; } + else if (type$2 == "," && prev$1 == types[i$3+1] && + (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; } + prev$1 = type$2; + } + + // W5. A sequence of European terminators adjacent to European + // numbers changes to all European numbers. + // W6. Otherwise, separators and terminators change to Other + // Neutral. + for (var i$4 = 0; i$4 < len; ++i$4) { + var type$3 = types[i$4]; + if (type$3 == ",") { types[i$4] = "N"; } + else if (type$3 == "%") { + var end = (void 0); + for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {} + var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"; + for (var j = i$4; j < end; ++j) { types[j] = replace; } + i$4 = end - 1; + } + } + + // W7. Search backwards from each instance of a European number + // until the first strong type (R, L, or sor) is found. If an L is + // found, then change the type of the European number to L. + for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) { + var type$4 = types[i$5]; + if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; } + else if (isStrong.test(type$4)) { cur$1 = type$4; } + } + + // N1. A sequence of neutrals takes the direction of the + // surrounding strong text if the text on both sides has the same + // direction. European and Arabic numbers act as if they were R in + // terms of their influence on neutrals. Start-of-level-run (sor) + // and end-of-level-run (eor) are used at level run boundaries. + // N2. Any remaining neutrals take the embedding direction. + for (var i$6 = 0; i$6 < len; ++i$6) { + if (isNeutral.test(types[i$6])) { + var end$1 = (void 0); + for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {} + var before = (i$6 ? types[i$6-1] : outerType) == "L"; + var after = (end$1 < len ? types[end$1] : outerType) == "L"; + var replace$1 = before == after ? (before ? "L" : "R") : outerType; + for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; } + i$6 = end$1 - 1; + } + } + + // Here we depart from the documented algorithm, in order to avoid + // building up an actual levels array. Since there are only three + // levels (0, 1, 2) in an implementation that doesn't take + // explicit embedding into account, we can build up the order on + // the fly, without following the level-based algorithm. + var order = [], m; + for (var i$7 = 0; i$7 < len;) { + if (countsAsLeft.test(types[i$7])) { + var start = i$7; + for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {} + order.push(new BidiSpan(0, start, i$7)); + } else { + var pos = i$7, at = order.length, isRTL = direction == "rtl" ? 1 : 0; + for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {} + for (var j$2 = pos; j$2 < i$7;) { + if (countsAsNum.test(types[j$2])) { + if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); at += isRTL; } + var nstart = j$2; + for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {} + order.splice(at, 0, new BidiSpan(2, nstart, j$2)); + at += isRTL; + pos = j$2; + } else { ++j$2; } + } + if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); } + } + } + if (direction == "ltr") { + if (order[0].level == 1 && (m = str.match(/^\s+/))) { + order[0].from = m[0].length; + order.unshift(new BidiSpan(0, 0, m[0].length)); + } + if (lst(order).level == 1 && (m = str.match(/\s+$/))) { + lst(order).to -= m[0].length; + order.push(new BidiSpan(0, len - m[0].length, len)); + } + } + + return direction == "rtl" ? order.reverse() : order + } + })(); + + // Get the bidi ordering for the given line (and cache it). Returns + // false for lines that are fully left-to-right, and an array of + // BidiSpan objects otherwise. + function getOrder(line, direction) { + var order = line.order; + if (order == null) { order = line.order = bidiOrdering(line.text, direction); } + return order + } + + // EVENT HANDLING + + // Lightweight event framework. on/off also work on DOM nodes, + // registering native DOM handlers. + + var noHandlers = []; + + var on = function(emitter, type, f) { + if (emitter.addEventListener) { + emitter.addEventListener(type, f, false); + } else if (emitter.attachEvent) { + emitter.attachEvent("on" + type, f); + } else { + var map = emitter._handlers || (emitter._handlers = {}); + map[type] = (map[type] || noHandlers).concat(f); + } + }; + + function getHandlers(emitter, type) { + return emitter._handlers && emitter._handlers[type] || noHandlers + } + + function off(emitter, type, f) { + if (emitter.removeEventListener) { + emitter.removeEventListener(type, f, false); + } else if (emitter.detachEvent) { + emitter.detachEvent("on" + type, f); + } else { + var map = emitter._handlers, arr = map && map[type]; + if (arr) { + var index = indexOf(arr, f); + if (index > -1) + { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)); } + } + } + } + + function signal(emitter, type /*, values...*/) { + var handlers = getHandlers(emitter, type); + if (!handlers.length) { return } + var args = Array.prototype.slice.call(arguments, 2); + for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); } + } + + // The DOM events that CodeMirror handles can be overridden by + // registering a (non-DOM) handler on the editor for the event name, + // and preventDefault-ing the event in that handler. + function signalDOMEvent(cm, e, override) { + if (typeof e == "string") + { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; } + signal(cm, override || e.type, cm, e); + return e_defaultPrevented(e) || e.codemirrorIgnore + } + + function signalCursorActivity(cm) { + var arr = cm._handlers && cm._handlers.cursorActivity; + if (!arr) { return } + var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []); + for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1) + { set.push(arr[i]); } } + } + + function hasHandler(emitter, type) { + return getHandlers(emitter, type).length > 0 + } + + // Add on and off methods to a constructor's prototype, to make + // registering events on such objects more convenient. + function eventMixin(ctor) { + ctor.prototype.on = function(type, f) {on(this, type, f);}; + ctor.prototype.off = function(type, f) {off(this, type, f);}; + } + + // Due to the fact that we still support jurassic IE versions, some + // compatibility wrappers are needed. + + function e_preventDefault(e) { + if (e.preventDefault) { e.preventDefault(); } + else { e.returnValue = false; } + } + function e_stopPropagation(e) { + if (e.stopPropagation) { e.stopPropagation(); } + else { e.cancelBubble = true; } + } + function e_defaultPrevented(e) { + return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false + } + function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);} + + function e_target(e) {return e.target || e.srcElement} + function e_button(e) { + var b = e.which; + if (b == null) { + if (e.button & 1) { b = 1; } + else if (e.button & 2) { b = 3; } + else if (e.button & 4) { b = 2; } + } + if (mac && e.ctrlKey && b == 1) { b = 3; } + return b + } + + // Detect drag-and-drop + var dragAndDrop = function() { + // There is *some* kind of drag-and-drop support in IE6-8, but I + // couldn't get it to work yet. + if (ie && ie_version < 9) { return false } + var div = elt('div'); + return "draggable" in div || "dragDrop" in div + }(); + + var zwspSupported; + function zeroWidthElement(measure) { + if (zwspSupported == null) { + var test = elt("span", "\u200b"); + removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")])); + if (measure.firstChild.offsetHeight != 0) + { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); } + } + var node = zwspSupported ? elt("span", "\u200b") : + elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px"); + node.setAttribute("cm-text", ""); + return node + } + + // Feature-detect IE's crummy client rect reporting for bidi text + var badBidiRects; + function hasBadBidiRects(measure) { + if (badBidiRects != null) { return badBidiRects } + var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA")); + var r0 = range(txt, 0, 1).getBoundingClientRect(); + var r1 = range(txt, 1, 2).getBoundingClientRect(); + removeChildren(measure); + if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780) + return badBidiRects = (r1.right - r0.right < 3) + } + + // See if "".split is the broken IE version, if so, provide an + // alternative way to split lines. + var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) { + var pos = 0, result = [], l = string.length; + while (pos <= l) { + var nl = string.indexOf("\n", pos); + if (nl == -1) { nl = string.length; } + var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl); + var rt = line.indexOf("\r"); + if (rt != -1) { + result.push(line.slice(0, rt)); + pos += rt + 1; + } else { + result.push(line); + pos = nl + 1; + } + } + return result + } : function (string) { return string.split(/\r\n?|\n/); }; + + var hasSelection = window.getSelection ? function (te) { + try { return te.selectionStart != te.selectionEnd } + catch(e) { return false } + } : function (te) { + var range; + try {range = te.ownerDocument.selection.createRange();} + catch(e) {} + if (!range || range.parentElement() != te) { return false } + return range.compareEndPoints("StartToEnd", range) != 0 + }; + + var hasCopyEvent = (function () { + var e = elt("div"); + if ("oncopy" in e) { return true } + e.setAttribute("oncopy", "return;"); + return typeof e.oncopy == "function" + })(); + + var badZoomedRects = null; + function hasBadZoomedRects(measure) { + if (badZoomedRects != null) { return badZoomedRects } + var node = removeChildrenAndAdd(measure, elt("span", "x")); + var normal = node.getBoundingClientRect(); + var fromRange = range(node, 0, 1).getBoundingClientRect(); + return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1 + } + + // Known modes, by name and by MIME + var modes = {}, mimeModes = {}; + + // Extra arguments are stored as the mode's dependencies, which is + // used by (legacy) mechanisms like loadmode.js to automatically + // load a mode. (Preferred mechanism is the require/define calls.) + function defineMode(name, mode) { + if (arguments.length > 2) + { mode.dependencies = Array.prototype.slice.call(arguments, 2); } + modes[name] = mode; + } + + function defineMIME(mime, spec) { + mimeModes[mime] = spec; + } + + // Given a MIME type, a {name, ...options} config object, or a name + // string, return a mode config object. + function resolveMode(spec) { + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { + spec = mimeModes[spec]; + } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { + var found = mimeModes[spec.name]; + if (typeof found == "string") { found = {name: found}; } + spec = createObj(found, spec); + spec.name = found.name; + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { + return resolveMode("application/xml") + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) { + return resolveMode("application/json") + } + if (typeof spec == "string") { return {name: spec} } + else { return spec || {name: "null"} } + } + + // Given a mode spec (anything that resolveMode accepts), find and + // initialize an actual mode object. + function getMode(options, spec) { + spec = resolveMode(spec); + var mfactory = modes[spec.name]; + if (!mfactory) { return getMode(options, "text/plain") } + var modeObj = mfactory(options, spec); + if (modeExtensions.hasOwnProperty(spec.name)) { + var exts = modeExtensions[spec.name]; + for (var prop in exts) { + if (!exts.hasOwnProperty(prop)) { continue } + if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; } + modeObj[prop] = exts[prop]; + } + } + modeObj.name = spec.name; + if (spec.helperType) { modeObj.helperType = spec.helperType; } + if (spec.modeProps) { for (var prop$1 in spec.modeProps) + { modeObj[prop$1] = spec.modeProps[prop$1]; } } + + return modeObj + } + + // This can be used to attach properties to mode objects from + // outside the actual mode definition. + var modeExtensions = {}; + function extendMode(mode, properties) { + var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); + copyObj(properties, exts); + } + + function copyState(mode, state) { + if (state === true) { return state } + if (mode.copyState) { return mode.copyState(state) } + var nstate = {}; + for (var n in state) { + var val = state[n]; + if (val instanceof Array) { val = val.concat([]); } + nstate[n] = val; + } + return nstate + } + + // Given a mode and a state (for that mode), find the inner mode and + // state at the position that the state refers to. + function innerMode(mode, state) { + var info; + while (mode.innerMode) { + info = mode.innerMode(state); + if (!info || info.mode == mode) { break } + state = info.state; + mode = info.mode; + } + return info || {mode: mode, state: state} + } + + function startState(mode, a1, a2) { + return mode.startState ? mode.startState(a1, a2) : true + } + + // STRING STREAM + + // Fed to the mode parsers, provides helper functions to make + // parsers more succinct. + + var StringStream = function(string, tabSize, lineOracle) { + this.pos = this.start = 0; + this.string = string; + this.tabSize = tabSize || 8; + this.lastColumnPos = this.lastColumnValue = 0; + this.lineStart = 0; + this.lineOracle = lineOracle; + }; + + StringStream.prototype.eol = function () {return this.pos >= this.string.length}; + StringStream.prototype.sol = function () {return this.pos == this.lineStart}; + StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined}; + StringStream.prototype.next = function () { + if (this.pos < this.string.length) + { return this.string.charAt(this.pos++) } + }; + StringStream.prototype.eat = function (match) { + var ch = this.string.charAt(this.pos); + var ok; + if (typeof match == "string") { ok = ch == match; } + else { ok = ch && (match.test ? match.test(ch) : match(ch)); } + if (ok) {++this.pos; return ch} + }; + StringStream.prototype.eatWhile = function (match) { + var start = this.pos; + while (this.eat(match)){} + return this.pos > start + }; + StringStream.prototype.eatSpace = function () { + var start = this.pos; + while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; } + return this.pos > start + }; + StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;}; + StringStream.prototype.skipTo = function (ch) { + var found = this.string.indexOf(ch, this.pos); + if (found > -1) {this.pos = found; return true} + }; + StringStream.prototype.backUp = function (n) {this.pos -= n;}; + StringStream.prototype.column = function () { + if (this.lastColumnPos < this.start) { + this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue); + this.lastColumnPos = this.start; + } + return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) + }; + StringStream.prototype.indentation = function () { + return countColumn(this.string, null, this.tabSize) - + (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) + }; + StringStream.prototype.match = function (pattern, consume, caseInsensitive) { + if (typeof pattern == "string") { + var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; }; + var substr = this.string.substr(this.pos, pattern.length); + if (cased(substr) == cased(pattern)) { + if (consume !== false) { this.pos += pattern.length; } + return true + } + } else { + var match = this.string.slice(this.pos).match(pattern); + if (match && match.index > 0) { return null } + if (match && consume !== false) { this.pos += match[0].length; } + return match + } + }; + StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)}; + StringStream.prototype.hideFirstChars = function (n, inner) { + this.lineStart += n; + try { return inner() } + finally { this.lineStart -= n; } + }; + StringStream.prototype.lookAhead = function (n) { + var oracle = this.lineOracle; + return oracle && oracle.lookAhead(n) + }; + StringStream.prototype.baseToken = function () { + var oracle = this.lineOracle; + return oracle && oracle.baseToken(this.pos) + }; + + // Find the line object corresponding to the given line number. + function getLine(doc, n) { + n -= doc.first; + if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") } + var chunk = doc; + while (!chunk.lines) { + for (var i = 0;; ++i) { + var child = chunk.children[i], sz = child.chunkSize(); + if (n < sz) { chunk = child; break } + n -= sz; + } + } + return chunk.lines[n] + } + + // Get the part of a document between two positions, as an array of + // strings. + function getBetween(doc, start, end) { + var out = [], n = start.line; + doc.iter(start.line, end.line + 1, function (line) { + var text = line.text; + if (n == end.line) { text = text.slice(0, end.ch); } + if (n == start.line) { text = text.slice(start.ch); } + out.push(text); + ++n; + }); + return out + } + // Get the lines between from and to, as array of strings. + function getLines(doc, from, to) { + var out = []; + doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value + return out + } + + // Update the height of a line, propagating the height change + // upwards to parent nodes. + function updateLineHeight(line, height) { + var diff = height - line.height; + if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } } + } + + // Given a line object, find its line number by walking up through + // its parent links. + function lineNo(line) { + if (line.parent == null) { return null } + var cur = line.parent, no = indexOf(cur.lines, line); + for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { + for (var i = 0;; ++i) { + if (chunk.children[i] == cur) { break } + no += chunk.children[i].chunkSize(); + } + } + return no + cur.first + } + + // Find the line at the given vertical position, using the height + // information in the document tree. + function lineAtHeight(chunk, h) { + var n = chunk.first; + outer: do { + for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) { + var child = chunk.children[i$1], ch = child.height; + if (h < ch) { chunk = child; continue outer } + h -= ch; + n += child.chunkSize(); + } + return n + } while (!chunk.lines) + var i = 0; + for (; i < chunk.lines.length; ++i) { + var line = chunk.lines[i], lh = line.height; + if (h < lh) { break } + h -= lh; + } + return n + i + } + + function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size} + + function lineNumberFor(options, i) { + return String(options.lineNumberFormatter(i + options.firstLineNumber)) + } + + // A Pos instance represents a position within the text. + function Pos(line, ch, sticky) { + if ( sticky === void 0 ) sticky = null; + + if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) } + this.line = line; + this.ch = ch; + this.sticky = sticky; + } + + // Compare two positions, return 0 if they are the same, a negative + // number when a is less, and a positive number otherwise. + function cmp(a, b) { return a.line - b.line || a.ch - b.ch } + + function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 } + + function copyPos(x) {return Pos(x.line, x.ch)} + function maxPos(a, b) { return cmp(a, b) < 0 ? b : a } + function minPos(a, b) { return cmp(a, b) < 0 ? a : b } + + // Most of the external API clips given positions to make sure they + // actually exist within the document. + function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))} + function clipPos(doc, pos) { + if (pos.line < doc.first) { return Pos(doc.first, 0) } + var last = doc.first + doc.size - 1; + if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) } + return clipToLen(pos, getLine(doc, pos.line).text.length) + } + function clipToLen(pos, linelen) { + var ch = pos.ch; + if (ch == null || ch > linelen) { return Pos(pos.line, linelen) } + else if (ch < 0) { return Pos(pos.line, 0) } + else { return pos } + } + function clipPosArray(doc, array) { + var out = []; + for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); } + return out + } + + var SavedContext = function(state, lookAhead) { + this.state = state; + this.lookAhead = lookAhead; + }; + + var Context = function(doc, state, line, lookAhead) { + this.state = state; + this.doc = doc; + this.line = line; + this.maxLookAhead = lookAhead || 0; + this.baseTokens = null; + this.baseTokenPos = 1; + }; + + Context.prototype.lookAhead = function (n) { + var line = this.doc.getLine(this.line + n); + if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; } + return line + }; + + Context.prototype.baseToken = function (n) { + if (!this.baseTokens) { return null } + while (this.baseTokens[this.baseTokenPos] <= n) + { this.baseTokenPos += 2; } + var type = this.baseTokens[this.baseTokenPos + 1]; + return {type: type && type.replace(/( |^)overlay .*/, ""), + size: this.baseTokens[this.baseTokenPos] - n} + }; + + Context.prototype.nextLine = function () { + this.line++; + if (this.maxLookAhead > 0) { this.maxLookAhead--; } + }; + + Context.fromSaved = function (doc, saved, line) { + if (saved instanceof SavedContext) + { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) } + else + { return new Context(doc, copyState(doc.mode, saved), line) } + }; + + Context.prototype.save = function (copy) { + var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state; + return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state + }; + + + // Compute a style array (an array starting with a mode generation + // -- for invalidation -- followed by pairs of end positions and + // style strings), which is used to highlight the tokens on the + // line. + function highlightLine(cm, line, context, forceToEnd) { + // A styles array always starts with a number identifying the + // mode/overlays that it is based on (for easy invalidation). + var st = [cm.state.modeGen], lineClasses = {}; + // Compute the base array of styles + runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); }, + lineClasses, forceToEnd); + var state = context.state; + + // Run overlays, adjust style array. + var loop = function ( o ) { + context.baseTokens = st; + var overlay = cm.state.overlays[o], i = 1, at = 0; + context.state = true; + runMode(cm, line.text, overlay.mode, context, function (end, style) { + var start = i; + // Ensure there's a token end at the current position, and that i points at it + while (at < end) { + var i_end = st[i]; + if (i_end > end) + { st.splice(i, 1, end, st[i+1], i_end); } + i += 2; + at = Math.min(end, i_end); + } + if (!style) { return } + if (overlay.opaque) { + st.splice(start, i - start, end, "overlay " + style); + i = start + 2; + } else { + for (; start < i; start += 2) { + var cur = st[start+1]; + st[start+1] = (cur ? cur + " " : "") + "overlay " + style; + } + } + }, lineClasses); + context.state = state; + context.baseTokens = null; + context.baseTokenPos = 1; + }; + + for (var o = 0; o < cm.state.overlays.length; ++o) loop( o ); + + return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null} + } + + function getLineStyles(cm, line, updateFrontier) { + if (!line.styles || line.styles[0] != cm.state.modeGen) { + var context = getContextBefore(cm, lineNo(line)); + var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state); + var result = highlightLine(cm, line, context); + if (resetState) { context.state = resetState; } + line.stateAfter = context.save(!resetState); + line.styles = result.styles; + if (result.classes) { line.styleClasses = result.classes; } + else if (line.styleClasses) { line.styleClasses = null; } + if (updateFrontier === cm.doc.highlightFrontier) + { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); } + } + return line.styles + } + + function getContextBefore(cm, n, precise) { + var doc = cm.doc, display = cm.display; + if (!doc.mode.startState) { return new Context(doc, true, n) } + var start = findStartLine(cm, n, precise); + var saved = start > doc.first && getLine(doc, start - 1).stateAfter; + var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start); + + doc.iter(start, n, function (line) { + processLine(cm, line.text, context); + var pos = context.line; + line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null; + context.nextLine(); + }); + if (precise) { doc.modeFrontier = context.line; } + return context + } + + // Lightweight form of highlight -- proceed over this line and + // update state, but don't save a style array. Used for lines that + // aren't currently visible. + function processLine(cm, text, context, startAt) { + var mode = cm.doc.mode; + var stream = new StringStream(text, cm.options.tabSize, context); + stream.start = stream.pos = startAt || 0; + if (text == "") { callBlankLine(mode, context.state); } + while (!stream.eol()) { + readToken(mode, stream, context.state); + stream.start = stream.pos; + } + } + + function callBlankLine(mode, state) { + if (mode.blankLine) { return mode.blankLine(state) } + if (!mode.innerMode) { return } + var inner = innerMode(mode, state); + if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) } + } + + function readToken(mode, stream, state, inner) { + for (var i = 0; i < 10; i++) { + if (inner) { inner[0] = innerMode(mode, state).mode; } + var style = mode.token(stream, state); + if (stream.pos > stream.start) { return style } + } + throw new Error("Mode " + mode.name + " failed to advance stream.") + } + + var Token = function(stream, type, state) { + this.start = stream.start; this.end = stream.pos; + this.string = stream.current(); + this.type = type || null; + this.state = state; + }; + + // Utility for getTokenAt and getLineTokens + function takeToken(cm, pos, precise, asArray) { + var doc = cm.doc, mode = doc.mode, style; + pos = clipPos(doc, pos); + var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise); + var stream = new StringStream(line.text, cm.options.tabSize, context), tokens; + if (asArray) { tokens = []; } + while ((asArray || stream.pos < pos.ch) && !stream.eol()) { + stream.start = stream.pos; + style = readToken(mode, stream, context.state); + if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); } + } + return asArray ? tokens : new Token(stream, style, context.state) + } + + function extractLineClasses(type, output) { + if (type) { for (;;) { + var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/); + if (!lineClass) { break } + type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length); + var prop = lineClass[1] ? "bgClass" : "textClass"; + if (output[prop] == null) + { output[prop] = lineClass[2]; } + else if (!(new RegExp("(?:^|\\s)" + lineClass[2] + "(?:$|\\s)")).test(output[prop])) + { output[prop] += " " + lineClass[2]; } + } } + return type + } + + // Run the given mode's parser over a line, calling f for each token. + function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) { + var flattenSpans = mode.flattenSpans; + if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; } + var curStart = 0, curStyle = null; + var stream = new StringStream(text, cm.options.tabSize, context), style; + var inner = cm.options.addModeClass && [null]; + if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); } + while (!stream.eol()) { + if (stream.pos > cm.options.maxHighlightLength) { + flattenSpans = false; + if (forceToEnd) { processLine(cm, text, context, stream.pos); } + stream.pos = text.length; + style = null; + } else { + style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses); + } + if (inner) { + var mName = inner[0].name; + if (mName) { style = "m-" + (style ? mName + " " + style : mName); } + } + if (!flattenSpans || curStyle != style) { + while (curStart < stream.start) { + curStart = Math.min(stream.start, curStart + 5000); + f(curStart, curStyle); + } + curStyle = style; + } + stream.start = stream.pos; + } + while (curStart < stream.pos) { + // Webkit seems to refuse to render text nodes longer than 57444 + // characters, and returns inaccurate measurements in nodes + // starting around 5000 chars. + var pos = Math.min(stream.pos, curStart + 5000); + f(pos, curStyle); + curStart = pos; + } + } + + // Finds the line to start with when starting a parse. Tries to + // find a line with a stateAfter, so that it can start with a + // valid state. If that fails, it returns the line with the + // smallest indentation, which tends to need the least context to + // parse correctly. + function findStartLine(cm, n, precise) { + var minindent, minline, doc = cm.doc; + var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100); + for (var search = n; search > lim; --search) { + if (search <= doc.first) { return doc.first } + var line = getLine(doc, search - 1), after = line.stateAfter; + if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier)) + { return search } + var indented = countColumn(line.text, null, cm.options.tabSize); + if (minline == null || minindent > indented) { + minline = search - 1; + minindent = indented; + } + } + return minline + } + + function retreatFrontier(doc, n) { + doc.modeFrontier = Math.min(doc.modeFrontier, n); + if (doc.highlightFrontier < n - 10) { return } + var start = doc.first; + for (var line = n - 1; line > start; line--) { + var saved = getLine(doc, line).stateAfter; + // change is on 3 + // state on line 1 looked ahead 2 -- so saw 3 + // test 1 + 2 < 3 should cover this + if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) { + start = line + 1; + break + } + } + doc.highlightFrontier = Math.min(doc.highlightFrontier, start); + } + + // Optimize some code when these features are not used. + var sawReadOnlySpans = false, sawCollapsedSpans = false; + + function seeReadOnlySpans() { + sawReadOnlySpans = true; + } + + function seeCollapsedSpans() { + sawCollapsedSpans = true; + } + + // TEXTMARKER SPANS + + function MarkedSpan(marker, from, to) { + this.marker = marker; + this.from = from; this.to = to; + } + + // Search an array of spans for a span matching the given marker. + function getMarkedSpanFor(spans, marker) { + if (spans) { for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.marker == marker) { return span } + } } + } + + // Remove a span from an array, returning undefined if no spans are + // left (we don't store arrays for lines without spans). + function removeMarkedSpan(spans, span) { + var r; + for (var i = 0; i < spans.length; ++i) + { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } } + return r + } + + // Add a span to a line. + function addMarkedSpan(line, span, op) { + var inThisOp = op && window.WeakSet && (op.markedSpans || (op.markedSpans = new WeakSet)); + if (inThisOp && inThisOp.has(line.markedSpans)) { + line.markedSpans.push(span); + } else { + line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]; + if (inThisOp) { inThisOp.add(line.markedSpans); } + } + span.marker.attachLine(line); + } + + // Used for the algorithm that adjusts markers for a change in the + // document. These functions cut an array of spans at a given + // character position, returning an array of remaining chunks (or + // undefined if nothing remains). + function markedSpansBefore(old, startCh, isInsert) { + var nw; + if (old) { for (var i = 0; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh); + if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) { + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh) + ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to)); + } + } } + return nw + } + function markedSpansAfter(old, endCh, isInsert) { + var nw; + if (old) { for (var i = 0; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh); + if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) { + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh) + ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh, + span.to == null ? null : span.to - endCh)); + } + } } + return nw + } + + // Given a change object, compute the new set of marker spans that + // cover the line in which the change took place. Removes spans + // entirely within the change, reconnects spans belonging to the + // same marker that appear on both sides of the change, and cuts off + // spans partially within the change. Returns an array of span + // arrays with one element for each line in (after) the change. + function stretchSpansOverChange(doc, change) { + if (change.full) { return null } + var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans; + var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans; + if (!oldFirst && !oldLast) { return null } + + var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0; + // Get the spans that 'stick out' on both sides + var first = markedSpansBefore(oldFirst, startCh, isInsert); + var last = markedSpansAfter(oldLast, endCh, isInsert); + + // Next, merge those two ends + var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0); + if (first) { + // Fix up .to properties of first + for (var i = 0; i < first.length; ++i) { + var span = first[i]; + if (span.to == null) { + var found = getMarkedSpanFor(last, span.marker); + if (!found) { span.to = startCh; } + else if (sameLine) { span.to = found.to == null ? null : found.to + offset; } + } + } + } + if (last) { + // Fix up .from in last (or move them into first in case of sameLine) + for (var i$1 = 0; i$1 < last.length; ++i$1) { + var span$1 = last[i$1]; + if (span$1.to != null) { span$1.to += offset; } + if (span$1.from == null) { + var found$1 = getMarkedSpanFor(first, span$1.marker); + if (!found$1) { + span$1.from = offset; + if (sameLine) { (first || (first = [])).push(span$1); } + } + } else { + span$1.from += offset; + if (sameLine) { (first || (first = [])).push(span$1); } + } + } + } + // Make sure we didn't create any zero-length spans + if (first) { first = clearEmptySpans(first); } + if (last && last != first) { last = clearEmptySpans(last); } + + var newMarkers = [first]; + if (!sameLine) { + // Fill gap with whole-line-spans + var gap = change.text.length - 2, gapMarkers; + if (gap > 0 && first) + { for (var i$2 = 0; i$2 < first.length; ++i$2) + { if (first[i$2].to == null) + { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } } + for (var i$3 = 0; i$3 < gap; ++i$3) + { newMarkers.push(gapMarkers); } + newMarkers.push(last); + } + return newMarkers + } + + // Remove spans that are empty and don't have a clearWhenEmpty + // option of false. + function clearEmptySpans(spans) { + for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false) + { spans.splice(i--, 1); } + } + if (!spans.length) { return null } + return spans + } + + // Used to 'clip' out readOnly ranges when making a change. + function removeReadOnlyRanges(doc, from, to) { + var markers = null; + doc.iter(from.line, to.line + 1, function (line) { + if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { + var mark = line.markedSpans[i].marker; + if (mark.readOnly && (!markers || indexOf(markers, mark) == -1)) + { (markers || (markers = [])).push(mark); } + } } + }); + if (!markers) { return null } + var parts = [{from: from, to: to}]; + for (var i = 0; i < markers.length; ++i) { + var mk = markers[i], m = mk.find(0); + for (var j = 0; j < parts.length; ++j) { + var p = parts[j]; + if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue } + var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to); + if (dfrom < 0 || !mk.inclusiveLeft && !dfrom) + { newParts.push({from: p.from, to: m.from}); } + if (dto > 0 || !mk.inclusiveRight && !dto) + { newParts.push({from: m.to, to: p.to}); } + parts.splice.apply(parts, newParts); + j += newParts.length - 3; + } + } + return parts + } + + // Connect or disconnect spans from a line. + function detachMarkedSpans(line) { + var spans = line.markedSpans; + if (!spans) { return } + for (var i = 0; i < spans.length; ++i) + { spans[i].marker.detachLine(line); } + line.markedSpans = null; + } + function attachMarkedSpans(line, spans) { + if (!spans) { return } + for (var i = 0; i < spans.length; ++i) + { spans[i].marker.attachLine(line); } + line.markedSpans = spans; + } + + // Helpers used when computing which overlapping collapsed span + // counts as the larger one. + function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 } + function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 } + + // Returns a number indicating which of two overlapping collapsed + // spans is larger (and thus includes the other). Falls back to + // comparing ids when the spans cover exactly the same range. + function compareCollapsedMarkers(a, b) { + var lenDiff = a.lines.length - b.lines.length; + if (lenDiff != 0) { return lenDiff } + var aPos = a.find(), bPos = b.find(); + var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b); + if (fromCmp) { return -fromCmp } + var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b); + if (toCmp) { return toCmp } + return b.id - a.id + } + + // Find out whether a line ends or starts in a collapsed span. If + // so, return the marker for that span. + function collapsedSpanAtSide(line, start) { + var sps = sawCollapsedSpans && line.markedSpans, found; + if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { + sp = sps[i]; + if (sp.marker.collapsed && (start ? sp.from : sp.to) == null && + (!found || compareCollapsedMarkers(found, sp.marker) < 0)) + { found = sp.marker; } + } } + return found + } + function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) } + function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) } + + function collapsedSpanAround(line, ch) { + var sps = sawCollapsedSpans && line.markedSpans, found; + if (sps) { for (var i = 0; i < sps.length; ++i) { + var sp = sps[i]; + if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) && + (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; } + } } + return found + } + + // Test whether there exists a collapsed span that partially + // overlaps (covers the start or end, but not both) of a new span. + // Such overlap is not allowed. + function conflictingCollapsedRange(doc, lineNo, from, to, marker) { + var line = getLine(doc, lineNo); + var sps = sawCollapsedSpans && line.markedSpans; + if (sps) { for (var i = 0; i < sps.length; ++i) { + var sp = sps[i]; + if (!sp.marker.collapsed) { continue } + var found = sp.marker.find(0); + var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker); + var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker); + if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue } + if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) || + fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0)) + { return true } + } } + } + + // A visual line is a line as drawn on the screen. Folding, for + // example, can cause multiple logical lines to appear on the same + // visual line. This finds the start of the visual line that the + // given line is part of (usually that is the line itself). + function visualLine(line) { + var merged; + while (merged = collapsedSpanAtStart(line)) + { line = merged.find(-1, true).line; } + return line + } + + function visualLineEnd(line) { + var merged; + while (merged = collapsedSpanAtEnd(line)) + { line = merged.find(1, true).line; } + return line + } + + // Returns an array of logical lines that continue the visual line + // started by the argument, or undefined if there are no such lines. + function visualLineContinued(line) { + var merged, lines; + while (merged = collapsedSpanAtEnd(line)) { + line = merged.find(1, true).line + ;(lines || (lines = [])).push(line); + } + return lines + } + + // Get the line number of the start of the visual line that the + // given line number is part of. + function visualLineNo(doc, lineN) { + var line = getLine(doc, lineN), vis = visualLine(line); + if (line == vis) { return lineN } + return lineNo(vis) + } + + // Get the line number of the start of the next visual line after + // the given line. + function visualLineEndNo(doc, lineN) { + if (lineN > doc.lastLine()) { return lineN } + var line = getLine(doc, lineN), merged; + if (!lineIsHidden(doc, line)) { return lineN } + while (merged = collapsedSpanAtEnd(line)) + { line = merged.find(1, true).line; } + return lineNo(line) + 1 + } + + // Compute whether a line is hidden. Lines count as hidden when they + // are part of a visual line that starts with another line, or when + // they are entirely covered by collapsed, non-widget span. + function lineIsHidden(doc, line) { + var sps = sawCollapsedSpans && line.markedSpans; + if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { + sp = sps[i]; + if (!sp.marker.collapsed) { continue } + if (sp.from == null) { return true } + if (sp.marker.widgetNode) { continue } + if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp)) + { return true } + } } + } + function lineIsHiddenInner(doc, line, span) { + if (span.to == null) { + var end = span.marker.find(1, true); + return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker)) + } + if (span.marker.inclusiveRight && span.to == line.text.length) + { return true } + for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) { + sp = line.markedSpans[i]; + if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to && + (sp.to == null || sp.to != span.from) && + (sp.marker.inclusiveLeft || span.marker.inclusiveRight) && + lineIsHiddenInner(doc, line, sp)) { return true } + } + } + + // Find the height above the given line. + function heightAtLine(lineObj) { + lineObj = visualLine(lineObj); + + var h = 0, chunk = lineObj.parent; + for (var i = 0; i < chunk.lines.length; ++i) { + var line = chunk.lines[i]; + if (line == lineObj) { break } + else { h += line.height; } + } + for (var p = chunk.parent; p; chunk = p, p = chunk.parent) { + for (var i$1 = 0; i$1 < p.children.length; ++i$1) { + var cur = p.children[i$1]; + if (cur == chunk) { break } + else { h += cur.height; } + } + } + return h + } + + // Compute the character length of a line, taking into account + // collapsed ranges (see markText) that might hide parts, and join + // other lines onto it. + function lineLength(line) { + if (line.height == 0) { return 0 } + var len = line.text.length, merged, cur = line; + while (merged = collapsedSpanAtStart(cur)) { + var found = merged.find(0, true); + cur = found.from.line; + len += found.from.ch - found.to.ch; + } + cur = line; + while (merged = collapsedSpanAtEnd(cur)) { + var found$1 = merged.find(0, true); + len -= cur.text.length - found$1.from.ch; + cur = found$1.to.line; + len += cur.text.length - found$1.to.ch; + } + return len + } + + // Find the longest line in the document. + function findMaxLine(cm) { + var d = cm.display, doc = cm.doc; + d.maxLine = getLine(doc, doc.first); + d.maxLineLength = lineLength(d.maxLine); + d.maxLineChanged = true; + doc.iter(function (line) { + var len = lineLength(line); + if (len > d.maxLineLength) { + d.maxLineLength = len; + d.maxLine = line; + } + }); + } + + // LINE DATA STRUCTURE + + // Line objects. These hold state related to a line, including + // highlighting info (the styles array). + var Line = function(text, markedSpans, estimateHeight) { + this.text = text; + attachMarkedSpans(this, markedSpans); + this.height = estimateHeight ? estimateHeight(this) : 1; + }; + + Line.prototype.lineNo = function () { return lineNo(this) }; + eventMixin(Line); + + // Change the content (text, markers) of a line. Automatically + // invalidates cached information and tries to re-estimate the + // line's height. + function updateLine(line, text, markedSpans, estimateHeight) { + line.text = text; + if (line.stateAfter) { line.stateAfter = null; } + if (line.styles) { line.styles = null; } + if (line.order != null) { line.order = null; } + detachMarkedSpans(line); + attachMarkedSpans(line, markedSpans); + var estHeight = estimateHeight ? estimateHeight(line) : 1; + if (estHeight != line.height) { updateLineHeight(line, estHeight); } + } + + // Detach a line from the document tree and its markers. + function cleanUpLine(line) { + line.parent = null; + detachMarkedSpans(line); + } + + // Convert a style as returned by a mode (either null, or a string + // containing one or more styles) to a CSS style. This is cached, + // and also looks for line-wide styles. + var styleToClassCache = {}, styleToClassCacheWithMode = {}; + function interpretTokenStyle(style, options) { + if (!style || /^\s*$/.test(style)) { return null } + var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache; + return cache[style] || + (cache[style] = style.replace(/\S+/g, "cm-$&")) + } + + // Render the DOM representation of the text of a line. Also builds + // up a 'line map', which points at the DOM nodes that represent + // specific stretches of text, and is used by the measuring code. + // The returned object contains the DOM node, this map, and + // information about line-wide styles that were set by the mode. + function buildLineContent(cm, lineView) { + // The padding-right forces the element to have a 'border', which + // is needed on Webkit to be able to get line-level bounding + // rectangles for it (in measureChar). + var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null); + var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content, + col: 0, pos: 0, cm: cm, + trailingSpace: false, + splitSpaces: cm.getOption("lineWrapping")}; + lineView.measure = {}; + + // Iterate over the logical lines that make up this visual line. + for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) { + var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0); + builder.pos = 0; + builder.addToken = buildToken; + // Optionally wire in some hacks into the token-rendering + // algorithm, to deal with browser quirks. + if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction))) + { builder.addToken = buildTokenBadBidi(builder.addToken, order); } + builder.map = []; + var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line); + insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate)); + if (line.styleClasses) { + if (line.styleClasses.bgClass) + { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); } + if (line.styleClasses.textClass) + { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); } + } + + // Ensure at least a single node is present, for measuring. + if (builder.map.length == 0) + { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); } + + // Store the map and a cache object for the current logical line + if (i == 0) { + lineView.measure.map = builder.map; + lineView.measure.cache = {}; + } else { + (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map) + ;(lineView.measure.caches || (lineView.measure.caches = [])).push({}); + } + } + + // See issue #2901 + if (webkit) { + var last = builder.content.lastChild; + if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab"))) + { builder.content.className = "cm-tab-wrap-hack"; } + } + + signal(cm, "renderLine", cm, lineView.line, builder.pre); + if (builder.pre.className) + { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); } + + return builder + } + + function defaultSpecialCharPlaceholder(ch) { + var token = elt("span", "\u2022", "cm-invalidchar"); + token.title = "\\u" + ch.charCodeAt(0).toString(16); + token.setAttribute("aria-label", token.title); + return token + } + + // Build up the DOM representation for a single token, and add it to + // the line map. Takes care to render special characters separately. + function buildToken(builder, text, style, startStyle, endStyle, css, attributes) { + if (!text) { return } + var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text; + var special = builder.cm.state.specialChars, mustWrap = false; + var content; + if (!special.test(text)) { + builder.col += text.length; + content = document.createTextNode(displayText); + builder.map.push(builder.pos, builder.pos + text.length, content); + if (ie && ie_version < 9) { mustWrap = true; } + builder.pos += text.length; + } else { + content = document.createDocumentFragment(); + var pos = 0; + while (true) { + special.lastIndex = pos; + var m = special.exec(text); + var skipped = m ? m.index - pos : text.length - pos; + if (skipped) { + var txt = document.createTextNode(displayText.slice(pos, pos + skipped)); + if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); } + else { content.appendChild(txt); } + builder.map.push(builder.pos, builder.pos + skipped, txt); + builder.col += skipped; + builder.pos += skipped; + } + if (!m) { break } + pos += skipped + 1; + var txt$1 = (void 0); + if (m[0] == "\t") { + var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize; + txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")); + txt$1.setAttribute("role", "presentation"); + txt$1.setAttribute("cm-text", "\t"); + builder.col += tabWidth; + } else if (m[0] == "\r" || m[0] == "\n") { + txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar")); + txt$1.setAttribute("cm-text", m[0]); + builder.col += 1; + } else { + txt$1 = builder.cm.options.specialCharPlaceholder(m[0]); + txt$1.setAttribute("cm-text", m[0]); + if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); } + else { content.appendChild(txt$1); } + builder.col += 1; + } + builder.map.push(builder.pos, builder.pos + 1, txt$1); + builder.pos++; + } + } + builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32; + if (style || startStyle || endStyle || mustWrap || css || attributes) { + var fullStyle = style || ""; + if (startStyle) { fullStyle += startStyle; } + if (endStyle) { fullStyle += endStyle; } + var token = elt("span", [content], fullStyle, css); + if (attributes) { + for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class") + { token.setAttribute(attr, attributes[attr]); } } + } + return builder.content.appendChild(token) + } + builder.content.appendChild(content); + } + + // Change some spaces to NBSP to prevent the browser from collapsing + // trailing spaces at the end of a line when rendering text (issue #1362). + function splitSpaces(text, trailingBefore) { + if (text.length > 1 && !/ /.test(text)) { return text } + var spaceBefore = trailingBefore, result = ""; + for (var i = 0; i < text.length; i++) { + var ch = text.charAt(i); + if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32)) + { ch = "\u00a0"; } + result += ch; + spaceBefore = ch == " "; + } + return result + } + + // Work around nonsense dimensions being reported for stretches of + // right-to-left text. + function buildTokenBadBidi(inner, order) { + return function (builder, text, style, startStyle, endStyle, css, attributes) { + style = style ? style + " cm-force-border" : "cm-force-border"; + var start = builder.pos, end = start + text.length; + for (;;) { + // Find the part that overlaps with the start of this text + var part = (void 0); + for (var i = 0; i < order.length; i++) { + part = order[i]; + if (part.to > start && part.from <= start) { break } + } + if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) } + inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes); + startStyle = null; + text = text.slice(part.to - start); + start = part.to; + } + } + } + + function buildCollapsedSpan(builder, size, marker, ignoreWidget) { + var widget = !ignoreWidget && marker.widgetNode; + if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); } + if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) { + if (!widget) + { widget = builder.content.appendChild(document.createElement("span")); } + widget.setAttribute("cm-marker", marker.id); + } + if (widget) { + builder.cm.display.input.setUneditable(widget); + builder.content.appendChild(widget); + } + builder.pos += size; + builder.trailingSpace = false; + } + + // Outputs a number of spans to make up a line, taking highlighting + // and marked text into account. + function insertLineContent(line, builder, styles) { + var spans = line.markedSpans, allText = line.text, at = 0; + if (!spans) { + for (var i$1 = 1; i$1 < styles.length; i$1+=2) + { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); } + return + } + + var len = allText.length, pos = 0, i = 1, text = "", style, css; + var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes; + for (;;) { + if (nextChange == pos) { // Update current marker set + spanStyle = spanEndStyle = spanStartStyle = css = ""; + attributes = null; + collapsed = null; nextChange = Infinity; + var foundBookmarks = [], endStyles = (void 0); + for (var j = 0; j < spans.length; ++j) { + var sp = spans[j], m = sp.marker; + if (m.type == "bookmark" && sp.from == pos && m.widgetNode) { + foundBookmarks.push(m); + } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) { + if (sp.to != null && sp.to != pos && nextChange > sp.to) { + nextChange = sp.to; + spanEndStyle = ""; + } + if (m.className) { spanStyle += " " + m.className; } + if (m.css) { css = (css ? css + ";" : "") + m.css; } + if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; } + if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); } + // support for the old title property + // https://github.com/codemirror/CodeMirror/pull/5673 + if (m.title) { (attributes || (attributes = {})).title = m.title; } + if (m.attributes) { + for (var attr in m.attributes) + { (attributes || (attributes = {}))[attr] = m.attributes[attr]; } + } + if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)) + { collapsed = sp; } + } else if (sp.from > pos && nextChange > sp.from) { + nextChange = sp.from; + } + } + if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2) + { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } } + + if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2) + { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } } + if (collapsed && (collapsed.from || 0) == pos) { + buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos, + collapsed.marker, collapsed.from == null); + if (collapsed.to == null) { return } + if (collapsed.to == pos) { collapsed = false; } + } + } + if (pos >= len) { break } + + var upto = Math.min(len, nextChange); + while (true) { + if (text) { + var end = pos + text.length; + if (!collapsed) { + var tokenText = end > upto ? text.slice(0, upto - pos) : text; + builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle, + spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes); + } + if (end >= upto) {text = text.slice(upto - pos); pos = upto; break} + pos = end; + spanStartStyle = ""; + } + text = allText.slice(at, at = styles[i++]); + style = interpretTokenStyle(styles[i++], builder.cm.options); + } + } + } + + + // These objects are used to represent the visible (currently drawn) + // part of the document. A LineView may correspond to multiple + // logical lines, if those are connected by collapsed ranges. + function LineView(doc, line, lineN) { + // The starting line + this.line = line; + // Continuing lines, if any + this.rest = visualLineContinued(line); + // Number of logical lines in this visual line + this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1; + this.node = this.text = null; + this.hidden = lineIsHidden(doc, line); + } + + // Create a range of LineView objects for the given lines. + function buildViewArray(cm, from, to) { + var array = [], nextPos; + for (var pos = from; pos < to; pos = nextPos) { + var view = new LineView(cm.doc, getLine(cm.doc, pos), pos); + nextPos = pos + view.size; + array.push(view); + } + return array + } + + var operationGroup = null; + + function pushOperation(op) { + if (operationGroup) { + operationGroup.ops.push(op); + } else { + op.ownsGroup = operationGroup = { + ops: [op], + delayedCallbacks: [] + }; + } + } + + function fireCallbacksForOps(group) { + // Calls delayed callbacks and cursorActivity handlers until no + // new ones appear + var callbacks = group.delayedCallbacks, i = 0; + do { + for (; i < callbacks.length; i++) + { callbacks[i].call(null); } + for (var j = 0; j < group.ops.length; j++) { + var op = group.ops[j]; + if (op.cursorActivityHandlers) + { while (op.cursorActivityCalled < op.cursorActivityHandlers.length) + { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } } + } + } while (i < callbacks.length) + } + + function finishOperation(op, endCb) { + var group = op.ownsGroup; + if (!group) { return } + + try { fireCallbacksForOps(group); } + finally { + operationGroup = null; + endCb(group); + } + } + + var orphanDelayedCallbacks = null; + + // Often, we want to signal events at a point where we are in the + // middle of some work, but don't want the handler to start calling + // other methods on the editor, which might be in an inconsistent + // state or simply not expect any other events to happen. + // signalLater looks whether there are any handlers, and schedules + // them to be executed when the last operation ends, or, if no + // operation is active, when a timeout fires. + function signalLater(emitter, type /*, values...*/) { + var arr = getHandlers(emitter, type); + if (!arr.length) { return } + var args = Array.prototype.slice.call(arguments, 2), list; + if (operationGroup) { + list = operationGroup.delayedCallbacks; + } else if (orphanDelayedCallbacks) { + list = orphanDelayedCallbacks; + } else { + list = orphanDelayedCallbacks = []; + setTimeout(fireOrphanDelayed, 0); + } + var loop = function ( i ) { + list.push(function () { return arr[i].apply(null, args); }); + }; + + for (var i = 0; i < arr.length; ++i) + loop( i ); + } + + function fireOrphanDelayed() { + var delayed = orphanDelayedCallbacks; + orphanDelayedCallbacks = null; + for (var i = 0; i < delayed.length; ++i) { delayed[i](); } + } + + // When an aspect of a line changes, a string is added to + // lineView.changes. This updates the relevant part of the line's + // DOM structure. + function updateLineForChanges(cm, lineView, lineN, dims) { + for (var j = 0; j < lineView.changes.length; j++) { + var type = lineView.changes[j]; + if (type == "text") { updateLineText(cm, lineView); } + else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); } + else if (type == "class") { updateLineClasses(cm, lineView); } + else if (type == "widget") { updateLineWidgets(cm, lineView, dims); } + } + lineView.changes = null; + } + + // Lines with gutter elements, widgets or a background class need to + // be wrapped, and have the extra elements added to the wrapper div + function ensureLineWrapped(lineView) { + if (lineView.node == lineView.text) { + lineView.node = elt("div", null, null, "position: relative"); + if (lineView.text.parentNode) + { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); } + lineView.node.appendChild(lineView.text); + if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; } + } + return lineView.node + } + + function updateLineBackground(cm, lineView) { + var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass; + if (cls) { cls += " CodeMirror-linebackground"; } + if (lineView.background) { + if (cls) { lineView.background.className = cls; } + else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; } + } else if (cls) { + var wrap = ensureLineWrapped(lineView); + lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild); + cm.display.input.setUneditable(lineView.background); + } + } + + // Wrapper around buildLineContent which will reuse the structure + // in display.externalMeasured when possible. + function getLineContent(cm, lineView) { + var ext = cm.display.externalMeasured; + if (ext && ext.line == lineView.line) { + cm.display.externalMeasured = null; + lineView.measure = ext.measure; + return ext.built + } + return buildLineContent(cm, lineView) + } + + // Redraw the line's text. Interacts with the background and text + // classes because the mode may output tokens that influence these + // classes. + function updateLineText(cm, lineView) { + var cls = lineView.text.className; + var built = getLineContent(cm, lineView); + if (lineView.text == lineView.node) { lineView.node = built.pre; } + lineView.text.parentNode.replaceChild(built.pre, lineView.text); + lineView.text = built.pre; + if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) { + lineView.bgClass = built.bgClass; + lineView.textClass = built.textClass; + updateLineClasses(cm, lineView); + } else if (cls) { + lineView.text.className = cls; + } + } + + function updateLineClasses(cm, lineView) { + updateLineBackground(cm, lineView); + if (lineView.line.wrapClass) + { ensureLineWrapped(lineView).className = lineView.line.wrapClass; } + else if (lineView.node != lineView.text) + { lineView.node.className = ""; } + var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass; + lineView.text.className = textClass || ""; + } + + function updateLineGutter(cm, lineView, lineN, dims) { + if (lineView.gutter) { + lineView.node.removeChild(lineView.gutter); + lineView.gutter = null; + } + if (lineView.gutterBackground) { + lineView.node.removeChild(lineView.gutterBackground); + lineView.gutterBackground = null; + } + if (lineView.line.gutterClass) { + var wrap = ensureLineWrapped(lineView); + lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass, + ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px")); + cm.display.input.setUneditable(lineView.gutterBackground); + wrap.insertBefore(lineView.gutterBackground, lineView.text); + } + var markers = lineView.line.gutterMarkers; + if (cm.options.lineNumbers || markers) { + var wrap$1 = ensureLineWrapped(lineView); + var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px")); + gutterWrap.setAttribute("aria-hidden", "true"); + cm.display.input.setUneditable(gutterWrap); + wrap$1.insertBefore(gutterWrap, lineView.text); + if (lineView.line.gutterClass) + { gutterWrap.className += " " + lineView.line.gutterClass; } + if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"])) + { lineView.lineNumber = gutterWrap.appendChild( + elt("div", lineNumberFor(cm.options, lineN), + "CodeMirror-linenumber CodeMirror-gutter-elt", + ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); } + if (markers) { for (var k = 0; k < cm.display.gutterSpecs.length; ++k) { + var id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id]; + if (found) + { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", + ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); } + } } + } + } + + function updateLineWidgets(cm, lineView, dims) { + if (lineView.alignable) { lineView.alignable = null; } + var isWidget = classTest("CodeMirror-linewidget"); + for (var node = lineView.node.firstChild, next = (void 0); node; node = next) { + next = node.nextSibling; + if (isWidget.test(node.className)) { lineView.node.removeChild(node); } + } + insertLineWidgets(cm, lineView, dims); + } + + // Build a line's DOM representation from scratch + function buildLineElement(cm, lineView, lineN, dims) { + var built = getLineContent(cm, lineView); + lineView.text = lineView.node = built.pre; + if (built.bgClass) { lineView.bgClass = built.bgClass; } + if (built.textClass) { lineView.textClass = built.textClass; } + + updateLineClasses(cm, lineView); + updateLineGutter(cm, lineView, lineN, dims); + insertLineWidgets(cm, lineView, dims); + return lineView.node + } + + // A lineView may contain multiple logical lines (when merged by + // collapsed spans). The widgets for all of them need to be drawn. + function insertLineWidgets(cm, lineView, dims) { + insertLineWidgetsFor(cm, lineView.line, lineView, dims, true); + if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) + { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } } + } + + function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) { + if (!line.widgets) { return } + var wrap = ensureLineWrapped(lineView); + for (var i = 0, ws = line.widgets; i < ws.length; ++i) { + var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget" + (widget.className ? " " + widget.className : "")); + if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); } + positionLineWidget(widget, node, lineView, dims); + cm.display.input.setUneditable(node); + if (allowAbove && widget.above) + { wrap.insertBefore(node, lineView.gutter || lineView.text); } + else + { wrap.appendChild(node); } + signalLater(widget, "redraw"); + } + } + + function positionLineWidget(widget, node, lineView, dims) { + if (widget.noHScroll) { + (lineView.alignable || (lineView.alignable = [])).push(node); + var width = dims.wrapperWidth; + node.style.left = dims.fixedPos + "px"; + if (!widget.coverGutter) { + width -= dims.gutterTotalWidth; + node.style.paddingLeft = dims.gutterTotalWidth + "px"; + } + node.style.width = width + "px"; + } + if (widget.coverGutter) { + node.style.zIndex = 5; + node.style.position = "relative"; + if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; } + } + } + + function widgetHeight(widget) { + if (widget.height != null) { return widget.height } + var cm = widget.doc.cm; + if (!cm) { return 0 } + if (!contains(document.body, widget.node)) { + var parentStyle = "position: relative;"; + if (widget.coverGutter) + { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; } + if (widget.noHScroll) + { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; } + removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle)); + } + return widget.height = widget.node.parentNode.offsetHeight + } + + // Return true when the given mouse event happened in a widget + function eventInWidget(display, e) { + for (var n = e_target(e); n != display.wrapper; n = n.parentNode) { + if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") || + (n.parentNode == display.sizer && n != display.mover)) + { return true } + } + } + + // POSITION MEASUREMENT + + function paddingTop(display) {return display.lineSpace.offsetTop} + function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight} + function paddingH(display) { + if (display.cachedPaddingH) { return display.cachedPaddingH } + var e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-line-like")); + var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle; + var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}; + if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; } + return data + } + + function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth } + function displayWidth(cm) { + return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth + } + function displayHeight(cm) { + return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight + } + + // Ensure the lineView.wrapping.heights array is populated. This is + // an array of bottom offsets for the lines that make up a drawn + // line. When lineWrapping is on, there might be more than one + // height. + function ensureLineHeights(cm, lineView, rect) { + var wrapping = cm.options.lineWrapping; + var curWidth = wrapping && displayWidth(cm); + if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) { + var heights = lineView.measure.heights = []; + if (wrapping) { + lineView.measure.width = curWidth; + var rects = lineView.text.firstChild.getClientRects(); + for (var i = 0; i < rects.length - 1; i++) { + var cur = rects[i], next = rects[i + 1]; + if (Math.abs(cur.bottom - next.bottom) > 2) + { heights.push((cur.bottom + next.top) / 2 - rect.top); } + } + } + heights.push(rect.bottom - rect.top); + } + } + + // Find a line map (mapping character offsets to text nodes) and a + // measurement cache for the given line number. (A line view might + // contain multiple lines when collapsed ranges are present.) + function mapFromLineView(lineView, line, lineN) { + if (lineView.line == line) + { return {map: lineView.measure.map, cache: lineView.measure.cache} } + for (var i = 0; i < lineView.rest.length; i++) + { if (lineView.rest[i] == line) + { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } } + for (var i$1 = 0; i$1 < lineView.rest.length; i$1++) + { if (lineNo(lineView.rest[i$1]) > lineN) + { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } } + } + + // Render a line into the hidden node display.externalMeasured. Used + // when measurement is needed for a line that's not in the viewport. + function updateExternalMeasurement(cm, line) { + line = visualLine(line); + var lineN = lineNo(line); + var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN); + view.lineN = lineN; + var built = view.built = buildLineContent(cm, view); + view.text = built.pre; + removeChildrenAndAdd(cm.display.lineMeasure, built.pre); + return view + } + + // Get a {top, bottom, left, right} box (in line-local coordinates) + // for a given character. + function measureChar(cm, line, ch, bias) { + return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias) + } + + // Find a line view that corresponds to the given line number. + function findViewForLine(cm, lineN) { + if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo) + { return cm.display.view[findViewIndex(cm, lineN)] } + var ext = cm.display.externalMeasured; + if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size) + { return ext } + } + + // Measurement can be split in two steps, the set-up work that + // applies to the whole line, and the measurement of the actual + // character. Functions like coordsChar, that need to do a lot of + // measurements in a row, can thus ensure that the set-up work is + // only done once. + function prepareMeasureForLine(cm, line) { + var lineN = lineNo(line); + var view = findViewForLine(cm, lineN); + if (view && !view.text) { + view = null; + } else if (view && view.changes) { + updateLineForChanges(cm, view, lineN, getDimensions(cm)); + cm.curOp.forceUpdate = true; + } + if (!view) + { view = updateExternalMeasurement(cm, line); } + + var info = mapFromLineView(view, line, lineN); + return { + line: line, view: view, rect: null, + map: info.map, cache: info.cache, before: info.before, + hasHeights: false + } + } + + // Given a prepared measurement object, measures the position of an + // actual character (or fetches it from the cache). + function measureCharPrepared(cm, prepared, ch, bias, varHeight) { + if (prepared.before) { ch = -1; } + var key = ch + (bias || ""), found; + if (prepared.cache.hasOwnProperty(key)) { + found = prepared.cache[key]; + } else { + if (!prepared.rect) + { prepared.rect = prepared.view.text.getBoundingClientRect(); } + if (!prepared.hasHeights) { + ensureLineHeights(cm, prepared.view, prepared.rect); + prepared.hasHeights = true; + } + found = measureCharInner(cm, prepared, ch, bias); + if (!found.bogus) { prepared.cache[key] = found; } + } + return {left: found.left, right: found.right, + top: varHeight ? found.rtop : found.top, + bottom: varHeight ? found.rbottom : found.bottom} + } + + var nullRect = {left: 0, right: 0, top: 0, bottom: 0}; + + function nodeAndOffsetInLineMap(map, ch, bias) { + var node, start, end, collapse, mStart, mEnd; + // First, search the line map for the text node corresponding to, + // or closest to, the target character. + for (var i = 0; i < map.length; i += 3) { + mStart = map[i]; + mEnd = map[i + 1]; + if (ch < mStart) { + start = 0; end = 1; + collapse = "left"; + } else if (ch < mEnd) { + start = ch - mStart; + end = start + 1; + } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) { + end = mEnd - mStart; + start = end - 1; + if (ch >= mEnd) { collapse = "right"; } + } + if (start != null) { + node = map[i + 2]; + if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right")) + { collapse = bias; } + if (bias == "left" && start == 0) + { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) { + node = map[(i -= 3) + 2]; + collapse = "left"; + } } + if (bias == "right" && start == mEnd - mStart) + { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) { + node = map[(i += 3) + 2]; + collapse = "right"; + } } + break + } + } + return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd} + } + + function getUsefulRect(rects, bias) { + var rect = nullRect; + if (bias == "left") { for (var i = 0; i < rects.length; i++) { + if ((rect = rects[i]).left != rect.right) { break } + } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) { + if ((rect = rects[i$1]).left != rect.right) { break } + } } + return rect + } + + function measureCharInner(cm, prepared, ch, bias) { + var place = nodeAndOffsetInLineMap(prepared.map, ch, bias); + var node = place.node, start = place.start, end = place.end, collapse = place.collapse; + + var rect; + if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates. + for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned + while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; } + while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; } + if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) + { rect = node.parentNode.getBoundingClientRect(); } + else + { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); } + if (rect.left || rect.right || start == 0) { break } + end = start; + start = start - 1; + collapse = "right"; + } + if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); } + } else { // If it is a widget, simply get the box for the whole widget. + if (start > 0) { collapse = bias = "right"; } + var rects; + if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1) + { rect = rects[bias == "right" ? rects.length - 1 : 0]; } + else + { rect = node.getBoundingClientRect(); } + } + if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) { + var rSpan = node.parentNode.getClientRects()[0]; + if (rSpan) + { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; } + else + { rect = nullRect; } + } + + var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top; + var mid = (rtop + rbot) / 2; + var heights = prepared.view.measure.heights; + var i = 0; + for (; i < heights.length - 1; i++) + { if (mid < heights[i]) { break } } + var top = i ? heights[i - 1] : 0, bot = heights[i]; + var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left, + right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left, + top: top, bottom: bot}; + if (!rect.left && !rect.right) { result.bogus = true; } + if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; } + + return result + } + + // Work around problem with bounding client rects on ranges being + // returned incorrectly when zoomed on IE10 and below. + function maybeUpdateRectForZooming(measure, rect) { + if (!window.screen || screen.logicalXDPI == null || + screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure)) + { return rect } + var scaleX = screen.logicalXDPI / screen.deviceXDPI; + var scaleY = screen.logicalYDPI / screen.deviceYDPI; + return {left: rect.left * scaleX, right: rect.right * scaleX, + top: rect.top * scaleY, bottom: rect.bottom * scaleY} + } + + function clearLineMeasurementCacheFor(lineView) { + if (lineView.measure) { + lineView.measure.cache = {}; + lineView.measure.heights = null; + if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) + { lineView.measure.caches[i] = {}; } } + } + } + + function clearLineMeasurementCache(cm) { + cm.display.externalMeasure = null; + removeChildren(cm.display.lineMeasure); + for (var i = 0; i < cm.display.view.length; i++) + { clearLineMeasurementCacheFor(cm.display.view[i]); } + } + + function clearCaches(cm) { + clearLineMeasurementCache(cm); + cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null; + if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; } + cm.display.lineNumChars = null; + } + + function pageScrollX() { + // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206 + // which causes page_Offset and bounding client rects to use + // different reference viewports and invalidate our calculations. + if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) } + return window.pageXOffset || (document.documentElement || document.body).scrollLeft + } + function pageScrollY() { + if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) } + return window.pageYOffset || (document.documentElement || document.body).scrollTop + } + + function widgetTopHeight(lineObj) { + var height = 0; + if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above) + { height += widgetHeight(lineObj.widgets[i]); } } } + return height + } + + // Converts a {top, bottom, left, right} box from line-local + // coordinates into another coordinate system. Context may be one of + // "line", "div" (display.lineDiv), "local"./null (editor), "window", + // or "page". + function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) { + if (!includeWidgets) { + var height = widgetTopHeight(lineObj); + rect.top += height; rect.bottom += height; + } + if (context == "line") { return rect } + if (!context) { context = "local"; } + var yOff = heightAtLine(lineObj); + if (context == "local") { yOff += paddingTop(cm.display); } + else { yOff -= cm.display.viewOffset; } + if (context == "page" || context == "window") { + var lOff = cm.display.lineSpace.getBoundingClientRect(); + yOff += lOff.top + (context == "window" ? 0 : pageScrollY()); + var xOff = lOff.left + (context == "window" ? 0 : pageScrollX()); + rect.left += xOff; rect.right += xOff; + } + rect.top += yOff; rect.bottom += yOff; + return rect + } + + // Coverts a box from "div" coords to another coordinate system. + // Context may be "window", "page", "div", or "local"./null. + function fromCoordSystem(cm, coords, context) { + if (context == "div") { return coords } + var left = coords.left, top = coords.top; + // First move into "page" coordinate system + if (context == "page") { + left -= pageScrollX(); + top -= pageScrollY(); + } else if (context == "local" || !context) { + var localBox = cm.display.sizer.getBoundingClientRect(); + left += localBox.left; + top += localBox.top; + } + + var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect(); + return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top} + } + + function charCoords(cm, pos, context, lineObj, bias) { + if (!lineObj) { lineObj = getLine(cm.doc, pos.line); } + return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context) + } + + // Returns a box for a given cursor position, which may have an + // 'other' property containing the position of the secondary cursor + // on a bidi boundary. + // A cursor Pos(line, char, "before") is on the same visual line as `char - 1` + // and after `char - 1` in writing order of `char - 1` + // A cursor Pos(line, char, "after") is on the same visual line as `char` + // and before `char` in writing order of `char` + // Examples (upper-case letters are RTL, lower-case are LTR): + // Pos(0, 1, ...) + // before after + // ab a|b a|b + // aB a|B aB| + // Ab |Ab A|b + // AB B|A B|A + // Every position after the last character on a line is considered to stick + // to the last character on the line. + function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) { + lineObj = lineObj || getLine(cm.doc, pos.line); + if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); } + function get(ch, right) { + var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight); + if (right) { m.left = m.right; } else { m.right = m.left; } + return intoCoordSystem(cm, lineObj, m, context) + } + var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky; + if (ch >= lineObj.text.length) { + ch = lineObj.text.length; + sticky = "before"; + } else if (ch <= 0) { + ch = 0; + sticky = "after"; + } + if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") } + + function getBidi(ch, partPos, invert) { + var part = order[partPos], right = part.level == 1; + return get(invert ? ch - 1 : ch, right != invert) + } + var partPos = getBidiPartAt(order, ch, sticky); + var other = bidiOther; + var val = getBidi(ch, partPos, sticky == "before"); + if (other != null) { val.other = getBidi(ch, other, sticky != "before"); } + return val + } + + // Used to cheaply estimate the coordinates for a position. Used for + // intermediate scroll updates. + function estimateCoords(cm, pos) { + var left = 0; + pos = clipPos(cm.doc, pos); + if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; } + var lineObj = getLine(cm.doc, pos.line); + var top = heightAtLine(lineObj) + paddingTop(cm.display); + return {left: left, right: left, top: top, bottom: top + lineObj.height} + } + + // Positions returned by coordsChar contain some extra information. + // xRel is the relative x position of the input coordinates compared + // to the found position (so xRel > 0 means the coordinates are to + // the right of the character position, for example). When outside + // is true, that means the coordinates lie outside the line's + // vertical range. + function PosWithInfo(line, ch, sticky, outside, xRel) { + var pos = Pos(line, ch, sticky); + pos.xRel = xRel; + if (outside) { pos.outside = outside; } + return pos + } + + // Compute the character position closest to the given coordinates. + // Input must be lineSpace-local ("div" coordinate system). + function coordsChar(cm, x, y) { + var doc = cm.doc; + y += cm.display.viewOffset; + if (y < 0) { return PosWithInfo(doc.first, 0, null, -1, -1) } + var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1; + if (lineN > last) + { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1) } + if (x < 0) { x = 0; } + + var lineObj = getLine(doc, lineN); + for (;;) { + var found = coordsCharInner(cm, lineObj, lineN, x, y); + var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0)); + if (!collapsed) { return found } + var rangeEnd = collapsed.find(1); + if (rangeEnd.line == lineN) { return rangeEnd } + lineObj = getLine(doc, lineN = rangeEnd.line); + } + } + + function wrappedLineExtent(cm, lineObj, preparedMeasure, y) { + y -= widgetTopHeight(lineObj); + var end = lineObj.text.length; + var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0); + end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end); + return {begin: begin, end: end} + } + + function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) { + if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); } + var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top; + return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop) + } + + // Returns true if the given side of a box is after the given + // coordinates, in top-to-bottom, left-to-right order. + function boxIsAfter(box, x, y, left) { + return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x + } + + function coordsCharInner(cm, lineObj, lineNo, x, y) { + // Move y into line-local coordinate space + y -= heightAtLine(lineObj); + var preparedMeasure = prepareMeasureForLine(cm, lineObj); + // When directly calling `measureCharPrepared`, we have to adjust + // for the widgets at this line. + var widgetHeight = widgetTopHeight(lineObj); + var begin = 0, end = lineObj.text.length, ltr = true; + + var order = getOrder(lineObj, cm.doc.direction); + // If the line isn't plain left-to-right text, first figure out + // which bidi section the coordinates fall into. + if (order) { + var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart) + (cm, lineObj, lineNo, preparedMeasure, order, x, y); + ltr = part.level != 1; + // The awkward -1 offsets are needed because findFirst (called + // on these below) will treat its first bound as inclusive, + // second as exclusive, but we want to actually address the + // characters in the part's range + begin = ltr ? part.from : part.to - 1; + end = ltr ? part.to : part.from - 1; + } + + // A binary search to find the first character whose bounding box + // starts after the coordinates. If we run across any whose box wrap + // the coordinates, store that. + var chAround = null, boxAround = null; + var ch = findFirst(function (ch) { + var box = measureCharPrepared(cm, preparedMeasure, ch); + box.top += widgetHeight; box.bottom += widgetHeight; + if (!boxIsAfter(box, x, y, false)) { return false } + if (box.top <= y && box.left <= x) { + chAround = ch; + boxAround = box; + } + return true + }, begin, end); + + var baseX, sticky, outside = false; + // If a box around the coordinates was found, use that + if (boxAround) { + // Distinguish coordinates nearer to the left or right side of the box + var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr; + ch = chAround + (atStart ? 0 : 1); + sticky = atStart ? "after" : "before"; + baseX = atLeft ? boxAround.left : boxAround.right; + } else { + // (Adjust for extended bound, if necessary.) + if (!ltr && (ch == end || ch == begin)) { ch++; } + // To determine which side to associate with, get the box to the + // left of the character and compare it's vertical position to the + // coordinates + sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" : + (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ? + "after" : "before"; + // Now get accurate coordinates for this place, in order to get a + // base X position + var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure); + baseX = coords.left; + outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0; + } + + ch = skipExtendingChars(lineObj.text, ch, 1); + return PosWithInfo(lineNo, ch, sticky, outside, x - baseX) + } + + function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) { + // Bidi parts are sorted left-to-right, and in a non-line-wrapping + // situation, we can take this ordering to correspond to the visual + // ordering. This finds the first part whose end is after the given + // coordinates. + var index = findFirst(function (i) { + var part = order[i], ltr = part.level != 1; + return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"), + "line", lineObj, preparedMeasure), x, y, true) + }, 0, order.length - 1); + var part = order[index]; + // If this isn't the first part, the part's start is also after + // the coordinates, and the coordinates aren't on the same line as + // that start, move one part back. + if (index > 0) { + var ltr = part.level != 1; + var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"), + "line", lineObj, preparedMeasure); + if (boxIsAfter(start, x, y, true) && start.top > y) + { part = order[index - 1]; } + } + return part + } + + function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) { + // In a wrapped line, rtl text on wrapping boundaries can do things + // that don't correspond to the ordering in our `order` array at + // all, so a binary search doesn't work, and we want to return a + // part that only spans one line so that the binary search in + // coordsCharInner is safe. As such, we first find the extent of the + // wrapped line, and then do a flat search in which we discard any + // spans that aren't on the line. + var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y); + var begin = ref.begin; + var end = ref.end; + if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; } + var part = null, closestDist = null; + for (var i = 0; i < order.length; i++) { + var p = order[i]; + if (p.from >= end || p.to <= begin) { continue } + var ltr = p.level != 1; + var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right; + // Weigh against spans ending before this, so that they are only + // picked if nothing ends after + var dist = endX < x ? x - endX + 1e9 : endX - x; + if (!part || closestDist > dist) { + part = p; + closestDist = dist; + } + } + if (!part) { part = order[order.length - 1]; } + // Clip the part to the wrapped line. + if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; } + if (part.to > end) { part = {from: part.from, to: end, level: part.level}; } + return part + } + + var measureText; + // Compute the default text height. + function textHeight(display) { + if (display.cachedTextHeight != null) { return display.cachedTextHeight } + if (measureText == null) { + measureText = elt("pre", null, "CodeMirror-line-like"); + // Measure a bunch of lines, for browsers that compute + // fractional heights. + for (var i = 0; i < 49; ++i) { + measureText.appendChild(document.createTextNode("x")); + measureText.appendChild(elt("br")); + } + measureText.appendChild(document.createTextNode("x")); + } + removeChildrenAndAdd(display.measure, measureText); + var height = measureText.offsetHeight / 50; + if (height > 3) { display.cachedTextHeight = height; } + removeChildren(display.measure); + return height || 1 + } + + // Compute the default character width. + function charWidth(display) { + if (display.cachedCharWidth != null) { return display.cachedCharWidth } + var anchor = elt("span", "xxxxxxxxxx"); + var pre = elt("pre", [anchor], "CodeMirror-line-like"); + removeChildrenAndAdd(display.measure, pre); + var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10; + if (width > 2) { display.cachedCharWidth = width; } + return width || 10 + } + + // Do a bulk-read of the DOM positions and sizes needed to draw the + // view, so that we don't interleave reading and writing to the DOM. + function getDimensions(cm) { + var d = cm.display, left = {}, width = {}; + var gutterLeft = d.gutters.clientLeft; + for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) { + var id = cm.display.gutterSpecs[i].className; + left[id] = n.offsetLeft + n.clientLeft + gutterLeft; + width[id] = n.clientWidth; + } + return {fixedPos: compensateForHScroll(d), + gutterTotalWidth: d.gutters.offsetWidth, + gutterLeft: left, + gutterWidth: width, + wrapperWidth: d.wrapper.clientWidth} + } + + // Computes display.scroller.scrollLeft + display.gutters.offsetWidth, + // but using getBoundingClientRect to get a sub-pixel-accurate + // result. + function compensateForHScroll(display) { + return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left + } + + // Returns a function that estimates the height of a line, to use as + // first approximation until the line becomes visible (and is thus + // properly measurable). + function estimateHeight(cm) { + var th = textHeight(cm.display), wrapping = cm.options.lineWrapping; + var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3); + return function (line) { + if (lineIsHidden(cm.doc, line)) { return 0 } + + var widgetsHeight = 0; + if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) { + if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; } + } } + + if (wrapping) + { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th } + else + { return widgetsHeight + th } + } + } + + function estimateLineHeights(cm) { + var doc = cm.doc, est = estimateHeight(cm); + doc.iter(function (line) { + var estHeight = est(line); + if (estHeight != line.height) { updateLineHeight(line, estHeight); } + }); + } + + // Given a mouse event, find the corresponding position. If liberal + // is false, it checks whether a gutter or scrollbar was clicked, + // and returns null if it was. forRect is used by rectangular + // selections, and tries to estimate a character position even for + // coordinates beyond the right of the text. + function posFromMouse(cm, e, liberal, forRect) { + var display = cm.display; + if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null } + + var x, y, space = display.lineSpace.getBoundingClientRect(); + // Fails unpredictably on IE[67] when mouse is dragged around quickly. + try { x = e.clientX - space.left; y = e.clientY - space.top; } + catch (e$1) { return null } + var coords = coordsChar(cm, x, y), line; + if (forRect && coords.xRel > 0 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) { + var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length; + coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff)); + } + return coords + } + + // Find the view element corresponding to a given line. Return null + // when the line isn't visible. + function findViewIndex(cm, n) { + if (n >= cm.display.viewTo) { return null } + n -= cm.display.viewFrom; + if (n < 0) { return null } + var view = cm.display.view; + for (var i = 0; i < view.length; i++) { + n -= view[i].size; + if (n < 0) { return i } + } + } + + // Updates the display.view data structure for a given change to the + // document. From and to are in pre-change coordinates. Lendiff is + // the amount of lines added or subtracted by the change. This is + // used for changes that span multiple lines, or change the way + // lines are divided into visual lines. regLineChange (below) + // registers single-line changes. + function regChange(cm, from, to, lendiff) { + if (from == null) { from = cm.doc.first; } + if (to == null) { to = cm.doc.first + cm.doc.size; } + if (!lendiff) { lendiff = 0; } + + var display = cm.display; + if (lendiff && to < display.viewTo && + (display.updateLineNumbers == null || display.updateLineNumbers > from)) + { display.updateLineNumbers = from; } + + cm.curOp.viewChanged = true; + + if (from >= display.viewTo) { // Change after + if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo) + { resetView(cm); } + } else if (to <= display.viewFrom) { // Change before + if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) { + resetView(cm); + } else { + display.viewFrom += lendiff; + display.viewTo += lendiff; + } + } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap + resetView(cm); + } else if (from <= display.viewFrom) { // Top overlap + var cut = viewCuttingPoint(cm, to, to + lendiff, 1); + if (cut) { + display.view = display.view.slice(cut.index); + display.viewFrom = cut.lineN; + display.viewTo += lendiff; + } else { + resetView(cm); + } + } else if (to >= display.viewTo) { // Bottom overlap + var cut$1 = viewCuttingPoint(cm, from, from, -1); + if (cut$1) { + display.view = display.view.slice(0, cut$1.index); + display.viewTo = cut$1.lineN; + } else { + resetView(cm); + } + } else { // Gap in the middle + var cutTop = viewCuttingPoint(cm, from, from, -1); + var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1); + if (cutTop && cutBot) { + display.view = display.view.slice(0, cutTop.index) + .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN)) + .concat(display.view.slice(cutBot.index)); + display.viewTo += lendiff; + } else { + resetView(cm); + } + } + + var ext = display.externalMeasured; + if (ext) { + if (to < ext.lineN) + { ext.lineN += lendiff; } + else if (from < ext.lineN + ext.size) + { display.externalMeasured = null; } + } + } + + // Register a change to a single line. Type must be one of "text", + // "gutter", "class", "widget" + function regLineChange(cm, line, type) { + cm.curOp.viewChanged = true; + var display = cm.display, ext = cm.display.externalMeasured; + if (ext && line >= ext.lineN && line < ext.lineN + ext.size) + { display.externalMeasured = null; } + + if (line < display.viewFrom || line >= display.viewTo) { return } + var lineView = display.view[findViewIndex(cm, line)]; + if (lineView.node == null) { return } + var arr = lineView.changes || (lineView.changes = []); + if (indexOf(arr, type) == -1) { arr.push(type); } + } + + // Clear the view. + function resetView(cm) { + cm.display.viewFrom = cm.display.viewTo = cm.doc.first; + cm.display.view = []; + cm.display.viewOffset = 0; + } + + function viewCuttingPoint(cm, oldN, newN, dir) { + var index = findViewIndex(cm, oldN), diff, view = cm.display.view; + if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size) + { return {index: index, lineN: newN} } + var n = cm.display.viewFrom; + for (var i = 0; i < index; i++) + { n += view[i].size; } + if (n != oldN) { + if (dir > 0) { + if (index == view.length - 1) { return null } + diff = (n + view[index].size) - oldN; + index++; + } else { + diff = n - oldN; + } + oldN += diff; newN += diff; + } + while (visualLineNo(cm.doc, newN) != newN) { + if (index == (dir < 0 ? 0 : view.length - 1)) { return null } + newN += dir * view[index - (dir < 0 ? 1 : 0)].size; + index += dir; + } + return {index: index, lineN: newN} + } + + // Force the view to cover a given range, adding empty view element + // or clipping off existing ones as needed. + function adjustView(cm, from, to) { + var display = cm.display, view = display.view; + if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) { + display.view = buildViewArray(cm, from, to); + display.viewFrom = from; + } else { + if (display.viewFrom > from) + { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); } + else if (display.viewFrom < from) + { display.view = display.view.slice(findViewIndex(cm, from)); } + display.viewFrom = from; + if (display.viewTo < to) + { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); } + else if (display.viewTo > to) + { display.view = display.view.slice(0, findViewIndex(cm, to)); } + } + display.viewTo = to; + } + + // Count the number of lines in the view whose DOM representation is + // out of date (or nonexistent). + function countDirtyView(cm) { + var view = cm.display.view, dirty = 0; + for (var i = 0; i < view.length; i++) { + var lineView = view[i]; + if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; } + } + return dirty + } + + function updateSelection(cm) { + cm.display.input.showSelection(cm.display.input.prepareSelection()); + } + + function prepareSelection(cm, primary) { + if ( primary === void 0 ) primary = true; + + var doc = cm.doc, result = {}; + var curFragment = result.cursors = document.createDocumentFragment(); + var selFragment = result.selection = document.createDocumentFragment(); + + for (var i = 0; i < doc.sel.ranges.length; i++) { + if (!primary && i == doc.sel.primIndex) { continue } + var range = doc.sel.ranges[i]; + if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue } + var collapsed = range.empty(); + if (collapsed || cm.options.showCursorWhenSelecting) + { drawSelectionCursor(cm, range.head, curFragment); } + if (!collapsed) + { drawSelectionRange(cm, range, selFragment); } + } + return result + } + + // Draws a cursor for the given range + function drawSelectionCursor(cm, head, output) { + var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine); + + var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")); + cursor.style.left = pos.left + "px"; + cursor.style.top = pos.top + "px"; + cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"; + + if (pos.other) { + // Secondary cursor, shown when on a 'jump' in bi-directional text + var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor")); + otherCursor.style.display = ""; + otherCursor.style.left = pos.other.left + "px"; + otherCursor.style.top = pos.other.top + "px"; + otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"; + } + } + + function cmpCoords(a, b) { return a.top - b.top || a.left - b.left } + + // Draws the given range as a highlighted selection + function drawSelectionRange(cm, range, output) { + var display = cm.display, doc = cm.doc; + var fragment = document.createDocumentFragment(); + var padding = paddingH(cm.display), leftSide = padding.left; + var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right; + var docLTR = doc.direction == "ltr"; + + function add(left, top, width, bottom) { + if (top < 0) { top = 0; } + top = Math.round(top); + bottom = Math.round(bottom); + fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: absolute; left: " + left + "px;\n top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px;\n height: " + (bottom - top) + "px"))); + } + + function drawForLine(line, fromArg, toArg) { + var lineObj = getLine(doc, line); + var lineLen = lineObj.text.length; + var start, end; + function coords(ch, bias) { + return charCoords(cm, Pos(line, ch), "div", lineObj, bias) + } + + function wrapX(pos, dir, side) { + var extent = wrappedLineExtentChar(cm, lineObj, null, pos); + var prop = (dir == "ltr") == (side == "after") ? "left" : "right"; + var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1); + return coords(ch, prop)[prop] + } + + var order = getOrder(lineObj, doc.direction); + iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) { + var ltr = dir == "ltr"; + var fromPos = coords(from, ltr ? "left" : "right"); + var toPos = coords(to - 1, ltr ? "right" : "left"); + + var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen; + var first = i == 0, last = !order || i == order.length - 1; + if (toPos.top - fromPos.top <= 3) { // Single line + var openLeft = (docLTR ? openStart : openEnd) && first; + var openRight = (docLTR ? openEnd : openStart) && last; + var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left; + var right = openRight ? rightSide : (ltr ? toPos : fromPos).right; + add(left, fromPos.top, right - left, fromPos.bottom); + } else { // Multiple lines + var topLeft, topRight, botLeft, botRight; + if (ltr) { + topLeft = docLTR && openStart && first ? leftSide : fromPos.left; + topRight = docLTR ? rightSide : wrapX(from, dir, "before"); + botLeft = docLTR ? leftSide : wrapX(to, dir, "after"); + botRight = docLTR && openEnd && last ? rightSide : toPos.right; + } else { + topLeft = !docLTR ? leftSide : wrapX(from, dir, "before"); + topRight = !docLTR && openStart && first ? rightSide : fromPos.right; + botLeft = !docLTR && openEnd && last ? leftSide : toPos.left; + botRight = !docLTR ? rightSide : wrapX(to, dir, "after"); + } + add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom); + if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); } + add(botLeft, toPos.top, botRight - botLeft, toPos.bottom); + } + + if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; } + if (cmpCoords(toPos, start) < 0) { start = toPos; } + if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; } + if (cmpCoords(toPos, end) < 0) { end = toPos; } + }); + return {start: start, end: end} + } + + var sFrom = range.from(), sTo = range.to(); + if (sFrom.line == sTo.line) { + drawForLine(sFrom.line, sFrom.ch, sTo.ch); + } else { + var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line); + var singleVLine = visualLine(fromLine) == visualLine(toLine); + var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end; + var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start; + if (singleVLine) { + if (leftEnd.top < rightStart.top - 2) { + add(leftEnd.right, leftEnd.top, null, leftEnd.bottom); + add(leftSide, rightStart.top, rightStart.left, rightStart.bottom); + } else { + add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom); + } + } + if (leftEnd.bottom < rightStart.top) + { add(leftSide, leftEnd.bottom, null, rightStart.top); } + } + + output.appendChild(fragment); + } + + // Cursor-blinking + function restartBlink(cm) { + if (!cm.state.focused) { return } + var display = cm.display; + clearInterval(display.blinker); + var on = true; + display.cursorDiv.style.visibility = ""; + if (cm.options.cursorBlinkRate > 0) + { display.blinker = setInterval(function () { + if (!cm.hasFocus()) { onBlur(cm); } + display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; + }, cm.options.cursorBlinkRate); } + else if (cm.options.cursorBlinkRate < 0) + { display.cursorDiv.style.visibility = "hidden"; } + } + + function ensureFocus(cm) { + if (!cm.hasFocus()) { + cm.display.input.focus(); + if (!cm.state.focused) { onFocus(cm); } + } + } + + function delayBlurEvent(cm) { + cm.state.delayingBlurEvent = true; + setTimeout(function () { if (cm.state.delayingBlurEvent) { + cm.state.delayingBlurEvent = false; + if (cm.state.focused) { onBlur(cm); } + } }, 100); + } + + function onFocus(cm, e) { + if (cm.state.delayingBlurEvent && !cm.state.draggingText) { cm.state.delayingBlurEvent = false; } + + if (cm.options.readOnly == "nocursor") { return } + if (!cm.state.focused) { + signal(cm, "focus", cm, e); + cm.state.focused = true; + addClass(cm.display.wrapper, "CodeMirror-focused"); + // This test prevents this from firing when a context + // menu is closed (since the input reset would kill the + // select-all detection hack) + if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) { + cm.display.input.reset(); + if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730 + } + cm.display.input.receivedFocus(); + } + restartBlink(cm); + } + function onBlur(cm, e) { + if (cm.state.delayingBlurEvent) { return } + + if (cm.state.focused) { + signal(cm, "blur", cm, e); + cm.state.focused = false; + rmClass(cm.display.wrapper, "CodeMirror-focused"); + } + clearInterval(cm.display.blinker); + setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150); + } + + // Read the actual heights of the rendered lines, and update their + // stored heights to match. + function updateHeightsInViewport(cm) { + var display = cm.display; + var prevBottom = display.lineDiv.offsetTop; + for (var i = 0; i < display.view.length; i++) { + var cur = display.view[i], wrapping = cm.options.lineWrapping; + var height = (void 0), width = 0; + if (cur.hidden) { continue } + if (ie && ie_version < 8) { + var bot = cur.node.offsetTop + cur.node.offsetHeight; + height = bot - prevBottom; + prevBottom = bot; + } else { + var box = cur.node.getBoundingClientRect(); + height = box.bottom - box.top; + // Check that lines don't extend past the right of the current + // editor width + if (!wrapping && cur.text.firstChild) + { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; } + } + var diff = cur.line.height - height; + if (diff > .005 || diff < -.005) { + updateLineHeight(cur.line, height); + updateWidgetHeight(cur.line); + if (cur.rest) { for (var j = 0; j < cur.rest.length; j++) + { updateWidgetHeight(cur.rest[j]); } } + } + if (width > cm.display.sizerWidth) { + var chWidth = Math.ceil(width / charWidth(cm.display)); + if (chWidth > cm.display.maxLineLength) { + cm.display.maxLineLength = chWidth; + cm.display.maxLine = cur.line; + cm.display.maxLineChanged = true; + } + } + } + } + + // Read and store the height of line widgets associated with the + // given line. + function updateWidgetHeight(line) { + if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) { + var w = line.widgets[i], parent = w.node.parentNode; + if (parent) { w.height = parent.offsetHeight; } + } } + } + + // Compute the lines that are visible in a given viewport (defaults + // the the current scroll position). viewport may contain top, + // height, and ensure (see op.scrollToPos) properties. + function visibleLines(display, doc, viewport) { + var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop; + top = Math.floor(top - paddingTop(display)); + var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight; + + var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom); + // Ensure is a {from: {line, ch}, to: {line, ch}} object, and + // forces those lines into the viewport (if possible). + if (viewport && viewport.ensure) { + var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line; + if (ensureFrom < from) { + from = ensureFrom; + to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight); + } else if (Math.min(ensureTo, doc.lastLine()) >= to) { + from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight); + to = ensureTo; + } + } + return {from: from, to: Math.max(to, from + 1)} + } + + // SCROLLING THINGS INTO VIEW + + // If an editor sits on the top or bottom of the window, partially + // scrolled out of view, this ensures that the cursor is visible. + function maybeScrollWindow(cm, rect) { + if (signalDOMEvent(cm, "scrollCursorIntoView")) { return } + + var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null; + if (rect.top + box.top < 0) { doScroll = true; } + else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; } + if (doScroll != null && !phantom) { + var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n top: " + (rect.top - display.viewOffset - paddingTop(cm.display)) + "px;\n height: " + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + "px;\n left: " + (rect.left) + "px; width: " + (Math.max(2, rect.right - rect.left)) + "px;")); + cm.display.lineSpace.appendChild(scrollNode); + scrollNode.scrollIntoView(doScroll); + cm.display.lineSpace.removeChild(scrollNode); + } + } + + // Scroll a given position into view (immediately), verifying that + // it actually became visible (as line heights are accurately + // measured, the position of something may 'drift' during drawing). + function scrollPosIntoView(cm, pos, end, margin) { + if (margin == null) { margin = 0; } + var rect; + if (!cm.options.lineWrapping && pos == end) { + // Set pos and end to the cursor positions around the character pos sticks to + // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch + // If pos == Pos(_, 0, "before"), pos and end are unchanged + end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos; + pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos; + } + for (var limit = 0; limit < 5; limit++) { + var changed = false; + var coords = cursorCoords(cm, pos); + var endCoords = !end || end == pos ? coords : cursorCoords(cm, end); + rect = {left: Math.min(coords.left, endCoords.left), + top: Math.min(coords.top, endCoords.top) - margin, + right: Math.max(coords.left, endCoords.left), + bottom: Math.max(coords.bottom, endCoords.bottom) + margin}; + var scrollPos = calculateScrollPos(cm, rect); + var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft; + if (scrollPos.scrollTop != null) { + updateScrollTop(cm, scrollPos.scrollTop); + if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; } + } + if (scrollPos.scrollLeft != null) { + setScrollLeft(cm, scrollPos.scrollLeft); + if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; } + } + if (!changed) { break } + } + return rect + } + + // Scroll a given set of coordinates into view (immediately). + function scrollIntoView(cm, rect) { + var scrollPos = calculateScrollPos(cm, rect); + if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); } + if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); } + } + + // Calculate a new scroll position needed to scroll the given + // rectangle into view. Returns an object with scrollTop and + // scrollLeft properties. When these are undefined, the + // vertical/horizontal position does not need to be adjusted. + function calculateScrollPos(cm, rect) { + var display = cm.display, snapMargin = textHeight(cm.display); + if (rect.top < 0) { rect.top = 0; } + var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop; + var screen = displayHeight(cm), result = {}; + if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; } + var docBottom = cm.doc.height + paddingVert(display); + var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin; + if (rect.top < screentop) { + result.scrollTop = atTop ? 0 : rect.top; + } else if (rect.bottom > screentop + screen) { + var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen); + if (newTop != screentop) { result.scrollTop = newTop; } + } + + var gutterSpace = cm.options.fixedGutter ? 0 : display.gutters.offsetWidth; + var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft - gutterSpace; + var screenw = displayWidth(cm) - display.gutters.offsetWidth; + var tooWide = rect.right - rect.left > screenw; + if (tooWide) { rect.right = rect.left + screenw; } + if (rect.left < 10) + { result.scrollLeft = 0; } + else if (rect.left < screenleft) + { result.scrollLeft = Math.max(0, rect.left + gutterSpace - (tooWide ? 0 : 10)); } + else if (rect.right > screenw + screenleft - 3) + { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; } + return result + } + + // Store a relative adjustment to the scroll position in the current + // operation (to be applied when the operation finishes). + function addToScrollTop(cm, top) { + if (top == null) { return } + resolveScrollToPos(cm); + cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top; + } + + // Make sure that at the end of the operation the current cursor is + // shown. + function ensureCursorVisible(cm) { + resolveScrollToPos(cm); + var cur = cm.getCursor(); + cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin}; + } + + function scrollToCoords(cm, x, y) { + if (x != null || y != null) { resolveScrollToPos(cm); } + if (x != null) { cm.curOp.scrollLeft = x; } + if (y != null) { cm.curOp.scrollTop = y; } + } + + function scrollToRange(cm, range) { + resolveScrollToPos(cm); + cm.curOp.scrollToPos = range; + } + + // When an operation has its scrollToPos property set, and another + // scroll action is applied before the end of the operation, this + // 'simulates' scrolling that position into view in a cheap way, so + // that the effect of intermediate scroll commands is not ignored. + function resolveScrollToPos(cm) { + var range = cm.curOp.scrollToPos; + if (range) { + cm.curOp.scrollToPos = null; + var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to); + scrollToCoordsRange(cm, from, to, range.margin); + } + } + + function scrollToCoordsRange(cm, from, to, margin) { + var sPos = calculateScrollPos(cm, { + left: Math.min(from.left, to.left), + top: Math.min(from.top, to.top) - margin, + right: Math.max(from.right, to.right), + bottom: Math.max(from.bottom, to.bottom) + margin + }); + scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop); + } + + // Sync the scrollable area and scrollbars, ensure the viewport + // covers the visible area. + function updateScrollTop(cm, val) { + if (Math.abs(cm.doc.scrollTop - val) < 2) { return } + if (!gecko) { updateDisplaySimple(cm, {top: val}); } + setScrollTop(cm, val, true); + if (gecko) { updateDisplaySimple(cm); } + startWorker(cm, 100); + } + + function setScrollTop(cm, val, forceScroll) { + val = Math.max(0, Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val)); + if (cm.display.scroller.scrollTop == val && !forceScroll) { return } + cm.doc.scrollTop = val; + cm.display.scrollbars.setScrollTop(val); + if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; } + } + + // Sync scroller and scrollbar, ensure the gutter elements are + // aligned. + function setScrollLeft(cm, val, isScroller, forceScroll) { + val = Math.max(0, Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth)); + if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return } + cm.doc.scrollLeft = val; + alignHorizontally(cm); + if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; } + cm.display.scrollbars.setScrollLeft(val); + } + + // SCROLLBARS + + // Prepare DOM reads needed to update the scrollbars. Done in one + // shot to minimize update/measure roundtrips. + function measureForScrollbars(cm) { + var d = cm.display, gutterW = d.gutters.offsetWidth; + var docH = Math.round(cm.doc.height + paddingVert(cm.display)); + return { + clientHeight: d.scroller.clientHeight, + viewHeight: d.wrapper.clientHeight, + scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth, + viewWidth: d.wrapper.clientWidth, + barLeft: cm.options.fixedGutter ? gutterW : 0, + docHeight: docH, + scrollHeight: docH + scrollGap(cm) + d.barHeight, + nativeBarWidth: d.nativeBarWidth, + gutterWidth: gutterW + } + } + + var NativeScrollbars = function(place, scroll, cm) { + this.cm = cm; + var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar"); + var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar"); + vert.tabIndex = horiz.tabIndex = -1; + place(vert); place(horiz); + + on(vert, "scroll", function () { + if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); } + }); + on(horiz, "scroll", function () { + if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); } + }); + + this.checkedZeroWidth = false; + // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). + if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; } + }; + + NativeScrollbars.prototype.update = function (measure) { + var needsH = measure.scrollWidth > measure.clientWidth + 1; + var needsV = measure.scrollHeight > measure.clientHeight + 1; + var sWidth = measure.nativeBarWidth; + + if (needsV) { + this.vert.style.display = "block"; + this.vert.style.bottom = needsH ? sWidth + "px" : "0"; + var totalHeight = measure.viewHeight - (needsH ? sWidth : 0); + // A bug in IE8 can cause this value to be negative, so guard it. + this.vert.firstChild.style.height = + Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"; + } else { + this.vert.style.display = ""; + this.vert.firstChild.style.height = "0"; + } + + if (needsH) { + this.horiz.style.display = "block"; + this.horiz.style.right = needsV ? sWidth + "px" : "0"; + this.horiz.style.left = measure.barLeft + "px"; + var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0); + this.horiz.firstChild.style.width = + Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px"; + } else { + this.horiz.style.display = ""; + this.horiz.firstChild.style.width = "0"; + } + + if (!this.checkedZeroWidth && measure.clientHeight > 0) { + if (sWidth == 0) { this.zeroWidthHack(); } + this.checkedZeroWidth = true; + } + + return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0} + }; + + NativeScrollbars.prototype.setScrollLeft = function (pos) { + if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; } + if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); } + }; + + NativeScrollbars.prototype.setScrollTop = function (pos) { + if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; } + if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); } + }; + + NativeScrollbars.prototype.zeroWidthHack = function () { + var w = mac && !mac_geMountainLion ? "12px" : "18px"; + this.horiz.style.height = this.vert.style.width = w; + this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none"; + this.disableHoriz = new Delayed; + this.disableVert = new Delayed; + }; + + NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) { + bar.style.pointerEvents = "auto"; + function maybeDisable() { + // To find out whether the scrollbar is still visible, we + // check whether the element under the pixel in the bottom + // right corner of the scrollbar box is the scrollbar box + // itself (when the bar is still visible) or its filler child + // (when the bar is hidden). If it is still visible, we keep + // it enabled, if it's hidden, we disable pointer events. + var box = bar.getBoundingClientRect(); + var elt = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2) + : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1); + if (elt != bar) { bar.style.pointerEvents = "none"; } + else { delay.set(1000, maybeDisable); } + } + delay.set(1000, maybeDisable); + }; + + NativeScrollbars.prototype.clear = function () { + var parent = this.horiz.parentNode; + parent.removeChild(this.horiz); + parent.removeChild(this.vert); + }; + + var NullScrollbars = function () {}; + + NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} }; + NullScrollbars.prototype.setScrollLeft = function () {}; + NullScrollbars.prototype.setScrollTop = function () {}; + NullScrollbars.prototype.clear = function () {}; + + function updateScrollbars(cm, measure) { + if (!measure) { measure = measureForScrollbars(cm); } + var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight; + updateScrollbarsInner(cm, measure); + for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) { + if (startWidth != cm.display.barWidth && cm.options.lineWrapping) + { updateHeightsInViewport(cm); } + updateScrollbarsInner(cm, measureForScrollbars(cm)); + startWidth = cm.display.barWidth; startHeight = cm.display.barHeight; + } + } + + // Re-synchronize the fake scrollbars with the actual size of the + // content. + function updateScrollbarsInner(cm, measure) { + var d = cm.display; + var sizes = d.scrollbars.update(measure); + + d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"; + d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"; + d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"; + + if (sizes.right && sizes.bottom) { + d.scrollbarFiller.style.display = "block"; + d.scrollbarFiller.style.height = sizes.bottom + "px"; + d.scrollbarFiller.style.width = sizes.right + "px"; + } else { d.scrollbarFiller.style.display = ""; } + if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) { + d.gutterFiller.style.display = "block"; + d.gutterFiller.style.height = sizes.bottom + "px"; + d.gutterFiller.style.width = measure.gutterWidth + "px"; + } else { d.gutterFiller.style.display = ""; } + } + + var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}; + + function initScrollbars(cm) { + if (cm.display.scrollbars) { + cm.display.scrollbars.clear(); + if (cm.display.scrollbars.addClass) + { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); } + } + + cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) { + cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller); + // Prevent clicks in the scrollbars from killing focus + on(node, "mousedown", function () { + if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); } + }); + node.setAttribute("cm-not-content", "true"); + }, function (pos, axis) { + if (axis == "horizontal") { setScrollLeft(cm, pos); } + else { updateScrollTop(cm, pos); } + }, cm); + if (cm.display.scrollbars.addClass) + { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); } + } + + // Operations are used to wrap a series of changes to the editor + // state in such a way that each change won't have to update the + // cursor and display (which would be awkward, slow, and + // error-prone). Instead, display updates are batched and then all + // combined and executed at once. + + var nextOpId = 0; + // Start a new operation. + function startOperation(cm) { + cm.curOp = { + cm: cm, + viewChanged: false, // Flag that indicates that lines might need to be redrawn + startHeight: cm.doc.height, // Used to detect need to update scrollbar + forceUpdate: false, // Used to force a redraw + updateInput: 0, // Whether to reset the input textarea + typing: false, // Whether this reset should be careful to leave existing text (for compositing) + changeObjs: null, // Accumulated changes, for firing change events + cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on + cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already + selectionChanged: false, // Whether the selection needs to be redrawn + updateMaxLine: false, // Set when the widest line needs to be determined anew + scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet + scrollToPos: null, // Used to scroll to a specific position + focus: false, + id: ++nextOpId, // Unique ID + markArrays: null // Used by addMarkedSpan + }; + pushOperation(cm.curOp); + } + + // Finish an operation, updating the display and signalling delayed events + function endOperation(cm) { + var op = cm.curOp; + if (op) { finishOperation(op, function (group) { + for (var i = 0; i < group.ops.length; i++) + { group.ops[i].cm.curOp = null; } + endOperations(group); + }); } + } + + // The DOM updates done when an operation finishes are batched so + // that the minimum number of relayouts are required. + function endOperations(group) { + var ops = group.ops; + for (var i = 0; i < ops.length; i++) // Read DOM + { endOperation_R1(ops[i]); } + for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe) + { endOperation_W1(ops[i$1]); } + for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM + { endOperation_R2(ops[i$2]); } + for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe) + { endOperation_W2(ops[i$3]); } + for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM + { endOperation_finish(ops[i$4]); } + } + + function endOperation_R1(op) { + var cm = op.cm, display = cm.display; + maybeClipScrollbars(cm); + if (op.updateMaxLine) { findMaxLine(cm); } + + op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null || + op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom || + op.scrollToPos.to.line >= display.viewTo) || + display.maxLineChanged && cm.options.lineWrapping; + op.update = op.mustUpdate && + new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate); + } + + function endOperation_W1(op) { + op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update); + } + + function endOperation_R2(op) { + var cm = op.cm, display = cm.display; + if (op.updatedDisplay) { updateHeightsInViewport(cm); } + + op.barMeasure = measureForScrollbars(cm); + + // If the max line changed since it was last measured, measure it, + // and ensure the document's width matches it. + // updateDisplay_W2 will use these properties to do the actual resizing + if (display.maxLineChanged && !cm.options.lineWrapping) { + op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3; + cm.display.sizerWidth = op.adjustWidthTo; + op.barMeasure.scrollWidth = + Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth); + op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm)); + } + + if (op.updatedDisplay || op.selectionChanged) + { op.preparedSelection = display.input.prepareSelection(); } + } + + function endOperation_W2(op) { + var cm = op.cm; + + if (op.adjustWidthTo != null) { + cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"; + if (op.maxScrollLeft < cm.doc.scrollLeft) + { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); } + cm.display.maxLineChanged = false; + } + + var takeFocus = op.focus && op.focus == activeElt(); + if (op.preparedSelection) + { cm.display.input.showSelection(op.preparedSelection, takeFocus); } + if (op.updatedDisplay || op.startHeight != cm.doc.height) + { updateScrollbars(cm, op.barMeasure); } + if (op.updatedDisplay) + { setDocumentHeight(cm, op.barMeasure); } + + if (op.selectionChanged) { restartBlink(cm); } + + if (cm.state.focused && op.updateInput) + { cm.display.input.reset(op.typing); } + if (takeFocus) { ensureFocus(op.cm); } + } + + function endOperation_finish(op) { + var cm = op.cm, display = cm.display, doc = cm.doc; + + if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); } + + // Abort mouse wheel delta measurement, when scrolling explicitly + if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos)) + { display.wheelStartX = display.wheelStartY = null; } + + // Propagate the scroll position to the actual DOM scroller + if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); } + + if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); } + // If we need to scroll a specific position into view, do so. + if (op.scrollToPos) { + var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from), + clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin); + maybeScrollWindow(cm, rect); + } + + // Fire events for markers that are hidden/unidden by editing or + // undoing + var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers; + if (hidden) { for (var i = 0; i < hidden.length; ++i) + { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } } + if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1) + { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } } + + if (display.wrapper.offsetHeight) + { doc.scrollTop = cm.display.scroller.scrollTop; } + + // Fire change events, and delayed event handlers + if (op.changeObjs) + { signal(cm, "changes", cm, op.changeObjs); } + if (op.update) + { op.update.finish(); } + } + + // Run the given function in an operation + function runInOp(cm, f) { + if (cm.curOp) { return f() } + startOperation(cm); + try { return f() } + finally { endOperation(cm); } + } + // Wraps a function in an operation. Returns the wrapped function. + function operation(cm, f) { + return function() { + if (cm.curOp) { return f.apply(cm, arguments) } + startOperation(cm); + try { return f.apply(cm, arguments) } + finally { endOperation(cm); } + } + } + // Used to add methods to editor and doc instances, wrapping them in + // operations. + function methodOp(f) { + return function() { + if (this.curOp) { return f.apply(this, arguments) } + startOperation(this); + try { return f.apply(this, arguments) } + finally { endOperation(this); } + } + } + function docMethodOp(f) { + return function() { + var cm = this.cm; + if (!cm || cm.curOp) { return f.apply(this, arguments) } + startOperation(cm); + try { return f.apply(this, arguments) } + finally { endOperation(cm); } + } + } + + // HIGHLIGHT WORKER + + function startWorker(cm, time) { + if (cm.doc.highlightFrontier < cm.display.viewTo) + { cm.state.highlight.set(time, bind(highlightWorker, cm)); } + } + + function highlightWorker(cm) { + var doc = cm.doc; + if (doc.highlightFrontier >= cm.display.viewTo) { return } + var end = +new Date + cm.options.workTime; + var context = getContextBefore(cm, doc.highlightFrontier); + var changedLines = []; + + doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) { + if (context.line >= cm.display.viewFrom) { // Visible + var oldStyles = line.styles; + var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null; + var highlighted = highlightLine(cm, line, context, true); + if (resetState) { context.state = resetState; } + line.styles = highlighted.styles; + var oldCls = line.styleClasses, newCls = highlighted.classes; + if (newCls) { line.styleClasses = newCls; } + else if (oldCls) { line.styleClasses = null; } + var ischange = !oldStyles || oldStyles.length != line.styles.length || + oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass); + for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; } + if (ischange) { changedLines.push(context.line); } + line.stateAfter = context.save(); + context.nextLine(); + } else { + if (line.text.length <= cm.options.maxHighlightLength) + { processLine(cm, line.text, context); } + line.stateAfter = context.line % 5 == 0 ? context.save() : null; + context.nextLine(); + } + if (+new Date > end) { + startWorker(cm, cm.options.workDelay); + return true + } + }); + doc.highlightFrontier = context.line; + doc.modeFrontier = Math.max(doc.modeFrontier, context.line); + if (changedLines.length) { runInOp(cm, function () { + for (var i = 0; i < changedLines.length; i++) + { regLineChange(cm, changedLines[i], "text"); } + }); } + } + + // DISPLAY DRAWING + + var DisplayUpdate = function(cm, viewport, force) { + var display = cm.display; + + this.viewport = viewport; + // Store some values that we'll need later (but don't want to force a relayout for) + this.visible = visibleLines(display, cm.doc, viewport); + this.editorIsHidden = !display.wrapper.offsetWidth; + this.wrapperHeight = display.wrapper.clientHeight; + this.wrapperWidth = display.wrapper.clientWidth; + this.oldDisplayWidth = displayWidth(cm); + this.force = force; + this.dims = getDimensions(cm); + this.events = []; + }; + + DisplayUpdate.prototype.signal = function (emitter, type) { + if (hasHandler(emitter, type)) + { this.events.push(arguments); } + }; + DisplayUpdate.prototype.finish = function () { + for (var i = 0; i < this.events.length; i++) + { signal.apply(null, this.events[i]); } + }; + + function maybeClipScrollbars(cm) { + var display = cm.display; + if (!display.scrollbarsClipped && display.scroller.offsetWidth) { + display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth; + display.heightForcer.style.height = scrollGap(cm) + "px"; + display.sizer.style.marginBottom = -display.nativeBarWidth + "px"; + display.sizer.style.borderRightWidth = scrollGap(cm) + "px"; + display.scrollbarsClipped = true; + } + } + + function selectionSnapshot(cm) { + if (cm.hasFocus()) { return null } + var active = activeElt(); + if (!active || !contains(cm.display.lineDiv, active)) { return null } + var result = {activeElt: active}; + if (window.getSelection) { + var sel = window.getSelection(); + if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) { + result.anchorNode = sel.anchorNode; + result.anchorOffset = sel.anchorOffset; + result.focusNode = sel.focusNode; + result.focusOffset = sel.focusOffset; + } + } + return result + } + + function restoreSelection(snapshot) { + if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return } + snapshot.activeElt.focus(); + if (!/^(INPUT|TEXTAREA)$/.test(snapshot.activeElt.nodeName) && + snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) { + var sel = window.getSelection(), range = document.createRange(); + range.setEnd(snapshot.anchorNode, snapshot.anchorOffset); + range.collapse(false); + sel.removeAllRanges(); + sel.addRange(range); + sel.extend(snapshot.focusNode, snapshot.focusOffset); + } + } + + // Does the actual updating of the line display. Bails out + // (returning false) when there is nothing to be done and forced is + // false. + function updateDisplayIfNeeded(cm, update) { + var display = cm.display, doc = cm.doc; + + if (update.editorIsHidden) { + resetView(cm); + return false + } + + // Bail out if the visible area is already rendered and nothing changed. + if (!update.force && + update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo && + (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) && + display.renderedView == display.view && countDirtyView(cm) == 0) + { return false } + + if (maybeUpdateLineNumberWidth(cm)) { + resetView(cm); + update.dims = getDimensions(cm); + } + + // Compute a suitable new viewport (from & to) + var end = doc.first + doc.size; + var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first); + var to = Math.min(end, update.visible.to + cm.options.viewportMargin); + if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); } + if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); } + if (sawCollapsedSpans) { + from = visualLineNo(cm.doc, from); + to = visualLineEndNo(cm.doc, to); + } + + var different = from != display.viewFrom || to != display.viewTo || + display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth; + adjustView(cm, from, to); + + display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom)); + // Position the mover div to align with the current scroll position + cm.display.mover.style.top = display.viewOffset + "px"; + + var toUpdate = countDirtyView(cm); + if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view && + (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo)) + { return false } + + // For big changes, we hide the enclosing element during the + // update, since that speeds up the operations on most browsers. + var selSnapshot = selectionSnapshot(cm); + if (toUpdate > 4) { display.lineDiv.style.display = "none"; } + patchDisplay(cm, display.updateLineNumbers, update.dims); + if (toUpdate > 4) { display.lineDiv.style.display = ""; } + display.renderedView = display.view; + // There might have been a widget with a focused element that got + // hidden or updated, if so re-focus it. + restoreSelection(selSnapshot); + + // Prevent selection and cursors from interfering with the scroll + // width and height. + removeChildren(display.cursorDiv); + removeChildren(display.selectionDiv); + display.gutters.style.height = display.sizer.style.minHeight = 0; + + if (different) { + display.lastWrapHeight = update.wrapperHeight; + display.lastWrapWidth = update.wrapperWidth; + startWorker(cm, 400); + } + + display.updateLineNumbers = null; + + return true + } + + function postUpdateDisplay(cm, update) { + var viewport = update.viewport; + + for (var first = true;; first = false) { + if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) { + // Clip forced viewport to actual scrollable area. + if (viewport && viewport.top != null) + { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; } + // Updated line heights might result in the drawn area not + // actually covering the viewport. Keep looping until it does. + update.visible = visibleLines(cm.display, cm.doc, viewport); + if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo) + { break } + } else if (first) { + update.visible = visibleLines(cm.display, cm.doc, viewport); + } + if (!updateDisplayIfNeeded(cm, update)) { break } + updateHeightsInViewport(cm); + var barMeasure = measureForScrollbars(cm); + updateSelection(cm); + updateScrollbars(cm, barMeasure); + setDocumentHeight(cm, barMeasure); + update.force = false; + } + + update.signal(cm, "update", cm); + if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) { + update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo); + cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo; + } + } + + function updateDisplaySimple(cm, viewport) { + var update = new DisplayUpdate(cm, viewport); + if (updateDisplayIfNeeded(cm, update)) { + updateHeightsInViewport(cm); + postUpdateDisplay(cm, update); + var barMeasure = measureForScrollbars(cm); + updateSelection(cm); + updateScrollbars(cm, barMeasure); + setDocumentHeight(cm, barMeasure); + update.finish(); + } + } + + // Sync the actual display DOM structure with display.view, removing + // nodes for lines that are no longer in view, and creating the ones + // that are not there yet, and updating the ones that are out of + // date. + function patchDisplay(cm, updateNumbersFrom, dims) { + var display = cm.display, lineNumbers = cm.options.lineNumbers; + var container = display.lineDiv, cur = container.firstChild; + + function rm(node) { + var next = node.nextSibling; + // Works around a throw-scroll bug in OS X Webkit + if (webkit && mac && cm.display.currentWheelTarget == node) + { node.style.display = "none"; } + else + { node.parentNode.removeChild(node); } + return next + } + + var view = display.view, lineN = display.viewFrom; + // Loop over the elements in the view, syncing cur (the DOM nodes + // in display.lineDiv) with the view as we go. + for (var i = 0; i < view.length; i++) { + var lineView = view[i]; + if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet + var node = buildLineElement(cm, lineView, lineN, dims); + container.insertBefore(node, cur); + } else { // Already drawn + while (cur != lineView.node) { cur = rm(cur); } + var updateNumber = lineNumbers && updateNumbersFrom != null && + updateNumbersFrom <= lineN && lineView.lineNumber; + if (lineView.changes) { + if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; } + updateLineForChanges(cm, lineView, lineN, dims); + } + if (updateNumber) { + removeChildren(lineView.lineNumber); + lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN))); + } + cur = lineView.node.nextSibling; + } + lineN += lineView.size; + } + while (cur) { cur = rm(cur); } + } + + function updateGutterSpace(display) { + var width = display.gutters.offsetWidth; + display.sizer.style.marginLeft = width + "px"; + // Send an event to consumers responding to changes in gutter width. + signalLater(display, "gutterChanged", display); + } + + function setDocumentHeight(cm, measure) { + cm.display.sizer.style.minHeight = measure.docHeight + "px"; + cm.display.heightForcer.style.top = measure.docHeight + "px"; + cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px"; + } + + // Re-align line numbers and gutter marks to compensate for + // horizontal scrolling. + function alignHorizontally(cm) { + var display = cm.display, view = display.view; + if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return } + var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft; + var gutterW = display.gutters.offsetWidth, left = comp + "px"; + for (var i = 0; i < view.length; i++) { if (!view[i].hidden) { + if (cm.options.fixedGutter) { + if (view[i].gutter) + { view[i].gutter.style.left = left; } + if (view[i].gutterBackground) + { view[i].gutterBackground.style.left = left; } + } + var align = view[i].alignable; + if (align) { for (var j = 0; j < align.length; j++) + { align[j].style.left = left; } } + } } + if (cm.options.fixedGutter) + { display.gutters.style.left = (comp + gutterW) + "px"; } + } + + // Used to ensure that the line number gutter is still the right + // size for the current document size. Returns true when an update + // is needed. + function maybeUpdateLineNumberWidth(cm) { + if (!cm.options.lineNumbers) { return false } + var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display; + if (last.length != display.lineNumChars) { + var test = display.measure.appendChild(elt("div", [elt("div", last)], + "CodeMirror-linenumber CodeMirror-gutter-elt")); + var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW; + display.lineGutter.style.width = ""; + display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1; + display.lineNumWidth = display.lineNumInnerWidth + padding; + display.lineNumChars = display.lineNumInnerWidth ? last.length : -1; + display.lineGutter.style.width = display.lineNumWidth + "px"; + updateGutterSpace(cm.display); + return true + } + return false + } + + function getGutters(gutters, lineNumbers) { + var result = [], sawLineNumbers = false; + for (var i = 0; i < gutters.length; i++) { + var name = gutters[i], style = null; + if (typeof name != "string") { style = name.style; name = name.className; } + if (name == "CodeMirror-linenumbers") { + if (!lineNumbers) { continue } + else { sawLineNumbers = true; } + } + result.push({className: name, style: style}); + } + if (lineNumbers && !sawLineNumbers) { result.push({className: "CodeMirror-linenumbers", style: null}); } + return result + } + + // Rebuild the gutter elements, ensure the margin to the left of the + // code matches their width. + function renderGutters(display) { + var gutters = display.gutters, specs = display.gutterSpecs; + removeChildren(gutters); + display.lineGutter = null; + for (var i = 0; i < specs.length; ++i) { + var ref = specs[i]; + var className = ref.className; + var style = ref.style; + var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + className)); + if (style) { gElt.style.cssText = style; } + if (className == "CodeMirror-linenumbers") { + display.lineGutter = gElt; + gElt.style.width = (display.lineNumWidth || 1) + "px"; + } + } + gutters.style.display = specs.length ? "" : "none"; + updateGutterSpace(display); + } + + function updateGutters(cm) { + renderGutters(cm.display); + regChange(cm); + alignHorizontally(cm); + } + + // The display handles the DOM integration, both for input reading + // and content drawing. It holds references to DOM nodes and + // display-related state. + + function Display(place, doc, input, options) { + var d = this; + this.input = input; + + // Covers bottom-right square when both scrollbars are present. + d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); + d.scrollbarFiller.setAttribute("cm-not-content", "true"); + // Covers bottom of gutter when coverGutterNextToScrollbar is on + // and h scrollbar is present. + d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler"); + d.gutterFiller.setAttribute("cm-not-content", "true"); + // Will contain the actual code, positioned to cover the viewport. + d.lineDiv = eltP("div", null, "CodeMirror-code"); + // Elements are added to these to represent selection and cursors. + d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); + d.cursorDiv = elt("div", null, "CodeMirror-cursors"); + // A visibility: hidden element used to find the size of things. + d.measure = elt("div", null, "CodeMirror-measure"); + // When lines outside of the viewport are measured, they are drawn in this. + d.lineMeasure = elt("div", null, "CodeMirror-measure"); + // Wraps everything that needs to exist inside the vertically-padded coordinate system + d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv], + null, "position: relative; outline: none"); + var lines = eltP("div", [d.lineSpace], "CodeMirror-lines"); + // Moved around its parent to cover visible view. + d.mover = elt("div", [lines], null, "position: relative"); + // Set to the height of the document, allowing scrolling. + d.sizer = elt("div", [d.mover], "CodeMirror-sizer"); + d.sizerWidth = null; + // Behavior of elts with overflow: auto and padding is + // inconsistent across browsers. This is used to ensure the + // scrollable area is big enough. + d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;"); + // Will contain the gutters, if any. + d.gutters = elt("div", null, "CodeMirror-gutters"); + d.lineGutter = null; + // Actual scrollable element. + d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll"); + d.scroller.setAttribute("tabIndex", "-1"); + // The element in which the editor lives. + d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror"); + + // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported) + if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; } + if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; } + + if (place) { + if (place.appendChild) { place.appendChild(d.wrapper); } + else { place(d.wrapper); } + } + + // Current rendered range (may be bigger than the view window). + d.viewFrom = d.viewTo = doc.first; + d.reportedViewFrom = d.reportedViewTo = doc.first; + // Information about the rendered lines. + d.view = []; + d.renderedView = null; + // Holds info about a single rendered line when it was rendered + // for measurement, while not in view. + d.externalMeasured = null; + // Empty space (in pixels) above the view + d.viewOffset = 0; + d.lastWrapHeight = d.lastWrapWidth = 0; + d.updateLineNumbers = null; + + d.nativeBarWidth = d.barHeight = d.barWidth = 0; + d.scrollbarsClipped = false; + + // Used to only resize the line number gutter when necessary (when + // the amount of lines crosses a boundary that makes its width change) + d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null; + // Set to true when a non-horizontal-scrolling line widget is + // added. As an optimization, line widget aligning is skipped when + // this is false. + d.alignWidgets = false; + + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; + + // Tracks the maximum line length so that the horizontal scrollbar + // can be kept static when scrolling. + d.maxLine = null; + d.maxLineLength = 0; + d.maxLineChanged = false; + + // Used for measuring wheel scrolling granularity + d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null; + + // True when shift is held down. + d.shift = false; + + // Used to track whether anything happened since the context menu + // was opened. + d.selForContextMenu = null; + + d.activeTouch = null; + + d.gutterSpecs = getGutters(options.gutters, options.lineNumbers); + renderGutters(d); + + input.init(d); + } + + // Since the delta values reported on mouse wheel events are + // unstandardized between browsers and even browser versions, and + // generally horribly unpredictable, this code starts by measuring + // the scroll effect that the first few mouse wheel events have, + // and, from that, detects the way it can convert deltas to pixel + // offsets afterwards. + // + // The reason we want to know the amount a wheel event will scroll + // is that it gives us a chance to update the display before the + // actual scrolling happens, reducing flickering. + + var wheelSamples = 0, wheelPixelsPerUnit = null; + // Fill in a browser-detected starting value on browsers where we + // know one. These don't have to be accurate -- the result of them + // being wrong would just be a slight flicker on the first wheel + // scroll (if it is large enough). + if (ie) { wheelPixelsPerUnit = -.53; } + else if (gecko) { wheelPixelsPerUnit = 15; } + else if (chrome) { wheelPixelsPerUnit = -.7; } + else if (safari) { wheelPixelsPerUnit = -1/3; } + + function wheelEventDelta(e) { + var dx = e.wheelDeltaX, dy = e.wheelDeltaY; + if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; } + if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; } + else if (dy == null) { dy = e.wheelDelta; } + return {x: dx, y: dy} + } + function wheelEventPixels(e) { + var delta = wheelEventDelta(e); + delta.x *= wheelPixelsPerUnit; + delta.y *= wheelPixelsPerUnit; + return delta + } + + function onScrollWheel(cm, e) { + var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y; + + var display = cm.display, scroll = display.scroller; + // Quit if there's nothing to scroll here + var canScrollX = scroll.scrollWidth > scroll.clientWidth; + var canScrollY = scroll.scrollHeight > scroll.clientHeight; + if (!(dx && canScrollX || dy && canScrollY)) { return } + + // Webkit browsers on OS X abort momentum scrolls when the target + // of the scroll event is removed from the scrollable element. + // This hack (see related code in patchDisplay) makes sure the + // element is kept around. + if (dy && mac && webkit) { + outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) { + for (var i = 0; i < view.length; i++) { + if (view[i].node == cur) { + cm.display.currentWheelTarget = cur; + break outer + } + } + } + } + + // On some browsers, horizontal scrolling will cause redraws to + // happen before the gutter has been realigned, causing it to + // wriggle around in a most unseemly way. When we have an + // estimated pixels/delta value, we just handle horizontal + // scrolling entirely here. It'll be slightly off from native, but + // better than glitching out. + if (dx && !gecko && !presto && wheelPixelsPerUnit != null) { + if (dy && canScrollY) + { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); } + setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit)); + // Only prevent default scrolling if vertical scrolling is + // actually possible. Otherwise, it causes vertical scroll + // jitter on OSX trackpads when deltaX is small and deltaY + // is large (issue #3579) + if (!dy || (dy && canScrollY)) + { e_preventDefault(e); } + display.wheelStartX = null; // Abort measurement, if in progress + return + } + + // 'Project' the visible viewport to cover the area that is being + // scrolled into view (if we know enough to estimate it). + if (dy && wheelPixelsPerUnit != null) { + var pixels = dy * wheelPixelsPerUnit; + var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight; + if (pixels < 0) { top = Math.max(0, top + pixels - 50); } + else { bot = Math.min(cm.doc.height, bot + pixels + 50); } + updateDisplaySimple(cm, {top: top, bottom: bot}); + } + + if (wheelSamples < 20) { + if (display.wheelStartX == null) { + display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop; + display.wheelDX = dx; display.wheelDY = dy; + setTimeout(function () { + if (display.wheelStartX == null) { return } + var movedX = scroll.scrollLeft - display.wheelStartX; + var movedY = scroll.scrollTop - display.wheelStartY; + var sample = (movedY && display.wheelDY && movedY / display.wheelDY) || + (movedX && display.wheelDX && movedX / display.wheelDX); + display.wheelStartX = display.wheelStartY = null; + if (!sample) { return } + wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1); + ++wheelSamples; + }, 200); + } else { + display.wheelDX += dx; display.wheelDY += dy; + } + } + } + + // Selection objects are immutable. A new one is created every time + // the selection changes. A selection is one or more non-overlapping + // (and non-touching) ranges, sorted, and an integer that indicates + // which one is the primary selection (the one that's scrolled into + // view, that getCursor returns, etc). + var Selection = function(ranges, primIndex) { + this.ranges = ranges; + this.primIndex = primIndex; + }; + + Selection.prototype.primary = function () { return this.ranges[this.primIndex] }; + + Selection.prototype.equals = function (other) { + if (other == this) { return true } + if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false } + for (var i = 0; i < this.ranges.length; i++) { + var here = this.ranges[i], there = other.ranges[i]; + if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false } + } + return true + }; + + Selection.prototype.deepCopy = function () { + var out = []; + for (var i = 0; i < this.ranges.length; i++) + { out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)); } + return new Selection(out, this.primIndex) + }; + + Selection.prototype.somethingSelected = function () { + for (var i = 0; i < this.ranges.length; i++) + { if (!this.ranges[i].empty()) { return true } } + return false + }; + + Selection.prototype.contains = function (pos, end) { + if (!end) { end = pos; } + for (var i = 0; i < this.ranges.length; i++) { + var range = this.ranges[i]; + if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) + { return i } + } + return -1 + }; + + var Range = function(anchor, head) { + this.anchor = anchor; this.head = head; + }; + + Range.prototype.from = function () { return minPos(this.anchor, this.head) }; + Range.prototype.to = function () { return maxPos(this.anchor, this.head) }; + Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch }; + + // Take an unsorted, potentially overlapping set of ranges, and + // build a selection out of it. 'Consumes' ranges array (modifying + // it). + function normalizeSelection(cm, ranges, primIndex) { + var mayTouch = cm && cm.options.selectionsMayTouch; + var prim = ranges[primIndex]; + ranges.sort(function (a, b) { return cmp(a.from(), b.from()); }); + primIndex = indexOf(ranges, prim); + for (var i = 1; i < ranges.length; i++) { + var cur = ranges[i], prev = ranges[i - 1]; + var diff = cmp(prev.to(), cur.from()); + if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) { + var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to()); + var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head; + if (i <= primIndex) { --primIndex; } + ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to)); + } + } + return new Selection(ranges, primIndex) + } + + function simpleSelection(anchor, head) { + return new Selection([new Range(anchor, head || anchor)], 0) + } + + // Compute the position of the end of a change (its 'to' property + // refers to the pre-change end). + function changeEnd(change) { + if (!change.text) { return change.to } + return Pos(change.from.line + change.text.length - 1, + lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)) + } + + // Adjust a position to refer to the post-change position of the + // same text, or the end of the change if the change covers it. + function adjustForChange(pos, change) { + if (cmp(pos, change.from) < 0) { return pos } + if (cmp(pos, change.to) <= 0) { return changeEnd(change) } + + var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch; + if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; } + return Pos(line, ch) + } + + function computeSelAfterChange(doc, change) { + var out = []; + for (var i = 0; i < doc.sel.ranges.length; i++) { + var range = doc.sel.ranges[i]; + out.push(new Range(adjustForChange(range.anchor, change), + adjustForChange(range.head, change))); + } + return normalizeSelection(doc.cm, out, doc.sel.primIndex) + } + + function offsetPos(pos, old, nw) { + if (pos.line == old.line) + { return Pos(nw.line, pos.ch - old.ch + nw.ch) } + else + { return Pos(nw.line + (pos.line - old.line), pos.ch) } + } + + // Used by replaceSelections to allow moving the selection to the + // start or around the replaced test. Hint may be "start" or "around". + function computeReplacedSel(doc, changes, hint) { + var out = []; + var oldPrev = Pos(doc.first, 0), newPrev = oldPrev; + for (var i = 0; i < changes.length; i++) { + var change = changes[i]; + var from = offsetPos(change.from, oldPrev, newPrev); + var to = offsetPos(changeEnd(change), oldPrev, newPrev); + oldPrev = change.to; + newPrev = to; + if (hint == "around") { + var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0; + out[i] = new Range(inv ? to : from, inv ? from : to); + } else { + out[i] = new Range(from, from); + } + } + return new Selection(out, doc.sel.primIndex) + } + + // Used to get the editor into a consistent state again when options change. + + function loadMode(cm) { + cm.doc.mode = getMode(cm.options, cm.doc.modeOption); + resetModeState(cm); + } + + function resetModeState(cm) { + cm.doc.iter(function (line) { + if (line.stateAfter) { line.stateAfter = null; } + if (line.styles) { line.styles = null; } + }); + cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first; + startWorker(cm, 100); + cm.state.modeGen++; + if (cm.curOp) { regChange(cm); } + } + + // DOCUMENT DATA STRUCTURE + + // By default, updates that start and end at the beginning of a line + // are treated specially, in order to make the association of line + // widgets and marker elements with the text behave more intuitive. + function isWholeLineUpdate(doc, change) { + return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" && + (!doc.cm || doc.cm.options.wholeLineUpdateBefore) + } + + // Perform a change on the document data structure. + function updateDoc(doc, change, markedSpans, estimateHeight) { + function spansFor(n) {return markedSpans ? markedSpans[n] : null} + function update(line, text, spans) { + updateLine(line, text, spans, estimateHeight); + signalLater(line, "change", line, change); + } + function linesFor(start, end) { + var result = []; + for (var i = start; i < end; ++i) + { result.push(new Line(text[i], spansFor(i), estimateHeight)); } + return result + } + + var from = change.from, to = change.to, text = change.text; + var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line); + var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line; + + // Adjust the line structure + if (change.full) { + doc.insert(0, linesFor(0, text.length)); + doc.remove(text.length, doc.size - text.length); + } else if (isWholeLineUpdate(doc, change)) { + // This is a whole-line replace. Treated specially to make + // sure line objects move the way they are supposed to. + var added = linesFor(0, text.length - 1); + update(lastLine, lastLine.text, lastSpans); + if (nlines) { doc.remove(from.line, nlines); } + if (added.length) { doc.insert(from.line, added); } + } else if (firstLine == lastLine) { + if (text.length == 1) { + update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans); + } else { + var added$1 = linesFor(1, text.length - 1); + added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight)); + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); + doc.insert(from.line + 1, added$1); + } + } else if (text.length == 1) { + update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0)); + doc.remove(from.line + 1, nlines); + } else { + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); + update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans); + var added$2 = linesFor(1, text.length - 1); + if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); } + doc.insert(from.line + 1, added$2); + } + + signalLater(doc, "change", doc, change); + } + + // Call f for all linked documents. + function linkedDocs(doc, f, sharedHistOnly) { + function propagate(doc, skip, sharedHist) { + if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) { + var rel = doc.linked[i]; + if (rel.doc == skip) { continue } + var shared = sharedHist && rel.sharedHist; + if (sharedHistOnly && !shared) { continue } + f(rel.doc, shared); + propagate(rel.doc, doc, shared); + } } + } + propagate(doc, null, true); + } + + // Attach a document to an editor. + function attachDoc(cm, doc) { + if (doc.cm) { throw new Error("This document is already in use.") } + cm.doc = doc; + doc.cm = cm; + estimateLineHeights(cm); + loadMode(cm); + setDirectionClass(cm); + cm.options.direction = doc.direction; + if (!cm.options.lineWrapping) { findMaxLine(cm); } + cm.options.mode = doc.modeOption; + regChange(cm); + } + + function setDirectionClass(cm) { + (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl"); + } + + function directionChanged(cm) { + runInOp(cm, function () { + setDirectionClass(cm); + regChange(cm); + }); + } + + function History(prev) { + // Arrays of change events and selections. Doing something adds an + // event to done and clears undo. Undoing moves events from done + // to undone, redoing moves them in the other direction. + this.done = []; this.undone = []; + this.undoDepth = prev ? prev.undoDepth : Infinity; + // Used to track when changes can be merged into a single undo + // event + this.lastModTime = this.lastSelTime = 0; + this.lastOp = this.lastSelOp = null; + this.lastOrigin = this.lastSelOrigin = null; + // Used by the isClean() method + this.generation = this.maxGeneration = prev ? prev.maxGeneration : 1; + } + + // Create a history change event from an updateDoc-style change + // object. + function historyChangeFromChange(doc, change) { + var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}; + attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); + linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true); + return histChange + } + + // Pop all selection events off the end of a history array. Stop at + // a change event. + function clearSelectionEvents(array) { + while (array.length) { + var last = lst(array); + if (last.ranges) { array.pop(); } + else { break } + } + } + + // Find the top change event in the history. Pop off selection + // events that are in the way. + function lastChangeEvent(hist, force) { + if (force) { + clearSelectionEvents(hist.done); + return lst(hist.done) + } else if (hist.done.length && !lst(hist.done).ranges) { + return lst(hist.done) + } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) { + hist.done.pop(); + return lst(hist.done) + } + } + + // Register a change in the history. Merges changes that are within + // a single operation, or are close together with an origin that + // allows merging (starting with "+") into a single event. + function addChangeToHistory(doc, change, selAfter, opId) { + var hist = doc.history; + hist.undone.length = 0; + var time = +new Date, cur; + var last; + + if ((hist.lastOp == opId || + hist.lastOrigin == change.origin && change.origin && + ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) || + change.origin.charAt(0) == "*")) && + (cur = lastChangeEvent(hist, hist.lastOp == opId))) { + // Merge this change into the last event + last = lst(cur.changes); + if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) { + // Optimized case for simple insertion -- don't want to add + // new changesets for every character typed + last.to = changeEnd(change); + } else { + // Add new sub-event + cur.changes.push(historyChangeFromChange(doc, change)); + } + } else { + // Can not be merged, start a new event. + var before = lst(hist.done); + if (!before || !before.ranges) + { pushSelectionToHistory(doc.sel, hist.done); } + cur = {changes: [historyChangeFromChange(doc, change)], + generation: hist.generation}; + hist.done.push(cur); + while (hist.done.length > hist.undoDepth) { + hist.done.shift(); + if (!hist.done[0].ranges) { hist.done.shift(); } + } + } + hist.done.push(selAfter); + hist.generation = ++hist.maxGeneration; + hist.lastModTime = hist.lastSelTime = time; + hist.lastOp = hist.lastSelOp = opId; + hist.lastOrigin = hist.lastSelOrigin = change.origin; + + if (!last) { signal(doc, "historyAdded"); } + } + + function selectionEventCanBeMerged(doc, origin, prev, sel) { + var ch = origin.charAt(0); + return ch == "*" || + ch == "+" && + prev.ranges.length == sel.ranges.length && + prev.somethingSelected() == sel.somethingSelected() && + new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500) + } + + // Called whenever the selection changes, sets the new selection as + // the pending selection in the history, and pushes the old pending + // selection into the 'done' array when it was significantly + // different (in number of selected ranges, emptiness, or time). + function addSelectionToHistory(doc, sel, opId, options) { + var hist = doc.history, origin = options && options.origin; + + // A new event is started when the previous origin does not match + // the current, or the origins don't allow matching. Origins + // starting with * are always merged, those starting with + are + // merged when similar and close together in time. + if (opId == hist.lastSelOp || + (origin && hist.lastSelOrigin == origin && + (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin || + selectionEventCanBeMerged(doc, origin, lst(hist.done), sel)))) + { hist.done[hist.done.length - 1] = sel; } + else + { pushSelectionToHistory(sel, hist.done); } + + hist.lastSelTime = +new Date; + hist.lastSelOrigin = origin; + hist.lastSelOp = opId; + if (options && options.clearRedo !== false) + { clearSelectionEvents(hist.undone); } + } + + function pushSelectionToHistory(sel, dest) { + var top = lst(dest); + if (!(top && top.ranges && top.equals(sel))) + { dest.push(sel); } + } + + // Used to store marked span information in the history. + function attachLocalSpans(doc, change, from, to) { + var existing = change["spans_" + doc.id], n = 0; + doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) { + if (line.markedSpans) + { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; } + ++n; + }); + } + + // When un/re-doing restores text containing marked spans, those + // that have been explicitly cleared should not be restored. + function removeClearedSpans(spans) { + if (!spans) { return null } + var out; + for (var i = 0; i < spans.length; ++i) { + if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } } + else if (out) { out.push(spans[i]); } + } + return !out ? spans : out.length ? out : null + } + + // Retrieve and filter the old marked spans stored in a change event. + function getOldSpans(doc, change) { + var found = change["spans_" + doc.id]; + if (!found) { return null } + var nw = []; + for (var i = 0; i < change.text.length; ++i) + { nw.push(removeClearedSpans(found[i])); } + return nw + } + + // Used for un/re-doing changes from the history. Combines the + // result of computing the existing spans with the set of spans that + // existed in the history (so that deleting around a span and then + // undoing brings back the span). + function mergeOldSpans(doc, change) { + var old = getOldSpans(doc, change); + var stretched = stretchSpansOverChange(doc, change); + if (!old) { return stretched } + if (!stretched) { return old } + + for (var i = 0; i < old.length; ++i) { + var oldCur = old[i], stretchCur = stretched[i]; + if (oldCur && stretchCur) { + spans: for (var j = 0; j < stretchCur.length; ++j) { + var span = stretchCur[j]; + for (var k = 0; k < oldCur.length; ++k) + { if (oldCur[k].marker == span.marker) { continue spans } } + oldCur.push(span); + } + } else if (stretchCur) { + old[i] = stretchCur; + } + } + return old + } + + // Used both to provide a JSON-safe object in .getHistory, and, when + // detaching a document, to split the history in two + function copyHistoryArray(events, newGroup, instantiateSel) { + var copy = []; + for (var i = 0; i < events.length; ++i) { + var event = events[i]; + if (event.ranges) { + copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event); + continue + } + var changes = event.changes, newChanges = []; + copy.push({changes: newChanges}); + for (var j = 0; j < changes.length; ++j) { + var change = changes[j], m = (void 0); + newChanges.push({from: change.from, to: change.to, text: change.text}); + if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) { + if (indexOf(newGroup, Number(m[1])) > -1) { + lst(newChanges)[prop] = change[prop]; + delete change[prop]; + } + } } } + } + } + return copy + } + + // The 'scroll' parameter given to many of these indicated whether + // the new cursor position should be scrolled into view after + // modifying the selection. + + // If shift is held or the extend flag is set, extends a range to + // include a given position (and optionally a second position). + // Otherwise, simply returns the range between the given positions. + // Used for cursor motion and such. + function extendRange(range, head, other, extend) { + if (extend) { + var anchor = range.anchor; + if (other) { + var posBefore = cmp(head, anchor) < 0; + if (posBefore != (cmp(other, anchor) < 0)) { + anchor = head; + head = other; + } else if (posBefore != (cmp(head, other) < 0)) { + head = other; + } + } + return new Range(anchor, head) + } else { + return new Range(other || head, head) + } + } + + // Extend the primary selection range, discard the rest. + function extendSelection(doc, head, other, options, extend) { + if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); } + setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options); + } + + // Extend all selections (pos is an array of selections with length + // equal the number of selections) + function extendSelections(doc, heads, options) { + var out = []; + var extend = doc.cm && (doc.cm.display.shift || doc.extend); + for (var i = 0; i < doc.sel.ranges.length; i++) + { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); } + var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex); + setSelection(doc, newSel, options); + } + + // Updates a single range in the selection. + function replaceOneSelection(doc, i, range, options) { + var ranges = doc.sel.ranges.slice(0); + ranges[i] = range; + setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options); + } + + // Reset the selection to a single range. + function setSimpleSelection(doc, anchor, head, options) { + setSelection(doc, simpleSelection(anchor, head), options); + } + + // Give beforeSelectionChange handlers a change to influence a + // selection update. + function filterSelectionChange(doc, sel, options) { + var obj = { + ranges: sel.ranges, + update: function(ranges) { + this.ranges = []; + for (var i = 0; i < ranges.length; i++) + { this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), + clipPos(doc, ranges[i].head)); } + }, + origin: options && options.origin + }; + signal(doc, "beforeSelectionChange", doc, obj); + if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); } + if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) } + else { return sel } + } + + function setSelectionReplaceHistory(doc, sel, options) { + var done = doc.history.done, last = lst(done); + if (last && last.ranges) { + done[done.length - 1] = sel; + setSelectionNoUndo(doc, sel, options); + } else { + setSelection(doc, sel, options); + } + } + + // Set a new selection. + function setSelection(doc, sel, options) { + setSelectionNoUndo(doc, sel, options); + addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options); + } + + function setSelectionNoUndo(doc, sel, options) { + if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) + { sel = filterSelectionChange(doc, sel, options); } + + var bias = options && options.bias || + (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1); + setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true)); + + if (!(options && options.scroll === false) && doc.cm && doc.cm.getOption("readOnly") != "nocursor") + { ensureCursorVisible(doc.cm); } + } + + function setSelectionInner(doc, sel) { + if (sel.equals(doc.sel)) { return } + + doc.sel = sel; + + if (doc.cm) { + doc.cm.curOp.updateInput = 1; + doc.cm.curOp.selectionChanged = true; + signalCursorActivity(doc.cm); + } + signalLater(doc, "cursorActivity", doc); + } + + // Verify that the selection does not partially select any atomic + // marked ranges. + function reCheckSelection(doc) { + setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false)); + } + + // Return a selection that does not partially select any atomic + // ranges. + function skipAtomicInSelection(doc, sel, bias, mayClear) { + var out; + for (var i = 0; i < sel.ranges.length; i++) { + var range = sel.ranges[i]; + var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]; + var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear); + var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear); + if (out || newAnchor != range.anchor || newHead != range.head) { + if (!out) { out = sel.ranges.slice(0, i); } + out[i] = new Range(newAnchor, newHead); + } + } + return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel + } + + function skipAtomicInner(doc, pos, oldPos, dir, mayClear) { + var line = getLine(doc, pos.line); + if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { + var sp = line.markedSpans[i], m = sp.marker; + + // Determine if we should prevent the cursor being placed to the left/right of an atomic marker + // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it + // is with selectLeft/Right + var preventCursorLeft = ("selectLeft" in m) ? !m.selectLeft : m.inclusiveLeft; + var preventCursorRight = ("selectRight" in m) ? !m.selectRight : m.inclusiveRight; + + if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) && + (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) { + if (mayClear) { + signal(m, "beforeCursorEnter"); + if (m.explicitlyCleared) { + if (!line.markedSpans) { break } + else {--i; continue} + } + } + if (!m.atomic) { continue } + + if (oldPos) { + var near = m.find(dir < 0 ? 1 : -1), diff = (void 0); + if (dir < 0 ? preventCursorRight : preventCursorLeft) + { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); } + if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0)) + { return skipAtomicInner(doc, near, pos, dir, mayClear) } + } + + var far = m.find(dir < 0 ? -1 : 1); + if (dir < 0 ? preventCursorLeft : preventCursorRight) + { far = movePos(doc, far, dir, far.line == pos.line ? line : null); } + return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null + } + } } + return pos + } + + // Ensure a given position is not inside an atomic range. + function skipAtomic(doc, pos, oldPos, bias, mayClear) { + var dir = bias || 1; + var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) || + (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) || + skipAtomicInner(doc, pos, oldPos, -dir, mayClear) || + (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true)); + if (!found) { + doc.cantEdit = true; + return Pos(doc.first, 0) + } + return found + } + + function movePos(doc, pos, dir, line) { + if (dir < 0 && pos.ch == 0) { + if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) } + else { return null } + } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) { + if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) } + else { return null } + } else { + return new Pos(pos.line, pos.ch + dir) + } + } + + function selectAll(cm) { + cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll); + } + + // UPDATING + + // Allow "beforeChange" event handlers to influence a change + function filterChange(doc, change, update) { + var obj = { + canceled: false, + from: change.from, + to: change.to, + text: change.text, + origin: change.origin, + cancel: function () { return obj.canceled = true; } + }; + if (update) { obj.update = function (from, to, text, origin) { + if (from) { obj.from = clipPos(doc, from); } + if (to) { obj.to = clipPos(doc, to); } + if (text) { obj.text = text; } + if (origin !== undefined) { obj.origin = origin; } + }; } + signal(doc, "beforeChange", doc, obj); + if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); } + + if (obj.canceled) { + if (doc.cm) { doc.cm.curOp.updateInput = 2; } + return null + } + return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin} + } + + // Apply a change to a document, and add it to the document's + // history, and propagating it to all linked documents. + function makeChange(doc, change, ignoreReadOnly) { + if (doc.cm) { + if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) } + if (doc.cm.state.suppressEdits) { return } + } + + if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) { + change = filterChange(doc, change, true); + if (!change) { return } + } + + // Possibly split or suppress the update based on the presence + // of read-only spans in its range. + var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to); + if (split) { + for (var i = split.length - 1; i >= 0; --i) + { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); } + } else { + makeChangeInner(doc, change); + } + } + + function makeChangeInner(doc, change) { + if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return } + var selAfter = computeSelAfterChange(doc, change); + addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN); + + makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)); + var rebased = []; + + linkedDocs(doc, function (doc, sharedHist) { + if (!sharedHist && indexOf(rebased, doc.history) == -1) { + rebaseHist(doc.history, change); + rebased.push(doc.history); + } + makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change)); + }); + } + + // Revert a change stored in a document's history. + function makeChangeFromHistory(doc, type, allowSelectionOnly) { + var suppress = doc.cm && doc.cm.state.suppressEdits; + if (suppress && !allowSelectionOnly) { return } + + var hist = doc.history, event, selAfter = doc.sel; + var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done; + + // Verify that there is a useable event (so that ctrl-z won't + // needlessly clear selection events) + var i = 0; + for (; i < source.length; i++) { + event = source[i]; + if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges) + { break } + } + if (i == source.length) { return } + hist.lastOrigin = hist.lastSelOrigin = null; + + for (;;) { + event = source.pop(); + if (event.ranges) { + pushSelectionToHistory(event, dest); + if (allowSelectionOnly && !event.equals(doc.sel)) { + setSelection(doc, event, {clearRedo: false}); + return + } + selAfter = event; + } else if (suppress) { + source.push(event); + return + } else { break } + } + + // Build up a reverse change object to add to the opposite history + // stack (redo when undoing, and vice versa). + var antiChanges = []; + pushSelectionToHistory(selAfter, dest); + dest.push({changes: antiChanges, generation: hist.generation}); + hist.generation = event.generation || ++hist.maxGeneration; + + var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange"); + + var loop = function ( i ) { + var change = event.changes[i]; + change.origin = type; + if (filter && !filterChange(doc, change, false)) { + source.length = 0; + return {} + } + + antiChanges.push(historyChangeFromChange(doc, change)); + + var after = i ? computeSelAfterChange(doc, change) : lst(source); + makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); + if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); } + var rebased = []; + + // Propagate to the linked documents + linkedDocs(doc, function (doc, sharedHist) { + if (!sharedHist && indexOf(rebased, doc.history) == -1) { + rebaseHist(doc.history, change); + rebased.push(doc.history); + } + makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change)); + }); + }; + + for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) { + var returned = loop( i$1 ); + + if ( returned ) return returned.v; + } + } + + // Sub-views need their line numbers shifted when text is added + // above or below them in the parent document. + function shiftDoc(doc, distance) { + if (distance == 0) { return } + doc.first += distance; + doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range( + Pos(range.anchor.line + distance, range.anchor.ch), + Pos(range.head.line + distance, range.head.ch) + ); }), doc.sel.primIndex); + if (doc.cm) { + regChange(doc.cm, doc.first, doc.first - distance, distance); + for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++) + { regLineChange(doc.cm, l, "gutter"); } + } + } + + // More lower-level change function, handling only a single document + // (not linked ones). + function makeChangeSingleDoc(doc, change, selAfter, spans) { + if (doc.cm && !doc.cm.curOp) + { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) } + + if (change.to.line < doc.first) { + shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line)); + return + } + if (change.from.line > doc.lastLine()) { return } + + // Clip the change to the size of this doc + if (change.from.line < doc.first) { + var shift = change.text.length - 1 - (doc.first - change.from.line); + shiftDoc(doc, shift); + change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch), + text: [lst(change.text)], origin: change.origin}; + } + var last = doc.lastLine(); + if (change.to.line > last) { + change = {from: change.from, to: Pos(last, getLine(doc, last).text.length), + text: [change.text[0]], origin: change.origin}; + } + + change.removed = getBetween(doc, change.from, change.to); + + if (!selAfter) { selAfter = computeSelAfterChange(doc, change); } + if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); } + else { updateDoc(doc, change, spans); } + setSelectionNoUndo(doc, selAfter, sel_dontScroll); + + if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0))) + { doc.cantEdit = false; } + } + + // Handle the interaction of a change to a document with the editor + // that this document is part of. + function makeChangeSingleDocInEditor(cm, change, spans) { + var doc = cm.doc, display = cm.display, from = change.from, to = change.to; + + var recomputeMaxLength = false, checkWidthStart = from.line; + if (!cm.options.lineWrapping) { + checkWidthStart = lineNo(visualLine(getLine(doc, from.line))); + doc.iter(checkWidthStart, to.line + 1, function (line) { + if (line == display.maxLine) { + recomputeMaxLength = true; + return true + } + }); + } + + if (doc.sel.contains(change.from, change.to) > -1) + { signalCursorActivity(cm); } + + updateDoc(doc, change, spans, estimateHeight(cm)); + + if (!cm.options.lineWrapping) { + doc.iter(checkWidthStart, from.line + change.text.length, function (line) { + var len = lineLength(line); + if (len > display.maxLineLength) { + display.maxLine = line; + display.maxLineLength = len; + display.maxLineChanged = true; + recomputeMaxLength = false; + } + }); + if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; } + } + + retreatFrontier(doc, from.line); + startWorker(cm, 400); + + var lendiff = change.text.length - (to.line - from.line) - 1; + // Remember that these lines changed, for updating the display + if (change.full) + { regChange(cm); } + else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change)) + { regLineChange(cm, from.line, "text"); } + else + { regChange(cm, from.line, to.line + 1, lendiff); } + + var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change"); + if (changeHandler || changesHandler) { + var obj = { + from: from, to: to, + text: change.text, + removed: change.removed, + origin: change.origin + }; + if (changeHandler) { signalLater(cm, "change", cm, obj); } + if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); } + } + cm.display.selForContextMenu = null; + } + + function replaceRange(doc, code, from, to, origin) { + var assign; + + if (!to) { to = from; } + if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); } + if (typeof code == "string") { code = doc.splitLines(code); } + makeChange(doc, {from: from, to: to, text: code, origin: origin}); + } + + // Rebasing/resetting history to deal with externally-sourced changes + + function rebaseHistSelSingle(pos, from, to, diff) { + if (to < pos.line) { + pos.line += diff; + } else if (from < pos.line) { + pos.line = from; + pos.ch = 0; + } + } + + // Tries to rebase an array of history events given a change in the + // document. If the change touches the same lines as the event, the + // event, and everything 'behind' it, is discarded. If the change is + // before the event, the event's positions are updated. Uses a + // copy-on-write scheme for the positions, to avoid having to + // reallocate them all on every rebase, but also avoid problems with + // shared position objects being unsafely updated. + function rebaseHistArray(array, from, to, diff) { + for (var i = 0; i < array.length; ++i) { + var sub = array[i], ok = true; + if (sub.ranges) { + if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; } + for (var j = 0; j < sub.ranges.length; j++) { + rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff); + rebaseHistSelSingle(sub.ranges[j].head, from, to, diff); + } + continue + } + for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) { + var cur = sub.changes[j$1]; + if (to < cur.from.line) { + cur.from = Pos(cur.from.line + diff, cur.from.ch); + cur.to = Pos(cur.to.line + diff, cur.to.ch); + } else if (from <= cur.to.line) { + ok = false; + break + } + } + if (!ok) { + array.splice(0, i + 1); + i = 0; + } + } + } + + function rebaseHist(hist, change) { + var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1; + rebaseHistArray(hist.done, from, to, diff); + rebaseHistArray(hist.undone, from, to, diff); + } + + // Utility for applying a change to a line by handle or number, + // returning the number and optionally registering the line as + // changed. + function changeLine(doc, handle, changeType, op) { + var no = handle, line = handle; + if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); } + else { no = lineNo(handle); } + if (no == null) { return null } + if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); } + return line + } + + // The document is represented as a BTree consisting of leaves, with + // chunk of lines in them, and branches, with up to ten leaves or + // other branch nodes below them. The top node is always a branch + // node, and is the document object itself (meaning it has + // additional methods and properties). + // + // All nodes have parent links. The tree is used both to go from + // line numbers to line objects, and to go from objects to numbers. + // It also indexes by height, and is used to convert between height + // and line object, and to find the total height of the document. + // + // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html + + function LeafChunk(lines) { + this.lines = lines; + this.parent = null; + var height = 0; + for (var i = 0; i < lines.length; ++i) { + lines[i].parent = this; + height += lines[i].height; + } + this.height = height; + } + + LeafChunk.prototype = { + chunkSize: function() { return this.lines.length }, + + // Remove the n lines at offset 'at'. + removeInner: function(at, n) { + for (var i = at, e = at + n; i < e; ++i) { + var line = this.lines[i]; + this.height -= line.height; + cleanUpLine(line); + signalLater(line, "delete"); + } + this.lines.splice(at, n); + }, + + // Helper used to collapse a small branch into a single leaf. + collapse: function(lines) { + lines.push.apply(lines, this.lines); + }, + + // Insert the given array of lines at offset 'at', count them as + // having the given height. + insertInner: function(at, lines, height) { + this.height += height; + this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at)); + for (var i = 0; i < lines.length; ++i) { lines[i].parent = this; } + }, + + // Used to iterate over a part of the tree. + iterN: function(at, n, op) { + for (var e = at + n; at < e; ++at) + { if (op(this.lines[at])) { return true } } + } + }; + + function BranchChunk(children) { + this.children = children; + var size = 0, height = 0; + for (var i = 0; i < children.length; ++i) { + var ch = children[i]; + size += ch.chunkSize(); height += ch.height; + ch.parent = this; + } + this.size = size; + this.height = height; + this.parent = null; + } + + BranchChunk.prototype = { + chunkSize: function() { return this.size }, + + removeInner: function(at, n) { + this.size -= n; + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at < sz) { + var rm = Math.min(n, sz - at), oldHeight = child.height; + child.removeInner(at, rm); + this.height -= oldHeight - child.height; + if (sz == rm) { this.children.splice(i--, 1); child.parent = null; } + if ((n -= rm) == 0) { break } + at = 0; + } else { at -= sz; } + } + // If the result is smaller than 25 lines, ensure that it is a + // single leaf node. + if (this.size - n < 25 && + (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) { + var lines = []; + this.collapse(lines); + this.children = [new LeafChunk(lines)]; + this.children[0].parent = this; + } + }, + + collapse: function(lines) { + for (var i = 0; i < this.children.length; ++i) { this.children[i].collapse(lines); } + }, + + insertInner: function(at, lines, height) { + this.size += lines.length; + this.height += height; + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at <= sz) { + child.insertInner(at, lines, height); + if (child.lines && child.lines.length > 50) { + // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced. + // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest. + var remaining = child.lines.length % 25 + 25; + for (var pos = remaining; pos < child.lines.length;) { + var leaf = new LeafChunk(child.lines.slice(pos, pos += 25)); + child.height -= leaf.height; + this.children.splice(++i, 0, leaf); + leaf.parent = this; + } + child.lines = child.lines.slice(0, remaining); + this.maybeSpill(); + } + break + } + at -= sz; + } + }, + + // When a node has grown, check whether it should be split. + maybeSpill: function() { + if (this.children.length <= 10) { return } + var me = this; + do { + var spilled = me.children.splice(me.children.length - 5, 5); + var sibling = new BranchChunk(spilled); + if (!me.parent) { // Become the parent node + var copy = new BranchChunk(me.children); + copy.parent = me; + me.children = [copy, sibling]; + me = copy; + } else { + me.size -= sibling.size; + me.height -= sibling.height; + var myIndex = indexOf(me.parent.children, me); + me.parent.children.splice(myIndex + 1, 0, sibling); + } + sibling.parent = me.parent; + } while (me.children.length > 10) + me.parent.maybeSpill(); + }, + + iterN: function(at, n, op) { + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at < sz) { + var used = Math.min(n, sz - at); + if (child.iterN(at, used, op)) { return true } + if ((n -= used) == 0) { break } + at = 0; + } else { at -= sz; } + } + } + }; + + // Line widgets are block elements displayed above or below a line. + + var LineWidget = function(doc, node, options) { + if (options) { for (var opt in options) { if (options.hasOwnProperty(opt)) + { this[opt] = options[opt]; } } } + this.doc = doc; + this.node = node; + }; + + LineWidget.prototype.clear = function () { + var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line); + if (no == null || !ws) { return } + for (var i = 0; i < ws.length; ++i) { if (ws[i] == this) { ws.splice(i--, 1); } } + if (!ws.length) { line.widgets = null; } + var height = widgetHeight(this); + updateLineHeight(line, Math.max(0, line.height - height)); + if (cm) { + runInOp(cm, function () { + adjustScrollWhenAboveVisible(cm, line, -height); + regLineChange(cm, no, "widget"); + }); + signalLater(cm, "lineWidgetCleared", cm, this, no); + } + }; + + LineWidget.prototype.changed = function () { + var this$1 = this; + + var oldH = this.height, cm = this.doc.cm, line = this.line; + this.height = null; + var diff = widgetHeight(this) - oldH; + if (!diff) { return } + if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); } + if (cm) { + runInOp(cm, function () { + cm.curOp.forceUpdate = true; + adjustScrollWhenAboveVisible(cm, line, diff); + signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line)); + }); + } + }; + eventMixin(LineWidget); + + function adjustScrollWhenAboveVisible(cm, line, diff) { + if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop)) + { addToScrollTop(cm, diff); } + } + + function addLineWidget(doc, handle, node, options) { + var widget = new LineWidget(doc, node, options); + var cm = doc.cm; + if (cm && widget.noHScroll) { cm.display.alignWidgets = true; } + changeLine(doc, handle, "widget", function (line) { + var widgets = line.widgets || (line.widgets = []); + if (widget.insertAt == null) { widgets.push(widget); } + else { widgets.splice(Math.min(widgets.length, Math.max(0, widget.insertAt)), 0, widget); } + widget.line = line; + if (cm && !lineIsHidden(doc, line)) { + var aboveVisible = heightAtLine(line) < doc.scrollTop; + updateLineHeight(line, line.height + widgetHeight(widget)); + if (aboveVisible) { addToScrollTop(cm, widget.height); } + cm.curOp.forceUpdate = true; + } + return true + }); + if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); } + return widget + } + + // TEXTMARKERS + + // Created with markText and setBookmark methods. A TextMarker is a + // handle that can be used to clear or find a marked position in the + // document. Line objects hold arrays (markedSpans) containing + // {from, to, marker} object pointing to such marker objects, and + // indicating that such a marker is present on that line. Multiple + // lines may point to the same marker when it spans across lines. + // The spans will have null for their from/to properties when the + // marker continues beyond the start/end of the line. Markers have + // links back to the lines they currently touch. + + // Collapsed markers have unique ids, in order to be able to order + // them, which is needed for uniquely determining an outer marker + // when they overlap (they may nest, but not partially overlap). + var nextMarkerId = 0; + + var TextMarker = function(doc, type) { + this.lines = []; + this.type = type; + this.doc = doc; + this.id = ++nextMarkerId; + }; + + // Clear the marker. + TextMarker.prototype.clear = function () { + if (this.explicitlyCleared) { return } + var cm = this.doc.cm, withOp = cm && !cm.curOp; + if (withOp) { startOperation(cm); } + if (hasHandler(this, "clear")) { + var found = this.find(); + if (found) { signalLater(this, "clear", found.from, found.to); } + } + var min = null, max = null; + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); + if (cm && !this.collapsed) { regLineChange(cm, lineNo(line), "text"); } + else if (cm) { + if (span.to != null) { max = lineNo(line); } + if (span.from != null) { min = lineNo(line); } + } + line.markedSpans = removeMarkedSpan(line.markedSpans, span); + if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm) + { updateLineHeight(line, textHeight(cm.display)); } + } + if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) { + var visual = visualLine(this.lines[i$1]), len = lineLength(visual); + if (len > cm.display.maxLineLength) { + cm.display.maxLine = visual; + cm.display.maxLineLength = len; + cm.display.maxLineChanged = true; + } + } } + + if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); } + this.lines.length = 0; + this.explicitlyCleared = true; + if (this.atomic && this.doc.cantEdit) { + this.doc.cantEdit = false; + if (cm) { reCheckSelection(cm.doc); } + } + if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); } + if (withOp) { endOperation(cm); } + if (this.parent) { this.parent.clear(); } + }; + + // Find the position of the marker in the document. Returns a {from, + // to} object by default. Side can be passed to get a specific side + // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the + // Pos objects returned contain a line object, rather than a line + // number (used to prevent looking up the same line twice). + TextMarker.prototype.find = function (side, lineObj) { + if (side == null && this.type == "bookmark") { side = 1; } + var from, to; + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); + if (span.from != null) { + from = Pos(lineObj ? line : lineNo(line), span.from); + if (side == -1) { return from } + } + if (span.to != null) { + to = Pos(lineObj ? line : lineNo(line), span.to); + if (side == 1) { return to } + } + } + return from && {from: from, to: to} + }; + + // Signals that the marker's widget changed, and surrounding layout + // should be recomputed. + TextMarker.prototype.changed = function () { + var this$1 = this; + + var pos = this.find(-1, true), widget = this, cm = this.doc.cm; + if (!pos || !cm) { return } + runInOp(cm, function () { + var line = pos.line, lineN = lineNo(pos.line); + var view = findViewForLine(cm, lineN); + if (view) { + clearLineMeasurementCacheFor(view); + cm.curOp.selectionChanged = cm.curOp.forceUpdate = true; + } + cm.curOp.updateMaxLine = true; + if (!lineIsHidden(widget.doc, line) && widget.height != null) { + var oldHeight = widget.height; + widget.height = null; + var dHeight = widgetHeight(widget) - oldHeight; + if (dHeight) + { updateLineHeight(line, line.height + dHeight); } + } + signalLater(cm, "markerChanged", cm, this$1); + }); + }; + + TextMarker.prototype.attachLine = function (line) { + if (!this.lines.length && this.doc.cm) { + var op = this.doc.cm.curOp; + if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) + { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); } + } + this.lines.push(line); + }; + + TextMarker.prototype.detachLine = function (line) { + this.lines.splice(indexOf(this.lines, line), 1); + if (!this.lines.length && this.doc.cm) { + var op = this.doc.cm.curOp + ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this); + } + }; + eventMixin(TextMarker); + + // Create a marker, wire it up to the right lines, and + function markText(doc, from, to, options, type) { + // Shared markers (across linked documents) are handled separately + // (markTextShared will call out to this again, once per + // document). + if (options && options.shared) { return markTextShared(doc, from, to, options, type) } + // Ensure we are in an operation. + if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) } + + var marker = new TextMarker(doc, type), diff = cmp(from, to); + if (options) { copyObj(options, marker, false); } + // Don't connect empty markers unless clearWhenEmpty is false + if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false) + { return marker } + if (marker.replacedWith) { + // Showing up as a widget implies collapsed (widget replaces text) + marker.collapsed = true; + marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget"); + if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); } + if (options.insertLeft) { marker.widgetNode.insertLeft = true; } + } + if (marker.collapsed) { + if (conflictingCollapsedRange(doc, from.line, from, to, marker) || + from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker)) + { throw new Error("Inserting collapsed marker partially overlapping an existing one") } + seeCollapsedSpans(); + } + + if (marker.addToHistory) + { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); } + + var curLine = from.line, cm = doc.cm, updateMaxLine; + doc.iter(curLine, to.line + 1, function (line) { + if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine) + { updateMaxLine = true; } + if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); } + addMarkedSpan(line, new MarkedSpan(marker, + curLine == from.line ? from.ch : null, + curLine == to.line ? to.ch : null), doc.cm && doc.cm.curOp); + ++curLine; + }); + // lineIsHidden depends on the presence of the spans, so needs a second pass + if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) { + if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); } + }); } + + if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); } + + if (marker.readOnly) { + seeReadOnlySpans(); + if (doc.history.done.length || doc.history.undone.length) + { doc.clearHistory(); } + } + if (marker.collapsed) { + marker.id = ++nextMarkerId; + marker.atomic = true; + } + if (cm) { + // Sync editor state + if (updateMaxLine) { cm.curOp.updateMaxLine = true; } + if (marker.collapsed) + { regChange(cm, from.line, to.line + 1); } + else if (marker.className || marker.startStyle || marker.endStyle || marker.css || + marker.attributes || marker.title) + { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } } + if (marker.atomic) { reCheckSelection(cm.doc); } + signalLater(cm, "markerAdded", cm, marker); + } + return marker + } + + // SHARED TEXTMARKERS + + // A shared marker spans multiple linked documents. It is + // implemented as a meta-marker-object controlling multiple normal + // markers. + var SharedTextMarker = function(markers, primary) { + this.markers = markers; + this.primary = primary; + for (var i = 0; i < markers.length; ++i) + { markers[i].parent = this; } + }; + + SharedTextMarker.prototype.clear = function () { + if (this.explicitlyCleared) { return } + this.explicitlyCleared = true; + for (var i = 0; i < this.markers.length; ++i) + { this.markers[i].clear(); } + signalLater(this, "clear"); + }; + + SharedTextMarker.prototype.find = function (side, lineObj) { + return this.primary.find(side, lineObj) + }; + eventMixin(SharedTextMarker); + + function markTextShared(doc, from, to, options, type) { + options = copyObj(options); + options.shared = false; + var markers = [markText(doc, from, to, options, type)], primary = markers[0]; + var widget = options.widgetNode; + linkedDocs(doc, function (doc) { + if (widget) { options.widgetNode = widget.cloneNode(true); } + markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type)); + for (var i = 0; i < doc.linked.length; ++i) + { if (doc.linked[i].isParent) { return } } + primary = lst(markers); + }); + return new SharedTextMarker(markers, primary) + } + + function findSharedMarkers(doc) { + return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; }) + } + + function copySharedMarkers(doc, markers) { + for (var i = 0; i < markers.length; i++) { + var marker = markers[i], pos = marker.find(); + var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to); + if (cmp(mFrom, mTo)) { + var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type); + marker.markers.push(subMark); + subMark.parent = marker; + } + } + } + + function detachSharedMarkers(markers) { + var loop = function ( i ) { + var marker = markers[i], linked = [marker.primary.doc]; + linkedDocs(marker.primary.doc, function (d) { return linked.push(d); }); + for (var j = 0; j < marker.markers.length; j++) { + var subMarker = marker.markers[j]; + if (indexOf(linked, subMarker.doc) == -1) { + subMarker.parent = null; + marker.markers.splice(j--, 1); + } + } + }; + + for (var i = 0; i < markers.length; i++) loop( i ); + } + + var nextDocId = 0; + var Doc = function(text, mode, firstLine, lineSep, direction) { + if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) } + if (firstLine == null) { firstLine = 0; } + + BranchChunk.call(this, [new LeafChunk([new Line("", null)])]); + this.first = firstLine; + this.scrollTop = this.scrollLeft = 0; + this.cantEdit = false; + this.cleanGeneration = 1; + this.modeFrontier = this.highlightFrontier = firstLine; + var start = Pos(firstLine, 0); + this.sel = simpleSelection(start); + this.history = new History(null); + this.id = ++nextDocId; + this.modeOption = mode; + this.lineSep = lineSep; + this.direction = (direction == "rtl") ? "rtl" : "ltr"; + this.extend = false; + + if (typeof text == "string") { text = this.splitLines(text); } + updateDoc(this, {from: start, to: start, text: text}); + setSelection(this, simpleSelection(start), sel_dontScroll); + }; + + Doc.prototype = createObj(BranchChunk.prototype, { + constructor: Doc, + // Iterate over the document. Supports two forms -- with only one + // argument, it calls that for each line in the document. With + // three, it iterates over the range given by the first two (with + // the second being non-inclusive). + iter: function(from, to, op) { + if (op) { this.iterN(from - this.first, to - from, op); } + else { this.iterN(this.first, this.first + this.size, from); } + }, + + // Non-public interface for adding and removing lines. + insert: function(at, lines) { + var height = 0; + for (var i = 0; i < lines.length; ++i) { height += lines[i].height; } + this.insertInner(at - this.first, lines, height); + }, + remove: function(at, n) { this.removeInner(at - this.first, n); }, + + // From here, the methods are part of the public interface. Most + // are also available from CodeMirror (editor) instances. + + getValue: function(lineSep) { + var lines = getLines(this, this.first, this.first + this.size); + if (lineSep === false) { return lines } + return lines.join(lineSep || this.lineSeparator()) + }, + setValue: docMethodOp(function(code) { + var top = Pos(this.first, 0), last = this.first + this.size - 1; + makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length), + text: this.splitLines(code), origin: "setValue", full: true}, true); + if (this.cm) { scrollToCoords(this.cm, 0, 0); } + setSelection(this, simpleSelection(top), sel_dontScroll); + }), + replaceRange: function(code, from, to, origin) { + from = clipPos(this, from); + to = to ? clipPos(this, to) : from; + replaceRange(this, code, from, to, origin); + }, + getRange: function(from, to, lineSep) { + var lines = getBetween(this, clipPos(this, from), clipPos(this, to)); + if (lineSep === false) { return lines } + if (lineSep === '') { return lines.join('') } + return lines.join(lineSep || this.lineSeparator()) + }, + + getLine: function(line) {var l = this.getLineHandle(line); return l && l.text}, + + getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }}, + getLineNumber: function(line) {return lineNo(line)}, + + getLineHandleVisualStart: function(line) { + if (typeof line == "number") { line = getLine(this, line); } + return visualLine(line) + }, + + lineCount: function() {return this.size}, + firstLine: function() {return this.first}, + lastLine: function() {return this.first + this.size - 1}, + + clipPos: function(pos) {return clipPos(this, pos)}, + + getCursor: function(start) { + var range = this.sel.primary(), pos; + if (start == null || start == "head") { pos = range.head; } + else if (start == "anchor") { pos = range.anchor; } + else if (start == "end" || start == "to" || start === false) { pos = range.to(); } + else { pos = range.from(); } + return pos + }, + listSelections: function() { return this.sel.ranges }, + somethingSelected: function() {return this.sel.somethingSelected()}, + + setCursor: docMethodOp(function(line, ch, options) { + setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options); + }), + setSelection: docMethodOp(function(anchor, head, options) { + setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options); + }), + extendSelection: docMethodOp(function(head, other, options) { + extendSelection(this, clipPos(this, head), other && clipPos(this, other), options); + }), + extendSelections: docMethodOp(function(heads, options) { + extendSelections(this, clipPosArray(this, heads), options); + }), + extendSelectionsBy: docMethodOp(function(f, options) { + var heads = map(this.sel.ranges, f); + extendSelections(this, clipPosArray(this, heads), options); + }), + setSelections: docMethodOp(function(ranges, primary, options) { + if (!ranges.length) { return } + var out = []; + for (var i = 0; i < ranges.length; i++) + { out[i] = new Range(clipPos(this, ranges[i].anchor), + clipPos(this, ranges[i].head || ranges[i].anchor)); } + if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); } + setSelection(this, normalizeSelection(this.cm, out, primary), options); + }), + addSelection: docMethodOp(function(anchor, head, options) { + var ranges = this.sel.ranges.slice(0); + ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor))); + setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options); + }), + + getSelection: function(lineSep) { + var ranges = this.sel.ranges, lines; + for (var i = 0; i < ranges.length; i++) { + var sel = getBetween(this, ranges[i].from(), ranges[i].to()); + lines = lines ? lines.concat(sel) : sel; + } + if (lineSep === false) { return lines } + else { return lines.join(lineSep || this.lineSeparator()) } + }, + getSelections: function(lineSep) { + var parts = [], ranges = this.sel.ranges; + for (var i = 0; i < ranges.length; i++) { + var sel = getBetween(this, ranges[i].from(), ranges[i].to()); + if (lineSep !== false) { sel = sel.join(lineSep || this.lineSeparator()); } + parts[i] = sel; + } + return parts + }, + replaceSelection: function(code, collapse, origin) { + var dup = []; + for (var i = 0; i < this.sel.ranges.length; i++) + { dup[i] = code; } + this.replaceSelections(dup, collapse, origin || "+input"); + }, + replaceSelections: docMethodOp(function(code, collapse, origin) { + var changes = [], sel = this.sel; + for (var i = 0; i < sel.ranges.length; i++) { + var range = sel.ranges[i]; + changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin}; + } + var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse); + for (var i$1 = changes.length - 1; i$1 >= 0; i$1--) + { makeChange(this, changes[i$1]); } + if (newSel) { setSelectionReplaceHistory(this, newSel); } + else if (this.cm) { ensureCursorVisible(this.cm); } + }), + undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}), + redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}), + undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}), + redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}), + + setExtending: function(val) {this.extend = val;}, + getExtending: function() {return this.extend}, + + historySize: function() { + var hist = this.history, done = 0, undone = 0; + for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } } + for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } } + return {undo: done, redo: undone} + }, + clearHistory: function() { + var this$1 = this; + + this.history = new History(this.history); + linkedDocs(this, function (doc) { return doc.history = this$1.history; }, true); + }, + + markClean: function() { + this.cleanGeneration = this.changeGeneration(true); + }, + changeGeneration: function(forceSplit) { + if (forceSplit) + { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; } + return this.history.generation + }, + isClean: function (gen) { + return this.history.generation == (gen || this.cleanGeneration) + }, + + getHistory: function() { + return {done: copyHistoryArray(this.history.done), + undone: copyHistoryArray(this.history.undone)} + }, + setHistory: function(histData) { + var hist = this.history = new History(this.history); + hist.done = copyHistoryArray(histData.done.slice(0), null, true); + hist.undone = copyHistoryArray(histData.undone.slice(0), null, true); + }, + + setGutterMarker: docMethodOp(function(line, gutterID, value) { + return changeLine(this, line, "gutter", function (line) { + var markers = line.gutterMarkers || (line.gutterMarkers = {}); + markers[gutterID] = value; + if (!value && isEmpty(markers)) { line.gutterMarkers = null; } + return true + }) + }), + + clearGutter: docMethodOp(function(gutterID) { + var this$1 = this; + + this.iter(function (line) { + if (line.gutterMarkers && line.gutterMarkers[gutterID]) { + changeLine(this$1, line, "gutter", function () { + line.gutterMarkers[gutterID] = null; + if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; } + return true + }); + } + }); + }), + + lineInfo: function(line) { + var n; + if (typeof line == "number") { + if (!isLine(this, line)) { return null } + n = line; + line = getLine(this, line); + if (!line) { return null } + } else { + n = lineNo(line); + if (n == null) { return null } + } + return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, + textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass, + widgets: line.widgets} + }, + + addLineClass: docMethodOp(function(handle, where, cls) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { + var prop = where == "text" ? "textClass" + : where == "background" ? "bgClass" + : where == "gutter" ? "gutterClass" : "wrapClass"; + if (!line[prop]) { line[prop] = cls; } + else if (classTest(cls).test(line[prop])) { return false } + else { line[prop] += " " + cls; } + return true + }) + }), + removeLineClass: docMethodOp(function(handle, where, cls) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { + var prop = where == "text" ? "textClass" + : where == "background" ? "bgClass" + : where == "gutter" ? "gutterClass" : "wrapClass"; + var cur = line[prop]; + if (!cur) { return false } + else if (cls == null) { line[prop] = null; } + else { + var found = cur.match(classTest(cls)); + if (!found) { return false } + var end = found.index + found[0].length; + line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null; + } + return true + }) + }), + + addLineWidget: docMethodOp(function(handle, node, options) { + return addLineWidget(this, handle, node, options) + }), + removeLineWidget: function(widget) { widget.clear(); }, + + markText: function(from, to, options) { + return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range") + }, + setBookmark: function(pos, options) { + var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options), + insertLeft: options && options.insertLeft, + clearWhenEmpty: false, shared: options && options.shared, + handleMouseEvents: options && options.handleMouseEvents}; + pos = clipPos(this, pos); + return markText(this, pos, pos, realOpts, "bookmark") + }, + findMarksAt: function(pos) { + pos = clipPos(this, pos); + var markers = [], spans = getLine(this, pos.line).markedSpans; + if (spans) { for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if ((span.from == null || span.from <= pos.ch) && + (span.to == null || span.to >= pos.ch)) + { markers.push(span.marker.parent || span.marker); } + } } + return markers + }, + findMarks: function(from, to, filter) { + from = clipPos(this, from); to = clipPos(this, to); + var found = [], lineNo = from.line; + this.iter(from.line, to.line + 1, function (line) { + var spans = line.markedSpans; + if (spans) { for (var i = 0; i < spans.length; i++) { + var span = spans[i]; + if (!(span.to != null && lineNo == from.line && from.ch >= span.to || + span.from == null && lineNo != from.line || + span.from != null && lineNo == to.line && span.from >= to.ch) && + (!filter || filter(span.marker))) + { found.push(span.marker.parent || span.marker); } + } } + ++lineNo; + }); + return found + }, + getAllMarks: function() { + var markers = []; + this.iter(function (line) { + var sps = line.markedSpans; + if (sps) { for (var i = 0; i < sps.length; ++i) + { if (sps[i].from != null) { markers.push(sps[i].marker); } } } + }); + return markers + }, + + posFromIndex: function(off) { + var ch, lineNo = this.first, sepSize = this.lineSeparator().length; + this.iter(function (line) { + var sz = line.text.length + sepSize; + if (sz > off) { ch = off; return true } + off -= sz; + ++lineNo; + }); + return clipPos(this, Pos(lineNo, ch)) + }, + indexFromPos: function (coords) { + coords = clipPos(this, coords); + var index = coords.ch; + if (coords.line < this.first || coords.ch < 0) { return 0 } + var sepSize = this.lineSeparator().length; + this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value + index += line.text.length + sepSize; + }); + return index + }, + + copy: function(copyHistory) { + var doc = new Doc(getLines(this, this.first, this.first + this.size), + this.modeOption, this.first, this.lineSep, this.direction); + doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft; + doc.sel = this.sel; + doc.extend = false; + if (copyHistory) { + doc.history.undoDepth = this.history.undoDepth; + doc.setHistory(this.getHistory()); + } + return doc + }, + + linkedDoc: function(options) { + if (!options) { options = {}; } + var from = this.first, to = this.first + this.size; + if (options.from != null && options.from > from) { from = options.from; } + if (options.to != null && options.to < to) { to = options.to; } + var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction); + if (options.sharedHist) { copy.history = this.history + ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist}); + copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]; + copySharedMarkers(copy, findSharedMarkers(this)); + return copy + }, + unlinkDoc: function(other) { + if (other instanceof CodeMirror) { other = other.doc; } + if (this.linked) { for (var i = 0; i < this.linked.length; ++i) { + var link = this.linked[i]; + if (link.doc != other) { continue } + this.linked.splice(i, 1); + other.unlinkDoc(this); + detachSharedMarkers(findSharedMarkers(this)); + break + } } + // If the histories were shared, split them again + if (other.history == this.history) { + var splitIds = [other.id]; + linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true); + other.history = new History(null); + other.history.done = copyHistoryArray(this.history.done, splitIds); + other.history.undone = copyHistoryArray(this.history.undone, splitIds); + } + }, + iterLinkedDocs: function(f) {linkedDocs(this, f);}, + + getMode: function() {return this.mode}, + getEditor: function() {return this.cm}, + + splitLines: function(str) { + if (this.lineSep) { return str.split(this.lineSep) } + return splitLinesAuto(str) + }, + lineSeparator: function() { return this.lineSep || "\n" }, + + setDirection: docMethodOp(function (dir) { + if (dir != "rtl") { dir = "ltr"; } + if (dir == this.direction) { return } + this.direction = dir; + this.iter(function (line) { return line.order = null; }); + if (this.cm) { directionChanged(this.cm); } + }) + }); + + // Public alias. + Doc.prototype.eachLine = Doc.prototype.iter; + + // Kludge to work around strange IE behavior where it'll sometimes + // re-fire a series of drag-related events right after the drop (#1551) + var lastDrop = 0; + + function onDrop(e) { + var cm = this; + clearDragCursor(cm); + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) + { return } + e_preventDefault(e); + if (ie) { lastDrop = +new Date; } + var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files; + if (!pos || cm.isReadOnly()) { return } + // Might be a file drop, in which case we simply extract the text + // and insert it. + if (files && files.length && window.FileReader && window.File) { + var n = files.length, text = Array(n), read = 0; + var markAsReadAndPasteIfAllFilesAreRead = function () { + if (++read == n) { + operation(cm, function () { + pos = clipPos(cm.doc, pos); + var change = {from: pos, to: pos, + text: cm.doc.splitLines( + text.filter(function (t) { return t != null; }).join(cm.doc.lineSeparator())), + origin: "paste"}; + makeChange(cm.doc, change); + setSelectionReplaceHistory(cm.doc, simpleSelection(clipPos(cm.doc, pos), clipPos(cm.doc, changeEnd(change)))); + })(); + } + }; + var readTextFromFile = function (file, i) { + if (cm.options.allowDropFileTypes && + indexOf(cm.options.allowDropFileTypes, file.type) == -1) { + markAsReadAndPasteIfAllFilesAreRead(); + return + } + var reader = new FileReader; + reader.onerror = function () { return markAsReadAndPasteIfAllFilesAreRead(); }; + reader.onload = function () { + var content = reader.result; + if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { + markAsReadAndPasteIfAllFilesAreRead(); + return + } + text[i] = content; + markAsReadAndPasteIfAllFilesAreRead(); + }; + reader.readAsText(file); + }; + for (var i = 0; i < files.length; i++) { readTextFromFile(files[i], i); } + } else { // Normal drop + // Don't do a replace if the drop happened inside of the selected text. + if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) { + cm.state.draggingText(e); + // Ensure the editor is re-focused + setTimeout(function () { return cm.display.input.focus(); }, 20); + return + } + try { + var text$1 = e.dataTransfer.getData("Text"); + if (text$1) { + var selected; + if (cm.state.draggingText && !cm.state.draggingText.copy) + { selected = cm.listSelections(); } + setSelectionNoUndo(cm.doc, simpleSelection(pos, pos)); + if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1) + { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } } + cm.replaceSelection(text$1, "around", "paste"); + cm.display.input.focus(); + } + } + catch(e$1){} + } + } + + function onDragStart(cm, e) { + if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return } + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return } + + e.dataTransfer.setData("Text", cm.getSelection()); + e.dataTransfer.effectAllowed = "copyMove"; + + // Use dummy image instead of default browsers image. + // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there. + if (e.dataTransfer.setDragImage && !safari) { + var img = elt("img", null, null, "position: fixed; left: 0; top: 0;"); + img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; + if (presto) { + img.width = img.height = 1; + cm.display.wrapper.appendChild(img); + // Force a relayout, or Opera won't use our image for some obscure reason + img._top = img.offsetTop; + } + e.dataTransfer.setDragImage(img, 0, 0); + if (presto) { img.parentNode.removeChild(img); } + } + } + + function onDragOver(cm, e) { + var pos = posFromMouse(cm, e); + if (!pos) { return } + var frag = document.createDocumentFragment(); + drawSelectionCursor(cm, pos, frag); + if (!cm.display.dragCursor) { + cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors"); + cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv); + } + removeChildrenAndAdd(cm.display.dragCursor, frag); + } + + function clearDragCursor(cm) { + if (cm.display.dragCursor) { + cm.display.lineSpace.removeChild(cm.display.dragCursor); + cm.display.dragCursor = null; + } + } + + // These must be handled carefully, because naively registering a + // handler for each editor will cause the editors to never be + // garbage collected. + + function forEachCodeMirror(f) { + if (!document.getElementsByClassName) { return } + var byClass = document.getElementsByClassName("CodeMirror"), editors = []; + for (var i = 0; i < byClass.length; i++) { + var cm = byClass[i].CodeMirror; + if (cm) { editors.push(cm); } + } + if (editors.length) { editors[0].operation(function () { + for (var i = 0; i < editors.length; i++) { f(editors[i]); } + }); } + } + + var globalsRegistered = false; + function ensureGlobalHandlers() { + if (globalsRegistered) { return } + registerGlobalHandlers(); + globalsRegistered = true; + } + function registerGlobalHandlers() { + // When the window resizes, we need to refresh active editors. + var resizeTimer; + on(window, "resize", function () { + if (resizeTimer == null) { resizeTimer = setTimeout(function () { + resizeTimer = null; + forEachCodeMirror(onResize); + }, 100); } + }); + // When the window loses focus, we want to show the editor as blurred + on(window, "blur", function () { return forEachCodeMirror(onBlur); }); + } + // Called when the window resizes + function onResize(cm) { + var d = cm.display; + // Might be a text scaling operation, clear size caches. + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; + d.scrollbarsClipped = false; + cm.setSize(); + } + + var keyNames = { + 3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", + 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", + 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", + 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", + 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 145: "ScrollLock", + 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", + 221: "]", 222: "'", 224: "Mod", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", + 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert" + }; + + // Number keys + for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); } + // Alphabetic keys + for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); } + // Function keys + for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; } + + var keyMap = {}; + + keyMap.basic = { + "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", + "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", + "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore", + "Tab": "defaultTab", "Shift-Tab": "indentAuto", + "Enter": "newlineAndIndent", "Insert": "toggleOverwrite", + "Esc": "singleSelection" + }; + // Note that the save and find-related commands aren't defined by + // default. User code or addons can define them. Unknown commands + // are simply ignored. + keyMap.pcDefault = { + "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", + "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown", + "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", + "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find", + "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", + "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", + "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection", + "fallthrough": "basic" + }; + // Very basic readline/emacs-style bindings, which are standard on Mac. + keyMap.emacsy = { + "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", + "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", + "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", + "Ctrl-T": "transposeChars", "Ctrl-O": "openLine" + }; + keyMap.macDefault = { + "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", + "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft", + "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore", + "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find", + "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", + "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight", + "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd", + "fallthrough": ["basic", "emacsy"] + }; + keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault; + + // KEYMAP DISPATCH + + function normalizeKeyName(name) { + var parts = name.split(/-(?!$)/); + name = parts[parts.length - 1]; + var alt, ctrl, shift, cmd; + for (var i = 0; i < parts.length - 1; i++) { + var mod = parts[i]; + if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; } + else if (/^a(lt)?$/i.test(mod)) { alt = true; } + else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; } + else if (/^s(hift)?$/i.test(mod)) { shift = true; } + else { throw new Error("Unrecognized modifier name: " + mod) } + } + if (alt) { name = "Alt-" + name; } + if (ctrl) { name = "Ctrl-" + name; } + if (cmd) { name = "Cmd-" + name; } + if (shift) { name = "Shift-" + name; } + return name + } + + // This is a kludge to keep keymaps mostly working as raw objects + // (backwards compatibility) while at the same time support features + // like normalization and multi-stroke key bindings. It compiles a + // new normalized keymap, and then updates the old object to reflect + // this. + function normalizeKeyMap(keymap) { + var copy = {}; + for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) { + var value = keymap[keyname]; + if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue } + if (value == "...") { delete keymap[keyname]; continue } + + var keys = map(keyname.split(" "), normalizeKeyName); + for (var i = 0; i < keys.length; i++) { + var val = (void 0), name = (void 0); + if (i == keys.length - 1) { + name = keys.join(" "); + val = value; + } else { + name = keys.slice(0, i + 1).join(" "); + val = "..."; + } + var prev = copy[name]; + if (!prev) { copy[name] = val; } + else if (prev != val) { throw new Error("Inconsistent bindings for " + name) } + } + delete keymap[keyname]; + } } + for (var prop in copy) { keymap[prop] = copy[prop]; } + return keymap + } + + function lookupKey(key, map, handle, context) { + map = getKeyMap(map); + var found = map.call ? map.call(key, context) : map[key]; + if (found === false) { return "nothing" } + if (found === "...") { return "multi" } + if (found != null && handle(found)) { return "handled" } + + if (map.fallthrough) { + if (Object.prototype.toString.call(map.fallthrough) != "[object Array]") + { return lookupKey(key, map.fallthrough, handle, context) } + for (var i = 0; i < map.fallthrough.length; i++) { + var result = lookupKey(key, map.fallthrough[i], handle, context); + if (result) { return result } + } + } + } + + // Modifier key presses don't count as 'real' key presses for the + // purpose of keymap fallthrough. + function isModifierKey(value) { + var name = typeof value == "string" ? value : keyNames[value.keyCode]; + return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod" + } + + function addModifierNames(name, event, noShift) { + var base = name; + if (event.altKey && base != "Alt") { name = "Alt-" + name; } + if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; } + if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Mod") { name = "Cmd-" + name; } + if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; } + return name + } + + // Look up the name of a key as indicated by an event object. + function keyName(event, noShift) { + if (presto && event.keyCode == 34 && event["char"]) { return false } + var name = keyNames[event.keyCode]; + if (name == null || event.altGraphKey) { return false } + // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause, + // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+) + if (event.keyCode == 3 && event.code) { name = event.code; } + return addModifierNames(name, event, noShift) + } + + function getKeyMap(val) { + return typeof val == "string" ? keyMap[val] : val + } + + // Helper for deleting text near the selection(s), used to implement + // backspace, delete, and similar functionality. + function deleteNearSelection(cm, compute) { + var ranges = cm.doc.sel.ranges, kill = []; + // Build up a set of ranges to kill first, merging overlapping + // ranges. + for (var i = 0; i < ranges.length; i++) { + var toKill = compute(ranges[i]); + while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) { + var replaced = kill.pop(); + if (cmp(replaced.from, toKill.from) < 0) { + toKill.from = replaced.from; + break + } + } + kill.push(toKill); + } + // Next, remove those actual ranges. + runInOp(cm, function () { + for (var i = kill.length - 1; i >= 0; i--) + { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); } + ensureCursorVisible(cm); + }); + } + + function moveCharLogically(line, ch, dir) { + var target = skipExtendingChars(line.text, ch + dir, dir); + return target < 0 || target > line.text.length ? null : target + } + + function moveLogically(line, start, dir) { + var ch = moveCharLogically(line, start.ch, dir); + return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before") + } + + function endOfLine(visually, cm, lineObj, lineNo, dir) { + if (visually) { + if (cm.doc.direction == "rtl") { dir = -dir; } + var order = getOrder(lineObj, cm.doc.direction); + if (order) { + var part = dir < 0 ? lst(order) : order[0]; + var moveInStorageOrder = (dir < 0) == (part.level == 1); + var sticky = moveInStorageOrder ? "after" : "before"; + var ch; + // With a wrapped rtl chunk (possibly spanning multiple bidi parts), + // it could be that the last bidi part is not on the last visual line, + // since visual lines contain content order-consecutive chunks. + // Thus, in rtl, we are looking for the first (content-order) character + // in the rtl chunk that is on the last line (that is, the same line + // as the last (content-order) character). + if (part.level > 0 || cm.doc.direction == "rtl") { + var prep = prepareMeasureForLine(cm, lineObj); + ch = dir < 0 ? lineObj.text.length - 1 : 0; + var targetTop = measureCharPrepared(cm, prep, ch).top; + ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch); + if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); } + } else { ch = dir < 0 ? part.to : part.from; } + return new Pos(lineNo, ch, sticky) + } + } + return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after") + } + + function moveVisually(cm, line, start, dir) { + var bidi = getOrder(line, cm.doc.direction); + if (!bidi) { return moveLogically(line, start, dir) } + if (start.ch >= line.text.length) { + start.ch = line.text.length; + start.sticky = "before"; + } else if (start.ch <= 0) { + start.ch = 0; + start.sticky = "after"; + } + var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]; + if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) { + // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines, + // nothing interesting happens. + return moveLogically(line, start, dir) + } + + var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }; + var prep; + var getWrappedLineExtent = function (ch) { + if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} } + prep = prep || prepareMeasureForLine(cm, line); + return wrappedLineExtentChar(cm, line, prep, ch) + }; + var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch); + + if (cm.doc.direction == "rtl" || part.level == 1) { + var moveInStorageOrder = (part.level == 1) == (dir < 0); + var ch = mv(start, moveInStorageOrder ? 1 : -1); + if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) { + // Case 2: We move within an rtl part or in an rtl editor on the same visual line + var sticky = moveInStorageOrder ? "before" : "after"; + return new Pos(start.line, ch, sticky) + } + } + + // Case 3: Could not move within this bidi part in this visual line, so leave + // the current bidi part + + var searchInVisualLine = function (partPos, dir, wrappedLineExtent) { + var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder + ? new Pos(start.line, mv(ch, 1), "before") + : new Pos(start.line, ch, "after"); }; + + for (; partPos >= 0 && partPos < bidi.length; partPos += dir) { + var part = bidi[partPos]; + var moveInStorageOrder = (dir > 0) == (part.level != 1); + var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1); + if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) } + ch = moveInStorageOrder ? part.from : mv(part.to, -1); + if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) } + } + }; + + // Case 3a: Look for other bidi parts on the same visual line + var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent); + if (res) { return res } + + // Case 3b: Look for other bidi parts on the next visual line + var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1); + if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) { + res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh)); + if (res) { return res } + } + + // Case 4: Nowhere to move + return null + } + + // Commands are parameter-less actions that can be performed on an + // editor, mostly used for keybindings. + var commands = { + selectAll: selectAll, + singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); }, + killLine: function (cm) { return deleteNearSelection(cm, function (range) { + if (range.empty()) { + var len = getLine(cm.doc, range.head.line).text.length; + if (range.head.ch == len && range.head.line < cm.lastLine()) + { return {from: range.head, to: Pos(range.head.line + 1, 0)} } + else + { return {from: range.head, to: Pos(range.head.line, len)} } + } else { + return {from: range.from(), to: range.to()} + } + }); }, + deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({ + from: Pos(range.from().line, 0), + to: clipPos(cm.doc, Pos(range.to().line + 1, 0)) + }); }); }, + delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({ + from: Pos(range.from().line, 0), to: range.from() + }); }); }, + delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { + var top = cm.charCoords(range.head, "div").top + 5; + var leftPos = cm.coordsChar({left: 0, top: top}, "div"); + return {from: leftPos, to: range.from()} + }); }, + delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) { + var top = cm.charCoords(range.head, "div").top + 5; + var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"); + return {from: range.from(), to: rightPos } + }); }, + undo: function (cm) { return cm.undo(); }, + redo: function (cm) { return cm.redo(); }, + undoSelection: function (cm) { return cm.undoSelection(); }, + redoSelection: function (cm) { return cm.redoSelection(); }, + goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); }, + goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); }, + goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); }, + {origin: "+move", bias: 1} + ); }, + goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); }, + {origin: "+move", bias: 1} + ); }, + goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); }, + {origin: "+move", bias: -1} + ); }, + goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.cursorCoords(range.head, "div").top + 5; + return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") + }, sel_move); }, + goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.cursorCoords(range.head, "div").top + 5; + return cm.coordsChar({left: 0, top: top}, "div") + }, sel_move); }, + goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.cursorCoords(range.head, "div").top + 5; + var pos = cm.coordsChar({left: 0, top: top}, "div"); + if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) } + return pos + }, sel_move); }, + goLineUp: function (cm) { return cm.moveV(-1, "line"); }, + goLineDown: function (cm) { return cm.moveV(1, "line"); }, + goPageUp: function (cm) { return cm.moveV(-1, "page"); }, + goPageDown: function (cm) { return cm.moveV(1, "page"); }, + goCharLeft: function (cm) { return cm.moveH(-1, "char"); }, + goCharRight: function (cm) { return cm.moveH(1, "char"); }, + goColumnLeft: function (cm) { return cm.moveH(-1, "column"); }, + goColumnRight: function (cm) { return cm.moveH(1, "column"); }, + goWordLeft: function (cm) { return cm.moveH(-1, "word"); }, + goGroupRight: function (cm) { return cm.moveH(1, "group"); }, + goGroupLeft: function (cm) { return cm.moveH(-1, "group"); }, + goWordRight: function (cm) { return cm.moveH(1, "word"); }, + delCharBefore: function (cm) { return cm.deleteH(-1, "codepoint"); }, + delCharAfter: function (cm) { return cm.deleteH(1, "char"); }, + delWordBefore: function (cm) { return cm.deleteH(-1, "word"); }, + delWordAfter: function (cm) { return cm.deleteH(1, "word"); }, + delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); }, + delGroupAfter: function (cm) { return cm.deleteH(1, "group"); }, + indentAuto: function (cm) { return cm.indentSelection("smart"); }, + indentMore: function (cm) { return cm.indentSelection("add"); }, + indentLess: function (cm) { return cm.indentSelection("subtract"); }, + insertTab: function (cm) { return cm.replaceSelection("\t"); }, + insertSoftTab: function (cm) { + var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize; + for (var i = 0; i < ranges.length; i++) { + var pos = ranges[i].from(); + var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize); + spaces.push(spaceStr(tabSize - col % tabSize)); + } + cm.replaceSelections(spaces); + }, + defaultTab: function (cm) { + if (cm.somethingSelected()) { cm.indentSelection("add"); } + else { cm.execCommand("insertTab"); } + }, + // Swap the two chars left and right of each selection's head. + // Move cursor behind the two swapped characters afterwards. + // + // Doesn't consider line feeds a character. + // Doesn't scan more than one line above to find a character. + // Doesn't do anything on an empty line. + // Doesn't do anything with non-empty selections. + transposeChars: function (cm) { return runInOp(cm, function () { + var ranges = cm.listSelections(), newSel = []; + for (var i = 0; i < ranges.length; i++) { + if (!ranges[i].empty()) { continue } + var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text; + if (line) { + if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); } + if (cur.ch > 0) { + cur = new Pos(cur.line, cur.ch + 1); + cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), + Pos(cur.line, cur.ch - 2), cur, "+transpose"); + } else if (cur.line > cm.doc.first) { + var prev = getLine(cm.doc, cur.line - 1).text; + if (prev) { + cur = new Pos(cur.line, 1); + cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() + + prev.charAt(prev.length - 1), + Pos(cur.line - 1, prev.length - 1), cur, "+transpose"); + } + } + } + newSel.push(new Range(cur, cur)); + } + cm.setSelections(newSel); + }); }, + newlineAndIndent: function (cm) { return runInOp(cm, function () { + var sels = cm.listSelections(); + for (var i = sels.length - 1; i >= 0; i--) + { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); } + sels = cm.listSelections(); + for (var i$1 = 0; i$1 < sels.length; i$1++) + { cm.indentLine(sels[i$1].from().line, null, true); } + ensureCursorVisible(cm); + }); }, + openLine: function (cm) { return cm.replaceSelection("\n", "start"); }, + toggleOverwrite: function (cm) { return cm.toggleOverwrite(); } + }; + + + function lineStart(cm, lineN) { + var line = getLine(cm.doc, lineN); + var visual = visualLine(line); + if (visual != line) { lineN = lineNo(visual); } + return endOfLine(true, cm, visual, lineN, 1) + } + function lineEnd(cm, lineN) { + var line = getLine(cm.doc, lineN); + var visual = visualLineEnd(line); + if (visual != line) { lineN = lineNo(visual); } + return endOfLine(true, cm, line, lineN, -1) + } + function lineStartSmart(cm, pos) { + var start = lineStart(cm, pos.line); + var line = getLine(cm.doc, start.line); + var order = getOrder(line, cm.doc.direction); + if (!order || order[0].level == 0) { + var firstNonWS = Math.max(start.ch, line.text.search(/\S/)); + var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch; + return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky) + } + return start + } + + // Run a handler that was bound to a key. + function doHandleBinding(cm, bound, dropShift) { + if (typeof bound == "string") { + bound = commands[bound]; + if (!bound) { return false } + } + // Ensure previous input has been read, so that the handler sees a + // consistent view of the document + cm.display.input.ensurePolled(); + var prevShift = cm.display.shift, done = false; + try { + if (cm.isReadOnly()) { cm.state.suppressEdits = true; } + if (dropShift) { cm.display.shift = false; } + done = bound(cm) != Pass; + } finally { + cm.display.shift = prevShift; + cm.state.suppressEdits = false; + } + return done + } + + function lookupKeyForEditor(cm, name, handle) { + for (var i = 0; i < cm.state.keyMaps.length; i++) { + var result = lookupKey(name, cm.state.keyMaps[i], handle, cm); + if (result) { return result } + } + return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm)) + || lookupKey(name, cm.options.keyMap, handle, cm) + } + + // Note that, despite the name, this function is also used to check + // for bound mouse clicks. + + var stopSeq = new Delayed; + + function dispatchKey(cm, name, e, handle) { + var seq = cm.state.keySeq; + if (seq) { + if (isModifierKey(name)) { return "handled" } + if (/\'$/.test(name)) + { cm.state.keySeq = null; } + else + { stopSeq.set(50, function () { + if (cm.state.keySeq == seq) { + cm.state.keySeq = null; + cm.display.input.reset(); + } + }); } + if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true } + } + return dispatchKeyInner(cm, name, e, handle) + } + + function dispatchKeyInner(cm, name, e, handle) { + var result = lookupKeyForEditor(cm, name, handle); + + if (result == "multi") + { cm.state.keySeq = name; } + if (result == "handled") + { signalLater(cm, "keyHandled", cm, name, e); } + + if (result == "handled" || result == "multi") { + e_preventDefault(e); + restartBlink(cm); + } + + return !!result + } + + // Handle a key from the keydown event. + function handleKeyBinding(cm, e) { + var name = keyName(e, true); + if (!name) { return false } + + if (e.shiftKey && !cm.state.keySeq) { + // First try to resolve full name (including 'Shift-'). Failing + // that, see if there is a cursor-motion command (starting with + // 'go') bound to the keyname without 'Shift-'. + return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); }) + || dispatchKey(cm, name, e, function (b) { + if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion) + { return doHandleBinding(cm, b) } + }) + } else { + return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); }) + } + } + + // Handle a key from the keypress event + function handleCharBinding(cm, e, ch) { + return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); }) + } + + var lastStoppedKey = null; + function onKeyDown(e) { + var cm = this; + if (e.target && e.target != cm.display.input.getField()) { return } + cm.curOp.focus = activeElt(); + if (signalDOMEvent(cm, e)) { return } + // IE does strange things with escape. + if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; } + var code = e.keyCode; + cm.display.shift = code == 16 || e.shiftKey; + var handled = handleKeyBinding(cm, e); + if (presto) { + lastStoppedKey = handled ? code : null; + // Opera has no cut event... we try to at least catch the key combo + if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey)) + { cm.replaceSelection("", null, "cut"); } + } + if (gecko && !mac && !handled && code == 46 && e.shiftKey && !e.ctrlKey && document.execCommand) + { document.execCommand("cut"); } + + // Turn mouse into crosshair when Alt is held on Mac. + if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className)) + { showCrossHair(cm); } + } + + function showCrossHair(cm) { + var lineDiv = cm.display.lineDiv; + addClass(lineDiv, "CodeMirror-crosshair"); + + function up(e) { + if (e.keyCode == 18 || !e.altKey) { + rmClass(lineDiv, "CodeMirror-crosshair"); + off(document, "keyup", up); + off(document, "mouseover", up); + } + } + on(document, "keyup", up); + on(document, "mouseover", up); + } + + function onKeyUp(e) { + if (e.keyCode == 16) { this.doc.sel.shift = false; } + signalDOMEvent(this, e); + } + + function onKeyPress(e) { + var cm = this; + if (e.target && e.target != cm.display.input.getField()) { return } + if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return } + var keyCode = e.keyCode, charCode = e.charCode; + if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return} + if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return } + var ch = String.fromCharCode(charCode == null ? keyCode : charCode); + // Some browsers fire keypress events for backspace + if (ch == "\x08") { return } + if (handleCharBinding(cm, e, ch)) { return } + cm.display.input.onKeyPress(e); + } + + var DOUBLECLICK_DELAY = 400; + + var PastClick = function(time, pos, button) { + this.time = time; + this.pos = pos; + this.button = button; + }; + + PastClick.prototype.compare = function (time, pos, button) { + return this.time + DOUBLECLICK_DELAY > time && + cmp(pos, this.pos) == 0 && button == this.button + }; + + var lastClick, lastDoubleClick; + function clickRepeat(pos, button) { + var now = +new Date; + if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) { + lastClick = lastDoubleClick = null; + return "triple" + } else if (lastClick && lastClick.compare(now, pos, button)) { + lastDoubleClick = new PastClick(now, pos, button); + lastClick = null; + return "double" + } else { + lastClick = new PastClick(now, pos, button); + lastDoubleClick = null; + return "single" + } + } + + // A mouse down can be a single click, double click, triple click, + // start of selection drag, start of text drag, new cursor + // (ctrl-click), rectangle drag (alt-drag), or xwin + // middle-click-paste. Or it might be a click on something we should + // not interfere with, such as a scrollbar or widget. + function onMouseDown(e) { + var cm = this, display = cm.display; + if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return } + display.input.ensurePolled(); + display.shift = e.shiftKey; + + if (eventInWidget(display, e)) { + if (!webkit) { + // Briefly turn off draggability, to allow widgets to do + // normal dragging things. + display.scroller.draggable = false; + setTimeout(function () { return display.scroller.draggable = true; }, 100); + } + return + } + if (clickInGutter(cm, e)) { return } + var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single"; + window.focus(); + + // #3261: make sure, that we're not starting a second selection + if (button == 1 && cm.state.selectingText) + { cm.state.selectingText(e); } + + if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return } + + if (button == 1) { + if (pos) { leftButtonDown(cm, pos, repeat, e); } + else if (e_target(e) == display.scroller) { e_preventDefault(e); } + } else if (button == 2) { + if (pos) { extendSelection(cm.doc, pos); } + setTimeout(function () { return display.input.focus(); }, 20); + } else if (button == 3) { + if (captureRightClick) { cm.display.input.onContextMenu(e); } + else { delayBlurEvent(cm); } + } + } + + function handleMappedButton(cm, button, pos, repeat, event) { + var name = "Click"; + if (repeat == "double") { name = "Double" + name; } + else if (repeat == "triple") { name = "Triple" + name; } + name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name; + + return dispatchKey(cm, addModifierNames(name, event), event, function (bound) { + if (typeof bound == "string") { bound = commands[bound]; } + if (!bound) { return false } + var done = false; + try { + if (cm.isReadOnly()) { cm.state.suppressEdits = true; } + done = bound(cm, pos) != Pass; + } finally { + cm.state.suppressEdits = false; + } + return done + }) + } + + function configureMouse(cm, repeat, event) { + var option = cm.getOption("configureMouse"); + var value = option ? option(cm, repeat, event) : {}; + if (value.unit == null) { + var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey; + value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line"; + } + if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; } + if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; } + if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); } + return value + } + + function leftButtonDown(cm, pos, repeat, event) { + if (ie) { setTimeout(bind(ensureFocus, cm), 0); } + else { cm.curOp.focus = activeElt(); } + + var behavior = configureMouse(cm, repeat, event); + + var sel = cm.doc.sel, contained; + if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() && + repeat == "single" && (contained = sel.contains(pos)) > -1 && + (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) && + (cmp(contained.to(), pos) > 0 || pos.xRel < 0)) + { leftButtonStartDrag(cm, event, pos, behavior); } + else + { leftButtonSelect(cm, event, pos, behavior); } + } + + // Start a text drag. When it ends, see if any dragging actually + // happen, and treat as a click if it didn't. + function leftButtonStartDrag(cm, event, pos, behavior) { + var display = cm.display, moved = false; + var dragEnd = operation(cm, function (e) { + if (webkit) { display.scroller.draggable = false; } + cm.state.draggingText = false; + if (cm.state.delayingBlurEvent) { + if (cm.hasFocus()) { cm.state.delayingBlurEvent = false; } + else { delayBlurEvent(cm); } + } + off(display.wrapper.ownerDocument, "mouseup", dragEnd); + off(display.wrapper.ownerDocument, "mousemove", mouseMove); + off(display.scroller, "dragstart", dragStart); + off(display.scroller, "drop", dragEnd); + if (!moved) { + e_preventDefault(e); + if (!behavior.addNew) + { extendSelection(cm.doc, pos, null, null, behavior.extend); } + // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081) + if ((webkit && !safari) || ie && ie_version == 9) + { setTimeout(function () {display.wrapper.ownerDocument.body.focus({preventScroll: true}); display.input.focus();}, 20); } + else + { display.input.focus(); } + } + }); + var mouseMove = function(e2) { + moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10; + }; + var dragStart = function () { return moved = true; }; + // Let the drag handler handle this. + if (webkit) { display.scroller.draggable = true; } + cm.state.draggingText = dragEnd; + dragEnd.copy = !behavior.moveOnDrag; + on(display.wrapper.ownerDocument, "mouseup", dragEnd); + on(display.wrapper.ownerDocument, "mousemove", mouseMove); + on(display.scroller, "dragstart", dragStart); + on(display.scroller, "drop", dragEnd); + + cm.state.delayingBlurEvent = true; + setTimeout(function () { return display.input.focus(); }, 20); + // IE's approach to draggable + if (display.scroller.dragDrop) { display.scroller.dragDrop(); } + } + + function rangeForUnit(cm, pos, unit) { + if (unit == "char") { return new Range(pos, pos) } + if (unit == "word") { return cm.findWordAt(pos) } + if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) } + var result = unit(cm, pos); + return new Range(result.from, result.to) + } + + // Normal selection, as opposed to text dragging. + function leftButtonSelect(cm, event, start, behavior) { + if (ie) { delayBlurEvent(cm); } + var display = cm.display, doc = cm.doc; + e_preventDefault(event); + + var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges; + if (behavior.addNew && !behavior.extend) { + ourIndex = doc.sel.contains(start); + if (ourIndex > -1) + { ourRange = ranges[ourIndex]; } + else + { ourRange = new Range(start, start); } + } else { + ourRange = doc.sel.primary(); + ourIndex = doc.sel.primIndex; + } + + if (behavior.unit == "rectangle") { + if (!behavior.addNew) { ourRange = new Range(start, start); } + start = posFromMouse(cm, event, true, true); + ourIndex = -1; + } else { + var range = rangeForUnit(cm, start, behavior.unit); + if (behavior.extend) + { ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend); } + else + { ourRange = range; } + } + + if (!behavior.addNew) { + ourIndex = 0; + setSelection(doc, new Selection([ourRange], 0), sel_mouse); + startSel = doc.sel; + } else if (ourIndex == -1) { + ourIndex = ranges.length; + setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex), + {scroll: false, origin: "*mouse"}); + } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) { + setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0), + {scroll: false, origin: "*mouse"}); + startSel = doc.sel; + } else { + replaceOneSelection(doc, ourIndex, ourRange, sel_mouse); + } + + var lastPos = start; + function extendTo(pos) { + if (cmp(lastPos, pos) == 0) { return } + lastPos = pos; + + if (behavior.unit == "rectangle") { + var ranges = [], tabSize = cm.options.tabSize; + var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize); + var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize); + var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol); + for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line)); + line <= end; line++) { + var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize); + if (left == right) + { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); } + else if (text.length > leftPos) + { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); } + } + if (!ranges.length) { ranges.push(new Range(start, start)); } + setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex), + {origin: "*mouse", scroll: false}); + cm.scrollIntoView(pos); + } else { + var oldRange = ourRange; + var range = rangeForUnit(cm, pos, behavior.unit); + var anchor = oldRange.anchor, head; + if (cmp(range.anchor, anchor) > 0) { + head = range.head; + anchor = minPos(oldRange.from(), range.anchor); + } else { + head = range.anchor; + anchor = maxPos(oldRange.to(), range.head); + } + var ranges$1 = startSel.ranges.slice(0); + ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head)); + setSelection(doc, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse); + } + } + + var editorSize = display.wrapper.getBoundingClientRect(); + // Used to ensure timeout re-tries don't fire when another extend + // happened in the meantime (clearTimeout isn't reliable -- at + // least on Chrome, the timeouts still happen even when cleared, + // if the clear happens after their scheduled firing time). + var counter = 0; + + function extend(e) { + var curCount = ++counter; + var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle"); + if (!cur) { return } + if (cmp(cur, lastPos) != 0) { + cm.curOp.focus = activeElt(); + extendTo(cur); + var visible = visibleLines(display, doc); + if (cur.line >= visible.to || cur.line < visible.from) + { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); } + } else { + var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0; + if (outside) { setTimeout(operation(cm, function () { + if (counter != curCount) { return } + display.scroller.scrollTop += outside; + extend(e); + }), 50); } + } + } + + function done(e) { + cm.state.selectingText = false; + counter = Infinity; + // If e is null or undefined we interpret this as someone trying + // to explicitly cancel the selection rather than the user + // letting go of the mouse button. + if (e) { + e_preventDefault(e); + display.input.focus(); + } + off(display.wrapper.ownerDocument, "mousemove", move); + off(display.wrapper.ownerDocument, "mouseup", up); + doc.history.lastSelOrigin = null; + } + + var move = operation(cm, function (e) { + if (e.buttons === 0 || !e_button(e)) { done(e); } + else { extend(e); } + }); + var up = operation(cm, done); + cm.state.selectingText = up; + on(display.wrapper.ownerDocument, "mousemove", move); + on(display.wrapper.ownerDocument, "mouseup", up); + } + + // Used when mouse-selecting to adjust the anchor to the proper side + // of a bidi jump depending on the visual position of the head. + function bidiSimplify(cm, range) { + var anchor = range.anchor; + var head = range.head; + var anchorLine = getLine(cm.doc, anchor.line); + if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range } + var order = getOrder(anchorLine); + if (!order) { return range } + var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index]; + if (part.from != anchor.ch && part.to != anchor.ch) { return range } + var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1); + if (boundary == 0 || boundary == order.length) { return range } + + // Compute the relative visual position of the head compared to the + // anchor (<0 is to the left, >0 to the right) + var leftSide; + if (head.line != anchor.line) { + leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0; + } else { + var headIndex = getBidiPartAt(order, head.ch, head.sticky); + var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1); + if (headIndex == boundary - 1 || headIndex == boundary) + { leftSide = dir < 0; } + else + { leftSide = dir > 0; } + } + + var usePart = order[boundary + (leftSide ? -1 : 0)]; + var from = leftSide == (usePart.level == 1); + var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before"; + return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head) + } + + + // Determines whether an event happened in the gutter, and fires the + // handlers for the corresponding event. + function gutterEvent(cm, e, type, prevent) { + var mX, mY; + if (e.touches) { + mX = e.touches[0].clientX; + mY = e.touches[0].clientY; + } else { + try { mX = e.clientX; mY = e.clientY; } + catch(e$1) { return false } + } + if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false } + if (prevent) { e_preventDefault(e); } + + var display = cm.display; + var lineBox = display.lineDiv.getBoundingClientRect(); + + if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) } + mY -= lineBox.top - display.viewOffset; + + for (var i = 0; i < cm.display.gutterSpecs.length; ++i) { + var g = display.gutters.childNodes[i]; + if (g && g.getBoundingClientRect().right >= mX) { + var line = lineAtHeight(cm.doc, mY); + var gutter = cm.display.gutterSpecs[i]; + signal(cm, type, cm, line, gutter.className, e); + return e_defaultPrevented(e) + } + } + } + + function clickInGutter(cm, e) { + return gutterEvent(cm, e, "gutterClick", true) + } + + // CONTEXT MENU HANDLING + + // To make the context menu work, we need to briefly unhide the + // textarea (making it as unobtrusive as possible) to let the + // right-click take effect on it. + function onContextMenu(cm, e) { + if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return } + if (signalDOMEvent(cm, e, "contextmenu")) { return } + if (!captureRightClick) { cm.display.input.onContextMenu(e); } + } + + function contextMenuInGutter(cm, e) { + if (!hasHandler(cm, "gutterContextMenu")) { return false } + return gutterEvent(cm, e, "gutterContextMenu", false) + } + + function themeChanged(cm) { + cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") + + cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-"); + clearCaches(cm); + } + + var Init = {toString: function(){return "CodeMirror.Init"}}; + + var defaults = {}; + var optionHandlers = {}; + + function defineOptions(CodeMirror) { + var optionHandlers = CodeMirror.optionHandlers; + + function option(name, deflt, handle, notOnInit) { + CodeMirror.defaults[name] = deflt; + if (handle) { optionHandlers[name] = + notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; } + } + + CodeMirror.defineOption = option; + + // Passed to option handlers when there is no old value. + CodeMirror.Init = Init; + + // These two are, on init, called from the constructor because they + // have to be initialized before the editor can start at all. + option("value", "", function (cm, val) { return cm.setValue(val); }, true); + option("mode", null, function (cm, val) { + cm.doc.modeOption = val; + loadMode(cm); + }, true); + + option("indentUnit", 2, loadMode, true); + option("indentWithTabs", false); + option("smartIndent", true); + option("tabSize", 4, function (cm) { + resetModeState(cm); + clearCaches(cm); + regChange(cm); + }, true); + + option("lineSeparator", null, function (cm, val) { + cm.doc.lineSep = val; + if (!val) { return } + var newBreaks = [], lineNo = cm.doc.first; + cm.doc.iter(function (line) { + for (var pos = 0;;) { + var found = line.text.indexOf(val, pos); + if (found == -1) { break } + pos = found + val.length; + newBreaks.push(Pos(lineNo, found)); + } + lineNo++; + }); + for (var i = newBreaks.length - 1; i >= 0; i--) + { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); } + }); + option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g, function (cm, val, old) { + cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g"); + if (old != Init) { cm.refresh(); } + }); + option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true); + option("electricChars", true); + option("inputStyle", mobile ? "contenteditable" : "textarea", function () { + throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME + }, true); + option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true); + option("autocorrect", false, function (cm, val) { return cm.getInputField().autocorrect = val; }, true); + option("autocapitalize", false, function (cm, val) { return cm.getInputField().autocapitalize = val; }, true); + option("rtlMoveVisually", !windows); + option("wholeLineUpdateBefore", true); + + option("theme", "default", function (cm) { + themeChanged(cm); + updateGutters(cm); + }, true); + option("keyMap", "default", function (cm, val, old) { + var next = getKeyMap(val); + var prev = old != Init && getKeyMap(old); + if (prev && prev.detach) { prev.detach(cm, next); } + if (next.attach) { next.attach(cm, prev || null); } + }); + option("extraKeys", null); + option("configureMouse", null); + + option("lineWrapping", false, wrappingChanged, true); + option("gutters", [], function (cm, val) { + cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers); + updateGutters(cm); + }, true); + option("fixedGutter", true, function (cm, val) { + cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"; + cm.refresh(); + }, true); + option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true); + option("scrollbarStyle", "native", function (cm) { + initScrollbars(cm); + updateScrollbars(cm); + cm.display.scrollbars.setScrollTop(cm.doc.scrollTop); + cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft); + }, true); + option("lineNumbers", false, function (cm, val) { + cm.display.gutterSpecs = getGutters(cm.options.gutters, val); + updateGutters(cm); + }, true); + option("firstLineNumber", 1, updateGutters, true); + option("lineNumberFormatter", function (integer) { return integer; }, updateGutters, true); + option("showCursorWhenSelecting", false, updateSelection, true); + + option("resetSelectionOnContextMenu", true); + option("lineWiseCopyCut", true); + option("pasteLinesPerSelection", true); + option("selectionsMayTouch", false); + + option("readOnly", false, function (cm, val) { + if (val == "nocursor") { + onBlur(cm); + cm.display.input.blur(); + } + cm.display.input.readOnlyChanged(val); + }); + + option("screenReaderLabel", null, function (cm, val) { + val = (val === '') ? null : val; + cm.display.input.screenReaderLabelChanged(val); + }); + + option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true); + option("dragDrop", true, dragDropChanged); + option("allowDropFileTypes", null); + + option("cursorBlinkRate", 530); + option("cursorScrollMargin", 0); + option("cursorHeight", 1, updateSelection, true); + option("singleCursorHeightPerLine", true, updateSelection, true); + option("workTime", 100); + option("workDelay", 100); + option("flattenSpans", true, resetModeState, true); + option("addModeClass", false, resetModeState, true); + option("pollInterval", 100); + option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; }); + option("historyEventDelay", 1250); + option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true); + option("maxHighlightLength", 10000, resetModeState, true); + option("moveInputWithCursor", true, function (cm, val) { + if (!val) { cm.display.input.resetPosition(); } + }); + + option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; }); + option("autofocus", null); + option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true); + option("phrases", null); + } + + function dragDropChanged(cm, value, old) { + var wasOn = old && old != Init; + if (!value != !wasOn) { + var funcs = cm.display.dragFunctions; + var toggle = value ? on : off; + toggle(cm.display.scroller, "dragstart", funcs.start); + toggle(cm.display.scroller, "dragenter", funcs.enter); + toggle(cm.display.scroller, "dragover", funcs.over); + toggle(cm.display.scroller, "dragleave", funcs.leave); + toggle(cm.display.scroller, "drop", funcs.drop); + } + } + + function wrappingChanged(cm) { + if (cm.options.lineWrapping) { + addClass(cm.display.wrapper, "CodeMirror-wrap"); + cm.display.sizer.style.minWidth = ""; + cm.display.sizerWidth = null; + } else { + rmClass(cm.display.wrapper, "CodeMirror-wrap"); + findMaxLine(cm); + } + estimateLineHeights(cm); + regChange(cm); + clearCaches(cm); + setTimeout(function () { return updateScrollbars(cm); }, 100); + } + + // A CodeMirror instance represents an editor. This is the object + // that user code is usually dealing with. + + function CodeMirror(place, options) { + var this$1 = this; + + if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) } + + this.options = options = options ? copyObj(options) : {}; + // Determine effective options based on given values and defaults. + copyObj(defaults, options, false); + + var doc = options.value; + if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); } + else if (options.mode) { doc.modeOption = options.mode; } + this.doc = doc; + + var input = new CodeMirror.inputStyles[options.inputStyle](this); + var display = this.display = new Display(place, doc, input, options); + display.wrapper.CodeMirror = this; + themeChanged(this); + if (options.lineWrapping) + { this.display.wrapper.className += " CodeMirror-wrap"; } + initScrollbars(this); + + this.state = { + keyMaps: [], // stores maps added by addKeyMap + overlays: [], // highlighting overlays, as added by addOverlay + modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info + overwrite: false, + delayingBlurEvent: false, + focused: false, + suppressEdits: false, // used to disable editing during key handlers when in readOnly mode + pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll + selectingText: false, + draggingText: false, + highlight: new Delayed(), // stores highlight worker timeout + keySeq: null, // Unfinished key sequence + specialChars: null + }; + + if (options.autofocus && !mobile) { display.input.focus(); } + + // Override magic textarea content restore that IE sometimes does + // on our hidden textarea on reload + if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); } + + registerEventHandlers(this); + ensureGlobalHandlers(); + + startOperation(this); + this.curOp.forceUpdate = true; + attachDoc(this, doc); + + if ((options.autofocus && !mobile) || this.hasFocus()) + { setTimeout(function () { + if (this$1.hasFocus() && !this$1.state.focused) { onFocus(this$1); } + }, 20); } + else + { onBlur(this); } + + for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt)) + { optionHandlers[opt](this, options[opt], Init); } } + maybeUpdateLineNumberWidth(this); + if (options.finishInit) { options.finishInit(this); } + for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this); } + endOperation(this); + // Suppress optimizelegibility in Webkit, since it breaks text + // measuring on line wrapping boundaries. + if (webkit && options.lineWrapping && + getComputedStyle(display.lineDiv).textRendering == "optimizelegibility") + { display.lineDiv.style.textRendering = "auto"; } + } + + // The default configuration options. + CodeMirror.defaults = defaults; + // Functions to run when options are changed. + CodeMirror.optionHandlers = optionHandlers; + + // Attach the necessary event handlers when initializing the editor + function registerEventHandlers(cm) { + var d = cm.display; + on(d.scroller, "mousedown", operation(cm, onMouseDown)); + // Older IE's will not fire a second mousedown for a double click + if (ie && ie_version < 11) + { on(d.scroller, "dblclick", operation(cm, function (e) { + if (signalDOMEvent(cm, e)) { return } + var pos = posFromMouse(cm, e); + if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return } + e_preventDefault(e); + var word = cm.findWordAt(pos); + extendSelection(cm.doc, word.anchor, word.head); + })); } + else + { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); } + // Some browsers fire contextmenu *after* opening the menu, at + // which point we can't mess with it anymore. Context menu is + // handled in onMouseDown for these browsers. + on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }); + on(d.input.getField(), "contextmenu", function (e) { + if (!d.scroller.contains(e.target)) { onContextMenu(cm, e); } + }); + + // Used to suppress mouse event handling when a touch happens + var touchFinished, prevTouch = {end: 0}; + function finishTouch() { + if (d.activeTouch) { + touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000); + prevTouch = d.activeTouch; + prevTouch.end = +new Date; + } + } + function isMouseLikeTouchEvent(e) { + if (e.touches.length != 1) { return false } + var touch = e.touches[0]; + return touch.radiusX <= 1 && touch.radiusY <= 1 + } + function farAway(touch, other) { + if (other.left == null) { return true } + var dx = other.left - touch.left, dy = other.top - touch.top; + return dx * dx + dy * dy > 20 * 20 + } + on(d.scroller, "touchstart", function (e) { + if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) { + d.input.ensurePolled(); + clearTimeout(touchFinished); + var now = +new Date; + d.activeTouch = {start: now, moved: false, + prev: now - prevTouch.end <= 300 ? prevTouch : null}; + if (e.touches.length == 1) { + d.activeTouch.left = e.touches[0].pageX; + d.activeTouch.top = e.touches[0].pageY; + } + } + }); + on(d.scroller, "touchmove", function () { + if (d.activeTouch) { d.activeTouch.moved = true; } + }); + on(d.scroller, "touchend", function (e) { + var touch = d.activeTouch; + if (touch && !eventInWidget(d, e) && touch.left != null && + !touch.moved && new Date - touch.start < 300) { + var pos = cm.coordsChar(d.activeTouch, "page"), range; + if (!touch.prev || farAway(touch, touch.prev)) // Single tap + { range = new Range(pos, pos); } + else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap + { range = cm.findWordAt(pos); } + else // Triple tap + { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); } + cm.setSelection(range.anchor, range.head); + cm.focus(); + e_preventDefault(e); + } + finishTouch(); + }); + on(d.scroller, "touchcancel", finishTouch); + + // Sync scrolling between fake scrollbars and real scrollable + // area, ensure viewport is updated when scrolling. + on(d.scroller, "scroll", function () { + if (d.scroller.clientHeight) { + updateScrollTop(cm, d.scroller.scrollTop); + setScrollLeft(cm, d.scroller.scrollLeft, true); + signal(cm, "scroll", cm); + } + }); + + // Listen to wheel events in order to try and update the viewport on time. + on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); }); + on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); }); + + // Prevent wrapper from ever scrolling + on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); + + d.dragFunctions = { + enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }}, + over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }}, + start: function (e) { return onDragStart(cm, e); }, + drop: operation(cm, onDrop), + leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }} + }; + + var inp = d.input.getField(); + on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); }); + on(inp, "keydown", operation(cm, onKeyDown)); + on(inp, "keypress", operation(cm, onKeyPress)); + on(inp, "focus", function (e) { return onFocus(cm, e); }); + on(inp, "blur", function (e) { return onBlur(cm, e); }); + } + + var initHooks = []; + CodeMirror.defineInitHook = function (f) { return initHooks.push(f); }; + + // Indent the given line. The how parameter can be "smart", + // "add"/null, "subtract", or "prev". When aggressive is false + // (typically set to true for forced single-line indents), empty + // lines are not indented, and places where the mode returns Pass + // are left alone. + function indentLine(cm, n, how, aggressive) { + var doc = cm.doc, state; + if (how == null) { how = "add"; } + if (how == "smart") { + // Fall back to "prev" when the mode doesn't have an indentation + // method. + if (!doc.mode.indent) { how = "prev"; } + else { state = getContextBefore(cm, n).state; } + } + + var tabSize = cm.options.tabSize; + var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize); + if (line.stateAfter) { line.stateAfter = null; } + var curSpaceString = line.text.match(/^\s*/)[0], indentation; + if (!aggressive && !/\S/.test(line.text)) { + indentation = 0; + how = "not"; + } else if (how == "smart") { + indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text); + if (indentation == Pass || indentation > 150) { + if (!aggressive) { return } + how = "prev"; + } + } + if (how == "prev") { + if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); } + else { indentation = 0; } + } else if (how == "add") { + indentation = curSpace + cm.options.indentUnit; + } else if (how == "subtract") { + indentation = curSpace - cm.options.indentUnit; + } else if (typeof how == "number") { + indentation = curSpace + how; + } + indentation = Math.max(0, indentation); + + var indentString = "", pos = 0; + if (cm.options.indentWithTabs) + { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} } + if (pos < indentation) { indentString += spaceStr(indentation - pos); } + + if (indentString != curSpaceString) { + replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); + line.stateAfter = null; + return true + } else { + // Ensure that, if the cursor was in the whitespace at the start + // of the line, it is moved to the end of that space. + for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) { + var range = doc.sel.ranges[i$1]; + if (range.head.line == n && range.head.ch < curSpaceString.length) { + var pos$1 = Pos(n, curSpaceString.length); + replaceOneSelection(doc, i$1, new Range(pos$1, pos$1)); + break + } + } + } + } + + // This will be set to a {lineWise: bool, text: [string]} object, so + // that, when pasting, we know what kind of selections the copied + // text was made out of. + var lastCopied = null; + + function setLastCopied(newLastCopied) { + lastCopied = newLastCopied; + } + + function applyTextInput(cm, inserted, deleted, sel, origin) { + var doc = cm.doc; + cm.display.shift = false; + if (!sel) { sel = doc.sel; } + + var recent = +new Date - 200; + var paste = origin == "paste" || cm.state.pasteIncoming > recent; + var textLines = splitLinesAuto(inserted), multiPaste = null; + // When pasting N lines into N selections, insert one line per selection + if (paste && sel.ranges.length > 1) { + if (lastCopied && lastCopied.text.join("\n") == inserted) { + if (sel.ranges.length % lastCopied.text.length == 0) { + multiPaste = []; + for (var i = 0; i < lastCopied.text.length; i++) + { multiPaste.push(doc.splitLines(lastCopied.text[i])); } + } + } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) { + multiPaste = map(textLines, function (l) { return [l]; }); + } + } + + var updateInput = cm.curOp.updateInput; + // Normal behavior is to insert the new text into every selection + for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) { + var range = sel.ranges[i$1]; + var from = range.from(), to = range.to(); + if (range.empty()) { + if (deleted && deleted > 0) // Handle deletion + { from = Pos(from.line, from.ch - deleted); } + else if (cm.state.overwrite && !paste) // Handle overwrite + { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); } + else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == textLines.join("\n")) + { from = to = Pos(from.line, 0); } + } + var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines, + origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")}; + makeChange(cm.doc, changeEvent); + signalLater(cm, "inputRead", cm, changeEvent); + } + if (inserted && !paste) + { triggerElectric(cm, inserted); } + + ensureCursorVisible(cm); + if (cm.curOp.updateInput < 2) { cm.curOp.updateInput = updateInput; } + cm.curOp.typing = true; + cm.state.pasteIncoming = cm.state.cutIncoming = -1; + } + + function handlePaste(e, cm) { + var pasted = e.clipboardData && e.clipboardData.getData("Text"); + if (pasted) { + e.preventDefault(); + if (!cm.isReadOnly() && !cm.options.disableInput) + { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); } + return true + } + } + + function triggerElectric(cm, inserted) { + // When an 'electric' character is inserted, immediately trigger a reindent + if (!cm.options.electricChars || !cm.options.smartIndent) { return } + var sel = cm.doc.sel; + + for (var i = sel.ranges.length - 1; i >= 0; i--) { + var range = sel.ranges[i]; + if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) { continue } + var mode = cm.getModeAt(range.head); + var indented = false; + if (mode.electricChars) { + for (var j = 0; j < mode.electricChars.length; j++) + { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) { + indented = indentLine(cm, range.head.line, "smart"); + break + } } + } else if (mode.electricInput) { + if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch))) + { indented = indentLine(cm, range.head.line, "smart"); } + } + if (indented) { signalLater(cm, "electricInput", cm, range.head.line); } + } + } + + function copyableRanges(cm) { + var text = [], ranges = []; + for (var i = 0; i < cm.doc.sel.ranges.length; i++) { + var line = cm.doc.sel.ranges[i].head.line; + var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}; + ranges.push(lineRange); + text.push(cm.getRange(lineRange.anchor, lineRange.head)); + } + return {text: text, ranges: ranges} + } + + function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) { + field.setAttribute("autocorrect", autocorrect ? "" : "off"); + field.setAttribute("autocapitalize", autocapitalize ? "" : "off"); + field.setAttribute("spellcheck", !!spellcheck); + } + + function hiddenTextarea() { + var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none"); + var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); + // The textarea is kept positioned near the cursor to prevent the + // fact that it'll be scrolled into view on input from scrolling + // our fake cursor out of view. On webkit, when wrap=off, paste is + // very slow. So make the area wide instead. + if (webkit) { te.style.width = "1000px"; } + else { te.setAttribute("wrap", "off"); } + // If border: 0; -- iOS fails to open keyboard (issue #1287) + if (ios) { te.style.border = "1px solid black"; } + disableBrowserMagic(te); + return div + } + + // The publicly visible API. Note that methodOp(f) means + // 'wrap f in an operation, performed on its `this` parameter'. + + // This is not the complete set of editor methods. Most of the + // methods defined on the Doc type are also injected into + // CodeMirror.prototype, for backwards compatibility and + // convenience. + + function addEditorMethods(CodeMirror) { + var optionHandlers = CodeMirror.optionHandlers; + + var helpers = CodeMirror.helpers = {}; + + CodeMirror.prototype = { + constructor: CodeMirror, + focus: function(){window.focus(); this.display.input.focus();}, + + setOption: function(option, value) { + var options = this.options, old = options[option]; + if (options[option] == value && option != "mode") { return } + options[option] = value; + if (optionHandlers.hasOwnProperty(option)) + { operation(this, optionHandlers[option])(this, value, old); } + signal(this, "optionChange", this, option); + }, + + getOption: function(option) {return this.options[option]}, + getDoc: function() {return this.doc}, + + addKeyMap: function(map, bottom) { + this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map)); + }, + removeKeyMap: function(map) { + var maps = this.state.keyMaps; + for (var i = 0; i < maps.length; ++i) + { if (maps[i] == map || maps[i].name == map) { + maps.splice(i, 1); + return true + } } + }, + + addOverlay: methodOp(function(spec, options) { + var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec); + if (mode.startState) { throw new Error("Overlays may not be stateful.") } + insertSorted(this.state.overlays, + {mode: mode, modeSpec: spec, opaque: options && options.opaque, + priority: (options && options.priority) || 0}, + function (overlay) { return overlay.priority; }); + this.state.modeGen++; + regChange(this); + }), + removeOverlay: methodOp(function(spec) { + var overlays = this.state.overlays; + for (var i = 0; i < overlays.length; ++i) { + var cur = overlays[i].modeSpec; + if (cur == spec || typeof spec == "string" && cur.name == spec) { + overlays.splice(i, 1); + this.state.modeGen++; + regChange(this); + return + } + } + }), + + indentLine: methodOp(function(n, dir, aggressive) { + if (typeof dir != "string" && typeof dir != "number") { + if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; } + else { dir = dir ? "add" : "subtract"; } + } + if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); } + }), + indentSelection: methodOp(function(how) { + var ranges = this.doc.sel.ranges, end = -1; + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i]; + if (!range.empty()) { + var from = range.from(), to = range.to(); + var start = Math.max(end, from.line); + end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; + for (var j = start; j < end; ++j) + { indentLine(this, j, how); } + var newRanges = this.doc.sel.ranges; + if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0) + { replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); } + } else if (range.head.line > end) { + indentLine(this, range.head.line, how, true); + end = range.head.line; + if (i == this.doc.sel.primIndex) { ensureCursorVisible(this); } + } + } + }), + + // Fetch the parser token for a given character. Useful for hacks + // that want to inspect the mode state (say, for completion). + getTokenAt: function(pos, precise) { + return takeToken(this, pos, precise) + }, + + getLineTokens: function(line, precise) { + return takeToken(this, Pos(line), precise, true) + }, + + getTokenTypeAt: function(pos) { + pos = clipPos(this.doc, pos); + var styles = getLineStyles(this, getLine(this.doc, pos.line)); + var before = 0, after = (styles.length - 1) / 2, ch = pos.ch; + var type; + if (ch == 0) { type = styles[2]; } + else { for (;;) { + var mid = (before + after) >> 1; + if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; } + else if (styles[mid * 2 + 1] < ch) { before = mid + 1; } + else { type = styles[mid * 2 + 2]; break } + } } + var cut = type ? type.indexOf("overlay ") : -1; + return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1) + }, + + getModeAt: function(pos) { + var mode = this.doc.mode; + if (!mode.innerMode) { return mode } + return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode + }, + + getHelper: function(pos, type) { + return this.getHelpers(pos, type)[0] + }, + + getHelpers: function(pos, type) { + var found = []; + if (!helpers.hasOwnProperty(type)) { return found } + var help = helpers[type], mode = this.getModeAt(pos); + if (typeof mode[type] == "string") { + if (help[mode[type]]) { found.push(help[mode[type]]); } + } else if (mode[type]) { + for (var i = 0; i < mode[type].length; i++) { + var val = help[mode[type][i]]; + if (val) { found.push(val); } + } + } else if (mode.helperType && help[mode.helperType]) { + found.push(help[mode.helperType]); + } else if (help[mode.name]) { + found.push(help[mode.name]); + } + for (var i$1 = 0; i$1 < help._global.length; i$1++) { + var cur = help._global[i$1]; + if (cur.pred(mode, this) && indexOf(found, cur.val) == -1) + { found.push(cur.val); } + } + return found + }, + + getStateAfter: function(line, precise) { + var doc = this.doc; + line = clipLine(doc, line == null ? doc.first + doc.size - 1: line); + return getContextBefore(this, line + 1, precise).state + }, + + cursorCoords: function(start, mode) { + var pos, range = this.doc.sel.primary(); + if (start == null) { pos = range.head; } + else if (typeof start == "object") { pos = clipPos(this.doc, start); } + else { pos = start ? range.from() : range.to(); } + return cursorCoords(this, pos, mode || "page") + }, + + charCoords: function(pos, mode) { + return charCoords(this, clipPos(this.doc, pos), mode || "page") + }, + + coordsChar: function(coords, mode) { + coords = fromCoordSystem(this, coords, mode || "page"); + return coordsChar(this, coords.left, coords.top) + }, + + lineAtHeight: function(height, mode) { + height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top; + return lineAtHeight(this.doc, height + this.display.viewOffset) + }, + heightAtLine: function(line, mode, includeWidgets) { + var end = false, lineObj; + if (typeof line == "number") { + var last = this.doc.first + this.doc.size - 1; + if (line < this.doc.first) { line = this.doc.first; } + else if (line > last) { line = last; end = true; } + lineObj = getLine(this.doc, line); + } else { + lineObj = line; + } + return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top + + (end ? this.doc.height - heightAtLine(lineObj) : 0) + }, + + defaultTextHeight: function() { return textHeight(this.display) }, + defaultCharWidth: function() { return charWidth(this.display) }, + + getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}}, + + addWidget: function(pos, node, scroll, vert, horiz) { + var display = this.display; + pos = cursorCoords(this, clipPos(this.doc, pos)); + var top = pos.bottom, left = pos.left; + node.style.position = "absolute"; + node.setAttribute("cm-ignore-events", "true"); + this.display.input.setUneditable(node); + display.sizer.appendChild(node); + if (vert == "over") { + top = pos.top; + } else if (vert == "above" || vert == "near") { + var vspace = Math.max(display.wrapper.clientHeight, this.doc.height), + hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth); + // Default to positioning above (if specified and possible); otherwise default to positioning below + if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight) + { top = pos.top - node.offsetHeight; } + else if (pos.bottom + node.offsetHeight <= vspace) + { top = pos.bottom; } + if (left + node.offsetWidth > hspace) + { left = hspace - node.offsetWidth; } + } + node.style.top = top + "px"; + node.style.left = node.style.right = ""; + if (horiz == "right") { + left = display.sizer.clientWidth - node.offsetWidth; + node.style.right = "0px"; + } else { + if (horiz == "left") { left = 0; } + else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; } + node.style.left = left + "px"; + } + if (scroll) + { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); } + }, + + triggerOnKeyDown: methodOp(onKeyDown), + triggerOnKeyPress: methodOp(onKeyPress), + triggerOnKeyUp: onKeyUp, + triggerOnMouseDown: methodOp(onMouseDown), + + execCommand: function(cmd) { + if (commands.hasOwnProperty(cmd)) + { return commands[cmd].call(null, this) } + }, + + triggerElectric: methodOp(function(text) { triggerElectric(this, text); }), + + findPosH: function(from, amount, unit, visually) { + var dir = 1; + if (amount < 0) { dir = -1; amount = -amount; } + var cur = clipPos(this.doc, from); + for (var i = 0; i < amount; ++i) { + cur = findPosH(this.doc, cur, dir, unit, visually); + if (cur.hitSide) { break } + } + return cur + }, + + moveH: methodOp(function(dir, unit) { + var this$1 = this; + + this.extendSelectionsBy(function (range) { + if (this$1.display.shift || this$1.doc.extend || range.empty()) + { return findPosH(this$1.doc, range.head, dir, unit, this$1.options.rtlMoveVisually) } + else + { return dir < 0 ? range.from() : range.to() } + }, sel_move); + }), + + deleteH: methodOp(function(dir, unit) { + var sel = this.doc.sel, doc = this.doc; + if (sel.somethingSelected()) + { doc.replaceSelection("", null, "+delete"); } + else + { deleteNearSelection(this, function (range) { + var other = findPosH(doc, range.head, dir, unit, false); + return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other} + }); } + }), + + findPosV: function(from, amount, unit, goalColumn) { + var dir = 1, x = goalColumn; + if (amount < 0) { dir = -1; amount = -amount; } + var cur = clipPos(this.doc, from); + for (var i = 0; i < amount; ++i) { + var coords = cursorCoords(this, cur, "div"); + if (x == null) { x = coords.left; } + else { coords.left = x; } + cur = findPosV(this, coords, dir, unit); + if (cur.hitSide) { break } + } + return cur + }, + + moveV: methodOp(function(dir, unit) { + var this$1 = this; + + var doc = this.doc, goals = []; + var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected(); + doc.extendSelectionsBy(function (range) { + if (collapse) + { return dir < 0 ? range.from() : range.to() } + var headPos = cursorCoords(this$1, range.head, "div"); + if (range.goalColumn != null) { headPos.left = range.goalColumn; } + goals.push(headPos.left); + var pos = findPosV(this$1, headPos, dir, unit); + if (unit == "page" && range == doc.sel.primary()) + { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); } + return pos + }, sel_move); + if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++) + { doc.sel.ranges[i].goalColumn = goals[i]; } } + }), + + // Find the word at the given position (as returned by coordsChar). + findWordAt: function(pos) { + var doc = this.doc, line = getLine(doc, pos.line).text; + var start = pos.ch, end = pos.ch; + if (line) { + var helper = this.getHelper(pos, "wordChars"); + if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; } + var startChar = line.charAt(start); + var check = isWordChar(startChar, helper) + ? function (ch) { return isWordChar(ch, helper); } + : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); } + : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); }; + while (start > 0 && check(line.charAt(start - 1))) { --start; } + while (end < line.length && check(line.charAt(end))) { ++end; } + } + return new Range(Pos(pos.line, start), Pos(pos.line, end)) + }, + + toggleOverwrite: function(value) { + if (value != null && value == this.state.overwrite) { return } + if (this.state.overwrite = !this.state.overwrite) + { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); } + else + { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); } + + signal(this, "overwriteToggle", this, this.state.overwrite); + }, + hasFocus: function() { return this.display.input.getField() == activeElt() }, + isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) }, + + scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }), + getScrollInfo: function() { + var scroller = this.display.scroller; + return {left: scroller.scrollLeft, top: scroller.scrollTop, + height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight, + width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth, + clientHeight: displayHeight(this), clientWidth: displayWidth(this)} + }, + + scrollIntoView: methodOp(function(range, margin) { + if (range == null) { + range = {from: this.doc.sel.primary().head, to: null}; + if (margin == null) { margin = this.options.cursorScrollMargin; } + } else if (typeof range == "number") { + range = {from: Pos(range, 0), to: null}; + } else if (range.from == null) { + range = {from: range, to: null}; + } + if (!range.to) { range.to = range.from; } + range.margin = margin || 0; + + if (range.from.line != null) { + scrollToRange(this, range); + } else { + scrollToCoordsRange(this, range.from, range.to, range.margin); + } + }), + + setSize: methodOp(function(width, height) { + var this$1 = this; + + var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; }; + if (width != null) { this.display.wrapper.style.width = interpret(width); } + if (height != null) { this.display.wrapper.style.height = interpret(height); } + if (this.options.lineWrapping) { clearLineMeasurementCache(this); } + var lineNo = this.display.viewFrom; + this.doc.iter(lineNo, this.display.viewTo, function (line) { + if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) + { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo, "widget"); break } } } + ++lineNo; + }); + this.curOp.forceUpdate = true; + signal(this, "refresh", this); + }), + + operation: function(f){return runInOp(this, f)}, + startOperation: function(){return startOperation(this)}, + endOperation: function(){return endOperation(this)}, + + refresh: methodOp(function() { + var oldHeight = this.display.cachedTextHeight; + regChange(this); + this.curOp.forceUpdate = true; + clearCaches(this); + scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop); + updateGutterSpace(this.display); + if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5 || this.options.lineWrapping) + { estimateLineHeights(this); } + signal(this, "refresh", this); + }), + + swapDoc: methodOp(function(doc) { + var old = this.doc; + old.cm = null; + // Cancel the current text selection if any (#5821) + if (this.state.selectingText) { this.state.selectingText(); } + attachDoc(this, doc); + clearCaches(this); + this.display.input.reset(); + scrollToCoords(this, doc.scrollLeft, doc.scrollTop); + this.curOp.forceScroll = true; + signalLater(this, "swapDoc", this, old); + return old + }), + + phrase: function(phraseText) { + var phrases = this.options.phrases; + return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText + }, + + getInputField: function(){return this.display.input.getField()}, + getWrapperElement: function(){return this.display.wrapper}, + getScrollerElement: function(){return this.display.scroller}, + getGutterElement: function(){return this.display.gutters} + }; + eventMixin(CodeMirror); + + CodeMirror.registerHelper = function(type, name, value) { + if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; } + helpers[type][name] = value; + }; + CodeMirror.registerGlobalHelper = function(type, name, predicate, value) { + CodeMirror.registerHelper(type, name, value); + helpers[type]._global.push({pred: predicate, val: value}); + }; + } + + // Used for horizontal relative motion. Dir is -1 or 1 (left or + // right), unit can be "codepoint", "char", "column" (like char, but + // doesn't cross line boundaries), "word" (across next word), or + // "group" (to the start of next group of word or + // non-word-non-whitespace chars). The visually param controls + // whether, in right-to-left text, direction 1 means to move towards + // the next index in the string, or towards the character to the right + // of the current position. The resulting position will have a + // hitSide=true property if it reached the end of the document. + function findPosH(doc, pos, dir, unit, visually) { + var oldPos = pos; + var origDir = dir; + var lineObj = getLine(doc, pos.line); + var lineDir = visually && doc.direction == "rtl" ? -dir : dir; + function findNextLine() { + var l = pos.line + lineDir; + if (l < doc.first || l >= doc.first + doc.size) { return false } + pos = new Pos(l, pos.ch, pos.sticky); + return lineObj = getLine(doc, l) + } + function moveOnce(boundToLine) { + var next; + if (unit == "codepoint") { + var ch = lineObj.text.charCodeAt(pos.ch + (dir > 0 ? 0 : -1)); + if (isNaN(ch)) { + next = null; + } else { + var astral = dir > 0 ? ch >= 0xD800 && ch < 0xDC00 : ch >= 0xDC00 && ch < 0xDFFF; + next = new Pos(pos.line, Math.max(0, Math.min(lineObj.text.length, pos.ch + dir * (astral ? 2 : 1))), -dir); + } + } else if (visually) { + next = moveVisually(doc.cm, lineObj, pos, dir); + } else { + next = moveLogically(lineObj, pos, dir); + } + if (next == null) { + if (!boundToLine && findNextLine()) + { pos = endOfLine(visually, doc.cm, lineObj, pos.line, lineDir); } + else + { return false } + } else { + pos = next; + } + return true + } + + if (unit == "char" || unit == "codepoint") { + moveOnce(); + } else if (unit == "column") { + moveOnce(true); + } else if (unit == "word" || unit == "group") { + var sawType = null, group = unit == "group"; + var helper = doc.cm && doc.cm.getHelper(pos, "wordChars"); + for (var first = true;; first = false) { + if (dir < 0 && !moveOnce(!first)) { break } + var cur = lineObj.text.charAt(pos.ch) || "\n"; + var type = isWordChar(cur, helper) ? "w" + : group && cur == "\n" ? "n" + : !group || /\s/.test(cur) ? null + : "p"; + if (group && !first && !type) { type = "s"; } + if (sawType && sawType != type) { + if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";} + break + } + + if (type) { sawType = type; } + if (dir > 0 && !moveOnce(!first)) { break } + } + } + var result = skipAtomic(doc, pos, oldPos, origDir, true); + if (equalCursorPos(oldPos, result)) { result.hitSide = true; } + return result + } + + // For relative vertical movement. Dir may be -1 or 1. Unit can be + // "page" or "line". The resulting position will have a hitSide=true + // property if it reached the end of the document. + function findPosV(cm, pos, dir, unit) { + var doc = cm.doc, x = pos.left, y; + if (unit == "page") { + var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight); + var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3); + y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount; + } else if (unit == "line") { + y = dir > 0 ? pos.bottom + 3 : pos.top - 3; + } + var target; + for (;;) { + target = coordsChar(cm, x, y); + if (!target.outside) { break } + if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break } + y += dir * 5; + } + return target + } + + // CONTENTEDITABLE INPUT STYLE + + var ContentEditableInput = function(cm) { + this.cm = cm; + this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null; + this.polling = new Delayed(); + this.composing = null; + this.gracePeriod = false; + this.readDOMTimeout = null; + }; + + ContentEditableInput.prototype.init = function (display) { + var this$1 = this; + + var input = this, cm = input.cm; + var div = input.div = display.lineDiv; + div.contentEditable = true; + disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize); + + function belongsToInput(e) { + for (var t = e.target; t; t = t.parentNode) { + if (t == div) { return true } + if (/\bCodeMirror-(?:line)?widget\b/.test(t.className)) { break } + } + return false + } + + on(div, "paste", function (e) { + if (!belongsToInput(e) || signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } + // IE doesn't fire input events, so we schedule a read for the pasted content in this way + if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); } + }); + + on(div, "compositionstart", function (e) { + this$1.composing = {data: e.data, done: false}; + }); + on(div, "compositionupdate", function (e) { + if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; } + }); + on(div, "compositionend", function (e) { + if (this$1.composing) { + if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); } + this$1.composing.done = true; + } + }); + + on(div, "touchstart", function () { return input.forceCompositionEnd(); }); + + on(div, "input", function () { + if (!this$1.composing) { this$1.readFromDOMSoon(); } + }); + + function onCopyCut(e) { + if (!belongsToInput(e) || signalDOMEvent(cm, e)) { return } + if (cm.somethingSelected()) { + setLastCopied({lineWise: false, text: cm.getSelections()}); + if (e.type == "cut") { cm.replaceSelection("", null, "cut"); } + } else if (!cm.options.lineWiseCopyCut) { + return + } else { + var ranges = copyableRanges(cm); + setLastCopied({lineWise: true, text: ranges.text}); + if (e.type == "cut") { + cm.operation(function () { + cm.setSelections(ranges.ranges, 0, sel_dontScroll); + cm.replaceSelection("", null, "cut"); + }); + } + } + if (e.clipboardData) { + e.clipboardData.clearData(); + var content = lastCopied.text.join("\n"); + // iOS exposes the clipboard API, but seems to discard content inserted into it + e.clipboardData.setData("Text", content); + if (e.clipboardData.getData("Text") == content) { + e.preventDefault(); + return + } + } + // Old-fashioned briefly-focus-a-textarea hack + var kludge = hiddenTextarea(), te = kludge.firstChild; + cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild); + te.value = lastCopied.text.join("\n"); + var hadFocus = activeElt(); + selectInput(te); + setTimeout(function () { + cm.display.lineSpace.removeChild(kludge); + hadFocus.focus(); + if (hadFocus == div) { input.showPrimarySelection(); } + }, 50); + } + on(div, "copy", onCopyCut); + on(div, "cut", onCopyCut); + }; + + ContentEditableInput.prototype.screenReaderLabelChanged = function (label) { + // Label for screenreaders, accessibility + if(label) { + this.div.setAttribute('aria-label', label); + } else { + this.div.removeAttribute('aria-label'); + } + }; + + ContentEditableInput.prototype.prepareSelection = function () { + var result = prepareSelection(this.cm, false); + result.focus = activeElt() == this.div; + return result + }; + + ContentEditableInput.prototype.showSelection = function (info, takeFocus) { + if (!info || !this.cm.display.view.length) { return } + if (info.focus || takeFocus) { this.showPrimarySelection(); } + this.showMultipleSelections(info); + }; + + ContentEditableInput.prototype.getSelection = function () { + return this.cm.display.wrapper.ownerDocument.getSelection() + }; + + ContentEditableInput.prototype.showPrimarySelection = function () { + var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary(); + var from = prim.from(), to = prim.to(); + + if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) { + sel.removeAllRanges(); + return + } + + var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); + var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset); + if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad && + cmp(minPos(curAnchor, curFocus), from) == 0 && + cmp(maxPos(curAnchor, curFocus), to) == 0) + { return } + + var view = cm.display.view; + var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) || + {node: view[0].measure.map[2], offset: 0}; + var end = to.line < cm.display.viewTo && posToDOM(cm, to); + if (!end) { + var measure = view[view.length - 1].measure; + var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map; + end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]}; + } + + if (!start || !end) { + sel.removeAllRanges(); + return + } + + var old = sel.rangeCount && sel.getRangeAt(0), rng; + try { rng = range(start.node, start.offset, end.offset, end.node); } + catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible + if (rng) { + if (!gecko && cm.state.focused) { + sel.collapse(start.node, start.offset); + if (!rng.collapsed) { + sel.removeAllRanges(); + sel.addRange(rng); + } + } else { + sel.removeAllRanges(); + sel.addRange(rng); + } + if (old && sel.anchorNode == null) { sel.addRange(old); } + else if (gecko) { this.startGracePeriod(); } + } + this.rememberSelection(); + }; + + ContentEditableInput.prototype.startGracePeriod = function () { + var this$1 = this; + + clearTimeout(this.gracePeriod); + this.gracePeriod = setTimeout(function () { + this$1.gracePeriod = false; + if (this$1.selectionChanged()) + { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); } + }, 20); + }; + + ContentEditableInput.prototype.showMultipleSelections = function (info) { + removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors); + removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection); + }; + + ContentEditableInput.prototype.rememberSelection = function () { + var sel = this.getSelection(); + this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset; + this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset; + }; + + ContentEditableInput.prototype.selectionInEditor = function () { + var sel = this.getSelection(); + if (!sel.rangeCount) { return false } + var node = sel.getRangeAt(0).commonAncestorContainer; + return contains(this.div, node) + }; + + ContentEditableInput.prototype.focus = function () { + if (this.cm.options.readOnly != "nocursor") { + if (!this.selectionInEditor() || activeElt() != this.div) + { this.showSelection(this.prepareSelection(), true); } + this.div.focus(); + } + }; + ContentEditableInput.prototype.blur = function () { this.div.blur(); }; + ContentEditableInput.prototype.getField = function () { return this.div }; + + ContentEditableInput.prototype.supportsTouch = function () { return true }; + + ContentEditableInput.prototype.receivedFocus = function () { + var input = this; + if (this.selectionInEditor()) + { this.pollSelection(); } + else + { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); } + + function poll() { + if (input.cm.state.focused) { + input.pollSelection(); + input.polling.set(input.cm.options.pollInterval, poll); + } + } + this.polling.set(this.cm.options.pollInterval, poll); + }; + + ContentEditableInput.prototype.selectionChanged = function () { + var sel = this.getSelection(); + return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset || + sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset + }; + + ContentEditableInput.prototype.pollSelection = function () { + if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return } + var sel = this.getSelection(), cm = this.cm; + // On Android Chrome (version 56, at least), backspacing into an + // uneditable block element will put the cursor in that element, + // and then, because it's not editable, hide the virtual keyboard. + // Because Android doesn't allow us to actually detect backspace + // presses in a sane way, this code checks for when that happens + // and simulates a backspace press in this case. + if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) { + this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs}); + this.blur(); + this.focus(); + return + } + if (this.composing) { return } + this.rememberSelection(); + var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); + var head = domToPos(cm, sel.focusNode, sel.focusOffset); + if (anchor && head) { runInOp(cm, function () { + setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll); + if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; } + }); } + }; + + ContentEditableInput.prototype.pollContent = function () { + if (this.readDOMTimeout != null) { + clearTimeout(this.readDOMTimeout); + this.readDOMTimeout = null; + } + + var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary(); + var from = sel.from(), to = sel.to(); + if (from.ch == 0 && from.line > cm.firstLine()) + { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); } + if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine()) + { to = Pos(to.line + 1, 0); } + if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false } + + var fromIndex, fromLine, fromNode; + if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) { + fromLine = lineNo(display.view[0].line); + fromNode = display.view[0].node; + } else { + fromLine = lineNo(display.view[fromIndex].line); + fromNode = display.view[fromIndex - 1].node.nextSibling; + } + var toIndex = findViewIndex(cm, to.line); + var toLine, toNode; + if (toIndex == display.view.length - 1) { + toLine = display.viewTo - 1; + toNode = display.lineDiv.lastChild; + } else { + toLine = lineNo(display.view[toIndex + 1].line) - 1; + toNode = display.view[toIndex + 1].node.previousSibling; + } + + if (!fromNode) { return false } + var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine)); + var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length)); + while (newText.length > 1 && oldText.length > 1) { + if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; } + else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; } + else { break } + } + + var cutFront = 0, cutEnd = 0; + var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length); + while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront)) + { ++cutFront; } + var newBot = lst(newText), oldBot = lst(oldText); + var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0), + oldBot.length - (oldText.length == 1 ? cutFront : 0)); + while (cutEnd < maxCutEnd && + newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) + { ++cutEnd; } + // Try to move start of change to start of selection if ambiguous + if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) { + while (cutFront && cutFront > from.ch && + newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) { + cutFront--; + cutEnd++; + } + } + + newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, ""); + newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, ""); + + var chFrom = Pos(fromLine, cutFront); + var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0); + if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) { + replaceRange(cm.doc, newText, chFrom, chTo, "+input"); + return true + } + }; + + ContentEditableInput.prototype.ensurePolled = function () { + this.forceCompositionEnd(); + }; + ContentEditableInput.prototype.reset = function () { + this.forceCompositionEnd(); + }; + ContentEditableInput.prototype.forceCompositionEnd = function () { + if (!this.composing) { return } + clearTimeout(this.readDOMTimeout); + this.composing = null; + this.updateFromDOM(); + this.div.blur(); + this.div.focus(); + }; + ContentEditableInput.prototype.readFromDOMSoon = function () { + var this$1 = this; + + if (this.readDOMTimeout != null) { return } + this.readDOMTimeout = setTimeout(function () { + this$1.readDOMTimeout = null; + if (this$1.composing) { + if (this$1.composing.done) { this$1.composing = null; } + else { return } + } + this$1.updateFromDOM(); + }, 80); + }; + + ContentEditableInput.prototype.updateFromDOM = function () { + var this$1 = this; + + if (this.cm.isReadOnly() || !this.pollContent()) + { runInOp(this.cm, function () { return regChange(this$1.cm); }); } + }; + + ContentEditableInput.prototype.setUneditable = function (node) { + node.contentEditable = "false"; + }; + + ContentEditableInput.prototype.onKeyPress = function (e) { + if (e.charCode == 0 || this.composing) { return } + e.preventDefault(); + if (!this.cm.isReadOnly()) + { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); } + }; + + ContentEditableInput.prototype.readOnlyChanged = function (val) { + this.div.contentEditable = String(val != "nocursor"); + }; + + ContentEditableInput.prototype.onContextMenu = function () {}; + ContentEditableInput.prototype.resetPosition = function () {}; + + ContentEditableInput.prototype.needsContentAttribute = true; + + function posToDOM(cm, pos) { + var view = findViewForLine(cm, pos.line); + if (!view || view.hidden) { return null } + var line = getLine(cm.doc, pos.line); + var info = mapFromLineView(view, line, pos.line); + + var order = getOrder(line, cm.doc.direction), side = "left"; + if (order) { + var partPos = getBidiPartAt(order, pos.ch); + side = partPos % 2 ? "right" : "left"; + } + var result = nodeAndOffsetInLineMap(info.map, pos.ch, side); + result.offset = result.collapse == "right" ? result.end : result.start; + return result + } + + function isInGutter(node) { + for (var scan = node; scan; scan = scan.parentNode) + { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } } + return false + } + + function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos } + + function domTextBetween(cm, from, to, fromLine, toLine) { + var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false; + function recognizeMarker(id) { return function (marker) { return marker.id == id; } } + function close() { + if (closing) { + text += lineSep; + if (extraLinebreak) { text += lineSep; } + closing = extraLinebreak = false; + } + } + function addText(str) { + if (str) { + close(); + text += str; + } + } + function walk(node) { + if (node.nodeType == 1) { + var cmText = node.getAttribute("cm-text"); + if (cmText) { + addText(cmText); + return + } + var markerID = node.getAttribute("cm-marker"), range; + if (markerID) { + var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID)); + if (found.length && (range = found[0].find(0))) + { addText(getBetween(cm.doc, range.from, range.to).join(lineSep)); } + return + } + if (node.getAttribute("contenteditable") == "false") { return } + var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName); + if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return } + + if (isBlock) { close(); } + for (var i = 0; i < node.childNodes.length; i++) + { walk(node.childNodes[i]); } + + if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; } + if (isBlock) { closing = true; } + } else if (node.nodeType == 3) { + addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " ")); + } + } + for (;;) { + walk(from); + if (from == to) { break } + from = from.nextSibling; + extraLinebreak = false; + } + return text + } + + function domToPos(cm, node, offset) { + var lineNode; + if (node == cm.display.lineDiv) { + lineNode = cm.display.lineDiv.childNodes[offset]; + if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) } + node = null; offset = 0; + } else { + for (lineNode = node;; lineNode = lineNode.parentNode) { + if (!lineNode || lineNode == cm.display.lineDiv) { return null } + if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break } + } + } + for (var i = 0; i < cm.display.view.length; i++) { + var lineView = cm.display.view[i]; + if (lineView.node == lineNode) + { return locateNodeInLineView(lineView, node, offset) } + } + } + + function locateNodeInLineView(lineView, node, offset) { + var wrapper = lineView.text.firstChild, bad = false; + if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) } + if (node == wrapper) { + bad = true; + node = wrapper.childNodes[offset]; + offset = 0; + if (!node) { + var line = lineView.rest ? lst(lineView.rest) : lineView.line; + return badPos(Pos(lineNo(line), line.text.length), bad) + } + } + + var textNode = node.nodeType == 3 ? node : null, topNode = node; + if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) { + textNode = node.firstChild; + if (offset) { offset = textNode.nodeValue.length; } + } + while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; } + var measure = lineView.measure, maps = measure.maps; + + function find(textNode, topNode, offset) { + for (var i = -1; i < (maps ? maps.length : 0); i++) { + var map = i < 0 ? measure.map : maps[i]; + for (var j = 0; j < map.length; j += 3) { + var curNode = map[j + 2]; + if (curNode == textNode || curNode == topNode) { + var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]); + var ch = map[j] + offset; + if (offset < 0 || curNode != textNode) { ch = map[j + (offset ? 1 : 0)]; } + return Pos(line, ch) + } + } + } + } + var found = find(textNode, topNode, offset); + if (found) { return badPos(found, bad) } + + // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems + for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) { + found = find(after, after.firstChild, 0); + if (found) + { return badPos(Pos(found.line, found.ch - dist), bad) } + else + { dist += after.textContent.length; } + } + for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) { + found = find(before, before.firstChild, -1); + if (found) + { return badPos(Pos(found.line, found.ch + dist$1), bad) } + else + { dist$1 += before.textContent.length; } + } + } + + // TEXTAREA INPUT STYLE + + var TextareaInput = function(cm) { + this.cm = cm; + // See input.poll and input.reset + this.prevInput = ""; + + // Flag that indicates whether we expect input to appear real soon + // now (after some event like 'keypress' or 'input') and are + // polling intensively. + this.pollingFast = false; + // Self-resetting timeout for the poller + this.polling = new Delayed(); + // Used to work around IE issue with selection being forgotten when focus moves away from textarea + this.hasSelection = false; + this.composing = null; + }; + + TextareaInput.prototype.init = function (display) { + var this$1 = this; + + var input = this, cm = this.cm; + this.createField(display); + var te = this.textarea; + + display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild); + + // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore) + if (ios) { te.style.width = "0px"; } + + on(te, "input", function () { + if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; } + input.poll(); + }); + + on(te, "paste", function (e) { + if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } + + cm.state.pasteIncoming = +new Date; + input.fastPoll(); + }); + + function prepareCopyCut(e) { + if (signalDOMEvent(cm, e)) { return } + if (cm.somethingSelected()) { + setLastCopied({lineWise: false, text: cm.getSelections()}); + } else if (!cm.options.lineWiseCopyCut) { + return + } else { + var ranges = copyableRanges(cm); + setLastCopied({lineWise: true, text: ranges.text}); + if (e.type == "cut") { + cm.setSelections(ranges.ranges, null, sel_dontScroll); + } else { + input.prevInput = ""; + te.value = ranges.text.join("\n"); + selectInput(te); + } + } + if (e.type == "cut") { cm.state.cutIncoming = +new Date; } + } + on(te, "cut", prepareCopyCut); + on(te, "copy", prepareCopyCut); + + on(display.scroller, "paste", function (e) { + if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return } + if (!te.dispatchEvent) { + cm.state.pasteIncoming = +new Date; + input.focus(); + return + } + + // Pass the `paste` event to the textarea so it's handled by its event listener. + var event = new Event("paste"); + event.clipboardData = e.clipboardData; + te.dispatchEvent(event); + }); + + // Prevent normal selection in the editor (we handle our own) + on(display.lineSpace, "selectstart", function (e) { + if (!eventInWidget(display, e)) { e_preventDefault(e); } + }); + + on(te, "compositionstart", function () { + var start = cm.getCursor("from"); + if (input.composing) { input.composing.range.clear(); } + input.composing = { + start: start, + range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"}) + }; + }); + on(te, "compositionend", function () { + if (input.composing) { + input.poll(); + input.composing.range.clear(); + input.composing = null; + } + }); + }; + + TextareaInput.prototype.createField = function (_display) { + // Wraps and hides input textarea + this.wrapper = hiddenTextarea(); + // The semihidden textarea that is focused when the editor is + // focused, and receives input. + this.textarea = this.wrapper.firstChild; + }; + + TextareaInput.prototype.screenReaderLabelChanged = function (label) { + // Label for screenreaders, accessibility + if(label) { + this.textarea.setAttribute('aria-label', label); + } else { + this.textarea.removeAttribute('aria-label'); + } + }; + + TextareaInput.prototype.prepareSelection = function () { + // Redraw the selection and/or cursor + var cm = this.cm, display = cm.display, doc = cm.doc; + var result = prepareSelection(cm); + + // Move the hidden textarea near the cursor to prevent scrolling artifacts + if (cm.options.moveInputWithCursor) { + var headPos = cursorCoords(cm, doc.sel.primary().head, "div"); + var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect(); + result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10, + headPos.top + lineOff.top - wrapOff.top)); + result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10, + headPos.left + lineOff.left - wrapOff.left)); + } + + return result + }; + + TextareaInput.prototype.showSelection = function (drawn) { + var cm = this.cm, display = cm.display; + removeChildrenAndAdd(display.cursorDiv, drawn.cursors); + removeChildrenAndAdd(display.selectionDiv, drawn.selection); + if (drawn.teTop != null) { + this.wrapper.style.top = drawn.teTop + "px"; + this.wrapper.style.left = drawn.teLeft + "px"; + } + }; + + // Reset the input to correspond to the selection (or to be empty, + // when not typing and nothing is selected) + TextareaInput.prototype.reset = function (typing) { + if (this.contextMenuPending || this.composing) { return } + var cm = this.cm; + if (cm.somethingSelected()) { + this.prevInput = ""; + var content = cm.getSelection(); + this.textarea.value = content; + if (cm.state.focused) { selectInput(this.textarea); } + if (ie && ie_version >= 9) { this.hasSelection = content; } + } else if (!typing) { + this.prevInput = this.textarea.value = ""; + if (ie && ie_version >= 9) { this.hasSelection = null; } + } + }; + + TextareaInput.prototype.getField = function () { return this.textarea }; + + TextareaInput.prototype.supportsTouch = function () { return false }; + + TextareaInput.prototype.focus = function () { + if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) { + try { this.textarea.focus(); } + catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM + } + }; + + TextareaInput.prototype.blur = function () { this.textarea.blur(); }; + + TextareaInput.prototype.resetPosition = function () { + this.wrapper.style.top = this.wrapper.style.left = 0; + }; + + TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); }; + + // Poll for input changes, using the normal rate of polling. This + // runs as long as the editor is focused. + TextareaInput.prototype.slowPoll = function () { + var this$1 = this; + + if (this.pollingFast) { return } + this.polling.set(this.cm.options.pollInterval, function () { + this$1.poll(); + if (this$1.cm.state.focused) { this$1.slowPoll(); } + }); + }; + + // When an event has just come in that is likely to add or change + // something in the input textarea, we poll faster, to ensure that + // the change appears on the screen quickly. + TextareaInput.prototype.fastPoll = function () { + var missed = false, input = this; + input.pollingFast = true; + function p() { + var changed = input.poll(); + if (!changed && !missed) {missed = true; input.polling.set(60, p);} + else {input.pollingFast = false; input.slowPoll();} + } + input.polling.set(20, p); + }; + + // Read input from the textarea, and update the document to match. + // When something is selected, it is present in the textarea, and + // selected (unless it is huge, in which case a placeholder is + // used). When nothing is selected, the cursor sits after previously + // seen text (can be empty), which is stored in prevInput (we must + // not reset the textarea when typing, because that breaks IME). + TextareaInput.prototype.poll = function () { + var this$1 = this; + + var cm = this.cm, input = this.textarea, prevInput = this.prevInput; + // Since this is called a *lot*, try to bail out as cheaply as + // possible when it is clear that nothing happened. hasSelection + // will be the case when there is a lot of text in the textarea, + // in which case reading its value would be expensive. + if (this.contextMenuPending || !cm.state.focused || + (hasSelection(input) && !prevInput && !this.composing) || + cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq) + { return false } + + var text = input.value; + // If nothing changed, bail. + if (text == prevInput && !cm.somethingSelected()) { return false } + // Work around nonsensical selection resetting in IE9/10, and + // inexplicable appearance of private area unicode characters on + // some key combos in Mac (#2689). + if (ie && ie_version >= 9 && this.hasSelection === text || + mac && /[\uf700-\uf7ff]/.test(text)) { + cm.display.input.reset(); + return false + } + + if (cm.doc.sel == cm.display.selForContextMenu) { + var first = text.charCodeAt(0); + if (first == 0x200b && !prevInput) { prevInput = "\u200b"; } + if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") } + } + // Find the part of the input that is actually new + var same = 0, l = Math.min(prevInput.length, text.length); + while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; } + + runInOp(cm, function () { + applyTextInput(cm, text.slice(same), prevInput.length - same, + null, this$1.composing ? "*compose" : null); + + // Don't leave long text in the textarea, since it makes further polling slow + if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; } + else { this$1.prevInput = text; } + + if (this$1.composing) { + this$1.composing.range.clear(); + this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"), + {className: "CodeMirror-composing"}); + } + }); + return true + }; + + TextareaInput.prototype.ensurePolled = function () { + if (this.pollingFast && this.poll()) { this.pollingFast = false; } + }; + + TextareaInput.prototype.onKeyPress = function () { + if (ie && ie_version >= 9) { this.hasSelection = null; } + this.fastPoll(); + }; + + TextareaInput.prototype.onContextMenu = function (e) { + var input = this, cm = input.cm, display = cm.display, te = input.textarea; + if (input.contextMenuPending) { input.contextMenuPending(); } + var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop; + if (!pos || presto) { return } // Opera is difficult. + + // Reset the current text selection only if the click is done outside of the selection + // and 'resetSelectionOnContextMenu' option is true. + var reset = cm.options.resetSelectionOnContextMenu; + if (reset && cm.doc.sel.contains(pos) == -1) + { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); } + + var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText; + var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect(); + input.wrapper.style.cssText = "position: static"; + te.style.cssText = "position: absolute; width: 30px; height: 30px;\n top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; + var oldScrollY; + if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712) + display.input.focus(); + if (webkit) { window.scrollTo(null, oldScrollY); } + display.input.reset(); + // Adds "Select all" to context menu in FF + if (!cm.somethingSelected()) { te.value = input.prevInput = " "; } + input.contextMenuPending = rehide; + display.selForContextMenu = cm.doc.sel; + clearTimeout(display.detectingSelectAll); + + // Select-all will be greyed out if there's nothing to select, so + // this adds a zero-width space so that we can later check whether + // it got selected. + function prepareSelectAllHack() { + if (te.selectionStart != null) { + var selected = cm.somethingSelected(); + var extval = "\u200b" + (selected ? te.value : ""); + te.value = "\u21da"; // Used to catch context-menu undo + te.value = extval; + input.prevInput = selected ? "" : "\u200b"; + te.selectionStart = 1; te.selectionEnd = extval.length; + // Re-set this, in case some other handler touched the + // selection in the meantime. + display.selForContextMenu = cm.doc.sel; + } + } + function rehide() { + if (input.contextMenuPending != rehide) { return } + input.contextMenuPending = false; + input.wrapper.style.cssText = oldWrapperCSS; + te.style.cssText = oldCSS; + if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); } + + // Try to detect the user choosing select-all + if (te.selectionStart != null) { + if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); } + var i = 0, poll = function () { + if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 && + te.selectionEnd > 0 && input.prevInput == "\u200b") { + operation(cm, selectAll)(cm); + } else if (i++ < 10) { + display.detectingSelectAll = setTimeout(poll, 500); + } else { + display.selForContextMenu = null; + display.input.reset(); + } + }; + display.detectingSelectAll = setTimeout(poll, 200); + } + } + + if (ie && ie_version >= 9) { prepareSelectAllHack(); } + if (captureRightClick) { + e_stop(e); + var mouseup = function () { + off(window, "mouseup", mouseup); + setTimeout(rehide, 20); + }; + on(window, "mouseup", mouseup); + } else { + setTimeout(rehide, 50); + } + }; + + TextareaInput.prototype.readOnlyChanged = function (val) { + if (!val) { this.reset(); } + this.textarea.disabled = val == "nocursor"; + this.textarea.readOnly = !!val; + }; + + TextareaInput.prototype.setUneditable = function () {}; + + TextareaInput.prototype.needsContentAttribute = false; + + function fromTextArea(textarea, options) { + options = options ? copyObj(options) : {}; + options.value = textarea.value; + if (!options.tabindex && textarea.tabIndex) + { options.tabindex = textarea.tabIndex; } + if (!options.placeholder && textarea.placeholder) + { options.placeholder = textarea.placeholder; } + // Set autofocus to true if this textarea is focused, or if it has + // autofocus and no other element is focused. + if (options.autofocus == null) { + var hasFocus = activeElt(); + options.autofocus = hasFocus == textarea || + textarea.getAttribute("autofocus") != null && hasFocus == document.body; + } + + function save() {textarea.value = cm.getValue();} + + var realSubmit; + if (textarea.form) { + on(textarea.form, "submit", save); + // Deplorable hack to make the submit method do the right thing. + if (!options.leaveSubmitMethodAlone) { + var form = textarea.form; + realSubmit = form.submit; + try { + var wrappedSubmit = form.submit = function () { + save(); + form.submit = realSubmit; + form.submit(); + form.submit = wrappedSubmit; + }; + } catch(e) {} + } + } + + options.finishInit = function (cm) { + cm.save = save; + cm.getTextArea = function () { return textarea; }; + cm.toTextArea = function () { + cm.toTextArea = isNaN; // Prevent this from being ran twice + save(); + textarea.parentNode.removeChild(cm.getWrapperElement()); + textarea.style.display = ""; + if (textarea.form) { + off(textarea.form, "submit", save); + if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == "function") + { textarea.form.submit = realSubmit; } + } + }; + }; + + textarea.style.display = "none"; + var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); }, + options); + return cm + } + + function addLegacyProps(CodeMirror) { + CodeMirror.off = off; + CodeMirror.on = on; + CodeMirror.wheelEventPixels = wheelEventPixels; + CodeMirror.Doc = Doc; + CodeMirror.splitLines = splitLinesAuto; + CodeMirror.countColumn = countColumn; + CodeMirror.findColumn = findColumn; + CodeMirror.isWordChar = isWordCharBasic; + CodeMirror.Pass = Pass; + CodeMirror.signal = signal; + CodeMirror.Line = Line; + CodeMirror.changeEnd = changeEnd; + CodeMirror.scrollbarModel = scrollbarModel; + CodeMirror.Pos = Pos; + CodeMirror.cmpPos = cmp; + CodeMirror.modes = modes; + CodeMirror.mimeModes = mimeModes; + CodeMirror.resolveMode = resolveMode; + CodeMirror.getMode = getMode; + CodeMirror.modeExtensions = modeExtensions; + CodeMirror.extendMode = extendMode; + CodeMirror.copyState = copyState; + CodeMirror.startState = startState; + CodeMirror.innerMode = innerMode; + CodeMirror.commands = commands; + CodeMirror.keyMap = keyMap; + CodeMirror.keyName = keyName; + CodeMirror.isModifierKey = isModifierKey; + CodeMirror.lookupKey = lookupKey; + CodeMirror.normalizeKeyMap = normalizeKeyMap; + CodeMirror.StringStream = StringStream; + CodeMirror.SharedTextMarker = SharedTextMarker; + CodeMirror.TextMarker = TextMarker; + CodeMirror.LineWidget = LineWidget; + CodeMirror.e_preventDefault = e_preventDefault; + CodeMirror.e_stopPropagation = e_stopPropagation; + CodeMirror.e_stop = e_stop; + CodeMirror.addClass = addClass; + CodeMirror.contains = contains; + CodeMirror.rmClass = rmClass; + CodeMirror.keyNames = keyNames; + } + + // EDITOR CONSTRUCTOR + + defineOptions(CodeMirror); + + addEditorMethods(CodeMirror); + + // Set up methods on CodeMirror's prototype to redirect to the editor's document. + var dontDelegate = "iter insert remove copy getEditor constructor".split(" "); + for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0) + { CodeMirror.prototype[prop] = (function(method) { + return function() {return method.apply(this.doc, arguments)} + })(Doc.prototype[prop]); } } + + eventMixin(Doc); + CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}; + + // Extra arguments are stored as the mode's dependencies, which is + // used by (legacy) mechanisms like loadmode.js to automatically + // load a mode. (Preferred mechanism is the require/define calls.) + CodeMirror.defineMode = function(name/*, mode, …*/) { + if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name; } + defineMode.apply(this, arguments); + }; + + CodeMirror.defineMIME = defineMIME; + + // Minimal default mode. + CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); }); + CodeMirror.defineMIME("text/plain", "null"); + + // EXTENSIONS + + CodeMirror.defineExtension = function (name, func) { + CodeMirror.prototype[name] = func; + }; + CodeMirror.defineDocExtension = function (name, func) { + Doc.prototype[name] = func; + }; + + CodeMirror.fromTextArea = fromTextArea; + + addLegacyProps(CodeMirror); + + CodeMirror.version = "5.62.0"; + + return CodeMirror; +}))); + + +/* +file https://github.com/codemirror/CodeMirror/blob/5.62.0/addon/edit/matchbrackets.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + var ie_lt8 = /MSIE \d/.test(navigator.userAgent) && + (document.documentMode == null || document.documentMode < 8); + + var Pos = CodeMirror.Pos; + + var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<", "<": ">>", ">": "<<"}; + + function bracketRegex(config) { + return config && config.bracketRegex || /[(){}[\]]/ + } + + function findMatchingBracket(cm, where, config) { + var line = cm.getLineHandle(where.line), pos = where.ch - 1; + var afterCursor = config && config.afterCursor + if (afterCursor == null) + afterCursor = /(^| )cm-fat-cursor($| )/.test(cm.getWrapperElement().className) + var re = bracketRegex(config) + + // A cursor is defined as between two characters, but in in vim command mode + // (i.e. not insert mode), the cursor is visually represented as a + // highlighted box on top of the 2nd character. Otherwise, we allow matches + // from before or after the cursor. + var match = (!afterCursor && pos >= 0 && re.test(line.text.charAt(pos)) && matching[line.text.charAt(pos)]) || + re.test(line.text.charAt(pos + 1)) && matching[line.text.charAt(++pos)]; + if (!match) return null; + var dir = match.charAt(1) == ">" ? 1 : -1; + if (config && config.strict && (dir > 0) != (pos == where.ch)) return null; + var style = cm.getTokenTypeAt(Pos(where.line, pos + 1)); + + var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style, config); + if (found == null) return null; + return {from: Pos(where.line, pos), to: found && found.pos, + match: found && found.ch == match.charAt(0), forward: dir > 0}; + } + + // bracketRegex is used to specify which type of bracket to scan + // should be a regexp, e.g. /[[\]]/ + // + // Note: If "where" is on an open bracket, then this bracket is ignored. + // + // Returns false when no bracket was found, null when it reached + // maxScanLines and gave up + function scanForBracket(cm, where, dir, style, config) { + var maxScanLen = (config && config.maxScanLineLength) || 10000; + var maxScanLines = (config && config.maxScanLines) || 1000; + + var stack = []; + var re = bracketRegex(config) + var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1) + : Math.max(cm.firstLine() - 1, where.line - maxScanLines); + for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) { + var line = cm.getLine(lineNo); + if (!line) continue; + var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1; + if (line.length > maxScanLen) continue; + if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0); + for (; pos != end; pos += dir) { + var ch = line.charAt(pos); + if (re.test(ch) && (style === undefined || + (cm.getTokenTypeAt(Pos(lineNo, pos + 1)) || "") == (style || ""))) { + var match = matching[ch]; + if (match && (match.charAt(1) == ">") == (dir > 0)) stack.push(ch); + else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch}; + else stack.pop(); + } + } + } + return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null; + } + + function matchBrackets(cm, autoclear, config) { + // Disable brace matching in long lines, since it'll cause hugely slow updates + var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000, + highlightNonMatching = config && config.highlightNonMatching; + var marks = [], ranges = cm.listSelections(); + for (var i = 0; i < ranges.length; i++) { + var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, config); + if (match && (match.match || highlightNonMatching !== false) && cm.getLine(match.from.line).length <= maxHighlightLen) { + var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; + marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style})); + if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen) + marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style})); + } + } + + if (marks.length) { + // Kludge to work around the IE bug from issue #1193, where text + // input stops going to the textarea whenever this fires. + if (ie_lt8 && cm.state.focused) cm.focus(); + + var clear = function() { + cm.operation(function() { + for (var i = 0; i < marks.length; i++) marks[i].clear(); + }); + }; + if (autoclear) setTimeout(clear, 800); + else return clear; + } + } + + function doMatchBrackets(cm) { + cm.operation(function() { + if (cm.state.matchBrackets.currentlyHighlighted) { + cm.state.matchBrackets.currentlyHighlighted(); + cm.state.matchBrackets.currentlyHighlighted = null; + } + cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets); + }); + } + + function clearHighlighted(cm) { + if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) { + cm.state.matchBrackets.currentlyHighlighted(); + cm.state.matchBrackets.currentlyHighlighted = null; + } + } + + CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) { + cm.off("cursorActivity", doMatchBrackets); + cm.off("focus", doMatchBrackets) + cm.off("blur", clearHighlighted) + clearHighlighted(cm); + } + if (val) { + cm.state.matchBrackets = typeof val == "object" ? val : {}; + cm.on("cursorActivity", doMatchBrackets); + cm.on("focus", doMatchBrackets) + cm.on("blur", clearHighlighted) + } + }); + + CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);}); + CodeMirror.defineExtension("findMatchingBracket", function(pos, config, oldConfig){ + // Backwards-compatibility kludge + if (oldConfig || typeof config == "boolean") { + if (!oldConfig) { + config = config ? {strict: true} : null + } else { + oldConfig.strict = config + config = oldConfig + } + } + return findMatchingBracket(this, pos, config) + }); + CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){ + return scanForBracket(this, pos, dir, style, config); + }); +}); + + +/* +file https://github.com/codemirror/CodeMirror/blob/5.62.0/addon/edit/trailingspace.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) { + if (prev == CodeMirror.Init) prev = false; + if (prev && !val) + cm.removeOverlay("trailingspace"); + else if (!prev && val) + cm.addOverlay({ + token: function(stream) { + for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {} + if (i > stream.pos) { stream.pos = i; return null; } + stream.pos = l; + return "trailingspace"; + }, + name: "trailingspace" + }); + }); +}); + + +/* +file https://github.com/codemirror/CodeMirror/blob/5.62.0/addon/lint/lint.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + var GUTTER_ID = "CodeMirror-lint-markers"; + var LINT_LINE_ID = "CodeMirror-lint-line-"; + + function showTooltip(cm, e, content) { + var tt = document.createElement("div"); + tt.className = "CodeMirror-lint-tooltip cm-s-" + cm.options.theme; + tt.appendChild(content.cloneNode(true)); + if (cm.state.lint.options.selfContain) + cm.getWrapperElement().appendChild(tt); + else + document.body.appendChild(tt); + + function position(e) { + if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position); + tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + "px"; + tt.style.left = (e.clientX + 5) + "px"; + } + CodeMirror.on(document, "mousemove", position); + position(e); + if (tt.style.opacity != null) tt.style.opacity = 1; + return tt; + } + function rm(elt) { + if (elt.parentNode) elt.parentNode.removeChild(elt); + } + function hideTooltip(tt) { + if (!tt.parentNode) return; + if (tt.style.opacity == null) rm(tt); + tt.style.opacity = 0; + setTimeout(function() { rm(tt); }, 600); + } + + function showTooltipFor(cm, e, content, node) { + var tooltip = showTooltip(cm, e, content); + function hide() { + CodeMirror.off(node, "mouseout", hide); + if (tooltip) { hideTooltip(tooltip); tooltip = null; } + } + var poll = setInterval(function() { + if (tooltip) for (var n = node;; n = n.parentNode) { + if (n && n.nodeType == 11) n = n.host; + if (n == document.body) return; + if (!n) { hide(); break; } + } + if (!tooltip) return clearInterval(poll); + }, 400); + CodeMirror.on(node, "mouseout", hide); + } + + function LintState(cm, options, hasGutter) { + this.marked = []; + this.options = options; + this.timeout = null; + this.hasGutter = hasGutter; + this.onMouseOver = function(e) { onMouseOver(cm, e); }; + this.waitingFor = 0 + } + + function parseOptions(_cm, options) { + if (options instanceof Function) return {getAnnotations: options}; + if (!options || options === true) options = {}; + return options; + } + + function clearMarks(cm) { + var state = cm.state.lint; + if (state.hasGutter) cm.clearGutter(GUTTER_ID); + if (isHighlightErrorLinesEnabled(state)) clearErrorLines(cm); + for (var i = 0; i < state.marked.length; ++i) + state.marked[i].clear(); + state.marked.length = 0; + } + + function clearErrorLines(cm) { + cm.eachLine(function(line) { + var has = line.wrapClass && /\bCodeMirror-lint-line-\w+\b/.exec(line.wrapClass); + if (has) cm.removeLineClass(line, "wrap", has[0]); + }) + } + + function isHighlightErrorLinesEnabled(state) { + return state.options.highlightLines; + } + + function makeMarker(cm, labels, severity, multiple, tooltips) { + var marker = document.createElement("div"), inner = marker; + marker.className = "CodeMirror-lint-marker CodeMirror-lint-marker-" + severity; + if (multiple) { + inner = marker.appendChild(document.createElement("div")); + inner.className = "CodeMirror-lint-marker CodeMirror-lint-marker-multiple"; + } + + if (tooltips != false) CodeMirror.on(inner, "mouseover", function(e) { + showTooltipFor(cm, e, labels, inner); + }); + + return marker; + } + + function getMaxSeverity(a, b) { + if (a == "error") return a; + else return b; + } + + function groupByLine(annotations) { + var lines = []; + for (var i = 0; i < annotations.length; ++i) { + var ann = annotations[i], line = ann.from.line; + (lines[line] || (lines[line] = [])).push(ann); + } + return lines; + } + + function annotationTooltip(ann) { + var severity = ann.severity; + if (!severity) severity = "error"; + var tip = document.createElement("div"); + tip.className = "CodeMirror-lint-message CodeMirror-lint-message-" + severity; + if (typeof ann.messageHTML != 'undefined') { + tip.innerHTML = ann.messageHTML; + } else { + tip.appendChild(document.createTextNode(ann.message)); + } + return tip; + } + + function lintAsync(cm, getAnnotations, passOptions) { + var state = cm.state.lint + var id = ++state.waitingFor + function abort() { + id = -1 + cm.off("change", abort) + } + cm.on("change", abort) + getAnnotations(cm.getValue(), function(annotations, arg2) { + cm.off("change", abort) + if (state.waitingFor != id) return + if (arg2 && annotations instanceof CodeMirror) annotations = arg2 + cm.operation(function() {updateLinting(cm, annotations)}) + }, passOptions, cm); + } + + function startLinting(cm) { + var state = cm.state.lint; + if (!state) return; + var options = state.options; + /* + * Passing rules in `options` property prevents JSHint (and other linters) from complaining + * about unrecognized rules like `onUpdateLinting`, `delay`, `lintOnChange`, etc. + */ + var passOptions = options.options || options; + var getAnnotations = options.getAnnotations || cm.getHelper(CodeMirror.Pos(0, 0), "lint"); + if (!getAnnotations) return; + if (options.async || getAnnotations.async) { + lintAsync(cm, getAnnotations, passOptions) + } else { + var annotations = getAnnotations(cm.getValue(), passOptions, cm); + if (!annotations) return; + if (annotations.then) annotations.then(function(issues) { + cm.operation(function() {updateLinting(cm, issues)}) + }); + else cm.operation(function() {updateLinting(cm, annotations)}) + } + } + + function updateLinting(cm, annotationsNotSorted) { + var state = cm.state.lint; + if (!state) return; + var options = state.options; + clearMarks(cm); + + var annotations = groupByLine(annotationsNotSorted); + + for (var line = 0; line < annotations.length; ++line) { + var anns = annotations[line]; + if (!anns) continue; + + // filter out duplicate messages + var message = []; + anns = anns.filter(function(item) { return message.indexOf(item.message) > -1 ? false : message.push(item.message) }); + + var maxSeverity = null; + var tipLabel = state.hasGutter && document.createDocumentFragment(); + + for (var i = 0; i < anns.length; ++i) { + var ann = anns[i]; + var severity = ann.severity; + if (!severity) severity = "error"; + maxSeverity = getMaxSeverity(maxSeverity, severity); + + if (options.formatAnnotation) ann = options.formatAnnotation(ann); + if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann)); + + if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, { + className: "CodeMirror-lint-mark CodeMirror-lint-mark-" + severity, + __annotation: ann + })); + } + // use original annotations[line] to show multiple messages + if (state.hasGutter) + cm.setGutterMarker(line, GUTTER_ID, makeMarker(cm, tipLabel, maxSeverity, annotations[line].length > 1, + state.options.tooltips)); + + if (isHighlightErrorLinesEnabled(state)) + cm.addLineClass(line, "wrap", LINT_LINE_ID + maxSeverity); + } + if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm); + } + + function onChange(cm) { + var state = cm.state.lint; + if (!state) return; + clearTimeout(state.timeout); + state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500); + } + + function popupTooltips(cm, annotations, e) { + var target = e.target || e.srcElement; + var tooltip = document.createDocumentFragment(); + for (var i = 0; i < annotations.length; i++) { + var ann = annotations[i]; + tooltip.appendChild(annotationTooltip(ann)); + } + showTooltipFor(cm, e, tooltip, target); + } + + function onMouseOver(cm, e) { + var target = e.target || e.srcElement; + if (!/\bCodeMirror-lint-mark-/.test(target.className)) return; + var box = target.getBoundingClientRect(), x = (box.left + box.right) / 2, y = (box.top + box.bottom) / 2; + var spans = cm.findMarksAt(cm.coordsChar({left: x, top: y}, "client")); + + var annotations = []; + for (var i = 0; i < spans.length; ++i) { + var ann = spans[i].__annotation; + if (ann) annotations.push(ann); + } + if (annotations.length) popupTooltips(cm, annotations, e); + } + + CodeMirror.defineOption("lint", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) { + clearMarks(cm); + if (cm.state.lint.options.lintOnChange !== false) + cm.off("change", onChange); + CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver); + clearTimeout(cm.state.lint.timeout); + delete cm.state.lint; + } + + if (val) { + var gutters = cm.getOption("gutters"), hasLintGutter = false; + for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true; + var state = cm.state.lint = new LintState(cm, parseOptions(cm, val), hasLintGutter); + if (state.options.lintOnChange !== false) + cm.on("change", onChange); + if (state.options.tooltips != false && state.options.tooltips != "gutter") + CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver); + + startLinting(cm); + } + }); + + CodeMirror.defineExtension("performLint", function() { + startLinting(this); + }); +}); + + +/* +file https://github.com/codemirror/CodeMirror/blob/5.62.0/mode/javascript/javascript.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("javascript", function(config, parserConfig) { + var indentUnit = config.indentUnit; + var statementIndent = parserConfig.statementIndent; + var jsonldMode = parserConfig.jsonld; + var jsonMode = parserConfig.json || jsonldMode; + var trackScope = parserConfig.trackScope !== false + var isTS = parserConfig.typescript; + var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/; + + // Tokenizer + + var keywords = function(){ + function kw(type) {return {type: type, style: "keyword"};} + var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d"); + var operator = kw("operator"), atom = {type: "atom", style: "atom"}; + + return { + "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, + "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C, + "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"), + "function": kw("function"), "catch": kw("catch"), + "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"), + "in": operator, "typeof": operator, "instanceof": operator, + "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom, + "this": kw("this"), "class": kw("class"), "super": kw("atom"), + "yield": C, "export": kw("export"), "import": kw("import"), "extends": C, + "await": C + }; + }(); + + var isOperatorChar = /[+\-*&%=<>!?|~^@]/; + var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/; + + function readRegexp(stream) { + var escaped = false, next, inSet = false; + while ((next = stream.next()) != null) { + if (!escaped) { + if (next == "/" && !inSet) return; + if (next == "[") inSet = true; + else if (inSet && next == "]") inSet = false; + } + escaped = !escaped && next == "\\"; + } + } + + // Used as scratch variables to communicate multiple values without + // consing up tons of objects. + var type, content; + function ret(tp, style, cont) { + type = tp; content = cont; + return style; + } + function tokenBase(stream, state) { + var ch = stream.next(); + if (ch == '"' || ch == "'") { + state.tokenize = tokenString(ch); + return state.tokenize(stream, state); + } else if (ch == "." && stream.match(/^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/)) { + return ret("number", "number"); + } else if (ch == "." && stream.match("..")) { + return ret("spread", "meta"); + } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) { + return ret(ch); + } else if (ch == "=" && stream.eat(">")) { + return ret("=>", "operator"); + } else if (ch == "0" && stream.match(/^(?:x[\dA-Fa-f_]+|o[0-7_]+|b[01_]+)n?/)) { + return ret("number", "number"); + } else if (/\d/.test(ch)) { + stream.match(/^[\d_]*(?:n|(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)?/); + return ret("number", "number"); + } else if (ch == "/") { + if (stream.eat("*")) { + state.tokenize = tokenComment; + return tokenComment(stream, state); + } else if (stream.eat("/")) { + stream.skipToEnd(); + return ret("comment", "comment"); + } else if (expressionAllowed(stream, state, 1)) { + readRegexp(stream); + stream.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/); + return ret("regexp", "string-2"); + } else { + stream.eat("="); + return ret("operator", "operator", stream.current()); + } + } else if (ch == "`") { + state.tokenize = tokenQuasi; + return tokenQuasi(stream, state); + } else if (ch == "#" && stream.peek() == "!") { + stream.skipToEnd(); + return ret("meta", "meta"); + } else if (ch == "#" && stream.eatWhile(wordRE)) { + return ret("variable", "property") + } else if (ch == "<" && stream.match("!--") || + (ch == "-" && stream.match("->") && !/\S/.test(stream.string.slice(0, stream.start)))) { + stream.skipToEnd() + return ret("comment", "comment") + } else if (isOperatorChar.test(ch)) { + if (ch != ">" || !state.lexical || state.lexical.type != ">") { + if (stream.eat("=")) { + if (ch == "!" || ch == "=") stream.eat("=") + } else if (/[<>*+\-|&?]/.test(ch)) { + stream.eat(ch) + if (ch == ">") stream.eat(ch) + } + } + if (ch == "?" && stream.eat(".")) return ret(".") + return ret("operator", "operator", stream.current()); + } else if (wordRE.test(ch)) { + stream.eatWhile(wordRE); + var word = stream.current() + if (state.lastType != ".") { + if (keywords.propertyIsEnumerable(word)) { + var kw = keywords[word] + return ret(kw.type, kw.style, word) + } + if (word == "async" && stream.match(/^(\s|\/\*([^*]|\*(?!\/))*?\*\/)*[\[\(\w]/, false)) + return ret("async", "keyword", word) + } + return ret("variable", "variable", word) + } + } + + function tokenString(quote) { + return function(stream, state) { + var escaped = false, next; + if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){ + state.tokenize = tokenBase; + return ret("jsonld-keyword", "meta"); + } + while ((next = stream.next()) != null) { + if (next == quote && !escaped) break; + escaped = !escaped && next == "\\"; + } + if (!escaped) state.tokenize = tokenBase; + return ret("string", "string"); + }; + } + + function tokenComment(stream, state) { + var maybeEnd = false, ch; + while (ch = stream.next()) { + if (ch == "/" && maybeEnd) { + state.tokenize = tokenBase; + break; + } + maybeEnd = (ch == "*"); + } + return ret("comment", "comment"); + } + + function tokenQuasi(stream, state) { + var escaped = false, next; + while ((next = stream.next()) != null) { + if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) { + state.tokenize = tokenBase; + break; + } + escaped = !escaped && next == "\\"; + } + return ret("quasi", "string-2", stream.current()); + } + + var brackets = "([{}])"; + // This is a crude lookahead trick to try and notice that we're + // parsing the argument patterns for a fat-arrow function before we + // actually hit the arrow token. It only works if the arrow is on + // the same line as the arguments and there's no strange noise + // (comments) in between. Fallback is to only notice when we hit the + // arrow, and not declare the arguments as locals for the arrow + // body. + function findFatArrow(stream, state) { + if (state.fatArrowAt) state.fatArrowAt = null; + var arrow = stream.string.indexOf("=>", stream.start); + if (arrow < 0) return; + + if (isTS) { // Try to skip TypeScript return type declarations after the arguments + var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow)) + if (m) arrow = m.index + } + + var depth = 0, sawSomething = false; + for (var pos = arrow - 1; pos >= 0; --pos) { + var ch = stream.string.charAt(pos); + var bracket = brackets.indexOf(ch); + if (bracket >= 0 && bracket < 3) { + if (!depth) { ++pos; break; } + if (--depth == 0) { if (ch == "(") sawSomething = true; break; } + } else if (bracket >= 3 && bracket < 6) { + ++depth; + } else if (wordRE.test(ch)) { + sawSomething = true; + } else if (/["'\/`]/.test(ch)) { + for (;; --pos) { + if (pos == 0) return + var next = stream.string.charAt(pos - 1) + if (next == ch && stream.string.charAt(pos - 2) != "\\") { pos--; break } + } + } else if (sawSomething && !depth) { + ++pos; + break; + } + } + if (sawSomething && !depth) state.fatArrowAt = pos; + } + + // Parser + + var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, + "regexp": true, "this": true, "import": true, "jsonld-keyword": true}; + + function JSLexical(indented, column, type, align, prev, info) { + this.indented = indented; + this.column = column; + this.type = type; + this.prev = prev; + this.info = info; + if (align != null) this.align = align; + } + + function inScope(state, varname) { + if (!trackScope) return false + for (var v = state.localVars; v; v = v.next) + if (v.name == varname) return true; + for (var cx = state.context; cx; cx = cx.prev) { + for (var v = cx.vars; v; v = v.next) + if (v.name == varname) return true; + } + } + + function parseJS(state, style, type, content, stream) { + var cc = state.cc; + // Communicate our context to the combinators. + // (Less wasteful than consing up a hundred closures on every call.) + cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style; + + if (!state.lexical.hasOwnProperty("align")) + state.lexical.align = true; + + while(true) { + var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement; + if (combinator(type, content)) { + while(cc.length && cc[cc.length - 1].lex) + cc.pop()(); + if (cx.marked) return cx.marked; + if (type == "variable" && inScope(state, content)) return "variable-2"; + return style; + } + } + } + + // Combinator utils + + var cx = {state: null, column: null, marked: null, cc: null}; + function pass() { + for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]); + } + function cont() { + pass.apply(null, arguments); + return true; + } + function inList(name, list) { + for (var v = list; v; v = v.next) if (v.name == name) return true + return false; + } + function register(varname) { + var state = cx.state; + cx.marked = "def"; + if (!trackScope) return + if (state.context) { + if (state.lexical.info == "var" && state.context && state.context.block) { + // FIXME function decls are also not block scoped + var newContext = registerVarScoped(varname, state.context) + if (newContext != null) { + state.context = newContext + return + } + } else if (!inList(varname, state.localVars)) { + state.localVars = new Var(varname, state.localVars) + return + } + } + // Fall through means this is global + if (parserConfig.globalVars && !inList(varname, state.globalVars)) + state.globalVars = new Var(varname, state.globalVars) + } + function registerVarScoped(varname, context) { + if (!context) { + return null + } else if (context.block) { + var inner = registerVarScoped(varname, context.prev) + if (!inner) return null + if (inner == context.prev) return context + return new Context(inner, context.vars, true) + } else if (inList(varname, context.vars)) { + return context + } else { + return new Context(context.prev, new Var(varname, context.vars), false) + } + } + + function isModifier(name) { + return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly" + } + + // Combinators + + function Context(prev, vars, block) { this.prev = prev; this.vars = vars; this.block = block } + function Var(name, next) { this.name = name; this.next = next } + + var defaultVars = new Var("this", new Var("arguments", null)) + function pushcontext() { + cx.state.context = new Context(cx.state.context, cx.state.localVars, false) + cx.state.localVars = defaultVars + } + function pushblockcontext() { + cx.state.context = new Context(cx.state.context, cx.state.localVars, true) + cx.state.localVars = null + } + function popcontext() { + cx.state.localVars = cx.state.context.vars + cx.state.context = cx.state.context.prev + } + popcontext.lex = true + function pushlex(type, info) { + var result = function() { + var state = cx.state, indent = state.indented; + if (state.lexical.type == "stat") indent = state.lexical.indented; + else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev) + indent = outer.indented; + state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info); + }; + result.lex = true; + return result; + } + function poplex() { + var state = cx.state; + if (state.lexical.prev) { + if (state.lexical.type == ")") + state.indented = state.lexical.indented; + state.lexical = state.lexical.prev; + } + } + poplex.lex = true; + + function expect(wanted) { + function exp(type) { + if (type == wanted) return cont(); + else if (wanted == ";" || type == "}" || type == ")" || type == "]") return pass(); + else return cont(exp); + }; + return exp; + } + + function statement(type, value) { + if (type == "var") return cont(pushlex("vardef", value), vardef, expect(";"), poplex); + if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex); + if (type == "keyword b") return cont(pushlex("form"), statement, poplex); + if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex); + if (type == "debugger") return cont(expect(";")); + if (type == "{") return cont(pushlex("}"), pushblockcontext, block, poplex, popcontext); + if (type == ";") return cont(); + if (type == "if") { + if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex) + cx.state.cc.pop()(); + return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse); + } + if (type == "function") return cont(functiondef); + if (type == "for") return cont(pushlex("form"), pushblockcontext, forspec, statement, popcontext, poplex); + if (type == "class" || (isTS && value == "interface")) { + cx.marked = "keyword" + return cont(pushlex("form", type == "class" ? type : value), className, poplex) + } + if (type == "variable") { + if (isTS && value == "declare") { + cx.marked = "keyword" + return cont(statement) + } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) { + cx.marked = "keyword" + if (value == "enum") return cont(enumdef); + else if (value == "type") return cont(typename, expect("operator"), typeexpr, expect(";")); + else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex) + } else if (isTS && value == "namespace") { + cx.marked = "keyword" + return cont(pushlex("form"), expression, statement, poplex) + } else if (isTS && value == "abstract") { + cx.marked = "keyword" + return cont(statement) + } else { + return cont(pushlex("stat"), maybelabel); + } + } + if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"), pushblockcontext, + block, poplex, poplex, popcontext); + if (type == "case") return cont(expression, expect(":")); + if (type == "default") return cont(expect(":")); + if (type == "catch") return cont(pushlex("form"), pushcontext, maybeCatchBinding, statement, poplex, popcontext); + if (type == "export") return cont(pushlex("stat"), afterExport, poplex); + if (type == "import") return cont(pushlex("stat"), afterImport, poplex); + if (type == "async") return cont(statement) + if (value == "@") return cont(expression, statement) + return pass(pushlex("stat"), expression, expect(";"), poplex); + } + function maybeCatchBinding(type) { + if (type == "(") return cont(funarg, expect(")")) + } + function expression(type, value) { + return expressionInner(type, value, false); + } + function expressionNoComma(type, value) { + return expressionInner(type, value, true); + } + function parenExpr(type) { + if (type != "(") return pass() + return cont(pushlex(")"), maybeexpression, expect(")"), poplex) + } + function expressionInner(type, value, noComma) { + if (cx.state.fatArrowAt == cx.stream.start) { + var body = noComma ? arrowBodyNoComma : arrowBody; + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext); + else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext); + } + + var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma; + if (atomicTypes.hasOwnProperty(type)) return cont(maybeop); + if (type == "function") return cont(functiondef, maybeop); + if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); } + if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression); + if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop); + if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression); + if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop); + if (type == "{") return contCommasep(objprop, "}", null, maybeop); + if (type == "quasi") return pass(quasi, maybeop); + if (type == "new") return cont(maybeTarget(noComma)); + return cont(); + } + function maybeexpression(type) { + if (type.match(/[;\}\)\],]/)) return pass(); + return pass(expression); + } + + function maybeoperatorComma(type, value) { + if (type == ",") return cont(maybeexpression); + return maybeoperatorNoComma(type, value, false); + } + function maybeoperatorNoComma(type, value, noComma) { + var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma; + var expr = noComma == false ? expression : expressionNoComma; + if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext); + if (type == "operator") { + if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me); + if (isTS && value == "<" && cx.stream.match(/^([^<>]|<[^<>]*>)*>\s*\(/, false)) + return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me); + if (value == "?") return cont(expression, expect(":"), expr); + return cont(expr); + } + if (type == "quasi") { return pass(quasi, me); } + if (type == ";") return; + if (type == "(") return contCommasep(expressionNoComma, ")", "call", me); + if (type == ".") return cont(property, me); + if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me); + if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) } + if (type == "regexp") { + cx.state.lastType = cx.marked = "operator" + cx.stream.backUp(cx.stream.pos - cx.stream.start - 1) + return cont(expr) + } + } + function quasi(type, value) { + if (type != "quasi") return pass(); + if (value.slice(value.length - 2) != "${") return cont(quasi); + return cont(maybeexpression, continueQuasi); + } + function continueQuasi(type) { + if (type == "}") { + cx.marked = "string-2"; + cx.state.tokenize = tokenQuasi; + return cont(quasi); + } + } + function arrowBody(type) { + findFatArrow(cx.stream, cx.state); + return pass(type == "{" ? statement : expression); + } + function arrowBodyNoComma(type) { + findFatArrow(cx.stream, cx.state); + return pass(type == "{" ? statement : expressionNoComma); + } + function maybeTarget(noComma) { + return function(type) { + if (type == ".") return cont(noComma ? targetNoComma : target); + else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma) + else return pass(noComma ? expressionNoComma : expression); + }; + } + function target(_, value) { + if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); } + } + function targetNoComma(_, value) { + if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); } + } + function maybelabel(type) { + if (type == ":") return cont(poplex, statement); + return pass(maybeoperatorComma, expect(";"), poplex); + } + function property(type) { + if (type == "variable") {cx.marked = "property"; return cont();} + } + function objprop(type, value) { + if (type == "async") { + cx.marked = "property"; + return cont(objprop); + } else if (type == "variable" || cx.style == "keyword") { + cx.marked = "property"; + if (value == "get" || value == "set") return cont(getterSetter); + var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params + if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false))) + cx.state.fatArrowAt = cx.stream.pos + m[0].length + return cont(afterprop); + } else if (type == "number" || type == "string") { + cx.marked = jsonldMode ? "property" : (cx.style + " property"); + return cont(afterprop); + } else if (type == "jsonld-keyword") { + return cont(afterprop); + } else if (isTS && isModifier(value)) { + cx.marked = "keyword" + return cont(objprop) + } else if (type == "[") { + return cont(expression, maybetype, expect("]"), afterprop); + } else if (type == "spread") { + return cont(expressionNoComma, afterprop); + } else if (value == "*") { + cx.marked = "keyword"; + return cont(objprop); + } else if (type == ":") { + return pass(afterprop) + } + } + function getterSetter(type) { + if (type != "variable") return pass(afterprop); + cx.marked = "property"; + return cont(functiondef); + } + function afterprop(type) { + if (type == ":") return cont(expressionNoComma); + if (type == "(") return pass(functiondef); + } + function commasep(what, end, sep) { + function proceed(type, value) { + if (sep ? sep.indexOf(type) > -1 : type == ",") { + var lex = cx.state.lexical; + if (lex.info == "call") lex.pos = (lex.pos || 0) + 1; + return cont(function(type, value) { + if (type == end || value == end) return pass() + return pass(what) + }, proceed); + } + if (type == end || value == end) return cont(); + if (sep && sep.indexOf(";") > -1) return pass(what) + return cont(expect(end)); + } + return function(type, value) { + if (type == end || value == end) return cont(); + return pass(what, proceed); + }; + } + function contCommasep(what, end, info) { + for (var i = 3; i < arguments.length; i++) + cx.cc.push(arguments[i]); + return cont(pushlex(end, info), commasep(what, end), poplex); + } + function block(type) { + if (type == "}") return cont(); + return pass(statement, block); + } + function maybetype(type, value) { + if (isTS) { + if (type == ":") return cont(typeexpr); + if (value == "?") return cont(maybetype); + } + } + function maybetypeOrIn(type, value) { + if (isTS && (type == ":" || value == "in")) return cont(typeexpr) + } + function mayberettype(type) { + if (isTS && type == ":") { + if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr) + else return cont(typeexpr) + } + } + function isKW(_, value) { + if (value == "is") { + cx.marked = "keyword" + return cont() + } + } + function typeexpr(type, value) { + if (value == "keyof" || value == "typeof" || value == "infer" || value == "readonly") { + cx.marked = "keyword" + return cont(value == "typeof" ? expressionNoComma : typeexpr) + } + if (type == "variable" || value == "void") { + cx.marked = "type" + return cont(afterType) + } + if (value == "|" || value == "&") return cont(typeexpr) + if (type == "string" || type == "number" || type == "atom") return cont(afterType); + if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType) + if (type == "{") return cont(pushlex("}"), typeprops, poplex, afterType) + if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType, afterType) + if (type == "<") return cont(commasep(typeexpr, ">"), typeexpr) + if (type == "quasi") { return pass(quasiType, afterType); } + } + function maybeReturnType(type) { + if (type == "=>") return cont(typeexpr) + } + function typeprops(type) { + if (type.match(/[\}\)\]]/)) return cont() + if (type == "," || type == ";") return cont(typeprops) + return pass(typeprop, typeprops) + } + function typeprop(type, value) { + if (type == "variable" || cx.style == "keyword") { + cx.marked = "property" + return cont(typeprop) + } else if (value == "?" || type == "number" || type == "string") { + return cont(typeprop) + } else if (type == ":") { + return cont(typeexpr) + } else if (type == "[") { + return cont(expect("variable"), maybetypeOrIn, expect("]"), typeprop) + } else if (type == "(") { + return pass(functiondecl, typeprop) + } else if (!type.match(/[;\}\)\],]/)) { + return cont() + } + } + function quasiType(type, value) { + if (type != "quasi") return pass(); + if (value.slice(value.length - 2) != "${") return cont(quasiType); + return cont(typeexpr, continueQuasiType); + } + function continueQuasiType(type) { + if (type == "}") { + cx.marked = "string-2"; + cx.state.tokenize = tokenQuasi; + return cont(quasiType); + } + } + function typearg(type, value) { + if (type == "variable" && cx.stream.match(/^\s*[?:]/, false) || value == "?") return cont(typearg) + if (type == ":") return cont(typeexpr) + if (type == "spread") return cont(typearg) + return pass(typeexpr) + } + function afterType(type, value) { + if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) + if (value == "|" || type == "." || value == "&") return cont(typeexpr) + if (type == "[") return cont(typeexpr, expect("]"), afterType) + if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) } + if (value == "?") return cont(typeexpr, expect(":"), typeexpr) + } + function maybeTypeArgs(_, value) { + if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) + } + function typeparam() { + return pass(typeexpr, maybeTypeDefault) + } + function maybeTypeDefault(_, value) { + if (value == "=") return cont(typeexpr) + } + function vardef(_, value) { + if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)} + return pass(pattern, maybetype, maybeAssign, vardefCont); + } + function pattern(type, value) { + if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) } + if (type == "variable") { register(value); return cont(); } + if (type == "spread") return cont(pattern); + if (type == "[") return contCommasep(eltpattern, "]"); + if (type == "{") return contCommasep(proppattern, "}"); + } + function proppattern(type, value) { + if (type == "variable" && !cx.stream.match(/^\s*:/, false)) { + register(value); + return cont(maybeAssign); + } + if (type == "variable") cx.marked = "property"; + if (type == "spread") return cont(pattern); + if (type == "}") return pass(); + if (type == "[") return cont(expression, expect(']'), expect(':'), proppattern); + return cont(expect(":"), pattern, maybeAssign); + } + function eltpattern() { + return pass(pattern, maybeAssign) + } + function maybeAssign(_type, value) { + if (value == "=") return cont(expressionNoComma); + } + function vardefCont(type) { + if (type == ",") return cont(vardef); + } + function maybeelse(type, value) { + if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex); + } + function forspec(type, value) { + if (value == "await") return cont(forspec); + if (type == "(") return cont(pushlex(")"), forspec1, poplex); + } + function forspec1(type) { + if (type == "var") return cont(vardef, forspec2); + if (type == "variable") return cont(forspec2); + return pass(forspec2) + } + function forspec2(type, value) { + if (type == ")") return cont() + if (type == ";") return cont(forspec2) + if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression, forspec2) } + return pass(expression, forspec2) + } + function functiondef(type, value) { + if (value == "*") {cx.marked = "keyword"; return cont(functiondef);} + if (type == "variable") {register(value); return cont(functiondef);} + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext); + if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef) + } + function functiondecl(type, value) { + if (value == "*") {cx.marked = "keyword"; return cont(functiondecl);} + if (type == "variable") {register(value); return cont(functiondecl);} + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, popcontext); + if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondecl) + } + function typename(type, value) { + if (type == "keyword" || type == "variable") { + cx.marked = "type" + return cont(typename) + } else if (value == "<") { + return cont(pushlex(">"), commasep(typeparam, ">"), poplex) + } + } + function funarg(type, value) { + if (value == "@") cont(expression, funarg) + if (type == "spread") return cont(funarg); + if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); } + if (isTS && type == "this") return cont(maybetype, maybeAssign) + return pass(pattern, maybetype, maybeAssign); + } + function classExpression(type, value) { + // Class expressions may have an optional name. + if (type == "variable") return className(type, value); + return classNameAfter(type, value); + } + function className(type, value) { + if (type == "variable") {register(value); return cont(classNameAfter);} + } + function classNameAfter(type, value) { + if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter) + if (value == "extends" || value == "implements" || (isTS && type == ",")) { + if (value == "implements") cx.marked = "keyword"; + return cont(isTS ? typeexpr : expression, classNameAfter); + } + if (type == "{") return cont(pushlex("}"), classBody, poplex); + } + function classBody(type, value) { + if (type == "async" || + (type == "variable" && + (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) && + cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) { + cx.marked = "keyword"; + return cont(classBody); + } + if (type == "variable" || cx.style == "keyword") { + cx.marked = "property"; + return cont(classfield, classBody); + } + if (type == "number" || type == "string") return cont(classfield, classBody); + if (type == "[") + return cont(expression, maybetype, expect("]"), classfield, classBody) + if (value == "*") { + cx.marked = "keyword"; + return cont(classBody); + } + if (isTS && type == "(") return pass(functiondecl, classBody) + if (type == ";" || type == ",") return cont(classBody); + if (type == "}") return cont(); + if (value == "@") return cont(expression, classBody) + } + function classfield(type, value) { + if (value == "!") return cont(classfield) + if (value == "?") return cont(classfield) + if (type == ":") return cont(typeexpr, maybeAssign) + if (value == "=") return cont(expressionNoComma) + var context = cx.state.lexical.prev, isInterface = context && context.info == "interface" + return pass(isInterface ? functiondecl : functiondef) + } + function afterExport(type, value) { + if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); } + if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); } + if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";")); + return pass(statement); + } + function exportField(type, value) { + if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); } + if (type == "variable") return pass(expressionNoComma, exportField); + } + function afterImport(type) { + if (type == "string") return cont(); + if (type == "(") return pass(expression); + if (type == ".") return pass(maybeoperatorComma); + return pass(importSpec, maybeMoreImports, maybeFrom); + } + function importSpec(type, value) { + if (type == "{") return contCommasep(importSpec, "}"); + if (type == "variable") register(value); + if (value == "*") cx.marked = "keyword"; + return cont(maybeAs); + } + function maybeMoreImports(type) { + if (type == ",") return cont(importSpec, maybeMoreImports) + } + function maybeAs(_type, value) { + if (value == "as") { cx.marked = "keyword"; return cont(importSpec); } + } + function maybeFrom(_type, value) { + if (value == "from") { cx.marked = "keyword"; return cont(expression); } + } + function arrayLiteral(type) { + if (type == "]") return cont(); + return pass(commasep(expressionNoComma, "]")); + } + function enumdef() { + return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex) + } + function enummember() { + return pass(pattern, maybeAssign); + } + + function isContinuedStatement(state, textAfter) { + return state.lastType == "operator" || state.lastType == "," || + isOperatorChar.test(textAfter.charAt(0)) || + /[,.]/.test(textAfter.charAt(0)); + } + + function expressionAllowed(stream, state, backUp) { + return state.tokenize == tokenBase && + /^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) || + (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0)))) + } + + // Interface + + return { + startState: function(basecolumn) { + var state = { + tokenize: tokenBase, + lastType: "sof", + cc: [], + lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false), + localVars: parserConfig.localVars, + context: parserConfig.localVars && new Context(null, null, false), + indented: basecolumn || 0 + }; + if (parserConfig.globalVars && typeof parserConfig.globalVars == "object") + state.globalVars = parserConfig.globalVars; + return state; + }, + + token: function(stream, state) { + if (stream.sol()) { + if (!state.lexical.hasOwnProperty("align")) + state.lexical.align = false; + state.indented = stream.indentation(); + findFatArrow(stream, state); + } + if (state.tokenize != tokenComment && stream.eatSpace()) return null; + var style = state.tokenize(stream, state); + if (type == "comment") return style; + state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type; + return parseJS(state, style, type, content, stream); + }, + + indent: function(state, textAfter) { + if (state.tokenize == tokenComment || state.tokenize == tokenQuasi) return CodeMirror.Pass; + if (state.tokenize != tokenBase) return 0; + var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top + // Kludge to prevent 'maybelse' from blocking lexical scope pops + if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) { + var c = state.cc[i]; + if (c == poplex) lexical = lexical.prev; + else if (c != maybeelse && c != popcontext) break; + } + while ((lexical.type == "stat" || lexical.type == "form") && + (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) && + (top == maybeoperatorComma || top == maybeoperatorNoComma) && + !/^[,\.=+\-*:?[\(]/.test(textAfter)))) + lexical = lexical.prev; + if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat") + lexical = lexical.prev; + var type = lexical.type, closing = firstChar == type; + + if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info.length + 1 : 0); + else if (type == "form" && firstChar == "{") return lexical.indented; + else if (type == "form") return lexical.indented + indentUnit; + else if (type == "stat") + return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0); + else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false) + return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit); + else if (lexical.align) return lexical.column + (closing ? 0 : 1); + else return lexical.indented + (closing ? 0 : indentUnit); + }, + + electricInput: /^\s*(?:case .*?:|default:|\{|\})$/, + blockCommentStart: jsonMode ? null : "/*", + blockCommentEnd: jsonMode ? null : "*/", + blockCommentContinue: jsonMode ? null : " * ", + lineComment: jsonMode ? null : "//", + fold: "brace", + closeBrackets: "()[]{}''\"\"``", + + helperType: jsonMode ? "json" : "javascript", + jsonldMode: jsonldMode, + jsonMode: jsonMode, + + expressionAllowed: expressionAllowed, + + skipExpression: function(state) { + parseJS(state, "atom", "atom", "true", new CodeMirror.StringStream("", 2, null)) + } + }; +}); + +CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/); + +CodeMirror.defineMIME("text/javascript", "javascript"); +CodeMirror.defineMIME("text/ecmascript", "javascript"); +CodeMirror.defineMIME("application/javascript", "javascript"); +CodeMirror.defineMIME("application/x-javascript", "javascript"); +CodeMirror.defineMIME("application/ecmascript", "javascript"); +CodeMirror.defineMIME("application/json", { name: "javascript", json: true }); +CodeMirror.defineMIME("application/x-json", { name: "javascript", json: true }); +CodeMirror.defineMIME("application/manifest+json", { name: "javascript", json: true }) +CodeMirror.defineMIME("application/ld+json", { name: "javascript", jsonld: true }); +CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true }); +CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true }); +}); + + +/* +file none +*/ +/*jslint-enable*/ diff --git a/asset-codemirror-v5.58.3-rollup.css b/asset-codemirror-v5.58.3-rollup.css new file mode 100644 index 000000000..26c68b54d --- /dev/null +++ b/asset-codemirror-v5.58.3-rollup.css @@ -0,0 +1,378 @@ +/*jslint-disable*/ +/* +shRawLibFetch +{ + "fetchList": [ + { + "url": "https://github.com/codemirror/CodeMirror/blob/5.58.3/lib/codemirror.css" + } + ] +} +*/ + + +/* +repo https://github.com/codemirror/CodeMirror/tree/5.58.3 +committed 2020-11-19T08:38:15Z +*/ + + +/* +file https://github.com/codemirror/CodeMirror/blob/5.58.3/lib/codemirror.css +*/ +/* BASICS */ + +.CodeMirror { + /* Set height, width, borders, and global font properties here */ + font-family: monospace; + height: 300px; + color: black; + direction: ltr; +} + +/* PADDING */ + +.CodeMirror-lines { + padding: 4px 0; /* Vertical padding around content */ +} +.CodeMirror pre.CodeMirror-line, +.CodeMirror pre.CodeMirror-line-like { + padding: 0 4px; /* Horizontal padding of content */ +} + +.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { + background-color: white; /* The little square between H and V scrollbars */ +} + +/* GUTTER */ + +.CodeMirror-gutters { + border-right: 1px solid #ddd; + background-color: #f7f7f7; + white-space: nowrap; +} +.CodeMirror-linenumbers {} +.CodeMirror-linenumber { + padding: 0 3px 0 5px; + min-width: 20px; + text-align: right; + color: #999; + white-space: nowrap; +} + +.CodeMirror-guttermarker { color: black; } +.CodeMirror-guttermarker-subtle { color: #999; } + +/* CURSOR */ + +.CodeMirror-cursor { + border-left: 1px solid black; + border-right: none; + width: 0; +} +/* Shown when moving in bi-directional text */ +.CodeMirror div.CodeMirror-secondarycursor { + border-left: 1px solid silver; +} +.cm-fat-cursor .CodeMirror-cursor { + width: auto; + border: 0 !important; + background: #7e7; +} +.cm-fat-cursor div.CodeMirror-cursors { + z-index: 1; +} +.cm-fat-cursor-mark { + background-color: rgba(20, 255, 20, 0.5); + -webkit-animation: blink 1.06s steps(1) infinite; + -moz-animation: blink 1.06s steps(1) infinite; + animation: blink 1.06s steps(1) infinite; +} +.cm-animate-fat-cursor { + width: auto; + border: 0; + -webkit-animation: blink 1.06s steps(1) infinite; + -moz-animation: blink 1.06s steps(1) infinite; + animation: blink 1.06s steps(1) infinite; + background-color: #7e7; +} +@-moz-keyframes blink { + 0% {} + 50% { background-color: transparent; } + 100% {} +} +@-webkit-keyframes blink { + 0% {} + 50% { background-color: transparent; } + 100% {} +} +@keyframes blink { + 0% {} + 50% { background-color: transparent; } + 100% {} +} + +/* Can style cursor different in overwrite (non-insert) mode */ +.CodeMirror-overwrite .CodeMirror-cursor {} + +.cm-tab { display: inline-block; text-decoration: inherit; } + +.CodeMirror-rulers { + position: absolute; + left: 0; right: 0; top: -50px; bottom: 0; + overflow: hidden; +} +.CodeMirror-ruler { + border-left: 1px solid #ccc; + top: 0; bottom: 0; + position: absolute; +} + +/* DEFAULT THEME */ + +.cm-s-default .cm-header {color: blue;} +.cm-s-default .cm-quote {color: #090;} +.cm-negative {color: #d44;} +.cm-positive {color: #292;} +.cm-header, .cm-strong {font-weight: bold;} +.cm-em {font-style: italic;} +.cm-link {text-decoration: underline;} +.cm-strikethrough {text-decoration: line-through;} + +.cm-s-default .cm-keyword {color: #708;} +.cm-s-default .cm-atom {color: #219;} +.cm-s-default .cm-number {color: #164;} +.cm-s-default .cm-def {color: #00f;} +.cm-s-default .cm-variable, +.cm-s-default .cm-punctuation, +.cm-s-default .cm-property, +.cm-s-default .cm-operator {} +.cm-s-default .cm-variable-2 {color: #05a;} +.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;} +.cm-s-default .cm-comment {color: #a50;} +.cm-s-default .cm-string {color: #a11;} +.cm-s-default .cm-string-2 {color: #f50;} +.cm-s-default .cm-meta {color: #555;} +.cm-s-default .cm-qualifier {color: #555;} +.cm-s-default .cm-builtin {color: #30a;} +.cm-s-default .cm-bracket {color: #997;} +.cm-s-default .cm-tag {color: #170;} +.cm-s-default .cm-attribute {color: #00c;} +.cm-s-default .cm-hr {color: #999;} +.cm-s-default .cm-link {color: #00c;} + +.cm-s-default .cm-error {color: #f00;} +.cm-invalidchar {color: #f00;} + +.CodeMirror-composing { border-bottom: 2px solid; } + +/* Default styles for common addons */ + +div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;} +div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} +.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } +.CodeMirror-activeline-background {background: #e8f2ff;} + +/* STOP */ + +/* The rest of this file contains styles related to the mechanics of + the editor. You probably shouldn't touch them. */ + +.CodeMirror { + position: relative; + overflow: hidden; + background: white; +} + +.CodeMirror-scroll { + overflow: scroll !important; /* Things will break if this is overridden */ + /* 50px is the magic margin used to hide the element's real scrollbars */ + /* See overflow: hidden in .CodeMirror */ + margin-bottom: -50px; margin-right: -50px; + padding-bottom: 50px; + height: 100%; + outline: none; /* Prevent dragging from highlighting the element */ + position: relative; +} +.CodeMirror-sizer { + position: relative; + border-right: 50px solid transparent; +} + +/* The fake, visible scrollbars. Used to force redraw during scrolling + before actual scrolling happens, thus preventing shaking and + flickering artifacts. */ +.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { + position: absolute; + z-index: 6; + display: none; + outline: none; +} +.CodeMirror-vscrollbar { + right: 0; top: 0; + overflow-x: hidden; + overflow-y: scroll; +} +.CodeMirror-hscrollbar { + bottom: 0; left: 0; + overflow-y: hidden; + overflow-x: scroll; +} +.CodeMirror-scrollbar-filler { + right: 0; bottom: 0; +} +.CodeMirror-gutter-filler { + left: 0; bottom: 0; +} + +.CodeMirror-gutters { + position: absolute; left: 0; top: 0; + min-height: 100%; + z-index: 3; +} +.CodeMirror-gutter { + white-space: normal; + height: 100%; + display: inline-block; + vertical-align: top; + margin-bottom: -50px; +} +.CodeMirror-gutter-wrapper { + position: absolute; + z-index: 4; + background: none !important; + border: none !important; +} +.CodeMirror-gutter-background { + position: absolute; + top: 0; bottom: 0; + z-index: 4; +} +.CodeMirror-gutter-elt { + position: absolute; + cursor: default; + z-index: 4; +} +.CodeMirror-gutter-wrapper ::selection { background-color: transparent } +.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent } + +.CodeMirror-lines { + cursor: text; + min-height: 1px; /* prevents collapsing before first draw */ +} +.CodeMirror pre.CodeMirror-line, +.CodeMirror pre.CodeMirror-line-like { + /* Reset some styles that the rest of the page might have set */ + -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; + border-width: 0; + background: transparent; + font-family: inherit; + font-size: inherit; + margin: 0; + white-space: pre; + word-wrap: normal; + line-height: inherit; + color: inherit; + z-index: 2; + position: relative; + overflow: visible; + -webkit-tap-highlight-color: transparent; + -webkit-font-variant-ligatures: contextual; + font-variant-ligatures: contextual; +} +.CodeMirror-wrap pre.CodeMirror-line, +.CodeMirror-wrap pre.CodeMirror-line-like { + word-wrap: break-word; + white-space: pre-wrap; + word-break: normal; +} + +.CodeMirror-linebackground { + position: absolute; + left: 0; right: 0; top: 0; bottom: 0; + z-index: 0; +} + +.CodeMirror-linewidget { + position: relative; + z-index: 2; + padding: 0.1px; /* Force widget margins to stay inside of the container */ +} + +.CodeMirror-widget {} + +.CodeMirror-rtl pre { direction: rtl; } + +.CodeMirror-code { + outline: none; +} + +/* Force content-box sizing for the elements where we expect it */ +.CodeMirror-scroll, +.CodeMirror-sizer, +.CodeMirror-gutter, +.CodeMirror-gutters, +.CodeMirror-linenumber { + -moz-box-sizing: content-box; + box-sizing: content-box; +} + +.CodeMirror-measure { + position: absolute; + width: 100%; + height: 0; + overflow: hidden; + visibility: hidden; +} + +.CodeMirror-cursor { + position: absolute; + pointer-events: none; +} +.CodeMirror-measure pre { position: static; } + +div.CodeMirror-cursors { + visibility: hidden; + position: relative; + z-index: 3; +} +div.CodeMirror-dragcursors { + visibility: visible; +} + +.CodeMirror-focused div.CodeMirror-cursors { + visibility: visible; +} + +.CodeMirror-selected { background: #d9d9d9; } +.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } +.CodeMirror-crosshair { cursor: crosshair; } +.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } +.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } + +.cm-searching { + background-color: #ffa; + background-color: rgba(255, 255, 0, .4); +} + +/* Used to force a border model for a node */ +.cm-force-border { padding-right: .1px; } + +@media print { + /* Hide the cursor when printing */ + .CodeMirror div.CodeMirror-cursors { + visibility: hidden; + } +} + +/* See issue #2901 */ +.cm-tab-wrap-hack:after { content: ''; } + +/* Help users use markselection to safely style text background */ +span.CodeMirror-selectedtext { background: none; } + + +/* +file none +*/ +/*jslint-enable*/ diff --git a/asset-codemirror-v5.58.3-rollup.js b/asset-codemirror-v5.58.3-rollup.js new file mode 100644 index 000000000..1c84e2f6e --- /dev/null +++ b/asset-codemirror-v5.58.3-rollup.js @@ -0,0 +1,10966 @@ +/*jslint-disable*/ +/* +shRawLibFetch +{ + "fetchList": [ + { + "url": "https://github.com/codemirror/CodeMirror/blob/5.58.3/codemirror.js", + "url2": "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.3/codemirror.js" + }, + { + "url": "https://github.com/codemirror/CodeMirror/blob/5.58.3/addon/edit/matchbrackets.js" + }, + { + "url": "https://github.com/codemirror/CodeMirror/blob/5.58.3/addon/edit/trailingspace.js" + }, + { + "url": "https://github.com/codemirror/CodeMirror/blob/5.58.3/mode/javascript/javascript.js" + } + ] +} +*/ + + +/* +repo https://github.com/codemirror/CodeMirror/tree/5.58.3 +committed 2020-11-19T08:38:15Z +*/ + + +/* +file https://github.com/codemirror/CodeMirror/blob/5.58.3/codemirror.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +// This is CodeMirror (https://codemirror.net), a code editor +// implemented in JavaScript on top of the browser's DOM. +// +// You can find some technical background for some of the code below +// at http://marijnhaverbeke.nl/blog/#cm-internals . + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, global.CodeMirror = factory()); +}(this, (function () { 'use strict'; + + // Kludges for bugs and behavior differences that can't be feature + // detected are enabled based on userAgent etc sniffing. + var userAgent = navigator.userAgent; + var platform = navigator.platform; + + var gecko = /gecko\/\d/i.test(userAgent); + var ie_upto10 = /MSIE \d/.test(userAgent); + var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent); + var edge = /Edge\/(\d+)/.exec(userAgent); + var ie = ie_upto10 || ie_11up || edge; + var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]); + var webkit = !edge && /WebKit\//.test(userAgent); + var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent); + var chrome = !edge && /Chrome\//.test(userAgent); + var presto = /Opera\//.test(userAgent); + var safari = /Apple Computer/.test(navigator.vendor); + var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent); + var phantom = /PhantomJS/.test(userAgent); + + var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent); + var android = /Android/.test(userAgent); + // This is woefully incomplete. Suggestions for alternative methods welcome. + var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent); + var mac = ios || /Mac/.test(platform); + var chromeOS = /\bCrOS\b/.test(userAgent); + var windows = /win/i.test(platform); + + var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/); + if (presto_version) { presto_version = Number(presto_version[1]); } + if (presto_version && presto_version >= 15) { presto = false; webkit = true; } + // Some browsers use the wrong event properties to signal cmd/ctrl on OS X + var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11)); + var captureRightClick = gecko || (ie && ie_version >= 9); + + function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") } + + var rmClass = function(node, cls) { + var current = node.className; + var match = classTest(cls).exec(current); + if (match) { + var after = current.slice(match.index + match[0].length); + node.className = current.slice(0, match.index) + (after ? match[1] + after : ""); + } + }; + + function removeChildren(e) { + for (var count = e.childNodes.length; count > 0; --count) + { e.removeChild(e.firstChild); } + return e + } + + function removeChildrenAndAdd(parent, e) { + return removeChildren(parent).appendChild(e) + } + + function elt(tag, content, className, style) { + var e = document.createElement(tag); + if (className) { e.className = className; } + if (style) { e.style.cssText = style; } + if (typeof content == "string") { e.appendChild(document.createTextNode(content)); } + else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } } + return e + } + // wrapper for elt, which removes the elt from the accessibility tree + function eltP(tag, content, className, style) { + var e = elt(tag, content, className, style); + e.setAttribute("role", "presentation"); + return e + } + + var range; + if (document.createRange) { range = function(node, start, end, endNode) { + var r = document.createRange(); + r.setEnd(endNode || node, end); + r.setStart(node, start); + return r + }; } + else { range = function(node, start, end) { + var r = document.body.createTextRange(); + try { r.moveToElementText(node.parentNode); } + catch(e) { return r } + r.collapse(true); + r.moveEnd("character", end); + r.moveStart("character", start); + return r + }; } + + function contains(parent, child) { + if (child.nodeType == 3) // Android browser always returns false when child is a textnode + { child = child.parentNode; } + if (parent.contains) + { return parent.contains(child) } + do { + if (child.nodeType == 11) { child = child.host; } + if (child == parent) { return true } + } while (child = child.parentNode) + } + + function activeElt() { + // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement. + // IE < 10 will throw when accessed while the page is loading or in an iframe. + // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable. + var activeElement; + try { + activeElement = document.activeElement; + } catch(e) { + activeElement = document.body || null; + } + while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement) + { activeElement = activeElement.shadowRoot.activeElement; } + return activeElement + } + + function addClass(node, cls) { + var current = node.className; + if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; } + } + function joinClasses(a, b) { + var as = a.split(" "); + for (var i = 0; i < as.length; i++) + { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } } + return b + } + + var selectInput = function(node) { node.select(); }; + if (ios) // Mobile Safari apparently has a bug where select() is broken. + { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; } + else if (ie) // Suppress mysterious IE10 errors + { selectInput = function(node) { try { node.select(); } catch(_e) {} }; } + + function bind(f) { + var args = Array.prototype.slice.call(arguments, 1); + return function(){return f.apply(null, args)} + } + + function copyObj(obj, target, overwrite) { + if (!target) { target = {}; } + for (var prop in obj) + { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop))) + { target[prop] = obj[prop]; } } + return target + } + + // Counts the column offset in a string, taking tabs into account. + // Used mostly to find indentation. + function countColumn(string, end, tabSize, startIndex, startValue) { + if (end == null) { + end = string.search(/[^\s\u00a0]/); + if (end == -1) { end = string.length; } + } + for (var i = startIndex || 0, n = startValue || 0;;) { + var nextTab = string.indexOf("\t", i); + if (nextTab < 0 || nextTab >= end) + { return n + (end - i) } + n += nextTab - i; + n += tabSize - (n % tabSize); + i = nextTab + 1; + } + } + + var Delayed = function() { + this.id = null; + this.f = null; + this.time = 0; + this.handler = bind(this.onTimeout, this); + }; + Delayed.prototype.onTimeout = function (self) { + self.id = 0; + if (self.time <= +new Date) { + self.f(); + } else { + setTimeout(self.handler, self.time - +new Date); + } + }; + Delayed.prototype.set = function (ms, f) { + this.f = f; + var time = +new Date + ms; + if (!this.id || time < this.time) { + clearTimeout(this.id); + this.id = setTimeout(this.handler, ms); + this.time = time; + } + }; + + function indexOf(array, elt) { + for (var i = 0; i < array.length; ++i) + { if (array[i] == elt) { return i } } + return -1 + } + + // Number of pixels added to scroller and sizer to hide scrollbar + var scrollerGap = 50; + + // Returned or thrown by various protocols to signal 'I'm not + // handling this'. + var Pass = {toString: function(){return "CodeMirror.Pass"}}; + + // Reused option objects for setSelection & friends + var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"}; + + // The inverse of countColumn -- find the offset that corresponds to + // a particular column. + function findColumn(string, goal, tabSize) { + for (var pos = 0, col = 0;;) { + var nextTab = string.indexOf("\t", pos); + if (nextTab == -1) { nextTab = string.length; } + var skipped = nextTab - pos; + if (nextTab == string.length || col + skipped >= goal) + { return pos + Math.min(skipped, goal - col) } + col += nextTab - pos; + col += tabSize - (col % tabSize); + pos = nextTab + 1; + if (col >= goal) { return pos } + } + } + + var spaceStrs = [""]; + function spaceStr(n) { + while (spaceStrs.length <= n) + { spaceStrs.push(lst(spaceStrs) + " "); } + return spaceStrs[n] + } + + function lst(arr) { return arr[arr.length-1] } + + function map(array, f) { + var out = []; + for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); } + return out + } + + function insertSorted(array, value, score) { + var pos = 0, priority = score(value); + while (pos < array.length && score(array[pos]) <= priority) { pos++; } + array.splice(pos, 0, value); + } + + function nothing() {} + + function createObj(base, props) { + var inst; + if (Object.create) { + inst = Object.create(base); + } else { + nothing.prototype = base; + inst = new nothing(); + } + if (props) { copyObj(props, inst); } + return inst + } + + var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; + function isWordCharBasic(ch) { + return /\w/.test(ch) || ch > "\x80" && + (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)) + } + function isWordChar(ch, helper) { + if (!helper) { return isWordCharBasic(ch) } + if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true } + return helper.test(ch) + } + + function isEmpty(obj) { + for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } } + return true + } + + // Extending unicode characters. A series of a non-extending char + + // any number of extending chars is treated as a single unit as far + // as editing and measuring is concerned. This is not fully correct, + // since some scripts/fonts/browsers also treat other configurations + // of code points as a group. + var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/; + function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) } + + // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range. + function skipExtendingChars(str, pos, dir) { + while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; } + return pos + } + + // Returns the value from the range [`from`; `to`] that satisfies + // `pred` and is closest to `from`. Assumes that at least `to` + // satisfies `pred`. Supports `from` being greater than `to`. + function findFirst(pred, from, to) { + // At any point we are certain `to` satisfies `pred`, don't know + // whether `from` does. + var dir = from > to ? -1 : 1; + for (;;) { + if (from == to) { return from } + var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF); + if (mid == from) { return pred(mid) ? from : to } + if (pred(mid)) { to = mid; } + else { from = mid + dir; } + } + } + + // BIDI HELPERS + + function iterateBidiSections(order, from, to, f) { + if (!order) { return f(from, to, "ltr", 0) } + var found = false; + for (var i = 0; i < order.length; ++i) { + var part = order[i]; + if (part.from < to && part.to > from || from == to && part.to == from) { + f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i); + found = true; + } + } + if (!found) { f(from, to, "ltr"); } + } + + var bidiOther = null; + function getBidiPartAt(order, ch, sticky) { + var found; + bidiOther = null; + for (var i = 0; i < order.length; ++i) { + var cur = order[i]; + if (cur.from < ch && cur.to > ch) { return i } + if (cur.to == ch) { + if (cur.from != cur.to && sticky == "before") { found = i; } + else { bidiOther = i; } + } + if (cur.from == ch) { + if (cur.from != cur.to && sticky != "before") { found = i; } + else { bidiOther = i; } + } + } + return found != null ? found : bidiOther + } + + // Bidirectional ordering algorithm + // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm + // that this (partially) implements. + + // One-char codes used for character types: + // L (L): Left-to-Right + // R (R): Right-to-Left + // r (AL): Right-to-Left Arabic + // 1 (EN): European Number + // + (ES): European Number Separator + // % (ET): European Number Terminator + // n (AN): Arabic Number + // , (CS): Common Number Separator + // m (NSM): Non-Spacing Mark + // b (BN): Boundary Neutral + // s (B): Paragraph Separator + // t (S): Segment Separator + // w (WS): Whitespace + // N (ON): Other Neutrals + + // Returns null if characters are ordered as they appear + // (left-to-right), or an array of sections ({from, to, level} + // objects) in the order in which they occur visually. + var bidiOrdering = (function() { + // Character types for codepoints 0 to 0xff + var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"; + // Character types for codepoints 0x600 to 0x6f9 + var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111"; + function charType(code) { + if (code <= 0xf7) { return lowTypes.charAt(code) } + else if (0x590 <= code && code <= 0x5f4) { return "R" } + else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) } + else if (0x6ee <= code && code <= 0x8ac) { return "r" } + else if (0x2000 <= code && code <= 0x200b) { return "w" } + else if (code == 0x200c) { return "b" } + else { return "L" } + } + + var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/; + var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/; + + function BidiSpan(level, from, to) { + this.level = level; + this.from = from; this.to = to; + } + + return function(str, direction) { + var outerType = direction == "ltr" ? "L" : "R"; + + if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false } + var len = str.length, types = []; + for (var i = 0; i < len; ++i) + { types.push(charType(str.charCodeAt(i))); } + + // W1. Examine each non-spacing mark (NSM) in the level run, and + // change the type of the NSM to the type of the previous + // character. If the NSM is at the start of the level run, it will + // get the type of sor. + for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) { + var type = types[i$1]; + if (type == "m") { types[i$1] = prev; } + else { prev = type; } + } + + // W2. Search backwards from each instance of a European number + // until the first strong type (R, L, AL, or sor) is found. If an + // AL is found, change the type of the European number to Arabic + // number. + // W3. Change all ALs to R. + for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) { + var type$1 = types[i$2]; + if (type$1 == "1" && cur == "r") { types[i$2] = "n"; } + else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } } + } + + // W4. A single European separator between two European numbers + // changes to a European number. A single common separator between + // two numbers of the same type changes to that type. + for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) { + var type$2 = types[i$3]; + if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; } + else if (type$2 == "," && prev$1 == types[i$3+1] && + (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; } + prev$1 = type$2; + } + + // W5. A sequence of European terminators adjacent to European + // numbers changes to all European numbers. + // W6. Otherwise, separators and terminators change to Other + // Neutral. + for (var i$4 = 0; i$4 < len; ++i$4) { + var type$3 = types[i$4]; + if (type$3 == ",") { types[i$4] = "N"; } + else if (type$3 == "%") { + var end = (void 0); + for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {} + var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"; + for (var j = i$4; j < end; ++j) { types[j] = replace; } + i$4 = end - 1; + } + } + + // W7. Search backwards from each instance of a European number + // until the first strong type (R, L, or sor) is found. If an L is + // found, then change the type of the European number to L. + for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) { + var type$4 = types[i$5]; + if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; } + else if (isStrong.test(type$4)) { cur$1 = type$4; } + } + + // N1. A sequence of neutrals takes the direction of the + // surrounding strong text if the text on both sides has the same + // direction. European and Arabic numbers act as if they were R in + // terms of their influence on neutrals. Start-of-level-run (sor) + // and end-of-level-run (eor) are used at level run boundaries. + // N2. Any remaining neutrals take the embedding direction. + for (var i$6 = 0; i$6 < len; ++i$6) { + if (isNeutral.test(types[i$6])) { + var end$1 = (void 0); + for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {} + var before = (i$6 ? types[i$6-1] : outerType) == "L"; + var after = (end$1 < len ? types[end$1] : outerType) == "L"; + var replace$1 = before == after ? (before ? "L" : "R") : outerType; + for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; } + i$6 = end$1 - 1; + } + } + + // Here we depart from the documented algorithm, in order to avoid + // building up an actual levels array. Since there are only three + // levels (0, 1, 2) in an implementation that doesn't take + // explicit embedding into account, we can build up the order on + // the fly, without following the level-based algorithm. + var order = [], m; + for (var i$7 = 0; i$7 < len;) { + if (countsAsLeft.test(types[i$7])) { + var start = i$7; + for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {} + order.push(new BidiSpan(0, start, i$7)); + } else { + var pos = i$7, at = order.length, isRTL = direction == "rtl" ? 1 : 0; + for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {} + for (var j$2 = pos; j$2 < i$7;) { + if (countsAsNum.test(types[j$2])) { + if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); at += isRTL; } + var nstart = j$2; + for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {} + order.splice(at, 0, new BidiSpan(2, nstart, j$2)); + at += isRTL; + pos = j$2; + } else { ++j$2; } + } + if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); } + } + } + if (direction == "ltr") { + if (order[0].level == 1 && (m = str.match(/^\s+/))) { + order[0].from = m[0].length; + order.unshift(new BidiSpan(0, 0, m[0].length)); + } + if (lst(order).level == 1 && (m = str.match(/\s+$/))) { + lst(order).to -= m[0].length; + order.push(new BidiSpan(0, len - m[0].length, len)); + } + } + + return direction == "rtl" ? order.reverse() : order + } + })(); + + // Get the bidi ordering for the given line (and cache it). Returns + // false for lines that are fully left-to-right, and an array of + // BidiSpan objects otherwise. + function getOrder(line, direction) { + var order = line.order; + if (order == null) { order = line.order = bidiOrdering(line.text, direction); } + return order + } + + // EVENT HANDLING + + // Lightweight event framework. on/off also work on DOM nodes, + // registering native DOM handlers. + + var noHandlers = []; + + var on = function(emitter, type, f) { + if (emitter.addEventListener) { + emitter.addEventListener(type, f, false); + } else if (emitter.attachEvent) { + emitter.attachEvent("on" + type, f); + } else { + var map = emitter._handlers || (emitter._handlers = {}); + map[type] = (map[type] || noHandlers).concat(f); + } + }; + + function getHandlers(emitter, type) { + return emitter._handlers && emitter._handlers[type] || noHandlers + } + + function off(emitter, type, f) { + if (emitter.removeEventListener) { + emitter.removeEventListener(type, f, false); + } else if (emitter.detachEvent) { + emitter.detachEvent("on" + type, f); + } else { + var map = emitter._handlers, arr = map && map[type]; + if (arr) { + var index = indexOf(arr, f); + if (index > -1) + { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)); } + } + } + } + + function signal(emitter, type /*, values...*/) { + var handlers = getHandlers(emitter, type); + if (!handlers.length) { return } + var args = Array.prototype.slice.call(arguments, 2); + for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); } + } + + // The DOM events that CodeMirror handles can be overridden by + // registering a (non-DOM) handler on the editor for the event name, + // and preventDefault-ing the event in that handler. + function signalDOMEvent(cm, e, override) { + if (typeof e == "string") + { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; } + signal(cm, override || e.type, cm, e); + return e_defaultPrevented(e) || e.codemirrorIgnore + } + + function signalCursorActivity(cm) { + var arr = cm._handlers && cm._handlers.cursorActivity; + if (!arr) { return } + var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []); + for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1) + { set.push(arr[i]); } } + } + + function hasHandler(emitter, type) { + return getHandlers(emitter, type).length > 0 + } + + // Add on and off methods to a constructor's prototype, to make + // registering events on such objects more convenient. + function eventMixin(ctor) { + ctor.prototype.on = function(type, f) {on(this, type, f);}; + ctor.prototype.off = function(type, f) {off(this, type, f);}; + } + + // Due to the fact that we still support jurassic IE versions, some + // compatibility wrappers are needed. + + function e_preventDefault(e) { + if (e.preventDefault) { e.preventDefault(); } + else { e.returnValue = false; } + } + function e_stopPropagation(e) { + if (e.stopPropagation) { e.stopPropagation(); } + else { e.cancelBubble = true; } + } + function e_defaultPrevented(e) { + return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false + } + function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);} + + function e_target(e) {return e.target || e.srcElement} + function e_button(e) { + var b = e.which; + if (b == null) { + if (e.button & 1) { b = 1; } + else if (e.button & 2) { b = 3; } + else if (e.button & 4) { b = 2; } + } + if (mac && e.ctrlKey && b == 1) { b = 3; } + return b + } + + // Detect drag-and-drop + var dragAndDrop = function() { + // There is *some* kind of drag-and-drop support in IE6-8, but I + // couldn't get it to work yet. + if (ie && ie_version < 9) { return false } + var div = elt('div'); + return "draggable" in div || "dragDrop" in div + }(); + + var zwspSupported; + function zeroWidthElement(measure) { + if (zwspSupported == null) { + var test = elt("span", "\u200b"); + removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")])); + if (measure.firstChild.offsetHeight != 0) + { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); } + } + var node = zwspSupported ? elt("span", "\u200b") : + elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px"); + node.setAttribute("cm-text", ""); + return node + } + + // Feature-detect IE's crummy client rect reporting for bidi text + var badBidiRects; + function hasBadBidiRects(measure) { + if (badBidiRects != null) { return badBidiRects } + var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA")); + var r0 = range(txt, 0, 1).getBoundingClientRect(); + var r1 = range(txt, 1, 2).getBoundingClientRect(); + removeChildren(measure); + if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780) + return badBidiRects = (r1.right - r0.right < 3) + } + + // See if "".split is the broken IE version, if so, provide an + // alternative way to split lines. + var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) { + var pos = 0, result = [], l = string.length; + while (pos <= l) { + var nl = string.indexOf("\n", pos); + if (nl == -1) { nl = string.length; } + var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl); + var rt = line.indexOf("\r"); + if (rt != -1) { + result.push(line.slice(0, rt)); + pos += rt + 1; + } else { + result.push(line); + pos = nl + 1; + } + } + return result + } : function (string) { return string.split(/\r\n?|\n/); }; + + var hasSelection = window.getSelection ? function (te) { + try { return te.selectionStart != te.selectionEnd } + catch(e) { return false } + } : function (te) { + var range; + try {range = te.ownerDocument.selection.createRange();} + catch(e) {} + if (!range || range.parentElement() != te) { return false } + return range.compareEndPoints("StartToEnd", range) != 0 + }; + + var hasCopyEvent = (function () { + var e = elt("div"); + if ("oncopy" in e) { return true } + e.setAttribute("oncopy", "return;"); + return typeof e.oncopy == "function" + })(); + + var badZoomedRects = null; + function hasBadZoomedRects(measure) { + if (badZoomedRects != null) { return badZoomedRects } + var node = removeChildrenAndAdd(measure, elt("span", "x")); + var normal = node.getBoundingClientRect(); + var fromRange = range(node, 0, 1).getBoundingClientRect(); + return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1 + } + + // Known modes, by name and by MIME + var modes = {}, mimeModes = {}; + + // Extra arguments are stored as the mode's dependencies, which is + // used by (legacy) mechanisms like loadmode.js to automatically + // load a mode. (Preferred mechanism is the require/define calls.) + function defineMode(name, mode) { + if (arguments.length > 2) + { mode.dependencies = Array.prototype.slice.call(arguments, 2); } + modes[name] = mode; + } + + function defineMIME(mime, spec) { + mimeModes[mime] = spec; + } + + // Given a MIME type, a {name, ...options} config object, or a name + // string, return a mode config object. + function resolveMode(spec) { + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { + spec = mimeModes[spec]; + } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { + var found = mimeModes[spec.name]; + if (typeof found == "string") { found = {name: found}; } + spec = createObj(found, spec); + spec.name = found.name; + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { + return resolveMode("application/xml") + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) { + return resolveMode("application/json") + } + if (typeof spec == "string") { return {name: spec} } + else { return spec || {name: "null"} } + } + + // Given a mode spec (anything that resolveMode accepts), find and + // initialize an actual mode object. + function getMode(options, spec) { + spec = resolveMode(spec); + var mfactory = modes[spec.name]; + if (!mfactory) { return getMode(options, "text/plain") } + var modeObj = mfactory(options, spec); + if (modeExtensions.hasOwnProperty(spec.name)) { + var exts = modeExtensions[spec.name]; + for (var prop in exts) { + if (!exts.hasOwnProperty(prop)) { continue } + if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; } + modeObj[prop] = exts[prop]; + } + } + modeObj.name = spec.name; + if (spec.helperType) { modeObj.helperType = spec.helperType; } + if (spec.modeProps) { for (var prop$1 in spec.modeProps) + { modeObj[prop$1] = spec.modeProps[prop$1]; } } + + return modeObj + } + + // This can be used to attach properties to mode objects from + // outside the actual mode definition. + var modeExtensions = {}; + function extendMode(mode, properties) { + var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); + copyObj(properties, exts); + } + + function copyState(mode, state) { + if (state === true) { return state } + if (mode.copyState) { return mode.copyState(state) } + var nstate = {}; + for (var n in state) { + var val = state[n]; + if (val instanceof Array) { val = val.concat([]); } + nstate[n] = val; + } + return nstate + } + + // Given a mode and a state (for that mode), find the inner mode and + // state at the position that the state refers to. + function innerMode(mode, state) { + var info; + while (mode.innerMode) { + info = mode.innerMode(state); + if (!info || info.mode == mode) { break } + state = info.state; + mode = info.mode; + } + return info || {mode: mode, state: state} + } + + function startState(mode, a1, a2) { + return mode.startState ? mode.startState(a1, a2) : true + } + + // STRING STREAM + + // Fed to the mode parsers, provides helper functions to make + // parsers more succinct. + + var StringStream = function(string, tabSize, lineOracle) { + this.pos = this.start = 0; + this.string = string; + this.tabSize = tabSize || 8; + this.lastColumnPos = this.lastColumnValue = 0; + this.lineStart = 0; + this.lineOracle = lineOracle; + }; + + StringStream.prototype.eol = function () {return this.pos >= this.string.length}; + StringStream.prototype.sol = function () {return this.pos == this.lineStart}; + StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined}; + StringStream.prototype.next = function () { + if (this.pos < this.string.length) + { return this.string.charAt(this.pos++) } + }; + StringStream.prototype.eat = function (match) { + var ch = this.string.charAt(this.pos); + var ok; + if (typeof match == "string") { ok = ch == match; } + else { ok = ch && (match.test ? match.test(ch) : match(ch)); } + if (ok) {++this.pos; return ch} + }; + StringStream.prototype.eatWhile = function (match) { + var start = this.pos; + while (this.eat(match)){} + return this.pos > start + }; + StringStream.prototype.eatSpace = function () { + var start = this.pos; + while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; } + return this.pos > start + }; + StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;}; + StringStream.prototype.skipTo = function (ch) { + var found = this.string.indexOf(ch, this.pos); + if (found > -1) {this.pos = found; return true} + }; + StringStream.prototype.backUp = function (n) {this.pos -= n;}; + StringStream.prototype.column = function () { + if (this.lastColumnPos < this.start) { + this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue); + this.lastColumnPos = this.start; + } + return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) + }; + StringStream.prototype.indentation = function () { + return countColumn(this.string, null, this.tabSize) - + (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) + }; + StringStream.prototype.match = function (pattern, consume, caseInsensitive) { + if (typeof pattern == "string") { + var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; }; + var substr = this.string.substr(this.pos, pattern.length); + if (cased(substr) == cased(pattern)) { + if (consume !== false) { this.pos += pattern.length; } + return true + } + } else { + var match = this.string.slice(this.pos).match(pattern); + if (match && match.index > 0) { return null } + if (match && consume !== false) { this.pos += match[0].length; } + return match + } + }; + StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)}; + StringStream.prototype.hideFirstChars = function (n, inner) { + this.lineStart += n; + try { return inner() } + finally { this.lineStart -= n; } + }; + StringStream.prototype.lookAhead = function (n) { + var oracle = this.lineOracle; + return oracle && oracle.lookAhead(n) + }; + StringStream.prototype.baseToken = function () { + var oracle = this.lineOracle; + return oracle && oracle.baseToken(this.pos) + }; + + // Find the line object corresponding to the given line number. + function getLine(doc, n) { + n -= doc.first; + if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") } + var chunk = doc; + while (!chunk.lines) { + for (var i = 0;; ++i) { + var child = chunk.children[i], sz = child.chunkSize(); + if (n < sz) { chunk = child; break } + n -= sz; + } + } + return chunk.lines[n] + } + + // Get the part of a document between two positions, as an array of + // strings. + function getBetween(doc, start, end) { + var out = [], n = start.line; + doc.iter(start.line, end.line + 1, function (line) { + var text = line.text; + if (n == end.line) { text = text.slice(0, end.ch); } + if (n == start.line) { text = text.slice(start.ch); } + out.push(text); + ++n; + }); + return out + } + // Get the lines between from and to, as array of strings. + function getLines(doc, from, to) { + var out = []; + doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value + return out + } + + // Update the height of a line, propagating the height change + // upwards to parent nodes. + function updateLineHeight(line, height) { + var diff = height - line.height; + if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } } + } + + // Given a line object, find its line number by walking up through + // its parent links. + function lineNo(line) { + if (line.parent == null) { return null } + var cur = line.parent, no = indexOf(cur.lines, line); + for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { + for (var i = 0;; ++i) { + if (chunk.children[i] == cur) { break } + no += chunk.children[i].chunkSize(); + } + } + return no + cur.first + } + + // Find the line at the given vertical position, using the height + // information in the document tree. + function lineAtHeight(chunk, h) { + var n = chunk.first; + outer: do { + for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) { + var child = chunk.children[i$1], ch = child.height; + if (h < ch) { chunk = child; continue outer } + h -= ch; + n += child.chunkSize(); + } + return n + } while (!chunk.lines) + var i = 0; + for (; i < chunk.lines.length; ++i) { + var line = chunk.lines[i], lh = line.height; + if (h < lh) { break } + h -= lh; + } + return n + i + } + + function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size} + + function lineNumberFor(options, i) { + return String(options.lineNumberFormatter(i + options.firstLineNumber)) + } + + // A Pos instance represents a position within the text. + function Pos(line, ch, sticky) { + if ( sticky === void 0 ) sticky = null; + + if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) } + this.line = line; + this.ch = ch; + this.sticky = sticky; + } + + // Compare two positions, return 0 if they are the same, a negative + // number when a is less, and a positive number otherwise. + function cmp(a, b) { return a.line - b.line || a.ch - b.ch } + + function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 } + + function copyPos(x) {return Pos(x.line, x.ch)} + function maxPos(a, b) { return cmp(a, b) < 0 ? b : a } + function minPos(a, b) { return cmp(a, b) < 0 ? a : b } + + // Most of the external API clips given positions to make sure they + // actually exist within the document. + function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))} + function clipPos(doc, pos) { + if (pos.line < doc.first) { return Pos(doc.first, 0) } + var last = doc.first + doc.size - 1; + if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) } + return clipToLen(pos, getLine(doc, pos.line).text.length) + } + function clipToLen(pos, linelen) { + var ch = pos.ch; + if (ch == null || ch > linelen) { return Pos(pos.line, linelen) } + else if (ch < 0) { return Pos(pos.line, 0) } + else { return pos } + } + function clipPosArray(doc, array) { + var out = []; + for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); } + return out + } + + var SavedContext = function(state, lookAhead) { + this.state = state; + this.lookAhead = lookAhead; + }; + + var Context = function(doc, state, line, lookAhead) { + this.state = state; + this.doc = doc; + this.line = line; + this.maxLookAhead = lookAhead || 0; + this.baseTokens = null; + this.baseTokenPos = 1; + }; + + Context.prototype.lookAhead = function (n) { + var line = this.doc.getLine(this.line + n); + if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; } + return line + }; + + Context.prototype.baseToken = function (n) { + if (!this.baseTokens) { return null } + while (this.baseTokens[this.baseTokenPos] <= n) + { this.baseTokenPos += 2; } + var type = this.baseTokens[this.baseTokenPos + 1]; + return {type: type && type.replace(/( |^)overlay .*/, ""), + size: this.baseTokens[this.baseTokenPos] - n} + }; + + Context.prototype.nextLine = function () { + this.line++; + if (this.maxLookAhead > 0) { this.maxLookAhead--; } + }; + + Context.fromSaved = function (doc, saved, line) { + if (saved instanceof SavedContext) + { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) } + else + { return new Context(doc, copyState(doc.mode, saved), line) } + }; + + Context.prototype.save = function (copy) { + var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state; + return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state + }; + + + // Compute a style array (an array starting with a mode generation + // -- for invalidation -- followed by pairs of end positions and + // style strings), which is used to highlight the tokens on the + // line. + function highlightLine(cm, line, context, forceToEnd) { + // A styles array always starts with a number identifying the + // mode/overlays that it is based on (for easy invalidation). + var st = [cm.state.modeGen], lineClasses = {}; + // Compute the base array of styles + runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); }, + lineClasses, forceToEnd); + var state = context.state; + + // Run overlays, adjust style array. + var loop = function ( o ) { + context.baseTokens = st; + var overlay = cm.state.overlays[o], i = 1, at = 0; + context.state = true; + runMode(cm, line.text, overlay.mode, context, function (end, style) { + var start = i; + // Ensure there's a token end at the current position, and that i points at it + while (at < end) { + var i_end = st[i]; + if (i_end > end) + { st.splice(i, 1, end, st[i+1], i_end); } + i += 2; + at = Math.min(end, i_end); + } + if (!style) { return } + if (overlay.opaque) { + st.splice(start, i - start, end, "overlay " + style); + i = start + 2; + } else { + for (; start < i; start += 2) { + var cur = st[start+1]; + st[start+1] = (cur ? cur + " " : "") + "overlay " + style; + } + } + }, lineClasses); + context.state = state; + context.baseTokens = null; + context.baseTokenPos = 1; + }; + + for (var o = 0; o < cm.state.overlays.length; ++o) loop( o ); + + return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null} + } + + function getLineStyles(cm, line, updateFrontier) { + if (!line.styles || line.styles[0] != cm.state.modeGen) { + var context = getContextBefore(cm, lineNo(line)); + var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state); + var result = highlightLine(cm, line, context); + if (resetState) { context.state = resetState; } + line.stateAfter = context.save(!resetState); + line.styles = result.styles; + if (result.classes) { line.styleClasses = result.classes; } + else if (line.styleClasses) { line.styleClasses = null; } + if (updateFrontier === cm.doc.highlightFrontier) + { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); } + } + return line.styles + } + + function getContextBefore(cm, n, precise) { + var doc = cm.doc, display = cm.display; + if (!doc.mode.startState) { return new Context(doc, true, n) } + var start = findStartLine(cm, n, precise); + var saved = start > doc.first && getLine(doc, start - 1).stateAfter; + var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start); + + doc.iter(start, n, function (line) { + processLine(cm, line.text, context); + var pos = context.line; + line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null; + context.nextLine(); + }); + if (precise) { doc.modeFrontier = context.line; } + return context + } + + // Lightweight form of highlight -- proceed over this line and + // update state, but don't save a style array. Used for lines that + // aren't currently visible. + function processLine(cm, text, context, startAt) { + var mode = cm.doc.mode; + var stream = new StringStream(text, cm.options.tabSize, context); + stream.start = stream.pos = startAt || 0; + if (text == "") { callBlankLine(mode, context.state); } + while (!stream.eol()) { + readToken(mode, stream, context.state); + stream.start = stream.pos; + } + } + + function callBlankLine(mode, state) { + if (mode.blankLine) { return mode.blankLine(state) } + if (!mode.innerMode) { return } + var inner = innerMode(mode, state); + if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) } + } + + function readToken(mode, stream, state, inner) { + for (var i = 0; i < 10; i++) { + if (inner) { inner[0] = innerMode(mode, state).mode; } + var style = mode.token(stream, state); + if (stream.pos > stream.start) { return style } + } + throw new Error("Mode " + mode.name + " failed to advance stream.") + } + + var Token = function(stream, type, state) { + this.start = stream.start; this.end = stream.pos; + this.string = stream.current(); + this.type = type || null; + this.state = state; + }; + + // Utility for getTokenAt and getLineTokens + function takeToken(cm, pos, precise, asArray) { + var doc = cm.doc, mode = doc.mode, style; + pos = clipPos(doc, pos); + var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise); + var stream = new StringStream(line.text, cm.options.tabSize, context), tokens; + if (asArray) { tokens = []; } + while ((asArray || stream.pos < pos.ch) && !stream.eol()) { + stream.start = stream.pos; + style = readToken(mode, stream, context.state); + if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); } + } + return asArray ? tokens : new Token(stream, style, context.state) + } + + function extractLineClasses(type, output) { + if (type) { for (;;) { + var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/); + if (!lineClass) { break } + type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length); + var prop = lineClass[1] ? "bgClass" : "textClass"; + if (output[prop] == null) + { output[prop] = lineClass[2]; } + else if (!(new RegExp("(?:^|\\s)" + lineClass[2] + "(?:$|\\s)")).test(output[prop])) + { output[prop] += " " + lineClass[2]; } + } } + return type + } + + // Run the given mode's parser over a line, calling f for each token. + function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) { + var flattenSpans = mode.flattenSpans; + if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; } + var curStart = 0, curStyle = null; + var stream = new StringStream(text, cm.options.tabSize, context), style; + var inner = cm.options.addModeClass && [null]; + if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); } + while (!stream.eol()) { + if (stream.pos > cm.options.maxHighlightLength) { + flattenSpans = false; + if (forceToEnd) { processLine(cm, text, context, stream.pos); } + stream.pos = text.length; + style = null; + } else { + style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses); + } + if (inner) { + var mName = inner[0].name; + if (mName) { style = "m-" + (style ? mName + " " + style : mName); } + } + if (!flattenSpans || curStyle != style) { + while (curStart < stream.start) { + curStart = Math.min(stream.start, curStart + 5000); + f(curStart, curStyle); + } + curStyle = style; + } + stream.start = stream.pos; + } + while (curStart < stream.pos) { + // Webkit seems to refuse to render text nodes longer than 57444 + // characters, and returns inaccurate measurements in nodes + // starting around 5000 chars. + var pos = Math.min(stream.pos, curStart + 5000); + f(pos, curStyle); + curStart = pos; + } + } + + // Finds the line to start with when starting a parse. Tries to + // find a line with a stateAfter, so that it can start with a + // valid state. If that fails, it returns the line with the + // smallest indentation, which tends to need the least context to + // parse correctly. + function findStartLine(cm, n, precise) { + var minindent, minline, doc = cm.doc; + var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100); + for (var search = n; search > lim; --search) { + if (search <= doc.first) { return doc.first } + var line = getLine(doc, search - 1), after = line.stateAfter; + if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier)) + { return search } + var indented = countColumn(line.text, null, cm.options.tabSize); + if (minline == null || minindent > indented) { + minline = search - 1; + minindent = indented; + } + } + return minline + } + + function retreatFrontier(doc, n) { + doc.modeFrontier = Math.min(doc.modeFrontier, n); + if (doc.highlightFrontier < n - 10) { return } + var start = doc.first; + for (var line = n - 1; line > start; line--) { + var saved = getLine(doc, line).stateAfter; + // change is on 3 + // state on line 1 looked ahead 2 -- so saw 3 + // test 1 + 2 < 3 should cover this + if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) { + start = line + 1; + break + } + } + doc.highlightFrontier = Math.min(doc.highlightFrontier, start); + } + + // Optimize some code when these features are not used. + var sawReadOnlySpans = false, sawCollapsedSpans = false; + + function seeReadOnlySpans() { + sawReadOnlySpans = true; + } + + function seeCollapsedSpans() { + sawCollapsedSpans = true; + } + + // TEXTMARKER SPANS + + function MarkedSpan(marker, from, to) { + this.marker = marker; + this.from = from; this.to = to; + } + + // Search an array of spans for a span matching the given marker. + function getMarkedSpanFor(spans, marker) { + if (spans) { for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.marker == marker) { return span } + } } + } + // Remove a span from an array, returning undefined if no spans are + // left (we don't store arrays for lines without spans). + function removeMarkedSpan(spans, span) { + var r; + for (var i = 0; i < spans.length; ++i) + { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } } + return r + } + // Add a span to a line. + function addMarkedSpan(line, span) { + line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]; + span.marker.attachLine(line); + } + + // Used for the algorithm that adjusts markers for a change in the + // document. These functions cut an array of spans at a given + // character position, returning an array of remaining chunks (or + // undefined if nothing remains). + function markedSpansBefore(old, startCh, isInsert) { + var nw; + if (old) { for (var i = 0; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh); + if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) { + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh) + ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to)); + } + } } + return nw + } + function markedSpansAfter(old, endCh, isInsert) { + var nw; + if (old) { for (var i = 0; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh); + if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) { + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh) + ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh, + span.to == null ? null : span.to - endCh)); + } + } } + return nw + } + + // Given a change object, compute the new set of marker spans that + // cover the line in which the change took place. Removes spans + // entirely within the change, reconnects spans belonging to the + // same marker that appear on both sides of the change, and cuts off + // spans partially within the change. Returns an array of span + // arrays with one element for each line in (after) the change. + function stretchSpansOverChange(doc, change) { + if (change.full) { return null } + var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans; + var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans; + if (!oldFirst && !oldLast) { return null } + + var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0; + // Get the spans that 'stick out' on both sides + var first = markedSpansBefore(oldFirst, startCh, isInsert); + var last = markedSpansAfter(oldLast, endCh, isInsert); + + // Next, merge those two ends + var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0); + if (first) { + // Fix up .to properties of first + for (var i = 0; i < first.length; ++i) { + var span = first[i]; + if (span.to == null) { + var found = getMarkedSpanFor(last, span.marker); + if (!found) { span.to = startCh; } + else if (sameLine) { span.to = found.to == null ? null : found.to + offset; } + } + } + } + if (last) { + // Fix up .from in last (or move them into first in case of sameLine) + for (var i$1 = 0; i$1 < last.length; ++i$1) { + var span$1 = last[i$1]; + if (span$1.to != null) { span$1.to += offset; } + if (span$1.from == null) { + var found$1 = getMarkedSpanFor(first, span$1.marker); + if (!found$1) { + span$1.from = offset; + if (sameLine) { (first || (first = [])).push(span$1); } + } + } else { + span$1.from += offset; + if (sameLine) { (first || (first = [])).push(span$1); } + } + } + } + // Make sure we didn't create any zero-length spans + if (first) { first = clearEmptySpans(first); } + if (last && last != first) { last = clearEmptySpans(last); } + + var newMarkers = [first]; + if (!sameLine) { + // Fill gap with whole-line-spans + var gap = change.text.length - 2, gapMarkers; + if (gap > 0 && first) + { for (var i$2 = 0; i$2 < first.length; ++i$2) + { if (first[i$2].to == null) + { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } } + for (var i$3 = 0; i$3 < gap; ++i$3) + { newMarkers.push(gapMarkers); } + newMarkers.push(last); + } + return newMarkers + } + + // Remove spans that are empty and don't have a clearWhenEmpty + // option of false. + function clearEmptySpans(spans) { + for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false) + { spans.splice(i--, 1); } + } + if (!spans.length) { return null } + return spans + } + + // Used to 'clip' out readOnly ranges when making a change. + function removeReadOnlyRanges(doc, from, to) { + var markers = null; + doc.iter(from.line, to.line + 1, function (line) { + if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { + var mark = line.markedSpans[i].marker; + if (mark.readOnly && (!markers || indexOf(markers, mark) == -1)) + { (markers || (markers = [])).push(mark); } + } } + }); + if (!markers) { return null } + var parts = [{from: from, to: to}]; + for (var i = 0; i < markers.length; ++i) { + var mk = markers[i], m = mk.find(0); + for (var j = 0; j < parts.length; ++j) { + var p = parts[j]; + if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue } + var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to); + if (dfrom < 0 || !mk.inclusiveLeft && !dfrom) + { newParts.push({from: p.from, to: m.from}); } + if (dto > 0 || !mk.inclusiveRight && !dto) + { newParts.push({from: m.to, to: p.to}); } + parts.splice.apply(parts, newParts); + j += newParts.length - 3; + } + } + return parts + } + + // Connect or disconnect spans from a line. + function detachMarkedSpans(line) { + var spans = line.markedSpans; + if (!spans) { return } + for (var i = 0; i < spans.length; ++i) + { spans[i].marker.detachLine(line); } + line.markedSpans = null; + } + function attachMarkedSpans(line, spans) { + if (!spans) { return } + for (var i = 0; i < spans.length; ++i) + { spans[i].marker.attachLine(line); } + line.markedSpans = spans; + } + + // Helpers used when computing which overlapping collapsed span + // counts as the larger one. + function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 } + function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 } + + // Returns a number indicating which of two overlapping collapsed + // spans is larger (and thus includes the other). Falls back to + // comparing ids when the spans cover exactly the same range. + function compareCollapsedMarkers(a, b) { + var lenDiff = a.lines.length - b.lines.length; + if (lenDiff != 0) { return lenDiff } + var aPos = a.find(), bPos = b.find(); + var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b); + if (fromCmp) { return -fromCmp } + var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b); + if (toCmp) { return toCmp } + return b.id - a.id + } + + // Find out whether a line ends or starts in a collapsed span. If + // so, return the marker for that span. + function collapsedSpanAtSide(line, start) { + var sps = sawCollapsedSpans && line.markedSpans, found; + if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { + sp = sps[i]; + if (sp.marker.collapsed && (start ? sp.from : sp.to) == null && + (!found || compareCollapsedMarkers(found, sp.marker) < 0)) + { found = sp.marker; } + } } + return found + } + function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) } + function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) } + + function collapsedSpanAround(line, ch) { + var sps = sawCollapsedSpans && line.markedSpans, found; + if (sps) { for (var i = 0; i < sps.length; ++i) { + var sp = sps[i]; + if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) && + (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; } + } } + return found + } + + // Test whether there exists a collapsed span that partially + // overlaps (covers the start or end, but not both) of a new span. + // Such overlap is not allowed. + function conflictingCollapsedRange(doc, lineNo, from, to, marker) { + var line = getLine(doc, lineNo); + var sps = sawCollapsedSpans && line.markedSpans; + if (sps) { for (var i = 0; i < sps.length; ++i) { + var sp = sps[i]; + if (!sp.marker.collapsed) { continue } + var found = sp.marker.find(0); + var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker); + var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker); + if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue } + if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) || + fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0)) + { return true } + } } + } + + // A visual line is a line as drawn on the screen. Folding, for + // example, can cause multiple logical lines to appear on the same + // visual line. This finds the start of the visual line that the + // given line is part of (usually that is the line itself). + function visualLine(line) { + var merged; + while (merged = collapsedSpanAtStart(line)) + { line = merged.find(-1, true).line; } + return line + } + + function visualLineEnd(line) { + var merged; + while (merged = collapsedSpanAtEnd(line)) + { line = merged.find(1, true).line; } + return line + } + + // Returns an array of logical lines that continue the visual line + // started by the argument, or undefined if there are no such lines. + function visualLineContinued(line) { + var merged, lines; + while (merged = collapsedSpanAtEnd(line)) { + line = merged.find(1, true).line + ;(lines || (lines = [])).push(line); + } + return lines + } + + // Get the line number of the start of the visual line that the + // given line number is part of. + function visualLineNo(doc, lineN) { + var line = getLine(doc, lineN), vis = visualLine(line); + if (line == vis) { return lineN } + return lineNo(vis) + } + + // Get the line number of the start of the next visual line after + // the given line. + function visualLineEndNo(doc, lineN) { + if (lineN > doc.lastLine()) { return lineN } + var line = getLine(doc, lineN), merged; + if (!lineIsHidden(doc, line)) { return lineN } + while (merged = collapsedSpanAtEnd(line)) + { line = merged.find(1, true).line; } + return lineNo(line) + 1 + } + + // Compute whether a line is hidden. Lines count as hidden when they + // are part of a visual line that starts with another line, or when + // they are entirely covered by collapsed, non-widget span. + function lineIsHidden(doc, line) { + var sps = sawCollapsedSpans && line.markedSpans; + if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { + sp = sps[i]; + if (!sp.marker.collapsed) { continue } + if (sp.from == null) { return true } + if (sp.marker.widgetNode) { continue } + if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp)) + { return true } + } } + } + function lineIsHiddenInner(doc, line, span) { + if (span.to == null) { + var end = span.marker.find(1, true); + return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker)) + } + if (span.marker.inclusiveRight && span.to == line.text.length) + { return true } + for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) { + sp = line.markedSpans[i]; + if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to && + (sp.to == null || sp.to != span.from) && + (sp.marker.inclusiveLeft || span.marker.inclusiveRight) && + lineIsHiddenInner(doc, line, sp)) { return true } + } + } + + // Find the height above the given line. + function heightAtLine(lineObj) { + lineObj = visualLine(lineObj); + + var h = 0, chunk = lineObj.parent; + for (var i = 0; i < chunk.lines.length; ++i) { + var line = chunk.lines[i]; + if (line == lineObj) { break } + else { h += line.height; } + } + for (var p = chunk.parent; p; chunk = p, p = chunk.parent) { + for (var i$1 = 0; i$1 < p.children.length; ++i$1) { + var cur = p.children[i$1]; + if (cur == chunk) { break } + else { h += cur.height; } + } + } + return h + } + + // Compute the character length of a line, taking into account + // collapsed ranges (see markText) that might hide parts, and join + // other lines onto it. + function lineLength(line) { + if (line.height == 0) { return 0 } + var len = line.text.length, merged, cur = line; + while (merged = collapsedSpanAtStart(cur)) { + var found = merged.find(0, true); + cur = found.from.line; + len += found.from.ch - found.to.ch; + } + cur = line; + while (merged = collapsedSpanAtEnd(cur)) { + var found$1 = merged.find(0, true); + len -= cur.text.length - found$1.from.ch; + cur = found$1.to.line; + len += cur.text.length - found$1.to.ch; + } + return len + } + + // Find the longest line in the document. + function findMaxLine(cm) { + var d = cm.display, doc = cm.doc; + d.maxLine = getLine(doc, doc.first); + d.maxLineLength = lineLength(d.maxLine); + d.maxLineChanged = true; + doc.iter(function (line) { + var len = lineLength(line); + if (len > d.maxLineLength) { + d.maxLineLength = len; + d.maxLine = line; + } + }); + } + + // LINE DATA STRUCTURE + + // Line objects. These hold state related to a line, including + // highlighting info (the styles array). + var Line = function(text, markedSpans, estimateHeight) { + this.text = text; + attachMarkedSpans(this, markedSpans); + this.height = estimateHeight ? estimateHeight(this) : 1; + }; + + Line.prototype.lineNo = function () { return lineNo(this) }; + eventMixin(Line); + + // Change the content (text, markers) of a line. Automatically + // invalidates cached information and tries to re-estimate the + // line's height. + function updateLine(line, text, markedSpans, estimateHeight) { + line.text = text; + if (line.stateAfter) { line.stateAfter = null; } + if (line.styles) { line.styles = null; } + if (line.order != null) { line.order = null; } + detachMarkedSpans(line); + attachMarkedSpans(line, markedSpans); + var estHeight = estimateHeight ? estimateHeight(line) : 1; + if (estHeight != line.height) { updateLineHeight(line, estHeight); } + } + + // Detach a line from the document tree and its markers. + function cleanUpLine(line) { + line.parent = null; + detachMarkedSpans(line); + } + + // Convert a style as returned by a mode (either null, or a string + // containing one or more styles) to a CSS style. This is cached, + // and also looks for line-wide styles. + var styleToClassCache = {}, styleToClassCacheWithMode = {}; + function interpretTokenStyle(style, options) { + if (!style || /^\s*$/.test(style)) { return null } + var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache; + return cache[style] || + (cache[style] = style.replace(/\S+/g, "cm-$&")) + } + + // Render the DOM representation of the text of a line. Also builds + // up a 'line map', which points at the DOM nodes that represent + // specific stretches of text, and is used by the measuring code. + // The returned object contains the DOM node, this map, and + // information about line-wide styles that were set by the mode. + function buildLineContent(cm, lineView) { + // The padding-right forces the element to have a 'border', which + // is needed on Webkit to be able to get line-level bounding + // rectangles for it (in measureChar). + var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null); + var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content, + col: 0, pos: 0, cm: cm, + trailingSpace: false, + splitSpaces: cm.getOption("lineWrapping")}; + lineView.measure = {}; + + // Iterate over the logical lines that make up this visual line. + for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) { + var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0); + builder.pos = 0; + builder.addToken = buildToken; + // Optionally wire in some hacks into the token-rendering + // algorithm, to deal with browser quirks. + if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction))) + { builder.addToken = buildTokenBadBidi(builder.addToken, order); } + builder.map = []; + var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line); + insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate)); + if (line.styleClasses) { + if (line.styleClasses.bgClass) + { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); } + if (line.styleClasses.textClass) + { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); } + } + + // Ensure at least a single node is present, for measuring. + if (builder.map.length == 0) + { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); } + + // Store the map and a cache object for the current logical line + if (i == 0) { + lineView.measure.map = builder.map; + lineView.measure.cache = {}; + } else { + (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map) + ;(lineView.measure.caches || (lineView.measure.caches = [])).push({}); + } + } + + // See issue #2901 + if (webkit) { + var last = builder.content.lastChild; + if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab"))) + { builder.content.className = "cm-tab-wrap-hack"; } + } + + signal(cm, "renderLine", cm, lineView.line, builder.pre); + if (builder.pre.className) + { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); } + + return builder + } + + function defaultSpecialCharPlaceholder(ch) { + var token = elt("span", "\u2022", "cm-invalidchar"); + token.title = "\\u" + ch.charCodeAt(0).toString(16); + token.setAttribute("aria-label", token.title); + return token + } + + // Build up the DOM representation for a single token, and add it to + // the line map. Takes care to render special characters separately. + function buildToken(builder, text, style, startStyle, endStyle, css, attributes) { + if (!text) { return } + var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text; + var special = builder.cm.state.specialChars, mustWrap = false; + var content; + if (!special.test(text)) { + builder.col += text.length; + content = document.createTextNode(displayText); + builder.map.push(builder.pos, builder.pos + text.length, content); + if (ie && ie_version < 9) { mustWrap = true; } + builder.pos += text.length; + } else { + content = document.createDocumentFragment(); + var pos = 0; + while (true) { + special.lastIndex = pos; + var m = special.exec(text); + var skipped = m ? m.index - pos : text.length - pos; + if (skipped) { + var txt = document.createTextNode(displayText.slice(pos, pos + skipped)); + if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); } + else { content.appendChild(txt); } + builder.map.push(builder.pos, builder.pos + skipped, txt); + builder.col += skipped; + builder.pos += skipped; + } + if (!m) { break } + pos += skipped + 1; + var txt$1 = (void 0); + if (m[0] == "\t") { + var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize; + txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")); + txt$1.setAttribute("role", "presentation"); + txt$1.setAttribute("cm-text", "\t"); + builder.col += tabWidth; + } else if (m[0] == "\r" || m[0] == "\n") { + txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar")); + txt$1.setAttribute("cm-text", m[0]); + builder.col += 1; + } else { + txt$1 = builder.cm.options.specialCharPlaceholder(m[0]); + txt$1.setAttribute("cm-text", m[0]); + if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); } + else { content.appendChild(txt$1); } + builder.col += 1; + } + builder.map.push(builder.pos, builder.pos + 1, txt$1); + builder.pos++; + } + } + builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32; + if (style || startStyle || endStyle || mustWrap || css || attributes) { + var fullStyle = style || ""; + if (startStyle) { fullStyle += startStyle; } + if (endStyle) { fullStyle += endStyle; } + var token = elt("span", [content], fullStyle, css); + if (attributes) { + for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class") + { token.setAttribute(attr, attributes[attr]); } } + } + return builder.content.appendChild(token) + } + builder.content.appendChild(content); + } + + // Change some spaces to NBSP to prevent the browser from collapsing + // trailing spaces at the end of a line when rendering text (issue #1362). + function splitSpaces(text, trailingBefore) { + if (text.length > 1 && !/ /.test(text)) { return text } + var spaceBefore = trailingBefore, result = ""; + for (var i = 0; i < text.length; i++) { + var ch = text.charAt(i); + if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32)) + { ch = "\u00a0"; } + result += ch; + spaceBefore = ch == " "; + } + return result + } + + // Work around nonsense dimensions being reported for stretches of + // right-to-left text. + function buildTokenBadBidi(inner, order) { + return function (builder, text, style, startStyle, endStyle, css, attributes) { + style = style ? style + " cm-force-border" : "cm-force-border"; + var start = builder.pos, end = start + text.length; + for (;;) { + // Find the part that overlaps with the start of this text + var part = (void 0); + for (var i = 0; i < order.length; i++) { + part = order[i]; + if (part.to > start && part.from <= start) { break } + } + if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) } + inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes); + startStyle = null; + text = text.slice(part.to - start); + start = part.to; + } + } + } + + function buildCollapsedSpan(builder, size, marker, ignoreWidget) { + var widget = !ignoreWidget && marker.widgetNode; + if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); } + if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) { + if (!widget) + { widget = builder.content.appendChild(document.createElement("span")); } + widget.setAttribute("cm-marker", marker.id); + } + if (widget) { + builder.cm.display.input.setUneditable(widget); + builder.content.appendChild(widget); + } + builder.pos += size; + builder.trailingSpace = false; + } + + // Outputs a number of spans to make up a line, taking highlighting + // and marked text into account. + function insertLineContent(line, builder, styles) { + var spans = line.markedSpans, allText = line.text, at = 0; + if (!spans) { + for (var i$1 = 1; i$1 < styles.length; i$1+=2) + { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); } + return + } + + var len = allText.length, pos = 0, i = 1, text = "", style, css; + var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes; + for (;;) { + if (nextChange == pos) { // Update current marker set + spanStyle = spanEndStyle = spanStartStyle = css = ""; + attributes = null; + collapsed = null; nextChange = Infinity; + var foundBookmarks = [], endStyles = (void 0); + for (var j = 0; j < spans.length; ++j) { + var sp = spans[j], m = sp.marker; + if (m.type == "bookmark" && sp.from == pos && m.widgetNode) { + foundBookmarks.push(m); + } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) { + if (sp.to != null && sp.to != pos && nextChange > sp.to) { + nextChange = sp.to; + spanEndStyle = ""; + } + if (m.className) { spanStyle += " " + m.className; } + if (m.css) { css = (css ? css + ";" : "") + m.css; } + if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; } + if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); } + // support for the old title property + // https://github.com/codemirror/CodeMirror/pull/5673 + if (m.title) { (attributes || (attributes = {})).title = m.title; } + if (m.attributes) { + for (var attr in m.attributes) + { (attributes || (attributes = {}))[attr] = m.attributes[attr]; } + } + if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)) + { collapsed = sp; } + } else if (sp.from > pos && nextChange > sp.from) { + nextChange = sp.from; + } + } + if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2) + { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } } + + if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2) + { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } } + if (collapsed && (collapsed.from || 0) == pos) { + buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos, + collapsed.marker, collapsed.from == null); + if (collapsed.to == null) { return } + if (collapsed.to == pos) { collapsed = false; } + } + } + if (pos >= len) { break } + + var upto = Math.min(len, nextChange); + while (true) { + if (text) { + var end = pos + text.length; + if (!collapsed) { + var tokenText = end > upto ? text.slice(0, upto - pos) : text; + builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle, + spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes); + } + if (end >= upto) {text = text.slice(upto - pos); pos = upto; break} + pos = end; + spanStartStyle = ""; + } + text = allText.slice(at, at = styles[i++]); + style = interpretTokenStyle(styles[i++], builder.cm.options); + } + } + } + + + // These objects are used to represent the visible (currently drawn) + // part of the document. A LineView may correspond to multiple + // logical lines, if those are connected by collapsed ranges. + function LineView(doc, line, lineN) { + // The starting line + this.line = line; + // Continuing lines, if any + this.rest = visualLineContinued(line); + // Number of logical lines in this visual line + this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1; + this.node = this.text = null; + this.hidden = lineIsHidden(doc, line); + } + + // Create a range of LineView objects for the given lines. + function buildViewArray(cm, from, to) { + var array = [], nextPos; + for (var pos = from; pos < to; pos = nextPos) { + var view = new LineView(cm.doc, getLine(cm.doc, pos), pos); + nextPos = pos + view.size; + array.push(view); + } + return array + } + + var operationGroup = null; + + function pushOperation(op) { + if (operationGroup) { + operationGroup.ops.push(op); + } else { + op.ownsGroup = operationGroup = { + ops: [op], + delayedCallbacks: [] + }; + } + } + + function fireCallbacksForOps(group) { + // Calls delayed callbacks and cursorActivity handlers until no + // new ones appear + var callbacks = group.delayedCallbacks, i = 0; + do { + for (; i < callbacks.length; i++) + { callbacks[i].call(null); } + for (var j = 0; j < group.ops.length; j++) { + var op = group.ops[j]; + if (op.cursorActivityHandlers) + { while (op.cursorActivityCalled < op.cursorActivityHandlers.length) + { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } } + } + } while (i < callbacks.length) + } + + function finishOperation(op, endCb) { + var group = op.ownsGroup; + if (!group) { return } + + try { fireCallbacksForOps(group); } + finally { + operationGroup = null; + endCb(group); + } + } + + var orphanDelayedCallbacks = null; + + // Often, we want to signal events at a point where we are in the + // middle of some work, but don't want the handler to start calling + // other methods on the editor, which might be in an inconsistent + // state or simply not expect any other events to happen. + // signalLater looks whether there are any handlers, and schedules + // them to be executed when the last operation ends, or, if no + // operation is active, when a timeout fires. + function signalLater(emitter, type /*, values...*/) { + var arr = getHandlers(emitter, type); + if (!arr.length) { return } + var args = Array.prototype.slice.call(arguments, 2), list; + if (operationGroup) { + list = operationGroup.delayedCallbacks; + } else if (orphanDelayedCallbacks) { + list = orphanDelayedCallbacks; + } else { + list = orphanDelayedCallbacks = []; + setTimeout(fireOrphanDelayed, 0); + } + var loop = function ( i ) { + list.push(function () { return arr[i].apply(null, args); }); + }; + + for (var i = 0; i < arr.length; ++i) + loop( i ); + } + + function fireOrphanDelayed() { + var delayed = orphanDelayedCallbacks; + orphanDelayedCallbacks = null; + for (var i = 0; i < delayed.length; ++i) { delayed[i](); } + } + + // When an aspect of a line changes, a string is added to + // lineView.changes. This updates the relevant part of the line's + // DOM structure. + function updateLineForChanges(cm, lineView, lineN, dims) { + for (var j = 0; j < lineView.changes.length; j++) { + var type = lineView.changes[j]; + if (type == "text") { updateLineText(cm, lineView); } + else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); } + else if (type == "class") { updateLineClasses(cm, lineView); } + else if (type == "widget") { updateLineWidgets(cm, lineView, dims); } + } + lineView.changes = null; + } + + // Lines with gutter elements, widgets or a background class need to + // be wrapped, and have the extra elements added to the wrapper div + function ensureLineWrapped(lineView) { + if (lineView.node == lineView.text) { + lineView.node = elt("div", null, null, "position: relative"); + if (lineView.text.parentNode) + { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); } + lineView.node.appendChild(lineView.text); + if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; } + } + return lineView.node + } + + function updateLineBackground(cm, lineView) { + var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass; + if (cls) { cls += " CodeMirror-linebackground"; } + if (lineView.background) { + if (cls) { lineView.background.className = cls; } + else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; } + } else if (cls) { + var wrap = ensureLineWrapped(lineView); + lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild); + cm.display.input.setUneditable(lineView.background); + } + } + + // Wrapper around buildLineContent which will reuse the structure + // in display.externalMeasured when possible. + function getLineContent(cm, lineView) { + var ext = cm.display.externalMeasured; + if (ext && ext.line == lineView.line) { + cm.display.externalMeasured = null; + lineView.measure = ext.measure; + return ext.built + } + return buildLineContent(cm, lineView) + } + + // Redraw the line's text. Interacts with the background and text + // classes because the mode may output tokens that influence these + // classes. + function updateLineText(cm, lineView) { + var cls = lineView.text.className; + var built = getLineContent(cm, lineView); + if (lineView.text == lineView.node) { lineView.node = built.pre; } + lineView.text.parentNode.replaceChild(built.pre, lineView.text); + lineView.text = built.pre; + if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) { + lineView.bgClass = built.bgClass; + lineView.textClass = built.textClass; + updateLineClasses(cm, lineView); + } else if (cls) { + lineView.text.className = cls; + } + } + + function updateLineClasses(cm, lineView) { + updateLineBackground(cm, lineView); + if (lineView.line.wrapClass) + { ensureLineWrapped(lineView).className = lineView.line.wrapClass; } + else if (lineView.node != lineView.text) + { lineView.node.className = ""; } + var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass; + lineView.text.className = textClass || ""; + } + + function updateLineGutter(cm, lineView, lineN, dims) { + if (lineView.gutter) { + lineView.node.removeChild(lineView.gutter); + lineView.gutter = null; + } + if (lineView.gutterBackground) { + lineView.node.removeChild(lineView.gutterBackground); + lineView.gutterBackground = null; + } + if (lineView.line.gutterClass) { + var wrap = ensureLineWrapped(lineView); + lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass, + ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px")); + cm.display.input.setUneditable(lineView.gutterBackground); + wrap.insertBefore(lineView.gutterBackground, lineView.text); + } + var markers = lineView.line.gutterMarkers; + if (cm.options.lineNumbers || markers) { + var wrap$1 = ensureLineWrapped(lineView); + var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px")); + cm.display.input.setUneditable(gutterWrap); + wrap$1.insertBefore(gutterWrap, lineView.text); + if (lineView.line.gutterClass) + { gutterWrap.className += " " + lineView.line.gutterClass; } + if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"])) + { lineView.lineNumber = gutterWrap.appendChild( + elt("div", lineNumberFor(cm.options, lineN), + "CodeMirror-linenumber CodeMirror-gutter-elt", + ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); } + if (markers) { for (var k = 0; k < cm.display.gutterSpecs.length; ++k) { + var id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id]; + if (found) + { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", + ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); } + } } + } + } + + function updateLineWidgets(cm, lineView, dims) { + if (lineView.alignable) { lineView.alignable = null; } + var isWidget = classTest("CodeMirror-linewidget"); + for (var node = lineView.node.firstChild, next = (void 0); node; node = next) { + next = node.nextSibling; + if (isWidget.test(node.className)) { lineView.node.removeChild(node); } + } + insertLineWidgets(cm, lineView, dims); + } + + // Build a line's DOM representation from scratch + function buildLineElement(cm, lineView, lineN, dims) { + var built = getLineContent(cm, lineView); + lineView.text = lineView.node = built.pre; + if (built.bgClass) { lineView.bgClass = built.bgClass; } + if (built.textClass) { lineView.textClass = built.textClass; } + + updateLineClasses(cm, lineView); + updateLineGutter(cm, lineView, lineN, dims); + insertLineWidgets(cm, lineView, dims); + return lineView.node + } + + // A lineView may contain multiple logical lines (when merged by + // collapsed spans). The widgets for all of them need to be drawn. + function insertLineWidgets(cm, lineView, dims) { + insertLineWidgetsFor(cm, lineView.line, lineView, dims, true); + if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) + { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } } + } + + function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) { + if (!line.widgets) { return } + var wrap = ensureLineWrapped(lineView); + for (var i = 0, ws = line.widgets; i < ws.length; ++i) { + var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget" + (widget.className ? " " + widget.className : "")); + if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); } + positionLineWidget(widget, node, lineView, dims); + cm.display.input.setUneditable(node); + if (allowAbove && widget.above) + { wrap.insertBefore(node, lineView.gutter || lineView.text); } + else + { wrap.appendChild(node); } + signalLater(widget, "redraw"); + } + } + + function positionLineWidget(widget, node, lineView, dims) { + if (widget.noHScroll) { + (lineView.alignable || (lineView.alignable = [])).push(node); + var width = dims.wrapperWidth; + node.style.left = dims.fixedPos + "px"; + if (!widget.coverGutter) { + width -= dims.gutterTotalWidth; + node.style.paddingLeft = dims.gutterTotalWidth + "px"; + } + node.style.width = width + "px"; + } + if (widget.coverGutter) { + node.style.zIndex = 5; + node.style.position = "relative"; + if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; } + } + } + + function widgetHeight(widget) { + if (widget.height != null) { return widget.height } + var cm = widget.doc.cm; + if (!cm) { return 0 } + if (!contains(document.body, widget.node)) { + var parentStyle = "position: relative;"; + if (widget.coverGutter) + { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; } + if (widget.noHScroll) + { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; } + removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle)); + } + return widget.height = widget.node.parentNode.offsetHeight + } + + // Return true when the given mouse event happened in a widget + function eventInWidget(display, e) { + for (var n = e_target(e); n != display.wrapper; n = n.parentNode) { + if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") || + (n.parentNode == display.sizer && n != display.mover)) + { return true } + } + } + + // POSITION MEASUREMENT + + function paddingTop(display) {return display.lineSpace.offsetTop} + function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight} + function paddingH(display) { + if (display.cachedPaddingH) { return display.cachedPaddingH } + var e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-line-like")); + var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle; + var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}; + if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; } + return data + } + + function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth } + function displayWidth(cm) { + return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth + } + function displayHeight(cm) { + return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight + } + + // Ensure the lineView.wrapping.heights array is populated. This is + // an array of bottom offsets for the lines that make up a drawn + // line. When lineWrapping is on, there might be more than one + // height. + function ensureLineHeights(cm, lineView, rect) { + var wrapping = cm.options.lineWrapping; + var curWidth = wrapping && displayWidth(cm); + if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) { + var heights = lineView.measure.heights = []; + if (wrapping) { + lineView.measure.width = curWidth; + var rects = lineView.text.firstChild.getClientRects(); + for (var i = 0; i < rects.length - 1; i++) { + var cur = rects[i], next = rects[i + 1]; + if (Math.abs(cur.bottom - next.bottom) > 2) + { heights.push((cur.bottom + next.top) / 2 - rect.top); } + } + } + heights.push(rect.bottom - rect.top); + } + } + + // Find a line map (mapping character offsets to text nodes) and a + // measurement cache for the given line number. (A line view might + // contain multiple lines when collapsed ranges are present.) + function mapFromLineView(lineView, line, lineN) { + if (lineView.line == line) + { return {map: lineView.measure.map, cache: lineView.measure.cache} } + for (var i = 0; i < lineView.rest.length; i++) + { if (lineView.rest[i] == line) + { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } } + for (var i$1 = 0; i$1 < lineView.rest.length; i$1++) + { if (lineNo(lineView.rest[i$1]) > lineN) + { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } } + } + + // Render a line into the hidden node display.externalMeasured. Used + // when measurement is needed for a line that's not in the viewport. + function updateExternalMeasurement(cm, line) { + line = visualLine(line); + var lineN = lineNo(line); + var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN); + view.lineN = lineN; + var built = view.built = buildLineContent(cm, view); + view.text = built.pre; + removeChildrenAndAdd(cm.display.lineMeasure, built.pre); + return view + } + + // Get a {top, bottom, left, right} box (in line-local coordinates) + // for a given character. + function measureChar(cm, line, ch, bias) { + return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias) + } + + // Find a line view that corresponds to the given line number. + function findViewForLine(cm, lineN) { + if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo) + { return cm.display.view[findViewIndex(cm, lineN)] } + var ext = cm.display.externalMeasured; + if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size) + { return ext } + } + + // Measurement can be split in two steps, the set-up work that + // applies to the whole line, and the measurement of the actual + // character. Functions like coordsChar, that need to do a lot of + // measurements in a row, can thus ensure that the set-up work is + // only done once. + function prepareMeasureForLine(cm, line) { + var lineN = lineNo(line); + var view = findViewForLine(cm, lineN); + if (view && !view.text) { + view = null; + } else if (view && view.changes) { + updateLineForChanges(cm, view, lineN, getDimensions(cm)); + cm.curOp.forceUpdate = true; + } + if (!view) + { view = updateExternalMeasurement(cm, line); } + + var info = mapFromLineView(view, line, lineN); + return { + line: line, view: view, rect: null, + map: info.map, cache: info.cache, before: info.before, + hasHeights: false + } + } + + // Given a prepared measurement object, measures the position of an + // actual character (or fetches it from the cache). + function measureCharPrepared(cm, prepared, ch, bias, varHeight) { + if (prepared.before) { ch = -1; } + var key = ch + (bias || ""), found; + if (prepared.cache.hasOwnProperty(key)) { + found = prepared.cache[key]; + } else { + if (!prepared.rect) + { prepared.rect = prepared.view.text.getBoundingClientRect(); } + if (!prepared.hasHeights) { + ensureLineHeights(cm, prepared.view, prepared.rect); + prepared.hasHeights = true; + } + found = measureCharInner(cm, prepared, ch, bias); + if (!found.bogus) { prepared.cache[key] = found; } + } + return {left: found.left, right: found.right, + top: varHeight ? found.rtop : found.top, + bottom: varHeight ? found.rbottom : found.bottom} + } + + var nullRect = {left: 0, right: 0, top: 0, bottom: 0}; + + function nodeAndOffsetInLineMap(map, ch, bias) { + var node, start, end, collapse, mStart, mEnd; + // First, search the line map for the text node corresponding to, + // or closest to, the target character. + for (var i = 0; i < map.length; i += 3) { + mStart = map[i]; + mEnd = map[i + 1]; + if (ch < mStart) { + start = 0; end = 1; + collapse = "left"; + } else if (ch < mEnd) { + start = ch - mStart; + end = start + 1; + } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) { + end = mEnd - mStart; + start = end - 1; + if (ch >= mEnd) { collapse = "right"; } + } + if (start != null) { + node = map[i + 2]; + if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right")) + { collapse = bias; } + if (bias == "left" && start == 0) + { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) { + node = map[(i -= 3) + 2]; + collapse = "left"; + } } + if (bias == "right" && start == mEnd - mStart) + { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) { + node = map[(i += 3) + 2]; + collapse = "right"; + } } + break + } + } + return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd} + } + + function getUsefulRect(rects, bias) { + var rect = nullRect; + if (bias == "left") { for (var i = 0; i < rects.length; i++) { + if ((rect = rects[i]).left != rect.right) { break } + } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) { + if ((rect = rects[i$1]).left != rect.right) { break } + } } + return rect + } + + function measureCharInner(cm, prepared, ch, bias) { + var place = nodeAndOffsetInLineMap(prepared.map, ch, bias); + var node = place.node, start = place.start, end = place.end, collapse = place.collapse; + + var rect; + if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates. + for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned + while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; } + while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; } + if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) + { rect = node.parentNode.getBoundingClientRect(); } + else + { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); } + if (rect.left || rect.right || start == 0) { break } + end = start; + start = start - 1; + collapse = "right"; + } + if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); } + } else { // If it is a widget, simply get the box for the whole widget. + if (start > 0) { collapse = bias = "right"; } + var rects; + if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1) + { rect = rects[bias == "right" ? rects.length - 1 : 0]; } + else + { rect = node.getBoundingClientRect(); } + } + if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) { + var rSpan = node.parentNode.getClientRects()[0]; + if (rSpan) + { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; } + else + { rect = nullRect; } + } + + var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top; + var mid = (rtop + rbot) / 2; + var heights = prepared.view.measure.heights; + var i = 0; + for (; i < heights.length - 1; i++) + { if (mid < heights[i]) { break } } + var top = i ? heights[i - 1] : 0, bot = heights[i]; + var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left, + right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left, + top: top, bottom: bot}; + if (!rect.left && !rect.right) { result.bogus = true; } + if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; } + + return result + } + + // Work around problem with bounding client rects on ranges being + // returned incorrectly when zoomed on IE10 and below. + function maybeUpdateRectForZooming(measure, rect) { + if (!window.screen || screen.logicalXDPI == null || + screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure)) + { return rect } + var scaleX = screen.logicalXDPI / screen.deviceXDPI; + var scaleY = screen.logicalYDPI / screen.deviceYDPI; + return {left: rect.left * scaleX, right: rect.right * scaleX, + top: rect.top * scaleY, bottom: rect.bottom * scaleY} + } + + function clearLineMeasurementCacheFor(lineView) { + if (lineView.measure) { + lineView.measure.cache = {}; + lineView.measure.heights = null; + if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) + { lineView.measure.caches[i] = {}; } } + } + } + + function clearLineMeasurementCache(cm) { + cm.display.externalMeasure = null; + removeChildren(cm.display.lineMeasure); + for (var i = 0; i < cm.display.view.length; i++) + { clearLineMeasurementCacheFor(cm.display.view[i]); } + } + + function clearCaches(cm) { + clearLineMeasurementCache(cm); + cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null; + if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; } + cm.display.lineNumChars = null; + } + + function pageScrollX() { + // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206 + // which causes page_Offset and bounding client rects to use + // different reference viewports and invalidate our calculations. + if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) } + return window.pageXOffset || (document.documentElement || document.body).scrollLeft + } + function pageScrollY() { + if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) } + return window.pageYOffset || (document.documentElement || document.body).scrollTop + } + + function widgetTopHeight(lineObj) { + var height = 0; + if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above) + { height += widgetHeight(lineObj.widgets[i]); } } } + return height + } + + // Converts a {top, bottom, left, right} box from line-local + // coordinates into another coordinate system. Context may be one of + // "line", "div" (display.lineDiv), "local"./null (editor), "window", + // or "page". + function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) { + if (!includeWidgets) { + var height = widgetTopHeight(lineObj); + rect.top += height; rect.bottom += height; + } + if (context == "line") { return rect } + if (!context) { context = "local"; } + var yOff = heightAtLine(lineObj); + if (context == "local") { yOff += paddingTop(cm.display); } + else { yOff -= cm.display.viewOffset; } + if (context == "page" || context == "window") { + var lOff = cm.display.lineSpace.getBoundingClientRect(); + yOff += lOff.top + (context == "window" ? 0 : pageScrollY()); + var xOff = lOff.left + (context == "window" ? 0 : pageScrollX()); + rect.left += xOff; rect.right += xOff; + } + rect.top += yOff; rect.bottom += yOff; + return rect + } + + // Coverts a box from "div" coords to another coordinate system. + // Context may be "window", "page", "div", or "local"./null. + function fromCoordSystem(cm, coords, context) { + if (context == "div") { return coords } + var left = coords.left, top = coords.top; + // First move into "page" coordinate system + if (context == "page") { + left -= pageScrollX(); + top -= pageScrollY(); + } else if (context == "local" || !context) { + var localBox = cm.display.sizer.getBoundingClientRect(); + left += localBox.left; + top += localBox.top; + } + + var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect(); + return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top} + } + + function charCoords(cm, pos, context, lineObj, bias) { + if (!lineObj) { lineObj = getLine(cm.doc, pos.line); } + return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context) + } + + // Returns a box for a given cursor position, which may have an + // 'other' property containing the position of the secondary cursor + // on a bidi boundary. + // A cursor Pos(line, char, "before") is on the same visual line as `char - 1` + // and after `char - 1` in writing order of `char - 1` + // A cursor Pos(line, char, "after") is on the same visual line as `char` + // and before `char` in writing order of `char` + // Examples (upper-case letters are RTL, lower-case are LTR): + // Pos(0, 1, ...) + // before after + // ab a|b a|b + // aB a|B aB| + // Ab |Ab A|b + // AB B|A B|A + // Every position after the last character on a line is considered to stick + // to the last character on the line. + function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) { + lineObj = lineObj || getLine(cm.doc, pos.line); + if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); } + function get(ch, right) { + var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight); + if (right) { m.left = m.right; } else { m.right = m.left; } + return intoCoordSystem(cm, lineObj, m, context) + } + var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky; + if (ch >= lineObj.text.length) { + ch = lineObj.text.length; + sticky = "before"; + } else if (ch <= 0) { + ch = 0; + sticky = "after"; + } + if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") } + + function getBidi(ch, partPos, invert) { + var part = order[partPos], right = part.level == 1; + return get(invert ? ch - 1 : ch, right != invert) + } + var partPos = getBidiPartAt(order, ch, sticky); + var other = bidiOther; + var val = getBidi(ch, partPos, sticky == "before"); + if (other != null) { val.other = getBidi(ch, other, sticky != "before"); } + return val + } + + // Used to cheaply estimate the coordinates for a position. Used for + // intermediate scroll updates. + function estimateCoords(cm, pos) { + var left = 0; + pos = clipPos(cm.doc, pos); + if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; } + var lineObj = getLine(cm.doc, pos.line); + var top = heightAtLine(lineObj) + paddingTop(cm.display); + return {left: left, right: left, top: top, bottom: top + lineObj.height} + } + + // Positions returned by coordsChar contain some extra information. + // xRel is the relative x position of the input coordinates compared + // to the found position (so xRel > 0 means the coordinates are to + // the right of the character position, for example). When outside + // is true, that means the coordinates lie outside the line's + // vertical range. + function PosWithInfo(line, ch, sticky, outside, xRel) { + var pos = Pos(line, ch, sticky); + pos.xRel = xRel; + if (outside) { pos.outside = outside; } + return pos + } + + // Compute the character position closest to the given coordinates. + // Input must be lineSpace-local ("div" coordinate system). + function coordsChar(cm, x, y) { + var doc = cm.doc; + y += cm.display.viewOffset; + if (y < 0) { return PosWithInfo(doc.first, 0, null, -1, -1) } + var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1; + if (lineN > last) + { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1) } + if (x < 0) { x = 0; } + + var lineObj = getLine(doc, lineN); + for (;;) { + var found = coordsCharInner(cm, lineObj, lineN, x, y); + var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0)); + if (!collapsed) { return found } + var rangeEnd = collapsed.find(1); + if (rangeEnd.line == lineN) { return rangeEnd } + lineObj = getLine(doc, lineN = rangeEnd.line); + } + } + + function wrappedLineExtent(cm, lineObj, preparedMeasure, y) { + y -= widgetTopHeight(lineObj); + var end = lineObj.text.length; + var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0); + end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end); + return {begin: begin, end: end} + } + + function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) { + if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); } + var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top; + return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop) + } + + // Returns true if the given side of a box is after the given + // coordinates, in top-to-bottom, left-to-right order. + function boxIsAfter(box, x, y, left) { + return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x + } + + function coordsCharInner(cm, lineObj, lineNo, x, y) { + // Move y into line-local coordinate space + y -= heightAtLine(lineObj); + var preparedMeasure = prepareMeasureForLine(cm, lineObj); + // When directly calling `measureCharPrepared`, we have to adjust + // for the widgets at this line. + var widgetHeight = widgetTopHeight(lineObj); + var begin = 0, end = lineObj.text.length, ltr = true; + + var order = getOrder(lineObj, cm.doc.direction); + // If the line isn't plain left-to-right text, first figure out + // which bidi section the coordinates fall into. + if (order) { + var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart) + (cm, lineObj, lineNo, preparedMeasure, order, x, y); + ltr = part.level != 1; + // The awkward -1 offsets are needed because findFirst (called + // on these below) will treat its first bound as inclusive, + // second as exclusive, but we want to actually address the + // characters in the part's range + begin = ltr ? part.from : part.to - 1; + end = ltr ? part.to : part.from - 1; + } + + // A binary search to find the first character whose bounding box + // starts after the coordinates. If we run across any whose box wrap + // the coordinates, store that. + var chAround = null, boxAround = null; + var ch = findFirst(function (ch) { + var box = measureCharPrepared(cm, preparedMeasure, ch); + box.top += widgetHeight; box.bottom += widgetHeight; + if (!boxIsAfter(box, x, y, false)) { return false } + if (box.top <= y && box.left <= x) { + chAround = ch; + boxAround = box; + } + return true + }, begin, end); + + var baseX, sticky, outside = false; + // If a box around the coordinates was found, use that + if (boxAround) { + // Distinguish coordinates nearer to the left or right side of the box + var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr; + ch = chAround + (atStart ? 0 : 1); + sticky = atStart ? "after" : "before"; + baseX = atLeft ? boxAround.left : boxAround.right; + } else { + // (Adjust for extended bound, if necessary.) + if (!ltr && (ch == end || ch == begin)) { ch++; } + // To determine which side to associate with, get the box to the + // left of the character and compare it's vertical position to the + // coordinates + sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" : + (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ? + "after" : "before"; + // Now get accurate coordinates for this place, in order to get a + // base X position + var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure); + baseX = coords.left; + outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0; + } + + ch = skipExtendingChars(lineObj.text, ch, 1); + return PosWithInfo(lineNo, ch, sticky, outside, x - baseX) + } + + function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) { + // Bidi parts are sorted left-to-right, and in a non-line-wrapping + // situation, we can take this ordering to correspond to the visual + // ordering. This finds the first part whose end is after the given + // coordinates. + var index = findFirst(function (i) { + var part = order[i], ltr = part.level != 1; + return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"), + "line", lineObj, preparedMeasure), x, y, true) + }, 0, order.length - 1); + var part = order[index]; + // If this isn't the first part, the part's start is also after + // the coordinates, and the coordinates aren't on the same line as + // that start, move one part back. + if (index > 0) { + var ltr = part.level != 1; + var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"), + "line", lineObj, preparedMeasure); + if (boxIsAfter(start, x, y, true) && start.top > y) + { part = order[index - 1]; } + } + return part + } + + function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) { + // In a wrapped line, rtl text on wrapping boundaries can do things + // that don't correspond to the ordering in our `order` array at + // all, so a binary search doesn't work, and we want to return a + // part that only spans one line so that the binary search in + // coordsCharInner is safe. As such, we first find the extent of the + // wrapped line, and then do a flat search in which we discard any + // spans that aren't on the line. + var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y); + var begin = ref.begin; + var end = ref.end; + if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; } + var part = null, closestDist = null; + for (var i = 0; i < order.length; i++) { + var p = order[i]; + if (p.from >= end || p.to <= begin) { continue } + var ltr = p.level != 1; + var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right; + // Weigh against spans ending before this, so that they are only + // picked if nothing ends after + var dist = endX < x ? x - endX + 1e9 : endX - x; + if (!part || closestDist > dist) { + part = p; + closestDist = dist; + } + } + if (!part) { part = order[order.length - 1]; } + // Clip the part to the wrapped line. + if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; } + if (part.to > end) { part = {from: part.from, to: end, level: part.level}; } + return part + } + + var measureText; + // Compute the default text height. + function textHeight(display) { + if (display.cachedTextHeight != null) { return display.cachedTextHeight } + if (measureText == null) { + measureText = elt("pre", null, "CodeMirror-line-like"); + // Measure a bunch of lines, for browsers that compute + // fractional heights. + for (var i = 0; i < 49; ++i) { + measureText.appendChild(document.createTextNode("x")); + measureText.appendChild(elt("br")); + } + measureText.appendChild(document.createTextNode("x")); + } + removeChildrenAndAdd(display.measure, measureText); + var height = measureText.offsetHeight / 50; + if (height > 3) { display.cachedTextHeight = height; } + removeChildren(display.measure); + return height || 1 + } + + // Compute the default character width. + function charWidth(display) { + if (display.cachedCharWidth != null) { return display.cachedCharWidth } + var anchor = elt("span", "xxxxxxxxxx"); + var pre = elt("pre", [anchor], "CodeMirror-line-like"); + removeChildrenAndAdd(display.measure, pre); + var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10; + if (width > 2) { display.cachedCharWidth = width; } + return width || 10 + } + + // Do a bulk-read of the DOM positions and sizes needed to draw the + // view, so that we don't interleave reading and writing to the DOM. + function getDimensions(cm) { + var d = cm.display, left = {}, width = {}; + var gutterLeft = d.gutters.clientLeft; + for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) { + var id = cm.display.gutterSpecs[i].className; + left[id] = n.offsetLeft + n.clientLeft + gutterLeft; + width[id] = n.clientWidth; + } + return {fixedPos: compensateForHScroll(d), + gutterTotalWidth: d.gutters.offsetWidth, + gutterLeft: left, + gutterWidth: width, + wrapperWidth: d.wrapper.clientWidth} + } + + // Computes display.scroller.scrollLeft + display.gutters.offsetWidth, + // but using getBoundingClientRect to get a sub-pixel-accurate + // result. + function compensateForHScroll(display) { + return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left + } + + // Returns a function that estimates the height of a line, to use as + // first approximation until the line becomes visible (and is thus + // properly measurable). + function estimateHeight(cm) { + var th = textHeight(cm.display), wrapping = cm.options.lineWrapping; + var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3); + return function (line) { + if (lineIsHidden(cm.doc, line)) { return 0 } + + var widgetsHeight = 0; + if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) { + if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; } + } } + + if (wrapping) + { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th } + else + { return widgetsHeight + th } + } + } + + function estimateLineHeights(cm) { + var doc = cm.doc, est = estimateHeight(cm); + doc.iter(function (line) { + var estHeight = est(line); + if (estHeight != line.height) { updateLineHeight(line, estHeight); } + }); + } + + // Given a mouse event, find the corresponding position. If liberal + // is false, it checks whether a gutter or scrollbar was clicked, + // and returns null if it was. forRect is used by rectangular + // selections, and tries to estimate a character position even for + // coordinates beyond the right of the text. + function posFromMouse(cm, e, liberal, forRect) { + var display = cm.display; + if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null } + + var x, y, space = display.lineSpace.getBoundingClientRect(); + // Fails unpredictably on IE[67] when mouse is dragged around quickly. + try { x = e.clientX - space.left; y = e.clientY - space.top; } + catch (e$1) { return null } + var coords = coordsChar(cm, x, y), line; + if (forRect && coords.xRel > 0 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) { + var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length; + coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff)); + } + return coords + } + + // Find the view element corresponding to a given line. Return null + // when the line isn't visible. + function findViewIndex(cm, n) { + if (n >= cm.display.viewTo) { return null } + n -= cm.display.viewFrom; + if (n < 0) { return null } + var view = cm.display.view; + for (var i = 0; i < view.length; i++) { + n -= view[i].size; + if (n < 0) { return i } + } + } + + // Updates the display.view data structure for a given change to the + // document. From and to are in pre-change coordinates. Lendiff is + // the amount of lines added or subtracted by the change. This is + // used for changes that span multiple lines, or change the way + // lines are divided into visual lines. regLineChange (below) + // registers single-line changes. + function regChange(cm, from, to, lendiff) { + if (from == null) { from = cm.doc.first; } + if (to == null) { to = cm.doc.first + cm.doc.size; } + if (!lendiff) { lendiff = 0; } + + var display = cm.display; + if (lendiff && to < display.viewTo && + (display.updateLineNumbers == null || display.updateLineNumbers > from)) + { display.updateLineNumbers = from; } + + cm.curOp.viewChanged = true; + + if (from >= display.viewTo) { // Change after + if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo) + { resetView(cm); } + } else if (to <= display.viewFrom) { // Change before + if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) { + resetView(cm); + } else { + display.viewFrom += lendiff; + display.viewTo += lendiff; + } + } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap + resetView(cm); + } else if (from <= display.viewFrom) { // Top overlap + var cut = viewCuttingPoint(cm, to, to + lendiff, 1); + if (cut) { + display.view = display.view.slice(cut.index); + display.viewFrom = cut.lineN; + display.viewTo += lendiff; + } else { + resetView(cm); + } + } else if (to >= display.viewTo) { // Bottom overlap + var cut$1 = viewCuttingPoint(cm, from, from, -1); + if (cut$1) { + display.view = display.view.slice(0, cut$1.index); + display.viewTo = cut$1.lineN; + } else { + resetView(cm); + } + } else { // Gap in the middle + var cutTop = viewCuttingPoint(cm, from, from, -1); + var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1); + if (cutTop && cutBot) { + display.view = display.view.slice(0, cutTop.index) + .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN)) + .concat(display.view.slice(cutBot.index)); + display.viewTo += lendiff; + } else { + resetView(cm); + } + } + + var ext = display.externalMeasured; + if (ext) { + if (to < ext.lineN) + { ext.lineN += lendiff; } + else if (from < ext.lineN + ext.size) + { display.externalMeasured = null; } + } + } + + // Register a change to a single line. Type must be one of "text", + // "gutter", "class", "widget" + function regLineChange(cm, line, type) { + cm.curOp.viewChanged = true; + var display = cm.display, ext = cm.display.externalMeasured; + if (ext && line >= ext.lineN && line < ext.lineN + ext.size) + { display.externalMeasured = null; } + + if (line < display.viewFrom || line >= display.viewTo) { return } + var lineView = display.view[findViewIndex(cm, line)]; + if (lineView.node == null) { return } + var arr = lineView.changes || (lineView.changes = []); + if (indexOf(arr, type) == -1) { arr.push(type); } + } + + // Clear the view. + function resetView(cm) { + cm.display.viewFrom = cm.display.viewTo = cm.doc.first; + cm.display.view = []; + cm.display.viewOffset = 0; + } + + function viewCuttingPoint(cm, oldN, newN, dir) { + var index = findViewIndex(cm, oldN), diff, view = cm.display.view; + if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size) + { return {index: index, lineN: newN} } + var n = cm.display.viewFrom; + for (var i = 0; i < index; i++) + { n += view[i].size; } + if (n != oldN) { + if (dir > 0) { + if (index == view.length - 1) { return null } + diff = (n + view[index].size) - oldN; + index++; + } else { + diff = n - oldN; + } + oldN += diff; newN += diff; + } + while (visualLineNo(cm.doc, newN) != newN) { + if (index == (dir < 0 ? 0 : view.length - 1)) { return null } + newN += dir * view[index - (dir < 0 ? 1 : 0)].size; + index += dir; + } + return {index: index, lineN: newN} + } + + // Force the view to cover a given range, adding empty view element + // or clipping off existing ones as needed. + function adjustView(cm, from, to) { + var display = cm.display, view = display.view; + if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) { + display.view = buildViewArray(cm, from, to); + display.viewFrom = from; + } else { + if (display.viewFrom > from) + { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); } + else if (display.viewFrom < from) + { display.view = display.view.slice(findViewIndex(cm, from)); } + display.viewFrom = from; + if (display.viewTo < to) + { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); } + else if (display.viewTo > to) + { display.view = display.view.slice(0, findViewIndex(cm, to)); } + } + display.viewTo = to; + } + + // Count the number of lines in the view whose DOM representation is + // out of date (or nonexistent). + function countDirtyView(cm) { + var view = cm.display.view, dirty = 0; + for (var i = 0; i < view.length; i++) { + var lineView = view[i]; + if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; } + } + return dirty + } + + function updateSelection(cm) { + cm.display.input.showSelection(cm.display.input.prepareSelection()); + } + + function prepareSelection(cm, primary) { + if ( primary === void 0 ) primary = true; + + var doc = cm.doc, result = {}; + var curFragment = result.cursors = document.createDocumentFragment(); + var selFragment = result.selection = document.createDocumentFragment(); + + for (var i = 0; i < doc.sel.ranges.length; i++) { + if (!primary && i == doc.sel.primIndex) { continue } + var range = doc.sel.ranges[i]; + if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue } + var collapsed = range.empty(); + if (collapsed || cm.options.showCursorWhenSelecting) + { drawSelectionCursor(cm, range.head, curFragment); } + if (!collapsed) + { drawSelectionRange(cm, range, selFragment); } + } + return result + } + + // Draws a cursor for the given range + function drawSelectionCursor(cm, head, output) { + var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine); + + var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")); + cursor.style.left = pos.left + "px"; + cursor.style.top = pos.top + "px"; + cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"; + + if (pos.other) { + // Secondary cursor, shown when on a 'jump' in bi-directional text + var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor")); + otherCursor.style.display = ""; + otherCursor.style.left = pos.other.left + "px"; + otherCursor.style.top = pos.other.top + "px"; + otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"; + } + } + + function cmpCoords(a, b) { return a.top - b.top || a.left - b.left } + + // Draws the given range as a highlighted selection + function drawSelectionRange(cm, range, output) { + var display = cm.display, doc = cm.doc; + var fragment = document.createDocumentFragment(); + var padding = paddingH(cm.display), leftSide = padding.left; + var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right; + var docLTR = doc.direction == "ltr"; + + function add(left, top, width, bottom) { + if (top < 0) { top = 0; } + top = Math.round(top); + bottom = Math.round(bottom); + fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: absolute; left: " + left + "px;\n top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px;\n height: " + (bottom - top) + "px"))); + } + + function drawForLine(line, fromArg, toArg) { + var lineObj = getLine(doc, line); + var lineLen = lineObj.text.length; + var start, end; + function coords(ch, bias) { + return charCoords(cm, Pos(line, ch), "div", lineObj, bias) + } + + function wrapX(pos, dir, side) { + var extent = wrappedLineExtentChar(cm, lineObj, null, pos); + var prop = (dir == "ltr") == (side == "after") ? "left" : "right"; + var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1); + return coords(ch, prop)[prop] + } + + var order = getOrder(lineObj, doc.direction); + iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) { + var ltr = dir == "ltr"; + var fromPos = coords(from, ltr ? "left" : "right"); + var toPos = coords(to - 1, ltr ? "right" : "left"); + + var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen; + var first = i == 0, last = !order || i == order.length - 1; + if (toPos.top - fromPos.top <= 3) { // Single line + var openLeft = (docLTR ? openStart : openEnd) && first; + var openRight = (docLTR ? openEnd : openStart) && last; + var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left; + var right = openRight ? rightSide : (ltr ? toPos : fromPos).right; + add(left, fromPos.top, right - left, fromPos.bottom); + } else { // Multiple lines + var topLeft, topRight, botLeft, botRight; + if (ltr) { + topLeft = docLTR && openStart && first ? leftSide : fromPos.left; + topRight = docLTR ? rightSide : wrapX(from, dir, "before"); + botLeft = docLTR ? leftSide : wrapX(to, dir, "after"); + botRight = docLTR && openEnd && last ? rightSide : toPos.right; + } else { + topLeft = !docLTR ? leftSide : wrapX(from, dir, "before"); + topRight = !docLTR && openStart && first ? rightSide : fromPos.right; + botLeft = !docLTR && openEnd && last ? leftSide : toPos.left; + botRight = !docLTR ? rightSide : wrapX(to, dir, "after"); + } + add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom); + if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); } + add(botLeft, toPos.top, botRight - botLeft, toPos.bottom); + } + + if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; } + if (cmpCoords(toPos, start) < 0) { start = toPos; } + if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; } + if (cmpCoords(toPos, end) < 0) { end = toPos; } + }); + return {start: start, end: end} + } + + var sFrom = range.from(), sTo = range.to(); + if (sFrom.line == sTo.line) { + drawForLine(sFrom.line, sFrom.ch, sTo.ch); + } else { + var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line); + var singleVLine = visualLine(fromLine) == visualLine(toLine); + var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end; + var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start; + if (singleVLine) { + if (leftEnd.top < rightStart.top - 2) { + add(leftEnd.right, leftEnd.top, null, leftEnd.bottom); + add(leftSide, rightStart.top, rightStart.left, rightStart.bottom); + } else { + add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom); + } + } + if (leftEnd.bottom < rightStart.top) + { add(leftSide, leftEnd.bottom, null, rightStart.top); } + } + + output.appendChild(fragment); + } + + // Cursor-blinking + function restartBlink(cm) { + if (!cm.state.focused) { return } + var display = cm.display; + clearInterval(display.blinker); + var on = true; + display.cursorDiv.style.visibility = ""; + if (cm.options.cursorBlinkRate > 0) + { display.blinker = setInterval(function () { + if (!cm.hasFocus()) { onBlur(cm); } + display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; + }, cm.options.cursorBlinkRate); } + else if (cm.options.cursorBlinkRate < 0) + { display.cursorDiv.style.visibility = "hidden"; } + } + + function ensureFocus(cm) { + if (!cm.hasFocus()) { + cm.display.input.focus(); + if (!cm.state.focused) { onFocus(cm); } + } + } + + function delayBlurEvent(cm) { + cm.state.delayingBlurEvent = true; + setTimeout(function () { if (cm.state.delayingBlurEvent) { + cm.state.delayingBlurEvent = false; + if (cm.state.focused) { onBlur(cm); } + } }, 100); + } + + function onFocus(cm, e) { + if (cm.state.delayingBlurEvent && !cm.state.draggingText) { cm.state.delayingBlurEvent = false; } + + if (cm.options.readOnly == "nocursor") { return } + if (!cm.state.focused) { + signal(cm, "focus", cm, e); + cm.state.focused = true; + addClass(cm.display.wrapper, "CodeMirror-focused"); + // This test prevents this from firing when a context + // menu is closed (since the input reset would kill the + // select-all detection hack) + if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) { + cm.display.input.reset(); + if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730 + } + cm.display.input.receivedFocus(); + } + restartBlink(cm); + } + function onBlur(cm, e) { + if (cm.state.delayingBlurEvent) { return } + + if (cm.state.focused) { + signal(cm, "blur", cm, e); + cm.state.focused = false; + rmClass(cm.display.wrapper, "CodeMirror-focused"); + } + clearInterval(cm.display.blinker); + setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150); + } + + // Read the actual heights of the rendered lines, and update their + // stored heights to match. + function updateHeightsInViewport(cm) { + var display = cm.display; + var prevBottom = display.lineDiv.offsetTop; + for (var i = 0; i < display.view.length; i++) { + var cur = display.view[i], wrapping = cm.options.lineWrapping; + var height = (void 0), width = 0; + if (cur.hidden) { continue } + if (ie && ie_version < 8) { + var bot = cur.node.offsetTop + cur.node.offsetHeight; + height = bot - prevBottom; + prevBottom = bot; + } else { + var box = cur.node.getBoundingClientRect(); + height = box.bottom - box.top; + // Check that lines don't extend past the right of the current + // editor width + if (!wrapping && cur.text.firstChild) + { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; } + } + var diff = cur.line.height - height; + if (diff > .005 || diff < -.005) { + updateLineHeight(cur.line, height); + updateWidgetHeight(cur.line); + if (cur.rest) { for (var j = 0; j < cur.rest.length; j++) + { updateWidgetHeight(cur.rest[j]); } } + } + if (width > cm.display.sizerWidth) { + var chWidth = Math.ceil(width / charWidth(cm.display)); + if (chWidth > cm.display.maxLineLength) { + cm.display.maxLineLength = chWidth; + cm.display.maxLine = cur.line; + cm.display.maxLineChanged = true; + } + } + } + } + + // Read and store the height of line widgets associated with the + // given line. + function updateWidgetHeight(line) { + if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) { + var w = line.widgets[i], parent = w.node.parentNode; + if (parent) { w.height = parent.offsetHeight; } + } } + } + + // Compute the lines that are visible in a given viewport (defaults + // the the current scroll position). viewport may contain top, + // height, and ensure (see op.scrollToPos) properties. + function visibleLines(display, doc, viewport) { + var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop; + top = Math.floor(top - paddingTop(display)); + var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight; + + var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom); + // Ensure is a {from: {line, ch}, to: {line, ch}} object, and + // forces those lines into the viewport (if possible). + if (viewport && viewport.ensure) { + var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line; + if (ensureFrom < from) { + from = ensureFrom; + to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight); + } else if (Math.min(ensureTo, doc.lastLine()) >= to) { + from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight); + to = ensureTo; + } + } + return {from: from, to: Math.max(to, from + 1)} + } + + // SCROLLING THINGS INTO VIEW + + // If an editor sits on the top or bottom of the window, partially + // scrolled out of view, this ensures that the cursor is visible. + function maybeScrollWindow(cm, rect) { + if (signalDOMEvent(cm, "scrollCursorIntoView")) { return } + + var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null; + if (rect.top + box.top < 0) { doScroll = true; } + else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; } + if (doScroll != null && !phantom) { + var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n top: " + (rect.top - display.viewOffset - paddingTop(cm.display)) + "px;\n height: " + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + "px;\n left: " + (rect.left) + "px; width: " + (Math.max(2, rect.right - rect.left)) + "px;")); + cm.display.lineSpace.appendChild(scrollNode); + scrollNode.scrollIntoView(doScroll); + cm.display.lineSpace.removeChild(scrollNode); + } + } + + // Scroll a given position into view (immediately), verifying that + // it actually became visible (as line heights are accurately + // measured, the position of something may 'drift' during drawing). + function scrollPosIntoView(cm, pos, end, margin) { + if (margin == null) { margin = 0; } + var rect; + if (!cm.options.lineWrapping && pos == end) { + // Set pos and end to the cursor positions around the character pos sticks to + // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch + // If pos == Pos(_, 0, "before"), pos and end are unchanged + pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos; + end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos; + } + for (var limit = 0; limit < 5; limit++) { + var changed = false; + var coords = cursorCoords(cm, pos); + var endCoords = !end || end == pos ? coords : cursorCoords(cm, end); + rect = {left: Math.min(coords.left, endCoords.left), + top: Math.min(coords.top, endCoords.top) - margin, + right: Math.max(coords.left, endCoords.left), + bottom: Math.max(coords.bottom, endCoords.bottom) + margin}; + var scrollPos = calculateScrollPos(cm, rect); + var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft; + if (scrollPos.scrollTop != null) { + updateScrollTop(cm, scrollPos.scrollTop); + if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; } + } + if (scrollPos.scrollLeft != null) { + setScrollLeft(cm, scrollPos.scrollLeft); + if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; } + } + if (!changed) { break } + } + return rect + } + + // Scroll a given set of coordinates into view (immediately). + function scrollIntoView(cm, rect) { + var scrollPos = calculateScrollPos(cm, rect); + if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); } + if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); } + } + + // Calculate a new scroll position needed to scroll the given + // rectangle into view. Returns an object with scrollTop and + // scrollLeft properties. When these are undefined, the + // vertical/horizontal position does not need to be adjusted. + function calculateScrollPos(cm, rect) { + var display = cm.display, snapMargin = textHeight(cm.display); + if (rect.top < 0) { rect.top = 0; } + var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop; + var screen = displayHeight(cm), result = {}; + if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; } + var docBottom = cm.doc.height + paddingVert(display); + var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin; + if (rect.top < screentop) { + result.scrollTop = atTop ? 0 : rect.top; + } else if (rect.bottom > screentop + screen) { + var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen); + if (newTop != screentop) { result.scrollTop = newTop; } + } + + var gutterSpace = cm.options.fixedGutter ? 0 : display.gutters.offsetWidth; + var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft - gutterSpace; + var screenw = displayWidth(cm) - display.gutters.offsetWidth; + var tooWide = rect.right - rect.left > screenw; + if (tooWide) { rect.right = rect.left + screenw; } + if (rect.left < 10) + { result.scrollLeft = 0; } + else if (rect.left < screenleft) + { result.scrollLeft = Math.max(0, rect.left + gutterSpace - (tooWide ? 0 : 10)); } + else if (rect.right > screenw + screenleft - 3) + { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; } + return result + } + + // Store a relative adjustment to the scroll position in the current + // operation (to be applied when the operation finishes). + function addToScrollTop(cm, top) { + if (top == null) { return } + resolveScrollToPos(cm); + cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top; + } + + // Make sure that at the end of the operation the current cursor is + // shown. + function ensureCursorVisible(cm) { + resolveScrollToPos(cm); + var cur = cm.getCursor(); + cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin}; + } + + function scrollToCoords(cm, x, y) { + if (x != null || y != null) { resolveScrollToPos(cm); } + if (x != null) { cm.curOp.scrollLeft = x; } + if (y != null) { cm.curOp.scrollTop = y; } + } + + function scrollToRange(cm, range) { + resolveScrollToPos(cm); + cm.curOp.scrollToPos = range; + } + + // When an operation has its scrollToPos property set, and another + // scroll action is applied before the end of the operation, this + // 'simulates' scrolling that position into view in a cheap way, so + // that the effect of intermediate scroll commands is not ignored. + function resolveScrollToPos(cm) { + var range = cm.curOp.scrollToPos; + if (range) { + cm.curOp.scrollToPos = null; + var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to); + scrollToCoordsRange(cm, from, to, range.margin); + } + } + + function scrollToCoordsRange(cm, from, to, margin) { + var sPos = calculateScrollPos(cm, { + left: Math.min(from.left, to.left), + top: Math.min(from.top, to.top) - margin, + right: Math.max(from.right, to.right), + bottom: Math.max(from.bottom, to.bottom) + margin + }); + scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop); + } + + // Sync the scrollable area and scrollbars, ensure the viewport + // covers the visible area. + function updateScrollTop(cm, val) { + if (Math.abs(cm.doc.scrollTop - val) < 2) { return } + if (!gecko) { updateDisplaySimple(cm, {top: val}); } + setScrollTop(cm, val, true); + if (gecko) { updateDisplaySimple(cm); } + startWorker(cm, 100); + } + + function setScrollTop(cm, val, forceScroll) { + val = Math.max(0, Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val)); + if (cm.display.scroller.scrollTop == val && !forceScroll) { return } + cm.doc.scrollTop = val; + cm.display.scrollbars.setScrollTop(val); + if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; } + } + + // Sync scroller and scrollbar, ensure the gutter elements are + // aligned. + function setScrollLeft(cm, val, isScroller, forceScroll) { + val = Math.max(0, Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth)); + if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return } + cm.doc.scrollLeft = val; + alignHorizontally(cm); + if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; } + cm.display.scrollbars.setScrollLeft(val); + } + + // SCROLLBARS + + // Prepare DOM reads needed to update the scrollbars. Done in one + // shot to minimize update/measure roundtrips. + function measureForScrollbars(cm) { + var d = cm.display, gutterW = d.gutters.offsetWidth; + var docH = Math.round(cm.doc.height + paddingVert(cm.display)); + return { + clientHeight: d.scroller.clientHeight, + viewHeight: d.wrapper.clientHeight, + scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth, + viewWidth: d.wrapper.clientWidth, + barLeft: cm.options.fixedGutter ? gutterW : 0, + docHeight: docH, + scrollHeight: docH + scrollGap(cm) + d.barHeight, + nativeBarWidth: d.nativeBarWidth, + gutterWidth: gutterW + } + } + + var NativeScrollbars = function(place, scroll, cm) { + this.cm = cm; + var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar"); + var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar"); + vert.tabIndex = horiz.tabIndex = -1; + place(vert); place(horiz); + + on(vert, "scroll", function () { + if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); } + }); + on(horiz, "scroll", function () { + if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); } + }); + + this.checkedZeroWidth = false; + // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). + if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; } + }; + + NativeScrollbars.prototype.update = function (measure) { + var needsH = measure.scrollWidth > measure.clientWidth + 1; + var needsV = measure.scrollHeight > measure.clientHeight + 1; + var sWidth = measure.nativeBarWidth; + + if (needsV) { + this.vert.style.display = "block"; + this.vert.style.bottom = needsH ? sWidth + "px" : "0"; + var totalHeight = measure.viewHeight - (needsH ? sWidth : 0); + // A bug in IE8 can cause this value to be negative, so guard it. + this.vert.firstChild.style.height = + Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"; + } else { + this.vert.style.display = ""; + this.vert.firstChild.style.height = "0"; + } + + if (needsH) { + this.horiz.style.display = "block"; + this.horiz.style.right = needsV ? sWidth + "px" : "0"; + this.horiz.style.left = measure.barLeft + "px"; + var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0); + this.horiz.firstChild.style.width = + Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px"; + } else { + this.horiz.style.display = ""; + this.horiz.firstChild.style.width = "0"; + } + + if (!this.checkedZeroWidth && measure.clientHeight > 0) { + if (sWidth == 0) { this.zeroWidthHack(); } + this.checkedZeroWidth = true; + } + + return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0} + }; + + NativeScrollbars.prototype.setScrollLeft = function (pos) { + if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; } + if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); } + }; + + NativeScrollbars.prototype.setScrollTop = function (pos) { + if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; } + if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); } + }; + + NativeScrollbars.prototype.zeroWidthHack = function () { + var w = mac && !mac_geMountainLion ? "12px" : "18px"; + this.horiz.style.height = this.vert.style.width = w; + this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none"; + this.disableHoriz = new Delayed; + this.disableVert = new Delayed; + }; + + NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) { + bar.style.pointerEvents = "auto"; + function maybeDisable() { + // To find out whether the scrollbar is still visible, we + // check whether the element under the pixel in the bottom + // right corner of the scrollbar box is the scrollbar box + // itself (when the bar is still visible) or its filler child + // (when the bar is hidden). If it is still visible, we keep + // it enabled, if it's hidden, we disable pointer events. + var box = bar.getBoundingClientRect(); + var elt = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2) + : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1); + if (elt != bar) { bar.style.pointerEvents = "none"; } + else { delay.set(1000, maybeDisable); } + } + delay.set(1000, maybeDisable); + }; + + NativeScrollbars.prototype.clear = function () { + var parent = this.horiz.parentNode; + parent.removeChild(this.horiz); + parent.removeChild(this.vert); + }; + + var NullScrollbars = function () {}; + + NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} }; + NullScrollbars.prototype.setScrollLeft = function () {}; + NullScrollbars.prototype.setScrollTop = function () {}; + NullScrollbars.prototype.clear = function () {}; + + function updateScrollbars(cm, measure) { + if (!measure) { measure = measureForScrollbars(cm); } + var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight; + updateScrollbarsInner(cm, measure); + for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) { + if (startWidth != cm.display.barWidth && cm.options.lineWrapping) + { updateHeightsInViewport(cm); } + updateScrollbarsInner(cm, measureForScrollbars(cm)); + startWidth = cm.display.barWidth; startHeight = cm.display.barHeight; + } + } + + // Re-synchronize the fake scrollbars with the actual size of the + // content. + function updateScrollbarsInner(cm, measure) { + var d = cm.display; + var sizes = d.scrollbars.update(measure); + + d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"; + d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"; + d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"; + + if (sizes.right && sizes.bottom) { + d.scrollbarFiller.style.display = "block"; + d.scrollbarFiller.style.height = sizes.bottom + "px"; + d.scrollbarFiller.style.width = sizes.right + "px"; + } else { d.scrollbarFiller.style.display = ""; } + if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) { + d.gutterFiller.style.display = "block"; + d.gutterFiller.style.height = sizes.bottom + "px"; + d.gutterFiller.style.width = measure.gutterWidth + "px"; + } else { d.gutterFiller.style.display = ""; } + } + + var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}; + + function initScrollbars(cm) { + if (cm.display.scrollbars) { + cm.display.scrollbars.clear(); + if (cm.display.scrollbars.addClass) + { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); } + } + + cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) { + cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller); + // Prevent clicks in the scrollbars from killing focus + on(node, "mousedown", function () { + if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); } + }); + node.setAttribute("cm-not-content", "true"); + }, function (pos, axis) { + if (axis == "horizontal") { setScrollLeft(cm, pos); } + else { updateScrollTop(cm, pos); } + }, cm); + if (cm.display.scrollbars.addClass) + { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); } + } + + // Operations are used to wrap a series of changes to the editor + // state in such a way that each change won't have to update the + // cursor and display (which would be awkward, slow, and + // error-prone). Instead, display updates are batched and then all + // combined and executed at once. + + var nextOpId = 0; + // Start a new operation. + function startOperation(cm) { + cm.curOp = { + cm: cm, + viewChanged: false, // Flag that indicates that lines might need to be redrawn + startHeight: cm.doc.height, // Used to detect need to update scrollbar + forceUpdate: false, // Used to force a redraw + updateInput: 0, // Whether to reset the input textarea + typing: false, // Whether this reset should be careful to leave existing text (for compositing) + changeObjs: null, // Accumulated changes, for firing change events + cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on + cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already + selectionChanged: false, // Whether the selection needs to be redrawn + updateMaxLine: false, // Set when the widest line needs to be determined anew + scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet + scrollToPos: null, // Used to scroll to a specific position + focus: false, + id: ++nextOpId // Unique ID + }; + pushOperation(cm.curOp); + } + + // Finish an operation, updating the display and signalling delayed events + function endOperation(cm) { + var op = cm.curOp; + if (op) { finishOperation(op, function (group) { + for (var i = 0; i < group.ops.length; i++) + { group.ops[i].cm.curOp = null; } + endOperations(group); + }); } + } + + // The DOM updates done when an operation finishes are batched so + // that the minimum number of relayouts are required. + function endOperations(group) { + var ops = group.ops; + for (var i = 0; i < ops.length; i++) // Read DOM + { endOperation_R1(ops[i]); } + for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe) + { endOperation_W1(ops[i$1]); } + for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM + { endOperation_R2(ops[i$2]); } + for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe) + { endOperation_W2(ops[i$3]); } + for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM + { endOperation_finish(ops[i$4]); } + } + + function endOperation_R1(op) { + var cm = op.cm, display = cm.display; + maybeClipScrollbars(cm); + if (op.updateMaxLine) { findMaxLine(cm); } + + op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null || + op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom || + op.scrollToPos.to.line >= display.viewTo) || + display.maxLineChanged && cm.options.lineWrapping; + op.update = op.mustUpdate && + new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate); + } + + function endOperation_W1(op) { + op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update); + } + + function endOperation_R2(op) { + var cm = op.cm, display = cm.display; + if (op.updatedDisplay) { updateHeightsInViewport(cm); } + + op.barMeasure = measureForScrollbars(cm); + + // If the max line changed since it was last measured, measure it, + // and ensure the document's width matches it. + // updateDisplay_W2 will use these properties to do the actual resizing + if (display.maxLineChanged && !cm.options.lineWrapping) { + op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3; + cm.display.sizerWidth = op.adjustWidthTo; + op.barMeasure.scrollWidth = + Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth); + op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm)); + } + + if (op.updatedDisplay || op.selectionChanged) + { op.preparedSelection = display.input.prepareSelection(); } + } + + function endOperation_W2(op) { + var cm = op.cm; + + if (op.adjustWidthTo != null) { + cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"; + if (op.maxScrollLeft < cm.doc.scrollLeft) + { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); } + cm.display.maxLineChanged = false; + } + + var takeFocus = op.focus && op.focus == activeElt(); + if (op.preparedSelection) + { cm.display.input.showSelection(op.preparedSelection, takeFocus); } + if (op.updatedDisplay || op.startHeight != cm.doc.height) + { updateScrollbars(cm, op.barMeasure); } + if (op.updatedDisplay) + { setDocumentHeight(cm, op.barMeasure); } + + if (op.selectionChanged) { restartBlink(cm); } + + if (cm.state.focused && op.updateInput) + { cm.display.input.reset(op.typing); } + if (takeFocus) { ensureFocus(op.cm); } + } + + function endOperation_finish(op) { + var cm = op.cm, display = cm.display, doc = cm.doc; + + if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); } + + // Abort mouse wheel delta measurement, when scrolling explicitly + if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos)) + { display.wheelStartX = display.wheelStartY = null; } + + // Propagate the scroll position to the actual DOM scroller + if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); } + + if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); } + // If we need to scroll a specific position into view, do so. + if (op.scrollToPos) { + var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from), + clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin); + maybeScrollWindow(cm, rect); + } + + // Fire events for markers that are hidden/unidden by editing or + // undoing + var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers; + if (hidden) { for (var i = 0; i < hidden.length; ++i) + { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } } + if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1) + { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } } + + if (display.wrapper.offsetHeight) + { doc.scrollTop = cm.display.scroller.scrollTop; } + + // Fire change events, and delayed event handlers + if (op.changeObjs) + { signal(cm, "changes", cm, op.changeObjs); } + if (op.update) + { op.update.finish(); } + } + + // Run the given function in an operation + function runInOp(cm, f) { + if (cm.curOp) { return f() } + startOperation(cm); + try { return f() } + finally { endOperation(cm); } + } + // Wraps a function in an operation. Returns the wrapped function. + function operation(cm, f) { + return function() { + if (cm.curOp) { return f.apply(cm, arguments) } + startOperation(cm); + try { return f.apply(cm, arguments) } + finally { endOperation(cm); } + } + } + // Used to add methods to editor and doc instances, wrapping them in + // operations. + function methodOp(f) { + return function() { + if (this.curOp) { return f.apply(this, arguments) } + startOperation(this); + try { return f.apply(this, arguments) } + finally { endOperation(this); } + } + } + function docMethodOp(f) { + return function() { + var cm = this.cm; + if (!cm || cm.curOp) { return f.apply(this, arguments) } + startOperation(cm); + try { return f.apply(this, arguments) } + finally { endOperation(cm); } + } + } + + // HIGHLIGHT WORKER + + function startWorker(cm, time) { + if (cm.doc.highlightFrontier < cm.display.viewTo) + { cm.state.highlight.set(time, bind(highlightWorker, cm)); } + } + + function highlightWorker(cm) { + var doc = cm.doc; + if (doc.highlightFrontier >= cm.display.viewTo) { return } + var end = +new Date + cm.options.workTime; + var context = getContextBefore(cm, doc.highlightFrontier); + var changedLines = []; + + doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) { + if (context.line >= cm.display.viewFrom) { // Visible + var oldStyles = line.styles; + var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null; + var highlighted = highlightLine(cm, line, context, true); + if (resetState) { context.state = resetState; } + line.styles = highlighted.styles; + var oldCls = line.styleClasses, newCls = highlighted.classes; + if (newCls) { line.styleClasses = newCls; } + else if (oldCls) { line.styleClasses = null; } + var ischange = !oldStyles || oldStyles.length != line.styles.length || + oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass); + for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; } + if (ischange) { changedLines.push(context.line); } + line.stateAfter = context.save(); + context.nextLine(); + } else { + if (line.text.length <= cm.options.maxHighlightLength) + { processLine(cm, line.text, context); } + line.stateAfter = context.line % 5 == 0 ? context.save() : null; + context.nextLine(); + } + if (+new Date > end) { + startWorker(cm, cm.options.workDelay); + return true + } + }); + doc.highlightFrontier = context.line; + doc.modeFrontier = Math.max(doc.modeFrontier, context.line); + if (changedLines.length) { runInOp(cm, function () { + for (var i = 0; i < changedLines.length; i++) + { regLineChange(cm, changedLines[i], "text"); } + }); } + } + + // DISPLAY DRAWING + + var DisplayUpdate = function(cm, viewport, force) { + var display = cm.display; + + this.viewport = viewport; + // Store some values that we'll need later (but don't want to force a relayout for) + this.visible = visibleLines(display, cm.doc, viewport); + this.editorIsHidden = !display.wrapper.offsetWidth; + this.wrapperHeight = display.wrapper.clientHeight; + this.wrapperWidth = display.wrapper.clientWidth; + this.oldDisplayWidth = displayWidth(cm); + this.force = force; + this.dims = getDimensions(cm); + this.events = []; + }; + + DisplayUpdate.prototype.signal = function (emitter, type) { + if (hasHandler(emitter, type)) + { this.events.push(arguments); } + }; + DisplayUpdate.prototype.finish = function () { + for (var i = 0; i < this.events.length; i++) + { signal.apply(null, this.events[i]); } + }; + + function maybeClipScrollbars(cm) { + var display = cm.display; + if (!display.scrollbarsClipped && display.scroller.offsetWidth) { + display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth; + display.heightForcer.style.height = scrollGap(cm) + "px"; + display.sizer.style.marginBottom = -display.nativeBarWidth + "px"; + display.sizer.style.borderRightWidth = scrollGap(cm) + "px"; + display.scrollbarsClipped = true; + } + } + + function selectionSnapshot(cm) { + if (cm.hasFocus()) { return null } + var active = activeElt(); + if (!active || !contains(cm.display.lineDiv, active)) { return null } + var result = {activeElt: active}; + if (window.getSelection) { + var sel = window.getSelection(); + if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) { + result.anchorNode = sel.anchorNode; + result.anchorOffset = sel.anchorOffset; + result.focusNode = sel.focusNode; + result.focusOffset = sel.focusOffset; + } + } + return result + } + + function restoreSelection(snapshot) { + if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return } + snapshot.activeElt.focus(); + if (!/^(INPUT|TEXTAREA)$/.test(snapshot.activeElt.nodeName) && + snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) { + var sel = window.getSelection(), range = document.createRange(); + range.setEnd(snapshot.anchorNode, snapshot.anchorOffset); + range.collapse(false); + sel.removeAllRanges(); + sel.addRange(range); + sel.extend(snapshot.focusNode, snapshot.focusOffset); + } + } + + // Does the actual updating of the line display. Bails out + // (returning false) when there is nothing to be done and forced is + // false. + function updateDisplayIfNeeded(cm, update) { + var display = cm.display, doc = cm.doc; + + if (update.editorIsHidden) { + resetView(cm); + return false + } + + // Bail out if the visible area is already rendered and nothing changed. + if (!update.force && + update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo && + (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) && + display.renderedView == display.view && countDirtyView(cm) == 0) + { return false } + + if (maybeUpdateLineNumberWidth(cm)) { + resetView(cm); + update.dims = getDimensions(cm); + } + + // Compute a suitable new viewport (from & to) + var end = doc.first + doc.size; + var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first); + var to = Math.min(end, update.visible.to + cm.options.viewportMargin); + if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); } + if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); } + if (sawCollapsedSpans) { + from = visualLineNo(cm.doc, from); + to = visualLineEndNo(cm.doc, to); + } + + var different = from != display.viewFrom || to != display.viewTo || + display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth; + adjustView(cm, from, to); + + display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom)); + // Position the mover div to align with the current scroll position + cm.display.mover.style.top = display.viewOffset + "px"; + + var toUpdate = countDirtyView(cm); + if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view && + (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo)) + { return false } + + // For big changes, we hide the enclosing element during the + // update, since that speeds up the operations on most browsers. + var selSnapshot = selectionSnapshot(cm); + if (toUpdate > 4) { display.lineDiv.style.display = "none"; } + patchDisplay(cm, display.updateLineNumbers, update.dims); + if (toUpdate > 4) { display.lineDiv.style.display = ""; } + display.renderedView = display.view; + // There might have been a widget with a focused element that got + // hidden or updated, if so re-focus it. + restoreSelection(selSnapshot); + + // Prevent selection and cursors from interfering with the scroll + // width and height. + removeChildren(display.cursorDiv); + removeChildren(display.selectionDiv); + display.gutters.style.height = display.sizer.style.minHeight = 0; + + if (different) { + display.lastWrapHeight = update.wrapperHeight; + display.lastWrapWidth = update.wrapperWidth; + startWorker(cm, 400); + } + + display.updateLineNumbers = null; + + return true + } + + function postUpdateDisplay(cm, update) { + var viewport = update.viewport; + + for (var first = true;; first = false) { + if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) { + // Clip forced viewport to actual scrollable area. + if (viewport && viewport.top != null) + { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; } + // Updated line heights might result in the drawn area not + // actually covering the viewport. Keep looping until it does. + update.visible = visibleLines(cm.display, cm.doc, viewport); + if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo) + { break } + } else if (first) { + update.visible = visibleLines(cm.display, cm.doc, viewport); + } + if (!updateDisplayIfNeeded(cm, update)) { break } + updateHeightsInViewport(cm); + var barMeasure = measureForScrollbars(cm); + updateSelection(cm); + updateScrollbars(cm, barMeasure); + setDocumentHeight(cm, barMeasure); + update.force = false; + } + + update.signal(cm, "update", cm); + if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) { + update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo); + cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo; + } + } + + function updateDisplaySimple(cm, viewport) { + var update = new DisplayUpdate(cm, viewport); + if (updateDisplayIfNeeded(cm, update)) { + updateHeightsInViewport(cm); + postUpdateDisplay(cm, update); + var barMeasure = measureForScrollbars(cm); + updateSelection(cm); + updateScrollbars(cm, barMeasure); + setDocumentHeight(cm, barMeasure); + update.finish(); + } + } + + // Sync the actual display DOM structure with display.view, removing + // nodes for lines that are no longer in view, and creating the ones + // that are not there yet, and updating the ones that are out of + // date. + function patchDisplay(cm, updateNumbersFrom, dims) { + var display = cm.display, lineNumbers = cm.options.lineNumbers; + var container = display.lineDiv, cur = container.firstChild; + + function rm(node) { + var next = node.nextSibling; + // Works around a throw-scroll bug in OS X Webkit + if (webkit && mac && cm.display.currentWheelTarget == node) + { node.style.display = "none"; } + else + { node.parentNode.removeChild(node); } + return next + } + + var view = display.view, lineN = display.viewFrom; + // Loop over the elements in the view, syncing cur (the DOM nodes + // in display.lineDiv) with the view as we go. + for (var i = 0; i < view.length; i++) { + var lineView = view[i]; + if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet + var node = buildLineElement(cm, lineView, lineN, dims); + container.insertBefore(node, cur); + } else { // Already drawn + while (cur != lineView.node) { cur = rm(cur); } + var updateNumber = lineNumbers && updateNumbersFrom != null && + updateNumbersFrom <= lineN && lineView.lineNumber; + if (lineView.changes) { + if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; } + updateLineForChanges(cm, lineView, lineN, dims); + } + if (updateNumber) { + removeChildren(lineView.lineNumber); + lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN))); + } + cur = lineView.node.nextSibling; + } + lineN += lineView.size; + } + while (cur) { cur = rm(cur); } + } + + function updateGutterSpace(display) { + var width = display.gutters.offsetWidth; + display.sizer.style.marginLeft = width + "px"; + } + + function setDocumentHeight(cm, measure) { + cm.display.sizer.style.minHeight = measure.docHeight + "px"; + cm.display.heightForcer.style.top = measure.docHeight + "px"; + cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px"; + } + + // Re-align line numbers and gutter marks to compensate for + // horizontal scrolling. + function alignHorizontally(cm) { + var display = cm.display, view = display.view; + if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return } + var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft; + var gutterW = display.gutters.offsetWidth, left = comp + "px"; + for (var i = 0; i < view.length; i++) { if (!view[i].hidden) { + if (cm.options.fixedGutter) { + if (view[i].gutter) + { view[i].gutter.style.left = left; } + if (view[i].gutterBackground) + { view[i].gutterBackground.style.left = left; } + } + var align = view[i].alignable; + if (align) { for (var j = 0; j < align.length; j++) + { align[j].style.left = left; } } + } } + if (cm.options.fixedGutter) + { display.gutters.style.left = (comp + gutterW) + "px"; } + } + + // Used to ensure that the line number gutter is still the right + // size for the current document size. Returns true when an update + // is needed. + function maybeUpdateLineNumberWidth(cm) { + if (!cm.options.lineNumbers) { return false } + var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display; + if (last.length != display.lineNumChars) { + var test = display.measure.appendChild(elt("div", [elt("div", last)], + "CodeMirror-linenumber CodeMirror-gutter-elt")); + var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW; + display.lineGutter.style.width = ""; + display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1; + display.lineNumWidth = display.lineNumInnerWidth + padding; + display.lineNumChars = display.lineNumInnerWidth ? last.length : -1; + display.lineGutter.style.width = display.lineNumWidth + "px"; + updateGutterSpace(cm.display); + return true + } + return false + } + + function getGutters(gutters, lineNumbers) { + var result = [], sawLineNumbers = false; + for (var i = 0; i < gutters.length; i++) { + var name = gutters[i], style = null; + if (typeof name != "string") { style = name.style; name = name.className; } + if (name == "CodeMirror-linenumbers") { + if (!lineNumbers) { continue } + else { sawLineNumbers = true; } + } + result.push({className: name, style: style}); + } + if (lineNumbers && !sawLineNumbers) { result.push({className: "CodeMirror-linenumbers", style: null}); } + return result + } + + // Rebuild the gutter elements, ensure the margin to the left of the + // code matches their width. + function renderGutters(display) { + var gutters = display.gutters, specs = display.gutterSpecs; + removeChildren(gutters); + display.lineGutter = null; + for (var i = 0; i < specs.length; ++i) { + var ref = specs[i]; + var className = ref.className; + var style = ref.style; + var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + className)); + if (style) { gElt.style.cssText = style; } + if (className == "CodeMirror-linenumbers") { + display.lineGutter = gElt; + gElt.style.width = (display.lineNumWidth || 1) + "px"; + } + } + gutters.style.display = specs.length ? "" : "none"; + updateGutterSpace(display); + } + + function updateGutters(cm) { + renderGutters(cm.display); + regChange(cm); + alignHorizontally(cm); + } + + // The display handles the DOM integration, both for input reading + // and content drawing. It holds references to DOM nodes and + // display-related state. + + function Display(place, doc, input, options) { + var d = this; + this.input = input; + + // Covers bottom-right square when both scrollbars are present. + d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); + d.scrollbarFiller.setAttribute("cm-not-content", "true"); + // Covers bottom of gutter when coverGutterNextToScrollbar is on + // and h scrollbar is present. + d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler"); + d.gutterFiller.setAttribute("cm-not-content", "true"); + // Will contain the actual code, positioned to cover the viewport. + d.lineDiv = eltP("div", null, "CodeMirror-code"); + // Elements are added to these to represent selection and cursors. + d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); + d.cursorDiv = elt("div", null, "CodeMirror-cursors"); + // A visibility: hidden element used to find the size of things. + d.measure = elt("div", null, "CodeMirror-measure"); + // When lines outside of the viewport are measured, they are drawn in this. + d.lineMeasure = elt("div", null, "CodeMirror-measure"); + // Wraps everything that needs to exist inside the vertically-padded coordinate system + d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv], + null, "position: relative; outline: none"); + var lines = eltP("div", [d.lineSpace], "CodeMirror-lines"); + // Moved around its parent to cover visible view. + d.mover = elt("div", [lines], null, "position: relative"); + // Set to the height of the document, allowing scrolling. + d.sizer = elt("div", [d.mover], "CodeMirror-sizer"); + d.sizerWidth = null; + // Behavior of elts with overflow: auto and padding is + // inconsistent across browsers. This is used to ensure the + // scrollable area is big enough. + d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;"); + // Will contain the gutters, if any. + d.gutters = elt("div", null, "CodeMirror-gutters"); + d.lineGutter = null; + // Actual scrollable element. + d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll"); + d.scroller.setAttribute("tabIndex", "-1"); + // The element in which the editor lives. + d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror"); + + // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported) + if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; } + if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; } + + if (place) { + if (place.appendChild) { place.appendChild(d.wrapper); } + else { place(d.wrapper); } + } + + // Current rendered range (may be bigger than the view window). + d.viewFrom = d.viewTo = doc.first; + d.reportedViewFrom = d.reportedViewTo = doc.first; + // Information about the rendered lines. + d.view = []; + d.renderedView = null; + // Holds info about a single rendered line when it was rendered + // for measurement, while not in view. + d.externalMeasured = null; + // Empty space (in pixels) above the view + d.viewOffset = 0; + d.lastWrapHeight = d.lastWrapWidth = 0; + d.updateLineNumbers = null; + + d.nativeBarWidth = d.barHeight = d.barWidth = 0; + d.scrollbarsClipped = false; + + // Used to only resize the line number gutter when necessary (when + // the amount of lines crosses a boundary that makes its width change) + d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null; + // Set to true when a non-horizontal-scrolling line widget is + // added. As an optimization, line widget aligning is skipped when + // this is false. + d.alignWidgets = false; + + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; + + // Tracks the maximum line length so that the horizontal scrollbar + // can be kept static when scrolling. + d.maxLine = null; + d.maxLineLength = 0; + d.maxLineChanged = false; + + // Used for measuring wheel scrolling granularity + d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null; + + // True when shift is held down. + d.shift = false; + + // Used to track whether anything happened since the context menu + // was opened. + d.selForContextMenu = null; + + d.activeTouch = null; + + d.gutterSpecs = getGutters(options.gutters, options.lineNumbers); + renderGutters(d); + + input.init(d); + } + + // Since the delta values reported on mouse wheel events are + // unstandardized between browsers and even browser versions, and + // generally horribly unpredictable, this code starts by measuring + // the scroll effect that the first few mouse wheel events have, + // and, from that, detects the way it can convert deltas to pixel + // offsets afterwards. + // + // The reason we want to know the amount a wheel event will scroll + // is that it gives us a chance to update the display before the + // actual scrolling happens, reducing flickering. + + var wheelSamples = 0, wheelPixelsPerUnit = null; + // Fill in a browser-detected starting value on browsers where we + // know one. These don't have to be accurate -- the result of them + // being wrong would just be a slight flicker on the first wheel + // scroll (if it is large enough). + if (ie) { wheelPixelsPerUnit = -.53; } + else if (gecko) { wheelPixelsPerUnit = 15; } + else if (chrome) { wheelPixelsPerUnit = -.7; } + else if (safari) { wheelPixelsPerUnit = -1/3; } + + function wheelEventDelta(e) { + var dx = e.wheelDeltaX, dy = e.wheelDeltaY; + if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; } + if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; } + else if (dy == null) { dy = e.wheelDelta; } + return {x: dx, y: dy} + } + function wheelEventPixels(e) { + var delta = wheelEventDelta(e); + delta.x *= wheelPixelsPerUnit; + delta.y *= wheelPixelsPerUnit; + return delta + } + + function onScrollWheel(cm, e) { + var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y; + + var display = cm.display, scroll = display.scroller; + // Quit if there's nothing to scroll here + var canScrollX = scroll.scrollWidth > scroll.clientWidth; + var canScrollY = scroll.scrollHeight > scroll.clientHeight; + if (!(dx && canScrollX || dy && canScrollY)) { return } + + // Webkit browsers on OS X abort momentum scrolls when the target + // of the scroll event is removed from the scrollable element. + // This hack (see related code in patchDisplay) makes sure the + // element is kept around. + if (dy && mac && webkit) { + outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) { + for (var i = 0; i < view.length; i++) { + if (view[i].node == cur) { + cm.display.currentWheelTarget = cur; + break outer + } + } + } + } + + // On some browsers, horizontal scrolling will cause redraws to + // happen before the gutter has been realigned, causing it to + // wriggle around in a most unseemly way. When we have an + // estimated pixels/delta value, we just handle horizontal + // scrolling entirely here. It'll be slightly off from native, but + // better than glitching out. + if (dx && !gecko && !presto && wheelPixelsPerUnit != null) { + if (dy && canScrollY) + { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); } + setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit)); + // Only prevent default scrolling if vertical scrolling is + // actually possible. Otherwise, it causes vertical scroll + // jitter on OSX trackpads when deltaX is small and deltaY + // is large (issue #3579) + if (!dy || (dy && canScrollY)) + { e_preventDefault(e); } + display.wheelStartX = null; // Abort measurement, if in progress + return + } + + // 'Project' the visible viewport to cover the area that is being + // scrolled into view (if we know enough to estimate it). + if (dy && wheelPixelsPerUnit != null) { + var pixels = dy * wheelPixelsPerUnit; + var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight; + if (pixels < 0) { top = Math.max(0, top + pixels - 50); } + else { bot = Math.min(cm.doc.height, bot + pixels + 50); } + updateDisplaySimple(cm, {top: top, bottom: bot}); + } + + if (wheelSamples < 20) { + if (display.wheelStartX == null) { + display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop; + display.wheelDX = dx; display.wheelDY = dy; + setTimeout(function () { + if (display.wheelStartX == null) { return } + var movedX = scroll.scrollLeft - display.wheelStartX; + var movedY = scroll.scrollTop - display.wheelStartY; + var sample = (movedY && display.wheelDY && movedY / display.wheelDY) || + (movedX && display.wheelDX && movedX / display.wheelDX); + display.wheelStartX = display.wheelStartY = null; + if (!sample) { return } + wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1); + ++wheelSamples; + }, 200); + } else { + display.wheelDX += dx; display.wheelDY += dy; + } + } + } + + // Selection objects are immutable. A new one is created every time + // the selection changes. A selection is one or more non-overlapping + // (and non-touching) ranges, sorted, and an integer that indicates + // which one is the primary selection (the one that's scrolled into + // view, that getCursor returns, etc). + var Selection = function(ranges, primIndex) { + this.ranges = ranges; + this.primIndex = primIndex; + }; + + Selection.prototype.primary = function () { return this.ranges[this.primIndex] }; + + Selection.prototype.equals = function (other) { + if (other == this) { return true } + if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false } + for (var i = 0; i < this.ranges.length; i++) { + var here = this.ranges[i], there = other.ranges[i]; + if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false } + } + return true + }; + + Selection.prototype.deepCopy = function () { + var out = []; + for (var i = 0; i < this.ranges.length; i++) + { out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)); } + return new Selection(out, this.primIndex) + }; + + Selection.prototype.somethingSelected = function () { + for (var i = 0; i < this.ranges.length; i++) + { if (!this.ranges[i].empty()) { return true } } + return false + }; + + Selection.prototype.contains = function (pos, end) { + if (!end) { end = pos; } + for (var i = 0; i < this.ranges.length; i++) { + var range = this.ranges[i]; + if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) + { return i } + } + return -1 + }; + + var Range = function(anchor, head) { + this.anchor = anchor; this.head = head; + }; + + Range.prototype.from = function () { return minPos(this.anchor, this.head) }; + Range.prototype.to = function () { return maxPos(this.anchor, this.head) }; + Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch }; + + // Take an unsorted, potentially overlapping set of ranges, and + // build a selection out of it. 'Consumes' ranges array (modifying + // it). + function normalizeSelection(cm, ranges, primIndex) { + var mayTouch = cm && cm.options.selectionsMayTouch; + var prim = ranges[primIndex]; + ranges.sort(function (a, b) { return cmp(a.from(), b.from()); }); + primIndex = indexOf(ranges, prim); + for (var i = 1; i < ranges.length; i++) { + var cur = ranges[i], prev = ranges[i - 1]; + var diff = cmp(prev.to(), cur.from()); + if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) { + var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to()); + var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head; + if (i <= primIndex) { --primIndex; } + ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to)); + } + } + return new Selection(ranges, primIndex) + } + + function simpleSelection(anchor, head) { + return new Selection([new Range(anchor, head || anchor)], 0) + } + + // Compute the position of the end of a change (its 'to' property + // refers to the pre-change end). + function changeEnd(change) { + if (!change.text) { return change.to } + return Pos(change.from.line + change.text.length - 1, + lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)) + } + + // Adjust a position to refer to the post-change position of the + // same text, or the end of the change if the change covers it. + function adjustForChange(pos, change) { + if (cmp(pos, change.from) < 0) { return pos } + if (cmp(pos, change.to) <= 0) { return changeEnd(change) } + + var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch; + if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; } + return Pos(line, ch) + } + + function computeSelAfterChange(doc, change) { + var out = []; + for (var i = 0; i < doc.sel.ranges.length; i++) { + var range = doc.sel.ranges[i]; + out.push(new Range(adjustForChange(range.anchor, change), + adjustForChange(range.head, change))); + } + return normalizeSelection(doc.cm, out, doc.sel.primIndex) + } + + function offsetPos(pos, old, nw) { + if (pos.line == old.line) + { return Pos(nw.line, pos.ch - old.ch + nw.ch) } + else + { return Pos(nw.line + (pos.line - old.line), pos.ch) } + } + + // Used by replaceSelections to allow moving the selection to the + // start or around the replaced test. Hint may be "start" or "around". + function computeReplacedSel(doc, changes, hint) { + var out = []; + var oldPrev = Pos(doc.first, 0), newPrev = oldPrev; + for (var i = 0; i < changes.length; i++) { + var change = changes[i]; + var from = offsetPos(change.from, oldPrev, newPrev); + var to = offsetPos(changeEnd(change), oldPrev, newPrev); + oldPrev = change.to; + newPrev = to; + if (hint == "around") { + var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0; + out[i] = new Range(inv ? to : from, inv ? from : to); + } else { + out[i] = new Range(from, from); + } + } + return new Selection(out, doc.sel.primIndex) + } + + // Used to get the editor into a consistent state again when options change. + + function loadMode(cm) { + cm.doc.mode = getMode(cm.options, cm.doc.modeOption); + resetModeState(cm); + } + + function resetModeState(cm) { + cm.doc.iter(function (line) { + if (line.stateAfter) { line.stateAfter = null; } + if (line.styles) { line.styles = null; } + }); + cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first; + startWorker(cm, 100); + cm.state.modeGen++; + if (cm.curOp) { regChange(cm); } + } + + // DOCUMENT DATA STRUCTURE + + // By default, updates that start and end at the beginning of a line + // are treated specially, in order to make the association of line + // widgets and marker elements with the text behave more intuitive. + function isWholeLineUpdate(doc, change) { + return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" && + (!doc.cm || doc.cm.options.wholeLineUpdateBefore) + } + + // Perform a change on the document data structure. + function updateDoc(doc, change, markedSpans, estimateHeight) { + function spansFor(n) {return markedSpans ? markedSpans[n] : null} + function update(line, text, spans) { + updateLine(line, text, spans, estimateHeight); + signalLater(line, "change", line, change); + } + function linesFor(start, end) { + var result = []; + for (var i = start; i < end; ++i) + { result.push(new Line(text[i], spansFor(i), estimateHeight)); } + return result + } + + var from = change.from, to = change.to, text = change.text; + var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line); + var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line; + + // Adjust the line structure + if (change.full) { + doc.insert(0, linesFor(0, text.length)); + doc.remove(text.length, doc.size - text.length); + } else if (isWholeLineUpdate(doc, change)) { + // This is a whole-line replace. Treated specially to make + // sure line objects move the way they are supposed to. + var added = linesFor(0, text.length - 1); + update(lastLine, lastLine.text, lastSpans); + if (nlines) { doc.remove(from.line, nlines); } + if (added.length) { doc.insert(from.line, added); } + } else if (firstLine == lastLine) { + if (text.length == 1) { + update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans); + } else { + var added$1 = linesFor(1, text.length - 1); + added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight)); + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); + doc.insert(from.line + 1, added$1); + } + } else if (text.length == 1) { + update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0)); + doc.remove(from.line + 1, nlines); + } else { + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); + update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans); + var added$2 = linesFor(1, text.length - 1); + if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); } + doc.insert(from.line + 1, added$2); + } + + signalLater(doc, "change", doc, change); + } + + // Call f for all linked documents. + function linkedDocs(doc, f, sharedHistOnly) { + function propagate(doc, skip, sharedHist) { + if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) { + var rel = doc.linked[i]; + if (rel.doc == skip) { continue } + var shared = sharedHist && rel.sharedHist; + if (sharedHistOnly && !shared) { continue } + f(rel.doc, shared); + propagate(rel.doc, doc, shared); + } } + } + propagate(doc, null, true); + } + + // Attach a document to an editor. + function attachDoc(cm, doc) { + if (doc.cm) { throw new Error("This document is already in use.") } + cm.doc = doc; + doc.cm = cm; + estimateLineHeights(cm); + loadMode(cm); + setDirectionClass(cm); + if (!cm.options.lineWrapping) { findMaxLine(cm); } + cm.options.mode = doc.modeOption; + regChange(cm); + } + + function setDirectionClass(cm) { + (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl"); + } + + function directionChanged(cm) { + runInOp(cm, function () { + setDirectionClass(cm); + regChange(cm); + }); + } + + function History(startGen) { + // Arrays of change events and selections. Doing something adds an + // event to done and clears undo. Undoing moves events from done + // to undone, redoing moves them in the other direction. + this.done = []; this.undone = []; + this.undoDepth = Infinity; + // Used to track when changes can be merged into a single undo + // event + this.lastModTime = this.lastSelTime = 0; + this.lastOp = this.lastSelOp = null; + this.lastOrigin = this.lastSelOrigin = null; + // Used by the isClean() method + this.generation = this.maxGeneration = startGen || 1; + } + + // Create a history change event from an updateDoc-style change + // object. + function historyChangeFromChange(doc, change) { + var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}; + attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); + linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true); + return histChange + } + + // Pop all selection events off the end of a history array. Stop at + // a change event. + function clearSelectionEvents(array) { + while (array.length) { + var last = lst(array); + if (last.ranges) { array.pop(); } + else { break } + } + } + + // Find the top change event in the history. Pop off selection + // events that are in the way. + function lastChangeEvent(hist, force) { + if (force) { + clearSelectionEvents(hist.done); + return lst(hist.done) + } else if (hist.done.length && !lst(hist.done).ranges) { + return lst(hist.done) + } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) { + hist.done.pop(); + return lst(hist.done) + } + } + + // Register a change in the history. Merges changes that are within + // a single operation, or are close together with an origin that + // allows merging (starting with "+") into a single event. + function addChangeToHistory(doc, change, selAfter, opId) { + var hist = doc.history; + hist.undone.length = 0; + var time = +new Date, cur; + var last; + + if ((hist.lastOp == opId || + hist.lastOrigin == change.origin && change.origin && + ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) || + change.origin.charAt(0) == "*")) && + (cur = lastChangeEvent(hist, hist.lastOp == opId))) { + // Merge this change into the last event + last = lst(cur.changes); + if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) { + // Optimized case for simple insertion -- don't want to add + // new changesets for every character typed + last.to = changeEnd(change); + } else { + // Add new sub-event + cur.changes.push(historyChangeFromChange(doc, change)); + } + } else { + // Can not be merged, start a new event. + var before = lst(hist.done); + if (!before || !before.ranges) + { pushSelectionToHistory(doc.sel, hist.done); } + cur = {changes: [historyChangeFromChange(doc, change)], + generation: hist.generation}; + hist.done.push(cur); + while (hist.done.length > hist.undoDepth) { + hist.done.shift(); + if (!hist.done[0].ranges) { hist.done.shift(); } + } + } + hist.done.push(selAfter); + hist.generation = ++hist.maxGeneration; + hist.lastModTime = hist.lastSelTime = time; + hist.lastOp = hist.lastSelOp = opId; + hist.lastOrigin = hist.lastSelOrigin = change.origin; + + if (!last) { signal(doc, "historyAdded"); } + } + + function selectionEventCanBeMerged(doc, origin, prev, sel) { + var ch = origin.charAt(0); + return ch == "*" || + ch == "+" && + prev.ranges.length == sel.ranges.length && + prev.somethingSelected() == sel.somethingSelected() && + new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500) + } + + // Called whenever the selection changes, sets the new selection as + // the pending selection in the history, and pushes the old pending + // selection into the 'done' array when it was significantly + // different (in number of selected ranges, emptiness, or time). + function addSelectionToHistory(doc, sel, opId, options) { + var hist = doc.history, origin = options && options.origin; + + // A new event is started when the previous origin does not match + // the current, or the origins don't allow matching. Origins + // starting with * are always merged, those starting with + are + // merged when similar and close together in time. + if (opId == hist.lastSelOp || + (origin && hist.lastSelOrigin == origin && + (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin || + selectionEventCanBeMerged(doc, origin, lst(hist.done), sel)))) + { hist.done[hist.done.length - 1] = sel; } + else + { pushSelectionToHistory(sel, hist.done); } + + hist.lastSelTime = +new Date; + hist.lastSelOrigin = origin; + hist.lastSelOp = opId; + if (options && options.clearRedo !== false) + { clearSelectionEvents(hist.undone); } + } + + function pushSelectionToHistory(sel, dest) { + var top = lst(dest); + if (!(top && top.ranges && top.equals(sel))) + { dest.push(sel); } + } + + // Used to store marked span information in the history. + function attachLocalSpans(doc, change, from, to) { + var existing = change["spans_" + doc.id], n = 0; + doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) { + if (line.markedSpans) + { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; } + ++n; + }); + } + + // When un/re-doing restores text containing marked spans, those + // that have been explicitly cleared should not be restored. + function removeClearedSpans(spans) { + if (!spans) { return null } + var out; + for (var i = 0; i < spans.length; ++i) { + if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } } + else if (out) { out.push(spans[i]); } + } + return !out ? spans : out.length ? out : null + } + + // Retrieve and filter the old marked spans stored in a change event. + function getOldSpans(doc, change) { + var found = change["spans_" + doc.id]; + if (!found) { return null } + var nw = []; + for (var i = 0; i < change.text.length; ++i) + { nw.push(removeClearedSpans(found[i])); } + return nw + } + + // Used for un/re-doing changes from the history. Combines the + // result of computing the existing spans with the set of spans that + // existed in the history (so that deleting around a span and then + // undoing brings back the span). + function mergeOldSpans(doc, change) { + var old = getOldSpans(doc, change); + var stretched = stretchSpansOverChange(doc, change); + if (!old) { return stretched } + if (!stretched) { return old } + + for (var i = 0; i < old.length; ++i) { + var oldCur = old[i], stretchCur = stretched[i]; + if (oldCur && stretchCur) { + spans: for (var j = 0; j < stretchCur.length; ++j) { + var span = stretchCur[j]; + for (var k = 0; k < oldCur.length; ++k) + { if (oldCur[k].marker == span.marker) { continue spans } } + oldCur.push(span); + } + } else if (stretchCur) { + old[i] = stretchCur; + } + } + return old + } + + // Used both to provide a JSON-safe object in .getHistory, and, when + // detaching a document, to split the history in two + function copyHistoryArray(events, newGroup, instantiateSel) { + var copy = []; + for (var i = 0; i < events.length; ++i) { + var event = events[i]; + if (event.ranges) { + copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event); + continue + } + var changes = event.changes, newChanges = []; + copy.push({changes: newChanges}); + for (var j = 0; j < changes.length; ++j) { + var change = changes[j], m = (void 0); + newChanges.push({from: change.from, to: change.to, text: change.text}); + if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) { + if (indexOf(newGroup, Number(m[1])) > -1) { + lst(newChanges)[prop] = change[prop]; + delete change[prop]; + } + } } } + } + } + return copy + } + + // The 'scroll' parameter given to many of these indicated whether + // the new cursor position should be scrolled into view after + // modifying the selection. + + // If shift is held or the extend flag is set, extends a range to + // include a given position (and optionally a second position). + // Otherwise, simply returns the range between the given positions. + // Used for cursor motion and such. + function extendRange(range, head, other, extend) { + if (extend) { + var anchor = range.anchor; + if (other) { + var posBefore = cmp(head, anchor) < 0; + if (posBefore != (cmp(other, anchor) < 0)) { + anchor = head; + head = other; + } else if (posBefore != (cmp(head, other) < 0)) { + head = other; + } + } + return new Range(anchor, head) + } else { + return new Range(other || head, head) + } + } + + // Extend the primary selection range, discard the rest. + function extendSelection(doc, head, other, options, extend) { + if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); } + setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options); + } + + // Extend all selections (pos is an array of selections with length + // equal the number of selections) + function extendSelections(doc, heads, options) { + var out = []; + var extend = doc.cm && (doc.cm.display.shift || doc.extend); + for (var i = 0; i < doc.sel.ranges.length; i++) + { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); } + var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex); + setSelection(doc, newSel, options); + } + + // Updates a single range in the selection. + function replaceOneSelection(doc, i, range, options) { + var ranges = doc.sel.ranges.slice(0); + ranges[i] = range; + setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options); + } + + // Reset the selection to a single range. + function setSimpleSelection(doc, anchor, head, options) { + setSelection(doc, simpleSelection(anchor, head), options); + } + + // Give beforeSelectionChange handlers a change to influence a + // selection update. + function filterSelectionChange(doc, sel, options) { + var obj = { + ranges: sel.ranges, + update: function(ranges) { + this.ranges = []; + for (var i = 0; i < ranges.length; i++) + { this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), + clipPos(doc, ranges[i].head)); } + }, + origin: options && options.origin + }; + signal(doc, "beforeSelectionChange", doc, obj); + if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); } + if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) } + else { return sel } + } + + function setSelectionReplaceHistory(doc, sel, options) { + var done = doc.history.done, last = lst(done); + if (last && last.ranges) { + done[done.length - 1] = sel; + setSelectionNoUndo(doc, sel, options); + } else { + setSelection(doc, sel, options); + } + } + + // Set a new selection. + function setSelection(doc, sel, options) { + setSelectionNoUndo(doc, sel, options); + addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options); + } + + function setSelectionNoUndo(doc, sel, options) { + if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) + { sel = filterSelectionChange(doc, sel, options); } + + var bias = options && options.bias || + (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1); + setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true)); + + if (!(options && options.scroll === false) && doc.cm) + { ensureCursorVisible(doc.cm); } + } + + function setSelectionInner(doc, sel) { + if (sel.equals(doc.sel)) { return } + + doc.sel = sel; + + if (doc.cm) { + doc.cm.curOp.updateInput = 1; + doc.cm.curOp.selectionChanged = true; + signalCursorActivity(doc.cm); + } + signalLater(doc, "cursorActivity", doc); + } + + // Verify that the selection does not partially select any atomic + // marked ranges. + function reCheckSelection(doc) { + setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false)); + } + + // Return a selection that does not partially select any atomic + // ranges. + function skipAtomicInSelection(doc, sel, bias, mayClear) { + var out; + for (var i = 0; i < sel.ranges.length; i++) { + var range = sel.ranges[i]; + var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]; + var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear); + var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear); + if (out || newAnchor != range.anchor || newHead != range.head) { + if (!out) { out = sel.ranges.slice(0, i); } + out[i] = new Range(newAnchor, newHead); + } + } + return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel + } + + function skipAtomicInner(doc, pos, oldPos, dir, mayClear) { + var line = getLine(doc, pos.line); + if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { + var sp = line.markedSpans[i], m = sp.marker; + + // Determine if we should prevent the cursor being placed to the left/right of an atomic marker + // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it + // is with selectLeft/Right + var preventCursorLeft = ("selectLeft" in m) ? !m.selectLeft : m.inclusiveLeft; + var preventCursorRight = ("selectRight" in m) ? !m.selectRight : m.inclusiveRight; + + if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) && + (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) { + if (mayClear) { + signal(m, "beforeCursorEnter"); + if (m.explicitlyCleared) { + if (!line.markedSpans) { break } + else {--i; continue} + } + } + if (!m.atomic) { continue } + + if (oldPos) { + var near = m.find(dir < 0 ? 1 : -1), diff = (void 0); + if (dir < 0 ? preventCursorRight : preventCursorLeft) + { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); } + if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0)) + { return skipAtomicInner(doc, near, pos, dir, mayClear) } + } + + var far = m.find(dir < 0 ? -1 : 1); + if (dir < 0 ? preventCursorLeft : preventCursorRight) + { far = movePos(doc, far, dir, far.line == pos.line ? line : null); } + return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null + } + } } + return pos + } + + // Ensure a given position is not inside an atomic range. + function skipAtomic(doc, pos, oldPos, bias, mayClear) { + var dir = bias || 1; + var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) || + (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) || + skipAtomicInner(doc, pos, oldPos, -dir, mayClear) || + (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true)); + if (!found) { + doc.cantEdit = true; + return Pos(doc.first, 0) + } + return found + } + + function movePos(doc, pos, dir, line) { + if (dir < 0 && pos.ch == 0) { + if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) } + else { return null } + } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) { + if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) } + else { return null } + } else { + return new Pos(pos.line, pos.ch + dir) + } + } + + function selectAll(cm) { + cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll); + } + + // UPDATING + + // Allow "beforeChange" event handlers to influence a change + function filterChange(doc, change, update) { + var obj = { + canceled: false, + from: change.from, + to: change.to, + text: change.text, + origin: change.origin, + cancel: function () { return obj.canceled = true; } + }; + if (update) { obj.update = function (from, to, text, origin) { + if (from) { obj.from = clipPos(doc, from); } + if (to) { obj.to = clipPos(doc, to); } + if (text) { obj.text = text; } + if (origin !== undefined) { obj.origin = origin; } + }; } + signal(doc, "beforeChange", doc, obj); + if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); } + + if (obj.canceled) { + if (doc.cm) { doc.cm.curOp.updateInput = 2; } + return null + } + return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin} + } + + // Apply a change to a document, and add it to the document's + // history, and propagating it to all linked documents. + function makeChange(doc, change, ignoreReadOnly) { + if (doc.cm) { + if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) } + if (doc.cm.state.suppressEdits) { return } + } + + if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) { + change = filterChange(doc, change, true); + if (!change) { return } + } + + // Possibly split or suppress the update based on the presence + // of read-only spans in its range. + var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to); + if (split) { + for (var i = split.length - 1; i >= 0; --i) + { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); } + } else { + makeChangeInner(doc, change); + } + } + + function makeChangeInner(doc, change) { + if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return } + var selAfter = computeSelAfterChange(doc, change); + addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN); + + makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)); + var rebased = []; + + linkedDocs(doc, function (doc, sharedHist) { + if (!sharedHist && indexOf(rebased, doc.history) == -1) { + rebaseHist(doc.history, change); + rebased.push(doc.history); + } + makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change)); + }); + } + + // Revert a change stored in a document's history. + function makeChangeFromHistory(doc, type, allowSelectionOnly) { + var suppress = doc.cm && doc.cm.state.suppressEdits; + if (suppress && !allowSelectionOnly) { return } + + var hist = doc.history, event, selAfter = doc.sel; + var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done; + + // Verify that there is a useable event (so that ctrl-z won't + // needlessly clear selection events) + var i = 0; + for (; i < source.length; i++) { + event = source[i]; + if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges) + { break } + } + if (i == source.length) { return } + hist.lastOrigin = hist.lastSelOrigin = null; + + for (;;) { + event = source.pop(); + if (event.ranges) { + pushSelectionToHistory(event, dest); + if (allowSelectionOnly && !event.equals(doc.sel)) { + setSelection(doc, event, {clearRedo: false}); + return + } + selAfter = event; + } else if (suppress) { + source.push(event); + return + } else { break } + } + + // Build up a reverse change object to add to the opposite history + // stack (redo when undoing, and vice versa). + var antiChanges = []; + pushSelectionToHistory(selAfter, dest); + dest.push({changes: antiChanges, generation: hist.generation}); + hist.generation = event.generation || ++hist.maxGeneration; + + var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange"); + + var loop = function ( i ) { + var change = event.changes[i]; + change.origin = type; + if (filter && !filterChange(doc, change, false)) { + source.length = 0; + return {} + } + + antiChanges.push(historyChangeFromChange(doc, change)); + + var after = i ? computeSelAfterChange(doc, change) : lst(source); + makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); + if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); } + var rebased = []; + + // Propagate to the linked documents + linkedDocs(doc, function (doc, sharedHist) { + if (!sharedHist && indexOf(rebased, doc.history) == -1) { + rebaseHist(doc.history, change); + rebased.push(doc.history); + } + makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change)); + }); + }; + + for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) { + var returned = loop( i$1 ); + + if ( returned ) return returned.v; + } + } + + // Sub-views need their line numbers shifted when text is added + // above or below them in the parent document. + function shiftDoc(doc, distance) { + if (distance == 0) { return } + doc.first += distance; + doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range( + Pos(range.anchor.line + distance, range.anchor.ch), + Pos(range.head.line + distance, range.head.ch) + ); }), doc.sel.primIndex); + if (doc.cm) { + regChange(doc.cm, doc.first, doc.first - distance, distance); + for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++) + { regLineChange(doc.cm, l, "gutter"); } + } + } + + // More lower-level change function, handling only a single document + // (not linked ones). + function makeChangeSingleDoc(doc, change, selAfter, spans) { + if (doc.cm && !doc.cm.curOp) + { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) } + + if (change.to.line < doc.first) { + shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line)); + return + } + if (change.from.line > doc.lastLine()) { return } + + // Clip the change to the size of this doc + if (change.from.line < doc.first) { + var shift = change.text.length - 1 - (doc.first - change.from.line); + shiftDoc(doc, shift); + change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch), + text: [lst(change.text)], origin: change.origin}; + } + var last = doc.lastLine(); + if (change.to.line > last) { + change = {from: change.from, to: Pos(last, getLine(doc, last).text.length), + text: [change.text[0]], origin: change.origin}; + } + + change.removed = getBetween(doc, change.from, change.to); + + if (!selAfter) { selAfter = computeSelAfterChange(doc, change); } + if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); } + else { updateDoc(doc, change, spans); } + setSelectionNoUndo(doc, selAfter, sel_dontScroll); + + if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0))) + { doc.cantEdit = false; } + } + + // Handle the interaction of a change to a document with the editor + // that this document is part of. + function makeChangeSingleDocInEditor(cm, change, spans) { + var doc = cm.doc, display = cm.display, from = change.from, to = change.to; + + var recomputeMaxLength = false, checkWidthStart = from.line; + if (!cm.options.lineWrapping) { + checkWidthStart = lineNo(visualLine(getLine(doc, from.line))); + doc.iter(checkWidthStart, to.line + 1, function (line) { + if (line == display.maxLine) { + recomputeMaxLength = true; + return true + } + }); + } + + if (doc.sel.contains(change.from, change.to) > -1) + { signalCursorActivity(cm); } + + updateDoc(doc, change, spans, estimateHeight(cm)); + + if (!cm.options.lineWrapping) { + doc.iter(checkWidthStart, from.line + change.text.length, function (line) { + var len = lineLength(line); + if (len > display.maxLineLength) { + display.maxLine = line; + display.maxLineLength = len; + display.maxLineChanged = true; + recomputeMaxLength = false; + } + }); + if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; } + } + + retreatFrontier(doc, from.line); + startWorker(cm, 400); + + var lendiff = change.text.length - (to.line - from.line) - 1; + // Remember that these lines changed, for updating the display + if (change.full) + { regChange(cm); } + else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change)) + { regLineChange(cm, from.line, "text"); } + else + { regChange(cm, from.line, to.line + 1, lendiff); } + + var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change"); + if (changeHandler || changesHandler) { + var obj = { + from: from, to: to, + text: change.text, + removed: change.removed, + origin: change.origin + }; + if (changeHandler) { signalLater(cm, "change", cm, obj); } + if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); } + } + cm.display.selForContextMenu = null; + } + + function replaceRange(doc, code, from, to, origin) { + var assign; + + if (!to) { to = from; } + if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); } + if (typeof code == "string") { code = doc.splitLines(code); } + makeChange(doc, {from: from, to: to, text: code, origin: origin}); + } + + // Rebasing/resetting history to deal with externally-sourced changes + + function rebaseHistSelSingle(pos, from, to, diff) { + if (to < pos.line) { + pos.line += diff; + } else if (from < pos.line) { + pos.line = from; + pos.ch = 0; + } + } + + // Tries to rebase an array of history events given a change in the + // document. If the change touches the same lines as the event, the + // event, and everything 'behind' it, is discarded. If the change is + // before the event, the event's positions are updated. Uses a + // copy-on-write scheme for the positions, to avoid having to + // reallocate them all on every rebase, but also avoid problems with + // shared position objects being unsafely updated. + function rebaseHistArray(array, from, to, diff) { + for (var i = 0; i < array.length; ++i) { + var sub = array[i], ok = true; + if (sub.ranges) { + if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; } + for (var j = 0; j < sub.ranges.length; j++) { + rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff); + rebaseHistSelSingle(sub.ranges[j].head, from, to, diff); + } + continue + } + for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) { + var cur = sub.changes[j$1]; + if (to < cur.from.line) { + cur.from = Pos(cur.from.line + diff, cur.from.ch); + cur.to = Pos(cur.to.line + diff, cur.to.ch); + } else if (from <= cur.to.line) { + ok = false; + break + } + } + if (!ok) { + array.splice(0, i + 1); + i = 0; + } + } + } + + function rebaseHist(hist, change) { + var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1; + rebaseHistArray(hist.done, from, to, diff); + rebaseHistArray(hist.undone, from, to, diff); + } + + // Utility for applying a change to a line by handle or number, + // returning the number and optionally registering the line as + // changed. + function changeLine(doc, handle, changeType, op) { + var no = handle, line = handle; + if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); } + else { no = lineNo(handle); } + if (no == null) { return null } + if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); } + return line + } + + // The document is represented as a BTree consisting of leaves, with + // chunk of lines in them, and branches, with up to ten leaves or + // other branch nodes below them. The top node is always a branch + // node, and is the document object itself (meaning it has + // additional methods and properties). + // + // All nodes have parent links. The tree is used both to go from + // line numbers to line objects, and to go from objects to numbers. + // It also indexes by height, and is used to convert between height + // and line object, and to find the total height of the document. + // + // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html + + function LeafChunk(lines) { + this.lines = lines; + this.parent = null; + var height = 0; + for (var i = 0; i < lines.length; ++i) { + lines[i].parent = this; + height += lines[i].height; + } + this.height = height; + } + + LeafChunk.prototype = { + chunkSize: function() { return this.lines.length }, + + // Remove the n lines at offset 'at'. + removeInner: function(at, n) { + for (var i = at, e = at + n; i < e; ++i) { + var line = this.lines[i]; + this.height -= line.height; + cleanUpLine(line); + signalLater(line, "delete"); + } + this.lines.splice(at, n); + }, + + // Helper used to collapse a small branch into a single leaf. + collapse: function(lines) { + lines.push.apply(lines, this.lines); + }, + + // Insert the given array of lines at offset 'at', count them as + // having the given height. + insertInner: function(at, lines, height) { + this.height += height; + this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at)); + for (var i = 0; i < lines.length; ++i) { lines[i].parent = this; } + }, + + // Used to iterate over a part of the tree. + iterN: function(at, n, op) { + for (var e = at + n; at < e; ++at) + { if (op(this.lines[at])) { return true } } + } + }; + + function BranchChunk(children) { + this.children = children; + var size = 0, height = 0; + for (var i = 0; i < children.length; ++i) { + var ch = children[i]; + size += ch.chunkSize(); height += ch.height; + ch.parent = this; + } + this.size = size; + this.height = height; + this.parent = null; + } + + BranchChunk.prototype = { + chunkSize: function() { return this.size }, + + removeInner: function(at, n) { + this.size -= n; + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at < sz) { + var rm = Math.min(n, sz - at), oldHeight = child.height; + child.removeInner(at, rm); + this.height -= oldHeight - child.height; + if (sz == rm) { this.children.splice(i--, 1); child.parent = null; } + if ((n -= rm) == 0) { break } + at = 0; + } else { at -= sz; } + } + // If the result is smaller than 25 lines, ensure that it is a + // single leaf node. + if (this.size - n < 25 && + (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) { + var lines = []; + this.collapse(lines); + this.children = [new LeafChunk(lines)]; + this.children[0].parent = this; + } + }, + + collapse: function(lines) { + for (var i = 0; i < this.children.length; ++i) { this.children[i].collapse(lines); } + }, + + insertInner: function(at, lines, height) { + this.size += lines.length; + this.height += height; + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at <= sz) { + child.insertInner(at, lines, height); + if (child.lines && child.lines.length > 50) { + // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced. + // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest. + var remaining = child.lines.length % 25 + 25; + for (var pos = remaining; pos < child.lines.length;) { + var leaf = new LeafChunk(child.lines.slice(pos, pos += 25)); + child.height -= leaf.height; + this.children.splice(++i, 0, leaf); + leaf.parent = this; + } + child.lines = child.lines.slice(0, remaining); + this.maybeSpill(); + } + break + } + at -= sz; + } + }, + + // When a node has grown, check whether it should be split. + maybeSpill: function() { + if (this.children.length <= 10) { return } + var me = this; + do { + var spilled = me.children.splice(me.children.length - 5, 5); + var sibling = new BranchChunk(spilled); + if (!me.parent) { // Become the parent node + var copy = new BranchChunk(me.children); + copy.parent = me; + me.children = [copy, sibling]; + me = copy; + } else { + me.size -= sibling.size; + me.height -= sibling.height; + var myIndex = indexOf(me.parent.children, me); + me.parent.children.splice(myIndex + 1, 0, sibling); + } + sibling.parent = me.parent; + } while (me.children.length > 10) + me.parent.maybeSpill(); + }, + + iterN: function(at, n, op) { + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at < sz) { + var used = Math.min(n, sz - at); + if (child.iterN(at, used, op)) { return true } + if ((n -= used) == 0) { break } + at = 0; + } else { at -= sz; } + } + } + }; + + // Line widgets are block elements displayed above or below a line. + + var LineWidget = function(doc, node, options) { + if (options) { for (var opt in options) { if (options.hasOwnProperty(opt)) + { this[opt] = options[opt]; } } } + this.doc = doc; + this.node = node; + }; + + LineWidget.prototype.clear = function () { + var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line); + if (no == null || !ws) { return } + for (var i = 0; i < ws.length; ++i) { if (ws[i] == this) { ws.splice(i--, 1); } } + if (!ws.length) { line.widgets = null; } + var height = widgetHeight(this); + updateLineHeight(line, Math.max(0, line.height - height)); + if (cm) { + runInOp(cm, function () { + adjustScrollWhenAboveVisible(cm, line, -height); + regLineChange(cm, no, "widget"); + }); + signalLater(cm, "lineWidgetCleared", cm, this, no); + } + }; + + LineWidget.prototype.changed = function () { + var this$1 = this; + + var oldH = this.height, cm = this.doc.cm, line = this.line; + this.height = null; + var diff = widgetHeight(this) - oldH; + if (!diff) { return } + if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); } + if (cm) { + runInOp(cm, function () { + cm.curOp.forceUpdate = true; + adjustScrollWhenAboveVisible(cm, line, diff); + signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line)); + }); + } + }; + eventMixin(LineWidget); + + function adjustScrollWhenAboveVisible(cm, line, diff) { + if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop)) + { addToScrollTop(cm, diff); } + } + + function addLineWidget(doc, handle, node, options) { + var widget = new LineWidget(doc, node, options); + var cm = doc.cm; + if (cm && widget.noHScroll) { cm.display.alignWidgets = true; } + changeLine(doc, handle, "widget", function (line) { + var widgets = line.widgets || (line.widgets = []); + if (widget.insertAt == null) { widgets.push(widget); } + else { widgets.splice(Math.min(widgets.length, Math.max(0, widget.insertAt)), 0, widget); } + widget.line = line; + if (cm && !lineIsHidden(doc, line)) { + var aboveVisible = heightAtLine(line) < doc.scrollTop; + updateLineHeight(line, line.height + widgetHeight(widget)); + if (aboveVisible) { addToScrollTop(cm, widget.height); } + cm.curOp.forceUpdate = true; + } + return true + }); + if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); } + return widget + } + + // TEXTMARKERS + + // Created with markText and setBookmark methods. A TextMarker is a + // handle that can be used to clear or find a marked position in the + // document. Line objects hold arrays (markedSpans) containing + // {from, to, marker} object pointing to such marker objects, and + // indicating that such a marker is present on that line. Multiple + // lines may point to the same marker when it spans across lines. + // The spans will have null for their from/to properties when the + // marker continues beyond the start/end of the line. Markers have + // links back to the lines they currently touch. + + // Collapsed markers have unique ids, in order to be able to order + // them, which is needed for uniquely determining an outer marker + // when they overlap (they may nest, but not partially overlap). + var nextMarkerId = 0; + + var TextMarker = function(doc, type) { + this.lines = []; + this.type = type; + this.doc = doc; + this.id = ++nextMarkerId; + }; + + // Clear the marker. + TextMarker.prototype.clear = function () { + if (this.explicitlyCleared) { return } + var cm = this.doc.cm, withOp = cm && !cm.curOp; + if (withOp) { startOperation(cm); } + if (hasHandler(this, "clear")) { + var found = this.find(); + if (found) { signalLater(this, "clear", found.from, found.to); } + } + var min = null, max = null; + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); + if (cm && !this.collapsed) { regLineChange(cm, lineNo(line), "text"); } + else if (cm) { + if (span.to != null) { max = lineNo(line); } + if (span.from != null) { min = lineNo(line); } + } + line.markedSpans = removeMarkedSpan(line.markedSpans, span); + if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm) + { updateLineHeight(line, textHeight(cm.display)); } + } + if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) { + var visual = visualLine(this.lines[i$1]), len = lineLength(visual); + if (len > cm.display.maxLineLength) { + cm.display.maxLine = visual; + cm.display.maxLineLength = len; + cm.display.maxLineChanged = true; + } + } } + + if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); } + this.lines.length = 0; + this.explicitlyCleared = true; + if (this.atomic && this.doc.cantEdit) { + this.doc.cantEdit = false; + if (cm) { reCheckSelection(cm.doc); } + } + if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); } + if (withOp) { endOperation(cm); } + if (this.parent) { this.parent.clear(); } + }; + + // Find the position of the marker in the document. Returns a {from, + // to} object by default. Side can be passed to get a specific side + // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the + // Pos objects returned contain a line object, rather than a line + // number (used to prevent looking up the same line twice). + TextMarker.prototype.find = function (side, lineObj) { + if (side == null && this.type == "bookmark") { side = 1; } + var from, to; + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); + if (span.from != null) { + from = Pos(lineObj ? line : lineNo(line), span.from); + if (side == -1) { return from } + } + if (span.to != null) { + to = Pos(lineObj ? line : lineNo(line), span.to); + if (side == 1) { return to } + } + } + return from && {from: from, to: to} + }; + + // Signals that the marker's widget changed, and surrounding layout + // should be recomputed. + TextMarker.prototype.changed = function () { + var this$1 = this; + + var pos = this.find(-1, true), widget = this, cm = this.doc.cm; + if (!pos || !cm) { return } + runInOp(cm, function () { + var line = pos.line, lineN = lineNo(pos.line); + var view = findViewForLine(cm, lineN); + if (view) { + clearLineMeasurementCacheFor(view); + cm.curOp.selectionChanged = cm.curOp.forceUpdate = true; + } + cm.curOp.updateMaxLine = true; + if (!lineIsHidden(widget.doc, line) && widget.height != null) { + var oldHeight = widget.height; + widget.height = null; + var dHeight = widgetHeight(widget) - oldHeight; + if (dHeight) + { updateLineHeight(line, line.height + dHeight); } + } + signalLater(cm, "markerChanged", cm, this$1); + }); + }; + + TextMarker.prototype.attachLine = function (line) { + if (!this.lines.length && this.doc.cm) { + var op = this.doc.cm.curOp; + if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) + { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); } + } + this.lines.push(line); + }; + + TextMarker.prototype.detachLine = function (line) { + this.lines.splice(indexOf(this.lines, line), 1); + if (!this.lines.length && this.doc.cm) { + var op = this.doc.cm.curOp + ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this); + } + }; + eventMixin(TextMarker); + + // Create a marker, wire it up to the right lines, and + function markText(doc, from, to, options, type) { + // Shared markers (across linked documents) are handled separately + // (markTextShared will call out to this again, once per + // document). + if (options && options.shared) { return markTextShared(doc, from, to, options, type) } + // Ensure we are in an operation. + if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) } + + var marker = new TextMarker(doc, type), diff = cmp(from, to); + if (options) { copyObj(options, marker, false); } + // Don't connect empty markers unless clearWhenEmpty is false + if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false) + { return marker } + if (marker.replacedWith) { + // Showing up as a widget implies collapsed (widget replaces text) + marker.collapsed = true; + marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget"); + if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); } + if (options.insertLeft) { marker.widgetNode.insertLeft = true; } + } + if (marker.collapsed) { + if (conflictingCollapsedRange(doc, from.line, from, to, marker) || + from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker)) + { throw new Error("Inserting collapsed marker partially overlapping an existing one") } + seeCollapsedSpans(); + } + + if (marker.addToHistory) + { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); } + + var curLine = from.line, cm = doc.cm, updateMaxLine; + doc.iter(curLine, to.line + 1, function (line) { + if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine) + { updateMaxLine = true; } + if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); } + addMarkedSpan(line, new MarkedSpan(marker, + curLine == from.line ? from.ch : null, + curLine == to.line ? to.ch : null)); + ++curLine; + }); + // lineIsHidden depends on the presence of the spans, so needs a second pass + if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) { + if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); } + }); } + + if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); } + + if (marker.readOnly) { + seeReadOnlySpans(); + if (doc.history.done.length || doc.history.undone.length) + { doc.clearHistory(); } + } + if (marker.collapsed) { + marker.id = ++nextMarkerId; + marker.atomic = true; + } + if (cm) { + // Sync editor state + if (updateMaxLine) { cm.curOp.updateMaxLine = true; } + if (marker.collapsed) + { regChange(cm, from.line, to.line + 1); } + else if (marker.className || marker.startStyle || marker.endStyle || marker.css || + marker.attributes || marker.title) + { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } } + if (marker.atomic) { reCheckSelection(cm.doc); } + signalLater(cm, "markerAdded", cm, marker); + } + return marker + } + + // SHARED TEXTMARKERS + + // A shared marker spans multiple linked documents. It is + // implemented as a meta-marker-object controlling multiple normal + // markers. + var SharedTextMarker = function(markers, primary) { + this.markers = markers; + this.primary = primary; + for (var i = 0; i < markers.length; ++i) + { markers[i].parent = this; } + }; + + SharedTextMarker.prototype.clear = function () { + if (this.explicitlyCleared) { return } + this.explicitlyCleared = true; + for (var i = 0; i < this.markers.length; ++i) + { this.markers[i].clear(); } + signalLater(this, "clear"); + }; + + SharedTextMarker.prototype.find = function (side, lineObj) { + return this.primary.find(side, lineObj) + }; + eventMixin(SharedTextMarker); + + function markTextShared(doc, from, to, options, type) { + options = copyObj(options); + options.shared = false; + var markers = [markText(doc, from, to, options, type)], primary = markers[0]; + var widget = options.widgetNode; + linkedDocs(doc, function (doc) { + if (widget) { options.widgetNode = widget.cloneNode(true); } + markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type)); + for (var i = 0; i < doc.linked.length; ++i) + { if (doc.linked[i].isParent) { return } } + primary = lst(markers); + }); + return new SharedTextMarker(markers, primary) + } + + function findSharedMarkers(doc) { + return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; }) + } + + function copySharedMarkers(doc, markers) { + for (var i = 0; i < markers.length; i++) { + var marker = markers[i], pos = marker.find(); + var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to); + if (cmp(mFrom, mTo)) { + var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type); + marker.markers.push(subMark); + subMark.parent = marker; + } + } + } + + function detachSharedMarkers(markers) { + var loop = function ( i ) { + var marker = markers[i], linked = [marker.primary.doc]; + linkedDocs(marker.primary.doc, function (d) { return linked.push(d); }); + for (var j = 0; j < marker.markers.length; j++) { + var subMarker = marker.markers[j]; + if (indexOf(linked, subMarker.doc) == -1) { + subMarker.parent = null; + marker.markers.splice(j--, 1); + } + } + }; + + for (var i = 0; i < markers.length; i++) loop( i ); + } + + var nextDocId = 0; + var Doc = function(text, mode, firstLine, lineSep, direction) { + if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) } + if (firstLine == null) { firstLine = 0; } + + BranchChunk.call(this, [new LeafChunk([new Line("", null)])]); + this.first = firstLine; + this.scrollTop = this.scrollLeft = 0; + this.cantEdit = false; + this.cleanGeneration = 1; + this.modeFrontier = this.highlightFrontier = firstLine; + var start = Pos(firstLine, 0); + this.sel = simpleSelection(start); + this.history = new History(null); + this.id = ++nextDocId; + this.modeOption = mode; + this.lineSep = lineSep; + this.direction = (direction == "rtl") ? "rtl" : "ltr"; + this.extend = false; + + if (typeof text == "string") { text = this.splitLines(text); } + updateDoc(this, {from: start, to: start, text: text}); + setSelection(this, simpleSelection(start), sel_dontScroll); + }; + + Doc.prototype = createObj(BranchChunk.prototype, { + constructor: Doc, + // Iterate over the document. Supports two forms -- with only one + // argument, it calls that for each line in the document. With + // three, it iterates over the range given by the first two (with + // the second being non-inclusive). + iter: function(from, to, op) { + if (op) { this.iterN(from - this.first, to - from, op); } + else { this.iterN(this.first, this.first + this.size, from); } + }, + + // Non-public interface for adding and removing lines. + insert: function(at, lines) { + var height = 0; + for (var i = 0; i < lines.length; ++i) { height += lines[i].height; } + this.insertInner(at - this.first, lines, height); + }, + remove: function(at, n) { this.removeInner(at - this.first, n); }, + + // From here, the methods are part of the public interface. Most + // are also available from CodeMirror (editor) instances. + + getValue: function(lineSep) { + var lines = getLines(this, this.first, this.first + this.size); + if (lineSep === false) { return lines } + return lines.join(lineSep || this.lineSeparator()) + }, + setValue: docMethodOp(function(code) { + var top = Pos(this.first, 0), last = this.first + this.size - 1; + makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length), + text: this.splitLines(code), origin: "setValue", full: true}, true); + if (this.cm) { scrollToCoords(this.cm, 0, 0); } + setSelection(this, simpleSelection(top), sel_dontScroll); + }), + replaceRange: function(code, from, to, origin) { + from = clipPos(this, from); + to = to ? clipPos(this, to) : from; + replaceRange(this, code, from, to, origin); + }, + getRange: function(from, to, lineSep) { + var lines = getBetween(this, clipPos(this, from), clipPos(this, to)); + if (lineSep === false) { return lines } + return lines.join(lineSep || this.lineSeparator()) + }, + + getLine: function(line) {var l = this.getLineHandle(line); return l && l.text}, + + getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }}, + getLineNumber: function(line) {return lineNo(line)}, + + getLineHandleVisualStart: function(line) { + if (typeof line == "number") { line = getLine(this, line); } + return visualLine(line) + }, + + lineCount: function() {return this.size}, + firstLine: function() {return this.first}, + lastLine: function() {return this.first + this.size - 1}, + + clipPos: function(pos) {return clipPos(this, pos)}, + + getCursor: function(start) { + var range = this.sel.primary(), pos; + if (start == null || start == "head") { pos = range.head; } + else if (start == "anchor") { pos = range.anchor; } + else if (start == "end" || start == "to" || start === false) { pos = range.to(); } + else { pos = range.from(); } + return pos + }, + listSelections: function() { return this.sel.ranges }, + somethingSelected: function() {return this.sel.somethingSelected()}, + + setCursor: docMethodOp(function(line, ch, options) { + setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options); + }), + setSelection: docMethodOp(function(anchor, head, options) { + setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options); + }), + extendSelection: docMethodOp(function(head, other, options) { + extendSelection(this, clipPos(this, head), other && clipPos(this, other), options); + }), + extendSelections: docMethodOp(function(heads, options) { + extendSelections(this, clipPosArray(this, heads), options); + }), + extendSelectionsBy: docMethodOp(function(f, options) { + var heads = map(this.sel.ranges, f); + extendSelections(this, clipPosArray(this, heads), options); + }), + setSelections: docMethodOp(function(ranges, primary, options) { + if (!ranges.length) { return } + var out = []; + for (var i = 0; i < ranges.length; i++) + { out[i] = new Range(clipPos(this, ranges[i].anchor), + clipPos(this, ranges[i].head)); } + if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); } + setSelection(this, normalizeSelection(this.cm, out, primary), options); + }), + addSelection: docMethodOp(function(anchor, head, options) { + var ranges = this.sel.ranges.slice(0); + ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor))); + setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options); + }), + + getSelection: function(lineSep) { + var ranges = this.sel.ranges, lines; + for (var i = 0; i < ranges.length; i++) { + var sel = getBetween(this, ranges[i].from(), ranges[i].to()); + lines = lines ? lines.concat(sel) : sel; + } + if (lineSep === false) { return lines } + else { return lines.join(lineSep || this.lineSeparator()) } + }, + getSelections: function(lineSep) { + var parts = [], ranges = this.sel.ranges; + for (var i = 0; i < ranges.length; i++) { + var sel = getBetween(this, ranges[i].from(), ranges[i].to()); + if (lineSep !== false) { sel = sel.join(lineSep || this.lineSeparator()); } + parts[i] = sel; + } + return parts + }, + replaceSelection: function(code, collapse, origin) { + var dup = []; + for (var i = 0; i < this.sel.ranges.length; i++) + { dup[i] = code; } + this.replaceSelections(dup, collapse, origin || "+input"); + }, + replaceSelections: docMethodOp(function(code, collapse, origin) { + var changes = [], sel = this.sel; + for (var i = 0; i < sel.ranges.length; i++) { + var range = sel.ranges[i]; + changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin}; + } + var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse); + for (var i$1 = changes.length - 1; i$1 >= 0; i$1--) + { makeChange(this, changes[i$1]); } + if (newSel) { setSelectionReplaceHistory(this, newSel); } + else if (this.cm) { ensureCursorVisible(this.cm); } + }), + undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}), + redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}), + undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}), + redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}), + + setExtending: function(val) {this.extend = val;}, + getExtending: function() {return this.extend}, + + historySize: function() { + var hist = this.history, done = 0, undone = 0; + for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } } + for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } } + return {undo: done, redo: undone} + }, + clearHistory: function() { + var this$1 = this; + + this.history = new History(this.history.maxGeneration); + linkedDocs(this, function (doc) { return doc.history = this$1.history; }, true); + }, + + markClean: function() { + this.cleanGeneration = this.changeGeneration(true); + }, + changeGeneration: function(forceSplit) { + if (forceSplit) + { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; } + return this.history.generation + }, + isClean: function (gen) { + return this.history.generation == (gen || this.cleanGeneration) + }, + + getHistory: function() { + return {done: copyHistoryArray(this.history.done), + undone: copyHistoryArray(this.history.undone)} + }, + setHistory: function(histData) { + var hist = this.history = new History(this.history.maxGeneration); + hist.done = copyHistoryArray(histData.done.slice(0), null, true); + hist.undone = copyHistoryArray(histData.undone.slice(0), null, true); + }, + + setGutterMarker: docMethodOp(function(line, gutterID, value) { + return changeLine(this, line, "gutter", function (line) { + var markers = line.gutterMarkers || (line.gutterMarkers = {}); + markers[gutterID] = value; + if (!value && isEmpty(markers)) { line.gutterMarkers = null; } + return true + }) + }), + + clearGutter: docMethodOp(function(gutterID) { + var this$1 = this; + + this.iter(function (line) { + if (line.gutterMarkers && line.gutterMarkers[gutterID]) { + changeLine(this$1, line, "gutter", function () { + line.gutterMarkers[gutterID] = null; + if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; } + return true + }); + } + }); + }), + + lineInfo: function(line) { + var n; + if (typeof line == "number") { + if (!isLine(this, line)) { return null } + n = line; + line = getLine(this, line); + if (!line) { return null } + } else { + n = lineNo(line); + if (n == null) { return null } + } + return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, + textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass, + widgets: line.widgets} + }, + + addLineClass: docMethodOp(function(handle, where, cls) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { + var prop = where == "text" ? "textClass" + : where == "background" ? "bgClass" + : where == "gutter" ? "gutterClass" : "wrapClass"; + if (!line[prop]) { line[prop] = cls; } + else if (classTest(cls).test(line[prop])) { return false } + else { line[prop] += " " + cls; } + return true + }) + }), + removeLineClass: docMethodOp(function(handle, where, cls) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { + var prop = where == "text" ? "textClass" + : where == "background" ? "bgClass" + : where == "gutter" ? "gutterClass" : "wrapClass"; + var cur = line[prop]; + if (!cur) { return false } + else if (cls == null) { line[prop] = null; } + else { + var found = cur.match(classTest(cls)); + if (!found) { return false } + var end = found.index + found[0].length; + line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null; + } + return true + }) + }), + + addLineWidget: docMethodOp(function(handle, node, options) { + return addLineWidget(this, handle, node, options) + }), + removeLineWidget: function(widget) { widget.clear(); }, + + markText: function(from, to, options) { + return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range") + }, + setBookmark: function(pos, options) { + var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options), + insertLeft: options && options.insertLeft, + clearWhenEmpty: false, shared: options && options.shared, + handleMouseEvents: options && options.handleMouseEvents}; + pos = clipPos(this, pos); + return markText(this, pos, pos, realOpts, "bookmark") + }, + findMarksAt: function(pos) { + pos = clipPos(this, pos); + var markers = [], spans = getLine(this, pos.line).markedSpans; + if (spans) { for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if ((span.from == null || span.from <= pos.ch) && + (span.to == null || span.to >= pos.ch)) + { markers.push(span.marker.parent || span.marker); } + } } + return markers + }, + findMarks: function(from, to, filter) { + from = clipPos(this, from); to = clipPos(this, to); + var found = [], lineNo = from.line; + this.iter(from.line, to.line + 1, function (line) { + var spans = line.markedSpans; + if (spans) { for (var i = 0; i < spans.length; i++) { + var span = spans[i]; + if (!(span.to != null && lineNo == from.line && from.ch >= span.to || + span.from == null && lineNo != from.line || + span.from != null && lineNo == to.line && span.from >= to.ch) && + (!filter || filter(span.marker))) + { found.push(span.marker.parent || span.marker); } + } } + ++lineNo; + }); + return found + }, + getAllMarks: function() { + var markers = []; + this.iter(function (line) { + var sps = line.markedSpans; + if (sps) { for (var i = 0; i < sps.length; ++i) + { if (sps[i].from != null) { markers.push(sps[i].marker); } } } + }); + return markers + }, + + posFromIndex: function(off) { + var ch, lineNo = this.first, sepSize = this.lineSeparator().length; + this.iter(function (line) { + var sz = line.text.length + sepSize; + if (sz > off) { ch = off; return true } + off -= sz; + ++lineNo; + }); + return clipPos(this, Pos(lineNo, ch)) + }, + indexFromPos: function (coords) { + coords = clipPos(this, coords); + var index = coords.ch; + if (coords.line < this.first || coords.ch < 0) { return 0 } + var sepSize = this.lineSeparator().length; + this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value + index += line.text.length + sepSize; + }); + return index + }, + + copy: function(copyHistory) { + var doc = new Doc(getLines(this, this.first, this.first + this.size), + this.modeOption, this.first, this.lineSep, this.direction); + doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft; + doc.sel = this.sel; + doc.extend = false; + if (copyHistory) { + doc.history.undoDepth = this.history.undoDepth; + doc.setHistory(this.getHistory()); + } + return doc + }, + + linkedDoc: function(options) { + if (!options) { options = {}; } + var from = this.first, to = this.first + this.size; + if (options.from != null && options.from > from) { from = options.from; } + if (options.to != null && options.to < to) { to = options.to; } + var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction); + if (options.sharedHist) { copy.history = this.history + ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist}); + copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]; + copySharedMarkers(copy, findSharedMarkers(this)); + return copy + }, + unlinkDoc: function(other) { + if (other instanceof CodeMirror) { other = other.doc; } + if (this.linked) { for (var i = 0; i < this.linked.length; ++i) { + var link = this.linked[i]; + if (link.doc != other) { continue } + this.linked.splice(i, 1); + other.unlinkDoc(this); + detachSharedMarkers(findSharedMarkers(this)); + break + } } + // If the histories were shared, split them again + if (other.history == this.history) { + var splitIds = [other.id]; + linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true); + other.history = new History(null); + other.history.done = copyHistoryArray(this.history.done, splitIds); + other.history.undone = copyHistoryArray(this.history.undone, splitIds); + } + }, + iterLinkedDocs: function(f) {linkedDocs(this, f);}, + + getMode: function() {return this.mode}, + getEditor: function() {return this.cm}, + + splitLines: function(str) { + if (this.lineSep) { return str.split(this.lineSep) } + return splitLinesAuto(str) + }, + lineSeparator: function() { return this.lineSep || "\n" }, + + setDirection: docMethodOp(function (dir) { + if (dir != "rtl") { dir = "ltr"; } + if (dir == this.direction) { return } + this.direction = dir; + this.iter(function (line) { return line.order = null; }); + if (this.cm) { directionChanged(this.cm); } + }) + }); + + // Public alias. + Doc.prototype.eachLine = Doc.prototype.iter; + + // Kludge to work around strange IE behavior where it'll sometimes + // re-fire a series of drag-related events right after the drop (#1551) + var lastDrop = 0; + + function onDrop(e) { + var cm = this; + clearDragCursor(cm); + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) + { return } + e_preventDefault(e); + if (ie) { lastDrop = +new Date; } + var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files; + if (!pos || cm.isReadOnly()) { return } + // Might be a file drop, in which case we simply extract the text + // and insert it. + if (files && files.length && window.FileReader && window.File) { + var n = files.length, text = Array(n), read = 0; + var markAsReadAndPasteIfAllFilesAreRead = function () { + if (++read == n) { + operation(cm, function () { + pos = clipPos(cm.doc, pos); + var change = {from: pos, to: pos, + text: cm.doc.splitLines( + text.filter(function (t) { return t != null; }).join(cm.doc.lineSeparator())), + origin: "paste"}; + makeChange(cm.doc, change); + setSelectionReplaceHistory(cm.doc, simpleSelection(clipPos(cm.doc, pos), clipPos(cm.doc, changeEnd(change)))); + })(); + } + }; + var readTextFromFile = function (file, i) { + if (cm.options.allowDropFileTypes && + indexOf(cm.options.allowDropFileTypes, file.type) == -1) { + markAsReadAndPasteIfAllFilesAreRead(); + return + } + var reader = new FileReader; + reader.onerror = function () { return markAsReadAndPasteIfAllFilesAreRead(); }; + reader.onload = function () { + var content = reader.result; + if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { + markAsReadAndPasteIfAllFilesAreRead(); + return + } + text[i] = content; + markAsReadAndPasteIfAllFilesAreRead(); + }; + reader.readAsText(file); + }; + for (var i = 0; i < files.length; i++) { readTextFromFile(files[i], i); } + } else { // Normal drop + // Don't do a replace if the drop happened inside of the selected text. + if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) { + cm.state.draggingText(e); + // Ensure the editor is re-focused + setTimeout(function () { return cm.display.input.focus(); }, 20); + return + } + try { + var text$1 = e.dataTransfer.getData("Text"); + if (text$1) { + var selected; + if (cm.state.draggingText && !cm.state.draggingText.copy) + { selected = cm.listSelections(); } + setSelectionNoUndo(cm.doc, simpleSelection(pos, pos)); + if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1) + { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } } + cm.replaceSelection(text$1, "around", "paste"); + cm.display.input.focus(); + } + } + catch(e$1){} + } + } + + function onDragStart(cm, e) { + if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return } + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return } + + e.dataTransfer.setData("Text", cm.getSelection()); + e.dataTransfer.effectAllowed = "copyMove"; + + // Use dummy image instead of default browsers image. + // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there. + if (e.dataTransfer.setDragImage && !safari) { + var img = elt("img", null, null, "position: fixed; left: 0; top: 0;"); + img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; + if (presto) { + img.width = img.height = 1; + cm.display.wrapper.appendChild(img); + // Force a relayout, or Opera won't use our image for some obscure reason + img._top = img.offsetTop; + } + e.dataTransfer.setDragImage(img, 0, 0); + if (presto) { img.parentNode.removeChild(img); } + } + } + + function onDragOver(cm, e) { + var pos = posFromMouse(cm, e); + if (!pos) { return } + var frag = document.createDocumentFragment(); + drawSelectionCursor(cm, pos, frag); + if (!cm.display.dragCursor) { + cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors"); + cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv); + } + removeChildrenAndAdd(cm.display.dragCursor, frag); + } + + function clearDragCursor(cm) { + if (cm.display.dragCursor) { + cm.display.lineSpace.removeChild(cm.display.dragCursor); + cm.display.dragCursor = null; + } + } + + // These must be handled carefully, because naively registering a + // handler for each editor will cause the editors to never be + // garbage collected. + + function forEachCodeMirror(f) { + if (!document.getElementsByClassName) { return } + var byClass = document.getElementsByClassName("CodeMirror"), editors = []; + for (var i = 0; i < byClass.length; i++) { + var cm = byClass[i].CodeMirror; + if (cm) { editors.push(cm); } + } + if (editors.length) { editors[0].operation(function () { + for (var i = 0; i < editors.length; i++) { f(editors[i]); } + }); } + } + + var globalsRegistered = false; + function ensureGlobalHandlers() { + if (globalsRegistered) { return } + registerGlobalHandlers(); + globalsRegistered = true; + } + function registerGlobalHandlers() { + // When the window resizes, we need to refresh active editors. + var resizeTimer; + on(window, "resize", function () { + if (resizeTimer == null) { resizeTimer = setTimeout(function () { + resizeTimer = null; + forEachCodeMirror(onResize); + }, 100); } + }); + // When the window loses focus, we want to show the editor as blurred + on(window, "blur", function () { return forEachCodeMirror(onBlur); }); + } + // Called when the window resizes + function onResize(cm) { + var d = cm.display; + // Might be a text scaling operation, clear size caches. + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; + d.scrollbarsClipped = false; + cm.setSize(); + } + + var keyNames = { + 3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", + 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", + 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", + 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", + 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 145: "ScrollLock", + 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", + 221: "]", 222: "'", 224: "Mod", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", + 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert" + }; + + // Number keys + for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); } + // Alphabetic keys + for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); } + // Function keys + for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; } + + var keyMap = {}; + + keyMap.basic = { + "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", + "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", + "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore", + "Tab": "defaultTab", "Shift-Tab": "indentAuto", + "Enter": "newlineAndIndent", "Insert": "toggleOverwrite", + "Esc": "singleSelection" + }; + // Note that the save and find-related commands aren't defined by + // default. User code or addons can define them. Unknown commands + // are simply ignored. + keyMap.pcDefault = { + "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", + "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown", + "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", + "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find", + "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", + "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", + "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection", + "fallthrough": "basic" + }; + // Very basic readline/emacs-style bindings, which are standard on Mac. + keyMap.emacsy = { + "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", + "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", + "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", + "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars", + "Ctrl-O": "openLine" + }; + keyMap.macDefault = { + "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", + "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft", + "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore", + "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find", + "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", + "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight", + "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd", + "fallthrough": ["basic", "emacsy"] + }; + keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault; + + // KEYMAP DISPATCH + + function normalizeKeyName(name) { + var parts = name.split(/-(?!$)/); + name = parts[parts.length - 1]; + var alt, ctrl, shift, cmd; + for (var i = 0; i < parts.length - 1; i++) { + var mod = parts[i]; + if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; } + else if (/^a(lt)?$/i.test(mod)) { alt = true; } + else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; } + else if (/^s(hift)?$/i.test(mod)) { shift = true; } + else { throw new Error("Unrecognized modifier name: " + mod) } + } + if (alt) { name = "Alt-" + name; } + if (ctrl) { name = "Ctrl-" + name; } + if (cmd) { name = "Cmd-" + name; } + if (shift) { name = "Shift-" + name; } + return name + } + + // This is a kludge to keep keymaps mostly working as raw objects + // (backwards compatibility) while at the same time support features + // like normalization and multi-stroke key bindings. It compiles a + // new normalized keymap, and then updates the old object to reflect + // this. + function normalizeKeyMap(keymap) { + var copy = {}; + for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) { + var value = keymap[keyname]; + if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue } + if (value == "...") { delete keymap[keyname]; continue } + + var keys = map(keyname.split(" "), normalizeKeyName); + for (var i = 0; i < keys.length; i++) { + var val = (void 0), name = (void 0); + if (i == keys.length - 1) { + name = keys.join(" "); + val = value; + } else { + name = keys.slice(0, i + 1).join(" "); + val = "..."; + } + var prev = copy[name]; + if (!prev) { copy[name] = val; } + else if (prev != val) { throw new Error("Inconsistent bindings for " + name) } + } + delete keymap[keyname]; + } } + for (var prop in copy) { keymap[prop] = copy[prop]; } + return keymap + } + + function lookupKey(key, map, handle, context) { + map = getKeyMap(map); + var found = map.call ? map.call(key, context) : map[key]; + if (found === false) { return "nothing" } + if (found === "...") { return "multi" } + if (found != null && handle(found)) { return "handled" } + + if (map.fallthrough) { + if (Object.prototype.toString.call(map.fallthrough) != "[object Array]") + { return lookupKey(key, map.fallthrough, handle, context) } + for (var i = 0; i < map.fallthrough.length; i++) { + var result = lookupKey(key, map.fallthrough[i], handle, context); + if (result) { return result } + } + } + } + + // Modifier key presses don't count as 'real' key presses for the + // purpose of keymap fallthrough. + function isModifierKey(value) { + var name = typeof value == "string" ? value : keyNames[value.keyCode]; + return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod" + } + + function addModifierNames(name, event, noShift) { + var base = name; + if (event.altKey && base != "Alt") { name = "Alt-" + name; } + if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; } + if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Mod") { name = "Cmd-" + name; } + if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; } + return name + } + + // Look up the name of a key as indicated by an event object. + function keyName(event, noShift) { + if (presto && event.keyCode == 34 && event["char"]) { return false } + var name = keyNames[event.keyCode]; + if (name == null || event.altGraphKey) { return false } + // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause, + // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+) + if (event.keyCode == 3 && event.code) { name = event.code; } + return addModifierNames(name, event, noShift) + } + + function getKeyMap(val) { + return typeof val == "string" ? keyMap[val] : val + } + + // Helper for deleting text near the selection(s), used to implement + // backspace, delete, and similar functionality. + function deleteNearSelection(cm, compute) { + var ranges = cm.doc.sel.ranges, kill = []; + // Build up a set of ranges to kill first, merging overlapping + // ranges. + for (var i = 0; i < ranges.length; i++) { + var toKill = compute(ranges[i]); + while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) { + var replaced = kill.pop(); + if (cmp(replaced.from, toKill.from) < 0) { + toKill.from = replaced.from; + break + } + } + kill.push(toKill); + } + // Next, remove those actual ranges. + runInOp(cm, function () { + for (var i = kill.length - 1; i >= 0; i--) + { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); } + ensureCursorVisible(cm); + }); + } + + function moveCharLogically(line, ch, dir) { + var target = skipExtendingChars(line.text, ch + dir, dir); + return target < 0 || target > line.text.length ? null : target + } + + function moveLogically(line, start, dir) { + var ch = moveCharLogically(line, start.ch, dir); + return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before") + } + + function endOfLine(visually, cm, lineObj, lineNo, dir) { + if (visually) { + if (cm.doc.direction == "rtl") { dir = -dir; } + var order = getOrder(lineObj, cm.doc.direction); + if (order) { + var part = dir < 0 ? lst(order) : order[0]; + var moveInStorageOrder = (dir < 0) == (part.level == 1); + var sticky = moveInStorageOrder ? "after" : "before"; + var ch; + // With a wrapped rtl chunk (possibly spanning multiple bidi parts), + // it could be that the last bidi part is not on the last visual line, + // since visual lines contain content order-consecutive chunks. + // Thus, in rtl, we are looking for the first (content-order) character + // in the rtl chunk that is on the last line (that is, the same line + // as the last (content-order) character). + if (part.level > 0 || cm.doc.direction == "rtl") { + var prep = prepareMeasureForLine(cm, lineObj); + ch = dir < 0 ? lineObj.text.length - 1 : 0; + var targetTop = measureCharPrepared(cm, prep, ch).top; + ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch); + if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); } + } else { ch = dir < 0 ? part.to : part.from; } + return new Pos(lineNo, ch, sticky) + } + } + return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after") + } + + function moveVisually(cm, line, start, dir) { + var bidi = getOrder(line, cm.doc.direction); + if (!bidi) { return moveLogically(line, start, dir) } + if (start.ch >= line.text.length) { + start.ch = line.text.length; + start.sticky = "before"; + } else if (start.ch <= 0) { + start.ch = 0; + start.sticky = "after"; + } + var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]; + if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) { + // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines, + // nothing interesting happens. + return moveLogically(line, start, dir) + } + + var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }; + var prep; + var getWrappedLineExtent = function (ch) { + if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} } + prep = prep || prepareMeasureForLine(cm, line); + return wrappedLineExtentChar(cm, line, prep, ch) + }; + var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch); + + if (cm.doc.direction == "rtl" || part.level == 1) { + var moveInStorageOrder = (part.level == 1) == (dir < 0); + var ch = mv(start, moveInStorageOrder ? 1 : -1); + if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) { + // Case 2: We move within an rtl part or in an rtl editor on the same visual line + var sticky = moveInStorageOrder ? "before" : "after"; + return new Pos(start.line, ch, sticky) + } + } + + // Case 3: Could not move within this bidi part in this visual line, so leave + // the current bidi part + + var searchInVisualLine = function (partPos, dir, wrappedLineExtent) { + var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder + ? new Pos(start.line, mv(ch, 1), "before") + : new Pos(start.line, ch, "after"); }; + + for (; partPos >= 0 && partPos < bidi.length; partPos += dir) { + var part = bidi[partPos]; + var moveInStorageOrder = (dir > 0) == (part.level != 1); + var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1); + if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) } + ch = moveInStorageOrder ? part.from : mv(part.to, -1); + if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) } + } + }; + + // Case 3a: Look for other bidi parts on the same visual line + var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent); + if (res) { return res } + + // Case 3b: Look for other bidi parts on the next visual line + var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1); + if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) { + res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh)); + if (res) { return res } + } + + // Case 4: Nowhere to move + return null + } + + // Commands are parameter-less actions that can be performed on an + // editor, mostly used for keybindings. + var commands = { + selectAll: selectAll, + singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); }, + killLine: function (cm) { return deleteNearSelection(cm, function (range) { + if (range.empty()) { + var len = getLine(cm.doc, range.head.line).text.length; + if (range.head.ch == len && range.head.line < cm.lastLine()) + { return {from: range.head, to: Pos(range.head.line + 1, 0)} } + else + { return {from: range.head, to: Pos(range.head.line, len)} } + } else { + return {from: range.from(), to: range.to()} + } + }); }, + deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({ + from: Pos(range.from().line, 0), + to: clipPos(cm.doc, Pos(range.to().line + 1, 0)) + }); }); }, + delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({ + from: Pos(range.from().line, 0), to: range.from() + }); }); }, + delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { + var top = cm.charCoords(range.head, "div").top + 5; + var leftPos = cm.coordsChar({left: 0, top: top}, "div"); + return {from: leftPos, to: range.from()} + }); }, + delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) { + var top = cm.charCoords(range.head, "div").top + 5; + var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"); + return {from: range.from(), to: rightPos } + }); }, + undo: function (cm) { return cm.undo(); }, + redo: function (cm) { return cm.redo(); }, + undoSelection: function (cm) { return cm.undoSelection(); }, + redoSelection: function (cm) { return cm.redoSelection(); }, + goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); }, + goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); }, + goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); }, + {origin: "+move", bias: 1} + ); }, + goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); }, + {origin: "+move", bias: 1} + ); }, + goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); }, + {origin: "+move", bias: -1} + ); }, + goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.cursorCoords(range.head, "div").top + 5; + return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") + }, sel_move); }, + goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.cursorCoords(range.head, "div").top + 5; + return cm.coordsChar({left: 0, top: top}, "div") + }, sel_move); }, + goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.cursorCoords(range.head, "div").top + 5; + var pos = cm.coordsChar({left: 0, top: top}, "div"); + if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) } + return pos + }, sel_move); }, + goLineUp: function (cm) { return cm.moveV(-1, "line"); }, + goLineDown: function (cm) { return cm.moveV(1, "line"); }, + goPageUp: function (cm) { return cm.moveV(-1, "page"); }, + goPageDown: function (cm) { return cm.moveV(1, "page"); }, + goCharLeft: function (cm) { return cm.moveH(-1, "char"); }, + goCharRight: function (cm) { return cm.moveH(1, "char"); }, + goColumnLeft: function (cm) { return cm.moveH(-1, "column"); }, + goColumnRight: function (cm) { return cm.moveH(1, "column"); }, + goWordLeft: function (cm) { return cm.moveH(-1, "word"); }, + goGroupRight: function (cm) { return cm.moveH(1, "group"); }, + goGroupLeft: function (cm) { return cm.moveH(-1, "group"); }, + goWordRight: function (cm) { return cm.moveH(1, "word"); }, + delCharBefore: function (cm) { return cm.deleteH(-1, "codepoint"); }, + delCharAfter: function (cm) { return cm.deleteH(1, "char"); }, + delWordBefore: function (cm) { return cm.deleteH(-1, "word"); }, + delWordAfter: function (cm) { return cm.deleteH(1, "word"); }, + delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); }, + delGroupAfter: function (cm) { return cm.deleteH(1, "group"); }, + indentAuto: function (cm) { return cm.indentSelection("smart"); }, + indentMore: function (cm) { return cm.indentSelection("add"); }, + indentLess: function (cm) { return cm.indentSelection("subtract"); }, + insertTab: function (cm) { return cm.replaceSelection("\t"); }, + insertSoftTab: function (cm) { + var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize; + for (var i = 0; i < ranges.length; i++) { + var pos = ranges[i].from(); + var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize); + spaces.push(spaceStr(tabSize - col % tabSize)); + } + cm.replaceSelections(spaces); + }, + defaultTab: function (cm) { + if (cm.somethingSelected()) { cm.indentSelection("add"); } + else { cm.execCommand("insertTab"); } + }, + // Swap the two chars left and right of each selection's head. + // Move cursor behind the two swapped characters afterwards. + // + // Doesn't consider line feeds a character. + // Doesn't scan more than one line above to find a character. + // Doesn't do anything on an empty line. + // Doesn't do anything with non-empty selections. + transposeChars: function (cm) { return runInOp(cm, function () { + var ranges = cm.listSelections(), newSel = []; + for (var i = 0; i < ranges.length; i++) { + if (!ranges[i].empty()) { continue } + var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text; + if (line) { + if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); } + if (cur.ch > 0) { + cur = new Pos(cur.line, cur.ch + 1); + cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), + Pos(cur.line, cur.ch - 2), cur, "+transpose"); + } else if (cur.line > cm.doc.first) { + var prev = getLine(cm.doc, cur.line - 1).text; + if (prev) { + cur = new Pos(cur.line, 1); + cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() + + prev.charAt(prev.length - 1), + Pos(cur.line - 1, prev.length - 1), cur, "+transpose"); + } + } + } + newSel.push(new Range(cur, cur)); + } + cm.setSelections(newSel); + }); }, + newlineAndIndent: function (cm) { return runInOp(cm, function () { + var sels = cm.listSelections(); + for (var i = sels.length - 1; i >= 0; i--) + { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); } + sels = cm.listSelections(); + for (var i$1 = 0; i$1 < sels.length; i$1++) + { cm.indentLine(sels[i$1].from().line, null, true); } + ensureCursorVisible(cm); + }); }, + openLine: function (cm) { return cm.replaceSelection("\n", "start"); }, + toggleOverwrite: function (cm) { return cm.toggleOverwrite(); } + }; + + + function lineStart(cm, lineN) { + var line = getLine(cm.doc, lineN); + var visual = visualLine(line); + if (visual != line) { lineN = lineNo(visual); } + return endOfLine(true, cm, visual, lineN, 1) + } + function lineEnd(cm, lineN) { + var line = getLine(cm.doc, lineN); + var visual = visualLineEnd(line); + if (visual != line) { lineN = lineNo(visual); } + return endOfLine(true, cm, line, lineN, -1) + } + function lineStartSmart(cm, pos) { + var start = lineStart(cm, pos.line); + var line = getLine(cm.doc, start.line); + var order = getOrder(line, cm.doc.direction); + if (!order || order[0].level == 0) { + var firstNonWS = Math.max(start.ch, line.text.search(/\S/)); + var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch; + return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky) + } + return start + } + + // Run a handler that was bound to a key. + function doHandleBinding(cm, bound, dropShift) { + if (typeof bound == "string") { + bound = commands[bound]; + if (!bound) { return false } + } + // Ensure previous input has been read, so that the handler sees a + // consistent view of the document + cm.display.input.ensurePolled(); + var prevShift = cm.display.shift, done = false; + try { + if (cm.isReadOnly()) { cm.state.suppressEdits = true; } + if (dropShift) { cm.display.shift = false; } + done = bound(cm) != Pass; + } finally { + cm.display.shift = prevShift; + cm.state.suppressEdits = false; + } + return done + } + + function lookupKeyForEditor(cm, name, handle) { + for (var i = 0; i < cm.state.keyMaps.length; i++) { + var result = lookupKey(name, cm.state.keyMaps[i], handle, cm); + if (result) { return result } + } + return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm)) + || lookupKey(name, cm.options.keyMap, handle, cm) + } + + // Note that, despite the name, this function is also used to check + // for bound mouse clicks. + + var stopSeq = new Delayed; + + function dispatchKey(cm, name, e, handle) { + var seq = cm.state.keySeq; + if (seq) { + if (isModifierKey(name)) { return "handled" } + if (/\'$/.test(name)) + { cm.state.keySeq = null; } + else + { stopSeq.set(50, function () { + if (cm.state.keySeq == seq) { + cm.state.keySeq = null; + cm.display.input.reset(); + } + }); } + if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true } + } + return dispatchKeyInner(cm, name, e, handle) + } + + function dispatchKeyInner(cm, name, e, handle) { + var result = lookupKeyForEditor(cm, name, handle); + + if (result == "multi") + { cm.state.keySeq = name; } + if (result == "handled") + { signalLater(cm, "keyHandled", cm, name, e); } + + if (result == "handled" || result == "multi") { + e_preventDefault(e); + restartBlink(cm); + } + + return !!result + } + + // Handle a key from the keydown event. + function handleKeyBinding(cm, e) { + var name = keyName(e, true); + if (!name) { return false } + + if (e.shiftKey && !cm.state.keySeq) { + // First try to resolve full name (including 'Shift-'). Failing + // that, see if there is a cursor-motion command (starting with + // 'go') bound to the keyname without 'Shift-'. + return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); }) + || dispatchKey(cm, name, e, function (b) { + if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion) + { return doHandleBinding(cm, b) } + }) + } else { + return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); }) + } + } + + // Handle a key from the keypress event + function handleCharBinding(cm, e, ch) { + return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); }) + } + + var lastStoppedKey = null; + function onKeyDown(e) { + var cm = this; + if (e.target && e.target != cm.display.input.getField()) { return } + cm.curOp.focus = activeElt(); + if (signalDOMEvent(cm, e)) { return } + // IE does strange things with escape. + if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; } + var code = e.keyCode; + cm.display.shift = code == 16 || e.shiftKey; + var handled = handleKeyBinding(cm, e); + if (presto) { + lastStoppedKey = handled ? code : null; + // Opera has no cut event... we try to at least catch the key combo + if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey)) + { cm.replaceSelection("", null, "cut"); } + } + if (gecko && !mac && !handled && code == 46 && e.shiftKey && !e.ctrlKey && document.execCommand) + { document.execCommand("cut"); } + + // Turn mouse into crosshair when Alt is held on Mac. + if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className)) + { showCrossHair(cm); } + } + + function showCrossHair(cm) { + var lineDiv = cm.display.lineDiv; + addClass(lineDiv, "CodeMirror-crosshair"); + + function up(e) { + if (e.keyCode == 18 || !e.altKey) { + rmClass(lineDiv, "CodeMirror-crosshair"); + off(document, "keyup", up); + off(document, "mouseover", up); + } + } + on(document, "keyup", up); + on(document, "mouseover", up); + } + + function onKeyUp(e) { + if (e.keyCode == 16) { this.doc.sel.shift = false; } + signalDOMEvent(this, e); + } + + function onKeyPress(e) { + var cm = this; + if (e.target && e.target != cm.display.input.getField()) { return } + if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return } + var keyCode = e.keyCode, charCode = e.charCode; + if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return} + if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return } + var ch = String.fromCharCode(charCode == null ? keyCode : charCode); + // Some browsers fire keypress events for backspace + if (ch == "\x08") { return } + if (handleCharBinding(cm, e, ch)) { return } + cm.display.input.onKeyPress(e); + } + + var DOUBLECLICK_DELAY = 400; + + var PastClick = function(time, pos, button) { + this.time = time; + this.pos = pos; + this.button = button; + }; + + PastClick.prototype.compare = function (time, pos, button) { + return this.time + DOUBLECLICK_DELAY > time && + cmp(pos, this.pos) == 0 && button == this.button + }; + + var lastClick, lastDoubleClick; + function clickRepeat(pos, button) { + var now = +new Date; + if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) { + lastClick = lastDoubleClick = null; + return "triple" + } else if (lastClick && lastClick.compare(now, pos, button)) { + lastDoubleClick = new PastClick(now, pos, button); + lastClick = null; + return "double" + } else { + lastClick = new PastClick(now, pos, button); + lastDoubleClick = null; + return "single" + } + } + + // A mouse down can be a single click, double click, triple click, + // start of selection drag, start of text drag, new cursor + // (ctrl-click), rectangle drag (alt-drag), or xwin + // middle-click-paste. Or it might be a click on something we should + // not interfere with, such as a scrollbar or widget. + function onMouseDown(e) { + var cm = this, display = cm.display; + if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return } + display.input.ensurePolled(); + display.shift = e.shiftKey; + + if (eventInWidget(display, e)) { + if (!webkit) { + // Briefly turn off draggability, to allow widgets to do + // normal dragging things. + display.scroller.draggable = false; + setTimeout(function () { return display.scroller.draggable = true; }, 100); + } + return + } + if (clickInGutter(cm, e)) { return } + var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single"; + window.focus(); + + // #3261: make sure, that we're not starting a second selection + if (button == 1 && cm.state.selectingText) + { cm.state.selectingText(e); } + + if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return } + + if (button == 1) { + if (pos) { leftButtonDown(cm, pos, repeat, e); } + else if (e_target(e) == display.scroller) { e_preventDefault(e); } + } else if (button == 2) { + if (pos) { extendSelection(cm.doc, pos); } + setTimeout(function () { return display.input.focus(); }, 20); + } else if (button == 3) { + if (captureRightClick) { cm.display.input.onContextMenu(e); } + else { delayBlurEvent(cm); } + } + } + + function handleMappedButton(cm, button, pos, repeat, event) { + var name = "Click"; + if (repeat == "double") { name = "Double" + name; } + else if (repeat == "triple") { name = "Triple" + name; } + name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name; + + return dispatchKey(cm, addModifierNames(name, event), event, function (bound) { + if (typeof bound == "string") { bound = commands[bound]; } + if (!bound) { return false } + var done = false; + try { + if (cm.isReadOnly()) { cm.state.suppressEdits = true; } + done = bound(cm, pos) != Pass; + } finally { + cm.state.suppressEdits = false; + } + return done + }) + } + + function configureMouse(cm, repeat, event) { + var option = cm.getOption("configureMouse"); + var value = option ? option(cm, repeat, event) : {}; + if (value.unit == null) { + var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey; + value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line"; + } + if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; } + if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; } + if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); } + return value + } + + function leftButtonDown(cm, pos, repeat, event) { + if (ie) { setTimeout(bind(ensureFocus, cm), 0); } + else { cm.curOp.focus = activeElt(); } + + var behavior = configureMouse(cm, repeat, event); + + var sel = cm.doc.sel, contained; + if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() && + repeat == "single" && (contained = sel.contains(pos)) > -1 && + (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) && + (cmp(contained.to(), pos) > 0 || pos.xRel < 0)) + { leftButtonStartDrag(cm, event, pos, behavior); } + else + { leftButtonSelect(cm, event, pos, behavior); } + } + + // Start a text drag. When it ends, see if any dragging actually + // happen, and treat as a click if it didn't. + function leftButtonStartDrag(cm, event, pos, behavior) { + var display = cm.display, moved = false; + var dragEnd = operation(cm, function (e) { + if (webkit) { display.scroller.draggable = false; } + cm.state.draggingText = false; + if (cm.state.delayingBlurEvent) { + if (cm.hasFocus()) { cm.state.delayingBlurEvent = false; } + else { delayBlurEvent(cm); } + } + off(display.wrapper.ownerDocument, "mouseup", dragEnd); + off(display.wrapper.ownerDocument, "mousemove", mouseMove); + off(display.scroller, "dragstart", dragStart); + off(display.scroller, "drop", dragEnd); + if (!moved) { + e_preventDefault(e); + if (!behavior.addNew) + { extendSelection(cm.doc, pos, null, null, behavior.extend); } + // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081) + if ((webkit && !safari) || ie && ie_version == 9) + { setTimeout(function () {display.wrapper.ownerDocument.body.focus({preventScroll: true}); display.input.focus();}, 20); } + else + { display.input.focus(); } + } + }); + var mouseMove = function(e2) { + moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10; + }; + var dragStart = function () { return moved = true; }; + // Let the drag handler handle this. + if (webkit) { display.scroller.draggable = true; } + cm.state.draggingText = dragEnd; + dragEnd.copy = !behavior.moveOnDrag; + on(display.wrapper.ownerDocument, "mouseup", dragEnd); + on(display.wrapper.ownerDocument, "mousemove", mouseMove); + on(display.scroller, "dragstart", dragStart); + on(display.scroller, "drop", dragEnd); + + cm.state.delayingBlurEvent = true; + setTimeout(function () { return display.input.focus(); }, 20); + // IE's approach to draggable + if (display.scroller.dragDrop) { display.scroller.dragDrop(); } + } + + function rangeForUnit(cm, pos, unit) { + if (unit == "char") { return new Range(pos, pos) } + if (unit == "word") { return cm.findWordAt(pos) } + if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) } + var result = unit(cm, pos); + return new Range(result.from, result.to) + } + + // Normal selection, as opposed to text dragging. + function leftButtonSelect(cm, event, start, behavior) { + if (ie) { delayBlurEvent(cm); } + var display = cm.display, doc = cm.doc; + e_preventDefault(event); + + var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges; + if (behavior.addNew && !behavior.extend) { + ourIndex = doc.sel.contains(start); + if (ourIndex > -1) + { ourRange = ranges[ourIndex]; } + else + { ourRange = new Range(start, start); } + } else { + ourRange = doc.sel.primary(); + ourIndex = doc.sel.primIndex; + } + + if (behavior.unit == "rectangle") { + if (!behavior.addNew) { ourRange = new Range(start, start); } + start = posFromMouse(cm, event, true, true); + ourIndex = -1; + } else { + var range = rangeForUnit(cm, start, behavior.unit); + if (behavior.extend) + { ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend); } + else + { ourRange = range; } + } + + if (!behavior.addNew) { + ourIndex = 0; + setSelection(doc, new Selection([ourRange], 0), sel_mouse); + startSel = doc.sel; + } else if (ourIndex == -1) { + ourIndex = ranges.length; + setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex), + {scroll: false, origin: "*mouse"}); + } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) { + setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0), + {scroll: false, origin: "*mouse"}); + startSel = doc.sel; + } else { + replaceOneSelection(doc, ourIndex, ourRange, sel_mouse); + } + + var lastPos = start; + function extendTo(pos) { + if (cmp(lastPos, pos) == 0) { return } + lastPos = pos; + + if (behavior.unit == "rectangle") { + var ranges = [], tabSize = cm.options.tabSize; + var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize); + var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize); + var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol); + for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line)); + line <= end; line++) { + var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize); + if (left == right) + { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); } + else if (text.length > leftPos) + { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); } + } + if (!ranges.length) { ranges.push(new Range(start, start)); } + setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex), + {origin: "*mouse", scroll: false}); + cm.scrollIntoView(pos); + } else { + var oldRange = ourRange; + var range = rangeForUnit(cm, pos, behavior.unit); + var anchor = oldRange.anchor, head; + if (cmp(range.anchor, anchor) > 0) { + head = range.head; + anchor = minPos(oldRange.from(), range.anchor); + } else { + head = range.anchor; + anchor = maxPos(oldRange.to(), range.head); + } + var ranges$1 = startSel.ranges.slice(0); + ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head)); + setSelection(doc, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse); + } + } + + var editorSize = display.wrapper.getBoundingClientRect(); + // Used to ensure timeout re-tries don't fire when another extend + // happened in the meantime (clearTimeout isn't reliable -- at + // least on Chrome, the timeouts still happen even when cleared, + // if the clear happens after their scheduled firing time). + var counter = 0; + + function extend(e) { + var curCount = ++counter; + var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle"); + if (!cur) { return } + if (cmp(cur, lastPos) != 0) { + cm.curOp.focus = activeElt(); + extendTo(cur); + var visible = visibleLines(display, doc); + if (cur.line >= visible.to || cur.line < visible.from) + { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); } + } else { + var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0; + if (outside) { setTimeout(operation(cm, function () { + if (counter != curCount) { return } + display.scroller.scrollTop += outside; + extend(e); + }), 50); } + } + } + + function done(e) { + cm.state.selectingText = false; + counter = Infinity; + // If e is null or undefined we interpret this as someone trying + // to explicitly cancel the selection rather than the user + // letting go of the mouse button. + if (e) { + e_preventDefault(e); + display.input.focus(); + } + off(display.wrapper.ownerDocument, "mousemove", move); + off(display.wrapper.ownerDocument, "mouseup", up); + doc.history.lastSelOrigin = null; + } + + var move = operation(cm, function (e) { + if (e.buttons === 0 || !e_button(e)) { done(e); } + else { extend(e); } + }); + var up = operation(cm, done); + cm.state.selectingText = up; + on(display.wrapper.ownerDocument, "mousemove", move); + on(display.wrapper.ownerDocument, "mouseup", up); + } + + // Used when mouse-selecting to adjust the anchor to the proper side + // of a bidi jump depending on the visual position of the head. + function bidiSimplify(cm, range) { + var anchor = range.anchor; + var head = range.head; + var anchorLine = getLine(cm.doc, anchor.line); + if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range } + var order = getOrder(anchorLine); + if (!order) { return range } + var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index]; + if (part.from != anchor.ch && part.to != anchor.ch) { return range } + var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1); + if (boundary == 0 || boundary == order.length) { return range } + + // Compute the relative visual position of the head compared to the + // anchor (<0 is to the left, >0 to the right) + var leftSide; + if (head.line != anchor.line) { + leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0; + } else { + var headIndex = getBidiPartAt(order, head.ch, head.sticky); + var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1); + if (headIndex == boundary - 1 || headIndex == boundary) + { leftSide = dir < 0; } + else + { leftSide = dir > 0; } + } + + var usePart = order[boundary + (leftSide ? -1 : 0)]; + var from = leftSide == (usePart.level == 1); + var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before"; + return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head) + } + + + // Determines whether an event happened in the gutter, and fires the + // handlers for the corresponding event. + function gutterEvent(cm, e, type, prevent) { + var mX, mY; + if (e.touches) { + mX = e.touches[0].clientX; + mY = e.touches[0].clientY; + } else { + try { mX = e.clientX; mY = e.clientY; } + catch(e$1) { return false } + } + if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false } + if (prevent) { e_preventDefault(e); } + + var display = cm.display; + var lineBox = display.lineDiv.getBoundingClientRect(); + + if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) } + mY -= lineBox.top - display.viewOffset; + + for (var i = 0; i < cm.display.gutterSpecs.length; ++i) { + var g = display.gutters.childNodes[i]; + if (g && g.getBoundingClientRect().right >= mX) { + var line = lineAtHeight(cm.doc, mY); + var gutter = cm.display.gutterSpecs[i]; + signal(cm, type, cm, line, gutter.className, e); + return e_defaultPrevented(e) + } + } + } + + function clickInGutter(cm, e) { + return gutterEvent(cm, e, "gutterClick", true) + } + + // CONTEXT MENU HANDLING + + // To make the context menu work, we need to briefly unhide the + // textarea (making it as unobtrusive as possible) to let the + // right-click take effect on it. + function onContextMenu(cm, e) { + if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return } + if (signalDOMEvent(cm, e, "contextmenu")) { return } + if (!captureRightClick) { cm.display.input.onContextMenu(e); } + } + + function contextMenuInGutter(cm, e) { + if (!hasHandler(cm, "gutterContextMenu")) { return false } + return gutterEvent(cm, e, "gutterContextMenu", false) + } + + function themeChanged(cm) { + cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") + + cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-"); + clearCaches(cm); + } + + var Init = {toString: function(){return "CodeMirror.Init"}}; + + var defaults = {}; + var optionHandlers = {}; + + function defineOptions(CodeMirror) { + var optionHandlers = CodeMirror.optionHandlers; + + function option(name, deflt, handle, notOnInit) { + CodeMirror.defaults[name] = deflt; + if (handle) { optionHandlers[name] = + notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; } + } + + CodeMirror.defineOption = option; + + // Passed to option handlers when there is no old value. + CodeMirror.Init = Init; + + // These two are, on init, called from the constructor because they + // have to be initialized before the editor can start at all. + option("value", "", function (cm, val) { return cm.setValue(val); }, true); + option("mode", null, function (cm, val) { + cm.doc.modeOption = val; + loadMode(cm); + }, true); + + option("indentUnit", 2, loadMode, true); + option("indentWithTabs", false); + option("smartIndent", true); + option("tabSize", 4, function (cm) { + resetModeState(cm); + clearCaches(cm); + regChange(cm); + }, true); + + option("lineSeparator", null, function (cm, val) { + cm.doc.lineSep = val; + if (!val) { return } + var newBreaks = [], lineNo = cm.doc.first; + cm.doc.iter(function (line) { + for (var pos = 0;;) { + var found = line.text.indexOf(val, pos); + if (found == -1) { break } + pos = found + val.length; + newBreaks.push(Pos(lineNo, found)); + } + lineNo++; + }); + for (var i = newBreaks.length - 1; i >= 0; i--) + { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); } + }); + option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200c\u200e\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g, function (cm, val, old) { + cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g"); + if (old != Init) { cm.refresh(); } + }); + option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true); + option("electricChars", true); + option("inputStyle", mobile ? "contenteditable" : "textarea", function () { + throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME + }, true); + option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true); + option("autocorrect", false, function (cm, val) { return cm.getInputField().autocorrect = val; }, true); + option("autocapitalize", false, function (cm, val) { return cm.getInputField().autocapitalize = val; }, true); + option("rtlMoveVisually", !windows); + option("wholeLineUpdateBefore", true); + + option("theme", "default", function (cm) { + themeChanged(cm); + updateGutters(cm); + }, true); + option("keyMap", "default", function (cm, val, old) { + var next = getKeyMap(val); + var prev = old != Init && getKeyMap(old); + if (prev && prev.detach) { prev.detach(cm, next); } + if (next.attach) { next.attach(cm, prev || null); } + }); + option("extraKeys", null); + option("configureMouse", null); + + option("lineWrapping", false, wrappingChanged, true); + option("gutters", [], function (cm, val) { + cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers); + updateGutters(cm); + }, true); + option("fixedGutter", true, function (cm, val) { + cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"; + cm.refresh(); + }, true); + option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true); + option("scrollbarStyle", "native", function (cm) { + initScrollbars(cm); + updateScrollbars(cm); + cm.display.scrollbars.setScrollTop(cm.doc.scrollTop); + cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft); + }, true); + option("lineNumbers", false, function (cm, val) { + cm.display.gutterSpecs = getGutters(cm.options.gutters, val); + updateGutters(cm); + }, true); + option("firstLineNumber", 1, updateGutters, true); + option("lineNumberFormatter", function (integer) { return integer; }, updateGutters, true); + option("showCursorWhenSelecting", false, updateSelection, true); + + option("resetSelectionOnContextMenu", true); + option("lineWiseCopyCut", true); + option("pasteLinesPerSelection", true); + option("selectionsMayTouch", false); + + option("readOnly", false, function (cm, val) { + if (val == "nocursor") { + onBlur(cm); + cm.display.input.blur(); + } + cm.display.input.readOnlyChanged(val); + }); + + option("screenReaderLabel", null, function (cm, val) { + val = (val === '') ? null : val; + cm.display.input.screenReaderLabelChanged(val); + }); + + option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true); + option("dragDrop", true, dragDropChanged); + option("allowDropFileTypes", null); + + option("cursorBlinkRate", 530); + option("cursorScrollMargin", 0); + option("cursorHeight", 1, updateSelection, true); + option("singleCursorHeightPerLine", true, updateSelection, true); + option("workTime", 100); + option("workDelay", 100); + option("flattenSpans", true, resetModeState, true); + option("addModeClass", false, resetModeState, true); + option("pollInterval", 100); + option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; }); + option("historyEventDelay", 1250); + option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true); + option("maxHighlightLength", 10000, resetModeState, true); + option("moveInputWithCursor", true, function (cm, val) { + if (!val) { cm.display.input.resetPosition(); } + }); + + option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; }); + option("autofocus", null); + option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true); + option("phrases", null); + } + + function dragDropChanged(cm, value, old) { + var wasOn = old && old != Init; + if (!value != !wasOn) { + var funcs = cm.display.dragFunctions; + var toggle = value ? on : off; + toggle(cm.display.scroller, "dragstart", funcs.start); + toggle(cm.display.scroller, "dragenter", funcs.enter); + toggle(cm.display.scroller, "dragover", funcs.over); + toggle(cm.display.scroller, "dragleave", funcs.leave); + toggle(cm.display.scroller, "drop", funcs.drop); + } + } + + function wrappingChanged(cm) { + if (cm.options.lineWrapping) { + addClass(cm.display.wrapper, "CodeMirror-wrap"); + cm.display.sizer.style.minWidth = ""; + cm.display.sizerWidth = null; + } else { + rmClass(cm.display.wrapper, "CodeMirror-wrap"); + findMaxLine(cm); + } + estimateLineHeights(cm); + regChange(cm); + clearCaches(cm); + setTimeout(function () { return updateScrollbars(cm); }, 100); + } + + // A CodeMirror instance represents an editor. This is the object + // that user code is usually dealing with. + + function CodeMirror(place, options) { + var this$1 = this; + + if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) } + + this.options = options = options ? copyObj(options) : {}; + // Determine effective options based on given values and defaults. + copyObj(defaults, options, false); + + var doc = options.value; + if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); } + else if (options.mode) { doc.modeOption = options.mode; } + this.doc = doc; + + var input = new CodeMirror.inputStyles[options.inputStyle](this); + var display = this.display = new Display(place, doc, input, options); + display.wrapper.CodeMirror = this; + themeChanged(this); + if (options.lineWrapping) + { this.display.wrapper.className += " CodeMirror-wrap"; } + initScrollbars(this); + + this.state = { + keyMaps: [], // stores maps added by addKeyMap + overlays: [], // highlighting overlays, as added by addOverlay + modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info + overwrite: false, + delayingBlurEvent: false, + focused: false, + suppressEdits: false, // used to disable editing during key handlers when in readOnly mode + pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll + selectingText: false, + draggingText: false, + highlight: new Delayed(), // stores highlight worker timeout + keySeq: null, // Unfinished key sequence + specialChars: null + }; + + if (options.autofocus && !mobile) { display.input.focus(); } + + // Override magic textarea content restore that IE sometimes does + // on our hidden textarea on reload + if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); } + + registerEventHandlers(this); + ensureGlobalHandlers(); + + startOperation(this); + this.curOp.forceUpdate = true; + attachDoc(this, doc); + + if ((options.autofocus && !mobile) || this.hasFocus()) + { setTimeout(function () { + if (this$1.hasFocus() && !this$1.state.focused) { onFocus(this$1); } + }, 20); } + else + { onBlur(this); } + + for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt)) + { optionHandlers[opt](this, options[opt], Init); } } + maybeUpdateLineNumberWidth(this); + if (options.finishInit) { options.finishInit(this); } + for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this); } + endOperation(this); + // Suppress optimizelegibility in Webkit, since it breaks text + // measuring on line wrapping boundaries. + if (webkit && options.lineWrapping && + getComputedStyle(display.lineDiv).textRendering == "optimizelegibility") + { display.lineDiv.style.textRendering = "auto"; } + } + + // The default configuration options. + CodeMirror.defaults = defaults; + // Functions to run when options are changed. + CodeMirror.optionHandlers = optionHandlers; + + // Attach the necessary event handlers when initializing the editor + function registerEventHandlers(cm) { + var d = cm.display; + on(d.scroller, "mousedown", operation(cm, onMouseDown)); + // Older IE's will not fire a second mousedown for a double click + if (ie && ie_version < 11) + { on(d.scroller, "dblclick", operation(cm, function (e) { + if (signalDOMEvent(cm, e)) { return } + var pos = posFromMouse(cm, e); + if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return } + e_preventDefault(e); + var word = cm.findWordAt(pos); + extendSelection(cm.doc, word.anchor, word.head); + })); } + else + { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); } + // Some browsers fire contextmenu *after* opening the menu, at + // which point we can't mess with it anymore. Context menu is + // handled in onMouseDown for these browsers. + on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }); + on(d.input.getField(), "contextmenu", function (e) { + if (!d.scroller.contains(e.target)) { onContextMenu(cm, e); } + }); + + // Used to suppress mouse event handling when a touch happens + var touchFinished, prevTouch = {end: 0}; + function finishTouch() { + if (d.activeTouch) { + touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000); + prevTouch = d.activeTouch; + prevTouch.end = +new Date; + } + } + function isMouseLikeTouchEvent(e) { + if (e.touches.length != 1) { return false } + var touch = e.touches[0]; + return touch.radiusX <= 1 && touch.radiusY <= 1 + } + function farAway(touch, other) { + if (other.left == null) { return true } + var dx = other.left - touch.left, dy = other.top - touch.top; + return dx * dx + dy * dy > 20 * 20 + } + on(d.scroller, "touchstart", function (e) { + if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) { + d.input.ensurePolled(); + clearTimeout(touchFinished); + var now = +new Date; + d.activeTouch = {start: now, moved: false, + prev: now - prevTouch.end <= 300 ? prevTouch : null}; + if (e.touches.length == 1) { + d.activeTouch.left = e.touches[0].pageX; + d.activeTouch.top = e.touches[0].pageY; + } + } + }); + on(d.scroller, "touchmove", function () { + if (d.activeTouch) { d.activeTouch.moved = true; } + }); + on(d.scroller, "touchend", function (e) { + var touch = d.activeTouch; + if (touch && !eventInWidget(d, e) && touch.left != null && + !touch.moved && new Date - touch.start < 300) { + var pos = cm.coordsChar(d.activeTouch, "page"), range; + if (!touch.prev || farAway(touch, touch.prev)) // Single tap + { range = new Range(pos, pos); } + else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap + { range = cm.findWordAt(pos); } + else // Triple tap + { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); } + cm.setSelection(range.anchor, range.head); + cm.focus(); + e_preventDefault(e); + } + finishTouch(); + }); + on(d.scroller, "touchcancel", finishTouch); + + // Sync scrolling between fake scrollbars and real scrollable + // area, ensure viewport is updated when scrolling. + on(d.scroller, "scroll", function () { + if (d.scroller.clientHeight) { + updateScrollTop(cm, d.scroller.scrollTop); + setScrollLeft(cm, d.scroller.scrollLeft, true); + signal(cm, "scroll", cm); + } + }); + + // Listen to wheel events in order to try and update the viewport on time. + on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); }); + on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); }); + + // Prevent wrapper from ever scrolling + on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); + + d.dragFunctions = { + enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }}, + over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }}, + start: function (e) { return onDragStart(cm, e); }, + drop: operation(cm, onDrop), + leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }} + }; + + var inp = d.input.getField(); + on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); }); + on(inp, "keydown", operation(cm, onKeyDown)); + on(inp, "keypress", operation(cm, onKeyPress)); + on(inp, "focus", function (e) { return onFocus(cm, e); }); + on(inp, "blur", function (e) { return onBlur(cm, e); }); + } + + var initHooks = []; + CodeMirror.defineInitHook = function (f) { return initHooks.push(f); }; + + // Indent the given line. The how parameter can be "smart", + // "add"/null, "subtract", or "prev". When aggressive is false + // (typically set to true for forced single-line indents), empty + // lines are not indented, and places where the mode returns Pass + // are left alone. + function indentLine(cm, n, how, aggressive) { + var doc = cm.doc, state; + if (how == null) { how = "add"; } + if (how == "smart") { + // Fall back to "prev" when the mode doesn't have an indentation + // method. + if (!doc.mode.indent) { how = "prev"; } + else { state = getContextBefore(cm, n).state; } + } + + var tabSize = cm.options.tabSize; + var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize); + if (line.stateAfter) { line.stateAfter = null; } + var curSpaceString = line.text.match(/^\s*/)[0], indentation; + if (!aggressive && !/\S/.test(line.text)) { + indentation = 0; + how = "not"; + } else if (how == "smart") { + indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text); + if (indentation == Pass || indentation > 150) { + if (!aggressive) { return } + how = "prev"; + } + } + if (how == "prev") { + if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); } + else { indentation = 0; } + } else if (how == "add") { + indentation = curSpace + cm.options.indentUnit; + } else if (how == "subtract") { + indentation = curSpace - cm.options.indentUnit; + } else if (typeof how == "number") { + indentation = curSpace + how; + } + indentation = Math.max(0, indentation); + + var indentString = "", pos = 0; + if (cm.options.indentWithTabs) + { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} } + if (pos < indentation) { indentString += spaceStr(indentation - pos); } + + if (indentString != curSpaceString) { + replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); + line.stateAfter = null; + return true + } else { + // Ensure that, if the cursor was in the whitespace at the start + // of the line, it is moved to the end of that space. + for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) { + var range = doc.sel.ranges[i$1]; + if (range.head.line == n && range.head.ch < curSpaceString.length) { + var pos$1 = Pos(n, curSpaceString.length); + replaceOneSelection(doc, i$1, new Range(pos$1, pos$1)); + break + } + } + } + } + + // This will be set to a {lineWise: bool, text: [string]} object, so + // that, when pasting, we know what kind of selections the copied + // text was made out of. + var lastCopied = null; + + function setLastCopied(newLastCopied) { + lastCopied = newLastCopied; + } + + function applyTextInput(cm, inserted, deleted, sel, origin) { + var doc = cm.doc; + cm.display.shift = false; + if (!sel) { sel = doc.sel; } + + var recent = +new Date - 200; + var paste = origin == "paste" || cm.state.pasteIncoming > recent; + var textLines = splitLinesAuto(inserted), multiPaste = null; + // When pasting N lines into N selections, insert one line per selection + if (paste && sel.ranges.length > 1) { + if (lastCopied && lastCopied.text.join("\n") == inserted) { + if (sel.ranges.length % lastCopied.text.length == 0) { + multiPaste = []; + for (var i = 0; i < lastCopied.text.length; i++) + { multiPaste.push(doc.splitLines(lastCopied.text[i])); } + } + } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) { + multiPaste = map(textLines, function (l) { return [l]; }); + } + } + + var updateInput = cm.curOp.updateInput; + // Normal behavior is to insert the new text into every selection + for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) { + var range = sel.ranges[i$1]; + var from = range.from(), to = range.to(); + if (range.empty()) { + if (deleted && deleted > 0) // Handle deletion + { from = Pos(from.line, from.ch - deleted); } + else if (cm.state.overwrite && !paste) // Handle overwrite + { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); } + else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == textLines.join("\n")) + { from = to = Pos(from.line, 0); } + } + var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines, + origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")}; + makeChange(cm.doc, changeEvent); + signalLater(cm, "inputRead", cm, changeEvent); + } + if (inserted && !paste) + { triggerElectric(cm, inserted); } + + ensureCursorVisible(cm); + if (cm.curOp.updateInput < 2) { cm.curOp.updateInput = updateInput; } + cm.curOp.typing = true; + cm.state.pasteIncoming = cm.state.cutIncoming = -1; + } + + function handlePaste(e, cm) { + var pasted = e.clipboardData && e.clipboardData.getData("Text"); + if (pasted) { + e.preventDefault(); + if (!cm.isReadOnly() && !cm.options.disableInput) + { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); } + return true + } + } + + function triggerElectric(cm, inserted) { + // When an 'electric' character is inserted, immediately trigger a reindent + if (!cm.options.electricChars || !cm.options.smartIndent) { return } + var sel = cm.doc.sel; + + for (var i = sel.ranges.length - 1; i >= 0; i--) { + var range = sel.ranges[i]; + if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) { continue } + var mode = cm.getModeAt(range.head); + var indented = false; + if (mode.electricChars) { + for (var j = 0; j < mode.electricChars.length; j++) + { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) { + indented = indentLine(cm, range.head.line, "smart"); + break + } } + } else if (mode.electricInput) { + if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch))) + { indented = indentLine(cm, range.head.line, "smart"); } + } + if (indented) { signalLater(cm, "electricInput", cm, range.head.line); } + } + } + + function copyableRanges(cm) { + var text = [], ranges = []; + for (var i = 0; i < cm.doc.sel.ranges.length; i++) { + var line = cm.doc.sel.ranges[i].head.line; + var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}; + ranges.push(lineRange); + text.push(cm.getRange(lineRange.anchor, lineRange.head)); + } + return {text: text, ranges: ranges} + } + + function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) { + field.setAttribute("autocorrect", autocorrect ? "" : "off"); + field.setAttribute("autocapitalize", autocapitalize ? "" : "off"); + field.setAttribute("spellcheck", !!spellcheck); + } + + function hiddenTextarea() { + var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none"); + var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); + // The textarea is kept positioned near the cursor to prevent the + // fact that it'll be scrolled into view on input from scrolling + // our fake cursor out of view. On webkit, when wrap=off, paste is + // very slow. So make the area wide instead. + if (webkit) { te.style.width = "1000px"; } + else { te.setAttribute("wrap", "off"); } + // If border: 0; -- iOS fails to open keyboard (issue #1287) + if (ios) { te.style.border = "1px solid black"; } + disableBrowserMagic(te); + return div + } + + // The publicly visible API. Note that methodOp(f) means + // 'wrap f in an operation, performed on its `this` parameter'. + + // This is not the complete set of editor methods. Most of the + // methods defined on the Doc type are also injected into + // CodeMirror.prototype, for backwards compatibility and + // convenience. + + function addEditorMethods(CodeMirror) { + var optionHandlers = CodeMirror.optionHandlers; + + var helpers = CodeMirror.helpers = {}; + + CodeMirror.prototype = { + constructor: CodeMirror, + focus: function(){window.focus(); this.display.input.focus();}, + + setOption: function(option, value) { + var options = this.options, old = options[option]; + if (options[option] == value && option != "mode") { return } + options[option] = value; + if (optionHandlers.hasOwnProperty(option)) + { operation(this, optionHandlers[option])(this, value, old); } + signal(this, "optionChange", this, option); + }, + + getOption: function(option) {return this.options[option]}, + getDoc: function() {return this.doc}, + + addKeyMap: function(map, bottom) { + this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map)); + }, + removeKeyMap: function(map) { + var maps = this.state.keyMaps; + for (var i = 0; i < maps.length; ++i) + { if (maps[i] == map || maps[i].name == map) { + maps.splice(i, 1); + return true + } } + }, + + addOverlay: methodOp(function(spec, options) { + var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec); + if (mode.startState) { throw new Error("Overlays may not be stateful.") } + insertSorted(this.state.overlays, + {mode: mode, modeSpec: spec, opaque: options && options.opaque, + priority: (options && options.priority) || 0}, + function (overlay) { return overlay.priority; }); + this.state.modeGen++; + regChange(this); + }), + removeOverlay: methodOp(function(spec) { + var overlays = this.state.overlays; + for (var i = 0; i < overlays.length; ++i) { + var cur = overlays[i].modeSpec; + if (cur == spec || typeof spec == "string" && cur.name == spec) { + overlays.splice(i, 1); + this.state.modeGen++; + regChange(this); + return + } + } + }), + + indentLine: methodOp(function(n, dir, aggressive) { + if (typeof dir != "string" && typeof dir != "number") { + if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; } + else { dir = dir ? "add" : "subtract"; } + } + if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); } + }), + indentSelection: methodOp(function(how) { + var ranges = this.doc.sel.ranges, end = -1; + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i]; + if (!range.empty()) { + var from = range.from(), to = range.to(); + var start = Math.max(end, from.line); + end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; + for (var j = start; j < end; ++j) + { indentLine(this, j, how); } + var newRanges = this.doc.sel.ranges; + if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0) + { replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); } + } else if (range.head.line > end) { + indentLine(this, range.head.line, how, true); + end = range.head.line; + if (i == this.doc.sel.primIndex) { ensureCursorVisible(this); } + } + } + }), + + // Fetch the parser token for a given character. Useful for hacks + // that want to inspect the mode state (say, for completion). + getTokenAt: function(pos, precise) { + return takeToken(this, pos, precise) + }, + + getLineTokens: function(line, precise) { + return takeToken(this, Pos(line), precise, true) + }, + + getTokenTypeAt: function(pos) { + pos = clipPos(this.doc, pos); + var styles = getLineStyles(this, getLine(this.doc, pos.line)); + var before = 0, after = (styles.length - 1) / 2, ch = pos.ch; + var type; + if (ch == 0) { type = styles[2]; } + else { for (;;) { + var mid = (before + after) >> 1; + if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; } + else if (styles[mid * 2 + 1] < ch) { before = mid + 1; } + else { type = styles[mid * 2 + 2]; break } + } } + var cut = type ? type.indexOf("overlay ") : -1; + return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1) + }, + + getModeAt: function(pos) { + var mode = this.doc.mode; + if (!mode.innerMode) { return mode } + return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode + }, + + getHelper: function(pos, type) { + return this.getHelpers(pos, type)[0] + }, + + getHelpers: function(pos, type) { + var found = []; + if (!helpers.hasOwnProperty(type)) { return found } + var help = helpers[type], mode = this.getModeAt(pos); + if (typeof mode[type] == "string") { + if (help[mode[type]]) { found.push(help[mode[type]]); } + } else if (mode[type]) { + for (var i = 0; i < mode[type].length; i++) { + var val = help[mode[type][i]]; + if (val) { found.push(val); } + } + } else if (mode.helperType && help[mode.helperType]) { + found.push(help[mode.helperType]); + } else if (help[mode.name]) { + found.push(help[mode.name]); + } + for (var i$1 = 0; i$1 < help._global.length; i$1++) { + var cur = help._global[i$1]; + if (cur.pred(mode, this) && indexOf(found, cur.val) == -1) + { found.push(cur.val); } + } + return found + }, + + getStateAfter: function(line, precise) { + var doc = this.doc; + line = clipLine(doc, line == null ? doc.first + doc.size - 1: line); + return getContextBefore(this, line + 1, precise).state + }, + + cursorCoords: function(start, mode) { + var pos, range = this.doc.sel.primary(); + if (start == null) { pos = range.head; } + else if (typeof start == "object") { pos = clipPos(this.doc, start); } + else { pos = start ? range.from() : range.to(); } + return cursorCoords(this, pos, mode || "page") + }, + + charCoords: function(pos, mode) { + return charCoords(this, clipPos(this.doc, pos), mode || "page") + }, + + coordsChar: function(coords, mode) { + coords = fromCoordSystem(this, coords, mode || "page"); + return coordsChar(this, coords.left, coords.top) + }, + + lineAtHeight: function(height, mode) { + height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top; + return lineAtHeight(this.doc, height + this.display.viewOffset) + }, + heightAtLine: function(line, mode, includeWidgets) { + var end = false, lineObj; + if (typeof line == "number") { + var last = this.doc.first + this.doc.size - 1; + if (line < this.doc.first) { line = this.doc.first; } + else if (line > last) { line = last; end = true; } + lineObj = getLine(this.doc, line); + } else { + lineObj = line; + } + return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top + + (end ? this.doc.height - heightAtLine(lineObj) : 0) + }, + + defaultTextHeight: function() { return textHeight(this.display) }, + defaultCharWidth: function() { return charWidth(this.display) }, + + getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}}, + + addWidget: function(pos, node, scroll, vert, horiz) { + var display = this.display; + pos = cursorCoords(this, clipPos(this.doc, pos)); + var top = pos.bottom, left = pos.left; + node.style.position = "absolute"; + node.setAttribute("cm-ignore-events", "true"); + this.display.input.setUneditable(node); + display.sizer.appendChild(node); + if (vert == "over") { + top = pos.top; + } else if (vert == "above" || vert == "near") { + var vspace = Math.max(display.wrapper.clientHeight, this.doc.height), + hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth); + // Default to positioning above (if specified and possible); otherwise default to positioning below + if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight) + { top = pos.top - node.offsetHeight; } + else if (pos.bottom + node.offsetHeight <= vspace) + { top = pos.bottom; } + if (left + node.offsetWidth > hspace) + { left = hspace - node.offsetWidth; } + } + node.style.top = top + "px"; + node.style.left = node.style.right = ""; + if (horiz == "right") { + left = display.sizer.clientWidth - node.offsetWidth; + node.style.right = "0px"; + } else { + if (horiz == "left") { left = 0; } + else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; } + node.style.left = left + "px"; + } + if (scroll) + { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); } + }, + + triggerOnKeyDown: methodOp(onKeyDown), + triggerOnKeyPress: methodOp(onKeyPress), + triggerOnKeyUp: onKeyUp, + triggerOnMouseDown: methodOp(onMouseDown), + + execCommand: function(cmd) { + if (commands.hasOwnProperty(cmd)) + { return commands[cmd].call(null, this) } + }, + + triggerElectric: methodOp(function(text) { triggerElectric(this, text); }), + + findPosH: function(from, amount, unit, visually) { + var dir = 1; + if (amount < 0) { dir = -1; amount = -amount; } + var cur = clipPos(this.doc, from); + for (var i = 0; i < amount; ++i) { + cur = findPosH(this.doc, cur, dir, unit, visually); + if (cur.hitSide) { break } + } + return cur + }, + + moveH: methodOp(function(dir, unit) { + var this$1 = this; + + this.extendSelectionsBy(function (range) { + if (this$1.display.shift || this$1.doc.extend || range.empty()) + { return findPosH(this$1.doc, range.head, dir, unit, this$1.options.rtlMoveVisually) } + else + { return dir < 0 ? range.from() : range.to() } + }, sel_move); + }), + + deleteH: methodOp(function(dir, unit) { + var sel = this.doc.sel, doc = this.doc; + if (sel.somethingSelected()) + { doc.replaceSelection("", null, "+delete"); } + else + { deleteNearSelection(this, function (range) { + var other = findPosH(doc, range.head, dir, unit, false); + return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other} + }); } + }), + + findPosV: function(from, amount, unit, goalColumn) { + var dir = 1, x = goalColumn; + if (amount < 0) { dir = -1; amount = -amount; } + var cur = clipPos(this.doc, from); + for (var i = 0; i < amount; ++i) { + var coords = cursorCoords(this, cur, "div"); + if (x == null) { x = coords.left; } + else { coords.left = x; } + cur = findPosV(this, coords, dir, unit); + if (cur.hitSide) { break } + } + return cur + }, + + moveV: methodOp(function(dir, unit) { + var this$1 = this; + + var doc = this.doc, goals = []; + var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected(); + doc.extendSelectionsBy(function (range) { + if (collapse) + { return dir < 0 ? range.from() : range.to() } + var headPos = cursorCoords(this$1, range.head, "div"); + if (range.goalColumn != null) { headPos.left = range.goalColumn; } + goals.push(headPos.left); + var pos = findPosV(this$1, headPos, dir, unit); + if (unit == "page" && range == doc.sel.primary()) + { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); } + return pos + }, sel_move); + if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++) + { doc.sel.ranges[i].goalColumn = goals[i]; } } + }), + + // Find the word at the given position (as returned by coordsChar). + findWordAt: function(pos) { + var doc = this.doc, line = getLine(doc, pos.line).text; + var start = pos.ch, end = pos.ch; + if (line) { + var helper = this.getHelper(pos, "wordChars"); + if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; } + var startChar = line.charAt(start); + var check = isWordChar(startChar, helper) + ? function (ch) { return isWordChar(ch, helper); } + : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); } + : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); }; + while (start > 0 && check(line.charAt(start - 1))) { --start; } + while (end < line.length && check(line.charAt(end))) { ++end; } + } + return new Range(Pos(pos.line, start), Pos(pos.line, end)) + }, + + toggleOverwrite: function(value) { + if (value != null && value == this.state.overwrite) { return } + if (this.state.overwrite = !this.state.overwrite) + { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); } + else + { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); } + + signal(this, "overwriteToggle", this, this.state.overwrite); + }, + hasFocus: function() { return this.display.input.getField() == activeElt() }, + isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) }, + + scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }), + getScrollInfo: function() { + var scroller = this.display.scroller; + return {left: scroller.scrollLeft, top: scroller.scrollTop, + height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight, + width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth, + clientHeight: displayHeight(this), clientWidth: displayWidth(this)} + }, + + scrollIntoView: methodOp(function(range, margin) { + if (range == null) { + range = {from: this.doc.sel.primary().head, to: null}; + if (margin == null) { margin = this.options.cursorScrollMargin; } + } else if (typeof range == "number") { + range = {from: Pos(range, 0), to: null}; + } else if (range.from == null) { + range = {from: range, to: null}; + } + if (!range.to) { range.to = range.from; } + range.margin = margin || 0; + + if (range.from.line != null) { + scrollToRange(this, range); + } else { + scrollToCoordsRange(this, range.from, range.to, range.margin); + } + }), + + setSize: methodOp(function(width, height) { + var this$1 = this; + + var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; }; + if (width != null) { this.display.wrapper.style.width = interpret(width); } + if (height != null) { this.display.wrapper.style.height = interpret(height); } + if (this.options.lineWrapping) { clearLineMeasurementCache(this); } + var lineNo = this.display.viewFrom; + this.doc.iter(lineNo, this.display.viewTo, function (line) { + if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) + { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo, "widget"); break } } } + ++lineNo; + }); + this.curOp.forceUpdate = true; + signal(this, "refresh", this); + }), + + operation: function(f){return runInOp(this, f)}, + startOperation: function(){return startOperation(this)}, + endOperation: function(){return endOperation(this)}, + + refresh: methodOp(function() { + var oldHeight = this.display.cachedTextHeight; + regChange(this); + this.curOp.forceUpdate = true; + clearCaches(this); + scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop); + updateGutterSpace(this.display); + if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5 || this.options.lineWrapping) + { estimateLineHeights(this); } + signal(this, "refresh", this); + }), + + swapDoc: methodOp(function(doc) { + var old = this.doc; + old.cm = null; + // Cancel the current text selection if any (#5821) + if (this.state.selectingText) { this.state.selectingText(); } + attachDoc(this, doc); + clearCaches(this); + this.display.input.reset(); + scrollToCoords(this, doc.scrollLeft, doc.scrollTop); + this.curOp.forceScroll = true; + signalLater(this, "swapDoc", this, old); + return old + }), + + phrase: function(phraseText) { + var phrases = this.options.phrases; + return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText + }, + + getInputField: function(){return this.display.input.getField()}, + getWrapperElement: function(){return this.display.wrapper}, + getScrollerElement: function(){return this.display.scroller}, + getGutterElement: function(){return this.display.gutters} + }; + eventMixin(CodeMirror); + + CodeMirror.registerHelper = function(type, name, value) { + if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; } + helpers[type][name] = value; + }; + CodeMirror.registerGlobalHelper = function(type, name, predicate, value) { + CodeMirror.registerHelper(type, name, value); + helpers[type]._global.push({pred: predicate, val: value}); + }; + } + + // Used for horizontal relative motion. Dir is -1 or 1 (left or + // right), unit can be "codepoint", "char", "column" (like char, but + // doesn't cross line boundaries), "word" (across next word), or + // "group" (to the start of next group of word or + // non-word-non-whitespace chars). The visually param controls + // whether, in right-to-left text, direction 1 means to move towards + // the next index in the string, or towards the character to the right + // of the current position. The resulting position will have a + // hitSide=true property if it reached the end of the document. + function findPosH(doc, pos, dir, unit, visually) { + var oldPos = pos; + var origDir = dir; + var lineObj = getLine(doc, pos.line); + var lineDir = visually && doc.direction == "rtl" ? -dir : dir; + function findNextLine() { + var l = pos.line + lineDir; + if (l < doc.first || l >= doc.first + doc.size) { return false } + pos = new Pos(l, pos.ch, pos.sticky); + return lineObj = getLine(doc, l) + } + function moveOnce(boundToLine) { + var next; + if (unit == "codepoint") { + var ch = lineObj.text.charCodeAt(pos.ch + (unit > 0 ? 0 : -1)); + if (isNaN(ch)) { next = null; } + else { next = new Pos(pos.line, Math.max(0, Math.min(lineObj.text.length, pos.ch + dir * (ch >= 0xD800 && ch < 0xDC00 ? 2 : 1))), + -dir); } + } else if (visually) { + next = moveVisually(doc.cm, lineObj, pos, dir); + } else { + next = moveLogically(lineObj, pos, dir); + } + if (next == null) { + if (!boundToLine && findNextLine()) + { pos = endOfLine(visually, doc.cm, lineObj, pos.line, lineDir); } + else + { return false } + } else { + pos = next; + } + return true + } + + if (unit == "char" || unit == "codepoint") { + moveOnce(); + } else if (unit == "column") { + moveOnce(true); + } else if (unit == "word" || unit == "group") { + var sawType = null, group = unit == "group"; + var helper = doc.cm && doc.cm.getHelper(pos, "wordChars"); + for (var first = true;; first = false) { + if (dir < 0 && !moveOnce(!first)) { break } + var cur = lineObj.text.charAt(pos.ch) || "\n"; + var type = isWordChar(cur, helper) ? "w" + : group && cur == "\n" ? "n" + : !group || /\s/.test(cur) ? null + : "p"; + if (group && !first && !type) { type = "s"; } + if (sawType && sawType != type) { + if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";} + break + } + + if (type) { sawType = type; } + if (dir > 0 && !moveOnce(!first)) { break } + } + } + var result = skipAtomic(doc, pos, oldPos, origDir, true); + if (equalCursorPos(oldPos, result)) { result.hitSide = true; } + return result + } + + // For relative vertical movement. Dir may be -1 or 1. Unit can be + // "page" or "line". The resulting position will have a hitSide=true + // property if it reached the end of the document. + function findPosV(cm, pos, dir, unit) { + var doc = cm.doc, x = pos.left, y; + if (unit == "page") { + var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight); + var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3); + y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount; + } else if (unit == "line") { + y = dir > 0 ? pos.bottom + 3 : pos.top - 3; + } + var target; + for (;;) { + target = coordsChar(cm, x, y); + if (!target.outside) { break } + if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break } + y += dir * 5; + } + return target + } + + // CONTENTEDITABLE INPUT STYLE + + var ContentEditableInput = function(cm) { + this.cm = cm; + this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null; + this.polling = new Delayed(); + this.composing = null; + this.gracePeriod = false; + this.readDOMTimeout = null; + }; + + ContentEditableInput.prototype.init = function (display) { + var this$1 = this; + + var input = this, cm = input.cm; + var div = input.div = display.lineDiv; + disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize); + + function belongsToInput(e) { + for (var t = e.target; t; t = t.parentNode) { + if (t == div) { return true } + if (/\bCodeMirror-(?:line)?widget\b/.test(t.className)) { break } + } + return false + } + + on(div, "paste", function (e) { + if (!belongsToInput(e) || signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } + // IE doesn't fire input events, so we schedule a read for the pasted content in this way + if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); } + }); + + on(div, "compositionstart", function (e) { + this$1.composing = {data: e.data, done: false}; + }); + on(div, "compositionupdate", function (e) { + if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; } + }); + on(div, "compositionend", function (e) { + if (this$1.composing) { + if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); } + this$1.composing.done = true; + } + }); + + on(div, "touchstart", function () { return input.forceCompositionEnd(); }); + + on(div, "input", function () { + if (!this$1.composing) { this$1.readFromDOMSoon(); } + }); + + function onCopyCut(e) { + if (!belongsToInput(e) || signalDOMEvent(cm, e)) { return } + if (cm.somethingSelected()) { + setLastCopied({lineWise: false, text: cm.getSelections()}); + if (e.type == "cut") { cm.replaceSelection("", null, "cut"); } + } else if (!cm.options.lineWiseCopyCut) { + return + } else { + var ranges = copyableRanges(cm); + setLastCopied({lineWise: true, text: ranges.text}); + if (e.type == "cut") { + cm.operation(function () { + cm.setSelections(ranges.ranges, 0, sel_dontScroll); + cm.replaceSelection("", null, "cut"); + }); + } + } + if (e.clipboardData) { + e.clipboardData.clearData(); + var content = lastCopied.text.join("\n"); + // iOS exposes the clipboard API, but seems to discard content inserted into it + e.clipboardData.setData("Text", content); + if (e.clipboardData.getData("Text") == content) { + e.preventDefault(); + return + } + } + // Old-fashioned briefly-focus-a-textarea hack + var kludge = hiddenTextarea(), te = kludge.firstChild; + cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild); + te.value = lastCopied.text.join("\n"); + var hadFocus = document.activeElement; + selectInput(te); + setTimeout(function () { + cm.display.lineSpace.removeChild(kludge); + hadFocus.focus(); + if (hadFocus == div) { input.showPrimarySelection(); } + }, 50); + } + on(div, "copy", onCopyCut); + on(div, "cut", onCopyCut); + }; + + ContentEditableInput.prototype.screenReaderLabelChanged = function (label) { + // Label for screenreaders, accessibility + if(label) { + this.div.setAttribute('aria-label', label); + } else { + this.div.removeAttribute('aria-label'); + } + }; + + ContentEditableInput.prototype.prepareSelection = function () { + var result = prepareSelection(this.cm, false); + result.focus = document.activeElement == this.div; + return result + }; + + ContentEditableInput.prototype.showSelection = function (info, takeFocus) { + if (!info || !this.cm.display.view.length) { return } + if (info.focus || takeFocus) { this.showPrimarySelection(); } + this.showMultipleSelections(info); + }; + + ContentEditableInput.prototype.getSelection = function () { + return this.cm.display.wrapper.ownerDocument.getSelection() + }; + + ContentEditableInput.prototype.showPrimarySelection = function () { + var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary(); + var from = prim.from(), to = prim.to(); + + if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) { + sel.removeAllRanges(); + return + } + + var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); + var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset); + if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad && + cmp(minPos(curAnchor, curFocus), from) == 0 && + cmp(maxPos(curAnchor, curFocus), to) == 0) + { return } + + var view = cm.display.view; + var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) || + {node: view[0].measure.map[2], offset: 0}; + var end = to.line < cm.display.viewTo && posToDOM(cm, to); + if (!end) { + var measure = view[view.length - 1].measure; + var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map; + end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]}; + } + + if (!start || !end) { + sel.removeAllRanges(); + return + } + + var old = sel.rangeCount && sel.getRangeAt(0), rng; + try { rng = range(start.node, start.offset, end.offset, end.node); } + catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible + if (rng) { + if (!gecko && cm.state.focused) { + sel.collapse(start.node, start.offset); + if (!rng.collapsed) { + sel.removeAllRanges(); + sel.addRange(rng); + } + } else { + sel.removeAllRanges(); + sel.addRange(rng); + } + if (old && sel.anchorNode == null) { sel.addRange(old); } + else if (gecko) { this.startGracePeriod(); } + } + this.rememberSelection(); + }; + + ContentEditableInput.prototype.startGracePeriod = function () { + var this$1 = this; + + clearTimeout(this.gracePeriod); + this.gracePeriod = setTimeout(function () { + this$1.gracePeriod = false; + if (this$1.selectionChanged()) + { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); } + }, 20); + }; + + ContentEditableInput.prototype.showMultipleSelections = function (info) { + removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors); + removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection); + }; + + ContentEditableInput.prototype.rememberSelection = function () { + var sel = this.getSelection(); + this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset; + this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset; + }; + + ContentEditableInput.prototype.selectionInEditor = function () { + var sel = this.getSelection(); + if (!sel.rangeCount) { return false } + var node = sel.getRangeAt(0).commonAncestorContainer; + return contains(this.div, node) + }; + + ContentEditableInput.prototype.focus = function () { + if (this.cm.options.readOnly != "nocursor") { + if (!this.selectionInEditor() || document.activeElement != this.div) + { this.showSelection(this.prepareSelection(), true); } + this.div.focus(); + } + }; + ContentEditableInput.prototype.blur = function () { this.div.blur(); }; + ContentEditableInput.prototype.getField = function () { return this.div }; + + ContentEditableInput.prototype.supportsTouch = function () { return true }; + + ContentEditableInput.prototype.receivedFocus = function () { + var input = this; + if (this.selectionInEditor()) + { this.pollSelection(); } + else + { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); } + + function poll() { + if (input.cm.state.focused) { + input.pollSelection(); + input.polling.set(input.cm.options.pollInterval, poll); + } + } + this.polling.set(this.cm.options.pollInterval, poll); + }; + + ContentEditableInput.prototype.selectionChanged = function () { + var sel = this.getSelection(); + return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset || + sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset + }; + + ContentEditableInput.prototype.pollSelection = function () { + if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return } + var sel = this.getSelection(), cm = this.cm; + // On Android Chrome (version 56, at least), backspacing into an + // uneditable block element will put the cursor in that element, + // and then, because it's not editable, hide the virtual keyboard. + // Because Android doesn't allow us to actually detect backspace + // presses in a sane way, this code checks for when that happens + // and simulates a backspace press in this case. + if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) { + this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs}); + this.blur(); + this.focus(); + return + } + if (this.composing) { return } + this.rememberSelection(); + var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); + var head = domToPos(cm, sel.focusNode, sel.focusOffset); + if (anchor && head) { runInOp(cm, function () { + setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll); + if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; } + }); } + }; + + ContentEditableInput.prototype.pollContent = function () { + if (this.readDOMTimeout != null) { + clearTimeout(this.readDOMTimeout); + this.readDOMTimeout = null; + } + + var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary(); + var from = sel.from(), to = sel.to(); + if (from.ch == 0 && from.line > cm.firstLine()) + { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); } + if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine()) + { to = Pos(to.line + 1, 0); } + if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false } + + var fromIndex, fromLine, fromNode; + if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) { + fromLine = lineNo(display.view[0].line); + fromNode = display.view[0].node; + } else { + fromLine = lineNo(display.view[fromIndex].line); + fromNode = display.view[fromIndex - 1].node.nextSibling; + } + var toIndex = findViewIndex(cm, to.line); + var toLine, toNode; + if (toIndex == display.view.length - 1) { + toLine = display.viewTo - 1; + toNode = display.lineDiv.lastChild; + } else { + toLine = lineNo(display.view[toIndex + 1].line) - 1; + toNode = display.view[toIndex + 1].node.previousSibling; + } + + if (!fromNode) { return false } + var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine)); + var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length)); + while (newText.length > 1 && oldText.length > 1) { + if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; } + else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; } + else { break } + } + + var cutFront = 0, cutEnd = 0; + var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length); + while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront)) + { ++cutFront; } + var newBot = lst(newText), oldBot = lst(oldText); + var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0), + oldBot.length - (oldText.length == 1 ? cutFront : 0)); + while (cutEnd < maxCutEnd && + newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) + { ++cutEnd; } + // Try to move start of change to start of selection if ambiguous + if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) { + while (cutFront && cutFront > from.ch && + newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) { + cutFront--; + cutEnd++; + } + } + + newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, ""); + newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, ""); + + var chFrom = Pos(fromLine, cutFront); + var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0); + if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) { + replaceRange(cm.doc, newText, chFrom, chTo, "+input"); + return true + } + }; + + ContentEditableInput.prototype.ensurePolled = function () { + this.forceCompositionEnd(); + }; + ContentEditableInput.prototype.reset = function () { + this.forceCompositionEnd(); + }; + ContentEditableInput.prototype.forceCompositionEnd = function () { + if (!this.composing) { return } + clearTimeout(this.readDOMTimeout); + this.composing = null; + this.updateFromDOM(); + this.div.blur(); + this.div.focus(); + }; + ContentEditableInput.prototype.readFromDOMSoon = function () { + var this$1 = this; + + if (this.readDOMTimeout != null) { return } + this.readDOMTimeout = setTimeout(function () { + this$1.readDOMTimeout = null; + if (this$1.composing) { + if (this$1.composing.done) { this$1.composing = null; } + else { return } + } + this$1.updateFromDOM(); + }, 80); + }; + + ContentEditableInput.prototype.updateFromDOM = function () { + var this$1 = this; + + if (this.cm.isReadOnly() || !this.pollContent()) + { runInOp(this.cm, function () { return regChange(this$1.cm); }); } + }; + + ContentEditableInput.prototype.setUneditable = function (node) { + node.contentEditable = "false"; + }; + + ContentEditableInput.prototype.onKeyPress = function (e) { + if (e.charCode == 0 || this.composing) { return } + e.preventDefault(); + if (!this.cm.isReadOnly()) + { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); } + }; + + ContentEditableInput.prototype.readOnlyChanged = function (val) { + this.div.contentEditable = String(val != "nocursor"); + }; + + ContentEditableInput.prototype.onContextMenu = function () {}; + ContentEditableInput.prototype.resetPosition = function () {}; + + ContentEditableInput.prototype.needsContentAttribute = true; + + function posToDOM(cm, pos) { + var view = findViewForLine(cm, pos.line); + if (!view || view.hidden) { return null } + var line = getLine(cm.doc, pos.line); + var info = mapFromLineView(view, line, pos.line); + + var order = getOrder(line, cm.doc.direction), side = "left"; + if (order) { + var partPos = getBidiPartAt(order, pos.ch); + side = partPos % 2 ? "right" : "left"; + } + var result = nodeAndOffsetInLineMap(info.map, pos.ch, side); + result.offset = result.collapse == "right" ? result.end : result.start; + return result + } + + function isInGutter(node) { + for (var scan = node; scan; scan = scan.parentNode) + { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } } + return false + } + + function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos } + + function domTextBetween(cm, from, to, fromLine, toLine) { + var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false; + function recognizeMarker(id) { return function (marker) { return marker.id == id; } } + function close() { + if (closing) { + text += lineSep; + if (extraLinebreak) { text += lineSep; } + closing = extraLinebreak = false; + } + } + function addText(str) { + if (str) { + close(); + text += str; + } + } + function walk(node) { + if (node.nodeType == 1) { + var cmText = node.getAttribute("cm-text"); + if (cmText) { + addText(cmText); + return + } + var markerID = node.getAttribute("cm-marker"), range; + if (markerID) { + var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID)); + if (found.length && (range = found[0].find(0))) + { addText(getBetween(cm.doc, range.from, range.to).join(lineSep)); } + return + } + if (node.getAttribute("contenteditable") == "false") { return } + var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName); + if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return } + + if (isBlock) { close(); } + for (var i = 0; i < node.childNodes.length; i++) + { walk(node.childNodes[i]); } + + if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; } + if (isBlock) { closing = true; } + } else if (node.nodeType == 3) { + addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " ")); + } + } + for (;;) { + walk(from); + if (from == to) { break } + from = from.nextSibling; + extraLinebreak = false; + } + return text + } + + function domToPos(cm, node, offset) { + var lineNode; + if (node == cm.display.lineDiv) { + lineNode = cm.display.lineDiv.childNodes[offset]; + if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) } + node = null; offset = 0; + } else { + for (lineNode = node;; lineNode = lineNode.parentNode) { + if (!lineNode || lineNode == cm.display.lineDiv) { return null } + if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break } + } + } + for (var i = 0; i < cm.display.view.length; i++) { + var lineView = cm.display.view[i]; + if (lineView.node == lineNode) + { return locateNodeInLineView(lineView, node, offset) } + } + } + + function locateNodeInLineView(lineView, node, offset) { + var wrapper = lineView.text.firstChild, bad = false; + if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) } + if (node == wrapper) { + bad = true; + node = wrapper.childNodes[offset]; + offset = 0; + if (!node) { + var line = lineView.rest ? lst(lineView.rest) : lineView.line; + return badPos(Pos(lineNo(line), line.text.length), bad) + } + } + + var textNode = node.nodeType == 3 ? node : null, topNode = node; + if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) { + textNode = node.firstChild; + if (offset) { offset = textNode.nodeValue.length; } + } + while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; } + var measure = lineView.measure, maps = measure.maps; + + function find(textNode, topNode, offset) { + for (var i = -1; i < (maps ? maps.length : 0); i++) { + var map = i < 0 ? measure.map : maps[i]; + for (var j = 0; j < map.length; j += 3) { + var curNode = map[j + 2]; + if (curNode == textNode || curNode == topNode) { + var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]); + var ch = map[j] + offset; + if (offset < 0 || curNode != textNode) { ch = map[j + (offset ? 1 : 0)]; } + return Pos(line, ch) + } + } + } + } + var found = find(textNode, topNode, offset); + if (found) { return badPos(found, bad) } + + // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems + for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) { + found = find(after, after.firstChild, 0); + if (found) + { return badPos(Pos(found.line, found.ch - dist), bad) } + else + { dist += after.textContent.length; } + } + for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) { + found = find(before, before.firstChild, -1); + if (found) + { return badPos(Pos(found.line, found.ch + dist$1), bad) } + else + { dist$1 += before.textContent.length; } + } + } + + // TEXTAREA INPUT STYLE + + var TextareaInput = function(cm) { + this.cm = cm; + // See input.poll and input.reset + this.prevInput = ""; + + // Flag that indicates whether we expect input to appear real soon + // now (after some event like 'keypress' or 'input') and are + // polling intensively. + this.pollingFast = false; + // Self-resetting timeout for the poller + this.polling = new Delayed(); + // Used to work around IE issue with selection being forgotten when focus moves away from textarea + this.hasSelection = false; + this.composing = null; + }; + + TextareaInput.prototype.init = function (display) { + var this$1 = this; + + var input = this, cm = this.cm; + this.createField(display); + var te = this.textarea; + + display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild); + + // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore) + if (ios) { te.style.width = "0px"; } + + on(te, "input", function () { + if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; } + input.poll(); + }); + + on(te, "paste", function (e) { + if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } + + cm.state.pasteIncoming = +new Date; + input.fastPoll(); + }); + + function prepareCopyCut(e) { + if (signalDOMEvent(cm, e)) { return } + if (cm.somethingSelected()) { + setLastCopied({lineWise: false, text: cm.getSelections()}); + } else if (!cm.options.lineWiseCopyCut) { + return + } else { + var ranges = copyableRanges(cm); + setLastCopied({lineWise: true, text: ranges.text}); + if (e.type == "cut") { + cm.setSelections(ranges.ranges, null, sel_dontScroll); + } else { + input.prevInput = ""; + te.value = ranges.text.join("\n"); + selectInput(te); + } + } + if (e.type == "cut") { cm.state.cutIncoming = +new Date; } + } + on(te, "cut", prepareCopyCut); + on(te, "copy", prepareCopyCut); + + on(display.scroller, "paste", function (e) { + if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return } + if (!te.dispatchEvent) { + cm.state.pasteIncoming = +new Date; + input.focus(); + return + } + + // Pass the `paste` event to the textarea so it's handled by its event listener. + var event = new Event("paste"); + event.clipboardData = e.clipboardData; + te.dispatchEvent(event); + }); + + // Prevent normal selection in the editor (we handle our own) + on(display.lineSpace, "selectstart", function (e) { + if (!eventInWidget(display, e)) { e_preventDefault(e); } + }); + + on(te, "compositionstart", function () { + var start = cm.getCursor("from"); + if (input.composing) { input.composing.range.clear(); } + input.composing = { + start: start, + range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"}) + }; + }); + on(te, "compositionend", function () { + if (input.composing) { + input.poll(); + input.composing.range.clear(); + input.composing = null; + } + }); + }; + + TextareaInput.prototype.createField = function (_display) { + // Wraps and hides input textarea + this.wrapper = hiddenTextarea(); + // The semihidden textarea that is focused when the editor is + // focused, and receives input. + this.textarea = this.wrapper.firstChild; + }; + + TextareaInput.prototype.screenReaderLabelChanged = function (label) { + // Label for screenreaders, accessibility + if(label) { + this.textarea.setAttribute('aria-label', label); + } else { + this.textarea.removeAttribute('aria-label'); + } + }; + + TextareaInput.prototype.prepareSelection = function () { + // Redraw the selection and/or cursor + var cm = this.cm, display = cm.display, doc = cm.doc; + var result = prepareSelection(cm); + + // Move the hidden textarea near the cursor to prevent scrolling artifacts + if (cm.options.moveInputWithCursor) { + var headPos = cursorCoords(cm, doc.sel.primary().head, "div"); + var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect(); + result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10, + headPos.top + lineOff.top - wrapOff.top)); + result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10, + headPos.left + lineOff.left - wrapOff.left)); + } + + return result + }; + + TextareaInput.prototype.showSelection = function (drawn) { + var cm = this.cm, display = cm.display; + removeChildrenAndAdd(display.cursorDiv, drawn.cursors); + removeChildrenAndAdd(display.selectionDiv, drawn.selection); + if (drawn.teTop != null) { + this.wrapper.style.top = drawn.teTop + "px"; + this.wrapper.style.left = drawn.teLeft + "px"; + } + }; + + // Reset the input to correspond to the selection (or to be empty, + // when not typing and nothing is selected) + TextareaInput.prototype.reset = function (typing) { + if (this.contextMenuPending || this.composing) { return } + var cm = this.cm; + if (cm.somethingSelected()) { + this.prevInput = ""; + var content = cm.getSelection(); + this.textarea.value = content; + if (cm.state.focused) { selectInput(this.textarea); } + if (ie && ie_version >= 9) { this.hasSelection = content; } + } else if (!typing) { + this.prevInput = this.textarea.value = ""; + if (ie && ie_version >= 9) { this.hasSelection = null; } + } + }; + + TextareaInput.prototype.getField = function () { return this.textarea }; + + TextareaInput.prototype.supportsTouch = function () { return false }; + + TextareaInput.prototype.focus = function () { + if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) { + try { this.textarea.focus(); } + catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM + } + }; + + TextareaInput.prototype.blur = function () { this.textarea.blur(); }; + + TextareaInput.prototype.resetPosition = function () { + this.wrapper.style.top = this.wrapper.style.left = 0; + }; + + TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); }; + + // Poll for input changes, using the normal rate of polling. This + // runs as long as the editor is focused. + TextareaInput.prototype.slowPoll = function () { + var this$1 = this; + + if (this.pollingFast) { return } + this.polling.set(this.cm.options.pollInterval, function () { + this$1.poll(); + if (this$1.cm.state.focused) { this$1.slowPoll(); } + }); + }; + + // When an event has just come in that is likely to add or change + // something in the input textarea, we poll faster, to ensure that + // the change appears on the screen quickly. + TextareaInput.prototype.fastPoll = function () { + var missed = false, input = this; + input.pollingFast = true; + function p() { + var changed = input.poll(); + if (!changed && !missed) {missed = true; input.polling.set(60, p);} + else {input.pollingFast = false; input.slowPoll();} + } + input.polling.set(20, p); + }; + + // Read input from the textarea, and update the document to match. + // When something is selected, it is present in the textarea, and + // selected (unless it is huge, in which case a placeholder is + // used). When nothing is selected, the cursor sits after previously + // seen text (can be empty), which is stored in prevInput (we must + // not reset the textarea when typing, because that breaks IME). + TextareaInput.prototype.poll = function () { + var this$1 = this; + + var cm = this.cm, input = this.textarea, prevInput = this.prevInput; + // Since this is called a *lot*, try to bail out as cheaply as + // possible when it is clear that nothing happened. hasSelection + // will be the case when there is a lot of text in the textarea, + // in which case reading its value would be expensive. + if (this.contextMenuPending || !cm.state.focused || + (hasSelection(input) && !prevInput && !this.composing) || + cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq) + { return false } + + var text = input.value; + // If nothing changed, bail. + if (text == prevInput && !cm.somethingSelected()) { return false } + // Work around nonsensical selection resetting in IE9/10, and + // inexplicable appearance of private area unicode characters on + // some key combos in Mac (#2689). + if (ie && ie_version >= 9 && this.hasSelection === text || + mac && /[\uf700-\uf7ff]/.test(text)) { + cm.display.input.reset(); + return false + } + + if (cm.doc.sel == cm.display.selForContextMenu) { + var first = text.charCodeAt(0); + if (first == 0x200b && !prevInput) { prevInput = "\u200b"; } + if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") } + } + // Find the part of the input that is actually new + var same = 0, l = Math.min(prevInput.length, text.length); + while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; } + + runInOp(cm, function () { + applyTextInput(cm, text.slice(same), prevInput.length - same, + null, this$1.composing ? "*compose" : null); + + // Don't leave long text in the textarea, since it makes further polling slow + if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; } + else { this$1.prevInput = text; } + + if (this$1.composing) { + this$1.composing.range.clear(); + this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"), + {className: "CodeMirror-composing"}); + } + }); + return true + }; + + TextareaInput.prototype.ensurePolled = function () { + if (this.pollingFast && this.poll()) { this.pollingFast = false; } + }; + + TextareaInput.prototype.onKeyPress = function () { + if (ie && ie_version >= 9) { this.hasSelection = null; } + this.fastPoll(); + }; + + TextareaInput.prototype.onContextMenu = function (e) { + var input = this, cm = input.cm, display = cm.display, te = input.textarea; + if (input.contextMenuPending) { input.contextMenuPending(); } + var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop; + if (!pos || presto) { return } // Opera is difficult. + + // Reset the current text selection only if the click is done outside of the selection + // and 'resetSelectionOnContextMenu' option is true. + var reset = cm.options.resetSelectionOnContextMenu; + if (reset && cm.doc.sel.contains(pos) == -1) + { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); } + + var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText; + var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect(); + input.wrapper.style.cssText = "position: static"; + te.style.cssText = "position: absolute; width: 30px; height: 30px;\n top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; + var oldScrollY; + if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712) + display.input.focus(); + if (webkit) { window.scrollTo(null, oldScrollY); } + display.input.reset(); + // Adds "Select all" to context menu in FF + if (!cm.somethingSelected()) { te.value = input.prevInput = " "; } + input.contextMenuPending = rehide; + display.selForContextMenu = cm.doc.sel; + clearTimeout(display.detectingSelectAll); + + // Select-all will be greyed out if there's nothing to select, so + // this adds a zero-width space so that we can later check whether + // it got selected. + function prepareSelectAllHack() { + if (te.selectionStart != null) { + var selected = cm.somethingSelected(); + var extval = "\u200b" + (selected ? te.value : ""); + te.value = "\u21da"; // Used to catch context-menu undo + te.value = extval; + input.prevInput = selected ? "" : "\u200b"; + te.selectionStart = 1; te.selectionEnd = extval.length; + // Re-set this, in case some other handler touched the + // selection in the meantime. + display.selForContextMenu = cm.doc.sel; + } + } + function rehide() { + if (input.contextMenuPending != rehide) { return } + input.contextMenuPending = false; + input.wrapper.style.cssText = oldWrapperCSS; + te.style.cssText = oldCSS; + if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); } + + // Try to detect the user choosing select-all + if (te.selectionStart != null) { + if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); } + var i = 0, poll = function () { + if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 && + te.selectionEnd > 0 && input.prevInput == "\u200b") { + operation(cm, selectAll)(cm); + } else if (i++ < 10) { + display.detectingSelectAll = setTimeout(poll, 500); + } else { + display.selForContextMenu = null; + display.input.reset(); + } + }; + display.detectingSelectAll = setTimeout(poll, 200); + } + } + + if (ie && ie_version >= 9) { prepareSelectAllHack(); } + if (captureRightClick) { + e_stop(e); + var mouseup = function () { + off(window, "mouseup", mouseup); + setTimeout(rehide, 20); + }; + on(window, "mouseup", mouseup); + } else { + setTimeout(rehide, 50); + } + }; + + TextareaInput.prototype.readOnlyChanged = function (val) { + if (!val) { this.reset(); } + this.textarea.disabled = val == "nocursor"; + this.textarea.readOnly = !!val; + }; + + TextareaInput.prototype.setUneditable = function () {}; + + TextareaInput.prototype.needsContentAttribute = false; + + function fromTextArea(textarea, options) { + options = options ? copyObj(options) : {}; + options.value = textarea.value; + if (!options.tabindex && textarea.tabIndex) + { options.tabindex = textarea.tabIndex; } + if (!options.placeholder && textarea.placeholder) + { options.placeholder = textarea.placeholder; } + // Set autofocus to true if this textarea is focused, or if it has + // autofocus and no other element is focused. + if (options.autofocus == null) { + var hasFocus = activeElt(); + options.autofocus = hasFocus == textarea || + textarea.getAttribute("autofocus") != null && hasFocus == document.body; + } + + function save() {textarea.value = cm.getValue();} + + var realSubmit; + if (textarea.form) { + on(textarea.form, "submit", save); + // Deplorable hack to make the submit method do the right thing. + if (!options.leaveSubmitMethodAlone) { + var form = textarea.form; + realSubmit = form.submit; + try { + var wrappedSubmit = form.submit = function () { + save(); + form.submit = realSubmit; + form.submit(); + form.submit = wrappedSubmit; + }; + } catch(e) {} + } + } + + options.finishInit = function (cm) { + cm.save = save; + cm.getTextArea = function () { return textarea; }; + cm.toTextArea = function () { + cm.toTextArea = isNaN; // Prevent this from being ran twice + save(); + textarea.parentNode.removeChild(cm.getWrapperElement()); + textarea.style.display = ""; + if (textarea.form) { + off(textarea.form, "submit", save); + if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == "function") + { textarea.form.submit = realSubmit; } + } + }; + }; + + textarea.style.display = "none"; + var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); }, + options); + return cm + } + + function addLegacyProps(CodeMirror) { + CodeMirror.off = off; + CodeMirror.on = on; + CodeMirror.wheelEventPixels = wheelEventPixels; + CodeMirror.Doc = Doc; + CodeMirror.splitLines = splitLinesAuto; + CodeMirror.countColumn = countColumn; + CodeMirror.findColumn = findColumn; + CodeMirror.isWordChar = isWordCharBasic; + CodeMirror.Pass = Pass; + CodeMirror.signal = signal; + CodeMirror.Line = Line; + CodeMirror.changeEnd = changeEnd; + CodeMirror.scrollbarModel = scrollbarModel; + CodeMirror.Pos = Pos; + CodeMirror.cmpPos = cmp; + CodeMirror.modes = modes; + CodeMirror.mimeModes = mimeModes; + CodeMirror.resolveMode = resolveMode; + CodeMirror.getMode = getMode; + CodeMirror.modeExtensions = modeExtensions; + CodeMirror.extendMode = extendMode; + CodeMirror.copyState = copyState; + CodeMirror.startState = startState; + CodeMirror.innerMode = innerMode; + CodeMirror.commands = commands; + CodeMirror.keyMap = keyMap; + CodeMirror.keyName = keyName; + CodeMirror.isModifierKey = isModifierKey; + CodeMirror.lookupKey = lookupKey; + CodeMirror.normalizeKeyMap = normalizeKeyMap; + CodeMirror.StringStream = StringStream; + CodeMirror.SharedTextMarker = SharedTextMarker; + CodeMirror.TextMarker = TextMarker; + CodeMirror.LineWidget = LineWidget; + CodeMirror.e_preventDefault = e_preventDefault; + CodeMirror.e_stopPropagation = e_stopPropagation; + CodeMirror.e_stop = e_stop; + CodeMirror.addClass = addClass; + CodeMirror.contains = contains; + CodeMirror.rmClass = rmClass; + CodeMirror.keyNames = keyNames; + } + + // EDITOR CONSTRUCTOR + + defineOptions(CodeMirror); + + addEditorMethods(CodeMirror); + + // Set up methods on CodeMirror's prototype to redirect to the editor's document. + var dontDelegate = "iter insert remove copy getEditor constructor".split(" "); + for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0) + { CodeMirror.prototype[prop] = (function(method) { + return function() {return method.apply(this.doc, arguments)} + })(Doc.prototype[prop]); } } + + eventMixin(Doc); + CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}; + + // Extra arguments are stored as the mode's dependencies, which is + // used by (legacy) mechanisms like loadmode.js to automatically + // load a mode. (Preferred mechanism is the require/define calls.) + CodeMirror.defineMode = function(name/*, mode, …*/) { + if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name; } + defineMode.apply(this, arguments); + }; + + CodeMirror.defineMIME = defineMIME; + + // Minimal default mode. + CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); }); + CodeMirror.defineMIME("text/plain", "null"); + + // EXTENSIONS + + CodeMirror.defineExtension = function (name, func) { + CodeMirror.prototype[name] = func; + }; + CodeMirror.defineDocExtension = function (name, func) { + Doc.prototype[name] = func; + }; + + CodeMirror.fromTextArea = fromTextArea; + + addLegacyProps(CodeMirror); + + CodeMirror.version = "5.58.3"; + + return CodeMirror; +}))); + + +/* +file https://github.com/codemirror/CodeMirror/blob/5.58.3/addon/edit/matchbrackets.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + var ie_lt8 = /MSIE \d/.test(navigator.userAgent) && + (document.documentMode == null || document.documentMode < 8); + + var Pos = CodeMirror.Pos; + + var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<", "<": ">>", ">": "<<"}; + + function bracketRegex(config) { + return config && config.bracketRegex || /[(){}[\]]/ + } + + function findMatchingBracket(cm, where, config) { + var line = cm.getLineHandle(where.line), pos = where.ch - 1; + var afterCursor = config && config.afterCursor + if (afterCursor == null) + afterCursor = /(^| )cm-fat-cursor($| )/.test(cm.getWrapperElement().className) + var re = bracketRegex(config) + + // A cursor is defined as between two characters, but in in vim command mode + // (i.e. not insert mode), the cursor is visually represented as a + // highlighted box on top of the 2nd character. Otherwise, we allow matches + // from before or after the cursor. + var match = (!afterCursor && pos >= 0 && re.test(line.text.charAt(pos)) && matching[line.text.charAt(pos)]) || + re.test(line.text.charAt(pos + 1)) && matching[line.text.charAt(++pos)]; + if (!match) return null; + var dir = match.charAt(1) == ">" ? 1 : -1; + if (config && config.strict && (dir > 0) != (pos == where.ch)) return null; + var style = cm.getTokenTypeAt(Pos(where.line, pos + 1)); + + var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style || null, config); + if (found == null) return null; + return {from: Pos(where.line, pos), to: found && found.pos, + match: found && found.ch == match.charAt(0), forward: dir > 0}; + } + + // bracketRegex is used to specify which type of bracket to scan + // should be a regexp, e.g. /[[\]]/ + // + // Note: If "where" is on an open bracket, then this bracket is ignored. + // + // Returns false when no bracket was found, null when it reached + // maxScanLines and gave up + function scanForBracket(cm, where, dir, style, config) { + var maxScanLen = (config && config.maxScanLineLength) || 10000; + var maxScanLines = (config && config.maxScanLines) || 1000; + + var stack = []; + var re = bracketRegex(config) + var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1) + : Math.max(cm.firstLine() - 1, where.line - maxScanLines); + for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) { + var line = cm.getLine(lineNo); + if (!line) continue; + var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1; + if (line.length > maxScanLen) continue; + if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0); + for (; pos != end; pos += dir) { + var ch = line.charAt(pos); + if (re.test(ch) && (style === undefined || cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style)) { + var match = matching[ch]; + if (match && (match.charAt(1) == ">") == (dir > 0)) stack.push(ch); + else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch}; + else stack.pop(); + } + } + } + return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null; + } + + function matchBrackets(cm, autoclear, config) { + // Disable brace matching in long lines, since it'll cause hugely slow updates + var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000; + var marks = [], ranges = cm.listSelections(); + for (var i = 0; i < ranges.length; i++) { + var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, config); + if (match && cm.getLine(match.from.line).length <= maxHighlightLen) { + var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; + marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style})); + if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen) + marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style})); + } + } + + if (marks.length) { + // Kludge to work around the IE bug from issue #1193, where text + // input stops going to the textare whever this fires. + if (ie_lt8 && cm.state.focused) cm.focus(); + + var clear = function() { + cm.operation(function() { + for (var i = 0; i < marks.length; i++) marks[i].clear(); + }); + }; + if (autoclear) setTimeout(clear, 800); + else return clear; + } + } + + function doMatchBrackets(cm) { + cm.operation(function() { + if (cm.state.matchBrackets.currentlyHighlighted) { + cm.state.matchBrackets.currentlyHighlighted(); + cm.state.matchBrackets.currentlyHighlighted = null; + } + cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets); + }); + } + + CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) { + function clear(cm) { + if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) { + cm.state.matchBrackets.currentlyHighlighted(); + cm.state.matchBrackets.currentlyHighlighted = null; + } + } + + if (old && old != CodeMirror.Init) { + cm.off("cursorActivity", doMatchBrackets); + cm.off("focus", doMatchBrackets) + cm.off("blur", clear) + clear(cm); + } + if (val) { + cm.state.matchBrackets = typeof val == "object" ? val : {}; + cm.on("cursorActivity", doMatchBrackets); + cm.on("focus", doMatchBrackets) + cm.on("blur", clear) + } + }); + + CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);}); + CodeMirror.defineExtension("findMatchingBracket", function(pos, config, oldConfig){ + // Backwards-compatibility kludge + if (oldConfig || typeof config == "boolean") { + if (!oldConfig) { + config = config ? {strict: true} : null + } else { + oldConfig.strict = config + config = oldConfig + } + } + return findMatchingBracket(this, pos, config) + }); + CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){ + return scanForBracket(this, pos, dir, style, config); + }); +}); + + +/* +file https://github.com/codemirror/CodeMirror/blob/5.58.3/addon/edit/trailingspace.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) { + if (prev == CodeMirror.Init) prev = false; + if (prev && !val) + cm.removeOverlay("trailingspace"); + else if (!prev && val) + cm.addOverlay({ + token: function(stream) { + for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {} + if (i > stream.pos) { stream.pos = i; return null; } + stream.pos = l; + return "trailingspace"; + }, + name: "trailingspace" + }); + }); +}); + + +/* +file https://github.com/codemirror/CodeMirror/blob/5.58.3/mode/javascript/javascript.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("javascript", function(config, parserConfig) { + var indentUnit = config.indentUnit; + var statementIndent = parserConfig.statementIndent; + var jsonldMode = parserConfig.jsonld; + var jsonMode = parserConfig.json || jsonldMode; + var isTS = parserConfig.typescript; + var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/; + + // Tokenizer + + var keywords = function(){ + function kw(type) {return {type: type, style: "keyword"};} + var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d"); + var operator = kw("operator"), atom = {type: "atom", style: "atom"}; + + return { + "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, + "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C, + "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"), + "function": kw("function"), "catch": kw("catch"), + "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"), + "in": operator, "typeof": operator, "instanceof": operator, + "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom, + "this": kw("this"), "class": kw("class"), "super": kw("atom"), + "yield": C, "export": kw("export"), "import": kw("import"), "extends": C, + "await": C + }; + }(); + + var isOperatorChar = /[+\-*&%=<>!?|~^@]/; + var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/; + + function readRegexp(stream) { + var escaped = false, next, inSet = false; + while ((next = stream.next()) != null) { + if (!escaped) { + if (next == "/" && !inSet) return; + if (next == "[") inSet = true; + else if (inSet && next == "]") inSet = false; + } + escaped = !escaped && next == "\\"; + } + } + + // Used as scratch variables to communicate multiple values without + // consing up tons of objects. + var type, content; + function ret(tp, style, cont) { + type = tp; content = cont; + return style; + } + function tokenBase(stream, state) { + var ch = stream.next(); + if (ch == '"' || ch == "'") { + state.tokenize = tokenString(ch); + return state.tokenize(stream, state); + } else if (ch == "." && stream.match(/^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/)) { + return ret("number", "number"); + } else if (ch == "." && stream.match("..")) { + return ret("spread", "meta"); + } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) { + return ret(ch); + } else if (ch == "=" && stream.eat(">")) { + return ret("=>", "operator"); + } else if (ch == "0" && stream.match(/^(?:x[\dA-Fa-f_]+|o[0-7_]+|b[01_]+)n?/)) { + return ret("number", "number"); + } else if (/\d/.test(ch)) { + stream.match(/^[\d_]*(?:n|(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)?/); + return ret("number", "number"); + } else if (ch == "/") { + if (stream.eat("*")) { + state.tokenize = tokenComment; + return tokenComment(stream, state); + } else if (stream.eat("/")) { + stream.skipToEnd(); + return ret("comment", "comment"); + } else if (expressionAllowed(stream, state, 1)) { + readRegexp(stream); + stream.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/); + return ret("regexp", "string-2"); + } else { + stream.eat("="); + return ret("operator", "operator", stream.current()); + } + } else if (ch == "`") { + state.tokenize = tokenQuasi; + return tokenQuasi(stream, state); + } else if (ch == "#" && stream.peek() == "!") { + stream.skipToEnd(); + return ret("meta", "meta"); + } else if (ch == "#" && stream.eatWhile(wordRE)) { + return ret("variable", "property") + } else if (ch == "<" && stream.match("!--") || + (ch == "-" && stream.match("->") && !/\S/.test(stream.string.slice(0, stream.start)))) { + stream.skipToEnd() + return ret("comment", "comment") + } else if (isOperatorChar.test(ch)) { + if (ch != ">" || !state.lexical || state.lexical.type != ">") { + if (stream.eat("=")) { + if (ch == "!" || ch == "=") stream.eat("=") + } else if (/[<>*+\-|&?]/.test(ch)) { + stream.eat(ch) + if (ch == ">") stream.eat(ch) + } + } + if (ch == "?" && stream.eat(".")) return ret(".") + return ret("operator", "operator", stream.current()); + } else if (wordRE.test(ch)) { + stream.eatWhile(wordRE); + var word = stream.current() + if (state.lastType != ".") { + if (keywords.propertyIsEnumerable(word)) { + var kw = keywords[word] + return ret(kw.type, kw.style, word) + } + if (word == "async" && stream.match(/^(\s|\/\*([^*]|\*(?!\/))*?\*\/)*[\[\(\w]/, false)) + return ret("async", "keyword", word) + } + return ret("variable", "variable", word) + } + } + + function tokenString(quote) { + return function(stream, state) { + var escaped = false, next; + if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){ + state.tokenize = tokenBase; + return ret("jsonld-keyword", "meta"); + } + while ((next = stream.next()) != null) { + if (next == quote && !escaped) break; + escaped = !escaped && next == "\\"; + } + if (!escaped) state.tokenize = tokenBase; + return ret("string", "string"); + }; + } + + function tokenComment(stream, state) { + var maybeEnd = false, ch; + while (ch = stream.next()) { + if (ch == "/" && maybeEnd) { + state.tokenize = tokenBase; + break; + } + maybeEnd = (ch == "*"); + } + return ret("comment", "comment"); + } + + function tokenQuasi(stream, state) { + var escaped = false, next; + while ((next = stream.next()) != null) { + if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) { + state.tokenize = tokenBase; + break; + } + escaped = !escaped && next == "\\"; + } + return ret("quasi", "string-2", stream.current()); + } + + var brackets = "([{}])"; + // This is a crude lookahead trick to try and notice that we're + // parsing the argument patterns for a fat-arrow function before we + // actually hit the arrow token. It only works if the arrow is on + // the same line as the arguments and there's no strange noise + // (comments) in between. Fallback is to only notice when we hit the + // arrow, and not declare the arguments as locals for the arrow + // body. + function findFatArrow(stream, state) { + if (state.fatArrowAt) state.fatArrowAt = null; + var arrow = stream.string.indexOf("=>", stream.start); + if (arrow < 0) return; + + if (isTS) { // Try to skip TypeScript return type declarations after the arguments + var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow)) + if (m) arrow = m.index + } + + var depth = 0, sawSomething = false; + for (var pos = arrow - 1; pos >= 0; --pos) { + var ch = stream.string.charAt(pos); + var bracket = brackets.indexOf(ch); + if (bracket >= 0 && bracket < 3) { + if (!depth) { ++pos; break; } + if (--depth == 0) { if (ch == "(") sawSomething = true; break; } + } else if (bracket >= 3 && bracket < 6) { + ++depth; + } else if (wordRE.test(ch)) { + sawSomething = true; + } else if (/["'\/`]/.test(ch)) { + for (;; --pos) { + if (pos == 0) return + var next = stream.string.charAt(pos - 1) + if (next == ch && stream.string.charAt(pos - 2) != "\\") { pos--; break } + } + } else if (sawSomething && !depth) { + ++pos; + break; + } + } + if (sawSomething && !depth) state.fatArrowAt = pos; + } + + // Parser + + var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true}; + + function JSLexical(indented, column, type, align, prev, info) { + this.indented = indented; + this.column = column; + this.type = type; + this.prev = prev; + this.info = info; + if (align != null) this.align = align; + } + + function inScope(state, varname) { + for (var v = state.localVars; v; v = v.next) + if (v.name == varname) return true; + for (var cx = state.context; cx; cx = cx.prev) { + for (var v = cx.vars; v; v = v.next) + if (v.name == varname) return true; + } + } + + function parseJS(state, style, type, content, stream) { + var cc = state.cc; + // Communicate our context to the combinators. + // (Less wasteful than consing up a hundred closures on every call.) + cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style; + + if (!state.lexical.hasOwnProperty("align")) + state.lexical.align = true; + + while(true) { + var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement; + if (combinator(type, content)) { + while(cc.length && cc[cc.length - 1].lex) + cc.pop()(); + if (cx.marked) return cx.marked; + if (type == "variable" && inScope(state, content)) return "variable-2"; + return style; + } + } + } + + // Combinator utils + + var cx = {state: null, column: null, marked: null, cc: null}; + function pass() { + for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]); + } + function cont() { + pass.apply(null, arguments); + return true; + } + function inList(name, list) { + for (var v = list; v; v = v.next) if (v.name == name) return true + return false; + } + function register(varname) { + var state = cx.state; + cx.marked = "def"; + if (state.context) { + if (state.lexical.info == "var" && state.context && state.context.block) { + // FIXME function decls are also not block scoped + var newContext = registerVarScoped(varname, state.context) + if (newContext != null) { + state.context = newContext + return + } + } else if (!inList(varname, state.localVars)) { + state.localVars = new Var(varname, state.localVars) + return + } + } + // Fall through means this is global + if (parserConfig.globalVars && !inList(varname, state.globalVars)) + state.globalVars = new Var(varname, state.globalVars) + } + function registerVarScoped(varname, context) { + if (!context) { + return null + } else if (context.block) { + var inner = registerVarScoped(varname, context.prev) + if (!inner) return null + if (inner == context.prev) return context + return new Context(inner, context.vars, true) + } else if (inList(varname, context.vars)) { + return context + } else { + return new Context(context.prev, new Var(varname, context.vars), false) + } + } + + function isModifier(name) { + return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly" + } + + // Combinators + + function Context(prev, vars, block) { this.prev = prev; this.vars = vars; this.block = block } + function Var(name, next) { this.name = name; this.next = next } + + var defaultVars = new Var("this", new Var("arguments", null)) + function pushcontext() { + cx.state.context = new Context(cx.state.context, cx.state.localVars, false) + cx.state.localVars = defaultVars + } + function pushblockcontext() { + cx.state.context = new Context(cx.state.context, cx.state.localVars, true) + cx.state.localVars = null + } + function popcontext() { + cx.state.localVars = cx.state.context.vars + cx.state.context = cx.state.context.prev + } + popcontext.lex = true + function pushlex(type, info) { + var result = function() { + var state = cx.state, indent = state.indented; + if (state.lexical.type == "stat") indent = state.lexical.indented; + else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev) + indent = outer.indented; + state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info); + }; + result.lex = true; + return result; + } + function poplex() { + var state = cx.state; + if (state.lexical.prev) { + if (state.lexical.type == ")") + state.indented = state.lexical.indented; + state.lexical = state.lexical.prev; + } + } + poplex.lex = true; + + function expect(wanted) { + function exp(type) { + if (type == wanted) return cont(); + else if (wanted == ";" || type == "}" || type == ")" || type == "]") return pass(); + else return cont(exp); + }; + return exp; + } + + function statement(type, value) { + if (type == "var") return cont(pushlex("vardef", value), vardef, expect(";"), poplex); + if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex); + if (type == "keyword b") return cont(pushlex("form"), statement, poplex); + if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex); + if (type == "debugger") return cont(expect(";")); + if (type == "{") return cont(pushlex("}"), pushblockcontext, block, poplex, popcontext); + if (type == ";") return cont(); + if (type == "if") { + if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex) + cx.state.cc.pop()(); + return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse); + } + if (type == "function") return cont(functiondef); + if (type == "for") return cont(pushlex("form"), forspec, statement, poplex); + if (type == "class" || (isTS && value == "interface")) { + cx.marked = "keyword" + return cont(pushlex("form", type == "class" ? type : value), className, poplex) + } + if (type == "variable") { + if (isTS && value == "declare") { + cx.marked = "keyword" + return cont(statement) + } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) { + cx.marked = "keyword" + if (value == "enum") return cont(enumdef); + else if (value == "type") return cont(typename, expect("operator"), typeexpr, expect(";")); + else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex) + } else if (isTS && value == "namespace") { + cx.marked = "keyword" + return cont(pushlex("form"), expression, statement, poplex) + } else if (isTS && value == "abstract") { + cx.marked = "keyword" + return cont(statement) + } else { + return cont(pushlex("stat"), maybelabel); + } + } + if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"), pushblockcontext, + block, poplex, poplex, popcontext); + if (type == "case") return cont(expression, expect(":")); + if (type == "default") return cont(expect(":")); + if (type == "catch") return cont(pushlex("form"), pushcontext, maybeCatchBinding, statement, poplex, popcontext); + if (type == "export") return cont(pushlex("stat"), afterExport, poplex); + if (type == "import") return cont(pushlex("stat"), afterImport, poplex); + if (type == "async") return cont(statement) + if (value == "@") return cont(expression, statement) + return pass(pushlex("stat"), expression, expect(";"), poplex); + } + function maybeCatchBinding(type) { + if (type == "(") return cont(funarg, expect(")")) + } + function expression(type, value) { + return expressionInner(type, value, false); + } + function expressionNoComma(type, value) { + return expressionInner(type, value, true); + } + function parenExpr(type) { + if (type != "(") return pass() + return cont(pushlex(")"), maybeexpression, expect(")"), poplex) + } + function expressionInner(type, value, noComma) { + if (cx.state.fatArrowAt == cx.stream.start) { + var body = noComma ? arrowBodyNoComma : arrowBody; + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext); + else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext); + } + + var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma; + if (atomicTypes.hasOwnProperty(type)) return cont(maybeop); + if (type == "function") return cont(functiondef, maybeop); + if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); } + if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression); + if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop); + if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression); + if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop); + if (type == "{") return contCommasep(objprop, "}", null, maybeop); + if (type == "quasi") return pass(quasi, maybeop); + if (type == "new") return cont(maybeTarget(noComma)); + if (type == "import") return cont(expression); + return cont(); + } + function maybeexpression(type) { + if (type.match(/[;\}\)\],]/)) return pass(); + return pass(expression); + } + + function maybeoperatorComma(type, value) { + if (type == ",") return cont(maybeexpression); + return maybeoperatorNoComma(type, value, false); + } + function maybeoperatorNoComma(type, value, noComma) { + var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma; + var expr = noComma == false ? expression : expressionNoComma; + if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext); + if (type == "operator") { + if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me); + if (isTS && value == "<" && cx.stream.match(/^([^<>]|<[^<>]*>)*>\s*\(/, false)) + return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me); + if (value == "?") return cont(expression, expect(":"), expr); + return cont(expr); + } + if (type == "quasi") { return pass(quasi, me); } + if (type == ";") return; + if (type == "(") return contCommasep(expressionNoComma, ")", "call", me); + if (type == ".") return cont(property, me); + if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me); + if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) } + if (type == "regexp") { + cx.state.lastType = cx.marked = "operator" + cx.stream.backUp(cx.stream.pos - cx.stream.start - 1) + return cont(expr) + } + } + function quasi(type, value) { + if (type != "quasi") return pass(); + if (value.slice(value.length - 2) != "${") return cont(quasi); + return cont(expression, continueQuasi); + } + function continueQuasi(type) { + if (type == "}") { + cx.marked = "string-2"; + cx.state.tokenize = tokenQuasi; + return cont(quasi); + } + } + function arrowBody(type) { + findFatArrow(cx.stream, cx.state); + return pass(type == "{" ? statement : expression); + } + function arrowBodyNoComma(type) { + findFatArrow(cx.stream, cx.state); + return pass(type == "{" ? statement : expressionNoComma); + } + function maybeTarget(noComma) { + return function(type) { + if (type == ".") return cont(noComma ? targetNoComma : target); + else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma) + else return pass(noComma ? expressionNoComma : expression); + }; + } + function target(_, value) { + if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); } + } + function targetNoComma(_, value) { + if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); } + } + function maybelabel(type) { + if (type == ":") return cont(poplex, statement); + return pass(maybeoperatorComma, expect(";"), poplex); + } + function property(type) { + if (type == "variable") {cx.marked = "property"; return cont();} + } + function objprop(type, value) { + if (type == "async") { + cx.marked = "property"; + return cont(objprop); + } else if (type == "variable" || cx.style == "keyword") { + cx.marked = "property"; + if (value == "get" || value == "set") return cont(getterSetter); + var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params + if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false))) + cx.state.fatArrowAt = cx.stream.pos + m[0].length + return cont(afterprop); + } else if (type == "number" || type == "string") { + cx.marked = jsonldMode ? "property" : (cx.style + " property"); + return cont(afterprop); + } else if (type == "jsonld-keyword") { + return cont(afterprop); + } else if (isTS && isModifier(value)) { + cx.marked = "keyword" + return cont(objprop) + } else if (type == "[") { + return cont(expression, maybetype, expect("]"), afterprop); + } else if (type == "spread") { + return cont(expressionNoComma, afterprop); + } else if (value == "*") { + cx.marked = "keyword"; + return cont(objprop); + } else if (type == ":") { + return pass(afterprop) + } + } + function getterSetter(type) { + if (type != "variable") return pass(afterprop); + cx.marked = "property"; + return cont(functiondef); + } + function afterprop(type) { + if (type == ":") return cont(expressionNoComma); + if (type == "(") return pass(functiondef); + } + function commasep(what, end, sep) { + function proceed(type, value) { + if (sep ? sep.indexOf(type) > -1 : type == ",") { + var lex = cx.state.lexical; + if (lex.info == "call") lex.pos = (lex.pos || 0) + 1; + return cont(function(type, value) { + if (type == end || value == end) return pass() + return pass(what) + }, proceed); + } + if (type == end || value == end) return cont(); + if (sep && sep.indexOf(";") > -1) return pass(what) + return cont(expect(end)); + } + return function(type, value) { + if (type == end || value == end) return cont(); + return pass(what, proceed); + }; + } + function contCommasep(what, end, info) { + for (var i = 3; i < arguments.length; i++) + cx.cc.push(arguments[i]); + return cont(pushlex(end, info), commasep(what, end), poplex); + } + function block(type) { + if (type == "}") return cont(); + return pass(statement, block); + } + function maybetype(type, value) { + if (isTS) { + if (type == ":") return cont(typeexpr); + if (value == "?") return cont(maybetype); + } + } + function maybetypeOrIn(type, value) { + if (isTS && (type == ":" || value == "in")) return cont(typeexpr) + } + function mayberettype(type) { + if (isTS && type == ":") { + if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr) + else return cont(typeexpr) + } + } + function isKW(_, value) { + if (value == "is") { + cx.marked = "keyword" + return cont() + } + } + function typeexpr(type, value) { + if (value == "keyof" || value == "typeof" || value == "infer") { + cx.marked = "keyword" + return cont(value == "typeof" ? expressionNoComma : typeexpr) + } + if (type == "variable" || value == "void") { + cx.marked = "type" + return cont(afterType) + } + if (value == "|" || value == "&") return cont(typeexpr) + if (type == "string" || type == "number" || type == "atom") return cont(afterType); + if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType) + if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex, afterType) + if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType, afterType) + if (type == "<") return cont(commasep(typeexpr, ">"), typeexpr) + } + function maybeReturnType(type) { + if (type == "=>") return cont(typeexpr) + } + function typeprop(type, value) { + if (type == "variable" || cx.style == "keyword") { + cx.marked = "property" + return cont(typeprop) + } else if (value == "?" || type == "number" || type == "string") { + return cont(typeprop) + } else if (type == ":") { + return cont(typeexpr) + } else if (type == "[") { + return cont(expect("variable"), maybetypeOrIn, expect("]"), typeprop) + } else if (type == "(") { + return pass(functiondecl, typeprop) + } + } + function typearg(type, value) { + if (type == "variable" && cx.stream.match(/^\s*[?:]/, false) || value == "?") return cont(typearg) + if (type == ":") return cont(typeexpr) + if (type == "spread") return cont(typearg) + return pass(typeexpr) + } + function afterType(type, value) { + if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) + if (value == "|" || type == "." || value == "&") return cont(typeexpr) + if (type == "[") return cont(typeexpr, expect("]"), afterType) + if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) } + if (value == "?") return cont(typeexpr, expect(":"), typeexpr) + } + function maybeTypeArgs(_, value) { + if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) + } + function typeparam() { + return pass(typeexpr, maybeTypeDefault) + } + function maybeTypeDefault(_, value) { + if (value == "=") return cont(typeexpr) + } + function vardef(_, value) { + if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)} + return pass(pattern, maybetype, maybeAssign, vardefCont); + } + function pattern(type, value) { + if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) } + if (type == "variable") { register(value); return cont(); } + if (type == "spread") return cont(pattern); + if (type == "[") return contCommasep(eltpattern, "]"); + if (type == "{") return contCommasep(proppattern, "}"); + } + function proppattern(type, value) { + if (type == "variable" && !cx.stream.match(/^\s*:/, false)) { + register(value); + return cont(maybeAssign); + } + if (type == "variable") cx.marked = "property"; + if (type == "spread") return cont(pattern); + if (type == "}") return pass(); + if (type == "[") return cont(expression, expect(']'), expect(':'), proppattern); + return cont(expect(":"), pattern, maybeAssign); + } + function eltpattern() { + return pass(pattern, maybeAssign) + } + function maybeAssign(_type, value) { + if (value == "=") return cont(expressionNoComma); + } + function vardefCont(type) { + if (type == ",") return cont(vardef); + } + function maybeelse(type, value) { + if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex); + } + function forspec(type, value) { + if (value == "await") return cont(forspec); + if (type == "(") return cont(pushlex(")"), forspec1, poplex); + } + function forspec1(type) { + if (type == "var") return cont(vardef, forspec2); + if (type == "variable") return cont(forspec2); + return pass(forspec2) + } + function forspec2(type, value) { + if (type == ")") return cont() + if (type == ";") return cont(forspec2) + if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression, forspec2) } + return pass(expression, forspec2) + } + function functiondef(type, value) { + if (value == "*") {cx.marked = "keyword"; return cont(functiondef);} + if (type == "variable") {register(value); return cont(functiondef);} + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext); + if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef) + } + function functiondecl(type, value) { + if (value == "*") {cx.marked = "keyword"; return cont(functiondecl);} + if (type == "variable") {register(value); return cont(functiondecl);} + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, popcontext); + if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondecl) + } + function typename(type, value) { + if (type == "keyword" || type == "variable") { + cx.marked = "type" + return cont(typename) + } else if (value == "<") { + return cont(pushlex(">"), commasep(typeparam, ">"), poplex) + } + } + function funarg(type, value) { + if (value == "@") cont(expression, funarg) + if (type == "spread") return cont(funarg); + if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); } + if (isTS && type == "this") return cont(maybetype, maybeAssign) + return pass(pattern, maybetype, maybeAssign); + } + function classExpression(type, value) { + // Class expressions may have an optional name. + if (type == "variable") return className(type, value); + return classNameAfter(type, value); + } + function className(type, value) { + if (type == "variable") {register(value); return cont(classNameAfter);} + } + function classNameAfter(type, value) { + if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter) + if (value == "extends" || value == "implements" || (isTS && type == ",")) { + if (value == "implements") cx.marked = "keyword"; + return cont(isTS ? typeexpr : expression, classNameAfter); + } + if (type == "{") return cont(pushlex("}"), classBody, poplex); + } + function classBody(type, value) { + if (type == "async" || + (type == "variable" && + (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) && + cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) { + cx.marked = "keyword"; + return cont(classBody); + } + if (type == "variable" || cx.style == "keyword") { + cx.marked = "property"; + return cont(classfield, classBody); + } + if (type == "number" || type == "string") return cont(classfield, classBody); + if (type == "[") + return cont(expression, maybetype, expect("]"), classfield, classBody) + if (value == "*") { + cx.marked = "keyword"; + return cont(classBody); + } + if (isTS && type == "(") return pass(functiondecl, classBody) + if (type == ";" || type == ",") return cont(classBody); + if (type == "}") return cont(); + if (value == "@") return cont(expression, classBody) + } + function classfield(type, value) { + if (value == "?") return cont(classfield) + if (type == ":") return cont(typeexpr, maybeAssign) + if (value == "=") return cont(expressionNoComma) + var context = cx.state.lexical.prev, isInterface = context && context.info == "interface" + return pass(isInterface ? functiondecl : functiondef) + } + function afterExport(type, value) { + if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); } + if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); } + if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";")); + return pass(statement); + } + function exportField(type, value) { + if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); } + if (type == "variable") return pass(expressionNoComma, exportField); + } + function afterImport(type) { + if (type == "string") return cont(); + if (type == "(") return pass(expression); + return pass(importSpec, maybeMoreImports, maybeFrom); + } + function importSpec(type, value) { + if (type == "{") return contCommasep(importSpec, "}"); + if (type == "variable") register(value); + if (value == "*") cx.marked = "keyword"; + return cont(maybeAs); + } + function maybeMoreImports(type) { + if (type == ",") return cont(importSpec, maybeMoreImports) + } + function maybeAs(_type, value) { + if (value == "as") { cx.marked = "keyword"; return cont(importSpec); } + } + function maybeFrom(_type, value) { + if (value == "from") { cx.marked = "keyword"; return cont(expression); } + } + function arrayLiteral(type) { + if (type == "]") return cont(); + return pass(commasep(expressionNoComma, "]")); + } + function enumdef() { + return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex) + } + function enummember() { + return pass(pattern, maybeAssign); + } + + function isContinuedStatement(state, textAfter) { + return state.lastType == "operator" || state.lastType == "," || + isOperatorChar.test(textAfter.charAt(0)) || + /[,.]/.test(textAfter.charAt(0)); + } + + function expressionAllowed(stream, state, backUp) { + return state.tokenize == tokenBase && + /^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) || + (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0)))) + } + + // Interface + + return { + startState: function(basecolumn) { + var state = { + tokenize: tokenBase, + lastType: "sof", + cc: [], + lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false), + localVars: parserConfig.localVars, + context: parserConfig.localVars && new Context(null, null, false), + indented: basecolumn || 0 + }; + if (parserConfig.globalVars && typeof parserConfig.globalVars == "object") + state.globalVars = parserConfig.globalVars; + return state; + }, + + token: function(stream, state) { + if (stream.sol()) { + if (!state.lexical.hasOwnProperty("align")) + state.lexical.align = false; + state.indented = stream.indentation(); + findFatArrow(stream, state); + } + if (state.tokenize != tokenComment && stream.eatSpace()) return null; + var style = state.tokenize(stream, state); + if (type == "comment") return style; + state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type; + return parseJS(state, style, type, content, stream); + }, + + indent: function(state, textAfter) { + if (state.tokenize == tokenComment || state.tokenize == tokenQuasi) return CodeMirror.Pass; + if (state.tokenize != tokenBase) return 0; + var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top + // Kludge to prevent 'maybelse' from blocking lexical scope pops + if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) { + var c = state.cc[i]; + if (c == poplex) lexical = lexical.prev; + else if (c != maybeelse) break; + } + while ((lexical.type == "stat" || lexical.type == "form") && + (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) && + (top == maybeoperatorComma || top == maybeoperatorNoComma) && + !/^[,\.=+\-*:?[\(]/.test(textAfter)))) + lexical = lexical.prev; + if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat") + lexical = lexical.prev; + var type = lexical.type, closing = firstChar == type; + + if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info.length + 1 : 0); + else if (type == "form" && firstChar == "{") return lexical.indented; + else if (type == "form") return lexical.indented + indentUnit; + else if (type == "stat") + return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0); + else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false) + return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit); + else if (lexical.align) return lexical.column + (closing ? 0 : 1); + else return lexical.indented + (closing ? 0 : indentUnit); + }, + + electricInput: /^\s*(?:case .*?:|default:|\{|\})$/, + blockCommentStart: jsonMode ? null : "/*", + blockCommentEnd: jsonMode ? null : "*/", + blockCommentContinue: jsonMode ? null : " * ", + lineComment: jsonMode ? null : "//", + fold: "brace", + closeBrackets: "()[]{}''\"\"``", + + helperType: jsonMode ? "json" : "javascript", + jsonldMode: jsonldMode, + jsonMode: jsonMode, + + expressionAllowed: expressionAllowed, + + skipExpression: function(state) { + var top = state.cc[state.cc.length - 1] + if (top == expression || top == expressionNoComma) state.cc.pop() + } + }; +}); + +CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/); + +CodeMirror.defineMIME("text/javascript", "javascript"); +CodeMirror.defineMIME("text/ecmascript", "javascript"); +CodeMirror.defineMIME("application/javascript", "javascript"); +CodeMirror.defineMIME("application/x-javascript", "javascript"); +CodeMirror.defineMIME("application/ecmascript", "javascript"); +CodeMirror.defineMIME("application/json", {name: "javascript", json: true}); +CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true}); +CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true}); +CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true }); +CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true }); +}); + + +/* +file none +*/ +/*jslint-enable*/ diff --git a/asset-font-daley-bold.woff2 b/asset-font-daley-bold.woff2 new file mode 100644 index 000000000..783bdd697 Binary files /dev/null and b/asset-font-daley-bold.woff2 differ diff --git a/asset-font-programma-bold.woff2 b/asset-font-programma-bold.woff2 new file mode 100644 index 000000000..dfb5a9753 Binary files /dev/null and b/asset-font-programma-bold.woff2 differ diff --git a/asset-image-folder-open-solid.svg b/asset-image-folder-open-solid.svg new file mode 100644 index 000000000..99d403c81 --- /dev/null +++ b/asset-image-folder-open-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/asset-image-github-brands.svg b/asset-image-github-brands.svg new file mode 100644 index 000000000..7870c06dc --- /dev/null +++ b/asset-image-github-brands.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/asset-image-jslint-512.svg b/asset-image-jslint-512.svg new file mode 100644 index 000000000..61872c4ae --- /dev/null +++ b/asset-image-jslint-512.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + diff --git a/asset-image-jslint-vim-plugin.png b/asset-image-jslint-vim-plugin.png new file mode 100644 index 000000000..d0f652305 Binary files /dev/null and b/asset-image-jslint-vim-plugin.png differ diff --git a/asset-image-json-160.gif b/asset-image-json-160.gif new file mode 100644 index 000000000..3bb55c8dd Binary files /dev/null and b/asset-image-json-160.gif differ diff --git a/asset-image-logo-512.svg b/asset-image-logo-512.svg new file mode 100644 index 000000000..61872c4ae --- /dev/null +++ b/asset-image-logo-512.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + diff --git a/asset-image-window-maximize-regular.svg b/asset-image-window-maximize-regular.svg new file mode 100644 index 000000000..7b7e5e166 --- /dev/null +++ b/asset-image-window-maximize-regular.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/asset_codemirror_rollup.js b/asset_codemirror_rollup.js new file mode 100644 index 000000000..9b93a0427 --- /dev/null +++ b/asset_codemirror_rollup.js @@ -0,0 +1,11481 @@ +/*jslint-disable*/ +/* +shRollupFetch +{ + "fetchList": [ + { + "comment": true, + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/LICENSE" + }, + { + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/codemirror.js", + "url2": "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.10/codemirror.js" + }, + { + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/addon/edit/matchbrackets.js" + }, + { + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/addon/edit/trailingspace.js" + }, + { + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/addon/lint/lint.js" + }, + { + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/addon/selection/active-line.js" + }, + { + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/mode/javascript/javascript.js" + } + ], + "replaceList": [] +} +*/ + + +/* +repo https://github.com/codemirror/codemirror5/tree/5.65.10 +committed 2022-11-20T15:35:33Z +*/ + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/LICENSE +*/ +/* +MIT License + +Copyright (C) 2017 by Marijn Haverbeke and others + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/codemirror.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +// This is CodeMirror (https://codemirror.net/5), a code editor +// implemented in JavaScript on top of the browser's DOM. +// +// You can find some technical background for some of the code below +// at http://marijnhaverbeke.nl/blog/#cm-internals . + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, global.CodeMirror = factory()); +}(this, (function () { 'use strict'; + + // Kludges for bugs and behavior differences that can't be feature + // detected are enabled based on userAgent etc sniffing. + var userAgent = navigator.userAgent; + var platform = navigator.platform; + + var gecko = /gecko\/\d/i.test(userAgent); + var ie_upto10 = /MSIE \d/.test(userAgent); + var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent); + var edge = /Edge\/(\d+)/.exec(userAgent); + var ie = ie_upto10 || ie_11up || edge; + var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]); + var webkit = !edge && /WebKit\//.test(userAgent); + var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent); + var chrome = !edge && /Chrome\/(\d+)/.exec(userAgent); + var chrome_version = chrome && +chrome[1]; + var presto = /Opera\//.test(userAgent); + var safari = /Apple Computer/.test(navigator.vendor); + var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent); + var phantom = /PhantomJS/.test(userAgent); + + var ios = safari && (/Mobile\/\w+/.test(userAgent) || navigator.maxTouchPoints > 2); + var android = /Android/.test(userAgent); + // This is woefully incomplete. Suggestions for alternative methods welcome. + var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent); + var mac = ios || /Mac/.test(platform); + var chromeOS = /\bCrOS\b/.test(userAgent); + var windows = /win/i.test(platform); + + var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/); + if (presto_version) { presto_version = Number(presto_version[1]); } + if (presto_version && presto_version >= 15) { presto = false; webkit = true; } + // Some browsers use the wrong event properties to signal cmd/ctrl on OS X + var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11)); + var captureRightClick = gecko || (ie && ie_version >= 9); + + function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") } + + var rmClass = function(node, cls) { + var current = node.className; + var match = classTest(cls).exec(current); + if (match) { + var after = current.slice(match.index + match[0].length); + node.className = current.slice(0, match.index) + (after ? match[1] + after : ""); + } + }; + + function removeChildren(e) { + for (var count = e.childNodes.length; count > 0; --count) + { e.removeChild(e.firstChild); } + return e + } + + function removeChildrenAndAdd(parent, e) { + return removeChildren(parent).appendChild(e) + } + + function elt(tag, content, className, style) { + var e = document.createElement(tag); + if (className) { e.className = className; } + if (style) { e.style.cssText = style; } + if (typeof content == "string") { e.appendChild(document.createTextNode(content)); } + else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } } + return e + } + // wrapper for elt, which removes the elt from the accessibility tree + function eltP(tag, content, className, style) { + var e = elt(tag, content, className, style); + e.setAttribute("role", "presentation"); + return e + } + + var range; + if (document.createRange) { range = function(node, start, end, endNode) { + var r = document.createRange(); + r.setEnd(endNode || node, end); + r.setStart(node, start); + return r + }; } + else { range = function(node, start, end) { + var r = document.body.createTextRange(); + try { r.moveToElementText(node.parentNode); } + catch(e) { return r } + r.collapse(true); + r.moveEnd("character", end); + r.moveStart("character", start); + return r + }; } + + function contains(parent, child) { + if (child.nodeType == 3) // Android browser always returns false when child is a textnode + { child = child.parentNode; } + if (parent.contains) + { return parent.contains(child) } + do { + if (child.nodeType == 11) { child = child.host; } + if (child == parent) { return true } + } while (child = child.parentNode) + } + + function activeElt(doc) { + // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement. + // IE < 10 will throw when accessed while the page is loading or in an iframe. + // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable. + var activeElement; + try { + activeElement = doc.activeElement; + } catch(e) { + activeElement = doc.body || null; + } + while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement) + { activeElement = activeElement.shadowRoot.activeElement; } + return activeElement + } + + function addClass(node, cls) { + var current = node.className; + if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; } + } + function joinClasses(a, b) { + var as = a.split(" "); + for (var i = 0; i < as.length; i++) + { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } } + return b + } + + var selectInput = function(node) { node.select(); }; + if (ios) // Mobile Safari apparently has a bug where select() is broken. + { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; } + else if (ie) // Suppress mysterious IE10 errors + { selectInput = function(node) { try { node.select(); } catch(_e) {} }; } + + function doc(cm) { return cm.display.wrapper.ownerDocument } + + function win(cm) { return doc(cm).defaultView } + + function bind(f) { + var args = Array.prototype.slice.call(arguments, 1); + return function(){return f.apply(null, args)} + } + + function copyObj(obj, target, overwrite) { + if (!target) { target = {}; } + for (var prop in obj) + { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop))) + { target[prop] = obj[prop]; } } + return target + } + + // Counts the column offset in a string, taking tabs into account. + // Used mostly to find indentation. + function countColumn(string, end, tabSize, startIndex, startValue) { + if (end == null) { + end = string.search(/[^\s\u00a0]/); + if (end == -1) { end = string.length; } + } + for (var i = startIndex || 0, n = startValue || 0;;) { + var nextTab = string.indexOf("\t", i); + if (nextTab < 0 || nextTab >= end) + { return n + (end - i) } + n += nextTab - i; + n += tabSize - (n % tabSize); + i = nextTab + 1; + } + } + + var Delayed = function() { + this.id = null; + this.f = null; + this.time = 0; + this.handler = bind(this.onTimeout, this); + }; + Delayed.prototype.onTimeout = function (self) { + self.id = 0; + if (self.time <= +new Date) { + self.f(); + } else { + setTimeout(self.handler, self.time - +new Date); + } + }; + Delayed.prototype.set = function (ms, f) { + this.f = f; + var time = +new Date + ms; + if (!this.id || time < this.time) { + clearTimeout(this.id); + this.id = setTimeout(this.handler, ms); + this.time = time; + } + }; + + function indexOf(array, elt) { + for (var i = 0; i < array.length; ++i) + { if (array[i] == elt) { return i } } + return -1 + } + + // Number of pixels added to scroller and sizer to hide scrollbar + var scrollerGap = 50; + + // Returned or thrown by various protocols to signal 'I'm not + // handling this'. + var Pass = {toString: function(){return "CodeMirror.Pass"}}; + + // Reused option objects for setSelection & friends + var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"}; + + // The inverse of countColumn -- find the offset that corresponds to + // a particular column. + function findColumn(string, goal, tabSize) { + for (var pos = 0, col = 0;;) { + var nextTab = string.indexOf("\t", pos); + if (nextTab == -1) { nextTab = string.length; } + var skipped = nextTab - pos; + if (nextTab == string.length || col + skipped >= goal) + { return pos + Math.min(skipped, goal - col) } + col += nextTab - pos; + col += tabSize - (col % tabSize); + pos = nextTab + 1; + if (col >= goal) { return pos } + } + } + + var spaceStrs = [""]; + function spaceStr(n) { + while (spaceStrs.length <= n) + { spaceStrs.push(lst(spaceStrs) + " "); } + return spaceStrs[n] + } + + function lst(arr) { return arr[arr.length-1] } + + function map(array, f) { + var out = []; + for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); } + return out + } + + function insertSorted(array, value, score) { + var pos = 0, priority = score(value); + while (pos < array.length && score(array[pos]) <= priority) { pos++; } + array.splice(pos, 0, value); + } + + function nothing() {} + + function createObj(base, props) { + var inst; + if (Object.create) { + inst = Object.create(base); + } else { + nothing.prototype = base; + inst = new nothing(); + } + if (props) { copyObj(props, inst); } + return inst + } + + var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; + function isWordCharBasic(ch) { + return /\w/.test(ch) || ch > "\x80" && + (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)) + } + function isWordChar(ch, helper) { + if (!helper) { return isWordCharBasic(ch) } + if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true } + return helper.test(ch) + } + + function isEmpty(obj) { + for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } } + return true + } + + // Extending unicode characters. A series of a non-extending char + + // any number of extending chars is treated as a single unit as far + // as editing and measuring is concerned. This is not fully correct, + // since some scripts/fonts/browsers also treat other configurations + // of code points as a group. + var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/; + function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) } + + // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range. + function skipExtendingChars(str, pos, dir) { + while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; } + return pos + } + + // Returns the value from the range [`from`; `to`] that satisfies + // `pred` and is closest to `from`. Assumes that at least `to` + // satisfies `pred`. Supports `from` being greater than `to`. + function findFirst(pred, from, to) { + // At any point we are certain `to` satisfies `pred`, don't know + // whether `from` does. + var dir = from > to ? -1 : 1; + for (;;) { + if (from == to) { return from } + var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF); + if (mid == from) { return pred(mid) ? from : to } + if (pred(mid)) { to = mid; } + else { from = mid + dir; } + } + } + + // BIDI HELPERS + + function iterateBidiSections(order, from, to, f) { + if (!order) { return f(from, to, "ltr", 0) } + var found = false; + for (var i = 0; i < order.length; ++i) { + var part = order[i]; + if (part.from < to && part.to > from || from == to && part.to == from) { + f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i); + found = true; + } + } + if (!found) { f(from, to, "ltr"); } + } + + var bidiOther = null; + function getBidiPartAt(order, ch, sticky) { + var found; + bidiOther = null; + for (var i = 0; i < order.length; ++i) { + var cur = order[i]; + if (cur.from < ch && cur.to > ch) { return i } + if (cur.to == ch) { + if (cur.from != cur.to && sticky == "before") { found = i; } + else { bidiOther = i; } + } + if (cur.from == ch) { + if (cur.from != cur.to && sticky != "before") { found = i; } + else { bidiOther = i; } + } + } + return found != null ? found : bidiOther + } + + // Bidirectional ordering algorithm + // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm + // that this (partially) implements. + + // One-char codes used for character types: + // L (L): Left-to-Right + // R (R): Right-to-Left + // r (AL): Right-to-Left Arabic + // 1 (EN): European Number + // + (ES): European Number Separator + // % (ET): European Number Terminator + // n (AN): Arabic Number + // , (CS): Common Number Separator + // m (NSM): Non-Spacing Mark + // b (BN): Boundary Neutral + // s (B): Paragraph Separator + // t (S): Segment Separator + // w (WS): Whitespace + // N (ON): Other Neutrals + + // Returns null if characters are ordered as they appear + // (left-to-right), or an array of sections ({from, to, level} + // objects) in the order in which they occur visually. + var bidiOrdering = (function() { + // Character types for codepoints 0 to 0xff + var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"; + // Character types for codepoints 0x600 to 0x6f9 + var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111"; + function charType(code) { + if (code <= 0xf7) { return lowTypes.charAt(code) } + else if (0x590 <= code && code <= 0x5f4) { return "R" } + else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) } + else if (0x6ee <= code && code <= 0x8ac) { return "r" } + else if (0x2000 <= code && code <= 0x200b) { return "w" } + else if (code == 0x200c) { return "b" } + else { return "L" } + } + + var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/; + var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/; + + function BidiSpan(level, from, to) { + this.level = level; + this.from = from; this.to = to; + } + + return function(str, direction) { + var outerType = direction == "ltr" ? "L" : "R"; + + if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false } + var len = str.length, types = []; + for (var i = 0; i < len; ++i) + { types.push(charType(str.charCodeAt(i))); } + + // W1. Examine each non-spacing mark (NSM) in the level run, and + // change the type of the NSM to the type of the previous + // character. If the NSM is at the start of the level run, it will + // get the type of sor. + for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) { + var type = types[i$1]; + if (type == "m") { types[i$1] = prev; } + else { prev = type; } + } + + // W2. Search backwards from each instance of a European number + // until the first strong type (R, L, AL, or sor) is found. If an + // AL is found, change the type of the European number to Arabic + // number. + // W3. Change all ALs to R. + for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) { + var type$1 = types[i$2]; + if (type$1 == "1" && cur == "r") { types[i$2] = "n"; } + else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } } + } + + // W4. A single European separator between two European numbers + // changes to a European number. A single common separator between + // two numbers of the same type changes to that type. + for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) { + var type$2 = types[i$3]; + if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; } + else if (type$2 == "," && prev$1 == types[i$3+1] && + (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; } + prev$1 = type$2; + } + + // W5. A sequence of European terminators adjacent to European + // numbers changes to all European numbers. + // W6. Otherwise, separators and terminators change to Other + // Neutral. + for (var i$4 = 0; i$4 < len; ++i$4) { + var type$3 = types[i$4]; + if (type$3 == ",") { types[i$4] = "N"; } + else if (type$3 == "%") { + var end = (void 0); + for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {} + var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"; + for (var j = i$4; j < end; ++j) { types[j] = replace; } + i$4 = end - 1; + } + } + + // W7. Search backwards from each instance of a European number + // until the first strong type (R, L, or sor) is found. If an L is + // found, then change the type of the European number to L. + for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) { + var type$4 = types[i$5]; + if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; } + else if (isStrong.test(type$4)) { cur$1 = type$4; } + } + + // N1. A sequence of neutrals takes the direction of the + // surrounding strong text if the text on both sides has the same + // direction. European and Arabic numbers act as if they were R in + // terms of their influence on neutrals. Start-of-level-run (sor) + // and end-of-level-run (eor) are used at level run boundaries. + // N2. Any remaining neutrals take the embedding direction. + for (var i$6 = 0; i$6 < len; ++i$6) { + if (isNeutral.test(types[i$6])) { + var end$1 = (void 0); + for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {} + var before = (i$6 ? types[i$6-1] : outerType) == "L"; + var after = (end$1 < len ? types[end$1] : outerType) == "L"; + var replace$1 = before == after ? (before ? "L" : "R") : outerType; + for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; } + i$6 = end$1 - 1; + } + } + + // Here we depart from the documented algorithm, in order to avoid + // building up an actual levels array. Since there are only three + // levels (0, 1, 2) in an implementation that doesn't take + // explicit embedding into account, we can build up the order on + // the fly, without following the level-based algorithm. + var order = [], m; + for (var i$7 = 0; i$7 < len;) { + if (countsAsLeft.test(types[i$7])) { + var start = i$7; + for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {} + order.push(new BidiSpan(0, start, i$7)); + } else { + var pos = i$7, at = order.length, isRTL = direction == "rtl" ? 1 : 0; + for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {} + for (var j$2 = pos; j$2 < i$7;) { + if (countsAsNum.test(types[j$2])) { + if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); at += isRTL; } + var nstart = j$2; + for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {} + order.splice(at, 0, new BidiSpan(2, nstart, j$2)); + at += isRTL; + pos = j$2; + } else { ++j$2; } + } + if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); } + } + } + if (direction == "ltr") { + if (order[0].level == 1 && (m = str.match(/^\s+/))) { + order[0].from = m[0].length; + order.unshift(new BidiSpan(0, 0, m[0].length)); + } + if (lst(order).level == 1 && (m = str.match(/\s+$/))) { + lst(order).to -= m[0].length; + order.push(new BidiSpan(0, len - m[0].length, len)); + } + } + + return direction == "rtl" ? order.reverse() : order + } + })(); + + // Get the bidi ordering for the given line (and cache it). Returns + // false for lines that are fully left-to-right, and an array of + // BidiSpan objects otherwise. + function getOrder(line, direction) { + var order = line.order; + if (order == null) { order = line.order = bidiOrdering(line.text, direction); } + return order + } + + // EVENT HANDLING + + // Lightweight event framework. on/off also work on DOM nodes, + // registering native DOM handlers. + + var noHandlers = []; + + var on = function(emitter, type, f) { + if (emitter.addEventListener) { + emitter.addEventListener(type, f, false); + } else if (emitter.attachEvent) { + emitter.attachEvent("on" + type, f); + } else { + var map = emitter._handlers || (emitter._handlers = {}); + map[type] = (map[type] || noHandlers).concat(f); + } + }; + + function getHandlers(emitter, type) { + return emitter._handlers && emitter._handlers[type] || noHandlers + } + + function off(emitter, type, f) { + if (emitter.removeEventListener) { + emitter.removeEventListener(type, f, false); + } else if (emitter.detachEvent) { + emitter.detachEvent("on" + type, f); + } else { + var map = emitter._handlers, arr = map && map[type]; + if (arr) { + var index = indexOf(arr, f); + if (index > -1) + { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)); } + } + } + } + + function signal(emitter, type /*, values...*/) { + var handlers = getHandlers(emitter, type); + if (!handlers.length) { return } + var args = Array.prototype.slice.call(arguments, 2); + for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); } + } + + // The DOM events that CodeMirror handles can be overridden by + // registering a (non-DOM) handler on the editor for the event name, + // and preventDefault-ing the event in that handler. + function signalDOMEvent(cm, e, override) { + if (typeof e == "string") + { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; } + signal(cm, override || e.type, cm, e); + return e_defaultPrevented(e) || e.codemirrorIgnore + } + + function signalCursorActivity(cm) { + var arr = cm._handlers && cm._handlers.cursorActivity; + if (!arr) { return } + var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []); + for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1) + { set.push(arr[i]); } } + } + + function hasHandler(emitter, type) { + return getHandlers(emitter, type).length > 0 + } + + // Add on and off methods to a constructor's prototype, to make + // registering events on such objects more convenient. + function eventMixin(ctor) { + ctor.prototype.on = function(type, f) {on(this, type, f);}; + ctor.prototype.off = function(type, f) {off(this, type, f);}; + } + + // Due to the fact that we still support jurassic IE versions, some + // compatibility wrappers are needed. + + function e_preventDefault(e) { + if (e.preventDefault) { e.preventDefault(); } + else { e.returnValue = false; } + } + function e_stopPropagation(e) { + if (e.stopPropagation) { e.stopPropagation(); } + else { e.cancelBubble = true; } + } + function e_defaultPrevented(e) { + return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false + } + function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);} + + function e_target(e) {return e.target || e.srcElement} + function e_button(e) { + var b = e.which; + if (b == null) { + if (e.button & 1) { b = 1; } + else if (e.button & 2) { b = 3; } + else if (e.button & 4) { b = 2; } + } + if (mac && e.ctrlKey && b == 1) { b = 3; } + return b + } + + // Detect drag-and-drop + var dragAndDrop = function() { + // There is *some* kind of drag-and-drop support in IE6-8, but I + // couldn't get it to work yet. + if (ie && ie_version < 9) { return false } + var div = elt('div'); + return "draggable" in div || "dragDrop" in div + }(); + + var zwspSupported; + function zeroWidthElement(measure) { + if (zwspSupported == null) { + var test = elt("span", "\u200b"); + removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")])); + if (measure.firstChild.offsetHeight != 0) + { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); } + } + var node = zwspSupported ? elt("span", "\u200b") : + elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px"); + node.setAttribute("cm-text", ""); + return node + } + + // Feature-detect IE's crummy client rect reporting for bidi text + var badBidiRects; + function hasBadBidiRects(measure) { + if (badBidiRects != null) { return badBidiRects } + var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA")); + var r0 = range(txt, 0, 1).getBoundingClientRect(); + var r1 = range(txt, 1, 2).getBoundingClientRect(); + removeChildren(measure); + if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780) + return badBidiRects = (r1.right - r0.right < 3) + } + + // See if "".split is the broken IE version, if so, provide an + // alternative way to split lines. + var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) { + var pos = 0, result = [], l = string.length; + while (pos <= l) { + var nl = string.indexOf("\n", pos); + if (nl == -1) { nl = string.length; } + var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl); + var rt = line.indexOf("\r"); + if (rt != -1) { + result.push(line.slice(0, rt)); + pos += rt + 1; + } else { + result.push(line); + pos = nl + 1; + } + } + return result + } : function (string) { return string.split(/\r\n?|\n/); }; + + var hasSelection = window.getSelection ? function (te) { + try { return te.selectionStart != te.selectionEnd } + catch(e) { return false } + } : function (te) { + var range; + try {range = te.ownerDocument.selection.createRange();} + catch(e) {} + if (!range || range.parentElement() != te) { return false } + return range.compareEndPoints("StartToEnd", range) != 0 + }; + + var hasCopyEvent = (function () { + var e = elt("div"); + if ("oncopy" in e) { return true } + e.setAttribute("oncopy", "return;"); + return typeof e.oncopy == "function" + })(); + + var badZoomedRects = null; + function hasBadZoomedRects(measure) { + if (badZoomedRects != null) { return badZoomedRects } + var node = removeChildrenAndAdd(measure, elt("span", "x")); + var normal = node.getBoundingClientRect(); + var fromRange = range(node, 0, 1).getBoundingClientRect(); + return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1 + } + + // Known modes, by name and by MIME + var modes = {}, mimeModes = {}; + + // Extra arguments are stored as the mode's dependencies, which is + // used by (legacy) mechanisms like loadmode.js to automatically + // load a mode. (Preferred mechanism is the require/define calls.) + function defineMode(name, mode) { + if (arguments.length > 2) + { mode.dependencies = Array.prototype.slice.call(arguments, 2); } + modes[name] = mode; + } + + function defineMIME(mime, spec) { + mimeModes[mime] = spec; + } + + // Given a MIME type, a {name, ...options} config object, or a name + // string, return a mode config object. + function resolveMode(spec) { + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { + spec = mimeModes[spec]; + } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { + var found = mimeModes[spec.name]; + if (typeof found == "string") { found = {name: found}; } + spec = createObj(found, spec); + spec.name = found.name; + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { + return resolveMode("application/xml") + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) { + return resolveMode("application/json") + } + if (typeof spec == "string") { return {name: spec} } + else { return spec || {name: "null"} } + } + + // Given a mode spec (anything that resolveMode accepts), find and + // initialize an actual mode object. + function getMode(options, spec) { + spec = resolveMode(spec); + var mfactory = modes[spec.name]; + if (!mfactory) { return getMode(options, "text/plain") } + var modeObj = mfactory(options, spec); + if (modeExtensions.hasOwnProperty(spec.name)) { + var exts = modeExtensions[spec.name]; + for (var prop in exts) { + if (!exts.hasOwnProperty(prop)) { continue } + if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; } + modeObj[prop] = exts[prop]; + } + } + modeObj.name = spec.name; + if (spec.helperType) { modeObj.helperType = spec.helperType; } + if (spec.modeProps) { for (var prop$1 in spec.modeProps) + { modeObj[prop$1] = spec.modeProps[prop$1]; } } + + return modeObj + } + + // This can be used to attach properties to mode objects from + // outside the actual mode definition. + var modeExtensions = {}; + function extendMode(mode, properties) { + var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); + copyObj(properties, exts); + } + + function copyState(mode, state) { + if (state === true) { return state } + if (mode.copyState) { return mode.copyState(state) } + var nstate = {}; + for (var n in state) { + var val = state[n]; + if (val instanceof Array) { val = val.concat([]); } + nstate[n] = val; + } + return nstate + } + + // Given a mode and a state (for that mode), find the inner mode and + // state at the position that the state refers to. + function innerMode(mode, state) { + var info; + while (mode.innerMode) { + info = mode.innerMode(state); + if (!info || info.mode == mode) { break } + state = info.state; + mode = info.mode; + } + return info || {mode: mode, state: state} + } + + function startState(mode, a1, a2) { + return mode.startState ? mode.startState(a1, a2) : true + } + + // STRING STREAM + + // Fed to the mode parsers, provides helper functions to make + // parsers more succinct. + + var StringStream = function(string, tabSize, lineOracle) { + this.pos = this.start = 0; + this.string = string; + this.tabSize = tabSize || 8; + this.lastColumnPos = this.lastColumnValue = 0; + this.lineStart = 0; + this.lineOracle = lineOracle; + }; + + StringStream.prototype.eol = function () {return this.pos >= this.string.length}; + StringStream.prototype.sol = function () {return this.pos == this.lineStart}; + StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined}; + StringStream.prototype.next = function () { + if (this.pos < this.string.length) + { return this.string.charAt(this.pos++) } + }; + StringStream.prototype.eat = function (match) { + var ch = this.string.charAt(this.pos); + var ok; + if (typeof match == "string") { ok = ch == match; } + else { ok = ch && (match.test ? match.test(ch) : match(ch)); } + if (ok) {++this.pos; return ch} + }; + StringStream.prototype.eatWhile = function (match) { + var start = this.pos; + while (this.eat(match)){} + return this.pos > start + }; + StringStream.prototype.eatSpace = function () { + var start = this.pos; + while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; } + return this.pos > start + }; + StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;}; + StringStream.prototype.skipTo = function (ch) { + var found = this.string.indexOf(ch, this.pos); + if (found > -1) {this.pos = found; return true} + }; + StringStream.prototype.backUp = function (n) {this.pos -= n;}; + StringStream.prototype.column = function () { + if (this.lastColumnPos < this.start) { + this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue); + this.lastColumnPos = this.start; + } + return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) + }; + StringStream.prototype.indentation = function () { + return countColumn(this.string, null, this.tabSize) - + (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) + }; + StringStream.prototype.match = function (pattern, consume, caseInsensitive) { + if (typeof pattern == "string") { + var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; }; + var substr = this.string.substr(this.pos, pattern.length); + if (cased(substr) == cased(pattern)) { + if (consume !== false) { this.pos += pattern.length; } + return true + } + } else { + var match = this.string.slice(this.pos).match(pattern); + if (match && match.index > 0) { return null } + if (match && consume !== false) { this.pos += match[0].length; } + return match + } + }; + StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)}; + StringStream.prototype.hideFirstChars = function (n, inner) { + this.lineStart += n; + try { return inner() } + finally { this.lineStart -= n; } + }; + StringStream.prototype.lookAhead = function (n) { + var oracle = this.lineOracle; + return oracle && oracle.lookAhead(n) + }; + StringStream.prototype.baseToken = function () { + var oracle = this.lineOracle; + return oracle && oracle.baseToken(this.pos) + }; + + // Find the line object corresponding to the given line number. + function getLine(doc, n) { + n -= doc.first; + if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") } + var chunk = doc; + while (!chunk.lines) { + for (var i = 0;; ++i) { + var child = chunk.children[i], sz = child.chunkSize(); + if (n < sz) { chunk = child; break } + n -= sz; + } + } + return chunk.lines[n] + } + + // Get the part of a document between two positions, as an array of + // strings. + function getBetween(doc, start, end) { + var out = [], n = start.line; + doc.iter(start.line, end.line + 1, function (line) { + var text = line.text; + if (n == end.line) { text = text.slice(0, end.ch); } + if (n == start.line) { text = text.slice(start.ch); } + out.push(text); + ++n; + }); + return out + } + // Get the lines between from and to, as array of strings. + function getLines(doc, from, to) { + var out = []; + doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value + return out + } + + // Update the height of a line, propagating the height change + // upwards to parent nodes. + function updateLineHeight(line, height) { + var diff = height - line.height; + if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } } + } + + // Given a line object, find its line number by walking up through + // its parent links. + function lineNo(line) { + if (line.parent == null) { return null } + var cur = line.parent, no = indexOf(cur.lines, line); + for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { + for (var i = 0;; ++i) { + if (chunk.children[i] == cur) { break } + no += chunk.children[i].chunkSize(); + } + } + return no + cur.first + } + + // Find the line at the given vertical position, using the height + // information in the document tree. + function lineAtHeight(chunk, h) { + var n = chunk.first; + outer: do { + for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) { + var child = chunk.children[i$1], ch = child.height; + if (h < ch) { chunk = child; continue outer } + h -= ch; + n += child.chunkSize(); + } + return n + } while (!chunk.lines) + var i = 0; + for (; i < chunk.lines.length; ++i) { + var line = chunk.lines[i], lh = line.height; + if (h < lh) { break } + h -= lh; + } + return n + i + } + + function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size} + + function lineNumberFor(options, i) { + return String(options.lineNumberFormatter(i + options.firstLineNumber)) + } + + // A Pos instance represents a position within the text. + function Pos(line, ch, sticky) { + if ( sticky === void 0 ) sticky = null; + + if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) } + this.line = line; + this.ch = ch; + this.sticky = sticky; + } + + // Compare two positions, return 0 if they are the same, a negative + // number when a is less, and a positive number otherwise. + function cmp(a, b) { return a.line - b.line || a.ch - b.ch } + + function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 } + + function copyPos(x) {return Pos(x.line, x.ch)} + function maxPos(a, b) { return cmp(a, b) < 0 ? b : a } + function minPos(a, b) { return cmp(a, b) < 0 ? a : b } + + // Most of the external API clips given positions to make sure they + // actually exist within the document. + function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))} + function clipPos(doc, pos) { + if (pos.line < doc.first) { return Pos(doc.first, 0) } + var last = doc.first + doc.size - 1; + if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) } + return clipToLen(pos, getLine(doc, pos.line).text.length) + } + function clipToLen(pos, linelen) { + var ch = pos.ch; + if (ch == null || ch > linelen) { return Pos(pos.line, linelen) } + else if (ch < 0) { return Pos(pos.line, 0) } + else { return pos } + } + function clipPosArray(doc, array) { + var out = []; + for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); } + return out + } + + var SavedContext = function(state, lookAhead) { + this.state = state; + this.lookAhead = lookAhead; + }; + + var Context = function(doc, state, line, lookAhead) { + this.state = state; + this.doc = doc; + this.line = line; + this.maxLookAhead = lookAhead || 0; + this.baseTokens = null; + this.baseTokenPos = 1; + }; + + Context.prototype.lookAhead = function (n) { + var line = this.doc.getLine(this.line + n); + if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; } + return line + }; + + Context.prototype.baseToken = function (n) { + if (!this.baseTokens) { return null } + while (this.baseTokens[this.baseTokenPos] <= n) + { this.baseTokenPos += 2; } + var type = this.baseTokens[this.baseTokenPos + 1]; + return {type: type && type.replace(/( |^)overlay .*/, ""), + size: this.baseTokens[this.baseTokenPos] - n} + }; + + Context.prototype.nextLine = function () { + this.line++; + if (this.maxLookAhead > 0) { this.maxLookAhead--; } + }; + + Context.fromSaved = function (doc, saved, line) { + if (saved instanceof SavedContext) + { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) } + else + { return new Context(doc, copyState(doc.mode, saved), line) } + }; + + Context.prototype.save = function (copy) { + var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state; + return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state + }; + + + // Compute a style array (an array starting with a mode generation + // -- for invalidation -- followed by pairs of end positions and + // style strings), which is used to highlight the tokens on the + // line. + function highlightLine(cm, line, context, forceToEnd) { + // A styles array always starts with a number identifying the + // mode/overlays that it is based on (for easy invalidation). + var st = [cm.state.modeGen], lineClasses = {}; + // Compute the base array of styles + runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); }, + lineClasses, forceToEnd); + var state = context.state; + + // Run overlays, adjust style array. + var loop = function ( o ) { + context.baseTokens = st; + var overlay = cm.state.overlays[o], i = 1, at = 0; + context.state = true; + runMode(cm, line.text, overlay.mode, context, function (end, style) { + var start = i; + // Ensure there's a token end at the current position, and that i points at it + while (at < end) { + var i_end = st[i]; + if (i_end > end) + { st.splice(i, 1, end, st[i+1], i_end); } + i += 2; + at = Math.min(end, i_end); + } + if (!style) { return } + if (overlay.opaque) { + st.splice(start, i - start, end, "overlay " + style); + i = start + 2; + } else { + for (; start < i; start += 2) { + var cur = st[start+1]; + st[start+1] = (cur ? cur + " " : "") + "overlay " + style; + } + } + }, lineClasses); + context.state = state; + context.baseTokens = null; + context.baseTokenPos = 1; + }; + + for (var o = 0; o < cm.state.overlays.length; ++o) loop( o ); + + return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null} + } + + function getLineStyles(cm, line, updateFrontier) { + if (!line.styles || line.styles[0] != cm.state.modeGen) { + var context = getContextBefore(cm, lineNo(line)); + var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state); + var result = highlightLine(cm, line, context); + if (resetState) { context.state = resetState; } + line.stateAfter = context.save(!resetState); + line.styles = result.styles; + if (result.classes) { line.styleClasses = result.classes; } + else if (line.styleClasses) { line.styleClasses = null; } + if (updateFrontier === cm.doc.highlightFrontier) + { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); } + } + return line.styles + } + + function getContextBefore(cm, n, precise) { + var doc = cm.doc, display = cm.display; + if (!doc.mode.startState) { return new Context(doc, true, n) } + var start = findStartLine(cm, n, precise); + var saved = start > doc.first && getLine(doc, start - 1).stateAfter; + var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start); + + doc.iter(start, n, function (line) { + processLine(cm, line.text, context); + var pos = context.line; + line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null; + context.nextLine(); + }); + if (precise) { doc.modeFrontier = context.line; } + return context + } + + // Lightweight form of highlight -- proceed over this line and + // update state, but don't save a style array. Used for lines that + // aren't currently visible. + function processLine(cm, text, context, startAt) { + var mode = cm.doc.mode; + var stream = new StringStream(text, cm.options.tabSize, context); + stream.start = stream.pos = startAt || 0; + if (text == "") { callBlankLine(mode, context.state); } + while (!stream.eol()) { + readToken(mode, stream, context.state); + stream.start = stream.pos; + } + } + + function callBlankLine(mode, state) { + if (mode.blankLine) { return mode.blankLine(state) } + if (!mode.innerMode) { return } + var inner = innerMode(mode, state); + if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) } + } + + function readToken(mode, stream, state, inner) { + for (var i = 0; i < 10; i++) { + if (inner) { inner[0] = innerMode(mode, state).mode; } + var style = mode.token(stream, state); + if (stream.pos > stream.start) { return style } + } + throw new Error("Mode " + mode.name + " failed to advance stream.") + } + + var Token = function(stream, type, state) { + this.start = stream.start; this.end = stream.pos; + this.string = stream.current(); + this.type = type || null; + this.state = state; + }; + + // Utility for getTokenAt and getLineTokens + function takeToken(cm, pos, precise, asArray) { + var doc = cm.doc, mode = doc.mode, style; + pos = clipPos(doc, pos); + var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise); + var stream = new StringStream(line.text, cm.options.tabSize, context), tokens; + if (asArray) { tokens = []; } + while ((asArray || stream.pos < pos.ch) && !stream.eol()) { + stream.start = stream.pos; + style = readToken(mode, stream, context.state); + if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); } + } + return asArray ? tokens : new Token(stream, style, context.state) + } + + function extractLineClasses(type, output) { + if (type) { for (;;) { + var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/); + if (!lineClass) { break } + type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length); + var prop = lineClass[1] ? "bgClass" : "textClass"; + if (output[prop] == null) + { output[prop] = lineClass[2]; } + else if (!(new RegExp("(?:^|\\s)" + lineClass[2] + "(?:$|\\s)")).test(output[prop])) + { output[prop] += " " + lineClass[2]; } + } } + return type + } + + // Run the given mode's parser over a line, calling f for each token. + function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) { + var flattenSpans = mode.flattenSpans; + if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; } + var curStart = 0, curStyle = null; + var stream = new StringStream(text, cm.options.tabSize, context), style; + var inner = cm.options.addModeClass && [null]; + if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); } + while (!stream.eol()) { + if (stream.pos > cm.options.maxHighlightLength) { + flattenSpans = false; + if (forceToEnd) { processLine(cm, text, context, stream.pos); } + stream.pos = text.length; + style = null; + } else { + style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses); + } + if (inner) { + var mName = inner[0].name; + if (mName) { style = "m-" + (style ? mName + " " + style : mName); } + } + if (!flattenSpans || curStyle != style) { + while (curStart < stream.start) { + curStart = Math.min(stream.start, curStart + 5000); + f(curStart, curStyle); + } + curStyle = style; + } + stream.start = stream.pos; + } + while (curStart < stream.pos) { + // Webkit seems to refuse to render text nodes longer than 57444 + // characters, and returns inaccurate measurements in nodes + // starting around 5000 chars. + var pos = Math.min(stream.pos, curStart + 5000); + f(pos, curStyle); + curStart = pos; + } + } + + // Finds the line to start with when starting a parse. Tries to + // find a line with a stateAfter, so that it can start with a + // valid state. If that fails, it returns the line with the + // smallest indentation, which tends to need the least context to + // parse correctly. + function findStartLine(cm, n, precise) { + var minindent, minline, doc = cm.doc; + var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100); + for (var search = n; search > lim; --search) { + if (search <= doc.first) { return doc.first } + var line = getLine(doc, search - 1), after = line.stateAfter; + if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier)) + { return search } + var indented = countColumn(line.text, null, cm.options.tabSize); + if (minline == null || minindent > indented) { + minline = search - 1; + minindent = indented; + } + } + return minline + } + + function retreatFrontier(doc, n) { + doc.modeFrontier = Math.min(doc.modeFrontier, n); + if (doc.highlightFrontier < n - 10) { return } + var start = doc.first; + for (var line = n - 1; line > start; line--) { + var saved = getLine(doc, line).stateAfter; + // change is on 3 + // state on line 1 looked ahead 2 -- so saw 3 + // test 1 + 2 < 3 should cover this + if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) { + start = line + 1; + break + } + } + doc.highlightFrontier = Math.min(doc.highlightFrontier, start); + } + + // Optimize some code when these features are not used. + var sawReadOnlySpans = false, sawCollapsedSpans = false; + + function seeReadOnlySpans() { + sawReadOnlySpans = true; + } + + function seeCollapsedSpans() { + sawCollapsedSpans = true; + } + + // TEXTMARKER SPANS + + function MarkedSpan(marker, from, to) { + this.marker = marker; + this.from = from; this.to = to; + } + + // Search an array of spans for a span matching the given marker. + function getMarkedSpanFor(spans, marker) { + if (spans) { for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.marker == marker) { return span } + } } + } + + // Remove a span from an array, returning undefined if no spans are + // left (we don't store arrays for lines without spans). + function removeMarkedSpan(spans, span) { + var r; + for (var i = 0; i < spans.length; ++i) + { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } } + return r + } + + // Add a span to a line. + function addMarkedSpan(line, span, op) { + var inThisOp = op && window.WeakSet && (op.markedSpans || (op.markedSpans = new WeakSet)); + if (inThisOp && line.markedSpans && inThisOp.has(line.markedSpans)) { + line.markedSpans.push(span); + } else { + line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]; + if (inThisOp) { inThisOp.add(line.markedSpans); } + } + span.marker.attachLine(line); + } + + // Used for the algorithm that adjusts markers for a change in the + // document. These functions cut an array of spans at a given + // character position, returning an array of remaining chunks (or + // undefined if nothing remains). + function markedSpansBefore(old, startCh, isInsert) { + var nw; + if (old) { for (var i = 0; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh); + if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) { + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh) + ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to)); + } + } } + return nw + } + function markedSpansAfter(old, endCh, isInsert) { + var nw; + if (old) { for (var i = 0; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh); + if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) { + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh) + ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh, + span.to == null ? null : span.to - endCh)); + } + } } + return nw + } + + // Given a change object, compute the new set of marker spans that + // cover the line in which the change took place. Removes spans + // entirely within the change, reconnects spans belonging to the + // same marker that appear on both sides of the change, and cuts off + // spans partially within the change. Returns an array of span + // arrays with one element for each line in (after) the change. + function stretchSpansOverChange(doc, change) { + if (change.full) { return null } + var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans; + var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans; + if (!oldFirst && !oldLast) { return null } + + var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0; + // Get the spans that 'stick out' on both sides + var first = markedSpansBefore(oldFirst, startCh, isInsert); + var last = markedSpansAfter(oldLast, endCh, isInsert); + + // Next, merge those two ends + var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0); + if (first) { + // Fix up .to properties of first + for (var i = 0; i < first.length; ++i) { + var span = first[i]; + if (span.to == null) { + var found = getMarkedSpanFor(last, span.marker); + if (!found) { span.to = startCh; } + else if (sameLine) { span.to = found.to == null ? null : found.to + offset; } + } + } + } + if (last) { + // Fix up .from in last (or move them into first in case of sameLine) + for (var i$1 = 0; i$1 < last.length; ++i$1) { + var span$1 = last[i$1]; + if (span$1.to != null) { span$1.to += offset; } + if (span$1.from == null) { + var found$1 = getMarkedSpanFor(first, span$1.marker); + if (!found$1) { + span$1.from = offset; + if (sameLine) { (first || (first = [])).push(span$1); } + } + } else { + span$1.from += offset; + if (sameLine) { (first || (first = [])).push(span$1); } + } + } + } + // Make sure we didn't create any zero-length spans + if (first) { first = clearEmptySpans(first); } + if (last && last != first) { last = clearEmptySpans(last); } + + var newMarkers = [first]; + if (!sameLine) { + // Fill gap with whole-line-spans + var gap = change.text.length - 2, gapMarkers; + if (gap > 0 && first) + { for (var i$2 = 0; i$2 < first.length; ++i$2) + { if (first[i$2].to == null) + { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } } + for (var i$3 = 0; i$3 < gap; ++i$3) + { newMarkers.push(gapMarkers); } + newMarkers.push(last); + } + return newMarkers + } + + // Remove spans that are empty and don't have a clearWhenEmpty + // option of false. + function clearEmptySpans(spans) { + for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false) + { spans.splice(i--, 1); } + } + if (!spans.length) { return null } + return spans + } + + // Used to 'clip' out readOnly ranges when making a change. + function removeReadOnlyRanges(doc, from, to) { + var markers = null; + doc.iter(from.line, to.line + 1, function (line) { + if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { + var mark = line.markedSpans[i].marker; + if (mark.readOnly && (!markers || indexOf(markers, mark) == -1)) + { (markers || (markers = [])).push(mark); } + } } + }); + if (!markers) { return null } + var parts = [{from: from, to: to}]; + for (var i = 0; i < markers.length; ++i) { + var mk = markers[i], m = mk.find(0); + for (var j = 0; j < parts.length; ++j) { + var p = parts[j]; + if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue } + var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to); + if (dfrom < 0 || !mk.inclusiveLeft && !dfrom) + { newParts.push({from: p.from, to: m.from}); } + if (dto > 0 || !mk.inclusiveRight && !dto) + { newParts.push({from: m.to, to: p.to}); } + parts.splice.apply(parts, newParts); + j += newParts.length - 3; + } + } + return parts + } + + // Connect or disconnect spans from a line. + function detachMarkedSpans(line) { + var spans = line.markedSpans; + if (!spans) { return } + for (var i = 0; i < spans.length; ++i) + { spans[i].marker.detachLine(line); } + line.markedSpans = null; + } + function attachMarkedSpans(line, spans) { + if (!spans) { return } + for (var i = 0; i < spans.length; ++i) + { spans[i].marker.attachLine(line); } + line.markedSpans = spans; + } + + // Helpers used when computing which overlapping collapsed span + // counts as the larger one. + function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 } + function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 } + + // Returns a number indicating which of two overlapping collapsed + // spans is larger (and thus includes the other). Falls back to + // comparing ids when the spans cover exactly the same range. + function compareCollapsedMarkers(a, b) { + var lenDiff = a.lines.length - b.lines.length; + if (lenDiff != 0) { return lenDiff } + var aPos = a.find(), bPos = b.find(); + var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b); + if (fromCmp) { return -fromCmp } + var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b); + if (toCmp) { return toCmp } + return b.id - a.id + } + + // Find out whether a line ends or starts in a collapsed span. If + // so, return the marker for that span. + function collapsedSpanAtSide(line, start) { + var sps = sawCollapsedSpans && line.markedSpans, found; + if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { + sp = sps[i]; + if (sp.marker.collapsed && (start ? sp.from : sp.to) == null && + (!found || compareCollapsedMarkers(found, sp.marker) < 0)) + { found = sp.marker; } + } } + return found + } + function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) } + function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) } + + function collapsedSpanAround(line, ch) { + var sps = sawCollapsedSpans && line.markedSpans, found; + if (sps) { for (var i = 0; i < sps.length; ++i) { + var sp = sps[i]; + if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) && + (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; } + } } + return found + } + + // Test whether there exists a collapsed span that partially + // overlaps (covers the start or end, but not both) of a new span. + // Such overlap is not allowed. + function conflictingCollapsedRange(doc, lineNo, from, to, marker) { + var line = getLine(doc, lineNo); + var sps = sawCollapsedSpans && line.markedSpans; + if (sps) { for (var i = 0; i < sps.length; ++i) { + var sp = sps[i]; + if (!sp.marker.collapsed) { continue } + var found = sp.marker.find(0); + var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker); + var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker); + if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue } + if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) || + fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0)) + { return true } + } } + } + + // A visual line is a line as drawn on the screen. Folding, for + // example, can cause multiple logical lines to appear on the same + // visual line. This finds the start of the visual line that the + // given line is part of (usually that is the line itself). + function visualLine(line) { + var merged; + while (merged = collapsedSpanAtStart(line)) + { line = merged.find(-1, true).line; } + return line + } + + function visualLineEnd(line) { + var merged; + while (merged = collapsedSpanAtEnd(line)) + { line = merged.find(1, true).line; } + return line + } + + // Returns an array of logical lines that continue the visual line + // started by the argument, or undefined if there are no such lines. + function visualLineContinued(line) { + var merged, lines; + while (merged = collapsedSpanAtEnd(line)) { + line = merged.find(1, true).line + ;(lines || (lines = [])).push(line); + } + return lines + } + + // Get the line number of the start of the visual line that the + // given line number is part of. + function visualLineNo(doc, lineN) { + var line = getLine(doc, lineN), vis = visualLine(line); + if (line == vis) { return lineN } + return lineNo(vis) + } + + // Get the line number of the start of the next visual line after + // the given line. + function visualLineEndNo(doc, lineN) { + if (lineN > doc.lastLine()) { return lineN } + var line = getLine(doc, lineN), merged; + if (!lineIsHidden(doc, line)) { return lineN } + while (merged = collapsedSpanAtEnd(line)) + { line = merged.find(1, true).line; } + return lineNo(line) + 1 + } + + // Compute whether a line is hidden. Lines count as hidden when they + // are part of a visual line that starts with another line, or when + // they are entirely covered by collapsed, non-widget span. + function lineIsHidden(doc, line) { + var sps = sawCollapsedSpans && line.markedSpans; + if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { + sp = sps[i]; + if (!sp.marker.collapsed) { continue } + if (sp.from == null) { return true } + if (sp.marker.widgetNode) { continue } + if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp)) + { return true } + } } + } + function lineIsHiddenInner(doc, line, span) { + if (span.to == null) { + var end = span.marker.find(1, true); + return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker)) + } + if (span.marker.inclusiveRight && span.to == line.text.length) + { return true } + for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) { + sp = line.markedSpans[i]; + if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to && + (sp.to == null || sp.to != span.from) && + (sp.marker.inclusiveLeft || span.marker.inclusiveRight) && + lineIsHiddenInner(doc, line, sp)) { return true } + } + } + + // Find the height above the given line. + function heightAtLine(lineObj) { + lineObj = visualLine(lineObj); + + var h = 0, chunk = lineObj.parent; + for (var i = 0; i < chunk.lines.length; ++i) { + var line = chunk.lines[i]; + if (line == lineObj) { break } + else { h += line.height; } + } + for (var p = chunk.parent; p; chunk = p, p = chunk.parent) { + for (var i$1 = 0; i$1 < p.children.length; ++i$1) { + var cur = p.children[i$1]; + if (cur == chunk) { break } + else { h += cur.height; } + } + } + return h + } + + // Compute the character length of a line, taking into account + // collapsed ranges (see markText) that might hide parts, and join + // other lines onto it. + function lineLength(line) { + if (line.height == 0) { return 0 } + var len = line.text.length, merged, cur = line; + while (merged = collapsedSpanAtStart(cur)) { + var found = merged.find(0, true); + cur = found.from.line; + len += found.from.ch - found.to.ch; + } + cur = line; + while (merged = collapsedSpanAtEnd(cur)) { + var found$1 = merged.find(0, true); + len -= cur.text.length - found$1.from.ch; + cur = found$1.to.line; + len += cur.text.length - found$1.to.ch; + } + return len + } + + // Find the longest line in the document. + function findMaxLine(cm) { + var d = cm.display, doc = cm.doc; + d.maxLine = getLine(doc, doc.first); + d.maxLineLength = lineLength(d.maxLine); + d.maxLineChanged = true; + doc.iter(function (line) { + var len = lineLength(line); + if (len > d.maxLineLength) { + d.maxLineLength = len; + d.maxLine = line; + } + }); + } + + // LINE DATA STRUCTURE + + // Line objects. These hold state related to a line, including + // highlighting info (the styles array). + var Line = function(text, markedSpans, estimateHeight) { + this.text = text; + attachMarkedSpans(this, markedSpans); + this.height = estimateHeight ? estimateHeight(this) : 1; + }; + + Line.prototype.lineNo = function () { return lineNo(this) }; + eventMixin(Line); + + // Change the content (text, markers) of a line. Automatically + // invalidates cached information and tries to re-estimate the + // line's height. + function updateLine(line, text, markedSpans, estimateHeight) { + line.text = text; + if (line.stateAfter) { line.stateAfter = null; } + if (line.styles) { line.styles = null; } + if (line.order != null) { line.order = null; } + detachMarkedSpans(line); + attachMarkedSpans(line, markedSpans); + var estHeight = estimateHeight ? estimateHeight(line) : 1; + if (estHeight != line.height) { updateLineHeight(line, estHeight); } + } + + // Detach a line from the document tree and its markers. + function cleanUpLine(line) { + line.parent = null; + detachMarkedSpans(line); + } + + // Convert a style as returned by a mode (either null, or a string + // containing one or more styles) to a CSS style. This is cached, + // and also looks for line-wide styles. + var styleToClassCache = {}, styleToClassCacheWithMode = {}; + function interpretTokenStyle(style, options) { + if (!style || /^\s*$/.test(style)) { return null } + var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache; + return cache[style] || + (cache[style] = style.replace(/\S+/g, "cm-$&")) + } + + // Render the DOM representation of the text of a line. Also builds + // up a 'line map', which points at the DOM nodes that represent + // specific stretches of text, and is used by the measuring code. + // The returned object contains the DOM node, this map, and + // information about line-wide styles that were set by the mode. + function buildLineContent(cm, lineView) { + // The padding-right forces the element to have a 'border', which + // is needed on Webkit to be able to get line-level bounding + // rectangles for it (in measureChar). + var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null); + var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content, + col: 0, pos: 0, cm: cm, + trailingSpace: false, + splitSpaces: cm.getOption("lineWrapping")}; + lineView.measure = {}; + + // Iterate over the logical lines that make up this visual line. + for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) { + var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0); + builder.pos = 0; + builder.addToken = buildToken; + // Optionally wire in some hacks into the token-rendering + // algorithm, to deal with browser quirks. + if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction))) + { builder.addToken = buildTokenBadBidi(builder.addToken, order); } + builder.map = []; + var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line); + insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate)); + if (line.styleClasses) { + if (line.styleClasses.bgClass) + { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); } + if (line.styleClasses.textClass) + { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); } + } + + // Ensure at least a single node is present, for measuring. + if (builder.map.length == 0) + { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); } + + // Store the map and a cache object for the current logical line + if (i == 0) { + lineView.measure.map = builder.map; + lineView.measure.cache = {}; + } else { + (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map) + ;(lineView.measure.caches || (lineView.measure.caches = [])).push({}); + } + } + + // See issue #2901 + if (webkit) { + var last = builder.content.lastChild; + if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab"))) + { builder.content.className = "cm-tab-wrap-hack"; } + } + + signal(cm, "renderLine", cm, lineView.line, builder.pre); + if (builder.pre.className) + { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); } + + return builder + } + + function defaultSpecialCharPlaceholder(ch) { + var token = elt("span", "\u2022", "cm-invalidchar"); + token.title = "\\u" + ch.charCodeAt(0).toString(16); + token.setAttribute("aria-label", token.title); + return token + } + + // Build up the DOM representation for a single token, and add it to + // the line map. Takes care to render special characters separately. + function buildToken(builder, text, style, startStyle, endStyle, css, attributes) { + if (!text) { return } + var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text; + var special = builder.cm.state.specialChars, mustWrap = false; + var content; + if (!special.test(text)) { + builder.col += text.length; + content = document.createTextNode(displayText); + builder.map.push(builder.pos, builder.pos + text.length, content); + if (ie && ie_version < 9) { mustWrap = true; } + builder.pos += text.length; + } else { + content = document.createDocumentFragment(); + var pos = 0; + while (true) { + special.lastIndex = pos; + var m = special.exec(text); + var skipped = m ? m.index - pos : text.length - pos; + if (skipped) { + var txt = document.createTextNode(displayText.slice(pos, pos + skipped)); + if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); } + else { content.appendChild(txt); } + builder.map.push(builder.pos, builder.pos + skipped, txt); + builder.col += skipped; + builder.pos += skipped; + } + if (!m) { break } + pos += skipped + 1; + var txt$1 = (void 0); + if (m[0] == "\t") { + var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize; + txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")); + txt$1.setAttribute("role", "presentation"); + txt$1.setAttribute("cm-text", "\t"); + builder.col += tabWidth; + } else if (m[0] == "\r" || m[0] == "\n") { + txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar")); + txt$1.setAttribute("cm-text", m[0]); + builder.col += 1; + } else { + txt$1 = builder.cm.options.specialCharPlaceholder(m[0]); + txt$1.setAttribute("cm-text", m[0]); + if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); } + else { content.appendChild(txt$1); } + builder.col += 1; + } + builder.map.push(builder.pos, builder.pos + 1, txt$1); + builder.pos++; + } + } + builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32; + if (style || startStyle || endStyle || mustWrap || css || attributes) { + var fullStyle = style || ""; + if (startStyle) { fullStyle += startStyle; } + if (endStyle) { fullStyle += endStyle; } + var token = elt("span", [content], fullStyle, css); + if (attributes) { + for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class") + { token.setAttribute(attr, attributes[attr]); } } + } + return builder.content.appendChild(token) + } + builder.content.appendChild(content); + } + + // Change some spaces to NBSP to prevent the browser from collapsing + // trailing spaces at the end of a line when rendering text (issue #1362). + function splitSpaces(text, trailingBefore) { + if (text.length > 1 && !/ /.test(text)) { return text } + var spaceBefore = trailingBefore, result = ""; + for (var i = 0; i < text.length; i++) { + var ch = text.charAt(i); + if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32)) + { ch = "\u00a0"; } + result += ch; + spaceBefore = ch == " "; + } + return result + } + + // Work around nonsense dimensions being reported for stretches of + // right-to-left text. + function buildTokenBadBidi(inner, order) { + return function (builder, text, style, startStyle, endStyle, css, attributes) { + style = style ? style + " cm-force-border" : "cm-force-border"; + var start = builder.pos, end = start + text.length; + for (;;) { + // Find the part that overlaps with the start of this text + var part = (void 0); + for (var i = 0; i < order.length; i++) { + part = order[i]; + if (part.to > start && part.from <= start) { break } + } + if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) } + inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes); + startStyle = null; + text = text.slice(part.to - start); + start = part.to; + } + } + } + + function buildCollapsedSpan(builder, size, marker, ignoreWidget) { + var widget = !ignoreWidget && marker.widgetNode; + if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); } + if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) { + if (!widget) + { widget = builder.content.appendChild(document.createElement("span")); } + widget.setAttribute("cm-marker", marker.id); + } + if (widget) { + builder.cm.display.input.setUneditable(widget); + builder.content.appendChild(widget); + } + builder.pos += size; + builder.trailingSpace = false; + } + + // Outputs a number of spans to make up a line, taking highlighting + // and marked text into account. + function insertLineContent(line, builder, styles) { + var spans = line.markedSpans, allText = line.text, at = 0; + if (!spans) { + for (var i$1 = 1; i$1 < styles.length; i$1+=2) + { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); } + return + } + + var len = allText.length, pos = 0, i = 1, text = "", style, css; + var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes; + for (;;) { + if (nextChange == pos) { // Update current marker set + spanStyle = spanEndStyle = spanStartStyle = css = ""; + attributes = null; + collapsed = null; nextChange = Infinity; + var foundBookmarks = [], endStyles = (void 0); + for (var j = 0; j < spans.length; ++j) { + var sp = spans[j], m = sp.marker; + if (m.type == "bookmark" && sp.from == pos && m.widgetNode) { + foundBookmarks.push(m); + } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) { + if (sp.to != null && sp.to != pos && nextChange > sp.to) { + nextChange = sp.to; + spanEndStyle = ""; + } + if (m.className) { spanStyle += " " + m.className; } + if (m.css) { css = (css ? css + ";" : "") + m.css; } + if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; } + if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); } + // support for the old title property + // https://github.com/codemirror/CodeMirror/pull/5673 + if (m.title) { (attributes || (attributes = {})).title = m.title; } + if (m.attributes) { + for (var attr in m.attributes) + { (attributes || (attributes = {}))[attr] = m.attributes[attr]; } + } + if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)) + { collapsed = sp; } + } else if (sp.from > pos && nextChange > sp.from) { + nextChange = sp.from; + } + } + if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2) + { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } } + + if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2) + { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } } + if (collapsed && (collapsed.from || 0) == pos) { + buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos, + collapsed.marker, collapsed.from == null); + if (collapsed.to == null) { return } + if (collapsed.to == pos) { collapsed = false; } + } + } + if (pos >= len) { break } + + var upto = Math.min(len, nextChange); + while (true) { + if (text) { + var end = pos + text.length; + if (!collapsed) { + var tokenText = end > upto ? text.slice(0, upto - pos) : text; + builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle, + spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes); + } + if (end >= upto) {text = text.slice(upto - pos); pos = upto; break} + pos = end; + spanStartStyle = ""; + } + text = allText.slice(at, at = styles[i++]); + style = interpretTokenStyle(styles[i++], builder.cm.options); + } + } + } + + + // These objects are used to represent the visible (currently drawn) + // part of the document. A LineView may correspond to multiple + // logical lines, if those are connected by collapsed ranges. + function LineView(doc, line, lineN) { + // The starting line + this.line = line; + // Continuing lines, if any + this.rest = visualLineContinued(line); + // Number of logical lines in this visual line + this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1; + this.node = this.text = null; + this.hidden = lineIsHidden(doc, line); + } + + // Create a range of LineView objects for the given lines. + function buildViewArray(cm, from, to) { + var array = [], nextPos; + for (var pos = from; pos < to; pos = nextPos) { + var view = new LineView(cm.doc, getLine(cm.doc, pos), pos); + nextPos = pos + view.size; + array.push(view); + } + return array + } + + var operationGroup = null; + + function pushOperation(op) { + if (operationGroup) { + operationGroup.ops.push(op); + } else { + op.ownsGroup = operationGroup = { + ops: [op], + delayedCallbacks: [] + }; + } + } + + function fireCallbacksForOps(group) { + // Calls delayed callbacks and cursorActivity handlers until no + // new ones appear + var callbacks = group.delayedCallbacks, i = 0; + do { + for (; i < callbacks.length; i++) + { callbacks[i].call(null); } + for (var j = 0; j < group.ops.length; j++) { + var op = group.ops[j]; + if (op.cursorActivityHandlers) + { while (op.cursorActivityCalled < op.cursorActivityHandlers.length) + { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } } + } + } while (i < callbacks.length) + } + + function finishOperation(op, endCb) { + var group = op.ownsGroup; + if (!group) { return } + + try { fireCallbacksForOps(group); } + finally { + operationGroup = null; + endCb(group); + } + } + + var orphanDelayedCallbacks = null; + + // Often, we want to signal events at a point where we are in the + // middle of some work, but don't want the handler to start calling + // other methods on the editor, which might be in an inconsistent + // state or simply not expect any other events to happen. + // signalLater looks whether there are any handlers, and schedules + // them to be executed when the last operation ends, or, if no + // operation is active, when a timeout fires. + function signalLater(emitter, type /*, values...*/) { + var arr = getHandlers(emitter, type); + if (!arr.length) { return } + var args = Array.prototype.slice.call(arguments, 2), list; + if (operationGroup) { + list = operationGroup.delayedCallbacks; + } else if (orphanDelayedCallbacks) { + list = orphanDelayedCallbacks; + } else { + list = orphanDelayedCallbacks = []; + setTimeout(fireOrphanDelayed, 0); + } + var loop = function ( i ) { + list.push(function () { return arr[i].apply(null, args); }); + }; + + for (var i = 0; i < arr.length; ++i) + loop( i ); + } + + function fireOrphanDelayed() { + var delayed = orphanDelayedCallbacks; + orphanDelayedCallbacks = null; + for (var i = 0; i < delayed.length; ++i) { delayed[i](); } + } + + // When an aspect of a line changes, a string is added to + // lineView.changes. This updates the relevant part of the line's + // DOM structure. + function updateLineForChanges(cm, lineView, lineN, dims) { + for (var j = 0; j < lineView.changes.length; j++) { + var type = lineView.changes[j]; + if (type == "text") { updateLineText(cm, lineView); } + else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); } + else if (type == "class") { updateLineClasses(cm, lineView); } + else if (type == "widget") { updateLineWidgets(cm, lineView, dims); } + } + lineView.changes = null; + } + + // Lines with gutter elements, widgets or a background class need to + // be wrapped, and have the extra elements added to the wrapper div + function ensureLineWrapped(lineView) { + if (lineView.node == lineView.text) { + lineView.node = elt("div", null, null, "position: relative"); + if (lineView.text.parentNode) + { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); } + lineView.node.appendChild(lineView.text); + if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; } + } + return lineView.node + } + + function updateLineBackground(cm, lineView) { + var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass; + if (cls) { cls += " CodeMirror-linebackground"; } + if (lineView.background) { + if (cls) { lineView.background.className = cls; } + else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; } + } else if (cls) { + var wrap = ensureLineWrapped(lineView); + lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild); + cm.display.input.setUneditable(lineView.background); + } + } + + // Wrapper around buildLineContent which will reuse the structure + // in display.externalMeasured when possible. + function getLineContent(cm, lineView) { + var ext = cm.display.externalMeasured; + if (ext && ext.line == lineView.line) { + cm.display.externalMeasured = null; + lineView.measure = ext.measure; + return ext.built + } + return buildLineContent(cm, lineView) + } + + // Redraw the line's text. Interacts with the background and text + // classes because the mode may output tokens that influence these + // classes. + function updateLineText(cm, lineView) { + var cls = lineView.text.className; + var built = getLineContent(cm, lineView); + if (lineView.text == lineView.node) { lineView.node = built.pre; } + lineView.text.parentNode.replaceChild(built.pre, lineView.text); + lineView.text = built.pre; + if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) { + lineView.bgClass = built.bgClass; + lineView.textClass = built.textClass; + updateLineClasses(cm, lineView); + } else if (cls) { + lineView.text.className = cls; + } + } + + function updateLineClasses(cm, lineView) { + updateLineBackground(cm, lineView); + if (lineView.line.wrapClass) + { ensureLineWrapped(lineView).className = lineView.line.wrapClass; } + else if (lineView.node != lineView.text) + { lineView.node.className = ""; } + var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass; + lineView.text.className = textClass || ""; + } + + function updateLineGutter(cm, lineView, lineN, dims) { + if (lineView.gutter) { + lineView.node.removeChild(lineView.gutter); + lineView.gutter = null; + } + if (lineView.gutterBackground) { + lineView.node.removeChild(lineView.gutterBackground); + lineView.gutterBackground = null; + } + if (lineView.line.gutterClass) { + var wrap = ensureLineWrapped(lineView); + lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass, + ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px")); + cm.display.input.setUneditable(lineView.gutterBackground); + wrap.insertBefore(lineView.gutterBackground, lineView.text); + } + var markers = lineView.line.gutterMarkers; + if (cm.options.lineNumbers || markers) { + var wrap$1 = ensureLineWrapped(lineView); + var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px")); + gutterWrap.setAttribute("aria-hidden", "true"); + cm.display.input.setUneditable(gutterWrap); + wrap$1.insertBefore(gutterWrap, lineView.text); + if (lineView.line.gutterClass) + { gutterWrap.className += " " + lineView.line.gutterClass; } + if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"])) + { lineView.lineNumber = gutterWrap.appendChild( + elt("div", lineNumberFor(cm.options, lineN), + "CodeMirror-linenumber CodeMirror-gutter-elt", + ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); } + if (markers) { for (var k = 0; k < cm.display.gutterSpecs.length; ++k) { + var id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id]; + if (found) + { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", + ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); } + } } + } + } + + function updateLineWidgets(cm, lineView, dims) { + if (lineView.alignable) { lineView.alignable = null; } + var isWidget = classTest("CodeMirror-linewidget"); + for (var node = lineView.node.firstChild, next = (void 0); node; node = next) { + next = node.nextSibling; + if (isWidget.test(node.className)) { lineView.node.removeChild(node); } + } + insertLineWidgets(cm, lineView, dims); + } + + // Build a line's DOM representation from scratch + function buildLineElement(cm, lineView, lineN, dims) { + var built = getLineContent(cm, lineView); + lineView.text = lineView.node = built.pre; + if (built.bgClass) { lineView.bgClass = built.bgClass; } + if (built.textClass) { lineView.textClass = built.textClass; } + + updateLineClasses(cm, lineView); + updateLineGutter(cm, lineView, lineN, dims); + insertLineWidgets(cm, lineView, dims); + return lineView.node + } + + // A lineView may contain multiple logical lines (when merged by + // collapsed spans). The widgets for all of them need to be drawn. + function insertLineWidgets(cm, lineView, dims) { + insertLineWidgetsFor(cm, lineView.line, lineView, dims, true); + if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) + { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } } + } + + function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) { + if (!line.widgets) { return } + var wrap = ensureLineWrapped(lineView); + for (var i = 0, ws = line.widgets; i < ws.length; ++i) { + var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget" + (widget.className ? " " + widget.className : "")); + if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); } + positionLineWidget(widget, node, lineView, dims); + cm.display.input.setUneditable(node); + if (allowAbove && widget.above) + { wrap.insertBefore(node, lineView.gutter || lineView.text); } + else + { wrap.appendChild(node); } + signalLater(widget, "redraw"); + } + } + + function positionLineWidget(widget, node, lineView, dims) { + if (widget.noHScroll) { + (lineView.alignable || (lineView.alignable = [])).push(node); + var width = dims.wrapperWidth; + node.style.left = dims.fixedPos + "px"; + if (!widget.coverGutter) { + width -= dims.gutterTotalWidth; + node.style.paddingLeft = dims.gutterTotalWidth + "px"; + } + node.style.width = width + "px"; + } + if (widget.coverGutter) { + node.style.zIndex = 5; + node.style.position = "relative"; + if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; } + } + } + + function widgetHeight(widget) { + if (widget.height != null) { return widget.height } + var cm = widget.doc.cm; + if (!cm) { return 0 } + if (!contains(document.body, widget.node)) { + var parentStyle = "position: relative;"; + if (widget.coverGutter) + { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; } + if (widget.noHScroll) + { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; } + removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle)); + } + return widget.height = widget.node.parentNode.offsetHeight + } + + // Return true when the given mouse event happened in a widget + function eventInWidget(display, e) { + for (var n = e_target(e); n != display.wrapper; n = n.parentNode) { + if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") || + (n.parentNode == display.sizer && n != display.mover)) + { return true } + } + } + + // POSITION MEASUREMENT + + function paddingTop(display) {return display.lineSpace.offsetTop} + function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight} + function paddingH(display) { + if (display.cachedPaddingH) { return display.cachedPaddingH } + var e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-line-like")); + var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle; + var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}; + if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; } + return data + } + + function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth } + function displayWidth(cm) { + return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth + } + function displayHeight(cm) { + return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight + } + + // Ensure the lineView.wrapping.heights array is populated. This is + // an array of bottom offsets for the lines that make up a drawn + // line. When lineWrapping is on, there might be more than one + // height. + function ensureLineHeights(cm, lineView, rect) { + var wrapping = cm.options.lineWrapping; + var curWidth = wrapping && displayWidth(cm); + if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) { + var heights = lineView.measure.heights = []; + if (wrapping) { + lineView.measure.width = curWidth; + var rects = lineView.text.firstChild.getClientRects(); + for (var i = 0; i < rects.length - 1; i++) { + var cur = rects[i], next = rects[i + 1]; + if (Math.abs(cur.bottom - next.bottom) > 2) + { heights.push((cur.bottom + next.top) / 2 - rect.top); } + } + } + heights.push(rect.bottom - rect.top); + } + } + + // Find a line map (mapping character offsets to text nodes) and a + // measurement cache for the given line number. (A line view might + // contain multiple lines when collapsed ranges are present.) + function mapFromLineView(lineView, line, lineN) { + if (lineView.line == line) + { return {map: lineView.measure.map, cache: lineView.measure.cache} } + if (lineView.rest) { + for (var i = 0; i < lineView.rest.length; i++) + { if (lineView.rest[i] == line) + { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } } + for (var i$1 = 0; i$1 < lineView.rest.length; i$1++) + { if (lineNo(lineView.rest[i$1]) > lineN) + { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } } + } + } + + // Render a line into the hidden node display.externalMeasured. Used + // when measurement is needed for a line that's not in the viewport. + function updateExternalMeasurement(cm, line) { + line = visualLine(line); + var lineN = lineNo(line); + var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN); + view.lineN = lineN; + var built = view.built = buildLineContent(cm, view); + view.text = built.pre; + removeChildrenAndAdd(cm.display.lineMeasure, built.pre); + return view + } + + // Get a {top, bottom, left, right} box (in line-local coordinates) + // for a given character. + function measureChar(cm, line, ch, bias) { + return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias) + } + + // Find a line view that corresponds to the given line number. + function findViewForLine(cm, lineN) { + if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo) + { return cm.display.view[findViewIndex(cm, lineN)] } + var ext = cm.display.externalMeasured; + if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size) + { return ext } + } + + // Measurement can be split in two steps, the set-up work that + // applies to the whole line, and the measurement of the actual + // character. Functions like coordsChar, that need to do a lot of + // measurements in a row, can thus ensure that the set-up work is + // only done once. + function prepareMeasureForLine(cm, line) { + var lineN = lineNo(line); + var view = findViewForLine(cm, lineN); + if (view && !view.text) { + view = null; + } else if (view && view.changes) { + updateLineForChanges(cm, view, lineN, getDimensions(cm)); + cm.curOp.forceUpdate = true; + } + if (!view) + { view = updateExternalMeasurement(cm, line); } + + var info = mapFromLineView(view, line, lineN); + return { + line: line, view: view, rect: null, + map: info.map, cache: info.cache, before: info.before, + hasHeights: false + } + } + + // Given a prepared measurement object, measures the position of an + // actual character (or fetches it from the cache). + function measureCharPrepared(cm, prepared, ch, bias, varHeight) { + if (prepared.before) { ch = -1; } + var key = ch + (bias || ""), found; + if (prepared.cache.hasOwnProperty(key)) { + found = prepared.cache[key]; + } else { + if (!prepared.rect) + { prepared.rect = prepared.view.text.getBoundingClientRect(); } + if (!prepared.hasHeights) { + ensureLineHeights(cm, prepared.view, prepared.rect); + prepared.hasHeights = true; + } + found = measureCharInner(cm, prepared, ch, bias); + if (!found.bogus) { prepared.cache[key] = found; } + } + return {left: found.left, right: found.right, + top: varHeight ? found.rtop : found.top, + bottom: varHeight ? found.rbottom : found.bottom} + } + + var nullRect = {left: 0, right: 0, top: 0, bottom: 0}; + + function nodeAndOffsetInLineMap(map, ch, bias) { + var node, start, end, collapse, mStart, mEnd; + // First, search the line map for the text node corresponding to, + // or closest to, the target character. + for (var i = 0; i < map.length; i += 3) { + mStart = map[i]; + mEnd = map[i + 1]; + if (ch < mStart) { + start = 0; end = 1; + collapse = "left"; + } else if (ch < mEnd) { + start = ch - mStart; + end = start + 1; + } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) { + end = mEnd - mStart; + start = end - 1; + if (ch >= mEnd) { collapse = "right"; } + } + if (start != null) { + node = map[i + 2]; + if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right")) + { collapse = bias; } + if (bias == "left" && start == 0) + { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) { + node = map[(i -= 3) + 2]; + collapse = "left"; + } } + if (bias == "right" && start == mEnd - mStart) + { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) { + node = map[(i += 3) + 2]; + collapse = "right"; + } } + break + } + } + return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd} + } + + function getUsefulRect(rects, bias) { + var rect = nullRect; + if (bias == "left") { for (var i = 0; i < rects.length; i++) { + if ((rect = rects[i]).left != rect.right) { break } + } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) { + if ((rect = rects[i$1]).left != rect.right) { break } + } } + return rect + } + + function measureCharInner(cm, prepared, ch, bias) { + var place = nodeAndOffsetInLineMap(prepared.map, ch, bias); + var node = place.node, start = place.start, end = place.end, collapse = place.collapse; + + var rect; + if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates. + for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned + while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; } + while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; } + if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) + { rect = node.parentNode.getBoundingClientRect(); } + else + { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); } + if (rect.left || rect.right || start == 0) { break } + end = start; + start = start - 1; + collapse = "right"; + } + if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); } + } else { // If it is a widget, simply get the box for the whole widget. + if (start > 0) { collapse = bias = "right"; } + var rects; + if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1) + { rect = rects[bias == "right" ? rects.length - 1 : 0]; } + else + { rect = node.getBoundingClientRect(); } + } + if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) { + var rSpan = node.parentNode.getClientRects()[0]; + if (rSpan) + { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; } + else + { rect = nullRect; } + } + + var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top; + var mid = (rtop + rbot) / 2; + var heights = prepared.view.measure.heights; + var i = 0; + for (; i < heights.length - 1; i++) + { if (mid < heights[i]) { break } } + var top = i ? heights[i - 1] : 0, bot = heights[i]; + var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left, + right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left, + top: top, bottom: bot}; + if (!rect.left && !rect.right) { result.bogus = true; } + if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; } + + return result + } + + // Work around problem with bounding client rects on ranges being + // returned incorrectly when zoomed on IE10 and below. + function maybeUpdateRectForZooming(measure, rect) { + if (!window.screen || screen.logicalXDPI == null || + screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure)) + { return rect } + var scaleX = screen.logicalXDPI / screen.deviceXDPI; + var scaleY = screen.logicalYDPI / screen.deviceYDPI; + return {left: rect.left * scaleX, right: rect.right * scaleX, + top: rect.top * scaleY, bottom: rect.bottom * scaleY} + } + + function clearLineMeasurementCacheFor(lineView) { + if (lineView.measure) { + lineView.measure.cache = {}; + lineView.measure.heights = null; + if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) + { lineView.measure.caches[i] = {}; } } + } + } + + function clearLineMeasurementCache(cm) { + cm.display.externalMeasure = null; + removeChildren(cm.display.lineMeasure); + for (var i = 0; i < cm.display.view.length; i++) + { clearLineMeasurementCacheFor(cm.display.view[i]); } + } + + function clearCaches(cm) { + clearLineMeasurementCache(cm); + cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null; + if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; } + cm.display.lineNumChars = null; + } + + function pageScrollX(doc) { + // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206 + // which causes page_Offset and bounding client rects to use + // different reference viewports and invalidate our calculations. + if (chrome && android) { return -(doc.body.getBoundingClientRect().left - parseInt(getComputedStyle(doc.body).marginLeft)) } + return doc.defaultView.pageXOffset || (doc.documentElement || doc.body).scrollLeft + } + function pageScrollY(doc) { + if (chrome && android) { return -(doc.body.getBoundingClientRect().top - parseInt(getComputedStyle(doc.body).marginTop)) } + return doc.defaultView.pageYOffset || (doc.documentElement || doc.body).scrollTop + } + + function widgetTopHeight(lineObj) { + var ref = visualLine(lineObj); + var widgets = ref.widgets; + var height = 0; + if (widgets) { for (var i = 0; i < widgets.length; ++i) { if (widgets[i].above) + { height += widgetHeight(widgets[i]); } } } + return height + } + + // Converts a {top, bottom, left, right} box from line-local + // coordinates into another coordinate system. Context may be one of + // "line", "div" (display.lineDiv), "local"./null (editor), "window", + // or "page". + function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) { + if (!includeWidgets) { + var height = widgetTopHeight(lineObj); + rect.top += height; rect.bottom += height; + } + if (context == "line") { return rect } + if (!context) { context = "local"; } + var yOff = heightAtLine(lineObj); + if (context == "local") { yOff += paddingTop(cm.display); } + else { yOff -= cm.display.viewOffset; } + if (context == "page" || context == "window") { + var lOff = cm.display.lineSpace.getBoundingClientRect(); + yOff += lOff.top + (context == "window" ? 0 : pageScrollY(doc(cm))); + var xOff = lOff.left + (context == "window" ? 0 : pageScrollX(doc(cm))); + rect.left += xOff; rect.right += xOff; + } + rect.top += yOff; rect.bottom += yOff; + return rect + } + + // Coverts a box from "div" coords to another coordinate system. + // Context may be "window", "page", "div", or "local"./null. + function fromCoordSystem(cm, coords, context) { + if (context == "div") { return coords } + var left = coords.left, top = coords.top; + // First move into "page" coordinate system + if (context == "page") { + left -= pageScrollX(doc(cm)); + top -= pageScrollY(doc(cm)); + } else if (context == "local" || !context) { + var localBox = cm.display.sizer.getBoundingClientRect(); + left += localBox.left; + top += localBox.top; + } + + var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect(); + return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top} + } + + function charCoords(cm, pos, context, lineObj, bias) { + if (!lineObj) { lineObj = getLine(cm.doc, pos.line); } + return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context) + } + + // Returns a box for a given cursor position, which may have an + // 'other' property containing the position of the secondary cursor + // on a bidi boundary. + // A cursor Pos(line, char, "before") is on the same visual line as `char - 1` + // and after `char - 1` in writing order of `char - 1` + // A cursor Pos(line, char, "after") is on the same visual line as `char` + // and before `char` in writing order of `char` + // Examples (upper-case letters are RTL, lower-case are LTR): + // Pos(0, 1, ...) + // before after + // ab a|b a|b + // aB a|B aB| + // Ab |Ab A|b + // AB B|A B|A + // Every position after the last character on a line is considered to stick + // to the last character on the line. + function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) { + lineObj = lineObj || getLine(cm.doc, pos.line); + if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); } + function get(ch, right) { + var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight); + if (right) { m.left = m.right; } else { m.right = m.left; } + return intoCoordSystem(cm, lineObj, m, context) + } + var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky; + if (ch >= lineObj.text.length) { + ch = lineObj.text.length; + sticky = "before"; + } else if (ch <= 0) { + ch = 0; + sticky = "after"; + } + if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") } + + function getBidi(ch, partPos, invert) { + var part = order[partPos], right = part.level == 1; + return get(invert ? ch - 1 : ch, right != invert) + } + var partPos = getBidiPartAt(order, ch, sticky); + var other = bidiOther; + var val = getBidi(ch, partPos, sticky == "before"); + if (other != null) { val.other = getBidi(ch, other, sticky != "before"); } + return val + } + + // Used to cheaply estimate the coordinates for a position. Used for + // intermediate scroll updates. + function estimateCoords(cm, pos) { + var left = 0; + pos = clipPos(cm.doc, pos); + if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; } + var lineObj = getLine(cm.doc, pos.line); + var top = heightAtLine(lineObj) + paddingTop(cm.display); + return {left: left, right: left, top: top, bottom: top + lineObj.height} + } + + // Positions returned by coordsChar contain some extra information. + // xRel is the relative x position of the input coordinates compared + // to the found position (so xRel > 0 means the coordinates are to + // the right of the character position, for example). When outside + // is true, that means the coordinates lie outside the line's + // vertical range. + function PosWithInfo(line, ch, sticky, outside, xRel) { + var pos = Pos(line, ch, sticky); + pos.xRel = xRel; + if (outside) { pos.outside = outside; } + return pos + } + + // Compute the character position closest to the given coordinates. + // Input must be lineSpace-local ("div" coordinate system). + function coordsChar(cm, x, y) { + var doc = cm.doc; + y += cm.display.viewOffset; + if (y < 0) { return PosWithInfo(doc.first, 0, null, -1, -1) } + var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1; + if (lineN > last) + { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1) } + if (x < 0) { x = 0; } + + var lineObj = getLine(doc, lineN); + for (;;) { + var found = coordsCharInner(cm, lineObj, lineN, x, y); + var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0)); + if (!collapsed) { return found } + var rangeEnd = collapsed.find(1); + if (rangeEnd.line == lineN) { return rangeEnd } + lineObj = getLine(doc, lineN = rangeEnd.line); + } + } + + function wrappedLineExtent(cm, lineObj, preparedMeasure, y) { + y -= widgetTopHeight(lineObj); + var end = lineObj.text.length; + var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0); + end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end); + return {begin: begin, end: end} + } + + function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) { + if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); } + var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top; + return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop) + } + + // Returns true if the given side of a box is after the given + // coordinates, in top-to-bottom, left-to-right order. + function boxIsAfter(box, x, y, left) { + return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x + } + + function coordsCharInner(cm, lineObj, lineNo, x, y) { + // Move y into line-local coordinate space + y -= heightAtLine(lineObj); + var preparedMeasure = prepareMeasureForLine(cm, lineObj); + // When directly calling `measureCharPrepared`, we have to adjust + // for the widgets at this line. + var widgetHeight = widgetTopHeight(lineObj); + var begin = 0, end = lineObj.text.length, ltr = true; + + var order = getOrder(lineObj, cm.doc.direction); + // If the line isn't plain left-to-right text, first figure out + // which bidi section the coordinates fall into. + if (order) { + var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart) + (cm, lineObj, lineNo, preparedMeasure, order, x, y); + ltr = part.level != 1; + // The awkward -1 offsets are needed because findFirst (called + // on these below) will treat its first bound as inclusive, + // second as exclusive, but we want to actually address the + // characters in the part's range + begin = ltr ? part.from : part.to - 1; + end = ltr ? part.to : part.from - 1; + } + + // A binary search to find the first character whose bounding box + // starts after the coordinates. If we run across any whose box wrap + // the coordinates, store that. + var chAround = null, boxAround = null; + var ch = findFirst(function (ch) { + var box = measureCharPrepared(cm, preparedMeasure, ch); + box.top += widgetHeight; box.bottom += widgetHeight; + if (!boxIsAfter(box, x, y, false)) { return false } + if (box.top <= y && box.left <= x) { + chAround = ch; + boxAround = box; + } + return true + }, begin, end); + + var baseX, sticky, outside = false; + // If a box around the coordinates was found, use that + if (boxAround) { + // Distinguish coordinates nearer to the left or right side of the box + var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr; + ch = chAround + (atStart ? 0 : 1); + sticky = atStart ? "after" : "before"; + baseX = atLeft ? boxAround.left : boxAround.right; + } else { + // (Adjust for extended bound, if necessary.) + if (!ltr && (ch == end || ch == begin)) { ch++; } + // To determine which side to associate with, get the box to the + // left of the character and compare it's vertical position to the + // coordinates + sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" : + (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ? + "after" : "before"; + // Now get accurate coordinates for this place, in order to get a + // base X position + var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure); + baseX = coords.left; + outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0; + } + + ch = skipExtendingChars(lineObj.text, ch, 1); + return PosWithInfo(lineNo, ch, sticky, outside, x - baseX) + } + + function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) { + // Bidi parts are sorted left-to-right, and in a non-line-wrapping + // situation, we can take this ordering to correspond to the visual + // ordering. This finds the first part whose end is after the given + // coordinates. + var index = findFirst(function (i) { + var part = order[i], ltr = part.level != 1; + return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"), + "line", lineObj, preparedMeasure), x, y, true) + }, 0, order.length - 1); + var part = order[index]; + // If this isn't the first part, the part's start is also after + // the coordinates, and the coordinates aren't on the same line as + // that start, move one part back. + if (index > 0) { + var ltr = part.level != 1; + var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"), + "line", lineObj, preparedMeasure); + if (boxIsAfter(start, x, y, true) && start.top > y) + { part = order[index - 1]; } + } + return part + } + + function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) { + // In a wrapped line, rtl text on wrapping boundaries can do things + // that don't correspond to the ordering in our `order` array at + // all, so a binary search doesn't work, and we want to return a + // part that only spans one line so that the binary search in + // coordsCharInner is safe. As such, we first find the extent of the + // wrapped line, and then do a flat search in which we discard any + // spans that aren't on the line. + var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y); + var begin = ref.begin; + var end = ref.end; + if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; } + var part = null, closestDist = null; + for (var i = 0; i < order.length; i++) { + var p = order[i]; + if (p.from >= end || p.to <= begin) { continue } + var ltr = p.level != 1; + var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right; + // Weigh against spans ending before this, so that they are only + // picked if nothing ends after + var dist = endX < x ? x - endX + 1e9 : endX - x; + if (!part || closestDist > dist) { + part = p; + closestDist = dist; + } + } + if (!part) { part = order[order.length - 1]; } + // Clip the part to the wrapped line. + if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; } + if (part.to > end) { part = {from: part.from, to: end, level: part.level}; } + return part + } + + var measureText; + // Compute the default text height. + function textHeight(display) { + if (display.cachedTextHeight != null) { return display.cachedTextHeight } + if (measureText == null) { + measureText = elt("pre", null, "CodeMirror-line-like"); + // Measure a bunch of lines, for browsers that compute + // fractional heights. + for (var i = 0; i < 49; ++i) { + measureText.appendChild(document.createTextNode("x")); + measureText.appendChild(elt("br")); + } + measureText.appendChild(document.createTextNode("x")); + } + removeChildrenAndAdd(display.measure, measureText); + var height = measureText.offsetHeight / 50; + if (height > 3) { display.cachedTextHeight = height; } + removeChildren(display.measure); + return height || 1 + } + + // Compute the default character width. + function charWidth(display) { + if (display.cachedCharWidth != null) { return display.cachedCharWidth } + var anchor = elt("span", "xxxxxxxxxx"); + var pre = elt("pre", [anchor], "CodeMirror-line-like"); + removeChildrenAndAdd(display.measure, pre); + var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10; + if (width > 2) { display.cachedCharWidth = width; } + return width || 10 + } + + // Do a bulk-read of the DOM positions and sizes needed to draw the + // view, so that we don't interleave reading and writing to the DOM. + function getDimensions(cm) { + var d = cm.display, left = {}, width = {}; + var gutterLeft = d.gutters.clientLeft; + for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) { + var id = cm.display.gutterSpecs[i].className; + left[id] = n.offsetLeft + n.clientLeft + gutterLeft; + width[id] = n.clientWidth; + } + return {fixedPos: compensateForHScroll(d), + gutterTotalWidth: d.gutters.offsetWidth, + gutterLeft: left, + gutterWidth: width, + wrapperWidth: d.wrapper.clientWidth} + } + + // Computes display.scroller.scrollLeft + display.gutters.offsetWidth, + // but using getBoundingClientRect to get a sub-pixel-accurate + // result. + function compensateForHScroll(display) { + return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left + } + + // Returns a function that estimates the height of a line, to use as + // first approximation until the line becomes visible (and is thus + // properly measurable). + function estimateHeight(cm) { + var th = textHeight(cm.display), wrapping = cm.options.lineWrapping; + var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3); + return function (line) { + if (lineIsHidden(cm.doc, line)) { return 0 } + + var widgetsHeight = 0; + if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) { + if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; } + } } + + if (wrapping) + { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th } + else + { return widgetsHeight + th } + } + } + + function estimateLineHeights(cm) { + var doc = cm.doc, est = estimateHeight(cm); + doc.iter(function (line) { + var estHeight = est(line); + if (estHeight != line.height) { updateLineHeight(line, estHeight); } + }); + } + + // Given a mouse event, find the corresponding position. If liberal + // is false, it checks whether a gutter or scrollbar was clicked, + // and returns null if it was. forRect is used by rectangular + // selections, and tries to estimate a character position even for + // coordinates beyond the right of the text. + function posFromMouse(cm, e, liberal, forRect) { + var display = cm.display; + if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null } + + var x, y, space = display.lineSpace.getBoundingClientRect(); + // Fails unpredictably on IE[67] when mouse is dragged around quickly. + try { x = e.clientX - space.left; y = e.clientY - space.top; } + catch (e$1) { return null } + var coords = coordsChar(cm, x, y), line; + if (forRect && coords.xRel > 0 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) { + var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length; + coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff)); + } + return coords + } + + // Find the view element corresponding to a given line. Return null + // when the line isn't visible. + function findViewIndex(cm, n) { + if (n >= cm.display.viewTo) { return null } + n -= cm.display.viewFrom; + if (n < 0) { return null } + var view = cm.display.view; + for (var i = 0; i < view.length; i++) { + n -= view[i].size; + if (n < 0) { return i } + } + } + + // Updates the display.view data structure for a given change to the + // document. From and to are in pre-change coordinates. Lendiff is + // the amount of lines added or subtracted by the change. This is + // used for changes that span multiple lines, or change the way + // lines are divided into visual lines. regLineChange (below) + // registers single-line changes. + function regChange(cm, from, to, lendiff) { + if (from == null) { from = cm.doc.first; } + if (to == null) { to = cm.doc.first + cm.doc.size; } + if (!lendiff) { lendiff = 0; } + + var display = cm.display; + if (lendiff && to < display.viewTo && + (display.updateLineNumbers == null || display.updateLineNumbers > from)) + { display.updateLineNumbers = from; } + + cm.curOp.viewChanged = true; + + if (from >= display.viewTo) { // Change after + if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo) + { resetView(cm); } + } else if (to <= display.viewFrom) { // Change before + if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) { + resetView(cm); + } else { + display.viewFrom += lendiff; + display.viewTo += lendiff; + } + } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap + resetView(cm); + } else if (from <= display.viewFrom) { // Top overlap + var cut = viewCuttingPoint(cm, to, to + lendiff, 1); + if (cut) { + display.view = display.view.slice(cut.index); + display.viewFrom = cut.lineN; + display.viewTo += lendiff; + } else { + resetView(cm); + } + } else if (to >= display.viewTo) { // Bottom overlap + var cut$1 = viewCuttingPoint(cm, from, from, -1); + if (cut$1) { + display.view = display.view.slice(0, cut$1.index); + display.viewTo = cut$1.lineN; + } else { + resetView(cm); + } + } else { // Gap in the middle + var cutTop = viewCuttingPoint(cm, from, from, -1); + var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1); + if (cutTop && cutBot) { + display.view = display.view.slice(0, cutTop.index) + .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN)) + .concat(display.view.slice(cutBot.index)); + display.viewTo += lendiff; + } else { + resetView(cm); + } + } + + var ext = display.externalMeasured; + if (ext) { + if (to < ext.lineN) + { ext.lineN += lendiff; } + else if (from < ext.lineN + ext.size) + { display.externalMeasured = null; } + } + } + + // Register a change to a single line. Type must be one of "text", + // "gutter", "class", "widget" + function regLineChange(cm, line, type) { + cm.curOp.viewChanged = true; + var display = cm.display, ext = cm.display.externalMeasured; + if (ext && line >= ext.lineN && line < ext.lineN + ext.size) + { display.externalMeasured = null; } + + if (line < display.viewFrom || line >= display.viewTo) { return } + var lineView = display.view[findViewIndex(cm, line)]; + if (lineView.node == null) { return } + var arr = lineView.changes || (lineView.changes = []); + if (indexOf(arr, type) == -1) { arr.push(type); } + } + + // Clear the view. + function resetView(cm) { + cm.display.viewFrom = cm.display.viewTo = cm.doc.first; + cm.display.view = []; + cm.display.viewOffset = 0; + } + + function viewCuttingPoint(cm, oldN, newN, dir) { + var index = findViewIndex(cm, oldN), diff, view = cm.display.view; + if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size) + { return {index: index, lineN: newN} } + var n = cm.display.viewFrom; + for (var i = 0; i < index; i++) + { n += view[i].size; } + if (n != oldN) { + if (dir > 0) { + if (index == view.length - 1) { return null } + diff = (n + view[index].size) - oldN; + index++; + } else { + diff = n - oldN; + } + oldN += diff; newN += diff; + } + while (visualLineNo(cm.doc, newN) != newN) { + if (index == (dir < 0 ? 0 : view.length - 1)) { return null } + newN += dir * view[index - (dir < 0 ? 1 : 0)].size; + index += dir; + } + return {index: index, lineN: newN} + } + + // Force the view to cover a given range, adding empty view element + // or clipping off existing ones as needed. + function adjustView(cm, from, to) { + var display = cm.display, view = display.view; + if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) { + display.view = buildViewArray(cm, from, to); + display.viewFrom = from; + } else { + if (display.viewFrom > from) + { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); } + else if (display.viewFrom < from) + { display.view = display.view.slice(findViewIndex(cm, from)); } + display.viewFrom = from; + if (display.viewTo < to) + { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); } + else if (display.viewTo > to) + { display.view = display.view.slice(0, findViewIndex(cm, to)); } + } + display.viewTo = to; + } + + // Count the number of lines in the view whose DOM representation is + // out of date (or nonexistent). + function countDirtyView(cm) { + var view = cm.display.view, dirty = 0; + for (var i = 0; i < view.length; i++) { + var lineView = view[i]; + if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; } + } + return dirty + } + + function updateSelection(cm) { + cm.display.input.showSelection(cm.display.input.prepareSelection()); + } + + function prepareSelection(cm, primary) { + if ( primary === void 0 ) primary = true; + + var doc = cm.doc, result = {}; + var curFragment = result.cursors = document.createDocumentFragment(); + var selFragment = result.selection = document.createDocumentFragment(); + + var customCursor = cm.options.$customCursor; + if (customCursor) { primary = true; } + for (var i = 0; i < doc.sel.ranges.length; i++) { + if (!primary && i == doc.sel.primIndex) { continue } + var range = doc.sel.ranges[i]; + if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue } + var collapsed = range.empty(); + if (customCursor) { + var head = customCursor(cm, range); + if (head) { drawSelectionCursor(cm, head, curFragment); } + } else if (collapsed || cm.options.showCursorWhenSelecting) { + drawSelectionCursor(cm, range.head, curFragment); + } + if (!collapsed) + { drawSelectionRange(cm, range, selFragment); } + } + return result + } + + // Draws a cursor for the given range + function drawSelectionCursor(cm, head, output) { + var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine); + + var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")); + cursor.style.left = pos.left + "px"; + cursor.style.top = pos.top + "px"; + cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"; + + if (/\bcm-fat-cursor\b/.test(cm.getWrapperElement().className)) { + var charPos = charCoords(cm, head, "div", null, null); + var width = charPos.right - charPos.left; + cursor.style.width = (width > 0 ? width : cm.defaultCharWidth()) + "px"; + } + + if (pos.other) { + // Secondary cursor, shown when on a 'jump' in bi-directional text + var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor")); + otherCursor.style.display = ""; + otherCursor.style.left = pos.other.left + "px"; + otherCursor.style.top = pos.other.top + "px"; + otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"; + } + } + + function cmpCoords(a, b) { return a.top - b.top || a.left - b.left } + + // Draws the given range as a highlighted selection + function drawSelectionRange(cm, range, output) { + var display = cm.display, doc = cm.doc; + var fragment = document.createDocumentFragment(); + var padding = paddingH(cm.display), leftSide = padding.left; + var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right; + var docLTR = doc.direction == "ltr"; + + function add(left, top, width, bottom) { + if (top < 0) { top = 0; } + top = Math.round(top); + bottom = Math.round(bottom); + fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: absolute; left: " + left + "px;\n top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px;\n height: " + (bottom - top) + "px"))); + } + + function drawForLine(line, fromArg, toArg) { + var lineObj = getLine(doc, line); + var lineLen = lineObj.text.length; + var start, end; + function coords(ch, bias) { + return charCoords(cm, Pos(line, ch), "div", lineObj, bias) + } + + function wrapX(pos, dir, side) { + var extent = wrappedLineExtentChar(cm, lineObj, null, pos); + var prop = (dir == "ltr") == (side == "after") ? "left" : "right"; + var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1); + return coords(ch, prop)[prop] + } + + var order = getOrder(lineObj, doc.direction); + iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) { + var ltr = dir == "ltr"; + var fromPos = coords(from, ltr ? "left" : "right"); + var toPos = coords(to - 1, ltr ? "right" : "left"); + + var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen; + var first = i == 0, last = !order || i == order.length - 1; + if (toPos.top - fromPos.top <= 3) { // Single line + var openLeft = (docLTR ? openStart : openEnd) && first; + var openRight = (docLTR ? openEnd : openStart) && last; + var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left; + var right = openRight ? rightSide : (ltr ? toPos : fromPos).right; + add(left, fromPos.top, right - left, fromPos.bottom); + } else { // Multiple lines + var topLeft, topRight, botLeft, botRight; + if (ltr) { + topLeft = docLTR && openStart && first ? leftSide : fromPos.left; + topRight = docLTR ? rightSide : wrapX(from, dir, "before"); + botLeft = docLTR ? leftSide : wrapX(to, dir, "after"); + botRight = docLTR && openEnd && last ? rightSide : toPos.right; + } else { + topLeft = !docLTR ? leftSide : wrapX(from, dir, "before"); + topRight = !docLTR && openStart && first ? rightSide : fromPos.right; + botLeft = !docLTR && openEnd && last ? leftSide : toPos.left; + botRight = !docLTR ? rightSide : wrapX(to, dir, "after"); + } + add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom); + if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); } + add(botLeft, toPos.top, botRight - botLeft, toPos.bottom); + } + + if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; } + if (cmpCoords(toPos, start) < 0) { start = toPos; } + if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; } + if (cmpCoords(toPos, end) < 0) { end = toPos; } + }); + return {start: start, end: end} + } + + var sFrom = range.from(), sTo = range.to(); + if (sFrom.line == sTo.line) { + drawForLine(sFrom.line, sFrom.ch, sTo.ch); + } else { + var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line); + var singleVLine = visualLine(fromLine) == visualLine(toLine); + var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end; + var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start; + if (singleVLine) { + if (leftEnd.top < rightStart.top - 2) { + add(leftEnd.right, leftEnd.top, null, leftEnd.bottom); + add(leftSide, rightStart.top, rightStart.left, rightStart.bottom); + } else { + add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom); + } + } + if (leftEnd.bottom < rightStart.top) + { add(leftSide, leftEnd.bottom, null, rightStart.top); } + } + + output.appendChild(fragment); + } + + // Cursor-blinking + function restartBlink(cm) { + if (!cm.state.focused) { return } + var display = cm.display; + clearInterval(display.blinker); + var on = true; + display.cursorDiv.style.visibility = ""; + if (cm.options.cursorBlinkRate > 0) + { display.blinker = setInterval(function () { + if (!cm.hasFocus()) { onBlur(cm); } + display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; + }, cm.options.cursorBlinkRate); } + else if (cm.options.cursorBlinkRate < 0) + { display.cursorDiv.style.visibility = "hidden"; } + } + + function ensureFocus(cm) { + if (!cm.hasFocus()) { + cm.display.input.focus(); + if (!cm.state.focused) { onFocus(cm); } + } + } + + function delayBlurEvent(cm) { + cm.state.delayingBlurEvent = true; + setTimeout(function () { if (cm.state.delayingBlurEvent) { + cm.state.delayingBlurEvent = false; + if (cm.state.focused) { onBlur(cm); } + } }, 100); + } + + function onFocus(cm, e) { + if (cm.state.delayingBlurEvent && !cm.state.draggingText) { cm.state.delayingBlurEvent = false; } + + if (cm.options.readOnly == "nocursor") { return } + if (!cm.state.focused) { + signal(cm, "focus", cm, e); + cm.state.focused = true; + addClass(cm.display.wrapper, "CodeMirror-focused"); + // This test prevents this from firing when a context + // menu is closed (since the input reset would kill the + // select-all detection hack) + if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) { + cm.display.input.reset(); + if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730 + } + cm.display.input.receivedFocus(); + } + restartBlink(cm); + } + function onBlur(cm, e) { + if (cm.state.delayingBlurEvent) { return } + + if (cm.state.focused) { + signal(cm, "blur", cm, e); + cm.state.focused = false; + rmClass(cm.display.wrapper, "CodeMirror-focused"); + } + clearInterval(cm.display.blinker); + setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150); + } + + // Read the actual heights of the rendered lines, and update their + // stored heights to match. + function updateHeightsInViewport(cm) { + var display = cm.display; + var prevBottom = display.lineDiv.offsetTop; + var viewTop = Math.max(0, display.scroller.getBoundingClientRect().top); + var oldHeight = display.lineDiv.getBoundingClientRect().top; + var mustScroll = 0; + for (var i = 0; i < display.view.length; i++) { + var cur = display.view[i], wrapping = cm.options.lineWrapping; + var height = (void 0), width = 0; + if (cur.hidden) { continue } + oldHeight += cur.line.height; + if (ie && ie_version < 8) { + var bot = cur.node.offsetTop + cur.node.offsetHeight; + height = bot - prevBottom; + prevBottom = bot; + } else { + var box = cur.node.getBoundingClientRect(); + height = box.bottom - box.top; + // Check that lines don't extend past the right of the current + // editor width + if (!wrapping && cur.text.firstChild) + { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; } + } + var diff = cur.line.height - height; + if (diff > .005 || diff < -.005) { + if (oldHeight < viewTop) { mustScroll -= diff; } + updateLineHeight(cur.line, height); + updateWidgetHeight(cur.line); + if (cur.rest) { for (var j = 0; j < cur.rest.length; j++) + { updateWidgetHeight(cur.rest[j]); } } + } + if (width > cm.display.sizerWidth) { + var chWidth = Math.ceil(width / charWidth(cm.display)); + if (chWidth > cm.display.maxLineLength) { + cm.display.maxLineLength = chWidth; + cm.display.maxLine = cur.line; + cm.display.maxLineChanged = true; + } + } + } + if (Math.abs(mustScroll) > 2) { display.scroller.scrollTop += mustScroll; } + } + + // Read and store the height of line widgets associated with the + // given line. + function updateWidgetHeight(line) { + if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) { + var w = line.widgets[i], parent = w.node.parentNode; + if (parent) { w.height = parent.offsetHeight; } + } } + } + + // Compute the lines that are visible in a given viewport (defaults + // the the current scroll position). viewport may contain top, + // height, and ensure (see op.scrollToPos) properties. + function visibleLines(display, doc, viewport) { + var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop; + top = Math.floor(top - paddingTop(display)); + var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight; + + var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom); + // Ensure is a {from: {line, ch}, to: {line, ch}} object, and + // forces those lines into the viewport (if possible). + if (viewport && viewport.ensure) { + var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line; + if (ensureFrom < from) { + from = ensureFrom; + to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight); + } else if (Math.min(ensureTo, doc.lastLine()) >= to) { + from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight); + to = ensureTo; + } + } + return {from: from, to: Math.max(to, from + 1)} + } + + // SCROLLING THINGS INTO VIEW + + // If an editor sits on the top or bottom of the window, partially + // scrolled out of view, this ensures that the cursor is visible. + function maybeScrollWindow(cm, rect) { + if (signalDOMEvent(cm, "scrollCursorIntoView")) { return } + + var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null; + var doc = display.wrapper.ownerDocument; + if (rect.top + box.top < 0) { doScroll = true; } + else if (rect.bottom + box.top > (doc.defaultView.innerHeight || doc.documentElement.clientHeight)) { doScroll = false; } + if (doScroll != null && !phantom) { + var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n top: " + (rect.top - display.viewOffset - paddingTop(cm.display)) + "px;\n height: " + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + "px;\n left: " + (rect.left) + "px; width: " + (Math.max(2, rect.right - rect.left)) + "px;")); + cm.display.lineSpace.appendChild(scrollNode); + scrollNode.scrollIntoView(doScroll); + cm.display.lineSpace.removeChild(scrollNode); + } + } + + // Scroll a given position into view (immediately), verifying that + // it actually became visible (as line heights are accurately + // measured, the position of something may 'drift' during drawing). + function scrollPosIntoView(cm, pos, end, margin) { + if (margin == null) { margin = 0; } + var rect; + if (!cm.options.lineWrapping && pos == end) { + // Set pos and end to the cursor positions around the character pos sticks to + // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch + // If pos == Pos(_, 0, "before"), pos and end are unchanged + end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos; + pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos; + } + for (var limit = 0; limit < 5; limit++) { + var changed = false; + var coords = cursorCoords(cm, pos); + var endCoords = !end || end == pos ? coords : cursorCoords(cm, end); + rect = {left: Math.min(coords.left, endCoords.left), + top: Math.min(coords.top, endCoords.top) - margin, + right: Math.max(coords.left, endCoords.left), + bottom: Math.max(coords.bottom, endCoords.bottom) + margin}; + var scrollPos = calculateScrollPos(cm, rect); + var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft; + if (scrollPos.scrollTop != null) { + updateScrollTop(cm, scrollPos.scrollTop); + if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; } + } + if (scrollPos.scrollLeft != null) { + setScrollLeft(cm, scrollPos.scrollLeft); + if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; } + } + if (!changed) { break } + } + return rect + } + + // Scroll a given set of coordinates into view (immediately). + function scrollIntoView(cm, rect) { + var scrollPos = calculateScrollPos(cm, rect); + if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); } + if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); } + } + + // Calculate a new scroll position needed to scroll the given + // rectangle into view. Returns an object with scrollTop and + // scrollLeft properties. When these are undefined, the + // vertical/horizontal position does not need to be adjusted. + function calculateScrollPos(cm, rect) { + var display = cm.display, snapMargin = textHeight(cm.display); + if (rect.top < 0) { rect.top = 0; } + var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop; + var screen = displayHeight(cm), result = {}; + if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; } + var docBottom = cm.doc.height + paddingVert(display); + var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin; + if (rect.top < screentop) { + result.scrollTop = atTop ? 0 : rect.top; + } else if (rect.bottom > screentop + screen) { + var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen); + if (newTop != screentop) { result.scrollTop = newTop; } + } + + var gutterSpace = cm.options.fixedGutter ? 0 : display.gutters.offsetWidth; + var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft - gutterSpace; + var screenw = displayWidth(cm) - display.gutters.offsetWidth; + var tooWide = rect.right - rect.left > screenw; + if (tooWide) { rect.right = rect.left + screenw; } + if (rect.left < 10) + { result.scrollLeft = 0; } + else if (rect.left < screenleft) + { result.scrollLeft = Math.max(0, rect.left + gutterSpace - (tooWide ? 0 : 10)); } + else if (rect.right > screenw + screenleft - 3) + { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; } + return result + } + + // Store a relative adjustment to the scroll position in the current + // operation (to be applied when the operation finishes). + function addToScrollTop(cm, top) { + if (top == null) { return } + resolveScrollToPos(cm); + cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top; + } + + // Make sure that at the end of the operation the current cursor is + // shown. + function ensureCursorVisible(cm) { + resolveScrollToPos(cm); + var cur = cm.getCursor(); + cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin}; + } + + function scrollToCoords(cm, x, y) { + if (x != null || y != null) { resolveScrollToPos(cm); } + if (x != null) { cm.curOp.scrollLeft = x; } + if (y != null) { cm.curOp.scrollTop = y; } + } + + function scrollToRange(cm, range) { + resolveScrollToPos(cm); + cm.curOp.scrollToPos = range; + } + + // When an operation has its scrollToPos property set, and another + // scroll action is applied before the end of the operation, this + // 'simulates' scrolling that position into view in a cheap way, so + // that the effect of intermediate scroll commands is not ignored. + function resolveScrollToPos(cm) { + var range = cm.curOp.scrollToPos; + if (range) { + cm.curOp.scrollToPos = null; + var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to); + scrollToCoordsRange(cm, from, to, range.margin); + } + } + + function scrollToCoordsRange(cm, from, to, margin) { + var sPos = calculateScrollPos(cm, { + left: Math.min(from.left, to.left), + top: Math.min(from.top, to.top) - margin, + right: Math.max(from.right, to.right), + bottom: Math.max(from.bottom, to.bottom) + margin + }); + scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop); + } + + // Sync the scrollable area and scrollbars, ensure the viewport + // covers the visible area. + function updateScrollTop(cm, val) { + if (Math.abs(cm.doc.scrollTop - val) < 2) { return } + if (!gecko) { updateDisplaySimple(cm, {top: val}); } + setScrollTop(cm, val, true); + if (gecko) { updateDisplaySimple(cm); } + startWorker(cm, 100); + } + + function setScrollTop(cm, val, forceScroll) { + val = Math.max(0, Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val)); + if (cm.display.scroller.scrollTop == val && !forceScroll) { return } + cm.doc.scrollTop = val; + cm.display.scrollbars.setScrollTop(val); + if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; } + } + + // Sync scroller and scrollbar, ensure the gutter elements are + // aligned. + function setScrollLeft(cm, val, isScroller, forceScroll) { + val = Math.max(0, Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth)); + if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return } + cm.doc.scrollLeft = val; + alignHorizontally(cm); + if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; } + cm.display.scrollbars.setScrollLeft(val); + } + + // SCROLLBARS + + // Prepare DOM reads needed to update the scrollbars. Done in one + // shot to minimize update/measure roundtrips. + function measureForScrollbars(cm) { + var d = cm.display, gutterW = d.gutters.offsetWidth; + var docH = Math.round(cm.doc.height + paddingVert(cm.display)); + return { + clientHeight: d.scroller.clientHeight, + viewHeight: d.wrapper.clientHeight, + scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth, + viewWidth: d.wrapper.clientWidth, + barLeft: cm.options.fixedGutter ? gutterW : 0, + docHeight: docH, + scrollHeight: docH + scrollGap(cm) + d.barHeight, + nativeBarWidth: d.nativeBarWidth, + gutterWidth: gutterW + } + } + + var NativeScrollbars = function(place, scroll, cm) { + this.cm = cm; + var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar"); + var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar"); + vert.tabIndex = horiz.tabIndex = -1; + place(vert); place(horiz); + + on(vert, "scroll", function () { + if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); } + }); + on(horiz, "scroll", function () { + if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); } + }); + + this.checkedZeroWidth = false; + // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). + if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; } + }; + + NativeScrollbars.prototype.update = function (measure) { + var needsH = measure.scrollWidth > measure.clientWidth + 1; + var needsV = measure.scrollHeight > measure.clientHeight + 1; + var sWidth = measure.nativeBarWidth; + + if (needsV) { + this.vert.style.display = "block"; + this.vert.style.bottom = needsH ? sWidth + "px" : "0"; + var totalHeight = measure.viewHeight - (needsH ? sWidth : 0); + // A bug in IE8 can cause this value to be negative, so guard it. + this.vert.firstChild.style.height = + Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"; + } else { + this.vert.scrollTop = 0; + this.vert.style.display = ""; + this.vert.firstChild.style.height = "0"; + } + + if (needsH) { + this.horiz.style.display = "block"; + this.horiz.style.right = needsV ? sWidth + "px" : "0"; + this.horiz.style.left = measure.barLeft + "px"; + var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0); + this.horiz.firstChild.style.width = + Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px"; + } else { + this.horiz.style.display = ""; + this.horiz.firstChild.style.width = "0"; + } + + if (!this.checkedZeroWidth && measure.clientHeight > 0) { + if (sWidth == 0) { this.zeroWidthHack(); } + this.checkedZeroWidth = true; + } + + return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0} + }; + + NativeScrollbars.prototype.setScrollLeft = function (pos) { + if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; } + if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); } + }; + + NativeScrollbars.prototype.setScrollTop = function (pos) { + if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; } + if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); } + }; + + NativeScrollbars.prototype.zeroWidthHack = function () { + var w = mac && !mac_geMountainLion ? "12px" : "18px"; + this.horiz.style.height = this.vert.style.width = w; + this.horiz.style.visibility = this.vert.style.visibility = "hidden"; + this.disableHoriz = new Delayed; + this.disableVert = new Delayed; + }; + + NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) { + bar.style.visibility = ""; + function maybeDisable() { + // To find out whether the scrollbar is still visible, we + // check whether the element under the pixel in the bottom + // right corner of the scrollbar box is the scrollbar box + // itself (when the bar is still visible) or its filler child + // (when the bar is hidden). If it is still visible, we keep + // it enabled, if it's hidden, we disable pointer events. + var box = bar.getBoundingClientRect(); + var elt = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2) + : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1); + if (elt != bar) { bar.style.visibility = "hidden"; } + else { delay.set(1000, maybeDisable); } + } + delay.set(1000, maybeDisable); + }; + + NativeScrollbars.prototype.clear = function () { + var parent = this.horiz.parentNode; + parent.removeChild(this.horiz); + parent.removeChild(this.vert); + }; + + var NullScrollbars = function () {}; + + NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} }; + NullScrollbars.prototype.setScrollLeft = function () {}; + NullScrollbars.prototype.setScrollTop = function () {}; + NullScrollbars.prototype.clear = function () {}; + + function updateScrollbars(cm, measure) { + if (!measure) { measure = measureForScrollbars(cm); } + var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight; + updateScrollbarsInner(cm, measure); + for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) { + if (startWidth != cm.display.barWidth && cm.options.lineWrapping) + { updateHeightsInViewport(cm); } + updateScrollbarsInner(cm, measureForScrollbars(cm)); + startWidth = cm.display.barWidth; startHeight = cm.display.barHeight; + } + } + + // Re-synchronize the fake scrollbars with the actual size of the + // content. + function updateScrollbarsInner(cm, measure) { + var d = cm.display; + var sizes = d.scrollbars.update(measure); + + d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"; + d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"; + d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"; + + if (sizes.right && sizes.bottom) { + d.scrollbarFiller.style.display = "block"; + d.scrollbarFiller.style.height = sizes.bottom + "px"; + d.scrollbarFiller.style.width = sizes.right + "px"; + } else { d.scrollbarFiller.style.display = ""; } + if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) { + d.gutterFiller.style.display = "block"; + d.gutterFiller.style.height = sizes.bottom + "px"; + d.gutterFiller.style.width = measure.gutterWidth + "px"; + } else { d.gutterFiller.style.display = ""; } + } + + var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}; + + function initScrollbars(cm) { + if (cm.display.scrollbars) { + cm.display.scrollbars.clear(); + if (cm.display.scrollbars.addClass) + { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); } + } + + cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) { + cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller); + // Prevent clicks in the scrollbars from killing focus + on(node, "mousedown", function () { + if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); } + }); + node.setAttribute("cm-not-content", "true"); + }, function (pos, axis) { + if (axis == "horizontal") { setScrollLeft(cm, pos); } + else { updateScrollTop(cm, pos); } + }, cm); + if (cm.display.scrollbars.addClass) + { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); } + } + + // Operations are used to wrap a series of changes to the editor + // state in such a way that each change won't have to update the + // cursor and display (which would be awkward, slow, and + // error-prone). Instead, display updates are batched and then all + // combined and executed at once. + + var nextOpId = 0; + // Start a new operation. + function startOperation(cm) { + cm.curOp = { + cm: cm, + viewChanged: false, // Flag that indicates that lines might need to be redrawn + startHeight: cm.doc.height, // Used to detect need to update scrollbar + forceUpdate: false, // Used to force a redraw + updateInput: 0, // Whether to reset the input textarea + typing: false, // Whether this reset should be careful to leave existing text (for compositing) + changeObjs: null, // Accumulated changes, for firing change events + cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on + cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already + selectionChanged: false, // Whether the selection needs to be redrawn + updateMaxLine: false, // Set when the widest line needs to be determined anew + scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet + scrollToPos: null, // Used to scroll to a specific position + focus: false, + id: ++nextOpId, // Unique ID + markArrays: null // Used by addMarkedSpan + }; + pushOperation(cm.curOp); + } + + // Finish an operation, updating the display and signalling delayed events + function endOperation(cm) { + var op = cm.curOp; + if (op) { finishOperation(op, function (group) { + for (var i = 0; i < group.ops.length; i++) + { group.ops[i].cm.curOp = null; } + endOperations(group); + }); } + } + + // The DOM updates done when an operation finishes are batched so + // that the minimum number of relayouts are required. + function endOperations(group) { + var ops = group.ops; + for (var i = 0; i < ops.length; i++) // Read DOM + { endOperation_R1(ops[i]); } + for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe) + { endOperation_W1(ops[i$1]); } + for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM + { endOperation_R2(ops[i$2]); } + for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe) + { endOperation_W2(ops[i$3]); } + for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM + { endOperation_finish(ops[i$4]); } + } + + function endOperation_R1(op) { + var cm = op.cm, display = cm.display; + maybeClipScrollbars(cm); + if (op.updateMaxLine) { findMaxLine(cm); } + + op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null || + op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom || + op.scrollToPos.to.line >= display.viewTo) || + display.maxLineChanged && cm.options.lineWrapping; + op.update = op.mustUpdate && + new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate); + } + + function endOperation_W1(op) { + op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update); + } + + function endOperation_R2(op) { + var cm = op.cm, display = cm.display; + if (op.updatedDisplay) { updateHeightsInViewport(cm); } + + op.barMeasure = measureForScrollbars(cm); + + // If the max line changed since it was last measured, measure it, + // and ensure the document's width matches it. + // updateDisplay_W2 will use these properties to do the actual resizing + if (display.maxLineChanged && !cm.options.lineWrapping) { + op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3; + cm.display.sizerWidth = op.adjustWidthTo; + op.barMeasure.scrollWidth = + Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth); + op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm)); + } + + if (op.updatedDisplay || op.selectionChanged) + { op.preparedSelection = display.input.prepareSelection(); } + } + + function endOperation_W2(op) { + var cm = op.cm; + + if (op.adjustWidthTo != null) { + cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"; + if (op.maxScrollLeft < cm.doc.scrollLeft) + { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); } + cm.display.maxLineChanged = false; + } + + var takeFocus = op.focus && op.focus == activeElt(doc(cm)); + if (op.preparedSelection) + { cm.display.input.showSelection(op.preparedSelection, takeFocus); } + if (op.updatedDisplay || op.startHeight != cm.doc.height) + { updateScrollbars(cm, op.barMeasure); } + if (op.updatedDisplay) + { setDocumentHeight(cm, op.barMeasure); } + + if (op.selectionChanged) { restartBlink(cm); } + + if (cm.state.focused && op.updateInput) + { cm.display.input.reset(op.typing); } + if (takeFocus) { ensureFocus(op.cm); } + } + + function endOperation_finish(op) { + var cm = op.cm, display = cm.display, doc = cm.doc; + + if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); } + + // Abort mouse wheel delta measurement, when scrolling explicitly + if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos)) + { display.wheelStartX = display.wheelStartY = null; } + + // Propagate the scroll position to the actual DOM scroller + if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); } + + if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); } + // If we need to scroll a specific position into view, do so. + if (op.scrollToPos) { + var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from), + clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin); + maybeScrollWindow(cm, rect); + } + + // Fire events for markers that are hidden/unidden by editing or + // undoing + var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers; + if (hidden) { for (var i = 0; i < hidden.length; ++i) + { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } } + if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1) + { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } } + + if (display.wrapper.offsetHeight) + { doc.scrollTop = cm.display.scroller.scrollTop; } + + // Fire change events, and delayed event handlers + if (op.changeObjs) + { signal(cm, "changes", cm, op.changeObjs); } + if (op.update) + { op.update.finish(); } + } + + // Run the given function in an operation + function runInOp(cm, f) { + if (cm.curOp) { return f() } + startOperation(cm); + try { return f() } + finally { endOperation(cm); } + } + // Wraps a function in an operation. Returns the wrapped function. + function operation(cm, f) { + return function() { + if (cm.curOp) { return f.apply(cm, arguments) } + startOperation(cm); + try { return f.apply(cm, arguments) } + finally { endOperation(cm); } + } + } + // Used to add methods to editor and doc instances, wrapping them in + // operations. + function methodOp(f) { + return function() { + if (this.curOp) { return f.apply(this, arguments) } + startOperation(this); + try { return f.apply(this, arguments) } + finally { endOperation(this); } + } + } + function docMethodOp(f) { + return function() { + var cm = this.cm; + if (!cm || cm.curOp) { return f.apply(this, arguments) } + startOperation(cm); + try { return f.apply(this, arguments) } + finally { endOperation(cm); } + } + } + + // HIGHLIGHT WORKER + + function startWorker(cm, time) { + if (cm.doc.highlightFrontier < cm.display.viewTo) + { cm.state.highlight.set(time, bind(highlightWorker, cm)); } + } + + function highlightWorker(cm) { + var doc = cm.doc; + if (doc.highlightFrontier >= cm.display.viewTo) { return } + var end = +new Date + cm.options.workTime; + var context = getContextBefore(cm, doc.highlightFrontier); + var changedLines = []; + + doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) { + if (context.line >= cm.display.viewFrom) { // Visible + var oldStyles = line.styles; + var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null; + var highlighted = highlightLine(cm, line, context, true); + if (resetState) { context.state = resetState; } + line.styles = highlighted.styles; + var oldCls = line.styleClasses, newCls = highlighted.classes; + if (newCls) { line.styleClasses = newCls; } + else if (oldCls) { line.styleClasses = null; } + var ischange = !oldStyles || oldStyles.length != line.styles.length || + oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass); + for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; } + if (ischange) { changedLines.push(context.line); } + line.stateAfter = context.save(); + context.nextLine(); + } else { + if (line.text.length <= cm.options.maxHighlightLength) + { processLine(cm, line.text, context); } + line.stateAfter = context.line % 5 == 0 ? context.save() : null; + context.nextLine(); + } + if (+new Date > end) { + startWorker(cm, cm.options.workDelay); + return true + } + }); + doc.highlightFrontier = context.line; + doc.modeFrontier = Math.max(doc.modeFrontier, context.line); + if (changedLines.length) { runInOp(cm, function () { + for (var i = 0; i < changedLines.length; i++) + { regLineChange(cm, changedLines[i], "text"); } + }); } + } + + // DISPLAY DRAWING + + var DisplayUpdate = function(cm, viewport, force) { + var display = cm.display; + + this.viewport = viewport; + // Store some values that we'll need later (but don't want to force a relayout for) + this.visible = visibleLines(display, cm.doc, viewport); + this.editorIsHidden = !display.wrapper.offsetWidth; + this.wrapperHeight = display.wrapper.clientHeight; + this.wrapperWidth = display.wrapper.clientWidth; + this.oldDisplayWidth = displayWidth(cm); + this.force = force; + this.dims = getDimensions(cm); + this.events = []; + }; + + DisplayUpdate.prototype.signal = function (emitter, type) { + if (hasHandler(emitter, type)) + { this.events.push(arguments); } + }; + DisplayUpdate.prototype.finish = function () { + for (var i = 0; i < this.events.length; i++) + { signal.apply(null, this.events[i]); } + }; + + function maybeClipScrollbars(cm) { + var display = cm.display; + if (!display.scrollbarsClipped && display.scroller.offsetWidth) { + display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth; + display.heightForcer.style.height = scrollGap(cm) + "px"; + display.sizer.style.marginBottom = -display.nativeBarWidth + "px"; + display.sizer.style.borderRightWidth = scrollGap(cm) + "px"; + display.scrollbarsClipped = true; + } + } + + function selectionSnapshot(cm) { + if (cm.hasFocus()) { return null } + var active = activeElt(doc(cm)); + if (!active || !contains(cm.display.lineDiv, active)) { return null } + var result = {activeElt: active}; + if (window.getSelection) { + var sel = win(cm).getSelection(); + if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) { + result.anchorNode = sel.anchorNode; + result.anchorOffset = sel.anchorOffset; + result.focusNode = sel.focusNode; + result.focusOffset = sel.focusOffset; + } + } + return result + } + + function restoreSelection(snapshot) { + if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt(snapshot.activeElt.ownerDocument)) { return } + snapshot.activeElt.focus(); + if (!/^(INPUT|TEXTAREA)$/.test(snapshot.activeElt.nodeName) && + snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) { + var doc = snapshot.activeElt.ownerDocument; + var sel = doc.defaultView.getSelection(), range = doc.createRange(); + range.setEnd(snapshot.anchorNode, snapshot.anchorOffset); + range.collapse(false); + sel.removeAllRanges(); + sel.addRange(range); + sel.extend(snapshot.focusNode, snapshot.focusOffset); + } + } + + // Does the actual updating of the line display. Bails out + // (returning false) when there is nothing to be done and forced is + // false. + function updateDisplayIfNeeded(cm, update) { + var display = cm.display, doc = cm.doc; + + if (update.editorIsHidden) { + resetView(cm); + return false + } + + // Bail out if the visible area is already rendered and nothing changed. + if (!update.force && + update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo && + (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) && + display.renderedView == display.view && countDirtyView(cm) == 0) + { return false } + + if (maybeUpdateLineNumberWidth(cm)) { + resetView(cm); + update.dims = getDimensions(cm); + } + + // Compute a suitable new viewport (from & to) + var end = doc.first + doc.size; + var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first); + var to = Math.min(end, update.visible.to + cm.options.viewportMargin); + if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); } + if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); } + if (sawCollapsedSpans) { + from = visualLineNo(cm.doc, from); + to = visualLineEndNo(cm.doc, to); + } + + var different = from != display.viewFrom || to != display.viewTo || + display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth; + adjustView(cm, from, to); + + display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom)); + // Position the mover div to align with the current scroll position + cm.display.mover.style.top = display.viewOffset + "px"; + + var toUpdate = countDirtyView(cm); + if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view && + (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo)) + { return false } + + // For big changes, we hide the enclosing element during the + // update, since that speeds up the operations on most browsers. + var selSnapshot = selectionSnapshot(cm); + if (toUpdate > 4) { display.lineDiv.style.display = "none"; } + patchDisplay(cm, display.updateLineNumbers, update.dims); + if (toUpdate > 4) { display.lineDiv.style.display = ""; } + display.renderedView = display.view; + // There might have been a widget with a focused element that got + // hidden or updated, if so re-focus it. + restoreSelection(selSnapshot); + + // Prevent selection and cursors from interfering with the scroll + // width and height. + removeChildren(display.cursorDiv); + removeChildren(display.selectionDiv); + display.gutters.style.height = display.sizer.style.minHeight = 0; + + if (different) { + display.lastWrapHeight = update.wrapperHeight; + display.lastWrapWidth = update.wrapperWidth; + startWorker(cm, 400); + } + + display.updateLineNumbers = null; + + return true + } + + function postUpdateDisplay(cm, update) { + var viewport = update.viewport; + + for (var first = true;; first = false) { + if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) { + // Clip forced viewport to actual scrollable area. + if (viewport && viewport.top != null) + { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; } + // Updated line heights might result in the drawn area not + // actually covering the viewport. Keep looping until it does. + update.visible = visibleLines(cm.display, cm.doc, viewport); + if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo) + { break } + } else if (first) { + update.visible = visibleLines(cm.display, cm.doc, viewport); + } + if (!updateDisplayIfNeeded(cm, update)) { break } + updateHeightsInViewport(cm); + var barMeasure = measureForScrollbars(cm); + updateSelection(cm); + updateScrollbars(cm, barMeasure); + setDocumentHeight(cm, barMeasure); + update.force = false; + } + + update.signal(cm, "update", cm); + if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) { + update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo); + cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo; + } + } + + function updateDisplaySimple(cm, viewport) { + var update = new DisplayUpdate(cm, viewport); + if (updateDisplayIfNeeded(cm, update)) { + updateHeightsInViewport(cm); + postUpdateDisplay(cm, update); + var barMeasure = measureForScrollbars(cm); + updateSelection(cm); + updateScrollbars(cm, barMeasure); + setDocumentHeight(cm, barMeasure); + update.finish(); + } + } + + // Sync the actual display DOM structure with display.view, removing + // nodes for lines that are no longer in view, and creating the ones + // that are not there yet, and updating the ones that are out of + // date. + function patchDisplay(cm, updateNumbersFrom, dims) { + var display = cm.display, lineNumbers = cm.options.lineNumbers; + var container = display.lineDiv, cur = container.firstChild; + + function rm(node) { + var next = node.nextSibling; + // Works around a throw-scroll bug in OS X Webkit + if (webkit && mac && cm.display.currentWheelTarget == node) + { node.style.display = "none"; } + else + { node.parentNode.removeChild(node); } + return next + } + + var view = display.view, lineN = display.viewFrom; + // Loop over the elements in the view, syncing cur (the DOM nodes + // in display.lineDiv) with the view as we go. + for (var i = 0; i < view.length; i++) { + var lineView = view[i]; + if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet + var node = buildLineElement(cm, lineView, lineN, dims); + container.insertBefore(node, cur); + } else { // Already drawn + while (cur != lineView.node) { cur = rm(cur); } + var updateNumber = lineNumbers && updateNumbersFrom != null && + updateNumbersFrom <= lineN && lineView.lineNumber; + if (lineView.changes) { + if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; } + updateLineForChanges(cm, lineView, lineN, dims); + } + if (updateNumber) { + removeChildren(lineView.lineNumber); + lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN))); + } + cur = lineView.node.nextSibling; + } + lineN += lineView.size; + } + while (cur) { cur = rm(cur); } + } + + function updateGutterSpace(display) { + var width = display.gutters.offsetWidth; + display.sizer.style.marginLeft = width + "px"; + // Send an event to consumers responding to changes in gutter width. + signalLater(display, "gutterChanged", display); + } + + function setDocumentHeight(cm, measure) { + cm.display.sizer.style.minHeight = measure.docHeight + "px"; + cm.display.heightForcer.style.top = measure.docHeight + "px"; + cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px"; + } + + // Re-align line numbers and gutter marks to compensate for + // horizontal scrolling. + function alignHorizontally(cm) { + var display = cm.display, view = display.view; + if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return } + var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft; + var gutterW = display.gutters.offsetWidth, left = comp + "px"; + for (var i = 0; i < view.length; i++) { if (!view[i].hidden) { + if (cm.options.fixedGutter) { + if (view[i].gutter) + { view[i].gutter.style.left = left; } + if (view[i].gutterBackground) + { view[i].gutterBackground.style.left = left; } + } + var align = view[i].alignable; + if (align) { for (var j = 0; j < align.length; j++) + { align[j].style.left = left; } } + } } + if (cm.options.fixedGutter) + { display.gutters.style.left = (comp + gutterW) + "px"; } + } + + // Used to ensure that the line number gutter is still the right + // size for the current document size. Returns true when an update + // is needed. + function maybeUpdateLineNumberWidth(cm) { + if (!cm.options.lineNumbers) { return false } + var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display; + if (last.length != display.lineNumChars) { + var test = display.measure.appendChild(elt("div", [elt("div", last)], + "CodeMirror-linenumber CodeMirror-gutter-elt")); + var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW; + display.lineGutter.style.width = ""; + display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1; + display.lineNumWidth = display.lineNumInnerWidth + padding; + display.lineNumChars = display.lineNumInnerWidth ? last.length : -1; + display.lineGutter.style.width = display.lineNumWidth + "px"; + updateGutterSpace(cm.display); + return true + } + return false + } + + function getGutters(gutters, lineNumbers) { + var result = [], sawLineNumbers = false; + for (var i = 0; i < gutters.length; i++) { + var name = gutters[i], style = null; + if (typeof name != "string") { style = name.style; name = name.className; } + if (name == "CodeMirror-linenumbers") { + if (!lineNumbers) { continue } + else { sawLineNumbers = true; } + } + result.push({className: name, style: style}); + } + if (lineNumbers && !sawLineNumbers) { result.push({className: "CodeMirror-linenumbers", style: null}); } + return result + } + + // Rebuild the gutter elements, ensure the margin to the left of the + // code matches their width. + function renderGutters(display) { + var gutters = display.gutters, specs = display.gutterSpecs; + removeChildren(gutters); + display.lineGutter = null; + for (var i = 0; i < specs.length; ++i) { + var ref = specs[i]; + var className = ref.className; + var style = ref.style; + var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + className)); + if (style) { gElt.style.cssText = style; } + if (className == "CodeMirror-linenumbers") { + display.lineGutter = gElt; + gElt.style.width = (display.lineNumWidth || 1) + "px"; + } + } + gutters.style.display = specs.length ? "" : "none"; + updateGutterSpace(display); + } + + function updateGutters(cm) { + renderGutters(cm.display); + regChange(cm); + alignHorizontally(cm); + } + + // The display handles the DOM integration, both for input reading + // and content drawing. It holds references to DOM nodes and + // display-related state. + + function Display(place, doc, input, options) { + var d = this; + this.input = input; + + // Covers bottom-right square when both scrollbars are present. + d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); + d.scrollbarFiller.setAttribute("cm-not-content", "true"); + // Covers bottom of gutter when coverGutterNextToScrollbar is on + // and h scrollbar is present. + d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler"); + d.gutterFiller.setAttribute("cm-not-content", "true"); + // Will contain the actual code, positioned to cover the viewport. + d.lineDiv = eltP("div", null, "CodeMirror-code"); + // Elements are added to these to represent selection and cursors. + d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); + d.cursorDiv = elt("div", null, "CodeMirror-cursors"); + // A visibility: hidden element used to find the size of things. + d.measure = elt("div", null, "CodeMirror-measure"); + // When lines outside of the viewport are measured, they are drawn in this. + d.lineMeasure = elt("div", null, "CodeMirror-measure"); + // Wraps everything that needs to exist inside the vertically-padded coordinate system + d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv], + null, "position: relative; outline: none"); + var lines = eltP("div", [d.lineSpace], "CodeMirror-lines"); + // Moved around its parent to cover visible view. + d.mover = elt("div", [lines], null, "position: relative"); + // Set to the height of the document, allowing scrolling. + d.sizer = elt("div", [d.mover], "CodeMirror-sizer"); + d.sizerWidth = null; + // Behavior of elts with overflow: auto and padding is + // inconsistent across browsers. This is used to ensure the + // scrollable area is big enough. + d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;"); + // Will contain the gutters, if any. + d.gutters = elt("div", null, "CodeMirror-gutters"); + d.lineGutter = null; + // Actual scrollable element. + d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll"); + d.scroller.setAttribute("tabIndex", "-1"); + // The element in which the editor lives. + d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror"); + // See #6982. FIXME remove when this has been fixed for a while in Chrome + if (chrome && chrome_version >= 105) { d.wrapper.style.clipPath = "inset(0px)"; } + + // This attribute is respected by automatic translation systems such as Google Translate, + // and may also be respected by tools used by human translators. + d.wrapper.setAttribute('translate', 'no'); + + // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported) + if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; } + if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; } + + if (place) { + if (place.appendChild) { place.appendChild(d.wrapper); } + else { place(d.wrapper); } + } + + // Current rendered range (may be bigger than the view window). + d.viewFrom = d.viewTo = doc.first; + d.reportedViewFrom = d.reportedViewTo = doc.first; + // Information about the rendered lines. + d.view = []; + d.renderedView = null; + // Holds info about a single rendered line when it was rendered + // for measurement, while not in view. + d.externalMeasured = null; + // Empty space (in pixels) above the view + d.viewOffset = 0; + d.lastWrapHeight = d.lastWrapWidth = 0; + d.updateLineNumbers = null; + + d.nativeBarWidth = d.barHeight = d.barWidth = 0; + d.scrollbarsClipped = false; + + // Used to only resize the line number gutter when necessary (when + // the amount of lines crosses a boundary that makes its width change) + d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null; + // Set to true when a non-horizontal-scrolling line widget is + // added. As an optimization, line widget aligning is skipped when + // this is false. + d.alignWidgets = false; + + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; + + // Tracks the maximum line length so that the horizontal scrollbar + // can be kept static when scrolling. + d.maxLine = null; + d.maxLineLength = 0; + d.maxLineChanged = false; + + // Used for measuring wheel scrolling granularity + d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null; + + // True when shift is held down. + d.shift = false; + + // Used to track whether anything happened since the context menu + // was opened. + d.selForContextMenu = null; + + d.activeTouch = null; + + d.gutterSpecs = getGutters(options.gutters, options.lineNumbers); + renderGutters(d); + + input.init(d); + } + + // Since the delta values reported on mouse wheel events are + // unstandardized between browsers and even browser versions, and + // generally horribly unpredictable, this code starts by measuring + // the scroll effect that the first few mouse wheel events have, + // and, from that, detects the way it can convert deltas to pixel + // offsets afterwards. + // + // The reason we want to know the amount a wheel event will scroll + // is that it gives us a chance to update the display before the + // actual scrolling happens, reducing flickering. + + var wheelSamples = 0, wheelPixelsPerUnit = null; + // Fill in a browser-detected starting value on browsers where we + // know one. These don't have to be accurate -- the result of them + // being wrong would just be a slight flicker on the first wheel + // scroll (if it is large enough). + if (ie) { wheelPixelsPerUnit = -.53; } + else if (gecko) { wheelPixelsPerUnit = 15; } + else if (chrome) { wheelPixelsPerUnit = -.7; } + else if (safari) { wheelPixelsPerUnit = -1/3; } + + function wheelEventDelta(e) { + var dx = e.wheelDeltaX, dy = e.wheelDeltaY; + if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; } + if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; } + else if (dy == null) { dy = e.wheelDelta; } + return {x: dx, y: dy} + } + function wheelEventPixels(e) { + var delta = wheelEventDelta(e); + delta.x *= wheelPixelsPerUnit; + delta.y *= wheelPixelsPerUnit; + return delta + } + + function onScrollWheel(cm, e) { + // On Chrome 102, viewport updates somehow stop wheel-based + // scrolling. Turning off pointer events during the scroll seems + // to avoid the issue. + if (chrome && chrome_version == 102) { + if (cm.display.chromeScrollHack == null) { cm.display.sizer.style.pointerEvents = "none"; } + else { clearTimeout(cm.display.chromeScrollHack); } + cm.display.chromeScrollHack = setTimeout(function () { + cm.display.chromeScrollHack = null; + cm.display.sizer.style.pointerEvents = ""; + }, 100); + } + var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y; + var pixelsPerUnit = wheelPixelsPerUnit; + if (e.deltaMode === 0) { + dx = e.deltaX; + dy = e.deltaY; + pixelsPerUnit = 1; + } + + var display = cm.display, scroll = display.scroller; + // Quit if there's nothing to scroll here + var canScrollX = scroll.scrollWidth > scroll.clientWidth; + var canScrollY = scroll.scrollHeight > scroll.clientHeight; + if (!(dx && canScrollX || dy && canScrollY)) { return } + + // Webkit browsers on OS X abort momentum scrolls when the target + // of the scroll event is removed from the scrollable element. + // This hack (see related code in patchDisplay) makes sure the + // element is kept around. + if (dy && mac && webkit) { + outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) { + for (var i = 0; i < view.length; i++) { + if (view[i].node == cur) { + cm.display.currentWheelTarget = cur; + break outer + } + } + } + } + + // On some browsers, horizontal scrolling will cause redraws to + // happen before the gutter has been realigned, causing it to + // wriggle around in a most unseemly way. When we have an + // estimated pixels/delta value, we just handle horizontal + // scrolling entirely here. It'll be slightly off from native, but + // better than glitching out. + if (dx && !gecko && !presto && pixelsPerUnit != null) { + if (dy && canScrollY) + { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * pixelsPerUnit)); } + setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * pixelsPerUnit)); + // Only prevent default scrolling if vertical scrolling is + // actually possible. Otherwise, it causes vertical scroll + // jitter on OSX trackpads when deltaX is small and deltaY + // is large (issue #3579) + if (!dy || (dy && canScrollY)) + { e_preventDefault(e); } + display.wheelStartX = null; // Abort measurement, if in progress + return + } + + // 'Project' the visible viewport to cover the area that is being + // scrolled into view (if we know enough to estimate it). + if (dy && pixelsPerUnit != null) { + var pixels = dy * pixelsPerUnit; + var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight; + if (pixels < 0) { top = Math.max(0, top + pixels - 50); } + else { bot = Math.min(cm.doc.height, bot + pixels + 50); } + updateDisplaySimple(cm, {top: top, bottom: bot}); + } + + if (wheelSamples < 20 && e.deltaMode !== 0) { + if (display.wheelStartX == null) { + display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop; + display.wheelDX = dx; display.wheelDY = dy; + setTimeout(function () { + if (display.wheelStartX == null) { return } + var movedX = scroll.scrollLeft - display.wheelStartX; + var movedY = scroll.scrollTop - display.wheelStartY; + var sample = (movedY && display.wheelDY && movedY / display.wheelDY) || + (movedX && display.wheelDX && movedX / display.wheelDX); + display.wheelStartX = display.wheelStartY = null; + if (!sample) { return } + wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1); + ++wheelSamples; + }, 200); + } else { + display.wheelDX += dx; display.wheelDY += dy; + } + } + } + + // Selection objects are immutable. A new one is created every time + // the selection changes. A selection is one or more non-overlapping + // (and non-touching) ranges, sorted, and an integer that indicates + // which one is the primary selection (the one that's scrolled into + // view, that getCursor returns, etc). + var Selection = function(ranges, primIndex) { + this.ranges = ranges; + this.primIndex = primIndex; + }; + + Selection.prototype.primary = function () { return this.ranges[this.primIndex] }; + + Selection.prototype.equals = function (other) { + if (other == this) { return true } + if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false } + for (var i = 0; i < this.ranges.length; i++) { + var here = this.ranges[i], there = other.ranges[i]; + if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false } + } + return true + }; + + Selection.prototype.deepCopy = function () { + var out = []; + for (var i = 0; i < this.ranges.length; i++) + { out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)); } + return new Selection(out, this.primIndex) + }; + + Selection.prototype.somethingSelected = function () { + for (var i = 0; i < this.ranges.length; i++) + { if (!this.ranges[i].empty()) { return true } } + return false + }; + + Selection.prototype.contains = function (pos, end) { + if (!end) { end = pos; } + for (var i = 0; i < this.ranges.length; i++) { + var range = this.ranges[i]; + if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) + { return i } + } + return -1 + }; + + var Range = function(anchor, head) { + this.anchor = anchor; this.head = head; + }; + + Range.prototype.from = function () { return minPos(this.anchor, this.head) }; + Range.prototype.to = function () { return maxPos(this.anchor, this.head) }; + Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch }; + + // Take an unsorted, potentially overlapping set of ranges, and + // build a selection out of it. 'Consumes' ranges array (modifying + // it). + function normalizeSelection(cm, ranges, primIndex) { + var mayTouch = cm && cm.options.selectionsMayTouch; + var prim = ranges[primIndex]; + ranges.sort(function (a, b) { return cmp(a.from(), b.from()); }); + primIndex = indexOf(ranges, prim); + for (var i = 1; i < ranges.length; i++) { + var cur = ranges[i], prev = ranges[i - 1]; + var diff = cmp(prev.to(), cur.from()); + if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) { + var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to()); + var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head; + if (i <= primIndex) { --primIndex; } + ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to)); + } + } + return new Selection(ranges, primIndex) + } + + function simpleSelection(anchor, head) { + return new Selection([new Range(anchor, head || anchor)], 0) + } + + // Compute the position of the end of a change (its 'to' property + // refers to the pre-change end). + function changeEnd(change) { + if (!change.text) { return change.to } + return Pos(change.from.line + change.text.length - 1, + lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)) + } + + // Adjust a position to refer to the post-change position of the + // same text, or the end of the change if the change covers it. + function adjustForChange(pos, change) { + if (cmp(pos, change.from) < 0) { return pos } + if (cmp(pos, change.to) <= 0) { return changeEnd(change) } + + var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch; + if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; } + return Pos(line, ch) + } + + function computeSelAfterChange(doc, change) { + var out = []; + for (var i = 0; i < doc.sel.ranges.length; i++) { + var range = doc.sel.ranges[i]; + out.push(new Range(adjustForChange(range.anchor, change), + adjustForChange(range.head, change))); + } + return normalizeSelection(doc.cm, out, doc.sel.primIndex) + } + + function offsetPos(pos, old, nw) { + if (pos.line == old.line) + { return Pos(nw.line, pos.ch - old.ch + nw.ch) } + else + { return Pos(nw.line + (pos.line - old.line), pos.ch) } + } + + // Used by replaceSelections to allow moving the selection to the + // start or around the replaced test. Hint may be "start" or "around". + function computeReplacedSel(doc, changes, hint) { + var out = []; + var oldPrev = Pos(doc.first, 0), newPrev = oldPrev; + for (var i = 0; i < changes.length; i++) { + var change = changes[i]; + var from = offsetPos(change.from, oldPrev, newPrev); + var to = offsetPos(changeEnd(change), oldPrev, newPrev); + oldPrev = change.to; + newPrev = to; + if (hint == "around") { + var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0; + out[i] = new Range(inv ? to : from, inv ? from : to); + } else { + out[i] = new Range(from, from); + } + } + return new Selection(out, doc.sel.primIndex) + } + + // Used to get the editor into a consistent state again when options change. + + function loadMode(cm) { + cm.doc.mode = getMode(cm.options, cm.doc.modeOption); + resetModeState(cm); + } + + function resetModeState(cm) { + cm.doc.iter(function (line) { + if (line.stateAfter) { line.stateAfter = null; } + if (line.styles) { line.styles = null; } + }); + cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first; + startWorker(cm, 100); + cm.state.modeGen++; + if (cm.curOp) { regChange(cm); } + } + + // DOCUMENT DATA STRUCTURE + + // By default, updates that start and end at the beginning of a line + // are treated specially, in order to make the association of line + // widgets and marker elements with the text behave more intuitive. + function isWholeLineUpdate(doc, change) { + return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" && + (!doc.cm || doc.cm.options.wholeLineUpdateBefore) + } + + // Perform a change on the document data structure. + function updateDoc(doc, change, markedSpans, estimateHeight) { + function spansFor(n) {return markedSpans ? markedSpans[n] : null} + function update(line, text, spans) { + updateLine(line, text, spans, estimateHeight); + signalLater(line, "change", line, change); + } + function linesFor(start, end) { + var result = []; + for (var i = start; i < end; ++i) + { result.push(new Line(text[i], spansFor(i), estimateHeight)); } + return result + } + + var from = change.from, to = change.to, text = change.text; + var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line); + var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line; + + // Adjust the line structure + if (change.full) { + doc.insert(0, linesFor(0, text.length)); + doc.remove(text.length, doc.size - text.length); + } else if (isWholeLineUpdate(doc, change)) { + // This is a whole-line replace. Treated specially to make + // sure line objects move the way they are supposed to. + var added = linesFor(0, text.length - 1); + update(lastLine, lastLine.text, lastSpans); + if (nlines) { doc.remove(from.line, nlines); } + if (added.length) { doc.insert(from.line, added); } + } else if (firstLine == lastLine) { + if (text.length == 1) { + update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans); + } else { + var added$1 = linesFor(1, text.length - 1); + added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight)); + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); + doc.insert(from.line + 1, added$1); + } + } else if (text.length == 1) { + update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0)); + doc.remove(from.line + 1, nlines); + } else { + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); + update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans); + var added$2 = linesFor(1, text.length - 1); + if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); } + doc.insert(from.line + 1, added$2); + } + + signalLater(doc, "change", doc, change); + } + + // Call f for all linked documents. + function linkedDocs(doc, f, sharedHistOnly) { + function propagate(doc, skip, sharedHist) { + if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) { + var rel = doc.linked[i]; + if (rel.doc == skip) { continue } + var shared = sharedHist && rel.sharedHist; + if (sharedHistOnly && !shared) { continue } + f(rel.doc, shared); + propagate(rel.doc, doc, shared); + } } + } + propagate(doc, null, true); + } + + // Attach a document to an editor. + function attachDoc(cm, doc) { + if (doc.cm) { throw new Error("This document is already in use.") } + cm.doc = doc; + doc.cm = cm; + estimateLineHeights(cm); + loadMode(cm); + setDirectionClass(cm); + cm.options.direction = doc.direction; + if (!cm.options.lineWrapping) { findMaxLine(cm); } + cm.options.mode = doc.modeOption; + regChange(cm); + } + + function setDirectionClass(cm) { + (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl"); + } + + function directionChanged(cm) { + runInOp(cm, function () { + setDirectionClass(cm); + regChange(cm); + }); + } + + function History(prev) { + // Arrays of change events and selections. Doing something adds an + // event to done and clears undo. Undoing moves events from done + // to undone, redoing moves them in the other direction. + this.done = []; this.undone = []; + this.undoDepth = prev ? prev.undoDepth : Infinity; + // Used to track when changes can be merged into a single undo + // event + this.lastModTime = this.lastSelTime = 0; + this.lastOp = this.lastSelOp = null; + this.lastOrigin = this.lastSelOrigin = null; + // Used by the isClean() method + this.generation = this.maxGeneration = prev ? prev.maxGeneration : 1; + } + + // Create a history change event from an updateDoc-style change + // object. + function historyChangeFromChange(doc, change) { + var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}; + attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); + linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true); + return histChange + } + + // Pop all selection events off the end of a history array. Stop at + // a change event. + function clearSelectionEvents(array) { + while (array.length) { + var last = lst(array); + if (last.ranges) { array.pop(); } + else { break } + } + } + + // Find the top change event in the history. Pop off selection + // events that are in the way. + function lastChangeEvent(hist, force) { + if (force) { + clearSelectionEvents(hist.done); + return lst(hist.done) + } else if (hist.done.length && !lst(hist.done).ranges) { + return lst(hist.done) + } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) { + hist.done.pop(); + return lst(hist.done) + } + } + + // Register a change in the history. Merges changes that are within + // a single operation, or are close together with an origin that + // allows merging (starting with "+") into a single event. + function addChangeToHistory(doc, change, selAfter, opId) { + var hist = doc.history; + hist.undone.length = 0; + var time = +new Date, cur; + var last; + + if ((hist.lastOp == opId || + hist.lastOrigin == change.origin && change.origin && + ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) || + change.origin.charAt(0) == "*")) && + (cur = lastChangeEvent(hist, hist.lastOp == opId))) { + // Merge this change into the last event + last = lst(cur.changes); + if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) { + // Optimized case for simple insertion -- don't want to add + // new changesets for every character typed + last.to = changeEnd(change); + } else { + // Add new sub-event + cur.changes.push(historyChangeFromChange(doc, change)); + } + } else { + // Can not be merged, start a new event. + var before = lst(hist.done); + if (!before || !before.ranges) + { pushSelectionToHistory(doc.sel, hist.done); } + cur = {changes: [historyChangeFromChange(doc, change)], + generation: hist.generation}; + hist.done.push(cur); + while (hist.done.length > hist.undoDepth) { + hist.done.shift(); + if (!hist.done[0].ranges) { hist.done.shift(); } + } + } + hist.done.push(selAfter); + hist.generation = ++hist.maxGeneration; + hist.lastModTime = hist.lastSelTime = time; + hist.lastOp = hist.lastSelOp = opId; + hist.lastOrigin = hist.lastSelOrigin = change.origin; + + if (!last) { signal(doc, "historyAdded"); } + } + + function selectionEventCanBeMerged(doc, origin, prev, sel) { + var ch = origin.charAt(0); + return ch == "*" || + ch == "+" && + prev.ranges.length == sel.ranges.length && + prev.somethingSelected() == sel.somethingSelected() && + new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500) + } + + // Called whenever the selection changes, sets the new selection as + // the pending selection in the history, and pushes the old pending + // selection into the 'done' array when it was significantly + // different (in number of selected ranges, emptiness, or time). + function addSelectionToHistory(doc, sel, opId, options) { + var hist = doc.history, origin = options && options.origin; + + // A new event is started when the previous origin does not match + // the current, or the origins don't allow matching. Origins + // starting with * are always merged, those starting with + are + // merged when similar and close together in time. + if (opId == hist.lastSelOp || + (origin && hist.lastSelOrigin == origin && + (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin || + selectionEventCanBeMerged(doc, origin, lst(hist.done), sel)))) + { hist.done[hist.done.length - 1] = sel; } + else + { pushSelectionToHistory(sel, hist.done); } + + hist.lastSelTime = +new Date; + hist.lastSelOrigin = origin; + hist.lastSelOp = opId; + if (options && options.clearRedo !== false) + { clearSelectionEvents(hist.undone); } + } + + function pushSelectionToHistory(sel, dest) { + var top = lst(dest); + if (!(top && top.ranges && top.equals(sel))) + { dest.push(sel); } + } + + // Used to store marked span information in the history. + function attachLocalSpans(doc, change, from, to) { + var existing = change["spans_" + doc.id], n = 0; + doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) { + if (line.markedSpans) + { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; } + ++n; + }); + } + + // When un/re-doing restores text containing marked spans, those + // that have been explicitly cleared should not be restored. + function removeClearedSpans(spans) { + if (!spans) { return null } + var out; + for (var i = 0; i < spans.length; ++i) { + if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } } + else if (out) { out.push(spans[i]); } + } + return !out ? spans : out.length ? out : null + } + + // Retrieve and filter the old marked spans stored in a change event. + function getOldSpans(doc, change) { + var found = change["spans_" + doc.id]; + if (!found) { return null } + var nw = []; + for (var i = 0; i < change.text.length; ++i) + { nw.push(removeClearedSpans(found[i])); } + return nw + } + + // Used for un/re-doing changes from the history. Combines the + // result of computing the existing spans with the set of spans that + // existed in the history (so that deleting around a span and then + // undoing brings back the span). + function mergeOldSpans(doc, change) { + var old = getOldSpans(doc, change); + var stretched = stretchSpansOverChange(doc, change); + if (!old) { return stretched } + if (!stretched) { return old } + + for (var i = 0; i < old.length; ++i) { + var oldCur = old[i], stretchCur = stretched[i]; + if (oldCur && stretchCur) { + spans: for (var j = 0; j < stretchCur.length; ++j) { + var span = stretchCur[j]; + for (var k = 0; k < oldCur.length; ++k) + { if (oldCur[k].marker == span.marker) { continue spans } } + oldCur.push(span); + } + } else if (stretchCur) { + old[i] = stretchCur; + } + } + return old + } + + // Used both to provide a JSON-safe object in .getHistory, and, when + // detaching a document, to split the history in two + function copyHistoryArray(events, newGroup, instantiateSel) { + var copy = []; + for (var i = 0; i < events.length; ++i) { + var event = events[i]; + if (event.ranges) { + copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event); + continue + } + var changes = event.changes, newChanges = []; + copy.push({changes: newChanges}); + for (var j = 0; j < changes.length; ++j) { + var change = changes[j], m = (void 0); + newChanges.push({from: change.from, to: change.to, text: change.text}); + if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) { + if (indexOf(newGroup, Number(m[1])) > -1) { + lst(newChanges)[prop] = change[prop]; + delete change[prop]; + } + } } } + } + } + return copy + } + + // The 'scroll' parameter given to many of these indicated whether + // the new cursor position should be scrolled into view after + // modifying the selection. + + // If shift is held or the extend flag is set, extends a range to + // include a given position (and optionally a second position). + // Otherwise, simply returns the range between the given positions. + // Used for cursor motion and such. + function extendRange(range, head, other, extend) { + if (extend) { + var anchor = range.anchor; + if (other) { + var posBefore = cmp(head, anchor) < 0; + if (posBefore != (cmp(other, anchor) < 0)) { + anchor = head; + head = other; + } else if (posBefore != (cmp(head, other) < 0)) { + head = other; + } + } + return new Range(anchor, head) + } else { + return new Range(other || head, head) + } + } + + // Extend the primary selection range, discard the rest. + function extendSelection(doc, head, other, options, extend) { + if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); } + setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options); + } + + // Extend all selections (pos is an array of selections with length + // equal the number of selections) + function extendSelections(doc, heads, options) { + var out = []; + var extend = doc.cm && (doc.cm.display.shift || doc.extend); + for (var i = 0; i < doc.sel.ranges.length; i++) + { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); } + var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex); + setSelection(doc, newSel, options); + } + + // Updates a single range in the selection. + function replaceOneSelection(doc, i, range, options) { + var ranges = doc.sel.ranges.slice(0); + ranges[i] = range; + setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options); + } + + // Reset the selection to a single range. + function setSimpleSelection(doc, anchor, head, options) { + setSelection(doc, simpleSelection(anchor, head), options); + } + + // Give beforeSelectionChange handlers a change to influence a + // selection update. + function filterSelectionChange(doc, sel, options) { + var obj = { + ranges: sel.ranges, + update: function(ranges) { + this.ranges = []; + for (var i = 0; i < ranges.length; i++) + { this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), + clipPos(doc, ranges[i].head)); } + }, + origin: options && options.origin + }; + signal(doc, "beforeSelectionChange", doc, obj); + if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); } + if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) } + else { return sel } + } + + function setSelectionReplaceHistory(doc, sel, options) { + var done = doc.history.done, last = lst(done); + if (last && last.ranges) { + done[done.length - 1] = sel; + setSelectionNoUndo(doc, sel, options); + } else { + setSelection(doc, sel, options); + } + } + + // Set a new selection. + function setSelection(doc, sel, options) { + setSelectionNoUndo(doc, sel, options); + addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options); + } + + function setSelectionNoUndo(doc, sel, options) { + if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) + { sel = filterSelectionChange(doc, sel, options); } + + var bias = options && options.bias || + (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1); + setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true)); + + if (!(options && options.scroll === false) && doc.cm && doc.cm.getOption("readOnly") != "nocursor") + { ensureCursorVisible(doc.cm); } + } + + function setSelectionInner(doc, sel) { + if (sel.equals(doc.sel)) { return } + + doc.sel = sel; + + if (doc.cm) { + doc.cm.curOp.updateInput = 1; + doc.cm.curOp.selectionChanged = true; + signalCursorActivity(doc.cm); + } + signalLater(doc, "cursorActivity", doc); + } + + // Verify that the selection does not partially select any atomic + // marked ranges. + function reCheckSelection(doc) { + setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false)); + } + + // Return a selection that does not partially select any atomic + // ranges. + function skipAtomicInSelection(doc, sel, bias, mayClear) { + var out; + for (var i = 0; i < sel.ranges.length; i++) { + var range = sel.ranges[i]; + var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]; + var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear); + var newHead = range.head == range.anchor ? newAnchor : skipAtomic(doc, range.head, old && old.head, bias, mayClear); + if (out || newAnchor != range.anchor || newHead != range.head) { + if (!out) { out = sel.ranges.slice(0, i); } + out[i] = new Range(newAnchor, newHead); + } + } + return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel + } + + function skipAtomicInner(doc, pos, oldPos, dir, mayClear) { + var line = getLine(doc, pos.line); + if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { + var sp = line.markedSpans[i], m = sp.marker; + + // Determine if we should prevent the cursor being placed to the left/right of an atomic marker + // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it + // is with selectLeft/Right + var preventCursorLeft = ("selectLeft" in m) ? !m.selectLeft : m.inclusiveLeft; + var preventCursorRight = ("selectRight" in m) ? !m.selectRight : m.inclusiveRight; + + if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) && + (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) { + if (mayClear) { + signal(m, "beforeCursorEnter"); + if (m.explicitlyCleared) { + if (!line.markedSpans) { break } + else {--i; continue} + } + } + if (!m.atomic) { continue } + + if (oldPos) { + var near = m.find(dir < 0 ? 1 : -1), diff = (void 0); + if (dir < 0 ? preventCursorRight : preventCursorLeft) + { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); } + if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0)) + { return skipAtomicInner(doc, near, pos, dir, mayClear) } + } + + var far = m.find(dir < 0 ? -1 : 1); + if (dir < 0 ? preventCursorLeft : preventCursorRight) + { far = movePos(doc, far, dir, far.line == pos.line ? line : null); } + return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null + } + } } + return pos + } + + // Ensure a given position is not inside an atomic range. + function skipAtomic(doc, pos, oldPos, bias, mayClear) { + var dir = bias || 1; + var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) || + (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) || + skipAtomicInner(doc, pos, oldPos, -dir, mayClear) || + (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true)); + if (!found) { + doc.cantEdit = true; + return Pos(doc.first, 0) + } + return found + } + + function movePos(doc, pos, dir, line) { + if (dir < 0 && pos.ch == 0) { + if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) } + else { return null } + } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) { + if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) } + else { return null } + } else { + return new Pos(pos.line, pos.ch + dir) + } + } + + function selectAll(cm) { + cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll); + } + + // UPDATING + + // Allow "beforeChange" event handlers to influence a change + function filterChange(doc, change, update) { + var obj = { + canceled: false, + from: change.from, + to: change.to, + text: change.text, + origin: change.origin, + cancel: function () { return obj.canceled = true; } + }; + if (update) { obj.update = function (from, to, text, origin) { + if (from) { obj.from = clipPos(doc, from); } + if (to) { obj.to = clipPos(doc, to); } + if (text) { obj.text = text; } + if (origin !== undefined) { obj.origin = origin; } + }; } + signal(doc, "beforeChange", doc, obj); + if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); } + + if (obj.canceled) { + if (doc.cm) { doc.cm.curOp.updateInput = 2; } + return null + } + return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin} + } + + // Apply a change to a document, and add it to the document's + // history, and propagating it to all linked documents. + function makeChange(doc, change, ignoreReadOnly) { + if (doc.cm) { + if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) } + if (doc.cm.state.suppressEdits) { return } + } + + if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) { + change = filterChange(doc, change, true); + if (!change) { return } + } + + // Possibly split or suppress the update based on the presence + // of read-only spans in its range. + var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to); + if (split) { + for (var i = split.length - 1; i >= 0; --i) + { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); } + } else { + makeChangeInner(doc, change); + } + } + + function makeChangeInner(doc, change) { + if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return } + var selAfter = computeSelAfterChange(doc, change); + addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN); + + makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)); + var rebased = []; + + linkedDocs(doc, function (doc, sharedHist) { + if (!sharedHist && indexOf(rebased, doc.history) == -1) { + rebaseHist(doc.history, change); + rebased.push(doc.history); + } + makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change)); + }); + } + + // Revert a change stored in a document's history. + function makeChangeFromHistory(doc, type, allowSelectionOnly) { + var suppress = doc.cm && doc.cm.state.suppressEdits; + if (suppress && !allowSelectionOnly) { return } + + var hist = doc.history, event, selAfter = doc.sel; + var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done; + + // Verify that there is a useable event (so that ctrl-z won't + // needlessly clear selection events) + var i = 0; + for (; i < source.length; i++) { + event = source[i]; + if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges) + { break } + } + if (i == source.length) { return } + hist.lastOrigin = hist.lastSelOrigin = null; + + for (;;) { + event = source.pop(); + if (event.ranges) { + pushSelectionToHistory(event, dest); + if (allowSelectionOnly && !event.equals(doc.sel)) { + setSelection(doc, event, {clearRedo: false}); + return + } + selAfter = event; + } else if (suppress) { + source.push(event); + return + } else { break } + } + + // Build up a reverse change object to add to the opposite history + // stack (redo when undoing, and vice versa). + var antiChanges = []; + pushSelectionToHistory(selAfter, dest); + dest.push({changes: antiChanges, generation: hist.generation}); + hist.generation = event.generation || ++hist.maxGeneration; + + var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange"); + + var loop = function ( i ) { + var change = event.changes[i]; + change.origin = type; + if (filter && !filterChange(doc, change, false)) { + source.length = 0; + return {} + } + + antiChanges.push(historyChangeFromChange(doc, change)); + + var after = i ? computeSelAfterChange(doc, change) : lst(source); + makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); + if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); } + var rebased = []; + + // Propagate to the linked documents + linkedDocs(doc, function (doc, sharedHist) { + if (!sharedHist && indexOf(rebased, doc.history) == -1) { + rebaseHist(doc.history, change); + rebased.push(doc.history); + } + makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change)); + }); + }; + + for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) { + var returned = loop( i$1 ); + + if ( returned ) return returned.v; + } + } + + // Sub-views need their line numbers shifted when text is added + // above or below them in the parent document. + function shiftDoc(doc, distance) { + if (distance == 0) { return } + doc.first += distance; + doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range( + Pos(range.anchor.line + distance, range.anchor.ch), + Pos(range.head.line + distance, range.head.ch) + ); }), doc.sel.primIndex); + if (doc.cm) { + regChange(doc.cm, doc.first, doc.first - distance, distance); + for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++) + { regLineChange(doc.cm, l, "gutter"); } + } + } + + // More lower-level change function, handling only a single document + // (not linked ones). + function makeChangeSingleDoc(doc, change, selAfter, spans) { + if (doc.cm && !doc.cm.curOp) + { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) } + + if (change.to.line < doc.first) { + shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line)); + return + } + if (change.from.line > doc.lastLine()) { return } + + // Clip the change to the size of this doc + if (change.from.line < doc.first) { + var shift = change.text.length - 1 - (doc.first - change.from.line); + shiftDoc(doc, shift); + change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch), + text: [lst(change.text)], origin: change.origin}; + } + var last = doc.lastLine(); + if (change.to.line > last) { + change = {from: change.from, to: Pos(last, getLine(doc, last).text.length), + text: [change.text[0]], origin: change.origin}; + } + + change.removed = getBetween(doc, change.from, change.to); + + if (!selAfter) { selAfter = computeSelAfterChange(doc, change); } + if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); } + else { updateDoc(doc, change, spans); } + setSelectionNoUndo(doc, selAfter, sel_dontScroll); + + if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0))) + { doc.cantEdit = false; } + } + + // Handle the interaction of a change to a document with the editor + // that this document is part of. + function makeChangeSingleDocInEditor(cm, change, spans) { + var doc = cm.doc, display = cm.display, from = change.from, to = change.to; + + var recomputeMaxLength = false, checkWidthStart = from.line; + if (!cm.options.lineWrapping) { + checkWidthStart = lineNo(visualLine(getLine(doc, from.line))); + doc.iter(checkWidthStart, to.line + 1, function (line) { + if (line == display.maxLine) { + recomputeMaxLength = true; + return true + } + }); + } + + if (doc.sel.contains(change.from, change.to) > -1) + { signalCursorActivity(cm); } + + updateDoc(doc, change, spans, estimateHeight(cm)); + + if (!cm.options.lineWrapping) { + doc.iter(checkWidthStart, from.line + change.text.length, function (line) { + var len = lineLength(line); + if (len > display.maxLineLength) { + display.maxLine = line; + display.maxLineLength = len; + display.maxLineChanged = true; + recomputeMaxLength = false; + } + }); + if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; } + } + + retreatFrontier(doc, from.line); + startWorker(cm, 400); + + var lendiff = change.text.length - (to.line - from.line) - 1; + // Remember that these lines changed, for updating the display + if (change.full) + { regChange(cm); } + else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change)) + { regLineChange(cm, from.line, "text"); } + else + { regChange(cm, from.line, to.line + 1, lendiff); } + + var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change"); + if (changeHandler || changesHandler) { + var obj = { + from: from, to: to, + text: change.text, + removed: change.removed, + origin: change.origin + }; + if (changeHandler) { signalLater(cm, "change", cm, obj); } + if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); } + } + cm.display.selForContextMenu = null; + } + + function replaceRange(doc, code, from, to, origin) { + var assign; + + if (!to) { to = from; } + if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); } + if (typeof code == "string") { code = doc.splitLines(code); } + makeChange(doc, {from: from, to: to, text: code, origin: origin}); + } + + // Rebasing/resetting history to deal with externally-sourced changes + + function rebaseHistSelSingle(pos, from, to, diff) { + if (to < pos.line) { + pos.line += diff; + } else if (from < pos.line) { + pos.line = from; + pos.ch = 0; + } + } + + // Tries to rebase an array of history events given a change in the + // document. If the change touches the same lines as the event, the + // event, and everything 'behind' it, is discarded. If the change is + // before the event, the event's positions are updated. Uses a + // copy-on-write scheme for the positions, to avoid having to + // reallocate them all on every rebase, but also avoid problems with + // shared position objects being unsafely updated. + function rebaseHistArray(array, from, to, diff) { + for (var i = 0; i < array.length; ++i) { + var sub = array[i], ok = true; + if (sub.ranges) { + if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; } + for (var j = 0; j < sub.ranges.length; j++) { + rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff); + rebaseHistSelSingle(sub.ranges[j].head, from, to, diff); + } + continue + } + for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) { + var cur = sub.changes[j$1]; + if (to < cur.from.line) { + cur.from = Pos(cur.from.line + diff, cur.from.ch); + cur.to = Pos(cur.to.line + diff, cur.to.ch); + } else if (from <= cur.to.line) { + ok = false; + break + } + } + if (!ok) { + array.splice(0, i + 1); + i = 0; + } + } + } + + function rebaseHist(hist, change) { + var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1; + rebaseHistArray(hist.done, from, to, diff); + rebaseHistArray(hist.undone, from, to, diff); + } + + // Utility for applying a change to a line by handle or number, + // returning the number and optionally registering the line as + // changed. + function changeLine(doc, handle, changeType, op) { + var no = handle, line = handle; + if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); } + else { no = lineNo(handle); } + if (no == null) { return null } + if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); } + return line + } + + // The document is represented as a BTree consisting of leaves, with + // chunk of lines in them, and branches, with up to ten leaves or + // other branch nodes below them. The top node is always a branch + // node, and is the document object itself (meaning it has + // additional methods and properties). + // + // All nodes have parent links. The tree is used both to go from + // line numbers to line objects, and to go from objects to numbers. + // It also indexes by height, and is used to convert between height + // and line object, and to find the total height of the document. + // + // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html + + function LeafChunk(lines) { + this.lines = lines; + this.parent = null; + var height = 0; + for (var i = 0; i < lines.length; ++i) { + lines[i].parent = this; + height += lines[i].height; + } + this.height = height; + } + + LeafChunk.prototype = { + chunkSize: function() { return this.lines.length }, + + // Remove the n lines at offset 'at'. + removeInner: function(at, n) { + for (var i = at, e = at + n; i < e; ++i) { + var line = this.lines[i]; + this.height -= line.height; + cleanUpLine(line); + signalLater(line, "delete"); + } + this.lines.splice(at, n); + }, + + // Helper used to collapse a small branch into a single leaf. + collapse: function(lines) { + lines.push.apply(lines, this.lines); + }, + + // Insert the given array of lines at offset 'at', count them as + // having the given height. + insertInner: function(at, lines, height) { + this.height += height; + this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at)); + for (var i = 0; i < lines.length; ++i) { lines[i].parent = this; } + }, + + // Used to iterate over a part of the tree. + iterN: function(at, n, op) { + for (var e = at + n; at < e; ++at) + { if (op(this.lines[at])) { return true } } + } + }; + + function BranchChunk(children) { + this.children = children; + var size = 0, height = 0; + for (var i = 0; i < children.length; ++i) { + var ch = children[i]; + size += ch.chunkSize(); height += ch.height; + ch.parent = this; + } + this.size = size; + this.height = height; + this.parent = null; + } + + BranchChunk.prototype = { + chunkSize: function() { return this.size }, + + removeInner: function(at, n) { + this.size -= n; + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at < sz) { + var rm = Math.min(n, sz - at), oldHeight = child.height; + child.removeInner(at, rm); + this.height -= oldHeight - child.height; + if (sz == rm) { this.children.splice(i--, 1); child.parent = null; } + if ((n -= rm) == 0) { break } + at = 0; + } else { at -= sz; } + } + // If the result is smaller than 25 lines, ensure that it is a + // single leaf node. + if (this.size - n < 25 && + (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) { + var lines = []; + this.collapse(lines); + this.children = [new LeafChunk(lines)]; + this.children[0].parent = this; + } + }, + + collapse: function(lines) { + for (var i = 0; i < this.children.length; ++i) { this.children[i].collapse(lines); } + }, + + insertInner: function(at, lines, height) { + this.size += lines.length; + this.height += height; + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at <= sz) { + child.insertInner(at, lines, height); + if (child.lines && child.lines.length > 50) { + // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced. + // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest. + var remaining = child.lines.length % 25 + 25; + for (var pos = remaining; pos < child.lines.length;) { + var leaf = new LeafChunk(child.lines.slice(pos, pos += 25)); + child.height -= leaf.height; + this.children.splice(++i, 0, leaf); + leaf.parent = this; + } + child.lines = child.lines.slice(0, remaining); + this.maybeSpill(); + } + break + } + at -= sz; + } + }, + + // When a node has grown, check whether it should be split. + maybeSpill: function() { + if (this.children.length <= 10) { return } + var me = this; + do { + var spilled = me.children.splice(me.children.length - 5, 5); + var sibling = new BranchChunk(spilled); + if (!me.parent) { // Become the parent node + var copy = new BranchChunk(me.children); + copy.parent = me; + me.children = [copy, sibling]; + me = copy; + } else { + me.size -= sibling.size; + me.height -= sibling.height; + var myIndex = indexOf(me.parent.children, me); + me.parent.children.splice(myIndex + 1, 0, sibling); + } + sibling.parent = me.parent; + } while (me.children.length > 10) + me.parent.maybeSpill(); + }, + + iterN: function(at, n, op) { + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at < sz) { + var used = Math.min(n, sz - at); + if (child.iterN(at, used, op)) { return true } + if ((n -= used) == 0) { break } + at = 0; + } else { at -= sz; } + } + } + }; + + // Line widgets are block elements displayed above or below a line. + + var LineWidget = function(doc, node, options) { + if (options) { for (var opt in options) { if (options.hasOwnProperty(opt)) + { this[opt] = options[opt]; } } } + this.doc = doc; + this.node = node; + }; + + LineWidget.prototype.clear = function () { + var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line); + if (no == null || !ws) { return } + for (var i = 0; i < ws.length; ++i) { if (ws[i] == this) { ws.splice(i--, 1); } } + if (!ws.length) { line.widgets = null; } + var height = widgetHeight(this); + updateLineHeight(line, Math.max(0, line.height - height)); + if (cm) { + runInOp(cm, function () { + adjustScrollWhenAboveVisible(cm, line, -height); + regLineChange(cm, no, "widget"); + }); + signalLater(cm, "lineWidgetCleared", cm, this, no); + } + }; + + LineWidget.prototype.changed = function () { + var this$1 = this; + + var oldH = this.height, cm = this.doc.cm, line = this.line; + this.height = null; + var diff = widgetHeight(this) - oldH; + if (!diff) { return } + if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); } + if (cm) { + runInOp(cm, function () { + cm.curOp.forceUpdate = true; + adjustScrollWhenAboveVisible(cm, line, diff); + signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line)); + }); + } + }; + eventMixin(LineWidget); + + function adjustScrollWhenAboveVisible(cm, line, diff) { + if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop)) + { addToScrollTop(cm, diff); } + } + + function addLineWidget(doc, handle, node, options) { + var widget = new LineWidget(doc, node, options); + var cm = doc.cm; + if (cm && widget.noHScroll) { cm.display.alignWidgets = true; } + changeLine(doc, handle, "widget", function (line) { + var widgets = line.widgets || (line.widgets = []); + if (widget.insertAt == null) { widgets.push(widget); } + else { widgets.splice(Math.min(widgets.length, Math.max(0, widget.insertAt)), 0, widget); } + widget.line = line; + if (cm && !lineIsHidden(doc, line)) { + var aboveVisible = heightAtLine(line) < doc.scrollTop; + updateLineHeight(line, line.height + widgetHeight(widget)); + if (aboveVisible) { addToScrollTop(cm, widget.height); } + cm.curOp.forceUpdate = true; + } + return true + }); + if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); } + return widget + } + + // TEXTMARKERS + + // Created with markText and setBookmark methods. A TextMarker is a + // handle that can be used to clear or find a marked position in the + // document. Line objects hold arrays (markedSpans) containing + // {from, to, marker} object pointing to such marker objects, and + // indicating that such a marker is present on that line. Multiple + // lines may point to the same marker when it spans across lines. + // The spans will have null for their from/to properties when the + // marker continues beyond the start/end of the line. Markers have + // links back to the lines they currently touch. + + // Collapsed markers have unique ids, in order to be able to order + // them, which is needed for uniquely determining an outer marker + // when they overlap (they may nest, but not partially overlap). + var nextMarkerId = 0; + + var TextMarker = function(doc, type) { + this.lines = []; + this.type = type; + this.doc = doc; + this.id = ++nextMarkerId; + }; + + // Clear the marker. + TextMarker.prototype.clear = function () { + if (this.explicitlyCleared) { return } + var cm = this.doc.cm, withOp = cm && !cm.curOp; + if (withOp) { startOperation(cm); } + if (hasHandler(this, "clear")) { + var found = this.find(); + if (found) { signalLater(this, "clear", found.from, found.to); } + } + var min = null, max = null; + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); + if (cm && !this.collapsed) { regLineChange(cm, lineNo(line), "text"); } + else if (cm) { + if (span.to != null) { max = lineNo(line); } + if (span.from != null) { min = lineNo(line); } + } + line.markedSpans = removeMarkedSpan(line.markedSpans, span); + if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm) + { updateLineHeight(line, textHeight(cm.display)); } + } + if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) { + var visual = visualLine(this.lines[i$1]), len = lineLength(visual); + if (len > cm.display.maxLineLength) { + cm.display.maxLine = visual; + cm.display.maxLineLength = len; + cm.display.maxLineChanged = true; + } + } } + + if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); } + this.lines.length = 0; + this.explicitlyCleared = true; + if (this.atomic && this.doc.cantEdit) { + this.doc.cantEdit = false; + if (cm) { reCheckSelection(cm.doc); } + } + if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); } + if (withOp) { endOperation(cm); } + if (this.parent) { this.parent.clear(); } + }; + + // Find the position of the marker in the document. Returns a {from, + // to} object by default. Side can be passed to get a specific side + // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the + // Pos objects returned contain a line object, rather than a line + // number (used to prevent looking up the same line twice). + TextMarker.prototype.find = function (side, lineObj) { + if (side == null && this.type == "bookmark") { side = 1; } + var from, to; + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); + if (span.from != null) { + from = Pos(lineObj ? line : lineNo(line), span.from); + if (side == -1) { return from } + } + if (span.to != null) { + to = Pos(lineObj ? line : lineNo(line), span.to); + if (side == 1) { return to } + } + } + return from && {from: from, to: to} + }; + + // Signals that the marker's widget changed, and surrounding layout + // should be recomputed. + TextMarker.prototype.changed = function () { + var this$1 = this; + + var pos = this.find(-1, true), widget = this, cm = this.doc.cm; + if (!pos || !cm) { return } + runInOp(cm, function () { + var line = pos.line, lineN = lineNo(pos.line); + var view = findViewForLine(cm, lineN); + if (view) { + clearLineMeasurementCacheFor(view); + cm.curOp.selectionChanged = cm.curOp.forceUpdate = true; + } + cm.curOp.updateMaxLine = true; + if (!lineIsHidden(widget.doc, line) && widget.height != null) { + var oldHeight = widget.height; + widget.height = null; + var dHeight = widgetHeight(widget) - oldHeight; + if (dHeight) + { updateLineHeight(line, line.height + dHeight); } + } + signalLater(cm, "markerChanged", cm, this$1); + }); + }; + + TextMarker.prototype.attachLine = function (line) { + if (!this.lines.length && this.doc.cm) { + var op = this.doc.cm.curOp; + if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) + { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); } + } + this.lines.push(line); + }; + + TextMarker.prototype.detachLine = function (line) { + this.lines.splice(indexOf(this.lines, line), 1); + if (!this.lines.length && this.doc.cm) { + var op = this.doc.cm.curOp + ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this); + } + }; + eventMixin(TextMarker); + + // Create a marker, wire it up to the right lines, and + function markText(doc, from, to, options, type) { + // Shared markers (across linked documents) are handled separately + // (markTextShared will call out to this again, once per + // document). + if (options && options.shared) { return markTextShared(doc, from, to, options, type) } + // Ensure we are in an operation. + if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) } + + var marker = new TextMarker(doc, type), diff = cmp(from, to); + if (options) { copyObj(options, marker, false); } + // Don't connect empty markers unless clearWhenEmpty is false + if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false) + { return marker } + if (marker.replacedWith) { + // Showing up as a widget implies collapsed (widget replaces text) + marker.collapsed = true; + marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget"); + if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); } + if (options.insertLeft) { marker.widgetNode.insertLeft = true; } + } + if (marker.collapsed) { + if (conflictingCollapsedRange(doc, from.line, from, to, marker) || + from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker)) + { throw new Error("Inserting collapsed marker partially overlapping an existing one") } + seeCollapsedSpans(); + } + + if (marker.addToHistory) + { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); } + + var curLine = from.line, cm = doc.cm, updateMaxLine; + doc.iter(curLine, to.line + 1, function (line) { + if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine) + { updateMaxLine = true; } + if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); } + addMarkedSpan(line, new MarkedSpan(marker, + curLine == from.line ? from.ch : null, + curLine == to.line ? to.ch : null), doc.cm && doc.cm.curOp); + ++curLine; + }); + // lineIsHidden depends on the presence of the spans, so needs a second pass + if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) { + if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); } + }); } + + if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); } + + if (marker.readOnly) { + seeReadOnlySpans(); + if (doc.history.done.length || doc.history.undone.length) + { doc.clearHistory(); } + } + if (marker.collapsed) { + marker.id = ++nextMarkerId; + marker.atomic = true; + } + if (cm) { + // Sync editor state + if (updateMaxLine) { cm.curOp.updateMaxLine = true; } + if (marker.collapsed) + { regChange(cm, from.line, to.line + 1); } + else if (marker.className || marker.startStyle || marker.endStyle || marker.css || + marker.attributes || marker.title) + { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } } + if (marker.atomic) { reCheckSelection(cm.doc); } + signalLater(cm, "markerAdded", cm, marker); + } + return marker + } + + // SHARED TEXTMARKERS + + // A shared marker spans multiple linked documents. It is + // implemented as a meta-marker-object controlling multiple normal + // markers. + var SharedTextMarker = function(markers, primary) { + this.markers = markers; + this.primary = primary; + for (var i = 0; i < markers.length; ++i) + { markers[i].parent = this; } + }; + + SharedTextMarker.prototype.clear = function () { + if (this.explicitlyCleared) { return } + this.explicitlyCleared = true; + for (var i = 0; i < this.markers.length; ++i) + { this.markers[i].clear(); } + signalLater(this, "clear"); + }; + + SharedTextMarker.prototype.find = function (side, lineObj) { + return this.primary.find(side, lineObj) + }; + eventMixin(SharedTextMarker); + + function markTextShared(doc, from, to, options, type) { + options = copyObj(options); + options.shared = false; + var markers = [markText(doc, from, to, options, type)], primary = markers[0]; + var widget = options.widgetNode; + linkedDocs(doc, function (doc) { + if (widget) { options.widgetNode = widget.cloneNode(true); } + markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type)); + for (var i = 0; i < doc.linked.length; ++i) + { if (doc.linked[i].isParent) { return } } + primary = lst(markers); + }); + return new SharedTextMarker(markers, primary) + } + + function findSharedMarkers(doc) { + return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; }) + } + + function copySharedMarkers(doc, markers) { + for (var i = 0; i < markers.length; i++) { + var marker = markers[i], pos = marker.find(); + var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to); + if (cmp(mFrom, mTo)) { + var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type); + marker.markers.push(subMark); + subMark.parent = marker; + } + } + } + + function detachSharedMarkers(markers) { + var loop = function ( i ) { + var marker = markers[i], linked = [marker.primary.doc]; + linkedDocs(marker.primary.doc, function (d) { return linked.push(d); }); + for (var j = 0; j < marker.markers.length; j++) { + var subMarker = marker.markers[j]; + if (indexOf(linked, subMarker.doc) == -1) { + subMarker.parent = null; + marker.markers.splice(j--, 1); + } + } + }; + + for (var i = 0; i < markers.length; i++) loop( i ); + } + + var nextDocId = 0; + var Doc = function(text, mode, firstLine, lineSep, direction) { + if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) } + if (firstLine == null) { firstLine = 0; } + + BranchChunk.call(this, [new LeafChunk([new Line("", null)])]); + this.first = firstLine; + this.scrollTop = this.scrollLeft = 0; + this.cantEdit = false; + this.cleanGeneration = 1; + this.modeFrontier = this.highlightFrontier = firstLine; + var start = Pos(firstLine, 0); + this.sel = simpleSelection(start); + this.history = new History(null); + this.id = ++nextDocId; + this.modeOption = mode; + this.lineSep = lineSep; + this.direction = (direction == "rtl") ? "rtl" : "ltr"; + this.extend = false; + + if (typeof text == "string") { text = this.splitLines(text); } + updateDoc(this, {from: start, to: start, text: text}); + setSelection(this, simpleSelection(start), sel_dontScroll); + }; + + Doc.prototype = createObj(BranchChunk.prototype, { + constructor: Doc, + // Iterate over the document. Supports two forms -- with only one + // argument, it calls that for each line in the document. With + // three, it iterates over the range given by the first two (with + // the second being non-inclusive). + iter: function(from, to, op) { + if (op) { this.iterN(from - this.first, to - from, op); } + else { this.iterN(this.first, this.first + this.size, from); } + }, + + // Non-public interface for adding and removing lines. + insert: function(at, lines) { + var height = 0; + for (var i = 0; i < lines.length; ++i) { height += lines[i].height; } + this.insertInner(at - this.first, lines, height); + }, + remove: function(at, n) { this.removeInner(at - this.first, n); }, + + // From here, the methods are part of the public interface. Most + // are also available from CodeMirror (editor) instances. + + getValue: function(lineSep) { + var lines = getLines(this, this.first, this.first + this.size); + if (lineSep === false) { return lines } + return lines.join(lineSep || this.lineSeparator()) + }, + setValue: docMethodOp(function(code) { + var top = Pos(this.first, 0), last = this.first + this.size - 1; + makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length), + text: this.splitLines(code), origin: "setValue", full: true}, true); + if (this.cm) { scrollToCoords(this.cm, 0, 0); } + setSelection(this, simpleSelection(top), sel_dontScroll); + }), + replaceRange: function(code, from, to, origin) { + from = clipPos(this, from); + to = to ? clipPos(this, to) : from; + replaceRange(this, code, from, to, origin); + }, + getRange: function(from, to, lineSep) { + var lines = getBetween(this, clipPos(this, from), clipPos(this, to)); + if (lineSep === false) { return lines } + if (lineSep === '') { return lines.join('') } + return lines.join(lineSep || this.lineSeparator()) + }, + + getLine: function(line) {var l = this.getLineHandle(line); return l && l.text}, + + getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }}, + getLineNumber: function(line) {return lineNo(line)}, + + getLineHandleVisualStart: function(line) { + if (typeof line == "number") { line = getLine(this, line); } + return visualLine(line) + }, + + lineCount: function() {return this.size}, + firstLine: function() {return this.first}, + lastLine: function() {return this.first + this.size - 1}, + + clipPos: function(pos) {return clipPos(this, pos)}, + + getCursor: function(start) { + var range = this.sel.primary(), pos; + if (start == null || start == "head") { pos = range.head; } + else if (start == "anchor") { pos = range.anchor; } + else if (start == "end" || start == "to" || start === false) { pos = range.to(); } + else { pos = range.from(); } + return pos + }, + listSelections: function() { return this.sel.ranges }, + somethingSelected: function() {return this.sel.somethingSelected()}, + + setCursor: docMethodOp(function(line, ch, options) { + setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options); + }), + setSelection: docMethodOp(function(anchor, head, options) { + setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options); + }), + extendSelection: docMethodOp(function(head, other, options) { + extendSelection(this, clipPos(this, head), other && clipPos(this, other), options); + }), + extendSelections: docMethodOp(function(heads, options) { + extendSelections(this, clipPosArray(this, heads), options); + }), + extendSelectionsBy: docMethodOp(function(f, options) { + var heads = map(this.sel.ranges, f); + extendSelections(this, clipPosArray(this, heads), options); + }), + setSelections: docMethodOp(function(ranges, primary, options) { + if (!ranges.length) { return } + var out = []; + for (var i = 0; i < ranges.length; i++) + { out[i] = new Range(clipPos(this, ranges[i].anchor), + clipPos(this, ranges[i].head || ranges[i].anchor)); } + if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); } + setSelection(this, normalizeSelection(this.cm, out, primary), options); + }), + addSelection: docMethodOp(function(anchor, head, options) { + var ranges = this.sel.ranges.slice(0); + ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor))); + setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options); + }), + + getSelection: function(lineSep) { + var ranges = this.sel.ranges, lines; + for (var i = 0; i < ranges.length; i++) { + var sel = getBetween(this, ranges[i].from(), ranges[i].to()); + lines = lines ? lines.concat(sel) : sel; + } + if (lineSep === false) { return lines } + else { return lines.join(lineSep || this.lineSeparator()) } + }, + getSelections: function(lineSep) { + var parts = [], ranges = this.sel.ranges; + for (var i = 0; i < ranges.length; i++) { + var sel = getBetween(this, ranges[i].from(), ranges[i].to()); + if (lineSep !== false) { sel = sel.join(lineSep || this.lineSeparator()); } + parts[i] = sel; + } + return parts + }, + replaceSelection: function(code, collapse, origin) { + var dup = []; + for (var i = 0; i < this.sel.ranges.length; i++) + { dup[i] = code; } + this.replaceSelections(dup, collapse, origin || "+input"); + }, + replaceSelections: docMethodOp(function(code, collapse, origin) { + var changes = [], sel = this.sel; + for (var i = 0; i < sel.ranges.length; i++) { + var range = sel.ranges[i]; + changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin}; + } + var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse); + for (var i$1 = changes.length - 1; i$1 >= 0; i$1--) + { makeChange(this, changes[i$1]); } + if (newSel) { setSelectionReplaceHistory(this, newSel); } + else if (this.cm) { ensureCursorVisible(this.cm); } + }), + undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}), + redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}), + undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}), + redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}), + + setExtending: function(val) {this.extend = val;}, + getExtending: function() {return this.extend}, + + historySize: function() { + var hist = this.history, done = 0, undone = 0; + for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } } + for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } } + return {undo: done, redo: undone} + }, + clearHistory: function() { + var this$1 = this; + + this.history = new History(this.history); + linkedDocs(this, function (doc) { return doc.history = this$1.history; }, true); + }, + + markClean: function() { + this.cleanGeneration = this.changeGeneration(true); + }, + changeGeneration: function(forceSplit) { + if (forceSplit) + { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; } + return this.history.generation + }, + isClean: function (gen) { + return this.history.generation == (gen || this.cleanGeneration) + }, + + getHistory: function() { + return {done: copyHistoryArray(this.history.done), + undone: copyHistoryArray(this.history.undone)} + }, + setHistory: function(histData) { + var hist = this.history = new History(this.history); + hist.done = copyHistoryArray(histData.done.slice(0), null, true); + hist.undone = copyHistoryArray(histData.undone.slice(0), null, true); + }, + + setGutterMarker: docMethodOp(function(line, gutterID, value) { + return changeLine(this, line, "gutter", function (line) { + var markers = line.gutterMarkers || (line.gutterMarkers = {}); + markers[gutterID] = value; + if (!value && isEmpty(markers)) { line.gutterMarkers = null; } + return true + }) + }), + + clearGutter: docMethodOp(function(gutterID) { + var this$1 = this; + + this.iter(function (line) { + if (line.gutterMarkers && line.gutterMarkers[gutterID]) { + changeLine(this$1, line, "gutter", function () { + line.gutterMarkers[gutterID] = null; + if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; } + return true + }); + } + }); + }), + + lineInfo: function(line) { + var n; + if (typeof line == "number") { + if (!isLine(this, line)) { return null } + n = line; + line = getLine(this, line); + if (!line) { return null } + } else { + n = lineNo(line); + if (n == null) { return null } + } + return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, + textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass, + widgets: line.widgets} + }, + + addLineClass: docMethodOp(function(handle, where, cls) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { + var prop = where == "text" ? "textClass" + : where == "background" ? "bgClass" + : where == "gutter" ? "gutterClass" : "wrapClass"; + if (!line[prop]) { line[prop] = cls; } + else if (classTest(cls).test(line[prop])) { return false } + else { line[prop] += " " + cls; } + return true + }) + }), + removeLineClass: docMethodOp(function(handle, where, cls) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { + var prop = where == "text" ? "textClass" + : where == "background" ? "bgClass" + : where == "gutter" ? "gutterClass" : "wrapClass"; + var cur = line[prop]; + if (!cur) { return false } + else if (cls == null) { line[prop] = null; } + else { + var found = cur.match(classTest(cls)); + if (!found) { return false } + var end = found.index + found[0].length; + line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null; + } + return true + }) + }), + + addLineWidget: docMethodOp(function(handle, node, options) { + return addLineWidget(this, handle, node, options) + }), + removeLineWidget: function(widget) { widget.clear(); }, + + markText: function(from, to, options) { + return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range") + }, + setBookmark: function(pos, options) { + var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options), + insertLeft: options && options.insertLeft, + clearWhenEmpty: false, shared: options && options.shared, + handleMouseEvents: options && options.handleMouseEvents}; + pos = clipPos(this, pos); + return markText(this, pos, pos, realOpts, "bookmark") + }, + findMarksAt: function(pos) { + pos = clipPos(this, pos); + var markers = [], spans = getLine(this, pos.line).markedSpans; + if (spans) { for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if ((span.from == null || span.from <= pos.ch) && + (span.to == null || span.to >= pos.ch)) + { markers.push(span.marker.parent || span.marker); } + } } + return markers + }, + findMarks: function(from, to, filter) { + from = clipPos(this, from); to = clipPos(this, to); + var found = [], lineNo = from.line; + this.iter(from.line, to.line + 1, function (line) { + var spans = line.markedSpans; + if (spans) { for (var i = 0; i < spans.length; i++) { + var span = spans[i]; + if (!(span.to != null && lineNo == from.line && from.ch >= span.to || + span.from == null && lineNo != from.line || + span.from != null && lineNo == to.line && span.from >= to.ch) && + (!filter || filter(span.marker))) + { found.push(span.marker.parent || span.marker); } + } } + ++lineNo; + }); + return found + }, + getAllMarks: function() { + var markers = []; + this.iter(function (line) { + var sps = line.markedSpans; + if (sps) { for (var i = 0; i < sps.length; ++i) + { if (sps[i].from != null) { markers.push(sps[i].marker); } } } + }); + return markers + }, + + posFromIndex: function(off) { + var ch, lineNo = this.first, sepSize = this.lineSeparator().length; + this.iter(function (line) { + var sz = line.text.length + sepSize; + if (sz > off) { ch = off; return true } + off -= sz; + ++lineNo; + }); + return clipPos(this, Pos(lineNo, ch)) + }, + indexFromPos: function (coords) { + coords = clipPos(this, coords); + var index = coords.ch; + if (coords.line < this.first || coords.ch < 0) { return 0 } + var sepSize = this.lineSeparator().length; + this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value + index += line.text.length + sepSize; + }); + return index + }, + + copy: function(copyHistory) { + var doc = new Doc(getLines(this, this.first, this.first + this.size), + this.modeOption, this.first, this.lineSep, this.direction); + doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft; + doc.sel = this.sel; + doc.extend = false; + if (copyHistory) { + doc.history.undoDepth = this.history.undoDepth; + doc.setHistory(this.getHistory()); + } + return doc + }, + + linkedDoc: function(options) { + if (!options) { options = {}; } + var from = this.first, to = this.first + this.size; + if (options.from != null && options.from > from) { from = options.from; } + if (options.to != null && options.to < to) { to = options.to; } + var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction); + if (options.sharedHist) { copy.history = this.history + ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist}); + copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]; + copySharedMarkers(copy, findSharedMarkers(this)); + return copy + }, + unlinkDoc: function(other) { + if (other instanceof CodeMirror) { other = other.doc; } + if (this.linked) { for (var i = 0; i < this.linked.length; ++i) { + var link = this.linked[i]; + if (link.doc != other) { continue } + this.linked.splice(i, 1); + other.unlinkDoc(this); + detachSharedMarkers(findSharedMarkers(this)); + break + } } + // If the histories were shared, split them again + if (other.history == this.history) { + var splitIds = [other.id]; + linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true); + other.history = new History(null); + other.history.done = copyHistoryArray(this.history.done, splitIds); + other.history.undone = copyHistoryArray(this.history.undone, splitIds); + } + }, + iterLinkedDocs: function(f) {linkedDocs(this, f);}, + + getMode: function() {return this.mode}, + getEditor: function() {return this.cm}, + + splitLines: function(str) { + if (this.lineSep) { return str.split(this.lineSep) } + return splitLinesAuto(str) + }, + lineSeparator: function() { return this.lineSep || "\n" }, + + setDirection: docMethodOp(function (dir) { + if (dir != "rtl") { dir = "ltr"; } + if (dir == this.direction) { return } + this.direction = dir; + this.iter(function (line) { return line.order = null; }); + if (this.cm) { directionChanged(this.cm); } + }) + }); + + // Public alias. + Doc.prototype.eachLine = Doc.prototype.iter; + + // Kludge to work around strange IE behavior where it'll sometimes + // re-fire a series of drag-related events right after the drop (#1551) + var lastDrop = 0; + + function onDrop(e) { + var cm = this; + clearDragCursor(cm); + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) + { return } + e_preventDefault(e); + if (ie) { lastDrop = +new Date; } + var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files; + if (!pos || cm.isReadOnly()) { return } + // Might be a file drop, in which case we simply extract the text + // and insert it. + if (files && files.length && window.FileReader && window.File) { + var n = files.length, text = Array(n), read = 0; + var markAsReadAndPasteIfAllFilesAreRead = function () { + if (++read == n) { + operation(cm, function () { + pos = clipPos(cm.doc, pos); + var change = {from: pos, to: pos, + text: cm.doc.splitLines( + text.filter(function (t) { return t != null; }).join(cm.doc.lineSeparator())), + origin: "paste"}; + makeChange(cm.doc, change); + setSelectionReplaceHistory(cm.doc, simpleSelection(clipPos(cm.doc, pos), clipPos(cm.doc, changeEnd(change)))); + })(); + } + }; + var readTextFromFile = function (file, i) { + if (cm.options.allowDropFileTypes && + indexOf(cm.options.allowDropFileTypes, file.type) == -1) { + markAsReadAndPasteIfAllFilesAreRead(); + return + } + var reader = new FileReader; + reader.onerror = function () { return markAsReadAndPasteIfAllFilesAreRead(); }; + reader.onload = function () { + var content = reader.result; + if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { + markAsReadAndPasteIfAllFilesAreRead(); + return + } + text[i] = content; + markAsReadAndPasteIfAllFilesAreRead(); + }; + reader.readAsText(file); + }; + for (var i = 0; i < files.length; i++) { readTextFromFile(files[i], i); } + } else { // Normal drop + // Don't do a replace if the drop happened inside of the selected text. + if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) { + cm.state.draggingText(e); + // Ensure the editor is re-focused + setTimeout(function () { return cm.display.input.focus(); }, 20); + return + } + try { + var text$1 = e.dataTransfer.getData("Text"); + if (text$1) { + var selected; + if (cm.state.draggingText && !cm.state.draggingText.copy) + { selected = cm.listSelections(); } + setSelectionNoUndo(cm.doc, simpleSelection(pos, pos)); + if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1) + { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } } + cm.replaceSelection(text$1, "around", "paste"); + cm.display.input.focus(); + } + } + catch(e$1){} + } + } + + function onDragStart(cm, e) { + if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return } + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return } + + e.dataTransfer.setData("Text", cm.getSelection()); + e.dataTransfer.effectAllowed = "copyMove"; + + // Use dummy image instead of default browsers image. + // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there. + if (e.dataTransfer.setDragImage && !safari) { + var img = elt("img", null, null, "position: fixed; left: 0; top: 0;"); + img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; + if (presto) { + img.width = img.height = 1; + cm.display.wrapper.appendChild(img); + // Force a relayout, or Opera won't use our image for some obscure reason + img._top = img.offsetTop; + } + e.dataTransfer.setDragImage(img, 0, 0); + if (presto) { img.parentNode.removeChild(img); } + } + } + + function onDragOver(cm, e) { + var pos = posFromMouse(cm, e); + if (!pos) { return } + var frag = document.createDocumentFragment(); + drawSelectionCursor(cm, pos, frag); + if (!cm.display.dragCursor) { + cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors"); + cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv); + } + removeChildrenAndAdd(cm.display.dragCursor, frag); + } + + function clearDragCursor(cm) { + if (cm.display.dragCursor) { + cm.display.lineSpace.removeChild(cm.display.dragCursor); + cm.display.dragCursor = null; + } + } + + // These must be handled carefully, because naively registering a + // handler for each editor will cause the editors to never be + // garbage collected. + + function forEachCodeMirror(f) { + if (!document.getElementsByClassName) { return } + var byClass = document.getElementsByClassName("CodeMirror"), editors = []; + for (var i = 0; i < byClass.length; i++) { + var cm = byClass[i].CodeMirror; + if (cm) { editors.push(cm); } + } + if (editors.length) { editors[0].operation(function () { + for (var i = 0; i < editors.length; i++) { f(editors[i]); } + }); } + } + + var globalsRegistered = false; + function ensureGlobalHandlers() { + if (globalsRegistered) { return } + registerGlobalHandlers(); + globalsRegistered = true; + } + function registerGlobalHandlers() { + // When the window resizes, we need to refresh active editors. + var resizeTimer; + on(window, "resize", function () { + if (resizeTimer == null) { resizeTimer = setTimeout(function () { + resizeTimer = null; + forEachCodeMirror(onResize); + }, 100); } + }); + // When the window loses focus, we want to show the editor as blurred + on(window, "blur", function () { return forEachCodeMirror(onBlur); }); + } + // Called when the window resizes + function onResize(cm) { + var d = cm.display; + // Might be a text scaling operation, clear size caches. + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; + d.scrollbarsClipped = false; + cm.setSize(); + } + + var keyNames = { + 3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", + 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", + 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", + 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", + 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 145: "ScrollLock", + 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", + 221: "]", 222: "'", 224: "Mod", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", + 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert" + }; + + // Number keys + for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); } + // Alphabetic keys + for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); } + // Function keys + for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; } + + var keyMap = {}; + + keyMap.basic = { + "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", + "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", + "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore", + "Tab": "defaultTab", "Shift-Tab": "indentAuto", + "Enter": "newlineAndIndent", "Insert": "toggleOverwrite", + "Esc": "singleSelection" + }; + // Note that the save and find-related commands aren't defined by + // default. User code or addons can define them. Unknown commands + // are simply ignored. + keyMap.pcDefault = { + "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", + "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown", + "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", + "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find", + "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", + "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", + "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection", + "fallthrough": "basic" + }; + // Very basic readline/emacs-style bindings, which are standard on Mac. + keyMap.emacsy = { + "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", + "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", + "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", + "Ctrl-T": "transposeChars", "Ctrl-O": "openLine" + }; + keyMap.macDefault = { + "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", + "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft", + "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore", + "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find", + "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", + "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight", + "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd", + "fallthrough": ["basic", "emacsy"] + }; + keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault; + + // KEYMAP DISPATCH + + function normalizeKeyName(name) { + var parts = name.split(/-(?!$)/); + name = parts[parts.length - 1]; + var alt, ctrl, shift, cmd; + for (var i = 0; i < parts.length - 1; i++) { + var mod = parts[i]; + if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; } + else if (/^a(lt)?$/i.test(mod)) { alt = true; } + else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; } + else if (/^s(hift)?$/i.test(mod)) { shift = true; } + else { throw new Error("Unrecognized modifier name: " + mod) } + } + if (alt) { name = "Alt-" + name; } + if (ctrl) { name = "Ctrl-" + name; } + if (cmd) { name = "Cmd-" + name; } + if (shift) { name = "Shift-" + name; } + return name + } + + // This is a kludge to keep keymaps mostly working as raw objects + // (backwards compatibility) while at the same time support features + // like normalization and multi-stroke key bindings. It compiles a + // new normalized keymap, and then updates the old object to reflect + // this. + function normalizeKeyMap(keymap) { + var copy = {}; + for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) { + var value = keymap[keyname]; + if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue } + if (value == "...") { delete keymap[keyname]; continue } + + var keys = map(keyname.split(" "), normalizeKeyName); + for (var i = 0; i < keys.length; i++) { + var val = (void 0), name = (void 0); + if (i == keys.length - 1) { + name = keys.join(" "); + val = value; + } else { + name = keys.slice(0, i + 1).join(" "); + val = "..."; + } + var prev = copy[name]; + if (!prev) { copy[name] = val; } + else if (prev != val) { throw new Error("Inconsistent bindings for " + name) } + } + delete keymap[keyname]; + } } + for (var prop in copy) { keymap[prop] = copy[prop]; } + return keymap + } + + function lookupKey(key, map, handle, context) { + map = getKeyMap(map); + var found = map.call ? map.call(key, context) : map[key]; + if (found === false) { return "nothing" } + if (found === "...") { return "multi" } + if (found != null && handle(found)) { return "handled" } + + if (map.fallthrough) { + if (Object.prototype.toString.call(map.fallthrough) != "[object Array]") + { return lookupKey(key, map.fallthrough, handle, context) } + for (var i = 0; i < map.fallthrough.length; i++) { + var result = lookupKey(key, map.fallthrough[i], handle, context); + if (result) { return result } + } + } + } + + // Modifier key presses don't count as 'real' key presses for the + // purpose of keymap fallthrough. + function isModifierKey(value) { + var name = typeof value == "string" ? value : keyNames[value.keyCode]; + return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod" + } + + function addModifierNames(name, event, noShift) { + var base = name; + if (event.altKey && base != "Alt") { name = "Alt-" + name; } + if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; } + if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Mod") { name = "Cmd-" + name; } + if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; } + return name + } + + // Look up the name of a key as indicated by an event object. + function keyName(event, noShift) { + if (presto && event.keyCode == 34 && event["char"]) { return false } + var name = keyNames[event.keyCode]; + if (name == null || event.altGraphKey) { return false } + // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause, + // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+) + if (event.keyCode == 3 && event.code) { name = event.code; } + return addModifierNames(name, event, noShift) + } + + function getKeyMap(val) { + return typeof val == "string" ? keyMap[val] : val + } + + // Helper for deleting text near the selection(s), used to implement + // backspace, delete, and similar functionality. + function deleteNearSelection(cm, compute) { + var ranges = cm.doc.sel.ranges, kill = []; + // Build up a set of ranges to kill first, merging overlapping + // ranges. + for (var i = 0; i < ranges.length; i++) { + var toKill = compute(ranges[i]); + while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) { + var replaced = kill.pop(); + if (cmp(replaced.from, toKill.from) < 0) { + toKill.from = replaced.from; + break + } + } + kill.push(toKill); + } + // Next, remove those actual ranges. + runInOp(cm, function () { + for (var i = kill.length - 1; i >= 0; i--) + { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); } + ensureCursorVisible(cm); + }); + } + + function moveCharLogically(line, ch, dir) { + var target = skipExtendingChars(line.text, ch + dir, dir); + return target < 0 || target > line.text.length ? null : target + } + + function moveLogically(line, start, dir) { + var ch = moveCharLogically(line, start.ch, dir); + return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before") + } + + function endOfLine(visually, cm, lineObj, lineNo, dir) { + if (visually) { + if (cm.doc.direction == "rtl") { dir = -dir; } + var order = getOrder(lineObj, cm.doc.direction); + if (order) { + var part = dir < 0 ? lst(order) : order[0]; + var moveInStorageOrder = (dir < 0) == (part.level == 1); + var sticky = moveInStorageOrder ? "after" : "before"; + var ch; + // With a wrapped rtl chunk (possibly spanning multiple bidi parts), + // it could be that the last bidi part is not on the last visual line, + // since visual lines contain content order-consecutive chunks. + // Thus, in rtl, we are looking for the first (content-order) character + // in the rtl chunk that is on the last line (that is, the same line + // as the last (content-order) character). + if (part.level > 0 || cm.doc.direction == "rtl") { + var prep = prepareMeasureForLine(cm, lineObj); + ch = dir < 0 ? lineObj.text.length - 1 : 0; + var targetTop = measureCharPrepared(cm, prep, ch).top; + ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch); + if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); } + } else { ch = dir < 0 ? part.to : part.from; } + return new Pos(lineNo, ch, sticky) + } + } + return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after") + } + + function moveVisually(cm, line, start, dir) { + var bidi = getOrder(line, cm.doc.direction); + if (!bidi) { return moveLogically(line, start, dir) } + if (start.ch >= line.text.length) { + start.ch = line.text.length; + start.sticky = "before"; + } else if (start.ch <= 0) { + start.ch = 0; + start.sticky = "after"; + } + var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]; + if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) { + // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines, + // nothing interesting happens. + return moveLogically(line, start, dir) + } + + var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }; + var prep; + var getWrappedLineExtent = function (ch) { + if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} } + prep = prep || prepareMeasureForLine(cm, line); + return wrappedLineExtentChar(cm, line, prep, ch) + }; + var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch); + + if (cm.doc.direction == "rtl" || part.level == 1) { + var moveInStorageOrder = (part.level == 1) == (dir < 0); + var ch = mv(start, moveInStorageOrder ? 1 : -1); + if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) { + // Case 2: We move within an rtl part or in an rtl editor on the same visual line + var sticky = moveInStorageOrder ? "before" : "after"; + return new Pos(start.line, ch, sticky) + } + } + + // Case 3: Could not move within this bidi part in this visual line, so leave + // the current bidi part + + var searchInVisualLine = function (partPos, dir, wrappedLineExtent) { + var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder + ? new Pos(start.line, mv(ch, 1), "before") + : new Pos(start.line, ch, "after"); }; + + for (; partPos >= 0 && partPos < bidi.length; partPos += dir) { + var part = bidi[partPos]; + var moveInStorageOrder = (dir > 0) == (part.level != 1); + var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1); + if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) } + ch = moveInStorageOrder ? part.from : mv(part.to, -1); + if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) } + } + }; + + // Case 3a: Look for other bidi parts on the same visual line + var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent); + if (res) { return res } + + // Case 3b: Look for other bidi parts on the next visual line + var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1); + if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) { + res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh)); + if (res) { return res } + } + + // Case 4: Nowhere to move + return null + } + + // Commands are parameter-less actions that can be performed on an + // editor, mostly used for keybindings. + var commands = { + selectAll: selectAll, + singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); }, + killLine: function (cm) { return deleteNearSelection(cm, function (range) { + if (range.empty()) { + var len = getLine(cm.doc, range.head.line).text.length; + if (range.head.ch == len && range.head.line < cm.lastLine()) + { return {from: range.head, to: Pos(range.head.line + 1, 0)} } + else + { return {from: range.head, to: Pos(range.head.line, len)} } + } else { + return {from: range.from(), to: range.to()} + } + }); }, + deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({ + from: Pos(range.from().line, 0), + to: clipPos(cm.doc, Pos(range.to().line + 1, 0)) + }); }); }, + delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({ + from: Pos(range.from().line, 0), to: range.from() + }); }); }, + delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { + var top = cm.charCoords(range.head, "div").top + 5; + var leftPos = cm.coordsChar({left: 0, top: top}, "div"); + return {from: leftPos, to: range.from()} + }); }, + delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) { + var top = cm.charCoords(range.head, "div").top + 5; + var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"); + return {from: range.from(), to: rightPos } + }); }, + undo: function (cm) { return cm.undo(); }, + redo: function (cm) { return cm.redo(); }, + undoSelection: function (cm) { return cm.undoSelection(); }, + redoSelection: function (cm) { return cm.redoSelection(); }, + goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); }, + goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); }, + goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); }, + {origin: "+move", bias: 1} + ); }, + goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); }, + {origin: "+move", bias: 1} + ); }, + goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); }, + {origin: "+move", bias: -1} + ); }, + goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.cursorCoords(range.head, "div").top + 5; + return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") + }, sel_move); }, + goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.cursorCoords(range.head, "div").top + 5; + return cm.coordsChar({left: 0, top: top}, "div") + }, sel_move); }, + goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.cursorCoords(range.head, "div").top + 5; + var pos = cm.coordsChar({left: 0, top: top}, "div"); + if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) } + return pos + }, sel_move); }, + goLineUp: function (cm) { return cm.moveV(-1, "line"); }, + goLineDown: function (cm) { return cm.moveV(1, "line"); }, + goPageUp: function (cm) { return cm.moveV(-1, "page"); }, + goPageDown: function (cm) { return cm.moveV(1, "page"); }, + goCharLeft: function (cm) { return cm.moveH(-1, "char"); }, + goCharRight: function (cm) { return cm.moveH(1, "char"); }, + goColumnLeft: function (cm) { return cm.moveH(-1, "column"); }, + goColumnRight: function (cm) { return cm.moveH(1, "column"); }, + goWordLeft: function (cm) { return cm.moveH(-1, "word"); }, + goGroupRight: function (cm) { return cm.moveH(1, "group"); }, + goGroupLeft: function (cm) { return cm.moveH(-1, "group"); }, + goWordRight: function (cm) { return cm.moveH(1, "word"); }, + delCharBefore: function (cm) { return cm.deleteH(-1, "codepoint"); }, + delCharAfter: function (cm) { return cm.deleteH(1, "char"); }, + delWordBefore: function (cm) { return cm.deleteH(-1, "word"); }, + delWordAfter: function (cm) { return cm.deleteH(1, "word"); }, + delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); }, + delGroupAfter: function (cm) { return cm.deleteH(1, "group"); }, + indentAuto: function (cm) { return cm.indentSelection("smart"); }, + indentMore: function (cm) { return cm.indentSelection("add"); }, + indentLess: function (cm) { return cm.indentSelection("subtract"); }, + insertTab: function (cm) { return cm.replaceSelection("\t"); }, + insertSoftTab: function (cm) { + var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize; + for (var i = 0; i < ranges.length; i++) { + var pos = ranges[i].from(); + var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize); + spaces.push(spaceStr(tabSize - col % tabSize)); + } + cm.replaceSelections(spaces); + }, + defaultTab: function (cm) { + if (cm.somethingSelected()) { cm.indentSelection("add"); } + else { cm.execCommand("insertTab"); } + }, + // Swap the two chars left and right of each selection's head. + // Move cursor behind the two swapped characters afterwards. + // + // Doesn't consider line feeds a character. + // Doesn't scan more than one line above to find a character. + // Doesn't do anything on an empty line. + // Doesn't do anything with non-empty selections. + transposeChars: function (cm) { return runInOp(cm, function () { + var ranges = cm.listSelections(), newSel = []; + for (var i = 0; i < ranges.length; i++) { + if (!ranges[i].empty()) { continue } + var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text; + if (line) { + if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); } + if (cur.ch > 0) { + cur = new Pos(cur.line, cur.ch + 1); + cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), + Pos(cur.line, cur.ch - 2), cur, "+transpose"); + } else if (cur.line > cm.doc.first) { + var prev = getLine(cm.doc, cur.line - 1).text; + if (prev) { + cur = new Pos(cur.line, 1); + cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() + + prev.charAt(prev.length - 1), + Pos(cur.line - 1, prev.length - 1), cur, "+transpose"); + } + } + } + newSel.push(new Range(cur, cur)); + } + cm.setSelections(newSel); + }); }, + newlineAndIndent: function (cm) { return runInOp(cm, function () { + var sels = cm.listSelections(); + for (var i = sels.length - 1; i >= 0; i--) + { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); } + sels = cm.listSelections(); + for (var i$1 = 0; i$1 < sels.length; i$1++) + { cm.indentLine(sels[i$1].from().line, null, true); } + ensureCursorVisible(cm); + }); }, + openLine: function (cm) { return cm.replaceSelection("\n", "start"); }, + toggleOverwrite: function (cm) { return cm.toggleOverwrite(); } + }; + + + function lineStart(cm, lineN) { + var line = getLine(cm.doc, lineN); + var visual = visualLine(line); + if (visual != line) { lineN = lineNo(visual); } + return endOfLine(true, cm, visual, lineN, 1) + } + function lineEnd(cm, lineN) { + var line = getLine(cm.doc, lineN); + var visual = visualLineEnd(line); + if (visual != line) { lineN = lineNo(visual); } + return endOfLine(true, cm, line, lineN, -1) + } + function lineStartSmart(cm, pos) { + var start = lineStart(cm, pos.line); + var line = getLine(cm.doc, start.line); + var order = getOrder(line, cm.doc.direction); + if (!order || order[0].level == 0) { + var firstNonWS = Math.max(start.ch, line.text.search(/\S/)); + var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch; + return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky) + } + return start + } + + // Run a handler that was bound to a key. + function doHandleBinding(cm, bound, dropShift) { + if (typeof bound == "string") { + bound = commands[bound]; + if (!bound) { return false } + } + // Ensure previous input has been read, so that the handler sees a + // consistent view of the document + cm.display.input.ensurePolled(); + var prevShift = cm.display.shift, done = false; + try { + if (cm.isReadOnly()) { cm.state.suppressEdits = true; } + if (dropShift) { cm.display.shift = false; } + done = bound(cm) != Pass; + } finally { + cm.display.shift = prevShift; + cm.state.suppressEdits = false; + } + return done + } + + function lookupKeyForEditor(cm, name, handle) { + for (var i = 0; i < cm.state.keyMaps.length; i++) { + var result = lookupKey(name, cm.state.keyMaps[i], handle, cm); + if (result) { return result } + } + return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm)) + || lookupKey(name, cm.options.keyMap, handle, cm) + } + + // Note that, despite the name, this function is also used to check + // for bound mouse clicks. + + var stopSeq = new Delayed; + + function dispatchKey(cm, name, e, handle) { + var seq = cm.state.keySeq; + if (seq) { + if (isModifierKey(name)) { return "handled" } + if (/\'$/.test(name)) + { cm.state.keySeq = null; } + else + { stopSeq.set(50, function () { + if (cm.state.keySeq == seq) { + cm.state.keySeq = null; + cm.display.input.reset(); + } + }); } + if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true } + } + return dispatchKeyInner(cm, name, e, handle) + } + + function dispatchKeyInner(cm, name, e, handle) { + var result = lookupKeyForEditor(cm, name, handle); + + if (result == "multi") + { cm.state.keySeq = name; } + if (result == "handled") + { signalLater(cm, "keyHandled", cm, name, e); } + + if (result == "handled" || result == "multi") { + e_preventDefault(e); + restartBlink(cm); + } + + return !!result + } + + // Handle a key from the keydown event. + function handleKeyBinding(cm, e) { + var name = keyName(e, true); + if (!name) { return false } + + if (e.shiftKey && !cm.state.keySeq) { + // First try to resolve full name (including 'Shift-'). Failing + // that, see if there is a cursor-motion command (starting with + // 'go') bound to the keyname without 'Shift-'. + return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); }) + || dispatchKey(cm, name, e, function (b) { + if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion) + { return doHandleBinding(cm, b) } + }) + } else { + return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); }) + } + } + + // Handle a key from the keypress event + function handleCharBinding(cm, e, ch) { + return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); }) + } + + var lastStoppedKey = null; + function onKeyDown(e) { + var cm = this; + if (e.target && e.target != cm.display.input.getField()) { return } + cm.curOp.focus = activeElt(doc(cm)); + if (signalDOMEvent(cm, e)) { return } + // IE does strange things with escape. + if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; } + var code = e.keyCode; + cm.display.shift = code == 16 || e.shiftKey; + var handled = handleKeyBinding(cm, e); + if (presto) { + lastStoppedKey = handled ? code : null; + // Opera has no cut event... we try to at least catch the key combo + if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey)) + { cm.replaceSelection("", null, "cut"); } + } + if (gecko && !mac && !handled && code == 46 && e.shiftKey && !e.ctrlKey && document.execCommand) + { document.execCommand("cut"); } + + // Turn mouse into crosshair when Alt is held on Mac. + if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className)) + { showCrossHair(cm); } + } + + function showCrossHair(cm) { + var lineDiv = cm.display.lineDiv; + addClass(lineDiv, "CodeMirror-crosshair"); + + function up(e) { + if (e.keyCode == 18 || !e.altKey) { + rmClass(lineDiv, "CodeMirror-crosshair"); + off(document, "keyup", up); + off(document, "mouseover", up); + } + } + on(document, "keyup", up); + on(document, "mouseover", up); + } + + function onKeyUp(e) { + if (e.keyCode == 16) { this.doc.sel.shift = false; } + signalDOMEvent(this, e); + } + + function onKeyPress(e) { + var cm = this; + if (e.target && e.target != cm.display.input.getField()) { return } + if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return } + var keyCode = e.keyCode, charCode = e.charCode; + if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return} + if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return } + var ch = String.fromCharCode(charCode == null ? keyCode : charCode); + // Some browsers fire keypress events for backspace + if (ch == "\x08") { return } + if (handleCharBinding(cm, e, ch)) { return } + cm.display.input.onKeyPress(e); + } + + var DOUBLECLICK_DELAY = 400; + + var PastClick = function(time, pos, button) { + this.time = time; + this.pos = pos; + this.button = button; + }; + + PastClick.prototype.compare = function (time, pos, button) { + return this.time + DOUBLECLICK_DELAY > time && + cmp(pos, this.pos) == 0 && button == this.button + }; + + var lastClick, lastDoubleClick; + function clickRepeat(pos, button) { + var now = +new Date; + if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) { + lastClick = lastDoubleClick = null; + return "triple" + } else if (lastClick && lastClick.compare(now, pos, button)) { + lastDoubleClick = new PastClick(now, pos, button); + lastClick = null; + return "double" + } else { + lastClick = new PastClick(now, pos, button); + lastDoubleClick = null; + return "single" + } + } + + // A mouse down can be a single click, double click, triple click, + // start of selection drag, start of text drag, new cursor + // (ctrl-click), rectangle drag (alt-drag), or xwin + // middle-click-paste. Or it might be a click on something we should + // not interfere with, such as a scrollbar or widget. + function onMouseDown(e) { + var cm = this, display = cm.display; + if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return } + display.input.ensurePolled(); + display.shift = e.shiftKey; + + if (eventInWidget(display, e)) { + if (!webkit) { + // Briefly turn off draggability, to allow widgets to do + // normal dragging things. + display.scroller.draggable = false; + setTimeout(function () { return display.scroller.draggable = true; }, 100); + } + return + } + if (clickInGutter(cm, e)) { return } + var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single"; + win(cm).focus(); + + // #3261: make sure, that we're not starting a second selection + if (button == 1 && cm.state.selectingText) + { cm.state.selectingText(e); } + + if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return } + + if (button == 1) { + if (pos) { leftButtonDown(cm, pos, repeat, e); } + else if (e_target(e) == display.scroller) { e_preventDefault(e); } + } else if (button == 2) { + if (pos) { extendSelection(cm.doc, pos); } + setTimeout(function () { return display.input.focus(); }, 20); + } else if (button == 3) { + if (captureRightClick) { cm.display.input.onContextMenu(e); } + else { delayBlurEvent(cm); } + } + } + + function handleMappedButton(cm, button, pos, repeat, event) { + var name = "Click"; + if (repeat == "double") { name = "Double" + name; } + else if (repeat == "triple") { name = "Triple" + name; } + name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name; + + return dispatchKey(cm, addModifierNames(name, event), event, function (bound) { + if (typeof bound == "string") { bound = commands[bound]; } + if (!bound) { return false } + var done = false; + try { + if (cm.isReadOnly()) { cm.state.suppressEdits = true; } + done = bound(cm, pos) != Pass; + } finally { + cm.state.suppressEdits = false; + } + return done + }) + } + + function configureMouse(cm, repeat, event) { + var option = cm.getOption("configureMouse"); + var value = option ? option(cm, repeat, event) : {}; + if (value.unit == null) { + var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey; + value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line"; + } + if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; } + if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; } + if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); } + return value + } + + function leftButtonDown(cm, pos, repeat, event) { + if (ie) { setTimeout(bind(ensureFocus, cm), 0); } + else { cm.curOp.focus = activeElt(doc(cm)); } + + var behavior = configureMouse(cm, repeat, event); + + var sel = cm.doc.sel, contained; + if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() && + repeat == "single" && (contained = sel.contains(pos)) > -1 && + (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) && + (cmp(contained.to(), pos) > 0 || pos.xRel < 0)) + { leftButtonStartDrag(cm, event, pos, behavior); } + else + { leftButtonSelect(cm, event, pos, behavior); } + } + + // Start a text drag. When it ends, see if any dragging actually + // happen, and treat as a click if it didn't. + function leftButtonStartDrag(cm, event, pos, behavior) { + var display = cm.display, moved = false; + var dragEnd = operation(cm, function (e) { + if (webkit) { display.scroller.draggable = false; } + cm.state.draggingText = false; + if (cm.state.delayingBlurEvent) { + if (cm.hasFocus()) { cm.state.delayingBlurEvent = false; } + else { delayBlurEvent(cm); } + } + off(display.wrapper.ownerDocument, "mouseup", dragEnd); + off(display.wrapper.ownerDocument, "mousemove", mouseMove); + off(display.scroller, "dragstart", dragStart); + off(display.scroller, "drop", dragEnd); + if (!moved) { + e_preventDefault(e); + if (!behavior.addNew) + { extendSelection(cm.doc, pos, null, null, behavior.extend); } + // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081) + if ((webkit && !safari) || ie && ie_version == 9) + { setTimeout(function () {display.wrapper.ownerDocument.body.focus({preventScroll: true}); display.input.focus();}, 20); } + else + { display.input.focus(); } + } + }); + var mouseMove = function(e2) { + moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10; + }; + var dragStart = function () { return moved = true; }; + // Let the drag handler handle this. + if (webkit) { display.scroller.draggable = true; } + cm.state.draggingText = dragEnd; + dragEnd.copy = !behavior.moveOnDrag; + on(display.wrapper.ownerDocument, "mouseup", dragEnd); + on(display.wrapper.ownerDocument, "mousemove", mouseMove); + on(display.scroller, "dragstart", dragStart); + on(display.scroller, "drop", dragEnd); + + cm.state.delayingBlurEvent = true; + setTimeout(function () { return display.input.focus(); }, 20); + // IE's approach to draggable + if (display.scroller.dragDrop) { display.scroller.dragDrop(); } + } + + function rangeForUnit(cm, pos, unit) { + if (unit == "char") { return new Range(pos, pos) } + if (unit == "word") { return cm.findWordAt(pos) } + if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) } + var result = unit(cm, pos); + return new Range(result.from, result.to) + } + + // Normal selection, as opposed to text dragging. + function leftButtonSelect(cm, event, start, behavior) { + if (ie) { delayBlurEvent(cm); } + var display = cm.display, doc$1 = cm.doc; + e_preventDefault(event); + + var ourRange, ourIndex, startSel = doc$1.sel, ranges = startSel.ranges; + if (behavior.addNew && !behavior.extend) { + ourIndex = doc$1.sel.contains(start); + if (ourIndex > -1) + { ourRange = ranges[ourIndex]; } + else + { ourRange = new Range(start, start); } + } else { + ourRange = doc$1.sel.primary(); + ourIndex = doc$1.sel.primIndex; + } + + if (behavior.unit == "rectangle") { + if (!behavior.addNew) { ourRange = new Range(start, start); } + start = posFromMouse(cm, event, true, true); + ourIndex = -1; + } else { + var range = rangeForUnit(cm, start, behavior.unit); + if (behavior.extend) + { ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend); } + else + { ourRange = range; } + } + + if (!behavior.addNew) { + ourIndex = 0; + setSelection(doc$1, new Selection([ourRange], 0), sel_mouse); + startSel = doc$1.sel; + } else if (ourIndex == -1) { + ourIndex = ranges.length; + setSelection(doc$1, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex), + {scroll: false, origin: "*mouse"}); + } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) { + setSelection(doc$1, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0), + {scroll: false, origin: "*mouse"}); + startSel = doc$1.sel; + } else { + replaceOneSelection(doc$1, ourIndex, ourRange, sel_mouse); + } + + var lastPos = start; + function extendTo(pos) { + if (cmp(lastPos, pos) == 0) { return } + lastPos = pos; + + if (behavior.unit == "rectangle") { + var ranges = [], tabSize = cm.options.tabSize; + var startCol = countColumn(getLine(doc$1, start.line).text, start.ch, tabSize); + var posCol = countColumn(getLine(doc$1, pos.line).text, pos.ch, tabSize); + var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol); + for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line)); + line <= end; line++) { + var text = getLine(doc$1, line).text, leftPos = findColumn(text, left, tabSize); + if (left == right) + { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); } + else if (text.length > leftPos) + { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); } + } + if (!ranges.length) { ranges.push(new Range(start, start)); } + setSelection(doc$1, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex), + {origin: "*mouse", scroll: false}); + cm.scrollIntoView(pos); + } else { + var oldRange = ourRange; + var range = rangeForUnit(cm, pos, behavior.unit); + var anchor = oldRange.anchor, head; + if (cmp(range.anchor, anchor) > 0) { + head = range.head; + anchor = minPos(oldRange.from(), range.anchor); + } else { + head = range.anchor; + anchor = maxPos(oldRange.to(), range.head); + } + var ranges$1 = startSel.ranges.slice(0); + ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc$1, anchor), head)); + setSelection(doc$1, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse); + } + } + + var editorSize = display.wrapper.getBoundingClientRect(); + // Used to ensure timeout re-tries don't fire when another extend + // happened in the meantime (clearTimeout isn't reliable -- at + // least on Chrome, the timeouts still happen even when cleared, + // if the clear happens after their scheduled firing time). + var counter = 0; + + function extend(e) { + var curCount = ++counter; + var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle"); + if (!cur) { return } + if (cmp(cur, lastPos) != 0) { + cm.curOp.focus = activeElt(doc(cm)); + extendTo(cur); + var visible = visibleLines(display, doc$1); + if (cur.line >= visible.to || cur.line < visible.from) + { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); } + } else { + var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0; + if (outside) { setTimeout(operation(cm, function () { + if (counter != curCount) { return } + display.scroller.scrollTop += outside; + extend(e); + }), 50); } + } + } + + function done(e) { + cm.state.selectingText = false; + counter = Infinity; + // If e is null or undefined we interpret this as someone trying + // to explicitly cancel the selection rather than the user + // letting go of the mouse button. + if (e) { + e_preventDefault(e); + display.input.focus(); + } + off(display.wrapper.ownerDocument, "mousemove", move); + off(display.wrapper.ownerDocument, "mouseup", up); + doc$1.history.lastSelOrigin = null; + } + + var move = operation(cm, function (e) { + if (e.buttons === 0 || !e_button(e)) { done(e); } + else { extend(e); } + }); + var up = operation(cm, done); + cm.state.selectingText = up; + on(display.wrapper.ownerDocument, "mousemove", move); + on(display.wrapper.ownerDocument, "mouseup", up); + } + + // Used when mouse-selecting to adjust the anchor to the proper side + // of a bidi jump depending on the visual position of the head. + function bidiSimplify(cm, range) { + var anchor = range.anchor; + var head = range.head; + var anchorLine = getLine(cm.doc, anchor.line); + if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range } + var order = getOrder(anchorLine); + if (!order) { return range } + var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index]; + if (part.from != anchor.ch && part.to != anchor.ch) { return range } + var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1); + if (boundary == 0 || boundary == order.length) { return range } + + // Compute the relative visual position of the head compared to the + // anchor (<0 is to the left, >0 to the right) + var leftSide; + if (head.line != anchor.line) { + leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0; + } else { + var headIndex = getBidiPartAt(order, head.ch, head.sticky); + var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1); + if (headIndex == boundary - 1 || headIndex == boundary) + { leftSide = dir < 0; } + else + { leftSide = dir > 0; } + } + + var usePart = order[boundary + (leftSide ? -1 : 0)]; + var from = leftSide == (usePart.level == 1); + var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before"; + return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head) + } + + + // Determines whether an event happened in the gutter, and fires the + // handlers for the corresponding event. + function gutterEvent(cm, e, type, prevent) { + var mX, mY; + if (e.touches) { + mX = e.touches[0].clientX; + mY = e.touches[0].clientY; + } else { + try { mX = e.clientX; mY = e.clientY; } + catch(e$1) { return false } + } + if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false } + if (prevent) { e_preventDefault(e); } + + var display = cm.display; + var lineBox = display.lineDiv.getBoundingClientRect(); + + if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) } + mY -= lineBox.top - display.viewOffset; + + for (var i = 0; i < cm.display.gutterSpecs.length; ++i) { + var g = display.gutters.childNodes[i]; + if (g && g.getBoundingClientRect().right >= mX) { + var line = lineAtHeight(cm.doc, mY); + var gutter = cm.display.gutterSpecs[i]; + signal(cm, type, cm, line, gutter.className, e); + return e_defaultPrevented(e) + } + } + } + + function clickInGutter(cm, e) { + return gutterEvent(cm, e, "gutterClick", true) + } + + // CONTEXT MENU HANDLING + + // To make the context menu work, we need to briefly unhide the + // textarea (making it as unobtrusive as possible) to let the + // right-click take effect on it. + function onContextMenu(cm, e) { + if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return } + if (signalDOMEvent(cm, e, "contextmenu")) { return } + if (!captureRightClick) { cm.display.input.onContextMenu(e); } + } + + function contextMenuInGutter(cm, e) { + if (!hasHandler(cm, "gutterContextMenu")) { return false } + return gutterEvent(cm, e, "gutterContextMenu", false) + } + + function themeChanged(cm) { + cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") + + cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-"); + clearCaches(cm); + } + + var Init = {toString: function(){return "CodeMirror.Init"}}; + + var defaults = {}; + var optionHandlers = {}; + + function defineOptions(CodeMirror) { + var optionHandlers = CodeMirror.optionHandlers; + + function option(name, deflt, handle, notOnInit) { + CodeMirror.defaults[name] = deflt; + if (handle) { optionHandlers[name] = + notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; } + } + + CodeMirror.defineOption = option; + + // Passed to option handlers when there is no old value. + CodeMirror.Init = Init; + + // These two are, on init, called from the constructor because they + // have to be initialized before the editor can start at all. + option("value", "", function (cm, val) { return cm.setValue(val); }, true); + option("mode", null, function (cm, val) { + cm.doc.modeOption = val; + loadMode(cm); + }, true); + + option("indentUnit", 2, loadMode, true); + option("indentWithTabs", false); + option("smartIndent", true); + option("tabSize", 4, function (cm) { + resetModeState(cm); + clearCaches(cm); + regChange(cm); + }, true); + + option("lineSeparator", null, function (cm, val) { + cm.doc.lineSep = val; + if (!val) { return } + var newBreaks = [], lineNo = cm.doc.first; + cm.doc.iter(function (line) { + for (var pos = 0;;) { + var found = line.text.indexOf(val, pos); + if (found == -1) { break } + pos = found + val.length; + newBreaks.push(Pos(lineNo, found)); + } + lineNo++; + }); + for (var i = newBreaks.length - 1; i >= 0; i--) + { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); } + }); + option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\u202d\u202e\u2066\u2067\u2069\ufeff\ufff9-\ufffc]/g, function (cm, val, old) { + cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g"); + if (old != Init) { cm.refresh(); } + }); + option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true); + option("electricChars", true); + option("inputStyle", mobile ? "contenteditable" : "textarea", function () { + throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME + }, true); + option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true); + option("autocorrect", false, function (cm, val) { return cm.getInputField().autocorrect = val; }, true); + option("autocapitalize", false, function (cm, val) { return cm.getInputField().autocapitalize = val; }, true); + option("rtlMoveVisually", !windows); + option("wholeLineUpdateBefore", true); + + option("theme", "default", function (cm) { + themeChanged(cm); + updateGutters(cm); + }, true); + option("keyMap", "default", function (cm, val, old) { + var next = getKeyMap(val); + var prev = old != Init && getKeyMap(old); + if (prev && prev.detach) { prev.detach(cm, next); } + if (next.attach) { next.attach(cm, prev || null); } + }); + option("extraKeys", null); + option("configureMouse", null); + + option("lineWrapping", false, wrappingChanged, true); + option("gutters", [], function (cm, val) { + cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers); + updateGutters(cm); + }, true); + option("fixedGutter", true, function (cm, val) { + cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"; + cm.refresh(); + }, true); + option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true); + option("scrollbarStyle", "native", function (cm) { + initScrollbars(cm); + updateScrollbars(cm); + cm.display.scrollbars.setScrollTop(cm.doc.scrollTop); + cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft); + }, true); + option("lineNumbers", false, function (cm, val) { + cm.display.gutterSpecs = getGutters(cm.options.gutters, val); + updateGutters(cm); + }, true); + option("firstLineNumber", 1, updateGutters, true); + option("lineNumberFormatter", function (integer) { return integer; }, updateGutters, true); + option("showCursorWhenSelecting", false, updateSelection, true); + + option("resetSelectionOnContextMenu", true); + option("lineWiseCopyCut", true); + option("pasteLinesPerSelection", true); + option("selectionsMayTouch", false); + + option("readOnly", false, function (cm, val) { + if (val == "nocursor") { + onBlur(cm); + cm.display.input.blur(); + } + cm.display.input.readOnlyChanged(val); + }); + + option("screenReaderLabel", null, function (cm, val) { + val = (val === '') ? null : val; + cm.display.input.screenReaderLabelChanged(val); + }); + + option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true); + option("dragDrop", true, dragDropChanged); + option("allowDropFileTypes", null); + + option("cursorBlinkRate", 530); + option("cursorScrollMargin", 0); + option("cursorHeight", 1, updateSelection, true); + option("singleCursorHeightPerLine", true, updateSelection, true); + option("workTime", 100); + option("workDelay", 100); + option("flattenSpans", true, resetModeState, true); + option("addModeClass", false, resetModeState, true); + option("pollInterval", 100); + option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; }); + option("historyEventDelay", 1250); + option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true); + option("maxHighlightLength", 10000, resetModeState, true); + option("moveInputWithCursor", true, function (cm, val) { + if (!val) { cm.display.input.resetPosition(); } + }); + + option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; }); + option("autofocus", null); + option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true); + option("phrases", null); + } + + function dragDropChanged(cm, value, old) { + var wasOn = old && old != Init; + if (!value != !wasOn) { + var funcs = cm.display.dragFunctions; + var toggle = value ? on : off; + toggle(cm.display.scroller, "dragstart", funcs.start); + toggle(cm.display.scroller, "dragenter", funcs.enter); + toggle(cm.display.scroller, "dragover", funcs.over); + toggle(cm.display.scroller, "dragleave", funcs.leave); + toggle(cm.display.scroller, "drop", funcs.drop); + } + } + + function wrappingChanged(cm) { + if (cm.options.lineWrapping) { + addClass(cm.display.wrapper, "CodeMirror-wrap"); + cm.display.sizer.style.minWidth = ""; + cm.display.sizerWidth = null; + } else { + rmClass(cm.display.wrapper, "CodeMirror-wrap"); + findMaxLine(cm); + } + estimateLineHeights(cm); + regChange(cm); + clearCaches(cm); + setTimeout(function () { return updateScrollbars(cm); }, 100); + } + + // A CodeMirror instance represents an editor. This is the object + // that user code is usually dealing with. + + function CodeMirror(place, options) { + var this$1 = this; + + if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) } + + this.options = options = options ? copyObj(options) : {}; + // Determine effective options based on given values and defaults. + copyObj(defaults, options, false); + + var doc = options.value; + if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); } + else if (options.mode) { doc.modeOption = options.mode; } + this.doc = doc; + + var input = new CodeMirror.inputStyles[options.inputStyle](this); + var display = this.display = new Display(place, doc, input, options); + display.wrapper.CodeMirror = this; + themeChanged(this); + if (options.lineWrapping) + { this.display.wrapper.className += " CodeMirror-wrap"; } + initScrollbars(this); + + this.state = { + keyMaps: [], // stores maps added by addKeyMap + overlays: [], // highlighting overlays, as added by addOverlay + modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info + overwrite: false, + delayingBlurEvent: false, + focused: false, + suppressEdits: false, // used to disable editing during key handlers when in readOnly mode + pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll + selectingText: false, + draggingText: false, + highlight: new Delayed(), // stores highlight worker timeout + keySeq: null, // Unfinished key sequence + specialChars: null + }; + + if (options.autofocus && !mobile) { display.input.focus(); } + + // Override magic textarea content restore that IE sometimes does + // on our hidden textarea on reload + if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); } + + registerEventHandlers(this); + ensureGlobalHandlers(); + + startOperation(this); + this.curOp.forceUpdate = true; + attachDoc(this, doc); + + if ((options.autofocus && !mobile) || this.hasFocus()) + { setTimeout(function () { + if (this$1.hasFocus() && !this$1.state.focused) { onFocus(this$1); } + }, 20); } + else + { onBlur(this); } + + for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt)) + { optionHandlers[opt](this, options[opt], Init); } } + maybeUpdateLineNumberWidth(this); + if (options.finishInit) { options.finishInit(this); } + for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this); } + endOperation(this); + // Suppress optimizelegibility in Webkit, since it breaks text + // measuring on line wrapping boundaries. + if (webkit && options.lineWrapping && + getComputedStyle(display.lineDiv).textRendering == "optimizelegibility") + { display.lineDiv.style.textRendering = "auto"; } + } + + // The default configuration options. + CodeMirror.defaults = defaults; + // Functions to run when options are changed. + CodeMirror.optionHandlers = optionHandlers; + + // Attach the necessary event handlers when initializing the editor + function registerEventHandlers(cm) { + var d = cm.display; + on(d.scroller, "mousedown", operation(cm, onMouseDown)); + // Older IE's will not fire a second mousedown for a double click + if (ie && ie_version < 11) + { on(d.scroller, "dblclick", operation(cm, function (e) { + if (signalDOMEvent(cm, e)) { return } + var pos = posFromMouse(cm, e); + if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return } + e_preventDefault(e); + var word = cm.findWordAt(pos); + extendSelection(cm.doc, word.anchor, word.head); + })); } + else + { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); } + // Some browsers fire contextmenu *after* opening the menu, at + // which point we can't mess with it anymore. Context menu is + // handled in onMouseDown for these browsers. + on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }); + on(d.input.getField(), "contextmenu", function (e) { + if (!d.scroller.contains(e.target)) { onContextMenu(cm, e); } + }); + + // Used to suppress mouse event handling when a touch happens + var touchFinished, prevTouch = {end: 0}; + function finishTouch() { + if (d.activeTouch) { + touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000); + prevTouch = d.activeTouch; + prevTouch.end = +new Date; + } + } + function isMouseLikeTouchEvent(e) { + if (e.touches.length != 1) { return false } + var touch = e.touches[0]; + return touch.radiusX <= 1 && touch.radiusY <= 1 + } + function farAway(touch, other) { + if (other.left == null) { return true } + var dx = other.left - touch.left, dy = other.top - touch.top; + return dx * dx + dy * dy > 20 * 20 + } + on(d.scroller, "touchstart", function (e) { + if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) { + d.input.ensurePolled(); + clearTimeout(touchFinished); + var now = +new Date; + d.activeTouch = {start: now, moved: false, + prev: now - prevTouch.end <= 300 ? prevTouch : null}; + if (e.touches.length == 1) { + d.activeTouch.left = e.touches[0].pageX; + d.activeTouch.top = e.touches[0].pageY; + } + } + }); + on(d.scroller, "touchmove", function () { + if (d.activeTouch) { d.activeTouch.moved = true; } + }); + on(d.scroller, "touchend", function (e) { + var touch = d.activeTouch; + if (touch && !eventInWidget(d, e) && touch.left != null && + !touch.moved && new Date - touch.start < 300) { + var pos = cm.coordsChar(d.activeTouch, "page"), range; + if (!touch.prev || farAway(touch, touch.prev)) // Single tap + { range = new Range(pos, pos); } + else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap + { range = cm.findWordAt(pos); } + else // Triple tap + { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); } + cm.setSelection(range.anchor, range.head); + cm.focus(); + e_preventDefault(e); + } + finishTouch(); + }); + on(d.scroller, "touchcancel", finishTouch); + + // Sync scrolling between fake scrollbars and real scrollable + // area, ensure viewport is updated when scrolling. + on(d.scroller, "scroll", function () { + if (d.scroller.clientHeight) { + updateScrollTop(cm, d.scroller.scrollTop); + setScrollLeft(cm, d.scroller.scrollLeft, true); + signal(cm, "scroll", cm); + } + }); + + // Listen to wheel events in order to try and update the viewport on time. + on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); }); + on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); }); + + // Prevent wrapper from ever scrolling + on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); + + d.dragFunctions = { + enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }}, + over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }}, + start: function (e) { return onDragStart(cm, e); }, + drop: operation(cm, onDrop), + leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }} + }; + + var inp = d.input.getField(); + on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); }); + on(inp, "keydown", operation(cm, onKeyDown)); + on(inp, "keypress", operation(cm, onKeyPress)); + on(inp, "focus", function (e) { return onFocus(cm, e); }); + on(inp, "blur", function (e) { return onBlur(cm, e); }); + } + + var initHooks = []; + CodeMirror.defineInitHook = function (f) { return initHooks.push(f); }; + + // Indent the given line. The how parameter can be "smart", + // "add"/null, "subtract", or "prev". When aggressive is false + // (typically set to true for forced single-line indents), empty + // lines are not indented, and places where the mode returns Pass + // are left alone. + function indentLine(cm, n, how, aggressive) { + var doc = cm.doc, state; + if (how == null) { how = "add"; } + if (how == "smart") { + // Fall back to "prev" when the mode doesn't have an indentation + // method. + if (!doc.mode.indent) { how = "prev"; } + else { state = getContextBefore(cm, n).state; } + } + + var tabSize = cm.options.tabSize; + var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize); + if (line.stateAfter) { line.stateAfter = null; } + var curSpaceString = line.text.match(/^\s*/)[0], indentation; + if (!aggressive && !/\S/.test(line.text)) { + indentation = 0; + how = "not"; + } else if (how == "smart") { + indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text); + if (indentation == Pass || indentation > 150) { + if (!aggressive) { return } + how = "prev"; + } + } + if (how == "prev") { + if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); } + else { indentation = 0; } + } else if (how == "add") { + indentation = curSpace + cm.options.indentUnit; + } else if (how == "subtract") { + indentation = curSpace - cm.options.indentUnit; + } else if (typeof how == "number") { + indentation = curSpace + how; + } + indentation = Math.max(0, indentation); + + var indentString = "", pos = 0; + if (cm.options.indentWithTabs) + { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} } + if (pos < indentation) { indentString += spaceStr(indentation - pos); } + + if (indentString != curSpaceString) { + replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); + line.stateAfter = null; + return true + } else { + // Ensure that, if the cursor was in the whitespace at the start + // of the line, it is moved to the end of that space. + for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) { + var range = doc.sel.ranges[i$1]; + if (range.head.line == n && range.head.ch < curSpaceString.length) { + var pos$1 = Pos(n, curSpaceString.length); + replaceOneSelection(doc, i$1, new Range(pos$1, pos$1)); + break + } + } + } + } + + // This will be set to a {lineWise: bool, text: [string]} object, so + // that, when pasting, we know what kind of selections the copied + // text was made out of. + var lastCopied = null; + + function setLastCopied(newLastCopied) { + lastCopied = newLastCopied; + } + + function applyTextInput(cm, inserted, deleted, sel, origin) { + var doc = cm.doc; + cm.display.shift = false; + if (!sel) { sel = doc.sel; } + + var recent = +new Date - 200; + var paste = origin == "paste" || cm.state.pasteIncoming > recent; + var textLines = splitLinesAuto(inserted), multiPaste = null; + // When pasting N lines into N selections, insert one line per selection + if (paste && sel.ranges.length > 1) { + if (lastCopied && lastCopied.text.join("\n") == inserted) { + if (sel.ranges.length % lastCopied.text.length == 0) { + multiPaste = []; + for (var i = 0; i < lastCopied.text.length; i++) + { multiPaste.push(doc.splitLines(lastCopied.text[i])); } + } + } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) { + multiPaste = map(textLines, function (l) { return [l]; }); + } + } + + var updateInput = cm.curOp.updateInput; + // Normal behavior is to insert the new text into every selection + for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) { + var range = sel.ranges[i$1]; + var from = range.from(), to = range.to(); + if (range.empty()) { + if (deleted && deleted > 0) // Handle deletion + { from = Pos(from.line, from.ch - deleted); } + else if (cm.state.overwrite && !paste) // Handle overwrite + { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); } + else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == textLines.join("\n")) + { from = to = Pos(from.line, 0); } + } + var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines, + origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")}; + makeChange(cm.doc, changeEvent); + signalLater(cm, "inputRead", cm, changeEvent); + } + if (inserted && !paste) + { triggerElectric(cm, inserted); } + + ensureCursorVisible(cm); + if (cm.curOp.updateInput < 2) { cm.curOp.updateInput = updateInput; } + cm.curOp.typing = true; + cm.state.pasteIncoming = cm.state.cutIncoming = -1; + } + + function handlePaste(e, cm) { + var pasted = e.clipboardData && e.clipboardData.getData("Text"); + if (pasted) { + e.preventDefault(); + if (!cm.isReadOnly() && !cm.options.disableInput && cm.hasFocus()) + { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); } + return true + } + } + + function triggerElectric(cm, inserted) { + // When an 'electric' character is inserted, immediately trigger a reindent + if (!cm.options.electricChars || !cm.options.smartIndent) { return } + var sel = cm.doc.sel; + + for (var i = sel.ranges.length - 1; i >= 0; i--) { + var range = sel.ranges[i]; + if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) { continue } + var mode = cm.getModeAt(range.head); + var indented = false; + if (mode.electricChars) { + for (var j = 0; j < mode.electricChars.length; j++) + { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) { + indented = indentLine(cm, range.head.line, "smart"); + break + } } + } else if (mode.electricInput) { + if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch))) + { indented = indentLine(cm, range.head.line, "smart"); } + } + if (indented) { signalLater(cm, "electricInput", cm, range.head.line); } + } + } + + function copyableRanges(cm) { + var text = [], ranges = []; + for (var i = 0; i < cm.doc.sel.ranges.length; i++) { + var line = cm.doc.sel.ranges[i].head.line; + var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}; + ranges.push(lineRange); + text.push(cm.getRange(lineRange.anchor, lineRange.head)); + } + return {text: text, ranges: ranges} + } + + function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) { + field.setAttribute("autocorrect", autocorrect ? "" : "off"); + field.setAttribute("autocapitalize", autocapitalize ? "" : "off"); + field.setAttribute("spellcheck", !!spellcheck); + } + + function hiddenTextarea() { + var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; min-height: 1em; outline: none"); + var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); + // The textarea is kept positioned near the cursor to prevent the + // fact that it'll be scrolled into view on input from scrolling + // our fake cursor out of view. On webkit, when wrap=off, paste is + // very slow. So make the area wide instead. + if (webkit) { te.style.width = "1000px"; } + else { te.setAttribute("wrap", "off"); } + // If border: 0; -- iOS fails to open keyboard (issue #1287) + if (ios) { te.style.border = "1px solid black"; } + disableBrowserMagic(te); + return div + } + + // The publicly visible API. Note that methodOp(f) means + // 'wrap f in an operation, performed on its `this` parameter'. + + // This is not the complete set of editor methods. Most of the + // methods defined on the Doc type are also injected into + // CodeMirror.prototype, for backwards compatibility and + // convenience. + + function addEditorMethods(CodeMirror) { + var optionHandlers = CodeMirror.optionHandlers; + + var helpers = CodeMirror.helpers = {}; + + CodeMirror.prototype = { + constructor: CodeMirror, + focus: function(){win(this).focus(); this.display.input.focus();}, + + setOption: function(option, value) { + var options = this.options, old = options[option]; + if (options[option] == value && option != "mode") { return } + options[option] = value; + if (optionHandlers.hasOwnProperty(option)) + { operation(this, optionHandlers[option])(this, value, old); } + signal(this, "optionChange", this, option); + }, + + getOption: function(option) {return this.options[option]}, + getDoc: function() {return this.doc}, + + addKeyMap: function(map, bottom) { + this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map)); + }, + removeKeyMap: function(map) { + var maps = this.state.keyMaps; + for (var i = 0; i < maps.length; ++i) + { if (maps[i] == map || maps[i].name == map) { + maps.splice(i, 1); + return true + } } + }, + + addOverlay: methodOp(function(spec, options) { + var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec); + if (mode.startState) { throw new Error("Overlays may not be stateful.") } + insertSorted(this.state.overlays, + {mode: mode, modeSpec: spec, opaque: options && options.opaque, + priority: (options && options.priority) || 0}, + function (overlay) { return overlay.priority; }); + this.state.modeGen++; + regChange(this); + }), + removeOverlay: methodOp(function(spec) { + var overlays = this.state.overlays; + for (var i = 0; i < overlays.length; ++i) { + var cur = overlays[i].modeSpec; + if (cur == spec || typeof spec == "string" && cur.name == spec) { + overlays.splice(i, 1); + this.state.modeGen++; + regChange(this); + return + } + } + }), + + indentLine: methodOp(function(n, dir, aggressive) { + if (typeof dir != "string" && typeof dir != "number") { + if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; } + else { dir = dir ? "add" : "subtract"; } + } + if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); } + }), + indentSelection: methodOp(function(how) { + var ranges = this.doc.sel.ranges, end = -1; + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i]; + if (!range.empty()) { + var from = range.from(), to = range.to(); + var start = Math.max(end, from.line); + end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; + for (var j = start; j < end; ++j) + { indentLine(this, j, how); } + var newRanges = this.doc.sel.ranges; + if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0) + { replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); } + } else if (range.head.line > end) { + indentLine(this, range.head.line, how, true); + end = range.head.line; + if (i == this.doc.sel.primIndex) { ensureCursorVisible(this); } + } + } + }), + + // Fetch the parser token for a given character. Useful for hacks + // that want to inspect the mode state (say, for completion). + getTokenAt: function(pos, precise) { + return takeToken(this, pos, precise) + }, + + getLineTokens: function(line, precise) { + return takeToken(this, Pos(line), precise, true) + }, + + getTokenTypeAt: function(pos) { + pos = clipPos(this.doc, pos); + var styles = getLineStyles(this, getLine(this.doc, pos.line)); + var before = 0, after = (styles.length - 1) / 2, ch = pos.ch; + var type; + if (ch == 0) { type = styles[2]; } + else { for (;;) { + var mid = (before + after) >> 1; + if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; } + else if (styles[mid * 2 + 1] < ch) { before = mid + 1; } + else { type = styles[mid * 2 + 2]; break } + } } + var cut = type ? type.indexOf("overlay ") : -1; + return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1) + }, + + getModeAt: function(pos) { + var mode = this.doc.mode; + if (!mode.innerMode) { return mode } + return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode + }, + + getHelper: function(pos, type) { + return this.getHelpers(pos, type)[0] + }, + + getHelpers: function(pos, type) { + var found = []; + if (!helpers.hasOwnProperty(type)) { return found } + var help = helpers[type], mode = this.getModeAt(pos); + if (typeof mode[type] == "string") { + if (help[mode[type]]) { found.push(help[mode[type]]); } + } else if (mode[type]) { + for (var i = 0; i < mode[type].length; i++) { + var val = help[mode[type][i]]; + if (val) { found.push(val); } + } + } else if (mode.helperType && help[mode.helperType]) { + found.push(help[mode.helperType]); + } else if (help[mode.name]) { + found.push(help[mode.name]); + } + for (var i$1 = 0; i$1 < help._global.length; i$1++) { + var cur = help._global[i$1]; + if (cur.pred(mode, this) && indexOf(found, cur.val) == -1) + { found.push(cur.val); } + } + return found + }, + + getStateAfter: function(line, precise) { + var doc = this.doc; + line = clipLine(doc, line == null ? doc.first + doc.size - 1: line); + return getContextBefore(this, line + 1, precise).state + }, + + cursorCoords: function(start, mode) { + var pos, range = this.doc.sel.primary(); + if (start == null) { pos = range.head; } + else if (typeof start == "object") { pos = clipPos(this.doc, start); } + else { pos = start ? range.from() : range.to(); } + return cursorCoords(this, pos, mode || "page") + }, + + charCoords: function(pos, mode) { + return charCoords(this, clipPos(this.doc, pos), mode || "page") + }, + + coordsChar: function(coords, mode) { + coords = fromCoordSystem(this, coords, mode || "page"); + return coordsChar(this, coords.left, coords.top) + }, + + lineAtHeight: function(height, mode) { + height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top; + return lineAtHeight(this.doc, height + this.display.viewOffset) + }, + heightAtLine: function(line, mode, includeWidgets) { + var end = false, lineObj; + if (typeof line == "number") { + var last = this.doc.first + this.doc.size - 1; + if (line < this.doc.first) { line = this.doc.first; } + else if (line > last) { line = last; end = true; } + lineObj = getLine(this.doc, line); + } else { + lineObj = line; + } + return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top + + (end ? this.doc.height - heightAtLine(lineObj) : 0) + }, + + defaultTextHeight: function() { return textHeight(this.display) }, + defaultCharWidth: function() { return charWidth(this.display) }, + + getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}}, + + addWidget: function(pos, node, scroll, vert, horiz) { + var display = this.display; + pos = cursorCoords(this, clipPos(this.doc, pos)); + var top = pos.bottom, left = pos.left; + node.style.position = "absolute"; + node.setAttribute("cm-ignore-events", "true"); + this.display.input.setUneditable(node); + display.sizer.appendChild(node); + if (vert == "over") { + top = pos.top; + } else if (vert == "above" || vert == "near") { + var vspace = Math.max(display.wrapper.clientHeight, this.doc.height), + hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth); + // Default to positioning above (if specified and possible); otherwise default to positioning below + if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight) + { top = pos.top - node.offsetHeight; } + else if (pos.bottom + node.offsetHeight <= vspace) + { top = pos.bottom; } + if (left + node.offsetWidth > hspace) + { left = hspace - node.offsetWidth; } + } + node.style.top = top + "px"; + node.style.left = node.style.right = ""; + if (horiz == "right") { + left = display.sizer.clientWidth - node.offsetWidth; + node.style.right = "0px"; + } else { + if (horiz == "left") { left = 0; } + else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; } + node.style.left = left + "px"; + } + if (scroll) + { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); } + }, + + triggerOnKeyDown: methodOp(onKeyDown), + triggerOnKeyPress: methodOp(onKeyPress), + triggerOnKeyUp: onKeyUp, + triggerOnMouseDown: methodOp(onMouseDown), + + execCommand: function(cmd) { + if (commands.hasOwnProperty(cmd)) + { return commands[cmd].call(null, this) } + }, + + triggerElectric: methodOp(function(text) { triggerElectric(this, text); }), + + findPosH: function(from, amount, unit, visually) { + var dir = 1; + if (amount < 0) { dir = -1; amount = -amount; } + var cur = clipPos(this.doc, from); + for (var i = 0; i < amount; ++i) { + cur = findPosH(this.doc, cur, dir, unit, visually); + if (cur.hitSide) { break } + } + return cur + }, + + moveH: methodOp(function(dir, unit) { + var this$1 = this; + + this.extendSelectionsBy(function (range) { + if (this$1.display.shift || this$1.doc.extend || range.empty()) + { return findPosH(this$1.doc, range.head, dir, unit, this$1.options.rtlMoveVisually) } + else + { return dir < 0 ? range.from() : range.to() } + }, sel_move); + }), + + deleteH: methodOp(function(dir, unit) { + var sel = this.doc.sel, doc = this.doc; + if (sel.somethingSelected()) + { doc.replaceSelection("", null, "+delete"); } + else + { deleteNearSelection(this, function (range) { + var other = findPosH(doc, range.head, dir, unit, false); + return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other} + }); } + }), + + findPosV: function(from, amount, unit, goalColumn) { + var dir = 1, x = goalColumn; + if (amount < 0) { dir = -1; amount = -amount; } + var cur = clipPos(this.doc, from); + for (var i = 0; i < amount; ++i) { + var coords = cursorCoords(this, cur, "div"); + if (x == null) { x = coords.left; } + else { coords.left = x; } + cur = findPosV(this, coords, dir, unit); + if (cur.hitSide) { break } + } + return cur + }, + + moveV: methodOp(function(dir, unit) { + var this$1 = this; + + var doc = this.doc, goals = []; + var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected(); + doc.extendSelectionsBy(function (range) { + if (collapse) + { return dir < 0 ? range.from() : range.to() } + var headPos = cursorCoords(this$1, range.head, "div"); + if (range.goalColumn != null) { headPos.left = range.goalColumn; } + goals.push(headPos.left); + var pos = findPosV(this$1, headPos, dir, unit); + if (unit == "page" && range == doc.sel.primary()) + { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); } + return pos + }, sel_move); + if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++) + { doc.sel.ranges[i].goalColumn = goals[i]; } } + }), + + // Find the word at the given position (as returned by coordsChar). + findWordAt: function(pos) { + var doc = this.doc, line = getLine(doc, pos.line).text; + var start = pos.ch, end = pos.ch; + if (line) { + var helper = this.getHelper(pos, "wordChars"); + if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; } + var startChar = line.charAt(start); + var check = isWordChar(startChar, helper) + ? function (ch) { return isWordChar(ch, helper); } + : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); } + : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); }; + while (start > 0 && check(line.charAt(start - 1))) { --start; } + while (end < line.length && check(line.charAt(end))) { ++end; } + } + return new Range(Pos(pos.line, start), Pos(pos.line, end)) + }, + + toggleOverwrite: function(value) { + if (value != null && value == this.state.overwrite) { return } + if (this.state.overwrite = !this.state.overwrite) + { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); } + else + { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); } + + signal(this, "overwriteToggle", this, this.state.overwrite); + }, + hasFocus: function() { return this.display.input.getField() == activeElt(doc(this)) }, + isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) }, + + scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }), + getScrollInfo: function() { + var scroller = this.display.scroller; + return {left: scroller.scrollLeft, top: scroller.scrollTop, + height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight, + width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth, + clientHeight: displayHeight(this), clientWidth: displayWidth(this)} + }, + + scrollIntoView: methodOp(function(range, margin) { + if (range == null) { + range = {from: this.doc.sel.primary().head, to: null}; + if (margin == null) { margin = this.options.cursorScrollMargin; } + } else if (typeof range == "number") { + range = {from: Pos(range, 0), to: null}; + } else if (range.from == null) { + range = {from: range, to: null}; + } + if (!range.to) { range.to = range.from; } + range.margin = margin || 0; + + if (range.from.line != null) { + scrollToRange(this, range); + } else { + scrollToCoordsRange(this, range.from, range.to, range.margin); + } + }), + + setSize: methodOp(function(width, height) { + var this$1 = this; + + var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; }; + if (width != null) { this.display.wrapper.style.width = interpret(width); } + if (height != null) { this.display.wrapper.style.height = interpret(height); } + if (this.options.lineWrapping) { clearLineMeasurementCache(this); } + var lineNo = this.display.viewFrom; + this.doc.iter(lineNo, this.display.viewTo, function (line) { + if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) + { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo, "widget"); break } } } + ++lineNo; + }); + this.curOp.forceUpdate = true; + signal(this, "refresh", this); + }), + + operation: function(f){return runInOp(this, f)}, + startOperation: function(){return startOperation(this)}, + endOperation: function(){return endOperation(this)}, + + refresh: methodOp(function() { + var oldHeight = this.display.cachedTextHeight; + regChange(this); + this.curOp.forceUpdate = true; + clearCaches(this); + scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop); + updateGutterSpace(this.display); + if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5 || this.options.lineWrapping) + { estimateLineHeights(this); } + signal(this, "refresh", this); + }), + + swapDoc: methodOp(function(doc) { + var old = this.doc; + old.cm = null; + // Cancel the current text selection if any (#5821) + if (this.state.selectingText) { this.state.selectingText(); } + attachDoc(this, doc); + clearCaches(this); + this.display.input.reset(); + scrollToCoords(this, doc.scrollLeft, doc.scrollTop); + this.curOp.forceScroll = true; + signalLater(this, "swapDoc", this, old); + return old + }), + + phrase: function(phraseText) { + var phrases = this.options.phrases; + return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText + }, + + getInputField: function(){return this.display.input.getField()}, + getWrapperElement: function(){return this.display.wrapper}, + getScrollerElement: function(){return this.display.scroller}, + getGutterElement: function(){return this.display.gutters} + }; + eventMixin(CodeMirror); + + CodeMirror.registerHelper = function(type, name, value) { + if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; } + helpers[type][name] = value; + }; + CodeMirror.registerGlobalHelper = function(type, name, predicate, value) { + CodeMirror.registerHelper(type, name, value); + helpers[type]._global.push({pred: predicate, val: value}); + }; + } + + // Used for horizontal relative motion. Dir is -1 or 1 (left or + // right), unit can be "codepoint", "char", "column" (like char, but + // doesn't cross line boundaries), "word" (across next word), or + // "group" (to the start of next group of word or + // non-word-non-whitespace chars). The visually param controls + // whether, in right-to-left text, direction 1 means to move towards + // the next index in the string, or towards the character to the right + // of the current position. The resulting position will have a + // hitSide=true property if it reached the end of the document. + function findPosH(doc, pos, dir, unit, visually) { + var oldPos = pos; + var origDir = dir; + var lineObj = getLine(doc, pos.line); + var lineDir = visually && doc.direction == "rtl" ? -dir : dir; + function findNextLine() { + var l = pos.line + lineDir; + if (l < doc.first || l >= doc.first + doc.size) { return false } + pos = new Pos(l, pos.ch, pos.sticky); + return lineObj = getLine(doc, l) + } + function moveOnce(boundToLine) { + var next; + if (unit == "codepoint") { + var ch = lineObj.text.charCodeAt(pos.ch + (dir > 0 ? 0 : -1)); + if (isNaN(ch)) { + next = null; + } else { + var astral = dir > 0 ? ch >= 0xD800 && ch < 0xDC00 : ch >= 0xDC00 && ch < 0xDFFF; + next = new Pos(pos.line, Math.max(0, Math.min(lineObj.text.length, pos.ch + dir * (astral ? 2 : 1))), -dir); + } + } else if (visually) { + next = moveVisually(doc.cm, lineObj, pos, dir); + } else { + next = moveLogically(lineObj, pos, dir); + } + if (next == null) { + if (!boundToLine && findNextLine()) + { pos = endOfLine(visually, doc.cm, lineObj, pos.line, lineDir); } + else + { return false } + } else { + pos = next; + } + return true + } + + if (unit == "char" || unit == "codepoint") { + moveOnce(); + } else if (unit == "column") { + moveOnce(true); + } else if (unit == "word" || unit == "group") { + var sawType = null, group = unit == "group"; + var helper = doc.cm && doc.cm.getHelper(pos, "wordChars"); + for (var first = true;; first = false) { + if (dir < 0 && !moveOnce(!first)) { break } + var cur = lineObj.text.charAt(pos.ch) || "\n"; + var type = isWordChar(cur, helper) ? "w" + : group && cur == "\n" ? "n" + : !group || /\s/.test(cur) ? null + : "p"; + if (group && !first && !type) { type = "s"; } + if (sawType && sawType != type) { + if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";} + break + } + + if (type) { sawType = type; } + if (dir > 0 && !moveOnce(!first)) { break } + } + } + var result = skipAtomic(doc, pos, oldPos, origDir, true); + if (equalCursorPos(oldPos, result)) { result.hitSide = true; } + return result + } + + // For relative vertical movement. Dir may be -1 or 1. Unit can be + // "page" or "line". The resulting position will have a hitSide=true + // property if it reached the end of the document. + function findPosV(cm, pos, dir, unit) { + var doc = cm.doc, x = pos.left, y; + if (unit == "page") { + var pageSize = Math.min(cm.display.wrapper.clientHeight, win(cm).innerHeight || doc(cm).documentElement.clientHeight); + var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3); + y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount; + } else if (unit == "line") { + y = dir > 0 ? pos.bottom + 3 : pos.top - 3; + } + var target; + for (;;) { + target = coordsChar(cm, x, y); + if (!target.outside) { break } + if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break } + y += dir * 5; + } + return target + } + + // CONTENTEDITABLE INPUT STYLE + + var ContentEditableInput = function(cm) { + this.cm = cm; + this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null; + this.polling = new Delayed(); + this.composing = null; + this.gracePeriod = false; + this.readDOMTimeout = null; + }; + + ContentEditableInput.prototype.init = function (display) { + var this$1 = this; + + var input = this, cm = input.cm; + var div = input.div = display.lineDiv; + div.contentEditable = true; + disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize); + + function belongsToInput(e) { + for (var t = e.target; t; t = t.parentNode) { + if (t == div) { return true } + if (/\bCodeMirror-(?:line)?widget\b/.test(t.className)) { break } + } + return false + } + + on(div, "paste", function (e) { + if (!belongsToInput(e) || signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } + // IE doesn't fire input events, so we schedule a read for the pasted content in this way + if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); } + }); + + on(div, "compositionstart", function (e) { + this$1.composing = {data: e.data, done: false}; + }); + on(div, "compositionupdate", function (e) { + if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; } + }); + on(div, "compositionend", function (e) { + if (this$1.composing) { + if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); } + this$1.composing.done = true; + } + }); + + on(div, "touchstart", function () { return input.forceCompositionEnd(); }); + + on(div, "input", function () { + if (!this$1.composing) { this$1.readFromDOMSoon(); } + }); + + function onCopyCut(e) { + if (!belongsToInput(e) || signalDOMEvent(cm, e)) { return } + if (cm.somethingSelected()) { + setLastCopied({lineWise: false, text: cm.getSelections()}); + if (e.type == "cut") { cm.replaceSelection("", null, "cut"); } + } else if (!cm.options.lineWiseCopyCut) { + return + } else { + var ranges = copyableRanges(cm); + setLastCopied({lineWise: true, text: ranges.text}); + if (e.type == "cut") { + cm.operation(function () { + cm.setSelections(ranges.ranges, 0, sel_dontScroll); + cm.replaceSelection("", null, "cut"); + }); + } + } + if (e.clipboardData) { + e.clipboardData.clearData(); + var content = lastCopied.text.join("\n"); + // iOS exposes the clipboard API, but seems to discard content inserted into it + e.clipboardData.setData("Text", content); + if (e.clipboardData.getData("Text") == content) { + e.preventDefault(); + return + } + } + // Old-fashioned briefly-focus-a-textarea hack + var kludge = hiddenTextarea(), te = kludge.firstChild; + cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild); + te.value = lastCopied.text.join("\n"); + var hadFocus = activeElt(div.ownerDocument); + selectInput(te); + setTimeout(function () { + cm.display.lineSpace.removeChild(kludge); + hadFocus.focus(); + if (hadFocus == div) { input.showPrimarySelection(); } + }, 50); + } + on(div, "copy", onCopyCut); + on(div, "cut", onCopyCut); + }; + + ContentEditableInput.prototype.screenReaderLabelChanged = function (label) { + // Label for screenreaders, accessibility + if(label) { + this.div.setAttribute('aria-label', label); + } else { + this.div.removeAttribute('aria-label'); + } + }; + + ContentEditableInput.prototype.prepareSelection = function () { + var result = prepareSelection(this.cm, false); + result.focus = activeElt(this.div.ownerDocument) == this.div; + return result + }; + + ContentEditableInput.prototype.showSelection = function (info, takeFocus) { + if (!info || !this.cm.display.view.length) { return } + if (info.focus || takeFocus) { this.showPrimarySelection(); } + this.showMultipleSelections(info); + }; + + ContentEditableInput.prototype.getSelection = function () { + return this.cm.display.wrapper.ownerDocument.getSelection() + }; + + ContentEditableInput.prototype.showPrimarySelection = function () { + var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary(); + var from = prim.from(), to = prim.to(); + + if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) { + sel.removeAllRanges(); + return + } + + var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); + var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset); + if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad && + cmp(minPos(curAnchor, curFocus), from) == 0 && + cmp(maxPos(curAnchor, curFocus), to) == 0) + { return } + + var view = cm.display.view; + var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) || + {node: view[0].measure.map[2], offset: 0}; + var end = to.line < cm.display.viewTo && posToDOM(cm, to); + if (!end) { + var measure = view[view.length - 1].measure; + var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map; + end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]}; + } + + if (!start || !end) { + sel.removeAllRanges(); + return + } + + var old = sel.rangeCount && sel.getRangeAt(0), rng; + try { rng = range(start.node, start.offset, end.offset, end.node); } + catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible + if (rng) { + if (!gecko && cm.state.focused) { + sel.collapse(start.node, start.offset); + if (!rng.collapsed) { + sel.removeAllRanges(); + sel.addRange(rng); + } + } else { + sel.removeAllRanges(); + sel.addRange(rng); + } + if (old && sel.anchorNode == null) { sel.addRange(old); } + else if (gecko) { this.startGracePeriod(); } + } + this.rememberSelection(); + }; + + ContentEditableInput.prototype.startGracePeriod = function () { + var this$1 = this; + + clearTimeout(this.gracePeriod); + this.gracePeriod = setTimeout(function () { + this$1.gracePeriod = false; + if (this$1.selectionChanged()) + { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); } + }, 20); + }; + + ContentEditableInput.prototype.showMultipleSelections = function (info) { + removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors); + removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection); + }; + + ContentEditableInput.prototype.rememberSelection = function () { + var sel = this.getSelection(); + this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset; + this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset; + }; + + ContentEditableInput.prototype.selectionInEditor = function () { + var sel = this.getSelection(); + if (!sel.rangeCount) { return false } + var node = sel.getRangeAt(0).commonAncestorContainer; + return contains(this.div, node) + }; + + ContentEditableInput.prototype.focus = function () { + if (this.cm.options.readOnly != "nocursor") { + if (!this.selectionInEditor() || activeElt(this.div.ownerDocument) != this.div) + { this.showSelection(this.prepareSelection(), true); } + this.div.focus(); + } + }; + ContentEditableInput.prototype.blur = function () { this.div.blur(); }; + ContentEditableInput.prototype.getField = function () { return this.div }; + + ContentEditableInput.prototype.supportsTouch = function () { return true }; + + ContentEditableInput.prototype.receivedFocus = function () { + var this$1 = this; + + var input = this; + if (this.selectionInEditor()) + { setTimeout(function () { return this$1.pollSelection(); }, 20); } + else + { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); } + + function poll() { + if (input.cm.state.focused) { + input.pollSelection(); + input.polling.set(input.cm.options.pollInterval, poll); + } + } + this.polling.set(this.cm.options.pollInterval, poll); + }; + + ContentEditableInput.prototype.selectionChanged = function () { + var sel = this.getSelection(); + return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset || + sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset + }; + + ContentEditableInput.prototype.pollSelection = function () { + if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return } + var sel = this.getSelection(), cm = this.cm; + // On Android Chrome (version 56, at least), backspacing into an + // uneditable block element will put the cursor in that element, + // and then, because it's not editable, hide the virtual keyboard. + // Because Android doesn't allow us to actually detect backspace + // presses in a sane way, this code checks for when that happens + // and simulates a backspace press in this case. + if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) { + this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs}); + this.blur(); + this.focus(); + return + } + if (this.composing) { return } + this.rememberSelection(); + var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); + var head = domToPos(cm, sel.focusNode, sel.focusOffset); + if (anchor && head) { runInOp(cm, function () { + setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll); + if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; } + }); } + }; + + ContentEditableInput.prototype.pollContent = function () { + if (this.readDOMTimeout != null) { + clearTimeout(this.readDOMTimeout); + this.readDOMTimeout = null; + } + + var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary(); + var from = sel.from(), to = sel.to(); + if (from.ch == 0 && from.line > cm.firstLine()) + { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); } + if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine()) + { to = Pos(to.line + 1, 0); } + if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false } + + var fromIndex, fromLine, fromNode; + if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) { + fromLine = lineNo(display.view[0].line); + fromNode = display.view[0].node; + } else { + fromLine = lineNo(display.view[fromIndex].line); + fromNode = display.view[fromIndex - 1].node.nextSibling; + } + var toIndex = findViewIndex(cm, to.line); + var toLine, toNode; + if (toIndex == display.view.length - 1) { + toLine = display.viewTo - 1; + toNode = display.lineDiv.lastChild; + } else { + toLine = lineNo(display.view[toIndex + 1].line) - 1; + toNode = display.view[toIndex + 1].node.previousSibling; + } + + if (!fromNode) { return false } + var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine)); + var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length)); + while (newText.length > 1 && oldText.length > 1) { + if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; } + else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; } + else { break } + } + + var cutFront = 0, cutEnd = 0; + var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length); + while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront)) + { ++cutFront; } + var newBot = lst(newText), oldBot = lst(oldText); + var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0), + oldBot.length - (oldText.length == 1 ? cutFront : 0)); + while (cutEnd < maxCutEnd && + newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) + { ++cutEnd; } + // Try to move start of change to start of selection if ambiguous + if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) { + while (cutFront && cutFront > from.ch && + newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) { + cutFront--; + cutEnd++; + } + } + + newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, ""); + newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, ""); + + var chFrom = Pos(fromLine, cutFront); + var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0); + if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) { + replaceRange(cm.doc, newText, chFrom, chTo, "+input"); + return true + } + }; + + ContentEditableInput.prototype.ensurePolled = function () { + this.forceCompositionEnd(); + }; + ContentEditableInput.prototype.reset = function () { + this.forceCompositionEnd(); + }; + ContentEditableInput.prototype.forceCompositionEnd = function () { + if (!this.composing) { return } + clearTimeout(this.readDOMTimeout); + this.composing = null; + this.updateFromDOM(); + this.div.blur(); + this.div.focus(); + }; + ContentEditableInput.prototype.readFromDOMSoon = function () { + var this$1 = this; + + if (this.readDOMTimeout != null) { return } + this.readDOMTimeout = setTimeout(function () { + this$1.readDOMTimeout = null; + if (this$1.composing) { + if (this$1.composing.done) { this$1.composing = null; } + else { return } + } + this$1.updateFromDOM(); + }, 80); + }; + + ContentEditableInput.prototype.updateFromDOM = function () { + var this$1 = this; + + if (this.cm.isReadOnly() || !this.pollContent()) + { runInOp(this.cm, function () { return regChange(this$1.cm); }); } + }; + + ContentEditableInput.prototype.setUneditable = function (node) { + node.contentEditable = "false"; + }; + + ContentEditableInput.prototype.onKeyPress = function (e) { + if (e.charCode == 0 || this.composing) { return } + e.preventDefault(); + if (!this.cm.isReadOnly()) + { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); } + }; + + ContentEditableInput.prototype.readOnlyChanged = function (val) { + this.div.contentEditable = String(val != "nocursor"); + }; + + ContentEditableInput.prototype.onContextMenu = function () {}; + ContentEditableInput.prototype.resetPosition = function () {}; + + ContentEditableInput.prototype.needsContentAttribute = true; + + function posToDOM(cm, pos) { + var view = findViewForLine(cm, pos.line); + if (!view || view.hidden) { return null } + var line = getLine(cm.doc, pos.line); + var info = mapFromLineView(view, line, pos.line); + + var order = getOrder(line, cm.doc.direction), side = "left"; + if (order) { + var partPos = getBidiPartAt(order, pos.ch); + side = partPos % 2 ? "right" : "left"; + } + var result = nodeAndOffsetInLineMap(info.map, pos.ch, side); + result.offset = result.collapse == "right" ? result.end : result.start; + return result + } + + function isInGutter(node) { + for (var scan = node; scan; scan = scan.parentNode) + { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } } + return false + } + + function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos } + + function domTextBetween(cm, from, to, fromLine, toLine) { + var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false; + function recognizeMarker(id) { return function (marker) { return marker.id == id; } } + function close() { + if (closing) { + text += lineSep; + if (extraLinebreak) { text += lineSep; } + closing = extraLinebreak = false; + } + } + function addText(str) { + if (str) { + close(); + text += str; + } + } + function walk(node) { + if (node.nodeType == 1) { + var cmText = node.getAttribute("cm-text"); + if (cmText) { + addText(cmText); + return + } + var markerID = node.getAttribute("cm-marker"), range; + if (markerID) { + var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID)); + if (found.length && (range = found[0].find(0))) + { addText(getBetween(cm.doc, range.from, range.to).join(lineSep)); } + return + } + if (node.getAttribute("contenteditable") == "false") { return } + var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName); + if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return } + + if (isBlock) { close(); } + for (var i = 0; i < node.childNodes.length; i++) + { walk(node.childNodes[i]); } + + if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; } + if (isBlock) { closing = true; } + } else if (node.nodeType == 3) { + addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " ")); + } + } + for (;;) { + walk(from); + if (from == to) { break } + from = from.nextSibling; + extraLinebreak = false; + } + return text + } + + function domToPos(cm, node, offset) { + var lineNode; + if (node == cm.display.lineDiv) { + lineNode = cm.display.lineDiv.childNodes[offset]; + if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) } + node = null; offset = 0; + } else { + for (lineNode = node;; lineNode = lineNode.parentNode) { + if (!lineNode || lineNode == cm.display.lineDiv) { return null } + if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break } + } + } + for (var i = 0; i < cm.display.view.length; i++) { + var lineView = cm.display.view[i]; + if (lineView.node == lineNode) + { return locateNodeInLineView(lineView, node, offset) } + } + } + + function locateNodeInLineView(lineView, node, offset) { + var wrapper = lineView.text.firstChild, bad = false; + if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) } + if (node == wrapper) { + bad = true; + node = wrapper.childNodes[offset]; + offset = 0; + if (!node) { + var line = lineView.rest ? lst(lineView.rest) : lineView.line; + return badPos(Pos(lineNo(line), line.text.length), bad) + } + } + + var textNode = node.nodeType == 3 ? node : null, topNode = node; + if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) { + textNode = node.firstChild; + if (offset) { offset = textNode.nodeValue.length; } + } + while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; } + var measure = lineView.measure, maps = measure.maps; + + function find(textNode, topNode, offset) { + for (var i = -1; i < (maps ? maps.length : 0); i++) { + var map = i < 0 ? measure.map : maps[i]; + for (var j = 0; j < map.length; j += 3) { + var curNode = map[j + 2]; + if (curNode == textNode || curNode == topNode) { + var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]); + var ch = map[j] + offset; + if (offset < 0 || curNode != textNode) { ch = map[j + (offset ? 1 : 0)]; } + return Pos(line, ch) + } + } + } + } + var found = find(textNode, topNode, offset); + if (found) { return badPos(found, bad) } + + // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems + for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) { + found = find(after, after.firstChild, 0); + if (found) + { return badPos(Pos(found.line, found.ch - dist), bad) } + else + { dist += after.textContent.length; } + } + for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) { + found = find(before, before.firstChild, -1); + if (found) + { return badPos(Pos(found.line, found.ch + dist$1), bad) } + else + { dist$1 += before.textContent.length; } + } + } + + // TEXTAREA INPUT STYLE + + var TextareaInput = function(cm) { + this.cm = cm; + // See input.poll and input.reset + this.prevInput = ""; + + // Flag that indicates whether we expect input to appear real soon + // now (after some event like 'keypress' or 'input') and are + // polling intensively. + this.pollingFast = false; + // Self-resetting timeout for the poller + this.polling = new Delayed(); + // Used to work around IE issue with selection being forgotten when focus moves away from textarea + this.hasSelection = false; + this.composing = null; + this.resetting = false; + }; + + TextareaInput.prototype.init = function (display) { + var this$1 = this; + + var input = this, cm = this.cm; + this.createField(display); + var te = this.textarea; + + display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild); + + // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore) + if (ios) { te.style.width = "0px"; } + + on(te, "input", function () { + if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; } + input.poll(); + }); + + on(te, "paste", function (e) { + if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } + + cm.state.pasteIncoming = +new Date; + input.fastPoll(); + }); + + function prepareCopyCut(e) { + if (signalDOMEvent(cm, e)) { return } + if (cm.somethingSelected()) { + setLastCopied({lineWise: false, text: cm.getSelections()}); + } else if (!cm.options.lineWiseCopyCut) { + return + } else { + var ranges = copyableRanges(cm); + setLastCopied({lineWise: true, text: ranges.text}); + if (e.type == "cut") { + cm.setSelections(ranges.ranges, null, sel_dontScroll); + } else { + input.prevInput = ""; + te.value = ranges.text.join("\n"); + selectInput(te); + } + } + if (e.type == "cut") { cm.state.cutIncoming = +new Date; } + } + on(te, "cut", prepareCopyCut); + on(te, "copy", prepareCopyCut); + + on(display.scroller, "paste", function (e) { + if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return } + if (!te.dispatchEvent) { + cm.state.pasteIncoming = +new Date; + input.focus(); + return + } + + // Pass the `paste` event to the textarea so it's handled by its event listener. + var event = new Event("paste"); + event.clipboardData = e.clipboardData; + te.dispatchEvent(event); + }); + + // Prevent normal selection in the editor (we handle our own) + on(display.lineSpace, "selectstart", function (e) { + if (!eventInWidget(display, e)) { e_preventDefault(e); } + }); + + on(te, "compositionstart", function () { + var start = cm.getCursor("from"); + if (input.composing) { input.composing.range.clear(); } + input.composing = { + start: start, + range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"}) + }; + }); + on(te, "compositionend", function () { + if (input.composing) { + input.poll(); + input.composing.range.clear(); + input.composing = null; + } + }); + }; + + TextareaInput.prototype.createField = function (_display) { + // Wraps and hides input textarea + this.wrapper = hiddenTextarea(); + // The semihidden textarea that is focused when the editor is + // focused, and receives input. + this.textarea = this.wrapper.firstChild; + }; + + TextareaInput.prototype.screenReaderLabelChanged = function (label) { + // Label for screenreaders, accessibility + if(label) { + this.textarea.setAttribute('aria-label', label); + } else { + this.textarea.removeAttribute('aria-label'); + } + }; + + TextareaInput.prototype.prepareSelection = function () { + // Redraw the selection and/or cursor + var cm = this.cm, display = cm.display, doc = cm.doc; + var result = prepareSelection(cm); + + // Move the hidden textarea near the cursor to prevent scrolling artifacts + if (cm.options.moveInputWithCursor) { + var headPos = cursorCoords(cm, doc.sel.primary().head, "div"); + var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect(); + result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10, + headPos.top + lineOff.top - wrapOff.top)); + result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10, + headPos.left + lineOff.left - wrapOff.left)); + } + + return result + }; + + TextareaInput.prototype.showSelection = function (drawn) { + var cm = this.cm, display = cm.display; + removeChildrenAndAdd(display.cursorDiv, drawn.cursors); + removeChildrenAndAdd(display.selectionDiv, drawn.selection); + if (drawn.teTop != null) { + this.wrapper.style.top = drawn.teTop + "px"; + this.wrapper.style.left = drawn.teLeft + "px"; + } + }; + + // Reset the input to correspond to the selection (or to be empty, + // when not typing and nothing is selected) + TextareaInput.prototype.reset = function (typing) { + if (this.contextMenuPending || this.composing && typing) { return } + var cm = this.cm; + this.resetting = true; + if (cm.somethingSelected()) { + this.prevInput = ""; + var content = cm.getSelection(); + this.textarea.value = content; + if (cm.state.focused) { selectInput(this.textarea); } + if (ie && ie_version >= 9) { this.hasSelection = content; } + } else if (!typing) { + this.prevInput = this.textarea.value = ""; + if (ie && ie_version >= 9) { this.hasSelection = null; } + } + this.resetting = false; + }; + + TextareaInput.prototype.getField = function () { return this.textarea }; + + TextareaInput.prototype.supportsTouch = function () { return false }; + + TextareaInput.prototype.focus = function () { + if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt(this.textarea.ownerDocument) != this.textarea)) { + try { this.textarea.focus(); } + catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM + } + }; + + TextareaInput.prototype.blur = function () { this.textarea.blur(); }; + + TextareaInput.prototype.resetPosition = function () { + this.wrapper.style.top = this.wrapper.style.left = 0; + }; + + TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); }; + + // Poll for input changes, using the normal rate of polling. This + // runs as long as the editor is focused. + TextareaInput.prototype.slowPoll = function () { + var this$1 = this; + + if (this.pollingFast) { return } + this.polling.set(this.cm.options.pollInterval, function () { + this$1.poll(); + if (this$1.cm.state.focused) { this$1.slowPoll(); } + }); + }; + + // When an event has just come in that is likely to add or change + // something in the input textarea, we poll faster, to ensure that + // the change appears on the screen quickly. + TextareaInput.prototype.fastPoll = function () { + var missed = false, input = this; + input.pollingFast = true; + function p() { + var changed = input.poll(); + if (!changed && !missed) {missed = true; input.polling.set(60, p);} + else {input.pollingFast = false; input.slowPoll();} + } + input.polling.set(20, p); + }; + + // Read input from the textarea, and update the document to match. + // When something is selected, it is present in the textarea, and + // selected (unless it is huge, in which case a placeholder is + // used). When nothing is selected, the cursor sits after previously + // seen text (can be empty), which is stored in prevInput (we must + // not reset the textarea when typing, because that breaks IME). + TextareaInput.prototype.poll = function () { + var this$1 = this; + + var cm = this.cm, input = this.textarea, prevInput = this.prevInput; + // Since this is called a *lot*, try to bail out as cheaply as + // possible when it is clear that nothing happened. hasSelection + // will be the case when there is a lot of text in the textarea, + // in which case reading its value would be expensive. + if (this.contextMenuPending || this.resetting || !cm.state.focused || + (hasSelection(input) && !prevInput && !this.composing) || + cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq) + { return false } + + var text = input.value; + // If nothing changed, bail. + if (text == prevInput && !cm.somethingSelected()) { return false } + // Work around nonsensical selection resetting in IE9/10, and + // inexplicable appearance of private area unicode characters on + // some key combos in Mac (#2689). + if (ie && ie_version >= 9 && this.hasSelection === text || + mac && /[\uf700-\uf7ff]/.test(text)) { + cm.display.input.reset(); + return false + } + + if (cm.doc.sel == cm.display.selForContextMenu) { + var first = text.charCodeAt(0); + if (first == 0x200b && !prevInput) { prevInput = "\u200b"; } + if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") } + } + // Find the part of the input that is actually new + var same = 0, l = Math.min(prevInput.length, text.length); + while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; } + + runInOp(cm, function () { + applyTextInput(cm, text.slice(same), prevInput.length - same, + null, this$1.composing ? "*compose" : null); + + // Don't leave long text in the textarea, since it makes further polling slow + if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; } + else { this$1.prevInput = text; } + + if (this$1.composing) { + this$1.composing.range.clear(); + this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"), + {className: "CodeMirror-composing"}); + } + }); + return true + }; + + TextareaInput.prototype.ensurePolled = function () { + if (this.pollingFast && this.poll()) { this.pollingFast = false; } + }; + + TextareaInput.prototype.onKeyPress = function () { + if (ie && ie_version >= 9) { this.hasSelection = null; } + this.fastPoll(); + }; + + TextareaInput.prototype.onContextMenu = function (e) { + var input = this, cm = input.cm, display = cm.display, te = input.textarea; + if (input.contextMenuPending) { input.contextMenuPending(); } + var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop; + if (!pos || presto) { return } // Opera is difficult. + + // Reset the current text selection only if the click is done outside of the selection + // and 'resetSelectionOnContextMenu' option is true. + var reset = cm.options.resetSelectionOnContextMenu; + if (reset && cm.doc.sel.contains(pos) == -1) + { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); } + + var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText; + var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect(); + input.wrapper.style.cssText = "position: static"; + te.style.cssText = "position: absolute; width: 30px; height: 30px;\n top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; + var oldScrollY; + if (webkit) { oldScrollY = te.ownerDocument.defaultView.scrollY; } // Work around Chrome issue (#2712) + display.input.focus(); + if (webkit) { te.ownerDocument.defaultView.scrollTo(null, oldScrollY); } + display.input.reset(); + // Adds "Select all" to context menu in FF + if (!cm.somethingSelected()) { te.value = input.prevInput = " "; } + input.contextMenuPending = rehide; + display.selForContextMenu = cm.doc.sel; + clearTimeout(display.detectingSelectAll); + + // Select-all will be greyed out if there's nothing to select, so + // this adds a zero-width space so that we can later check whether + // it got selected. + function prepareSelectAllHack() { + if (te.selectionStart != null) { + var selected = cm.somethingSelected(); + var extval = "\u200b" + (selected ? te.value : ""); + te.value = "\u21da"; // Used to catch context-menu undo + te.value = extval; + input.prevInput = selected ? "" : "\u200b"; + te.selectionStart = 1; te.selectionEnd = extval.length; + // Re-set this, in case some other handler touched the + // selection in the meantime. + display.selForContextMenu = cm.doc.sel; + } + } + function rehide() { + if (input.contextMenuPending != rehide) { return } + input.contextMenuPending = false; + input.wrapper.style.cssText = oldWrapperCSS; + te.style.cssText = oldCSS; + if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); } + + // Try to detect the user choosing select-all + if (te.selectionStart != null) { + if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); } + var i = 0, poll = function () { + if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 && + te.selectionEnd > 0 && input.prevInput == "\u200b") { + operation(cm, selectAll)(cm); + } else if (i++ < 10) { + display.detectingSelectAll = setTimeout(poll, 500); + } else { + display.selForContextMenu = null; + display.input.reset(); + } + }; + display.detectingSelectAll = setTimeout(poll, 200); + } + } + + if (ie && ie_version >= 9) { prepareSelectAllHack(); } + if (captureRightClick) { + e_stop(e); + var mouseup = function () { + off(window, "mouseup", mouseup); + setTimeout(rehide, 20); + }; + on(window, "mouseup", mouseup); + } else { + setTimeout(rehide, 50); + } + }; + + TextareaInput.prototype.readOnlyChanged = function (val) { + if (!val) { this.reset(); } + this.textarea.disabled = val == "nocursor"; + this.textarea.readOnly = !!val; + }; + + TextareaInput.prototype.setUneditable = function () {}; + + TextareaInput.prototype.needsContentAttribute = false; + + function fromTextArea(textarea, options) { + options = options ? copyObj(options) : {}; + options.value = textarea.value; + if (!options.tabindex && textarea.tabIndex) + { options.tabindex = textarea.tabIndex; } + if (!options.placeholder && textarea.placeholder) + { options.placeholder = textarea.placeholder; } + // Set autofocus to true if this textarea is focused, or if it has + // autofocus and no other element is focused. + if (options.autofocus == null) { + var hasFocus = activeElt(textarea.ownerDocument); + options.autofocus = hasFocus == textarea || + textarea.getAttribute("autofocus") != null && hasFocus == document.body; + } + + function save() {textarea.value = cm.getValue();} + + var realSubmit; + if (textarea.form) { + on(textarea.form, "submit", save); + // Deplorable hack to make the submit method do the right thing. + if (!options.leaveSubmitMethodAlone) { + var form = textarea.form; + realSubmit = form.submit; + try { + var wrappedSubmit = form.submit = function () { + save(); + form.submit = realSubmit; + form.submit(); + form.submit = wrappedSubmit; + }; + } catch(e) {} + } + } + + options.finishInit = function (cm) { + cm.save = save; + cm.getTextArea = function () { return textarea; }; + cm.toTextArea = function () { + cm.toTextArea = isNaN; // Prevent this from being ran twice + save(); + textarea.parentNode.removeChild(cm.getWrapperElement()); + textarea.style.display = ""; + if (textarea.form) { + off(textarea.form, "submit", save); + if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == "function") + { textarea.form.submit = realSubmit; } + } + }; + }; + + textarea.style.display = "none"; + var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); }, + options); + return cm + } + + function addLegacyProps(CodeMirror) { + CodeMirror.off = off; + CodeMirror.on = on; + CodeMirror.wheelEventPixels = wheelEventPixels; + CodeMirror.Doc = Doc; + CodeMirror.splitLines = splitLinesAuto; + CodeMirror.countColumn = countColumn; + CodeMirror.findColumn = findColumn; + CodeMirror.isWordChar = isWordCharBasic; + CodeMirror.Pass = Pass; + CodeMirror.signal = signal; + CodeMirror.Line = Line; + CodeMirror.changeEnd = changeEnd; + CodeMirror.scrollbarModel = scrollbarModel; + CodeMirror.Pos = Pos; + CodeMirror.cmpPos = cmp; + CodeMirror.modes = modes; + CodeMirror.mimeModes = mimeModes; + CodeMirror.resolveMode = resolveMode; + CodeMirror.getMode = getMode; + CodeMirror.modeExtensions = modeExtensions; + CodeMirror.extendMode = extendMode; + CodeMirror.copyState = copyState; + CodeMirror.startState = startState; + CodeMirror.innerMode = innerMode; + CodeMirror.commands = commands; + CodeMirror.keyMap = keyMap; + CodeMirror.keyName = keyName; + CodeMirror.isModifierKey = isModifierKey; + CodeMirror.lookupKey = lookupKey; + CodeMirror.normalizeKeyMap = normalizeKeyMap; + CodeMirror.StringStream = StringStream; + CodeMirror.SharedTextMarker = SharedTextMarker; + CodeMirror.TextMarker = TextMarker; + CodeMirror.LineWidget = LineWidget; + CodeMirror.e_preventDefault = e_preventDefault; + CodeMirror.e_stopPropagation = e_stopPropagation; + CodeMirror.e_stop = e_stop; + CodeMirror.addClass = addClass; + CodeMirror.contains = contains; + CodeMirror.rmClass = rmClass; + CodeMirror.keyNames = keyNames; + } + + // EDITOR CONSTRUCTOR + + defineOptions(CodeMirror); + + addEditorMethods(CodeMirror); + + // Set up methods on CodeMirror's prototype to redirect to the editor's document. + var dontDelegate = "iter insert remove copy getEditor constructor".split(" "); + for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0) + { CodeMirror.prototype[prop] = (function(method) { + return function() {return method.apply(this.doc, arguments)} + })(Doc.prototype[prop]); } } + + eventMixin(Doc); + CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}; + + // Extra arguments are stored as the mode's dependencies, which is + // used by (legacy) mechanisms like loadmode.js to automatically + // load a mode. (Preferred mechanism is the require/define calls.) + CodeMirror.defineMode = function(name/*, mode, …*/) { + if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name; } + defineMode.apply(this, arguments); + }; + + CodeMirror.defineMIME = defineMIME; + + // Minimal default mode. + CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); }); + CodeMirror.defineMIME("text/plain", "null"); + + // EXTENSIONS + + CodeMirror.defineExtension = function (name, func) { + CodeMirror.prototype[name] = func; + }; + CodeMirror.defineDocExtension = function (name, func) { + Doc.prototype[name] = func; + }; + + CodeMirror.fromTextArea = fromTextArea; + + addLegacyProps(CodeMirror); + + CodeMirror.version = "5.65.10"; + + return CodeMirror; +}))); + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/addon/edit/matchbrackets.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + var ie_lt8 = /MSIE \d/.test(navigator.userAgent) && + (document.documentMode == null || document.documentMode < 8); + + var Pos = CodeMirror.Pos; + + var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<", "<": ">>", ">": "<<"}; + + function bracketRegex(config) { + return config && config.bracketRegex || /[(){}[\]]/ + } + + function findMatchingBracket(cm, where, config) { + var line = cm.getLineHandle(where.line), pos = where.ch - 1; + var afterCursor = config && config.afterCursor + if (afterCursor == null) + afterCursor = /(^| )cm-fat-cursor($| )/.test(cm.getWrapperElement().className) + var re = bracketRegex(config) + + // A cursor is defined as between two characters, but in in vim command mode + // (i.e. not insert mode), the cursor is visually represented as a + // highlighted box on top of the 2nd character. Otherwise, we allow matches + // from before or after the cursor. + var match = (!afterCursor && pos >= 0 && re.test(line.text.charAt(pos)) && matching[line.text.charAt(pos)]) || + re.test(line.text.charAt(pos + 1)) && matching[line.text.charAt(++pos)]; + if (!match) return null; + var dir = match.charAt(1) == ">" ? 1 : -1; + if (config && config.strict && (dir > 0) != (pos == where.ch)) return null; + var style = cm.getTokenTypeAt(Pos(where.line, pos + 1)); + + var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style, config); + if (found == null) return null; + return {from: Pos(where.line, pos), to: found && found.pos, + match: found && found.ch == match.charAt(0), forward: dir > 0}; + } + + // bracketRegex is used to specify which type of bracket to scan + // should be a regexp, e.g. /[[\]]/ + // + // Note: If "where" is on an open bracket, then this bracket is ignored. + // + // Returns false when no bracket was found, null when it reached + // maxScanLines and gave up + function scanForBracket(cm, where, dir, style, config) { + var maxScanLen = (config && config.maxScanLineLength) || 10000; + var maxScanLines = (config && config.maxScanLines) || 1000; + + var stack = []; + var re = bracketRegex(config) + var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1) + : Math.max(cm.firstLine() - 1, where.line - maxScanLines); + for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) { + var line = cm.getLine(lineNo); + if (!line) continue; + var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1; + if (line.length > maxScanLen) continue; + if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0); + for (; pos != end; pos += dir) { + var ch = line.charAt(pos); + if (re.test(ch) && (style === undefined || + (cm.getTokenTypeAt(Pos(lineNo, pos + 1)) || "") == (style || ""))) { + var match = matching[ch]; + if (match && (match.charAt(1) == ">") == (dir > 0)) stack.push(ch); + else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch}; + else stack.pop(); + } + } + } + return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null; + } + + function matchBrackets(cm, autoclear, config) { + // Disable brace matching in long lines, since it'll cause hugely slow updates + var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000, + highlightNonMatching = config && config.highlightNonMatching; + var marks = [], ranges = cm.listSelections(); + for (var i = 0; i < ranges.length; i++) { + var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, config); + if (match && (match.match || highlightNonMatching !== false) && cm.getLine(match.from.line).length <= maxHighlightLen) { + var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; + marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style})); + if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen) + marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style})); + } + } + + if (marks.length) { + // Kludge to work around the IE bug from issue #1193, where text + // input stops going to the textarea whenever this fires. + if (ie_lt8 && cm.state.focused) cm.focus(); + + var clear = function() { + cm.operation(function() { + for (var i = 0; i < marks.length; i++) marks[i].clear(); + }); + }; + if (autoclear) setTimeout(clear, 800); + else return clear; + } + } + + function doMatchBrackets(cm) { + cm.operation(function() { + if (cm.state.matchBrackets.currentlyHighlighted) { + cm.state.matchBrackets.currentlyHighlighted(); + cm.state.matchBrackets.currentlyHighlighted = null; + } + cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets); + }); + } + + function clearHighlighted(cm) { + if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) { + cm.state.matchBrackets.currentlyHighlighted(); + cm.state.matchBrackets.currentlyHighlighted = null; + } + } + + CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) { + cm.off("cursorActivity", doMatchBrackets); + cm.off("focus", doMatchBrackets) + cm.off("blur", clearHighlighted) + clearHighlighted(cm); + } + if (val) { + cm.state.matchBrackets = typeof val == "object" ? val : {}; + cm.on("cursorActivity", doMatchBrackets); + cm.on("focus", doMatchBrackets) + cm.on("blur", clearHighlighted) + } + }); + + CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);}); + CodeMirror.defineExtension("findMatchingBracket", function(pos, config, oldConfig){ + // Backwards-compatibility kludge + if (oldConfig || typeof config == "boolean") { + if (!oldConfig) { + config = config ? {strict: true} : null + } else { + oldConfig.strict = config + config = oldConfig + } + } + return findMatchingBracket(this, pos, config) + }); + CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){ + return scanForBracket(this, pos, dir, style, config); + }); +}); + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/addon/edit/trailingspace.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) { + if (prev == CodeMirror.Init) prev = false; + if (prev && !val) + cm.removeOverlay("trailingspace"); + else if (!prev && val) + cm.addOverlay({ + token: function(stream) { + for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {} + if (i > stream.pos) { stream.pos = i; return null; } + stream.pos = l; + return "trailingspace"; + }, + name: "trailingspace" + }); + }); +}); + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/addon/lint/lint.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + var GUTTER_ID = "CodeMirror-lint-markers"; + var LINT_LINE_ID = "CodeMirror-lint-line-"; + + function showTooltip(cm, e, content) { + var tt = document.createElement("div"); + tt.className = "CodeMirror-lint-tooltip cm-s-" + cm.options.theme; + tt.appendChild(content.cloneNode(true)); + if (cm.state.lint.options.selfContain) + cm.getWrapperElement().appendChild(tt); + else + document.body.appendChild(tt); + + function position(e) { + if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position); + tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + "px"; + tt.style.left = (e.clientX + 5) + "px"; + } + CodeMirror.on(document, "mousemove", position); + position(e); + if (tt.style.opacity != null) tt.style.opacity = 1; + return tt; + } + function rm(elt) { + if (elt.parentNode) elt.parentNode.removeChild(elt); + } + function hideTooltip(tt) { + if (!tt.parentNode) return; + if (tt.style.opacity == null) rm(tt); + tt.style.opacity = 0; + setTimeout(function() { rm(tt); }, 600); + } + + function showTooltipFor(cm, e, content, node) { + var tooltip = showTooltip(cm, e, content); + function hide() { + CodeMirror.off(node, "mouseout", hide); + if (tooltip) { hideTooltip(tooltip); tooltip = null; } + } + var poll = setInterval(function() { + if (tooltip) for (var n = node;; n = n.parentNode) { + if (n && n.nodeType == 11) n = n.host; + if (n == document.body) return; + if (!n) { hide(); break; } + } + if (!tooltip) return clearInterval(poll); + }, 400); + CodeMirror.on(node, "mouseout", hide); + } + + function LintState(cm, conf, hasGutter) { + this.marked = []; + if (conf instanceof Function) conf = {getAnnotations: conf}; + if (!conf || conf === true) conf = {}; + this.options = {}; + this.linterOptions = conf.options || {}; + for (var prop in defaults) this.options[prop] = defaults[prop]; + for (var prop in conf) { + if (defaults.hasOwnProperty(prop)) { + if (conf[prop] != null) this.options[prop] = conf[prop]; + } else if (!conf.options) { + this.linterOptions[prop] = conf[prop]; + } + } + this.timeout = null; + this.hasGutter = hasGutter; + this.onMouseOver = function(e) { onMouseOver(cm, e); }; + this.waitingFor = 0 + } + + var defaults = { + highlightLines: false, + tooltips: true, + delay: 500, + lintOnChange: true, + getAnnotations: null, + async: false, + selfContain: null, + formatAnnotation: null, + onUpdateLinting: null + } + + function clearMarks(cm) { + var state = cm.state.lint; + if (state.hasGutter) cm.clearGutter(GUTTER_ID); + if (state.options.highlightLines) clearErrorLines(cm); + for (var i = 0; i < state.marked.length; ++i) + state.marked[i].clear(); + state.marked.length = 0; + } + + function clearErrorLines(cm) { + cm.eachLine(function(line) { + var has = line.wrapClass && /\bCodeMirror-lint-line-\w+\b/.exec(line.wrapClass); + if (has) cm.removeLineClass(line, "wrap", has[0]); + }) + } + + function makeMarker(cm, labels, severity, multiple, tooltips) { + var marker = document.createElement("div"), inner = marker; + marker.className = "CodeMirror-lint-marker CodeMirror-lint-marker-" + severity; + if (multiple) { + inner = marker.appendChild(document.createElement("div")); + inner.className = "CodeMirror-lint-marker CodeMirror-lint-marker-multiple"; + } + + if (tooltips != false) CodeMirror.on(inner, "mouseover", function(e) { + showTooltipFor(cm, e, labels, inner); + }); + + return marker; + } + + function getMaxSeverity(a, b) { + if (a == "error") return a; + else return b; + } + + function groupByLine(annotations) { + var lines = []; + for (var i = 0; i < annotations.length; ++i) { + var ann = annotations[i], line = ann.from.line; + (lines[line] || (lines[line] = [])).push(ann); + } + return lines; + } + + function annotationTooltip(ann) { + var severity = ann.severity; + if (!severity) severity = "error"; + var tip = document.createElement("div"); + tip.className = "CodeMirror-lint-message CodeMirror-lint-message-" + severity; + if (typeof ann.messageHTML != 'undefined') { + tip.innerHTML = ann.messageHTML; + } else { + tip.appendChild(document.createTextNode(ann.message)); + } + return tip; + } + + function lintAsync(cm, getAnnotations) { + var state = cm.state.lint + var id = ++state.waitingFor + function abort() { + id = -1 + cm.off("change", abort) + } + cm.on("change", abort) + getAnnotations(cm.getValue(), function(annotations, arg2) { + cm.off("change", abort) + if (state.waitingFor != id) return + if (arg2 && annotations instanceof CodeMirror) annotations = arg2 + cm.operation(function() {updateLinting(cm, annotations)}) + }, state.linterOptions, cm); + } + + function startLinting(cm) { + var state = cm.state.lint; + if (!state) return; + var options = state.options; + /* + * Passing rules in `options` property prevents JSHint (and other linters) from complaining + * about unrecognized rules like `onUpdateLinting`, `delay`, `lintOnChange`, etc. + */ + var getAnnotations = options.getAnnotations || cm.getHelper(CodeMirror.Pos(0, 0), "lint"); + if (!getAnnotations) return; + if (options.async || getAnnotations.async) { + lintAsync(cm, getAnnotations) + } else { + var annotations = getAnnotations(cm.getValue(), state.linterOptions, cm); + if (!annotations) return; + if (annotations.then) annotations.then(function(issues) { + cm.operation(function() {updateLinting(cm, issues)}) + }); + else cm.operation(function() {updateLinting(cm, annotations)}) + } + } + + function updateLinting(cm, annotationsNotSorted) { + var state = cm.state.lint; + if (!state) return; + var options = state.options; + clearMarks(cm); + + var annotations = groupByLine(annotationsNotSorted); + + for (var line = 0; line < annotations.length; ++line) { + var anns = annotations[line]; + if (!anns) continue; + + // filter out duplicate messages + var message = []; + anns = anns.filter(function(item) { return message.indexOf(item.message) > -1 ? false : message.push(item.message) }); + + var maxSeverity = null; + var tipLabel = state.hasGutter && document.createDocumentFragment(); + + for (var i = 0; i < anns.length; ++i) { + var ann = anns[i]; + var severity = ann.severity; + if (!severity) severity = "error"; + maxSeverity = getMaxSeverity(maxSeverity, severity); + + if (options.formatAnnotation) ann = options.formatAnnotation(ann); + if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann)); + + if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, { + className: "CodeMirror-lint-mark CodeMirror-lint-mark-" + severity, + __annotation: ann + })); + } + // use original annotations[line] to show multiple messages + if (state.hasGutter) + cm.setGutterMarker(line, GUTTER_ID, makeMarker(cm, tipLabel, maxSeverity, annotations[line].length > 1, + options.tooltips)); + + if (options.highlightLines) + cm.addLineClass(line, "wrap", LINT_LINE_ID + maxSeverity); + } + if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm); + } + + function onChange(cm) { + var state = cm.state.lint; + if (!state) return; + clearTimeout(state.timeout); + state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay); + } + + function popupTooltips(cm, annotations, e) { + var target = e.target || e.srcElement; + var tooltip = document.createDocumentFragment(); + for (var i = 0; i < annotations.length; i++) { + var ann = annotations[i]; + tooltip.appendChild(annotationTooltip(ann)); + } + showTooltipFor(cm, e, tooltip, target); + } + + function onMouseOver(cm, e) { + var target = e.target || e.srcElement; + if (!/\bCodeMirror-lint-mark-/.test(target.className)) return; + var box = target.getBoundingClientRect(), x = (box.left + box.right) / 2, y = (box.top + box.bottom) / 2; + var spans = cm.findMarksAt(cm.coordsChar({left: x, top: y}, "client")); + + var annotations = []; + for (var i = 0; i < spans.length; ++i) { + var ann = spans[i].__annotation; + if (ann) annotations.push(ann); + } + if (annotations.length) popupTooltips(cm, annotations, e); + } + + CodeMirror.defineOption("lint", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) { + clearMarks(cm); + if (cm.state.lint.options.lintOnChange !== false) + cm.off("change", onChange); + CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver); + clearTimeout(cm.state.lint.timeout); + delete cm.state.lint; + } + + if (val) { + var gutters = cm.getOption("gutters"), hasLintGutter = false; + for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true; + var state = cm.state.lint = new LintState(cm, val, hasLintGutter); + if (state.options.lintOnChange) + cm.on("change", onChange); + if (state.options.tooltips != false && state.options.tooltips != "gutter") + CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver); + + startLinting(cm); + } + }); + + CodeMirror.defineExtension("performLint", function() { + startLinting(this); + }); +}); + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/addon/selection/active-line.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + var WRAP_CLASS = "CodeMirror-activeline"; + var BACK_CLASS = "CodeMirror-activeline-background"; + var GUTT_CLASS = "CodeMirror-activeline-gutter"; + + CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) { + var prev = old == CodeMirror.Init ? false : old; + if (val == prev) return + if (prev) { + cm.off("beforeSelectionChange", selectionChange); + clearActiveLines(cm); + delete cm.state.activeLines; + } + if (val) { + cm.state.activeLines = []; + updateActiveLines(cm, cm.listSelections()); + cm.on("beforeSelectionChange", selectionChange); + } + }); + + function clearActiveLines(cm) { + for (var i = 0; i < cm.state.activeLines.length; i++) { + cm.removeLineClass(cm.state.activeLines[i], "wrap", WRAP_CLASS); + cm.removeLineClass(cm.state.activeLines[i], "background", BACK_CLASS); + cm.removeLineClass(cm.state.activeLines[i], "gutter", GUTT_CLASS); + } + } + + function sameArray(a, b) { + if (a.length != b.length) return false; + for (var i = 0; i < a.length; i++) + if (a[i] != b[i]) return false; + return true; + } + + function updateActiveLines(cm, ranges) { + var active = []; + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i]; + var option = cm.getOption("styleActiveLine"); + if (typeof option == "object" && option.nonEmpty ? range.anchor.line != range.head.line : !range.empty()) + continue + var line = cm.getLineHandleVisualStart(range.head.line); + if (active[active.length - 1] != line) active.push(line); + } + if (sameArray(cm.state.activeLines, active)) return; + cm.operation(function() { + clearActiveLines(cm); + for (var i = 0; i < active.length; i++) { + cm.addLineClass(active[i], "wrap", WRAP_CLASS); + cm.addLineClass(active[i], "background", BACK_CLASS); + cm.addLineClass(active[i], "gutter", GUTT_CLASS); + } + cm.state.activeLines = active; + }); + } + + function selectionChange(cm, sel) { + updateActiveLines(cm, sel.ranges); + } +}); + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/mode/javascript/javascript.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("javascript", function(config, parserConfig) { + var indentUnit = config.indentUnit; + var statementIndent = parserConfig.statementIndent; + var jsonldMode = parserConfig.jsonld; + var jsonMode = parserConfig.json || jsonldMode; + var trackScope = parserConfig.trackScope !== false + var isTS = parserConfig.typescript; + var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/; + + // Tokenizer + + var keywords = function(){ + function kw(type) {return {type: type, style: "keyword"};} + var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d"); + var operator = kw("operator"), atom = {type: "atom", style: "atom"}; + + return { + "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, + "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C, + "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"), + "function": kw("function"), "catch": kw("catch"), + "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"), + "in": operator, "typeof": operator, "instanceof": operator, + "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom, + "this": kw("this"), "class": kw("class"), "super": kw("atom"), + "yield": C, "export": kw("export"), "import": kw("import"), "extends": C, + "await": C + }; + }(); + + var isOperatorChar = /[+\-*&%=<>!?|~^@]/; + var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/; + + function readRegexp(stream) { + var escaped = false, next, inSet = false; + while ((next = stream.next()) != null) { + if (!escaped) { + if (next == "/" && !inSet) return; + if (next == "[") inSet = true; + else if (inSet && next == "]") inSet = false; + } + escaped = !escaped && next == "\\"; + } + } + + // Used as scratch variables to communicate multiple values without + // consing up tons of objects. + var type, content; + function ret(tp, style, cont) { + type = tp; content = cont; + return style; + } + function tokenBase(stream, state) { + var ch = stream.next(); + if (ch == '"' || ch == "'") { + state.tokenize = tokenString(ch); + return state.tokenize(stream, state); + } else if (ch == "." && stream.match(/^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/)) { + return ret("number", "number"); + } else if (ch == "." && stream.match("..")) { + return ret("spread", "meta"); + } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) { + return ret(ch); + } else if (ch == "=" && stream.eat(">")) { + return ret("=>", "operator"); + } else if (ch == "0" && stream.match(/^(?:x[\dA-Fa-f_]+|o[0-7_]+|b[01_]+)n?/)) { + return ret("number", "number"); + } else if (/\d/.test(ch)) { + stream.match(/^[\d_]*(?:n|(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)?/); + return ret("number", "number"); + } else if (ch == "/") { + if (stream.eat("*")) { + state.tokenize = tokenComment; + return tokenComment(stream, state); + } else if (stream.eat("/")) { + stream.skipToEnd(); + return ret("comment", "comment"); + } else if (expressionAllowed(stream, state, 1)) { + readRegexp(stream); + stream.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/); + return ret("regexp", "string-2"); + } else { + stream.eat("="); + return ret("operator", "operator", stream.current()); + } + } else if (ch == "`") { + state.tokenize = tokenQuasi; + return tokenQuasi(stream, state); + } else if (ch == "#" && stream.peek() == "!") { + stream.skipToEnd(); + return ret("meta", "meta"); + } else if (ch == "#" && stream.eatWhile(wordRE)) { + return ret("variable", "property") + } else if (ch == "<" && stream.match("!--") || + (ch == "-" && stream.match("->") && !/\S/.test(stream.string.slice(0, stream.start)))) { + stream.skipToEnd() + return ret("comment", "comment") + } else if (isOperatorChar.test(ch)) { + if (ch != ">" || !state.lexical || state.lexical.type != ">") { + if (stream.eat("=")) { + if (ch == "!" || ch == "=") stream.eat("=") + } else if (/[<>*+\-|&?]/.test(ch)) { + stream.eat(ch) + if (ch == ">") stream.eat(ch) + } + } + if (ch == "?" && stream.eat(".")) return ret(".") + return ret("operator", "operator", stream.current()); + } else if (wordRE.test(ch)) { + stream.eatWhile(wordRE); + var word = stream.current() + if (state.lastType != ".") { + if (keywords.propertyIsEnumerable(word)) { + var kw = keywords[word] + return ret(kw.type, kw.style, word) + } + if (word == "async" && stream.match(/^(\s|\/\*([^*]|\*(?!\/))*?\*\/)*[\[\(\w]/, false)) + return ret("async", "keyword", word) + } + return ret("variable", "variable", word) + } + } + + function tokenString(quote) { + return function(stream, state) { + var escaped = false, next; + if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){ + state.tokenize = tokenBase; + return ret("jsonld-keyword", "meta"); + } + while ((next = stream.next()) != null) { + if (next == quote && !escaped) break; + escaped = !escaped && next == "\\"; + } + if (!escaped) state.tokenize = tokenBase; + return ret("string", "string"); + }; + } + + function tokenComment(stream, state) { + var maybeEnd = false, ch; + while (ch = stream.next()) { + if (ch == "/" && maybeEnd) { + state.tokenize = tokenBase; + break; + } + maybeEnd = (ch == "*"); + } + return ret("comment", "comment"); + } + + function tokenQuasi(stream, state) { + var escaped = false, next; + while ((next = stream.next()) != null) { + if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) { + state.tokenize = tokenBase; + break; + } + escaped = !escaped && next == "\\"; + } + return ret("quasi", "string-2", stream.current()); + } + + var brackets = "([{}])"; + // This is a crude lookahead trick to try and notice that we're + // parsing the argument patterns for a fat-arrow function before we + // actually hit the arrow token. It only works if the arrow is on + // the same line as the arguments and there's no strange noise + // (comments) in between. Fallback is to only notice when we hit the + // arrow, and not declare the arguments as locals for the arrow + // body. + function findFatArrow(stream, state) { + if (state.fatArrowAt) state.fatArrowAt = null; + var arrow = stream.string.indexOf("=>", stream.start); + if (arrow < 0) return; + + if (isTS) { // Try to skip TypeScript return type declarations after the arguments + var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow)) + if (m) arrow = m.index + } + + var depth = 0, sawSomething = false; + for (var pos = arrow - 1; pos >= 0; --pos) { + var ch = stream.string.charAt(pos); + var bracket = brackets.indexOf(ch); + if (bracket >= 0 && bracket < 3) { + if (!depth) { ++pos; break; } + if (--depth == 0) { if (ch == "(") sawSomething = true; break; } + } else if (bracket >= 3 && bracket < 6) { + ++depth; + } else if (wordRE.test(ch)) { + sawSomething = true; + } else if (/["'\/`]/.test(ch)) { + for (;; --pos) { + if (pos == 0) return + var next = stream.string.charAt(pos - 1) + if (next == ch && stream.string.charAt(pos - 2) != "\\") { pos--; break } + } + } else if (sawSomething && !depth) { + ++pos; + break; + } + } + if (sawSomething && !depth) state.fatArrowAt = pos; + } + + // Parser + + var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, + "regexp": true, "this": true, "import": true, "jsonld-keyword": true}; + + function JSLexical(indented, column, type, align, prev, info) { + this.indented = indented; + this.column = column; + this.type = type; + this.prev = prev; + this.info = info; + if (align != null) this.align = align; + } + + function inScope(state, varname) { + if (!trackScope) return false + for (var v = state.localVars; v; v = v.next) + if (v.name == varname) return true; + for (var cx = state.context; cx; cx = cx.prev) { + for (var v = cx.vars; v; v = v.next) + if (v.name == varname) return true; + } + } + + function parseJS(state, style, type, content, stream) { + var cc = state.cc; + // Communicate our context to the combinators. + // (Less wasteful than consing up a hundred closures on every call.) + cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style; + + if (!state.lexical.hasOwnProperty("align")) + state.lexical.align = true; + + while(true) { + var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement; + if (combinator(type, content)) { + while(cc.length && cc[cc.length - 1].lex) + cc.pop()(); + if (cx.marked) return cx.marked; + if (type == "variable" && inScope(state, content)) return "variable-2"; + return style; + } + } + } + + // Combinator utils + + var cx = {state: null, column: null, marked: null, cc: null}; + function pass() { + for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]); + } + function cont() { + pass.apply(null, arguments); + return true; + } + function inList(name, list) { + for (var v = list; v; v = v.next) if (v.name == name) return true + return false; + } + function register(varname) { + var state = cx.state; + cx.marked = "def"; + if (!trackScope) return + if (state.context) { + if (state.lexical.info == "var" && state.context && state.context.block) { + // FIXME function decls are also not block scoped + var newContext = registerVarScoped(varname, state.context) + if (newContext != null) { + state.context = newContext + return + } + } else if (!inList(varname, state.localVars)) { + state.localVars = new Var(varname, state.localVars) + return + } + } + // Fall through means this is global + if (parserConfig.globalVars && !inList(varname, state.globalVars)) + state.globalVars = new Var(varname, state.globalVars) + } + function registerVarScoped(varname, context) { + if (!context) { + return null + } else if (context.block) { + var inner = registerVarScoped(varname, context.prev) + if (!inner) return null + if (inner == context.prev) return context + return new Context(inner, context.vars, true) + } else if (inList(varname, context.vars)) { + return context + } else { + return new Context(context.prev, new Var(varname, context.vars), false) + } + } + + function isModifier(name) { + return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly" + } + + // Combinators + + function Context(prev, vars, block) { this.prev = prev; this.vars = vars; this.block = block } + function Var(name, next) { this.name = name; this.next = next } + + var defaultVars = new Var("this", new Var("arguments", null)) + function pushcontext() { + cx.state.context = new Context(cx.state.context, cx.state.localVars, false) + cx.state.localVars = defaultVars + } + function pushblockcontext() { + cx.state.context = new Context(cx.state.context, cx.state.localVars, true) + cx.state.localVars = null + } + pushcontext.lex = pushblockcontext.lex = true + function popcontext() { + cx.state.localVars = cx.state.context.vars + cx.state.context = cx.state.context.prev + } + popcontext.lex = true + function pushlex(type, info) { + var result = function() { + var state = cx.state, indent = state.indented; + if (state.lexical.type == "stat") indent = state.lexical.indented; + else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev) + indent = outer.indented; + state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info); + }; + result.lex = true; + return result; + } + function poplex() { + var state = cx.state; + if (state.lexical.prev) { + if (state.lexical.type == ")") + state.indented = state.lexical.indented; + state.lexical = state.lexical.prev; + } + } + poplex.lex = true; + + function expect(wanted) { + function exp(type) { + if (type == wanted) return cont(); + else if (wanted == ";" || type == "}" || type == ")" || type == "]") return pass(); + else return cont(exp); + }; + return exp; + } + + function statement(type, value) { + if (type == "var") return cont(pushlex("vardef", value), vardef, expect(";"), poplex); + if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex); + if (type == "keyword b") return cont(pushlex("form"), statement, poplex); + if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex); + if (type == "debugger") return cont(expect(";")); + if (type == "{") return cont(pushlex("}"), pushblockcontext, block, poplex, popcontext); + if (type == ";") return cont(); + if (type == "if") { + if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex) + cx.state.cc.pop()(); + return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse); + } + if (type == "function") return cont(functiondef); + if (type == "for") return cont(pushlex("form"), pushblockcontext, forspec, statement, popcontext, poplex); + if (type == "class" || (isTS && value == "interface")) { + cx.marked = "keyword" + return cont(pushlex("form", type == "class" ? type : value), className, poplex) + } + if (type == "variable") { + if (isTS && value == "declare") { + cx.marked = "keyword" + return cont(statement) + } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) { + cx.marked = "keyword" + if (value == "enum") return cont(enumdef); + else if (value == "type") return cont(typename, expect("operator"), typeexpr, expect(";")); + else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex) + } else if (isTS && value == "namespace") { + cx.marked = "keyword" + return cont(pushlex("form"), expression, statement, poplex) + } else if (isTS && value == "abstract") { + cx.marked = "keyword" + return cont(statement) + } else { + return cont(pushlex("stat"), maybelabel); + } + } + if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"), pushblockcontext, + block, poplex, poplex, popcontext); + if (type == "case") return cont(expression, expect(":")); + if (type == "default") return cont(expect(":")); + if (type == "catch") return cont(pushlex("form"), pushcontext, maybeCatchBinding, statement, poplex, popcontext); + if (type == "export") return cont(pushlex("stat"), afterExport, poplex); + if (type == "import") return cont(pushlex("stat"), afterImport, poplex); + if (type == "async") return cont(statement) + if (value == "@") return cont(expression, statement) + return pass(pushlex("stat"), expression, expect(";"), poplex); + } + function maybeCatchBinding(type) { + if (type == "(") return cont(funarg, expect(")")) + } + function expression(type, value) { + return expressionInner(type, value, false); + } + function expressionNoComma(type, value) { + return expressionInner(type, value, true); + } + function parenExpr(type) { + if (type != "(") return pass() + return cont(pushlex(")"), maybeexpression, expect(")"), poplex) + } + function expressionInner(type, value, noComma) { + if (cx.state.fatArrowAt == cx.stream.start) { + var body = noComma ? arrowBodyNoComma : arrowBody; + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext); + else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext); + } + + var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma; + if (atomicTypes.hasOwnProperty(type)) return cont(maybeop); + if (type == "function") return cont(functiondef, maybeop); + if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); } + if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression); + if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop); + if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression); + if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop); + if (type == "{") return contCommasep(objprop, "}", null, maybeop); + if (type == "quasi") return pass(quasi, maybeop); + if (type == "new") return cont(maybeTarget(noComma)); + return cont(); + } + function maybeexpression(type) { + if (type.match(/[;\}\)\],]/)) return pass(); + return pass(expression); + } + + function maybeoperatorComma(type, value) { + if (type == ",") return cont(maybeexpression); + return maybeoperatorNoComma(type, value, false); + } + function maybeoperatorNoComma(type, value, noComma) { + var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma; + var expr = noComma == false ? expression : expressionNoComma; + if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext); + if (type == "operator") { + if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me); + if (isTS && value == "<" && cx.stream.match(/^([^<>]|<[^<>]*>)*>\s*\(/, false)) + return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me); + if (value == "?") return cont(expression, expect(":"), expr); + return cont(expr); + } + if (type == "quasi") { return pass(quasi, me); } + if (type == ";") return; + if (type == "(") return contCommasep(expressionNoComma, ")", "call", me); + if (type == ".") return cont(property, me); + if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me); + if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) } + if (type == "regexp") { + cx.state.lastType = cx.marked = "operator" + cx.stream.backUp(cx.stream.pos - cx.stream.start - 1) + return cont(expr) + } + } + function quasi(type, value) { + if (type != "quasi") return pass(); + if (value.slice(value.length - 2) != "${") return cont(quasi); + return cont(maybeexpression, continueQuasi); + } + function continueQuasi(type) { + if (type == "}") { + cx.marked = "string-2"; + cx.state.tokenize = tokenQuasi; + return cont(quasi); + } + } + function arrowBody(type) { + findFatArrow(cx.stream, cx.state); + return pass(type == "{" ? statement : expression); + } + function arrowBodyNoComma(type) { + findFatArrow(cx.stream, cx.state); + return pass(type == "{" ? statement : expressionNoComma); + } + function maybeTarget(noComma) { + return function(type) { + if (type == ".") return cont(noComma ? targetNoComma : target); + else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma) + else return pass(noComma ? expressionNoComma : expression); + }; + } + function target(_, value) { + if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); } + } + function targetNoComma(_, value) { + if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); } + } + function maybelabel(type) { + if (type == ":") return cont(poplex, statement); + return pass(maybeoperatorComma, expect(";"), poplex); + } + function property(type) { + if (type == "variable") {cx.marked = "property"; return cont();} + } + function objprop(type, value) { + if (type == "async") { + cx.marked = "property"; + return cont(objprop); + } else if (type == "variable" || cx.style == "keyword") { + cx.marked = "property"; + if (value == "get" || value == "set") return cont(getterSetter); + var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params + if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false))) + cx.state.fatArrowAt = cx.stream.pos + m[0].length + return cont(afterprop); + } else if (type == "number" || type == "string") { + cx.marked = jsonldMode ? "property" : (cx.style + " property"); + return cont(afterprop); + } else if (type == "jsonld-keyword") { + return cont(afterprop); + } else if (isTS && isModifier(value)) { + cx.marked = "keyword" + return cont(objprop) + } else if (type == "[") { + return cont(expression, maybetype, expect("]"), afterprop); + } else if (type == "spread") { + return cont(expressionNoComma, afterprop); + } else if (value == "*") { + cx.marked = "keyword"; + return cont(objprop); + } else if (type == ":") { + return pass(afterprop) + } + } + function getterSetter(type) { + if (type != "variable") return pass(afterprop); + cx.marked = "property"; + return cont(functiondef); + } + function afterprop(type) { + if (type == ":") return cont(expressionNoComma); + if (type == "(") return pass(functiondef); + } + function commasep(what, end, sep) { + function proceed(type, value) { + if (sep ? sep.indexOf(type) > -1 : type == ",") { + var lex = cx.state.lexical; + if (lex.info == "call") lex.pos = (lex.pos || 0) + 1; + return cont(function(type, value) { + if (type == end || value == end) return pass() + return pass(what) + }, proceed); + } + if (type == end || value == end) return cont(); + if (sep && sep.indexOf(";") > -1) return pass(what) + return cont(expect(end)); + } + return function(type, value) { + if (type == end || value == end) return cont(); + return pass(what, proceed); + }; + } + function contCommasep(what, end, info) { + for (var i = 3; i < arguments.length; i++) + cx.cc.push(arguments[i]); + return cont(pushlex(end, info), commasep(what, end), poplex); + } + function block(type) { + if (type == "}") return cont(); + return pass(statement, block); + } + function maybetype(type, value) { + if (isTS) { + if (type == ":") return cont(typeexpr); + if (value == "?") return cont(maybetype); + } + } + function maybetypeOrIn(type, value) { + if (isTS && (type == ":" || value == "in")) return cont(typeexpr) + } + function mayberettype(type) { + if (isTS && type == ":") { + if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr) + else return cont(typeexpr) + } + } + function isKW(_, value) { + if (value == "is") { + cx.marked = "keyword" + return cont() + } + } + function typeexpr(type, value) { + if (value == "keyof" || value == "typeof" || value == "infer" || value == "readonly") { + cx.marked = "keyword" + return cont(value == "typeof" ? expressionNoComma : typeexpr) + } + if (type == "variable" || value == "void") { + cx.marked = "type" + return cont(afterType) + } + if (value == "|" || value == "&") return cont(typeexpr) + if (type == "string" || type == "number" || type == "atom") return cont(afterType); + if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType) + if (type == "{") return cont(pushlex("}"), typeprops, poplex, afterType) + if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType, afterType) + if (type == "<") return cont(commasep(typeexpr, ">"), typeexpr) + if (type == "quasi") { return pass(quasiType, afterType); } + } + function maybeReturnType(type) { + if (type == "=>") return cont(typeexpr) + } + function typeprops(type) { + if (type.match(/[\}\)\]]/)) return cont() + if (type == "," || type == ";") return cont(typeprops) + return pass(typeprop, typeprops) + } + function typeprop(type, value) { + if (type == "variable" || cx.style == "keyword") { + cx.marked = "property" + return cont(typeprop) + } else if (value == "?" || type == "number" || type == "string") { + return cont(typeprop) + } else if (type == ":") { + return cont(typeexpr) + } else if (type == "[") { + return cont(expect("variable"), maybetypeOrIn, expect("]"), typeprop) + } else if (type == "(") { + return pass(functiondecl, typeprop) + } else if (!type.match(/[;\}\)\],]/)) { + return cont() + } + } + function quasiType(type, value) { + if (type != "quasi") return pass(); + if (value.slice(value.length - 2) != "${") return cont(quasiType); + return cont(typeexpr, continueQuasiType); + } + function continueQuasiType(type) { + if (type == "}") { + cx.marked = "string-2"; + cx.state.tokenize = tokenQuasi; + return cont(quasiType); + } + } + function typearg(type, value) { + if (type == "variable" && cx.stream.match(/^\s*[?:]/, false) || value == "?") return cont(typearg) + if (type == ":") return cont(typeexpr) + if (type == "spread") return cont(typearg) + return pass(typeexpr) + } + function afterType(type, value) { + if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) + if (value == "|" || type == "." || value == "&") return cont(typeexpr) + if (type == "[") return cont(typeexpr, expect("]"), afterType) + if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) } + if (value == "?") return cont(typeexpr, expect(":"), typeexpr) + } + function maybeTypeArgs(_, value) { + if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) + } + function typeparam() { + return pass(typeexpr, maybeTypeDefault) + } + function maybeTypeDefault(_, value) { + if (value == "=") return cont(typeexpr) + } + function vardef(_, value) { + if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)} + return pass(pattern, maybetype, maybeAssign, vardefCont); + } + function pattern(type, value) { + if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) } + if (type == "variable") { register(value); return cont(); } + if (type == "spread") return cont(pattern); + if (type == "[") return contCommasep(eltpattern, "]"); + if (type == "{") return contCommasep(proppattern, "}"); + } + function proppattern(type, value) { + if (type == "variable" && !cx.stream.match(/^\s*:/, false)) { + register(value); + return cont(maybeAssign); + } + if (type == "variable") cx.marked = "property"; + if (type == "spread") return cont(pattern); + if (type == "}") return pass(); + if (type == "[") return cont(expression, expect(']'), expect(':'), proppattern); + return cont(expect(":"), pattern, maybeAssign); + } + function eltpattern() { + return pass(pattern, maybeAssign) + } + function maybeAssign(_type, value) { + if (value == "=") return cont(expressionNoComma); + } + function vardefCont(type) { + if (type == ",") return cont(vardef); + } + function maybeelse(type, value) { + if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex); + } + function forspec(type, value) { + if (value == "await") return cont(forspec); + if (type == "(") return cont(pushlex(")"), forspec1, poplex); + } + function forspec1(type) { + if (type == "var") return cont(vardef, forspec2); + if (type == "variable") return cont(forspec2); + return pass(forspec2) + } + function forspec2(type, value) { + if (type == ")") return cont() + if (type == ";") return cont(forspec2) + if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression, forspec2) } + return pass(expression, forspec2) + } + function functiondef(type, value) { + if (value == "*") {cx.marked = "keyword"; return cont(functiondef);} + if (type == "variable") {register(value); return cont(functiondef);} + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext); + if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef) + } + function functiondecl(type, value) { + if (value == "*") {cx.marked = "keyword"; return cont(functiondecl);} + if (type == "variable") {register(value); return cont(functiondecl);} + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, popcontext); + if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondecl) + } + function typename(type, value) { + if (type == "keyword" || type == "variable") { + cx.marked = "type" + return cont(typename) + } else if (value == "<") { + return cont(pushlex(">"), commasep(typeparam, ">"), poplex) + } + } + function funarg(type, value) { + if (value == "@") cont(expression, funarg) + if (type == "spread") return cont(funarg); + if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); } + if (isTS && type == "this") return cont(maybetype, maybeAssign) + return pass(pattern, maybetype, maybeAssign); + } + function classExpression(type, value) { + // Class expressions may have an optional name. + if (type == "variable") return className(type, value); + return classNameAfter(type, value); + } + function className(type, value) { + if (type == "variable") {register(value); return cont(classNameAfter);} + } + function classNameAfter(type, value) { + if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter) + if (value == "extends" || value == "implements" || (isTS && type == ",")) { + if (value == "implements") cx.marked = "keyword"; + return cont(isTS ? typeexpr : expression, classNameAfter); + } + if (type == "{") return cont(pushlex("}"), classBody, poplex); + } + function classBody(type, value) { + if (type == "async" || + (type == "variable" && + (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) && + cx.stream.match(/^\s+#?[\w$\xa1-\uffff]/, false))) { + cx.marked = "keyword"; + return cont(classBody); + } + if (type == "variable" || cx.style == "keyword") { + cx.marked = "property"; + return cont(classfield, classBody); + } + if (type == "number" || type == "string") return cont(classfield, classBody); + if (type == "[") + return cont(expression, maybetype, expect("]"), classfield, classBody) + if (value == "*") { + cx.marked = "keyword"; + return cont(classBody); + } + if (isTS && type == "(") return pass(functiondecl, classBody) + if (type == ";" || type == ",") return cont(classBody); + if (type == "}") return cont(); + if (value == "@") return cont(expression, classBody) + } + function classfield(type, value) { + if (value == "!") return cont(classfield) + if (value == "?") return cont(classfield) + if (type == ":") return cont(typeexpr, maybeAssign) + if (value == "=") return cont(expressionNoComma) + var context = cx.state.lexical.prev, isInterface = context && context.info == "interface" + return pass(isInterface ? functiondecl : functiondef) + } + function afterExport(type, value) { + if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); } + if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); } + if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";")); + return pass(statement); + } + function exportField(type, value) { + if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); } + if (type == "variable") return pass(expressionNoComma, exportField); + } + function afterImport(type) { + if (type == "string") return cont(); + if (type == "(") return pass(expression); + if (type == ".") return pass(maybeoperatorComma); + return pass(importSpec, maybeMoreImports, maybeFrom); + } + function importSpec(type, value) { + if (type == "{") return contCommasep(importSpec, "}"); + if (type == "variable") register(value); + if (value == "*") cx.marked = "keyword"; + return cont(maybeAs); + } + function maybeMoreImports(type) { + if (type == ",") return cont(importSpec, maybeMoreImports) + } + function maybeAs(_type, value) { + if (value == "as") { cx.marked = "keyword"; return cont(importSpec); } + } + function maybeFrom(_type, value) { + if (value == "from") { cx.marked = "keyword"; return cont(expression); } + } + function arrayLiteral(type) { + if (type == "]") return cont(); + return pass(commasep(expressionNoComma, "]")); + } + function enumdef() { + return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex) + } + function enummember() { + return pass(pattern, maybeAssign); + } + + function isContinuedStatement(state, textAfter) { + return state.lastType == "operator" || state.lastType == "," || + isOperatorChar.test(textAfter.charAt(0)) || + /[,.]/.test(textAfter.charAt(0)); + } + + function expressionAllowed(stream, state, backUp) { + return state.tokenize == tokenBase && + /^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) || + (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0)))) + } + + // Interface + + return { + startState: function(basecolumn) { + var state = { + tokenize: tokenBase, + lastType: "sof", + cc: [], + lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false), + localVars: parserConfig.localVars, + context: parserConfig.localVars && new Context(null, null, false), + indented: basecolumn || 0 + }; + if (parserConfig.globalVars && typeof parserConfig.globalVars == "object") + state.globalVars = parserConfig.globalVars; + return state; + }, + + token: function(stream, state) { + if (stream.sol()) { + if (!state.lexical.hasOwnProperty("align")) + state.lexical.align = false; + state.indented = stream.indentation(); + findFatArrow(stream, state); + } + if (state.tokenize != tokenComment && stream.eatSpace()) return null; + var style = state.tokenize(stream, state); + if (type == "comment") return style; + state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type; + return parseJS(state, style, type, content, stream); + }, + + indent: function(state, textAfter) { + if (state.tokenize == tokenComment || state.tokenize == tokenQuasi) return CodeMirror.Pass; + if (state.tokenize != tokenBase) return 0; + var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top + // Kludge to prevent 'maybelse' from blocking lexical scope pops + if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) { + var c = state.cc[i]; + if (c == poplex) lexical = lexical.prev; + else if (c != maybeelse && c != popcontext) break; + } + while ((lexical.type == "stat" || lexical.type == "form") && + (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) && + (top == maybeoperatorComma || top == maybeoperatorNoComma) && + !/^[,\.=+\-*:?[\(]/.test(textAfter)))) + lexical = lexical.prev; + if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat") + lexical = lexical.prev; + var type = lexical.type, closing = firstChar == type; + + if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info.length + 1 : 0); + else if (type == "form" && firstChar == "{") return lexical.indented; + else if (type == "form") return lexical.indented + indentUnit; + else if (type == "stat") + return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0); + else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false) + return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit); + else if (lexical.align) return lexical.column + (closing ? 0 : 1); + else return lexical.indented + (closing ? 0 : indentUnit); + }, + + electricInput: /^\s*(?:case .*?:|default:|\{|\})$/, + blockCommentStart: jsonMode ? null : "/*", + blockCommentEnd: jsonMode ? null : "*/", + blockCommentContinue: jsonMode ? null : " * ", + lineComment: jsonMode ? null : "//", + fold: "brace", + closeBrackets: "()[]{}''\"\"``", + + helperType: jsonMode ? "json" : "javascript", + jsonldMode: jsonldMode, + jsonMode: jsonMode, + + expressionAllowed: expressionAllowed, + + skipExpression: function(state) { + parseJS(state, "atom", "atom", "true", new CodeMirror.StringStream("", 2, null)) + } + }; +}); + +CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/); + +CodeMirror.defineMIME("text/javascript", "javascript"); +CodeMirror.defineMIME("text/ecmascript", "javascript"); +CodeMirror.defineMIME("application/javascript", "javascript"); +CodeMirror.defineMIME("application/x-javascript", "javascript"); +CodeMirror.defineMIME("application/ecmascript", "javascript"); +CodeMirror.defineMIME("application/json", { name: "javascript", json: true }); +CodeMirror.defineMIME("application/x-json", { name: "javascript", json: true }); +CodeMirror.defineMIME("application/manifest+json", { name: "javascript", json: true }) +CodeMirror.defineMIME("application/ld+json", { name: "javascript", jsonld: true }); +CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true }); +CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true }); +}); + + +/* +file none +*/ +/*jslint-enable*/ diff --git a/asset_font_daley_bold.woff2 b/asset_font_daley_bold.woff2 new file mode 100644 index 000000000..783bdd697 Binary files /dev/null and b/asset_font_daley_bold.woff2 differ diff --git a/asset_image_folder_open_solid.svg b/asset_image_folder_open_solid.svg new file mode 100644 index 000000000..99d403c81 --- /dev/null +++ b/asset_image_folder_open_solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/asset_image_github_brands.svg b/asset_image_github_brands.svg new file mode 100644 index 000000000..7870c06dc --- /dev/null +++ b/asset_image_github_brands.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/asset_image_jslint_vim_plugin.png b/asset_image_jslint_vim_plugin.png new file mode 100644 index 000000000..d0f652305 Binary files /dev/null and b/asset_image_jslint_vim_plugin.png differ diff --git a/asset_image_jslint_wrapper_vim.png b/asset_image_jslint_wrapper_vim.png new file mode 100644 index 000000000..c987b46b6 Binary files /dev/null and b/asset_image_jslint_wrapper_vim.png differ diff --git a/asset_image_jslint_wrapper_vscode.png b/asset_image_jslint_wrapper_vscode.png new file mode 100644 index 000000000..1f4297171 Binary files /dev/null and b/asset_image_jslint_wrapper_vscode.png differ diff --git a/asset_image_json_160.svg b/asset_image_json_160.svg new file mode 100644 index 000000000..fca9b8749 --- /dev/null +++ b/asset_image_json_160.svg @@ -0,0 +1,104 @@ + + + + + JSON logo + + + + + + + + + + + + diff --git a/asset_image_logo_512.html b/asset_image_logo_512.html new file mode 100644 index 000000000..b538e29dd --- /dev/null +++ b/asset_image_logo_512.html @@ -0,0 +1,199 @@ + + + +logo + + + +
    +
    JS
    +
    Lint
    +
    + + diff --git a/asset_image_logo_512.png b/asset_image_logo_512.png new file mode 100644 index 000000000..19d154d68 Binary files /dev/null and b/asset_image_logo_512.png differ diff --git a/asset_image_logo_512.svg b/asset_image_logo_512.svg new file mode 100644 index 000000000..61872c4ae --- /dev/null +++ b/asset_image_logo_512.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + diff --git a/branch-alpha/.artifact/apidoc.html b/branch-alpha/.artifact/apidoc.html new file mode 100644 index 000000000..c6101547c --- /dev/null +++ b/branch-alpha/.artifact/apidoc.html @@ -0,0 +1,2076 @@ + + + + + + +JSLint apidoc + + + +
    +

    API Doc for JSLint (v2024.11.24)

    +
    + +

    Table of Contents

    +
    + +
    + +
    +

    Module "./jslint.mjs"

    +
      + +
    • +

      + 1. + function assertErrorThrownAsync(asyncFunc, regexp) + +

      +
    • +
    • Description and source-code:
      async function assertErrorThrownAsync(asyncFunc, regexp) {
      +
      +// This function will assert calling <asyncFunc> throws an error.
      +
      +    let err;
      +    try {
      +        await asyncFunc();
      +    } catch (errCaught) {
      +        err = errCaught;
      +    }
      +    assertOrThrow(err, "No error thrown.");
      +    assertOrThrow(
      +        !regexp || new RegExp(regexp).test(err.message),
      +        err
      +    );
      +}
    • +
    • Example usage:
      ...
      +
      +jstestDescribe((
      +"test v8CoverageReportCreate handling-behavior"
      +), function testBehaviorV8CoverageReportCreate() {
      +jstestIt((
      +    "test null-case handling-behavior"
      +), async function () {
      +    await assertErrorThrownAsync(function () {
      +        return v8CoverageReportCreate({});
      +    }, "invalid coverageDir");
      +});
      +jstestIt((
      +    "test coverage-report jslint.mjs handling-behavior"
      +), async function () {
      +    // test remove-old-coverage handling-behavior
      +...
    • + +
    • +

      + 2. + function assertJsonEqual(aa, bb, message) + +

      +
    • +
    • Description and source-code:
      function assertJsonEqual(aa, bb, message) {
      +
      +// This function will assert JSON.stringify(<aa>) === JSON.stringify(<bb>).
      +
      +    aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa), undefined, 1);
      +    bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb), undefined, 1);
      +    if (aa !== bb) {
      +        throw new Error(
      +            "\n" + aa + "\n!==\n" + bb
      +            + (
      +                typeof message === "string"
      +                ? " - " + message
      +                : message
      +                ? " - " + JSON.stringify(message)
      +                : ""
      +            )
      +        );
      +    }
      +}
    • +
    • Example usage:
      ...
      +
      +// Debug data1.
      +// await moduleFs.promises.writeFile(
      +//     ".test_v8_coverage_node_sqlite_merged.json",
      +//     JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n"
      +// );
      +
      +    assertJsonEqual(data1, data2);
      +});
      +});
      +
      +jstestDescribe((
      +"test v8CoverageReportCreate handling-behavior"
      +), function testBehaviorV8CoverageReportCreate() {
      +jstestIt((
      +...
    • + +
    • +

      + 3. + function assertOrThrow(condition, message) + +

      +
    • +
    • Description and source-code:
      function assertOrThrow(condition, message) {
      +
      +// This function will throw <message> if <condition> is falsy.
      +
      +    if (!condition) {
      +        throw (
      +            (!message || typeof message === "string")
      +            ? new Error(String(message).slice(0, 2048))
      +            : message
      +        );
      +    }
      +}
    • +
    • Example usage:
      ...
      +            assertJsonEqual(1, 2, "undefined");
      +        });
      +        await assertErrorThrownAsync(function () {
      +            assertJsonEqual(1, 2, {});
      +        });
      +        // test assertOrThrow error handling-behavior
      +        await assertErrorThrownAsync(function () {
      +            assertOrThrow(undefined, "undefined");
      +        });
      +        await assertErrorThrownAsync(function () {
      +            assertOrThrow(undefined, new Error());
      +        });
      +    });
      +});
      +...
    • + +
    • +

      + 4. + function debugInline(...argv) + +

      +
    • +
    • Description and source-code:
      function debug(...argv) {
      +
      +// This function will print <argv> to stderr and then return <argv>[0].
      +
      +    __consoleError("\n\ndebugInline");
      +    __consoleError(...argv);
      +    __consoleError("\n");
      +    return argv[0];
      +}
    • +
    • Example usage:
      N/A
    • + +
    • +

      + 5. + function fsWriteFileWithParents(pathname, data) + +

      +
    • +
    • Description and source-code:
      async function fsWriteFileWithParents(pathname, data) {
      +
      +// This function will write <data> to <pathname> and lazy-mkdirp if necessary.
      +
      +    await moduleFsInit();
      +
      +// Try writing to pathname.
      +
      +    try {
      +        await moduleFs.promises.writeFile(pathname, data);
      +    } catch (ignore) {
      +
      +// Lazy mkdirp.
      +
      +        await moduleFs.promises.mkdir(modulePath.dirname(pathname), {
      +            recursive: true
      +        });
      +
      +// Retry writing to pathname.
      +
      +        await moduleFs.promises.writeFile(pathname, data);
      +    }
      +    console.error("wrote file " + pathname);
      +}
    • +
    • Example usage:
      ...
      +                }
      +            ]
      +        }, undefined, 4)
      +    ]
      +].map(async function ([
      +    file, data
      +]) {
      +    await fsWriteFileWithParents(file, data);
      +}));
      +await jslint.jslint_cli({
      +    console_error: noop, // comment to debug
      +    mode_cli: true,
      +    process_argv: [
      +        "node", "jslint.mjs",
      +        "v8_coverage_report=.tmp/coverage_misc"
      +...
    • + +
    • +

      + 6. + function globExclude({ + excludeList = [], + includeList = [], + pathnameList = [] +}) + +

      +
    • +
    • Description and source-code:
      function globExclude({
      +    excludeList = [],
      +    includeList = [],
      +    pathnameList = []
      +}) {
      +
      +// This function will
      +// 1. Exclude pathnames in <pathnameList> that don't match glob-patterns in
      +//    <includeList>.
      +// 2. Exclude pathnames in <pathnameList> that match glob-patterns in
      +//    <excludeList>.
      +
      +    function globAssertNotWeird(list, name) {
      +
      +// This function will check if <list> of strings contain weird characters.
      +
      +        [
      +            [
      +                "\n", (
      +                    /^.*?([\u0000-\u0007\r]).*/gm
      +                )
      +            ],
      +            [
      +                "\r", (
      +                    /^.*?([\n]).*/gm
      +                )
      +            ]
      +        ].forEach(function ([
      +            separator, rgx
      +        ]) {
      +            list.join(separator).replace(rgx, function (match0, char) {
      +                throw new Error(
      +                    "Weird character "
      +                    + JSON.stringify(char)
      +                    + " found in " + name + " "
      +                    + JSON.stringify(match0)
      +                );
      +            });
      +        });
      +    }
      +
      +    function globToRegexp(pattern) {
      +
      +// This function will translate glob <pattern> to javascript-regexp,
      +// which javascript can then use to "glob" pathnames.
      +
      +        let ii = 0;
      +        let isClass = false;
      +        let strClass = "";
      +        let strRegex = "";
      +        pattern = pattern.replace((
      +            /\/\/+/g
      +        ), "/");
      +        pattern = pattern.replace((
      +            /\*\*\*+/g
      +        ), "**");
      +        pattern.replace((
      +            /\\\\|\\\[|\\\]|\[|\]|./g
      +        ), function (match0) {
      +            switch (match0) {
      +            case "[":
      +                if (isClass) {
      +                    strClass += "[";
      +                    return;
      +                }
      +                strClass += "\u0000";
      +                strRegex += "\u0000";
      +                isClass = true;
      +                return;
      +            case "]":
      +                if (isClass) {
      +                    isClass = false;...
      +}
      +
    • +
    • Example usage:
      ...
      +    "test/support/helper.js": (
      +        /^test\/support\/helper\.js$/gm
      +    )
      +}).forEach(function ([
      +    pattern, rgx
      +]) {
      +    assertJsonEqual(
      +        globExclude({
      +            excludeList: [
      +                pattern
      +            ]
      +        }).excludeList[0].source,
      +        rgx.source
      +    );
      +    assertJsonEqual(
      +...
    • + +
    • +

      + 7. + function htmlEscape(str) + +

      +
    • +
    • Description and source-code:
      function htmlEscape(str) {
      +
      +// This function will make <str> html-safe by escaping & < >.
      +
      +    return String(str).replace((
      +        /&/g
      +    ), "&amp;").replace((
      +        /</g
      +    ), "&lt;").replace((
      +        />/g
      +    ), "&gt;");
      +}
    • +
    • Example usage:
      ...
      +                            inHole = isHole;
      +                        }
      +                        chunk += char;
      +                    });
      +                    lineHtml += htmlEscape(chunk);
      +                    break;
      +                default:
      +                    lineHtml += htmlEscape(line);
      +                }
      +                html += String(`
      +<pre>
      +<span class="lineno">
      +<a href="#${lineId}" id="${lineId}">${String(ii + 1).padStart(5, " ")}.</a>
      +</span>
      +<span class="count
      +...
    • + +
    • +

      + 8. + function jslint( + source = "", + option_dict = empty(), + global_list = [] +) + +

      +
    • +
    • Description and source-code:
      function jslint(
      +    source = "",                // A text to analyze.
      +    option_dict = empty(),      // An object whose keys correspond to option
      +                                // ... names.
      +    global_list = []            // An array of strings containing global
      +                                // ... variables that the file is allowed
      +                                // ... readonly access.
      +) {
      +
      +// The jslint function itself.
      +
      +    let catch_list = [];        // The array containing all catch-blocks.
      +    let catch_stack = [         // The stack of catch-blocks.
      +        {
      +            context: empty()
      +        }
      +    ];
      +    let cause_dict = empty();   // The object of test-causes.
      +    let directive_list = [];    // The directive comments.
      +    let export_dict = empty();  // The exported names and values.
      +    let function_list = [];     // The array containing all functions.
      +    let function_stack = [];    // The stack of functions.
      +    let global_dict = empty();  // The object containing the global
      +                                // ... declarations.
      +    let import_list = [];       // The array collecting all import-from strings.
      +    let line_list = String(     // The array containing source lines.
      +        "\n" + source
      +    ).split(jslint_rgx_crlf).map(function (line_source) {
      +        return {
      +            line_source
      +        };
      +    });
      +    let mode_stop = false;      // true if JSLint cannot finish.
      +    let property_dict = empty();        // The object containing the tallied
      +                                        // ... property names.
      +    let state = empty();        // jslint state-object to be passed between
      +                                // jslint functions.
      +    let syntax_dict = empty();  // The object containing the parser.
      +    let tenure = empty();       // The predefined property registry.
      +    let token_global = {        // The global object; the outermost context.
      +        async: 0,
      +        body: true,
      +        context: empty(),
      +        finally: 0,
      +        from: 0,
      +...
      +}
      +
    • +
    • Example usage:
      ...
      +import fs from "fs";
      +(async function () {
      +    let result;
      +    let source = "function foo() {console.log(\u0027hello world\u0027);}\n";
      +
      +// Create JSLint report from <source> in javascript.
      +
      +    result = jslint.jslint(source);
      +    result = jslint.jslint_report(result);
      +    result = `<body class="JSLINT_ JSLINT_REPORT_">\n${result}</body>\n`;
      +
      +    await fs.promises.mkdir(".artifact/", {recursive: true});
      +    await fs.promises.writeFile(".artifact/jslint_report_hello.html", result);
      +    console.error("wrote file .artifact/jslint_report_hello.html");
      +}());
      +...
    • + +
    • +

      + 9. + function jslint_apidoc({ + example_list, + github_repo, + module_list, + package_name, + pathname, + version +}) + +

      +
    • +
    • Description and source-code:
      async function jslint_apidoc({
      +    example_list,
      +    github_repo,
      +    module_list,
      +    package_name,
      +    pathname,
      +    version
      +}) {
      +
      +// This function will create API Doc from <module_list>.
      +
      +    let elem_ii = 0;
      +    let html;
      +
      +    function elem_create(moduleObj, key, moduleName) {
      +
      +// This function will create a sub API Doc from elem <moduleObj>[<key>].
      +
      +        let example = "N/A";
      +        let id = encodeURIComponent("apidoc.elem." + moduleName + "." + key);
      +        let name;
      +        let signature;
      +        let source;
      +        name = htmlEscape((typeof moduleObj[key]) + " " + key);
      +        if (typeof moduleObj[key] !== "function") {
      +            return {
      +                name,
      +                signature: (`
      +<a class="apidocElementLiA" href="#${id}">
      +${name}
      +</a>
      +                `),
      +                source: (`
      +<li>
      +    <h2>
      +    <a href="#${id}" id="${id}">
      +    ${name}
      +    </a>
      +    </h2>
      +</li>
      +                `)
      +            };
      +        }
      +        // init source
      +        source = htmlEscape(trim_start(moduleObj[key].toString()));
      +        // init signature
      +        source = source.replace((
      +            /(\([\S\s]*?\)) \{/
      +        ), function (match0, match1) {
      +            signature = htmlEscape(
      +                match1.replace((
      +                    / *?\/\*[\S\s]*?\*\/ */g
      +                ), "").replace((
      +                    / *?\/\/.*/g
      +                ), "").replace((
      +                    /\n{2,}/g
      +                ), "\n")
      +            );
      +            return match0;
      +        });
      +        // init comment
      +        source = source.replace((
      +            /\n(?:\/\/.*?\n)+\n/
      +        ), "<span class=\"apidocCodeCommentSpan\">$&</span>");
      +        // init example
      +        example_list.some(function (example2) {
      +            example2.replace(
      +                new RegExp((
      +                    "((?:\\n.*?){8}(function )?)\\b"
      +                    + key
      +                    + "(\\((?:.*?\\n){8})"
      +                ), "g"),
      +  ...
      +}
      +
    • +
    • Example usage:
      ...
      +command[1] = command.slice(1).join("=");
      +
      +switch (command[0]) {
      +
      +// PR-362 - Add API Doc.
      +
      +case "jslint_apidoc":
      +    await jslint_apidoc(Object.assign(JSON.parse(process_argv[3]), {
      +        pathname: command[1]
      +    }));
      +    return;
      +
      +// PR-363 - Add command jslint_report.
      +
      +case "jslint_report":
      +...
    • + +
    • +

      + 10. + function jslint_assert(condition, message) + +

      +
    • +
    • Description and source-code:
      function jslint_assert(condition, message) {
      +
      +// This function will throw <message> if <condition> is falsy.
      +
      +    if (condition) {
      +        return condition;
      +    }
      +    throw new Error(
      +        `This was caused by a bug in JSLint.
      +Please open an issue with this stack-trace (and possible example-code) at
      +https://github.com/jslint-org/jslint/issues.
      +edition = "${jslint_edition}";
      +${String(message).slice(0, 2000)}`
      +    );
      +}
    • +
    • Example usage:
      ...
      +// ["let aa={};", "whitage", "opener", "", 0]
      +
      +test_cause("opener");
      +
      +// Probably deadcode.
      +// case "${}":
      +
      +jslint_assert(
      +    !(left.id + right.id === "${}"),
      +    "Expected !(left.id + right.id === \"${}\")."
      +);
      +switch (left.id + right.id) {
      +case "()":
      +case "[]":
      +case "{}":
      +...
    • + +
    • +

      + 11. + function jslint_cli({ + console_error, + console_log, + file, + import_meta_url, + mode_cli, + mode_noop, + option, + process_argv, + process_env, + process_exit, + source +}) + +

      +
    • +
    • Description and source-code:
      async function jslint_cli({
      +    console_error,
      +    console_log,
      +    file,
      +    import_meta_url,
      +    mode_cli,
      +    mode_noop,
      +    option,
      +    process_argv,
      +    process_env,
      +    process_exit,
      +    source
      +}) {
      +
      +// This function will run jslint from nodejs-cli.
      +
      +    let command;
      +    let data;
      +    let exit_code = 0;
      +    let mode_report;
      +    let mode_wrapper_vim;
      +    let result;
      +
      +    function jslint_from_file({
      +        code,
      +        file,
      +        line_offset = 0,
      +        mode_conditional,
      +        option = empty()
      +    }) {
      +        let result_from_file;
      +        if (
      +            mode_conditional
      +            && !(
      +                /^\/\*jslint\b/m
      +            ).test(code.slice(0, 65536))
      +        ) {
      +            return;
      +        }
      +        option = Object.assign(empty(), option, {
      +            file
      +        });
      +        switch ((
      +            /\.\w+?$|$/m
      +        ).exec(file)[0]) {
      +        case ".html":
      +
      +// Recursively jslint embedded "<script>\n...\n</script>".
      +
      +            code.replace((
      +                /^<script\b[^>]*?>\n([\S\s]*?\n)<\/script>$/gm
      +            ), function (ignore, match1, ii) {
      +                jslint_from_file({
      +                    code: match1,
      +                    file: file + ".<script>.js",
      +                    line_offset: string_line_count(code.slice(0, ii)) + 1,
      +                    option: Object.assign(empty(), {
      +                        browser: true
      +                    }, option)
      +                });
      +                return "";
      +            });
      +            return;
      +        case ".md":
      +
      +// Recursively jslint embedded "node --eval '\n...\n'".
      +
      +            jslint_node_eval({
      +                code,
      +                file,
      +                mode_conditional: true,
      +                option
      +            });
      +            return;
      +        case ".sh":
      +
      +// Recursively jslint embedded "node --eval '\n...\n'".
      +
      +            jslint_node_eval({
      +                code,
      +                file,
      +                option
      +            });
      +            return;
      +       ...
      +}
      +
    • +
    • Example usage:
      ...
      +        }, undefined, 4)
      +    ]
      +].map(async function ([
      +    file, data
      +]) {
      +    await fsWriteFileWithParents(file, data);
      +}));
      +await jslint.jslint_cli({
      +    console_error: noop, // comment to debug
      +    mode_cli: true,
      +    process_argv: [
      +        "node", "jslint.mjs",
      +        "v8_coverage_report=.tmp/coverage_misc"
      +        // "node", ".tmp/coverage_misc/aa.js"
      +    ]
      +...
    • + +
    • +

      + 12. + function jslint_phase1_split() + +

      +
    • +
    • Description and source-code:
      function jslint_phase1_split() {
      +
      +// PHASE 1. Split <source> by newlines into <line_list>.
      +
      +    return;
      +}
    • +
    • Example usage:
      ...
      +            warn,
      +            warn_at,
      +            warning_list
      +        });
      +
      +// PHASE 1. Split <source> by newlines into <line_list>.
      +
      +        jslint_phase1_split(state);
      +        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
      +        jslint_assert(
      +            function_stack.length === 0,
      +            `function_stack.length === 0.`
      +        );
      +
      +// PHASE 2. Lex <line_list> into <token_list>.
      +...
    • + +
    • +

      + 13. + function jslint_phase2_lex(state) + +

      +
    • +
    • Description and source-code:
      function jslint_phase2_lex(state) {
      +
      +// PHASE 2. Lex <line_list> into <token_list>.
      +
      +    let {
      +        artifact,
      +        directive_list,
      +        global_dict,
      +        global_list,
      +        line_list,
      +        option_dict,
      +        stop,
      +        stop_at,
      +        tenure,
      +        test_cause,
      +        token_global,
      +        token_list,
      +        warn,
      +        warn_at
      +    } = state;
      +    let char;                   // The current character being lexed.
      +    let column = 0;             // The column number of the next character.
      +    let from;                   // The starting column number of the token.
      +    let from_mega;              // The starting column of megastring.
      +    let line = 0;               // The line number of the next character.
      +    let line_disable;           // The starting line of "/*jslint-disable*/".
      +    let line_mega;              // The starting line of megastring.
      +    let line_source = "";       // The remaining line source string.
      +    let line_whole = "";        // The whole line source string.
      +    let mode_digits_empty_string = 1;
      +    let mode_digits_numeric_separator = 2;
      +    let mode_directive = true;  // true if directives are still allowed.
      +    let mode_mega = false;      // true if currently parsing a megastring
      +                                // ... literal.
      +    let mode_regexp;            // true if regular expression literal seen on
      +                                // ... this line.
      +    let paren_backtrack_list = [];      // List of most recent "(" tokens at any
      +                                        // ... paren-depth.
      +    let paren_depth = 0;        // Keeps track of current paren-depth.
      +    let snippet = "";           // A piece of string.
      +    let token_1;                // The first token.
      +    let token_prv = token_global;       // The previous token including
      +                                        // ... comments.
      +    let token_prv_expr = token_global;  // The previous token excluding
      +                                        // ... comm...
      +}
      +
    • +
    • Example usage:
      ...
      +        jslint_assert(
      +            function_stack.length === 0,
      +            `function_stack.length === 0.`
      +        );
      +
      +// PHASE 2. Lex <line_list> into <token_list>.
      +
      +        jslint_phase2_lex(state);
      +        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
      +        jslint_assert(
      +            function_stack.length === 0,
      +            `function_stack.length === 0.`
      +        );
      +
      +// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
      +...
    • + +
    • +

      + 14. + function jslint_phase3_parse(state) + +

      +
    • +
    • Description and source-code:
      function jslint_phase3_parse(state) {
      +
      +// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
      +
      +// Parsing:
      +
      +// Parsing weaves the tokens into an abstract syntax tree. During that process,
      +// a token may be given any of these properties:
      +
      +//      arity       string
      +//      label       identifier
      +//      name        identifier
      +//      expression  expressions
      +//      block       statements
      +//      else        statements (else, default, catch)
      +
      +// Specialized tokens may have additional properties.
      +
      +    let anon = "anonymous";     // The guessed name for anonymous functions.
      +    let {
      +        artifact,
      +        catch_list,
      +        catch_stack,
      +        export_dict,
      +        function_list,
      +        function_stack,
      +        global_dict,
      +        import_list,
      +        is_equal,
      +        option_dict,
      +        property_dict,
      +        stop,
      +        syntax_dict,
      +        tenure,
      +        test_cause,
      +        token_global,
      +        token_list,
      +        warn,
      +        warn_at
      +    } = state;
      +    let catchage = catch_stack[0];      // The current catch-block.
      +    let functionage = token_global;     // The current function.
      +    let mode_var;               // "var" if using var; "let" if using let.
      +    let token_ii = 0;           // The number of the next token.
      +    let token_now = token_global;       // The current token being examined in
      +                                        // ... the parse.
      +    let token_nxt = token_global;       // The next token to be examined in
      +                                        // ... <token_list>.
      +
      +    function advance(id, match) {
      +
      +// Produce the next token.
      +
      +// Attempt to give helpful names to anonymous functions.
      +
      +        if (
      +            token_now.identifier
      +            && token_now.id !== "function"
      +            && token_now.id !== "async"
      +        ) {
      +            anon = token_now.id;
      +        } else if (
      +            token_now.id === "(string)"
      +            && jslint_rgx_identifier.test(token_now.value)
      +       ...
      +}
      +
    • +
    • Example usage:
      ...
      +        jslint_assert(
      +            function_stack.length === 0,
      +            `function_stack.length === 0.`
      +        );
      +
      +// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
      +
      +        jslint_phase3_parse(state);
      +        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
      +        jslint_assert(
      +            function_stack.length === 0,
      +            `function_stack.length === 0.`
      +        );
      +
      +// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
      +...
    • + +
    • +

      + 15. + function jslint_phase4_walk(state) + +

      +
    • +
    • Description and source-code:
      function jslint_phase4_walk(state) {
      +
      +// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
      +//          recursive traversal. Each node may be processed on the way down
      +//          (preaction) and on the way up (postaction).
      +
      +    let {
      +        artifact,
      +        catch_stack,
      +        function_stack,
      +        global_dict,
      +        is_equal,
      +        is_weird,
      +        option_dict,
      +        syntax_dict,
      +        test_cause,
      +        token_global,
      +        warn
      +    } = state;
      +    let block_stack = [];               // The stack of blocks.
      +    let blockage = token_global;        // The current block.
      +    let catchage = catch_stack[0];      // The current catch-block.
      +    let functionage = token_global;     // The current function.
      +    let postaction;
      +    let postamble;
      +    let posts = empty();
      +    let preaction;
      +    let preamble;
      +    let pres = empty();
      +
      +// The relational operators.
      +
      +    let relationop = object_assign_from_list(empty(), [
      +        "!=", "!==", "<", "<=", "==", "===", ">", ">="
      +    ], true);
      +
      +// Ambulation of the parse tree.
      +
      +    function action(when) {
      +
      +// Produce a function that will register task functions that will be called as
      +// the tree is traversed.
      +
      +        return function (arity, id, task) {
      +            let a_set = when[arity];
      +            let i_set;
      +
      +// The id parameter is optional. If excluded, the task will be applied to all
      +// ids.
      +
      +            if (typeof id !== "string") {
      +                task = id;
      +                id = "(all)";
      +            }
      +
      +// If this arity has no registrations yet, then create a set object to hold
      +// them.
      +
      +            if (a_set === undefined) {
      +                a_set = empty();
      +                when[arity] = a_set;
      +            }
      +
      +// If this id has no registrations yet, then create a set array to hold them.
      +
      +            i_set = a_set[id];
      +            if (i_set === undefined) {
      +                i_set = [];
      +                a_set[id] = i_set;
      +            }
      +
      +// Register the task with the arity and the id.
      +...
      +}
      +
    • +
    • Example usage:
      ...
      +);
      +
      +// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
      +//          recursive traversal. Each node may be processed on the way down
      +//          (preaction) and on the way up (postaction).
      +
      +if (!state.mode_json) {
      +    jslint_phase4_walk(state);
      +}
      +jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
      +jslint_assert(
      +    function_stack.length === 0,
      +    `function_stack.length === 0.`
      +);
      +...
    • + +
    • +

      + 16. + function jslint_phase5_whitage(state) + +

      +
    • +
    • Description and source-code:
      function jslint_phase5_whitage(state) {
      +
      +// PHASE 5. Check whitespace between tokens in <token_list>.
      +
      +    let {
      +        artifact,
      +        catch_list,
      +        function_list,
      +        function_stack,
      +        option_dict,
      +        test_cause,
      +        token_global,
      +        token_list,
      +        warn
      +    } = state;
      +    let closer = "(end)";
      +    let free = false;
      +
      +// free = false
      +
      +// cause:
      +// "()=>0"
      +// "aa()"
      +// "aa(0,0)"
      +// "function(){}"
      +
      +// free = true
      +
      +// cause:
      +// "(0)"
      +// "(aa)"
      +// "aa(0)"
      +// "do{}while()"
      +// "for(){}"
      +// "if(){}"
      +// "switch(){}"
      +// "while(){}"
      +
      +    let left = token_global;
      +    let margin = 0;
      +    let mode_indent = (
      +
      +// PR-330 - Allow 2-space indent.
      +
      +        option_dict.indent2
      +        ? 2
      +        : 4
      +    );
      +    let nr_comments_skipped = 0;
      +    let open = true;
      +    let opening = true;
      +    let right;
      +
      +// This is the set of infix operators that require a space on each side.
      +
      +    let spaceop = object_assign_from_list(empty(), [
      +        "!=", "!==", "%", "%=", "&", "&&", "&=", "*", "*=", "+=", "-=", "/",
      +        "/=", "<", "<<", "<<=", "<=", "=", "==", "===", "=>", ">", ">=", ">>",
      +        ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||"
      +    ], true);
      +
      +    function at_margin(fit) {
      +        const at = margin + fit;
      +        if (right.from !== at) {
      +            return expected_at(at);
      +        }
      +    }
      +
      +    function delve(the_function) {
      +        Object.keys(the_function.context).forEach(function (id) {
      +            const name = the_function.context[id];
      +            if (id !== "ignore" && name.parent === the_function) {
      +
      +// test_cause:
      +// ["function aa(aa) {return aa;}", "delve", "id", "", 0]
      +
      +                test_cause("id");
      +                if (
      +                    name.used === 0
      +
      +// Probably deadcode.
      +// && (
      +//     name.role !== "function"
      +//     || name.parent.arity !== "unary"
      +// )
      +
      +                    && jslint_assert(
      +                        name.role...
      +}
      +
    • +
    • Example usage:
      ...
      +    function_stack.length === 0,
      +    `function_stack.length === 0.`
      +);
      +
      +// PHASE 5. Check whitespace between tokens in <token_list>.
      +
      +if (!state.mode_json && warning_list.length === 0) {
      +    jslint_phase5_whitage(state);
      +}
      +jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
      +jslint_assert(
      +    function_stack.length === 0,
      +    `function_stack.length === 0.`
      +);
      +...
    • + +
    • +

      + 17. + function jslint_report({ + exports, + froms, + functions, + global, + json, + module, + property, + stop, + warnings +}) + +

      +
    • +
    • Description and source-code:
      function jslint_report({
      +    exports,
      +    froms,
      +    functions,
      +    global,
      +    json,
      +    module,
      +    property,
      +    stop,
      +    warnings
      +}) {
      +
      +// This function will create human-readable, html-report
      +// for warnings, properties, and functions from jslint-result-object.
      +//
      +// Example usage:
      +//  let result = jslint("console.log('hello world')");
      +//  let html = jslint_report(result);
      +
      +    let html = "";
      +    let length_80 = 1111;
      +
      +    function address(line = 1, column = 1) {
      +
      +// This function will create HTML address element from <line> and <column>
      +
      +        return `<address>${Number(line)}: ${Number(column)}</address>`;
      +
      +    }
      +
      +    function detail(title, list) {
      +        return (
      +            (Array.isArray(list) && list.length > 0)
      +            ? (
      +
      +// Google Lighthouse Accessibility - <dl>'s do not contain only properly-ordered
      +// <dt> and <dd> groups, <script>, <template> or <div> elements.
      +
      +                "<dl>"
      +                + "<dt>" + htmlEscape(title) + "</dt>"
      +                + "<dd>" + list.join(", ") + "</dd>"
      +                + "</dl>"
      +            )
      +            : ""
      +        );
      +    }
      +
      +    html += String(`
      +<style class="JSLINT_REPORT_STYLE">
      +/* jslint utility2:true */
      +/*csslint box-model: false, ids:false */
      +/*csslint ignore:start*/
      +@font-face {
      +    font-display: swap;
      +    font-family: "Daley";
      +    src: url(
      +"data:font/woff2;base64,d09GMgABAAAAABy4AA4AAAAAThwAABxiAAEAAAAAAAAAAAAA\
      +AAAAAAAAAAAAAAAABmAAgiQINAmcDBEICuc41DEBNgIkA4R2C4I+AAQgBYkuByAMgScfYUIF\
      +7NgjsHGAbcDVFkXZ5Jwd+P96IGPc9rl9ETBEaCzCJkvY2UpziRZ7zftZWk8052U9+NqX6vXL\
      +KDflSQnlJ0bP+QnPQAy744n9mup6H9PaCDFwM5zjf8exB89bZ1cdrYOP0NgnuRDRWlk9u/fE\
      +llkxqmfH8lmRQ/DAmER9opk9wR6suc1LvTiXNEe1vbhUCH2USgnEwH3vUm05JQqejGvZvOtz\
      +7sIKEGgLdDNl/IrfqWVZG/wr42ekomEm91VA1p4LhHBuFzHF8//u7vvbREHMQqGtNLmiOOD/\
      +X7WWiwqyCE98qt0jk5JJmgR5WJJElBmzRb1F7a66MmSLTNWZ2XSHfKBSKHoVteSEJ6EOdvVw\
      +fNZOtXKDe39jXdRlkmMnOWIOFBgeEK/b0mFsgffnP...
      +}
      +
    • +
    • Example usage:
      ...
      +
      +    editor.on("lintJslintAfter", function (options) {
      +
      +// Generate jslint-report from options.result.
      +
      +        document.querySelector(
      +            ".JSLINT_REPORT_"
      +        ).innerHTML = window.jslint.jslint_report(options.result);
      +    });
      +
      +// Manually trigger linter.
      +
      +    editor.performLint();
      +});
      +</script>
      +...
    • + +
    • +

      + 18. + function jstestDescribe(description, testFunction) + +

      +
    • +
    • Description and source-code:
      async function jstestDescribe(description, testFunction) {
      +
      +// This function will create-and-run test-group <testFunction>
      +// with given <description>.
      +
      +    let message;
      +    let result;
      +    let timerTimeout;
      +
      +// Init jstestTimeStart.
      +
      +    if (jstestTimeStart === undefined) {
      +        jstestTimeStart = jstestTimeStart || Date.now();
      +        process.on("exit", jstestOnExit);
      +    }
      +
      +// PR-457 - Wait awhile for imports to initialize.
      +
      +    await new Promise(function (resolve) {
      +        setTimeout(resolve);
      +    });
      +
      +// Init jstestItList.
      +
      +    jstestItList = [];
      +    testFunction();
      +
      +// Wait for jstestItList to resolve.
      +
      +    timerTimeout = setTimeout(noop, 0x7fffffff);
      +    result = await Promise.all(jstestItList);
      +    clearTimeout(timerTimeout);
      +
      +// Print test results.
      +
      +    message = (
      +        "\n  " + (Date.now() - jstestTimeStart) + "ms"
      +        + " - test describe - " + description + "\n"
      +        + result.map(function ([
      +            err, description, mode
      +        ]) {
      +            jstestItCount += 1;
      +            if (err) {
      +                jstestCountFailed += 1;
      +                err = (
      +                    "    \u001b[31m\u2718 " + jstestItCount + ". test it - "
      +                    + description + "\n" + err.stack + "\u001b[39m"
      +                );
      +                if (mode === "pass") {
      +                    jstestCountFailed -= 1;
      +                    err = "";
      +                }
      +            }
      +            return err || (
      +                "    \u001b[32m\u2714 " + jstestItCount + ". test it - "
      +                + description + "\u001b[39m"
      +            );
      +        }).join("\n")
      +    );
      +    console.error(message);
      +}
    • +
    • Example usage:
      ...
      +//     JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n"
      +// );
      +
      +    assertJsonEqual(data1, data2);
      +});
      +});
      +
      +jstestDescribe((
      +"test v8CoverageReportCreate handling-behavior"
      +), function testBehaviorV8CoverageReportCreate() {
      +jstestIt((
      +    "test null-case handling-behavior"
      +), async function () {
      +    await assertErrorThrownAsync(function () {
      +        return v8CoverageReportCreate({});
      +...
    • + +
    • +

      + 19. + function jstestIt(description, testFunction, mode) + +

      +
    • +
    • Description and source-code:
      function jstestIt(description, testFunction, mode) {
      +
      +// This function will create-and-run test-case <testFunction>
      +// inside current test-group with given <description>.
      +
      +    jstestCountTotal += 1;
      +    jstestItList.push(new Promise(async function (resolve) {
      +        let err;
      +        try {
      +            await testFunction();
      +        } catch (errCaught) {
      +            err = errCaught;
      +        }
      +        resolve([err, description, mode]);
      +    }));
      +}
    • +
    • Example usage:
      ...
      +                "v8_coverage_report=" + dir,
      +                "node",
      +                file
      +            ]
      +        });
      +    });
      +});
      +jstestIt((
      +    "test npm handling-behavior"
      +), async function () {
      +    await jslint.jslint_cli({
      +        console_error: noop, // comment to debug
      +        mode_cli: true,
      +        process_argv: [
      +            "node", "jslint.mjs",
      +...
    • + +
    • +

      + 20. + function jstestOnExit(exitCode, mode) + +

      +
    • +
    • Description and source-code:
      function jstestOnExit(exitCode, mode) {
      +
      +// This function will on process-exit, print test-report
      +// and exit with non-zero exit-code if any test failed.
      +
      +    let message = (
      +        (
      +            (jstestCountFailed || mode === "testsFailed")
      +            ? "\n\u001b[31m"
      +            : "\n\u001b[32m"
      +        )
      +        + "  tests total  - " + jstestCountTotal + "\n"
      +        + "  tests failed - " + jstestCountFailed + "\n"
      +        + "\n"
      +        + "  time finished - "
      +        + Number(Date.now() - jstestTimeStart).toLocaleString()
      +        + " ms\n"
      +        + "\u001b[39m"
      +    );
      +    if (mode !== "testsFailed") {
      +        console.error(message);
      +    }
      +    process.exitCode = exitCode || jstestCountFailed;
      +    return message;
      +}
    • +
    • Example usage:
      ...
      +    "test jstestDescribe error handling-behavior"
      +), function () {
      +    throw new Error();
      +}, "pass");
      +jstestIt((
      +    "test jstestOnExit tests-failed handling-behavior"
      +), function () {
      +    jstestOnExit(undefined, "testsFailed");
      +});
      +});
      +
      +jstestDescribe((
      +"test misc handling-behavior"
      +), function testBehaviorMisc() {
      +jstestIt((
      +...
    • + +
    • +

      + 21. + function moduleFsInit() + +

      +
    • +
    • Description and source-code:
      async function moduleFsInit() {
      +
      +// This function will import nodejs builtin-modules if they have not yet been
      +// imported.
      +
      +// State 3 - Modules already imported.
      +
      +    if (moduleFs !== undefined) {
      +        return;
      +    }
      +
      +// State 2 - Wait while modules are importing.
      +
      +    if (moduleFsInitResolveList !== undefined) {
      +        return new Promise(function (resolve) {
      +            moduleFsInitResolveList.push(resolve);
      +        });
      +    }
      +
      +// State 1 - Start importing modules.
      +
      +    moduleFsInitResolveList = [];
      +    [
      +        moduleChildProcess,
      +        moduleFs,
      +        modulePath,
      +        moduleUrl
      +    ] = await Promise.all([
      +        import("child_process"),
      +        import("fs"),
      +        import("path"),
      +        import("url")
      +    ]);
      +    while (moduleFsInitResolveList.length > 0) {
      +        moduleFsInitResolveList.shift()();
      +    }
      +}
    • +
    • Example usage:
      ...
      +let sourceJslintMjs;
      +let testCoverageMergeData;
      +
      +await (async function init() {
      +
      +// Coverage-hack - Ugly-hack to get test-coverage for all initialization-states.
      +
      +moduleFsInit();
      +moduleFsInit();
      +
      +// Cleanup directory .tmp
      +
      +await moduleFs.promises.rm(".tmp", {
      +    recursive: true
      +}).catch(noop);
      +...
    • + +
    • +

      + 22. + function noop(val) + +

      +
    • +
    • Description and source-code:
      function noop(val) {
      +
      +// This function will do nothing except return <val>.
      +
      +    return val;
      +}
    • +
    • Example usage:
      ...
      +jstestDescribe((
      +"test misc handling-behavior"
      +), function testBehaviorMisc() {
      +jstestIt((
      +    "test misc handling-behavior"
      +), async function () {
      +    // test debugInline handling-behavior
      +    noop(debugInline);
      +    // test assertErrorThrownAsync error handling-behavior
      +    await assertErrorThrownAsync(function () {
      +        return assertErrorThrownAsync(noop);
      +    });
      +    // test assertJsonEqual error handling-behavior
      +    await assertErrorThrownAsync(function () {
      +        assertJsonEqual(1, 2);
      +...
    • + +
    • +

      + 23. + function objectDeepCopyWithKeysSorted(obj) + +

      +
    • +
    • Description and source-code:
      function objectDeepCopyWithKeysSorted(obj) {
      +
      +// This function will recursively deep-copy <obj> with keys sorted.
      +
      +    let sorted;
      +    if (typeof obj !== "object" || !obj) {
      +        return obj;
      +    }
      +
      +// Recursively deep-copy list with child-keys sorted.
      +
      +    if (Array.isArray(obj)) {
      +        return obj.map(objectDeepCopyWithKeysSorted);
      +    }
      +
      +// Recursively deep-copy obj with keys sorted.
      +
      +    sorted = Object.create(null);
      +    Object.keys(obj).sort().forEach(function (key) {
      +        sorted[key] = objectDeepCopyWithKeysSorted(obj[key]);
      +    });
      +    return sorted;
      +}
    • +
    • Example usage:
      ...
      +        ];
      +        data1 = v8CoverageListMerge(data1);
      +        data1 = v8CoverageListMerge([data1]);
      +
      +// Debug data1.
      +// await moduleFs.promises.writeFile(
      +//     ".test_v8_coverage_node_sqlite_merged.json",
      +//     JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n"
      +// );
      +
      +        assertJsonEqual(data1, data2);
      +    });
      +});
      +
      +jstestDescribe((
      +...
    • + +
    • +

      + 24. + function v8CoverageListMerge(processCovs) + +

      +
    • +
    • Description and source-code:
      function v8CoverageListMerge(processCovs) {
      +
      +// This function is derived from MIT Licensed v8-coverage at
      +// https://github.com/demurgos/v8-coverage/tree/master/ts
      +// https://github.com/demurgos/v8-coverage/blob/master/ts/LICENSE.md
      +//
      +// Merges a list of v8 process coverages.
      +// The result is normalized.
      +// The input values may be mutated, it is not safe to use them after passing
      +// them to this function.
      +// The computation is synchronous.
      +// @param processCovs Process coverages to merge.
      +// @return Merged process coverage.
      +
      +    let resultMerged = [];      // List of merged scripts from processCovs.
      +    let urlToScriptDict = new Map();    // Map scriptCov.url to scriptCovs.
      +
      +    function compareRangeList(aa, bb) {
      +
      +// Compares two range coverages.
      +// The ranges are first ordered by ascending `startOffset` and then by
      +// descending `endOffset`.
      +// This corresponds to a pre-order tree traversal.
      +
      +        if (aa.startOffset !== bb.startOffset) {
      +            return aa.startOffset - bb.startOffset;
      +        }
      +        return bb.endOffset - aa.endOffset;
      +    }
      +
      +    function dictKeyValueAppend(dict, key, val) {
      +
      +// This function will append <val> to list <dict>[<key>].
      +
      +        let list = dict.get(key);
      +        if (list === undefined) {
      +            list = [];
      +            dict.set(key, list);
      +        }
      +        list.push(val);
      +    }
      +
      +    function mergeTreeList(parentTrees) {
      +
      +// This function will return RangeTree object with <parentTrees> merged into
      +// property-children.
      +// @precondition Same `start` and `end` for all the parentTrees
      +
      +        if (parentTrees.length <= 1) {
      +            return parentTrees[0];
      +        }
      +
      +// new RangeTree().
      +
      +        return {
      +
      +// Merge parentTrees into property-children.
      +
      +            children: mergeTreeListToChildren(parentTrees),
      +            delta: parentTrees.reduce(function (aa, bb) {
      +                return aa + bb.delta;
      +            }, 0),
      +            end: parentTrees[0].end,
      +            start: parentTrees[0].start
      +   ...
      +}
      +
    • +
    • Example usage:
      ...
      +            "test_v8_coverage_node_sqlite_13216_1633662333140_0.json"
      +        ].map(function (file) {
      +            return testCoverageMergeData[file];
      +        });
      +        let data2 = testCoverageMergeData[
      +            "test_v8_coverage_node_sqlite_merged.json"
      +        ];
      +        data1 = v8CoverageListMerge(data1);
      +        data1 = v8CoverageListMerge([data1]);
      +
      +// Debug data1.
      +// await moduleFs.promises.writeFile(
      +//     ".test_v8_coverage_node_sqlite_merged.json",
      +//     JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n"
      +// );
      +...
    • + +
    • +

      + 25. + function v8CoverageReportCreate({ + consoleError, + coverageDir, + processArgv = [] +}) + +

      +
    • +
    • Description and source-code:
      async function v8CoverageReportCreate({
      +    consoleError,
      +    coverageDir,
      +    processArgv = []
      +}) {
      +
      +// This function will create html-coverage-reports directly from
      +// v8-coverage-files in <coverageDir>.
      +// 1. Spawn node.js program <processArgv> with NODE_V8_COVERAGE.
      +// 2. Merge JSON v8-coverage-files in <coverageDir>.
      +// 3. Create html-coverage-reports in <coverageDir>.
      +
      +    let cwd;
      +    let excludeList = [];
      +    let exitCode = 0;
      +    let fileDict;
      +    let includeList = [];
      +    let modeIncludeNodeModules;
      +    let processArgElem;
      +    let promiseList = [];
      +    let v8CoverageObj;
      +
      +    function htmlRender({
      +        fileList,
      +        lineList,
      +        modeIndex,
      +        pathname
      +    }) {
      +        let html;
      +        let padLines;
      +        let padPathname;
      +        let txt;
      +        let txtBorder;
      +        html = "";
      +        html += String(`
      +<!DOCTYPE html>
      +<html lang="en">
      +<head>
      +<title>V8 Coverage Report</title>
      +<style>
      +/* jslint utility2:true */
      +/*csslint ignore:start*/
      +.coverage,
      +.coverage a,
      +.coverage div,
      +.coverage pre,
      +.coverage span,
      +.coverage table,
      +.coverage tbody,
      +.coverage td,
      +.coverage th,
      +.coverage thead,
      +.coverage tr {
      +    box-sizing: border-box;
      +    font-family: monospace;
      +}
      +/*csslint ignore:end*/
      +
      +/* css - coverage_report - general */
      +body {
      +    margin: 0;
      +}
      +.coverage pre {
      +    margin: 5px 0;
      +}
      +.coverage table {
      +    border-collapse: collapse;
      +}
      +.coverage td,
      +.coverage th {
      +    border: 1px solid #777;
      +    line-height: 20px;
      +    margin: 0;
      +    padding: 5px 10px;
      +}
      +.coverage td span {
      +    display: inline-block;
      +    width: 100%;
      +}
      +.coverage .content {
      +    padding: 0 5px;
      +}
      +.coverage .content a {
      +    text-decoration: none;
      +}
      +.coverage .count {
      +    margin: 0 5px;
      +    padding: 0 5px;
      +}
      +.coverage .footer,
      +.coverage .header {
      +    padding: 20px;
      +}
      +.coverage .footer {
      +    text-align: center;
      +}
      +.coverage .percentbar {
      +    height: 12px;
      +    margin: 2px 0;
      +    min-width: 200px;
      +    position: relative;
      +    width: 100%;...
      +}
      +
    • +
    • Example usage:
      ...
      +
      +/*jslint node*/
      +import jslint from "../jslint.mjs";
      +(async function () {
      +
      +// Create V8 coverage report from program `npm run test` in javascript.
      +
      +await jslint.v8CoverageReportCreate({
      +    coverageDir: "../.artifact/coverage_sqlite3_js/",
      +    processArgv: [
      +        "--exclude=tes?/",
      +        "--exclude=tes[!0-9A-Z_a-z-]/",
      +        "--exclude=tes[0-9A-Z_a-z-]/",
      +        "--exclude=tes[^0-9A-Z_a-z-]/",
      +        "--exclude=test/**/*.js",
      +...
    • + +
    • +

      + 26. + string jslint_charset_ascii + +

      +
    • + +
    • +

      + 27. + string jslint_edition + +

      +
    • + +
    +
    + +
    + [ + This document was created with + JSLint + ] +
    +
    + + diff --git a/branch-alpha/.artifact/asset_image_logo_128.png b/branch-alpha/.artifact/asset_image_logo_128.png new file mode 100644 index 000000000..cc65bfc4a Binary files /dev/null and b/branch-alpha/.artifact/asset_image_logo_128.png differ diff --git a/branch-alpha/.artifact/asset_image_logo_256.png b/branch-alpha/.artifact/asset_image_logo_256.png new file mode 100644 index 000000000..5939efebe Binary files /dev/null and b/branch-alpha/.artifact/asset_image_logo_256.png differ diff --git a/branch-alpha/.artifact/asset_image_logo_32.png b/branch-alpha/.artifact/asset_image_logo_32.png new file mode 100644 index 000000000..daeb0b3fd Binary files /dev/null and b/branch-alpha/.artifact/asset_image_logo_32.png differ diff --git a/branch-alpha/.artifact/asset_image_logo_512.png b/branch-alpha/.artifact/asset_image_logo_512.png new file mode 100644 index 000000000..585908e3a Binary files /dev/null and b/branch-alpha/.artifact/asset_image_logo_512.png differ diff --git a/branch-alpha/.artifact/asset_image_logo_64.png b/branch-alpha/.artifact/asset_image_logo_64.png new file mode 100644 index 000000000..d9d80e8c3 Binary files /dev/null and b/branch-alpha/.artifact/asset_image_logo_64.png differ diff --git a/branch-alpha/.artifact/coverage/coverage_badge.svg b/branch-alpha/.artifact/coverage/coverage_badge.svg new file mode 100644 index 000000000..5e137a1ce --- /dev/null +++ b/branch-alpha/.artifact/coverage/coverage_badge.svg @@ -0,0 +1,14 @@ + + + + +coverage +100.00 % + + diff --git a/branch-alpha/.artifact/coverage/coverage_report.txt b/branch-alpha/.artifact/coverage/coverage_report.txt new file mode 100644 index 000000000..18fd858a1 --- /dev/null +++ b/branch-alpha/.artifact/coverage/coverage_report.txt @@ -0,0 +1,16 @@ +V8 Coverage Report ++----------------------------------+-------------------+-------------------+ +| Files covered | Lines | Remaining | ++----------------------------------+-------------------+-------------------+ +| ./ | 100.00 % | | +| ******************************** | 13199 / 13199 | 0 / 13199 | ++----------------------------------+-------------------+-------------------+ +| ./jslint.mjs | 100.00 % | | +| ******************************** | 11574 / 11574 | 0 / 11574 | ++----------------------------------+-------------------+-------------------+ +| ./jslint_wrapper_cjs.cjs | 100.00 % | | +| ******************************** | 49 / 49 | 0 / 49 | ++----------------------------------+-------------------+-------------------+ +| ./test.mjs | 100.00 % | | +| ******************************** | 1576 / 1576 | 0 / 1576 | ++----------------------------------+-------------------+-------------------+ diff --git a/branch-alpha/.artifact/coverage/index.html b/branch-alpha/.artifact/coverage/index.html new file mode 100644 index 000000000..38a80ec05 --- /dev/null +++ b/branch-alpha/.artifact/coverage/index.html @@ -0,0 +1,212 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . /
    +
    +
    +
    +
    + 100.00 %
    + 13199 / 13199 +
    +
    + 0 / 13199 +
    + . / jslint.mjs
    +
    +
    +
    +
    + 100.00 %
    + 11574 / 11574 +
    +
    + 0 / 11574 +
    + . / jslint_wrapper_cjs.cjs
    +
    +
    +
    +
    + 100.00 %
    + 49 / 49 +
    +
    + 0 / 49 +
    + . / test.mjs
    +
    +
    +
    +
    + 100.00 %
    + 1576 / 1576 +
    +
    + 0 / 1576 +
    +
    + + + + diff --git a/branch-alpha/.artifact/coverage/jslint.mjs.html b/branch-alpha/.artifact/coverage/jslint.mjs.html new file mode 100644 index 000000000..a140d315f --- /dev/null +++ b/branch-alpha/.artifact/coverage/jslint.mjs.html @@ -0,0 +1,11742 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / jslint.mjs
    +
    +
    +
    +
    + 100.00 %
    + 11574 / 11574 +
    +
    + 0 / 11574 +
    +
    + + +
    +
        1.      1// #!/usr/bin/env node
    +
        2.      1// JSLint
    +
        3.      1
    +
        4.      1// The Unlicense
    +
        5.      1//
    +
        6.      1// This is free and unencumbered software released into the public domain.
    +
        7.      1//
    +
        8.      1// Anyone is free to copy, modify, publish, use, compile, sell, or
    +
        9.      1// distribute this software, either in source code form or as a compiled
    +
       10.      1// binary, for any purpose, commercial or non-commercial, and by any
    +
       11.      1// means.
    +
       12.      1//
    +
       13.      1// In jurisdictions that recognize copyright laws, the author or authors
    +
       14.      1// of this software dedicate any and all copyright interest in the
    +
       15.      1// software to the public domain. We make this dedication for the benefit
    +
       16.      1// of the public at large and to the detriment of our heirs and
    +
       17.      1// successors. We intend this dedication to be an overt act of
    +
       18.      1// relinquishment in perpetuity of all present and future rights to this
    +
       19.      1// software under copyright law.
    +
       20.      1//
    +
       21.      1// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    +
       22.      1// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    +
       23.      1// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    +
       24.      1// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
    +
       25.      1// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
    +
       26.      1// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    +
       27.      1// OTHER DEALINGS IN THE SOFTWARE.
    +
       28.      1
    +
       29.      1
    +
       30.      1// jslint(source, option_dict, global_list) is a function that takes 3
    +
       31.      1// arguments. The second two arguments are optional.
    +
       32.      1
    +
       33.      1//      source          A text to analyze.
    +
       34.      1//      option_dict     An object whose keys correspond to option names.
    +
       35.      1//      global_list     An array of strings containing global variables that
    +
       36.      1//                      the file is allowed readonly access.
    +
       37.      1
    +
       38.      1// jslint returns an object containing its results. The object contains a lot
    +
       39.      1// of valuable information. It can be used to generate reports. The object
    +
       40.      1// contains:
    +
       41.      1
    +
       42.      1//      directives: an array of directive comment tokens.
    +
       43.      1//      edition: the version of JSLint that did the analysis.
    +
       44.      1//      exports: the names exported from the module.
    +
       45.      1//      froms: an array of strings representing each of the imports.
    +
       46.      1//      functions: an array of objects that represent all functions
    +
       47.      1//              declared in the file.
    +
       48.      1//      global: an object representing the global object. Its .context property
    +
       49.      1//              is an object containing a property for each global variable.
    +
       50.      1//      id: "(JSLint)"
    +
       51.      1//      json: true if the file is a JSON text.
    +
       52.      1//      lines: an array of strings, the source.
    +
       53.      1//      module: true if an import or export statement was used.
    +
       54.      1//      ok: true if no warnings were generated. This is what you want.
    +
       55.      1//      option: the option argument.
    +
       56.      1//      property: a property object.
    +
       57.      1//      stop: true if JSLint was unable to finish. You don't want this.
    +
       58.      1//      tokens: an array of objects representing the tokens in the file.
    +
       59.      1//      tree: the token objects arranged in a tree.
    +
       60.      1//      warnings: an array of warning objects. A warning object can contain:
    +
       61.      1//          name: "JSLintError"
    +
       62.      1//          column: A column number in the file.
    +
       63.      1//          line: A line number in the file.
    +
       64.      1//          code: A warning code string.
    +
       65.      1//          message: The warning message string.
    +
       66.      1//          a: Exhibit A.
    +
       67.      1//          b: Exhibit B.
    +
       68.      1//          c: Exhibit C.
    +
       69.      1//          d: Exhibit D.
    +
       70.      1
    +
       71.      1// jslint works in several phases. In any of these phases, errors might be
    +
       72.      1// found. Sometimes JSLint is able to recover from an error and continue
    +
       73.      1// parsing. In some cases, it cannot and will stop early. If that should happen,
    +
       74.      1// repair your code and try again.
    +
       75.      1
    +
       76.      1// Phases:
    +
       77.      1
    +
       78.      1// PHASE 1. Split <source> by newlines into <line_list>.
    +
       79.      1// PHASE 2. Lex <line_list> into <token_list>.
    +
       80.      1// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
    +
       81.      1// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
    +
       82.      1//          recursive traversal. Each node may be processed on the way down
    +
       83.      1//          (preaction) and on the way up (postaction).
    +
       84.      1// PHASE 5. Check whitespace between tokens in <token_list>.
    +
       85.      1
    +
       86.      1// jslint can also examine JSON text. It decides that a file is JSON text if
    +
       87.      1// the first token is "[" or "{". Processing of JSON text is much simpler than
    +
       88.      1// the processing of JavaScript programs. Only the first three phases are
    +
       89.      1// required.
    +
       90.      1
    +
       91.      1// WARNING: JSLint will hurt your feelings.
    +
       92.      1
    +
       93.      1/*jslint beta, node*/
    +
       94.      1/*property
    +
       95.      1    JSLINT_BETA, NODE_V8_COVERAGE, a, all, argv, arity, artifact,
    +
       96.      1    assertErrorThrownAsync, assertJsonEqual, assertOrThrow, assign, async, b,
    +
       97.      1    beta, bitwise, block, body, browser, c, calls, catch, catch_list,
    +
       98.      1    catch_stack, causes, char, children, clear, closer, closure, code, column,
    +
       99.      1    concat, consoleError, console_error, console_log, constant, context,
    +
      100.      1    convert, count, coverageDir, create, cwd, d, dead, debugInline, default,
    +
      101.      1    delta, devel, directive, directive_ignore_line, directive_list, directives,
    +
      102.      1    dirname, disrupt, dot, edition, elem_list, ellipsis, else, end, endOffset,
    +
      103.      1    endsWith, entries, env, error, eval, every, example_list, excludeList, exec,
    +
      104.      1    execArgv, exit, exitCode, export_dict, exports, expression, extra, fart,
    +
      105.      1    file, fileList, fileURLToPath, filter, finally, flag, floor, for, forEach,
    +
      106.      1    formatted_message, free, freeze, from, froms, fsWriteFileWithParents,
    +
      107.      1    fud_stmt, functionName, function_list, function_stack, functions, get,
    +
      108.      1    getset, github_repo, globExclude, global, global_dict, global_list,
    +
      109.      1    holeList, htmlEscape, id, identifier, import, import_list, import_meta_url,
    +
      110.      1    inc, includeList, indent2, index, indexOf, init, initial, isArray,
    +
      111.      1    isBlockCoverage, isHole, isNaN, is_equal, is_weird, join, jslint,
    +
      112.      1    jslint_apidoc, jslint_assert, jslint_charset_ascii, jslint_cli,
    +
      113.      1    jslint_edition, jslint_phase1_split, jslint_phase2_lex, jslint_phase3_parse,
    +
      114.      1    jslint_phase4_walk, jslint_phase5_whitage, jslint_report, json,
    +
      115.      1    jstestDescribe, jstestIt, jstestOnExit, keys, label, lbp, led_infix, length,
    +
      116.      1    level, line, lineList, line_list, line_offset, line_source, lines,
    +
      117.      1    linesCovered, linesTotal, live, log, long, loop, m, map, margin, match, max,
    +
      118.      1    message, meta, min, mkdir, modeCoverageIgnoreFile, modeIndex, mode_cli,
    +
      119.      1    mode_conditional, mode_json, mode_module, mode_noop, mode_property,
    +
      120.      1    mode_shebang, mode_stop, module, moduleFsInit, moduleName, module_list,
    +
      121.      1    name, names, node, nomen, noop, now, nr, nud_prefix,
    +
      122.      1    objectDeepCopyWithKeysSorted, ok, on, open, opening, option, option_dict,
    +
      123.      1    order, package_name, padEnd, padStart, parameters, parent, parentIi, parse,
    +
      124.      1    pathname, pathnameList, platform, pop, processArgv, process_argv,
    +
      125.      1    process_env, process_exit, promises, property, property_dict, push, quote,
    +
      126.      1    ranges, readFile, readdir, readonly, recursive, reduce, repeat, replace,
    +
      127.      1    resolve, result, reverse, role, round, scriptId, search, set, shebang,
    +
      128.      1    shell, shift, signature, single, slice, some, sort, source, spawn, splice,
    +
      129.      1    split, stack, stack_trace, start, startOffset, startsWith, statement,
    +
      130.      1    statement_prv, stdio, stop, stop_at, stringify, subscript, switch,
    +
      131.      1    syntax_dict, tenure, test, test_cause, test_internal_error, this, thru,
    +
      132.      1    toLocaleString, toString, token, token_global, token_list, token_nxt,
    +
      133.      1    token_tree, tokens, trace, tree, trim, trimEnd, trimRight, try, type,
    +
      134.      1    unlink, unordered, unshift, url, used, v8CoverageListMerge,
    +
      135.      1    v8CoverageReportCreate, value, variable, version, versions, warn, warn_at,
    +
      136.      1    warning, warning_list, warnings, white, wrapped, writeFile
    +
      137.      1*/
    +
      138.      1
    +
      139.      1// init debugInline
    +
      140.      1let debugInline = (function () {
    +
      141.      3    let __consoleError = function () {
    +
      142.      3        return;
    +
      143.      3    };
    +
      144.      1    function debug(...argv) {
    +
      145.      1
    +
      146.      1// This function will print <argv> to stderr and then return <argv>[0].
    +
      147.      1
    +
      148.      1        __consoleError("\n\ndebugInline");
    +
      149.      1        __consoleError(...argv);
    +
      150.      1        __consoleError("\n");
    +
      151.      1        return argv[0];
    +
      152.      1    }
    +
      153.      1    debug(); // Coverage-hack.
    +
      154.      1    __consoleError = console.error; //jslint-ignore-line
    +
      155.      1    return debug;
    +
      156.      1}());
    +
      157.      1let jslint_charset_ascii = (
    +
      158.      1    "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007"
    +
      159.      1    + "\b\t\n\u000b\f\r\u000e\u000f"
    +
      160.      1    + "\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017"
    +
      161.      1    + "\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f"
    +
      162.      1    + " !\"#$%&'()*+,-./0123456789:;<=>?"
    +
      163.      1    + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
    +
      164.      1    + "`abcdefghijklmnopqrstuvwxyz{|}~\u007f"
    +
      165.      1);
    +
      166.      1let jslint_edition = "v2024.11.24";
    +
      167.      1let jslint_export;                      // The jslint object to be exported.
    +
      168.      1let jslint_fudge = 1;                   // Fudge starting line and starting
    +
      169.      1                                        // ... column to 1.
    +
      170.      1let jslint_import_meta_url = "";        // import.meta.url used by cli.
    +
      171.      1let jslint_rgx_cap = (
    +
      172.      1    /^[A-Z]/
    +
      173.      1);
    +
      174.      1let jslint_rgx_crlf = (
    +
      175.      1    /\n|\r\n?/
    +
      176.      1);
    +
      177.      1let jslint_rgx_digits_bits = (
    +
      178.      1    /^[01_]*/
    +
      179.      1);
    +
      180.      1let jslint_rgx_digits_decimals = (
    +
      181.      1    /^[0-9_]*/
    +
      182.      1);
    +
      183.      1let jslint_rgx_digits_hexs = (
    +
      184.      1    /^[0-9A-F_]*/i
    +
      185.      1);
    +
      186.      1let jslint_rgx_digits_octals = (
    +
      187.      1    /^[0-7_]*/
    +
      188.      1);
    +
      189.      1let jslint_rgx_directive = (
    +
      190.      1    /^(jslint|property|global)\s+(.*)$/
    +
      191.      1);
    +
      192.      1let jslint_rgx_directive_part = (
    +
      193.      1    /([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*|$/g
    +
      194.      1);
    +
      195.      1let jslint_rgx_identifier = (
    +
      196.      1    /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/
    +
      197.      1);
    +
      198.      1let jslint_rgx_json_number = (
    +
      199.      1
    +
      200.      1// https://datatracker.ietf.org/doc/html/rfc7159#section-6
    +
      201.      1// number = [ minus ] int [ frac ] [ exp ]
    +
      202.      1
    +
      203.      1    /^-?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][\-+]?\d+)?$/
    +
      204.      1);
    +
      205.      1let jslint_rgx_mega = (
    +
      206.      1
    +
      207.      1// Vim-hack - vim-editor has trouble parsing naked '`' in regexp
    +
      208.      1
    +
      209.      1    /[\u0060\\]|\$\{/
    +
      210.      1);
    +
      211.      1let jslint_rgx_module = (
    +
      212.      1    /^[a-zA-Z0-9_$:.@\-\/]+$/
    +
      213.      1);
    +
      214.      1let jslint_rgx_numeric_separator_illegal = (
    +
      215.      1    /__|_$|_n$/m
    +
      216.      1);
    +
      217.      1let jslint_rgx_slash_star_or_slash = (
    +
      218.      1    /\/\*|\/$/
    +
      219.      1);
    +
      220.      1let jslint_rgx_tab = (
    +
      221.      1    /\t/g
    +
      222.      1);
    +
      223.      1let jslint_rgx_todo = (
    +
      224.      1    /\b(?:todo|TO\s?DO|HACK)\b/
    +
      225.      1);
    +
      226.      1let jslint_rgx_token = new RegExp(
    +
      227.      1    "^("
    +
      228.      1    + "(\\s+)"
    +
      229.      1    + "|([a-zA-Z_$][a-zA-Z0-9_$]*)"
    +
      230.      1    + "|[(){}\\[\\],:;'\"~\\`]"
    +
      231.      1    + "|\\?[?.]?"
    +
      232.      1    + "|=(?:==?|>)?"
    +
      233.      1    + "|\\.+"
    +
      234.      1    + "|\\*[*\\/=]?"
    +
      235.      1    + "|\\/[*\\/]?"
    +
      236.      1    + "|\\+[=+]?"
    +
      237.      1    + "|-[=\\-]?"
    +
      238.      1    + "|[\\^%]=?"
    +
      239.      1    + "|&[&=]?"
    +
      240.      1    + "|\\"
    +
      241.      1    + "|[|=]?"
    +
      242.      1    + "|>{1,3}=?"
    +
      243.      1    + "|<<?=?"
    +
      244.      1    + "|!(?:!|==?)?"
    +
      245.      1
    +
      246.      1// PR-351 - Add BigInt support.
    +
      247.      1// PR-390 - Add numeric-separator support.
    +
      248.      1
    +
      249.      1    + "|((?:0_?|[1-9][0-9_]*)n?)"
    +
      250.      1    + ")"
    +
      251.      1    + "(.*)$"
    +
      252.      1);
    +
      253.      1let jslint_rgx_url_search_window_jslint = (
    +
      254.      1    /[&?]window_jslint=1(?:$|&)/m
    +
      255.      1);
    +
      256.      1let jslint_rgx_weird_property = (
    +
      257.      1    /^_|\$|Sync$|_$/m
    +
      258.      1);
    +
      259.      1let jstestCountFailed = 0;
    +
      260.      1let jstestCountTotal = 0;
    +
      261.      1let jstestItCount = 0;
    +
      262.      1let jstestItList = [];
    +
      263.      1let jstestTimeStart;
    +
      264.      1let moduleChildProcess;
    +
      265.      1let moduleFs;
    +
      266.      1let moduleFsInitResolveList;
    +
      267.      1let modulePath;
    +
      268.      1let moduleUrl;
    +
      269.      1
    +
      270.     11async function assertErrorThrownAsync(asyncFunc, regexp) {
    +
      271.     11
    +
      272.     11// This function will assert calling <asyncFunc> throws an error.
    +
      273.     11
    +
      274.     11    let err;
    +
      275.     11    try {
    +
      276.      1        await asyncFunc();
    +
      277.     10    } catch (errCaught) {
    +
      278.     10        err = errCaught;
    +
      279.     10    }
    +
      280.     11    assertOrThrow(err, "No error thrown.");
    +
      281.     11    assertOrThrow(
    +
      282.      4        !regexp || new RegExp(regexp).test(err.message),
    +
      283.     11        err
    +
      284.     11    );
    +
      285.     11}
    +
      286.      1
    +
      287.    267function assertJsonEqual(aa, bb, message) {
    +
      288.    267
    +
      289.    267// This function will assert JSON.stringify(<aa>) === JSON.stringify(<bb>).
    +
      290.    267
    +
      291.    267    aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa), undefined, 1);
    +
      292.    267    bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb), undefined, 1);
    +
      293.      3    if (aa !== bb) {
    +
      294.      3        throw new Error(
    +
      295.      3            "\n" + aa + "\n!==\n" + bb
    +
      296.      3            + (
    +
      297.      3                typeof message === "string"
    +
      298.      3                ? " - " + message
    +
      299.      3                : message
    +
      300.      3                ? " - " + JSON.stringify(message)
    +
      301.      3                : ""
    +
      302.      3            )
    +
      303.      3        );
    +
      304.      3    }
    +
      305.    267}
    +
      306.      1
    +
      307.   1931function assertOrThrow(condition, message) {
    +
      308.   1931
    +
      309.   1931// This function will throw <message> if <condition> is falsy.
    +
      310.   1931
    +
      311.      4    if (!condition) {
    +
      312.      4        throw (
    +
      313.      4            (!message || typeof message === "string")
    +
      314.      4            ? new Error(String(message).slice(0, 2048))
    +
      315.      4            : message
    +
      316.      4        );
    +
      317.      4    }
    +
      318.   1931}
    +
      319.      1
    +
      320.  94133function empty() {
    +
      321.  94133
    +
      322.  94133// The empty function produces a new empty object that inherits nothing. This is
    +
      323.  94133// much better than '{}' because confusions around accidental method names like
    +
      324.  94133// 'constructor' are completely avoided.
    +
      325.  94133
    +
      326.  94133    return Object.create(null);
    +
      327.  94133}
    +
      328.      1
    +
      329.     59async function fsWriteFileWithParents(pathname, data) {
    +
      330.     59
    +
      331.     59// This function will write <data> to <pathname> and lazy-mkdirp if necessary.
    +
      332.     59
    +
      333.     59    await moduleFsInit();
    +
      334.     59
    +
      335.     59// Try writing to pathname.
    +
      336.     59
    +
      337.     59    try {
    +
      338.     41        await moduleFs.promises.writeFile(pathname, data);
    +
      339.     41    } catch (ignore) {
    +
      340.     18
    +
      341.     18// Lazy mkdirp.
    +
      342.     18
    +
      343.     18        await moduleFs.promises.mkdir(modulePath.dirname(pathname), {
    +
      344.     18            recursive: true
    +
      345.     18        });
    +
      346.     18
    +
      347.     18// Retry writing to pathname.
    +
      348.     18
    +
      349.     18        await moduleFs.promises.writeFile(pathname, data);
    +
      350.     18    }
    +
      351.     59    console.error("wrote file " + pathname);
    +
      352.     59}
    +
      353.      1
    +
      354.    184function globExclude({
    +
      355.    184    excludeList = [],
    +
      356.    184    includeList = [],
    +
      357.    184    pathnameList = []
    +
      358.    184}) {
    +
      359.    184
    +
      360.    184// This function will
    +
      361.    184// 1. Exclude pathnames in <pathnameList> that don't match glob-patterns in
    +
      362.    184//    <includeList>.
    +
      363.    184// 2. Exclude pathnames in <pathnameList> that match glob-patterns in
    +
      364.    184//    <excludeList>.
    +
      365.    184
    +
      366.    552    function globAssertNotWeird(list, name) {
    +
      367.    552
    +
      368.    552// This function will check if <list> of strings contain weird characters.
    +
      369.    552
    +
      370.    552        [
    +
      371.    552            [
    +
      372.    552                "\n", (
    +
      373.    552                    /^.*?([\u0000-\u0007\r]).*/gm
    +
      374.    552                )
    +
      375.    552            ],
    +
      376.    552            [
    +
      377.    552                "\r", (
    +
      378.    552                    /^.*?([\n]).*/gm
    +
      379.    552                )
    +
      380.    552            ]
    +
      381.   1102        ].forEach(function ([
    +
      382.   1102            separator, rgx
    +
      383.   1102        ]) {
    +
      384.      3            list.join(separator).replace(rgx, function (match0, char) {
    +
      385.      3                throw new Error(
    +
      386.      3                    "Weird character "
    +
      387.      3                    + JSON.stringify(char)
    +
      388.      3                    + " found in " + name + " "
    +
      389.      3                    + JSON.stringify(match0)
    +
      390.      3                );
    +
      391.      3            });
    +
      392.   1102        });
    +
      393.    552    }
    +
      394.    184
    +
      395.   1370    function globToRegexp(pattern) {
    +
      396.   1370
    +
      397.   1370// This function will translate glob <pattern> to javascript-regexp,
    +
      398.   1370// which javascript can then use to "glob" pathnames.
    +
      399.   1370
    +
      400.   1370        let ii = 0;
    +
      401.   1370        let isClass = false;
    +
      402.   1370        let strClass = "";
    +
      403.   1370        let strRegex = "";
    +
      404.   1370        pattern = pattern.replace((
    +
      405.   1370            /\/\/+/g
    +
      406.   1370        ), "/");
    +
      407.   1370        pattern = pattern.replace((
    +
      408.   1370            /\*\*\*+/g
    +
      409.   1370        ), "**");
    +
      410.   1370        pattern.replace((
    +
      411.   1370            /\\\\|\\\[|\\\]|\[|\]|./g
    +
      412.  18691        ), function (match0) {
    +
      413.  18691            switch (match0) {
    +
      414.    310            case "[":
    +
      415.    310                if (isClass) {
    +
      416.    310                    strClass += "[";
    +
      417.    310                    return;
    +
      418.    310                }
    +
      419.    310                strClass += "\u0000";
    +
      420.    310                strRegex += "\u0000";
    +
      421.    310                isClass = true;
    +
      422.    310                return;
    +
      423.    310            case "]":
    +
      424.    310                if (isClass) {
    +
      425.    310                    isClass = false;
    +
      426.    310                    return;
    +
      427.    310                }
    +
      428.    310                strRegex += "]";
    +
      429.    310                return;
    +
      430.  18071            default:
    +
      431.  18071                if (isClass) {
    +
      432.  18071                    strClass += match0;
    +
      433.  18071                    return;
    +
      434.  18071                }
    +
      435.  18071                strRegex += match0;
    +
      436.  15021            }
    +
      437.  15021            return "";
    +
      438.  15021        });
    +
      439.   1370        strClass += "\u0000";
    +
      440.   1370
    +
      441.   1370// An expression "[!...]" matches a single character, namely any character that
    +
      442.   1370// is not matched by the expression obtained by removing the first '!' from it.
    +
      443.   1370// (Thus, "[!a-]" matches any single character except 'a', and '-'.)
    +
      444.   1370
    +
      445.   1370        strClass = strClass.replace((
    +
      446.   1370            /\u0000!/g
    +
      447.   1370        ), "\u0000^");
    +
      448.   1370
    +
      449.   1370// One may include '-' in its literal meaning by making it the first or last
    +
      450.   1370// character between the brackets.
    +
      451.   1370
    +
      452.   1370        strClass = strClass.replace((
    +
      453.   1370            /\u0000-/g
    +
      454.   1370        ), "\u0000\\-");
    +
      455.   1370        strClass = strClass.replace((
    +
      456.   1370            /-\u0000/g
    +
      457.   1370        ), "\\-\u0000");
    +
      458.   1370
    +
      459.   1370// Escape brackets '[', ']' in character class.
    +
      460.   1370
    +
      461.   1370        strClass = strClass.replace((
    +
      462.   1370            /[\[\]]/g
    +
      463.   1370        ), "\\$&");
    +
      464.   1370
    +
      465.   1370// https://stackoverflow.com/questions/3561493
    +
      466.   1370// /is-there-a-regexp-escape-function-in-javascript
    +
      467.   1370// $()*+-./?[\]^{|}
    +
      468.   1370
    +
      469.   1370        strRegex = strRegex.replace((
    +
      470.   1370
    +
      471.   1370// Ignore [-/].
    +
      472.   1370
    +
      473.   1370            /[$()*+.?\[\\\]\^{|}]/g
    +
      474.   1370        ), "\\$&");
    +
      475.   1370
    +
      476.   1370// Expand wildcard '**/*'.
    +
      477.   1370
    +
      478.   1370        strRegex = strRegex.replace((
    +
      479.   1370            /\\\*\\\*\/(?:\\\*)+/g
    +
      480.   1370        ), ".*?");
    +
      481.   1370
    +
      482.   1370// Expand wildcard '**'.
    +
      483.   1370
    +
      484.   1370        strRegex = strRegex.replace((
    +
      485.   1370            /(^|\/)\\\*\\\*(\/|$)/gm
    +
      486.   1370        ), "$1.*?$2");
    +
      487.   1370
    +
      488.   1370// Expand wildcard '*'.
    +
      489.   1370
    +
      490.   1370        strRegex = strRegex.replace((
    +
      491.   1370            /(?:\\\*)+/g
    +
      492.   1370        ), "[^\\/]*?");
    +
      493.   1370
    +
      494.   1370// Expand wildcard '?'.
    +
      495.   1370
    +
      496.   1370        strRegex = strRegex.replace((
    +
      497.   1370            /\\\?/g
    +
      498.   1370        ), "[^\\/]");
    +
      499.   1370
    +
      500.   1370// Expand directory-with-trailing-slash '.../'.
    +
      501.   1370
    +
      502.   1370        strRegex = strRegex.replace((
    +
      503.   1370            /\/$/gm
    +
      504.   1370        ), "\\/.*?");
    +
      505.   1370
    +
      506.   1370// Merge strClass into strRegex.
    +
      507.   1370
    +
      508.   1370        ii = 0;
    +
      509.   1370        strClass = strClass.split("\u0000");
    +
      510.   1370        strRegex = strRegex.replace((
    +
      511.   1370            /\u0000/g
    +
      512.    306        ), function () {
    +
      513.    306            ii += 1;
    +
      514.      2            if (strClass[ii] === "") {
    +
      515.      2                return "";
    +
      516.    304            }
    +
      517.    304            return "[" + strClass[ii] + "]";
    +
      518.    304        });
    +
      519.   1370
    +
      520.   1370// Change strRegex from string to regexp.
    +
      521.   1370
    +
      522.   1370        strRegex = new RegExp("^" + strRegex + "$", "gm");
    +
      523.   1370        return strRegex;
    +
      524.   1370    }
    +
      525.    184
    +
      526.    184// Validate excludeList, includeList, pathnameList.
    +
      527.    184
    +
      528.    184    globAssertNotWeird(excludeList, "pattern");
    +
      529.    184    globAssertNotWeird(includeList, "pattern");
    +
      530.    184    globAssertNotWeird(pathnameList, "pathname");
    +
      531.    184
    +
      532.    184// Optimization
    +
      533.    184// Concat pathnames into a single, newline-separated string,
    +
      534.    184// whose pathnames can all be filtered with a single, regexp-pass.
    +
      535.    184
    +
      536.    184    pathnameList = pathnameList.join("\n");
    +
      537.    184
    +
      538.    184// 1. Exclude pathnames in <pathnameList> that don't match glob-patterns in
    +
      539.    184//    <includeList>.
    +
      540.    184
    +
      541.    142    if (includeList.length > 0) {
    +
      542.    142        includeList = includeList.map(globToRegexp);
    +
      543.    574        includeList.forEach(function (pattern) {
    +
      544.    574            pathnameList = pathnameList.replace(pattern, "\u0000$&");
    +
      545.    574        });
    +
      546.    142        pathnameList = pathnameList.replace((
    +
      547.    142            /^[^\u0000].*/gm
    +
      548.    142        ), "");
    +
      549.    142        pathnameList = pathnameList.replace((
    +
      550.    142            /^\u0000+/gm
    +
      551.    142        ), "");
    +
      552.    181    }
    +
      553.    181
    +
      554.    181// 2. Exclude pathnames in <pathnameList> that match glob-patterns in
    +
      555.    181//    <excludeList>.
    +
      556.    181
    +
      557.    181    excludeList = excludeList.map(globToRegexp);
    +
      558.    796    excludeList.forEach(function (pattern) {
    +
      559.    796        pathnameList = pathnameList.replace(pattern, "");
    +
      560.    796    });
    +
      561.    181
    +
      562.    181// Split newline-separated pathnames back to list.
    +
      563.    181
    +
      564.  10117    pathnameList = pathnameList.split("\n").filter(function (elem) {
    +
      565.  10117        return elem;
    +
      566.  10117    });
    +
      567.    181    return {
    +
      568.    181        excludeList,
    +
      569.    181        includeList,
    +
      570.    181        pathnameList
    +
      571.    181    };
    +
      572.    181}
    +
      573.      1
    +
      574.  14214function htmlEscape(str) {
    +
      575.  14214
    +
      576.  14214// This function will make <str> html-safe by escaping & < >.
    +
      577.  14214
    +
      578.  14214    return String(str).replace((
    +
      579.  14214        /&/g
    +
      580.  14214    ), "&amp;").replace((
    +
      581.  14214        /</g
    +
      582.  14214    ), "&lt;").replace((
    +
      583.  14214        />/g
    +
      584.  14214    ), "&gt;");
    +
      585.  14214}
    +
      586.      1
    +
      587.    668function jslint(
    +
      588.    668    source = "",                // A text to analyze.
    +
      589.    668    option_dict = empty(),      // An object whose keys correspond to option
    +
      590.    668                                // ... names.
    +
      591.    668    global_list = []            // An array of strings containing global
    +
      592.    668                                // ... variables that the file is allowed
    +
      593.    668                                // ... readonly access.
    +
      594.    668) {
    +
      595.    668
    +
      596.    668// The jslint function itself.
    +
      597.    668
    +
      598.    668    let catch_list = [];        // The array containing all catch-blocks.
    +
      599.    668    let catch_stack = [         // The stack of catch-blocks.
    +
      600.    668        {
    +
      601.    668            context: empty()
    +
      602.    668        }
    +
      603.    668    ];
    +
      604.    668    let cause_dict = empty();   // The object of test-causes.
    +
      605.    668    let directive_list = [];    // The directive comments.
    +
      606.    668    let export_dict = empty();  // The exported names and values.
    +
      607.    668    let function_list = [];     // The array containing all functions.
    +
      608.    668    let function_stack = [];    // The stack of functions.
    +
      609.    668    let global_dict = empty();  // The object containing the global
    +
      610.    668                                // ... declarations.
    +
      611.    668    let import_list = [];       // The array collecting all import-from strings.
    +
      612.    668    let line_list = String(     // The array containing source lines.
    +
      613.    668        "\n" + source
    +
      614. 105217    ).split(jslint_rgx_crlf).map(function (line_source) {
    +
      615. 105217        return {
    +
      616. 105217            line_source
    +
      617. 105217        };
    +
      618. 105217    });
    +
      619.    668    let mode_stop = false;      // true if JSLint cannot finish.
    +
      620.    668    let property_dict = empty();        // The object containing the tallied
    +
      621.    668                                        // ... property names.
    +
      622.    668    let state = empty();        // jslint state-object to be passed between
    +
      623.    668                                // jslint functions.
    +
      624.    668    let syntax_dict = empty();  // The object containing the parser.
    +
      625.    668    let tenure = empty();       // The predefined property registry.
    +
      626.    668    let token_global = {        // The global object; the outermost context.
    +
      627.    668        async: 0,
    +
      628.    668        body: true,
    +
      629.    668        context: empty(),
    +
      630.    668        finally: 0,
    +
      631.    668        from: 0,
    +
      632.    668        id: "(global)",
    +
      633.    668        level: 0,
    +
      634.    668        line: jslint_fudge,
    +
      635.    668        live: [],
    +
      636.    668        loop: 0,
    +
      637.    668        switch: 0,
    +
      638.    668        thru: 0,
    +
      639.    668        try: 0
    +
      640.    668    };
    +
      641.    668    let token_list = [];        // The array of tokens.
    +
      642.    668    let warning_list = [];      // The array collecting all generated warnings.
    +
      643.    668
    +
      644.    668// Error reportage functions:
    +
      645.    668
    +
      646.   8041    function artifact(the_token) {
    +
      647.   8041
    +
      648.   8041// Return a string representing an artifact.
    +
      649.   8041
    +
      650.    256        the_token = the_token || state.token_nxt;
    +
      651.   8041        return (
    +
      652.   5266            (the_token.id === "(string)" || the_token.id === "(number)")
    +
      653.   2900            ? String(the_token.value)
    +
      654.   5141            : the_token.id
    +
      655.   8041        );
    +
      656.   8041    }
    +
      657.    668
    +
      658.  31957    function is_equal(aa, bb) {
    +
      659.  31957
    +
      660.  31957// test_cause:
    +
      661.  31957// ["0&&0", "is_equal", "", "", 0]
    +
      662.  31957
    +
      663.  31957        test_cause("");
    +
      664.  31957
    +
      665.  31957// Probably deadcode.
    +
      666.  31957// if (aa === bb) {
    +
      667.  31957//     return true;
    +
      668.  31957// }
    +
      669.  31957
    +
      670.  31957        jslint_assert(!(aa === bb), `Expected !(aa === bb).`);
    +
      671.     27        if (Array.isArray(aa)) {
    +
      672.     27            return (
    +
      673.     27                Array.isArray(bb)
    +
      674.     27                && aa.length === bb.length
    +
      675.     27                && aa.every(function (value, index) {
    +
      676.     27
    +
      677.     27// test_cause:
    +
      678.     27// ["`${0}`&&`${0}`", "is_equal", "recurse_isArray", "", 0]
    +
      679.     27// ["`${0}`&&`${1}`", "is_equal", "recurse_isArray", "", 0]
    +
      680.     27
    +
      681.     27                    test_cause("recurse_isArray");
    +
      682.     27                    return is_equal(value, bb[index]);
    +
      683.     27                })
    +
      684.     27            );
    +
      685.  31930        }
    +
      686.  31930
    +
      687.  31930// Probably deadcode.
    +
      688.  31930// if (Array.isArray(bb)) {
    +
      689.  31930//     return false;
    +
      690.  31930// }
    +
      691.  31930
    +
      692.  31930        jslint_assert(!Array.isArray(bb), `Expected !Array.isArray(bb).`);
    +
      693.  31930        switch (aa.id === bb.id && aa.id) {
    +
      694.     65        case "(number)":
    +
      695.  23429        case "(string)":
    +
      696.  23429            return aa.value === bb.value;
    +
      697.  31957
    +
      698.  31957// PR-394 - Bugfix
    +
      699.  31957// Fix jslint falsely believing megastring literals `0` and `1` are similar.
    +
      700.  31957
    +
      701.     15        case "`":
    +
      702.     15            if (!is_equal(aa.value, bb.value)) {
    +
      703.     15                return false;
    +
      704.     15            }
    +
      705.     15            break;
    +
      706.   8498        }
    +
      707.   8498        if (is_weird(aa) || is_weird(bb)) {
    +
      708.     34
    +
      709.     34// test_cause:
    +
      710.     34// ["aa(/./)||{}", "is_equal", "false", "", 0]
    +
      711.     34
    +
      712.     34            test_cause("false");
    +
      713.     34            return false;
    +
      714.   8464        }
    +
      715.   8464        if (aa.arity === bb.arity && aa.id === bb.id) {
    +
      716.   2147            if (aa.id === "." || aa.id === "?.") {
    +
      717.   2147
    +
      718.   2147// test_cause:
    +
      719.   2147// ["aa.bb&&aa.bb", "is_equal", "recurse_arity_id", "", 0]
    +
      720.   2147// ["aa?.bb&&aa?.bb", "is_equal", "recurse_arity_id", "", 0]
    +
      721.   2147
    +
      722.   2147                test_cause("recurse_arity_id");
    +
      723.   2147                return (
    +
      724.   2147                    is_equal(aa.expression, bb.expression)
    +
      725.   2147                    && is_equal(aa.name, bb.name)
    +
      726.   2147                );
    +
      727.   2147            }
    +
      728.   2147            if (aa.arity === "unary") {
    +
      729.   2147
    +
      730.   2147// test_cause:
    +
      731.   2147// ["+0&&+0", "is_equal", "recurse_unary", "", 0]
    +
      732.   2147
    +
      733.   2147                test_cause("recurse_unary");
    +
      734.   2147                return is_equal(aa.expression, bb.expression);
    +
      735.   2147            }
    +
      736.   2147            if (aa.arity === "binary") {
    +
      737.   2147
    +
      738.   2147// test_cause:
    +
      739.   2147// ["aa[0]&&aa[0]", "is_equal", "recurse_binary", "", 0]
    +
      740.   2147
    +
      741.   2147                test_cause("recurse_binary");
    +
      742.   2147                return (
    +
      743.   2147                    aa.id !== "("
    +
      744.   2147                    && is_equal(aa.expression[0], bb.expression[0])
    +
      745.   2147                    && is_equal(aa.expression[1], bb.expression[1])
    +
      746.   2147                );
    +
      747.   2147            }
    +
      748.   2147            if (aa.arity === "ternary") {
    +
      749.   2147
    +
      750.   2147// test_cause:
    +
      751.   2147// ["aa=(``?``:``)&&(``?``:``)", "is_equal", "recurse_ternary", "", 0]
    +
      752.   2147
    +
      753.   2147                test_cause("recurse_ternary");
    +
      754.   2147                return (
    +
      755.   2147                    is_equal(aa.expression[0], bb.expression[0])
    +
      756.   2147                    && is_equal(aa.expression[1], bb.expression[1])
    +
      757.   2147                    && is_equal(aa.expression[2], bb.expression[2])
    +
      758.   2147                );
    +
      759.   2147            }
    +
      760.   2147
    +
      761.   2147// Probably deadcode.
    +
      762.   2147// if (aa.arity === "function" || aa.arity === "regexp") {
    +
      763.   2147//     return false;
    +
      764.   2147// }
    +
      765.   2147
    +
      766.   2147            jslint_assert(
    +
      767.   2147                !(aa.arity === "function" || aa.arity === "regexp"),
    +
      768.   2147                `Expected !(aa.arity === "function" || aa.arity === "regexp").`
    +
      769.   2147            );
    +
      770.   2147
    +
      771.   2147// test_cause:
    +
      772.   2147// ["undefined&&undefined", "is_equal", "true", "", 0]
    +
      773.   2147
    +
      774.   2147            test_cause("true");
    +
      775.   2147            return true;
    +
      776.   6317        }
    +
      777.   6317
    +
      778.   6317// test_cause:
    +
      779.   6317// ["null&&undefined", "is_equal", "false", "", 0]
    +
      780.   6317
    +
      781.   6317        test_cause("false");
    +
      782.   6317        return false;
    +
      783.   6317    }
    +
      784.    668
    +
      785.  28763    function is_weird(thing) {
    +
      786.  28763        switch (thing.id) {
    +
      787.      1        case "(regexp)":
    +
      788.      1            return true;
    +
      789.      1        case "=>":
    +
      790.      1            return true;
    +
      791.    593        case "[":
    +
      792.    593            return thing.arity === "unary";
    +
      793.     12        case "function":
    +
      794.     12            return true;
    +
      795.      8        case "{":
    +
      796.      8            return true;
    +
      797.  28148        default:
    +
      798.  28148            return false;
    +
      799.  28763        }
    +
      800.  28763    }
    +
      801.    668
    +
      802.    106    function stop(code, the_token, a, b, c, d) {
    +
      803.    106
    +
      804.    106// Similar to warn and stop_at. If the token already had a warning, that
    +
      805.    106// warning will be replaced with this new one. It is likely that the stopping
    +
      806.    106// warning will be the more meaningful.
    +
      807.    106
    +
      808.     38        the_token = the_token || state.token_nxt;
    +
      809.    106        delete the_token.warning;
    +
      810.    106        throw warn(code, the_token, a, b, c, d);
    +
      811.    106    }
    +
      812.    668
    +
      813.     28    function stop_at(code, line, column, a, b, c, d) {
    +
      814.     28
    +
      815.     28// Same as warn_at, except that it stops the analysis.
    +
      816.     28
    +
      817.     28        throw warn_at(code, line, column, a, b, c, d);
    +
      818.     28    }
    +
      819.    668
    +
      820. 339547    function test_cause(code, aa, column) {
    +
      821. 339547
    +
      822. 339547// This function will instrument <cause> to <cause_dict> for test-purposes.
    +
      823. 339547
    +
      824.   4882        if (option_dict.test_cause) {
    +
      825.   4882            cause_dict[JSON.stringify([
    +
      826.   4882                String(new Error().stack).replace((
    +
      827.   4882                    /^    at (?:file|stop|stop_at|test_cause|warn|warn_at)\b.*?\n/gm
    +
      828.   4882                ), "").match(
    +
      829.   4882                    /\n    at ((?:Object\.\w+?_)?\w+?) /
    +
      830.   4882                )[1].replace((
    +
      831.   4882                    /^Object\./
    +
      832.   4882                ), ""),
    +
      833.   4882                code,
    +
      834.   4882                String(
    +
      835.   4882                    (aa === undefined || aa === token_global)
    +
      836.   4882                    ? ""
    +
      837.   4882                    : aa
    +
      838.   4882                ),
    +
      839.   4882                column || 0
    +
      840.   4882            ])] = true;
    +
      841.   4882        }
    +
      842. 339547    }
    +
      843.    668
    +
      844.   1075    function warn(code, the_token, a, b, c, d) {
    +
      845.   1075
    +
      846.   1075// Same as warn_at, except the warning will be associated with a specific token.
    +
      847.   1075// If there is already a warning on this token, suppress the new one. It is
    +
      848.   1075// likely that the first warning will be the most meaningful.
    +
      849.   1075
    +
      850.   1075        let the_warning;
    +
      851.     20        the_token = the_token || state.token_nxt;
    +
      852.   1075        the_warning = warn_at(
    +
      853.   1075            code,
    +
      854.   1075            the_token.line,
    +
      855.    376            (the_token.from || 0) + jslint_fudge,
    +
      856.    835            a || artifact(the_token),
    +
      857.   1075            b,
    +
      858.   1075            c,
    +
      859.   1075            d
    +
      860.   1075        );
    +
      861.   1075
    +
      862.   1075// Issue #408
    +
      863.   1075// Warnings that should be ignored sometimes suppress legitimate warnings.
    +
      864.   1075
    +
      865.     26        if (the_warning.directive_ignore_line) {
    +
      866.     26            return the_warning;
    +
      867.   1049        }
    +
      868.   1049
    +
      869.   1049// If there is already a warning on this token, suppress the new one. It is
    +
      870.   1049// likely that the first warning will be the most meaningful.
    +
      871.   1049
    +
      872.   1049        if (the_token.warning) {
    +
      873.    192            warning_list.pop();
    +
      874.    192            return the_warning;
    +
      875.    857        }
    +
      876.    857        the_token.warning = the_warning;
    +
      877.    857        return the_warning;
    +
      878.    857    }
    +
      879.    668
    +
      880.   1394    function warn_at(code, line, column, a, b, c, d) {
    +
      881.   1394
    +
      882.   1394// Report an error at some line and column of the program. The warning object
    +
      883.   1394// resembles an exception.
    +
      884.   1394
    +
      885.   1394        let mm;
    +
      886.   1394        let warning = Object.assign(empty(), {
    +
      887.   1394            a,
    +
      888.   1394            b,
    +
      889.   1394            c,
    +
      890.   1394            code,
    +
      891.   1394
    +
      892.   1394// Fudge column numbers in warning message.
    +
      893.   1394
    +
      894.     27            column: column || jslint_fudge,
    +
      895.   1394            d,
    +
      896.   1394            line,
    +
      897.   1394            line_source: "",
    +
      898.   1394            name: "JSLintError"
    +
      899.   1394        }, line_list[line]);
    +
      900.   1394        warning.column = Math.max(
    +
      901.   1394            Math.min(warning.column, warning.line_source.length),
    +
      902.   1394            jslint_fudge
    +
      903.   1394        );
    +
      904.    926        test_cause(code, b || a, warning.column);
    +
      905.   1394        switch (code) {
    +
      906.   1394
    +
      907.   1394// The bundle contains the raw text messages that are generated by jslint. It
    +
      908.   1394// seems that they are all error messages and warnings. There are no "Atta
    +
      909.   1394// boy!" or "You are so awesome!" messages. There is no positive reinforcement
    +
      910.   1394// or encouragement. This relentless negativity can undermine self-esteem and
    +
      911.   1394// wound the inner child. But if you accept it as sound advice rather than as
    +
      912.   1394// personal criticism, it can make your programs better.
    +
      913.   1394
    +
      914.      1        case "and":
    +
      915.      1            mm = `The '&&' subexpression should be wrapped in parens.`;
    +
      916.      1            break;
    +
      917.     71        case "bad_assignment_a":
    +
      918.     71            mm = `Bad assignment to '${a}'.`;
    +
      919.     71            break;
    +
      920.      1        case "bad_directive_a":
    +
      921.      1            mm = `Bad directive '${a}'.`;
    +
      922.      1            break;
    +
      923.      1        case "bad_get":
    +
      924.      1            mm = `A get function takes no parameters.`;
    +
      925.      1            break;
    +
      926.      1        case "bad_module_name_a":
    +
      927.      1            mm = `Bad module name '${a}'.`;
    +
      928.      1            break;
    +
      929.      2        case "bad_option_a":
    +
      930.      2            mm = `Bad option '${a}'.`;
    +
      931.      2            break;
    +
      932.      1        case "bad_set":
    +
      933.      1            mm = `A set function takes one parameter.`;
    +
      934.      1            break;
    +
      935.      6        case "duplicate_a":
    +
      936.      6            mm = `Duplicate '${a}'.`;
    +
      937.      6            break;
    +
      938.     64        case "empty_block":
    +
      939.     64            mm = `Empty block.`;
    +
      940.     64            break;
    +
      941.      5        case "expected_a":
    +
      942.      5            mm = `Expected '${a}'.`;
    +
      943.      5            break;
    +
      944.     25        case "expected_a_at_b_c":
    +
      945.     25            mm = `Expected '${a}' at column ${b}, not column ${c}.`;
    +
      946.     25            break;
    +
      947.    286        case "expected_a_b":
    +
      948.    286            mm = `Expected '${a}' and instead saw '${b}'.`;
    +
      949.    286            break;
    +
      950.     17        case "expected_a_b_before_c_d":
    +
      951.     17            mm = `Expected ${a} '${b}' to be ordered before ${c} '${d}'.`;
    +
      952.     17            break;
    +
      953.      2        case "expected_a_b_from_c_d":
    +
      954.      2            mm = (
    +
      955.      2                `Expected '${a}' to match '${b}' from line ${c}`
    +
      956.      2                + ` and instead saw '${d}'.`
    +
      957.      2            );
    +
      958.      2            break;
    +
      959.     30        case "expected_a_before_b":
    +
      960.     30            mm = `Expected '${a}' before '${b}'.`;
    +
      961.     30            break;
    +
      962.      2        case "expected_digits_after_a":
    +
      963.      2            mm = `Expected digits after '${a}'.`;
    +
      964.      2            break;
    +
      965.      1        case "expected_four_digits":
    +
      966.      1            mm = `Expected four digits after '\\u'.`;
    +
      967.      1            break;
    +
      968.     31        case "expected_identifier_a":
    +
      969.     31            mm = `Expected an identifier and instead saw '${a}'.`;
    +
      970.     31            break;
    +
      971.      6        case "expected_line_break_a_b":
    +
      972.      6            mm = `Expected a line break between '${a}' and '${b}'.`;
    +
      973.      6            break;
    +
      974.      3        case "expected_regexp_factor_a":
    +
      975.      3            mm = `Expected a regexp factor and instead saw '${a}'.`;
    +
      976.      3            break;
    +
      977.     76        case "expected_space_a_b":
    +
      978.     76            mm = `Expected one space between '${a}' and '${b}'.`;
    +
      979.     76            break;
    +
      980.      2        case "expected_statements_a":
    +
      981.      2            mm = `Expected statements before '${a}'.`;
    +
      982.      2            break;
    +
      983.      1        case "expected_string_a":
    +
      984.      1            mm = `Expected a string and instead saw '${a}'.`;
    +
      985.      1            break;
    +
      986.      1        case "expected_type_string_a":
    +
      987.      1            mm = `Expected a type string and instead saw '${a}'.`;
    +
      988.      1            break;
    +
      989.      6        case "freeze_exports":
    +
      990.      6            mm = (
    +
      991.      6                `Expected 'Object.freeze('. All export values should be frozen.`
    +
      992.      6            );
    +
      993.      6            break;
    +
      994.   1394
    +
      995.   1394// PR-378 - Relax warning "function_in_loop".
    +
      996.   1394//
    +
      997.   1394//         case "function_in_loop":
    +
      998.   1394//             mm = `Don't create functions within a loop.`;
    +
      999.   1394//             break;
    +
     1000.   1394
    +
     1001.   1394// PR-390 - Add numeric-separator check.
    +
     1002.   1394
    +
     1003.      7        case "illegal_num_separator":
    +
     1004.      7            mm = `Illegal numeric separator '_' at column ${column}.`;
    +
     1005.      7            break;
    +
     1006.      1        case "infix_in":
    +
     1007.      1            mm = (
    +
     1008.      1                `Unexpected 'in'. Compare with undefined,`
    +
     1009.      1                + ` or use the hasOwnProperty method instead.`
    +
     1010.      1            );
    +
     1011.      1            break;
    +
     1012.      1        case "label_a":
    +
     1013.      1            mm = `'${a}' is a statement label.`;
    +
     1014.      1            break;
    +
     1015.      1        case "misplaced_a":
    +
     1016.      1            mm = `Place '${a}' at the outermost level.`;
    +
     1017.      1            break;
    +
     1018.      1        case "misplaced_directive_a":
    +
     1019.      1            mm = `Place the '/*${a}*/' directive before the first statement.`;
    +
     1020.      1            break;
    +
     1021.      3        case "missing_await_statement":
    +
     1022.      3            mm = `Expected await statement in async function.`;
    +
     1023.      3            break;
    +
     1024.   1394
    +
     1025.   1394// PR-347 - Disable warning "missing_browser".
    +
     1026.   1394//
    +
     1027.   1394//         case "missing_browser":
    +
     1028.   1394//             mm = `/*global*/ requires the Assume a browser option.`;
    +
     1029.   1394//             break;
    +
     1030.   1394
    +
     1031.      1        case "missing_m":
    +
     1032.      1            mm = `Expected 'm' flag on a multiline regular expression.`;
    +
     1033.      1            break;
    +
     1034.      5        case "naked_block":
    +
     1035.      5            mm = `Naked block.`;
    +
     1036.      5            break;
    +
     1037.      2        case "nested_comment":
    +
     1038.      2            mm = `Nested comment.`;
    +
     1039.      2            break;
    +
     1040.      1        case "not_label_a":
    +
     1041.      1            mm = `'${a}' is not a label.`;
    +
     1042.      1            break;
    +
     1043.      2        case "number_isNaN":
    +
     1044.      2            mm = `Use Number.isNaN function to compare with NaN.`;
    +
     1045.      2            break;
    +
     1046.      4        case "out_of_scope_a":
    +
     1047.      4            mm = `'${a}' is out of scope.`;
    +
     1048.      4            break;
    +
     1049.     11        case "redefinition_a_b":
    +
     1050.     11            mm = `Redefinition of '${a}' from line ${b}.`;
    +
     1051.     11            break;
    +
     1052.      1        case "redefinition_global_a_b":
    +
     1053.      1            mm = `Redefinition of global ${a} variable '${b}'.`;
    +
     1054.      1            break;
    +
     1055.      6        case "required_a_optional_b":
    +
     1056.      6            mm = `Required parameter '${a}' after optional parameter '${b}'.`;
    +
     1057.      6            break;
    +
     1058.      1        case "reserved_a":
    +
     1059.      1            mm = `Reserved name '${a}'.`;
    +
     1060.      1            break;
    +
     1061.      1        case "subscript_a":
    +
     1062.      1            mm = `['${a}'] is better written in dot notation.`;
    +
     1063.      1            break;
    +
     1064.     16        case "todo_comment":
    +
     1065.     16            mm = `Unexpected TODO comment.`;
    +
     1066.     16            break;
    +
     1067.     13        case "too_long":
    +
     1068.     13            mm = `Line is longer than 80 characters.`;
    +
     1069.     13            break;
    +
     1070.      1        case "too_many_digits":
    +
     1071.      1            mm = `Too many digits.`;
    +
     1072.      1            break;
    +
     1073.      3        case "unclosed_comment":
    +
     1074.      3            mm = `Unclosed comment.`;
    +
     1075.      3            break;
    +
     1076.      3        case "unclosed_disable":
    +
     1077.      3            mm = (
    +
     1078.      3                `Directive '/*jslint-disable*/' was not closed`
    +
     1079.      3                + ` with '/*jslint-enable*/'.`
    +
     1080.      3            );
    +
     1081.      3            break;
    +
     1082.      3        case "unclosed_mega":
    +
     1083.      3            mm = `Unclosed mega literal.`;
    +
     1084.      3            break;
    +
     1085.      2        case "unclosed_string":
    +
     1086.      2            mm = `Unclosed string.`;
    +
     1087.      2            break;
    +
     1088.    137        case "undeclared_a":
    +
     1089.    137            mm = `Undeclared '${a}'.`;
    +
     1090.    137            break;
    +
     1091.    237        case "unexpected_a":
    +
     1092.    237            mm = `Unexpected '${a}'.`;
    +
     1093.    237            break;
    +
     1094.      2        case "unexpected_a_after_b":
    +
     1095.      2            mm = `Unexpected '${a}' after '${b}'.`;
    +
     1096.      2            break;
    +
     1097.      2        case "unexpected_a_before_b":
    +
     1098.      2            mm = `Unexpected '${a}' before '${b}'.`;
    +
     1099.      2            break;
    +
     1100.     34        case "unexpected_at_top_level_a":
    +
     1101.     34            mm = `Expected '${a}' to be in a function.`;
    +
     1102.     34            break;
    +
     1103.      1        case "unexpected_char_a":
    +
     1104.      1            mm = `Unexpected character '${a}'.`;
    +
     1105.      1            break;
    +
     1106.      2        case "unexpected_comment":
    +
     1107.      2            mm = `Unexpected comment.`;
    +
     1108.      2            break;
    +
     1109.   1394
    +
     1110.   1394// PR-347 - Disable warning "unexpected_directive_a".
    +
     1111.   1394//
    +
     1112.   1394//         case "unexpected_directive_a":
    +
     1113.   1394//             mm = `When using modules, don't use directive '/\u002a${a}'.`;
    +
     1114.   1394//             break;
    +
     1115.   1394
    +
     1116.    128        case "unexpected_expression_a":
    +
     1117.    128            mm = `Unexpected expression '${a}' in statement position.`;
    +
     1118.    128            break;
    +
     1119.      4        case "unexpected_label_a":
    +
     1120.      4            mm = `Unexpected label '${a}'.`;
    +
     1121.      4            break;
    +
     1122.      3        case "unexpected_parens":
    +
     1123.      3            mm = `Don't wrap function literals in parens.`;
    +
     1124.      3            break;
    +
     1125.      8        case "unexpected_space_a_b":
    +
     1126.      8            mm = `Unexpected space between '${a}' and '${b}'.`;
    +
     1127.      8            break;
    +
     1128.      1        case "unexpected_statement_a":
    +
     1129.      1            mm = `Unexpected statement '${a}' in expression position.`;
    +
     1130.      1            break;
    +
     1131.      2        case "unexpected_trailing_space":
    +
     1132.      2            mm = `Unexpected trailing space.`;
    +
     1133.      2            break;
    +
     1134.      1        case "unexpected_typeof_a":
    +
     1135.      1            mm = (
    +
     1136.      1                `Unexpected 'typeof'. Use '===' to compare directly with ${a}.`
    +
     1137.      1            );
    +
     1138.      1            break;
    +
     1139.      1        case "uninitialized_a":
    +
     1140.      1            mm = `Uninitialized '${a}'.`;
    +
     1141.      1            break;
    +
     1142.      1        case "unopened_enable":
    +
     1143.      1            mm = (
    +
     1144.      1                `Directive '/*jslint-enable*/' was not opened`
    +
     1145.      1                + ` with '/*jslint-disable*/'.`
    +
     1146.      1            );
    +
     1147.      1            break;
    +
     1148.      1        case "unreachable_a":
    +
     1149.      1            mm = `Unreachable '${a}'.`;
    +
     1150.      1            break;
    +
     1151.      1        case "unregistered_property_a":
    +
     1152.      1            mm = `Unregistered property name '${a}'.`;
    +
     1153.      1            break;
    +
     1154.      6        case "unused_a":
    +
     1155.      6            mm = `Unused '${a}'.`;
    +
     1156.      6            break;
    +
     1157.      2        case "use_double":
    +
     1158.      2            mm = `Use double quotes, not single quotes.`;
    +
     1159.      2            break;
    +
     1160.   1394
    +
     1161.   1394// PR-386 - Fix issue #382 - Make fart-related warnings more readable.
    +
     1162.   1394
    +
     1163.      4        case "use_function_not_fart":
    +
     1164.      4            mm = (
    +
     1165.      4                `Use 'function (...)', not '(...) =>' when arrow functions`
    +
     1166.      4                + ` become too complex.`
    +
     1167.      4            );
    +
     1168.      4            break;
    +
     1169.      7        case "use_open":
    +
     1170.      7            mm = (
    +
     1171.      7                `Wrap a ternary expression in parens,`
    +
     1172.      7                + ` with a line break after the left paren.`
    +
     1173.      7            );
    +
     1174.      7            break;
    +
     1175.      1        case "use_spaces":
    +
     1176.      1            mm = `Use spaces, not tabs.`;
    +
     1177.      1            break;
    +
     1178.      5        case "var_on_top":
    +
     1179.      5            mm = `Move variable declaration to top of function or script.`;
    +
     1180.      5            break;
    +
     1181.      1        case "var_switch":
    +
     1182.      1            mm = `Don't declare variables in a switch.`;
    +
     1183.      1            break;
    +
     1184.     23        case "weird_condition_a":
    +
     1185.     23            mm = `Weird condition '${a}'.`;
    +
     1186.     23            break;
    +
     1187.      5        case "weird_expression_a":
    +
     1188.      5            mm = `Weird expression '${a}'.`;
    +
     1189.      5            break;
    +
     1190.      3        case "weird_loop":
    +
     1191.      3            mm = `Weird loop.`;
    +
     1192.      3            break;
    +
     1193.      9        case "weird_property_a":
    +
     1194.      9            mm = `Weird property name '${a}'.`;
    +
     1195.      9            break;
    +
     1196.      8        case "weird_relation_a":
    +
     1197.      8            mm = `Weird relation '${a}'.`;
    +
     1198.      8            break;
    +
     1199.      1        case "wrap_condition":
    +
     1200.      1            mm = `Wrap the condition in parens.`;
    +
     1201.      1            break;
    +
     1202.   1394
    +
     1203.   1394// PR-386 - Fix issue #382 - Make fart-related warnings more readable.
    +
     1204.   1394
    +
     1205.      1        case "wrap_fart_parameter":
    +
     1206.      1            mm = `Wrap the parameter before '=>' in parens.`;
    +
     1207.      1            break;
    +
     1208.      1        case "wrap_immediate":
    +
     1209.      1            mm = (
    +
     1210.      1                `Wrap an immediate function invocation in parentheses to assist`
    +
     1211.      1                + ` the reader in understanding that the expression is the`
    +
     1212.      1                + ` result of a function, and not the function itself.`
    +
     1213.      1            );
    +
     1214.      1            break;
    +
     1215.     18        case "wrap_regexp":
    +
     1216.     18            mm = `Wrap this regexp in parens to avoid confusion.`;
    +
     1217.     18            break;
    +
     1218.      1        case "wrap_unary":
    +
     1219.      1            mm = `Wrap the unary expression in parens.`;
    +
     1220.      1            break;
    +
     1221.   1394        }
    +
     1222.   1394
    +
     1223.   1394// Validate mm.
    +
     1224.   1394
    +
     1225.   1394        jslint_assert(mm, code);
    +
     1226.   1394        warning.message = mm;
    +
     1227.   1394
    +
     1228.   1394// PR-242 - Include stack_trace for jslint to debug itself for errors.
    +
     1229.   1394
    +
     1230.      6        if (option_dict.trace) {
    +
     1231.      6            warning.stack_trace = new Error().stack;
    +
     1232.      6        }
    +
     1233.     41        if (warning.directive_ignore_line) {
    +
     1234.     41
    +
     1235.     41// test_cause:
    +
     1236.     41// ["0 //jslint-ignore-line", "semicolon", "directive_ignore_line", "", 0]
    +
     1237.     41
    +
     1238.     41            test_cause("directive_ignore_line");
    +
     1239.     41            return warning;
    +
     1240.   1353        }
    +
     1241.   1353        warning_list.push(warning);
    +
     1242.   1353        return warning;
    +
     1243.   1353    }
    +
     1244.    668
    +
     1245.    668    try {
    +
     1246.    668
    +
     1247.    668// tokenize takes a source and produces from it an array of token objects.
    +
     1248.    668// JavaScript is notoriously difficult to tokenize because of the horrible
    +
     1249.    668// interactions between automatic semicolon insertion, regular expression
    +
     1250.    668// literals, and now megastring literals. JSLint benefits from eliminating
    +
     1251.    668// automatic semicolon insertion and nested megastring literals, which allows
    +
     1252.    668// full tokenization to precede parsing.
    +
     1253.    668
    +
     1254.    668        option_dict = Object.assign(empty(), option_dict);
    +
     1255.    668        Object.assign(state, {
    +
     1256.    668            artifact,
    +
     1257.    668            catch_list,
    +
     1258.    668            catch_stack,
    +
     1259.    668            directive_list,
    +
     1260.    668            export_dict,
    +
     1261.    668            function_list,
    +
     1262.    668            function_stack,
    +
     1263.    668            global_dict,
    +
     1264.    668            global_list,
    +
     1265.    668            import_list,
    +
     1266.    668            is_equal,
    +
     1267.    668            is_weird,
    +
     1268.    668            line_list,
    +
     1269.    668            mode_json: false,           // true if parsing JSON.
    +
     1270.    668            mode_module: false,         // true if import or export was used.
    +
     1271.    668            mode_property: false,       // true if directive /*property*/ is
    +
     1272.    668                                        // ... used.
    +
     1273.    668            mode_shebang: false,        // true if #! is seen on the first line.
    +
     1274.    668            option_dict,
    +
     1275.    668            property_dict,
    +
     1276.    668            source,
    +
     1277.    668            stop,
    +
     1278.    668            stop_at,
    +
     1279.    668            syntax_dict,
    +
     1280.    668            tenure,
    +
     1281.    668            test_cause,
    +
     1282.    668            token_global,
    +
     1283.    668            token_list,
    +
     1284.    668            token_nxt: token_global,
    +
     1285.    668            warn,
    +
     1286.    668            warn_at,
    +
     1287.    668            warning_list
    +
     1288.    668        });
    +
     1289.    668
    +
     1290.    668// PHASE 1. Split <source> by newlines into <line_list>.
    +
     1291.    668
    +
     1292.    668        jslint_phase1_split(state);
    +
     1293.    668        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
    +
     1294.    668        jslint_assert(
    +
     1295.    668            function_stack.length === 0,
    +
     1296.    668            `function_stack.length === 0.`
    +
     1297.    668        );
    +
     1298.    668
    +
     1299.    668// PHASE 2. Lex <line_list> into <token_list>.
    +
     1300.    668
    +
     1301.    668        jslint_phase2_lex(state);
    +
     1302.    668        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
    +
     1303.    668        jslint_assert(
    +
     1304.    668            function_stack.length === 0,
    +
     1305.    668            `function_stack.length === 0.`
    +
     1306.    668        );
    +
     1307.    668
    +
     1308.    668// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
    +
     1309.    668
    +
     1310.    668        jslint_phase3_parse(state);
    +
     1311.    668        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
    +
     1312.    668        jslint_assert(
    +
     1313.    668            function_stack.length === 0,
    +
     1314.    668            `function_stack.length === 0.`
    +
     1315.    668        );
    +
     1316.    668
    +
     1317.    668// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
    +
     1318.    668//          recursive traversal. Each node may be processed on the way down
    +
     1319.    668//          (preaction) and on the way up (postaction).
    +
     1320.    668
    +
     1321.    518        if (!state.mode_json) {
    +
     1322.    518            jslint_phase4_walk(state);
    +
     1323.    533        }
    +
     1324.    533        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
    +
     1325.    533        jslint_assert(
    +
     1326.    533            function_stack.length === 0,
    +
     1327.    533            `function_stack.length === 0.`
    +
     1328.    533        );
    +
     1329.    533
    +
     1330.    533// PHASE 5. Check whitespace between tokens in <token_list>.
    +
     1331.    533
    +
     1332.    533        if (!state.mode_json && warning_list.length === 0) {
    +
     1333.    208            jslint_phase5_whitage(state);
    +
     1334.    533        }
    +
     1335.    533        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
    +
     1336.    533        jslint_assert(
    +
     1337.    533            function_stack.length === 0,
    +
     1338.    533            `function_stack.length === 0.`
    +
     1339.    533        );
    +
     1340.    533
    +
     1341.    533// PR-347 - Disable warning "missing_browser".
    +
     1342.    533//
    +
     1343.    533//         if (!option_dict.browser) {
    +
     1344.    533//             directive_list.forEach(function (comment) {
    +
     1345.    533//                 if (comment.directive === "global") {
    +
     1346.    533//
    +
     1347.    533// // test_cause:
    +
     1348.    533// // ["/*global aa*/", "jslint", "missing_browser", "(comment)", 1]
    +
     1349.    533//
    +
     1350.    533//                     warn("missing_browser", comment);
    +
     1351.    533//                 }
    +
     1352.    533//             });
    +
     1353.    533//         }
    +
     1354.    533
    +
     1355.    533        if (option_dict.test_internal_error) {
    +
     1356.      2            jslint_assert(undefined, "test_internal_error");
    +
     1357.      2        }
    +
     1358.    137    } catch (err) {
    +
     1359.    137        mode_stop = true;
    +
     1360.    137        err.message = "[JSLint was unable to finish] " + err.message;
    +
     1361.    137        err.mode_stop = true;
    +
     1362.    137        if (err.name !== "JSLintError") {
    +
     1363.    137            Object.assign(err, {
    +
     1364.    137                column: jslint_fudge,
    +
     1365.    137                line: jslint_fudge,
    +
     1366.    137                line_source: "",
    +
     1367.    137                stack_trace: err.stack
    +
     1368.    137            });
    +
     1369.    137        }
    +
     1370.    137        if (warning_list.indexOf(err) === -1) {
    +
     1371.    137            warning_list.push(err);
    +
     1372.    137        }
    +
     1373.    137    }
    +
     1374.    668
    +
     1375.    668// Sort warning_list by mode_stop first, line, column respectively.
    +
     1376.    668
    +
     1377.    986    warning_list.sort(function (aa, bb) {
    +
     1378.    986        return (
    +
     1379.    986            Boolean(bb.mode_stop) - Boolean(aa.mode_stop)
    +
     1380.    896            || aa.line - bb.line
    +
     1381.    876            || aa.column - bb.column
    +
     1382.    986        );
    +
     1383.    986
    +
     1384.    986// Update each warning with formatted_message ready-for-use by jslint_cli.
    +
     1385.    986
    +
     1386.   1164    }).map(function ({
    +
     1387.   1164        column,
    +
     1388.   1164        line,
    +
     1389.   1164        line_source,
    +
     1390.   1164        message,
    +
     1391.   1164        stack_trace = ""
    +
     1392.   1164    }, ii, list) {
    +
     1393.   1164        list[ii].formatted_message = String(
    +
     1394.   1164            String(ii + 1).padStart(2, " ")
    +
     1395.   1164            + ". \u001b[31m" + message + "\u001b[39m"
    +
     1396.   1164            + " \u001b[90m\/\/ line " + line + ", column " + column
    +
     1397.   1164            + "\u001b[39m\n"
    +
     1398.   1164            + ("    " + line_source.trim()).slice(0, 72) + "\n"
    +
     1399.   1164            + stack_trace
    +
     1400.   1164        ).trimRight();
    +
     1401.   1164    });
    +
     1402.    668
    +
     1403.    668    return {
    +
     1404.    668        causes: cause_dict,
    +
     1405.    668        directives: directive_list,
    +
     1406.    668        edition: jslint_edition,
    +
     1407.    668        exports: export_dict,
    +
     1408.    668        froms: import_list,
    +
     1409.    668        functions: function_list,
    +
     1410.    668        global: token_global,
    +
     1411.    668        id: "(JSLint)",
    +
     1412.    668        json: state.mode_json,
    +
     1413.    668        lines: line_list,
    +
     1414.    668        module: state.mode_module === true,
    +
     1415.    164        ok: warning_list.length === 0 && !mode_stop,
    +
     1416.    668        option: option_dict,
    +
     1417.    668        property: property_dict,
    +
     1418.    668        shebang: (
    +
     1419.    668            state.mode_shebang
    +
     1420.      1            ? line_list[jslint_fudge].line_source
    +
     1421.    667            : undefined
    +
     1422.    668        ),
    +
     1423.    668        stop: mode_stop,
    +
     1424.    668        tokens: token_list,
    +
     1425.    668        tree: state.token_tree,
    +
     1426.    668        warnings: warning_list
    +
     1427.    668    };
    +
     1428.    668}
    +
     1429.      1
    +
     1430.      1// PR-362 - Add API Doc.
    +
     1431.      1
    +
     1432.      1async function jslint_apidoc({
    +
     1433.      1    example_list,
    +
     1434.      1    github_repo,
    +
     1435.      1    module_list,
    +
     1436.      1    package_name,
    +
     1437.      1    pathname,
    +
     1438.      1    version
    +
     1439.      1}) {
    +
     1440.      1
    +
     1441.      1// This function will create API Doc from <module_list>.
    +
     1442.      1
    +
     1443.      1    let elem_ii = 0;
    +
     1444.      1    let html;
    +
     1445.      1
    +
     1446.     27    function elem_create(moduleObj, key, moduleName) {
    +
     1447.     27
    +
     1448.     27// This function will create a sub API Doc from elem <moduleObj>[<key>].
    +
     1449.     27
    +
     1450.     27        let example = "N/A";
    +
     1451.     27        let id = encodeURIComponent("apidoc.elem." + moduleName + "." + key);
    +
     1452.     27        let name;
    +
     1453.     27        let signature;
    +
     1454.     27        let source;
    +
     1455.     27        name = htmlEscape((typeof moduleObj[key]) + " " + key);
    +
     1456.      2        if (typeof moduleObj[key] !== "function") {
    +
     1457.      2            return {
    +
     1458.      2                name,
    +
     1459.      2                signature: (`
    +
     1460.      2<a class="apidocElementLiA" href="#${id}">
    +
     1461.      2${name}
    +
     1462.      2</a>
    +
     1463.      2                `),
    +
     1464.      2                source: (`
    +
     1465.      2<li>
    +
     1466.      2    <h2>
    +
     1467.      2    <a href="#${id}" id="${id}">
    +
     1468.      2    ${name}
    +
     1469.      2    </a>
    +
     1470.      2    </h2>
    +
     1471.      2</li>
    +
     1472.      2                `)
    +
     1473.      2            };
    +
     1474.     25        }
    +
     1475.     25        // init source
    +
     1476.     25        source = htmlEscape(trim_start(moduleObj[key].toString()));
    +
     1477.     25        // init signature
    +
     1478.     25        source = source.replace((
    +
     1479.     25            /(\([\S\s]*?\)) \{/
    +
     1480.     25        ), function (match0, match1) {
    +
     1481.     25            signature = htmlEscape(
    +
     1482.     25                match1.replace((
    +
     1483.     25                    / *?\/\*[\S\s]*?\*\/ */g
    +
     1484.     25                ), "").replace((
    +
     1485.     25                    / *?\/\/.*/g
    +
     1486.     25                ), "").replace((
    +
     1487.     25                    /\n{2,}/g
    +
     1488.     25                ), "\n")
    +
     1489.     25            );
    +
     1490.     25            return match0;
    +
     1491.     25        });
    +
     1492.     25        // init comment
    +
     1493.     25        source = source.replace((
    +
     1494.     25            /\n(?:\/\/.*?\n)+\n/
    +
     1495.     25        ), "<span class=\"apidocCodeCommentSpan\">$&</span>");
    +
     1496.     25        // init example
    +
     1497.     56        example_list.some(function (example2) {
    +
     1498.     56            example2.replace(
    +
     1499.     56                new RegExp((
    +
     1500.     56                    "((?:\\n.*?){8}(function )?)\\b"
    +
     1501.     56                    + key
    +
     1502.     56                    + "(\\((?:.*?\\n){8})"
    +
     1503.     56                ), "g"),
    +
     1504.    132                function (ignore, header, isDeclaration, footer) {
    +
     1505.    124                    if (!isDeclaration) {
    +
     1506.    124                        example = "..." + trim_start(
    +
     1507.    124                            htmlEscape(header)
    +
     1508.    124                            + "<span class=\"apidocCodeKeywordSpan\">"
    +
     1509.    124                            + htmlEscape(key)
    +
     1510.    124                            + "</span>"
    +
     1511.    124                            + htmlEscape(footer)
    +
     1512.    124                        ).trimEnd() + "\n...";
    +
     1513.    124                    }
    +
     1514.    132                    return "";
    +
     1515.    132                }
    +
     1516.     56            );
    +
     1517.     56            return example !== "N/A";
    +
     1518.     56        });
    +
     1519.     25        if (source.length > 2048) {
    +
     1520.     11            source = source.slice(0, 2048) + "...\n}\n";
    +
     1521.     25        }
    +
     1522.     25        return {
    +
     1523.     25            name,
    +
     1524.     25            signature: (`
    +
     1525.     25<a class="apidocElementLiA" href="#${id}">
    +
     1526.     25${name}<span class="apidocSignatureSpan">${signature}</span>
    +
     1527.     25</a>
    +
     1528.     25            `),
    +
     1529.     25            source: (`
    +
     1530.     25<li>
    +
     1531.     25    <h2>
    +
     1532.     25    <a href="#${id}" id="${id}">
    +
     1533.     25    ${name}<span class="apidocSignatureSpan">${signature}</span>
    +
     1534.     25    </a>
    +
     1535.     25    </h2>
    +
     1536.     25</li>
    +
     1537.     25<li>Description and source-code:<pre class="apidocCodePre">${source}</pre></li>
    +
     1538.     25<li>Example usage:<pre class="apidocCodePre">${example}</pre></li>
    +
     1539.     25            `)
    +
     1540.     25        };
    +
     1541.     25    }
    +
     1542.      1
    +
     1543.    149    function trim_start(str) {
    +
     1544.    149
    +
     1545.    149// This function will normalize whitespace before <str>.
    +
     1546.    149
    +
     1547.    149        let whitespace = "";
    +
     1548.    149        str.trim().replace((
    +
     1549.    149            /^ */gm
    +
     1550.  13071        ), function (match0) {
    +
     1551.   8412            if (whitespace === "" || match0.length < whitespace.length) {
    +
     1552.   6610                whitespace = match0;
    +
     1553.   6610            }
    +
     1554.  13071            return "";
    +
     1555.  13071        });
    +
     1556.    149        str = str.replace(new RegExp("^" + whitespace, "gm"), "");
    +
     1557.    149        return str;
    +
     1558.    149    }
    +
     1559.      1    await moduleFsInit();
    +
     1560.      1
    +
     1561.      1// Html-escape params.
    +
     1562.      1
    +
     1563.      1    github_repo = htmlEscape(github_repo);
    +
     1564.      1    package_name = htmlEscape(package_name);
    +
     1565.      1    version = htmlEscape(version);
    +
     1566.      1
    +
     1567.      1// Init example_list.
    +
     1568.      1
    +
     1569.      3    example_list = await Promise.all(example_list.map(async function (file) {
    +
     1570.      3
    +
     1571.      3// This function will read example from given file.
    +
     1572.      3
    +
     1573.      3        let result = await moduleFs.promises.readFile(file, "utf8");
    +
     1574.      3        result = (
    +
     1575.      3            "\n\n\n\n\n\n\n\n"
    +
     1576.      3            // bug-workaround - truncate example to manageable size
    +
     1577.      3            + result.slice(0, 524288)
    +
     1578.      3            + "\n\n\n\n\n\n\n\n"
    +
     1579.      3        );
    +
     1580.      3        result = result.replace((
    +
     1581.      3            /\r\n*/g
    +
     1582.      3        ), "\n");
    +
     1583.      3        return result;
    +
     1584.      3    }));
    +
     1585.      1
    +
     1586.      1// Init module_list.
    +
     1587.      1
    +
     1588.      1    module_list = await Promise.all(module_list.map(async function ({
    +
     1589.      1        pathname
    +
     1590.      1    }) {
    +
     1591.      1        let moduleName = htmlEscape(JSON.stringify(pathname));
    +
     1592.      1        let moduleObj = await import(pathname);
    +
     1593.      1        if (moduleObj.default) {
    +
     1594.      1            moduleObj = moduleObj.default;
    +
     1595.      1        }
    +
     1596.      1        return {
    +
     1597.     27            elem_list: Object.keys(moduleObj).map(function (key) {
    +
     1598.     27                return elem_create(moduleObj, key, moduleName);
    +
     1599.     78            }).sort(function (aa, bb) {
    +
     1600.     78                return (
    +
     1601.     78                    aa.name < bb.name
    +
     1602.     22                    ? -1
    +
     1603.     56                    : 1
    +
     1604.     78                );
    +
     1605.     27            }).map(function (elem) {
    +
     1606.     27                elem_ii += 1;
    +
     1607.     27                elem.signature = elem.signature.replace(
    +
     1608.     27                    ">",
    +
     1609.     27                    ">" + elem_ii + ". "
    +
     1610.     27                );
    +
     1611.     27                elem.source = elem.source.replace(
    +
     1612.     27                    "\">",
    +
     1613.     27                    "\">" + elem_ii + ". "
    +
     1614.     27                );
    +
     1615.     27                return elem;
    +
     1616.     27            }),
    +
     1617.      1            id: encodeURIComponent("apidoc.module." + moduleName),
    +
     1618.      1            moduleName
    +
     1619.      1        };
    +
     1620.      1    }));
    +
     1621.      1    html = (`
    +
     1622.      1<!DOCTYPE html>
    +
     1623.      1<html lang="en">
    +
     1624.      1<head>
    +
     1625.      1<meta charset="utf-8">
    +
     1626.      1<meta name="viewport" content="width=device-width, initial-scale=1">
    +
     1627.      1<meta name="description" content="${package_name} API Doc">
    +
     1628.      1<title>${package_name} apidoc</title>
    +
     1629.      1<style>
    +
     1630.      1/* jslint utility2:true */
    +
     1631.      1/*csslint*/
    +
     1632.      1body {
    +
     1633.      1    margin: 0;
    +
     1634.      1    padding: 20px;
    +
     1635.      1}
    +
     1636.      1.apidocCodeCommentSpan,
    +
     1637.      1.apidocCodeKeywordSpan {
    +
     1638.      1    background: royalblue;
    +
     1639.      1    color: white;
    +
     1640.      1}
    +
     1641.      1.apidocCodeCommentSpan {
    +
     1642.      1    display: block;
    +
     1643.      1}
    +
     1644.      1.apidocCodePre {
    +
     1645.      1    background: #eef;
    +
     1646.      1    border: 1px solid;
    +
     1647.      1    font-size: 14px;
    +
     1648.      1    overflow-wrap: break-word;
    +
     1649.      1    padding: 5px;
    +
     1650.      1    white-space: pre-wrap;
    +
     1651.      1}
    +
     1652.      1.apidocDiv {
    +
     1653.      1    color: #555;
    +
     1654.      1    font-family: sans-serif;
    +
     1655.      1}
    +
     1656.      1.apidocDiv a[href] {
    +
     1657.      1    color: royalblue;
    +
     1658.      1    text-decoration: none;
    +
     1659.      1}
    +
     1660.      1.apidocDiv a[href]:hover {
    +
     1661.      1    text-decoration: underline;
    +
     1662.      1}
    +
     1663.      1.apidocDiv li a {
    +
     1664.      1    display: inline-block;
    +
     1665.      1    padding: 8px 0;
    +
     1666.      1}
    +
     1667.      1.apidocDiv ul {
    +
     1668.      1    list-style: none;
    +
     1669.      1    padding-left: 20px;
    +
     1670.      1}
    +
     1671.      1.apidocFooterDiv {
    +
     1672.      1    margin-top: 20px;
    +
     1673.      1    text-align: center;
    +
     1674.      1}
    +
     1675.      1.apidocModuleA {
    +
     1676.      1    font-size: 24px;
    +
     1677.      1    font-weight: bold;
    +
     1678.      1}
    +
     1679.      1.apidocSectionDiv {
    +
     1680.      1    border-top: 1px solid;
    +
     1681.      1    margin-top: 20px;
    +
     1682.      1}
    +
     1683.      1.apidocSignatureSpan {
    +
     1684.      1    color: #666;
    +
     1685.      1    white-space: pre-wrap;
    +
     1686.      1}
    +
     1687.      1</style>
    +
     1688.      1</head>
    +
     1689.      1<body>
    +
     1690.      1<div class="apidocDiv">
    +
     1691.      1<h1>API Doc for <a href="${github_repo}">${package_name} (${version})</a></h1>
    +
     1692.      1<div class="apidocSectionDiv">
    +
     1693.      1    <a href="#apidocTableOfContents1" id="apidocTableOfContents1">
    +
     1694.      1        <h1>Table of Contents</h1>
    +
     1695.      1    </a>
    +
     1696.      1    <ul>
    +
     1697.      1    `) + module_list.map(function ({
    +
     1698.      1        elem_list,
    +
     1699.      1        id,
    +
     1700.      1        moduleName
    +
     1701.      1    }) {
    +
     1702.      1        return (
    +
     1703.      1            (`
    +
     1704.      1        <li>
    +
     1705.      1            <a class="apidocModuleA" href="#${id}">Module ${moduleName}</a>
    +
     1706.      1            <ul>
    +
     1707.      1            `)
    +
     1708.     27            + elem_list.map(function ({
    +
     1709.     27                signature
    +
     1710.     27            }) {
    +
     1711.     27                return "<li>\n" + signature + "\n</li>\n";
    +
     1712.     27            }).join("")
    +
     1713.      1            + (`
    +
     1714.      1            </ul>
    +
     1715.      1        </li>
    +
     1716.      1            `)
    +
     1717.      1        );
    +
     1718.      1    }).join("") + (`
    +
     1719.      1    </ul>
    +
     1720.      1</div>
    +
     1721.      1    `) + module_list.map(function ({
    +
     1722.      1        elem_list,
    +
     1723.      1        id,
    +
     1724.      1        moduleName
    +
     1725.      1    }) {
    +
     1726.      1        return (
    +
     1727.      1            (`
    +
     1728.      1<div class="apidocSectionDiv">
    +
     1729.      1    <h1><a href="#${id}" id="${id}">Module ${moduleName}</a></h1>
    +
     1730.      1    <ul>
    +
     1731.      1            `)
    +
     1732.     27            + elem_list.map(function ({
    +
     1733.     27                source
    +
     1734.     27            }) {
    +
     1735.     27                return source;
    +
     1736.     27            }).join("")
    +
     1737.      1            + (`
    +
     1738.      1    </ul>
    +
     1739.      1</div>
    +
     1740.      1            `)
    +
     1741.      1        );
    +
     1742.      1    }).join("") + (`
    +
     1743.      1<div class="apidocFooterDiv">
    +
     1744.      1    [
    +
     1745.      1    This document was created with
    +
     1746.      1    <a href="https://github.com/jslint-org/jslint">JSLint</a>
    +
     1747.      1    ]
    +
     1748.      1</div>
    +
     1749.      1</div>
    +
     1750.      1</body>
    +
     1751.      1</html>
    +
     1752.      1    `);
    +
     1753.      1    html = html.trim().replace((
    +
     1754.      1        / +?$/gm
    +
     1755.      1    ), "") + "\n";
    +
     1756.      1    await fsWriteFileWithParents(pathname, html);
    +
     1757.      1}
    +
     1758.      1
    +
     1759. 102849function jslint_assert(condition, message) {
    +
     1760. 102849
    +
     1761. 102849// This function will throw <message> if <condition> is falsy.
    +
     1762. 102849
    +
     1763. 102847    if (condition) {
    +
     1764. 102847        return condition;
    +
     1765. 102847    }
    +
     1766.      2    throw new Error(
    +
     1767.      2        `This was caused by a bug in JSLint.
    +
     1768.      2Please open an issue with this stack-trace (and possible example-code) at
    +
     1769.      2https://github.com/jslint-org/jslint/issues.
    +
     1770.      2edition = "${jslint_edition}";
    +
     1771.      2${String(message).slice(0, 2000)}`
    +
     1772.      2    );
    +
     1773.      2}
    +
     1774.      1
    +
     1775.     38async function jslint_cli({
    +
     1776.     38    console_error,
    +
     1777.     38    console_log,
    +
     1778.     38    file,
    +
     1779.     38    import_meta_url,
    +
     1780.     38    mode_cli,
    +
     1781.     38    mode_noop,
    +
     1782.     38    option,
    +
     1783.     38    process_argv,
    +
     1784.     38    process_env,
    +
     1785.     38    process_exit,
    +
     1786.     38    source
    +
     1787.     38}) {
    +
     1788.     38
    +
     1789.     38// This function will run jslint from nodejs-cli.
    +
     1790.     38
    +
     1791.     38    let command;
    +
     1792.     38    let data;
    +
     1793.     38    let exit_code = 0;
    +
     1794.     38    let mode_report;
    +
     1795.     38    let mode_wrapper_vim;
    +
     1796.     38    let result;
    +
     1797.     38
    +
     1798.     51    function jslint_from_file({
    +
     1799.     51        code,
    +
     1800.     51        file,
    +
     1801.     51        line_offset = 0,
    +
     1802.     51        mode_conditional,
    +
     1803.     51        option = empty()
    +
     1804.     51    }) {
    +
     1805.     51        let result_from_file;
    +
     1806.     51        if (
    +
     1807.     51            mode_conditional
    +
     1808.      5            && !(
    +
     1809.      5                /^\/\*jslint\b/m
    +
     1810.      5            ).test(code.slice(0, 65536))
    +
     1811.      1        ) {
    +
     1812.      1            return;
    +
     1813.     50        }
    +
     1814.     50        option = Object.assign(empty(), option, {
    +
     1815.     50            file
    +
     1816.     50        });
    +
     1817.     50        switch ((
    +
     1818.     50            /\.\w+?$|$/m
    +
     1819.     50        ).exec(file)[0]) {
    +
     1820.     50        case ".html":
    +
     1821.      3
    +
     1822.      3// Recursively jslint embedded "<script>\n...\n</script>".
    +
     1823.      3
    +
     1824.      3            code.replace((
    +
     1825.      3                /^<script\b[^>]*?>\n([\S\s]*?\n)<\/script>$/gm
    +
     1826.      3            ), function (ignore, match1, ii) {
    +
     1827.      3                jslint_from_file({
    +
     1828.      3                    code: match1,
    +
     1829.      3                    file: file + ".<script>.js",
    +
     1830.      3                    line_offset: string_line_count(code.slice(0, ii)) + 1,
    +
     1831.      3                    option: Object.assign(empty(), {
    +
     1832.      3                        browser: true
    +
     1833.      3                    }, option)
    +
     1834.      3                });
    +
     1835.      3                return "";
    +
     1836.      3            });
    +
     1837.      3            return;
    +
     1838.      2        case ".md":
    +
     1839.      2
    +
     1840.      2// Recursively jslint embedded "node --eval '\n...\n'".
    +
     1841.      2
    +
     1842.      2            jslint_node_eval({
    +
     1843.      2                code,
    +
     1844.      2                file,
    +
     1845.      2                mode_conditional: true,
    +
     1846.      2                option
    +
     1847.      2            });
    +
     1848.      2            return;
    +
     1849.      2        case ".sh":
    +
     1850.      2
    +
     1851.      2// Recursively jslint embedded "node --eval '\n...\n'".
    +
     1852.      2
    +
     1853.      2            jslint_node_eval({
    +
     1854.      2                code,
    +
     1855.      2                file,
    +
     1856.      2                option
    +
     1857.      2            });
    +
     1858.      2            return;
    +
     1859.     43        default:
    +
     1860.     43            result_from_file = jslint("\n".repeat(line_offset) + code, option);
    +
     1861.     43        }
    +
     1862.     43
    +
     1863.     43// Print only first 10 warnings to stderr.
    +
     1864.     43
    +
     1865.     43        if (result_from_file.warnings.length > 0) {
    +
     1866.      5            exit_code = 1;
    +
     1867.      5            console_error(
    +
     1868.      5                mode_wrapper_vim
    +
     1869.      5
    +
     1870.      5// PR-349 - Print warnings in format readable by vim.
    +
     1871.      5
    +
     1872.      5                ? result_from_file.warnings.slice(0, 10).map(function ({
    +
     1873.      5                    column,
    +
     1874.      5                    line,
    +
     1875.      5                    message
    +
     1876.      5                }, ii) {
    +
     1877.      5                    return (
    +
     1878.      5                        file
    +
     1879.      5                        + ":" + ii
    +
     1880.      5                        + ":" + line
    +
     1881.      5                        + ":" + column
    +
     1882.      5                        + ":" + message
    +
     1883.      5                    );
    +
     1884.      5                }).join("\n")
    +
     1885.      5
    +
     1886.      5// Print warnings in format readable by human.
    +
     1887.      5
    +
     1888.      5                : "\u001b[1mjslint " + file + "\u001b[22m\n"
    +
     1889.     11                + result_from_file.warnings.slice(0, 10).map(function ({
    +
     1890.     11                    formatted_message
    +
     1891.     11                }) {
    +
     1892.     11                    return formatted_message;
    +
     1893.     11                }).join("\n")
    +
     1894.      5            );
    +
     1895.     43        }
    +
     1896.     43        return result_from_file;
    +
     1897.     43    }
    +
     1898.     38
    +
     1899.      4    function jslint_node_eval({
    +
     1900.      4        code,
    +
     1901.      4        file,
    +
     1902.      4        mode_conditional,
    +
     1903.      4        option = empty()
    +
     1904.      4    }) {
    +
     1905.      4        code.replace((
    +
     1906.      4            /\bnode\b.*? (?:--eval|-e) '\n([\S\s]*?\n)'/gm
    +
     1907.     26        ), function (ignore, match1, ii) {
    +
     1908.     26            jslint_from_file({
    +
     1909.     26                code: match1,
    +
     1910.     26                file: file + ".<node -e>.js",
    +
     1911.     26                line_offset: string_line_count(code.slice(0, ii)) + 1,
    +
     1912.     26                mode_conditional,
    +
     1913.     26                option: Object.assign(empty(), {
    +
     1914.     26                    beta: Boolean(
    +
     1915.     26                        process_env.JSLINT_BETA
    +
     1916.     26                        && !(
    +
     1917.     26                            /0|false|null|undefined/
    +
     1918.     26                        ).test(process_env.JSLINT_BETA)
    +
     1919.     26                    ),
    +
     1920.     26                    node: true
    +
     1921.     26                }, option)
    +
     1922.     26            });
    +
     1923.     26            return "";
    +
     1924.     26        });
    +
     1925.      4    }
    +
     1926.     38
    +
     1927.     28    function string_line_count(code) {
    +
     1928.     28
    +
     1929.     28// This function will count number of newlines in <code>.
    +
     1930.     28
    +
     1931.     28        let count;
    +
     1932.     28        let ii;
    +
     1933.     28
    +
     1934.     28// https://jsperf.com/regexp-counting-2/8
    +
     1935.     28
    +
     1936.     28        count = 0;
    +
     1937.     28        ii = 0;
    +
     1938.  23104        while (true) {
    +
     1939.  23104            ii = code.indexOf("\n", ii) + 1;
    +
     1940.  23104            if (ii === 0) {
    +
     1941.  23104                break;
    +
     1942.  23104            }
    +
     1943.  23104            count += 1;
    +
     1944.  23104        }
    +
     1945.     28        return count;
    +
     1946.     28    }
    +
     1947.     38
    +
     1948.     38// PR-396 - window.jslint
    +
     1949.     38// Check import.meta.url for directive to export jslint to window-object.
    +
     1950.     38// Useful for ES5-era browser-scripts that rely on window.jslint,
    +
     1951.     38// like CodeMirror.
    +
     1952.     38//
    +
     1953.     38// Example usage:
    +
     1954.     38// <script type="module" src="./jslint.mjs?window_jslint=1"></script>
    +
     1955.     38
    +
     1956.     22    import_meta_url = import_meta_url || jslint_import_meta_url;
    +
     1957.     38    if (
    +
     1958.     38        jslint_rgx_url_search_window_jslint.test(import_meta_url)
    +
     1959.      4        && (typeof globalThis === "object" && globalThis)
    +
     1960.      4    ) {
    +
     1961.      4        globalThis.jslint = jslint;
    +
     1962.      4    }
    +
     1963.     38
    +
     1964.     38// Feature-detect nodejs.
    +
     1965.     38
    +
     1966.     38    if (!(
    +
     1967.     38        (typeof process === "object" && process)
    +
     1968.     38        && process.versions
    +
     1969.     38        && typeof process.versions.node === "string"
    +
     1970.     38        && !mode_noop
    +
     1971.      1    )) {
    +
     1972.      1        return exit_code;
    +
     1973.     37    }
    +
     1974.     37    console_error = console_error || console.error;
    +
     1975.     37    console_log = console_log || console.log;
    +
     1976.     22    process_argv = process_argv || process.argv;
    +
     1977.     34    process_env = process_env || process.env;
    +
     1978.     24    process_exit = process_exit || process.exit;
    +
     1979.     37    await moduleFsInit();
    +
     1980.     37    if (
    +
     1981.     37        !(
    +
     1982.     37
    +
     1983.     37// Feature-detect nodejs-cli.
    +
     1984.     37
    +
     1985.     37            process.execArgv.indexOf("--eval") === -1
    +
     1986.     37            && process.execArgv.indexOf("-e") === -1
    +
     1987.     37            && (
    +
     1988.     37                (
    +
     1989.     37                    /[\/|\\]jslint(?:\.[cm]?js)?$/m
    +
     1990.     37                ).test(process_argv[1])
    +
     1991.     37                || mode_cli
    +
     1992.     37            )
    +
     1993.     20            && (
    +
     1994.     20                moduleUrl.fileURLToPath(import_meta_url)
    +
     1995.     20                ===
    +
     1996.     20                modulePath.resolve(process_argv[1])
    +
     1997.     20            )
    +
     1998.     38        )
    +
     1999.     22        && !mode_cli
    +
     2000.     17    ) {
    +
     2001.     17        return exit_code;
    +
     2002.     20    }
    +
     2003.     20
    +
     2004.     20// init commmand
    +
     2005.     20
    +
     2006.     20    command = String(process_argv[2]).split("=");
    +
     2007.     20    command[1] = command.slice(1).join("=");
    +
     2008.     20
    +
     2009.     20    switch (command[0]) {
    +
     2010.     20
    +
     2011.     20// PR-362 - Add API Doc.
    +
     2012.     20
    +
     2013.     20    case "jslint_apidoc":
    +
     2014.      1        await jslint_apidoc(Object.assign(JSON.parse(process_argv[3]), {
    +
     2015.      1            pathname: command[1]
    +
     2016.      1        }));
    +
     2017.      1        return;
    +
     2018.     38
    +
     2019.     38// PR-363 - Add command jslint_report.
    +
     2020.     38
    +
     2021.      6    case "jslint_report":
    +
     2022.      6        mode_report = command[1];
    +
     2023.      6        process_argv = process_argv.slice(1);
    +
     2024.      6        break;
    +
     2025.     38
    +
     2026.     38// COMMIT-b26d6df2 - Add command jslint_wrapper_vim.
    +
     2027.     38
    +
     2028.      1    case "jslint_wrapper_vim":
    +
     2029.      1        mode_wrapper_vim = true;
    +
     2030.      1        process_argv = process_argv.slice(1);
    +
     2031.      1        break;
    +
     2032.     38
    +
     2033.     38// PR-364 - Add command v8_coverage_report.
    +
     2034.     38
    +
     2035.      7    case "v8_coverage_report":
    +
     2036.      7        await v8CoverageReportCreate({
    +
     2037.      7            consoleError: console_error,
    +
     2038.      7            coverageDir: command[1],
    +
     2039.      7            processArgv: process_argv.slice(3)
    +
     2040.      7        });
    +
     2041.      7        return;
    +
     2042.     12    }
    +
     2043.     12
    +
     2044.     12// Normalize file relative to process.cwd().
    +
     2045.     12
    +
     2046.     12    process_argv.slice(2).some(function (arg) {
    +
     2047.     12        if (!arg.startsWith("-")) {
    +
     2048.     12            file = file || arg;
    +
     2049.     12            return true;
    +
     2050.     12        }
    +
     2051.     12    });
    +
     2052.     12    if (!file) {
    +
     2053.      1        return;
    +
     2054.     11    }
    +
     2055.     11    file = modulePath.resolve(file) + "/";
    +
     2056.     11    if (file.startsWith(process.cwd() + "/")) {
    +
     2057.     11        file = file.replace(process.cwd() + "/", "").slice(0, -1) || ".";
    +
     2058.     11    }
    +
     2059.     11    file = file.replace((
    +
     2060.     11        /\\/g
    +
     2061.     11    ), "/").replace((
    +
     2062.     11        /\/$/g
    +
     2063.     11    ), "");
    +
     2064.     11    if (source) {
    +
     2065.      7        data = source;
    +
     2066.      7    } else {
    +
     2067.      4
    +
     2068.      4// jslint_cli - jslint directory.
    +
     2069.      4
    +
     2070.      4        try {
    +
     2071.      4            data = await moduleFs.promises.readdir(file, "utf8");
    +
     2072.      4        } catch (ignore) {}
    +
     2073.      4        if (data) {
    +
     2074.     34            await Promise.all(data.map(async function (file2) {
    +
     2075.     34                let code;
    +
     2076.     34                let time_start = Date.now();
    +
     2077.     34                file2 = file + "/" + file2;
    +
     2078.     34                switch ((
    +
     2079.     34                    /\.\w+?$|$/m
    +
     2080.     34                ).exec(file2)[0]) {
    +
     2081.      4                case ".cjs":
    +
     2082.      5                case ".html":
    +
     2083.      8                case ".js":
    +
     2084.     10                case ".json":
    +
     2085.     12                case ".md":
    +
     2086.     14                case ".mjs":
    +
     2087.     16                case ".sh":
    +
     2088.     16                    break;
    +
     2089.     18                default:
    +
     2090.     18                    return;
    +
     2091.     16                }
    +
     2092.     16                try {
    +
     2093.     16                    code = await moduleFs.promises.readFile(file2, "utf8");
    +
     2094.     15                } catch (ignore) {
    +
     2095.      4                    return;
    +
     2096.     15                }
    +
     2097.     15                if (
    +
     2098.     15                    (
    +
     2099.     15                        /(?:\b|_)(?:lock|min|raw|rollup)(?:\b|_)/
    +
     2100.     15                    ).test(file2)
    +
     2101.     15                    || !(code && code.length < 1048576)
    +
     2102.      4                ) {
    +
     2103.      4                    return;
    +
     2104.     14                }
    +
     2105.     14                jslint_from_file({
    +
     2106.     14                    code,
    +
     2107.     14                    file: file2,
    +
     2108.     14                    option
    +
     2109.     14                });
    +
     2110.     14                console_error(
    +
     2111.     14                    "jslint - " + (Date.now() - time_start) + "ms - " + file2
    +
     2112.     14                );
    +
     2113.     14            }));
    +
     2114.      4            process_exit(exit_code);
    +
     2115.      4            return exit_code;
    +
     2116.      4        }
    +
     2117.      4
    +
     2118.      4// jslint_cli - jslint file.
    +
     2119.      4
    +
     2120.      4        try {
    +
     2121.      4            data = await moduleFs.promises.readFile(file, "utf8");
    +
     2122.      4        } catch (err) {
    +
     2123.      4            console_error(err);
    +
     2124.      4            exit_code = 1;
    +
     2125.      4            process_exit(exit_code);
    +
     2126.      4            return exit_code;
    +
     2127.      4        }
    +
     2128.      9    }
    +
     2129.      9    result = jslint_from_file({
    +
     2130.      9        code: data,
    +
     2131.      9        file,
    +
     2132.      9        option
    +
     2133.      9    });
    +
     2134.      9    if (mode_report) {
    +
     2135.      6        result = jslint.jslint_report(result);
    +
     2136.      6        result = `<body class="JSLINT_ JSLINT_REPORT_">\n${result}</body>\n`;
    +
     2137.      6        await fsWriteFileWithParents(mode_report, result);
    +
     2138.      9    }
    +
     2139.      9    process_exit(exit_code);
    +
     2140.      9    return exit_code;
    +
     2141.      9}
    +
     2142.      1
    +
     2143.    668function jslint_phase1_split() {
    +
     2144.    668
    +
     2145.    668// PHASE 1. Split <source> by newlines into <line_list>.
    +
     2146.    668
    +
     2147.    668    return;
    +
     2148.    668}
    +
     2149.      1
    +
     2150.    668function jslint_phase2_lex(state) {
    +
     2151.    668
    +
     2152.    668// PHASE 2. Lex <line_list> into <token_list>.
    +
     2153.    668
    +
     2154.    668    let {
    +
     2155.    668        artifact,
    +
     2156.    668        directive_list,
    +
     2157.    668        global_dict,
    +
     2158.    668        global_list,
    +
     2159.    668        line_list,
    +
     2160.    668        option_dict,
    +
     2161.    668        stop,
    +
     2162.    668        stop_at,
    +
     2163.    668        tenure,
    +
     2164.    668        test_cause,
    +
     2165.    668        token_global,
    +
     2166.    668        token_list,
    +
     2167.    668        warn,
    +
     2168.    668        warn_at
    +
     2169.    668    } = state;
    +
     2170.    668    let char;                   // The current character being lexed.
    +
     2171.    668    let column = 0;             // The column number of the next character.
    +
     2172.    668    let from;                   // The starting column number of the token.
    +
     2173.    668    let from_mega;              // The starting column of megastring.
    +
     2174.    668    let line = 0;               // The line number of the next character.
    +
     2175.    668    let line_disable;           // The starting line of "/*jslint-disable*/".
    +
     2176.    668    let line_mega;              // The starting line of megastring.
    +
     2177.    668    let line_source = "";       // The remaining line source string.
    +
     2178.    668    let line_whole = "";        // The whole line source string.
    +
     2179.    668    let mode_digits_empty_string = 1;
    +
     2180.    668    let mode_digits_numeric_separator = 2;
    +
     2181.    668    let mode_directive = true;  // true if directives are still allowed.
    +
     2182.    668    let mode_mega = false;      // true if currently parsing a megastring
    +
     2183.    668                                // ... literal.
    +
     2184.    668    let mode_regexp;            // true if regular expression literal seen on
    +
     2185.    668                                // ... this line.
    +
     2186.    668    let paren_backtrack_list = [];      // List of most recent "(" tokens at any
    +
     2187.    668                                        // ... paren-depth.
    +
     2188.    668    let paren_depth = 0;        // Keeps track of current paren-depth.
    +
     2189.    668    let snippet = "";           // A piece of string.
    +
     2190.    668    let token_1;                // The first token.
    +
     2191.    668    let token_prv = token_global;       // The previous token including
    +
     2192.    668                                        // ... comments.
    +
     2193.    668    let token_prv_expr = token_global;  // The previous token excluding
    +
     2194.    668                                        // ... comments.
    +
     2195.    668
    +
     2196.    668// Most tokens, including the identifiers, operators, and punctuators, can be
    +
     2197.    668// found with a regular expression. Regular expressions cannot correctly match
    +
     2198.    668// regular expression literals, so we will match those the hard way. String
    +
     2199.    668// literals and number literals can be matched by regular expressions, but they
    +
     2200.    668// don't provide good warnings. The functions char_after, char_before,
    +
     2201.    668// read_digits, and char_after_escape help in the parsing of literals.
    +
     2202.    668
    +
     2203. 238213    function char_after(match) {
    +
     2204. 238213
    +
     2205. 238213// Get the next character from the source line. Remove it from the line_source,
    +
     2206. 238213// and append it to the snippet. Optionally check that the previous character
    +
     2207. 238213// matched an expected value.
    +
     2208. 238213
    +
     2209.   5986        if (match !== undefined && char !== match) {
    +
     2210.     10
    +
     2211.     10// test_cause:
    +
     2212.     10// ["aa=/[", "char_after", "expected_a", "]", 5]
    +
     2213.     10// ["aa=/aa{/", "char_after", "expected_a_b", "/", 8]
    +
     2214.     10
    +
     2215.     10            return (
    +
     2216.     10                char === ""
    +
     2217.     10                ? stop_at("expected_a", line, column - 1, match)
    +
     2218.     10                : stop_at("expected_a_b", line, column, match, char)
    +
     2219.     10            );
    +
     2220. 238203        }
    +
     2221. 238203        char = line_source.slice(0, 1);
    +
     2222. 238203        line_source = line_source.slice(1);
    +
     2223. 238203        snippet += char || " ";
    +
     2224. 238213        column += 1;
    +
     2225. 238213        return char;
    +
     2226. 238213    }
    +
     2227.    668
    +
     2228.   2953    function char_after_escape(extra) {
    +
     2229.   2953
    +
     2230.   2953// Validate char after escape "\\".
    +
     2231.   2953
    +
     2232.   2953        char_after("\\");
    +
     2233.   2953        switch (char) {
    +
     2234.      1        case "":
    +
     2235.      1
    +
     2236.      1// test_cause:
    +
     2237.      1// ["\"\\", "char_after_escape", "unclosed_string", "", 2]
    +
     2238.      1
    +
     2239.      1            return stop_at("unclosed_string", line, column);
    +
     2240.    219        case "/":
    +
     2241.    219            return char_after();
    +
     2242.    355        case "\\":
    +
     2243.    355            return char_after();
    +
     2244.      1        case "`":
    +
     2245.      1            return char_after();
    +
     2246.     62        case "b":
    +
     2247.     62            return char_after();
    +
     2248.      6        case "f":
    +
     2249.      6            return char_after();
    +
     2250.   1015        case "n":
    +
     2251.   1015            return char_after();
    +
     2252.     32        case "r":
    +
     2253.     32            return char_after();
    +
     2254.     24        case "t":
    +
     2255.     24
    +
     2256.     24// test_cause:
    +
     2257.     24// ["\"\\/\\\\\\`\\b\\f\\n\\r\\t\"", "char_after_escape", "char_after", "", 0]
    +
     2258.     24
    +
     2259.     24            test_cause("char_after");
    +
     2260.     24            return char_after();
    +
     2261.    376        case "u":
    +
     2262.    376            if (char_after("u") === "{") {
    +
     2263.    376                if (state.mode_json) {
    +
     2264.    376
    +
     2265.    376// test_cause:
    +
     2266.    376// ["[\"\\u{12345}\"]", "char_after_escape", "unexpected_a", "{", 5]
    +
     2267.    376
    +
     2268.    376                    warn_at("unexpected_a", line, column, char);
    +
     2269.    376                }
    +
     2270.    376                if (read_digits("x", undefined) > 5) {
    +
     2271.    376
    +
     2272.    376// test_cause:
    +
     2273.    376// ["\"\\u{123456}\"", "char_after_escape", "too_many_digits", "", 11]
    +
     2274.    376
    +
     2275.    376                    warn_at("too_many_digits", line, column);
    +
     2276.    376                }
    +
     2277.    376                if (char !== "}") {
    +
     2278.    376
    +
     2279.    376// test_cause:
    +
     2280.    376// ["\"\\u{12345\"", "char_after_escape", "expected_a_before_b", "\"", 10]
    +
     2281.    376
    +
     2282.    376                    stop_at("expected_a_before_b", line, column, "}", char);
    +
     2283.    376                }
    +
     2284.    376                return char_after();
    +
     2285.    376            }
    +
     2286.    376            char_before();
    +
     2287.    376            if (read_digits("x", mode_digits_empty_string) < 4) {
    +
     2288.    376
    +
     2289.    376// test_cause:
    +
     2290.    376// ["\"\\u0\"", "char_after_escape", "expected_four_digits", "", 5]
    +
     2291.    376
    +
     2292.    376                warn_at("expected_four_digits", line, column);
    +
     2293.    376            }
    +
     2294.    376            return;
    +
     2295.    862        default:
    +
     2296.    862            if (extra && extra.indexOf(char) >= 0) {
    +
     2297.    862                return char_after();
    +
     2298.    862            }
    +
     2299.    862
    +
     2300.    862// test_cause:
    +
     2301.    862// ["\"\\0\"", "char_after_escape", "unexpected_a_before_b", "0", 3]
    +
     2302.    862
    +
     2303.    862            warn_at("unexpected_a_before_b", line, column, "\\", char);
    +
     2304.   2953        }
    +
     2305.   2953    }
    +
     2306.    668
    +
     2307.  10013    function char_before() {
    +
     2308.  10013
    +
     2309.  10013// Back up one character by moving a character from the end of the snippet to
    +
     2310.  10013// the front of the line_source.
    +
     2311.  10013
    +
     2312.  10013        char = snippet.slice(-1);
    +
     2313.  10013        line_source = char + line_source;
    +
     2314.  10013        column -= char.length;
    +
     2315.  10013
    +
     2316.  10013// Remove last character from snippet.
    +
     2317.  10013
    +
     2318.  10013        snippet = snippet.slice(0, -1);
    +
     2319.  10013        return char;
    +
     2320.  10013    }
    +
     2321.    668
    +
     2322.   8924    function check_numeric_separator(digits, column) {
    +
     2323.   8924
    +
     2324.   8924// This function will check for illegal numeric-separator in <digits>.
    +
     2325.   8924
    +
     2326.   8924        digits.replace((
    +
     2327.   8924            jslint_rgx_numeric_separator_illegal
    +
     2328.      6        ), function (ignore, ii) {
    +
     2329.      6
    +
     2330.      6// test_cause:
    +
     2331.      6// ["0x0_0_;", "check_numeric_separator", "illegal_num_separator", "", 6]
    +
     2332.      6// ["0x0_0__0;", "check_numeric_separator", "illegal_num_separator", "", 6]
    +
     2333.      6// ["aa=1_2_;", "check_numeric_separator", "illegal_num_separator", "", 7]
    +
     2334.      6// ["aa=1_2__3;", "check_numeric_separator", "illegal_num_separator", "", 7]
    +
     2335.      6// ["aa=1_2_n;", "check_numeric_separator", "illegal_num_separator", "", 7]
    +
     2336.      6
    +
     2337.      6            warn_at("illegal_num_separator", line, column + ii + 1);
    +
     2338.      6            return "";
    +
     2339.      6        });
    +
     2340.   8924    }
    +
     2341.    668
    +
     2342.  11221    function lex_comment() {
    +
     2343.  11221        let body;
    +
     2344.  11221        let ii = 0;
    +
     2345.  11221        let jj = 0;
    +
     2346.  11221        let the_comment;
    +
     2347.  11221
    +
     2348.  11221// Create a comment object. Comments are not allowed in JSON text. Comments can
    +
     2349.  11221// include directives and notices of incompletion.
    +
     2350.  11221
    +
     2351.  11221// Create token from comment //....
    +
     2352.  11221
    +
     2353.  11107        if (snippet === "//") {
    +
     2354.  11107            snippet = line_source;
    +
     2355.  11107            line_source = "";
    +
     2356.  11107            the_comment = token_create("(comment)", snippet);
    +
     2357.  11107            if (mode_mega) {
    +
     2358.  11107
    +
     2359.  11107// test_cause:
    +
     2360.  11107// ["`${//}`", "lex_comment", "unexpected_comment", "`", 4]
    +
     2361.  11107
    +
     2362.  11107                warn("unexpected_comment", the_comment, "`");
    +
     2363.  11107            }
    +
     2364.  11107
    +
     2365.  11107// Create token from comment /*...*/.
    +
     2366.  11107
    +
     2367.  11107        } else {
    +
     2368.    114            snippet = [];
    +
     2369.    114            if (line_source[0] === "/") {
    +
     2370.    114
    +
     2371.    114// test_cause:
    +
     2372.    114// ["/*/", "lex_comment", "unexpected_a", "/", 2]
    +
     2373.    114
    +
     2374.    114                warn_at("unexpected_a", line, column + ii, "/");
    +
     2375.    114            }
    +
     2376.    114
    +
     2377.    114// Lex/loop through each line until "*/".
    +
     2378.    114
    +
     2379.    696            while (true) {
    +
     2380.    696                // jslint_rgx_star_slash
    +
     2381.    696                ii = line_source.indexOf("*/");
    +
     2382.    696                if (ii >= 0) {
    +
     2383.    696                    break;
    +
     2384.    696                }
    +
     2385.    696                // jslint_rgx_slash_star
    +
     2386.    696                ii = line_source.indexOf("/*");
    +
     2387.    696                if (ii >= 0) {
    +
     2388.    696
    +
     2389.    696// test_cause:
    +
     2390.    696// ["/*/*", "lex_comment", "nested_comment", "", 2]
    +
     2391.    696
    +
     2392.    696                    warn_at("nested_comment", line, column + ii);
    +
     2393.    696                }
    +
     2394.    696                snippet.push(line_source);
    +
     2395.    696                line_source = read_line();
    +
     2396.    696                if (line_source === undefined) {
    +
     2397.    696
    +
     2398.    696// test_cause:
    +
     2399.    696// ["/*", "lex_comment", "unclosed_comment", "", 1]
    +
     2400.    696
    +
     2401.    696                    return stop_at("unclosed_comment", line, column);
    +
     2402.    696                }
    +
     2403.    696            }
    +
     2404.    114            jj = line_source.slice(0, ii).search(
    +
     2405.    114                jslint_rgx_slash_star_or_slash
    +
     2406.    114            );
    +
     2407.    114            if (jj >= 0) {
    +
     2408.    114
    +
     2409.    114// test_cause:
    +
     2410.    114// ["/*/**/", "lex_comment", "nested_comment", "", 2]
    +
     2411.    114
    +
     2412.    114                warn_at("nested_comment", line, column + jj);
    +
     2413.    114            }
    +
     2414.    114            snippet.push(line_source.slice(0, ii));
    +
     2415.    114            snippet = snippet.join(" ");
    +
     2416.    114            column += ii + 2;
    +
     2417.    114            line_source = line_source.slice(ii + 2);
    +
     2418.    114            the_comment = token_create("(comment)", snippet);
    +
     2419.  11218        }
    +
     2420.  11218
    +
     2421.  11218// Uncompleted work comment.
    +
     2422.  11218
    +
     2423.  11218        if (!option_dict.devel && jslint_rgx_todo.test(snippet)) {
    +
     2424.     16
    +
     2425.     16// test_cause:
    +
     2426.     16// ["//todo", "lex_comment", "todo_comment", "(comment)", 1] //jslint-ignore-line
    +
     2427.     16
    +
     2428.     16            warn("todo_comment", the_comment);
    +
     2429.  11218        }
    +
     2430.  11218
    +
     2431.  11218// Lex directives in comment.
    +
     2432.  11218
    +
     2433.  11218        [
    +
     2434.  11218            the_comment.directive, body
    +
     2435.  11218        ] = Array.from(snippet.match(jslint_rgx_directive) || []).slice(1);
    +
     2436.  11141        if (the_comment.directive === undefined) {
    +
     2437.  11141            return the_comment;
    +
     2438.  11141        }
    +
     2439.     77        directive_list.push(the_comment);
    +
     2440.     77        if (!mode_directive) {
    +
     2441.      1
    +
     2442.      1// test_cause:
    +
     2443.      1// ["0\n/*global aa*/", "lex_comment", "misplaced_directive_a", "global", 1]
    +
     2444.      1
    +
     2445.      1            warn_at("misplaced_directive_a", line, from, the_comment.directive);
    +
     2446.      1            return the_comment;
    +
     2447.     76        }
    +
     2448.     76
    +
     2449.     76// lex_directive();
    +
     2450.     76// JSLint recognizes three directives that can be encoded in comments. This
    +
     2451.     76// function processes one item, and calls itself recursively to process the
    +
     2452.     76// next one.
    +
     2453.     76
    +
     2454.     76// Lex/loop through each directive in /*...*/
    +
     2455.     76
    +
     2456.     76        ii = 0;
    +
     2457.   1835        body.replace(jslint_rgx_directive_part, function (
    +
     2458.   1835            match0,
    +
     2459.   1835            key,
    +
     2460.   1835            val,
    +
     2461.   1835            jj
    +
     2462.   1835        ) {
    +
     2463.     76            if (ii !== jj) {
    +
     2464.     76
    +
     2465.     76// test_cause:
    +
     2466.     76// ["/*jslint !*/", "lex_comment", "bad_directive_a", "!", 1]
    +
     2467.     76
    +
     2468.     76                return stop("bad_directive_a", the_comment, body.slice(ii));
    +
     2469.   1834            }
    +
     2470.   1834            if (match0 === "") {
    +
     2471.     76                return "";
    +
     2472.   1759            }
    +
     2473.   1759            ii += match0.length;
    +
     2474.   1759            switch (the_comment.directive) {
    +
     2475.   1759            case "global":
    +
     2476.     76                if (val) {
    +
     2477.     76
    +
     2478.     76// test_cause:
    +
     2479.     76// ["/*global aa:false*/", "lex_comment", "bad_option_a", "aa:false", 1]
    +
     2480.     76
    +
     2481.     76                    warn("bad_option_a", the_comment, key + ":" + val);
    +
     2482.     76                }
    +
     2483.     76                global_dict[key] = "user-defined";
    +
     2484.     76
    +
     2485.     76// PR-347 - Disable warning "unexpected_directive_a".
    +
     2486.     76//
    +
     2487.     76//                 state.mode_module = the_comment;
    +
     2488.     76
    +
     2489.     76                break;
    +
     2490.     99            case "jslint":
    +
     2491.     99                if (!option_set_item(key, val !== "false")) {
    +
     2492.     99
    +
     2493.     99// test_cause:
    +
     2494.     99// ["/*jslint undefined*/", "lex_comment", "bad_option_a", "undefined", 1]
    +
     2495.     99
    +
     2496.     99                    warn("bad_option_a", the_comment, key);
    +
     2497.     99                }
    +
     2498.     99                break;
    +
     2499.   1651            case "property":
    +
     2500.   1651                state.mode_property = true;
    +
     2501.   1651                tenure[key] = true;
    +
     2502.   1651                break;
    +
     2503.   1759            }
    +
     2504.   1759            return "";
    +
     2505.   1759        });
    +
     2506.     76        return the_comment;
    +
     2507.     76    }
    +
     2508.    668
    +
     2509.    789    function lex_megastring() {
    +
     2510.    789        let id;
    +
     2511.    789        let match;
    +
     2512.    789
    +
     2513.    789// The token is a megastring. We don't allow any kind of mega nesting.
    +
     2514.    789
    +
     2515.      1        if (mode_mega) {
    +
     2516.      1
    +
     2517.      1// test_cause:
    +
     2518.      1// ["`${`", "lex_megastring", "expected_a_b", "`", 4]
    +
     2519.      1
    +
     2520.      1            return stop_at("expected_a_b", line, column, "}", "`");
    +
     2521.    788        }
    +
     2522.    788        from_mega = from;
    +
     2523.    788        line_mega = line;
    +
     2524.    788        mode_mega = true;
    +
     2525.    788        snippet = "";
    +
     2526.    788
    +
     2527.    788// Parsing a mega literal is tricky. First create a ` token.
    +
     2528.    788
    +
     2529.    788        token_create("`");
    +
     2530.    788        from += 1;
    +
     2531.    788
    +
     2532.    788// Then loop, building up a string, possibly from many lines, until seeing
    +
     2533.    788// the end of file, a closing `, or a ${ indicting an expression within the
    +
     2534.    788// string.
    +
     2535.    788
    +
     2536.   5963        while (true) {
    +
     2537.   5963            match = line_source.match(jslint_rgx_mega) || {
    +
     2538.   5963                "0": "",
    +
     2539.   5963                index: 0
    +
     2540.   5963            };
    +
     2541.   5963            snippet += line_source.slice(0, match.index);
    +
     2542.   5963            column += match.index;
    +
     2543.   5963            line_source = line_source.slice(match.index);
    +
     2544.   5963            match = match[0];
    +
     2545.   5963            switch (match) {
    +
     2546.   5963            case "${":
    +
     2547.   5963
    +
     2548.   5963// if either ` or ${ was found, then the preceding joins the snippet to become
    +
     2549.   5963// a string token.
    +
     2550.   5963
    +
     2551.   5963                token_create("(string)", snippet).quote = "`";
    +
     2552.   5963                snippet = "";
    +
     2553.   5963
    +
     2554.   5963// If ${, then create tokens that will become part of an expression until
    +
     2555.   5963// a } token is made.
    +
     2556.   5963
    +
     2557.   5963                column += 2;
    +
     2558.   5963                token_create("${");
    +
     2559.   5963                line_source = line_source.slice(2);
    +
     2560.   5963
    +
     2561.   5963// Lex/loop through each token inside megastring-expression `${...}`.
    +
     2562.   5963
    +
     2563.   5963                while (true) {
    +
     2564.   5963                    id = lex_token().id;
    +
     2565.   5963                    if (id === "{") {
    +
     2566.   5963
    +
     2567.   5963// test_cause:
    +
     2568.   5963// ["`${{", "lex_megastring", "expected_a_b", "{", 4]
    +
     2569.   5963
    +
     2570.   5963                        return stop_at("expected_a_b", line, column, "}", "{");
    +
     2571.   5963                    }
    +
     2572.   5963                    if (id === "}") {
    +
     2573.   5963                        break;
    +
     2574.   5963                    }
    +
     2575.   5963                }
    +
     2576.   5963                break;
    +
     2577.   5963            case "\\":
    +
     2578.   5963                snippet += line_source.slice(0, 2);
    +
     2579.   5963                line_source = line_source.slice(2);
    +
     2580.   5963                column += 2;
    +
     2581.   5963                break;
    +
     2582.   5963            case "`":
    +
     2583.   5963
    +
     2584.   5963// if either ` or ${ was found, then the preceding joins the snippet to become
    +
     2585.   5963// a string token.
    +
     2586.   5963
    +
     2587.   5963                token_create("(string)", snippet).quote = "`";
    +
     2588.   5963                snippet = "";
    +
     2589.   5963
    +
     2590.   5963// Terminate megastring with `.
    +
     2591.   5963
    +
     2592.   5963                line_source = line_source.slice(1);
    +
     2593.   5963                column += 1;
    +
     2594.   5963                mode_mega = false;
    +
     2595.   5963                return token_create("`");
    +
     2596.   5963            default:
    +
     2597.   5963
    +
     2598.   5963// If neither ` nor ${ is seen, then the whole line joins the snippet.
    +
     2599.   5963
    +
     2600.   5963                snippet += line_source + "\n";
    +
     2601.   5963                if (read_line() === undefined) {
    +
     2602.   5963
    +
     2603.   5963// test_cause:
    +
     2604.   5963// ["`", "lex_megastring", "unclosed_mega", "", 1]
    +
     2605.   5963
    +
     2606.   5963                    return stop_at("unclosed_mega", line_mega, from_mega);
    +
     2607.   5963                }
    +
     2608.   5963            }
    +
     2609.   5963        }
    +
     2610.    789    }
    +
     2611.    668
    +
     2612.   8851    function lex_number() {
    +
     2613.   8851        let prefix = snippet;
    +
     2614.   8851
    +
     2615.   8851// PR-390 - Add numeric-separator check.
    +
     2616.   8851
    +
     2617.   8851        check_numeric_separator(prefix, column - prefix.length);
    +
     2618.   8851        char_after();
    +
     2619.   2880        switch (prefix === "0" && char) {
    +
     2620.      3        case "b":
    +
     2621.      6        case "o":
    +
     2622.     33        case "x":
    +
     2623.     33            read_digits(char, mode_digits_numeric_separator);
    +
     2624.     33
    +
     2625.     33// PR-351 - Ignore BigInt suffix 'n'.
    +
     2626.     33
    +
     2627.     33            if (char === "n") {
    +
     2628.     33                char_after("n");
    +
     2629.     33            }
    +
     2630.     33            break;
    +
     2631.   8818        default:
    +
     2632.   8818            if (char === ".") {
    +
     2633.   8818                read_digits("d", mode_digits_numeric_separator);
    +
     2634.   8818            }
    +
     2635.   8818            if (char === "E" || char === "e") {
    +
     2636.   8818                char_after(char);
    +
     2637.   8818                if (char !== "+" && char !== "-") {
    +
     2638.   8818                    char_before();
    +
     2639.   8818                }
    +
     2640.   8818                read_digits("d", mode_digits_numeric_separator);
    +
     2641.   8818            }
    +
     2642.   8851        }
    +
     2643.   8851
    +
     2644.   8851// If the next character after a number is a digit or letter, then something
    +
     2645.   8851// unexpected is going on.
    +
     2646.   8851
    +
     2647.   8851        if (
    +
     2648.   1567            (char >= "0" && char <= "9")
    +
     2649.     44            || (char >= "a" && char <= "z")
    +
     2650.   8850            || (char >= "A" && char <= "Z")
    +
     2651.      1        ) {
    +
     2652.      1
    +
     2653.      1// test_cause:
    +
     2654.      1// ["0a", "lex_number", "unexpected_a_after_b", "0", 2]
    +
     2655.      1
    +
     2656.      1            return stop_at(
    +
     2657.      1                "unexpected_a_after_b",
    +
     2658.      1                line,
    +
     2659.      1                column,
    +
     2660.      1                snippet.slice(-1),
    +
     2661.      1                snippet.slice(0, -1)
    +
     2662.      1            );
    +
     2663.   8850        }
    +
     2664.   8850        char_before();
    +
     2665.   8850        return token_create("(number)", snippet);
    +
     2666.   8850    }
    +
     2667.    668
    +
     2668.    583    function lex_regexp() {
    +
     2669.    583
    +
     2670.    583// Regexp
    +
     2671.    583// Lex a regular expression literal.
    +
     2672.    583
    +
     2673.    583        let flag;
    +
     2674.    583        let mode_regexp_multiline;
    +
     2675.    583        let result;
    +
     2676.    583        let value;
    +
     2677.    583        mode_regexp = true;
    +
     2678.    583
    +
     2679.    209        function lex_regexp_bracketed() {
    +
     2680.    209            let mode_regexp_range;
    +
     2681.    209
    +
     2682.    209// RegExp
    +
     2683.    209// Match a class.
    +
     2684.    209
    +
     2685.    209            char_after("[");
    +
     2686.     35            if (char === "^") {
    +
     2687.     35                char_after("^");
    +
     2688.     35            }
    +
     2689.    902            while (true) {
    +
     2690.    902
    +
     2691.    902// RegExp
    +
     2692.    902// Match a character in a character class.
    +
     2693.    902
    +
     2694.    902                switch (char) {
    +
     2695.    902                case "":
    +
     2696.    902                case "]":
    +
     2697.    902
    +
     2698.    902// test_cause:
    +
     2699.    902// ["aa=/[", "lex_regexp_bracketed", "closer", "", 0]
    +
     2700.    902// ["aa=/[]/", "lex_regexp_bracketed", "closer", "", 0]
    +
     2701.    902
    +
     2702.    902                    test_cause("closer");
    +
     2703.    902                    if (mode_regexp_range) {
    +
     2704.    902
    +
     2705.    902// test_cause:
    +
     2706.    902// ["aa=/[0-]/", "lex_regexp_bracketed", "unexpected_a", "-", 7]
    +
     2707.    902
    +
     2708.    902                        warn_at("unexpected_a", line, column - 1, "-");
    +
     2709.    902                    }
    +
     2710.    902                    return char_after("]");
    +
     2711.    902
    +
     2712.    902// PR-362 - Relax regexp-warning against using <space>.
    +
     2713.    902//
    +
     2714.    902//                 case " ":
    +
     2715.    902//
    +
     2716.    902// // test_cause:
    +
     2717.    902// // ["aa=/[ ]/", "lex_regexp_bracketed", "expected_a_b", " ", 6]
    +
     2718.    902//
    +
     2719.    902//                     warn_at("expected_a_b", line, column, "\\u0020", " ");
    +
     2720.    902//                     break;
    +
     2721.    902
    +
     2722.    902                case "-":
    +
     2723.    902                case "/":
    +
     2724.    902                case "[":
    +
     2725.    902                case "^":
    +
     2726.    902
    +
     2727.    902// test_cause:
    +
     2728.    902// ["aa=/[-]/", "lex_regexp_bracketed", "expected_a_before_b", "-", 6]
    +
     2729.    902// ["aa=/[.^]/", "lex_regexp_bracketed", "expected_a_before_b", "^", 7]
    +
     2730.    902// ["aa=/[/", "lex_regexp_bracketed", "expected_a_before_b", "/", 6]
    +
     2731.    902// ["aa=/[\\\\/]/", "lex_regexp_bracketed", "expected_a_before_b", "/", 8]
    +
     2732.    902// ["aa=/[\\\\[]/", "lex_regexp_bracketed", "expected_a_before_b", "[", 8]
    +
     2733.    902
    +
     2734.    902                    warn_at("expected_a_before_b", line, column, "\\", char);
    +
     2735.    902                    break;
    +
     2736.    902                case "\\":
    +
     2737.    902                    char_after_escape("BbDdSsWw-[]^");
    +
     2738.    902                    char_before();
    +
     2739.    902                    break;
    +
     2740.    902                case "`":
    +
     2741.    902                    if (mode_mega) {
    +
     2742.    902
    +
     2743.    902// test_cause:
    +
     2744.    902// ["`${/[`]/}`", "lex_regexp_bracketed", "unexpected_a", "`", 6]
    +
     2745.    902
    +
     2746.    902                        warn_at("unexpected_a", line, column, "`");
    +
     2747.    902                    }
    +
     2748.    902                    break;
    +
     2749.    902                }
    +
     2750.    902                char_after();
    +
     2751.    902                mode_regexp_range = false;
    +
     2752.    902                if (char === "-") {
    +
     2753.    902
    +
     2754.    902// RegExp
    +
     2755.    902// Match a range of subclasses.
    +
     2756.    902
    +
     2757.    902                    mode_regexp_range = true;
    +
     2758.    902                    char_after("-");
    +
     2759.    902                }
    +
     2760.    902            }
    +
     2761.    209        }
    +
     2762.    583
    +
     2763.    794        function lex_regexp_group() {
    +
     2764.    794
    +
     2765.    794// RegExp
    +
     2766.    794// Lex sequence of characters in regexp.
    +
     2767.    794
    +
     2768.    794            switch (char) {
    +
     2769.      1            case "":
    +
     2770.      1                warn_at("expected_regexp_factor_a", line, column, char);
    +
     2771.      1                break;
    +
     2772.      1            case ")":
    +
     2773.      1                warn_at("expected_regexp_factor_a", line, column, char);
    +
     2774.      1                break;
    +
     2775.      1            case "]":
    +
     2776.      1
    +
     2777.      1// test_cause:
    +
     2778.      1// ["/ /", "lex_regexp_group", "expected_regexp_factor_a", "", 3]
    +
     2779.      1// ["aa=/)", "lex_regexp_group", "expected_regexp_factor_a", ")", 5]
    +
     2780.      1// ["aa=/]", "lex_regexp_group", "expected_regexp_factor_a", "]", 5]
    +
     2781.      1
    +
     2782.      1                warn_at("expected_regexp_factor_a", line, column, char);
    +
     2783.      1                break;
    +
     2784.    794            }
    +
     2785.   5592            while (true) {
    +
     2786.   5592                switch (char) {
    +
     2787.   5592                case "":
    +
     2788.   5592                case ")":
    +
     2789.   5592                case "/":
    +
     2790.   5592                case "]":
    +
     2791.   5592                    return;
    +
     2792.   5592
    +
     2793.   5592// PR-362 - Relax regexp-warning against using <space>.
    +
     2794.   5592//
    +
     2795.   5592//                 case " ":
    +
     2796.   5592//
    +
     2797.   5592// // test_cause:
    +
     2798.   5592// // ["aa=/ /", "lex_regexp_group", "expected_a_b", " ", 5]
    +
     2799.   5592//
    +
     2800.   5592//                     warn_at("expected_a_b", line, column, "\\s", " ");
    +
     2801.   5592//                     char_after();
    +
     2802.   5592//                     break;
    +
     2803.   5592
    +
     2804.   5592                case "$":
    +
     2805.   5592                    if (line_source[0] !== "/") {
    +
     2806.   5592                        mode_regexp_multiline = true;
    +
     2807.   5592                    }
    +
     2808.   5592                    char_after();
    +
     2809.   5592                    break;
    +
     2810.   5592                case "(":
    +
     2811.   5592
    +
     2812.   5592// RegExp
    +
     2813.   5592// Match a group that starts with left paren.
    +
     2814.   5592
    +
     2815.   5592                    char_after("(");
    +
     2816.   5592                    switch (char) {
    +
     2817.   5592                    case ":":
    +
     2818.   5592
    +
     2819.   5592// test_cause:
    +
     2820.   5592// ["aa=/(:)/", "lex_regexp_group", "expected_a_before_b", ":", 6]
    +
     2821.   5592// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5]
    +
     2822.   5592
    +
     2823.   5592                        warn_at("expected_a_before_b", line, column, "?", ":");
    +
     2824.   5592                        break;
    +
     2825.   5592                    case "?":
    +
     2826.   5592                        char_after("?");
    +
     2827.   5592                        switch (char) {
    +
     2828.   5592                        case "!":
    +
     2829.   5592
    +
     2830.   5592// PR-437 - Add grammar for regexp-named-capture-group.
    +
     2831.   5592
    +
     2832.   5592                        case "<":
    +
     2833.   5592                        case "=":
    +
     2834.   5592                            char_after();
    +
     2835.   5592                            break;
    +
     2836.   5592                        default:
    +
     2837.   5592                            char_after(":");
    +
     2838.   5592                        }
    +
     2839.   5592                        break;
    +
     2840.   5592                    }
    +
     2841.   5592
    +
     2842.   5592// RegExp
    +
     2843.   5592// Recurse lex_regexp_group().
    +
     2844.   5592
    +
     2845.   5592                    lex_regexp_group();
    +
     2846.   5592                    char_after(")");
    +
     2847.   5592                    break;
    +
     2848.   5592                case "*":
    +
     2849.   5592                case "+":
    +
     2850.   5592                case "?":
    +
     2851.   5592                case "{":
    +
     2852.   5592                case "}":
    +
     2853.   5592
    +
     2854.   5592// test_cause:
    +
     2855.   5592// ["aa=/+/", "lex_regexp_group", "expected_a_before_b", "+", 5]
    +
     2856.   5592// ["aa=/.**/", "lex_regexp_group", "expected_a_before_b", "*", 7]
    +
     2857.   5592// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5]
    +
     2858.   5592// ["aa=/{/", "lex_regexp_group", "expected_a_before_b", "{", 5]
    +
     2859.   5592// ["aa=/}/", "lex_regexp_group", "expected_a_before_b", "}", 5]
    +
     2860.   5592
    +
     2861.   5592                    warn_at("expected_a_before_b", line, column, "\\", char);
    +
     2862.   5592                    char_after();
    +
     2863.   5592                    break;
    +
     2864.   5592                case "[":
    +
     2865.   5592                    lex_regexp_bracketed();
    +
     2866.   5592                    break;
    +
     2867.   5592                case "\\":
    +
     2868.   5592
    +
     2869.   5592// test_cause:
    +
     2870.   5592// ["aa=/\\/", "lex_regexp_group", "escape", "", 0]
    +
     2871.   5592
    +
     2872.   5592                    test_cause("escape");
    +
     2873.   5592
    +
     2874.   5592// PR-437 - Add grammar for regexp-named-backreference.
    +
     2875.   5592
    +
     2876.   5592                    char_after_escape("BbDdSsWw^${}[]():=!.|*+?k");
    +
     2877.   5592                    break;
    +
     2878.   5592                case "^":
    +
     2879.   5592                    if (snippet !== "^") {
    +
     2880.   5592                        mode_regexp_multiline = true;
    +
     2881.   5592                    }
    +
     2882.   5592                    char_after();
    +
     2883.   5592                    break;
    +
     2884.   5592                case "`":
    +
     2885.   5592                    if (mode_mega) {
    +
     2886.   5592
    +
     2887.   5592// test_cause:
    +
     2888.   5592// ["`${/`/}`", "lex_regexp_group", "unexpected_a", "`", 5]
    +
     2889.   5592
    +
     2890.   5592                        warn_at("unexpected_a", line, column, "`");
    +
     2891.   5592                    }
    +
     2892.   5592                    char_after();
    +
     2893.   5592                    break;
    +
     2894.   5592                default:
    +
     2895.   5592                    char_after();
    +
     2896.   5592                }
    +
     2897.   5592
    +
     2898.   5592// RegExp
    +
     2899.   5592// Match an optional quantifier.
    +
     2900.   5592
    +
     2901.   5592                switch (char) {
    +
     2902.   5592                case "*":
    +
     2903.   5592                case "+":
    +
     2904.   5592                    if (char_after(char) === "?") {
    +
     2905.   5592
    +
     2906.   5592// test_cause:
    +
     2907.   5592// ["aa=/.*?/", "lex_regexp_group", "?", "", 0]
    +
     2908.   5592// ["aa=/.+?/", "lex_regexp_group", "?", "", 0]
    +
     2909.   5592
    +
     2910.   5592                        test_cause("?");
    +
     2911.   5592                        char_after("?");
    +
     2912.   5592                    }
    +
     2913.   5592                    break;
    +
     2914.   5592                case "?":
    +
     2915.   5592                    if (char_after("?") === "?") {
    +
     2916.   5592
    +
     2917.   5592// test_cause:
    +
     2918.   5592// ["aa=/.??/", "lex_regexp_group", "unexpected_a", "?", 7]
    +
     2919.   5592
    +
     2920.   5592                        warn_at("unexpected_a", line, column, char);
    +
     2921.   5592                        char_after("?");
    +
     2922.   5592                    }
    +
     2923.   5592                    break;
    +
     2924.   5592                case "{":
    +
     2925.   5592                    if (read_digits("d", mode_digits_empty_string) === 0) {
    +
     2926.   5592
    +
     2927.   5592// test_cause:
    +
     2928.   5592// ["aa=/aa{/", "lex_regexp_group", "expected_a_before_b", ",", 8]
    +
     2929.   5592
    +
     2930.   5592                        warn_at("expected_a_before_b", line, column, "0", ",");
    +
     2931.   5592                    }
    +
     2932.   5592                    if (char === ",") {
    +
     2933.   5592
    +
     2934.   5592// test_cause:
    +
     2935.   5592// ["aa=/.{,/", "lex_regexp_group", "comma", "", 0]
    +
     2936.   5592
    +
     2937.   5592                        test_cause("comma");
    +
     2938.   5592                        read_digits("d", mode_digits_empty_string);
    +
     2939.   5592                    }
    +
     2940.   5592                    if (char_after("}") === "?") {
    +
     2941.   5592
    +
     2942.   5592// test_cause:
    +
     2943.   5592// ["aa=/.{0}?/", "lex_regexp_group", "unexpected_a", "?", 9]
    +
     2944.   5592
    +
     2945.   5592                        warn_at("unexpected_a", line, column, char);
    +
     2946.   5592                        char_after("?");
    +
     2947.   5592                    }
    +
     2948.   5592                    break;
    +
     2949.   5592                }
    +
     2950.   5592            }
    +
     2951.    794        }
    +
     2952.    583
    +
     2953.    583// RegExp
    +
     2954.    583// Scan the regexp literal. Give a warning if the first character is = because
    +
     2955.    583// /= looks like a division assignment operator.
    +
     2956.    583
    +
     2957.    583        snippet = "";
    +
     2958.    583        char_after();
    +
     2959.      1        if (char === "=") {
    +
     2960.      1
    +
     2961.      1// test_cause:
    +
     2962.      1// ["aa=/=/", "lex_regexp", "expected_a_before_b", "=", 5]
    +
     2963.      1
    +
     2964.      1            warn_at("expected_a_before_b", line, column, "\\", "=");
    +
     2965.      1        }
    +
     2966.    583        lex_regexp_group();
    +
     2967.    583
    +
     2968.    583// RegExp
    +
     2969.    583// Remove last character from snippet.
    +
     2970.    583
    +
     2971.    583        snippet = snippet.slice(0, -1);
    +
     2972.    583
    +
     2973.    583// RegExp
    +
     2974.    583// Make sure there is a closing slash.
    +
     2975.    583
    +
     2976.    583        value = snippet;
    +
     2977.    583        char_after("/");
    +
     2978.    583
    +
     2979.    583// RegExp
    +
     2980.    583// Create flag.
    +
     2981.    583
    +
     2982.    583        flag = empty();
    +
     2983.    583        while (
    +
     2984.    583
    +
     2985.    583// Regexp
    +
     2986.    583// char is a letter.
    +
     2987.    583
    +
     2988.    512            (char >= "a" && char <= "z\uffff")
    +
     2989.    573            || (char >= "A" && char <= "Z\uffff")
    +
     2990.    510        ) {
    +
     2991.    510
    +
     2992.    510// RegExp
    +
     2993.    510// Process dangling flag letters.
    +
     2994.    510
    +
     2995.    510            switch (!flag[char] && char) {
    +
     2996.    510            case "g":
    +
     2997.    510                break;
    +
     2998.    510            case "i":
    +
     2999.    510                break;
    +
     3000.    510            case "m":
    +
     3001.    510                break;
    +
     3002.    510            case "u":
    +
     3003.    510                break;
    +
     3004.    510            case "y":
    +
     3005.    510
    +
     3006.    510// test_cause:
    +
     3007.    510// ["aa=/./gimuy", "lex_regexp", "flag", "", 0]
    +
     3008.    510
    +
     3009.    510                test_cause("flag");
    +
     3010.    510                break;
    +
     3011.    510            default:
    +
     3012.    510
    +
     3013.    510// test_cause:
    +
     3014.    510// ["aa=/./gg", "lex_regexp", "unexpected_a", "g", 8]
    +
     3015.    510// ["aa=/./z", "lex_regexp", "unexpected_a", "z", 7]
    +
     3016.    510
    +
     3017.    510                warn_at("unexpected_a", line, column, char);
    +
     3018.    510            }
    +
     3019.    510            flag[char] = true;
    +
     3020.    510            char_after();
    +
     3021.    573        }
    +
     3022.    573        char_before();
    +
     3023.    573        if (char === "/" || char === "*") {
    +
     3024.      1
    +
     3025.      1// test_cause:
    +
     3026.      1// ["aa=/.//", "lex_regexp", "unexpected_a", "/", 3]
    +
     3027.      1
    +
     3028.      1            return stop_at("unexpected_a", line, from, char);
    +
     3029.    572        }
    +
     3030.    572        result = token_create("(regexp)", char);
    +
     3031.    572        result.flag = flag;
    +
     3032.    572        result.value = value;
    +
     3033.    572        if (mode_regexp_multiline && !flag.m) {
    +
     3034.      1
    +
     3035.      1// test_cause:
    +
     3036.      1// ["aa=/$^/", "lex_regexp", "missing_m", "", 7]
    +
     3037.      1
    +
     3038.      1            warn_at("missing_m", line, column);
    +
     3039.    572        }
    +
     3040.    572        return result;
    +
     3041.    572    }
    +
     3042.    668
    +
     3043.    598    function lex_slash_or_regexp() {
    +
     3044.    598
    +
     3045.    598// The / can be a division operator or the beginning of a regular expression
    +
     3046.    598// literal. It is not possible to know which without doing a complete parse.
    +
     3047.    598// We want to complete the tokenization before we begin to parse, so we will
    +
     3048.    598// estimate. This estimator can fail in some cases. For example, it cannot
    +
     3049.    598// know if "}" is ending a block or ending an object literal, so it can
    +
     3050.    598// behave incorrectly in that case; it is not meaningful to divide an
    +
     3051.    598// object, so it is likely that we can get away with it. We avoided the worst
    +
     3052.    598// cases by eliminating automatic semicolon insertion.
    +
     3053.    598
    +
     3054.    598        let the_token;
    +
     3055.    598        switch (
    +
     3056.    598            token_prv_expr.identifier
    +
     3057.     18            && !token_prv_expr.dot
    +
     3058.     15            && token_prv_expr.id
    +
     3059.    598        ) {
    +
     3060.      1        case "case":
    +
     3061.      2        case "delete":
    +
     3062.      3        case "in":
    +
     3063.      4        case "instanceof":
    +
     3064.      5        case "new":
    +
     3065.      6        case "typeof":
    +
     3066.      7        case "void":
    +
     3067.      8        case "yield":
    +
     3068.      8            the_token = lex_regexp();
    +
     3069.      8
    +
     3070.      8// test_cause:
    +
     3071.      8// ["case /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6]
    +
     3072.      8// ["delete /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8]
    +
     3073.      8// ["in /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 4]
    +
     3074.      8// ["instanceof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 12]
    +
     3075.      8// ["new /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 5]
    +
     3076.      8// ["typeof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8]
    +
     3077.      8// ["void /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6]
    +
     3078.      8// ["yield /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 7]
    +
     3079.      8
    +
     3080.      8            return stop("unexpected_a", the_token);
    +
     3081.      1        case "return":
    +
     3082.      1            return lex_regexp();
    +
     3083.    589        }
    +
     3084.    589        switch (!token_prv_expr.identifier && token_prv_expr.id.slice(-1)) {
    +
     3085.      1        case "!":
    +
     3086.      2        case "%":
    +
     3087.      4        case "&":
    +
     3088.      5        case "*":
    +
     3089.      6        case "+":
    +
     3090.      7        case "-":
    +
     3091.      9        case "/":
    +
     3092.     10        case ";":
    +
     3093.     11        case "<":
    +
     3094.     12        case ">":
    +
     3095.     13        case "^":
    +
     3096.     16        case "{":
    +
     3097.     17        case "|":
    +
     3098.     18        case "}":
    +
     3099.     19        case "~":
    +
     3100.     19            the_token = lex_regexp();
    +
     3101.     19
    +
     3102.     19// test_cause:
    +
     3103.     19// ["!/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3104.     19// ["%/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3105.     19// ["&/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3106.     19// ["+/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3107.     19// ["-/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3108.     19// ["0 * /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5]
    +
     3109.     19// ["0 / /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5]
    +
     3110.     19// [";/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3111.     19// ["</./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3112.     19// [">/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3113.     19// ["^/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3114.     19// ["{/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3115.     19// ["|/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3116.     19// ["}/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3117.     19// ["~/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3118.     19
    +
     3119.     19            warn("wrap_regexp", the_token);
    +
     3120.     19            return the_token;
    +
     3121.    514        case "(":
    +
     3122.    515        case ",":
    +
     3123.    516        case ":":
    +
     3124.    553        case "=":
    +
     3125.    554        case "?":
    +
     3126.    555        case "[":
    +
     3127.    555
    +
     3128.    555// test_cause:
    +
     3129.    555// ["(/./", "lex_slash_or_regexp", "recurse", "", 0]
    +
     3130.    555// [",/./", "lex_slash_or_regexp", "recurse", "", 0]
    +
     3131.    555// [":/./", "lex_slash_or_regexp", "recurse", "", 0]
    +
     3132.    555// ["=/./", "lex_slash_or_regexp", "recurse", "", 0]
    +
     3133.    555// ["?/./", "lex_slash_or_regexp", "recurse", "", 0]
    +
     3134.    555// ["aa[/./", "lex_slash_or_regexp", "recurse", "", 0]
    +
     3135.    555
    +
     3136.    555            test_cause("recurse");
    +
     3137.    555            return lex_regexp();
    +
     3138.     15        }
    +
     3139.     15        if (line_source[0] === "=") {
    +
     3140.      1            column += 1;
    +
     3141.      1            line_source = line_source.slice(1);
    +
     3142.      1            snippet = "/=";
    +
     3143.      1            warn_at("unexpected_a", line, column, "/=");
    +
     3144.     15        }
    +
     3145.     15        return token_create(snippet);
    +
     3146.     15    }
    +
     3147.    668
    +
     3148.  24335    function lex_string(quote) {
    +
     3149.  24335
    +
     3150.  24335// Create a string token.
    +
     3151.  24335
    +
     3152.  24335        let the_token;
    +
     3153.  24333        if (!option_dict.single && quote === "'") {
    +
     3154.      2
    +
     3155.      2// test_cause:
    +
     3156.      2// ["''", "lex_string", "use_double", "", 1]
    +
     3157.      2
    +
     3158.      2            warn_at("use_double", line, column);
    +
     3159.      2        }
    +
     3160.  24335        snippet = "";
    +
     3161.  24335        char_after();
    +
     3162.  24335
    +
     3163.  24335// Lex/loop through each character in "...".
    +
     3164.  24335
    +
     3165. 217055        while (true) {
    +
     3166. 217055            switch (char) {
    +
     3167. 217055            case "":
    +
     3168. 217055
    +
     3169. 217055// test_cause:
    +
     3170. 217055// ["\"", "lex_string", "unclosed_string", "", 1]
    +
     3171. 217055
    +
     3172. 217055                return stop_at("unclosed_string", line, column);
    +
     3173. 217055            case "\\":
    +
     3174. 217055                char_after_escape(quote);
    +
     3175. 217055                break;
    +
     3176. 217055            case "`":
    +
     3177. 217055                if (mode_mega) {
    +
     3178. 217055
    +
     3179. 217055// test_cause:
    +
     3180. 217055// ["`${\"`\"}`", "lex_string", "unexpected_a", "`", 5]
    +
     3181. 217055
    +
     3182. 217055                    warn_at("unexpected_a", line, column, "`");
    +
     3183. 217055                }
    +
     3184. 217055                char_after("`");
    +
     3185. 217055                break;
    +
     3186. 217055            case quote:
    +
     3187. 217055
    +
     3188. 217055// Remove last character from snippet.
    +
     3189. 217055
    +
     3190. 217055                snippet = snippet.slice(0, -1);
    +
     3191. 217055                the_token = token_create("(string)", snippet);
    +
     3192. 217055                the_token.quote = quote;
    +
     3193. 217055                return the_token;
    +
     3194. 217055            default:
    +
     3195. 217055                char_after();
    +
     3196. 217055            }
    +
     3197. 217055        }
    +
     3198.  24335    }
    +
     3199.    668
    +
     3200. 252255    function lex_token() {
    +
     3201. 252255        let match;
    +
     3202. 252255
    +
     3203. 252255// Lex/loop through each whitespace.
    +
     3204. 252255
    +
     3205. 376039        while (true) {
    +
     3206. 376039
    +
     3207. 376039// Lex/loop through each blank-line.
    +
     3208. 376039
    +
     3209. 376039            while (!line_source) {
    +
     3210. 376039                line_source = read_line();
    +
     3211. 376039                from = 0;
    +
     3212. 376039                if (line_source === undefined) {
    +
     3213. 376039                    return (
    +
     3214. 376039                        mode_mega
    +
     3215. 376039
    +
     3216. 376039// test_cause:
    +
     3217. 376039// ["`${//}`", "lex_token", "unclosed_mega", "", 1]
    +
     3218. 376039
    +
     3219. 376039                        ? stop_at("unclosed_mega", line_mega, from_mega)
    +
     3220. 376039                        : line_disable !== undefined
    +
     3221. 376039
    +
     3222. 376039// test_cause:
    +
     3223. 376039// ["/*jslint-disable*/", "lex_token", "unclosed_disable", "", 1]
    +
     3224. 376039
    +
     3225. 376039                        ? stop_at("unclosed_disable", line_disable)
    +
     3226. 376039                        : token_create("(end)")
    +
     3227. 376039                    );
    +
     3228. 376039                }
    +
     3229. 376039            }
    +
     3230. 376039            from = column;
    +
     3231. 376039            match = line_source.match(jslint_rgx_token);
    +
     3232. 376039
    +
     3233. 376039// match[1] token
    +
     3234. 376039// match[2] whitespace
    +
     3235. 376039// match[3] identifier
    +
     3236. 376039// match[4] number
    +
     3237. 376039// match[5] rest
    +
     3238. 376039
    +
     3239. 376039            if (!match) {
    +
     3240. 376039
    +
     3241. 376039// test_cause:
    +
     3242. 376039// ["#", "lex_token", "unexpected_char_a", "#", 1]
    +
     3243. 376039
    +
     3244. 376039                return stop_at(
    +
     3245. 376039                    "unexpected_char_a",
    +
     3246. 376039                    line,
    +
     3247. 376039                    column,
    +
     3248. 376039                    line_source[0]
    +
     3249. 376039                );
    +
     3250. 376039            }
    +
     3251. 376039            snippet = match[1];
    +
     3252. 376039            column += snippet.length;
    +
     3253. 376039            line_source = match[5];
    +
     3254. 376039            if (!match[2]) {
    +
     3255. 376039                break;
    +
     3256. 376039            }
    +
     3257. 376039        }
    +
     3258. 251612
    +
     3259. 251612// The token is an identifier.
    +
     3260. 251612
    +
     3261. 251612        if (match[3]) {
    +
     3262.  67699            return token_create(snippet, undefined, true);
    +
     3263. 183913        }
    +
     3264. 183913
    +
     3265. 183913// Create token from number.
    +
     3266. 183913
    +
     3267. 183913        if (match[4]) {
    +
     3268.   8851            return lex_number();
    +
     3269. 175062        }
    +
     3270. 175062
    +
     3271. 175062// Create token from string "..." or '...'.
    +
     3272. 175062
    +
     3273. 175062        if (snippet === "\"" || snippet === "'") {
    +
     3274.  24335            return lex_string(snippet);
    +
     3275. 150727        }
    +
     3276. 150727
    +
     3277. 150727// Create token from megastring `...`.
    +
     3278. 150727
    +
     3279. 150727        if (snippet === "`") {
    +
     3280.    789            return lex_megastring();
    +
     3281. 149938        }
    +
     3282. 149938
    +
     3283. 149938// Create token from comment /*...*/ or //....
    +
     3284. 149938
    +
     3285. 149938        if (snippet === "/*" || snippet === "//") {
    +
     3286.  11221            return lex_comment();
    +
     3287. 138717        }
    +
     3288. 138717
    +
     3289. 138717// Create token from slash /.
    +
     3290. 138717
    +
     3291. 138717        if (snippet === "/") {
    +
     3292.    598            return lex_slash_or_regexp();
    +
     3293. 138119        }
    +
     3294. 138119        return token_create(snippet);
    +
     3295. 138119    }
    +
     3296.    668
    +
     3297.   2667    function option_set_item(key, val) {
    +
     3298.   2667
    +
     3299.   2667// These are the options that are recognized in the option object or that may
    +
     3300.   2667// appear in a /*jslint*/ directive. Most options will have a boolean value,
    +
     3301.   2667// usually true. Some options will also predefine some number of global
    +
     3302.   2667// variables.
    +
     3303.   2667
    +
     3304.   2667        switch (key) {
    +
     3305.    659        case "beta":            // Enable experimental warnings.
    +
     3306.    661        case "bitwise":         // Allow bitwise operator.
    +
     3307.    668        case "browser":         // Assume browser environment.
    +
     3308.    670        case "convert":         // Allow conversion operator.
    +
     3309.    672        case "couch":           // Assume CouchDb environment.
    +
     3310.    678        case "devel":           // Allow console.log() and friends.
    +
     3311.   2014        case "ecma":            // Assume ECMAScript environment.
    +
     3312.   2020        case "eval":            // Allow eval().
    +
     3313.   2024        case "fart":            // Allow complex fat-arrow.
    +
     3314.   2029        case "for":             // Allow for-statement.
    +
     3315.   2035        case "getset":          // Allow get() and set().
    +
     3316.   2040        case "indent2":         // Use 2-space indent.
    +
     3317.   2042        case "long":            // Allow long lines.
    +
     3318.   2089        case "node":            // Assume Node.js environment.
    +
     3319.   2093        case "nomen":           // Allow weird property name.
    +
     3320.   2095        case "single":          // Allow single-quote strings.
    +
     3321.   2097        case "subscript":       // Allow identifier in subscript-notation.
    +
     3322.   2602        case "test_cause":      // Test jslint's causes.
    +
     3323.   2604        case "test_internal_error":     // Test jslint's internal-error
    +
     3324.   2667                                        // ... handling-ability.
    +
     3325.   2606        case "this":            // Allow 'this'.
    +
     3326.   2609        case "trace":           // Include jslint stack-trace in warnings.
    +
     3327.   2613        case "unordered":       // Allow unordered cases, params, properties,
    +
     3328.   2667                                // ... variables, and exports.
    +
     3329.   2617        case "variable":        // Allow unordered const and let declarations
    +
     3330.   2667                                // ... that are not at top of function-scope.
    +
     3331.   2619        case "white":           // Allow messy whitespace.
    +
     3332.   2619            option_dict[key] = val;
    +
     3333.   2619            break;
    +
     3334.   2667
    +
     3335.   2667// PR-404 - Alias "evil" to jslint-directive "eval" for backwards-compat.
    +
     3336.   2667
    +
     3337.      2        case "evil":
    +
     3338.      2            return option_set_item("eval", val);
    +
     3339.   2667
    +
     3340.   2667// PR-404 - Alias "nomen" to jslint-directive "name" for backwards-compat.
    +
     3341.   2667
    +
     3342.      2        case "name":
    +
     3343.      2            return option_set_item("nomen", val);
    +
     3344.     44        default:
    +
     3345.     44            return false;
    +
     3346.   2619        }
    +
     3347.   2619
    +
     3348.   2619// Initialize global-variables.
    +
     3349.   2619
    +
     3350.   2619        switch (val && key) {
    +
     3351.   2667
    +
     3352.   2667// Assign global browser variables to global_dict.
    +
     3353.   2667/*
    +
     3354.   2667// /\*jslint beta, browser, devel*\/
    +
     3355.   2667console.log(JSON.stringify(Object.keys(window).sort(), undefined, 4));
    +
     3356.   2667*/
    +
     3357.   2667
    +
     3358.      6        case "browser":
    +
     3359.      6            object_assign_from_list(global_dict, [
    +
     3360.      6
    +
     3361.      6// Shared with Node.js.
    +
     3362.      6
    +
     3363.      6                "AbortController",
    +
     3364.      6                // "Buffer",
    +
     3365.      6                // "Crypto",
    +
     3366.      6                // "CryptoKey",
    +
     3367.      6                "Event",
    +
     3368.      6                "EventTarget",
    +
     3369.      6                "MessageChannel",
    +
     3370.      6                "MessageEvent",
    +
     3371.      6                "MessagePort",
    +
     3372.      6                // "Request",
    +
     3373.      6                // "Response",
    +
     3374.      6                // "SubtleCrypto",
    +
     3375.      6                "TextDecoder",
    +
     3376.      6                "TextEncoder",
    +
     3377.      6                "URL",
    +
     3378.      6                "URLSearchParams",
    +
     3379.      6                "WebAssembly",
    +
     3380.      6                // "__dirname",
    +
     3381.      6                // "__filename",
    +
     3382.      6                // "atob",
    +
     3383.      6                // "btoa",
    +
     3384.      6                // "clearImmediate",
    +
     3385.      6                "clearInterval",
    +
     3386.      6                "clearTimeout",
    +
     3387.      6                // "console",
    +
     3388.      6                // "crypto",
    +
     3389.      6                // "exports",
    +
     3390.      6                // "fetch",
    +
     3391.      6                // "global",
    +
     3392.      6                // "module",
    +
     3393.      6                "performance",
    +
     3394.      6                // "process",
    +
     3395.      6                "queueMicrotask",
    +
     3396.      6                // "require",
    +
     3397.      6                // "setImmediate",
    +
     3398.      6                "setInterval",
    +
     3399.      6                "setTimeout",
    +
     3400.      6
    +
     3401.      6// Web worker only.
    +
     3402.      6// https://github.com/mdn/content/blob/main/files/en-us/web/api
    +
     3403.      6// /workerglobalscope/index.md
    +
     3404.      6
    +
     3405.      6                "importScripts",
    +
     3406.      6
    +
     3407.      6// Window.
    +
     3408.      6
    +
     3409.      6                "Blob",
    +
     3410.      6                // "CharacterData",
    +
     3411.      6                // "DocumentType",
    +
     3412.      6                // "Element",
    +
     3413.      6                // "Event",
    +
     3414.      6                "FileReader",
    +
     3415.      6                // "FontFace",
    +
     3416.      6                "FormData",
    +
     3417.      6                "IntersectionObserver",
    +
     3418.      6                "MutationObserver",
    +
     3419.      6                // "Storage",
    +
     3420.      6                // "TextDecoder",
    +
     3421.      6                // "TextEncoder",
    +
     3422.      6                // "URL",
    +
     3423.      6                "Worker",
    +
     3424.      6                "XMLHttpRequest",
    +
     3425.      6                // "caches",
    +
     3426.      6                // "clearInterval",
    +
     3427.      6                // "clearTimeout",
    +
     3428.      6                "document",
    +
     3429.      6                // "event",
    +
     3430.      6                "fetch",
    +
     3431.      6                // "history",
    +
     3432.      6                "indexedDb",
    +
     3433.      6                "localStorage",
    +
     3434.      6                "location",
    +
     3435.      6                // "name",
    +
     3436.      6                "navigator",
    +
     3437.      6                "postMessage",
    +
     3438.      6                // "screen",
    +
     3439.      6                "sessionStorage",
    +
     3440.      6                // "setInterval",
    +
     3441.      6                // "setTimeout",
    +
     3442.      6                "structuredClone",
    +
     3443.      6                "window"
    +
     3444.      6            ], "browser");
    +
     3445.      6            break;
    +
     3446.   2667
    +
     3447.   2667// https://docs.couchdb.org/en/stable/query-server/javascript.html#javascript
    +
     3448.   2667
    +
     3449.      2        case "couch":
    +
     3450.      2            object_assign_from_list(global_dict, [
    +
     3451.      2                "emit",
    +
     3452.      2                "getRow",
    +
     3453.      2                "isArray",
    +
     3454.      2                "log",
    +
     3455.      2                "provides",
    +
     3456.      2                "registerType",
    +
     3457.      2                "require",
    +
     3458.      2                "send",
    +
     3459.      2                "start",
    +
     3460.      2                "sum",
    +
     3461.      2                "toJSON"
    +
     3462.      2            ], "CouchDb");
    +
     3463.      2            break;
    +
     3464.      6        case "devel":
    +
     3465.      6            object_assign_from_list(global_dict, [
    +
     3466.      6                "alert", "confirm", "console", "prompt"
    +
     3467.      6            ], "development");
    +
     3468.      6            break;
    +
     3469.   2667
    +
     3470.   2667// These are the globals that are provided by the language standard.
    +
     3471.   2667// Assign global ECMAScript variables to global_dict.
    +
     3472.   2667/*
    +
     3473.   2667node --input-type=module --eval '
    +
     3474.   2667// /\*jslint beta, node*\/
    +
     3475.   2667import https from "https";
    +
     3476.   2667(async function () {
    +
     3477.   2667    let dict = {import: true};
    +
     3478.   2667    let result = "";
    +
     3479.   2667    await new Promise(function (resolve) {
    +
     3480.   2667        https.get((
    +
     3481.   2667            "https://raw.githubusercontent.com/mdn/content/main/files"
    +
     3482.   2667            + "/en-us/web/javascript/reference/global_objects/index.md"
    +
     3483.   2667        ), function (res) {
    +
     3484.   2667            res.on("data", function (chunk) {
    +
     3485.   2667                result += chunk;
    +
     3486.   2667            }).on("end", resolve).setEncoding("utf8");
    +
     3487.   2667        });
    +
     3488.   2667    });
    +
     3489.   2667    result.replace((
    +
     3490.   2667        /\n- \{\{JSxRef\("(?:Global_Objects\/)?([^"\/]+?)"/g
    +
     3491.   2667    ), function (ignore, key) {
    +
     3492.   2667        if (globalThis.hasOwnProperty(key)) {
    +
     3493.   2667            dict[key] = true;
    +
     3494.   2667        }
    +
     3495.   2667        return "";
    +
     3496.   2667    });
    +
     3497.   2667    console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4));
    +
     3498.   2667}());
    +
     3499.   2667'
    +
     3500.   2667*/
    +
     3501.   2667
    +
     3502.   1336        case "ecma":
    +
     3503.   1336            object_assign_from_list(global_dict, [
    +
     3504.   1336                "AggregateError",
    +
     3505.   1336                "Array",
    +
     3506.   1336                "ArrayBuffer",
    +
     3507.   1336                "Atomics",
    +
     3508.   1336                "BigInt",
    +
     3509.   1336                "BigInt64Array",
    +
     3510.   1336                "BigUint64Array",
    +
     3511.   1336                "Boolean",
    +
     3512.   1336                "DataView",
    +
     3513.   1336                "Date",
    +
     3514.   1336                "Error",
    +
     3515.   1336                "EvalError",
    +
     3516.   1336                "Float32Array",
    +
     3517.   1336                "Float64Array",
    +
     3518.   1336                "Function",
    +
     3519.   1336                "Infinity",
    +
     3520.   1336                "Int16Array",
    +
     3521.   1336                "Int32Array",
    +
     3522.   1336                "Int8Array",
    +
     3523.   1336                "Intl",
    +
     3524.   1336                "JSON",
    +
     3525.   1336                "Map",
    +
     3526.   1336                "Math",
    +
     3527.   1336                "NaN",
    +
     3528.   1336                "Number",
    +
     3529.   1336                "Object",
    +
     3530.   1336                "Promise",
    +
     3531.   1336                "Proxy",
    +
     3532.   1336                "RangeError",
    +
     3533.   1336                "ReferenceError",
    +
     3534.   1336                "Reflect",
    +
     3535.   1336                "RegExp",
    +
     3536.   1336                "Set",
    +
     3537.   1336                "SharedArrayBuffer",
    +
     3538.   1336                "String",
    +
     3539.   1336                "Symbol",
    +
     3540.   1336                "SyntaxError",
    +
     3541.   1336                "TypeError",
    +
     3542.   1336                "URIError",
    +
     3543.   1336                "Uint16Array",
    +
     3544.   1336                "Uint32Array",
    +
     3545.   1336                "Uint8Array",
    +
     3546.   1336                "Uint8ClampedArray",
    +
     3547.   1336                "WeakMap",
    +
     3548.   1336                "WeakSet",
    +
     3549.   1336                "WebAssembly",
    +
     3550.   1336                "decodeURI",
    +
     3551.   1336                "decodeURIComponent",
    +
     3552.   1336                "encodeURI",
    +
     3553.   1336                "encodeURIComponent",
    +
     3554.   1336                "eval",
    +
     3555.   1336                "globalThis",
    +
     3556.   1336                "import",
    +
     3557.   1336                "isFinite",
    +
     3558.   1336                "isNaN",
    +
     3559.   1336                "parseFloat",
    +
     3560.   1336                "parseInt",
    +
     3561.   1336                "undefined"
    +
     3562.   1336            ], "ECMAScript");
    +
     3563.   1336            break;
    +
     3564.   2667
    +
     3565.   2667// Assign global Node.js variables to global_dict.
    +
     3566.   2667/*
    +
     3567.   2667node --input-type=module --eval '
    +
     3568.   2667// /\*jslint beta, node*\/
    +
     3569.   2667import moduleHttps from "https";
    +
     3570.   2667(async function () {
    +
     3571.   2667    let dict = Object.create(null);
    +
     3572.   2667    let result = "";
    +
     3573.   2667    await new Promise(function (resolve) {
    +
     3574.   2667        moduleHttps.get((
    +
     3575.   2667            "https://raw.githubusercontent.com/nodejs/node/v16.x/doc/api"
    +
     3576.   2667            + "/globals.md"
    +
     3577.   2667        ), function (res) {
    +
     3578.   2667            res.on("data", function (chunk) {
    +
     3579.   2667                result += chunk;
    +
     3580.   2667            }).on("end", resolve).setEncoding("utf8");
    +
     3581.   2667        });
    +
     3582.   2667    });
    +
     3583.   2667    result.replace((
    +
     3584.   2667        /\n(?:\* \[`|## |## Class: )`\w+/g
    +
     3585.   2667    ), function (match0) {
    +
     3586.   2667        dict[match0.split("`")[1]] = true;
    +
     3587.   2667        return "";
    +
     3588.   2667    });
    +
     3589.   2667    console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4));
    +
     3590.   2667}());
    +
     3591.   2667'
    +
     3592.   2667*/
    +
     3593.   2667
    +
     3594.     47        case "node":
    +
     3595.     47            object_assign_from_list(global_dict, [
    +
     3596.     47                "AbortController",
    +
     3597.     47                "Buffer",
    +
     3598.     47                // "Crypto",
    +
     3599.     47                // "CryptoKey",
    +
     3600.     47                "Event",
    +
     3601.     47                "EventTarget",
    +
     3602.     47                "MessageChannel",
    +
     3603.     47                "MessageEvent",
    +
     3604.     47                "MessagePort",
    +
     3605.     47                // "Request",
    +
     3606.     47                // "Response",
    +
     3607.     47                // "SubtleCrypto",
    +
     3608.     47                "TextDecoder",
    +
     3609.     47                "TextEncoder",
    +
     3610.     47                "URL",
    +
     3611.     47                "URLSearchParams",
    +
     3612.     47                "WebAssembly",
    +
     3613.     47                "__dirname",
    +
     3614.     47                "__filename",
    +
     3615.     47                // "atob",
    +
     3616.     47                // "btoa",
    +
     3617.     47                "clearImmediate",
    +
     3618.     47                "clearInterval",
    +
     3619.     47                "clearTimeout",
    +
     3620.     47                "console",
    +
     3621.     47                // "crypto",
    +
     3622.     47                "exports",
    +
     3623.     47                // "fetch",
    +
     3624.     47                "global",
    +
     3625.     47                "module",
    +
     3626.     47                "performance",
    +
     3627.     47                "process",
    +
     3628.     47                "queueMicrotask",
    +
     3629.     47                "require",
    +
     3630.     47                "setImmediate",
    +
     3631.     47                "setInterval",
    +
     3632.     47                "setTimeout"
    +
     3633.     47            ], "Node.js");
    +
     3634.     47            break;
    +
     3635.   2619        }
    +
     3636.   2619        return true;
    +
     3637.   2619    }
    +
     3638.    668
    +
     3639.    469    function read_digits(base, mode) {
    +
     3640.    469        let digits = line_source.match(
    +
     3641.    469            base === "b"
    +
     3642.      3            ? jslint_rgx_digits_bits
    +
     3643.    466            : base === "o"
    +
     3644.    466            ? jslint_rgx_digits_octals
    +
     3645.    466            : base === "x"
    +
     3646.    466            ? jslint_rgx_digits_hexs
    +
     3647.    466            : jslint_rgx_digits_decimals
    +
     3648.    469        )[0];
    +
     3649.    469        if (
    +
     3650.     77            (mode !== mode_digits_empty_string && digits.length === 0)
    +
     3651.    468            || digits[0] === "_"
    +
     3652.      2        ) {
    +
     3653.      2
    +
     3654.      2// test_cause:
    +
     3655.      2// ["0x", "read_digits", "expected_digits_after_a", "0x", 2]
    +
     3656.      2// ["0x_", "read_digits", "expected_digits_after_a", "0x", 2]
    +
     3657.      2
    +
     3658.      2            warn_at("expected_digits_after_a", line, column, snippet);
    +
     3659.      2        }
    +
     3660.    469
    +
     3661.    469// PR-390 - Add numeric-separator check.
    +
     3662.    469
    +
     3663.     73        if (mode === mode_digits_numeric_separator) {
    +
     3664.     73            check_numeric_separator(digits, column);
    +
     3665.    396        } else if (digits.indexOf("_") >= 0) {
    +
     3666.    396
    +
     3667.    396// test_cause:
    +
     3668.    396// ["\"\\u{1_2}\"", "read_digits", "illegal_num_separator", "", 6]
    +
     3669.    396
    +
     3670.    396            warn_at(
    +
     3671.    396                "illegal_num_separator",
    +
     3672.    396                line,
    +
     3673.    396                column + digits.indexOf("_") + 1
    +
     3674.    396            );
    +
     3675.    396        }
    +
     3676.    469        column += digits.length;
    +
     3677.    469        line_source = line_source.slice(digits.length);
    +
     3678.    469        snippet += digits;
    +
     3679.    469        char_after();
    +
     3680.    469        return digits.length;
    +
     3681.    469    }
    +
     3682.    668
    +
     3683. 105193    function read_line() {
    +
     3684. 105193
    +
     3685. 105193// Put the next line of source in line_source. If the line contains tabs,
    +
     3686. 105193// replace them with spaces and give a warning. Also warn if the line contains
    +
     3687. 105193// unsafe characters or is too damn long.
    +
     3688. 105193
    +
     3689. 105193        if (
    +
     3690. 105193            !option_dict.long
    +
     3691. 105189            && line_whole.length > 80
    +
     3692.     56            && line_disable === undefined
    +
     3693.     56            && !state.mode_json
    +
     3694.     23            && token_1
    +
     3695.     23            && !mode_regexp
    +
     3696.     13        ) {
    +
     3697.     13
    +
     3698.     13// test_cause:
    +
     3699.     13// ["/////////////////////////////////////////////////////////////////////////////////", "read_line", "too_long", "", 1] //jslint-ignore-line
    +
     3700.     13
    +
     3701.     13            warn_at("too_long", line);
    +
     3702.     13        }
    +
     3703. 105193        column = 0;
    +
     3704. 105193        line += 1;
    +
     3705. 105193        mode_regexp = false;
    +
     3706. 105193        line_source = undefined;
    +
     3707. 105193        line_whole = "";
    +
     3708.    645        if (line_list[line] === undefined) {
    +
     3709.    645            return line_source;
    +
     3710. 104548        }
    +
     3711. 104548        line_source = line_list[line].line_source;
    +
     3712. 104548        line_whole = line_source;
    +
     3713. 104548
    +
     3714. 104548// Scan each line for following ignore-directives:
    +
     3715. 104548// "/*jslint-disable*/"
    +
     3716. 104548// "/*jslint-enable*/"
    +
     3717. 104548// "//jslint-ignore-line"
    +
     3718. 104548
    +
     3719. 104548        if (line_source === "/*jslint-disable*/") {
    +
     3720.      5
    +
     3721.      5// test_cause:
    +
     3722.      5// ["/*jslint-disable*/", "read_line", "jslint_disable", "", 0]
    +
     3723.      5
    +
     3724.      5            test_cause("jslint_disable");
    +
     3725.      5            line_disable = line;
    +
     3726. 104543        } else if (line_source === "/*jslint-enable*/") {
    +
     3727. 104543            if (line_disable === undefined) {
    +
     3728. 104543
    +
     3729. 104543// test_cause:
    +
     3730. 104543// ["/*jslint-enable*/", "read_line", "unopened_enable", "", 1]
    +
     3731. 104543
    +
     3732. 104543                stop_at("unopened_enable", line);
    +
     3733. 104543            }
    +
     3734. 104543            line_disable = undefined;
    +
     3735. 104543        } else if (
    +
     3736. 104543            line_source.endsWith(" //jslint-ignore-line")
    +
     3737. 104543            || line_source.endsWith(" //jslint-quiet")
    +
     3738. 104543        ) {
    +
     3739. 104543
    +
     3740. 104543// test_cause:
    +
     3741. 104543// ["0 //jslint-ignore-line", "read_line", "jslint_ignore_line", "", 0]
    +
     3742. 104543
    +
     3743. 104543            test_cause("jslint_ignore_line");
    +
     3744. 104543            line_list[line].directive_ignore_line = true;
    +
     3745. 104547        }
    +
     3746. 104547        if (line_disable !== undefined) {
    +
     3747.      9
    +
     3748.      9// test_cause:
    +
     3749.      9// ["/*jslint-disable*/\n0", "read_line", "line_disable", "", 0]
    +
     3750.      9
    +
     3751.      9            test_cause("line_disable");
    +
     3752.      9            line_source = "";
    +
     3753. 104547        }
    +
     3754. 104547        // jslint_rgx_tab
    +
     3755. 104547        if (line_source.indexOf("\t") >= 0) {
    +
     3756.      3            if (!option_dict.white) {
    +
     3757.      3
    +
     3758.      3// test_cause:
    +
     3759.      3// ["\t", "read_line", "use_spaces", "", 1]
    +
     3760.      3
    +
     3761.      3                warn_at("use_spaces", line, line_source.indexOf("\t") + 1);
    +
     3762.      3            }
    +
     3763.      3            line_source = line_source.replace(jslint_rgx_tab, " ");
    +
     3764. 104547        }
    +
     3765. 104547        if (!option_dict.white && line_source.endsWith(" ")) {
    +
     3766.      2
    +
     3767.      2// test_cause:
    +
     3768.      2// [" ", "read_line", "unexpected_trailing_space", "", 1]
    +
     3769.      2
    +
     3770.      2            warn_at("unexpected_trailing_space", line, line_source.length - 1);
    +
     3771. 104547        }
    +
     3772. 104547        return line_source;
    +
     3773. 104547    }
    +
     3774.    668
    +
     3775. 255241    function token_create(id, value, identifier) {
    +
     3776. 255241
    +
     3777. 255241// Create the token object and append it to token_list.
    +
     3778. 255241
    +
     3779. 255241        let the_token = {
    +
     3780. 255241            from,
    +
     3781. 255241            id,
    +
     3782. 255241            identifier: Boolean(identifier),
    +
     3783. 255241            line,
    +
     3784. 255241            nr: token_list.length,
    +
     3785. 255241            thru: column,
    +
     3786. 255241            value
    +
     3787. 255241        };
    +
     3788. 255241        token_list.push(the_token);
    +
     3789. 255241
    +
     3790. 255241// Directives must appear before the first statement.
    +
     3791. 255241
    +
     3792. 244023        if (id !== "(comment)" && id !== ";") {
    +
     3793. 228006            mode_directive = false;
    +
     3794. 228006        }
    +
     3795. 255241
    +
     3796. 255241// If this token is an identifier that touches a preceding number, or
    +
     3797. 255241// a "/", comment, or regular expression literal that touches a preceding
    +
     3798. 255241// comment or regular expression literal, then give a missing space warning.
    +
     3799. 255241// This warning is not suppressed by option_dict.white.
    +
     3800. 255241
    +
     3801. 255241        if (
    +
     3802. 255241            token_prv.line === line
    +
     3803. 185838            && token_prv.thru === from
    +
     3804. 119910            && (id === "(comment)" || id === "(regexp)" || id === "/")
    +
     3805.    125            && (token_prv.id === "(comment)" || token_prv.id === "(regexp)")
    +
     3806.      1        ) {
    +
     3807.      1
    +
     3808.      1// test_cause:
    +
     3809.      1// ["/**//**/", "token_create", "expected_space_a_b", "(comment)", 5]
    +
     3810.      1
    +
     3811.      1            warn(
    +
     3812.      1                "expected_space_a_b",
    +
     3813.      1                the_token,
    +
     3814.      1                artifact(token_prv),
    +
     3815.      1                artifact(the_token)
    +
     3816.      1            );
    +
     3817.      1        }
    +
     3818.  11603        if (token_prv.id === "." && id === "(number)") {
    +
     3819.      4
    +
     3820.      4// test_cause:
    +
     3821.      4// [".0", "token_create", "expected_a_before_b", ".", 1]
    +
     3822.      4
    +
     3823.      4            warn("expected_a_before_b", token_prv, "0", ".");
    +
     3824.      4        }
    +
     3825.  11603        if (token_prv_expr.id === "." && the_token.identifier) {
    +
     3826.  11598            the_token.dot = true;
    +
     3827.  11598        }
    +
     3828. 255241
    +
     3829. 255241// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart.
    +
     3830. 255241// Farts are now detected by keeping a list of most recent "(" tokens at any
    +
     3831. 255241// given depth. When a "=>" token is encountered, the most recent "(" token at
    +
     3832. 255241// current depth is marked as a fart.
    +
     3833. 255241
    +
     3834. 255241        switch (id) {
    +
     3835.  17591        case "(":
    +
     3836.  17591            paren_backtrack_list[paren_depth] = the_token;
    +
     3837.  17591            paren_depth += 1;
    +
     3838.  17591            break;
    +
     3839.  17590        case ")":
    +
     3840.  17590            paren_depth -= 1;
    +
     3841.  17590            break;
    +
     3842.     16        case "=>":
    +
     3843.     16            if (
    +
     3844.     16                token_prv_expr.id === ")"
    +
     3845.     16                && paren_backtrack_list[paren_depth]
    +
     3846.     16            ) {
    +
     3847.     16                paren_backtrack_list[paren_depth].fart = the_token;
    +
     3848.     16            }
    +
     3849.     16            break;
    +
     3850. 255241        }
    +
     3851. 255241
    +
     3852. 255241// The previous token is used to detect adjacency problems.
    +
     3853. 255241
    +
     3854. 255241        token_prv = the_token;
    +
     3855. 255241
    +
     3856. 255241// The token_prv_expr token is a previous token that was not a comment.
    +
     3857. 255241// The token_prv_expr token
    +
     3858. 255241// is used to disambiguate "/", which can mean division or regular expression
    +
     3859. 255241// literal.
    +
     3860. 255241
    +
     3861. 244023        if (token_prv.id !== "(comment)") {
    +
     3862. 244023            token_prv_expr = token_prv;
    +
     3863. 244023        }
    +
     3864. 255241        return the_token;
    +
     3865. 255241    }
    +
     3866.    668
    +
     3867.    668// Init global_dict and option_dict.
    +
     3868.    668
    +
     3869.    668    option_set_item("ecma", true);
    +
     3870.   1896    Object.keys(option_dict).sort().forEach(function (key) {
    +
     3871.   1896        option_set_item(key, option_dict[key] === true);
    +
     3872.   1896    });
    +
     3873.    668    object_assign_from_list(global_dict, global_list, "user-defined");
    +
     3874.    668
    +
     3875.    668// Scan first line for "#!" and ignore it.
    +
     3876.    668
    +
     3877.      1    if (line_list[jslint_fudge].line_source.startsWith("#!")) {
    +
     3878.      1        line += 1;
    +
     3879.      1        state.mode_shebang = true;
    +
     3880.      1    }
    +
     3881.    668    token_1 = lex_token();
    +
     3882.    640    state.mode_json = token_1.id === "{" || token_1.id === "[";
    +
     3883.    668
    +
     3884.    668// Lex/loop through each token until (end).
    +
     3885.    668
    +
     3886. 249474    while (true) {
    +
     3887. 249474        if (lex_token().id === "(end)") {
    +
     3888. 249474            break;
    +
     3889. 249474        }
    +
     3890. 249474    }
    +
     3891.    668}
    +
     3892.      1
    +
     3893.    631function jslint_phase3_parse(state) {
    +
     3894.    631
    +
     3895.    631// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
    +
     3896.    631
    +
     3897.    631// Parsing:
    +
     3898.    631
    +
     3899.    631// Parsing weaves the tokens into an abstract syntax tree. During that process,
    +
     3900.    631// a token may be given any of these properties:
    +
     3901.    631
    +
     3902.    631//      arity       string
    +
     3903.    631//      label       identifier
    +
     3904.    631//      name        identifier
    +
     3905.    631//      expression  expressions
    +
     3906.    631//      block       statements
    +
     3907.    631//      else        statements (else, default, catch)
    +
     3908.    631
    +
     3909.    631// Specialized tokens may have additional properties.
    +
     3910.    631
    +
     3911.    631    let anon = "anonymous";     // The guessed name for anonymous functions.
    +
     3912.    631    let {
    +
     3913.    631        artifact,
    +
     3914.    631        catch_list,
    +
     3915.    631        catch_stack,
    +
     3916.    631        export_dict,
    +
     3917.    631        function_list,
    +
     3918.    631        function_stack,
    +
     3919.    631        global_dict,
    +
     3920.    631        import_list,
    +
     3921.    631        is_equal,
    +
     3922.    631        option_dict,
    +
     3923.    631        property_dict,
    +
     3924.    631        stop,
    +
     3925.    631        syntax_dict,
    +
     3926.    631        tenure,
    +
     3927.    631        test_cause,
    +
     3928.    631        token_global,
    +
     3929.    631        token_list,
    +
     3930.    631        warn,
    +
     3931.    631        warn_at
    +
     3932.    631    } = state;
    +
     3933.    631    let catchage = catch_stack[0];      // The current catch-block.
    +
     3934.    631    let functionage = token_global;     // The current function.
    +
     3935.    631    let mode_var;               // "var" if using var; "let" if using let.
    +
     3936.    631    let token_ii = 0;           // The number of the next token.
    +
     3937.    631    let token_now = token_global;       // The current token being examined in
    +
     3938.    631                                        // ... the parse.
    +
     3939.    631    let token_nxt = token_global;       // The next token to be examined in
    +
     3940.    631                                        // ... <token_list>.
    +
     3941.    631
    +
     3942. 244361    function advance(id, match) {
    +
     3943. 244361
    +
     3944. 244361// Produce the next token.
    +
     3945. 244361
    +
     3946. 244361// Attempt to give helpful names to anonymous functions.
    +
     3947. 244361
    +
     3948. 244361        if (
    +
     3949. 244361            token_now.identifier
    +
     3950.  67656            && token_now.id !== "function"
    +
     3951.  65664            && token_now.id !== "async"
    +
     3952.  65483        ) {
    +
     3953.  65483            anon = token_now.id;
    +
     3954. 178878        } else if (
    +
     3955. 178878            token_now.id === "(string)"
    +
     3956. 178878            && jslint_rgx_identifier.test(token_now.value)
    +
     3957. 178878        ) {
    +
     3958. 178878            anon = token_now.value;
    +
     3959. 178878        }
    +
     3960. 244361
    +
     3961. 244361// Attempt to match token_nxt with an expected id.
    +
     3962. 244361
    +
     3963. 120065        if (id !== undefined && token_nxt.id !== id) {
    +
     3964.     26            return (
    +
     3965.     26                match === undefined
    +
     3966.     26
    +
     3967.     26// test_cause:
    +
     3968.     26// ["{0:0}", "advance", "expected_a_b", "0", 2]
    +
     3969.     26
    +
     3970.     26                ? stop("expected_a_b", token_nxt, id, artifact())
    +
     3971.     26
    +
     3972.     26// test_cause:
    +
     3973.     26// ["{\"aa\":0", "advance", "expected_a_b_from_c_d", "{", 1]
    +
     3974.     26
    +
     3975.     26                : stop(
    +
     3976.     26                    "expected_a_b_from_c_d",
    +
     3977.     26                    token_nxt,
    +
     3978.     26                    id,
    +
     3979.     26                    artifact(match),
    +
     3980.     26                    match.line,
    +
     3981.     26                    artifact()
    +
     3982.     26                )
    +
     3983.     26            );
    +
     3984. 244335        }
    +
     3985. 244335
    +
     3986. 244335// Promote the tokens, skipping comments.
    +
     3987. 244335
    +
     3988. 244335        token_now = token_nxt;
    +
     3989. 255550        while (true) {
    +
     3990. 255550            token_nxt = token_list[token_ii];
    +
     3991. 255550            state.token_nxt = token_nxt;
    +
     3992. 255550            token_ii += 1;
    +
     3993. 255550            if (token_nxt.id !== "(comment)") {
    +
     3994. 255550                if (token_nxt.id === "(end)") {
    +
     3995. 255550                    token_ii -= 1;
    +
     3996. 255550                }
    +
     3997. 255550                break;
    +
     3998. 255550            }
    +
     3999. 255550            if (state.mode_json) {
    +
     4000. 255550
    +
     4001. 255550// test_cause:
    +
     4002. 255550// ["[//]", "advance", "unexpected_a", "(comment)", 2]
    +
     4003. 255550
    +
     4004. 255550                warn("unexpected_a");
    +
     4005. 255550            }
    +
     4006. 255550        }
    +
     4007. 244361    }
    +
     4008.    631
    +
     4009.   7572    function assignment(id) {
    +
     4010.   7572
    +
     4011.   7572// Create an assignment operator. The one true assignment is different because
    +
     4012.   7572// its left side, when it is a variable, is not treated as an expression.
    +
     4013.   7572// That case is special because that is when a variable gets initialized. The
    +
     4014.   7572// other assignment operators can modify, but they cannot initialize.
    +
     4015.   7572
    +
     4016.   7572        const the_symbol = symbol(id, 20);
    +
     4017.   5013        the_symbol.led_infix = function (left) {
    +
     4018.   5013            const the_token = token_now;
    +
     4019.   5013            let right;
    +
     4020.   5013            the_token.arity = "assignment";
    +
     4021.   5013            right = parse_expression(20 - 1);
    +
     4022.   4234            if (id === "=" && left.arity === "variable") {
    +
     4023.   2818                the_token.names = left;
    +
     4024.   2818                the_token.expression = right;
    +
     4025.   2818            } else {
    +
     4026.   2191                the_token.expression = [left, right];
    +
     4027.   5009            }
    +
     4028.   5009            if (
    +
     4029.   5009                right.arity === "assignment"
    +
     4030.   5009                || right.arity === "preassign"
    +
     4031.   5007                || right.arity === "postassign"
    +
     4032.      2            ) {
    +
     4033.      2                warn("unexpected_a", right);
    +
     4034.   5009            }
    +
     4035.   5009            check_mutation(left);
    +
     4036.   5009            return the_token;
    +
     4037.   5009        };
    +
     4038.   7572        return the_symbol;
    +
     4039.   7572    }
    +
     4040.    631
    +
     4041.   5713    function block(special) {
    +
     4042.   5713
    +
     4043.   5713// Parse a block, a sequence of statements wrapped in braces.
    +
     4044.   5713//  special "body"      The block is a function body.
    +
     4045.   5713//          "ignore"    No warning on an empty block.
    +
     4046.   5713//          "naked"     No advance.
    +
     4047.   5713//          undefined   An ordinary block.
    +
     4048.   5713
    +
     4049.   5713        let stmts;
    +
     4050.   5713        let the_block;
    +
     4051.   5708        if (special !== "naked") {
    +
     4052.   5708            advance("{");
    +
     4053.   5712        }
    +
     4054.   5712        the_block = token_now;
    +
     4055.   5712        if (special !== "body") {
    +
     4056.   3715            functionage.statement_prv = the_block;
    +
     4057.   5712        }
    +
     4058.   5712        the_block.arity = "statement";
    +
     4059.   5712        the_block.body = special === "body";
    +
     4060.   5712
    +
     4061.   5712// Top level function bodies may include the "use strict" pragma.
    +
     4062.   5712
    +
     4063.   5712        if (
    +
     4064.   5712            special === "body"
    +
     4065.   5712            && function_stack.length === 1
    +
     4066.    281            && token_nxt.value === "use strict"
    +
     4067.      4        ) {
    +
     4068.      4            token_nxt.statement = true;
    +
     4069.      4            advance("(string)");
    +
     4070.      4            advance(";");
    +
     4071.   5712        }
    +
     4072.   5712        stmts = parse_statements();
    +
     4073.   5712        the_block.block = stmts;
    +
     4074.   5712        if (stmts.length === 0) {
    +
     4075.     72            if (!option_dict.devel && special !== "ignore") {
    +
     4076.     72
    +
     4077.     72// test_cause:
    +
     4078.     72// ["function aa(){}", "block", "empty_block", "{", 14]
    +
     4079.     72
    +
     4080.     72                warn("empty_block", the_block);
    +
     4081.     72            }
    +
     4082.     72            the_block.disrupt = false;
    +
     4083.   5633        } else {
    +
     4084.   5633            the_block.disrupt = stmts[stmts.length - 1].disrupt;
    +
     4085.   5705        }
    +
     4086.   5705        advance("}");
    +
     4087.   5705        return the_block;
    +
     4088.   5705    }
    +
     4089.    631
    +
     4090.  23297    function check_left(left, right) {
    +
     4091.  23297
    +
     4092.  23297// Warn if the left is not one of these:
    +
     4093.  23297//      ?.
    +
     4094.  23297//      ?:
    +
     4095.  23297//      e()
    +
     4096.  23297//      e.b
    +
     4097.  23297//      e[b]
    +
     4098.  23297//      identifier
    +
     4099.  23297
    +
     4100.  23297        const id = left.id;
    +
     4101.  23297        if (
    +
     4102.  23297            !left.identifier
    +
     4103.   6298            && (
    +
     4104.   6298                left.arity !== "ternary"
    +
     4105.   6298                || (
    +
     4106.   6298                    !check_left(left.expression[1])
    +
     4107.   6298                    && !check_left(left.expression[2])
    +
     4108.   6298                )
    +
     4109.   6298            )
    +
     4110.   6298            && (
    +
     4111.   6298                left.arity !== "binary"
    +
     4112.   6298                || (id !== "." && id !== "?." && id !== "(" && id !== "[")
    +
     4113.   6298            )
    +
     4114.     11        ) {
    +
     4115.     11            warn("unexpected_a", right || token_nxt);
    +
     4116.     11            return false;
    +
     4117.  23286        }
    +
     4118.  23286        return true;
    +
     4119.  23286    }
    +
     4120.    631
    +
     4121.   5012    function check_mutation(the_thing) {
    +
     4122.   5012
    +
     4123.   5012// The only expressions that may be assigned to are
    +
     4124.   5012//      e.b
    +
     4125.   5012//      e[b]
    +
     4126.   5012//      v
    +
     4127.   5012//      [destructure]
    +
     4128.   5012//      {destructure}
    +
     4129.   5012
    +
     4130.   5012        if (
    +
     4131.   5012            the_thing.arity !== "variable"
    +
     4132.   1543            && the_thing.id !== "."
    +
     4133.    192            && the_thing.id !== "["
    +
     4134.      7            && the_thing.id !== "{"
    +
     4135.      7        ) {
    +
     4136.      7
    +
     4137.      7// test_cause:
    +
     4138.      7// ["0=0", "check_mutation", "bad_assignment_a", "0", 1]
    +
     4139.      7
    +
     4140.      7            warn("bad_assignment_a", the_thing);
    +
     4141.      7            return false;
    +
     4142.   5005        }
    +
     4143.   5005        return true;
    +
     4144.   5005    }
    +
     4145.    631
    +
     4146.   2208    function check_not_top_level(thing) {
    +
     4147.   2208
    +
     4148.   2208// Some features should not be at the outermost level.
    +
     4149.   2208
    +
     4150.     34        if (functionage === token_global) {
    +
     4151.     34
    +
     4152.     34// test_cause:
    +
     4153.     34// ["
    +
     4154.     34// while(0){}
    +
     4155.     34// ", "check_not_top_level", "unexpected_at_top_level_a", "while", 1]
    +
     4156.     34
    +
     4157.     34            warn("unexpected_at_top_level_a", thing);
    +
     4158.     34        }
    +
     4159.   2208    }
    +
     4160.    631
    +
     4161.   3360    function check_ordered(type, token_list) {
    +
     4162.   3360
    +
     4163.   3360// This function will warn if <token_list> is unordered.
    +
     4164.   3360
    +
     4165.   4062        token_list.reduce(function (aa, token) {
    +
     4166.   4062            const bb = artifact(token);
    +
     4167.   4048            if (!option_dict.unordered && aa > bb) {
    +
     4168.      4                warn("expected_a_b_before_c_d", token, type, bb, type, aa);
    +
     4169.      4            }
    +
     4170.   4062            return bb;
    +
     4171.   4062        }, "");
    +
     4172.   3360    }
    +
     4173.    631
    +
     4174.   1301    function check_ordered_case(case_list) {
    +
     4175.   1301
    +
     4176.   1301// This function will warn if <case_list> is unordered.
    +
     4177.   1301
    +
     4178.   2691        case_list.filter(noop).map(function (token) {
    +
     4179.   2650            switch (token.identifier || token.id) {
    +
     4180.     34            case "(number)":
    +
     4181.     34                return {
    +
     4182.     34                    order: 1,
    +
     4183.     34                    token,
    +
     4184.     34                    type: "number",
    +
     4185.     34                    value: Number(artifact(token))
    +
     4186.     34                };
    +
     4187.   2604            case "(string)":
    +
     4188.   2604                return {
    +
     4189.   2604                    order: 2,
    +
     4190.   2604                    token,
    +
     4191.   2604                    type: "string",
    +
     4192.   2604                    value: artifact(token)
    +
     4193.   2604                };
    +
     4194.     41            case true:
    +
     4195.     41                return {
    +
     4196.     41                    order: 3,
    +
     4197.     41                    token,
    +
     4198.     41                    type: "identifier",
    +
     4199.     41                    value: artifact(token)
    +
     4200.     41                };
    +
     4201.   2691            }
    +
     4202.   2691        }).reduce(function (aa, bb) {
    +
     4203.   2691            if (
    +
     4204.   2691
    +
     4205.   2691// PR-419 - Hide warning about unordered case-statements behind beta-flag.
    +
     4206.   2691
    +
     4207.   2691                option_dict.beta
    +
     4208.   2691                && !option_dict.unordered
    +
     4209.   2683                && aa && bb
    +
     4210.   1384                && (
    +
     4211.   1384                    aa.order > bb.order
    +
     4212.   1384                    || (aa.order === bb.order && aa.value > bb.value)
    +
     4213.   1384                )
    +
     4214.     10            ) {
    +
     4215.     10                warn(
    +
     4216.     10                    "expected_a_b_before_c_d",
    +
     4217.     10                    bb.token,
    +
     4218.     10                    `case-${bb.type}`,
    +
     4219.     10                    bb.value,
    +
     4220.     10                    `case-${aa.type}`,
    +
     4221.     10                    aa.value
    +
     4222.     10                );
    +
     4223.     10            }
    +
     4224.   2691            return bb;
    +
     4225.   2691        }, undefined);
    +
     4226.   1301    }
    +
     4227.    631
    +
     4228.   3263    function condition() {
    +
     4229.   3263
    +
     4230.   3263// Parse the condition part of a do, if, while.
    +
     4231.   3263
    +
     4232.   3263        const the_paren = token_nxt;
    +
     4233.   3263        let the_value;
    +
     4234.   3263
    +
     4235.   3263// test_cause:
    +
     4236.   3263// ["do{}while()", "condition", "", "", 0]
    +
     4237.   3263// ["if(){}", "condition", "", "", 0]
    +
     4238.   3263// ["while(){}", "condition", "", "", 0]
    +
     4239.   3263
    +
     4240.   3263        test_cause("");
    +
     4241.   3263        the_paren.free = true;
    +
     4242.   3263        advance("(");
    +
     4243.   3263        the_value = parse_expression(0);
    +
     4244.   3263        advance(")");
    +
     4245.      1        if (the_value.wrapped === true) {
    +
     4246.      1
    +
     4247.      1// test_cause:
    +
     4248.      1// ["while((0)){}", "condition", "unexpected_a", "(", 6]
    +
     4249.      1
    +
     4250.      1            warn("unexpected_a", the_paren);
    +
     4251.   3259        }
    +
     4252.   3259
    +
     4253.   3259// Check for anticondition.
    +
     4254.   3259
    +
     4255.   3259        switch (the_value.id) {
    +
     4256.   3259        case "%":
    +
     4257.      1            warn("unexpected_a", the_value);
    +
     4258.      1            break;
    +
     4259.      1        case "&":
    +
     4260.      1            warn("unexpected_a", the_value);
    +
     4261.      1            break;
    +
     4262.     17        case "(number)":
    +
     4263.     17            warn("unexpected_a", the_value);
    +
     4264.     17            break;
    +
     4265.      1        case "(string)":
    +
     4266.      1            warn("unexpected_a", the_value);
    +
     4267.      1            break;
    +
     4268.      1        case "*":
    +
     4269.      1            warn("unexpected_a", the_value);
    +
     4270.      1            break;
    +
     4271.      1        case "+":
    +
     4272.      1            warn("unexpected_a", the_value);
    +
     4273.      1            break;
    +
     4274.      1        case "-":
    +
     4275.      1            warn("unexpected_a", the_value);
    +
     4276.      1            break;
    +
     4277.      1        case "/":
    +
     4278.      1            warn("unexpected_a", the_value);
    +
     4279.      1            break;
    +
     4280.      1        case "<<":
    +
     4281.      1            warn("unexpected_a", the_value);
    +
     4282.      1            break;
    +
     4283.      1        case ">>":
    +
     4284.      1            warn("unexpected_a", the_value);
    +
     4285.      1            break;
    +
     4286.      1        case ">>>":
    +
     4287.      1            warn("unexpected_a", the_value);
    +
     4288.      1            break;
    +
     4289.      1        case "?":
    +
     4290.      1            warn("unexpected_a", the_value);
    +
     4291.      1            break;
    +
     4292.      1        case "^":
    +
     4293.      1            warn("unexpected_a", the_value);
    +
     4294.      1            break;
    +
     4295.      1        case "typeof":
    +
     4296.      1            warn("unexpected_a", the_value);
    +
     4297.      1            break;
    +
     4298.      1        case "|":
    +
     4299.      1            warn("unexpected_a", the_value);
    +
     4300.      1            break;
    +
     4301.      1        case "~":
    +
     4302.      1
    +
     4303.      1// test_cause:
    +
     4304.      1// ["if(0%0){}", "condition", "unexpected_a", "%", 5]
    +
     4305.      1// ["if(0&0){}", "condition", "unexpected_a", "&", 5]
    +
     4306.      1// ["if(0){}", "condition", "unexpected_a", "0", 4]
    +
     4307.      1// ["if(0*0){}", "condition", "unexpected_a", "*", 5]
    +
     4308.      1// ["if(0+0){}", "condition", "unexpected_a", "+", 5]
    +
     4309.      1// ["if(0-0){}", "condition", "unexpected_a", "-", 5]
    +
     4310.      1// ["if(0/0){}", "condition", "unexpected_a", "/", 5]
    +
     4311.      1// ["if(0<<0){}", "condition", "unexpected_a", "<<", 5]
    +
     4312.      1// ["if(0>>0){}", "condition", "unexpected_a", ">>", 5]
    +
     4313.      1// ["if(0>>>0){}", "condition", "unexpected_a", ">>>", 5]
    +
     4314.      1// ["if(0?0:0){}", "condition", "unexpected_a", "?", 5]
    +
     4315.      1// ["if(0^0){}", "condition", "unexpected_a", "^", 5]
    +
     4316.      1// ["if(0|0){}", "condition", "unexpected_a", "|", 5]
    +
     4317.      1// ["if(\"aa\"){}", "condition", "unexpected_a", "aa", 4]
    +
     4318.      1// ["if(typeof 0){}", "condition", "unexpected_a", "typeof", 4]
    +
     4319.      1// ["if(~0){}", "condition", "unexpected_a", "~", 4]
    +
     4320.      1
    +
     4321.      1            warn("unexpected_a", the_value);
    +
     4322.      1            break;
    +
     4323.   3259        }
    +
     4324.   3259        return the_value;
    +
     4325.   3259    }
    +
     4326.    631
    +
     4327.  10096    function constant(id, type, value) {
    +
     4328.  10096
    +
     4329.  10096// Create a constant symbol.
    +
     4330.  10096
    +
     4331.  10096        const the_symbol = symbol(id);
    +
     4332.  10096        the_symbol.constant = true;
    +
     4333.  10096        the_symbol.nud_prefix = (
    +
     4334.  10096            typeof value === "function"
    +
     4335.   4417            ? value
    +
     4336.  18689            : function () {
    +
     4337.  18689                token_now.constant = true;
    +
     4338.   5679                if (value !== undefined) {
    +
     4339.   5679                    token_now.value = value;
    +
     4340.   5679                }
    +
     4341.  18689                return token_now;
    +
     4342.  18689            }
    +
     4343.  10096        );
    +
     4344.  10096        the_symbol.type = type;
    +
     4345.  10096        the_symbol.value = value;
    +
     4346.  10096        return the_symbol;
    +
     4347.  10096    }
    +
     4348.    631
    +
     4349.      5    function constant_Function() {
    +
     4350.      2        if (!option_dict.eval) {
    +
     4351.      2
    +
     4352.      2// test_cause:
    +
     4353.      2// ["Function", "constant_Function", "unexpected_a", "Function", 1]
    +
     4354.      2
    +
     4355.      2            warn("unexpected_a", token_now);
    +
     4356.      3        } else if (token_nxt.id !== "(") {
    +
     4357.      3
    +
     4358.      3// test_cause:
    +
     4359.      3// ["
    +
     4360.      3// /*jslint eval*/
    +
     4361.      3// Function
    +
     4362.      3// ", "constant_Function", "expected_a_before_b", "(end)", 1]
    +
     4363.      3
    +
     4364.      3            warn("expected_a_before_b", token_nxt, "(", artifact());
    +
     4365.      3        }
    +
     4366.      5        return token_now;
    +
     4367.      5    }
    +
     4368.    631
    +
     4369.      1    function constant_arguments() {
    +
     4370.      1
    +
     4371.      1// test_cause:
    +
     4372.      1// ["arguments", "constant_arguments", "unexpected_a", "arguments", 1]
    +
     4373.      1
    +
     4374.      1        warn("unexpected_a", token_now);
    +
     4375.      1        return token_now;
    +
     4376.      1    }
    +
     4377.    631
    +
     4378.      4    function constant_eval() {
    +
     4379.      1        if (!option_dict.eval) {
    +
     4380.      1
    +
     4381.      1// test_cause:
    +
     4382.      1// ["eval", "constant_eval", "unexpected_a", "eval", 1]
    +
     4383.      1
    +
     4384.      1            warn("unexpected_a", token_now);
    +
     4385.      3        } else if (token_nxt.id !== "(") {
    +
     4386.      3
    +
     4387.      3// test_cause:
    +
     4388.      3// ["/*jslint eval*/\neval", "constant_eval", "expected_a_before_b", "(end)", 1]
    +
     4389.      3
    +
     4390.      3            warn("expected_a_before_b", token_nxt, "(", artifact());
    +
     4391.      3        }
    +
     4392.      4        return token_now;
    +
     4393.      4    }
    +
     4394.    631
    +
     4395.      1    function constant_ignore() {
    +
     4396.      1
    +
     4397.      1// test_cause:
    +
     4398.      1// ["ignore", "constant_ignore", "unexpected_a", "ignore", 1]
    +
     4399.      1
    +
     4400.      1        warn("unexpected_a", token_now);
    +
     4401.      1        return token_now;
    +
     4402.      1    }
    +
     4403.    631
    +
     4404.      1    function constant_isInfinite() {
    +
     4405.      1
    +
     4406.      1// test_cause:
    +
     4407.      1// ["isFinite", "constant_isInfinite", "expected_a_b", "isFinite", 1]
    +
     4408.      1
    +
     4409.      1        warn("expected_a_b", token_now, "Number.isFinite", "isFinite");
    +
     4410.      1        return token_now;
    +
     4411.      1    }
    +
     4412.    631
    +
     4413.      1    function constant_isNaN() {
    +
     4414.      1
    +
     4415.      1// test_cause:
    +
     4416.      1// ["isNaN(0)", "constant_isNaN", "number_isNaN", "isNaN", 1]
    +
     4417.      1
    +
     4418.      1        warn("number_isNaN", token_now);
    +
     4419.      1        return token_now;
    +
     4420.      1    }
    +
     4421.    631
    +
     4422.      3    function constant_this() {
    +
     4423.      1        if (!option_dict.this) {
    +
     4424.      1
    +
     4425.      1// test_cause:
    +
     4426.      1// ["this", "constant_this", "unexpected_a", "this", 1]
    +
     4427.      1
    +
     4428.      1            warn("unexpected_a", token_now);
    +
     4429.      1        }
    +
     4430.      3        return token_now;
    +
     4431.      3    }
    +
     4432.    631
    +
     4433.   6366    function enroll(name, role, readonly) {
    +
     4434.   6366
    +
     4435.   6366// Enroll a name into the current function context. The role can be exception,
    +
     4436.   6366// function, label, parameter, or variable. We look for variable redefinition
    +
     4437.   6366// because it causes confusion.
    +
     4438.   6366
    +
     4439.   6366        let earlier;
    +
     4440.   6366        let id = name.id;
    +
     4441.   6366
    +
     4442.   6366// Reserved words may not be enrolled.
    +
     4443.   6366
    +
     4444.     42        if (syntax_dict[id] !== undefined && id !== "ignore") {
    +
     4445.      1
    +
     4446.      1// test_cause:
    +
     4447.      1// ["let undefined", "enroll", "reserved_a", "undefined", 5]
    +
     4448.      1
    +
     4449.      1            warn("reserved_a", name);
    +
     4450.      1            return;
    +
     4451.   6365        }
    +
     4452.   6365
    +
     4453.   6365// Has the name been enrolled in this context?
    +
     4454.   6365
    +
     4455.   6365        earlier = functionage.context[id] || catchage.context[id];
    +
     4456.      7        if (earlier) {
    +
     4457.      7
    +
     4458.      7// test_cause:
    +
     4459.      7// ["let aa;let aa", "enroll", "redefinition_a_b", "1", 12]
    +
     4460.      7
    +
     4461.      7            warn("redefinition_a_b", name, id, earlier.line);
    +
     4462.      7            return;
    +
     4463.   6358        }
    +
     4464.   6358
    +
     4465.   6358// Has the name been enrolled in an outer context?
    +
     4466.   6358
    +
     4467.  10756        function_stack.forEach(function ({
    +
     4468.  10756            context
    +
     4469.  10756        }) {
    +
     4470.  10601            earlier = context[id] || earlier;
    +
     4471.  10756        });
    +
     4472.   6358        if (earlier && id === "ignore") {
    +
     4473.      4            if (earlier.role === "variable") {
    +
     4474.      4
    +
     4475.      4// test_cause:
    +
     4476.      4// ["let ignore;function aa(ignore){}", "enroll", "redefinition_a_b", "1", 24]
    +
     4477.      4
    +
     4478.      4                warn("redefinition_a_b", name, id, earlier.line);
    +
     4479.      4            }
    +
     4480.   6354        } else if (
    +
     4481.   6354            earlier
    +
     4482.   6354            && role !== "parameter" && role !== "function"
    +
     4483.   6354            && (role !== "exception" || earlier.role !== "exception")
    +
     4484.   6354        ) {
    +
     4485.   6354
    +
     4486.   6354// test_cause:
    +
     4487.   6354// ["
    +
     4488.   6354// function aa(){try{aa();}catch(aa){aa();}}
    +
     4489.   6354// ", "enroll", "redefinition_a_b", "1", 31]
    +
     4490.   6354// ["function aa(){var aa;}", "enroll", "redefinition_a_b", "1", 19]
    +
     4491.   6354
    +
     4492.   6354            warn("redefinition_a_b", name, id, earlier.line);
    +
     4493.   6354        } else if (
    +
     4494.   6354            option_dict.beta
    +
     4495.   6354            && global_dict[id]
    +
     4496.   6354            && role !== "parameter"
    +
     4497.   6354        ) {
    +
     4498.   6354
    +
     4499.   6354// test_cause:
    +
     4500.   6354// ["let Array", "enroll", "redefinition_global_a_b", "Array", 5]
    +
     4501.   6354
    +
     4502.   6354            warn("redefinition_global_a_b", name, global_dict[id], id);
    +
     4503.   6358        }
    +
     4504.   6358
    +
     4505.   6358// Enroll it.
    +
     4506.   6358
    +
     4507.   6358        Object.assign(name, {
    +
     4508.   6358            dead: true,
    +
     4509.   6358            init: false,
    +
     4510.   6358            parent: (
    +
     4511.   6358                role === "exception"
    +
     4512.   6358                ? catchage
    +
     4513.   6328                : functionage
    +
     4514.   6366            ),
    +
     4515.   6366            readonly,
    +
     4516.   6366            role,
    +
     4517.   6366            used: 0
    +
     4518.   6366        });
    +
     4519.   6366        name.parent.context[id] = name;
    +
     4520.   6366    }
    +
     4521.    631
    +
     4522.  18930    function infix(bp, id, f) {
    +
     4523.  18930
    +
     4524.  18930// Create an infix operator.
    +
     4525.  18930
    +
     4526.  18930        const the_symbol = symbol(id, bp);
    +
     4527.  31941        the_symbol.led_infix = function (left) {
    +
     4528.  31941            const the_token = token_now;
    +
     4529.  31941            the_token.arity = "binary";
    +
     4530.  23493            if (f !== undefined) {
    +
     4531.  23493                return f(left);
    +
     4532.  23493            }
    +
     4533.   8448            the_token.expression = [left, parse_expression(bp)];
    +
     4534.   8448            return the_token;
    +
     4535.   8448        };
    +
     4536.  18930        return the_symbol;
    +
     4537.  18930    }
    +
     4538.    631
    +
     4539.  11597    function infix_dot(left) {
    +
     4540.  11597        const the_token = token_now;
    +
     4541.  11597        let name = token_nxt;
    +
     4542.  11597        if (
    +
     4543.  11597            (
    +
     4544.  11597                left.id !== "(string)"
    +
     4545.     44                || (name.id !== "indexOf" && name.id !== "repeat")
    +
     4546.  11597            )
    +
     4547.  11554            && (
    +
     4548.  11554                left.id !== "["
    +
     4549.  11554                || (
    +
     4550.  11554                    name.id !== "concat"
    +
     4551.  11554                    && name.id !== "flat"
    +
     4552.  11554                    && name.id !== "flatMap"
    +
     4553.  11554                    && name.id !== "forEach"
    +
     4554.  11554                    && name.id !== "join"
    +
     4555.  11554                    && name.id !== "map"
    +
     4556.  11554                )
    +
     4557.  11554            )
    +
     4558.  11509            && (left.id !== "+" || name.id !== "slice")
    +
     4559.  11504            && (
    +
     4560.  11504                left.id !== "(regexp)"
    +
     4561.  11504                || (name.id !== "exec" && name.id !== "test")
    +
     4562.  11504            )
    +
     4563.  11437        ) {
    +
     4564.  11437
    +
     4565.  11437// test_cause:
    +
     4566.  11437// ["\"\".aa", "check_left", "unexpected_a", ".", 3]
    +
     4567.  11437
    +
     4568.  11437            check_left(left, the_token);
    +
     4569.  11437        }
    +
     4570.      1        if (!name.identifier) {
    +
     4571.      1
    +
     4572.      1// test_cause:
    +
     4573.      1// ["aa.0", "infix_dot", "expected_identifier_a", "0", 4]
    +
     4574.      1
    +
     4575.      1            stop("expected_identifier_a");
    +
     4576.  11596        }
    +
     4577.  11596        advance();
    +
     4578.  11596        survey(name);
    +
     4579.  11596
    +
     4580.  11596// The property name is not an expression.
    +
     4581.  11596
    +
     4582.  11596        the_token.name = name;
    +
     4583.  11596        the_token.expression = left;
    +
     4584.  11596        return the_token;
    +
     4585.  11596    }
    +
     4586.    631
    +
     4587.      1    function infix_fart_unwrapped() {
    +
     4588.      1
    +
     4589.      1// test_cause:
    +
     4590.      1// ["aa=>0", "infix_fart_unwrapped", "wrap_fart_parameter", "=>", 3]
    +
     4591.      1
    +
     4592.      1        return stop("wrap_fart_parameter", token_now);
    +
     4593.      1    }
    +
     4594.    631
    +
     4595.      1    function infix_grave(left) {
    +
     4596.      1        const the_tick = prefix_tick();
    +
     4597.      1
    +
     4598.      1// test_cause:
    +
     4599.      1// ["0``", "check_left", "unexpected_a", "`", 2]
    +
     4600.      1
    +
     4601.      1        check_left(left, the_tick);
    +
     4602.      1        the_tick.expression = [left].concat(the_tick.expression);
    +
     4603.      1        return the_tick;
    +
     4604.      1    }
    +
     4605.    631
    +
     4606.   1461    function infix_lbracket(left) {
    +
     4607.   1461        const the_token = token_now;
    +
     4608.   1461        let name;
    +
     4609.   1461        let the_subscript = parse_expression(0);
    +
     4610.   1438        if (the_subscript.id === "(string)" || the_subscript.id === "`") {
    +
     4611.     25            name = survey(the_subscript);
    +
     4612.     25
    +
     4613.     25// PR-404 - Add new directive "subscript" to play nice with Google Closure.
    +
     4614.     25
    +
     4615.     25            if (!option_dict.subscript && jslint_rgx_identifier.test(name)) {
    +
     4616.     25
    +
     4617.     25// test_cause:
    +
     4618.     25// ["aa[`aa`]", "infix_lbracket", "subscript_a", "aa", 4]
    +
     4619.     25
    +
     4620.     25                warn("subscript_a", the_subscript, name);
    +
     4621.     25            }
    +
     4622.     25        }
    +
     4623.   1461
    +
     4624.   1461// test_cause:
    +
     4625.   1461// ["0[0]", "check_left", "unexpected_a", "[", 2]
    +
     4626.   1461
    +
     4627.   1461        check_left(left, the_token);
    +
     4628.   1461        the_token.expression = [left, the_subscript];
    +
     4629.   1461        advance("]");
    +
     4630.   1461        return the_token;
    +
     4631.   1461    }
    +
     4632.    631
    +
     4633.  10421    function infix_lparen(left) {
    +
     4634.  10421        const the_paren = token_now;
    +
     4635.  10421        let ellipsis;
    +
     4636.  10421        let the_argument;
    +
     4637.  10384        if (left.id !== "function") {
    +
     4638.  10384
    +
     4639.  10384// test_cause:
    +
     4640.  10384// ["(0?0:0)()", "check_left", "unexpected_a", "(", 8]
    +
     4641.  10384// ["0()", "check_left", "unexpected_a", "(", 2]
    +
     4642.  10384
    +
     4643.  10384            check_left(left, the_paren);
    +
     4644.  10384        }
    +
     4645.   7699        if (functionage.arity === "statement" && left.identifier) {
    +
     4646.   5503            functionage.name.calls[left.id] = left;
    +
     4647.   5503        }
    +
     4648.  10421        the_paren.expression = [left];
    +
     4649.   8962        if (token_nxt.id !== ")") {
    +
     4650.   8962
    +
     4651.   8962// Parse/loop through each token in expression (...).
    +
     4652.   8962
    +
     4653.  14286            while (true) {
    +
     4654.  14286                if (token_nxt.id === "...") {
    +
     4655.  14286                    ellipsis = true;
    +
     4656.  14286                    advance("...");
    +
     4657.  14286                }
    +
     4658.  14286                the_argument = parse_expression(10);
    +
     4659.  14286                if (ellipsis) {
    +
     4660.  14286                    the_argument.ellipsis = true;
    +
     4661.  14286                }
    +
     4662.  14286                the_paren.expression.push(the_argument);
    +
     4663.  14286                if (token_nxt.id !== ",") {
    +
     4664.  14286                    break;
    +
     4665.  14286                }
    +
     4666.  14286                advance(",");
    +
     4667.  14286            }
    +
     4668.   8962        }
    +
     4669.  10421        advance(")", the_paren);
    +
     4670.   5260        if (the_paren.expression.length === 2) {
    +
     4671.   5260
    +
     4672.   5260// test_cause:
    +
     4673.   5260// ["aa(0)", "infix_lparen", "free", "", 0]
    +
     4674.   5260
    +
     4675.   5260            test_cause("free");
    +
     4676.   5260            the_paren.free = true;
    +
     4677.   5260            if (the_argument.wrapped === true) {
    +
     4678.   5260
    +
     4679.   5260// test_cause:
    +
     4680.   5260// ["aa((0))", "infix_lparen", "unexpected_a", "(", 3]
    +
     4681.   5260
    +
     4682.   5260                warn("unexpected_a", the_paren);
    +
     4683.   5260            }
    +
     4684.   5260            if (the_argument.id === "(") {
    +
     4685.   5260                the_argument.wrapped = true;
    +
     4686.   5260            }
    +
     4687.   5260        } else {
    +
     4688.   5161
    +
     4689.   5161// test_cause:
    +
     4690.   5161// ["aa()", "infix_lparen", "not_free", "", 0]
    +
     4691.   5161// ["aa(0,0)", "infix_lparen", "not_free", "", 0]
    +
     4692.   5161
    +
     4693.   5161            test_cause("not_free");
    +
     4694.   5161            the_paren.free = false;
    +
     4695.   5161        }
    +
     4696.  10421        return the_paren;
    +
     4697.  10421    }
    +
     4698.    631
    +
     4699.     12    function infix_option_chain(left) {
    +
     4700.     12        const the_token = token_now;
    +
     4701.     12        let name = token_nxt;
    +
     4702.     12        if (
    +
     4703.     12            (
    +
     4704.     12                left.id !== "(string)"
    +
     4705.      1                || (name.id !== "indexOf" && name.id !== "repeat")
    +
     4706.     12            )
    +
     4707.     12            && (
    +
     4708.     12                left.id !== "["
    +
     4709.      1                || (
    +
     4710.      1                    name.id !== "concat"
    +
     4711.      1                    && name.id !== "forEach"
    +
     4712.      1                    && name.id !== "join"
    +
     4713.      1                    && name.id !== "map"
    +
     4714.      1                )
    +
     4715.     12            )
    +
     4716.     12
    +
     4717.     12// test_cause:
    +
     4718.     12// ["(0+0)?.0", "infix_option_chain", "check_left", "", 0]
    +
     4719.     12
    +
     4720.      1            && (left.id !== "+" || name.id !== "slice")
    +
     4721.     12            && (
    +
     4722.     12                left.id !== "(regexp)"
    +
     4723.      1                || (name.id !== "exec" && name.id !== "test")
    +
     4724.     12            )
    +
     4725.     12        ) {
    +
     4726.     12            test_cause("check_left");
    +
     4727.     12
    +
     4728.     12// test_cause:
    +
     4729.     12// ["(/./)?.0", "check_left", "unexpected_a", "?.", 6]
    +
     4730.     12// ["\"aa\"?.0", "check_left", "unexpected_a", "?.", 5]
    +
     4731.     12// ["aa=[]?.aa", "check_left", "unexpected_a", "?.", 6]
    +
     4732.     12
    +
     4733.     12            check_left(left, the_token);
    +
     4734.     12        }
    +
     4735.     12
    +
     4736.     12// Issue #468 - Fix optional dynamic-property/function-call not recognized.
    +
     4737.     12
    +
     4738.     11        if (name.id === "[" || name.id === "(") {
    +
     4739.      2            test_cause("dyn_prop_or_call");
    +
     4740.      2
    +
     4741.      2// test_cause:
    +
     4742.      2// ["aa?.(bb)", "infix_option_chain", "dyn_prop_or_call", "", 0]
    +
     4743.      2// ["aa?.[bb]", "infix_option_chain", "dyn_prop_or_call", "", 0]
    +
     4744.      2
    +
     4745.      2            return left;
    +
     4746.     10        }
    +
     4747.     10        if (!name.identifier) {
    +
     4748.      4
    +
     4749.      4// test_cause:
    +
     4750.      4// ["aa?.0", "infix_option_chain", "expected_identifier_a", "0", 5]
    +
     4751.      4
    +
     4752.      4            stop("expected_identifier_a");
    +
     4753.      6        }
    +
     4754.      6        advance();
    +
     4755.      6        survey(name);
    +
     4756.      6
    +
     4757.      6// The property name is not an expression.
    +
     4758.      6
    +
     4759.      6        the_token.name = name;
    +
     4760.      6        the_token.expression = left;
    +
     4761.      6        return the_token;
    +
     4762.      6    }
    +
     4763.    631
    +
     4764.    631    function infixr(bp, id) {
    +
     4765.    631
    +
     4766.    631// Create a right associative infix operator.
    +
     4767.    631
    +
     4768.    631        const the_symbol = symbol(id, bp);
    +
     4769.      1        the_symbol.led_infix = function parse_infixr_led(left) {
    +
     4770.      1            const the_token = token_now;
    +
     4771.      1
    +
     4772.      1// test_cause:
    +
     4773.      1// ["0**0", "parse_infixr_led", "led_infix", "", 0]
    +
     4774.      1
    +
     4775.      1            test_cause("led_infix");
    +
     4776.      1            the_token.arity = "binary";
    +
     4777.      1            the_token.expression = [left, parse_expression(bp - 1)];
    +
     4778.      1            return the_token;
    +
     4779.      1        };
    +
     4780.    631        return the_symbol;
    +
     4781.    631    }
    +
     4782.    631
    +
     4783.  56002    function parse_expression(rbp, initial) {
    +
     4784.  56002
    +
     4785.  56002// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it
    +
     4786.  56002// is looking for ad hoc lint patterns. We add .fud_stmt to Pratt's model, which
    +
     4787.  56002// is like .nud_prefix except that it is only used on the first token of a
    +
     4788.  56002// statement. Having .fud_stmt makes it much easier to define statement-oriented
    +
     4789.  56002// languages like JavaScript. I retained Pratt's nomenclature.
    +
     4790.  56002// They are elements of the parsing method called Top Down Operator Precedence.
    +
     4791.  56002
    +
     4792.  56002// .nud_prefix  Null denotation. The prefix handler.
    +
     4793.  56002// .fud_stmt    First null denotation. The statement handler.
    +
     4794.  56002// .led_infix   Left denotation. The infix/postfix handler.
    +
     4795.  56002//  lbp         Left binding power of infix operator. It tells us how strongly
    +
     4796.  56002//              the operator binds to the argument at its left.
    +
     4797.  56002//  rbp         Right binding power.
    +
     4798.  56002
    +
     4799.  56002// It processes a nud_prefix (variable, constant, prefix operator). It will then
    +
     4800.  56002// process leds (infix operators) until the bind powers cause it to stop (it
    +
     4801.  56002// consumes tokens until it meets a token whose lbp <= rbp). Specifically, it
    +
     4802.  56002// means that it collects all tokens that bind together before returning to the
    +
     4803.  56002// operator that called it. It returns the expression's parse tree.
    +
     4804.  56002
    +
     4805.  56002// For example, "3 + 1 * 2 * 4 + 5"
    +
     4806.  56002// parses into
    +
     4807.  56002// {
    +
     4808.  56002//     "id": "+",
    +
     4809.  56002//     "expression": [
    +
     4810.  56002//         {
    +
     4811.  56002//             "id": "+",
    +
     4812.  56002//             "expression": [
    +
     4813.  56002//                 {
    +
     4814.  56002//                     "id": "(number)",
    +
     4815.  56002//                     "value": "3"
    +
     4816.  56002//                 },
    +
     4817.  56002//                 {
    +
     4818.  56002//                     "id": "*",
    +
     4819.  56002//                     "expression": [
    +
     4820.  56002//                         {
    +
     4821.  56002//                             "id": "*",
    +
     4822.  56002//                             "expression": [
    +
     4823.  56002//                                 {
    +
     4824.  56002//                                     "id": "(number)",
    +
     4825.  56002//                                     "value": "1"
    +
     4826.  56002//                                 },
    +
     4827.  56002//                                 {
    +
     4828.  56002//                                     "id": "(number)",
    +
     4829.  56002//                                     "value": "2"
    +
     4830.  56002//                                 }
    +
     4831.  56002//                             ]
    +
     4832.  56002//                         },
    +
     4833.  56002//                         {
    +
     4834.  56002//                             "id": "(number)",
    +
     4835.  56002//                             "value": "4"
    +
     4836.  56002//                         }
    +
     4837.  56002//                     ]
    +
     4838.  56002//                 }
    +
     4839.  56002//             ]
    +
     4840.  56002//         },
    +
     4841.  56002//         {
    +
     4842.  56002//             "id": "(number)",
    +
     4843.  56002//             "value": "5"
    +
     4844.  56002//         }
    +
     4845.  56002//     ]
    +
     4846.  56002// }
    +
     4847.  56002
    +
     4848.  56002        let left;
    +
     4849.  56002        let the_symbol;
    +
     4850.  56002
    +
     4851.  56002// Statements will have already advanced, so advance now only if the token is
    +
     4852.  56002// not the first of a statement.
    +
     4853.  56002
    +
     4854.  44642        if (!initial) {
    +
     4855.  44642            advance();
    +
     4856.  44642        }
    +
     4857.  56002        the_symbol = syntax_dict[token_now.id];
    +
     4858.  24510        if (the_symbol !== undefined && the_symbol.nud_prefix !== undefined) {
    +
     4859.  24451
    +
     4860.  24451// test_cause:
    +
     4861.  24451// ["0", "parse_expression", "symbol", "", 0]
    +
     4862.  24451
    +
     4863.  24451            test_cause("symbol");
    +
     4864.  24451            left = the_symbol.nud_prefix();
    +
     4865.  31551        } else if (token_now.identifier) {
    +
     4866.  31551
    +
     4867.  31551// test_cause:
    +
     4868.  31551// ["aa", "parse_expression", "identifier", "", 0]
    +
     4869.  31551
    +
     4870.  31551            test_cause("identifier");
    +
     4871.  31551            left = token_now;
    +
     4872.  31551            left.arity = "variable";
    +
     4873.  31551        } else {
    +
     4874.  31551
    +
     4875.  31551// test_cause:
    +
     4876.  31551// ["!", "parse_expression", "unexpected_a", "(end)", 1]
    +
     4877.  31551// ["/./", "parse_expression", "unexpected_a", "/", 1]
    +
     4878.  31551// ["let aa=`${}`;", "parse_expression", "unexpected_a", "}", 11]
    +
     4879.  31551
    +
     4880.  31551            return stop("unexpected_a", token_now);
    +
     4881.  55966        }
    +
     4882.  55966
    +
     4883.  55966// Parse/loop through each symbol in expression.
    +
     4884.  55966
    +
     4885.  93124        while (true) {
    +
     4886.  93124            the_symbol = syntax_dict[token_nxt.id];
    +
     4887.  93124            if (
    +
     4888.  93124                the_symbol === undefined
    +
     4889.  93124                || the_symbol.led_infix === undefined
    +
     4890.  93124                || the_symbol.lbp <= rbp
    +
     4891.  93124            ) {
    +
     4892.  93124                break;
    +
     4893.  93124            }
    +
     4894.  93124            advance();
    +
     4895.  93124            left = the_symbol.led_infix(left);
    +
     4896.  93124        }
    +
     4897.  55955        return left;
    +
     4898.  55955    }
    +
     4899.    631
    +
     4900.     14    function parse_fart(the_fart) {
    +
     4901.     14
    +
     4902.     14// Give the function properties storing its names and for observing the depth
    +
     4903.     14// of loops and switches.
    +
     4904.     14
    +
     4905.     14        Object.assign(the_fart, {
    +
     4906.     14            arity: "binary",
    +
     4907.     14            context: empty(),
    +
     4908.     14            finally: 0,
    +
     4909.     14            level: functionage.level + 1,
    +
     4910.     14            loop: 0,
    +
     4911.     14            name: anon,
    +
     4912.     14            switch: 0,
    +
     4913.     14            try: 0
    +
     4914.     14        });
    +
     4915.     14
    +
     4916.     14// PR-384 - Relax warning "function_in_loop".
    +
     4917.     14//
    +
     4918.     14//         if (functionage.loop > 0) {
    +
     4919.     14
    +
     4920.     14// // test_cause:
    +
     4921.     14// // ["while(0){aa.map(()=>0);}", "parse_fart", "function_in_loop", "=>", 19]
    +
     4922.     14//
    +
     4923.     14//             warn("function_in_loop", the_fart);
    +
     4924.     14//         }
    +
     4925.     14
    +
     4926.     14// Push the current function context and establish a new one.
    +
     4927.     14
    +
     4928.     14        function_list.push(the_fart);
    +
     4929.     14        function_stack.push(functionage);
    +
     4930.     14        functionage = the_fart;
    +
     4931.     14
    +
     4932.     14// Parse the parameter list.
    +
     4933.     14
    +
     4934.     14        prefix_function_parameter(the_fart);
    +
     4935.     14        advance("=>");
    +
     4936.     14
    +
     4937.     14// The function's body is a block.
    +
     4938.     14
    +
     4939.      3        if (token_nxt.id === "{") {
    +
     4940.      3            if (!option_dict.fart) {
    +
     4941.      3
    +
     4942.      3// test_cause:
    +
     4943.      3// ["()=>{}", "parse_fart", "use_function_not_fart", "=>", 3]
    +
     4944.      3
    +
     4945.      3                warn("use_function_not_fart", the_fart);
    +
     4946.      3            }
    +
     4947.      3            the_fart.block = block("body");
    +
     4948.     11        } else if (
    +
     4949.     11            syntax_dict[token_nxt.id] !== undefined
    +
     4950.     11            && syntax_dict[token_nxt.id].fud_stmt !== undefined
    +
     4951.     11        ) {
    +
     4952.     11
    +
     4953.     11// PR-384 - Bugfix - Fixes issue #379 - warn against naked-statement in fart.
    +
     4954.     11
    +
     4955.     11// test_cause:
    +
     4956.     11// ["()=>delete aa", "parse_fart", "unexpected_a_after_b", "=>", 5]
    +
     4957.     11
    +
     4958.     11            stop("unexpected_a_after_b", token_nxt, token_nxt.id, "=>");
    +
     4959.     11
    +
     4960.     11// The function's body is an expression.
    +
     4961.     11
    +
     4962.     11        } else {
    +
     4963.     11            the_fart.expression = parse_expression(0);
    +
     4964.     13        }
    +
     4965.     13
    +
     4966.     13// Restore the previous context.
    +
     4967.     13
    +
     4968.     13        functionage = function_stack.pop();
    +
     4969.     13        return the_fart;
    +
     4970.     13    }
    +
     4971.    631
    +
     4972.  12961    function parse_json() {
    +
     4973.  12961        let container;
    +
     4974.  12961        let is_dup;
    +
     4975.  12961        let name;
    +
     4976.  12961        let negative;
    +
     4977.  12961        switch (token_nxt.id) {
    +
     4978.   5262        case "(number)":
    +
     4979.   5262            if (!jslint_rgx_json_number.test(token_nxt.value)) {
    +
     4980.   5262
    +
     4981.   5262// test_cause:
    +
     4982.   5262// ["[-.0]", "parse_json", "unexpected_a", ".", 3]
    +
     4983.   5262// ["[-0x0]", "parse_json", "unexpected_a", "0x0", 3]
    +
     4984.   5262// ["[.0]", "parse_json", "unexpected_a", ".", 2]
    +
     4985.   5262// ["[0x0]", "parse_json", "unexpected_a", "0x0", 2]
    +
     4986.   5262
    +
     4987.   5262                warn("unexpected_a");
    +
     4988.   5262            }
    +
     4989.   5262            advance("(number)");
    +
     4990.   5262            return token_now;
    +
     4991.   1767        case "(string)":
    +
     4992.   1767            if (token_nxt.quote !== "\"") {
    +
     4993.   1767
    +
     4994.   1767// test_cause:
    +
     4995.   1767// ["['']", "parse_json", "unexpected_a", "'", 2]
    +
     4996.   1767
    +
     4997.   1767                warn("unexpected_a", token_nxt, token_nxt.quote);
    +
     4998.   1767            }
    +
     4999.   1767            advance("(string)");
    +
     5000.   1767            return token_now;
    +
     5001.      3        case "-":
    +
     5002.      3            negative = token_nxt;
    +
     5003.      3            negative.arity = "unary";
    +
     5004.      3            advance("-");
    +
     5005.      3
    +
     5006.      3// Recurse parse_json().
    +
     5007.      3
    +
     5008.      3            negative.expression = parse_json();
    +
     5009.      3            return negative;
    +
     5010.   1649        case "[":
    +
     5011.   1649
    +
     5012.   1649// test_cause:
    +
     5013.   1649// ["[]", "parse_json", "bracket", "", 0]
    +
     5014.   1649
    +
     5015.   1649            test_cause("bracket");
    +
     5016.   1649            container = token_nxt;
    +
     5017.   1649            container.expression = [];
    +
     5018.   1649            advance("[");
    +
     5019.   1649            if (token_nxt.id !== "]") {
    +
     5020.   3300                while (true) {
    +
     5021.   3300
    +
     5022.   3300// Recurse parse_json().
    +
     5023.   3300
    +
     5024.   3300                    container.expression.push(parse_json());
    +
     5025.   3300                    if (token_nxt.id !== ",") {
    +
     5026.   3300
    +
     5027.   3300// test_cause:
    +
     5028.   3300// ["[0,0]", "parse_json", "comma", "", 0]
    +
     5029.   3300
    +
     5030.   3300                        test_cause("comma");
    +
     5031.   3300                        break;
    +
     5032.   3300                    }
    +
     5033.   3300                    advance(",");
    +
     5034.   3300                }
    +
     5035.   1649            }
    +
     5036.   1649            advance("]", container);
    +
     5037.   1649            return container;
    +
     5038.    509        case "false":
    +
     5039.    511        case "null":
    +
     5040.    896        case "true":
    +
     5041.    896
    +
     5042.    896// test_cause:
    +
     5043.    896// ["[false]", "parse_json", "constant", "", 0]
    +
     5044.    896// ["[null]", "parse_json", "constant", "", 0]
    +
     5045.    896// ["[true]", "parse_json", "constant", "", 0]
    +
     5046.    896
    +
     5047.    896            test_cause("constant");
    +
     5048.    896            advance();
    +
     5049.    896            return token_now;
    +
     5050.   3379        case "{":
    +
     5051.   3379
    +
     5052.   3379// test_cause:
    +
     5053.   3379// ["{}", "parse_json", "brace", "", 0]
    +
     5054.   3379
    +
     5055.   3379            test_cause("brace");
    +
     5056.   3379            container = token_nxt;
    +
     5057.   3379
    +
     5058.   3379// Explicit empty-object required to detect "__proto__".
    +
     5059.   3379
    +
     5060.   3379            is_dup = empty();
    +
     5061.   3379            container.expression = [];
    +
     5062.   3379            advance("{");
    +
     5063.   3379            if (token_nxt.id !== "}") {
    +
     5064.   3379
    +
     5065.   3379// JSON
    +
     5066.   3379// Parse/loop through each property in {...}.
    +
     5067.   3379
    +
     5068.   9636                while (true) {
    +
     5069.   9636                    if (token_nxt.quote !== "\"") {
    +
     5070.   9636
    +
     5071.   9636// test_cause:
    +
     5072.   9636// ["{0:0}", "parse_json", "unexpected_a", "0", 2]
    +
     5073.   9636
    +
     5074.   9636                        warn(
    +
     5075.   9636                            "unexpected_a",
    +
     5076.   9636                            token_nxt,
    +
     5077.   9636                            token_nxt.quote
    +
     5078.   9636                        );
    +
     5079.   9636                    }
    +
     5080.   9636                    name = token_nxt;
    +
     5081.   9636                    advance("(string)");
    +
     5082.   9636                    if (is_dup[token_now.value] !== undefined) {
    +
     5083.   9636
    +
     5084.   9636// test_cause:
    +
     5085.   9636// ["{\"aa\":0,\"aa\":0}", "parse_json", "duplicate_a", "aa", 9]
    +
     5086.   9636
    +
     5087.   9636                        warn("duplicate_a", token_now);
    +
     5088.   9636                    } else if (token_now.value === "__proto__") {
    +
     5089.   9636
    +
     5090.   9636// test_cause:
    +
     5091.   9636// ["{\"__proto__\":0}", "parse_json", "weird_property_a", "__proto__", 2]
    +
     5092.   9636
    +
     5093.   9636                        warn("weird_property_a", token_now);
    +
     5094.   9636                    } else {
    +
     5095.   9636                        is_dup[token_now.value] = token_now;
    +
     5096.   9636                    }
    +
     5097.   9636                    advance(":");
    +
     5098.   9636                    container.expression.push(
    +
     5099.   9636
    +
     5100.   9636// Recurse parse_json().
    +
     5101.   9636
    +
     5102.   9636                        Object.assign(parse_json(), {
    +
     5103.   9636                            label: name
    +
     5104.   9636                        })
    +
     5105.   9636                    );
    +
     5106.   9636                    if (token_nxt.id !== ",") {
    +
     5107.   9636                        break;
    +
     5108.   9636                    }
    +
     5109.   9636                    advance(",");
    +
     5110.   9636                }
    +
     5111.   3379            }
    +
     5112.   3379            advance("}", container);
    +
     5113.   3379            return container;
    +
     5114.      5        default:
    +
     5115.      5
    +
     5116.      5// test_cause:
    +
     5117.      5// ["[undefined]", "parse_json", "unexpected_a", "undefined", 2]
    +
     5118.      5
    +
     5119.      5            stop("unexpected_a");
    +
     5120.  12961        }
    +
     5121.  12961    }
    +
     5122.    631
    +
     5123.  20929    function parse_statement() {
    +
     5124.  20929
    +
     5125.  20929// Parse a statement. Any statement may have a label, but only four statements
    +
     5126.  20929// have use for one. A statement can be one of the standard statements, or
    +
     5127.  20929// an assignment expression, or an invocation expression.
    +
     5128.  20929
    +
     5129.  20929        let first;
    +
     5130.  20929        let the_label;
    +
     5131.  20929        let the_statement;
    +
     5132.  20929        let the_symbol;
    +
     5133.  20929        advance();
    +
     5134.  20742        if (token_now.identifier && token_nxt.id === ":") {
    +
     5135.     13            the_label = token_now;
    +
     5136.     13            if (the_label.id === "ignore") {
    +
     5137.     13
    +
     5138.     13// test_cause:
    +
     5139.     13// ["ignore:", "parse_statement", "unexpected_a", "ignore", 1]
    +
     5140.     13
    +
     5141.     13                warn("unexpected_a", the_label);
    +
     5142.     13            }
    +
     5143.     13            advance(":");
    +
     5144.     13            switch (token_nxt.id) {
    +
     5145.     13            case "do":
    +
     5146.     13            case "for":
    +
     5147.     13            case "switch":
    +
     5148.     13            case "while":
    +
     5149.     13
    +
     5150.     13// test_cause:
    +
     5151.     13// ["aa:do{}", "parse_statement", "the_statement_label", "do", 0]
    +
     5152.     13// ["aa:for{}", "parse_statement", "the_statement_label", "for", 0]
    +
     5153.     13// ["aa:switch{}", "parse_statement", "the_statement_label", "switch", 0]
    +
     5154.     13// ["aa:while{}", "parse_statement", "the_statement_label", "while", 0]
    +
     5155.     13
    +
     5156.     13                test_cause("the_statement_label", token_nxt.id);
    +
     5157.     13                enroll(the_label, "label", true);
    +
     5158.     13                the_label.dead = false;
    +
     5159.     13                the_label.init = true;
    +
     5160.     13                the_statement = parse_statement();
    +
     5161.     13                functionage.statement_prv = the_statement;
    +
     5162.     13                the_statement.label = the_label;
    +
     5163.     13                the_statement.statement = true;
    +
     5164.     13                return the_statement;
    +
     5165.     13            }
    +
     5166.     13            advance();
    +
     5167.     13
    +
     5168.     13// test_cause:
    +
     5169.     13// ["aa:", "parse_statement", "unexpected_label_a", "aa", 1]
    +
     5170.     13
    +
     5171.     13            warn("unexpected_label_a", the_label);
    +
     5172.  20920        }
    +
     5173.  20920
    +
     5174.  20920// Parse the statement.
    +
     5175.  20920
    +
     5176.  20920        first = token_now;
    +
     5177.  20920        first.statement = true;
    +
     5178.  20920        the_symbol = syntax_dict[first.id];
    +
     5179.  20920        if (
    +
     5180.  20920            the_symbol !== undefined
    +
     5181.  20920            && the_symbol.fud_stmt !== undefined
    +
     5182.  20929
    +
     5183.  20929// PR-318 - Bugfix - Fixes issues #316, #317 - dynamic-import().
    +
     5184.  20929
    +
     5185.  10154            && !(the_symbol.id === "import" && token_nxt.id === "(")
    +
     5186.  10152        ) {
    +
     5187.  10152            the_symbol.disrupt = false;
    +
     5188.  10152            the_symbol.statement = true;
    +
     5189.  10152            token_now.arity = "statement";
    +
     5190.  10152            the_statement = the_symbol.fud_stmt();
    +
     5191.  10152            functionage.statement_prv = the_statement;
    +
     5192.  10768        } else {
    +
     5193.  10768
    +
     5194.  10768// It is an expression statement.
    +
     5195.  10768
    +
     5196.  10768            the_statement = parse_expression(0, true);
    +
     5197.  10768            functionage.statement_prv = the_statement;
    +
     5198.  10768            if (the_statement.wrapped && the_statement.id !== "(") {
    +
     5199.  10768
    +
     5200.  10768// test_cause:
    +
     5201.  10768// ["(0)", "parse_statement", "unexpected_a", "(", 1]
    +
     5202.  10768
    +
     5203.  10768                warn("unexpected_a", first);
    +
     5204.  10768            }
    +
     5205.  10768            semicolon();
    +
     5206.  20826        }
    +
     5207.  20826        if (the_label !== undefined) {
    +
     5208.      1            the_label.dead = true;
    +
     5209.  20826        }
    +
     5210.  20826        return the_statement;
    +
     5211.  20826    }
    +
     5212.    631
    +
     5213.   7501    function parse_statements() {
    +
     5214.   7501
    +
     5215.   7501// Parse a list of statements. Give a warning if an unreachable statement
    +
     5216.   7501// follows a disruptive statement.
    +
     5217.   7501
    +
     5218.   7501        const statement_list = [];
    +
     5219.   7501        let a_statement;
    +
     5220.   7501        let disrupt = false;
    +
     5221.   7501
    +
     5222.   7501// Parse/loop each statement until a statement-terminator is reached.
    +
     5223.   7501
    +
     5224.  28003        while (true) {
    +
     5225.  28003            switch (token_nxt.id) {
    +
     5226.  28003            case "(end)":
    +
     5227.  28003            case "case":
    +
     5228.  28003            case "default":
    +
     5229.  28003            case "else":
    +
     5230.  28003            case "}":
    +
     5231.  28003
    +
     5232.  28003// test_cause:
    +
     5233.  28003// [";", "parse_statements", "closer", "", 0]
    +
     5234.  28003// ["case", "parse_statements", "closer", "", 0]
    +
     5235.  28003// ["default", "parse_statements", "closer", "", 0]
    +
     5236.  28003// ["else", "parse_statements", "closer", "", 0]
    +
     5237.  28003// ["}", "parse_statements", "closer", "", 0]
    +
     5238.  28003
    +
     5239.  28003                test_cause("closer");
    +
     5240.  28003                return statement_list;
    +
     5241.  28003            }
    +
     5242.  28003            a_statement = parse_statement();
    +
     5243.  28003            statement_list.push(a_statement);
    +
     5244.  28003            if (disrupt) {
    +
     5245.  28003
    +
     5246.  28003// test_cause:
    +
     5247.  28003// ["while(0){break;0;}", "parse_statements", "unreachable_a", "0", 16]
    +
     5248.  28003
    +
     5249.  28003                warn("unreachable_a", a_statement);
    +
     5250.  28003            }
    +
     5251.  28003            disrupt = a_statement.disrupt;
    +
     5252.  28003        }
    +
     5253.   7501    }
    +
     5254.    631
    +
     5255.   1262    function postassign(id) {
    +
     5256.   1262
    +
     5257.   1262// Create one of the postassign operators.
    +
     5258.   1262
    +
     5259.   1262        const the_symbol = symbol(id, 150);
    +
     5260.      1        the_symbol.led_infix = function (left) {
    +
     5261.      1            token_now.expression = left;
    +
     5262.      1            token_now.arity = "postassign";
    +
     5263.      1            check_mutation(token_now.expression);
    +
     5264.      1            return token_now;
    +
     5265.      1        };
    +
     5266.   1262        return the_symbol;
    +
     5267.   1262    }
    +
     5268.    631
    +
     5269.   1262    function preassign(id) {
    +
     5270.   1262
    +
     5271.   1262// Create one of the preassign operators.
    +
     5272.   1262
    +
     5273.   1262        const the_symbol = symbol(id);
    +
     5274.      2        the_symbol.nud_prefix = function () {
    +
     5275.      2            const the_token = token_now;
    +
     5276.      2            the_token.arity = "preassign";
    +
     5277.      2            the_token.expression = parse_expression(150);
    +
     5278.      2            check_mutation(the_token.expression);
    +
     5279.      2            return the_token;
    +
     5280.      2        };
    +
     5281.   1262        return the_symbol;
    +
     5282.   1262    }
    +
     5283.    631
    +
     5284.  10727    function prefix(id, f) {
    +
     5285.  10727
    +
     5286.  10727// Create a prefix operator.
    +
     5287.  10727
    +
     5288.  10727        const the_symbol = symbol(id);
    +
     5289.   5744        the_symbol.nud_prefix = function () {
    +
     5290.   5744            const the_token = token_now;
    +
     5291.   5744            the_token.arity = "unary";
    +
     5292.   4932            if (typeof f === "function") {
    +
     5293.   4932                return f();
    +
     5294.   4932            }
    +
     5295.    812            the_token.expression = parse_expression(150);
    +
     5296.    812            return the_token;
    +
     5297.    812        };
    +
     5298.  10727        return the_symbol;
    +
     5299.  10727    }
    +
     5300.    631
    +
     5301.      1    function prefix_assign_divide() {
    +
     5302.      1
    +
     5303.      1// test_cause:
    +
     5304.      1// ["/=", "prefix_assign_divide", "expected_a_b", "/=", 1]
    +
     5305.      1
    +
     5306.      1        stop("expected_a_b", token_now, "/\\=", "/=");
    +
     5307.      1    }
    +
     5308.    631
    +
     5309.    136    function prefix_async() {
    +
     5310.    136        let the_async = token_now;
    +
     5311.    136        let the_function;
    +
     5312.    136        token_nxt.arity = the_async.arity;
    +
     5313.    136
    +
     5314.    136// PR-414 - Parse async fart.
    +
     5315.    136
    +
     5316.      3        if (token_nxt.fart) {
    +
     5317.      3            advance("(");
    +
     5318.      3            the_function = Object.assign(token_now.fart, {
    +
     5319.      3                async: 1
    +
     5320.      3            });
    +
     5321.      3            if (!option_dict.fart) {
    +
     5322.      3
    +
     5323.      3// test_cause:
    +
     5324.      3// ["async()=>0", "prefix_async", "use_function_not_fart", "=>", 8]
    +
     5325.      3
    +
     5326.      3                warn("use_function_not_fart", the_function);
    +
     5327.      3            }
    +
     5328.      3            prefix_lparen();
    +
     5329.      3
    +
     5330.      3// Parse async function.
    +
     5331.      3
    +
     5332.    133        } else {
    +
     5333.    133            advance("function");
    +
     5334.    133            the_function = Object.assign(token_now, {
    +
     5335.    133                async: 1
    +
     5336.    133            });
    +
     5337.    133            prefix_function();
    +
     5338.    133        }
    +
     5339.      3        if (the_function.async === 1) {
    +
     5340.      3
    +
     5341.      3// test_cause:
    +
     5342.      3// ["
    +
     5343.      3// async function aa(){}
    +
     5344.      3// ", "prefix_async", "missing_await_statement", "function", 7]
    +
     5345.      3
    +
     5346.      3            warn("missing_await_statement", the_function);
    +
     5347.      3        }
    +
     5348.    136        return the_function;
    +
     5349.    136    }
    +
     5350.    631
    +
     5351.    297    function prefix_await() {
    +
     5352.    297        const the_await = token_now;
    +
     5353.    297
    +
     5354.    297// PR-370 - Add top-level-await support.
    +
     5355.    297
    +
     5356.      4        if (functionage.async === 0 && functionage !== token_global) {
    +
     5357.      2
    +
     5358.      2// test_cause:
    +
     5359.      2// ["function aa(){aa=await 0;}", "prefix_await", "unexpected_a", "await", 18]
    +
     5360.      2// ["function aa(){await 0;}", "prefix_await", "unexpected_a", "await", 15]
    +
     5361.      2
    +
     5362.      2            warn("unexpected_a", the_await);
    +
     5363.    295        } else {
    +
     5364.    295            functionage.async += 1;
    +
     5365.    295        }
    +
     5366.    185        if (the_await.arity === "statement") {
    +
     5367.    185
    +
     5368.    185// PR-405 - Bugfix - fix expression after "await" mis-identified as statement.
    +
     5369.    185
    +
     5370.    185            the_await.expression = parse_expression(150);
    +
     5371.    185            semicolon();
    +
     5372.    185        } else {
    +
     5373.    112            the_await.expression = parse_expression(150);
    +
     5374.    112        }
    +
     5375.    297        return the_await;
    +
     5376.    297    }
    +
     5377.    631
    +
     5378.      1    function prefix_fart() {
    +
     5379.      1
    +
     5380.      1// test_cause:
    +
     5381.      1// ["=>0", "prefix_fart", "expected_a_before_b", "=>", 1]
    +
     5382.      1
    +
     5383.      1        return stop("expected_a_before_b", token_now, "()", "=>");
    +
     5384.      1    }
    +
     5385.    631
    +
     5386.   2005    function prefix_function(the_function) {
    +
     5387.     11        let name = the_function && the_function.name;
    +
     5388.   1994        if (the_function === undefined) {
    +
     5389.   1994            the_function = token_now;
    +
     5390.   1994
    +
     5391.   1994// A function statement must have a name that will be in the parent's scope.
    +
     5392.   1994
    +
     5393.   1994            if (the_function.arity === "statement") {
    +
     5394.   1994                if (!token_nxt.identifier) {
    +
     5395.   1994
    +
     5396.   1994// test_cause:
    +
     5397.   1994// ["function(){}", "prefix_function", "expected_identifier_a", "(", 9]
    +
     5398.   1994// ["function*aa(){}", "prefix_function", "expected_identifier_a", "*", 9]
    +
     5399.   1994
    +
     5400.   1994                    return stop("expected_identifier_a");
    +
     5401.   1994                }
    +
     5402.   1994                name = token_nxt;
    +
     5403.   1994                enroll(name, "variable", true);
    +
     5404.   1994                the_function.name = Object.assign(name, {
    +
     5405.   1994                    calls: empty(),
    +
     5406.   1994
    +
     5407.   1994// PR-331 - Bugfix - Fixes issue #272 - function hoisting not allowed.
    +
     5408.   1994
    +
     5409.   1994                    dead: false,
    +
     5410.   1994                    init: true
    +
     5411.   1994                });
    +
     5412.   1994                advance();
    +
     5413.   1994            } else if (name === undefined) {
    +
     5414.   1994
    +
     5415.   1994// A function expression may have an optional name.
    +
     5416.   1994
    +
     5417.   1994                the_function.name = anon;
    +
     5418.   1994                if (token_nxt.identifier) {
    +
     5419.   1994                    name = token_nxt;
    +
     5420.   1994                    the_function.name = name;
    +
     5421.   1994                    advance();
    +
     5422.   1994                }
    +
     5423.   1994            }
    +
     5424.   2003        }
    +
     5425.   2003
    +
     5426.   2003//  Probably deadcode.
    +
     5427.   2003//  if (mode_mega) {
    +
     5428.   2003//      warn("unexpected_a", the_function);
    +
     5429.   2003//  }
    +
     5430.   2003//  jslint_assert(!mode_mega, `Expected !mode_mega.`);
    +
     5431.   2003
    +
     5432.   2003// PR-378 - Relax warning "function_in_loop".
    +
     5433.   2003//
    +
     5434.   2003// // Don't create functions in loops. It is inefficient, and it can lead to
    +
     5435.   2003// // scoping errors.
    +
     5436.   2003//
    +
     5437.   2003//         if (functionage.loop > 0) {
    +
     5438.   2003//
    +
     5439.   2003// // test_cause:
    +
     5440.   2003// // ["
    +
     5441.   2003// // while(0){aa.map(function(){});}
    +
     5442.   2003// // ", "prefix_function", "function_in_loop", "function", 17]
    +
     5443.   2003//
    +
     5444.   2003//             warn("function_in_loop", the_function);
    +
     5445.   2003//         }
    +
     5446.   2003
    +
     5447.   2003// Give the function properties for storing its names and for observing the
    +
     5448.   2003// depth of loops and switches.
    +
     5449.   2003
    +
     5450.   2003        Object.assign(the_function, {
    +
     5451.   2003            async: the_function.async || 0,
    +
     5452.   2005            context: empty(),
    +
     5453.   2005            finally: 0,
    +
     5454.   2005            level: functionage.level + 1,
    +
     5455.   2005            loop: 0,
    +
     5456.   2005            statement_prv: undefined,
    +
     5457.   2005            switch: 0,
    +
     5458.   2005            try: 0
    +
     5459.   2005        });
    +
     5460.    929        if (the_function.arity !== "statement" && typeof name === "object") {
    +
     5461.     38
    +
     5462.     38// test_cause:
    +
     5463.     38// ["let aa=function bb(){return;};", "prefix_function", "expression", "bb", 0]
    +
     5464.     38
    +
     5465.     38            test_cause("expression", name.id);
    +
     5466.     38            enroll(name, "function", true);
    +
     5467.     38            name.dead = false;
    +
     5468.     38            name.init = true;
    +
     5469.     38            name.used = 1;
    +
     5470.   2003        }
    +
     5471.   2003
    +
     5472.   2003// PR-334 - Bugfix - fix function-redefinition not warned inside function-call.
    +
     5473.   2003// Push the current function context and establish a new one.
    +
     5474.   2003
    +
     5475.   2003        function_list.push(the_function);
    +
     5476.   2003        function_stack.push(functionage);
    +
     5477.   2003        functionage = the_function;
    +
     5478.   2003
    +
     5479.   2003// Parse the parameter list.
    +
     5480.   2003
    +
     5481.   2003        advance("(");
    +
     5482.   2003        token_now.arity = "function";
    +
     5483.   2003        prefix_function_parameter(the_function);
    +
     5484.   2003
    +
     5485.   2003// The function's body is a block.
    +
     5486.   2003
    +
     5487.   2003        the_function.block = block("body");
    +
     5488.   2003        if (
    +
     5489.   2003            the_function.arity === "statement"
    +
     5490.   2003            && token_nxt.line === token_now.line
    +
     5491.      2        ) {
    +
     5492.      2
    +
     5493.      2// test_cause:
    +
     5494.      2// ["function aa(){}0", "prefix_function", "unexpected_a", "0", 16]
    +
     5495.      2
    +
     5496.      2            return stop("unexpected_a");
    +
     5497.   1990        }
    +
     5498.   1990        if (
    +
     5499.   1990            token_nxt.id === "."
    +
     5500.   1990            || token_nxt.id === "?."
    +
     5501.   2005
    +
     5502.   2005// PR-459 - Allow destructuring-assignment after function-definition.
    +
     5503.   2005
    +
     5504.   2005            // || token_nxt.id === "["
    +
     5505.      2        ) {
    +
     5506.      2
    +
     5507.      2// test_cause:
    +
     5508.      2// ["function aa(){}\n.aa", "prefix_function", "unexpected_a", ".", 1]
    +
     5509.      2// ["function aa(){}\n?.aa", "prefix_function", "unexpected_a", "?.", 1]
    +
     5510.      2
    +
     5511.      2            warn("unexpected_a");
    +
     5512.   1990        }
    +
     5513.   1990
    +
     5514.   1990// Check functions are ordered.
    +
     5515.   1990
    +
     5516.   1990        check_ordered(
    +
     5517.   1990            "function",
    +
     5518.   1990            function_list.slice(
    +
     5519.   1990                function_list.indexOf(the_function) + 1
    +
     5520.   2515            ).map(function ({
    +
     5521.   2515                level,
    +
     5522.   2515                name
    +
     5523.   2515            }) {
    +
     5524.   1990                return (level === the_function.level + 1) && name;
    +
     5525.   2515            }).filter(function (name) {
    +
     5526.   2510                return option_dict.beta && name && name.id;
    +
     5527.   2515            })
    +
     5528.   1990        );
    +
     5529.   1990
    +
     5530.   1990// Restore the previous context.
    +
     5531.   1990
    +
     5532.   1990        functionage = function_stack.pop();
    +
     5533.   1990        return the_function;
    +
     5534.   1990    }
    +
     5535.    631
    +
     5536.   2017    function prefix_function_parameter(the_function) {
    +
     5537.   2017
    +
     5538.   2017// This function will parse input <parameters> at beginning of <the_function>
    +
     5539.   2017
    +
     5540.   2017        let optional;
    +
     5541.   2017        let parameters = [];
    +
     5542.   2017        let signature = ["("];
    +
     5543.   2017        let subparam;
    +
     5544.   2781        function param_enroll(name) {
    +
     5545.   2514            if (name.identifier) {
    +
     5546.   2514                enroll(name, "parameter", false);
    +
     5547.   2514            } else {
    +
     5548.    267
    +
     5549.    267// test_cause:
    +
     5550.    267// ["([aa])=>0", "param_enroll", "use_function_not_fart", "=>", 7]
    +
     5551.    267// ["({aa})=>0", "param_enroll", "use_function_not_fart", "=>", 7]
    +
     5552.    267
    +
     5553.    267                if (the_function.id === "=>" && !option_dict.fart) {
    +
     5554.    267                    warn("use_function_not_fart", the_function);
    +
     5555.    267                }
    +
     5556.    267
    +
     5557.    267// Recurse param_enroll().
    +
     5558.    267
    +
     5559.    267                name.names.forEach(param_enroll);
    +
     5560.    267            }
    +
     5561.   2781        }
    +
     5562.   2077        function param_parse() {
    +
     5563.   2077            let ellipsis = false;
    +
     5564.   2077            let param;
    +
     5565.    227            if (token_nxt.id === "{") {
    +
     5566.    227                if (optional !== undefined) {
    +
     5567.    227
    +
     5568.    227// test_cause:
    +
     5569.    227// ["function aa(aa=0,{}){}", "param_parse", "required_a_optional_b", "aa", 18]
    +
     5570.    227
    +
     5571.    227                    warn(
    +
     5572.    227                        "required_a_optional_b",
    +
     5573.    227                        token_nxt,
    +
     5574.    227                        token_nxt.id,
    +
     5575.    227                        optional.id
    +
     5576.    227                    );
    +
     5577.    227                }
    +
     5578.    227                param = token_nxt;
    +
     5579.    227                param.names = [];
    +
     5580.    227                advance("{");
    +
     5581.    227                signature.push("{");
    +
     5582.    625                while (true) {
    +
     5583.    625                    subparam = token_nxt;
    +
     5584.    625                    if (!subparam.identifier) {
    +
     5585.    625
    +
     5586.    625// test_cause:
    +
     5587.    625// ["function aa(aa=0,{}){}", "param_parse", "expected_identifier_a", "}", 19]
    +
     5588.    625// ["function aa({0}){}", "param_parse", "expected_identifier_a", "0", 14]
    +
     5589.    625
    +
     5590.    625                        return stop("expected_identifier_a");
    +
     5591.    625                    }
    +
     5592.    625                    survey(subparam);
    +
     5593.    625                    advance();
    +
     5594.    625                    signature.push(subparam.id);
    +
     5595.    625                    if (token_nxt.id === ":") {
    +
     5596.    625                        advance(":");
    +
     5597.    625                        advance();
    +
     5598.    625                        token_now.label = subparam;
    +
     5599.    625                        subparam = token_now;
    +
     5600.    625                        if (!subparam.identifier) {
    +
     5601.    625
    +
     5602.    625// test_cause:
    +
     5603.    625// ["function aa({aa:0}){}", "param_parse", "expected_identifier_a", "}", 18]
    +
     5604.    625
    +
     5605.    625                            return stop(
    +
     5606.    625                                "expected_identifier_a",
    +
     5607.    625                                token_nxt
    +
     5608.    625                            );
    +
     5609.    625                        }
    +
     5610.    625                    }
    +
     5611.    625
    +
     5612.    625// test_cause:
    +
     5613.    625// ["function aa({aa=aa},aa){}", "param_parse", "equal", "", 0]
    +
     5614.    625
    +
     5615.    625                    test_cause("equal");
    +
     5616.    625                    if (token_nxt.id === "=") {
    +
     5617.    625                        advance("=");
    +
     5618.    625                        subparam.expression = parse_expression();
    +
     5619.    625                        param.open = true;
    +
     5620.    625                    }
    +
     5621.    625                    param.names.push(subparam);
    +
     5622.    625                    if (token_nxt.id === ",") {
    +
     5623.    625                        advance(",");
    +
     5624.    625                        signature.push(", ");
    +
     5625.    625                    } else {
    +
     5626.    625                        break;
    +
     5627.    625                    }
    +
     5628.    625                }
    +
     5629.    227                parameters.push(param);
    +
     5630.    227
    +
     5631.    227// test_cause:
    +
     5632.    227// ["
    +
     5633.    227// function aa({bb,aa}){}
    +
     5634.    227// ", "check_ordered", "expected_a_b_before_c_d", "aa", 17]
    +
     5635.    227
    +
     5636.    227                check_ordered("parameter", param.names);
    +
     5637.    227                advance("}");
    +
     5638.    227                signature.push("}");
    +
     5639.    227                if (token_nxt.id === ",") {
    +
     5640.    227                    advance(",");
    +
     5641.    227                    signature.push(", ");
    +
     5642.    227                    param_parse();
    +
     5643.    227                    return;
    +
     5644.    227                }
    +
     5645.   1850            } else if (token_nxt.id === "[") {
    +
     5646.   1850                if (optional !== undefined) {
    +
     5647.   1850
    +
     5648.   1850// test_cause:
    +
     5649.   1850// ["function aa(aa=0,[]){}", "param_parse", "required_a_optional_b", "aa", 18]
    +
     5650.   1850
    +
     5651.   1850                    warn(
    +
     5652.   1850                        "required_a_optional_b",
    +
     5653.   1850                        token_nxt,
    +
     5654.   1850                        token_nxt.id,
    +
     5655.   1850                        optional.id
    +
     5656.   1850                    );
    +
     5657.   1850                }
    +
     5658.   1850                param = token_nxt;
    +
     5659.   1850                param.names = [];
    +
     5660.   1850                advance("[");
    +
     5661.   1850                signature.push("[]");
    +
     5662.   1850                while (true) {
    +
     5663.   1850                    subparam = token_nxt;
    +
     5664.   1850                    if (!subparam.identifier) {
    +
     5665.   1850
    +
     5666.   1850// test_cause:
    +
     5667.   1850// ["function aa(aa=0,[]){}", "param_parse", "expected_identifier_a", "]", 19]
    +
     5668.   1850
    +
     5669.   1850                        return stop("expected_identifier_a");
    +
     5670.   1850                    }
    +
     5671.   1850                    advance();
    +
     5672.   1850                    param.names.push(subparam);
    +
     5673.   1850
    +
     5674.   1850// test_cause:
    +
     5675.   1850// ["function aa([aa=aa],aa){}", "param_parse", "id", "", 0]
    +
     5676.   1850
    +
     5677.   1850                    test_cause("id");
    +
     5678.   1850                    if (token_nxt.id === "=") {
    +
     5679.   1850                        advance("=");
    +
     5680.   1850                        subparam.expression = parse_expression();
    +
     5681.   1850                        param.open = true;
    +
     5682.   1850                    }
    +
     5683.   1850                    if (token_nxt.id === ",") {
    +
     5684.   1850                        advance(",");
    +
     5685.   1850                    } else {
    +
     5686.   1850                        break;
    +
     5687.   1850                    }
    +
     5688.   1850                }
    +
     5689.   1850                parameters.push(param);
    +
     5690.   1850                advance("]");
    +
     5691.   1850                if (token_nxt.id === ",") {
    +
     5692.   1850                    advance(",");
    +
     5693.   1850                    signature.push(", ");
    +
     5694.   1850                    param_parse();
    +
     5695.   1850                    return;
    +
     5696.   1850                }
    +
     5697.   1850            } else {
    +
     5698.   1850                if (token_nxt.id === "...") {
    +
     5699.   1850                    ellipsis = true;
    +
     5700.   1850                    signature.push("...");
    +
     5701.   1850                    advance("...");
    +
     5702.   1850                    if (optional !== undefined) {
    +
     5703.   1850
    +
     5704.   1850// test_cause:
    +
     5705.   1850// ["function aa(aa=0,...){}", "param_parse", "required_a_optional_b", "aa", 21]
    +
     5706.   1850
    +
     5707.   1850                        warn(
    +
     5708.   1850                            "required_a_optional_b",
    +
     5709.   1850                            token_nxt,
    +
     5710.   1850                            token_nxt.id,
    +
     5711.   1850                            optional.id
    +
     5712.   1850                        );
    +
     5713.   1850                    }
    +
     5714.   1850                }
    +
     5715.   1850                if (!token_nxt.identifier) {
    +
     5716.   1850
    +
     5717.   1850// test_cause:
    +
     5718.   1850// ["function aa(0){}", "param_parse", "expected_identifier_a", "0", 13]
    +
     5719.   1850
    +
     5720.   1850                    return stop("expected_identifier_a");
    +
     5721.   1850                }
    +
     5722.   1850                param = token_nxt;
    +
     5723.   1850                parameters.push(param);
    +
     5724.   1850                advance();
    +
     5725.   1850                signature.push(param.id);
    +
     5726.   1850                if (ellipsis) {
    +
     5727.   1850                    param.ellipsis = true;
    +
     5728.   1850                } else {
    +
     5729.   1850                    if (token_nxt.id === "=") {
    +
     5730.   1850                        optional = param;
    +
     5731.   1850                        advance("=");
    +
     5732.   1850                        param.expression = parse_expression(0);
    +
     5733.   1850                    } else {
    +
     5734.   1850                        if (optional !== undefined) {
    +
     5735.   1850
    +
     5736.   1850// test_cause:
    +
     5737.   1850// ["function aa(aa=0,bb){}", "param_parse", "required_a_optional_b", "aa", 18]
    +
     5738.   1850
    +
     5739.   1850                            warn(
    +
     5740.   1850                                "required_a_optional_b",
    +
     5741.   1850                                param,
    +
     5742.   1850                                param.id,
    +
     5743.   1850                                optional.id
    +
     5744.   1850                            );
    +
     5745.   1850                        }
    +
     5746.   1850                    }
    +
     5747.   1850                    if (token_nxt.id === ",") {
    +
     5748.   1850                        advance(",");
    +
     5749.   1850                        signature.push(", ");
    +
     5750.   1850                        param_parse();
    +
     5751.   1850                        return;
    +
     5752.   1850                    }
    +
     5753.   1850                }
    +
     5754.   1850            }
    +
     5755.   2077        }
    +
     5756.   2017
    +
     5757.   2017// test_cause:
    +
     5758.   2017// ["function aa(){}", "prefix_function_parameter", "opener", "(", 0]
    +
     5759.   2017
    +
     5760.   2017        test_cause("opener", token_now.id);
    +
     5761.   2017        token_now.free = false;
    +
     5762.   1485        if (token_nxt.id !== ")" && token_nxt.id !== "(end)") {
    +
     5763.   1485            param_parse();
    +
     5764.   2009        }
    +
     5765.   2009        advance(")");
    +
     5766.   2009        signature.push(")");
    +
     5767.   2009        parameters.forEach(param_enroll);
    +
     5768.   2009        the_function.parameters = parameters;
    +
     5769.   2009        the_function.signature = signature.join("");
    +
     5770.   2009    }
    +
     5771.    631
    +
     5772.    588    function prefix_lbrace() {
    +
     5773.    588        const seen = empty();
    +
     5774.    588        const the_brace = token_now;
    +
     5775.    588        let extra;
    +
     5776.    588        let full;
    +
     5777.    588        let id;
    +
     5778.    588        let name;
    +
     5779.    588        let the_colon;
    +
     5780.    588        let value;
    +
     5781.    588        the_brace.expression = [];
    +
     5782.    548        if (token_nxt.id !== "}") {
    +
     5783.    548
    +
     5784.    548// Parse/loop through each property in {...}.
    +
     5785.    548
    +
     5786.   1996            while (true) {
    +
     5787.   1996                name = token_nxt;
    +
     5788.   1996                advance();
    +
     5789.   1996                if (
    +
     5790.   1996                    (name.id === "get" || name.id === "set")
    +
     5791.   1996                    && token_nxt.identifier
    +
     5792.   1996                ) {
    +
     5793.   1996                    if (!option_dict.getset) {
    +
     5794.   1996
    +
     5795.   1996// test_cause:
    +
     5796.   1996// ["aa={get aa(){}}", "prefix_lbrace", "unexpected_a", "get", 5]
    +
     5797.   1996
    +
     5798.   1996                        warn("unexpected_a", name);
    +
     5799.   1996                    }
    +
     5800.   1996                    extra = name.id;
    +
     5801.   1996                    full = extra + " " + token_nxt.id;
    +
     5802.   1996                    name = token_nxt;
    +
     5803.   1996                    advance();
    +
     5804.   1996                    id = survey(name);
    +
     5805.   1996                    if (seen[full] === true || seen[id] === true) {
    +
     5806.   1996
    +
     5807.   1996// test_cause:
    +
     5808.   1996// ["aa={get aa(){},get aa(){}}", "prefix_lbrace", "duplicate_a", "aa", 20]
    +
     5809.   1996
    +
     5810.   1996                        warn("duplicate_a", name);
    +
     5811.   1996                    }
    +
     5812.   1996                    seen[id] = false;
    +
     5813.   1996                    seen[full] = true;
    +
     5814.   1996                } else if (name.id === "`") {
    +
     5815.   1996
    +
     5816.   1996// test_cause:
    +
     5817.   1996// ["aa={`aa`:0}", "prefix_lbrace", "unexpected_a", "`", 5]
    +
     5818.   1996
    +
     5819.   1996                    stop("unexpected_a", name);
    +
     5820.   1996
    +
     5821.   1996                } else {
    +
     5822.   1996                    id = survey(name);
    +
     5823.   1996                    if (typeof seen[id] === "boolean") {
    +
     5824.   1996
    +
     5825.   1996// test_cause:
    +
     5826.   1996// ["aa={aa,aa}", "prefix_lbrace", "duplicate_a", "aa", 8]
    +
     5827.   1996
    +
     5828.   1996                        warn("duplicate_a", name);
    +
     5829.   1996                    }
    +
     5830.   1996                    seen[id] = true;
    +
     5831.   1996                }
    +
     5832.   1996                if (name.identifier) {
    +
     5833.   1996                    if (token_nxt.id === "}" || token_nxt.id === ",") {
    +
     5834.   1996                        if (typeof extra === "string") {
    +
     5835.   1996
    +
     5836.   1996// test_cause:
    +
     5837.   1996// ["aa={get aa}", "prefix_lbrace", "closer", "", 0]
    +
     5838.   1996
    +
     5839.   1996                            test_cause("closer");
    +
     5840.   1996                            advance("(");
    +
     5841.   1996                        }
    +
     5842.   1996                        value = parse_expression(Infinity, true);
    +
     5843.   1996                    } else if (token_nxt.id === "(") {
    +
     5844.   1996
    +
     5845.   1996// test_cause:
    +
     5846.   1996// ["aa={aa()}", "prefix_lbrace", "paren", "", 0]
    +
     5847.   1996// ["aa={get aa(){}}", "prefix_lbrace", "paren", "", 0]
    +
     5848.   1996
    +
     5849.   1996                        test_cause("paren");
    +
     5850.   1996                        value = prefix_function({
    +
     5851.   1996                            arity: "unary",
    +
     5852.   1996                            from: name.from,
    +
     5853.   1996                            id: "function",
    +
     5854.   1996                            line: name.line,
    +
     5855.   1996                            name: (
    +
     5856.   1996                                typeof extra === "string"
    +
     5857.   1996                                ? extra
    +
     5858.   1996                                : id
    +
     5859.   1996                            ),
    +
     5860.   1996                            thru: name.from
    +
     5861.   1996                        });
    +
     5862.   1996                    } else {
    +
     5863.   1996                        if (typeof extra === "string") {
    +
     5864.   1996
    +
     5865.   1996// test_cause:
    +
     5866.   1996// ["aa={get aa.aa}", "prefix_lbrace", "paren", "", 0]
    +
     5867.   1996
    +
     5868.   1996                            test_cause("paren");
    +
     5869.   1996                            advance("(");
    +
     5870.   1996                        }
    +
     5871.   1996                        the_colon = token_nxt;
    +
     5872.   1996                        advance(":");
    +
     5873.   1996                        value = parse_expression(0);
    +
     5874.   1996                        if (
    +
     5875.   1996                            value.id === name.id
    +
     5876.   1996                            && value.id !== "function"
    +
     5877.   1996                        ) {
    +
     5878.   1996
    +
     5879.   1996// test_cause:
    +
     5880.   1996// ["aa={aa:aa}", "prefix_lbrace", "unexpected_a", ": aa", 7]
    +
     5881.   1996
    +
     5882.   1996                            warn("unexpected_a", the_colon, ": " + name.id);
    +
     5883.   1996                        }
    +
     5884.   1996                    }
    +
     5885.   1996                    value.label = name;
    +
     5886.   1996                    if (typeof extra === "string") {
    +
     5887.   1996                        value.extra = extra;
    +
     5888.   1996                    }
    +
     5889.   1996                    the_brace.expression.push(value);
    +
     5890.   1996                } else {
    +
     5891.   1996
    +
     5892.   1996// test_cause:
    +
     5893.   1996// ["aa={\"aa\":0}", "prefix_lbrace", "colon", "", 0]
    +
     5894.   1996
    +
     5895.   1996                    test_cause("colon");
    +
     5896.   1996                    advance(":");
    +
     5897.   1996                    value = parse_expression(0);
    +
     5898.   1996                    value.label = name;
    +
     5899.   1996                    the_brace.expression.push(value);
    +
     5900.   1996                }
    +
     5901.   1996                if (token_nxt.id !== ",") {
    +
     5902.   1996                    break;
    +
     5903.   1996                }
    +
     5904.   1996
    +
     5905.   1996// test_cause:
    +
     5906.   1996// ["aa={\"aa\":0,\"bb\":0}", "prefix_lbrace", "comma", "", 0]
    +
     5907.   1996
    +
     5908.   1996                test_cause("comma");
    +
     5909.   1996                advance(",");
    +
     5910.   1996                if (token_nxt.id === "}") {
    +
     5911.   1996
    +
     5912.   1996// test_cause:
    +
     5913.   1996// ["let aa={aa:0,}", "prefix_lbrace", "unexpected_a", ",", 13]
    +
     5914.   1996
    +
     5915.   1996                    warn("unexpected_a", token_now);
    +
     5916.   1996                    break;
    +
     5917.   1996                }
    +
     5918.   1996            }
    +
     5919.    583        }
    +
     5920.    583
    +
     5921.    583// test_cause:
    +
     5922.    583// ["aa={bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8]
    +
     5923.    583
    +
     5924.    583        check_ordered(
    +
     5925.    583            "property",
    +
     5926.   1991            the_brace.expression.map(function ({
    +
     5927.   1991                label
    +
     5928.   1991            }) {
    +
     5929.   1991                return label;
    +
     5930.   1991            })
    +
     5931.    583        );
    +
     5932.    583        advance("}");
    +
     5933.    583        return the_brace;
    +
     5934.    583    }
    +
     5935.    631
    +
     5936.    759    function prefix_lbracket() {
    +
     5937.    759        const the_token = token_now;
    +
     5938.    759        let element;
    +
     5939.    759        let ellipsis;
    +
     5940.    759        the_token.expression = [];
    +
     5941.    392        if (token_nxt.id !== "]") {
    +
     5942.    392
    +
     5943.    392// Parse/loop through each element in [...].
    +
     5944.    392
    +
     5945.   1884            while (true) {
    +
     5946.   1884                ellipsis = false;
    +
     5947.   1884                if (token_nxt.id === "...") {
    +
     5948.   1884                    ellipsis = true;
    +
     5949.   1884                    advance("...");
    +
     5950.   1884                }
    +
     5951.   1884                element = parse_expression(10);
    +
     5952.   1884                if (ellipsis) {
    +
     5953.   1884                    element.ellipsis = true;
    +
     5954.   1884                }
    +
     5955.   1884                the_token.expression.push(element);
    +
     5956.   1884                if (token_nxt.id !== ",") {
    +
     5957.   1884                    break;
    +
     5958.   1884                }
    +
     5959.   1884                advance(",");
    +
     5960.   1884                if (token_nxt.id === "]") {
    +
     5961.   1884
    +
     5962.   1884// test_cause:
    +
     5963.   1884// ["let aa=[0,]", "prefix_lbracket", "unexpected_a", ",", 10]
    +
     5964.   1884
    +
     5965.   1884                    warn("unexpected_a", token_now);
    +
     5966.   1884                    break;
    +
     5967.   1884                }
    +
     5968.   1884            }
    +
     5969.    392        }
    +
     5970.    759        advance("]");
    +
     5971.    759        return the_token;
    +
     5972.    759    }
    +
     5973.    631
    +
     5974.   1616    function prefix_lparen() {
    +
     5975.   1616        let the_paren = token_now;
    +
     5976.   1616        let the_value;
    +
     5977.   1616
    +
     5978.   1616// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart.
    +
     5979.   1616
    +
     5980.     14        if (token_now.fart) {
    +
     5981.     14            return parse_fart(token_now.fart);
    +
     5982.   1602        }
    +
     5983.   1602
    +
     5984.   1602// test_cause:
    +
     5985.   1602// ["(0)", "prefix_lparen", "expr", "", 0]
    +
     5986.   1602
    +
     5987.   1602        test_cause("expr");
    +
     5988.   1602        the_paren.free = true;
    +
     5989.   1602        the_value = parse_expression(0);
    +
     5990.   1602        if (the_value.wrapped === true) {
    +
     5991.      1
    +
     5992.      1// test_cause:
    +
     5993.      1// ["((0))", "prefix_lparen", "unexpected_a", "(", 1]
    +
     5994.      1
    +
     5995.      1            warn("unexpected_a", the_paren);
    +
     5996.   1602        }
    +
     5997.   1602        the_value.wrapped = true;
    +
     5998.   1602        advance(")", the_paren);
    +
     5999.   1602        return the_value;
    +
     6000.   1602    }
    +
     6001.    631
    +
     6002.    155    function prefix_new() {
    +
     6003.    155        const the_new = token_now;
    +
     6004.    155        let right;
    +
     6005.    155        right = parse_expression(160);
    +
     6006.      1        if (token_nxt.id !== "(") {
    +
     6007.      1
    +
     6008.      1// test_cause:
    +
     6009.      1// ["new aa", "prefix_new", "expected_a_before_b", "(end)", 1]
    +
     6010.      1
    +
     6011.      1            warn("expected_a_before_b", token_nxt, "()", artifact());
    +
     6012.      1        }
    +
     6013.    155        the_new.expression = right;
    +
     6014.    155        return the_new;
    +
     6015.    155    }
    +
     6016.    631
    +
     6017.    782    function prefix_tick() {
    +
     6018.    782        const the_tick = token_now;
    +
     6019.    782        the_tick.value = [];
    +
     6020.    782        the_tick.expression = [];
    +
     6021.    782        if (token_nxt.id !== "`") {
    +
     6022.    782
    +
     6023.    782// Parse/loop through each token in `${...}`.
    +
     6024.    782
    +
     6025.   1499            while (true) {
    +
     6026.   1499                advance("(string)");
    +
     6027.   1499                the_tick.value.push(token_now);
    +
     6028.   1499                if (token_nxt.id !== "${") {
    +
     6029.   1499                    break;
    +
     6030.   1499                }
    +
     6031.   1499                advance("${");
    +
     6032.   1499
    +
     6033.   1499// test_cause:
    +
     6034.   1499// ["let aa=`${}`;", "prefix_tick", "${", "", 0]
    +
     6035.   1499
    +
     6036.   1499                test_cause("${");
    +
     6037.   1499                the_tick.expression.push(parse_expression(0));
    +
     6038.   1499                advance("}");
    +
     6039.   1499            }
    +
     6040.    780        }
    +
     6041.    780        advance("`");
    +
     6042.    780        return the_tick;
    +
     6043.    780    }
    +
     6044.    631
    +
     6045.      2    function prefix_void() {
    +
     6046.      2        const the_void = token_now;
    +
     6047.      2
    +
     6048.      2// test_cause:
    +
     6049.      2// ["void 0", "prefix_void", "unexpected_a", "void", 1]
    +
     6050.      2// ["void", "prefix_void", "unexpected_a", "void", 1]
    +
     6051.      2
    +
     6052.      2        warn("unexpected_a", the_void);
    +
     6053.      2        the_void.expression = parse_expression(0);
    +
     6054.      2        return the_void;
    +
     6055.      2    }
    +
     6056.    631
    +
     6057.  13430    function semicolon() {
    +
     6058.  13430
    +
     6059.  13430// Try to match a semicolon.
    +
     6060.  13430
    +
     6061.  13205        if (token_nxt.id === ";") {
    +
     6062.  13205            advance(";");
    +
     6063.  13205        } else {
    +
     6064.    225
    +
     6065.    225// test_cause:
    +
     6066.    225// ["0", "semicolon", "expected_a_b", "(end)", 1]
    +
     6067.    225
    +
     6068.    225            warn_at(
    +
     6069.    225                "expected_a_b",
    +
     6070.    225                token_now.line,
    +
     6071.    225                token_now.thru + 1,
    +
     6072.    225                ";",
    +
     6073.    225                artifact()
    +
     6074.    225            );
    +
     6075.    225        }
    +
     6076.  13430        anon = "anonymous";
    +
     6077.  13430    }
    +
     6078.    631
    +
     6079.  14513    function stmt(id, fud_stmt) {
    +
     6080.  14513
    +
     6081.  14513// Create a statement.
    +
     6082.  14513
    +
     6083.  14513        const the_symbol = symbol(id);
    +
     6084.  14513        the_symbol.fud_stmt = fud_stmt;
    +
     6085.  14513        return the_symbol;
    +
     6086.  14513    }
    +
     6087.    631
    +
     6088.   1023    function stmt_break() {
    +
     6089.   1023        const the_break = token_now;
    +
     6090.   1023        let the_label;
    +
     6091.   1023        if (
    +
     6092.    719            (functionage.loop < 1 && functionage.switch < 1)
    +
     6093.   1017            || functionage.finally > 0
    +
     6094.      6        ) {
    +
     6095.      6
    +
     6096.      6// test_cause:
    +
     6097.      6// ["break", "stmt_break", "unexpected_a", "break", 1]
    +
     6098.      6
    +
     6099.      6            warn("unexpected_a", the_break);
    +
     6100.      6        }
    +
     6101.   1023        the_break.disrupt = true;
    +
     6102.      5        if (token_nxt.identifier && token_now.line === token_nxt.line) {
    +
     6103.      5            the_label = functionage.context[token_nxt.id];
    +
     6104.      5            if (
    +
     6105.      5                the_label === undefined
    +
     6106.      5                || the_label.role !== "label"
    +
     6107.      5                || the_label.dead
    +
     6108.      5            ) {
    +
     6109.      5                if (the_label !== undefined && the_label.dead) {
    +
     6110.      5
    +
     6111.      5// test_cause:
    +
     6112.      5// ["aa:{function aa(aa){break aa;}}", "stmt_break", "out_of_scope_a", "aa", 27]
    +
     6113.      5
    +
     6114.      5                    warn("out_of_scope_a");
    +
     6115.      5                } else {
    +
     6116.      5
    +
     6117.      5// test_cause:
    +
     6118.      5// ["aa:{break aa;}", "stmt_break", "not_label_a", "aa", 11]
    +
     6119.      5
    +
     6120.      5                    warn("not_label_a");
    +
     6121.      5                }
    +
     6122.      5            } else {
    +
     6123.      5                the_label.used += 1;
    +
     6124.      5            }
    +
     6125.      5            the_break.label = token_nxt;
    +
     6126.      5            advance();
    +
     6127.      5        }
    +
     6128.   1023        advance(";");
    +
     6129.   1023        return the_break;
    +
     6130.   1023    }
    +
     6131.    631
    +
     6132.      2    function stmt_continue() {
    +
     6133.      2        const the_continue = token_now;
    +
     6134.      1        if (functionage.loop < 1 || functionage.finally > 0) {
    +
     6135.      2
    +
     6136.      2// test_cause:
    +
     6137.      2// ["continue", "stmt_continue", "unexpected_a", "continue", 1]
    +
     6138.      2// ["
    +
     6139.      2// function aa(){while(0){try{}finally{continue}}}
    +
     6140.      2// ", "stmt_continue", "unexpected_a", "continue", 37]
    +
     6141.      2
    +
     6142.      2            warn("unexpected_a", the_continue);
    +
     6143.      2        }
    +
     6144.      2        check_not_top_level(the_continue);
    +
     6145.      2        the_continue.disrupt = true;
    +
     6146.      2        warn("unexpected_a", the_continue);
    +
     6147.      2        advance(";");
    +
     6148.      2        return the_continue;
    +
     6149.      2    }
    +
     6150.    631
    +
     6151.      3    function stmt_debugger() {
    +
     6152.      3        const the_debug = token_now;
    +
     6153.      1        if (!option_dict.devel) {
    +
     6154.      1
    +
     6155.      1// test_cause:
    +
     6156.      1// ["debugger", "stmt_debugger", "unexpected_a", "debugger", 1]
    +
     6157.      1
    +
     6158.      1            warn("unexpected_a", the_debug);
    +
     6159.      1        }
    +
     6160.      3        semicolon();
    +
     6161.      3        return the_debug;
    +
     6162.      3    }
    +
     6163.    631
    +
     6164.     72    function stmt_delete() {
    +
     6165.     72        const the_token = token_now;
    +
     6166.     72        const the_value = parse_expression(0);
    +
     6167.     72        if (
    +
     6168.      1            (the_value.id !== "." && the_value.id !== "[")
    +
     6169.     71            || the_value.arity !== "binary"
    +
     6170.      1        ) {
    +
     6171.      1
    +
     6172.      1// test_cause:
    +
     6173.      1// ["delete 0", "stmt_delete", "expected_a_b", "0", 8]
    +
     6174.      1
    +
     6175.      1            stop("expected_a_b", the_value, ".", artifact(the_value));
    +
     6176.     71        }
    +
     6177.     71        the_token.expression = the_value;
    +
     6178.     71        semicolon();
    +
     6179.     71        return the_token;
    +
     6180.     71    }
    +
     6181.    631
    +
     6182.      5    function stmt_do() {
    +
     6183.      5        const the_do = token_now;
    +
     6184.      5        check_not_top_level(the_do);
    +
     6185.      5        functionage.loop += 1;
    +
     6186.      5        the_do.block = block();
    +
     6187.      5        advance("while");
    +
     6188.      5        the_do.expression = condition();
    +
     6189.      5        semicolon();
    +
     6190.      1        if (the_do.block.disrupt === true) {
    +
     6191.      1
    +
     6192.      1// test_cause:
    +
     6193.      1// ["function aa(){do{break;}while(0)}", "stmt_do", "weird_loop", "do", 15]
    +
     6194.      1
    +
     6195.      1            warn("weird_loop", the_do);
    +
     6196.      3        }
    +
     6197.      3        functionage.loop -= 1;
    +
     6198.      3        return the_do;
    +
     6199.      3    }
    +
     6200.    631
    +
     6201.     24    function stmt_export() {
    +
     6202.     24        let export_list = [];
    +
     6203.     24        let the_export = token_now;
    +
     6204.     24        let the_id;
    +
     6205.     24        let the_name;
    +
     6206.     24        let the_thing;
    +
     6207.     24
    +
     6208.     24        the_export.expression = [];
    +
     6209.     11        if (token_nxt.id === "default") {
    +
     6210.     11            if (export_dict.default !== undefined) {
    +
     6211.     11
    +
     6212.     11// test_cause:
    +
     6213.     11// ["
    +
     6214.     11// export default 0;export default 0
    +
     6215.     11// ", "stmt_export", "duplicate_a", "default", 25]
    +
     6216.     11
    +
     6217.     11                warn("duplicate_a");
    +
     6218.     11            }
    +
     6219.     11            advance("default");
    +
     6220.     11            the_thing = parse_expression(0);
    +
     6221.     11            if (
    +
     6222.     11                the_thing.id !== "("
    +
     6223.     11                || the_thing.expression[0].id !== "."
    +
     6224.     11                || the_thing.expression[0].expression.id !== "Object"
    +
     6225.     11                || the_thing.expression[0].name.id !== "freeze"
    +
     6226.     11            ) {
    +
     6227.     11
    +
     6228.     11// test_cause:
    +
     6229.     11// ["export default {}", "stmt_export", "freeze_exports", "{", 16]
    +
     6230.     11
    +
     6231.     11                warn("freeze_exports", the_thing);
    +
     6232.     11
    +
     6233.     11// PR-301 - Bugfix - Fixes issues #282 - optional-semicolon.
    +
     6234.     11
    +
     6235.     11            } else {
    +
     6236.     11
    +
     6237.     11// test_cause:
    +
     6238.     11// ["
    +
     6239.     11// export default Object.freeze({})
    +
     6240.     11// ", "semicolon", "expected_a_b", "(end)", 32]
    +
     6241.     11
    +
     6242.     11                semicolon();
    +
     6243.     11            }
    +
     6244.     11            export_dict.default = the_thing;
    +
     6245.     11            the_export.expression.push(the_thing);
    +
     6246.     13        } else {
    +
     6247.     13
    +
     6248.     13// PR-439 - Add grammar for "export async function ...".
    +
     6249.     13
    +
     6250.     13            if (token_nxt.id === "function" || token_nxt.id === "async") {
    +
     6251.     13
    +
     6252.     13// test_cause:
    +
     6253.     13// ["export async function aa(){}", "stmt_export", "freeze_exports", "async", 8]
    +
     6254.     13// ["export function aa(){}", "stmt_export", "freeze_exports", "function", 8]
    +
     6255.     13
    +
     6256.     13                warn("freeze_exports");
    +
     6257.     13                the_thing = parse_statement();
    +
     6258.     13                the_name = the_thing.name;
    +
     6259.     13                the_id = the_name.id;
    +
     6260.     13                the_name.used += 1;
    +
     6261.     13                if (export_dict[the_id] !== undefined) {
    +
     6262.     13
    +
     6263.     13// test_cause:
    +
     6264.     13// ["
    +
     6265.     13// let aa;export{aa};export function aa(){}
    +
     6266.     13// ", "stmt_export", "duplicate_a", "aa", 35]
    +
     6267.     13
    +
     6268.     13                    warn("duplicate_a", the_name);
    +
     6269.     13                }
    +
     6270.     13                export_dict[the_id] = the_thing;
    +
     6271.     13                the_export.expression.push(the_thing);
    +
     6272.     13                the_thing.statement = false;
    +
     6273.     13                the_thing.arity = "unary";
    +
     6274.     13            } else if (
    +
     6275.     13                token_nxt.id === "var"
    +
     6276.     13                || token_nxt.id === "let"
    +
     6277.     13                || token_nxt.id === "const"
    +
     6278.     13            ) {
    +
     6279.     13
    +
     6280.     13// test_cause:
    +
     6281.     13// ["export const", "stmt_export", "unexpected_a", "const", 8]
    +
     6282.     13// ["export let", "stmt_export", "unexpected_a", "let", 8]
    +
     6283.     13// ["export var", "stmt_export", "unexpected_a", "var", 8]
    +
     6284.     13
    +
     6285.     13                warn("unexpected_a");
    +
     6286.     13                parse_statement();
    +
     6287.     13            } else if (token_nxt.id === "{") {
    +
     6288.     13
    +
     6289.     13// test_cause:
    +
     6290.     13// ["export {}", "stmt_export", "advance{", "", 0]
    +
     6291.     13
    +
     6292.     13                test_cause("advance{");
    +
     6293.     13                advance("{");
    +
     6294.     13                while (true) {
    +
     6295.     13                    if (!token_nxt.identifier) {
    +
     6296.     13
    +
     6297.     13// test_cause:
    +
     6298.     13// ["export {}", "stmt_export", "expected_identifier_a", "}", 9]
    +
     6299.     13
    +
     6300.     13                        stop("expected_identifier_a");
    +
     6301.     13                    }
    +
     6302.     13                    the_id = token_nxt.id;
    +
     6303.     13                    export_list.push(token_nxt);
    +
     6304.     13                    the_name = token_global.context[the_id];
    +
     6305.     13                    if (the_name === undefined) {
    +
     6306.     13
    +
     6307.     13// test_cause:
    +
     6308.     13// ["export {aa}", "stmt_export", "unexpected_a", "aa", 9]
    +
     6309.     13
    +
     6310.     13                        warn("unexpected_a");
    +
     6311.     13                    } else {
    +
     6312.     13                        the_name.used += 1;
    +
     6313.     13                        if (export_dict[the_id] !== undefined) {
    +
     6314.     13
    +
     6315.     13// test_cause:
    +
     6316.     13// ["let aa;export{aa,aa}", "stmt_export", "duplicate_a", "aa", 18]
    +
     6317.     13
    +
     6318.     13                            warn("duplicate_a");
    +
     6319.     13                        }
    +
     6320.     13                        export_dict[the_id] = the_name;
    +
     6321.     13                    }
    +
     6322.     13                    advance();
    +
     6323.     13                    the_export.expression.push(the_thing);
    +
     6324.     13                    if (token_nxt.id === ",") {
    +
     6325.     13                        advance(",");
    +
     6326.     13                    } else {
    +
     6327.     13                        break;
    +
     6328.     13                    }
    +
     6329.     13                }
    +
     6330.     13
    +
     6331.     13// PR-439 - Check exported properties are ordered.
    +
     6332.     13
    +
     6333.     13// test_cause:
    +
     6334.     13// ["export {bb, aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 13]
    +
     6335.     13
    +
     6336.     13                check_ordered("export", export_list);
    +
     6337.     13                advance("}");
    +
     6338.     13                semicolon();
    +
     6339.     13            } else {
    +
     6340.     13
    +
     6341.     13// test_cause:
    +
     6342.     13// ["export", "stmt_export", "unexpected_a", "(end)", 1]
    +
     6343.     13
    +
     6344.     13                stop("unexpected_a");
    +
     6345.     13            }
    +
     6346.     18        }
    +
     6347.     18        state.mode_module = true;
    +
     6348.     18        return the_export;
    +
     6349.     18    }
    +
     6350.    631
    +
     6351.     12    function stmt_for() {
    +
     6352.     12        let first;
    +
     6353.     12        let the_for = token_now;
    +
     6354.      7        if (!option_dict.for) {
    +
     6355.      7
    +
     6356.      7// test_cause:
    +
     6357.      7// ["for", "stmt_for", "unexpected_a", "for", 1]
    +
     6358.      7
    +
     6359.      7            warn("unexpected_a", the_for);
    +
     6360.      7        }
    +
     6361.     12        check_not_top_level(the_for);
    +
     6362.     12        functionage.loop += 1;
    +
     6363.     12        advance("(");
    +
     6364.     12        token_now.free = true;
    +
     6365.      1        if (token_nxt.id === ";") {
    +
     6366.      1
    +
     6367.      1// test_cause:
    +
     6368.      1// ["for(;;){}", "stmt_for", "expected_a_b", "for (;", 1]
    +
     6369.      1
    +
     6370.      1            return stop("expected_a_b", the_for, "while (", "for (;");
    +
     6371.      9        }
    +
     6372.      9        switch (token_nxt.id) {
    +
     6373.      9        case "const":
    +
     6374.      1        case "let":
    +
     6375.      1        case "var":
    +
     6376.      1
    +
     6377.      1// test_cause:
    +
     6378.      1// ["for(const aa in aa){}", "stmt_for", "unexpected_a", "const", 5]
    +
     6379.      1
    +
     6380.      1            return stop("unexpected_a");
    +
     6381.      8        }
    +
     6382.      8        first = parse_expression(0);
    +
     6383.      8        if (first.id === "in") {
    +
     6384.      2            if (first.expression[0].arity !== "variable") {
    +
     6385.      2
    +
     6386.      2// test_cause:
    +
     6387.      2// ["for(0 in aa){}", "stmt_for", "bad_assignment_a", "0", 5]
    +
     6388.      2
    +
     6389.      2                warn("bad_assignment_a", first.expression[0]);
    +
     6390.      2            }
    +
     6391.      2            the_for.name = first.expression[0];
    +
     6392.      2            the_for.expression = first.expression[1];
    +
     6393.      2            warn("expected_a_b", the_for, "Object.keys", "for in");
    +
     6394.      6        } else {
    +
     6395.      6            the_for.initial = first;
    +
     6396.      6            advance(";");
    +
     6397.      6            the_for.expression = parse_expression(0);
    +
     6398.      6            advance(";");
    +
     6399.      6            the_for.inc = parse_expression(0);
    +
     6400.      6            if (the_for.inc.id === "++") {
    +
     6401.      6
    +
     6402.      6// test_cause:
    +
     6403.      6// ["for(aa;aa;aa++){}", "stmt_for", "expected_a_b", "++", 13]
    +
     6404.      6
    +
     6405.      6                warn("expected_a_b", the_for.inc, "+= 1", "++");
    +
     6406.      6            }
    +
     6407.      8        }
    +
     6408.      8        advance(")");
    +
     6409.      8        the_for.block = block();
    +
     6410.      8        if (the_for.block.disrupt === true) {
    +
     6411.      1
    +
     6412.      1// test_cause:
    +
     6413.      1// ["
    +
     6414.      1// /*jslint for*/
    +
     6415.      1// function aa(bb,cc){for(0;0;0){break;}}
    +
     6416.      1// ", "stmt_for", "weird_loop", "for", 20]
    +
     6417.      1
    +
     6418.      1            warn("weird_loop", the_for);
    +
     6419.      8        }
    +
     6420.      8        functionage.loop -= 1;
    +
     6421.      8        return the_for;
    +
     6422.      8    }
    +
     6423.    631
    +
     6424.   3051    function stmt_if() {
    +
     6425.   3051        const the_if = token_now;
    +
     6426.   3051        let the_else;
    +
     6427.   3051        the_if.expression = condition();
    +
     6428.   3051        the_if.block = block();
    +
     6429.    642        if (token_nxt.id === "else") {
    +
     6430.    642            advance("else");
    +
     6431.    642            the_else = token_now;
    +
     6432.    642            the_if.else = (
    +
     6433.    642                token_nxt.id === "if"
    +
     6434.    642                ? parse_statement()
    +
     6435.    642                : block()
    +
     6436.    642            );
    +
     6437.    642
    +
     6438.    642// test_cause:
    +
     6439.    642// ["if(0){0}else if(0){0}", "stmt_if", "else", "", 0]
    +
     6440.    642// ["if(0){0}else{0}", "stmt_if", "else", "", 0]
    +
     6441.    642
    +
     6442.    642            test_cause("else");
    +
     6443.    642            if (the_if.block.disrupt === true) {
    +
     6444.    642                if (the_if.else.disrupt === true) {
    +
     6445.    642
    +
     6446.    642// test_cause:
    +
     6447.    642// ["if(0){break;}else{break;}", "stmt_if", "disrupt", "", 0]
    +
     6448.    642
    +
     6449.    642                    test_cause("disrupt");
    +
     6450.    642                    the_if.disrupt = true;
    +
     6451.    642                } else {
    +
     6452.    642
    +
     6453.    642// test_cause:
    +
     6454.    642// ["if(0){break;}else{}", "stmt_if", "unexpected_a", "else", 14]
    +
     6455.    642
    +
     6456.    642                    warn("unexpected_a", the_else);
    +
     6457.    642                }
    +
     6458.    642            }
    +
     6459.   3050        }
    +
     6460.   3050        return the_if;
    +
     6461.   3050    }
    +
     6462.    631
    +
     6463.     62    function stmt_import() {
    +
     6464.     62        const the_import = token_now;
    +
     6465.     62        let name;
    +
     6466.     62        let names;
    +
     6467.     62
    +
     6468.     62// PR-347 - Disable warning "unexpected_directive_a".
    +
     6469.     62//
    +
     6470.     62//         if (typeof state.mode_module === "object") {
    +
     6471.     62//
    +
     6472.     62// // test_cause:
    +
     6473.     62// // ["
    +
     6474.     62// // /*global aa*/
    +
     6475.     62// // import aa from "aa"
    +
     6476.     62// // ", "stmt_import", "unexpected_directive_a", "global", 1]
    +
     6477.     62//
    +
     6478.     62//             warn(
    +
     6479.     62//                 "unexpected_directive_a",
    +
     6480.     62//                 state.mode_module,
    +
     6481.     62//                 state.mode_module.directive
    +
     6482.     62//             );
    +
     6483.     62//         }
    +
     6484.     62
    +
     6485.     62        state.mode_module = true;
    +
     6486.     62
    +
     6487.     62// PR-436 - Add grammar for side-effect import-statement.
    +
     6488.     62
    +
     6489.      1        if (token_nxt.id === "(string)") {
    +
     6490.      1
    +
     6491.      1// test_cause:
    +
     6492.      1// ["import \"./aa.mjs\";", "stmt_import", "import_side_effect", "", 0]
    +
     6493.      1
    +
     6494.      1            test_cause("import_side_effect");
    +
     6495.      1            warn("expected_a_b", token_nxt, "{", artifact());
    +
     6496.      1            advance();
    +
     6497.      1            semicolon();
    +
     6498.      1            return the_import;
    +
     6499.     61        }
    +
     6500.     61        if (token_nxt.identifier) {
    +
     6501.     57            name = token_nxt;
    +
     6502.     57            advance();
    +
     6503.     57            if (name.id === "ignore") {
    +
     6504.     57
    +
     6505.     57// test_cause:
    +
     6506.     57// ["import ignore from \"aa\"", "stmt_import", "unexpected_a", "ignore", 8]
    +
     6507.     57
    +
     6508.     57                warn("unexpected_a", name);
    +
     6509.     57            }
    +
     6510.     57            enroll(name, "variable", true);
    +
     6511.     57            the_import.name = name;
    +
     6512.     57        } else {
    +
     6513.      4            names = [];
    +
     6514.      4            advance("{");
    +
     6515.      4            if (token_nxt.id !== "}") {
    +
     6516.      4                while (true) {
    +
     6517.      4                    if (!token_nxt.identifier) {
    +
     6518.      4
    +
     6519.      4// test_cause:
    +
     6520.      4// ["import {", "stmt_import", "expected_identifier_a", "(end)", 1]
    +
     6521.      4
    +
     6522.      4                        stop("expected_identifier_a");
    +
     6523.      4                    }
    +
     6524.      4                    name = token_nxt;
    +
     6525.      4                    advance();
    +
     6526.      4                    if (name.id === "ignore") {
    +
     6527.      4
    +
     6528.      4// test_cause:
    +
     6529.      4// ["import {ignore} from \"aa\"", "stmt_import", "unexpected_a", "ignore", 9]
    +
     6530.      4
    +
     6531.      4                        warn("unexpected_a", name);
    +
     6532.      4                    }
    +
     6533.      4                    enroll(name, "variable", true);
    +
     6534.      4                    names.push(name);
    +
     6535.      4                    if (token_nxt.id !== ",") {
    +
     6536.      4                        break;
    +
     6537.      4                    }
    +
     6538.      4                    advance(",");
    +
     6539.      4                }
    +
     6540.      4            }
    +
     6541.      4            advance("}");
    +
     6542.      4            the_import.name = names;
    +
     6543.     60        }
    +
     6544.     60        advance("from");
    +
     6545.     60        advance("(string)");
    +
     6546.     60        the_import.import = token_now;
    +
     6547.     60        if (!jslint_rgx_module.test(token_now.value)) {
    +
     6548.      1
    +
     6549.      1// test_cause:
    +
     6550.      1// ["import aa from \"!aa\"", "stmt_import", "bad_module_name_a", "!aa", 16]
    +
     6551.      1
    +
     6552.      1            warn("bad_module_name_a", token_now);
    +
     6553.     60        }
    +
     6554.     60        import_list.push(token_now.value);
    +
     6555.     60        semicolon();
    +
     6556.     60        return the_import;
    +
     6557.     60    }
    +
     6558.    631
    +
     6559.      5    function stmt_lbrace() {
    +
     6560.      5
    +
     6561.      5// test_cause:
    +
     6562.      5// [";{}", "stmt_lbrace", "naked_block", "{", 2]
    +
     6563.      5// ["class aa{}", "stmt_lbrace", "naked_block", "{", 9]
    +
     6564.      5
    +
     6565.      5        warn("naked_block", token_now);
    +
     6566.      5        return block("naked");
    +
     6567.      5    }
    +
     6568.    631
    +
     6569.   1761    function stmt_return() {
    +
     6570.   1761        const the_return = token_now;
    +
     6571.   1761        check_not_top_level(the_return);
    +
     6572.      1        if (functionage.finally > 0) {
    +
     6573.      1
    +
     6574.      1// test_cause:
    +
     6575.      1// ["
    +
     6576.      1// function aa(){try{}finally{return;}}
    +
     6577.      1// ", "stmt_return", "unexpected_a", "return", 28]
    +
     6578.      1
    +
     6579.      1            warn("unexpected_a", the_return);
    +
     6580.      1        }
    +
     6581.   1761        the_return.disrupt = true;
    +
     6582.   1509        if (token_nxt.id !== ";" && the_return.line === token_nxt.line) {
    +
     6583.   1509            the_return.expression = parse_expression(10);
    +
     6584.   1509        }
    +
     6585.   1761        advance(";");
    +
     6586.   1761        return the_return;
    +
     6587.   1761    }
    +
     6588.    631
    +
     6589.      5    function stmt_semicolon() {
    +
     6590.      5
    +
     6591.      5// test_cause:
    +
     6592.      5// [";", "stmt_semicolon", "unexpected_a", ";", 1]
    +
     6593.      5
    +
     6594.      5        warn("unexpected_a", token_now);
    +
     6595.      5        return token_now;
    +
     6596.      5    }
    +
     6597.    631
    +
     6598.    220    function stmt_switch() {
    +
     6599.    220        const the_cases = [];
    +
     6600.    220        const the_switch = token_now;
    +
     6601.    220        let dups = [];
    +
     6602.    220        let exp;
    +
     6603.    220        let last;
    +
     6604.    220        let stmts;
    +
     6605.    220        let the_case;
    +
     6606.    220        let the_default;
    +
     6607.    220        let the_disrupt = true;
    +
     6608.    220        let the_last;
    +
     6609.  23088        function is_dup(thing) {
    +
     6610.  23088            return is_equal(thing, exp);
    +
     6611.  23088        }
    +
     6612.    220        check_not_top_level(the_switch);
    +
     6613.      1        if (functionage.finally > 0) {
    +
     6614.      1
    +
     6615.      1// test_cause:
    +
     6616.      1// ["
    +
     6617.      1// function aa(){try{}finally{switch(0){}}}
    +
     6618.      1// ", "stmt_switch", "unexpected_a", "switch", 28]
    +
     6619.      1
    +
     6620.      1            warn("unexpected_a", the_switch);
    +
     6621.      1        }
    +
     6622.    220        functionage.switch += 1;
    +
     6623.    220        advance("(");
    +
     6624.    220        token_now.free = true;
    +
     6625.    220        the_switch.expression = parse_expression(0);
    +
     6626.    220        the_switch.block = the_cases;
    +
     6627.    220        advance(")");
    +
     6628.    220        advance("{");
    +
     6629.   1085        while (true) {
    +
     6630.   1085
    +
     6631.   1085// Loop through cases with breaks.
    +
     6632.   1085
    +
     6633.   1085            the_case = token_nxt;
    +
     6634.   1085            the_case.arity = "statement";
    +
     6635.   1085            the_case.expression = [];
    +
     6636.   1612            while (true) {
    +
     6637.   1612
    +
     6638.   1612// Loop through fallthrough cases.
    +
     6639.   1612
    +
     6640.   1612                advance("case");
    +
     6641.   1612                token_now.switch = true;
    +
     6642.   1612                exp = parse_expression(0);
    +
     6643.   1612                if (dups.some(is_dup)) {
    +
     6644.   1612
    +
     6645.   1612// test_cause:
    +
     6646.   1612// ["
    +
     6647.   1612// switch(0){case 0:break;case 0:break}
    +
     6648.   1612// ", "stmt_switch", "unexpected_a", "0", 29]
    +
     6649.   1612
    +
     6650.   1612                    warn("unexpected_a", exp);
    +
     6651.   1612                }
    +
     6652.   1612                dups.push(exp);
    +
     6653.   1612                the_case.expression.push(exp);
    +
     6654.   1612                advance(":");
    +
     6655.   1612                if (token_nxt.id !== "case") {
    +
     6656.   1612                    break;
    +
     6657.   1612                }
    +
     6658.   1612            }
    +
     6659.   1085
    +
     6660.   1085// test_cause:
    +
     6661.   1085// ["
    +
     6662.   1085// switch(0){case 1:case 0:break;}
    +
     6663.   1085// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 23]
    +
     6664.   1085// ["
    +
     6665.   1085// switch(0){case "aa":case 0:break;}
    +
     6666.   1085// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 26]
    +
     6667.   1085// ["
    +
     6668.   1085// switch(0){case "bb":case "aa":break;}
    +
     6669.   1085// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 26]
    +
     6670.   1085// ["
    +
     6671.   1085// switch(0){case aa:case "aa":break;}
    +
     6672.   1085// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24]
    +
     6673.   1085// ["
    +
     6674.   1085// switch(0){case bb:case aa:break;}
    +
     6675.   1085// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24]
    +
     6676.   1085
    +
     6677.   1085            check_ordered_case(the_case.expression);
    +
     6678.   1085            stmts = parse_statements();
    +
     6679.   1085            if (stmts.length < 1) {
    +
     6680.   1085
    +
     6681.   1085// test_cause:
    +
     6682.   1085// ["
    +
     6683.   1085// switch(0){case 0:default:}
    +
     6684.   1085// ", "stmt_switch", "expected_statements_a", "default", 18]
    +
     6685.   1085// ["switch(0){case 0:}", "stmt_switch", "expected_statements_a", "}", 18]
    +
     6686.   1085
    +
     6687.   1085                warn("expected_statements_a");
    +
     6688.   1085
    +
     6689.   1085// PR-359 - Bugfix - Fixes issue #358 - switch-statement crashes jslint.
    +
     6690.   1085
    +
     6691.   1085                break;
    +
     6692.   1085            }
    +
     6693.   1085            the_case.block = stmts;
    +
     6694.   1085            the_cases.push(the_case);
    +
     6695.   1085            last = stmts[stmts.length - 1];
    +
     6696.   1085            if (last.disrupt) {
    +
     6697.   1085                if (last.id === "break" && last.label === undefined) {
    +
     6698.   1085                    the_disrupt = false;
    +
     6699.   1085                }
    +
     6700.   1085            } else {
    +
     6701.   1085                warn("expected_a_before_b", token_nxt, "break;", artifact());
    +
     6702.   1085            }
    +
     6703.   1085            if (token_nxt.id !== "case") {
    +
     6704.   1085                break;
    +
     6705.   1085            }
    +
     6706.   1085        }
    +
     6707.    217
    +
     6708.    217// test_cause:
    +
     6709.    217// ["
    +
     6710.    217// switch(0){case 1:break;case 0:break;}
    +
     6711.    217// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 29]
    +
     6712.    217// ["
    +
     6713.    217// switch(0){case "aa":break;case 0:break;}
    +
     6714.    217// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 32]
    +
     6715.    217// ["
    +
     6716.    217// switch(0){case "bb":break;case "aa":break;}
    +
     6717.    217// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 32]
    +
     6718.    217// ["
    +
     6719.    217// switch(0){case aa:break;case "aa":break;}
    +
     6720.    217// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30]
    +
     6721.    217// ["
    +
     6722.    217// switch(0){case bb:break;case aa:break;}
    +
     6723.    217// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30]
    +
     6724.    217
    +
     6725.   1080        check_ordered_case(the_cases.map(function ({
    +
     6726.   1080            expression
    +
     6727.   1080        }) {
    +
     6728.   1080            return expression[0];
    +
     6729.   1080        }));
    +
     6730.    217        dups = undefined;
    +
     6731.    217        if (token_nxt.id === "default") {
    +
     6732.     99            the_default = token_nxt;
    +
     6733.     99            advance("default");
    +
     6734.     99            token_now.switch = true;
    +
     6735.     99            advance(":");
    +
     6736.     99            the_switch.else = parse_statements();
    +
     6737.     99            if (the_switch.else.length < 1) {
    +
     6738.     99
    +
     6739.     99// test_cause:
    +
     6740.     99// ["
    +
     6741.     99// switch(0){case 0:break;default:}
    +
     6742.     99// ", "stmt_switch", "unexpected_a", "default", 24]
    +
     6743.     99
    +
     6744.     99                warn("unexpected_a", the_default);
    +
     6745.     99                the_disrupt = false;
    +
     6746.     99            } else {
    +
     6747.     99                the_last = the_switch.else[
    +
     6748.     99                    the_switch.else.length - 1
    +
     6749.     99                ];
    +
     6750.     99                if (
    +
     6751.     99                    the_last.id === "break"
    +
     6752.     99                    && the_last.label === undefined
    +
     6753.     99                ) {
    +
     6754.     99
    +
     6755.     99// test_cause:
    +
     6756.     99// ["
    +
     6757.     99// switch(0){case 0:break;default:break;}
    +
     6758.     99// ", "stmt_switch", "unexpected_a", "break", 32]
    +
     6759.     99
    +
     6760.     99                    warn("unexpected_a", the_last);
    +
     6761.     99                    the_last.disrupt = false;
    +
     6762.     99                }
    +
     6763.     99                the_disrupt = the_disrupt && the_last.disrupt;
    +
     6764.     99            }
    +
     6765.    118        } else {
    +
     6766.    118            the_disrupt = false;
    +
     6767.    217        }
    +
     6768.    217        advance("}", the_switch);
    +
     6769.    217        functionage.switch -= 1;
    +
     6770.    217        the_switch.disrupt = the_disrupt;
    +
     6771.    217        return the_switch;
    +
     6772.    217    }
    +
     6773.    631
    +
     6774.     40    function stmt_throw() {
    +
     6775.     40        const the_throw = token_now;
    +
     6776.     40        the_throw.disrupt = true;
    +
     6777.     40        the_throw.expression = parse_expression(10);
    +
     6778.     40        semicolon();
    +
     6779.      1        if (functionage.try > 0) {
    +
     6780.      1
    +
     6781.      1// test_cause:
    +
     6782.      1// ["try{throw 0}catch(){}", "stmt_throw", "unexpected_a", "throw", 5]
    +
     6783.      1
    +
     6784.      1            warn("unexpected_a", the_throw);
    +
     6785.      1        }
    +
     6786.     40        return the_throw;
    +
     6787.     40    }
    +
     6788.    631
    +
     6789.     62    function stmt_try() {
    +
     6790.     62        const the_try = token_now;
    +
     6791.     62        let ignored;
    +
     6792.     62        let the_catch;
    +
     6793.     62        let the_disrupt;
    +
     6794.      1        if (functionage.try > 0) {
    +
     6795.      1
    +
     6796.      1// test_cause:
    +
     6797.      1// ["try{try{}catch(){}}catch(){}", "stmt_try", "unexpected_a", "try", 5]
    +
     6798.      1
    +
     6799.      1            warn("unexpected_a", the_try);
    +
     6800.      1        }
    +
     6801.     62        functionage.try += 1;
    +
     6802.     62        the_try.block = block();
    +
     6803.     62        the_disrupt = the_try.block.disrupt;
    +
     6804.     57        if (token_nxt.id === "catch") {
    +
     6805.     57            ignored = "ignore";
    +
     6806.     57            the_catch = token_nxt;
    +
     6807.     57            the_try.catch = the_catch;
    +
     6808.     57            advance("catch");
    +
     6809.     57
    +
     6810.     57// Create new catch-scope for catch-parameter.
    +
     6811.     57
    +
     6812.     57            catch_stack.push(catchage);
    +
     6813.     57            catchage = the_catch;
    +
     6814.     57            catch_list.push(catchage);
    +
     6815.     57            the_catch.context = empty();
    +
     6816.     57            if (token_nxt.id === "(") {
    +
     6817.     57                advance("(");
    +
     6818.     57                if (!token_nxt.identifier) {
    +
     6819.     57
    +
     6820.     57// test_cause:
    +
     6821.     57// ["try{}catch(){}", "stmt_try", "expected_identifier_a", ")", 12]
    +
     6822.     57
    +
     6823.     57                    return stop("expected_identifier_a");
    +
     6824.     57                }
    +
     6825.     57                if (token_nxt.id !== "ignore") {
    +
     6826.     57                    ignored = undefined;
    +
     6827.     57                    the_catch.name = token_nxt;
    +
     6828.     57                    enroll(token_nxt, "exception", true);
    +
     6829.     57                }
    +
     6830.     57                advance();
    +
     6831.     57                advance(")");
    +
     6832.     57            }
    +
     6833.     57            the_catch.block = block(ignored);
    +
     6834.     57            if (the_catch.block.disrupt !== true) {
    +
     6835.     57                the_disrupt = false;
    +
     6836.     57            }
    +
     6837.     57
    +
     6838.     57// Restore previous catch-scope after catch-block.
    +
     6839.     57
    +
     6840.     57            catchage = catch_stack.pop();
    +
     6841.     57
    +
     6842.     57// PR-404 - Relax warning about missing `catch` in `try...finally` statement.
    +
     6843.     57//
    +
     6844.     57//         } else {
    +
     6845.     57//
    +
     6846.     57// // test_cause:
    +
     6847.     57// // ["try{}finally{break;}", "stmt_try", "expected_a_before_b", "finally", 6]
    +
     6848.     57//
    +
     6849.     57//             warn("expected_a_before_b", token_nxt, "catch", artifact());
    +
     6850.     57
    +
     6851.     58        }
    +
     6852.     58        if (token_nxt.id === "finally") {
    +
     6853.      4            functionage.finally += 1;
    +
     6854.      4            advance("finally");
    +
     6855.      4            the_try.else = block();
    +
     6856.      4            the_disrupt = the_try.else.disrupt;
    +
     6857.      4            functionage.finally -= 1;
    +
     6858.     56        }
    +
     6859.     56        the_try.disrupt = the_disrupt;
    +
     6860.     56        functionage.try -= 1;
    +
     6861.     56        return the_try;
    +
     6862.     56    }
    +
     6863.    631
    +
     6864.   2334    function stmt_var() {
    +
     6865.   2334        let ellipsis;
    +
     6866.   2334        let mode_const;
    +
     6867.   2334        let name;
    +
     6868.   2334        let the_brace;
    +
     6869.   2334        let the_bracket;
    +
     6870.   2334        let the_variable = token_now;
    +
     6871.   2334        let variable_prv;
    +
     6872.   2334        mode_const = the_variable.id === "const";
    +
     6873.   2334        the_variable.names = [];
    +
     6874.   2334
    +
     6875.   2334// A program may use var or let, but not both.
    +
     6876.   2334
    +
     6877.   2058        if (!mode_const) {
    +
     6878.   2058            if (mode_var === undefined) {
    +
     6879.   2058                mode_var = the_variable.id;
    +
     6880.   2058            } else if (the_variable.id !== mode_var) {
    +
     6881.   2058
    +
     6882.   2058// test_cause:
    +
     6883.   2058// ["let aa;var aa", "stmt_var", "expected_a_b", "var", 8]
    +
     6884.   2058
    +
     6885.   2058                warn("expected_a_b", the_variable, mode_var, the_variable.id);
    +
     6886.   2058            }
    +
     6887.   2058        }
    +
     6888.   2334
    +
     6889.   2334// We don't expect to see variables created in switch statements.
    +
     6890.   2334
    +
     6891.      1        if (functionage.switch > 0) {
    +
     6892.      1
    +
     6893.      1// test_cause:
    +
     6894.      1// ["switch(0){case 0:var aa}", "stmt_var", "var_switch", "var", 18]
    +
     6895.      1
    +
     6896.      1            warn("var_switch", the_variable);
    +
     6897.      1        }
    +
     6898.   2334        switch (
    +
     6899.   2334            Boolean(functionage.statement_prv)
    +
     6900.   1449            && functionage.statement_prv.id
    +
     6901.   2334        ) {
    +
     6902.    107        case "const":
    +
     6903.   1437        case "let":
    +
     6904.   1439        case "var":
    +
     6905.   1439
    +
     6906.   1439// test_cause:
    +
     6907.   1439// ["const aa=0;const bb=0;", "stmt_var", "var_prv", "const", 0]
    +
     6908.   1439// ["let aa=0;let bb=0;", "stmt_var", "var_prv", "let", 0]
    +
     6909.   1439// ["var aa=0;var bb=0;", "stmt_var", "var_prv", "var", 0]
    +
     6910.   1439
    +
     6911.   1439            test_cause("var_prv", functionage.statement_prv.id);
    +
     6912.   1439            variable_prv = functionage.statement_prv;
    +
     6913.   1439            break;
    +
     6914.      3        case "import":
    +
     6915.      3
    +
     6916.      3// test_cause:
    +
     6917.      3// ["import aa from \"aa\";\nlet bb=0;", "stmt_var", "import_prv", "", 0]
    +
     6918.      3
    +
     6919.      3            test_cause("import_prv");
    +
     6920.      3            break;
    +
     6921.    885        case false:
    +
     6922.    885            break;
    +
     6923.      7        default:
    +
     6924.      7            if (
    +
     6925.      7                (option_dict.beta && !option_dict.variable)
    +
     6926.      7                || the_variable.id === "var"
    +
     6927.      7            ) {
    +
     6928.      7
    +
     6929.      7// test_cause:
    +
     6930.      7// ["console.log();let aa=0;", "stmt_var", "var_on_top", "let", 15]
    +
     6931.      7// ["console.log();var aa=0;", "stmt_var", "var_on_top", "var", 15]
    +
     6932.      7// ["try{aa();}catch(aa){var aa=0;}", "stmt_var", "var_on_top", "var", 21]
    +
     6933.      7// ["while(0){var aa;}", "stmt_var", "var_on_top", "var", 10]
    +
     6934.      7
    +
     6935.      7                warn("var_on_top", token_now);
    +
     6936.      7            }
    +
     6937.   2334        }
    +
     6938.   2335        while (true) {
    +
     6939.   2335            if (token_nxt.id === "{") {
    +
     6940.   2335                if (the_variable.id === "var") {
    +
     6941.   2335
    +
     6942.   2335// test_cause:
    +
     6943.   2335// ["var{aa}=0", "stmt_var", "unexpected_a", "var", 1]
    +
     6944.   2335
    +
     6945.   2335                    warn("unexpected_a", the_variable);
    +
     6946.   2335                }
    +
     6947.   2335                the_brace = token_nxt;
    +
     6948.   2335                advance("{");
    +
     6949.   2335                while (true) {
    +
     6950.   2335                    name = token_nxt;
    +
     6951.   2335                    if (!name.identifier) {
    +
     6952.   2335
    +
     6953.   2335// test_cause:
    +
     6954.   2335// ["let {0}", "stmt_var", "expected_identifier_a", "0", 6]
    +
     6955.   2335
    +
     6956.   2335                        return stop("expected_identifier_a");
    +
     6957.   2335                    }
    +
     6958.   2335                    survey(name);
    +
     6959.   2335                    advance();
    +
     6960.   2335                    if (token_nxt.id === ":") {
    +
     6961.   2335                        advance(":");
    +
     6962.   2335                        if (!token_nxt.identifier) {
    +
     6963.   2335
    +
     6964.   2335// test_cause:
    +
     6965.   2335// ["let {aa:0}", "stmt_var", "expected_identifier_a", "0", 9]
    +
     6966.   2335// ["let {aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 9]
    +
     6967.   2335
    +
     6968.   2335                            return stop("expected_identifier_a");
    +
     6969.   2335                        }
    +
     6970.   2335
    +
     6971.   2335// PR-363 - Bugfix
    +
     6972.   2335// Add test against false-warning <uninitialized 'bb'> in code
    +
     6973.   2335// '/*jslint node*/\nlet {aa:bb} = {}; bb();'.
    +
     6974.   2335//
    +
     6975.   2335//                         token_nxt.label = name;
    +
     6976.   2335//                         the_variable.names.push(token_nxt);
    +
     6977.   2335//                         enroll(token_nxt, "variable", mode_const);
    +
     6978.   2335
    +
     6979.   2335                        name = token_nxt;
    +
     6980.   2335                        the_variable.names.push(name);
    +
     6981.   2335                        survey(name);
    +
     6982.   2335                        enroll(name, "variable", mode_const);
    +
     6983.   2335
    +
     6984.   2335                        advance();
    +
     6985.   2335                        the_brace.open = true;
    +
     6986.   2335                    } else {
    +
     6987.   2335                        the_variable.names.push(name);
    +
     6988.   2335                        enroll(name, "variable", mode_const);
    +
     6989.   2335                    }
    +
     6990.   2335                    name.dead = false;
    +
     6991.   2335                    name.init = true;
    +
     6992.   2335                    if (token_nxt.id === "=") {
    +
     6993.   2335
    +
     6994.   2335// test_cause:
    +
     6995.   2335// ["let {aa=0}", "stmt_var", "assign", "", 0]
    +
     6996.   2335
    +
     6997.   2335                        test_cause("assign");
    +
     6998.   2335                        advance("=");
    +
     6999.   2335                        name.expression = parse_expression();
    +
     7000.   2335                        the_brace.open = true;
    +
     7001.   2335                    }
    +
     7002.   2335                    if (token_nxt.id !== ",") {
    +
     7003.   2335                        break;
    +
     7004.   2335                    }
    +
     7005.   2335                    advance(",");
    +
     7006.   2335                }
    +
     7007.   2335
    +
     7008.   2335// test_cause:
    +
     7009.   2335// ["let{bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8]
    +
     7010.   2335
    +
     7011.   2335                check_ordered(the_variable.id, the_variable.names);
    +
     7012.   2335                advance("}");
    +
     7013.   2335                advance("=");
    +
     7014.   2335                the_variable.expression = parse_expression(0);
    +
     7015.   2335            } else if (token_nxt.id === "[") {
    +
     7016.   2335                if (the_variable.id === "var") {
    +
     7017.   2335
    +
     7018.   2335// test_cause:
    +
     7019.   2335// ["var[aa]=0", "stmt_var", "unexpected_a", "var", 1]
    +
     7020.   2335
    +
     7021.   2335                    warn("unexpected_a", the_variable);
    +
     7022.   2335                }
    +
     7023.   2335                the_bracket = token_nxt;
    +
     7024.   2335                advance("[");
    +
     7025.   2335                while (true) {
    +
     7026.   2335                    ellipsis = false;
    +
     7027.   2335                    if (token_nxt.id === "...") {
    +
     7028.   2335                        ellipsis = true;
    +
     7029.   2335                        advance("...");
    +
     7030.   2335                    }
    +
     7031.   2335                    if (!token_nxt.identifier) {
    +
     7032.   2335
    +
     7033.   2335// test_cause:
    +
     7034.   2335// ["let[]", "stmt_var", "expected_identifier_a", "]", 5]
    +
     7035.   2335
    +
     7036.   2335                        return stop("expected_identifier_a");
    +
     7037.   2335                    }
    +
     7038.   2335                    name = token_nxt;
    +
     7039.   2335                    advance();
    +
     7040.   2335                    the_variable.names.push(name);
    +
     7041.   2335                    enroll(name, "variable", mode_const);
    +
     7042.   2335                    name.dead = false;
    +
     7043.   2335                    name.init = true;
    +
     7044.   2335                    if (ellipsis) {
    +
     7045.   2335                        name.ellipsis = true;
    +
     7046.   2335                        break;
    +
     7047.   2335                    }
    +
     7048.   2335                    if (token_nxt.id === "=") {
    +
     7049.   2335                        advance("=");
    +
     7050.   2335                        name.expression = parse_expression();
    +
     7051.   2335                        the_bracket.open = true;
    +
     7052.   2335                    }
    +
     7053.   2335                    if (token_nxt.id !== ",") {
    +
     7054.   2335                        break;
    +
     7055.   2335                    }
    +
     7056.   2335                    advance(",");
    +
     7057.   2335                }
    +
     7058.   2335                advance("]");
    +
     7059.   2335                advance("=");
    +
     7060.   2335                the_variable.expression = parse_expression(0);
    +
     7061.   2335            } else if (token_nxt.identifier) {
    +
     7062.   2335                name = token_nxt;
    +
     7063.   2335                advance();
    +
     7064.   2335                if (name.id === "ignore") {
    +
     7065.   2335
    +
     7066.   2335// test_cause:
    +
     7067.   2335// ["
    +
     7068.   2335// let ignore;function aa(ignore) {}
    +
     7069.   2335// ", "stmt_var", "unexpected_a", "ignore", 5]
    +
     7070.   2335
    +
     7071.   2335                    warn("unexpected_a", name);
    +
     7072.   2335                }
    +
     7073.   2335                enroll(name, "variable", mode_const);
    +
     7074.   2335                if (token_nxt.id === "=" || mode_const) {
    +
     7075.   2335                    advance("=");
    +
     7076.   2335                    name.dead = false;
    +
     7077.   2335                    name.init = true;
    +
     7078.   2335                    name.expression = parse_expression(0);
    +
     7079.   2335                }
    +
     7080.   2335                the_variable.names.push(name);
    +
     7081.   2335            } else {
    +
     7082.   2335
    +
     7083.   2335// test_cause:
    +
     7084.   2335// ["let 0", "stmt_var", "expected_identifier_a", "0", 5]
    +
     7085.   2335// ["var{aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 8]
    +
     7086.   2335
    +
     7087.   2335                return stop("expected_identifier_a");
    +
     7088.   2335            }
    +
     7089.   2335            if (token_nxt.id !== ",") {
    +
     7090.   2335                break;
    +
     7091.   2335            }
    +
     7092.   2335
    +
     7093.   2335// test_cause:
    +
     7094.   2335// ["let aa,bb;", "stmt_var", "expected_a_b", ",", 7]
    +
     7095.   2335
    +
     7096.   2335            warn("expected_a_b", token_nxt, ";", ",");
    +
     7097.   2335            advance(",");
    +
     7098.   2335        }
    +
     7099.   2320
    +
     7100.   2320// Warn if variable declarations are unordered.
    +
     7101.   2320
    +
     7102.   2320        if (
    +
     7103.   2320            option_dict.beta
    +
     7104.   2320            && !option_dict.unordered
    +
     7105.   2315            && !option_dict.variable
    +
     7106.   2309            && variable_prv
    +
     7107.   1437            && (
    +
     7108.   1437                variable_prv.id + " " + variable_prv.names[0].id
    +
     7109.   1437                > the_variable.id + " " + the_variable.names[0].id
    +
     7110.   1437            )
    +
     7111.      3        ) {
    +
     7112.      3
    +
     7113.      3// test_cause:
    +
     7114.      3// ["const bb=0;const aa=0;", "stmt_var", "expected_a_b_before_c_d", "aa", 12]
    +
     7115.      3// ["let bb;let aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8]
    +
     7116.      3// ["var bb;var aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8]
    +
     7117.      3
    +
     7118.      3            warn(
    +
     7119.      3                "expected_a_b_before_c_d",
    +
     7120.      3                the_variable,
    +
     7121.      3                the_variable.id,
    +
     7122.      3                the_variable.names[0].id,
    +
     7123.      3                variable_prv.id,
    +
     7124.      3                variable_prv.names[0].id
    +
     7125.      3            );
    +
     7126.   2320        }
    +
     7127.   2320        semicolon();
    +
     7128.   2320        return the_variable;
    +
     7129.   2320    }
    +
     7130.    631
    +
     7131.    208    function stmt_while() {
    +
     7132.    208        const the_while = token_now;
    +
     7133.    208        check_not_top_level(the_while);
    +
     7134.    208        functionage.loop += 1;
    +
     7135.    208        the_while.expression = condition();
    +
     7136.    208        the_while.block = block();
    +
     7137.      1        if (the_while.block.disrupt === true) {
    +
     7138.      1
    +
     7139.      1// test_cause:
    +
     7140.      1// ["function aa(){while(0){break;}}", "stmt_while", "weird_loop", "while", 15]
    +
     7141.      1
    +
     7142.      1            warn("weird_loop", the_while);
    +
     7143.    205        }
    +
     7144.    205        functionage.loop -= 1;
    +
     7145.    205        return the_while;
    +
     7146.    205    }
    +
     7147.    631
    +
     7148.      1    function stmt_with() {
    +
     7149.      1
    +
     7150.      1// test_cause:
    +
     7151.      1// ["with", "stmt_with", "unexpected_a", "with", 1]
    +
     7152.      1
    +
     7153.      1        stop("unexpected_a", token_now);
    +
     7154.      1    }
    +
     7155.    631
    +
     7156.  14600    function survey(name) {
    +
     7157.  14600        let id = name.id;
    +
     7158.  14600
    +
     7159.  14600// Tally the property name. If it is a string, only tally strings that conform
    +
     7160.  14600// to the identifier rules.
    +
     7161.  14600
    +
     7162.    183        if (id === "(string)") {
    +
     7163.    183            id = name.value;
    +
     7164.    183            if (!jslint_rgx_identifier.test(id)) {
    +
     7165.    183                return id;
    +
     7166.    183            }
    +
     7167.  14417        } else if (id === "`") {
    +
     7168.  14417            if (name.value.length === 1) {
    +
     7169.  14417                id = name.value[0].value;
    +
     7170.  14417                if (!jslint_rgx_identifier.test(id)) {
    +
     7171.  14417                    return id;
    +
     7172.  14417                }
    +
     7173.  14417            }
    +
     7174.  14417        } else if (!name.identifier) {
    +
     7175.  14417
    +
     7176.  14417// test_cause:
    +
     7177.  14417// ["let aa={0:0}", "survey", "expected_identifier_a", "0", 9]
    +
     7178.  14417
    +
     7179.  14417            return stop("expected_identifier_a", name);
    +
     7180.  14513        }
    +
     7181.  14513
    +
     7182.  14513// If we have seen this name before, increment its count.
    +
     7183.  14513
    +
     7184.  14513        if (typeof property_dict[id] === "number") {
    +
     7185.  12086            property_dict[id] += 1;
    +
     7186.  12086
    +
     7187.  12086// If this is the first time seeing this property name, and if there is a
    +
     7188.  12086// tenure list, then it must be on the list. Otherwise, it must conform to
    +
     7189.  12086// the rules for good property names.
    +
     7190.  12086
    +
     7191.  12086        } else {
    +
     7192.   2427            if (state.mode_property) {
    +
     7193.   2427                if (tenure[id] !== true) {
    +
     7194.   2427
    +
     7195.   2427// test_cause:
    +
     7196.   2427// ["/*property aa*/\naa.bb", "survey", "unregistered_property_a", "bb", 4]
    +
     7197.   2427
    +
     7198.   2427                    warn("unregistered_property_a", name);
    +
     7199.   2427                }
    +
     7200.   2427            } else if (
    +
     7201.   2427                !option_dict.nomen
    +
     7202.   2427                && name.identifier
    +
     7203.   2427                && jslint_rgx_weird_property.test(id)
    +
     7204.   2427            ) {
    +
     7205.   2427
    +
     7206.   2427// test_cause:
    +
     7207.   2427// ["aa.$", "survey", "weird_property_a", "$", 4]
    +
     7208.   2427// ["aa._", "survey", "weird_property_a", "_", 4]
    +
     7209.   2427// ["aa._aa", "survey", "weird_property_a", "_aa", 4]
    +
     7210.   2427// ["aa.aaSync", "survey", "weird_property_a", "aaSync", 4]
    +
     7211.   2427// ["aa.aa_", "survey", "weird_property_a", "aa_", 4]
    +
     7212.   2427
    +
     7213.   2427                warn("weird_property_a", name);
    +
     7214.   2427            }
    +
     7215.   2427            property_dict[id] = 1;
    +
     7216.  14513        }
    +
     7217.  14513        return id;
    +
     7218.  14513    }
    +
     7219.    631
    +
     7220.    631// These functions are used to specify the grammar of our language:
    +
     7221.    631
    +
     7222.  82030    function symbol(id, bp) {
    +
     7223.  82030
    +
     7224.  82030// Create a symbol if it does not already exist in the language's syntax.
    +
     7225.  82030
    +
     7226.  82030        let the_symbol = syntax_dict[id];
    +
     7227.  71303        if (the_symbol === undefined) {
    +
     7228.  71303            the_symbol = empty();
    +
     7229.  71303            the_symbol.id = id;
    +
     7230.  71303            the_symbol.lbp = bp || 0;
    +
     7231.  71303            syntax_dict[id] = the_symbol;
    +
     7232.  71303        }
    +
     7233.  82030        return the_symbol;
    +
     7234.  82030    }
    +
     7235.    631
    +
     7236.    631    function ternary(id1, id2) {
    +
     7237.    631
    +
     7238.    631// Create a ternary operator.
    +
     7239.    631
    +
     7240.    631        const the_symbol = symbol(id1, 30);
    +
     7241.    213        the_symbol.led_infix = function parse_ternary_led(left) {
    +
     7242.    213            const the_token = token_now;
    +
     7243.    213            let second;
    +
     7244.    213            second = parse_expression(20);
    +
     7245.    213            advance(id2);
    +
     7246.    213            token_now.arity = "ternary";
    +
     7247.    213            the_token.arity = "ternary";
    +
     7248.    213            the_token.expression = [left, second, parse_expression(10)];
    +
     7249.      5            if (token_nxt.id !== ")") {
    +
     7250.      5
    +
     7251.      5// test_cause:
    +
     7252.      5// ["0?0:0", "parse_ternary_led", "use_open", "?", 2]
    +
     7253.      5
    +
     7254.      5                warn("use_open", the_token);
    +
     7255.      5            }
    +
     7256.    213            return the_token;
    +
     7257.    213        };
    +
     7258.    631        return the_symbol;
    +
     7259.    631    }
    +
     7260.    631
    +
     7261.    631// Now we parse JavaScript.
    +
     7262.    631// Begin defining the language.
    +
     7263.    631
    +
     7264.    631    assignment("%=");
    +
     7265.    631    assignment("&=");
    +
     7266.    631    assignment("*=");
    +
     7267.    631    assignment("+=");
    +
     7268.    631    assignment("-=");
    +
     7269.    631    assignment("/=");
    +
     7270.    631    assignment("<<=");
    +
     7271.    631    assignment("=");
    +
     7272.    631    assignment(">>=");
    +
     7273.    631    assignment(">>>=");
    +
     7274.    631    assignment("^=");
    +
     7275.    631    assignment("|=");
    +
     7276.    631    constant("(number)", "number");
    +
     7277.    631    constant("(regexp)", "regexp");
    +
     7278.    631    constant("(string)", "string");
    +
     7279.    631    constant("Function", "function", constant_Function);
    +
     7280.    631    constant("Infinity", "number", Infinity);
    +
     7281.    631    constant("NaN", "number", NaN);
    +
     7282.    631    constant("arguments", "object", constant_arguments);
    +
     7283.    631    constant("eval", "function", constant_eval);
    +
     7284.    631    constant("false", "boolean", false);
    +
     7285.    631    constant("ignore", "undefined", constant_ignore);
    +
     7286.    631    constant("isFinite", "function", constant_isInfinite);
    +
     7287.    631    constant("isNaN", "function", constant_isNaN);
    +
     7288.    631    constant("null", "null", null);
    +
     7289.    631    constant("this", "object", constant_this);
    +
     7290.    631    constant("true", "boolean", true);
    +
     7291.    631    constant("undefined", "undefined");
    +
     7292.    631    infix(100, "!=");
    +
     7293.    631    infix(100, "!==");
    +
     7294.    631    infix(100, "==");
    +
     7295.    631    infix(100, "===");
    +
     7296.    631    infix(110, "<");
    +
     7297.    631    infix(110, "<=");
    +
     7298.    631    infix(110, ">");
    +
     7299.    631    infix(110, ">=");
    +
     7300.    631    infix(110, "in");
    +
     7301.    631    infix(110, "instanceof");
    +
     7302.    631    infix(120, "<<");
    +
     7303.    631    infix(120, ">>");
    +
     7304.    631    infix(120, ">>>");
    +
     7305.    631    infix(130, "+");
    +
     7306.    631    infix(130, "-");
    +
     7307.    631    infix(140, "%");
    +
     7308.    631    infix(140, "*");
    +
     7309.    631    infix(140, "/");
    +
     7310.    631    infix(160, "(", infix_lparen);
    +
     7311.    631    infix(160, "`", infix_grave);
    +
     7312.    631    infix(170, ".", infix_dot);
    +
     7313.    631    infix(170, "=>", infix_fart_unwrapped);
    +
     7314.    631    infix(170, "?.", infix_option_chain);
    +
     7315.    631    infix(170, "[", infix_lbracket);
    +
     7316.    631    infix(35, "??");
    +
     7317.    631    infix(40, "||");
    +
     7318.    631    infix(50, "&&");
    +
     7319.    631    infix(70, "|");
    +
     7320.    631    infix(80, "^");
    +
     7321.    631    infix(90, "&");
    +
     7322.    631    infixr(150, "**");
    +
     7323.    631    postassign("++");
    +
     7324.    631    postassign("--");
    +
     7325.    631    preassign("++");
    +
     7326.    631    preassign("--");
    +
     7327.    631    prefix("!!");
    +
     7328.    631    prefix("!");
    +
     7329.    631    prefix("(", prefix_lparen);
    +
     7330.    631    prefix("+");
    +
     7331.    631    prefix("-");
    +
     7332.    631    prefix("/=", prefix_assign_divide);
    +
     7333.    631    prefix("=>", prefix_fart);
    +
     7334.    631    prefix("[", prefix_lbracket);
    +
     7335.    631    prefix("`", prefix_tick);
    +
     7336.    631    prefix("async", prefix_async);
    +
     7337.    631    prefix("await", prefix_await);
    +
     7338.    631    prefix("function", prefix_function);
    +
     7339.    631    prefix("new", prefix_new);
    +
     7340.    631    prefix("typeof");
    +
     7341.    631    prefix("void", prefix_void);
    +
     7342.    631    prefix("{", prefix_lbrace);
    +
     7343.    631    prefix("~");
    +
     7344.    631    stmt(";", stmt_semicolon);
    +
     7345.    631    stmt("async", prefix_async);
    +
     7346.    631    stmt("await", prefix_await);
    +
     7347.    631    stmt("break", stmt_break);
    +
     7348.    631    stmt("const", stmt_var);
    +
     7349.    631    stmt("continue", stmt_continue);
    +
     7350.    631    stmt("debugger", stmt_debugger);
    +
     7351.    631    stmt("delete", stmt_delete);
    +
     7352.    631    stmt("do", stmt_do);
    +
     7353.    631    stmt("export", stmt_export);
    +
     7354.    631    stmt("for", stmt_for);
    +
     7355.    631    stmt("function", prefix_function);
    +
     7356.    631    stmt("if", stmt_if);
    +
     7357.    631    stmt("import", stmt_import);
    +
     7358.    631    stmt("let", stmt_var);
    +
     7359.    631    stmt("return", stmt_return);
    +
     7360.    631    stmt("switch", stmt_switch);
    +
     7361.    631    stmt("throw", stmt_throw);
    +
     7362.    631    stmt("try", stmt_try);
    +
     7363.    631    stmt("var", stmt_var);
    +
     7364.    631    stmt("while", stmt_while);
    +
     7365.    631    stmt("with", stmt_with);
    +
     7366.    631    stmt("{", stmt_lbrace);
    +
     7367.    631    symbol(")");
    +
     7368.    631    symbol("*/");
    +
     7369.    631    symbol(",");
    +
     7370.    631    symbol(":");
    +
     7371.    631    symbol(";");
    +
     7372.    631    symbol("]");
    +
     7373.    631    symbol("async");
    +
     7374.    631    symbol("await");
    +
     7375.    631    symbol("case");
    +
     7376.    631    symbol("catch");
    +
     7377.    631    symbol("class");
    +
     7378.    631    symbol("default");
    +
     7379.    631    symbol("else");
    +
     7380.    631    symbol("enum");
    +
     7381.    631    symbol("finally");
    +
     7382.    631    symbol("implements");
    +
     7383.    631    symbol("interface");
    +
     7384.    631    symbol("package");
    +
     7385.    631    symbol("private");
    +
     7386.    631    symbol("protected");
    +
     7387.    631    symbol("public");
    +
     7388.    631    symbol("static");
    +
     7389.    631    symbol("super");
    +
     7390.    631    symbol("void");
    +
     7391.    631    symbol("yield");
    +
     7392.    631    symbol("}");
    +
     7393.    631    ternary("?", ":");
    +
     7394.    631
    +
     7395.    631// Init token_nxt.
    +
     7396.    631
    +
     7397.    631    advance();
    +
     7398.    631
    +
     7399.    631// Parsing of JSON is simple:
    +
     7400.    631
    +
     7401.     25    if (state.mode_json) {
    +
     7402.     25        state.token_tree = parse_json();
    +
     7403.     25        advance("(end)");
    +
     7404.     25        return;
    +
     7405.    606    }
    +
     7406.    606
    +
     7407.    606// Because browsers encourage combining of script files, the first token might
    +
     7408.    606// be a semicolon to defend against a missing semicolon in the preceding file.
    +
     7409.    606
    +
     7410.    606    if (option_dict.browser) {
    +
     7411.      5        if (token_nxt.id === ";") {
    +
     7412.      5            advance(";");
    +
     7413.      5        }
    +
     7414.      5
    +
     7415.      5// If we are not in a browser, then the file form of strict pragma may be used.
    +
     7416.      5
    +
     7417.    601    } else if (token_nxt.value === "use strict") {
    +
     7418.    601        advance("(string)");
    +
     7419.    601        advance(";");
    +
     7420.    606    }
    +
     7421.    606    state.token_tree = parse_statements();
    +
     7422.    606    advance("(end)");
    +
     7423.    606
    +
     7424.    606// Check global functions are ordered.
    +
     7425.    606
    +
     7426.    606    check_ordered(
    +
     7427.    606        "function",
    +
     7428.   2001        function_list.map(function ({
    +
     7429.   2001            level,
    +
     7430.   2001            name
    +
     7431.   2001        }) {
    +
     7432.    606            return (level === 1) && name;
    +
     7433.   2001        }).filter(function (name) {
    +
     7434.   1992            return option_dict.beta && name && name.id;
    +
     7435.   2001        })
    +
     7436.    606    );
    +
     7437.    606}
    +
     7438.      1
    +
     7439.    518function jslint_phase4_walk(state) {
    +
     7440.    518
    +
     7441.    518// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
    +
     7442.    518//          recursive traversal. Each node may be processed on the way down
    +
     7443.    518//          (preaction) and on the way up (postaction).
    +
     7444.    518
    +
     7445.    518    let {
    +
     7446.    518        artifact,
    +
     7447.    518        catch_stack,
    +
     7448.    518        function_stack,
    +
     7449.    518        global_dict,
    +
     7450.    518        is_equal,
    +
     7451.    518        is_weird,
    +
     7452.    518        option_dict,
    +
     7453.    518        syntax_dict,
    +
     7454.    518        test_cause,
    +
     7455.    518        token_global,
    +
     7456.    518        warn
    +
     7457.    518    } = state;
    +
     7458.    518    let block_stack = [];               // The stack of blocks.
    +
     7459.    518    let blockage = token_global;        // The current block.
    +
     7460.    518    let catchage = catch_stack[0];      // The current catch-block.
    +
     7461.    518    let functionage = token_global;     // The current function.
    +
     7462.    518    let postaction;
    +
     7463.    518    let postamble;
    +
     7464.    518    let posts = empty();
    +
     7465.    518    let preaction;
    +
     7466.    518    let preamble;
    +
     7467.    518    let pres = empty();
    +
     7468.    518
    +
     7469.    518// The relational operators.
    +
     7470.    518
    +
     7471.    518    let relationop = object_assign_from_list(empty(), [
    +
     7472.    518        "!=", "!==", "<", "<=", "==", "===", ">", ">="
    +
     7473.    518    ], true);
    +
     7474.    518
    +
     7475.    518// Ambulation of the parse tree.
    +
     7476.    518
    +
     7477.   1036    function action(when) {
    +
     7478.   1036
    +
     7479.   1036// Produce a function that will register task functions that will be called as
    +
     7480.   1036// the tree is traversed.
    +
     7481.   1036
    +
     7482.  19684        return function (arity, id, task) {
    +
     7483.  19684            let a_set = when[arity];
    +
     7484.  19684            let i_set;
    +
     7485.  19684
    +
     7486.  19684// The id parameter is optional. If excluded, the task will be applied to all
    +
     7487.  19684// ids.
    +
     7488.  19684
    +
     7489.   4144            if (typeof id !== "string") {
    +
     7490.   4144                task = id;
    +
     7491.   4144                id = "(all)";
    +
     7492.   4144            }
    +
     7493.  19684
    +
     7494.  19684// If this arity has no registrations yet, then create a set object to hold
    +
     7495.  19684// them.
    +
     7496.  19684
    +
     7497.   5180            if (a_set === undefined) {
    +
     7498.   5180                a_set = empty();
    +
     7499.   5180                when[arity] = a_set;
    +
     7500.   5180            }
    +
     7501.  19684
    +
     7502.  19684// If this id has no registrations yet, then create a set array to hold them.
    +
     7503.  19684
    +
     7504.  19684            i_set = a_set[id];
    +
     7505.  19166            if (i_set === undefined) {
    +
     7506.  19166                i_set = [];
    +
     7507.  19166                a_set[id] = i_set;
    +
     7508.  19166            }
    +
     7509.  19684
    +
     7510.  19684// Register the task with the arity and the id.
    +
     7511.  19684
    +
     7512.  19684            i_set.push(task);
    +
     7513.  19684        };
    +
     7514.   1036    }
    +
     7515.    518
    +
     7516.   1036    function amble(when) {
    +
     7517.   1036
    +
     7518.   1036// Produce a function that will act on the tasks registered by an action
    +
     7519.   1036// function while walking the tree.
    +
     7520.   1036
    +
     7521. 210942        return function (the_token) {
    +
     7522. 210942
    +
     7523. 210942// Given a task set that was built by an action function, run all
    +
     7524. 210942// relevant tasks on the token.
    +
     7525. 210942
    +
     7526. 210942            let a_set = when[the_token.arity];
    +
     7527. 210942            let i_set;
    +
     7528. 210942
    +
     7529. 210942// If there are tasks associated with the token's arity...
    +
     7530. 210942
    +
     7531. 144702            if (a_set !== undefined) {
    +
     7532. 144702
    +
     7533. 144702// If there are tasks associated with the token's id...
    +
     7534. 144702
    +
     7535. 144702                i_set = a_set[the_token.id];
    +
     7536. 144702                if (i_set !== undefined) {
    +
     7537. 144702                    i_set.forEach(function (task) {
    +
     7538. 144702                        task(the_token);
    +
     7539. 144702                    });
    +
     7540. 144702                }
    +
     7541. 144702
    +
     7542. 144702// If there are tasks for all ids.
    +
     7543. 144702
    +
     7544. 144702                i_set = a_set["(all)"];
    +
     7545. 144702                if (i_set !== undefined) {
    +
     7546. 144702                    i_set.forEach(function (task) {
    +
     7547. 144702                        task(the_token);
    +
     7548. 144702                    });
    +
     7549. 144702                }
    +
     7550. 144702            }
    +
     7551. 210942        };
    +
     7552.   1036    }
    +
     7553.    518
    +
     7554.   2818    function init_variable(name) {
    +
     7555.   2818        let the_variable = lookup(name);
    +
     7556.   2759        if (!the_variable || the_variable.readonly) {
    +
     7557.     61            warn("bad_assignment_a", name);
    +
     7558.     61            return;
    +
     7559.   2757        }
    +
     7560.   2757        the_variable.init = true;
    +
     7561.   2757    }
    +
     7562.    518
    +
     7563.  31509    function lookup(thing) {
    +
     7564.  31509        let id = thing.id;
    +
     7565.  31509        let the_variable;
    +
     7566.      1        if (thing.arity !== "variable") {
    +
     7567.      1            return;
    +
     7568.  31508        }
    +
     7569.  31508
    +
     7570.  31508// Look up the variable in the current context.
    +
     7571.  31508
    +
     7572.  31508        the_variable = functionage.context[id] || catchage.context[id];
    +
     7573.  31509
    +
     7574.  31509// If it isn't local, search all the other contexts. If there are name
    +
     7575.  31509// collisions, take the most recent.
    +
     7576.  31509
    +
     7577.  24629        if (the_variable && the_variable.role === "label") {
    +
     7578.      1
    +
     7579.      1// test_cause:
    +
     7580.      1// ["aa:while(0){aa;}", "lookup", "label_a", "aa", 13]
    +
     7581.      1
    +
     7582.      1            warn("label_a", thing);
    +
     7583.      1            return the_variable;
    +
     7584.  31507        }
    +
     7585.  31507        if (!the_variable) {
    +
     7586.  14775            function_stack.forEach(function ({
    +
     7587.  14775                context
    +
     7588.  14775            }) {
    +
     7589.   6879                if (context[id] && context[id].role !== "label") {
    +
     7590.   6879                    the_variable = context[id];
    +
     7591.   6879                }
    +
     7592.  14775            });
    +
     7593.   6879
    +
     7594.   6879// If it isn't in any of those either, perhaps it is a predefined global.
    +
     7595.   6879// If so, add it to the global context.
    +
     7596.   6879
    +
     7597.   6879            if (!the_variable && global_dict[id] === undefined) {
    +
     7598.   6879
    +
     7599.   6879// test_cause:
    +
     7600.   6879// ["aa", "lookup", "undeclared_a", "aa", 1]
    +
     7601.   6879// ["class aa{}", "lookup", "undeclared_a", "aa", 7]
    +
     7602.   6879// ["
    +
     7603.   6879// let aa=0;try{aa();}catch(bb){bb();}bb();
    +
     7604.   6879// ", "lookup", "undeclared_a", "bb", 36]
    +
     7605.   6879// ["
    +
     7606.   6879// let aa=0;try{aa();}catch(ignore){bb();}
    +
     7607.   6879// ", "lookup", "undeclared_a", "bb", 34]
    +
     7608.   6879
    +
     7609.   6879                warn("undeclared_a", thing);
    +
     7610.   6879                return;
    +
     7611.   6879            }
    +
     7612.   6879            if (!the_variable) {
    +
     7613.   6879                the_variable = {
    +
     7614.   6879                    dead: false,
    +
     7615.   6879                    id,
    +
     7616.   6879                    init: true,
    +
     7617.   6879                    parent: token_global,
    +
     7618.   6879                    readonly: true,
    +
     7619.   6879                    role: "variable",
    +
     7620.   6879                    used: 0
    +
     7621.   6879                };
    +
     7622.   6879                token_global.context[id] = the_variable;
    +
     7623.   6879            }
    +
     7624.   6879            the_variable.closure = true;
    +
     7625.   6879            functionage.context[id] = the_variable;
    +
     7626.  31370        }
    +
     7627.  31370        if (
    +
     7628.  31370            (
    +
     7629.  31370                the_variable.calls === undefined
    +
     7630.  31370                || functionage.name === undefined
    +
     7631.   4364                || the_variable.calls[functionage.name.id] === undefined
    +
     7632.  31509            )
    +
     7633.  31185            && the_variable.dead
    +
     7634.      3        ) {
    +
     7635.      3
    +
     7636.      3// test_cause:
    +
     7637.      3// ["let aa;if(aa){let bb;}bb;", "lookup", "out_of_scope_a", "bb", 23]
    +
     7638.      3
    +
     7639.      3            warn("out_of_scope_a", thing);
    +
     7640.  31370        }
    +
     7641.  31370        return the_variable;
    +
     7642.  31370    }
    +
     7643.    518
    +
     7644.   5009    function post_a(thing) {
    +
     7645.   5009
    +
     7646.   5009// Assignment using = sets the init property of a variable. No other assignment
    +
     7647.   5009// operator can do this. A = token keeps that variable (or array of variables
    +
     7648.   5009// in case of destructuring) in its name property.
    +
     7649.   5009
    +
     7650.   5009        const lvalue = thing.expression[0];
    +
     7651.   5009        let right;
    +
     7652.   4234        if (thing.id === "=") {
    +
     7653.   4234            if (thing.names !== undefined) {
    +
     7654.   4234
    +
     7655.   4234// test_cause:
    +
     7656.   4234// ["if(0){aa=0}", "post_a", "=", "", 0]
    +
     7657.   4234
    +
     7658.   4234                test_cause("=");
    +
     7659.   4234
    +
     7660.   4234// Probably deadcode.
    +
     7661.   4234// if (Array.isArray(thing.names)) {
    +
     7662.   4234//     thing.names.forEach(init_variable);
    +
     7663.   4234// } else {
    +
     7664.   4234//     init_variable(thing.names);
    +
     7665.   4234// }
    +
     7666.   4234
    +
     7667.   4234                jslint_assert(
    +
     7668.   4234                    !Array.isArray(thing.names),
    +
     7669.   4234                    `Expected !Array.isArray(thing.names).`
    +
     7670.   4234                );
    +
     7671.   4234                init_variable(thing.names);
    +
     7672.   4234            } else {
    +
     7673.   4234                if (lvalue.id === "[" || lvalue.id === "{") {
    +
     7674.   4234                    lvalue.expression.forEach(function (thing) {
    +
     7675.   4234                        if (thing.variable) {
    +
     7676.   4234                            thing.variable.init = true;
    +
     7677.   4234                        }
    +
     7678.   4234                    });
    +
     7679.   4234                } else if (
    +
     7680.   4234                    lvalue.id === "."
    +
     7681.   4234                    && thing.expression[1].id === "undefined"
    +
     7682.   4234                ) {
    +
     7683.   4234
    +
     7684.   4234// test_cause:
    +
     7685.   4234// ["aa.aa=undefined", "post_a", "expected_a_b", "undefined", 1]
    +
     7686.   4234
    +
     7687.   4234                    warn(
    +
     7688.   4234                        "expected_a_b",
    +
     7689.   4234                        lvalue.expression,
    +
     7690.   4234                        "delete",
    +
     7691.   4234                        "undefined"
    +
     7692.   4234                    );
    +
     7693.   4234                }
    +
     7694.   4234            }
    +
     7695.   4234        } else {
    +
     7696.    775            if (lvalue.arity === "variable") {
    +
     7697.    775                if (!lvalue.variable || lvalue.variable.readonly) {
    +
     7698.    775                    warn("bad_assignment_a", lvalue);
    +
     7699.    775                }
    +
     7700.    775            }
    +
     7701.    775            right = syntax_dict[thing.expression[1].id];
    +
     7702.    775            if (
    +
     7703.    775                right !== undefined
    +
     7704.    775                && (
    +
     7705.    775                    right.id === "function"
    +
     7706.    775                    || right.id === "=>"
    +
     7707.    775                    || (
    +
     7708.    775                        right.constant
    +
     7709.    775                        && right.id !== "(number)"
    +
     7710.    775                        && (right.id !== "(string)" || thing.id !== "+=")
    +
     7711.    775                    )
    +
     7712.    775                )
    +
     7713.    775            ) {
    +
     7714.    775
    +
     7715.    775// test_cause:
    +
     7716.    775// ["aa+=undefined", "post_a", "unexpected_a", "undefined", 5]
    +
     7717.    775
    +
     7718.    775                warn("unexpected_a", thing.expression[1]);
    +
     7719.    775            }
    +
     7720.    775        }
    +
     7721.   5009    }
    +
     7722.    518
    +
     7723.    706    function post_a_pluseq(thing) {
    +
     7724.    706        const right = thing.expression[1];
    +
     7725.    396        if (right.constant) {
    +
     7726.    396            if (
    +
     7727.    396                right.value === ""
    +
     7728.    396                || (right.id === "(number)" && right.value === "0")
    +
     7729.    396                || right.id === "(boolean)"
    +
     7730.    396                || right.id === "null"
    +
     7731.    396                || right.id === "undefined"
    +
     7732.    396                || Number.isNaN(right.value)
    +
     7733.    396            ) {
    +
     7734.    396                warn("unexpected_a", right);
    +
     7735.    396            }
    +
     7736.    396        }
    +
     7737.    706    }
    +
     7738.    518
    +
     7739.  31933    function post_b(thing) {
    +
     7740.  31933        let right;
    +
     7741.   3960        if (relationop[thing.id]) {
    +
     7742.   3960            if (
    +
     7743.   3960                is_weird(thing.expression[0])
    +
     7744.   3960                || is_weird(thing.expression[1])
    +
     7745.   3960                || is_equal(thing.expression[0], thing.expression[1])
    +
     7746.   3960                || (
    +
     7747.   3960                    thing.expression[0].constant === true
    +
     7748.   3960                    && thing.expression[1].constant === true
    +
     7749.   3960                )
    +
     7750.   3960            ) {
    +
     7751.   3960
    +
     7752.   3960// test_cause:
    +
     7753.   3960// ["if(0===0){0}", "post_b", "weird_relation_a", "===", 5]
    +
     7754.   3960
    +
     7755.   3960                warn("weird_relation_a", thing);
    +
     7756.   3960            }
    +
     7757.   3960        }
    +
     7758.   2023        if (thing.id === "+") {
    +
     7759.   2023            if (!option_dict.convert) {
    +
     7760.   2023                if (thing.expression[0].value === "") {
    +
     7761.   2023
    +
     7762.   2023// test_cause:
    +
     7763.   2023// ["\"\"+0", "post_b", "expected_a_b", "\"\" +", 3]
    +
     7764.   2023
    +
     7765.   2023                    warn("expected_a_b", thing, "String(...)", "\"\" +");
    +
     7766.   2023                } else if (thing.expression[1].value === "") {
    +
     7767.   2023
    +
     7768.   2023// test_cause:
    +
     7769.   2023// ["0+\"\"", "post_b", "expected_a_b", "+ \"\"", 2]
    +
     7770.   2023
    +
     7771.   2023                    warn("expected_a_b", thing, "String(...)", "+ \"\"");
    +
     7772.   2023                }
    +
     7773.   2023            }
    +
     7774.  29910        } else if (thing.id === "[") {
    +
     7775.  29910            if (thing.expression[0].id === "window") {
    +
     7776.  29910
    +
     7777.  29910// test_cause:
    +
     7778.  29910// ["aa=window[0]", "post_b", "weird_expression_a", "window[...]", 10]
    +
     7779.  29910
    +
     7780.  29910                warn("weird_expression_a", thing, "window[...]");
    +
     7781.  29910            }
    +
     7782.  29910            if (thing.expression[0].id === "self") {
    +
     7783.  29910
    +
     7784.  29910// test_cause:
    +
     7785.  29910// ["aa=self[0]", "post_b", "weird_expression_a", "self[...]", 8]
    +
     7786.  29910
    +
     7787.  29910                warn("weird_expression_a", thing, "self[...]");
    +
     7788.  29910            }
    +
     7789.  29910        } else if (thing.id === "." || thing.id === "?.") {
    +
     7790.  29910            if (thing.expression.id === "RegExp") {
    +
     7791.  29910
    +
     7792.  29910// test_cause:
    +
     7793.  29910// ["aa=RegExp.aa", "post_b", "weird_expression_a", ".", 10]
    +
     7794.  29910
    +
     7795.  29910                warn("weird_expression_a", thing);
    +
     7796.  29910            }
    +
     7797.  29910        } else if (thing.id !== "=>" && thing.id !== "(") {
    +
     7798.  29910            right = thing.expression[1];
    +
     7799.  29910            if (
    +
     7800.  29910                (thing.id === "+" || thing.id === "-")
    +
     7801.  29910                && right.id === thing.id
    +
     7802.  29910                && right.arity === "unary"
    +
     7803.  29910                && !right.wrapped
    +
     7804.  29910            ) {
    +
     7805.  29910
    +
     7806.  29910// test_cause:
    +
     7807.  29910// ["0- -0", "post_b", "wrap_unary", "-", 4]
    +
     7808.  29910
    +
     7809.  29910                warn("wrap_unary", right);
    +
     7810.  29910            }
    +
     7811.  29910            if (
    +
     7812.  29910                thing.expression[0].constant === true
    +
     7813.  29910                && right.constant === true
    +
     7814.  29910            ) {
    +
     7815.  29910                thing.constant = true;
    +
     7816.  29910            }
    +
     7817.  29910        }
    +
     7818.  31933    }
    +
     7819.    518
    +
     7820.   1202    function post_b_and(thing) {
    +
     7821.   1202        if (
    +
     7822.   1202            is_weird(thing.expression[0])
    +
     7823.   1197            || is_equal(thing.expression[0], thing.expression[1])
    +
     7824.   1183            || thing.expression[0].constant === true
    +
     7825.   1181            || thing.expression[1].constant === true
    +
     7826.     21        ) {
    +
     7827.     21
    +
     7828.     21// test_cause:
    +
     7829.     21// ["aa=(()=>0)&&(()=>0)", "post_b_and", "weird_condition_a", "&&", 11]
    +
     7830.     21// ["aa=(``?``:``)&&(``?``:``)", "post_b_and", "weird_condition_a", "&&", 14]
    +
     7831.     21// ["aa=/./&&/./", "post_b_and", "weird_condition_a", "&&", 7]
    +
     7832.     21// ["aa=0&&0", "post_b_and", "weird_condition_a", "&&", 5]
    +
     7833.     21// ["aa=[]&&[]", "post_b_and", "weird_condition_a", "&&", 6]
    +
     7834.     21// ["aa=`${0}`&&`${0}`", "post_b_and", "weird_condition_a", "&&", 10]
    +
     7835.     21// ["
    +
     7836.     21// aa=function aa(){}&&function aa(){}
    +
     7837.     21// ", "post_b_and", "weird_condition_a", "&&", 19]
    +
     7838.     21// ["aa={}&&{}", "post_b_and", "weird_condition_a", "&&", 6]
    +
     7839.     21
    +
     7840.     21            warn("weird_condition_a", thing);
    +
     7841.     21        }
    +
     7842.   1202    }
    +
     7843.    518
    +
     7844.   1460    function post_b_lbracket(thing) {
    +
     7845.      1        if (thing.expression[0].id === "RegExp") {
    +
     7846.      1
    +
     7847.      1// test_cause:
    +
     7848.      1// ["aa=RegExp[0]", "post_b_lbracket", "weird_expression_a", "[", 10]
    +
     7849.      1
    +
     7850.      1            warn("weird_expression_a", thing);
    +
     7851.      1        }
    +
     7852.      1        if (is_weird(thing.expression[1])) {
    +
     7853.      1
    +
     7854.      1// test_cause:
    +
     7855.      1// ["aa[[0]]", "post_b_lbracket", "weird_expression_a", "[", 4]
    +
     7856.      1
    +
     7857.      1            warn("weird_expression_a", thing.expression[1]);
    +
     7858.      1        }
    +
     7859.   1460    }
    +
     7860.    518
    +
     7861.  10411    function post_b_lparen(thing) {
    +
     7862.  10411        let arg;
    +
     7863.  10411        let array;
    +
     7864.  10411        let cack;
    +
     7865.  10411        let left = thing.expression[0];
    +
     7866.  10411        let new_date;
    +
     7867.  10411        let paren;
    +
     7868.  10411        let the_new;
    +
     7869.    154        if (left.id === "new") {
    +
     7870.    154            the_new = left;
    +
     7871.    154            left = left.expression;
    +
     7872.    154        }
    +
     7873.     37        if (left.id === "function") {
    +
     7874.     37            if (!thing.wrapped) {
    +
     7875.     37
    +
     7876.     37// test_cause:
    +
     7877.     37// ["aa=function(){}()", "post_b_lparen", "wrap_immediate", "(", 16]
    +
     7878.     37
    +
     7879.     37                warn("wrap_immediate", thing);
    +
     7880.     37            }
    +
     7881.  10374        } else if (left.identifier) {
    +
     7882.  10374            if (the_new !== undefined) {
    +
     7883.  10374                if (
    +
     7884.  10374                    left.id[0] > "Z"
    +
     7885.  10374                    || left.id === "BigInt"
    +
     7886.  10374                    || left.id === "Boolean"
    +
     7887.  10374                    || left.id === "Number"
    +
     7888.  10374                    || left.id === "String"
    +
     7889.  10374                    || left.id === "Symbol"
    +
     7890.  10374                ) {
    +
     7891.  10374
    +
     7892.  10374// test_cause:
    +
     7893.  10374// ["new BigInt()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7894.  10374// ["new Boolean()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7895.  10374// ["new Number()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7896.  10374// ["new String()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7897.  10374// ["new Symbol()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7898.  10374// ["new aa()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7899.  10374
    +
     7900.  10374                    warn("unexpected_a", the_new);
    +
     7901.  10374                } else if (left.id === "Function") {
    +
     7902.  10374                    if (!option_dict.eval) {
    +
     7903.  10374
    +
     7904.  10374// test_cause:
    +
     7905.  10374// ["new Function()", "post_b_lparen", "unexpected_a", "new Function", 5]
    +
     7906.  10374
    +
     7907.  10374                        warn("unexpected_a", left, "new Function");
    +
     7908.  10374                    }
    +
     7909.  10374                } else if (left.id === "Array") {
    +
     7910.  10374                    arg = thing.expression;
    +
     7911.  10374                    if (arg.length !== 2 || arg[1].id === "(string)") {
    +
     7912.  10374
    +
     7913.  10374// test_cause:
    +
     7914.  10374// ["new Array()", "post_b_lparen", "expected_a_b", "new Array", 5]
    +
     7915.  10374
    +
     7916.  10374                        warn("expected_a_b", left, "[]", "new Array");
    +
     7917.  10374                    }
    +
     7918.  10374                } else if (left.id === "Object") {
    +
     7919.  10374
    +
     7920.  10374// test_cause:
    +
     7921.  10374// ["new Object()", "post_b_lparen", "expected_a_b", "new Object", 5]
    +
     7922.  10374
    +
     7923.  10374                    warn(
    +
     7924.  10374                        "expected_a_b",
    +
     7925.  10374                        left,
    +
     7926.  10374                        "Object.create(null)",
    +
     7927.  10374                        "new Object"
    +
     7928.  10374                    );
    +
     7929.  10374                }
    +
     7930.  10374            } else {
    +
     7931.  10374                if (
    +
     7932.  10374                    left.id[0] >= "A"
    +
     7933.  10374                    && left.id[0] <= "Z"
    +
     7934.  10374                    && left.id !== "BigInt"
    +
     7935.  10374                    && left.id !== "Boolean"
    +
     7936.  10374                    && left.id !== "Number"
    +
     7937.  10374                    && left.id !== "String"
    +
     7938.  10374                    && left.id !== "Symbol"
    +
     7939.  10374                ) {
    +
     7940.  10374
    +
     7941.  10374// test_cause:
    +
     7942.  10374// ["let Aa=Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8]
    +
     7943.  10374
    +
     7944.  10374                    warn("expected_a_before_b", left, "new", artifact(left));
    +
     7945.  10374                }
    +
     7946.  10374            }
    +
     7947.  10374        } else if (left.id === ".") {
    +
     7948.  10374            cack = the_new !== undefined;
    +
     7949.  10374            if (left.expression.id === "Date" && left.name.id === "UTC") {
    +
     7950.  10374
    +
     7951.  10374// test_cause:
    +
     7952.  10374// ["new Date.UTC()", "post_b_lparen", "cack", "", 0]
    +
     7953.  10374
    +
     7954.  10374                test_cause("cack");
    +
     7955.  10374                cack = !cack;
    +
     7956.  10374            }
    +
     7957.  10374            if (jslint_rgx_cap.test(left.name.id) !== cack) {
    +
     7958.  10374                if (the_new !== undefined) {
    +
     7959.  10374
    +
     7960.  10374// test_cause:
    +
     7961.  10374// ["new Date.UTC()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7962.  10374
    +
     7963.  10374                    warn("unexpected_a", the_new);
    +
     7964.  10374                } else {
    +
     7965.  10374
    +
     7966.  10374// test_cause:
    +
     7967.  10374// ["let Aa=Aa.Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8]
    +
     7968.  10374
    +
     7969.  10374                    warn(
    +
     7970.  10374                        "expected_a_before_b",
    +
     7971.  10374                        left.expression,
    +
     7972.  10374                        "new",
    +
     7973.  10374                        left.name.id
    +
     7974.  10374                    );
    +
     7975.  10374                }
    +
     7976.  10374            }
    +
     7977.  10374            if (left.name.id === "getTime") {
    +
     7978.  10374                paren = left.expression;
    +
     7979.  10374                if (paren.id === "(") {
    +
     7980.  10374                    array = paren.expression;
    +
     7981.  10374                    if (array.length === 1) {
    +
     7982.  10374                        new_date = array[0];
    +
     7983.  10374                        if (
    +
     7984.  10374                            new_date.id === "new"
    +
     7985.  10374                            && new_date.expression.id === "Date"
    +
     7986.  10374                        ) {
    +
     7987.  10374
    +
     7988.  10374// test_cause:
    +
     7989.  10374// ["
    +
     7990.  10374// new Date().getTime()
    +
     7991.  10374// ", "post_b_lparen", "expected_a_b", "new Date().getTime()", 1]
    +
     7992.  10374
    +
     7993.  10374                            warn(
    +
     7994.  10374                                "expected_a_b",
    +
     7995.  10374                                new_date,
    +
     7996.  10374                                "Date.now()",
    +
     7997.  10374                                "new Date().getTime()"
    +
     7998.  10374                            );
    +
     7999.  10374                        }
    +
     8000.  10374                    }
    +
     8001.  10374                }
    +
     8002.  10374            }
    +
     8003.  10374        }
    +
     8004.  10411    }
    +
     8005.    518
    +
     8006.    974    function post_b_or(thing) {
    +
     8007.    974        if (
    +
     8008.    974            is_weird(thing.expression[0])
    +
     8009.    974            || is_equal(thing.expression[0], thing.expression[1])
    +
     8010.    973            || thing.expression[0].constant === true
    +
     8011.      2        ) {
    +
     8012.      2
    +
     8013.      2// test_cause:
    +
     8014.      2// ["aa=0||0", "post_b_or", "weird_condition_a", "||", 5]
    +
     8015.      2
    +
     8016.      2            warn("weird_condition_a", thing);
    +
     8017.      2        }
    +
     8018.    974    }
    +
     8019.    518
    +
     8020.     78    function post_s_export(the_thing) {
    +
     8021.     78
    +
     8022.     78// Some features must be at the most outermost level.
    +
     8023.     78
    +
     8024.      1        if (blockage !== token_global) {
    +
     8025.      1
    +
     8026.      1// test_cause:
    +
     8027.      1// ["
    +
     8028.      1// if(0){import aa from "aa";}
    +
     8029.      1// ", "post_s_export", "misplaced_a", "import", 7]
    +
     8030.      1
    +
     8031.      1            warn("misplaced_a", the_thing);
    +
     8032.      1        }
    +
     8033.     78    }
    +
     8034.    518
    +
     8035.      8    function post_s_for(thing) {
    +
     8036.      8
    +
     8037.      8// Recurse walk_statement().
    +
     8038.      8
    +
     8039.      8        walk_statement(thing.inc);
    +
     8040.      8    }
    +
     8041.    518
    +
     8042.   2001    function post_s_function(thing) {
    +
     8043.   2001        delete functionage.async;
    +
     8044.   2001        delete functionage.finally;
    +
     8045.   2001        delete functionage.loop;
    +
     8046.   2001        delete functionage.statement_prv;
    +
     8047.   2001        delete functionage.switch;
    +
     8048.   2001        delete functionage.try;
    +
     8049.   2001        functionage = function_stack.pop();
    +
     8050.      3        if (thing.wrapped) {
    +
     8051.      3
    +
     8052.      3// test_cause:
    +
     8053.      3// ["aa=(function(){})", "post_s_function", "unexpected_parens", "function", 5]
    +
     8054.      3
    +
     8055.      3            warn("unexpected_parens", thing);
    +
     8056.      3        }
    +
     8057.   2001        return post_s_lbrace();
    +
     8058.   2001    }
    +
     8059.    518
    +
     8060.     61    function post_s_import(the_thing) {
    +
     8061.     61        const name = the_thing.name;
    +
     8062.     60        if (name) {
    +
     8063.     60            if (Array.isArray(name)) {
    +
     8064.     60                name.forEach(function (name) {
    +
     8065.     60                    name.dead = false;
    +
     8066.     60                    name.init = true;
    +
     8067.     60                    blockage.live.push(name);
    +
     8068.     60                });
    +
     8069.     60            } else {
    +
     8070.     60                name.dead = false;
    +
     8071.     60                name.init = true;
    +
     8072.     60                blockage.live.push(name);
    +
     8073.     60            }
    +
     8074.     60            return post_s_export(the_thing);
    +
     8075.     60        }
    +
     8076.     61    }
    +
     8077.    518
    +
     8078.   7695    function post_s_lbrace() {
    +
     8079.   2987        blockage.live.forEach(function (name) {
    +
     8080.   2987            name.dead = true;
    +
     8081.   2987        });
    +
     8082.   7695        delete blockage.live;
    +
     8083.   7695        blockage = block_stack.pop();
    +
     8084.   7695    }
    +
     8085.    518
    +
     8086.     56    function post_s_try(thing) {
    +
     8087.     54        if (thing.catch) {
    +
     8088.     54            if (thing.catch.name) {
    +
     8089.     54                Object.assign(catchage.context[thing.catch.name.id], {
    +
     8090.     54                    dead: false,
    +
     8091.     54                    init: true
    +
     8092.     54                });
    +
     8093.     54            }
    +
     8094.     54
    +
     8095.     54// Recurse walk_statement().
    +
     8096.     54
    +
     8097.     54            walk_statement(thing.catch.block);
    +
     8098.     54
    +
     8099.     54// Restore previous catch-scope after catch-block.
    +
     8100.     54
    +
     8101.     54            catchage = catch_stack.pop();
    +
     8102.     54        }
    +
     8103.     56    }
    +
     8104.    518
    +
     8105.   2320    function post_s_var(thing) {
    +
     8106.   2635        thing.names.forEach(function (name) {
    +
     8107.   2635            name.dead = false;
    +
     8108.   1250            if (name.expression !== undefined) {
    +
     8109.   1250                walk_expression(name.expression);
    +
     8110.   1250
    +
     8111.   1250// Probably deadcode.
    +
     8112.   1250// if (name.id === "{" || name.id === "[") {
    +
     8113.   1250//     name.names.forEach(subactivate);
    +
     8114.   1250// } else {
    +
     8115.   1250//     name.init = true;
    +
     8116.   1250// }
    +
     8117.   1250
    +
     8118.   1250                jslint_assert(
    +
     8119.   1250                    !(name.id === "{" || name.id === "["),
    +
     8120.   1250                    `Expected !(name.id === "{" || name.id === "[").`
    +
     8121.   1250                );
    +
     8122.   1250                name.init = true;
    +
     8123.   1250            }
    +
     8124.   2635            blockage.live.push(name);
    +
     8125.   2635        });
    +
     8126.   2320    }
    +
     8127.    518
    +
     8128.    213    function post_t(thing) {
    +
     8129.    213        if (
    +
     8130.    213            is_weird(thing.expression[0])
    +
     8131.    213            || thing.expression[0].constant === true
    +
     8132.    206            || is_equal(thing.expression[1], thing.expression[2])
    +
     8133.      9        ) {
    +
     8134.      9
    +
     8135.      9// test_cause:
    +
     8136.      9// ["let aa=(aa?`${0}`:`${0}`);", "post_t", "unexpected_a", "?", 11]
    +
     8137.      9// ["let aa=(aa?`0`:`0`);", "post_t", "unexpected_a", "?", 11]
    +
     8138.      9
    +
     8139.      9            warn("unexpected_a", thing);
    +
     8140.    204        } else if (is_equal(thing.expression[0], thing.expression[1])) {
    +
     8141.    204
    +
     8142.    204// test_cause:
    +
     8143.    204// ["aa?aa:0", "post_t", "expected_a_b", "?", 3]
    +
     8144.    204
    +
     8145.    204            warn("expected_a_b", thing, "||", "?");
    +
     8146.    204        } else if (is_equal(thing.expression[0], thing.expression[2])) {
    +
     8147.    204
    +
     8148.    204// test_cause:
    +
     8149.    204// ["aa?0:aa", "post_t", "expected_a_b", "?", 3]
    +
     8150.    204
    +
     8151.    204            warn("expected_a_b", thing, "&&", "?");
    +
     8152.    204        } else if (
    +
     8153.    204            thing.expression[1].id === "true"
    +
     8154.    204            && thing.expression[2].id === "false"
    +
     8155.    204        ) {
    +
     8156.    204
    +
     8157.    204// test_cause:
    +
     8158.    204// ["aa?true:false", "post_t", "expected_a_b", "?", 3]
    +
     8159.    204
    +
     8160.    204            warn("expected_a_b", thing, "!!", "?");
    +
     8161.    204        } else if (
    +
     8162.    204            thing.expression[1].id === "false"
    +
     8163.    204            && thing.expression[2].id === "true"
    +
     8164.    204        ) {
    +
     8165.    204
    +
     8166.    204// test_cause:
    +
     8167.    204// ["aa?false:true", "post_t", "expected_a_b", "?", 3]
    +
     8168.    204
    +
     8169.    204            warn("expected_a_b", thing, "!", "?");
    +
     8170.    204        } else if (
    +
     8171.    204            thing.expression[0].wrapped !== true
    +
     8172.    204            && (
    +
     8173.    204                thing.expression[0].id === "||"
    +
     8174.    204                || thing.expression[0].id === "&&"
    +
     8175.    204            )
    +
     8176.    204        ) {
    +
     8177.    204
    +
     8178.    204// test_cause:
    +
     8179.    204// ["(aa&&!aa?0:1)", "post_t", "wrap_condition", "&&", 4]
    +
     8180.    204
    +
     8181.    204            warn("wrap_condition", thing.expression[0]);
    +
     8182.    204        }
    +
     8183.    213    }
    +
     8184.    518
    +
     8185.   4106    function post_u(thing) {
    +
     8186.    779        if (thing.id === "`") {
    +
     8187.    779            if (thing.expression.every(function (thing) {
    +
     8188.    779                return thing.constant;
    +
     8189.    779            })) {
    +
     8190.    779                thing.constant = true;
    +
     8191.    779            }
    +
     8192.   3327        } else if (thing.id === "!") {
    +
     8193.   3327            if (thing.expression.constant === true) {
    +
     8194.   3327                warn("unexpected_a", thing);
    +
     8195.   3327            }
    +
     8196.   3327        } else if (thing.id === "!!") {
    +
     8197.   3327            if (!option_dict.convert) {
    +
     8198.   3327
    +
     8199.   3327// test_cause:
    +
     8200.   3327// ["!!0", "post_u", "expected_a_b", "!!", 1]
    +
     8201.   3327
    +
     8202.   3327                warn("expected_a_b", thing, "Boolean(...)", "!!");
    +
     8203.   3327            }
    +
     8204.   3327        } else if (
    +
     8205.   3327            thing.id !== "["
    +
     8206.   3327            && thing.id !== "{"
    +
     8207.   3327            && thing.id !== "function"
    +
     8208.   3327            && thing.id !== "new"
    +
     8209.   3327        ) {
    +
     8210.   3327            if (thing.expression.constant === true) {
    +
     8211.   3327                thing.constant = true;
    +
     8212.   3327            }
    +
     8213.   3327        }
    +
     8214.   4106    }
    +
     8215.    518
    +
     8216.      7    function post_u_plus(thing) {
    +
     8217.      7        const right = thing.expression;
    +
     8218.      7        if (!option_dict.convert) {
    +
     8219.      7
    +
     8220.      7// test_cause:
    +
     8221.      7// ["aa=+0", "post_u_plus", "expected_a_b", "+", 4]
    +
     8222.      7
    +
     8223.      7            warn("expected_a_b", thing, "Number(...)", "+");
    +
     8224.      7        }
    +
     8225.      1        if (right.id === "(" && right.expression[0].id === "new") {
    +
     8226.      1            warn("unexpected_a_before_b", thing, "+", "new");
    +
     8227.      6        } else if (
    +
     8228.      6            right.constant
    +
     8229.      6            || right.id === "{"
    +
     8230.      6            || (right.id === "[" && right.arity !== "binary")
    +
     8231.      6        ) {
    +
     8232.      6            warn("unexpected_a", thing, "+");
    +
     8233.      6        }
    +
     8234.      7    }
    +
     8235.    518
    +
     8236.  36945    function pre_a_bitwise(thing) {
    +
     8237.  36945
    +
     8238.  36945// These are the bitwise operators.
    +
     8239.  36945
    +
     8240.  36943        switch (!option_dict.bitwise && thing.id) {
    +
     8241.      2        case "&":
    +
     8242.      3        case "&=":
    +
     8243.      5        case "<<":
    +
     8244.      6        case "<<=":
    +
     8245.      8        case ">>":
    +
     8246.      9        case ">>=":
    +
     8247.     11        case ">>>":
    +
     8248.     12        case ">>>=":
    +
     8249.     14        case "^":
    +
     8250.     15        case "^=":
    +
     8251.     17        case "|":
    +
     8252.     18        case "|=":
    +
     8253.     21        case "~":
    +
     8254.     21
    +
     8255.     21// test_cause:
    +
     8256.     21// ["0&0", "pre_a_bitwise", "unexpected_a", "&", 2]
    +
     8257.     21// ["0&=0", "pre_a_bitwise", "unexpected_a", "&=", 2]
    +
     8258.     21// ["0<<0", "pre_a_bitwise", "unexpected_a", "<<", 2]
    +
     8259.     21// ["0<<=0", "pre_a_bitwise", "unexpected_a", "<<=", 2]
    +
     8260.     21// ["0>>0", "pre_a_bitwise", "unexpected_a", ">>", 2]
    +
     8261.     21// ["0>>=0", "pre_a_bitwise", "unexpected_a", ">>=", 2]
    +
     8262.     21// ["0>>>0", "pre_a_bitwise", "unexpected_a", ">>>", 2]
    +
     8263.     21// ["0>>>=0", "pre_a_bitwise", "unexpected_a", ">>>=", 2]
    +
     8264.     21// ["0^0", "pre_a_bitwise", "unexpected_a", "^", 2]
    +
     8265.     21// ["0^=0", "pre_a_bitwise", "unexpected_a", "^=", 2]
    +
     8266.     21// ["0|0", "pre_a_bitwise", "unexpected_a", "|", 2]
    +
     8267.     21// ["0|=0", "pre_a_bitwise", "unexpected_a", "|=", 2]
    +
     8268.     21// ["~0", "pre_a_bitwise", "unexpected_a", "~", 1]
    +
     8269.     21
    +
     8270.     21            warn("unexpected_a", thing);
    +
     8271.     21            break;
    +
     8272.  36945        }
    +
     8273.  36945        if (
    +
     8274.  36945            thing.id !== "("
    +
     8275.  26534            && thing.id !== "&&"
    +
     8276.  25332            && thing.id !== "||"
    +
     8277.  24358            && thing.id !== "="
    +
     8278.  20124            && Array.isArray(thing.expression)
    +
     8279.   8506            && thing.expression.length === 2
    +
     8280.   8505            && (
    +
     8281.   8505                relationop[thing.expression[0].id] === true
    +
     8282.   8505                || relationop[thing.expression[1].id] === true
    +
     8283.   8505            )
    +
     8284.      1        ) {
    +
     8285.      1
    +
     8286.      1// test_cause:
    +
     8287.      1// ["0<0<0", "pre_a_bitwise", "unexpected_a", "<", 4]
    +
     8288.      1
    +
     8289.      1            warn("unexpected_a", thing);
    +
     8290.      1        }
    +
     8291.  36945    }
    +
     8292.    518
    +
     8293.  31933    function pre_b(thing) {
    +
     8294.  31933        let left;
    +
     8295.  31933        let right;
    +
     8296.  31933        let value;
    +
     8297.   3960        if (relationop[thing.id] === true) {
    +
     8298.   3960            left = thing.expression[0];
    +
     8299.   3960            right = thing.expression[1];
    +
     8300.   3960            if (left.id === "NaN" || right.id === "NaN") {
    +
     8301.   3960
    +
     8302.   3960// test_cause:
    +
     8303.   3960// ["NaN===NaN", "pre_b", "number_isNaN", "===", 4]
    +
     8304.   3960
    +
     8305.   3960                warn("number_isNaN", thing);
    +
     8306.   3960            } else if (left.id === "typeof") {
    +
     8307.   3960                if (right.id !== "(string)") {
    +
     8308.   3960                    if (right.id !== "typeof") {
    +
     8309.   3960
    +
     8310.   3960// test_cause:
    +
     8311.   3960// ["typeof 0===0", "pre_b", "expected_string_a", "0", 12]
    +
     8312.   3960
    +
     8313.   3960                        warn("expected_string_a", right);
    +
     8314.   3960                    }
    +
     8315.   3960                } else {
    +
     8316.   3960                    value = right.value;
    +
     8317.   3960                    if (value === "null" || value === "undefined") {
    +
     8318.   3960
    +
     8319.   3960// test_cause:
    +
     8320.   3960// ["
    +
     8321.   3960// typeof aa==="undefined"
    +
     8322.   3960// ", "pre_b", "unexpected_typeof_a", "undefined", 13]
    +
     8323.   3960
    +
     8324.   3960                        warn("unexpected_typeof_a", right, value);
    +
     8325.   3960                    } else if (
    +
     8326.   3960                        value !== "bigint"
    +
     8327.   3960                        && value !== "boolean"
    +
     8328.   3960                        && value !== "function"
    +
     8329.   3960                        && value !== "number"
    +
     8330.   3960                        && value !== "object"
    +
     8331.   3960                        && value !== "string"
    +
     8332.   3960                        && value !== "symbol"
    +
     8333.   3960                    ) {
    +
     8334.   3960
    +
     8335.   3960// test_cause:
    +
     8336.   3960// ["typeof 0===\"aa\"", "pre_b", "expected_type_string_a", "aa", 12]
    +
     8337.   3960
    +
     8338.   3960                        warn("expected_type_string_a", right, value);
    +
     8339.   3960                    }
    +
     8340.   3960                }
    +
     8341.   3960            }
    +
     8342.   3960        }
    +
     8343.  31933    }
    +
     8344.    518
    +
     8345.      1    function pre_b_eqeq(thing) {
    +
     8346.      1
    +
     8347.      1// test_cause:
    +
     8348.      1// ["0==0", "pre_b_eqeq", "expected_a_b", "==", 2]
    +
     8349.      1
    +
     8350.      1        warn("expected_a_b", thing, "===", "==");
    +
     8351.      1    }
    +
     8352.    518
    +
     8353.      1    function pre_b_in(thing) {
    +
     8354.      1
    +
     8355.      1// test_cause:
    +
     8356.      1// ["aa in aa", "pre_b_in", "infix_in", "in", 4]
    +
     8357.      1
    +
     8358.      1        warn("infix_in", thing);
    +
     8359.      1    }
    +
     8360.    518
    +
     8361.      1    function pre_b_instanceof(thing) {
    +
     8362.      1
    +
     8363.      1// test_cause:
    +
     8364.      1// ["0 instanceof 0", "pre_b_instanceof", "unexpected_a", "instanceof", 3]
    +
     8365.      1
    +
     8366.      1        warn("unexpected_a", thing);
    +
     8367.      1    }
    +
     8368.    518
    +
     8369.  10411    function pre_b_lparen(thing) {
    +
     8370.  10411        const left = thing.expression[0];
    +
     8371.  10411        let left_variable;
    +
     8372.  10411        let parent;
    +
     8373.  10411        if (
    +
     8374.  10411            left.identifier
    +
     8375.   6658            && functionage.context[left.id] === undefined
    +
     8376.   3034            && typeof functionage.name === "object"
    +
     8377.   2395        ) {
    +
     8378.   2395            parent = functionage.name.parent;
    +
     8379.   2395            if (parent) {
    +
     8380.   2395                left_variable = parent.context[left.id];
    +
     8381.   2395                if (
    +
     8382.   2395                    left_variable !== undefined
    +
     8383.   2395
    +
     8384.   2395// Probably deadcode.
    +
     8385.   2395// && left_variable.dead
    +
     8386.   2395
    +
     8387.   2395                    && left_variable.parent === parent
    +
     8388.   2395                    && left_variable.calls !== undefined
    +
     8389.   2395                    && left_variable.calls[functionage.name.id] !== undefined
    +
     8390.   2395                ) {
    +
     8391.   2395                    left_variable.dead = false;
    +
     8392.   2395                }
    +
     8393.   2395            }
    +
     8394.   2395        }
    +
     8395.  10411    }
    +
     8396.    518
    +
     8397.      1    function pre_b_noteq(thing) {
    +
     8398.      1
    +
     8399.      1// test_cause:
    +
     8400.      1// ["0!=0", "pre_b_noteq", "expected_a_b", "!=", 2]
    +
     8401.      1
    +
     8402.      1        warn("expected_a_b", thing, "!==", "!=");
    +
     8403.      1    }
    +
     8404.    518
    +
     8405.    974    function pre_b_or(thing) {
    +
     8406.   1948        thing.expression.forEach(function (thang) {
    +
     8407.    177            if (thang.id === "&&" && !thang.wrapped) {
    +
     8408.      1
    +
     8409.      1// test_cause:
    +
     8410.      1// ["0&&0||0", "pre_b_or", "and", "&&", 2]
    +
     8411.      1
    +
     8412.      1                warn("and", thang);
    +
     8413.      1            }
    +
     8414.   1948        });
    +
     8415.    974    }
    +
     8416.    518
    +
     8417.      8    function pre_s_for(thing) {
    +
     8418.      8        let the_variable;
    +
     8419.      2        if (thing.name !== undefined) {
    +
     8420.      2            thing.name.dead = false;
    +
     8421.      2            the_variable = lookup(thing.name);
    +
     8422.      2            if (the_variable !== undefined) {
    +
     8423.      2                if (the_variable.init && the_variable.readonly) {
    +
     8424.      2
    +
     8425.      2// test_cause:
    +
     8426.      2// ["const aa=0;for(aa in aa){}", "pre_s_for", "bad_assignment_a", "aa", 16]
    +
     8427.      2
    +
     8428.      2                    warn("bad_assignment_a", thing.name);
    +
     8429.      2                }
    +
     8430.      2                the_variable.init = true;
    +
     8431.      2            }
    +
     8432.      2        }
    +
     8433.      8
    +
     8434.      8// Recurse walk_statement().
    +
     8435.      8
    +
     8436.      8        walk_statement(thing.initial);
    +
     8437.      8    }
    +
     8438.    518
    +
     8439.   2001    function pre_s_function(thing) {
    +
     8440.   2001
    +
     8441.   2001// test_cause:
    +
     8442.   2001// ["()=>0", "pre_s_function", "", "", 0]
    +
     8443.   2001// ["(function (){}())", "pre_s_function", "", "", 0]
    +
     8444.   2001// ["function aa(){}", "pre_s_function", "", "", 0]
    +
     8445.   2001
    +
     8446.   2001        test_cause("");
    +
     8447.   1057        if (thing.arity === "statement" && blockage.body !== true) {
    +
     8448.      1
    +
     8449.      1// test_cause:
    +
     8450.      1// ["if(0){function aa(){}\n}", "pre_s_function", "unexpected_a", "function", 7]
    +
     8451.      1
    +
     8452.      1            warn("unexpected_a", thing);
    +
     8453.      1        }
    +
     8454.   2001        function_stack.push(functionage);
    +
     8455.   2001        block_stack.push(blockage);
    +
     8456.   2001        functionage = thing;
    +
     8457.   2001        blockage = thing;
    +
     8458.   2001        thing.live = [];
    +
     8459.   1098        if (typeof thing.name === "object") {
    +
     8460.   1098            thing.name.dead = false;
    +
     8461.   1098            thing.name.init = true;
    +
     8462.   1098        }
    +
     8463.      7        if (thing.extra === "get") {
    +
     8464.      7            if (thing.parameters.length !== 0) {
    +
     8465.      7
    +
     8466.      7// test_cause:
    +
     8467.      7// ["
    +
     8468.      7// /*jslint getset*/
    +
     8469.      7// aa={get aa(aa){}}
    +
     8470.      7// ", "pre_s_function", "bad_get", "function", 9]
    +
     8471.      7
    +
     8472.      7                warn("bad_get", thing);
    +
     8473.      7            }
    +
     8474.   1994        } else if (thing.extra === "set") {
    +
     8475.   1994            if (thing.parameters.length !== 1) {
    +
     8476.   1994
    +
     8477.   1994// test_cause:
    +
     8478.   1994// ["
    +
     8479.   1994// /*jslint getset*/
    +
     8480.   1994// aa={set aa(){}}
    +
     8481.   1994// ", "pre_s_function", "bad_set", "function", 9]
    +
     8482.   1994
    +
     8483.   1994                warn("bad_set", thing);
    +
     8484.   1994            }
    +
     8485.   1994        }
    +
     8486.   2063        thing.parameters.forEach(function (name) {
    +
     8487.   2063            walk_expression(name.expression);
    +
     8488.   1840            if (name.id === "{" || name.id === "[") {
    +
     8489.    267                name.names.forEach(subactivate);
    +
     8490.   1796            } else {
    +
     8491.   1796                name.dead = false;
    +
     8492.   1796                name.init = true;
    +
     8493.   1796            }
    +
     8494.   2063        });
    +
     8495.   2001    }
    +
     8496.    518
    +
     8497.   5694    function pre_s_lbrace(thing) {
    +
     8498.   5694        block_stack.push(blockage);
    +
     8499.   5694        blockage = thing;
    +
     8500.   5694        thing.live = [];
    +
     8501.   5694    }
    +
     8502.    518
    +
     8503.     56    function pre_try(thing) {
    +
     8504.     54        if (thing.catch !== undefined) {
    +
     8505.     54
    +
     8506.     54// Create new catch-scope for catch-parameter.
    +
     8507.     54
    +
     8508.     54            catch_stack.push(catchage);
    +
     8509.     54            catchage = thing.catch;
    +
     8510.     54        }
    +
     8511.     56    }
    +
     8512.    518
    +
     8513.  28689    function pre_v(thing) {
    +
     8514.  28689        const the_variable = lookup(thing);
    +
     8515.  28611        if (the_variable !== undefined) {
    +
     8516.  28611            thing.variable = the_variable;
    +
     8517.  28611            the_variable.used += 1;
    +
     8518.  28611        }
    +
     8519.  28689    }
    +
     8520.    518
    +
     8521.    717    function subactivate(name) {
    +
     8522.    717        name.init = true;
    +
     8523.    717        name.dead = false;
    +
     8524.    717        blockage.live.push(name);
    +
     8525.    717    }
    +
     8526.    518
    +
     8527. 164534    function walk_expression(thing) {
    +
     8528. 103787        if (thing) {
    +
     8529. 103787            if (Array.isArray(thing)) {
    +
     8530. 103787
    +
     8531. 103787// test_cause:
    +
     8532. 103787// ["(function(){}())", "walk_expression", "isArray", "", 0]
    +
     8533. 103787// ["0&&0", "walk_expression", "isArray", "", 0]
    +
     8534. 103787
    +
     8535. 103787                test_cause("isArray");
    +
     8536. 103787                thing.forEach(walk_expression);
    +
     8537. 103787            } else {
    +
     8538. 103787                preamble(thing);
    +
     8539. 103787                walk_expression(thing.expression);
    +
     8540. 103787
    +
     8541. 103787// PR-414 - Bugfix - fix fart-body not being walked.
    +
     8542. 103787
    +
     8543. 103787                if (thing.id === "function" || thing.id === "=>") {
    +
     8544. 103787
    +
     8545. 103787// test_cause:
    +
     8546. 103787// ["aa=()=>0", "walk_expression", "function", "=>", 0]
    +
     8547. 103787// ["aa=function(){}", "walk_expression", "function", "function", 0]
    +
     8548. 103787
    +
     8549. 103787                    test_cause("function", thing.id);
    +
     8550. 103787
    +
     8551. 103787// Recurse walk_statement().
    +
     8552. 103787
    +
     8553. 103787                    walk_statement(thing.block);
    +
     8554. 103787                }
    +
     8555. 103787                if (
    +
     8556. 103787                    thing.arity === "preassign" || thing.arity === "postassign"
    +
     8557. 103787                ) {
    +
     8558. 103787
    +
     8559. 103787// test_cause:
    +
     8560. 103787// ["aa=++aa", "walk_expression", "unexpected_a", "++", 4]
    +
     8561. 103787// ["aa=--aa", "walk_expression", "unexpected_a", "--", 4]
    +
     8562. 103787
    +
     8563. 103787                    warn("unexpected_a", thing);
    +
     8564. 103787                } else if (
    +
     8565. 103787                    thing.arity === "statement"
    +
     8566. 103787                    || thing.arity === "assignment"
    +
     8567. 103787                ) {
    +
     8568. 103787
    +
     8569. 103787// test_cause:
    +
     8570. 103787// ["aa[aa=0]", "walk_expression", "unexpected_statement_a", "=", 6]
    +
     8571. 103787
    +
     8572. 103787                    warn("unexpected_statement_a", thing);
    +
     8573. 103787                }
    +
     8574. 103787
    +
     8575. 103787// test_cause:
    +
     8576. 103787// ["aa=0", "walk_expression", "default", "", 0]
    +
     8577. 103787
    +
     8578. 103787                test_cause("default");
    +
     8579. 103787                postamble(thing);
    +
     8580. 103787            }
    +
     8581. 103787        }
    +
     8582. 164534    }
    +
     8583.    518
    +
     8584.  78303    function walk_statement(thing) {
    +
     8585.  43095        if (!thing) {
    +
     8586.  43095            return;
    +
     8587.  43095        }
    +
     8588.  35208        if (Array.isArray(thing)) {
    +
     8589.   7608
    +
     8590.   7608// test_cause:
    +
     8591.   7608// ["+[]", "walk_statement", "isArray", "", 0]
    +
     8592.   7608
    +
     8593.   7608            test_cause("isArray");
    +
     8594.   7608
    +
     8595.   7608// Recurse walk_statement().
    +
     8596.   7608
    +
     8597.   7608            thing.forEach(walk_statement);
    +
     8598.   7608            return;
    +
     8599.  27600        }
    +
     8600.  27600        preamble(thing);
    +
     8601.  27600        walk_expression(thing.expression);
    +
     8602.  27600        if (thing.arity === "binary") {
    +
     8603.   5665            if (thing.id !== "(") {
    +
     8604.   5665
    +
     8605.   5665// test_cause:
    +
     8606.   5665// ["0&&0", "walk_statement", "unexpected_expression_a", "&&", 2]
    +
     8607.   5665
    +
     8608.   5665                warn("unexpected_expression_a", thing);
    +
     8609.   5665            }
    +
     8610.  21935        } else if (
    +
     8611.  21935            thing.arity !== "statement"
    +
     8612.  21935            && thing.arity !== "assignment"
    +
     8613.  21935            && thing.id !== "import"
    +
     8614.  21935        ) {
    +
     8615.  21935
    +
     8616.  21935// test_cause:
    +
     8617.  21935// ["!0", "walk_statement", "unexpected_expression_a", "!", 1]
    +
     8618.  21935// ["+[]", "walk_statement", "unexpected_expression_a", "+", 1]
    +
     8619.  21935// ["+new aa()", "walk_statement", "unexpected_expression_a", "+", 1]
    +
     8620.  21935// ["0", "walk_statement", "unexpected_expression_a", "0", 1]
    +
     8621.  21935// ["typeof 0", "walk_statement", "unexpected_expression_a", "typeof", 1]
    +
     8622.  21935
    +
     8623.  21935            warn("unexpected_expression_a", thing);
    +
     8624.  27600        }
    +
     8625.  27600
    +
     8626.  27600// Recurse walk_statement().
    +
     8627.  27600
    +
     8628.  27600        walk_statement(thing.block);
    +
     8629.  27600        walk_statement(thing.else);
    +
     8630.  27600        postamble(thing);
    +
     8631.  27600    }
    +
     8632.    518
    +
     8633.    518    postaction = action(posts);
    +
     8634.    518    postamble = amble(posts);
    +
     8635.    518    preaction = action(pres);
    +
     8636.    518    preamble = amble(pres);
    +
     8637.    518    postaction("assignment", "+=", post_a_pluseq);
    +
     8638.    518    postaction("assignment", post_a);
    +
     8639.    518    postaction("binary", "&&", post_b_and);
    +
     8640.    518    postaction("binary", "(", post_b_lparen);
    +
     8641.    518    postaction("binary", "=>", post_s_function);
    +
     8642.    518    postaction("binary", "[", post_b_lbracket);
    +
     8643.    518    postaction("binary", "||", post_b_or);
    +
     8644.    518    postaction("binary", post_b);
    +
     8645.    518    postaction("statement", "const", post_s_var);
    +
     8646.    518    postaction("statement", "export", post_s_export);
    +
     8647.    518    postaction("statement", "for", post_s_for);
    +
     8648.    518    postaction("statement", "function", post_s_function);
    +
     8649.    518    postaction("statement", "import", post_s_import);
    +
     8650.    518    postaction("statement", "let", post_s_var);
    +
     8651.    518    postaction("statement", "try", post_s_try);
    +
     8652.    518    postaction("statement", "var", post_s_var);
    +
     8653.    518    postaction("statement", "{", post_s_lbrace);
    +
     8654.    518    postaction("ternary", post_t);
    +
     8655.    518    postaction("unary", "+", post_u_plus);
    +
     8656.    518    postaction("unary", "function", post_s_function);
    +
     8657.    518    postaction("unary", post_u);
    +
     8658.    518    preaction("assignment", pre_a_bitwise);
    +
     8659.    518    preaction("binary", "!=", pre_b_noteq);
    +
     8660.    518    preaction("binary", "(", pre_b_lparen);
    +
     8661.    518    preaction("binary", "==", pre_b_eqeq);
    +
     8662.    518    preaction("binary", "=>", pre_s_function);
    +
     8663.    518    preaction("binary", "in", pre_b_in);
    +
     8664.    518    preaction("binary", "instanceof", pre_b_instanceof);
    +
     8665.    518    preaction("binary", "||", pre_b_or);
    +
     8666.    518    preaction("binary", pre_b);
    +
     8667.    518    preaction("binary", pre_a_bitwise);
    +
     8668.    518    preaction("statement", "for", pre_s_for);
    +
     8669.    518    preaction("statement", "function", pre_s_function);
    +
     8670.    518    preaction("statement", "try", pre_try);
    +
     8671.    518    preaction("statement", "{", pre_s_lbrace);
    +
     8672.    518    preaction("unary", "function", pre_s_function);
    +
     8673.    518    preaction("unary", "~", pre_a_bitwise);
    +
     8674.    518    preaction("variable", pre_v);
    +
     8675.    518
    +
     8676.    518    walk_statement(state.token_tree);
    +
     8677.    518}
    +
     8678.      1
    +
     8679.    208function jslint_phase5_whitage(state) {
    +
     8680.    208
    +
     8681.    208// PHASE 5. Check whitespace between tokens in <token_list>.
    +
     8682.    208
    +
     8683.    208    let {
    +
     8684.    208        artifact,
    +
     8685.    208        catch_list,
    +
     8686.    208        function_list,
    +
     8687.    208        function_stack,
    +
     8688.    208        option_dict,
    +
     8689.    208        test_cause,
    +
     8690.    208        token_global,
    +
     8691.    208        token_list,
    +
     8692.    208        warn
    +
     8693.    208    } = state;
    +
     8694.    208    let closer = "(end)";
    +
     8695.    208    let free = false;
    +
     8696.    208
    +
     8697.    208// free = false
    +
     8698.    208
    +
     8699.    208// cause:
    +
     8700.    208// "()=>0"
    +
     8701.    208// "aa()"
    +
     8702.    208// "aa(0,0)"
    +
     8703.    208// "function(){}"
    +
     8704.    208
    +
     8705.    208// free = true
    +
     8706.    208
    +
     8707.    208// cause:
    +
     8708.    208// "(0)"
    +
     8709.    208// "(aa)"
    +
     8710.    208// "aa(0)"
    +
     8711.    208// "do{}while()"
    +
     8712.    208// "for(){}"
    +
     8713.    208// "if(){}"
    +
     8714.    208// "switch(){}"
    +
     8715.    208// "while(){}"
    +
     8716.    208
    +
     8717.    208    let left = token_global;
    +
     8718.    208    let margin = 0;
    +
     8719.    208    let mode_indent = (
    +
     8720.    208
    +
     8721.    208// PR-330 - Allow 2-space indent.
    +
     8722.    208
    +
     8723.    208        option_dict.indent2
    +
     8724.      5        ? 2
    +
     8725.    203        : 4
    +
     8726.    208    );
    +
     8727.    208    let nr_comments_skipped = 0;
    +
     8728.    208    let open = true;
    +
     8729.    208    let opening = true;
    +
     8730.    208    let right;
    +
     8731.    208
    +
     8732.    208// This is the set of infix operators that require a space on each side.
    +
     8733.    208
    +
     8734.    208    let spaceop = object_assign_from_list(empty(), [
    +
     8735.    208        "!=", "!==", "%", "%=", "&", "&&", "&=", "*", "*=", "+=", "-=", "/",
    +
     8736.    208        "/=", "<", "<<", "<<=", "<=", "=", "==", "===", "=>", ">", ">=", ">>",
    +
     8737.    208        ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||"
    +
     8738.    208    ], true);
    +
     8739.    208
    +
     8740.  38092    function at_margin(fit) {
    +
     8741.  38092        const at = margin + fit;
    +
     8742.     21        if (right.from !== at) {
    +
     8743.     21            return expected_at(at);
    +
     8744.     21        }
    +
     8745.  38092    }
    +
     8746.    208
    +
     8747.   2057    function delve(the_function) {
    +
     8748.  13014        Object.keys(the_function.context).forEach(function (id) {
    +
     8749.  13014            const name = the_function.context[id];
    +
     8750.  12979            if (id !== "ignore" && name.parent === the_function) {
    +
     8751.   6286
    +
     8752.   6286// test_cause:
    +
     8753.   6286// ["function aa(aa) {return aa;}", "delve", "id", "", 0]
    +
     8754.   6286
    +
     8755.   6286                test_cause("id");
    +
     8756.   6286                if (
    +
     8757.   6286                    name.used === 0
    +
     8758.   6286
    +
     8759.   6286// Probably deadcode.
    +
     8760.   6286// && (
    +
     8761.   6286//     name.role !== "function"
    +
     8762.   6286//     || name.parent.arity !== "unary"
    +
     8763.   6286// )
    +
     8764.   6286
    +
     8765.   6286                    && jslint_assert(
    +
     8766.   6286                        name.role !== "function",
    +
     8767.   6286                        `Expected name.role !== "function".`
    +
     8768.   6286                    )
    +
     8769.   6286                ) {
    +
     8770.   6286
    +
     8771.   6286// test_cause:
    +
     8772.   6286// ["/*jslint node*/\nlet aa;", "delve", "unused_a", "aa", 5]
    +
     8773.   6286// ["function aa(aa){return;}", "delve", "unused_a", "aa", 13]
    +
     8774.   6286// ["let aa=0;try{aa();}catch(bb){aa();}", "delve", "unused_a", "bb", 26]
    +
     8775.   6286
    +
     8776.   6286                    warn("unused_a", name);
    +
     8777.   6286                } else if (!name.init) {
    +
     8778.   6286
    +
     8779.   6286// test_cause:
    +
     8780.   6286// ["/*jslint node*/\nlet aa;aa();", "delve", "uninitialized_a", "aa", 5]
    +
     8781.   6286
    +
     8782.   6286                    warn("uninitialized_a", name);
    +
     8783.   6286                }
    +
     8784.   6286            }
    +
     8785.  13014        });
    +
     8786.   2057    }
    +
     8787.    208
    +
     8788.     25    function expected_at(at) {
    +
     8789.     25
    +
     8790.     25// Probably deadcode.
    +
     8791.     25// if (right === undefined) {
    +
     8792.     25//     right = token_nxt;
    +
     8793.     25// }
    +
     8794.     25
    +
     8795.     25        jslint_assert(
    +
     8796.     25            !(right === undefined),
    +
     8797.     25            `Expected !(right === undefined).`
    +
     8798.     25        );
    +
     8799.     25        warn(
    +
     8800.     25            "expected_a_at_b_c",
    +
     8801.     25            right,
    +
     8802.     25            artifact(right),
    +
     8803.     25
    +
     8804.     25// Fudge column numbers in warning message.
    +
     8805.     25
    +
     8806.     25            at + jslint_fudge,
    +
     8807.     25            right.from + jslint_fudge
    +
     8808.     25        );
    +
     8809.     25    }
    +
     8810.    208
    +
     8811.   3095    function no_space() {
    +
     8812.   3094        if (left.line === right.line) {
    +
     8813.   3094
    +
     8814.   3094// from:
    +
     8815.   3094// if (left.line === right.line) {
    +
     8816.   3094//     no_space();
    +
     8817.   3094// } else {
    +
     8818.   3094
    +
     8819.   3094            if (left.thru !== right.from && nr_comments_skipped === 0) {
    +
     8820.   3094
    +
     8821.   3094// test_cause:
    +
     8822.   3094// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14]
    +
     8823.   3094
    +
     8824.   3094                warn(
    +
     8825.   3094                    "unexpected_space_a_b",
    +
     8826.   3094                    right,
    +
     8827.   3094                    artifact(left),
    +
     8828.   3094                    artifact(right)
    +
     8829.   3094                );
    +
     8830.   3094            }
    +
     8831.   3094        } else {
    +
     8832.      1
    +
     8833.      1// from:
    +
     8834.      1// } else if (
    +
     8835.      1//     right.arity === "binary"
    +
     8836.      1//     && right.id === "("
    +
     8837.      1//     && free
    +
     8838.      1// ) {
    +
     8839.      1//     no_space();
    +
     8840.      1// } else if (
    +
     8841.      1
    +
     8842.      1// Probably deadcode.
    +
     8843.      1// if (open) {
    +
     8844.      1//     const at = (
    +
     8845.      1//         free
    +
     8846.      1//         ? margin
    +
     8847.      1//         : margin + 8
    +
     8848.      1//     );
    +
     8849.      1//     if (right.from < at) {
    +
     8850.      1//         expected_at(at);
    +
     8851.      1//     }
    +
     8852.      1// } else {
    +
     8853.      1//     if (right.from !== margin + 8) {
    +
     8854.      1//         expected_at(margin + 8);
    +
     8855.      1//     }
    +
     8856.      1// }
    +
     8857.      1
    +
     8858.      1            jslint_assert(open, `Expected open.`);
    +
     8859.      1            jslint_assert(free, `Expected free.`);
    +
     8860.      1            if (right.from < margin) {
    +
     8861.      1
    +
     8862.      1// test_cause:
    +
     8863.      1// ["let aa = aa(\naa\n()\n);", "expected_at", "expected_a_at_b_c", "5", 1]
    +
     8864.      1
    +
     8865.      1                expected_at(margin);
    +
     8866.      1            }
    +
     8867.      1        }
    +
     8868.   3095    }
    +
     8869.    208
    +
     8870.  96059    function no_space_only() {
    +
     8871.  96059        if (
    +
     8872.  96059            left.id !== "(global)"
    +
     8873.  96057            && left.nr + 1 === right.nr
    +
     8874.  96057            && (
    +
     8875.  96057                left.line !== right.line
    +
     8876.  96057                || left.thru !== right.from
    +
     8877.  96057            )
    +
     8878.      5        ) {
    +
     8879.      5            warn(
    +
     8880.      5                "unexpected_space_a_b",
    +
     8881.      5                right,
    +
     8882.      5                artifact(left),
    +
     8883.      5                artifact(right)
    +
     8884.      5            );
    +
     8885.      5        }
    +
     8886.  96059    }
    +
     8887.    208
    +
     8888.  46227    function one_space() {
    +
     8889.  44324        if (left.line === right.line || !open) {
    +
     8890.  44324            if (left.thru + 1 !== right.from && nr_comments_skipped === 0) {
    +
     8891.  44324                warn(
    +
     8892.  44324                    "expected_space_a_b",
    +
     8893.  44324                    right,
    +
     8894.  44324                    artifact(left),
    +
     8895.  44324                    artifact(right)
    +
     8896.  44324                );
    +
     8897.  44324            }
    +
     8898.  44324        } else {
    +
     8899.   1903            if (right.from !== margin) {
    +
     8900.   1903                expected_at(margin);
    +
     8901.   1903            }
    +
     8902.   1903        }
    +
     8903.  46227    }
    +
     8904.    208
    +
     8905.   9344    function one_space_only() {
    +
     8906.      8        if (left.line !== right.line || left.thru + 1 !== right.from) {
    +
     8907.      8            warn("expected_space_a_b", right, artifact(left), artifact(right));
    +
     8908.      8        }
    +
     8909.   9344    }
    +
     8910.    208
    +
     8911.  24667    function pop() {
    +
     8912.  24667        const previous = function_stack.pop();
    +
     8913.  24667        closer = previous.closer;
    +
     8914.  24667        free = previous.free;
    +
     8915.  24667        margin = previous.margin;
    +
     8916.  24667        open = previous.open;
    +
     8917.  24667        opening = previous.opening;
    +
     8918.  24667    }
    +
     8919.    208
    +
     8920.  24667    function push() {
    +
     8921.  24667        function_stack.push({
    +
     8922.  24667            closer,
    +
     8923.  24667            free,
    +
     8924.  24667            margin,
    +
     8925.  24667            open,
    +
     8926.  24667            opening
    +
     8927.  24667        });
    +
     8928.  24667    }
    +
     8929.    208
    +
     8930.    208// uninitialized_and_unused();
    +
     8931.    208// Delve into the functions looking for variables that were not initialized
    +
     8932.    208// or used. If the file imports or exports, then its global object is also
    +
     8933.    208// delved.
    +
     8934.    208
    +
     8935.    174    if (state.mode_module === true || option_dict.node) {
    +
     8936.     51        delve(token_global);
    +
     8937.     51    }
    +
     8938.    208    catch_list.forEach(delve);
    +
     8939.    208    function_list.forEach(delve);
    +
     8940.    208
    +
     8941.      2    if (option_dict.white) {
    +
     8942.      2        return;
    +
     8943.    206    }
    +
     8944.    206
    +
     8945.    206// whitage();
    +
     8946.    206// Go through the token list, looking at usage of whitespace.
    +
     8947.    206
    +
     8948. 207158    token_list.forEach(function whitage(the_token) {
    +
     8949. 207158        right = the_token;
    +
     8950. 195959        if (right.id === "(comment)" || right.id === "(end)") {
    +
     8951.  11407            nr_comments_skipped += 1;
    +
     8952. 195751        } else {
    +
     8953. 195751
    +
     8954. 195751// If left is an opener and right is not the closer, then push the previous
    +
     8955. 195751// state. If the token following the opener is on the next line, then this is
    +
     8956. 195751// an open form. If the tokens are on the same line, then it is a closed form.
    +
     8957. 195751// Open form is more readable, with each item (statement, argument, parameter,
    +
     8958. 195751// etc) starting on its own line. Closed form is more compact. Statement blocks
    +
     8959. 195751// are always in open form.
    +
     8960. 195751
    +
     8961. 195751// The open and close pairs.
    +
     8962. 195751
    +
     8963. 195751            switch (left.id) {
    +
     8964. 195751            case "${":
    +
     8965. 195751            case "(":
    +
     8966. 195751            case "[":
    +
     8967. 195751            case "{":
    +
     8968. 195751
    +
     8969. 195751// test_cause:
    +
     8970. 195751// ["let aa=[];", "whitage", "opener", "", 0]
    +
     8971. 195751// ["let aa=`${0}`;", "whitage", "opener", "", 0]
    +
     8972. 195751// ["let aa=aa();", "whitage", "opener", "", 0]
    +
     8973. 195751// ["let aa={};", "whitage", "opener", "", 0]
    +
     8974. 195751
    +
     8975. 195751                test_cause("opener");
    +
     8976. 195751
    +
     8977. 195751// Probably deadcode.
    +
     8978. 195751// case "${}":
    +
     8979. 195751
    +
     8980. 195751                jslint_assert(
    +
     8981. 195751                    !(left.id + right.id === "${}"),
    +
     8982. 195751                    "Expected !(left.id + right.id === \"${}\")."
    +
     8983. 195751                );
    +
     8984. 195751                switch (left.id + right.id) {
    +
     8985. 195751                case "()":
    +
     8986. 195751                case "[]":
    +
     8987. 195751                case "{}":
    +
     8988. 195751
    +
     8989. 195751// If left and right are opener and closer, then the placement of right depends
    +
     8990. 195751// on the openness. Illegal pairs (like '{]') have already been detected.
    +
     8991. 195751
    +
     8992. 195751// test_cause:
    +
     8993. 195751// ["let aa=[];", "whitage", "opener_closer", "", 0]
    +
     8994. 195751// ["let aa=aa();", "whitage", "opener_closer", "", 0]
    +
     8995. 195751// ["let aa={};", "whitage", "opener_closer", "", 0]
    +
     8996. 195751
    +
     8997. 195751                    test_cause("opener_closer");
    +
     8998. 195751                    if (left.line === right.line) {
    +
     8999. 195751
    +
     9000. 195751// test_cause:
    +
     9001. 195751// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14]
    +
     9002. 195751
    +
     9003. 195751                        no_space();
    +
     9004. 195751                    } else {
    +
     9005. 195751
    +
     9006. 195751// test_cause:
    +
     9007. 195751// ["let aa = aa(\n );", "expected_at", "expected_a_at_b_c", "1", 2]
    +
     9008. 195751
    +
     9009. 195751                        at_margin(0);
    +
     9010. 195751                    }
    +
     9011. 195751                    break;
    +
     9012. 195751                default:
    +
     9013. 195751
    +
     9014. 195751// test_cause:
    +
     9015. 195751// ["let aa=(0);", "whitage", "opener_operand", "", 0]
    +
     9016. 195751// ["let aa=[0];", "whitage", "opener_operand", "", 0]
    +
     9017. 195751// ["let aa=`${0}`;", "whitage", "opener_operand", "", 0]
    +
     9018. 195751// ["let aa=aa(0);", "whitage", "opener_operand", "", 0]
    +
     9019. 195751// ["let aa={aa:0};", "whitage", "opener_operand", "", 0]
    +
     9020. 195751
    +
     9021. 195751                    test_cause("opener_operand");
    +
     9022. 195751                    opening = left.open || (left.line !== right.line);
    +
     9023. 195751                    push();
    +
     9024. 195751                    switch (left.id) {
    +
     9025. 195751                    case "${":
    +
     9026. 195751                        closer = "}";
    +
     9027. 195751                        break;
    +
     9028. 195751                    case "(":
    +
     9029. 195751                        closer = ")";
    +
     9030. 195751                        break;
    +
     9031. 195751                    case "[":
    +
     9032. 195751                        closer = "]";
    +
     9033. 195751                        break;
    +
     9034. 195751                    case "{":
    +
     9035. 195751                        closer = "}";
    +
     9036. 195751                        break;
    +
     9037. 195751                    }
    +
     9038. 195751                    if (opening) {
    +
     9039. 195751
    +
     9040. 195751// test_cause:
    +
     9041. 195751// ["function aa(){\nreturn;\n}", "whitage", "opening", "", 0]
    +
     9042. 195751// ["let aa=(\n0\n);", "whitage", "opening", "", 0]
    +
     9043. 195751// ["let aa=[\n0\n];", "whitage", "opening", "", 0]
    +
     9044. 195751// ["let aa=`${\n0\n}`;", "whitage", "opening", "", 0]
    +
     9045. 195751// ["let aa={\naa:0\n};", "whitage", "opening", "", 0]
    +
     9046. 195751
    +
     9047. 195751                        test_cause("opening");
    +
     9048. 195751                        free = closer === ")" && left.free;
    +
     9049. 195751                        open = true;
    +
     9050. 195751                        margin += mode_indent;
    +
     9051. 195751                        if (right.role === "label") {
    +
     9052. 195751                            if (right.from !== 0) {
    +
     9053. 195751
    +
     9054. 195751// test_cause:
    +
     9055. 195751// ["
    +
     9056. 195751// function aa() {
    +
     9057. 195751//  bb:
    +
     9058. 195751//     while (aa) {
    +
     9059. 195751//         if (aa) {
    +
     9060. 195751//             break bb;
    +
     9061. 195751//         }
    +
     9062. 195751//     }
    +
     9063. 195751// }
    +
     9064. 195751// ", "expected_at", "expected_a_at_b_c", "1", 2]
    +
     9065. 195751
    +
     9066. 195751                                expected_at(0);
    +
     9067. 195751                            }
    +
     9068. 195751                        } else if (right.switch) {
    +
     9069. 195751                            at_margin(-mode_indent);
    +
     9070. 195751                        } else {
    +
     9071. 195751                            at_margin(0);
    +
     9072. 195751                        }
    +
     9073. 195751                    } else {
    +
     9074. 195751                        if (right.statement || right.role === "label") {
    +
     9075. 195751
    +
     9076. 195751// test_cause:
    +
     9077. 195751// ["
    +
     9078. 195751// function aa() {bb:
    +
     9079. 195751//     while (aa) {
    +
     9080. 195751//         aa();
    +
     9081. 195751//     }
    +
     9082. 195751// }
    +
     9083. 195751// ", "whitage", "expected_line_break_a_b", "bb", 16]
    +
     9084. 195751
    +
     9085. 195751                            warn(
    +
     9086. 195751                                "expected_line_break_a_b",
    +
     9087. 195751                                right,
    +
     9088. 195751                                artifact(left),
    +
     9089. 195751                                artifact(right)
    +
     9090. 195751                            );
    +
     9091. 195751                        }
    +
     9092. 195751
    +
     9093. 195751// test_cause:
    +
     9094. 195751// ["let aa=(0);", "whitage", "not_free", "", 0]
    +
     9095. 195751// ["let aa=[0];", "whitage", "not_free", "", 0]
    +
     9096. 195751// ["let aa=`${0}`;", "whitage", "not_free", "", 0]
    +
     9097. 195751// ["let aa={aa:0};", "whitage", "not_free", "", 0]
    +
     9098. 195751
    +
     9099. 195751                        test_cause("not_free");
    +
     9100. 195751                        free = false;
    +
     9101. 195751                        open = false;
    +
     9102. 195751
    +
     9103. 195751// test_cause:
    +
     9104. 195751// ["let aa = ( 0 );", "no_space_only", "unexpected_space_a_b", "0", 12]
    +
     9105. 195751
    +
     9106. 195751                        no_space_only();
    +
     9107. 195751                    }
    +
     9108. 195751                }
    +
     9109. 195751                break;
    +
     9110. 195751            default:
    +
     9111. 195751                if (right.statement === true) {
    +
     9112. 195751                    if (left.id === "else") {
    +
     9113. 195751
    +
     9114. 195751// test_cause:
    +
     9115. 195751// ["
    +
     9116. 195751// let aa = 0;
    +
     9117. 195751// if (aa) {
    +
     9118. 195751//     aa();
    +
     9119. 195751// } else  if (aa) {
    +
     9120. 195751//     aa();
    +
     9121. 195751// }
    +
     9122. 195751// ", "one_space_only", "expected_space_a_b", "if", 9]
    +
     9123. 195751
    +
     9124. 195751                        one_space_only();
    +
     9125. 195751                    } else {
    +
     9126. 195751
    +
     9127. 195751// test_cause:
    +
     9128. 195751// [" let aa = 0;", "expected_at", "expected_a_at_b_c", "1", 2]
    +
     9129. 195751
    +
     9130. 195751                        at_margin(0);
    +
     9131. 195751                        open = false;
    +
     9132. 195751                    }
    +
     9133. 195751
    +
     9134. 195751// If right is a closer, then pop the previous state.
    +
     9135. 195751
    +
     9136. 195751                } else if (right.id === closer) {
    +
     9137. 195751                    pop();
    +
     9138. 195751                    if (opening && right.id !== ";") {
    +
     9139. 195751                        at_margin(0);
    +
     9140. 195751                    } else {
    +
     9141. 195751                        no_space_only();
    +
     9142. 195751                    }
    +
     9143. 195751                } else {
    +
     9144. 195751
    +
     9145. 195751// Left is not an opener, and right is not a closer.
    +
     9146. 195751// The nature of left and right will determine the space between them.
    +
     9147. 195751
    +
     9148. 195751// If left is ',' or ';' or right is a statement then if open,
    +
     9149. 195751// right must go at the margin, or if closed, a space between.
    +
     9150. 195751
    +
     9151. 195751                    if (right.switch) {
    +
     9152. 195751                        at_margin(-mode_indent);
    +
     9153. 195751                    } else if (right.role === "label") {
    +
     9154. 195751                        if (right.from !== 0) {
    +
     9155. 195751
    +
     9156. 195751// test_cause:
    +
     9157. 195751// ["
    +
     9158. 195751// function aa() {
    +
     9159. 195751//     aa();cc:
    +
     9160. 195751//     while (aa) {
    +
     9161. 195751//         if (aa) {
    +
     9162. 195751//             break cc;
    +
     9163. 195751//         }
    +
     9164. 195751//     }
    +
     9165. 195751// }
    +
     9166. 195751// ", "expected_at", "expected_a_at_b_c", "1", 10]
    +
     9167. 195751
    +
     9168. 195751                            expected_at(0);
    +
     9169. 195751                        }
    +
     9170. 195751                    } else if (left.id === ",") {
    +
     9171. 195751                        if (!open || (
    +
     9172. 195751                            (free || closer === "]")
    +
     9173. 195751                            && left.line === right.line
    +
     9174. 195751                        )) {
    +
     9175. 195751
    +
     9176. 195751// test_cause:
    +
     9177. 195751// ["let {aa,bb} = 0;", "one_space", "expected_space_a_b", "bb", 9]
    +
     9178. 195751
    +
     9179. 195751                            one_space();
    +
     9180. 195751                        } else {
    +
     9181. 195751
    +
     9182. 195751// test_cause:
    +
     9183. 195751// ["
    +
     9184. 195751// function aa() {
    +
     9185. 195751//     aa(
    +
     9186. 195751//         0,0
    +
     9187. 195751//     );
    +
     9188. 195751// }
    +
     9189. 195751// ", "expected_at", "expected_a_at_b_c", "9", 11]
    +
     9190. 195751
    +
     9191. 195751                            at_margin(0);
    +
     9192. 195751                        }
    +
     9193. 195751
    +
     9194. 195751// If right is a ternary operator, line it up on the margin.
    +
     9195. 195751
    +
     9196. 195751                    } else if (right.arity === "ternary") {
    +
     9197. 195751                        if (open) {
    +
     9198. 195751
    +
     9199. 195751// test_cause:
    +
     9200. 195751// ["
    +
     9201. 195751// let aa = (
    +
     9202. 195751//     aa
    +
     9203. 195751//     ? 0
    +
     9204. 195751// : 1
    +
     9205. 195751// );
    +
     9206. 195751// ", "expected_at", "expected_a_at_b_c", "5", 1]
    +
     9207. 195751
    +
     9208. 195751                            at_margin(0);
    +
     9209. 195751                        } else {
    +
     9210. 195751
    +
     9211. 195751// test_cause:
    +
     9212. 195751// ["let aa = (aa ? 0 : 1);", "whitage", "use_open", "?", 14]
    +
     9213. 195751
    +
     9214. 195751                            warn("use_open", right);
    +
     9215. 195751                        }
    +
     9216. 195751                    } else if (
    +
     9217. 195751                        right.arity === "binary"
    +
     9218. 195751                        && right.id === "("
    +
     9219. 195751                        && free
    +
     9220. 195751                    ) {
    +
     9221. 195751
    +
     9222. 195751// test_cause:
    +
     9223. 195751// ["let aa = aa(\naa ()\n);", "no_space", "unexpected_space_a_b", "(", 4]
    +
     9224. 195751
    +
     9225. 195751                        no_space();
    +
     9226. 195751                    } else if (
    +
     9227. 195751                        left.id === "."
    +
     9228. 195751                        || left.id === "?."
    +
     9229. 195751                        || left.id === "..."
    +
     9230. 195751                        || right.id === ","
    +
     9231. 195751                        || right.id === ";"
    +
     9232. 195751                        || right.id === ":"
    +
     9233. 195751                        || (
    +
     9234. 195751                            right.arity === "binary"
    +
     9235. 195751                            && (right.id === "(" || right.id === "[")
    +
     9236. 195751                        )
    +
     9237. 195751                        || (
    +
     9238. 195751                            right.arity === "function"
    +
     9239. 195751                            && left.id !== "function"
    +
     9240. 195751                        )
    +
     9241. 195751                        || (right.id === "." || right.id === "?.")
    +
     9242. 195751                    ) {
    +
     9243. 195751
    +
     9244. 195751// test_cause:
    +
     9245. 195751// ["let aa = 0 ;", "no_space_only", "unexpected_space_a_b", ";", 12]
    +
     9246. 195751// ["let aa = aa ?.aa;", "no_space_only", "unexpected_space_a_b", "?.", 13]
    +
     9247. 195751
    +
     9248. 195751                        no_space_only();
    +
     9249. 195751                    } else if (left.id === ";") {
    +
     9250. 195751
    +
     9251. 195751// test_cause:
    +
     9252. 195751// ["
    +
     9253. 195751// /*jslint for*/
    +
     9254. 195751// function aa() {
    +
     9255. 195751//     for (
    +
     9256. 195751//         aa();
    +
     9257. 195751// aa;
    +
     9258. 195751//         aa()
    +
     9259. 195751//     ) {
    +
     9260. 195751//         aa();
    +
     9261. 195751//     }
    +
     9262. 195751// }
    +
     9263. 195751// ", "expected_at", "expected_a_at_b_c", "9", 1]
    +
     9264. 195751
    +
     9265. 195751                        if (open) {
    +
     9266. 195751                            at_margin(0);
    +
     9267. 195751                        }
    +
     9268. 195751                    } else if (
    +
     9269. 195751                        left.arity === "ternary"
    +
     9270. 195751                        || left.id === "case"
    +
     9271. 195751                        || left.id === "catch"
    +
     9272. 195751                        || left.id === "else"
    +
     9273. 195751                        || left.id === "finally"
    +
     9274. 195751                        || left.id === "while"
    +
     9275. 195751                        || left.id === "await"
    +
     9276. 195751                        || right.id === "catch"
    +
     9277. 195751                        || right.id === "else"
    +
     9278. 195751                        || right.id === "finally"
    +
     9279. 195751                        || (right.id === "while" && !right.statement)
    +
     9280. 195751                        || (left.id === ")" && right.id === "{")
    +
     9281. 195751                    ) {
    +
     9282. 195751
    +
     9283. 195751// test_cause:
    +
     9284. 195751// ["
    +
     9285. 195751// function aa() {
    +
     9286. 195751//     do {
    +
     9287. 195751//         aa();
    +
     9288. 195751//     } while(aa());
    +
     9289. 195751// }
    +
     9290. 195751// ", "one_space_only", "expected_space_a_b", "(", 12]
    +
     9291. 195751
    +
     9292. 195751                        one_space_only();
    +
     9293. 195751                    } else if (
    +
     9294. 195751
    +
     9295. 195751// There is a space between left and right.
    +
     9296. 195751
    +
     9297. 195751                        spaceop[left.id] === true
    +
     9298. 195751                        || spaceop[right.id] === true
    +
     9299. 195751                        || (
    +
     9300. 195751                            left.arity === "binary"
    +
     9301. 195751                            && (left.id === "+" || left.id === "-")
    +
     9302. 195751                        )
    +
     9303. 195751                        || (
    +
     9304. 195751                            right.arity === "binary"
    +
     9305. 195751                            && (right.id === "+" || right.id === "-")
    +
     9306. 195751                        )
    +
     9307. 195751                        || left.id === "function"
    +
     9308. 195751                        || left.id === ":"
    +
     9309. 195751                        || left.id === "async"
    +
     9310. 195751                        || (
    +
     9311. 195751                            (
    +
     9312. 195751                                left.identifier
    +
     9313. 195751                                || left.id === "(string)"
    +
     9314. 195751                                || left.id === "(number)"
    +
     9315. 195751                            )
    +
     9316. 195751                            && (
    +
     9317. 195751                                right.identifier
    +
     9318. 195751                                || right.id === "(string)"
    +
     9319. 195751                                || right.id === "(number)"
    +
     9320. 195751                            )
    +
     9321. 195751                        )
    +
     9322. 195751                        || (left.arity === "statement" && right.id !== ";")
    +
     9323. 195751                    ) {
    +
     9324. 195751
    +
     9325. 195751// test_cause:
    +
     9326. 195751// ["let aa=0;", "one_space", "expected_space_a_b", "0", 8]
    +
     9327. 195751// ["let aa={\naa:\n0\n};", "expected_at", "expected_a_at_b_c", "5", 1]
    +
     9328. 195751
    +
     9329. 195751                        one_space();
    +
     9330. 195751                    } else if (left.arity === "unary" && left.id !== "`") {
    +
     9331. 195751                        no_space_only();
    +
     9332. 195751                    }
    +
     9333. 195751                }
    +
     9334. 195751            }
    +
     9335. 195751            nr_comments_skipped = 0;
    +
     9336. 195751            delete left.calls;
    +
     9337. 195751            delete left.dead;
    +
     9338. 195751            delete left.free;
    +
     9339. 195751            delete left.init;
    +
     9340. 195751            delete left.open;
    +
     9341. 195751            delete left.used;
    +
     9342. 195751            left = right;
    +
     9343. 195751        }
    +
     9344. 207158    });
    +
     9345.    206}
    +
     9346.      1
    +
     9347.      6function jslint_report({
    +
     9348.      6    exports,
    +
     9349.      6    froms,
    +
     9350.      6    functions,
    +
     9351.      6    global,
    +
     9352.      6    json,
    +
     9353.      6    module,
    +
     9354.      6    property,
    +
     9355.      6    stop,
    +
     9356.      6    warnings
    +
     9357.      6}) {
    +
     9358.      6
    +
     9359.      6// This function will create human-readable, html-report
    +
     9360.      6// for warnings, properties, and functions from jslint-result-object.
    +
     9361.      6//
    +
     9362.      6// Example usage:
    +
     9363.      6//  let result = jslint("console.log('hello world')");
    +
     9364.      6//  let html = jslint_report(result);
    +
     9365.      6
    +
     9366.      6    let html = "";
    +
     9367.      6    let length_80 = 1111;
    +
     9368.      6
    +
     9369.    328    function address(line = 1, column = 1) {
    +
     9370.    328
    +
     9371.    328// This function will create HTML address element from <line> and <column>
    +
     9372.    328
    +
     9373.    328        return `<address>${Number(line)}: ${Number(column)}</address>`;
    +
     9374.    328
    +
     9375.    328    }
    +
     9376.      6
    +
     9377.   2256    function detail(title, list) {
    +
     9378.   2256        return (
    +
     9379.   2256            (Array.isArray(list) && list.length > 0)
    +
     9380.    781            ? (
    +
     9381.    781
    +
     9382.    781// Google Lighthouse Accessibility - <dl>'s do not contain only properly-ordered
    +
     9383.    781// <dt> and <dd> groups, <script>, <template> or <div> elements.
    +
     9384.    781
    +
     9385.    781                "<dl>"
    +
     9386.    781                + "<dt>" + htmlEscape(title) + "</dt>"
    +
     9387.    781                + "<dd>" + list.join(", ") + "</dd>"
    +
     9388.    781                + "</dl>"
    +
     9389.    781            )
    +
     9390.   1475            : ""
    +
     9391.   2256        );
    +
     9392.   2256    }
    +
     9393.      6
    +
     9394.      6    html += String(`
    +
     9395.      6<style class="JSLINT_REPORT_STYLE">
    +
     9396.      6/* jslint utility2:true */
    +
     9397.      6/*csslint box-model: false, ids:false */
    +
     9398.      6/*csslint ignore:start*/
    +
     9399.      6@font-face {
    +
     9400.      6    font-display: swap;
    +
     9401.      6    font-family: "Daley";
    +
     9402.      6    src: url(
    +
     9403.      6"data:font/woff2;base64,d09GMgABAAAAABy4AA4AAAAAThwAABxiAAEAAAAAAAAAAAAA\
    +
     9404.      6AAAAAAAAAAAAAAAABmAAgiQINAmcDBEICuc41DEBNgIkA4R2C4I+AAQgBYkuByAMgScfYUIF\
    +
     9405.      67NgjsHGAbcDVFkXZ5Jwd+P96IGPc9rl9ETBEaCzCJkvY2UpziRZ7zftZWk8052U9+NqX6vXL\
    +
     9406.      6KDflSQnlJ0bP+QnPQAy744n9mup6H9PaCDFwM5zjf8exB89bZ1cdrYOP0NgnuRDRWlk9u/fE\
    +
     9407.      6llkxqmfH8lmRQ/DAmER9opk9wR6suc1LvTiXNEe1vbhUCH2USgnEwH3vUm05JQqejGvZvOtz\
    +
     9408.      67sIKEGgLdDNl/IrfqWVZG/wr42ekomEm91VA1p4LhHBuFzHF8//u7vvbREHMQqGtNLmiOOD/\
    +
     9409.      6X7WWiwqyCE98qt0jk5JJmgR5WJJElBmzRb1F7a66MmSLTNWZ2XSHfKBSKHoVteSEJ6EOdvVw\
    +
     9410.      6fNZOtXKDe39jXdRlkmMnOWIOFBgeEK/b0mFsgffnPyyAitNyutKky7J8a8MSEkAKGLgfptnS\
    +
     9411.      6/gDRSo7vwdNUmQDB7oP6pK7QF5d9SrY8M/tkrXcurSIQAmX7tz7pd33LIB7GQkBQ/k81s/0D\
    +
     9412.      6gpt4gbw7x0Cn/PocitK5KIGPGQIzQzAMuCeC2ERAidx9TySVqX06goT0SFFOOV9Kuxdi5Rg7\
    +
     9413.      6l6n3c+nKRemidOm2dtFV1jXMk4rP2m6RJ8xEdPYONLTbeMgaJ1nwS2W4su3MHwqkkvJ2PdDU\
    +
     9414.      6r7pgAnVRt4Kh789FXlD0r3p6jUtNO19O1s74U9pnIxqFpw+mBgF+8y30PAyw1dzlknLLVcSB\
    +
     9415.      6J2OuCr9eV5Efew6cOGd47ZEfhrW7HXI+FBNFvWgWnugUU4UvlrV63niv2ZPeKu8M76y/HQaG\
    +
     9416.      6weU+4Gzp+Y+cfb9R9djDWcd1Svr1xG7l+j/yf3eM996548qlC+dOzOqQ8//Lo0uaSEQCFuLD\
    +
     9417.      6/bXyWhJ6aPmyaRonVPxGABFL4/0slcKI6f+PmT0M+QRsplmWnv4F49VT+JsPifoa6aeyr2Hz\
    +
     9418.      6EeLdP1FEOV/ZN+c9sAuoNh0BRS0xgCCc9wME5s0HOKj/wc0fWYsTbFQpsZL5SayJPkL45kDo\
    +
     9419.      6DcJJ10MvD0ZSq7FEIr1TfqZ7NC6s75zSp8viaNO5/PczYCV9z6NTa0KBdnGBg6kbdeBkRLfU\
    +
     9420.      6qRd3D9Pqw5jWCc5WM/i95OE8731MBd1u2EmsXIa5dCvavY32U1Ytza4nfbERg6OVRZka7jq0\
    +
     9421.      6r2FcXNDyEhXheaHtaU1o1kvO9MuBOHqugLUEzN+4jznu0oK9wZPur1lWVFfxl8lZzn2XwcjZ\
    +
     9422.      6Csg/RJy0mAMMmgnqXS8ELhOCRUSLzvsM5gAPudEh2lVoRxGgyUVnArZMruE0YS1PqFMD3upb\
    +
     9423.      6jVoecGj1KpWl6/ZysuyzkG4SGA4bps6FBQSg4e4IxNUgdmosmoDn0TpIex/s1BFau6GBNO4z\
    +
     9424.      6cvWXypm4hEg5k3llelySFqNmUtRZ3PHBA7p4MBX1nK4awwAV6kWzIVbUA67A55QKYbMsgVaH\
    +
     9425.      6c1ZxKuZ0DCyqxCsJjLyCEY36gf0wjAu3t0zemc87PmBCJbU9Lso0YAaYJUx8wsR02hYz5hGy\
    +
     9426.      6Js0+A4uHGZgfuf5SOR9iBQuLhpOExaIFrHj6JlXanebzGHp2ELDh6av09PVE1fmdsj2oHRWs\
    +
     9427.      6fOtYrV6wRCyx7XogHqvpnZiPBBdNcL6kIoS9UI/DOIlumlveSgv9oqMBYp7WZ2fGxAXmZmaG\
    +
     9428.      6OCyJG6+wAszZFCQw/EXVjx+YA2uVyN6bhNWiZhgtYjAwR5U/7uV1scghiTGiAPZbA5ZqHw5u\
    +
     9429.      6Yu1cDjhRwREBFyq2wa0R8GgceDUKPo2BX+MhoAkQ1EQIaZqVHMwH3xM+P32TTA34tmOMNZ4n\
    +
     9430.      6mHXqn49fmE3qX1+wMNYoYetOsPx6wxKzkURImERJIjGSSJwkkiCJJEkiKZJImiSSIYlkSYqK\
    +
     9431.      6UBu0UOopuLMmasiJW0PMFOO2UgbDif2NaQUqkBbyaGjdTUvuyamEQwCq9DWsxsG9qPt+VFqV\
    +
     9432.      66cIsXcyWujWIEtNFdeia9ssNrJUpe3IDMPQZOReC8x+qvt17drPWdcHeL0gTarWwoQ6o828o\
    +
     9433.      60EJzrA20yZsgVyVHdlCJOF3NaACxHbP38TA+MGx3St9c5t2CxbGtunB4J9AF4Px2rSr1wyK9\
    +
     9434.      69KoXBR13vw9Fk9qhTX0ivZoanrvhLa5oiJO8cqR0lX7QtJ2c1a62V3PMtutaaoit+hxtXuC5\
    +
     9435.      6ZUXJePSR6btQlt5g7PqPQ822g7F8D123pc4kaGXz7qYztJxDXCxJr7foKqxwy4rikI/NvINx\
    +
     9436.      6bkArRTTnnMWy6YA8J39LfTweThKsqlt7Mz078NDSOPOGgtGTpeG8ZRBF+xKBjdSoNe8gE6uC\
    +
     9437.      6ucOH98jE4+cv1JEjI555TFjYj4+0KdFlojzJGWp2wc1tCaYGSeO8dBfT0u3lpDY3tazzu4wn\
    +
     9438.      6lF9wzy2nK+sTr/qEVdANoZ0ToBdD+MY4ewOHNnkXPBvKVXLSbEGfGVD0Nzr0Fs3HID3Y1Kqx\
    +
     9439.      6mzJ6p1C1/R6Xneyw/q9YRDLahbnsI1u76XzMLPqsK0yvQDeQ4TMR41709sIssmEgs0XH1lcj\
    +
     9440.      67HLnUG6u2Xpy5vbOowIGqrR6cwF0TLGI5PF7pkbzIVYQU0sIaoNgul3LGAH2B1nREFYXUMia\
    +
     9441.      6prCeAzggGxrC5gIK2dK0exs/AIRKdlIIuxkUspdSsU+rqXagqXaooXakqTiWS/a0E7zA6QIK\
    +
     9442.      6OdMUznMAh+RCQ7hcQCFXmspr3ciuds/6gPsZFPIgpfJhwUIepRAeZ1DIk5Tue4oKfSfKZyNV\
    +
     9443.      6pKU/J7J4Abx1EMV5mXSRDl6lMfU6jfBmBww4k7f6gLzTB+J9od/kA/uGj2mET2nkn7+zQ/JF\
    +
     9444.      6H5Kv+pB804fkOyvwI43wM438V5sdkd/6iPzRR+SvPiL/WIH/aYRxGqMb/Oqe3d54+LWR1vr2\
    +
     9445.      6knnnc467iD247eXBA3YYBAiFfierClXz/8jyL3Qh/zP8y+Y/1eN8jq+SKZAML/lIidjwZ8N4\
    +
     9446.      6aLthvhxGUkGPo+p0eHKZ0sT5FsqJcQCy9UhHIvcJFIlIvANTPFWUTUhSiVdsNRnvwEQxm5uc\
    +
     9447.      6ksjdv5evJfpOgI6c7juH8pnG2RKwlXaDYe9g8rMwYfML3A2SMWeBDopJJsmS5dUE2KttnmQa\
    +
     9448.      6JZlMspvEpJioiEDFNpPUTbwqG3Zjhx2VCeJrIf60s2mI6blZMZVyAyYzI+1a2Y0AIqcbLUgR\
    +
     9449.      66iRbNtnp82GrImXW0YbcbczDgqQDWNdTenvtTAlT9iPHenluV+d3eed1/5MjMBrX2LgrK2ml\
    +
     9450.      6FuoDOz036n/kaHbAeszR3jHoI4NWB3lusTfuVgkMUkLQaH0F6+pSCS11fXRwT421vs9s7axd\
    +
     9451.      6nvtF7/eeIeq9s1aCLsLWdh+w7sXz3IYdEsSQ0LVsebmES/vXDU9k653W4MiNq8bMj5nLioCY\
    +
     9452.      6edGgOT6tmYwqiOW1ugiEmew6iwjvvYb3SaeZJb7XNufOo9oH8FTneWGL+BLiclptpnhPwcui\
    +
     9453.      6T+rzcF34+ycsL7p3AveuML9i9h13beylyg8CzEz5HppadqmmDxKrAquG9L3ztedRoWxEsAYt\
    +
     9454.      6OM1Eu0G0gyTHkxf7cSkHJQRbA4xmlqHWkv1C0KhFhBq1z81Wq1CZoWic8TJ570WfSj5qsM+Q\
    +
     9455.      6nl4k3H5+P+P3zlv9ltQrzv41qyiSwV/gOadyQBchsmwDGu/JI8tXflE8jqUVA0Zw0SKbdDC9\
    +
     9456.      6c4FR+fak95SdF7uqpoRe9z6YRv+85YUzF4qJy6Q8GOVNwUn/ymyjNNbmcuVfXYeH2osLdCte\
    +
     9457.      6ebmZRyUfQQZA1BSCLK4PWA/z1kBvDZm0t+i3or1LkMD6en95pGG0UOa8ZJXgS9TdEA1I2mZw\
    +
     9458.      61JOWWxDu0NEh4rM19H55rvueMBUZV1RjkmB3oxkXhAckpa5gzzxUDA2VLOrWFAXx+4gmfU17\
    +
     9459.      65o3v9H7EYdvGFuM+tDB3TA4ITjVUKduO/R4bXRAcPXZusWkN+t59sFz7Hyi0FkSdzrHXQVFq\
    +
     9460.      6b8c9k9eLRjVlBbNvt4172CanYg/F3Rket1zCTc77UZ61Gq/Be9J8hrKrxbDZMEotf5o8zHDc\
    +
     9461.      6/UJaEtdhgwHEcBEQKM+6NBWIewLmI1sHuWYAedZCw8U1hJfSWcld+2tv3jpCFc5FnosLWC0+\
    +
     9462.      6DnAlnOXUXLoMXrmCVerNQkZHvRm8YtE12vG8+N/vOnPcu3vM1uOnzE3u3VP2ppmLZawm2NuO\
    +
     9463.      6tPa7xwHFCgVKpox5PVrOmaDHrThk1tX864a2+/qhJd3nCFRQ+bfUKI4O+Wgk5byB3saMcUfV\
    +
     9464.      6C8G137yMd16zRm3ZSq+UrDlk5ha3TiAj0b74prWO/vYG+RC+ronP1/McDtefBtY1XhZE0PIB\
    +
     9465.      6wTe7CBTte2U6KPbYd5GffApQlDGssdfmxYGSlnHrQt7++KEwUg3ikkoQyKPixgUDB6Lozjv5\
    +
     9466.      6vM5PBnllt+UzMnP6DStFsOfossbXOefWhQApACCNpkTYGAONIowDfndqDKRFuzn685nthZPe\
    +
     9467.      6vEL7TIWkXAG2yxKBH90+yMzuRzWn3KMmyKGwZWnIErlJ9Vwt8OtR6+4TKad5y9+ViBtTzVG+\
    +
     9468.      6tpv/xiLrcGKJRtYvCUlGeL4Dwy1jo1CSQe0X71EXK1YG44ztxTONjIslL8SwY0Cki0k0vsX/\
    +
     9469.      6/xz7CxkAc9dEdJZhMy/JCGzD2FAGtUcag0tc2e2miJkp477V2qTKB+nFnDl/noxpXJ+yqVdO\
    +
     9470.      6wNjbplmeiuburg9ii1Z1zwtG8QjcJAiVPSOV2mHzq1Qt7p2+YCcIKPmFusE5O+m8s+Wd8o3t\
    +
     9471.      6qO1b1IZF8N0tx6RQnZ9Ux3gXijHlolixst6vhJV6ao0ZFzSprfAc3x0MLvxU0OsmXEVddMVK\
    +
     9472.      629CC6mPgPtXTUW7tVnZxwm0DTJwNOeVRV4axMSPlpgyv1Va1MQhQqWwUOb0s+gVLOecos4Nf\
    +
     9473.      6eqlFW3fLQrlP86R4XRxrDHF0VIx6ArM5/sTWtObY6U2aosgxbN6FUa1iNTUpMThk1sUfJOC6\
    +
     9474.      6s1SKo9D0g1NfiVmavyful/K7nZdDgutV1A26i7FR3r16bv3zz1cGw+ta17IX/+ripyutix3C\
    +
     9475.      6xNmCxs7uiqKu9/Zjjn06tblXpJxlaLF5Od0d5W9QhQrs2u6UN0trQlCyEK2j9VYgCEIDrhQN\
    +
     9476.      6c00rxg/FOfZ1N+nLV7RXDsYP+p0EzqKcuPujzuzEQsu2mFf4nYvf3Yp32rq/RYLetDLuOOTc\
    +
     9477.      60WXBtgoech7AHUxAxPBg81qWCsYlzTofRU5/MpuyNoegR6mCJO5ckrLOhWbG7xo/VGwGgpRb\
    +
     9478.      6+Ch+TmlcuY6Qct/2x3gxzeDUU9u+ltexrjelJ0VRR9KXH/AqrbYxHa0vmQ/kBnE5EORBK1ZH\
    +
     9479.      6mTSy7A8DJMgzzqDsu9ML5J3ufkuUNDCfN5UKAjBgw2I/QlS8MQ6o/ll9dTAdoM7HYtV4cNWE\
    +
     9480.      6U4pOl5Y4SIzdMbNSjXFmsBV1uXXf7GaBZZslpFGFiIpokSzxWj4hjlGl4VKJDACo7ScxQf29\
    +
     9481.      6kM8gHD3nUJkwkN2aW2TGttqwOrygJ7r9nYX2tYqy7Z3TQV5ocWzUI8l871y3LsQLoTgEO76B\
    +
     9482.      6Upp69hy6VKRpZvpvgfQ2T06qgXjxh38eatREitX6bzKggIYmN4sAkA3a5oeJZDK3ahQrVJwa\
    +
     9483.      6AD65cEGBkS/tKH9TtybiREEWCMcKD0HH0gELtjB+KNSk7bspmpr6eb0CscIiFyZpmXu8+gxw\
    +
     9484.      6O7pJNbAK2h9q2c5dMHBaoi5DylbNGdweVVdN3Jm9u6YXXlmx4nYY2vIPfSkrE/vyv9gn/Z+j\
    +
     9485.      6R3HKExaUhdV0Az77YWbQPhNfjw+F0vTteSMin+wIfxyPe0DEoI4uz6o2IXwsZC7sg8MicQ3o\
    +
     9486.      6wys+NJYKVW72YiVQ5LKDVwrEg2jNVM6XdNjbsHlRDcAkD08o5iWtFB2dVoydRmmDRLalE+4t\
    +
     9487.      63gBbAPa7n7qXXXbTZTJXZKy5+1W0K7dgYEcIlu90ovC0C+5gxXiKtZisT14qDJ7f2ksyK59U\
    +
     9488.      6r3QeHtBb24mPz7YDB3rgMTyUZ/fxM8h2i1Z21B8/VA5+9l7BKaOJZ15lWsyPv/z6CjU32ZKq\
    +
     9489.      6+QFeyUywxYnUxUmcQfGc1Sp69oE2n6zFL8BXf5rc3cJMM6S97gagTT1bi7cmAV4MibkC4rz/\
    +
     9490.      6icmmFtMlo5aN1Wp3uxsBfd4+9T42xmxvd79FV/hfuviBcrIaX092PrY5rle9FR4wTnDzrwj4\
    +
     9491.      67frD2d0KsMcdcADQ1Yu1LECg9Wj3yOS8OhrJdQBqXqsam17vmt2wjjjouHE/EO9sGPdqt23v\
    +
     9492.      6j8rL6wid6ulagtNK5p1hjRkFtUxTIaZnIXk63Zb3P0t5MQ+3vxHIFrmgAdWwiDuA67tbVIF6\
    +
     9493.      6wJ53z0uhyhsfH9bgF0kPT9v2hrT3HKIBgUXIYoxsVU+uryemiUiQEwh+BfxP//qLShlumR26\
    +
     9494.      6I8OqjD+x3hHDj/IrEWmvyL6ioG/atfxe+5GzIqRgfaoayWOiTk+YixO15KDO6Os3XACDjboe\
    +
     9495.      6ryXXOuEmTpDsc7czk+H04Kw1PNJazW32CAURHwBldqK0/nqYHtcrtLyyTYmoD8hbcnJUfa3U\
    +
     9496.      63FxWNus7uic3Qm1BzEecJW0MAz+W2CyN9FLIy+EpSy6CjkXsllZw1uBs1SxrQWM97/vnHu7m\
    +
     9497.      6OtrkRl8AtBN3RDxI/fg7dZLLtDFYuCYYPMwXiO6ZIpwJ1GGydI9oUYYgnQQKDKoMTcwsjrfe\
    +
     9498.      6Tcht6y18bLcpNfX41WE27447vLNzHuF+j15co5N7Py8vKUpTCoghHMEYKkM6y02lvX+9XiFg\
    +
     9499.      6xBKMRNiwX69+LJb2Xa5WGqo7Rlk0cxsLVd0l2UXAW5jORg31sFMKYWXsDcRUKRDP8Q87OjiM\
    +
     9500.      6dI1hNEt43netf8rOyfp+L58fq3holY9gxXwRJLY6gahgLQi4hS8w9LS+rFcJtdSCBrQLWsMs\
    +
     9501.      6aDg/n8/P8/N+fcyoLepYr3W/CIUT7HsRQTtkduddbVfbo6Twt6fyJVPRrUGqRkWp3rdry65v\
    +
     9502.      6sPYInyq1mPHrQDrqGJYI/LzA/QAzAXLnx+lu9uxHTEka9xgWgRvqEioskh+UWgD4nDvTAxaz\
    +
     9503.      63v9BqqmFuQwy1wSXye1Df1NXVF7G8bUFxUE4F9axG5fm+vFQJvP8iuYjrFveB6++AqmJTQJ0\
    +
     9504.      69GHjbPhzdSzkZGxokQzONVs0R0FCPJz1hJKbvDKsaj9hT0vp/gH5oiT8pAbWsBChwAbxHgDd\
    +
     9505.      659iJVZE3bAzPRN1RuG+MT7th+J3i6KFwVJvPvsGRDIZW4P2rVfiKjDVBM2Va+w6PgI0c5u3K\
    +
     9506.      6O7MrWryPhXFFdBoAwi2JCaW9sZ3fTagQ4Tld6u4djwcWzeCdiYoeNbfalsRYo740afYQ1Rid\
    +
     9507.      6Bp/E9mbcTemEjoWWXIU7I5nK5H/BEqmZnPMyhDV234BTLQKCe6nhU+frwQo1gNFWf+eQGN62\
    +
     9508.      6aeF7BuzaN/94W2xlKd8t8KMA/3uoxymFt19OlCjYZeaMWbTKM9Yog9zDhptYMOzIQAoO7kn6\
    +
     9509.      6nTao8CxjrRRgjKe7mKa+tzuufhAAZtgjA92THkulWvIzEi0++j1DvXMnupDUS8aVusWain+c\
    +
     9510.      6CcvmR5orC+RcJs3wVahLYyEcqbvAS2e0QJ6BlU36R/IEd9Aol9q+M+UGvlo8EyRzISvqusNS\
    +
     9511.      67ePQ6cQzG1s725db4jNYNHAfF3KFG8wHqDwZDpWDsJ5qRLXR1ulFx85GhkypPubYaCiOQ5DR\
    +
     9512.      6PQUiNpgk4fLJHenSMLMiswXsqW4Cpln1rFoHzpOoBbuZIixmVyhKajeqlFmp8zNAEsbEJz0g\
    +
     9513.      6X0qlQuykZhf82pkhq2hWtCtYUdBODn6iPTBJT5Zk8IqFxqfBeFKjXk/sMeumhT8muOtq2Bgn\
    +
     9514.      6dR4fj6RoOi0zI25kajAXlDZhUhS39jipk39h/69AfDPBLmOxhDj7Lg/WUTbOwJiJ3p7WtOpm\
    +
     9515.      6ypARmhorQifINNm1CNS99GfDcLbD8sn8Fvlmvn7CmW65Pdmu6bKtuE0tn7NglIX1e/JAJP+G\
    +
     9516.      6gB3At7cSOp92rl0lp0pp0xVb5YaQedwGgcJA1pT4cy24lS+jvzDw86YTfb2igJm5MysHmejW\
    +
     9517.      6ZTGXpoAoLBLucUGEz/DwbjqOdzGAl5jy5VoCQws5zNYl4SVt030aZulYMgpDBPZd+kL0wV+w\
    +
     9518.      6nob2LPRDQGEbdUoeFm4fEKio9c/ferVlpSO8Bx7OFZyHip1PIizvoqFe02kpmS17TvIOty42\
    +
     9519.      6+Q0QaCnOpeLsPwwo+vixIeIeUjucUsKejVlez35qyuC0mm5pJJJLEVP2JAe/LTOwUUfKJkNy\
    +
     9520.      6lEe3Kdth241ZNQmkVcAIh6DZJBzvQov5fn3JZA0phBWdNq5iTsm5N2D8gyve3V3X2o3zF3VY\
    +
     9521.      6OqEBgTIADAbC69z7vOKJjGOzHRmUUwLU66iabtIbqR6SPOHCL+fCTfvpKcB/oG2p3wRKErEJ\
    +
     9522.      6v1YOfu9iaKEMLXS3ptdH8fwN2Rdww9bZ7rFa2bwrzcyux3o+hPV6bJZpb71j7lLAdzge3VX7\
    +
     9523.      69uSCdz6f/FDb7+wzWnbbDSPj9R20+PybDUm/lVAsTuF0aycFQwJfPCUwcBvCGWEq6xoTIEOy\
    +
     9524.      6b0bLta20+LYRYdyEceX7ypfezQKIy5OvJTAHCJy/WyOYaDVyPucMsHnZ0GCH75Cd//te1Bv2\
    +
     9525.      6RkMykqYurBiNbuH3Xfuprirr4Dd453O6abAYGb5tw1d6wrBL8p1J1Sx9Lgw7yxqYn0FTrc0y\
    +
     9526.      659yLlV+4zIkLfZlPFRVnanHpTyrIlpn4lGkt269+JXnIWhEQWNvuVsrt531jr+8AHkVZfQU8\
    +
     9527.      68U/4AUZMuOj5iliigFrof/usmloYEI1f8erhJku75snYW7YmFmUcoC7UtG/KfJRuz6j0tWPa\
    +
     9528.      656J5QA0rJHwSIhNT4GWvez19HT2lia+Pz7/+MVEWlvjY6+9P85a0y9qWkTzQ7nF0wDXpQpw3\
    +
     9529.      6K4xnfK2L08b5MrxdeI+DDfVDeV2JY0Fp6KH602tj2MbxxKM8oG+wTkE/dr8jyo4Sfs/IV6uf\
    +
     9530.      6+IIXpH2Nca1+WCJV5qEv193bcUELLR4iFu83xUedKy9353tn+3o01dF2bNEQ3fK9Q5tCXrCi\
    +
     9531.      6La+woCuvEeYrr+PiN2+i2V/eDJck580pyra8BV5ZIZbpe3kr5vJD3pqoGsnbcl/d+ndvR23b\
    +
     9532.      6K41M4dKwaAwDaMA1gZGBoQWAcYE9mYkmQOnAjkaG41FkGkIP2BAIgKvUvzhpE5JbA6lze2iL\
    +
     9533.      65Nr+AwiDo2W4BStvK30dKy0JGNbzAY5akexsrV0xo5K8rY50LOTLvDyukIZNbRLKOCk18mD3\
    +
     9534.      6WxmZGlsCMxNdGFYGNJnetUWyCPgo4BONEL4I9b8UeEBGYXuCdCd+DkctrqVLYXGSfE46kvAu\
    +
     9535.      6+ltK5SRxQPnjUqyJXsSYs6VY6WPKfns9+IXjHhd5wQvGipgFdMwVEQ+A7a2AAS0clQwH7KHW\
    +
     9536.      6SEGjhnklSVRghMtPy6gEtJRIKJeYkpQyQiequQunFOOU4BLdK1yp55olZS6npyPhMnvK7xIa\
    +
     9537.      6pyNj+JctcQLXenBOCms46aMkenIx45WpXqxxVJQLz/vgpmAVa0fmDv6Pue9xVTBPfVxCUGfj\
    +
     9538.      61R8uVi8Zu9nRFqk/t0gR6wmWOlzuKRqk33HpO8qQ+nbGoEZLL/0Va156SJ+u+t86/os7ic49\
    +
     9539.      6/7xoEqvL+2E8VOyCTuT/7j269Zy4jUtN+g4="
    +
     9540.      6    ) format("woff2");
    +
     9541.      6}
    +
     9542.      6.JSLINT_,
    +
     9543.      6.JSLINT_ address,
    +
     9544.      6.JSLINT_ button,
    +
     9545.      6.JSLINT_ cite,
    +
     9546.      6.JSLINT_ dd,
    +
     9547.      6.JSLINT_ dfn,
    +
     9548.      6.JSLINT_ dl,
    +
     9549.      6.JSLINT_ dt,
    +
     9550.      6.JSLINT_ fieldset,
    +
     9551.      6.JSLINT_ fieldset > div,
    +
     9552.      6.JSLINT_ input,
    +
     9553.      6.JSLINT_ label,
    +
     9554.      6.JSLINT_ legend,
    +
     9555.      6.JSLINT_ ol,
    +
     9556.      6.JSLINT_ samp,
    +
     9557.      6.JSLINT_ style,
    +
     9558.      6.JSLINT_ textarea,
    +
     9559.      6.JSLINT_ ul {
    +
     9560.      6    border: 0;
    +
     9561.      6    box-sizing: border-box;
    +
     9562.      6    margin: 0;
    +
     9563.      6    padding: 0;
    +
     9564.      6}
    +
     9565.      6/* disable text inflation algorithm used on some smartphones and tablets */
    +
     9566.      6.JSLINT_ {
    +
     9567.      6    -ms-text-size-adjust: none;
    +
     9568.      6    -webkit-text-size-adjust: none;
    +
     9569.      6    text-size-adjust: none;
    +
     9570.      6}
    +
     9571.      6.JSLINT_REPORT_ div {
    +
     9572.      6    box-sizing: border-box;
    +
     9573.      6}
    +
     9574.      6/*csslint ignore:end*/
    +
     9575.      6
    +
     9576.      6/* css - jslint_report - font */
    +
     9577.      6.JSLINT_,
    +
     9578.      6.JSLINT_ fieldset legend,
    +
     9579.      6.JSLINT_ .center {
    +
     9580.      6    font-family: daley, sans-serif;
    +
     9581.      6    font-size: 14px;
    +
     9582.      6}
    +
     9583.      6.JSLINT_ fieldset textarea,
    +
     9584.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS dt,
    +
     9585.      6.JSLINT_ #JSLINT_REPORT_WARNINGS samp {
    +
     9586.      6    font-size: 12px;
    +
     9587.      6}
    +
     9588.      6.JSLINT_ fieldset textarea,
    +
     9589.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS > div {
    +
     9590.      6    font-family: monospace;
    +
     9591.      6}
    +
     9592.      6.JSLINT_ fieldset > div {
    +
     9593.      6    font-family: sans-serif;
    +
     9594.      6}
    +
     9595.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dfn {
    +
     9596.      6    font-style: normal;
    +
     9597.      6    font-weight: bold;
    +
     9598.      6}
    +
     9599.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dt {
    +
     9600.      6    font-style: italic;
    +
     9601.      6}
    +
     9602.      6.JSLINT_ #JSLINT_REPORT_TITLE {
    +
     9603.      6    font-size: 32px;
    +
     9604.      6}
    +
     9605.      6
    +
     9606.      6/* css - jslint_report - general */
    +
     9607.      6.JSLINT_ {
    +
     9608.      6    background: antiquewhite;
    +
     9609.      6}
    +
     9610.      6.JSLINT_ fieldset {
    +
     9611.      6    background: gainsboro;
    +
     9612.      6    clear: both;
    +
     9613.      6    margin: 16px 40px;
    +
     9614.      6    width: auto;
    +
     9615.      6}
    +
     9616.      6.JSLINT_ fieldset address {
    +
     9617.      6    float: right;
    +
     9618.      6}
    +
     9619.      6.JSLINT_ fieldset legend,
    +
     9620.      6.JSLINT_ .center {
    +
     9621.      6    text-align: center;
    +
     9622.      6}
    +
     9623.      6.JSLINT_ fieldset legend {
    +
     9624.      6    background: darkslategray;
    +
     9625.      6    color: white;
    +
     9626.      6    padding: 4px 0;
    +
     9627.      6    width: 100%;
    +
     9628.      6}
    +
     9629.      6.JSLINT_ fieldset textarea {
    +
     9630.      6    padding: 4px;
    +
     9631.      6    resize: none;
    +
     9632.      6    white-space: pre;
    +
     9633.      6    width: 100%;
    +
     9634.      6}
    +
     9635.      6.JSLINT_ fieldset textarea::selection {
    +
     9636.      6    background: wheat;
    +
     9637.      6}
    +
     9638.      6.JSLINT_ fieldset > div {
    +
     9639.      6    padding: 16px;
    +
     9640.      6    width: 100%;
    +
     9641.      6    word-wrap: break-word;
    +
     9642.      6}
    +
     9643.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level {
    +
     9644.      6    background: cornsilk;
    +
     9645.      6    padding: 8px 16px;
    +
     9646.      6}
    +
     9647.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dd {
    +
     9648.      6    line-height: 20px;
    +
     9649.      6    padding-left: 120px;
    +
     9650.      6}
    +
     9651.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dfn {
    +
     9652.      6    display: block;
    +
     9653.      6    line-height: 20px;
    +
     9654.      6}
    +
     9655.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dl {
    +
     9656.      6    position: relative
    +
     9657.      6}
    +
     9658.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dt {
    +
     9659.      6    line-height: 20px;
    +
     9660.      6    position: absolute;
    +
     9661.      6    text-align: right;
    +
     9662.      6    width: 100px;
    +
     9663.      6}
    +
     9664.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level0 {
    +
     9665.      6    background: white;
    +
     9666.      6}
    +
     9667.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level1 {
    +
     9668.      6    /* yellow */
    +
     9669.      6    background: #ffffe0;
    +
     9670.      6    margin-left: 16px;
    +
     9671.      6}
    +
     9672.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level2 {
    +
     9673.      6    /* green */
    +
     9674.      6    background: #e0ffe0;
    +
     9675.      6    margin-left: 32px;
    +
     9676.      6}
    +
     9677.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level3 {
    +
     9678.      6    /* blue */
    +
     9679.      6    background: #D0D0ff;
    +
     9680.      6    margin-left: 48px;
    +
     9681.      6}
    +
     9682.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level4 {
    +
     9683.      6    /* purple */
    +
     9684.      6    background: #ffe0ff;
    +
     9685.      6    margin-left: 64px;
    +
     9686.      6}
    +
     9687.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level5 {
    +
     9688.      6    /* red */
    +
     9689.      6    background: #ffe0e0;
    +
     9690.      6    margin-left: 80px;
    +
     9691.      6}
    +
     9692.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level6 {
    +
     9693.      6    /* orange */
    +
     9694.      6    background: #ffe390;
    +
     9695.      6    margin-left: 96px;
    +
     9696.      6}
    +
     9697.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level7 {
    +
     9698.      6    /* gray */
    +
     9699.      6    background: #e0e0e0;
    +
     9700.      6    margin-left: 112px;
    +
     9701.      6}
    +
     9702.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level8 {
    +
     9703.      6    margin-left: 128px;
    +
     9704.      6}
    +
     9705.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level9 {
    +
     9706.      6    margin-left: 144px;
    +
     9707.      6}
    +
     9708.      6.JSLINT_ #JSLINT_REPORT_PROPERTIES {
    +
     9709.      6    background: transparent;
    +
     9710.      6}
    +
     9711.      6.JSLINT_ #JSLINT_REPORT_PROPERTIES textarea {
    +
     9712.      6    background: honeydew;
    +
     9713.      6    height: 100px;
    +
     9714.      6}
    +
     9715.      6.JSLINT_ #JSLINT_REPORT_TITLE {
    +
     9716.      6    color: darkslategray;
    +
     9717.      6    padding-top: 16px;
    +
     9718.      6}
    +
     9719.      6.JSLINT_ #JSLINT_REPORT_WARNINGS cite {
    +
     9720.      6    display: block;
    +
     9721.      6    margin: 16px 0 4px 0;
    +
     9722.      6    overflow-x: hidden;
    +
     9723.      6    white-space: pre-line;
    +
     9724.      6}
    +
     9725.      6.JSLINT_ #JSLINT_REPORT_WARNINGS cite:nth-child(1) {
    +
     9726.      6    margin-top: 0;
    +
     9727.      6}
    +
     9728.      6.JSLINT_ #JSLINT_REPORT_WARNINGS samp {
    +
     9729.      6    background: lavenderblush;
    +
     9730.      6    display: block;
    +
     9731.      6    padding: 4px;
    +
     9732.      6    white-space: pre-wrap;
    +
     9733.      6}
    +
     9734.      6.JSLINT_ #JSLINT_REPORT_WARNINGS > div {
    +
     9735.      6    background: pink;
    +
     9736.      6    max-height: 400px;
    +
     9737.      6    overflow-y: auto;
    +
     9738.      6}
    +
     9739.      6.JSLINT_ #JSLINT_REPORT_WARNINGS > legend {
    +
     9740.      6/* Google Lighthouse Accessibility - Background and foreground colors do not */
    +
     9741.      6/* have a sufficient contrast ratio. */
    +
     9742.      6    /* background: indianred; */
    +
     9743.      6    background: #b44;
    +
     9744.      6}
    +
     9745.      6</style>
    +
     9746.      6            `).trim() + "\n";
    +
     9747.      6
    +
     9748.      6// Produce the Title.
    +
     9749.      6
    +
     9750.      6    html += "<div class=\"center\" id=\"JSLINT_REPORT_TITLE\">\n";
    +
     9751.      6    html += "JSLint Report\n";
    +
     9752.      6    html += "</div>\n";
    +
     9753.      6
    +
     9754.      6// Produce the HTML Error Report.
    +
     9755.      6// <cite>
    +
     9756.      6//     <address>LINE_NUMBER</address>
    +
     9757.      6//     MESSAGE
    +
     9758.      6// </cite>
    +
     9759.      6// <samp>EVIDENCE</samp>
    +
     9760.      6
    +
     9761.      6    html += "<fieldset id=\"JSLINT_REPORT_WARNINGS\">\n";
    +
     9762.      6    html += "<legend>Report: Warnings (" + warnings.length + ")</legend>\n";
    +
     9763.      6    html += "<div>\n";
    +
     9764.      1    if (stop) {
    +
     9765.      1        html += "<div class=\"center\">JSLint was unable to finish.</div>\n";
    +
     9766.      1    }
    +
     9767.      7    warnings.forEach(function ({
    +
     9768.      7        column,
    +
     9769.      7        line,
    +
     9770.      7        line_source,
    +
     9771.      7        message,
    +
     9772.      7        stack_trace = ""
    +
     9773.      7    }, ii) {
    +
     9774.      7        html += (
    +
     9775.      7            "<cite>"
    +
     9776.      7            + address(line, column)
    +
     9777.      7            + htmlEscape((ii + 1) + ". " + message)
    +
     9778.      7            + "</cite>"
    +
     9779.      7            + "<samp>"
    +
     9780.      7            + htmlEscape(line_source.slice(0, 400) + "\n" + stack_trace)
    +
     9781.      7            + "</samp>\n"
    +
     9782.      7        );
    +
     9783.      7    });
    +
     9784.      3    if (warnings.length === 0) {
    +
     9785.      3        html += "<div class=\"center\">There are no warnings.</div>\n";
    +
     9786.      3    }
    +
     9787.      6    html += "</div>\n";
    +
     9788.      6    html += "</fieldset>\n";
    +
     9789.      6
    +
     9790.      6// Produce the /*property*/ directive.
    +
     9791.      6
    +
     9792.      6    html += "<fieldset id=\"JSLINT_REPORT_PROPERTIES\">\n";
    +
     9793.      6    html += (
    +
     9794.      6        "<legend>Report: Properties ("
    +
     9795.      6        + Object.keys(property).length
    +
     9796.      6        + ")</legend>\n"
    +
     9797.      6    );
    +
     9798.      6    html += "<label>\n";
    +
     9799.      6    html += "<textarea readonly>";
    +
     9800.      6    html += "/*property";
    +
     9801.    301    Object.keys(property).sort().forEach(function (key, ii) {
    +
     9802.    300        if (ii !== 0) {
    +
     9803.    300            html += ",";
    +
     9804.    300            length_80 += 2;
    +
     9805.    300        }
    +
     9806.     42        if (length_80 + key.length >= 80) {
    +
     9807.     42            length_80 = 4;
    +
     9808.     42            html += "\n   ";
    +
     9809.     42        }
    +
     9810.    301        html += " " + key;
    +
     9811.    301        length_80 += key.length;
    +
     9812.    301    });
    +
     9813.      6    html += "\n*/\n";
    +
     9814.      6    html += "</textarea>\n";
    +
     9815.      6    html += "</label>\n";
    +
     9816.      6    html += "</fieldset>\n";
    +
     9817.      6
    +
     9818.      6// Produce the HTML Function Report.
    +
     9819.      6// <div class=LEVEL>
    +
     9820.      6//     <address>LINE_NUMBER</address>
    +
     9821.      6//     <dfn>FUNCTION_NAME_AND_SIGNATURE</dfn>
    +
     9822.      6//     <dl>
    +
     9823.      6//         <dt>DETAIL</dt>
    +
     9824.      6//         <dd>NAMES</dd>
    +
     9825.      6//     </dl>
    +
     9826.      6// </div>
    +
     9827.      6
    +
     9828.      6    html += "<fieldset id=\"JSLINT_REPORT_FUNCTIONS\">\n";
    +
     9829.      6    html += "<legend>Report: Functions (" + functions.length + ")</legend>\n";
    +
     9830.      6    html += "<div>\n";
    +
     9831.      2    if (json) {
    +
     9832.      2
    +
     9833.      2// Bugfix - fix website crashing when linting pure json-object.
    +
     9834.      2// return (
    +
     9835.      2
    +
     9836.      2        html += (
    +
     9837.      2            warnings.length === 0
    +
     9838.      2            ? "<div class=\"center\">JSON: good.</div>\n"
    +
     9839.      2            : "<div class=\"center\">JSON: bad.</div>\n"
    +
     9840.      2        );
    +
     9841.      4    } else if (functions.length === 0) {
    +
     9842.      4        html += "<div class=\"center\">There are no functions.</div>\n";
    +
     9843.      4    }
    +
     9844.      6    exports = Object.keys(exports).sort();
    +
     9845.      6    froms.sort();
    +
     9846.      6    global = Object.keys(global.context).sort();
    +
     9847.      6    module = (
    +
     9848.      6        module
    +
     9849.      1        ? "module"
    +
     9850.      5        : "global"
    +
     9851.      6    );
    +
     9852.      3    if (global.length + froms.length + exports.length > 0) {
    +
     9853.      3        if (functions.length === 0) {
    +
     9854.      3            html += "<br>\n";
    +
     9855.      3        }
    +
     9856.      3        html += "<div class=\"level level0\">\n";
    +
     9857.      3        html += detail(module, global);
    +
     9858.      3        html += detail("import from", froms);
    +
     9859.      3        html += detail("export", exports);
    +
     9860.      3        html += "</div>\n";
    +
     9861.      3    }
    +
     9862.    321    functions.forEach(function (the_function) {
    +
     9863.    321        let {
    +
     9864.    321            context,
    +
     9865.    321            from,
    +
     9866.    321            id,
    +
     9867.    321            level,
    +
     9868.    321            line,
    +
     9869.    321            name,
    +
     9870.    321
    +
     9871.    321// Bugfix - fix html-report from crashing if parameters is undefined.
    +
     9872.    321
    +
     9873.    321            parameters = [],
    +
     9874.    321            signature
    +
     9875.    321        } = the_function;
    +
     9876.    321        let list = Object.keys(context);
    +
     9877.    321        let params;
    +
     9878.    321        html += (
    +
     9879.    321            "<div class=\"level level" + htmlEscape(level) + "\">"
    +
     9880.    321            + address(line, from + 1)
    +
     9881.    321            + "<dfn>"
    +
     9882.    321            + (
    +
     9883.    321                id === "=>"
    +
     9884.      1                ? (
    +
     9885.      1                    "\u00ab" + htmlEscape(name) + "\u00bb"
    +
     9886.      1                    + htmlEscape(signature)
    +
     9887.      1                    + " =>"
    +
     9888.      1                )
    +
     9889.    320                : (
    +
     9890.    320                    typeof name === "string"
    +
     9891.    320                    ? "\u00ab" + htmlEscape(name) + "\u00bb"
    +
     9892.    320                    : htmlEscape(name.id)
    +
     9893.    320                ) + htmlEscape(signature)
    +
     9894.    321            )
    +
     9895.    321            + "</dfn>"
    +
     9896.    321        );
    +
     9897.    321        params = [];
    +
     9898.    470        parameters.forEach(function extract({
    +
     9899.    470            id,
    +
     9900.    470            names
    +
     9901.    470        }) {
    +
     9902.    470            switch (id) {
    +
     9903.      6            case "[":
    +
     9904.     42            case "{":
    +
     9905.     42
    +
     9906.     42// Recurse extract().
    +
     9907.     42
    +
     9908.     42                names.forEach(extract);
    +
     9909.     42                break;
    +
     9910.      4            case "ignore":
    +
     9911.      4                break;
    +
     9912.    424            default:
    +
     9913.    424                params.push(id);
    +
     9914.    470            }
    +
     9915.    470        });
    +
     9916.    321        html += detail("parameter", params.sort());
    +
     9917.    321        list.sort();
    +
     9918.   2203        html += detail("variable", list.filter(function (id) {
    +
     9919.   2203            return (
    +
     9920.   2203                context[id].role === "variable"
    +
     9921.   1693                && context[id].parent === the_function
    +
     9922.   2203            );
    +
     9923.   2203        }));
    +
     9924.   2203        html += detail("exception", list.filter(function (id) {
    +
     9925.   2203            return context[id].role === "exception";
    +
     9926.   2203        }));
    +
     9927.   2203        html += detail("closure", list.filter(function (id) {
    +
     9928.   2203            return (
    +
     9929.   2203                context[id].closure === true
    +
     9930.   1494                && context[id].parent === the_function
    +
     9931.   2203            );
    +
     9932.   2203        }));
    +
     9933.   2203        html += detail("outer", list.filter(function (id) {
    +
     9934.   2203            return (
    +
     9935.   2203                context[id].parent !== the_function
    +
     9936.   1190                && context[id].parent.id !== "(global)"
    +
     9937.   2203            );
    +
     9938.   2203        }));
    +
     9939.   2203        html += detail(module, list.filter(function (id) {
    +
     9940.   2203            return context[id].parent.id === "(global)";
    +
     9941.   2203        }));
    +
     9942.   2203        html += detail("label", list.filter(function (id) {
    +
     9943.   2203            return context[id].role === "label";
    +
     9944.   2203        }));
    +
     9945.    321        html += "</div>\n";
    +
     9946.    321    });
    +
     9947.      6    html += "</div>\n";
    +
     9948.      6    html += "</fieldset>\n";
    +
     9949.      6    return html;
    +
     9950.      6}
    +
     9951.      1
    +
     9952.     10async function jstestDescribe(description, testFunction) {
    +
     9953.     10
    +
     9954.     10// This function will create-and-run test-group <testFunction>
    +
     9955.     10// with given <description>.
    +
     9956.     10
    +
     9957.     10    let message;
    +
     9958.     10    let result;
    +
     9959.     10    let timerTimeout;
    +
     9960.     10
    +
     9961.     10// Init jstestTimeStart.
    +
     9962.     10
    +
     9963.      1    if (jstestTimeStart === undefined) {
    +
     9964.      1        jstestTimeStart = jstestTimeStart || Date.now();
    +
     9965.      1        process.on("exit", jstestOnExit);
    +
     9966.      1    }
    +
     9967.     10
    +
     9968.     10// PR-457 - Wait awhile for imports to initialize.
    +
     9969.     10
    +
     9970.     10    await new Promise(function (resolve) {
    +
     9971.     10        setTimeout(resolve);
    +
     9972.     10    });
    +
     9973.     10
    +
     9974.     10// Init jstestItList.
    +
     9975.     10
    +
     9976.     10    jstestItList = [];
    +
     9977.     10    testFunction();
    +
     9978.     10
    +
     9979.     10// Wait for jstestItList to resolve.
    +
     9980.     10
    +
     9981.     10    timerTimeout = setTimeout(noop, 0x7fffffff);
    +
     9982.     10    result = await Promise.all(jstestItList);
    +
     9983.     10    clearTimeout(timerTimeout);
    +
     9984.     10
    +
     9985.     10// Print test results.
    +
     9986.     10
    +
     9987.     10    message = (
    +
     9988.     10        "\n  " + (Date.now() - jstestTimeStart) + "ms"
    +
     9989.     10        + " - test describe - " + description + "\n"
    +
     9990.     66        + result.map(function ([
    +
     9991.     66            err, description, mode
    +
     9992.     66        ]) {
    +
     9993.     66            jstestItCount += 1;
    +
     9994.      1            if (err) {
    +
     9995.      1                jstestCountFailed += 1;
    +
     9996.      1                err = (
    +
     9997.      1                    "    \u001b[31m\u2718 " + jstestItCount + ". test it - "
    +
     9998.      1                    + description + "\n" + err.stack + "\u001b[39m"
    +
     9999.      1                );
    +
    10000.      1                if (mode === "pass") {
    +
    10001.      1                    jstestCountFailed -= 1;
    +
    10002.      1                    err = "";
    +
    10003.      1                }
    +
    10004.      1            }
    +
    10005.     66            return err || (
    +
    10006.     66                "    \u001b[32m\u2714 " + jstestItCount + ". test it - "
    +
    10007.     66                + description + "\u001b[39m"
    +
    10008.     66            );
    +
    10009.     66        }).join("\n")
    +
    10010.     10    );
    +
    10011.     10    console.error(message);
    +
    10012.     10}
    +
    10013.      1
    +
    10014.     66function jstestIt(description, testFunction, mode) {
    +
    10015.     66
    +
    10016.     66// This function will create-and-run test-case <testFunction>
    +
    10017.     66// inside current test-group with given <description>.
    +
    10018.     66
    +
    10019.     66    jstestCountTotal += 1;
    +
    10020.     66    jstestItList.push(new Promise(async function (resolve) {
    +
    10021.     66        let err;
    +
    10022.     66        try {
    +
    10023.     65            await testFunction();
    +
    10024.     65        } catch (errCaught) {
    +
    10025.      1            err = errCaught;
    +
    10026.      1        }
    +
    10027.     66        resolve([err, description, mode]);
    +
    10028.     66    }));
    +
    10029.     66}
    +
    10030.      1
    +
    10031.      2function jstestOnExit(exitCode, mode) {
    +
    10032.      2
    +
    10033.      2// This function will on process-exit, print test-report
    +
    10034.      2// and exit with non-zero exit-code if any test failed.
    +
    10035.      2
    +
    10036.      2    let message = (
    +
    10037.      2        (
    +
    10038.      2            (jstestCountFailed || mode === "testsFailed")
    +
    10039.      1            ? "\n\u001b[31m"
    +
    10040.      1            : "\n\u001b[32m"
    +
    10041.      2        )
    +
    10042.      2        + "  tests total  - " + jstestCountTotal + "\n"
    +
    10043.      2        + "  tests failed - " + jstestCountFailed + "\n"
    +
    10044.      2        + "\n"
    +
    10045.      2        + "  time finished - "
    +
    10046.      2        + Number(Date.now() - jstestTimeStart).toLocaleString()
    +
    10047.      2        + " ms\n"
    +
    10048.      2        + "\u001b[39m"
    +
    10049.      2    );
    +
    10050.      1    if (mode !== "testsFailed") {
    +
    10051.      1        console.error(message);
    +
    10052.      1    }
    +
    10053.      2    process.exitCode = exitCode || jstestCountFailed;
    +
    10054.      2    return message;
    +
    10055.      2}
    +
    10056.      1
    +
    10057.    107async function moduleFsInit() {
    +
    10058.    107
    +
    10059.    107// This function will import nodejs builtin-modules if they have not yet been
    +
    10060.    107// imported.
    +
    10061.    107
    +
    10062.    107// State 3 - Modules already imported.
    +
    10063.    107
    +
    10064.    104    if (moduleFs !== undefined) {
    +
    10065.    104        return;
    +
    10066.    104    }
    +
    10067.      3
    +
    10068.      3// State 2 - Wait while modules are importing.
    +
    10069.      3
    +
    10070.      3    if (moduleFsInitResolveList !== undefined) {
    +
    10071.      2        return new Promise(function (resolve) {
    +
    10072.      2            moduleFsInitResolveList.push(resolve);
    +
    10073.      2        });
    +
    10074.      2    }
    +
    10075.      1
    +
    10076.      1// State 1 - Start importing modules.
    +
    10077.      1
    +
    10078.      1    moduleFsInitResolveList = [];
    +
    10079.      1    [
    +
    10080.      1        moduleChildProcess,
    +
    10081.      1        moduleFs,
    +
    10082.      1        modulePath,
    +
    10083.      1        moduleUrl
    +
    10084.      1    ] = await Promise.all([
    +
    10085.      1        import("child_process"),
    +
    10086.      1        import("fs"),
    +
    10087.      1        import("path"),
    +
    10088.      1        import("url")
    +
    10089.      1    ]);
    +
    10090.      2    while (moduleFsInitResolveList.length > 0) {
    +
    10091.      2        moduleFsInitResolveList.shift()();
    +
    10092.      2    }
    +
    10093.    107}
    +
    10094.      1
    +
    10095.   2728function noop(val) {
    +
    10096.   2728
    +
    10097.   2728// This function will do nothing except return <val>.
    +
    10098.   2728
    +
    10099.   2728    return val;
    +
    10100.   2728}
    +
    10101.      1
    +
    10102.  12919function objectDeepCopyWithKeysSorted(obj) {
    +
    10103.  12919
    +
    10104.  12919// This function will recursively deep-copy <obj> with keys sorted.
    +
    10105.  12919
    +
    10106.  12919    let sorted;
    +
    10107.   8995    if (typeof obj !== "object" || !obj) {
    +
    10108.   8995        return obj;
    +
    10109.   8995    }
    +
    10110.   3924
    +
    10111.   3924// Recursively deep-copy list with child-keys sorted.
    +
    10112.   3924
    +
    10113.   3924    if (Array.isArray(obj)) {
    +
    10114.   1338        return obj.map(objectDeepCopyWithKeysSorted);
    +
    10115.   2586    }
    +
    10116.   2586
    +
    10117.   2586// Recursively deep-copy obj with keys sorted.
    +
    10118.   2586
    +
    10119.   2586    sorted = Object.create(null);
    +
    10120.   7457    Object.keys(obj).sort().forEach(function (key) {
    +
    10121.   7457        sorted[key] = objectDeepCopyWithKeysSorted(obj[key]);
    +
    10122.   7457    });
    +
    10123.   2586    return sorted;
    +
    10124.   2586}
    +
    10125.      1
    +
    10126.   2791function object_assign_from_list(dict, list, val) {
    +
    10127.   2791
    +
    10128.   2791// Assign each property-name from <list> to <dict>.
    +
    10129.   2791
    +
    10130.  89862    list.forEach(function (key) {
    +
    10131.  89862        dict[key] = val;
    +
    10132.  89862    });
    +
    10133.   2791    return dict;
    +
    10134.   2791}
    +
    10135.      1
    +
    10136.     97function v8CoverageListMerge(processCovs) {
    +
    10137.     97
    +
    10138.     97// This function is derived from MIT Licensed v8-coverage at
    +
    10139.     97// https://github.com/demurgos/v8-coverage/tree/master/ts
    +
    10140.     97// https://github.com/demurgos/v8-coverage/blob/master/ts/LICENSE.md
    +
    10141.     97//
    +
    10142.     97// Merges a list of v8 process coverages.
    +
    10143.     97// The result is normalized.
    +
    10144.     97// The input values may be mutated, it is not safe to use them after passing
    +
    10145.     97// them to this function.
    +
    10146.     97// The computation is synchronous.
    +
    10147.     97// @param processCovs Process coverages to merge.
    +
    10148.     97// @return Merged process coverage.
    +
    10149.     97
    +
    10150.     97    let resultMerged = [];      // List of merged scripts from processCovs.
    +
    10151.     97    let urlToScriptDict = new Map();    // Map scriptCov.url to scriptCovs.
    +
    10152.     97
    +
    10153.   1094    function compareRangeList(aa, bb) {
    +
    10154.   1094
    +
    10155.   1094// Compares two range coverages.
    +
    10156.   1094// The ranges are first ordered by ascending `startOffset` and then by
    +
    10157.   1094// descending `endOffset`.
    +
    10158.   1094// This corresponds to a pre-order tree traversal.
    +
    10159.   1094
    +
    10160.   1065        if (aa.startOffset !== bb.startOffset) {
    +
    10161.   1065            return aa.startOffset - bb.startOffset;
    +
    10162.   1065        }
    +
    10163.     29        return bb.endOffset - aa.endOffset;
    +
    10164.     29    }
    +
    10165.     97
    +
    10166.   1707    function dictKeyValueAppend(dict, key, val) {
    +
    10167.   1707
    +
    10168.   1707// This function will append <val> to list <dict>[<key>].
    +
    10169.   1707
    +
    10170.   1707        let list = dict.get(key);
    +
    10171.   1165        if (list === undefined) {
    +
    10172.   1165            list = [];
    +
    10173.   1165            dict.set(key, list);
    +
    10174.   1165        }
    +
    10175.   1707        list.push(val);
    +
    10176.   1707    }
    +
    10177.     97
    +
    10178.    384    function mergeTreeList(parentTrees) {
    +
    10179.    384
    +
    10180.    384// This function will return RangeTree object with <parentTrees> merged into
    +
    10181.    384// property-children.
    +
    10182.    384// @precondition Same `start` and `end` for all the parentTrees
    +
    10183.    384
    +
    10184.    119        if (parentTrees.length <= 1) {
    +
    10185.    119            return parentTrees[0];
    +
    10186.    265        }
    +
    10187.    265
    +
    10188.    265// new RangeTree().
    +
    10189.    265
    +
    10190.    265        return {
    +
    10191.    265
    +
    10192.    265// Merge parentTrees into property-children.
    +
    10193.    265
    +
    10194.    265            children: mergeTreeListToChildren(parentTrees),
    +
    10195.    669            delta: parentTrees.reduce(function (aa, bb) {
    +
    10196.    669                return aa + bb.delta;
    +
    10197.    669            }, 0),
    +
    10198.    265            end: parentTrees[0].end,
    +
    10199.    265            start: parentTrees[0].start
    +
    10200.    265        };
    +
    10201.    265    }
    +
    10202.     97
    +
    10203.    265    function mergeTreeListToChildren(parentTrees) {
    +
    10204.    265
    +
    10205.    265// This function will return <resultChildren> with <parentTrees> merged.
    +
    10206.    265
    +
    10207.    265        let openRange;
    +
    10208.    265        let parentToChildDict = new Map();      // Map parent to child.
    +
    10209.    265        let queueList;
    +
    10210.    265        let queueListIi = 0;
    +
    10211.    265        let queueOffset;
    +
    10212.    265        let queueTrees;
    +
    10213.    265        let resultChildren = [];
    +
    10214.    265        let startToTreeDict = new Map();        // Map tree.start to tree.
    +
    10215.    639        function nextXxx() {
    +
    10216.    639
    +
    10217.    639// Increment nextOffset, nextTrees.
    +
    10218.    639
    +
    10219.    639            let [
    +
    10220.    639                nextOffset, nextTrees
    +
    10221.    300            ] = queueList[queueListIi] || [];
    +
    10222.    639            let openRangeEnd;
    +
    10223.    583            if (queueTrees === undefined) {
    +
    10224.    583                queueListIi += 1;
    +
    10225.    583
    +
    10226.    583// Increment nextOffset, nextTrees.
    +
    10227.    583
    +
    10228.    583            } else if (nextOffset === undefined || nextOffset > queueOffset) {
    +
    10229.     56                nextOffset = queueOffset;
    +
    10230.     56                nextTrees = queueTrees;
    +
    10231.     56                queueTrees = undefined;
    +
    10232.     56
    +
    10233.     56// Concat queueTrees to nextTrees.
    +
    10234.     56
    +
    10235.     56            } else {
    +
    10236.     56                if (nextOffset === queueOffset) {
    +
    10237.     56                    queueTrees.forEach(function (tree) {
    +
    10238.     56                        nextTrees.push(tree);
    +
    10239.     56                    });
    +
    10240.     56                    queueTrees = undefined;
    +
    10241.     56                }
    +
    10242.     56                queueListIi += 1;
    +
    10243.     56            }
    +
    10244.    639
    +
    10245.    639// Reached end of queueList.
    +
    10246.    639
    +
    10247.    265            if (nextOffset === undefined) {
    +
    10248.    265                if (openRange !== undefined) {
    +
    10249.    265
    +
    10250.    265// Append nested-children from parentToChildDict (within openRange) to
    +
    10251.    265// resultChildren.
    +
    10252.    265
    +
    10253.    265                    resultAppendNextChild();
    +
    10254.    265                }
    +
    10255.    265                return true;
    +
    10256.    374            }
    +
    10257.    374            if (openRange !== undefined && openRange.end <= nextOffset) {
    +
    10258.    129
    +
    10259.    129// Append nested-children from parentToChildDict (within openRange) to
    +
    10260.    129// resultChildren.
    +
    10261.    129
    +
    10262.    129                resultAppendNextChild();
    +
    10263.    129                openRange = undefined;
    +
    10264.    374            }
    +
    10265.    374            if (openRange === undefined) {
    +
    10266.    292                openRangeEnd = nextOffset + 1;
    +
    10267.    502                nextTrees.forEach(function ({
    +
    10268.    502                    parentIi,
    +
    10269.    502                    tree
    +
    10270.    502                }) {
    +
    10271.    502                    openRangeEnd = Math.max(openRangeEnd, tree.end);
    +
    10272.    502
    +
    10273.    502// Append children from nextTrees to parentToChildDict.
    +
    10274.    502
    +
    10275.    502                    dictKeyValueAppend(parentToChildDict, parentIi, tree);
    +
    10276.    502                });
    +
    10277.    292                queueOffset = openRangeEnd;
    +
    10278.    292                openRange = {
    +
    10279.    292                    end: openRangeEnd,
    +
    10280.    292                    start: nextOffset
    +
    10281.    292                };
    +
    10282.    292            } else {
    +
    10283.    114                nextTrees.forEach(function ({
    +
    10284.    114                    parentIi,
    +
    10285.    114                    tree
    +
    10286.    114                }) {
    +
    10287.    114                    let right;
    +
    10288.     82                    if (tree.end > openRange.end) {
    +
    10289.     82                        right = treeSplit(tree, openRange.end);
    +
    10290.     82                        if (queueTrees === undefined) {
    +
    10291.     82                            queueTrees = [];
    +
    10292.     82                        }
    +
    10293.     82
    +
    10294.     82// new RangeTreeWithParent().
    +
    10295.     82
    +
    10296.     82                        queueTrees.push({
    +
    10297.     82                            parentIi,
    +
    10298.     82                            tree: right
    +
    10299.     82                        });
    +
    10300.     82                    }
    +
    10301.    114
    +
    10302.    114// Append children from nextTrees to parentToChildDict.
    +
    10303.    114
    +
    10304.    114                    dictKeyValueAppend(parentToChildDict, parentIi, tree);
    +
    10305.    114                });
    +
    10306.     82            }
    +
    10307.    639        }
    +
    10308.    292        function resultAppendNextChild() {
    +
    10309.    292
    +
    10310.    292// This function will append next child to <resultChildren>.
    +
    10311.    292
    +
    10312.    292            let treesMatching = [];
    +
    10313.    589            parentToChildDict.forEach(function (nested) {
    +
    10314.    589                if (
    +
    10315.    589                    nested.length === 1
    +
    10316.    563                    && nested[0].start === openRange.start
    +
    10317.    480                    && nested[0].end === openRange.end
    +
    10318.    468                ) {
    +
    10319.    468                    treesMatching.push(nested[0]);
    +
    10320.    468                } else {
    +
    10321.    121
    +
    10322.    121// new rangeTreeCreate().
    +
    10323.    121
    +
    10324.    121                    treesMatching.push({
    +
    10325.    121                        children: nested,
    +
    10326.    121                        delta: 0,
    +
    10327.    121                        end: openRange.end,
    +
    10328.    121                        start: openRange.start
    +
    10329.    121                    });
    +
    10330.    121                }
    +
    10331.    589            });
    +
    10332.    292            parentToChildDict.clear();
    +
    10333.    292
    +
    10334.    292// Recurse mergeTreeList().
    +
    10335.    292
    +
    10336.    292            resultChildren.push(mergeTreeList(treesMatching));
    +
    10337.    292        }
    +
    10338.     75        function treeSplit(tree, offset) {
    +
    10339.     75
    +
    10340.     75// This function will split <tree> along <offset> and return the right-side.
    +
    10341.     75// @precondition `tree.start < offset && offset < tree.end`
    +
    10342.     75// @return RangeTree Right part
    +
    10343.     75
    +
    10344.     75            let child;
    +
    10345.     75            let ii = 0;
    +
    10346.     75            let leftChildLen = tree.children.length;
    +
    10347.     75            let mid;
    +
    10348.     75            let resultTree;
    +
    10349.     75            let rightChildren;
    +
    10350.     75
    +
    10351.     75// TODO(perf): Binary search (check overhead) //jslint-ignore-line
    +
    10352.     75
    +
    10353.     19            while (ii < tree.children.length) {
    +
    10354.     19                child = tree.children[ii];
    +
    10355.     19                if (child.start < offset && offset < child.end) {
    +
    10356.     19
    +
    10357.     19// Recurse treeSplit().
    +
    10358.     19
    +
    10359.     19                    mid = treeSplit(child, offset);
    +
    10360.     19                    leftChildLen = ii + 1;
    +
    10361.     19                    break;
    +
    10362.     19                }
    +
    10363.     19                if (child.start >= offset) {
    +
    10364.     19                    leftChildLen = ii;
    +
    10365.     19                    break;
    +
    10366.     19                }
    +
    10367.     19                ii += 1;
    +
    10368.     19            }
    +
    10369.     75            rightChildren = tree.children.splice(
    +
    10370.     75                leftChildLen,
    +
    10371.     75                tree.children.length - leftChildLen
    +
    10372.     75            );
    +
    10373.      4            if (mid !== undefined) {
    +
    10374.      4                rightChildren.unshift(mid);
    +
    10375.      4            }
    +
    10376.     75
    +
    10377.     75// new rangeTreeCreate().
    +
    10378.     75
    +
    10379.     75            resultTree = {
    +
    10380.     75                children: rightChildren,
    +
    10381.     75                delta: tree.delta,
    +
    10382.     75                end: tree.end,
    +
    10383.     75                start: offset
    +
    10384.     75            };
    +
    10385.     75            tree.end = offset;
    +
    10386.     75            return resultTree;
    +
    10387.     75        }
    +
    10388.    265
    +
    10389.    265// Init startToTreeDict.
    +
    10390.    265
    +
    10391.    669        parentTrees.forEach(function (parentTree, parentIi) {
    +
    10392.    545            parentTree.children.forEach(function (child) {
    +
    10393.    545
    +
    10394.    545// Append child with child.start to startToTreeDict.
    +
    10395.    545
    +
    10396.    545                dictKeyValueAppend(startToTreeDict, child.start, {
    +
    10397.    545                    parentIi,
    +
    10398.    545                    tree: child
    +
    10399.    545                });
    +
    10400.    545            });
    +
    10401.    669        });
    +
    10402.    265
    +
    10403.    265// init queueList.
    +
    10404.    265
    +
    10405.    335        queueList = Array.from(startToTreeDict).map(function ([
    +
    10406.    335            startOffset, trees
    +
    10407.    335        ]) {
    +
    10408.    335
    +
    10409.    335// new StartEvent().
    +
    10410.    335
    +
    10411.    335            return [
    +
    10412.    335                startOffset, trees
    +
    10413.    335            ];
    +
    10414.    217        }).sort(function (aa, bb) {
    +
    10415.    217            return aa[0] - bb[0];
    +
    10416.    217        });
    +
    10417.    639        while (true) {
    +
    10418.    639            if (nextXxx()) {
    +
    10419.    639                break;
    +
    10420.    639            }
    +
    10421.    639        }
    +
    10422.    265        return resultChildren;
    +
    10423.    265    }
    +
    10424.     97
    +
    10425.    689    function sortFunc(funcCov) {
    +
    10426.    689
    +
    10427.    689// This function will normalize-and-sort <funcCov>.ranges.
    +
    10428.    689// Sorts the ranges (pre-order sort).
    +
    10429.    689// TODO: Tree-based normalization of the ranges. //jslint-ignore-line
    +
    10430.    689// @param funcCov Function coverage to normalize.
    +
    10431.    689
    +
    10432.    689        funcCov.ranges = treeToRanges(treeFromSortedRanges(
    +
    10433.    689            funcCov.ranges.sort(compareRangeList)
    +
    10434.    689        ));
    +
    10435.    689        return funcCov;
    +
    10436.    689    }
    +
    10437.     97
    +
    10438.    129    function sortScript(scriptCov) {
    +
    10439.    129
    +
    10440.    129// This function will normalize-and-sort <scriptCov>.functions.
    +
    10441.    129
    +
    10442.    129// Normalize-and-sort functions[xxx].ranges.
    +
    10443.    129
    +
    10444.    688        scriptCov.functions.forEach(function (funcCov) {
    +
    10445.    688            sortFunc(funcCov);
    +
    10446.    688        });
    +
    10447.    129
    +
    10448.    129// Sort functions by root range (pre-order sort).
    +
    10449.    129
    +
    10450.    559        scriptCov.functions.sort(function (aa, bb) {
    +
    10451.    559            return compareRangeList(aa.ranges[0], bb.ranges[0]);
    +
    10452.    559        });
    +
    10453.    129        return scriptCov;
    +
    10454.    129    }
    +
    10455.     97
    +
    10456.    888    function treeFromSortedRanges(ranges) {
    +
    10457.    888
    +
    10458.    888// @precondition `ranges` are well-formed and pre-order sorted
    +
    10459.    888
    +
    10460.    888        let root;
    +
    10461.    888        let stack = [];   // Stack of parent trees and parent counts.
    +
    10462.   1856        ranges.forEach(function (range) {
    +
    10463.   1856
    +
    10464.   1856// new rangeTreeCreate().
    +
    10465.   1856
    +
    10466.   1856            let node = {
    +
    10467.   1856                children: [],
    +
    10468.   1856                delta: range.count,
    +
    10469.   1856                end: range.endOffset,
    +
    10470.   1856                start: range.startOffset
    +
    10471.   1856            };
    +
    10472.   1856            let parent;
    +
    10473.   1856            let parentCount;
    +
    10474.    888            if (root === undefined) {
    +
    10475.    888                root = node;
    +
    10476.    888                stack.push([
    +
    10477.    888                    node, range.count
    +
    10478.    888                ]);
    +
    10479.    888                return;
    +
    10480.    968            }
    +
    10481.   1565            while (true) {
    +
    10482.   1565                [
    +
    10483.   1565                    parent, parentCount
    +
    10484.   1565                ] = stack[stack.length - 1];
    +
    10485.   1565
    +
    10486.   1565// assert: `top !== undefined` (the ranges are sorted)
    +
    10487.   1565
    +
    10488.   1565                if (range.startOffset < parent.end) {
    +
    10489.   1565                    break;
    +
    10490.   1565                }
    +
    10491.   1565                stack.pop();
    +
    10492.   1565            }
    +
    10493.    968            node.delta -= parentCount;
    +
    10494.    968            parent.children.push(node);
    +
    10495.    968            stack.push([
    +
    10496.    968                node, range.count
    +
    10497.    968            ]);
    +
    10498.    968        });
    +
    10499.    888        return root;
    +
    10500.    888    }
    +
    10501.     97
    +
    10502.    781    function treeToRanges(tree) {
    +
    10503.    781
    +
    10504.    781// Get the range coverages corresponding to the tree.
    +
    10505.    781// The ranges are pre-order sorted.
    +
    10506.    781
    +
    10507.    781        let count;
    +
    10508.    781        let cur;
    +
    10509.    781        let ii;
    +
    10510.    781        let parentCount;
    +
    10511.    781        let ranges = [];
    +
    10512.    781        let stack = [           // Stack of parent trees and counts.
    +
    10513.    781            [
    +
    10514.    781                tree, 0
    +
    10515.    781            ]
    +
    10516.    781        ];
    +
    10517.   1630        function normalizeRange(tree) {
    +
    10518.   1630
    +
    10519.   1630// @internal
    +
    10520.   1630
    +
    10521.   1630            let children = [];
    +
    10522.   1630            let curEnd;
    +
    10523.   1630            let head;
    +
    10524.   1630            let tail = [];
    +
    10525.    849            function endChain() {
    +
    10526.     18                if (tail.length !== 0) {
    +
    10527.     18                    head.end = tail[tail.length - 1].end;
    +
    10528.     18                    tail.forEach(function (tailTree) {
    +
    10529.     18                        tailTree.children.forEach(function (subChild) {
    +
    10530.     18                            subChild.delta += tailTree.delta - head.delta;
    +
    10531.     18                            head.children.push(subChild);
    +
    10532.     18                        });
    +
    10533.     18                    });
    +
    10534.     18                    tail.length = 0;
    +
    10535.     18                }
    +
    10536.    849
    +
    10537.    849// Recurse normalizeRange().
    +
    10538.    849
    +
    10539.    849                normalizeRange(head);
    +
    10540.    849                children.push(head);
    +
    10541.    849            }
    +
    10542.    867            tree.children.forEach(function (child) {
    +
    10543.    432                if (head === undefined) {
    +
    10544.    432                    head = child;
    +
    10545.    435                } else if (
    +
    10546.    435                    child.delta === head.delta && child.start === curEnd
    +
    10547.    435                ) {
    +
    10548.    435                    tail.push(child);
    +
    10549.    435                } else {
    +
    10550.    435                    endChain();
    +
    10551.    435                    head = child;
    +
    10552.    435                }
    +
    10553.    867                curEnd = child.end;
    +
    10554.    867            });
    +
    10555.    432            if (head !== undefined) {
    +
    10556.    432                endChain();
    +
    10557.    432            }
    +
    10558.    238            if (children.length === 1) {
    +
    10559.    238                if (
    +
    10560.    238                    children[0].start === tree.start
    +
    10561.    238                    && children[0].end === tree.end
    +
    10562.    238                ) {
    +
    10563.    238                    tree.delta += children[0].delta;
    +
    10564.    238                    tree.children = children[0].children;
    +
    10565.    238
    +
    10566.    238// `.lazyCount` is zero for both (both are after normalization)
    +
    10567.    238
    +
    10568.    238                    return;
    +
    10569.    238                }
    +
    10570.   1624            }
    +
    10571.   1624            tree.children = children;
    +
    10572.   1624        }
    +
    10573.    781        normalizeRange(tree);
    +
    10574.   1624        while (stack.length > 0) {
    +
    10575.   1624            [
    +
    10576.   1624                cur, parentCount
    +
    10577.   1624            ] = stack.pop();
    +
    10578.   1624            count = parentCount + cur.delta;
    +
    10579.   1624            ranges.push({
    +
    10580.   1624                count,
    +
    10581.   1624                endOffset: cur.end,
    +
    10582.   1624                startOffset: cur.start
    +
    10583.   1624            });
    +
    10584.   1624            ii = cur.children.length - 1;
    +
    10585.   1624            while (ii >= 0) {
    +
    10586.   1624                stack.push([
    +
    10587.   1624                    cur.children[ii], count
    +
    10588.   1624                ]);
    +
    10589.   1624                ii -= 1;
    +
    10590.   1624            }
    +
    10591.   1624        }
    +
    10592.    781        return ranges;
    +
    10593.    781    }
    +
    10594.     97
    +
    10595.      1    if (processCovs.length === 0) {
    +
    10596.      1        return {
    +
    10597.      1            result: []
    +
    10598.      1        };
    +
    10599.     96    }
    +
    10600.     96
    +
    10601.     96// Init urlToScriptDict.
    +
    10602.     96
    +
    10603.    234    processCovs.forEach(function ({
    +
    10604.    234        result
    +
    10605.    234    }) {
    +
    10606.    269        result.forEach(function (scriptCov) {
    +
    10607.    269            dictKeyValueAppend(urlToScriptDict, scriptCov.url, scriptCov);
    +
    10608.    269        });
    +
    10609.    234    });
    +
    10610.    129    urlToScriptDict.forEach(function (scriptCovs) {
    +
    10611.    129
    +
    10612.    129// assert: `scriptCovs.length > 0`
    +
    10613.    129
    +
    10614.    129// function mergeScriptList(scriptCovs) {
    +
    10615.    129// Merges a list of matching script coverages.
    +
    10616.    129// Scripts are matching if they have the same `url`.
    +
    10617.    129// The result is normalized.
    +
    10618.    129// The input values may be mutated, it is not safe to use them after passing
    +
    10619.    129// them to this function.
    +
    10620.    129// The computation is synchronous.
    +
    10621.    129// @param scriptCovs Process coverages to merge.
    +
    10622.    129// @return Merged script coverage, or `undefined` if the input list was empty.
    +
    10623.    129
    +
    10624.    129        let functions = [];
    +
    10625.    129
    +
    10626.    129// Map funcCovRoot.startOffset:funcCovRoot.endOffset to funcCov.
    +
    10627.    129
    +
    10628.    129        let rangeToFuncDict = new Map();
    +
    10629.    129
    +
    10630.    129// Probably deadcode.
    +
    10631.    129// if (scriptCovs.length === 0) {
    +
    10632.    129//     return undefined;
    +
    10633.    129// }
    +
    10634.    129
    +
    10635.     96        if (scriptCovs.length === 1) {
    +
    10636.     96            resultMerged.push(sortScript(scriptCovs[0]));
    +
    10637.     96            return;
    +
    10638.     96        }
    +
    10639.     96
    +
    10640.     96// Init rangeToFuncDict.
    +
    10641.     96// Map funcCovRoot.startOffset:funcCovRoot.endOffset to funcCov.
    +
    10642.     96
    +
    10643.    226        scriptCovs.forEach(function ({
    +
    10644.    226            functions
    +
    10645.    226        }) {
    +
    10646.    277            functions.forEach(function (funcCov) {
    +
    10647.    277                dictKeyValueAppend(
    +
    10648.    277                    rangeToFuncDict,
    +
    10649.    277
    +
    10650.    277// This string can be used to match function with same root range.
    +
    10651.    277// The string is derived from the start and end offsets of the root range of
    +
    10652.    277// the function.
    +
    10653.    277// This assumes that `ranges` is non-empty (true for valid function coverages).
    +
    10654.    277
    +
    10655.    277                    (
    +
    10656.    277                        funcCov.ranges[0].startOffset
    +
    10657.    277                        + ";" + funcCov.ranges[0].endOffset
    +
    10658.    277                    ),
    +
    10659.    277                    funcCov
    +
    10660.    277                );
    +
    10661.    277            });
    +
    10662.    226        });
    +
    10663.    112        rangeToFuncDict.forEach(function (funcCovs) {
    +
    10664.    112
    +
    10665.    112// assert: `funcCovs.length > 0`
    +
    10666.    112
    +
    10667.    112// function mergeFuncList(funcCovs) {
    +
    10668.    112// Merges a list of matching function coverages.
    +
    10669.    112// Functions are matching if their root ranges have the same span.
    +
    10670.    112// The result is normalized.
    +
    10671.    112// The input values may be mutated, it is not safe to use them after passing
    +
    10672.    112// them to this function.
    +
    10673.    112// The computation is synchronous.
    +
    10674.    112// @param funcCovs Function coverages to merge.
    +
    10675.    112// @return Merged function coverage, or `undefined` if the input list was empty.
    +
    10676.    112
    +
    10677.    112            let count = 0;
    +
    10678.    112            let isBlockCoverage;
    +
    10679.    112            let merged;
    +
    10680.    112            let ranges;
    +
    10681.    112            let trees = [];
    +
    10682.    112
    +
    10683.    112// Probably deadcode.
    +
    10684.    112// if (funcCovs.length === 0) {
    +
    10685.    112//     return undefined;
    +
    10686.    112// }
    +
    10687.    112
    +
    10688.     96            if (funcCovs.length === 1) {
    +
    10689.     96                functions.push(sortFunc(funcCovs[0]));
    +
    10690.     96                return;
    +
    10691.    111            }
    +
    10692.    111
    +
    10693.    111// assert: `funcCovs[0].ranges.length > 0`
    +
    10694.    111
    +
    10695.    276            funcCovs.forEach(function (funcCov) {
    +
    10696.    276
    +
    10697.    276// assert: `funcCov.ranges.length > 0`
    +
    10698.    276// assert: `funcCov.ranges` is sorted
    +
    10699.    276
    +
    10700.    276                count += (
    +
    10701.    276                    funcCov.count !== undefined
    +
    10702.    111                    ? funcCov.count
    +
    10703.    274                    : funcCov.ranges[0].count
    +
    10704.    276                );
    +
    10705.    199                if (funcCov.isBlockCoverage) {
    +
    10706.    199                    trees.push(treeFromSortedRanges(funcCov.ranges));
    +
    10707.    199                }
    +
    10708.    276            });
    +
    10709.    111            if (trees.length > 0) {
    +
    10710.     96                isBlockCoverage = true;
    +
    10711.     96                ranges = treeToRanges(mergeTreeList(trees));
    +
    10712.     96            } else {
    +
    10713.     96                isBlockCoverage = false;
    +
    10714.     96                ranges = [
    +
    10715.     96                    {
    +
    10716.     96                        count,
    +
    10717.     96                        endOffset: funcCovs[0].ranges[0].endOffset,
    +
    10718.     96                        startOffset: funcCovs[0].ranges[0].startOffset
    +
    10719.     96                    }
    +
    10720.     96                ];
    +
    10721.    111            }
    +
    10722.    111            merged = {
    +
    10723.    111                functionName: funcCovs[0].functionName,
    +
    10724.    111                isBlockCoverage,
    +
    10725.    111                ranges
    +
    10726.    111            };
    +
    10727.    111            if (count !== ranges[0].count) {
    +
    10728.     96                merged.count = count;
    +
    10729.    111            }
    +
    10730.    111
    +
    10731.    111// assert: `merged` is normalized
    +
    10732.    111
    +
    10733.    111            functions.push(merged);
    +
    10734.    111        });
    +
    10735.     96        resultMerged.push(sortScript({
    +
    10736.     96            functions,
    +
    10737.     96            scriptId: scriptCovs[0].scriptId,
    +
    10738.     96            url: scriptCovs[0].url
    +
    10739.     96        }));
    +
    10740.     96    });
    +
    10741.     96
    +
    10742.     96// Sorts the scripts alphabetically by `url`.
    +
    10743.     96// Reassigns script ids: the script at index `0` receives `"0"`, the script at
    +
    10744.     96// index `1` receives `"1"` etc.
    +
    10745.     96
    +
    10746.     96    Object.entries(resultMerged.sort(function (aa, bb) {
    +
    10747.     96        return (
    +
    10748.     96            aa.url > bb.url
    +
    10749.     96            ? 1
    +
    10750.     96            : -1
    +
    10751.     96        );
    +
    10752.    129    })).forEach(function ([
    +
    10753.    129        scriptId, scriptCov
    +
    10754.    129    ]) {
    +
    10755.    129        scriptCov.scriptId = scriptId.toString(10);
    +
    10756.    129    });
    +
    10757.     96    return {
    +
    10758.     96        result: resultMerged
    +
    10759.     96    };
    +
    10760.     96}
    +
    10761.      1
    +
    10762.      8async function v8CoverageReportCreate({
    +
    10763.      8    consoleError,
    +
    10764.      8    coverageDir,
    +
    10765.      8    processArgv = []
    +
    10766.      8}) {
    +
    10767.      8
    +
    10768.      8// This function will create html-coverage-reports directly from
    +
    10769.      8// v8-coverage-files in <coverageDir>.
    +
    10770.      8// 1. Spawn node.js program <processArgv> with NODE_V8_COVERAGE.
    +
    10771.      8// 2. Merge JSON v8-coverage-files in <coverageDir>.
    +
    10772.      8// 3. Create html-coverage-reports in <coverageDir>.
    +
    10773.      8
    +
    10774.      8    let cwd;
    +
    10775.      8    let excludeList = [];
    +
    10776.      8    let exitCode = 0;
    +
    10777.      8    let fileDict;
    +
    10778.      8    let includeList = [];
    +
    10779.      8    let modeIncludeNodeModules;
    +
    10780.      8    let processArgElem;
    +
    10781.      8    let promiseList = [];
    +
    10782.      8    let v8CoverageObj;
    +
    10783.      8
    +
    10784.     13    function htmlRender({
    +
    10785.     13        fileList,
    +
    10786.     13        lineList,
    +
    10787.     13        modeIndex,
    +
    10788.     13        pathname
    +
    10789.     13    }) {
    +
    10790.     13        let html;
    +
    10791.     13        let padLines;
    +
    10792.     13        let padPathname;
    +
    10793.     13        let txt;
    +
    10794.     13        let txtBorder;
    +
    10795.     13        html = "";
    +
    10796.     13        html += String(`
    +
    10797.     13<!DOCTYPE html>
    +
    10798.     13<html lang="en">
    +
    10799.     13<head>
    +
    10800.     13<title>V8 Coverage Report</title>
    +
    10801.     13<style>
    +
    10802.     13/* jslint utility2:true */
    +
    10803.     13/*csslint ignore:start*/
    +
    10804.     13.coverage,
    +
    10805.     13.coverage a,
    +
    10806.     13.coverage div,
    +
    10807.     13.coverage pre,
    +
    10808.     13.coverage span,
    +
    10809.     13.coverage table,
    +
    10810.     13.coverage tbody,
    +
    10811.     13.coverage td,
    +
    10812.     13.coverage th,
    +
    10813.     13.coverage thead,
    +
    10814.     13.coverage tr {
    +
    10815.     13    box-sizing: border-box;
    +
    10816.     13    font-family: monospace;
    +
    10817.     13}
    +
    10818.     13/*csslint ignore:end*/
    +
    10819.     13
    +
    10820.     13/* css - coverage_report - general */
    +
    10821.     13body {
    +
    10822.     13    margin: 0;
    +
    10823.     13}
    +
    10824.     13.coverage pre {
    +
    10825.     13    margin: 5px 0;
    +
    10826.     13}
    +
    10827.     13.coverage table {
    +
    10828.     13    border-collapse: collapse;
    +
    10829.     13}
    +
    10830.     13.coverage td,
    +
    10831.     13.coverage th {
    +
    10832.     13    border: 1px solid #777;
    +
    10833.     13    line-height: 20px;
    +
    10834.     13    margin: 0;
    +
    10835.     13    padding: 5px 10px;
    +
    10836.     13}
    +
    10837.     13.coverage td span {
    +
    10838.     13    display: inline-block;
    +
    10839.     13    width: 100%;
    +
    10840.     13}
    +
    10841.     13.coverage .content {
    +
    10842.     13    padding: 0 5px;
    +
    10843.     13}
    +
    10844.     13.coverage .content a {
    +
    10845.     13    text-decoration: none;
    +
    10846.     13}
    +
    10847.     13.coverage .count {
    +
    10848.     13    margin: 0 5px;
    +
    10849.     13    padding: 0 5px;
    +
    10850.     13}
    +
    10851.     13.coverage .footer,
    +
    10852.     13.coverage .header {
    +
    10853.     13    padding: 20px;
    +
    10854.     13}
    +
    10855.     13.coverage .footer {
    +
    10856.     13    text-align: center;
    +
    10857.     13}
    +
    10858.     13.coverage .percentbar {
    +
    10859.     13    height: 12px;
    +
    10860.     13    margin: 2px 0;
    +
    10861.     13    min-width: 200px;
    +
    10862.     13    position: relative;
    +
    10863.     13    width: 100%;
    +
    10864.     13}
    +
    10865.     13.coverage .percentbar div {
    +
    10866.     13    height: 100%;
    +
    10867.     13    position: absolute;
    +
    10868.     13}
    +
    10869.     13.coverage .title {
    +
    10870.     13    font-size: large;
    +
    10871.     13    font-weight: bold;
    +
    10872.     13    margin-bottom: 10px;
    +
    10873.     13}
    +
    10874.     13
    +
    10875.     13/* css - coverage_report - color */
    +
    10876.     13.coverage td,
    +
    10877.     13.coverage th {
    +
    10878.     13    background: #fff;
    +
    10879.     13}
    +
    10880.     13.coverage .count,
    +
    10881.     13.coverage .coverageHigh {
    +
    10882.     13    background: #9d9;
    +
    10883.     13}
    +
    10884.     13.coverage .count {
    +
    10885.     13    color: #666;
    +
    10886.     13}
    +
    10887.     13.coverage .coverageIgnore {
    +
    10888.     13    background: #ccc;
    +
    10889.     13}
    +
    10890.     13.coverage .coverageLow,
    +
    10891.     13.coverage .uncovered {
    +
    10892.     13    background: #ebb;
    +
    10893.     13}
    +
    10894.     13.coverage .coverageMedium {
    +
    10895.     13    background: #fd7;
    +
    10896.     13}
    +
    10897.     13.coverage .footer,
    +
    10898.     13.coverage .header,
    +
    10899.     13.coverage .lineno {
    +
    10900.     13    background: #ddd;
    +
    10901.     13}
    +
    10902.     13.coverage .percentbar {
    +
    10903.     13    background: #999;
    +
    10904.     13}
    +
    10905.     13.coverage .percentbar div {
    +
    10906.     13    background: #666;
    +
    10907.     13}
    +
    10908.     13
    +
    10909.     13/* css - coverage_report - important */
    +
    10910.     13.coverage pre:hover span,
    +
    10911.     13.coverage tr:hover td {
    +
    10912.     13    background: #7d7;
    +
    10913.     13}
    +
    10914.     13.coverage pre:hover span.uncovered,
    +
    10915.     13.coverage tr:hover td.coverageLow {
    +
    10916.     13    background: #f99;
    +
    10917.     13}
    +
    10918.     13</style>
    +
    10919.     13</head>
    +
    10920.     13<body class="coverage">
    +
    10921.     13<!-- header start -->
    +
    10922.     13<div class="header">
    +
    10923.     13<div class="title">V8 Coverage Report</div>
    +
    10924.     13<table>
    +
    10925.     13<thead>
    +
    10926.     13    <tr>
    +
    10927.     13    <th>Files covered</th>
    +
    10928.     13    <th>Lines</th>
    +
    10929.     13    <th>Remaining</th>
    +
    10930.     13    </tr>
    +
    10931.     13</thead>
    +
    10932.     13<tbody>
    +
    10933.     13        `).trim() + "\n";
    +
    10934.      7        if (modeIndex) {
    +
    10935.      7            padLines = String("(ignore) 100.00 %").length;
    +
    10936.      7            padPathname = 32;
    +
    10937.      7            fileList.unshift({
    +
    10938.      7                linesCovered: 0,
    +
    10939.      7                linesTotal: 0,
    +
    10940.      7                modeCoverageIgnoreFile: "",
    +
    10941.      7                pathname: "./"
    +
    10942.      7            });
    +
    10943.      7            fileList.slice(1).forEach(function ({
    +
    10944.      7                linesCovered,
    +
    10945.      7                linesTotal,
    +
    10946.      7                modeCoverageIgnoreFile,
    +
    10947.      7                pathname
    +
    10948.      7            }) {
    +
    10949.      7                if (!modeCoverageIgnoreFile) {
    +
    10950.      7                    fileList[0].linesCovered += linesCovered;
    +
    10951.      7                    fileList[0].linesTotal += linesTotal;
    +
    10952.      7                }
    +
    10953.      7                padPathname = Math.max(padPathname, pathname.length + 2);
    +
    10954.      7                padLines = Math.max(
    +
    10955.      7                    padLines,
    +
    10956.      7                    String(linesCovered + " / " + linesTotal).length
    +
    10957.      7                );
    +
    10958.      7            });
    +
    10959.      7        }
    +
    10960.     13        txtBorder = (
    +
    10961.     13            "+" + "-".repeat(padPathname + 2) + "+"
    +
    10962.     13            + "-".repeat(padLines + 2) + "+"
    +
    10963.     13            + "-".repeat(padLines + 2) + "+\n"
    +
    10964.     13        );
    +
    10965.     13        txt = "";
    +
    10966.     13        txt += "V8 Coverage Report\n";
    +
    10967.     13        txt += txtBorder;
    +
    10968.     13        txt += (
    +
    10969.     13            "| " + String("Files covered").padEnd(padPathname, " ") + " | "
    +
    10970.     13            + String("Lines").padStart(padLines, " ") + " | "
    +
    10971.     13            + String("Remaining").padStart(padLines, " ") + " |\n"
    +
    10972.     13        );
    +
    10973.     13        txt += txtBorder;
    +
    10974.     19        fileList.forEach(function ({
    +
    10975.     19            linesCovered,
    +
    10976.     19            linesTotal,
    +
    10977.     19            modeCoverageIgnoreFile,
    +
    10978.     19            pathname
    +
    10979.     19        }, ii) {
    +
    10980.     19            let coverageLevel;
    +
    10981.     19            let coveragePct;
    +
    10982.     19            let fill;
    +
    10983.     19            let str1;
    +
    10984.     19            let str2;
    +
    10985.     19            let xx1;
    +
    10986.     19            let xx2;
    +
    10987.      2            coveragePct = Math.floor(10000 * linesCovered / linesTotal || 0);
    +
    10988.     19            coverageLevel = (
    +
    10989.     19                modeCoverageIgnoreFile
    +
    10990.      2                ? "coverageIgnore"
    +
    10991.     17                : coveragePct >= 8000
    +
    10992.     17                ? "coverageHigh"
    +
    10993.     17                : coveragePct >= 5000
    +
    10994.     17                ? "coverageMedium"
    +
    10995.     17                : "coverageLow"
    +
    10996.     19            );
    +
    10997.     19            coveragePct = String(coveragePct).replace((
    +
    10998.     19                /..$/m
    +
    10999.     19            ), ".$&");
    +
    11000.     13            if (modeIndex && ii === 0) {
    +
    11001.      7                fill = (
    +
    11002.      7
    +
    11003.      7// Badge-color rgb-red.
    +
    11004.      7
    +
    11005.      7                    "#" + Math.round(
    +
    11006.      7                        (100 - Number(coveragePct)) * 2.21
    +
    11007.      7                    ).toString(16).padStart(2, "0")
    +
    11008.      7
    +
    11009.      7// Badge-color rgb-green.
    +
    11010.      7
    +
    11011.      7                    + Math.round(
    +
    11012.      7                        Number(coveragePct) * 2.21
    +
    11013.      7                    ).toString(16).padStart(2, "0")
    +
    11014.      7
    +
    11015.      7// Badge-color rgb-blue.
    +
    11016.      7
    +
    11017.      7                    + "00"
    +
    11018.      7                );
    +
    11019.      7                str1 = "coverage";
    +
    11020.      7                str2 = coveragePct + " %";
    +
    11021.      7                xx1 = 6 * str1.length + 20;
    +
    11022.      7                xx2 = 6 * str2.length + 20;
    +
    11023.      7
    +
    11024.      7// Fs - write coverage_badge.svg.
    +
    11025.      7
    +
    11026.      7                promiseList.push(fsWriteFileWithParents((
    +
    11027.      7                    coverageDir + "coverage_badge.svg"
    +
    11028.      7                ), String(`
    +
    11029.      7<svg height="20" width="${xx1 + xx2}" xmlns="http://www.w3.org/2000/svg">
    +
    11030.      7<rect fill="#555" height="20" width="${xx1 + xx2}"/>
    +
    11031.      7<rect fill="${fill}" height="20" width="${xx2}" x="${xx1}"/>
    +
    11032.      7<g
    +
    11033.      7    fill="#fff"
    +
    11034.      7    font-family="verdana, geneva, dejavu sans, sans-serif"
    +
    11035.      7    font-size="11"
    +
    11036.      7    font-weight="bold"
    +
    11037.      7    text-anchor="middle"
    +
    11038.      7>
    +
    11039.      7<text x="${0.5 * xx1}" y="14">${str1}</text>
    +
    11040.      7<text x="${xx1 + 0.5 * xx2}" y="14">${str2}</text>
    +
    11041.      7</g>
    +
    11042.      7</svg>
    +
    11043.      7                `).trim() + "\n"));
    +
    11044.      7                pathname = "";
    +
    11045.      7            }
    +
    11046.     19            txt += (
    +
    11047.     19                "| "
    +
    11048.     19                + String("./" + pathname).padEnd(padPathname, " ") + " | "
    +
    11049.     19                + String(
    +
    11050.     19                    modeCoverageIgnoreFile + " " + coveragePct + " %"
    +
    11051.     19                ).padStart(padLines, " ") + " | "
    +
    11052.     19                + " ".repeat(padLines) + " |\n"
    +
    11053.     19            );
    +
    11054.     19            txt += (
    +
    11055.     19                "| " + "*".repeat(
    +
    11056.     19                    Math.round(0.01 * coveragePct * padPathname)
    +
    11057.     19                ).padEnd(padPathname, "_") + " | "
    +
    11058.     19                + String(
    +
    11059.     19                    linesCovered + " / " + linesTotal
    +
    11060.     19                ).padStart(padLines, " ") + " | "
    +
    11061.     19                + String(
    +
    11062.     19                    (linesTotal - linesCovered) + " / " + linesTotal
    +
    11063.     19                ).padStart(padLines, " ") + " |\n"
    +
    11064.     19            );
    +
    11065.     19            txt += txtBorder;
    +
    11066.     19            pathname = htmlEscape(pathname);
    +
    11067.     19
    +
    11068.     19// CL-37251d17 - Bugfix - Fix incorrect http-link to index.html.
    +
    11069.     19
    +
    11070.     19            html += String(`
    +
    11071.     19    <tr>
    +
    11072.     19    <td class="${coverageLevel}">
    +
    11073.     19            ${(
    +
    11074.     19                modeIndex
    +
    11075.     13                ? (
    +
    11076.     13                    "<a href=\"" + (pathname || "index") + ".html\">. / "
    +
    11077.     13                    + pathname + "</a><br>"
    +
    11078.     13                )
    +
    11079.      6                : (
    +
    11080.      6                    "<a href=\""
    +
    11081.      6                    + "../".repeat(pathname.split("/").length - 1)
    +
    11082.      6                    + "index.html\">. / </a>"
    +
    11083.      6                    + pathname + "<br>"
    +
    11084.      6                )
    +
    11085.     19            )}
    +
    11086.     19        <div class="percentbar">
    +
    11087.     19            <div style="width: ${coveragePct}%;"></div>
    +
    11088.     19        </div>
    +
    11089.     19    </td>
    +
    11090.     19    <td style="text-align: right;">
    +
    11091.     19        ${modeCoverageIgnoreFile} ${coveragePct} %<br>
    +
    11092.     19        ${linesCovered} / ${linesTotal}
    +
    11093.     19    </td>
    +
    11094.     19    <td style="text-align: right;">
    +
    11095.     19        <br>
    +
    11096.     19        ${linesTotal - linesCovered} / ${linesTotal}
    +
    11097.     19    </td>
    +
    11098.     19    </tr>
    +
    11099.     19        `).trim() + "\n";
    +
    11100.     19        });
    +
    11101.     13        html += String(`
    +
    11102.     13</tbody>
    +
    11103.     13</table>
    +
    11104.     13</div>
    +
    11105.     13<!-- header end -->
    +
    11106.     13        `).trim() + "\n";
    +
    11107.      6        if (!modeIndex) {
    +
    11108.      6            html += String(`
    +
    11109.      6<!-- content start -->
    +
    11110.      6<div class="content">
    +
    11111.      6            `).trim() + "\n";
    +
    11112.  11853            lineList.forEach(function ({
    +
    11113.  11853                count,
    +
    11114.  11853                holeList,
    +
    11115.  11853                line,
    +
    11116.  11853                startOffset
    +
    11117.  11853            }, ii) {
    +
    11118.  11853                let chunk;
    +
    11119.  11853                let inHole;
    +
    11120.  11853                let lineHtml;
    +
    11121.  11853                let lineId;
    +
    11122.  11853                lineHtml = "";
    +
    11123.  11853                lineId = "line_" + (ii + 1);
    +
    11124.  11853                switch (count) {
    +
    11125.     32                case -1:
    +
    11126.  11219                case 0:
    +
    11127.  11219                    if (holeList.length === 0) {
    +
    11128.  11219                        lineHtml += "</span>";
    +
    11129.  11219                        lineHtml += "<span class=\"uncovered\">";
    +
    11130.  11219                        lineHtml += htmlEscape(line);
    +
    11131.  11219                        break;
    +
    11132.  11219                    }
    +
    11133.  11219                    line = line.split("").map(function (char) {
    +
    11134.  11219                        return {
    +
    11135.  11219                            char,
    +
    11136.  11219                            isHole: undefined
    +
    11137.  11219                        };
    +
    11138.  11219                    });
    +
    11139.  11219                    holeList.forEach(function ([
    +
    11140.  11219                        aa, bb
    +
    11141.  11219                    ]) {
    +
    11142.  11219                        aa = Math.max(aa - startOffset, 0);
    +
    11143.  11219                        bb = Math.min(bb - startOffset, line.length);
    +
    11144.  11219                        while (aa < bb) {
    +
    11145.  11219                            line[aa].isHole = true;
    +
    11146.  11219                            aa += 1;
    +
    11147.  11219                        }
    +
    11148.  11219                    });
    +
    11149.  11219                    chunk = "";
    +
    11150.  11219                    line.forEach(function ({
    +
    11151.  11219                        char,
    +
    11152.  11219                        isHole
    +
    11153.  11219                    }) {
    +
    11154.  11219                        if (inHole !== isHole) {
    +
    11155.  11219                            lineHtml += htmlEscape(chunk);
    +
    11156.  11219                            lineHtml += "</span><span";
    +
    11157.  11219
    +
    11158.  11219// Coverage-hack - Ugly-hack around possible deadcode where isHole is always
    +
    11159.  11219// true.
    +
    11160.  11219
    +
    11161.  11219                            if (isHole) {
    +
    11162.  11219                                lineHtml += " class=\"uncovered\"";
    +
    11163.  11219                            }
    +
    11164.  11219                            lineHtml += ">";
    +
    11165.  11219                            chunk = "";
    +
    11166.  11219                            inHole = isHole;
    +
    11167.  11219                        }
    +
    11168.  11219                        chunk += char;
    +
    11169.  11219                    });
    +
    11170.  11219                    lineHtml += htmlEscape(chunk);
    +
    11171.  11219                    break;
    +
    11172.    634                default:
    +
    11173.    634                    lineHtml += htmlEscape(line);
    +
    11174.  11853                }
    +
    11175.  11853                html += String(`
    +
    11176.  11853<pre>
    +
    11177.  11853<span class="lineno">
    +
    11178.  11853<a href="#${lineId}" id="${lineId}">${String(ii + 1).padStart(5, " ")}.</a>
    +
    11179.  11853</span>
    +
    11180.  11853<span class="count
    +
    11181.  11853                ${(
    +
    11182.  11853                    count <= 0
    +
    11183.  11219                    ? "uncovered"
    +
    11184.    634                    : ""
    +
    11185.  11853                )}"
    +
    11186.  11853>
    +
    11187.  11187${String(count || "-0").padStart(7, " ")}
    +
    11188.  11853</span>
    +
    11189.  11853<span>${lineHtml}</span>
    +
    11190.  11853</pre>
    +
    11191.  11853                `).replace((
    +
    11192.  11853                    /\n/g
    +
    11193.  11853                ), "").trim() + "\n";
    +
    11194.  11853            });
    +
    11195.      6            html += String(`
    +
    11196.      6</div>
    +
    11197.      6<!-- content end -->
    +
    11198.      6            `).trim() + "\n";
    +
    11199.      6        }
    +
    11200.     13        html += String(`
    +
    11201.     13<div class="footer">
    +
    11202.     13    [
    +
    11203.     13    This document was created with
    +
    11204.     13    <a href="https://github.com/jslint-org/jslint">JSLint</a>
    +
    11205.     13    ]
    +
    11206.     13</div>
    +
    11207.     13</body>
    +
    11208.     13</html>
    +
    11209.     13        `).trim() + "\n";
    +
    11210.     13
    +
    11211.     13// Fs - write <file>.html.
    +
    11212.     13
    +
    11213.     13        promiseList.push(fsWriteFileWithParents(pathname + ".html", html));
    +
    11214.      6        if (!modeIndex) {
    +
    11215.      6            return;
    +
    11216.      7        }
    +
    11217.      7
    +
    11218.      7// Fs - write coverage_report.txt.
    +
    11219.      7
    +
    11220.      7        consoleError("\n" + txt);
    +
    11221.      7        promiseList.push(fsWriteFileWithParents((
    +
    11222.      7            coverageDir + "coverage_report.txt"
    +
    11223.      7        ), txt));
    +
    11224.      7    }
    +
    11225.      8
    +
    11226.      8/*
    +
    11227.      8function sentinel() {}
    +
    11228.      8*/
    +
    11229.      8
    +
    11230.      8    await moduleFsInit();
    +
    11231.      1    consoleError = consoleError || console.error;
    +
    11232.      8    cwd = process.cwd().replace((
    +
    11233.      8        /\\/g
    +
    11234.      8    ), "/") + "/";
    +
    11235.      8
    +
    11236.      8// Init coverageDir.
    +
    11237.      8// Assert coverageDir is subdirectory of cwd.
    +
    11238.      8
    +
    11239.      8    assertOrThrow(coverageDir, "invalid coverageDir " + coverageDir);
    +
    11240.      8
    +
    11241.      8// CL-61b11012 - coverage - Relax requirement for coverageDir to be in cwd.
    +
    11242.      8//     assertOrThrow(
    +
    11243.      8//         pathnameRelativeCwd(coverageDir),
    +
    11244.      8//         "coverageDir " + coverageDir + " is not subdirectory of cwd " + cwd
    +
    11245.      8//     );
    +
    11246.      8
    +
    11247.      8    coverageDir = modulePath.resolve(coverageDir).replace((
    +
    11248.      8        /\\/g
    +
    11249.      8    ), "/") + "/";
    +
    11250.      8
    +
    11251.      8    processArgv = processArgv.slice();
    +
    11252.      9    while (processArgv[0] && processArgv[0][0] === "-") {
    +
    11253.      3        processArgElem = processArgv.shift().split("=");
    +
    11254.      3        processArgElem[1] = processArgElem.slice(1).join("=");
    +
    11255.      3        switch (processArgElem[0]) {
    +
    11256.      3
    +
    11257.      3// PR-371 - Add cli-option `--exclude=...`.
    +
    11258.      3
    +
    11259.      3        case "--exclude":
    +
    11260.      3            excludeList.push(processArgElem[1]);
    +
    11261.      3            break;
    +
    11262.      3
    +
    11263.      3// PR-371 - Add cli-option `--include=...`
    +
    11264.      3
    +
    11265.      3        case "--include":
    +
    11266.      3            includeList.push(processArgElem[1]);
    +
    11267.      3            break;
    +
    11268.      3
    +
    11269.      3// PR-400
    +
    11270.      3// Disable default-coverage of directory `node_modules`,
    +
    11271.      3// but allow override with cli-option `--include-node-modules=1`.
    +
    11272.      3
    +
    11273.      3        case "--include-node-modules":
    +
    11274.      3            modeIncludeNodeModules = !(
    +
    11275.      3                /0|false|null|undefined/
    +
    11276.      3            ).test(processArgElem[1]);
    +
    11277.      3            break;
    +
    11278.      3        }
    +
    11279.      7    }
    +
    11280.      7
    +
    11281.      7// 1. Spawn node.js program <processArgv> with coverage
    +
    11282.      7
    +
    11283.      7    if (processArgv.length > 0) {
    +
    11284.      6
    +
    11285.      6// Remove old coverage-files.
    +
    11286.      6
    +
    11287.      6        await fsWriteFileWithParents(coverageDir + "/touch.txt", "");
    +
    11288.      6        await Promise.all(Array.from(
    +
    11289.      6            await moduleFs.promises.readdir(coverageDir)
    +
    11290.     11        ).map(async function (file) {
    +
    11291.     11            if ((
    +
    11292.     11                /^coverage-\d+?-\d+?-\d+?\.json$/
    +
    11293.      6            ).test(file)) {
    +
    11294.      6                consoleError("rm file " + coverageDir + file);
    +
    11295.      6                await moduleFs.promises.unlink(coverageDir + file);
    +
    11296.      6            }
    +
    11297.     11        }));
    +
    11298.      6        exitCode = await new Promise(function (resolve) {
    +
    11299.      6            let processArgv0 = processArgv[0];
    +
    11300.      6
    +
    11301.      6// If win32 environment, then replace program npm with npm.cmd.
    +
    11302.      6// Coverage-hack - Ugly-hack to get test-coverage under both win32 and linux.
    +
    11303.      6
    +
    11304.      6            if (processArgv0 === "npm") {
    +
    11305.      6                processArgv0 = process.platform.replace(
    +
    11306.      6                    "win32",
    +
    11307.      6                    "npm.cmd"
    +
    11308.      6                ).replace(
    +
    11309.      6                    process.platform,
    +
    11310.      6                    "npm"
    +
    11311.      6                );
    +
    11312.      6            }
    +
    11313.      6            moduleChildProcess.spawn(
    +
    11314.      6                processArgv0,
    +
    11315.      6                processArgv.slice(1),
    +
    11316.      6                {
    +
    11317.      6                    env: Object.assign({}, process.env, {
    +
    11318.      6                        NODE_V8_COVERAGE: coverageDir
    +
    11319.      6                    }),
    +
    11320.      6
    +
    11321.      6// PR-465
    +
    11322.      6// https://nodejs.org/en/blog/vulnerability/april-2024-security-releases-2
    +
    11323.      6// Node.js will now error with EINVAL if a .bat or .cmd file is passed to
    +
    11324.      6// child_process.spawn and child_process.spawnSync without the shell option set.
    +
    11325.      6
    +
    11326.      6                    shell: (
    +
    11327.      6                        processArgv0.endsWith(".bat")
    +
    11328.      6                        || processArgv0.endsWith(".cmd")
    +
    11329.      6                    ),
    +
    11330.      6                    stdio: ["ignore", 1, 2]
    +
    11331.      6                }
    +
    11332.      6            ).on("exit", resolve);
    +
    11333.      6        });
    +
    11334.      6        consoleError(
    +
    11335.      6            `v8CoverageReportCreate - program exited with exitCode=${exitCode}`
    +
    11336.      6        );
    +
    11337.      7    }
    +
    11338.      7
    +
    11339.      7// 2. Merge JSON v8-coverage-files in <coverageDir>.
    +
    11340.      7
    +
    11341.      7    consoleError("v8CoverageReportCreate - merging coverage files...");
    +
    11342.      7    v8CoverageObj = await moduleFs.promises.readdir(coverageDir);
    +
    11343.     18    v8CoverageObj = v8CoverageObj.filter(function (file) {
    +
    11344.     18        return (
    +
    11345.     18            /^coverage-\d+?-\d+?-\d+?\.json$/
    +
    11346.     18        ).test(file);
    +
    11347.     18    });
    +
    11348.      7    v8CoverageObj = await Promise.all(v8CoverageObj.map(async function (file) {
    +
    11349.      7        let data;
    +
    11350.      7        let pathnameDict = Object.create(null);
    +
    11351.      7        data = await moduleFs.promises.readFile(coverageDir + file, "utf8");
    +
    11352.      7        data = JSON.parse(data);
    +
    11353.    473        data.result.forEach(function (scriptCov) {
    +
    11354.    473            let pathname = scriptCov.url;
    +
    11355.    473
    +
    11356.    473// Filter out internal coverages.
    +
    11357.    473
    +
    11358.    393            if (!pathname.startsWith("file:///")) {
    +
    11359.    393                return;
    +
    11360.    393            }
    +
    11361.     80
    +
    11362.     80// Normalize pathname.
    +
    11363.     80
    +
    11364.     80            pathname = moduleUrl.fileURLToPath(pathname);
    +
    11365.     80            pathname = modulePath.resolve(pathname).replace((
    +
    11366.     80                /\\/g
    +
    11367.     80            ), "/");
    +
    11368.     80
    +
    11369.     80// Filter files outside of cwd.
    +
    11370.     80
    +
    11371.     80            if (pathname.indexOf("[") >= 0 || !pathname.startsWith(cwd)) {
    +
    11372.     74                return;
    +
    11373.     74            }
    +
    11374.      7
    +
    11375.      7// Normalize pathname relative to cwd.
    +
    11376.      7
    +
    11377.      7            pathname = pathname.slice(cwd.length);
    +
    11378.      7            scriptCov.url = pathname;
    +
    11379.      7            pathnameDict[pathname] = scriptCov;
    +
    11380.      7        });
    +
    11381.      7
    +
    11382.      7// PR-400 - Filter directory `node_modules`.
    +
    11383.      7
    +
    11384.      7        if (!modeIncludeNodeModules) {
    +
    11385.      7            excludeList.push("node_modules/");
    +
    11386.      7        }
    +
    11387.      7
    +
    11388.      7// PR-400 - Filter files by glob-patterns in excludeList, includeList.
    +
    11389.      7
    +
    11390.      7        data.result = globExclude({
    +
    11391.      7            excludeList,
    +
    11392.      7            includeList,
    +
    11393.      7            pathnameList: Object.keys(pathnameDict)
    +
    11394.      7        }).pathnameList.map(function (pathname) {
    +
    11395.      7            return pathnameDict[pathname];
    +
    11396.      7        });
    +
    11397.      7        return data;
    +
    11398.      7    }));
    +
    11399.      7
    +
    11400.      7// Merge v8CoverageObj.
    +
    11401.      7
    +
    11402.      7    v8CoverageObj = v8CoverageListMerge(v8CoverageObj);
    +
    11403.      7
    +
    11404.      7// Debug v8CoverageObj.
    +
    11405.      7
    +
    11406.      7    await fsWriteFileWithParents(
    +
    11407.      7        coverageDir + "v8_coverage_merged.json",
    +
    11408.      7        JSON.stringify(v8CoverageObj, undefined, 1)
    +
    11409.      7    );
    +
    11410.      7
    +
    11411.      7// 3. Create html-coverage-reports in <coverageDir>.
    +
    11412.      7
    +
    11413.      7    consoleError("v8CoverageReportCreate - creating html-coverage-report...");
    +
    11414.      7    fileDict = Object.create(null);
    +
    11415.      7    await Promise.all(v8CoverageObj.result.map(async function ({
    +
    11416.      7        functions,
    +
    11417.      7        url: pathname
    +
    11418.      7    }) {
    +
    11419.      7        let lineList;
    +
    11420.      7        let linesCovered;
    +
    11421.      7        let linesTotal;
    +
    11422.      7        let source;
    +
    11423.      7        source = await moduleFs.promises.readFile(pathname, "utf8");
    +
    11424.      7        lineList = [{}];
    +
    11425.      7        source.replace((
    +
    11426.      7            /^.*$/gm
    +
    11427.  11853        ), function (line, startOffset) {
    +
    11428.  11853            lineList[lineList.length - 1].endOffset = startOffset - 1;
    +
    11429.  11853            lineList.push({
    +
    11430.  11853                count: -1,
    +
    11431.  11853                endOffset: 0,
    +
    11432.  11853                holeList: [],
    +
    11433.  11853                line,
    +
    11434.  11853                startOffset
    +
    11435.  11853            });
    +
    11436.  11853            return "";
    +
    11437.  11853        });
    +
    11438.      7        lineList.shift();
    +
    11439.      7        lineList[lineList.length - 1].endOffset = source.length;
    +
    11440.     41        functions.reverse().forEach(function ({
    +
    11441.     41            ranges
    +
    11442.     41        }) {
    +
    11443.     65            ranges.reverse().forEach(function ({
    +
    11444.     65                count,
    +
    11445.     65                endOffset,
    +
    11446.     65                startOffset
    +
    11447.     65            }, ii, list) {
    +
    11448. 580047                lineList.forEach(function (elem) {
    +
    11449. 580047                    if (!(
    +
    11450. 580047                        (
    +
    11451. 580047                            elem.startOffset <= startOffset
    +
    11452. 205728                            && startOffset <= elem.endOffset
    +
    11453. 579982                        ) || (
    +
    11454. 579982                            elem.startOffset <= endOffset
    +
    11455. 579982                            && endOffset <= elem.endOffset
    +
    11456. 579982                        ) || (
    +
    11457. 579926                            startOffset <= elem.startOffset
    +
    11458. 579926                            && elem.endOffset <= endOffset
    +
    11459. 579926                        )
    +
    11460. 556497                    )) {
    +
    11461. 556497                        return;
    +
    11462. 556497                    }
    +
    11463.  23550
    +
    11464.  23550// Handle tree-root.
    +
    11465.  23550
    +
    11466.  23550                    if (ii + 1 === list.length) {
    +
    11467.  23281                        if (elem.count === -1) {
    +
    11468.  23281                            elem.count = count;
    +
    11469.  23281                        }
    +
    11470.  23281                        return;
    +
    11471.  23281                    }
    +
    11472.    269
    +
    11473.    269// Handle tree-children.
    +
    11474.    269
    +
    11475.    269                    if (elem.count !== 0) {
    +
    11476.    170                        elem.count = Math.max(count, elem.count);
    +
    11477.    269                    }
    +
    11478.    269                    if (count === 0) {
    +
    11479.    203                        elem.count = 0;
    +
    11480.    203                        elem.holeList.push([
    +
    11481.    203                            startOffset, endOffset
    +
    11482.    203                        ]);
    +
    11483.    203                    }
    +
    11484. 580047                });
    +
    11485.     65            });
    +
    11486.     41        });
    +
    11487.      7        linesTotal = lineList.length;
    +
    11488.  11853        linesCovered = lineList.filter(function ({
    +
    11489.  11853            count
    +
    11490.  11853        }) {
    +
    11491.  11853            return count > 0;
    +
    11492.  11853        }).length;
    +
    11493.      7        await moduleFs.promises.mkdir((
    +
    11494.      7            modulePath.dirname(coverageDir + pathname)
    +
    11495.      7        ), {
    +
    11496.      7            recursive: true
    +
    11497.      7        });
    +
    11498.      7        fileDict[pathname] = {
    +
    11499.      7            lineList,
    +
    11500.      7            linesCovered,
    +
    11501.      7            linesTotal,
    +
    11502.      7            modeCoverageIgnoreFile: (
    +
    11503.      7                (
    +
    11504.      7                    /^\/\*coverage-ignore-file\*\/$/m
    +
    11505.      7                ).test(source.slice(0, 65536))
    +
    11506.      7                ? "(ignore)"
    +
    11507.      7                : ""
    +
    11508.      7            ),
    +
    11509.      7            pathname
    +
    11510.      7        };
    +
    11511.      7        htmlRender({
    +
    11512.      7            fileList: [
    +
    11513.      7                fileDict[pathname]
    +
    11514.      7            ],
    +
    11515.      7            lineList,
    +
    11516.      7            pathname: coverageDir + pathname
    +
    11517.      7        });
    +
    11518.      7    }));
    +
    11519.      7    htmlRender({
    +
    11520.      7        fileList: Object.keys(fileDict).sort().map(function (pathname) {
    +
    11521.      7            return fileDict[pathname];
    +
    11522.      7        }),
    +
    11523.      7        modeIndex: true,
    +
    11524.      7        pathname: coverageDir + "index"
    +
    11525.      7    });
    +
    11526.      7    await Promise.all(promiseList);
    +
    11527.      7    assertOrThrow(
    +
    11528.      7        exitCode === 0,
    +
    11529.      7        "v8CoverageReportCreate - nonzero exitCode " + exitCode
    +
    11530.      7    );
    +
    11531.      7}
    +
    11532.      1
    +
    11533.      1/*
    +
    11534.      1function sentinel() {}
    +
    11535.      1*/
    +
    11536.      1
    +
    11537.      1// Export jslint as cjs/esm.
    +
    11538.      1
    +
    11539.      1jslint_export = Object.freeze(Object.assign(jslint, {
    +
    11540.      1    assertErrorThrownAsync,
    +
    11541.      1    assertJsonEqual,
    +
    11542.      1    assertOrThrow,
    +
    11543.      1    debugInline,
    +
    11544.      1    fsWriteFileWithParents,
    +
    11545.      1    globExclude,
    +
    11546.      1    htmlEscape,
    +
    11547.      1    jslint,
    +
    11548.      1    jslint_apidoc,
    +
    11549.      1    jslint_assert,
    +
    11550.      1    jslint_charset_ascii,
    +
    11551.      1    jslint_cli,
    +
    11552.      1    jslint_edition,
    +
    11553.      1    jslint_phase1_split,
    +
    11554.      1    jslint_phase2_lex,
    +
    11555.      1    jslint_phase3_parse,
    +
    11556.      1    jslint_phase4_walk,
    +
    11557.      1    jslint_phase5_whitage,
    +
    11558.      1    jslint_report,
    +
    11559.      1    jstestDescribe,
    +
    11560.      1    jstestIt,
    +
    11561.      1    jstestOnExit,
    +
    11562.      1    moduleFsInit,
    +
    11563.      1    noop,
    +
    11564.      1    objectDeepCopyWithKeysSorted,
    +
    11565.      1    v8CoverageListMerge,
    +
    11566.      1    v8CoverageReportCreate
    +
    11567.      1}));
    +
    11568.      1// module.exports = jslint_export;              // Export jslint as cjs.
    +
    11569.      1export default Object.freeze(jslint_export);    // Export jslint as esm.
    +
    11570.      1jslint_import_meta_url = import.meta.url;
    +
    11571.      1
    +
    11572.      1// Run jslint_cli.
    +
    11573.      1jslint_cli({});
    +
    11574.      1
    +
    + + + + diff --git a/branch-alpha/.artifact/coverage/jslint_wrapper_cjs.cjs.html b/branch-alpha/.artifact/coverage/jslint_wrapper_cjs.cjs.html new file mode 100644 index 000000000..7e2512946 --- /dev/null +++ b/branch-alpha/.artifact/coverage/jslint_wrapper_cjs.cjs.html @@ -0,0 +1,217 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / jslint_wrapper_cjs.cjs
    +
    +
    +
    +
    + 100.00 %
    + 49 / 49 +
    +
    + 0 / 49 +
    +
    + + +
    +
        1.      1// The Unlicense
    +
        2.      1//
    +
        3.      1// This is free and unencumbered software released into the public domain.
    +
        4.      1//
    +
        5.      1// Anyone is free to copy, modify, publish, use, compile, sell, or
    +
        6.      1// distribute this software, either in source code form or as a compiled
    +
        7.      1// binary, for any purpose, commercial or non-commercial, and by any
    +
        8.      1// means.
    +
        9.      1//
    +
       10.      1// In jurisdictions that recognize copyright laws, the author or authors
    +
       11.      1// of this software dedicate any and all copyright interest in the
    +
       12.      1// software to the public domain. We make this dedication for the benefit
    +
       13.      1// of the public at large and to the detriment of our heirs and
    +
       14.      1// successors. We intend this dedication to be an overt act of
    +
       15.      1// relinquishment in perpetuity of all present and future rights to this
    +
       16.      1// software under copyright law.
    +
       17.      1//
    +
       18.      1// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    +
       19.      1// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    +
       20.      1// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    +
       21.      1// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
    +
       22.      1// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
    +
       23.      1// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    +
       24.      1// OTHER DEALINGS IN THE SOFTWARE.
    +
       25.      1
    +
       26.      1
    +
       27.      1/*jslint beta, node*/
    +
       28.      1/*property
    +
       29.      1    module, readFileSync, replace, runInNewContext
    +
       30.      1*/
    +
       31.      1require("vm").runInNewContext(
    +
       32.      1    (
    +
       33.      1        "\"use strict\";"
    +
       34.      1        + require("fs").readFileSync(
    +
       35.      1            __dirname + "/jslint.mjs",
    +
       36.      1            "utf8"
    +
       37.      1        ).replace(
    +
       38.      1            "\nexport default Object.freeze(jslint_export);",
    +
       39.      1            "\nmodule.exports = jslint_export;"
    +
       40.      1        ).replace(
    +
       41.      1            "\njslint_import_meta_url = import.meta.url;",
    +
       42.      1            "\n// jslint_import_meta_url = import.meta.url;"
    +
       43.      1        )
    +
       44.      1    ),
    +
       45.      1    {
    +
       46.      1        module
    +
       47.      1    }
    +
       48.      1);
    +
       49.      1
    +
    + + + + diff --git a/branch-alpha/.artifact/coverage/test.mjs.html b/branch-alpha/.artifact/coverage/test.mjs.html new file mode 100644 index 000000000..455e80af5 --- /dev/null +++ b/branch-alpha/.artifact/coverage/test.mjs.html @@ -0,0 +1,1744 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test.mjs
    +
    +
    +
    +
    + 100.00 %
    + 1576 / 1576 +
    +
    + 0 / 1576 +
    +
    + + +
    +
        1.      1/*jslint beta, node*/
    +
        2.      1import jslint from "./jslint.mjs";
    +
        3.      1import jslintCjs from "./jslint_wrapper_cjs.cjs";
    +
        4.      1import moduleFs from "fs";
    +
        5.      1import modulePath from "path";
    +
        6.      1
    +
        7.      1let {
    +
        8.      1    assertErrorThrownAsync,
    +
        9.      1    assertJsonEqual,
    +
       10.      1    assertOrThrow,
    +
       11.      1    debugInline,
    +
       12.      1    fsWriteFileWithParents,
    +
       13.      1    globExclude,
    +
       14.      1    jstestDescribe,
    +
       15.      1    jstestIt,
    +
       16.      1    jstestOnExit,
    +
       17.      1    moduleFsInit,
    +
       18.      1    noop,
    +
       19.      1    v8CoverageListMerge,
    +
       20.      1    v8CoverageReportCreate
    +
       21.      1} = jslint;
    +
       22.      1let sourceJslintMjs;
    +
       23.      1let testCoverageMergeData;
    +
       24.      1
    +
       25.      1await (async function init() {
    +
       26.      1
    +
       27.      1// Coverage-hack - Ugly-hack to get test-coverage for all initialization-states.
    +
       28.      1
    +
       29.      1    moduleFsInit();
    +
       30.      1    moduleFsInit();
    +
       31.      1
    +
       32.      1// Cleanup directory .tmp
    +
       33.      1
    +
       34.      1    await moduleFs.promises.rm(".tmp", {
    +
       35.      1        recursive: true
    +
       36.      1    }).catch(noop);
    +
       37.      1
    +
       38.      1// init sourceJslintMjs
    +
       39.      1
    +
       40.      1    sourceJslintMjs = await moduleFs.promises.readFile("jslint.mjs", "utf8");
    +
       41.      1
    +
       42.      1// init testCoverageMergeData
    +
       43.      1
    +
       44.      1    testCoverageMergeData = JSON.parse(
    +
       45.      1        await moduleFs.promises.readFile(
    +
       46.      1            "test_coverage_merge_data.json",
    +
       47.      1            "utf8"
    +
       48.      1        )
    +
       49.      1    );
    +
       50.      1}());
    +
       51.      1
    +
       52.      1jstestDescribe((
    +
       53.      1    "test fsXxx handling-behavior"
    +
       54.      1), function testBehaviorFsXxx() {
    +
       55.      1    jstestIt((
    +
       56.      1        "test fsWriteFileWithParents handling-behavior"
    +
       57.      1    ), async function () {
    +
       58.      1        await Promise.all([
    +
       59.      1            1, 2, 3, 4
    +
       60.      4        ].map(async function () {
    +
       61.      4            await fsWriteFileWithParents(
    +
       62.      4                ".tmp/fsWriteFileWithParents/aa/bb/cc",
    +
       63.      4                "aa"
    +
       64.      4            );
    +
       65.      4        }));
    +
       66.      1        assertJsonEqual(
    +
       67.      1            await moduleFs.promises.readFile(
    +
       68.      1                ".tmp/fsWriteFileWithParents/aa/bb/cc",
    +
       69.      1                "utf8"
    +
       70.      1            ),
    +
       71.      1            "aa"
    +
       72.      1        );
    +
       73.      1    });
    +
       74.      1});
    +
       75.      1
    +
       76.      1jstestDescribe((
    +
       77.      1    "test globXxx handling-behavior"
    +
       78.      1), function testBehaviorGlobXxx() {
    +
       79.      1    jstestIt((
    +
       80.      1        "test globAssertNotWeird-error handling-behavior"
    +
       81.      1    ), async function () {
    +
       82.      1        await Promise.all([
    +
       83.      1            "\n",
    +
       84.      1            "\r",
    +
       85.      1            "\u0000"
    +
       86.      3        ].map(async function (char) {
    +
       87.      3            await assertErrorThrownAsync(function () {
    +
       88.      3                return globExclude({
    +
       89.      3                    pathnameList: [
    +
       90.      3                        "aa",
    +
       91.      3                        `cc/${char}/dd`,
    +
       92.      3                        "bb"
    +
       93.      3                    ]
    +
       94.      3                });
    +
       95.      3            }, (
    +
       96.      3                "Weird character "
    +
       97.      3                + JSON.stringify(char).replace("\\", "\\\\")
    +
       98.      3                + " found in "
    +
       99.      3            ));
    +
      100.      3        }));
    +
      101.      1    });
    +
      102.      1    jstestIt((
    +
      103.      1        "test globExclude handling-behavior"
    +
      104.      1    ), function () {
    +
      105.      1        let pathnameList = [
    +
      106.      1            ".dockerignore",
    +
      107.      1            ".eslintrc.js",
    +
      108.      1            ".gitignore",
    +
      109.      1            ".npmignore",
    +
      110.      1            ".travis.yml",
    +
      111.      1            "/node_modules/aa/bb/cc.js",
    +
      112.      1            "/node_modules/aa/bb/dd.js",
    +
      113.      1            "CHANGELOG.md",
    +
      114.      1            "CONTRIBUTING.md",
    +
      115.      1            "Dockerfile",
    +
      116.      1            "LICENSE",
    +
      117.      1            "Makefile",
    +
      118.      1            "README.md",
    +
      119.      1            "appveyor.yml",
    +
      120.      1            "benchmark/insert-transaction.sql",
    +
      121.      1            "benchmark/insert.js",
    +
      122.      1            "binding.gyp",
    +
      123.      1            "cloudformation/ci.template.js",
    +
      124.      1            "deps/common-sqlite.gypi",
    +
      125.      1            "deps/extract.py",
    +
      126.      1            "deps/sqlite-autoconf-3340000.tar.gz",
    +
      127.      1            "deps/sqlite3.gyp",
    +
      128.      1            "examples/simple-chaining.js",
    +
      129.      1            "lib/index.js",
    +
      130.      1            "lib/sqlite3-binding.js",
    +
      131.      1            "lib/sqlite3.js",
    +
      132.      1            "lib/trace.js",
    +
      133.      1            "node_modules/aa/bb/cc.js",
    +
      134.      1            "node_modules/aa/bb/dd.js",
    +
      135.      1            "package.json",
    +
      136.      1            "scripts/build-appveyor.bat",
    +
      137.      1            "scripts/build-local.bat",
    +
      138.      1            "scripts/build_against_electron.sh",
    +
      139.      1            "scripts/build_against_node.sh",
    +
      140.      1            "scripts/build_against_node_webkit.sh",
    +
      141.      1            "scripts/build_for_node_webkit.cmd",
    +
      142.      1            "scripts/install_node.sh",
    +
      143.      1            "scripts/validate_tag.sh",
    +
      144.      1            "sqlite3.js",
    +
      145.      1            "src/async.h",
    +
      146.      1            "src/backup.cc",
    +
      147.      1            "src/backup.h",
    +
      148.      1            "src/database.cc",
    +
      149.      1            "src/database.h",
    +
      150.      1            "src/gcc-preinclude.h",
    +
      151.      1            "src/macros.h",
    +
      152.      1            "src/node_sqlite3.cc",
    +
      153.      1            "src/statement.cc",
    +
      154.      1            "src/statement.h",
    +
      155.      1            "src/threading.h",
    +
      156.      1            "test/affected.test.js",
    +
      157.      1            "test/backup.test.js",
    +
      158.      1            "test/blob.test.js",
    +
      159.      1            "test/cache.test.js",
    +
      160.      1            "test/constants.test.js",
    +
      161.      1            "test/database_fail.test.js",
    +
      162.      1            "test/each.test.js",
    +
      163.      1            "test/exec.test.js",
    +
      164.      1            "test/extension.test.js",
    +
      165.      1            "test/fts-content.test.js",
    +
      166.      1            "test/interrupt.test.js",
    +
      167.      1            "test/issue-108.test.js",
    +
      168.      1            "test/json.test.js",
    +
      169.      1            "test/map.test.js",
    +
      170.      1            "test/named_columns.test.js",
    +
      171.      1            "test/named_params.test.js",
    +
      172.      1            "test/null_error.test.js",
    +
      173.      1            "test/nw/.gitignore",
    +
      174.      1            "test/nw/Makefile",
    +
      175.      1            "test/nw/index.html",
    +
      176.      1            "test/nw/package.json",
    +
      177.      1            "test/open_close.test.js",
    +
      178.      1            "test/other_objects.test.js",
    +
      179.      1            "test/parallel_insert.test.js",
    +
      180.      1            "test/prepare.test.js",
    +
      181.      1            "test/profile.test.js",
    +
      182.      1            "test/rerun.test.js",
    +
      183.      1            "test/scheduling.test.js",
    +
      184.      1            "test/serialization.test.js",
    +
      185.      1            "test/support/createdb-electron.js",
    +
      186.      1            "test/support/createdb.js",
    +
      187.      1            "test/support/elmo.png",
    +
      188.      1            "test/support/helper.js",
    +
      189.      1            "test/support/prepare.db",
    +
      190.      1            "test/support/script.sql",
    +
      191.      1            "test/trace.test.js",
    +
      192.      1            "test/unicode.test.js",
    +
      193.      1            "test/upsert.test.js",
    +
      194.      1            "test/verbose.test.js",
    +
      195.      1            "tools/docker/architecture/linux-arm/Dockerfile",
    +
      196.      1            "tools/docker/architecture/linux-arm/run.sh",
    +
      197.      1            "tools/docker/architecture/linux-arm64/Dockerfile",
    +
      198.      1            "tools/docker/architecture/linux-arm64/run.sh"
    +
      199.      1        ];
    +
      200.      1        [
    +
      201.      1            "tes?/",
    +
      202.      1            "tes[-t-]/",
    +
      203.      1            "tes[-t]/",
    +
      204.      1            "tes[0-9A-Z_a-z-]/",
    +
      205.      1            "tes[t-]/",
    +
      206.      1            "test/**/*.js"
    +
      207.      6        ].forEach(function (aa) {
    +
      208.      6            [
    +
      209.      6                "li*/*.js",
    +
      210.      6                "li?/*.js",
    +
      211.      6                "lib/",
    +
      212.      6                "lib/*",
    +
      213.      6                "lib/**/*.js",
    +
      214.      6                "lib/*.js"
    +
      215.     36            ].forEach(function (bb) {
    +
      216.     36                [
    +
      217.     36                    "",
    +
      218.     36                    "**/node_modules/",
    +
      219.     36                    "node_modules/"
    +
      220.    108                ].forEach(function (cc) {
    +
      221.    108                    assertJsonEqual(
    +
      222.    108                        globExclude({
    +
      223.    108                            excludeList: [
    +
      224.    108                                "tes[!0-9A-Z_a-z-]/",
    +
      225.    108                                "tes[^0-9A-Z_a-z-]/",
    +
      226.    108                                "test/suppor*/*elper.js",
    +
      227.    108                                "test/suppor?/?elper.js",
    +
      228.    108                                "test/support/helper.js"
    +
      229.    108                            ].concat(aa, cc),
    +
      230.    108                            includeList: [
    +
      231.    108                                "**/*.cjs",
    +
      232.    108                                "**/*.js",
    +
      233.    108                                "**/*.mjs",
    +
      234.    108                                "lib/sqlite3.js"
    +
      235.    108                            ].concat(bb),
    +
      236.    108                            pathnameList
    +
      237.    108                        }).pathnameList,
    +
      238.    108                        [
    +
      239.    108                            ".eslintrc.js",
    +
      240.    108                            "benchmark/insert.js",
    +
      241.    108                            "cloudformation/ci.template.js",
    +
      242.    108                            "examples/simple-chaining.js",
    +
      243.    108                            "lib/index.js",
    +
      244.    108                            "lib/sqlite3-binding.js",
    +
      245.    108                            "lib/sqlite3.js",
    +
      246.    108                            "lib/trace.js",
    +
      247.    108                            "sqlite3.js"
    +
      248.    108                        ].concat(
    +
      249.    108                            cc === "**/node_modules/"
    +
      250.     36                            ? [
    +
      251.     36                                "node_modules/aa/bb/cc.js",
    +
      252.     36                                "node_modules/aa/bb/dd.js"
    +
      253.     36                            ]
    +
      254.     72                            : cc === "node_modules/"
    +
      255.     72                            ? [
    +
      256.     72                                "/node_modules/aa/bb/cc.js",
    +
      257.     72                                "/node_modules/aa/bb/dd.js"
    +
      258.     72                            ]
    +
      259.     72                            : [
    +
      260.     72                                "/node_modules/aa/bb/cc.js",
    +
      261.     72                                "/node_modules/aa/bb/dd.js",
    +
      262.     72                                "node_modules/aa/bb/cc.js",
    +
      263.     72                                "node_modules/aa/bb/dd.js"
    +
      264.     72                            ]
    +
      265.    108                        ).sort()
    +
      266.    108                    );
    +
      267.    108                });
    +
      268.     36            });
    +
      269.      6        });
    +
      270.      1    });
    +
      271.      1    jstestIt((
    +
      272.      1        "test globToRegexp handling-behavior"
    +
      273.      1    ), function () {
    +
      274.      1        Object.entries({
    +
      275.      1            "*": (
    +
      276.      1                /^[^\/]*?$/gm
    +
      277.      1            ),
    +
      278.      1            "**": (
    +
      279.      1                /^.*?$/gm
    +
      280.      1            ),
    +
      281.      1            "***": (
    +
      282.      1                /^.*?$/gm
    +
      283.      1            ),
    +
      284.      1            "****": (
    +
      285.      1                /^.*?$/gm
    +
      286.      1            ),
    +
      287.      1            "****////****": (
    +
      288.      1                /^.*?$/gm
    +
      289.      1            ),
    +
      290.      1            "***///***": (
    +
      291.      1                /^.*?$/gm
    +
      292.      1            ),
    +
      293.      1            "**/*": (
    +
      294.      1                /^.*?$/gm
    +
      295.      1            ),
    +
      296.      1            "**/node_modules/": (
    +
      297.      1                /^.*?\/node_modules\/.*?$/gm
    +
      298.      1            ),
    +
      299.      1            "**/node_modules/**/*": (
    +
      300.      1                /^.*?\/node_modules\/.*?$/gm
    +
      301.      1            ),
    +
      302.      1            "?": (
    +
      303.      1                /^[^\/]$/gm
    +
      304.      1            ),
    +
      305.      1            "[!0-9A-Za-z-]": (
    +
      306.      1                /^[^0-9A-Za-z\-]$/gm
    +
      307.      1            ),
    +
      308.      1            "[0-9A-Za-z-]": (
    +
      309.      1                /^[0-9A-Za-z\-]$/gm
    +
      310.      1            ),
    +
      311.      1            "[[]] ]][[": (
    +
      312.      1                /^[\[]\] \]\][\[]$/gm
    +
      313.      1            ),
    +
      314.      1            "[]": (
    +
      315.      1                /^$/gm
    +
      316.      1            ),
    +
      317.      1            "[^0-9A-Za-z-]": (
    +
      318.      1                /^[^0-9A-Za-z\-]$/gm
    +
      319.      1            ),
    +
      320.      1            "aa/bb/cc": (
    +
      321.      1                /^aa\/bb\/cc$/gm
    +
      322.      1            ),
    +
      323.      1            "aa/bb/cc/": (
    +
      324.      1                /^aa\/bb\/cc\/.*?$/gm
    +
      325.      1            ),
    +
      326.      1            "li*/*": (
    +
      327.      1                /^li[^\/]*?\/[^\/]*?$/gm
    +
      328.      1            ),
    +
      329.      1            "li?/*": (
    +
      330.      1                /^li[^\/]\/[^\/]*?$/gm
    +
      331.      1            ),
    +
      332.      1            "lib/": (
    +
      333.      1                /^lib\/.*?$/gm
    +
      334.      1            ),
    +
      335.      1            "lib/*": (
    +
      336.      1                /^lib\/[^\/]*?$/gm
    +
      337.      1            ),
    +
      338.      1            "lib/**/*.js": (
    +
      339.      1                /^lib\/.*?\.js$/gm
    +
      340.      1            ),
    +
      341.      1            "lib/*.js": (
    +
      342.      1                /^lib\/[^\/]*?\.js$/gm
    +
      343.      1            ),
    +
      344.      1            "node_modules/": (
    +
      345.      1                /^node_modules\/.*?$/gm
    +
      346.      1            ),
    +
      347.      1            "node_modules/**/*": (
    +
      348.      1                /^node_modules\/.*?$/gm
    +
      349.      1            ),
    +
      350.      1            "tes[!0-9A-Z_a-z-]/**/*": (
    +
      351.      1                /^tes[^0-9A-Z_a-z\-]\/.*?$/gm
    +
      352.      1            ),
    +
      353.      1            "tes[0-9A-Z_a-z-]/**/*": (
    +
      354.      1                /^tes[0-9A-Z_a-z\-]\/.*?$/gm
    +
      355.      1            ),
    +
      356.      1            "tes[^0-9A-Z_a-z-]/**/*": (
    +
      357.      1                /^tes[^0-9A-Z_a-z\-]\/.*?$/gm
    +
      358.      1            ),
    +
      359.      1            "test/**/*": (
    +
      360.      1                /^test\/.*?$/gm
    +
      361.      1            ),
    +
      362.      1            "test/**/*.js": (
    +
      363.      1                /^test\/.*?\.js$/gm
    +
      364.      1            ),
    +
      365.      1            "test/suppor*/*elper.js": (
    +
      366.      1                /^test\/suppor[^\/]*?\/[^\/]*?elper\.js$/gm
    +
      367.      1            ),
    +
      368.      1            "test/suppor?/?elper.js": (
    +
      369.      1                /^test\/suppor[^\/]\/[^\/]elper\.js$/gm
    +
      370.      1            ),
    +
      371.      1            "test/support/helper.js": (
    +
      372.      1                /^test\/support\/helper\.js$/gm
    +
      373.      1            )
    +
      374.     33        }).forEach(function ([
    +
      375.     33            pattern, rgx
    +
      376.     33        ]) {
    +
      377.     33            assertJsonEqual(
    +
      378.     33                globExclude({
    +
      379.     33                    excludeList: [
    +
      380.     33                        pattern
    +
      381.     33                    ]
    +
      382.     33                }).excludeList[0].source,
    +
      383.     33                rgx.source
    +
      384.     33            );
    +
      385.     33            assertJsonEqual(
    +
      386.     33                globExclude({
    +
      387.     33                    includeList: [
    +
      388.     33                        pattern
    +
      389.     33                    ]
    +
      390.     33                }).includeList[0].source,
    +
      391.     33                rgx.source
    +
      392.     33            );
    +
      393.     33        });
    +
      394.      1    });
    +
      395.      1});
    +
      396.      1
    +
      397.      1jstestDescribe((
    +
      398.      1    "test jslint's cli handling-behavior"
    +
      399.      1), function testBehaviorJslintCli() {
    +
      400.      5    function processExit0(exitCode) {
    +
      401.      5        assertOrThrow(exitCode === 0, exitCode);
    +
      402.      5    }
    +
      403.      6    function processExit1(exitCode) {
    +
      404.      6        assertOrThrow(exitCode === 1, exitCode);
    +
      405.      6    }
    +
      406.      1    jstestIt((
    +
      407.      1        "test cli-null-case handling-behavior"
    +
      408.      1    ), function () {
    +
      409.      1        jslint.jslint_cli({
    +
      410.      1            mode_noop: true,
    +
      411.      1            process_exit: processExit0
    +
      412.      1        });
    +
      413.      1    });
    +
      414.      1    jstestIt((
    +
      415.      1        "test cli-window-jslint handling-behavior"
    +
      416.      1    ), function () {
    +
      417.      1        [
    +
      418.      1            "&window_jslint=",
    +
      419.      1            "&window_jslint=12",
    +
      420.      1            "&window_jslint=1?",
    +
      421.      1            "&window_jslint=?",
    +
      422.      1            "?window_jslint=",
    +
      423.      1            "?window_jslint=12",
    +
      424.      1            "?window_jslint=1?",
    +
      425.      1            "?window_jslint=?",
    +
      426.      1            "window_jslint=1",
    +
      427.      1            "window_jslint=1&",
    +
      428.      1            "window_jslint=12",
    +
      429.      1            "window_jslint=1?"
    +
      430.     12        ].forEach(function (import_meta_url) {
    +
      431.     12            jslint.jslint_cli({
    +
      432.     12                import_meta_url
    +
      433.     12            });
    +
      434.     12            assertOrThrow(globalThis.jslint === undefined);
    +
      435.     12        });
    +
      436.      1        [
    +
      437.      1            "&window_jslint=1",
    +
      438.      1            "&window_jslint=1&",
    +
      439.      1            "?window_jslint=1",
    +
      440.      1            "?window_jslint=1&"
    +
      441.      4        ].forEach(function (import_meta_url) {
    +
      442.      4            jslint.jslint_cli({
    +
      443.      4                import_meta_url
    +
      444.      4            });
    +
      445.      4            assertOrThrow(globalThis.jslint === jslint);
    +
      446.      4            delete globalThis.jslint;
    +
      447.      4        });
    +
      448.      1    });
    +
      449.      1    jstestIt((
    +
      450.      1        "test cli-cjs-and-invalid-file handling-behavior"
    +
      451.      1    ), async function () {
    +
      452.      1        await fsWriteFileWithParents(".test_dir.cjs/touch.txt", "");
    +
      453.      1        [
    +
      454.      1            ".",            // test dir handling-behavior
    +
      455.      1            "jslint.mjs",   // test file handling-behavior
    +
      456.      1            undefined       // test file-undefined handling-behavior
    +
      457.      3        ].forEach(function (file) {
    +
      458.      3            jslint.jslint_cli({
    +
      459.      3                file,
    +
      460.      3                mode_cli: true,
    +
      461.      3                process_env: {
    +
      462.      3                    JSLINT_BETA: "1"
    +
      463.      3                },
    +
      464.      3                process_exit: processExit0
    +
      465.      3            });
    +
      466.      3        });
    +
      467.      1    });
    +
      468.      1    jstestIt((
    +
      469.      1        "test cli-apidoc handling-behavior"
    +
      470.      1    ), function () {
    +
      471.      1        jslint.jslint_cli({
    +
      472.      1            mode_cli: true,
    +
      473.      1            process_argv: [
    +
      474.      1                "node",
    +
      475.      1                "jslint.mjs",
    +
      476.      1                "jslint_apidoc=.artifact/apidoc.html",
    +
      477.      1                JSON.stringify({
    +
      478.      1                    example_list: [
    +
      479.      1                        "README.md",
    +
      480.      1                        "test.mjs",
    +
      481.      1                        "jslint.mjs"
    +
      482.      1                    ],
    +
      483.      1                    github_repo: "https://github.com/jslint-org/jslint",
    +
      484.      1                    module_list: [
    +
      485.      1                        {
    +
      486.      1                            pathname: "./jslint.mjs"
    +
      487.      1                        }
    +
      488.      1                    ],
    +
      489.      1                    package_name: "JSLint",
    +
      490.      1                    version: jslint.jslint_edition
    +
      491.      1                })
    +
      492.      1            ],
    +
      493.      1            process_exit: processExit0
    +
      494.      1        });
    +
      495.      1    });
    +
      496.      1    jstestIt((
    +
      497.      1        "test cli-file-error handling-behavior"
    +
      498.      1    ), function () {
    +
      499.      1        jslint.jslint_cli({
    +
      500.      1            // suppress error
    +
      501.      1            console_error: noop,
    +
      502.      1            file: "undefined",
    +
      503.      1            mode_cli: true,
    +
      504.      1            process_exit: processExit1
    +
      505.      1        });
    +
      506.      1    });
    +
      507.      1    jstestIt((
    +
      508.      1        "test cli-syntax-error handling-behavior"
    +
      509.      1    ), function () {
    +
      510.      1        jslint.jslint_cli({
    +
      511.      1            // suppress error
    +
      512.      1            console_error: noop,
    +
      513.      1            file: "syntax-error.js",
    +
      514.      1            mode_cli: true,
    +
      515.      1            option: {
    +
      516.      1                trace: true
    +
      517.      1            },
    +
      518.      1            process_exit: processExit1,
    +
      519.      1            source: "syntax error"
    +
      520.      1        });
    +
      521.      1    });
    +
      522.      1    jstestIt((
    +
      523.      1        "test cli-report handling-behavior"
    +
      524.      1    ), function () {
    +
      525.      1        jslint.jslint_cli({
    +
      526.      1            // suppress error
    +
      527.      1            console_error: noop,
    +
      528.      1            mode_cli: true,
    +
      529.      1            process_argv: [
    +
      530.      1                "node",
    +
      531.      1                "jslint.mjs",
    +
      532.      1                "jslint_report=.tmp/jslint_report.html",
    +
      533.      1                "jslint.mjs"
    +
      534.      1            ],
    +
      535.      1            process_exit: processExit0
    +
      536.      1        });
    +
      537.      1    });
    +
      538.      1    jstestIt((
    +
      539.      1        "test cli-report-error handling-behavior"
    +
      540.      1    ), function () {
    +
      541.      1        jslint.jslint_cli({
    +
      542.      1            // suppress error
    +
      543.      1            console_error: noop,
    +
      544.      1            mode_cli: true,
    +
      545.      1            process_argv: [
    +
      546.      1                "node",
    +
      547.      1                "jslint.mjs",
    +
      548.      1                "jslint_report=.tmp/jslint_report.html",
    +
      549.      1                "syntax-error.js"
    +
      550.      1            ],
    +
      551.      1            process_exit: processExit1,
    +
      552.      1            source: "syntax error"
    +
      553.      1        });
    +
      554.      1    });
    +
      555.      1    jstestIt((
    +
      556.      1        "test cli-report-json handling-behavior"
    +
      557.      1    ), function () {
    +
      558.      1        jslint.jslint_cli({
    +
      559.      1            // suppress error
    +
      560.      1            console_error: noop,
    +
      561.      1            mode_cli: true,
    +
      562.      1            process_argv: [
    +
      563.      1                "node",
    +
      564.      1                "jslint.mjs",
    +
      565.      1                "jslint_report=.tmp/jslint_report.html",
    +
      566.      1                "aa.json"
    +
      567.      1            ],
    +
      568.      1            process_exit: processExit0,
    +
      569.      1            source: "[]"
    +
      570.      1        });
    +
      571.      1    });
    +
      572.      1    jstestIt((
    +
      573.      1        "test cli-report-json-error handling-behavior"
    +
      574.      1    ), function () {
    +
      575.      1        jslint.jslint_cli({
    +
      576.      1            // suppress error
    +
      577.      1            console_error: noop,
    +
      578.      1            mode_cli: true,
    +
      579.      1            process_argv: [
    +
      580.      1                "node",
    +
      581.      1                "jslint.mjs",
    +
      582.      1                "jslint_report=.tmp/jslint_report.html",
    +
      583.      1                "aa.json"
    +
      584.      1            ],
    +
      585.      1            process_exit: processExit1,
    +
      586.      1            source: "["
    +
      587.      1        });
    +
      588.      1    });
    +
      589.      1    jstestIt((
    +
      590.      1        "test cli-report-misc handling-behavior"
    +
      591.      1    ), function () {
    +
      592.      1        jslint.jslint_cli({
    +
      593.      1            // suppress error
    +
      594.      1            console_error: noop,
    +
      595.      1            mode_cli: true,
    +
      596.      1            process_argv: [
    +
      597.      1                "node",
    +
      598.      1                "jslint.mjs",
    +
      599.      1                "jslint_report=.tmp/jslint_report.html",
    +
      600.      1                "aa.js"
    +
      601.      1            ],
    +
      602.      1            process_exit: processExit0,
    +
      603.      1            source: "let aa = 0;"
    +
      604.      1        });
    +
      605.      1        jslint.jslint_cli({
    +
      606.      1            // suppress error
    +
      607.      1            console_error: noop,
    +
      608.      1            mode_cli: true,
    +
      609.      1            process_argv: [
    +
      610.      1                "node",
    +
      611.      1                "jslint.mjs",
    +
      612.      1                "jslint_report=.tmp/jslint_report.html",
    +
      613.      1                "aa.js"
    +
      614.      1            ],
    +
      615.      1            process_exit: processExit1,
    +
      616.      1            source: "(aa)=>aa; function aa([aa]){}"
    +
      617.      1        });
    +
      618.      1    });
    +
      619.      1    jstestIt((
    +
      620.      1        "test cli-jslint-wrapper-vim handling-behavior"
    +
      621.      1    ), function () {
    +
      622.      1        jslint.jslint_cli({
    +
      623.      1            // suppress error
    +
      624.      1            console_error: noop,
    +
      625.      1            mode_cli: true,
    +
      626.      1            process_argv: [
    +
      627.      1                "node",
    +
      628.      1                "jslint.mjs",
    +
      629.      1                "jslint_wrapper_vim",
    +
      630.      1                "syntax-error.js"
    +
      631.      1            ],
    +
      632.      1            process_exit: processExit1,
    +
      633.      1            source: "syntax error"
    +
      634.      1        });
    +
      635.      1    });
    +
      636.      1});
    +
      637.      1
    +
      638.      1jstestDescribe((
    +
      639.      1    "test jslint's no-warnings handling-behavior"
    +
      640.      1), function testBehaviorJslintNoWarnings() {
    +
      641.      1    jstestIt((
    +
      642.      1        "test jslint's no-warnings handling-behavior"
    +
      643.      1    ), function () {
    +
      644.      1        Object.values({
    +
      645.      1            array: [
    +
      646.      1                "new Array(0);"
    +
      647.      1            ],
    +
      648.      1            async_await: [
    +
      649.      1                "async function aa() {\n    await aa();\n}",
    +
      650.      1
    +
      651.      1// PR-405 - Bugfix - fix expression after "await" mis-identified as statement.
    +
      652.      1
    +
      653.      1                "async function aa() {\n    await aa;\n}",
    +
      654.      1                (
    +
      655.      1                    "async function aa() {\n"
    +
      656.      1                    + "    try {\n"
    +
      657.      1                    + "        aa();\n"
    +
      658.      1                    + "    } catch (err) {\n"
    +
      659.      1                    + "        await err();\n"
    +
      660.      1                    + "    }\n"
    +
      661.      1                    + "}\n"
    +
      662.      1                ),
    +
      663.      1                (
    +
      664.      1                    "async function aa() {\n"
    +
      665.      1                    + "    try {\n"
    +
      666.      1                    + "        await aa();\n"
    +
      667.      1                    + "    } catch (err) {\n"
    +
      668.      1                    + "        await err();\n"
    +
      669.      1                    + "    }\n"
    +
      670.      1                    + "}\n"
    +
      671.      1                ),
    +
      672.      1
    +
      673.      1// PR-370 - Add top-level-await support.
    +
      674.      1
    +
      675.      1                "await String();\n"
    +
      676.      1            ],
    +
      677.      1
    +
      678.      1// PR-351 - Add BigInt support.
    +
      679.      1
    +
      680.      1            bigint: [
    +
      681.      1                "let aa = 0b0n;\n",
    +
      682.      1                "let aa = 0o0n;\n",
    +
      683.      1                "let aa = 0x0n;\n",
    +
      684.      1                "let aa = BigInt(0n);\n",
    +
      685.      1                "let aa = typeof aa === \"bigint\";\n"
    +
      686.      1            ],
    +
      687.      1            date: [
    +
      688.      1                "Date.getTime();",
    +
      689.      1                "let aa = aa().getTime();",
    +
      690.      1                "let aa = aa.aa().getTime();"
    +
      691.      1            ],
    +
      692.      1            directive: [
    +
      693.      1                "#!\n/*jslint browser:false, node*/\n\"use strict\";",
    +
      694.      1                "/*property aa bb*/"
    +
      695.      1            ],
    +
      696.      1            for: [
    +
      697.      1                (
    +
      698.      1                    "/*jslint for*/\n"
    +
      699.      1                    + "function aa(bb) {\n"
    +
      700.      1                    + "    for (bb = 0; bb < 0; bb += 1) {\n"
    +
      701.      1                    + "        bb();\n"
    +
      702.      1                    + "    }\n"
    +
      703.      1                    + "}\n"
    +
      704.      1                )
    +
      705.      1            ],
    +
      706.      1            jslint_disable: [
    +
      707.      1                "/*jslint-disable*/\n0\n/*jslint-enable*/"
    +
      708.      1            ],
    +
      709.      1            jslint_ignore_line: [
    +
      710.      1                "0 //jslint-ignore-line"
    +
      711.      1            ],
    +
      712.      1            json: [
    +
      713.      1                "{\"aa\":[[],-0,null]}"
    +
      714.      1            ],
    +
      715.      1            label: [
    +
      716.      1                (
    +
      717.      1                    "function aa() {\n"
    +
      718.      1                    + "bb:\n"
    +
      719.      1                    + "    while (true) {\n"
    +
      720.      1                    + "        if (true) {\n"
    +
      721.      1                    + "            break bb;\n"
    +
      722.      1                    + "        }\n"
    +
      723.      1                    + "    }\n"
    +
      724.      1                    + "}\n"
    +
      725.      1                )
    +
      726.      1            ],
    +
      727.      1            loop: [
    +
      728.      1                (
    +
      729.      1                    "function aa() {\n"
    +
      730.      1                    + "    do {\n"
    +
      731.      1                    + "        aa();\n"
    +
      732.      1                    + "    } while (aa());\n"
    +
      733.      1                    + "}\n"
    +
      734.      1                ),
    +
      735.      1
    +
      736.      1// PR-378 - Relax warning "function_in_loop".
    +
      737.      1
    +
      738.      1                (
    +
      739.      1                    "function aa() {\n"
    +
      740.      1                    + "    while (true) {\n"
    +
      741.      1                    + "        (function () {\n"
    +
      742.      1                    + "            return;\n"
    +
      743.      1                    + "        }());\n"
    +
      744.      1                    + "    }\n"
    +
      745.      1                    + "}\n"
    +
      746.      1                )
    +
      747.      1            ],
    +
      748.      1            module: [
    +
      749.      1                "export default Object.freeze();",
    +
      750.      1
    +
      751.      1// PR-439 - Add grammar for "export async function ...".
    +
      752.      1
    +
      753.      1                (
    +
      754.      1                    "export default Object.freeze(async function () {\n"
    +
      755.      1                    + "    return await 0;\n"
    +
      756.      1                    + "});\n"
    +
      757.      1                ),
    +
      758.      1                "import {aa, bb} from \"aa\";\naa(bb);",
    +
      759.      1                "import {} from \"aa\";",
    +
      760.      1                "import(\"aa\").then(function () {\n    return;\n});",
    +
      761.      1                (
    +
      762.      1                    "let aa = 0;\n"
    +
      763.      1                    + "import(aa).then(aa).then(aa)"
    +
      764.      1                    + ".catch(aa).finally(aa);\n"
    +
      765.      1                )
    +
      766.      1            ],
    +
      767.      1            number: [
    +
      768.      1                "let aa = 0.0e0;",
    +
      769.      1                "let aa = 0b0;",
    +
      770.      1                "let aa = 0o0;",
    +
      771.      1                "let aa = 0x0;"
    +
      772.      1            ],
    +
      773.      1
    +
      774.      1// PR-390 - Add numeric-separator support.
    +
      775.      1
    +
      776.      1            numeric_separator: [
    +
      777.      1                "let aa = 0.0_0_0;",
    +
      778.      1                "let aa = 0b0_1111_1111n;\n",
    +
      779.      1                "let aa = 0o0_1234_1234n;\n",
    +
      780.      1                "let aa = 0x0_1234_1234n;\n",
    +
      781.      1                "let aa = 1_234_234.1_234_234E1_234_234;"
    +
      782.      1            ],
    +
      783.      1            optional_chaining: [
    +
      784.      1                "let aa = aa?.bb?.cc;"
    +
      785.      1            ],
    +
      786.      1            param: [
    +
      787.      1                "function aa({aa, bb}) {\n    return {aa, bb};\n}\n",
    +
      788.      1                (
    +
      789.      1                    "function aa({constructor}) {\n"
    +
      790.      1                    + "    return {constructor};\n"
    +
      791.      1                    + "}\n"
    +
      792.      1                )
    +
      793.      1            ],
    +
      794.      1            property: [
    +
      795.      1                "let aa = aa[`!`];"
    +
      796.      1            ],
    +
      797.      1            regexp: [
    +
      798.      1                "function aa() {\n    return /./;\n}",
    +
      799.      1                "let aa = /(?!.)(?:.)(?=.)/;",
    +
      800.      1                "let aa = /./gimuy;",
    +
      801.      1                "let aa = /[\\--\\-]/;"
    +
      802.      1            ],
    +
      803.      1            ternary: [
    +
      804.      1                (
    +
      805.      1                    "let aa = (\n"
    +
      806.      1                    + "    aa()\n"
    +
      807.      1                    + "    ? 0\n"
    +
      808.      1                    + "    : 1\n"
    +
      809.      1                    + ") "
    +
      810.      1                    + "&& (\n"
    +
      811.      1                    + "    aa()\n"
    +
      812.      1                    + "    ? 0\n"
    +
      813.      1                    + "    : 1\n"
    +
      814.      1                    + ");"
    +
      815.      1                ),
    +
      816.      1                (
    +
      817.      1                    "let aa = (\n"
    +
      818.      1                    + "    aa()\n"
    +
      819.      1                    + "    ? `${0}`\n"
    +
      820.      1                    + "    : `${1}`\n"
    +
      821.      1                    + ");"
    +
      822.      1                ),
    +
      823.      1
    +
      824.      1// PR-394 - Bugfix
    +
      825.      1// Fix jslint falsely believing megastring literals `0` and `1` are similar.
    +
      826.      1
    +
      827.      1                (
    +
      828.      1                    "let aa = (\n"
    +
      829.      1                    + "    aa()\n"
    +
      830.      1                    + "    ? `0`\n"
    +
      831.      1                    + "    : `1`\n"
    +
      832.      1                    + ");"
    +
      833.      1                )
    +
      834.      1            ],
    +
      835.      1            try_catch: [
    +
      836.      1                (
    +
      837.      1                    "let aa = 0;\n"
    +
      838.      1                    + "try {\n"
    +
      839.      1                    + "    aa();\n"
    +
      840.      1                    + "} catch (err) {\n"
    +
      841.      1                    + "    aa = err;\n"
    +
      842.      1                    + "}\n"
    +
      843.      1                    + "try {\n"
    +
      844.      1                    + "    aa();\n"
    +
      845.      1                    + "} catch (err) {\n"
    +
      846.      1                    + "    aa = err;\n"
    +
      847.      1                    + "}\n"
    +
      848.      1                    + "aa();\n"
    +
      849.      1                )
    +
      850.      1            ],
    +
      851.      1            try_finally: [
    +
      852.      1                (
    +
      853.      1                    "let aa = 0;\n"
    +
      854.      1                    + "try {\n"
    +
      855.      1                    + "    aa();\n"
    +
      856.      1                    + "} finally {\n"
    +
      857.      1                    + "    aa();\n"
    +
      858.      1                    + "}\n"
    +
      859.      1                )
    +
      860.      1            ],
    +
      861.      1            use_strict: [
    +
      862.      1                (
    +
      863.      1                    "\"use strict\";\n"
    +
      864.      1                    + "let aa = 0;\n"
    +
      865.      1                    + "function bb() {\n"
    +
      866.      1                    + "    \"use strict\";\n"
    +
      867.      1                    + "    return aa;\n"
    +
      868.      1                    + "}\n"
    +
      869.      1                )
    +
      870.      1            ],
    +
      871.      1            var: [
    +
      872.      1
    +
      873.      1// PR-363 - Bugfix
    +
      874.      1// Add test against false-warning <uninitialized 'bb'> in code
    +
      875.      1// '/*jslint node*/\nlet {aa:bb} = {}; bb();'.
    +
      876.      1
    +
      877.      1                "/*jslint node*/\n",
    +
      878.      1                ""
    +
      879.      2            ].map(function (directive) {
    +
      880.      2                return [
    +
      881.      2                    "let [\n    aa, bb = 0\n] = 0;\naa();\nbb();",
    +
      882.      2                    "let aa = 0;\nlet [...bb] = [...aa];\nbb();",
    +
      883.      2
    +
      884.      2// PR-459 - Allow destructuring-assignment after function-definition.
    +
      885.      2
    +
      886.      2                    (
    +
      887.      2                        "let aa;\n"
    +
      888.      2                        + "let bb;\n"
    +
      889.      2                        + "function cc() {\n"
    +
      890.      2                        + "    return;\n"
    +
      891.      2                        + "}\n"
    +
      892.      2                        + "[aa, bb] = cc();\n"
    +
      893.      2                    ),
    +
      894.      2                    "let constructor = 0;\nconstructor();",
    +
      895.      2                    "let {\n    aa: bb\n} = 0;\nbb();",
    +
      896.      2                    "let {\n    aa: bb,\n    bb: cc\n} = 0;\nbb();\ncc();",
    +
      897.      2                    "let {aa, bb} = 0;\naa();\nbb();",
    +
      898.      2                    "let {constructor} = 0;\nconstructor();"
    +
      899.     16                ].map(function (code) {
    +
      900.     16                    return directive + code;
    +
      901.     16                });
    +
      902.      2            }).flat()
    +
      903.     23        }).forEach(function (codeList) {
    +
      904.     23            let elemPrv = "";
    +
      905.     68            codeList.forEach(function (code) {
    +
      906.     68                let warnings;
    +
      907.     68                // Assert codeList is sorted.
    +
      908.     68                assertOrThrow(elemPrv < code, JSON.stringify([
    +
      909.     68                    elemPrv, code
    +
      910.     68                ], undefined, 4));
    +
      911.     68                elemPrv = code;
    +
      912.     68                [
    +
      913.     68                    jslint.jslint,
    +
      914.     68                    jslintCjs.jslint
    +
      915.    136                ].forEach(function (jslint) {
    +
      916.    136                    warnings = jslint(code, {
    +
      917.    136                        beta: true
    +
      918.    136                    }).warnings;
    +
      919.    136                    assertOrThrow(
    +
      920.    136                        warnings.length === 0,
    +
      921.    136                        JSON.stringify([code, warnings])
    +
      922.    136                    );
    +
      923.    136                });
    +
      924.     68            });
    +
      925.     23        });
    +
      926.      1    });
    +
      927.      1});
    +
      928.      1
    +
      929.      1jstestDescribe((
    +
      930.      1    "test jslint's option handling-behavior"
    +
      931.      1), function testBehaviorJslintOption() {
    +
      932.      1    let elemPrv = "";
    +
      933.      1    [
    +
      934.      1        [
    +
      935.      1            "let aa = aa | 0;", {bitwise: true}, []
    +
      936.      1        ], [
    +
      937.      1            ";\naa(new XMLHttpRequest());", {browser: true}, ["aa"]
    +
      938.      1        ], [
    +
      939.      1            "let aa = \"aa\" + 0;", {convert: true}, []
    +
      940.      1        ], [
    +
      941.      1            "registerType();", {couch: true}, []
    +
      942.      1        ], [
    +
      943.      1            "debugger;", {devel: true}, []
    +
      944.      1        ], [
    +
      945.      1
    +
      946.      1// PR-404 - Alias "evil" to jslint-directive "eval" for backwards-compat.
    +
      947.      1
    +
      948.      1            "new Function();\neval();", {eval: true, evil: true}, []
    +
      949.      1        ], [
    +
      950.      1            "let aa = () => 0;", {fart: true}, []
    +
      951.      1        ], [
    +
      952.      1            (
    +
      953.      1                "let aa = async (bb, {cc, dd}, [ee, ff], ...gg) => {\n"
    +
      954.      1                + "    bb += 1;\n"
    +
      955.      1                + "    return await (bb + cc + dd + ee + ff + gg);\n"
    +
      956.      1                + "};\n"
    +
      957.      1            ), {fart: true}, []
    +
      958.      1        ], [
    +
      959.      1            (
    +
      960.      1                "function aa(aa) {\n"
    +
      961.      1                + "    for (aa = 0; aa < 0; aa += 1) {\n"
    +
      962.      1                + "        aa();\n"
    +
      963.      1                + "    }\n"
    +
      964.      1                + "}\n"
    +
      965.      1            ), {for: true}, []
    +
      966.      1        ], [
    +
      967.      1            "let aa = {get aa() {\n    return;\n}};", {getset: true}, []
    +
      968.      1        ], [
    +
      969.      1            "let aa = {set aa(aa) {\n    return aa;\n}};", {getset: true}, []
    +
      970.      1        ], [
    +
      971.      1            sourceJslintMjs.replace((
    +
      972.      1                /    /g
    +
      973.      1            ), "  "), {indent2: true}, []
    +
      974.      1        ], [
    +
      975.      1            "function aa() {\n  return;\n}", {indent2: true}, []
    +
      976.      1        ], [
    +
      977.      1            "/".repeat(100), {long: true}, []
    +
      978.      1        ], [
    +
      979.      1
    +
      980.      1// PR-404 - Alias "nomen" to jslint-directive "name" for backwards-compat.
    +
      981.      1
    +
      982.      1            "let aa = aa._;", {name: true, nomen: true}, []
    +
      983.      1        ], [
    +
      984.      1            "require();", {node: true}, []
    +
      985.      1        ], [
    +
      986.      1            "let aa = 'aa';", {single: true}, []
    +
      987.      1        ], [
    +
      988.      1
    +
      989.      1// PR-404 - Add new directive "subscript" to play nice with Google Closure.
    +
      990.      1
    +
      991.      1            "aa[\"aa\"] = 1;", {subscript: true}, ["aa"]
    +
      992.      1        ], [
    +
      993.      1            "", {test_internal_error: true}, []
    +
      994.      1        ], [
    +
      995.      1            "let aa = this;", {this: true}, []
    +
      996.      1        ], [
    +
      997.      1            "", {trace: true}, []
    +
      998.      1        ], [
    +
      999.      1            (
    +
     1000.      1                "function aa({bb, aa}) {\n"
    +
     1001.      1                + "    switch (aa) {\n"
    +
     1002.      1                + "    case 1:\n"
    +
     1003.      1                + "        break;\n"
    +
     1004.      1                + "    case 0:\n"
    +
     1005.      1                + "        break;\n"
    +
     1006.      1                + "    default:\n"
    +
     1007.      1                + "        return {bb, aa};\n"
    +
     1008.      1                + "    }\n"
    +
     1009.      1                + "}\n"
    +
     1010.      1            ), {unordered: true}, []
    +
     1011.      1        ], [
    +
     1012.      1            "let {bb, aa} = 0;", {unordered: true}, []
    +
     1013.      1        ], [
    +
     1014.      1            (
    +
     1015.      1                "function aa() {\n"
    +
     1016.      1                + "    if (aa) {\n"
    +
     1017.      1                + "        let bb = 0;\n"
    +
     1018.      1                + "        return bb;\n"
    +
     1019.      1                + "    }\n"
    +
     1020.      1                + "}\n"
    +
     1021.      1            ), {variable: true}, []
    +
     1022.      1        ], [
    +
     1023.      1            "let bb = 0;\nlet aa = 0;", {variable: true}, []
    +
     1024.      1        ], [
    +
     1025.      1            "\t", {white: true}, []
    +
     1026.      1        ]
    +
     1027.     26    ].forEach(function ([
    +
     1028.     26        source, option_dict, global_list
    +
     1029.     26    ]) {
    +
     1030.     26        jstestIt((
    +
     1031.     26            `test option=${JSON.stringify(option_dict)} handling-behavior`
    +
     1032.     26        ), function () {
    +
     1033.     26            let elemNow = JSON.stringify([
    +
     1034.     26                option_dict, source, global_list
    +
     1035.     26            ]);
    +
     1036.     26            let warningsLength = (
    +
     1037.     26                option_dict.test_internal_error
    +
     1038.      1                ? 1
    +
     1039.     25                : 0
    +
     1040.     26            );
    +
     1041.     26            // Assert list is sorted.
    +
     1042.     26            assertOrThrow(elemPrv < elemNow, JSON.stringify([
    +
     1043.     26                elemPrv, elemNow
    +
     1044.     26            ], undefined, 4));
    +
     1045.     26            elemPrv = elemNow;
    +
     1046.     26            option_dict.beta = true;
    +
     1047.     26            [
    +
     1048.     26                jslint.jslint,
    +
     1049.     26                jslintCjs.jslint
    +
     1050.     52            ].forEach(function (jslint) {
    +
     1051.     52                // test jslint's option handling-behavior
    +
     1052.     52                assertOrThrow(
    +
     1053.     52                    jslint(
    +
     1054.     52                        source,
    +
     1055.     52                        option_dict,
    +
     1056.     52                        global_list
    +
     1057.     52                    ).warnings.length === warningsLength,
    +
     1058.     52                    "jslint.jslint(" + JSON.stringify([
    +
     1059.     52                        source, option_dict, global_list
    +
     1060.     52                    ]) + ")"
    +
     1061.     52                );
    +
     1062.     52                // test jslint's directive handling-behavior
    +
     1063.     52                source = (
    +
     1064.     52                    "/*jslint " + JSON.stringify(
    +
     1065.     52                        option_dict
    +
     1066.     52                    ).slice(1, -1).replace((
    +
     1067.     52                        /"/g
    +
     1068.     52                    ), "") + "*/\n"
    +
     1069.     52                    + (
    +
     1070.     52                        global_list.length === 0
    +
     1071.     48                        ? ""
    +
     1072.      4                        : "/*global " + global_list.join(",") + "*/\n"
    +
     1073.     52                    )
    +
     1074.     52                    + source.replace((
    +
     1075.     52                        /^#!/
    +
     1076.     52                    ), "//")
    +
     1077.     52                );
    +
     1078.     52                assertOrThrow(
    +
     1079.     52                    jslint(source).warnings.length === warningsLength,
    +
     1080.     52                    source
    +
     1081.     52                );
    +
     1082.     52            });
    +
     1083.     26        });
    +
     1084.     26    });
    +
     1085.      1});
    +
     1086.      1
    +
     1087.      1jstestDescribe((
    +
     1088.      1    "test jslint's warnings handling-behavior"
    +
     1089.      1), function testBehaviorJslintWarnings() {
    +
     1090.      1    jstestIt((
    +
     1091.      1        "test jslint's warning handling-behavior"
    +
     1092.      1    ), function () {
    +
     1093.      1
    +
     1094.      1// this function will validate each jslint <warning> is raised with given
    +
     1095.      1// malformed <code>
    +
     1096.      1
    +
     1097.      1        sourceJslintMjs.replace((
    +
     1098.      1            /(\n\s*?\/\/\s*?test_cause:\s*?)(\S[\S\s]*?\S)(\n\n\s*?) *?\S/g
    +
     1099.    328        ), function (match0, header, causeList, footer) {
    +
     1100.    328            let tmp;
    +
     1101.    328            // console.error(match0);
    +
     1102.    328            // Validate header.
    +
     1103.    328            assertOrThrow(header === "\n\n// test_cause:\n", match0);
    +
     1104.    328            // Validate footer.
    +
     1105.    328            assertOrThrow(footer === "\n\n", match0);
    +
     1106.    328            // Validate causeList.
    +
     1107.    328            causeList = causeList.replace((
    +
     1108.    328                /^\/\/ /gm
    +
     1109.    328            ), "").replace((
    +
     1110.    328                /^\["\n([\S\s]*?)\n"(,.*?)$/gm
    +
     1111.     43            ), function (ignore, source, param) {
    +
     1112.     43                source = "[" + JSON.stringify(source) + param;
    +
     1113.     43                assertOrThrow(source.length > (80 - 3), source);
    +
     1114.     43                return source;
    +
     1115.     43            }).replace((
    +
     1116.    328                / \/\/jslint-ignore-line$/gm
    +
     1117.    328            ), "");
    +
     1118.    505            tmp = causeList.split("\n").map(function (cause) {
    +
     1119.    505                return (
    +
     1120.    505                    "["
    +
     1121.   2525                    + JSON.parse(cause).map(function (elem) {
    +
     1122.   2525                        return JSON.stringify(elem);
    +
     1123.   2525                    }).join(", ")
    +
     1124.    505                    + "]"
    +
     1125.    505                );
    +
     1126.    505            }).sort().join("\n");
    +
     1127.    328            assertOrThrow(
    +
     1128.    328                causeList === tmp,
    +
     1129.    328                "\n" + causeList + "\n\n" + tmp
    +
     1130.    328            );
    +
     1131.    505            causeList.split("\n").forEach(function (cause) {
    +
     1132.    505                cause = JSON.parse(cause);
    +
     1133.    505                tmp = jslint.jslint(cause[0], {
    +
     1134.    505                    beta: true,
    +
     1135.    505                    test_cause: true
    +
     1136.    505                }).causes;
    +
     1137.    505                // Validate cause.
    +
     1138.    505                assertOrThrow(
    +
     1139.    505                    tmp[JSON.stringify(cause.slice(1))],
    +
     1140.    505                    (
    +
     1141.    505                        "\n" + JSON.stringify(cause) + "\n\n"
    +
     1142.    505                        + Object.keys(tmp).sort().join("\n")
    +
     1143.    505                    )
    +
     1144.    505                );
    +
     1145.    505            });
    +
     1146.    328            return "";
    +
     1147.    328        });
    +
     1148.      1    });
    +
     1149.      1});
    +
     1150.      1
    +
     1151.      1jstestDescribe((
    +
     1152.      1    "test jstestXxx handling-behavior"
    +
     1153.      1), function testBehaviorJstestXxx() {
    +
     1154.      1    jstestIt((
    +
     1155.      1        "test jstestDescribe error handling-behavior"
    +
     1156.      1    ), function () {
    +
     1157.      1        throw new Error();
    +
     1158.      1    }, "pass");
    +
     1159.      1    jstestIt((
    +
     1160.      1        "test jstestOnExit tests-failed handling-behavior"
    +
     1161.      1    ), function () {
    +
     1162.      1        jstestOnExit(undefined, "testsFailed");
    +
     1163.      1    });
    +
     1164.      1});
    +
     1165.      1
    +
     1166.      1jstestDescribe((
    +
     1167.      1    "test misc handling-behavior"
    +
     1168.      1), function testBehaviorMisc() {
    +
     1169.      1    jstestIt((
    +
     1170.      1        "test misc handling-behavior"
    +
     1171.      1    ), async function () {
    +
     1172.      1        // test debugInline handling-behavior
    +
     1173.      1        noop(debugInline);
    +
     1174.      1        // test assertErrorThrownAsync error handling-behavior
    +
     1175.      1        await assertErrorThrownAsync(function () {
    +
     1176.      1            return assertErrorThrownAsync(noop);
    +
     1177.      1        });
    +
     1178.      1        // test assertJsonEqual error handling-behavior
    +
     1179.      1        await assertErrorThrownAsync(function () {
    +
     1180.      1            assertJsonEqual(1, 2);
    +
     1181.      1        });
    +
     1182.      1        await assertErrorThrownAsync(function () {
    +
     1183.      1            assertJsonEqual(1, 2, "undefined");
    +
     1184.      1        });
    +
     1185.      1        await assertErrorThrownAsync(function () {
    +
     1186.      1            assertJsonEqual(1, 2, {});
    +
     1187.      1        });
    +
     1188.      1        // test assertOrThrow error handling-behavior
    +
     1189.      1        await assertErrorThrownAsync(function () {
    +
     1190.      1            assertOrThrow(undefined, "undefined");
    +
     1191.      1        });
    +
     1192.      1        await assertErrorThrownAsync(function () {
    +
     1193.      1            assertOrThrow(undefined, new Error());
    +
     1194.      1        });
    +
     1195.      1    });
    +
     1196.      1});
    +
     1197.      1
    +
     1198.      1jstestDescribe((
    +
     1199.      1    "test v8CoverageListMerge handling-behavior"
    +
     1200.      1), function testBehaviorV8CoverageListMerge() {
    +
     1201.      1    let functionsInput = JSON.stringify([
    +
     1202.      1        {
    +
     1203.      1            functionName: "test",
    +
     1204.      1            isBlockCoverage: true,
    +
     1205.      1            ranges: [
    +
     1206.      1                {
    +
     1207.      1                    count: 2,
    +
     1208.      1                    endOffset: 4,
    +
     1209.      1                    startOffset: 0
    +
     1210.      1                },
    +
     1211.      1                {
    +
     1212.      1                    count: 1,
    +
     1213.      1                    endOffset: 2,
    +
     1214.      1                    startOffset: 1
    +
     1215.      1                },
    +
     1216.      1                {
    +
     1217.      1                    count: 1,
    +
     1218.      1                    endOffset: 3,
    +
     1219.      1                    startOffset: 2
    +
     1220.      1                }
    +
     1221.      1            ]
    +
     1222.      1        }
    +
     1223.      1    ]);
    +
     1224.      1    jstestIt((
    +
     1225.      1        "accepts empty arrays for `v8CoverageListMerge`"
    +
     1226.      1    ), function () {
    +
     1227.      1        assertJsonEqual(v8CoverageListMerge([]), {
    +
     1228.      1            result: []
    +
     1229.      1        });
    +
     1230.      1    });
    +
     1231.      1    jstestIt((
    +
     1232.      1        "funcCovs.length === 1"
    +
     1233.      1    ), function () {
    +
     1234.      1        assertJsonEqual(v8CoverageListMerge([
    +
     1235.      1            {
    +
     1236.      1                result: [
    +
     1237.      1                    {
    +
     1238.      1                        functions: [
    +
     1239.      1                            {
    +
     1240.      1                                functionName: "test",
    +
     1241.      1                                isBlockCoverage: true,
    +
     1242.      1                                ranges: [
    +
     1243.      1                                    {
    +
     1244.      1                                        count: 2,
    +
     1245.      1                                        endOffset: 4,
    +
     1246.      1                                        startOffset: 0
    +
     1247.      1                                    }
    +
     1248.      1                                ]
    +
     1249.      1                            }
    +
     1250.      1                        ],
    +
     1251.      1                        moduleUrl: "/lib.js",
    +
     1252.      1                        scriptId: "1"
    +
     1253.      1                    }
    +
     1254.      1                ]
    +
     1255.      1            },
    +
     1256.      1            {
    +
     1257.      1                result: [
    +
     1258.      1                    {
    +
     1259.      1                        functions: [],
    +
     1260.      1                        moduleUrl: "/lib.js",
    +
     1261.      1                        scriptId: "2"
    +
     1262.      1                    }
    +
     1263.      1                ]
    +
     1264.      1            }
    +
     1265.      1        ]), {
    +
     1266.      1            result: [
    +
     1267.      1                {
    +
     1268.      1                    functions: [
    +
     1269.      1                        {
    +
     1270.      1                            functionName: "test",
    +
     1271.      1                            isBlockCoverage: true,
    +
     1272.      1                            ranges: [
    +
     1273.      1                                {
    +
     1274.      1                                    count: 2,
    +
     1275.      1                                    endOffset: 4,
    +
     1276.      1                                    startOffset: 0
    +
     1277.      1                                }
    +
     1278.      1                            ]
    +
     1279.      1                        }
    +
     1280.      1                    ],
    +
     1281.      1                    scriptId: "0"
    +
     1282.      1                }
    +
     1283.      1            ]
    +
     1284.      1        });
    +
     1285.      1    });
    +
     1286.      1    jstestIt((
    +
     1287.      1        "accepts arrays with a single item for `v8CoverageListMerge`"
    +
     1288.      1    ), function () {
    +
     1289.      1        assertJsonEqual(v8CoverageListMerge([
    +
     1290.      1            {
    +
     1291.      1                result: [
    +
     1292.      1                    {
    +
     1293.      1                        functions: JSON.parse(functionsInput),
    +
     1294.      1                        moduleUrl: "/lib.js",
    +
     1295.      1                        scriptId: "123"
    +
     1296.      1                    }
    +
     1297.      1                ]
    +
     1298.      1            }
    +
     1299.      1        ]), {
    +
     1300.      1            result: [
    +
     1301.      1                {
    +
     1302.      1                    functions: [
    +
     1303.      1                        {
    +
     1304.      1                            functionName: "test",
    +
     1305.      1                            isBlockCoverage: true,
    +
     1306.      1                            ranges: [
    +
     1307.      1                                {
    +
     1308.      1                                    count: 2,
    +
     1309.      1                                    endOffset: 4,
    +
     1310.      1                                    startOffset: 0
    +
     1311.      1                                },
    +
     1312.      1                                {
    +
     1313.      1                                    count: 1,
    +
     1314.      1                                    endOffset: 3,
    +
     1315.      1                                    startOffset: 1
    +
     1316.      1                                }
    +
     1317.      1                            ]
    +
     1318.      1                        }
    +
     1319.      1                    ],
    +
     1320.      1                    moduleUrl: "/lib.js",
    +
     1321.      1                    scriptId: "0"
    +
     1322.      1                }
    +
     1323.      1            ]
    +
     1324.      1        });
    +
     1325.      1    });
    +
     1326.      1    jstestIt((
    +
     1327.      1        "accepts arrays with two identical items for"
    +
     1328.      1        + " `v8CoverageListMerge`"
    +
     1329.      1    ), function () {
    +
     1330.      1        assertJsonEqual(v8CoverageListMerge([
    +
     1331.      1            {
    +
     1332.      1                result: [
    +
     1333.      1                    {
    +
     1334.      1                        functions: JSON.parse(functionsInput),
    +
     1335.      1                        scriptId: "123",
    +
     1336.      1                        url: "/lib.js"
    +
     1337.      1                    }, {
    +
     1338.      1                        functions: JSON.parse(functionsInput),
    +
     1339.      1                        scriptId: "123",
    +
     1340.      1                        url: "/lib.js"
    +
     1341.      1                    }
    +
     1342.      1                ]
    +
     1343.      1            }
    +
     1344.      1        ]), {
    +
     1345.      1            result: [
    +
     1346.      1                {
    +
     1347.      1                    functions: [
    +
     1348.      1                        {
    +
     1349.      1                            functionName: "test",
    +
     1350.      1                            isBlockCoverage: true,
    +
     1351.      1                            ranges: [
    +
     1352.      1                                {
    +
     1353.      1                                    count: 4,
    +
     1354.      1                                    endOffset: 4,
    +
     1355.      1                                    startOffset: 0
    +
     1356.      1                                },
    +
     1357.      1                                {
    +
     1358.      1                                    count: 2,
    +
     1359.      1                                    endOffset: 3,
    +
     1360.      1                                    startOffset: 1
    +
     1361.      1                                }
    +
     1362.      1                            ]
    +
     1363.      1                        }
    +
     1364.      1                    ],
    +
     1365.      1                    scriptId: "0",
    +
     1366.      1                    url: "/lib.js"
    +
     1367.      1                }
    +
     1368.      1            ]
    +
     1369.      1        });
    +
     1370.      1    });
    +
     1371.      1    [
    +
     1372.      1        "test_coverage_merge_is_block_coverage_test.json",
    +
     1373.      1        "test_coverage_merge_issue_2_mixed_is_block_coverage_test.json",
    +
     1374.      1        "test_coverage_merge_node_10_internal_errors_one_of_test.json",
    +
     1375.      1        "test_coverage_merge_reduced_test.json",
    +
     1376.      1        "test_coverage_merge_simple_test.json",
    +
     1377.      1        "test_coverage_merge_various_test.json"
    +
     1378.      6    ].forEach(function (file) {
    +
     1379.      6        jstestIt(file, function () {
    +
     1380.      6            file = testCoverageMergeData[file];
    +
     1381.     84            file.forEach(function ({
    +
     1382.     84                expected,
    +
     1383.     84                inputs
    +
     1384.     84            }) {
    +
     1385.     84                assertJsonEqual(v8CoverageListMerge(inputs), expected);
    +
     1386.     84            });
    +
     1387.      6        });
    +
     1388.      6    });
    +
     1389.      1    jstestIt((
    +
     1390.      1        "merge multiple node-sqlite coverage files"
    +
     1391.      1    ), function () {
    +
     1392.      1        let data1 = [
    +
     1393.      1            "test_v8_coverage_node_sqlite_9884_1633662346346_0.json",
    +
     1394.      1            "test_v8_coverage_node_sqlite_13216_1633662333140_0.json"
    +
     1395.      2        ].map(function (file) {
    +
     1396.      2            return testCoverageMergeData[file];
    +
     1397.      2        });
    +
     1398.      1        let data2 = testCoverageMergeData[
    +
     1399.      1            "test_v8_coverage_node_sqlite_merged.json"
    +
     1400.      1        ];
    +
     1401.      1        data1 = v8CoverageListMerge(data1);
    +
     1402.      1        data1 = v8CoverageListMerge([data1]);
    +
     1403.      1
    +
     1404.      1// Debug data1.
    +
     1405.      1// await moduleFs.promises.writeFile(
    +
     1406.      1//     ".test_v8_coverage_node_sqlite_merged.json",
    +
     1407.      1//     JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n"
    +
     1408.      1// );
    +
     1409.      1
    +
     1410.      1        assertJsonEqual(data1, data2);
    +
     1411.      1    });
    +
     1412.      1});
    +
     1413.      1
    +
     1414.      1jstestDescribe((
    +
     1415.      1    "test v8CoverageReportCreate handling-behavior"
    +
     1416.      1), function testBehaviorV8CoverageReportCreate() {
    +
     1417.      1    jstestIt((
    +
     1418.      1        "test null-case handling-behavior"
    +
     1419.      1    ), async function () {
    +
     1420.      1        await assertErrorThrownAsync(function () {
    +
     1421.      1            return v8CoverageReportCreate({});
    +
     1422.      1        }, "invalid coverageDir");
    +
     1423.      1    });
    +
     1424.      1    jstestIt((
    +
     1425.      1        "test coverage-report jslint.mjs handling-behavior"
    +
     1426.      1    ), async function () {
    +
     1427.      1        // test remove-old-coverage handling-behavior
    +
     1428.      1        await fsWriteFileWithParents(
    +
     1429.      1            ".tmp/coverage_jslint/coverage-0-0-0.json",
    +
     1430.      1            ""
    +
     1431.      1        );
    +
     1432.      1        await jslint.jslint_cli({
    +
     1433.      1            console_error: noop, // comment to debug
    +
     1434.      1            mode_cli: true,
    +
     1435.      1            process_argv: [
    +
     1436.      1                "node", "jslint.mjs",
    +
     1437.      1                "v8_coverage_report=.tmp/coverage_jslint",
    +
     1438.      1                "--exclude=aa.js",
    +
     1439.      1                "--include-node-modules=1",
    +
     1440.      1                "--include=jslint.mjs",
    +
     1441.      1                "node", "jslint.mjs"
    +
     1442.      1            ]
    +
     1443.      1        });
    +
     1444.      1    });
    +
     1445.      1    [
    +
     1446.      1        [
    +
     1447.      1            "v8CoverageReportCreate_high.js", (
    +
     1448.      1                "switch(0){\n"
    +
     1449.      1                + "case 0:break;\n"
    +
     1450.      1                + "}\n"
    +
     1451.      1            )
    +
     1452.      1        ], [
    +
     1453.      1            "v8CoverageReportCreate_ignore.js", (
    +
     1454.      1                "/*coverage-ignore-file*/\n"
    +
     1455.      1                + "switch(0){\n"
    +
     1456.      1                + "case 0:break;\n"
    +
     1457.      1                + "}\n"
    +
     1458.      1            )
    +
     1459.      1        ], [
    +
     1460.      1            "v8CoverageReportCreate_low.js", (
    +
     1461.      1                "switch(0){\n"
    +
     1462.      1                + "case 1:break;\n"
    +
     1463.      1                + "case 2:break;\n"
    +
     1464.      1                + "case 3:break;\n"
    +
     1465.      1                + "case 4:break;\n"
    +
     1466.      1                + "}\n"
    +
     1467.      1            )
    +
     1468.      1        ], [
    +
     1469.      1            "v8CoverageReportCreate_medium.js", (
    +
     1470.      1                "switch(0){\n"
    +
     1471.      1                + "case 0:break;\n"
    +
     1472.      1                + "case 1:break;\n"
    +
     1473.      1                + "case 2:break;\n"
    +
     1474.      1                + "}\n"
    +
     1475.      1            )
    +
     1476.      1        ]
    +
     1477.      4    ].forEach(function ([
    +
     1478.      4        file, data
    +
     1479.      4    ], ii) {
    +
     1480.      4        jstestIt(file, async function () {
    +
     1481.      4            let dir = ".tmp/coverage_" + ii + "/";
    +
     1482.      4            file = dir + file;
    +
     1483.      4            await fsWriteFileWithParents(file, data);
    +
     1484.      4            await jslint.jslint_cli({
    +
     1485.      4                console_error: noop, // comment to debug
    +
     1486.      4                mode_cli: true,
    +
     1487.      4                process_argv: [
    +
     1488.      4                    "node", "jslint.mjs",
    +
     1489.      4                    "v8_coverage_report=" + dir,
    +
     1490.      4                    "node",
    +
     1491.      4                    file
    +
     1492.      4                ]
    +
     1493.      4            });
    +
     1494.      4        });
    +
     1495.      4    });
    +
     1496.      1    jstestIt((
    +
     1497.      1        "test npm handling-behavior"
    +
     1498.      1    ), async function () {
    +
     1499.      1        await jslint.jslint_cli({
    +
     1500.      1            console_error: noop, // comment to debug
    +
     1501.      1            mode_cli: true,
    +
     1502.      1            process_argv: [
    +
     1503.      1                "node", "jslint.mjs",
    +
     1504.      1                "v8_coverage_report=.tmp/coverage_npm",
    +
     1505.      1                "npm", "--version"
    +
     1506.      1            ]
    +
     1507.      1        });
    +
     1508.      1    });
    +
     1509.      1    jstestIt((
    +
     1510.      1        "test misc handling-behavior"
    +
     1511.      1    ), async function () {
    +
     1512.      1        await Promise.all([
    +
     1513.      1            [
    +
     1514.      1                ".tmp/coverage_misc/aa.js", "\n".repeat(0x100)
    +
     1515.      1            ], [
    +
     1516.      1                ".tmp/coverage_misc/coverage-0-0-0.json", JSON.stringify({
    +
     1517.      1                    "result": [
    +
     1518.      1                        {
    +
     1519.      1                            "functions": [
    +
     1520.      1                                {
    +
     1521.      1                                    "functionName": "",
    +
     1522.      1                                    "isBlockCoverage": true,
    +
     1523.      1                                    "ranges": [
    +
     1524.      1                                        {
    +
     1525.      1                                            "count": 1,
    +
     1526.      1                                            "endOffset": 0xf0,
    +
     1527.      1                                            "startOffset": 0x10
    +
     1528.      1                                        },
    +
     1529.      1                                        {
    +
     1530.      1                                            "count": 1,
    +
     1531.      1                                            "endOffset": 0x40,
    +
     1532.      1                                            "startOffset": 0x20
    +
     1533.      1                                        },
    +
     1534.      1                                        {
    +
     1535.      1                                            "count": 1,
    +
     1536.      1                                            "endOffset": 0x80,
    +
     1537.      1                                            "startOffset": 0x60
    +
     1538.      1                                        },
    +
     1539.      1                                        {
    +
     1540.      1                                            "count": 0,
    +
     1541.      1                                            "endOffset": 0x45,
    +
     1542.      1                                            "startOffset": 0x25
    +
     1543.      1                                        },
    +
     1544.      1                                        {
    +
     1545.      1                                            "count": 0,
    +
     1546.      1                                            "endOffset": 0x85,
    +
     1547.      1                                            "startOffset": 0x65
    +
     1548.      1                                        }
    +
     1549.      1                                    ]
    +
     1550.      1                                }
    +
     1551.      1                            ],
    +
     1552.      1                            "scriptId": "0",
    +
     1553.      1                            "url": "file:///" + modulePath.resolve(
    +
     1554.      1                                ".tmp/coverage_misc/aa.js"
    +
     1555.      1                            )
    +
     1556.      1                        }
    +
     1557.      1                    ]
    +
     1558.      1                }, undefined, 4)
    +
     1559.      1            ]
    +
     1560.      2        ].map(async function ([
    +
     1561.      2            file, data
    +
     1562.      2        ]) {
    +
     1563.      2            await fsWriteFileWithParents(file, data);
    +
     1564.      2        }));
    +
     1565.      1        await jslint.jslint_cli({
    +
     1566.      1            console_error: noop, // comment to debug
    +
     1567.      1            mode_cli: true,
    +
     1568.      1            process_argv: [
    +
     1569.      1                "node", "jslint.mjs",
    +
     1570.      1                "v8_coverage_report=.tmp/coverage_misc"
    +
     1571.      1                // "node", ".tmp/coverage_misc/aa.js"
    +
     1572.      1            ]
    +
     1573.      1        });
    +
     1574.      1    });
    +
     1575.      1});
    +
     1576.      1
    +
    + + + + diff --git a/branch-alpha/.artifact/coverage/touch.txt b/branch-alpha/.artifact/coverage/touch.txt new file mode 100644 index 000000000..e69de29bb diff --git a/branch-alpha/.artifact/coverage_sqlite3_js/coverage_badge.svg b/branch-alpha/.artifact/coverage_sqlite3_js/coverage_badge.svg new file mode 100644 index 000000000..8e28d5ff5 --- /dev/null +++ b/branch-alpha/.artifact/coverage_sqlite3_js/coverage_badge.svg @@ -0,0 +1,14 @@ + + + + +coverage +97.62 % + + diff --git a/branch-alpha/.artifact/coverage_sqlite3_js/coverage_report.txt b/branch-alpha/.artifact/coverage_sqlite3_js/coverage_report.txt new file mode 100644 index 000000000..b19e8684a --- /dev/null +++ b/branch-alpha/.artifact/coverage_sqlite3_js/coverage_report.txt @@ -0,0 +1,16 @@ +V8 Coverage Report ++----------------------------------+-------------------+-------------------+ +| Files covered | Lines | Remaining | ++----------------------------------+-------------------+-------------------+ +| ./ | 97.62 % | | +| *******************************_ | 247 / 253 | 6 / 253 | ++----------------------------------+-------------------+-------------------+ +| ./lib/sqlite3-binding.js | 100.00 % | | +| ******************************** | 6 / 6 | 0 / 6 | ++----------------------------------+-------------------+-------------------+ +| ./lib/sqlite3.js | 97.11 % | | +| *******************************_ | 202 / 208 | 6 / 208 | ++----------------------------------+-------------------+-------------------+ +| ./lib/trace.js | 100.00 % | | +| ******************************** | 39 / 39 | 0 / 39 | ++----------------------------------+-------------------+-------------------+ diff --git a/branch-alpha/.artifact/coverage_sqlite3_js/index.html b/branch-alpha/.artifact/coverage_sqlite3_js/index.html new file mode 100644 index 000000000..83d95a7bd --- /dev/null +++ b/branch-alpha/.artifact/coverage_sqlite3_js/index.html @@ -0,0 +1,212 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . /
    +
    +
    +
    +
    + 97.62 %
    + 247 / 253 +
    +
    + 6 / 253 +
    + . / lib/sqlite3-binding.js
    +
    +
    +
    +
    + 100.00 %
    + 6 / 6 +
    +
    + 0 / 6 +
    + . / lib/sqlite3.js
    +
    +
    +
    +
    + 97.11 %
    + 202 / 208 +
    +
    + 6 / 208 +
    + . / lib/trace.js
    +
    +
    +
    +
    + 100.00 %
    + 39 / 39 +
    +
    + 0 / 39 +
    +
    + + + + diff --git a/branch-alpha/.artifact/coverage_sqlite3_js/lib/sqlite3-binding.js.html b/branch-alpha/.artifact/coverage_sqlite3_js/lib/sqlite3-binding.js.html new file mode 100644 index 000000000..d3fec08a6 --- /dev/null +++ b/branch-alpha/.artifact/coverage_sqlite3_js/lib/sqlite3-binding.js.html @@ -0,0 +1,174 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / lib/sqlite3-binding.js
    +
    +
    +
    +
    + 100.00 %
    + 6 / 6 +
    +
    + 0 / 6 +
    +
    + + +
    +
        1.      2const binary = require('@mapbox/node-pre-gyp');
    +
        2.      2const path = require('path');
    +
        3.      2const binding_path = binary.find(path.resolve(path.join(__dirname,'../package.json')));
    +
        4.      2const binding = require(binding_path);
    +
        5.      2module.exports = exports = binding;
    +
        6.      2
    +
    + + + + diff --git a/branch-alpha/.artifact/coverage_sqlite3_js/lib/sqlite3.js.html b/branch-alpha/.artifact/coverage_sqlite3_js/lib/sqlite3.js.html new file mode 100644 index 000000000..9e0c137cd --- /dev/null +++ b/branch-alpha/.artifact/coverage_sqlite3_js/lib/sqlite3.js.html @@ -0,0 +1,376 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / lib/sqlite3.js
    +
    +
    +
    +
    + 97.11 %
    + 202 / 208 +
    +
    + 6 / 208 +
    +
    + + +
    +
        1.      2const path = require('path');
    +
        2.      2const sqlite3 = require('./sqlite3-binding.js');
    +
        3.      2const EventEmitter = require('events').EventEmitter;
    +
        4.      2module.exports = exports = sqlite3;
    +
        5.      2
    +
        6.     12function normalizeMethod (fn) {
    +
        7.   3917    return function (sql) {
    +
        8.   3917        let errBack;
    +
        9.   3917        const args = Array.prototype.slice.call(arguments, 1);
    +
       10.   3917
    +
       11.   1119        if (typeof args[args.length - 1] === 'function') {
    +
       12.   1119            const callback = args[args.length - 1];
    +
       13.   1119            errBack = function(err) {
    +
       14.   1119                if (err) {
    +
       15.   1119                    callback(err);
    +
       16.   1119                }
    +
       17.   1119            };
    +
       18.   1119        }
    +
       19.   3917        const statement = new Statement(this, sql, errBack);
    +
       20.   3917        return fn.call(this, statement, args);
    +
       21.   3917    };
    +
       22.     12}
    +
       23.      2
    +
       24.      6function inherits(target, source) {
    +
       25.      6    for (const k in source.prototype)
    +
       26.    108        target.prototype[k] = source.prototype[k];
    +
       27.      6}
    +
       28.      2
    +
       29.      2sqlite3.cached = {
    +
       30.      4    Database: function(file, a, b) {
    +
       31.     -0        if (file === '' || file === ':memory:') {
    +
       32.     -0            // Don't cache special databases.
    +
       33.     -0            return new Database(file, a, b);
    +
       34.     -0        }
    +
       35.      4
    +
       36.      4        let db;
    +
       37.      4        file = path.resolve(file);
    +
       38.      4
    +
       39.      2        if (!sqlite3.cached.objects[file]) {
    +
       40.      2            db = sqlite3.cached.objects[file] = new Database(file, a, b);
    +
       41.      2        }
    +
       42.      2        else {
    +
       43.      2            // Make sure the callback is called.
    +
       44.      2            db = sqlite3.cached.objects[file];
    +
       45.     -0            const callback = (typeof a === 'number') ? b : a;
    +
       46.      2            if (typeof callback === 'function') {
    +
       47.      2                function cb() { callback.call(db, null); }
    +
       48.      2                if (db.open) process.nextTick(cb);
    +
       49.      2                else db.once('open', cb);
    +
       50.      2            }
    +
       51.      2        }
    +
       52.      4
    +
       53.      4        return db;
    +
       54.      4    },
    +
       55.      2    objects: {}
    +
       56.      2};
    +
       57.      2
    +
       58.      2
    +
       59.      2const Database = sqlite3.Database;
    +
       60.      2const Statement = sqlite3.Statement;
    +
       61.      2const Backup = sqlite3.Backup;
    +
       62.      2
    +
       63.      2inherits(Database, EventEmitter);
    +
       64.      2inherits(Statement, EventEmitter);
    +
       65.      2inherits(Backup, EventEmitter);
    +
       66.      2
    +
       67.      2// Database#prepare(sql, [bind1, bind2, ...], [callback])
    +
       68.   1790Database.prototype.prepare = normalizeMethod(function(statement, params) {
    +
       69.   1790    return params.length
    +
       70.      7        ? statement.bind.apply(statement, params)
    +
       71.   1783        : statement;
    +
       72.   1790});
    +
       73.      2
    +
       74.      2// Database#run(sql, [bind1, bind2, ...], [callback])
    +
       75.   2074Database.prototype.run = normalizeMethod(function(statement, params) {
    +
       76.   2074    statement.run.apply(statement, params).finalize();
    +
       77.   2074    return this;
    +
       78.   2074});
    +
       79.      2
    +
       80.      2// Database#get(sql, [bind1, bind2, ...], [callback])
    +
       81.     35Database.prototype.get = normalizeMethod(function(statement, params) {
    +
       82.     35    statement.get.apply(statement, params).finalize();
    +
       83.     35    return this;
    +
       84.     35});
    +
       85.      2
    +
       86.      2// Database#all(sql, [bind1, bind2, ...], [callback])
    +
       87.      9Database.prototype.all = normalizeMethod(function(statement, params) {
    +
       88.      9    statement.all.apply(statement, params).finalize();
    +
       89.      9    return this;
    +
       90.      9});
    +
       91.      2
    +
       92.      2// Database#each(sql, [bind1, bind2, ...], [callback], [complete])
    +
       93.      7Database.prototype.each = normalizeMethod(function(statement, params) {
    +
       94.      7    statement.each.apply(statement, params).finalize();
    +
       95.      7    return this;
    +
       96.      7});
    +
       97.      2
    +
       98.      2Database.prototype.map = normalizeMethod(function(statement, params) {
    +
       99.      2    statement.map.apply(statement, params).finalize();
    +
      100.      2    return this;
    +
      101.      2});
    +
      102.      2
    +
      103.      2// Database#backup(filename, [callback])
    +
      104.      2// Database#backup(filename, destName, sourceName, filenameIsDest, [callback])
    +
      105.     15Database.prototype.backup = function() {
    +
      106.     15    let backup;
    +
      107.     13    if (arguments.length <= 2) {
    +
      108.     13        // By default, we write the main database out to the main database of the named file.
    +
      109.     13        // This is the most likely use of the backup api.
    +
      110.     13        backup = new Backup(this, arguments[0], 'main', 'main', true, arguments[1]);
    +
      111.     13    } else {
    +
      112.      2        // Otherwise, give the user full control over the sqlite3_backup_init arguments.
    +
      113.      2        backup = new Backup(this, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]);
    +
      114.      2    }
    +
      115.     15    // Per the sqlite docs, exclude the following errors as non-fatal by default.
    +
      116.     15    backup.retryErrors = [sqlite3.BUSY, sqlite3.LOCKED];
    +
      117.     15    return backup;
    +
      118.     15};
    +
      119.      2
    +
      120.      2Statement.prototype.map = function() {
    +
      121.      2    const params = Array.prototype.slice.call(arguments);
    +
      122.      2    const callback = params.pop();
    +
      123.      2    params.push(function(err, rows) {
    +
      124.     -0        if (err) return callback(err);
    +
      125.      2        const result = {};
    +
      126.      2        if (rows.length) {
    +
      127.      2            const keys = Object.keys(rows[0]);
    +
      128.      2            const key = keys[0];
    +
      129.      1            if (keys.length > 2) {
    +
      130.      1                // Value is an object
    +
      131.      5                for (let i = 0; i < rows.length; i++) {
    +
      132.      5                    result[rows[i][key]] = rows[i];
    +
      133.      5                }
    +
      134.      1            } else {
    +
      135.      1                const value = keys[1];
    +
      136.      1                // Value is a plain value
    +
      137.      5                for (let i = 0; i < rows.length; i++) {
    +
      138.      5                    result[rows[i][key]] = rows[i][value];
    +
      139.      5                }
    +
      140.      1            }
    +
      141.      2        }
    +
      142.      2        callback(err, result);
    +
      143.      2    });
    +
      144.      2    return this.all.apply(this, params);
    +
      145.      2};
    +
      146.      2
    +
      147.      2let isVerbose = false;
    +
      148.      2
    +
      149.      2const supportedEvents = [ 'trace', 'profile', 'insert', 'update', 'delete' ];
    +
      150.      2
    +
      151.      7Database.prototype.addListener = Database.prototype.on = function(type) {
    +
      152.      7    const val = EventEmitter.prototype.addListener.apply(this, arguments);
    +
      153.      4    if (supportedEvents.indexOf(type) >= 0) {
    +
      154.      4        this.configure(type, true);
    +
      155.      4    }
    +
      156.      7    return val;
    +
      157.      7};
    +
      158.      2
    +
      159.      2Database.prototype.removeListener = function(type) {
    +
      160.      2    const val = EventEmitter.prototype.removeListener.apply(this, arguments);
    +
      161.      1    if (supportedEvents.indexOf(type) >= 0 && !this._events[type]) {
    +
      162.      1        this.configure(type, false);
    +
      163.      1    }
    +
      164.      2    return val;
    +
      165.      2};
    +
      166.      2
    +
      167.      1Database.prototype.removeAllListeners = function(type) {
    +
      168.      1    const val = EventEmitter.prototype.removeAllListeners.apply(this, arguments);
    +
      169.      1    if (supportedEvents.indexOf(type) >= 0) {
    +
      170.      1        this.configure(type, false);
    +
      171.      1    }
    +
      172.      1    return val;
    +
      173.      1};
    +
      174.      2
    +
      175.      2// Save the stack trace over EIO callbacks.
    +
      176.      1sqlite3.verbose = function() {
    +
      177.      1    if (!isVerbose) {
    +
      178.      1        const trace = require('./trace');
    +
      179.      1        [
    +
      180.      1            'prepare',
    +
      181.      1            'get',
    +
      182.      1            'run',
    +
      183.      1            'all',
    +
      184.      1            'each',
    +
      185.      1            'map',
    +
      186.      1            'close',
    +
      187.      1            'exec'
    +
      188.      8        ].forEach(function (name) {
    +
      189.      8            trace.extendTrace(Database.prototype, name);
    +
      190.      8        });
    +
      191.      1        [
    +
      192.      1            'bind',
    +
      193.      1            'get',
    +
      194.      1            'run',
    +
      195.      1            'all',
    +
      196.      1            'each',
    +
      197.      1            'map',
    +
      198.      1            'reset',
    +
      199.      1            'finalize',
    +
      200.      8        ].forEach(function (name) {
    +
      201.      8            trace.extendTrace(Statement.prototype, name);
    +
      202.      8        });
    +
      203.      1        isVerbose = true;
    +
      204.      1    }
    +
      205.      1
    +
      206.      1    return this;
    +
      207.      1};
    +
      208.      2
    +
    + + + + diff --git a/branch-alpha/.artifact/coverage_sqlite3_js/lib/trace.js.html b/branch-alpha/.artifact/coverage_sqlite3_js/lib/trace.js.html new file mode 100644 index 000000000..28608da6d --- /dev/null +++ b/branch-alpha/.artifact/coverage_sqlite3_js/lib/trace.js.html @@ -0,0 +1,207 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / lib/trace.js
    +
    +
    +
    +
    + 100.00 %
    + 39 / 39 +
    +
    + 0 / 39 +
    +
    + + +
    +
        1.      1// Inspired by https://github.com/tlrobinson/long-stack-traces
    +
        2.      1const util = require('util');
    +
        3.      1
    +
        4.     16function extendTrace(object, property, pos) {
    +
        5.     16    const old = object[property];
    +
        6.      1    object[property] = function() {
    +
        7.      1        const error = new Error();
    +
        8.      1        const name = object.constructor.name + '#' + property + '(' +
    +
        9.      2            Array.prototype.slice.call(arguments).map(function(el) {
    +
       10.      2                return util.inspect(el, false, 0);
    +
       11.      2            }).join(', ') + ')';
    +
       12.      1
    +
       13.      1        if (typeof pos === 'undefined') pos = -1;
    +
       14.      1        if (pos < 0) pos += arguments.length;
    +
       15.      1        const cb = arguments[pos];
    +
       16.      1        if (typeof arguments[pos] === 'function') {
    +
       17.      1            arguments[pos] = function replacement() {
    +
       18.      1                const err = arguments[0];
    +
       19.      1                if (err && err.stack && !err.__augmented) {
    +
       20.      1                    err.stack = filter(err).join('\n');
    +
       21.      1                    err.stack += '\n--> in ' + name;
    +
       22.      1                    err.stack += '\n' + filter(error).slice(1).join('\n');
    +
       23.      1                    err.__augmented = true;
    +
       24.      1                }
    +
       25.      1                return cb.apply(this, arguments);
    +
       26.      1            };
    +
       27.      1        }
    +
       28.      1        return old.apply(this, arguments);
    +
       29.      1    };
    +
       30.     16}
    +
       31.      1exports.extendTrace = extendTrace;
    +
       32.      1
    +
       33.      1
    +
       34.      2function filter(error) {
    +
       35.     13    return error.stack.split('\n').filter(function(line) {
    +
       36.     13        return line.indexOf(__filename) < 0;
    +
       37.     13    });
    +
       38.      2}
    +
       39.      1
    +
    + + + + diff --git a/branch-alpha/.artifact/coverage_sqlite3_js/touch.txt b/branch-alpha/.artifact/coverage_sqlite3_js/touch.txt new file mode 100644 index 000000000..e69de29bb diff --git a/branch-alpha/.artifact/coverage_sqlite3_sh/coverage_badge.svg b/branch-alpha/.artifact/coverage_sqlite3_sh/coverage_badge.svg new file mode 100644 index 000000000..8e28d5ff5 --- /dev/null +++ b/branch-alpha/.artifact/coverage_sqlite3_sh/coverage_badge.svg @@ -0,0 +1,14 @@ + + + + +coverage +97.62 % + + diff --git a/branch-alpha/.artifact/coverage_sqlite3_sh/coverage_report.txt b/branch-alpha/.artifact/coverage_sqlite3_sh/coverage_report.txt new file mode 100644 index 000000000..b19e8684a --- /dev/null +++ b/branch-alpha/.artifact/coverage_sqlite3_sh/coverage_report.txt @@ -0,0 +1,16 @@ +V8 Coverage Report ++----------------------------------+-------------------+-------------------+ +| Files covered | Lines | Remaining | ++----------------------------------+-------------------+-------------------+ +| ./ | 97.62 % | | +| *******************************_ | 247 / 253 | 6 / 253 | ++----------------------------------+-------------------+-------------------+ +| ./lib/sqlite3-binding.js | 100.00 % | | +| ******************************** | 6 / 6 | 0 / 6 | ++----------------------------------+-------------------+-------------------+ +| ./lib/sqlite3.js | 97.11 % | | +| *******************************_ | 202 / 208 | 6 / 208 | ++----------------------------------+-------------------+-------------------+ +| ./lib/trace.js | 100.00 % | | +| ******************************** | 39 / 39 | 0 / 39 | ++----------------------------------+-------------------+-------------------+ diff --git a/branch-alpha/.artifact/coverage_sqlite3_sh/index.html b/branch-alpha/.artifact/coverage_sqlite3_sh/index.html new file mode 100644 index 000000000..83d95a7bd --- /dev/null +++ b/branch-alpha/.artifact/coverage_sqlite3_sh/index.html @@ -0,0 +1,212 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . /
    +
    +
    +
    +
    + 97.62 %
    + 247 / 253 +
    +
    + 6 / 253 +
    + . / lib/sqlite3-binding.js
    +
    +
    +
    +
    + 100.00 %
    + 6 / 6 +
    +
    + 0 / 6 +
    + . / lib/sqlite3.js
    +
    +
    +
    +
    + 97.11 %
    + 202 / 208 +
    +
    + 6 / 208 +
    + . / lib/trace.js
    +
    +
    +
    +
    + 100.00 %
    + 39 / 39 +
    +
    + 0 / 39 +
    +
    + + + + diff --git a/branch-alpha/.artifact/coverage_sqlite3_sh/lib/sqlite3-binding.js.html b/branch-alpha/.artifact/coverage_sqlite3_sh/lib/sqlite3-binding.js.html new file mode 100644 index 000000000..d3fec08a6 --- /dev/null +++ b/branch-alpha/.artifact/coverage_sqlite3_sh/lib/sqlite3-binding.js.html @@ -0,0 +1,174 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / lib/sqlite3-binding.js
    +
    +
    +
    +
    + 100.00 %
    + 6 / 6 +
    +
    + 0 / 6 +
    +
    + + +
    +
        1.      2const binary = require('@mapbox/node-pre-gyp');
    +
        2.      2const path = require('path');
    +
        3.      2const binding_path = binary.find(path.resolve(path.join(__dirname,'../package.json')));
    +
        4.      2const binding = require(binding_path);
    +
        5.      2module.exports = exports = binding;
    +
        6.      2
    +
    + + + + diff --git a/branch-alpha/.artifact/coverage_sqlite3_sh/lib/sqlite3.js.html b/branch-alpha/.artifact/coverage_sqlite3_sh/lib/sqlite3.js.html new file mode 100644 index 000000000..1f142d933 --- /dev/null +++ b/branch-alpha/.artifact/coverage_sqlite3_sh/lib/sqlite3.js.html @@ -0,0 +1,376 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / lib/sqlite3.js
    +
    +
    +
    +
    + 97.11 %
    + 202 / 208 +
    +
    + 6 / 208 +
    +
    + + +
    +
        1.      2const path = require('path');
    +
        2.      2const sqlite3 = require('./sqlite3-binding.js');
    +
        3.      2const EventEmitter = require('events').EventEmitter;
    +
        4.      2module.exports = exports = sqlite3;
    +
        5.      2
    +
        6.     12function normalizeMethod (fn) {
    +
        7.   3799    return function (sql) {
    +
        8.   3799        let errBack;
    +
        9.   3799        const args = Array.prototype.slice.call(arguments, 1);
    +
       10.   3799
    +
       11.   1119        if (typeof args[args.length - 1] === 'function') {
    +
       12.   1119            const callback = args[args.length - 1];
    +
       13.   1119            errBack = function(err) {
    +
       14.   1119                if (err) {
    +
       15.   1119                    callback(err);
    +
       16.   1119                }
    +
       17.   1119            };
    +
       18.   1119        }
    +
       19.   3799        const statement = new Statement(this, sql, errBack);
    +
       20.   3799        return fn.call(this, statement, args);
    +
       21.   3799    };
    +
       22.     12}
    +
       23.      2
    +
       24.      6function inherits(target, source) {
    +
       25.      6    for (const k in source.prototype)
    +
       26.    108        target.prototype[k] = source.prototype[k];
    +
       27.      6}
    +
       28.      2
    +
       29.      2sqlite3.cached = {
    +
       30.      4    Database: function(file, a, b) {
    +
       31.     -0        if (file === '' || file === ':memory:') {
    +
       32.     -0            // Don't cache special databases.
    +
       33.     -0            return new Database(file, a, b);
    +
       34.     -0        }
    +
       35.      4
    +
       36.      4        let db;
    +
       37.      4        file = path.resolve(file);
    +
       38.      4
    +
       39.      2        if (!sqlite3.cached.objects[file]) {
    +
       40.      2            db = sqlite3.cached.objects[file] = new Database(file, a, b);
    +
       41.      2        }
    +
       42.      2        else {
    +
       43.      2            // Make sure the callback is called.
    +
       44.      2            db = sqlite3.cached.objects[file];
    +
       45.     -0            const callback = (typeof a === 'number') ? b : a;
    +
       46.      2            if (typeof callback === 'function') {
    +
       47.      2                function cb() { callback.call(db, null); }
    +
       48.      2                if (db.open) process.nextTick(cb);
    +
       49.      2                else db.once('open', cb);
    +
       50.      2            }
    +
       51.      2        }
    +
       52.      4
    +
       53.      4        return db;
    +
       54.      4    },
    +
       55.      2    objects: {}
    +
       56.      2};
    +
       57.      2
    +
       58.      2
    +
       59.      2const Database = sqlite3.Database;
    +
       60.      2const Statement = sqlite3.Statement;
    +
       61.      2const Backup = sqlite3.Backup;
    +
       62.      2
    +
       63.      2inherits(Database, EventEmitter);
    +
       64.      2inherits(Statement, EventEmitter);
    +
       65.      2inherits(Backup, EventEmitter);
    +
       66.      2
    +
       67.      2// Database#prepare(sql, [bind1, bind2, ...], [callback])
    +
       68.   1672Database.prototype.prepare = normalizeMethod(function(statement, params) {
    +
       69.   1672    return params.length
    +
       70.      7        ? statement.bind.apply(statement, params)
    +
       71.   1665        : statement;
    +
       72.   1672});
    +
       73.      2
    +
       74.      2// Database#run(sql, [bind1, bind2, ...], [callback])
    +
       75.   2074Database.prototype.run = normalizeMethod(function(statement, params) {
    +
       76.   2074    statement.run.apply(statement, params).finalize();
    +
       77.   2074    return this;
    +
       78.   2074});
    +
       79.      2
    +
       80.      2// Database#get(sql, [bind1, bind2, ...], [callback])
    +
       81.     35Database.prototype.get = normalizeMethod(function(statement, params) {
    +
       82.     35    statement.get.apply(statement, params).finalize();
    +
       83.     35    return this;
    +
       84.     35});
    +
       85.      2
    +
       86.      2// Database#all(sql, [bind1, bind2, ...], [callback])
    +
       87.      9Database.prototype.all = normalizeMethod(function(statement, params) {
    +
       88.      9    statement.all.apply(statement, params).finalize();
    +
       89.      9    return this;
    +
       90.      9});
    +
       91.      2
    +
       92.      2// Database#each(sql, [bind1, bind2, ...], [callback], [complete])
    +
       93.      7Database.prototype.each = normalizeMethod(function(statement, params) {
    +
       94.      7    statement.each.apply(statement, params).finalize();
    +
       95.      7    return this;
    +
       96.      7});
    +
       97.      2
    +
       98.      2Database.prototype.map = normalizeMethod(function(statement, params) {
    +
       99.      2    statement.map.apply(statement, params).finalize();
    +
      100.      2    return this;
    +
      101.      2});
    +
      102.      2
    +
      103.      2// Database#backup(filename, [callback])
    +
      104.      2// Database#backup(filename, destName, sourceName, filenameIsDest, [callback])
    +
      105.     15Database.prototype.backup = function() {
    +
      106.     15    let backup;
    +
      107.     13    if (arguments.length <= 2) {
    +
      108.     13        // By default, we write the main database out to the main database of the named file.
    +
      109.     13        // This is the most likely use of the backup api.
    +
      110.     13        backup = new Backup(this, arguments[0], 'main', 'main', true, arguments[1]);
    +
      111.     13    } else {
    +
      112.      2        // Otherwise, give the user full control over the sqlite3_backup_init arguments.
    +
      113.      2        backup = new Backup(this, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]);
    +
      114.      2    }
    +
      115.     15    // Per the sqlite docs, exclude the following errors as non-fatal by default.
    +
      116.     15    backup.retryErrors = [sqlite3.BUSY, sqlite3.LOCKED];
    +
      117.     15    return backup;
    +
      118.     15};
    +
      119.      2
    +
      120.      2Statement.prototype.map = function() {
    +
      121.      2    const params = Array.prototype.slice.call(arguments);
    +
      122.      2    const callback = params.pop();
    +
      123.      2    params.push(function(err, rows) {
    +
      124.     -0        if (err) return callback(err);
    +
      125.      2        const result = {};
    +
      126.      2        if (rows.length) {
    +
      127.      2            const keys = Object.keys(rows[0]);
    +
      128.      2            const key = keys[0];
    +
      129.      1            if (keys.length > 2) {
    +
      130.      1                // Value is an object
    +
      131.      5                for (let i = 0; i < rows.length; i++) {
    +
      132.      5                    result[rows[i][key]] = rows[i];
    +
      133.      5                }
    +
      134.      1            } else {
    +
      135.      1                const value = keys[1];
    +
      136.      1                // Value is a plain value
    +
      137.      5                for (let i = 0; i < rows.length; i++) {
    +
      138.      5                    result[rows[i][key]] = rows[i][value];
    +
      139.      5                }
    +
      140.      1            }
    +
      141.      2        }
    +
      142.      2        callback(err, result);
    +
      143.      2    });
    +
      144.      2    return this.all.apply(this, params);
    +
      145.      2};
    +
      146.      2
    +
      147.      2let isVerbose = false;
    +
      148.      2
    +
      149.      2const supportedEvents = [ 'trace', 'profile', 'insert', 'update', 'delete' ];
    +
      150.      2
    +
      151.      7Database.prototype.addListener = Database.prototype.on = function(type) {
    +
      152.      7    const val = EventEmitter.prototype.addListener.apply(this, arguments);
    +
      153.      4    if (supportedEvents.indexOf(type) >= 0) {
    +
      154.      4        this.configure(type, true);
    +
      155.      4    }
    +
      156.      7    return val;
    +
      157.      7};
    +
      158.      2
    +
      159.      2Database.prototype.removeListener = function(type) {
    +
      160.      2    const val = EventEmitter.prototype.removeListener.apply(this, arguments);
    +
      161.      1    if (supportedEvents.indexOf(type) >= 0 && !this._events[type]) {
    +
      162.      1        this.configure(type, false);
    +
      163.      1    }
    +
      164.      2    return val;
    +
      165.      2};
    +
      166.      2
    +
      167.      1Database.prototype.removeAllListeners = function(type) {
    +
      168.      1    const val = EventEmitter.prototype.removeAllListeners.apply(this, arguments);
    +
      169.      1    if (supportedEvents.indexOf(type) >= 0) {
    +
      170.      1        this.configure(type, false);
    +
      171.      1    }
    +
      172.      1    return val;
    +
      173.      1};
    +
      174.      2
    +
      175.      2// Save the stack trace over EIO callbacks.
    +
      176.      1sqlite3.verbose = function() {
    +
      177.      1    if (!isVerbose) {
    +
      178.      1        const trace = require('./trace');
    +
      179.      1        [
    +
      180.      1            'prepare',
    +
      181.      1            'get',
    +
      182.      1            'run',
    +
      183.      1            'all',
    +
      184.      1            'each',
    +
      185.      1            'map',
    +
      186.      1            'close',
    +
      187.      1            'exec'
    +
      188.      8        ].forEach(function (name) {
    +
      189.      8            trace.extendTrace(Database.prototype, name);
    +
      190.      8        });
    +
      191.      1        [
    +
      192.      1            'bind',
    +
      193.      1            'get',
    +
      194.      1            'run',
    +
      195.      1            'all',
    +
      196.      1            'each',
    +
      197.      1            'map',
    +
      198.      1            'reset',
    +
      199.      1            'finalize',
    +
      200.      8        ].forEach(function (name) {
    +
      201.      8            trace.extendTrace(Statement.prototype, name);
    +
      202.      8        });
    +
      203.      1        isVerbose = true;
    +
      204.      1    }
    +
      205.      1
    +
      206.      1    return this;
    +
      207.      1};
    +
      208.      2
    +
    + + + + diff --git a/branch-alpha/.artifact/coverage_sqlite3_sh/lib/trace.js.html b/branch-alpha/.artifact/coverage_sqlite3_sh/lib/trace.js.html new file mode 100644 index 000000000..28608da6d --- /dev/null +++ b/branch-alpha/.artifact/coverage_sqlite3_sh/lib/trace.js.html @@ -0,0 +1,207 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / lib/trace.js
    +
    +
    +
    +
    + 100.00 %
    + 39 / 39 +
    +
    + 0 / 39 +
    +
    + + +
    +
        1.      1// Inspired by https://github.com/tlrobinson/long-stack-traces
    +
        2.      1const util = require('util');
    +
        3.      1
    +
        4.     16function extendTrace(object, property, pos) {
    +
        5.     16    const old = object[property];
    +
        6.      1    object[property] = function() {
    +
        7.      1        const error = new Error();
    +
        8.      1        const name = object.constructor.name + '#' + property + '(' +
    +
        9.      2            Array.prototype.slice.call(arguments).map(function(el) {
    +
       10.      2                return util.inspect(el, false, 0);
    +
       11.      2            }).join(', ') + ')';
    +
       12.      1
    +
       13.      1        if (typeof pos === 'undefined') pos = -1;
    +
       14.      1        if (pos < 0) pos += arguments.length;
    +
       15.      1        const cb = arguments[pos];
    +
       16.      1        if (typeof arguments[pos] === 'function') {
    +
       17.      1            arguments[pos] = function replacement() {
    +
       18.      1                const err = arguments[0];
    +
       19.      1                if (err && err.stack && !err.__augmented) {
    +
       20.      1                    err.stack = filter(err).join('\n');
    +
       21.      1                    err.stack += '\n--> in ' + name;
    +
       22.      1                    err.stack += '\n' + filter(error).slice(1).join('\n');
    +
       23.      1                    err.__augmented = true;
    +
       24.      1                }
    +
       25.      1                return cb.apply(this, arguments);
    +
       26.      1            };
    +
       27.      1        }
    +
       28.      1        return old.apply(this, arguments);
    +
       29.      1    };
    +
       30.     16}
    +
       31.      1exports.extendTrace = extendTrace;
    +
       32.      1
    +
       33.      1
    +
       34.      2function filter(error) {
    +
       35.     13    return error.stack.split('\n').filter(function(line) {
    +
       36.     13        return line.indexOf(__filename) < 0;
    +
       37.     13    });
    +
       38.      2}
    +
       39.      1
    +
    + + + + diff --git a/branch-alpha/.artifact/coverage_sqlite3_sh/touch.txt b/branch-alpha/.artifact/coverage_sqlite3_sh/touch.txt new file mode 100644 index 000000000..e69de29bb diff --git a/branch-alpha/.artifact/jslint_report_hello.html b/branch-alpha/.artifact/jslint_report_hello.html new file mode 100644 index 000000000..ffa15a7ff --- /dev/null +++ b/branch-alpha/.artifact/jslint_report_hello.html @@ -0,0 +1,246 @@ + + +
    +JSLint Report +
    +
    +Report: Warnings (2) +
    +
    1: 17
    1. Undeclared 'console'.
    function foo() {console.log('hello world');} + +
    1: 29
    2. Use double quotes, not single quotes.
    function foo() {console.log('hello world');} + +
    +
    +
    +Report: Properties (1) + +
    +
    +Report: Functions (1) +
    +
    +
    global
    foo
    +
    1: 1
    foo()
    +
    +
    + diff --git a/branch-alpha/.artifact/jslint_wrapper_vscode/.vscode/launch.json b/branch-alpha/.artifact/jslint_wrapper_vscode/.vscode/launch.json new file mode 100644 index 000000000..140e1f4ec --- /dev/null +++ b/branch-alpha/.artifact/jslint_wrapper_vscode/.vscode/launch.json @@ -0,0 +1,13 @@ +{ + "configurations": [ + { + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}" + ], + "name": "Run Extension", + "request": "launch", + "type": "extensionHost" + } + ], + "version": "0.0.1" +} diff --git a/branch-alpha/.artifact/jslint_wrapper_vscode/.vscodeignore b/branch-alpha/.artifact/jslint_wrapper_vscode/.vscodeignore new file mode 100644 index 000000000..b81046e0b --- /dev/null +++ b/branch-alpha/.artifact/jslint_wrapper_vscode/.vscodeignore @@ -0,0 +1,12 @@ +* +.* +node_modules +!.npmignore +!CHANGELOG.md +!LICENSE +!README.md + +!asset_* +!jslint.mjs +!jslint_wrapper_cjs.cjs +!jslint_wrapper_vscode.js diff --git a/branch-alpha/.artifact/jslint_wrapper_vscode/LICENSE b/branch-alpha/.artifact/jslint_wrapper_vscode/LICENSE new file mode 100644 index 000000000..b3dbff00c --- /dev/null +++ b/branch-alpha/.artifact/jslint_wrapper_vscode/LICENSE @@ -0,0 +1,22 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/branch-alpha/.artifact/jslint_wrapper_vscode/README.md b/branch-alpha/.artifact/jslint_wrapper_vscode/README.md new file mode 100644 index 000000000..af9808967 --- /dev/null +++ b/branch-alpha/.artifact/jslint_wrapper_vscode/README.md @@ -0,0 +1,9 @@ +# Quickstart JSLint in VSCode +1. In VSCode, search and install extension [`vscode-jslint`](https://marketplace.visualstudio.com/items?itemName=jslint.vscode-jslint) +2. In VSCode, while editing a javascript file: + - right-click context-menu and select `[JSLint - Lint File]` + - or use key-binding `[Ctrl + Shift + J], [L]` + - or use key-binding `[ Cmd + Shift + J], [L]` for Mac +- screenshot + +[![screenshot](https://jslint-org.github.io/jslint/asset_image_jslint_wrapper_vscode.png)](https://marketplace.visualstudio.com/items?itemName=jslint.vscode-jslint) diff --git a/branch-alpha/.artifact/jslint_wrapper_vscode/asset_image_logo_512.png b/branch-alpha/.artifact/jslint_wrapper_vscode/asset_image_logo_512.png new file mode 100644 index 000000000..19d154d68 Binary files /dev/null and b/branch-alpha/.artifact/jslint_wrapper_vscode/asset_image_logo_512.png differ diff --git a/branch-alpha/.artifact/jslint_wrapper_vscode/jslint.mjs b/branch-alpha/.artifact/jslint_wrapper_vscode/jslint.mjs new file mode 100644 index 000000000..b486c1ce2 --- /dev/null +++ b/branch-alpha/.artifact/jslint_wrapper_vscode/jslint.mjs @@ -0,0 +1,11573 @@ +// #!/usr/bin/env node +// JSLint + +// The Unlicense +// +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + + +// jslint(source, option_dict, global_list) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze. +// option_dict An object whose keys correspond to option names. +// global_list An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// PHASE 1. Split by newlines into . +// PHASE 2. Lex into . +// PHASE 3. Parse into using the Pratt-parser. +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// PHASE 5. Check whitespace between tokens in . + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*jslint beta, node*/ +/*property + JSLINT_BETA, NODE_V8_COVERAGE, a, all, argv, arity, artifact, + assertErrorThrownAsync, assertJsonEqual, assertOrThrow, assign, async, b, + beta, bitwise, block, body, browser, c, calls, catch, catch_list, + catch_stack, causes, char, children, clear, closer, closure, code, column, + concat, consoleError, console_error, console_log, constant, context, + convert, count, coverageDir, create, cwd, d, dead, debugInline, default, + delta, devel, directive, directive_ignore_line, directive_list, directives, + dirname, disrupt, dot, edition, elem_list, ellipsis, else, end, endOffset, + endsWith, entries, env, error, eval, every, example_list, excludeList, exec, + execArgv, exit, exitCode, export_dict, exports, expression, extra, fart, + file, fileList, fileURLToPath, filter, finally, flag, floor, for, forEach, + formatted_message, free, freeze, from, froms, fsWriteFileWithParents, + fud_stmt, functionName, function_list, function_stack, functions, get, + getset, github_repo, globExclude, global, global_dict, global_list, + holeList, htmlEscape, id, identifier, import, import_list, import_meta_url, + inc, includeList, indent2, index, indexOf, init, initial, isArray, + isBlockCoverage, isHole, isNaN, is_equal, is_weird, join, jslint, + jslint_apidoc, jslint_assert, jslint_charset_ascii, jslint_cli, + jslint_edition, jslint_phase1_split, jslint_phase2_lex, jslint_phase3_parse, + jslint_phase4_walk, jslint_phase5_whitage, jslint_report, json, + jstestDescribe, jstestIt, jstestOnExit, keys, label, lbp, led_infix, length, + level, line, lineList, line_list, line_offset, line_source, lines, + linesCovered, linesTotal, live, log, long, loop, m, map, margin, match, max, + message, meta, min, mkdir, modeCoverageIgnoreFile, modeIndex, mode_cli, + mode_conditional, mode_json, mode_module, mode_noop, mode_property, + mode_shebang, mode_stop, module, moduleFsInit, moduleName, module_list, + name, names, node, nomen, noop, now, nr, nud_prefix, + objectDeepCopyWithKeysSorted, ok, on, open, opening, option, option_dict, + order, package_name, padEnd, padStart, parameters, parent, parentIi, parse, + pathname, pathnameList, platform, pop, processArgv, process_argv, + process_env, process_exit, promises, property, property_dict, push, quote, + ranges, readFile, readdir, readonly, recursive, reduce, repeat, replace, + resolve, result, reverse, role, round, scriptId, search, set, shebang, + shell, shift, signature, single, slice, some, sort, source, spawn, splice, + split, stack, stack_trace, start, startOffset, startsWith, statement, + statement_prv, stdio, stop, stop_at, stringify, subscript, switch, + syntax_dict, tenure, test, test_cause, test_internal_error, this, thru, + toLocaleString, toString, token, token_global, token_list, token_nxt, + token_tree, tokens, trace, tree, trim, trimEnd, trimRight, try, type, + unlink, unordered, unshift, url, used, v8CoverageListMerge, + v8CoverageReportCreate, value, variable, version, versions, warn, warn_at, + warning, warning_list, warnings, white, wrapped, writeFile +*/ + +// init debugInline +let debugInline = (function () { + let __consoleError = function () { + return; + }; + function debug(...argv) { + +// This function will print to stderr and then return [0]. + + __consoleError("\n\ndebugInline"); + __consoleError(...argv); + __consoleError("\n"); + return argv[0]; + } + debug(); // Coverage-hack. + __consoleError = console.error; //jslint-ignore-line + return debug; +}()); +let jslint_charset_ascii = ( + "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007" + + "\b\t\n\u000b\f\r\u000e\u000f" + + "\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017" + + "\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f" + + " !\"#$%&'()*+,-./0123456789:;<=>?" + + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + + "`abcdefghijklmnopqrstuvwxyz{|}~\u007f" +); +let jslint_edition = "v2024.11.24"; +let jslint_export; // The jslint object to be exported. +let jslint_fudge = 1; // Fudge starting line and starting + // ... column to 1. +let jslint_import_meta_url = ""; // import.meta.url used by cli. +let jslint_rgx_cap = ( + /^[A-Z]/ +); +let jslint_rgx_crlf = ( + /\n|\r\n?/ +); +let jslint_rgx_digits_bits = ( + /^[01_]*/ +); +let jslint_rgx_digits_decimals = ( + /^[0-9_]*/ +); +let jslint_rgx_digits_hexs = ( + /^[0-9A-F_]*/i +); +let jslint_rgx_digits_octals = ( + /^[0-7_]*/ +); +let jslint_rgx_directive = ( + /^(jslint|property|global)\s+(.*)$/ +); +let jslint_rgx_directive_part = ( + /([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*|$/g +); +let jslint_rgx_identifier = ( + /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/ +); +let jslint_rgx_json_number = ( + +// https://datatracker.ietf.org/doc/html/rfc7159#section-6 +// number = [ minus ] int [ frac ] [ exp ] + + /^-?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][\-+]?\d+)?$/ +); +let jslint_rgx_mega = ( + +// Vim-hack - vim-editor has trouble parsing naked '`' in regexp + + /[\u0060\\]|\$\{/ +); +let jslint_rgx_module = ( + /^[a-zA-Z0-9_$:.@\-\/]+$/ +); +let jslint_rgx_numeric_separator_illegal = ( + /__|_$|_n$/m +); +let jslint_rgx_slash_star_or_slash = ( + /\/\*|\/$/ +); +let jslint_rgx_tab = ( + /\t/g +); +let jslint_rgx_todo = ( + /\b(?:todo|TO\s?DO|HACK)\b/ +); +let jslint_rgx_token = new RegExp( + "^(" + + "(\\s+)" + + "|([a-zA-Z_$][a-zA-Z0-9_$]*)" + + "|[(){}\\[\\],:;'\"~\\`]" + + "|\\?[?.]?" + + "|=(?:==?|>)?" + + "|\\.+" + + "|\\*[*\\/=]?" + + "|\\/[*\\/]?" + + "|\\+[=+]?" + + "|-[=\\-]?" + + "|[\\^%]=?" + + "|&[&=]?" + + "|\\" + + "|[|=]?" + + "|>{1,3}=?" + + "|< throws an error. + + let err; + try { + await asyncFunc(); + } catch (errCaught) { + err = errCaught; + } + assertOrThrow(err, "No error thrown."); + assertOrThrow( + !regexp || new RegExp(regexp).test(err.message), + err + ); +} + +function assertJsonEqual(aa, bb, message) { + +// This function will assert JSON.stringify() === JSON.stringify(). + + aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa), undefined, 1); + bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb), undefined, 1); + if (aa !== bb) { + throw new Error( + "\n" + aa + "\n!==\n" + bb + + ( + typeof message === "string" + ? " - " + message + : message + ? " - " + JSON.stringify(message) + : "" + ) + ); + } +} + +function assertOrThrow(condition, message) { + +// This function will throw if is falsy. + + if (!condition) { + throw ( + (!message || typeof message === "string") + ? new Error(String(message).slice(0, 2048)) + : message + ); + } +} + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than '{}' because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +async function fsWriteFileWithParents(pathname, data) { + +// This function will write to and lazy-mkdirp if necessary. + + await moduleFsInit(); + +// Try writing to pathname. + + try { + await moduleFs.promises.writeFile(pathname, data); + } catch (ignore) { + +// Lazy mkdirp. + + await moduleFs.promises.mkdir(modulePath.dirname(pathname), { + recursive: true + }); + +// Retry writing to pathname. + + await moduleFs.promises.writeFile(pathname, data); + } + console.error("wrote file " + pathname); +} + +function globExclude({ + excludeList = [], + includeList = [], + pathnameList = [] +}) { + +// This function will +// 1. Exclude pathnames in that don't match glob-patterns in +// . +// 2. Exclude pathnames in that match glob-patterns in +// . + + function globAssertNotWeird(list, name) { + +// This function will check if of strings contain weird characters. + + [ + [ + "\n", ( + /^.*?([\u0000-\u0007\r]).*/gm + ) + ], + [ + "\r", ( + /^.*?([\n]).*/gm + ) + ] + ].forEach(function ([ + separator, rgx + ]) { + list.join(separator).replace(rgx, function (match0, char) { + throw new Error( + "Weird character " + + JSON.stringify(char) + + " found in " + name + " " + + JSON.stringify(match0) + ); + }); + }); + } + + function globToRegexp(pattern) { + +// This function will translate glob to javascript-regexp, +// which javascript can then use to "glob" pathnames. + + let ii = 0; + let isClass = false; + let strClass = ""; + let strRegex = ""; + pattern = pattern.replace(( + /\/\/+/g + ), "/"); + pattern = pattern.replace(( + /\*\*\*+/g + ), "**"); + pattern.replace(( + /\\\\|\\\[|\\\]|\[|\]|./g + ), function (match0) { + switch (match0) { + case "[": + if (isClass) { + strClass += "["; + return; + } + strClass += "\u0000"; + strRegex += "\u0000"; + isClass = true; + return; + case "]": + if (isClass) { + isClass = false; + return; + } + strRegex += "]"; + return; + default: + if (isClass) { + strClass += match0; + return; + } + strRegex += match0; + } + return ""; + }); + strClass += "\u0000"; + +// An expression "[!...]" matches a single character, namely any character that +// is not matched by the expression obtained by removing the first '!' from it. +// (Thus, "[!a-]" matches any single character except 'a', and '-'.) + + strClass = strClass.replace(( + /\u0000!/g + ), "\u0000^"); + +// One may include '-' in its literal meaning by making it the first or last +// character between the brackets. + + strClass = strClass.replace(( + /\u0000-/g + ), "\u0000\\-"); + strClass = strClass.replace(( + /-\u0000/g + ), "\\-\u0000"); + +// Escape brackets '[', ']' in character class. + + strClass = strClass.replace(( + /[\[\]]/g + ), "\\$&"); + +// https://stackoverflow.com/questions/3561493 +// /is-there-a-regexp-escape-function-in-javascript +// $()*+-./?[\]^{|} + + strRegex = strRegex.replace(( + +// Ignore [-/]. + + /[$()*+.?\[\\\]\^{|}]/g + ), "\\$&"); + +// Expand wildcard '**/*'. + + strRegex = strRegex.replace(( + /\\\*\\\*\/(?:\\\*)+/g + ), ".*?"); + +// Expand wildcard '**'. + + strRegex = strRegex.replace(( + /(^|\/)\\\*\\\*(\/|$)/gm + ), "$1.*?$2"); + +// Expand wildcard '*'. + + strRegex = strRegex.replace(( + /(?:\\\*)+/g + ), "[^\\/]*?"); + +// Expand wildcard '?'. + + strRegex = strRegex.replace(( + /\\\?/g + ), "[^\\/]"); + +// Expand directory-with-trailing-slash '.../'. + + strRegex = strRegex.replace(( + /\/$/gm + ), "\\/.*?"); + +// Merge strClass into strRegex. + + ii = 0; + strClass = strClass.split("\u0000"); + strRegex = strRegex.replace(( + /\u0000/g + ), function () { + ii += 1; + if (strClass[ii] === "") { + return ""; + } + return "[" + strClass[ii] + "]"; + }); + +// Change strRegex from string to regexp. + + strRegex = new RegExp("^" + strRegex + "$", "gm"); + return strRegex; + } + +// Validate excludeList, includeList, pathnameList. + + globAssertNotWeird(excludeList, "pattern"); + globAssertNotWeird(includeList, "pattern"); + globAssertNotWeird(pathnameList, "pathname"); + +// Optimization +// Concat pathnames into a single, newline-separated string, +// whose pathnames can all be filtered with a single, regexp-pass. + + pathnameList = pathnameList.join("\n"); + +// 1. Exclude pathnames in that don't match glob-patterns in +// . + + if (includeList.length > 0) { + includeList = includeList.map(globToRegexp); + includeList.forEach(function (pattern) { + pathnameList = pathnameList.replace(pattern, "\u0000$&"); + }); + pathnameList = pathnameList.replace(( + /^[^\u0000].*/gm + ), ""); + pathnameList = pathnameList.replace(( + /^\u0000+/gm + ), ""); + } + +// 2. Exclude pathnames in that match glob-patterns in +// . + + excludeList = excludeList.map(globToRegexp); + excludeList.forEach(function (pattern) { + pathnameList = pathnameList.replace(pattern, ""); + }); + +// Split newline-separated pathnames back to list. + + pathnameList = pathnameList.split("\n").filter(function (elem) { + return elem; + }); + return { + excludeList, + includeList, + pathnameList + }; +} + +function htmlEscape(str) { + +// This function will make html-safe by escaping & < >. + + return String(str).replace(( + /&/g + ), "&").replace(( + //g + ), ">"); +} + +function jslint( + source = "", // A text to analyze. + option_dict = empty(), // An object whose keys correspond to option + // ... names. + global_list = [] // An array of strings containing global + // ... variables that the file is allowed + // ... readonly access. +) { + +// The jslint function itself. + + let catch_list = []; // The array containing all catch-blocks. + let catch_stack = [ // The stack of catch-blocks. + { + context: empty() + } + ]; + let cause_dict = empty(); // The object of test-causes. + let directive_list = []; // The directive comments. + let export_dict = empty(); // The exported names and values. + let function_list = []; // The array containing all functions. + let function_stack = []; // The stack of functions. + let global_dict = empty(); // The object containing the global + // ... declarations. + let import_list = []; // The array collecting all import-from strings. + let line_list = String( // The array containing source lines. + "\n" + source + ).split(jslint_rgx_crlf).map(function (line_source) { + return { + line_source + }; + }); + let mode_stop = false; // true if JSLint cannot finish. + let property_dict = empty(); // The object containing the tallied + // ... property names. + let state = empty(); // jslint state-object to be passed between + // jslint functions. + let syntax_dict = empty(); // The object containing the parser. + let tenure = empty(); // The predefined property registry. + let token_global = { // The global object; the outermost context. + async: 0, + body: true, + context: empty(), + finally: 0, + from: 0, + id: "(global)", + level: 0, + line: jslint_fudge, + live: [], + loop: 0, + switch: 0, + thru: 0, + try: 0 + }; + let token_list = []; // The array of tokens. + let warning_list = []; // The array collecting all generated warnings. + +// Error reportage functions: + + function artifact(the_token) { + +// Return a string representing an artifact. + + the_token = the_token || state.token_nxt; + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); + } + + function is_equal(aa, bb) { + +// test_cause: +// ["0&&0", "is_equal", "", "", 0] + + test_cause(""); + +// Probably deadcode. +// if (aa === bb) { +// return true; +// } + + jslint_assert(!(aa === bb), `Expected !(aa === bb).`); + if (Array.isArray(aa)) { + return ( + Array.isArray(bb) + && aa.length === bb.length + && aa.every(function (value, index) { + +// test_cause: +// ["`${0}`&&`${0}`", "is_equal", "recurse_isArray", "", 0] +// ["`${0}`&&`${1}`", "is_equal", "recurse_isArray", "", 0] + + test_cause("recurse_isArray"); + return is_equal(value, bb[index]); + }) + ); + } + +// Probably deadcode. +// if (Array.isArray(bb)) { +// return false; +// } + + jslint_assert(!Array.isArray(bb), `Expected !Array.isArray(bb).`); + switch (aa.id === bb.id && aa.id) { + case "(number)": + case "(string)": + return aa.value === bb.value; + +// PR-394 - Bugfix +// Fix jslint falsely believing megastring literals `0` and `1` are similar. + + case "`": + if (!is_equal(aa.value, bb.value)) { + return false; + } + break; + } + if (is_weird(aa) || is_weird(bb)) { + +// test_cause: +// ["aa(/./)||{}", "is_equal", "false", "", 0] + + test_cause("false"); + return false; + } + if (aa.arity === bb.arity && aa.id === bb.id) { + if (aa.id === "." || aa.id === "?.") { + +// test_cause: +// ["aa.bb&&aa.bb", "is_equal", "recurse_arity_id", "", 0] +// ["aa?.bb&&aa?.bb", "is_equal", "recurse_arity_id", "", 0] + + test_cause("recurse_arity_id"); + return ( + is_equal(aa.expression, bb.expression) + && is_equal(aa.name, bb.name) + ); + } + if (aa.arity === "unary") { + +// test_cause: +// ["+0&&+0", "is_equal", "recurse_unary", "", 0] + + test_cause("recurse_unary"); + return is_equal(aa.expression, bb.expression); + } + if (aa.arity === "binary") { + +// test_cause: +// ["aa[0]&&aa[0]", "is_equal", "recurse_binary", "", 0] + + test_cause("recurse_binary"); + return ( + aa.id !== "(" + && is_equal(aa.expression[0], bb.expression[0]) + && is_equal(aa.expression[1], bb.expression[1]) + ); + } + if (aa.arity === "ternary") { + +// test_cause: +// ["aa=(``?``:``)&&(``?``:``)", "is_equal", "recurse_ternary", "", 0] + + test_cause("recurse_ternary"); + return ( + is_equal(aa.expression[0], bb.expression[0]) + && is_equal(aa.expression[1], bb.expression[1]) + && is_equal(aa.expression[2], bb.expression[2]) + ); + } + +// Probably deadcode. +// if (aa.arity === "function" || aa.arity === "regexp") { +// return false; +// } + + jslint_assert( + !(aa.arity === "function" || aa.arity === "regexp"), + `Expected !(aa.arity === "function" || aa.arity === "regexp").` + ); + +// test_cause: +// ["undefined&&undefined", "is_equal", "true", "", 0] + + test_cause("true"); + return true; + } + +// test_cause: +// ["null&&undefined", "is_equal", "false", "", 0] + + test_cause("false"); + return false; + } + + function is_weird(thing) { + switch (thing.id) { + case "(regexp)": + return true; + case "=>": + return true; + case "[": + return thing.arity === "unary"; + case "function": + return true; + case "{": + return true; + default: + return false; + } + } + + function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + the_token = the_token || state.token_nxt; + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); + } + + function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); + } + + function test_cause(code, aa, column) { + +// This function will instrument to for test-purposes. + + if (option_dict.test_cause) { + cause_dict[JSON.stringify([ + String(new Error().stack).replace(( + /^ at (?:file|stop|stop_at|test_cause|warn|warn_at)\b.*?\n/gm + ), "").match( + /\n at ((?:Object\.\w+?_)?\w+?) / + )[1].replace(( + /^Object\./ + ), ""), + code, + String( + (aa === undefined || aa === token_global) + ? "" + : aa + ), + column || 0 + ])] = true; + } + } + + function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + let the_warning; + the_token = the_token || state.token_nxt; + the_warning = warn_at( + code, + the_token.line, + (the_token.from || 0) + jslint_fudge, + a || artifact(the_token), + b, + c, + d + ); + +// Issue #408 +// Warnings that should be ignored sometimes suppress legitimate warnings. + + if (the_warning.directive_ignore_line) { + return the_warning; + } + +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token.warning) { + warning_list.pop(); + return the_warning; + } + the_token.warning = the_warning; + return the_warning; + } + + function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + let mm; + let warning = Object.assign(empty(), { + a, + b, + c, + code, + +// Fudge column numbers in warning message. + + column: column || jslint_fudge, + d, + line, + line_source: "", + name: "JSLintError" + }, line_list[line]); + warning.column = Math.max( + Math.min(warning.column, warning.line_source.length), + jslint_fudge + ); + test_cause(code, b || a, warning.column); + switch (code) { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + case "and": + mm = `The '&&' subexpression should be wrapped in parens.`; + break; + case "bad_assignment_a": + mm = `Bad assignment to '${a}'.`; + break; + case "bad_directive_a": + mm = `Bad directive '${a}'.`; + break; + case "bad_get": + mm = `A get function takes no parameters.`; + break; + case "bad_module_name_a": + mm = `Bad module name '${a}'.`; + break; + case "bad_option_a": + mm = `Bad option '${a}'.`; + break; + case "bad_set": + mm = `A set function takes one parameter.`; + break; + case "duplicate_a": + mm = `Duplicate '${a}'.`; + break; + case "empty_block": + mm = `Empty block.`; + break; + case "expected_a": + mm = `Expected '${a}'.`; + break; + case "expected_a_at_b_c": + mm = `Expected '${a}' at column ${b}, not column ${c}.`; + break; + case "expected_a_b": + mm = `Expected '${a}' and instead saw '${b}'.`; + break; + case "expected_a_b_before_c_d": + mm = `Expected ${a} '${b}' to be ordered before ${c} '${d}'.`; + break; + case "expected_a_b_from_c_d": + mm = ( + `Expected '${a}' to match '${b}' from line ${c}` + + ` and instead saw '${d}'.` + ); + break; + case "expected_a_before_b": + mm = `Expected '${a}' before '${b}'.`; + break; + case "expected_digits_after_a": + mm = `Expected digits after '${a}'.`; + break; + case "expected_four_digits": + mm = `Expected four digits after '\\u'.`; + break; + case "expected_identifier_a": + mm = `Expected an identifier and instead saw '${a}'.`; + break; + case "expected_line_break_a_b": + mm = `Expected a line break between '${a}' and '${b}'.`; + break; + case "expected_regexp_factor_a": + mm = `Expected a regexp factor and instead saw '${a}'.`; + break; + case "expected_space_a_b": + mm = `Expected one space between '${a}' and '${b}'.`; + break; + case "expected_statements_a": + mm = `Expected statements before '${a}'.`; + break; + case "expected_string_a": + mm = `Expected a string and instead saw '${a}'.`; + break; + case "expected_type_string_a": + mm = `Expected a type string and instead saw '${a}'.`; + break; + case "freeze_exports": + mm = ( + `Expected 'Object.freeze('. All export values should be frozen.` + ); + break; + +// PR-378 - Relax warning "function_in_loop". +// +// case "function_in_loop": +// mm = `Don't create functions within a loop.`; +// break; + +// PR-390 - Add numeric-separator check. + + case "illegal_num_separator": + mm = `Illegal numeric separator '_' at column ${column}.`; + break; + case "infix_in": + mm = ( + `Unexpected 'in'. Compare with undefined,` + + ` or use the hasOwnProperty method instead.` + ); + break; + case "label_a": + mm = `'${a}' is a statement label.`; + break; + case "misplaced_a": + mm = `Place '${a}' at the outermost level.`; + break; + case "misplaced_directive_a": + mm = `Place the '/*${a}*/' directive before the first statement.`; + break; + case "missing_await_statement": + mm = `Expected await statement in async function.`; + break; + +// PR-347 - Disable warning "missing_browser". +// +// case "missing_browser": +// mm = `/*global*/ requires the Assume a browser option.`; +// break; + + case "missing_m": + mm = `Expected 'm' flag on a multiline regular expression.`; + break; + case "naked_block": + mm = `Naked block.`; + break; + case "nested_comment": + mm = `Nested comment.`; + break; + case "not_label_a": + mm = `'${a}' is not a label.`; + break; + case "number_isNaN": + mm = `Use Number.isNaN function to compare with NaN.`; + break; + case "out_of_scope_a": + mm = `'${a}' is out of scope.`; + break; + case "redefinition_a_b": + mm = `Redefinition of '${a}' from line ${b}.`; + break; + case "redefinition_global_a_b": + mm = `Redefinition of global ${a} variable '${b}'.`; + break; + case "required_a_optional_b": + mm = `Required parameter '${a}' after optional parameter '${b}'.`; + break; + case "reserved_a": + mm = `Reserved name '${a}'.`; + break; + case "subscript_a": + mm = `['${a}'] is better written in dot notation.`; + break; + case "todo_comment": + mm = `Unexpected TODO comment.`; + break; + case "too_long": + mm = `Line is longer than 80 characters.`; + break; + case "too_many_digits": + mm = `Too many digits.`; + break; + case "unclosed_comment": + mm = `Unclosed comment.`; + break; + case "unclosed_disable": + mm = ( + `Directive '/*jslint-disable*/' was not closed` + + ` with '/*jslint-enable*/'.` + ); + break; + case "unclosed_mega": + mm = `Unclosed mega literal.`; + break; + case "unclosed_string": + mm = `Unclosed string.`; + break; + case "undeclared_a": + mm = `Undeclared '${a}'.`; + break; + case "unexpected_a": + mm = `Unexpected '${a}'.`; + break; + case "unexpected_a_after_b": + mm = `Unexpected '${a}' after '${b}'.`; + break; + case "unexpected_a_before_b": + mm = `Unexpected '${a}' before '${b}'.`; + break; + case "unexpected_at_top_level_a": + mm = `Expected '${a}' to be in a function.`; + break; + case "unexpected_char_a": + mm = `Unexpected character '${a}'.`; + break; + case "unexpected_comment": + mm = `Unexpected comment.`; + break; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// case "unexpected_directive_a": +// mm = `When using modules, don't use directive '/\u002a${a}'.`; +// break; + + case "unexpected_expression_a": + mm = `Unexpected expression '${a}' in statement position.`; + break; + case "unexpected_label_a": + mm = `Unexpected label '${a}'.`; + break; + case "unexpected_parens": + mm = `Don't wrap function literals in parens.`; + break; + case "unexpected_space_a_b": + mm = `Unexpected space between '${a}' and '${b}'.`; + break; + case "unexpected_statement_a": + mm = `Unexpected statement '${a}' in expression position.`; + break; + case "unexpected_trailing_space": + mm = `Unexpected trailing space.`; + break; + case "unexpected_typeof_a": + mm = ( + `Unexpected 'typeof'. Use '===' to compare directly with ${a}.` + ); + break; + case "uninitialized_a": + mm = `Uninitialized '${a}'.`; + break; + case "unopened_enable": + mm = ( + `Directive '/*jslint-enable*/' was not opened` + + ` with '/*jslint-disable*/'.` + ); + break; + case "unreachable_a": + mm = `Unreachable '${a}'.`; + break; + case "unregistered_property_a": + mm = `Unregistered property name '${a}'.`; + break; + case "unused_a": + mm = `Unused '${a}'.`; + break; + case "use_double": + mm = `Use double quotes, not single quotes.`; + break; + +// PR-386 - Fix issue #382 - Make fart-related warnings more readable. + + case "use_function_not_fart": + mm = ( + `Use 'function (...)', not '(...) =>' when arrow functions` + + ` become too complex.` + ); + break; + case "use_open": + mm = ( + `Wrap a ternary expression in parens,` + + ` with a line break after the left paren.` + ); + break; + case "use_spaces": + mm = `Use spaces, not tabs.`; + break; + case "var_on_top": + mm = `Move variable declaration to top of function or script.`; + break; + case "var_switch": + mm = `Don't declare variables in a switch.`; + break; + case "weird_condition_a": + mm = `Weird condition '${a}'.`; + break; + case "weird_expression_a": + mm = `Weird expression '${a}'.`; + break; + case "weird_loop": + mm = `Weird loop.`; + break; + case "weird_property_a": + mm = `Weird property name '${a}'.`; + break; + case "weird_relation_a": + mm = `Weird relation '${a}'.`; + break; + case "wrap_condition": + mm = `Wrap the condition in parens.`; + break; + +// PR-386 - Fix issue #382 - Make fart-related warnings more readable. + + case "wrap_fart_parameter": + mm = `Wrap the parameter before '=>' in parens.`; + break; + case "wrap_immediate": + mm = ( + `Wrap an immediate function invocation in parentheses to assist` + + ` the reader in understanding that the expression is the` + + ` result of a function, and not the function itself.` + ); + break; + case "wrap_regexp": + mm = `Wrap this regexp in parens to avoid confusion.`; + break; + case "wrap_unary": + mm = `Wrap the unary expression in parens.`; + break; + } + +// Validate mm. + + jslint_assert(mm, code); + warning.message = mm; + +// PR-242 - Include stack_trace for jslint to debug itself for errors. + + if (option_dict.trace) { + warning.stack_trace = new Error().stack; + } + if (warning.directive_ignore_line) { + +// test_cause: +// ["0 //jslint-ignore-line", "semicolon", "directive_ignore_line", "", 0] + + test_cause("directive_ignore_line"); + return warning; + } + warning_list.push(warning); + return warning; + } + + try { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + + option_dict = Object.assign(empty(), option_dict); + Object.assign(state, { + artifact, + catch_list, + catch_stack, + directive_list, + export_dict, + function_list, + function_stack, + global_dict, + global_list, + import_list, + is_equal, + is_weird, + line_list, + mode_json: false, // true if parsing JSON. + mode_module: false, // true if import or export was used. + mode_property: false, // true if directive /*property*/ is + // ... used. + mode_shebang: false, // true if #! is seen on the first line. + option_dict, + property_dict, + source, + stop, + stop_at, + syntax_dict, + tenure, + test_cause, + token_global, + token_list, + token_nxt: token_global, + warn, + warn_at, + warning_list + }); + +// PHASE 1. Split by newlines into . + + jslint_phase1_split(state); + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 2. Lex into . + + jslint_phase2_lex(state); + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 3. Parse into using the Pratt-parser. + + jslint_phase3_parse(state); + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). + + if (!state.mode_json) { + jslint_phase4_walk(state); + } + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 5. Check whitespace between tokens in . + + if (!state.mode_json && warning_list.length === 0) { + jslint_phase5_whitage(state); + } + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PR-347 - Disable warning "missing_browser". +// +// if (!option_dict.browser) { +// directive_list.forEach(function (comment) { +// if (comment.directive === "global") { +// +// // test_cause: +// // ["/*global aa*/", "jslint", "missing_browser", "(comment)", 1] +// +// warn("missing_browser", comment); +// } +// }); +// } + + if (option_dict.test_internal_error) { + jslint_assert(undefined, "test_internal_error"); + } + } catch (err) { + mode_stop = true; + err.message = "[JSLint was unable to finish] " + err.message; + err.mode_stop = true; + if (err.name !== "JSLintError") { + Object.assign(err, { + column: jslint_fudge, + line: jslint_fudge, + line_source: "", + stack_trace: err.stack + }); + } + if (warning_list.indexOf(err) === -1) { + warning_list.push(err); + } + } + +// Sort warning_list by mode_stop first, line, column respectively. + + warning_list.sort(function (aa, bb) { + return ( + Boolean(bb.mode_stop) - Boolean(aa.mode_stop) + || aa.line - bb.line + || aa.column - bb.column + ); + +// Update each warning with formatted_message ready-for-use by jslint_cli. + + }).map(function ({ + column, + line, + line_source, + message, + stack_trace = "" + }, ii, list) { + list[ii].formatted_message = String( + String(ii + 1).padStart(2, " ") + + ". \u001b[31m" + message + "\u001b[39m" + + " \u001b[90m\/\/ line " + line + ", column " + column + + "\u001b[39m\n" + + (" " + line_source.trim()).slice(0, 72) + "\n" + + stack_trace + ).trimRight(); + }); + + return { + causes: cause_dict, + directives: directive_list, + edition: jslint_edition, + exports: export_dict, + froms: import_list, + functions: function_list, + global: token_global, + id: "(JSLint)", + json: state.mode_json, + lines: line_list, + module: state.mode_module === true, + ok: warning_list.length === 0 && !mode_stop, + option: option_dict, + property: property_dict, + shebang: ( + state.mode_shebang + ? line_list[jslint_fudge].line_source + : undefined + ), + stop: mode_stop, + tokens: token_list, + tree: state.token_tree, + warnings: warning_list + }; +} + +// PR-362 - Add API Doc. + +async function jslint_apidoc({ + example_list, + github_repo, + module_list, + package_name, + pathname, + version +}) { + +// This function will create API Doc from . + + let elem_ii = 0; + let html; + + function elem_create(moduleObj, key, moduleName) { + +// This function will create a sub API Doc from elem []. + + let example = "N/A"; + let id = encodeURIComponent("apidoc.elem." + moduleName + "." + key); + let name; + let signature; + let source; + name = htmlEscape((typeof moduleObj[key]) + " " + key); + if (typeof moduleObj[key] !== "function") { + return { + name, + signature: (` + +${name} + + `), + source: (` +
  • +

    + + ${name} + +

    +
  • + `) + }; + } + // init source + source = htmlEscape(trim_start(moduleObj[key].toString())); + // init signature + source = source.replace(( + /(\([\S\s]*?\)) \{/ + ), function (match0, match1) { + signature = htmlEscape( + match1.replace(( + / *?\/\*[\S\s]*?\*\/ */g + ), "").replace(( + / *?\/\/.*/g + ), "").replace(( + /\n{2,}/g + ), "\n") + ); + return match0; + }); + // init comment + source = source.replace(( + /\n(?:\/\/.*?\n)+\n/ + ), "$&"); + // init example + example_list.some(function (example2) { + example2.replace( + new RegExp(( + "((?:\\n.*?){8}(function )?)\\b" + + key + + "(\\((?:.*?\\n){8})" + ), "g"), + function (ignore, header, isDeclaration, footer) { + if (!isDeclaration) { + example = "..." + trim_start( + htmlEscape(header) + + "" + + htmlEscape(key) + + "" + + htmlEscape(footer) + ).trimEnd() + "\n..."; + } + return ""; + } + ); + return example !== "N/A"; + }); + if (source.length > 2048) { + source = source.slice(0, 2048) + "...\n}\n"; + } + return { + name, + signature: (` + +${name}${signature} + + `), + source: (` +
  • +

    + + ${name}${signature} + +

    +
  • +
  • Description and source-code:
    ${source}
  • +
  • Example usage:
    ${example}
  • + `) + }; + } + + function trim_start(str) { + +// This function will normalize whitespace before . + + let whitespace = ""; + str.trim().replace(( + /^ */gm + ), function (match0) { + if (whitespace === "" || match0.length < whitespace.length) { + whitespace = match0; + } + return ""; + }); + str = str.replace(new RegExp("^" + whitespace, "gm"), ""); + return str; + } + await moduleFsInit(); + +// Html-escape params. + + github_repo = htmlEscape(github_repo); + package_name = htmlEscape(package_name); + version = htmlEscape(version); + +// Init example_list. + + example_list = await Promise.all(example_list.map(async function (file) { + +// This function will read example from given file. + + let result = await moduleFs.promises.readFile(file, "utf8"); + result = ( + "\n\n\n\n\n\n\n\n" + // bug-workaround - truncate example to manageable size + + result.slice(0, 524288) + + "\n\n\n\n\n\n\n\n" + ); + result = result.replace(( + /\r\n*/g + ), "\n"); + return result; + })); + +// Init module_list. + + module_list = await Promise.all(module_list.map(async function ({ + pathname + }) { + let moduleName = htmlEscape(JSON.stringify(pathname)); + let moduleObj = await import(pathname); + if (moduleObj.default) { + moduleObj = moduleObj.default; + } + return { + elem_list: Object.keys(moduleObj).map(function (key) { + return elem_create(moduleObj, key, moduleName); + }).sort(function (aa, bb) { + return ( + aa.name < bb.name + ? -1 + : 1 + ); + }).map(function (elem) { + elem_ii += 1; + elem.signature = elem.signature.replace( + ">", + ">" + elem_ii + ". " + ); + elem.source = elem.source.replace( + "\">", + "\">" + elem_ii + ". " + ); + return elem; + }), + id: encodeURIComponent("apidoc.module." + moduleName), + moduleName + }; + })); + html = (` + + + + + + +${package_name} apidoc + + + +
    +

    API Doc for ${package_name} (${version})

    +
    + +

    Table of Contents

    +
    +
      + `) + module_list.map(function ({ + elem_list, + id, + moduleName + }) { + return ( + (` +
    • + Module ${moduleName} +
        + `) + + elem_list.map(function ({ + signature + }) { + return "
      • \n" + signature + "\n
      • \n"; + }).join("") + + (` +
      +
    • + `) + ); + }).join("") + (` +
    +
    + `) + module_list.map(function ({ + elem_list, + id, + moduleName + }) { + return ( + (` +
    +

    Module ${moduleName}

    +
      + `) + + elem_list.map(function ({ + source + }) { + return source; + }).join("") + + (` +
    +
    + `) + ); + }).join("") + (` +
    + [ + This document was created with + JSLint + ] +
    +
    + + + `); + html = html.trim().replace(( + / +?$/gm + ), "") + "\n"; + await fsWriteFileWithParents(pathname, html); +} + +function jslint_assert(condition, message) { + +// This function will throw if is falsy. + + if (condition) { + return condition; + } + throw new Error( + `This was caused by a bug in JSLint. +Please open an issue with this stack-trace (and possible example-code) at +https://github.com/jslint-org/jslint/issues. +edition = "${jslint_edition}"; +${String(message).slice(0, 2000)}` + ); +} + +async function jslint_cli({ + console_error, + console_log, + file, + import_meta_url, + mode_cli, + mode_noop, + option, + process_argv, + process_env, + process_exit, + source +}) { + +// This function will run jslint from nodejs-cli. + + let command; + let data; + let exit_code = 0; + let mode_report; + let mode_wrapper_vim; + let result; + + function jslint_from_file({ + code, + file, + line_offset = 0, + mode_conditional, + option = empty() + }) { + let result_from_file; + if ( + mode_conditional + && !( + /^\/\*jslint\b/m + ).test(code.slice(0, 65536)) + ) { + return; + } + option = Object.assign(empty(), option, { + file + }); + switch (( + /\.\w+?$|$/m + ).exec(file)[0]) { + case ".html": + +// Recursively jslint embedded "". + + code.replace(( + /^]*?>\n([\S\s]*?\n)<\/script>$/gm + ), function (ignore, match1, ii) { + jslint_from_file({ + code: match1, + file: file + ". + + import_meta_url = import_meta_url || jslint_import_meta_url; + if ( + jslint_rgx_url_search_window_jslint.test(import_meta_url) + && (typeof globalThis === "object" && globalThis) + ) { + globalThis.jslint = jslint; + } + +// Feature-detect nodejs. + + if (!( + (typeof process === "object" && process) + && process.versions + && typeof process.versions.node === "string" + && !mode_noop + )) { + return exit_code; + } + console_error = console_error || console.error; + console_log = console_log || console.log; + process_argv = process_argv || process.argv; + process_env = process_env || process.env; + process_exit = process_exit || process.exit; + await moduleFsInit(); + if ( + !( + +// Feature-detect nodejs-cli. + + process.execArgv.indexOf("--eval") === -1 + && process.execArgv.indexOf("-e") === -1 + && ( + ( + /[\/|\\]jslint(?:\.[cm]?js)?$/m + ).test(process_argv[1]) + || mode_cli + ) + && ( + moduleUrl.fileURLToPath(import_meta_url) + === + modulePath.resolve(process_argv[1]) + ) + ) + && !mode_cli + ) { + return exit_code; + } + +// init commmand + + command = String(process_argv[2]).split("="); + command[1] = command.slice(1).join("="); + + switch (command[0]) { + +// PR-362 - Add API Doc. + + case "jslint_apidoc": + await jslint_apidoc(Object.assign(JSON.parse(process_argv[3]), { + pathname: command[1] + })); + return; + +// PR-363 - Add command jslint_report. + + case "jslint_report": + mode_report = command[1]; + process_argv = process_argv.slice(1); + break; + +// COMMIT-b26d6df2 - Add command jslint_wrapper_vim. + + case "jslint_wrapper_vim": + mode_wrapper_vim = true; + process_argv = process_argv.slice(1); + break; + +// PR-364 - Add command v8_coverage_report. + + case "v8_coverage_report": + await v8CoverageReportCreate({ + consoleError: console_error, + coverageDir: command[1], + processArgv: process_argv.slice(3) + }); + return; + } + +// Normalize file relative to process.cwd(). + + process_argv.slice(2).some(function (arg) { + if (!arg.startsWith("-")) { + file = file || arg; + return true; + } + }); + if (!file) { + return; + } + file = modulePath.resolve(file) + "/"; + if (file.startsWith(process.cwd() + "/")) { + file = file.replace(process.cwd() + "/", "").slice(0, -1) || "."; + } + file = file.replace(( + /\\/g + ), "/").replace(( + /\/$/g + ), ""); + if (source) { + data = source; + } else { + +// jslint_cli - jslint directory. + + try { + data = await moduleFs.promises.readdir(file, "utf8"); + } catch (ignore) {} + if (data) { + await Promise.all(data.map(async function (file2) { + let code; + let time_start = Date.now(); + file2 = file + "/" + file2; + switch (( + /\.\w+?$|$/m + ).exec(file2)[0]) { + case ".cjs": + case ".html": + case ".js": + case ".json": + case ".md": + case ".mjs": + case ".sh": + break; + default: + return; + } + try { + code = await moduleFs.promises.readFile(file2, "utf8"); + } catch (ignore) { + return; + } + if ( + ( + /(?:\b|_)(?:lock|min|raw|rollup)(?:\b|_)/ + ).test(file2) + || !(code && code.length < 1048576) + ) { + return; + } + jslint_from_file({ + code, + file: file2, + option + }); + console_error( + "jslint - " + (Date.now() - time_start) + "ms - " + file2 + ); + })); + process_exit(exit_code); + return exit_code; + } + +// jslint_cli - jslint file. + + try { + data = await moduleFs.promises.readFile(file, "utf8"); + } catch (err) { + console_error(err); + exit_code = 1; + process_exit(exit_code); + return exit_code; + } + } + result = jslint_from_file({ + code: data, + file, + option + }); + if (mode_report) { + result = jslint.jslint_report(result); + result = `\n${result}\n`; + await fsWriteFileWithParents(mode_report, result); + } + process_exit(exit_code); + return exit_code; +} + +function jslint_phase1_split() { + +// PHASE 1. Split by newlines into . + + return; +} + +function jslint_phase2_lex(state) { + +// PHASE 2. Lex into . + + let { + artifact, + directive_list, + global_dict, + global_list, + line_list, + option_dict, + stop, + stop_at, + tenure, + test_cause, + token_global, + token_list, + warn, + warn_at + } = state; + let char; // The current character being lexed. + let column = 0; // The column number of the next character. + let from; // The starting column number of the token. + let from_mega; // The starting column of megastring. + let line = 0; // The line number of the next character. + let line_disable; // The starting line of "/*jslint-disable*/". + let line_mega; // The starting line of megastring. + let line_source = ""; // The remaining line source string. + let line_whole = ""; // The whole line source string. + let mode_digits_empty_string = 1; + let mode_digits_numeric_separator = 2; + let mode_directive = true; // true if directives are still allowed. + let mode_mega = false; // true if currently parsing a megastring + // ... literal. + let mode_regexp; // true if regular expression literal seen on + // ... this line. + let paren_backtrack_list = []; // List of most recent "(" tokens at any + // ... paren-depth. + let paren_depth = 0; // Keeps track of current paren-depth. + let snippet = ""; // A piece of string. + let token_1; // The first token. + let token_prv = token_global; // The previous token including + // ... comments. + let token_prv_expr = token_global; // The previous token excluding + // ... comments. + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions char_after, char_before, +// read_digits, and char_after_escape help in the parsing of literals. + + function char_after(match) { + +// Get the next character from the source line. Remove it from the line_source, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + +// test_cause: +// ["aa=/[", "char_after", "expected_a", "]", 5] +// ["aa=/aa{/", "char_after", "expected_a_b", "/", 8] + + return ( + char === "" + ? stop_at("expected_a", line, column - 1, match) + : stop_at("expected_a_b", line, column, match, char) + ); + } + char = line_source.slice(0, 1); + line_source = line_source.slice(1); + snippet += char || " "; + column += 1; + return char; + } + + function char_after_escape(extra) { + +// Validate char after escape "\\". + + char_after("\\"); + switch (char) { + case "": + +// test_cause: +// ["\"\\", "char_after_escape", "unclosed_string", "", 2] + + return stop_at("unclosed_string", line, column); + case "/": + return char_after(); + case "\\": + return char_after(); + case "`": + return char_after(); + case "b": + return char_after(); + case "f": + return char_after(); + case "n": + return char_after(); + case "r": + return char_after(); + case "t": + +// test_cause: +// ["\"\\/\\\\\\`\\b\\f\\n\\r\\t\"", "char_after_escape", "char_after", "", 0] + + test_cause("char_after"); + return char_after(); + case "u": + if (char_after("u") === "{") { + if (state.mode_json) { + +// test_cause: +// ["[\"\\u{12345}\"]", "char_after_escape", "unexpected_a", "{", 5] + + warn_at("unexpected_a", line, column, char); + } + if (read_digits("x", undefined) > 5) { + +// test_cause: +// ["\"\\u{123456}\"", "char_after_escape", "too_many_digits", "", 11] + + warn_at("too_many_digits", line, column); + } + if (char !== "}") { + +// test_cause: +// ["\"\\u{12345\"", "char_after_escape", "expected_a_before_b", "\"", 10] + + stop_at("expected_a_before_b", line, column, "}", char); + } + return char_after(); + } + char_before(); + if (read_digits("x", mode_digits_empty_string) < 4) { + +// test_cause: +// ["\"\\u0\"", "char_after_escape", "expected_four_digits", "", 5] + + warn_at("expected_four_digits", line, column); + } + return; + default: + if (extra && extra.indexOf(char) >= 0) { + return char_after(); + } + +// test_cause: +// ["\"\\0\"", "char_after_escape", "unexpected_a_before_b", "0", 3] + + warn_at("unexpected_a_before_b", line, column, "\\", char); + } + } + + function char_before() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the line_source. + + char = snippet.slice(-1); + line_source = char + line_source; + column -= char.length; + +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + return char; + } + + function check_numeric_separator(digits, column) { + +// This function will check for illegal numeric-separator in . + + digits.replace(( + jslint_rgx_numeric_separator_illegal + ), function (ignore, ii) { + +// test_cause: +// ["0x0_0_;", "check_numeric_separator", "illegal_num_separator", "", 6] +// ["0x0_0__0;", "check_numeric_separator", "illegal_num_separator", "", 6] +// ["aa=1_2_;", "check_numeric_separator", "illegal_num_separator", "", 7] +// ["aa=1_2__3;", "check_numeric_separator", "illegal_num_separator", "", 7] +// ["aa=1_2_n;", "check_numeric_separator", "illegal_num_separator", "", 7] + + warn_at("illegal_num_separator", line, column + ii + 1); + return ""; + }); + } + + function lex_comment() { + let body; + let ii = 0; + let jj = 0; + let the_comment; + +// Create a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + +// Create token from comment //.... + + if (snippet === "//") { + snippet = line_source; + line_source = ""; + the_comment = token_create("(comment)", snippet); + if (mode_mega) { + +// test_cause: +// ["`${//}`", "lex_comment", "unexpected_comment", "`", 4] + + warn("unexpected_comment", the_comment, "`"); + } + +// Create token from comment /*...*/. + + } else { + snippet = []; + if (line_source[0] === "/") { + +// test_cause: +// ["/*/", "lex_comment", "unexpected_a", "/", 2] + + warn_at("unexpected_a", line, column + ii, "/"); + } + +// Lex/loop through each line until "*/". + + while (true) { + // jslint_rgx_star_slash + ii = line_source.indexOf("*/"); + if (ii >= 0) { + break; + } + // jslint_rgx_slash_star + ii = line_source.indexOf("/*"); + if (ii >= 0) { + +// test_cause: +// ["/*/*", "lex_comment", "nested_comment", "", 2] + + warn_at("nested_comment", line, column + ii); + } + snippet.push(line_source); + line_source = read_line(); + if (line_source === undefined) { + +// test_cause: +// ["/*", "lex_comment", "unclosed_comment", "", 1] + + return stop_at("unclosed_comment", line, column); + } + } + jj = line_source.slice(0, ii).search( + jslint_rgx_slash_star_or_slash + ); + if (jj >= 0) { + +// test_cause: +// ["/*/**/", "lex_comment", "nested_comment", "", 2] + + warn_at("nested_comment", line, column + jj); + } + snippet.push(line_source.slice(0, ii)); + snippet = snippet.join(" "); + column += ii + 2; + line_source = line_source.slice(ii + 2); + the_comment = token_create("(comment)", snippet); + } + +// Uncompleted work comment. + + if (!option_dict.devel && jslint_rgx_todo.test(snippet)) { + +// test_cause: +// ["//todo", "lex_comment", "todo_comment", "(comment)", 1] //jslint-ignore-line + + warn("todo_comment", the_comment); + } + +// Lex directives in comment. + + [ + the_comment.directive, body + ] = Array.from(snippet.match(jslint_rgx_directive) || []).slice(1); + if (the_comment.directive === undefined) { + return the_comment; + } + directive_list.push(the_comment); + if (!mode_directive) { + +// test_cause: +// ["0\n/*global aa*/", "lex_comment", "misplaced_directive_a", "global", 1] + + warn_at("misplaced_directive_a", line, from, the_comment.directive); + return the_comment; + } + +// lex_directive(); +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + +// Lex/loop through each directive in /*...*/ + + ii = 0; + body.replace(jslint_rgx_directive_part, function ( + match0, + key, + val, + jj + ) { + if (ii !== jj) { + +// test_cause: +// ["/*jslint !*/", "lex_comment", "bad_directive_a", "!", 1] + + return stop("bad_directive_a", the_comment, body.slice(ii)); + } + if (match0 === "") { + return ""; + } + ii += match0.length; + switch (the_comment.directive) { + case "global": + if (val) { + +// test_cause: +// ["/*global aa:false*/", "lex_comment", "bad_option_a", "aa:false", 1] + + warn("bad_option_a", the_comment, key + ":" + val); + } + global_dict[key] = "user-defined"; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// state.mode_module = the_comment; + + break; + case "jslint": + if (!option_set_item(key, val !== "false")) { + +// test_cause: +// ["/*jslint undefined*/", "lex_comment", "bad_option_a", "undefined", 1] + + warn("bad_option_a", the_comment, key); + } + break; + case "property": + state.mode_property = true; + tenure[key] = true; + break; + } + return ""; + }); + return the_comment; + } + + function lex_megastring() { + let id; + let match; + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (mode_mega) { + +// test_cause: +// ["`${`", "lex_megastring", "expected_a_b", "`", 4] + + return stop_at("expected_a_b", line, column, "}", "`"); + } + from_mega = from; + line_mega = line; + mode_mega = true; + snippet = ""; + +// Parsing a mega literal is tricky. First create a ` token. + + token_create("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + while (true) { + match = line_source.match(jslint_rgx_mega) || { + "0": "", + index: 0 + }; + snippet += line_source.slice(0, match.index); + column += match.index; + line_source = line_source.slice(match.index); + match = match[0]; + switch (match) { + case "${": + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + token_create("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then create tokens that will become part of an expression until +// a } token is made. + + column += 2; + token_create("${"); + line_source = line_source.slice(2); + +// Lex/loop through each token inside megastring-expression `${...}`. + + while (true) { + id = lex_token().id; + if (id === "{") { + +// test_cause: +// ["`${{", "lex_megastring", "expected_a_b", "{", 4] + + return stop_at("expected_a_b", line, column, "}", "{"); + } + if (id === "}") { + break; + } + } + break; + case "\\": + snippet += line_source.slice(0, 2); + line_source = line_source.slice(2); + column += 2; + break; + case "`": + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + token_create("(string)", snippet).quote = "`"; + snippet = ""; + +// Terminate megastring with `. + + line_source = line_source.slice(1); + column += 1; + mode_mega = false; + return token_create("`"); + default: + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + snippet += line_source + "\n"; + if (read_line() === undefined) { + +// test_cause: +// ["`", "lex_megastring", "unclosed_mega", "", 1] + + return stop_at("unclosed_mega", line_mega, from_mega); + } + } + } + } + + function lex_number() { + let prefix = snippet; + +// PR-390 - Add numeric-separator check. + + check_numeric_separator(prefix, column - prefix.length); + char_after(); + switch (prefix === "0" && char) { + case "b": + case "o": + case "x": + read_digits(char, mode_digits_numeric_separator); + +// PR-351 - Ignore BigInt suffix 'n'. + + if (char === "n") { + char_after("n"); + } + break; + default: + if (char === ".") { + read_digits("d", mode_digits_numeric_separator); + } + if (char === "E" || char === "e") { + char_after(char); + if (char !== "+" && char !== "-") { + char_before(); + } + read_digits("d", mode_digits_numeric_separator); + } + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + +// test_cause: +// ["0a", "lex_number", "unexpected_a_after_b", "0", 2] + + return stop_at( + "unexpected_a_after_b", + line, + column, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + char_before(); + return token_create("(number)", snippet); + } + + function lex_regexp() { + +// Regexp +// Lex a regular expression literal. + + let flag; + let mode_regexp_multiline; + let result; + let value; + mode_regexp = true; + + function lex_regexp_bracketed() { + let mode_regexp_range; + +// RegExp +// Match a class. + + char_after("["); + if (char === "^") { + char_after("^"); + } + while (true) { + +// RegExp +// Match a character in a character class. + + switch (char) { + case "": + case "]": + +// test_cause: +// ["aa=/[", "lex_regexp_bracketed", "closer", "", 0] +// ["aa=/[]/", "lex_regexp_bracketed", "closer", "", 0] + + test_cause("closer"); + if (mode_regexp_range) { + +// test_cause: +// ["aa=/[0-]/", "lex_regexp_bracketed", "unexpected_a", "-", 7] + + warn_at("unexpected_a", line, column - 1, "-"); + } + return char_after("]"); + +// PR-362 - Relax regexp-warning against using . +// +// case " ": +// +// // test_cause: +// // ["aa=/[ ]/", "lex_regexp_bracketed", "expected_a_b", " ", 6] +// +// warn_at("expected_a_b", line, column, "\\u0020", " "); +// break; + + case "-": + case "/": + case "[": + case "^": + +// test_cause: +// ["aa=/[-]/", "lex_regexp_bracketed", "expected_a_before_b", "-", 6] +// ["aa=/[.^]/", "lex_regexp_bracketed", "expected_a_before_b", "^", 7] +// ["aa=/[/", "lex_regexp_bracketed", "expected_a_before_b", "/", 6] +// ["aa=/[\\\\/]/", "lex_regexp_bracketed", "expected_a_before_b", "/", 8] +// ["aa=/[\\\\[]/", "lex_regexp_bracketed", "expected_a_before_b", "[", 8] + + warn_at("expected_a_before_b", line, column, "\\", char); + break; + case "\\": + char_after_escape("BbDdSsWw-[]^"); + char_before(); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${/[`]/}`", "lex_regexp_bracketed", "unexpected_a", "`", 6] + + warn_at("unexpected_a", line, column, "`"); + } + break; + } + char_after(); + mode_regexp_range = false; + if (char === "-") { + +// RegExp +// Match a range of subclasses. + + mode_regexp_range = true; + char_after("-"); + } + } + } + + function lex_regexp_group() { + +// RegExp +// Lex sequence of characters in regexp. + + switch (char) { + case "": + warn_at("expected_regexp_factor_a", line, column, char); + break; + case ")": + warn_at("expected_regexp_factor_a", line, column, char); + break; + case "]": + +// test_cause: +// ["/ /", "lex_regexp_group", "expected_regexp_factor_a", "", 3] +// ["aa=/)", "lex_regexp_group", "expected_regexp_factor_a", ")", 5] +// ["aa=/]", "lex_regexp_group", "expected_regexp_factor_a", "]", 5] + + warn_at("expected_regexp_factor_a", line, column, char); + break; + } + while (true) { + switch (char) { + case "": + case ")": + case "/": + case "]": + return; + +// PR-362 - Relax regexp-warning against using . +// +// case " ": +// +// // test_cause: +// // ["aa=/ /", "lex_regexp_group", "expected_a_b", " ", 5] +// +// warn_at("expected_a_b", line, column, "\\s", " "); +// char_after(); +// break; + + case "$": + if (line_source[0] !== "/") { + mode_regexp_multiline = true; + } + char_after(); + break; + case "(": + +// RegExp +// Match a group that starts with left paren. + + char_after("("); + switch (char) { + case ":": + +// test_cause: +// ["aa=/(:)/", "lex_regexp_group", "expected_a_before_b", ":", 6] +// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5] + + warn_at("expected_a_before_b", line, column, "?", ":"); + break; + case "?": + char_after("?"); + switch (char) { + case "!": + +// PR-437 - Add grammar for regexp-named-capture-group. + + case "<": + case "=": + char_after(); + break; + default: + char_after(":"); + } + break; + } + +// RegExp +// Recurse lex_regexp_group(). + + lex_regexp_group(); + char_after(")"); + break; + case "*": + case "+": + case "?": + case "{": + case "}": + +// test_cause: +// ["aa=/+/", "lex_regexp_group", "expected_a_before_b", "+", 5] +// ["aa=/.**/", "lex_regexp_group", "expected_a_before_b", "*", 7] +// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5] +// ["aa=/{/", "lex_regexp_group", "expected_a_before_b", "{", 5] +// ["aa=/}/", "lex_regexp_group", "expected_a_before_b", "}", 5] + + warn_at("expected_a_before_b", line, column, "\\", char); + char_after(); + break; + case "[": + lex_regexp_bracketed(); + break; + case "\\": + +// test_cause: +// ["aa=/\\/", "lex_regexp_group", "escape", "", 0] + + test_cause("escape"); + +// PR-437 - Add grammar for regexp-named-backreference. + + char_after_escape("BbDdSsWw^${}[]():=!.|*+?k"); + break; + case "^": + if (snippet !== "^") { + mode_regexp_multiline = true; + } + char_after(); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${/`/}`", "lex_regexp_group", "unexpected_a", "`", 5] + + warn_at("unexpected_a", line, column, "`"); + } + char_after(); + break; + default: + char_after(); + } + +// RegExp +// Match an optional quantifier. + + switch (char) { + case "*": + case "+": + if (char_after(char) === "?") { + +// test_cause: +// ["aa=/.*?/", "lex_regexp_group", "?", "", 0] +// ["aa=/.+?/", "lex_regexp_group", "?", "", 0] + + test_cause("?"); + char_after("?"); + } + break; + case "?": + if (char_after("?") === "?") { + +// test_cause: +// ["aa=/.??/", "lex_regexp_group", "unexpected_a", "?", 7] + + warn_at("unexpected_a", line, column, char); + char_after("?"); + } + break; + case "{": + if (read_digits("d", mode_digits_empty_string) === 0) { + +// test_cause: +// ["aa=/aa{/", "lex_regexp_group", "expected_a_before_b", ",", 8] + + warn_at("expected_a_before_b", line, column, "0", ","); + } + if (char === ",") { + +// test_cause: +// ["aa=/.{,/", "lex_regexp_group", "comma", "", 0] + + test_cause("comma"); + read_digits("d", mode_digits_empty_string); + } + if (char_after("}") === "?") { + +// test_cause: +// ["aa=/.{0}?/", "lex_regexp_group", "unexpected_a", "?", 9] + + warn_at("unexpected_a", line, column, char); + char_after("?"); + } + break; + } + } + } + +// RegExp +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + char_after(); + if (char === "=") { + +// test_cause: +// ["aa=/=/", "lex_regexp", "expected_a_before_b", "=", 5] + + warn_at("expected_a_before_b", line, column, "\\", "="); + } + lex_regexp_group(); + +// RegExp +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + +// RegExp +// Make sure there is a closing slash. + + value = snippet; + char_after("/"); + +// RegExp +// Create flag. + + flag = empty(); + while ( + +// Regexp +// char is a letter. + + (char >= "a" && char <= "z\uffff") + || (char >= "A" && char <= "Z\uffff") + ) { + +// RegExp +// Process dangling flag letters. + + switch (!flag[char] && char) { + case "g": + break; + case "i": + break; + case "m": + break; + case "u": + break; + case "y": + +// test_cause: +// ["aa=/./gimuy", "lex_regexp", "flag", "", 0] + + test_cause("flag"); + break; + default: + +// test_cause: +// ["aa=/./gg", "lex_regexp", "unexpected_a", "g", 8] +// ["aa=/./z", "lex_regexp", "unexpected_a", "z", 7] + + warn_at("unexpected_a", line, column, char); + } + flag[char] = true; + char_after(); + } + char_before(); + if (char === "/" || char === "*") { + +// test_cause: +// ["aa=/.//", "lex_regexp", "unexpected_a", "/", 3] + + return stop_at("unexpected_a", line, from, char); + } + result = token_create("(regexp)", char); + result.flag = flag; + result.value = value; + if (mode_regexp_multiline && !flag.m) { + +// test_cause: +// ["aa=/$^/", "lex_regexp", "missing_m", "", 7] + + warn_at("missing_m", line, column); + } + return result; + } + + function lex_slash_or_regexp() { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + let the_token; + switch ( + token_prv_expr.identifier + && !token_prv_expr.dot + && token_prv_expr.id + ) { + case "case": + case "delete": + case "in": + case "instanceof": + case "new": + case "typeof": + case "void": + case "yield": + the_token = lex_regexp(); + +// test_cause: +// ["case /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6] +// ["delete /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8] +// ["in /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 4] +// ["instanceof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 12] +// ["new /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 5] +// ["typeof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8] +// ["void /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6] +// ["yield /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 7] + + return stop("unexpected_a", the_token); + case "return": + return lex_regexp(); + } + switch (!token_prv_expr.identifier && token_prv_expr.id.slice(-1)) { + case "!": + case "%": + case "&": + case "*": + case "+": + case "-": + case "/": + case ";": + case "<": + case ">": + case "^": + case "{": + case "|": + case "}": + case "~": + the_token = lex_regexp(); + +// test_cause: +// ["!/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["%/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["&/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["+/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["-/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["0 * /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5] +// ["0 / /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5] +// [";/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["^/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["{/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["|/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["}/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["~/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] + + warn("wrap_regexp", the_token); + return the_token; + case "(": + case ",": + case ":": + case "=": + case "?": + case "[": + +// test_cause: +// ["(/./", "lex_slash_or_regexp", "recurse", "", 0] +// [",/./", "lex_slash_or_regexp", "recurse", "", 0] +// [":/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["=/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["?/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["aa[/./", "lex_slash_or_regexp", "recurse", "", 0] + + test_cause("recurse"); + return lex_regexp(); + } + if (line_source[0] === "=") { + column += 1; + line_source = line_source.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + return token_create(snippet); + } + + function lex_string(quote) { + +// Create a string token. + + let the_token; + if (!option_dict.single && quote === "'") { + +// test_cause: +// ["''", "lex_string", "use_double", "", 1] + + warn_at("use_double", line, column); + } + snippet = ""; + char_after(); + +// Lex/loop through each character in "...". + + while (true) { + switch (char) { + case "": + +// test_cause: +// ["\"", "lex_string", "unclosed_string", "", 1] + + return stop_at("unclosed_string", line, column); + case "\\": + char_after_escape(quote); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${\"`\"}`", "lex_string", "unexpected_a", "`", 5] + + warn_at("unexpected_a", line, column, "`"); + } + char_after("`"); + break; + case quote: + +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + the_token = token_create("(string)", snippet); + the_token.quote = quote; + return the_token; + default: + char_after(); + } + } + } + + function lex_token() { + let match; + +// Lex/loop through each whitespace. + + while (true) { + +// Lex/loop through each blank-line. + + while (!line_source) { + line_source = read_line(); + from = 0; + if (line_source === undefined) { + return ( + mode_mega + +// test_cause: +// ["`${//}`", "lex_token", "unclosed_mega", "", 1] + + ? stop_at("unclosed_mega", line_mega, from_mega) + : line_disable !== undefined + +// test_cause: +// ["/*jslint-disable*/", "lex_token", "unclosed_disable", "", 1] + + ? stop_at("unclosed_disable", line_disable) + : token_create("(end)") + ); + } + } + from = column; + match = line_source.match(jslint_rgx_token); + +// match[1] token +// match[2] whitespace +// match[3] identifier +// match[4] number +// match[5] rest + + if (!match) { + +// test_cause: +// ["#", "lex_token", "unexpected_char_a", "#", 1] + + return stop_at( + "unexpected_char_a", + line, + column, + line_source[0] + ); + } + snippet = match[1]; + column += snippet.length; + line_source = match[5]; + if (!match[2]) { + break; + } + } + +// The token is an identifier. + + if (match[3]) { + return token_create(snippet, undefined, true); + } + +// Create token from number. + + if (match[4]) { + return lex_number(); + } + +// Create token from string "..." or '...'. + + if (snippet === "\"" || snippet === "'") { + return lex_string(snippet); + } + +// Create token from megastring `...`. + + if (snippet === "`") { + return lex_megastring(); + } + +// Create token from comment /*...*/ or //.... + + if (snippet === "/*" || snippet === "//") { + return lex_comment(); + } + +// Create token from slash /. + + if (snippet === "/") { + return lex_slash_or_regexp(); + } + return token_create(snippet); + } + + function option_set_item(key, val) { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + switch (key) { + case "beta": // Enable experimental warnings. + case "bitwise": // Allow bitwise operator. + case "browser": // Assume browser environment. + case "convert": // Allow conversion operator. + case "couch": // Assume CouchDb environment. + case "devel": // Allow console.log() and friends. + case "ecma": // Assume ECMAScript environment. + case "eval": // Allow eval(). + case "fart": // Allow complex fat-arrow. + case "for": // Allow for-statement. + case "getset": // Allow get() and set(). + case "indent2": // Use 2-space indent. + case "long": // Allow long lines. + case "node": // Assume Node.js environment. + case "nomen": // Allow weird property name. + case "single": // Allow single-quote strings. + case "subscript": // Allow identifier in subscript-notation. + case "test_cause": // Test jslint's causes. + case "test_internal_error": // Test jslint's internal-error + // ... handling-ability. + case "this": // Allow 'this'. + case "trace": // Include jslint stack-trace in warnings. + case "unordered": // Allow unordered cases, params, properties, + // ... variables, and exports. + case "variable": // Allow unordered const and let declarations + // ... that are not at top of function-scope. + case "white": // Allow messy whitespace. + option_dict[key] = val; + break; + +// PR-404 - Alias "evil" to jslint-directive "eval" for backwards-compat. + + case "evil": + return option_set_item("eval", val); + +// PR-404 - Alias "nomen" to jslint-directive "name" for backwards-compat. + + case "name": + return option_set_item("nomen", val); + default: + return false; + } + +// Initialize global-variables. + + switch (val && key) { + +// Assign global browser variables to global_dict. +/* +// /\*jslint beta, browser, devel*\/ +console.log(JSON.stringify(Object.keys(window).sort(), undefined, 4)); +*/ + + case "browser": + object_assign_from_list(global_dict, [ + +// Shared with Node.js. + + "AbortController", + // "Buffer", + // "Crypto", + // "CryptoKey", + "Event", + "EventTarget", + "MessageChannel", + "MessageEvent", + "MessagePort", + // "Request", + // "Response", + // "SubtleCrypto", + "TextDecoder", + "TextEncoder", + "URL", + "URLSearchParams", + "WebAssembly", + // "__dirname", + // "__filename", + // "atob", + // "btoa", + // "clearImmediate", + "clearInterval", + "clearTimeout", + // "console", + // "crypto", + // "exports", + // "fetch", + // "global", + // "module", + "performance", + // "process", + "queueMicrotask", + // "require", + // "setImmediate", + "setInterval", + "setTimeout", + +// Web worker only. +// https://github.com/mdn/content/blob/main/files/en-us/web/api +// /workerglobalscope/index.md + + "importScripts", + +// Window. + + "Blob", + // "CharacterData", + // "DocumentType", + // "Element", + // "Event", + "FileReader", + // "FontFace", + "FormData", + "IntersectionObserver", + "MutationObserver", + // "Storage", + // "TextDecoder", + // "TextEncoder", + // "URL", + "Worker", + "XMLHttpRequest", + // "caches", + // "clearInterval", + // "clearTimeout", + "document", + // "event", + "fetch", + // "history", + "indexedDb", + "localStorage", + "location", + // "name", + "navigator", + "postMessage", + // "screen", + "sessionStorage", + // "setInterval", + // "setTimeout", + "structuredClone", + "window" + ], "browser"); + break; + +// https://docs.couchdb.org/en/stable/query-server/javascript.html#javascript + + case "couch": + object_assign_from_list(global_dict, [ + "emit", + "getRow", + "isArray", + "log", + "provides", + "registerType", + "require", + "send", + "start", + "sum", + "toJSON" + ], "CouchDb"); + break; + case "devel": + object_assign_from_list(global_dict, [ + "alert", "confirm", "console", "prompt" + ], "development"); + break; + +// These are the globals that are provided by the language standard. +// Assign global ECMAScript variables to global_dict. +/* +node --input-type=module --eval ' +// /\*jslint beta, node*\/ +import https from "https"; +(async function () { + let dict = {import: true}; + let result = ""; + await new Promise(function (resolve) { + https.get(( + "https://raw.githubusercontent.com/mdn/content/main/files" + + "/en-us/web/javascript/reference/global_objects/index.md" + ), function (res) { + res.on("data", function (chunk) { + result += chunk; + }).on("end", resolve).setEncoding("utf8"); + }); + }); + result.replace(( + /\n- \{\{JSxRef\("(?:Global_Objects\/)?([^"\/]+?)"/g + ), function (ignore, key) { + if (globalThis.hasOwnProperty(key)) { + dict[key] = true; + } + return ""; + }); + console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4)); +}()); +' +*/ + + case "ecma": + object_assign_from_list(global_dict, [ + "AggregateError", + "Array", + "ArrayBuffer", + "Atomics", + "BigInt", + "BigInt64Array", + "BigUint64Array", + "Boolean", + "DataView", + "Date", + "Error", + "EvalError", + "Float32Array", + "Float64Array", + "Function", + "Infinity", + "Int16Array", + "Int32Array", + "Int8Array", + "Intl", + "JSON", + "Map", + "Math", + "NaN", + "Number", + "Object", + "Promise", + "Proxy", + "RangeError", + "ReferenceError", + "Reflect", + "RegExp", + "Set", + "SharedArrayBuffer", + "String", + "Symbol", + "SyntaxError", + "TypeError", + "URIError", + "Uint16Array", + "Uint32Array", + "Uint8Array", + "Uint8ClampedArray", + "WeakMap", + "WeakSet", + "WebAssembly", + "decodeURI", + "decodeURIComponent", + "encodeURI", + "encodeURIComponent", + "eval", + "globalThis", + "import", + "isFinite", + "isNaN", + "parseFloat", + "parseInt", + "undefined" + ], "ECMAScript"); + break; + +// Assign global Node.js variables to global_dict. +/* +node --input-type=module --eval ' +// /\*jslint beta, node*\/ +import moduleHttps from "https"; +(async function () { + let dict = Object.create(null); + let result = ""; + await new Promise(function (resolve) { + moduleHttps.get(( + "https://raw.githubusercontent.com/nodejs/node/v16.x/doc/api" + + "/globals.md" + ), function (res) { + res.on("data", function (chunk) { + result += chunk; + }).on("end", resolve).setEncoding("utf8"); + }); + }); + result.replace(( + /\n(?:\* \[`|## |## Class: )`\w+/g + ), function (match0) { + dict[match0.split("`")[1]] = true; + return ""; + }); + console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4)); +}()); +' +*/ + + case "node": + object_assign_from_list(global_dict, [ + "AbortController", + "Buffer", + // "Crypto", + // "CryptoKey", + "Event", + "EventTarget", + "MessageChannel", + "MessageEvent", + "MessagePort", + // "Request", + // "Response", + // "SubtleCrypto", + "TextDecoder", + "TextEncoder", + "URL", + "URLSearchParams", + "WebAssembly", + "__dirname", + "__filename", + // "atob", + // "btoa", + "clearImmediate", + "clearInterval", + "clearTimeout", + "console", + // "crypto", + "exports", + // "fetch", + "global", + "module", + "performance", + "process", + "queueMicrotask", + "require", + "setImmediate", + "setInterval", + "setTimeout" + ], "Node.js"); + break; + } + return true; + } + + function read_digits(base, mode) { + let digits = line_source.match( + base === "b" + ? jslint_rgx_digits_bits + : base === "o" + ? jslint_rgx_digits_octals + : base === "x" + ? jslint_rgx_digits_hexs + : jslint_rgx_digits_decimals + )[0]; + if ( + (mode !== mode_digits_empty_string && digits.length === 0) + || digits[0] === "_" + ) { + +// test_cause: +// ["0x", "read_digits", "expected_digits_after_a", "0x", 2] +// ["0x_", "read_digits", "expected_digits_after_a", "0x", 2] + + warn_at("expected_digits_after_a", line, column, snippet); + } + +// PR-390 - Add numeric-separator check. + + if (mode === mode_digits_numeric_separator) { + check_numeric_separator(digits, column); + } else if (digits.indexOf("_") >= 0) { + +// test_cause: +// ["\"\\u{1_2}\"", "read_digits", "illegal_num_separator", "", 6] + + warn_at( + "illegal_num_separator", + line, + column + digits.indexOf("_") + 1 + ); + } + column += digits.length; + line_source = line_source.slice(digits.length); + snippet += digits; + char_after(); + return digits.length; + } + + function read_line() { + +// Put the next line of source in line_source. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + if ( + !option_dict.long + && line_whole.length > 80 + && line_disable === undefined + && !state.mode_json + && token_1 + && !mode_regexp + ) { + +// test_cause: +// ["/////////////////////////////////////////////////////////////////////////////////", "read_line", "too_long", "", 1] //jslint-ignore-line + + warn_at("too_long", line); + } + column = 0; + line += 1; + mode_regexp = false; + line_source = undefined; + line_whole = ""; + if (line_list[line] === undefined) { + return line_source; + } + line_source = line_list[line].line_source; + line_whole = line_source; + +// Scan each line for following ignore-directives: +// "/*jslint-disable*/" +// "/*jslint-enable*/" +// "//jslint-ignore-line" + + if (line_source === "/*jslint-disable*/") { + +// test_cause: +// ["/*jslint-disable*/", "read_line", "jslint_disable", "", 0] + + test_cause("jslint_disable"); + line_disable = line; + } else if (line_source === "/*jslint-enable*/") { + if (line_disable === undefined) { + +// test_cause: +// ["/*jslint-enable*/", "read_line", "unopened_enable", "", 1] + + stop_at("unopened_enable", line); + } + line_disable = undefined; + } else if ( + line_source.endsWith(" //jslint-ignore-line") + || line_source.endsWith(" //jslint-quiet") + ) { + +// test_cause: +// ["0 //jslint-ignore-line", "read_line", "jslint_ignore_line", "", 0] + + test_cause("jslint_ignore_line"); + line_list[line].directive_ignore_line = true; + } + if (line_disable !== undefined) { + +// test_cause: +// ["/*jslint-disable*/\n0", "read_line", "line_disable", "", 0] + + test_cause("line_disable"); + line_source = ""; + } + // jslint_rgx_tab + if (line_source.indexOf("\t") >= 0) { + if (!option_dict.white) { + +// test_cause: +// ["\t", "read_line", "use_spaces", "", 1] + + warn_at("use_spaces", line, line_source.indexOf("\t") + 1); + } + line_source = line_source.replace(jslint_rgx_tab, " "); + } + if (!option_dict.white && line_source.endsWith(" ")) { + +// test_cause: +// [" ", "read_line", "unexpected_trailing_space", "", 1] + + warn_at("unexpected_trailing_space", line, line_source.length - 1); + } + return line_source; + } + + function token_create(id, value, identifier) { + +// Create the token object and append it to token_list. + + let the_token = { + from, + id, + identifier: Boolean(identifier), + line, + nr: token_list.length, + thru: column, + value + }; + token_list.push(the_token); + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + mode_directive = false; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option_dict.white. + + if ( + token_prv.line === line + && token_prv.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (token_prv.id === "(comment)" || token_prv.id === "(regexp)") + ) { + +// test_cause: +// ["/**//**/", "token_create", "expected_space_a_b", "(comment)", 5] + + warn( + "expected_space_a_b", + the_token, + artifact(token_prv), + artifact(the_token) + ); + } + if (token_prv.id === "." && id === "(number)") { + +// test_cause: +// [".0", "token_create", "expected_a_before_b", ".", 1] + + warn("expected_a_before_b", token_prv, "0", "."); + } + if (token_prv_expr.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart. +// Farts are now detected by keeping a list of most recent "(" tokens at any +// given depth. When a "=>" token is encountered, the most recent "(" token at +// current depth is marked as a fart. + + switch (id) { + case "(": + paren_backtrack_list[paren_depth] = the_token; + paren_depth += 1; + break; + case ")": + paren_depth -= 1; + break; + case "=>": + if ( + token_prv_expr.id === ")" + && paren_backtrack_list[paren_depth] + ) { + paren_backtrack_list[paren_depth].fart = the_token; + } + break; + } + +// The previous token is used to detect adjacency problems. + + token_prv = the_token; + +// The token_prv_expr token is a previous token that was not a comment. +// The token_prv_expr token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (token_prv.id !== "(comment)") { + token_prv_expr = token_prv; + } + return the_token; + } + +// Init global_dict and option_dict. + + option_set_item("ecma", true); + Object.keys(option_dict).sort().forEach(function (key) { + option_set_item(key, option_dict[key] === true); + }); + object_assign_from_list(global_dict, global_list, "user-defined"); + +// Scan first line for "#!" and ignore it. + + if (line_list[jslint_fudge].line_source.startsWith("#!")) { + line += 1; + state.mode_shebang = true; + } + token_1 = lex_token(); + state.mode_json = token_1.id === "{" || token_1.id === "["; + +// Lex/loop through each token until (end). + + while (true) { + if (lex_token().id === "(end)") { + break; + } + } +} + +function jslint_phase3_parse(state) { + +// PHASE 3. Parse into using the Pratt-parser. + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + + let anon = "anonymous"; // The guessed name for anonymous functions. + let { + artifact, + catch_list, + catch_stack, + export_dict, + function_list, + function_stack, + global_dict, + import_list, + is_equal, + option_dict, + property_dict, + stop, + syntax_dict, + tenure, + test_cause, + token_global, + token_list, + warn, + warn_at + } = state; + let catchage = catch_stack[0]; // The current catch-block. + let functionage = token_global; // The current function. + let mode_var; // "var" if using var; "let" if using let. + let token_ii = 0; // The number of the next token. + let token_now = token_global; // The current token being examined in + // ... the parse. + let token_nxt = token_global; // The next token to be examined in + // ... . + + function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if ( + token_now.identifier + && token_now.id !== "function" + && token_now.id !== "async" + ) { + anon = token_now.id; + } else if ( + token_now.id === "(string)" + && jslint_rgx_identifier.test(token_now.value) + ) { + anon = token_now.value; + } + +// Attempt to match token_nxt with an expected id. + + if (id !== undefined && token_nxt.id !== id) { + return ( + match === undefined + +// test_cause: +// ["{0:0}", "advance", "expected_a_b", "0", 2] + + ? stop("expected_a_b", token_nxt, id, artifact()) + +// test_cause: +// ["{\"aa\":0", "advance", "expected_a_b_from_c_d", "{", 1] + + : stop( + "expected_a_b_from_c_d", + token_nxt, + id, + artifact(match), + match.line, + artifact() + ) + ); + } + +// Promote the tokens, skipping comments. + + token_now = token_nxt; + while (true) { + token_nxt = token_list[token_ii]; + state.token_nxt = token_nxt; + token_ii += 1; + if (token_nxt.id !== "(comment)") { + if (token_nxt.id === "(end)") { + token_ii -= 1; + } + break; + } + if (state.mode_json) { + +// test_cause: +// ["[//]", "advance", "unexpected_a", "(comment)", 2] + + warn("unexpected_a"); + } + } + } + + function assignment(id) { + +// Create an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led_infix = function (left) { + const the_token = token_now; + let right; + the_token.arity = "assignment"; + right = parse_expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "preassign" + || right.arity === "postassign" + ) { + warn("unexpected_a", right); + } + check_mutation(left); + return the_token; + }; + return the_symbol; + } + + function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token_now; + if (special !== "body") { + functionage.statement_prv = the_block; + } + the_block.arity = "statement"; + the_block.body = special === "body"; + +// Top level function bodies may include the "use strict" pragma. + + if ( + special === "body" + && function_stack.length === 1 + && token_nxt.value === "use strict" + ) { + token_nxt.statement = true; + advance("(string)"); + advance(";"); + } + stmts = parse_statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option_dict.devel && special !== "ignore") { + +// test_cause: +// ["function aa(){}", "block", "empty_block", "{", 14] + + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; + } + + function check_left(left, right) { + +// Warn if the left is not one of these: +// ?. +// ?: +// e() +// e.b +// e[b] +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !check_left(left.expression[1]) + && !check_left(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "?." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right || token_nxt); + return false; + } + return true; + } + + function check_mutation(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + +// test_cause: +// ["0=0", "check_mutation", "bad_assignment_a", "0", 1] + + warn("bad_assignment_a", the_thing); + return false; + } + return true; + } + + function check_not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === token_global) { + +// test_cause: +// [" +// while(0){} +// ", "check_not_top_level", "unexpected_at_top_level_a", "while", 1] + + warn("unexpected_at_top_level_a", thing); + } + } + + function check_ordered(type, token_list) { + +// This function will warn if is unordered. + + token_list.reduce(function (aa, token) { + const bb = artifact(token); + if (!option_dict.unordered && aa > bb) { + warn("expected_a_b_before_c_d", token, type, bb, type, aa); + } + return bb; + }, ""); + } + + function check_ordered_case(case_list) { + +// This function will warn if is unordered. + + case_list.filter(noop).map(function (token) { + switch (token.identifier || token.id) { + case "(number)": + return { + order: 1, + token, + type: "number", + value: Number(artifact(token)) + }; + case "(string)": + return { + order: 2, + token, + type: "string", + value: artifact(token) + }; + case true: + return { + order: 3, + token, + type: "identifier", + value: artifact(token) + }; + } + }).reduce(function (aa, bb) { + if ( + +// PR-419 - Hide warning about unordered case-statements behind beta-flag. + + option_dict.beta + && !option_dict.unordered + && aa && bb + && ( + aa.order > bb.order + || (aa.order === bb.order && aa.value > bb.value) + ) + ) { + warn( + "expected_a_b_before_c_d", + bb.token, + `case-${bb.type}`, + bb.value, + `case-${aa.type}`, + aa.value + ); + } + return bb; + }, undefined); + } + + function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = token_nxt; + let the_value; + +// test_cause: +// ["do{}while()", "condition", "", "", 0] +// ["if(){}", "condition", "", "", 0] +// ["while(){}", "condition", "", "", 0] + + test_cause(""); + the_paren.free = true; + advance("("); + the_value = parse_expression(0); + advance(")"); + if (the_value.wrapped === true) { + +// test_cause: +// ["while((0)){}", "condition", "unexpected_a", "(", 6] + + warn("unexpected_a", the_paren); + } + +// Check for anticondition. + + switch (the_value.id) { + case "%": + warn("unexpected_a", the_value); + break; + case "&": + warn("unexpected_a", the_value); + break; + case "(number)": + warn("unexpected_a", the_value); + break; + case "(string)": + warn("unexpected_a", the_value); + break; + case "*": + warn("unexpected_a", the_value); + break; + case "+": + warn("unexpected_a", the_value); + break; + case "-": + warn("unexpected_a", the_value); + break; + case "/": + warn("unexpected_a", the_value); + break; + case "<<": + warn("unexpected_a", the_value); + break; + case ">>": + warn("unexpected_a", the_value); + break; + case ">>>": + warn("unexpected_a", the_value); + break; + case "?": + warn("unexpected_a", the_value); + break; + case "^": + warn("unexpected_a", the_value); + break; + case "typeof": + warn("unexpected_a", the_value); + break; + case "|": + warn("unexpected_a", the_value); + break; + case "~": + +// test_cause: +// ["if(0%0){}", "condition", "unexpected_a", "%", 5] +// ["if(0&0){}", "condition", "unexpected_a", "&", 5] +// ["if(0){}", "condition", "unexpected_a", "0", 4] +// ["if(0*0){}", "condition", "unexpected_a", "*", 5] +// ["if(0+0){}", "condition", "unexpected_a", "+", 5] +// ["if(0-0){}", "condition", "unexpected_a", "-", 5] +// ["if(0/0){}", "condition", "unexpected_a", "/", 5] +// ["if(0<<0){}", "condition", "unexpected_a", "<<", 5] +// ["if(0>>0){}", "condition", "unexpected_a", ">>", 5] +// ["if(0>>>0){}", "condition", "unexpected_a", ">>>", 5] +// ["if(0?0:0){}", "condition", "unexpected_a", "?", 5] +// ["if(0^0){}", "condition", "unexpected_a", "^", 5] +// ["if(0|0){}", "condition", "unexpected_a", "|", 5] +// ["if(\"aa\"){}", "condition", "unexpected_a", "aa", 4] +// ["if(typeof 0){}", "condition", "unexpected_a", "typeof", 4] +// ["if(~0){}", "condition", "unexpected_a", "~", 4] + + warn("unexpected_a", the_value); + break; + } + return the_value; + } + + function constant(id, type, value) { + +// Create a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud_prefix = ( + typeof value === "function" + ? value + : function () { + token_now.constant = true; + if (value !== undefined) { + token_now.value = value; + } + return token_now; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; + } + + function constant_Function() { + if (!option_dict.eval) { + +// test_cause: +// ["Function", "constant_Function", "unexpected_a", "Function", 1] + + warn("unexpected_a", token_now); + } else if (token_nxt.id !== "(") { + +// test_cause: +// [" +// /*jslint eval*/ +// Function +// ", "constant_Function", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "(", artifact()); + } + return token_now; + } + + function constant_arguments() { + +// test_cause: +// ["arguments", "constant_arguments", "unexpected_a", "arguments", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function constant_eval() { + if (!option_dict.eval) { + +// test_cause: +// ["eval", "constant_eval", "unexpected_a", "eval", 1] + + warn("unexpected_a", token_now); + } else if (token_nxt.id !== "(") { + +// test_cause: +// ["/*jslint eval*/\neval", "constant_eval", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "(", artifact()); + } + return token_now; + } + + function constant_ignore() { + +// test_cause: +// ["ignore", "constant_ignore", "unexpected_a", "ignore", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function constant_isInfinite() { + +// test_cause: +// ["isFinite", "constant_isInfinite", "expected_a_b", "isFinite", 1] + + warn("expected_a_b", token_now, "Number.isFinite", "isFinite"); + return token_now; + } + + function constant_isNaN() { + +// test_cause: +// ["isNaN(0)", "constant_isNaN", "number_isNaN", "isNaN", 1] + + warn("number_isNaN", token_now); + return token_now; + } + + function constant_this() { + if (!option_dict.this) { + +// test_cause: +// ["this", "constant_this", "unexpected_a", "this", 1] + + warn("unexpected_a", token_now); + } + return token_now; + } + + function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + let earlier; + let id = name.id; + +// Reserved words may not be enrolled. + + if (syntax_dict[id] !== undefined && id !== "ignore") { + +// test_cause: +// ["let undefined", "enroll", "reserved_a", "undefined", 5] + + warn("reserved_a", name); + return; + } + +// Has the name been enrolled in this context? + + earlier = functionage.context[id] || catchage.context[id]; + if (earlier) { + +// test_cause: +// ["let aa;let aa", "enroll", "redefinition_a_b", "1", 12] + + warn("redefinition_a_b", name, id, earlier.line); + return; + } + +// Has the name been enrolled in an outer context? + + function_stack.forEach(function ({ + context + }) { + earlier = context[id] || earlier; + }); + if (earlier && id === "ignore") { + if (earlier.role === "variable") { + +// test_cause: +// ["let ignore;function aa(ignore){}", "enroll", "redefinition_a_b", "1", 24] + + warn("redefinition_a_b", name, id, earlier.line); + } + } else if ( + earlier + && role !== "parameter" && role !== "function" + && (role !== "exception" || earlier.role !== "exception") + ) { + +// test_cause: +// [" +// function aa(){try{aa();}catch(aa){aa();}} +// ", "enroll", "redefinition_a_b", "1", 31] +// ["function aa(){var aa;}", "enroll", "redefinition_a_b", "1", 19] + + warn("redefinition_a_b", name, id, earlier.line); + } else if ( + option_dict.beta + && global_dict[id] + && role !== "parameter" + ) { + +// test_cause: +// ["let Array", "enroll", "redefinition_global_a_b", "Array", 5] + + warn("redefinition_global_a_b", name, global_dict[id], id); + } + +// Enroll it. + + Object.assign(name, { + dead: true, + init: false, + parent: ( + role === "exception" + ? catchage + : functionage + ), + readonly, + role, + used: 0 + }); + name.parent.context[id] = name; + } + + function infix(bp, id, f) { + +// Create an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led_infix = function (left) { + const the_token = token_now; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, parse_expression(bp)]; + return the_token; + }; + return the_symbol; + } + + function infix_dot(left) { + const the_token = token_now; + let name = token_nxt; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "flat" + && name.id !== "flatMap" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + +// test_cause: +// ["\"\".aa", "check_left", "unexpected_a", ".", 3] + + check_left(left, the_token); + } + if (!name.identifier) { + +// test_cause: +// ["aa.0", "infix_dot", "expected_identifier_a", "0", 4] + + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; + } + + function infix_fart_unwrapped() { + +// test_cause: +// ["aa=>0", "infix_fart_unwrapped", "wrap_fart_parameter", "=>", 3] + + return stop("wrap_fart_parameter", token_now); + } + + function infix_grave(left) { + const the_tick = prefix_tick(); + +// test_cause: +// ["0``", "check_left", "unexpected_a", "`", 2] + + check_left(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; + } + + function infix_lbracket(left) { + const the_token = token_now; + let name; + let the_subscript = parse_expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + name = survey(the_subscript); + +// PR-404 - Add new directive "subscript" to play nice with Google Closure. + + if (!option_dict.subscript && jslint_rgx_identifier.test(name)) { + +// test_cause: +// ["aa[`aa`]", "infix_lbracket", "subscript_a", "aa", 4] + + warn("subscript_a", the_subscript, name); + } + } + +// test_cause: +// ["0[0]", "check_left", "unexpected_a", "[", 2] + + check_left(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; + } + + function infix_lparen(left) { + const the_paren = token_now; + let ellipsis; + let the_argument; + if (left.id !== "function") { + +// test_cause: +// ["(0?0:0)()", "check_left", "unexpected_a", "(", 8] +// ["0()", "check_left", "unexpected_a", "(", 2] + + check_left(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (token_nxt.id !== ")") { + +// Parse/loop through each token in expression (...). + + while (true) { + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = parse_expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + +// test_cause: +// ["aa(0)", "infix_lparen", "free", "", 0] + + test_cause("free"); + the_paren.free = true; + if (the_argument.wrapped === true) { + +// test_cause: +// ["aa((0))", "infix_lparen", "unexpected_a", "(", 3] + + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + +// test_cause: +// ["aa()", "infix_lparen", "not_free", "", 0] +// ["aa(0,0)", "infix_lparen", "not_free", "", 0] + + test_cause("not_free"); + the_paren.free = false; + } + return the_paren; + } + + function infix_option_chain(left) { + const the_token = token_now; + let name = token_nxt; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + +// test_cause: +// ["(0+0)?.0", "infix_option_chain", "check_left", "", 0] + + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + test_cause("check_left"); + +// test_cause: +// ["(/./)?.0", "check_left", "unexpected_a", "?.", 6] +// ["\"aa\"?.0", "check_left", "unexpected_a", "?.", 5] +// ["aa=[]?.aa", "check_left", "unexpected_a", "?.", 6] + + check_left(left, the_token); + } + +// Issue #468 - Fix optional dynamic-property/function-call not recognized. + + if (name.id === "[" || name.id === "(") { + test_cause("dyn_prop_or_call"); + +// test_cause: +// ["aa?.(bb)", "infix_option_chain", "dyn_prop_or_call", "", 0] +// ["aa?.[bb]", "infix_option_chain", "dyn_prop_or_call", "", 0] + + return left; + } + if (!name.identifier) { + +// test_cause: +// ["aa?.0", "infix_option_chain", "expected_identifier_a", "0", 5] + + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; + } + + function infixr(bp, id) { + +// Create a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led_infix = function parse_infixr_led(left) { + const the_token = token_now; + +// test_cause: +// ["0**0", "parse_infixr_led", "led_infix", "", 0] + + test_cause("led_infix"); + the_token.arity = "binary"; + the_token.expression = [left, parse_expression(bp - 1)]; + return the_token; + }; + return the_symbol; + } + + function parse_expression(rbp, initial) { + +// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it +// is looking for ad hoc lint patterns. We add .fud_stmt to Pratt's model, which +// is like .nud_prefix except that it is only used on the first token of a +// statement. Having .fud_stmt makes it much easier to define statement-oriented +// languages like JavaScript. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// .nud_prefix Null denotation. The prefix handler. +// .fud_stmt First null denotation. The statement handler. +// .led_infix Left denotation. The infix/postfix handler. +// lbp Left binding power of infix operator. It tells us how strongly +// the operator binds to the argument at its left. +// rbp Right binding power. + +// It processes a nud_prefix (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop (it +// consumes tokens until it meets a token whose lbp <= rbp). Specifically, it +// means that it collects all tokens that bind together before returning to the +// operator that called it. It returns the expression's parse tree. + +// For example, "3 + 1 * 2 * 4 + 5" +// parses into +// { +// "id": "+", +// "expression": [ +// { +// "id": "+", +// "expression": [ +// { +// "id": "(number)", +// "value": "3" +// }, +// { +// "id": "*", +// "expression": [ +// { +// "id": "*", +// "expression": [ +// { +// "id": "(number)", +// "value": "1" +// }, +// { +// "id": "(number)", +// "value": "2" +// } +// ] +// }, +// { +// "id": "(number)", +// "value": "4" +// } +// ] +// } +// ] +// }, +// { +// "id": "(number)", +// "value": "5" +// } +// ] +// } + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement. + + if (!initial) { + advance(); + } + the_symbol = syntax_dict[token_now.id]; + if (the_symbol !== undefined && the_symbol.nud_prefix !== undefined) { + +// test_cause: +// ["0", "parse_expression", "symbol", "", 0] + + test_cause("symbol"); + left = the_symbol.nud_prefix(); + } else if (token_now.identifier) { + +// test_cause: +// ["aa", "parse_expression", "identifier", "", 0] + + test_cause("identifier"); + left = token_now; + left.arity = "variable"; + } else { + +// test_cause: +// ["!", "parse_expression", "unexpected_a", "(end)", 1] +// ["/./", "parse_expression", "unexpected_a", "/", 1] +// ["let aa=`${}`;", "parse_expression", "unexpected_a", "}", 11] + + return stop("unexpected_a", token_now); + } + +// Parse/loop through each symbol in expression. + + while (true) { + the_symbol = syntax_dict[token_nxt.id]; + if ( + the_symbol === undefined + || the_symbol.led_infix === undefined + || the_symbol.lbp <= rbp + ) { + break; + } + advance(); + left = the_symbol.led_infix(left); + } + return left; + } + + function parse_fart(the_fart) { + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + Object.assign(the_fart, { + arity: "binary", + context: empty(), + finally: 0, + level: functionage.level + 1, + loop: 0, + name: anon, + switch: 0, + try: 0 + }); + +// PR-384 - Relax warning "function_in_loop". +// +// if (functionage.loop > 0) { + +// // test_cause: +// // ["while(0){aa.map(()=>0);}", "parse_fart", "function_in_loop", "=>", 19] +// +// warn("function_in_loop", the_fart); +// } + +// Push the current function context and establish a new one. + + function_list.push(the_fart); + function_stack.push(functionage); + functionage = the_fart; + +// Parse the parameter list. + + prefix_function_parameter(the_fart); + advance("=>"); + +// The function's body is a block. + + if (token_nxt.id === "{") { + if (!option_dict.fart) { + +// test_cause: +// ["()=>{}", "parse_fart", "use_function_not_fart", "=>", 3] + + warn("use_function_not_fart", the_fart); + } + the_fart.block = block("body"); + } else if ( + syntax_dict[token_nxt.id] !== undefined + && syntax_dict[token_nxt.id].fud_stmt !== undefined + ) { + +// PR-384 - Bugfix - Fixes issue #379 - warn against naked-statement in fart. + +// test_cause: +// ["()=>delete aa", "parse_fart", "unexpected_a_after_b", "=>", 5] + + stop("unexpected_a_after_b", token_nxt, token_nxt.id, "=>"); + +// The function's body is an expression. + + } else { + the_fart.expression = parse_expression(0); + } + +// Restore the previous context. + + functionage = function_stack.pop(); + return the_fart; + } + + function parse_json() { + let container; + let is_dup; + let name; + let negative; + switch (token_nxt.id) { + case "(number)": + if (!jslint_rgx_json_number.test(token_nxt.value)) { + +// test_cause: +// ["[-.0]", "parse_json", "unexpected_a", ".", 3] +// ["[-0x0]", "parse_json", "unexpected_a", "0x0", 3] +// ["[.0]", "parse_json", "unexpected_a", ".", 2] +// ["[0x0]", "parse_json", "unexpected_a", "0x0", 2] + + warn("unexpected_a"); + } + advance("(number)"); + return token_now; + case "(string)": + if (token_nxt.quote !== "\"") { + +// test_cause: +// ["['']", "parse_json", "unexpected_a", "'", 2] + + warn("unexpected_a", token_nxt, token_nxt.quote); + } + advance("(string)"); + return token_now; + case "-": + negative = token_nxt; + negative.arity = "unary"; + advance("-"); + +// Recurse parse_json(). + + negative.expression = parse_json(); + return negative; + case "[": + +// test_cause: +// ["[]", "parse_json", "bracket", "", 0] + + test_cause("bracket"); + container = token_nxt; + container.expression = []; + advance("["); + if (token_nxt.id !== "]") { + while (true) { + +// Recurse parse_json(). + + container.expression.push(parse_json()); + if (token_nxt.id !== ",") { + +// test_cause: +// ["[0,0]", "parse_json", "comma", "", 0] + + test_cause("comma"); + break; + } + advance(","); + } + } + advance("]", container); + return container; + case "false": + case "null": + case "true": + +// test_cause: +// ["[false]", "parse_json", "constant", "", 0] +// ["[null]", "parse_json", "constant", "", 0] +// ["[true]", "parse_json", "constant", "", 0] + + test_cause("constant"); + advance(); + return token_now; + case "{": + +// test_cause: +// ["{}", "parse_json", "brace", "", 0] + + test_cause("brace"); + container = token_nxt; + +// Explicit empty-object required to detect "__proto__". + + is_dup = empty(); + container.expression = []; + advance("{"); + if (token_nxt.id !== "}") { + +// JSON +// Parse/loop through each property in {...}. + + while (true) { + if (token_nxt.quote !== "\"") { + +// test_cause: +// ["{0:0}", "parse_json", "unexpected_a", "0", 2] + + warn( + "unexpected_a", + token_nxt, + token_nxt.quote + ); + } + name = token_nxt; + advance("(string)"); + if (is_dup[token_now.value] !== undefined) { + +// test_cause: +// ["{\"aa\":0,\"aa\":0}", "parse_json", "duplicate_a", "aa", 9] + + warn("duplicate_a", token_now); + } else if (token_now.value === "__proto__") { + +// test_cause: +// ["{\"__proto__\":0}", "parse_json", "weird_property_a", "__proto__", 2] + + warn("weird_property_a", token_now); + } else { + is_dup[token_now.value] = token_now; + } + advance(":"); + container.expression.push( + +// Recurse parse_json(). + + Object.assign(parse_json(), { + label: name + }) + ); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance("}", container); + return container; + default: + +// test_cause: +// ["[undefined]", "parse_json", "unexpected_a", "undefined", 2] + + stop("unexpected_a"); + } + } + + function parse_statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token_now.identifier && token_nxt.id === ":") { + the_label = token_now; + if (the_label.id === "ignore") { + +// test_cause: +// ["ignore:", "parse_statement", "unexpected_a", "ignore", 1] + + warn("unexpected_a", the_label); + } + advance(":"); + switch (token_nxt.id) { + case "do": + case "for": + case "switch": + case "while": + +// test_cause: +// ["aa:do{}", "parse_statement", "the_statement_label", "do", 0] +// ["aa:for{}", "parse_statement", "the_statement_label", "for", 0] +// ["aa:switch{}", "parse_statement", "the_statement_label", "switch", 0] +// ["aa:while{}", "parse_statement", "the_statement_label", "while", 0] + + test_cause("the_statement_label", token_nxt.id); + enroll(the_label, "label", true); + the_label.dead = false; + the_label.init = true; + the_statement = parse_statement(); + functionage.statement_prv = the_statement; + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + +// test_cause: +// ["aa:", "parse_statement", "unexpected_label_a", "aa", 1] + + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token_now; + first.statement = true; + the_symbol = syntax_dict[first.id]; + if ( + the_symbol !== undefined + && the_symbol.fud_stmt !== undefined + +// PR-318 - Bugfix - Fixes issues #316, #317 - dynamic-import(). + + && !(the_symbol.id === "import" && token_nxt.id === "(") + ) { + the_symbol.disrupt = false; + the_symbol.statement = true; + token_now.arity = "statement"; + the_statement = the_symbol.fud_stmt(); + functionage.statement_prv = the_statement; + } else { + +// It is an expression statement. + + the_statement = parse_expression(0, true); + functionage.statement_prv = the_statement; + if (the_statement.wrapped && the_statement.id !== "(") { + +// test_cause: +// ["(0)", "parse_statement", "unexpected_a", "(", 1] + + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; + } + + function parse_statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const statement_list = []; + let a_statement; + let disrupt = false; + +// Parse/loop each statement until a statement-terminator is reached. + + while (true) { + switch (token_nxt.id) { + case "(end)": + case "case": + case "default": + case "else": + case "}": + +// test_cause: +// [";", "parse_statements", "closer", "", 0] +// ["case", "parse_statements", "closer", "", 0] +// ["default", "parse_statements", "closer", "", 0] +// ["else", "parse_statements", "closer", "", 0] +// ["}", "parse_statements", "closer", "", 0] + + test_cause("closer"); + return statement_list; + } + a_statement = parse_statement(); + statement_list.push(a_statement); + if (disrupt) { + +// test_cause: +// ["while(0){break;0;}", "parse_statements", "unreachable_a", "0", 16] + + warn("unreachable_a", a_statement); + } + disrupt = a_statement.disrupt; + } + } + + function postassign(id) { + +// Create one of the postassign operators. + + const the_symbol = symbol(id, 150); + the_symbol.led_infix = function (left) { + token_now.expression = left; + token_now.arity = "postassign"; + check_mutation(token_now.expression); + return token_now; + }; + return the_symbol; + } + + function preassign(id) { + +// Create one of the preassign operators. + + const the_symbol = symbol(id); + the_symbol.nud_prefix = function () { + const the_token = token_now; + the_token.arity = "preassign"; + the_token.expression = parse_expression(150); + check_mutation(the_token.expression); + return the_token; + }; + return the_symbol; + } + + function prefix(id, f) { + +// Create a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud_prefix = function () { + const the_token = token_now; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = parse_expression(150); + return the_token; + }; + return the_symbol; + } + + function prefix_assign_divide() { + +// test_cause: +// ["/=", "prefix_assign_divide", "expected_a_b", "/=", 1] + + stop("expected_a_b", token_now, "/\\=", "/="); + } + + function prefix_async() { + let the_async = token_now; + let the_function; + token_nxt.arity = the_async.arity; + +// PR-414 - Parse async fart. + + if (token_nxt.fart) { + advance("("); + the_function = Object.assign(token_now.fart, { + async: 1 + }); + if (!option_dict.fart) { + +// test_cause: +// ["async()=>0", "prefix_async", "use_function_not_fart", "=>", 8] + + warn("use_function_not_fart", the_function); + } + prefix_lparen(); + +// Parse async function. + + } else { + advance("function"); + the_function = Object.assign(token_now, { + async: 1 + }); + prefix_function(); + } + if (the_function.async === 1) { + +// test_cause: +// [" +// async function aa(){} +// ", "prefix_async", "missing_await_statement", "function", 7] + + warn("missing_await_statement", the_function); + } + return the_function; + } + + function prefix_await() { + const the_await = token_now; + +// PR-370 - Add top-level-await support. + + if (functionage.async === 0 && functionage !== token_global) { + +// test_cause: +// ["function aa(){aa=await 0;}", "prefix_await", "unexpected_a", "await", 18] +// ["function aa(){await 0;}", "prefix_await", "unexpected_a", "await", 15] + + warn("unexpected_a", the_await); + } else { + functionage.async += 1; + } + if (the_await.arity === "statement") { + +// PR-405 - Bugfix - fix expression after "await" mis-identified as statement. + + the_await.expression = parse_expression(150); + semicolon(); + } else { + the_await.expression = parse_expression(150); + } + return the_await; + } + + function prefix_fart() { + +// test_cause: +// ["=>0", "prefix_fart", "expected_a_before_b", "=>", 1] + + return stop("expected_a_before_b", token_now, "()", "=>"); + } + + function prefix_function(the_function) { + let name = the_function && the_function.name; + if (the_function === undefined) { + the_function = token_now; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!token_nxt.identifier) { + +// test_cause: +// ["function(){}", "prefix_function", "expected_identifier_a", "(", 9] +// ["function*aa(){}", "prefix_function", "expected_identifier_a", "*", 9] + + return stop("expected_identifier_a"); + } + name = token_nxt; + enroll(name, "variable", true); + the_function.name = Object.assign(name, { + calls: empty(), + +// PR-331 - Bugfix - Fixes issue #272 - function hoisting not allowed. + + dead: false, + init: true + }); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + the_function.name = anon; + if (token_nxt.identifier) { + name = token_nxt; + the_function.name = name; + advance(); + } + } + } + +// Probably deadcode. +// if (mode_mega) { +// warn("unexpected_a", the_function); +// } +// jslint_assert(!mode_mega, `Expected !mode_mega.`); + +// PR-378 - Relax warning "function_in_loop". +// +// // Don't create functions in loops. It is inefficient, and it can lead to +// // scoping errors. +// +// if (functionage.loop > 0) { +// +// // test_cause: +// // [" +// // while(0){aa.map(function(){});} +// // ", "prefix_function", "function_in_loop", "function", 17] +// +// warn("function_in_loop", the_function); +// } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + Object.assign(the_function, { + async: the_function.async || 0, + context: empty(), + finally: 0, + level: functionage.level + 1, + loop: 0, + statement_prv: undefined, + switch: 0, + try: 0 + }); + if (the_function.arity !== "statement" && typeof name === "object") { + +// test_cause: +// ["let aa=function bb(){return;};", "prefix_function", "expression", "bb", 0] + + test_cause("expression", name.id); + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// PR-334 - Bugfix - fix function-redefinition not warned inside function-call. +// Push the current function context and establish a new one. + + function_list.push(the_function); + function_stack.push(functionage); + functionage = the_function; + +// Parse the parameter list. + + advance("("); + token_now.arity = "function"; + prefix_function_parameter(the_function); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && token_nxt.line === token_now.line + ) { + +// test_cause: +// ["function aa(){}0", "prefix_function", "unexpected_a", "0", 16] + + return stop("unexpected_a"); + } + if ( + token_nxt.id === "." + || token_nxt.id === "?." + +// PR-459 - Allow destructuring-assignment after function-definition. + + // || token_nxt.id === "[" + ) { + +// test_cause: +// ["function aa(){}\n.aa", "prefix_function", "unexpected_a", ".", 1] +// ["function aa(){}\n?.aa", "prefix_function", "unexpected_a", "?.", 1] + + warn("unexpected_a"); + } + +// Check functions are ordered. + + check_ordered( + "function", + function_list.slice( + function_list.indexOf(the_function) + 1 + ).map(function ({ + level, + name + }) { + return (level === the_function.level + 1) && name; + }).filter(function (name) { + return option_dict.beta && name && name.id; + }) + ); + +// Restore the previous context. + + functionage = function_stack.pop(); + return the_function; + } + + function prefix_function_parameter(the_function) { + +// This function will parse input at beginning of + + let optional; + let parameters = []; + let signature = ["("]; + let subparam; + function param_enroll(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + +// test_cause: +// ["([aa])=>0", "param_enroll", "use_function_not_fart", "=>", 7] +// ["({aa})=>0", "param_enroll", "use_function_not_fart", "=>", 7] + + if (the_function.id === "=>" && !option_dict.fart) { + warn("use_function_not_fart", the_function); + } + +// Recurse param_enroll(). + + name.names.forEach(param_enroll); + } + } + function param_parse() { + let ellipsis = false; + let param; + if (token_nxt.id === "{") { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,{}){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + param = token_nxt; + param.names = []; + advance("{"); + signature.push("{"); + while (true) { + subparam = token_nxt; + if (!subparam.identifier) { + +// test_cause: +// ["function aa(aa=0,{}){}", "param_parse", "expected_identifier_a", "}", 19] +// ["function aa({0}){}", "param_parse", "expected_identifier_a", "0", 14] + + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (token_nxt.id === ":") { + advance(":"); + advance(); + token_now.label = subparam; + subparam = token_now; + if (!subparam.identifier) { + +// test_cause: +// ["function aa({aa:0}){}", "param_parse", "expected_identifier_a", "}", 18] + + return stop( + "expected_identifier_a", + token_nxt + ); + } + } + +// test_cause: +// ["function aa({aa=aa},aa){}", "param_parse", "equal", "", 0] + + test_cause("equal"); + if (token_nxt.id === "=") { + advance("="); + subparam.expression = parse_expression(); + param.open = true; + } + param.names.push(subparam); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + } else { + break; + } + } + parameters.push(param); + +// test_cause: +// [" +// function aa({bb,aa}){} +// ", "check_ordered", "expected_a_b_before_c_d", "aa", 17] + + check_ordered("parameter", param.names); + advance("}"); + signature.push("}"); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } else if (token_nxt.id === "[") { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,[]){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + param = token_nxt; + param.names = []; + advance("["); + signature.push("[]"); + while (true) { + subparam = token_nxt; + if (!subparam.identifier) { + +// test_cause: +// ["function aa(aa=0,[]){}", "param_parse", "expected_identifier_a", "]", 19] + + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + +// test_cause: +// ["function aa([aa=aa],aa){}", "param_parse", "id", "", 0] + + test_cause("id"); + if (token_nxt.id === "=") { + advance("="); + subparam.expression = parse_expression(); + param.open = true; + } + if (token_nxt.id === ",") { + advance(","); + } else { + break; + } + } + parameters.push(param); + advance("]"); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } else { + if (token_nxt.id === "...") { + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,...){}", "param_parse", "required_a_optional_b", "aa", 21] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + } + if (!token_nxt.identifier) { + +// test_cause: +// ["function aa(0){}", "param_parse", "expected_identifier_a", "0", 13] + + return stop("expected_identifier_a"); + } + param = token_nxt; + parameters.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (token_nxt.id === "=") { + optional = param; + advance("="); + param.expression = parse_expression(0); + } else { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,bb){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } + } + } + +// test_cause: +// ["function aa(){}", "prefix_function_parameter", "opener", "(", 0] + + test_cause("opener", token_now.id); + token_now.free = false; + if (token_nxt.id !== ")" && token_nxt.id !== "(end)") { + param_parse(); + } + advance(")"); + signature.push(")"); + parameters.forEach(param_enroll); + the_function.parameters = parameters; + the_function.signature = signature.join(""); + } + + function prefix_lbrace() { + const seen = empty(); + const the_brace = token_now; + let extra; + let full; + let id; + let name; + let the_colon; + let value; + the_brace.expression = []; + if (token_nxt.id !== "}") { + +// Parse/loop through each property in {...}. + + while (true) { + name = token_nxt; + advance(); + if ( + (name.id === "get" || name.id === "set") + && token_nxt.identifier + ) { + if (!option_dict.getset) { + +// test_cause: +// ["aa={get aa(){}}", "prefix_lbrace", "unexpected_a", "get", 5] + + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + token_nxt.id; + name = token_nxt; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + +// test_cause: +// ["aa={get aa(){},get aa(){}}", "prefix_lbrace", "duplicate_a", "aa", 20] + + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else if (name.id === "`") { + +// test_cause: +// ["aa={`aa`:0}", "prefix_lbrace", "unexpected_a", "`", 5] + + stop("unexpected_a", name); + + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + +// test_cause: +// ["aa={aa,aa}", "prefix_lbrace", "duplicate_a", "aa", 8] + + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (token_nxt.id === "}" || token_nxt.id === ",") { + if (typeof extra === "string") { + +// test_cause: +// ["aa={get aa}", "prefix_lbrace", "closer", "", 0] + + test_cause("closer"); + advance("("); + } + value = parse_expression(Infinity, true); + } else if (token_nxt.id === "(") { + +// test_cause: +// ["aa={aa()}", "prefix_lbrace", "paren", "", 0] +// ["aa={get aa(){}}", "prefix_lbrace", "paren", "", 0] + + test_cause("paren"); + value = prefix_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + +// test_cause: +// ["aa={get aa.aa}", "prefix_lbrace", "paren", "", 0] + + test_cause("paren"); + advance("("); + } + the_colon = token_nxt; + advance(":"); + value = parse_expression(0); + if ( + value.id === name.id + && value.id !== "function" + ) { + +// test_cause: +// ["aa={aa:aa}", "prefix_lbrace", "unexpected_a", ": aa", 7] + + warn("unexpected_a", the_colon, ": " + name.id); + } + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + +// test_cause: +// ["aa={\"aa\":0}", "prefix_lbrace", "colon", "", 0] + + test_cause("colon"); + advance(":"); + value = parse_expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (token_nxt.id !== ",") { + break; + } + +// test_cause: +// ["aa={\"aa\":0,\"bb\":0}", "prefix_lbrace", "comma", "", 0] + + test_cause("comma"); + advance(","); + if (token_nxt.id === "}") { + +// test_cause: +// ["let aa={aa:0,}", "prefix_lbrace", "unexpected_a", ",", 13] + + warn("unexpected_a", token_now); + break; + } + } + } + +// test_cause: +// ["aa={bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8] + + check_ordered( + "property", + the_brace.expression.map(function ({ + label + }) { + return label; + }) + ); + advance("}"); + return the_brace; + } + + function prefix_lbracket() { + const the_token = token_now; + let element; + let ellipsis; + the_token.expression = []; + if (token_nxt.id !== "]") { + +// Parse/loop through each element in [...]. + + while (true) { + ellipsis = false; + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + element = parse_expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (token_nxt.id !== ",") { + break; + } + advance(","); + if (token_nxt.id === "]") { + +// test_cause: +// ["let aa=[0,]", "prefix_lbracket", "unexpected_a", ",", 10] + + warn("unexpected_a", token_now); + break; + } + } + } + advance("]"); + return the_token; + } + + function prefix_lparen() { + let the_paren = token_now; + let the_value; + +// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart. + + if (token_now.fart) { + return parse_fart(token_now.fart); + } + +// test_cause: +// ["(0)", "prefix_lparen", "expr", "", 0] + + test_cause("expr"); + the_paren.free = true; + the_value = parse_expression(0); + if (the_value.wrapped === true) { + +// test_cause: +// ["((0))", "prefix_lparen", "unexpected_a", "(", 1] + + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + return the_value; + } + + function prefix_new() { + const the_new = token_now; + let right; + right = parse_expression(160); + if (token_nxt.id !== "(") { + +// test_cause: +// ["new aa", "prefix_new", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "()", artifact()); + } + the_new.expression = right; + return the_new; + } + + function prefix_tick() { + const the_tick = token_now; + the_tick.value = []; + the_tick.expression = []; + if (token_nxt.id !== "`") { + +// Parse/loop through each token in `${...}`. + + while (true) { + advance("(string)"); + the_tick.value.push(token_now); + if (token_nxt.id !== "${") { + break; + } + advance("${"); + +// test_cause: +// ["let aa=`${}`;", "prefix_tick", "${", "", 0] + + test_cause("${"); + the_tick.expression.push(parse_expression(0)); + advance("}"); + } + } + advance("`"); + return the_tick; + } + + function prefix_void() { + const the_void = token_now; + +// test_cause: +// ["void 0", "prefix_void", "unexpected_a", "void", 1] +// ["void", "prefix_void", "unexpected_a", "void", 1] + + warn("unexpected_a", the_void); + the_void.expression = parse_expression(0); + return the_void; + } + + function semicolon() { + +// Try to match a semicolon. + + if (token_nxt.id === ";") { + advance(";"); + } else { + +// test_cause: +// ["0", "semicolon", "expected_a_b", "(end)", 1] + + warn_at( + "expected_a_b", + token_now.line, + token_now.thru + 1, + ";", + artifact() + ); + } + anon = "anonymous"; + } + + function stmt(id, fud_stmt) { + +// Create a statement. + + const the_symbol = symbol(id); + the_symbol.fud_stmt = fud_stmt; + return the_symbol; + } + + function stmt_break() { + const the_break = token_now; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + +// test_cause: +// ["break", "stmt_break", "unexpected_a", "break", 1] + + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (token_nxt.identifier && token_now.line === token_nxt.line) { + the_label = functionage.context[token_nxt.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + if (the_label !== undefined && the_label.dead) { + +// test_cause: +// ["aa:{function aa(aa){break aa;}}", "stmt_break", "out_of_scope_a", "aa", 27] + + warn("out_of_scope_a"); + } else { + +// test_cause: +// ["aa:{break aa;}", "stmt_break", "not_label_a", "aa", 11] + + warn("not_label_a"); + } + } else { + the_label.used += 1; + } + the_break.label = token_nxt; + advance(); + } + advance(";"); + return the_break; + } + + function stmt_continue() { + const the_continue = token_now; + if (functionage.loop < 1 || functionage.finally > 0) { + +// test_cause: +// ["continue", "stmt_continue", "unexpected_a", "continue", 1] +// [" +// function aa(){while(0){try{}finally{continue}}} +// ", "stmt_continue", "unexpected_a", "continue", 37] + + warn("unexpected_a", the_continue); + } + check_not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; + } + + function stmt_debugger() { + const the_debug = token_now; + if (!option_dict.devel) { + +// test_cause: +// ["debugger", "stmt_debugger", "unexpected_a", "debugger", 1] + + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; + } + + function stmt_delete() { + const the_token = token_now; + const the_value = parse_expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + +// test_cause: +// ["delete 0", "stmt_delete", "expected_a_b", "0", 8] + + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; + } + + function stmt_do() { + const the_do = token_now; + check_not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + +// test_cause: +// ["function aa(){do{break;}while(0)}", "stmt_do", "weird_loop", "do", 15] + + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; + } + + function stmt_export() { + let export_list = []; + let the_export = token_now; + let the_id; + let the_name; + let the_thing; + + the_export.expression = []; + if (token_nxt.id === "default") { + if (export_dict.default !== undefined) { + +// test_cause: +// [" +// export default 0;export default 0 +// ", "stmt_export", "duplicate_a", "default", 25] + + warn("duplicate_a"); + } + advance("default"); + the_thing = parse_expression(0); + if ( + the_thing.id !== "(" + || the_thing.expression[0].id !== "." + || the_thing.expression[0].expression.id !== "Object" + || the_thing.expression[0].name.id !== "freeze" + ) { + +// test_cause: +// ["export default {}", "stmt_export", "freeze_exports", "{", 16] + + warn("freeze_exports", the_thing); + +// PR-301 - Bugfix - Fixes issues #282 - optional-semicolon. + + } else { + +// test_cause: +// [" +// export default Object.freeze({}) +// ", "semicolon", "expected_a_b", "(end)", 32] + + semicolon(); + } + export_dict.default = the_thing; + the_export.expression.push(the_thing); + } else { + +// PR-439 - Add grammar for "export async function ...". + + if (token_nxt.id === "function" || token_nxt.id === "async") { + +// test_cause: +// ["export async function aa(){}", "stmt_export", "freeze_exports", "async", 8] +// ["export function aa(){}", "stmt_export", "freeze_exports", "function", 8] + + warn("freeze_exports"); + the_thing = parse_statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (export_dict[the_id] !== undefined) { + +// test_cause: +// [" +// let aa;export{aa};export function aa(){} +// ", "stmt_export", "duplicate_a", "aa", 35] + + warn("duplicate_a", the_name); + } + export_dict[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + token_nxt.id === "var" + || token_nxt.id === "let" + || token_nxt.id === "const" + ) { + +// test_cause: +// ["export const", "stmt_export", "unexpected_a", "const", 8] +// ["export let", "stmt_export", "unexpected_a", "let", 8] +// ["export var", "stmt_export", "unexpected_a", "var", 8] + + warn("unexpected_a"); + parse_statement(); + } else if (token_nxt.id === "{") { + +// test_cause: +// ["export {}", "stmt_export", "advance{", "", 0] + + test_cause("advance{"); + advance("{"); + while (true) { + if (!token_nxt.identifier) { + +// test_cause: +// ["export {}", "stmt_export", "expected_identifier_a", "}", 9] + + stop("expected_identifier_a"); + } + the_id = token_nxt.id; + export_list.push(token_nxt); + the_name = token_global.context[the_id]; + if (the_name === undefined) { + +// test_cause: +// ["export {aa}", "stmt_export", "unexpected_a", "aa", 9] + + warn("unexpected_a"); + } else { + the_name.used += 1; + if (export_dict[the_id] !== undefined) { + +// test_cause: +// ["let aa;export{aa,aa}", "stmt_export", "duplicate_a", "aa", 18] + + warn("duplicate_a"); + } + export_dict[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + if (token_nxt.id === ",") { + advance(","); + } else { + break; + } + } + +// PR-439 - Check exported properties are ordered. + +// test_cause: +// ["export {bb, aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 13] + + check_ordered("export", export_list); + advance("}"); + semicolon(); + } else { + +// test_cause: +// ["export", "stmt_export", "unexpected_a", "(end)", 1] + + stop("unexpected_a"); + } + } + state.mode_module = true; + return the_export; + } + + function stmt_for() { + let first; + let the_for = token_now; + if (!option_dict.for) { + +// test_cause: +// ["for", "stmt_for", "unexpected_a", "for", 1] + + warn("unexpected_a", the_for); + } + check_not_top_level(the_for); + functionage.loop += 1; + advance("("); + token_now.free = true; + if (token_nxt.id === ";") { + +// test_cause: +// ["for(;;){}", "stmt_for", "expected_a_b", "for (;", 1] + + return stop("expected_a_b", the_for, "while (", "for (;"); + } + switch (token_nxt.id) { + case "const": + case "let": + case "var": + +// test_cause: +// ["for(const aa in aa){}", "stmt_for", "unexpected_a", "const", 5] + + return stop("unexpected_a"); + } + first = parse_expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + +// test_cause: +// ["for(0 in aa){}", "stmt_for", "bad_assignment_a", "0", 5] + + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = parse_expression(0); + advance(";"); + the_for.inc = parse_expression(0); + if (the_for.inc.id === "++") { + +// test_cause: +// ["for(aa;aa;aa++){}", "stmt_for", "expected_a_b", "++", 13] + + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + +// test_cause: +// [" +// /*jslint for*/ +// function aa(bb,cc){for(0;0;0){break;}} +// ", "stmt_for", "weird_loop", "for", 20] + + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; + } + + function stmt_if() { + const the_if = token_now; + let the_else; + the_if.expression = condition(); + the_if.block = block(); + if (token_nxt.id === "else") { + advance("else"); + the_else = token_now; + the_if.else = ( + token_nxt.id === "if" + ? parse_statement() + : block() + ); + +// test_cause: +// ["if(0){0}else if(0){0}", "stmt_if", "else", "", 0] +// ["if(0){0}else{0}", "stmt_if", "else", "", 0] + + test_cause("else"); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + +// test_cause: +// ["if(0){break;}else{break;}", "stmt_if", "disrupt", "", 0] + + test_cause("disrupt"); + the_if.disrupt = true; + } else { + +// test_cause: +// ["if(0){break;}else{}", "stmt_if", "unexpected_a", "else", 14] + + warn("unexpected_a", the_else); + } + } + } + return the_if; + } + + function stmt_import() { + const the_import = token_now; + let name; + let names; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// if (typeof state.mode_module === "object") { +// +// // test_cause: +// // [" +// // /*global aa*/ +// // import aa from "aa" +// // ", "stmt_import", "unexpected_directive_a", "global", 1] +// +// warn( +// "unexpected_directive_a", +// state.mode_module, +// state.mode_module.directive +// ); +// } + + state.mode_module = true; + +// PR-436 - Add grammar for side-effect import-statement. + + if (token_nxt.id === "(string)") { + +// test_cause: +// ["import \"./aa.mjs\";", "stmt_import", "import_side_effect", "", 0] + + test_cause("import_side_effect"); + warn("expected_a_b", token_nxt, "{", artifact()); + advance(); + semicolon(); + return the_import; + } + if (token_nxt.identifier) { + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// ["import ignore from \"aa\"", "stmt_import", "unexpected_a", "ignore", 8] + + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + names = []; + advance("{"); + if (token_nxt.id !== "}") { + while (true) { + if (!token_nxt.identifier) { + +// test_cause: +// ["import {", "stmt_import", "expected_identifier_a", "(end)", 1] + + stop("expected_identifier_a"); + } + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// ["import {ignore} from \"aa\"", "stmt_import", "unexpected_a", "ignore", 9] + + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token_now; + if (!jslint_rgx_module.test(token_now.value)) { + +// test_cause: +// ["import aa from \"!aa\"", "stmt_import", "bad_module_name_a", "!aa", 16] + + warn("bad_module_name_a", token_now); + } + import_list.push(token_now.value); + semicolon(); + return the_import; + } + + function stmt_lbrace() { + +// test_cause: +// [";{}", "stmt_lbrace", "naked_block", "{", 2] +// ["class aa{}", "stmt_lbrace", "naked_block", "{", 9] + + warn("naked_block", token_now); + return block("naked"); + } + + function stmt_return() { + const the_return = token_now; + check_not_top_level(the_return); + if (functionage.finally > 0) { + +// test_cause: +// [" +// function aa(){try{}finally{return;}} +// ", "stmt_return", "unexpected_a", "return", 28] + + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (token_nxt.id !== ";" && the_return.line === token_nxt.line) { + the_return.expression = parse_expression(10); + } + advance(";"); + return the_return; + } + + function stmt_semicolon() { + +// test_cause: +// [";", "stmt_semicolon", "unexpected_a", ";", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function stmt_switch() { + const the_cases = []; + const the_switch = token_now; + let dups = []; + let exp; + let last; + let stmts; + let the_case; + let the_default; + let the_disrupt = true; + let the_last; + function is_dup(thing) { + return is_equal(thing, exp); + } + check_not_top_level(the_switch); + if (functionage.finally > 0) { + +// test_cause: +// [" +// function aa(){try{}finally{switch(0){}}} +// ", "stmt_switch", "unexpected_a", "switch", 28] + + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token_now.free = true; + the_switch.expression = parse_expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + while (true) { + +// Loop through cases with breaks. + + the_case = token_nxt; + the_case.arity = "statement"; + the_case.expression = []; + while (true) { + +// Loop through fallthrough cases. + + advance("case"); + token_now.switch = true; + exp = parse_expression(0); + if (dups.some(is_dup)) { + +// test_cause: +// [" +// switch(0){case 0:break;case 0:break} +// ", "stmt_switch", "unexpected_a", "0", 29] + + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (token_nxt.id !== "case") { + break; + } + } + +// test_cause: +// [" +// switch(0){case 1:case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 23] +// [" +// switch(0){case "aa":case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 26] +// [" +// switch(0){case "bb":case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 26] +// [" +// switch(0){case aa:case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24] +// [" +// switch(0){case bb:case aa:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24] + + check_ordered_case(the_case.expression); + stmts = parse_statements(); + if (stmts.length < 1) { + +// test_cause: +// [" +// switch(0){case 0:default:} +// ", "stmt_switch", "expected_statements_a", "default", 18] +// ["switch(0){case 0:}", "stmt_switch", "expected_statements_a", "}", 18] + + warn("expected_statements_a"); + +// PR-359 - Bugfix - Fixes issue #358 - switch-statement crashes jslint. + + break; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn("expected_a_before_b", token_nxt, "break;", artifact()); + } + if (token_nxt.id !== "case") { + break; + } + } + +// test_cause: +// [" +// switch(0){case 1:break;case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 29] +// [" +// switch(0){case "aa":break;case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 32] +// [" +// switch(0){case "bb":break;case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 32] +// [" +// switch(0){case aa:break;case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30] +// [" +// switch(0){case bb:break;case aa:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30] + + check_ordered_case(the_cases.map(function ({ + expression + }) { + return expression[0]; + })); + dups = undefined; + if (token_nxt.id === "default") { + the_default = token_nxt; + advance("default"); + token_now.switch = true; + advance(":"); + the_switch.else = parse_statements(); + if (the_switch.else.length < 1) { + +// test_cause: +// [" +// switch(0){case 0:break;default:} +// ", "stmt_switch", "unexpected_a", "default", 24] + + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + the_last = the_switch.else[ + the_switch.else.length - 1 + ]; + if ( + the_last.id === "break" + && the_last.label === undefined + ) { + +// test_cause: +// [" +// switch(0){case 0:break;default:break;} +// ", "stmt_switch", "unexpected_a", "break", 32] + + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; + } + + function stmt_throw() { + const the_throw = token_now; + the_throw.disrupt = true; + the_throw.expression = parse_expression(10); + semicolon(); + if (functionage.try > 0) { + +// test_cause: +// ["try{throw 0}catch(){}", "stmt_throw", "unexpected_a", "throw", 5] + + warn("unexpected_a", the_throw); + } + return the_throw; + } + + function stmt_try() { + const the_try = token_now; + let ignored; + let the_catch; + let the_disrupt; + if (functionage.try > 0) { + +// test_cause: +// ["try{try{}catch(){}}catch(){}", "stmt_try", "unexpected_a", "try", 5] + + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (token_nxt.id === "catch") { + ignored = "ignore"; + the_catch = token_nxt; + the_try.catch = the_catch; + advance("catch"); + +// Create new catch-scope for catch-parameter. + + catch_stack.push(catchage); + catchage = the_catch; + catch_list.push(catchage); + the_catch.context = empty(); + if (token_nxt.id === "(") { + advance("("); + if (!token_nxt.identifier) { + +// test_cause: +// ["try{}catch(){}", "stmt_try", "expected_identifier_a", ")", 12] + + return stop("expected_identifier_a"); + } + if (token_nxt.id !== "ignore") { + ignored = undefined; + the_catch.name = token_nxt; + enroll(token_nxt, "exception", true); + } + advance(); + advance(")"); + } + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + +// Restore previous catch-scope after catch-block. + + catchage = catch_stack.pop(); + +// PR-404 - Relax warning about missing `catch` in `try...finally` statement. +// +// } else { +// +// // test_cause: +// // ["try{}finally{break;}", "stmt_try", "expected_a_before_b", "finally", 6] +// +// warn("expected_a_before_b", token_nxt, "catch", artifact()); + + } + if (token_nxt.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; + } + + function stmt_var() { + let ellipsis; + let mode_const; + let name; + let the_brace; + let the_bracket; + let the_variable = token_now; + let variable_prv; + mode_const = the_variable.id === "const"; + the_variable.names = []; + +// A program may use var or let, but not both. + + if (!mode_const) { + if (mode_var === undefined) { + mode_var = the_variable.id; + } else if (the_variable.id !== mode_var) { + +// test_cause: +// ["let aa;var aa", "stmt_var", "expected_a_b", "var", 8] + + warn("expected_a_b", the_variable, mode_var, the_variable.id); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + +// test_cause: +// ["switch(0){case 0:var aa}", "stmt_var", "var_switch", "var", 18] + + warn("var_switch", the_variable); + } + switch ( + Boolean(functionage.statement_prv) + && functionage.statement_prv.id + ) { + case "const": + case "let": + case "var": + +// test_cause: +// ["const aa=0;const bb=0;", "stmt_var", "var_prv", "const", 0] +// ["let aa=0;let bb=0;", "stmt_var", "var_prv", "let", 0] +// ["var aa=0;var bb=0;", "stmt_var", "var_prv", "var", 0] + + test_cause("var_prv", functionage.statement_prv.id); + variable_prv = functionage.statement_prv; + break; + case "import": + +// test_cause: +// ["import aa from \"aa\";\nlet bb=0;", "stmt_var", "import_prv", "", 0] + + test_cause("import_prv"); + break; + case false: + break; + default: + if ( + (option_dict.beta && !option_dict.variable) + || the_variable.id === "var" + ) { + +// test_cause: +// ["console.log();let aa=0;", "stmt_var", "var_on_top", "let", 15] +// ["console.log();var aa=0;", "stmt_var", "var_on_top", "var", 15] +// ["try{aa();}catch(aa){var aa=0;}", "stmt_var", "var_on_top", "var", 21] +// ["while(0){var aa;}", "stmt_var", "var_on_top", "var", 10] + + warn("var_on_top", token_now); + } + } + while (true) { + if (token_nxt.id === "{") { + if (the_variable.id === "var") { + +// test_cause: +// ["var{aa}=0", "stmt_var", "unexpected_a", "var", 1] + + warn("unexpected_a", the_variable); + } + the_brace = token_nxt; + advance("{"); + while (true) { + name = token_nxt; + if (!name.identifier) { + +// test_cause: +// ["let {0}", "stmt_var", "expected_identifier_a", "0", 6] + + return stop("expected_identifier_a"); + } + survey(name); + advance(); + if (token_nxt.id === ":") { + advance(":"); + if (!token_nxt.identifier) { + +// test_cause: +// ["let {aa:0}", "stmt_var", "expected_identifier_a", "0", 9] +// ["let {aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 9] + + return stop("expected_identifier_a"); + } + +// PR-363 - Bugfix +// Add test against false-warning in code +// '/*jslint node*/\nlet {aa:bb} = {}; bb();'. +// +// token_nxt.label = name; +// the_variable.names.push(token_nxt); +// enroll(token_nxt, "variable", mode_const); + + name = token_nxt; + the_variable.names.push(name); + survey(name); + enroll(name, "variable", mode_const); + + advance(); + the_brace.open = true; + } else { + the_variable.names.push(name); + enroll(name, "variable", mode_const); + } + name.dead = false; + name.init = true; + if (token_nxt.id === "=") { + +// test_cause: +// ["let {aa=0}", "stmt_var", "assign", "", 0] + + test_cause("assign"); + advance("="); + name.expression = parse_expression(); + the_brace.open = true; + } + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + +// test_cause: +// ["let{bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8] + + check_ordered(the_variable.id, the_variable.names); + advance("}"); + advance("="); + the_variable.expression = parse_expression(0); + } else if (token_nxt.id === "[") { + if (the_variable.id === "var") { + +// test_cause: +// ["var[aa]=0", "stmt_var", "unexpected_a", "var", 1] + + warn("unexpected_a", the_variable); + } + the_bracket = token_nxt; + advance("["); + while (true) { + ellipsis = false; + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + if (!token_nxt.identifier) { + +// test_cause: +// ["let[]", "stmt_var", "expected_identifier_a", "]", 5] + + return stop("expected_identifier_a"); + } + name = token_nxt; + advance(); + the_variable.names.push(name); + enroll(name, "variable", mode_const); + name.dead = false; + name.init = true; + if (ellipsis) { + name.ellipsis = true; + break; + } + if (token_nxt.id === "=") { + advance("="); + name.expression = parse_expression(); + the_bracket.open = true; + } + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + advance("]"); + advance("="); + the_variable.expression = parse_expression(0); + } else if (token_nxt.identifier) { + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// [" +// let ignore;function aa(ignore) {} +// ", "stmt_var", "unexpected_a", "ignore", 5] + + warn("unexpected_a", name); + } + enroll(name, "variable", mode_const); + if (token_nxt.id === "=" || mode_const) { + advance("="); + name.dead = false; + name.init = true; + name.expression = parse_expression(0); + } + the_variable.names.push(name); + } else { + +// test_cause: +// ["let 0", "stmt_var", "expected_identifier_a", "0", 5] +// ["var{aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 8] + + return stop("expected_identifier_a"); + } + if (token_nxt.id !== ",") { + break; + } + +// test_cause: +// ["let aa,bb;", "stmt_var", "expected_a_b", ",", 7] + + warn("expected_a_b", token_nxt, ";", ","); + advance(","); + } + +// Warn if variable declarations are unordered. + + if ( + option_dict.beta + && !option_dict.unordered + && !option_dict.variable + && variable_prv + && ( + variable_prv.id + " " + variable_prv.names[0].id + > the_variable.id + " " + the_variable.names[0].id + ) + ) { + +// test_cause: +// ["const bb=0;const aa=0;", "stmt_var", "expected_a_b_before_c_d", "aa", 12] +// ["let bb;let aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8] +// ["var bb;var aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8] + + warn( + "expected_a_b_before_c_d", + the_variable, + the_variable.id, + the_variable.names[0].id, + variable_prv.id, + variable_prv.names[0].id + ); + } + semicolon(); + return the_variable; + } + + function stmt_while() { + const the_while = token_now; + check_not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + +// test_cause: +// ["function aa(){while(0){break;}}", "stmt_while", "weird_loop", "while", 15] + + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; + } + + function stmt_with() { + +// test_cause: +// ["with", "stmt_with", "unexpected_a", "with", 1] + + stop("unexpected_a", token_now); + } + + function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!jslint_rgx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!jslint_rgx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + +// test_cause: +// ["let aa={0:0}", "survey", "expected_identifier_a", "0", 9] + + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property_dict[id] === "number") { + property_dict[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (state.mode_property) { + if (tenure[id] !== true) { + +// test_cause: +// ["/*property aa*/\naa.bb", "survey", "unregistered_property_a", "bb", 4] + + warn("unregistered_property_a", name); + } + } else if ( + !option_dict.nomen + && name.identifier + && jslint_rgx_weird_property.test(id) + ) { + +// test_cause: +// ["aa.$", "survey", "weird_property_a", "$", 4] +// ["aa._", "survey", "weird_property_a", "_", 4] +// ["aa._aa", "survey", "weird_property_a", "_aa", 4] +// ["aa.aaSync", "survey", "weird_property_a", "aaSync", 4] +// ["aa.aa_", "survey", "weird_property_a", "aa_", 4] + + warn("weird_property_a", name); + } + property_dict[id] = 1; + } + return id; + } + +// These functions are used to specify the grammar of our language: + + function symbol(id, bp) { + +// Create a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax_dict[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax_dict[id] = the_symbol; + } + return the_symbol; + } + + function ternary(id1, id2) { + +// Create a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led_infix = function parse_ternary_led(left) { + const the_token = token_now; + let second; + second = parse_expression(20); + advance(id2); + token_now.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, parse_expression(10)]; + if (token_nxt.id !== ")") { + +// test_cause: +// ["0?0:0", "parse_ternary_led", "use_open", "?", 2] + + warn("use_open", the_token); + } + return the_token; + }; + return the_symbol; + } + +// Now we parse JavaScript. +// Begin defining the language. + + assignment("%="); + assignment("&="); + assignment("*="); + assignment("+="); + assignment("-="); + assignment("/="); + assignment("<<="); + assignment("="); + assignment(">>="); + assignment(">>>="); + assignment("^="); + assignment("|="); + constant("(number)", "number"); + constant("(regexp)", "regexp"); + constant("(string)", "string"); + constant("Function", "function", constant_Function); + constant("Infinity", "number", Infinity); + constant("NaN", "number", NaN); + constant("arguments", "object", constant_arguments); + constant("eval", "function", constant_eval); + constant("false", "boolean", false); + constant("ignore", "undefined", constant_ignore); + constant("isFinite", "function", constant_isInfinite); + constant("isNaN", "function", constant_isNaN); + constant("null", "null", null); + constant("this", "object", constant_this); + constant("true", "boolean", true); + constant("undefined", "undefined"); + infix(100, "!="); + infix(100, "!=="); + infix(100, "=="); + infix(100, "==="); + infix(110, "<"); + infix(110, "<="); + infix(110, ">"); + infix(110, ">="); + infix(110, "in"); + infix(110, "instanceof"); + infix(120, "<<"); + infix(120, ">>"); + infix(120, ">>>"); + infix(130, "+"); + infix(130, "-"); + infix(140, "%"); + infix(140, "*"); + infix(140, "/"); + infix(160, "(", infix_lparen); + infix(160, "`", infix_grave); + infix(170, ".", infix_dot); + infix(170, "=>", infix_fart_unwrapped); + infix(170, "?.", infix_option_chain); + infix(170, "[", infix_lbracket); + infix(35, "??"); + infix(40, "||"); + infix(50, "&&"); + infix(70, "|"); + infix(80, "^"); + infix(90, "&"); + infixr(150, "**"); + postassign("++"); + postassign("--"); + preassign("++"); + preassign("--"); + prefix("!!"); + prefix("!"); + prefix("(", prefix_lparen); + prefix("+"); + prefix("-"); + prefix("/=", prefix_assign_divide); + prefix("=>", prefix_fart); + prefix("[", prefix_lbracket); + prefix("`", prefix_tick); + prefix("async", prefix_async); + prefix("await", prefix_await); + prefix("function", prefix_function); + prefix("new", prefix_new); + prefix("typeof"); + prefix("void", prefix_void); + prefix("{", prefix_lbrace); + prefix("~"); + stmt(";", stmt_semicolon); + stmt("async", prefix_async); + stmt("await", prefix_await); + stmt("break", stmt_break); + stmt("const", stmt_var); + stmt("continue", stmt_continue); + stmt("debugger", stmt_debugger); + stmt("delete", stmt_delete); + stmt("do", stmt_do); + stmt("export", stmt_export); + stmt("for", stmt_for); + stmt("function", prefix_function); + stmt("if", stmt_if); + stmt("import", stmt_import); + stmt("let", stmt_var); + stmt("return", stmt_return); + stmt("switch", stmt_switch); + stmt("throw", stmt_throw); + stmt("try", stmt_try); + stmt("var", stmt_var); + stmt("while", stmt_while); + stmt("with", stmt_with); + stmt("{", stmt_lbrace); + symbol(")"); + symbol("*/"); + symbol(","); + symbol(":"); + symbol(";"); + symbol("]"); + symbol("async"); + symbol("await"); + symbol("case"); + symbol("catch"); + symbol("class"); + symbol("default"); + symbol("else"); + symbol("enum"); + symbol("finally"); + symbol("implements"); + symbol("interface"); + symbol("package"); + symbol("private"); + symbol("protected"); + symbol("public"); + symbol("static"); + symbol("super"); + symbol("void"); + symbol("yield"); + symbol("}"); + ternary("?", ":"); + +// Init token_nxt. + + advance(); + +// Parsing of JSON is simple: + + if (state.mode_json) { + state.token_tree = parse_json(); + advance("(end)"); + return; + } + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option_dict.browser) { + if (token_nxt.id === ";") { + advance(";"); + } + +// If we are not in a browser, then the file form of strict pragma may be used. + + } else if (token_nxt.value === "use strict") { + advance("(string)"); + advance(";"); + } + state.token_tree = parse_statements(); + advance("(end)"); + +// Check global functions are ordered. + + check_ordered( + "function", + function_list.map(function ({ + level, + name + }) { + return (level === 1) && name; + }).filter(function (name) { + return option_dict.beta && name && name.id; + }) + ); +} + +function jslint_phase4_walk(state) { + +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). + + let { + artifact, + catch_stack, + function_stack, + global_dict, + is_equal, + is_weird, + option_dict, + syntax_dict, + test_cause, + token_global, + warn + } = state; + let block_stack = []; // The stack of blocks. + let blockage = token_global; // The current block. + let catchage = catch_stack[0]; // The current catch-block. + let functionage = token_global; // The current function. + let postaction; + let postamble; + let posts = empty(); + let preaction; + let preamble; + let pres = empty(); + +// The relational operators. + + let relationop = object_assign_from_list(empty(), [ + "!=", "!==", "<", "<=", "==", "===", ">", ">=" + ], true); + +// Ambulation of the parse tree. + + function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; + } + + function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + task(the_token); + }); + } + } + }; + } + + function init_variable(name) { + let the_variable = lookup(name); + if (!the_variable || the_variable.readonly) { + warn("bad_assignment_a", name); + return; + } + the_variable.init = true; + } + + function lookup(thing) { + let id = thing.id; + let the_variable; + if (thing.arity !== "variable") { + return; + } + +// Look up the variable in the current context. + + the_variable = functionage.context[id] || catchage.context[id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable && the_variable.role === "label") { + +// test_cause: +// ["aa:while(0){aa;}", "lookup", "label_a", "aa", 13] + + warn("label_a", thing); + return the_variable; + } + if (!the_variable) { + function_stack.forEach(function ({ + context + }) { + if (context[id] && context[id].role !== "label") { + the_variable = context[id]; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (!the_variable && global_dict[id] === undefined) { + +// test_cause: +// ["aa", "lookup", "undeclared_a", "aa", 1] +// ["class aa{}", "lookup", "undeclared_a", "aa", 7] +// [" +// let aa=0;try{aa();}catch(bb){bb();}bb(); +// ", "lookup", "undeclared_a", "bb", 36] +// [" +// let aa=0;try{aa();}catch(ignore){bb();} +// ", "lookup", "undeclared_a", "bb", 34] + + warn("undeclared_a", thing); + return; + } + if (!the_variable) { + the_variable = { + dead: false, + id, + init: true, + parent: token_global, + readonly: true, + role: "variable", + used: 0 + }; + token_global.context[id] = the_variable; + } + the_variable.closure = true; + functionage.context[id] = the_variable; + } + if ( + ( + the_variable.calls === undefined + || functionage.name === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + && the_variable.dead + ) { + +// test_cause: +// ["let aa;if(aa){let bb;}bb;", "lookup", "out_of_scope_a", "bb", 23] + + warn("out_of_scope_a", thing); + } + return the_variable; + } + + function post_a(thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + let right; + if (thing.id === "=") { + if (thing.names !== undefined) { + +// test_cause: +// ["if(0){aa=0}", "post_a", "=", "", 0] + + test_cause("="); + +// Probably deadcode. +// if (Array.isArray(thing.names)) { +// thing.names.forEach(init_variable); +// } else { +// init_variable(thing.names); +// } + + jslint_assert( + !Array.isArray(thing.names), + `Expected !Array.isArray(thing.names).` + ); + init_variable(thing.names); + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + +// test_cause: +// ["aa.aa=undefined", "post_a", "expected_a_b", "undefined", 1] + + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.readonly) { + warn("bad_assignment_a", lvalue); + } + } + right = syntax_dict[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + +// test_cause: +// ["aa+=undefined", "post_a", "unexpected_a", "undefined", 5] + + warn("unexpected_a", thing.expression[1]); + } + } + } + + function post_a_pluseq(thing) { + const right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } + } + + function post_b(thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || is_equal(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + +// test_cause: +// ["if(0===0){0}", "post_b", "weird_relation_a", "===", 5] + + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option_dict.convert) { + if (thing.expression[0].value === "") { + +// test_cause: +// ["\"\"+0", "post_b", "expected_a_b", "\"\" +", 3] + + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + +// test_cause: +// ["0+\"\"", "post_b", "expected_a_b", "+ \"\"", 2] + + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + +// test_cause: +// ["aa=window[0]", "post_b", "weird_expression_a", "window[...]", 10] + + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + +// test_cause: +// ["aa=self[0]", "post_b", "weird_expression_a", "self[...]", 8] + + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === "." || thing.id === "?.") { + if (thing.expression.id === "RegExp") { + +// test_cause: +// ["aa=RegExp.aa", "post_b", "weird_expression_a", ".", 10] + + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + +// test_cause: +// ["0- -0", "post_b", "wrap_unary", "-", 4] + + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } + } + + function post_b_and(thing) { + if ( + is_weird(thing.expression[0]) + || is_equal(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + +// test_cause: +// ["aa=(()=>0)&&(()=>0)", "post_b_and", "weird_condition_a", "&&", 11] +// ["aa=(``?``:``)&&(``?``:``)", "post_b_and", "weird_condition_a", "&&", 14] +// ["aa=/./&&/./", "post_b_and", "weird_condition_a", "&&", 7] +// ["aa=0&&0", "post_b_and", "weird_condition_a", "&&", 5] +// ["aa=[]&&[]", "post_b_and", "weird_condition_a", "&&", 6] +// ["aa=`${0}`&&`${0}`", "post_b_and", "weird_condition_a", "&&", 10] +// [" +// aa=function aa(){}&&function aa(){} +// ", "post_b_and", "weird_condition_a", "&&", 19] +// ["aa={}&&{}", "post_b_and", "weird_condition_a", "&&", 6] + + warn("weird_condition_a", thing); + } + } + + function post_b_lbracket(thing) { + if (thing.expression[0].id === "RegExp") { + +// test_cause: +// ["aa=RegExp[0]", "post_b_lbracket", "weird_expression_a", "[", 10] + + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + +// test_cause: +// ["aa[[0]]", "post_b_lbracket", "weird_expression_a", "[", 4] + + warn("weird_expression_a", thing.expression[1]); + } + } + + function post_b_lparen(thing) { + let arg; + let array; + let cack; + let left = thing.expression[0]; + let new_date; + let paren; + let the_new; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + +// test_cause: +// ["aa=function(){}()", "post_b_lparen", "wrap_immediate", "(", 16] + + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "BigInt" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + +// test_cause: +// ["new BigInt()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Boolean()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Number()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new String()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Symbol()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new aa()", "post_b_lparen", "unexpected_a", "new", 1] + + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option_dict.eval) { + +// test_cause: +// ["new Function()", "post_b_lparen", "unexpected_a", "new Function", 5] + + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + +// test_cause: +// ["new Array()", "post_b_lparen", "expected_a_b", "new Array", 5] + + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + +// test_cause: +// ["new Object()", "post_b_lparen", "expected_a_b", "new Object", 5] + + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "BigInt" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + +// test_cause: +// ["let Aa=Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8] + + warn("expected_a_before_b", left, "new", artifact(left)); + } + } + } else if (left.id === ".") { + cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + +// test_cause: +// ["new Date.UTC()", "post_b_lparen", "cack", "", 0] + + test_cause("cack"); + cack = !cack; + } + if (jslint_rgx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + +// test_cause: +// ["new Date.UTC()", "post_b_lparen", "unexpected_a", "new", 1] + + warn("unexpected_a", the_new); + } else { + +// test_cause: +// ["let Aa=Aa.Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8] + + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + paren = left.expression; + if (paren.id === "(") { + array = paren.expression; + if (array.length === 1) { + new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + +// test_cause: +// [" +// new Date().getTime() +// ", "post_b_lparen", "expected_a_b", "new Date().getTime()", 1] + + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } + } + + function post_b_or(thing) { + if ( + is_weird(thing.expression[0]) + || is_equal(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + +// test_cause: +// ["aa=0||0", "post_b_or", "weird_condition_a", "||", 5] + + warn("weird_condition_a", thing); + } + } + + function post_s_export(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== token_global) { + +// test_cause: +// [" +// if(0){import aa from "aa";} +// ", "post_s_export", "misplaced_a", "import", 7] + + warn("misplaced_a", the_thing); + } + } + + function post_s_for(thing) { + +// Recurse walk_statement(). + + walk_statement(thing.inc); + } + + function post_s_function(thing) { + delete functionage.async; + delete functionage.finally; + delete functionage.loop; + delete functionage.statement_prv; + delete functionage.switch; + delete functionage.try; + functionage = function_stack.pop(); + if (thing.wrapped) { + +// test_cause: +// ["aa=(function(){})", "post_s_function", "unexpected_parens", "function", 5] + + warn("unexpected_parens", thing); + } + return post_s_lbrace(); + } + + function post_s_import(the_thing) { + const name = the_thing.name; + if (name) { + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return post_s_export(the_thing); + } + } + + function post_s_lbrace() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); + } + + function post_s_try(thing) { + if (thing.catch) { + if (thing.catch.name) { + Object.assign(catchage.context[thing.catch.name.id], { + dead: false, + init: true + }); + } + +// Recurse walk_statement(). + + walk_statement(thing.catch.block); + +// Restore previous catch-scope after catch-block. + + catchage = catch_stack.pop(); + } + } + + function post_s_var(thing) { + thing.names.forEach(function (name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + +// Probably deadcode. +// if (name.id === "{" || name.id === "[") { +// name.names.forEach(subactivate); +// } else { +// name.init = true; +// } + + jslint_assert( + !(name.id === "{" || name.id === "["), + `Expected !(name.id === "{" || name.id === "[").` + ); + name.init = true; + } + blockage.live.push(name); + }); + } + + function post_t(thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || is_equal(thing.expression[1], thing.expression[2]) + ) { + +// test_cause: +// ["let aa=(aa?`${0}`:`${0}`);", "post_t", "unexpected_a", "?", 11] +// ["let aa=(aa?`0`:`0`);", "post_t", "unexpected_a", "?", 11] + + warn("unexpected_a", thing); + } else if (is_equal(thing.expression[0], thing.expression[1])) { + +// test_cause: +// ["aa?aa:0", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "||", "?"); + } else if (is_equal(thing.expression[0], thing.expression[2])) { + +// test_cause: +// ["aa?0:aa", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + +// test_cause: +// ["aa?true:false", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + +// test_cause: +// ["aa?false:true", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + +// test_cause: +// ["(aa&&!aa?0:1)", "post_t", "wrap_condition", "&&", 4] + + warn("wrap_condition", thing.expression[0]); + } + } + + function post_u(thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option_dict.convert) { + +// test_cause: +// ["!!0", "post_u", "expected_a_b", "!!", 1] + + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } + } + + function post_u_plus(thing) { + const right = thing.expression; + if (!option_dict.convert) { + +// test_cause: +// ["aa=+0", "post_u_plus", "expected_a_b", "+", 4] + + warn("expected_a_b", thing, "Number(...)", "+"); + } + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } + } + + function pre_a_bitwise(thing) { + +// These are the bitwise operators. + + switch (!option_dict.bitwise && thing.id) { + case "&": + case "&=": + case "<<": + case "<<=": + case ">>": + case ">>=": + case ">>>": + case ">>>=": + case "^": + case "^=": + case "|": + case "|=": + case "~": + +// test_cause: +// ["0&0", "pre_a_bitwise", "unexpected_a", "&", 2] +// ["0&=0", "pre_a_bitwise", "unexpected_a", "&=", 2] +// ["0<<0", "pre_a_bitwise", "unexpected_a", "<<", 2] +// ["0<<=0", "pre_a_bitwise", "unexpected_a", "<<=", 2] +// ["0>>0", "pre_a_bitwise", "unexpected_a", ">>", 2] +// ["0>>=0", "pre_a_bitwise", "unexpected_a", ">>=", 2] +// ["0>>>0", "pre_a_bitwise", "unexpected_a", ">>>", 2] +// ["0>>>=0", "pre_a_bitwise", "unexpected_a", ">>>=", 2] +// ["0^0", "pre_a_bitwise", "unexpected_a", "^", 2] +// ["0^=0", "pre_a_bitwise", "unexpected_a", "^=", 2] +// ["0|0", "pre_a_bitwise", "unexpected_a", "|", 2] +// ["0|=0", "pre_a_bitwise", "unexpected_a", "|=", 2] +// ["~0", "pre_a_bitwise", "unexpected_a", "~", 1] + + warn("unexpected_a", thing); + break; + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + +// test_cause: +// ["0<0<0", "pre_a_bitwise", "unexpected_a", "<", 4] + + warn("unexpected_a", thing); + } + } + + function pre_b(thing) { + let left; + let right; + let value; + if (relationop[thing.id] === true) { + left = thing.expression[0]; + right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + +// test_cause: +// ["NaN===NaN", "pre_b", "number_isNaN", "===", 4] + + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + +// test_cause: +// ["typeof 0===0", "pre_b", "expected_string_a", "0", 12] + + warn("expected_string_a", right); + } + } else { + value = right.value; + if (value === "null" || value === "undefined") { + +// test_cause: +// [" +// typeof aa==="undefined" +// ", "pre_b", "unexpected_typeof_a", "undefined", 13] + + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "bigint" + && value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + +// test_cause: +// ["typeof 0===\"aa\"", "pre_b", "expected_type_string_a", "aa", 12] + + warn("expected_type_string_a", right, value); + } + } + } + } + } + + function pre_b_eqeq(thing) { + +// test_cause: +// ["0==0", "pre_b_eqeq", "expected_a_b", "==", 2] + + warn("expected_a_b", thing, "===", "=="); + } + + function pre_b_in(thing) { + +// test_cause: +// ["aa in aa", "pre_b_in", "infix_in", "in", 4] + + warn("infix_in", thing); + } + + function pre_b_instanceof(thing) { + +// test_cause: +// ["0 instanceof 0", "pre_b_instanceof", "unexpected_a", "instanceof", 3] + + warn("unexpected_a", thing); + } + + function pre_b_lparen(thing) { + const left = thing.expression[0]; + let left_variable; + let parent; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + parent = functionage.name.parent; + if (parent) { + left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + +// Probably deadcode. +// && left_variable.dead + + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } + } + + function pre_b_noteq(thing) { + +// test_cause: +// ["0!=0", "pre_b_noteq", "expected_a_b", "!=", 2] + + warn("expected_a_b", thing, "!==", "!="); + } + + function pre_b_or(thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + +// test_cause: +// ["0&&0||0", "pre_b_or", "and", "&&", 2] + + warn("and", thang); + } + }); + } + + function pre_s_for(thing) { + let the_variable; + if (thing.name !== undefined) { + thing.name.dead = false; + the_variable = lookup(thing.name); + if (the_variable !== undefined) { + if (the_variable.init && the_variable.readonly) { + +// test_cause: +// ["const aa=0;for(aa in aa){}", "pre_s_for", "bad_assignment_a", "aa", 16] + + warn("bad_assignment_a", thing.name); + } + the_variable.init = true; + } + } + +// Recurse walk_statement(). + + walk_statement(thing.initial); + } + + function pre_s_function(thing) { + +// test_cause: +// ["()=>0", "pre_s_function", "", "", 0] +// ["(function (){}())", "pre_s_function", "", "", 0] +// ["function aa(){}", "pre_s_function", "", "", 0] + + test_cause(""); + if (thing.arity === "statement" && blockage.body !== true) { + +// test_cause: +// ["if(0){function aa(){}\n}", "pre_s_function", "unexpected_a", "function", 7] + + warn("unexpected_a", thing); + } + function_stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + +// test_cause: +// [" +// /*jslint getset*/ +// aa={get aa(aa){}} +// ", "pre_s_function", "bad_get", "function", 9] + + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + +// test_cause: +// [" +// /*jslint getset*/ +// aa={set aa(){}} +// ", "pre_s_function", "bad_set", "function", 9] + + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); + } + + function pre_s_lbrace(thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; + } + + function pre_try(thing) { + if (thing.catch !== undefined) { + +// Create new catch-scope for catch-parameter. + + catch_stack.push(catchage); + catchage = thing.catch; + } + } + + function pre_v(thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } + } + + function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); + } + + function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + +// test_cause: +// ["(function(){}())", "walk_expression", "isArray", "", 0] +// ["0&&0", "walk_expression", "isArray", "", 0] + + test_cause("isArray"); + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + +// PR-414 - Bugfix - fix fart-body not being walked. + + if (thing.id === "function" || thing.id === "=>") { + +// test_cause: +// ["aa=()=>0", "walk_expression", "function", "=>", 0] +// ["aa=function(){}", "walk_expression", "function", "function", 0] + + test_cause("function", thing.id); + +// Recurse walk_statement(). + + walk_statement(thing.block); + } + if ( + thing.arity === "preassign" || thing.arity === "postassign" + ) { + +// test_cause: +// ["aa=++aa", "walk_expression", "unexpected_a", "++", 4] +// ["aa=--aa", "walk_expression", "unexpected_a", "--", 4] + + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + +// test_cause: +// ["aa[aa=0]", "walk_expression", "unexpected_statement_a", "=", 6] + + warn("unexpected_statement_a", thing); + } + +// test_cause: +// ["aa=0", "walk_expression", "default", "", 0] + + test_cause("default"); + postamble(thing); + } + } + } + + function walk_statement(thing) { + if (!thing) { + return; + } + if (Array.isArray(thing)) { + +// test_cause: +// ["+[]", "walk_statement", "isArray", "", 0] + + test_cause("isArray"); + +// Recurse walk_statement(). + + thing.forEach(walk_statement); + return; + } + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + +// test_cause: +// ["0&&0", "walk_statement", "unexpected_expression_a", "&&", 2] + + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + && thing.id !== "import" + ) { + +// test_cause: +// ["!0", "walk_statement", "unexpected_expression_a", "!", 1] +// ["+[]", "walk_statement", "unexpected_expression_a", "+", 1] +// ["+new aa()", "walk_statement", "unexpected_expression_a", "+", 1] +// ["0", "walk_statement", "unexpected_expression_a", "0", 1] +// ["typeof 0", "walk_statement", "unexpected_expression_a", "typeof", 1] + + warn("unexpected_expression_a", thing); + } + +// Recurse walk_statement(). + + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + + postaction = action(posts); + postamble = amble(posts); + preaction = action(pres); + preamble = amble(pres); + postaction("assignment", "+=", post_a_pluseq); + postaction("assignment", post_a); + postaction("binary", "&&", post_b_and); + postaction("binary", "(", post_b_lparen); + postaction("binary", "=>", post_s_function); + postaction("binary", "[", post_b_lbracket); + postaction("binary", "||", post_b_or); + postaction("binary", post_b); + postaction("statement", "const", post_s_var); + postaction("statement", "export", post_s_export); + postaction("statement", "for", post_s_for); + postaction("statement", "function", post_s_function); + postaction("statement", "import", post_s_import); + postaction("statement", "let", post_s_var); + postaction("statement", "try", post_s_try); + postaction("statement", "var", post_s_var); + postaction("statement", "{", post_s_lbrace); + postaction("ternary", post_t); + postaction("unary", "+", post_u_plus); + postaction("unary", "function", post_s_function); + postaction("unary", post_u); + preaction("assignment", pre_a_bitwise); + preaction("binary", "!=", pre_b_noteq); + preaction("binary", "(", pre_b_lparen); + preaction("binary", "==", pre_b_eqeq); + preaction("binary", "=>", pre_s_function); + preaction("binary", "in", pre_b_in); + preaction("binary", "instanceof", pre_b_instanceof); + preaction("binary", "||", pre_b_or); + preaction("binary", pre_b); + preaction("binary", pre_a_bitwise); + preaction("statement", "for", pre_s_for); + preaction("statement", "function", pre_s_function); + preaction("statement", "try", pre_try); + preaction("statement", "{", pre_s_lbrace); + preaction("unary", "function", pre_s_function); + preaction("unary", "~", pre_a_bitwise); + preaction("variable", pre_v); + + walk_statement(state.token_tree); +} + +function jslint_phase5_whitage(state) { + +// PHASE 5. Check whitespace between tokens in . + + let { + artifact, + catch_list, + function_list, + function_stack, + option_dict, + test_cause, + token_global, + token_list, + warn + } = state; + let closer = "(end)"; + let free = false; + +// free = false + +// cause: +// "()=>0" +// "aa()" +// "aa(0,0)" +// "function(){}" + +// free = true + +// cause: +// "(0)" +// "(aa)" +// "aa(0)" +// "do{}while()" +// "for(){}" +// "if(){}" +// "switch(){}" +// "while(){}" + + let left = token_global; + let margin = 0; + let mode_indent = ( + +// PR-330 - Allow 2-space indent. + + option_dict.indent2 + ? 2 + : 4 + ); + let nr_comments_skipped = 0; + let open = true; + let opening = true; + let right; + +// This is the set of infix operators that require a space on each side. + + let spaceop = object_assign_from_list(empty(), [ + "!=", "!==", "%", "%=", "&", "&&", "&=", "*", "*=", "+=", "-=", "/", + "/=", "<", "<<", "<<=", "<=", "=", "==", "===", "=>", ">", ">=", ">>", + ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" + ], true); + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + const name = the_function.context[id]; + if (id !== "ignore" && name.parent === the_function) { + +// test_cause: +// ["function aa(aa) {return aa;}", "delve", "id", "", 0] + + test_cause("id"); + if ( + name.used === 0 + +// Probably deadcode. +// && ( +// name.role !== "function" +// || name.parent.arity !== "unary" +// ) + + && jslint_assert( + name.role !== "function", + `Expected name.role !== "function".` + ) + ) { + +// test_cause: +// ["/*jslint node*/\nlet aa;", "delve", "unused_a", "aa", 5] +// ["function aa(aa){return;}", "delve", "unused_a", "aa", 13] +// ["let aa=0;try{aa();}catch(bb){aa();}", "delve", "unused_a", "bb", 26] + + warn("unused_a", name); + } else if (!name.init) { + +// test_cause: +// ["/*jslint node*/\nlet aa;aa();", "delve", "uninitialized_a", "aa", 5] + + warn("uninitialized_a", name); + } + } + }); + } + + function expected_at(at) { + +// Probably deadcode. +// if (right === undefined) { +// right = token_nxt; +// } + + jslint_assert( + !(right === undefined), + `Expected !(right === undefined).` + ); + warn( + "expected_a_at_b_c", + right, + artifact(right), + +// Fudge column numbers in warning message. + + at + jslint_fudge, + right.from + jslint_fudge + ); + } + + function no_space() { + if (left.line === right.line) { + +// from: +// if (left.line === right.line) { +// no_space(); +// } else { + + if (left.thru !== right.from && nr_comments_skipped === 0) { + +// test_cause: +// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14] + + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + +// from: +// } else if ( +// right.arity === "binary" +// && right.id === "(" +// && free +// ) { +// no_space(); +// } else if ( + +// Probably deadcode. +// if (open) { +// const at = ( +// free +// ? margin +// : margin + 8 +// ); +// if (right.from < at) { +// expected_at(at); +// } +// } else { +// if (right.from !== margin + 8) { +// expected_at(margin + 8); +// } +// } + + jslint_assert(open, `Expected open.`); + jslint_assert(free, `Expected free.`); + if (right.from < margin) { + +// test_cause: +// ["let aa = aa(\naa\n()\n);", "expected_at", "expected_a_at_b_c", "5", 1] + + expected_at(margin); + } + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line || !open) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (right.from !== margin) { + expected_at(margin); + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn("expected_space_a_b", right, artifact(left), artifact(right)); + } + } + + function pop() { + const previous = function_stack.pop(); + closer = previous.closer; + free = previous.free; + margin = previous.margin; + open = previous.open; + opening = previous.opening; + } + + function push() { + function_stack.push({ + closer, + free, + margin, + open, + opening + }); + } + +// uninitialized_and_unused(); +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (state.mode_module === true || option_dict.node) { + delve(token_global); + } + catch_list.forEach(delve); + function_list.forEach(delve); + + if (option_dict.white) { + return; + } + +// whitage(); +// Go through the token list, looking at usage of whitespace. + + token_list.forEach(function whitage(the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + +// The open and close pairs. + + switch (left.id) { + case "${": + case "(": + case "[": + case "{": + +// test_cause: +// ["let aa=[];", "whitage", "opener", "", 0] +// ["let aa=`${0}`;", "whitage", "opener", "", 0] +// ["let aa=aa();", "whitage", "opener", "", 0] +// ["let aa={};", "whitage", "opener", "", 0] + + test_cause("opener"); + +// Probably deadcode. +// case "${}": + + jslint_assert( + !(left.id + right.id === "${}"), + "Expected !(left.id + right.id === \"${}\")." + ); + switch (left.id + right.id) { + case "()": + case "[]": + case "{}": + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like '{]') have already been detected. + +// test_cause: +// ["let aa=[];", "whitage", "opener_closer", "", 0] +// ["let aa=aa();", "whitage", "opener_closer", "", 0] +// ["let aa={};", "whitage", "opener_closer", "", 0] + + test_cause("opener_closer"); + if (left.line === right.line) { + +// test_cause: +// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14] + + no_space(); + } else { + +// test_cause: +// ["let aa = aa(\n );", "expected_at", "expected_a_at_b_c", "1", 2] + + at_margin(0); + } + break; + default: + +// test_cause: +// ["let aa=(0);", "whitage", "opener_operand", "", 0] +// ["let aa=[0];", "whitage", "opener_operand", "", 0] +// ["let aa=`${0}`;", "whitage", "opener_operand", "", 0] +// ["let aa=aa(0);", "whitage", "opener_operand", "", 0] +// ["let aa={aa:0};", "whitage", "opener_operand", "", 0] + + test_cause("opener_operand"); + opening = left.open || (left.line !== right.line); + push(); + switch (left.id) { + case "${": + closer = "}"; + break; + case "(": + closer = ")"; + break; + case "[": + closer = "]"; + break; + case "{": + closer = "}"; + break; + } + if (opening) { + +// test_cause: +// ["function aa(){\nreturn;\n}", "whitage", "opening", "", 0] +// ["let aa=(\n0\n);", "whitage", "opening", "", 0] +// ["let aa=[\n0\n];", "whitage", "opening", "", 0] +// ["let aa=`${\n0\n}`;", "whitage", "opening", "", 0] +// ["let aa={\naa:0\n};", "whitage", "opening", "", 0] + + test_cause("opening"); + free = closer === ")" && left.free; + open = true; + margin += mode_indent; + if (right.role === "label") { + if (right.from !== 0) { + +// test_cause: +// [" +// function aa() { +// bb: +// while (aa) { +// if (aa) { +// break bb; +// } +// } +// } +// ", "expected_at", "expected_a_at_b_c", "1", 2] + + expected_at(0); + } + } else if (right.switch) { + at_margin(-mode_indent); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + +// test_cause: +// [" +// function aa() {bb: +// while (aa) { +// aa(); +// } +// } +// ", "whitage", "expected_line_break_a_b", "bb", 16] + + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + +// test_cause: +// ["let aa=(0);", "whitage", "not_free", "", 0] +// ["let aa=[0];", "whitage", "not_free", "", 0] +// ["let aa=`${0}`;", "whitage", "not_free", "", 0] +// ["let aa={aa:0};", "whitage", "not_free", "", 0] + + test_cause("not_free"); + free = false; + open = false; + +// test_cause: +// ["let aa = ( 0 );", "no_space_only", "unexpected_space_a_b", "0", 12] + + no_space_only(); + } + } + break; + default: + if (right.statement === true) { + if (left.id === "else") { + +// test_cause: +// [" +// let aa = 0; +// if (aa) { +// aa(); +// } else if (aa) { +// aa(); +// } +// ", "one_space_only", "expected_space_a_b", "if", 9] + + one_space_only(); + } else { + +// test_cause: +// [" let aa = 0;", "expected_at", "expected_a_at_b_c", "1", 2] + + at_margin(0); + open = false; + } + +// If right is a closer, then pop the previous state. + + } else if (right.id === closer) { + pop(); + if (opening && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-mode_indent); + } else if (right.role === "label") { + if (right.from !== 0) { + +// test_cause: +// [" +// function aa() { +// aa();cc: +// while (aa) { +// if (aa) { +// break cc; +// } +// } +// } +// ", "expected_at", "expected_a_at_b_c", "1", 10] + + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + +// test_cause: +// ["let {aa,bb} = 0;", "one_space", "expected_space_a_b", "bb", 9] + + one_space(); + } else { + +// test_cause: +// [" +// function aa() { +// aa( +// 0,0 +// ); +// } +// ", "expected_at", "expected_a_at_b_c", "9", 11] + + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + +// test_cause: +// [" +// let aa = ( +// aa +// ? 0 +// : 1 +// ); +// ", "expected_at", "expected_a_at_b_c", "5", 1] + + at_margin(0); + } else { + +// test_cause: +// ["let aa = (aa ? 0 : 1);", "whitage", "use_open", "?", 14] + + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + +// test_cause: +// ["let aa = aa(\naa ()\n);", "no_space", "unexpected_space_a_b", "(", 4] + + no_space(); + } else if ( + left.id === "." + || left.id === "?." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + || (right.id === "." || right.id === "?.") + ) { + +// test_cause: +// ["let aa = 0 ;", "no_space_only", "unexpected_space_a_b", ";", 12] +// ["let aa = aa ?.aa;", "no_space_only", "unexpected_space_a_b", "?.", 13] + + no_space_only(); + } else if (left.id === ";") { + +// test_cause: +// [" +// /*jslint for*/ +// function aa() { +// for ( +// aa(); +// aa; +// aa() +// ) { +// aa(); +// } +// } +// ", "expected_at", "expected_a_at_b_c", "9", 1] + + if (open) { + at_margin(0); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || left.id === "await" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + +// test_cause: +// [" +// function aa() { +// do { +// aa(); +// } while(aa()); +// } +// ", "one_space_only", "expected_space_a_b", "(", 12] + + one_space_only(); + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || left.id === "async" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + +// test_cause: +// ["let aa=0;", "one_space", "expected_space_a_b", "0", 8] +// ["let aa={\naa:\n0\n};", "expected_at", "expected_a_at_b_c", "5", 1] + + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +function jslint_report({ + exports, + froms, + functions, + global, + json, + module, + property, + stop, + warnings +}) { + +// This function will create human-readable, html-report +// for warnings, properties, and functions from jslint-result-object. +// +// Example usage: +// let result = jslint("console.log('hello world')"); +// let html = jslint_report(result); + + let html = ""; + let length_80 = 1111; + + function address(line = 1, column = 1) { + +// This function will create HTML address element from and + + return `
    ${Number(line)}: ${Number(column)}
    `; + + } + + function detail(title, list) { + return ( + (Array.isArray(list) && list.length > 0) + ? ( + +// Google Lighthouse Accessibility -
    's do not contain only properly-ordered +//
    and
    groups, + + + + + + + + + + + + +

    CodeMirror: JSLint Demo

    +

    +This demo will auto-lint the code below, and auto-generate a report as you type. +

    + + + + + + + +
    + + + + + +``` +3. Live example at https://www.jslint.com/jslint_wrapper_codemirror.html + +[![screenshot](https://kaizhu256.github.io/jslint/branch-alpha/.artifact/screenshot_browser__2fjslint_2fbranch-alpha_2fjslint_wrapper_codemirror.html.png)](https://kaizhu256.github.io/jslint/jslint_wrapper_codemirror.html) + + +

    +# Quickstart JSLint in Vim +1. Download and save [`jslint.mjs`](https://www.jslint.com/jslint.mjs), [`jslint_wrapper_vim.vim`](https://www.jslint.com/jslint_wrapper_vim.vim) to directory `~/.vim/` +2. Add vim-command `:source ~/.vim/jslint_wrapper_vim.vim` to file `~/.vimrc` + - If above files were saved to custom-directory, then use that directory instead, e.g.: + - save [`jslint.mjs`](https://www.jslint.com/jslint.mjs), [`jslint_wrapper_vim.vim`](https://www.jslint.com/jslint_wrapper_vim.vim) to directory `~/vimfiles/` + - vim-command `:source ~/vimfiles/jslint_wrapper_vim.vim` +3. Vim can now jslint files (via nodejs): + - with vim-command `:SaveAndJslint` + - with vim-key-combo ` ` +- screenshot + +[![screenshot](asset_image_jslint_wrapper_vim.png)](https://www.jslint.com/jslint_wrapper_vim.vim) + + +

    +# Quickstart JSLint in VSCode +1. In VSCode, search and install extension [`vscode-jslint`](https://marketplace.visualstudio.com/items?itemName=jslint.vscode-jslint) +2. In VSCode, while editing a javascript file: + - right-click context-menu and select `[JSLint - Lint File]` + - or use key-binding `[Ctrl + Shift + J], [L]` + - or use key-binding `[ Cmd + Shift + J], [L]` for Mac +- screenshot + +[![screenshot](https://kaizhu256.github.io/jslint/asset_image_jslint_wrapper_vscode.png)](https://marketplace.visualstudio.com/items?itemName=jslint.vscode-jslint) + + +

    +# Documentation + + +- [jslint.mjs](jslint.mjs) contains the jslint function. It parses and analyzes a source file, returning an object with information about the file. It can also take an object that sets options. + +- [index.html](index.html) runs the jslint.mjs function in a web page. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[https://www.crockford.com/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. + + +

    +### API Doc +- https://www.jslint.com/apidoc.html + +[![screenshot](https://kaizhu256.github.io/jslint/branch-alpha/.artifact/screenshot_browser__2f.artifact_2fapidoc.html.png)](https://www.jslint.com/apidoc.html) + + +

    +### Directive `/*jslint*/` + +
    + +##### `/*jslint beta*/` + +```js +/*jslint beta*/ +// Enable experimental warnings. +// Warn if global variables are redefined. +// Warn if const / let statements are not declared at top of function or +// script, similar to var statements. +// Warn if const / let / var statements are not declared in ascii-order. +// Warn if named-functions are not declared in ascii-order. +// Warn if cases in switch-statements are not in ascii-order. +``` + +
    + +##### `/*jslint bitwise*/` + +```js +/*jslint bitwise*/ +// Allow bitwise operator. + +let foo = 0 | 1; +``` + +
    + +##### `/*jslint browser*/` + +```js +/*jslint browser*/ +// Assume browser environment. + +localStorage.getItem("foo"); +``` + +
    + +##### `/*jslint convert*/` + +```js +/*jslint convert*/ +// Allow conversion operator. + +let foo = new Date() + ""; +let bar = !!0; +``` + +
    + +##### `/*jslint couch*/` + +```js +/*jslint couch*/ +// Assume CouchDb environment. + +registerType("text-json", "text/json"); +``` + +
    + +##### `/*jslint devel*/` + +```js +/*jslint devel*/ +// Allow console.log() and friends. + +console.log("hello"); +``` + +
    + +##### `/*jslint eval*/` + +```js +/*jslint eval*/ +// Allow eval(). + +eval("1"); +``` + +
    + +##### `/*jslint fart*/` + +```js +/*jslint fart*/ +// Allow complex fat-arrow. + +let foo = async ({bar, baz}) => { + return await bar(baz); +}; +``` + +
    + +##### `/*jslint for*/` + +```js +/*jslint for*/ +// Allow for-loop. + +function foo() { + let ii; + for (ii = 0; ii < 10; ii += 1) { + foo(); + } +} +``` + +
    + +##### `/*jslint getset*/` + +```js +/*jslint getset, this, devel*/ +// Allow get() and set(). + +let foo = { + bar: 0, + get getBar() { + return this.bar; + }, + set setBar(value) { + this.bar = value; + } +}; +console.log(foo.getBar); // 0 +foo.setBar = 1; +console.log(foo.getBar); // 1 +``` + +
    + +##### `/*jslint indent2*/` + +```js +/*jslint indent2*/ +// Use 2-space indent. + +function foo() { + return; +} +``` + +
    + +##### `/*jslint long*/` + +```js +/*jslint long*/ +// Allow long lines. + +let foo = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; +``` + +
    + +##### `/*jslint node*/` + +```js +/*jslint node*/ +// Assume Node.js environment. + +require("fs"); +``` + +
    + +##### `/*jslint nomen*/` + +```js +/*jslint nomen*/ +// Allow weird property name. + +let foo = {}; +foo._bar = 1; +``` + +
    + +##### `/*jslint single*/` + +```js +/*jslint single*/ +// Allow single-quote strings. + +let foo = ''; +``` + +
    + +##### `/*jslint subscript*/` + +```js +/*jslint subscript*/ +// Allow identifiers in subscript-notation. + +let foo = {}; +foo["bar"] = 1; +``` + +
    + +##### `/*jslint this*/` + +```js +/*jslint this*/ +// Allow 'this'. + +function foo() { + return this; +} +``` + +
    + +##### `/*jslint trace*/` + +```js +/*jslint trace*/ +// Include jslint stack-trace in warnings. + +console.log('hello world'); +/* +1. Undeclared 'console'. +console.log('hello world'); +Error + at warn_at (...) + at warn (...) + at lookup (...) + at pre_v (...) + at jslint.mjs +2. Use double quotes, not single quotes. +console.log(...); +Error + at warn_at (...) + at lex_string (...) + at lex_token (...) + at jslint_phase2_lex (...) + at Function.jslint (...) + at jslint.mjs +*/ +``` + +
    + +##### `/*jslint unordered*/` + +```js +/*jslint unordered*/ +// Allow unordered cases, params, properties, variables, and exports. + +let foo = {bb: 1, aa: 0}; + +function bar({ + bb = 1, + aa = 0 +}) { + return aa + bb; +} + +export { + foo, + bar +}; +``` + +
    + +##### `/*jslint white*/` + +```js +/*jslint white*/ +// Allow messy whitespace. + +let foo = 1; let bar = 2; +``` + + +

    +### Directive `/*global*/` + +```js +/*global foo, bar*/ +// Declare global variables foo, bar. + +foo(); +bar(); +``` + + +

    +### Directive `/*property*/` + +```js +/*property foo, bar*/ +// Restrict property-access to only .foo, .bar. + +let aa = {bar: 1, foo: 2}; +``` + + +

    +### Directive `/*jslint-disable*/.../*jslint-enable*/` + +```js +/*jslint-disable*/ + +JSLint will ignore and treat this region as blank-lines. +Syntax error. + +/*jslint-enable*/ +``` + + +

    +### Directive `//jslint-ignore-line` + +```js +// JSLint will ignore non-fatal warnings at given line. + +eval("1"); //jslint-ignore-line +``` + + +

    +# Package Listing +![screenshot_package_listing.svg](https://kaizhu256.github.io/jslint/branch-alpha/.artifact/screenshot_package_listing.svg) + + +

    +# Changelog +- [Full CHANGELOG.md](CHANGELOG.md) + +![screenshot_changelog.svg](https://kaizhu256.github.io/jslint/branch-alpha/.artifact/screenshot_changelog.svg) + + +

    +# License +- JSLint is under [Unlicense License](LICENSE). +- CodeMirror editor is under [MIT License](https://github.com/codemirror/codemirror5/blob/d0e3b2e727c41aa4fd89fbad0adfb3815339174c/LICENSE). +- Function `v8CoverageListMerge` is derived from [MIT Licensed v8-coverage](https://github.com/demurgos/v8-coverage/blob/73446087dc38f61b09832c9867122a23f8577099/ts/LICENSE.md). + + +

    +# Devops Instruction + + +

    +### pull-request merge +- find highest issue-number at https://github.com/kaizhu256/jslint/issues/, https://github.com/kaizhu256/jslint/pulls/, and add +1 to it for PR-xxx +- `shGitPullrequest beta beta` + - verify ci-success for origin-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- `git push upstream alpha -f` + - verify ci-success for upstream-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- goto https://github.com/kaizhu256/jslint/compare/beta...kaizhu256:jslint:branch-p2024.11.24 +- click `Create pull request` +- input `Add your description here...` with: +``` +Fixes #xxx. +- + +This PR will ... + +This PR will additionally: +- +... + + +``` +- verify `commit into jslint-org:beta` +- click `Create pull request` + - verify ci-success for pull-request + - https://github.com/kaizhu256/jslint/actions/workflows/on_pull_request.yml +- wait awhile before continuing ... +- click `Rebase and merge` + - verify ci-success for upstream-branch-beta + - https://github.com/kaizhu256/jslint/actions +- `shGitPullrequestCleanup` + - verify ci-success for origin-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- `git push upstream alpha -f` + - verify ci-success for upstream-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- click `Delete branch` + + +

    +### branch-master commit +- `shGitPullrequest master beta` + - verify ci-success for origin-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- `git push upstream alpha -f` + - verify ci-success for upstream-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- goto https://github.com/kaizhu256/jslint/compare/beta...kaizhu256:jslint:branch-v2024.11.24 +- click `Create pull request` +- input `Add a title` with: `# v20yy.mm.dd` +- input `Add a description` with: +``` +- +- +``` +- verify `commit into jslint-org:beta` +- click `Create pull request` + - verify ci-success for pull-request + - https://github.com/kaizhu256/jslint/actions/workflows/on_pull_request.yml +- wait awhile before continuing ... +- click `Rebase and merge` + - verify ci-success for upstream-branch-beta + - https://github.com/kaizhu256/jslint/actions +- `shGitPullrequestCleanup` + - verify ci-success for origin-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- `git push upstream alpha -f` + - verify ci-success for upstream-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- click `Delete branch` +- `git push origin beta:master` + - verify ci-success for origin-branch-master + - https://github.com/kaizhu256/jslint/actions +- `git push upstream beta:master` + - verify ci-success for upstream-branch-master + - https://github.com/kaizhu256/jslint/actions + + +

    +### branch-master publish +- `git push upstream beta:master` + - verify ci-success for upstream-branch-master + - https://github.com/kaizhu256/jslint/actions +- goto https://github.com/kaizhu256/jslint/releases/new +- input `Choose a tag` with: `v20yy.mm.dd` +- click `Create new tag: v20yy.mm.dd on publish` + - verify correct-year `20yy` +- select `Target: master` +- select `Previous tag:auto` +- input `Release title` with: `v20yy.mm.dd - ` +- input `Describe this release` with: +``` +- +- +``` +- click `Generate release notes` +- click `Set as the latest release` +- click `Preview` and review +- click `Publish release` + - verify ci-success for upstream-branch-publish + - https://github.com/kaizhu256/jslint/actions + - verify email-notification `Successfully published @kaizhu256/jslint@20yy.mm.dd` + + +

    +### vscode-jslint publish +- goto https://github.com/kaizhu256/jslint/tree/gh-pages/branch-alpha/.artifact/jslint_wrapper_vscode +- click `vscode-jslint-20yy.mm.dd.vsix` +- click `Raw` to download +- goto https://marketplace.visualstudio.com/manage/publishers/jslint +- right-click `Update` +- upload downloaded file `vscode-jslint-20yy.mm.dd.vsix` +- click 'Upload' +- verify email-notification `[Succeeded] Extension publish on Visual Studio Marketplace - vscode-jslint` + + + diff --git a/branch-alpha/asset_codemirror_rollup.js b/branch-alpha/asset_codemirror_rollup.js new file mode 100644 index 000000000..9b93a0427 --- /dev/null +++ b/branch-alpha/asset_codemirror_rollup.js @@ -0,0 +1,11481 @@ +/*jslint-disable*/ +/* +shRollupFetch +{ + "fetchList": [ + { + "comment": true, + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/LICENSE" + }, + { + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/codemirror.js", + "url2": "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.10/codemirror.js" + }, + { + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/addon/edit/matchbrackets.js" + }, + { + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/addon/edit/trailingspace.js" + }, + { + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/addon/lint/lint.js" + }, + { + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/addon/selection/active-line.js" + }, + { + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/mode/javascript/javascript.js" + } + ], + "replaceList": [] +} +*/ + + +/* +repo https://github.com/codemirror/codemirror5/tree/5.65.10 +committed 2022-11-20T15:35:33Z +*/ + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/LICENSE +*/ +/* +MIT License + +Copyright (C) 2017 by Marijn Haverbeke and others + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/codemirror.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +// This is CodeMirror (https://codemirror.net/5), a code editor +// implemented in JavaScript on top of the browser's DOM. +// +// You can find some technical background for some of the code below +// at http://marijnhaverbeke.nl/blog/#cm-internals . + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, global.CodeMirror = factory()); +}(this, (function () { 'use strict'; + + // Kludges for bugs and behavior differences that can't be feature + // detected are enabled based on userAgent etc sniffing. + var userAgent = navigator.userAgent; + var platform = navigator.platform; + + var gecko = /gecko\/\d/i.test(userAgent); + var ie_upto10 = /MSIE \d/.test(userAgent); + var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent); + var edge = /Edge\/(\d+)/.exec(userAgent); + var ie = ie_upto10 || ie_11up || edge; + var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]); + var webkit = !edge && /WebKit\//.test(userAgent); + var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent); + var chrome = !edge && /Chrome\/(\d+)/.exec(userAgent); + var chrome_version = chrome && +chrome[1]; + var presto = /Opera\//.test(userAgent); + var safari = /Apple Computer/.test(navigator.vendor); + var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent); + var phantom = /PhantomJS/.test(userAgent); + + var ios = safari && (/Mobile\/\w+/.test(userAgent) || navigator.maxTouchPoints > 2); + var android = /Android/.test(userAgent); + // This is woefully incomplete. Suggestions for alternative methods welcome. + var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent); + var mac = ios || /Mac/.test(platform); + var chromeOS = /\bCrOS\b/.test(userAgent); + var windows = /win/i.test(platform); + + var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/); + if (presto_version) { presto_version = Number(presto_version[1]); } + if (presto_version && presto_version >= 15) { presto = false; webkit = true; } + // Some browsers use the wrong event properties to signal cmd/ctrl on OS X + var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11)); + var captureRightClick = gecko || (ie && ie_version >= 9); + + function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") } + + var rmClass = function(node, cls) { + var current = node.className; + var match = classTest(cls).exec(current); + if (match) { + var after = current.slice(match.index + match[0].length); + node.className = current.slice(0, match.index) + (after ? match[1] + after : ""); + } + }; + + function removeChildren(e) { + for (var count = e.childNodes.length; count > 0; --count) + { e.removeChild(e.firstChild); } + return e + } + + function removeChildrenAndAdd(parent, e) { + return removeChildren(parent).appendChild(e) + } + + function elt(tag, content, className, style) { + var e = document.createElement(tag); + if (className) { e.className = className; } + if (style) { e.style.cssText = style; } + if (typeof content == "string") { e.appendChild(document.createTextNode(content)); } + else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } } + return e + } + // wrapper for elt, which removes the elt from the accessibility tree + function eltP(tag, content, className, style) { + var e = elt(tag, content, className, style); + e.setAttribute("role", "presentation"); + return e + } + + var range; + if (document.createRange) { range = function(node, start, end, endNode) { + var r = document.createRange(); + r.setEnd(endNode || node, end); + r.setStart(node, start); + return r + }; } + else { range = function(node, start, end) { + var r = document.body.createTextRange(); + try { r.moveToElementText(node.parentNode); } + catch(e) { return r } + r.collapse(true); + r.moveEnd("character", end); + r.moveStart("character", start); + return r + }; } + + function contains(parent, child) { + if (child.nodeType == 3) // Android browser always returns false when child is a textnode + { child = child.parentNode; } + if (parent.contains) + { return parent.contains(child) } + do { + if (child.nodeType == 11) { child = child.host; } + if (child == parent) { return true } + } while (child = child.parentNode) + } + + function activeElt(doc) { + // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement. + // IE < 10 will throw when accessed while the page is loading or in an iframe. + // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable. + var activeElement; + try { + activeElement = doc.activeElement; + } catch(e) { + activeElement = doc.body || null; + } + while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement) + { activeElement = activeElement.shadowRoot.activeElement; } + return activeElement + } + + function addClass(node, cls) { + var current = node.className; + if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; } + } + function joinClasses(a, b) { + var as = a.split(" "); + for (var i = 0; i < as.length; i++) + { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } } + return b + } + + var selectInput = function(node) { node.select(); }; + if (ios) // Mobile Safari apparently has a bug where select() is broken. + { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; } + else if (ie) // Suppress mysterious IE10 errors + { selectInput = function(node) { try { node.select(); } catch(_e) {} }; } + + function doc(cm) { return cm.display.wrapper.ownerDocument } + + function win(cm) { return doc(cm).defaultView } + + function bind(f) { + var args = Array.prototype.slice.call(arguments, 1); + return function(){return f.apply(null, args)} + } + + function copyObj(obj, target, overwrite) { + if (!target) { target = {}; } + for (var prop in obj) + { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop))) + { target[prop] = obj[prop]; } } + return target + } + + // Counts the column offset in a string, taking tabs into account. + // Used mostly to find indentation. + function countColumn(string, end, tabSize, startIndex, startValue) { + if (end == null) { + end = string.search(/[^\s\u00a0]/); + if (end == -1) { end = string.length; } + } + for (var i = startIndex || 0, n = startValue || 0;;) { + var nextTab = string.indexOf("\t", i); + if (nextTab < 0 || nextTab >= end) + { return n + (end - i) } + n += nextTab - i; + n += tabSize - (n % tabSize); + i = nextTab + 1; + } + } + + var Delayed = function() { + this.id = null; + this.f = null; + this.time = 0; + this.handler = bind(this.onTimeout, this); + }; + Delayed.prototype.onTimeout = function (self) { + self.id = 0; + if (self.time <= +new Date) { + self.f(); + } else { + setTimeout(self.handler, self.time - +new Date); + } + }; + Delayed.prototype.set = function (ms, f) { + this.f = f; + var time = +new Date + ms; + if (!this.id || time < this.time) { + clearTimeout(this.id); + this.id = setTimeout(this.handler, ms); + this.time = time; + } + }; + + function indexOf(array, elt) { + for (var i = 0; i < array.length; ++i) + { if (array[i] == elt) { return i } } + return -1 + } + + // Number of pixels added to scroller and sizer to hide scrollbar + var scrollerGap = 50; + + // Returned or thrown by various protocols to signal 'I'm not + // handling this'. + var Pass = {toString: function(){return "CodeMirror.Pass"}}; + + // Reused option objects for setSelection & friends + var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"}; + + // The inverse of countColumn -- find the offset that corresponds to + // a particular column. + function findColumn(string, goal, tabSize) { + for (var pos = 0, col = 0;;) { + var nextTab = string.indexOf("\t", pos); + if (nextTab == -1) { nextTab = string.length; } + var skipped = nextTab - pos; + if (nextTab == string.length || col + skipped >= goal) + { return pos + Math.min(skipped, goal - col) } + col += nextTab - pos; + col += tabSize - (col % tabSize); + pos = nextTab + 1; + if (col >= goal) { return pos } + } + } + + var spaceStrs = [""]; + function spaceStr(n) { + while (spaceStrs.length <= n) + { spaceStrs.push(lst(spaceStrs) + " "); } + return spaceStrs[n] + } + + function lst(arr) { return arr[arr.length-1] } + + function map(array, f) { + var out = []; + for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); } + return out + } + + function insertSorted(array, value, score) { + var pos = 0, priority = score(value); + while (pos < array.length && score(array[pos]) <= priority) { pos++; } + array.splice(pos, 0, value); + } + + function nothing() {} + + function createObj(base, props) { + var inst; + if (Object.create) { + inst = Object.create(base); + } else { + nothing.prototype = base; + inst = new nothing(); + } + if (props) { copyObj(props, inst); } + return inst + } + + var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; + function isWordCharBasic(ch) { + return /\w/.test(ch) || ch > "\x80" && + (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)) + } + function isWordChar(ch, helper) { + if (!helper) { return isWordCharBasic(ch) } + if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true } + return helper.test(ch) + } + + function isEmpty(obj) { + for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } } + return true + } + + // Extending unicode characters. A series of a non-extending char + + // any number of extending chars is treated as a single unit as far + // as editing and measuring is concerned. This is not fully correct, + // since some scripts/fonts/browsers also treat other configurations + // of code points as a group. + var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/; + function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) } + + // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range. + function skipExtendingChars(str, pos, dir) { + while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; } + return pos + } + + // Returns the value from the range [`from`; `to`] that satisfies + // `pred` and is closest to `from`. Assumes that at least `to` + // satisfies `pred`. Supports `from` being greater than `to`. + function findFirst(pred, from, to) { + // At any point we are certain `to` satisfies `pred`, don't know + // whether `from` does. + var dir = from > to ? -1 : 1; + for (;;) { + if (from == to) { return from } + var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF); + if (mid == from) { return pred(mid) ? from : to } + if (pred(mid)) { to = mid; } + else { from = mid + dir; } + } + } + + // BIDI HELPERS + + function iterateBidiSections(order, from, to, f) { + if (!order) { return f(from, to, "ltr", 0) } + var found = false; + for (var i = 0; i < order.length; ++i) { + var part = order[i]; + if (part.from < to && part.to > from || from == to && part.to == from) { + f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i); + found = true; + } + } + if (!found) { f(from, to, "ltr"); } + } + + var bidiOther = null; + function getBidiPartAt(order, ch, sticky) { + var found; + bidiOther = null; + for (var i = 0; i < order.length; ++i) { + var cur = order[i]; + if (cur.from < ch && cur.to > ch) { return i } + if (cur.to == ch) { + if (cur.from != cur.to && sticky == "before") { found = i; } + else { bidiOther = i; } + } + if (cur.from == ch) { + if (cur.from != cur.to && sticky != "before") { found = i; } + else { bidiOther = i; } + } + } + return found != null ? found : bidiOther + } + + // Bidirectional ordering algorithm + // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm + // that this (partially) implements. + + // One-char codes used for character types: + // L (L): Left-to-Right + // R (R): Right-to-Left + // r (AL): Right-to-Left Arabic + // 1 (EN): European Number + // + (ES): European Number Separator + // % (ET): European Number Terminator + // n (AN): Arabic Number + // , (CS): Common Number Separator + // m (NSM): Non-Spacing Mark + // b (BN): Boundary Neutral + // s (B): Paragraph Separator + // t (S): Segment Separator + // w (WS): Whitespace + // N (ON): Other Neutrals + + // Returns null if characters are ordered as they appear + // (left-to-right), or an array of sections ({from, to, level} + // objects) in the order in which they occur visually. + var bidiOrdering = (function() { + // Character types for codepoints 0 to 0xff + var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"; + // Character types for codepoints 0x600 to 0x6f9 + var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111"; + function charType(code) { + if (code <= 0xf7) { return lowTypes.charAt(code) } + else if (0x590 <= code && code <= 0x5f4) { return "R" } + else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) } + else if (0x6ee <= code && code <= 0x8ac) { return "r" } + else if (0x2000 <= code && code <= 0x200b) { return "w" } + else if (code == 0x200c) { return "b" } + else { return "L" } + } + + var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/; + var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/; + + function BidiSpan(level, from, to) { + this.level = level; + this.from = from; this.to = to; + } + + return function(str, direction) { + var outerType = direction == "ltr" ? "L" : "R"; + + if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false } + var len = str.length, types = []; + for (var i = 0; i < len; ++i) + { types.push(charType(str.charCodeAt(i))); } + + // W1. Examine each non-spacing mark (NSM) in the level run, and + // change the type of the NSM to the type of the previous + // character. If the NSM is at the start of the level run, it will + // get the type of sor. + for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) { + var type = types[i$1]; + if (type == "m") { types[i$1] = prev; } + else { prev = type; } + } + + // W2. Search backwards from each instance of a European number + // until the first strong type (R, L, AL, or sor) is found. If an + // AL is found, change the type of the European number to Arabic + // number. + // W3. Change all ALs to R. + for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) { + var type$1 = types[i$2]; + if (type$1 == "1" && cur == "r") { types[i$2] = "n"; } + else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } } + } + + // W4. A single European separator between two European numbers + // changes to a European number. A single common separator between + // two numbers of the same type changes to that type. + for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) { + var type$2 = types[i$3]; + if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; } + else if (type$2 == "," && prev$1 == types[i$3+1] && + (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; } + prev$1 = type$2; + } + + // W5. A sequence of European terminators adjacent to European + // numbers changes to all European numbers. + // W6. Otherwise, separators and terminators change to Other + // Neutral. + for (var i$4 = 0; i$4 < len; ++i$4) { + var type$3 = types[i$4]; + if (type$3 == ",") { types[i$4] = "N"; } + else if (type$3 == "%") { + var end = (void 0); + for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {} + var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"; + for (var j = i$4; j < end; ++j) { types[j] = replace; } + i$4 = end - 1; + } + } + + // W7. Search backwards from each instance of a European number + // until the first strong type (R, L, or sor) is found. If an L is + // found, then change the type of the European number to L. + for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) { + var type$4 = types[i$5]; + if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; } + else if (isStrong.test(type$4)) { cur$1 = type$4; } + } + + // N1. A sequence of neutrals takes the direction of the + // surrounding strong text if the text on both sides has the same + // direction. European and Arabic numbers act as if they were R in + // terms of their influence on neutrals. Start-of-level-run (sor) + // and end-of-level-run (eor) are used at level run boundaries. + // N2. Any remaining neutrals take the embedding direction. + for (var i$6 = 0; i$6 < len; ++i$6) { + if (isNeutral.test(types[i$6])) { + var end$1 = (void 0); + for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {} + var before = (i$6 ? types[i$6-1] : outerType) == "L"; + var after = (end$1 < len ? types[end$1] : outerType) == "L"; + var replace$1 = before == after ? (before ? "L" : "R") : outerType; + for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; } + i$6 = end$1 - 1; + } + } + + // Here we depart from the documented algorithm, in order to avoid + // building up an actual levels array. Since there are only three + // levels (0, 1, 2) in an implementation that doesn't take + // explicit embedding into account, we can build up the order on + // the fly, without following the level-based algorithm. + var order = [], m; + for (var i$7 = 0; i$7 < len;) { + if (countsAsLeft.test(types[i$7])) { + var start = i$7; + for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {} + order.push(new BidiSpan(0, start, i$7)); + } else { + var pos = i$7, at = order.length, isRTL = direction == "rtl" ? 1 : 0; + for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {} + for (var j$2 = pos; j$2 < i$7;) { + if (countsAsNum.test(types[j$2])) { + if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); at += isRTL; } + var nstart = j$2; + for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {} + order.splice(at, 0, new BidiSpan(2, nstart, j$2)); + at += isRTL; + pos = j$2; + } else { ++j$2; } + } + if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); } + } + } + if (direction == "ltr") { + if (order[0].level == 1 && (m = str.match(/^\s+/))) { + order[0].from = m[0].length; + order.unshift(new BidiSpan(0, 0, m[0].length)); + } + if (lst(order).level == 1 && (m = str.match(/\s+$/))) { + lst(order).to -= m[0].length; + order.push(new BidiSpan(0, len - m[0].length, len)); + } + } + + return direction == "rtl" ? order.reverse() : order + } + })(); + + // Get the bidi ordering for the given line (and cache it). Returns + // false for lines that are fully left-to-right, and an array of + // BidiSpan objects otherwise. + function getOrder(line, direction) { + var order = line.order; + if (order == null) { order = line.order = bidiOrdering(line.text, direction); } + return order + } + + // EVENT HANDLING + + // Lightweight event framework. on/off also work on DOM nodes, + // registering native DOM handlers. + + var noHandlers = []; + + var on = function(emitter, type, f) { + if (emitter.addEventListener) { + emitter.addEventListener(type, f, false); + } else if (emitter.attachEvent) { + emitter.attachEvent("on" + type, f); + } else { + var map = emitter._handlers || (emitter._handlers = {}); + map[type] = (map[type] || noHandlers).concat(f); + } + }; + + function getHandlers(emitter, type) { + return emitter._handlers && emitter._handlers[type] || noHandlers + } + + function off(emitter, type, f) { + if (emitter.removeEventListener) { + emitter.removeEventListener(type, f, false); + } else if (emitter.detachEvent) { + emitter.detachEvent("on" + type, f); + } else { + var map = emitter._handlers, arr = map && map[type]; + if (arr) { + var index = indexOf(arr, f); + if (index > -1) + { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)); } + } + } + } + + function signal(emitter, type /*, values...*/) { + var handlers = getHandlers(emitter, type); + if (!handlers.length) { return } + var args = Array.prototype.slice.call(arguments, 2); + for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); } + } + + // The DOM events that CodeMirror handles can be overridden by + // registering a (non-DOM) handler on the editor for the event name, + // and preventDefault-ing the event in that handler. + function signalDOMEvent(cm, e, override) { + if (typeof e == "string") + { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; } + signal(cm, override || e.type, cm, e); + return e_defaultPrevented(e) || e.codemirrorIgnore + } + + function signalCursorActivity(cm) { + var arr = cm._handlers && cm._handlers.cursorActivity; + if (!arr) { return } + var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []); + for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1) + { set.push(arr[i]); } } + } + + function hasHandler(emitter, type) { + return getHandlers(emitter, type).length > 0 + } + + // Add on and off methods to a constructor's prototype, to make + // registering events on such objects more convenient. + function eventMixin(ctor) { + ctor.prototype.on = function(type, f) {on(this, type, f);}; + ctor.prototype.off = function(type, f) {off(this, type, f);}; + } + + // Due to the fact that we still support jurassic IE versions, some + // compatibility wrappers are needed. + + function e_preventDefault(e) { + if (e.preventDefault) { e.preventDefault(); } + else { e.returnValue = false; } + } + function e_stopPropagation(e) { + if (e.stopPropagation) { e.stopPropagation(); } + else { e.cancelBubble = true; } + } + function e_defaultPrevented(e) { + return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false + } + function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);} + + function e_target(e) {return e.target || e.srcElement} + function e_button(e) { + var b = e.which; + if (b == null) { + if (e.button & 1) { b = 1; } + else if (e.button & 2) { b = 3; } + else if (e.button & 4) { b = 2; } + } + if (mac && e.ctrlKey && b == 1) { b = 3; } + return b + } + + // Detect drag-and-drop + var dragAndDrop = function() { + // There is *some* kind of drag-and-drop support in IE6-8, but I + // couldn't get it to work yet. + if (ie && ie_version < 9) { return false } + var div = elt('div'); + return "draggable" in div || "dragDrop" in div + }(); + + var zwspSupported; + function zeroWidthElement(measure) { + if (zwspSupported == null) { + var test = elt("span", "\u200b"); + removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")])); + if (measure.firstChild.offsetHeight != 0) + { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); } + } + var node = zwspSupported ? elt("span", "\u200b") : + elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px"); + node.setAttribute("cm-text", ""); + return node + } + + // Feature-detect IE's crummy client rect reporting for bidi text + var badBidiRects; + function hasBadBidiRects(measure) { + if (badBidiRects != null) { return badBidiRects } + var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA")); + var r0 = range(txt, 0, 1).getBoundingClientRect(); + var r1 = range(txt, 1, 2).getBoundingClientRect(); + removeChildren(measure); + if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780) + return badBidiRects = (r1.right - r0.right < 3) + } + + // See if "".split is the broken IE version, if so, provide an + // alternative way to split lines. + var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) { + var pos = 0, result = [], l = string.length; + while (pos <= l) { + var nl = string.indexOf("\n", pos); + if (nl == -1) { nl = string.length; } + var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl); + var rt = line.indexOf("\r"); + if (rt != -1) { + result.push(line.slice(0, rt)); + pos += rt + 1; + } else { + result.push(line); + pos = nl + 1; + } + } + return result + } : function (string) { return string.split(/\r\n?|\n/); }; + + var hasSelection = window.getSelection ? function (te) { + try { return te.selectionStart != te.selectionEnd } + catch(e) { return false } + } : function (te) { + var range; + try {range = te.ownerDocument.selection.createRange();} + catch(e) {} + if (!range || range.parentElement() != te) { return false } + return range.compareEndPoints("StartToEnd", range) != 0 + }; + + var hasCopyEvent = (function () { + var e = elt("div"); + if ("oncopy" in e) { return true } + e.setAttribute("oncopy", "return;"); + return typeof e.oncopy == "function" + })(); + + var badZoomedRects = null; + function hasBadZoomedRects(measure) { + if (badZoomedRects != null) { return badZoomedRects } + var node = removeChildrenAndAdd(measure, elt("span", "x")); + var normal = node.getBoundingClientRect(); + var fromRange = range(node, 0, 1).getBoundingClientRect(); + return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1 + } + + // Known modes, by name and by MIME + var modes = {}, mimeModes = {}; + + // Extra arguments are stored as the mode's dependencies, which is + // used by (legacy) mechanisms like loadmode.js to automatically + // load a mode. (Preferred mechanism is the require/define calls.) + function defineMode(name, mode) { + if (arguments.length > 2) + { mode.dependencies = Array.prototype.slice.call(arguments, 2); } + modes[name] = mode; + } + + function defineMIME(mime, spec) { + mimeModes[mime] = spec; + } + + // Given a MIME type, a {name, ...options} config object, or a name + // string, return a mode config object. + function resolveMode(spec) { + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { + spec = mimeModes[spec]; + } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { + var found = mimeModes[spec.name]; + if (typeof found == "string") { found = {name: found}; } + spec = createObj(found, spec); + spec.name = found.name; + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { + return resolveMode("application/xml") + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) { + return resolveMode("application/json") + } + if (typeof spec == "string") { return {name: spec} } + else { return spec || {name: "null"} } + } + + // Given a mode spec (anything that resolveMode accepts), find and + // initialize an actual mode object. + function getMode(options, spec) { + spec = resolveMode(spec); + var mfactory = modes[spec.name]; + if (!mfactory) { return getMode(options, "text/plain") } + var modeObj = mfactory(options, spec); + if (modeExtensions.hasOwnProperty(spec.name)) { + var exts = modeExtensions[spec.name]; + for (var prop in exts) { + if (!exts.hasOwnProperty(prop)) { continue } + if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; } + modeObj[prop] = exts[prop]; + } + } + modeObj.name = spec.name; + if (spec.helperType) { modeObj.helperType = spec.helperType; } + if (spec.modeProps) { for (var prop$1 in spec.modeProps) + { modeObj[prop$1] = spec.modeProps[prop$1]; } } + + return modeObj + } + + // This can be used to attach properties to mode objects from + // outside the actual mode definition. + var modeExtensions = {}; + function extendMode(mode, properties) { + var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); + copyObj(properties, exts); + } + + function copyState(mode, state) { + if (state === true) { return state } + if (mode.copyState) { return mode.copyState(state) } + var nstate = {}; + for (var n in state) { + var val = state[n]; + if (val instanceof Array) { val = val.concat([]); } + nstate[n] = val; + } + return nstate + } + + // Given a mode and a state (for that mode), find the inner mode and + // state at the position that the state refers to. + function innerMode(mode, state) { + var info; + while (mode.innerMode) { + info = mode.innerMode(state); + if (!info || info.mode == mode) { break } + state = info.state; + mode = info.mode; + } + return info || {mode: mode, state: state} + } + + function startState(mode, a1, a2) { + return mode.startState ? mode.startState(a1, a2) : true + } + + // STRING STREAM + + // Fed to the mode parsers, provides helper functions to make + // parsers more succinct. + + var StringStream = function(string, tabSize, lineOracle) { + this.pos = this.start = 0; + this.string = string; + this.tabSize = tabSize || 8; + this.lastColumnPos = this.lastColumnValue = 0; + this.lineStart = 0; + this.lineOracle = lineOracle; + }; + + StringStream.prototype.eol = function () {return this.pos >= this.string.length}; + StringStream.prototype.sol = function () {return this.pos == this.lineStart}; + StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined}; + StringStream.prototype.next = function () { + if (this.pos < this.string.length) + { return this.string.charAt(this.pos++) } + }; + StringStream.prototype.eat = function (match) { + var ch = this.string.charAt(this.pos); + var ok; + if (typeof match == "string") { ok = ch == match; } + else { ok = ch && (match.test ? match.test(ch) : match(ch)); } + if (ok) {++this.pos; return ch} + }; + StringStream.prototype.eatWhile = function (match) { + var start = this.pos; + while (this.eat(match)){} + return this.pos > start + }; + StringStream.prototype.eatSpace = function () { + var start = this.pos; + while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; } + return this.pos > start + }; + StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;}; + StringStream.prototype.skipTo = function (ch) { + var found = this.string.indexOf(ch, this.pos); + if (found > -1) {this.pos = found; return true} + }; + StringStream.prototype.backUp = function (n) {this.pos -= n;}; + StringStream.prototype.column = function () { + if (this.lastColumnPos < this.start) { + this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue); + this.lastColumnPos = this.start; + } + return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) + }; + StringStream.prototype.indentation = function () { + return countColumn(this.string, null, this.tabSize) - + (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) + }; + StringStream.prototype.match = function (pattern, consume, caseInsensitive) { + if (typeof pattern == "string") { + var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; }; + var substr = this.string.substr(this.pos, pattern.length); + if (cased(substr) == cased(pattern)) { + if (consume !== false) { this.pos += pattern.length; } + return true + } + } else { + var match = this.string.slice(this.pos).match(pattern); + if (match && match.index > 0) { return null } + if (match && consume !== false) { this.pos += match[0].length; } + return match + } + }; + StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)}; + StringStream.prototype.hideFirstChars = function (n, inner) { + this.lineStart += n; + try { return inner() } + finally { this.lineStart -= n; } + }; + StringStream.prototype.lookAhead = function (n) { + var oracle = this.lineOracle; + return oracle && oracle.lookAhead(n) + }; + StringStream.prototype.baseToken = function () { + var oracle = this.lineOracle; + return oracle && oracle.baseToken(this.pos) + }; + + // Find the line object corresponding to the given line number. + function getLine(doc, n) { + n -= doc.first; + if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") } + var chunk = doc; + while (!chunk.lines) { + for (var i = 0;; ++i) { + var child = chunk.children[i], sz = child.chunkSize(); + if (n < sz) { chunk = child; break } + n -= sz; + } + } + return chunk.lines[n] + } + + // Get the part of a document between two positions, as an array of + // strings. + function getBetween(doc, start, end) { + var out = [], n = start.line; + doc.iter(start.line, end.line + 1, function (line) { + var text = line.text; + if (n == end.line) { text = text.slice(0, end.ch); } + if (n == start.line) { text = text.slice(start.ch); } + out.push(text); + ++n; + }); + return out + } + // Get the lines between from and to, as array of strings. + function getLines(doc, from, to) { + var out = []; + doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value + return out + } + + // Update the height of a line, propagating the height change + // upwards to parent nodes. + function updateLineHeight(line, height) { + var diff = height - line.height; + if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } } + } + + // Given a line object, find its line number by walking up through + // its parent links. + function lineNo(line) { + if (line.parent == null) { return null } + var cur = line.parent, no = indexOf(cur.lines, line); + for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { + for (var i = 0;; ++i) { + if (chunk.children[i] == cur) { break } + no += chunk.children[i].chunkSize(); + } + } + return no + cur.first + } + + // Find the line at the given vertical position, using the height + // information in the document tree. + function lineAtHeight(chunk, h) { + var n = chunk.first; + outer: do { + for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) { + var child = chunk.children[i$1], ch = child.height; + if (h < ch) { chunk = child; continue outer } + h -= ch; + n += child.chunkSize(); + } + return n + } while (!chunk.lines) + var i = 0; + for (; i < chunk.lines.length; ++i) { + var line = chunk.lines[i], lh = line.height; + if (h < lh) { break } + h -= lh; + } + return n + i + } + + function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size} + + function lineNumberFor(options, i) { + return String(options.lineNumberFormatter(i + options.firstLineNumber)) + } + + // A Pos instance represents a position within the text. + function Pos(line, ch, sticky) { + if ( sticky === void 0 ) sticky = null; + + if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) } + this.line = line; + this.ch = ch; + this.sticky = sticky; + } + + // Compare two positions, return 0 if they are the same, a negative + // number when a is less, and a positive number otherwise. + function cmp(a, b) { return a.line - b.line || a.ch - b.ch } + + function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 } + + function copyPos(x) {return Pos(x.line, x.ch)} + function maxPos(a, b) { return cmp(a, b) < 0 ? b : a } + function minPos(a, b) { return cmp(a, b) < 0 ? a : b } + + // Most of the external API clips given positions to make sure they + // actually exist within the document. + function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))} + function clipPos(doc, pos) { + if (pos.line < doc.first) { return Pos(doc.first, 0) } + var last = doc.first + doc.size - 1; + if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) } + return clipToLen(pos, getLine(doc, pos.line).text.length) + } + function clipToLen(pos, linelen) { + var ch = pos.ch; + if (ch == null || ch > linelen) { return Pos(pos.line, linelen) } + else if (ch < 0) { return Pos(pos.line, 0) } + else { return pos } + } + function clipPosArray(doc, array) { + var out = []; + for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); } + return out + } + + var SavedContext = function(state, lookAhead) { + this.state = state; + this.lookAhead = lookAhead; + }; + + var Context = function(doc, state, line, lookAhead) { + this.state = state; + this.doc = doc; + this.line = line; + this.maxLookAhead = lookAhead || 0; + this.baseTokens = null; + this.baseTokenPos = 1; + }; + + Context.prototype.lookAhead = function (n) { + var line = this.doc.getLine(this.line + n); + if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; } + return line + }; + + Context.prototype.baseToken = function (n) { + if (!this.baseTokens) { return null } + while (this.baseTokens[this.baseTokenPos] <= n) + { this.baseTokenPos += 2; } + var type = this.baseTokens[this.baseTokenPos + 1]; + return {type: type && type.replace(/( |^)overlay .*/, ""), + size: this.baseTokens[this.baseTokenPos] - n} + }; + + Context.prototype.nextLine = function () { + this.line++; + if (this.maxLookAhead > 0) { this.maxLookAhead--; } + }; + + Context.fromSaved = function (doc, saved, line) { + if (saved instanceof SavedContext) + { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) } + else + { return new Context(doc, copyState(doc.mode, saved), line) } + }; + + Context.prototype.save = function (copy) { + var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state; + return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state + }; + + + // Compute a style array (an array starting with a mode generation + // -- for invalidation -- followed by pairs of end positions and + // style strings), which is used to highlight the tokens on the + // line. + function highlightLine(cm, line, context, forceToEnd) { + // A styles array always starts with a number identifying the + // mode/overlays that it is based on (for easy invalidation). + var st = [cm.state.modeGen], lineClasses = {}; + // Compute the base array of styles + runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); }, + lineClasses, forceToEnd); + var state = context.state; + + // Run overlays, adjust style array. + var loop = function ( o ) { + context.baseTokens = st; + var overlay = cm.state.overlays[o], i = 1, at = 0; + context.state = true; + runMode(cm, line.text, overlay.mode, context, function (end, style) { + var start = i; + // Ensure there's a token end at the current position, and that i points at it + while (at < end) { + var i_end = st[i]; + if (i_end > end) + { st.splice(i, 1, end, st[i+1], i_end); } + i += 2; + at = Math.min(end, i_end); + } + if (!style) { return } + if (overlay.opaque) { + st.splice(start, i - start, end, "overlay " + style); + i = start + 2; + } else { + for (; start < i; start += 2) { + var cur = st[start+1]; + st[start+1] = (cur ? cur + " " : "") + "overlay " + style; + } + } + }, lineClasses); + context.state = state; + context.baseTokens = null; + context.baseTokenPos = 1; + }; + + for (var o = 0; o < cm.state.overlays.length; ++o) loop( o ); + + return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null} + } + + function getLineStyles(cm, line, updateFrontier) { + if (!line.styles || line.styles[0] != cm.state.modeGen) { + var context = getContextBefore(cm, lineNo(line)); + var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state); + var result = highlightLine(cm, line, context); + if (resetState) { context.state = resetState; } + line.stateAfter = context.save(!resetState); + line.styles = result.styles; + if (result.classes) { line.styleClasses = result.classes; } + else if (line.styleClasses) { line.styleClasses = null; } + if (updateFrontier === cm.doc.highlightFrontier) + { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); } + } + return line.styles + } + + function getContextBefore(cm, n, precise) { + var doc = cm.doc, display = cm.display; + if (!doc.mode.startState) { return new Context(doc, true, n) } + var start = findStartLine(cm, n, precise); + var saved = start > doc.first && getLine(doc, start - 1).stateAfter; + var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start); + + doc.iter(start, n, function (line) { + processLine(cm, line.text, context); + var pos = context.line; + line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null; + context.nextLine(); + }); + if (precise) { doc.modeFrontier = context.line; } + return context + } + + // Lightweight form of highlight -- proceed over this line and + // update state, but don't save a style array. Used for lines that + // aren't currently visible. + function processLine(cm, text, context, startAt) { + var mode = cm.doc.mode; + var stream = new StringStream(text, cm.options.tabSize, context); + stream.start = stream.pos = startAt || 0; + if (text == "") { callBlankLine(mode, context.state); } + while (!stream.eol()) { + readToken(mode, stream, context.state); + stream.start = stream.pos; + } + } + + function callBlankLine(mode, state) { + if (mode.blankLine) { return mode.blankLine(state) } + if (!mode.innerMode) { return } + var inner = innerMode(mode, state); + if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) } + } + + function readToken(mode, stream, state, inner) { + for (var i = 0; i < 10; i++) { + if (inner) { inner[0] = innerMode(mode, state).mode; } + var style = mode.token(stream, state); + if (stream.pos > stream.start) { return style } + } + throw new Error("Mode " + mode.name + " failed to advance stream.") + } + + var Token = function(stream, type, state) { + this.start = stream.start; this.end = stream.pos; + this.string = stream.current(); + this.type = type || null; + this.state = state; + }; + + // Utility for getTokenAt and getLineTokens + function takeToken(cm, pos, precise, asArray) { + var doc = cm.doc, mode = doc.mode, style; + pos = clipPos(doc, pos); + var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise); + var stream = new StringStream(line.text, cm.options.tabSize, context), tokens; + if (asArray) { tokens = []; } + while ((asArray || stream.pos < pos.ch) && !stream.eol()) { + stream.start = stream.pos; + style = readToken(mode, stream, context.state); + if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); } + } + return asArray ? tokens : new Token(stream, style, context.state) + } + + function extractLineClasses(type, output) { + if (type) { for (;;) { + var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/); + if (!lineClass) { break } + type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length); + var prop = lineClass[1] ? "bgClass" : "textClass"; + if (output[prop] == null) + { output[prop] = lineClass[2]; } + else if (!(new RegExp("(?:^|\\s)" + lineClass[2] + "(?:$|\\s)")).test(output[prop])) + { output[prop] += " " + lineClass[2]; } + } } + return type + } + + // Run the given mode's parser over a line, calling f for each token. + function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) { + var flattenSpans = mode.flattenSpans; + if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; } + var curStart = 0, curStyle = null; + var stream = new StringStream(text, cm.options.tabSize, context), style; + var inner = cm.options.addModeClass && [null]; + if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); } + while (!stream.eol()) { + if (stream.pos > cm.options.maxHighlightLength) { + flattenSpans = false; + if (forceToEnd) { processLine(cm, text, context, stream.pos); } + stream.pos = text.length; + style = null; + } else { + style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses); + } + if (inner) { + var mName = inner[0].name; + if (mName) { style = "m-" + (style ? mName + " " + style : mName); } + } + if (!flattenSpans || curStyle != style) { + while (curStart < stream.start) { + curStart = Math.min(stream.start, curStart + 5000); + f(curStart, curStyle); + } + curStyle = style; + } + stream.start = stream.pos; + } + while (curStart < stream.pos) { + // Webkit seems to refuse to render text nodes longer than 57444 + // characters, and returns inaccurate measurements in nodes + // starting around 5000 chars. + var pos = Math.min(stream.pos, curStart + 5000); + f(pos, curStyle); + curStart = pos; + } + } + + // Finds the line to start with when starting a parse. Tries to + // find a line with a stateAfter, so that it can start with a + // valid state. If that fails, it returns the line with the + // smallest indentation, which tends to need the least context to + // parse correctly. + function findStartLine(cm, n, precise) { + var minindent, minline, doc = cm.doc; + var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100); + for (var search = n; search > lim; --search) { + if (search <= doc.first) { return doc.first } + var line = getLine(doc, search - 1), after = line.stateAfter; + if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier)) + { return search } + var indented = countColumn(line.text, null, cm.options.tabSize); + if (minline == null || minindent > indented) { + minline = search - 1; + minindent = indented; + } + } + return minline + } + + function retreatFrontier(doc, n) { + doc.modeFrontier = Math.min(doc.modeFrontier, n); + if (doc.highlightFrontier < n - 10) { return } + var start = doc.first; + for (var line = n - 1; line > start; line--) { + var saved = getLine(doc, line).stateAfter; + // change is on 3 + // state on line 1 looked ahead 2 -- so saw 3 + // test 1 + 2 < 3 should cover this + if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) { + start = line + 1; + break + } + } + doc.highlightFrontier = Math.min(doc.highlightFrontier, start); + } + + // Optimize some code when these features are not used. + var sawReadOnlySpans = false, sawCollapsedSpans = false; + + function seeReadOnlySpans() { + sawReadOnlySpans = true; + } + + function seeCollapsedSpans() { + sawCollapsedSpans = true; + } + + // TEXTMARKER SPANS + + function MarkedSpan(marker, from, to) { + this.marker = marker; + this.from = from; this.to = to; + } + + // Search an array of spans for a span matching the given marker. + function getMarkedSpanFor(spans, marker) { + if (spans) { for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.marker == marker) { return span } + } } + } + + // Remove a span from an array, returning undefined if no spans are + // left (we don't store arrays for lines without spans). + function removeMarkedSpan(spans, span) { + var r; + for (var i = 0; i < spans.length; ++i) + { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } } + return r + } + + // Add a span to a line. + function addMarkedSpan(line, span, op) { + var inThisOp = op && window.WeakSet && (op.markedSpans || (op.markedSpans = new WeakSet)); + if (inThisOp && line.markedSpans && inThisOp.has(line.markedSpans)) { + line.markedSpans.push(span); + } else { + line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]; + if (inThisOp) { inThisOp.add(line.markedSpans); } + } + span.marker.attachLine(line); + } + + // Used for the algorithm that adjusts markers for a change in the + // document. These functions cut an array of spans at a given + // character position, returning an array of remaining chunks (or + // undefined if nothing remains). + function markedSpansBefore(old, startCh, isInsert) { + var nw; + if (old) { for (var i = 0; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh); + if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) { + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh) + ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to)); + } + } } + return nw + } + function markedSpansAfter(old, endCh, isInsert) { + var nw; + if (old) { for (var i = 0; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh); + if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) { + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh) + ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh, + span.to == null ? null : span.to - endCh)); + } + } } + return nw + } + + // Given a change object, compute the new set of marker spans that + // cover the line in which the change took place. Removes spans + // entirely within the change, reconnects spans belonging to the + // same marker that appear on both sides of the change, and cuts off + // spans partially within the change. Returns an array of span + // arrays with one element for each line in (after) the change. + function stretchSpansOverChange(doc, change) { + if (change.full) { return null } + var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans; + var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans; + if (!oldFirst && !oldLast) { return null } + + var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0; + // Get the spans that 'stick out' on both sides + var first = markedSpansBefore(oldFirst, startCh, isInsert); + var last = markedSpansAfter(oldLast, endCh, isInsert); + + // Next, merge those two ends + var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0); + if (first) { + // Fix up .to properties of first + for (var i = 0; i < first.length; ++i) { + var span = first[i]; + if (span.to == null) { + var found = getMarkedSpanFor(last, span.marker); + if (!found) { span.to = startCh; } + else if (sameLine) { span.to = found.to == null ? null : found.to + offset; } + } + } + } + if (last) { + // Fix up .from in last (or move them into first in case of sameLine) + for (var i$1 = 0; i$1 < last.length; ++i$1) { + var span$1 = last[i$1]; + if (span$1.to != null) { span$1.to += offset; } + if (span$1.from == null) { + var found$1 = getMarkedSpanFor(first, span$1.marker); + if (!found$1) { + span$1.from = offset; + if (sameLine) { (first || (first = [])).push(span$1); } + } + } else { + span$1.from += offset; + if (sameLine) { (first || (first = [])).push(span$1); } + } + } + } + // Make sure we didn't create any zero-length spans + if (first) { first = clearEmptySpans(first); } + if (last && last != first) { last = clearEmptySpans(last); } + + var newMarkers = [first]; + if (!sameLine) { + // Fill gap with whole-line-spans + var gap = change.text.length - 2, gapMarkers; + if (gap > 0 && first) + { for (var i$2 = 0; i$2 < first.length; ++i$2) + { if (first[i$2].to == null) + { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } } + for (var i$3 = 0; i$3 < gap; ++i$3) + { newMarkers.push(gapMarkers); } + newMarkers.push(last); + } + return newMarkers + } + + // Remove spans that are empty and don't have a clearWhenEmpty + // option of false. + function clearEmptySpans(spans) { + for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false) + { spans.splice(i--, 1); } + } + if (!spans.length) { return null } + return spans + } + + // Used to 'clip' out readOnly ranges when making a change. + function removeReadOnlyRanges(doc, from, to) { + var markers = null; + doc.iter(from.line, to.line + 1, function (line) { + if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { + var mark = line.markedSpans[i].marker; + if (mark.readOnly && (!markers || indexOf(markers, mark) == -1)) + { (markers || (markers = [])).push(mark); } + } } + }); + if (!markers) { return null } + var parts = [{from: from, to: to}]; + for (var i = 0; i < markers.length; ++i) { + var mk = markers[i], m = mk.find(0); + for (var j = 0; j < parts.length; ++j) { + var p = parts[j]; + if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue } + var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to); + if (dfrom < 0 || !mk.inclusiveLeft && !dfrom) + { newParts.push({from: p.from, to: m.from}); } + if (dto > 0 || !mk.inclusiveRight && !dto) + { newParts.push({from: m.to, to: p.to}); } + parts.splice.apply(parts, newParts); + j += newParts.length - 3; + } + } + return parts + } + + // Connect or disconnect spans from a line. + function detachMarkedSpans(line) { + var spans = line.markedSpans; + if (!spans) { return } + for (var i = 0; i < spans.length; ++i) + { spans[i].marker.detachLine(line); } + line.markedSpans = null; + } + function attachMarkedSpans(line, spans) { + if (!spans) { return } + for (var i = 0; i < spans.length; ++i) + { spans[i].marker.attachLine(line); } + line.markedSpans = spans; + } + + // Helpers used when computing which overlapping collapsed span + // counts as the larger one. + function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 } + function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 } + + // Returns a number indicating which of two overlapping collapsed + // spans is larger (and thus includes the other). Falls back to + // comparing ids when the spans cover exactly the same range. + function compareCollapsedMarkers(a, b) { + var lenDiff = a.lines.length - b.lines.length; + if (lenDiff != 0) { return lenDiff } + var aPos = a.find(), bPos = b.find(); + var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b); + if (fromCmp) { return -fromCmp } + var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b); + if (toCmp) { return toCmp } + return b.id - a.id + } + + // Find out whether a line ends or starts in a collapsed span. If + // so, return the marker for that span. + function collapsedSpanAtSide(line, start) { + var sps = sawCollapsedSpans && line.markedSpans, found; + if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { + sp = sps[i]; + if (sp.marker.collapsed && (start ? sp.from : sp.to) == null && + (!found || compareCollapsedMarkers(found, sp.marker) < 0)) + { found = sp.marker; } + } } + return found + } + function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) } + function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) } + + function collapsedSpanAround(line, ch) { + var sps = sawCollapsedSpans && line.markedSpans, found; + if (sps) { for (var i = 0; i < sps.length; ++i) { + var sp = sps[i]; + if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) && + (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; } + } } + return found + } + + // Test whether there exists a collapsed span that partially + // overlaps (covers the start or end, but not both) of a new span. + // Such overlap is not allowed. + function conflictingCollapsedRange(doc, lineNo, from, to, marker) { + var line = getLine(doc, lineNo); + var sps = sawCollapsedSpans && line.markedSpans; + if (sps) { for (var i = 0; i < sps.length; ++i) { + var sp = sps[i]; + if (!sp.marker.collapsed) { continue } + var found = sp.marker.find(0); + var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker); + var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker); + if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue } + if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) || + fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0)) + { return true } + } } + } + + // A visual line is a line as drawn on the screen. Folding, for + // example, can cause multiple logical lines to appear on the same + // visual line. This finds the start of the visual line that the + // given line is part of (usually that is the line itself). + function visualLine(line) { + var merged; + while (merged = collapsedSpanAtStart(line)) + { line = merged.find(-1, true).line; } + return line + } + + function visualLineEnd(line) { + var merged; + while (merged = collapsedSpanAtEnd(line)) + { line = merged.find(1, true).line; } + return line + } + + // Returns an array of logical lines that continue the visual line + // started by the argument, or undefined if there are no such lines. + function visualLineContinued(line) { + var merged, lines; + while (merged = collapsedSpanAtEnd(line)) { + line = merged.find(1, true).line + ;(lines || (lines = [])).push(line); + } + return lines + } + + // Get the line number of the start of the visual line that the + // given line number is part of. + function visualLineNo(doc, lineN) { + var line = getLine(doc, lineN), vis = visualLine(line); + if (line == vis) { return lineN } + return lineNo(vis) + } + + // Get the line number of the start of the next visual line after + // the given line. + function visualLineEndNo(doc, lineN) { + if (lineN > doc.lastLine()) { return lineN } + var line = getLine(doc, lineN), merged; + if (!lineIsHidden(doc, line)) { return lineN } + while (merged = collapsedSpanAtEnd(line)) + { line = merged.find(1, true).line; } + return lineNo(line) + 1 + } + + // Compute whether a line is hidden. Lines count as hidden when they + // are part of a visual line that starts with another line, or when + // they are entirely covered by collapsed, non-widget span. + function lineIsHidden(doc, line) { + var sps = sawCollapsedSpans && line.markedSpans; + if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { + sp = sps[i]; + if (!sp.marker.collapsed) { continue } + if (sp.from == null) { return true } + if (sp.marker.widgetNode) { continue } + if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp)) + { return true } + } } + } + function lineIsHiddenInner(doc, line, span) { + if (span.to == null) { + var end = span.marker.find(1, true); + return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker)) + } + if (span.marker.inclusiveRight && span.to == line.text.length) + { return true } + for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) { + sp = line.markedSpans[i]; + if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to && + (sp.to == null || sp.to != span.from) && + (sp.marker.inclusiveLeft || span.marker.inclusiveRight) && + lineIsHiddenInner(doc, line, sp)) { return true } + } + } + + // Find the height above the given line. + function heightAtLine(lineObj) { + lineObj = visualLine(lineObj); + + var h = 0, chunk = lineObj.parent; + for (var i = 0; i < chunk.lines.length; ++i) { + var line = chunk.lines[i]; + if (line == lineObj) { break } + else { h += line.height; } + } + for (var p = chunk.parent; p; chunk = p, p = chunk.parent) { + for (var i$1 = 0; i$1 < p.children.length; ++i$1) { + var cur = p.children[i$1]; + if (cur == chunk) { break } + else { h += cur.height; } + } + } + return h + } + + // Compute the character length of a line, taking into account + // collapsed ranges (see markText) that might hide parts, and join + // other lines onto it. + function lineLength(line) { + if (line.height == 0) { return 0 } + var len = line.text.length, merged, cur = line; + while (merged = collapsedSpanAtStart(cur)) { + var found = merged.find(0, true); + cur = found.from.line; + len += found.from.ch - found.to.ch; + } + cur = line; + while (merged = collapsedSpanAtEnd(cur)) { + var found$1 = merged.find(0, true); + len -= cur.text.length - found$1.from.ch; + cur = found$1.to.line; + len += cur.text.length - found$1.to.ch; + } + return len + } + + // Find the longest line in the document. + function findMaxLine(cm) { + var d = cm.display, doc = cm.doc; + d.maxLine = getLine(doc, doc.first); + d.maxLineLength = lineLength(d.maxLine); + d.maxLineChanged = true; + doc.iter(function (line) { + var len = lineLength(line); + if (len > d.maxLineLength) { + d.maxLineLength = len; + d.maxLine = line; + } + }); + } + + // LINE DATA STRUCTURE + + // Line objects. These hold state related to a line, including + // highlighting info (the styles array). + var Line = function(text, markedSpans, estimateHeight) { + this.text = text; + attachMarkedSpans(this, markedSpans); + this.height = estimateHeight ? estimateHeight(this) : 1; + }; + + Line.prototype.lineNo = function () { return lineNo(this) }; + eventMixin(Line); + + // Change the content (text, markers) of a line. Automatically + // invalidates cached information and tries to re-estimate the + // line's height. + function updateLine(line, text, markedSpans, estimateHeight) { + line.text = text; + if (line.stateAfter) { line.stateAfter = null; } + if (line.styles) { line.styles = null; } + if (line.order != null) { line.order = null; } + detachMarkedSpans(line); + attachMarkedSpans(line, markedSpans); + var estHeight = estimateHeight ? estimateHeight(line) : 1; + if (estHeight != line.height) { updateLineHeight(line, estHeight); } + } + + // Detach a line from the document tree and its markers. + function cleanUpLine(line) { + line.parent = null; + detachMarkedSpans(line); + } + + // Convert a style as returned by a mode (either null, or a string + // containing one or more styles) to a CSS style. This is cached, + // and also looks for line-wide styles. + var styleToClassCache = {}, styleToClassCacheWithMode = {}; + function interpretTokenStyle(style, options) { + if (!style || /^\s*$/.test(style)) { return null } + var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache; + return cache[style] || + (cache[style] = style.replace(/\S+/g, "cm-$&")) + } + + // Render the DOM representation of the text of a line. Also builds + // up a 'line map', which points at the DOM nodes that represent + // specific stretches of text, and is used by the measuring code. + // The returned object contains the DOM node, this map, and + // information about line-wide styles that were set by the mode. + function buildLineContent(cm, lineView) { + // The padding-right forces the element to have a 'border', which + // is needed on Webkit to be able to get line-level bounding + // rectangles for it (in measureChar). + var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null); + var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content, + col: 0, pos: 0, cm: cm, + trailingSpace: false, + splitSpaces: cm.getOption("lineWrapping")}; + lineView.measure = {}; + + // Iterate over the logical lines that make up this visual line. + for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) { + var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0); + builder.pos = 0; + builder.addToken = buildToken; + // Optionally wire in some hacks into the token-rendering + // algorithm, to deal with browser quirks. + if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction))) + { builder.addToken = buildTokenBadBidi(builder.addToken, order); } + builder.map = []; + var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line); + insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate)); + if (line.styleClasses) { + if (line.styleClasses.bgClass) + { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); } + if (line.styleClasses.textClass) + { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); } + } + + // Ensure at least a single node is present, for measuring. + if (builder.map.length == 0) + { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); } + + // Store the map and a cache object for the current logical line + if (i == 0) { + lineView.measure.map = builder.map; + lineView.measure.cache = {}; + } else { + (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map) + ;(lineView.measure.caches || (lineView.measure.caches = [])).push({}); + } + } + + // See issue #2901 + if (webkit) { + var last = builder.content.lastChild; + if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab"))) + { builder.content.className = "cm-tab-wrap-hack"; } + } + + signal(cm, "renderLine", cm, lineView.line, builder.pre); + if (builder.pre.className) + { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); } + + return builder + } + + function defaultSpecialCharPlaceholder(ch) { + var token = elt("span", "\u2022", "cm-invalidchar"); + token.title = "\\u" + ch.charCodeAt(0).toString(16); + token.setAttribute("aria-label", token.title); + return token + } + + // Build up the DOM representation for a single token, and add it to + // the line map. Takes care to render special characters separately. + function buildToken(builder, text, style, startStyle, endStyle, css, attributes) { + if (!text) { return } + var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text; + var special = builder.cm.state.specialChars, mustWrap = false; + var content; + if (!special.test(text)) { + builder.col += text.length; + content = document.createTextNode(displayText); + builder.map.push(builder.pos, builder.pos + text.length, content); + if (ie && ie_version < 9) { mustWrap = true; } + builder.pos += text.length; + } else { + content = document.createDocumentFragment(); + var pos = 0; + while (true) { + special.lastIndex = pos; + var m = special.exec(text); + var skipped = m ? m.index - pos : text.length - pos; + if (skipped) { + var txt = document.createTextNode(displayText.slice(pos, pos + skipped)); + if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); } + else { content.appendChild(txt); } + builder.map.push(builder.pos, builder.pos + skipped, txt); + builder.col += skipped; + builder.pos += skipped; + } + if (!m) { break } + pos += skipped + 1; + var txt$1 = (void 0); + if (m[0] == "\t") { + var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize; + txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")); + txt$1.setAttribute("role", "presentation"); + txt$1.setAttribute("cm-text", "\t"); + builder.col += tabWidth; + } else if (m[0] == "\r" || m[0] == "\n") { + txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar")); + txt$1.setAttribute("cm-text", m[0]); + builder.col += 1; + } else { + txt$1 = builder.cm.options.specialCharPlaceholder(m[0]); + txt$1.setAttribute("cm-text", m[0]); + if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); } + else { content.appendChild(txt$1); } + builder.col += 1; + } + builder.map.push(builder.pos, builder.pos + 1, txt$1); + builder.pos++; + } + } + builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32; + if (style || startStyle || endStyle || mustWrap || css || attributes) { + var fullStyle = style || ""; + if (startStyle) { fullStyle += startStyle; } + if (endStyle) { fullStyle += endStyle; } + var token = elt("span", [content], fullStyle, css); + if (attributes) { + for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class") + { token.setAttribute(attr, attributes[attr]); } } + } + return builder.content.appendChild(token) + } + builder.content.appendChild(content); + } + + // Change some spaces to NBSP to prevent the browser from collapsing + // trailing spaces at the end of a line when rendering text (issue #1362). + function splitSpaces(text, trailingBefore) { + if (text.length > 1 && !/ /.test(text)) { return text } + var spaceBefore = trailingBefore, result = ""; + for (var i = 0; i < text.length; i++) { + var ch = text.charAt(i); + if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32)) + { ch = "\u00a0"; } + result += ch; + spaceBefore = ch == " "; + } + return result + } + + // Work around nonsense dimensions being reported for stretches of + // right-to-left text. + function buildTokenBadBidi(inner, order) { + return function (builder, text, style, startStyle, endStyle, css, attributes) { + style = style ? style + " cm-force-border" : "cm-force-border"; + var start = builder.pos, end = start + text.length; + for (;;) { + // Find the part that overlaps with the start of this text + var part = (void 0); + for (var i = 0; i < order.length; i++) { + part = order[i]; + if (part.to > start && part.from <= start) { break } + } + if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) } + inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes); + startStyle = null; + text = text.slice(part.to - start); + start = part.to; + } + } + } + + function buildCollapsedSpan(builder, size, marker, ignoreWidget) { + var widget = !ignoreWidget && marker.widgetNode; + if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); } + if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) { + if (!widget) + { widget = builder.content.appendChild(document.createElement("span")); } + widget.setAttribute("cm-marker", marker.id); + } + if (widget) { + builder.cm.display.input.setUneditable(widget); + builder.content.appendChild(widget); + } + builder.pos += size; + builder.trailingSpace = false; + } + + // Outputs a number of spans to make up a line, taking highlighting + // and marked text into account. + function insertLineContent(line, builder, styles) { + var spans = line.markedSpans, allText = line.text, at = 0; + if (!spans) { + for (var i$1 = 1; i$1 < styles.length; i$1+=2) + { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); } + return + } + + var len = allText.length, pos = 0, i = 1, text = "", style, css; + var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes; + for (;;) { + if (nextChange == pos) { // Update current marker set + spanStyle = spanEndStyle = spanStartStyle = css = ""; + attributes = null; + collapsed = null; nextChange = Infinity; + var foundBookmarks = [], endStyles = (void 0); + for (var j = 0; j < spans.length; ++j) { + var sp = spans[j], m = sp.marker; + if (m.type == "bookmark" && sp.from == pos && m.widgetNode) { + foundBookmarks.push(m); + } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) { + if (sp.to != null && sp.to != pos && nextChange > sp.to) { + nextChange = sp.to; + spanEndStyle = ""; + } + if (m.className) { spanStyle += " " + m.className; } + if (m.css) { css = (css ? css + ";" : "") + m.css; } + if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; } + if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); } + // support for the old title property + // https://github.com/codemirror/CodeMirror/pull/5673 + if (m.title) { (attributes || (attributes = {})).title = m.title; } + if (m.attributes) { + for (var attr in m.attributes) + { (attributes || (attributes = {}))[attr] = m.attributes[attr]; } + } + if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)) + { collapsed = sp; } + } else if (sp.from > pos && nextChange > sp.from) { + nextChange = sp.from; + } + } + if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2) + { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } } + + if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2) + { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } } + if (collapsed && (collapsed.from || 0) == pos) { + buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos, + collapsed.marker, collapsed.from == null); + if (collapsed.to == null) { return } + if (collapsed.to == pos) { collapsed = false; } + } + } + if (pos >= len) { break } + + var upto = Math.min(len, nextChange); + while (true) { + if (text) { + var end = pos + text.length; + if (!collapsed) { + var tokenText = end > upto ? text.slice(0, upto - pos) : text; + builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle, + spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes); + } + if (end >= upto) {text = text.slice(upto - pos); pos = upto; break} + pos = end; + spanStartStyle = ""; + } + text = allText.slice(at, at = styles[i++]); + style = interpretTokenStyle(styles[i++], builder.cm.options); + } + } + } + + + // These objects are used to represent the visible (currently drawn) + // part of the document. A LineView may correspond to multiple + // logical lines, if those are connected by collapsed ranges. + function LineView(doc, line, lineN) { + // The starting line + this.line = line; + // Continuing lines, if any + this.rest = visualLineContinued(line); + // Number of logical lines in this visual line + this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1; + this.node = this.text = null; + this.hidden = lineIsHidden(doc, line); + } + + // Create a range of LineView objects for the given lines. + function buildViewArray(cm, from, to) { + var array = [], nextPos; + for (var pos = from; pos < to; pos = nextPos) { + var view = new LineView(cm.doc, getLine(cm.doc, pos), pos); + nextPos = pos + view.size; + array.push(view); + } + return array + } + + var operationGroup = null; + + function pushOperation(op) { + if (operationGroup) { + operationGroup.ops.push(op); + } else { + op.ownsGroup = operationGroup = { + ops: [op], + delayedCallbacks: [] + }; + } + } + + function fireCallbacksForOps(group) { + // Calls delayed callbacks and cursorActivity handlers until no + // new ones appear + var callbacks = group.delayedCallbacks, i = 0; + do { + for (; i < callbacks.length; i++) + { callbacks[i].call(null); } + for (var j = 0; j < group.ops.length; j++) { + var op = group.ops[j]; + if (op.cursorActivityHandlers) + { while (op.cursorActivityCalled < op.cursorActivityHandlers.length) + { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } } + } + } while (i < callbacks.length) + } + + function finishOperation(op, endCb) { + var group = op.ownsGroup; + if (!group) { return } + + try { fireCallbacksForOps(group); } + finally { + operationGroup = null; + endCb(group); + } + } + + var orphanDelayedCallbacks = null; + + // Often, we want to signal events at a point where we are in the + // middle of some work, but don't want the handler to start calling + // other methods on the editor, which might be in an inconsistent + // state or simply not expect any other events to happen. + // signalLater looks whether there are any handlers, and schedules + // them to be executed when the last operation ends, or, if no + // operation is active, when a timeout fires. + function signalLater(emitter, type /*, values...*/) { + var arr = getHandlers(emitter, type); + if (!arr.length) { return } + var args = Array.prototype.slice.call(arguments, 2), list; + if (operationGroup) { + list = operationGroup.delayedCallbacks; + } else if (orphanDelayedCallbacks) { + list = orphanDelayedCallbacks; + } else { + list = orphanDelayedCallbacks = []; + setTimeout(fireOrphanDelayed, 0); + } + var loop = function ( i ) { + list.push(function () { return arr[i].apply(null, args); }); + }; + + for (var i = 0; i < arr.length; ++i) + loop( i ); + } + + function fireOrphanDelayed() { + var delayed = orphanDelayedCallbacks; + orphanDelayedCallbacks = null; + for (var i = 0; i < delayed.length; ++i) { delayed[i](); } + } + + // When an aspect of a line changes, a string is added to + // lineView.changes. This updates the relevant part of the line's + // DOM structure. + function updateLineForChanges(cm, lineView, lineN, dims) { + for (var j = 0; j < lineView.changes.length; j++) { + var type = lineView.changes[j]; + if (type == "text") { updateLineText(cm, lineView); } + else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); } + else if (type == "class") { updateLineClasses(cm, lineView); } + else if (type == "widget") { updateLineWidgets(cm, lineView, dims); } + } + lineView.changes = null; + } + + // Lines with gutter elements, widgets or a background class need to + // be wrapped, and have the extra elements added to the wrapper div + function ensureLineWrapped(lineView) { + if (lineView.node == lineView.text) { + lineView.node = elt("div", null, null, "position: relative"); + if (lineView.text.parentNode) + { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); } + lineView.node.appendChild(lineView.text); + if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; } + } + return lineView.node + } + + function updateLineBackground(cm, lineView) { + var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass; + if (cls) { cls += " CodeMirror-linebackground"; } + if (lineView.background) { + if (cls) { lineView.background.className = cls; } + else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; } + } else if (cls) { + var wrap = ensureLineWrapped(lineView); + lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild); + cm.display.input.setUneditable(lineView.background); + } + } + + // Wrapper around buildLineContent which will reuse the structure + // in display.externalMeasured when possible. + function getLineContent(cm, lineView) { + var ext = cm.display.externalMeasured; + if (ext && ext.line == lineView.line) { + cm.display.externalMeasured = null; + lineView.measure = ext.measure; + return ext.built + } + return buildLineContent(cm, lineView) + } + + // Redraw the line's text. Interacts with the background and text + // classes because the mode may output tokens that influence these + // classes. + function updateLineText(cm, lineView) { + var cls = lineView.text.className; + var built = getLineContent(cm, lineView); + if (lineView.text == lineView.node) { lineView.node = built.pre; } + lineView.text.parentNode.replaceChild(built.pre, lineView.text); + lineView.text = built.pre; + if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) { + lineView.bgClass = built.bgClass; + lineView.textClass = built.textClass; + updateLineClasses(cm, lineView); + } else if (cls) { + lineView.text.className = cls; + } + } + + function updateLineClasses(cm, lineView) { + updateLineBackground(cm, lineView); + if (lineView.line.wrapClass) + { ensureLineWrapped(lineView).className = lineView.line.wrapClass; } + else if (lineView.node != lineView.text) + { lineView.node.className = ""; } + var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass; + lineView.text.className = textClass || ""; + } + + function updateLineGutter(cm, lineView, lineN, dims) { + if (lineView.gutter) { + lineView.node.removeChild(lineView.gutter); + lineView.gutter = null; + } + if (lineView.gutterBackground) { + lineView.node.removeChild(lineView.gutterBackground); + lineView.gutterBackground = null; + } + if (lineView.line.gutterClass) { + var wrap = ensureLineWrapped(lineView); + lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass, + ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px")); + cm.display.input.setUneditable(lineView.gutterBackground); + wrap.insertBefore(lineView.gutterBackground, lineView.text); + } + var markers = lineView.line.gutterMarkers; + if (cm.options.lineNumbers || markers) { + var wrap$1 = ensureLineWrapped(lineView); + var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px")); + gutterWrap.setAttribute("aria-hidden", "true"); + cm.display.input.setUneditable(gutterWrap); + wrap$1.insertBefore(gutterWrap, lineView.text); + if (lineView.line.gutterClass) + { gutterWrap.className += " " + lineView.line.gutterClass; } + if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"])) + { lineView.lineNumber = gutterWrap.appendChild( + elt("div", lineNumberFor(cm.options, lineN), + "CodeMirror-linenumber CodeMirror-gutter-elt", + ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); } + if (markers) { for (var k = 0; k < cm.display.gutterSpecs.length; ++k) { + var id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id]; + if (found) + { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", + ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); } + } } + } + } + + function updateLineWidgets(cm, lineView, dims) { + if (lineView.alignable) { lineView.alignable = null; } + var isWidget = classTest("CodeMirror-linewidget"); + for (var node = lineView.node.firstChild, next = (void 0); node; node = next) { + next = node.nextSibling; + if (isWidget.test(node.className)) { lineView.node.removeChild(node); } + } + insertLineWidgets(cm, lineView, dims); + } + + // Build a line's DOM representation from scratch + function buildLineElement(cm, lineView, lineN, dims) { + var built = getLineContent(cm, lineView); + lineView.text = lineView.node = built.pre; + if (built.bgClass) { lineView.bgClass = built.bgClass; } + if (built.textClass) { lineView.textClass = built.textClass; } + + updateLineClasses(cm, lineView); + updateLineGutter(cm, lineView, lineN, dims); + insertLineWidgets(cm, lineView, dims); + return lineView.node + } + + // A lineView may contain multiple logical lines (when merged by + // collapsed spans). The widgets for all of them need to be drawn. + function insertLineWidgets(cm, lineView, dims) { + insertLineWidgetsFor(cm, lineView.line, lineView, dims, true); + if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) + { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } } + } + + function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) { + if (!line.widgets) { return } + var wrap = ensureLineWrapped(lineView); + for (var i = 0, ws = line.widgets; i < ws.length; ++i) { + var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget" + (widget.className ? " " + widget.className : "")); + if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); } + positionLineWidget(widget, node, lineView, dims); + cm.display.input.setUneditable(node); + if (allowAbove && widget.above) + { wrap.insertBefore(node, lineView.gutter || lineView.text); } + else + { wrap.appendChild(node); } + signalLater(widget, "redraw"); + } + } + + function positionLineWidget(widget, node, lineView, dims) { + if (widget.noHScroll) { + (lineView.alignable || (lineView.alignable = [])).push(node); + var width = dims.wrapperWidth; + node.style.left = dims.fixedPos + "px"; + if (!widget.coverGutter) { + width -= dims.gutterTotalWidth; + node.style.paddingLeft = dims.gutterTotalWidth + "px"; + } + node.style.width = width + "px"; + } + if (widget.coverGutter) { + node.style.zIndex = 5; + node.style.position = "relative"; + if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; } + } + } + + function widgetHeight(widget) { + if (widget.height != null) { return widget.height } + var cm = widget.doc.cm; + if (!cm) { return 0 } + if (!contains(document.body, widget.node)) { + var parentStyle = "position: relative;"; + if (widget.coverGutter) + { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; } + if (widget.noHScroll) + { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; } + removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle)); + } + return widget.height = widget.node.parentNode.offsetHeight + } + + // Return true when the given mouse event happened in a widget + function eventInWidget(display, e) { + for (var n = e_target(e); n != display.wrapper; n = n.parentNode) { + if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") || + (n.parentNode == display.sizer && n != display.mover)) + { return true } + } + } + + // POSITION MEASUREMENT + + function paddingTop(display) {return display.lineSpace.offsetTop} + function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight} + function paddingH(display) { + if (display.cachedPaddingH) { return display.cachedPaddingH } + var e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-line-like")); + var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle; + var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}; + if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; } + return data + } + + function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth } + function displayWidth(cm) { + return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth + } + function displayHeight(cm) { + return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight + } + + // Ensure the lineView.wrapping.heights array is populated. This is + // an array of bottom offsets for the lines that make up a drawn + // line. When lineWrapping is on, there might be more than one + // height. + function ensureLineHeights(cm, lineView, rect) { + var wrapping = cm.options.lineWrapping; + var curWidth = wrapping && displayWidth(cm); + if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) { + var heights = lineView.measure.heights = []; + if (wrapping) { + lineView.measure.width = curWidth; + var rects = lineView.text.firstChild.getClientRects(); + for (var i = 0; i < rects.length - 1; i++) { + var cur = rects[i], next = rects[i + 1]; + if (Math.abs(cur.bottom - next.bottom) > 2) + { heights.push((cur.bottom + next.top) / 2 - rect.top); } + } + } + heights.push(rect.bottom - rect.top); + } + } + + // Find a line map (mapping character offsets to text nodes) and a + // measurement cache for the given line number. (A line view might + // contain multiple lines when collapsed ranges are present.) + function mapFromLineView(lineView, line, lineN) { + if (lineView.line == line) + { return {map: lineView.measure.map, cache: lineView.measure.cache} } + if (lineView.rest) { + for (var i = 0; i < lineView.rest.length; i++) + { if (lineView.rest[i] == line) + { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } } + for (var i$1 = 0; i$1 < lineView.rest.length; i$1++) + { if (lineNo(lineView.rest[i$1]) > lineN) + { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } } + } + } + + // Render a line into the hidden node display.externalMeasured. Used + // when measurement is needed for a line that's not in the viewport. + function updateExternalMeasurement(cm, line) { + line = visualLine(line); + var lineN = lineNo(line); + var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN); + view.lineN = lineN; + var built = view.built = buildLineContent(cm, view); + view.text = built.pre; + removeChildrenAndAdd(cm.display.lineMeasure, built.pre); + return view + } + + // Get a {top, bottom, left, right} box (in line-local coordinates) + // for a given character. + function measureChar(cm, line, ch, bias) { + return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias) + } + + // Find a line view that corresponds to the given line number. + function findViewForLine(cm, lineN) { + if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo) + { return cm.display.view[findViewIndex(cm, lineN)] } + var ext = cm.display.externalMeasured; + if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size) + { return ext } + } + + // Measurement can be split in two steps, the set-up work that + // applies to the whole line, and the measurement of the actual + // character. Functions like coordsChar, that need to do a lot of + // measurements in a row, can thus ensure that the set-up work is + // only done once. + function prepareMeasureForLine(cm, line) { + var lineN = lineNo(line); + var view = findViewForLine(cm, lineN); + if (view && !view.text) { + view = null; + } else if (view && view.changes) { + updateLineForChanges(cm, view, lineN, getDimensions(cm)); + cm.curOp.forceUpdate = true; + } + if (!view) + { view = updateExternalMeasurement(cm, line); } + + var info = mapFromLineView(view, line, lineN); + return { + line: line, view: view, rect: null, + map: info.map, cache: info.cache, before: info.before, + hasHeights: false + } + } + + // Given a prepared measurement object, measures the position of an + // actual character (or fetches it from the cache). + function measureCharPrepared(cm, prepared, ch, bias, varHeight) { + if (prepared.before) { ch = -1; } + var key = ch + (bias || ""), found; + if (prepared.cache.hasOwnProperty(key)) { + found = prepared.cache[key]; + } else { + if (!prepared.rect) + { prepared.rect = prepared.view.text.getBoundingClientRect(); } + if (!prepared.hasHeights) { + ensureLineHeights(cm, prepared.view, prepared.rect); + prepared.hasHeights = true; + } + found = measureCharInner(cm, prepared, ch, bias); + if (!found.bogus) { prepared.cache[key] = found; } + } + return {left: found.left, right: found.right, + top: varHeight ? found.rtop : found.top, + bottom: varHeight ? found.rbottom : found.bottom} + } + + var nullRect = {left: 0, right: 0, top: 0, bottom: 0}; + + function nodeAndOffsetInLineMap(map, ch, bias) { + var node, start, end, collapse, mStart, mEnd; + // First, search the line map for the text node corresponding to, + // or closest to, the target character. + for (var i = 0; i < map.length; i += 3) { + mStart = map[i]; + mEnd = map[i + 1]; + if (ch < mStart) { + start = 0; end = 1; + collapse = "left"; + } else if (ch < mEnd) { + start = ch - mStart; + end = start + 1; + } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) { + end = mEnd - mStart; + start = end - 1; + if (ch >= mEnd) { collapse = "right"; } + } + if (start != null) { + node = map[i + 2]; + if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right")) + { collapse = bias; } + if (bias == "left" && start == 0) + { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) { + node = map[(i -= 3) + 2]; + collapse = "left"; + } } + if (bias == "right" && start == mEnd - mStart) + { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) { + node = map[(i += 3) + 2]; + collapse = "right"; + } } + break + } + } + return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd} + } + + function getUsefulRect(rects, bias) { + var rect = nullRect; + if (bias == "left") { for (var i = 0; i < rects.length; i++) { + if ((rect = rects[i]).left != rect.right) { break } + } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) { + if ((rect = rects[i$1]).left != rect.right) { break } + } } + return rect + } + + function measureCharInner(cm, prepared, ch, bias) { + var place = nodeAndOffsetInLineMap(prepared.map, ch, bias); + var node = place.node, start = place.start, end = place.end, collapse = place.collapse; + + var rect; + if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates. + for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned + while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; } + while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; } + if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) + { rect = node.parentNode.getBoundingClientRect(); } + else + { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); } + if (rect.left || rect.right || start == 0) { break } + end = start; + start = start - 1; + collapse = "right"; + } + if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); } + } else { // If it is a widget, simply get the box for the whole widget. + if (start > 0) { collapse = bias = "right"; } + var rects; + if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1) + { rect = rects[bias == "right" ? rects.length - 1 : 0]; } + else + { rect = node.getBoundingClientRect(); } + } + if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) { + var rSpan = node.parentNode.getClientRects()[0]; + if (rSpan) + { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; } + else + { rect = nullRect; } + } + + var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top; + var mid = (rtop + rbot) / 2; + var heights = prepared.view.measure.heights; + var i = 0; + for (; i < heights.length - 1; i++) + { if (mid < heights[i]) { break } } + var top = i ? heights[i - 1] : 0, bot = heights[i]; + var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left, + right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left, + top: top, bottom: bot}; + if (!rect.left && !rect.right) { result.bogus = true; } + if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; } + + return result + } + + // Work around problem with bounding client rects on ranges being + // returned incorrectly when zoomed on IE10 and below. + function maybeUpdateRectForZooming(measure, rect) { + if (!window.screen || screen.logicalXDPI == null || + screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure)) + { return rect } + var scaleX = screen.logicalXDPI / screen.deviceXDPI; + var scaleY = screen.logicalYDPI / screen.deviceYDPI; + return {left: rect.left * scaleX, right: rect.right * scaleX, + top: rect.top * scaleY, bottom: rect.bottom * scaleY} + } + + function clearLineMeasurementCacheFor(lineView) { + if (lineView.measure) { + lineView.measure.cache = {}; + lineView.measure.heights = null; + if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) + { lineView.measure.caches[i] = {}; } } + } + } + + function clearLineMeasurementCache(cm) { + cm.display.externalMeasure = null; + removeChildren(cm.display.lineMeasure); + for (var i = 0; i < cm.display.view.length; i++) + { clearLineMeasurementCacheFor(cm.display.view[i]); } + } + + function clearCaches(cm) { + clearLineMeasurementCache(cm); + cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null; + if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; } + cm.display.lineNumChars = null; + } + + function pageScrollX(doc) { + // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206 + // which causes page_Offset and bounding client rects to use + // different reference viewports and invalidate our calculations. + if (chrome && android) { return -(doc.body.getBoundingClientRect().left - parseInt(getComputedStyle(doc.body).marginLeft)) } + return doc.defaultView.pageXOffset || (doc.documentElement || doc.body).scrollLeft + } + function pageScrollY(doc) { + if (chrome && android) { return -(doc.body.getBoundingClientRect().top - parseInt(getComputedStyle(doc.body).marginTop)) } + return doc.defaultView.pageYOffset || (doc.documentElement || doc.body).scrollTop + } + + function widgetTopHeight(lineObj) { + var ref = visualLine(lineObj); + var widgets = ref.widgets; + var height = 0; + if (widgets) { for (var i = 0; i < widgets.length; ++i) { if (widgets[i].above) + { height += widgetHeight(widgets[i]); } } } + return height + } + + // Converts a {top, bottom, left, right} box from line-local + // coordinates into another coordinate system. Context may be one of + // "line", "div" (display.lineDiv), "local"./null (editor), "window", + // or "page". + function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) { + if (!includeWidgets) { + var height = widgetTopHeight(lineObj); + rect.top += height; rect.bottom += height; + } + if (context == "line") { return rect } + if (!context) { context = "local"; } + var yOff = heightAtLine(lineObj); + if (context == "local") { yOff += paddingTop(cm.display); } + else { yOff -= cm.display.viewOffset; } + if (context == "page" || context == "window") { + var lOff = cm.display.lineSpace.getBoundingClientRect(); + yOff += lOff.top + (context == "window" ? 0 : pageScrollY(doc(cm))); + var xOff = lOff.left + (context == "window" ? 0 : pageScrollX(doc(cm))); + rect.left += xOff; rect.right += xOff; + } + rect.top += yOff; rect.bottom += yOff; + return rect + } + + // Coverts a box from "div" coords to another coordinate system. + // Context may be "window", "page", "div", or "local"./null. + function fromCoordSystem(cm, coords, context) { + if (context == "div") { return coords } + var left = coords.left, top = coords.top; + // First move into "page" coordinate system + if (context == "page") { + left -= pageScrollX(doc(cm)); + top -= pageScrollY(doc(cm)); + } else if (context == "local" || !context) { + var localBox = cm.display.sizer.getBoundingClientRect(); + left += localBox.left; + top += localBox.top; + } + + var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect(); + return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top} + } + + function charCoords(cm, pos, context, lineObj, bias) { + if (!lineObj) { lineObj = getLine(cm.doc, pos.line); } + return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context) + } + + // Returns a box for a given cursor position, which may have an + // 'other' property containing the position of the secondary cursor + // on a bidi boundary. + // A cursor Pos(line, char, "before") is on the same visual line as `char - 1` + // and after `char - 1` in writing order of `char - 1` + // A cursor Pos(line, char, "after") is on the same visual line as `char` + // and before `char` in writing order of `char` + // Examples (upper-case letters are RTL, lower-case are LTR): + // Pos(0, 1, ...) + // before after + // ab a|b a|b + // aB a|B aB| + // Ab |Ab A|b + // AB B|A B|A + // Every position after the last character on a line is considered to stick + // to the last character on the line. + function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) { + lineObj = lineObj || getLine(cm.doc, pos.line); + if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); } + function get(ch, right) { + var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight); + if (right) { m.left = m.right; } else { m.right = m.left; } + return intoCoordSystem(cm, lineObj, m, context) + } + var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky; + if (ch >= lineObj.text.length) { + ch = lineObj.text.length; + sticky = "before"; + } else if (ch <= 0) { + ch = 0; + sticky = "after"; + } + if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") } + + function getBidi(ch, partPos, invert) { + var part = order[partPos], right = part.level == 1; + return get(invert ? ch - 1 : ch, right != invert) + } + var partPos = getBidiPartAt(order, ch, sticky); + var other = bidiOther; + var val = getBidi(ch, partPos, sticky == "before"); + if (other != null) { val.other = getBidi(ch, other, sticky != "before"); } + return val + } + + // Used to cheaply estimate the coordinates for a position. Used for + // intermediate scroll updates. + function estimateCoords(cm, pos) { + var left = 0; + pos = clipPos(cm.doc, pos); + if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; } + var lineObj = getLine(cm.doc, pos.line); + var top = heightAtLine(lineObj) + paddingTop(cm.display); + return {left: left, right: left, top: top, bottom: top + lineObj.height} + } + + // Positions returned by coordsChar contain some extra information. + // xRel is the relative x position of the input coordinates compared + // to the found position (so xRel > 0 means the coordinates are to + // the right of the character position, for example). When outside + // is true, that means the coordinates lie outside the line's + // vertical range. + function PosWithInfo(line, ch, sticky, outside, xRel) { + var pos = Pos(line, ch, sticky); + pos.xRel = xRel; + if (outside) { pos.outside = outside; } + return pos + } + + // Compute the character position closest to the given coordinates. + // Input must be lineSpace-local ("div" coordinate system). + function coordsChar(cm, x, y) { + var doc = cm.doc; + y += cm.display.viewOffset; + if (y < 0) { return PosWithInfo(doc.first, 0, null, -1, -1) } + var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1; + if (lineN > last) + { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1) } + if (x < 0) { x = 0; } + + var lineObj = getLine(doc, lineN); + for (;;) { + var found = coordsCharInner(cm, lineObj, lineN, x, y); + var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0)); + if (!collapsed) { return found } + var rangeEnd = collapsed.find(1); + if (rangeEnd.line == lineN) { return rangeEnd } + lineObj = getLine(doc, lineN = rangeEnd.line); + } + } + + function wrappedLineExtent(cm, lineObj, preparedMeasure, y) { + y -= widgetTopHeight(lineObj); + var end = lineObj.text.length; + var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0); + end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end); + return {begin: begin, end: end} + } + + function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) { + if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); } + var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top; + return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop) + } + + // Returns true if the given side of a box is after the given + // coordinates, in top-to-bottom, left-to-right order. + function boxIsAfter(box, x, y, left) { + return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x + } + + function coordsCharInner(cm, lineObj, lineNo, x, y) { + // Move y into line-local coordinate space + y -= heightAtLine(lineObj); + var preparedMeasure = prepareMeasureForLine(cm, lineObj); + // When directly calling `measureCharPrepared`, we have to adjust + // for the widgets at this line. + var widgetHeight = widgetTopHeight(lineObj); + var begin = 0, end = lineObj.text.length, ltr = true; + + var order = getOrder(lineObj, cm.doc.direction); + // If the line isn't plain left-to-right text, first figure out + // which bidi section the coordinates fall into. + if (order) { + var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart) + (cm, lineObj, lineNo, preparedMeasure, order, x, y); + ltr = part.level != 1; + // The awkward -1 offsets are needed because findFirst (called + // on these below) will treat its first bound as inclusive, + // second as exclusive, but we want to actually address the + // characters in the part's range + begin = ltr ? part.from : part.to - 1; + end = ltr ? part.to : part.from - 1; + } + + // A binary search to find the first character whose bounding box + // starts after the coordinates. If we run across any whose box wrap + // the coordinates, store that. + var chAround = null, boxAround = null; + var ch = findFirst(function (ch) { + var box = measureCharPrepared(cm, preparedMeasure, ch); + box.top += widgetHeight; box.bottom += widgetHeight; + if (!boxIsAfter(box, x, y, false)) { return false } + if (box.top <= y && box.left <= x) { + chAround = ch; + boxAround = box; + } + return true + }, begin, end); + + var baseX, sticky, outside = false; + // If a box around the coordinates was found, use that + if (boxAround) { + // Distinguish coordinates nearer to the left or right side of the box + var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr; + ch = chAround + (atStart ? 0 : 1); + sticky = atStart ? "after" : "before"; + baseX = atLeft ? boxAround.left : boxAround.right; + } else { + // (Adjust for extended bound, if necessary.) + if (!ltr && (ch == end || ch == begin)) { ch++; } + // To determine which side to associate with, get the box to the + // left of the character and compare it's vertical position to the + // coordinates + sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" : + (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ? + "after" : "before"; + // Now get accurate coordinates for this place, in order to get a + // base X position + var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure); + baseX = coords.left; + outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0; + } + + ch = skipExtendingChars(lineObj.text, ch, 1); + return PosWithInfo(lineNo, ch, sticky, outside, x - baseX) + } + + function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) { + // Bidi parts are sorted left-to-right, and in a non-line-wrapping + // situation, we can take this ordering to correspond to the visual + // ordering. This finds the first part whose end is after the given + // coordinates. + var index = findFirst(function (i) { + var part = order[i], ltr = part.level != 1; + return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"), + "line", lineObj, preparedMeasure), x, y, true) + }, 0, order.length - 1); + var part = order[index]; + // If this isn't the first part, the part's start is also after + // the coordinates, and the coordinates aren't on the same line as + // that start, move one part back. + if (index > 0) { + var ltr = part.level != 1; + var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"), + "line", lineObj, preparedMeasure); + if (boxIsAfter(start, x, y, true) && start.top > y) + { part = order[index - 1]; } + } + return part + } + + function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) { + // In a wrapped line, rtl text on wrapping boundaries can do things + // that don't correspond to the ordering in our `order` array at + // all, so a binary search doesn't work, and we want to return a + // part that only spans one line so that the binary search in + // coordsCharInner is safe. As such, we first find the extent of the + // wrapped line, and then do a flat search in which we discard any + // spans that aren't on the line. + var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y); + var begin = ref.begin; + var end = ref.end; + if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; } + var part = null, closestDist = null; + for (var i = 0; i < order.length; i++) { + var p = order[i]; + if (p.from >= end || p.to <= begin) { continue } + var ltr = p.level != 1; + var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right; + // Weigh against spans ending before this, so that they are only + // picked if nothing ends after + var dist = endX < x ? x - endX + 1e9 : endX - x; + if (!part || closestDist > dist) { + part = p; + closestDist = dist; + } + } + if (!part) { part = order[order.length - 1]; } + // Clip the part to the wrapped line. + if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; } + if (part.to > end) { part = {from: part.from, to: end, level: part.level}; } + return part + } + + var measureText; + // Compute the default text height. + function textHeight(display) { + if (display.cachedTextHeight != null) { return display.cachedTextHeight } + if (measureText == null) { + measureText = elt("pre", null, "CodeMirror-line-like"); + // Measure a bunch of lines, for browsers that compute + // fractional heights. + for (var i = 0; i < 49; ++i) { + measureText.appendChild(document.createTextNode("x")); + measureText.appendChild(elt("br")); + } + measureText.appendChild(document.createTextNode("x")); + } + removeChildrenAndAdd(display.measure, measureText); + var height = measureText.offsetHeight / 50; + if (height > 3) { display.cachedTextHeight = height; } + removeChildren(display.measure); + return height || 1 + } + + // Compute the default character width. + function charWidth(display) { + if (display.cachedCharWidth != null) { return display.cachedCharWidth } + var anchor = elt("span", "xxxxxxxxxx"); + var pre = elt("pre", [anchor], "CodeMirror-line-like"); + removeChildrenAndAdd(display.measure, pre); + var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10; + if (width > 2) { display.cachedCharWidth = width; } + return width || 10 + } + + // Do a bulk-read of the DOM positions and sizes needed to draw the + // view, so that we don't interleave reading and writing to the DOM. + function getDimensions(cm) { + var d = cm.display, left = {}, width = {}; + var gutterLeft = d.gutters.clientLeft; + for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) { + var id = cm.display.gutterSpecs[i].className; + left[id] = n.offsetLeft + n.clientLeft + gutterLeft; + width[id] = n.clientWidth; + } + return {fixedPos: compensateForHScroll(d), + gutterTotalWidth: d.gutters.offsetWidth, + gutterLeft: left, + gutterWidth: width, + wrapperWidth: d.wrapper.clientWidth} + } + + // Computes display.scroller.scrollLeft + display.gutters.offsetWidth, + // but using getBoundingClientRect to get a sub-pixel-accurate + // result. + function compensateForHScroll(display) { + return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left + } + + // Returns a function that estimates the height of a line, to use as + // first approximation until the line becomes visible (and is thus + // properly measurable). + function estimateHeight(cm) { + var th = textHeight(cm.display), wrapping = cm.options.lineWrapping; + var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3); + return function (line) { + if (lineIsHidden(cm.doc, line)) { return 0 } + + var widgetsHeight = 0; + if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) { + if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; } + } } + + if (wrapping) + { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th } + else + { return widgetsHeight + th } + } + } + + function estimateLineHeights(cm) { + var doc = cm.doc, est = estimateHeight(cm); + doc.iter(function (line) { + var estHeight = est(line); + if (estHeight != line.height) { updateLineHeight(line, estHeight); } + }); + } + + // Given a mouse event, find the corresponding position. If liberal + // is false, it checks whether a gutter or scrollbar was clicked, + // and returns null if it was. forRect is used by rectangular + // selections, and tries to estimate a character position even for + // coordinates beyond the right of the text. + function posFromMouse(cm, e, liberal, forRect) { + var display = cm.display; + if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null } + + var x, y, space = display.lineSpace.getBoundingClientRect(); + // Fails unpredictably on IE[67] when mouse is dragged around quickly. + try { x = e.clientX - space.left; y = e.clientY - space.top; } + catch (e$1) { return null } + var coords = coordsChar(cm, x, y), line; + if (forRect && coords.xRel > 0 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) { + var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length; + coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff)); + } + return coords + } + + // Find the view element corresponding to a given line. Return null + // when the line isn't visible. + function findViewIndex(cm, n) { + if (n >= cm.display.viewTo) { return null } + n -= cm.display.viewFrom; + if (n < 0) { return null } + var view = cm.display.view; + for (var i = 0; i < view.length; i++) { + n -= view[i].size; + if (n < 0) { return i } + } + } + + // Updates the display.view data structure for a given change to the + // document. From and to are in pre-change coordinates. Lendiff is + // the amount of lines added or subtracted by the change. This is + // used for changes that span multiple lines, or change the way + // lines are divided into visual lines. regLineChange (below) + // registers single-line changes. + function regChange(cm, from, to, lendiff) { + if (from == null) { from = cm.doc.first; } + if (to == null) { to = cm.doc.first + cm.doc.size; } + if (!lendiff) { lendiff = 0; } + + var display = cm.display; + if (lendiff && to < display.viewTo && + (display.updateLineNumbers == null || display.updateLineNumbers > from)) + { display.updateLineNumbers = from; } + + cm.curOp.viewChanged = true; + + if (from >= display.viewTo) { // Change after + if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo) + { resetView(cm); } + } else if (to <= display.viewFrom) { // Change before + if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) { + resetView(cm); + } else { + display.viewFrom += lendiff; + display.viewTo += lendiff; + } + } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap + resetView(cm); + } else if (from <= display.viewFrom) { // Top overlap + var cut = viewCuttingPoint(cm, to, to + lendiff, 1); + if (cut) { + display.view = display.view.slice(cut.index); + display.viewFrom = cut.lineN; + display.viewTo += lendiff; + } else { + resetView(cm); + } + } else if (to >= display.viewTo) { // Bottom overlap + var cut$1 = viewCuttingPoint(cm, from, from, -1); + if (cut$1) { + display.view = display.view.slice(0, cut$1.index); + display.viewTo = cut$1.lineN; + } else { + resetView(cm); + } + } else { // Gap in the middle + var cutTop = viewCuttingPoint(cm, from, from, -1); + var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1); + if (cutTop && cutBot) { + display.view = display.view.slice(0, cutTop.index) + .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN)) + .concat(display.view.slice(cutBot.index)); + display.viewTo += lendiff; + } else { + resetView(cm); + } + } + + var ext = display.externalMeasured; + if (ext) { + if (to < ext.lineN) + { ext.lineN += lendiff; } + else if (from < ext.lineN + ext.size) + { display.externalMeasured = null; } + } + } + + // Register a change to a single line. Type must be one of "text", + // "gutter", "class", "widget" + function regLineChange(cm, line, type) { + cm.curOp.viewChanged = true; + var display = cm.display, ext = cm.display.externalMeasured; + if (ext && line >= ext.lineN && line < ext.lineN + ext.size) + { display.externalMeasured = null; } + + if (line < display.viewFrom || line >= display.viewTo) { return } + var lineView = display.view[findViewIndex(cm, line)]; + if (lineView.node == null) { return } + var arr = lineView.changes || (lineView.changes = []); + if (indexOf(arr, type) == -1) { arr.push(type); } + } + + // Clear the view. + function resetView(cm) { + cm.display.viewFrom = cm.display.viewTo = cm.doc.first; + cm.display.view = []; + cm.display.viewOffset = 0; + } + + function viewCuttingPoint(cm, oldN, newN, dir) { + var index = findViewIndex(cm, oldN), diff, view = cm.display.view; + if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size) + { return {index: index, lineN: newN} } + var n = cm.display.viewFrom; + for (var i = 0; i < index; i++) + { n += view[i].size; } + if (n != oldN) { + if (dir > 0) { + if (index == view.length - 1) { return null } + diff = (n + view[index].size) - oldN; + index++; + } else { + diff = n - oldN; + } + oldN += diff; newN += diff; + } + while (visualLineNo(cm.doc, newN) != newN) { + if (index == (dir < 0 ? 0 : view.length - 1)) { return null } + newN += dir * view[index - (dir < 0 ? 1 : 0)].size; + index += dir; + } + return {index: index, lineN: newN} + } + + // Force the view to cover a given range, adding empty view element + // or clipping off existing ones as needed. + function adjustView(cm, from, to) { + var display = cm.display, view = display.view; + if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) { + display.view = buildViewArray(cm, from, to); + display.viewFrom = from; + } else { + if (display.viewFrom > from) + { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); } + else if (display.viewFrom < from) + { display.view = display.view.slice(findViewIndex(cm, from)); } + display.viewFrom = from; + if (display.viewTo < to) + { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); } + else if (display.viewTo > to) + { display.view = display.view.slice(0, findViewIndex(cm, to)); } + } + display.viewTo = to; + } + + // Count the number of lines in the view whose DOM representation is + // out of date (or nonexistent). + function countDirtyView(cm) { + var view = cm.display.view, dirty = 0; + for (var i = 0; i < view.length; i++) { + var lineView = view[i]; + if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; } + } + return dirty + } + + function updateSelection(cm) { + cm.display.input.showSelection(cm.display.input.prepareSelection()); + } + + function prepareSelection(cm, primary) { + if ( primary === void 0 ) primary = true; + + var doc = cm.doc, result = {}; + var curFragment = result.cursors = document.createDocumentFragment(); + var selFragment = result.selection = document.createDocumentFragment(); + + var customCursor = cm.options.$customCursor; + if (customCursor) { primary = true; } + for (var i = 0; i < doc.sel.ranges.length; i++) { + if (!primary && i == doc.sel.primIndex) { continue } + var range = doc.sel.ranges[i]; + if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue } + var collapsed = range.empty(); + if (customCursor) { + var head = customCursor(cm, range); + if (head) { drawSelectionCursor(cm, head, curFragment); } + } else if (collapsed || cm.options.showCursorWhenSelecting) { + drawSelectionCursor(cm, range.head, curFragment); + } + if (!collapsed) + { drawSelectionRange(cm, range, selFragment); } + } + return result + } + + // Draws a cursor for the given range + function drawSelectionCursor(cm, head, output) { + var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine); + + var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")); + cursor.style.left = pos.left + "px"; + cursor.style.top = pos.top + "px"; + cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"; + + if (/\bcm-fat-cursor\b/.test(cm.getWrapperElement().className)) { + var charPos = charCoords(cm, head, "div", null, null); + var width = charPos.right - charPos.left; + cursor.style.width = (width > 0 ? width : cm.defaultCharWidth()) + "px"; + } + + if (pos.other) { + // Secondary cursor, shown when on a 'jump' in bi-directional text + var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor")); + otherCursor.style.display = ""; + otherCursor.style.left = pos.other.left + "px"; + otherCursor.style.top = pos.other.top + "px"; + otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"; + } + } + + function cmpCoords(a, b) { return a.top - b.top || a.left - b.left } + + // Draws the given range as a highlighted selection + function drawSelectionRange(cm, range, output) { + var display = cm.display, doc = cm.doc; + var fragment = document.createDocumentFragment(); + var padding = paddingH(cm.display), leftSide = padding.left; + var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right; + var docLTR = doc.direction == "ltr"; + + function add(left, top, width, bottom) { + if (top < 0) { top = 0; } + top = Math.round(top); + bottom = Math.round(bottom); + fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: absolute; left: " + left + "px;\n top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px;\n height: " + (bottom - top) + "px"))); + } + + function drawForLine(line, fromArg, toArg) { + var lineObj = getLine(doc, line); + var lineLen = lineObj.text.length; + var start, end; + function coords(ch, bias) { + return charCoords(cm, Pos(line, ch), "div", lineObj, bias) + } + + function wrapX(pos, dir, side) { + var extent = wrappedLineExtentChar(cm, lineObj, null, pos); + var prop = (dir == "ltr") == (side == "after") ? "left" : "right"; + var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1); + return coords(ch, prop)[prop] + } + + var order = getOrder(lineObj, doc.direction); + iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) { + var ltr = dir == "ltr"; + var fromPos = coords(from, ltr ? "left" : "right"); + var toPos = coords(to - 1, ltr ? "right" : "left"); + + var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen; + var first = i == 0, last = !order || i == order.length - 1; + if (toPos.top - fromPos.top <= 3) { // Single line + var openLeft = (docLTR ? openStart : openEnd) && first; + var openRight = (docLTR ? openEnd : openStart) && last; + var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left; + var right = openRight ? rightSide : (ltr ? toPos : fromPos).right; + add(left, fromPos.top, right - left, fromPos.bottom); + } else { // Multiple lines + var topLeft, topRight, botLeft, botRight; + if (ltr) { + topLeft = docLTR && openStart && first ? leftSide : fromPos.left; + topRight = docLTR ? rightSide : wrapX(from, dir, "before"); + botLeft = docLTR ? leftSide : wrapX(to, dir, "after"); + botRight = docLTR && openEnd && last ? rightSide : toPos.right; + } else { + topLeft = !docLTR ? leftSide : wrapX(from, dir, "before"); + topRight = !docLTR && openStart && first ? rightSide : fromPos.right; + botLeft = !docLTR && openEnd && last ? leftSide : toPos.left; + botRight = !docLTR ? rightSide : wrapX(to, dir, "after"); + } + add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom); + if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); } + add(botLeft, toPos.top, botRight - botLeft, toPos.bottom); + } + + if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; } + if (cmpCoords(toPos, start) < 0) { start = toPos; } + if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; } + if (cmpCoords(toPos, end) < 0) { end = toPos; } + }); + return {start: start, end: end} + } + + var sFrom = range.from(), sTo = range.to(); + if (sFrom.line == sTo.line) { + drawForLine(sFrom.line, sFrom.ch, sTo.ch); + } else { + var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line); + var singleVLine = visualLine(fromLine) == visualLine(toLine); + var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end; + var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start; + if (singleVLine) { + if (leftEnd.top < rightStart.top - 2) { + add(leftEnd.right, leftEnd.top, null, leftEnd.bottom); + add(leftSide, rightStart.top, rightStart.left, rightStart.bottom); + } else { + add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom); + } + } + if (leftEnd.bottom < rightStart.top) + { add(leftSide, leftEnd.bottom, null, rightStart.top); } + } + + output.appendChild(fragment); + } + + // Cursor-blinking + function restartBlink(cm) { + if (!cm.state.focused) { return } + var display = cm.display; + clearInterval(display.blinker); + var on = true; + display.cursorDiv.style.visibility = ""; + if (cm.options.cursorBlinkRate > 0) + { display.blinker = setInterval(function () { + if (!cm.hasFocus()) { onBlur(cm); } + display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; + }, cm.options.cursorBlinkRate); } + else if (cm.options.cursorBlinkRate < 0) + { display.cursorDiv.style.visibility = "hidden"; } + } + + function ensureFocus(cm) { + if (!cm.hasFocus()) { + cm.display.input.focus(); + if (!cm.state.focused) { onFocus(cm); } + } + } + + function delayBlurEvent(cm) { + cm.state.delayingBlurEvent = true; + setTimeout(function () { if (cm.state.delayingBlurEvent) { + cm.state.delayingBlurEvent = false; + if (cm.state.focused) { onBlur(cm); } + } }, 100); + } + + function onFocus(cm, e) { + if (cm.state.delayingBlurEvent && !cm.state.draggingText) { cm.state.delayingBlurEvent = false; } + + if (cm.options.readOnly == "nocursor") { return } + if (!cm.state.focused) { + signal(cm, "focus", cm, e); + cm.state.focused = true; + addClass(cm.display.wrapper, "CodeMirror-focused"); + // This test prevents this from firing when a context + // menu is closed (since the input reset would kill the + // select-all detection hack) + if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) { + cm.display.input.reset(); + if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730 + } + cm.display.input.receivedFocus(); + } + restartBlink(cm); + } + function onBlur(cm, e) { + if (cm.state.delayingBlurEvent) { return } + + if (cm.state.focused) { + signal(cm, "blur", cm, e); + cm.state.focused = false; + rmClass(cm.display.wrapper, "CodeMirror-focused"); + } + clearInterval(cm.display.blinker); + setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150); + } + + // Read the actual heights of the rendered lines, and update their + // stored heights to match. + function updateHeightsInViewport(cm) { + var display = cm.display; + var prevBottom = display.lineDiv.offsetTop; + var viewTop = Math.max(0, display.scroller.getBoundingClientRect().top); + var oldHeight = display.lineDiv.getBoundingClientRect().top; + var mustScroll = 0; + for (var i = 0; i < display.view.length; i++) { + var cur = display.view[i], wrapping = cm.options.lineWrapping; + var height = (void 0), width = 0; + if (cur.hidden) { continue } + oldHeight += cur.line.height; + if (ie && ie_version < 8) { + var bot = cur.node.offsetTop + cur.node.offsetHeight; + height = bot - prevBottom; + prevBottom = bot; + } else { + var box = cur.node.getBoundingClientRect(); + height = box.bottom - box.top; + // Check that lines don't extend past the right of the current + // editor width + if (!wrapping && cur.text.firstChild) + { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; } + } + var diff = cur.line.height - height; + if (diff > .005 || diff < -.005) { + if (oldHeight < viewTop) { mustScroll -= diff; } + updateLineHeight(cur.line, height); + updateWidgetHeight(cur.line); + if (cur.rest) { for (var j = 0; j < cur.rest.length; j++) + { updateWidgetHeight(cur.rest[j]); } } + } + if (width > cm.display.sizerWidth) { + var chWidth = Math.ceil(width / charWidth(cm.display)); + if (chWidth > cm.display.maxLineLength) { + cm.display.maxLineLength = chWidth; + cm.display.maxLine = cur.line; + cm.display.maxLineChanged = true; + } + } + } + if (Math.abs(mustScroll) > 2) { display.scroller.scrollTop += mustScroll; } + } + + // Read and store the height of line widgets associated with the + // given line. + function updateWidgetHeight(line) { + if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) { + var w = line.widgets[i], parent = w.node.parentNode; + if (parent) { w.height = parent.offsetHeight; } + } } + } + + // Compute the lines that are visible in a given viewport (defaults + // the the current scroll position). viewport may contain top, + // height, and ensure (see op.scrollToPos) properties. + function visibleLines(display, doc, viewport) { + var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop; + top = Math.floor(top - paddingTop(display)); + var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight; + + var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom); + // Ensure is a {from: {line, ch}, to: {line, ch}} object, and + // forces those lines into the viewport (if possible). + if (viewport && viewport.ensure) { + var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line; + if (ensureFrom < from) { + from = ensureFrom; + to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight); + } else if (Math.min(ensureTo, doc.lastLine()) >= to) { + from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight); + to = ensureTo; + } + } + return {from: from, to: Math.max(to, from + 1)} + } + + // SCROLLING THINGS INTO VIEW + + // If an editor sits on the top or bottom of the window, partially + // scrolled out of view, this ensures that the cursor is visible. + function maybeScrollWindow(cm, rect) { + if (signalDOMEvent(cm, "scrollCursorIntoView")) { return } + + var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null; + var doc = display.wrapper.ownerDocument; + if (rect.top + box.top < 0) { doScroll = true; } + else if (rect.bottom + box.top > (doc.defaultView.innerHeight || doc.documentElement.clientHeight)) { doScroll = false; } + if (doScroll != null && !phantom) { + var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n top: " + (rect.top - display.viewOffset - paddingTop(cm.display)) + "px;\n height: " + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + "px;\n left: " + (rect.left) + "px; width: " + (Math.max(2, rect.right - rect.left)) + "px;")); + cm.display.lineSpace.appendChild(scrollNode); + scrollNode.scrollIntoView(doScroll); + cm.display.lineSpace.removeChild(scrollNode); + } + } + + // Scroll a given position into view (immediately), verifying that + // it actually became visible (as line heights are accurately + // measured, the position of something may 'drift' during drawing). + function scrollPosIntoView(cm, pos, end, margin) { + if (margin == null) { margin = 0; } + var rect; + if (!cm.options.lineWrapping && pos == end) { + // Set pos and end to the cursor positions around the character pos sticks to + // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch + // If pos == Pos(_, 0, "before"), pos and end are unchanged + end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos; + pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos; + } + for (var limit = 0; limit < 5; limit++) { + var changed = false; + var coords = cursorCoords(cm, pos); + var endCoords = !end || end == pos ? coords : cursorCoords(cm, end); + rect = {left: Math.min(coords.left, endCoords.left), + top: Math.min(coords.top, endCoords.top) - margin, + right: Math.max(coords.left, endCoords.left), + bottom: Math.max(coords.bottom, endCoords.bottom) + margin}; + var scrollPos = calculateScrollPos(cm, rect); + var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft; + if (scrollPos.scrollTop != null) { + updateScrollTop(cm, scrollPos.scrollTop); + if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; } + } + if (scrollPos.scrollLeft != null) { + setScrollLeft(cm, scrollPos.scrollLeft); + if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; } + } + if (!changed) { break } + } + return rect + } + + // Scroll a given set of coordinates into view (immediately). + function scrollIntoView(cm, rect) { + var scrollPos = calculateScrollPos(cm, rect); + if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); } + if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); } + } + + // Calculate a new scroll position needed to scroll the given + // rectangle into view. Returns an object with scrollTop and + // scrollLeft properties. When these are undefined, the + // vertical/horizontal position does not need to be adjusted. + function calculateScrollPos(cm, rect) { + var display = cm.display, snapMargin = textHeight(cm.display); + if (rect.top < 0) { rect.top = 0; } + var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop; + var screen = displayHeight(cm), result = {}; + if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; } + var docBottom = cm.doc.height + paddingVert(display); + var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin; + if (rect.top < screentop) { + result.scrollTop = atTop ? 0 : rect.top; + } else if (rect.bottom > screentop + screen) { + var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen); + if (newTop != screentop) { result.scrollTop = newTop; } + } + + var gutterSpace = cm.options.fixedGutter ? 0 : display.gutters.offsetWidth; + var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft - gutterSpace; + var screenw = displayWidth(cm) - display.gutters.offsetWidth; + var tooWide = rect.right - rect.left > screenw; + if (tooWide) { rect.right = rect.left + screenw; } + if (rect.left < 10) + { result.scrollLeft = 0; } + else if (rect.left < screenleft) + { result.scrollLeft = Math.max(0, rect.left + gutterSpace - (tooWide ? 0 : 10)); } + else if (rect.right > screenw + screenleft - 3) + { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; } + return result + } + + // Store a relative adjustment to the scroll position in the current + // operation (to be applied when the operation finishes). + function addToScrollTop(cm, top) { + if (top == null) { return } + resolveScrollToPos(cm); + cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top; + } + + // Make sure that at the end of the operation the current cursor is + // shown. + function ensureCursorVisible(cm) { + resolveScrollToPos(cm); + var cur = cm.getCursor(); + cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin}; + } + + function scrollToCoords(cm, x, y) { + if (x != null || y != null) { resolveScrollToPos(cm); } + if (x != null) { cm.curOp.scrollLeft = x; } + if (y != null) { cm.curOp.scrollTop = y; } + } + + function scrollToRange(cm, range) { + resolveScrollToPos(cm); + cm.curOp.scrollToPos = range; + } + + // When an operation has its scrollToPos property set, and another + // scroll action is applied before the end of the operation, this + // 'simulates' scrolling that position into view in a cheap way, so + // that the effect of intermediate scroll commands is not ignored. + function resolveScrollToPos(cm) { + var range = cm.curOp.scrollToPos; + if (range) { + cm.curOp.scrollToPos = null; + var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to); + scrollToCoordsRange(cm, from, to, range.margin); + } + } + + function scrollToCoordsRange(cm, from, to, margin) { + var sPos = calculateScrollPos(cm, { + left: Math.min(from.left, to.left), + top: Math.min(from.top, to.top) - margin, + right: Math.max(from.right, to.right), + bottom: Math.max(from.bottom, to.bottom) + margin + }); + scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop); + } + + // Sync the scrollable area and scrollbars, ensure the viewport + // covers the visible area. + function updateScrollTop(cm, val) { + if (Math.abs(cm.doc.scrollTop - val) < 2) { return } + if (!gecko) { updateDisplaySimple(cm, {top: val}); } + setScrollTop(cm, val, true); + if (gecko) { updateDisplaySimple(cm); } + startWorker(cm, 100); + } + + function setScrollTop(cm, val, forceScroll) { + val = Math.max(0, Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val)); + if (cm.display.scroller.scrollTop == val && !forceScroll) { return } + cm.doc.scrollTop = val; + cm.display.scrollbars.setScrollTop(val); + if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; } + } + + // Sync scroller and scrollbar, ensure the gutter elements are + // aligned. + function setScrollLeft(cm, val, isScroller, forceScroll) { + val = Math.max(0, Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth)); + if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return } + cm.doc.scrollLeft = val; + alignHorizontally(cm); + if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; } + cm.display.scrollbars.setScrollLeft(val); + } + + // SCROLLBARS + + // Prepare DOM reads needed to update the scrollbars. Done in one + // shot to minimize update/measure roundtrips. + function measureForScrollbars(cm) { + var d = cm.display, gutterW = d.gutters.offsetWidth; + var docH = Math.round(cm.doc.height + paddingVert(cm.display)); + return { + clientHeight: d.scroller.clientHeight, + viewHeight: d.wrapper.clientHeight, + scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth, + viewWidth: d.wrapper.clientWidth, + barLeft: cm.options.fixedGutter ? gutterW : 0, + docHeight: docH, + scrollHeight: docH + scrollGap(cm) + d.barHeight, + nativeBarWidth: d.nativeBarWidth, + gutterWidth: gutterW + } + } + + var NativeScrollbars = function(place, scroll, cm) { + this.cm = cm; + var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar"); + var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar"); + vert.tabIndex = horiz.tabIndex = -1; + place(vert); place(horiz); + + on(vert, "scroll", function () { + if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); } + }); + on(horiz, "scroll", function () { + if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); } + }); + + this.checkedZeroWidth = false; + // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). + if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; } + }; + + NativeScrollbars.prototype.update = function (measure) { + var needsH = measure.scrollWidth > measure.clientWidth + 1; + var needsV = measure.scrollHeight > measure.clientHeight + 1; + var sWidth = measure.nativeBarWidth; + + if (needsV) { + this.vert.style.display = "block"; + this.vert.style.bottom = needsH ? sWidth + "px" : "0"; + var totalHeight = measure.viewHeight - (needsH ? sWidth : 0); + // A bug in IE8 can cause this value to be negative, so guard it. + this.vert.firstChild.style.height = + Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"; + } else { + this.vert.scrollTop = 0; + this.vert.style.display = ""; + this.vert.firstChild.style.height = "0"; + } + + if (needsH) { + this.horiz.style.display = "block"; + this.horiz.style.right = needsV ? sWidth + "px" : "0"; + this.horiz.style.left = measure.barLeft + "px"; + var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0); + this.horiz.firstChild.style.width = + Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px"; + } else { + this.horiz.style.display = ""; + this.horiz.firstChild.style.width = "0"; + } + + if (!this.checkedZeroWidth && measure.clientHeight > 0) { + if (sWidth == 0) { this.zeroWidthHack(); } + this.checkedZeroWidth = true; + } + + return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0} + }; + + NativeScrollbars.prototype.setScrollLeft = function (pos) { + if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; } + if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); } + }; + + NativeScrollbars.prototype.setScrollTop = function (pos) { + if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; } + if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); } + }; + + NativeScrollbars.prototype.zeroWidthHack = function () { + var w = mac && !mac_geMountainLion ? "12px" : "18px"; + this.horiz.style.height = this.vert.style.width = w; + this.horiz.style.visibility = this.vert.style.visibility = "hidden"; + this.disableHoriz = new Delayed; + this.disableVert = new Delayed; + }; + + NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) { + bar.style.visibility = ""; + function maybeDisable() { + // To find out whether the scrollbar is still visible, we + // check whether the element under the pixel in the bottom + // right corner of the scrollbar box is the scrollbar box + // itself (when the bar is still visible) or its filler child + // (when the bar is hidden). If it is still visible, we keep + // it enabled, if it's hidden, we disable pointer events. + var box = bar.getBoundingClientRect(); + var elt = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2) + : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1); + if (elt != bar) { bar.style.visibility = "hidden"; } + else { delay.set(1000, maybeDisable); } + } + delay.set(1000, maybeDisable); + }; + + NativeScrollbars.prototype.clear = function () { + var parent = this.horiz.parentNode; + parent.removeChild(this.horiz); + parent.removeChild(this.vert); + }; + + var NullScrollbars = function () {}; + + NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} }; + NullScrollbars.prototype.setScrollLeft = function () {}; + NullScrollbars.prototype.setScrollTop = function () {}; + NullScrollbars.prototype.clear = function () {}; + + function updateScrollbars(cm, measure) { + if (!measure) { measure = measureForScrollbars(cm); } + var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight; + updateScrollbarsInner(cm, measure); + for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) { + if (startWidth != cm.display.barWidth && cm.options.lineWrapping) + { updateHeightsInViewport(cm); } + updateScrollbarsInner(cm, measureForScrollbars(cm)); + startWidth = cm.display.barWidth; startHeight = cm.display.barHeight; + } + } + + // Re-synchronize the fake scrollbars with the actual size of the + // content. + function updateScrollbarsInner(cm, measure) { + var d = cm.display; + var sizes = d.scrollbars.update(measure); + + d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"; + d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"; + d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"; + + if (sizes.right && sizes.bottom) { + d.scrollbarFiller.style.display = "block"; + d.scrollbarFiller.style.height = sizes.bottom + "px"; + d.scrollbarFiller.style.width = sizes.right + "px"; + } else { d.scrollbarFiller.style.display = ""; } + if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) { + d.gutterFiller.style.display = "block"; + d.gutterFiller.style.height = sizes.bottom + "px"; + d.gutterFiller.style.width = measure.gutterWidth + "px"; + } else { d.gutterFiller.style.display = ""; } + } + + var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}; + + function initScrollbars(cm) { + if (cm.display.scrollbars) { + cm.display.scrollbars.clear(); + if (cm.display.scrollbars.addClass) + { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); } + } + + cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) { + cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller); + // Prevent clicks in the scrollbars from killing focus + on(node, "mousedown", function () { + if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); } + }); + node.setAttribute("cm-not-content", "true"); + }, function (pos, axis) { + if (axis == "horizontal") { setScrollLeft(cm, pos); } + else { updateScrollTop(cm, pos); } + }, cm); + if (cm.display.scrollbars.addClass) + { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); } + } + + // Operations are used to wrap a series of changes to the editor + // state in such a way that each change won't have to update the + // cursor and display (which would be awkward, slow, and + // error-prone). Instead, display updates are batched and then all + // combined and executed at once. + + var nextOpId = 0; + // Start a new operation. + function startOperation(cm) { + cm.curOp = { + cm: cm, + viewChanged: false, // Flag that indicates that lines might need to be redrawn + startHeight: cm.doc.height, // Used to detect need to update scrollbar + forceUpdate: false, // Used to force a redraw + updateInput: 0, // Whether to reset the input textarea + typing: false, // Whether this reset should be careful to leave existing text (for compositing) + changeObjs: null, // Accumulated changes, for firing change events + cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on + cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already + selectionChanged: false, // Whether the selection needs to be redrawn + updateMaxLine: false, // Set when the widest line needs to be determined anew + scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet + scrollToPos: null, // Used to scroll to a specific position + focus: false, + id: ++nextOpId, // Unique ID + markArrays: null // Used by addMarkedSpan + }; + pushOperation(cm.curOp); + } + + // Finish an operation, updating the display and signalling delayed events + function endOperation(cm) { + var op = cm.curOp; + if (op) { finishOperation(op, function (group) { + for (var i = 0; i < group.ops.length; i++) + { group.ops[i].cm.curOp = null; } + endOperations(group); + }); } + } + + // The DOM updates done when an operation finishes are batched so + // that the minimum number of relayouts are required. + function endOperations(group) { + var ops = group.ops; + for (var i = 0; i < ops.length; i++) // Read DOM + { endOperation_R1(ops[i]); } + for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe) + { endOperation_W1(ops[i$1]); } + for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM + { endOperation_R2(ops[i$2]); } + for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe) + { endOperation_W2(ops[i$3]); } + for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM + { endOperation_finish(ops[i$4]); } + } + + function endOperation_R1(op) { + var cm = op.cm, display = cm.display; + maybeClipScrollbars(cm); + if (op.updateMaxLine) { findMaxLine(cm); } + + op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null || + op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom || + op.scrollToPos.to.line >= display.viewTo) || + display.maxLineChanged && cm.options.lineWrapping; + op.update = op.mustUpdate && + new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate); + } + + function endOperation_W1(op) { + op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update); + } + + function endOperation_R2(op) { + var cm = op.cm, display = cm.display; + if (op.updatedDisplay) { updateHeightsInViewport(cm); } + + op.barMeasure = measureForScrollbars(cm); + + // If the max line changed since it was last measured, measure it, + // and ensure the document's width matches it. + // updateDisplay_W2 will use these properties to do the actual resizing + if (display.maxLineChanged && !cm.options.lineWrapping) { + op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3; + cm.display.sizerWidth = op.adjustWidthTo; + op.barMeasure.scrollWidth = + Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth); + op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm)); + } + + if (op.updatedDisplay || op.selectionChanged) + { op.preparedSelection = display.input.prepareSelection(); } + } + + function endOperation_W2(op) { + var cm = op.cm; + + if (op.adjustWidthTo != null) { + cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"; + if (op.maxScrollLeft < cm.doc.scrollLeft) + { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); } + cm.display.maxLineChanged = false; + } + + var takeFocus = op.focus && op.focus == activeElt(doc(cm)); + if (op.preparedSelection) + { cm.display.input.showSelection(op.preparedSelection, takeFocus); } + if (op.updatedDisplay || op.startHeight != cm.doc.height) + { updateScrollbars(cm, op.barMeasure); } + if (op.updatedDisplay) + { setDocumentHeight(cm, op.barMeasure); } + + if (op.selectionChanged) { restartBlink(cm); } + + if (cm.state.focused && op.updateInput) + { cm.display.input.reset(op.typing); } + if (takeFocus) { ensureFocus(op.cm); } + } + + function endOperation_finish(op) { + var cm = op.cm, display = cm.display, doc = cm.doc; + + if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); } + + // Abort mouse wheel delta measurement, when scrolling explicitly + if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos)) + { display.wheelStartX = display.wheelStartY = null; } + + // Propagate the scroll position to the actual DOM scroller + if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); } + + if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); } + // If we need to scroll a specific position into view, do so. + if (op.scrollToPos) { + var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from), + clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin); + maybeScrollWindow(cm, rect); + } + + // Fire events for markers that are hidden/unidden by editing or + // undoing + var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers; + if (hidden) { for (var i = 0; i < hidden.length; ++i) + { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } } + if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1) + { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } } + + if (display.wrapper.offsetHeight) + { doc.scrollTop = cm.display.scroller.scrollTop; } + + // Fire change events, and delayed event handlers + if (op.changeObjs) + { signal(cm, "changes", cm, op.changeObjs); } + if (op.update) + { op.update.finish(); } + } + + // Run the given function in an operation + function runInOp(cm, f) { + if (cm.curOp) { return f() } + startOperation(cm); + try { return f() } + finally { endOperation(cm); } + } + // Wraps a function in an operation. Returns the wrapped function. + function operation(cm, f) { + return function() { + if (cm.curOp) { return f.apply(cm, arguments) } + startOperation(cm); + try { return f.apply(cm, arguments) } + finally { endOperation(cm); } + } + } + // Used to add methods to editor and doc instances, wrapping them in + // operations. + function methodOp(f) { + return function() { + if (this.curOp) { return f.apply(this, arguments) } + startOperation(this); + try { return f.apply(this, arguments) } + finally { endOperation(this); } + } + } + function docMethodOp(f) { + return function() { + var cm = this.cm; + if (!cm || cm.curOp) { return f.apply(this, arguments) } + startOperation(cm); + try { return f.apply(this, arguments) } + finally { endOperation(cm); } + } + } + + // HIGHLIGHT WORKER + + function startWorker(cm, time) { + if (cm.doc.highlightFrontier < cm.display.viewTo) + { cm.state.highlight.set(time, bind(highlightWorker, cm)); } + } + + function highlightWorker(cm) { + var doc = cm.doc; + if (doc.highlightFrontier >= cm.display.viewTo) { return } + var end = +new Date + cm.options.workTime; + var context = getContextBefore(cm, doc.highlightFrontier); + var changedLines = []; + + doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) { + if (context.line >= cm.display.viewFrom) { // Visible + var oldStyles = line.styles; + var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null; + var highlighted = highlightLine(cm, line, context, true); + if (resetState) { context.state = resetState; } + line.styles = highlighted.styles; + var oldCls = line.styleClasses, newCls = highlighted.classes; + if (newCls) { line.styleClasses = newCls; } + else if (oldCls) { line.styleClasses = null; } + var ischange = !oldStyles || oldStyles.length != line.styles.length || + oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass); + for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; } + if (ischange) { changedLines.push(context.line); } + line.stateAfter = context.save(); + context.nextLine(); + } else { + if (line.text.length <= cm.options.maxHighlightLength) + { processLine(cm, line.text, context); } + line.stateAfter = context.line % 5 == 0 ? context.save() : null; + context.nextLine(); + } + if (+new Date > end) { + startWorker(cm, cm.options.workDelay); + return true + } + }); + doc.highlightFrontier = context.line; + doc.modeFrontier = Math.max(doc.modeFrontier, context.line); + if (changedLines.length) { runInOp(cm, function () { + for (var i = 0; i < changedLines.length; i++) + { regLineChange(cm, changedLines[i], "text"); } + }); } + } + + // DISPLAY DRAWING + + var DisplayUpdate = function(cm, viewport, force) { + var display = cm.display; + + this.viewport = viewport; + // Store some values that we'll need later (but don't want to force a relayout for) + this.visible = visibleLines(display, cm.doc, viewport); + this.editorIsHidden = !display.wrapper.offsetWidth; + this.wrapperHeight = display.wrapper.clientHeight; + this.wrapperWidth = display.wrapper.clientWidth; + this.oldDisplayWidth = displayWidth(cm); + this.force = force; + this.dims = getDimensions(cm); + this.events = []; + }; + + DisplayUpdate.prototype.signal = function (emitter, type) { + if (hasHandler(emitter, type)) + { this.events.push(arguments); } + }; + DisplayUpdate.prototype.finish = function () { + for (var i = 0; i < this.events.length; i++) + { signal.apply(null, this.events[i]); } + }; + + function maybeClipScrollbars(cm) { + var display = cm.display; + if (!display.scrollbarsClipped && display.scroller.offsetWidth) { + display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth; + display.heightForcer.style.height = scrollGap(cm) + "px"; + display.sizer.style.marginBottom = -display.nativeBarWidth + "px"; + display.sizer.style.borderRightWidth = scrollGap(cm) + "px"; + display.scrollbarsClipped = true; + } + } + + function selectionSnapshot(cm) { + if (cm.hasFocus()) { return null } + var active = activeElt(doc(cm)); + if (!active || !contains(cm.display.lineDiv, active)) { return null } + var result = {activeElt: active}; + if (window.getSelection) { + var sel = win(cm).getSelection(); + if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) { + result.anchorNode = sel.anchorNode; + result.anchorOffset = sel.anchorOffset; + result.focusNode = sel.focusNode; + result.focusOffset = sel.focusOffset; + } + } + return result + } + + function restoreSelection(snapshot) { + if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt(snapshot.activeElt.ownerDocument)) { return } + snapshot.activeElt.focus(); + if (!/^(INPUT|TEXTAREA)$/.test(snapshot.activeElt.nodeName) && + snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) { + var doc = snapshot.activeElt.ownerDocument; + var sel = doc.defaultView.getSelection(), range = doc.createRange(); + range.setEnd(snapshot.anchorNode, snapshot.anchorOffset); + range.collapse(false); + sel.removeAllRanges(); + sel.addRange(range); + sel.extend(snapshot.focusNode, snapshot.focusOffset); + } + } + + // Does the actual updating of the line display. Bails out + // (returning false) when there is nothing to be done and forced is + // false. + function updateDisplayIfNeeded(cm, update) { + var display = cm.display, doc = cm.doc; + + if (update.editorIsHidden) { + resetView(cm); + return false + } + + // Bail out if the visible area is already rendered and nothing changed. + if (!update.force && + update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo && + (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) && + display.renderedView == display.view && countDirtyView(cm) == 0) + { return false } + + if (maybeUpdateLineNumberWidth(cm)) { + resetView(cm); + update.dims = getDimensions(cm); + } + + // Compute a suitable new viewport (from & to) + var end = doc.first + doc.size; + var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first); + var to = Math.min(end, update.visible.to + cm.options.viewportMargin); + if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); } + if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); } + if (sawCollapsedSpans) { + from = visualLineNo(cm.doc, from); + to = visualLineEndNo(cm.doc, to); + } + + var different = from != display.viewFrom || to != display.viewTo || + display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth; + adjustView(cm, from, to); + + display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom)); + // Position the mover div to align with the current scroll position + cm.display.mover.style.top = display.viewOffset + "px"; + + var toUpdate = countDirtyView(cm); + if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view && + (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo)) + { return false } + + // For big changes, we hide the enclosing element during the + // update, since that speeds up the operations on most browsers. + var selSnapshot = selectionSnapshot(cm); + if (toUpdate > 4) { display.lineDiv.style.display = "none"; } + patchDisplay(cm, display.updateLineNumbers, update.dims); + if (toUpdate > 4) { display.lineDiv.style.display = ""; } + display.renderedView = display.view; + // There might have been a widget with a focused element that got + // hidden or updated, if so re-focus it. + restoreSelection(selSnapshot); + + // Prevent selection and cursors from interfering with the scroll + // width and height. + removeChildren(display.cursorDiv); + removeChildren(display.selectionDiv); + display.gutters.style.height = display.sizer.style.minHeight = 0; + + if (different) { + display.lastWrapHeight = update.wrapperHeight; + display.lastWrapWidth = update.wrapperWidth; + startWorker(cm, 400); + } + + display.updateLineNumbers = null; + + return true + } + + function postUpdateDisplay(cm, update) { + var viewport = update.viewport; + + for (var first = true;; first = false) { + if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) { + // Clip forced viewport to actual scrollable area. + if (viewport && viewport.top != null) + { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; } + // Updated line heights might result in the drawn area not + // actually covering the viewport. Keep looping until it does. + update.visible = visibleLines(cm.display, cm.doc, viewport); + if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo) + { break } + } else if (first) { + update.visible = visibleLines(cm.display, cm.doc, viewport); + } + if (!updateDisplayIfNeeded(cm, update)) { break } + updateHeightsInViewport(cm); + var barMeasure = measureForScrollbars(cm); + updateSelection(cm); + updateScrollbars(cm, barMeasure); + setDocumentHeight(cm, barMeasure); + update.force = false; + } + + update.signal(cm, "update", cm); + if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) { + update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo); + cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo; + } + } + + function updateDisplaySimple(cm, viewport) { + var update = new DisplayUpdate(cm, viewport); + if (updateDisplayIfNeeded(cm, update)) { + updateHeightsInViewport(cm); + postUpdateDisplay(cm, update); + var barMeasure = measureForScrollbars(cm); + updateSelection(cm); + updateScrollbars(cm, barMeasure); + setDocumentHeight(cm, barMeasure); + update.finish(); + } + } + + // Sync the actual display DOM structure with display.view, removing + // nodes for lines that are no longer in view, and creating the ones + // that are not there yet, and updating the ones that are out of + // date. + function patchDisplay(cm, updateNumbersFrom, dims) { + var display = cm.display, lineNumbers = cm.options.lineNumbers; + var container = display.lineDiv, cur = container.firstChild; + + function rm(node) { + var next = node.nextSibling; + // Works around a throw-scroll bug in OS X Webkit + if (webkit && mac && cm.display.currentWheelTarget == node) + { node.style.display = "none"; } + else + { node.parentNode.removeChild(node); } + return next + } + + var view = display.view, lineN = display.viewFrom; + // Loop over the elements in the view, syncing cur (the DOM nodes + // in display.lineDiv) with the view as we go. + for (var i = 0; i < view.length; i++) { + var lineView = view[i]; + if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet + var node = buildLineElement(cm, lineView, lineN, dims); + container.insertBefore(node, cur); + } else { // Already drawn + while (cur != lineView.node) { cur = rm(cur); } + var updateNumber = lineNumbers && updateNumbersFrom != null && + updateNumbersFrom <= lineN && lineView.lineNumber; + if (lineView.changes) { + if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; } + updateLineForChanges(cm, lineView, lineN, dims); + } + if (updateNumber) { + removeChildren(lineView.lineNumber); + lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN))); + } + cur = lineView.node.nextSibling; + } + lineN += lineView.size; + } + while (cur) { cur = rm(cur); } + } + + function updateGutterSpace(display) { + var width = display.gutters.offsetWidth; + display.sizer.style.marginLeft = width + "px"; + // Send an event to consumers responding to changes in gutter width. + signalLater(display, "gutterChanged", display); + } + + function setDocumentHeight(cm, measure) { + cm.display.sizer.style.minHeight = measure.docHeight + "px"; + cm.display.heightForcer.style.top = measure.docHeight + "px"; + cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px"; + } + + // Re-align line numbers and gutter marks to compensate for + // horizontal scrolling. + function alignHorizontally(cm) { + var display = cm.display, view = display.view; + if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return } + var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft; + var gutterW = display.gutters.offsetWidth, left = comp + "px"; + for (var i = 0; i < view.length; i++) { if (!view[i].hidden) { + if (cm.options.fixedGutter) { + if (view[i].gutter) + { view[i].gutter.style.left = left; } + if (view[i].gutterBackground) + { view[i].gutterBackground.style.left = left; } + } + var align = view[i].alignable; + if (align) { for (var j = 0; j < align.length; j++) + { align[j].style.left = left; } } + } } + if (cm.options.fixedGutter) + { display.gutters.style.left = (comp + gutterW) + "px"; } + } + + // Used to ensure that the line number gutter is still the right + // size for the current document size. Returns true when an update + // is needed. + function maybeUpdateLineNumberWidth(cm) { + if (!cm.options.lineNumbers) { return false } + var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display; + if (last.length != display.lineNumChars) { + var test = display.measure.appendChild(elt("div", [elt("div", last)], + "CodeMirror-linenumber CodeMirror-gutter-elt")); + var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW; + display.lineGutter.style.width = ""; + display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1; + display.lineNumWidth = display.lineNumInnerWidth + padding; + display.lineNumChars = display.lineNumInnerWidth ? last.length : -1; + display.lineGutter.style.width = display.lineNumWidth + "px"; + updateGutterSpace(cm.display); + return true + } + return false + } + + function getGutters(gutters, lineNumbers) { + var result = [], sawLineNumbers = false; + for (var i = 0; i < gutters.length; i++) { + var name = gutters[i], style = null; + if (typeof name != "string") { style = name.style; name = name.className; } + if (name == "CodeMirror-linenumbers") { + if (!lineNumbers) { continue } + else { sawLineNumbers = true; } + } + result.push({className: name, style: style}); + } + if (lineNumbers && !sawLineNumbers) { result.push({className: "CodeMirror-linenumbers", style: null}); } + return result + } + + // Rebuild the gutter elements, ensure the margin to the left of the + // code matches their width. + function renderGutters(display) { + var gutters = display.gutters, specs = display.gutterSpecs; + removeChildren(gutters); + display.lineGutter = null; + for (var i = 0; i < specs.length; ++i) { + var ref = specs[i]; + var className = ref.className; + var style = ref.style; + var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + className)); + if (style) { gElt.style.cssText = style; } + if (className == "CodeMirror-linenumbers") { + display.lineGutter = gElt; + gElt.style.width = (display.lineNumWidth || 1) + "px"; + } + } + gutters.style.display = specs.length ? "" : "none"; + updateGutterSpace(display); + } + + function updateGutters(cm) { + renderGutters(cm.display); + regChange(cm); + alignHorizontally(cm); + } + + // The display handles the DOM integration, both for input reading + // and content drawing. It holds references to DOM nodes and + // display-related state. + + function Display(place, doc, input, options) { + var d = this; + this.input = input; + + // Covers bottom-right square when both scrollbars are present. + d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); + d.scrollbarFiller.setAttribute("cm-not-content", "true"); + // Covers bottom of gutter when coverGutterNextToScrollbar is on + // and h scrollbar is present. + d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler"); + d.gutterFiller.setAttribute("cm-not-content", "true"); + // Will contain the actual code, positioned to cover the viewport. + d.lineDiv = eltP("div", null, "CodeMirror-code"); + // Elements are added to these to represent selection and cursors. + d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); + d.cursorDiv = elt("div", null, "CodeMirror-cursors"); + // A visibility: hidden element used to find the size of things. + d.measure = elt("div", null, "CodeMirror-measure"); + // When lines outside of the viewport are measured, they are drawn in this. + d.lineMeasure = elt("div", null, "CodeMirror-measure"); + // Wraps everything that needs to exist inside the vertically-padded coordinate system + d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv], + null, "position: relative; outline: none"); + var lines = eltP("div", [d.lineSpace], "CodeMirror-lines"); + // Moved around its parent to cover visible view. + d.mover = elt("div", [lines], null, "position: relative"); + // Set to the height of the document, allowing scrolling. + d.sizer = elt("div", [d.mover], "CodeMirror-sizer"); + d.sizerWidth = null; + // Behavior of elts with overflow: auto and padding is + // inconsistent across browsers. This is used to ensure the + // scrollable area is big enough. + d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;"); + // Will contain the gutters, if any. + d.gutters = elt("div", null, "CodeMirror-gutters"); + d.lineGutter = null; + // Actual scrollable element. + d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll"); + d.scroller.setAttribute("tabIndex", "-1"); + // The element in which the editor lives. + d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror"); + // See #6982. FIXME remove when this has been fixed for a while in Chrome + if (chrome && chrome_version >= 105) { d.wrapper.style.clipPath = "inset(0px)"; } + + // This attribute is respected by automatic translation systems such as Google Translate, + // and may also be respected by tools used by human translators. + d.wrapper.setAttribute('translate', 'no'); + + // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported) + if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; } + if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; } + + if (place) { + if (place.appendChild) { place.appendChild(d.wrapper); } + else { place(d.wrapper); } + } + + // Current rendered range (may be bigger than the view window). + d.viewFrom = d.viewTo = doc.first; + d.reportedViewFrom = d.reportedViewTo = doc.first; + // Information about the rendered lines. + d.view = []; + d.renderedView = null; + // Holds info about a single rendered line when it was rendered + // for measurement, while not in view. + d.externalMeasured = null; + // Empty space (in pixels) above the view + d.viewOffset = 0; + d.lastWrapHeight = d.lastWrapWidth = 0; + d.updateLineNumbers = null; + + d.nativeBarWidth = d.barHeight = d.barWidth = 0; + d.scrollbarsClipped = false; + + // Used to only resize the line number gutter when necessary (when + // the amount of lines crosses a boundary that makes its width change) + d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null; + // Set to true when a non-horizontal-scrolling line widget is + // added. As an optimization, line widget aligning is skipped when + // this is false. + d.alignWidgets = false; + + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; + + // Tracks the maximum line length so that the horizontal scrollbar + // can be kept static when scrolling. + d.maxLine = null; + d.maxLineLength = 0; + d.maxLineChanged = false; + + // Used for measuring wheel scrolling granularity + d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null; + + // True when shift is held down. + d.shift = false; + + // Used to track whether anything happened since the context menu + // was opened. + d.selForContextMenu = null; + + d.activeTouch = null; + + d.gutterSpecs = getGutters(options.gutters, options.lineNumbers); + renderGutters(d); + + input.init(d); + } + + // Since the delta values reported on mouse wheel events are + // unstandardized between browsers and even browser versions, and + // generally horribly unpredictable, this code starts by measuring + // the scroll effect that the first few mouse wheel events have, + // and, from that, detects the way it can convert deltas to pixel + // offsets afterwards. + // + // The reason we want to know the amount a wheel event will scroll + // is that it gives us a chance to update the display before the + // actual scrolling happens, reducing flickering. + + var wheelSamples = 0, wheelPixelsPerUnit = null; + // Fill in a browser-detected starting value on browsers where we + // know one. These don't have to be accurate -- the result of them + // being wrong would just be a slight flicker on the first wheel + // scroll (if it is large enough). + if (ie) { wheelPixelsPerUnit = -.53; } + else if (gecko) { wheelPixelsPerUnit = 15; } + else if (chrome) { wheelPixelsPerUnit = -.7; } + else if (safari) { wheelPixelsPerUnit = -1/3; } + + function wheelEventDelta(e) { + var dx = e.wheelDeltaX, dy = e.wheelDeltaY; + if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; } + if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; } + else if (dy == null) { dy = e.wheelDelta; } + return {x: dx, y: dy} + } + function wheelEventPixels(e) { + var delta = wheelEventDelta(e); + delta.x *= wheelPixelsPerUnit; + delta.y *= wheelPixelsPerUnit; + return delta + } + + function onScrollWheel(cm, e) { + // On Chrome 102, viewport updates somehow stop wheel-based + // scrolling. Turning off pointer events during the scroll seems + // to avoid the issue. + if (chrome && chrome_version == 102) { + if (cm.display.chromeScrollHack == null) { cm.display.sizer.style.pointerEvents = "none"; } + else { clearTimeout(cm.display.chromeScrollHack); } + cm.display.chromeScrollHack = setTimeout(function () { + cm.display.chromeScrollHack = null; + cm.display.sizer.style.pointerEvents = ""; + }, 100); + } + var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y; + var pixelsPerUnit = wheelPixelsPerUnit; + if (e.deltaMode === 0) { + dx = e.deltaX; + dy = e.deltaY; + pixelsPerUnit = 1; + } + + var display = cm.display, scroll = display.scroller; + // Quit if there's nothing to scroll here + var canScrollX = scroll.scrollWidth > scroll.clientWidth; + var canScrollY = scroll.scrollHeight > scroll.clientHeight; + if (!(dx && canScrollX || dy && canScrollY)) { return } + + // Webkit browsers on OS X abort momentum scrolls when the target + // of the scroll event is removed from the scrollable element. + // This hack (see related code in patchDisplay) makes sure the + // element is kept around. + if (dy && mac && webkit) { + outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) { + for (var i = 0; i < view.length; i++) { + if (view[i].node == cur) { + cm.display.currentWheelTarget = cur; + break outer + } + } + } + } + + // On some browsers, horizontal scrolling will cause redraws to + // happen before the gutter has been realigned, causing it to + // wriggle around in a most unseemly way. When we have an + // estimated pixels/delta value, we just handle horizontal + // scrolling entirely here. It'll be slightly off from native, but + // better than glitching out. + if (dx && !gecko && !presto && pixelsPerUnit != null) { + if (dy && canScrollY) + { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * pixelsPerUnit)); } + setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * pixelsPerUnit)); + // Only prevent default scrolling if vertical scrolling is + // actually possible. Otherwise, it causes vertical scroll + // jitter on OSX trackpads when deltaX is small and deltaY + // is large (issue #3579) + if (!dy || (dy && canScrollY)) + { e_preventDefault(e); } + display.wheelStartX = null; // Abort measurement, if in progress + return + } + + // 'Project' the visible viewport to cover the area that is being + // scrolled into view (if we know enough to estimate it). + if (dy && pixelsPerUnit != null) { + var pixels = dy * pixelsPerUnit; + var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight; + if (pixels < 0) { top = Math.max(0, top + pixels - 50); } + else { bot = Math.min(cm.doc.height, bot + pixels + 50); } + updateDisplaySimple(cm, {top: top, bottom: bot}); + } + + if (wheelSamples < 20 && e.deltaMode !== 0) { + if (display.wheelStartX == null) { + display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop; + display.wheelDX = dx; display.wheelDY = dy; + setTimeout(function () { + if (display.wheelStartX == null) { return } + var movedX = scroll.scrollLeft - display.wheelStartX; + var movedY = scroll.scrollTop - display.wheelStartY; + var sample = (movedY && display.wheelDY && movedY / display.wheelDY) || + (movedX && display.wheelDX && movedX / display.wheelDX); + display.wheelStartX = display.wheelStartY = null; + if (!sample) { return } + wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1); + ++wheelSamples; + }, 200); + } else { + display.wheelDX += dx; display.wheelDY += dy; + } + } + } + + // Selection objects are immutable. A new one is created every time + // the selection changes. A selection is one or more non-overlapping + // (and non-touching) ranges, sorted, and an integer that indicates + // which one is the primary selection (the one that's scrolled into + // view, that getCursor returns, etc). + var Selection = function(ranges, primIndex) { + this.ranges = ranges; + this.primIndex = primIndex; + }; + + Selection.prototype.primary = function () { return this.ranges[this.primIndex] }; + + Selection.prototype.equals = function (other) { + if (other == this) { return true } + if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false } + for (var i = 0; i < this.ranges.length; i++) { + var here = this.ranges[i], there = other.ranges[i]; + if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false } + } + return true + }; + + Selection.prototype.deepCopy = function () { + var out = []; + for (var i = 0; i < this.ranges.length; i++) + { out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)); } + return new Selection(out, this.primIndex) + }; + + Selection.prototype.somethingSelected = function () { + for (var i = 0; i < this.ranges.length; i++) + { if (!this.ranges[i].empty()) { return true } } + return false + }; + + Selection.prototype.contains = function (pos, end) { + if (!end) { end = pos; } + for (var i = 0; i < this.ranges.length; i++) { + var range = this.ranges[i]; + if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) + { return i } + } + return -1 + }; + + var Range = function(anchor, head) { + this.anchor = anchor; this.head = head; + }; + + Range.prototype.from = function () { return minPos(this.anchor, this.head) }; + Range.prototype.to = function () { return maxPos(this.anchor, this.head) }; + Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch }; + + // Take an unsorted, potentially overlapping set of ranges, and + // build a selection out of it. 'Consumes' ranges array (modifying + // it). + function normalizeSelection(cm, ranges, primIndex) { + var mayTouch = cm && cm.options.selectionsMayTouch; + var prim = ranges[primIndex]; + ranges.sort(function (a, b) { return cmp(a.from(), b.from()); }); + primIndex = indexOf(ranges, prim); + for (var i = 1; i < ranges.length; i++) { + var cur = ranges[i], prev = ranges[i - 1]; + var diff = cmp(prev.to(), cur.from()); + if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) { + var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to()); + var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head; + if (i <= primIndex) { --primIndex; } + ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to)); + } + } + return new Selection(ranges, primIndex) + } + + function simpleSelection(anchor, head) { + return new Selection([new Range(anchor, head || anchor)], 0) + } + + // Compute the position of the end of a change (its 'to' property + // refers to the pre-change end). + function changeEnd(change) { + if (!change.text) { return change.to } + return Pos(change.from.line + change.text.length - 1, + lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)) + } + + // Adjust a position to refer to the post-change position of the + // same text, or the end of the change if the change covers it. + function adjustForChange(pos, change) { + if (cmp(pos, change.from) < 0) { return pos } + if (cmp(pos, change.to) <= 0) { return changeEnd(change) } + + var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch; + if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; } + return Pos(line, ch) + } + + function computeSelAfterChange(doc, change) { + var out = []; + for (var i = 0; i < doc.sel.ranges.length; i++) { + var range = doc.sel.ranges[i]; + out.push(new Range(adjustForChange(range.anchor, change), + adjustForChange(range.head, change))); + } + return normalizeSelection(doc.cm, out, doc.sel.primIndex) + } + + function offsetPos(pos, old, nw) { + if (pos.line == old.line) + { return Pos(nw.line, pos.ch - old.ch + nw.ch) } + else + { return Pos(nw.line + (pos.line - old.line), pos.ch) } + } + + // Used by replaceSelections to allow moving the selection to the + // start or around the replaced test. Hint may be "start" or "around". + function computeReplacedSel(doc, changes, hint) { + var out = []; + var oldPrev = Pos(doc.first, 0), newPrev = oldPrev; + for (var i = 0; i < changes.length; i++) { + var change = changes[i]; + var from = offsetPos(change.from, oldPrev, newPrev); + var to = offsetPos(changeEnd(change), oldPrev, newPrev); + oldPrev = change.to; + newPrev = to; + if (hint == "around") { + var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0; + out[i] = new Range(inv ? to : from, inv ? from : to); + } else { + out[i] = new Range(from, from); + } + } + return new Selection(out, doc.sel.primIndex) + } + + // Used to get the editor into a consistent state again when options change. + + function loadMode(cm) { + cm.doc.mode = getMode(cm.options, cm.doc.modeOption); + resetModeState(cm); + } + + function resetModeState(cm) { + cm.doc.iter(function (line) { + if (line.stateAfter) { line.stateAfter = null; } + if (line.styles) { line.styles = null; } + }); + cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first; + startWorker(cm, 100); + cm.state.modeGen++; + if (cm.curOp) { regChange(cm); } + } + + // DOCUMENT DATA STRUCTURE + + // By default, updates that start and end at the beginning of a line + // are treated specially, in order to make the association of line + // widgets and marker elements with the text behave more intuitive. + function isWholeLineUpdate(doc, change) { + return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" && + (!doc.cm || doc.cm.options.wholeLineUpdateBefore) + } + + // Perform a change on the document data structure. + function updateDoc(doc, change, markedSpans, estimateHeight) { + function spansFor(n) {return markedSpans ? markedSpans[n] : null} + function update(line, text, spans) { + updateLine(line, text, spans, estimateHeight); + signalLater(line, "change", line, change); + } + function linesFor(start, end) { + var result = []; + for (var i = start; i < end; ++i) + { result.push(new Line(text[i], spansFor(i), estimateHeight)); } + return result + } + + var from = change.from, to = change.to, text = change.text; + var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line); + var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line; + + // Adjust the line structure + if (change.full) { + doc.insert(0, linesFor(0, text.length)); + doc.remove(text.length, doc.size - text.length); + } else if (isWholeLineUpdate(doc, change)) { + // This is a whole-line replace. Treated specially to make + // sure line objects move the way they are supposed to. + var added = linesFor(0, text.length - 1); + update(lastLine, lastLine.text, lastSpans); + if (nlines) { doc.remove(from.line, nlines); } + if (added.length) { doc.insert(from.line, added); } + } else if (firstLine == lastLine) { + if (text.length == 1) { + update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans); + } else { + var added$1 = linesFor(1, text.length - 1); + added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight)); + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); + doc.insert(from.line + 1, added$1); + } + } else if (text.length == 1) { + update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0)); + doc.remove(from.line + 1, nlines); + } else { + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); + update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans); + var added$2 = linesFor(1, text.length - 1); + if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); } + doc.insert(from.line + 1, added$2); + } + + signalLater(doc, "change", doc, change); + } + + // Call f for all linked documents. + function linkedDocs(doc, f, sharedHistOnly) { + function propagate(doc, skip, sharedHist) { + if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) { + var rel = doc.linked[i]; + if (rel.doc == skip) { continue } + var shared = sharedHist && rel.sharedHist; + if (sharedHistOnly && !shared) { continue } + f(rel.doc, shared); + propagate(rel.doc, doc, shared); + } } + } + propagate(doc, null, true); + } + + // Attach a document to an editor. + function attachDoc(cm, doc) { + if (doc.cm) { throw new Error("This document is already in use.") } + cm.doc = doc; + doc.cm = cm; + estimateLineHeights(cm); + loadMode(cm); + setDirectionClass(cm); + cm.options.direction = doc.direction; + if (!cm.options.lineWrapping) { findMaxLine(cm); } + cm.options.mode = doc.modeOption; + regChange(cm); + } + + function setDirectionClass(cm) { + (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl"); + } + + function directionChanged(cm) { + runInOp(cm, function () { + setDirectionClass(cm); + regChange(cm); + }); + } + + function History(prev) { + // Arrays of change events and selections. Doing something adds an + // event to done and clears undo. Undoing moves events from done + // to undone, redoing moves them in the other direction. + this.done = []; this.undone = []; + this.undoDepth = prev ? prev.undoDepth : Infinity; + // Used to track when changes can be merged into a single undo + // event + this.lastModTime = this.lastSelTime = 0; + this.lastOp = this.lastSelOp = null; + this.lastOrigin = this.lastSelOrigin = null; + // Used by the isClean() method + this.generation = this.maxGeneration = prev ? prev.maxGeneration : 1; + } + + // Create a history change event from an updateDoc-style change + // object. + function historyChangeFromChange(doc, change) { + var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}; + attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); + linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true); + return histChange + } + + // Pop all selection events off the end of a history array. Stop at + // a change event. + function clearSelectionEvents(array) { + while (array.length) { + var last = lst(array); + if (last.ranges) { array.pop(); } + else { break } + } + } + + // Find the top change event in the history. Pop off selection + // events that are in the way. + function lastChangeEvent(hist, force) { + if (force) { + clearSelectionEvents(hist.done); + return lst(hist.done) + } else if (hist.done.length && !lst(hist.done).ranges) { + return lst(hist.done) + } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) { + hist.done.pop(); + return lst(hist.done) + } + } + + // Register a change in the history. Merges changes that are within + // a single operation, or are close together with an origin that + // allows merging (starting with "+") into a single event. + function addChangeToHistory(doc, change, selAfter, opId) { + var hist = doc.history; + hist.undone.length = 0; + var time = +new Date, cur; + var last; + + if ((hist.lastOp == opId || + hist.lastOrigin == change.origin && change.origin && + ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) || + change.origin.charAt(0) == "*")) && + (cur = lastChangeEvent(hist, hist.lastOp == opId))) { + // Merge this change into the last event + last = lst(cur.changes); + if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) { + // Optimized case for simple insertion -- don't want to add + // new changesets for every character typed + last.to = changeEnd(change); + } else { + // Add new sub-event + cur.changes.push(historyChangeFromChange(doc, change)); + } + } else { + // Can not be merged, start a new event. + var before = lst(hist.done); + if (!before || !before.ranges) + { pushSelectionToHistory(doc.sel, hist.done); } + cur = {changes: [historyChangeFromChange(doc, change)], + generation: hist.generation}; + hist.done.push(cur); + while (hist.done.length > hist.undoDepth) { + hist.done.shift(); + if (!hist.done[0].ranges) { hist.done.shift(); } + } + } + hist.done.push(selAfter); + hist.generation = ++hist.maxGeneration; + hist.lastModTime = hist.lastSelTime = time; + hist.lastOp = hist.lastSelOp = opId; + hist.lastOrigin = hist.lastSelOrigin = change.origin; + + if (!last) { signal(doc, "historyAdded"); } + } + + function selectionEventCanBeMerged(doc, origin, prev, sel) { + var ch = origin.charAt(0); + return ch == "*" || + ch == "+" && + prev.ranges.length == sel.ranges.length && + prev.somethingSelected() == sel.somethingSelected() && + new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500) + } + + // Called whenever the selection changes, sets the new selection as + // the pending selection in the history, and pushes the old pending + // selection into the 'done' array when it was significantly + // different (in number of selected ranges, emptiness, or time). + function addSelectionToHistory(doc, sel, opId, options) { + var hist = doc.history, origin = options && options.origin; + + // A new event is started when the previous origin does not match + // the current, or the origins don't allow matching. Origins + // starting with * are always merged, those starting with + are + // merged when similar and close together in time. + if (opId == hist.lastSelOp || + (origin && hist.lastSelOrigin == origin && + (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin || + selectionEventCanBeMerged(doc, origin, lst(hist.done), sel)))) + { hist.done[hist.done.length - 1] = sel; } + else + { pushSelectionToHistory(sel, hist.done); } + + hist.lastSelTime = +new Date; + hist.lastSelOrigin = origin; + hist.lastSelOp = opId; + if (options && options.clearRedo !== false) + { clearSelectionEvents(hist.undone); } + } + + function pushSelectionToHistory(sel, dest) { + var top = lst(dest); + if (!(top && top.ranges && top.equals(sel))) + { dest.push(sel); } + } + + // Used to store marked span information in the history. + function attachLocalSpans(doc, change, from, to) { + var existing = change["spans_" + doc.id], n = 0; + doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) { + if (line.markedSpans) + { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; } + ++n; + }); + } + + // When un/re-doing restores text containing marked spans, those + // that have been explicitly cleared should not be restored. + function removeClearedSpans(spans) { + if (!spans) { return null } + var out; + for (var i = 0; i < spans.length; ++i) { + if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } } + else if (out) { out.push(spans[i]); } + } + return !out ? spans : out.length ? out : null + } + + // Retrieve and filter the old marked spans stored in a change event. + function getOldSpans(doc, change) { + var found = change["spans_" + doc.id]; + if (!found) { return null } + var nw = []; + for (var i = 0; i < change.text.length; ++i) + { nw.push(removeClearedSpans(found[i])); } + return nw + } + + // Used for un/re-doing changes from the history. Combines the + // result of computing the existing spans with the set of spans that + // existed in the history (so that deleting around a span and then + // undoing brings back the span). + function mergeOldSpans(doc, change) { + var old = getOldSpans(doc, change); + var stretched = stretchSpansOverChange(doc, change); + if (!old) { return stretched } + if (!stretched) { return old } + + for (var i = 0; i < old.length; ++i) { + var oldCur = old[i], stretchCur = stretched[i]; + if (oldCur && stretchCur) { + spans: for (var j = 0; j < stretchCur.length; ++j) { + var span = stretchCur[j]; + for (var k = 0; k < oldCur.length; ++k) + { if (oldCur[k].marker == span.marker) { continue spans } } + oldCur.push(span); + } + } else if (stretchCur) { + old[i] = stretchCur; + } + } + return old + } + + // Used both to provide a JSON-safe object in .getHistory, and, when + // detaching a document, to split the history in two + function copyHistoryArray(events, newGroup, instantiateSel) { + var copy = []; + for (var i = 0; i < events.length; ++i) { + var event = events[i]; + if (event.ranges) { + copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event); + continue + } + var changes = event.changes, newChanges = []; + copy.push({changes: newChanges}); + for (var j = 0; j < changes.length; ++j) { + var change = changes[j], m = (void 0); + newChanges.push({from: change.from, to: change.to, text: change.text}); + if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) { + if (indexOf(newGroup, Number(m[1])) > -1) { + lst(newChanges)[prop] = change[prop]; + delete change[prop]; + } + } } } + } + } + return copy + } + + // The 'scroll' parameter given to many of these indicated whether + // the new cursor position should be scrolled into view after + // modifying the selection. + + // If shift is held or the extend flag is set, extends a range to + // include a given position (and optionally a second position). + // Otherwise, simply returns the range between the given positions. + // Used for cursor motion and such. + function extendRange(range, head, other, extend) { + if (extend) { + var anchor = range.anchor; + if (other) { + var posBefore = cmp(head, anchor) < 0; + if (posBefore != (cmp(other, anchor) < 0)) { + anchor = head; + head = other; + } else if (posBefore != (cmp(head, other) < 0)) { + head = other; + } + } + return new Range(anchor, head) + } else { + return new Range(other || head, head) + } + } + + // Extend the primary selection range, discard the rest. + function extendSelection(doc, head, other, options, extend) { + if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); } + setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options); + } + + // Extend all selections (pos is an array of selections with length + // equal the number of selections) + function extendSelections(doc, heads, options) { + var out = []; + var extend = doc.cm && (doc.cm.display.shift || doc.extend); + for (var i = 0; i < doc.sel.ranges.length; i++) + { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); } + var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex); + setSelection(doc, newSel, options); + } + + // Updates a single range in the selection. + function replaceOneSelection(doc, i, range, options) { + var ranges = doc.sel.ranges.slice(0); + ranges[i] = range; + setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options); + } + + // Reset the selection to a single range. + function setSimpleSelection(doc, anchor, head, options) { + setSelection(doc, simpleSelection(anchor, head), options); + } + + // Give beforeSelectionChange handlers a change to influence a + // selection update. + function filterSelectionChange(doc, sel, options) { + var obj = { + ranges: sel.ranges, + update: function(ranges) { + this.ranges = []; + for (var i = 0; i < ranges.length; i++) + { this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), + clipPos(doc, ranges[i].head)); } + }, + origin: options && options.origin + }; + signal(doc, "beforeSelectionChange", doc, obj); + if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); } + if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) } + else { return sel } + } + + function setSelectionReplaceHistory(doc, sel, options) { + var done = doc.history.done, last = lst(done); + if (last && last.ranges) { + done[done.length - 1] = sel; + setSelectionNoUndo(doc, sel, options); + } else { + setSelection(doc, sel, options); + } + } + + // Set a new selection. + function setSelection(doc, sel, options) { + setSelectionNoUndo(doc, sel, options); + addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options); + } + + function setSelectionNoUndo(doc, sel, options) { + if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) + { sel = filterSelectionChange(doc, sel, options); } + + var bias = options && options.bias || + (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1); + setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true)); + + if (!(options && options.scroll === false) && doc.cm && doc.cm.getOption("readOnly") != "nocursor") + { ensureCursorVisible(doc.cm); } + } + + function setSelectionInner(doc, sel) { + if (sel.equals(doc.sel)) { return } + + doc.sel = sel; + + if (doc.cm) { + doc.cm.curOp.updateInput = 1; + doc.cm.curOp.selectionChanged = true; + signalCursorActivity(doc.cm); + } + signalLater(doc, "cursorActivity", doc); + } + + // Verify that the selection does not partially select any atomic + // marked ranges. + function reCheckSelection(doc) { + setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false)); + } + + // Return a selection that does not partially select any atomic + // ranges. + function skipAtomicInSelection(doc, sel, bias, mayClear) { + var out; + for (var i = 0; i < sel.ranges.length; i++) { + var range = sel.ranges[i]; + var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]; + var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear); + var newHead = range.head == range.anchor ? newAnchor : skipAtomic(doc, range.head, old && old.head, bias, mayClear); + if (out || newAnchor != range.anchor || newHead != range.head) { + if (!out) { out = sel.ranges.slice(0, i); } + out[i] = new Range(newAnchor, newHead); + } + } + return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel + } + + function skipAtomicInner(doc, pos, oldPos, dir, mayClear) { + var line = getLine(doc, pos.line); + if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { + var sp = line.markedSpans[i], m = sp.marker; + + // Determine if we should prevent the cursor being placed to the left/right of an atomic marker + // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it + // is with selectLeft/Right + var preventCursorLeft = ("selectLeft" in m) ? !m.selectLeft : m.inclusiveLeft; + var preventCursorRight = ("selectRight" in m) ? !m.selectRight : m.inclusiveRight; + + if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) && + (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) { + if (mayClear) { + signal(m, "beforeCursorEnter"); + if (m.explicitlyCleared) { + if (!line.markedSpans) { break } + else {--i; continue} + } + } + if (!m.atomic) { continue } + + if (oldPos) { + var near = m.find(dir < 0 ? 1 : -1), diff = (void 0); + if (dir < 0 ? preventCursorRight : preventCursorLeft) + { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); } + if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0)) + { return skipAtomicInner(doc, near, pos, dir, mayClear) } + } + + var far = m.find(dir < 0 ? -1 : 1); + if (dir < 0 ? preventCursorLeft : preventCursorRight) + { far = movePos(doc, far, dir, far.line == pos.line ? line : null); } + return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null + } + } } + return pos + } + + // Ensure a given position is not inside an atomic range. + function skipAtomic(doc, pos, oldPos, bias, mayClear) { + var dir = bias || 1; + var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) || + (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) || + skipAtomicInner(doc, pos, oldPos, -dir, mayClear) || + (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true)); + if (!found) { + doc.cantEdit = true; + return Pos(doc.first, 0) + } + return found + } + + function movePos(doc, pos, dir, line) { + if (dir < 0 && pos.ch == 0) { + if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) } + else { return null } + } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) { + if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) } + else { return null } + } else { + return new Pos(pos.line, pos.ch + dir) + } + } + + function selectAll(cm) { + cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll); + } + + // UPDATING + + // Allow "beforeChange" event handlers to influence a change + function filterChange(doc, change, update) { + var obj = { + canceled: false, + from: change.from, + to: change.to, + text: change.text, + origin: change.origin, + cancel: function () { return obj.canceled = true; } + }; + if (update) { obj.update = function (from, to, text, origin) { + if (from) { obj.from = clipPos(doc, from); } + if (to) { obj.to = clipPos(doc, to); } + if (text) { obj.text = text; } + if (origin !== undefined) { obj.origin = origin; } + }; } + signal(doc, "beforeChange", doc, obj); + if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); } + + if (obj.canceled) { + if (doc.cm) { doc.cm.curOp.updateInput = 2; } + return null + } + return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin} + } + + // Apply a change to a document, and add it to the document's + // history, and propagating it to all linked documents. + function makeChange(doc, change, ignoreReadOnly) { + if (doc.cm) { + if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) } + if (doc.cm.state.suppressEdits) { return } + } + + if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) { + change = filterChange(doc, change, true); + if (!change) { return } + } + + // Possibly split or suppress the update based on the presence + // of read-only spans in its range. + var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to); + if (split) { + for (var i = split.length - 1; i >= 0; --i) + { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); } + } else { + makeChangeInner(doc, change); + } + } + + function makeChangeInner(doc, change) { + if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return } + var selAfter = computeSelAfterChange(doc, change); + addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN); + + makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)); + var rebased = []; + + linkedDocs(doc, function (doc, sharedHist) { + if (!sharedHist && indexOf(rebased, doc.history) == -1) { + rebaseHist(doc.history, change); + rebased.push(doc.history); + } + makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change)); + }); + } + + // Revert a change stored in a document's history. + function makeChangeFromHistory(doc, type, allowSelectionOnly) { + var suppress = doc.cm && doc.cm.state.suppressEdits; + if (suppress && !allowSelectionOnly) { return } + + var hist = doc.history, event, selAfter = doc.sel; + var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done; + + // Verify that there is a useable event (so that ctrl-z won't + // needlessly clear selection events) + var i = 0; + for (; i < source.length; i++) { + event = source[i]; + if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges) + { break } + } + if (i == source.length) { return } + hist.lastOrigin = hist.lastSelOrigin = null; + + for (;;) { + event = source.pop(); + if (event.ranges) { + pushSelectionToHistory(event, dest); + if (allowSelectionOnly && !event.equals(doc.sel)) { + setSelection(doc, event, {clearRedo: false}); + return + } + selAfter = event; + } else if (suppress) { + source.push(event); + return + } else { break } + } + + // Build up a reverse change object to add to the opposite history + // stack (redo when undoing, and vice versa). + var antiChanges = []; + pushSelectionToHistory(selAfter, dest); + dest.push({changes: antiChanges, generation: hist.generation}); + hist.generation = event.generation || ++hist.maxGeneration; + + var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange"); + + var loop = function ( i ) { + var change = event.changes[i]; + change.origin = type; + if (filter && !filterChange(doc, change, false)) { + source.length = 0; + return {} + } + + antiChanges.push(historyChangeFromChange(doc, change)); + + var after = i ? computeSelAfterChange(doc, change) : lst(source); + makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); + if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); } + var rebased = []; + + // Propagate to the linked documents + linkedDocs(doc, function (doc, sharedHist) { + if (!sharedHist && indexOf(rebased, doc.history) == -1) { + rebaseHist(doc.history, change); + rebased.push(doc.history); + } + makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change)); + }); + }; + + for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) { + var returned = loop( i$1 ); + + if ( returned ) return returned.v; + } + } + + // Sub-views need their line numbers shifted when text is added + // above or below them in the parent document. + function shiftDoc(doc, distance) { + if (distance == 0) { return } + doc.first += distance; + doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range( + Pos(range.anchor.line + distance, range.anchor.ch), + Pos(range.head.line + distance, range.head.ch) + ); }), doc.sel.primIndex); + if (doc.cm) { + regChange(doc.cm, doc.first, doc.first - distance, distance); + for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++) + { regLineChange(doc.cm, l, "gutter"); } + } + } + + // More lower-level change function, handling only a single document + // (not linked ones). + function makeChangeSingleDoc(doc, change, selAfter, spans) { + if (doc.cm && !doc.cm.curOp) + { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) } + + if (change.to.line < doc.first) { + shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line)); + return + } + if (change.from.line > doc.lastLine()) { return } + + // Clip the change to the size of this doc + if (change.from.line < doc.first) { + var shift = change.text.length - 1 - (doc.first - change.from.line); + shiftDoc(doc, shift); + change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch), + text: [lst(change.text)], origin: change.origin}; + } + var last = doc.lastLine(); + if (change.to.line > last) { + change = {from: change.from, to: Pos(last, getLine(doc, last).text.length), + text: [change.text[0]], origin: change.origin}; + } + + change.removed = getBetween(doc, change.from, change.to); + + if (!selAfter) { selAfter = computeSelAfterChange(doc, change); } + if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); } + else { updateDoc(doc, change, spans); } + setSelectionNoUndo(doc, selAfter, sel_dontScroll); + + if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0))) + { doc.cantEdit = false; } + } + + // Handle the interaction of a change to a document with the editor + // that this document is part of. + function makeChangeSingleDocInEditor(cm, change, spans) { + var doc = cm.doc, display = cm.display, from = change.from, to = change.to; + + var recomputeMaxLength = false, checkWidthStart = from.line; + if (!cm.options.lineWrapping) { + checkWidthStart = lineNo(visualLine(getLine(doc, from.line))); + doc.iter(checkWidthStart, to.line + 1, function (line) { + if (line == display.maxLine) { + recomputeMaxLength = true; + return true + } + }); + } + + if (doc.sel.contains(change.from, change.to) > -1) + { signalCursorActivity(cm); } + + updateDoc(doc, change, spans, estimateHeight(cm)); + + if (!cm.options.lineWrapping) { + doc.iter(checkWidthStart, from.line + change.text.length, function (line) { + var len = lineLength(line); + if (len > display.maxLineLength) { + display.maxLine = line; + display.maxLineLength = len; + display.maxLineChanged = true; + recomputeMaxLength = false; + } + }); + if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; } + } + + retreatFrontier(doc, from.line); + startWorker(cm, 400); + + var lendiff = change.text.length - (to.line - from.line) - 1; + // Remember that these lines changed, for updating the display + if (change.full) + { regChange(cm); } + else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change)) + { regLineChange(cm, from.line, "text"); } + else + { regChange(cm, from.line, to.line + 1, lendiff); } + + var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change"); + if (changeHandler || changesHandler) { + var obj = { + from: from, to: to, + text: change.text, + removed: change.removed, + origin: change.origin + }; + if (changeHandler) { signalLater(cm, "change", cm, obj); } + if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); } + } + cm.display.selForContextMenu = null; + } + + function replaceRange(doc, code, from, to, origin) { + var assign; + + if (!to) { to = from; } + if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); } + if (typeof code == "string") { code = doc.splitLines(code); } + makeChange(doc, {from: from, to: to, text: code, origin: origin}); + } + + // Rebasing/resetting history to deal with externally-sourced changes + + function rebaseHistSelSingle(pos, from, to, diff) { + if (to < pos.line) { + pos.line += diff; + } else if (from < pos.line) { + pos.line = from; + pos.ch = 0; + } + } + + // Tries to rebase an array of history events given a change in the + // document. If the change touches the same lines as the event, the + // event, and everything 'behind' it, is discarded. If the change is + // before the event, the event's positions are updated. Uses a + // copy-on-write scheme for the positions, to avoid having to + // reallocate them all on every rebase, but also avoid problems with + // shared position objects being unsafely updated. + function rebaseHistArray(array, from, to, diff) { + for (var i = 0; i < array.length; ++i) { + var sub = array[i], ok = true; + if (sub.ranges) { + if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; } + for (var j = 0; j < sub.ranges.length; j++) { + rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff); + rebaseHistSelSingle(sub.ranges[j].head, from, to, diff); + } + continue + } + for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) { + var cur = sub.changes[j$1]; + if (to < cur.from.line) { + cur.from = Pos(cur.from.line + diff, cur.from.ch); + cur.to = Pos(cur.to.line + diff, cur.to.ch); + } else if (from <= cur.to.line) { + ok = false; + break + } + } + if (!ok) { + array.splice(0, i + 1); + i = 0; + } + } + } + + function rebaseHist(hist, change) { + var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1; + rebaseHistArray(hist.done, from, to, diff); + rebaseHistArray(hist.undone, from, to, diff); + } + + // Utility for applying a change to a line by handle or number, + // returning the number and optionally registering the line as + // changed. + function changeLine(doc, handle, changeType, op) { + var no = handle, line = handle; + if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); } + else { no = lineNo(handle); } + if (no == null) { return null } + if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); } + return line + } + + // The document is represented as a BTree consisting of leaves, with + // chunk of lines in them, and branches, with up to ten leaves or + // other branch nodes below them. The top node is always a branch + // node, and is the document object itself (meaning it has + // additional methods and properties). + // + // All nodes have parent links. The tree is used both to go from + // line numbers to line objects, and to go from objects to numbers. + // It also indexes by height, and is used to convert between height + // and line object, and to find the total height of the document. + // + // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html + + function LeafChunk(lines) { + this.lines = lines; + this.parent = null; + var height = 0; + for (var i = 0; i < lines.length; ++i) { + lines[i].parent = this; + height += lines[i].height; + } + this.height = height; + } + + LeafChunk.prototype = { + chunkSize: function() { return this.lines.length }, + + // Remove the n lines at offset 'at'. + removeInner: function(at, n) { + for (var i = at, e = at + n; i < e; ++i) { + var line = this.lines[i]; + this.height -= line.height; + cleanUpLine(line); + signalLater(line, "delete"); + } + this.lines.splice(at, n); + }, + + // Helper used to collapse a small branch into a single leaf. + collapse: function(lines) { + lines.push.apply(lines, this.lines); + }, + + // Insert the given array of lines at offset 'at', count them as + // having the given height. + insertInner: function(at, lines, height) { + this.height += height; + this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at)); + for (var i = 0; i < lines.length; ++i) { lines[i].parent = this; } + }, + + // Used to iterate over a part of the tree. + iterN: function(at, n, op) { + for (var e = at + n; at < e; ++at) + { if (op(this.lines[at])) { return true } } + } + }; + + function BranchChunk(children) { + this.children = children; + var size = 0, height = 0; + for (var i = 0; i < children.length; ++i) { + var ch = children[i]; + size += ch.chunkSize(); height += ch.height; + ch.parent = this; + } + this.size = size; + this.height = height; + this.parent = null; + } + + BranchChunk.prototype = { + chunkSize: function() { return this.size }, + + removeInner: function(at, n) { + this.size -= n; + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at < sz) { + var rm = Math.min(n, sz - at), oldHeight = child.height; + child.removeInner(at, rm); + this.height -= oldHeight - child.height; + if (sz == rm) { this.children.splice(i--, 1); child.parent = null; } + if ((n -= rm) == 0) { break } + at = 0; + } else { at -= sz; } + } + // If the result is smaller than 25 lines, ensure that it is a + // single leaf node. + if (this.size - n < 25 && + (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) { + var lines = []; + this.collapse(lines); + this.children = [new LeafChunk(lines)]; + this.children[0].parent = this; + } + }, + + collapse: function(lines) { + for (var i = 0; i < this.children.length; ++i) { this.children[i].collapse(lines); } + }, + + insertInner: function(at, lines, height) { + this.size += lines.length; + this.height += height; + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at <= sz) { + child.insertInner(at, lines, height); + if (child.lines && child.lines.length > 50) { + // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced. + // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest. + var remaining = child.lines.length % 25 + 25; + for (var pos = remaining; pos < child.lines.length;) { + var leaf = new LeafChunk(child.lines.slice(pos, pos += 25)); + child.height -= leaf.height; + this.children.splice(++i, 0, leaf); + leaf.parent = this; + } + child.lines = child.lines.slice(0, remaining); + this.maybeSpill(); + } + break + } + at -= sz; + } + }, + + // When a node has grown, check whether it should be split. + maybeSpill: function() { + if (this.children.length <= 10) { return } + var me = this; + do { + var spilled = me.children.splice(me.children.length - 5, 5); + var sibling = new BranchChunk(spilled); + if (!me.parent) { // Become the parent node + var copy = new BranchChunk(me.children); + copy.parent = me; + me.children = [copy, sibling]; + me = copy; + } else { + me.size -= sibling.size; + me.height -= sibling.height; + var myIndex = indexOf(me.parent.children, me); + me.parent.children.splice(myIndex + 1, 0, sibling); + } + sibling.parent = me.parent; + } while (me.children.length > 10) + me.parent.maybeSpill(); + }, + + iterN: function(at, n, op) { + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at < sz) { + var used = Math.min(n, sz - at); + if (child.iterN(at, used, op)) { return true } + if ((n -= used) == 0) { break } + at = 0; + } else { at -= sz; } + } + } + }; + + // Line widgets are block elements displayed above or below a line. + + var LineWidget = function(doc, node, options) { + if (options) { for (var opt in options) { if (options.hasOwnProperty(opt)) + { this[opt] = options[opt]; } } } + this.doc = doc; + this.node = node; + }; + + LineWidget.prototype.clear = function () { + var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line); + if (no == null || !ws) { return } + for (var i = 0; i < ws.length; ++i) { if (ws[i] == this) { ws.splice(i--, 1); } } + if (!ws.length) { line.widgets = null; } + var height = widgetHeight(this); + updateLineHeight(line, Math.max(0, line.height - height)); + if (cm) { + runInOp(cm, function () { + adjustScrollWhenAboveVisible(cm, line, -height); + regLineChange(cm, no, "widget"); + }); + signalLater(cm, "lineWidgetCleared", cm, this, no); + } + }; + + LineWidget.prototype.changed = function () { + var this$1 = this; + + var oldH = this.height, cm = this.doc.cm, line = this.line; + this.height = null; + var diff = widgetHeight(this) - oldH; + if (!diff) { return } + if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); } + if (cm) { + runInOp(cm, function () { + cm.curOp.forceUpdate = true; + adjustScrollWhenAboveVisible(cm, line, diff); + signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line)); + }); + } + }; + eventMixin(LineWidget); + + function adjustScrollWhenAboveVisible(cm, line, diff) { + if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop)) + { addToScrollTop(cm, diff); } + } + + function addLineWidget(doc, handle, node, options) { + var widget = new LineWidget(doc, node, options); + var cm = doc.cm; + if (cm && widget.noHScroll) { cm.display.alignWidgets = true; } + changeLine(doc, handle, "widget", function (line) { + var widgets = line.widgets || (line.widgets = []); + if (widget.insertAt == null) { widgets.push(widget); } + else { widgets.splice(Math.min(widgets.length, Math.max(0, widget.insertAt)), 0, widget); } + widget.line = line; + if (cm && !lineIsHidden(doc, line)) { + var aboveVisible = heightAtLine(line) < doc.scrollTop; + updateLineHeight(line, line.height + widgetHeight(widget)); + if (aboveVisible) { addToScrollTop(cm, widget.height); } + cm.curOp.forceUpdate = true; + } + return true + }); + if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); } + return widget + } + + // TEXTMARKERS + + // Created with markText and setBookmark methods. A TextMarker is a + // handle that can be used to clear or find a marked position in the + // document. Line objects hold arrays (markedSpans) containing + // {from, to, marker} object pointing to such marker objects, and + // indicating that such a marker is present on that line. Multiple + // lines may point to the same marker when it spans across lines. + // The spans will have null for their from/to properties when the + // marker continues beyond the start/end of the line. Markers have + // links back to the lines they currently touch. + + // Collapsed markers have unique ids, in order to be able to order + // them, which is needed for uniquely determining an outer marker + // when they overlap (they may nest, but not partially overlap). + var nextMarkerId = 0; + + var TextMarker = function(doc, type) { + this.lines = []; + this.type = type; + this.doc = doc; + this.id = ++nextMarkerId; + }; + + // Clear the marker. + TextMarker.prototype.clear = function () { + if (this.explicitlyCleared) { return } + var cm = this.doc.cm, withOp = cm && !cm.curOp; + if (withOp) { startOperation(cm); } + if (hasHandler(this, "clear")) { + var found = this.find(); + if (found) { signalLater(this, "clear", found.from, found.to); } + } + var min = null, max = null; + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); + if (cm && !this.collapsed) { regLineChange(cm, lineNo(line), "text"); } + else if (cm) { + if (span.to != null) { max = lineNo(line); } + if (span.from != null) { min = lineNo(line); } + } + line.markedSpans = removeMarkedSpan(line.markedSpans, span); + if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm) + { updateLineHeight(line, textHeight(cm.display)); } + } + if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) { + var visual = visualLine(this.lines[i$1]), len = lineLength(visual); + if (len > cm.display.maxLineLength) { + cm.display.maxLine = visual; + cm.display.maxLineLength = len; + cm.display.maxLineChanged = true; + } + } } + + if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); } + this.lines.length = 0; + this.explicitlyCleared = true; + if (this.atomic && this.doc.cantEdit) { + this.doc.cantEdit = false; + if (cm) { reCheckSelection(cm.doc); } + } + if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); } + if (withOp) { endOperation(cm); } + if (this.parent) { this.parent.clear(); } + }; + + // Find the position of the marker in the document. Returns a {from, + // to} object by default. Side can be passed to get a specific side + // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the + // Pos objects returned contain a line object, rather than a line + // number (used to prevent looking up the same line twice). + TextMarker.prototype.find = function (side, lineObj) { + if (side == null && this.type == "bookmark") { side = 1; } + var from, to; + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); + if (span.from != null) { + from = Pos(lineObj ? line : lineNo(line), span.from); + if (side == -1) { return from } + } + if (span.to != null) { + to = Pos(lineObj ? line : lineNo(line), span.to); + if (side == 1) { return to } + } + } + return from && {from: from, to: to} + }; + + // Signals that the marker's widget changed, and surrounding layout + // should be recomputed. + TextMarker.prototype.changed = function () { + var this$1 = this; + + var pos = this.find(-1, true), widget = this, cm = this.doc.cm; + if (!pos || !cm) { return } + runInOp(cm, function () { + var line = pos.line, lineN = lineNo(pos.line); + var view = findViewForLine(cm, lineN); + if (view) { + clearLineMeasurementCacheFor(view); + cm.curOp.selectionChanged = cm.curOp.forceUpdate = true; + } + cm.curOp.updateMaxLine = true; + if (!lineIsHidden(widget.doc, line) && widget.height != null) { + var oldHeight = widget.height; + widget.height = null; + var dHeight = widgetHeight(widget) - oldHeight; + if (dHeight) + { updateLineHeight(line, line.height + dHeight); } + } + signalLater(cm, "markerChanged", cm, this$1); + }); + }; + + TextMarker.prototype.attachLine = function (line) { + if (!this.lines.length && this.doc.cm) { + var op = this.doc.cm.curOp; + if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) + { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); } + } + this.lines.push(line); + }; + + TextMarker.prototype.detachLine = function (line) { + this.lines.splice(indexOf(this.lines, line), 1); + if (!this.lines.length && this.doc.cm) { + var op = this.doc.cm.curOp + ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this); + } + }; + eventMixin(TextMarker); + + // Create a marker, wire it up to the right lines, and + function markText(doc, from, to, options, type) { + // Shared markers (across linked documents) are handled separately + // (markTextShared will call out to this again, once per + // document). + if (options && options.shared) { return markTextShared(doc, from, to, options, type) } + // Ensure we are in an operation. + if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) } + + var marker = new TextMarker(doc, type), diff = cmp(from, to); + if (options) { copyObj(options, marker, false); } + // Don't connect empty markers unless clearWhenEmpty is false + if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false) + { return marker } + if (marker.replacedWith) { + // Showing up as a widget implies collapsed (widget replaces text) + marker.collapsed = true; + marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget"); + if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); } + if (options.insertLeft) { marker.widgetNode.insertLeft = true; } + } + if (marker.collapsed) { + if (conflictingCollapsedRange(doc, from.line, from, to, marker) || + from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker)) + { throw new Error("Inserting collapsed marker partially overlapping an existing one") } + seeCollapsedSpans(); + } + + if (marker.addToHistory) + { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); } + + var curLine = from.line, cm = doc.cm, updateMaxLine; + doc.iter(curLine, to.line + 1, function (line) { + if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine) + { updateMaxLine = true; } + if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); } + addMarkedSpan(line, new MarkedSpan(marker, + curLine == from.line ? from.ch : null, + curLine == to.line ? to.ch : null), doc.cm && doc.cm.curOp); + ++curLine; + }); + // lineIsHidden depends on the presence of the spans, so needs a second pass + if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) { + if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); } + }); } + + if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); } + + if (marker.readOnly) { + seeReadOnlySpans(); + if (doc.history.done.length || doc.history.undone.length) + { doc.clearHistory(); } + } + if (marker.collapsed) { + marker.id = ++nextMarkerId; + marker.atomic = true; + } + if (cm) { + // Sync editor state + if (updateMaxLine) { cm.curOp.updateMaxLine = true; } + if (marker.collapsed) + { regChange(cm, from.line, to.line + 1); } + else if (marker.className || marker.startStyle || marker.endStyle || marker.css || + marker.attributes || marker.title) + { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } } + if (marker.atomic) { reCheckSelection(cm.doc); } + signalLater(cm, "markerAdded", cm, marker); + } + return marker + } + + // SHARED TEXTMARKERS + + // A shared marker spans multiple linked documents. It is + // implemented as a meta-marker-object controlling multiple normal + // markers. + var SharedTextMarker = function(markers, primary) { + this.markers = markers; + this.primary = primary; + for (var i = 0; i < markers.length; ++i) + { markers[i].parent = this; } + }; + + SharedTextMarker.prototype.clear = function () { + if (this.explicitlyCleared) { return } + this.explicitlyCleared = true; + for (var i = 0; i < this.markers.length; ++i) + { this.markers[i].clear(); } + signalLater(this, "clear"); + }; + + SharedTextMarker.prototype.find = function (side, lineObj) { + return this.primary.find(side, lineObj) + }; + eventMixin(SharedTextMarker); + + function markTextShared(doc, from, to, options, type) { + options = copyObj(options); + options.shared = false; + var markers = [markText(doc, from, to, options, type)], primary = markers[0]; + var widget = options.widgetNode; + linkedDocs(doc, function (doc) { + if (widget) { options.widgetNode = widget.cloneNode(true); } + markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type)); + for (var i = 0; i < doc.linked.length; ++i) + { if (doc.linked[i].isParent) { return } } + primary = lst(markers); + }); + return new SharedTextMarker(markers, primary) + } + + function findSharedMarkers(doc) { + return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; }) + } + + function copySharedMarkers(doc, markers) { + for (var i = 0; i < markers.length; i++) { + var marker = markers[i], pos = marker.find(); + var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to); + if (cmp(mFrom, mTo)) { + var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type); + marker.markers.push(subMark); + subMark.parent = marker; + } + } + } + + function detachSharedMarkers(markers) { + var loop = function ( i ) { + var marker = markers[i], linked = [marker.primary.doc]; + linkedDocs(marker.primary.doc, function (d) { return linked.push(d); }); + for (var j = 0; j < marker.markers.length; j++) { + var subMarker = marker.markers[j]; + if (indexOf(linked, subMarker.doc) == -1) { + subMarker.parent = null; + marker.markers.splice(j--, 1); + } + } + }; + + for (var i = 0; i < markers.length; i++) loop( i ); + } + + var nextDocId = 0; + var Doc = function(text, mode, firstLine, lineSep, direction) { + if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) } + if (firstLine == null) { firstLine = 0; } + + BranchChunk.call(this, [new LeafChunk([new Line("", null)])]); + this.first = firstLine; + this.scrollTop = this.scrollLeft = 0; + this.cantEdit = false; + this.cleanGeneration = 1; + this.modeFrontier = this.highlightFrontier = firstLine; + var start = Pos(firstLine, 0); + this.sel = simpleSelection(start); + this.history = new History(null); + this.id = ++nextDocId; + this.modeOption = mode; + this.lineSep = lineSep; + this.direction = (direction == "rtl") ? "rtl" : "ltr"; + this.extend = false; + + if (typeof text == "string") { text = this.splitLines(text); } + updateDoc(this, {from: start, to: start, text: text}); + setSelection(this, simpleSelection(start), sel_dontScroll); + }; + + Doc.prototype = createObj(BranchChunk.prototype, { + constructor: Doc, + // Iterate over the document. Supports two forms -- with only one + // argument, it calls that for each line in the document. With + // three, it iterates over the range given by the first two (with + // the second being non-inclusive). + iter: function(from, to, op) { + if (op) { this.iterN(from - this.first, to - from, op); } + else { this.iterN(this.first, this.first + this.size, from); } + }, + + // Non-public interface for adding and removing lines. + insert: function(at, lines) { + var height = 0; + for (var i = 0; i < lines.length; ++i) { height += lines[i].height; } + this.insertInner(at - this.first, lines, height); + }, + remove: function(at, n) { this.removeInner(at - this.first, n); }, + + // From here, the methods are part of the public interface. Most + // are also available from CodeMirror (editor) instances. + + getValue: function(lineSep) { + var lines = getLines(this, this.first, this.first + this.size); + if (lineSep === false) { return lines } + return lines.join(lineSep || this.lineSeparator()) + }, + setValue: docMethodOp(function(code) { + var top = Pos(this.first, 0), last = this.first + this.size - 1; + makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length), + text: this.splitLines(code), origin: "setValue", full: true}, true); + if (this.cm) { scrollToCoords(this.cm, 0, 0); } + setSelection(this, simpleSelection(top), sel_dontScroll); + }), + replaceRange: function(code, from, to, origin) { + from = clipPos(this, from); + to = to ? clipPos(this, to) : from; + replaceRange(this, code, from, to, origin); + }, + getRange: function(from, to, lineSep) { + var lines = getBetween(this, clipPos(this, from), clipPos(this, to)); + if (lineSep === false) { return lines } + if (lineSep === '') { return lines.join('') } + return lines.join(lineSep || this.lineSeparator()) + }, + + getLine: function(line) {var l = this.getLineHandle(line); return l && l.text}, + + getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }}, + getLineNumber: function(line) {return lineNo(line)}, + + getLineHandleVisualStart: function(line) { + if (typeof line == "number") { line = getLine(this, line); } + return visualLine(line) + }, + + lineCount: function() {return this.size}, + firstLine: function() {return this.first}, + lastLine: function() {return this.first + this.size - 1}, + + clipPos: function(pos) {return clipPos(this, pos)}, + + getCursor: function(start) { + var range = this.sel.primary(), pos; + if (start == null || start == "head") { pos = range.head; } + else if (start == "anchor") { pos = range.anchor; } + else if (start == "end" || start == "to" || start === false) { pos = range.to(); } + else { pos = range.from(); } + return pos + }, + listSelections: function() { return this.sel.ranges }, + somethingSelected: function() {return this.sel.somethingSelected()}, + + setCursor: docMethodOp(function(line, ch, options) { + setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options); + }), + setSelection: docMethodOp(function(anchor, head, options) { + setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options); + }), + extendSelection: docMethodOp(function(head, other, options) { + extendSelection(this, clipPos(this, head), other && clipPos(this, other), options); + }), + extendSelections: docMethodOp(function(heads, options) { + extendSelections(this, clipPosArray(this, heads), options); + }), + extendSelectionsBy: docMethodOp(function(f, options) { + var heads = map(this.sel.ranges, f); + extendSelections(this, clipPosArray(this, heads), options); + }), + setSelections: docMethodOp(function(ranges, primary, options) { + if (!ranges.length) { return } + var out = []; + for (var i = 0; i < ranges.length; i++) + { out[i] = new Range(clipPos(this, ranges[i].anchor), + clipPos(this, ranges[i].head || ranges[i].anchor)); } + if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); } + setSelection(this, normalizeSelection(this.cm, out, primary), options); + }), + addSelection: docMethodOp(function(anchor, head, options) { + var ranges = this.sel.ranges.slice(0); + ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor))); + setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options); + }), + + getSelection: function(lineSep) { + var ranges = this.sel.ranges, lines; + for (var i = 0; i < ranges.length; i++) { + var sel = getBetween(this, ranges[i].from(), ranges[i].to()); + lines = lines ? lines.concat(sel) : sel; + } + if (lineSep === false) { return lines } + else { return lines.join(lineSep || this.lineSeparator()) } + }, + getSelections: function(lineSep) { + var parts = [], ranges = this.sel.ranges; + for (var i = 0; i < ranges.length; i++) { + var sel = getBetween(this, ranges[i].from(), ranges[i].to()); + if (lineSep !== false) { sel = sel.join(lineSep || this.lineSeparator()); } + parts[i] = sel; + } + return parts + }, + replaceSelection: function(code, collapse, origin) { + var dup = []; + for (var i = 0; i < this.sel.ranges.length; i++) + { dup[i] = code; } + this.replaceSelections(dup, collapse, origin || "+input"); + }, + replaceSelections: docMethodOp(function(code, collapse, origin) { + var changes = [], sel = this.sel; + for (var i = 0; i < sel.ranges.length; i++) { + var range = sel.ranges[i]; + changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin}; + } + var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse); + for (var i$1 = changes.length - 1; i$1 >= 0; i$1--) + { makeChange(this, changes[i$1]); } + if (newSel) { setSelectionReplaceHistory(this, newSel); } + else if (this.cm) { ensureCursorVisible(this.cm); } + }), + undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}), + redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}), + undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}), + redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}), + + setExtending: function(val) {this.extend = val;}, + getExtending: function() {return this.extend}, + + historySize: function() { + var hist = this.history, done = 0, undone = 0; + for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } } + for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } } + return {undo: done, redo: undone} + }, + clearHistory: function() { + var this$1 = this; + + this.history = new History(this.history); + linkedDocs(this, function (doc) { return doc.history = this$1.history; }, true); + }, + + markClean: function() { + this.cleanGeneration = this.changeGeneration(true); + }, + changeGeneration: function(forceSplit) { + if (forceSplit) + { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; } + return this.history.generation + }, + isClean: function (gen) { + return this.history.generation == (gen || this.cleanGeneration) + }, + + getHistory: function() { + return {done: copyHistoryArray(this.history.done), + undone: copyHistoryArray(this.history.undone)} + }, + setHistory: function(histData) { + var hist = this.history = new History(this.history); + hist.done = copyHistoryArray(histData.done.slice(0), null, true); + hist.undone = copyHistoryArray(histData.undone.slice(0), null, true); + }, + + setGutterMarker: docMethodOp(function(line, gutterID, value) { + return changeLine(this, line, "gutter", function (line) { + var markers = line.gutterMarkers || (line.gutterMarkers = {}); + markers[gutterID] = value; + if (!value && isEmpty(markers)) { line.gutterMarkers = null; } + return true + }) + }), + + clearGutter: docMethodOp(function(gutterID) { + var this$1 = this; + + this.iter(function (line) { + if (line.gutterMarkers && line.gutterMarkers[gutterID]) { + changeLine(this$1, line, "gutter", function () { + line.gutterMarkers[gutterID] = null; + if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; } + return true + }); + } + }); + }), + + lineInfo: function(line) { + var n; + if (typeof line == "number") { + if (!isLine(this, line)) { return null } + n = line; + line = getLine(this, line); + if (!line) { return null } + } else { + n = lineNo(line); + if (n == null) { return null } + } + return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, + textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass, + widgets: line.widgets} + }, + + addLineClass: docMethodOp(function(handle, where, cls) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { + var prop = where == "text" ? "textClass" + : where == "background" ? "bgClass" + : where == "gutter" ? "gutterClass" : "wrapClass"; + if (!line[prop]) { line[prop] = cls; } + else if (classTest(cls).test(line[prop])) { return false } + else { line[prop] += " " + cls; } + return true + }) + }), + removeLineClass: docMethodOp(function(handle, where, cls) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { + var prop = where == "text" ? "textClass" + : where == "background" ? "bgClass" + : where == "gutter" ? "gutterClass" : "wrapClass"; + var cur = line[prop]; + if (!cur) { return false } + else if (cls == null) { line[prop] = null; } + else { + var found = cur.match(classTest(cls)); + if (!found) { return false } + var end = found.index + found[0].length; + line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null; + } + return true + }) + }), + + addLineWidget: docMethodOp(function(handle, node, options) { + return addLineWidget(this, handle, node, options) + }), + removeLineWidget: function(widget) { widget.clear(); }, + + markText: function(from, to, options) { + return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range") + }, + setBookmark: function(pos, options) { + var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options), + insertLeft: options && options.insertLeft, + clearWhenEmpty: false, shared: options && options.shared, + handleMouseEvents: options && options.handleMouseEvents}; + pos = clipPos(this, pos); + return markText(this, pos, pos, realOpts, "bookmark") + }, + findMarksAt: function(pos) { + pos = clipPos(this, pos); + var markers = [], spans = getLine(this, pos.line).markedSpans; + if (spans) { for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if ((span.from == null || span.from <= pos.ch) && + (span.to == null || span.to >= pos.ch)) + { markers.push(span.marker.parent || span.marker); } + } } + return markers + }, + findMarks: function(from, to, filter) { + from = clipPos(this, from); to = clipPos(this, to); + var found = [], lineNo = from.line; + this.iter(from.line, to.line + 1, function (line) { + var spans = line.markedSpans; + if (spans) { for (var i = 0; i < spans.length; i++) { + var span = spans[i]; + if (!(span.to != null && lineNo == from.line && from.ch >= span.to || + span.from == null && lineNo != from.line || + span.from != null && lineNo == to.line && span.from >= to.ch) && + (!filter || filter(span.marker))) + { found.push(span.marker.parent || span.marker); } + } } + ++lineNo; + }); + return found + }, + getAllMarks: function() { + var markers = []; + this.iter(function (line) { + var sps = line.markedSpans; + if (sps) { for (var i = 0; i < sps.length; ++i) + { if (sps[i].from != null) { markers.push(sps[i].marker); } } } + }); + return markers + }, + + posFromIndex: function(off) { + var ch, lineNo = this.first, sepSize = this.lineSeparator().length; + this.iter(function (line) { + var sz = line.text.length + sepSize; + if (sz > off) { ch = off; return true } + off -= sz; + ++lineNo; + }); + return clipPos(this, Pos(lineNo, ch)) + }, + indexFromPos: function (coords) { + coords = clipPos(this, coords); + var index = coords.ch; + if (coords.line < this.first || coords.ch < 0) { return 0 } + var sepSize = this.lineSeparator().length; + this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value + index += line.text.length + sepSize; + }); + return index + }, + + copy: function(copyHistory) { + var doc = new Doc(getLines(this, this.first, this.first + this.size), + this.modeOption, this.first, this.lineSep, this.direction); + doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft; + doc.sel = this.sel; + doc.extend = false; + if (copyHistory) { + doc.history.undoDepth = this.history.undoDepth; + doc.setHistory(this.getHistory()); + } + return doc + }, + + linkedDoc: function(options) { + if (!options) { options = {}; } + var from = this.first, to = this.first + this.size; + if (options.from != null && options.from > from) { from = options.from; } + if (options.to != null && options.to < to) { to = options.to; } + var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction); + if (options.sharedHist) { copy.history = this.history + ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist}); + copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]; + copySharedMarkers(copy, findSharedMarkers(this)); + return copy + }, + unlinkDoc: function(other) { + if (other instanceof CodeMirror) { other = other.doc; } + if (this.linked) { for (var i = 0; i < this.linked.length; ++i) { + var link = this.linked[i]; + if (link.doc != other) { continue } + this.linked.splice(i, 1); + other.unlinkDoc(this); + detachSharedMarkers(findSharedMarkers(this)); + break + } } + // If the histories were shared, split them again + if (other.history == this.history) { + var splitIds = [other.id]; + linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true); + other.history = new History(null); + other.history.done = copyHistoryArray(this.history.done, splitIds); + other.history.undone = copyHistoryArray(this.history.undone, splitIds); + } + }, + iterLinkedDocs: function(f) {linkedDocs(this, f);}, + + getMode: function() {return this.mode}, + getEditor: function() {return this.cm}, + + splitLines: function(str) { + if (this.lineSep) { return str.split(this.lineSep) } + return splitLinesAuto(str) + }, + lineSeparator: function() { return this.lineSep || "\n" }, + + setDirection: docMethodOp(function (dir) { + if (dir != "rtl") { dir = "ltr"; } + if (dir == this.direction) { return } + this.direction = dir; + this.iter(function (line) { return line.order = null; }); + if (this.cm) { directionChanged(this.cm); } + }) + }); + + // Public alias. + Doc.prototype.eachLine = Doc.prototype.iter; + + // Kludge to work around strange IE behavior where it'll sometimes + // re-fire a series of drag-related events right after the drop (#1551) + var lastDrop = 0; + + function onDrop(e) { + var cm = this; + clearDragCursor(cm); + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) + { return } + e_preventDefault(e); + if (ie) { lastDrop = +new Date; } + var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files; + if (!pos || cm.isReadOnly()) { return } + // Might be a file drop, in which case we simply extract the text + // and insert it. + if (files && files.length && window.FileReader && window.File) { + var n = files.length, text = Array(n), read = 0; + var markAsReadAndPasteIfAllFilesAreRead = function () { + if (++read == n) { + operation(cm, function () { + pos = clipPos(cm.doc, pos); + var change = {from: pos, to: pos, + text: cm.doc.splitLines( + text.filter(function (t) { return t != null; }).join(cm.doc.lineSeparator())), + origin: "paste"}; + makeChange(cm.doc, change); + setSelectionReplaceHistory(cm.doc, simpleSelection(clipPos(cm.doc, pos), clipPos(cm.doc, changeEnd(change)))); + })(); + } + }; + var readTextFromFile = function (file, i) { + if (cm.options.allowDropFileTypes && + indexOf(cm.options.allowDropFileTypes, file.type) == -1) { + markAsReadAndPasteIfAllFilesAreRead(); + return + } + var reader = new FileReader; + reader.onerror = function () { return markAsReadAndPasteIfAllFilesAreRead(); }; + reader.onload = function () { + var content = reader.result; + if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { + markAsReadAndPasteIfAllFilesAreRead(); + return + } + text[i] = content; + markAsReadAndPasteIfAllFilesAreRead(); + }; + reader.readAsText(file); + }; + for (var i = 0; i < files.length; i++) { readTextFromFile(files[i], i); } + } else { // Normal drop + // Don't do a replace if the drop happened inside of the selected text. + if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) { + cm.state.draggingText(e); + // Ensure the editor is re-focused + setTimeout(function () { return cm.display.input.focus(); }, 20); + return + } + try { + var text$1 = e.dataTransfer.getData("Text"); + if (text$1) { + var selected; + if (cm.state.draggingText && !cm.state.draggingText.copy) + { selected = cm.listSelections(); } + setSelectionNoUndo(cm.doc, simpleSelection(pos, pos)); + if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1) + { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } } + cm.replaceSelection(text$1, "around", "paste"); + cm.display.input.focus(); + } + } + catch(e$1){} + } + } + + function onDragStart(cm, e) { + if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return } + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return } + + e.dataTransfer.setData("Text", cm.getSelection()); + e.dataTransfer.effectAllowed = "copyMove"; + + // Use dummy image instead of default browsers image. + // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there. + if (e.dataTransfer.setDragImage && !safari) { + var img = elt("img", null, null, "position: fixed; left: 0; top: 0;"); + img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; + if (presto) { + img.width = img.height = 1; + cm.display.wrapper.appendChild(img); + // Force a relayout, or Opera won't use our image for some obscure reason + img._top = img.offsetTop; + } + e.dataTransfer.setDragImage(img, 0, 0); + if (presto) { img.parentNode.removeChild(img); } + } + } + + function onDragOver(cm, e) { + var pos = posFromMouse(cm, e); + if (!pos) { return } + var frag = document.createDocumentFragment(); + drawSelectionCursor(cm, pos, frag); + if (!cm.display.dragCursor) { + cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors"); + cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv); + } + removeChildrenAndAdd(cm.display.dragCursor, frag); + } + + function clearDragCursor(cm) { + if (cm.display.dragCursor) { + cm.display.lineSpace.removeChild(cm.display.dragCursor); + cm.display.dragCursor = null; + } + } + + // These must be handled carefully, because naively registering a + // handler for each editor will cause the editors to never be + // garbage collected. + + function forEachCodeMirror(f) { + if (!document.getElementsByClassName) { return } + var byClass = document.getElementsByClassName("CodeMirror"), editors = []; + for (var i = 0; i < byClass.length; i++) { + var cm = byClass[i].CodeMirror; + if (cm) { editors.push(cm); } + } + if (editors.length) { editors[0].operation(function () { + for (var i = 0; i < editors.length; i++) { f(editors[i]); } + }); } + } + + var globalsRegistered = false; + function ensureGlobalHandlers() { + if (globalsRegistered) { return } + registerGlobalHandlers(); + globalsRegistered = true; + } + function registerGlobalHandlers() { + // When the window resizes, we need to refresh active editors. + var resizeTimer; + on(window, "resize", function () { + if (resizeTimer == null) { resizeTimer = setTimeout(function () { + resizeTimer = null; + forEachCodeMirror(onResize); + }, 100); } + }); + // When the window loses focus, we want to show the editor as blurred + on(window, "blur", function () { return forEachCodeMirror(onBlur); }); + } + // Called when the window resizes + function onResize(cm) { + var d = cm.display; + // Might be a text scaling operation, clear size caches. + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; + d.scrollbarsClipped = false; + cm.setSize(); + } + + var keyNames = { + 3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", + 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", + 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", + 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", + 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 145: "ScrollLock", + 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", + 221: "]", 222: "'", 224: "Mod", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", + 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert" + }; + + // Number keys + for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); } + // Alphabetic keys + for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); } + // Function keys + for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; } + + var keyMap = {}; + + keyMap.basic = { + "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", + "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", + "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore", + "Tab": "defaultTab", "Shift-Tab": "indentAuto", + "Enter": "newlineAndIndent", "Insert": "toggleOverwrite", + "Esc": "singleSelection" + }; + // Note that the save and find-related commands aren't defined by + // default. User code or addons can define them. Unknown commands + // are simply ignored. + keyMap.pcDefault = { + "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", + "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown", + "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", + "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find", + "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", + "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", + "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection", + "fallthrough": "basic" + }; + // Very basic readline/emacs-style bindings, which are standard on Mac. + keyMap.emacsy = { + "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", + "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", + "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", + "Ctrl-T": "transposeChars", "Ctrl-O": "openLine" + }; + keyMap.macDefault = { + "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", + "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft", + "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore", + "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find", + "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", + "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight", + "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd", + "fallthrough": ["basic", "emacsy"] + }; + keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault; + + // KEYMAP DISPATCH + + function normalizeKeyName(name) { + var parts = name.split(/-(?!$)/); + name = parts[parts.length - 1]; + var alt, ctrl, shift, cmd; + for (var i = 0; i < parts.length - 1; i++) { + var mod = parts[i]; + if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; } + else if (/^a(lt)?$/i.test(mod)) { alt = true; } + else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; } + else if (/^s(hift)?$/i.test(mod)) { shift = true; } + else { throw new Error("Unrecognized modifier name: " + mod) } + } + if (alt) { name = "Alt-" + name; } + if (ctrl) { name = "Ctrl-" + name; } + if (cmd) { name = "Cmd-" + name; } + if (shift) { name = "Shift-" + name; } + return name + } + + // This is a kludge to keep keymaps mostly working as raw objects + // (backwards compatibility) while at the same time support features + // like normalization and multi-stroke key bindings. It compiles a + // new normalized keymap, and then updates the old object to reflect + // this. + function normalizeKeyMap(keymap) { + var copy = {}; + for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) { + var value = keymap[keyname]; + if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue } + if (value == "...") { delete keymap[keyname]; continue } + + var keys = map(keyname.split(" "), normalizeKeyName); + for (var i = 0; i < keys.length; i++) { + var val = (void 0), name = (void 0); + if (i == keys.length - 1) { + name = keys.join(" "); + val = value; + } else { + name = keys.slice(0, i + 1).join(" "); + val = "..."; + } + var prev = copy[name]; + if (!prev) { copy[name] = val; } + else if (prev != val) { throw new Error("Inconsistent bindings for " + name) } + } + delete keymap[keyname]; + } } + for (var prop in copy) { keymap[prop] = copy[prop]; } + return keymap + } + + function lookupKey(key, map, handle, context) { + map = getKeyMap(map); + var found = map.call ? map.call(key, context) : map[key]; + if (found === false) { return "nothing" } + if (found === "...") { return "multi" } + if (found != null && handle(found)) { return "handled" } + + if (map.fallthrough) { + if (Object.prototype.toString.call(map.fallthrough) != "[object Array]") + { return lookupKey(key, map.fallthrough, handle, context) } + for (var i = 0; i < map.fallthrough.length; i++) { + var result = lookupKey(key, map.fallthrough[i], handle, context); + if (result) { return result } + } + } + } + + // Modifier key presses don't count as 'real' key presses for the + // purpose of keymap fallthrough. + function isModifierKey(value) { + var name = typeof value == "string" ? value : keyNames[value.keyCode]; + return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod" + } + + function addModifierNames(name, event, noShift) { + var base = name; + if (event.altKey && base != "Alt") { name = "Alt-" + name; } + if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; } + if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Mod") { name = "Cmd-" + name; } + if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; } + return name + } + + // Look up the name of a key as indicated by an event object. + function keyName(event, noShift) { + if (presto && event.keyCode == 34 && event["char"]) { return false } + var name = keyNames[event.keyCode]; + if (name == null || event.altGraphKey) { return false } + // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause, + // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+) + if (event.keyCode == 3 && event.code) { name = event.code; } + return addModifierNames(name, event, noShift) + } + + function getKeyMap(val) { + return typeof val == "string" ? keyMap[val] : val + } + + // Helper for deleting text near the selection(s), used to implement + // backspace, delete, and similar functionality. + function deleteNearSelection(cm, compute) { + var ranges = cm.doc.sel.ranges, kill = []; + // Build up a set of ranges to kill first, merging overlapping + // ranges. + for (var i = 0; i < ranges.length; i++) { + var toKill = compute(ranges[i]); + while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) { + var replaced = kill.pop(); + if (cmp(replaced.from, toKill.from) < 0) { + toKill.from = replaced.from; + break + } + } + kill.push(toKill); + } + // Next, remove those actual ranges. + runInOp(cm, function () { + for (var i = kill.length - 1; i >= 0; i--) + { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); } + ensureCursorVisible(cm); + }); + } + + function moveCharLogically(line, ch, dir) { + var target = skipExtendingChars(line.text, ch + dir, dir); + return target < 0 || target > line.text.length ? null : target + } + + function moveLogically(line, start, dir) { + var ch = moveCharLogically(line, start.ch, dir); + return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before") + } + + function endOfLine(visually, cm, lineObj, lineNo, dir) { + if (visually) { + if (cm.doc.direction == "rtl") { dir = -dir; } + var order = getOrder(lineObj, cm.doc.direction); + if (order) { + var part = dir < 0 ? lst(order) : order[0]; + var moveInStorageOrder = (dir < 0) == (part.level == 1); + var sticky = moveInStorageOrder ? "after" : "before"; + var ch; + // With a wrapped rtl chunk (possibly spanning multiple bidi parts), + // it could be that the last bidi part is not on the last visual line, + // since visual lines contain content order-consecutive chunks. + // Thus, in rtl, we are looking for the first (content-order) character + // in the rtl chunk that is on the last line (that is, the same line + // as the last (content-order) character). + if (part.level > 0 || cm.doc.direction == "rtl") { + var prep = prepareMeasureForLine(cm, lineObj); + ch = dir < 0 ? lineObj.text.length - 1 : 0; + var targetTop = measureCharPrepared(cm, prep, ch).top; + ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch); + if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); } + } else { ch = dir < 0 ? part.to : part.from; } + return new Pos(lineNo, ch, sticky) + } + } + return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after") + } + + function moveVisually(cm, line, start, dir) { + var bidi = getOrder(line, cm.doc.direction); + if (!bidi) { return moveLogically(line, start, dir) } + if (start.ch >= line.text.length) { + start.ch = line.text.length; + start.sticky = "before"; + } else if (start.ch <= 0) { + start.ch = 0; + start.sticky = "after"; + } + var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]; + if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) { + // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines, + // nothing interesting happens. + return moveLogically(line, start, dir) + } + + var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }; + var prep; + var getWrappedLineExtent = function (ch) { + if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} } + prep = prep || prepareMeasureForLine(cm, line); + return wrappedLineExtentChar(cm, line, prep, ch) + }; + var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch); + + if (cm.doc.direction == "rtl" || part.level == 1) { + var moveInStorageOrder = (part.level == 1) == (dir < 0); + var ch = mv(start, moveInStorageOrder ? 1 : -1); + if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) { + // Case 2: We move within an rtl part or in an rtl editor on the same visual line + var sticky = moveInStorageOrder ? "before" : "after"; + return new Pos(start.line, ch, sticky) + } + } + + // Case 3: Could not move within this bidi part in this visual line, so leave + // the current bidi part + + var searchInVisualLine = function (partPos, dir, wrappedLineExtent) { + var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder + ? new Pos(start.line, mv(ch, 1), "before") + : new Pos(start.line, ch, "after"); }; + + for (; partPos >= 0 && partPos < bidi.length; partPos += dir) { + var part = bidi[partPos]; + var moveInStorageOrder = (dir > 0) == (part.level != 1); + var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1); + if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) } + ch = moveInStorageOrder ? part.from : mv(part.to, -1); + if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) } + } + }; + + // Case 3a: Look for other bidi parts on the same visual line + var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent); + if (res) { return res } + + // Case 3b: Look for other bidi parts on the next visual line + var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1); + if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) { + res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh)); + if (res) { return res } + } + + // Case 4: Nowhere to move + return null + } + + // Commands are parameter-less actions that can be performed on an + // editor, mostly used for keybindings. + var commands = { + selectAll: selectAll, + singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); }, + killLine: function (cm) { return deleteNearSelection(cm, function (range) { + if (range.empty()) { + var len = getLine(cm.doc, range.head.line).text.length; + if (range.head.ch == len && range.head.line < cm.lastLine()) + { return {from: range.head, to: Pos(range.head.line + 1, 0)} } + else + { return {from: range.head, to: Pos(range.head.line, len)} } + } else { + return {from: range.from(), to: range.to()} + } + }); }, + deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({ + from: Pos(range.from().line, 0), + to: clipPos(cm.doc, Pos(range.to().line + 1, 0)) + }); }); }, + delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({ + from: Pos(range.from().line, 0), to: range.from() + }); }); }, + delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { + var top = cm.charCoords(range.head, "div").top + 5; + var leftPos = cm.coordsChar({left: 0, top: top}, "div"); + return {from: leftPos, to: range.from()} + }); }, + delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) { + var top = cm.charCoords(range.head, "div").top + 5; + var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"); + return {from: range.from(), to: rightPos } + }); }, + undo: function (cm) { return cm.undo(); }, + redo: function (cm) { return cm.redo(); }, + undoSelection: function (cm) { return cm.undoSelection(); }, + redoSelection: function (cm) { return cm.redoSelection(); }, + goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); }, + goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); }, + goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); }, + {origin: "+move", bias: 1} + ); }, + goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); }, + {origin: "+move", bias: 1} + ); }, + goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); }, + {origin: "+move", bias: -1} + ); }, + goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.cursorCoords(range.head, "div").top + 5; + return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") + }, sel_move); }, + goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.cursorCoords(range.head, "div").top + 5; + return cm.coordsChar({left: 0, top: top}, "div") + }, sel_move); }, + goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.cursorCoords(range.head, "div").top + 5; + var pos = cm.coordsChar({left: 0, top: top}, "div"); + if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) } + return pos + }, sel_move); }, + goLineUp: function (cm) { return cm.moveV(-1, "line"); }, + goLineDown: function (cm) { return cm.moveV(1, "line"); }, + goPageUp: function (cm) { return cm.moveV(-1, "page"); }, + goPageDown: function (cm) { return cm.moveV(1, "page"); }, + goCharLeft: function (cm) { return cm.moveH(-1, "char"); }, + goCharRight: function (cm) { return cm.moveH(1, "char"); }, + goColumnLeft: function (cm) { return cm.moveH(-1, "column"); }, + goColumnRight: function (cm) { return cm.moveH(1, "column"); }, + goWordLeft: function (cm) { return cm.moveH(-1, "word"); }, + goGroupRight: function (cm) { return cm.moveH(1, "group"); }, + goGroupLeft: function (cm) { return cm.moveH(-1, "group"); }, + goWordRight: function (cm) { return cm.moveH(1, "word"); }, + delCharBefore: function (cm) { return cm.deleteH(-1, "codepoint"); }, + delCharAfter: function (cm) { return cm.deleteH(1, "char"); }, + delWordBefore: function (cm) { return cm.deleteH(-1, "word"); }, + delWordAfter: function (cm) { return cm.deleteH(1, "word"); }, + delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); }, + delGroupAfter: function (cm) { return cm.deleteH(1, "group"); }, + indentAuto: function (cm) { return cm.indentSelection("smart"); }, + indentMore: function (cm) { return cm.indentSelection("add"); }, + indentLess: function (cm) { return cm.indentSelection("subtract"); }, + insertTab: function (cm) { return cm.replaceSelection("\t"); }, + insertSoftTab: function (cm) { + var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize; + for (var i = 0; i < ranges.length; i++) { + var pos = ranges[i].from(); + var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize); + spaces.push(spaceStr(tabSize - col % tabSize)); + } + cm.replaceSelections(spaces); + }, + defaultTab: function (cm) { + if (cm.somethingSelected()) { cm.indentSelection("add"); } + else { cm.execCommand("insertTab"); } + }, + // Swap the two chars left and right of each selection's head. + // Move cursor behind the two swapped characters afterwards. + // + // Doesn't consider line feeds a character. + // Doesn't scan more than one line above to find a character. + // Doesn't do anything on an empty line. + // Doesn't do anything with non-empty selections. + transposeChars: function (cm) { return runInOp(cm, function () { + var ranges = cm.listSelections(), newSel = []; + for (var i = 0; i < ranges.length; i++) { + if (!ranges[i].empty()) { continue } + var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text; + if (line) { + if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); } + if (cur.ch > 0) { + cur = new Pos(cur.line, cur.ch + 1); + cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), + Pos(cur.line, cur.ch - 2), cur, "+transpose"); + } else if (cur.line > cm.doc.first) { + var prev = getLine(cm.doc, cur.line - 1).text; + if (prev) { + cur = new Pos(cur.line, 1); + cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() + + prev.charAt(prev.length - 1), + Pos(cur.line - 1, prev.length - 1), cur, "+transpose"); + } + } + } + newSel.push(new Range(cur, cur)); + } + cm.setSelections(newSel); + }); }, + newlineAndIndent: function (cm) { return runInOp(cm, function () { + var sels = cm.listSelections(); + for (var i = sels.length - 1; i >= 0; i--) + { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); } + sels = cm.listSelections(); + for (var i$1 = 0; i$1 < sels.length; i$1++) + { cm.indentLine(sels[i$1].from().line, null, true); } + ensureCursorVisible(cm); + }); }, + openLine: function (cm) { return cm.replaceSelection("\n", "start"); }, + toggleOverwrite: function (cm) { return cm.toggleOverwrite(); } + }; + + + function lineStart(cm, lineN) { + var line = getLine(cm.doc, lineN); + var visual = visualLine(line); + if (visual != line) { lineN = lineNo(visual); } + return endOfLine(true, cm, visual, lineN, 1) + } + function lineEnd(cm, lineN) { + var line = getLine(cm.doc, lineN); + var visual = visualLineEnd(line); + if (visual != line) { lineN = lineNo(visual); } + return endOfLine(true, cm, line, lineN, -1) + } + function lineStartSmart(cm, pos) { + var start = lineStart(cm, pos.line); + var line = getLine(cm.doc, start.line); + var order = getOrder(line, cm.doc.direction); + if (!order || order[0].level == 0) { + var firstNonWS = Math.max(start.ch, line.text.search(/\S/)); + var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch; + return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky) + } + return start + } + + // Run a handler that was bound to a key. + function doHandleBinding(cm, bound, dropShift) { + if (typeof bound == "string") { + bound = commands[bound]; + if (!bound) { return false } + } + // Ensure previous input has been read, so that the handler sees a + // consistent view of the document + cm.display.input.ensurePolled(); + var prevShift = cm.display.shift, done = false; + try { + if (cm.isReadOnly()) { cm.state.suppressEdits = true; } + if (dropShift) { cm.display.shift = false; } + done = bound(cm) != Pass; + } finally { + cm.display.shift = prevShift; + cm.state.suppressEdits = false; + } + return done + } + + function lookupKeyForEditor(cm, name, handle) { + for (var i = 0; i < cm.state.keyMaps.length; i++) { + var result = lookupKey(name, cm.state.keyMaps[i], handle, cm); + if (result) { return result } + } + return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm)) + || lookupKey(name, cm.options.keyMap, handle, cm) + } + + // Note that, despite the name, this function is also used to check + // for bound mouse clicks. + + var stopSeq = new Delayed; + + function dispatchKey(cm, name, e, handle) { + var seq = cm.state.keySeq; + if (seq) { + if (isModifierKey(name)) { return "handled" } + if (/\'$/.test(name)) + { cm.state.keySeq = null; } + else + { stopSeq.set(50, function () { + if (cm.state.keySeq == seq) { + cm.state.keySeq = null; + cm.display.input.reset(); + } + }); } + if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true } + } + return dispatchKeyInner(cm, name, e, handle) + } + + function dispatchKeyInner(cm, name, e, handle) { + var result = lookupKeyForEditor(cm, name, handle); + + if (result == "multi") + { cm.state.keySeq = name; } + if (result == "handled") + { signalLater(cm, "keyHandled", cm, name, e); } + + if (result == "handled" || result == "multi") { + e_preventDefault(e); + restartBlink(cm); + } + + return !!result + } + + // Handle a key from the keydown event. + function handleKeyBinding(cm, e) { + var name = keyName(e, true); + if (!name) { return false } + + if (e.shiftKey && !cm.state.keySeq) { + // First try to resolve full name (including 'Shift-'). Failing + // that, see if there is a cursor-motion command (starting with + // 'go') bound to the keyname without 'Shift-'. + return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); }) + || dispatchKey(cm, name, e, function (b) { + if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion) + { return doHandleBinding(cm, b) } + }) + } else { + return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); }) + } + } + + // Handle a key from the keypress event + function handleCharBinding(cm, e, ch) { + return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); }) + } + + var lastStoppedKey = null; + function onKeyDown(e) { + var cm = this; + if (e.target && e.target != cm.display.input.getField()) { return } + cm.curOp.focus = activeElt(doc(cm)); + if (signalDOMEvent(cm, e)) { return } + // IE does strange things with escape. + if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; } + var code = e.keyCode; + cm.display.shift = code == 16 || e.shiftKey; + var handled = handleKeyBinding(cm, e); + if (presto) { + lastStoppedKey = handled ? code : null; + // Opera has no cut event... we try to at least catch the key combo + if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey)) + { cm.replaceSelection("", null, "cut"); } + } + if (gecko && !mac && !handled && code == 46 && e.shiftKey && !e.ctrlKey && document.execCommand) + { document.execCommand("cut"); } + + // Turn mouse into crosshair when Alt is held on Mac. + if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className)) + { showCrossHair(cm); } + } + + function showCrossHair(cm) { + var lineDiv = cm.display.lineDiv; + addClass(lineDiv, "CodeMirror-crosshair"); + + function up(e) { + if (e.keyCode == 18 || !e.altKey) { + rmClass(lineDiv, "CodeMirror-crosshair"); + off(document, "keyup", up); + off(document, "mouseover", up); + } + } + on(document, "keyup", up); + on(document, "mouseover", up); + } + + function onKeyUp(e) { + if (e.keyCode == 16) { this.doc.sel.shift = false; } + signalDOMEvent(this, e); + } + + function onKeyPress(e) { + var cm = this; + if (e.target && e.target != cm.display.input.getField()) { return } + if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return } + var keyCode = e.keyCode, charCode = e.charCode; + if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return} + if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return } + var ch = String.fromCharCode(charCode == null ? keyCode : charCode); + // Some browsers fire keypress events for backspace + if (ch == "\x08") { return } + if (handleCharBinding(cm, e, ch)) { return } + cm.display.input.onKeyPress(e); + } + + var DOUBLECLICK_DELAY = 400; + + var PastClick = function(time, pos, button) { + this.time = time; + this.pos = pos; + this.button = button; + }; + + PastClick.prototype.compare = function (time, pos, button) { + return this.time + DOUBLECLICK_DELAY > time && + cmp(pos, this.pos) == 0 && button == this.button + }; + + var lastClick, lastDoubleClick; + function clickRepeat(pos, button) { + var now = +new Date; + if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) { + lastClick = lastDoubleClick = null; + return "triple" + } else if (lastClick && lastClick.compare(now, pos, button)) { + lastDoubleClick = new PastClick(now, pos, button); + lastClick = null; + return "double" + } else { + lastClick = new PastClick(now, pos, button); + lastDoubleClick = null; + return "single" + } + } + + // A mouse down can be a single click, double click, triple click, + // start of selection drag, start of text drag, new cursor + // (ctrl-click), rectangle drag (alt-drag), or xwin + // middle-click-paste. Or it might be a click on something we should + // not interfere with, such as a scrollbar or widget. + function onMouseDown(e) { + var cm = this, display = cm.display; + if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return } + display.input.ensurePolled(); + display.shift = e.shiftKey; + + if (eventInWidget(display, e)) { + if (!webkit) { + // Briefly turn off draggability, to allow widgets to do + // normal dragging things. + display.scroller.draggable = false; + setTimeout(function () { return display.scroller.draggable = true; }, 100); + } + return + } + if (clickInGutter(cm, e)) { return } + var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single"; + win(cm).focus(); + + // #3261: make sure, that we're not starting a second selection + if (button == 1 && cm.state.selectingText) + { cm.state.selectingText(e); } + + if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return } + + if (button == 1) { + if (pos) { leftButtonDown(cm, pos, repeat, e); } + else if (e_target(e) == display.scroller) { e_preventDefault(e); } + } else if (button == 2) { + if (pos) { extendSelection(cm.doc, pos); } + setTimeout(function () { return display.input.focus(); }, 20); + } else if (button == 3) { + if (captureRightClick) { cm.display.input.onContextMenu(e); } + else { delayBlurEvent(cm); } + } + } + + function handleMappedButton(cm, button, pos, repeat, event) { + var name = "Click"; + if (repeat == "double") { name = "Double" + name; } + else if (repeat == "triple") { name = "Triple" + name; } + name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name; + + return dispatchKey(cm, addModifierNames(name, event), event, function (bound) { + if (typeof bound == "string") { bound = commands[bound]; } + if (!bound) { return false } + var done = false; + try { + if (cm.isReadOnly()) { cm.state.suppressEdits = true; } + done = bound(cm, pos) != Pass; + } finally { + cm.state.suppressEdits = false; + } + return done + }) + } + + function configureMouse(cm, repeat, event) { + var option = cm.getOption("configureMouse"); + var value = option ? option(cm, repeat, event) : {}; + if (value.unit == null) { + var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey; + value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line"; + } + if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; } + if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; } + if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); } + return value + } + + function leftButtonDown(cm, pos, repeat, event) { + if (ie) { setTimeout(bind(ensureFocus, cm), 0); } + else { cm.curOp.focus = activeElt(doc(cm)); } + + var behavior = configureMouse(cm, repeat, event); + + var sel = cm.doc.sel, contained; + if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() && + repeat == "single" && (contained = sel.contains(pos)) > -1 && + (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) && + (cmp(contained.to(), pos) > 0 || pos.xRel < 0)) + { leftButtonStartDrag(cm, event, pos, behavior); } + else + { leftButtonSelect(cm, event, pos, behavior); } + } + + // Start a text drag. When it ends, see if any dragging actually + // happen, and treat as a click if it didn't. + function leftButtonStartDrag(cm, event, pos, behavior) { + var display = cm.display, moved = false; + var dragEnd = operation(cm, function (e) { + if (webkit) { display.scroller.draggable = false; } + cm.state.draggingText = false; + if (cm.state.delayingBlurEvent) { + if (cm.hasFocus()) { cm.state.delayingBlurEvent = false; } + else { delayBlurEvent(cm); } + } + off(display.wrapper.ownerDocument, "mouseup", dragEnd); + off(display.wrapper.ownerDocument, "mousemove", mouseMove); + off(display.scroller, "dragstart", dragStart); + off(display.scroller, "drop", dragEnd); + if (!moved) { + e_preventDefault(e); + if (!behavior.addNew) + { extendSelection(cm.doc, pos, null, null, behavior.extend); } + // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081) + if ((webkit && !safari) || ie && ie_version == 9) + { setTimeout(function () {display.wrapper.ownerDocument.body.focus({preventScroll: true}); display.input.focus();}, 20); } + else + { display.input.focus(); } + } + }); + var mouseMove = function(e2) { + moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10; + }; + var dragStart = function () { return moved = true; }; + // Let the drag handler handle this. + if (webkit) { display.scroller.draggable = true; } + cm.state.draggingText = dragEnd; + dragEnd.copy = !behavior.moveOnDrag; + on(display.wrapper.ownerDocument, "mouseup", dragEnd); + on(display.wrapper.ownerDocument, "mousemove", mouseMove); + on(display.scroller, "dragstart", dragStart); + on(display.scroller, "drop", dragEnd); + + cm.state.delayingBlurEvent = true; + setTimeout(function () { return display.input.focus(); }, 20); + // IE's approach to draggable + if (display.scroller.dragDrop) { display.scroller.dragDrop(); } + } + + function rangeForUnit(cm, pos, unit) { + if (unit == "char") { return new Range(pos, pos) } + if (unit == "word") { return cm.findWordAt(pos) } + if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) } + var result = unit(cm, pos); + return new Range(result.from, result.to) + } + + // Normal selection, as opposed to text dragging. + function leftButtonSelect(cm, event, start, behavior) { + if (ie) { delayBlurEvent(cm); } + var display = cm.display, doc$1 = cm.doc; + e_preventDefault(event); + + var ourRange, ourIndex, startSel = doc$1.sel, ranges = startSel.ranges; + if (behavior.addNew && !behavior.extend) { + ourIndex = doc$1.sel.contains(start); + if (ourIndex > -1) + { ourRange = ranges[ourIndex]; } + else + { ourRange = new Range(start, start); } + } else { + ourRange = doc$1.sel.primary(); + ourIndex = doc$1.sel.primIndex; + } + + if (behavior.unit == "rectangle") { + if (!behavior.addNew) { ourRange = new Range(start, start); } + start = posFromMouse(cm, event, true, true); + ourIndex = -1; + } else { + var range = rangeForUnit(cm, start, behavior.unit); + if (behavior.extend) + { ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend); } + else + { ourRange = range; } + } + + if (!behavior.addNew) { + ourIndex = 0; + setSelection(doc$1, new Selection([ourRange], 0), sel_mouse); + startSel = doc$1.sel; + } else if (ourIndex == -1) { + ourIndex = ranges.length; + setSelection(doc$1, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex), + {scroll: false, origin: "*mouse"}); + } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) { + setSelection(doc$1, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0), + {scroll: false, origin: "*mouse"}); + startSel = doc$1.sel; + } else { + replaceOneSelection(doc$1, ourIndex, ourRange, sel_mouse); + } + + var lastPos = start; + function extendTo(pos) { + if (cmp(lastPos, pos) == 0) { return } + lastPos = pos; + + if (behavior.unit == "rectangle") { + var ranges = [], tabSize = cm.options.tabSize; + var startCol = countColumn(getLine(doc$1, start.line).text, start.ch, tabSize); + var posCol = countColumn(getLine(doc$1, pos.line).text, pos.ch, tabSize); + var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol); + for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line)); + line <= end; line++) { + var text = getLine(doc$1, line).text, leftPos = findColumn(text, left, tabSize); + if (left == right) + { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); } + else if (text.length > leftPos) + { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); } + } + if (!ranges.length) { ranges.push(new Range(start, start)); } + setSelection(doc$1, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex), + {origin: "*mouse", scroll: false}); + cm.scrollIntoView(pos); + } else { + var oldRange = ourRange; + var range = rangeForUnit(cm, pos, behavior.unit); + var anchor = oldRange.anchor, head; + if (cmp(range.anchor, anchor) > 0) { + head = range.head; + anchor = minPos(oldRange.from(), range.anchor); + } else { + head = range.anchor; + anchor = maxPos(oldRange.to(), range.head); + } + var ranges$1 = startSel.ranges.slice(0); + ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc$1, anchor), head)); + setSelection(doc$1, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse); + } + } + + var editorSize = display.wrapper.getBoundingClientRect(); + // Used to ensure timeout re-tries don't fire when another extend + // happened in the meantime (clearTimeout isn't reliable -- at + // least on Chrome, the timeouts still happen even when cleared, + // if the clear happens after their scheduled firing time). + var counter = 0; + + function extend(e) { + var curCount = ++counter; + var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle"); + if (!cur) { return } + if (cmp(cur, lastPos) != 0) { + cm.curOp.focus = activeElt(doc(cm)); + extendTo(cur); + var visible = visibleLines(display, doc$1); + if (cur.line >= visible.to || cur.line < visible.from) + { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); } + } else { + var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0; + if (outside) { setTimeout(operation(cm, function () { + if (counter != curCount) { return } + display.scroller.scrollTop += outside; + extend(e); + }), 50); } + } + } + + function done(e) { + cm.state.selectingText = false; + counter = Infinity; + // If e is null or undefined we interpret this as someone trying + // to explicitly cancel the selection rather than the user + // letting go of the mouse button. + if (e) { + e_preventDefault(e); + display.input.focus(); + } + off(display.wrapper.ownerDocument, "mousemove", move); + off(display.wrapper.ownerDocument, "mouseup", up); + doc$1.history.lastSelOrigin = null; + } + + var move = operation(cm, function (e) { + if (e.buttons === 0 || !e_button(e)) { done(e); } + else { extend(e); } + }); + var up = operation(cm, done); + cm.state.selectingText = up; + on(display.wrapper.ownerDocument, "mousemove", move); + on(display.wrapper.ownerDocument, "mouseup", up); + } + + // Used when mouse-selecting to adjust the anchor to the proper side + // of a bidi jump depending on the visual position of the head. + function bidiSimplify(cm, range) { + var anchor = range.anchor; + var head = range.head; + var anchorLine = getLine(cm.doc, anchor.line); + if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range } + var order = getOrder(anchorLine); + if (!order) { return range } + var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index]; + if (part.from != anchor.ch && part.to != anchor.ch) { return range } + var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1); + if (boundary == 0 || boundary == order.length) { return range } + + // Compute the relative visual position of the head compared to the + // anchor (<0 is to the left, >0 to the right) + var leftSide; + if (head.line != anchor.line) { + leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0; + } else { + var headIndex = getBidiPartAt(order, head.ch, head.sticky); + var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1); + if (headIndex == boundary - 1 || headIndex == boundary) + { leftSide = dir < 0; } + else + { leftSide = dir > 0; } + } + + var usePart = order[boundary + (leftSide ? -1 : 0)]; + var from = leftSide == (usePart.level == 1); + var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before"; + return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head) + } + + + // Determines whether an event happened in the gutter, and fires the + // handlers for the corresponding event. + function gutterEvent(cm, e, type, prevent) { + var mX, mY; + if (e.touches) { + mX = e.touches[0].clientX; + mY = e.touches[0].clientY; + } else { + try { mX = e.clientX; mY = e.clientY; } + catch(e$1) { return false } + } + if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false } + if (prevent) { e_preventDefault(e); } + + var display = cm.display; + var lineBox = display.lineDiv.getBoundingClientRect(); + + if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) } + mY -= lineBox.top - display.viewOffset; + + for (var i = 0; i < cm.display.gutterSpecs.length; ++i) { + var g = display.gutters.childNodes[i]; + if (g && g.getBoundingClientRect().right >= mX) { + var line = lineAtHeight(cm.doc, mY); + var gutter = cm.display.gutterSpecs[i]; + signal(cm, type, cm, line, gutter.className, e); + return e_defaultPrevented(e) + } + } + } + + function clickInGutter(cm, e) { + return gutterEvent(cm, e, "gutterClick", true) + } + + // CONTEXT MENU HANDLING + + // To make the context menu work, we need to briefly unhide the + // textarea (making it as unobtrusive as possible) to let the + // right-click take effect on it. + function onContextMenu(cm, e) { + if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return } + if (signalDOMEvent(cm, e, "contextmenu")) { return } + if (!captureRightClick) { cm.display.input.onContextMenu(e); } + } + + function contextMenuInGutter(cm, e) { + if (!hasHandler(cm, "gutterContextMenu")) { return false } + return gutterEvent(cm, e, "gutterContextMenu", false) + } + + function themeChanged(cm) { + cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") + + cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-"); + clearCaches(cm); + } + + var Init = {toString: function(){return "CodeMirror.Init"}}; + + var defaults = {}; + var optionHandlers = {}; + + function defineOptions(CodeMirror) { + var optionHandlers = CodeMirror.optionHandlers; + + function option(name, deflt, handle, notOnInit) { + CodeMirror.defaults[name] = deflt; + if (handle) { optionHandlers[name] = + notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; } + } + + CodeMirror.defineOption = option; + + // Passed to option handlers when there is no old value. + CodeMirror.Init = Init; + + // These two are, on init, called from the constructor because they + // have to be initialized before the editor can start at all. + option("value", "", function (cm, val) { return cm.setValue(val); }, true); + option("mode", null, function (cm, val) { + cm.doc.modeOption = val; + loadMode(cm); + }, true); + + option("indentUnit", 2, loadMode, true); + option("indentWithTabs", false); + option("smartIndent", true); + option("tabSize", 4, function (cm) { + resetModeState(cm); + clearCaches(cm); + regChange(cm); + }, true); + + option("lineSeparator", null, function (cm, val) { + cm.doc.lineSep = val; + if (!val) { return } + var newBreaks = [], lineNo = cm.doc.first; + cm.doc.iter(function (line) { + for (var pos = 0;;) { + var found = line.text.indexOf(val, pos); + if (found == -1) { break } + pos = found + val.length; + newBreaks.push(Pos(lineNo, found)); + } + lineNo++; + }); + for (var i = newBreaks.length - 1; i >= 0; i--) + { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); } + }); + option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\u202d\u202e\u2066\u2067\u2069\ufeff\ufff9-\ufffc]/g, function (cm, val, old) { + cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g"); + if (old != Init) { cm.refresh(); } + }); + option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true); + option("electricChars", true); + option("inputStyle", mobile ? "contenteditable" : "textarea", function () { + throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME + }, true); + option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true); + option("autocorrect", false, function (cm, val) { return cm.getInputField().autocorrect = val; }, true); + option("autocapitalize", false, function (cm, val) { return cm.getInputField().autocapitalize = val; }, true); + option("rtlMoveVisually", !windows); + option("wholeLineUpdateBefore", true); + + option("theme", "default", function (cm) { + themeChanged(cm); + updateGutters(cm); + }, true); + option("keyMap", "default", function (cm, val, old) { + var next = getKeyMap(val); + var prev = old != Init && getKeyMap(old); + if (prev && prev.detach) { prev.detach(cm, next); } + if (next.attach) { next.attach(cm, prev || null); } + }); + option("extraKeys", null); + option("configureMouse", null); + + option("lineWrapping", false, wrappingChanged, true); + option("gutters", [], function (cm, val) { + cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers); + updateGutters(cm); + }, true); + option("fixedGutter", true, function (cm, val) { + cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"; + cm.refresh(); + }, true); + option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true); + option("scrollbarStyle", "native", function (cm) { + initScrollbars(cm); + updateScrollbars(cm); + cm.display.scrollbars.setScrollTop(cm.doc.scrollTop); + cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft); + }, true); + option("lineNumbers", false, function (cm, val) { + cm.display.gutterSpecs = getGutters(cm.options.gutters, val); + updateGutters(cm); + }, true); + option("firstLineNumber", 1, updateGutters, true); + option("lineNumberFormatter", function (integer) { return integer; }, updateGutters, true); + option("showCursorWhenSelecting", false, updateSelection, true); + + option("resetSelectionOnContextMenu", true); + option("lineWiseCopyCut", true); + option("pasteLinesPerSelection", true); + option("selectionsMayTouch", false); + + option("readOnly", false, function (cm, val) { + if (val == "nocursor") { + onBlur(cm); + cm.display.input.blur(); + } + cm.display.input.readOnlyChanged(val); + }); + + option("screenReaderLabel", null, function (cm, val) { + val = (val === '') ? null : val; + cm.display.input.screenReaderLabelChanged(val); + }); + + option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true); + option("dragDrop", true, dragDropChanged); + option("allowDropFileTypes", null); + + option("cursorBlinkRate", 530); + option("cursorScrollMargin", 0); + option("cursorHeight", 1, updateSelection, true); + option("singleCursorHeightPerLine", true, updateSelection, true); + option("workTime", 100); + option("workDelay", 100); + option("flattenSpans", true, resetModeState, true); + option("addModeClass", false, resetModeState, true); + option("pollInterval", 100); + option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; }); + option("historyEventDelay", 1250); + option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true); + option("maxHighlightLength", 10000, resetModeState, true); + option("moveInputWithCursor", true, function (cm, val) { + if (!val) { cm.display.input.resetPosition(); } + }); + + option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; }); + option("autofocus", null); + option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true); + option("phrases", null); + } + + function dragDropChanged(cm, value, old) { + var wasOn = old && old != Init; + if (!value != !wasOn) { + var funcs = cm.display.dragFunctions; + var toggle = value ? on : off; + toggle(cm.display.scroller, "dragstart", funcs.start); + toggle(cm.display.scroller, "dragenter", funcs.enter); + toggle(cm.display.scroller, "dragover", funcs.over); + toggle(cm.display.scroller, "dragleave", funcs.leave); + toggle(cm.display.scroller, "drop", funcs.drop); + } + } + + function wrappingChanged(cm) { + if (cm.options.lineWrapping) { + addClass(cm.display.wrapper, "CodeMirror-wrap"); + cm.display.sizer.style.minWidth = ""; + cm.display.sizerWidth = null; + } else { + rmClass(cm.display.wrapper, "CodeMirror-wrap"); + findMaxLine(cm); + } + estimateLineHeights(cm); + regChange(cm); + clearCaches(cm); + setTimeout(function () { return updateScrollbars(cm); }, 100); + } + + // A CodeMirror instance represents an editor. This is the object + // that user code is usually dealing with. + + function CodeMirror(place, options) { + var this$1 = this; + + if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) } + + this.options = options = options ? copyObj(options) : {}; + // Determine effective options based on given values and defaults. + copyObj(defaults, options, false); + + var doc = options.value; + if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); } + else if (options.mode) { doc.modeOption = options.mode; } + this.doc = doc; + + var input = new CodeMirror.inputStyles[options.inputStyle](this); + var display = this.display = new Display(place, doc, input, options); + display.wrapper.CodeMirror = this; + themeChanged(this); + if (options.lineWrapping) + { this.display.wrapper.className += " CodeMirror-wrap"; } + initScrollbars(this); + + this.state = { + keyMaps: [], // stores maps added by addKeyMap + overlays: [], // highlighting overlays, as added by addOverlay + modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info + overwrite: false, + delayingBlurEvent: false, + focused: false, + suppressEdits: false, // used to disable editing during key handlers when in readOnly mode + pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll + selectingText: false, + draggingText: false, + highlight: new Delayed(), // stores highlight worker timeout + keySeq: null, // Unfinished key sequence + specialChars: null + }; + + if (options.autofocus && !mobile) { display.input.focus(); } + + // Override magic textarea content restore that IE sometimes does + // on our hidden textarea on reload + if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); } + + registerEventHandlers(this); + ensureGlobalHandlers(); + + startOperation(this); + this.curOp.forceUpdate = true; + attachDoc(this, doc); + + if ((options.autofocus && !mobile) || this.hasFocus()) + { setTimeout(function () { + if (this$1.hasFocus() && !this$1.state.focused) { onFocus(this$1); } + }, 20); } + else + { onBlur(this); } + + for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt)) + { optionHandlers[opt](this, options[opt], Init); } } + maybeUpdateLineNumberWidth(this); + if (options.finishInit) { options.finishInit(this); } + for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this); } + endOperation(this); + // Suppress optimizelegibility in Webkit, since it breaks text + // measuring on line wrapping boundaries. + if (webkit && options.lineWrapping && + getComputedStyle(display.lineDiv).textRendering == "optimizelegibility") + { display.lineDiv.style.textRendering = "auto"; } + } + + // The default configuration options. + CodeMirror.defaults = defaults; + // Functions to run when options are changed. + CodeMirror.optionHandlers = optionHandlers; + + // Attach the necessary event handlers when initializing the editor + function registerEventHandlers(cm) { + var d = cm.display; + on(d.scroller, "mousedown", operation(cm, onMouseDown)); + // Older IE's will not fire a second mousedown for a double click + if (ie && ie_version < 11) + { on(d.scroller, "dblclick", operation(cm, function (e) { + if (signalDOMEvent(cm, e)) { return } + var pos = posFromMouse(cm, e); + if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return } + e_preventDefault(e); + var word = cm.findWordAt(pos); + extendSelection(cm.doc, word.anchor, word.head); + })); } + else + { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); } + // Some browsers fire contextmenu *after* opening the menu, at + // which point we can't mess with it anymore. Context menu is + // handled in onMouseDown for these browsers. + on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }); + on(d.input.getField(), "contextmenu", function (e) { + if (!d.scroller.contains(e.target)) { onContextMenu(cm, e); } + }); + + // Used to suppress mouse event handling when a touch happens + var touchFinished, prevTouch = {end: 0}; + function finishTouch() { + if (d.activeTouch) { + touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000); + prevTouch = d.activeTouch; + prevTouch.end = +new Date; + } + } + function isMouseLikeTouchEvent(e) { + if (e.touches.length != 1) { return false } + var touch = e.touches[0]; + return touch.radiusX <= 1 && touch.radiusY <= 1 + } + function farAway(touch, other) { + if (other.left == null) { return true } + var dx = other.left - touch.left, dy = other.top - touch.top; + return dx * dx + dy * dy > 20 * 20 + } + on(d.scroller, "touchstart", function (e) { + if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) { + d.input.ensurePolled(); + clearTimeout(touchFinished); + var now = +new Date; + d.activeTouch = {start: now, moved: false, + prev: now - prevTouch.end <= 300 ? prevTouch : null}; + if (e.touches.length == 1) { + d.activeTouch.left = e.touches[0].pageX; + d.activeTouch.top = e.touches[0].pageY; + } + } + }); + on(d.scroller, "touchmove", function () { + if (d.activeTouch) { d.activeTouch.moved = true; } + }); + on(d.scroller, "touchend", function (e) { + var touch = d.activeTouch; + if (touch && !eventInWidget(d, e) && touch.left != null && + !touch.moved && new Date - touch.start < 300) { + var pos = cm.coordsChar(d.activeTouch, "page"), range; + if (!touch.prev || farAway(touch, touch.prev)) // Single tap + { range = new Range(pos, pos); } + else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap + { range = cm.findWordAt(pos); } + else // Triple tap + { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); } + cm.setSelection(range.anchor, range.head); + cm.focus(); + e_preventDefault(e); + } + finishTouch(); + }); + on(d.scroller, "touchcancel", finishTouch); + + // Sync scrolling between fake scrollbars and real scrollable + // area, ensure viewport is updated when scrolling. + on(d.scroller, "scroll", function () { + if (d.scroller.clientHeight) { + updateScrollTop(cm, d.scroller.scrollTop); + setScrollLeft(cm, d.scroller.scrollLeft, true); + signal(cm, "scroll", cm); + } + }); + + // Listen to wheel events in order to try and update the viewport on time. + on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); }); + on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); }); + + // Prevent wrapper from ever scrolling + on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); + + d.dragFunctions = { + enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }}, + over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }}, + start: function (e) { return onDragStart(cm, e); }, + drop: operation(cm, onDrop), + leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }} + }; + + var inp = d.input.getField(); + on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); }); + on(inp, "keydown", operation(cm, onKeyDown)); + on(inp, "keypress", operation(cm, onKeyPress)); + on(inp, "focus", function (e) { return onFocus(cm, e); }); + on(inp, "blur", function (e) { return onBlur(cm, e); }); + } + + var initHooks = []; + CodeMirror.defineInitHook = function (f) { return initHooks.push(f); }; + + // Indent the given line. The how parameter can be "smart", + // "add"/null, "subtract", or "prev". When aggressive is false + // (typically set to true for forced single-line indents), empty + // lines are not indented, and places where the mode returns Pass + // are left alone. + function indentLine(cm, n, how, aggressive) { + var doc = cm.doc, state; + if (how == null) { how = "add"; } + if (how == "smart") { + // Fall back to "prev" when the mode doesn't have an indentation + // method. + if (!doc.mode.indent) { how = "prev"; } + else { state = getContextBefore(cm, n).state; } + } + + var tabSize = cm.options.tabSize; + var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize); + if (line.stateAfter) { line.stateAfter = null; } + var curSpaceString = line.text.match(/^\s*/)[0], indentation; + if (!aggressive && !/\S/.test(line.text)) { + indentation = 0; + how = "not"; + } else if (how == "smart") { + indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text); + if (indentation == Pass || indentation > 150) { + if (!aggressive) { return } + how = "prev"; + } + } + if (how == "prev") { + if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); } + else { indentation = 0; } + } else if (how == "add") { + indentation = curSpace + cm.options.indentUnit; + } else if (how == "subtract") { + indentation = curSpace - cm.options.indentUnit; + } else if (typeof how == "number") { + indentation = curSpace + how; + } + indentation = Math.max(0, indentation); + + var indentString = "", pos = 0; + if (cm.options.indentWithTabs) + { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} } + if (pos < indentation) { indentString += spaceStr(indentation - pos); } + + if (indentString != curSpaceString) { + replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); + line.stateAfter = null; + return true + } else { + // Ensure that, if the cursor was in the whitespace at the start + // of the line, it is moved to the end of that space. + for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) { + var range = doc.sel.ranges[i$1]; + if (range.head.line == n && range.head.ch < curSpaceString.length) { + var pos$1 = Pos(n, curSpaceString.length); + replaceOneSelection(doc, i$1, new Range(pos$1, pos$1)); + break + } + } + } + } + + // This will be set to a {lineWise: bool, text: [string]} object, so + // that, when pasting, we know what kind of selections the copied + // text was made out of. + var lastCopied = null; + + function setLastCopied(newLastCopied) { + lastCopied = newLastCopied; + } + + function applyTextInput(cm, inserted, deleted, sel, origin) { + var doc = cm.doc; + cm.display.shift = false; + if (!sel) { sel = doc.sel; } + + var recent = +new Date - 200; + var paste = origin == "paste" || cm.state.pasteIncoming > recent; + var textLines = splitLinesAuto(inserted), multiPaste = null; + // When pasting N lines into N selections, insert one line per selection + if (paste && sel.ranges.length > 1) { + if (lastCopied && lastCopied.text.join("\n") == inserted) { + if (sel.ranges.length % lastCopied.text.length == 0) { + multiPaste = []; + for (var i = 0; i < lastCopied.text.length; i++) + { multiPaste.push(doc.splitLines(lastCopied.text[i])); } + } + } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) { + multiPaste = map(textLines, function (l) { return [l]; }); + } + } + + var updateInput = cm.curOp.updateInput; + // Normal behavior is to insert the new text into every selection + for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) { + var range = sel.ranges[i$1]; + var from = range.from(), to = range.to(); + if (range.empty()) { + if (deleted && deleted > 0) // Handle deletion + { from = Pos(from.line, from.ch - deleted); } + else if (cm.state.overwrite && !paste) // Handle overwrite + { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); } + else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == textLines.join("\n")) + { from = to = Pos(from.line, 0); } + } + var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines, + origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")}; + makeChange(cm.doc, changeEvent); + signalLater(cm, "inputRead", cm, changeEvent); + } + if (inserted && !paste) + { triggerElectric(cm, inserted); } + + ensureCursorVisible(cm); + if (cm.curOp.updateInput < 2) { cm.curOp.updateInput = updateInput; } + cm.curOp.typing = true; + cm.state.pasteIncoming = cm.state.cutIncoming = -1; + } + + function handlePaste(e, cm) { + var pasted = e.clipboardData && e.clipboardData.getData("Text"); + if (pasted) { + e.preventDefault(); + if (!cm.isReadOnly() && !cm.options.disableInput && cm.hasFocus()) + { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); } + return true + } + } + + function triggerElectric(cm, inserted) { + // When an 'electric' character is inserted, immediately trigger a reindent + if (!cm.options.electricChars || !cm.options.smartIndent) { return } + var sel = cm.doc.sel; + + for (var i = sel.ranges.length - 1; i >= 0; i--) { + var range = sel.ranges[i]; + if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) { continue } + var mode = cm.getModeAt(range.head); + var indented = false; + if (mode.electricChars) { + for (var j = 0; j < mode.electricChars.length; j++) + { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) { + indented = indentLine(cm, range.head.line, "smart"); + break + } } + } else if (mode.electricInput) { + if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch))) + { indented = indentLine(cm, range.head.line, "smart"); } + } + if (indented) { signalLater(cm, "electricInput", cm, range.head.line); } + } + } + + function copyableRanges(cm) { + var text = [], ranges = []; + for (var i = 0; i < cm.doc.sel.ranges.length; i++) { + var line = cm.doc.sel.ranges[i].head.line; + var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}; + ranges.push(lineRange); + text.push(cm.getRange(lineRange.anchor, lineRange.head)); + } + return {text: text, ranges: ranges} + } + + function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) { + field.setAttribute("autocorrect", autocorrect ? "" : "off"); + field.setAttribute("autocapitalize", autocapitalize ? "" : "off"); + field.setAttribute("spellcheck", !!spellcheck); + } + + function hiddenTextarea() { + var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; min-height: 1em; outline: none"); + var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); + // The textarea is kept positioned near the cursor to prevent the + // fact that it'll be scrolled into view on input from scrolling + // our fake cursor out of view. On webkit, when wrap=off, paste is + // very slow. So make the area wide instead. + if (webkit) { te.style.width = "1000px"; } + else { te.setAttribute("wrap", "off"); } + // If border: 0; -- iOS fails to open keyboard (issue #1287) + if (ios) { te.style.border = "1px solid black"; } + disableBrowserMagic(te); + return div + } + + // The publicly visible API. Note that methodOp(f) means + // 'wrap f in an operation, performed on its `this` parameter'. + + // This is not the complete set of editor methods. Most of the + // methods defined on the Doc type are also injected into + // CodeMirror.prototype, for backwards compatibility and + // convenience. + + function addEditorMethods(CodeMirror) { + var optionHandlers = CodeMirror.optionHandlers; + + var helpers = CodeMirror.helpers = {}; + + CodeMirror.prototype = { + constructor: CodeMirror, + focus: function(){win(this).focus(); this.display.input.focus();}, + + setOption: function(option, value) { + var options = this.options, old = options[option]; + if (options[option] == value && option != "mode") { return } + options[option] = value; + if (optionHandlers.hasOwnProperty(option)) + { operation(this, optionHandlers[option])(this, value, old); } + signal(this, "optionChange", this, option); + }, + + getOption: function(option) {return this.options[option]}, + getDoc: function() {return this.doc}, + + addKeyMap: function(map, bottom) { + this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map)); + }, + removeKeyMap: function(map) { + var maps = this.state.keyMaps; + for (var i = 0; i < maps.length; ++i) + { if (maps[i] == map || maps[i].name == map) { + maps.splice(i, 1); + return true + } } + }, + + addOverlay: methodOp(function(spec, options) { + var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec); + if (mode.startState) { throw new Error("Overlays may not be stateful.") } + insertSorted(this.state.overlays, + {mode: mode, modeSpec: spec, opaque: options && options.opaque, + priority: (options && options.priority) || 0}, + function (overlay) { return overlay.priority; }); + this.state.modeGen++; + regChange(this); + }), + removeOverlay: methodOp(function(spec) { + var overlays = this.state.overlays; + for (var i = 0; i < overlays.length; ++i) { + var cur = overlays[i].modeSpec; + if (cur == spec || typeof spec == "string" && cur.name == spec) { + overlays.splice(i, 1); + this.state.modeGen++; + regChange(this); + return + } + } + }), + + indentLine: methodOp(function(n, dir, aggressive) { + if (typeof dir != "string" && typeof dir != "number") { + if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; } + else { dir = dir ? "add" : "subtract"; } + } + if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); } + }), + indentSelection: methodOp(function(how) { + var ranges = this.doc.sel.ranges, end = -1; + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i]; + if (!range.empty()) { + var from = range.from(), to = range.to(); + var start = Math.max(end, from.line); + end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; + for (var j = start; j < end; ++j) + { indentLine(this, j, how); } + var newRanges = this.doc.sel.ranges; + if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0) + { replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); } + } else if (range.head.line > end) { + indentLine(this, range.head.line, how, true); + end = range.head.line; + if (i == this.doc.sel.primIndex) { ensureCursorVisible(this); } + } + } + }), + + // Fetch the parser token for a given character. Useful for hacks + // that want to inspect the mode state (say, for completion). + getTokenAt: function(pos, precise) { + return takeToken(this, pos, precise) + }, + + getLineTokens: function(line, precise) { + return takeToken(this, Pos(line), precise, true) + }, + + getTokenTypeAt: function(pos) { + pos = clipPos(this.doc, pos); + var styles = getLineStyles(this, getLine(this.doc, pos.line)); + var before = 0, after = (styles.length - 1) / 2, ch = pos.ch; + var type; + if (ch == 0) { type = styles[2]; } + else { for (;;) { + var mid = (before + after) >> 1; + if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; } + else if (styles[mid * 2 + 1] < ch) { before = mid + 1; } + else { type = styles[mid * 2 + 2]; break } + } } + var cut = type ? type.indexOf("overlay ") : -1; + return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1) + }, + + getModeAt: function(pos) { + var mode = this.doc.mode; + if (!mode.innerMode) { return mode } + return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode + }, + + getHelper: function(pos, type) { + return this.getHelpers(pos, type)[0] + }, + + getHelpers: function(pos, type) { + var found = []; + if (!helpers.hasOwnProperty(type)) { return found } + var help = helpers[type], mode = this.getModeAt(pos); + if (typeof mode[type] == "string") { + if (help[mode[type]]) { found.push(help[mode[type]]); } + } else if (mode[type]) { + for (var i = 0; i < mode[type].length; i++) { + var val = help[mode[type][i]]; + if (val) { found.push(val); } + } + } else if (mode.helperType && help[mode.helperType]) { + found.push(help[mode.helperType]); + } else if (help[mode.name]) { + found.push(help[mode.name]); + } + for (var i$1 = 0; i$1 < help._global.length; i$1++) { + var cur = help._global[i$1]; + if (cur.pred(mode, this) && indexOf(found, cur.val) == -1) + { found.push(cur.val); } + } + return found + }, + + getStateAfter: function(line, precise) { + var doc = this.doc; + line = clipLine(doc, line == null ? doc.first + doc.size - 1: line); + return getContextBefore(this, line + 1, precise).state + }, + + cursorCoords: function(start, mode) { + var pos, range = this.doc.sel.primary(); + if (start == null) { pos = range.head; } + else if (typeof start == "object") { pos = clipPos(this.doc, start); } + else { pos = start ? range.from() : range.to(); } + return cursorCoords(this, pos, mode || "page") + }, + + charCoords: function(pos, mode) { + return charCoords(this, clipPos(this.doc, pos), mode || "page") + }, + + coordsChar: function(coords, mode) { + coords = fromCoordSystem(this, coords, mode || "page"); + return coordsChar(this, coords.left, coords.top) + }, + + lineAtHeight: function(height, mode) { + height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top; + return lineAtHeight(this.doc, height + this.display.viewOffset) + }, + heightAtLine: function(line, mode, includeWidgets) { + var end = false, lineObj; + if (typeof line == "number") { + var last = this.doc.first + this.doc.size - 1; + if (line < this.doc.first) { line = this.doc.first; } + else if (line > last) { line = last; end = true; } + lineObj = getLine(this.doc, line); + } else { + lineObj = line; + } + return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top + + (end ? this.doc.height - heightAtLine(lineObj) : 0) + }, + + defaultTextHeight: function() { return textHeight(this.display) }, + defaultCharWidth: function() { return charWidth(this.display) }, + + getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}}, + + addWidget: function(pos, node, scroll, vert, horiz) { + var display = this.display; + pos = cursorCoords(this, clipPos(this.doc, pos)); + var top = pos.bottom, left = pos.left; + node.style.position = "absolute"; + node.setAttribute("cm-ignore-events", "true"); + this.display.input.setUneditable(node); + display.sizer.appendChild(node); + if (vert == "over") { + top = pos.top; + } else if (vert == "above" || vert == "near") { + var vspace = Math.max(display.wrapper.clientHeight, this.doc.height), + hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth); + // Default to positioning above (if specified and possible); otherwise default to positioning below + if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight) + { top = pos.top - node.offsetHeight; } + else if (pos.bottom + node.offsetHeight <= vspace) + { top = pos.bottom; } + if (left + node.offsetWidth > hspace) + { left = hspace - node.offsetWidth; } + } + node.style.top = top + "px"; + node.style.left = node.style.right = ""; + if (horiz == "right") { + left = display.sizer.clientWidth - node.offsetWidth; + node.style.right = "0px"; + } else { + if (horiz == "left") { left = 0; } + else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; } + node.style.left = left + "px"; + } + if (scroll) + { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); } + }, + + triggerOnKeyDown: methodOp(onKeyDown), + triggerOnKeyPress: methodOp(onKeyPress), + triggerOnKeyUp: onKeyUp, + triggerOnMouseDown: methodOp(onMouseDown), + + execCommand: function(cmd) { + if (commands.hasOwnProperty(cmd)) + { return commands[cmd].call(null, this) } + }, + + triggerElectric: methodOp(function(text) { triggerElectric(this, text); }), + + findPosH: function(from, amount, unit, visually) { + var dir = 1; + if (amount < 0) { dir = -1; amount = -amount; } + var cur = clipPos(this.doc, from); + for (var i = 0; i < amount; ++i) { + cur = findPosH(this.doc, cur, dir, unit, visually); + if (cur.hitSide) { break } + } + return cur + }, + + moveH: methodOp(function(dir, unit) { + var this$1 = this; + + this.extendSelectionsBy(function (range) { + if (this$1.display.shift || this$1.doc.extend || range.empty()) + { return findPosH(this$1.doc, range.head, dir, unit, this$1.options.rtlMoveVisually) } + else + { return dir < 0 ? range.from() : range.to() } + }, sel_move); + }), + + deleteH: methodOp(function(dir, unit) { + var sel = this.doc.sel, doc = this.doc; + if (sel.somethingSelected()) + { doc.replaceSelection("", null, "+delete"); } + else + { deleteNearSelection(this, function (range) { + var other = findPosH(doc, range.head, dir, unit, false); + return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other} + }); } + }), + + findPosV: function(from, amount, unit, goalColumn) { + var dir = 1, x = goalColumn; + if (amount < 0) { dir = -1; amount = -amount; } + var cur = clipPos(this.doc, from); + for (var i = 0; i < amount; ++i) { + var coords = cursorCoords(this, cur, "div"); + if (x == null) { x = coords.left; } + else { coords.left = x; } + cur = findPosV(this, coords, dir, unit); + if (cur.hitSide) { break } + } + return cur + }, + + moveV: methodOp(function(dir, unit) { + var this$1 = this; + + var doc = this.doc, goals = []; + var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected(); + doc.extendSelectionsBy(function (range) { + if (collapse) + { return dir < 0 ? range.from() : range.to() } + var headPos = cursorCoords(this$1, range.head, "div"); + if (range.goalColumn != null) { headPos.left = range.goalColumn; } + goals.push(headPos.left); + var pos = findPosV(this$1, headPos, dir, unit); + if (unit == "page" && range == doc.sel.primary()) + { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); } + return pos + }, sel_move); + if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++) + { doc.sel.ranges[i].goalColumn = goals[i]; } } + }), + + // Find the word at the given position (as returned by coordsChar). + findWordAt: function(pos) { + var doc = this.doc, line = getLine(doc, pos.line).text; + var start = pos.ch, end = pos.ch; + if (line) { + var helper = this.getHelper(pos, "wordChars"); + if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; } + var startChar = line.charAt(start); + var check = isWordChar(startChar, helper) + ? function (ch) { return isWordChar(ch, helper); } + : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); } + : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); }; + while (start > 0 && check(line.charAt(start - 1))) { --start; } + while (end < line.length && check(line.charAt(end))) { ++end; } + } + return new Range(Pos(pos.line, start), Pos(pos.line, end)) + }, + + toggleOverwrite: function(value) { + if (value != null && value == this.state.overwrite) { return } + if (this.state.overwrite = !this.state.overwrite) + { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); } + else + { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); } + + signal(this, "overwriteToggle", this, this.state.overwrite); + }, + hasFocus: function() { return this.display.input.getField() == activeElt(doc(this)) }, + isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) }, + + scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }), + getScrollInfo: function() { + var scroller = this.display.scroller; + return {left: scroller.scrollLeft, top: scroller.scrollTop, + height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight, + width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth, + clientHeight: displayHeight(this), clientWidth: displayWidth(this)} + }, + + scrollIntoView: methodOp(function(range, margin) { + if (range == null) { + range = {from: this.doc.sel.primary().head, to: null}; + if (margin == null) { margin = this.options.cursorScrollMargin; } + } else if (typeof range == "number") { + range = {from: Pos(range, 0), to: null}; + } else if (range.from == null) { + range = {from: range, to: null}; + } + if (!range.to) { range.to = range.from; } + range.margin = margin || 0; + + if (range.from.line != null) { + scrollToRange(this, range); + } else { + scrollToCoordsRange(this, range.from, range.to, range.margin); + } + }), + + setSize: methodOp(function(width, height) { + var this$1 = this; + + var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; }; + if (width != null) { this.display.wrapper.style.width = interpret(width); } + if (height != null) { this.display.wrapper.style.height = interpret(height); } + if (this.options.lineWrapping) { clearLineMeasurementCache(this); } + var lineNo = this.display.viewFrom; + this.doc.iter(lineNo, this.display.viewTo, function (line) { + if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) + { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo, "widget"); break } } } + ++lineNo; + }); + this.curOp.forceUpdate = true; + signal(this, "refresh", this); + }), + + operation: function(f){return runInOp(this, f)}, + startOperation: function(){return startOperation(this)}, + endOperation: function(){return endOperation(this)}, + + refresh: methodOp(function() { + var oldHeight = this.display.cachedTextHeight; + regChange(this); + this.curOp.forceUpdate = true; + clearCaches(this); + scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop); + updateGutterSpace(this.display); + if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5 || this.options.lineWrapping) + { estimateLineHeights(this); } + signal(this, "refresh", this); + }), + + swapDoc: methodOp(function(doc) { + var old = this.doc; + old.cm = null; + // Cancel the current text selection if any (#5821) + if (this.state.selectingText) { this.state.selectingText(); } + attachDoc(this, doc); + clearCaches(this); + this.display.input.reset(); + scrollToCoords(this, doc.scrollLeft, doc.scrollTop); + this.curOp.forceScroll = true; + signalLater(this, "swapDoc", this, old); + return old + }), + + phrase: function(phraseText) { + var phrases = this.options.phrases; + return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText + }, + + getInputField: function(){return this.display.input.getField()}, + getWrapperElement: function(){return this.display.wrapper}, + getScrollerElement: function(){return this.display.scroller}, + getGutterElement: function(){return this.display.gutters} + }; + eventMixin(CodeMirror); + + CodeMirror.registerHelper = function(type, name, value) { + if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; } + helpers[type][name] = value; + }; + CodeMirror.registerGlobalHelper = function(type, name, predicate, value) { + CodeMirror.registerHelper(type, name, value); + helpers[type]._global.push({pred: predicate, val: value}); + }; + } + + // Used for horizontal relative motion. Dir is -1 or 1 (left or + // right), unit can be "codepoint", "char", "column" (like char, but + // doesn't cross line boundaries), "word" (across next word), or + // "group" (to the start of next group of word or + // non-word-non-whitespace chars). The visually param controls + // whether, in right-to-left text, direction 1 means to move towards + // the next index in the string, or towards the character to the right + // of the current position. The resulting position will have a + // hitSide=true property if it reached the end of the document. + function findPosH(doc, pos, dir, unit, visually) { + var oldPos = pos; + var origDir = dir; + var lineObj = getLine(doc, pos.line); + var lineDir = visually && doc.direction == "rtl" ? -dir : dir; + function findNextLine() { + var l = pos.line + lineDir; + if (l < doc.first || l >= doc.first + doc.size) { return false } + pos = new Pos(l, pos.ch, pos.sticky); + return lineObj = getLine(doc, l) + } + function moveOnce(boundToLine) { + var next; + if (unit == "codepoint") { + var ch = lineObj.text.charCodeAt(pos.ch + (dir > 0 ? 0 : -1)); + if (isNaN(ch)) { + next = null; + } else { + var astral = dir > 0 ? ch >= 0xD800 && ch < 0xDC00 : ch >= 0xDC00 && ch < 0xDFFF; + next = new Pos(pos.line, Math.max(0, Math.min(lineObj.text.length, pos.ch + dir * (astral ? 2 : 1))), -dir); + } + } else if (visually) { + next = moveVisually(doc.cm, lineObj, pos, dir); + } else { + next = moveLogically(lineObj, pos, dir); + } + if (next == null) { + if (!boundToLine && findNextLine()) + { pos = endOfLine(visually, doc.cm, lineObj, pos.line, lineDir); } + else + { return false } + } else { + pos = next; + } + return true + } + + if (unit == "char" || unit == "codepoint") { + moveOnce(); + } else if (unit == "column") { + moveOnce(true); + } else if (unit == "word" || unit == "group") { + var sawType = null, group = unit == "group"; + var helper = doc.cm && doc.cm.getHelper(pos, "wordChars"); + for (var first = true;; first = false) { + if (dir < 0 && !moveOnce(!first)) { break } + var cur = lineObj.text.charAt(pos.ch) || "\n"; + var type = isWordChar(cur, helper) ? "w" + : group && cur == "\n" ? "n" + : !group || /\s/.test(cur) ? null + : "p"; + if (group && !first && !type) { type = "s"; } + if (sawType && sawType != type) { + if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";} + break + } + + if (type) { sawType = type; } + if (dir > 0 && !moveOnce(!first)) { break } + } + } + var result = skipAtomic(doc, pos, oldPos, origDir, true); + if (equalCursorPos(oldPos, result)) { result.hitSide = true; } + return result + } + + // For relative vertical movement. Dir may be -1 or 1. Unit can be + // "page" or "line". The resulting position will have a hitSide=true + // property if it reached the end of the document. + function findPosV(cm, pos, dir, unit) { + var doc = cm.doc, x = pos.left, y; + if (unit == "page") { + var pageSize = Math.min(cm.display.wrapper.clientHeight, win(cm).innerHeight || doc(cm).documentElement.clientHeight); + var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3); + y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount; + } else if (unit == "line") { + y = dir > 0 ? pos.bottom + 3 : pos.top - 3; + } + var target; + for (;;) { + target = coordsChar(cm, x, y); + if (!target.outside) { break } + if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break } + y += dir * 5; + } + return target + } + + // CONTENTEDITABLE INPUT STYLE + + var ContentEditableInput = function(cm) { + this.cm = cm; + this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null; + this.polling = new Delayed(); + this.composing = null; + this.gracePeriod = false; + this.readDOMTimeout = null; + }; + + ContentEditableInput.prototype.init = function (display) { + var this$1 = this; + + var input = this, cm = input.cm; + var div = input.div = display.lineDiv; + div.contentEditable = true; + disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize); + + function belongsToInput(e) { + for (var t = e.target; t; t = t.parentNode) { + if (t == div) { return true } + if (/\bCodeMirror-(?:line)?widget\b/.test(t.className)) { break } + } + return false + } + + on(div, "paste", function (e) { + if (!belongsToInput(e) || signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } + // IE doesn't fire input events, so we schedule a read for the pasted content in this way + if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); } + }); + + on(div, "compositionstart", function (e) { + this$1.composing = {data: e.data, done: false}; + }); + on(div, "compositionupdate", function (e) { + if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; } + }); + on(div, "compositionend", function (e) { + if (this$1.composing) { + if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); } + this$1.composing.done = true; + } + }); + + on(div, "touchstart", function () { return input.forceCompositionEnd(); }); + + on(div, "input", function () { + if (!this$1.composing) { this$1.readFromDOMSoon(); } + }); + + function onCopyCut(e) { + if (!belongsToInput(e) || signalDOMEvent(cm, e)) { return } + if (cm.somethingSelected()) { + setLastCopied({lineWise: false, text: cm.getSelections()}); + if (e.type == "cut") { cm.replaceSelection("", null, "cut"); } + } else if (!cm.options.lineWiseCopyCut) { + return + } else { + var ranges = copyableRanges(cm); + setLastCopied({lineWise: true, text: ranges.text}); + if (e.type == "cut") { + cm.operation(function () { + cm.setSelections(ranges.ranges, 0, sel_dontScroll); + cm.replaceSelection("", null, "cut"); + }); + } + } + if (e.clipboardData) { + e.clipboardData.clearData(); + var content = lastCopied.text.join("\n"); + // iOS exposes the clipboard API, but seems to discard content inserted into it + e.clipboardData.setData("Text", content); + if (e.clipboardData.getData("Text") == content) { + e.preventDefault(); + return + } + } + // Old-fashioned briefly-focus-a-textarea hack + var kludge = hiddenTextarea(), te = kludge.firstChild; + cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild); + te.value = lastCopied.text.join("\n"); + var hadFocus = activeElt(div.ownerDocument); + selectInput(te); + setTimeout(function () { + cm.display.lineSpace.removeChild(kludge); + hadFocus.focus(); + if (hadFocus == div) { input.showPrimarySelection(); } + }, 50); + } + on(div, "copy", onCopyCut); + on(div, "cut", onCopyCut); + }; + + ContentEditableInput.prototype.screenReaderLabelChanged = function (label) { + // Label for screenreaders, accessibility + if(label) { + this.div.setAttribute('aria-label', label); + } else { + this.div.removeAttribute('aria-label'); + } + }; + + ContentEditableInput.prototype.prepareSelection = function () { + var result = prepareSelection(this.cm, false); + result.focus = activeElt(this.div.ownerDocument) == this.div; + return result + }; + + ContentEditableInput.prototype.showSelection = function (info, takeFocus) { + if (!info || !this.cm.display.view.length) { return } + if (info.focus || takeFocus) { this.showPrimarySelection(); } + this.showMultipleSelections(info); + }; + + ContentEditableInput.prototype.getSelection = function () { + return this.cm.display.wrapper.ownerDocument.getSelection() + }; + + ContentEditableInput.prototype.showPrimarySelection = function () { + var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary(); + var from = prim.from(), to = prim.to(); + + if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) { + sel.removeAllRanges(); + return + } + + var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); + var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset); + if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad && + cmp(minPos(curAnchor, curFocus), from) == 0 && + cmp(maxPos(curAnchor, curFocus), to) == 0) + { return } + + var view = cm.display.view; + var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) || + {node: view[0].measure.map[2], offset: 0}; + var end = to.line < cm.display.viewTo && posToDOM(cm, to); + if (!end) { + var measure = view[view.length - 1].measure; + var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map; + end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]}; + } + + if (!start || !end) { + sel.removeAllRanges(); + return + } + + var old = sel.rangeCount && sel.getRangeAt(0), rng; + try { rng = range(start.node, start.offset, end.offset, end.node); } + catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible + if (rng) { + if (!gecko && cm.state.focused) { + sel.collapse(start.node, start.offset); + if (!rng.collapsed) { + sel.removeAllRanges(); + sel.addRange(rng); + } + } else { + sel.removeAllRanges(); + sel.addRange(rng); + } + if (old && sel.anchorNode == null) { sel.addRange(old); } + else if (gecko) { this.startGracePeriod(); } + } + this.rememberSelection(); + }; + + ContentEditableInput.prototype.startGracePeriod = function () { + var this$1 = this; + + clearTimeout(this.gracePeriod); + this.gracePeriod = setTimeout(function () { + this$1.gracePeriod = false; + if (this$1.selectionChanged()) + { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); } + }, 20); + }; + + ContentEditableInput.prototype.showMultipleSelections = function (info) { + removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors); + removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection); + }; + + ContentEditableInput.prototype.rememberSelection = function () { + var sel = this.getSelection(); + this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset; + this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset; + }; + + ContentEditableInput.prototype.selectionInEditor = function () { + var sel = this.getSelection(); + if (!sel.rangeCount) { return false } + var node = sel.getRangeAt(0).commonAncestorContainer; + return contains(this.div, node) + }; + + ContentEditableInput.prototype.focus = function () { + if (this.cm.options.readOnly != "nocursor") { + if (!this.selectionInEditor() || activeElt(this.div.ownerDocument) != this.div) + { this.showSelection(this.prepareSelection(), true); } + this.div.focus(); + } + }; + ContentEditableInput.prototype.blur = function () { this.div.blur(); }; + ContentEditableInput.prototype.getField = function () { return this.div }; + + ContentEditableInput.prototype.supportsTouch = function () { return true }; + + ContentEditableInput.prototype.receivedFocus = function () { + var this$1 = this; + + var input = this; + if (this.selectionInEditor()) + { setTimeout(function () { return this$1.pollSelection(); }, 20); } + else + { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); } + + function poll() { + if (input.cm.state.focused) { + input.pollSelection(); + input.polling.set(input.cm.options.pollInterval, poll); + } + } + this.polling.set(this.cm.options.pollInterval, poll); + }; + + ContentEditableInput.prototype.selectionChanged = function () { + var sel = this.getSelection(); + return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset || + sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset + }; + + ContentEditableInput.prototype.pollSelection = function () { + if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return } + var sel = this.getSelection(), cm = this.cm; + // On Android Chrome (version 56, at least), backspacing into an + // uneditable block element will put the cursor in that element, + // and then, because it's not editable, hide the virtual keyboard. + // Because Android doesn't allow us to actually detect backspace + // presses in a sane way, this code checks for when that happens + // and simulates a backspace press in this case. + if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) { + this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs}); + this.blur(); + this.focus(); + return + } + if (this.composing) { return } + this.rememberSelection(); + var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); + var head = domToPos(cm, sel.focusNode, sel.focusOffset); + if (anchor && head) { runInOp(cm, function () { + setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll); + if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; } + }); } + }; + + ContentEditableInput.prototype.pollContent = function () { + if (this.readDOMTimeout != null) { + clearTimeout(this.readDOMTimeout); + this.readDOMTimeout = null; + } + + var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary(); + var from = sel.from(), to = sel.to(); + if (from.ch == 0 && from.line > cm.firstLine()) + { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); } + if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine()) + { to = Pos(to.line + 1, 0); } + if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false } + + var fromIndex, fromLine, fromNode; + if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) { + fromLine = lineNo(display.view[0].line); + fromNode = display.view[0].node; + } else { + fromLine = lineNo(display.view[fromIndex].line); + fromNode = display.view[fromIndex - 1].node.nextSibling; + } + var toIndex = findViewIndex(cm, to.line); + var toLine, toNode; + if (toIndex == display.view.length - 1) { + toLine = display.viewTo - 1; + toNode = display.lineDiv.lastChild; + } else { + toLine = lineNo(display.view[toIndex + 1].line) - 1; + toNode = display.view[toIndex + 1].node.previousSibling; + } + + if (!fromNode) { return false } + var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine)); + var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length)); + while (newText.length > 1 && oldText.length > 1) { + if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; } + else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; } + else { break } + } + + var cutFront = 0, cutEnd = 0; + var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length); + while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront)) + { ++cutFront; } + var newBot = lst(newText), oldBot = lst(oldText); + var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0), + oldBot.length - (oldText.length == 1 ? cutFront : 0)); + while (cutEnd < maxCutEnd && + newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) + { ++cutEnd; } + // Try to move start of change to start of selection if ambiguous + if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) { + while (cutFront && cutFront > from.ch && + newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) { + cutFront--; + cutEnd++; + } + } + + newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, ""); + newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, ""); + + var chFrom = Pos(fromLine, cutFront); + var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0); + if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) { + replaceRange(cm.doc, newText, chFrom, chTo, "+input"); + return true + } + }; + + ContentEditableInput.prototype.ensurePolled = function () { + this.forceCompositionEnd(); + }; + ContentEditableInput.prototype.reset = function () { + this.forceCompositionEnd(); + }; + ContentEditableInput.prototype.forceCompositionEnd = function () { + if (!this.composing) { return } + clearTimeout(this.readDOMTimeout); + this.composing = null; + this.updateFromDOM(); + this.div.blur(); + this.div.focus(); + }; + ContentEditableInput.prototype.readFromDOMSoon = function () { + var this$1 = this; + + if (this.readDOMTimeout != null) { return } + this.readDOMTimeout = setTimeout(function () { + this$1.readDOMTimeout = null; + if (this$1.composing) { + if (this$1.composing.done) { this$1.composing = null; } + else { return } + } + this$1.updateFromDOM(); + }, 80); + }; + + ContentEditableInput.prototype.updateFromDOM = function () { + var this$1 = this; + + if (this.cm.isReadOnly() || !this.pollContent()) + { runInOp(this.cm, function () { return regChange(this$1.cm); }); } + }; + + ContentEditableInput.prototype.setUneditable = function (node) { + node.contentEditable = "false"; + }; + + ContentEditableInput.prototype.onKeyPress = function (e) { + if (e.charCode == 0 || this.composing) { return } + e.preventDefault(); + if (!this.cm.isReadOnly()) + { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); } + }; + + ContentEditableInput.prototype.readOnlyChanged = function (val) { + this.div.contentEditable = String(val != "nocursor"); + }; + + ContentEditableInput.prototype.onContextMenu = function () {}; + ContentEditableInput.prototype.resetPosition = function () {}; + + ContentEditableInput.prototype.needsContentAttribute = true; + + function posToDOM(cm, pos) { + var view = findViewForLine(cm, pos.line); + if (!view || view.hidden) { return null } + var line = getLine(cm.doc, pos.line); + var info = mapFromLineView(view, line, pos.line); + + var order = getOrder(line, cm.doc.direction), side = "left"; + if (order) { + var partPos = getBidiPartAt(order, pos.ch); + side = partPos % 2 ? "right" : "left"; + } + var result = nodeAndOffsetInLineMap(info.map, pos.ch, side); + result.offset = result.collapse == "right" ? result.end : result.start; + return result + } + + function isInGutter(node) { + for (var scan = node; scan; scan = scan.parentNode) + { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } } + return false + } + + function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos } + + function domTextBetween(cm, from, to, fromLine, toLine) { + var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false; + function recognizeMarker(id) { return function (marker) { return marker.id == id; } } + function close() { + if (closing) { + text += lineSep; + if (extraLinebreak) { text += lineSep; } + closing = extraLinebreak = false; + } + } + function addText(str) { + if (str) { + close(); + text += str; + } + } + function walk(node) { + if (node.nodeType == 1) { + var cmText = node.getAttribute("cm-text"); + if (cmText) { + addText(cmText); + return + } + var markerID = node.getAttribute("cm-marker"), range; + if (markerID) { + var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID)); + if (found.length && (range = found[0].find(0))) + { addText(getBetween(cm.doc, range.from, range.to).join(lineSep)); } + return + } + if (node.getAttribute("contenteditable") == "false") { return } + var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName); + if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return } + + if (isBlock) { close(); } + for (var i = 0; i < node.childNodes.length; i++) + { walk(node.childNodes[i]); } + + if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; } + if (isBlock) { closing = true; } + } else if (node.nodeType == 3) { + addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " ")); + } + } + for (;;) { + walk(from); + if (from == to) { break } + from = from.nextSibling; + extraLinebreak = false; + } + return text + } + + function domToPos(cm, node, offset) { + var lineNode; + if (node == cm.display.lineDiv) { + lineNode = cm.display.lineDiv.childNodes[offset]; + if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) } + node = null; offset = 0; + } else { + for (lineNode = node;; lineNode = lineNode.parentNode) { + if (!lineNode || lineNode == cm.display.lineDiv) { return null } + if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break } + } + } + for (var i = 0; i < cm.display.view.length; i++) { + var lineView = cm.display.view[i]; + if (lineView.node == lineNode) + { return locateNodeInLineView(lineView, node, offset) } + } + } + + function locateNodeInLineView(lineView, node, offset) { + var wrapper = lineView.text.firstChild, bad = false; + if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) } + if (node == wrapper) { + bad = true; + node = wrapper.childNodes[offset]; + offset = 0; + if (!node) { + var line = lineView.rest ? lst(lineView.rest) : lineView.line; + return badPos(Pos(lineNo(line), line.text.length), bad) + } + } + + var textNode = node.nodeType == 3 ? node : null, topNode = node; + if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) { + textNode = node.firstChild; + if (offset) { offset = textNode.nodeValue.length; } + } + while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; } + var measure = lineView.measure, maps = measure.maps; + + function find(textNode, topNode, offset) { + for (var i = -1; i < (maps ? maps.length : 0); i++) { + var map = i < 0 ? measure.map : maps[i]; + for (var j = 0; j < map.length; j += 3) { + var curNode = map[j + 2]; + if (curNode == textNode || curNode == topNode) { + var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]); + var ch = map[j] + offset; + if (offset < 0 || curNode != textNode) { ch = map[j + (offset ? 1 : 0)]; } + return Pos(line, ch) + } + } + } + } + var found = find(textNode, topNode, offset); + if (found) { return badPos(found, bad) } + + // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems + for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) { + found = find(after, after.firstChild, 0); + if (found) + { return badPos(Pos(found.line, found.ch - dist), bad) } + else + { dist += after.textContent.length; } + } + for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) { + found = find(before, before.firstChild, -1); + if (found) + { return badPos(Pos(found.line, found.ch + dist$1), bad) } + else + { dist$1 += before.textContent.length; } + } + } + + // TEXTAREA INPUT STYLE + + var TextareaInput = function(cm) { + this.cm = cm; + // See input.poll and input.reset + this.prevInput = ""; + + // Flag that indicates whether we expect input to appear real soon + // now (after some event like 'keypress' or 'input') and are + // polling intensively. + this.pollingFast = false; + // Self-resetting timeout for the poller + this.polling = new Delayed(); + // Used to work around IE issue with selection being forgotten when focus moves away from textarea + this.hasSelection = false; + this.composing = null; + this.resetting = false; + }; + + TextareaInput.prototype.init = function (display) { + var this$1 = this; + + var input = this, cm = this.cm; + this.createField(display); + var te = this.textarea; + + display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild); + + // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore) + if (ios) { te.style.width = "0px"; } + + on(te, "input", function () { + if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; } + input.poll(); + }); + + on(te, "paste", function (e) { + if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } + + cm.state.pasteIncoming = +new Date; + input.fastPoll(); + }); + + function prepareCopyCut(e) { + if (signalDOMEvent(cm, e)) { return } + if (cm.somethingSelected()) { + setLastCopied({lineWise: false, text: cm.getSelections()}); + } else if (!cm.options.lineWiseCopyCut) { + return + } else { + var ranges = copyableRanges(cm); + setLastCopied({lineWise: true, text: ranges.text}); + if (e.type == "cut") { + cm.setSelections(ranges.ranges, null, sel_dontScroll); + } else { + input.prevInput = ""; + te.value = ranges.text.join("\n"); + selectInput(te); + } + } + if (e.type == "cut") { cm.state.cutIncoming = +new Date; } + } + on(te, "cut", prepareCopyCut); + on(te, "copy", prepareCopyCut); + + on(display.scroller, "paste", function (e) { + if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return } + if (!te.dispatchEvent) { + cm.state.pasteIncoming = +new Date; + input.focus(); + return + } + + // Pass the `paste` event to the textarea so it's handled by its event listener. + var event = new Event("paste"); + event.clipboardData = e.clipboardData; + te.dispatchEvent(event); + }); + + // Prevent normal selection in the editor (we handle our own) + on(display.lineSpace, "selectstart", function (e) { + if (!eventInWidget(display, e)) { e_preventDefault(e); } + }); + + on(te, "compositionstart", function () { + var start = cm.getCursor("from"); + if (input.composing) { input.composing.range.clear(); } + input.composing = { + start: start, + range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"}) + }; + }); + on(te, "compositionend", function () { + if (input.composing) { + input.poll(); + input.composing.range.clear(); + input.composing = null; + } + }); + }; + + TextareaInput.prototype.createField = function (_display) { + // Wraps and hides input textarea + this.wrapper = hiddenTextarea(); + // The semihidden textarea that is focused when the editor is + // focused, and receives input. + this.textarea = this.wrapper.firstChild; + }; + + TextareaInput.prototype.screenReaderLabelChanged = function (label) { + // Label for screenreaders, accessibility + if(label) { + this.textarea.setAttribute('aria-label', label); + } else { + this.textarea.removeAttribute('aria-label'); + } + }; + + TextareaInput.prototype.prepareSelection = function () { + // Redraw the selection and/or cursor + var cm = this.cm, display = cm.display, doc = cm.doc; + var result = prepareSelection(cm); + + // Move the hidden textarea near the cursor to prevent scrolling artifacts + if (cm.options.moveInputWithCursor) { + var headPos = cursorCoords(cm, doc.sel.primary().head, "div"); + var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect(); + result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10, + headPos.top + lineOff.top - wrapOff.top)); + result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10, + headPos.left + lineOff.left - wrapOff.left)); + } + + return result + }; + + TextareaInput.prototype.showSelection = function (drawn) { + var cm = this.cm, display = cm.display; + removeChildrenAndAdd(display.cursorDiv, drawn.cursors); + removeChildrenAndAdd(display.selectionDiv, drawn.selection); + if (drawn.teTop != null) { + this.wrapper.style.top = drawn.teTop + "px"; + this.wrapper.style.left = drawn.teLeft + "px"; + } + }; + + // Reset the input to correspond to the selection (or to be empty, + // when not typing and nothing is selected) + TextareaInput.prototype.reset = function (typing) { + if (this.contextMenuPending || this.composing && typing) { return } + var cm = this.cm; + this.resetting = true; + if (cm.somethingSelected()) { + this.prevInput = ""; + var content = cm.getSelection(); + this.textarea.value = content; + if (cm.state.focused) { selectInput(this.textarea); } + if (ie && ie_version >= 9) { this.hasSelection = content; } + } else if (!typing) { + this.prevInput = this.textarea.value = ""; + if (ie && ie_version >= 9) { this.hasSelection = null; } + } + this.resetting = false; + }; + + TextareaInput.prototype.getField = function () { return this.textarea }; + + TextareaInput.prototype.supportsTouch = function () { return false }; + + TextareaInput.prototype.focus = function () { + if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt(this.textarea.ownerDocument) != this.textarea)) { + try { this.textarea.focus(); } + catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM + } + }; + + TextareaInput.prototype.blur = function () { this.textarea.blur(); }; + + TextareaInput.prototype.resetPosition = function () { + this.wrapper.style.top = this.wrapper.style.left = 0; + }; + + TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); }; + + // Poll for input changes, using the normal rate of polling. This + // runs as long as the editor is focused. + TextareaInput.prototype.slowPoll = function () { + var this$1 = this; + + if (this.pollingFast) { return } + this.polling.set(this.cm.options.pollInterval, function () { + this$1.poll(); + if (this$1.cm.state.focused) { this$1.slowPoll(); } + }); + }; + + // When an event has just come in that is likely to add or change + // something in the input textarea, we poll faster, to ensure that + // the change appears on the screen quickly. + TextareaInput.prototype.fastPoll = function () { + var missed = false, input = this; + input.pollingFast = true; + function p() { + var changed = input.poll(); + if (!changed && !missed) {missed = true; input.polling.set(60, p);} + else {input.pollingFast = false; input.slowPoll();} + } + input.polling.set(20, p); + }; + + // Read input from the textarea, and update the document to match. + // When something is selected, it is present in the textarea, and + // selected (unless it is huge, in which case a placeholder is + // used). When nothing is selected, the cursor sits after previously + // seen text (can be empty), which is stored in prevInput (we must + // not reset the textarea when typing, because that breaks IME). + TextareaInput.prototype.poll = function () { + var this$1 = this; + + var cm = this.cm, input = this.textarea, prevInput = this.prevInput; + // Since this is called a *lot*, try to bail out as cheaply as + // possible when it is clear that nothing happened. hasSelection + // will be the case when there is a lot of text in the textarea, + // in which case reading its value would be expensive. + if (this.contextMenuPending || this.resetting || !cm.state.focused || + (hasSelection(input) && !prevInput && !this.composing) || + cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq) + { return false } + + var text = input.value; + // If nothing changed, bail. + if (text == prevInput && !cm.somethingSelected()) { return false } + // Work around nonsensical selection resetting in IE9/10, and + // inexplicable appearance of private area unicode characters on + // some key combos in Mac (#2689). + if (ie && ie_version >= 9 && this.hasSelection === text || + mac && /[\uf700-\uf7ff]/.test(text)) { + cm.display.input.reset(); + return false + } + + if (cm.doc.sel == cm.display.selForContextMenu) { + var first = text.charCodeAt(0); + if (first == 0x200b && !prevInput) { prevInput = "\u200b"; } + if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") } + } + // Find the part of the input that is actually new + var same = 0, l = Math.min(prevInput.length, text.length); + while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; } + + runInOp(cm, function () { + applyTextInput(cm, text.slice(same), prevInput.length - same, + null, this$1.composing ? "*compose" : null); + + // Don't leave long text in the textarea, since it makes further polling slow + if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; } + else { this$1.prevInput = text; } + + if (this$1.composing) { + this$1.composing.range.clear(); + this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"), + {className: "CodeMirror-composing"}); + } + }); + return true + }; + + TextareaInput.prototype.ensurePolled = function () { + if (this.pollingFast && this.poll()) { this.pollingFast = false; } + }; + + TextareaInput.prototype.onKeyPress = function () { + if (ie && ie_version >= 9) { this.hasSelection = null; } + this.fastPoll(); + }; + + TextareaInput.prototype.onContextMenu = function (e) { + var input = this, cm = input.cm, display = cm.display, te = input.textarea; + if (input.contextMenuPending) { input.contextMenuPending(); } + var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop; + if (!pos || presto) { return } // Opera is difficult. + + // Reset the current text selection only if the click is done outside of the selection + // and 'resetSelectionOnContextMenu' option is true. + var reset = cm.options.resetSelectionOnContextMenu; + if (reset && cm.doc.sel.contains(pos) == -1) + { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); } + + var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText; + var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect(); + input.wrapper.style.cssText = "position: static"; + te.style.cssText = "position: absolute; width: 30px; height: 30px;\n top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; + var oldScrollY; + if (webkit) { oldScrollY = te.ownerDocument.defaultView.scrollY; } // Work around Chrome issue (#2712) + display.input.focus(); + if (webkit) { te.ownerDocument.defaultView.scrollTo(null, oldScrollY); } + display.input.reset(); + // Adds "Select all" to context menu in FF + if (!cm.somethingSelected()) { te.value = input.prevInput = " "; } + input.contextMenuPending = rehide; + display.selForContextMenu = cm.doc.sel; + clearTimeout(display.detectingSelectAll); + + // Select-all will be greyed out if there's nothing to select, so + // this adds a zero-width space so that we can later check whether + // it got selected. + function prepareSelectAllHack() { + if (te.selectionStart != null) { + var selected = cm.somethingSelected(); + var extval = "\u200b" + (selected ? te.value : ""); + te.value = "\u21da"; // Used to catch context-menu undo + te.value = extval; + input.prevInput = selected ? "" : "\u200b"; + te.selectionStart = 1; te.selectionEnd = extval.length; + // Re-set this, in case some other handler touched the + // selection in the meantime. + display.selForContextMenu = cm.doc.sel; + } + } + function rehide() { + if (input.contextMenuPending != rehide) { return } + input.contextMenuPending = false; + input.wrapper.style.cssText = oldWrapperCSS; + te.style.cssText = oldCSS; + if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); } + + // Try to detect the user choosing select-all + if (te.selectionStart != null) { + if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); } + var i = 0, poll = function () { + if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 && + te.selectionEnd > 0 && input.prevInput == "\u200b") { + operation(cm, selectAll)(cm); + } else if (i++ < 10) { + display.detectingSelectAll = setTimeout(poll, 500); + } else { + display.selForContextMenu = null; + display.input.reset(); + } + }; + display.detectingSelectAll = setTimeout(poll, 200); + } + } + + if (ie && ie_version >= 9) { prepareSelectAllHack(); } + if (captureRightClick) { + e_stop(e); + var mouseup = function () { + off(window, "mouseup", mouseup); + setTimeout(rehide, 20); + }; + on(window, "mouseup", mouseup); + } else { + setTimeout(rehide, 50); + } + }; + + TextareaInput.prototype.readOnlyChanged = function (val) { + if (!val) { this.reset(); } + this.textarea.disabled = val == "nocursor"; + this.textarea.readOnly = !!val; + }; + + TextareaInput.prototype.setUneditable = function () {}; + + TextareaInput.prototype.needsContentAttribute = false; + + function fromTextArea(textarea, options) { + options = options ? copyObj(options) : {}; + options.value = textarea.value; + if (!options.tabindex && textarea.tabIndex) + { options.tabindex = textarea.tabIndex; } + if (!options.placeholder && textarea.placeholder) + { options.placeholder = textarea.placeholder; } + // Set autofocus to true if this textarea is focused, or if it has + // autofocus and no other element is focused. + if (options.autofocus == null) { + var hasFocus = activeElt(textarea.ownerDocument); + options.autofocus = hasFocus == textarea || + textarea.getAttribute("autofocus") != null && hasFocus == document.body; + } + + function save() {textarea.value = cm.getValue();} + + var realSubmit; + if (textarea.form) { + on(textarea.form, "submit", save); + // Deplorable hack to make the submit method do the right thing. + if (!options.leaveSubmitMethodAlone) { + var form = textarea.form; + realSubmit = form.submit; + try { + var wrappedSubmit = form.submit = function () { + save(); + form.submit = realSubmit; + form.submit(); + form.submit = wrappedSubmit; + }; + } catch(e) {} + } + } + + options.finishInit = function (cm) { + cm.save = save; + cm.getTextArea = function () { return textarea; }; + cm.toTextArea = function () { + cm.toTextArea = isNaN; // Prevent this from being ran twice + save(); + textarea.parentNode.removeChild(cm.getWrapperElement()); + textarea.style.display = ""; + if (textarea.form) { + off(textarea.form, "submit", save); + if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == "function") + { textarea.form.submit = realSubmit; } + } + }; + }; + + textarea.style.display = "none"; + var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); }, + options); + return cm + } + + function addLegacyProps(CodeMirror) { + CodeMirror.off = off; + CodeMirror.on = on; + CodeMirror.wheelEventPixels = wheelEventPixels; + CodeMirror.Doc = Doc; + CodeMirror.splitLines = splitLinesAuto; + CodeMirror.countColumn = countColumn; + CodeMirror.findColumn = findColumn; + CodeMirror.isWordChar = isWordCharBasic; + CodeMirror.Pass = Pass; + CodeMirror.signal = signal; + CodeMirror.Line = Line; + CodeMirror.changeEnd = changeEnd; + CodeMirror.scrollbarModel = scrollbarModel; + CodeMirror.Pos = Pos; + CodeMirror.cmpPos = cmp; + CodeMirror.modes = modes; + CodeMirror.mimeModes = mimeModes; + CodeMirror.resolveMode = resolveMode; + CodeMirror.getMode = getMode; + CodeMirror.modeExtensions = modeExtensions; + CodeMirror.extendMode = extendMode; + CodeMirror.copyState = copyState; + CodeMirror.startState = startState; + CodeMirror.innerMode = innerMode; + CodeMirror.commands = commands; + CodeMirror.keyMap = keyMap; + CodeMirror.keyName = keyName; + CodeMirror.isModifierKey = isModifierKey; + CodeMirror.lookupKey = lookupKey; + CodeMirror.normalizeKeyMap = normalizeKeyMap; + CodeMirror.StringStream = StringStream; + CodeMirror.SharedTextMarker = SharedTextMarker; + CodeMirror.TextMarker = TextMarker; + CodeMirror.LineWidget = LineWidget; + CodeMirror.e_preventDefault = e_preventDefault; + CodeMirror.e_stopPropagation = e_stopPropagation; + CodeMirror.e_stop = e_stop; + CodeMirror.addClass = addClass; + CodeMirror.contains = contains; + CodeMirror.rmClass = rmClass; + CodeMirror.keyNames = keyNames; + } + + // EDITOR CONSTRUCTOR + + defineOptions(CodeMirror); + + addEditorMethods(CodeMirror); + + // Set up methods on CodeMirror's prototype to redirect to the editor's document. + var dontDelegate = "iter insert remove copy getEditor constructor".split(" "); + for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0) + { CodeMirror.prototype[prop] = (function(method) { + return function() {return method.apply(this.doc, arguments)} + })(Doc.prototype[prop]); } } + + eventMixin(Doc); + CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}; + + // Extra arguments are stored as the mode's dependencies, which is + // used by (legacy) mechanisms like loadmode.js to automatically + // load a mode. (Preferred mechanism is the require/define calls.) + CodeMirror.defineMode = function(name/*, mode, …*/) { + if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name; } + defineMode.apply(this, arguments); + }; + + CodeMirror.defineMIME = defineMIME; + + // Minimal default mode. + CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); }); + CodeMirror.defineMIME("text/plain", "null"); + + // EXTENSIONS + + CodeMirror.defineExtension = function (name, func) { + CodeMirror.prototype[name] = func; + }; + CodeMirror.defineDocExtension = function (name, func) { + Doc.prototype[name] = func; + }; + + CodeMirror.fromTextArea = fromTextArea; + + addLegacyProps(CodeMirror); + + CodeMirror.version = "5.65.10"; + + return CodeMirror; +}))); + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/addon/edit/matchbrackets.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + var ie_lt8 = /MSIE \d/.test(navigator.userAgent) && + (document.documentMode == null || document.documentMode < 8); + + var Pos = CodeMirror.Pos; + + var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<", "<": ">>", ">": "<<"}; + + function bracketRegex(config) { + return config && config.bracketRegex || /[(){}[\]]/ + } + + function findMatchingBracket(cm, where, config) { + var line = cm.getLineHandle(where.line), pos = where.ch - 1; + var afterCursor = config && config.afterCursor + if (afterCursor == null) + afterCursor = /(^| )cm-fat-cursor($| )/.test(cm.getWrapperElement().className) + var re = bracketRegex(config) + + // A cursor is defined as between two characters, but in in vim command mode + // (i.e. not insert mode), the cursor is visually represented as a + // highlighted box on top of the 2nd character. Otherwise, we allow matches + // from before or after the cursor. + var match = (!afterCursor && pos >= 0 && re.test(line.text.charAt(pos)) && matching[line.text.charAt(pos)]) || + re.test(line.text.charAt(pos + 1)) && matching[line.text.charAt(++pos)]; + if (!match) return null; + var dir = match.charAt(1) == ">" ? 1 : -1; + if (config && config.strict && (dir > 0) != (pos == where.ch)) return null; + var style = cm.getTokenTypeAt(Pos(where.line, pos + 1)); + + var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style, config); + if (found == null) return null; + return {from: Pos(where.line, pos), to: found && found.pos, + match: found && found.ch == match.charAt(0), forward: dir > 0}; + } + + // bracketRegex is used to specify which type of bracket to scan + // should be a regexp, e.g. /[[\]]/ + // + // Note: If "where" is on an open bracket, then this bracket is ignored. + // + // Returns false when no bracket was found, null when it reached + // maxScanLines and gave up + function scanForBracket(cm, where, dir, style, config) { + var maxScanLen = (config && config.maxScanLineLength) || 10000; + var maxScanLines = (config && config.maxScanLines) || 1000; + + var stack = []; + var re = bracketRegex(config) + var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1) + : Math.max(cm.firstLine() - 1, where.line - maxScanLines); + for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) { + var line = cm.getLine(lineNo); + if (!line) continue; + var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1; + if (line.length > maxScanLen) continue; + if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0); + for (; pos != end; pos += dir) { + var ch = line.charAt(pos); + if (re.test(ch) && (style === undefined || + (cm.getTokenTypeAt(Pos(lineNo, pos + 1)) || "") == (style || ""))) { + var match = matching[ch]; + if (match && (match.charAt(1) == ">") == (dir > 0)) stack.push(ch); + else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch}; + else stack.pop(); + } + } + } + return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null; + } + + function matchBrackets(cm, autoclear, config) { + // Disable brace matching in long lines, since it'll cause hugely slow updates + var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000, + highlightNonMatching = config && config.highlightNonMatching; + var marks = [], ranges = cm.listSelections(); + for (var i = 0; i < ranges.length; i++) { + var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, config); + if (match && (match.match || highlightNonMatching !== false) && cm.getLine(match.from.line).length <= maxHighlightLen) { + var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; + marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style})); + if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen) + marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style})); + } + } + + if (marks.length) { + // Kludge to work around the IE bug from issue #1193, where text + // input stops going to the textarea whenever this fires. + if (ie_lt8 && cm.state.focused) cm.focus(); + + var clear = function() { + cm.operation(function() { + for (var i = 0; i < marks.length; i++) marks[i].clear(); + }); + }; + if (autoclear) setTimeout(clear, 800); + else return clear; + } + } + + function doMatchBrackets(cm) { + cm.operation(function() { + if (cm.state.matchBrackets.currentlyHighlighted) { + cm.state.matchBrackets.currentlyHighlighted(); + cm.state.matchBrackets.currentlyHighlighted = null; + } + cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets); + }); + } + + function clearHighlighted(cm) { + if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) { + cm.state.matchBrackets.currentlyHighlighted(); + cm.state.matchBrackets.currentlyHighlighted = null; + } + } + + CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) { + cm.off("cursorActivity", doMatchBrackets); + cm.off("focus", doMatchBrackets) + cm.off("blur", clearHighlighted) + clearHighlighted(cm); + } + if (val) { + cm.state.matchBrackets = typeof val == "object" ? val : {}; + cm.on("cursorActivity", doMatchBrackets); + cm.on("focus", doMatchBrackets) + cm.on("blur", clearHighlighted) + } + }); + + CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);}); + CodeMirror.defineExtension("findMatchingBracket", function(pos, config, oldConfig){ + // Backwards-compatibility kludge + if (oldConfig || typeof config == "boolean") { + if (!oldConfig) { + config = config ? {strict: true} : null + } else { + oldConfig.strict = config + config = oldConfig + } + } + return findMatchingBracket(this, pos, config) + }); + CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){ + return scanForBracket(this, pos, dir, style, config); + }); +}); + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/addon/edit/trailingspace.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) { + if (prev == CodeMirror.Init) prev = false; + if (prev && !val) + cm.removeOverlay("trailingspace"); + else if (!prev && val) + cm.addOverlay({ + token: function(stream) { + for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {} + if (i > stream.pos) { stream.pos = i; return null; } + stream.pos = l; + return "trailingspace"; + }, + name: "trailingspace" + }); + }); +}); + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/addon/lint/lint.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + var GUTTER_ID = "CodeMirror-lint-markers"; + var LINT_LINE_ID = "CodeMirror-lint-line-"; + + function showTooltip(cm, e, content) { + var tt = document.createElement("div"); + tt.className = "CodeMirror-lint-tooltip cm-s-" + cm.options.theme; + tt.appendChild(content.cloneNode(true)); + if (cm.state.lint.options.selfContain) + cm.getWrapperElement().appendChild(tt); + else + document.body.appendChild(tt); + + function position(e) { + if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position); + tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + "px"; + tt.style.left = (e.clientX + 5) + "px"; + } + CodeMirror.on(document, "mousemove", position); + position(e); + if (tt.style.opacity != null) tt.style.opacity = 1; + return tt; + } + function rm(elt) { + if (elt.parentNode) elt.parentNode.removeChild(elt); + } + function hideTooltip(tt) { + if (!tt.parentNode) return; + if (tt.style.opacity == null) rm(tt); + tt.style.opacity = 0; + setTimeout(function() { rm(tt); }, 600); + } + + function showTooltipFor(cm, e, content, node) { + var tooltip = showTooltip(cm, e, content); + function hide() { + CodeMirror.off(node, "mouseout", hide); + if (tooltip) { hideTooltip(tooltip); tooltip = null; } + } + var poll = setInterval(function() { + if (tooltip) for (var n = node;; n = n.parentNode) { + if (n && n.nodeType == 11) n = n.host; + if (n == document.body) return; + if (!n) { hide(); break; } + } + if (!tooltip) return clearInterval(poll); + }, 400); + CodeMirror.on(node, "mouseout", hide); + } + + function LintState(cm, conf, hasGutter) { + this.marked = []; + if (conf instanceof Function) conf = {getAnnotations: conf}; + if (!conf || conf === true) conf = {}; + this.options = {}; + this.linterOptions = conf.options || {}; + for (var prop in defaults) this.options[prop] = defaults[prop]; + for (var prop in conf) { + if (defaults.hasOwnProperty(prop)) { + if (conf[prop] != null) this.options[prop] = conf[prop]; + } else if (!conf.options) { + this.linterOptions[prop] = conf[prop]; + } + } + this.timeout = null; + this.hasGutter = hasGutter; + this.onMouseOver = function(e) { onMouseOver(cm, e); }; + this.waitingFor = 0 + } + + var defaults = { + highlightLines: false, + tooltips: true, + delay: 500, + lintOnChange: true, + getAnnotations: null, + async: false, + selfContain: null, + formatAnnotation: null, + onUpdateLinting: null + } + + function clearMarks(cm) { + var state = cm.state.lint; + if (state.hasGutter) cm.clearGutter(GUTTER_ID); + if (state.options.highlightLines) clearErrorLines(cm); + for (var i = 0; i < state.marked.length; ++i) + state.marked[i].clear(); + state.marked.length = 0; + } + + function clearErrorLines(cm) { + cm.eachLine(function(line) { + var has = line.wrapClass && /\bCodeMirror-lint-line-\w+\b/.exec(line.wrapClass); + if (has) cm.removeLineClass(line, "wrap", has[0]); + }) + } + + function makeMarker(cm, labels, severity, multiple, tooltips) { + var marker = document.createElement("div"), inner = marker; + marker.className = "CodeMirror-lint-marker CodeMirror-lint-marker-" + severity; + if (multiple) { + inner = marker.appendChild(document.createElement("div")); + inner.className = "CodeMirror-lint-marker CodeMirror-lint-marker-multiple"; + } + + if (tooltips != false) CodeMirror.on(inner, "mouseover", function(e) { + showTooltipFor(cm, e, labels, inner); + }); + + return marker; + } + + function getMaxSeverity(a, b) { + if (a == "error") return a; + else return b; + } + + function groupByLine(annotations) { + var lines = []; + for (var i = 0; i < annotations.length; ++i) { + var ann = annotations[i], line = ann.from.line; + (lines[line] || (lines[line] = [])).push(ann); + } + return lines; + } + + function annotationTooltip(ann) { + var severity = ann.severity; + if (!severity) severity = "error"; + var tip = document.createElement("div"); + tip.className = "CodeMirror-lint-message CodeMirror-lint-message-" + severity; + if (typeof ann.messageHTML != 'undefined') { + tip.innerHTML = ann.messageHTML; + } else { + tip.appendChild(document.createTextNode(ann.message)); + } + return tip; + } + + function lintAsync(cm, getAnnotations) { + var state = cm.state.lint + var id = ++state.waitingFor + function abort() { + id = -1 + cm.off("change", abort) + } + cm.on("change", abort) + getAnnotations(cm.getValue(), function(annotations, arg2) { + cm.off("change", abort) + if (state.waitingFor != id) return + if (arg2 && annotations instanceof CodeMirror) annotations = arg2 + cm.operation(function() {updateLinting(cm, annotations)}) + }, state.linterOptions, cm); + } + + function startLinting(cm) { + var state = cm.state.lint; + if (!state) return; + var options = state.options; + /* + * Passing rules in `options` property prevents JSHint (and other linters) from complaining + * about unrecognized rules like `onUpdateLinting`, `delay`, `lintOnChange`, etc. + */ + var getAnnotations = options.getAnnotations || cm.getHelper(CodeMirror.Pos(0, 0), "lint"); + if (!getAnnotations) return; + if (options.async || getAnnotations.async) { + lintAsync(cm, getAnnotations) + } else { + var annotations = getAnnotations(cm.getValue(), state.linterOptions, cm); + if (!annotations) return; + if (annotations.then) annotations.then(function(issues) { + cm.operation(function() {updateLinting(cm, issues)}) + }); + else cm.operation(function() {updateLinting(cm, annotations)}) + } + } + + function updateLinting(cm, annotationsNotSorted) { + var state = cm.state.lint; + if (!state) return; + var options = state.options; + clearMarks(cm); + + var annotations = groupByLine(annotationsNotSorted); + + for (var line = 0; line < annotations.length; ++line) { + var anns = annotations[line]; + if (!anns) continue; + + // filter out duplicate messages + var message = []; + anns = anns.filter(function(item) { return message.indexOf(item.message) > -1 ? false : message.push(item.message) }); + + var maxSeverity = null; + var tipLabel = state.hasGutter && document.createDocumentFragment(); + + for (var i = 0; i < anns.length; ++i) { + var ann = anns[i]; + var severity = ann.severity; + if (!severity) severity = "error"; + maxSeverity = getMaxSeverity(maxSeverity, severity); + + if (options.formatAnnotation) ann = options.formatAnnotation(ann); + if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann)); + + if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, { + className: "CodeMirror-lint-mark CodeMirror-lint-mark-" + severity, + __annotation: ann + })); + } + // use original annotations[line] to show multiple messages + if (state.hasGutter) + cm.setGutterMarker(line, GUTTER_ID, makeMarker(cm, tipLabel, maxSeverity, annotations[line].length > 1, + options.tooltips)); + + if (options.highlightLines) + cm.addLineClass(line, "wrap", LINT_LINE_ID + maxSeverity); + } + if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm); + } + + function onChange(cm) { + var state = cm.state.lint; + if (!state) return; + clearTimeout(state.timeout); + state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay); + } + + function popupTooltips(cm, annotations, e) { + var target = e.target || e.srcElement; + var tooltip = document.createDocumentFragment(); + for (var i = 0; i < annotations.length; i++) { + var ann = annotations[i]; + tooltip.appendChild(annotationTooltip(ann)); + } + showTooltipFor(cm, e, tooltip, target); + } + + function onMouseOver(cm, e) { + var target = e.target || e.srcElement; + if (!/\bCodeMirror-lint-mark-/.test(target.className)) return; + var box = target.getBoundingClientRect(), x = (box.left + box.right) / 2, y = (box.top + box.bottom) / 2; + var spans = cm.findMarksAt(cm.coordsChar({left: x, top: y}, "client")); + + var annotations = []; + for (var i = 0; i < spans.length; ++i) { + var ann = spans[i].__annotation; + if (ann) annotations.push(ann); + } + if (annotations.length) popupTooltips(cm, annotations, e); + } + + CodeMirror.defineOption("lint", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) { + clearMarks(cm); + if (cm.state.lint.options.lintOnChange !== false) + cm.off("change", onChange); + CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver); + clearTimeout(cm.state.lint.timeout); + delete cm.state.lint; + } + + if (val) { + var gutters = cm.getOption("gutters"), hasLintGutter = false; + for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true; + var state = cm.state.lint = new LintState(cm, val, hasLintGutter); + if (state.options.lintOnChange) + cm.on("change", onChange); + if (state.options.tooltips != false && state.options.tooltips != "gutter") + CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver); + + startLinting(cm); + } + }); + + CodeMirror.defineExtension("performLint", function() { + startLinting(this); + }); +}); + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/addon/selection/active-line.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + var WRAP_CLASS = "CodeMirror-activeline"; + var BACK_CLASS = "CodeMirror-activeline-background"; + var GUTT_CLASS = "CodeMirror-activeline-gutter"; + + CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) { + var prev = old == CodeMirror.Init ? false : old; + if (val == prev) return + if (prev) { + cm.off("beforeSelectionChange", selectionChange); + clearActiveLines(cm); + delete cm.state.activeLines; + } + if (val) { + cm.state.activeLines = []; + updateActiveLines(cm, cm.listSelections()); + cm.on("beforeSelectionChange", selectionChange); + } + }); + + function clearActiveLines(cm) { + for (var i = 0; i < cm.state.activeLines.length; i++) { + cm.removeLineClass(cm.state.activeLines[i], "wrap", WRAP_CLASS); + cm.removeLineClass(cm.state.activeLines[i], "background", BACK_CLASS); + cm.removeLineClass(cm.state.activeLines[i], "gutter", GUTT_CLASS); + } + } + + function sameArray(a, b) { + if (a.length != b.length) return false; + for (var i = 0; i < a.length; i++) + if (a[i] != b[i]) return false; + return true; + } + + function updateActiveLines(cm, ranges) { + var active = []; + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i]; + var option = cm.getOption("styleActiveLine"); + if (typeof option == "object" && option.nonEmpty ? range.anchor.line != range.head.line : !range.empty()) + continue + var line = cm.getLineHandleVisualStart(range.head.line); + if (active[active.length - 1] != line) active.push(line); + } + if (sameArray(cm.state.activeLines, active)) return; + cm.operation(function() { + clearActiveLines(cm); + for (var i = 0; i < active.length; i++) { + cm.addLineClass(active[i], "wrap", WRAP_CLASS); + cm.addLineClass(active[i], "background", BACK_CLASS); + cm.addLineClass(active[i], "gutter", GUTT_CLASS); + } + cm.state.activeLines = active; + }); + } + + function selectionChange(cm, sel) { + updateActiveLines(cm, sel.ranges); + } +}); + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/mode/javascript/javascript.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("javascript", function(config, parserConfig) { + var indentUnit = config.indentUnit; + var statementIndent = parserConfig.statementIndent; + var jsonldMode = parserConfig.jsonld; + var jsonMode = parserConfig.json || jsonldMode; + var trackScope = parserConfig.trackScope !== false + var isTS = parserConfig.typescript; + var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/; + + // Tokenizer + + var keywords = function(){ + function kw(type) {return {type: type, style: "keyword"};} + var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d"); + var operator = kw("operator"), atom = {type: "atom", style: "atom"}; + + return { + "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, + "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C, + "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"), + "function": kw("function"), "catch": kw("catch"), + "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"), + "in": operator, "typeof": operator, "instanceof": operator, + "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom, + "this": kw("this"), "class": kw("class"), "super": kw("atom"), + "yield": C, "export": kw("export"), "import": kw("import"), "extends": C, + "await": C + }; + }(); + + var isOperatorChar = /[+\-*&%=<>!?|~^@]/; + var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/; + + function readRegexp(stream) { + var escaped = false, next, inSet = false; + while ((next = stream.next()) != null) { + if (!escaped) { + if (next == "/" && !inSet) return; + if (next == "[") inSet = true; + else if (inSet && next == "]") inSet = false; + } + escaped = !escaped && next == "\\"; + } + } + + // Used as scratch variables to communicate multiple values without + // consing up tons of objects. + var type, content; + function ret(tp, style, cont) { + type = tp; content = cont; + return style; + } + function tokenBase(stream, state) { + var ch = stream.next(); + if (ch == '"' || ch == "'") { + state.tokenize = tokenString(ch); + return state.tokenize(stream, state); + } else if (ch == "." && stream.match(/^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/)) { + return ret("number", "number"); + } else if (ch == "." && stream.match("..")) { + return ret("spread", "meta"); + } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) { + return ret(ch); + } else if (ch == "=" && stream.eat(">")) { + return ret("=>", "operator"); + } else if (ch == "0" && stream.match(/^(?:x[\dA-Fa-f_]+|o[0-7_]+|b[01_]+)n?/)) { + return ret("number", "number"); + } else if (/\d/.test(ch)) { + stream.match(/^[\d_]*(?:n|(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)?/); + return ret("number", "number"); + } else if (ch == "/") { + if (stream.eat("*")) { + state.tokenize = tokenComment; + return tokenComment(stream, state); + } else if (stream.eat("/")) { + stream.skipToEnd(); + return ret("comment", "comment"); + } else if (expressionAllowed(stream, state, 1)) { + readRegexp(stream); + stream.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/); + return ret("regexp", "string-2"); + } else { + stream.eat("="); + return ret("operator", "operator", stream.current()); + } + } else if (ch == "`") { + state.tokenize = tokenQuasi; + return tokenQuasi(stream, state); + } else if (ch == "#" && stream.peek() == "!") { + stream.skipToEnd(); + return ret("meta", "meta"); + } else if (ch == "#" && stream.eatWhile(wordRE)) { + return ret("variable", "property") + } else if (ch == "<" && stream.match("!--") || + (ch == "-" && stream.match("->") && !/\S/.test(stream.string.slice(0, stream.start)))) { + stream.skipToEnd() + return ret("comment", "comment") + } else if (isOperatorChar.test(ch)) { + if (ch != ">" || !state.lexical || state.lexical.type != ">") { + if (stream.eat("=")) { + if (ch == "!" || ch == "=") stream.eat("=") + } else if (/[<>*+\-|&?]/.test(ch)) { + stream.eat(ch) + if (ch == ">") stream.eat(ch) + } + } + if (ch == "?" && stream.eat(".")) return ret(".") + return ret("operator", "operator", stream.current()); + } else if (wordRE.test(ch)) { + stream.eatWhile(wordRE); + var word = stream.current() + if (state.lastType != ".") { + if (keywords.propertyIsEnumerable(word)) { + var kw = keywords[word] + return ret(kw.type, kw.style, word) + } + if (word == "async" && stream.match(/^(\s|\/\*([^*]|\*(?!\/))*?\*\/)*[\[\(\w]/, false)) + return ret("async", "keyword", word) + } + return ret("variable", "variable", word) + } + } + + function tokenString(quote) { + return function(stream, state) { + var escaped = false, next; + if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){ + state.tokenize = tokenBase; + return ret("jsonld-keyword", "meta"); + } + while ((next = stream.next()) != null) { + if (next == quote && !escaped) break; + escaped = !escaped && next == "\\"; + } + if (!escaped) state.tokenize = tokenBase; + return ret("string", "string"); + }; + } + + function tokenComment(stream, state) { + var maybeEnd = false, ch; + while (ch = stream.next()) { + if (ch == "/" && maybeEnd) { + state.tokenize = tokenBase; + break; + } + maybeEnd = (ch == "*"); + } + return ret("comment", "comment"); + } + + function tokenQuasi(stream, state) { + var escaped = false, next; + while ((next = stream.next()) != null) { + if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) { + state.tokenize = tokenBase; + break; + } + escaped = !escaped && next == "\\"; + } + return ret("quasi", "string-2", stream.current()); + } + + var brackets = "([{}])"; + // This is a crude lookahead trick to try and notice that we're + // parsing the argument patterns for a fat-arrow function before we + // actually hit the arrow token. It only works if the arrow is on + // the same line as the arguments and there's no strange noise + // (comments) in between. Fallback is to only notice when we hit the + // arrow, and not declare the arguments as locals for the arrow + // body. + function findFatArrow(stream, state) { + if (state.fatArrowAt) state.fatArrowAt = null; + var arrow = stream.string.indexOf("=>", stream.start); + if (arrow < 0) return; + + if (isTS) { // Try to skip TypeScript return type declarations after the arguments + var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow)) + if (m) arrow = m.index + } + + var depth = 0, sawSomething = false; + for (var pos = arrow - 1; pos >= 0; --pos) { + var ch = stream.string.charAt(pos); + var bracket = brackets.indexOf(ch); + if (bracket >= 0 && bracket < 3) { + if (!depth) { ++pos; break; } + if (--depth == 0) { if (ch == "(") sawSomething = true; break; } + } else if (bracket >= 3 && bracket < 6) { + ++depth; + } else if (wordRE.test(ch)) { + sawSomething = true; + } else if (/["'\/`]/.test(ch)) { + for (;; --pos) { + if (pos == 0) return + var next = stream.string.charAt(pos - 1) + if (next == ch && stream.string.charAt(pos - 2) != "\\") { pos--; break } + } + } else if (sawSomething && !depth) { + ++pos; + break; + } + } + if (sawSomething && !depth) state.fatArrowAt = pos; + } + + // Parser + + var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, + "regexp": true, "this": true, "import": true, "jsonld-keyword": true}; + + function JSLexical(indented, column, type, align, prev, info) { + this.indented = indented; + this.column = column; + this.type = type; + this.prev = prev; + this.info = info; + if (align != null) this.align = align; + } + + function inScope(state, varname) { + if (!trackScope) return false + for (var v = state.localVars; v; v = v.next) + if (v.name == varname) return true; + for (var cx = state.context; cx; cx = cx.prev) { + for (var v = cx.vars; v; v = v.next) + if (v.name == varname) return true; + } + } + + function parseJS(state, style, type, content, stream) { + var cc = state.cc; + // Communicate our context to the combinators. + // (Less wasteful than consing up a hundred closures on every call.) + cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style; + + if (!state.lexical.hasOwnProperty("align")) + state.lexical.align = true; + + while(true) { + var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement; + if (combinator(type, content)) { + while(cc.length && cc[cc.length - 1].lex) + cc.pop()(); + if (cx.marked) return cx.marked; + if (type == "variable" && inScope(state, content)) return "variable-2"; + return style; + } + } + } + + // Combinator utils + + var cx = {state: null, column: null, marked: null, cc: null}; + function pass() { + for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]); + } + function cont() { + pass.apply(null, arguments); + return true; + } + function inList(name, list) { + for (var v = list; v; v = v.next) if (v.name == name) return true + return false; + } + function register(varname) { + var state = cx.state; + cx.marked = "def"; + if (!trackScope) return + if (state.context) { + if (state.lexical.info == "var" && state.context && state.context.block) { + // FIXME function decls are also not block scoped + var newContext = registerVarScoped(varname, state.context) + if (newContext != null) { + state.context = newContext + return + } + } else if (!inList(varname, state.localVars)) { + state.localVars = new Var(varname, state.localVars) + return + } + } + // Fall through means this is global + if (parserConfig.globalVars && !inList(varname, state.globalVars)) + state.globalVars = new Var(varname, state.globalVars) + } + function registerVarScoped(varname, context) { + if (!context) { + return null + } else if (context.block) { + var inner = registerVarScoped(varname, context.prev) + if (!inner) return null + if (inner == context.prev) return context + return new Context(inner, context.vars, true) + } else if (inList(varname, context.vars)) { + return context + } else { + return new Context(context.prev, new Var(varname, context.vars), false) + } + } + + function isModifier(name) { + return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly" + } + + // Combinators + + function Context(prev, vars, block) { this.prev = prev; this.vars = vars; this.block = block } + function Var(name, next) { this.name = name; this.next = next } + + var defaultVars = new Var("this", new Var("arguments", null)) + function pushcontext() { + cx.state.context = new Context(cx.state.context, cx.state.localVars, false) + cx.state.localVars = defaultVars + } + function pushblockcontext() { + cx.state.context = new Context(cx.state.context, cx.state.localVars, true) + cx.state.localVars = null + } + pushcontext.lex = pushblockcontext.lex = true + function popcontext() { + cx.state.localVars = cx.state.context.vars + cx.state.context = cx.state.context.prev + } + popcontext.lex = true + function pushlex(type, info) { + var result = function() { + var state = cx.state, indent = state.indented; + if (state.lexical.type == "stat") indent = state.lexical.indented; + else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev) + indent = outer.indented; + state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info); + }; + result.lex = true; + return result; + } + function poplex() { + var state = cx.state; + if (state.lexical.prev) { + if (state.lexical.type == ")") + state.indented = state.lexical.indented; + state.lexical = state.lexical.prev; + } + } + poplex.lex = true; + + function expect(wanted) { + function exp(type) { + if (type == wanted) return cont(); + else if (wanted == ";" || type == "}" || type == ")" || type == "]") return pass(); + else return cont(exp); + }; + return exp; + } + + function statement(type, value) { + if (type == "var") return cont(pushlex("vardef", value), vardef, expect(";"), poplex); + if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex); + if (type == "keyword b") return cont(pushlex("form"), statement, poplex); + if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex); + if (type == "debugger") return cont(expect(";")); + if (type == "{") return cont(pushlex("}"), pushblockcontext, block, poplex, popcontext); + if (type == ";") return cont(); + if (type == "if") { + if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex) + cx.state.cc.pop()(); + return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse); + } + if (type == "function") return cont(functiondef); + if (type == "for") return cont(pushlex("form"), pushblockcontext, forspec, statement, popcontext, poplex); + if (type == "class" || (isTS && value == "interface")) { + cx.marked = "keyword" + return cont(pushlex("form", type == "class" ? type : value), className, poplex) + } + if (type == "variable") { + if (isTS && value == "declare") { + cx.marked = "keyword" + return cont(statement) + } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) { + cx.marked = "keyword" + if (value == "enum") return cont(enumdef); + else if (value == "type") return cont(typename, expect("operator"), typeexpr, expect(";")); + else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex) + } else if (isTS && value == "namespace") { + cx.marked = "keyword" + return cont(pushlex("form"), expression, statement, poplex) + } else if (isTS && value == "abstract") { + cx.marked = "keyword" + return cont(statement) + } else { + return cont(pushlex("stat"), maybelabel); + } + } + if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"), pushblockcontext, + block, poplex, poplex, popcontext); + if (type == "case") return cont(expression, expect(":")); + if (type == "default") return cont(expect(":")); + if (type == "catch") return cont(pushlex("form"), pushcontext, maybeCatchBinding, statement, poplex, popcontext); + if (type == "export") return cont(pushlex("stat"), afterExport, poplex); + if (type == "import") return cont(pushlex("stat"), afterImport, poplex); + if (type == "async") return cont(statement) + if (value == "@") return cont(expression, statement) + return pass(pushlex("stat"), expression, expect(";"), poplex); + } + function maybeCatchBinding(type) { + if (type == "(") return cont(funarg, expect(")")) + } + function expression(type, value) { + return expressionInner(type, value, false); + } + function expressionNoComma(type, value) { + return expressionInner(type, value, true); + } + function parenExpr(type) { + if (type != "(") return pass() + return cont(pushlex(")"), maybeexpression, expect(")"), poplex) + } + function expressionInner(type, value, noComma) { + if (cx.state.fatArrowAt == cx.stream.start) { + var body = noComma ? arrowBodyNoComma : arrowBody; + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext); + else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext); + } + + var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma; + if (atomicTypes.hasOwnProperty(type)) return cont(maybeop); + if (type == "function") return cont(functiondef, maybeop); + if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); } + if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression); + if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop); + if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression); + if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop); + if (type == "{") return contCommasep(objprop, "}", null, maybeop); + if (type == "quasi") return pass(quasi, maybeop); + if (type == "new") return cont(maybeTarget(noComma)); + return cont(); + } + function maybeexpression(type) { + if (type.match(/[;\}\)\],]/)) return pass(); + return pass(expression); + } + + function maybeoperatorComma(type, value) { + if (type == ",") return cont(maybeexpression); + return maybeoperatorNoComma(type, value, false); + } + function maybeoperatorNoComma(type, value, noComma) { + var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma; + var expr = noComma == false ? expression : expressionNoComma; + if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext); + if (type == "operator") { + if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me); + if (isTS && value == "<" && cx.stream.match(/^([^<>]|<[^<>]*>)*>\s*\(/, false)) + return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me); + if (value == "?") return cont(expression, expect(":"), expr); + return cont(expr); + } + if (type == "quasi") { return pass(quasi, me); } + if (type == ";") return; + if (type == "(") return contCommasep(expressionNoComma, ")", "call", me); + if (type == ".") return cont(property, me); + if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me); + if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) } + if (type == "regexp") { + cx.state.lastType = cx.marked = "operator" + cx.stream.backUp(cx.stream.pos - cx.stream.start - 1) + return cont(expr) + } + } + function quasi(type, value) { + if (type != "quasi") return pass(); + if (value.slice(value.length - 2) != "${") return cont(quasi); + return cont(maybeexpression, continueQuasi); + } + function continueQuasi(type) { + if (type == "}") { + cx.marked = "string-2"; + cx.state.tokenize = tokenQuasi; + return cont(quasi); + } + } + function arrowBody(type) { + findFatArrow(cx.stream, cx.state); + return pass(type == "{" ? statement : expression); + } + function arrowBodyNoComma(type) { + findFatArrow(cx.stream, cx.state); + return pass(type == "{" ? statement : expressionNoComma); + } + function maybeTarget(noComma) { + return function(type) { + if (type == ".") return cont(noComma ? targetNoComma : target); + else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma) + else return pass(noComma ? expressionNoComma : expression); + }; + } + function target(_, value) { + if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); } + } + function targetNoComma(_, value) { + if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); } + } + function maybelabel(type) { + if (type == ":") return cont(poplex, statement); + return pass(maybeoperatorComma, expect(";"), poplex); + } + function property(type) { + if (type == "variable") {cx.marked = "property"; return cont();} + } + function objprop(type, value) { + if (type == "async") { + cx.marked = "property"; + return cont(objprop); + } else if (type == "variable" || cx.style == "keyword") { + cx.marked = "property"; + if (value == "get" || value == "set") return cont(getterSetter); + var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params + if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false))) + cx.state.fatArrowAt = cx.stream.pos + m[0].length + return cont(afterprop); + } else if (type == "number" || type == "string") { + cx.marked = jsonldMode ? "property" : (cx.style + " property"); + return cont(afterprop); + } else if (type == "jsonld-keyword") { + return cont(afterprop); + } else if (isTS && isModifier(value)) { + cx.marked = "keyword" + return cont(objprop) + } else if (type == "[") { + return cont(expression, maybetype, expect("]"), afterprop); + } else if (type == "spread") { + return cont(expressionNoComma, afterprop); + } else if (value == "*") { + cx.marked = "keyword"; + return cont(objprop); + } else if (type == ":") { + return pass(afterprop) + } + } + function getterSetter(type) { + if (type != "variable") return pass(afterprop); + cx.marked = "property"; + return cont(functiondef); + } + function afterprop(type) { + if (type == ":") return cont(expressionNoComma); + if (type == "(") return pass(functiondef); + } + function commasep(what, end, sep) { + function proceed(type, value) { + if (sep ? sep.indexOf(type) > -1 : type == ",") { + var lex = cx.state.lexical; + if (lex.info == "call") lex.pos = (lex.pos || 0) + 1; + return cont(function(type, value) { + if (type == end || value == end) return pass() + return pass(what) + }, proceed); + } + if (type == end || value == end) return cont(); + if (sep && sep.indexOf(";") > -1) return pass(what) + return cont(expect(end)); + } + return function(type, value) { + if (type == end || value == end) return cont(); + return pass(what, proceed); + }; + } + function contCommasep(what, end, info) { + for (var i = 3; i < arguments.length; i++) + cx.cc.push(arguments[i]); + return cont(pushlex(end, info), commasep(what, end), poplex); + } + function block(type) { + if (type == "}") return cont(); + return pass(statement, block); + } + function maybetype(type, value) { + if (isTS) { + if (type == ":") return cont(typeexpr); + if (value == "?") return cont(maybetype); + } + } + function maybetypeOrIn(type, value) { + if (isTS && (type == ":" || value == "in")) return cont(typeexpr) + } + function mayberettype(type) { + if (isTS && type == ":") { + if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr) + else return cont(typeexpr) + } + } + function isKW(_, value) { + if (value == "is") { + cx.marked = "keyword" + return cont() + } + } + function typeexpr(type, value) { + if (value == "keyof" || value == "typeof" || value == "infer" || value == "readonly") { + cx.marked = "keyword" + return cont(value == "typeof" ? expressionNoComma : typeexpr) + } + if (type == "variable" || value == "void") { + cx.marked = "type" + return cont(afterType) + } + if (value == "|" || value == "&") return cont(typeexpr) + if (type == "string" || type == "number" || type == "atom") return cont(afterType); + if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType) + if (type == "{") return cont(pushlex("}"), typeprops, poplex, afterType) + if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType, afterType) + if (type == "<") return cont(commasep(typeexpr, ">"), typeexpr) + if (type == "quasi") { return pass(quasiType, afterType); } + } + function maybeReturnType(type) { + if (type == "=>") return cont(typeexpr) + } + function typeprops(type) { + if (type.match(/[\}\)\]]/)) return cont() + if (type == "," || type == ";") return cont(typeprops) + return pass(typeprop, typeprops) + } + function typeprop(type, value) { + if (type == "variable" || cx.style == "keyword") { + cx.marked = "property" + return cont(typeprop) + } else if (value == "?" || type == "number" || type == "string") { + return cont(typeprop) + } else if (type == ":") { + return cont(typeexpr) + } else if (type == "[") { + return cont(expect("variable"), maybetypeOrIn, expect("]"), typeprop) + } else if (type == "(") { + return pass(functiondecl, typeprop) + } else if (!type.match(/[;\}\)\],]/)) { + return cont() + } + } + function quasiType(type, value) { + if (type != "quasi") return pass(); + if (value.slice(value.length - 2) != "${") return cont(quasiType); + return cont(typeexpr, continueQuasiType); + } + function continueQuasiType(type) { + if (type == "}") { + cx.marked = "string-2"; + cx.state.tokenize = tokenQuasi; + return cont(quasiType); + } + } + function typearg(type, value) { + if (type == "variable" && cx.stream.match(/^\s*[?:]/, false) || value == "?") return cont(typearg) + if (type == ":") return cont(typeexpr) + if (type == "spread") return cont(typearg) + return pass(typeexpr) + } + function afterType(type, value) { + if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) + if (value == "|" || type == "." || value == "&") return cont(typeexpr) + if (type == "[") return cont(typeexpr, expect("]"), afterType) + if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) } + if (value == "?") return cont(typeexpr, expect(":"), typeexpr) + } + function maybeTypeArgs(_, value) { + if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) + } + function typeparam() { + return pass(typeexpr, maybeTypeDefault) + } + function maybeTypeDefault(_, value) { + if (value == "=") return cont(typeexpr) + } + function vardef(_, value) { + if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)} + return pass(pattern, maybetype, maybeAssign, vardefCont); + } + function pattern(type, value) { + if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) } + if (type == "variable") { register(value); return cont(); } + if (type == "spread") return cont(pattern); + if (type == "[") return contCommasep(eltpattern, "]"); + if (type == "{") return contCommasep(proppattern, "}"); + } + function proppattern(type, value) { + if (type == "variable" && !cx.stream.match(/^\s*:/, false)) { + register(value); + return cont(maybeAssign); + } + if (type == "variable") cx.marked = "property"; + if (type == "spread") return cont(pattern); + if (type == "}") return pass(); + if (type == "[") return cont(expression, expect(']'), expect(':'), proppattern); + return cont(expect(":"), pattern, maybeAssign); + } + function eltpattern() { + return pass(pattern, maybeAssign) + } + function maybeAssign(_type, value) { + if (value == "=") return cont(expressionNoComma); + } + function vardefCont(type) { + if (type == ",") return cont(vardef); + } + function maybeelse(type, value) { + if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex); + } + function forspec(type, value) { + if (value == "await") return cont(forspec); + if (type == "(") return cont(pushlex(")"), forspec1, poplex); + } + function forspec1(type) { + if (type == "var") return cont(vardef, forspec2); + if (type == "variable") return cont(forspec2); + return pass(forspec2) + } + function forspec2(type, value) { + if (type == ")") return cont() + if (type == ";") return cont(forspec2) + if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression, forspec2) } + return pass(expression, forspec2) + } + function functiondef(type, value) { + if (value == "*") {cx.marked = "keyword"; return cont(functiondef);} + if (type == "variable") {register(value); return cont(functiondef);} + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext); + if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef) + } + function functiondecl(type, value) { + if (value == "*") {cx.marked = "keyword"; return cont(functiondecl);} + if (type == "variable") {register(value); return cont(functiondecl);} + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, popcontext); + if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondecl) + } + function typename(type, value) { + if (type == "keyword" || type == "variable") { + cx.marked = "type" + return cont(typename) + } else if (value == "<") { + return cont(pushlex(">"), commasep(typeparam, ">"), poplex) + } + } + function funarg(type, value) { + if (value == "@") cont(expression, funarg) + if (type == "spread") return cont(funarg); + if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); } + if (isTS && type == "this") return cont(maybetype, maybeAssign) + return pass(pattern, maybetype, maybeAssign); + } + function classExpression(type, value) { + // Class expressions may have an optional name. + if (type == "variable") return className(type, value); + return classNameAfter(type, value); + } + function className(type, value) { + if (type == "variable") {register(value); return cont(classNameAfter);} + } + function classNameAfter(type, value) { + if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter) + if (value == "extends" || value == "implements" || (isTS && type == ",")) { + if (value == "implements") cx.marked = "keyword"; + return cont(isTS ? typeexpr : expression, classNameAfter); + } + if (type == "{") return cont(pushlex("}"), classBody, poplex); + } + function classBody(type, value) { + if (type == "async" || + (type == "variable" && + (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) && + cx.stream.match(/^\s+#?[\w$\xa1-\uffff]/, false))) { + cx.marked = "keyword"; + return cont(classBody); + } + if (type == "variable" || cx.style == "keyword") { + cx.marked = "property"; + return cont(classfield, classBody); + } + if (type == "number" || type == "string") return cont(classfield, classBody); + if (type == "[") + return cont(expression, maybetype, expect("]"), classfield, classBody) + if (value == "*") { + cx.marked = "keyword"; + return cont(classBody); + } + if (isTS && type == "(") return pass(functiondecl, classBody) + if (type == ";" || type == ",") return cont(classBody); + if (type == "}") return cont(); + if (value == "@") return cont(expression, classBody) + } + function classfield(type, value) { + if (value == "!") return cont(classfield) + if (value == "?") return cont(classfield) + if (type == ":") return cont(typeexpr, maybeAssign) + if (value == "=") return cont(expressionNoComma) + var context = cx.state.lexical.prev, isInterface = context && context.info == "interface" + return pass(isInterface ? functiondecl : functiondef) + } + function afterExport(type, value) { + if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); } + if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); } + if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";")); + return pass(statement); + } + function exportField(type, value) { + if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); } + if (type == "variable") return pass(expressionNoComma, exportField); + } + function afterImport(type) { + if (type == "string") return cont(); + if (type == "(") return pass(expression); + if (type == ".") return pass(maybeoperatorComma); + return pass(importSpec, maybeMoreImports, maybeFrom); + } + function importSpec(type, value) { + if (type == "{") return contCommasep(importSpec, "}"); + if (type == "variable") register(value); + if (value == "*") cx.marked = "keyword"; + return cont(maybeAs); + } + function maybeMoreImports(type) { + if (type == ",") return cont(importSpec, maybeMoreImports) + } + function maybeAs(_type, value) { + if (value == "as") { cx.marked = "keyword"; return cont(importSpec); } + } + function maybeFrom(_type, value) { + if (value == "from") { cx.marked = "keyword"; return cont(expression); } + } + function arrayLiteral(type) { + if (type == "]") return cont(); + return pass(commasep(expressionNoComma, "]")); + } + function enumdef() { + return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex) + } + function enummember() { + return pass(pattern, maybeAssign); + } + + function isContinuedStatement(state, textAfter) { + return state.lastType == "operator" || state.lastType == "," || + isOperatorChar.test(textAfter.charAt(0)) || + /[,.]/.test(textAfter.charAt(0)); + } + + function expressionAllowed(stream, state, backUp) { + return state.tokenize == tokenBase && + /^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) || + (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0)))) + } + + // Interface + + return { + startState: function(basecolumn) { + var state = { + tokenize: tokenBase, + lastType: "sof", + cc: [], + lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false), + localVars: parserConfig.localVars, + context: parserConfig.localVars && new Context(null, null, false), + indented: basecolumn || 0 + }; + if (parserConfig.globalVars && typeof parserConfig.globalVars == "object") + state.globalVars = parserConfig.globalVars; + return state; + }, + + token: function(stream, state) { + if (stream.sol()) { + if (!state.lexical.hasOwnProperty("align")) + state.lexical.align = false; + state.indented = stream.indentation(); + findFatArrow(stream, state); + } + if (state.tokenize != tokenComment && stream.eatSpace()) return null; + var style = state.tokenize(stream, state); + if (type == "comment") return style; + state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type; + return parseJS(state, style, type, content, stream); + }, + + indent: function(state, textAfter) { + if (state.tokenize == tokenComment || state.tokenize == tokenQuasi) return CodeMirror.Pass; + if (state.tokenize != tokenBase) return 0; + var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top + // Kludge to prevent 'maybelse' from blocking lexical scope pops + if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) { + var c = state.cc[i]; + if (c == poplex) lexical = lexical.prev; + else if (c != maybeelse && c != popcontext) break; + } + while ((lexical.type == "stat" || lexical.type == "form") && + (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) && + (top == maybeoperatorComma || top == maybeoperatorNoComma) && + !/^[,\.=+\-*:?[\(]/.test(textAfter)))) + lexical = lexical.prev; + if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat") + lexical = lexical.prev; + var type = lexical.type, closing = firstChar == type; + + if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info.length + 1 : 0); + else if (type == "form" && firstChar == "{") return lexical.indented; + else if (type == "form") return lexical.indented + indentUnit; + else if (type == "stat") + return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0); + else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false) + return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit); + else if (lexical.align) return lexical.column + (closing ? 0 : 1); + else return lexical.indented + (closing ? 0 : indentUnit); + }, + + electricInput: /^\s*(?:case .*?:|default:|\{|\})$/, + blockCommentStart: jsonMode ? null : "/*", + blockCommentEnd: jsonMode ? null : "*/", + blockCommentContinue: jsonMode ? null : " * ", + lineComment: jsonMode ? null : "//", + fold: "brace", + closeBrackets: "()[]{}''\"\"``", + + helperType: jsonMode ? "json" : "javascript", + jsonldMode: jsonldMode, + jsonMode: jsonMode, + + expressionAllowed: expressionAllowed, + + skipExpression: function(state) { + parseJS(state, "atom", "atom", "true", new CodeMirror.StringStream("", 2, null)) + } + }; +}); + +CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/); + +CodeMirror.defineMIME("text/javascript", "javascript"); +CodeMirror.defineMIME("text/ecmascript", "javascript"); +CodeMirror.defineMIME("application/javascript", "javascript"); +CodeMirror.defineMIME("application/x-javascript", "javascript"); +CodeMirror.defineMIME("application/ecmascript", "javascript"); +CodeMirror.defineMIME("application/json", { name: "javascript", json: true }); +CodeMirror.defineMIME("application/x-json", { name: "javascript", json: true }); +CodeMirror.defineMIME("application/manifest+json", { name: "javascript", json: true }) +CodeMirror.defineMIME("application/ld+json", { name: "javascript", jsonld: true }); +CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true }); +CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true }); +}); + + +/* +file none +*/ +/*jslint-enable*/ diff --git a/branch-alpha/asset_font_daley_bold.woff2 b/branch-alpha/asset_font_daley_bold.woff2 new file mode 100644 index 000000000..783bdd697 Binary files /dev/null and b/branch-alpha/asset_font_daley_bold.woff2 differ diff --git a/branch-alpha/asset_image_folder_open_solid.svg b/branch-alpha/asset_image_folder_open_solid.svg new file mode 100644 index 000000000..99d403c81 --- /dev/null +++ b/branch-alpha/asset_image_folder_open_solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/branch-alpha/asset_image_github_brands.svg b/branch-alpha/asset_image_github_brands.svg new file mode 100644 index 000000000..7870c06dc --- /dev/null +++ b/branch-alpha/asset_image_github_brands.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/branch-alpha/asset_image_jslint_wrapper_vim.png b/branch-alpha/asset_image_jslint_wrapper_vim.png new file mode 100644 index 000000000..c987b46b6 Binary files /dev/null and b/branch-alpha/asset_image_jslint_wrapper_vim.png differ diff --git a/branch-alpha/asset_image_jslint_wrapper_vscode.png b/branch-alpha/asset_image_jslint_wrapper_vscode.png new file mode 100644 index 000000000..1f4297171 Binary files /dev/null and b/branch-alpha/asset_image_jslint_wrapper_vscode.png differ diff --git a/branch-alpha/asset_image_json_160.svg b/branch-alpha/asset_image_json_160.svg new file mode 100644 index 000000000..fca9b8749 --- /dev/null +++ b/branch-alpha/asset_image_json_160.svg @@ -0,0 +1,104 @@ + + + + + JSON logo + + + + + + + + + + + + diff --git a/branch-alpha/asset_image_logo_512.html b/branch-alpha/asset_image_logo_512.html new file mode 100644 index 000000000..b538e29dd --- /dev/null +++ b/branch-alpha/asset_image_logo_512.html @@ -0,0 +1,199 @@ + + + +logo + + + +
    +
    JS
    +
    Lint
    +
    + + diff --git a/branch-alpha/asset_image_logo_512.png b/branch-alpha/asset_image_logo_512.png new file mode 100644 index 000000000..19d154d68 Binary files /dev/null and b/branch-alpha/asset_image_logo_512.png differ diff --git a/branch-alpha/asset_image_logo_512.svg b/branch-alpha/asset_image_logo_512.svg new file mode 100644 index 000000000..61872c4ae --- /dev/null +++ b/branch-alpha/asset_image_logo_512.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + diff --git a/branch-alpha/index.html b/branch-alpha/index.html new file mode 100644 index 000000000..abb7cda53 --- /dev/null +++ b/branch-alpha/index.html @@ -0,0 +1,1649 @@ + + + + + + + + + JSLint: The JavaScript Code Quality and Coverage Tool + + + + + + + + + + + + +
    +
    Loading modules
    +   + + . + . + . + +
    +
    + +
    + Source + +
    +
    + + + +
    +
    + Options +
    +
    + Env... +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    + Allow... +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
      +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
      +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
      +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    +
    + + diff --git a/branch-alpha/jslint.js b/branch-alpha/jslint.js new file mode 100644 index 000000000..b486c1ce2 --- /dev/null +++ b/branch-alpha/jslint.js @@ -0,0 +1,11573 @@ +// #!/usr/bin/env node +// JSLint + +// The Unlicense +// +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + + +// jslint(source, option_dict, global_list) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze. +// option_dict An object whose keys correspond to option names. +// global_list An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// PHASE 1. Split by newlines into . +// PHASE 2. Lex into . +// PHASE 3. Parse into using the Pratt-parser. +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// PHASE 5. Check whitespace between tokens in . + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*jslint beta, node*/ +/*property + JSLINT_BETA, NODE_V8_COVERAGE, a, all, argv, arity, artifact, + assertErrorThrownAsync, assertJsonEqual, assertOrThrow, assign, async, b, + beta, bitwise, block, body, browser, c, calls, catch, catch_list, + catch_stack, causes, char, children, clear, closer, closure, code, column, + concat, consoleError, console_error, console_log, constant, context, + convert, count, coverageDir, create, cwd, d, dead, debugInline, default, + delta, devel, directive, directive_ignore_line, directive_list, directives, + dirname, disrupt, dot, edition, elem_list, ellipsis, else, end, endOffset, + endsWith, entries, env, error, eval, every, example_list, excludeList, exec, + execArgv, exit, exitCode, export_dict, exports, expression, extra, fart, + file, fileList, fileURLToPath, filter, finally, flag, floor, for, forEach, + formatted_message, free, freeze, from, froms, fsWriteFileWithParents, + fud_stmt, functionName, function_list, function_stack, functions, get, + getset, github_repo, globExclude, global, global_dict, global_list, + holeList, htmlEscape, id, identifier, import, import_list, import_meta_url, + inc, includeList, indent2, index, indexOf, init, initial, isArray, + isBlockCoverage, isHole, isNaN, is_equal, is_weird, join, jslint, + jslint_apidoc, jslint_assert, jslint_charset_ascii, jslint_cli, + jslint_edition, jslint_phase1_split, jslint_phase2_lex, jslint_phase3_parse, + jslint_phase4_walk, jslint_phase5_whitage, jslint_report, json, + jstestDescribe, jstestIt, jstestOnExit, keys, label, lbp, led_infix, length, + level, line, lineList, line_list, line_offset, line_source, lines, + linesCovered, linesTotal, live, log, long, loop, m, map, margin, match, max, + message, meta, min, mkdir, modeCoverageIgnoreFile, modeIndex, mode_cli, + mode_conditional, mode_json, mode_module, mode_noop, mode_property, + mode_shebang, mode_stop, module, moduleFsInit, moduleName, module_list, + name, names, node, nomen, noop, now, nr, nud_prefix, + objectDeepCopyWithKeysSorted, ok, on, open, opening, option, option_dict, + order, package_name, padEnd, padStart, parameters, parent, parentIi, parse, + pathname, pathnameList, platform, pop, processArgv, process_argv, + process_env, process_exit, promises, property, property_dict, push, quote, + ranges, readFile, readdir, readonly, recursive, reduce, repeat, replace, + resolve, result, reverse, role, round, scriptId, search, set, shebang, + shell, shift, signature, single, slice, some, sort, source, spawn, splice, + split, stack, stack_trace, start, startOffset, startsWith, statement, + statement_prv, stdio, stop, stop_at, stringify, subscript, switch, + syntax_dict, tenure, test, test_cause, test_internal_error, this, thru, + toLocaleString, toString, token, token_global, token_list, token_nxt, + token_tree, tokens, trace, tree, trim, trimEnd, trimRight, try, type, + unlink, unordered, unshift, url, used, v8CoverageListMerge, + v8CoverageReportCreate, value, variable, version, versions, warn, warn_at, + warning, warning_list, warnings, white, wrapped, writeFile +*/ + +// init debugInline +let debugInline = (function () { + let __consoleError = function () { + return; + }; + function debug(...argv) { + +// This function will print to stderr and then return [0]. + + __consoleError("\n\ndebugInline"); + __consoleError(...argv); + __consoleError("\n"); + return argv[0]; + } + debug(); // Coverage-hack. + __consoleError = console.error; //jslint-ignore-line + return debug; +}()); +let jslint_charset_ascii = ( + "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007" + + "\b\t\n\u000b\f\r\u000e\u000f" + + "\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017" + + "\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f" + + " !\"#$%&'()*+,-./0123456789:;<=>?" + + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + + "`abcdefghijklmnopqrstuvwxyz{|}~\u007f" +); +let jslint_edition = "v2024.11.24"; +let jslint_export; // The jslint object to be exported. +let jslint_fudge = 1; // Fudge starting line and starting + // ... column to 1. +let jslint_import_meta_url = ""; // import.meta.url used by cli. +let jslint_rgx_cap = ( + /^[A-Z]/ +); +let jslint_rgx_crlf = ( + /\n|\r\n?/ +); +let jslint_rgx_digits_bits = ( + /^[01_]*/ +); +let jslint_rgx_digits_decimals = ( + /^[0-9_]*/ +); +let jslint_rgx_digits_hexs = ( + /^[0-9A-F_]*/i +); +let jslint_rgx_digits_octals = ( + /^[0-7_]*/ +); +let jslint_rgx_directive = ( + /^(jslint|property|global)\s+(.*)$/ +); +let jslint_rgx_directive_part = ( + /([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*|$/g +); +let jslint_rgx_identifier = ( + /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/ +); +let jslint_rgx_json_number = ( + +// https://datatracker.ietf.org/doc/html/rfc7159#section-6 +// number = [ minus ] int [ frac ] [ exp ] + + /^-?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][\-+]?\d+)?$/ +); +let jslint_rgx_mega = ( + +// Vim-hack - vim-editor has trouble parsing naked '`' in regexp + + /[\u0060\\]|\$\{/ +); +let jslint_rgx_module = ( + /^[a-zA-Z0-9_$:.@\-\/]+$/ +); +let jslint_rgx_numeric_separator_illegal = ( + /__|_$|_n$/m +); +let jslint_rgx_slash_star_or_slash = ( + /\/\*|\/$/ +); +let jslint_rgx_tab = ( + /\t/g +); +let jslint_rgx_todo = ( + /\b(?:todo|TO\s?DO|HACK)\b/ +); +let jslint_rgx_token = new RegExp( + "^(" + + "(\\s+)" + + "|([a-zA-Z_$][a-zA-Z0-9_$]*)" + + "|[(){}\\[\\],:;'\"~\\`]" + + "|\\?[?.]?" + + "|=(?:==?|>)?" + + "|\\.+" + + "|\\*[*\\/=]?" + + "|\\/[*\\/]?" + + "|\\+[=+]?" + + "|-[=\\-]?" + + "|[\\^%]=?" + + "|&[&=]?" + + "|\\" + + "|[|=]?" + + "|>{1,3}=?" + + "|< throws an error. + + let err; + try { + await asyncFunc(); + } catch (errCaught) { + err = errCaught; + } + assertOrThrow(err, "No error thrown."); + assertOrThrow( + !regexp || new RegExp(regexp).test(err.message), + err + ); +} + +function assertJsonEqual(aa, bb, message) { + +// This function will assert JSON.stringify() === JSON.stringify(). + + aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa), undefined, 1); + bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb), undefined, 1); + if (aa !== bb) { + throw new Error( + "\n" + aa + "\n!==\n" + bb + + ( + typeof message === "string" + ? " - " + message + : message + ? " - " + JSON.stringify(message) + : "" + ) + ); + } +} + +function assertOrThrow(condition, message) { + +// This function will throw if is falsy. + + if (!condition) { + throw ( + (!message || typeof message === "string") + ? new Error(String(message).slice(0, 2048)) + : message + ); + } +} + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than '{}' because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +async function fsWriteFileWithParents(pathname, data) { + +// This function will write to and lazy-mkdirp if necessary. + + await moduleFsInit(); + +// Try writing to pathname. + + try { + await moduleFs.promises.writeFile(pathname, data); + } catch (ignore) { + +// Lazy mkdirp. + + await moduleFs.promises.mkdir(modulePath.dirname(pathname), { + recursive: true + }); + +// Retry writing to pathname. + + await moduleFs.promises.writeFile(pathname, data); + } + console.error("wrote file " + pathname); +} + +function globExclude({ + excludeList = [], + includeList = [], + pathnameList = [] +}) { + +// This function will +// 1. Exclude pathnames in that don't match glob-patterns in +// . +// 2. Exclude pathnames in that match glob-patterns in +// . + + function globAssertNotWeird(list, name) { + +// This function will check if of strings contain weird characters. + + [ + [ + "\n", ( + /^.*?([\u0000-\u0007\r]).*/gm + ) + ], + [ + "\r", ( + /^.*?([\n]).*/gm + ) + ] + ].forEach(function ([ + separator, rgx + ]) { + list.join(separator).replace(rgx, function (match0, char) { + throw new Error( + "Weird character " + + JSON.stringify(char) + + " found in " + name + " " + + JSON.stringify(match0) + ); + }); + }); + } + + function globToRegexp(pattern) { + +// This function will translate glob to javascript-regexp, +// which javascript can then use to "glob" pathnames. + + let ii = 0; + let isClass = false; + let strClass = ""; + let strRegex = ""; + pattern = pattern.replace(( + /\/\/+/g + ), "/"); + pattern = pattern.replace(( + /\*\*\*+/g + ), "**"); + pattern.replace(( + /\\\\|\\\[|\\\]|\[|\]|./g + ), function (match0) { + switch (match0) { + case "[": + if (isClass) { + strClass += "["; + return; + } + strClass += "\u0000"; + strRegex += "\u0000"; + isClass = true; + return; + case "]": + if (isClass) { + isClass = false; + return; + } + strRegex += "]"; + return; + default: + if (isClass) { + strClass += match0; + return; + } + strRegex += match0; + } + return ""; + }); + strClass += "\u0000"; + +// An expression "[!...]" matches a single character, namely any character that +// is not matched by the expression obtained by removing the first '!' from it. +// (Thus, "[!a-]" matches any single character except 'a', and '-'.) + + strClass = strClass.replace(( + /\u0000!/g + ), "\u0000^"); + +// One may include '-' in its literal meaning by making it the first or last +// character between the brackets. + + strClass = strClass.replace(( + /\u0000-/g + ), "\u0000\\-"); + strClass = strClass.replace(( + /-\u0000/g + ), "\\-\u0000"); + +// Escape brackets '[', ']' in character class. + + strClass = strClass.replace(( + /[\[\]]/g + ), "\\$&"); + +// https://stackoverflow.com/questions/3561493 +// /is-there-a-regexp-escape-function-in-javascript +// $()*+-./?[\]^{|} + + strRegex = strRegex.replace(( + +// Ignore [-/]. + + /[$()*+.?\[\\\]\^{|}]/g + ), "\\$&"); + +// Expand wildcard '**/*'. + + strRegex = strRegex.replace(( + /\\\*\\\*\/(?:\\\*)+/g + ), ".*?"); + +// Expand wildcard '**'. + + strRegex = strRegex.replace(( + /(^|\/)\\\*\\\*(\/|$)/gm + ), "$1.*?$2"); + +// Expand wildcard '*'. + + strRegex = strRegex.replace(( + /(?:\\\*)+/g + ), "[^\\/]*?"); + +// Expand wildcard '?'. + + strRegex = strRegex.replace(( + /\\\?/g + ), "[^\\/]"); + +// Expand directory-with-trailing-slash '.../'. + + strRegex = strRegex.replace(( + /\/$/gm + ), "\\/.*?"); + +// Merge strClass into strRegex. + + ii = 0; + strClass = strClass.split("\u0000"); + strRegex = strRegex.replace(( + /\u0000/g + ), function () { + ii += 1; + if (strClass[ii] === "") { + return ""; + } + return "[" + strClass[ii] + "]"; + }); + +// Change strRegex from string to regexp. + + strRegex = new RegExp("^" + strRegex + "$", "gm"); + return strRegex; + } + +// Validate excludeList, includeList, pathnameList. + + globAssertNotWeird(excludeList, "pattern"); + globAssertNotWeird(includeList, "pattern"); + globAssertNotWeird(pathnameList, "pathname"); + +// Optimization +// Concat pathnames into a single, newline-separated string, +// whose pathnames can all be filtered with a single, regexp-pass. + + pathnameList = pathnameList.join("\n"); + +// 1. Exclude pathnames in that don't match glob-patterns in +// . + + if (includeList.length > 0) { + includeList = includeList.map(globToRegexp); + includeList.forEach(function (pattern) { + pathnameList = pathnameList.replace(pattern, "\u0000$&"); + }); + pathnameList = pathnameList.replace(( + /^[^\u0000].*/gm + ), ""); + pathnameList = pathnameList.replace(( + /^\u0000+/gm + ), ""); + } + +// 2. Exclude pathnames in that match glob-patterns in +// . + + excludeList = excludeList.map(globToRegexp); + excludeList.forEach(function (pattern) { + pathnameList = pathnameList.replace(pattern, ""); + }); + +// Split newline-separated pathnames back to list. + + pathnameList = pathnameList.split("\n").filter(function (elem) { + return elem; + }); + return { + excludeList, + includeList, + pathnameList + }; +} + +function htmlEscape(str) { + +// This function will make html-safe by escaping & < >. + + return String(str).replace(( + /&/g + ), "&").replace(( + //g + ), ">"); +} + +function jslint( + source = "", // A text to analyze. + option_dict = empty(), // An object whose keys correspond to option + // ... names. + global_list = [] // An array of strings containing global + // ... variables that the file is allowed + // ... readonly access. +) { + +// The jslint function itself. + + let catch_list = []; // The array containing all catch-blocks. + let catch_stack = [ // The stack of catch-blocks. + { + context: empty() + } + ]; + let cause_dict = empty(); // The object of test-causes. + let directive_list = []; // The directive comments. + let export_dict = empty(); // The exported names and values. + let function_list = []; // The array containing all functions. + let function_stack = []; // The stack of functions. + let global_dict = empty(); // The object containing the global + // ... declarations. + let import_list = []; // The array collecting all import-from strings. + let line_list = String( // The array containing source lines. + "\n" + source + ).split(jslint_rgx_crlf).map(function (line_source) { + return { + line_source + }; + }); + let mode_stop = false; // true if JSLint cannot finish. + let property_dict = empty(); // The object containing the tallied + // ... property names. + let state = empty(); // jslint state-object to be passed between + // jslint functions. + let syntax_dict = empty(); // The object containing the parser. + let tenure = empty(); // The predefined property registry. + let token_global = { // The global object; the outermost context. + async: 0, + body: true, + context: empty(), + finally: 0, + from: 0, + id: "(global)", + level: 0, + line: jslint_fudge, + live: [], + loop: 0, + switch: 0, + thru: 0, + try: 0 + }; + let token_list = []; // The array of tokens. + let warning_list = []; // The array collecting all generated warnings. + +// Error reportage functions: + + function artifact(the_token) { + +// Return a string representing an artifact. + + the_token = the_token || state.token_nxt; + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); + } + + function is_equal(aa, bb) { + +// test_cause: +// ["0&&0", "is_equal", "", "", 0] + + test_cause(""); + +// Probably deadcode. +// if (aa === bb) { +// return true; +// } + + jslint_assert(!(aa === bb), `Expected !(aa === bb).`); + if (Array.isArray(aa)) { + return ( + Array.isArray(bb) + && aa.length === bb.length + && aa.every(function (value, index) { + +// test_cause: +// ["`${0}`&&`${0}`", "is_equal", "recurse_isArray", "", 0] +// ["`${0}`&&`${1}`", "is_equal", "recurse_isArray", "", 0] + + test_cause("recurse_isArray"); + return is_equal(value, bb[index]); + }) + ); + } + +// Probably deadcode. +// if (Array.isArray(bb)) { +// return false; +// } + + jslint_assert(!Array.isArray(bb), `Expected !Array.isArray(bb).`); + switch (aa.id === bb.id && aa.id) { + case "(number)": + case "(string)": + return aa.value === bb.value; + +// PR-394 - Bugfix +// Fix jslint falsely believing megastring literals `0` and `1` are similar. + + case "`": + if (!is_equal(aa.value, bb.value)) { + return false; + } + break; + } + if (is_weird(aa) || is_weird(bb)) { + +// test_cause: +// ["aa(/./)||{}", "is_equal", "false", "", 0] + + test_cause("false"); + return false; + } + if (aa.arity === bb.arity && aa.id === bb.id) { + if (aa.id === "." || aa.id === "?.") { + +// test_cause: +// ["aa.bb&&aa.bb", "is_equal", "recurse_arity_id", "", 0] +// ["aa?.bb&&aa?.bb", "is_equal", "recurse_arity_id", "", 0] + + test_cause("recurse_arity_id"); + return ( + is_equal(aa.expression, bb.expression) + && is_equal(aa.name, bb.name) + ); + } + if (aa.arity === "unary") { + +// test_cause: +// ["+0&&+0", "is_equal", "recurse_unary", "", 0] + + test_cause("recurse_unary"); + return is_equal(aa.expression, bb.expression); + } + if (aa.arity === "binary") { + +// test_cause: +// ["aa[0]&&aa[0]", "is_equal", "recurse_binary", "", 0] + + test_cause("recurse_binary"); + return ( + aa.id !== "(" + && is_equal(aa.expression[0], bb.expression[0]) + && is_equal(aa.expression[1], bb.expression[1]) + ); + } + if (aa.arity === "ternary") { + +// test_cause: +// ["aa=(``?``:``)&&(``?``:``)", "is_equal", "recurse_ternary", "", 0] + + test_cause("recurse_ternary"); + return ( + is_equal(aa.expression[0], bb.expression[0]) + && is_equal(aa.expression[1], bb.expression[1]) + && is_equal(aa.expression[2], bb.expression[2]) + ); + } + +// Probably deadcode. +// if (aa.arity === "function" || aa.arity === "regexp") { +// return false; +// } + + jslint_assert( + !(aa.arity === "function" || aa.arity === "regexp"), + `Expected !(aa.arity === "function" || aa.arity === "regexp").` + ); + +// test_cause: +// ["undefined&&undefined", "is_equal", "true", "", 0] + + test_cause("true"); + return true; + } + +// test_cause: +// ["null&&undefined", "is_equal", "false", "", 0] + + test_cause("false"); + return false; + } + + function is_weird(thing) { + switch (thing.id) { + case "(regexp)": + return true; + case "=>": + return true; + case "[": + return thing.arity === "unary"; + case "function": + return true; + case "{": + return true; + default: + return false; + } + } + + function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + the_token = the_token || state.token_nxt; + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); + } + + function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); + } + + function test_cause(code, aa, column) { + +// This function will instrument to for test-purposes. + + if (option_dict.test_cause) { + cause_dict[JSON.stringify([ + String(new Error().stack).replace(( + /^ at (?:file|stop|stop_at|test_cause|warn|warn_at)\b.*?\n/gm + ), "").match( + /\n at ((?:Object\.\w+?_)?\w+?) / + )[1].replace(( + /^Object\./ + ), ""), + code, + String( + (aa === undefined || aa === token_global) + ? "" + : aa + ), + column || 0 + ])] = true; + } + } + + function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + let the_warning; + the_token = the_token || state.token_nxt; + the_warning = warn_at( + code, + the_token.line, + (the_token.from || 0) + jslint_fudge, + a || artifact(the_token), + b, + c, + d + ); + +// Issue #408 +// Warnings that should be ignored sometimes suppress legitimate warnings. + + if (the_warning.directive_ignore_line) { + return the_warning; + } + +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token.warning) { + warning_list.pop(); + return the_warning; + } + the_token.warning = the_warning; + return the_warning; + } + + function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + let mm; + let warning = Object.assign(empty(), { + a, + b, + c, + code, + +// Fudge column numbers in warning message. + + column: column || jslint_fudge, + d, + line, + line_source: "", + name: "JSLintError" + }, line_list[line]); + warning.column = Math.max( + Math.min(warning.column, warning.line_source.length), + jslint_fudge + ); + test_cause(code, b || a, warning.column); + switch (code) { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + case "and": + mm = `The '&&' subexpression should be wrapped in parens.`; + break; + case "bad_assignment_a": + mm = `Bad assignment to '${a}'.`; + break; + case "bad_directive_a": + mm = `Bad directive '${a}'.`; + break; + case "bad_get": + mm = `A get function takes no parameters.`; + break; + case "bad_module_name_a": + mm = `Bad module name '${a}'.`; + break; + case "bad_option_a": + mm = `Bad option '${a}'.`; + break; + case "bad_set": + mm = `A set function takes one parameter.`; + break; + case "duplicate_a": + mm = `Duplicate '${a}'.`; + break; + case "empty_block": + mm = `Empty block.`; + break; + case "expected_a": + mm = `Expected '${a}'.`; + break; + case "expected_a_at_b_c": + mm = `Expected '${a}' at column ${b}, not column ${c}.`; + break; + case "expected_a_b": + mm = `Expected '${a}' and instead saw '${b}'.`; + break; + case "expected_a_b_before_c_d": + mm = `Expected ${a} '${b}' to be ordered before ${c} '${d}'.`; + break; + case "expected_a_b_from_c_d": + mm = ( + `Expected '${a}' to match '${b}' from line ${c}` + + ` and instead saw '${d}'.` + ); + break; + case "expected_a_before_b": + mm = `Expected '${a}' before '${b}'.`; + break; + case "expected_digits_after_a": + mm = `Expected digits after '${a}'.`; + break; + case "expected_four_digits": + mm = `Expected four digits after '\\u'.`; + break; + case "expected_identifier_a": + mm = `Expected an identifier and instead saw '${a}'.`; + break; + case "expected_line_break_a_b": + mm = `Expected a line break between '${a}' and '${b}'.`; + break; + case "expected_regexp_factor_a": + mm = `Expected a regexp factor and instead saw '${a}'.`; + break; + case "expected_space_a_b": + mm = `Expected one space between '${a}' and '${b}'.`; + break; + case "expected_statements_a": + mm = `Expected statements before '${a}'.`; + break; + case "expected_string_a": + mm = `Expected a string and instead saw '${a}'.`; + break; + case "expected_type_string_a": + mm = `Expected a type string and instead saw '${a}'.`; + break; + case "freeze_exports": + mm = ( + `Expected 'Object.freeze('. All export values should be frozen.` + ); + break; + +// PR-378 - Relax warning "function_in_loop". +// +// case "function_in_loop": +// mm = `Don't create functions within a loop.`; +// break; + +// PR-390 - Add numeric-separator check. + + case "illegal_num_separator": + mm = `Illegal numeric separator '_' at column ${column}.`; + break; + case "infix_in": + mm = ( + `Unexpected 'in'. Compare with undefined,` + + ` or use the hasOwnProperty method instead.` + ); + break; + case "label_a": + mm = `'${a}' is a statement label.`; + break; + case "misplaced_a": + mm = `Place '${a}' at the outermost level.`; + break; + case "misplaced_directive_a": + mm = `Place the '/*${a}*/' directive before the first statement.`; + break; + case "missing_await_statement": + mm = `Expected await statement in async function.`; + break; + +// PR-347 - Disable warning "missing_browser". +// +// case "missing_browser": +// mm = `/*global*/ requires the Assume a browser option.`; +// break; + + case "missing_m": + mm = `Expected 'm' flag on a multiline regular expression.`; + break; + case "naked_block": + mm = `Naked block.`; + break; + case "nested_comment": + mm = `Nested comment.`; + break; + case "not_label_a": + mm = `'${a}' is not a label.`; + break; + case "number_isNaN": + mm = `Use Number.isNaN function to compare with NaN.`; + break; + case "out_of_scope_a": + mm = `'${a}' is out of scope.`; + break; + case "redefinition_a_b": + mm = `Redefinition of '${a}' from line ${b}.`; + break; + case "redefinition_global_a_b": + mm = `Redefinition of global ${a} variable '${b}'.`; + break; + case "required_a_optional_b": + mm = `Required parameter '${a}' after optional parameter '${b}'.`; + break; + case "reserved_a": + mm = `Reserved name '${a}'.`; + break; + case "subscript_a": + mm = `['${a}'] is better written in dot notation.`; + break; + case "todo_comment": + mm = `Unexpected TODO comment.`; + break; + case "too_long": + mm = `Line is longer than 80 characters.`; + break; + case "too_many_digits": + mm = `Too many digits.`; + break; + case "unclosed_comment": + mm = `Unclosed comment.`; + break; + case "unclosed_disable": + mm = ( + `Directive '/*jslint-disable*/' was not closed` + + ` with '/*jslint-enable*/'.` + ); + break; + case "unclosed_mega": + mm = `Unclosed mega literal.`; + break; + case "unclosed_string": + mm = `Unclosed string.`; + break; + case "undeclared_a": + mm = `Undeclared '${a}'.`; + break; + case "unexpected_a": + mm = `Unexpected '${a}'.`; + break; + case "unexpected_a_after_b": + mm = `Unexpected '${a}' after '${b}'.`; + break; + case "unexpected_a_before_b": + mm = `Unexpected '${a}' before '${b}'.`; + break; + case "unexpected_at_top_level_a": + mm = `Expected '${a}' to be in a function.`; + break; + case "unexpected_char_a": + mm = `Unexpected character '${a}'.`; + break; + case "unexpected_comment": + mm = `Unexpected comment.`; + break; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// case "unexpected_directive_a": +// mm = `When using modules, don't use directive '/\u002a${a}'.`; +// break; + + case "unexpected_expression_a": + mm = `Unexpected expression '${a}' in statement position.`; + break; + case "unexpected_label_a": + mm = `Unexpected label '${a}'.`; + break; + case "unexpected_parens": + mm = `Don't wrap function literals in parens.`; + break; + case "unexpected_space_a_b": + mm = `Unexpected space between '${a}' and '${b}'.`; + break; + case "unexpected_statement_a": + mm = `Unexpected statement '${a}' in expression position.`; + break; + case "unexpected_trailing_space": + mm = `Unexpected trailing space.`; + break; + case "unexpected_typeof_a": + mm = ( + `Unexpected 'typeof'. Use '===' to compare directly with ${a}.` + ); + break; + case "uninitialized_a": + mm = `Uninitialized '${a}'.`; + break; + case "unopened_enable": + mm = ( + `Directive '/*jslint-enable*/' was not opened` + + ` with '/*jslint-disable*/'.` + ); + break; + case "unreachable_a": + mm = `Unreachable '${a}'.`; + break; + case "unregistered_property_a": + mm = `Unregistered property name '${a}'.`; + break; + case "unused_a": + mm = `Unused '${a}'.`; + break; + case "use_double": + mm = `Use double quotes, not single quotes.`; + break; + +// PR-386 - Fix issue #382 - Make fart-related warnings more readable. + + case "use_function_not_fart": + mm = ( + `Use 'function (...)', not '(...) =>' when arrow functions` + + ` become too complex.` + ); + break; + case "use_open": + mm = ( + `Wrap a ternary expression in parens,` + + ` with a line break after the left paren.` + ); + break; + case "use_spaces": + mm = `Use spaces, not tabs.`; + break; + case "var_on_top": + mm = `Move variable declaration to top of function or script.`; + break; + case "var_switch": + mm = `Don't declare variables in a switch.`; + break; + case "weird_condition_a": + mm = `Weird condition '${a}'.`; + break; + case "weird_expression_a": + mm = `Weird expression '${a}'.`; + break; + case "weird_loop": + mm = `Weird loop.`; + break; + case "weird_property_a": + mm = `Weird property name '${a}'.`; + break; + case "weird_relation_a": + mm = `Weird relation '${a}'.`; + break; + case "wrap_condition": + mm = `Wrap the condition in parens.`; + break; + +// PR-386 - Fix issue #382 - Make fart-related warnings more readable. + + case "wrap_fart_parameter": + mm = `Wrap the parameter before '=>' in parens.`; + break; + case "wrap_immediate": + mm = ( + `Wrap an immediate function invocation in parentheses to assist` + + ` the reader in understanding that the expression is the` + + ` result of a function, and not the function itself.` + ); + break; + case "wrap_regexp": + mm = `Wrap this regexp in parens to avoid confusion.`; + break; + case "wrap_unary": + mm = `Wrap the unary expression in parens.`; + break; + } + +// Validate mm. + + jslint_assert(mm, code); + warning.message = mm; + +// PR-242 - Include stack_trace for jslint to debug itself for errors. + + if (option_dict.trace) { + warning.stack_trace = new Error().stack; + } + if (warning.directive_ignore_line) { + +// test_cause: +// ["0 //jslint-ignore-line", "semicolon", "directive_ignore_line", "", 0] + + test_cause("directive_ignore_line"); + return warning; + } + warning_list.push(warning); + return warning; + } + + try { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + + option_dict = Object.assign(empty(), option_dict); + Object.assign(state, { + artifact, + catch_list, + catch_stack, + directive_list, + export_dict, + function_list, + function_stack, + global_dict, + global_list, + import_list, + is_equal, + is_weird, + line_list, + mode_json: false, // true if parsing JSON. + mode_module: false, // true if import or export was used. + mode_property: false, // true if directive /*property*/ is + // ... used. + mode_shebang: false, // true if #! is seen on the first line. + option_dict, + property_dict, + source, + stop, + stop_at, + syntax_dict, + tenure, + test_cause, + token_global, + token_list, + token_nxt: token_global, + warn, + warn_at, + warning_list + }); + +// PHASE 1. Split by newlines into . + + jslint_phase1_split(state); + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 2. Lex into . + + jslint_phase2_lex(state); + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 3. Parse into using the Pratt-parser. + + jslint_phase3_parse(state); + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). + + if (!state.mode_json) { + jslint_phase4_walk(state); + } + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 5. Check whitespace between tokens in . + + if (!state.mode_json && warning_list.length === 0) { + jslint_phase5_whitage(state); + } + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PR-347 - Disable warning "missing_browser". +// +// if (!option_dict.browser) { +// directive_list.forEach(function (comment) { +// if (comment.directive === "global") { +// +// // test_cause: +// // ["/*global aa*/", "jslint", "missing_browser", "(comment)", 1] +// +// warn("missing_browser", comment); +// } +// }); +// } + + if (option_dict.test_internal_error) { + jslint_assert(undefined, "test_internal_error"); + } + } catch (err) { + mode_stop = true; + err.message = "[JSLint was unable to finish] " + err.message; + err.mode_stop = true; + if (err.name !== "JSLintError") { + Object.assign(err, { + column: jslint_fudge, + line: jslint_fudge, + line_source: "", + stack_trace: err.stack + }); + } + if (warning_list.indexOf(err) === -1) { + warning_list.push(err); + } + } + +// Sort warning_list by mode_stop first, line, column respectively. + + warning_list.sort(function (aa, bb) { + return ( + Boolean(bb.mode_stop) - Boolean(aa.mode_stop) + || aa.line - bb.line + || aa.column - bb.column + ); + +// Update each warning with formatted_message ready-for-use by jslint_cli. + + }).map(function ({ + column, + line, + line_source, + message, + stack_trace = "" + }, ii, list) { + list[ii].formatted_message = String( + String(ii + 1).padStart(2, " ") + + ". \u001b[31m" + message + "\u001b[39m" + + " \u001b[90m\/\/ line " + line + ", column " + column + + "\u001b[39m\n" + + (" " + line_source.trim()).slice(0, 72) + "\n" + + stack_trace + ).trimRight(); + }); + + return { + causes: cause_dict, + directives: directive_list, + edition: jslint_edition, + exports: export_dict, + froms: import_list, + functions: function_list, + global: token_global, + id: "(JSLint)", + json: state.mode_json, + lines: line_list, + module: state.mode_module === true, + ok: warning_list.length === 0 && !mode_stop, + option: option_dict, + property: property_dict, + shebang: ( + state.mode_shebang + ? line_list[jslint_fudge].line_source + : undefined + ), + stop: mode_stop, + tokens: token_list, + tree: state.token_tree, + warnings: warning_list + }; +} + +// PR-362 - Add API Doc. + +async function jslint_apidoc({ + example_list, + github_repo, + module_list, + package_name, + pathname, + version +}) { + +// This function will create API Doc from . + + let elem_ii = 0; + let html; + + function elem_create(moduleObj, key, moduleName) { + +// This function will create a sub API Doc from elem []. + + let example = "N/A"; + let id = encodeURIComponent("apidoc.elem." + moduleName + "." + key); + let name; + let signature; + let source; + name = htmlEscape((typeof moduleObj[key]) + " " + key); + if (typeof moduleObj[key] !== "function") { + return { + name, + signature: (` + +${name} + + `), + source: (` +
  • +

    + + ${name} + +

    +
  • + `) + }; + } + // init source + source = htmlEscape(trim_start(moduleObj[key].toString())); + // init signature + source = source.replace(( + /(\([\S\s]*?\)) \{/ + ), function (match0, match1) { + signature = htmlEscape( + match1.replace(( + / *?\/\*[\S\s]*?\*\/ */g + ), "").replace(( + / *?\/\/.*/g + ), "").replace(( + /\n{2,}/g + ), "\n") + ); + return match0; + }); + // init comment + source = source.replace(( + /\n(?:\/\/.*?\n)+\n/ + ), "$&"); + // init example + example_list.some(function (example2) { + example2.replace( + new RegExp(( + "((?:\\n.*?){8}(function )?)\\b" + + key + + "(\\((?:.*?\\n){8})" + ), "g"), + function (ignore, header, isDeclaration, footer) { + if (!isDeclaration) { + example = "..." + trim_start( + htmlEscape(header) + + "" + + htmlEscape(key) + + "" + + htmlEscape(footer) + ).trimEnd() + "\n..."; + } + return ""; + } + ); + return example !== "N/A"; + }); + if (source.length > 2048) { + source = source.slice(0, 2048) + "...\n}\n"; + } + return { + name, + signature: (` + +${name}${signature} + + `), + source: (` +
  • +

    + + ${name}${signature} + +

    +
  • +
  • Description and source-code:
    ${source}
  • +
  • Example usage:
    ${example}
  • + `) + }; + } + + function trim_start(str) { + +// This function will normalize whitespace before . + + let whitespace = ""; + str.trim().replace(( + /^ */gm + ), function (match0) { + if (whitespace === "" || match0.length < whitespace.length) { + whitespace = match0; + } + return ""; + }); + str = str.replace(new RegExp("^" + whitespace, "gm"), ""); + return str; + } + await moduleFsInit(); + +// Html-escape params. + + github_repo = htmlEscape(github_repo); + package_name = htmlEscape(package_name); + version = htmlEscape(version); + +// Init example_list. + + example_list = await Promise.all(example_list.map(async function (file) { + +// This function will read example from given file. + + let result = await moduleFs.promises.readFile(file, "utf8"); + result = ( + "\n\n\n\n\n\n\n\n" + // bug-workaround - truncate example to manageable size + + result.slice(0, 524288) + + "\n\n\n\n\n\n\n\n" + ); + result = result.replace(( + /\r\n*/g + ), "\n"); + return result; + })); + +// Init module_list. + + module_list = await Promise.all(module_list.map(async function ({ + pathname + }) { + let moduleName = htmlEscape(JSON.stringify(pathname)); + let moduleObj = await import(pathname); + if (moduleObj.default) { + moduleObj = moduleObj.default; + } + return { + elem_list: Object.keys(moduleObj).map(function (key) { + return elem_create(moduleObj, key, moduleName); + }).sort(function (aa, bb) { + return ( + aa.name < bb.name + ? -1 + : 1 + ); + }).map(function (elem) { + elem_ii += 1; + elem.signature = elem.signature.replace( + ">", + ">" + elem_ii + ". " + ); + elem.source = elem.source.replace( + "\">", + "\">" + elem_ii + ". " + ); + return elem; + }), + id: encodeURIComponent("apidoc.module." + moduleName), + moduleName + }; + })); + html = (` + + + + + + +${package_name} apidoc + + + +
    +

    API Doc for ${package_name} (${version})

    +
    + +

    Table of Contents

    +
    +
      + `) + module_list.map(function ({ + elem_list, + id, + moduleName + }) { + return ( + (` +
    • + Module ${moduleName} +
        + `) + + elem_list.map(function ({ + signature + }) { + return "
      • \n" + signature + "\n
      • \n"; + }).join("") + + (` +
      +
    • + `) + ); + }).join("") + (` +
    +
    + `) + module_list.map(function ({ + elem_list, + id, + moduleName + }) { + return ( + (` +
    +

    Module ${moduleName}

    +
      + `) + + elem_list.map(function ({ + source + }) { + return source; + }).join("") + + (` +
    +
    + `) + ); + }).join("") + (` +
    + [ + This document was created with + JSLint + ] +
    +
    + + + `); + html = html.trim().replace(( + / +?$/gm + ), "") + "\n"; + await fsWriteFileWithParents(pathname, html); +} + +function jslint_assert(condition, message) { + +// This function will throw if is falsy. + + if (condition) { + return condition; + } + throw new Error( + `This was caused by a bug in JSLint. +Please open an issue with this stack-trace (and possible example-code) at +https://github.com/jslint-org/jslint/issues. +edition = "${jslint_edition}"; +${String(message).slice(0, 2000)}` + ); +} + +async function jslint_cli({ + console_error, + console_log, + file, + import_meta_url, + mode_cli, + mode_noop, + option, + process_argv, + process_env, + process_exit, + source +}) { + +// This function will run jslint from nodejs-cli. + + let command; + let data; + let exit_code = 0; + let mode_report; + let mode_wrapper_vim; + let result; + + function jslint_from_file({ + code, + file, + line_offset = 0, + mode_conditional, + option = empty() + }) { + let result_from_file; + if ( + mode_conditional + && !( + /^\/\*jslint\b/m + ).test(code.slice(0, 65536)) + ) { + return; + } + option = Object.assign(empty(), option, { + file + }); + switch (( + /\.\w+?$|$/m + ).exec(file)[0]) { + case ".html": + +// Recursively jslint embedded "". + + code.replace(( + /^]*?>\n([\S\s]*?\n)<\/script>$/gm + ), function (ignore, match1, ii) { + jslint_from_file({ + code: match1, + file: file + ". + + import_meta_url = import_meta_url || jslint_import_meta_url; + if ( + jslint_rgx_url_search_window_jslint.test(import_meta_url) + && (typeof globalThis === "object" && globalThis) + ) { + globalThis.jslint = jslint; + } + +// Feature-detect nodejs. + + if (!( + (typeof process === "object" && process) + && process.versions + && typeof process.versions.node === "string" + && !mode_noop + )) { + return exit_code; + } + console_error = console_error || console.error; + console_log = console_log || console.log; + process_argv = process_argv || process.argv; + process_env = process_env || process.env; + process_exit = process_exit || process.exit; + await moduleFsInit(); + if ( + !( + +// Feature-detect nodejs-cli. + + process.execArgv.indexOf("--eval") === -1 + && process.execArgv.indexOf("-e") === -1 + && ( + ( + /[\/|\\]jslint(?:\.[cm]?js)?$/m + ).test(process_argv[1]) + || mode_cli + ) + && ( + moduleUrl.fileURLToPath(import_meta_url) + === + modulePath.resolve(process_argv[1]) + ) + ) + && !mode_cli + ) { + return exit_code; + } + +// init commmand + + command = String(process_argv[2]).split("="); + command[1] = command.slice(1).join("="); + + switch (command[0]) { + +// PR-362 - Add API Doc. + + case "jslint_apidoc": + await jslint_apidoc(Object.assign(JSON.parse(process_argv[3]), { + pathname: command[1] + })); + return; + +// PR-363 - Add command jslint_report. + + case "jslint_report": + mode_report = command[1]; + process_argv = process_argv.slice(1); + break; + +// COMMIT-b26d6df2 - Add command jslint_wrapper_vim. + + case "jslint_wrapper_vim": + mode_wrapper_vim = true; + process_argv = process_argv.slice(1); + break; + +// PR-364 - Add command v8_coverage_report. + + case "v8_coverage_report": + await v8CoverageReportCreate({ + consoleError: console_error, + coverageDir: command[1], + processArgv: process_argv.slice(3) + }); + return; + } + +// Normalize file relative to process.cwd(). + + process_argv.slice(2).some(function (arg) { + if (!arg.startsWith("-")) { + file = file || arg; + return true; + } + }); + if (!file) { + return; + } + file = modulePath.resolve(file) + "/"; + if (file.startsWith(process.cwd() + "/")) { + file = file.replace(process.cwd() + "/", "").slice(0, -1) || "."; + } + file = file.replace(( + /\\/g + ), "/").replace(( + /\/$/g + ), ""); + if (source) { + data = source; + } else { + +// jslint_cli - jslint directory. + + try { + data = await moduleFs.promises.readdir(file, "utf8"); + } catch (ignore) {} + if (data) { + await Promise.all(data.map(async function (file2) { + let code; + let time_start = Date.now(); + file2 = file + "/" + file2; + switch (( + /\.\w+?$|$/m + ).exec(file2)[0]) { + case ".cjs": + case ".html": + case ".js": + case ".json": + case ".md": + case ".mjs": + case ".sh": + break; + default: + return; + } + try { + code = await moduleFs.promises.readFile(file2, "utf8"); + } catch (ignore) { + return; + } + if ( + ( + /(?:\b|_)(?:lock|min|raw|rollup)(?:\b|_)/ + ).test(file2) + || !(code && code.length < 1048576) + ) { + return; + } + jslint_from_file({ + code, + file: file2, + option + }); + console_error( + "jslint - " + (Date.now() - time_start) + "ms - " + file2 + ); + })); + process_exit(exit_code); + return exit_code; + } + +// jslint_cli - jslint file. + + try { + data = await moduleFs.promises.readFile(file, "utf8"); + } catch (err) { + console_error(err); + exit_code = 1; + process_exit(exit_code); + return exit_code; + } + } + result = jslint_from_file({ + code: data, + file, + option + }); + if (mode_report) { + result = jslint.jslint_report(result); + result = `\n${result}\n`; + await fsWriteFileWithParents(mode_report, result); + } + process_exit(exit_code); + return exit_code; +} + +function jslint_phase1_split() { + +// PHASE 1. Split by newlines into . + + return; +} + +function jslint_phase2_lex(state) { + +// PHASE 2. Lex into . + + let { + artifact, + directive_list, + global_dict, + global_list, + line_list, + option_dict, + stop, + stop_at, + tenure, + test_cause, + token_global, + token_list, + warn, + warn_at + } = state; + let char; // The current character being lexed. + let column = 0; // The column number of the next character. + let from; // The starting column number of the token. + let from_mega; // The starting column of megastring. + let line = 0; // The line number of the next character. + let line_disable; // The starting line of "/*jslint-disable*/". + let line_mega; // The starting line of megastring. + let line_source = ""; // The remaining line source string. + let line_whole = ""; // The whole line source string. + let mode_digits_empty_string = 1; + let mode_digits_numeric_separator = 2; + let mode_directive = true; // true if directives are still allowed. + let mode_mega = false; // true if currently parsing a megastring + // ... literal. + let mode_regexp; // true if regular expression literal seen on + // ... this line. + let paren_backtrack_list = []; // List of most recent "(" tokens at any + // ... paren-depth. + let paren_depth = 0; // Keeps track of current paren-depth. + let snippet = ""; // A piece of string. + let token_1; // The first token. + let token_prv = token_global; // The previous token including + // ... comments. + let token_prv_expr = token_global; // The previous token excluding + // ... comments. + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions char_after, char_before, +// read_digits, and char_after_escape help in the parsing of literals. + + function char_after(match) { + +// Get the next character from the source line. Remove it from the line_source, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + +// test_cause: +// ["aa=/[", "char_after", "expected_a", "]", 5] +// ["aa=/aa{/", "char_after", "expected_a_b", "/", 8] + + return ( + char === "" + ? stop_at("expected_a", line, column - 1, match) + : stop_at("expected_a_b", line, column, match, char) + ); + } + char = line_source.slice(0, 1); + line_source = line_source.slice(1); + snippet += char || " "; + column += 1; + return char; + } + + function char_after_escape(extra) { + +// Validate char after escape "\\". + + char_after("\\"); + switch (char) { + case "": + +// test_cause: +// ["\"\\", "char_after_escape", "unclosed_string", "", 2] + + return stop_at("unclosed_string", line, column); + case "/": + return char_after(); + case "\\": + return char_after(); + case "`": + return char_after(); + case "b": + return char_after(); + case "f": + return char_after(); + case "n": + return char_after(); + case "r": + return char_after(); + case "t": + +// test_cause: +// ["\"\\/\\\\\\`\\b\\f\\n\\r\\t\"", "char_after_escape", "char_after", "", 0] + + test_cause("char_after"); + return char_after(); + case "u": + if (char_after("u") === "{") { + if (state.mode_json) { + +// test_cause: +// ["[\"\\u{12345}\"]", "char_after_escape", "unexpected_a", "{", 5] + + warn_at("unexpected_a", line, column, char); + } + if (read_digits("x", undefined) > 5) { + +// test_cause: +// ["\"\\u{123456}\"", "char_after_escape", "too_many_digits", "", 11] + + warn_at("too_many_digits", line, column); + } + if (char !== "}") { + +// test_cause: +// ["\"\\u{12345\"", "char_after_escape", "expected_a_before_b", "\"", 10] + + stop_at("expected_a_before_b", line, column, "}", char); + } + return char_after(); + } + char_before(); + if (read_digits("x", mode_digits_empty_string) < 4) { + +// test_cause: +// ["\"\\u0\"", "char_after_escape", "expected_four_digits", "", 5] + + warn_at("expected_four_digits", line, column); + } + return; + default: + if (extra && extra.indexOf(char) >= 0) { + return char_after(); + } + +// test_cause: +// ["\"\\0\"", "char_after_escape", "unexpected_a_before_b", "0", 3] + + warn_at("unexpected_a_before_b", line, column, "\\", char); + } + } + + function char_before() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the line_source. + + char = snippet.slice(-1); + line_source = char + line_source; + column -= char.length; + +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + return char; + } + + function check_numeric_separator(digits, column) { + +// This function will check for illegal numeric-separator in . + + digits.replace(( + jslint_rgx_numeric_separator_illegal + ), function (ignore, ii) { + +// test_cause: +// ["0x0_0_;", "check_numeric_separator", "illegal_num_separator", "", 6] +// ["0x0_0__0;", "check_numeric_separator", "illegal_num_separator", "", 6] +// ["aa=1_2_;", "check_numeric_separator", "illegal_num_separator", "", 7] +// ["aa=1_2__3;", "check_numeric_separator", "illegal_num_separator", "", 7] +// ["aa=1_2_n;", "check_numeric_separator", "illegal_num_separator", "", 7] + + warn_at("illegal_num_separator", line, column + ii + 1); + return ""; + }); + } + + function lex_comment() { + let body; + let ii = 0; + let jj = 0; + let the_comment; + +// Create a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + +// Create token from comment //.... + + if (snippet === "//") { + snippet = line_source; + line_source = ""; + the_comment = token_create("(comment)", snippet); + if (mode_mega) { + +// test_cause: +// ["`${//}`", "lex_comment", "unexpected_comment", "`", 4] + + warn("unexpected_comment", the_comment, "`"); + } + +// Create token from comment /*...*/. + + } else { + snippet = []; + if (line_source[0] === "/") { + +// test_cause: +// ["/*/", "lex_comment", "unexpected_a", "/", 2] + + warn_at("unexpected_a", line, column + ii, "/"); + } + +// Lex/loop through each line until "*/". + + while (true) { + // jslint_rgx_star_slash + ii = line_source.indexOf("*/"); + if (ii >= 0) { + break; + } + // jslint_rgx_slash_star + ii = line_source.indexOf("/*"); + if (ii >= 0) { + +// test_cause: +// ["/*/*", "lex_comment", "nested_comment", "", 2] + + warn_at("nested_comment", line, column + ii); + } + snippet.push(line_source); + line_source = read_line(); + if (line_source === undefined) { + +// test_cause: +// ["/*", "lex_comment", "unclosed_comment", "", 1] + + return stop_at("unclosed_comment", line, column); + } + } + jj = line_source.slice(0, ii).search( + jslint_rgx_slash_star_or_slash + ); + if (jj >= 0) { + +// test_cause: +// ["/*/**/", "lex_comment", "nested_comment", "", 2] + + warn_at("nested_comment", line, column + jj); + } + snippet.push(line_source.slice(0, ii)); + snippet = snippet.join(" "); + column += ii + 2; + line_source = line_source.slice(ii + 2); + the_comment = token_create("(comment)", snippet); + } + +// Uncompleted work comment. + + if (!option_dict.devel && jslint_rgx_todo.test(snippet)) { + +// test_cause: +// ["//todo", "lex_comment", "todo_comment", "(comment)", 1] //jslint-ignore-line + + warn("todo_comment", the_comment); + } + +// Lex directives in comment. + + [ + the_comment.directive, body + ] = Array.from(snippet.match(jslint_rgx_directive) || []).slice(1); + if (the_comment.directive === undefined) { + return the_comment; + } + directive_list.push(the_comment); + if (!mode_directive) { + +// test_cause: +// ["0\n/*global aa*/", "lex_comment", "misplaced_directive_a", "global", 1] + + warn_at("misplaced_directive_a", line, from, the_comment.directive); + return the_comment; + } + +// lex_directive(); +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + +// Lex/loop through each directive in /*...*/ + + ii = 0; + body.replace(jslint_rgx_directive_part, function ( + match0, + key, + val, + jj + ) { + if (ii !== jj) { + +// test_cause: +// ["/*jslint !*/", "lex_comment", "bad_directive_a", "!", 1] + + return stop("bad_directive_a", the_comment, body.slice(ii)); + } + if (match0 === "") { + return ""; + } + ii += match0.length; + switch (the_comment.directive) { + case "global": + if (val) { + +// test_cause: +// ["/*global aa:false*/", "lex_comment", "bad_option_a", "aa:false", 1] + + warn("bad_option_a", the_comment, key + ":" + val); + } + global_dict[key] = "user-defined"; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// state.mode_module = the_comment; + + break; + case "jslint": + if (!option_set_item(key, val !== "false")) { + +// test_cause: +// ["/*jslint undefined*/", "lex_comment", "bad_option_a", "undefined", 1] + + warn("bad_option_a", the_comment, key); + } + break; + case "property": + state.mode_property = true; + tenure[key] = true; + break; + } + return ""; + }); + return the_comment; + } + + function lex_megastring() { + let id; + let match; + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (mode_mega) { + +// test_cause: +// ["`${`", "lex_megastring", "expected_a_b", "`", 4] + + return stop_at("expected_a_b", line, column, "}", "`"); + } + from_mega = from; + line_mega = line; + mode_mega = true; + snippet = ""; + +// Parsing a mega literal is tricky. First create a ` token. + + token_create("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + while (true) { + match = line_source.match(jslint_rgx_mega) || { + "0": "", + index: 0 + }; + snippet += line_source.slice(0, match.index); + column += match.index; + line_source = line_source.slice(match.index); + match = match[0]; + switch (match) { + case "${": + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + token_create("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then create tokens that will become part of an expression until +// a } token is made. + + column += 2; + token_create("${"); + line_source = line_source.slice(2); + +// Lex/loop through each token inside megastring-expression `${...}`. + + while (true) { + id = lex_token().id; + if (id === "{") { + +// test_cause: +// ["`${{", "lex_megastring", "expected_a_b", "{", 4] + + return stop_at("expected_a_b", line, column, "}", "{"); + } + if (id === "}") { + break; + } + } + break; + case "\\": + snippet += line_source.slice(0, 2); + line_source = line_source.slice(2); + column += 2; + break; + case "`": + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + token_create("(string)", snippet).quote = "`"; + snippet = ""; + +// Terminate megastring with `. + + line_source = line_source.slice(1); + column += 1; + mode_mega = false; + return token_create("`"); + default: + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + snippet += line_source + "\n"; + if (read_line() === undefined) { + +// test_cause: +// ["`", "lex_megastring", "unclosed_mega", "", 1] + + return stop_at("unclosed_mega", line_mega, from_mega); + } + } + } + } + + function lex_number() { + let prefix = snippet; + +// PR-390 - Add numeric-separator check. + + check_numeric_separator(prefix, column - prefix.length); + char_after(); + switch (prefix === "0" && char) { + case "b": + case "o": + case "x": + read_digits(char, mode_digits_numeric_separator); + +// PR-351 - Ignore BigInt suffix 'n'. + + if (char === "n") { + char_after("n"); + } + break; + default: + if (char === ".") { + read_digits("d", mode_digits_numeric_separator); + } + if (char === "E" || char === "e") { + char_after(char); + if (char !== "+" && char !== "-") { + char_before(); + } + read_digits("d", mode_digits_numeric_separator); + } + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + +// test_cause: +// ["0a", "lex_number", "unexpected_a_after_b", "0", 2] + + return stop_at( + "unexpected_a_after_b", + line, + column, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + char_before(); + return token_create("(number)", snippet); + } + + function lex_regexp() { + +// Regexp +// Lex a regular expression literal. + + let flag; + let mode_regexp_multiline; + let result; + let value; + mode_regexp = true; + + function lex_regexp_bracketed() { + let mode_regexp_range; + +// RegExp +// Match a class. + + char_after("["); + if (char === "^") { + char_after("^"); + } + while (true) { + +// RegExp +// Match a character in a character class. + + switch (char) { + case "": + case "]": + +// test_cause: +// ["aa=/[", "lex_regexp_bracketed", "closer", "", 0] +// ["aa=/[]/", "lex_regexp_bracketed", "closer", "", 0] + + test_cause("closer"); + if (mode_regexp_range) { + +// test_cause: +// ["aa=/[0-]/", "lex_regexp_bracketed", "unexpected_a", "-", 7] + + warn_at("unexpected_a", line, column - 1, "-"); + } + return char_after("]"); + +// PR-362 - Relax regexp-warning against using . +// +// case " ": +// +// // test_cause: +// // ["aa=/[ ]/", "lex_regexp_bracketed", "expected_a_b", " ", 6] +// +// warn_at("expected_a_b", line, column, "\\u0020", " "); +// break; + + case "-": + case "/": + case "[": + case "^": + +// test_cause: +// ["aa=/[-]/", "lex_regexp_bracketed", "expected_a_before_b", "-", 6] +// ["aa=/[.^]/", "lex_regexp_bracketed", "expected_a_before_b", "^", 7] +// ["aa=/[/", "lex_regexp_bracketed", "expected_a_before_b", "/", 6] +// ["aa=/[\\\\/]/", "lex_regexp_bracketed", "expected_a_before_b", "/", 8] +// ["aa=/[\\\\[]/", "lex_regexp_bracketed", "expected_a_before_b", "[", 8] + + warn_at("expected_a_before_b", line, column, "\\", char); + break; + case "\\": + char_after_escape("BbDdSsWw-[]^"); + char_before(); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${/[`]/}`", "lex_regexp_bracketed", "unexpected_a", "`", 6] + + warn_at("unexpected_a", line, column, "`"); + } + break; + } + char_after(); + mode_regexp_range = false; + if (char === "-") { + +// RegExp +// Match a range of subclasses. + + mode_regexp_range = true; + char_after("-"); + } + } + } + + function lex_regexp_group() { + +// RegExp +// Lex sequence of characters in regexp. + + switch (char) { + case "": + warn_at("expected_regexp_factor_a", line, column, char); + break; + case ")": + warn_at("expected_regexp_factor_a", line, column, char); + break; + case "]": + +// test_cause: +// ["/ /", "lex_regexp_group", "expected_regexp_factor_a", "", 3] +// ["aa=/)", "lex_regexp_group", "expected_regexp_factor_a", ")", 5] +// ["aa=/]", "lex_regexp_group", "expected_regexp_factor_a", "]", 5] + + warn_at("expected_regexp_factor_a", line, column, char); + break; + } + while (true) { + switch (char) { + case "": + case ")": + case "/": + case "]": + return; + +// PR-362 - Relax regexp-warning against using . +// +// case " ": +// +// // test_cause: +// // ["aa=/ /", "lex_regexp_group", "expected_a_b", " ", 5] +// +// warn_at("expected_a_b", line, column, "\\s", " "); +// char_after(); +// break; + + case "$": + if (line_source[0] !== "/") { + mode_regexp_multiline = true; + } + char_after(); + break; + case "(": + +// RegExp +// Match a group that starts with left paren. + + char_after("("); + switch (char) { + case ":": + +// test_cause: +// ["aa=/(:)/", "lex_regexp_group", "expected_a_before_b", ":", 6] +// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5] + + warn_at("expected_a_before_b", line, column, "?", ":"); + break; + case "?": + char_after("?"); + switch (char) { + case "!": + +// PR-437 - Add grammar for regexp-named-capture-group. + + case "<": + case "=": + char_after(); + break; + default: + char_after(":"); + } + break; + } + +// RegExp +// Recurse lex_regexp_group(). + + lex_regexp_group(); + char_after(")"); + break; + case "*": + case "+": + case "?": + case "{": + case "}": + +// test_cause: +// ["aa=/+/", "lex_regexp_group", "expected_a_before_b", "+", 5] +// ["aa=/.**/", "lex_regexp_group", "expected_a_before_b", "*", 7] +// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5] +// ["aa=/{/", "lex_regexp_group", "expected_a_before_b", "{", 5] +// ["aa=/}/", "lex_regexp_group", "expected_a_before_b", "}", 5] + + warn_at("expected_a_before_b", line, column, "\\", char); + char_after(); + break; + case "[": + lex_regexp_bracketed(); + break; + case "\\": + +// test_cause: +// ["aa=/\\/", "lex_regexp_group", "escape", "", 0] + + test_cause("escape"); + +// PR-437 - Add grammar for regexp-named-backreference. + + char_after_escape("BbDdSsWw^${}[]():=!.|*+?k"); + break; + case "^": + if (snippet !== "^") { + mode_regexp_multiline = true; + } + char_after(); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${/`/}`", "lex_regexp_group", "unexpected_a", "`", 5] + + warn_at("unexpected_a", line, column, "`"); + } + char_after(); + break; + default: + char_after(); + } + +// RegExp +// Match an optional quantifier. + + switch (char) { + case "*": + case "+": + if (char_after(char) === "?") { + +// test_cause: +// ["aa=/.*?/", "lex_regexp_group", "?", "", 0] +// ["aa=/.+?/", "lex_regexp_group", "?", "", 0] + + test_cause("?"); + char_after("?"); + } + break; + case "?": + if (char_after("?") === "?") { + +// test_cause: +// ["aa=/.??/", "lex_regexp_group", "unexpected_a", "?", 7] + + warn_at("unexpected_a", line, column, char); + char_after("?"); + } + break; + case "{": + if (read_digits("d", mode_digits_empty_string) === 0) { + +// test_cause: +// ["aa=/aa{/", "lex_regexp_group", "expected_a_before_b", ",", 8] + + warn_at("expected_a_before_b", line, column, "0", ","); + } + if (char === ",") { + +// test_cause: +// ["aa=/.{,/", "lex_regexp_group", "comma", "", 0] + + test_cause("comma"); + read_digits("d", mode_digits_empty_string); + } + if (char_after("}") === "?") { + +// test_cause: +// ["aa=/.{0}?/", "lex_regexp_group", "unexpected_a", "?", 9] + + warn_at("unexpected_a", line, column, char); + char_after("?"); + } + break; + } + } + } + +// RegExp +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + char_after(); + if (char === "=") { + +// test_cause: +// ["aa=/=/", "lex_regexp", "expected_a_before_b", "=", 5] + + warn_at("expected_a_before_b", line, column, "\\", "="); + } + lex_regexp_group(); + +// RegExp +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + +// RegExp +// Make sure there is a closing slash. + + value = snippet; + char_after("/"); + +// RegExp +// Create flag. + + flag = empty(); + while ( + +// Regexp +// char is a letter. + + (char >= "a" && char <= "z\uffff") + || (char >= "A" && char <= "Z\uffff") + ) { + +// RegExp +// Process dangling flag letters. + + switch (!flag[char] && char) { + case "g": + break; + case "i": + break; + case "m": + break; + case "u": + break; + case "y": + +// test_cause: +// ["aa=/./gimuy", "lex_regexp", "flag", "", 0] + + test_cause("flag"); + break; + default: + +// test_cause: +// ["aa=/./gg", "lex_regexp", "unexpected_a", "g", 8] +// ["aa=/./z", "lex_regexp", "unexpected_a", "z", 7] + + warn_at("unexpected_a", line, column, char); + } + flag[char] = true; + char_after(); + } + char_before(); + if (char === "/" || char === "*") { + +// test_cause: +// ["aa=/.//", "lex_regexp", "unexpected_a", "/", 3] + + return stop_at("unexpected_a", line, from, char); + } + result = token_create("(regexp)", char); + result.flag = flag; + result.value = value; + if (mode_regexp_multiline && !flag.m) { + +// test_cause: +// ["aa=/$^/", "lex_regexp", "missing_m", "", 7] + + warn_at("missing_m", line, column); + } + return result; + } + + function lex_slash_or_regexp() { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + let the_token; + switch ( + token_prv_expr.identifier + && !token_prv_expr.dot + && token_prv_expr.id + ) { + case "case": + case "delete": + case "in": + case "instanceof": + case "new": + case "typeof": + case "void": + case "yield": + the_token = lex_regexp(); + +// test_cause: +// ["case /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6] +// ["delete /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8] +// ["in /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 4] +// ["instanceof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 12] +// ["new /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 5] +// ["typeof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8] +// ["void /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6] +// ["yield /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 7] + + return stop("unexpected_a", the_token); + case "return": + return lex_regexp(); + } + switch (!token_prv_expr.identifier && token_prv_expr.id.slice(-1)) { + case "!": + case "%": + case "&": + case "*": + case "+": + case "-": + case "/": + case ";": + case "<": + case ">": + case "^": + case "{": + case "|": + case "}": + case "~": + the_token = lex_regexp(); + +// test_cause: +// ["!/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["%/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["&/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["+/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["-/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["0 * /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5] +// ["0 / /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5] +// [";/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["^/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["{/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["|/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["}/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["~/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] + + warn("wrap_regexp", the_token); + return the_token; + case "(": + case ",": + case ":": + case "=": + case "?": + case "[": + +// test_cause: +// ["(/./", "lex_slash_or_regexp", "recurse", "", 0] +// [",/./", "lex_slash_or_regexp", "recurse", "", 0] +// [":/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["=/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["?/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["aa[/./", "lex_slash_or_regexp", "recurse", "", 0] + + test_cause("recurse"); + return lex_regexp(); + } + if (line_source[0] === "=") { + column += 1; + line_source = line_source.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + return token_create(snippet); + } + + function lex_string(quote) { + +// Create a string token. + + let the_token; + if (!option_dict.single && quote === "'") { + +// test_cause: +// ["''", "lex_string", "use_double", "", 1] + + warn_at("use_double", line, column); + } + snippet = ""; + char_after(); + +// Lex/loop through each character in "...". + + while (true) { + switch (char) { + case "": + +// test_cause: +// ["\"", "lex_string", "unclosed_string", "", 1] + + return stop_at("unclosed_string", line, column); + case "\\": + char_after_escape(quote); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${\"`\"}`", "lex_string", "unexpected_a", "`", 5] + + warn_at("unexpected_a", line, column, "`"); + } + char_after("`"); + break; + case quote: + +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + the_token = token_create("(string)", snippet); + the_token.quote = quote; + return the_token; + default: + char_after(); + } + } + } + + function lex_token() { + let match; + +// Lex/loop through each whitespace. + + while (true) { + +// Lex/loop through each blank-line. + + while (!line_source) { + line_source = read_line(); + from = 0; + if (line_source === undefined) { + return ( + mode_mega + +// test_cause: +// ["`${//}`", "lex_token", "unclosed_mega", "", 1] + + ? stop_at("unclosed_mega", line_mega, from_mega) + : line_disable !== undefined + +// test_cause: +// ["/*jslint-disable*/", "lex_token", "unclosed_disable", "", 1] + + ? stop_at("unclosed_disable", line_disable) + : token_create("(end)") + ); + } + } + from = column; + match = line_source.match(jslint_rgx_token); + +// match[1] token +// match[2] whitespace +// match[3] identifier +// match[4] number +// match[5] rest + + if (!match) { + +// test_cause: +// ["#", "lex_token", "unexpected_char_a", "#", 1] + + return stop_at( + "unexpected_char_a", + line, + column, + line_source[0] + ); + } + snippet = match[1]; + column += snippet.length; + line_source = match[5]; + if (!match[2]) { + break; + } + } + +// The token is an identifier. + + if (match[3]) { + return token_create(snippet, undefined, true); + } + +// Create token from number. + + if (match[4]) { + return lex_number(); + } + +// Create token from string "..." or '...'. + + if (snippet === "\"" || snippet === "'") { + return lex_string(snippet); + } + +// Create token from megastring `...`. + + if (snippet === "`") { + return lex_megastring(); + } + +// Create token from comment /*...*/ or //.... + + if (snippet === "/*" || snippet === "//") { + return lex_comment(); + } + +// Create token from slash /. + + if (snippet === "/") { + return lex_slash_or_regexp(); + } + return token_create(snippet); + } + + function option_set_item(key, val) { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + switch (key) { + case "beta": // Enable experimental warnings. + case "bitwise": // Allow bitwise operator. + case "browser": // Assume browser environment. + case "convert": // Allow conversion operator. + case "couch": // Assume CouchDb environment. + case "devel": // Allow console.log() and friends. + case "ecma": // Assume ECMAScript environment. + case "eval": // Allow eval(). + case "fart": // Allow complex fat-arrow. + case "for": // Allow for-statement. + case "getset": // Allow get() and set(). + case "indent2": // Use 2-space indent. + case "long": // Allow long lines. + case "node": // Assume Node.js environment. + case "nomen": // Allow weird property name. + case "single": // Allow single-quote strings. + case "subscript": // Allow identifier in subscript-notation. + case "test_cause": // Test jslint's causes. + case "test_internal_error": // Test jslint's internal-error + // ... handling-ability. + case "this": // Allow 'this'. + case "trace": // Include jslint stack-trace in warnings. + case "unordered": // Allow unordered cases, params, properties, + // ... variables, and exports. + case "variable": // Allow unordered const and let declarations + // ... that are not at top of function-scope. + case "white": // Allow messy whitespace. + option_dict[key] = val; + break; + +// PR-404 - Alias "evil" to jslint-directive "eval" for backwards-compat. + + case "evil": + return option_set_item("eval", val); + +// PR-404 - Alias "nomen" to jslint-directive "name" for backwards-compat. + + case "name": + return option_set_item("nomen", val); + default: + return false; + } + +// Initialize global-variables. + + switch (val && key) { + +// Assign global browser variables to global_dict. +/* +// /\*jslint beta, browser, devel*\/ +console.log(JSON.stringify(Object.keys(window).sort(), undefined, 4)); +*/ + + case "browser": + object_assign_from_list(global_dict, [ + +// Shared with Node.js. + + "AbortController", + // "Buffer", + // "Crypto", + // "CryptoKey", + "Event", + "EventTarget", + "MessageChannel", + "MessageEvent", + "MessagePort", + // "Request", + // "Response", + // "SubtleCrypto", + "TextDecoder", + "TextEncoder", + "URL", + "URLSearchParams", + "WebAssembly", + // "__dirname", + // "__filename", + // "atob", + // "btoa", + // "clearImmediate", + "clearInterval", + "clearTimeout", + // "console", + // "crypto", + // "exports", + // "fetch", + // "global", + // "module", + "performance", + // "process", + "queueMicrotask", + // "require", + // "setImmediate", + "setInterval", + "setTimeout", + +// Web worker only. +// https://github.com/mdn/content/blob/main/files/en-us/web/api +// /workerglobalscope/index.md + + "importScripts", + +// Window. + + "Blob", + // "CharacterData", + // "DocumentType", + // "Element", + // "Event", + "FileReader", + // "FontFace", + "FormData", + "IntersectionObserver", + "MutationObserver", + // "Storage", + // "TextDecoder", + // "TextEncoder", + // "URL", + "Worker", + "XMLHttpRequest", + // "caches", + // "clearInterval", + // "clearTimeout", + "document", + // "event", + "fetch", + // "history", + "indexedDb", + "localStorage", + "location", + // "name", + "navigator", + "postMessage", + // "screen", + "sessionStorage", + // "setInterval", + // "setTimeout", + "structuredClone", + "window" + ], "browser"); + break; + +// https://docs.couchdb.org/en/stable/query-server/javascript.html#javascript + + case "couch": + object_assign_from_list(global_dict, [ + "emit", + "getRow", + "isArray", + "log", + "provides", + "registerType", + "require", + "send", + "start", + "sum", + "toJSON" + ], "CouchDb"); + break; + case "devel": + object_assign_from_list(global_dict, [ + "alert", "confirm", "console", "prompt" + ], "development"); + break; + +// These are the globals that are provided by the language standard. +// Assign global ECMAScript variables to global_dict. +/* +node --input-type=module --eval ' +// /\*jslint beta, node*\/ +import https from "https"; +(async function () { + let dict = {import: true}; + let result = ""; + await new Promise(function (resolve) { + https.get(( + "https://raw.githubusercontent.com/mdn/content/main/files" + + "/en-us/web/javascript/reference/global_objects/index.md" + ), function (res) { + res.on("data", function (chunk) { + result += chunk; + }).on("end", resolve).setEncoding("utf8"); + }); + }); + result.replace(( + /\n- \{\{JSxRef\("(?:Global_Objects\/)?([^"\/]+?)"/g + ), function (ignore, key) { + if (globalThis.hasOwnProperty(key)) { + dict[key] = true; + } + return ""; + }); + console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4)); +}()); +' +*/ + + case "ecma": + object_assign_from_list(global_dict, [ + "AggregateError", + "Array", + "ArrayBuffer", + "Atomics", + "BigInt", + "BigInt64Array", + "BigUint64Array", + "Boolean", + "DataView", + "Date", + "Error", + "EvalError", + "Float32Array", + "Float64Array", + "Function", + "Infinity", + "Int16Array", + "Int32Array", + "Int8Array", + "Intl", + "JSON", + "Map", + "Math", + "NaN", + "Number", + "Object", + "Promise", + "Proxy", + "RangeError", + "ReferenceError", + "Reflect", + "RegExp", + "Set", + "SharedArrayBuffer", + "String", + "Symbol", + "SyntaxError", + "TypeError", + "URIError", + "Uint16Array", + "Uint32Array", + "Uint8Array", + "Uint8ClampedArray", + "WeakMap", + "WeakSet", + "WebAssembly", + "decodeURI", + "decodeURIComponent", + "encodeURI", + "encodeURIComponent", + "eval", + "globalThis", + "import", + "isFinite", + "isNaN", + "parseFloat", + "parseInt", + "undefined" + ], "ECMAScript"); + break; + +// Assign global Node.js variables to global_dict. +/* +node --input-type=module --eval ' +// /\*jslint beta, node*\/ +import moduleHttps from "https"; +(async function () { + let dict = Object.create(null); + let result = ""; + await new Promise(function (resolve) { + moduleHttps.get(( + "https://raw.githubusercontent.com/nodejs/node/v16.x/doc/api" + + "/globals.md" + ), function (res) { + res.on("data", function (chunk) { + result += chunk; + }).on("end", resolve).setEncoding("utf8"); + }); + }); + result.replace(( + /\n(?:\* \[`|## |## Class: )`\w+/g + ), function (match0) { + dict[match0.split("`")[1]] = true; + return ""; + }); + console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4)); +}()); +' +*/ + + case "node": + object_assign_from_list(global_dict, [ + "AbortController", + "Buffer", + // "Crypto", + // "CryptoKey", + "Event", + "EventTarget", + "MessageChannel", + "MessageEvent", + "MessagePort", + // "Request", + // "Response", + // "SubtleCrypto", + "TextDecoder", + "TextEncoder", + "URL", + "URLSearchParams", + "WebAssembly", + "__dirname", + "__filename", + // "atob", + // "btoa", + "clearImmediate", + "clearInterval", + "clearTimeout", + "console", + // "crypto", + "exports", + // "fetch", + "global", + "module", + "performance", + "process", + "queueMicrotask", + "require", + "setImmediate", + "setInterval", + "setTimeout" + ], "Node.js"); + break; + } + return true; + } + + function read_digits(base, mode) { + let digits = line_source.match( + base === "b" + ? jslint_rgx_digits_bits + : base === "o" + ? jslint_rgx_digits_octals + : base === "x" + ? jslint_rgx_digits_hexs + : jslint_rgx_digits_decimals + )[0]; + if ( + (mode !== mode_digits_empty_string && digits.length === 0) + || digits[0] === "_" + ) { + +// test_cause: +// ["0x", "read_digits", "expected_digits_after_a", "0x", 2] +// ["0x_", "read_digits", "expected_digits_after_a", "0x", 2] + + warn_at("expected_digits_after_a", line, column, snippet); + } + +// PR-390 - Add numeric-separator check. + + if (mode === mode_digits_numeric_separator) { + check_numeric_separator(digits, column); + } else if (digits.indexOf("_") >= 0) { + +// test_cause: +// ["\"\\u{1_2}\"", "read_digits", "illegal_num_separator", "", 6] + + warn_at( + "illegal_num_separator", + line, + column + digits.indexOf("_") + 1 + ); + } + column += digits.length; + line_source = line_source.slice(digits.length); + snippet += digits; + char_after(); + return digits.length; + } + + function read_line() { + +// Put the next line of source in line_source. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + if ( + !option_dict.long + && line_whole.length > 80 + && line_disable === undefined + && !state.mode_json + && token_1 + && !mode_regexp + ) { + +// test_cause: +// ["/////////////////////////////////////////////////////////////////////////////////", "read_line", "too_long", "", 1] //jslint-ignore-line + + warn_at("too_long", line); + } + column = 0; + line += 1; + mode_regexp = false; + line_source = undefined; + line_whole = ""; + if (line_list[line] === undefined) { + return line_source; + } + line_source = line_list[line].line_source; + line_whole = line_source; + +// Scan each line for following ignore-directives: +// "/*jslint-disable*/" +// "/*jslint-enable*/" +// "//jslint-ignore-line" + + if (line_source === "/*jslint-disable*/") { + +// test_cause: +// ["/*jslint-disable*/", "read_line", "jslint_disable", "", 0] + + test_cause("jslint_disable"); + line_disable = line; + } else if (line_source === "/*jslint-enable*/") { + if (line_disable === undefined) { + +// test_cause: +// ["/*jslint-enable*/", "read_line", "unopened_enable", "", 1] + + stop_at("unopened_enable", line); + } + line_disable = undefined; + } else if ( + line_source.endsWith(" //jslint-ignore-line") + || line_source.endsWith(" //jslint-quiet") + ) { + +// test_cause: +// ["0 //jslint-ignore-line", "read_line", "jslint_ignore_line", "", 0] + + test_cause("jslint_ignore_line"); + line_list[line].directive_ignore_line = true; + } + if (line_disable !== undefined) { + +// test_cause: +// ["/*jslint-disable*/\n0", "read_line", "line_disable", "", 0] + + test_cause("line_disable"); + line_source = ""; + } + // jslint_rgx_tab + if (line_source.indexOf("\t") >= 0) { + if (!option_dict.white) { + +// test_cause: +// ["\t", "read_line", "use_spaces", "", 1] + + warn_at("use_spaces", line, line_source.indexOf("\t") + 1); + } + line_source = line_source.replace(jslint_rgx_tab, " "); + } + if (!option_dict.white && line_source.endsWith(" ")) { + +// test_cause: +// [" ", "read_line", "unexpected_trailing_space", "", 1] + + warn_at("unexpected_trailing_space", line, line_source.length - 1); + } + return line_source; + } + + function token_create(id, value, identifier) { + +// Create the token object and append it to token_list. + + let the_token = { + from, + id, + identifier: Boolean(identifier), + line, + nr: token_list.length, + thru: column, + value + }; + token_list.push(the_token); + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + mode_directive = false; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option_dict.white. + + if ( + token_prv.line === line + && token_prv.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (token_prv.id === "(comment)" || token_prv.id === "(regexp)") + ) { + +// test_cause: +// ["/**//**/", "token_create", "expected_space_a_b", "(comment)", 5] + + warn( + "expected_space_a_b", + the_token, + artifact(token_prv), + artifact(the_token) + ); + } + if (token_prv.id === "." && id === "(number)") { + +// test_cause: +// [".0", "token_create", "expected_a_before_b", ".", 1] + + warn("expected_a_before_b", token_prv, "0", "."); + } + if (token_prv_expr.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart. +// Farts are now detected by keeping a list of most recent "(" tokens at any +// given depth. When a "=>" token is encountered, the most recent "(" token at +// current depth is marked as a fart. + + switch (id) { + case "(": + paren_backtrack_list[paren_depth] = the_token; + paren_depth += 1; + break; + case ")": + paren_depth -= 1; + break; + case "=>": + if ( + token_prv_expr.id === ")" + && paren_backtrack_list[paren_depth] + ) { + paren_backtrack_list[paren_depth].fart = the_token; + } + break; + } + +// The previous token is used to detect adjacency problems. + + token_prv = the_token; + +// The token_prv_expr token is a previous token that was not a comment. +// The token_prv_expr token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (token_prv.id !== "(comment)") { + token_prv_expr = token_prv; + } + return the_token; + } + +// Init global_dict and option_dict. + + option_set_item("ecma", true); + Object.keys(option_dict).sort().forEach(function (key) { + option_set_item(key, option_dict[key] === true); + }); + object_assign_from_list(global_dict, global_list, "user-defined"); + +// Scan first line for "#!" and ignore it. + + if (line_list[jslint_fudge].line_source.startsWith("#!")) { + line += 1; + state.mode_shebang = true; + } + token_1 = lex_token(); + state.mode_json = token_1.id === "{" || token_1.id === "["; + +// Lex/loop through each token until (end). + + while (true) { + if (lex_token().id === "(end)") { + break; + } + } +} + +function jslint_phase3_parse(state) { + +// PHASE 3. Parse into using the Pratt-parser. + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + + let anon = "anonymous"; // The guessed name for anonymous functions. + let { + artifact, + catch_list, + catch_stack, + export_dict, + function_list, + function_stack, + global_dict, + import_list, + is_equal, + option_dict, + property_dict, + stop, + syntax_dict, + tenure, + test_cause, + token_global, + token_list, + warn, + warn_at + } = state; + let catchage = catch_stack[0]; // The current catch-block. + let functionage = token_global; // The current function. + let mode_var; // "var" if using var; "let" if using let. + let token_ii = 0; // The number of the next token. + let token_now = token_global; // The current token being examined in + // ... the parse. + let token_nxt = token_global; // The next token to be examined in + // ... . + + function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if ( + token_now.identifier + && token_now.id !== "function" + && token_now.id !== "async" + ) { + anon = token_now.id; + } else if ( + token_now.id === "(string)" + && jslint_rgx_identifier.test(token_now.value) + ) { + anon = token_now.value; + } + +// Attempt to match token_nxt with an expected id. + + if (id !== undefined && token_nxt.id !== id) { + return ( + match === undefined + +// test_cause: +// ["{0:0}", "advance", "expected_a_b", "0", 2] + + ? stop("expected_a_b", token_nxt, id, artifact()) + +// test_cause: +// ["{\"aa\":0", "advance", "expected_a_b_from_c_d", "{", 1] + + : stop( + "expected_a_b_from_c_d", + token_nxt, + id, + artifact(match), + match.line, + artifact() + ) + ); + } + +// Promote the tokens, skipping comments. + + token_now = token_nxt; + while (true) { + token_nxt = token_list[token_ii]; + state.token_nxt = token_nxt; + token_ii += 1; + if (token_nxt.id !== "(comment)") { + if (token_nxt.id === "(end)") { + token_ii -= 1; + } + break; + } + if (state.mode_json) { + +// test_cause: +// ["[//]", "advance", "unexpected_a", "(comment)", 2] + + warn("unexpected_a"); + } + } + } + + function assignment(id) { + +// Create an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led_infix = function (left) { + const the_token = token_now; + let right; + the_token.arity = "assignment"; + right = parse_expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "preassign" + || right.arity === "postassign" + ) { + warn("unexpected_a", right); + } + check_mutation(left); + return the_token; + }; + return the_symbol; + } + + function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token_now; + if (special !== "body") { + functionage.statement_prv = the_block; + } + the_block.arity = "statement"; + the_block.body = special === "body"; + +// Top level function bodies may include the "use strict" pragma. + + if ( + special === "body" + && function_stack.length === 1 + && token_nxt.value === "use strict" + ) { + token_nxt.statement = true; + advance("(string)"); + advance(";"); + } + stmts = parse_statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option_dict.devel && special !== "ignore") { + +// test_cause: +// ["function aa(){}", "block", "empty_block", "{", 14] + + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; + } + + function check_left(left, right) { + +// Warn if the left is not one of these: +// ?. +// ?: +// e() +// e.b +// e[b] +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !check_left(left.expression[1]) + && !check_left(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "?." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right || token_nxt); + return false; + } + return true; + } + + function check_mutation(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + +// test_cause: +// ["0=0", "check_mutation", "bad_assignment_a", "0", 1] + + warn("bad_assignment_a", the_thing); + return false; + } + return true; + } + + function check_not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === token_global) { + +// test_cause: +// [" +// while(0){} +// ", "check_not_top_level", "unexpected_at_top_level_a", "while", 1] + + warn("unexpected_at_top_level_a", thing); + } + } + + function check_ordered(type, token_list) { + +// This function will warn if is unordered. + + token_list.reduce(function (aa, token) { + const bb = artifact(token); + if (!option_dict.unordered && aa > bb) { + warn("expected_a_b_before_c_d", token, type, bb, type, aa); + } + return bb; + }, ""); + } + + function check_ordered_case(case_list) { + +// This function will warn if is unordered. + + case_list.filter(noop).map(function (token) { + switch (token.identifier || token.id) { + case "(number)": + return { + order: 1, + token, + type: "number", + value: Number(artifact(token)) + }; + case "(string)": + return { + order: 2, + token, + type: "string", + value: artifact(token) + }; + case true: + return { + order: 3, + token, + type: "identifier", + value: artifact(token) + }; + } + }).reduce(function (aa, bb) { + if ( + +// PR-419 - Hide warning about unordered case-statements behind beta-flag. + + option_dict.beta + && !option_dict.unordered + && aa && bb + && ( + aa.order > bb.order + || (aa.order === bb.order && aa.value > bb.value) + ) + ) { + warn( + "expected_a_b_before_c_d", + bb.token, + `case-${bb.type}`, + bb.value, + `case-${aa.type}`, + aa.value + ); + } + return bb; + }, undefined); + } + + function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = token_nxt; + let the_value; + +// test_cause: +// ["do{}while()", "condition", "", "", 0] +// ["if(){}", "condition", "", "", 0] +// ["while(){}", "condition", "", "", 0] + + test_cause(""); + the_paren.free = true; + advance("("); + the_value = parse_expression(0); + advance(")"); + if (the_value.wrapped === true) { + +// test_cause: +// ["while((0)){}", "condition", "unexpected_a", "(", 6] + + warn("unexpected_a", the_paren); + } + +// Check for anticondition. + + switch (the_value.id) { + case "%": + warn("unexpected_a", the_value); + break; + case "&": + warn("unexpected_a", the_value); + break; + case "(number)": + warn("unexpected_a", the_value); + break; + case "(string)": + warn("unexpected_a", the_value); + break; + case "*": + warn("unexpected_a", the_value); + break; + case "+": + warn("unexpected_a", the_value); + break; + case "-": + warn("unexpected_a", the_value); + break; + case "/": + warn("unexpected_a", the_value); + break; + case "<<": + warn("unexpected_a", the_value); + break; + case ">>": + warn("unexpected_a", the_value); + break; + case ">>>": + warn("unexpected_a", the_value); + break; + case "?": + warn("unexpected_a", the_value); + break; + case "^": + warn("unexpected_a", the_value); + break; + case "typeof": + warn("unexpected_a", the_value); + break; + case "|": + warn("unexpected_a", the_value); + break; + case "~": + +// test_cause: +// ["if(0%0){}", "condition", "unexpected_a", "%", 5] +// ["if(0&0){}", "condition", "unexpected_a", "&", 5] +// ["if(0){}", "condition", "unexpected_a", "0", 4] +// ["if(0*0){}", "condition", "unexpected_a", "*", 5] +// ["if(0+0){}", "condition", "unexpected_a", "+", 5] +// ["if(0-0){}", "condition", "unexpected_a", "-", 5] +// ["if(0/0){}", "condition", "unexpected_a", "/", 5] +// ["if(0<<0){}", "condition", "unexpected_a", "<<", 5] +// ["if(0>>0){}", "condition", "unexpected_a", ">>", 5] +// ["if(0>>>0){}", "condition", "unexpected_a", ">>>", 5] +// ["if(0?0:0){}", "condition", "unexpected_a", "?", 5] +// ["if(0^0){}", "condition", "unexpected_a", "^", 5] +// ["if(0|0){}", "condition", "unexpected_a", "|", 5] +// ["if(\"aa\"){}", "condition", "unexpected_a", "aa", 4] +// ["if(typeof 0){}", "condition", "unexpected_a", "typeof", 4] +// ["if(~0){}", "condition", "unexpected_a", "~", 4] + + warn("unexpected_a", the_value); + break; + } + return the_value; + } + + function constant(id, type, value) { + +// Create a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud_prefix = ( + typeof value === "function" + ? value + : function () { + token_now.constant = true; + if (value !== undefined) { + token_now.value = value; + } + return token_now; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; + } + + function constant_Function() { + if (!option_dict.eval) { + +// test_cause: +// ["Function", "constant_Function", "unexpected_a", "Function", 1] + + warn("unexpected_a", token_now); + } else if (token_nxt.id !== "(") { + +// test_cause: +// [" +// /*jslint eval*/ +// Function +// ", "constant_Function", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "(", artifact()); + } + return token_now; + } + + function constant_arguments() { + +// test_cause: +// ["arguments", "constant_arguments", "unexpected_a", "arguments", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function constant_eval() { + if (!option_dict.eval) { + +// test_cause: +// ["eval", "constant_eval", "unexpected_a", "eval", 1] + + warn("unexpected_a", token_now); + } else if (token_nxt.id !== "(") { + +// test_cause: +// ["/*jslint eval*/\neval", "constant_eval", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "(", artifact()); + } + return token_now; + } + + function constant_ignore() { + +// test_cause: +// ["ignore", "constant_ignore", "unexpected_a", "ignore", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function constant_isInfinite() { + +// test_cause: +// ["isFinite", "constant_isInfinite", "expected_a_b", "isFinite", 1] + + warn("expected_a_b", token_now, "Number.isFinite", "isFinite"); + return token_now; + } + + function constant_isNaN() { + +// test_cause: +// ["isNaN(0)", "constant_isNaN", "number_isNaN", "isNaN", 1] + + warn("number_isNaN", token_now); + return token_now; + } + + function constant_this() { + if (!option_dict.this) { + +// test_cause: +// ["this", "constant_this", "unexpected_a", "this", 1] + + warn("unexpected_a", token_now); + } + return token_now; + } + + function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + let earlier; + let id = name.id; + +// Reserved words may not be enrolled. + + if (syntax_dict[id] !== undefined && id !== "ignore") { + +// test_cause: +// ["let undefined", "enroll", "reserved_a", "undefined", 5] + + warn("reserved_a", name); + return; + } + +// Has the name been enrolled in this context? + + earlier = functionage.context[id] || catchage.context[id]; + if (earlier) { + +// test_cause: +// ["let aa;let aa", "enroll", "redefinition_a_b", "1", 12] + + warn("redefinition_a_b", name, id, earlier.line); + return; + } + +// Has the name been enrolled in an outer context? + + function_stack.forEach(function ({ + context + }) { + earlier = context[id] || earlier; + }); + if (earlier && id === "ignore") { + if (earlier.role === "variable") { + +// test_cause: +// ["let ignore;function aa(ignore){}", "enroll", "redefinition_a_b", "1", 24] + + warn("redefinition_a_b", name, id, earlier.line); + } + } else if ( + earlier + && role !== "parameter" && role !== "function" + && (role !== "exception" || earlier.role !== "exception") + ) { + +// test_cause: +// [" +// function aa(){try{aa();}catch(aa){aa();}} +// ", "enroll", "redefinition_a_b", "1", 31] +// ["function aa(){var aa;}", "enroll", "redefinition_a_b", "1", 19] + + warn("redefinition_a_b", name, id, earlier.line); + } else if ( + option_dict.beta + && global_dict[id] + && role !== "parameter" + ) { + +// test_cause: +// ["let Array", "enroll", "redefinition_global_a_b", "Array", 5] + + warn("redefinition_global_a_b", name, global_dict[id], id); + } + +// Enroll it. + + Object.assign(name, { + dead: true, + init: false, + parent: ( + role === "exception" + ? catchage + : functionage + ), + readonly, + role, + used: 0 + }); + name.parent.context[id] = name; + } + + function infix(bp, id, f) { + +// Create an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led_infix = function (left) { + const the_token = token_now; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, parse_expression(bp)]; + return the_token; + }; + return the_symbol; + } + + function infix_dot(left) { + const the_token = token_now; + let name = token_nxt; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "flat" + && name.id !== "flatMap" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + +// test_cause: +// ["\"\".aa", "check_left", "unexpected_a", ".", 3] + + check_left(left, the_token); + } + if (!name.identifier) { + +// test_cause: +// ["aa.0", "infix_dot", "expected_identifier_a", "0", 4] + + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; + } + + function infix_fart_unwrapped() { + +// test_cause: +// ["aa=>0", "infix_fart_unwrapped", "wrap_fart_parameter", "=>", 3] + + return stop("wrap_fart_parameter", token_now); + } + + function infix_grave(left) { + const the_tick = prefix_tick(); + +// test_cause: +// ["0``", "check_left", "unexpected_a", "`", 2] + + check_left(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; + } + + function infix_lbracket(left) { + const the_token = token_now; + let name; + let the_subscript = parse_expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + name = survey(the_subscript); + +// PR-404 - Add new directive "subscript" to play nice with Google Closure. + + if (!option_dict.subscript && jslint_rgx_identifier.test(name)) { + +// test_cause: +// ["aa[`aa`]", "infix_lbracket", "subscript_a", "aa", 4] + + warn("subscript_a", the_subscript, name); + } + } + +// test_cause: +// ["0[0]", "check_left", "unexpected_a", "[", 2] + + check_left(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; + } + + function infix_lparen(left) { + const the_paren = token_now; + let ellipsis; + let the_argument; + if (left.id !== "function") { + +// test_cause: +// ["(0?0:0)()", "check_left", "unexpected_a", "(", 8] +// ["0()", "check_left", "unexpected_a", "(", 2] + + check_left(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (token_nxt.id !== ")") { + +// Parse/loop through each token in expression (...). + + while (true) { + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = parse_expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + +// test_cause: +// ["aa(0)", "infix_lparen", "free", "", 0] + + test_cause("free"); + the_paren.free = true; + if (the_argument.wrapped === true) { + +// test_cause: +// ["aa((0))", "infix_lparen", "unexpected_a", "(", 3] + + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + +// test_cause: +// ["aa()", "infix_lparen", "not_free", "", 0] +// ["aa(0,0)", "infix_lparen", "not_free", "", 0] + + test_cause("not_free"); + the_paren.free = false; + } + return the_paren; + } + + function infix_option_chain(left) { + const the_token = token_now; + let name = token_nxt; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + +// test_cause: +// ["(0+0)?.0", "infix_option_chain", "check_left", "", 0] + + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + test_cause("check_left"); + +// test_cause: +// ["(/./)?.0", "check_left", "unexpected_a", "?.", 6] +// ["\"aa\"?.0", "check_left", "unexpected_a", "?.", 5] +// ["aa=[]?.aa", "check_left", "unexpected_a", "?.", 6] + + check_left(left, the_token); + } + +// Issue #468 - Fix optional dynamic-property/function-call not recognized. + + if (name.id === "[" || name.id === "(") { + test_cause("dyn_prop_or_call"); + +// test_cause: +// ["aa?.(bb)", "infix_option_chain", "dyn_prop_or_call", "", 0] +// ["aa?.[bb]", "infix_option_chain", "dyn_prop_or_call", "", 0] + + return left; + } + if (!name.identifier) { + +// test_cause: +// ["aa?.0", "infix_option_chain", "expected_identifier_a", "0", 5] + + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; + } + + function infixr(bp, id) { + +// Create a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led_infix = function parse_infixr_led(left) { + const the_token = token_now; + +// test_cause: +// ["0**0", "parse_infixr_led", "led_infix", "", 0] + + test_cause("led_infix"); + the_token.arity = "binary"; + the_token.expression = [left, parse_expression(bp - 1)]; + return the_token; + }; + return the_symbol; + } + + function parse_expression(rbp, initial) { + +// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it +// is looking for ad hoc lint patterns. We add .fud_stmt to Pratt's model, which +// is like .nud_prefix except that it is only used on the first token of a +// statement. Having .fud_stmt makes it much easier to define statement-oriented +// languages like JavaScript. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// .nud_prefix Null denotation. The prefix handler. +// .fud_stmt First null denotation. The statement handler. +// .led_infix Left denotation. The infix/postfix handler. +// lbp Left binding power of infix operator. It tells us how strongly +// the operator binds to the argument at its left. +// rbp Right binding power. + +// It processes a nud_prefix (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop (it +// consumes tokens until it meets a token whose lbp <= rbp). Specifically, it +// means that it collects all tokens that bind together before returning to the +// operator that called it. It returns the expression's parse tree. + +// For example, "3 + 1 * 2 * 4 + 5" +// parses into +// { +// "id": "+", +// "expression": [ +// { +// "id": "+", +// "expression": [ +// { +// "id": "(number)", +// "value": "3" +// }, +// { +// "id": "*", +// "expression": [ +// { +// "id": "*", +// "expression": [ +// { +// "id": "(number)", +// "value": "1" +// }, +// { +// "id": "(number)", +// "value": "2" +// } +// ] +// }, +// { +// "id": "(number)", +// "value": "4" +// } +// ] +// } +// ] +// }, +// { +// "id": "(number)", +// "value": "5" +// } +// ] +// } + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement. + + if (!initial) { + advance(); + } + the_symbol = syntax_dict[token_now.id]; + if (the_symbol !== undefined && the_symbol.nud_prefix !== undefined) { + +// test_cause: +// ["0", "parse_expression", "symbol", "", 0] + + test_cause("symbol"); + left = the_symbol.nud_prefix(); + } else if (token_now.identifier) { + +// test_cause: +// ["aa", "parse_expression", "identifier", "", 0] + + test_cause("identifier"); + left = token_now; + left.arity = "variable"; + } else { + +// test_cause: +// ["!", "parse_expression", "unexpected_a", "(end)", 1] +// ["/./", "parse_expression", "unexpected_a", "/", 1] +// ["let aa=`${}`;", "parse_expression", "unexpected_a", "}", 11] + + return stop("unexpected_a", token_now); + } + +// Parse/loop through each symbol in expression. + + while (true) { + the_symbol = syntax_dict[token_nxt.id]; + if ( + the_symbol === undefined + || the_symbol.led_infix === undefined + || the_symbol.lbp <= rbp + ) { + break; + } + advance(); + left = the_symbol.led_infix(left); + } + return left; + } + + function parse_fart(the_fart) { + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + Object.assign(the_fart, { + arity: "binary", + context: empty(), + finally: 0, + level: functionage.level + 1, + loop: 0, + name: anon, + switch: 0, + try: 0 + }); + +// PR-384 - Relax warning "function_in_loop". +// +// if (functionage.loop > 0) { + +// // test_cause: +// // ["while(0){aa.map(()=>0);}", "parse_fart", "function_in_loop", "=>", 19] +// +// warn("function_in_loop", the_fart); +// } + +// Push the current function context and establish a new one. + + function_list.push(the_fart); + function_stack.push(functionage); + functionage = the_fart; + +// Parse the parameter list. + + prefix_function_parameter(the_fart); + advance("=>"); + +// The function's body is a block. + + if (token_nxt.id === "{") { + if (!option_dict.fart) { + +// test_cause: +// ["()=>{}", "parse_fart", "use_function_not_fart", "=>", 3] + + warn("use_function_not_fart", the_fart); + } + the_fart.block = block("body"); + } else if ( + syntax_dict[token_nxt.id] !== undefined + && syntax_dict[token_nxt.id].fud_stmt !== undefined + ) { + +// PR-384 - Bugfix - Fixes issue #379 - warn against naked-statement in fart. + +// test_cause: +// ["()=>delete aa", "parse_fart", "unexpected_a_after_b", "=>", 5] + + stop("unexpected_a_after_b", token_nxt, token_nxt.id, "=>"); + +// The function's body is an expression. + + } else { + the_fart.expression = parse_expression(0); + } + +// Restore the previous context. + + functionage = function_stack.pop(); + return the_fart; + } + + function parse_json() { + let container; + let is_dup; + let name; + let negative; + switch (token_nxt.id) { + case "(number)": + if (!jslint_rgx_json_number.test(token_nxt.value)) { + +// test_cause: +// ["[-.0]", "parse_json", "unexpected_a", ".", 3] +// ["[-0x0]", "parse_json", "unexpected_a", "0x0", 3] +// ["[.0]", "parse_json", "unexpected_a", ".", 2] +// ["[0x0]", "parse_json", "unexpected_a", "0x0", 2] + + warn("unexpected_a"); + } + advance("(number)"); + return token_now; + case "(string)": + if (token_nxt.quote !== "\"") { + +// test_cause: +// ["['']", "parse_json", "unexpected_a", "'", 2] + + warn("unexpected_a", token_nxt, token_nxt.quote); + } + advance("(string)"); + return token_now; + case "-": + negative = token_nxt; + negative.arity = "unary"; + advance("-"); + +// Recurse parse_json(). + + negative.expression = parse_json(); + return negative; + case "[": + +// test_cause: +// ["[]", "parse_json", "bracket", "", 0] + + test_cause("bracket"); + container = token_nxt; + container.expression = []; + advance("["); + if (token_nxt.id !== "]") { + while (true) { + +// Recurse parse_json(). + + container.expression.push(parse_json()); + if (token_nxt.id !== ",") { + +// test_cause: +// ["[0,0]", "parse_json", "comma", "", 0] + + test_cause("comma"); + break; + } + advance(","); + } + } + advance("]", container); + return container; + case "false": + case "null": + case "true": + +// test_cause: +// ["[false]", "parse_json", "constant", "", 0] +// ["[null]", "parse_json", "constant", "", 0] +// ["[true]", "parse_json", "constant", "", 0] + + test_cause("constant"); + advance(); + return token_now; + case "{": + +// test_cause: +// ["{}", "parse_json", "brace", "", 0] + + test_cause("brace"); + container = token_nxt; + +// Explicit empty-object required to detect "__proto__". + + is_dup = empty(); + container.expression = []; + advance("{"); + if (token_nxt.id !== "}") { + +// JSON +// Parse/loop through each property in {...}. + + while (true) { + if (token_nxt.quote !== "\"") { + +// test_cause: +// ["{0:0}", "parse_json", "unexpected_a", "0", 2] + + warn( + "unexpected_a", + token_nxt, + token_nxt.quote + ); + } + name = token_nxt; + advance("(string)"); + if (is_dup[token_now.value] !== undefined) { + +// test_cause: +// ["{\"aa\":0,\"aa\":0}", "parse_json", "duplicate_a", "aa", 9] + + warn("duplicate_a", token_now); + } else if (token_now.value === "__proto__") { + +// test_cause: +// ["{\"__proto__\":0}", "parse_json", "weird_property_a", "__proto__", 2] + + warn("weird_property_a", token_now); + } else { + is_dup[token_now.value] = token_now; + } + advance(":"); + container.expression.push( + +// Recurse parse_json(). + + Object.assign(parse_json(), { + label: name + }) + ); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance("}", container); + return container; + default: + +// test_cause: +// ["[undefined]", "parse_json", "unexpected_a", "undefined", 2] + + stop("unexpected_a"); + } + } + + function parse_statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token_now.identifier && token_nxt.id === ":") { + the_label = token_now; + if (the_label.id === "ignore") { + +// test_cause: +// ["ignore:", "parse_statement", "unexpected_a", "ignore", 1] + + warn("unexpected_a", the_label); + } + advance(":"); + switch (token_nxt.id) { + case "do": + case "for": + case "switch": + case "while": + +// test_cause: +// ["aa:do{}", "parse_statement", "the_statement_label", "do", 0] +// ["aa:for{}", "parse_statement", "the_statement_label", "for", 0] +// ["aa:switch{}", "parse_statement", "the_statement_label", "switch", 0] +// ["aa:while{}", "parse_statement", "the_statement_label", "while", 0] + + test_cause("the_statement_label", token_nxt.id); + enroll(the_label, "label", true); + the_label.dead = false; + the_label.init = true; + the_statement = parse_statement(); + functionage.statement_prv = the_statement; + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + +// test_cause: +// ["aa:", "parse_statement", "unexpected_label_a", "aa", 1] + + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token_now; + first.statement = true; + the_symbol = syntax_dict[first.id]; + if ( + the_symbol !== undefined + && the_symbol.fud_stmt !== undefined + +// PR-318 - Bugfix - Fixes issues #316, #317 - dynamic-import(). + + && !(the_symbol.id === "import" && token_nxt.id === "(") + ) { + the_symbol.disrupt = false; + the_symbol.statement = true; + token_now.arity = "statement"; + the_statement = the_symbol.fud_stmt(); + functionage.statement_prv = the_statement; + } else { + +// It is an expression statement. + + the_statement = parse_expression(0, true); + functionage.statement_prv = the_statement; + if (the_statement.wrapped && the_statement.id !== "(") { + +// test_cause: +// ["(0)", "parse_statement", "unexpected_a", "(", 1] + + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; + } + + function parse_statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const statement_list = []; + let a_statement; + let disrupt = false; + +// Parse/loop each statement until a statement-terminator is reached. + + while (true) { + switch (token_nxt.id) { + case "(end)": + case "case": + case "default": + case "else": + case "}": + +// test_cause: +// [";", "parse_statements", "closer", "", 0] +// ["case", "parse_statements", "closer", "", 0] +// ["default", "parse_statements", "closer", "", 0] +// ["else", "parse_statements", "closer", "", 0] +// ["}", "parse_statements", "closer", "", 0] + + test_cause("closer"); + return statement_list; + } + a_statement = parse_statement(); + statement_list.push(a_statement); + if (disrupt) { + +// test_cause: +// ["while(0){break;0;}", "parse_statements", "unreachable_a", "0", 16] + + warn("unreachable_a", a_statement); + } + disrupt = a_statement.disrupt; + } + } + + function postassign(id) { + +// Create one of the postassign operators. + + const the_symbol = symbol(id, 150); + the_symbol.led_infix = function (left) { + token_now.expression = left; + token_now.arity = "postassign"; + check_mutation(token_now.expression); + return token_now; + }; + return the_symbol; + } + + function preassign(id) { + +// Create one of the preassign operators. + + const the_symbol = symbol(id); + the_symbol.nud_prefix = function () { + const the_token = token_now; + the_token.arity = "preassign"; + the_token.expression = parse_expression(150); + check_mutation(the_token.expression); + return the_token; + }; + return the_symbol; + } + + function prefix(id, f) { + +// Create a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud_prefix = function () { + const the_token = token_now; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = parse_expression(150); + return the_token; + }; + return the_symbol; + } + + function prefix_assign_divide() { + +// test_cause: +// ["/=", "prefix_assign_divide", "expected_a_b", "/=", 1] + + stop("expected_a_b", token_now, "/\\=", "/="); + } + + function prefix_async() { + let the_async = token_now; + let the_function; + token_nxt.arity = the_async.arity; + +// PR-414 - Parse async fart. + + if (token_nxt.fart) { + advance("("); + the_function = Object.assign(token_now.fart, { + async: 1 + }); + if (!option_dict.fart) { + +// test_cause: +// ["async()=>0", "prefix_async", "use_function_not_fart", "=>", 8] + + warn("use_function_not_fart", the_function); + } + prefix_lparen(); + +// Parse async function. + + } else { + advance("function"); + the_function = Object.assign(token_now, { + async: 1 + }); + prefix_function(); + } + if (the_function.async === 1) { + +// test_cause: +// [" +// async function aa(){} +// ", "prefix_async", "missing_await_statement", "function", 7] + + warn("missing_await_statement", the_function); + } + return the_function; + } + + function prefix_await() { + const the_await = token_now; + +// PR-370 - Add top-level-await support. + + if (functionage.async === 0 && functionage !== token_global) { + +// test_cause: +// ["function aa(){aa=await 0;}", "prefix_await", "unexpected_a", "await", 18] +// ["function aa(){await 0;}", "prefix_await", "unexpected_a", "await", 15] + + warn("unexpected_a", the_await); + } else { + functionage.async += 1; + } + if (the_await.arity === "statement") { + +// PR-405 - Bugfix - fix expression after "await" mis-identified as statement. + + the_await.expression = parse_expression(150); + semicolon(); + } else { + the_await.expression = parse_expression(150); + } + return the_await; + } + + function prefix_fart() { + +// test_cause: +// ["=>0", "prefix_fart", "expected_a_before_b", "=>", 1] + + return stop("expected_a_before_b", token_now, "()", "=>"); + } + + function prefix_function(the_function) { + let name = the_function && the_function.name; + if (the_function === undefined) { + the_function = token_now; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!token_nxt.identifier) { + +// test_cause: +// ["function(){}", "prefix_function", "expected_identifier_a", "(", 9] +// ["function*aa(){}", "prefix_function", "expected_identifier_a", "*", 9] + + return stop("expected_identifier_a"); + } + name = token_nxt; + enroll(name, "variable", true); + the_function.name = Object.assign(name, { + calls: empty(), + +// PR-331 - Bugfix - Fixes issue #272 - function hoisting not allowed. + + dead: false, + init: true + }); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + the_function.name = anon; + if (token_nxt.identifier) { + name = token_nxt; + the_function.name = name; + advance(); + } + } + } + +// Probably deadcode. +// if (mode_mega) { +// warn("unexpected_a", the_function); +// } +// jslint_assert(!mode_mega, `Expected !mode_mega.`); + +// PR-378 - Relax warning "function_in_loop". +// +// // Don't create functions in loops. It is inefficient, and it can lead to +// // scoping errors. +// +// if (functionage.loop > 0) { +// +// // test_cause: +// // [" +// // while(0){aa.map(function(){});} +// // ", "prefix_function", "function_in_loop", "function", 17] +// +// warn("function_in_loop", the_function); +// } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + Object.assign(the_function, { + async: the_function.async || 0, + context: empty(), + finally: 0, + level: functionage.level + 1, + loop: 0, + statement_prv: undefined, + switch: 0, + try: 0 + }); + if (the_function.arity !== "statement" && typeof name === "object") { + +// test_cause: +// ["let aa=function bb(){return;};", "prefix_function", "expression", "bb", 0] + + test_cause("expression", name.id); + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// PR-334 - Bugfix - fix function-redefinition not warned inside function-call. +// Push the current function context and establish a new one. + + function_list.push(the_function); + function_stack.push(functionage); + functionage = the_function; + +// Parse the parameter list. + + advance("("); + token_now.arity = "function"; + prefix_function_parameter(the_function); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && token_nxt.line === token_now.line + ) { + +// test_cause: +// ["function aa(){}0", "prefix_function", "unexpected_a", "0", 16] + + return stop("unexpected_a"); + } + if ( + token_nxt.id === "." + || token_nxt.id === "?." + +// PR-459 - Allow destructuring-assignment after function-definition. + + // || token_nxt.id === "[" + ) { + +// test_cause: +// ["function aa(){}\n.aa", "prefix_function", "unexpected_a", ".", 1] +// ["function aa(){}\n?.aa", "prefix_function", "unexpected_a", "?.", 1] + + warn("unexpected_a"); + } + +// Check functions are ordered. + + check_ordered( + "function", + function_list.slice( + function_list.indexOf(the_function) + 1 + ).map(function ({ + level, + name + }) { + return (level === the_function.level + 1) && name; + }).filter(function (name) { + return option_dict.beta && name && name.id; + }) + ); + +// Restore the previous context. + + functionage = function_stack.pop(); + return the_function; + } + + function prefix_function_parameter(the_function) { + +// This function will parse input at beginning of + + let optional; + let parameters = []; + let signature = ["("]; + let subparam; + function param_enroll(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + +// test_cause: +// ["([aa])=>0", "param_enroll", "use_function_not_fart", "=>", 7] +// ["({aa})=>0", "param_enroll", "use_function_not_fart", "=>", 7] + + if (the_function.id === "=>" && !option_dict.fart) { + warn("use_function_not_fart", the_function); + } + +// Recurse param_enroll(). + + name.names.forEach(param_enroll); + } + } + function param_parse() { + let ellipsis = false; + let param; + if (token_nxt.id === "{") { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,{}){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + param = token_nxt; + param.names = []; + advance("{"); + signature.push("{"); + while (true) { + subparam = token_nxt; + if (!subparam.identifier) { + +// test_cause: +// ["function aa(aa=0,{}){}", "param_parse", "expected_identifier_a", "}", 19] +// ["function aa({0}){}", "param_parse", "expected_identifier_a", "0", 14] + + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (token_nxt.id === ":") { + advance(":"); + advance(); + token_now.label = subparam; + subparam = token_now; + if (!subparam.identifier) { + +// test_cause: +// ["function aa({aa:0}){}", "param_parse", "expected_identifier_a", "}", 18] + + return stop( + "expected_identifier_a", + token_nxt + ); + } + } + +// test_cause: +// ["function aa({aa=aa},aa){}", "param_parse", "equal", "", 0] + + test_cause("equal"); + if (token_nxt.id === "=") { + advance("="); + subparam.expression = parse_expression(); + param.open = true; + } + param.names.push(subparam); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + } else { + break; + } + } + parameters.push(param); + +// test_cause: +// [" +// function aa({bb,aa}){} +// ", "check_ordered", "expected_a_b_before_c_d", "aa", 17] + + check_ordered("parameter", param.names); + advance("}"); + signature.push("}"); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } else if (token_nxt.id === "[") { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,[]){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + param = token_nxt; + param.names = []; + advance("["); + signature.push("[]"); + while (true) { + subparam = token_nxt; + if (!subparam.identifier) { + +// test_cause: +// ["function aa(aa=0,[]){}", "param_parse", "expected_identifier_a", "]", 19] + + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + +// test_cause: +// ["function aa([aa=aa],aa){}", "param_parse", "id", "", 0] + + test_cause("id"); + if (token_nxt.id === "=") { + advance("="); + subparam.expression = parse_expression(); + param.open = true; + } + if (token_nxt.id === ",") { + advance(","); + } else { + break; + } + } + parameters.push(param); + advance("]"); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } else { + if (token_nxt.id === "...") { + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,...){}", "param_parse", "required_a_optional_b", "aa", 21] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + } + if (!token_nxt.identifier) { + +// test_cause: +// ["function aa(0){}", "param_parse", "expected_identifier_a", "0", 13] + + return stop("expected_identifier_a"); + } + param = token_nxt; + parameters.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (token_nxt.id === "=") { + optional = param; + advance("="); + param.expression = parse_expression(0); + } else { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,bb){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } + } + } + +// test_cause: +// ["function aa(){}", "prefix_function_parameter", "opener", "(", 0] + + test_cause("opener", token_now.id); + token_now.free = false; + if (token_nxt.id !== ")" && token_nxt.id !== "(end)") { + param_parse(); + } + advance(")"); + signature.push(")"); + parameters.forEach(param_enroll); + the_function.parameters = parameters; + the_function.signature = signature.join(""); + } + + function prefix_lbrace() { + const seen = empty(); + const the_brace = token_now; + let extra; + let full; + let id; + let name; + let the_colon; + let value; + the_brace.expression = []; + if (token_nxt.id !== "}") { + +// Parse/loop through each property in {...}. + + while (true) { + name = token_nxt; + advance(); + if ( + (name.id === "get" || name.id === "set") + && token_nxt.identifier + ) { + if (!option_dict.getset) { + +// test_cause: +// ["aa={get aa(){}}", "prefix_lbrace", "unexpected_a", "get", 5] + + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + token_nxt.id; + name = token_nxt; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + +// test_cause: +// ["aa={get aa(){},get aa(){}}", "prefix_lbrace", "duplicate_a", "aa", 20] + + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else if (name.id === "`") { + +// test_cause: +// ["aa={`aa`:0}", "prefix_lbrace", "unexpected_a", "`", 5] + + stop("unexpected_a", name); + + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + +// test_cause: +// ["aa={aa,aa}", "prefix_lbrace", "duplicate_a", "aa", 8] + + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (token_nxt.id === "}" || token_nxt.id === ",") { + if (typeof extra === "string") { + +// test_cause: +// ["aa={get aa}", "prefix_lbrace", "closer", "", 0] + + test_cause("closer"); + advance("("); + } + value = parse_expression(Infinity, true); + } else if (token_nxt.id === "(") { + +// test_cause: +// ["aa={aa()}", "prefix_lbrace", "paren", "", 0] +// ["aa={get aa(){}}", "prefix_lbrace", "paren", "", 0] + + test_cause("paren"); + value = prefix_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + +// test_cause: +// ["aa={get aa.aa}", "prefix_lbrace", "paren", "", 0] + + test_cause("paren"); + advance("("); + } + the_colon = token_nxt; + advance(":"); + value = parse_expression(0); + if ( + value.id === name.id + && value.id !== "function" + ) { + +// test_cause: +// ["aa={aa:aa}", "prefix_lbrace", "unexpected_a", ": aa", 7] + + warn("unexpected_a", the_colon, ": " + name.id); + } + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + +// test_cause: +// ["aa={\"aa\":0}", "prefix_lbrace", "colon", "", 0] + + test_cause("colon"); + advance(":"); + value = parse_expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (token_nxt.id !== ",") { + break; + } + +// test_cause: +// ["aa={\"aa\":0,\"bb\":0}", "prefix_lbrace", "comma", "", 0] + + test_cause("comma"); + advance(","); + if (token_nxt.id === "}") { + +// test_cause: +// ["let aa={aa:0,}", "prefix_lbrace", "unexpected_a", ",", 13] + + warn("unexpected_a", token_now); + break; + } + } + } + +// test_cause: +// ["aa={bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8] + + check_ordered( + "property", + the_brace.expression.map(function ({ + label + }) { + return label; + }) + ); + advance("}"); + return the_brace; + } + + function prefix_lbracket() { + const the_token = token_now; + let element; + let ellipsis; + the_token.expression = []; + if (token_nxt.id !== "]") { + +// Parse/loop through each element in [...]. + + while (true) { + ellipsis = false; + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + element = parse_expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (token_nxt.id !== ",") { + break; + } + advance(","); + if (token_nxt.id === "]") { + +// test_cause: +// ["let aa=[0,]", "prefix_lbracket", "unexpected_a", ",", 10] + + warn("unexpected_a", token_now); + break; + } + } + } + advance("]"); + return the_token; + } + + function prefix_lparen() { + let the_paren = token_now; + let the_value; + +// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart. + + if (token_now.fart) { + return parse_fart(token_now.fart); + } + +// test_cause: +// ["(0)", "prefix_lparen", "expr", "", 0] + + test_cause("expr"); + the_paren.free = true; + the_value = parse_expression(0); + if (the_value.wrapped === true) { + +// test_cause: +// ["((0))", "prefix_lparen", "unexpected_a", "(", 1] + + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + return the_value; + } + + function prefix_new() { + const the_new = token_now; + let right; + right = parse_expression(160); + if (token_nxt.id !== "(") { + +// test_cause: +// ["new aa", "prefix_new", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "()", artifact()); + } + the_new.expression = right; + return the_new; + } + + function prefix_tick() { + const the_tick = token_now; + the_tick.value = []; + the_tick.expression = []; + if (token_nxt.id !== "`") { + +// Parse/loop through each token in `${...}`. + + while (true) { + advance("(string)"); + the_tick.value.push(token_now); + if (token_nxt.id !== "${") { + break; + } + advance("${"); + +// test_cause: +// ["let aa=`${}`;", "prefix_tick", "${", "", 0] + + test_cause("${"); + the_tick.expression.push(parse_expression(0)); + advance("}"); + } + } + advance("`"); + return the_tick; + } + + function prefix_void() { + const the_void = token_now; + +// test_cause: +// ["void 0", "prefix_void", "unexpected_a", "void", 1] +// ["void", "prefix_void", "unexpected_a", "void", 1] + + warn("unexpected_a", the_void); + the_void.expression = parse_expression(0); + return the_void; + } + + function semicolon() { + +// Try to match a semicolon. + + if (token_nxt.id === ";") { + advance(";"); + } else { + +// test_cause: +// ["0", "semicolon", "expected_a_b", "(end)", 1] + + warn_at( + "expected_a_b", + token_now.line, + token_now.thru + 1, + ";", + artifact() + ); + } + anon = "anonymous"; + } + + function stmt(id, fud_stmt) { + +// Create a statement. + + const the_symbol = symbol(id); + the_symbol.fud_stmt = fud_stmt; + return the_symbol; + } + + function stmt_break() { + const the_break = token_now; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + +// test_cause: +// ["break", "stmt_break", "unexpected_a", "break", 1] + + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (token_nxt.identifier && token_now.line === token_nxt.line) { + the_label = functionage.context[token_nxt.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + if (the_label !== undefined && the_label.dead) { + +// test_cause: +// ["aa:{function aa(aa){break aa;}}", "stmt_break", "out_of_scope_a", "aa", 27] + + warn("out_of_scope_a"); + } else { + +// test_cause: +// ["aa:{break aa;}", "stmt_break", "not_label_a", "aa", 11] + + warn("not_label_a"); + } + } else { + the_label.used += 1; + } + the_break.label = token_nxt; + advance(); + } + advance(";"); + return the_break; + } + + function stmt_continue() { + const the_continue = token_now; + if (functionage.loop < 1 || functionage.finally > 0) { + +// test_cause: +// ["continue", "stmt_continue", "unexpected_a", "continue", 1] +// [" +// function aa(){while(0){try{}finally{continue}}} +// ", "stmt_continue", "unexpected_a", "continue", 37] + + warn("unexpected_a", the_continue); + } + check_not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; + } + + function stmt_debugger() { + const the_debug = token_now; + if (!option_dict.devel) { + +// test_cause: +// ["debugger", "stmt_debugger", "unexpected_a", "debugger", 1] + + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; + } + + function stmt_delete() { + const the_token = token_now; + const the_value = parse_expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + +// test_cause: +// ["delete 0", "stmt_delete", "expected_a_b", "0", 8] + + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; + } + + function stmt_do() { + const the_do = token_now; + check_not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + +// test_cause: +// ["function aa(){do{break;}while(0)}", "stmt_do", "weird_loop", "do", 15] + + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; + } + + function stmt_export() { + let export_list = []; + let the_export = token_now; + let the_id; + let the_name; + let the_thing; + + the_export.expression = []; + if (token_nxt.id === "default") { + if (export_dict.default !== undefined) { + +// test_cause: +// [" +// export default 0;export default 0 +// ", "stmt_export", "duplicate_a", "default", 25] + + warn("duplicate_a"); + } + advance("default"); + the_thing = parse_expression(0); + if ( + the_thing.id !== "(" + || the_thing.expression[0].id !== "." + || the_thing.expression[0].expression.id !== "Object" + || the_thing.expression[0].name.id !== "freeze" + ) { + +// test_cause: +// ["export default {}", "stmt_export", "freeze_exports", "{", 16] + + warn("freeze_exports", the_thing); + +// PR-301 - Bugfix - Fixes issues #282 - optional-semicolon. + + } else { + +// test_cause: +// [" +// export default Object.freeze({}) +// ", "semicolon", "expected_a_b", "(end)", 32] + + semicolon(); + } + export_dict.default = the_thing; + the_export.expression.push(the_thing); + } else { + +// PR-439 - Add grammar for "export async function ...". + + if (token_nxt.id === "function" || token_nxt.id === "async") { + +// test_cause: +// ["export async function aa(){}", "stmt_export", "freeze_exports", "async", 8] +// ["export function aa(){}", "stmt_export", "freeze_exports", "function", 8] + + warn("freeze_exports"); + the_thing = parse_statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (export_dict[the_id] !== undefined) { + +// test_cause: +// [" +// let aa;export{aa};export function aa(){} +// ", "stmt_export", "duplicate_a", "aa", 35] + + warn("duplicate_a", the_name); + } + export_dict[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + token_nxt.id === "var" + || token_nxt.id === "let" + || token_nxt.id === "const" + ) { + +// test_cause: +// ["export const", "stmt_export", "unexpected_a", "const", 8] +// ["export let", "stmt_export", "unexpected_a", "let", 8] +// ["export var", "stmt_export", "unexpected_a", "var", 8] + + warn("unexpected_a"); + parse_statement(); + } else if (token_nxt.id === "{") { + +// test_cause: +// ["export {}", "stmt_export", "advance{", "", 0] + + test_cause("advance{"); + advance("{"); + while (true) { + if (!token_nxt.identifier) { + +// test_cause: +// ["export {}", "stmt_export", "expected_identifier_a", "}", 9] + + stop("expected_identifier_a"); + } + the_id = token_nxt.id; + export_list.push(token_nxt); + the_name = token_global.context[the_id]; + if (the_name === undefined) { + +// test_cause: +// ["export {aa}", "stmt_export", "unexpected_a", "aa", 9] + + warn("unexpected_a"); + } else { + the_name.used += 1; + if (export_dict[the_id] !== undefined) { + +// test_cause: +// ["let aa;export{aa,aa}", "stmt_export", "duplicate_a", "aa", 18] + + warn("duplicate_a"); + } + export_dict[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + if (token_nxt.id === ",") { + advance(","); + } else { + break; + } + } + +// PR-439 - Check exported properties are ordered. + +// test_cause: +// ["export {bb, aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 13] + + check_ordered("export", export_list); + advance("}"); + semicolon(); + } else { + +// test_cause: +// ["export", "stmt_export", "unexpected_a", "(end)", 1] + + stop("unexpected_a"); + } + } + state.mode_module = true; + return the_export; + } + + function stmt_for() { + let first; + let the_for = token_now; + if (!option_dict.for) { + +// test_cause: +// ["for", "stmt_for", "unexpected_a", "for", 1] + + warn("unexpected_a", the_for); + } + check_not_top_level(the_for); + functionage.loop += 1; + advance("("); + token_now.free = true; + if (token_nxt.id === ";") { + +// test_cause: +// ["for(;;){}", "stmt_for", "expected_a_b", "for (;", 1] + + return stop("expected_a_b", the_for, "while (", "for (;"); + } + switch (token_nxt.id) { + case "const": + case "let": + case "var": + +// test_cause: +// ["for(const aa in aa){}", "stmt_for", "unexpected_a", "const", 5] + + return stop("unexpected_a"); + } + first = parse_expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + +// test_cause: +// ["for(0 in aa){}", "stmt_for", "bad_assignment_a", "0", 5] + + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = parse_expression(0); + advance(";"); + the_for.inc = parse_expression(0); + if (the_for.inc.id === "++") { + +// test_cause: +// ["for(aa;aa;aa++){}", "stmt_for", "expected_a_b", "++", 13] + + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + +// test_cause: +// [" +// /*jslint for*/ +// function aa(bb,cc){for(0;0;0){break;}} +// ", "stmt_for", "weird_loop", "for", 20] + + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; + } + + function stmt_if() { + const the_if = token_now; + let the_else; + the_if.expression = condition(); + the_if.block = block(); + if (token_nxt.id === "else") { + advance("else"); + the_else = token_now; + the_if.else = ( + token_nxt.id === "if" + ? parse_statement() + : block() + ); + +// test_cause: +// ["if(0){0}else if(0){0}", "stmt_if", "else", "", 0] +// ["if(0){0}else{0}", "stmt_if", "else", "", 0] + + test_cause("else"); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + +// test_cause: +// ["if(0){break;}else{break;}", "stmt_if", "disrupt", "", 0] + + test_cause("disrupt"); + the_if.disrupt = true; + } else { + +// test_cause: +// ["if(0){break;}else{}", "stmt_if", "unexpected_a", "else", 14] + + warn("unexpected_a", the_else); + } + } + } + return the_if; + } + + function stmt_import() { + const the_import = token_now; + let name; + let names; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// if (typeof state.mode_module === "object") { +// +// // test_cause: +// // [" +// // /*global aa*/ +// // import aa from "aa" +// // ", "stmt_import", "unexpected_directive_a", "global", 1] +// +// warn( +// "unexpected_directive_a", +// state.mode_module, +// state.mode_module.directive +// ); +// } + + state.mode_module = true; + +// PR-436 - Add grammar for side-effect import-statement. + + if (token_nxt.id === "(string)") { + +// test_cause: +// ["import \"./aa.mjs\";", "stmt_import", "import_side_effect", "", 0] + + test_cause("import_side_effect"); + warn("expected_a_b", token_nxt, "{", artifact()); + advance(); + semicolon(); + return the_import; + } + if (token_nxt.identifier) { + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// ["import ignore from \"aa\"", "stmt_import", "unexpected_a", "ignore", 8] + + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + names = []; + advance("{"); + if (token_nxt.id !== "}") { + while (true) { + if (!token_nxt.identifier) { + +// test_cause: +// ["import {", "stmt_import", "expected_identifier_a", "(end)", 1] + + stop("expected_identifier_a"); + } + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// ["import {ignore} from \"aa\"", "stmt_import", "unexpected_a", "ignore", 9] + + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token_now; + if (!jslint_rgx_module.test(token_now.value)) { + +// test_cause: +// ["import aa from \"!aa\"", "stmt_import", "bad_module_name_a", "!aa", 16] + + warn("bad_module_name_a", token_now); + } + import_list.push(token_now.value); + semicolon(); + return the_import; + } + + function stmt_lbrace() { + +// test_cause: +// [";{}", "stmt_lbrace", "naked_block", "{", 2] +// ["class aa{}", "stmt_lbrace", "naked_block", "{", 9] + + warn("naked_block", token_now); + return block("naked"); + } + + function stmt_return() { + const the_return = token_now; + check_not_top_level(the_return); + if (functionage.finally > 0) { + +// test_cause: +// [" +// function aa(){try{}finally{return;}} +// ", "stmt_return", "unexpected_a", "return", 28] + + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (token_nxt.id !== ";" && the_return.line === token_nxt.line) { + the_return.expression = parse_expression(10); + } + advance(";"); + return the_return; + } + + function stmt_semicolon() { + +// test_cause: +// [";", "stmt_semicolon", "unexpected_a", ";", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function stmt_switch() { + const the_cases = []; + const the_switch = token_now; + let dups = []; + let exp; + let last; + let stmts; + let the_case; + let the_default; + let the_disrupt = true; + let the_last; + function is_dup(thing) { + return is_equal(thing, exp); + } + check_not_top_level(the_switch); + if (functionage.finally > 0) { + +// test_cause: +// [" +// function aa(){try{}finally{switch(0){}}} +// ", "stmt_switch", "unexpected_a", "switch", 28] + + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token_now.free = true; + the_switch.expression = parse_expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + while (true) { + +// Loop through cases with breaks. + + the_case = token_nxt; + the_case.arity = "statement"; + the_case.expression = []; + while (true) { + +// Loop through fallthrough cases. + + advance("case"); + token_now.switch = true; + exp = parse_expression(0); + if (dups.some(is_dup)) { + +// test_cause: +// [" +// switch(0){case 0:break;case 0:break} +// ", "stmt_switch", "unexpected_a", "0", 29] + + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (token_nxt.id !== "case") { + break; + } + } + +// test_cause: +// [" +// switch(0){case 1:case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 23] +// [" +// switch(0){case "aa":case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 26] +// [" +// switch(0){case "bb":case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 26] +// [" +// switch(0){case aa:case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24] +// [" +// switch(0){case bb:case aa:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24] + + check_ordered_case(the_case.expression); + stmts = parse_statements(); + if (stmts.length < 1) { + +// test_cause: +// [" +// switch(0){case 0:default:} +// ", "stmt_switch", "expected_statements_a", "default", 18] +// ["switch(0){case 0:}", "stmt_switch", "expected_statements_a", "}", 18] + + warn("expected_statements_a"); + +// PR-359 - Bugfix - Fixes issue #358 - switch-statement crashes jslint. + + break; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn("expected_a_before_b", token_nxt, "break;", artifact()); + } + if (token_nxt.id !== "case") { + break; + } + } + +// test_cause: +// [" +// switch(0){case 1:break;case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 29] +// [" +// switch(0){case "aa":break;case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 32] +// [" +// switch(0){case "bb":break;case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 32] +// [" +// switch(0){case aa:break;case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30] +// [" +// switch(0){case bb:break;case aa:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30] + + check_ordered_case(the_cases.map(function ({ + expression + }) { + return expression[0]; + })); + dups = undefined; + if (token_nxt.id === "default") { + the_default = token_nxt; + advance("default"); + token_now.switch = true; + advance(":"); + the_switch.else = parse_statements(); + if (the_switch.else.length < 1) { + +// test_cause: +// [" +// switch(0){case 0:break;default:} +// ", "stmt_switch", "unexpected_a", "default", 24] + + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + the_last = the_switch.else[ + the_switch.else.length - 1 + ]; + if ( + the_last.id === "break" + && the_last.label === undefined + ) { + +// test_cause: +// [" +// switch(0){case 0:break;default:break;} +// ", "stmt_switch", "unexpected_a", "break", 32] + + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; + } + + function stmt_throw() { + const the_throw = token_now; + the_throw.disrupt = true; + the_throw.expression = parse_expression(10); + semicolon(); + if (functionage.try > 0) { + +// test_cause: +// ["try{throw 0}catch(){}", "stmt_throw", "unexpected_a", "throw", 5] + + warn("unexpected_a", the_throw); + } + return the_throw; + } + + function stmt_try() { + const the_try = token_now; + let ignored; + let the_catch; + let the_disrupt; + if (functionage.try > 0) { + +// test_cause: +// ["try{try{}catch(){}}catch(){}", "stmt_try", "unexpected_a", "try", 5] + + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (token_nxt.id === "catch") { + ignored = "ignore"; + the_catch = token_nxt; + the_try.catch = the_catch; + advance("catch"); + +// Create new catch-scope for catch-parameter. + + catch_stack.push(catchage); + catchage = the_catch; + catch_list.push(catchage); + the_catch.context = empty(); + if (token_nxt.id === "(") { + advance("("); + if (!token_nxt.identifier) { + +// test_cause: +// ["try{}catch(){}", "stmt_try", "expected_identifier_a", ")", 12] + + return stop("expected_identifier_a"); + } + if (token_nxt.id !== "ignore") { + ignored = undefined; + the_catch.name = token_nxt; + enroll(token_nxt, "exception", true); + } + advance(); + advance(")"); + } + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + +// Restore previous catch-scope after catch-block. + + catchage = catch_stack.pop(); + +// PR-404 - Relax warning about missing `catch` in `try...finally` statement. +// +// } else { +// +// // test_cause: +// // ["try{}finally{break;}", "stmt_try", "expected_a_before_b", "finally", 6] +// +// warn("expected_a_before_b", token_nxt, "catch", artifact()); + + } + if (token_nxt.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; + } + + function stmt_var() { + let ellipsis; + let mode_const; + let name; + let the_brace; + let the_bracket; + let the_variable = token_now; + let variable_prv; + mode_const = the_variable.id === "const"; + the_variable.names = []; + +// A program may use var or let, but not both. + + if (!mode_const) { + if (mode_var === undefined) { + mode_var = the_variable.id; + } else if (the_variable.id !== mode_var) { + +// test_cause: +// ["let aa;var aa", "stmt_var", "expected_a_b", "var", 8] + + warn("expected_a_b", the_variable, mode_var, the_variable.id); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + +// test_cause: +// ["switch(0){case 0:var aa}", "stmt_var", "var_switch", "var", 18] + + warn("var_switch", the_variable); + } + switch ( + Boolean(functionage.statement_prv) + && functionage.statement_prv.id + ) { + case "const": + case "let": + case "var": + +// test_cause: +// ["const aa=0;const bb=0;", "stmt_var", "var_prv", "const", 0] +// ["let aa=0;let bb=0;", "stmt_var", "var_prv", "let", 0] +// ["var aa=0;var bb=0;", "stmt_var", "var_prv", "var", 0] + + test_cause("var_prv", functionage.statement_prv.id); + variable_prv = functionage.statement_prv; + break; + case "import": + +// test_cause: +// ["import aa from \"aa\";\nlet bb=0;", "stmt_var", "import_prv", "", 0] + + test_cause("import_prv"); + break; + case false: + break; + default: + if ( + (option_dict.beta && !option_dict.variable) + || the_variable.id === "var" + ) { + +// test_cause: +// ["console.log();let aa=0;", "stmt_var", "var_on_top", "let", 15] +// ["console.log();var aa=0;", "stmt_var", "var_on_top", "var", 15] +// ["try{aa();}catch(aa){var aa=0;}", "stmt_var", "var_on_top", "var", 21] +// ["while(0){var aa;}", "stmt_var", "var_on_top", "var", 10] + + warn("var_on_top", token_now); + } + } + while (true) { + if (token_nxt.id === "{") { + if (the_variable.id === "var") { + +// test_cause: +// ["var{aa}=0", "stmt_var", "unexpected_a", "var", 1] + + warn("unexpected_a", the_variable); + } + the_brace = token_nxt; + advance("{"); + while (true) { + name = token_nxt; + if (!name.identifier) { + +// test_cause: +// ["let {0}", "stmt_var", "expected_identifier_a", "0", 6] + + return stop("expected_identifier_a"); + } + survey(name); + advance(); + if (token_nxt.id === ":") { + advance(":"); + if (!token_nxt.identifier) { + +// test_cause: +// ["let {aa:0}", "stmt_var", "expected_identifier_a", "0", 9] +// ["let {aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 9] + + return stop("expected_identifier_a"); + } + +// PR-363 - Bugfix +// Add test against false-warning in code +// '/*jslint node*/\nlet {aa:bb} = {}; bb();'. +// +// token_nxt.label = name; +// the_variable.names.push(token_nxt); +// enroll(token_nxt, "variable", mode_const); + + name = token_nxt; + the_variable.names.push(name); + survey(name); + enroll(name, "variable", mode_const); + + advance(); + the_brace.open = true; + } else { + the_variable.names.push(name); + enroll(name, "variable", mode_const); + } + name.dead = false; + name.init = true; + if (token_nxt.id === "=") { + +// test_cause: +// ["let {aa=0}", "stmt_var", "assign", "", 0] + + test_cause("assign"); + advance("="); + name.expression = parse_expression(); + the_brace.open = true; + } + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + +// test_cause: +// ["let{bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8] + + check_ordered(the_variable.id, the_variable.names); + advance("}"); + advance("="); + the_variable.expression = parse_expression(0); + } else if (token_nxt.id === "[") { + if (the_variable.id === "var") { + +// test_cause: +// ["var[aa]=0", "stmt_var", "unexpected_a", "var", 1] + + warn("unexpected_a", the_variable); + } + the_bracket = token_nxt; + advance("["); + while (true) { + ellipsis = false; + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + if (!token_nxt.identifier) { + +// test_cause: +// ["let[]", "stmt_var", "expected_identifier_a", "]", 5] + + return stop("expected_identifier_a"); + } + name = token_nxt; + advance(); + the_variable.names.push(name); + enroll(name, "variable", mode_const); + name.dead = false; + name.init = true; + if (ellipsis) { + name.ellipsis = true; + break; + } + if (token_nxt.id === "=") { + advance("="); + name.expression = parse_expression(); + the_bracket.open = true; + } + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + advance("]"); + advance("="); + the_variable.expression = parse_expression(0); + } else if (token_nxt.identifier) { + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// [" +// let ignore;function aa(ignore) {} +// ", "stmt_var", "unexpected_a", "ignore", 5] + + warn("unexpected_a", name); + } + enroll(name, "variable", mode_const); + if (token_nxt.id === "=" || mode_const) { + advance("="); + name.dead = false; + name.init = true; + name.expression = parse_expression(0); + } + the_variable.names.push(name); + } else { + +// test_cause: +// ["let 0", "stmt_var", "expected_identifier_a", "0", 5] +// ["var{aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 8] + + return stop("expected_identifier_a"); + } + if (token_nxt.id !== ",") { + break; + } + +// test_cause: +// ["let aa,bb;", "stmt_var", "expected_a_b", ",", 7] + + warn("expected_a_b", token_nxt, ";", ","); + advance(","); + } + +// Warn if variable declarations are unordered. + + if ( + option_dict.beta + && !option_dict.unordered + && !option_dict.variable + && variable_prv + && ( + variable_prv.id + " " + variable_prv.names[0].id + > the_variable.id + " " + the_variable.names[0].id + ) + ) { + +// test_cause: +// ["const bb=0;const aa=0;", "stmt_var", "expected_a_b_before_c_d", "aa", 12] +// ["let bb;let aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8] +// ["var bb;var aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8] + + warn( + "expected_a_b_before_c_d", + the_variable, + the_variable.id, + the_variable.names[0].id, + variable_prv.id, + variable_prv.names[0].id + ); + } + semicolon(); + return the_variable; + } + + function stmt_while() { + const the_while = token_now; + check_not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + +// test_cause: +// ["function aa(){while(0){break;}}", "stmt_while", "weird_loop", "while", 15] + + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; + } + + function stmt_with() { + +// test_cause: +// ["with", "stmt_with", "unexpected_a", "with", 1] + + stop("unexpected_a", token_now); + } + + function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!jslint_rgx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!jslint_rgx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + +// test_cause: +// ["let aa={0:0}", "survey", "expected_identifier_a", "0", 9] + + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property_dict[id] === "number") { + property_dict[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (state.mode_property) { + if (tenure[id] !== true) { + +// test_cause: +// ["/*property aa*/\naa.bb", "survey", "unregistered_property_a", "bb", 4] + + warn("unregistered_property_a", name); + } + } else if ( + !option_dict.nomen + && name.identifier + && jslint_rgx_weird_property.test(id) + ) { + +// test_cause: +// ["aa.$", "survey", "weird_property_a", "$", 4] +// ["aa._", "survey", "weird_property_a", "_", 4] +// ["aa._aa", "survey", "weird_property_a", "_aa", 4] +// ["aa.aaSync", "survey", "weird_property_a", "aaSync", 4] +// ["aa.aa_", "survey", "weird_property_a", "aa_", 4] + + warn("weird_property_a", name); + } + property_dict[id] = 1; + } + return id; + } + +// These functions are used to specify the grammar of our language: + + function symbol(id, bp) { + +// Create a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax_dict[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax_dict[id] = the_symbol; + } + return the_symbol; + } + + function ternary(id1, id2) { + +// Create a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led_infix = function parse_ternary_led(left) { + const the_token = token_now; + let second; + second = parse_expression(20); + advance(id2); + token_now.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, parse_expression(10)]; + if (token_nxt.id !== ")") { + +// test_cause: +// ["0?0:0", "parse_ternary_led", "use_open", "?", 2] + + warn("use_open", the_token); + } + return the_token; + }; + return the_symbol; + } + +// Now we parse JavaScript. +// Begin defining the language. + + assignment("%="); + assignment("&="); + assignment("*="); + assignment("+="); + assignment("-="); + assignment("/="); + assignment("<<="); + assignment("="); + assignment(">>="); + assignment(">>>="); + assignment("^="); + assignment("|="); + constant("(number)", "number"); + constant("(regexp)", "regexp"); + constant("(string)", "string"); + constant("Function", "function", constant_Function); + constant("Infinity", "number", Infinity); + constant("NaN", "number", NaN); + constant("arguments", "object", constant_arguments); + constant("eval", "function", constant_eval); + constant("false", "boolean", false); + constant("ignore", "undefined", constant_ignore); + constant("isFinite", "function", constant_isInfinite); + constant("isNaN", "function", constant_isNaN); + constant("null", "null", null); + constant("this", "object", constant_this); + constant("true", "boolean", true); + constant("undefined", "undefined"); + infix(100, "!="); + infix(100, "!=="); + infix(100, "=="); + infix(100, "==="); + infix(110, "<"); + infix(110, "<="); + infix(110, ">"); + infix(110, ">="); + infix(110, "in"); + infix(110, "instanceof"); + infix(120, "<<"); + infix(120, ">>"); + infix(120, ">>>"); + infix(130, "+"); + infix(130, "-"); + infix(140, "%"); + infix(140, "*"); + infix(140, "/"); + infix(160, "(", infix_lparen); + infix(160, "`", infix_grave); + infix(170, ".", infix_dot); + infix(170, "=>", infix_fart_unwrapped); + infix(170, "?.", infix_option_chain); + infix(170, "[", infix_lbracket); + infix(35, "??"); + infix(40, "||"); + infix(50, "&&"); + infix(70, "|"); + infix(80, "^"); + infix(90, "&"); + infixr(150, "**"); + postassign("++"); + postassign("--"); + preassign("++"); + preassign("--"); + prefix("!!"); + prefix("!"); + prefix("(", prefix_lparen); + prefix("+"); + prefix("-"); + prefix("/=", prefix_assign_divide); + prefix("=>", prefix_fart); + prefix("[", prefix_lbracket); + prefix("`", prefix_tick); + prefix("async", prefix_async); + prefix("await", prefix_await); + prefix("function", prefix_function); + prefix("new", prefix_new); + prefix("typeof"); + prefix("void", prefix_void); + prefix("{", prefix_lbrace); + prefix("~"); + stmt(";", stmt_semicolon); + stmt("async", prefix_async); + stmt("await", prefix_await); + stmt("break", stmt_break); + stmt("const", stmt_var); + stmt("continue", stmt_continue); + stmt("debugger", stmt_debugger); + stmt("delete", stmt_delete); + stmt("do", stmt_do); + stmt("export", stmt_export); + stmt("for", stmt_for); + stmt("function", prefix_function); + stmt("if", stmt_if); + stmt("import", stmt_import); + stmt("let", stmt_var); + stmt("return", stmt_return); + stmt("switch", stmt_switch); + stmt("throw", stmt_throw); + stmt("try", stmt_try); + stmt("var", stmt_var); + stmt("while", stmt_while); + stmt("with", stmt_with); + stmt("{", stmt_lbrace); + symbol(")"); + symbol("*/"); + symbol(","); + symbol(":"); + symbol(";"); + symbol("]"); + symbol("async"); + symbol("await"); + symbol("case"); + symbol("catch"); + symbol("class"); + symbol("default"); + symbol("else"); + symbol("enum"); + symbol("finally"); + symbol("implements"); + symbol("interface"); + symbol("package"); + symbol("private"); + symbol("protected"); + symbol("public"); + symbol("static"); + symbol("super"); + symbol("void"); + symbol("yield"); + symbol("}"); + ternary("?", ":"); + +// Init token_nxt. + + advance(); + +// Parsing of JSON is simple: + + if (state.mode_json) { + state.token_tree = parse_json(); + advance("(end)"); + return; + } + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option_dict.browser) { + if (token_nxt.id === ";") { + advance(";"); + } + +// If we are not in a browser, then the file form of strict pragma may be used. + + } else if (token_nxt.value === "use strict") { + advance("(string)"); + advance(";"); + } + state.token_tree = parse_statements(); + advance("(end)"); + +// Check global functions are ordered. + + check_ordered( + "function", + function_list.map(function ({ + level, + name + }) { + return (level === 1) && name; + }).filter(function (name) { + return option_dict.beta && name && name.id; + }) + ); +} + +function jslint_phase4_walk(state) { + +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). + + let { + artifact, + catch_stack, + function_stack, + global_dict, + is_equal, + is_weird, + option_dict, + syntax_dict, + test_cause, + token_global, + warn + } = state; + let block_stack = []; // The stack of blocks. + let blockage = token_global; // The current block. + let catchage = catch_stack[0]; // The current catch-block. + let functionage = token_global; // The current function. + let postaction; + let postamble; + let posts = empty(); + let preaction; + let preamble; + let pres = empty(); + +// The relational operators. + + let relationop = object_assign_from_list(empty(), [ + "!=", "!==", "<", "<=", "==", "===", ">", ">=" + ], true); + +// Ambulation of the parse tree. + + function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; + } + + function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + task(the_token); + }); + } + } + }; + } + + function init_variable(name) { + let the_variable = lookup(name); + if (!the_variable || the_variable.readonly) { + warn("bad_assignment_a", name); + return; + } + the_variable.init = true; + } + + function lookup(thing) { + let id = thing.id; + let the_variable; + if (thing.arity !== "variable") { + return; + } + +// Look up the variable in the current context. + + the_variable = functionage.context[id] || catchage.context[id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable && the_variable.role === "label") { + +// test_cause: +// ["aa:while(0){aa;}", "lookup", "label_a", "aa", 13] + + warn("label_a", thing); + return the_variable; + } + if (!the_variable) { + function_stack.forEach(function ({ + context + }) { + if (context[id] && context[id].role !== "label") { + the_variable = context[id]; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (!the_variable && global_dict[id] === undefined) { + +// test_cause: +// ["aa", "lookup", "undeclared_a", "aa", 1] +// ["class aa{}", "lookup", "undeclared_a", "aa", 7] +// [" +// let aa=0;try{aa();}catch(bb){bb();}bb(); +// ", "lookup", "undeclared_a", "bb", 36] +// [" +// let aa=0;try{aa();}catch(ignore){bb();} +// ", "lookup", "undeclared_a", "bb", 34] + + warn("undeclared_a", thing); + return; + } + if (!the_variable) { + the_variable = { + dead: false, + id, + init: true, + parent: token_global, + readonly: true, + role: "variable", + used: 0 + }; + token_global.context[id] = the_variable; + } + the_variable.closure = true; + functionage.context[id] = the_variable; + } + if ( + ( + the_variable.calls === undefined + || functionage.name === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + && the_variable.dead + ) { + +// test_cause: +// ["let aa;if(aa){let bb;}bb;", "lookup", "out_of_scope_a", "bb", 23] + + warn("out_of_scope_a", thing); + } + return the_variable; + } + + function post_a(thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + let right; + if (thing.id === "=") { + if (thing.names !== undefined) { + +// test_cause: +// ["if(0){aa=0}", "post_a", "=", "", 0] + + test_cause("="); + +// Probably deadcode. +// if (Array.isArray(thing.names)) { +// thing.names.forEach(init_variable); +// } else { +// init_variable(thing.names); +// } + + jslint_assert( + !Array.isArray(thing.names), + `Expected !Array.isArray(thing.names).` + ); + init_variable(thing.names); + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + +// test_cause: +// ["aa.aa=undefined", "post_a", "expected_a_b", "undefined", 1] + + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.readonly) { + warn("bad_assignment_a", lvalue); + } + } + right = syntax_dict[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + +// test_cause: +// ["aa+=undefined", "post_a", "unexpected_a", "undefined", 5] + + warn("unexpected_a", thing.expression[1]); + } + } + } + + function post_a_pluseq(thing) { + const right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } + } + + function post_b(thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || is_equal(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + +// test_cause: +// ["if(0===0){0}", "post_b", "weird_relation_a", "===", 5] + + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option_dict.convert) { + if (thing.expression[0].value === "") { + +// test_cause: +// ["\"\"+0", "post_b", "expected_a_b", "\"\" +", 3] + + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + +// test_cause: +// ["0+\"\"", "post_b", "expected_a_b", "+ \"\"", 2] + + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + +// test_cause: +// ["aa=window[0]", "post_b", "weird_expression_a", "window[...]", 10] + + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + +// test_cause: +// ["aa=self[0]", "post_b", "weird_expression_a", "self[...]", 8] + + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === "." || thing.id === "?.") { + if (thing.expression.id === "RegExp") { + +// test_cause: +// ["aa=RegExp.aa", "post_b", "weird_expression_a", ".", 10] + + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + +// test_cause: +// ["0- -0", "post_b", "wrap_unary", "-", 4] + + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } + } + + function post_b_and(thing) { + if ( + is_weird(thing.expression[0]) + || is_equal(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + +// test_cause: +// ["aa=(()=>0)&&(()=>0)", "post_b_and", "weird_condition_a", "&&", 11] +// ["aa=(``?``:``)&&(``?``:``)", "post_b_and", "weird_condition_a", "&&", 14] +// ["aa=/./&&/./", "post_b_and", "weird_condition_a", "&&", 7] +// ["aa=0&&0", "post_b_and", "weird_condition_a", "&&", 5] +// ["aa=[]&&[]", "post_b_and", "weird_condition_a", "&&", 6] +// ["aa=`${0}`&&`${0}`", "post_b_and", "weird_condition_a", "&&", 10] +// [" +// aa=function aa(){}&&function aa(){} +// ", "post_b_and", "weird_condition_a", "&&", 19] +// ["aa={}&&{}", "post_b_and", "weird_condition_a", "&&", 6] + + warn("weird_condition_a", thing); + } + } + + function post_b_lbracket(thing) { + if (thing.expression[0].id === "RegExp") { + +// test_cause: +// ["aa=RegExp[0]", "post_b_lbracket", "weird_expression_a", "[", 10] + + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + +// test_cause: +// ["aa[[0]]", "post_b_lbracket", "weird_expression_a", "[", 4] + + warn("weird_expression_a", thing.expression[1]); + } + } + + function post_b_lparen(thing) { + let arg; + let array; + let cack; + let left = thing.expression[0]; + let new_date; + let paren; + let the_new; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + +// test_cause: +// ["aa=function(){}()", "post_b_lparen", "wrap_immediate", "(", 16] + + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "BigInt" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + +// test_cause: +// ["new BigInt()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Boolean()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Number()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new String()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Symbol()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new aa()", "post_b_lparen", "unexpected_a", "new", 1] + + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option_dict.eval) { + +// test_cause: +// ["new Function()", "post_b_lparen", "unexpected_a", "new Function", 5] + + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + +// test_cause: +// ["new Array()", "post_b_lparen", "expected_a_b", "new Array", 5] + + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + +// test_cause: +// ["new Object()", "post_b_lparen", "expected_a_b", "new Object", 5] + + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "BigInt" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + +// test_cause: +// ["let Aa=Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8] + + warn("expected_a_before_b", left, "new", artifact(left)); + } + } + } else if (left.id === ".") { + cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + +// test_cause: +// ["new Date.UTC()", "post_b_lparen", "cack", "", 0] + + test_cause("cack"); + cack = !cack; + } + if (jslint_rgx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + +// test_cause: +// ["new Date.UTC()", "post_b_lparen", "unexpected_a", "new", 1] + + warn("unexpected_a", the_new); + } else { + +// test_cause: +// ["let Aa=Aa.Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8] + + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + paren = left.expression; + if (paren.id === "(") { + array = paren.expression; + if (array.length === 1) { + new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + +// test_cause: +// [" +// new Date().getTime() +// ", "post_b_lparen", "expected_a_b", "new Date().getTime()", 1] + + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } + } + + function post_b_or(thing) { + if ( + is_weird(thing.expression[0]) + || is_equal(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + +// test_cause: +// ["aa=0||0", "post_b_or", "weird_condition_a", "||", 5] + + warn("weird_condition_a", thing); + } + } + + function post_s_export(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== token_global) { + +// test_cause: +// [" +// if(0){import aa from "aa";} +// ", "post_s_export", "misplaced_a", "import", 7] + + warn("misplaced_a", the_thing); + } + } + + function post_s_for(thing) { + +// Recurse walk_statement(). + + walk_statement(thing.inc); + } + + function post_s_function(thing) { + delete functionage.async; + delete functionage.finally; + delete functionage.loop; + delete functionage.statement_prv; + delete functionage.switch; + delete functionage.try; + functionage = function_stack.pop(); + if (thing.wrapped) { + +// test_cause: +// ["aa=(function(){})", "post_s_function", "unexpected_parens", "function", 5] + + warn("unexpected_parens", thing); + } + return post_s_lbrace(); + } + + function post_s_import(the_thing) { + const name = the_thing.name; + if (name) { + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return post_s_export(the_thing); + } + } + + function post_s_lbrace() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); + } + + function post_s_try(thing) { + if (thing.catch) { + if (thing.catch.name) { + Object.assign(catchage.context[thing.catch.name.id], { + dead: false, + init: true + }); + } + +// Recurse walk_statement(). + + walk_statement(thing.catch.block); + +// Restore previous catch-scope after catch-block. + + catchage = catch_stack.pop(); + } + } + + function post_s_var(thing) { + thing.names.forEach(function (name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + +// Probably deadcode. +// if (name.id === "{" || name.id === "[") { +// name.names.forEach(subactivate); +// } else { +// name.init = true; +// } + + jslint_assert( + !(name.id === "{" || name.id === "["), + `Expected !(name.id === "{" || name.id === "[").` + ); + name.init = true; + } + blockage.live.push(name); + }); + } + + function post_t(thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || is_equal(thing.expression[1], thing.expression[2]) + ) { + +// test_cause: +// ["let aa=(aa?`${0}`:`${0}`);", "post_t", "unexpected_a", "?", 11] +// ["let aa=(aa?`0`:`0`);", "post_t", "unexpected_a", "?", 11] + + warn("unexpected_a", thing); + } else if (is_equal(thing.expression[0], thing.expression[1])) { + +// test_cause: +// ["aa?aa:0", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "||", "?"); + } else if (is_equal(thing.expression[0], thing.expression[2])) { + +// test_cause: +// ["aa?0:aa", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + +// test_cause: +// ["aa?true:false", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + +// test_cause: +// ["aa?false:true", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + +// test_cause: +// ["(aa&&!aa?0:1)", "post_t", "wrap_condition", "&&", 4] + + warn("wrap_condition", thing.expression[0]); + } + } + + function post_u(thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option_dict.convert) { + +// test_cause: +// ["!!0", "post_u", "expected_a_b", "!!", 1] + + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } + } + + function post_u_plus(thing) { + const right = thing.expression; + if (!option_dict.convert) { + +// test_cause: +// ["aa=+0", "post_u_plus", "expected_a_b", "+", 4] + + warn("expected_a_b", thing, "Number(...)", "+"); + } + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } + } + + function pre_a_bitwise(thing) { + +// These are the bitwise operators. + + switch (!option_dict.bitwise && thing.id) { + case "&": + case "&=": + case "<<": + case "<<=": + case ">>": + case ">>=": + case ">>>": + case ">>>=": + case "^": + case "^=": + case "|": + case "|=": + case "~": + +// test_cause: +// ["0&0", "pre_a_bitwise", "unexpected_a", "&", 2] +// ["0&=0", "pre_a_bitwise", "unexpected_a", "&=", 2] +// ["0<<0", "pre_a_bitwise", "unexpected_a", "<<", 2] +// ["0<<=0", "pre_a_bitwise", "unexpected_a", "<<=", 2] +// ["0>>0", "pre_a_bitwise", "unexpected_a", ">>", 2] +// ["0>>=0", "pre_a_bitwise", "unexpected_a", ">>=", 2] +// ["0>>>0", "pre_a_bitwise", "unexpected_a", ">>>", 2] +// ["0>>>=0", "pre_a_bitwise", "unexpected_a", ">>>=", 2] +// ["0^0", "pre_a_bitwise", "unexpected_a", "^", 2] +// ["0^=0", "pre_a_bitwise", "unexpected_a", "^=", 2] +// ["0|0", "pre_a_bitwise", "unexpected_a", "|", 2] +// ["0|=0", "pre_a_bitwise", "unexpected_a", "|=", 2] +// ["~0", "pre_a_bitwise", "unexpected_a", "~", 1] + + warn("unexpected_a", thing); + break; + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + +// test_cause: +// ["0<0<0", "pre_a_bitwise", "unexpected_a", "<", 4] + + warn("unexpected_a", thing); + } + } + + function pre_b(thing) { + let left; + let right; + let value; + if (relationop[thing.id] === true) { + left = thing.expression[0]; + right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + +// test_cause: +// ["NaN===NaN", "pre_b", "number_isNaN", "===", 4] + + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + +// test_cause: +// ["typeof 0===0", "pre_b", "expected_string_a", "0", 12] + + warn("expected_string_a", right); + } + } else { + value = right.value; + if (value === "null" || value === "undefined") { + +// test_cause: +// [" +// typeof aa==="undefined" +// ", "pre_b", "unexpected_typeof_a", "undefined", 13] + + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "bigint" + && value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + +// test_cause: +// ["typeof 0===\"aa\"", "pre_b", "expected_type_string_a", "aa", 12] + + warn("expected_type_string_a", right, value); + } + } + } + } + } + + function pre_b_eqeq(thing) { + +// test_cause: +// ["0==0", "pre_b_eqeq", "expected_a_b", "==", 2] + + warn("expected_a_b", thing, "===", "=="); + } + + function pre_b_in(thing) { + +// test_cause: +// ["aa in aa", "pre_b_in", "infix_in", "in", 4] + + warn("infix_in", thing); + } + + function pre_b_instanceof(thing) { + +// test_cause: +// ["0 instanceof 0", "pre_b_instanceof", "unexpected_a", "instanceof", 3] + + warn("unexpected_a", thing); + } + + function pre_b_lparen(thing) { + const left = thing.expression[0]; + let left_variable; + let parent; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + parent = functionage.name.parent; + if (parent) { + left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + +// Probably deadcode. +// && left_variable.dead + + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } + } + + function pre_b_noteq(thing) { + +// test_cause: +// ["0!=0", "pre_b_noteq", "expected_a_b", "!=", 2] + + warn("expected_a_b", thing, "!==", "!="); + } + + function pre_b_or(thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + +// test_cause: +// ["0&&0||0", "pre_b_or", "and", "&&", 2] + + warn("and", thang); + } + }); + } + + function pre_s_for(thing) { + let the_variable; + if (thing.name !== undefined) { + thing.name.dead = false; + the_variable = lookup(thing.name); + if (the_variable !== undefined) { + if (the_variable.init && the_variable.readonly) { + +// test_cause: +// ["const aa=0;for(aa in aa){}", "pre_s_for", "bad_assignment_a", "aa", 16] + + warn("bad_assignment_a", thing.name); + } + the_variable.init = true; + } + } + +// Recurse walk_statement(). + + walk_statement(thing.initial); + } + + function pre_s_function(thing) { + +// test_cause: +// ["()=>0", "pre_s_function", "", "", 0] +// ["(function (){}())", "pre_s_function", "", "", 0] +// ["function aa(){}", "pre_s_function", "", "", 0] + + test_cause(""); + if (thing.arity === "statement" && blockage.body !== true) { + +// test_cause: +// ["if(0){function aa(){}\n}", "pre_s_function", "unexpected_a", "function", 7] + + warn("unexpected_a", thing); + } + function_stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + +// test_cause: +// [" +// /*jslint getset*/ +// aa={get aa(aa){}} +// ", "pre_s_function", "bad_get", "function", 9] + + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + +// test_cause: +// [" +// /*jslint getset*/ +// aa={set aa(){}} +// ", "pre_s_function", "bad_set", "function", 9] + + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); + } + + function pre_s_lbrace(thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; + } + + function pre_try(thing) { + if (thing.catch !== undefined) { + +// Create new catch-scope for catch-parameter. + + catch_stack.push(catchage); + catchage = thing.catch; + } + } + + function pre_v(thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } + } + + function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); + } + + function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + +// test_cause: +// ["(function(){}())", "walk_expression", "isArray", "", 0] +// ["0&&0", "walk_expression", "isArray", "", 0] + + test_cause("isArray"); + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + +// PR-414 - Bugfix - fix fart-body not being walked. + + if (thing.id === "function" || thing.id === "=>") { + +// test_cause: +// ["aa=()=>0", "walk_expression", "function", "=>", 0] +// ["aa=function(){}", "walk_expression", "function", "function", 0] + + test_cause("function", thing.id); + +// Recurse walk_statement(). + + walk_statement(thing.block); + } + if ( + thing.arity === "preassign" || thing.arity === "postassign" + ) { + +// test_cause: +// ["aa=++aa", "walk_expression", "unexpected_a", "++", 4] +// ["aa=--aa", "walk_expression", "unexpected_a", "--", 4] + + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + +// test_cause: +// ["aa[aa=0]", "walk_expression", "unexpected_statement_a", "=", 6] + + warn("unexpected_statement_a", thing); + } + +// test_cause: +// ["aa=0", "walk_expression", "default", "", 0] + + test_cause("default"); + postamble(thing); + } + } + } + + function walk_statement(thing) { + if (!thing) { + return; + } + if (Array.isArray(thing)) { + +// test_cause: +// ["+[]", "walk_statement", "isArray", "", 0] + + test_cause("isArray"); + +// Recurse walk_statement(). + + thing.forEach(walk_statement); + return; + } + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + +// test_cause: +// ["0&&0", "walk_statement", "unexpected_expression_a", "&&", 2] + + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + && thing.id !== "import" + ) { + +// test_cause: +// ["!0", "walk_statement", "unexpected_expression_a", "!", 1] +// ["+[]", "walk_statement", "unexpected_expression_a", "+", 1] +// ["+new aa()", "walk_statement", "unexpected_expression_a", "+", 1] +// ["0", "walk_statement", "unexpected_expression_a", "0", 1] +// ["typeof 0", "walk_statement", "unexpected_expression_a", "typeof", 1] + + warn("unexpected_expression_a", thing); + } + +// Recurse walk_statement(). + + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + + postaction = action(posts); + postamble = amble(posts); + preaction = action(pres); + preamble = amble(pres); + postaction("assignment", "+=", post_a_pluseq); + postaction("assignment", post_a); + postaction("binary", "&&", post_b_and); + postaction("binary", "(", post_b_lparen); + postaction("binary", "=>", post_s_function); + postaction("binary", "[", post_b_lbracket); + postaction("binary", "||", post_b_or); + postaction("binary", post_b); + postaction("statement", "const", post_s_var); + postaction("statement", "export", post_s_export); + postaction("statement", "for", post_s_for); + postaction("statement", "function", post_s_function); + postaction("statement", "import", post_s_import); + postaction("statement", "let", post_s_var); + postaction("statement", "try", post_s_try); + postaction("statement", "var", post_s_var); + postaction("statement", "{", post_s_lbrace); + postaction("ternary", post_t); + postaction("unary", "+", post_u_plus); + postaction("unary", "function", post_s_function); + postaction("unary", post_u); + preaction("assignment", pre_a_bitwise); + preaction("binary", "!=", pre_b_noteq); + preaction("binary", "(", pre_b_lparen); + preaction("binary", "==", pre_b_eqeq); + preaction("binary", "=>", pre_s_function); + preaction("binary", "in", pre_b_in); + preaction("binary", "instanceof", pre_b_instanceof); + preaction("binary", "||", pre_b_or); + preaction("binary", pre_b); + preaction("binary", pre_a_bitwise); + preaction("statement", "for", pre_s_for); + preaction("statement", "function", pre_s_function); + preaction("statement", "try", pre_try); + preaction("statement", "{", pre_s_lbrace); + preaction("unary", "function", pre_s_function); + preaction("unary", "~", pre_a_bitwise); + preaction("variable", pre_v); + + walk_statement(state.token_tree); +} + +function jslint_phase5_whitage(state) { + +// PHASE 5. Check whitespace between tokens in . + + let { + artifact, + catch_list, + function_list, + function_stack, + option_dict, + test_cause, + token_global, + token_list, + warn + } = state; + let closer = "(end)"; + let free = false; + +// free = false + +// cause: +// "()=>0" +// "aa()" +// "aa(0,0)" +// "function(){}" + +// free = true + +// cause: +// "(0)" +// "(aa)" +// "aa(0)" +// "do{}while()" +// "for(){}" +// "if(){}" +// "switch(){}" +// "while(){}" + + let left = token_global; + let margin = 0; + let mode_indent = ( + +// PR-330 - Allow 2-space indent. + + option_dict.indent2 + ? 2 + : 4 + ); + let nr_comments_skipped = 0; + let open = true; + let opening = true; + let right; + +// This is the set of infix operators that require a space on each side. + + let spaceop = object_assign_from_list(empty(), [ + "!=", "!==", "%", "%=", "&", "&&", "&=", "*", "*=", "+=", "-=", "/", + "/=", "<", "<<", "<<=", "<=", "=", "==", "===", "=>", ">", ">=", ">>", + ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" + ], true); + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + const name = the_function.context[id]; + if (id !== "ignore" && name.parent === the_function) { + +// test_cause: +// ["function aa(aa) {return aa;}", "delve", "id", "", 0] + + test_cause("id"); + if ( + name.used === 0 + +// Probably deadcode. +// && ( +// name.role !== "function" +// || name.parent.arity !== "unary" +// ) + + && jslint_assert( + name.role !== "function", + `Expected name.role !== "function".` + ) + ) { + +// test_cause: +// ["/*jslint node*/\nlet aa;", "delve", "unused_a", "aa", 5] +// ["function aa(aa){return;}", "delve", "unused_a", "aa", 13] +// ["let aa=0;try{aa();}catch(bb){aa();}", "delve", "unused_a", "bb", 26] + + warn("unused_a", name); + } else if (!name.init) { + +// test_cause: +// ["/*jslint node*/\nlet aa;aa();", "delve", "uninitialized_a", "aa", 5] + + warn("uninitialized_a", name); + } + } + }); + } + + function expected_at(at) { + +// Probably deadcode. +// if (right === undefined) { +// right = token_nxt; +// } + + jslint_assert( + !(right === undefined), + `Expected !(right === undefined).` + ); + warn( + "expected_a_at_b_c", + right, + artifact(right), + +// Fudge column numbers in warning message. + + at + jslint_fudge, + right.from + jslint_fudge + ); + } + + function no_space() { + if (left.line === right.line) { + +// from: +// if (left.line === right.line) { +// no_space(); +// } else { + + if (left.thru !== right.from && nr_comments_skipped === 0) { + +// test_cause: +// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14] + + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + +// from: +// } else if ( +// right.arity === "binary" +// && right.id === "(" +// && free +// ) { +// no_space(); +// } else if ( + +// Probably deadcode. +// if (open) { +// const at = ( +// free +// ? margin +// : margin + 8 +// ); +// if (right.from < at) { +// expected_at(at); +// } +// } else { +// if (right.from !== margin + 8) { +// expected_at(margin + 8); +// } +// } + + jslint_assert(open, `Expected open.`); + jslint_assert(free, `Expected free.`); + if (right.from < margin) { + +// test_cause: +// ["let aa = aa(\naa\n()\n);", "expected_at", "expected_a_at_b_c", "5", 1] + + expected_at(margin); + } + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line || !open) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (right.from !== margin) { + expected_at(margin); + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn("expected_space_a_b", right, artifact(left), artifact(right)); + } + } + + function pop() { + const previous = function_stack.pop(); + closer = previous.closer; + free = previous.free; + margin = previous.margin; + open = previous.open; + opening = previous.opening; + } + + function push() { + function_stack.push({ + closer, + free, + margin, + open, + opening + }); + } + +// uninitialized_and_unused(); +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (state.mode_module === true || option_dict.node) { + delve(token_global); + } + catch_list.forEach(delve); + function_list.forEach(delve); + + if (option_dict.white) { + return; + } + +// whitage(); +// Go through the token list, looking at usage of whitespace. + + token_list.forEach(function whitage(the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + +// The open and close pairs. + + switch (left.id) { + case "${": + case "(": + case "[": + case "{": + +// test_cause: +// ["let aa=[];", "whitage", "opener", "", 0] +// ["let aa=`${0}`;", "whitage", "opener", "", 0] +// ["let aa=aa();", "whitage", "opener", "", 0] +// ["let aa={};", "whitage", "opener", "", 0] + + test_cause("opener"); + +// Probably deadcode. +// case "${}": + + jslint_assert( + !(left.id + right.id === "${}"), + "Expected !(left.id + right.id === \"${}\")." + ); + switch (left.id + right.id) { + case "()": + case "[]": + case "{}": + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like '{]') have already been detected. + +// test_cause: +// ["let aa=[];", "whitage", "opener_closer", "", 0] +// ["let aa=aa();", "whitage", "opener_closer", "", 0] +// ["let aa={};", "whitage", "opener_closer", "", 0] + + test_cause("opener_closer"); + if (left.line === right.line) { + +// test_cause: +// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14] + + no_space(); + } else { + +// test_cause: +// ["let aa = aa(\n );", "expected_at", "expected_a_at_b_c", "1", 2] + + at_margin(0); + } + break; + default: + +// test_cause: +// ["let aa=(0);", "whitage", "opener_operand", "", 0] +// ["let aa=[0];", "whitage", "opener_operand", "", 0] +// ["let aa=`${0}`;", "whitage", "opener_operand", "", 0] +// ["let aa=aa(0);", "whitage", "opener_operand", "", 0] +// ["let aa={aa:0};", "whitage", "opener_operand", "", 0] + + test_cause("opener_operand"); + opening = left.open || (left.line !== right.line); + push(); + switch (left.id) { + case "${": + closer = "}"; + break; + case "(": + closer = ")"; + break; + case "[": + closer = "]"; + break; + case "{": + closer = "}"; + break; + } + if (opening) { + +// test_cause: +// ["function aa(){\nreturn;\n}", "whitage", "opening", "", 0] +// ["let aa=(\n0\n);", "whitage", "opening", "", 0] +// ["let aa=[\n0\n];", "whitage", "opening", "", 0] +// ["let aa=`${\n0\n}`;", "whitage", "opening", "", 0] +// ["let aa={\naa:0\n};", "whitage", "opening", "", 0] + + test_cause("opening"); + free = closer === ")" && left.free; + open = true; + margin += mode_indent; + if (right.role === "label") { + if (right.from !== 0) { + +// test_cause: +// [" +// function aa() { +// bb: +// while (aa) { +// if (aa) { +// break bb; +// } +// } +// } +// ", "expected_at", "expected_a_at_b_c", "1", 2] + + expected_at(0); + } + } else if (right.switch) { + at_margin(-mode_indent); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + +// test_cause: +// [" +// function aa() {bb: +// while (aa) { +// aa(); +// } +// } +// ", "whitage", "expected_line_break_a_b", "bb", 16] + + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + +// test_cause: +// ["let aa=(0);", "whitage", "not_free", "", 0] +// ["let aa=[0];", "whitage", "not_free", "", 0] +// ["let aa=`${0}`;", "whitage", "not_free", "", 0] +// ["let aa={aa:0};", "whitage", "not_free", "", 0] + + test_cause("not_free"); + free = false; + open = false; + +// test_cause: +// ["let aa = ( 0 );", "no_space_only", "unexpected_space_a_b", "0", 12] + + no_space_only(); + } + } + break; + default: + if (right.statement === true) { + if (left.id === "else") { + +// test_cause: +// [" +// let aa = 0; +// if (aa) { +// aa(); +// } else if (aa) { +// aa(); +// } +// ", "one_space_only", "expected_space_a_b", "if", 9] + + one_space_only(); + } else { + +// test_cause: +// [" let aa = 0;", "expected_at", "expected_a_at_b_c", "1", 2] + + at_margin(0); + open = false; + } + +// If right is a closer, then pop the previous state. + + } else if (right.id === closer) { + pop(); + if (opening && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-mode_indent); + } else if (right.role === "label") { + if (right.from !== 0) { + +// test_cause: +// [" +// function aa() { +// aa();cc: +// while (aa) { +// if (aa) { +// break cc; +// } +// } +// } +// ", "expected_at", "expected_a_at_b_c", "1", 10] + + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + +// test_cause: +// ["let {aa,bb} = 0;", "one_space", "expected_space_a_b", "bb", 9] + + one_space(); + } else { + +// test_cause: +// [" +// function aa() { +// aa( +// 0,0 +// ); +// } +// ", "expected_at", "expected_a_at_b_c", "9", 11] + + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + +// test_cause: +// [" +// let aa = ( +// aa +// ? 0 +// : 1 +// ); +// ", "expected_at", "expected_a_at_b_c", "5", 1] + + at_margin(0); + } else { + +// test_cause: +// ["let aa = (aa ? 0 : 1);", "whitage", "use_open", "?", 14] + + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + +// test_cause: +// ["let aa = aa(\naa ()\n);", "no_space", "unexpected_space_a_b", "(", 4] + + no_space(); + } else if ( + left.id === "." + || left.id === "?." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + || (right.id === "." || right.id === "?.") + ) { + +// test_cause: +// ["let aa = 0 ;", "no_space_only", "unexpected_space_a_b", ";", 12] +// ["let aa = aa ?.aa;", "no_space_only", "unexpected_space_a_b", "?.", 13] + + no_space_only(); + } else if (left.id === ";") { + +// test_cause: +// [" +// /*jslint for*/ +// function aa() { +// for ( +// aa(); +// aa; +// aa() +// ) { +// aa(); +// } +// } +// ", "expected_at", "expected_a_at_b_c", "9", 1] + + if (open) { + at_margin(0); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || left.id === "await" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + +// test_cause: +// [" +// function aa() { +// do { +// aa(); +// } while(aa()); +// } +// ", "one_space_only", "expected_space_a_b", "(", 12] + + one_space_only(); + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || left.id === "async" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + +// test_cause: +// ["let aa=0;", "one_space", "expected_space_a_b", "0", 8] +// ["let aa={\naa:\n0\n};", "expected_at", "expected_a_at_b_c", "5", 1] + + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +function jslint_report({ + exports, + froms, + functions, + global, + json, + module, + property, + stop, + warnings +}) { + +// This function will create human-readable, html-report +// for warnings, properties, and functions from jslint-result-object. +// +// Example usage: +// let result = jslint("console.log('hello world')"); +// let html = jslint_report(result); + + let html = ""; + let length_80 = 1111; + + function address(line = 1, column = 1) { + +// This function will create HTML address element from and + + return `
    ${Number(line)}: ${Number(column)}
    `; + + } + + function detail(title, list) { + return ( + (Array.isArray(list) && list.length > 0) + ? ( + +// Google Lighthouse Accessibility -
    's do not contain only properly-ordered +//
    and
    groups, ". + + code.replace(( + /^]*?>\n([\S\s]*?\n)<\/script>$/gm + ), function (ignore, match1, ii) { + jslint_from_file({ + code: match1, + file: file + ". + + import_meta_url = import_meta_url || jslint_import_meta_url; + if ( + jslint_rgx_url_search_window_jslint.test(import_meta_url) + && (typeof globalThis === "object" && globalThis) + ) { + globalThis.jslint = jslint; + } + +// Feature-detect nodejs. + + if (!( + (typeof process === "object" && process) + && process.versions + && typeof process.versions.node === "string" + && !mode_noop + )) { + return exit_code; + } + console_error = console_error || console.error; + console_log = console_log || console.log; + process_argv = process_argv || process.argv; + process_env = process_env || process.env; + process_exit = process_exit || process.exit; + await moduleFsInit(); + if ( + !( + +// Feature-detect nodejs-cli. + + process.execArgv.indexOf("--eval") === -1 + && process.execArgv.indexOf("-e") === -1 + && ( + ( + /[\/|\\]jslint(?:\.[cm]?js)?$/m + ).test(process_argv[1]) + || mode_cli + ) + && ( + moduleUrl.fileURLToPath(import_meta_url) + === + modulePath.resolve(process_argv[1]) + ) + ) + && !mode_cli + ) { + return exit_code; + } + +// init commmand + + command = String(process_argv[2]).split("="); + command[1] = command.slice(1).join("="); + + switch (command[0]) { + +// PR-362 - Add API Doc. + + case "jslint_apidoc": + await jslint_apidoc(Object.assign(JSON.parse(process_argv[3]), { + pathname: command[1] + })); + return; + +// PR-363 - Add command jslint_report. + + case "jslint_report": + mode_report = command[1]; + process_argv = process_argv.slice(1); + break; + +// COMMIT-b26d6df2 - Add command jslint_wrapper_vim. + + case "jslint_wrapper_vim": + mode_wrapper_vim = true; + process_argv = process_argv.slice(1); + break; + +// PR-364 - Add command v8_coverage_report. + + case "v8_coverage_report": + await v8CoverageReportCreate({ + consoleError: console_error, + coverageDir: command[1], + processArgv: process_argv.slice(3) + }); + return; + } + +// Normalize file relative to process.cwd(). + + process_argv.slice(2).some(function (arg) { + if (!arg.startsWith("-")) { + file = file || arg; + return true; + } + }); + if (!file) { + return; + } + file = modulePath.resolve(file) + "/"; + if (file.startsWith(process.cwd() + "/")) { + file = file.replace(process.cwd() + "/", "").slice(0, -1) || "."; + } + file = file.replace(( + /\\/g + ), "/").replace(( + /\/$/g + ), ""); + if (source) { + data = source; + } else { + +// jslint_cli - jslint directory. + + try { + data = await moduleFs.promises.readdir(file, "utf8"); + } catch (ignore) {} + if (data) { + await Promise.all(data.map(async function (file2) { + let code; + let time_start = Date.now(); + file2 = file + "/" + file2; + switch (( + /\.\w+?$|$/m + ).exec(file2)[0]) { + case ".cjs": + case ".html": + case ".js": + case ".json": + case ".md": + case ".mjs": + case ".sh": + break; + default: + return; + } + try { + code = await moduleFs.promises.readFile(file2, "utf8"); + } catch (ignore) { + return; + } + if ( + ( + /(?:\b|_)(?:lock|min|raw|rollup)(?:\b|_)/ + ).test(file2) + || !(code && code.length < 1048576) + ) { + return; + } + jslint_from_file({ + code, + file: file2, + option + }); + console_error( + "jslint - " + (Date.now() - time_start) + "ms - " + file2 + ); + })); + process_exit(exit_code); + return exit_code; + } + +// jslint_cli - jslint file. + + try { + data = await moduleFs.promises.readFile(file, "utf8"); + } catch (err) { + console_error(err); + exit_code = 1; + process_exit(exit_code); + return exit_code; + } + } + result = jslint_from_file({ + code: data, + file, + option + }); + if (mode_report) { + result = jslint.jslint_report(result); + result = `\n${result}\n`; + await fsWriteFileWithParents(mode_report, result); + } + process_exit(exit_code); + return exit_code; +} + +function jslint_phase1_split() { + +// PHASE 1. Split by newlines into . + + return; +} + +function jslint_phase2_lex(state) { + +// PHASE 2. Lex into . + + let { + artifact, + directive_list, + global_dict, + global_list, + line_list, + option_dict, + stop, + stop_at, + tenure, + test_cause, + token_global, + token_list, + warn, + warn_at + } = state; + let char; // The current character being lexed. + let column = 0; // The column number of the next character. + let from; // The starting column number of the token. + let from_mega; // The starting column of megastring. + let line = 0; // The line number of the next character. + let line_disable; // The starting line of "/*jslint-disable*/". + let line_mega; // The starting line of megastring. + let line_source = ""; // The remaining line source string. + let line_whole = ""; // The whole line source string. + let mode_digits_empty_string = 1; + let mode_digits_numeric_separator = 2; + let mode_directive = true; // true if directives are still allowed. + let mode_mega = false; // true if currently parsing a megastring + // ... literal. + let mode_regexp; // true if regular expression literal seen on + // ... this line. + let paren_backtrack_list = []; // List of most recent "(" tokens at any + // ... paren-depth. + let paren_depth = 0; // Keeps track of current paren-depth. + let snippet = ""; // A piece of string. + let token_1; // The first token. + let token_prv = token_global; // The previous token including + // ... comments. + let token_prv_expr = token_global; // The previous token excluding + // ... comments. + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions char_after, char_before, +// read_digits, and char_after_escape help in the parsing of literals. + + function char_after(match) { + +// Get the next character from the source line. Remove it from the line_source, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + +// test_cause: +// ["aa=/[", "char_after", "expected_a", "]", 5] +// ["aa=/aa{/", "char_after", "expected_a_b", "/", 8] + + return ( + char === "" + ? stop_at("expected_a", line, column - 1, match) + : stop_at("expected_a_b", line, column, match, char) + ); + } + char = line_source.slice(0, 1); + line_source = line_source.slice(1); + snippet += char || " "; + column += 1; + return char; + } + + function char_after_escape(extra) { + +// Validate char after escape "\\". + + char_after("\\"); + switch (char) { + case "": + +// test_cause: +// ["\"\\", "char_after_escape", "unclosed_string", "", 2] + + return stop_at("unclosed_string", line, column); + case "/": + return char_after(); + case "\\": + return char_after(); + case "`": + return char_after(); + case "b": + return char_after(); + case "f": + return char_after(); + case "n": + return char_after(); + case "r": + return char_after(); + case "t": + +// test_cause: +// ["\"\\/\\\\\\`\\b\\f\\n\\r\\t\"", "char_after_escape", "char_after", "", 0] + + test_cause("char_after"); + return char_after(); + case "u": + if (char_after("u") === "{") { + if (state.mode_json) { + +// test_cause: +// ["[\"\\u{12345}\"]", "char_after_escape", "unexpected_a", "{", 5] + + warn_at("unexpected_a", line, column, char); + } + if (read_digits("x", undefined) > 5) { + +// test_cause: +// ["\"\\u{123456}\"", "char_after_escape", "too_many_digits", "", 11] + + warn_at("too_many_digits", line, column); + } + if (char !== "}") { + +// test_cause: +// ["\"\\u{12345\"", "char_after_escape", "expected_a_before_b", "\"", 10] + + stop_at("expected_a_before_b", line, column, "}", char); + } + return char_after(); + } + char_before(); + if (read_digits("x", mode_digits_empty_string) < 4) { + +// test_cause: +// ["\"\\u0\"", "char_after_escape", "expected_four_digits", "", 5] + + warn_at("expected_four_digits", line, column); + } + return; + default: + if (extra && extra.indexOf(char) >= 0) { + return char_after(); + } + +// test_cause: +// ["\"\\0\"", "char_after_escape", "unexpected_a_before_b", "0", 3] + + warn_at("unexpected_a_before_b", line, column, "\\", char); + } + } + + function char_before() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the line_source. + + char = snippet.slice(-1); + line_source = char + line_source; + column -= char.length; + +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + return char; + } + + function check_numeric_separator(digits, column) { + +// This function will check for illegal numeric-separator in . + + digits.replace(( + jslint_rgx_numeric_separator_illegal + ), function (ignore, ii) { + +// test_cause: +// ["0x0_0_;", "check_numeric_separator", "illegal_num_separator", "", 6] +// ["0x0_0__0;", "check_numeric_separator", "illegal_num_separator", "", 6] +// ["aa=1_2_;", "check_numeric_separator", "illegal_num_separator", "", 7] +// ["aa=1_2__3;", "check_numeric_separator", "illegal_num_separator", "", 7] +// ["aa=1_2_n;", "check_numeric_separator", "illegal_num_separator", "", 7] + + warn_at("illegal_num_separator", line, column + ii + 1); + return ""; + }); + } + + function lex_comment() { + let body; + let ii = 0; + let jj = 0; + let the_comment; + +// Create a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + +// Create token from comment //.... + + if (snippet === "//") { + snippet = line_source; + line_source = ""; + the_comment = token_create("(comment)", snippet); + if (mode_mega) { + +// test_cause: +// ["`${//}`", "lex_comment", "unexpected_comment", "`", 4] + + warn("unexpected_comment", the_comment, "`"); + } + +// Create token from comment /*...*/. + + } else { + snippet = []; + if (line_source[0] === "/") { + +// test_cause: +// ["/*/", "lex_comment", "unexpected_a", "/", 2] + + warn_at("unexpected_a", line, column + ii, "/"); + } + +// Lex/loop through each line until "*/". + + while (true) { + // jslint_rgx_star_slash + ii = line_source.indexOf("*/"); + if (ii >= 0) { + break; + } + // jslint_rgx_slash_star + ii = line_source.indexOf("/*"); + if (ii >= 0) { + +// test_cause: +// ["/*/*", "lex_comment", "nested_comment", "", 2] + + warn_at("nested_comment", line, column + ii); + } + snippet.push(line_source); + line_source = read_line(); + if (line_source === undefined) { + +// test_cause: +// ["/*", "lex_comment", "unclosed_comment", "", 1] + + return stop_at("unclosed_comment", line, column); + } + } + jj = line_source.slice(0, ii).search( + jslint_rgx_slash_star_or_slash + ); + if (jj >= 0) { + +// test_cause: +// ["/*/**/", "lex_comment", "nested_comment", "", 2] + + warn_at("nested_comment", line, column + jj); + } + snippet.push(line_source.slice(0, ii)); + snippet = snippet.join(" "); + column += ii + 2; + line_source = line_source.slice(ii + 2); + the_comment = token_create("(comment)", snippet); + } + +// Uncompleted work comment. + + if (!option_dict.devel && jslint_rgx_todo.test(snippet)) { + +// test_cause: +// ["//todo", "lex_comment", "todo_comment", "(comment)", 1] //jslint-ignore-line + + warn("todo_comment", the_comment); + } + +// Lex directives in comment. + + [ + the_comment.directive, body + ] = Array.from(snippet.match(jslint_rgx_directive) || []).slice(1); + if (the_comment.directive === undefined) { + return the_comment; + } + directive_list.push(the_comment); + if (!mode_directive) { + +// test_cause: +// ["0\n/*global aa*/", "lex_comment", "misplaced_directive_a", "global", 1] + + warn_at("misplaced_directive_a", line, from, the_comment.directive); + return the_comment; + } + +// lex_directive(); +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + +// Lex/loop through each directive in /*...*/ + + ii = 0; + body.replace(jslint_rgx_directive_part, function ( + match0, + key, + val, + jj + ) { + if (ii !== jj) { + +// test_cause: +// ["/*jslint !*/", "lex_comment", "bad_directive_a", "!", 1] + + return stop("bad_directive_a", the_comment, body.slice(ii)); + } + if (match0 === "") { + return ""; + } + ii += match0.length; + switch (the_comment.directive) { + case "global": + if (val) { + +// test_cause: +// ["/*global aa:false*/", "lex_comment", "bad_option_a", "aa:false", 1] + + warn("bad_option_a", the_comment, key + ":" + val); + } + global_dict[key] = "user-defined"; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// state.mode_module = the_comment; + + break; + case "jslint": + if (!option_set_item(key, val !== "false")) { + +// test_cause: +// ["/*jslint undefined*/", "lex_comment", "bad_option_a", "undefined", 1] + + warn("bad_option_a", the_comment, key); + } + break; + case "property": + state.mode_property = true; + tenure[key] = true; + break; + } + return ""; + }); + return the_comment; + } + + function lex_megastring() { + let id; + let match; + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (mode_mega) { + +// test_cause: +// ["`${`", "lex_megastring", "expected_a_b", "`", 4] + + return stop_at("expected_a_b", line, column, "}", "`"); + } + from_mega = from; + line_mega = line; + mode_mega = true; + snippet = ""; + +// Parsing a mega literal is tricky. First create a ` token. + + token_create("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + while (true) { + match = line_source.match(jslint_rgx_mega) || { + "0": "", + index: 0 + }; + snippet += line_source.slice(0, match.index); + column += match.index; + line_source = line_source.slice(match.index); + match = match[0]; + switch (match) { + case "${": + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + token_create("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then create tokens that will become part of an expression until +// a } token is made. + + column += 2; + token_create("${"); + line_source = line_source.slice(2); + +// Lex/loop through each token inside megastring-expression `${...}`. + + while (true) { + id = lex_token().id; + if (id === "{") { + +// test_cause: +// ["`${{", "lex_megastring", "expected_a_b", "{", 4] + + return stop_at("expected_a_b", line, column, "}", "{"); + } + if (id === "}") { + break; + } + } + break; + case "\\": + snippet += line_source.slice(0, 2); + line_source = line_source.slice(2); + column += 2; + break; + case "`": + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + token_create("(string)", snippet).quote = "`"; + snippet = ""; + +// Terminate megastring with `. + + line_source = line_source.slice(1); + column += 1; + mode_mega = false; + return token_create("`"); + default: + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + snippet += line_source + "\n"; + if (read_line() === undefined) { + +// test_cause: +// ["`", "lex_megastring", "unclosed_mega", "", 1] + + return stop_at("unclosed_mega", line_mega, from_mega); + } + } + } + } + + function lex_number() { + let prefix = snippet; + +// PR-390 - Add numeric-separator check. + + check_numeric_separator(prefix, column - prefix.length); + char_after(); + switch (prefix === "0" && char) { + case "b": + case "o": + case "x": + read_digits(char, mode_digits_numeric_separator); + +// PR-351 - Ignore BigInt suffix 'n'. + + if (char === "n") { + char_after("n"); + } + break; + default: + if (char === ".") { + read_digits("d", mode_digits_numeric_separator); + } + if (char === "E" || char === "e") { + char_after(char); + if (char !== "+" && char !== "-") { + char_before(); + } + read_digits("d", mode_digits_numeric_separator); + } + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + +// test_cause: +// ["0a", "lex_number", "unexpected_a_after_b", "0", 2] + + return stop_at( + "unexpected_a_after_b", + line, + column, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + char_before(); + return token_create("(number)", snippet); + } + + function lex_regexp() { + +// Regexp +// Lex a regular expression literal. + + let flag; + let mode_regexp_multiline; + let result; + let value; + mode_regexp = true; + + function lex_regexp_bracketed() { + let mode_regexp_range; + +// RegExp +// Match a class. + + char_after("["); + if (char === "^") { + char_after("^"); + } + while (true) { + +// RegExp +// Match a character in a character class. + + switch (char) { + case "": + case "]": + +// test_cause: +// ["aa=/[", "lex_regexp_bracketed", "closer", "", 0] +// ["aa=/[]/", "lex_regexp_bracketed", "closer", "", 0] + + test_cause("closer"); + if (mode_regexp_range) { + +// test_cause: +// ["aa=/[0-]/", "lex_regexp_bracketed", "unexpected_a", "-", 7] + + warn_at("unexpected_a", line, column - 1, "-"); + } + return char_after("]"); + +// PR-362 - Relax regexp-warning against using . +// +// case " ": +// +// // test_cause: +// // ["aa=/[ ]/", "lex_regexp_bracketed", "expected_a_b", " ", 6] +// +// warn_at("expected_a_b", line, column, "\\u0020", " "); +// break; + + case "-": + case "/": + case "[": + case "^": + +// test_cause: +// ["aa=/[-]/", "lex_regexp_bracketed", "expected_a_before_b", "-", 6] +// ["aa=/[.^]/", "lex_regexp_bracketed", "expected_a_before_b", "^", 7] +// ["aa=/[/", "lex_regexp_bracketed", "expected_a_before_b", "/", 6] +// ["aa=/[\\\\/]/", "lex_regexp_bracketed", "expected_a_before_b", "/", 8] +// ["aa=/[\\\\[]/", "lex_regexp_bracketed", "expected_a_before_b", "[", 8] + + warn_at("expected_a_before_b", line, column, "\\", char); + break; + case "\\": + char_after_escape("BbDdSsWw-[]^"); + char_before(); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${/[`]/}`", "lex_regexp_bracketed", "unexpected_a", "`", 6] + + warn_at("unexpected_a", line, column, "`"); + } + break; + } + char_after(); + mode_regexp_range = false; + if (char === "-") { + +// RegExp +// Match a range of subclasses. + + mode_regexp_range = true; + char_after("-"); + } + } + } + + function lex_regexp_group() { + +// RegExp +// Lex sequence of characters in regexp. + + switch (char) { + case "": + warn_at("expected_regexp_factor_a", line, column, char); + break; + case ")": + warn_at("expected_regexp_factor_a", line, column, char); + break; + case "]": + +// test_cause: +// ["/ /", "lex_regexp_group", "expected_regexp_factor_a", "", 3] +// ["aa=/)", "lex_regexp_group", "expected_regexp_factor_a", ")", 5] +// ["aa=/]", "lex_regexp_group", "expected_regexp_factor_a", "]", 5] + + warn_at("expected_regexp_factor_a", line, column, char); + break; + } + while (true) { + switch (char) { + case "": + case ")": + case "/": + case "]": + return; + +// PR-362 - Relax regexp-warning against using . +// +// case " ": +// +// // test_cause: +// // ["aa=/ /", "lex_regexp_group", "expected_a_b", " ", 5] +// +// warn_at("expected_a_b", line, column, "\\s", " "); +// char_after(); +// break; + + case "$": + if (line_source[0] !== "/") { + mode_regexp_multiline = true; + } + char_after(); + break; + case "(": + +// RegExp +// Match a group that starts with left paren. + + char_after("("); + switch (char) { + case ":": + +// test_cause: +// ["aa=/(:)/", "lex_regexp_group", "expected_a_before_b", ":", 6] +// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5] + + warn_at("expected_a_before_b", line, column, "?", ":"); + break; + case "?": + char_after("?"); + switch (char) { + case "!": + +// PR-437 - Add grammar for regexp-named-capture-group. + + case "<": + case "=": + char_after(); + break; + default: + char_after(":"); + } + break; + } + +// RegExp +// Recurse lex_regexp_group(). + + lex_regexp_group(); + char_after(")"); + break; + case "*": + case "+": + case "?": + case "{": + case "}": + +// test_cause: +// ["aa=/+/", "lex_regexp_group", "expected_a_before_b", "+", 5] +// ["aa=/.**/", "lex_regexp_group", "expected_a_before_b", "*", 7] +// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5] +// ["aa=/{/", "lex_regexp_group", "expected_a_before_b", "{", 5] +// ["aa=/}/", "lex_regexp_group", "expected_a_before_b", "}", 5] + + warn_at("expected_a_before_b", line, column, "\\", char); + char_after(); + break; + case "[": + lex_regexp_bracketed(); + break; + case "\\": + +// test_cause: +// ["aa=/\\/", "lex_regexp_group", "escape", "", 0] + + test_cause("escape"); + +// PR-437 - Add grammar for regexp-named-backreference. + + char_after_escape("BbDdSsWw^${}[]():=!.|*+?k"); + break; + case "^": + if (snippet !== "^") { + mode_regexp_multiline = true; + } + char_after(); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${/`/}`", "lex_regexp_group", "unexpected_a", "`", 5] + + warn_at("unexpected_a", line, column, "`"); + } + char_after(); + break; + default: + char_after(); + } + +// RegExp +// Match an optional quantifier. + + switch (char) { + case "*": + case "+": + if (char_after(char) === "?") { + +// test_cause: +// ["aa=/.*?/", "lex_regexp_group", "?", "", 0] +// ["aa=/.+?/", "lex_regexp_group", "?", "", 0] + + test_cause("?"); + char_after("?"); + } + break; + case "?": + if (char_after("?") === "?") { + +// test_cause: +// ["aa=/.??/", "lex_regexp_group", "unexpected_a", "?", 7] + + warn_at("unexpected_a", line, column, char); + char_after("?"); + } + break; + case "{": + if (read_digits("d", mode_digits_empty_string) === 0) { + +// test_cause: +// ["aa=/aa{/", "lex_regexp_group", "expected_a_before_b", ",", 8] + + warn_at("expected_a_before_b", line, column, "0", ","); + } + if (char === ",") { + +// test_cause: +// ["aa=/.{,/", "lex_regexp_group", "comma", "", 0] + + test_cause("comma"); + read_digits("d", mode_digits_empty_string); + } + if (char_after("}") === "?") { + +// test_cause: +// ["aa=/.{0}?/", "lex_regexp_group", "unexpected_a", "?", 9] + + warn_at("unexpected_a", line, column, char); + char_after("?"); + } + break; + } + } + } + +// RegExp +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + char_after(); + if (char === "=") { + +// test_cause: +// ["aa=/=/", "lex_regexp", "expected_a_before_b", "=", 5] + + warn_at("expected_a_before_b", line, column, "\\", "="); + } + lex_regexp_group(); + +// RegExp +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + +// RegExp +// Make sure there is a closing slash. + + value = snippet; + char_after("/"); + +// RegExp +// Create flag. + + flag = empty(); + while ( + +// Regexp +// char is a letter. + + (char >= "a" && char <= "z\uffff") + || (char >= "A" && char <= "Z\uffff") + ) { + +// RegExp +// Process dangling flag letters. + + switch (!flag[char] && char) { + case "g": + break; + case "i": + break; + case "m": + break; + case "u": + break; + case "y": + +// test_cause: +// ["aa=/./gimuy", "lex_regexp", "flag", "", 0] + + test_cause("flag"); + break; + default: + +// test_cause: +// ["aa=/./gg", "lex_regexp", "unexpected_a", "g", 8] +// ["aa=/./z", "lex_regexp", "unexpected_a", "z", 7] + + warn_at("unexpected_a", line, column, char); + } + flag[char] = true; + char_after(); + } + char_before(); + if (char === "/" || char === "*") { + +// test_cause: +// ["aa=/.//", "lex_regexp", "unexpected_a", "/", 3] + + return stop_at("unexpected_a", line, from, char); + } + result = token_create("(regexp)", char); + result.flag = flag; + result.value = value; + if (mode_regexp_multiline && !flag.m) { + +// test_cause: +// ["aa=/$^/", "lex_regexp", "missing_m", "", 7] + + warn_at("missing_m", line, column); + } + return result; + } + + function lex_slash_or_regexp() { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + let the_token; + switch ( + token_prv_expr.identifier + && !token_prv_expr.dot + && token_prv_expr.id + ) { + case "case": + case "delete": + case "in": + case "instanceof": + case "new": + case "typeof": + case "void": + case "yield": + the_token = lex_regexp(); + +// test_cause: +// ["case /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6] +// ["delete /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8] +// ["in /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 4] +// ["instanceof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 12] +// ["new /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 5] +// ["typeof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8] +// ["void /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6] +// ["yield /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 7] + + return stop("unexpected_a", the_token); + case "return": + return lex_regexp(); + } + switch (!token_prv_expr.identifier && token_prv_expr.id.slice(-1)) { + case "!": + case "%": + case "&": + case "*": + case "+": + case "-": + case "/": + case ";": + case "<": + case ">": + case "^": + case "{": + case "|": + case "}": + case "~": + the_token = lex_regexp(); + +// test_cause: +// ["!/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["%/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["&/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["+/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["-/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["0 * /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5] +// ["0 / /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5] +// [";/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["^/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["{/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["|/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["}/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["~/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] + + warn("wrap_regexp", the_token); + return the_token; + case "(": + case ",": + case ":": + case "=": + case "?": + case "[": + +// test_cause: +// ["(/./", "lex_slash_or_regexp", "recurse", "", 0] +// [",/./", "lex_slash_or_regexp", "recurse", "", 0] +// [":/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["=/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["?/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["aa[/./", "lex_slash_or_regexp", "recurse", "", 0] + + test_cause("recurse"); + return lex_regexp(); + } + if (line_source[0] === "=") { + column += 1; + line_source = line_source.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + return token_create(snippet); + } + + function lex_string(quote) { + +// Create a string token. + + let the_token; + if (!option_dict.single && quote === "'") { + +// test_cause: +// ["''", "lex_string", "use_double", "", 1] + + warn_at("use_double", line, column); + } + snippet = ""; + char_after(); + +// Lex/loop through each character in "...". + + while (true) { + switch (char) { + case "": + +// test_cause: +// ["\"", "lex_string", "unclosed_string", "", 1] + + return stop_at("unclosed_string", line, column); + case "\\": + char_after_escape(quote); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${\"`\"}`", "lex_string", "unexpected_a", "`", 5] + + warn_at("unexpected_a", line, column, "`"); + } + char_after("`"); + break; + case quote: + +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + the_token = token_create("(string)", snippet); + the_token.quote = quote; + return the_token; + default: + char_after(); + } + } + } + + function lex_token() { + let match; + +// Lex/loop through each whitespace. + + while (true) { + +// Lex/loop through each blank-line. + + while (!line_source) { + line_source = read_line(); + from = 0; + if (line_source === undefined) { + return ( + mode_mega + +// test_cause: +// ["`${//}`", "lex_token", "unclosed_mega", "", 1] + + ? stop_at("unclosed_mega", line_mega, from_mega) + : line_disable !== undefined + +// test_cause: +// ["/*jslint-disable*/", "lex_token", "unclosed_disable", "", 1] + + ? stop_at("unclosed_disable", line_disable) + : token_create("(end)") + ); + } + } + from = column; + match = line_source.match(jslint_rgx_token); + +// match[1] token +// match[2] whitespace +// match[3] identifier +// match[4] number +// match[5] rest + + if (!match) { + +// test_cause: +// ["#", "lex_token", "unexpected_char_a", "#", 1] + + return stop_at( + "unexpected_char_a", + line, + column, + line_source[0] + ); + } + snippet = match[1]; + column += snippet.length; + line_source = match[5]; + if (!match[2]) { + break; + } + } + +// The token is an identifier. + + if (match[3]) { + return token_create(snippet, undefined, true); + } + +// Create token from number. + + if (match[4]) { + return lex_number(); + } + +// Create token from string "..." or '...'. + + if (snippet === "\"" || snippet === "'") { + return lex_string(snippet); + } + +// Create token from megastring `...`. + + if (snippet === "`") { + return lex_megastring(); + } + +// Create token from comment /*...*/ or //.... + + if (snippet === "/*" || snippet === "//") { + return lex_comment(); + } + +// Create token from slash /. + + if (snippet === "/") { + return lex_slash_or_regexp(); + } + return token_create(snippet); + } + + function option_set_item(key, val) { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + switch (key) { + case "beta": // Enable experimental warnings. + case "bitwise": // Allow bitwise operator. + case "browser": // Assume browser environment. + case "convert": // Allow conversion operator. + case "couch": // Assume CouchDb environment. + case "devel": // Allow console.log() and friends. + case "ecma": // Assume ECMAScript environment. + case "eval": // Allow eval(). + case "fart": // Allow complex fat-arrow. + case "for": // Allow for-statement. + case "getset": // Allow get() and set(). + case "indent2": // Use 2-space indent. + case "long": // Allow long lines. + case "node": // Assume Node.js environment. + case "nomen": // Allow weird property name. + case "single": // Allow single-quote strings. + case "subscript": // Allow identifier in subscript-notation. + case "test_cause": // Test jslint's causes. + case "test_internal_error": // Test jslint's internal-error + // ... handling-ability. + case "this": // Allow 'this'. + case "trace": // Include jslint stack-trace in warnings. + case "unordered": // Allow unordered cases, params, properties, + // ... variables, and exports. + case "variable": // Allow unordered const and let declarations + // ... that are not at top of function-scope. + case "white": // Allow messy whitespace. + option_dict[key] = val; + break; + +// PR-404 - Alias "evil" to jslint-directive "eval" for backwards-compat. + + case "evil": + return option_set_item("eval", val); + +// PR-404 - Alias "nomen" to jslint-directive "name" for backwards-compat. + + case "name": + return option_set_item("nomen", val); + default: + return false; + } + +// Initialize global-variables. + + switch (val && key) { + +// Assign global browser variables to global_dict. +/* +// /\*jslint beta, browser, devel*\/ +console.log(JSON.stringify(Object.keys(window).sort(), undefined, 4)); +*/ + + case "browser": + object_assign_from_list(global_dict, [ + +// Shared with Node.js. + + "AbortController", + // "Buffer", + // "Crypto", + // "CryptoKey", + "Event", + "EventTarget", + "MessageChannel", + "MessageEvent", + "MessagePort", + // "Request", + // "Response", + // "SubtleCrypto", + "TextDecoder", + "TextEncoder", + "URL", + "URLSearchParams", + "WebAssembly", + // "__dirname", + // "__filename", + // "atob", + // "btoa", + // "clearImmediate", + "clearInterval", + "clearTimeout", + // "console", + // "crypto", + // "exports", + // "fetch", + // "global", + // "module", + "performance", + // "process", + "queueMicrotask", + // "require", + // "setImmediate", + "setInterval", + "setTimeout", + +// Web worker only. +// https://github.com/mdn/content/blob/main/files/en-us/web/api +// /workerglobalscope/index.md + + "importScripts", + +// Window. + + "Blob", + // "CharacterData", + // "DocumentType", + // "Element", + // "Event", + "FileReader", + // "FontFace", + "FormData", + "IntersectionObserver", + "MutationObserver", + // "Storage", + // "TextDecoder", + // "TextEncoder", + // "URL", + "Worker", + "XMLHttpRequest", + // "caches", + // "clearInterval", + // "clearTimeout", + "document", + // "event", + "fetch", + // "history", + "indexedDb", + "localStorage", + "location", + // "name", + "navigator", + "postMessage", + // "screen", + "sessionStorage", + // "setInterval", + // "setTimeout", + "structuredClone", + "window" + ], "browser"); + break; + +// https://docs.couchdb.org/en/stable/query-server/javascript.html#javascript + + case "couch": + object_assign_from_list(global_dict, [ + "emit", + "getRow", + "isArray", + "log", + "provides", + "registerType", + "require", + "send", + "start", + "sum", + "toJSON" + ], "CouchDb"); + break; + case "devel": + object_assign_from_list(global_dict, [ + "alert", "confirm", "console", "prompt" + ], "development"); + break; + +// These are the globals that are provided by the language standard. +// Assign global ECMAScript variables to global_dict. +/* +node --input-type=module --eval ' +// /\*jslint beta, node*\/ +import https from "https"; +(async function () { + let dict = {import: true}; + let result = ""; + await new Promise(function (resolve) { + https.get(( + "https://raw.githubusercontent.com/mdn/content/main/files" + + "/en-us/web/javascript/reference/global_objects/index.md" + ), function (res) { + res.on("data", function (chunk) { + result += chunk; + }).on("end", resolve).setEncoding("utf8"); + }); + }); + result.replace(( + /\n- \{\{JSxRef\("(?:Global_Objects\/)?([^"\/]+?)"/g + ), function (ignore, key) { + if (globalThis.hasOwnProperty(key)) { + dict[key] = true; + } + return ""; + }); + console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4)); +}()); +' +*/ + + case "ecma": + object_assign_from_list(global_dict, [ + "AggregateError", + "Array", + "ArrayBuffer", + "Atomics", + "BigInt", + "BigInt64Array", + "BigUint64Array", + "Boolean", + "DataView", + "Date", + "Error", + "EvalError", + "Float32Array", + "Float64Array", + "Function", + "Infinity", + "Int16Array", + "Int32Array", + "Int8Array", + "Intl", + "JSON", + "Map", + "Math", + "NaN", + "Number", + "Object", + "Promise", + "Proxy", + "RangeError", + "ReferenceError", + "Reflect", + "RegExp", + "Set", + "SharedArrayBuffer", + "String", + "Symbol", + "SyntaxError", + "TypeError", + "URIError", + "Uint16Array", + "Uint32Array", + "Uint8Array", + "Uint8ClampedArray", + "WeakMap", + "WeakSet", + "WebAssembly", + "decodeURI", + "decodeURIComponent", + "encodeURI", + "encodeURIComponent", + "eval", + "globalThis", + "import", + "isFinite", + "isNaN", + "parseFloat", + "parseInt", + "undefined" + ], "ECMAScript"); + break; + +// Assign global Node.js variables to global_dict. +/* +node --input-type=module --eval ' +// /\*jslint beta, node*\/ +import moduleHttps from "https"; +(async function () { + let dict = Object.create(null); + let result = ""; + await new Promise(function (resolve) { + moduleHttps.get(( + "https://raw.githubusercontent.com/nodejs/node/v16.x/doc/api" + + "/globals.md" + ), function (res) { + res.on("data", function (chunk) { + result += chunk; + }).on("end", resolve).setEncoding("utf8"); + }); + }); + result.replace(( + /\n(?:\* \[`|## |## Class: )`\w+/g + ), function (match0) { + dict[match0.split("`")[1]] = true; + return ""; + }); + console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4)); +}()); +' +*/ + + case "node": + object_assign_from_list(global_dict, [ + "AbortController", + "Buffer", + // "Crypto", + // "CryptoKey", + "Event", + "EventTarget", + "MessageChannel", + "MessageEvent", + "MessagePort", + // "Request", + // "Response", + // "SubtleCrypto", + "TextDecoder", + "TextEncoder", + "URL", + "URLSearchParams", + "WebAssembly", + "__dirname", + "__filename", + // "atob", + // "btoa", + "clearImmediate", + "clearInterval", + "clearTimeout", + "console", + // "crypto", + "exports", + // "fetch", + "global", + "module", + "performance", + "process", + "queueMicrotask", + "require", + "setImmediate", + "setInterval", + "setTimeout" + ], "Node.js"); + break; + } + return true; + } + + function read_digits(base, mode) { + let digits = line_source.match( + base === "b" + ? jslint_rgx_digits_bits + : base === "o" + ? jslint_rgx_digits_octals + : base === "x" + ? jslint_rgx_digits_hexs + : jslint_rgx_digits_decimals + )[0]; + if ( + (mode !== mode_digits_empty_string && digits.length === 0) + || digits[0] === "_" + ) { + +// test_cause: +// ["0x", "read_digits", "expected_digits_after_a", "0x", 2] +// ["0x_", "read_digits", "expected_digits_after_a", "0x", 2] + + warn_at("expected_digits_after_a", line, column, snippet); + } + +// PR-390 - Add numeric-separator check. + + if (mode === mode_digits_numeric_separator) { + check_numeric_separator(digits, column); + } else if (digits.indexOf("_") >= 0) { + +// test_cause: +// ["\"\\u{1_2}\"", "read_digits", "illegal_num_separator", "", 6] + + warn_at( + "illegal_num_separator", + line, + column + digits.indexOf("_") + 1 + ); + } + column += digits.length; + line_source = line_source.slice(digits.length); + snippet += digits; + char_after(); + return digits.length; + } + + function read_line() { + +// Put the next line of source in line_source. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + if ( + !option_dict.long + && line_whole.length > 80 + && line_disable === undefined + && !state.mode_json + && token_1 + && !mode_regexp + ) { + +// test_cause: +// ["/////////////////////////////////////////////////////////////////////////////////", "read_line", "too_long", "", 1] //jslint-ignore-line + + warn_at("too_long", line); + } + column = 0; + line += 1; + mode_regexp = false; + line_source = undefined; + line_whole = ""; + if (line_list[line] === undefined) { + return line_source; + } + line_source = line_list[line].line_source; + line_whole = line_source; + +// Scan each line for following ignore-directives: +// "/*jslint-disable*/" +// "/*jslint-enable*/" +// "//jslint-ignore-line" + + if (line_source === "/*jslint-disable*/") { + +// test_cause: +// ["/*jslint-disable*/", "read_line", "jslint_disable", "", 0] + + test_cause("jslint_disable"); + line_disable = line; + } else if (line_source === "/*jslint-enable*/") { + if (line_disable === undefined) { + +// test_cause: +// ["/*jslint-enable*/", "read_line", "unopened_enable", "", 1] + + stop_at("unopened_enable", line); + } + line_disable = undefined; + } else if ( + line_source.endsWith(" //jslint-ignore-line") + || line_source.endsWith(" //jslint-quiet") + ) { + +// test_cause: +// ["0 //jslint-ignore-line", "read_line", "jslint_ignore_line", "", 0] + + test_cause("jslint_ignore_line"); + line_list[line].directive_ignore_line = true; + } + if (line_disable !== undefined) { + +// test_cause: +// ["/*jslint-disable*/\n0", "read_line", "line_disable", "", 0] + + test_cause("line_disable"); + line_source = ""; + } + // jslint_rgx_tab + if (line_source.indexOf("\t") >= 0) { + if (!option_dict.white) { + +// test_cause: +// ["\t", "read_line", "use_spaces", "", 1] + + warn_at("use_spaces", line, line_source.indexOf("\t") + 1); + } + line_source = line_source.replace(jslint_rgx_tab, " "); + } + if (!option_dict.white && line_source.endsWith(" ")) { + +// test_cause: +// [" ", "read_line", "unexpected_trailing_space", "", 1] + + warn_at("unexpected_trailing_space", line, line_source.length - 1); + } + return line_source; + } + + function token_create(id, value, identifier) { + +// Create the token object and append it to token_list. + + let the_token = { + from, + id, + identifier: Boolean(identifier), + line, + nr: token_list.length, + thru: column, + value + }; + token_list.push(the_token); + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + mode_directive = false; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option_dict.white. + + if ( + token_prv.line === line + && token_prv.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (token_prv.id === "(comment)" || token_prv.id === "(regexp)") + ) { + +// test_cause: +// ["/**//**/", "token_create", "expected_space_a_b", "(comment)", 5] + + warn( + "expected_space_a_b", + the_token, + artifact(token_prv), + artifact(the_token) + ); + } + if (token_prv.id === "." && id === "(number)") { + +// test_cause: +// [".0", "token_create", "expected_a_before_b", ".", 1] + + warn("expected_a_before_b", token_prv, "0", "."); + } + if (token_prv_expr.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart. +// Farts are now detected by keeping a list of most recent "(" tokens at any +// given depth. When a "=>" token is encountered, the most recent "(" token at +// current depth is marked as a fart. + + switch (id) { + case "(": + paren_backtrack_list[paren_depth] = the_token; + paren_depth += 1; + break; + case ")": + paren_depth -= 1; + break; + case "=>": + if ( + token_prv_expr.id === ")" + && paren_backtrack_list[paren_depth] + ) { + paren_backtrack_list[paren_depth].fart = the_token; + } + break; + } + +// The previous token is used to detect adjacency problems. + + token_prv = the_token; + +// The token_prv_expr token is a previous token that was not a comment. +// The token_prv_expr token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (token_prv.id !== "(comment)") { + token_prv_expr = token_prv; + } + return the_token; + } + +// Init global_dict and option_dict. + + option_set_item("ecma", true); + Object.keys(option_dict).sort().forEach(function (key) { + option_set_item(key, option_dict[key] === true); + }); + object_assign_from_list(global_dict, global_list, "user-defined"); + +// Scan first line for "#!" and ignore it. + + if (line_list[jslint_fudge].line_source.startsWith("#!")) { + line += 1; + state.mode_shebang = true; + } + token_1 = lex_token(); + state.mode_json = token_1.id === "{" || token_1.id === "["; + +// Lex/loop through each token until (end). + + while (true) { + if (lex_token().id === "(end)") { + break; + } + } +} + +function jslint_phase3_parse(state) { + +// PHASE 3. Parse into using the Pratt-parser. + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + + let anon = "anonymous"; // The guessed name for anonymous functions. + let { + artifact, + catch_list, + catch_stack, + export_dict, + function_list, + function_stack, + global_dict, + import_list, + is_equal, + option_dict, + property_dict, + stop, + syntax_dict, + tenure, + test_cause, + token_global, + token_list, + warn, + warn_at + } = state; + let catchage = catch_stack[0]; // The current catch-block. + let functionage = token_global; // The current function. + let mode_var; // "var" if using var; "let" if using let. + let token_ii = 0; // The number of the next token. + let token_now = token_global; // The current token being examined in + // ... the parse. + let token_nxt = token_global; // The next token to be examined in + // ... . + + function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if ( + token_now.identifier + && token_now.id !== "function" + && token_now.id !== "async" + ) { + anon = token_now.id; + } else if ( + token_now.id === "(string)" + && jslint_rgx_identifier.test(token_now.value) + ) { + anon = token_now.value; + } + +// Attempt to match token_nxt with an expected id. + + if (id !== undefined && token_nxt.id !== id) { + return ( + match === undefined + +// test_cause: +// ["{0:0}", "advance", "expected_a_b", "0", 2] + + ? stop("expected_a_b", token_nxt, id, artifact()) + +// test_cause: +// ["{\"aa\":0", "advance", "expected_a_b_from_c_d", "{", 1] + + : stop( + "expected_a_b_from_c_d", + token_nxt, + id, + artifact(match), + match.line, + artifact() + ) + ); + } + +// Promote the tokens, skipping comments. + + token_now = token_nxt; + while (true) { + token_nxt = token_list[token_ii]; + state.token_nxt = token_nxt; + token_ii += 1; + if (token_nxt.id !== "(comment)") { + if (token_nxt.id === "(end)") { + token_ii -= 1; + } + break; + } + if (state.mode_json) { + +// test_cause: +// ["[//]", "advance", "unexpected_a", "(comment)", 2] + + warn("unexpected_a"); + } + } + } + + function assignment(id) { + +// Create an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led_infix = function (left) { + const the_token = token_now; + let right; + the_token.arity = "assignment"; + right = parse_expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "preassign" + || right.arity === "postassign" + ) { + warn("unexpected_a", right); + } + check_mutation(left); + return the_token; + }; + return the_symbol; + } + + function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token_now; + if (special !== "body") { + functionage.statement_prv = the_block; + } + the_block.arity = "statement"; + the_block.body = special === "body"; + +// Top level function bodies may include the "use strict" pragma. + + if ( + special === "body" + && function_stack.length === 1 + && token_nxt.value === "use strict" + ) { + token_nxt.statement = true; + advance("(string)"); + advance(";"); + } + stmts = parse_statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option_dict.devel && special !== "ignore") { + +// test_cause: +// ["function aa(){}", "block", "empty_block", "{", 14] + + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; + } + + function check_left(left, right) { + +// Warn if the left is not one of these: +// ?. +// ?: +// e() +// e.b +// e[b] +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !check_left(left.expression[1]) + && !check_left(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "?." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right || token_nxt); + return false; + } + return true; + } + + function check_mutation(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + +// test_cause: +// ["0=0", "check_mutation", "bad_assignment_a", "0", 1] + + warn("bad_assignment_a", the_thing); + return false; + } + return true; + } + + function check_not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === token_global) { + +// test_cause: +// [" +// while(0){} +// ", "check_not_top_level", "unexpected_at_top_level_a", "while", 1] + + warn("unexpected_at_top_level_a", thing); + } + } + + function check_ordered(type, token_list) { + +// This function will warn if is unordered. + + token_list.reduce(function (aa, token) { + const bb = artifact(token); + if (!option_dict.unordered && aa > bb) { + warn("expected_a_b_before_c_d", token, type, bb, type, aa); + } + return bb; + }, ""); + } + + function check_ordered_case(case_list) { + +// This function will warn if is unordered. + + case_list.filter(noop).map(function (token) { + switch (token.identifier || token.id) { + case "(number)": + return { + order: 1, + token, + type: "number", + value: Number(artifact(token)) + }; + case "(string)": + return { + order: 2, + token, + type: "string", + value: artifact(token) + }; + case true: + return { + order: 3, + token, + type: "identifier", + value: artifact(token) + }; + } + }).reduce(function (aa, bb) { + if ( + +// PR-419 - Hide warning about unordered case-statements behind beta-flag. + + option_dict.beta + && !option_dict.unordered + && aa && bb + && ( + aa.order > bb.order + || (aa.order === bb.order && aa.value > bb.value) + ) + ) { + warn( + "expected_a_b_before_c_d", + bb.token, + `case-${bb.type}`, + bb.value, + `case-${aa.type}`, + aa.value + ); + } + return bb; + }, undefined); + } + + function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = token_nxt; + let the_value; + +// test_cause: +// ["do{}while()", "condition", "", "", 0] +// ["if(){}", "condition", "", "", 0] +// ["while(){}", "condition", "", "", 0] + + test_cause(""); + the_paren.free = true; + advance("("); + the_value = parse_expression(0); + advance(")"); + if (the_value.wrapped === true) { + +// test_cause: +// ["while((0)){}", "condition", "unexpected_a", "(", 6] + + warn("unexpected_a", the_paren); + } + +// Check for anticondition. + + switch (the_value.id) { + case "%": + warn("unexpected_a", the_value); + break; + case "&": + warn("unexpected_a", the_value); + break; + case "(number)": + warn("unexpected_a", the_value); + break; + case "(string)": + warn("unexpected_a", the_value); + break; + case "*": + warn("unexpected_a", the_value); + break; + case "+": + warn("unexpected_a", the_value); + break; + case "-": + warn("unexpected_a", the_value); + break; + case "/": + warn("unexpected_a", the_value); + break; + case "<<": + warn("unexpected_a", the_value); + break; + case ">>": + warn("unexpected_a", the_value); + break; + case ">>>": + warn("unexpected_a", the_value); + break; + case "?": + warn("unexpected_a", the_value); + break; + case "^": + warn("unexpected_a", the_value); + break; + case "typeof": + warn("unexpected_a", the_value); + break; + case "|": + warn("unexpected_a", the_value); + break; + case "~": + +// test_cause: +// ["if(0%0){}", "condition", "unexpected_a", "%", 5] +// ["if(0&0){}", "condition", "unexpected_a", "&", 5] +// ["if(0){}", "condition", "unexpected_a", "0", 4] +// ["if(0*0){}", "condition", "unexpected_a", "*", 5] +// ["if(0+0){}", "condition", "unexpected_a", "+", 5] +// ["if(0-0){}", "condition", "unexpected_a", "-", 5] +// ["if(0/0){}", "condition", "unexpected_a", "/", 5] +// ["if(0<<0){}", "condition", "unexpected_a", "<<", 5] +// ["if(0>>0){}", "condition", "unexpected_a", ">>", 5] +// ["if(0>>>0){}", "condition", "unexpected_a", ">>>", 5] +// ["if(0?0:0){}", "condition", "unexpected_a", "?", 5] +// ["if(0^0){}", "condition", "unexpected_a", "^", 5] +// ["if(0|0){}", "condition", "unexpected_a", "|", 5] +// ["if(\"aa\"){}", "condition", "unexpected_a", "aa", 4] +// ["if(typeof 0){}", "condition", "unexpected_a", "typeof", 4] +// ["if(~0){}", "condition", "unexpected_a", "~", 4] + + warn("unexpected_a", the_value); + break; + } + return the_value; + } + + function constant(id, type, value) { + +// Create a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud_prefix = ( + typeof value === "function" + ? value + : function () { + token_now.constant = true; + if (value !== undefined) { + token_now.value = value; + } + return token_now; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; + } + + function constant_Function() { + if (!option_dict.eval) { + +// test_cause: +// ["Function", "constant_Function", "unexpected_a", "Function", 1] + + warn("unexpected_a", token_now); + } else if (token_nxt.id !== "(") { + +// test_cause: +// [" +// /*jslint eval*/ +// Function +// ", "constant_Function", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "(", artifact()); + } + return token_now; + } + + function constant_arguments() { + +// test_cause: +// ["arguments", "constant_arguments", "unexpected_a", "arguments", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function constant_eval() { + if (!option_dict.eval) { + +// test_cause: +// ["eval", "constant_eval", "unexpected_a", "eval", 1] + + warn("unexpected_a", token_now); + } else if (token_nxt.id !== "(") { + +// test_cause: +// ["/*jslint eval*/\neval", "constant_eval", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "(", artifact()); + } + return token_now; + } + + function constant_ignore() { + +// test_cause: +// ["ignore", "constant_ignore", "unexpected_a", "ignore", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function constant_isInfinite() { + +// test_cause: +// ["isFinite", "constant_isInfinite", "expected_a_b", "isFinite", 1] + + warn("expected_a_b", token_now, "Number.isFinite", "isFinite"); + return token_now; + } + + function constant_isNaN() { + +// test_cause: +// ["isNaN(0)", "constant_isNaN", "number_isNaN", "isNaN", 1] + + warn("number_isNaN", token_now); + return token_now; + } + + function constant_this() { + if (!option_dict.this) { + +// test_cause: +// ["this", "constant_this", "unexpected_a", "this", 1] + + warn("unexpected_a", token_now); + } + return token_now; + } + + function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + let earlier; + let id = name.id; + +// Reserved words may not be enrolled. + + if (syntax_dict[id] !== undefined && id !== "ignore") { + +// test_cause: +// ["let undefined", "enroll", "reserved_a", "undefined", 5] + + warn("reserved_a", name); + return; + } + +// Has the name been enrolled in this context? + + earlier = functionage.context[id] || catchage.context[id]; + if (earlier) { + +// test_cause: +// ["let aa;let aa", "enroll", "redefinition_a_b", "1", 12] + + warn("redefinition_a_b", name, id, earlier.line); + return; + } + +// Has the name been enrolled in an outer context? + + function_stack.forEach(function ({ + context + }) { + earlier = context[id] || earlier; + }); + if (earlier && id === "ignore") { + if (earlier.role === "variable") { + +// test_cause: +// ["let ignore;function aa(ignore){}", "enroll", "redefinition_a_b", "1", 24] + + warn("redefinition_a_b", name, id, earlier.line); + } + } else if ( + earlier + && role !== "parameter" && role !== "function" + && (role !== "exception" || earlier.role !== "exception") + ) { + +// test_cause: +// [" +// function aa(){try{aa();}catch(aa){aa();}} +// ", "enroll", "redefinition_a_b", "1", 31] +// ["function aa(){var aa;}", "enroll", "redefinition_a_b", "1", 19] + + warn("redefinition_a_b", name, id, earlier.line); + } else if ( + option_dict.beta + && global_dict[id] + && role !== "parameter" + ) { + +// test_cause: +// ["let Array", "enroll", "redefinition_global_a_b", "Array", 5] + + warn("redefinition_global_a_b", name, global_dict[id], id); + } + +// Enroll it. + + Object.assign(name, { + dead: true, + init: false, + parent: ( + role === "exception" + ? catchage + : functionage + ), + readonly, + role, + used: 0 + }); + name.parent.context[id] = name; + } + + function infix(bp, id, f) { + +// Create an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led_infix = function (left) { + const the_token = token_now; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, parse_expression(bp)]; + return the_token; + }; + return the_symbol; + } + + function infix_dot(left) { + const the_token = token_now; + let name = token_nxt; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "flat" + && name.id !== "flatMap" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + +// test_cause: +// ["\"\".aa", "check_left", "unexpected_a", ".", 3] + + check_left(left, the_token); + } + if (!name.identifier) { + +// test_cause: +// ["aa.0", "infix_dot", "expected_identifier_a", "0", 4] + + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; + } + + function infix_fart_unwrapped() { + +// test_cause: +// ["aa=>0", "infix_fart_unwrapped", "wrap_fart_parameter", "=>", 3] + + return stop("wrap_fart_parameter", token_now); + } + + function infix_grave(left) { + const the_tick = prefix_tick(); + +// test_cause: +// ["0``", "check_left", "unexpected_a", "`", 2] + + check_left(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; + } + + function infix_lbracket(left) { + const the_token = token_now; + let name; + let the_subscript = parse_expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + name = survey(the_subscript); + +// PR-404 - Add new directive "subscript" to play nice with Google Closure. + + if (!option_dict.subscript && jslint_rgx_identifier.test(name)) { + +// test_cause: +// ["aa[`aa`]", "infix_lbracket", "subscript_a", "aa", 4] + + warn("subscript_a", the_subscript, name); + } + } + +// test_cause: +// ["0[0]", "check_left", "unexpected_a", "[", 2] + + check_left(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; + } + + function infix_lparen(left) { + const the_paren = token_now; + let ellipsis; + let the_argument; + if (left.id !== "function") { + +// test_cause: +// ["(0?0:0)()", "check_left", "unexpected_a", "(", 8] +// ["0()", "check_left", "unexpected_a", "(", 2] + + check_left(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (token_nxt.id !== ")") { + +// Parse/loop through each token in expression (...). + + while (true) { + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = parse_expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + +// test_cause: +// ["aa(0)", "infix_lparen", "free", "", 0] + + test_cause("free"); + the_paren.free = true; + if (the_argument.wrapped === true) { + +// test_cause: +// ["aa((0))", "infix_lparen", "unexpected_a", "(", 3] + + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + +// test_cause: +// ["aa()", "infix_lparen", "not_free", "", 0] +// ["aa(0,0)", "infix_lparen", "not_free", "", 0] + + test_cause("not_free"); + the_paren.free = false; + } + return the_paren; + } + + function infix_option_chain(left) { + const the_token = token_now; + let name = token_nxt; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + +// test_cause: +// ["(0+0)?.0", "infix_option_chain", "check_left", "", 0] + + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + test_cause("check_left"); + +// test_cause: +// ["(/./)?.0", "check_left", "unexpected_a", "?.", 6] +// ["\"aa\"?.0", "check_left", "unexpected_a", "?.", 5] +// ["aa=[]?.aa", "check_left", "unexpected_a", "?.", 6] + + check_left(left, the_token); + } + +// Issue #468 - Fix optional dynamic-property/function-call not recognized. + + if (name.id === "[" || name.id === "(") { + test_cause("dyn_prop_or_call"); + +// test_cause: +// ["aa?.(bb)", "infix_option_chain", "dyn_prop_or_call", "", 0] +// ["aa?.[bb]", "infix_option_chain", "dyn_prop_or_call", "", 0] + + return left; + } + if (!name.identifier) { + +// test_cause: +// ["aa?.0", "infix_option_chain", "expected_identifier_a", "0", 5] + + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; + } + + function infixr(bp, id) { + +// Create a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led_infix = function parse_infixr_led(left) { + const the_token = token_now; + +// test_cause: +// ["0**0", "parse_infixr_led", "led_infix", "", 0] + + test_cause("led_infix"); + the_token.arity = "binary"; + the_token.expression = [left, parse_expression(bp - 1)]; + return the_token; + }; + return the_symbol; + } + + function parse_expression(rbp, initial) { + +// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it +// is looking for ad hoc lint patterns. We add .fud_stmt to Pratt's model, which +// is like .nud_prefix except that it is only used on the first token of a +// statement. Having .fud_stmt makes it much easier to define statement-oriented +// languages like JavaScript. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// .nud_prefix Null denotation. The prefix handler. +// .fud_stmt First null denotation. The statement handler. +// .led_infix Left denotation. The infix/postfix handler. +// lbp Left binding power of infix operator. It tells us how strongly +// the operator binds to the argument at its left. +// rbp Right binding power. + +// It processes a nud_prefix (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop (it +// consumes tokens until it meets a token whose lbp <= rbp). Specifically, it +// means that it collects all tokens that bind together before returning to the +// operator that called it. It returns the expression's parse tree. + +// For example, "3 + 1 * 2 * 4 + 5" +// parses into +// { +// "id": "+", +// "expression": [ +// { +// "id": "+", +// "expression": [ +// { +// "id": "(number)", +// "value": "3" +// }, +// { +// "id": "*", +// "expression": [ +// { +// "id": "*", +// "expression": [ +// { +// "id": "(number)", +// "value": "1" +// }, +// { +// "id": "(number)", +// "value": "2" +// } +// ] +// }, +// { +// "id": "(number)", +// "value": "4" +// } +// ] +// } +// ] +// }, +// { +// "id": "(number)", +// "value": "5" +// } +// ] +// } + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement. + + if (!initial) { + advance(); + } + the_symbol = syntax_dict[token_now.id]; + if (the_symbol !== undefined && the_symbol.nud_prefix !== undefined) { + +// test_cause: +// ["0", "parse_expression", "symbol", "", 0] + + test_cause("symbol"); + left = the_symbol.nud_prefix(); + } else if (token_now.identifier) { + +// test_cause: +// ["aa", "parse_expression", "identifier", "", 0] + + test_cause("identifier"); + left = token_now; + left.arity = "variable"; + } else { + +// test_cause: +// ["!", "parse_expression", "unexpected_a", "(end)", 1] +// ["/./", "parse_expression", "unexpected_a", "/", 1] +// ["let aa=`${}`;", "parse_expression", "unexpected_a", "}", 11] + + return stop("unexpected_a", token_now); + } + +// Parse/loop through each symbol in expression. + + while (true) { + the_symbol = syntax_dict[token_nxt.id]; + if ( + the_symbol === undefined + || the_symbol.led_infix === undefined + || the_symbol.lbp <= rbp + ) { + break; + } + advance(); + left = the_symbol.led_infix(left); + } + return left; + } + + function parse_fart(the_fart) { + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + Object.assign(the_fart, { + arity: "binary", + context: empty(), + finally: 0, + level: functionage.level + 1, + loop: 0, + name: anon, + switch: 0, + try: 0 + }); + +// PR-384 - Relax warning "function_in_loop". +// +// if (functionage.loop > 0) { + +// // test_cause: +// // ["while(0){aa.map(()=>0);}", "parse_fart", "function_in_loop", "=>", 19] +// +// warn("function_in_loop", the_fart); +// } + +// Push the current function context and establish a new one. + + function_list.push(the_fart); + function_stack.push(functionage); + functionage = the_fart; + +// Parse the parameter list. + + prefix_function_parameter(the_fart); + advance("=>"); + +// The function's body is a block. + + if (token_nxt.id === "{") { + if (!option_dict.fart) { + +// test_cause: +// ["()=>{}", "parse_fart", "use_function_not_fart", "=>", 3] + + warn("use_function_not_fart", the_fart); + } + the_fart.block = block("body"); + } else if ( + syntax_dict[token_nxt.id] !== undefined + && syntax_dict[token_nxt.id].fud_stmt !== undefined + ) { + +// PR-384 - Bugfix - Fixes issue #379 - warn against naked-statement in fart. + +// test_cause: +// ["()=>delete aa", "parse_fart", "unexpected_a_after_b", "=>", 5] + + stop("unexpected_a_after_b", token_nxt, token_nxt.id, "=>"); + +// The function's body is an expression. + + } else { + the_fart.expression = parse_expression(0); + } + +// Restore the previous context. + + functionage = function_stack.pop(); + return the_fart; + } + + function parse_json() { + let container; + let is_dup; + let name; + let negative; + switch (token_nxt.id) { + case "(number)": + if (!jslint_rgx_json_number.test(token_nxt.value)) { + +// test_cause: +// ["[-.0]", "parse_json", "unexpected_a", ".", 3] +// ["[-0x0]", "parse_json", "unexpected_a", "0x0", 3] +// ["[.0]", "parse_json", "unexpected_a", ".", 2] +// ["[0x0]", "parse_json", "unexpected_a", "0x0", 2] + + warn("unexpected_a"); + } + advance("(number)"); + return token_now; + case "(string)": + if (token_nxt.quote !== "\"") { + +// test_cause: +// ["['']", "parse_json", "unexpected_a", "'", 2] + + warn("unexpected_a", token_nxt, token_nxt.quote); + } + advance("(string)"); + return token_now; + case "-": + negative = token_nxt; + negative.arity = "unary"; + advance("-"); + +// Recurse parse_json(). + + negative.expression = parse_json(); + return negative; + case "[": + +// test_cause: +// ["[]", "parse_json", "bracket", "", 0] + + test_cause("bracket"); + container = token_nxt; + container.expression = []; + advance("["); + if (token_nxt.id !== "]") { + while (true) { + +// Recurse parse_json(). + + container.expression.push(parse_json()); + if (token_nxt.id !== ",") { + +// test_cause: +// ["[0,0]", "parse_json", "comma", "", 0] + + test_cause("comma"); + break; + } + advance(","); + } + } + advance("]", container); + return container; + case "false": + case "null": + case "true": + +// test_cause: +// ["[false]", "parse_json", "constant", "", 0] +// ["[null]", "parse_json", "constant", "", 0] +// ["[true]", "parse_json", "constant", "", 0] + + test_cause("constant"); + advance(); + return token_now; + case "{": + +// test_cause: +// ["{}", "parse_json", "brace", "", 0] + + test_cause("brace"); + container = token_nxt; + +// Explicit empty-object required to detect "__proto__". + + is_dup = empty(); + container.expression = []; + advance("{"); + if (token_nxt.id !== "}") { + +// JSON +// Parse/loop through each property in {...}. + + while (true) { + if (token_nxt.quote !== "\"") { + +// test_cause: +// ["{0:0}", "parse_json", "unexpected_a", "0", 2] + + warn( + "unexpected_a", + token_nxt, + token_nxt.quote + ); + } + name = token_nxt; + advance("(string)"); + if (is_dup[token_now.value] !== undefined) { + +// test_cause: +// ["{\"aa\":0,\"aa\":0}", "parse_json", "duplicate_a", "aa", 9] + + warn("duplicate_a", token_now); + } else if (token_now.value === "__proto__") { + +// test_cause: +// ["{\"__proto__\":0}", "parse_json", "weird_property_a", "__proto__", 2] + + warn("weird_property_a", token_now); + } else { + is_dup[token_now.value] = token_now; + } + advance(":"); + container.expression.push( + +// Recurse parse_json(). + + Object.assign(parse_json(), { + label: name + }) + ); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance("}", container); + return container; + default: + +// test_cause: +// ["[undefined]", "parse_json", "unexpected_a", "undefined", 2] + + stop("unexpected_a"); + } + } + + function parse_statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token_now.identifier && token_nxt.id === ":") { + the_label = token_now; + if (the_label.id === "ignore") { + +// test_cause: +// ["ignore:", "parse_statement", "unexpected_a", "ignore", 1] + + warn("unexpected_a", the_label); + } + advance(":"); + switch (token_nxt.id) { + case "do": + case "for": + case "switch": + case "while": + +// test_cause: +// ["aa:do{}", "parse_statement", "the_statement_label", "do", 0] +// ["aa:for{}", "parse_statement", "the_statement_label", "for", 0] +// ["aa:switch{}", "parse_statement", "the_statement_label", "switch", 0] +// ["aa:while{}", "parse_statement", "the_statement_label", "while", 0] + + test_cause("the_statement_label", token_nxt.id); + enroll(the_label, "label", true); + the_label.dead = false; + the_label.init = true; + the_statement = parse_statement(); + functionage.statement_prv = the_statement; + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + +// test_cause: +// ["aa:", "parse_statement", "unexpected_label_a", "aa", 1] + + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token_now; + first.statement = true; + the_symbol = syntax_dict[first.id]; + if ( + the_symbol !== undefined + && the_symbol.fud_stmt !== undefined + +// PR-318 - Bugfix - Fixes issues #316, #317 - dynamic-import(). + + && !(the_symbol.id === "import" && token_nxt.id === "(") + ) { + the_symbol.disrupt = false; + the_symbol.statement = true; + token_now.arity = "statement"; + the_statement = the_symbol.fud_stmt(); + functionage.statement_prv = the_statement; + } else { + +// It is an expression statement. + + the_statement = parse_expression(0, true); + functionage.statement_prv = the_statement; + if (the_statement.wrapped && the_statement.id !== "(") { + +// test_cause: +// ["(0)", "parse_statement", "unexpected_a", "(", 1] + + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; + } + + function parse_statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const statement_list = []; + let a_statement; + let disrupt = false; + +// Parse/loop each statement until a statement-terminator is reached. + + while (true) { + switch (token_nxt.id) { + case "(end)": + case "case": + case "default": + case "else": + case "}": + +// test_cause: +// [";", "parse_statements", "closer", "", 0] +// ["case", "parse_statements", "closer", "", 0] +// ["default", "parse_statements", "closer", "", 0] +// ["else", "parse_statements", "closer", "", 0] +// ["}", "parse_statements", "closer", "", 0] + + test_cause("closer"); + return statement_list; + } + a_statement = parse_statement(); + statement_list.push(a_statement); + if (disrupt) { + +// test_cause: +// ["while(0){break;0;}", "parse_statements", "unreachable_a", "0", 16] + + warn("unreachable_a", a_statement); + } + disrupt = a_statement.disrupt; + } + } + + function postassign(id) { + +// Create one of the postassign operators. + + const the_symbol = symbol(id, 150); + the_symbol.led_infix = function (left) { + token_now.expression = left; + token_now.arity = "postassign"; + check_mutation(token_now.expression); + return token_now; + }; + return the_symbol; + } + + function preassign(id) { + +// Create one of the preassign operators. + + const the_symbol = symbol(id); + the_symbol.nud_prefix = function () { + const the_token = token_now; + the_token.arity = "preassign"; + the_token.expression = parse_expression(150); + check_mutation(the_token.expression); + return the_token; + }; + return the_symbol; + } + + function prefix(id, f) { + +// Create a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud_prefix = function () { + const the_token = token_now; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = parse_expression(150); + return the_token; + }; + return the_symbol; + } + + function prefix_assign_divide() { + +// test_cause: +// ["/=", "prefix_assign_divide", "expected_a_b", "/=", 1] + + stop("expected_a_b", token_now, "/\\=", "/="); + } + + function prefix_async() { + let the_async = token_now; + let the_function; + token_nxt.arity = the_async.arity; + +// PR-414 - Parse async fart. + + if (token_nxt.fart) { + advance("("); + the_function = Object.assign(token_now.fart, { + async: 1 + }); + if (!option_dict.fart) { + +// test_cause: +// ["async()=>0", "prefix_async", "use_function_not_fart", "=>", 8] + + warn("use_function_not_fart", the_function); + } + prefix_lparen(); + +// Parse async function. + + } else { + advance("function"); + the_function = Object.assign(token_now, { + async: 1 + }); + prefix_function(); + } + if (the_function.async === 1) { + +// test_cause: +// [" +// async function aa(){} +// ", "prefix_async", "missing_await_statement", "function", 7] + + warn("missing_await_statement", the_function); + } + return the_function; + } + + function prefix_await() { + const the_await = token_now; + +// PR-370 - Add top-level-await support. + + if (functionage.async === 0 && functionage !== token_global) { + +// test_cause: +// ["function aa(){aa=await 0;}", "prefix_await", "unexpected_a", "await", 18] +// ["function aa(){await 0;}", "prefix_await", "unexpected_a", "await", 15] + + warn("unexpected_a", the_await); + } else { + functionage.async += 1; + } + if (the_await.arity === "statement") { + +// PR-405 - Bugfix - fix expression after "await" mis-identified as statement. + + the_await.expression = parse_expression(150); + semicolon(); + } else { + the_await.expression = parse_expression(150); + } + return the_await; + } + + function prefix_fart() { + +// test_cause: +// ["=>0", "prefix_fart", "expected_a_before_b", "=>", 1] + + return stop("expected_a_before_b", token_now, "()", "=>"); + } + + function prefix_function(the_function) { + let name = the_function && the_function.name; + if (the_function === undefined) { + the_function = token_now; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!token_nxt.identifier) { + +// test_cause: +// ["function(){}", "prefix_function", "expected_identifier_a", "(", 9] +// ["function*aa(){}", "prefix_function", "expected_identifier_a", "*", 9] + + return stop("expected_identifier_a"); + } + name = token_nxt; + enroll(name, "variable", true); + the_function.name = Object.assign(name, { + calls: empty(), + +// PR-331 - Bugfix - Fixes issue #272 - function hoisting not allowed. + + dead: false, + init: true + }); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + the_function.name = anon; + if (token_nxt.identifier) { + name = token_nxt; + the_function.name = name; + advance(); + } + } + } + +// Probably deadcode. +// if (mode_mega) { +// warn("unexpected_a", the_function); +// } +// jslint_assert(!mode_mega, `Expected !mode_mega.`); + +// PR-378 - Relax warning "function_in_loop". +// +// // Don't create functions in loops. It is inefficient, and it can lead to +// // scoping errors. +// +// if (functionage.loop > 0) { +// +// // test_cause: +// // [" +// // while(0){aa.map(function(){});} +// // ", "prefix_function", "function_in_loop", "function", 17] +// +// warn("function_in_loop", the_function); +// } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + Object.assign(the_function, { + async: the_function.async || 0, + context: empty(), + finally: 0, + level: functionage.level + 1, + loop: 0, + statement_prv: undefined, + switch: 0, + try: 0 + }); + if (the_function.arity !== "statement" && typeof name === "object") { + +// test_cause: +// ["let aa=function bb(){return;};", "prefix_function", "expression", "bb", 0] + + test_cause("expression", name.id); + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// PR-334 - Bugfix - fix function-redefinition not warned inside function-call. +// Push the current function context and establish a new one. + + function_list.push(the_function); + function_stack.push(functionage); + functionage = the_function; + +// Parse the parameter list. + + advance("("); + token_now.arity = "function"; + prefix_function_parameter(the_function); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && token_nxt.line === token_now.line + ) { + +// test_cause: +// ["function aa(){}0", "prefix_function", "unexpected_a", "0", 16] + + return stop("unexpected_a"); + } + if ( + token_nxt.id === "." + || token_nxt.id === "?." + +// PR-459 - Allow destructuring-assignment after function-definition. + + // || token_nxt.id === "[" + ) { + +// test_cause: +// ["function aa(){}\n.aa", "prefix_function", "unexpected_a", ".", 1] +// ["function aa(){}\n?.aa", "prefix_function", "unexpected_a", "?.", 1] + + warn("unexpected_a"); + } + +// Check functions are ordered. + + check_ordered( + "function", + function_list.slice( + function_list.indexOf(the_function) + 1 + ).map(function ({ + level, + name + }) { + return (level === the_function.level + 1) && name; + }).filter(function (name) { + return option_dict.beta && name && name.id; + }) + ); + +// Restore the previous context. + + functionage = function_stack.pop(); + return the_function; + } + + function prefix_function_parameter(the_function) { + +// This function will parse input at beginning of + + let optional; + let parameters = []; + let signature = ["("]; + let subparam; + function param_enroll(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + +// test_cause: +// ["([aa])=>0", "param_enroll", "use_function_not_fart", "=>", 7] +// ["({aa})=>0", "param_enroll", "use_function_not_fart", "=>", 7] + + if (the_function.id === "=>" && !option_dict.fart) { + warn("use_function_not_fart", the_function); + } + +// Recurse param_enroll(). + + name.names.forEach(param_enroll); + } + } + function param_parse() { + let ellipsis = false; + let param; + if (token_nxt.id === "{") { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,{}){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + param = token_nxt; + param.names = []; + advance("{"); + signature.push("{"); + while (true) { + subparam = token_nxt; + if (!subparam.identifier) { + +// test_cause: +// ["function aa(aa=0,{}){}", "param_parse", "expected_identifier_a", "}", 19] +// ["function aa({0}){}", "param_parse", "expected_identifier_a", "0", 14] + + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (token_nxt.id === ":") { + advance(":"); + advance(); + token_now.label = subparam; + subparam = token_now; + if (!subparam.identifier) { + +// test_cause: +// ["function aa({aa:0}){}", "param_parse", "expected_identifier_a", "}", 18] + + return stop( + "expected_identifier_a", + token_nxt + ); + } + } + +// test_cause: +// ["function aa({aa=aa},aa){}", "param_parse", "equal", "", 0] + + test_cause("equal"); + if (token_nxt.id === "=") { + advance("="); + subparam.expression = parse_expression(); + param.open = true; + } + param.names.push(subparam); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + } else { + break; + } + } + parameters.push(param); + +// test_cause: +// [" +// function aa({bb,aa}){} +// ", "check_ordered", "expected_a_b_before_c_d", "aa", 17] + + check_ordered("parameter", param.names); + advance("}"); + signature.push("}"); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } else if (token_nxt.id === "[") { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,[]){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + param = token_nxt; + param.names = []; + advance("["); + signature.push("[]"); + while (true) { + subparam = token_nxt; + if (!subparam.identifier) { + +// test_cause: +// ["function aa(aa=0,[]){}", "param_parse", "expected_identifier_a", "]", 19] + + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + +// test_cause: +// ["function aa([aa=aa],aa){}", "param_parse", "id", "", 0] + + test_cause("id"); + if (token_nxt.id === "=") { + advance("="); + subparam.expression = parse_expression(); + param.open = true; + } + if (token_nxt.id === ",") { + advance(","); + } else { + break; + } + } + parameters.push(param); + advance("]"); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } else { + if (token_nxt.id === "...") { + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,...){}", "param_parse", "required_a_optional_b", "aa", 21] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + } + if (!token_nxt.identifier) { + +// test_cause: +// ["function aa(0){}", "param_parse", "expected_identifier_a", "0", 13] + + return stop("expected_identifier_a"); + } + param = token_nxt; + parameters.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (token_nxt.id === "=") { + optional = param; + advance("="); + param.expression = parse_expression(0); + } else { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,bb){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } + } + } + +// test_cause: +// ["function aa(){}", "prefix_function_parameter", "opener", "(", 0] + + test_cause("opener", token_now.id); + token_now.free = false; + if (token_nxt.id !== ")" && token_nxt.id !== "(end)") { + param_parse(); + } + advance(")"); + signature.push(")"); + parameters.forEach(param_enroll); + the_function.parameters = parameters; + the_function.signature = signature.join(""); + } + + function prefix_lbrace() { + const seen = empty(); + const the_brace = token_now; + let extra; + let full; + let id; + let name; + let the_colon; + let value; + the_brace.expression = []; + if (token_nxt.id !== "}") { + +// Parse/loop through each property in {...}. + + while (true) { + name = token_nxt; + advance(); + if ( + (name.id === "get" || name.id === "set") + && token_nxt.identifier + ) { + if (!option_dict.getset) { + +// test_cause: +// ["aa={get aa(){}}", "prefix_lbrace", "unexpected_a", "get", 5] + + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + token_nxt.id; + name = token_nxt; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + +// test_cause: +// ["aa={get aa(){},get aa(){}}", "prefix_lbrace", "duplicate_a", "aa", 20] + + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else if (name.id === "`") { + +// test_cause: +// ["aa={`aa`:0}", "prefix_lbrace", "unexpected_a", "`", 5] + + stop("unexpected_a", name); + + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + +// test_cause: +// ["aa={aa,aa}", "prefix_lbrace", "duplicate_a", "aa", 8] + + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (token_nxt.id === "}" || token_nxt.id === ",") { + if (typeof extra === "string") { + +// test_cause: +// ["aa={get aa}", "prefix_lbrace", "closer", "", 0] + + test_cause("closer"); + advance("("); + } + value = parse_expression(Infinity, true); + } else if (token_nxt.id === "(") { + +// test_cause: +// ["aa={aa()}", "prefix_lbrace", "paren", "", 0] +// ["aa={get aa(){}}", "prefix_lbrace", "paren", "", 0] + + test_cause("paren"); + value = prefix_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + +// test_cause: +// ["aa={get aa.aa}", "prefix_lbrace", "paren", "", 0] + + test_cause("paren"); + advance("("); + } + the_colon = token_nxt; + advance(":"); + value = parse_expression(0); + if ( + value.id === name.id + && value.id !== "function" + ) { + +// test_cause: +// ["aa={aa:aa}", "prefix_lbrace", "unexpected_a", ": aa", 7] + + warn("unexpected_a", the_colon, ": " + name.id); + } + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + +// test_cause: +// ["aa={\"aa\":0}", "prefix_lbrace", "colon", "", 0] + + test_cause("colon"); + advance(":"); + value = parse_expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (token_nxt.id !== ",") { + break; + } + +// test_cause: +// ["aa={\"aa\":0,\"bb\":0}", "prefix_lbrace", "comma", "", 0] + + test_cause("comma"); + advance(","); + if (token_nxt.id === "}") { + +// test_cause: +// ["let aa={aa:0,}", "prefix_lbrace", "unexpected_a", ",", 13] + + warn("unexpected_a", token_now); + break; + } + } + } + +// test_cause: +// ["aa={bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8] + + check_ordered( + "property", + the_brace.expression.map(function ({ + label + }) { + return label; + }) + ); + advance("}"); + return the_brace; + } + + function prefix_lbracket() { + const the_token = token_now; + let element; + let ellipsis; + the_token.expression = []; + if (token_nxt.id !== "]") { + +// Parse/loop through each element in [...]. + + while (true) { + ellipsis = false; + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + element = parse_expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (token_nxt.id !== ",") { + break; + } + advance(","); + if (token_nxt.id === "]") { + +// test_cause: +// ["let aa=[0,]", "prefix_lbracket", "unexpected_a", ",", 10] + + warn("unexpected_a", token_now); + break; + } + } + } + advance("]"); + return the_token; + } + + function prefix_lparen() { + let the_paren = token_now; + let the_value; + +// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart. + + if (token_now.fart) { + return parse_fart(token_now.fart); + } + +// test_cause: +// ["(0)", "prefix_lparen", "expr", "", 0] + + test_cause("expr"); + the_paren.free = true; + the_value = parse_expression(0); + if (the_value.wrapped === true) { + +// test_cause: +// ["((0))", "prefix_lparen", "unexpected_a", "(", 1] + + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + return the_value; + } + + function prefix_new() { + const the_new = token_now; + let right; + right = parse_expression(160); + if (token_nxt.id !== "(") { + +// test_cause: +// ["new aa", "prefix_new", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "()", artifact()); + } + the_new.expression = right; + return the_new; + } + + function prefix_tick() { + const the_tick = token_now; + the_tick.value = []; + the_tick.expression = []; + if (token_nxt.id !== "`") { + +// Parse/loop through each token in `${...}`. + + while (true) { + advance("(string)"); + the_tick.value.push(token_now); + if (token_nxt.id !== "${") { + break; + } + advance("${"); + +// test_cause: +// ["let aa=`${}`;", "prefix_tick", "${", "", 0] + + test_cause("${"); + the_tick.expression.push(parse_expression(0)); + advance("}"); + } + } + advance("`"); + return the_tick; + } + + function prefix_void() { + const the_void = token_now; + +// test_cause: +// ["void 0", "prefix_void", "unexpected_a", "void", 1] +// ["void", "prefix_void", "unexpected_a", "void", 1] + + warn("unexpected_a", the_void); + the_void.expression = parse_expression(0); + return the_void; + } + + function semicolon() { + +// Try to match a semicolon. + + if (token_nxt.id === ";") { + advance(";"); + } else { + +// test_cause: +// ["0", "semicolon", "expected_a_b", "(end)", 1] + + warn_at( + "expected_a_b", + token_now.line, + token_now.thru + 1, + ";", + artifact() + ); + } + anon = "anonymous"; + } + + function stmt(id, fud_stmt) { + +// Create a statement. + + const the_symbol = symbol(id); + the_symbol.fud_stmt = fud_stmt; + return the_symbol; + } + + function stmt_break() { + const the_break = token_now; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + +// test_cause: +// ["break", "stmt_break", "unexpected_a", "break", 1] + + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (token_nxt.identifier && token_now.line === token_nxt.line) { + the_label = functionage.context[token_nxt.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + if (the_label !== undefined && the_label.dead) { + +// test_cause: +// ["aa:{function aa(aa){break aa;}}", "stmt_break", "out_of_scope_a", "aa", 27] + + warn("out_of_scope_a"); + } else { + +// test_cause: +// ["aa:{break aa;}", "stmt_break", "not_label_a", "aa", 11] + + warn("not_label_a"); + } + } else { + the_label.used += 1; + } + the_break.label = token_nxt; + advance(); + } + advance(";"); + return the_break; + } + + function stmt_continue() { + const the_continue = token_now; + if (functionage.loop < 1 || functionage.finally > 0) { + +// test_cause: +// ["continue", "stmt_continue", "unexpected_a", "continue", 1] +// [" +// function aa(){while(0){try{}finally{continue}}} +// ", "stmt_continue", "unexpected_a", "continue", 37] + + warn("unexpected_a", the_continue); + } + check_not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; + } + + function stmt_debugger() { + const the_debug = token_now; + if (!option_dict.devel) { + +// test_cause: +// ["debugger", "stmt_debugger", "unexpected_a", "debugger", 1] + + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; + } + + function stmt_delete() { + const the_token = token_now; + const the_value = parse_expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + +// test_cause: +// ["delete 0", "stmt_delete", "expected_a_b", "0", 8] + + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; + } + + function stmt_do() { + const the_do = token_now; + check_not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + +// test_cause: +// ["function aa(){do{break;}while(0)}", "stmt_do", "weird_loop", "do", 15] + + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; + } + + function stmt_export() { + let export_list = []; + let the_export = token_now; + let the_id; + let the_name; + let the_thing; + + the_export.expression = []; + if (token_nxt.id === "default") { + if (export_dict.default !== undefined) { + +// test_cause: +// [" +// export default 0;export default 0 +// ", "stmt_export", "duplicate_a", "default", 25] + + warn("duplicate_a"); + } + advance("default"); + the_thing = parse_expression(0); + if ( + the_thing.id !== "(" + || the_thing.expression[0].id !== "." + || the_thing.expression[0].expression.id !== "Object" + || the_thing.expression[0].name.id !== "freeze" + ) { + +// test_cause: +// ["export default {}", "stmt_export", "freeze_exports", "{", 16] + + warn("freeze_exports", the_thing); + +// PR-301 - Bugfix - Fixes issues #282 - optional-semicolon. + + } else { + +// test_cause: +// [" +// export default Object.freeze({}) +// ", "semicolon", "expected_a_b", "(end)", 32] + + semicolon(); + } + export_dict.default = the_thing; + the_export.expression.push(the_thing); + } else { + +// PR-439 - Add grammar for "export async function ...". + + if (token_nxt.id === "function" || token_nxt.id === "async") { + +// test_cause: +// ["export async function aa(){}", "stmt_export", "freeze_exports", "async", 8] +// ["export function aa(){}", "stmt_export", "freeze_exports", "function", 8] + + warn("freeze_exports"); + the_thing = parse_statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (export_dict[the_id] !== undefined) { + +// test_cause: +// [" +// let aa;export{aa};export function aa(){} +// ", "stmt_export", "duplicate_a", "aa", 35] + + warn("duplicate_a", the_name); + } + export_dict[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + token_nxt.id === "var" + || token_nxt.id === "let" + || token_nxt.id === "const" + ) { + +// test_cause: +// ["export const", "stmt_export", "unexpected_a", "const", 8] +// ["export let", "stmt_export", "unexpected_a", "let", 8] +// ["export var", "stmt_export", "unexpected_a", "var", 8] + + warn("unexpected_a"); + parse_statement(); + } else if (token_nxt.id === "{") { + +// test_cause: +// ["export {}", "stmt_export", "advance{", "", 0] + + test_cause("advance{"); + advance("{"); + while (true) { + if (!token_nxt.identifier) { + +// test_cause: +// ["export {}", "stmt_export", "expected_identifier_a", "}", 9] + + stop("expected_identifier_a"); + } + the_id = token_nxt.id; + export_list.push(token_nxt); + the_name = token_global.context[the_id]; + if (the_name === undefined) { + +// test_cause: +// ["export {aa}", "stmt_export", "unexpected_a", "aa", 9] + + warn("unexpected_a"); + } else { + the_name.used += 1; + if (export_dict[the_id] !== undefined) { + +// test_cause: +// ["let aa;export{aa,aa}", "stmt_export", "duplicate_a", "aa", 18] + + warn("duplicate_a"); + } + export_dict[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + if (token_nxt.id === ",") { + advance(","); + } else { + break; + } + } + +// PR-439 - Check exported properties are ordered. + +// test_cause: +// ["export {bb, aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 13] + + check_ordered("export", export_list); + advance("}"); + semicolon(); + } else { + +// test_cause: +// ["export", "stmt_export", "unexpected_a", "(end)", 1] + + stop("unexpected_a"); + } + } + state.mode_module = true; + return the_export; + } + + function stmt_for() { + let first; + let the_for = token_now; + if (!option_dict.for) { + +// test_cause: +// ["for", "stmt_for", "unexpected_a", "for", 1] + + warn("unexpected_a", the_for); + } + check_not_top_level(the_for); + functionage.loop += 1; + advance("("); + token_now.free = true; + if (token_nxt.id === ";") { + +// test_cause: +// ["for(;;){}", "stmt_for", "expected_a_b", "for (;", 1] + + return stop("expected_a_b", the_for, "while (", "for (;"); + } + switch (token_nxt.id) { + case "const": + case "let": + case "var": + +// test_cause: +// ["for(const aa in aa){}", "stmt_for", "unexpected_a", "const", 5] + + return stop("unexpected_a"); + } + first = parse_expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + +// test_cause: +// ["for(0 in aa){}", "stmt_for", "bad_assignment_a", "0", 5] + + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = parse_expression(0); + advance(";"); + the_for.inc = parse_expression(0); + if (the_for.inc.id === "++") { + +// test_cause: +// ["for(aa;aa;aa++){}", "stmt_for", "expected_a_b", "++", 13] + + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + +// test_cause: +// [" +// /*jslint for*/ +// function aa(bb,cc){for(0;0;0){break;}} +// ", "stmt_for", "weird_loop", "for", 20] + + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; + } + + function stmt_if() { + const the_if = token_now; + let the_else; + the_if.expression = condition(); + the_if.block = block(); + if (token_nxt.id === "else") { + advance("else"); + the_else = token_now; + the_if.else = ( + token_nxt.id === "if" + ? parse_statement() + : block() + ); + +// test_cause: +// ["if(0){0}else if(0){0}", "stmt_if", "else", "", 0] +// ["if(0){0}else{0}", "stmt_if", "else", "", 0] + + test_cause("else"); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + +// test_cause: +// ["if(0){break;}else{break;}", "stmt_if", "disrupt", "", 0] + + test_cause("disrupt"); + the_if.disrupt = true; + } else { + +// test_cause: +// ["if(0){break;}else{}", "stmt_if", "unexpected_a", "else", 14] + + warn("unexpected_a", the_else); + } + } + } + return the_if; + } + + function stmt_import() { + const the_import = token_now; + let name; + let names; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// if (typeof state.mode_module === "object") { +// +// // test_cause: +// // [" +// // /*global aa*/ +// // import aa from "aa" +// // ", "stmt_import", "unexpected_directive_a", "global", 1] +// +// warn( +// "unexpected_directive_a", +// state.mode_module, +// state.mode_module.directive +// ); +// } + + state.mode_module = true; + +// PR-436 - Add grammar for side-effect import-statement. + + if (token_nxt.id === "(string)") { + +// test_cause: +// ["import \"./aa.mjs\";", "stmt_import", "import_side_effect", "", 0] + + test_cause("import_side_effect"); + warn("expected_a_b", token_nxt, "{", artifact()); + advance(); + semicolon(); + return the_import; + } + if (token_nxt.identifier) { + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// ["import ignore from \"aa\"", "stmt_import", "unexpected_a", "ignore", 8] + + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + names = []; + advance("{"); + if (token_nxt.id !== "}") { + while (true) { + if (!token_nxt.identifier) { + +// test_cause: +// ["import {", "stmt_import", "expected_identifier_a", "(end)", 1] + + stop("expected_identifier_a"); + } + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// ["import {ignore} from \"aa\"", "stmt_import", "unexpected_a", "ignore", 9] + + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token_now; + if (!jslint_rgx_module.test(token_now.value)) { + +// test_cause: +// ["import aa from \"!aa\"", "stmt_import", "bad_module_name_a", "!aa", 16] + + warn("bad_module_name_a", token_now); + } + import_list.push(token_now.value); + semicolon(); + return the_import; + } + + function stmt_lbrace() { + +// test_cause: +// [";{}", "stmt_lbrace", "naked_block", "{", 2] +// ["class aa{}", "stmt_lbrace", "naked_block", "{", 9] + + warn("naked_block", token_now); + return block("naked"); + } + + function stmt_return() { + const the_return = token_now; + check_not_top_level(the_return); + if (functionage.finally > 0) { + +// test_cause: +// [" +// function aa(){try{}finally{return;}} +// ", "stmt_return", "unexpected_a", "return", 28] + + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (token_nxt.id !== ";" && the_return.line === token_nxt.line) { + the_return.expression = parse_expression(10); + } + advance(";"); + return the_return; + } + + function stmt_semicolon() { + +// test_cause: +// [";", "stmt_semicolon", "unexpected_a", ";", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function stmt_switch() { + const the_cases = []; + const the_switch = token_now; + let dups = []; + let exp; + let last; + let stmts; + let the_case; + let the_default; + let the_disrupt = true; + let the_last; + function is_dup(thing) { + return is_equal(thing, exp); + } + check_not_top_level(the_switch); + if (functionage.finally > 0) { + +// test_cause: +// [" +// function aa(){try{}finally{switch(0){}}} +// ", "stmt_switch", "unexpected_a", "switch", 28] + + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token_now.free = true; + the_switch.expression = parse_expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + while (true) { + +// Loop through cases with breaks. + + the_case = token_nxt; + the_case.arity = "statement"; + the_case.expression = []; + while (true) { + +// Loop through fallthrough cases. + + advance("case"); + token_now.switch = true; + exp = parse_expression(0); + if (dups.some(is_dup)) { + +// test_cause: +// [" +// switch(0){case 0:break;case 0:break} +// ", "stmt_switch", "unexpected_a", "0", 29] + + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (token_nxt.id !== "case") { + break; + } + } + +// test_cause: +// [" +// switch(0){case 1:case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 23] +// [" +// switch(0){case "aa":case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 26] +// [" +// switch(0){case "bb":case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 26] +// [" +// switch(0){case aa:case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24] +// [" +// switch(0){case bb:case aa:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24] + + check_ordered_case(the_case.expression); + stmts = parse_statements(); + if (stmts.length < 1) { + +// test_cause: +// [" +// switch(0){case 0:default:} +// ", "stmt_switch", "expected_statements_a", "default", 18] +// ["switch(0){case 0:}", "stmt_switch", "expected_statements_a", "}", 18] + + warn("expected_statements_a"); + +// PR-359 - Bugfix - Fixes issue #358 - switch-statement crashes jslint. + + break; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn("expected_a_before_b", token_nxt, "break;", artifact()); + } + if (token_nxt.id !== "case") { + break; + } + } + +// test_cause: +// [" +// switch(0){case 1:break;case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 29] +// [" +// switch(0){case "aa":break;case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 32] +// [" +// switch(0){case "bb":break;case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 32] +// [" +// switch(0){case aa:break;case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30] +// [" +// switch(0){case bb:break;case aa:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30] + + check_ordered_case(the_cases.map(function ({ + expression + }) { + return expression[0]; + })); + dups = undefined; + if (token_nxt.id === "default") { + the_default = token_nxt; + advance("default"); + token_now.switch = true; + advance(":"); + the_switch.else = parse_statements(); + if (the_switch.else.length < 1) { + +// test_cause: +// [" +// switch(0){case 0:break;default:} +// ", "stmt_switch", "unexpected_a", "default", 24] + + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + the_last = the_switch.else[ + the_switch.else.length - 1 + ]; + if ( + the_last.id === "break" + && the_last.label === undefined + ) { + +// test_cause: +// [" +// switch(0){case 0:break;default:break;} +// ", "stmt_switch", "unexpected_a", "break", 32] + + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; + } + + function stmt_throw() { + const the_throw = token_now; + the_throw.disrupt = true; + the_throw.expression = parse_expression(10); + semicolon(); + if (functionage.try > 0) { + +// test_cause: +// ["try{throw 0}catch(){}", "stmt_throw", "unexpected_a", "throw", 5] + + warn("unexpected_a", the_throw); + } + return the_throw; + } + + function stmt_try() { + const the_try = token_now; + let ignored; + let the_catch; + let the_disrupt; + if (functionage.try > 0) { + +// test_cause: +// ["try{try{}catch(){}}catch(){}", "stmt_try", "unexpected_a", "try", 5] + + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (token_nxt.id === "catch") { + ignored = "ignore"; + the_catch = token_nxt; + the_try.catch = the_catch; + advance("catch"); + +// Create new catch-scope for catch-parameter. + + catch_stack.push(catchage); + catchage = the_catch; + catch_list.push(catchage); + the_catch.context = empty(); + if (token_nxt.id === "(") { + advance("("); + if (!token_nxt.identifier) { + +// test_cause: +// ["try{}catch(){}", "stmt_try", "expected_identifier_a", ")", 12] + + return stop("expected_identifier_a"); + } + if (token_nxt.id !== "ignore") { + ignored = undefined; + the_catch.name = token_nxt; + enroll(token_nxt, "exception", true); + } + advance(); + advance(")"); + } + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + +// Restore previous catch-scope after catch-block. + + catchage = catch_stack.pop(); + +// PR-404 - Relax warning about missing `catch` in `try...finally` statement. +// +// } else { +// +// // test_cause: +// // ["try{}finally{break;}", "stmt_try", "expected_a_before_b", "finally", 6] +// +// warn("expected_a_before_b", token_nxt, "catch", artifact()); + + } + if (token_nxt.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; + } + + function stmt_var() { + let ellipsis; + let mode_const; + let name; + let the_brace; + let the_bracket; + let the_variable = token_now; + let variable_prv; + mode_const = the_variable.id === "const"; + the_variable.names = []; + +// A program may use var or let, but not both. + + if (!mode_const) { + if (mode_var === undefined) { + mode_var = the_variable.id; + } else if (the_variable.id !== mode_var) { + +// test_cause: +// ["let aa;var aa", "stmt_var", "expected_a_b", "var", 8] + + warn("expected_a_b", the_variable, mode_var, the_variable.id); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + +// test_cause: +// ["switch(0){case 0:var aa}", "stmt_var", "var_switch", "var", 18] + + warn("var_switch", the_variable); + } + switch ( + Boolean(functionage.statement_prv) + && functionage.statement_prv.id + ) { + case "const": + case "let": + case "var": + +// test_cause: +// ["const aa=0;const bb=0;", "stmt_var", "var_prv", "const", 0] +// ["let aa=0;let bb=0;", "stmt_var", "var_prv", "let", 0] +// ["var aa=0;var bb=0;", "stmt_var", "var_prv", "var", 0] + + test_cause("var_prv", functionage.statement_prv.id); + variable_prv = functionage.statement_prv; + break; + case "import": + +// test_cause: +// ["import aa from \"aa\";\nlet bb=0;", "stmt_var", "import_prv", "", 0] + + test_cause("import_prv"); + break; + case false: + break; + default: + if ( + (option_dict.beta && !option_dict.variable) + || the_variable.id === "var" + ) { + +// test_cause: +// ["console.log();let aa=0;", "stmt_var", "var_on_top", "let", 15] +// ["console.log();var aa=0;", "stmt_var", "var_on_top", "var", 15] +// ["try{aa();}catch(aa){var aa=0;}", "stmt_var", "var_on_top", "var", 21] +// ["while(0){var aa;}", "stmt_var", "var_on_top", "var", 10] + + warn("var_on_top", token_now); + } + } + while (true) { + if (token_nxt.id === "{") { + if (the_variable.id === "var") { + +// test_cause: +// ["var{aa}=0", "stmt_var", "unexpected_a", "var", 1] + + warn("unexpected_a", the_variable); + } + the_brace = token_nxt; + advance("{"); + while (true) { + name = token_nxt; + if (!name.identifier) { + +// test_cause: +// ["let {0}", "stmt_var", "expected_identifier_a", "0", 6] + + return stop("expected_identifier_a"); + } + survey(name); + advance(); + if (token_nxt.id === ":") { + advance(":"); + if (!token_nxt.identifier) { + +// test_cause: +// ["let {aa:0}", "stmt_var", "expected_identifier_a", "0", 9] +// ["let {aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 9] + + return stop("expected_identifier_a"); + } + +// PR-363 - Bugfix +// Add test against false-warning in code +// '/*jslint node*/\nlet {aa:bb} = {}; bb();'. +// +// token_nxt.label = name; +// the_variable.names.push(token_nxt); +// enroll(token_nxt, "variable", mode_const); + + name = token_nxt; + the_variable.names.push(name); + survey(name); + enroll(name, "variable", mode_const); + + advance(); + the_brace.open = true; + } else { + the_variable.names.push(name); + enroll(name, "variable", mode_const); + } + name.dead = false; + name.init = true; + if (token_nxt.id === "=") { + +// test_cause: +// ["let {aa=0}", "stmt_var", "assign", "", 0] + + test_cause("assign"); + advance("="); + name.expression = parse_expression(); + the_brace.open = true; + } + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + +// test_cause: +// ["let{bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8] + + check_ordered(the_variable.id, the_variable.names); + advance("}"); + advance("="); + the_variable.expression = parse_expression(0); + } else if (token_nxt.id === "[") { + if (the_variable.id === "var") { + +// test_cause: +// ["var[aa]=0", "stmt_var", "unexpected_a", "var", 1] + + warn("unexpected_a", the_variable); + } + the_bracket = token_nxt; + advance("["); + while (true) { + ellipsis = false; + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + if (!token_nxt.identifier) { + +// test_cause: +// ["let[]", "stmt_var", "expected_identifier_a", "]", 5] + + return stop("expected_identifier_a"); + } + name = token_nxt; + advance(); + the_variable.names.push(name); + enroll(name, "variable", mode_const); + name.dead = false; + name.init = true; + if (ellipsis) { + name.ellipsis = true; + break; + } + if (token_nxt.id === "=") { + advance("="); + name.expression = parse_expression(); + the_bracket.open = true; + } + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + advance("]"); + advance("="); + the_variable.expression = parse_expression(0); + } else if (token_nxt.identifier) { + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// [" +// let ignore;function aa(ignore) {} +// ", "stmt_var", "unexpected_a", "ignore", 5] + + warn("unexpected_a", name); + } + enroll(name, "variable", mode_const); + if (token_nxt.id === "=" || mode_const) { + advance("="); + name.dead = false; + name.init = true; + name.expression = parse_expression(0); + } + the_variable.names.push(name); + } else { + +// test_cause: +// ["let 0", "stmt_var", "expected_identifier_a", "0", 5] +// ["var{aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 8] + + return stop("expected_identifier_a"); + } + if (token_nxt.id !== ",") { + break; + } + +// test_cause: +// ["let aa,bb;", "stmt_var", "expected_a_b", ",", 7] + + warn("expected_a_b", token_nxt, ";", ","); + advance(","); + } + +// Warn if variable declarations are unordered. + + if ( + option_dict.beta + && !option_dict.unordered + && !option_dict.variable + && variable_prv + && ( + variable_prv.id + " " + variable_prv.names[0].id + > the_variable.id + " " + the_variable.names[0].id + ) + ) { + +// test_cause: +// ["const bb=0;const aa=0;", "stmt_var", "expected_a_b_before_c_d", "aa", 12] +// ["let bb;let aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8] +// ["var bb;var aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8] + + warn( + "expected_a_b_before_c_d", + the_variable, + the_variable.id, + the_variable.names[0].id, + variable_prv.id, + variable_prv.names[0].id + ); + } + semicolon(); + return the_variable; + } + + function stmt_while() { + const the_while = token_now; + check_not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + +// test_cause: +// ["function aa(){while(0){break;}}", "stmt_while", "weird_loop", "while", 15] + + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; + } + + function stmt_with() { + +// test_cause: +// ["with", "stmt_with", "unexpected_a", "with", 1] + + stop("unexpected_a", token_now); + } + + function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!jslint_rgx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!jslint_rgx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + +// test_cause: +// ["let aa={0:0}", "survey", "expected_identifier_a", "0", 9] + + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property_dict[id] === "number") { + property_dict[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (state.mode_property) { + if (tenure[id] !== true) { + +// test_cause: +// ["/*property aa*/\naa.bb", "survey", "unregistered_property_a", "bb", 4] + + warn("unregistered_property_a", name); + } + } else if ( + !option_dict.nomen + && name.identifier + && jslint_rgx_weird_property.test(id) + ) { + +// test_cause: +// ["aa.$", "survey", "weird_property_a", "$", 4] +// ["aa._", "survey", "weird_property_a", "_", 4] +// ["aa._aa", "survey", "weird_property_a", "_aa", 4] +// ["aa.aaSync", "survey", "weird_property_a", "aaSync", 4] +// ["aa.aa_", "survey", "weird_property_a", "aa_", 4] + + warn("weird_property_a", name); + } + property_dict[id] = 1; + } + return id; + } + +// These functions are used to specify the grammar of our language: + + function symbol(id, bp) { + +// Create a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax_dict[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax_dict[id] = the_symbol; + } + return the_symbol; + } + + function ternary(id1, id2) { + +// Create a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led_infix = function parse_ternary_led(left) { + const the_token = token_now; + let second; + second = parse_expression(20); + advance(id2); + token_now.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, parse_expression(10)]; + if (token_nxt.id !== ")") { + +// test_cause: +// ["0?0:0", "parse_ternary_led", "use_open", "?", 2] + + warn("use_open", the_token); + } + return the_token; + }; + return the_symbol; + } + +// Now we parse JavaScript. +// Begin defining the language. + + assignment("%="); + assignment("&="); + assignment("*="); + assignment("+="); + assignment("-="); + assignment("/="); + assignment("<<="); + assignment("="); + assignment(">>="); + assignment(">>>="); + assignment("^="); + assignment("|="); + constant("(number)", "number"); + constant("(regexp)", "regexp"); + constant("(string)", "string"); + constant("Function", "function", constant_Function); + constant("Infinity", "number", Infinity); + constant("NaN", "number", NaN); + constant("arguments", "object", constant_arguments); + constant("eval", "function", constant_eval); + constant("false", "boolean", false); + constant("ignore", "undefined", constant_ignore); + constant("isFinite", "function", constant_isInfinite); + constant("isNaN", "function", constant_isNaN); + constant("null", "null", null); + constant("this", "object", constant_this); + constant("true", "boolean", true); + constant("undefined", "undefined"); + infix(100, "!="); + infix(100, "!=="); + infix(100, "=="); + infix(100, "==="); + infix(110, "<"); + infix(110, "<="); + infix(110, ">"); + infix(110, ">="); + infix(110, "in"); + infix(110, "instanceof"); + infix(120, "<<"); + infix(120, ">>"); + infix(120, ">>>"); + infix(130, "+"); + infix(130, "-"); + infix(140, "%"); + infix(140, "*"); + infix(140, "/"); + infix(160, "(", infix_lparen); + infix(160, "`", infix_grave); + infix(170, ".", infix_dot); + infix(170, "=>", infix_fart_unwrapped); + infix(170, "?.", infix_option_chain); + infix(170, "[", infix_lbracket); + infix(35, "??"); + infix(40, "||"); + infix(50, "&&"); + infix(70, "|"); + infix(80, "^"); + infix(90, "&"); + infixr(150, "**"); + postassign("++"); + postassign("--"); + preassign("++"); + preassign("--"); + prefix("!!"); + prefix("!"); + prefix("(", prefix_lparen); + prefix("+"); + prefix("-"); + prefix("/=", prefix_assign_divide); + prefix("=>", prefix_fart); + prefix("[", prefix_lbracket); + prefix("`", prefix_tick); + prefix("async", prefix_async); + prefix("await", prefix_await); + prefix("function", prefix_function); + prefix("new", prefix_new); + prefix("typeof"); + prefix("void", prefix_void); + prefix("{", prefix_lbrace); + prefix("~"); + stmt(";", stmt_semicolon); + stmt("async", prefix_async); + stmt("await", prefix_await); + stmt("break", stmt_break); + stmt("const", stmt_var); + stmt("continue", stmt_continue); + stmt("debugger", stmt_debugger); + stmt("delete", stmt_delete); + stmt("do", stmt_do); + stmt("export", stmt_export); + stmt("for", stmt_for); + stmt("function", prefix_function); + stmt("if", stmt_if); + stmt("import", stmt_import); + stmt("let", stmt_var); + stmt("return", stmt_return); + stmt("switch", stmt_switch); + stmt("throw", stmt_throw); + stmt("try", stmt_try); + stmt("var", stmt_var); + stmt("while", stmt_while); + stmt("with", stmt_with); + stmt("{", stmt_lbrace); + symbol(")"); + symbol("*/"); + symbol(","); + symbol(":"); + symbol(";"); + symbol("]"); + symbol("async"); + symbol("await"); + symbol("case"); + symbol("catch"); + symbol("class"); + symbol("default"); + symbol("else"); + symbol("enum"); + symbol("finally"); + symbol("implements"); + symbol("interface"); + symbol("package"); + symbol("private"); + symbol("protected"); + symbol("public"); + symbol("static"); + symbol("super"); + symbol("void"); + symbol("yield"); + symbol("}"); + ternary("?", ":"); + +// Init token_nxt. + + advance(); + +// Parsing of JSON is simple: + + if (state.mode_json) { + state.token_tree = parse_json(); + advance("(end)"); + return; + } + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option_dict.browser) { + if (token_nxt.id === ";") { + advance(";"); + } + +// If we are not in a browser, then the file form of strict pragma may be used. + + } else if (token_nxt.value === "use strict") { + advance("(string)"); + advance(";"); + } + state.token_tree = parse_statements(); + advance("(end)"); + +// Check global functions are ordered. + + check_ordered( + "function", + function_list.map(function ({ + level, + name + }) { + return (level === 1) && name; + }).filter(function (name) { + return option_dict.beta && name && name.id; + }) + ); +} + +function jslint_phase4_walk(state) { + +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). + + let { + artifact, + catch_stack, + function_stack, + global_dict, + is_equal, + is_weird, + option_dict, + syntax_dict, + test_cause, + token_global, + warn + } = state; + let block_stack = []; // The stack of blocks. + let blockage = token_global; // The current block. + let catchage = catch_stack[0]; // The current catch-block. + let functionage = token_global; // The current function. + let postaction; + let postamble; + let posts = empty(); + let preaction; + let preamble; + let pres = empty(); + +// The relational operators. + + let relationop = object_assign_from_list(empty(), [ + "!=", "!==", "<", "<=", "==", "===", ">", ">=" + ], true); + +// Ambulation of the parse tree. + + function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; + } + + function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + task(the_token); + }); + } + } + }; + } + + function init_variable(name) { + let the_variable = lookup(name); + if (!the_variable || the_variable.readonly) { + warn("bad_assignment_a", name); + return; + } + the_variable.init = true; + } + + function lookup(thing) { + let id = thing.id; + let the_variable; + if (thing.arity !== "variable") { + return; + } + +// Look up the variable in the current context. + + the_variable = functionage.context[id] || catchage.context[id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable && the_variable.role === "label") { + +// test_cause: +// ["aa:while(0){aa;}", "lookup", "label_a", "aa", 13] + + warn("label_a", thing); + return the_variable; + } + if (!the_variable) { + function_stack.forEach(function ({ + context + }) { + if (context[id] && context[id].role !== "label") { + the_variable = context[id]; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (!the_variable && global_dict[id] === undefined) { + +// test_cause: +// ["aa", "lookup", "undeclared_a", "aa", 1] +// ["class aa{}", "lookup", "undeclared_a", "aa", 7] +// [" +// let aa=0;try{aa();}catch(bb){bb();}bb(); +// ", "lookup", "undeclared_a", "bb", 36] +// [" +// let aa=0;try{aa();}catch(ignore){bb();} +// ", "lookup", "undeclared_a", "bb", 34] + + warn("undeclared_a", thing); + return; + } + if (!the_variable) { + the_variable = { + dead: false, + id, + init: true, + parent: token_global, + readonly: true, + role: "variable", + used: 0 + }; + token_global.context[id] = the_variable; + } + the_variable.closure = true; + functionage.context[id] = the_variable; + } + if ( + ( + the_variable.calls === undefined + || functionage.name === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + && the_variable.dead + ) { + +// test_cause: +// ["let aa;if(aa){let bb;}bb;", "lookup", "out_of_scope_a", "bb", 23] + + warn("out_of_scope_a", thing); + } + return the_variable; + } + + function post_a(thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + let right; + if (thing.id === "=") { + if (thing.names !== undefined) { + +// test_cause: +// ["if(0){aa=0}", "post_a", "=", "", 0] + + test_cause("="); + +// Probably deadcode. +// if (Array.isArray(thing.names)) { +// thing.names.forEach(init_variable); +// } else { +// init_variable(thing.names); +// } + + jslint_assert( + !Array.isArray(thing.names), + `Expected !Array.isArray(thing.names).` + ); + init_variable(thing.names); + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + +// test_cause: +// ["aa.aa=undefined", "post_a", "expected_a_b", "undefined", 1] + + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.readonly) { + warn("bad_assignment_a", lvalue); + } + } + right = syntax_dict[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + +// test_cause: +// ["aa+=undefined", "post_a", "unexpected_a", "undefined", 5] + + warn("unexpected_a", thing.expression[1]); + } + } + } + + function post_a_pluseq(thing) { + const right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } + } + + function post_b(thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || is_equal(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + +// test_cause: +// ["if(0===0){0}", "post_b", "weird_relation_a", "===", 5] + + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option_dict.convert) { + if (thing.expression[0].value === "") { + +// test_cause: +// ["\"\"+0", "post_b", "expected_a_b", "\"\" +", 3] + + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + +// test_cause: +// ["0+\"\"", "post_b", "expected_a_b", "+ \"\"", 2] + + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + +// test_cause: +// ["aa=window[0]", "post_b", "weird_expression_a", "window[...]", 10] + + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + +// test_cause: +// ["aa=self[0]", "post_b", "weird_expression_a", "self[...]", 8] + + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === "." || thing.id === "?.") { + if (thing.expression.id === "RegExp") { + +// test_cause: +// ["aa=RegExp.aa", "post_b", "weird_expression_a", ".", 10] + + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + +// test_cause: +// ["0- -0", "post_b", "wrap_unary", "-", 4] + + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } + } + + function post_b_and(thing) { + if ( + is_weird(thing.expression[0]) + || is_equal(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + +// test_cause: +// ["aa=(()=>0)&&(()=>0)", "post_b_and", "weird_condition_a", "&&", 11] +// ["aa=(``?``:``)&&(``?``:``)", "post_b_and", "weird_condition_a", "&&", 14] +// ["aa=/./&&/./", "post_b_and", "weird_condition_a", "&&", 7] +// ["aa=0&&0", "post_b_and", "weird_condition_a", "&&", 5] +// ["aa=[]&&[]", "post_b_and", "weird_condition_a", "&&", 6] +// ["aa=`${0}`&&`${0}`", "post_b_and", "weird_condition_a", "&&", 10] +// [" +// aa=function aa(){}&&function aa(){} +// ", "post_b_and", "weird_condition_a", "&&", 19] +// ["aa={}&&{}", "post_b_and", "weird_condition_a", "&&", 6] + + warn("weird_condition_a", thing); + } + } + + function post_b_lbracket(thing) { + if (thing.expression[0].id === "RegExp") { + +// test_cause: +// ["aa=RegExp[0]", "post_b_lbracket", "weird_expression_a", "[", 10] + + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + +// test_cause: +// ["aa[[0]]", "post_b_lbracket", "weird_expression_a", "[", 4] + + warn("weird_expression_a", thing.expression[1]); + } + } + + function post_b_lparen(thing) { + let arg; + let array; + let cack; + let left = thing.expression[0]; + let new_date; + let paren; + let the_new; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + +// test_cause: +// ["aa=function(){}()", "post_b_lparen", "wrap_immediate", "(", 16] + + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "BigInt" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + +// test_cause: +// ["new BigInt()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Boolean()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Number()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new String()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Symbol()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new aa()", "post_b_lparen", "unexpected_a", "new", 1] + + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option_dict.eval) { + +// test_cause: +// ["new Function()", "post_b_lparen", "unexpected_a", "new Function", 5] + + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + +// test_cause: +// ["new Array()", "post_b_lparen", "expected_a_b", "new Array", 5] + + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + +// test_cause: +// ["new Object()", "post_b_lparen", "expected_a_b", "new Object", 5] + + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "BigInt" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + +// test_cause: +// ["let Aa=Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8] + + warn("expected_a_before_b", left, "new", artifact(left)); + } + } + } else if (left.id === ".") { + cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + +// test_cause: +// ["new Date.UTC()", "post_b_lparen", "cack", "", 0] + + test_cause("cack"); + cack = !cack; + } + if (jslint_rgx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + +// test_cause: +// ["new Date.UTC()", "post_b_lparen", "unexpected_a", "new", 1] + + warn("unexpected_a", the_new); + } else { + +// test_cause: +// ["let Aa=Aa.Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8] + + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + paren = left.expression; + if (paren.id === "(") { + array = paren.expression; + if (array.length === 1) { + new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + +// test_cause: +// [" +// new Date().getTime() +// ", "post_b_lparen", "expected_a_b", "new Date().getTime()", 1] + + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } + } + + function post_b_or(thing) { + if ( + is_weird(thing.expression[0]) + || is_equal(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + +// test_cause: +// ["aa=0||0", "post_b_or", "weird_condition_a", "||", 5] + + warn("weird_condition_a", thing); + } + } + + function post_s_export(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== token_global) { + +// test_cause: +// [" +// if(0){import aa from "aa";} +// ", "post_s_export", "misplaced_a", "import", 7] + + warn("misplaced_a", the_thing); + } + } + + function post_s_for(thing) { + +// Recurse walk_statement(). + + walk_statement(thing.inc); + } + + function post_s_function(thing) { + delete functionage.async; + delete functionage.finally; + delete functionage.loop; + delete functionage.statement_prv; + delete functionage.switch; + delete functionage.try; + functionage = function_stack.pop(); + if (thing.wrapped) { + +// test_cause: +// ["aa=(function(){})", "post_s_function", "unexpected_parens", "function", 5] + + warn("unexpected_parens", thing); + } + return post_s_lbrace(); + } + + function post_s_import(the_thing) { + const name = the_thing.name; + if (name) { + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return post_s_export(the_thing); + } + } + + function post_s_lbrace() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); + } + + function post_s_try(thing) { + if (thing.catch) { + if (thing.catch.name) { + Object.assign(catchage.context[thing.catch.name.id], { + dead: false, + init: true + }); + } + +// Recurse walk_statement(). + + walk_statement(thing.catch.block); + +// Restore previous catch-scope after catch-block. + + catchage = catch_stack.pop(); + } + } + + function post_s_var(thing) { + thing.names.forEach(function (name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + +// Probably deadcode. +// if (name.id === "{" || name.id === "[") { +// name.names.forEach(subactivate); +// } else { +// name.init = true; +// } + + jslint_assert( + !(name.id === "{" || name.id === "["), + `Expected !(name.id === "{" || name.id === "[").` + ); + name.init = true; + } + blockage.live.push(name); + }); + } + + function post_t(thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || is_equal(thing.expression[1], thing.expression[2]) + ) { + +// test_cause: +// ["let aa=(aa?`${0}`:`${0}`);", "post_t", "unexpected_a", "?", 11] +// ["let aa=(aa?`0`:`0`);", "post_t", "unexpected_a", "?", 11] + + warn("unexpected_a", thing); + } else if (is_equal(thing.expression[0], thing.expression[1])) { + +// test_cause: +// ["aa?aa:0", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "||", "?"); + } else if (is_equal(thing.expression[0], thing.expression[2])) { + +// test_cause: +// ["aa?0:aa", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + +// test_cause: +// ["aa?true:false", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + +// test_cause: +// ["aa?false:true", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + +// test_cause: +// ["(aa&&!aa?0:1)", "post_t", "wrap_condition", "&&", 4] + + warn("wrap_condition", thing.expression[0]); + } + } + + function post_u(thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option_dict.convert) { + +// test_cause: +// ["!!0", "post_u", "expected_a_b", "!!", 1] + + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } + } + + function post_u_plus(thing) { + const right = thing.expression; + if (!option_dict.convert) { + +// test_cause: +// ["aa=+0", "post_u_plus", "expected_a_b", "+", 4] + + warn("expected_a_b", thing, "Number(...)", "+"); + } + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } + } + + function pre_a_bitwise(thing) { + +// These are the bitwise operators. + + switch (!option_dict.bitwise && thing.id) { + case "&": + case "&=": + case "<<": + case "<<=": + case ">>": + case ">>=": + case ">>>": + case ">>>=": + case "^": + case "^=": + case "|": + case "|=": + case "~": + +// test_cause: +// ["0&0", "pre_a_bitwise", "unexpected_a", "&", 2] +// ["0&=0", "pre_a_bitwise", "unexpected_a", "&=", 2] +// ["0<<0", "pre_a_bitwise", "unexpected_a", "<<", 2] +// ["0<<=0", "pre_a_bitwise", "unexpected_a", "<<=", 2] +// ["0>>0", "pre_a_bitwise", "unexpected_a", ">>", 2] +// ["0>>=0", "pre_a_bitwise", "unexpected_a", ">>=", 2] +// ["0>>>0", "pre_a_bitwise", "unexpected_a", ">>>", 2] +// ["0>>>=0", "pre_a_bitwise", "unexpected_a", ">>>=", 2] +// ["0^0", "pre_a_bitwise", "unexpected_a", "^", 2] +// ["0^=0", "pre_a_bitwise", "unexpected_a", "^=", 2] +// ["0|0", "pre_a_bitwise", "unexpected_a", "|", 2] +// ["0|=0", "pre_a_bitwise", "unexpected_a", "|=", 2] +// ["~0", "pre_a_bitwise", "unexpected_a", "~", 1] + + warn("unexpected_a", thing); + break; + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + +// test_cause: +// ["0<0<0", "pre_a_bitwise", "unexpected_a", "<", 4] + + warn("unexpected_a", thing); + } + } + + function pre_b(thing) { + let left; + let right; + let value; + if (relationop[thing.id] === true) { + left = thing.expression[0]; + right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + +// test_cause: +// ["NaN===NaN", "pre_b", "number_isNaN", "===", 4] + + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + +// test_cause: +// ["typeof 0===0", "pre_b", "expected_string_a", "0", 12] + + warn("expected_string_a", right); + } + } else { + value = right.value; + if (value === "null" || value === "undefined") { + +// test_cause: +// [" +// typeof aa==="undefined" +// ", "pre_b", "unexpected_typeof_a", "undefined", 13] + + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "bigint" + && value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + +// test_cause: +// ["typeof 0===\"aa\"", "pre_b", "expected_type_string_a", "aa", 12] + + warn("expected_type_string_a", right, value); + } + } + } + } + } + + function pre_b_eqeq(thing) { + +// test_cause: +// ["0==0", "pre_b_eqeq", "expected_a_b", "==", 2] + + warn("expected_a_b", thing, "===", "=="); + } + + function pre_b_in(thing) { + +// test_cause: +// ["aa in aa", "pre_b_in", "infix_in", "in", 4] + + warn("infix_in", thing); + } + + function pre_b_instanceof(thing) { + +// test_cause: +// ["0 instanceof 0", "pre_b_instanceof", "unexpected_a", "instanceof", 3] + + warn("unexpected_a", thing); + } + + function pre_b_lparen(thing) { + const left = thing.expression[0]; + let left_variable; + let parent; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + parent = functionage.name.parent; + if (parent) { + left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + +// Probably deadcode. +// && left_variable.dead + + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } + } + + function pre_b_noteq(thing) { + +// test_cause: +// ["0!=0", "pre_b_noteq", "expected_a_b", "!=", 2] + + warn("expected_a_b", thing, "!==", "!="); + } + + function pre_b_or(thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + +// test_cause: +// ["0&&0||0", "pre_b_or", "and", "&&", 2] + + warn("and", thang); + } + }); + } + + function pre_s_for(thing) { + let the_variable; + if (thing.name !== undefined) { + thing.name.dead = false; + the_variable = lookup(thing.name); + if (the_variable !== undefined) { + if (the_variable.init && the_variable.readonly) { + +// test_cause: +// ["const aa=0;for(aa in aa){}", "pre_s_for", "bad_assignment_a", "aa", 16] + + warn("bad_assignment_a", thing.name); + } + the_variable.init = true; + } + } + +// Recurse walk_statement(). + + walk_statement(thing.initial); + } + + function pre_s_function(thing) { + +// test_cause: +// ["()=>0", "pre_s_function", "", "", 0] +// ["(function (){}())", "pre_s_function", "", "", 0] +// ["function aa(){}", "pre_s_function", "", "", 0] + + test_cause(""); + if (thing.arity === "statement" && blockage.body !== true) { + +// test_cause: +// ["if(0){function aa(){}\n}", "pre_s_function", "unexpected_a", "function", 7] + + warn("unexpected_a", thing); + } + function_stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + +// test_cause: +// [" +// /*jslint getset*/ +// aa={get aa(aa){}} +// ", "pre_s_function", "bad_get", "function", 9] + + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + +// test_cause: +// [" +// /*jslint getset*/ +// aa={set aa(){}} +// ", "pre_s_function", "bad_set", "function", 9] + + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); + } + + function pre_s_lbrace(thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; + } + + function pre_try(thing) { + if (thing.catch !== undefined) { + +// Create new catch-scope for catch-parameter. + + catch_stack.push(catchage); + catchage = thing.catch; + } + } + + function pre_v(thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } + } + + function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); + } + + function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + +// test_cause: +// ["(function(){}())", "walk_expression", "isArray", "", 0] +// ["0&&0", "walk_expression", "isArray", "", 0] + + test_cause("isArray"); + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + +// PR-414 - Bugfix - fix fart-body not being walked. + + if (thing.id === "function" || thing.id === "=>") { + +// test_cause: +// ["aa=()=>0", "walk_expression", "function", "=>", 0] +// ["aa=function(){}", "walk_expression", "function", "function", 0] + + test_cause("function", thing.id); + +// Recurse walk_statement(). + + walk_statement(thing.block); + } + if ( + thing.arity === "preassign" || thing.arity === "postassign" + ) { + +// test_cause: +// ["aa=++aa", "walk_expression", "unexpected_a", "++", 4] +// ["aa=--aa", "walk_expression", "unexpected_a", "--", 4] + + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + +// test_cause: +// ["aa[aa=0]", "walk_expression", "unexpected_statement_a", "=", 6] + + warn("unexpected_statement_a", thing); + } + +// test_cause: +// ["aa=0", "walk_expression", "default", "", 0] + + test_cause("default"); + postamble(thing); + } + } + } + + function walk_statement(thing) { + if (!thing) { + return; + } + if (Array.isArray(thing)) { + +// test_cause: +// ["+[]", "walk_statement", "isArray", "", 0] + + test_cause("isArray"); + +// Recurse walk_statement(). + + thing.forEach(walk_statement); + return; + } + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + +// test_cause: +// ["0&&0", "walk_statement", "unexpected_expression_a", "&&", 2] + + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + && thing.id !== "import" + ) { + +// test_cause: +// ["!0", "walk_statement", "unexpected_expression_a", "!", 1] +// ["+[]", "walk_statement", "unexpected_expression_a", "+", 1] +// ["+new aa()", "walk_statement", "unexpected_expression_a", "+", 1] +// ["0", "walk_statement", "unexpected_expression_a", "0", 1] +// ["typeof 0", "walk_statement", "unexpected_expression_a", "typeof", 1] + + warn("unexpected_expression_a", thing); + } + +// Recurse walk_statement(). + + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + + postaction = action(posts); + postamble = amble(posts); + preaction = action(pres); + preamble = amble(pres); + postaction("assignment", "+=", post_a_pluseq); + postaction("assignment", post_a); + postaction("binary", "&&", post_b_and); + postaction("binary", "(", post_b_lparen); + postaction("binary", "=>", post_s_function); + postaction("binary", "[", post_b_lbracket); + postaction("binary", "||", post_b_or); + postaction("binary", post_b); + postaction("statement", "const", post_s_var); + postaction("statement", "export", post_s_export); + postaction("statement", "for", post_s_for); + postaction("statement", "function", post_s_function); + postaction("statement", "import", post_s_import); + postaction("statement", "let", post_s_var); + postaction("statement", "try", post_s_try); + postaction("statement", "var", post_s_var); + postaction("statement", "{", post_s_lbrace); + postaction("ternary", post_t); + postaction("unary", "+", post_u_plus); + postaction("unary", "function", post_s_function); + postaction("unary", post_u); + preaction("assignment", pre_a_bitwise); + preaction("binary", "!=", pre_b_noteq); + preaction("binary", "(", pre_b_lparen); + preaction("binary", "==", pre_b_eqeq); + preaction("binary", "=>", pre_s_function); + preaction("binary", "in", pre_b_in); + preaction("binary", "instanceof", pre_b_instanceof); + preaction("binary", "||", pre_b_or); + preaction("binary", pre_b); + preaction("binary", pre_a_bitwise); + preaction("statement", "for", pre_s_for); + preaction("statement", "function", pre_s_function); + preaction("statement", "try", pre_try); + preaction("statement", "{", pre_s_lbrace); + preaction("unary", "function", pre_s_function); + preaction("unary", "~", pre_a_bitwise); + preaction("variable", pre_v); + + walk_statement(state.token_tree); +} + +function jslint_phase5_whitage(state) { + +// PHASE 5. Check whitespace between tokens in . + + let { + artifact, + catch_list, + function_list, + function_stack, + option_dict, + test_cause, + token_global, + token_list, + warn + } = state; + let closer = "(end)"; + let free = false; + +// free = false + +// cause: +// "()=>0" +// "aa()" +// "aa(0,0)" +// "function(){}" + +// free = true + +// cause: +// "(0)" +// "(aa)" +// "aa(0)" +// "do{}while()" +// "for(){}" +// "if(){}" +// "switch(){}" +// "while(){}" + + let left = token_global; + let margin = 0; + let mode_indent = ( + +// PR-330 - Allow 2-space indent. + + option_dict.indent2 + ? 2 + : 4 + ); + let nr_comments_skipped = 0; + let open = true; + let opening = true; + let right; + +// This is the set of infix operators that require a space on each side. + + let spaceop = object_assign_from_list(empty(), [ + "!=", "!==", "%", "%=", "&", "&&", "&=", "*", "*=", "+=", "-=", "/", + "/=", "<", "<<", "<<=", "<=", "=", "==", "===", "=>", ">", ">=", ">>", + ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" + ], true); + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + const name = the_function.context[id]; + if (id !== "ignore" && name.parent === the_function) { + +// test_cause: +// ["function aa(aa) {return aa;}", "delve", "id", "", 0] + + test_cause("id"); + if ( + name.used === 0 + +// Probably deadcode. +// && ( +// name.role !== "function" +// || name.parent.arity !== "unary" +// ) + + && jslint_assert( + name.role !== "function", + `Expected name.role !== "function".` + ) + ) { + +// test_cause: +// ["/*jslint node*/\nlet aa;", "delve", "unused_a", "aa", 5] +// ["function aa(aa){return;}", "delve", "unused_a", "aa", 13] +// ["let aa=0;try{aa();}catch(bb){aa();}", "delve", "unused_a", "bb", 26] + + warn("unused_a", name); + } else if (!name.init) { + +// test_cause: +// ["/*jslint node*/\nlet aa;aa();", "delve", "uninitialized_a", "aa", 5] + + warn("uninitialized_a", name); + } + } + }); + } + + function expected_at(at) { + +// Probably deadcode. +// if (right === undefined) { +// right = token_nxt; +// } + + jslint_assert( + !(right === undefined), + `Expected !(right === undefined).` + ); + warn( + "expected_a_at_b_c", + right, + artifact(right), + +// Fudge column numbers in warning message. + + at + jslint_fudge, + right.from + jslint_fudge + ); + } + + function no_space() { + if (left.line === right.line) { + +// from: +// if (left.line === right.line) { +// no_space(); +// } else { + + if (left.thru !== right.from && nr_comments_skipped === 0) { + +// test_cause: +// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14] + + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + +// from: +// } else if ( +// right.arity === "binary" +// && right.id === "(" +// && free +// ) { +// no_space(); +// } else if ( + +// Probably deadcode. +// if (open) { +// const at = ( +// free +// ? margin +// : margin + 8 +// ); +// if (right.from < at) { +// expected_at(at); +// } +// } else { +// if (right.from !== margin + 8) { +// expected_at(margin + 8); +// } +// } + + jslint_assert(open, `Expected open.`); + jslint_assert(free, `Expected free.`); + if (right.from < margin) { + +// test_cause: +// ["let aa = aa(\naa\n()\n);", "expected_at", "expected_a_at_b_c", "5", 1] + + expected_at(margin); + } + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line || !open) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (right.from !== margin) { + expected_at(margin); + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn("expected_space_a_b", right, artifact(left), artifact(right)); + } + } + + function pop() { + const previous = function_stack.pop(); + closer = previous.closer; + free = previous.free; + margin = previous.margin; + open = previous.open; + opening = previous.opening; + } + + function push() { + function_stack.push({ + closer, + free, + margin, + open, + opening + }); + } + +// uninitialized_and_unused(); +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (state.mode_module === true || option_dict.node) { + delve(token_global); + } + catch_list.forEach(delve); + function_list.forEach(delve); + + if (option_dict.white) { + return; + } + +// whitage(); +// Go through the token list, looking at usage of whitespace. + + token_list.forEach(function whitage(the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + +// The open and close pairs. + + switch (left.id) { + case "${": + case "(": + case "[": + case "{": + +// test_cause: +// ["let aa=[];", "whitage", "opener", "", 0] +// ["let aa=`${0}`;", "whitage", "opener", "", 0] +// ["let aa=aa();", "whitage", "opener", "", 0] +// ["let aa={};", "whitage", "opener", "", 0] + + test_cause("opener"); + +// Probably deadcode. +// case "${}": + + jslint_assert( + !(left.id + right.id === "${}"), + "Expected !(left.id + right.id === \"${}\")." + ); + switch (left.id + right.id) { + case "()": + case "[]": + case "{}": + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like '{]') have already been detected. + +// test_cause: +// ["let aa=[];", "whitage", "opener_closer", "", 0] +// ["let aa=aa();", "whitage", "opener_closer", "", 0] +// ["let aa={};", "whitage", "opener_closer", "", 0] + + test_cause("opener_closer"); + if (left.line === right.line) { + +// test_cause: +// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14] + + no_space(); + } else { + +// test_cause: +// ["let aa = aa(\n );", "expected_at", "expected_a_at_b_c", "1", 2] + + at_margin(0); + } + break; + default: + +// test_cause: +// ["let aa=(0);", "whitage", "opener_operand", "", 0] +// ["let aa=[0];", "whitage", "opener_operand", "", 0] +// ["let aa=`${0}`;", "whitage", "opener_operand", "", 0] +// ["let aa=aa(0);", "whitage", "opener_operand", "", 0] +// ["let aa={aa:0};", "whitage", "opener_operand", "", 0] + + test_cause("opener_operand"); + opening = left.open || (left.line !== right.line); + push(); + switch (left.id) { + case "${": + closer = "}"; + break; + case "(": + closer = ")"; + break; + case "[": + closer = "]"; + break; + case "{": + closer = "}"; + break; + } + if (opening) { + +// test_cause: +// ["function aa(){\nreturn;\n}", "whitage", "opening", "", 0] +// ["let aa=(\n0\n);", "whitage", "opening", "", 0] +// ["let aa=[\n0\n];", "whitage", "opening", "", 0] +// ["let aa=`${\n0\n}`;", "whitage", "opening", "", 0] +// ["let aa={\naa:0\n};", "whitage", "opening", "", 0] + + test_cause("opening"); + free = closer === ")" && left.free; + open = true; + margin += mode_indent; + if (right.role === "label") { + if (right.from !== 0) { + +// test_cause: +// [" +// function aa() { +// bb: +// while (aa) { +// if (aa) { +// break bb; +// } +// } +// } +// ", "expected_at", "expected_a_at_b_c", "1", 2] + + expected_at(0); + } + } else if (right.switch) { + at_margin(-mode_indent); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + +// test_cause: +// [" +// function aa() {bb: +// while (aa) { +// aa(); +// } +// } +// ", "whitage", "expected_line_break_a_b", "bb", 16] + + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + +// test_cause: +// ["let aa=(0);", "whitage", "not_free", "", 0] +// ["let aa=[0];", "whitage", "not_free", "", 0] +// ["let aa=`${0}`;", "whitage", "not_free", "", 0] +// ["let aa={aa:0};", "whitage", "not_free", "", 0] + + test_cause("not_free"); + free = false; + open = false; + +// test_cause: +// ["let aa = ( 0 );", "no_space_only", "unexpected_space_a_b", "0", 12] + + no_space_only(); + } + } + break; + default: + if (right.statement === true) { + if (left.id === "else") { + +// test_cause: +// [" +// let aa = 0; +// if (aa) { +// aa(); +// } else if (aa) { +// aa(); +// } +// ", "one_space_only", "expected_space_a_b", "if", 9] + + one_space_only(); + } else { + +// test_cause: +// [" let aa = 0;", "expected_at", "expected_a_at_b_c", "1", 2] + + at_margin(0); + open = false; + } + +// If right is a closer, then pop the previous state. + + } else if (right.id === closer) { + pop(); + if (opening && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-mode_indent); + } else if (right.role === "label") { + if (right.from !== 0) { + +// test_cause: +// [" +// function aa() { +// aa();cc: +// while (aa) { +// if (aa) { +// break cc; +// } +// } +// } +// ", "expected_at", "expected_a_at_b_c", "1", 10] + + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + +// test_cause: +// ["let {aa,bb} = 0;", "one_space", "expected_space_a_b", "bb", 9] + + one_space(); + } else { + +// test_cause: +// [" +// function aa() { +// aa( +// 0,0 +// ); +// } +// ", "expected_at", "expected_a_at_b_c", "9", 11] + + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + +// test_cause: +// [" +// let aa = ( +// aa +// ? 0 +// : 1 +// ); +// ", "expected_at", "expected_a_at_b_c", "5", 1] + + at_margin(0); + } else { + +// test_cause: +// ["let aa = (aa ? 0 : 1);", "whitage", "use_open", "?", 14] + + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + +// test_cause: +// ["let aa = aa(\naa ()\n);", "no_space", "unexpected_space_a_b", "(", 4] + + no_space(); + } else if ( + left.id === "." + || left.id === "?." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + || (right.id === "." || right.id === "?.") + ) { + +// test_cause: +// ["let aa = 0 ;", "no_space_only", "unexpected_space_a_b", ";", 12] +// ["let aa = aa ?.aa;", "no_space_only", "unexpected_space_a_b", "?.", 13] + + no_space_only(); + } else if (left.id === ";") { + +// test_cause: +// [" +// /*jslint for*/ +// function aa() { +// for ( +// aa(); +// aa; +// aa() +// ) { +// aa(); +// } +// } +// ", "expected_at", "expected_a_at_b_c", "9", 1] + + if (open) { + at_margin(0); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || left.id === "await" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + +// test_cause: +// [" +// function aa() { +// do { +// aa(); +// } while(aa()); +// } +// ", "one_space_only", "expected_space_a_b", "(", 12] + + one_space_only(); + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || left.id === "async" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + +// test_cause: +// ["let aa=0;", "one_space", "expected_space_a_b", "0", 8] +// ["let aa={\naa:\n0\n};", "expected_at", "expected_a_at_b_c", "5", 1] + + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +function jslint_report({ + exports, + froms, + functions, + global, + json, + module, + property, + stop, + warnings +}) { + +// This function will create human-readable, html-report +// for warnings, properties, and functions from jslint-result-object. +// +// Example usage: +// let result = jslint("console.log('hello world')"); +// let html = jslint_report(result); + + let html = ""; + let length_80 = 1111; + + function address(line = 1, column = 1) { + +// This function will create HTML address element from and + + return `
    ${Number(line)}: ${Number(column)}
    `; + + } + + function detail(title, list) { + return ( + (Array.isArray(list) && list.length > 0) + ? ( + +// Google Lighthouse Accessibility -
    's do not contain only properly-ordered +//
    and
    groups, + + + + + + + + + + + + +

    CodeMirror: JSLint Demo

    +

    +This demo will auto-lint the code below, and auto-generate a report as you type. +

    + + + + + + + +
    + + + + + diff --git a/branch-alpha/jslint_wrapper_codemirror.js b/branch-alpha/jslint_wrapper_codemirror.js new file mode 100644 index 000000000..0cdc0950c --- /dev/null +++ b/branch-alpha/jslint_wrapper_codemirror.js @@ -0,0 +1,146 @@ +// The Unlicense +// +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + + +// This wrapper will integrate JSLint with CodeMirror's lint.js addon. +// Requires CodeMirror and JSLint. +// +// Example usage: +/* + + + + + + + + + + + +*/ + +/*jslint browser, devel*/ +/*global CodeMirror define exports jslint module require*/ +/*property + Pos, amd, column, error, from, globals, jslint, line, map, message, + mode_stop, registerHelper, result, severity, signal, source, to, warnings +*/ + +(function (mod) { + "use strict"; + +// CommonJS + + if (typeof exports === "object" && typeof module === "object") { + mod(require("../../lib/codemirror")); + +// AMD + + } else if (typeof define === "function" && define.amd) { + define(["../../lib/codemirror"], mod); + +// Plain browser env + + } else { + mod(CodeMirror); + } +}(function (CodeMirror) { + "use strict"; + if (!window.jslint) { + console.error( + "Error: window.jslint not defined," + + " CodeMirror JavaScript linting cannot run." + ); + return []; + } + CodeMirror.registerHelper("lint", "javascript", function ( + source, + options, + editor + ) { + let result; + +// Emit before linter is run, so it can be modified +// before passing to jslint. +// +// Example usage: +// editor.on("lintJslintBefore", function (options) { +// options.browser = true; +// options.node = true; +// options.globals = ["caches", "indexedDb"]; +// }); + + options.source = source; + CodeMirror.signal(editor, "lintJslintBefore", options); + +// Run jslint. + + result = jslint.jslint(source, options, options.globals); + +// Emit after linter is run, so it can be used to generate reports. +// +// Example usage: +// editor.on("lintJslintAfter", function (options) { +// divReport.innerHTML = jslint.jslint_report(options.result); +// }); + + options.result = result; + CodeMirror.signal(editor, "lintJslintAfter", options); + +// Return warnings. + + return result.warnings.map(function ({ + column, + line, + message, + mode_stop + }) { + return { + from: CodeMirror.Pos(line - 1, column - 1), //jslint-ignore-line + message, + severity: ( + mode_stop + ? "error" + : "warning" + ), + to: CodeMirror.Pos(line - 1, column) //jslint-ignore-line + }; + }); + }); +})); diff --git a/branch-alpha/jslint_wrapper_vim.vim b/branch-alpha/jslint_wrapper_vim.vim new file mode 100644 index 000000000..ce6700c1c --- /dev/null +++ b/branch-alpha/jslint_wrapper_vim.vim @@ -0,0 +1,59 @@ +"" The Unlicense +"" +"" This is free and unencumbered software released into the public domain. +"" +"" Anyone is free to copy, modify, publish, use, compile, sell, or +"" distribute this software, either in source code form or as a compiled +"" binary, for any purpose, commercial or non-commercial, and by any +"" means. +"" +"" In jurisdictions that recognize copyright laws, the author or authors +"" of this software dedicate any and all copyright interest in the +"" software to the public domain. We make this dedication for the benefit +"" of the public at large and to the detriment of our heirs and +"" successors. We intend this dedication to be an overt act of +"" relinquishment in perpetuity of all present and future rights to this +"" software under copyright law. +"" +"" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +"" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +"" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +"" IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +"" OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +"" ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +"" OTHER DEALINGS IN THE SOFTWARE. + + +"" jslint_wrapper_vim.vim +"" +"" jslint wrapper for vim +"" +"" 1. Save this file and "jslint.mjs" to directory "~/.vim/" +"" 2. Add vim-command ":source ~/.vim/jslint_wrapper_vim.vim" to file "~/.vimrc" +"" 3. Vim can now jslint files (via nodejs): +"" - with vim-command ":SaveAndJslint" +"" - with vim-key-combo " " + +let s:dir = expand(":p:h") + +"" this function will save current file and jslint it (via nodejs) +function! SaveAndJslint(bang) + "" save file + if a:bang == "!" | write! | else | write | endif + "" jslint file (via nodejs) + let &l:errorformat = + \ "%f..js:%n:%l:%c:%m," . + \ "%f:%n:%l:%c:%m" + let &l:makeprg = "node" + \ . " \"" . s:dir . "/jslint.mjs\"" + \ . " jslint_wrapper_vim" + \ . " \"" . fnamemodify(bufname("%"), ":p") . "\"" + silent make! | cwindow | redraw! +endfunction + +"" create vim-command ":SaveAndJslint" +command! -nargs=* -bang SaveAndJslint call SaveAndJslint("") + +"" map vim-key-combo " " to ":SaveAndJslint" +inoremap :SaveAndJslint +nnoremap :SaveAndJslint diff --git a/branch-alpha/jslint_wrapper_vscode.js b/branch-alpha/jslint_wrapper_vscode.js new file mode 100644 index 000000000..3ecf2a54e --- /dev/null +++ b/branch-alpha/jslint_wrapper_vscode.js @@ -0,0 +1,243 @@ +// The Unlicense +// +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + + +/*jslint beta, node*/ +/*property + Diagnostic, DiagnosticSeverity, ProgressLocation, Warning, Window, activate, + cancellable, character, clear, column, commands, createDiagnosticCollection, + document, end, endsWith, exports, fsPath, getText, increment, insert, + isEmpty, jslint, languages, line, lineAt, location, map, message, module, + promises, push, range, rangeIncludingLineBreak, readFileSync, + registerTextEditorCommand, replace, report, runInNewContext, selection, set, + slice, start, subscriptions, title, uri, warnings, window, withProgress, + writeFile +*/ + +"use strict"; + +function activate({ + subscriptions +}) { +/** + * @param {vscode.ExtensionContext} context + */ +// This method is called when your extension is activated. +// Your extension is activated the very first time the command is executed. + +// Directly print diagnostic without language-server. +// https://stackoverflow.com/questions/35581332 +// /create-diagnostics-entries-without-language-server-in-visual-studio-code + + let diagnosticCollection; + let jslint; + let vscode; + + function jslintClear() { + return vscode.window.withProgress({ + cancellable: false, + location: vscode.ProgressLocation.Window, + title: "JSLint clear warnings ..." + }, async function (progress) { + progress.report({ + increment: 0 + }); + +// Clear "Problems" tab. + + diagnosticCollection.clear(); + +// Wait awhile for flicker to give user visual confirmation "Problems" tab +// is refreshed. + + await new Promise(function (resolve) { + setTimeout(resolve, 500); + }); + + progress.report({ + increment: 100 + }); + }); + } + + function jslintDisableRegion({ + document, + selection + }, edit) { + let range; + let text; + edit.insert({ + character: 0, + line: selection.start.line + }, "/*jslint-disable*/\n"); + range = document.lineAt(selection.end).rangeIncludingLineBreak; + text = document.getText(range); + +// If selection-end is EOL without preceding line-break, +// then prepend line-break before directive. + + if (!text.endsWith("\n")) { + text += "\n/*jslint-enable*/"; + +// If selection-end is start of a new line, then prepend directive before it. + + } else if (!selection.isEmpty && selection.end.character === 0) { + text = "/*jslint-enable*/\n" + text; + +// Append directive to selection-end. + + } else { + text += "/*jslint-enable*/\n"; + } + edit.replace(range, text); + } + + function jslintIgnoreLine({ + document, + selection + }, edit) { + edit.insert({ + character: document.lineAt(selection.end).range.end.character, + line: selection.end.line + }, " //jslint-ignore-line"); + } + + function jslintLint({ + document + }) { + return vscode.window.withProgress({ + cancellable: false, + location: vscode.ProgressLocation.Window, + title: "JSLint lint file ..." + }, async function (progress) { + let result; + progress.report({ + increment: 0 + }); + +// Clear "Problems" tab. + + diagnosticCollection.clear(); + result = document.getText(); + result = jslint.jslint(result); + result = result.warnings.slice(0, 100).map(function ({ + column, + line, + // line_source, + message + }) { + return new vscode.Diagnostic( + // code: line_source, + { + end: { + character: column - 1, + line: line - 1 + }, + start: { + character: column - 1, + line: line - 1 + } + }, + `JSLint - ${message}`, + vscode.DiagnosticSeverity.Warning + ); + }); + +// Wait awhile for flicker to give user visual confirmation "Problems" tab +// is refreshed. + + await new Promise(function (resolve) { + setTimeout(resolve, 100); + }); + +// Update "Problems" tab. + + diagnosticCollection.set(document.uri, result); + progress.report({ + increment: 100 + }); + }); + } + +// PR-429 - Add manual lint-on-save command. + + async function jslintLintAndSave({ + document + }) { + jslintLint({ + document + }); + await require("fs").promises.writeFile( + document.uri.fsPath, + document.getText() + ); + } + +// Initialize vscode and jslint. + + vscode = require("vscode"); + diagnosticCollection = vscode.languages.createDiagnosticCollection( + "jslint" + ); + require("vm").runInNewContext( + ( + "\"use strict\";" + + require("fs").readFileSync( //jslint-ignore-line + __dirname + "/jslint.mjs", + "utf8" + ).replace( + "\nexport default Object.freeze(jslint_export);", + "\nmodule.exports = jslint_export;" + ).replace( + "\njslint_import_meta_url = import.meta.url;", + "\n// jslint_import_meta_url = import.meta.url;" + ) + ), + { + module + } + ); + jslint = module.exports; + +// Register extension commands. + + subscriptions.push(vscode.commands.registerTextEditorCommand(( + "jslint.clear" + ), jslintClear)); + subscriptions.push(vscode.commands.registerTextEditorCommand(( + "jslint.disableRegion" + ), jslintDisableRegion)); + subscriptions.push(vscode.commands.registerTextEditorCommand(( + "jslint.ignoreLine" + ), jslintIgnoreLine)); + subscriptions.push(vscode.commands.registerTextEditorCommand(( + "jslint.lint" + ), jslintLint)); + subscriptions.push(vscode.commands.registerTextEditorCommand(( + "jslint.lintAndSave" + ), jslintLintAndSave)); +} + +exports.activate = activate; diff --git a/branch-alpha/package.json b/branch-alpha/package.json new file mode 100644 index 000000000..d1f5d2207 --- /dev/null +++ b/branch-alpha/package.json @@ -0,0 +1,39 @@ +{ + "bin": { + "jslint": "jslint.mjs" + }, + "bugs": { + "url": "https://github.com/jslint-org/jslint/issues" + }, + "counter": 0, + "description": "JSLint, The JavaScript Code Quality and Coverage Tool", + "exports": { + "default": "./jslint_wrapper_cjs.cjs", + "import": "./jslint.mjs" + }, + "fileCount": 34, + "keywords": [ + "coverage-report", + "javascript", + "jslint", + "linter", + "zero-config", + "zero-dependency" + ], + "license": "UNLICENSE", + "main": "./jslint_wrapper_cjs.cjs", + "module": "./jslint.mjs", + "name": "@jslint-org/jslint", + "repository": { + "type": "git", + "url": "git+https://github.com/jslint-org/jslint.git" + }, + "scripts": { + "test": "node jslint.mjs v8_coverage_report=.artifact/coverage node test.mjs", + "test2": "sh jslint_ci.sh shCiBase" + }, + "shCiArtifactUpload": 1, + "shCiPublishNpm": 1, + "type": "module", + "version": "2024.11.24" +} diff --git a/branch-alpha/test.mjs b/branch-alpha/test.mjs new file mode 100644 index 000000000..24967f887 --- /dev/null +++ b/branch-alpha/test.mjs @@ -0,0 +1,1575 @@ +/*jslint beta, node*/ +import jslint from "./jslint.mjs"; +import jslintCjs from "./jslint_wrapper_cjs.cjs"; +import moduleFs from "fs"; +import modulePath from "path"; + +let { + assertErrorThrownAsync, + assertJsonEqual, + assertOrThrow, + debugInline, + fsWriteFileWithParents, + globExclude, + jstestDescribe, + jstestIt, + jstestOnExit, + moduleFsInit, + noop, + v8CoverageListMerge, + v8CoverageReportCreate +} = jslint; +let sourceJslintMjs; +let testCoverageMergeData; + +await (async function init() { + +// Coverage-hack - Ugly-hack to get test-coverage for all initialization-states. + + moduleFsInit(); + moduleFsInit(); + +// Cleanup directory .tmp + + await moduleFs.promises.rm(".tmp", { + recursive: true + }).catch(noop); + +// init sourceJslintMjs + + sourceJslintMjs = await moduleFs.promises.readFile("jslint.mjs", "utf8"); + +// init testCoverageMergeData + + testCoverageMergeData = JSON.parse( + await moduleFs.promises.readFile( + "test_coverage_merge_data.json", + "utf8" + ) + ); +}()); + +jstestDescribe(( + "test fsXxx handling-behavior" +), function testBehaviorFsXxx() { + jstestIt(( + "test fsWriteFileWithParents handling-behavior" + ), async function () { + await Promise.all([ + 1, 2, 3, 4 + ].map(async function () { + await fsWriteFileWithParents( + ".tmp/fsWriteFileWithParents/aa/bb/cc", + "aa" + ); + })); + assertJsonEqual( + await moduleFs.promises.readFile( + ".tmp/fsWriteFileWithParents/aa/bb/cc", + "utf8" + ), + "aa" + ); + }); +}); + +jstestDescribe(( + "test globXxx handling-behavior" +), function testBehaviorGlobXxx() { + jstestIt(( + "test globAssertNotWeird-error handling-behavior" + ), async function () { + await Promise.all([ + "\n", + "\r", + "\u0000" + ].map(async function (char) { + await assertErrorThrownAsync(function () { + return globExclude({ + pathnameList: [ + "aa", + `cc/${char}/dd`, + "bb" + ] + }); + }, ( + "Weird character " + + JSON.stringify(char).replace("\\", "\\\\") + + " found in " + )); + })); + }); + jstestIt(( + "test globExclude handling-behavior" + ), function () { + let pathnameList = [ + ".dockerignore", + ".eslintrc.js", + ".gitignore", + ".npmignore", + ".travis.yml", + "/node_modules/aa/bb/cc.js", + "/node_modules/aa/bb/dd.js", + "CHANGELOG.md", + "CONTRIBUTING.md", + "Dockerfile", + "LICENSE", + "Makefile", + "README.md", + "appveyor.yml", + "benchmark/insert-transaction.sql", + "benchmark/insert.js", + "binding.gyp", + "cloudformation/ci.template.js", + "deps/common-sqlite.gypi", + "deps/extract.py", + "deps/sqlite-autoconf-3340000.tar.gz", + "deps/sqlite3.gyp", + "examples/simple-chaining.js", + "lib/index.js", + "lib/sqlite3-binding.js", + "lib/sqlite3.js", + "lib/trace.js", + "node_modules/aa/bb/cc.js", + "node_modules/aa/bb/dd.js", + "package.json", + "scripts/build-appveyor.bat", + "scripts/build-local.bat", + "scripts/build_against_electron.sh", + "scripts/build_against_node.sh", + "scripts/build_against_node_webkit.sh", + "scripts/build_for_node_webkit.cmd", + "scripts/install_node.sh", + "scripts/validate_tag.sh", + "sqlite3.js", + "src/async.h", + "src/backup.cc", + "src/backup.h", + "src/database.cc", + "src/database.h", + "src/gcc-preinclude.h", + "src/macros.h", + "src/node_sqlite3.cc", + "src/statement.cc", + "src/statement.h", + "src/threading.h", + "test/affected.test.js", + "test/backup.test.js", + "test/blob.test.js", + "test/cache.test.js", + "test/constants.test.js", + "test/database_fail.test.js", + "test/each.test.js", + "test/exec.test.js", + "test/extension.test.js", + "test/fts-content.test.js", + "test/interrupt.test.js", + "test/issue-108.test.js", + "test/json.test.js", + "test/map.test.js", + "test/named_columns.test.js", + "test/named_params.test.js", + "test/null_error.test.js", + "test/nw/.gitignore", + "test/nw/Makefile", + "test/nw/index.html", + "test/nw/package.json", + "test/open_close.test.js", + "test/other_objects.test.js", + "test/parallel_insert.test.js", + "test/prepare.test.js", + "test/profile.test.js", + "test/rerun.test.js", + "test/scheduling.test.js", + "test/serialization.test.js", + "test/support/createdb-electron.js", + "test/support/createdb.js", + "test/support/elmo.png", + "test/support/helper.js", + "test/support/prepare.db", + "test/support/script.sql", + "test/trace.test.js", + "test/unicode.test.js", + "test/upsert.test.js", + "test/verbose.test.js", + "tools/docker/architecture/linux-arm/Dockerfile", + "tools/docker/architecture/linux-arm/run.sh", + "tools/docker/architecture/linux-arm64/Dockerfile", + "tools/docker/architecture/linux-arm64/run.sh" + ]; + [ + "tes?/", + "tes[-t-]/", + "tes[-t]/", + "tes[0-9A-Z_a-z-]/", + "tes[t-]/", + "test/**/*.js" + ].forEach(function (aa) { + [ + "li*/*.js", + "li?/*.js", + "lib/", + "lib/*", + "lib/**/*.js", + "lib/*.js" + ].forEach(function (bb) { + [ + "", + "**/node_modules/", + "node_modules/" + ].forEach(function (cc) { + assertJsonEqual( + globExclude({ + excludeList: [ + "tes[!0-9A-Z_a-z-]/", + "tes[^0-9A-Z_a-z-]/", + "test/suppor*/*elper.js", + "test/suppor?/?elper.js", + "test/support/helper.js" + ].concat(aa, cc), + includeList: [ + "**/*.cjs", + "**/*.js", + "**/*.mjs", + "lib/sqlite3.js" + ].concat(bb), + pathnameList + }).pathnameList, + [ + ".eslintrc.js", + "benchmark/insert.js", + "cloudformation/ci.template.js", + "examples/simple-chaining.js", + "lib/index.js", + "lib/sqlite3-binding.js", + "lib/sqlite3.js", + "lib/trace.js", + "sqlite3.js" + ].concat( + cc === "**/node_modules/" + ? [ + "node_modules/aa/bb/cc.js", + "node_modules/aa/bb/dd.js" + ] + : cc === "node_modules/" + ? [ + "/node_modules/aa/bb/cc.js", + "/node_modules/aa/bb/dd.js" + ] + : [ + "/node_modules/aa/bb/cc.js", + "/node_modules/aa/bb/dd.js", + "node_modules/aa/bb/cc.js", + "node_modules/aa/bb/dd.js" + ] + ).sort() + ); + }); + }); + }); + }); + jstestIt(( + "test globToRegexp handling-behavior" + ), function () { + Object.entries({ + "*": ( + /^[^\/]*?$/gm + ), + "**": ( + /^.*?$/gm + ), + "***": ( + /^.*?$/gm + ), + "****": ( + /^.*?$/gm + ), + "****////****": ( + /^.*?$/gm + ), + "***///***": ( + /^.*?$/gm + ), + "**/*": ( + /^.*?$/gm + ), + "**/node_modules/": ( + /^.*?\/node_modules\/.*?$/gm + ), + "**/node_modules/**/*": ( + /^.*?\/node_modules\/.*?$/gm + ), + "?": ( + /^[^\/]$/gm + ), + "[!0-9A-Za-z-]": ( + /^[^0-9A-Za-z\-]$/gm + ), + "[0-9A-Za-z-]": ( + /^[0-9A-Za-z\-]$/gm + ), + "[[]] ]][[": ( + /^[\[]\] \]\][\[]$/gm + ), + "[]": ( + /^$/gm + ), + "[^0-9A-Za-z-]": ( + /^[^0-9A-Za-z\-]$/gm + ), + "aa/bb/cc": ( + /^aa\/bb\/cc$/gm + ), + "aa/bb/cc/": ( + /^aa\/bb\/cc\/.*?$/gm + ), + "li*/*": ( + /^li[^\/]*?\/[^\/]*?$/gm + ), + "li?/*": ( + /^li[^\/]\/[^\/]*?$/gm + ), + "lib/": ( + /^lib\/.*?$/gm + ), + "lib/*": ( + /^lib\/[^\/]*?$/gm + ), + "lib/**/*.js": ( + /^lib\/.*?\.js$/gm + ), + "lib/*.js": ( + /^lib\/[^\/]*?\.js$/gm + ), + "node_modules/": ( + /^node_modules\/.*?$/gm + ), + "node_modules/**/*": ( + /^node_modules\/.*?$/gm + ), + "tes[!0-9A-Z_a-z-]/**/*": ( + /^tes[^0-9A-Z_a-z\-]\/.*?$/gm + ), + "tes[0-9A-Z_a-z-]/**/*": ( + /^tes[0-9A-Z_a-z\-]\/.*?$/gm + ), + "tes[^0-9A-Z_a-z-]/**/*": ( + /^tes[^0-9A-Z_a-z\-]\/.*?$/gm + ), + "test/**/*": ( + /^test\/.*?$/gm + ), + "test/**/*.js": ( + /^test\/.*?\.js$/gm + ), + "test/suppor*/*elper.js": ( + /^test\/suppor[^\/]*?\/[^\/]*?elper\.js$/gm + ), + "test/suppor?/?elper.js": ( + /^test\/suppor[^\/]\/[^\/]elper\.js$/gm + ), + "test/support/helper.js": ( + /^test\/support\/helper\.js$/gm + ) + }).forEach(function ([ + pattern, rgx + ]) { + assertJsonEqual( + globExclude({ + excludeList: [ + pattern + ] + }).excludeList[0].source, + rgx.source + ); + assertJsonEqual( + globExclude({ + includeList: [ + pattern + ] + }).includeList[0].source, + rgx.source + ); + }); + }); +}); + +jstestDescribe(( + "test jslint's cli handling-behavior" +), function testBehaviorJslintCli() { + function processExit0(exitCode) { + assertOrThrow(exitCode === 0, exitCode); + } + function processExit1(exitCode) { + assertOrThrow(exitCode === 1, exitCode); + } + jstestIt(( + "test cli-null-case handling-behavior" + ), function () { + jslint.jslint_cli({ + mode_noop: true, + process_exit: processExit0 + }); + }); + jstestIt(( + "test cli-window-jslint handling-behavior" + ), function () { + [ + "&window_jslint=", + "&window_jslint=12", + "&window_jslint=1?", + "&window_jslint=?", + "?window_jslint=", + "?window_jslint=12", + "?window_jslint=1?", + "?window_jslint=?", + "window_jslint=1", + "window_jslint=1&", + "window_jslint=12", + "window_jslint=1?" + ].forEach(function (import_meta_url) { + jslint.jslint_cli({ + import_meta_url + }); + assertOrThrow(globalThis.jslint === undefined); + }); + [ + "&window_jslint=1", + "&window_jslint=1&", + "?window_jslint=1", + "?window_jslint=1&" + ].forEach(function (import_meta_url) { + jslint.jslint_cli({ + import_meta_url + }); + assertOrThrow(globalThis.jslint === jslint); + delete globalThis.jslint; + }); + }); + jstestIt(( + "test cli-cjs-and-invalid-file handling-behavior" + ), async function () { + await fsWriteFileWithParents(".test_dir.cjs/touch.txt", ""); + [ + ".", // test dir handling-behavior + "jslint.mjs", // test file handling-behavior + undefined // test file-undefined handling-behavior + ].forEach(function (file) { + jslint.jslint_cli({ + file, + mode_cli: true, + process_env: { + JSLINT_BETA: "1" + }, + process_exit: processExit0 + }); + }); + }); + jstestIt(( + "test cli-apidoc handling-behavior" + ), function () { + jslint.jslint_cli({ + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_apidoc=.artifact/apidoc.html", + JSON.stringify({ + example_list: [ + "README.md", + "test.mjs", + "jslint.mjs" + ], + github_repo: "https://github.com/jslint-org/jslint", + module_list: [ + { + pathname: "./jslint.mjs" + } + ], + package_name: "JSLint", + version: jslint.jslint_edition + }) + ], + process_exit: processExit0 + }); + }); + jstestIt(( + "test cli-file-error handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + file: "undefined", + mode_cli: true, + process_exit: processExit1 + }); + }); + jstestIt(( + "test cli-syntax-error handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + file: "syntax-error.js", + mode_cli: true, + option: { + trace: true + }, + process_exit: processExit1, + source: "syntax error" + }); + }); + jstestIt(( + "test cli-report handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_report=.tmp/jslint_report.html", + "jslint.mjs" + ], + process_exit: processExit0 + }); + }); + jstestIt(( + "test cli-report-error handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_report=.tmp/jslint_report.html", + "syntax-error.js" + ], + process_exit: processExit1, + source: "syntax error" + }); + }); + jstestIt(( + "test cli-report-json handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_report=.tmp/jslint_report.html", + "aa.json" + ], + process_exit: processExit0, + source: "[]" + }); + }); + jstestIt(( + "test cli-report-json-error handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_report=.tmp/jslint_report.html", + "aa.json" + ], + process_exit: processExit1, + source: "[" + }); + }); + jstestIt(( + "test cli-report-misc handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_report=.tmp/jslint_report.html", + "aa.js" + ], + process_exit: processExit0, + source: "let aa = 0;" + }); + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_report=.tmp/jslint_report.html", + "aa.js" + ], + process_exit: processExit1, + source: "(aa)=>aa; function aa([aa]){}" + }); + }); + jstestIt(( + "test cli-jslint-wrapper-vim handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_wrapper_vim", + "syntax-error.js" + ], + process_exit: processExit1, + source: "syntax error" + }); + }); +}); + +jstestDescribe(( + "test jslint's no-warnings handling-behavior" +), function testBehaviorJslintNoWarnings() { + jstestIt(( + "test jslint's no-warnings handling-behavior" + ), function () { + Object.values({ + array: [ + "new Array(0);" + ], + async_await: [ + "async function aa() {\n await aa();\n}", + +// PR-405 - Bugfix - fix expression after "await" mis-identified as statement. + + "async function aa() {\n await aa;\n}", + ( + "async function aa() {\n" + + " try {\n" + + " aa();\n" + + " } catch (err) {\n" + + " await err();\n" + + " }\n" + + "}\n" + ), + ( + "async function aa() {\n" + + " try {\n" + + " await aa();\n" + + " } catch (err) {\n" + + " await err();\n" + + " }\n" + + "}\n" + ), + +// PR-370 - Add top-level-await support. + + "await String();\n" + ], + +// PR-351 - Add BigInt support. + + bigint: [ + "let aa = 0b0n;\n", + "let aa = 0o0n;\n", + "let aa = 0x0n;\n", + "let aa = BigInt(0n);\n", + "let aa = typeof aa === \"bigint\";\n" + ], + date: [ + "Date.getTime();", + "let aa = aa().getTime();", + "let aa = aa.aa().getTime();" + ], + directive: [ + "#!\n/*jslint browser:false, node*/\n\"use strict\";", + "/*property aa bb*/" + ], + for: [ + ( + "/*jslint for*/\n" + + "function aa(bb) {\n" + + " for (bb = 0; bb < 0; bb += 1) {\n" + + " bb();\n" + + " }\n" + + "}\n" + ) + ], + jslint_disable: [ + "/*jslint-disable*/\n0\n/*jslint-enable*/" + ], + jslint_ignore_line: [ + "0 //jslint-ignore-line" + ], + json: [ + "{\"aa\":[[],-0,null]}" + ], + label: [ + ( + "function aa() {\n" + + "bb:\n" + + " while (true) {\n" + + " if (true) {\n" + + " break bb;\n" + + " }\n" + + " }\n" + + "}\n" + ) + ], + loop: [ + ( + "function aa() {\n" + + " do {\n" + + " aa();\n" + + " } while (aa());\n" + + "}\n" + ), + +// PR-378 - Relax warning "function_in_loop". + + ( + "function aa() {\n" + + " while (true) {\n" + + " (function () {\n" + + " return;\n" + + " }());\n" + + " }\n" + + "}\n" + ) + ], + module: [ + "export default Object.freeze();", + +// PR-439 - Add grammar for "export async function ...". + + ( + "export default Object.freeze(async function () {\n" + + " return await 0;\n" + + "});\n" + ), + "import {aa, bb} from \"aa\";\naa(bb);", + "import {} from \"aa\";", + "import(\"aa\").then(function () {\n return;\n});", + ( + "let aa = 0;\n" + + "import(aa).then(aa).then(aa)" + + ".catch(aa).finally(aa);\n" + ) + ], + number: [ + "let aa = 0.0e0;", + "let aa = 0b0;", + "let aa = 0o0;", + "let aa = 0x0;" + ], + +// PR-390 - Add numeric-separator support. + + numeric_separator: [ + "let aa = 0.0_0_0;", + "let aa = 0b0_1111_1111n;\n", + "let aa = 0o0_1234_1234n;\n", + "let aa = 0x0_1234_1234n;\n", + "let aa = 1_234_234.1_234_234E1_234_234;" + ], + optional_chaining: [ + "let aa = aa?.bb?.cc;" + ], + param: [ + "function aa({aa, bb}) {\n return {aa, bb};\n}\n", + ( + "function aa({constructor}) {\n" + + " return {constructor};\n" + + "}\n" + ) + ], + property: [ + "let aa = aa[`!`];" + ], + regexp: [ + "function aa() {\n return /./;\n}", + "let aa = /(?!.)(?:.)(?=.)/;", + "let aa = /./gimuy;", + "let aa = /[\\--\\-]/;" + ], + ternary: [ + ( + "let aa = (\n" + + " aa()\n" + + " ? 0\n" + + " : 1\n" + + ") " + + "&& (\n" + + " aa()\n" + + " ? 0\n" + + " : 1\n" + + ");" + ), + ( + "let aa = (\n" + + " aa()\n" + + " ? `${0}`\n" + + " : `${1}`\n" + + ");" + ), + +// PR-394 - Bugfix +// Fix jslint falsely believing megastring literals `0` and `1` are similar. + + ( + "let aa = (\n" + + " aa()\n" + + " ? `0`\n" + + " : `1`\n" + + ");" + ) + ], + try_catch: [ + ( + "let aa = 0;\n" + + "try {\n" + + " aa();\n" + + "} catch (err) {\n" + + " aa = err;\n" + + "}\n" + + "try {\n" + + " aa();\n" + + "} catch (err) {\n" + + " aa = err;\n" + + "}\n" + + "aa();\n" + ) + ], + try_finally: [ + ( + "let aa = 0;\n" + + "try {\n" + + " aa();\n" + + "} finally {\n" + + " aa();\n" + + "}\n" + ) + ], + use_strict: [ + ( + "\"use strict\";\n" + + "let aa = 0;\n" + + "function bb() {\n" + + " \"use strict\";\n" + + " return aa;\n" + + "}\n" + ) + ], + var: [ + +// PR-363 - Bugfix +// Add test against false-warning in code +// '/*jslint node*/\nlet {aa:bb} = {}; bb();'. + + "/*jslint node*/\n", + "" + ].map(function (directive) { + return [ + "let [\n aa, bb = 0\n] = 0;\naa();\nbb();", + "let aa = 0;\nlet [...bb] = [...aa];\nbb();", + +// PR-459 - Allow destructuring-assignment after function-definition. + + ( + "let aa;\n" + + "let bb;\n" + + "function cc() {\n" + + " return;\n" + + "}\n" + + "[aa, bb] = cc();\n" + ), + "let constructor = 0;\nconstructor();", + "let {\n aa: bb\n} = 0;\nbb();", + "let {\n aa: bb,\n bb: cc\n} = 0;\nbb();\ncc();", + "let {aa, bb} = 0;\naa();\nbb();", + "let {constructor} = 0;\nconstructor();" + ].map(function (code) { + return directive + code; + }); + }).flat() + }).forEach(function (codeList) { + let elemPrv = ""; + codeList.forEach(function (code) { + let warnings; + // Assert codeList is sorted. + assertOrThrow(elemPrv < code, JSON.stringify([ + elemPrv, code + ], undefined, 4)); + elemPrv = code; + [ + jslint.jslint, + jslintCjs.jslint + ].forEach(function (jslint) { + warnings = jslint(code, { + beta: true + }).warnings; + assertOrThrow( + warnings.length === 0, + JSON.stringify([code, warnings]) + ); + }); + }); + }); + }); +}); + +jstestDescribe(( + "test jslint's option handling-behavior" +), function testBehaviorJslintOption() { + let elemPrv = ""; + [ + [ + "let aa = aa | 0;", {bitwise: true}, [] + ], [ + ";\naa(new XMLHttpRequest());", {browser: true}, ["aa"] + ], [ + "let aa = \"aa\" + 0;", {convert: true}, [] + ], [ + "registerType();", {couch: true}, [] + ], [ + "debugger;", {devel: true}, [] + ], [ + +// PR-404 - Alias "evil" to jslint-directive "eval" for backwards-compat. + + "new Function();\neval();", {eval: true, evil: true}, [] + ], [ + "let aa = () => 0;", {fart: true}, [] + ], [ + ( + "let aa = async (bb, {cc, dd}, [ee, ff], ...gg) => {\n" + + " bb += 1;\n" + + " return await (bb + cc + dd + ee + ff + gg);\n" + + "};\n" + ), {fart: true}, [] + ], [ + ( + "function aa(aa) {\n" + + " for (aa = 0; aa < 0; aa += 1) {\n" + + " aa();\n" + + " }\n" + + "}\n" + ), {for: true}, [] + ], [ + "let aa = {get aa() {\n return;\n}};", {getset: true}, [] + ], [ + "let aa = {set aa(aa) {\n return aa;\n}};", {getset: true}, [] + ], [ + sourceJslintMjs.replace(( + / /g + ), " "), {indent2: true}, [] + ], [ + "function aa() {\n return;\n}", {indent2: true}, [] + ], [ + "/".repeat(100), {long: true}, [] + ], [ + +// PR-404 - Alias "nomen" to jslint-directive "name" for backwards-compat. + + "let aa = aa._;", {name: true, nomen: true}, [] + ], [ + "require();", {node: true}, [] + ], [ + "let aa = 'aa';", {single: true}, [] + ], [ + +// PR-404 - Add new directive "subscript" to play nice with Google Closure. + + "aa[\"aa\"] = 1;", {subscript: true}, ["aa"] + ], [ + "", {test_internal_error: true}, [] + ], [ + "let aa = this;", {this: true}, [] + ], [ + "", {trace: true}, [] + ], [ + ( + "function aa({bb, aa}) {\n" + + " switch (aa) {\n" + + " case 1:\n" + + " break;\n" + + " case 0:\n" + + " break;\n" + + " default:\n" + + " return {bb, aa};\n" + + " }\n" + + "}\n" + ), {unordered: true}, [] + ], [ + "let {bb, aa} = 0;", {unordered: true}, [] + ], [ + ( + "function aa() {\n" + + " if (aa) {\n" + + " let bb = 0;\n" + + " return bb;\n" + + " }\n" + + "}\n" + ), {variable: true}, [] + ], [ + "let bb = 0;\nlet aa = 0;", {variable: true}, [] + ], [ + "\t", {white: true}, [] + ] + ].forEach(function ([ + source, option_dict, global_list + ]) { + jstestIt(( + `test option=${JSON.stringify(option_dict)} handling-behavior` + ), function () { + let elemNow = JSON.stringify([ + option_dict, source, global_list + ]); + let warningsLength = ( + option_dict.test_internal_error + ? 1 + : 0 + ); + // Assert list is sorted. + assertOrThrow(elemPrv < elemNow, JSON.stringify([ + elemPrv, elemNow + ], undefined, 4)); + elemPrv = elemNow; + option_dict.beta = true; + [ + jslint.jslint, + jslintCjs.jslint + ].forEach(function (jslint) { + // test jslint's option handling-behavior + assertOrThrow( + jslint( + source, + option_dict, + global_list + ).warnings.length === warningsLength, + "jslint.jslint(" + JSON.stringify([ + source, option_dict, global_list + ]) + ")" + ); + // test jslint's directive handling-behavior + source = ( + "/*jslint " + JSON.stringify( + option_dict + ).slice(1, -1).replace(( + /"/g + ), "") + "*/\n" + + ( + global_list.length === 0 + ? "" + : "/*global " + global_list.join(",") + "*/\n" + ) + + source.replace(( + /^#!/ + ), "//") + ); + assertOrThrow( + jslint(source).warnings.length === warningsLength, + source + ); + }); + }); + }); +}); + +jstestDescribe(( + "test jslint's warnings handling-behavior" +), function testBehaviorJslintWarnings() { + jstestIt(( + "test jslint's warning handling-behavior" + ), function () { + +// this function will validate each jslint is raised with given +// malformed + + sourceJslintMjs.replace(( + /(\n\s*?\/\/\s*?test_cause:\s*?)(\S[\S\s]*?\S)(\n\n\s*?) *?\S/g + ), function (match0, header, causeList, footer) { + let tmp; + // console.error(match0); + // Validate header. + assertOrThrow(header === "\n\n// test_cause:\n", match0); + // Validate footer. + assertOrThrow(footer === "\n\n", match0); + // Validate causeList. + causeList = causeList.replace(( + /^\/\/ /gm + ), "").replace(( + /^\["\n([\S\s]*?)\n"(,.*?)$/gm + ), function (ignore, source, param) { + source = "[" + JSON.stringify(source) + param; + assertOrThrow(source.length > (80 - 3), source); + return source; + }).replace(( + / \/\/jslint-ignore-line$/gm + ), ""); + tmp = causeList.split("\n").map(function (cause) { + return ( + "[" + + JSON.parse(cause).map(function (elem) { + return JSON.stringify(elem); + }).join(", ") + + "]" + ); + }).sort().join("\n"); + assertOrThrow( + causeList === tmp, + "\n" + causeList + "\n\n" + tmp + ); + causeList.split("\n").forEach(function (cause) { + cause = JSON.parse(cause); + tmp = jslint.jslint(cause[0], { + beta: true, + test_cause: true + }).causes; + // Validate cause. + assertOrThrow( + tmp[JSON.stringify(cause.slice(1))], + ( + "\n" + JSON.stringify(cause) + "\n\n" + + Object.keys(tmp).sort().join("\n") + ) + ); + }); + return ""; + }); + }); +}); + +jstestDescribe(( + "test jstestXxx handling-behavior" +), function testBehaviorJstestXxx() { + jstestIt(( + "test jstestDescribe error handling-behavior" + ), function () { + throw new Error(); + }, "pass"); + jstestIt(( + "test jstestOnExit tests-failed handling-behavior" + ), function () { + jstestOnExit(undefined, "testsFailed"); + }); +}); + +jstestDescribe(( + "test misc handling-behavior" +), function testBehaviorMisc() { + jstestIt(( + "test misc handling-behavior" + ), async function () { + // test debugInline handling-behavior + noop(debugInline); + // test assertErrorThrownAsync error handling-behavior + await assertErrorThrownAsync(function () { + return assertErrorThrownAsync(noop); + }); + // test assertJsonEqual error handling-behavior + await assertErrorThrownAsync(function () { + assertJsonEqual(1, 2); + }); + await assertErrorThrownAsync(function () { + assertJsonEqual(1, 2, "undefined"); + }); + await assertErrorThrownAsync(function () { + assertJsonEqual(1, 2, {}); + }); + // test assertOrThrow error handling-behavior + await assertErrorThrownAsync(function () { + assertOrThrow(undefined, "undefined"); + }); + await assertErrorThrownAsync(function () { + assertOrThrow(undefined, new Error()); + }); + }); +}); + +jstestDescribe(( + "test v8CoverageListMerge handling-behavior" +), function testBehaviorV8CoverageListMerge() { + let functionsInput = JSON.stringify([ + { + functionName: "test", + isBlockCoverage: true, + ranges: [ + { + count: 2, + endOffset: 4, + startOffset: 0 + }, + { + count: 1, + endOffset: 2, + startOffset: 1 + }, + { + count: 1, + endOffset: 3, + startOffset: 2 + } + ] + } + ]); + jstestIt(( + "accepts empty arrays for `v8CoverageListMerge`" + ), function () { + assertJsonEqual(v8CoverageListMerge([]), { + result: [] + }); + }); + jstestIt(( + "funcCovs.length === 1" + ), function () { + assertJsonEqual(v8CoverageListMerge([ + { + result: [ + { + functions: [ + { + functionName: "test", + isBlockCoverage: true, + ranges: [ + { + count: 2, + endOffset: 4, + startOffset: 0 + } + ] + } + ], + moduleUrl: "/lib.js", + scriptId: "1" + } + ] + }, + { + result: [ + { + functions: [], + moduleUrl: "/lib.js", + scriptId: "2" + } + ] + } + ]), { + result: [ + { + functions: [ + { + functionName: "test", + isBlockCoverage: true, + ranges: [ + { + count: 2, + endOffset: 4, + startOffset: 0 + } + ] + } + ], + scriptId: "0" + } + ] + }); + }); + jstestIt(( + "accepts arrays with a single item for `v8CoverageListMerge`" + ), function () { + assertJsonEqual(v8CoverageListMerge([ + { + result: [ + { + functions: JSON.parse(functionsInput), + moduleUrl: "/lib.js", + scriptId: "123" + } + ] + } + ]), { + result: [ + { + functions: [ + { + functionName: "test", + isBlockCoverage: true, + ranges: [ + { + count: 2, + endOffset: 4, + startOffset: 0 + }, + { + count: 1, + endOffset: 3, + startOffset: 1 + } + ] + } + ], + moduleUrl: "/lib.js", + scriptId: "0" + } + ] + }); + }); + jstestIt(( + "accepts arrays with two identical items for" + + " `v8CoverageListMerge`" + ), function () { + assertJsonEqual(v8CoverageListMerge([ + { + result: [ + { + functions: JSON.parse(functionsInput), + scriptId: "123", + url: "/lib.js" + }, { + functions: JSON.parse(functionsInput), + scriptId: "123", + url: "/lib.js" + } + ] + } + ]), { + result: [ + { + functions: [ + { + functionName: "test", + isBlockCoverage: true, + ranges: [ + { + count: 4, + endOffset: 4, + startOffset: 0 + }, + { + count: 2, + endOffset: 3, + startOffset: 1 + } + ] + } + ], + scriptId: "0", + url: "/lib.js" + } + ] + }); + }); + [ + "test_coverage_merge_is_block_coverage_test.json", + "test_coverage_merge_issue_2_mixed_is_block_coverage_test.json", + "test_coverage_merge_node_10_internal_errors_one_of_test.json", + "test_coverage_merge_reduced_test.json", + "test_coverage_merge_simple_test.json", + "test_coverage_merge_various_test.json" + ].forEach(function (file) { + jstestIt(file, function () { + file = testCoverageMergeData[file]; + file.forEach(function ({ + expected, + inputs + }) { + assertJsonEqual(v8CoverageListMerge(inputs), expected); + }); + }); + }); + jstestIt(( + "merge multiple node-sqlite coverage files" + ), function () { + let data1 = [ + "test_v8_coverage_node_sqlite_9884_1633662346346_0.json", + "test_v8_coverage_node_sqlite_13216_1633662333140_0.json" + ].map(function (file) { + return testCoverageMergeData[file]; + }); + let data2 = testCoverageMergeData[ + "test_v8_coverage_node_sqlite_merged.json" + ]; + data1 = v8CoverageListMerge(data1); + data1 = v8CoverageListMerge([data1]); + +// Debug data1. +// await moduleFs.promises.writeFile( +// ".test_v8_coverage_node_sqlite_merged.json", +// JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n" +// ); + + assertJsonEqual(data1, data2); + }); +}); + +jstestDescribe(( + "test v8CoverageReportCreate handling-behavior" +), function testBehaviorV8CoverageReportCreate() { + jstestIt(( + "test null-case handling-behavior" + ), async function () { + await assertErrorThrownAsync(function () { + return v8CoverageReportCreate({}); + }, "invalid coverageDir"); + }); + jstestIt(( + "test coverage-report jslint.mjs handling-behavior" + ), async function () { + // test remove-old-coverage handling-behavior + await fsWriteFileWithParents( + ".tmp/coverage_jslint/coverage-0-0-0.json", + "" + ); + await jslint.jslint_cli({ + console_error: noop, // comment to debug + mode_cli: true, + process_argv: [ + "node", "jslint.mjs", + "v8_coverage_report=.tmp/coverage_jslint", + "--exclude=aa.js", + "--include-node-modules=1", + "--include=jslint.mjs", + "node", "jslint.mjs" + ] + }); + }); + [ + [ + "v8CoverageReportCreate_high.js", ( + "switch(0){\n" + + "case 0:break;\n" + + "}\n" + ) + ], [ + "v8CoverageReportCreate_ignore.js", ( + "/*coverage-ignore-file*/\n" + + "switch(0){\n" + + "case 0:break;\n" + + "}\n" + ) + ], [ + "v8CoverageReportCreate_low.js", ( + "switch(0){\n" + + "case 1:break;\n" + + "case 2:break;\n" + + "case 3:break;\n" + + "case 4:break;\n" + + "}\n" + ) + ], [ + "v8CoverageReportCreate_medium.js", ( + "switch(0){\n" + + "case 0:break;\n" + + "case 1:break;\n" + + "case 2:break;\n" + + "}\n" + ) + ] + ].forEach(function ([ + file, data + ], ii) { + jstestIt(file, async function () { + let dir = ".tmp/coverage_" + ii + "/"; + file = dir + file; + await fsWriteFileWithParents(file, data); + await jslint.jslint_cli({ + console_error: noop, // comment to debug + mode_cli: true, + process_argv: [ + "node", "jslint.mjs", + "v8_coverage_report=" + dir, + "node", + file + ] + }); + }); + }); + jstestIt(( + "test npm handling-behavior" + ), async function () { + await jslint.jslint_cli({ + console_error: noop, // comment to debug + mode_cli: true, + process_argv: [ + "node", "jslint.mjs", + "v8_coverage_report=.tmp/coverage_npm", + "npm", "--version" + ] + }); + }); + jstestIt(( + "test misc handling-behavior" + ), async function () { + await Promise.all([ + [ + ".tmp/coverage_misc/aa.js", "\n".repeat(0x100) + ], [ + ".tmp/coverage_misc/coverage-0-0-0.json", JSON.stringify({ + "result": [ + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 0xf0, + "startOffset": 0x10 + }, + { + "count": 1, + "endOffset": 0x40, + "startOffset": 0x20 + }, + { + "count": 1, + "endOffset": 0x80, + "startOffset": 0x60 + }, + { + "count": 0, + "endOffset": 0x45, + "startOffset": 0x25 + }, + { + "count": 0, + "endOffset": 0x85, + "startOffset": 0x65 + } + ] + } + ], + "scriptId": "0", + "url": "file:///" + modulePath.resolve( + ".tmp/coverage_misc/aa.js" + ) + } + ] + }, undefined, 4) + ] + ].map(async function ([ + file, data + ]) { + await fsWriteFileWithParents(file, data); + })); + await jslint.jslint_cli({ + console_error: noop, // comment to debug + mode_cli: true, + process_argv: [ + "node", "jslint.mjs", + "v8_coverage_report=.tmp/coverage_misc" + // "node", ".tmp/coverage_misc/aa.js" + ] + }); + }); +}); diff --git a/branch-alpha/test_coverage_merge_data.json b/branch-alpha/test_coverage_merge_data.json new file mode 100644 index 000000000..bc0bb0e48 --- /dev/null +++ b/branch-alpha/test_coverage_merge_data.json @@ -0,0 +1,17877 @@ +{ + "test_coverage_merge_is_block_coverage_test.json": [ + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Identity preserves 'isBlockCoverage: true'", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Identity preserves 'isBlockCoverage: false'", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a flat block-coverage (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a flat block-coverage (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a flat block-coverage (permutation 2)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a flat block-coverage (permutation 3)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a flat block-coverage (permutation 4)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a flat block-coverage (permutation 5)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a block-coverage with a child (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a block-coverage with a child (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a block-coverage with a child (permutation 2)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a block-coverage with a child (permutation 3)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a block-coverage with a child (permutation 4)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a block-coverage with a child (permutation 5)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage and two flat block-coverages (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage and two flat block-coverages (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage and two flat block-coverages (permutation 2)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage and two flat block-coverages (permutation 3)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage and two flat block-coverages (permutation 4)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage and two flat block-coverages (permutation 5)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 24, + "endOffset": 6, + "startOffset": 2 + }, + { + "count": 6, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 42, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverages and two block-coverages with children (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 24, + "endOffset": 6, + "startOffset": 2 + }, + { + "count": 6, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 42, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverages and two block-coverages with children (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 24, + "endOffset": 6, + "startOffset": 2 + }, + { + "count": 6, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 42, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverages and two block-coverages with children (permutation 2)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 24, + "endOffset": 6, + "startOffset": 2 + }, + { + "count": 6, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 42, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverages and two block-coverages with children (permutation 3)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 24, + "endOffset": 6, + "startOffset": 2 + }, + { + "count": 6, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 42, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverages and two block-coverages with children (permutation 4)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 24, + "endOffset": 6, + "startOffset": 2 + }, + { + "count": 6, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 42, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverages and two block-coverages with children (permutation 5)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 50, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "count": 40, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage with a flat block-coverage with count (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 50, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "count": 40, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage with a flat block-coverage with count (permutation 1)", + "status": "run" + } + ], + "test_coverage_merge_issue_2_mixed_is_block_coverage_test.json": [ + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 23, + "functionName": "realpathSync", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 42951, + "startOffset": 39013 + }, + { + "count": 15, + "endOffset": 39088, + "startOffset": 39069 + }, + { + "count": 5, + "endOffset": 39140, + "startOffset": 39088 + }, + { + "count": 0, + "endOffset": 39214, + "startOffset": 39196 + }, + { + "count": 5, + "endOffset": 39356, + "startOffset": 39341 + }, + { + "count": 1, + "endOffset": 39418, + "startOffset": 39383 + }, + { + "count": 19, + "endOffset": 39991, + "startOffset": 39418 + }, + { + "count": 0, + "endOffset": 40010, + "startOffset": 39991 + }, + { + "count": 0, + "endOffset": 40187, + "startOffset": 40012 + }, + { + "count": 19, + "endOffset": 40324, + "startOffset": 40187 + }, + { + "count": 427, + "endOffset": 42868, + "startOffset": 40324 + }, + { + "count": 34, + "endOffset": 40551, + "startOffset": 40436 + }, + { + "count": 393, + "endOffset": 40677, + "startOffset": 40551 + }, + { + "count": 187, + "endOffset": 40798, + "startOffset": 40760 + }, + { + "count": 28, + "endOffset": 40797, + "startOffset": 40770 + }, + { + "count": 257, + "endOffset": 40937, + "startOffset": 40800 + }, + { + "count": 0, + "endOffset": 40915, + "startOffset": 40891 + }, + { + "count": 170, + "endOffset": 40999, + "startOffset": 40937 + }, + { + "count": 11, + "endOffset": 41017, + "startOffset": 40999 + }, + { + "count": 0, + "endOffset": 41097, + "startOffset": 41048 + }, + { + "count": 170, + "endOffset": 42382, + "startOffset": 41097 + }, + { + "count": 135, + "endOffset": 41551, + "startOffset": 41450 + }, + { + "count": 11, + "endOffset": 41525, + "startOffset": 41503 + }, + { + "count": 35, + "endOffset": 41964, + "startOffset": 41551 + }, + { + "count": 13, + "endOffset": 41924, + "startOffset": 41875 + }, + { + "count": 22, + "endOffset": 42214, + "startOffset": 41964 + }, + { + "count": 34, + "endOffset": 42296, + "startOffset": 42214 + }, + { + "count": 0, + "endOffset": 42326, + "startOffset": 42296 + }, + { + "count": 34, + "endOffset": 42376, + "startOffset": 42326 + }, + { + "count": 34, + "endOffset": 42658, + "startOffset": 42382 + }, + { + "count": 0, + "endOffset": 42677, + "startOffset": 42658 + }, + { + "count": 0, + "endOffset": 42864, + "startOffset": 42679 + }, + { + "count": 18, + "endOffset": 42883, + "startOffset": 42868 + }, + { + "count": 4, + "endOffset": 42906, + "startOffset": 42883 + }, + { + "count": 18, + "endOffset": 42950, + "startOffset": 42906 + } + ] + } + ], + "scriptId": "0", + "url": "fs.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "realpathSync", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 42951, + "startOffset": 39013 + }, + { + "count": 15, + "endOffset": 39088, + "startOffset": 39069 + }, + { + "count": 5, + "endOffset": 39140, + "startOffset": 39088 + }, + { + "count": 0, + "endOffset": 39214, + "startOffset": 39196 + }, + { + "count": 5, + "endOffset": 39356, + "startOffset": 39341 + }, + { + "count": 1, + "endOffset": 39418, + "startOffset": 39383 + }, + { + "count": 19, + "endOffset": 39991, + "startOffset": 39418 + }, + { + "count": 0, + "endOffset": 40010, + "startOffset": 39991 + }, + { + "count": 0, + "endOffset": 40187, + "startOffset": 40012 + }, + { + "count": 19, + "endOffset": 40324, + "startOffset": 40187 + }, + { + "count": 427, + "endOffset": 42868, + "startOffset": 40324 + }, + { + "count": 34, + "endOffset": 40551, + "startOffset": 40436 + }, + { + "count": 393, + "endOffset": 40677, + "startOffset": 40551 + }, + { + "count": 187, + "endOffset": 40798, + "startOffset": 40760 + }, + { + "count": 28, + "endOffset": 40797, + "startOffset": 40770 + }, + { + "count": 257, + "endOffset": 40937, + "startOffset": 40800 + }, + { + "count": 0, + "endOffset": 40915, + "startOffset": 40891 + }, + { + "count": 170, + "endOffset": 40999, + "startOffset": 40937 + }, + { + "count": 11, + "endOffset": 41017, + "startOffset": 40999 + }, + { + "count": 0, + "endOffset": 41097, + "startOffset": 41048 + }, + { + "count": 170, + "endOffset": 42382, + "startOffset": 41097 + }, + { + "count": 135, + "endOffset": 41551, + "startOffset": 41450 + }, + { + "count": 11, + "endOffset": 41525, + "startOffset": 41503 + }, + { + "count": 35, + "endOffset": 41932, + "startOffset": 41551 + }, + { + "count": 13, + "endOffset": 41924, + "startOffset": 41875 + }, + { + "count": 35, + "endOffset": 41964, + "startOffset": 41932 + }, + { + "count": 22, + "endOffset": 42214, + "startOffset": 41964 + }, + { + "count": 34, + "endOffset": 42296, + "startOffset": 42214 + }, + { + "count": 0, + "endOffset": 42326, + "startOffset": 42296 + }, + { + "count": 34, + "endOffset": 42376, + "startOffset": 42326 + }, + { + "count": 34, + "endOffset": 42658, + "startOffset": 42382 + }, + { + "count": 0, + "endOffset": 42677, + "startOffset": 42658 + }, + { + "count": 0, + "endOffset": 42864, + "startOffset": 42679 + }, + { + "count": 18, + "endOffset": 42883, + "startOffset": 42868 + }, + { + "count": 4, + "endOffset": 42906, + "startOffset": 42883 + }, + { + "count": 18, + "endOffset": 42950, + "startOffset": 42906 + } + ] + } + ], + "scriptId": "48", + "url": "fs.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "realpathSync", + "isBlockCoverage": false, + "ranges": [ + { + "count": 3, + "endOffset": 42951, + "startOffset": 39013 + } + ] + } + ], + "scriptId": "48", + "url": "fs.js" + } + ] + } + ], + "name": "Issue 2: Mixed isBlockCoverage (https://github.com/demurgos/v8-coverage/issues/2)", + "status": "run" + } + ], + "test_coverage_merge_node_10_internal_errors_one_of_test.json": [ + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2516, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 719, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 718, + "endOffset": 148299, + "startOffset": 147892 + }, + { + "count": 537, + "endOffset": 148155, + "startOffset": 147892 + }, + { + "count": 530, + "endOffset": 148061, + "startOffset": 147892 + }, + { + "count": 2, + "endOffset": 148061, + "startOffset": 148055 + }, + { + "count": 2, + "endOffset": 148155, + "startOffset": 148061 + }, + { + "count": 188, + "endOffset": 148299, + "startOffset": 148155 + }, + { + "count": 2, + "endOffset": 148299, + "startOffset": 148291 + }, + { + "count": 38, + "endOffset": 148437, + "startOffset": 148299 + }, + { + "count": 5, + "endOffset": 148433, + "startOffset": 148299 + }, + { + "count": 4, + "endOffset": 148427, + "startOffset": 148299 + }, + { + "count": 2, + "endOffset": 148433, + "startOffset": 148427 + }, + { + "count": 2, + "endOffset": 148437, + "startOffset": 148433 + }, + { + "count": 2019, + "endOffset": 148555, + "startOffset": 148437 + }, + { + "count": 1799, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 2, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 148556, + "startOffset": 147273 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1453, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 4, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 1, + "endOffset": 148061, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148061, + "startOffset": 148055 + }, + { + "count": 2, + "endOffset": 148433, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148155, + "startOffset": 148061 + }, + { + "count": 1, + "endOffset": 148299, + "startOffset": 148155 + }, + { + "count": 0, + "endOffset": 148299, + "startOffset": 148291 + }, + { + "count": 1, + "endOffset": 148427, + "startOffset": 148299 + }, + { + "count": 0, + "endOffset": 148433, + "startOffset": 148427 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 148433 + }, + { + "count": 1, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 28, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 2, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 1, + "endOffset": 148061, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148061, + "startOffset": 148055 + }, + { + "count": 1, + "endOffset": 148433, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148155, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148433, + "startOffset": 148291 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 148433 + }, + { + "count": 26, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 1, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 0, + "endOffset": 148299, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 148427 + }, + { + "count": 1, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 173, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 33, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 0, + "endOffset": 148155, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 148291 + }, + { + "count": 140, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 313, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 154, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 148055 + }, + { + "count": 159, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 43, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 26, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 24, + "endOffset": 148061, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148061, + "startOffset": 148055 + }, + { + "count": 2, + "endOffset": 148433, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148155, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148433, + "startOffset": 148291 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 148433 + }, + { + "count": 17, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 148, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 0, + "endOffset": 148155, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148291 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 2, + "endOffset": 148061, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148061, + "startOffset": 148055 + }, + { + "count": 1, + "endOffset": 148433, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148155, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148433, + "startOffset": 148291 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148433 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 346, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148055 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "node@10.11.0: internal/errors.js#oneOf", + "status": "run" + } + ], + "test_coverage_merge_reduced_test.json": [ + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1962, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 332, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 331, + "endOffset": 4, + "startOffset": 2 + }, + { + "count": 329, + "endOffset": 4, + "startOffset": 3 + }, + { + "count": 30, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 4 + }, + { + "count": 29, + "endOffset": 6, + "startOffset": 5 + }, + { + "count": 1814, + "endOffset": 7, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1811, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 181, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 180, + "endOffset": 4, + "startOffset": 2 + }, + { + "count": 27, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 4 + }, + { + "count": 26, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 148, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 0, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "node@10.11.0: internal/errors.js#oneOf: reduced", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1962, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 332, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 331, + "endOffset": 4, + "startOffset": 2 + }, + { + "count": 329, + "endOffset": 4, + "startOffset": 3 + }, + { + "count": 30, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 4 + }, + { + "count": 29, + "endOffset": 6, + "startOffset": 5 + }, + { + "count": 1814, + "endOffset": 7, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1811, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 181, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 180, + "endOffset": 4, + "startOffset": 2 + }, + { + "count": 27, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 4 + }, + { + "count": 26, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 148, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 0, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "node@10.11.0: internal/errors.js#oneOf: reduced with hint", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 66, + "endOffset": 4, + "startOffset": 2 + }, + { + "count": 30, + "endOffset": 4, + "startOffset": 3 + }, + { + "count": 43, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 4 + }, + { + "count": 52, + "endOffset": 7, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 6, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "node@10.11.0: internal/errors.js#oneOf: minimal", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 66, + "endOffset": 4, + "startOffset": 2 + }, + { + "count": 30, + "endOffset": 4, + "startOffset": 3 + }, + { + "count": 43, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 4 + }, + { + "count": 52, + "endOffset": 7, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 6, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 40, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "node@10.11.0: internal/errors.js#oneOf: minimal with hint", + "status": "run" + } + ], + "test_coverage_merge_simple_test.json": [ + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "No children", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "One child", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Equal", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 3, + "startOffset": 1 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 3, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (BeforeAdjacent)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 2, + "startOffset": 1 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 2, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (BeforeStrict)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 12, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (AfterAdjacent)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 12, + "endOffset": 8, + "startOffset": 7 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 7 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (AfterStrict)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Nested (OverlapStrict)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 6, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Nested (OverlapEqualEnd)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 8, + "startOffset": 3 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Nested (OverlapEqualStart)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Nested (ContainedEqualStart)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 6, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Nested (ContainedEqualEnd)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Nested (ContainedStrict)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Partial overlap (PartialOverlapStart)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 12, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Partial overlap (PartialOverlapEnd)", + "status": "run" + } + ], + "test_coverage_merge_various_test.json": [ + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three empty trees", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 3, + "endOffset": 3, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 3, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 3, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Equal children, same startOffset as parents", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 3, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Equal children, same endOffset as parents", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 3, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 3, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (strict) children, offsets matching parents (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 3, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 3, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (strict) children, offsets matching parents (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 4, + "startOffset": 1 + }, + { + "count": 12, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (adjacent) children (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 4, + "startOffset": 1 + }, + { + "count": 12, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (adjacent) children (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 2 left & 1 right (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 2 left & 1 right (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 2 left & 1 right (permutation 2)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 2 left & 1 right (permutation 3)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 2 left & 1 right (permutation 4)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 2 left & 1 right (permutation 5)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 7, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 16, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 1 left & 2 right (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 7, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 16, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 1 left & 2 right (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 7, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 16, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 1 left & 2 right (permutation 2)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 7, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 16, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 1 left & 2 right (permutation 3)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 7, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 16, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 1 left & 2 right (permutation 4)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 7, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 16, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 1 left & 2 right (permutation 5)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 13, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 10, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 9, + "endOffset": 8, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 6, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 7, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 8, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Normalization: two trees with complementary children (not summing to the same count)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 14, + "endOffset": 8, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 6, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 5, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 8, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 8, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 9, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 8, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Normalization: two trees with complementary children (summing to the same count, same as parent)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 10, + "endOffset": 8, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 6, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 7, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Normalization: two trees with complementary children (summing to the same count, different from parent)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 5, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 4, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: a+b", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 5, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 4, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: b+a", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 6, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: a+c", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: b+c", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 5, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 4, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: (a+b)+c", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 6, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: (a+c)+b", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: a+(b+c)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: a+b+c", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 40, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 31, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 22, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 13, + "endOffset": 5, + "startOffset": 2 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 7, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: a+b+c+d", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 4, + "startOffset": 0 + }, + { + "count": 3, + "endOffset": 4, + "startOffset": 1 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a tagged sliding chain: a+b", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 4, + "startOffset": 0 + }, + { + "count": 3, + "endOffset": 4, + "startOffset": 1 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a tagged sliding chain: a+b", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 15, + "endOffset": 16, + "startOffset": 0 + }, + { + "count": 20, + "endOffset": 4, + "startOffset": 0 + }, + { + "count": 18, + "endOffset": 1, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 2, + "startOffset": 1 + }, + { + "count": 17, + "endOffset": 4, + "startOffset": 3 + }, + { + "count": 12, + "endOffset": 11, + "startOffset": 4 + }, + { + "count": 13, + "endOffset": 8, + "startOffset": 5 + }, + { + "count": 16, + "endOffset": 7, + "startOffset": 6 + }, + { + "count": 9, + "endOffset": 8, + "startOffset": 7 + }, + { + "count": 11, + "endOffset": 11, + "startOffset": 9 + }, + { + "count": 19, + "endOffset": 11, + "startOffset": 10 + }, + { + "count": 23, + "endOffset": 15, + "startOffset": 11 + }, + { + "count": 22, + "endOffset": 12, + "startOffset": 11 + }, + { + "count": 16, + "endOffset": 14, + "startOffset": 13 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 16, + "startOffset": 0 + }, + { + "count": 6, + "endOffset": 4, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 2, + "startOffset": 1 + }, + { + "count": 4, + "endOffset": 7, + "startOffset": 6 + }, + { + "count": 9, + "endOffset": 15, + "startOffset": 10 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 16, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 1, + "startOffset": 0 + }, + { + "count": 5, + "endOffset": 8, + "startOffset": 5 + }, + { + "count": 3, + "endOffset": 12, + "startOffset": 9 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 16, + "startOffset": 0 + }, + { + "count": 7, + "endOffset": 11, + "startOffset": 3 + }, + { + "count": 3, + "endOffset": 8, + "startOffset": 7 + }, + { + "count": 3, + "endOffset": 14, + "startOffset": 13 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges three trees with a complex relation (chains, nesting)", + "status": "run" + } + ], + "test_v8_coverage_node_sqlite_13216_1633662333140_0.json": { + "result": [ + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 20604, + "startOffset": 0 + } + ] + }, + { + "functionName": "Console", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4925, + "startOffset": 2686 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5200, + "startOffset": 5144 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5669, + "startOffset": 5458 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 6489, + "startOffset": 5878 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 6165, + "startOffset": 6067 + }, + { + "count": 1, + "endOffset": 6124, + "startOffset": 6101 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6208, + "startOffset": 6178 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6417, + "startOffset": 6315 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6460, + "startOffset": 6430 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 7776, + "startOffset": 6563 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9334, + "startOffset": 7850 + }, + { + "count": 0, + "endOffset": 8095, + "startOffset": 8081 + }, + { + "count": 0, + "endOffset": 8197, + "startOffset": 8171 + }, + { + "count": 0, + "endOffset": 8432, + "startOffset": 8238 + }, + { + "count": 0, + "endOffset": 8521, + "startOffset": 8493 + }, + { + "count": 0, + "endOffset": 9261, + "startOffset": 8971 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9918, + "startOffset": 9411 + }, + { + "count": 0, + "endOffset": 9635, + "startOffset": 9629 + }, + { + "count": 0, + "endOffset": 9840, + "startOffset": 9714 + }, + { + "count": 0, + "endOffset": 9910, + "startOffset": 9886 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 10124, + "startOffset": 9993 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10330, + "startOffset": 10199 + } + ] + }, + { + "functionName": "createWriteErrorHandler", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 11331, + "startOffset": 10424 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 11327, + "startOffset": 10493 + }, + { + "count": 0, + "endOffset": 10786, + "startOffset": 10768 + } + ] + }, + { + "functionName": "log", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 11452, + "startOffset": 11363 + } + ] + }, + { + "functionName": "warn", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11551, + "startOffset": 11461 + } + ] + }, + { + "functionName": "dir", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11751, + "startOffset": 11560 + } + ] + }, + { + "functionName": "time", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12125, + "startOffset": 11758 + } + ] + }, + { + "functionName": "timeEnd", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12429, + "startOffset": 12132 + } + ] + }, + { + "functionName": "timeLog", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12679, + "startOffset": 12436 + } + ] + }, + { + "functionName": "trace", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12886, + "startOffset": 12693 + } + ] + }, + { + "functionName": "assert", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 13116, + "startOffset": 12893 + } + ] + }, + { + "functionName": "clear", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 13620, + "startOffset": 13180 + } + ] + }, + { + "functionName": "count", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14153, + "startOffset": 13684 + } + ] + }, + { + "functionName": "countReset", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14518, + "startOffset": 14222 + } + ] + }, + { + "functionName": "group", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14699, + "startOffset": 14525 + } + ] + }, + { + "functionName": "groupEnd", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14880, + "startOffset": 14706 + } + ] + }, + { + "functionName": "table", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18454, + "startOffset": 14932 + } + ] + }, + { + "functionName": "timeLogImpl", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19012, + "startOffset": 18499 + } + ] + }, + { + "functionName": "pad", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19095, + "startOffset": 19016 + } + ] + }, + { + "functionName": "formatTime", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19892, + "startOffset": 19099 + } + ] + }, + { + "functionName": "isArray", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20089, + "startOffset": 20033 + } + ] + }, + { + "functionName": "noop", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20112, + "startOffset": 20094 + } + ] + } + ], + "scriptId": "28", + "url": "internal/console/constructor.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1638, + "startOffset": 0 + } + ] + } + ], + "scriptId": "29", + "url": "internal/constants.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 2283, + "startOffset": 0 + } + ] + }, + { + "functionName": "sendInspectorCommand", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 453, + "startOffset": 100 + } + ] + }, + { + "functionName": "installConsoleExtensions", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1094, + "startOffset": 530 + } + ] + }, + { + "functionName": "wrapConsole", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 1984, + "startOffset": 1176 + }, + { + "count": 23, + "endOffset": 1981, + "startOffset": 1336 + }, + { + "count": 19, + "endOffset": 1855, + "startOffset": 1555 + }, + { + "count": 4, + "endOffset": 1976, + "startOffset": 1855 + } + ] + }, + { + "functionName": "get consoleFromVM", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2217, + "startOffset": 2164 + } + ] + }, + { + "functionName": "set consoleFromVM", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 2277, + "startOffset": 2222 + } + ] + } + ], + "scriptId": "30", + "url": "internal/util/inspector.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 42795, + "startOffset": 0 + } + ] + }, + { + "functionName": "toUSVString", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2627, + "startOffset": 2323 + } + ] + }, + { + "functionName": "serializeTupleOrigin", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2965, + "startOffset": 2845 + } + ] + }, + { + "functionName": "URLContext", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 3611, + "startOffset": 3378 + } + ] + }, + { + "functionName": "URLSearchParams", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 6333, + "startOffset": 3909 + }, + { + "count": 0, + "endOffset": 6267, + "startOffset": 4027 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7439, + "startOffset": 6339 + } + ] + }, + { + "functionName": "onParseComplete", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 8156, + "startOffset": 7446 + }, + { + "count": 0, + "endOffset": 7716, + "startOffset": 7706 + }, + { + "count": 0, + "endOffset": 7790, + "startOffset": 7780 + }, + { + "count": 0, + "endOffset": 7877, + "startOffset": 7873 + } + ] + }, + { + "functionName": "onParseError", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8237, + "startOffset": 8160 + } + ] + }, + { + "functionName": "onParseProtocolComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8596, + "startOffset": 8241 + } + ] + }, + { + "functionName": "onParseHostnameComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8956, + "startOffset": 8600 + } + ] + }, + { + "functionName": "onParsePortComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9125, + "startOffset": 8960 + } + ] + }, + { + "functionName": "onParseHostComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9440, + "startOffset": 9129 + } + ] + }, + { + "functionName": "onParsePathComplete", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 9954, + "startOffset": 9444 + }, + { + "count": 0, + "endOffset": 9787, + "startOffset": 9716 + } + ] + }, + { + "functionName": "onParseSearchComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10129, + "startOffset": 9958 + } + ] + }, + { + "functionName": "onParseHashComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10306, + "startOffset": 10133 + } + ] + }, + { + "functionName": "URL", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 10663, + "startOffset": 10325 + }, + { + "count": 0, + "endOffset": 10518, + "startOffset": 10464 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10752, + "startOffset": 10669 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 327, + "endOffset": 10853, + "startOffset": 10758 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11136, + "startOffset": 10931 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12107, + "startOffset": 11142 + } + ] + }, + { + "functionName": "format", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 13502, + "startOffset": 12284 + }, + { + "count": 0, + "endOffset": 12432, + "startOffset": 12371 + }, + { + "count": 0, + "endOffset": 13023, + "startOffset": 12856 + }, + { + "count": 0, + "endOffset": 13094, + "startOffset": 13056 + }, + { + "count": 0, + "endOffset": 13172, + "startOffset": 13150 + }, + { + "count": 0, + "endOffset": 13247, + "startOffset": 13181 + }, + { + "count": 0, + "endOffset": 13386, + "startOffset": 13363 + }, + { + "count": 0, + "endOffset": 13476, + "startOffset": 13450 + } + ] + }, + { + "functionName": "toString", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 13849, + "startOffset": 13788 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 13967, + "startOffset": 13920 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14159, + "startOffset": 13974 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14895, + "startOffset": 14245 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15020, + "startOffset": 14970 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15408, + "startOffset": 15027 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15535, + "startOffset": 15483 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15953, + "startOffset": 15542 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16080, + "startOffset": 16028 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16498, + "startOffset": 16087 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16731, + "startOffset": 16569 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17038, + "startOffset": 16738 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17167, + "startOffset": 17113 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17482, + "startOffset": 17174 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17656, + "startOffset": 17553 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17991, + "startOffset": 17663 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": true, + "ranges": [ + { + "count": 218, + "endOffset": 18268, + "startOffset": 18066 + }, + { + "count": 0, + "endOffset": 18167, + "startOffset": 18148 + }, + { + "count": 0, + "endOffset": 18221, + "startOffset": 18211 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 18501, + "startOffset": 18275 + }, + { + "count": 0, + "endOffset": 18396, + "startOffset": 18389 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18718, + "startOffset": 18574 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19232, + "startOffset": 18725 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19372, + "startOffset": 19324 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19599, + "startOffset": 19443 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20037, + "startOffset": 19606 + } + ] + }, + { + "functionName": "toJSON", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20249, + "startOffset": 20190 + } + ] + }, + { + "functionName": "update", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20581, + "startOffset": 20263 + } + ] + }, + { + "functionName": "initSearchParams", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 20731, + "startOffset": 20585 + }, + { + "count": 0, + "endOffset": 20730, + "startOffset": 20686 + } + ] + }, + { + "functionName": "parseParams", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 23241, + "startOffset": 20844 + } + ] + }, + { + "functionName": "serializeParams", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 24820, + "startOffset": 24244 + } + ] + }, + { + "functionName": "defineIDLClass", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 25592, + "startOffset": 24878 + }, + { + "count": 13, + "endOffset": 25379, + "startOffset": 25226 + }, + { + "count": 1, + "endOffset": 25589, + "startOffset": 25435 + } + ] + }, + { + "functionName": "merge", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 26271, + "startOffset": 25615 + } + ] + }, + { + "functionName": "append", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 26740, + "startOffset": 26341 + } + ] + }, + { + "functionName": "delete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27265, + "startOffset": 26747 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27722, + "startOffset": 27272 + } + ] + }, + { + "functionName": "getAll", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 28214, + "startOffset": 27729 + } + ] + }, + { + "functionName": "has", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 28665, + "startOffset": 28221 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 29736, + "startOffset": 28672 + } + ] + }, + { + "functionName": "sort", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 30962, + "startOffset": 29743 + } + ] + }, + { + "functionName": "entries", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31345, + "startOffset": 31130 + } + ] + }, + { + "functionName": "forEach", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31943, + "startOffset": 31352 + } + ] + }, + { + "functionName": "keys", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 32207, + "startOffset": 32001 + } + ] + }, + { + "functionName": "values", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 32424, + "startOffset": 32214 + } + ] + }, + { + "functionName": "toString", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 32767, + "startOffset": 32561 + } + ] + }, + { + "functionName": "createSearchParamsIterator", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33269, + "startOffset": 33058 + } + ] + }, + { + "functionName": "next", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 34289, + "startOffset": 33499 + } + ] + }, + { + "functionName": "defineIDLClass", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 35544, + "startOffset": 34294 + } + ] + }, + { + "functionName": "domainToASCII", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 35734, + "startOffset": 35553 + } + ] + }, + { + "functionName": "domainToUnicode", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 35923, + "startOffset": 35738 + } + ] + }, + { + "functionName": "urlToOptions", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36633, + "startOffset": 36071 + } + ] + }, + { + "functionName": "getPathFromURLWin32", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 38099, + "startOffset": 36673 + } + ] + }, + { + "functionName": "getPathFromURLPosix", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 38623, + "startOffset": 38103 + } + ] + }, + { + "functionName": "fileURLToPath", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 38982, + "startOffset": 38627 + } + ] + }, + { + "functionName": "encodePathChars", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 40330, + "startOffset": 39761 + }, + { + "count": 0, + "endOffset": 39883, + "startOffset": 39834 + }, + { + "count": 0, + "endOffset": 39985, + "startOffset": 39959 + }, + { + "count": 0, + "endOffset": 40043, + "startOffset": 39992 + }, + { + "count": 0, + "endOffset": 40130, + "startOffset": 40081 + }, + { + "count": 0, + "endOffset": 40224, + "startOffset": 40168 + }, + { + "count": 0, + "endOffset": 40307, + "startOffset": 40262 + } + ] + }, + { + "functionName": "pathToFileURL", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 41482, + "startOffset": 40334 + }, + { + "count": 0, + "endOffset": 41025, + "startOffset": 40456 + }, + { + "count": 0, + "endOffset": 41381, + "startOffset": 41327 + }, + { + "count": 0, + "endOffset": 41406, + "startOffset": 41390 + } + ] + }, + { + "functionName": "isURLInstance", + "isBlockCoverage": true, + "ranges": [ + { + "count": 277, + "endOffset": 41607, + "startOffset": 41486 + }, + { + "count": 0, + "endOffset": 41603, + "startOffset": 41580 + } + ] + }, + { + "functionName": "toPathIfFileURL", + "isBlockCoverage": true, + "ranges": [ + { + "count": 277, + "endOffset": 41760, + "startOffset": 41611 + }, + { + "count": 0, + "endOffset": 41759, + "startOffset": 41717 + } + ] + }, + { + "functionName": "constructUrl", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 42484, + "startOffset": 41764 + } + ] + } + ], + "scriptId": "31", + "url": "internal/url.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 3134, + "startOffset": 0 + } + ] + }, + { + "functionName": "encodeStr", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3065, + "startOffset": 1374 + } + ] + } + ], + "scriptId": "32", + "url": "internal/querystring.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 44873, + "startOffset": 0 + } + ] + }, + { + "functionName": "isPathSeparator", + "isBlockCoverage": true, + "ranges": [ + { + "count": 144876, + "endOffset": 1635, + "startOffset": 1529 + }, + { + "count": 144646, + "endOffset": 1631, + "startOffset": 1600 + } + ] + }, + { + "functionName": "isPosixPathSeparator", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1718, + "startOffset": 1639 + } + ] + }, + { + "functionName": "isWindowsDeviceRoot", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5087, + "endOffset": 1895, + "startOffset": 1722 + }, + { + "count": 4801, + "endOffset": 1822, + "startOffset": 1795 + }, + { + "count": 1027, + "endOffset": 1891, + "startOffset": 1824 + }, + { + "count": 741, + "endOffset": 1890, + "startOffset": 1863 + } + ] + }, + { + "functionName": "normalizeString", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2284, + "endOffset": 3826, + "startOffset": 1961 + }, + { + "count": 131113, + "endOffset": 3808, + "startOffset": 2184 + }, + { + "count": 128829, + "endOffset": 2245, + "startOffset": 2219 + }, + { + "count": 2284, + "endOffset": 2340, + "startOffset": 2245 + }, + { + "count": 2279, + "endOffset": 2296, + "startOffset": 2290 + }, + { + "count": 5, + "endOffset": 2340, + "startOffset": 2296 + }, + { + "count": 128834, + "endOffset": 2375, + "startOffset": 2340 + }, + { + "count": 12268, + "endOffset": 3704, + "startOffset": 2375 + }, + { + "count": 11644, + "endOffset": 2421, + "startOffset": 2408 + }, + { + "count": 724, + "endOffset": 2450, + "startOffset": 2423 + }, + { + "count": 11544, + "endOffset": 3658, + "startOffset": 2450 + }, + { + "count": 31, + "endOffset": 3439, + "startOffset": 2472 + }, + { + "count": 0, + "endOffset": 2588, + "startOffset": 2529 + }, + { + "count": 0, + "endOffset": 2648, + "startOffset": 2589 + }, + { + "count": 0, + "endOffset": 2868, + "startOffset": 2789 + }, + { + "count": 0, + "endOffset": 3283, + "startOffset": 3108 + }, + { + "count": 0, + "endOffset": 3430, + "startOffset": 3294 + }, + { + "count": 11513, + "endOffset": 3658, + "startOffset": 3439 + }, + { + "count": 9341, + "endOffset": 3540, + "startOffset": 3487 + }, + { + "count": 2172, + "endOffset": 3601, + "startOffset": 3540 + }, + { + "count": 12237, + "endOffset": 3704, + "startOffset": 3658 + }, + { + "count": 116566, + "endOffset": 3803, + "startOffset": 3704 + }, + { + "count": 1642, + "endOffset": 3746, + "startOffset": 3732 + }, + { + "count": 165, + "endOffset": 3771, + "startOffset": 3748 + }, + { + "count": 116401, + "endOffset": 3803, + "startOffset": 3771 + } + ] + }, + { + "functionName": "_format", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4453, + "startOffset": 4017 + } + ] + }, + { + "functionName": "resolve", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2279, + "endOffset": 9115, + "startOffset": 4579 + }, + { + "count": 3106, + "endOffset": 8648, + "startOffset": 4742 + }, + { + "count": 3105, + "endOffset": 4953, + "startOffset": 4780 + }, + { + "count": 0, + "endOffset": 4944, + "startOffset": 4911 + }, + { + "count": 1, + "endOffset": 5845, + "startOffset": 4953 + }, + { + "count": 0, + "endOffset": 5845, + "startOffset": 5033 + }, + { + "count": 0, + "endOffset": 6218, + "startOffset": 6053 + }, + { + "count": 0, + "endOffset": 7598, + "startOffset": 6251 + }, + { + "count": 2975, + "endOffset": 7689, + "startOffset": 7634 + }, + { + "count": 2279, + "endOffset": 8015, + "startOffset": 7691 + }, + { + "count": 2279, + "endOffset": 8333, + "startOffset": 8048 + }, + { + "count": 0, + "endOffset": 8270, + "startOffset": 8090 + }, + { + "count": 0, + "endOffset": 8433, + "startOffset": 8365 + }, + { + "count": 2279, + "endOffset": 8600, + "startOffset": 8572 + }, + { + "count": 2279, + "endOffset": 8632, + "startOffset": 8602 + }, + { + "count": 0, + "endOffset": 9109, + "startOffset": 9059 + } + ] + }, + { + "functionName": "normalize", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 12023, + "startOffset": 9187 + }, + { + "count": 0, + "endOffset": 9308, + "startOffset": 9297 + }, + { + "count": 0, + "endOffset": 9622, + "startOffset": 9465 + }, + { + "count": 0, + "endOffset": 11204, + "startOffset": 9655 + }, + { + "count": 4, + "endOffset": 11582, + "startOffset": 11278 + }, + { + "count": 0, + "endOffset": 11708, + "startOffset": 11697 + }, + { + "count": 0, + "endOffset": 11751, + "startOffset": 11737 + }, + { + "count": 0, + "endOffset": 11771, + "startOffset": 11760 + }, + { + "count": 0, + "endOffset": 11863, + "startOffset": 11850 + }, + { + "count": 1, + "endOffset": 11950, + "startOffset": 11895 + }, + { + "count": 0, + "endOffset": 11935, + "startOffset": 11922 + }, + { + "count": 4, + "endOffset": 11996, + "startOffset": 11950 + }, + { + "count": 0, + "endOffset": 12017, + "startOffset": 11997 + } + ] + }, + { + "functionName": "isAbsolute", + "isBlockCoverage": true, + "ranges": [ + { + "count": 312, + "endOffset": 12477, + "startOffset": 12096 + }, + { + "count": 0, + "endOffset": 12220, + "startOffset": 12207 + }, + { + "count": 311, + "endOffset": 12380, + "startOffset": 12345 + }, + { + "count": 156, + "endOffset": 12424, + "startOffset": 12381 + }, + { + "count": 112, + "endOffset": 12470, + "startOffset": 12425 + } + ] + }, + { + "functionName": "join", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 14671, + "startOffset": 12552 + }, + { + "count": 0, + "endOffset": 12614, + "startOffset": 12603 + }, + { + "count": 11, + "endOffset": 12926, + "startOffset": 12697 + }, + { + "count": 5, + "endOffset": 12863, + "startOffset": 12838 + }, + { + "count": 6, + "endOffset": 12910, + "startOffset": 12863 + }, + { + "count": 0, + "endOffset": 12978, + "startOffset": 12967 + }, + { + "count": 0, + "endOffset": 14271, + "startOffset": 13851 + }, + { + "count": 0, + "endOffset": 14499, + "startOffset": 14466 + }, + { + "count": 0, + "endOffset": 14620, + "startOffset": 14579 + } + ] + }, + { + "functionName": "relative", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18564, + "startOffset": 14979 + } + ] + }, + { + "functionName": "toNamespacedPath", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1064, + "endOffset": 19630, + "startOffset": 18571 + }, + { + "count": 0, + "endOffset": 18702, + "startOffset": 18690 + }, + { + "count": 0, + "endOffset": 18759, + "startOffset": 18733 + }, + { + "count": 0, + "endOffset": 18865, + "startOffset": 18853 + }, + { + "count": 0, + "endOffset": 19300, + "startOffset": 18929 + }, + { + "count": 0, + "endOffset": 19629, + "startOffset": 19605 + } + ] + }, + { + "functionName": "dirname", + "isBlockCoverage": true, + "ranges": [ + { + "count": 489, + "endOffset": 22114, + "startOffset": 19702 + }, + { + "count": 0, + "endOffset": 19821, + "startOffset": 19810 + }, + { + "count": 0, + "endOffset": 20090, + "startOffset": 19926 + }, + { + "count": 0, + "endOffset": 21455, + "startOffset": 20153 + }, + { + "count": 0, + "endOffset": 21602, + "startOffset": 21599 + }, + { + "count": 5638, + "endOffset": 21973, + "startOffset": 21732 + }, + { + "count": 489, + "endOffset": 21871, + "startOffset": 21782 + }, + { + "count": 5149, + "endOffset": 21966, + "startOffset": 21871 + }, + { + "count": 0, + "endOffset": 22077, + "startOffset": 21997 + } + ] + }, + { + "functionName": "basename", + "isBlockCoverage": true, + "ranges": [ + { + "count": 112, + "endOffset": 24930, + "startOffset": 22214 + }, + { + "count": 0, + "endOffset": 22298, + "startOffset": 22271 + }, + { + "count": 0, + "endOffset": 22778, + "startOffset": 22761 + }, + { + "count": 0, + "endOffset": 22807, + "startOffset": 22779 + }, + { + "count": 0, + "endOffset": 24318, + "startOffset": 22809 + }, + { + "count": 1309, + "endOffset": 24848, + "startOffset": 24371 + }, + { + "count": 112, + "endOffset": 24652, + "startOffset": 24421 + }, + { + "count": 1197, + "endOffset": 24841, + "startOffset": 24652 + }, + { + "count": 112, + "endOffset": 24841, + "startOffset": 24674 + }, + { + "count": 0, + "endOffset": 24889, + "startOffset": 24879 + } + ] + }, + { + "functionName": "extname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27073, + "startOffset": 25002 + } + ] + }, + { + "functionName": "parse", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31903, + "startOffset": 27291 + } + ] + }, + { + "functionName": "resolve", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33025, + "startOffset": 32100 + } + ] + }, + { + "functionName": "normalize", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33694, + "startOffset": 33097 + } + ] + }, + { + "functionName": "isAbsolute", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33899, + "startOffset": 33767 + } + ] + }, + { + "functionName": "join", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 34405, + "startOffset": 33974 + } + ] + }, + { + "functionName": "relative", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36780, + "startOffset": 34502 + } + ] + }, + { + "functionName": "toNamespacedPath", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36866, + "startOffset": 36787 + } + ] + }, + { + "functionName": "dirname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 37590, + "startOffset": 36938 + } + ] + }, + { + "functionName": "basename", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 40083, + "startOffset": 37690 + } + ] + }, + { + "functionName": "extname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 41863, + "startOffset": 40155 + } + ] + }, + { + "functionName": "parse", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 44518, + "startOffset": 42086 + } + ] + } + ], + "scriptId": "33", + "url": "path.js" + } + ], + "timestamp": 967695.999409 + }, + "test_v8_coverage_node_sqlite_9884_1633662346346_0.json": { + "result": [ + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 3134, + "startOffset": 0 + } + ] + }, + { + "functionName": "encodeStr", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3065, + "startOffset": 1374 + } + ] + } + ], + "scriptId": "32", + "url": "internal/querystring.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 44873, + "startOffset": 0 + } + ] + }, + { + "functionName": "isPathSeparator", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2111, + "endOffset": 1635, + "startOffset": 1529 + }, + { + "count": 2108, + "endOffset": 1631, + "startOffset": 1600 + } + ] + }, + { + "functionName": "isPosixPathSeparator", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1718, + "startOffset": 1639 + } + ] + }, + { + "functionName": "isWindowsDeviceRoot", + "isBlockCoverage": true, + "ranges": [ + { + "count": 86, + "endOffset": 1895, + "startOffset": 1722 + }, + { + "count": 81, + "endOffset": 1822, + "startOffset": 1795 + }, + { + "count": 14, + "endOffset": 1891, + "startOffset": 1824 + }, + { + "count": 9, + "endOffset": 1890, + "startOffset": 1863 + } + ] + }, + { + "functionName": "normalizeString", + "isBlockCoverage": true, + "ranges": [ + { + "count": 41, + "endOffset": 3826, + "startOffset": 1961 + }, + { + "count": 1915, + "endOffset": 3808, + "startOffset": 2184 + }, + { + "count": 1874, + "endOffset": 2245, + "startOffset": 2219 + }, + { + "count": 41, + "endOffset": 2340, + "startOffset": 2245 + }, + { + "count": 40, + "endOffset": 2296, + "startOffset": 2290 + }, + { + "count": 1, + "endOffset": 2340, + "startOffset": 2296 + }, + { + "count": 1875, + "endOffset": 2375, + "startOffset": 2340 + }, + { + "count": 209, + "endOffset": 3704, + "startOffset": 2375 + }, + { + "count": 200, + "endOffset": 2421, + "startOffset": 2408 + }, + { + "count": 10, + "endOffset": 2450, + "startOffset": 2423 + }, + { + "count": 199, + "endOffset": 3658, + "startOffset": 2450 + }, + { + "count": 2, + "endOffset": 3439, + "startOffset": 2472 + }, + { + "count": 0, + "endOffset": 2588, + "startOffset": 2529 + }, + { + "count": 0, + "endOffset": 2648, + "startOffset": 2589 + }, + { + "count": 0, + "endOffset": 2868, + "startOffset": 2789 + }, + { + "count": 0, + "endOffset": 3283, + "startOffset": 3108 + }, + { + "count": 0, + "endOffset": 3430, + "startOffset": 3294 + }, + { + "count": 197, + "endOffset": 3658, + "startOffset": 3439 + }, + { + "count": 158, + "endOffset": 3540, + "startOffset": 3487 + }, + { + "count": 39, + "endOffset": 3601, + "startOffset": 3540 + }, + { + "count": 207, + "endOffset": 3704, + "startOffset": 3658 + }, + { + "count": 1666, + "endOffset": 3803, + "startOffset": 3704 + }, + { + "count": 24, + "endOffset": 3746, + "startOffset": 3732 + }, + { + "count": 9, + "endOffset": 3771, + "startOffset": 3748 + }, + { + "count": 1657, + "endOffset": 3803, + "startOffset": 3771 + } + ] + }, + { + "functionName": "_format", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4453, + "startOffset": 4017 + } + ] + }, + { + "functionName": "resolve", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9115, + "startOffset": 4579 + }, + { + "count": 53, + "endOffset": 8648, + "startOffset": 4742 + }, + { + "count": 52, + "endOffset": 4953, + "startOffset": 4780 + }, + { + "count": 0, + "endOffset": 4944, + "startOffset": 4911 + }, + { + "count": 1, + "endOffset": 5845, + "startOffset": 4953 + }, + { + "count": 0, + "endOffset": 5845, + "startOffset": 5033 + }, + { + "count": 0, + "endOffset": 6218, + "startOffset": 6053 + }, + { + "count": 0, + "endOffset": 7598, + "startOffset": 6251 + }, + { + "count": 49, + "endOffset": 7689, + "startOffset": 7634 + }, + { + "count": 40, + "endOffset": 8015, + "startOffset": 7691 + }, + { + "count": 40, + "endOffset": 8333, + "startOffset": 8048 + }, + { + "count": 0, + "endOffset": 8270, + "startOffset": 8090 + }, + { + "count": 0, + "endOffset": 8433, + "startOffset": 8365 + }, + { + "count": 40, + "endOffset": 8600, + "startOffset": 8572 + }, + { + "count": 40, + "endOffset": 8632, + "startOffset": 8602 + }, + { + "count": 0, + "endOffset": 9109, + "startOffset": 9059 + } + ] + }, + { + "functionName": "normalize", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 12023, + "startOffset": 9187 + }, + { + "count": 0, + "endOffset": 9308, + "startOffset": 9297 + }, + { + "count": 0, + "endOffset": 9622, + "startOffset": 9465 + }, + { + "count": 0, + "endOffset": 11204, + "startOffset": 9655 + }, + { + "count": 0, + "endOffset": 11708, + "startOffset": 11697 + }, + { + "count": 0, + "endOffset": 11751, + "startOffset": 11737 + }, + { + "count": 0, + "endOffset": 11771, + "startOffset": 11760 + }, + { + "count": 0, + "endOffset": 11863, + "startOffset": 11850 + }, + { + "count": 0, + "endOffset": 11950, + "startOffset": 11895 + }, + { + "count": 0, + "endOffset": 12017, + "startOffset": 11997 + } + ] + }, + { + "functionName": "isAbsolute", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 12477, + "startOffset": 12096 + }, + { + "count": 0, + "endOffset": 12220, + "startOffset": 12207 + }, + { + "count": 4, + "endOffset": 12424, + "startOffset": 12381 + }, + { + "count": 4, + "endOffset": 12470, + "startOffset": 12425 + } + ] + }, + { + "functionName": "join", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 14671, + "startOffset": 12552 + }, + { + "count": 0, + "endOffset": 12614, + "startOffset": 12603 + }, + { + "count": 2, + "endOffset": 12926, + "startOffset": 12697 + }, + { + "count": 1, + "endOffset": 12910, + "startOffset": 12838 + }, + { + "count": 0, + "endOffset": 12978, + "startOffset": 12967 + }, + { + "count": 0, + "endOffset": 14271, + "startOffset": 13851 + }, + { + "count": 0, + "endOffset": 14499, + "startOffset": 14466 + }, + { + "count": 0, + "endOffset": 14620, + "startOffset": 14579 + } + ] + }, + { + "functionName": "relative", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18564, + "startOffset": 14979 + } + ] + }, + { + "functionName": "toNamespacedPath", + "isBlockCoverage": true, + "ranges": [ + { + "count": 18, + "endOffset": 19630, + "startOffset": 18571 + }, + { + "count": 0, + "endOffset": 18702, + "startOffset": 18690 + }, + { + "count": 0, + "endOffset": 18759, + "startOffset": 18733 + }, + { + "count": 0, + "endOffset": 18865, + "startOffset": 18853 + }, + { + "count": 0, + "endOffset": 19300, + "startOffset": 18929 + }, + { + "count": 0, + "endOffset": 19629, + "startOffset": 19605 + } + ] + }, + { + "functionName": "dirname", + "isBlockCoverage": true, + "ranges": [ + { + "count": 7, + "endOffset": 22114, + "startOffset": 19702 + }, + { + "count": 0, + "endOffset": 19821, + "startOffset": 19810 + }, + { + "count": 0, + "endOffset": 20090, + "startOffset": 19926 + }, + { + "count": 0, + "endOffset": 21455, + "startOffset": 20153 + }, + { + "count": 0, + "endOffset": 21602, + "startOffset": 21599 + }, + { + "count": 57, + "endOffset": 21973, + "startOffset": 21732 + }, + { + "count": 7, + "endOffset": 21871, + "startOffset": 21782 + }, + { + "count": 50, + "endOffset": 21966, + "startOffset": 21871 + }, + { + "count": 0, + "endOffset": 22077, + "startOffset": 21997 + } + ] + }, + { + "functionName": "basename", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 24930, + "startOffset": 22214 + }, + { + "count": 0, + "endOffset": 22298, + "startOffset": 22271 + }, + { + "count": 0, + "endOffset": 22778, + "startOffset": 22761 + }, + { + "count": 0, + "endOffset": 22807, + "startOffset": 22779 + }, + { + "count": 0, + "endOffset": 24318, + "startOffset": 22809 + }, + { + "count": 17, + "endOffset": 24848, + "startOffset": 24371 + }, + { + "count": 2, + "endOffset": 24652, + "startOffset": 24421 + }, + { + "count": 15, + "endOffset": 24841, + "startOffset": 24652 + }, + { + "count": 2, + "endOffset": 24841, + "startOffset": 24674 + }, + { + "count": 0, + "endOffset": 24889, + "startOffset": 24879 + } + ] + }, + { + "functionName": "extname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27073, + "startOffset": 25002 + } + ] + }, + { + "functionName": "parse", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31903, + "startOffset": 27291 + } + ] + }, + { + "functionName": "resolve", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33025, + "startOffset": 32100 + } + ] + }, + { + "functionName": "normalize", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33694, + "startOffset": 33097 + } + ] + }, + { + "functionName": "isAbsolute", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33899, + "startOffset": 33767 + } + ] + }, + { + "functionName": "join", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 34405, + "startOffset": 33974 + } + ] + }, + { + "functionName": "relative", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36780, + "startOffset": 34502 + } + ] + }, + { + "functionName": "toNamespacedPath", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36866, + "startOffset": 36787 + } + ] + }, + { + "functionName": "dirname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 37590, + "startOffset": 36938 + } + ] + }, + { + "functionName": "basename", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 40083, + "startOffset": 37690 + } + ] + }, + { + "functionName": "extname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 41863, + "startOffset": 40155 + } + ] + }, + { + "functionName": "parse", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 44518, + "startOffset": 42086 + } + ] + } + ], + "scriptId": "33", + "url": "path.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 16488, + "startOffset": 0 + } + ] + }, + { + "functionName": "lazyBuffer", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1119, + "startOffset": 1006 + } + ] + }, + { + "functionName": "validateEncoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1251, + "startOffset": 1123 + } + ] + }, + { + "functionName": "validateDecoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1383, + "startOffset": 1255 + } + ] + }, + { + "functionName": "validateArgument", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1601, + "startOffset": 1387 + } + ] + }, + { + "functionName": "trimAsciiWhitespace", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9068, + "startOffset": 8566 + } + ] + }, + { + "functionName": "getEncodingFromLabel", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9255, + "startOffset": 9072 + } + ] + }, + { + "functionName": "TextEncoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9379, + "startOffset": 9331 + } + ] + }, + { + "functionName": "get encoding", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9455, + "startOffset": 9385 + } + ] + }, + { + "functionName": "encode", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9556, + "startOffset": 9461 + } + ] + }, + { + "functionName": "encodeInto", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9879, + "startOffset": 9562 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10291, + "startOffset": 9885 + } + ] + }, + { + "functionName": "makeTextDecoderICU", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 12397, + "startOffset": 10680 + } + ] + }, + { + "functionName": "TextDecoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11574, + "startOffset": 10825 + } + ] + }, + { + "functionName": "decode", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12364, + "startOffset": 11584 + } + ] + }, + { + "functionName": "makeTextDecoderJS", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15025, + "startOffset": 12401 + } + ] + }, + { + "functionName": "get encoding", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15240, + "startOffset": 15156 + } + ] + }, + { + "functionName": "get fatal", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15379, + "startOffset": 15249 + } + ] + }, + { + "functionName": "get ignoreBOM", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15547, + "startOffset": 15388 + } + ] + }, + { + "functionName": "ObjectGetOwnPropertyDescriptors", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16186, + "startOffset": 15556 + } + ] + } + ], + "scriptId": "34", + "url": "internal/encoding.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 8643, + "startOffset": 0 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1882, + "startOffset": 1855 + } + ] + }, + { + "functionName": "unenroll", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3615, + "startOffset": 2396 + } + ] + }, + { + "functionName": "enroll", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4089, + "startOffset": 3827 + } + ] + }, + { + "functionName": "setTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4800, + "startOffset": 4127 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5019, + "startOffset": 4881 + } + ] + }, + { + "functionName": "clearTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5407, + "startOffset": 5028 + } + ] + }, + { + "functionName": "setInterval", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6086, + "startOffset": 5411 + } + ] + }, + { + "functionName": "clearInterval", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6393, + "startOffset": 6090 + } + ] + }, + { + "functionName": "Timeout.close", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6477, + "startOffset": 6423 + } + ] + }, + { + "functionName": "Timeout.", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6689, + "startOffset": 6521 + } + ] + }, + { + "functionName": "setImmediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7259, + "startOffset": 6694 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7482, + "startOffset": 7342 + } + ] + }, + { + "functionName": "clearImmediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7977, + "startOffset": 7493 + } + ] + } + ], + "scriptId": "35", + "url": "timers.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1166, + "startOffset": 0 + } + ] + }, + { + "functionName": "init", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 93, + "startOffset": 17 + } + ] + }, + { + "functionName": "peek", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 221, + "startOffset": 126 + } + ] + }, + { + "functionName": "remove", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 497, + "startOffset": 259 + } + ] + }, + { + "functionName": "append", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1021, + "startOffset": 556 + } + ] + }, + { + "functionName": "isEmpty", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1087, + "startOffset": 1025 + } + ] + } + ], + "scriptId": "36", + "url": "internal/linkedlist.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 19158, + "startOffset": 0 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4543, + "startOffset": 4516 + } + ] + }, + { + "functionName": "initAsyncResource", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5802, + "startOffset": 5518 + } + ] + }, + { + "functionName": "Timeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6918, + "startOffset": 5891 + } + ] + }, + { + "functionName": "Timeout.", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7213, + "startOffset": 7034 + } + ] + }, + { + "functionName": "Timeout.refresh", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7351, + "startOffset": 7246 + } + ] + }, + { + "functionName": "Timeout.unref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7517, + "startOffset": 7382 + } + ] + }, + { + "functionName": "Timeout.ref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7681, + "startOffset": 7546 + } + ] + }, + { + "functionName": "Timeout.hasRef", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7752, + "startOffset": 7713 + } + ] + }, + { + "functionName": "TimersList", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8064, + "startOffset": 7757 + } + ] + }, + { + "functionName": "TimersList.", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8362, + "startOffset": 8183 + } + ] + }, + { + "functionName": "ImmediateList", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 8494, + "startOffset": 8423 + } + ] + }, + { + "functionName": "ImmediateList.append", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8852, + "startOffset": 8677 + } + ] + }, + { + "functionName": "ImmediateList.remove", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9387, + "startOffset": 9034 + } + ] + }, + { + "functionName": "incRefCount", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9471, + "startOffset": 9392 + } + ] + }, + { + "functionName": "decRefCount", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9555, + "startOffset": 9475 + } + ] + }, + { + "functionName": "active", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9698, + "startOffset": 9642 + } + ] + }, + { + "functionName": "unrefActive", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9911, + "startOffset": 9849 + } + ] + }, + { + "functionName": "insertGuarded", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10677, + "startOffset": 10138 + } + ] + }, + { + "functionName": "insert", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11352, + "startOffset": 10681 + } + ] + }, + { + "functionName": "setUnrefTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11672, + "startOffset": 11356 + } + ] + }, + { + "functionName": "getTimerDuration", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12279, + "startOffset": 11742 + } + ] + }, + { + "functionName": "compareTimersLists", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12497, + "startOffset": 12283 + } + ] + }, + { + "functionName": "setPosition", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12574, + "startOffset": 12501 + } + ] + }, + { + "functionName": "getTimerCallbacks", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 17857, + "startOffset": 12578 + } + ] + }, + { + "functionName": "processImmediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14758, + "startOffset": 12896 + } + ] + }, + { + "functionName": "processTimers", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15258, + "startOffset": 14766 + } + ] + }, + { + "functionName": "listOnTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17792, + "startOffset": 15264 + } + ] + }, + { + "functionName": "Immediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18218, + "startOffset": 17882 + } + ] + }, + { + "functionName": "ref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18404, + "startOffset": 18224 + } + ] + }, + { + "functionName": "unref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18593, + "startOffset": 18410 + } + ] + }, + { + "functionName": "hasRef", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18642, + "startOffset": 18599 + } + ] + } + ], + "scriptId": "37", + "url": "internal/timers.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 3052, + "startOffset": 0 + } + ] + }, + { + "functionName": "PriorityQueue", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 838, + "startOffset": 589 + } + ] + }, + { + "functionName": "module.exports", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 886, + "startOffset": 844 + } + ] + }, + { + "functionName": "insert", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1086, + "startOffset": 892 + } + ] + }, + { + "functionName": "peek", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1133, + "startOffset": 1092 + } + ] + }, + { + "functionName": "percolateDown", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1829, + "startOffset": 1139 + } + ] + }, + { + "functionName": "percolateUp", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2344, + "startOffset": 1835 + } + ] + }, + { + "functionName": "removeAt", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2695, + "startOffset": 2350 + } + ] + }, + { + "functionName": "remove", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2876, + "startOffset": 2701 + } + ] + }, + { + "functionName": "shift", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3046, + "startOffset": 2882 + } + ] + } + ], + "scriptId": "38", + "url": "internal/priority_queue.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 2918, + "startOffset": 0 + } + ] + }, + { + "functionName": "initializeDebugEnv", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 904, + "startOffset": 521 + }, + { + "count": 0, + "endOffset": 819, + "startOffset": 614 + } + ] + }, + { + "functionName": "emitWarningIfNeeded", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1314, + "startOffset": 982 + } + ] + }, + { + "functionName": "noop", + "isBlockCoverage": true, + "ranges": [ + { + "count": 13, + "endOffset": 1336, + "startOffset": 1318 + } + ] + }, + { + "functionName": "debuglogImpl", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 1921, + "startOffset": 1340 + }, + { + "count": 2, + "endOffset": 1891, + "startOffset": 1416 + }, + { + "count": 0, + "endOffset": 1841, + "startOffset": 1436 + } + ] + }, + { + "functionName": "debug", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1833, + "startOffset": 1528 + } + ] + }, + { + "functionName": "debuglog", + "isBlockCoverage": true, + "ranges": [ + { + "count": 12, + "endOffset": 2855, + "startOffset": 2147 + } + ] + }, + { + "functionName": "init", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 2278, + "startOffset": 2179 + } + ] + }, + { + "functionName": "debug", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 2539, + "startOffset": 2294 + } + ] + }, + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2644, + "startOffset": 2571 + } + ] + }, + { + "functionName": "logger", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 2691, + "startOffset": 2664 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2779, + "startOffset": 2743 + } + ] + } + ], + "scriptId": "39", + "url": "internal/util/debuglog.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 7161, + "startOffset": 0 + } + ] + }, + { + "functionName": "tryGetCwd", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 890, + "startOffset": 551 + }, + { + "count": 0, + "endOffset": 887, + "startOffset": 615 + } + ] + }, + { + "functionName": "evalModule", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1347, + "startOffset": 894 + } + ] + }, + { + "functionName": "evalScript", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2774, + "startOffset": 1351 + } + ] + }, + { + "functionName": "setUncaughtExceptionCaptureCallback", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3570, + "startOffset": 2858 + } + ] + }, + { + "functionName": "hasUncaughtExceptionCaptureCallback", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3677, + "startOffset": 3574 + } + ] + }, + { + "functionName": "noop", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3699, + "startOffset": 3681 + } + ] + }, + { + "functionName": "createOnGlobalUncaughtException", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 6711, + "startOffset": 4252 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6707, + "startOffset": 4525 + } + ] + }, + { + "functionName": "readStdin", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6931, + "startOffset": 6715 + } + ] + } + ], + "scriptId": "40", + "url": "internal/process/execution.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 5002, + "startOffset": 0 + } + ] + }, + { + "functionName": "lazyOption", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 864, + "startOffset": 295 + } + ] + }, + { + "functionName": "writeOut", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1097, + "startOffset": 970 + } + ] + }, + { + "functionName": "writeToFile", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1504, + "startOffset": 1101 + } + ] + }, + { + "functionName": "doEmitWarning", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1581, + "startOffset": 1508 + } + ] + }, + { + "functionName": "onWarning", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2833, + "startOffset": 1623 + } + ] + }, + { + "functionName": "emitWarning", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4139, + "startOffset": 2961 + } + ] + }, + { + "functionName": "emitWarningSync", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4239, + "startOffset": 4143 + } + ] + }, + { + "functionName": "createWarningObject", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4924, + "startOffset": 4243 + } + ] + } + ], + "scriptId": "41", + "url": "internal/process/warning.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 6632, + "startOffset": 0 + } + ] + }, + { + "functionName": "process._startProfilerIdleNotifier", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 533, + "startOffset": 525 + } + ] + }, + { + "functionName": "process._stopProfilerIdleNotifier", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 580, + "startOffset": 572 + } + ] + }, + { + "functionName": "defineStream", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 738, + "startOffset": 585 + } + ] + }, + { + "functionName": "createWritableStdioStream", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2943, + "startOffset": 1318 + } + ] + }, + { + "functionName": "dummyDestroy", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3343, + "startOffset": 2947 + } + ] + }, + { + "functionName": "getStdout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3728, + "startOffset": 3387 + } + ] + }, + { + "functionName": "getStderr", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4073, + "startOffset": 3732 + } + ] + }, + { + "functionName": "getStdin", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6480, + "startOffset": 4077 + } + ] + }, + { + "functionName": "rawMethods.resetStdioForTesting", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6629, + "startOffset": 6546 + } + ] + } + ], + "scriptId": "42", + "url": "internal/bootstrap/switches/is_main_thread.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1177, + "startOffset": 0 + } + ] + }, + { + "functionName": "isSignal", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 318, + "startOffset": 220 + } + ] + }, + { + "functionName": "startListeningIfSignal", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 892, + "startOffset": 385 + }, + { + "count": 1, + "endOffset": 472, + "startOffset": 447 + }, + { + "count": 1, + "endOffset": 889, + "startOffset": 474 + }, + { + "count": 0, + "endOffset": 848, + "startOffset": 766 + } + ] + }, + { + "functionName": "stopListeningIfSignal", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1097, + "startOffset": 896 + } + ] + } + ], + "scriptId": "43", + "url": "internal/process/signal.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 3655, + "startOffset": 0 + }, + { + "count": 0, + "endOffset": 598, + "startOffset": 298 + } + ] + }, + { + "functionName": "wrapPosixCredentialSetters", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3064, + "startOffset": 846 + } + ] + }, + { + "functionName": "wrappedChdir", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3397, + "startOffset": 3221 + } + ] + }, + { + "functionName": "wrappedUmask", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3542, + "startOffset": 3401 + } + ] + }, + { + "functionName": "wrappedCwd", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 3653, + "startOffset": 3546 + }, + { + "count": 1, + "endOffset": 3629, + "startOffset": 3600 + } + ] + } + ], + "scriptId": "44", + "url": "internal/bootstrap/switches/does_own_process_state.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 649, + "startOffset": 0 + } + ] + } + ], + "scriptId": "45", + "url": "internal/main/run_main_module.js" + } + ], + "timestamp": 967709.209879 + }, + "test_v8_coverage_node_sqlite_merged.json": { + "result": [ + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 3655, + "startOffset": 0 + }, + { + "count": 0, + "endOffset": 598, + "startOffset": 298 + } + ] + }, + { + "functionName": "wrapPosixCredentialSetters", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3064, + "startOffset": 846 + } + ] + }, + { + "functionName": "wrappedChdir", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3397, + "startOffset": 3221 + } + ] + }, + { + "functionName": "wrappedUmask", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3542, + "startOffset": 3401 + } + ] + }, + { + "functionName": "wrappedCwd", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 3653, + "startOffset": 3546 + }, + { + "count": 1, + "endOffset": 3629, + "startOffset": 3600 + } + ] + } + ], + "scriptId": "0", + "url": "internal/bootstrap/switches/does_own_process_state.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 6632, + "startOffset": 0 + } + ] + }, + { + "functionName": "process._startProfilerIdleNotifier", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 533, + "startOffset": 525 + } + ] + }, + { + "functionName": "process._stopProfilerIdleNotifier", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 580, + "startOffset": 572 + } + ] + }, + { + "functionName": "defineStream", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 738, + "startOffset": 585 + } + ] + }, + { + "functionName": "createWritableStdioStream", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2943, + "startOffset": 1318 + } + ] + }, + { + "functionName": "dummyDestroy", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3343, + "startOffset": 2947 + } + ] + }, + { + "functionName": "getStdout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3728, + "startOffset": 3387 + } + ] + }, + { + "functionName": "getStderr", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4073, + "startOffset": 3732 + } + ] + }, + { + "functionName": "getStdin", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6480, + "startOffset": 4077 + } + ] + }, + { + "functionName": "rawMethods.resetStdioForTesting", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6629, + "startOffset": 6546 + } + ] + } + ], + "scriptId": "1", + "url": "internal/bootstrap/switches/is_main_thread.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 20604, + "startOffset": 0 + } + ] + }, + { + "functionName": "Console", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4925, + "startOffset": 2686 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5200, + "startOffset": 5144 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5669, + "startOffset": 5458 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 6489, + "startOffset": 5878 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 6165, + "startOffset": 6067 + }, + { + "count": 1, + "endOffset": 6124, + "startOffset": 6101 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6208, + "startOffset": 6178 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6417, + "startOffset": 6315 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6460, + "startOffset": 6430 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 7776, + "startOffset": 6563 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9334, + "startOffset": 7850 + }, + { + "count": 0, + "endOffset": 8095, + "startOffset": 8081 + }, + { + "count": 0, + "endOffset": 8197, + "startOffset": 8171 + }, + { + "count": 0, + "endOffset": 8432, + "startOffset": 8238 + }, + { + "count": 0, + "endOffset": 8521, + "startOffset": 8493 + }, + { + "count": 0, + "endOffset": 9261, + "startOffset": 8971 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9918, + "startOffset": 9411 + }, + { + "count": 0, + "endOffset": 9635, + "startOffset": 9629 + }, + { + "count": 0, + "endOffset": 9840, + "startOffset": 9714 + }, + { + "count": 0, + "endOffset": 9910, + "startOffset": 9886 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 10124, + "startOffset": 9993 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10330, + "startOffset": 10199 + } + ] + }, + { + "functionName": "createWriteErrorHandler", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 11331, + "startOffset": 10424 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 11327, + "startOffset": 10493 + }, + { + "count": 0, + "endOffset": 10786, + "startOffset": 10768 + } + ] + }, + { + "functionName": "log", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 11452, + "startOffset": 11363 + } + ] + }, + { + "functionName": "warn", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11551, + "startOffset": 11461 + } + ] + }, + { + "functionName": "dir", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11751, + "startOffset": 11560 + } + ] + }, + { + "functionName": "time", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12125, + "startOffset": 11758 + } + ] + }, + { + "functionName": "timeEnd", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12429, + "startOffset": 12132 + } + ] + }, + { + "functionName": "timeLog", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12679, + "startOffset": 12436 + } + ] + }, + { + "functionName": "trace", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12886, + "startOffset": 12693 + } + ] + }, + { + "functionName": "assert", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 13116, + "startOffset": 12893 + } + ] + }, + { + "functionName": "clear", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 13620, + "startOffset": 13180 + } + ] + }, + { + "functionName": "count", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14153, + "startOffset": 13684 + } + ] + }, + { + "functionName": "countReset", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14518, + "startOffset": 14222 + } + ] + }, + { + "functionName": "group", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14699, + "startOffset": 14525 + } + ] + }, + { + "functionName": "groupEnd", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14880, + "startOffset": 14706 + } + ] + }, + { + "functionName": "table", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18454, + "startOffset": 14932 + } + ] + }, + { + "functionName": "timeLogImpl", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19012, + "startOffset": 18499 + } + ] + }, + { + "functionName": "pad", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19095, + "startOffset": 19016 + } + ] + }, + { + "functionName": "formatTime", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19892, + "startOffset": 19099 + } + ] + }, + { + "functionName": "isArray", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20089, + "startOffset": 20033 + } + ] + }, + { + "functionName": "noop", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20112, + "startOffset": 20094 + } + ] + } + ], + "scriptId": "2", + "url": "internal/console/constructor.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1638, + "startOffset": 0 + } + ] + } + ], + "scriptId": "3", + "url": "internal/constants.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 16488, + "startOffset": 0 + } + ] + }, + { + "functionName": "lazyBuffer", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1119, + "startOffset": 1006 + } + ] + }, + { + "functionName": "validateEncoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1251, + "startOffset": 1123 + } + ] + }, + { + "functionName": "validateDecoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1383, + "startOffset": 1255 + } + ] + }, + { + "functionName": "validateArgument", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1601, + "startOffset": 1387 + } + ] + }, + { + "functionName": "trimAsciiWhitespace", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9068, + "startOffset": 8566 + } + ] + }, + { + "functionName": "getEncodingFromLabel", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9255, + "startOffset": 9072 + } + ] + }, + { + "functionName": "TextEncoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9379, + "startOffset": 9331 + } + ] + }, + { + "functionName": "get encoding", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9455, + "startOffset": 9385 + } + ] + }, + { + "functionName": "encode", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9556, + "startOffset": 9461 + } + ] + }, + { + "functionName": "encodeInto", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9879, + "startOffset": 9562 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10291, + "startOffset": 9885 + } + ] + }, + { + "functionName": "makeTextDecoderICU", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 12397, + "startOffset": 10680 + } + ] + }, + { + "functionName": "TextDecoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11574, + "startOffset": 10825 + } + ] + }, + { + "functionName": "decode", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12364, + "startOffset": 11584 + } + ] + }, + { + "functionName": "makeTextDecoderJS", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15025, + "startOffset": 12401 + } + ] + }, + { + "functionName": "get encoding", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15240, + "startOffset": 15156 + } + ] + }, + { + "functionName": "get fatal", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15379, + "startOffset": 15249 + } + ] + }, + { + "functionName": "get ignoreBOM", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15547, + "startOffset": 15388 + } + ] + }, + { + "functionName": "ObjectGetOwnPropertyDescriptors", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16186, + "startOffset": 15556 + } + ] + } + ], + "scriptId": "4", + "url": "internal/encoding.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1166, + "startOffset": 0 + } + ] + }, + { + "functionName": "init", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 93, + "startOffset": 17 + } + ] + }, + { + "functionName": "peek", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 221, + "startOffset": 126 + } + ] + }, + { + "functionName": "remove", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 497, + "startOffset": 259 + } + ] + }, + { + "functionName": "append", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1021, + "startOffset": 556 + } + ] + }, + { + "functionName": "isEmpty", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1087, + "startOffset": 1025 + } + ] + } + ], + "scriptId": "5", + "url": "internal/linkedlist.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 649, + "startOffset": 0 + } + ] + } + ], + "scriptId": "6", + "url": "internal/main/run_main_module.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 3052, + "startOffset": 0 + } + ] + }, + { + "functionName": "PriorityQueue", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 838, + "startOffset": 589 + } + ] + }, + { + "functionName": "module.exports", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 886, + "startOffset": 844 + } + ] + }, + { + "functionName": "insert", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1086, + "startOffset": 892 + } + ] + }, + { + "functionName": "peek", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1133, + "startOffset": 1092 + } + ] + }, + { + "functionName": "percolateDown", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1829, + "startOffset": 1139 + } + ] + }, + { + "functionName": "percolateUp", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2344, + "startOffset": 1835 + } + ] + }, + { + "functionName": "removeAt", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2695, + "startOffset": 2350 + } + ] + }, + { + "functionName": "remove", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2876, + "startOffset": 2701 + } + ] + }, + { + "functionName": "shift", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3046, + "startOffset": 2882 + } + ] + } + ], + "scriptId": "7", + "url": "internal/priority_queue.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 7161, + "startOffset": 0 + } + ] + }, + { + "functionName": "tryGetCwd", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 890, + "startOffset": 551 + }, + { + "count": 0, + "endOffset": 887, + "startOffset": 615 + } + ] + }, + { + "functionName": "evalModule", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1347, + "startOffset": 894 + } + ] + }, + { + "functionName": "evalScript", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2774, + "startOffset": 1351 + } + ] + }, + { + "functionName": "setUncaughtExceptionCaptureCallback", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3570, + "startOffset": 2858 + } + ] + }, + { + "functionName": "hasUncaughtExceptionCaptureCallback", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3677, + "startOffset": 3574 + } + ] + }, + { + "functionName": "noop", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3699, + "startOffset": 3681 + } + ] + }, + { + "functionName": "createOnGlobalUncaughtException", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 6711, + "startOffset": 4252 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6707, + "startOffset": 4525 + } + ] + }, + { + "functionName": "readStdin", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6931, + "startOffset": 6715 + } + ] + } + ], + "scriptId": "8", + "url": "internal/process/execution.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1177, + "startOffset": 0 + } + ] + }, + { + "functionName": "isSignal", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 318, + "startOffset": 220 + } + ] + }, + { + "functionName": "startListeningIfSignal", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 892, + "startOffset": 385 + }, + { + "count": 1, + "endOffset": 472, + "startOffset": 447 + }, + { + "count": 1, + "endOffset": 889, + "startOffset": 474 + }, + { + "count": 0, + "endOffset": 848, + "startOffset": 766 + } + ] + }, + { + "functionName": "stopListeningIfSignal", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1097, + "startOffset": 896 + } + ] + } + ], + "scriptId": "9", + "url": "internal/process/signal.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 5002, + "startOffset": 0 + } + ] + }, + { + "functionName": "lazyOption", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 864, + "startOffset": 295 + } + ] + }, + { + "functionName": "writeOut", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1097, + "startOffset": 970 + } + ] + }, + { + "functionName": "writeToFile", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1504, + "startOffset": 1101 + } + ] + }, + { + "functionName": "doEmitWarning", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1581, + "startOffset": 1508 + } + ] + }, + { + "functionName": "onWarning", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2833, + "startOffset": 1623 + } + ] + }, + { + "functionName": "emitWarning", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4139, + "startOffset": 2961 + } + ] + }, + { + "functionName": "emitWarningSync", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4239, + "startOffset": 4143 + } + ] + }, + { + "functionName": "createWarningObject", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4924, + "startOffset": 4243 + } + ] + } + ], + "scriptId": "10", + "url": "internal/process/warning.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 2, + "endOffset": 3134, + "startOffset": 0 + } + ] + }, + { + "functionName": "encodeStr", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3065, + "startOffset": 1374 + } + ] + } + ], + "scriptId": "11", + "url": "internal/querystring.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 19158, + "startOffset": 0 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4543, + "startOffset": 4516 + } + ] + }, + { + "functionName": "initAsyncResource", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5802, + "startOffset": 5518 + } + ] + }, + { + "functionName": "Timeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6918, + "startOffset": 5891 + } + ] + }, + { + "functionName": "Timeout.", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7213, + "startOffset": 7034 + } + ] + }, + { + "functionName": "Timeout.refresh", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7351, + "startOffset": 7246 + } + ] + }, + { + "functionName": "Timeout.unref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7517, + "startOffset": 7382 + } + ] + }, + { + "functionName": "Timeout.ref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7681, + "startOffset": 7546 + } + ] + }, + { + "functionName": "Timeout.hasRef", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7752, + "startOffset": 7713 + } + ] + }, + { + "functionName": "TimersList", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8064, + "startOffset": 7757 + } + ] + }, + { + "functionName": "TimersList.", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8362, + "startOffset": 8183 + } + ] + }, + { + "functionName": "ImmediateList", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 8494, + "startOffset": 8423 + } + ] + }, + { + "functionName": "ImmediateList.append", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8852, + "startOffset": 8677 + } + ] + }, + { + "functionName": "ImmediateList.remove", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9387, + "startOffset": 9034 + } + ] + }, + { + "functionName": "incRefCount", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9471, + "startOffset": 9392 + } + ] + }, + { + "functionName": "decRefCount", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9555, + "startOffset": 9475 + } + ] + }, + { + "functionName": "active", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9698, + "startOffset": 9642 + } + ] + }, + { + "functionName": "unrefActive", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9911, + "startOffset": 9849 + } + ] + }, + { + "functionName": "insertGuarded", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10677, + "startOffset": 10138 + } + ] + }, + { + "functionName": "insert", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11352, + "startOffset": 10681 + } + ] + }, + { + "functionName": "setUnrefTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11672, + "startOffset": 11356 + } + ] + }, + { + "functionName": "getTimerDuration", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12279, + "startOffset": 11742 + } + ] + }, + { + "functionName": "compareTimersLists", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12497, + "startOffset": 12283 + } + ] + }, + { + "functionName": "setPosition", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12574, + "startOffset": 12501 + } + ] + }, + { + "functionName": "getTimerCallbacks", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 17857, + "startOffset": 12578 + } + ] + }, + { + "functionName": "processImmediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14758, + "startOffset": 12896 + } + ] + }, + { + "functionName": "processTimers", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15258, + "startOffset": 14766 + } + ] + }, + { + "functionName": "listOnTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17792, + "startOffset": 15264 + } + ] + }, + { + "functionName": "Immediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18218, + "startOffset": 17882 + } + ] + }, + { + "functionName": "ref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18404, + "startOffset": 18224 + } + ] + }, + { + "functionName": "unref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18593, + "startOffset": 18410 + } + ] + }, + { + "functionName": "hasRef", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18642, + "startOffset": 18599 + } + ] + } + ], + "scriptId": "12", + "url": "internal/timers.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 42795, + "startOffset": 0 + } + ] + }, + { + "functionName": "toUSVString", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2627, + "startOffset": 2323 + } + ] + }, + { + "functionName": "serializeTupleOrigin", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2965, + "startOffset": 2845 + } + ] + }, + { + "functionName": "URLContext", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 3611, + "startOffset": 3378 + } + ] + }, + { + "functionName": "URLSearchParams", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 6333, + "startOffset": 3909 + }, + { + "count": 0, + "endOffset": 6267, + "startOffset": 4027 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7439, + "startOffset": 6339 + } + ] + }, + { + "functionName": "onParseComplete", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 8156, + "startOffset": 7446 + }, + { + "count": 0, + "endOffset": 7716, + "startOffset": 7706 + }, + { + "count": 0, + "endOffset": 7790, + "startOffset": 7780 + }, + { + "count": 0, + "endOffset": 7877, + "startOffset": 7873 + } + ] + }, + { + "functionName": "onParseError", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8237, + "startOffset": 8160 + } + ] + }, + { + "functionName": "onParseProtocolComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8596, + "startOffset": 8241 + } + ] + }, + { + "functionName": "onParseHostnameComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8956, + "startOffset": 8600 + } + ] + }, + { + "functionName": "onParsePortComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9125, + "startOffset": 8960 + } + ] + }, + { + "functionName": "onParseHostComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9440, + "startOffset": 9129 + } + ] + }, + { + "functionName": "onParsePathComplete", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 9954, + "startOffset": 9444 + }, + { + "count": 0, + "endOffset": 9787, + "startOffset": 9716 + } + ] + }, + { + "functionName": "onParseSearchComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10129, + "startOffset": 9958 + } + ] + }, + { + "functionName": "onParseHashComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10306, + "startOffset": 10133 + } + ] + }, + { + "functionName": "URL", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 10663, + "startOffset": 10325 + }, + { + "count": 0, + "endOffset": 10518, + "startOffset": 10464 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10752, + "startOffset": 10669 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 327, + "endOffset": 10853, + "startOffset": 10758 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11136, + "startOffset": 10931 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12107, + "startOffset": 11142 + } + ] + }, + { + "functionName": "format", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 13502, + "startOffset": 12284 + }, + { + "count": 0, + "endOffset": 12432, + "startOffset": 12371 + }, + { + "count": 0, + "endOffset": 13023, + "startOffset": 12856 + }, + { + "count": 0, + "endOffset": 13094, + "startOffset": 13056 + }, + { + "count": 0, + "endOffset": 13172, + "startOffset": 13150 + }, + { + "count": 0, + "endOffset": 13247, + "startOffset": 13181 + }, + { + "count": 0, + "endOffset": 13386, + "startOffset": 13363 + }, + { + "count": 0, + "endOffset": 13476, + "startOffset": 13450 + } + ] + }, + { + "functionName": "toString", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 13849, + "startOffset": 13788 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 13967, + "startOffset": 13920 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14159, + "startOffset": 13974 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14895, + "startOffset": 14245 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15020, + "startOffset": 14970 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15408, + "startOffset": 15027 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15535, + "startOffset": 15483 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15953, + "startOffset": 15542 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16080, + "startOffset": 16028 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16498, + "startOffset": 16087 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16731, + "startOffset": 16569 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17038, + "startOffset": 16738 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17167, + "startOffset": 17113 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17482, + "startOffset": 17174 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17656, + "startOffset": 17553 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17991, + "startOffset": 17663 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": true, + "ranges": [ + { + "count": 218, + "endOffset": 18268, + "startOffset": 18066 + }, + { + "count": 0, + "endOffset": 18167, + "startOffset": 18148 + }, + { + "count": 0, + "endOffset": 18221, + "startOffset": 18211 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 18501, + "startOffset": 18275 + }, + { + "count": 0, + "endOffset": 18396, + "startOffset": 18389 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18718, + "startOffset": 18574 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19232, + "startOffset": 18725 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19372, + "startOffset": 19324 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19599, + "startOffset": 19443 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20037, + "startOffset": 19606 + } + ] + }, + { + "functionName": "toJSON", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20249, + "startOffset": 20190 + } + ] + }, + { + "functionName": "update", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20581, + "startOffset": 20263 + } + ] + }, + { + "functionName": "initSearchParams", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 20731, + "startOffset": 20585 + }, + { + "count": 0, + "endOffset": 20730, + "startOffset": 20686 + } + ] + }, + { + "functionName": "parseParams", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 23241, + "startOffset": 20844 + } + ] + }, + { + "functionName": "serializeParams", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 24820, + "startOffset": 24244 + } + ] + }, + { + "functionName": "defineIDLClass", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 25592, + "startOffset": 24878 + }, + { + "count": 13, + "endOffset": 25379, + "startOffset": 25226 + }, + { + "count": 1, + "endOffset": 25589, + "startOffset": 25435 + } + ] + }, + { + "functionName": "merge", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 26271, + "startOffset": 25615 + } + ] + }, + { + "functionName": "append", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 26740, + "startOffset": 26341 + } + ] + }, + { + "functionName": "delete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27265, + "startOffset": 26747 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27722, + "startOffset": 27272 + } + ] + }, + { + "functionName": "getAll", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 28214, + "startOffset": 27729 + } + ] + }, + { + "functionName": "has", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 28665, + "startOffset": 28221 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 29736, + "startOffset": 28672 + } + ] + }, + { + "functionName": "sort", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 30962, + "startOffset": 29743 + } + ] + }, + { + "functionName": "entries", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31345, + "startOffset": 31130 + } + ] + }, + { + "functionName": "forEach", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31943, + "startOffset": 31352 + } + ] + }, + { + "functionName": "keys", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 32207, + "startOffset": 32001 + } + ] + }, + { + "functionName": "values", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 32424, + "startOffset": 32214 + } + ] + }, + { + "functionName": "toString", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 32767, + "startOffset": 32561 + } + ] + }, + { + "functionName": "createSearchParamsIterator", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33269, + "startOffset": 33058 + } + ] + }, + { + "functionName": "next", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 34289, + "startOffset": 33499 + } + ] + }, + { + "functionName": "defineIDLClass", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 35544, + "startOffset": 34294 + } + ] + }, + { + "functionName": "domainToASCII", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 35734, + "startOffset": 35553 + } + ] + }, + { + "functionName": "domainToUnicode", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 35923, + "startOffset": 35738 + } + ] + }, + { + "functionName": "urlToOptions", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36633, + "startOffset": 36071 + } + ] + }, + { + "functionName": "getPathFromURLWin32", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 38099, + "startOffset": 36673 + } + ] + }, + { + "functionName": "getPathFromURLPosix", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 38623, + "startOffset": 38103 + } + ] + }, + { + "functionName": "fileURLToPath", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 38982, + "startOffset": 38627 + } + ] + }, + { + "functionName": "encodePathChars", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 40330, + "startOffset": 39761 + }, + { + "count": 0, + "endOffset": 39883, + "startOffset": 39834 + }, + { + "count": 0, + "endOffset": 39985, + "startOffset": 39959 + }, + { + "count": 0, + "endOffset": 40043, + "startOffset": 39992 + }, + { + "count": 0, + "endOffset": 40130, + "startOffset": 40081 + }, + { + "count": 0, + "endOffset": 40224, + "startOffset": 40168 + }, + { + "count": 0, + "endOffset": 40307, + "startOffset": 40262 + } + ] + }, + { + "functionName": "pathToFileURL", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 41482, + "startOffset": 40334 + }, + { + "count": 0, + "endOffset": 41025, + "startOffset": 40456 + }, + { + "count": 0, + "endOffset": 41381, + "startOffset": 41327 + }, + { + "count": 0, + "endOffset": 41406, + "startOffset": 41390 + } + ] + }, + { + "functionName": "isURLInstance", + "isBlockCoverage": true, + "ranges": [ + { + "count": 277, + "endOffset": 41607, + "startOffset": 41486 + }, + { + "count": 0, + "endOffset": 41603, + "startOffset": 41580 + } + ] + }, + { + "functionName": "toPathIfFileURL", + "isBlockCoverage": true, + "ranges": [ + { + "count": 277, + "endOffset": 41760, + "startOffset": 41611 + }, + { + "count": 0, + "endOffset": 41759, + "startOffset": 41717 + } + ] + }, + { + "functionName": "constructUrl", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 42484, + "startOffset": 41764 + } + ] + } + ], + "scriptId": "13", + "url": "internal/url.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 2918, + "startOffset": 0 + } + ] + }, + { + "functionName": "initializeDebugEnv", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 904, + "startOffset": 521 + }, + { + "count": 0, + "endOffset": 819, + "startOffset": 614 + } + ] + }, + { + "functionName": "emitWarningIfNeeded", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1314, + "startOffset": 982 + } + ] + }, + { + "functionName": "noop", + "isBlockCoverage": true, + "ranges": [ + { + "count": 13, + "endOffset": 1336, + "startOffset": 1318 + } + ] + }, + { + "functionName": "debuglogImpl", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 1921, + "startOffset": 1340 + }, + { + "count": 2, + "endOffset": 1891, + "startOffset": 1416 + }, + { + "count": 0, + "endOffset": 1841, + "startOffset": 1436 + } + ] + }, + { + "functionName": "debug", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1833, + "startOffset": 1528 + } + ] + }, + { + "functionName": "debuglog", + "isBlockCoverage": true, + "ranges": [ + { + "count": 12, + "endOffset": 2855, + "startOffset": 2147 + } + ] + }, + { + "functionName": "init", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 2278, + "startOffset": 2179 + } + ] + }, + { + "functionName": "debug", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 2539, + "startOffset": 2294 + } + ] + }, + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2644, + "startOffset": 2571 + } + ] + }, + { + "functionName": "logger", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 2691, + "startOffset": 2664 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2779, + "startOffset": 2743 + } + ] + } + ], + "scriptId": "14", + "url": "internal/util/debuglog.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 2283, + "startOffset": 0 + } + ] + }, + { + "functionName": "sendInspectorCommand", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 453, + "startOffset": 100 + } + ] + }, + { + "functionName": "installConsoleExtensions", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1094, + "startOffset": 530 + } + ] + }, + { + "functionName": "wrapConsole", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 1984, + "startOffset": 1176 + }, + { + "count": 23, + "endOffset": 1981, + "startOffset": 1336 + }, + { + "count": 19, + "endOffset": 1855, + "startOffset": 1555 + }, + { + "count": 4, + "endOffset": 1976, + "startOffset": 1855 + } + ] + }, + { + "functionName": "get consoleFromVM", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2217, + "startOffset": 2164 + } + ] + }, + { + "functionName": "set consoleFromVM", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 2277, + "startOffset": 2222 + } + ] + } + ], + "scriptId": "15", + "url": "internal/util/inspector.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 2, + "endOffset": 44873, + "startOffset": 0 + } + ] + }, + { + "functionName": "isPathSeparator", + "isBlockCoverage": true, + "ranges": [ + { + "count": 146987, + "endOffset": 1635, + "startOffset": 1529 + }, + { + "count": 146754, + "endOffset": 1631, + "startOffset": 1600 + } + ] + }, + { + "functionName": "isPosixPathSeparator", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1718, + "startOffset": 1639 + } + ] + }, + { + "functionName": "isWindowsDeviceRoot", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5173, + "endOffset": 1895, + "startOffset": 1722 + }, + { + "count": 4882, + "endOffset": 1822, + "startOffset": 1795 + }, + { + "count": 1041, + "endOffset": 1891, + "startOffset": 1824 + }, + { + "count": 750, + "endOffset": 1890, + "startOffset": 1863 + } + ] + }, + { + "functionName": "normalizeString", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2325, + "endOffset": 3826, + "startOffset": 1961 + }, + { + "count": 133028, + "endOffset": 3808, + "startOffset": 2184 + }, + { + "count": 130703, + "endOffset": 2245, + "startOffset": 2219 + }, + { + "count": 2325, + "endOffset": 2340, + "startOffset": 2245 + }, + { + "count": 2319, + "endOffset": 2296, + "startOffset": 2290 + }, + { + "count": 6, + "endOffset": 2340, + "startOffset": 2296 + }, + { + "count": 130709, + "endOffset": 2375, + "startOffset": 2340 + }, + { + "count": 12477, + "endOffset": 3704, + "startOffset": 2375 + }, + { + "count": 11844, + "endOffset": 2421, + "startOffset": 2408 + }, + { + "count": 734, + "endOffset": 2450, + "startOffset": 2423 + }, + { + "count": 11743, + "endOffset": 3658, + "startOffset": 2450 + }, + { + "count": 33, + "endOffset": 3439, + "startOffset": 2472 + }, + { + "count": 0, + "endOffset": 2588, + "startOffset": 2529 + }, + { + "count": 0, + "endOffset": 2648, + "startOffset": 2589 + }, + { + "count": 0, + "endOffset": 2868, + "startOffset": 2789 + }, + { + "count": 0, + "endOffset": 3283, + "startOffset": 3108 + }, + { + "count": 0, + "endOffset": 3430, + "startOffset": 3294 + }, + { + "count": 11710, + "endOffset": 3658, + "startOffset": 3439 + }, + { + "count": 9499, + "endOffset": 3540, + "startOffset": 3487 + }, + { + "count": 2211, + "endOffset": 3601, + "startOffset": 3540 + }, + { + "count": 12444, + "endOffset": 3704, + "startOffset": 3658 + }, + { + "count": 118232, + "endOffset": 3803, + "startOffset": 3704 + }, + { + "count": 1666, + "endOffset": 3746, + "startOffset": 3732 + }, + { + "count": 174, + "endOffset": 3771, + "startOffset": 3748 + }, + { + "count": 118058, + "endOffset": 3803, + "startOffset": 3771 + } + ] + }, + { + "functionName": "_format", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4453, + "startOffset": 4017 + } + ] + }, + { + "functionName": "resolve", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2319, + "endOffset": 9115, + "startOffset": 4579 + }, + { + "count": 3159, + "endOffset": 8648, + "startOffset": 4742 + }, + { + "count": 3157, + "endOffset": 4953, + "startOffset": 4780 + }, + { + "count": 0, + "endOffset": 4944, + "startOffset": 4911 + }, + { + "count": 2, + "endOffset": 5845, + "startOffset": 4953 + }, + { + "count": 0, + "endOffset": 5845, + "startOffset": 5033 + }, + { + "count": 0, + "endOffset": 6218, + "startOffset": 6053 + }, + { + "count": 0, + "endOffset": 7598, + "startOffset": 6251 + }, + { + "count": 3024, + "endOffset": 7689, + "startOffset": 7634 + }, + { + "count": 2319, + "endOffset": 8015, + "startOffset": 7691 + }, + { + "count": 2319, + "endOffset": 8333, + "startOffset": 8048 + }, + { + "count": 0, + "endOffset": 8270, + "startOffset": 8090 + }, + { + "count": 0, + "endOffset": 8433, + "startOffset": 8365 + }, + { + "count": 2319, + "endOffset": 8600, + "startOffset": 8572 + }, + { + "count": 2319, + "endOffset": 8632, + "startOffset": 8602 + }, + { + "count": 0, + "endOffset": 9109, + "startOffset": 9059 + } + ] + }, + { + "functionName": "normalize", + "isBlockCoverage": true, + "ranges": [ + { + "count": 6, + "endOffset": 12023, + "startOffset": 9187 + }, + { + "count": 0, + "endOffset": 9308, + "startOffset": 9297 + }, + { + "count": 0, + "endOffset": 9622, + "startOffset": 9465 + }, + { + "count": 0, + "endOffset": 11204, + "startOffset": 9655 + }, + { + "count": 5, + "endOffset": 11582, + "startOffset": 11278 + }, + { + "count": 0, + "endOffset": 11708, + "startOffset": 11697 + }, + { + "count": 0, + "endOffset": 11751, + "startOffset": 11737 + }, + { + "count": 0, + "endOffset": 11771, + "startOffset": 11760 + }, + { + "count": 0, + "endOffset": 11863, + "startOffset": 11850 + }, + { + "count": 1, + "endOffset": 11950, + "startOffset": 11895 + }, + { + "count": 0, + "endOffset": 11935, + "startOffset": 11922 + }, + { + "count": 5, + "endOffset": 11996, + "startOffset": 11950 + }, + { + "count": 0, + "endOffset": 12017, + "startOffset": 11997 + } + ] + }, + { + "functionName": "isAbsolute", + "isBlockCoverage": true, + "ranges": [ + { + "count": 317, + "endOffset": 12477, + "startOffset": 12096 + }, + { + "count": 0, + "endOffset": 12220, + "startOffset": 12207 + }, + { + "count": 316, + "endOffset": 12380, + "startOffset": 12345 + }, + { + "count": 160, + "endOffset": 12424, + "startOffset": 12381 + }, + { + "count": 116, + "endOffset": 12470, + "startOffset": 12425 + } + ] + }, + { + "functionName": "join", + "isBlockCoverage": true, + "ranges": [ + { + "count": 6, + "endOffset": 14671, + "startOffset": 12552 + }, + { + "count": 0, + "endOffset": 12614, + "startOffset": 12603 + }, + { + "count": 13, + "endOffset": 12926, + "startOffset": 12697 + }, + { + "count": 12, + "endOffset": 12910, + "startOffset": 12838 + }, + { + "count": 6, + "endOffset": 12863, + "startOffset": 12838 + }, + { + "count": 7, + "endOffset": 12910, + "startOffset": 12863 + }, + { + "count": 0, + "endOffset": 12978, + "startOffset": 12967 + }, + { + "count": 0, + "endOffset": 14271, + "startOffset": 13851 + }, + { + "count": 0, + "endOffset": 14499, + "startOffset": 14466 + }, + { + "count": 0, + "endOffset": 14620, + "startOffset": 14579 + } + ] + }, + { + "functionName": "relative", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18564, + "startOffset": 14979 + } + ] + }, + { + "functionName": "toNamespacedPath", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1082, + "endOffset": 19630, + "startOffset": 18571 + }, + { + "count": 0, + "endOffset": 18702, + "startOffset": 18690 + }, + { + "count": 0, + "endOffset": 18759, + "startOffset": 18733 + }, + { + "count": 0, + "endOffset": 18865, + "startOffset": 18853 + }, + { + "count": 0, + "endOffset": 19300, + "startOffset": 18929 + }, + { + "count": 0, + "endOffset": 19629, + "startOffset": 19605 + } + ] + }, + { + "functionName": "dirname", + "isBlockCoverage": true, + "ranges": [ + { + "count": 496, + "endOffset": 22114, + "startOffset": 19702 + }, + { + "count": 0, + "endOffset": 19821, + "startOffset": 19810 + }, + { + "count": 0, + "endOffset": 20090, + "startOffset": 19926 + }, + { + "count": 0, + "endOffset": 21455, + "startOffset": 20153 + }, + { + "count": 0, + "endOffset": 21602, + "startOffset": 21599 + }, + { + "count": 5695, + "endOffset": 21973, + "startOffset": 21732 + }, + { + "count": 496, + "endOffset": 21871, + "startOffset": 21782 + }, + { + "count": 5199, + "endOffset": 21966, + "startOffset": 21871 + }, + { + "count": 0, + "endOffset": 22077, + "startOffset": 21997 + } + ] + }, + { + "functionName": "basename", + "isBlockCoverage": true, + "ranges": [ + { + "count": 114, + "endOffset": 24930, + "startOffset": 22214 + }, + { + "count": 0, + "endOffset": 22298, + "startOffset": 22271 + }, + { + "count": 0, + "endOffset": 22778, + "startOffset": 22761 + }, + { + "count": 0, + "endOffset": 22807, + "startOffset": 22779 + }, + { + "count": 0, + "endOffset": 24318, + "startOffset": 22809 + }, + { + "count": 1326, + "endOffset": 24848, + "startOffset": 24371 + }, + { + "count": 114, + "endOffset": 24652, + "startOffset": 24421 + }, + { + "count": 1212, + "endOffset": 24841, + "startOffset": 24652 + }, + { + "count": 114, + "endOffset": 24841, + "startOffset": 24674 + }, + { + "count": 0, + "endOffset": 24889, + "startOffset": 24879 + } + ] + }, + { + "functionName": "extname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27073, + "startOffset": 25002 + } + ] + }, + { + "functionName": "parse", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31903, + "startOffset": 27291 + } + ] + }, + { + "functionName": "resolve", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33025, + "startOffset": 32100 + } + ] + }, + { + "functionName": "normalize", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33694, + "startOffset": 33097 + } + ] + }, + { + "functionName": "isAbsolute", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33899, + "startOffset": 33767 + } + ] + }, + { + "functionName": "join", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 34405, + "startOffset": 33974 + } + ] + }, + { + "functionName": "relative", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36780, + "startOffset": 34502 + } + ] + }, + { + "functionName": "toNamespacedPath", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36866, + "startOffset": 36787 + } + ] + }, + { + "functionName": "dirname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 37590, + "startOffset": 36938 + } + ] + }, + { + "functionName": "basename", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 40083, + "startOffset": 37690 + } + ] + }, + { + "functionName": "extname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 41863, + "startOffset": 40155 + } + ] + }, + { + "functionName": "parse", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 44518, + "startOffset": 42086 + } + ] + } + ], + "scriptId": "16", + "url": "path.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 8643, + "startOffset": 0 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1882, + "startOffset": 1855 + } + ] + }, + { + "functionName": "unenroll", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3615, + "startOffset": 2396 + } + ] + }, + { + "functionName": "enroll", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4089, + "startOffset": 3827 + } + ] + }, + { + "functionName": "setTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4800, + "startOffset": 4127 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5019, + "startOffset": 4881 + } + ] + }, + { + "functionName": "clearTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5407, + "startOffset": 5028 + } + ] + }, + { + "functionName": "setInterval", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6086, + "startOffset": 5411 + } + ] + }, + { + "functionName": "clearInterval", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6393, + "startOffset": 6090 + } + ] + }, + { + "functionName": "Timeout.close", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6477, + "startOffset": 6423 + } + ] + }, + { + "functionName": "Timeout.", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6689, + "startOffset": 6521 + } + ] + }, + { + "functionName": "setImmediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7259, + "startOffset": 6694 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7482, + "startOffset": 7342 + } + ] + }, + { + "functionName": "clearImmediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7977, + "startOffset": 7493 + } + ] + } + ], + "scriptId": "17", + "url": "timers.js" + } + ] + } +} diff --git a/branch-beta/.artifact/apidoc.html b/branch-beta/.artifact/apidoc.html new file mode 100644 index 000000000..c6101547c --- /dev/null +++ b/branch-beta/.artifact/apidoc.html @@ -0,0 +1,2076 @@ + + + + + + +JSLint apidoc + + + +
    +

    API Doc for JSLint (v2024.11.24)

    +
    + +

    Table of Contents

    +
    + +
    + +
    +

    Module "./jslint.mjs"

    +
      + +
    • +

      + 1. + function assertErrorThrownAsync(asyncFunc, regexp) + +

      +
    • +
    • Description and source-code:
      async function assertErrorThrownAsync(asyncFunc, regexp) {
      +
      +// This function will assert calling <asyncFunc> throws an error.
      +
      +    let err;
      +    try {
      +        await asyncFunc();
      +    } catch (errCaught) {
      +        err = errCaught;
      +    }
      +    assertOrThrow(err, "No error thrown.");
      +    assertOrThrow(
      +        !regexp || new RegExp(regexp).test(err.message),
      +        err
      +    );
      +}
    • +
    • Example usage:
      ...
      +
      +jstestDescribe((
      +"test v8CoverageReportCreate handling-behavior"
      +), function testBehaviorV8CoverageReportCreate() {
      +jstestIt((
      +    "test null-case handling-behavior"
      +), async function () {
      +    await assertErrorThrownAsync(function () {
      +        return v8CoverageReportCreate({});
      +    }, "invalid coverageDir");
      +});
      +jstestIt((
      +    "test coverage-report jslint.mjs handling-behavior"
      +), async function () {
      +    // test remove-old-coverage handling-behavior
      +...
    • + +
    • +

      + 2. + function assertJsonEqual(aa, bb, message) + +

      +
    • +
    • Description and source-code:
      function assertJsonEqual(aa, bb, message) {
      +
      +// This function will assert JSON.stringify(<aa>) === JSON.stringify(<bb>).
      +
      +    aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa), undefined, 1);
      +    bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb), undefined, 1);
      +    if (aa !== bb) {
      +        throw new Error(
      +            "\n" + aa + "\n!==\n" + bb
      +            + (
      +                typeof message === "string"
      +                ? " - " + message
      +                : message
      +                ? " - " + JSON.stringify(message)
      +                : ""
      +            )
      +        );
      +    }
      +}
    • +
    • Example usage:
      ...
      +
      +// Debug data1.
      +// await moduleFs.promises.writeFile(
      +//     ".test_v8_coverage_node_sqlite_merged.json",
      +//     JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n"
      +// );
      +
      +    assertJsonEqual(data1, data2);
      +});
      +});
      +
      +jstestDescribe((
      +"test v8CoverageReportCreate handling-behavior"
      +), function testBehaviorV8CoverageReportCreate() {
      +jstestIt((
      +...
    • + +
    • +

      + 3. + function assertOrThrow(condition, message) + +

      +
    • +
    • Description and source-code:
      function assertOrThrow(condition, message) {
      +
      +// This function will throw <message> if <condition> is falsy.
      +
      +    if (!condition) {
      +        throw (
      +            (!message || typeof message === "string")
      +            ? new Error(String(message).slice(0, 2048))
      +            : message
      +        );
      +    }
      +}
    • +
    • Example usage:
      ...
      +            assertJsonEqual(1, 2, "undefined");
      +        });
      +        await assertErrorThrownAsync(function () {
      +            assertJsonEqual(1, 2, {});
      +        });
      +        // test assertOrThrow error handling-behavior
      +        await assertErrorThrownAsync(function () {
      +            assertOrThrow(undefined, "undefined");
      +        });
      +        await assertErrorThrownAsync(function () {
      +            assertOrThrow(undefined, new Error());
      +        });
      +    });
      +});
      +...
    • + +
    • +

      + 4. + function debugInline(...argv) + +

      +
    • +
    • Description and source-code:
      function debug(...argv) {
      +
      +// This function will print <argv> to stderr and then return <argv>[0].
      +
      +    __consoleError("\n\ndebugInline");
      +    __consoleError(...argv);
      +    __consoleError("\n");
      +    return argv[0];
      +}
    • +
    • Example usage:
      N/A
    • + +
    • +

      + 5. + function fsWriteFileWithParents(pathname, data) + +

      +
    • +
    • Description and source-code:
      async function fsWriteFileWithParents(pathname, data) {
      +
      +// This function will write <data> to <pathname> and lazy-mkdirp if necessary.
      +
      +    await moduleFsInit();
      +
      +// Try writing to pathname.
      +
      +    try {
      +        await moduleFs.promises.writeFile(pathname, data);
      +    } catch (ignore) {
      +
      +// Lazy mkdirp.
      +
      +        await moduleFs.promises.mkdir(modulePath.dirname(pathname), {
      +            recursive: true
      +        });
      +
      +// Retry writing to pathname.
      +
      +        await moduleFs.promises.writeFile(pathname, data);
      +    }
      +    console.error("wrote file " + pathname);
      +}
    • +
    • Example usage:
      ...
      +                }
      +            ]
      +        }, undefined, 4)
      +    ]
      +].map(async function ([
      +    file, data
      +]) {
      +    await fsWriteFileWithParents(file, data);
      +}));
      +await jslint.jslint_cli({
      +    console_error: noop, // comment to debug
      +    mode_cli: true,
      +    process_argv: [
      +        "node", "jslint.mjs",
      +        "v8_coverage_report=.tmp/coverage_misc"
      +...
    • + +
    • +

      + 6. + function globExclude({ + excludeList = [], + includeList = [], + pathnameList = [] +}) + +

      +
    • +
    • Description and source-code:
      function globExclude({
      +    excludeList = [],
      +    includeList = [],
      +    pathnameList = []
      +}) {
      +
      +// This function will
      +// 1. Exclude pathnames in <pathnameList> that don't match glob-patterns in
      +//    <includeList>.
      +// 2. Exclude pathnames in <pathnameList> that match glob-patterns in
      +//    <excludeList>.
      +
      +    function globAssertNotWeird(list, name) {
      +
      +// This function will check if <list> of strings contain weird characters.
      +
      +        [
      +            [
      +                "\n", (
      +                    /^.*?([\u0000-\u0007\r]).*/gm
      +                )
      +            ],
      +            [
      +                "\r", (
      +                    /^.*?([\n]).*/gm
      +                )
      +            ]
      +        ].forEach(function ([
      +            separator, rgx
      +        ]) {
      +            list.join(separator).replace(rgx, function (match0, char) {
      +                throw new Error(
      +                    "Weird character "
      +                    + JSON.stringify(char)
      +                    + " found in " + name + " "
      +                    + JSON.stringify(match0)
      +                );
      +            });
      +        });
      +    }
      +
      +    function globToRegexp(pattern) {
      +
      +// This function will translate glob <pattern> to javascript-regexp,
      +// which javascript can then use to "glob" pathnames.
      +
      +        let ii = 0;
      +        let isClass = false;
      +        let strClass = "";
      +        let strRegex = "";
      +        pattern = pattern.replace((
      +            /\/\/+/g
      +        ), "/");
      +        pattern = pattern.replace((
      +            /\*\*\*+/g
      +        ), "**");
      +        pattern.replace((
      +            /\\\\|\\\[|\\\]|\[|\]|./g
      +        ), function (match0) {
      +            switch (match0) {
      +            case "[":
      +                if (isClass) {
      +                    strClass += "[";
      +                    return;
      +                }
      +                strClass += "\u0000";
      +                strRegex += "\u0000";
      +                isClass = true;
      +                return;
      +            case "]":
      +                if (isClass) {
      +                    isClass = false;...
      +}
      +
    • +
    • Example usage:
      ...
      +    "test/support/helper.js": (
      +        /^test\/support\/helper\.js$/gm
      +    )
      +}).forEach(function ([
      +    pattern, rgx
      +]) {
      +    assertJsonEqual(
      +        globExclude({
      +            excludeList: [
      +                pattern
      +            ]
      +        }).excludeList[0].source,
      +        rgx.source
      +    );
      +    assertJsonEqual(
      +...
    • + +
    • +

      + 7. + function htmlEscape(str) + +

      +
    • +
    • Description and source-code:
      function htmlEscape(str) {
      +
      +// This function will make <str> html-safe by escaping & < >.
      +
      +    return String(str).replace((
      +        /&/g
      +    ), "&amp;").replace((
      +        /</g
      +    ), "&lt;").replace((
      +        />/g
      +    ), "&gt;");
      +}
    • +
    • Example usage:
      ...
      +                            inHole = isHole;
      +                        }
      +                        chunk += char;
      +                    });
      +                    lineHtml += htmlEscape(chunk);
      +                    break;
      +                default:
      +                    lineHtml += htmlEscape(line);
      +                }
      +                html += String(`
      +<pre>
      +<span class="lineno">
      +<a href="#${lineId}" id="${lineId}">${String(ii + 1).padStart(5, " ")}.</a>
      +</span>
      +<span class="count
      +...
    • + +
    • +

      + 8. + function jslint( + source = "", + option_dict = empty(), + global_list = [] +) + +

      +
    • +
    • Description and source-code:
      function jslint(
      +    source = "",                // A text to analyze.
      +    option_dict = empty(),      // An object whose keys correspond to option
      +                                // ... names.
      +    global_list = []            // An array of strings containing global
      +                                // ... variables that the file is allowed
      +                                // ... readonly access.
      +) {
      +
      +// The jslint function itself.
      +
      +    let catch_list = [];        // The array containing all catch-blocks.
      +    let catch_stack = [         // The stack of catch-blocks.
      +        {
      +            context: empty()
      +        }
      +    ];
      +    let cause_dict = empty();   // The object of test-causes.
      +    let directive_list = [];    // The directive comments.
      +    let export_dict = empty();  // The exported names and values.
      +    let function_list = [];     // The array containing all functions.
      +    let function_stack = [];    // The stack of functions.
      +    let global_dict = empty();  // The object containing the global
      +                                // ... declarations.
      +    let import_list = [];       // The array collecting all import-from strings.
      +    let line_list = String(     // The array containing source lines.
      +        "\n" + source
      +    ).split(jslint_rgx_crlf).map(function (line_source) {
      +        return {
      +            line_source
      +        };
      +    });
      +    let mode_stop = false;      // true if JSLint cannot finish.
      +    let property_dict = empty();        // The object containing the tallied
      +                                        // ... property names.
      +    let state = empty();        // jslint state-object to be passed between
      +                                // jslint functions.
      +    let syntax_dict = empty();  // The object containing the parser.
      +    let tenure = empty();       // The predefined property registry.
      +    let token_global = {        // The global object; the outermost context.
      +        async: 0,
      +        body: true,
      +        context: empty(),
      +        finally: 0,
      +        from: 0,
      +...
      +}
      +
    • +
    • Example usage:
      ...
      +import fs from "fs";
      +(async function () {
      +    let result;
      +    let source = "function foo() {console.log(\u0027hello world\u0027);}\n";
      +
      +// Create JSLint report from <source> in javascript.
      +
      +    result = jslint.jslint(source);
      +    result = jslint.jslint_report(result);
      +    result = `<body class="JSLINT_ JSLINT_REPORT_">\n${result}</body>\n`;
      +
      +    await fs.promises.mkdir(".artifact/", {recursive: true});
      +    await fs.promises.writeFile(".artifact/jslint_report_hello.html", result);
      +    console.error("wrote file .artifact/jslint_report_hello.html");
      +}());
      +...
    • + +
    • +

      + 9. + function jslint_apidoc({ + example_list, + github_repo, + module_list, + package_name, + pathname, + version +}) + +

      +
    • +
    • Description and source-code:
      async function jslint_apidoc({
      +    example_list,
      +    github_repo,
      +    module_list,
      +    package_name,
      +    pathname,
      +    version
      +}) {
      +
      +// This function will create API Doc from <module_list>.
      +
      +    let elem_ii = 0;
      +    let html;
      +
      +    function elem_create(moduleObj, key, moduleName) {
      +
      +// This function will create a sub API Doc from elem <moduleObj>[<key>].
      +
      +        let example = "N/A";
      +        let id = encodeURIComponent("apidoc.elem." + moduleName + "." + key);
      +        let name;
      +        let signature;
      +        let source;
      +        name = htmlEscape((typeof moduleObj[key]) + " " + key);
      +        if (typeof moduleObj[key] !== "function") {
      +            return {
      +                name,
      +                signature: (`
      +<a class="apidocElementLiA" href="#${id}">
      +${name}
      +</a>
      +                `),
      +                source: (`
      +<li>
      +    <h2>
      +    <a href="#${id}" id="${id}">
      +    ${name}
      +    </a>
      +    </h2>
      +</li>
      +                `)
      +            };
      +        }
      +        // init source
      +        source = htmlEscape(trim_start(moduleObj[key].toString()));
      +        // init signature
      +        source = source.replace((
      +            /(\([\S\s]*?\)) \{/
      +        ), function (match0, match1) {
      +            signature = htmlEscape(
      +                match1.replace((
      +                    / *?\/\*[\S\s]*?\*\/ */g
      +                ), "").replace((
      +                    / *?\/\/.*/g
      +                ), "").replace((
      +                    /\n{2,}/g
      +                ), "\n")
      +            );
      +            return match0;
      +        });
      +        // init comment
      +        source = source.replace((
      +            /\n(?:\/\/.*?\n)+\n/
      +        ), "<span class=\"apidocCodeCommentSpan\">$&</span>");
      +        // init example
      +        example_list.some(function (example2) {
      +            example2.replace(
      +                new RegExp((
      +                    "((?:\\n.*?){8}(function )?)\\b"
      +                    + key
      +                    + "(\\((?:.*?\\n){8})"
      +                ), "g"),
      +  ...
      +}
      +
    • +
    • Example usage:
      ...
      +command[1] = command.slice(1).join("=");
      +
      +switch (command[0]) {
      +
      +// PR-362 - Add API Doc.
      +
      +case "jslint_apidoc":
      +    await jslint_apidoc(Object.assign(JSON.parse(process_argv[3]), {
      +        pathname: command[1]
      +    }));
      +    return;
      +
      +// PR-363 - Add command jslint_report.
      +
      +case "jslint_report":
      +...
    • + +
    • +

      + 10. + function jslint_assert(condition, message) + +

      +
    • +
    • Description and source-code:
      function jslint_assert(condition, message) {
      +
      +// This function will throw <message> if <condition> is falsy.
      +
      +    if (condition) {
      +        return condition;
      +    }
      +    throw new Error(
      +        `This was caused by a bug in JSLint.
      +Please open an issue with this stack-trace (and possible example-code) at
      +https://github.com/jslint-org/jslint/issues.
      +edition = "${jslint_edition}";
      +${String(message).slice(0, 2000)}`
      +    );
      +}
    • +
    • Example usage:
      ...
      +// ["let aa={};", "whitage", "opener", "", 0]
      +
      +test_cause("opener");
      +
      +// Probably deadcode.
      +// case "${}":
      +
      +jslint_assert(
      +    !(left.id + right.id === "${}"),
      +    "Expected !(left.id + right.id === \"${}\")."
      +);
      +switch (left.id + right.id) {
      +case "()":
      +case "[]":
      +case "{}":
      +...
    • + +
    • +

      + 11. + function jslint_cli({ + console_error, + console_log, + file, + import_meta_url, + mode_cli, + mode_noop, + option, + process_argv, + process_env, + process_exit, + source +}) + +

      +
    • +
    • Description and source-code:
      async function jslint_cli({
      +    console_error,
      +    console_log,
      +    file,
      +    import_meta_url,
      +    mode_cli,
      +    mode_noop,
      +    option,
      +    process_argv,
      +    process_env,
      +    process_exit,
      +    source
      +}) {
      +
      +// This function will run jslint from nodejs-cli.
      +
      +    let command;
      +    let data;
      +    let exit_code = 0;
      +    let mode_report;
      +    let mode_wrapper_vim;
      +    let result;
      +
      +    function jslint_from_file({
      +        code,
      +        file,
      +        line_offset = 0,
      +        mode_conditional,
      +        option = empty()
      +    }) {
      +        let result_from_file;
      +        if (
      +            mode_conditional
      +            && !(
      +                /^\/\*jslint\b/m
      +            ).test(code.slice(0, 65536))
      +        ) {
      +            return;
      +        }
      +        option = Object.assign(empty(), option, {
      +            file
      +        });
      +        switch ((
      +            /\.\w+?$|$/m
      +        ).exec(file)[0]) {
      +        case ".html":
      +
      +// Recursively jslint embedded "<script>\n...\n</script>".
      +
      +            code.replace((
      +                /^<script\b[^>]*?>\n([\S\s]*?\n)<\/script>$/gm
      +            ), function (ignore, match1, ii) {
      +                jslint_from_file({
      +                    code: match1,
      +                    file: file + ".<script>.js",
      +                    line_offset: string_line_count(code.slice(0, ii)) + 1,
      +                    option: Object.assign(empty(), {
      +                        browser: true
      +                    }, option)
      +                });
      +                return "";
      +            });
      +            return;
      +        case ".md":
      +
      +// Recursively jslint embedded "node --eval '\n...\n'".
      +
      +            jslint_node_eval({
      +                code,
      +                file,
      +                mode_conditional: true,
      +                option
      +            });
      +            return;
      +        case ".sh":
      +
      +// Recursively jslint embedded "node --eval '\n...\n'".
      +
      +            jslint_node_eval({
      +                code,
      +                file,
      +                option
      +            });
      +            return;
      +       ...
      +}
      +
    • +
    • Example usage:
      ...
      +        }, undefined, 4)
      +    ]
      +].map(async function ([
      +    file, data
      +]) {
      +    await fsWriteFileWithParents(file, data);
      +}));
      +await jslint.jslint_cli({
      +    console_error: noop, // comment to debug
      +    mode_cli: true,
      +    process_argv: [
      +        "node", "jslint.mjs",
      +        "v8_coverage_report=.tmp/coverage_misc"
      +        // "node", ".tmp/coverage_misc/aa.js"
      +    ]
      +...
    • + +
    • +

      + 12. + function jslint_phase1_split() + +

      +
    • +
    • Description and source-code:
      function jslint_phase1_split() {
      +
      +// PHASE 1. Split <source> by newlines into <line_list>.
      +
      +    return;
      +}
    • +
    • Example usage:
      ...
      +            warn,
      +            warn_at,
      +            warning_list
      +        });
      +
      +// PHASE 1. Split <source> by newlines into <line_list>.
      +
      +        jslint_phase1_split(state);
      +        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
      +        jslint_assert(
      +            function_stack.length === 0,
      +            `function_stack.length === 0.`
      +        );
      +
      +// PHASE 2. Lex <line_list> into <token_list>.
      +...
    • + +
    • +

      + 13. + function jslint_phase2_lex(state) + +

      +
    • +
    • Description and source-code:
      function jslint_phase2_lex(state) {
      +
      +// PHASE 2. Lex <line_list> into <token_list>.
      +
      +    let {
      +        artifact,
      +        directive_list,
      +        global_dict,
      +        global_list,
      +        line_list,
      +        option_dict,
      +        stop,
      +        stop_at,
      +        tenure,
      +        test_cause,
      +        token_global,
      +        token_list,
      +        warn,
      +        warn_at
      +    } = state;
      +    let char;                   // The current character being lexed.
      +    let column = 0;             // The column number of the next character.
      +    let from;                   // The starting column number of the token.
      +    let from_mega;              // The starting column of megastring.
      +    let line = 0;               // The line number of the next character.
      +    let line_disable;           // The starting line of "/*jslint-disable*/".
      +    let line_mega;              // The starting line of megastring.
      +    let line_source = "";       // The remaining line source string.
      +    let line_whole = "";        // The whole line source string.
      +    let mode_digits_empty_string = 1;
      +    let mode_digits_numeric_separator = 2;
      +    let mode_directive = true;  // true if directives are still allowed.
      +    let mode_mega = false;      // true if currently parsing a megastring
      +                                // ... literal.
      +    let mode_regexp;            // true if regular expression literal seen on
      +                                // ... this line.
      +    let paren_backtrack_list = [];      // List of most recent "(" tokens at any
      +                                        // ... paren-depth.
      +    let paren_depth = 0;        // Keeps track of current paren-depth.
      +    let snippet = "";           // A piece of string.
      +    let token_1;                // The first token.
      +    let token_prv = token_global;       // The previous token including
      +                                        // ... comments.
      +    let token_prv_expr = token_global;  // The previous token excluding
      +                                        // ... comm...
      +}
      +
    • +
    • Example usage:
      ...
      +        jslint_assert(
      +            function_stack.length === 0,
      +            `function_stack.length === 0.`
      +        );
      +
      +// PHASE 2. Lex <line_list> into <token_list>.
      +
      +        jslint_phase2_lex(state);
      +        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
      +        jslint_assert(
      +            function_stack.length === 0,
      +            `function_stack.length === 0.`
      +        );
      +
      +// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
      +...
    • + +
    • +

      + 14. + function jslint_phase3_parse(state) + +

      +
    • +
    • Description and source-code:
      function jslint_phase3_parse(state) {
      +
      +// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
      +
      +// Parsing:
      +
      +// Parsing weaves the tokens into an abstract syntax tree. During that process,
      +// a token may be given any of these properties:
      +
      +//      arity       string
      +//      label       identifier
      +//      name        identifier
      +//      expression  expressions
      +//      block       statements
      +//      else        statements (else, default, catch)
      +
      +// Specialized tokens may have additional properties.
      +
      +    let anon = "anonymous";     // The guessed name for anonymous functions.
      +    let {
      +        artifact,
      +        catch_list,
      +        catch_stack,
      +        export_dict,
      +        function_list,
      +        function_stack,
      +        global_dict,
      +        import_list,
      +        is_equal,
      +        option_dict,
      +        property_dict,
      +        stop,
      +        syntax_dict,
      +        tenure,
      +        test_cause,
      +        token_global,
      +        token_list,
      +        warn,
      +        warn_at
      +    } = state;
      +    let catchage = catch_stack[0];      // The current catch-block.
      +    let functionage = token_global;     // The current function.
      +    let mode_var;               // "var" if using var; "let" if using let.
      +    let token_ii = 0;           // The number of the next token.
      +    let token_now = token_global;       // The current token being examined in
      +                                        // ... the parse.
      +    let token_nxt = token_global;       // The next token to be examined in
      +                                        // ... <token_list>.
      +
      +    function advance(id, match) {
      +
      +// Produce the next token.
      +
      +// Attempt to give helpful names to anonymous functions.
      +
      +        if (
      +            token_now.identifier
      +            && token_now.id !== "function"
      +            && token_now.id !== "async"
      +        ) {
      +            anon = token_now.id;
      +        } else if (
      +            token_now.id === "(string)"
      +            && jslint_rgx_identifier.test(token_now.value)
      +       ...
      +}
      +
    • +
    • Example usage:
      ...
      +        jslint_assert(
      +            function_stack.length === 0,
      +            `function_stack.length === 0.`
      +        );
      +
      +// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
      +
      +        jslint_phase3_parse(state);
      +        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
      +        jslint_assert(
      +            function_stack.length === 0,
      +            `function_stack.length === 0.`
      +        );
      +
      +// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
      +...
    • + +
    • +

      + 15. + function jslint_phase4_walk(state) + +

      +
    • +
    • Description and source-code:
      function jslint_phase4_walk(state) {
      +
      +// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
      +//          recursive traversal. Each node may be processed on the way down
      +//          (preaction) and on the way up (postaction).
      +
      +    let {
      +        artifact,
      +        catch_stack,
      +        function_stack,
      +        global_dict,
      +        is_equal,
      +        is_weird,
      +        option_dict,
      +        syntax_dict,
      +        test_cause,
      +        token_global,
      +        warn
      +    } = state;
      +    let block_stack = [];               // The stack of blocks.
      +    let blockage = token_global;        // The current block.
      +    let catchage = catch_stack[0];      // The current catch-block.
      +    let functionage = token_global;     // The current function.
      +    let postaction;
      +    let postamble;
      +    let posts = empty();
      +    let preaction;
      +    let preamble;
      +    let pres = empty();
      +
      +// The relational operators.
      +
      +    let relationop = object_assign_from_list(empty(), [
      +        "!=", "!==", "<", "<=", "==", "===", ">", ">="
      +    ], true);
      +
      +// Ambulation of the parse tree.
      +
      +    function action(when) {
      +
      +// Produce a function that will register task functions that will be called as
      +// the tree is traversed.
      +
      +        return function (arity, id, task) {
      +            let a_set = when[arity];
      +            let i_set;
      +
      +// The id parameter is optional. If excluded, the task will be applied to all
      +// ids.
      +
      +            if (typeof id !== "string") {
      +                task = id;
      +                id = "(all)";
      +            }
      +
      +// If this arity has no registrations yet, then create a set object to hold
      +// them.
      +
      +            if (a_set === undefined) {
      +                a_set = empty();
      +                when[arity] = a_set;
      +            }
      +
      +// If this id has no registrations yet, then create a set array to hold them.
      +
      +            i_set = a_set[id];
      +            if (i_set === undefined) {
      +                i_set = [];
      +                a_set[id] = i_set;
      +            }
      +
      +// Register the task with the arity and the id.
      +...
      +}
      +
    • +
    • Example usage:
      ...
      +);
      +
      +// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
      +//          recursive traversal. Each node may be processed on the way down
      +//          (preaction) and on the way up (postaction).
      +
      +if (!state.mode_json) {
      +    jslint_phase4_walk(state);
      +}
      +jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
      +jslint_assert(
      +    function_stack.length === 0,
      +    `function_stack.length === 0.`
      +);
      +...
    • + +
    • +

      + 16. + function jslint_phase5_whitage(state) + +

      +
    • +
    • Description and source-code:
      function jslint_phase5_whitage(state) {
      +
      +// PHASE 5. Check whitespace between tokens in <token_list>.
      +
      +    let {
      +        artifact,
      +        catch_list,
      +        function_list,
      +        function_stack,
      +        option_dict,
      +        test_cause,
      +        token_global,
      +        token_list,
      +        warn
      +    } = state;
      +    let closer = "(end)";
      +    let free = false;
      +
      +// free = false
      +
      +// cause:
      +// "()=>0"
      +// "aa()"
      +// "aa(0,0)"
      +// "function(){}"
      +
      +// free = true
      +
      +// cause:
      +// "(0)"
      +// "(aa)"
      +// "aa(0)"
      +// "do{}while()"
      +// "for(){}"
      +// "if(){}"
      +// "switch(){}"
      +// "while(){}"
      +
      +    let left = token_global;
      +    let margin = 0;
      +    let mode_indent = (
      +
      +// PR-330 - Allow 2-space indent.
      +
      +        option_dict.indent2
      +        ? 2
      +        : 4
      +    );
      +    let nr_comments_skipped = 0;
      +    let open = true;
      +    let opening = true;
      +    let right;
      +
      +// This is the set of infix operators that require a space on each side.
      +
      +    let spaceop = object_assign_from_list(empty(), [
      +        "!=", "!==", "%", "%=", "&", "&&", "&=", "*", "*=", "+=", "-=", "/",
      +        "/=", "<", "<<", "<<=", "<=", "=", "==", "===", "=>", ">", ">=", ">>",
      +        ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||"
      +    ], true);
      +
      +    function at_margin(fit) {
      +        const at = margin + fit;
      +        if (right.from !== at) {
      +            return expected_at(at);
      +        }
      +    }
      +
      +    function delve(the_function) {
      +        Object.keys(the_function.context).forEach(function (id) {
      +            const name = the_function.context[id];
      +            if (id !== "ignore" && name.parent === the_function) {
      +
      +// test_cause:
      +// ["function aa(aa) {return aa;}", "delve", "id", "", 0]
      +
      +                test_cause("id");
      +                if (
      +                    name.used === 0
      +
      +// Probably deadcode.
      +// && (
      +//     name.role !== "function"
      +//     || name.parent.arity !== "unary"
      +// )
      +
      +                    && jslint_assert(
      +                        name.role...
      +}
      +
    • +
    • Example usage:
      ...
      +    function_stack.length === 0,
      +    `function_stack.length === 0.`
      +);
      +
      +// PHASE 5. Check whitespace between tokens in <token_list>.
      +
      +if (!state.mode_json && warning_list.length === 0) {
      +    jslint_phase5_whitage(state);
      +}
      +jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
      +jslint_assert(
      +    function_stack.length === 0,
      +    `function_stack.length === 0.`
      +);
      +...
    • + +
    • +

      + 17. + function jslint_report({ + exports, + froms, + functions, + global, + json, + module, + property, + stop, + warnings +}) + +

      +
    • +
    • Description and source-code:
      function jslint_report({
      +    exports,
      +    froms,
      +    functions,
      +    global,
      +    json,
      +    module,
      +    property,
      +    stop,
      +    warnings
      +}) {
      +
      +// This function will create human-readable, html-report
      +// for warnings, properties, and functions from jslint-result-object.
      +//
      +// Example usage:
      +//  let result = jslint("console.log('hello world')");
      +//  let html = jslint_report(result);
      +
      +    let html = "";
      +    let length_80 = 1111;
      +
      +    function address(line = 1, column = 1) {
      +
      +// This function will create HTML address element from <line> and <column>
      +
      +        return `<address>${Number(line)}: ${Number(column)}</address>`;
      +
      +    }
      +
      +    function detail(title, list) {
      +        return (
      +            (Array.isArray(list) && list.length > 0)
      +            ? (
      +
      +// Google Lighthouse Accessibility - <dl>'s do not contain only properly-ordered
      +// <dt> and <dd> groups, <script>, <template> or <div> elements.
      +
      +                "<dl>"
      +                + "<dt>" + htmlEscape(title) + "</dt>"
      +                + "<dd>" + list.join(", ") + "</dd>"
      +                + "</dl>"
      +            )
      +            : ""
      +        );
      +    }
      +
      +    html += String(`
      +<style class="JSLINT_REPORT_STYLE">
      +/* jslint utility2:true */
      +/*csslint box-model: false, ids:false */
      +/*csslint ignore:start*/
      +@font-face {
      +    font-display: swap;
      +    font-family: "Daley";
      +    src: url(
      +"data:font/woff2;base64,d09GMgABAAAAABy4AA4AAAAAThwAABxiAAEAAAAAAAAAAAAA\
      +AAAAAAAAAAAAAAAABmAAgiQINAmcDBEICuc41DEBNgIkA4R2C4I+AAQgBYkuByAMgScfYUIF\
      +7NgjsHGAbcDVFkXZ5Jwd+P96IGPc9rl9ETBEaCzCJkvY2UpziRZ7zftZWk8052U9+NqX6vXL\
      +KDflSQnlJ0bP+QnPQAy744n9mup6H9PaCDFwM5zjf8exB89bZ1cdrYOP0NgnuRDRWlk9u/fE\
      +llkxqmfH8lmRQ/DAmER9opk9wR6suc1LvTiXNEe1vbhUCH2USgnEwH3vUm05JQqejGvZvOtz\
      +7sIKEGgLdDNl/IrfqWVZG/wr42ekomEm91VA1p4LhHBuFzHF8//u7vvbREHMQqGtNLmiOOD/\
      +X7WWiwqyCE98qt0jk5JJmgR5WJJElBmzRb1F7a66MmSLTNWZ2XSHfKBSKHoVteSEJ6EOdvVw\
      +fNZOtXKDe39jXdRlkmMnOWIOFBgeEK/b0mFsgffnP...
      +}
      +
    • +
    • Example usage:
      ...
      +
      +    editor.on("lintJslintAfter", function (options) {
      +
      +// Generate jslint-report from options.result.
      +
      +        document.querySelector(
      +            ".JSLINT_REPORT_"
      +        ).innerHTML = window.jslint.jslint_report(options.result);
      +    });
      +
      +// Manually trigger linter.
      +
      +    editor.performLint();
      +});
      +</script>
      +...
    • + +
    • +

      + 18. + function jstestDescribe(description, testFunction) + +

      +
    • +
    • Description and source-code:
      async function jstestDescribe(description, testFunction) {
      +
      +// This function will create-and-run test-group <testFunction>
      +// with given <description>.
      +
      +    let message;
      +    let result;
      +    let timerTimeout;
      +
      +// Init jstestTimeStart.
      +
      +    if (jstestTimeStart === undefined) {
      +        jstestTimeStart = jstestTimeStart || Date.now();
      +        process.on("exit", jstestOnExit);
      +    }
      +
      +// PR-457 - Wait awhile for imports to initialize.
      +
      +    await new Promise(function (resolve) {
      +        setTimeout(resolve);
      +    });
      +
      +// Init jstestItList.
      +
      +    jstestItList = [];
      +    testFunction();
      +
      +// Wait for jstestItList to resolve.
      +
      +    timerTimeout = setTimeout(noop, 0x7fffffff);
      +    result = await Promise.all(jstestItList);
      +    clearTimeout(timerTimeout);
      +
      +// Print test results.
      +
      +    message = (
      +        "\n  " + (Date.now() - jstestTimeStart) + "ms"
      +        + " - test describe - " + description + "\n"
      +        + result.map(function ([
      +            err, description, mode
      +        ]) {
      +            jstestItCount += 1;
      +            if (err) {
      +                jstestCountFailed += 1;
      +                err = (
      +                    "    \u001b[31m\u2718 " + jstestItCount + ". test it - "
      +                    + description + "\n" + err.stack + "\u001b[39m"
      +                );
      +                if (mode === "pass") {
      +                    jstestCountFailed -= 1;
      +                    err = "";
      +                }
      +            }
      +            return err || (
      +                "    \u001b[32m\u2714 " + jstestItCount + ". test it - "
      +                + description + "\u001b[39m"
      +            );
      +        }).join("\n")
      +    );
      +    console.error(message);
      +}
    • +
    • Example usage:
      ...
      +//     JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n"
      +// );
      +
      +    assertJsonEqual(data1, data2);
      +});
      +});
      +
      +jstestDescribe((
      +"test v8CoverageReportCreate handling-behavior"
      +), function testBehaviorV8CoverageReportCreate() {
      +jstestIt((
      +    "test null-case handling-behavior"
      +), async function () {
      +    await assertErrorThrownAsync(function () {
      +        return v8CoverageReportCreate({});
      +...
    • + +
    • +

      + 19. + function jstestIt(description, testFunction, mode) + +

      +
    • +
    • Description and source-code:
      function jstestIt(description, testFunction, mode) {
      +
      +// This function will create-and-run test-case <testFunction>
      +// inside current test-group with given <description>.
      +
      +    jstestCountTotal += 1;
      +    jstestItList.push(new Promise(async function (resolve) {
      +        let err;
      +        try {
      +            await testFunction();
      +        } catch (errCaught) {
      +            err = errCaught;
      +        }
      +        resolve([err, description, mode]);
      +    }));
      +}
    • +
    • Example usage:
      ...
      +                "v8_coverage_report=" + dir,
      +                "node",
      +                file
      +            ]
      +        });
      +    });
      +});
      +jstestIt((
      +    "test npm handling-behavior"
      +), async function () {
      +    await jslint.jslint_cli({
      +        console_error: noop, // comment to debug
      +        mode_cli: true,
      +        process_argv: [
      +            "node", "jslint.mjs",
      +...
    • + +
    • +

      + 20. + function jstestOnExit(exitCode, mode) + +

      +
    • +
    • Description and source-code:
      function jstestOnExit(exitCode, mode) {
      +
      +// This function will on process-exit, print test-report
      +// and exit with non-zero exit-code if any test failed.
      +
      +    let message = (
      +        (
      +            (jstestCountFailed || mode === "testsFailed")
      +            ? "\n\u001b[31m"
      +            : "\n\u001b[32m"
      +        )
      +        + "  tests total  - " + jstestCountTotal + "\n"
      +        + "  tests failed - " + jstestCountFailed + "\n"
      +        + "\n"
      +        + "  time finished - "
      +        + Number(Date.now() - jstestTimeStart).toLocaleString()
      +        + " ms\n"
      +        + "\u001b[39m"
      +    );
      +    if (mode !== "testsFailed") {
      +        console.error(message);
      +    }
      +    process.exitCode = exitCode || jstestCountFailed;
      +    return message;
      +}
    • +
    • Example usage:
      ...
      +    "test jstestDescribe error handling-behavior"
      +), function () {
      +    throw new Error();
      +}, "pass");
      +jstestIt((
      +    "test jstestOnExit tests-failed handling-behavior"
      +), function () {
      +    jstestOnExit(undefined, "testsFailed");
      +});
      +});
      +
      +jstestDescribe((
      +"test misc handling-behavior"
      +), function testBehaviorMisc() {
      +jstestIt((
      +...
    • + +
    • +

      + 21. + function moduleFsInit() + +

      +
    • +
    • Description and source-code:
      async function moduleFsInit() {
      +
      +// This function will import nodejs builtin-modules if they have not yet been
      +// imported.
      +
      +// State 3 - Modules already imported.
      +
      +    if (moduleFs !== undefined) {
      +        return;
      +    }
      +
      +// State 2 - Wait while modules are importing.
      +
      +    if (moduleFsInitResolveList !== undefined) {
      +        return new Promise(function (resolve) {
      +            moduleFsInitResolveList.push(resolve);
      +        });
      +    }
      +
      +// State 1 - Start importing modules.
      +
      +    moduleFsInitResolveList = [];
      +    [
      +        moduleChildProcess,
      +        moduleFs,
      +        modulePath,
      +        moduleUrl
      +    ] = await Promise.all([
      +        import("child_process"),
      +        import("fs"),
      +        import("path"),
      +        import("url")
      +    ]);
      +    while (moduleFsInitResolveList.length > 0) {
      +        moduleFsInitResolveList.shift()();
      +    }
      +}
    • +
    • Example usage:
      ...
      +let sourceJslintMjs;
      +let testCoverageMergeData;
      +
      +await (async function init() {
      +
      +// Coverage-hack - Ugly-hack to get test-coverage for all initialization-states.
      +
      +moduleFsInit();
      +moduleFsInit();
      +
      +// Cleanup directory .tmp
      +
      +await moduleFs.promises.rm(".tmp", {
      +    recursive: true
      +}).catch(noop);
      +...
    • + +
    • +

      + 22. + function noop(val) + +

      +
    • +
    • Description and source-code:
      function noop(val) {
      +
      +// This function will do nothing except return <val>.
      +
      +    return val;
      +}
    • +
    • Example usage:
      ...
      +jstestDescribe((
      +"test misc handling-behavior"
      +), function testBehaviorMisc() {
      +jstestIt((
      +    "test misc handling-behavior"
      +), async function () {
      +    // test debugInline handling-behavior
      +    noop(debugInline);
      +    // test assertErrorThrownAsync error handling-behavior
      +    await assertErrorThrownAsync(function () {
      +        return assertErrorThrownAsync(noop);
      +    });
      +    // test assertJsonEqual error handling-behavior
      +    await assertErrorThrownAsync(function () {
      +        assertJsonEqual(1, 2);
      +...
    • + +
    • +

      + 23. + function objectDeepCopyWithKeysSorted(obj) + +

      +
    • +
    • Description and source-code:
      function objectDeepCopyWithKeysSorted(obj) {
      +
      +// This function will recursively deep-copy <obj> with keys sorted.
      +
      +    let sorted;
      +    if (typeof obj !== "object" || !obj) {
      +        return obj;
      +    }
      +
      +// Recursively deep-copy list with child-keys sorted.
      +
      +    if (Array.isArray(obj)) {
      +        return obj.map(objectDeepCopyWithKeysSorted);
      +    }
      +
      +// Recursively deep-copy obj with keys sorted.
      +
      +    sorted = Object.create(null);
      +    Object.keys(obj).sort().forEach(function (key) {
      +        sorted[key] = objectDeepCopyWithKeysSorted(obj[key]);
      +    });
      +    return sorted;
      +}
    • +
    • Example usage:
      ...
      +        ];
      +        data1 = v8CoverageListMerge(data1);
      +        data1 = v8CoverageListMerge([data1]);
      +
      +// Debug data1.
      +// await moduleFs.promises.writeFile(
      +//     ".test_v8_coverage_node_sqlite_merged.json",
      +//     JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n"
      +// );
      +
      +        assertJsonEqual(data1, data2);
      +    });
      +});
      +
      +jstestDescribe((
      +...
    • + +
    • +

      + 24. + function v8CoverageListMerge(processCovs) + +

      +
    • +
    • Description and source-code:
      function v8CoverageListMerge(processCovs) {
      +
      +// This function is derived from MIT Licensed v8-coverage at
      +// https://github.com/demurgos/v8-coverage/tree/master/ts
      +// https://github.com/demurgos/v8-coverage/blob/master/ts/LICENSE.md
      +//
      +// Merges a list of v8 process coverages.
      +// The result is normalized.
      +// The input values may be mutated, it is not safe to use them after passing
      +// them to this function.
      +// The computation is synchronous.
      +// @param processCovs Process coverages to merge.
      +// @return Merged process coverage.
      +
      +    let resultMerged = [];      // List of merged scripts from processCovs.
      +    let urlToScriptDict = new Map();    // Map scriptCov.url to scriptCovs.
      +
      +    function compareRangeList(aa, bb) {
      +
      +// Compares two range coverages.
      +// The ranges are first ordered by ascending `startOffset` and then by
      +// descending `endOffset`.
      +// This corresponds to a pre-order tree traversal.
      +
      +        if (aa.startOffset !== bb.startOffset) {
      +            return aa.startOffset - bb.startOffset;
      +        }
      +        return bb.endOffset - aa.endOffset;
      +    }
      +
      +    function dictKeyValueAppend(dict, key, val) {
      +
      +// This function will append <val> to list <dict>[<key>].
      +
      +        let list = dict.get(key);
      +        if (list === undefined) {
      +            list = [];
      +            dict.set(key, list);
      +        }
      +        list.push(val);
      +    }
      +
      +    function mergeTreeList(parentTrees) {
      +
      +// This function will return RangeTree object with <parentTrees> merged into
      +// property-children.
      +// @precondition Same `start` and `end` for all the parentTrees
      +
      +        if (parentTrees.length <= 1) {
      +            return parentTrees[0];
      +        }
      +
      +// new RangeTree().
      +
      +        return {
      +
      +// Merge parentTrees into property-children.
      +
      +            children: mergeTreeListToChildren(parentTrees),
      +            delta: parentTrees.reduce(function (aa, bb) {
      +                return aa + bb.delta;
      +            }, 0),
      +            end: parentTrees[0].end,
      +            start: parentTrees[0].start
      +   ...
      +}
      +
    • +
    • Example usage:
      ...
      +            "test_v8_coverage_node_sqlite_13216_1633662333140_0.json"
      +        ].map(function (file) {
      +            return testCoverageMergeData[file];
      +        });
      +        let data2 = testCoverageMergeData[
      +            "test_v8_coverage_node_sqlite_merged.json"
      +        ];
      +        data1 = v8CoverageListMerge(data1);
      +        data1 = v8CoverageListMerge([data1]);
      +
      +// Debug data1.
      +// await moduleFs.promises.writeFile(
      +//     ".test_v8_coverage_node_sqlite_merged.json",
      +//     JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n"
      +// );
      +...
    • + +
    • +

      + 25. + function v8CoverageReportCreate({ + consoleError, + coverageDir, + processArgv = [] +}) + +

      +
    • +
    • Description and source-code:
      async function v8CoverageReportCreate({
      +    consoleError,
      +    coverageDir,
      +    processArgv = []
      +}) {
      +
      +// This function will create html-coverage-reports directly from
      +// v8-coverage-files in <coverageDir>.
      +// 1. Spawn node.js program <processArgv> with NODE_V8_COVERAGE.
      +// 2. Merge JSON v8-coverage-files in <coverageDir>.
      +// 3. Create html-coverage-reports in <coverageDir>.
      +
      +    let cwd;
      +    let excludeList = [];
      +    let exitCode = 0;
      +    let fileDict;
      +    let includeList = [];
      +    let modeIncludeNodeModules;
      +    let processArgElem;
      +    let promiseList = [];
      +    let v8CoverageObj;
      +
      +    function htmlRender({
      +        fileList,
      +        lineList,
      +        modeIndex,
      +        pathname
      +    }) {
      +        let html;
      +        let padLines;
      +        let padPathname;
      +        let txt;
      +        let txtBorder;
      +        html = "";
      +        html += String(`
      +<!DOCTYPE html>
      +<html lang="en">
      +<head>
      +<title>V8 Coverage Report</title>
      +<style>
      +/* jslint utility2:true */
      +/*csslint ignore:start*/
      +.coverage,
      +.coverage a,
      +.coverage div,
      +.coverage pre,
      +.coverage span,
      +.coverage table,
      +.coverage tbody,
      +.coverage td,
      +.coverage th,
      +.coverage thead,
      +.coverage tr {
      +    box-sizing: border-box;
      +    font-family: monospace;
      +}
      +/*csslint ignore:end*/
      +
      +/* css - coverage_report - general */
      +body {
      +    margin: 0;
      +}
      +.coverage pre {
      +    margin: 5px 0;
      +}
      +.coverage table {
      +    border-collapse: collapse;
      +}
      +.coverage td,
      +.coverage th {
      +    border: 1px solid #777;
      +    line-height: 20px;
      +    margin: 0;
      +    padding: 5px 10px;
      +}
      +.coverage td span {
      +    display: inline-block;
      +    width: 100%;
      +}
      +.coverage .content {
      +    padding: 0 5px;
      +}
      +.coverage .content a {
      +    text-decoration: none;
      +}
      +.coverage .count {
      +    margin: 0 5px;
      +    padding: 0 5px;
      +}
      +.coverage .footer,
      +.coverage .header {
      +    padding: 20px;
      +}
      +.coverage .footer {
      +    text-align: center;
      +}
      +.coverage .percentbar {
      +    height: 12px;
      +    margin: 2px 0;
      +    min-width: 200px;
      +    position: relative;
      +    width: 100%;...
      +}
      +
    • +
    • Example usage:
      ...
      +
      +/*jslint node*/
      +import jslint from "../jslint.mjs";
      +(async function () {
      +
      +// Create V8 coverage report from program `npm run test` in javascript.
      +
      +await jslint.v8CoverageReportCreate({
      +    coverageDir: "../.artifact/coverage_sqlite3_js/",
      +    processArgv: [
      +        "--exclude=tes?/",
      +        "--exclude=tes[!0-9A-Z_a-z-]/",
      +        "--exclude=tes[0-9A-Z_a-z-]/",
      +        "--exclude=tes[^0-9A-Z_a-z-]/",
      +        "--exclude=test/**/*.js",
      +...
    • + +
    • +

      + 26. + string jslint_charset_ascii + +

      +
    • + +
    • +

      + 27. + string jslint_edition + +

      +
    • + +
    +
    + +
    + [ + This document was created with + JSLint + ] +
    +
    + + diff --git a/branch-beta/.artifact/asset_image_logo_128.png b/branch-beta/.artifact/asset_image_logo_128.png new file mode 100644 index 000000000..f9a632942 Binary files /dev/null and b/branch-beta/.artifact/asset_image_logo_128.png differ diff --git a/branch-beta/.artifact/asset_image_logo_256.png b/branch-beta/.artifact/asset_image_logo_256.png new file mode 100644 index 000000000..df67d1dce Binary files /dev/null and b/branch-beta/.artifact/asset_image_logo_256.png differ diff --git a/branch-beta/.artifact/asset_image_logo_32.png b/branch-beta/.artifact/asset_image_logo_32.png new file mode 100644 index 000000000..6308fbc6a Binary files /dev/null and b/branch-beta/.artifact/asset_image_logo_32.png differ diff --git a/branch-beta/.artifact/asset_image_logo_512.png b/branch-beta/.artifact/asset_image_logo_512.png new file mode 100644 index 000000000..585908e3a Binary files /dev/null and b/branch-beta/.artifact/asset_image_logo_512.png differ diff --git a/branch-beta/.artifact/asset_image_logo_64.png b/branch-beta/.artifact/asset_image_logo_64.png new file mode 100644 index 000000000..c70d5aa49 Binary files /dev/null and b/branch-beta/.artifact/asset_image_logo_64.png differ diff --git a/branch-beta/.artifact/coverage/coverage_badge.svg b/branch-beta/.artifact/coverage/coverage_badge.svg new file mode 100644 index 000000000..5e137a1ce --- /dev/null +++ b/branch-beta/.artifact/coverage/coverage_badge.svg @@ -0,0 +1,14 @@ + + + + +coverage +100.00 % + + diff --git a/branch-beta/.artifact/coverage/coverage_report.txt b/branch-beta/.artifact/coverage/coverage_report.txt new file mode 100644 index 000000000..18fd858a1 --- /dev/null +++ b/branch-beta/.artifact/coverage/coverage_report.txt @@ -0,0 +1,16 @@ +V8 Coverage Report ++----------------------------------+-------------------+-------------------+ +| Files covered | Lines | Remaining | ++----------------------------------+-------------------+-------------------+ +| ./ | 100.00 % | | +| ******************************** | 13199 / 13199 | 0 / 13199 | ++----------------------------------+-------------------+-------------------+ +| ./jslint.mjs | 100.00 % | | +| ******************************** | 11574 / 11574 | 0 / 11574 | ++----------------------------------+-------------------+-------------------+ +| ./jslint_wrapper_cjs.cjs | 100.00 % | | +| ******************************** | 49 / 49 | 0 / 49 | ++----------------------------------+-------------------+-------------------+ +| ./test.mjs | 100.00 % | | +| ******************************** | 1576 / 1576 | 0 / 1576 | ++----------------------------------+-------------------+-------------------+ diff --git a/branch-beta/.artifact/coverage/index.html b/branch-beta/.artifact/coverage/index.html new file mode 100644 index 000000000..38a80ec05 --- /dev/null +++ b/branch-beta/.artifact/coverage/index.html @@ -0,0 +1,212 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . /
    +
    +
    +
    +
    + 100.00 %
    + 13199 / 13199 +
    +
    + 0 / 13199 +
    + . / jslint.mjs
    +
    +
    +
    +
    + 100.00 %
    + 11574 / 11574 +
    +
    + 0 / 11574 +
    + . / jslint_wrapper_cjs.cjs
    +
    +
    +
    +
    + 100.00 %
    + 49 / 49 +
    +
    + 0 / 49 +
    + . / test.mjs
    +
    +
    +
    +
    + 100.00 %
    + 1576 / 1576 +
    +
    + 0 / 1576 +
    +
    + + + + diff --git a/branch-beta/.artifact/coverage/jslint.mjs.html b/branch-beta/.artifact/coverage/jslint.mjs.html new file mode 100644 index 000000000..a140d315f --- /dev/null +++ b/branch-beta/.artifact/coverage/jslint.mjs.html @@ -0,0 +1,11742 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / jslint.mjs
    +
    +
    +
    +
    + 100.00 %
    + 11574 / 11574 +
    +
    + 0 / 11574 +
    +
    + + +
    +
        1.      1// #!/usr/bin/env node
    +
        2.      1// JSLint
    +
        3.      1
    +
        4.      1// The Unlicense
    +
        5.      1//
    +
        6.      1// This is free and unencumbered software released into the public domain.
    +
        7.      1//
    +
        8.      1// Anyone is free to copy, modify, publish, use, compile, sell, or
    +
        9.      1// distribute this software, either in source code form or as a compiled
    +
       10.      1// binary, for any purpose, commercial or non-commercial, and by any
    +
       11.      1// means.
    +
       12.      1//
    +
       13.      1// In jurisdictions that recognize copyright laws, the author or authors
    +
       14.      1// of this software dedicate any and all copyright interest in the
    +
       15.      1// software to the public domain. We make this dedication for the benefit
    +
       16.      1// of the public at large and to the detriment of our heirs and
    +
       17.      1// successors. We intend this dedication to be an overt act of
    +
       18.      1// relinquishment in perpetuity of all present and future rights to this
    +
       19.      1// software under copyright law.
    +
       20.      1//
    +
       21.      1// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    +
       22.      1// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    +
       23.      1// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    +
       24.      1// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
    +
       25.      1// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
    +
       26.      1// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    +
       27.      1// OTHER DEALINGS IN THE SOFTWARE.
    +
       28.      1
    +
       29.      1
    +
       30.      1// jslint(source, option_dict, global_list) is a function that takes 3
    +
       31.      1// arguments. The second two arguments are optional.
    +
       32.      1
    +
       33.      1//      source          A text to analyze.
    +
       34.      1//      option_dict     An object whose keys correspond to option names.
    +
       35.      1//      global_list     An array of strings containing global variables that
    +
       36.      1//                      the file is allowed readonly access.
    +
       37.      1
    +
       38.      1// jslint returns an object containing its results. The object contains a lot
    +
       39.      1// of valuable information. It can be used to generate reports. The object
    +
       40.      1// contains:
    +
       41.      1
    +
       42.      1//      directives: an array of directive comment tokens.
    +
       43.      1//      edition: the version of JSLint that did the analysis.
    +
       44.      1//      exports: the names exported from the module.
    +
       45.      1//      froms: an array of strings representing each of the imports.
    +
       46.      1//      functions: an array of objects that represent all functions
    +
       47.      1//              declared in the file.
    +
       48.      1//      global: an object representing the global object. Its .context property
    +
       49.      1//              is an object containing a property for each global variable.
    +
       50.      1//      id: "(JSLint)"
    +
       51.      1//      json: true if the file is a JSON text.
    +
       52.      1//      lines: an array of strings, the source.
    +
       53.      1//      module: true if an import or export statement was used.
    +
       54.      1//      ok: true if no warnings were generated. This is what you want.
    +
       55.      1//      option: the option argument.
    +
       56.      1//      property: a property object.
    +
       57.      1//      stop: true if JSLint was unable to finish. You don't want this.
    +
       58.      1//      tokens: an array of objects representing the tokens in the file.
    +
       59.      1//      tree: the token objects arranged in a tree.
    +
       60.      1//      warnings: an array of warning objects. A warning object can contain:
    +
       61.      1//          name: "JSLintError"
    +
       62.      1//          column: A column number in the file.
    +
       63.      1//          line: A line number in the file.
    +
       64.      1//          code: A warning code string.
    +
       65.      1//          message: The warning message string.
    +
       66.      1//          a: Exhibit A.
    +
       67.      1//          b: Exhibit B.
    +
       68.      1//          c: Exhibit C.
    +
       69.      1//          d: Exhibit D.
    +
       70.      1
    +
       71.      1// jslint works in several phases. In any of these phases, errors might be
    +
       72.      1// found. Sometimes JSLint is able to recover from an error and continue
    +
       73.      1// parsing. In some cases, it cannot and will stop early. If that should happen,
    +
       74.      1// repair your code and try again.
    +
       75.      1
    +
       76.      1// Phases:
    +
       77.      1
    +
       78.      1// PHASE 1. Split <source> by newlines into <line_list>.
    +
       79.      1// PHASE 2. Lex <line_list> into <token_list>.
    +
       80.      1// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
    +
       81.      1// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
    +
       82.      1//          recursive traversal. Each node may be processed on the way down
    +
       83.      1//          (preaction) and on the way up (postaction).
    +
       84.      1// PHASE 5. Check whitespace between tokens in <token_list>.
    +
       85.      1
    +
       86.      1// jslint can also examine JSON text. It decides that a file is JSON text if
    +
       87.      1// the first token is "[" or "{". Processing of JSON text is much simpler than
    +
       88.      1// the processing of JavaScript programs. Only the first three phases are
    +
       89.      1// required.
    +
       90.      1
    +
       91.      1// WARNING: JSLint will hurt your feelings.
    +
       92.      1
    +
       93.      1/*jslint beta, node*/
    +
       94.      1/*property
    +
       95.      1    JSLINT_BETA, NODE_V8_COVERAGE, a, all, argv, arity, artifact,
    +
       96.      1    assertErrorThrownAsync, assertJsonEqual, assertOrThrow, assign, async, b,
    +
       97.      1    beta, bitwise, block, body, browser, c, calls, catch, catch_list,
    +
       98.      1    catch_stack, causes, char, children, clear, closer, closure, code, column,
    +
       99.      1    concat, consoleError, console_error, console_log, constant, context,
    +
      100.      1    convert, count, coverageDir, create, cwd, d, dead, debugInline, default,
    +
      101.      1    delta, devel, directive, directive_ignore_line, directive_list, directives,
    +
      102.      1    dirname, disrupt, dot, edition, elem_list, ellipsis, else, end, endOffset,
    +
      103.      1    endsWith, entries, env, error, eval, every, example_list, excludeList, exec,
    +
      104.      1    execArgv, exit, exitCode, export_dict, exports, expression, extra, fart,
    +
      105.      1    file, fileList, fileURLToPath, filter, finally, flag, floor, for, forEach,
    +
      106.      1    formatted_message, free, freeze, from, froms, fsWriteFileWithParents,
    +
      107.      1    fud_stmt, functionName, function_list, function_stack, functions, get,
    +
      108.      1    getset, github_repo, globExclude, global, global_dict, global_list,
    +
      109.      1    holeList, htmlEscape, id, identifier, import, import_list, import_meta_url,
    +
      110.      1    inc, includeList, indent2, index, indexOf, init, initial, isArray,
    +
      111.      1    isBlockCoverage, isHole, isNaN, is_equal, is_weird, join, jslint,
    +
      112.      1    jslint_apidoc, jslint_assert, jslint_charset_ascii, jslint_cli,
    +
      113.      1    jslint_edition, jslint_phase1_split, jslint_phase2_lex, jslint_phase3_parse,
    +
      114.      1    jslint_phase4_walk, jslint_phase5_whitage, jslint_report, json,
    +
      115.      1    jstestDescribe, jstestIt, jstestOnExit, keys, label, lbp, led_infix, length,
    +
      116.      1    level, line, lineList, line_list, line_offset, line_source, lines,
    +
      117.      1    linesCovered, linesTotal, live, log, long, loop, m, map, margin, match, max,
    +
      118.      1    message, meta, min, mkdir, modeCoverageIgnoreFile, modeIndex, mode_cli,
    +
      119.      1    mode_conditional, mode_json, mode_module, mode_noop, mode_property,
    +
      120.      1    mode_shebang, mode_stop, module, moduleFsInit, moduleName, module_list,
    +
      121.      1    name, names, node, nomen, noop, now, nr, nud_prefix,
    +
      122.      1    objectDeepCopyWithKeysSorted, ok, on, open, opening, option, option_dict,
    +
      123.      1    order, package_name, padEnd, padStart, parameters, parent, parentIi, parse,
    +
      124.      1    pathname, pathnameList, platform, pop, processArgv, process_argv,
    +
      125.      1    process_env, process_exit, promises, property, property_dict, push, quote,
    +
      126.      1    ranges, readFile, readdir, readonly, recursive, reduce, repeat, replace,
    +
      127.      1    resolve, result, reverse, role, round, scriptId, search, set, shebang,
    +
      128.      1    shell, shift, signature, single, slice, some, sort, source, spawn, splice,
    +
      129.      1    split, stack, stack_trace, start, startOffset, startsWith, statement,
    +
      130.      1    statement_prv, stdio, stop, stop_at, stringify, subscript, switch,
    +
      131.      1    syntax_dict, tenure, test, test_cause, test_internal_error, this, thru,
    +
      132.      1    toLocaleString, toString, token, token_global, token_list, token_nxt,
    +
      133.      1    token_tree, tokens, trace, tree, trim, trimEnd, trimRight, try, type,
    +
      134.      1    unlink, unordered, unshift, url, used, v8CoverageListMerge,
    +
      135.      1    v8CoverageReportCreate, value, variable, version, versions, warn, warn_at,
    +
      136.      1    warning, warning_list, warnings, white, wrapped, writeFile
    +
      137.      1*/
    +
      138.      1
    +
      139.      1// init debugInline
    +
      140.      1let debugInline = (function () {
    +
      141.      3    let __consoleError = function () {
    +
      142.      3        return;
    +
      143.      3    };
    +
      144.      1    function debug(...argv) {
    +
      145.      1
    +
      146.      1// This function will print <argv> to stderr and then return <argv>[0].
    +
      147.      1
    +
      148.      1        __consoleError("\n\ndebugInline");
    +
      149.      1        __consoleError(...argv);
    +
      150.      1        __consoleError("\n");
    +
      151.      1        return argv[0];
    +
      152.      1    }
    +
      153.      1    debug(); // Coverage-hack.
    +
      154.      1    __consoleError = console.error; //jslint-ignore-line
    +
      155.      1    return debug;
    +
      156.      1}());
    +
      157.      1let jslint_charset_ascii = (
    +
      158.      1    "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007"
    +
      159.      1    + "\b\t\n\u000b\f\r\u000e\u000f"
    +
      160.      1    + "\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017"
    +
      161.      1    + "\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f"
    +
      162.      1    + " !\"#$%&'()*+,-./0123456789:;<=>?"
    +
      163.      1    + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
    +
      164.      1    + "`abcdefghijklmnopqrstuvwxyz{|}~\u007f"
    +
      165.      1);
    +
      166.      1let jslint_edition = "v2024.11.24";
    +
      167.      1let jslint_export;                      // The jslint object to be exported.
    +
      168.      1let jslint_fudge = 1;                   // Fudge starting line and starting
    +
      169.      1                                        // ... column to 1.
    +
      170.      1let jslint_import_meta_url = "";        // import.meta.url used by cli.
    +
      171.      1let jslint_rgx_cap = (
    +
      172.      1    /^[A-Z]/
    +
      173.      1);
    +
      174.      1let jslint_rgx_crlf = (
    +
      175.      1    /\n|\r\n?/
    +
      176.      1);
    +
      177.      1let jslint_rgx_digits_bits = (
    +
      178.      1    /^[01_]*/
    +
      179.      1);
    +
      180.      1let jslint_rgx_digits_decimals = (
    +
      181.      1    /^[0-9_]*/
    +
      182.      1);
    +
      183.      1let jslint_rgx_digits_hexs = (
    +
      184.      1    /^[0-9A-F_]*/i
    +
      185.      1);
    +
      186.      1let jslint_rgx_digits_octals = (
    +
      187.      1    /^[0-7_]*/
    +
      188.      1);
    +
      189.      1let jslint_rgx_directive = (
    +
      190.      1    /^(jslint|property|global)\s+(.*)$/
    +
      191.      1);
    +
      192.      1let jslint_rgx_directive_part = (
    +
      193.      1    /([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*|$/g
    +
      194.      1);
    +
      195.      1let jslint_rgx_identifier = (
    +
      196.      1    /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/
    +
      197.      1);
    +
      198.      1let jslint_rgx_json_number = (
    +
      199.      1
    +
      200.      1// https://datatracker.ietf.org/doc/html/rfc7159#section-6
    +
      201.      1// number = [ minus ] int [ frac ] [ exp ]
    +
      202.      1
    +
      203.      1    /^-?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][\-+]?\d+)?$/
    +
      204.      1);
    +
      205.      1let jslint_rgx_mega = (
    +
      206.      1
    +
      207.      1// Vim-hack - vim-editor has trouble parsing naked '`' in regexp
    +
      208.      1
    +
      209.      1    /[\u0060\\]|\$\{/
    +
      210.      1);
    +
      211.      1let jslint_rgx_module = (
    +
      212.      1    /^[a-zA-Z0-9_$:.@\-\/]+$/
    +
      213.      1);
    +
      214.      1let jslint_rgx_numeric_separator_illegal = (
    +
      215.      1    /__|_$|_n$/m
    +
      216.      1);
    +
      217.      1let jslint_rgx_slash_star_or_slash = (
    +
      218.      1    /\/\*|\/$/
    +
      219.      1);
    +
      220.      1let jslint_rgx_tab = (
    +
      221.      1    /\t/g
    +
      222.      1);
    +
      223.      1let jslint_rgx_todo = (
    +
      224.      1    /\b(?:todo|TO\s?DO|HACK)\b/
    +
      225.      1);
    +
      226.      1let jslint_rgx_token = new RegExp(
    +
      227.      1    "^("
    +
      228.      1    + "(\\s+)"
    +
      229.      1    + "|([a-zA-Z_$][a-zA-Z0-9_$]*)"
    +
      230.      1    + "|[(){}\\[\\],:;'\"~\\`]"
    +
      231.      1    + "|\\?[?.]?"
    +
      232.      1    + "|=(?:==?|>)?"
    +
      233.      1    + "|\\.+"
    +
      234.      1    + "|\\*[*\\/=]?"
    +
      235.      1    + "|\\/[*\\/]?"
    +
      236.      1    + "|\\+[=+]?"
    +
      237.      1    + "|-[=\\-]?"
    +
      238.      1    + "|[\\^%]=?"
    +
      239.      1    + "|&[&=]?"
    +
      240.      1    + "|\\"
    +
      241.      1    + "|[|=]?"
    +
      242.      1    + "|>{1,3}=?"
    +
      243.      1    + "|<<?=?"
    +
      244.      1    + "|!(?:!|==?)?"
    +
      245.      1
    +
      246.      1// PR-351 - Add BigInt support.
    +
      247.      1// PR-390 - Add numeric-separator support.
    +
      248.      1
    +
      249.      1    + "|((?:0_?|[1-9][0-9_]*)n?)"
    +
      250.      1    + ")"
    +
      251.      1    + "(.*)$"
    +
      252.      1);
    +
      253.      1let jslint_rgx_url_search_window_jslint = (
    +
      254.      1    /[&?]window_jslint=1(?:$|&)/m
    +
      255.      1);
    +
      256.      1let jslint_rgx_weird_property = (
    +
      257.      1    /^_|\$|Sync$|_$/m
    +
      258.      1);
    +
      259.      1let jstestCountFailed = 0;
    +
      260.      1let jstestCountTotal = 0;
    +
      261.      1let jstestItCount = 0;
    +
      262.      1let jstestItList = [];
    +
      263.      1let jstestTimeStart;
    +
      264.      1let moduleChildProcess;
    +
      265.      1let moduleFs;
    +
      266.      1let moduleFsInitResolveList;
    +
      267.      1let modulePath;
    +
      268.      1let moduleUrl;
    +
      269.      1
    +
      270.     11async function assertErrorThrownAsync(asyncFunc, regexp) {
    +
      271.     11
    +
      272.     11// This function will assert calling <asyncFunc> throws an error.
    +
      273.     11
    +
      274.     11    let err;
    +
      275.     11    try {
    +
      276.      1        await asyncFunc();
    +
      277.     10    } catch (errCaught) {
    +
      278.     10        err = errCaught;
    +
      279.     10    }
    +
      280.     11    assertOrThrow(err, "No error thrown.");
    +
      281.     11    assertOrThrow(
    +
      282.      4        !regexp || new RegExp(regexp).test(err.message),
    +
      283.     11        err
    +
      284.     11    );
    +
      285.     11}
    +
      286.      1
    +
      287.    267function assertJsonEqual(aa, bb, message) {
    +
      288.    267
    +
      289.    267// This function will assert JSON.stringify(<aa>) === JSON.stringify(<bb>).
    +
      290.    267
    +
      291.    267    aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa), undefined, 1);
    +
      292.    267    bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb), undefined, 1);
    +
      293.      3    if (aa !== bb) {
    +
      294.      3        throw new Error(
    +
      295.      3            "\n" + aa + "\n!==\n" + bb
    +
      296.      3            + (
    +
      297.      3                typeof message === "string"
    +
      298.      3                ? " - " + message
    +
      299.      3                : message
    +
      300.      3                ? " - " + JSON.stringify(message)
    +
      301.      3                : ""
    +
      302.      3            )
    +
      303.      3        );
    +
      304.      3    }
    +
      305.    267}
    +
      306.      1
    +
      307.   1931function assertOrThrow(condition, message) {
    +
      308.   1931
    +
      309.   1931// This function will throw <message> if <condition> is falsy.
    +
      310.   1931
    +
      311.      4    if (!condition) {
    +
      312.      4        throw (
    +
      313.      4            (!message || typeof message === "string")
    +
      314.      4            ? new Error(String(message).slice(0, 2048))
    +
      315.      4            : message
    +
      316.      4        );
    +
      317.      4    }
    +
      318.   1931}
    +
      319.      1
    +
      320.  94133function empty() {
    +
      321.  94133
    +
      322.  94133// The empty function produces a new empty object that inherits nothing. This is
    +
      323.  94133// much better than '{}' because confusions around accidental method names like
    +
      324.  94133// 'constructor' are completely avoided.
    +
      325.  94133
    +
      326.  94133    return Object.create(null);
    +
      327.  94133}
    +
      328.      1
    +
      329.     59async function fsWriteFileWithParents(pathname, data) {
    +
      330.     59
    +
      331.     59// This function will write <data> to <pathname> and lazy-mkdirp if necessary.
    +
      332.     59
    +
      333.     59    await moduleFsInit();
    +
      334.     59
    +
      335.     59// Try writing to pathname.
    +
      336.     59
    +
      337.     59    try {
    +
      338.     41        await moduleFs.promises.writeFile(pathname, data);
    +
      339.     41    } catch (ignore) {
    +
      340.     18
    +
      341.     18// Lazy mkdirp.
    +
      342.     18
    +
      343.     18        await moduleFs.promises.mkdir(modulePath.dirname(pathname), {
    +
      344.     18            recursive: true
    +
      345.     18        });
    +
      346.     18
    +
      347.     18// Retry writing to pathname.
    +
      348.     18
    +
      349.     18        await moduleFs.promises.writeFile(pathname, data);
    +
      350.     18    }
    +
      351.     59    console.error("wrote file " + pathname);
    +
      352.     59}
    +
      353.      1
    +
      354.    184function globExclude({
    +
      355.    184    excludeList = [],
    +
      356.    184    includeList = [],
    +
      357.    184    pathnameList = []
    +
      358.    184}) {
    +
      359.    184
    +
      360.    184// This function will
    +
      361.    184// 1. Exclude pathnames in <pathnameList> that don't match glob-patterns in
    +
      362.    184//    <includeList>.
    +
      363.    184// 2. Exclude pathnames in <pathnameList> that match glob-patterns in
    +
      364.    184//    <excludeList>.
    +
      365.    184
    +
      366.    552    function globAssertNotWeird(list, name) {
    +
      367.    552
    +
      368.    552// This function will check if <list> of strings contain weird characters.
    +
      369.    552
    +
      370.    552        [
    +
      371.    552            [
    +
      372.    552                "\n", (
    +
      373.    552                    /^.*?([\u0000-\u0007\r]).*/gm
    +
      374.    552                )
    +
      375.    552            ],
    +
      376.    552            [
    +
      377.    552                "\r", (
    +
      378.    552                    /^.*?([\n]).*/gm
    +
      379.    552                )
    +
      380.    552            ]
    +
      381.   1102        ].forEach(function ([
    +
      382.   1102            separator, rgx
    +
      383.   1102        ]) {
    +
      384.      3            list.join(separator).replace(rgx, function (match0, char) {
    +
      385.      3                throw new Error(
    +
      386.      3                    "Weird character "
    +
      387.      3                    + JSON.stringify(char)
    +
      388.      3                    + " found in " + name + " "
    +
      389.      3                    + JSON.stringify(match0)
    +
      390.      3                );
    +
      391.      3            });
    +
      392.   1102        });
    +
      393.    552    }
    +
      394.    184
    +
      395.   1370    function globToRegexp(pattern) {
    +
      396.   1370
    +
      397.   1370// This function will translate glob <pattern> to javascript-regexp,
    +
      398.   1370// which javascript can then use to "glob" pathnames.
    +
      399.   1370
    +
      400.   1370        let ii = 0;
    +
      401.   1370        let isClass = false;
    +
      402.   1370        let strClass = "";
    +
      403.   1370        let strRegex = "";
    +
      404.   1370        pattern = pattern.replace((
    +
      405.   1370            /\/\/+/g
    +
      406.   1370        ), "/");
    +
      407.   1370        pattern = pattern.replace((
    +
      408.   1370            /\*\*\*+/g
    +
      409.   1370        ), "**");
    +
      410.   1370        pattern.replace((
    +
      411.   1370            /\\\\|\\\[|\\\]|\[|\]|./g
    +
      412.  18691        ), function (match0) {
    +
      413.  18691            switch (match0) {
    +
      414.    310            case "[":
    +
      415.    310                if (isClass) {
    +
      416.    310                    strClass += "[";
    +
      417.    310                    return;
    +
      418.    310                }
    +
      419.    310                strClass += "\u0000";
    +
      420.    310                strRegex += "\u0000";
    +
      421.    310                isClass = true;
    +
      422.    310                return;
    +
      423.    310            case "]":
    +
      424.    310                if (isClass) {
    +
      425.    310                    isClass = false;
    +
      426.    310                    return;
    +
      427.    310                }
    +
      428.    310                strRegex += "]";
    +
      429.    310                return;
    +
      430.  18071            default:
    +
      431.  18071                if (isClass) {
    +
      432.  18071                    strClass += match0;
    +
      433.  18071                    return;
    +
      434.  18071                }
    +
      435.  18071                strRegex += match0;
    +
      436.  15021            }
    +
      437.  15021            return "";
    +
      438.  15021        });
    +
      439.   1370        strClass += "\u0000";
    +
      440.   1370
    +
      441.   1370// An expression "[!...]" matches a single character, namely any character that
    +
      442.   1370// is not matched by the expression obtained by removing the first '!' from it.
    +
      443.   1370// (Thus, "[!a-]" matches any single character except 'a', and '-'.)
    +
      444.   1370
    +
      445.   1370        strClass = strClass.replace((
    +
      446.   1370            /\u0000!/g
    +
      447.   1370        ), "\u0000^");
    +
      448.   1370
    +
      449.   1370// One may include '-' in its literal meaning by making it the first or last
    +
      450.   1370// character between the brackets.
    +
      451.   1370
    +
      452.   1370        strClass = strClass.replace((
    +
      453.   1370            /\u0000-/g
    +
      454.   1370        ), "\u0000\\-");
    +
      455.   1370        strClass = strClass.replace((
    +
      456.   1370            /-\u0000/g
    +
      457.   1370        ), "\\-\u0000");
    +
      458.   1370
    +
      459.   1370// Escape brackets '[', ']' in character class.
    +
      460.   1370
    +
      461.   1370        strClass = strClass.replace((
    +
      462.   1370            /[\[\]]/g
    +
      463.   1370        ), "\\$&");
    +
      464.   1370
    +
      465.   1370// https://stackoverflow.com/questions/3561493
    +
      466.   1370// /is-there-a-regexp-escape-function-in-javascript
    +
      467.   1370// $()*+-./?[\]^{|}
    +
      468.   1370
    +
      469.   1370        strRegex = strRegex.replace((
    +
      470.   1370
    +
      471.   1370// Ignore [-/].
    +
      472.   1370
    +
      473.   1370            /[$()*+.?\[\\\]\^{|}]/g
    +
      474.   1370        ), "\\$&");
    +
      475.   1370
    +
      476.   1370// Expand wildcard '**/*'.
    +
      477.   1370
    +
      478.   1370        strRegex = strRegex.replace((
    +
      479.   1370            /\\\*\\\*\/(?:\\\*)+/g
    +
      480.   1370        ), ".*?");
    +
      481.   1370
    +
      482.   1370// Expand wildcard '**'.
    +
      483.   1370
    +
      484.   1370        strRegex = strRegex.replace((
    +
      485.   1370            /(^|\/)\\\*\\\*(\/|$)/gm
    +
      486.   1370        ), "$1.*?$2");
    +
      487.   1370
    +
      488.   1370// Expand wildcard '*'.
    +
      489.   1370
    +
      490.   1370        strRegex = strRegex.replace((
    +
      491.   1370            /(?:\\\*)+/g
    +
      492.   1370        ), "[^\\/]*?");
    +
      493.   1370
    +
      494.   1370// Expand wildcard '?'.
    +
      495.   1370
    +
      496.   1370        strRegex = strRegex.replace((
    +
      497.   1370            /\\\?/g
    +
      498.   1370        ), "[^\\/]");
    +
      499.   1370
    +
      500.   1370// Expand directory-with-trailing-slash '.../'.
    +
      501.   1370
    +
      502.   1370        strRegex = strRegex.replace((
    +
      503.   1370            /\/$/gm
    +
      504.   1370        ), "\\/.*?");
    +
      505.   1370
    +
      506.   1370// Merge strClass into strRegex.
    +
      507.   1370
    +
      508.   1370        ii = 0;
    +
      509.   1370        strClass = strClass.split("\u0000");
    +
      510.   1370        strRegex = strRegex.replace((
    +
      511.   1370            /\u0000/g
    +
      512.    306        ), function () {
    +
      513.    306            ii += 1;
    +
      514.      2            if (strClass[ii] === "") {
    +
      515.      2                return "";
    +
      516.    304            }
    +
      517.    304            return "[" + strClass[ii] + "]";
    +
      518.    304        });
    +
      519.   1370
    +
      520.   1370// Change strRegex from string to regexp.
    +
      521.   1370
    +
      522.   1370        strRegex = new RegExp("^" + strRegex + "$", "gm");
    +
      523.   1370        return strRegex;
    +
      524.   1370    }
    +
      525.    184
    +
      526.    184// Validate excludeList, includeList, pathnameList.
    +
      527.    184
    +
      528.    184    globAssertNotWeird(excludeList, "pattern");
    +
      529.    184    globAssertNotWeird(includeList, "pattern");
    +
      530.    184    globAssertNotWeird(pathnameList, "pathname");
    +
      531.    184
    +
      532.    184// Optimization
    +
      533.    184// Concat pathnames into a single, newline-separated string,
    +
      534.    184// whose pathnames can all be filtered with a single, regexp-pass.
    +
      535.    184
    +
      536.    184    pathnameList = pathnameList.join("\n");
    +
      537.    184
    +
      538.    184// 1. Exclude pathnames in <pathnameList> that don't match glob-patterns in
    +
      539.    184//    <includeList>.
    +
      540.    184
    +
      541.    142    if (includeList.length > 0) {
    +
      542.    142        includeList = includeList.map(globToRegexp);
    +
      543.    574        includeList.forEach(function (pattern) {
    +
      544.    574            pathnameList = pathnameList.replace(pattern, "\u0000$&");
    +
      545.    574        });
    +
      546.    142        pathnameList = pathnameList.replace((
    +
      547.    142            /^[^\u0000].*/gm
    +
      548.    142        ), "");
    +
      549.    142        pathnameList = pathnameList.replace((
    +
      550.    142            /^\u0000+/gm
    +
      551.    142        ), "");
    +
      552.    181    }
    +
      553.    181
    +
      554.    181// 2. Exclude pathnames in <pathnameList> that match glob-patterns in
    +
      555.    181//    <excludeList>.
    +
      556.    181
    +
      557.    181    excludeList = excludeList.map(globToRegexp);
    +
      558.    796    excludeList.forEach(function (pattern) {
    +
      559.    796        pathnameList = pathnameList.replace(pattern, "");
    +
      560.    796    });
    +
      561.    181
    +
      562.    181// Split newline-separated pathnames back to list.
    +
      563.    181
    +
      564.  10117    pathnameList = pathnameList.split("\n").filter(function (elem) {
    +
      565.  10117        return elem;
    +
      566.  10117    });
    +
      567.    181    return {
    +
      568.    181        excludeList,
    +
      569.    181        includeList,
    +
      570.    181        pathnameList
    +
      571.    181    };
    +
      572.    181}
    +
      573.      1
    +
      574.  14214function htmlEscape(str) {
    +
      575.  14214
    +
      576.  14214// This function will make <str> html-safe by escaping & < >.
    +
      577.  14214
    +
      578.  14214    return String(str).replace((
    +
      579.  14214        /&/g
    +
      580.  14214    ), "&amp;").replace((
    +
      581.  14214        /</g
    +
      582.  14214    ), "&lt;").replace((
    +
      583.  14214        />/g
    +
      584.  14214    ), "&gt;");
    +
      585.  14214}
    +
      586.      1
    +
      587.    668function jslint(
    +
      588.    668    source = "",                // A text to analyze.
    +
      589.    668    option_dict = empty(),      // An object whose keys correspond to option
    +
      590.    668                                // ... names.
    +
      591.    668    global_list = []            // An array of strings containing global
    +
      592.    668                                // ... variables that the file is allowed
    +
      593.    668                                // ... readonly access.
    +
      594.    668) {
    +
      595.    668
    +
      596.    668// The jslint function itself.
    +
      597.    668
    +
      598.    668    let catch_list = [];        // The array containing all catch-blocks.
    +
      599.    668    let catch_stack = [         // The stack of catch-blocks.
    +
      600.    668        {
    +
      601.    668            context: empty()
    +
      602.    668        }
    +
      603.    668    ];
    +
      604.    668    let cause_dict = empty();   // The object of test-causes.
    +
      605.    668    let directive_list = [];    // The directive comments.
    +
      606.    668    let export_dict = empty();  // The exported names and values.
    +
      607.    668    let function_list = [];     // The array containing all functions.
    +
      608.    668    let function_stack = [];    // The stack of functions.
    +
      609.    668    let global_dict = empty();  // The object containing the global
    +
      610.    668                                // ... declarations.
    +
      611.    668    let import_list = [];       // The array collecting all import-from strings.
    +
      612.    668    let line_list = String(     // The array containing source lines.
    +
      613.    668        "\n" + source
    +
      614. 105217    ).split(jslint_rgx_crlf).map(function (line_source) {
    +
      615. 105217        return {
    +
      616. 105217            line_source
    +
      617. 105217        };
    +
      618. 105217    });
    +
      619.    668    let mode_stop = false;      // true if JSLint cannot finish.
    +
      620.    668    let property_dict = empty();        // The object containing the tallied
    +
      621.    668                                        // ... property names.
    +
      622.    668    let state = empty();        // jslint state-object to be passed between
    +
      623.    668                                // jslint functions.
    +
      624.    668    let syntax_dict = empty();  // The object containing the parser.
    +
      625.    668    let tenure = empty();       // The predefined property registry.
    +
      626.    668    let token_global = {        // The global object; the outermost context.
    +
      627.    668        async: 0,
    +
      628.    668        body: true,
    +
      629.    668        context: empty(),
    +
      630.    668        finally: 0,
    +
      631.    668        from: 0,
    +
      632.    668        id: "(global)",
    +
      633.    668        level: 0,
    +
      634.    668        line: jslint_fudge,
    +
      635.    668        live: [],
    +
      636.    668        loop: 0,
    +
      637.    668        switch: 0,
    +
      638.    668        thru: 0,
    +
      639.    668        try: 0
    +
      640.    668    };
    +
      641.    668    let token_list = [];        // The array of tokens.
    +
      642.    668    let warning_list = [];      // The array collecting all generated warnings.
    +
      643.    668
    +
      644.    668// Error reportage functions:
    +
      645.    668
    +
      646.   8041    function artifact(the_token) {
    +
      647.   8041
    +
      648.   8041// Return a string representing an artifact.
    +
      649.   8041
    +
      650.    256        the_token = the_token || state.token_nxt;
    +
      651.   8041        return (
    +
      652.   5266            (the_token.id === "(string)" || the_token.id === "(number)")
    +
      653.   2900            ? String(the_token.value)
    +
      654.   5141            : the_token.id
    +
      655.   8041        );
    +
      656.   8041    }
    +
      657.    668
    +
      658.  31957    function is_equal(aa, bb) {
    +
      659.  31957
    +
      660.  31957// test_cause:
    +
      661.  31957// ["0&&0", "is_equal", "", "", 0]
    +
      662.  31957
    +
      663.  31957        test_cause("");
    +
      664.  31957
    +
      665.  31957// Probably deadcode.
    +
      666.  31957// if (aa === bb) {
    +
      667.  31957//     return true;
    +
      668.  31957// }
    +
      669.  31957
    +
      670.  31957        jslint_assert(!(aa === bb), `Expected !(aa === bb).`);
    +
      671.     27        if (Array.isArray(aa)) {
    +
      672.     27            return (
    +
      673.     27                Array.isArray(bb)
    +
      674.     27                && aa.length === bb.length
    +
      675.     27                && aa.every(function (value, index) {
    +
      676.     27
    +
      677.     27// test_cause:
    +
      678.     27// ["`${0}`&&`${0}`", "is_equal", "recurse_isArray", "", 0]
    +
      679.     27// ["`${0}`&&`${1}`", "is_equal", "recurse_isArray", "", 0]
    +
      680.     27
    +
      681.     27                    test_cause("recurse_isArray");
    +
      682.     27                    return is_equal(value, bb[index]);
    +
      683.     27                })
    +
      684.     27            );
    +
      685.  31930        }
    +
      686.  31930
    +
      687.  31930// Probably deadcode.
    +
      688.  31930// if (Array.isArray(bb)) {
    +
      689.  31930//     return false;
    +
      690.  31930// }
    +
      691.  31930
    +
      692.  31930        jslint_assert(!Array.isArray(bb), `Expected !Array.isArray(bb).`);
    +
      693.  31930        switch (aa.id === bb.id && aa.id) {
    +
      694.     65        case "(number)":
    +
      695.  23429        case "(string)":
    +
      696.  23429            return aa.value === bb.value;
    +
      697.  31957
    +
      698.  31957// PR-394 - Bugfix
    +
      699.  31957// Fix jslint falsely believing megastring literals `0` and `1` are similar.
    +
      700.  31957
    +
      701.     15        case "`":
    +
      702.     15            if (!is_equal(aa.value, bb.value)) {
    +
      703.     15                return false;
    +
      704.     15            }
    +
      705.     15            break;
    +
      706.   8498        }
    +
      707.   8498        if (is_weird(aa) || is_weird(bb)) {
    +
      708.     34
    +
      709.     34// test_cause:
    +
      710.     34// ["aa(/./)||{}", "is_equal", "false", "", 0]
    +
      711.     34
    +
      712.     34            test_cause("false");
    +
      713.     34            return false;
    +
      714.   8464        }
    +
      715.   8464        if (aa.arity === bb.arity && aa.id === bb.id) {
    +
      716.   2147            if (aa.id === "." || aa.id === "?.") {
    +
      717.   2147
    +
      718.   2147// test_cause:
    +
      719.   2147// ["aa.bb&&aa.bb", "is_equal", "recurse_arity_id", "", 0]
    +
      720.   2147// ["aa?.bb&&aa?.bb", "is_equal", "recurse_arity_id", "", 0]
    +
      721.   2147
    +
      722.   2147                test_cause("recurse_arity_id");
    +
      723.   2147                return (
    +
      724.   2147                    is_equal(aa.expression, bb.expression)
    +
      725.   2147                    && is_equal(aa.name, bb.name)
    +
      726.   2147                );
    +
      727.   2147            }
    +
      728.   2147            if (aa.arity === "unary") {
    +
      729.   2147
    +
      730.   2147// test_cause:
    +
      731.   2147// ["+0&&+0", "is_equal", "recurse_unary", "", 0]
    +
      732.   2147
    +
      733.   2147                test_cause("recurse_unary");
    +
      734.   2147                return is_equal(aa.expression, bb.expression);
    +
      735.   2147            }
    +
      736.   2147            if (aa.arity === "binary") {
    +
      737.   2147
    +
      738.   2147// test_cause:
    +
      739.   2147// ["aa[0]&&aa[0]", "is_equal", "recurse_binary", "", 0]
    +
      740.   2147
    +
      741.   2147                test_cause("recurse_binary");
    +
      742.   2147                return (
    +
      743.   2147                    aa.id !== "("
    +
      744.   2147                    && is_equal(aa.expression[0], bb.expression[0])
    +
      745.   2147                    && is_equal(aa.expression[1], bb.expression[1])
    +
      746.   2147                );
    +
      747.   2147            }
    +
      748.   2147            if (aa.arity === "ternary") {
    +
      749.   2147
    +
      750.   2147// test_cause:
    +
      751.   2147// ["aa=(``?``:``)&&(``?``:``)", "is_equal", "recurse_ternary", "", 0]
    +
      752.   2147
    +
      753.   2147                test_cause("recurse_ternary");
    +
      754.   2147                return (
    +
      755.   2147                    is_equal(aa.expression[0], bb.expression[0])
    +
      756.   2147                    && is_equal(aa.expression[1], bb.expression[1])
    +
      757.   2147                    && is_equal(aa.expression[2], bb.expression[2])
    +
      758.   2147                );
    +
      759.   2147            }
    +
      760.   2147
    +
      761.   2147// Probably deadcode.
    +
      762.   2147// if (aa.arity === "function" || aa.arity === "regexp") {
    +
      763.   2147//     return false;
    +
      764.   2147// }
    +
      765.   2147
    +
      766.   2147            jslint_assert(
    +
      767.   2147                !(aa.arity === "function" || aa.arity === "regexp"),
    +
      768.   2147                `Expected !(aa.arity === "function" || aa.arity === "regexp").`
    +
      769.   2147            );
    +
      770.   2147
    +
      771.   2147// test_cause:
    +
      772.   2147// ["undefined&&undefined", "is_equal", "true", "", 0]
    +
      773.   2147
    +
      774.   2147            test_cause("true");
    +
      775.   2147            return true;
    +
      776.   6317        }
    +
      777.   6317
    +
      778.   6317// test_cause:
    +
      779.   6317// ["null&&undefined", "is_equal", "false", "", 0]
    +
      780.   6317
    +
      781.   6317        test_cause("false");
    +
      782.   6317        return false;
    +
      783.   6317    }
    +
      784.    668
    +
      785.  28763    function is_weird(thing) {
    +
      786.  28763        switch (thing.id) {
    +
      787.      1        case "(regexp)":
    +
      788.      1            return true;
    +
      789.      1        case "=>":
    +
      790.      1            return true;
    +
      791.    593        case "[":
    +
      792.    593            return thing.arity === "unary";
    +
      793.     12        case "function":
    +
      794.     12            return true;
    +
      795.      8        case "{":
    +
      796.      8            return true;
    +
      797.  28148        default:
    +
      798.  28148            return false;
    +
      799.  28763        }
    +
      800.  28763    }
    +
      801.    668
    +
      802.    106    function stop(code, the_token, a, b, c, d) {
    +
      803.    106
    +
      804.    106// Similar to warn and stop_at. If the token already had a warning, that
    +
      805.    106// warning will be replaced with this new one. It is likely that the stopping
    +
      806.    106// warning will be the more meaningful.
    +
      807.    106
    +
      808.     38        the_token = the_token || state.token_nxt;
    +
      809.    106        delete the_token.warning;
    +
      810.    106        throw warn(code, the_token, a, b, c, d);
    +
      811.    106    }
    +
      812.    668
    +
      813.     28    function stop_at(code, line, column, a, b, c, d) {
    +
      814.     28
    +
      815.     28// Same as warn_at, except that it stops the analysis.
    +
      816.     28
    +
      817.     28        throw warn_at(code, line, column, a, b, c, d);
    +
      818.     28    }
    +
      819.    668
    +
      820. 339547    function test_cause(code, aa, column) {
    +
      821. 339547
    +
      822. 339547// This function will instrument <cause> to <cause_dict> for test-purposes.
    +
      823. 339547
    +
      824.   4882        if (option_dict.test_cause) {
    +
      825.   4882            cause_dict[JSON.stringify([
    +
      826.   4882                String(new Error().stack).replace((
    +
      827.   4882                    /^    at (?:file|stop|stop_at|test_cause|warn|warn_at)\b.*?\n/gm
    +
      828.   4882                ), "").match(
    +
      829.   4882                    /\n    at ((?:Object\.\w+?_)?\w+?) /
    +
      830.   4882                )[1].replace((
    +
      831.   4882                    /^Object\./
    +
      832.   4882                ), ""),
    +
      833.   4882                code,
    +
      834.   4882                String(
    +
      835.   4882                    (aa === undefined || aa === token_global)
    +
      836.   4882                    ? ""
    +
      837.   4882                    : aa
    +
      838.   4882                ),
    +
      839.   4882                column || 0
    +
      840.   4882            ])] = true;
    +
      841.   4882        }
    +
      842. 339547    }
    +
      843.    668
    +
      844.   1075    function warn(code, the_token, a, b, c, d) {
    +
      845.   1075
    +
      846.   1075// Same as warn_at, except the warning will be associated with a specific token.
    +
      847.   1075// If there is already a warning on this token, suppress the new one. It is
    +
      848.   1075// likely that the first warning will be the most meaningful.
    +
      849.   1075
    +
      850.   1075        let the_warning;
    +
      851.     20        the_token = the_token || state.token_nxt;
    +
      852.   1075        the_warning = warn_at(
    +
      853.   1075            code,
    +
      854.   1075            the_token.line,
    +
      855.    376            (the_token.from || 0) + jslint_fudge,
    +
      856.    835            a || artifact(the_token),
    +
      857.   1075            b,
    +
      858.   1075            c,
    +
      859.   1075            d
    +
      860.   1075        );
    +
      861.   1075
    +
      862.   1075// Issue #408
    +
      863.   1075// Warnings that should be ignored sometimes suppress legitimate warnings.
    +
      864.   1075
    +
      865.     26        if (the_warning.directive_ignore_line) {
    +
      866.     26            return the_warning;
    +
      867.   1049        }
    +
      868.   1049
    +
      869.   1049// If there is already a warning on this token, suppress the new one. It is
    +
      870.   1049// likely that the first warning will be the most meaningful.
    +
      871.   1049
    +
      872.   1049        if (the_token.warning) {
    +
      873.    192            warning_list.pop();
    +
      874.    192            return the_warning;
    +
      875.    857        }
    +
      876.    857        the_token.warning = the_warning;
    +
      877.    857        return the_warning;
    +
      878.    857    }
    +
      879.    668
    +
      880.   1394    function warn_at(code, line, column, a, b, c, d) {
    +
      881.   1394
    +
      882.   1394// Report an error at some line and column of the program. The warning object
    +
      883.   1394// resembles an exception.
    +
      884.   1394
    +
      885.   1394        let mm;
    +
      886.   1394        let warning = Object.assign(empty(), {
    +
      887.   1394            a,
    +
      888.   1394            b,
    +
      889.   1394            c,
    +
      890.   1394            code,
    +
      891.   1394
    +
      892.   1394// Fudge column numbers in warning message.
    +
      893.   1394
    +
      894.     27            column: column || jslint_fudge,
    +
      895.   1394            d,
    +
      896.   1394            line,
    +
      897.   1394            line_source: "",
    +
      898.   1394            name: "JSLintError"
    +
      899.   1394        }, line_list[line]);
    +
      900.   1394        warning.column = Math.max(
    +
      901.   1394            Math.min(warning.column, warning.line_source.length),
    +
      902.   1394            jslint_fudge
    +
      903.   1394        );
    +
      904.    926        test_cause(code, b || a, warning.column);
    +
      905.   1394        switch (code) {
    +
      906.   1394
    +
      907.   1394// The bundle contains the raw text messages that are generated by jslint. It
    +
      908.   1394// seems that they are all error messages and warnings. There are no "Atta
    +
      909.   1394// boy!" or "You are so awesome!" messages. There is no positive reinforcement
    +
      910.   1394// or encouragement. This relentless negativity can undermine self-esteem and
    +
      911.   1394// wound the inner child. But if you accept it as sound advice rather than as
    +
      912.   1394// personal criticism, it can make your programs better.
    +
      913.   1394
    +
      914.      1        case "and":
    +
      915.      1            mm = `The '&&' subexpression should be wrapped in parens.`;
    +
      916.      1            break;
    +
      917.     71        case "bad_assignment_a":
    +
      918.     71            mm = `Bad assignment to '${a}'.`;
    +
      919.     71            break;
    +
      920.      1        case "bad_directive_a":
    +
      921.      1            mm = `Bad directive '${a}'.`;
    +
      922.      1            break;
    +
      923.      1        case "bad_get":
    +
      924.      1            mm = `A get function takes no parameters.`;
    +
      925.      1            break;
    +
      926.      1        case "bad_module_name_a":
    +
      927.      1            mm = `Bad module name '${a}'.`;
    +
      928.      1            break;
    +
      929.      2        case "bad_option_a":
    +
      930.      2            mm = `Bad option '${a}'.`;
    +
      931.      2            break;
    +
      932.      1        case "bad_set":
    +
      933.      1            mm = `A set function takes one parameter.`;
    +
      934.      1            break;
    +
      935.      6        case "duplicate_a":
    +
      936.      6            mm = `Duplicate '${a}'.`;
    +
      937.      6            break;
    +
      938.     64        case "empty_block":
    +
      939.     64            mm = `Empty block.`;
    +
      940.     64            break;
    +
      941.      5        case "expected_a":
    +
      942.      5            mm = `Expected '${a}'.`;
    +
      943.      5            break;
    +
      944.     25        case "expected_a_at_b_c":
    +
      945.     25            mm = `Expected '${a}' at column ${b}, not column ${c}.`;
    +
      946.     25            break;
    +
      947.    286        case "expected_a_b":
    +
      948.    286            mm = `Expected '${a}' and instead saw '${b}'.`;
    +
      949.    286            break;
    +
      950.     17        case "expected_a_b_before_c_d":
    +
      951.     17            mm = `Expected ${a} '${b}' to be ordered before ${c} '${d}'.`;
    +
      952.     17            break;
    +
      953.      2        case "expected_a_b_from_c_d":
    +
      954.      2            mm = (
    +
      955.      2                `Expected '${a}' to match '${b}' from line ${c}`
    +
      956.      2                + ` and instead saw '${d}'.`
    +
      957.      2            );
    +
      958.      2            break;
    +
      959.     30        case "expected_a_before_b":
    +
      960.     30            mm = `Expected '${a}' before '${b}'.`;
    +
      961.     30            break;
    +
      962.      2        case "expected_digits_after_a":
    +
      963.      2            mm = `Expected digits after '${a}'.`;
    +
      964.      2            break;
    +
      965.      1        case "expected_four_digits":
    +
      966.      1            mm = `Expected four digits after '\\u'.`;
    +
      967.      1            break;
    +
      968.     31        case "expected_identifier_a":
    +
      969.     31            mm = `Expected an identifier and instead saw '${a}'.`;
    +
      970.     31            break;
    +
      971.      6        case "expected_line_break_a_b":
    +
      972.      6            mm = `Expected a line break between '${a}' and '${b}'.`;
    +
      973.      6            break;
    +
      974.      3        case "expected_regexp_factor_a":
    +
      975.      3            mm = `Expected a regexp factor and instead saw '${a}'.`;
    +
      976.      3            break;
    +
      977.     76        case "expected_space_a_b":
    +
      978.     76            mm = `Expected one space between '${a}' and '${b}'.`;
    +
      979.     76            break;
    +
      980.      2        case "expected_statements_a":
    +
      981.      2            mm = `Expected statements before '${a}'.`;
    +
      982.      2            break;
    +
      983.      1        case "expected_string_a":
    +
      984.      1            mm = `Expected a string and instead saw '${a}'.`;
    +
      985.      1            break;
    +
      986.      1        case "expected_type_string_a":
    +
      987.      1            mm = `Expected a type string and instead saw '${a}'.`;
    +
      988.      1            break;
    +
      989.      6        case "freeze_exports":
    +
      990.      6            mm = (
    +
      991.      6                `Expected 'Object.freeze('. All export values should be frozen.`
    +
      992.      6            );
    +
      993.      6            break;
    +
      994.   1394
    +
      995.   1394// PR-378 - Relax warning "function_in_loop".
    +
      996.   1394//
    +
      997.   1394//         case "function_in_loop":
    +
      998.   1394//             mm = `Don't create functions within a loop.`;
    +
      999.   1394//             break;
    +
     1000.   1394
    +
     1001.   1394// PR-390 - Add numeric-separator check.
    +
     1002.   1394
    +
     1003.      7        case "illegal_num_separator":
    +
     1004.      7            mm = `Illegal numeric separator '_' at column ${column}.`;
    +
     1005.      7            break;
    +
     1006.      1        case "infix_in":
    +
     1007.      1            mm = (
    +
     1008.      1                `Unexpected 'in'. Compare with undefined,`
    +
     1009.      1                + ` or use the hasOwnProperty method instead.`
    +
     1010.      1            );
    +
     1011.      1            break;
    +
     1012.      1        case "label_a":
    +
     1013.      1            mm = `'${a}' is a statement label.`;
    +
     1014.      1            break;
    +
     1015.      1        case "misplaced_a":
    +
     1016.      1            mm = `Place '${a}' at the outermost level.`;
    +
     1017.      1            break;
    +
     1018.      1        case "misplaced_directive_a":
    +
     1019.      1            mm = `Place the '/*${a}*/' directive before the first statement.`;
    +
     1020.      1            break;
    +
     1021.      3        case "missing_await_statement":
    +
     1022.      3            mm = `Expected await statement in async function.`;
    +
     1023.      3            break;
    +
     1024.   1394
    +
     1025.   1394// PR-347 - Disable warning "missing_browser".
    +
     1026.   1394//
    +
     1027.   1394//         case "missing_browser":
    +
     1028.   1394//             mm = `/*global*/ requires the Assume a browser option.`;
    +
     1029.   1394//             break;
    +
     1030.   1394
    +
     1031.      1        case "missing_m":
    +
     1032.      1            mm = `Expected 'm' flag on a multiline regular expression.`;
    +
     1033.      1            break;
    +
     1034.      5        case "naked_block":
    +
     1035.      5            mm = `Naked block.`;
    +
     1036.      5            break;
    +
     1037.      2        case "nested_comment":
    +
     1038.      2            mm = `Nested comment.`;
    +
     1039.      2            break;
    +
     1040.      1        case "not_label_a":
    +
     1041.      1            mm = `'${a}' is not a label.`;
    +
     1042.      1            break;
    +
     1043.      2        case "number_isNaN":
    +
     1044.      2            mm = `Use Number.isNaN function to compare with NaN.`;
    +
     1045.      2            break;
    +
     1046.      4        case "out_of_scope_a":
    +
     1047.      4            mm = `'${a}' is out of scope.`;
    +
     1048.      4            break;
    +
     1049.     11        case "redefinition_a_b":
    +
     1050.     11            mm = `Redefinition of '${a}' from line ${b}.`;
    +
     1051.     11            break;
    +
     1052.      1        case "redefinition_global_a_b":
    +
     1053.      1            mm = `Redefinition of global ${a} variable '${b}'.`;
    +
     1054.      1            break;
    +
     1055.      6        case "required_a_optional_b":
    +
     1056.      6            mm = `Required parameter '${a}' after optional parameter '${b}'.`;
    +
     1057.      6            break;
    +
     1058.      1        case "reserved_a":
    +
     1059.      1            mm = `Reserved name '${a}'.`;
    +
     1060.      1            break;
    +
     1061.      1        case "subscript_a":
    +
     1062.      1            mm = `['${a}'] is better written in dot notation.`;
    +
     1063.      1            break;
    +
     1064.     16        case "todo_comment":
    +
     1065.     16            mm = `Unexpected TODO comment.`;
    +
     1066.     16            break;
    +
     1067.     13        case "too_long":
    +
     1068.     13            mm = `Line is longer than 80 characters.`;
    +
     1069.     13            break;
    +
     1070.      1        case "too_many_digits":
    +
     1071.      1            mm = `Too many digits.`;
    +
     1072.      1            break;
    +
     1073.      3        case "unclosed_comment":
    +
     1074.      3            mm = `Unclosed comment.`;
    +
     1075.      3            break;
    +
     1076.      3        case "unclosed_disable":
    +
     1077.      3            mm = (
    +
     1078.      3                `Directive '/*jslint-disable*/' was not closed`
    +
     1079.      3                + ` with '/*jslint-enable*/'.`
    +
     1080.      3            );
    +
     1081.      3            break;
    +
     1082.      3        case "unclosed_mega":
    +
     1083.      3            mm = `Unclosed mega literal.`;
    +
     1084.      3            break;
    +
     1085.      2        case "unclosed_string":
    +
     1086.      2            mm = `Unclosed string.`;
    +
     1087.      2            break;
    +
     1088.    137        case "undeclared_a":
    +
     1089.    137            mm = `Undeclared '${a}'.`;
    +
     1090.    137            break;
    +
     1091.    237        case "unexpected_a":
    +
     1092.    237            mm = `Unexpected '${a}'.`;
    +
     1093.    237            break;
    +
     1094.      2        case "unexpected_a_after_b":
    +
     1095.      2            mm = `Unexpected '${a}' after '${b}'.`;
    +
     1096.      2            break;
    +
     1097.      2        case "unexpected_a_before_b":
    +
     1098.      2            mm = `Unexpected '${a}' before '${b}'.`;
    +
     1099.      2            break;
    +
     1100.     34        case "unexpected_at_top_level_a":
    +
     1101.     34            mm = `Expected '${a}' to be in a function.`;
    +
     1102.     34            break;
    +
     1103.      1        case "unexpected_char_a":
    +
     1104.      1            mm = `Unexpected character '${a}'.`;
    +
     1105.      1            break;
    +
     1106.      2        case "unexpected_comment":
    +
     1107.      2            mm = `Unexpected comment.`;
    +
     1108.      2            break;
    +
     1109.   1394
    +
     1110.   1394// PR-347 - Disable warning "unexpected_directive_a".
    +
     1111.   1394//
    +
     1112.   1394//         case "unexpected_directive_a":
    +
     1113.   1394//             mm = `When using modules, don't use directive '/\u002a${a}'.`;
    +
     1114.   1394//             break;
    +
     1115.   1394
    +
     1116.    128        case "unexpected_expression_a":
    +
     1117.    128            mm = `Unexpected expression '${a}' in statement position.`;
    +
     1118.    128            break;
    +
     1119.      4        case "unexpected_label_a":
    +
     1120.      4            mm = `Unexpected label '${a}'.`;
    +
     1121.      4            break;
    +
     1122.      3        case "unexpected_parens":
    +
     1123.      3            mm = `Don't wrap function literals in parens.`;
    +
     1124.      3            break;
    +
     1125.      8        case "unexpected_space_a_b":
    +
     1126.      8            mm = `Unexpected space between '${a}' and '${b}'.`;
    +
     1127.      8            break;
    +
     1128.      1        case "unexpected_statement_a":
    +
     1129.      1            mm = `Unexpected statement '${a}' in expression position.`;
    +
     1130.      1            break;
    +
     1131.      2        case "unexpected_trailing_space":
    +
     1132.      2            mm = `Unexpected trailing space.`;
    +
     1133.      2            break;
    +
     1134.      1        case "unexpected_typeof_a":
    +
     1135.      1            mm = (
    +
     1136.      1                `Unexpected 'typeof'. Use '===' to compare directly with ${a}.`
    +
     1137.      1            );
    +
     1138.      1            break;
    +
     1139.      1        case "uninitialized_a":
    +
     1140.      1            mm = `Uninitialized '${a}'.`;
    +
     1141.      1            break;
    +
     1142.      1        case "unopened_enable":
    +
     1143.      1            mm = (
    +
     1144.      1                `Directive '/*jslint-enable*/' was not opened`
    +
     1145.      1                + ` with '/*jslint-disable*/'.`
    +
     1146.      1            );
    +
     1147.      1            break;
    +
     1148.      1        case "unreachable_a":
    +
     1149.      1            mm = `Unreachable '${a}'.`;
    +
     1150.      1            break;
    +
     1151.      1        case "unregistered_property_a":
    +
     1152.      1            mm = `Unregistered property name '${a}'.`;
    +
     1153.      1            break;
    +
     1154.      6        case "unused_a":
    +
     1155.      6            mm = `Unused '${a}'.`;
    +
     1156.      6            break;
    +
     1157.      2        case "use_double":
    +
     1158.      2            mm = `Use double quotes, not single quotes.`;
    +
     1159.      2            break;
    +
     1160.   1394
    +
     1161.   1394// PR-386 - Fix issue #382 - Make fart-related warnings more readable.
    +
     1162.   1394
    +
     1163.      4        case "use_function_not_fart":
    +
     1164.      4            mm = (
    +
     1165.      4                `Use 'function (...)', not '(...) =>' when arrow functions`
    +
     1166.      4                + ` become too complex.`
    +
     1167.      4            );
    +
     1168.      4            break;
    +
     1169.      7        case "use_open":
    +
     1170.      7            mm = (
    +
     1171.      7                `Wrap a ternary expression in parens,`
    +
     1172.      7                + ` with a line break after the left paren.`
    +
     1173.      7            );
    +
     1174.      7            break;
    +
     1175.      1        case "use_spaces":
    +
     1176.      1            mm = `Use spaces, not tabs.`;
    +
     1177.      1            break;
    +
     1178.      5        case "var_on_top":
    +
     1179.      5            mm = `Move variable declaration to top of function or script.`;
    +
     1180.      5            break;
    +
     1181.      1        case "var_switch":
    +
     1182.      1            mm = `Don't declare variables in a switch.`;
    +
     1183.      1            break;
    +
     1184.     23        case "weird_condition_a":
    +
     1185.     23            mm = `Weird condition '${a}'.`;
    +
     1186.     23            break;
    +
     1187.      5        case "weird_expression_a":
    +
     1188.      5            mm = `Weird expression '${a}'.`;
    +
     1189.      5            break;
    +
     1190.      3        case "weird_loop":
    +
     1191.      3            mm = `Weird loop.`;
    +
     1192.      3            break;
    +
     1193.      9        case "weird_property_a":
    +
     1194.      9            mm = `Weird property name '${a}'.`;
    +
     1195.      9            break;
    +
     1196.      8        case "weird_relation_a":
    +
     1197.      8            mm = `Weird relation '${a}'.`;
    +
     1198.      8            break;
    +
     1199.      1        case "wrap_condition":
    +
     1200.      1            mm = `Wrap the condition in parens.`;
    +
     1201.      1            break;
    +
     1202.   1394
    +
     1203.   1394// PR-386 - Fix issue #382 - Make fart-related warnings more readable.
    +
     1204.   1394
    +
     1205.      1        case "wrap_fart_parameter":
    +
     1206.      1            mm = `Wrap the parameter before '=>' in parens.`;
    +
     1207.      1            break;
    +
     1208.      1        case "wrap_immediate":
    +
     1209.      1            mm = (
    +
     1210.      1                `Wrap an immediate function invocation in parentheses to assist`
    +
     1211.      1                + ` the reader in understanding that the expression is the`
    +
     1212.      1                + ` result of a function, and not the function itself.`
    +
     1213.      1            );
    +
     1214.      1            break;
    +
     1215.     18        case "wrap_regexp":
    +
     1216.     18            mm = `Wrap this regexp in parens to avoid confusion.`;
    +
     1217.     18            break;
    +
     1218.      1        case "wrap_unary":
    +
     1219.      1            mm = `Wrap the unary expression in parens.`;
    +
     1220.      1            break;
    +
     1221.   1394        }
    +
     1222.   1394
    +
     1223.   1394// Validate mm.
    +
     1224.   1394
    +
     1225.   1394        jslint_assert(mm, code);
    +
     1226.   1394        warning.message = mm;
    +
     1227.   1394
    +
     1228.   1394// PR-242 - Include stack_trace for jslint to debug itself for errors.
    +
     1229.   1394
    +
     1230.      6        if (option_dict.trace) {
    +
     1231.      6            warning.stack_trace = new Error().stack;
    +
     1232.      6        }
    +
     1233.     41        if (warning.directive_ignore_line) {
    +
     1234.     41
    +
     1235.     41// test_cause:
    +
     1236.     41// ["0 //jslint-ignore-line", "semicolon", "directive_ignore_line", "", 0]
    +
     1237.     41
    +
     1238.     41            test_cause("directive_ignore_line");
    +
     1239.     41            return warning;
    +
     1240.   1353        }
    +
     1241.   1353        warning_list.push(warning);
    +
     1242.   1353        return warning;
    +
     1243.   1353    }
    +
     1244.    668
    +
     1245.    668    try {
    +
     1246.    668
    +
     1247.    668// tokenize takes a source and produces from it an array of token objects.
    +
     1248.    668// JavaScript is notoriously difficult to tokenize because of the horrible
    +
     1249.    668// interactions between automatic semicolon insertion, regular expression
    +
     1250.    668// literals, and now megastring literals. JSLint benefits from eliminating
    +
     1251.    668// automatic semicolon insertion and nested megastring literals, which allows
    +
     1252.    668// full tokenization to precede parsing.
    +
     1253.    668
    +
     1254.    668        option_dict = Object.assign(empty(), option_dict);
    +
     1255.    668        Object.assign(state, {
    +
     1256.    668            artifact,
    +
     1257.    668            catch_list,
    +
     1258.    668            catch_stack,
    +
     1259.    668            directive_list,
    +
     1260.    668            export_dict,
    +
     1261.    668            function_list,
    +
     1262.    668            function_stack,
    +
     1263.    668            global_dict,
    +
     1264.    668            global_list,
    +
     1265.    668            import_list,
    +
     1266.    668            is_equal,
    +
     1267.    668            is_weird,
    +
     1268.    668            line_list,
    +
     1269.    668            mode_json: false,           // true if parsing JSON.
    +
     1270.    668            mode_module: false,         // true if import or export was used.
    +
     1271.    668            mode_property: false,       // true if directive /*property*/ is
    +
     1272.    668                                        // ... used.
    +
     1273.    668            mode_shebang: false,        // true if #! is seen on the first line.
    +
     1274.    668            option_dict,
    +
     1275.    668            property_dict,
    +
     1276.    668            source,
    +
     1277.    668            stop,
    +
     1278.    668            stop_at,
    +
     1279.    668            syntax_dict,
    +
     1280.    668            tenure,
    +
     1281.    668            test_cause,
    +
     1282.    668            token_global,
    +
     1283.    668            token_list,
    +
     1284.    668            token_nxt: token_global,
    +
     1285.    668            warn,
    +
     1286.    668            warn_at,
    +
     1287.    668            warning_list
    +
     1288.    668        });
    +
     1289.    668
    +
     1290.    668// PHASE 1. Split <source> by newlines into <line_list>.
    +
     1291.    668
    +
     1292.    668        jslint_phase1_split(state);
    +
     1293.    668        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
    +
     1294.    668        jslint_assert(
    +
     1295.    668            function_stack.length === 0,
    +
     1296.    668            `function_stack.length === 0.`
    +
     1297.    668        );
    +
     1298.    668
    +
     1299.    668// PHASE 2. Lex <line_list> into <token_list>.
    +
     1300.    668
    +
     1301.    668        jslint_phase2_lex(state);
    +
     1302.    668        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
    +
     1303.    668        jslint_assert(
    +
     1304.    668            function_stack.length === 0,
    +
     1305.    668            `function_stack.length === 0.`
    +
     1306.    668        );
    +
     1307.    668
    +
     1308.    668// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
    +
     1309.    668
    +
     1310.    668        jslint_phase3_parse(state);
    +
     1311.    668        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
    +
     1312.    668        jslint_assert(
    +
     1313.    668            function_stack.length === 0,
    +
     1314.    668            `function_stack.length === 0.`
    +
     1315.    668        );
    +
     1316.    668
    +
     1317.    668// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
    +
     1318.    668//          recursive traversal. Each node may be processed on the way down
    +
     1319.    668//          (preaction) and on the way up (postaction).
    +
     1320.    668
    +
     1321.    518        if (!state.mode_json) {
    +
     1322.    518            jslint_phase4_walk(state);
    +
     1323.    533        }
    +
     1324.    533        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
    +
     1325.    533        jslint_assert(
    +
     1326.    533            function_stack.length === 0,
    +
     1327.    533            `function_stack.length === 0.`
    +
     1328.    533        );
    +
     1329.    533
    +
     1330.    533// PHASE 5. Check whitespace between tokens in <token_list>.
    +
     1331.    533
    +
     1332.    533        if (!state.mode_json && warning_list.length === 0) {
    +
     1333.    208            jslint_phase5_whitage(state);
    +
     1334.    533        }
    +
     1335.    533        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
    +
     1336.    533        jslint_assert(
    +
     1337.    533            function_stack.length === 0,
    +
     1338.    533            `function_stack.length === 0.`
    +
     1339.    533        );
    +
     1340.    533
    +
     1341.    533// PR-347 - Disable warning "missing_browser".
    +
     1342.    533//
    +
     1343.    533//         if (!option_dict.browser) {
    +
     1344.    533//             directive_list.forEach(function (comment) {
    +
     1345.    533//                 if (comment.directive === "global") {
    +
     1346.    533//
    +
     1347.    533// // test_cause:
    +
     1348.    533// // ["/*global aa*/", "jslint", "missing_browser", "(comment)", 1]
    +
     1349.    533//
    +
     1350.    533//                     warn("missing_browser", comment);
    +
     1351.    533//                 }
    +
     1352.    533//             });
    +
     1353.    533//         }
    +
     1354.    533
    +
     1355.    533        if (option_dict.test_internal_error) {
    +
     1356.      2            jslint_assert(undefined, "test_internal_error");
    +
     1357.      2        }
    +
     1358.    137    } catch (err) {
    +
     1359.    137        mode_stop = true;
    +
     1360.    137        err.message = "[JSLint was unable to finish] " + err.message;
    +
     1361.    137        err.mode_stop = true;
    +
     1362.    137        if (err.name !== "JSLintError") {
    +
     1363.    137            Object.assign(err, {
    +
     1364.    137                column: jslint_fudge,
    +
     1365.    137                line: jslint_fudge,
    +
     1366.    137                line_source: "",
    +
     1367.    137                stack_trace: err.stack
    +
     1368.    137            });
    +
     1369.    137        }
    +
     1370.    137        if (warning_list.indexOf(err) === -1) {
    +
     1371.    137            warning_list.push(err);
    +
     1372.    137        }
    +
     1373.    137    }
    +
     1374.    668
    +
     1375.    668// Sort warning_list by mode_stop first, line, column respectively.
    +
     1376.    668
    +
     1377.    986    warning_list.sort(function (aa, bb) {
    +
     1378.    986        return (
    +
     1379.    986            Boolean(bb.mode_stop) - Boolean(aa.mode_stop)
    +
     1380.    896            || aa.line - bb.line
    +
     1381.    876            || aa.column - bb.column
    +
     1382.    986        );
    +
     1383.    986
    +
     1384.    986// Update each warning with formatted_message ready-for-use by jslint_cli.
    +
     1385.    986
    +
     1386.   1164    }).map(function ({
    +
     1387.   1164        column,
    +
     1388.   1164        line,
    +
     1389.   1164        line_source,
    +
     1390.   1164        message,
    +
     1391.   1164        stack_trace = ""
    +
     1392.   1164    }, ii, list) {
    +
     1393.   1164        list[ii].formatted_message = String(
    +
     1394.   1164            String(ii + 1).padStart(2, " ")
    +
     1395.   1164            + ". \u001b[31m" + message + "\u001b[39m"
    +
     1396.   1164            + " \u001b[90m\/\/ line " + line + ", column " + column
    +
     1397.   1164            + "\u001b[39m\n"
    +
     1398.   1164            + ("    " + line_source.trim()).slice(0, 72) + "\n"
    +
     1399.   1164            + stack_trace
    +
     1400.   1164        ).trimRight();
    +
     1401.   1164    });
    +
     1402.    668
    +
     1403.    668    return {
    +
     1404.    668        causes: cause_dict,
    +
     1405.    668        directives: directive_list,
    +
     1406.    668        edition: jslint_edition,
    +
     1407.    668        exports: export_dict,
    +
     1408.    668        froms: import_list,
    +
     1409.    668        functions: function_list,
    +
     1410.    668        global: token_global,
    +
     1411.    668        id: "(JSLint)",
    +
     1412.    668        json: state.mode_json,
    +
     1413.    668        lines: line_list,
    +
     1414.    668        module: state.mode_module === true,
    +
     1415.    164        ok: warning_list.length === 0 && !mode_stop,
    +
     1416.    668        option: option_dict,
    +
     1417.    668        property: property_dict,
    +
     1418.    668        shebang: (
    +
     1419.    668            state.mode_shebang
    +
     1420.      1            ? line_list[jslint_fudge].line_source
    +
     1421.    667            : undefined
    +
     1422.    668        ),
    +
     1423.    668        stop: mode_stop,
    +
     1424.    668        tokens: token_list,
    +
     1425.    668        tree: state.token_tree,
    +
     1426.    668        warnings: warning_list
    +
     1427.    668    };
    +
     1428.    668}
    +
     1429.      1
    +
     1430.      1// PR-362 - Add API Doc.
    +
     1431.      1
    +
     1432.      1async function jslint_apidoc({
    +
     1433.      1    example_list,
    +
     1434.      1    github_repo,
    +
     1435.      1    module_list,
    +
     1436.      1    package_name,
    +
     1437.      1    pathname,
    +
     1438.      1    version
    +
     1439.      1}) {
    +
     1440.      1
    +
     1441.      1// This function will create API Doc from <module_list>.
    +
     1442.      1
    +
     1443.      1    let elem_ii = 0;
    +
     1444.      1    let html;
    +
     1445.      1
    +
     1446.     27    function elem_create(moduleObj, key, moduleName) {
    +
     1447.     27
    +
     1448.     27// This function will create a sub API Doc from elem <moduleObj>[<key>].
    +
     1449.     27
    +
     1450.     27        let example = "N/A";
    +
     1451.     27        let id = encodeURIComponent("apidoc.elem." + moduleName + "." + key);
    +
     1452.     27        let name;
    +
     1453.     27        let signature;
    +
     1454.     27        let source;
    +
     1455.     27        name = htmlEscape((typeof moduleObj[key]) + " " + key);
    +
     1456.      2        if (typeof moduleObj[key] !== "function") {
    +
     1457.      2            return {
    +
     1458.      2                name,
    +
     1459.      2                signature: (`
    +
     1460.      2<a class="apidocElementLiA" href="#${id}">
    +
     1461.      2${name}
    +
     1462.      2</a>
    +
     1463.      2                `),
    +
     1464.      2                source: (`
    +
     1465.      2<li>
    +
     1466.      2    <h2>
    +
     1467.      2    <a href="#${id}" id="${id}">
    +
     1468.      2    ${name}
    +
     1469.      2    </a>
    +
     1470.      2    </h2>
    +
     1471.      2</li>
    +
     1472.      2                `)
    +
     1473.      2            };
    +
     1474.     25        }
    +
     1475.     25        // init source
    +
     1476.     25        source = htmlEscape(trim_start(moduleObj[key].toString()));
    +
     1477.     25        // init signature
    +
     1478.     25        source = source.replace((
    +
     1479.     25            /(\([\S\s]*?\)) \{/
    +
     1480.     25        ), function (match0, match1) {
    +
     1481.     25            signature = htmlEscape(
    +
     1482.     25                match1.replace((
    +
     1483.     25                    / *?\/\*[\S\s]*?\*\/ */g
    +
     1484.     25                ), "").replace((
    +
     1485.     25                    / *?\/\/.*/g
    +
     1486.     25                ), "").replace((
    +
     1487.     25                    /\n{2,}/g
    +
     1488.     25                ), "\n")
    +
     1489.     25            );
    +
     1490.     25            return match0;
    +
     1491.     25        });
    +
     1492.     25        // init comment
    +
     1493.     25        source = source.replace((
    +
     1494.     25            /\n(?:\/\/.*?\n)+\n/
    +
     1495.     25        ), "<span class=\"apidocCodeCommentSpan\">$&</span>");
    +
     1496.     25        // init example
    +
     1497.     56        example_list.some(function (example2) {
    +
     1498.     56            example2.replace(
    +
     1499.     56                new RegExp((
    +
     1500.     56                    "((?:\\n.*?){8}(function )?)\\b"
    +
     1501.     56                    + key
    +
     1502.     56                    + "(\\((?:.*?\\n){8})"
    +
     1503.     56                ), "g"),
    +
     1504.    132                function (ignore, header, isDeclaration, footer) {
    +
     1505.    124                    if (!isDeclaration) {
    +
     1506.    124                        example = "..." + trim_start(
    +
     1507.    124                            htmlEscape(header)
    +
     1508.    124                            + "<span class=\"apidocCodeKeywordSpan\">"
    +
     1509.    124                            + htmlEscape(key)
    +
     1510.    124                            + "</span>"
    +
     1511.    124                            + htmlEscape(footer)
    +
     1512.    124                        ).trimEnd() + "\n...";
    +
     1513.    124                    }
    +
     1514.    132                    return "";
    +
     1515.    132                }
    +
     1516.     56            );
    +
     1517.     56            return example !== "N/A";
    +
     1518.     56        });
    +
     1519.     25        if (source.length > 2048) {
    +
     1520.     11            source = source.slice(0, 2048) + "...\n}\n";
    +
     1521.     25        }
    +
     1522.     25        return {
    +
     1523.     25            name,
    +
     1524.     25            signature: (`
    +
     1525.     25<a class="apidocElementLiA" href="#${id}">
    +
     1526.     25${name}<span class="apidocSignatureSpan">${signature}</span>
    +
     1527.     25</a>
    +
     1528.     25            `),
    +
     1529.     25            source: (`
    +
     1530.     25<li>
    +
     1531.     25    <h2>
    +
     1532.     25    <a href="#${id}" id="${id}">
    +
     1533.     25    ${name}<span class="apidocSignatureSpan">${signature}</span>
    +
     1534.     25    </a>
    +
     1535.     25    </h2>
    +
     1536.     25</li>
    +
     1537.     25<li>Description and source-code:<pre class="apidocCodePre">${source}</pre></li>
    +
     1538.     25<li>Example usage:<pre class="apidocCodePre">${example}</pre></li>
    +
     1539.     25            `)
    +
     1540.     25        };
    +
     1541.     25    }
    +
     1542.      1
    +
     1543.    149    function trim_start(str) {
    +
     1544.    149
    +
     1545.    149// This function will normalize whitespace before <str>.
    +
     1546.    149
    +
     1547.    149        let whitespace = "";
    +
     1548.    149        str.trim().replace((
    +
     1549.    149            /^ */gm
    +
     1550.  13071        ), function (match0) {
    +
     1551.   8412            if (whitespace === "" || match0.length < whitespace.length) {
    +
     1552.   6610                whitespace = match0;
    +
     1553.   6610            }
    +
     1554.  13071            return "";
    +
     1555.  13071        });
    +
     1556.    149        str = str.replace(new RegExp("^" + whitespace, "gm"), "");
    +
     1557.    149        return str;
    +
     1558.    149    }
    +
     1559.      1    await moduleFsInit();
    +
     1560.      1
    +
     1561.      1// Html-escape params.
    +
     1562.      1
    +
     1563.      1    github_repo = htmlEscape(github_repo);
    +
     1564.      1    package_name = htmlEscape(package_name);
    +
     1565.      1    version = htmlEscape(version);
    +
     1566.      1
    +
     1567.      1// Init example_list.
    +
     1568.      1
    +
     1569.      3    example_list = await Promise.all(example_list.map(async function (file) {
    +
     1570.      3
    +
     1571.      3// This function will read example from given file.
    +
     1572.      3
    +
     1573.      3        let result = await moduleFs.promises.readFile(file, "utf8");
    +
     1574.      3        result = (
    +
     1575.      3            "\n\n\n\n\n\n\n\n"
    +
     1576.      3            // bug-workaround - truncate example to manageable size
    +
     1577.      3            + result.slice(0, 524288)
    +
     1578.      3            + "\n\n\n\n\n\n\n\n"
    +
     1579.      3        );
    +
     1580.      3        result = result.replace((
    +
     1581.      3            /\r\n*/g
    +
     1582.      3        ), "\n");
    +
     1583.      3        return result;
    +
     1584.      3    }));
    +
     1585.      1
    +
     1586.      1// Init module_list.
    +
     1587.      1
    +
     1588.      1    module_list = await Promise.all(module_list.map(async function ({
    +
     1589.      1        pathname
    +
     1590.      1    }) {
    +
     1591.      1        let moduleName = htmlEscape(JSON.stringify(pathname));
    +
     1592.      1        let moduleObj = await import(pathname);
    +
     1593.      1        if (moduleObj.default) {
    +
     1594.      1            moduleObj = moduleObj.default;
    +
     1595.      1        }
    +
     1596.      1        return {
    +
     1597.     27            elem_list: Object.keys(moduleObj).map(function (key) {
    +
     1598.     27                return elem_create(moduleObj, key, moduleName);
    +
     1599.     78            }).sort(function (aa, bb) {
    +
     1600.     78                return (
    +
     1601.     78                    aa.name < bb.name
    +
     1602.     22                    ? -1
    +
     1603.     56                    : 1
    +
     1604.     78                );
    +
     1605.     27            }).map(function (elem) {
    +
     1606.     27                elem_ii += 1;
    +
     1607.     27                elem.signature = elem.signature.replace(
    +
     1608.     27                    ">",
    +
     1609.     27                    ">" + elem_ii + ". "
    +
     1610.     27                );
    +
     1611.     27                elem.source = elem.source.replace(
    +
     1612.     27                    "\">",
    +
     1613.     27                    "\">" + elem_ii + ". "
    +
     1614.     27                );
    +
     1615.     27                return elem;
    +
     1616.     27            }),
    +
     1617.      1            id: encodeURIComponent("apidoc.module." + moduleName),
    +
     1618.      1            moduleName
    +
     1619.      1        };
    +
     1620.      1    }));
    +
     1621.      1    html = (`
    +
     1622.      1<!DOCTYPE html>
    +
     1623.      1<html lang="en">
    +
     1624.      1<head>
    +
     1625.      1<meta charset="utf-8">
    +
     1626.      1<meta name="viewport" content="width=device-width, initial-scale=1">
    +
     1627.      1<meta name="description" content="${package_name} API Doc">
    +
     1628.      1<title>${package_name} apidoc</title>
    +
     1629.      1<style>
    +
     1630.      1/* jslint utility2:true */
    +
     1631.      1/*csslint*/
    +
     1632.      1body {
    +
     1633.      1    margin: 0;
    +
     1634.      1    padding: 20px;
    +
     1635.      1}
    +
     1636.      1.apidocCodeCommentSpan,
    +
     1637.      1.apidocCodeKeywordSpan {
    +
     1638.      1    background: royalblue;
    +
     1639.      1    color: white;
    +
     1640.      1}
    +
     1641.      1.apidocCodeCommentSpan {
    +
     1642.      1    display: block;
    +
     1643.      1}
    +
     1644.      1.apidocCodePre {
    +
     1645.      1    background: #eef;
    +
     1646.      1    border: 1px solid;
    +
     1647.      1    font-size: 14px;
    +
     1648.      1    overflow-wrap: break-word;
    +
     1649.      1    padding: 5px;
    +
     1650.      1    white-space: pre-wrap;
    +
     1651.      1}
    +
     1652.      1.apidocDiv {
    +
     1653.      1    color: #555;
    +
     1654.      1    font-family: sans-serif;
    +
     1655.      1}
    +
     1656.      1.apidocDiv a[href] {
    +
     1657.      1    color: royalblue;
    +
     1658.      1    text-decoration: none;
    +
     1659.      1}
    +
     1660.      1.apidocDiv a[href]:hover {
    +
     1661.      1    text-decoration: underline;
    +
     1662.      1}
    +
     1663.      1.apidocDiv li a {
    +
     1664.      1    display: inline-block;
    +
     1665.      1    padding: 8px 0;
    +
     1666.      1}
    +
     1667.      1.apidocDiv ul {
    +
     1668.      1    list-style: none;
    +
     1669.      1    padding-left: 20px;
    +
     1670.      1}
    +
     1671.      1.apidocFooterDiv {
    +
     1672.      1    margin-top: 20px;
    +
     1673.      1    text-align: center;
    +
     1674.      1}
    +
     1675.      1.apidocModuleA {
    +
     1676.      1    font-size: 24px;
    +
     1677.      1    font-weight: bold;
    +
     1678.      1}
    +
     1679.      1.apidocSectionDiv {
    +
     1680.      1    border-top: 1px solid;
    +
     1681.      1    margin-top: 20px;
    +
     1682.      1}
    +
     1683.      1.apidocSignatureSpan {
    +
     1684.      1    color: #666;
    +
     1685.      1    white-space: pre-wrap;
    +
     1686.      1}
    +
     1687.      1</style>
    +
     1688.      1</head>
    +
     1689.      1<body>
    +
     1690.      1<div class="apidocDiv">
    +
     1691.      1<h1>API Doc for <a href="${github_repo}">${package_name} (${version})</a></h1>
    +
     1692.      1<div class="apidocSectionDiv">
    +
     1693.      1    <a href="#apidocTableOfContents1" id="apidocTableOfContents1">
    +
     1694.      1        <h1>Table of Contents</h1>
    +
     1695.      1    </a>
    +
     1696.      1    <ul>
    +
     1697.      1    `) + module_list.map(function ({
    +
     1698.      1        elem_list,
    +
     1699.      1        id,
    +
     1700.      1        moduleName
    +
     1701.      1    }) {
    +
     1702.      1        return (
    +
     1703.      1            (`
    +
     1704.      1        <li>
    +
     1705.      1            <a class="apidocModuleA" href="#${id}">Module ${moduleName}</a>
    +
     1706.      1            <ul>
    +
     1707.      1            `)
    +
     1708.     27            + elem_list.map(function ({
    +
     1709.     27                signature
    +
     1710.     27            }) {
    +
     1711.     27                return "<li>\n" + signature + "\n</li>\n";
    +
     1712.     27            }).join("")
    +
     1713.      1            + (`
    +
     1714.      1            </ul>
    +
     1715.      1        </li>
    +
     1716.      1            `)
    +
     1717.      1        );
    +
     1718.      1    }).join("") + (`
    +
     1719.      1    </ul>
    +
     1720.      1</div>
    +
     1721.      1    `) + module_list.map(function ({
    +
     1722.      1        elem_list,
    +
     1723.      1        id,
    +
     1724.      1        moduleName
    +
     1725.      1    }) {
    +
     1726.      1        return (
    +
     1727.      1            (`
    +
     1728.      1<div class="apidocSectionDiv">
    +
     1729.      1    <h1><a href="#${id}" id="${id}">Module ${moduleName}</a></h1>
    +
     1730.      1    <ul>
    +
     1731.      1            `)
    +
     1732.     27            + elem_list.map(function ({
    +
     1733.     27                source
    +
     1734.     27            }) {
    +
     1735.     27                return source;
    +
     1736.     27            }).join("")
    +
     1737.      1            + (`
    +
     1738.      1    </ul>
    +
     1739.      1</div>
    +
     1740.      1            `)
    +
     1741.      1        );
    +
     1742.      1    }).join("") + (`
    +
     1743.      1<div class="apidocFooterDiv">
    +
     1744.      1    [
    +
     1745.      1    This document was created with
    +
     1746.      1    <a href="https://github.com/jslint-org/jslint">JSLint</a>
    +
     1747.      1    ]
    +
     1748.      1</div>
    +
     1749.      1</div>
    +
     1750.      1</body>
    +
     1751.      1</html>
    +
     1752.      1    `);
    +
     1753.      1    html = html.trim().replace((
    +
     1754.      1        / +?$/gm
    +
     1755.      1    ), "") + "\n";
    +
     1756.      1    await fsWriteFileWithParents(pathname, html);
    +
     1757.      1}
    +
     1758.      1
    +
     1759. 102849function jslint_assert(condition, message) {
    +
     1760. 102849
    +
     1761. 102849// This function will throw <message> if <condition> is falsy.
    +
     1762. 102849
    +
     1763. 102847    if (condition) {
    +
     1764. 102847        return condition;
    +
     1765. 102847    }
    +
     1766.      2    throw new Error(
    +
     1767.      2        `This was caused by a bug in JSLint.
    +
     1768.      2Please open an issue with this stack-trace (and possible example-code) at
    +
     1769.      2https://github.com/jslint-org/jslint/issues.
    +
     1770.      2edition = "${jslint_edition}";
    +
     1771.      2${String(message).slice(0, 2000)}`
    +
     1772.      2    );
    +
     1773.      2}
    +
     1774.      1
    +
     1775.     38async function jslint_cli({
    +
     1776.     38    console_error,
    +
     1777.     38    console_log,
    +
     1778.     38    file,
    +
     1779.     38    import_meta_url,
    +
     1780.     38    mode_cli,
    +
     1781.     38    mode_noop,
    +
     1782.     38    option,
    +
     1783.     38    process_argv,
    +
     1784.     38    process_env,
    +
     1785.     38    process_exit,
    +
     1786.     38    source
    +
     1787.     38}) {
    +
     1788.     38
    +
     1789.     38// This function will run jslint from nodejs-cli.
    +
     1790.     38
    +
     1791.     38    let command;
    +
     1792.     38    let data;
    +
     1793.     38    let exit_code = 0;
    +
     1794.     38    let mode_report;
    +
     1795.     38    let mode_wrapper_vim;
    +
     1796.     38    let result;
    +
     1797.     38
    +
     1798.     51    function jslint_from_file({
    +
     1799.     51        code,
    +
     1800.     51        file,
    +
     1801.     51        line_offset = 0,
    +
     1802.     51        mode_conditional,
    +
     1803.     51        option = empty()
    +
     1804.     51    }) {
    +
     1805.     51        let result_from_file;
    +
     1806.     51        if (
    +
     1807.     51            mode_conditional
    +
     1808.      5            && !(
    +
     1809.      5                /^\/\*jslint\b/m
    +
     1810.      5            ).test(code.slice(0, 65536))
    +
     1811.      1        ) {
    +
     1812.      1            return;
    +
     1813.     50        }
    +
     1814.     50        option = Object.assign(empty(), option, {
    +
     1815.     50            file
    +
     1816.     50        });
    +
     1817.     50        switch ((
    +
     1818.     50            /\.\w+?$|$/m
    +
     1819.     50        ).exec(file)[0]) {
    +
     1820.     50        case ".html":
    +
     1821.      3
    +
     1822.      3// Recursively jslint embedded "<script>\n...\n</script>".
    +
     1823.      3
    +
     1824.      3            code.replace((
    +
     1825.      3                /^<script\b[^>]*?>\n([\S\s]*?\n)<\/script>$/gm
    +
     1826.      3            ), function (ignore, match1, ii) {
    +
     1827.      3                jslint_from_file({
    +
     1828.      3                    code: match1,
    +
     1829.      3                    file: file + ".<script>.js",
    +
     1830.      3                    line_offset: string_line_count(code.slice(0, ii)) + 1,
    +
     1831.      3                    option: Object.assign(empty(), {
    +
     1832.      3                        browser: true
    +
     1833.      3                    }, option)
    +
     1834.      3                });
    +
     1835.      3                return "";
    +
     1836.      3            });
    +
     1837.      3            return;
    +
     1838.      2        case ".md":
    +
     1839.      2
    +
     1840.      2// Recursively jslint embedded "node --eval '\n...\n'".
    +
     1841.      2
    +
     1842.      2            jslint_node_eval({
    +
     1843.      2                code,
    +
     1844.      2                file,
    +
     1845.      2                mode_conditional: true,
    +
     1846.      2                option
    +
     1847.      2            });
    +
     1848.      2            return;
    +
     1849.      2        case ".sh":
    +
     1850.      2
    +
     1851.      2// Recursively jslint embedded "node --eval '\n...\n'".
    +
     1852.      2
    +
     1853.      2            jslint_node_eval({
    +
     1854.      2                code,
    +
     1855.      2                file,
    +
     1856.      2                option
    +
     1857.      2            });
    +
     1858.      2            return;
    +
     1859.     43        default:
    +
     1860.     43            result_from_file = jslint("\n".repeat(line_offset) + code, option);
    +
     1861.     43        }
    +
     1862.     43
    +
     1863.     43// Print only first 10 warnings to stderr.
    +
     1864.     43
    +
     1865.     43        if (result_from_file.warnings.length > 0) {
    +
     1866.      5            exit_code = 1;
    +
     1867.      5            console_error(
    +
     1868.      5                mode_wrapper_vim
    +
     1869.      5
    +
     1870.      5// PR-349 - Print warnings in format readable by vim.
    +
     1871.      5
    +
     1872.      5                ? result_from_file.warnings.slice(0, 10).map(function ({
    +
     1873.      5                    column,
    +
     1874.      5                    line,
    +
     1875.      5                    message
    +
     1876.      5                }, ii) {
    +
     1877.      5                    return (
    +
     1878.      5                        file
    +
     1879.      5                        + ":" + ii
    +
     1880.      5                        + ":" + line
    +
     1881.      5                        + ":" + column
    +
     1882.      5                        + ":" + message
    +
     1883.      5                    );
    +
     1884.      5                }).join("\n")
    +
     1885.      5
    +
     1886.      5// Print warnings in format readable by human.
    +
     1887.      5
    +
     1888.      5                : "\u001b[1mjslint " + file + "\u001b[22m\n"
    +
     1889.     11                + result_from_file.warnings.slice(0, 10).map(function ({
    +
     1890.     11                    formatted_message
    +
     1891.     11                }) {
    +
     1892.     11                    return formatted_message;
    +
     1893.     11                }).join("\n")
    +
     1894.      5            );
    +
     1895.     43        }
    +
     1896.     43        return result_from_file;
    +
     1897.     43    }
    +
     1898.     38
    +
     1899.      4    function jslint_node_eval({
    +
     1900.      4        code,
    +
     1901.      4        file,
    +
     1902.      4        mode_conditional,
    +
     1903.      4        option = empty()
    +
     1904.      4    }) {
    +
     1905.      4        code.replace((
    +
     1906.      4            /\bnode\b.*? (?:--eval|-e) '\n([\S\s]*?\n)'/gm
    +
     1907.     26        ), function (ignore, match1, ii) {
    +
     1908.     26            jslint_from_file({
    +
     1909.     26                code: match1,
    +
     1910.     26                file: file + ".<node -e>.js",
    +
     1911.     26                line_offset: string_line_count(code.slice(0, ii)) + 1,
    +
     1912.     26                mode_conditional,
    +
     1913.     26                option: Object.assign(empty(), {
    +
     1914.     26                    beta: Boolean(
    +
     1915.     26                        process_env.JSLINT_BETA
    +
     1916.     26                        && !(
    +
     1917.     26                            /0|false|null|undefined/
    +
     1918.     26                        ).test(process_env.JSLINT_BETA)
    +
     1919.     26                    ),
    +
     1920.     26                    node: true
    +
     1921.     26                }, option)
    +
     1922.     26            });
    +
     1923.     26            return "";
    +
     1924.     26        });
    +
     1925.      4    }
    +
     1926.     38
    +
     1927.     28    function string_line_count(code) {
    +
     1928.     28
    +
     1929.     28// This function will count number of newlines in <code>.
    +
     1930.     28
    +
     1931.     28        let count;
    +
     1932.     28        let ii;
    +
     1933.     28
    +
     1934.     28// https://jsperf.com/regexp-counting-2/8
    +
     1935.     28
    +
     1936.     28        count = 0;
    +
     1937.     28        ii = 0;
    +
     1938.  23104        while (true) {
    +
     1939.  23104            ii = code.indexOf("\n", ii) + 1;
    +
     1940.  23104            if (ii === 0) {
    +
     1941.  23104                break;
    +
     1942.  23104            }
    +
     1943.  23104            count += 1;
    +
     1944.  23104        }
    +
     1945.     28        return count;
    +
     1946.     28    }
    +
     1947.     38
    +
     1948.     38// PR-396 - window.jslint
    +
     1949.     38// Check import.meta.url for directive to export jslint to window-object.
    +
     1950.     38// Useful for ES5-era browser-scripts that rely on window.jslint,
    +
     1951.     38// like CodeMirror.
    +
     1952.     38//
    +
     1953.     38// Example usage:
    +
     1954.     38// <script type="module" src="./jslint.mjs?window_jslint=1"></script>
    +
     1955.     38
    +
     1956.     22    import_meta_url = import_meta_url || jslint_import_meta_url;
    +
     1957.     38    if (
    +
     1958.     38        jslint_rgx_url_search_window_jslint.test(import_meta_url)
    +
     1959.      4        && (typeof globalThis === "object" && globalThis)
    +
     1960.      4    ) {
    +
     1961.      4        globalThis.jslint = jslint;
    +
     1962.      4    }
    +
     1963.     38
    +
     1964.     38// Feature-detect nodejs.
    +
     1965.     38
    +
     1966.     38    if (!(
    +
     1967.     38        (typeof process === "object" && process)
    +
     1968.     38        && process.versions
    +
     1969.     38        && typeof process.versions.node === "string"
    +
     1970.     38        && !mode_noop
    +
     1971.      1    )) {
    +
     1972.      1        return exit_code;
    +
     1973.     37    }
    +
     1974.     37    console_error = console_error || console.error;
    +
     1975.     37    console_log = console_log || console.log;
    +
     1976.     22    process_argv = process_argv || process.argv;
    +
     1977.     34    process_env = process_env || process.env;
    +
     1978.     24    process_exit = process_exit || process.exit;
    +
     1979.     37    await moduleFsInit();
    +
     1980.     37    if (
    +
     1981.     37        !(
    +
     1982.     37
    +
     1983.     37// Feature-detect nodejs-cli.
    +
     1984.     37
    +
     1985.     37            process.execArgv.indexOf("--eval") === -1
    +
     1986.     37            && process.execArgv.indexOf("-e") === -1
    +
     1987.     37            && (
    +
     1988.     37                (
    +
     1989.     37                    /[\/|\\]jslint(?:\.[cm]?js)?$/m
    +
     1990.     37                ).test(process_argv[1])
    +
     1991.     37                || mode_cli
    +
     1992.     37            )
    +
     1993.     20            && (
    +
     1994.     20                moduleUrl.fileURLToPath(import_meta_url)
    +
     1995.     20                ===
    +
     1996.     20                modulePath.resolve(process_argv[1])
    +
     1997.     20            )
    +
     1998.     38        )
    +
     1999.     22        && !mode_cli
    +
     2000.     17    ) {
    +
     2001.     17        return exit_code;
    +
     2002.     20    }
    +
     2003.     20
    +
     2004.     20// init commmand
    +
     2005.     20
    +
     2006.     20    command = String(process_argv[2]).split("=");
    +
     2007.     20    command[1] = command.slice(1).join("=");
    +
     2008.     20
    +
     2009.     20    switch (command[0]) {
    +
     2010.     20
    +
     2011.     20// PR-362 - Add API Doc.
    +
     2012.     20
    +
     2013.     20    case "jslint_apidoc":
    +
     2014.      1        await jslint_apidoc(Object.assign(JSON.parse(process_argv[3]), {
    +
     2015.      1            pathname: command[1]
    +
     2016.      1        }));
    +
     2017.      1        return;
    +
     2018.     38
    +
     2019.     38// PR-363 - Add command jslint_report.
    +
     2020.     38
    +
     2021.      6    case "jslint_report":
    +
     2022.      6        mode_report = command[1];
    +
     2023.      6        process_argv = process_argv.slice(1);
    +
     2024.      6        break;
    +
     2025.     38
    +
     2026.     38// COMMIT-b26d6df2 - Add command jslint_wrapper_vim.
    +
     2027.     38
    +
     2028.      1    case "jslint_wrapper_vim":
    +
     2029.      1        mode_wrapper_vim = true;
    +
     2030.      1        process_argv = process_argv.slice(1);
    +
     2031.      1        break;
    +
     2032.     38
    +
     2033.     38// PR-364 - Add command v8_coverage_report.
    +
     2034.     38
    +
     2035.      7    case "v8_coverage_report":
    +
     2036.      7        await v8CoverageReportCreate({
    +
     2037.      7            consoleError: console_error,
    +
     2038.      7            coverageDir: command[1],
    +
     2039.      7            processArgv: process_argv.slice(3)
    +
     2040.      7        });
    +
     2041.      7        return;
    +
     2042.     12    }
    +
     2043.     12
    +
     2044.     12// Normalize file relative to process.cwd().
    +
     2045.     12
    +
     2046.     12    process_argv.slice(2).some(function (arg) {
    +
     2047.     12        if (!arg.startsWith("-")) {
    +
     2048.     12            file = file || arg;
    +
     2049.     12            return true;
    +
     2050.     12        }
    +
     2051.     12    });
    +
     2052.     12    if (!file) {
    +
     2053.      1        return;
    +
     2054.     11    }
    +
     2055.     11    file = modulePath.resolve(file) + "/";
    +
     2056.     11    if (file.startsWith(process.cwd() + "/")) {
    +
     2057.     11        file = file.replace(process.cwd() + "/", "").slice(0, -1) || ".";
    +
     2058.     11    }
    +
     2059.     11    file = file.replace((
    +
     2060.     11        /\\/g
    +
     2061.     11    ), "/").replace((
    +
     2062.     11        /\/$/g
    +
     2063.     11    ), "");
    +
     2064.     11    if (source) {
    +
     2065.      7        data = source;
    +
     2066.      7    } else {
    +
     2067.      4
    +
     2068.      4// jslint_cli - jslint directory.
    +
     2069.      4
    +
     2070.      4        try {
    +
     2071.      4            data = await moduleFs.promises.readdir(file, "utf8");
    +
     2072.      4        } catch (ignore) {}
    +
     2073.      4        if (data) {
    +
     2074.     34            await Promise.all(data.map(async function (file2) {
    +
     2075.     34                let code;
    +
     2076.     34                let time_start = Date.now();
    +
     2077.     34                file2 = file + "/" + file2;
    +
     2078.     34                switch ((
    +
     2079.     34                    /\.\w+?$|$/m
    +
     2080.     34                ).exec(file2)[0]) {
    +
     2081.      4                case ".cjs":
    +
     2082.      5                case ".html":
    +
     2083.      8                case ".js":
    +
     2084.     10                case ".json":
    +
     2085.     12                case ".md":
    +
     2086.     14                case ".mjs":
    +
     2087.     16                case ".sh":
    +
     2088.     16                    break;
    +
     2089.     18                default:
    +
     2090.     18                    return;
    +
     2091.     16                }
    +
     2092.     16                try {
    +
     2093.     16                    code = await moduleFs.promises.readFile(file2, "utf8");
    +
     2094.     15                } catch (ignore) {
    +
     2095.      4                    return;
    +
     2096.     15                }
    +
     2097.     15                if (
    +
     2098.     15                    (
    +
     2099.     15                        /(?:\b|_)(?:lock|min|raw|rollup)(?:\b|_)/
    +
     2100.     15                    ).test(file2)
    +
     2101.     15                    || !(code && code.length < 1048576)
    +
     2102.      4                ) {
    +
     2103.      4                    return;
    +
     2104.     14                }
    +
     2105.     14                jslint_from_file({
    +
     2106.     14                    code,
    +
     2107.     14                    file: file2,
    +
     2108.     14                    option
    +
     2109.     14                });
    +
     2110.     14                console_error(
    +
     2111.     14                    "jslint - " + (Date.now() - time_start) + "ms - " + file2
    +
     2112.     14                );
    +
     2113.     14            }));
    +
     2114.      4            process_exit(exit_code);
    +
     2115.      4            return exit_code;
    +
     2116.      4        }
    +
     2117.      4
    +
     2118.      4// jslint_cli - jslint file.
    +
     2119.      4
    +
     2120.      4        try {
    +
     2121.      4            data = await moduleFs.promises.readFile(file, "utf8");
    +
     2122.      4        } catch (err) {
    +
     2123.      4            console_error(err);
    +
     2124.      4            exit_code = 1;
    +
     2125.      4            process_exit(exit_code);
    +
     2126.      4            return exit_code;
    +
     2127.      4        }
    +
     2128.      9    }
    +
     2129.      9    result = jslint_from_file({
    +
     2130.      9        code: data,
    +
     2131.      9        file,
    +
     2132.      9        option
    +
     2133.      9    });
    +
     2134.      9    if (mode_report) {
    +
     2135.      6        result = jslint.jslint_report(result);
    +
     2136.      6        result = `<body class="JSLINT_ JSLINT_REPORT_">\n${result}</body>\n`;
    +
     2137.      6        await fsWriteFileWithParents(mode_report, result);
    +
     2138.      9    }
    +
     2139.      9    process_exit(exit_code);
    +
     2140.      9    return exit_code;
    +
     2141.      9}
    +
     2142.      1
    +
     2143.    668function jslint_phase1_split() {
    +
     2144.    668
    +
     2145.    668// PHASE 1. Split <source> by newlines into <line_list>.
    +
     2146.    668
    +
     2147.    668    return;
    +
     2148.    668}
    +
     2149.      1
    +
     2150.    668function jslint_phase2_lex(state) {
    +
     2151.    668
    +
     2152.    668// PHASE 2. Lex <line_list> into <token_list>.
    +
     2153.    668
    +
     2154.    668    let {
    +
     2155.    668        artifact,
    +
     2156.    668        directive_list,
    +
     2157.    668        global_dict,
    +
     2158.    668        global_list,
    +
     2159.    668        line_list,
    +
     2160.    668        option_dict,
    +
     2161.    668        stop,
    +
     2162.    668        stop_at,
    +
     2163.    668        tenure,
    +
     2164.    668        test_cause,
    +
     2165.    668        token_global,
    +
     2166.    668        token_list,
    +
     2167.    668        warn,
    +
     2168.    668        warn_at
    +
     2169.    668    } = state;
    +
     2170.    668    let char;                   // The current character being lexed.
    +
     2171.    668    let column = 0;             // The column number of the next character.
    +
     2172.    668    let from;                   // The starting column number of the token.
    +
     2173.    668    let from_mega;              // The starting column of megastring.
    +
     2174.    668    let line = 0;               // The line number of the next character.
    +
     2175.    668    let line_disable;           // The starting line of "/*jslint-disable*/".
    +
     2176.    668    let line_mega;              // The starting line of megastring.
    +
     2177.    668    let line_source = "";       // The remaining line source string.
    +
     2178.    668    let line_whole = "";        // The whole line source string.
    +
     2179.    668    let mode_digits_empty_string = 1;
    +
     2180.    668    let mode_digits_numeric_separator = 2;
    +
     2181.    668    let mode_directive = true;  // true if directives are still allowed.
    +
     2182.    668    let mode_mega = false;      // true if currently parsing a megastring
    +
     2183.    668                                // ... literal.
    +
     2184.    668    let mode_regexp;            // true if regular expression literal seen on
    +
     2185.    668                                // ... this line.
    +
     2186.    668    let paren_backtrack_list = [];      // List of most recent "(" tokens at any
    +
     2187.    668                                        // ... paren-depth.
    +
     2188.    668    let paren_depth = 0;        // Keeps track of current paren-depth.
    +
     2189.    668    let snippet = "";           // A piece of string.
    +
     2190.    668    let token_1;                // The first token.
    +
     2191.    668    let token_prv = token_global;       // The previous token including
    +
     2192.    668                                        // ... comments.
    +
     2193.    668    let token_prv_expr = token_global;  // The previous token excluding
    +
     2194.    668                                        // ... comments.
    +
     2195.    668
    +
     2196.    668// Most tokens, including the identifiers, operators, and punctuators, can be
    +
     2197.    668// found with a regular expression. Regular expressions cannot correctly match
    +
     2198.    668// regular expression literals, so we will match those the hard way. String
    +
     2199.    668// literals and number literals can be matched by regular expressions, but they
    +
     2200.    668// don't provide good warnings. The functions char_after, char_before,
    +
     2201.    668// read_digits, and char_after_escape help in the parsing of literals.
    +
     2202.    668
    +
     2203. 238213    function char_after(match) {
    +
     2204. 238213
    +
     2205. 238213// Get the next character from the source line. Remove it from the line_source,
    +
     2206. 238213// and append it to the snippet. Optionally check that the previous character
    +
     2207. 238213// matched an expected value.
    +
     2208. 238213
    +
     2209.   5986        if (match !== undefined && char !== match) {
    +
     2210.     10
    +
     2211.     10// test_cause:
    +
     2212.     10// ["aa=/[", "char_after", "expected_a", "]", 5]
    +
     2213.     10// ["aa=/aa{/", "char_after", "expected_a_b", "/", 8]
    +
     2214.     10
    +
     2215.     10            return (
    +
     2216.     10                char === ""
    +
     2217.     10                ? stop_at("expected_a", line, column - 1, match)
    +
     2218.     10                : stop_at("expected_a_b", line, column, match, char)
    +
     2219.     10            );
    +
     2220. 238203        }
    +
     2221. 238203        char = line_source.slice(0, 1);
    +
     2222. 238203        line_source = line_source.slice(1);
    +
     2223. 238203        snippet += char || " ";
    +
     2224. 238213        column += 1;
    +
     2225. 238213        return char;
    +
     2226. 238213    }
    +
     2227.    668
    +
     2228.   2953    function char_after_escape(extra) {
    +
     2229.   2953
    +
     2230.   2953// Validate char after escape "\\".
    +
     2231.   2953
    +
     2232.   2953        char_after("\\");
    +
     2233.   2953        switch (char) {
    +
     2234.      1        case "":
    +
     2235.      1
    +
     2236.      1// test_cause:
    +
     2237.      1// ["\"\\", "char_after_escape", "unclosed_string", "", 2]
    +
     2238.      1
    +
     2239.      1            return stop_at("unclosed_string", line, column);
    +
     2240.    219        case "/":
    +
     2241.    219            return char_after();
    +
     2242.    355        case "\\":
    +
     2243.    355            return char_after();
    +
     2244.      1        case "`":
    +
     2245.      1            return char_after();
    +
     2246.     62        case "b":
    +
     2247.     62            return char_after();
    +
     2248.      6        case "f":
    +
     2249.      6            return char_after();
    +
     2250.   1015        case "n":
    +
     2251.   1015            return char_after();
    +
     2252.     32        case "r":
    +
     2253.     32            return char_after();
    +
     2254.     24        case "t":
    +
     2255.     24
    +
     2256.     24// test_cause:
    +
     2257.     24// ["\"\\/\\\\\\`\\b\\f\\n\\r\\t\"", "char_after_escape", "char_after", "", 0]
    +
     2258.     24
    +
     2259.     24            test_cause("char_after");
    +
     2260.     24            return char_after();
    +
     2261.    376        case "u":
    +
     2262.    376            if (char_after("u") === "{") {
    +
     2263.    376                if (state.mode_json) {
    +
     2264.    376
    +
     2265.    376// test_cause:
    +
     2266.    376// ["[\"\\u{12345}\"]", "char_after_escape", "unexpected_a", "{", 5]
    +
     2267.    376
    +
     2268.    376                    warn_at("unexpected_a", line, column, char);
    +
     2269.    376                }
    +
     2270.    376                if (read_digits("x", undefined) > 5) {
    +
     2271.    376
    +
     2272.    376// test_cause:
    +
     2273.    376// ["\"\\u{123456}\"", "char_after_escape", "too_many_digits", "", 11]
    +
     2274.    376
    +
     2275.    376                    warn_at("too_many_digits", line, column);
    +
     2276.    376                }
    +
     2277.    376                if (char !== "}") {
    +
     2278.    376
    +
     2279.    376// test_cause:
    +
     2280.    376// ["\"\\u{12345\"", "char_after_escape", "expected_a_before_b", "\"", 10]
    +
     2281.    376
    +
     2282.    376                    stop_at("expected_a_before_b", line, column, "}", char);
    +
     2283.    376                }
    +
     2284.    376                return char_after();
    +
     2285.    376            }
    +
     2286.    376            char_before();
    +
     2287.    376            if (read_digits("x", mode_digits_empty_string) < 4) {
    +
     2288.    376
    +
     2289.    376// test_cause:
    +
     2290.    376// ["\"\\u0\"", "char_after_escape", "expected_four_digits", "", 5]
    +
     2291.    376
    +
     2292.    376                warn_at("expected_four_digits", line, column);
    +
     2293.    376            }
    +
     2294.    376            return;
    +
     2295.    862        default:
    +
     2296.    862            if (extra && extra.indexOf(char) >= 0) {
    +
     2297.    862                return char_after();
    +
     2298.    862            }
    +
     2299.    862
    +
     2300.    862// test_cause:
    +
     2301.    862// ["\"\\0\"", "char_after_escape", "unexpected_a_before_b", "0", 3]
    +
     2302.    862
    +
     2303.    862            warn_at("unexpected_a_before_b", line, column, "\\", char);
    +
     2304.   2953        }
    +
     2305.   2953    }
    +
     2306.    668
    +
     2307.  10013    function char_before() {
    +
     2308.  10013
    +
     2309.  10013// Back up one character by moving a character from the end of the snippet to
    +
     2310.  10013// the front of the line_source.
    +
     2311.  10013
    +
     2312.  10013        char = snippet.slice(-1);
    +
     2313.  10013        line_source = char + line_source;
    +
     2314.  10013        column -= char.length;
    +
     2315.  10013
    +
     2316.  10013// Remove last character from snippet.
    +
     2317.  10013
    +
     2318.  10013        snippet = snippet.slice(0, -1);
    +
     2319.  10013        return char;
    +
     2320.  10013    }
    +
     2321.    668
    +
     2322.   8924    function check_numeric_separator(digits, column) {
    +
     2323.   8924
    +
     2324.   8924// This function will check for illegal numeric-separator in <digits>.
    +
     2325.   8924
    +
     2326.   8924        digits.replace((
    +
     2327.   8924            jslint_rgx_numeric_separator_illegal
    +
     2328.      6        ), function (ignore, ii) {
    +
     2329.      6
    +
     2330.      6// test_cause:
    +
     2331.      6// ["0x0_0_;", "check_numeric_separator", "illegal_num_separator", "", 6]
    +
     2332.      6// ["0x0_0__0;", "check_numeric_separator", "illegal_num_separator", "", 6]
    +
     2333.      6// ["aa=1_2_;", "check_numeric_separator", "illegal_num_separator", "", 7]
    +
     2334.      6// ["aa=1_2__3;", "check_numeric_separator", "illegal_num_separator", "", 7]
    +
     2335.      6// ["aa=1_2_n;", "check_numeric_separator", "illegal_num_separator", "", 7]
    +
     2336.      6
    +
     2337.      6            warn_at("illegal_num_separator", line, column + ii + 1);
    +
     2338.      6            return "";
    +
     2339.      6        });
    +
     2340.   8924    }
    +
     2341.    668
    +
     2342.  11221    function lex_comment() {
    +
     2343.  11221        let body;
    +
     2344.  11221        let ii = 0;
    +
     2345.  11221        let jj = 0;
    +
     2346.  11221        let the_comment;
    +
     2347.  11221
    +
     2348.  11221// Create a comment object. Comments are not allowed in JSON text. Comments can
    +
     2349.  11221// include directives and notices of incompletion.
    +
     2350.  11221
    +
     2351.  11221// Create token from comment //....
    +
     2352.  11221
    +
     2353.  11107        if (snippet === "//") {
    +
     2354.  11107            snippet = line_source;
    +
     2355.  11107            line_source = "";
    +
     2356.  11107            the_comment = token_create("(comment)", snippet);
    +
     2357.  11107            if (mode_mega) {
    +
     2358.  11107
    +
     2359.  11107// test_cause:
    +
     2360.  11107// ["`${//}`", "lex_comment", "unexpected_comment", "`", 4]
    +
     2361.  11107
    +
     2362.  11107                warn("unexpected_comment", the_comment, "`");
    +
     2363.  11107            }
    +
     2364.  11107
    +
     2365.  11107// Create token from comment /*...*/.
    +
     2366.  11107
    +
     2367.  11107        } else {
    +
     2368.    114            snippet = [];
    +
     2369.    114            if (line_source[0] === "/") {
    +
     2370.    114
    +
     2371.    114// test_cause:
    +
     2372.    114// ["/*/", "lex_comment", "unexpected_a", "/", 2]
    +
     2373.    114
    +
     2374.    114                warn_at("unexpected_a", line, column + ii, "/");
    +
     2375.    114            }
    +
     2376.    114
    +
     2377.    114// Lex/loop through each line until "*/".
    +
     2378.    114
    +
     2379.    696            while (true) {
    +
     2380.    696                // jslint_rgx_star_slash
    +
     2381.    696                ii = line_source.indexOf("*/");
    +
     2382.    696                if (ii >= 0) {
    +
     2383.    696                    break;
    +
     2384.    696                }
    +
     2385.    696                // jslint_rgx_slash_star
    +
     2386.    696                ii = line_source.indexOf("/*");
    +
     2387.    696                if (ii >= 0) {
    +
     2388.    696
    +
     2389.    696// test_cause:
    +
     2390.    696// ["/*/*", "lex_comment", "nested_comment", "", 2]
    +
     2391.    696
    +
     2392.    696                    warn_at("nested_comment", line, column + ii);
    +
     2393.    696                }
    +
     2394.    696                snippet.push(line_source);
    +
     2395.    696                line_source = read_line();
    +
     2396.    696                if (line_source === undefined) {
    +
     2397.    696
    +
     2398.    696// test_cause:
    +
     2399.    696// ["/*", "lex_comment", "unclosed_comment", "", 1]
    +
     2400.    696
    +
     2401.    696                    return stop_at("unclosed_comment", line, column);
    +
     2402.    696                }
    +
     2403.    696            }
    +
     2404.    114            jj = line_source.slice(0, ii).search(
    +
     2405.    114                jslint_rgx_slash_star_or_slash
    +
     2406.    114            );
    +
     2407.    114            if (jj >= 0) {
    +
     2408.    114
    +
     2409.    114// test_cause:
    +
     2410.    114// ["/*/**/", "lex_comment", "nested_comment", "", 2]
    +
     2411.    114
    +
     2412.    114                warn_at("nested_comment", line, column + jj);
    +
     2413.    114            }
    +
     2414.    114            snippet.push(line_source.slice(0, ii));
    +
     2415.    114            snippet = snippet.join(" ");
    +
     2416.    114            column += ii + 2;
    +
     2417.    114            line_source = line_source.slice(ii + 2);
    +
     2418.    114            the_comment = token_create("(comment)", snippet);
    +
     2419.  11218        }
    +
     2420.  11218
    +
     2421.  11218// Uncompleted work comment.
    +
     2422.  11218
    +
     2423.  11218        if (!option_dict.devel && jslint_rgx_todo.test(snippet)) {
    +
     2424.     16
    +
     2425.     16// test_cause:
    +
     2426.     16// ["//todo", "lex_comment", "todo_comment", "(comment)", 1] //jslint-ignore-line
    +
     2427.     16
    +
     2428.     16            warn("todo_comment", the_comment);
    +
     2429.  11218        }
    +
     2430.  11218
    +
     2431.  11218// Lex directives in comment.
    +
     2432.  11218
    +
     2433.  11218        [
    +
     2434.  11218            the_comment.directive, body
    +
     2435.  11218        ] = Array.from(snippet.match(jslint_rgx_directive) || []).slice(1);
    +
     2436.  11141        if (the_comment.directive === undefined) {
    +
     2437.  11141            return the_comment;
    +
     2438.  11141        }
    +
     2439.     77        directive_list.push(the_comment);
    +
     2440.     77        if (!mode_directive) {
    +
     2441.      1
    +
     2442.      1// test_cause:
    +
     2443.      1// ["0\n/*global aa*/", "lex_comment", "misplaced_directive_a", "global", 1]
    +
     2444.      1
    +
     2445.      1            warn_at("misplaced_directive_a", line, from, the_comment.directive);
    +
     2446.      1            return the_comment;
    +
     2447.     76        }
    +
     2448.     76
    +
     2449.     76// lex_directive();
    +
     2450.     76// JSLint recognizes three directives that can be encoded in comments. This
    +
     2451.     76// function processes one item, and calls itself recursively to process the
    +
     2452.     76// next one.
    +
     2453.     76
    +
     2454.     76// Lex/loop through each directive in /*...*/
    +
     2455.     76
    +
     2456.     76        ii = 0;
    +
     2457.   1835        body.replace(jslint_rgx_directive_part, function (
    +
     2458.   1835            match0,
    +
     2459.   1835            key,
    +
     2460.   1835            val,
    +
     2461.   1835            jj
    +
     2462.   1835        ) {
    +
     2463.     76            if (ii !== jj) {
    +
     2464.     76
    +
     2465.     76// test_cause:
    +
     2466.     76// ["/*jslint !*/", "lex_comment", "bad_directive_a", "!", 1]
    +
     2467.     76
    +
     2468.     76                return stop("bad_directive_a", the_comment, body.slice(ii));
    +
     2469.   1834            }
    +
     2470.   1834            if (match0 === "") {
    +
     2471.     76                return "";
    +
     2472.   1759            }
    +
     2473.   1759            ii += match0.length;
    +
     2474.   1759            switch (the_comment.directive) {
    +
     2475.   1759            case "global":
    +
     2476.     76                if (val) {
    +
     2477.     76
    +
     2478.     76// test_cause:
    +
     2479.     76// ["/*global aa:false*/", "lex_comment", "bad_option_a", "aa:false", 1]
    +
     2480.     76
    +
     2481.     76                    warn("bad_option_a", the_comment, key + ":" + val);
    +
     2482.     76                }
    +
     2483.     76                global_dict[key] = "user-defined";
    +
     2484.     76
    +
     2485.     76// PR-347 - Disable warning "unexpected_directive_a".
    +
     2486.     76//
    +
     2487.     76//                 state.mode_module = the_comment;
    +
     2488.     76
    +
     2489.     76                break;
    +
     2490.     99            case "jslint":
    +
     2491.     99                if (!option_set_item(key, val !== "false")) {
    +
     2492.     99
    +
     2493.     99// test_cause:
    +
     2494.     99// ["/*jslint undefined*/", "lex_comment", "bad_option_a", "undefined", 1]
    +
     2495.     99
    +
     2496.     99                    warn("bad_option_a", the_comment, key);
    +
     2497.     99                }
    +
     2498.     99                break;
    +
     2499.   1651            case "property":
    +
     2500.   1651                state.mode_property = true;
    +
     2501.   1651                tenure[key] = true;
    +
     2502.   1651                break;
    +
     2503.   1759            }
    +
     2504.   1759            return "";
    +
     2505.   1759        });
    +
     2506.     76        return the_comment;
    +
     2507.     76    }
    +
     2508.    668
    +
     2509.    789    function lex_megastring() {
    +
     2510.    789        let id;
    +
     2511.    789        let match;
    +
     2512.    789
    +
     2513.    789// The token is a megastring. We don't allow any kind of mega nesting.
    +
     2514.    789
    +
     2515.      1        if (mode_mega) {
    +
     2516.      1
    +
     2517.      1// test_cause:
    +
     2518.      1// ["`${`", "lex_megastring", "expected_a_b", "`", 4]
    +
     2519.      1
    +
     2520.      1            return stop_at("expected_a_b", line, column, "}", "`");
    +
     2521.    788        }
    +
     2522.    788        from_mega = from;
    +
     2523.    788        line_mega = line;
    +
     2524.    788        mode_mega = true;
    +
     2525.    788        snippet = "";
    +
     2526.    788
    +
     2527.    788// Parsing a mega literal is tricky. First create a ` token.
    +
     2528.    788
    +
     2529.    788        token_create("`");
    +
     2530.    788        from += 1;
    +
     2531.    788
    +
     2532.    788// Then loop, building up a string, possibly from many lines, until seeing
    +
     2533.    788// the end of file, a closing `, or a ${ indicting an expression within the
    +
     2534.    788// string.
    +
     2535.    788
    +
     2536.   5963        while (true) {
    +
     2537.   5963            match = line_source.match(jslint_rgx_mega) || {
    +
     2538.   5963                "0": "",
    +
     2539.   5963                index: 0
    +
     2540.   5963            };
    +
     2541.   5963            snippet += line_source.slice(0, match.index);
    +
     2542.   5963            column += match.index;
    +
     2543.   5963            line_source = line_source.slice(match.index);
    +
     2544.   5963            match = match[0];
    +
     2545.   5963            switch (match) {
    +
     2546.   5963            case "${":
    +
     2547.   5963
    +
     2548.   5963// if either ` or ${ was found, then the preceding joins the snippet to become
    +
     2549.   5963// a string token.
    +
     2550.   5963
    +
     2551.   5963                token_create("(string)", snippet).quote = "`";
    +
     2552.   5963                snippet = "";
    +
     2553.   5963
    +
     2554.   5963// If ${, then create tokens that will become part of an expression until
    +
     2555.   5963// a } token is made.
    +
     2556.   5963
    +
     2557.   5963                column += 2;
    +
     2558.   5963                token_create("${");
    +
     2559.   5963                line_source = line_source.slice(2);
    +
     2560.   5963
    +
     2561.   5963// Lex/loop through each token inside megastring-expression `${...}`.
    +
     2562.   5963
    +
     2563.   5963                while (true) {
    +
     2564.   5963                    id = lex_token().id;
    +
     2565.   5963                    if (id === "{") {
    +
     2566.   5963
    +
     2567.   5963// test_cause:
    +
     2568.   5963// ["`${{", "lex_megastring", "expected_a_b", "{", 4]
    +
     2569.   5963
    +
     2570.   5963                        return stop_at("expected_a_b", line, column, "}", "{");
    +
     2571.   5963                    }
    +
     2572.   5963                    if (id === "}") {
    +
     2573.   5963                        break;
    +
     2574.   5963                    }
    +
     2575.   5963                }
    +
     2576.   5963                break;
    +
     2577.   5963            case "\\":
    +
     2578.   5963                snippet += line_source.slice(0, 2);
    +
     2579.   5963                line_source = line_source.slice(2);
    +
     2580.   5963                column += 2;
    +
     2581.   5963                break;
    +
     2582.   5963            case "`":
    +
     2583.   5963
    +
     2584.   5963// if either ` or ${ was found, then the preceding joins the snippet to become
    +
     2585.   5963// a string token.
    +
     2586.   5963
    +
     2587.   5963                token_create("(string)", snippet).quote = "`";
    +
     2588.   5963                snippet = "";
    +
     2589.   5963
    +
     2590.   5963// Terminate megastring with `.
    +
     2591.   5963
    +
     2592.   5963                line_source = line_source.slice(1);
    +
     2593.   5963                column += 1;
    +
     2594.   5963                mode_mega = false;
    +
     2595.   5963                return token_create("`");
    +
     2596.   5963            default:
    +
     2597.   5963
    +
     2598.   5963// If neither ` nor ${ is seen, then the whole line joins the snippet.
    +
     2599.   5963
    +
     2600.   5963                snippet += line_source + "\n";
    +
     2601.   5963                if (read_line() === undefined) {
    +
     2602.   5963
    +
     2603.   5963// test_cause:
    +
     2604.   5963// ["`", "lex_megastring", "unclosed_mega", "", 1]
    +
     2605.   5963
    +
     2606.   5963                    return stop_at("unclosed_mega", line_mega, from_mega);
    +
     2607.   5963                }
    +
     2608.   5963            }
    +
     2609.   5963        }
    +
     2610.    789    }
    +
     2611.    668
    +
     2612.   8851    function lex_number() {
    +
     2613.   8851        let prefix = snippet;
    +
     2614.   8851
    +
     2615.   8851// PR-390 - Add numeric-separator check.
    +
     2616.   8851
    +
     2617.   8851        check_numeric_separator(prefix, column - prefix.length);
    +
     2618.   8851        char_after();
    +
     2619.   2880        switch (prefix === "0" && char) {
    +
     2620.      3        case "b":
    +
     2621.      6        case "o":
    +
     2622.     33        case "x":
    +
     2623.     33            read_digits(char, mode_digits_numeric_separator);
    +
     2624.     33
    +
     2625.     33// PR-351 - Ignore BigInt suffix 'n'.
    +
     2626.     33
    +
     2627.     33            if (char === "n") {
    +
     2628.     33                char_after("n");
    +
     2629.     33            }
    +
     2630.     33            break;
    +
     2631.   8818        default:
    +
     2632.   8818            if (char === ".") {
    +
     2633.   8818                read_digits("d", mode_digits_numeric_separator);
    +
     2634.   8818            }
    +
     2635.   8818            if (char === "E" || char === "e") {
    +
     2636.   8818                char_after(char);
    +
     2637.   8818                if (char !== "+" && char !== "-") {
    +
     2638.   8818                    char_before();
    +
     2639.   8818                }
    +
     2640.   8818                read_digits("d", mode_digits_numeric_separator);
    +
     2641.   8818            }
    +
     2642.   8851        }
    +
     2643.   8851
    +
     2644.   8851// If the next character after a number is a digit or letter, then something
    +
     2645.   8851// unexpected is going on.
    +
     2646.   8851
    +
     2647.   8851        if (
    +
     2648.   1567            (char >= "0" && char <= "9")
    +
     2649.     44            || (char >= "a" && char <= "z")
    +
     2650.   8850            || (char >= "A" && char <= "Z")
    +
     2651.      1        ) {
    +
     2652.      1
    +
     2653.      1// test_cause:
    +
     2654.      1// ["0a", "lex_number", "unexpected_a_after_b", "0", 2]
    +
     2655.      1
    +
     2656.      1            return stop_at(
    +
     2657.      1                "unexpected_a_after_b",
    +
     2658.      1                line,
    +
     2659.      1                column,
    +
     2660.      1                snippet.slice(-1),
    +
     2661.      1                snippet.slice(0, -1)
    +
     2662.      1            );
    +
     2663.   8850        }
    +
     2664.   8850        char_before();
    +
     2665.   8850        return token_create("(number)", snippet);
    +
     2666.   8850    }
    +
     2667.    668
    +
     2668.    583    function lex_regexp() {
    +
     2669.    583
    +
     2670.    583// Regexp
    +
     2671.    583// Lex a regular expression literal.
    +
     2672.    583
    +
     2673.    583        let flag;
    +
     2674.    583        let mode_regexp_multiline;
    +
     2675.    583        let result;
    +
     2676.    583        let value;
    +
     2677.    583        mode_regexp = true;
    +
     2678.    583
    +
     2679.    209        function lex_regexp_bracketed() {
    +
     2680.    209            let mode_regexp_range;
    +
     2681.    209
    +
     2682.    209// RegExp
    +
     2683.    209// Match a class.
    +
     2684.    209
    +
     2685.    209            char_after("[");
    +
     2686.     35            if (char === "^") {
    +
     2687.     35                char_after("^");
    +
     2688.     35            }
    +
     2689.    902            while (true) {
    +
     2690.    902
    +
     2691.    902// RegExp
    +
     2692.    902// Match a character in a character class.
    +
     2693.    902
    +
     2694.    902                switch (char) {
    +
     2695.    902                case "":
    +
     2696.    902                case "]":
    +
     2697.    902
    +
     2698.    902// test_cause:
    +
     2699.    902// ["aa=/[", "lex_regexp_bracketed", "closer", "", 0]
    +
     2700.    902// ["aa=/[]/", "lex_regexp_bracketed", "closer", "", 0]
    +
     2701.    902
    +
     2702.    902                    test_cause("closer");
    +
     2703.    902                    if (mode_regexp_range) {
    +
     2704.    902
    +
     2705.    902// test_cause:
    +
     2706.    902// ["aa=/[0-]/", "lex_regexp_bracketed", "unexpected_a", "-", 7]
    +
     2707.    902
    +
     2708.    902                        warn_at("unexpected_a", line, column - 1, "-");
    +
     2709.    902                    }
    +
     2710.    902                    return char_after("]");
    +
     2711.    902
    +
     2712.    902// PR-362 - Relax regexp-warning against using <space>.
    +
     2713.    902//
    +
     2714.    902//                 case " ":
    +
     2715.    902//
    +
     2716.    902// // test_cause:
    +
     2717.    902// // ["aa=/[ ]/", "lex_regexp_bracketed", "expected_a_b", " ", 6]
    +
     2718.    902//
    +
     2719.    902//                     warn_at("expected_a_b", line, column, "\\u0020", " ");
    +
     2720.    902//                     break;
    +
     2721.    902
    +
     2722.    902                case "-":
    +
     2723.    902                case "/":
    +
     2724.    902                case "[":
    +
     2725.    902                case "^":
    +
     2726.    902
    +
     2727.    902// test_cause:
    +
     2728.    902// ["aa=/[-]/", "lex_regexp_bracketed", "expected_a_before_b", "-", 6]
    +
     2729.    902// ["aa=/[.^]/", "lex_regexp_bracketed", "expected_a_before_b", "^", 7]
    +
     2730.    902// ["aa=/[/", "lex_regexp_bracketed", "expected_a_before_b", "/", 6]
    +
     2731.    902// ["aa=/[\\\\/]/", "lex_regexp_bracketed", "expected_a_before_b", "/", 8]
    +
     2732.    902// ["aa=/[\\\\[]/", "lex_regexp_bracketed", "expected_a_before_b", "[", 8]
    +
     2733.    902
    +
     2734.    902                    warn_at("expected_a_before_b", line, column, "\\", char);
    +
     2735.    902                    break;
    +
     2736.    902                case "\\":
    +
     2737.    902                    char_after_escape("BbDdSsWw-[]^");
    +
     2738.    902                    char_before();
    +
     2739.    902                    break;
    +
     2740.    902                case "`":
    +
     2741.    902                    if (mode_mega) {
    +
     2742.    902
    +
     2743.    902// test_cause:
    +
     2744.    902// ["`${/[`]/}`", "lex_regexp_bracketed", "unexpected_a", "`", 6]
    +
     2745.    902
    +
     2746.    902                        warn_at("unexpected_a", line, column, "`");
    +
     2747.    902                    }
    +
     2748.    902                    break;
    +
     2749.    902                }
    +
     2750.    902                char_after();
    +
     2751.    902                mode_regexp_range = false;
    +
     2752.    902                if (char === "-") {
    +
     2753.    902
    +
     2754.    902// RegExp
    +
     2755.    902// Match a range of subclasses.
    +
     2756.    902
    +
     2757.    902                    mode_regexp_range = true;
    +
     2758.    902                    char_after("-");
    +
     2759.    902                }
    +
     2760.    902            }
    +
     2761.    209        }
    +
     2762.    583
    +
     2763.    794        function lex_regexp_group() {
    +
     2764.    794
    +
     2765.    794// RegExp
    +
     2766.    794// Lex sequence of characters in regexp.
    +
     2767.    794
    +
     2768.    794            switch (char) {
    +
     2769.      1            case "":
    +
     2770.      1                warn_at("expected_regexp_factor_a", line, column, char);
    +
     2771.      1                break;
    +
     2772.      1            case ")":
    +
     2773.      1                warn_at("expected_regexp_factor_a", line, column, char);
    +
     2774.      1                break;
    +
     2775.      1            case "]":
    +
     2776.      1
    +
     2777.      1// test_cause:
    +
     2778.      1// ["/ /", "lex_regexp_group", "expected_regexp_factor_a", "", 3]
    +
     2779.      1// ["aa=/)", "lex_regexp_group", "expected_regexp_factor_a", ")", 5]
    +
     2780.      1// ["aa=/]", "lex_regexp_group", "expected_regexp_factor_a", "]", 5]
    +
     2781.      1
    +
     2782.      1                warn_at("expected_regexp_factor_a", line, column, char);
    +
     2783.      1                break;
    +
     2784.    794            }
    +
     2785.   5592            while (true) {
    +
     2786.   5592                switch (char) {
    +
     2787.   5592                case "":
    +
     2788.   5592                case ")":
    +
     2789.   5592                case "/":
    +
     2790.   5592                case "]":
    +
     2791.   5592                    return;
    +
     2792.   5592
    +
     2793.   5592// PR-362 - Relax regexp-warning against using <space>.
    +
     2794.   5592//
    +
     2795.   5592//                 case " ":
    +
     2796.   5592//
    +
     2797.   5592// // test_cause:
    +
     2798.   5592// // ["aa=/ /", "lex_regexp_group", "expected_a_b", " ", 5]
    +
     2799.   5592//
    +
     2800.   5592//                     warn_at("expected_a_b", line, column, "\\s", " ");
    +
     2801.   5592//                     char_after();
    +
     2802.   5592//                     break;
    +
     2803.   5592
    +
     2804.   5592                case "$":
    +
     2805.   5592                    if (line_source[0] !== "/") {
    +
     2806.   5592                        mode_regexp_multiline = true;
    +
     2807.   5592                    }
    +
     2808.   5592                    char_after();
    +
     2809.   5592                    break;
    +
     2810.   5592                case "(":
    +
     2811.   5592
    +
     2812.   5592// RegExp
    +
     2813.   5592// Match a group that starts with left paren.
    +
     2814.   5592
    +
     2815.   5592                    char_after("(");
    +
     2816.   5592                    switch (char) {
    +
     2817.   5592                    case ":":
    +
     2818.   5592
    +
     2819.   5592// test_cause:
    +
     2820.   5592// ["aa=/(:)/", "lex_regexp_group", "expected_a_before_b", ":", 6]
    +
     2821.   5592// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5]
    +
     2822.   5592
    +
     2823.   5592                        warn_at("expected_a_before_b", line, column, "?", ":");
    +
     2824.   5592                        break;
    +
     2825.   5592                    case "?":
    +
     2826.   5592                        char_after("?");
    +
     2827.   5592                        switch (char) {
    +
     2828.   5592                        case "!":
    +
     2829.   5592
    +
     2830.   5592// PR-437 - Add grammar for regexp-named-capture-group.
    +
     2831.   5592
    +
     2832.   5592                        case "<":
    +
     2833.   5592                        case "=":
    +
     2834.   5592                            char_after();
    +
     2835.   5592                            break;
    +
     2836.   5592                        default:
    +
     2837.   5592                            char_after(":");
    +
     2838.   5592                        }
    +
     2839.   5592                        break;
    +
     2840.   5592                    }
    +
     2841.   5592
    +
     2842.   5592// RegExp
    +
     2843.   5592// Recurse lex_regexp_group().
    +
     2844.   5592
    +
     2845.   5592                    lex_regexp_group();
    +
     2846.   5592                    char_after(")");
    +
     2847.   5592                    break;
    +
     2848.   5592                case "*":
    +
     2849.   5592                case "+":
    +
     2850.   5592                case "?":
    +
     2851.   5592                case "{":
    +
     2852.   5592                case "}":
    +
     2853.   5592
    +
     2854.   5592// test_cause:
    +
     2855.   5592// ["aa=/+/", "lex_regexp_group", "expected_a_before_b", "+", 5]
    +
     2856.   5592// ["aa=/.**/", "lex_regexp_group", "expected_a_before_b", "*", 7]
    +
     2857.   5592// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5]
    +
     2858.   5592// ["aa=/{/", "lex_regexp_group", "expected_a_before_b", "{", 5]
    +
     2859.   5592// ["aa=/}/", "lex_regexp_group", "expected_a_before_b", "}", 5]
    +
     2860.   5592
    +
     2861.   5592                    warn_at("expected_a_before_b", line, column, "\\", char);
    +
     2862.   5592                    char_after();
    +
     2863.   5592                    break;
    +
     2864.   5592                case "[":
    +
     2865.   5592                    lex_regexp_bracketed();
    +
     2866.   5592                    break;
    +
     2867.   5592                case "\\":
    +
     2868.   5592
    +
     2869.   5592// test_cause:
    +
     2870.   5592// ["aa=/\\/", "lex_regexp_group", "escape", "", 0]
    +
     2871.   5592
    +
     2872.   5592                    test_cause("escape");
    +
     2873.   5592
    +
     2874.   5592// PR-437 - Add grammar for regexp-named-backreference.
    +
     2875.   5592
    +
     2876.   5592                    char_after_escape("BbDdSsWw^${}[]():=!.|*+?k");
    +
     2877.   5592                    break;
    +
     2878.   5592                case "^":
    +
     2879.   5592                    if (snippet !== "^") {
    +
     2880.   5592                        mode_regexp_multiline = true;
    +
     2881.   5592                    }
    +
     2882.   5592                    char_after();
    +
     2883.   5592                    break;
    +
     2884.   5592                case "`":
    +
     2885.   5592                    if (mode_mega) {
    +
     2886.   5592
    +
     2887.   5592// test_cause:
    +
     2888.   5592// ["`${/`/}`", "lex_regexp_group", "unexpected_a", "`", 5]
    +
     2889.   5592
    +
     2890.   5592                        warn_at("unexpected_a", line, column, "`");
    +
     2891.   5592                    }
    +
     2892.   5592                    char_after();
    +
     2893.   5592                    break;
    +
     2894.   5592                default:
    +
     2895.   5592                    char_after();
    +
     2896.   5592                }
    +
     2897.   5592
    +
     2898.   5592// RegExp
    +
     2899.   5592// Match an optional quantifier.
    +
     2900.   5592
    +
     2901.   5592                switch (char) {
    +
     2902.   5592                case "*":
    +
     2903.   5592                case "+":
    +
     2904.   5592                    if (char_after(char) === "?") {
    +
     2905.   5592
    +
     2906.   5592// test_cause:
    +
     2907.   5592// ["aa=/.*?/", "lex_regexp_group", "?", "", 0]
    +
     2908.   5592// ["aa=/.+?/", "lex_regexp_group", "?", "", 0]
    +
     2909.   5592
    +
     2910.   5592                        test_cause("?");
    +
     2911.   5592                        char_after("?");
    +
     2912.   5592                    }
    +
     2913.   5592                    break;
    +
     2914.   5592                case "?":
    +
     2915.   5592                    if (char_after("?") === "?") {
    +
     2916.   5592
    +
     2917.   5592// test_cause:
    +
     2918.   5592// ["aa=/.??/", "lex_regexp_group", "unexpected_a", "?", 7]
    +
     2919.   5592
    +
     2920.   5592                        warn_at("unexpected_a", line, column, char);
    +
     2921.   5592                        char_after("?");
    +
     2922.   5592                    }
    +
     2923.   5592                    break;
    +
     2924.   5592                case "{":
    +
     2925.   5592                    if (read_digits("d", mode_digits_empty_string) === 0) {
    +
     2926.   5592
    +
     2927.   5592// test_cause:
    +
     2928.   5592// ["aa=/aa{/", "lex_regexp_group", "expected_a_before_b", ",", 8]
    +
     2929.   5592
    +
     2930.   5592                        warn_at("expected_a_before_b", line, column, "0", ",");
    +
     2931.   5592                    }
    +
     2932.   5592                    if (char === ",") {
    +
     2933.   5592
    +
     2934.   5592// test_cause:
    +
     2935.   5592// ["aa=/.{,/", "lex_regexp_group", "comma", "", 0]
    +
     2936.   5592
    +
     2937.   5592                        test_cause("comma");
    +
     2938.   5592                        read_digits("d", mode_digits_empty_string);
    +
     2939.   5592                    }
    +
     2940.   5592                    if (char_after("}") === "?") {
    +
     2941.   5592
    +
     2942.   5592// test_cause:
    +
     2943.   5592// ["aa=/.{0}?/", "lex_regexp_group", "unexpected_a", "?", 9]
    +
     2944.   5592
    +
     2945.   5592                        warn_at("unexpected_a", line, column, char);
    +
     2946.   5592                        char_after("?");
    +
     2947.   5592                    }
    +
     2948.   5592                    break;
    +
     2949.   5592                }
    +
     2950.   5592            }
    +
     2951.    794        }
    +
     2952.    583
    +
     2953.    583// RegExp
    +
     2954.    583// Scan the regexp literal. Give a warning if the first character is = because
    +
     2955.    583// /= looks like a division assignment operator.
    +
     2956.    583
    +
     2957.    583        snippet = "";
    +
     2958.    583        char_after();
    +
     2959.      1        if (char === "=") {
    +
     2960.      1
    +
     2961.      1// test_cause:
    +
     2962.      1// ["aa=/=/", "lex_regexp", "expected_a_before_b", "=", 5]
    +
     2963.      1
    +
     2964.      1            warn_at("expected_a_before_b", line, column, "\\", "=");
    +
     2965.      1        }
    +
     2966.    583        lex_regexp_group();
    +
     2967.    583
    +
     2968.    583// RegExp
    +
     2969.    583// Remove last character from snippet.
    +
     2970.    583
    +
     2971.    583        snippet = snippet.slice(0, -1);
    +
     2972.    583
    +
     2973.    583// RegExp
    +
     2974.    583// Make sure there is a closing slash.
    +
     2975.    583
    +
     2976.    583        value = snippet;
    +
     2977.    583        char_after("/");
    +
     2978.    583
    +
     2979.    583// RegExp
    +
     2980.    583// Create flag.
    +
     2981.    583
    +
     2982.    583        flag = empty();
    +
     2983.    583        while (
    +
     2984.    583
    +
     2985.    583// Regexp
    +
     2986.    583// char is a letter.
    +
     2987.    583
    +
     2988.    512            (char >= "a" && char <= "z\uffff")
    +
     2989.    573            || (char >= "A" && char <= "Z\uffff")
    +
     2990.    510        ) {
    +
     2991.    510
    +
     2992.    510// RegExp
    +
     2993.    510// Process dangling flag letters.
    +
     2994.    510
    +
     2995.    510            switch (!flag[char] && char) {
    +
     2996.    510            case "g":
    +
     2997.    510                break;
    +
     2998.    510            case "i":
    +
     2999.    510                break;
    +
     3000.    510            case "m":
    +
     3001.    510                break;
    +
     3002.    510            case "u":
    +
     3003.    510                break;
    +
     3004.    510            case "y":
    +
     3005.    510
    +
     3006.    510// test_cause:
    +
     3007.    510// ["aa=/./gimuy", "lex_regexp", "flag", "", 0]
    +
     3008.    510
    +
     3009.    510                test_cause("flag");
    +
     3010.    510                break;
    +
     3011.    510            default:
    +
     3012.    510
    +
     3013.    510// test_cause:
    +
     3014.    510// ["aa=/./gg", "lex_regexp", "unexpected_a", "g", 8]
    +
     3015.    510// ["aa=/./z", "lex_regexp", "unexpected_a", "z", 7]
    +
     3016.    510
    +
     3017.    510                warn_at("unexpected_a", line, column, char);
    +
     3018.    510            }
    +
     3019.    510            flag[char] = true;
    +
     3020.    510            char_after();
    +
     3021.    573        }
    +
     3022.    573        char_before();
    +
     3023.    573        if (char === "/" || char === "*") {
    +
     3024.      1
    +
     3025.      1// test_cause:
    +
     3026.      1// ["aa=/.//", "lex_regexp", "unexpected_a", "/", 3]
    +
     3027.      1
    +
     3028.      1            return stop_at("unexpected_a", line, from, char);
    +
     3029.    572        }
    +
     3030.    572        result = token_create("(regexp)", char);
    +
     3031.    572        result.flag = flag;
    +
     3032.    572        result.value = value;
    +
     3033.    572        if (mode_regexp_multiline && !flag.m) {
    +
     3034.      1
    +
     3035.      1// test_cause:
    +
     3036.      1// ["aa=/$^/", "lex_regexp", "missing_m", "", 7]
    +
     3037.      1
    +
     3038.      1            warn_at("missing_m", line, column);
    +
     3039.    572        }
    +
     3040.    572        return result;
    +
     3041.    572    }
    +
     3042.    668
    +
     3043.    598    function lex_slash_or_regexp() {
    +
     3044.    598
    +
     3045.    598// The / can be a division operator or the beginning of a regular expression
    +
     3046.    598// literal. It is not possible to know which without doing a complete parse.
    +
     3047.    598// We want to complete the tokenization before we begin to parse, so we will
    +
     3048.    598// estimate. This estimator can fail in some cases. For example, it cannot
    +
     3049.    598// know if "}" is ending a block or ending an object literal, so it can
    +
     3050.    598// behave incorrectly in that case; it is not meaningful to divide an
    +
     3051.    598// object, so it is likely that we can get away with it. We avoided the worst
    +
     3052.    598// cases by eliminating automatic semicolon insertion.
    +
     3053.    598
    +
     3054.    598        let the_token;
    +
     3055.    598        switch (
    +
     3056.    598            token_prv_expr.identifier
    +
     3057.     18            && !token_prv_expr.dot
    +
     3058.     15            && token_prv_expr.id
    +
     3059.    598        ) {
    +
     3060.      1        case "case":
    +
     3061.      2        case "delete":
    +
     3062.      3        case "in":
    +
     3063.      4        case "instanceof":
    +
     3064.      5        case "new":
    +
     3065.      6        case "typeof":
    +
     3066.      7        case "void":
    +
     3067.      8        case "yield":
    +
     3068.      8            the_token = lex_regexp();
    +
     3069.      8
    +
     3070.      8// test_cause:
    +
     3071.      8// ["case /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6]
    +
     3072.      8// ["delete /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8]
    +
     3073.      8// ["in /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 4]
    +
     3074.      8// ["instanceof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 12]
    +
     3075.      8// ["new /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 5]
    +
     3076.      8// ["typeof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8]
    +
     3077.      8// ["void /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6]
    +
     3078.      8// ["yield /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 7]
    +
     3079.      8
    +
     3080.      8            return stop("unexpected_a", the_token);
    +
     3081.      1        case "return":
    +
     3082.      1            return lex_regexp();
    +
     3083.    589        }
    +
     3084.    589        switch (!token_prv_expr.identifier && token_prv_expr.id.slice(-1)) {
    +
     3085.      1        case "!":
    +
     3086.      2        case "%":
    +
     3087.      4        case "&":
    +
     3088.      5        case "*":
    +
     3089.      6        case "+":
    +
     3090.      7        case "-":
    +
     3091.      9        case "/":
    +
     3092.     10        case ";":
    +
     3093.     11        case "<":
    +
     3094.     12        case ">":
    +
     3095.     13        case "^":
    +
     3096.     16        case "{":
    +
     3097.     17        case "|":
    +
     3098.     18        case "}":
    +
     3099.     19        case "~":
    +
     3100.     19            the_token = lex_regexp();
    +
     3101.     19
    +
     3102.     19// test_cause:
    +
     3103.     19// ["!/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3104.     19// ["%/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3105.     19// ["&/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3106.     19// ["+/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3107.     19// ["-/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3108.     19// ["0 * /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5]
    +
     3109.     19// ["0 / /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5]
    +
     3110.     19// [";/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3111.     19// ["</./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3112.     19// [">/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3113.     19// ["^/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3114.     19// ["{/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3115.     19// ["|/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3116.     19// ["}/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3117.     19// ["~/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3118.     19
    +
     3119.     19            warn("wrap_regexp", the_token);
    +
     3120.     19            return the_token;
    +
     3121.    514        case "(":
    +
     3122.    515        case ",":
    +
     3123.    516        case ":":
    +
     3124.    553        case "=":
    +
     3125.    554        case "?":
    +
     3126.    555        case "[":
    +
     3127.    555
    +
     3128.    555// test_cause:
    +
     3129.    555// ["(/./", "lex_slash_or_regexp", "recurse", "", 0]
    +
     3130.    555// [",/./", "lex_slash_or_regexp", "recurse", "", 0]
    +
     3131.    555// [":/./", "lex_slash_or_regexp", "recurse", "", 0]
    +
     3132.    555// ["=/./", "lex_slash_or_regexp", "recurse", "", 0]
    +
     3133.    555// ["?/./", "lex_slash_or_regexp", "recurse", "", 0]
    +
     3134.    555// ["aa[/./", "lex_slash_or_regexp", "recurse", "", 0]
    +
     3135.    555
    +
     3136.    555            test_cause("recurse");
    +
     3137.    555            return lex_regexp();
    +
     3138.     15        }
    +
     3139.     15        if (line_source[0] === "=") {
    +
     3140.      1            column += 1;
    +
     3141.      1            line_source = line_source.slice(1);
    +
     3142.      1            snippet = "/=";
    +
     3143.      1            warn_at("unexpected_a", line, column, "/=");
    +
     3144.     15        }
    +
     3145.     15        return token_create(snippet);
    +
     3146.     15    }
    +
     3147.    668
    +
     3148.  24335    function lex_string(quote) {
    +
     3149.  24335
    +
     3150.  24335// Create a string token.
    +
     3151.  24335
    +
     3152.  24335        let the_token;
    +
     3153.  24333        if (!option_dict.single && quote === "'") {
    +
     3154.      2
    +
     3155.      2// test_cause:
    +
     3156.      2// ["''", "lex_string", "use_double", "", 1]
    +
     3157.      2
    +
     3158.      2            warn_at("use_double", line, column);
    +
     3159.      2        }
    +
     3160.  24335        snippet = "";
    +
     3161.  24335        char_after();
    +
     3162.  24335
    +
     3163.  24335// Lex/loop through each character in "...".
    +
     3164.  24335
    +
     3165. 217055        while (true) {
    +
     3166. 217055            switch (char) {
    +
     3167. 217055            case "":
    +
     3168. 217055
    +
     3169. 217055// test_cause:
    +
     3170. 217055// ["\"", "lex_string", "unclosed_string", "", 1]
    +
     3171. 217055
    +
     3172. 217055                return stop_at("unclosed_string", line, column);
    +
     3173. 217055            case "\\":
    +
     3174. 217055                char_after_escape(quote);
    +
     3175. 217055                break;
    +
     3176. 217055            case "`":
    +
     3177. 217055                if (mode_mega) {
    +
     3178. 217055
    +
     3179. 217055// test_cause:
    +
     3180. 217055// ["`${\"`\"}`", "lex_string", "unexpected_a", "`", 5]
    +
     3181. 217055
    +
     3182. 217055                    warn_at("unexpected_a", line, column, "`");
    +
     3183. 217055                }
    +
     3184. 217055                char_after("`");
    +
     3185. 217055                break;
    +
     3186. 217055            case quote:
    +
     3187. 217055
    +
     3188. 217055// Remove last character from snippet.
    +
     3189. 217055
    +
     3190. 217055                snippet = snippet.slice(0, -1);
    +
     3191. 217055                the_token = token_create("(string)", snippet);
    +
     3192. 217055                the_token.quote = quote;
    +
     3193. 217055                return the_token;
    +
     3194. 217055            default:
    +
     3195. 217055                char_after();
    +
     3196. 217055            }
    +
     3197. 217055        }
    +
     3198.  24335    }
    +
     3199.    668
    +
     3200. 252255    function lex_token() {
    +
     3201. 252255        let match;
    +
     3202. 252255
    +
     3203. 252255// Lex/loop through each whitespace.
    +
     3204. 252255
    +
     3205. 376039        while (true) {
    +
     3206. 376039
    +
     3207. 376039// Lex/loop through each blank-line.
    +
     3208. 376039
    +
     3209. 376039            while (!line_source) {
    +
     3210. 376039                line_source = read_line();
    +
     3211. 376039                from = 0;
    +
     3212. 376039                if (line_source === undefined) {
    +
     3213. 376039                    return (
    +
     3214. 376039                        mode_mega
    +
     3215. 376039
    +
     3216. 376039// test_cause:
    +
     3217. 376039// ["`${//}`", "lex_token", "unclosed_mega", "", 1]
    +
     3218. 376039
    +
     3219. 376039                        ? stop_at("unclosed_mega", line_mega, from_mega)
    +
     3220. 376039                        : line_disable !== undefined
    +
     3221. 376039
    +
     3222. 376039// test_cause:
    +
     3223. 376039// ["/*jslint-disable*/", "lex_token", "unclosed_disable", "", 1]
    +
     3224. 376039
    +
     3225. 376039                        ? stop_at("unclosed_disable", line_disable)
    +
     3226. 376039                        : token_create("(end)")
    +
     3227. 376039                    );
    +
     3228. 376039                }
    +
     3229. 376039            }
    +
     3230. 376039            from = column;
    +
     3231. 376039            match = line_source.match(jslint_rgx_token);
    +
     3232. 376039
    +
     3233. 376039// match[1] token
    +
     3234. 376039// match[2] whitespace
    +
     3235. 376039// match[3] identifier
    +
     3236. 376039// match[4] number
    +
     3237. 376039// match[5] rest
    +
     3238. 376039
    +
     3239. 376039            if (!match) {
    +
     3240. 376039
    +
     3241. 376039// test_cause:
    +
     3242. 376039// ["#", "lex_token", "unexpected_char_a", "#", 1]
    +
     3243. 376039
    +
     3244. 376039                return stop_at(
    +
     3245. 376039                    "unexpected_char_a",
    +
     3246. 376039                    line,
    +
     3247. 376039                    column,
    +
     3248. 376039                    line_source[0]
    +
     3249. 376039                );
    +
     3250. 376039            }
    +
     3251. 376039            snippet = match[1];
    +
     3252. 376039            column += snippet.length;
    +
     3253. 376039            line_source = match[5];
    +
     3254. 376039            if (!match[2]) {
    +
     3255. 376039                break;
    +
     3256. 376039            }
    +
     3257. 376039        }
    +
     3258. 251612
    +
     3259. 251612// The token is an identifier.
    +
     3260. 251612
    +
     3261. 251612        if (match[3]) {
    +
     3262.  67699            return token_create(snippet, undefined, true);
    +
     3263. 183913        }
    +
     3264. 183913
    +
     3265. 183913// Create token from number.
    +
     3266. 183913
    +
     3267. 183913        if (match[4]) {
    +
     3268.   8851            return lex_number();
    +
     3269. 175062        }
    +
     3270. 175062
    +
     3271. 175062// Create token from string "..." or '...'.
    +
     3272. 175062
    +
     3273. 175062        if (snippet === "\"" || snippet === "'") {
    +
     3274.  24335            return lex_string(snippet);
    +
     3275. 150727        }
    +
     3276. 150727
    +
     3277. 150727// Create token from megastring `...`.
    +
     3278. 150727
    +
     3279. 150727        if (snippet === "`") {
    +
     3280.    789            return lex_megastring();
    +
     3281. 149938        }
    +
     3282. 149938
    +
     3283. 149938// Create token from comment /*...*/ or //....
    +
     3284. 149938
    +
     3285. 149938        if (snippet === "/*" || snippet === "//") {
    +
     3286.  11221            return lex_comment();
    +
     3287. 138717        }
    +
     3288. 138717
    +
     3289. 138717// Create token from slash /.
    +
     3290. 138717
    +
     3291. 138717        if (snippet === "/") {
    +
     3292.    598            return lex_slash_or_regexp();
    +
     3293. 138119        }
    +
     3294. 138119        return token_create(snippet);
    +
     3295. 138119    }
    +
     3296.    668
    +
     3297.   2667    function option_set_item(key, val) {
    +
     3298.   2667
    +
     3299.   2667// These are the options that are recognized in the option object or that may
    +
     3300.   2667// appear in a /*jslint*/ directive. Most options will have a boolean value,
    +
     3301.   2667// usually true. Some options will also predefine some number of global
    +
     3302.   2667// variables.
    +
     3303.   2667
    +
     3304.   2667        switch (key) {
    +
     3305.    659        case "beta":            // Enable experimental warnings.
    +
     3306.    661        case "bitwise":         // Allow bitwise operator.
    +
     3307.    668        case "browser":         // Assume browser environment.
    +
     3308.    670        case "convert":         // Allow conversion operator.
    +
     3309.    672        case "couch":           // Assume CouchDb environment.
    +
     3310.    678        case "devel":           // Allow console.log() and friends.
    +
     3311.   2014        case "ecma":            // Assume ECMAScript environment.
    +
     3312.   2020        case "eval":            // Allow eval().
    +
     3313.   2024        case "fart":            // Allow complex fat-arrow.
    +
     3314.   2029        case "for":             // Allow for-statement.
    +
     3315.   2035        case "getset":          // Allow get() and set().
    +
     3316.   2040        case "indent2":         // Use 2-space indent.
    +
     3317.   2042        case "long":            // Allow long lines.
    +
     3318.   2089        case "node":            // Assume Node.js environment.
    +
     3319.   2093        case "nomen":           // Allow weird property name.
    +
     3320.   2095        case "single":          // Allow single-quote strings.
    +
     3321.   2097        case "subscript":       // Allow identifier in subscript-notation.
    +
     3322.   2602        case "test_cause":      // Test jslint's causes.
    +
     3323.   2604        case "test_internal_error":     // Test jslint's internal-error
    +
     3324.   2667                                        // ... handling-ability.
    +
     3325.   2606        case "this":            // Allow 'this'.
    +
     3326.   2609        case "trace":           // Include jslint stack-trace in warnings.
    +
     3327.   2613        case "unordered":       // Allow unordered cases, params, properties,
    +
     3328.   2667                                // ... variables, and exports.
    +
     3329.   2617        case "variable":        // Allow unordered const and let declarations
    +
     3330.   2667                                // ... that are not at top of function-scope.
    +
     3331.   2619        case "white":           // Allow messy whitespace.
    +
     3332.   2619            option_dict[key] = val;
    +
     3333.   2619            break;
    +
     3334.   2667
    +
     3335.   2667// PR-404 - Alias "evil" to jslint-directive "eval" for backwards-compat.
    +
     3336.   2667
    +
     3337.      2        case "evil":
    +
     3338.      2            return option_set_item("eval", val);
    +
     3339.   2667
    +
     3340.   2667// PR-404 - Alias "nomen" to jslint-directive "name" for backwards-compat.
    +
     3341.   2667
    +
     3342.      2        case "name":
    +
     3343.      2            return option_set_item("nomen", val);
    +
     3344.     44        default:
    +
     3345.     44            return false;
    +
     3346.   2619        }
    +
     3347.   2619
    +
     3348.   2619// Initialize global-variables.
    +
     3349.   2619
    +
     3350.   2619        switch (val && key) {
    +
     3351.   2667
    +
     3352.   2667// Assign global browser variables to global_dict.
    +
     3353.   2667/*
    +
     3354.   2667// /\*jslint beta, browser, devel*\/
    +
     3355.   2667console.log(JSON.stringify(Object.keys(window).sort(), undefined, 4));
    +
     3356.   2667*/
    +
     3357.   2667
    +
     3358.      6        case "browser":
    +
     3359.      6            object_assign_from_list(global_dict, [
    +
     3360.      6
    +
     3361.      6// Shared with Node.js.
    +
     3362.      6
    +
     3363.      6                "AbortController",
    +
     3364.      6                // "Buffer",
    +
     3365.      6                // "Crypto",
    +
     3366.      6                // "CryptoKey",
    +
     3367.      6                "Event",
    +
     3368.      6                "EventTarget",
    +
     3369.      6                "MessageChannel",
    +
     3370.      6                "MessageEvent",
    +
     3371.      6                "MessagePort",
    +
     3372.      6                // "Request",
    +
     3373.      6                // "Response",
    +
     3374.      6                // "SubtleCrypto",
    +
     3375.      6                "TextDecoder",
    +
     3376.      6                "TextEncoder",
    +
     3377.      6                "URL",
    +
     3378.      6                "URLSearchParams",
    +
     3379.      6                "WebAssembly",
    +
     3380.      6                // "__dirname",
    +
     3381.      6                // "__filename",
    +
     3382.      6                // "atob",
    +
     3383.      6                // "btoa",
    +
     3384.      6                // "clearImmediate",
    +
     3385.      6                "clearInterval",
    +
     3386.      6                "clearTimeout",
    +
     3387.      6                // "console",
    +
     3388.      6                // "crypto",
    +
     3389.      6                // "exports",
    +
     3390.      6                // "fetch",
    +
     3391.      6                // "global",
    +
     3392.      6                // "module",
    +
     3393.      6                "performance",
    +
     3394.      6                // "process",
    +
     3395.      6                "queueMicrotask",
    +
     3396.      6                // "require",
    +
     3397.      6                // "setImmediate",
    +
     3398.      6                "setInterval",
    +
     3399.      6                "setTimeout",
    +
     3400.      6
    +
     3401.      6// Web worker only.
    +
     3402.      6// https://github.com/mdn/content/blob/main/files/en-us/web/api
    +
     3403.      6// /workerglobalscope/index.md
    +
     3404.      6
    +
     3405.      6                "importScripts",
    +
     3406.      6
    +
     3407.      6// Window.
    +
     3408.      6
    +
     3409.      6                "Blob",
    +
     3410.      6                // "CharacterData",
    +
     3411.      6                // "DocumentType",
    +
     3412.      6                // "Element",
    +
     3413.      6                // "Event",
    +
     3414.      6                "FileReader",
    +
     3415.      6                // "FontFace",
    +
     3416.      6                "FormData",
    +
     3417.      6                "IntersectionObserver",
    +
     3418.      6                "MutationObserver",
    +
     3419.      6                // "Storage",
    +
     3420.      6                // "TextDecoder",
    +
     3421.      6                // "TextEncoder",
    +
     3422.      6                // "URL",
    +
     3423.      6                "Worker",
    +
     3424.      6                "XMLHttpRequest",
    +
     3425.      6                // "caches",
    +
     3426.      6                // "clearInterval",
    +
     3427.      6                // "clearTimeout",
    +
     3428.      6                "document",
    +
     3429.      6                // "event",
    +
     3430.      6                "fetch",
    +
     3431.      6                // "history",
    +
     3432.      6                "indexedDb",
    +
     3433.      6                "localStorage",
    +
     3434.      6                "location",
    +
     3435.      6                // "name",
    +
     3436.      6                "navigator",
    +
     3437.      6                "postMessage",
    +
     3438.      6                // "screen",
    +
     3439.      6                "sessionStorage",
    +
     3440.      6                // "setInterval",
    +
     3441.      6                // "setTimeout",
    +
     3442.      6                "structuredClone",
    +
     3443.      6                "window"
    +
     3444.      6            ], "browser");
    +
     3445.      6            break;
    +
     3446.   2667
    +
     3447.   2667// https://docs.couchdb.org/en/stable/query-server/javascript.html#javascript
    +
     3448.   2667
    +
     3449.      2        case "couch":
    +
     3450.      2            object_assign_from_list(global_dict, [
    +
     3451.      2                "emit",
    +
     3452.      2                "getRow",
    +
     3453.      2                "isArray",
    +
     3454.      2                "log",
    +
     3455.      2                "provides",
    +
     3456.      2                "registerType",
    +
     3457.      2                "require",
    +
     3458.      2                "send",
    +
     3459.      2                "start",
    +
     3460.      2                "sum",
    +
     3461.      2                "toJSON"
    +
     3462.      2            ], "CouchDb");
    +
     3463.      2            break;
    +
     3464.      6        case "devel":
    +
     3465.      6            object_assign_from_list(global_dict, [
    +
     3466.      6                "alert", "confirm", "console", "prompt"
    +
     3467.      6            ], "development");
    +
     3468.      6            break;
    +
     3469.   2667
    +
     3470.   2667// These are the globals that are provided by the language standard.
    +
     3471.   2667// Assign global ECMAScript variables to global_dict.
    +
     3472.   2667/*
    +
     3473.   2667node --input-type=module --eval '
    +
     3474.   2667// /\*jslint beta, node*\/
    +
     3475.   2667import https from "https";
    +
     3476.   2667(async function () {
    +
     3477.   2667    let dict = {import: true};
    +
     3478.   2667    let result = "";
    +
     3479.   2667    await new Promise(function (resolve) {
    +
     3480.   2667        https.get((
    +
     3481.   2667            "https://raw.githubusercontent.com/mdn/content/main/files"
    +
     3482.   2667            + "/en-us/web/javascript/reference/global_objects/index.md"
    +
     3483.   2667        ), function (res) {
    +
     3484.   2667            res.on("data", function (chunk) {
    +
     3485.   2667                result += chunk;
    +
     3486.   2667            }).on("end", resolve).setEncoding("utf8");
    +
     3487.   2667        });
    +
     3488.   2667    });
    +
     3489.   2667    result.replace((
    +
     3490.   2667        /\n- \{\{JSxRef\("(?:Global_Objects\/)?([^"\/]+?)"/g
    +
     3491.   2667    ), function (ignore, key) {
    +
     3492.   2667        if (globalThis.hasOwnProperty(key)) {
    +
     3493.   2667            dict[key] = true;
    +
     3494.   2667        }
    +
     3495.   2667        return "";
    +
     3496.   2667    });
    +
     3497.   2667    console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4));
    +
     3498.   2667}());
    +
     3499.   2667'
    +
     3500.   2667*/
    +
     3501.   2667
    +
     3502.   1336        case "ecma":
    +
     3503.   1336            object_assign_from_list(global_dict, [
    +
     3504.   1336                "AggregateError",
    +
     3505.   1336                "Array",
    +
     3506.   1336                "ArrayBuffer",
    +
     3507.   1336                "Atomics",
    +
     3508.   1336                "BigInt",
    +
     3509.   1336                "BigInt64Array",
    +
     3510.   1336                "BigUint64Array",
    +
     3511.   1336                "Boolean",
    +
     3512.   1336                "DataView",
    +
     3513.   1336                "Date",
    +
     3514.   1336                "Error",
    +
     3515.   1336                "EvalError",
    +
     3516.   1336                "Float32Array",
    +
     3517.   1336                "Float64Array",
    +
     3518.   1336                "Function",
    +
     3519.   1336                "Infinity",
    +
     3520.   1336                "Int16Array",
    +
     3521.   1336                "Int32Array",
    +
     3522.   1336                "Int8Array",
    +
     3523.   1336                "Intl",
    +
     3524.   1336                "JSON",
    +
     3525.   1336                "Map",
    +
     3526.   1336                "Math",
    +
     3527.   1336                "NaN",
    +
     3528.   1336                "Number",
    +
     3529.   1336                "Object",
    +
     3530.   1336                "Promise",
    +
     3531.   1336                "Proxy",
    +
     3532.   1336                "RangeError",
    +
     3533.   1336                "ReferenceError",
    +
     3534.   1336                "Reflect",
    +
     3535.   1336                "RegExp",
    +
     3536.   1336                "Set",
    +
     3537.   1336                "SharedArrayBuffer",
    +
     3538.   1336                "String",
    +
     3539.   1336                "Symbol",
    +
     3540.   1336                "SyntaxError",
    +
     3541.   1336                "TypeError",
    +
     3542.   1336                "URIError",
    +
     3543.   1336                "Uint16Array",
    +
     3544.   1336                "Uint32Array",
    +
     3545.   1336                "Uint8Array",
    +
     3546.   1336                "Uint8ClampedArray",
    +
     3547.   1336                "WeakMap",
    +
     3548.   1336                "WeakSet",
    +
     3549.   1336                "WebAssembly",
    +
     3550.   1336                "decodeURI",
    +
     3551.   1336                "decodeURIComponent",
    +
     3552.   1336                "encodeURI",
    +
     3553.   1336                "encodeURIComponent",
    +
     3554.   1336                "eval",
    +
     3555.   1336                "globalThis",
    +
     3556.   1336                "import",
    +
     3557.   1336                "isFinite",
    +
     3558.   1336                "isNaN",
    +
     3559.   1336                "parseFloat",
    +
     3560.   1336                "parseInt",
    +
     3561.   1336                "undefined"
    +
     3562.   1336            ], "ECMAScript");
    +
     3563.   1336            break;
    +
     3564.   2667
    +
     3565.   2667// Assign global Node.js variables to global_dict.
    +
     3566.   2667/*
    +
     3567.   2667node --input-type=module --eval '
    +
     3568.   2667// /\*jslint beta, node*\/
    +
     3569.   2667import moduleHttps from "https";
    +
     3570.   2667(async function () {
    +
     3571.   2667    let dict = Object.create(null);
    +
     3572.   2667    let result = "";
    +
     3573.   2667    await new Promise(function (resolve) {
    +
     3574.   2667        moduleHttps.get((
    +
     3575.   2667            "https://raw.githubusercontent.com/nodejs/node/v16.x/doc/api"
    +
     3576.   2667            + "/globals.md"
    +
     3577.   2667        ), function (res) {
    +
     3578.   2667            res.on("data", function (chunk) {
    +
     3579.   2667                result += chunk;
    +
     3580.   2667            }).on("end", resolve).setEncoding("utf8");
    +
     3581.   2667        });
    +
     3582.   2667    });
    +
     3583.   2667    result.replace((
    +
     3584.   2667        /\n(?:\* \[`|## |## Class: )`\w+/g
    +
     3585.   2667    ), function (match0) {
    +
     3586.   2667        dict[match0.split("`")[1]] = true;
    +
     3587.   2667        return "";
    +
     3588.   2667    });
    +
     3589.   2667    console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4));
    +
     3590.   2667}());
    +
     3591.   2667'
    +
     3592.   2667*/
    +
     3593.   2667
    +
     3594.     47        case "node":
    +
     3595.     47            object_assign_from_list(global_dict, [
    +
     3596.     47                "AbortController",
    +
     3597.     47                "Buffer",
    +
     3598.     47                // "Crypto",
    +
     3599.     47                // "CryptoKey",
    +
     3600.     47                "Event",
    +
     3601.     47                "EventTarget",
    +
     3602.     47                "MessageChannel",
    +
     3603.     47                "MessageEvent",
    +
     3604.     47                "MessagePort",
    +
     3605.     47                // "Request",
    +
     3606.     47                // "Response",
    +
     3607.     47                // "SubtleCrypto",
    +
     3608.     47                "TextDecoder",
    +
     3609.     47                "TextEncoder",
    +
     3610.     47                "URL",
    +
     3611.     47                "URLSearchParams",
    +
     3612.     47                "WebAssembly",
    +
     3613.     47                "__dirname",
    +
     3614.     47                "__filename",
    +
     3615.     47                // "atob",
    +
     3616.     47                // "btoa",
    +
     3617.     47                "clearImmediate",
    +
     3618.     47                "clearInterval",
    +
     3619.     47                "clearTimeout",
    +
     3620.     47                "console",
    +
     3621.     47                // "crypto",
    +
     3622.     47                "exports",
    +
     3623.     47                // "fetch",
    +
     3624.     47                "global",
    +
     3625.     47                "module",
    +
     3626.     47                "performance",
    +
     3627.     47                "process",
    +
     3628.     47                "queueMicrotask",
    +
     3629.     47                "require",
    +
     3630.     47                "setImmediate",
    +
     3631.     47                "setInterval",
    +
     3632.     47                "setTimeout"
    +
     3633.     47            ], "Node.js");
    +
     3634.     47            break;
    +
     3635.   2619        }
    +
     3636.   2619        return true;
    +
     3637.   2619    }
    +
     3638.    668
    +
     3639.    469    function read_digits(base, mode) {
    +
     3640.    469        let digits = line_source.match(
    +
     3641.    469            base === "b"
    +
     3642.      3            ? jslint_rgx_digits_bits
    +
     3643.    466            : base === "o"
    +
     3644.    466            ? jslint_rgx_digits_octals
    +
     3645.    466            : base === "x"
    +
     3646.    466            ? jslint_rgx_digits_hexs
    +
     3647.    466            : jslint_rgx_digits_decimals
    +
     3648.    469        )[0];
    +
     3649.    469        if (
    +
     3650.     77            (mode !== mode_digits_empty_string && digits.length === 0)
    +
     3651.    468            || digits[0] === "_"
    +
     3652.      2        ) {
    +
     3653.      2
    +
     3654.      2// test_cause:
    +
     3655.      2// ["0x", "read_digits", "expected_digits_after_a", "0x", 2]
    +
     3656.      2// ["0x_", "read_digits", "expected_digits_after_a", "0x", 2]
    +
     3657.      2
    +
     3658.      2            warn_at("expected_digits_after_a", line, column, snippet);
    +
     3659.      2        }
    +
     3660.    469
    +
     3661.    469// PR-390 - Add numeric-separator check.
    +
     3662.    469
    +
     3663.     73        if (mode === mode_digits_numeric_separator) {
    +
     3664.     73            check_numeric_separator(digits, column);
    +
     3665.    396        } else if (digits.indexOf("_") >= 0) {
    +
     3666.    396
    +
     3667.    396// test_cause:
    +
     3668.    396// ["\"\\u{1_2}\"", "read_digits", "illegal_num_separator", "", 6]
    +
     3669.    396
    +
     3670.    396            warn_at(
    +
     3671.    396                "illegal_num_separator",
    +
     3672.    396                line,
    +
     3673.    396                column + digits.indexOf("_") + 1
    +
     3674.    396            );
    +
     3675.    396        }
    +
     3676.    469        column += digits.length;
    +
     3677.    469        line_source = line_source.slice(digits.length);
    +
     3678.    469        snippet += digits;
    +
     3679.    469        char_after();
    +
     3680.    469        return digits.length;
    +
     3681.    469    }
    +
     3682.    668
    +
     3683. 105193    function read_line() {
    +
     3684. 105193
    +
     3685. 105193// Put the next line of source in line_source. If the line contains tabs,
    +
     3686. 105193// replace them with spaces and give a warning. Also warn if the line contains
    +
     3687. 105193// unsafe characters or is too damn long.
    +
     3688. 105193
    +
     3689. 105193        if (
    +
     3690. 105193            !option_dict.long
    +
     3691. 105189            && line_whole.length > 80
    +
     3692.     56            && line_disable === undefined
    +
     3693.     56            && !state.mode_json
    +
     3694.     23            && token_1
    +
     3695.     23            && !mode_regexp
    +
     3696.     13        ) {
    +
     3697.     13
    +
     3698.     13// test_cause:
    +
     3699.     13// ["/////////////////////////////////////////////////////////////////////////////////", "read_line", "too_long", "", 1] //jslint-ignore-line
    +
     3700.     13
    +
     3701.     13            warn_at("too_long", line);
    +
     3702.     13        }
    +
     3703. 105193        column = 0;
    +
     3704. 105193        line += 1;
    +
     3705. 105193        mode_regexp = false;
    +
     3706. 105193        line_source = undefined;
    +
     3707. 105193        line_whole = "";
    +
     3708.    645        if (line_list[line] === undefined) {
    +
     3709.    645            return line_source;
    +
     3710. 104548        }
    +
     3711. 104548        line_source = line_list[line].line_source;
    +
     3712. 104548        line_whole = line_source;
    +
     3713. 104548
    +
     3714. 104548// Scan each line for following ignore-directives:
    +
     3715. 104548// "/*jslint-disable*/"
    +
     3716. 104548// "/*jslint-enable*/"
    +
     3717. 104548// "//jslint-ignore-line"
    +
     3718. 104548
    +
     3719. 104548        if (line_source === "/*jslint-disable*/") {
    +
     3720.      5
    +
     3721.      5// test_cause:
    +
     3722.      5// ["/*jslint-disable*/", "read_line", "jslint_disable", "", 0]
    +
     3723.      5
    +
     3724.      5            test_cause("jslint_disable");
    +
     3725.      5            line_disable = line;
    +
     3726. 104543        } else if (line_source === "/*jslint-enable*/") {
    +
     3727. 104543            if (line_disable === undefined) {
    +
     3728. 104543
    +
     3729. 104543// test_cause:
    +
     3730. 104543// ["/*jslint-enable*/", "read_line", "unopened_enable", "", 1]
    +
     3731. 104543
    +
     3732. 104543                stop_at("unopened_enable", line);
    +
     3733. 104543            }
    +
     3734. 104543            line_disable = undefined;
    +
     3735. 104543        } else if (
    +
     3736. 104543            line_source.endsWith(" //jslint-ignore-line")
    +
     3737. 104543            || line_source.endsWith(" //jslint-quiet")
    +
     3738. 104543        ) {
    +
     3739. 104543
    +
     3740. 104543// test_cause:
    +
     3741. 104543// ["0 //jslint-ignore-line", "read_line", "jslint_ignore_line", "", 0]
    +
     3742. 104543
    +
     3743. 104543            test_cause("jslint_ignore_line");
    +
     3744. 104543            line_list[line].directive_ignore_line = true;
    +
     3745. 104547        }
    +
     3746. 104547        if (line_disable !== undefined) {
    +
     3747.      9
    +
     3748.      9// test_cause:
    +
     3749.      9// ["/*jslint-disable*/\n0", "read_line", "line_disable", "", 0]
    +
     3750.      9
    +
     3751.      9            test_cause("line_disable");
    +
     3752.      9            line_source = "";
    +
     3753. 104547        }
    +
     3754. 104547        // jslint_rgx_tab
    +
     3755. 104547        if (line_source.indexOf("\t") >= 0) {
    +
     3756.      3            if (!option_dict.white) {
    +
     3757.      3
    +
     3758.      3// test_cause:
    +
     3759.      3// ["\t", "read_line", "use_spaces", "", 1]
    +
     3760.      3
    +
     3761.      3                warn_at("use_spaces", line, line_source.indexOf("\t") + 1);
    +
     3762.      3            }
    +
     3763.      3            line_source = line_source.replace(jslint_rgx_tab, " ");
    +
     3764. 104547        }
    +
     3765. 104547        if (!option_dict.white && line_source.endsWith(" ")) {
    +
     3766.      2
    +
     3767.      2// test_cause:
    +
     3768.      2// [" ", "read_line", "unexpected_trailing_space", "", 1]
    +
     3769.      2
    +
     3770.      2            warn_at("unexpected_trailing_space", line, line_source.length - 1);
    +
     3771. 104547        }
    +
     3772. 104547        return line_source;
    +
     3773. 104547    }
    +
     3774.    668
    +
     3775. 255241    function token_create(id, value, identifier) {
    +
     3776. 255241
    +
     3777. 255241// Create the token object and append it to token_list.
    +
     3778. 255241
    +
     3779. 255241        let the_token = {
    +
     3780. 255241            from,
    +
     3781. 255241            id,
    +
     3782. 255241            identifier: Boolean(identifier),
    +
     3783. 255241            line,
    +
     3784. 255241            nr: token_list.length,
    +
     3785. 255241            thru: column,
    +
     3786. 255241            value
    +
     3787. 255241        };
    +
     3788. 255241        token_list.push(the_token);
    +
     3789. 255241
    +
     3790. 255241// Directives must appear before the first statement.
    +
     3791. 255241
    +
     3792. 244023        if (id !== "(comment)" && id !== ";") {
    +
     3793. 228006            mode_directive = false;
    +
     3794. 228006        }
    +
     3795. 255241
    +
     3796. 255241// If this token is an identifier that touches a preceding number, or
    +
     3797. 255241// a "/", comment, or regular expression literal that touches a preceding
    +
     3798. 255241// comment or regular expression literal, then give a missing space warning.
    +
     3799. 255241// This warning is not suppressed by option_dict.white.
    +
     3800. 255241
    +
     3801. 255241        if (
    +
     3802. 255241            token_prv.line === line
    +
     3803. 185838            && token_prv.thru === from
    +
     3804. 119910            && (id === "(comment)" || id === "(regexp)" || id === "/")
    +
     3805.    125            && (token_prv.id === "(comment)" || token_prv.id === "(regexp)")
    +
     3806.      1        ) {
    +
     3807.      1
    +
     3808.      1// test_cause:
    +
     3809.      1// ["/**//**/", "token_create", "expected_space_a_b", "(comment)", 5]
    +
     3810.      1
    +
     3811.      1            warn(
    +
     3812.      1                "expected_space_a_b",
    +
     3813.      1                the_token,
    +
     3814.      1                artifact(token_prv),
    +
     3815.      1                artifact(the_token)
    +
     3816.      1            );
    +
     3817.      1        }
    +
     3818.  11603        if (token_prv.id === "." && id === "(number)") {
    +
     3819.      4
    +
     3820.      4// test_cause:
    +
     3821.      4// [".0", "token_create", "expected_a_before_b", ".", 1]
    +
     3822.      4
    +
     3823.      4            warn("expected_a_before_b", token_prv, "0", ".");
    +
     3824.      4        }
    +
     3825.  11603        if (token_prv_expr.id === "." && the_token.identifier) {
    +
     3826.  11598            the_token.dot = true;
    +
     3827.  11598        }
    +
     3828. 255241
    +
     3829. 255241// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart.
    +
     3830. 255241// Farts are now detected by keeping a list of most recent "(" tokens at any
    +
     3831. 255241// given depth. When a "=>" token is encountered, the most recent "(" token at
    +
     3832. 255241// current depth is marked as a fart.
    +
     3833. 255241
    +
     3834. 255241        switch (id) {
    +
     3835.  17591        case "(":
    +
     3836.  17591            paren_backtrack_list[paren_depth] = the_token;
    +
     3837.  17591            paren_depth += 1;
    +
     3838.  17591            break;
    +
     3839.  17590        case ")":
    +
     3840.  17590            paren_depth -= 1;
    +
     3841.  17590            break;
    +
     3842.     16        case "=>":
    +
     3843.     16            if (
    +
     3844.     16                token_prv_expr.id === ")"
    +
     3845.     16                && paren_backtrack_list[paren_depth]
    +
     3846.     16            ) {
    +
     3847.     16                paren_backtrack_list[paren_depth].fart = the_token;
    +
     3848.     16            }
    +
     3849.     16            break;
    +
     3850. 255241        }
    +
     3851. 255241
    +
     3852. 255241// The previous token is used to detect adjacency problems.
    +
     3853. 255241
    +
     3854. 255241        token_prv = the_token;
    +
     3855. 255241
    +
     3856. 255241// The token_prv_expr token is a previous token that was not a comment.
    +
     3857. 255241// The token_prv_expr token
    +
     3858. 255241// is used to disambiguate "/", which can mean division or regular expression
    +
     3859. 255241// literal.
    +
     3860. 255241
    +
     3861. 244023        if (token_prv.id !== "(comment)") {
    +
     3862. 244023            token_prv_expr = token_prv;
    +
     3863. 244023        }
    +
     3864. 255241        return the_token;
    +
     3865. 255241    }
    +
     3866.    668
    +
     3867.    668// Init global_dict and option_dict.
    +
     3868.    668
    +
     3869.    668    option_set_item("ecma", true);
    +
     3870.   1896    Object.keys(option_dict).sort().forEach(function (key) {
    +
     3871.   1896        option_set_item(key, option_dict[key] === true);
    +
     3872.   1896    });
    +
     3873.    668    object_assign_from_list(global_dict, global_list, "user-defined");
    +
     3874.    668
    +
     3875.    668// Scan first line for "#!" and ignore it.
    +
     3876.    668
    +
     3877.      1    if (line_list[jslint_fudge].line_source.startsWith("#!")) {
    +
     3878.      1        line += 1;
    +
     3879.      1        state.mode_shebang = true;
    +
     3880.      1    }
    +
     3881.    668    token_1 = lex_token();
    +
     3882.    640    state.mode_json = token_1.id === "{" || token_1.id === "[";
    +
     3883.    668
    +
     3884.    668// Lex/loop through each token until (end).
    +
     3885.    668
    +
     3886. 249474    while (true) {
    +
     3887. 249474        if (lex_token().id === "(end)") {
    +
     3888. 249474            break;
    +
     3889. 249474        }
    +
     3890. 249474    }
    +
     3891.    668}
    +
     3892.      1
    +
     3893.    631function jslint_phase3_parse(state) {
    +
     3894.    631
    +
     3895.    631// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
    +
     3896.    631
    +
     3897.    631// Parsing:
    +
     3898.    631
    +
     3899.    631// Parsing weaves the tokens into an abstract syntax tree. During that process,
    +
     3900.    631// a token may be given any of these properties:
    +
     3901.    631
    +
     3902.    631//      arity       string
    +
     3903.    631//      label       identifier
    +
     3904.    631//      name        identifier
    +
     3905.    631//      expression  expressions
    +
     3906.    631//      block       statements
    +
     3907.    631//      else        statements (else, default, catch)
    +
     3908.    631
    +
     3909.    631// Specialized tokens may have additional properties.
    +
     3910.    631
    +
     3911.    631    let anon = "anonymous";     // The guessed name for anonymous functions.
    +
     3912.    631    let {
    +
     3913.    631        artifact,
    +
     3914.    631        catch_list,
    +
     3915.    631        catch_stack,
    +
     3916.    631        export_dict,
    +
     3917.    631        function_list,
    +
     3918.    631        function_stack,
    +
     3919.    631        global_dict,
    +
     3920.    631        import_list,
    +
     3921.    631        is_equal,
    +
     3922.    631        option_dict,
    +
     3923.    631        property_dict,
    +
     3924.    631        stop,
    +
     3925.    631        syntax_dict,
    +
     3926.    631        tenure,
    +
     3927.    631        test_cause,
    +
     3928.    631        token_global,
    +
     3929.    631        token_list,
    +
     3930.    631        warn,
    +
     3931.    631        warn_at
    +
     3932.    631    } = state;
    +
     3933.    631    let catchage = catch_stack[0];      // The current catch-block.
    +
     3934.    631    let functionage = token_global;     // The current function.
    +
     3935.    631    let mode_var;               // "var" if using var; "let" if using let.
    +
     3936.    631    let token_ii = 0;           // The number of the next token.
    +
     3937.    631    let token_now = token_global;       // The current token being examined in
    +
     3938.    631                                        // ... the parse.
    +
     3939.    631    let token_nxt = token_global;       // The next token to be examined in
    +
     3940.    631                                        // ... <token_list>.
    +
     3941.    631
    +
     3942. 244361    function advance(id, match) {
    +
     3943. 244361
    +
     3944. 244361// Produce the next token.
    +
     3945. 244361
    +
     3946. 244361// Attempt to give helpful names to anonymous functions.
    +
     3947. 244361
    +
     3948. 244361        if (
    +
     3949. 244361            token_now.identifier
    +
     3950.  67656            && token_now.id !== "function"
    +
     3951.  65664            && token_now.id !== "async"
    +
     3952.  65483        ) {
    +
     3953.  65483            anon = token_now.id;
    +
     3954. 178878        } else if (
    +
     3955. 178878            token_now.id === "(string)"
    +
     3956. 178878            && jslint_rgx_identifier.test(token_now.value)
    +
     3957. 178878        ) {
    +
     3958. 178878            anon = token_now.value;
    +
     3959. 178878        }
    +
     3960. 244361
    +
     3961. 244361// Attempt to match token_nxt with an expected id.
    +
     3962. 244361
    +
     3963. 120065        if (id !== undefined && token_nxt.id !== id) {
    +
     3964.     26            return (
    +
     3965.     26                match === undefined
    +
     3966.     26
    +
     3967.     26// test_cause:
    +
     3968.     26// ["{0:0}", "advance", "expected_a_b", "0", 2]
    +
     3969.     26
    +
     3970.     26                ? stop("expected_a_b", token_nxt, id, artifact())
    +
     3971.     26
    +
     3972.     26// test_cause:
    +
     3973.     26// ["{\"aa\":0", "advance", "expected_a_b_from_c_d", "{", 1]
    +
     3974.     26
    +
     3975.     26                : stop(
    +
     3976.     26                    "expected_a_b_from_c_d",
    +
     3977.     26                    token_nxt,
    +
     3978.     26                    id,
    +
     3979.     26                    artifact(match),
    +
     3980.     26                    match.line,
    +
     3981.     26                    artifact()
    +
     3982.     26                )
    +
     3983.     26            );
    +
     3984. 244335        }
    +
     3985. 244335
    +
     3986. 244335// Promote the tokens, skipping comments.
    +
     3987. 244335
    +
     3988. 244335        token_now = token_nxt;
    +
     3989. 255550        while (true) {
    +
     3990. 255550            token_nxt = token_list[token_ii];
    +
     3991. 255550            state.token_nxt = token_nxt;
    +
     3992. 255550            token_ii += 1;
    +
     3993. 255550            if (token_nxt.id !== "(comment)") {
    +
     3994. 255550                if (token_nxt.id === "(end)") {
    +
     3995. 255550                    token_ii -= 1;
    +
     3996. 255550                }
    +
     3997. 255550                break;
    +
     3998. 255550            }
    +
     3999. 255550            if (state.mode_json) {
    +
     4000. 255550
    +
     4001. 255550// test_cause:
    +
     4002. 255550// ["[//]", "advance", "unexpected_a", "(comment)", 2]
    +
     4003. 255550
    +
     4004. 255550                warn("unexpected_a");
    +
     4005. 255550            }
    +
     4006. 255550        }
    +
     4007. 244361    }
    +
     4008.    631
    +
     4009.   7572    function assignment(id) {
    +
     4010.   7572
    +
     4011.   7572// Create an assignment operator. The one true assignment is different because
    +
     4012.   7572// its left side, when it is a variable, is not treated as an expression.
    +
     4013.   7572// That case is special because that is when a variable gets initialized. The
    +
     4014.   7572// other assignment operators can modify, but they cannot initialize.
    +
     4015.   7572
    +
     4016.   7572        const the_symbol = symbol(id, 20);
    +
     4017.   5013        the_symbol.led_infix = function (left) {
    +
     4018.   5013            const the_token = token_now;
    +
     4019.   5013            let right;
    +
     4020.   5013            the_token.arity = "assignment";
    +
     4021.   5013            right = parse_expression(20 - 1);
    +
     4022.   4234            if (id === "=" && left.arity === "variable") {
    +
     4023.   2818                the_token.names = left;
    +
     4024.   2818                the_token.expression = right;
    +
     4025.   2818            } else {
    +
     4026.   2191                the_token.expression = [left, right];
    +
     4027.   5009            }
    +
     4028.   5009            if (
    +
     4029.   5009                right.arity === "assignment"
    +
     4030.   5009                || right.arity === "preassign"
    +
     4031.   5007                || right.arity === "postassign"
    +
     4032.      2            ) {
    +
     4033.      2                warn("unexpected_a", right);
    +
     4034.   5009            }
    +
     4035.   5009            check_mutation(left);
    +
     4036.   5009            return the_token;
    +
     4037.   5009        };
    +
     4038.   7572        return the_symbol;
    +
     4039.   7572    }
    +
     4040.    631
    +
     4041.   5713    function block(special) {
    +
     4042.   5713
    +
     4043.   5713// Parse a block, a sequence of statements wrapped in braces.
    +
     4044.   5713//  special "body"      The block is a function body.
    +
     4045.   5713//          "ignore"    No warning on an empty block.
    +
     4046.   5713//          "naked"     No advance.
    +
     4047.   5713//          undefined   An ordinary block.
    +
     4048.   5713
    +
     4049.   5713        let stmts;
    +
     4050.   5713        let the_block;
    +
     4051.   5708        if (special !== "naked") {
    +
     4052.   5708            advance("{");
    +
     4053.   5712        }
    +
     4054.   5712        the_block = token_now;
    +
     4055.   5712        if (special !== "body") {
    +
     4056.   3715            functionage.statement_prv = the_block;
    +
     4057.   5712        }
    +
     4058.   5712        the_block.arity = "statement";
    +
     4059.   5712        the_block.body = special === "body";
    +
     4060.   5712
    +
     4061.   5712// Top level function bodies may include the "use strict" pragma.
    +
     4062.   5712
    +
     4063.   5712        if (
    +
     4064.   5712            special === "body"
    +
     4065.   5712            && function_stack.length === 1
    +
     4066.    281            && token_nxt.value === "use strict"
    +
     4067.      4        ) {
    +
     4068.      4            token_nxt.statement = true;
    +
     4069.      4            advance("(string)");
    +
     4070.      4            advance(";");
    +
     4071.   5712        }
    +
     4072.   5712        stmts = parse_statements();
    +
     4073.   5712        the_block.block = stmts;
    +
     4074.   5712        if (stmts.length === 0) {
    +
     4075.     72            if (!option_dict.devel && special !== "ignore") {
    +
     4076.     72
    +
     4077.     72// test_cause:
    +
     4078.     72// ["function aa(){}", "block", "empty_block", "{", 14]
    +
     4079.     72
    +
     4080.     72                warn("empty_block", the_block);
    +
     4081.     72            }
    +
     4082.     72            the_block.disrupt = false;
    +
     4083.   5633        } else {
    +
     4084.   5633            the_block.disrupt = stmts[stmts.length - 1].disrupt;
    +
     4085.   5705        }
    +
     4086.   5705        advance("}");
    +
     4087.   5705        return the_block;
    +
     4088.   5705    }
    +
     4089.    631
    +
     4090.  23297    function check_left(left, right) {
    +
     4091.  23297
    +
     4092.  23297// Warn if the left is not one of these:
    +
     4093.  23297//      ?.
    +
     4094.  23297//      ?:
    +
     4095.  23297//      e()
    +
     4096.  23297//      e.b
    +
     4097.  23297//      e[b]
    +
     4098.  23297//      identifier
    +
     4099.  23297
    +
     4100.  23297        const id = left.id;
    +
     4101.  23297        if (
    +
     4102.  23297            !left.identifier
    +
     4103.   6298            && (
    +
     4104.   6298                left.arity !== "ternary"
    +
     4105.   6298                || (
    +
     4106.   6298                    !check_left(left.expression[1])
    +
     4107.   6298                    && !check_left(left.expression[2])
    +
     4108.   6298                )
    +
     4109.   6298            )
    +
     4110.   6298            && (
    +
     4111.   6298                left.arity !== "binary"
    +
     4112.   6298                || (id !== "." && id !== "?." && id !== "(" && id !== "[")
    +
     4113.   6298            )
    +
     4114.     11        ) {
    +
     4115.     11            warn("unexpected_a", right || token_nxt);
    +
     4116.     11            return false;
    +
     4117.  23286        }
    +
     4118.  23286        return true;
    +
     4119.  23286    }
    +
     4120.    631
    +
     4121.   5012    function check_mutation(the_thing) {
    +
     4122.   5012
    +
     4123.   5012// The only expressions that may be assigned to are
    +
     4124.   5012//      e.b
    +
     4125.   5012//      e[b]
    +
     4126.   5012//      v
    +
     4127.   5012//      [destructure]
    +
     4128.   5012//      {destructure}
    +
     4129.   5012
    +
     4130.   5012        if (
    +
     4131.   5012            the_thing.arity !== "variable"
    +
     4132.   1543            && the_thing.id !== "."
    +
     4133.    192            && the_thing.id !== "["
    +
     4134.      7            && the_thing.id !== "{"
    +
     4135.      7        ) {
    +
     4136.      7
    +
     4137.      7// test_cause:
    +
     4138.      7// ["0=0", "check_mutation", "bad_assignment_a", "0", 1]
    +
     4139.      7
    +
     4140.      7            warn("bad_assignment_a", the_thing);
    +
     4141.      7            return false;
    +
     4142.   5005        }
    +
     4143.   5005        return true;
    +
     4144.   5005    }
    +
     4145.    631
    +
     4146.   2208    function check_not_top_level(thing) {
    +
     4147.   2208
    +
     4148.   2208// Some features should not be at the outermost level.
    +
     4149.   2208
    +
     4150.     34        if (functionage === token_global) {
    +
     4151.     34
    +
     4152.     34// test_cause:
    +
     4153.     34// ["
    +
     4154.     34// while(0){}
    +
     4155.     34// ", "check_not_top_level", "unexpected_at_top_level_a", "while", 1]
    +
     4156.     34
    +
     4157.     34            warn("unexpected_at_top_level_a", thing);
    +
     4158.     34        }
    +
     4159.   2208    }
    +
     4160.    631
    +
     4161.   3360    function check_ordered(type, token_list) {
    +
     4162.   3360
    +
     4163.   3360// This function will warn if <token_list> is unordered.
    +
     4164.   3360
    +
     4165.   4062        token_list.reduce(function (aa, token) {
    +
     4166.   4062            const bb = artifact(token);
    +
     4167.   4048            if (!option_dict.unordered && aa > bb) {
    +
     4168.      4                warn("expected_a_b_before_c_d", token, type, bb, type, aa);
    +
     4169.      4            }
    +
     4170.   4062            return bb;
    +
     4171.   4062        }, "");
    +
     4172.   3360    }
    +
     4173.    631
    +
     4174.   1301    function check_ordered_case(case_list) {
    +
     4175.   1301
    +
     4176.   1301// This function will warn if <case_list> is unordered.
    +
     4177.   1301
    +
     4178.   2691        case_list.filter(noop).map(function (token) {
    +
     4179.   2650            switch (token.identifier || token.id) {
    +
     4180.     34            case "(number)":
    +
     4181.     34                return {
    +
     4182.     34                    order: 1,
    +
     4183.     34                    token,
    +
     4184.     34                    type: "number",
    +
     4185.     34                    value: Number(artifact(token))
    +
     4186.     34                };
    +
     4187.   2604            case "(string)":
    +
     4188.   2604                return {
    +
     4189.   2604                    order: 2,
    +
     4190.   2604                    token,
    +
     4191.   2604                    type: "string",
    +
     4192.   2604                    value: artifact(token)
    +
     4193.   2604                };
    +
     4194.     41            case true:
    +
     4195.     41                return {
    +
     4196.     41                    order: 3,
    +
     4197.     41                    token,
    +
     4198.     41                    type: "identifier",
    +
     4199.     41                    value: artifact(token)
    +
     4200.     41                };
    +
     4201.   2691            }
    +
     4202.   2691        }).reduce(function (aa, bb) {
    +
     4203.   2691            if (
    +
     4204.   2691
    +
     4205.   2691// PR-419 - Hide warning about unordered case-statements behind beta-flag.
    +
     4206.   2691
    +
     4207.   2691                option_dict.beta
    +
     4208.   2691                && !option_dict.unordered
    +
     4209.   2683                && aa && bb
    +
     4210.   1384                && (
    +
     4211.   1384                    aa.order > bb.order
    +
     4212.   1384                    || (aa.order === bb.order && aa.value > bb.value)
    +
     4213.   1384                )
    +
     4214.     10            ) {
    +
     4215.     10                warn(
    +
     4216.     10                    "expected_a_b_before_c_d",
    +
     4217.     10                    bb.token,
    +
     4218.     10                    `case-${bb.type}`,
    +
     4219.     10                    bb.value,
    +
     4220.     10                    `case-${aa.type}`,
    +
     4221.     10                    aa.value
    +
     4222.     10                );
    +
     4223.     10            }
    +
     4224.   2691            return bb;
    +
     4225.   2691        }, undefined);
    +
     4226.   1301    }
    +
     4227.    631
    +
     4228.   3263    function condition() {
    +
     4229.   3263
    +
     4230.   3263// Parse the condition part of a do, if, while.
    +
     4231.   3263
    +
     4232.   3263        const the_paren = token_nxt;
    +
     4233.   3263        let the_value;
    +
     4234.   3263
    +
     4235.   3263// test_cause:
    +
     4236.   3263// ["do{}while()", "condition", "", "", 0]
    +
     4237.   3263// ["if(){}", "condition", "", "", 0]
    +
     4238.   3263// ["while(){}", "condition", "", "", 0]
    +
     4239.   3263
    +
     4240.   3263        test_cause("");
    +
     4241.   3263        the_paren.free = true;
    +
     4242.   3263        advance("(");
    +
     4243.   3263        the_value = parse_expression(0);
    +
     4244.   3263        advance(")");
    +
     4245.      1        if (the_value.wrapped === true) {
    +
     4246.      1
    +
     4247.      1// test_cause:
    +
     4248.      1// ["while((0)){}", "condition", "unexpected_a", "(", 6]
    +
     4249.      1
    +
     4250.      1            warn("unexpected_a", the_paren);
    +
     4251.   3259        }
    +
     4252.   3259
    +
     4253.   3259// Check for anticondition.
    +
     4254.   3259
    +
     4255.   3259        switch (the_value.id) {
    +
     4256.   3259        case "%":
    +
     4257.      1            warn("unexpected_a", the_value);
    +
     4258.      1            break;
    +
     4259.      1        case "&":
    +
     4260.      1            warn("unexpected_a", the_value);
    +
     4261.      1            break;
    +
     4262.     17        case "(number)":
    +
     4263.     17            warn("unexpected_a", the_value);
    +
     4264.     17            break;
    +
     4265.      1        case "(string)":
    +
     4266.      1            warn("unexpected_a", the_value);
    +
     4267.      1            break;
    +
     4268.      1        case "*":
    +
     4269.      1            warn("unexpected_a", the_value);
    +
     4270.      1            break;
    +
     4271.      1        case "+":
    +
     4272.      1            warn("unexpected_a", the_value);
    +
     4273.      1            break;
    +
     4274.      1        case "-":
    +
     4275.      1            warn("unexpected_a", the_value);
    +
     4276.      1            break;
    +
     4277.      1        case "/":
    +
     4278.      1            warn("unexpected_a", the_value);
    +
     4279.      1            break;
    +
     4280.      1        case "<<":
    +
     4281.      1            warn("unexpected_a", the_value);
    +
     4282.      1            break;
    +
     4283.      1        case ">>":
    +
     4284.      1            warn("unexpected_a", the_value);
    +
     4285.      1            break;
    +
     4286.      1        case ">>>":
    +
     4287.      1            warn("unexpected_a", the_value);
    +
     4288.      1            break;
    +
     4289.      1        case "?":
    +
     4290.      1            warn("unexpected_a", the_value);
    +
     4291.      1            break;
    +
     4292.      1        case "^":
    +
     4293.      1            warn("unexpected_a", the_value);
    +
     4294.      1            break;
    +
     4295.      1        case "typeof":
    +
     4296.      1            warn("unexpected_a", the_value);
    +
     4297.      1            break;
    +
     4298.      1        case "|":
    +
     4299.      1            warn("unexpected_a", the_value);
    +
     4300.      1            break;
    +
     4301.      1        case "~":
    +
     4302.      1
    +
     4303.      1// test_cause:
    +
     4304.      1// ["if(0%0){}", "condition", "unexpected_a", "%", 5]
    +
     4305.      1// ["if(0&0){}", "condition", "unexpected_a", "&", 5]
    +
     4306.      1// ["if(0){}", "condition", "unexpected_a", "0", 4]
    +
     4307.      1// ["if(0*0){}", "condition", "unexpected_a", "*", 5]
    +
     4308.      1// ["if(0+0){}", "condition", "unexpected_a", "+", 5]
    +
     4309.      1// ["if(0-0){}", "condition", "unexpected_a", "-", 5]
    +
     4310.      1// ["if(0/0){}", "condition", "unexpected_a", "/", 5]
    +
     4311.      1// ["if(0<<0){}", "condition", "unexpected_a", "<<", 5]
    +
     4312.      1// ["if(0>>0){}", "condition", "unexpected_a", ">>", 5]
    +
     4313.      1// ["if(0>>>0){}", "condition", "unexpected_a", ">>>", 5]
    +
     4314.      1// ["if(0?0:0){}", "condition", "unexpected_a", "?", 5]
    +
     4315.      1// ["if(0^0){}", "condition", "unexpected_a", "^", 5]
    +
     4316.      1// ["if(0|0){}", "condition", "unexpected_a", "|", 5]
    +
     4317.      1// ["if(\"aa\"){}", "condition", "unexpected_a", "aa", 4]
    +
     4318.      1// ["if(typeof 0){}", "condition", "unexpected_a", "typeof", 4]
    +
     4319.      1// ["if(~0){}", "condition", "unexpected_a", "~", 4]
    +
     4320.      1
    +
     4321.      1            warn("unexpected_a", the_value);
    +
     4322.      1            break;
    +
     4323.   3259        }
    +
     4324.   3259        return the_value;
    +
     4325.   3259    }
    +
     4326.    631
    +
     4327.  10096    function constant(id, type, value) {
    +
     4328.  10096
    +
     4329.  10096// Create a constant symbol.
    +
     4330.  10096
    +
     4331.  10096        const the_symbol = symbol(id);
    +
     4332.  10096        the_symbol.constant = true;
    +
     4333.  10096        the_symbol.nud_prefix = (
    +
     4334.  10096            typeof value === "function"
    +
     4335.   4417            ? value
    +
     4336.  18689            : function () {
    +
     4337.  18689                token_now.constant = true;
    +
     4338.   5679                if (value !== undefined) {
    +
     4339.   5679                    token_now.value = value;
    +
     4340.   5679                }
    +
     4341.  18689                return token_now;
    +
     4342.  18689            }
    +
     4343.  10096        );
    +
     4344.  10096        the_symbol.type = type;
    +
     4345.  10096        the_symbol.value = value;
    +
     4346.  10096        return the_symbol;
    +
     4347.  10096    }
    +
     4348.    631
    +
     4349.      5    function constant_Function() {
    +
     4350.      2        if (!option_dict.eval) {
    +
     4351.      2
    +
     4352.      2// test_cause:
    +
     4353.      2// ["Function", "constant_Function", "unexpected_a", "Function", 1]
    +
     4354.      2
    +
     4355.      2            warn("unexpected_a", token_now);
    +
     4356.      3        } else if (token_nxt.id !== "(") {
    +
     4357.      3
    +
     4358.      3// test_cause:
    +
     4359.      3// ["
    +
     4360.      3// /*jslint eval*/
    +
     4361.      3// Function
    +
     4362.      3// ", "constant_Function", "expected_a_before_b", "(end)", 1]
    +
     4363.      3
    +
     4364.      3            warn("expected_a_before_b", token_nxt, "(", artifact());
    +
     4365.      3        }
    +
     4366.      5        return token_now;
    +
     4367.      5    }
    +
     4368.    631
    +
     4369.      1    function constant_arguments() {
    +
     4370.      1
    +
     4371.      1// test_cause:
    +
     4372.      1// ["arguments", "constant_arguments", "unexpected_a", "arguments", 1]
    +
     4373.      1
    +
     4374.      1        warn("unexpected_a", token_now);
    +
     4375.      1        return token_now;
    +
     4376.      1    }
    +
     4377.    631
    +
     4378.      4    function constant_eval() {
    +
     4379.      1        if (!option_dict.eval) {
    +
     4380.      1
    +
     4381.      1// test_cause:
    +
     4382.      1// ["eval", "constant_eval", "unexpected_a", "eval", 1]
    +
     4383.      1
    +
     4384.      1            warn("unexpected_a", token_now);
    +
     4385.      3        } else if (token_nxt.id !== "(") {
    +
     4386.      3
    +
     4387.      3// test_cause:
    +
     4388.      3// ["/*jslint eval*/\neval", "constant_eval", "expected_a_before_b", "(end)", 1]
    +
     4389.      3
    +
     4390.      3            warn("expected_a_before_b", token_nxt, "(", artifact());
    +
     4391.      3        }
    +
     4392.      4        return token_now;
    +
     4393.      4    }
    +
     4394.    631
    +
     4395.      1    function constant_ignore() {
    +
     4396.      1
    +
     4397.      1// test_cause:
    +
     4398.      1// ["ignore", "constant_ignore", "unexpected_a", "ignore", 1]
    +
     4399.      1
    +
     4400.      1        warn("unexpected_a", token_now);
    +
     4401.      1        return token_now;
    +
     4402.      1    }
    +
     4403.    631
    +
     4404.      1    function constant_isInfinite() {
    +
     4405.      1
    +
     4406.      1// test_cause:
    +
     4407.      1// ["isFinite", "constant_isInfinite", "expected_a_b", "isFinite", 1]
    +
     4408.      1
    +
     4409.      1        warn("expected_a_b", token_now, "Number.isFinite", "isFinite");
    +
     4410.      1        return token_now;
    +
     4411.      1    }
    +
     4412.    631
    +
     4413.      1    function constant_isNaN() {
    +
     4414.      1
    +
     4415.      1// test_cause:
    +
     4416.      1// ["isNaN(0)", "constant_isNaN", "number_isNaN", "isNaN", 1]
    +
     4417.      1
    +
     4418.      1        warn("number_isNaN", token_now);
    +
     4419.      1        return token_now;
    +
     4420.      1    }
    +
     4421.    631
    +
     4422.      3    function constant_this() {
    +
     4423.      1        if (!option_dict.this) {
    +
     4424.      1
    +
     4425.      1// test_cause:
    +
     4426.      1// ["this", "constant_this", "unexpected_a", "this", 1]
    +
     4427.      1
    +
     4428.      1            warn("unexpected_a", token_now);
    +
     4429.      1        }
    +
     4430.      3        return token_now;
    +
     4431.      3    }
    +
     4432.    631
    +
     4433.   6366    function enroll(name, role, readonly) {
    +
     4434.   6366
    +
     4435.   6366// Enroll a name into the current function context. The role can be exception,
    +
     4436.   6366// function, label, parameter, or variable. We look for variable redefinition
    +
     4437.   6366// because it causes confusion.
    +
     4438.   6366
    +
     4439.   6366        let earlier;
    +
     4440.   6366        let id = name.id;
    +
     4441.   6366
    +
     4442.   6366// Reserved words may not be enrolled.
    +
     4443.   6366
    +
     4444.     42        if (syntax_dict[id] !== undefined && id !== "ignore") {
    +
     4445.      1
    +
     4446.      1// test_cause:
    +
     4447.      1// ["let undefined", "enroll", "reserved_a", "undefined", 5]
    +
     4448.      1
    +
     4449.      1            warn("reserved_a", name);
    +
     4450.      1            return;
    +
     4451.   6365        }
    +
     4452.   6365
    +
     4453.   6365// Has the name been enrolled in this context?
    +
     4454.   6365
    +
     4455.   6365        earlier = functionage.context[id] || catchage.context[id];
    +
     4456.      7        if (earlier) {
    +
     4457.      7
    +
     4458.      7// test_cause:
    +
     4459.      7// ["let aa;let aa", "enroll", "redefinition_a_b", "1", 12]
    +
     4460.      7
    +
     4461.      7            warn("redefinition_a_b", name, id, earlier.line);
    +
     4462.      7            return;
    +
     4463.   6358        }
    +
     4464.   6358
    +
     4465.   6358// Has the name been enrolled in an outer context?
    +
     4466.   6358
    +
     4467.  10756        function_stack.forEach(function ({
    +
     4468.  10756            context
    +
     4469.  10756        }) {
    +
     4470.  10601            earlier = context[id] || earlier;
    +
     4471.  10756        });
    +
     4472.   6358        if (earlier && id === "ignore") {
    +
     4473.      4            if (earlier.role === "variable") {
    +
     4474.      4
    +
     4475.      4// test_cause:
    +
     4476.      4// ["let ignore;function aa(ignore){}", "enroll", "redefinition_a_b", "1", 24]
    +
     4477.      4
    +
     4478.      4                warn("redefinition_a_b", name, id, earlier.line);
    +
     4479.      4            }
    +
     4480.   6354        } else if (
    +
     4481.   6354            earlier
    +
     4482.   6354            && role !== "parameter" && role !== "function"
    +
     4483.   6354            && (role !== "exception" || earlier.role !== "exception")
    +
     4484.   6354        ) {
    +
     4485.   6354
    +
     4486.   6354// test_cause:
    +
     4487.   6354// ["
    +
     4488.   6354// function aa(){try{aa();}catch(aa){aa();}}
    +
     4489.   6354// ", "enroll", "redefinition_a_b", "1", 31]
    +
     4490.   6354// ["function aa(){var aa;}", "enroll", "redefinition_a_b", "1", 19]
    +
     4491.   6354
    +
     4492.   6354            warn("redefinition_a_b", name, id, earlier.line);
    +
     4493.   6354        } else if (
    +
     4494.   6354            option_dict.beta
    +
     4495.   6354            && global_dict[id]
    +
     4496.   6354            && role !== "parameter"
    +
     4497.   6354        ) {
    +
     4498.   6354
    +
     4499.   6354// test_cause:
    +
     4500.   6354// ["let Array", "enroll", "redefinition_global_a_b", "Array", 5]
    +
     4501.   6354
    +
     4502.   6354            warn("redefinition_global_a_b", name, global_dict[id], id);
    +
     4503.   6358        }
    +
     4504.   6358
    +
     4505.   6358// Enroll it.
    +
     4506.   6358
    +
     4507.   6358        Object.assign(name, {
    +
     4508.   6358            dead: true,
    +
     4509.   6358            init: false,
    +
     4510.   6358            parent: (
    +
     4511.   6358                role === "exception"
    +
     4512.   6358                ? catchage
    +
     4513.   6328                : functionage
    +
     4514.   6366            ),
    +
     4515.   6366            readonly,
    +
     4516.   6366            role,
    +
     4517.   6366            used: 0
    +
     4518.   6366        });
    +
     4519.   6366        name.parent.context[id] = name;
    +
     4520.   6366    }
    +
     4521.    631
    +
     4522.  18930    function infix(bp, id, f) {
    +
     4523.  18930
    +
     4524.  18930// Create an infix operator.
    +
     4525.  18930
    +
     4526.  18930        const the_symbol = symbol(id, bp);
    +
     4527.  31941        the_symbol.led_infix = function (left) {
    +
     4528.  31941            const the_token = token_now;
    +
     4529.  31941            the_token.arity = "binary";
    +
     4530.  23493            if (f !== undefined) {
    +
     4531.  23493                return f(left);
    +
     4532.  23493            }
    +
     4533.   8448            the_token.expression = [left, parse_expression(bp)];
    +
     4534.   8448            return the_token;
    +
     4535.   8448        };
    +
     4536.  18930        return the_symbol;
    +
     4537.  18930    }
    +
     4538.    631
    +
     4539.  11597    function infix_dot(left) {
    +
     4540.  11597        const the_token = token_now;
    +
     4541.  11597        let name = token_nxt;
    +
     4542.  11597        if (
    +
     4543.  11597            (
    +
     4544.  11597                left.id !== "(string)"
    +
     4545.     44                || (name.id !== "indexOf" && name.id !== "repeat")
    +
     4546.  11597            )
    +
     4547.  11554            && (
    +
     4548.  11554                left.id !== "["
    +
     4549.  11554                || (
    +
     4550.  11554                    name.id !== "concat"
    +
     4551.  11554                    && name.id !== "flat"
    +
     4552.  11554                    && name.id !== "flatMap"
    +
     4553.  11554                    && name.id !== "forEach"
    +
     4554.  11554                    && name.id !== "join"
    +
     4555.  11554                    && name.id !== "map"
    +
     4556.  11554                )
    +
     4557.  11554            )
    +
     4558.  11509            && (left.id !== "+" || name.id !== "slice")
    +
     4559.  11504            && (
    +
     4560.  11504                left.id !== "(regexp)"
    +
     4561.  11504                || (name.id !== "exec" && name.id !== "test")
    +
     4562.  11504            )
    +
     4563.  11437        ) {
    +
     4564.  11437
    +
     4565.  11437// test_cause:
    +
     4566.  11437// ["\"\".aa", "check_left", "unexpected_a", ".", 3]
    +
     4567.  11437
    +
     4568.  11437            check_left(left, the_token);
    +
     4569.  11437        }
    +
     4570.      1        if (!name.identifier) {
    +
     4571.      1
    +
     4572.      1// test_cause:
    +
     4573.      1// ["aa.0", "infix_dot", "expected_identifier_a", "0", 4]
    +
     4574.      1
    +
     4575.      1            stop("expected_identifier_a");
    +
     4576.  11596        }
    +
     4577.  11596        advance();
    +
     4578.  11596        survey(name);
    +
     4579.  11596
    +
     4580.  11596// The property name is not an expression.
    +
     4581.  11596
    +
     4582.  11596        the_token.name = name;
    +
     4583.  11596        the_token.expression = left;
    +
     4584.  11596        return the_token;
    +
     4585.  11596    }
    +
     4586.    631
    +
     4587.      1    function infix_fart_unwrapped() {
    +
     4588.      1
    +
     4589.      1// test_cause:
    +
     4590.      1// ["aa=>0", "infix_fart_unwrapped", "wrap_fart_parameter", "=>", 3]
    +
     4591.      1
    +
     4592.      1        return stop("wrap_fart_parameter", token_now);
    +
     4593.      1    }
    +
     4594.    631
    +
     4595.      1    function infix_grave(left) {
    +
     4596.      1        const the_tick = prefix_tick();
    +
     4597.      1
    +
     4598.      1// test_cause:
    +
     4599.      1// ["0``", "check_left", "unexpected_a", "`", 2]
    +
     4600.      1
    +
     4601.      1        check_left(left, the_tick);
    +
     4602.      1        the_tick.expression = [left].concat(the_tick.expression);
    +
     4603.      1        return the_tick;
    +
     4604.      1    }
    +
     4605.    631
    +
     4606.   1461    function infix_lbracket(left) {
    +
     4607.   1461        const the_token = token_now;
    +
     4608.   1461        let name;
    +
     4609.   1461        let the_subscript = parse_expression(0);
    +
     4610.   1438        if (the_subscript.id === "(string)" || the_subscript.id === "`") {
    +
     4611.     25            name = survey(the_subscript);
    +
     4612.     25
    +
     4613.     25// PR-404 - Add new directive "subscript" to play nice with Google Closure.
    +
     4614.     25
    +
     4615.     25            if (!option_dict.subscript && jslint_rgx_identifier.test(name)) {
    +
     4616.     25
    +
     4617.     25// test_cause:
    +
     4618.     25// ["aa[`aa`]", "infix_lbracket", "subscript_a", "aa", 4]
    +
     4619.     25
    +
     4620.     25                warn("subscript_a", the_subscript, name);
    +
     4621.     25            }
    +
     4622.     25        }
    +
     4623.   1461
    +
     4624.   1461// test_cause:
    +
     4625.   1461// ["0[0]", "check_left", "unexpected_a", "[", 2]
    +
     4626.   1461
    +
     4627.   1461        check_left(left, the_token);
    +
     4628.   1461        the_token.expression = [left, the_subscript];
    +
     4629.   1461        advance("]");
    +
     4630.   1461        return the_token;
    +
     4631.   1461    }
    +
     4632.    631
    +
     4633.  10421    function infix_lparen(left) {
    +
     4634.  10421        const the_paren = token_now;
    +
     4635.  10421        let ellipsis;
    +
     4636.  10421        let the_argument;
    +
     4637.  10384        if (left.id !== "function") {
    +
     4638.  10384
    +
     4639.  10384// test_cause:
    +
     4640.  10384// ["(0?0:0)()", "check_left", "unexpected_a", "(", 8]
    +
     4641.  10384// ["0()", "check_left", "unexpected_a", "(", 2]
    +
     4642.  10384
    +
     4643.  10384            check_left(left, the_paren);
    +
     4644.  10384        }
    +
     4645.   7699        if (functionage.arity === "statement" && left.identifier) {
    +
     4646.   5503            functionage.name.calls[left.id] = left;
    +
     4647.   5503        }
    +
     4648.  10421        the_paren.expression = [left];
    +
     4649.   8962        if (token_nxt.id !== ")") {
    +
     4650.   8962
    +
     4651.   8962// Parse/loop through each token in expression (...).
    +
     4652.   8962
    +
     4653.  14286            while (true) {
    +
     4654.  14286                if (token_nxt.id === "...") {
    +
     4655.  14286                    ellipsis = true;
    +
     4656.  14286                    advance("...");
    +
     4657.  14286                }
    +
     4658.  14286                the_argument = parse_expression(10);
    +
     4659.  14286                if (ellipsis) {
    +
     4660.  14286                    the_argument.ellipsis = true;
    +
     4661.  14286                }
    +
     4662.  14286                the_paren.expression.push(the_argument);
    +
     4663.  14286                if (token_nxt.id !== ",") {
    +
     4664.  14286                    break;
    +
     4665.  14286                }
    +
     4666.  14286                advance(",");
    +
     4667.  14286            }
    +
     4668.   8962        }
    +
     4669.  10421        advance(")", the_paren);
    +
     4670.   5260        if (the_paren.expression.length === 2) {
    +
     4671.   5260
    +
     4672.   5260// test_cause:
    +
     4673.   5260// ["aa(0)", "infix_lparen", "free", "", 0]
    +
     4674.   5260
    +
     4675.   5260            test_cause("free");
    +
     4676.   5260            the_paren.free = true;
    +
     4677.   5260            if (the_argument.wrapped === true) {
    +
     4678.   5260
    +
     4679.   5260// test_cause:
    +
     4680.   5260// ["aa((0))", "infix_lparen", "unexpected_a", "(", 3]
    +
     4681.   5260
    +
     4682.   5260                warn("unexpected_a", the_paren);
    +
     4683.   5260            }
    +
     4684.   5260            if (the_argument.id === "(") {
    +
     4685.   5260                the_argument.wrapped = true;
    +
     4686.   5260            }
    +
     4687.   5260        } else {
    +
     4688.   5161
    +
     4689.   5161// test_cause:
    +
     4690.   5161// ["aa()", "infix_lparen", "not_free", "", 0]
    +
     4691.   5161// ["aa(0,0)", "infix_lparen", "not_free", "", 0]
    +
     4692.   5161
    +
     4693.   5161            test_cause("not_free");
    +
     4694.   5161            the_paren.free = false;
    +
     4695.   5161        }
    +
     4696.  10421        return the_paren;
    +
     4697.  10421    }
    +
     4698.    631
    +
     4699.     12    function infix_option_chain(left) {
    +
     4700.     12        const the_token = token_now;
    +
     4701.     12        let name = token_nxt;
    +
     4702.     12        if (
    +
     4703.     12            (
    +
     4704.     12                left.id !== "(string)"
    +
     4705.      1                || (name.id !== "indexOf" && name.id !== "repeat")
    +
     4706.     12            )
    +
     4707.     12            && (
    +
     4708.     12                left.id !== "["
    +
     4709.      1                || (
    +
     4710.      1                    name.id !== "concat"
    +
     4711.      1                    && name.id !== "forEach"
    +
     4712.      1                    && name.id !== "join"
    +
     4713.      1                    && name.id !== "map"
    +
     4714.      1                )
    +
     4715.     12            )
    +
     4716.     12
    +
     4717.     12// test_cause:
    +
     4718.     12// ["(0+0)?.0", "infix_option_chain", "check_left", "", 0]
    +
     4719.     12
    +
     4720.      1            && (left.id !== "+" || name.id !== "slice")
    +
     4721.     12            && (
    +
     4722.     12                left.id !== "(regexp)"
    +
     4723.      1                || (name.id !== "exec" && name.id !== "test")
    +
     4724.     12            )
    +
     4725.     12        ) {
    +
     4726.     12            test_cause("check_left");
    +
     4727.     12
    +
     4728.     12// test_cause:
    +
     4729.     12// ["(/./)?.0", "check_left", "unexpected_a", "?.", 6]
    +
     4730.     12// ["\"aa\"?.0", "check_left", "unexpected_a", "?.", 5]
    +
     4731.     12// ["aa=[]?.aa", "check_left", "unexpected_a", "?.", 6]
    +
     4732.     12
    +
     4733.     12            check_left(left, the_token);
    +
     4734.     12        }
    +
     4735.     12
    +
     4736.     12// Issue #468 - Fix optional dynamic-property/function-call not recognized.
    +
     4737.     12
    +
     4738.     11        if (name.id === "[" || name.id === "(") {
    +
     4739.      2            test_cause("dyn_prop_or_call");
    +
     4740.      2
    +
     4741.      2// test_cause:
    +
     4742.      2// ["aa?.(bb)", "infix_option_chain", "dyn_prop_or_call", "", 0]
    +
     4743.      2// ["aa?.[bb]", "infix_option_chain", "dyn_prop_or_call", "", 0]
    +
     4744.      2
    +
     4745.      2            return left;
    +
     4746.     10        }
    +
     4747.     10        if (!name.identifier) {
    +
     4748.      4
    +
     4749.      4// test_cause:
    +
     4750.      4// ["aa?.0", "infix_option_chain", "expected_identifier_a", "0", 5]
    +
     4751.      4
    +
     4752.      4            stop("expected_identifier_a");
    +
     4753.      6        }
    +
     4754.      6        advance();
    +
     4755.      6        survey(name);
    +
     4756.      6
    +
     4757.      6// The property name is not an expression.
    +
     4758.      6
    +
     4759.      6        the_token.name = name;
    +
     4760.      6        the_token.expression = left;
    +
     4761.      6        return the_token;
    +
     4762.      6    }
    +
     4763.    631
    +
     4764.    631    function infixr(bp, id) {
    +
     4765.    631
    +
     4766.    631// Create a right associative infix operator.
    +
     4767.    631
    +
     4768.    631        const the_symbol = symbol(id, bp);
    +
     4769.      1        the_symbol.led_infix = function parse_infixr_led(left) {
    +
     4770.      1            const the_token = token_now;
    +
     4771.      1
    +
     4772.      1// test_cause:
    +
     4773.      1// ["0**0", "parse_infixr_led", "led_infix", "", 0]
    +
     4774.      1
    +
     4775.      1            test_cause("led_infix");
    +
     4776.      1            the_token.arity = "binary";
    +
     4777.      1            the_token.expression = [left, parse_expression(bp - 1)];
    +
     4778.      1            return the_token;
    +
     4779.      1        };
    +
     4780.    631        return the_symbol;
    +
     4781.    631    }
    +
     4782.    631
    +
     4783.  56002    function parse_expression(rbp, initial) {
    +
     4784.  56002
    +
     4785.  56002// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it
    +
     4786.  56002// is looking for ad hoc lint patterns. We add .fud_stmt to Pratt's model, which
    +
     4787.  56002// is like .nud_prefix except that it is only used on the first token of a
    +
     4788.  56002// statement. Having .fud_stmt makes it much easier to define statement-oriented
    +
     4789.  56002// languages like JavaScript. I retained Pratt's nomenclature.
    +
     4790.  56002// They are elements of the parsing method called Top Down Operator Precedence.
    +
     4791.  56002
    +
     4792.  56002// .nud_prefix  Null denotation. The prefix handler.
    +
     4793.  56002// .fud_stmt    First null denotation. The statement handler.
    +
     4794.  56002// .led_infix   Left denotation. The infix/postfix handler.
    +
     4795.  56002//  lbp         Left binding power of infix operator. It tells us how strongly
    +
     4796.  56002//              the operator binds to the argument at its left.
    +
     4797.  56002//  rbp         Right binding power.
    +
     4798.  56002
    +
     4799.  56002// It processes a nud_prefix (variable, constant, prefix operator). It will then
    +
     4800.  56002// process leds (infix operators) until the bind powers cause it to stop (it
    +
     4801.  56002// consumes tokens until it meets a token whose lbp <= rbp). Specifically, it
    +
     4802.  56002// means that it collects all tokens that bind together before returning to the
    +
     4803.  56002// operator that called it. It returns the expression's parse tree.
    +
     4804.  56002
    +
     4805.  56002// For example, "3 + 1 * 2 * 4 + 5"
    +
     4806.  56002// parses into
    +
     4807.  56002// {
    +
     4808.  56002//     "id": "+",
    +
     4809.  56002//     "expression": [
    +
     4810.  56002//         {
    +
     4811.  56002//             "id": "+",
    +
     4812.  56002//             "expression": [
    +
     4813.  56002//                 {
    +
     4814.  56002//                     "id": "(number)",
    +
     4815.  56002//                     "value": "3"
    +
     4816.  56002//                 },
    +
     4817.  56002//                 {
    +
     4818.  56002//                     "id": "*",
    +
     4819.  56002//                     "expression": [
    +
     4820.  56002//                         {
    +
     4821.  56002//                             "id": "*",
    +
     4822.  56002//                             "expression": [
    +
     4823.  56002//                                 {
    +
     4824.  56002//                                     "id": "(number)",
    +
     4825.  56002//                                     "value": "1"
    +
     4826.  56002//                                 },
    +
     4827.  56002//                                 {
    +
     4828.  56002//                                     "id": "(number)",
    +
     4829.  56002//                                     "value": "2"
    +
     4830.  56002//                                 }
    +
     4831.  56002//                             ]
    +
     4832.  56002//                         },
    +
     4833.  56002//                         {
    +
     4834.  56002//                             "id": "(number)",
    +
     4835.  56002//                             "value": "4"
    +
     4836.  56002//                         }
    +
     4837.  56002//                     ]
    +
     4838.  56002//                 }
    +
     4839.  56002//             ]
    +
     4840.  56002//         },
    +
     4841.  56002//         {
    +
     4842.  56002//             "id": "(number)",
    +
     4843.  56002//             "value": "5"
    +
     4844.  56002//         }
    +
     4845.  56002//     ]
    +
     4846.  56002// }
    +
     4847.  56002
    +
     4848.  56002        let left;
    +
     4849.  56002        let the_symbol;
    +
     4850.  56002
    +
     4851.  56002// Statements will have already advanced, so advance now only if the token is
    +
     4852.  56002// not the first of a statement.
    +
     4853.  56002
    +
     4854.  44642        if (!initial) {
    +
     4855.  44642            advance();
    +
     4856.  44642        }
    +
     4857.  56002        the_symbol = syntax_dict[token_now.id];
    +
     4858.  24510        if (the_symbol !== undefined && the_symbol.nud_prefix !== undefined) {
    +
     4859.  24451
    +
     4860.  24451// test_cause:
    +
     4861.  24451// ["0", "parse_expression", "symbol", "", 0]
    +
     4862.  24451
    +
     4863.  24451            test_cause("symbol");
    +
     4864.  24451            left = the_symbol.nud_prefix();
    +
     4865.  31551        } else if (token_now.identifier) {
    +
     4866.  31551
    +
     4867.  31551// test_cause:
    +
     4868.  31551// ["aa", "parse_expression", "identifier", "", 0]
    +
     4869.  31551
    +
     4870.  31551            test_cause("identifier");
    +
     4871.  31551            left = token_now;
    +
     4872.  31551            left.arity = "variable";
    +
     4873.  31551        } else {
    +
     4874.  31551
    +
     4875.  31551// test_cause:
    +
     4876.  31551// ["!", "parse_expression", "unexpected_a", "(end)", 1]
    +
     4877.  31551// ["/./", "parse_expression", "unexpected_a", "/", 1]
    +
     4878.  31551// ["let aa=`${}`;", "parse_expression", "unexpected_a", "}", 11]
    +
     4879.  31551
    +
     4880.  31551            return stop("unexpected_a", token_now);
    +
     4881.  55966        }
    +
     4882.  55966
    +
     4883.  55966// Parse/loop through each symbol in expression.
    +
     4884.  55966
    +
     4885.  93124        while (true) {
    +
     4886.  93124            the_symbol = syntax_dict[token_nxt.id];
    +
     4887.  93124            if (
    +
     4888.  93124                the_symbol === undefined
    +
     4889.  93124                || the_symbol.led_infix === undefined
    +
     4890.  93124                || the_symbol.lbp <= rbp
    +
     4891.  93124            ) {
    +
     4892.  93124                break;
    +
     4893.  93124            }
    +
     4894.  93124            advance();
    +
     4895.  93124            left = the_symbol.led_infix(left);
    +
     4896.  93124        }
    +
     4897.  55955        return left;
    +
     4898.  55955    }
    +
     4899.    631
    +
     4900.     14    function parse_fart(the_fart) {
    +
     4901.     14
    +
     4902.     14// Give the function properties storing its names and for observing the depth
    +
     4903.     14// of loops and switches.
    +
     4904.     14
    +
     4905.     14        Object.assign(the_fart, {
    +
     4906.     14            arity: "binary",
    +
     4907.     14            context: empty(),
    +
     4908.     14            finally: 0,
    +
     4909.     14            level: functionage.level + 1,
    +
     4910.     14            loop: 0,
    +
     4911.     14            name: anon,
    +
     4912.     14            switch: 0,
    +
     4913.     14            try: 0
    +
     4914.     14        });
    +
     4915.     14
    +
     4916.     14// PR-384 - Relax warning "function_in_loop".
    +
     4917.     14//
    +
     4918.     14//         if (functionage.loop > 0) {
    +
     4919.     14
    +
     4920.     14// // test_cause:
    +
     4921.     14// // ["while(0){aa.map(()=>0);}", "parse_fart", "function_in_loop", "=>", 19]
    +
     4922.     14//
    +
     4923.     14//             warn("function_in_loop", the_fart);
    +
     4924.     14//         }
    +
     4925.     14
    +
     4926.     14// Push the current function context and establish a new one.
    +
     4927.     14
    +
     4928.     14        function_list.push(the_fart);
    +
     4929.     14        function_stack.push(functionage);
    +
     4930.     14        functionage = the_fart;
    +
     4931.     14
    +
     4932.     14// Parse the parameter list.
    +
     4933.     14
    +
     4934.     14        prefix_function_parameter(the_fart);
    +
     4935.     14        advance("=>");
    +
     4936.     14
    +
     4937.     14// The function's body is a block.
    +
     4938.     14
    +
     4939.      3        if (token_nxt.id === "{") {
    +
     4940.      3            if (!option_dict.fart) {
    +
     4941.      3
    +
     4942.      3// test_cause:
    +
     4943.      3// ["()=>{}", "parse_fart", "use_function_not_fart", "=>", 3]
    +
     4944.      3
    +
     4945.      3                warn("use_function_not_fart", the_fart);
    +
     4946.      3            }
    +
     4947.      3            the_fart.block = block("body");
    +
     4948.     11        } else if (
    +
     4949.     11            syntax_dict[token_nxt.id] !== undefined
    +
     4950.     11            && syntax_dict[token_nxt.id].fud_stmt !== undefined
    +
     4951.     11        ) {
    +
     4952.     11
    +
     4953.     11// PR-384 - Bugfix - Fixes issue #379 - warn against naked-statement in fart.
    +
     4954.     11
    +
     4955.     11// test_cause:
    +
     4956.     11// ["()=>delete aa", "parse_fart", "unexpected_a_after_b", "=>", 5]
    +
     4957.     11
    +
     4958.     11            stop("unexpected_a_after_b", token_nxt, token_nxt.id, "=>");
    +
     4959.     11
    +
     4960.     11// The function's body is an expression.
    +
     4961.     11
    +
     4962.     11        } else {
    +
     4963.     11            the_fart.expression = parse_expression(0);
    +
     4964.     13        }
    +
     4965.     13
    +
     4966.     13// Restore the previous context.
    +
     4967.     13
    +
     4968.     13        functionage = function_stack.pop();
    +
     4969.     13        return the_fart;
    +
     4970.     13    }
    +
     4971.    631
    +
     4972.  12961    function parse_json() {
    +
     4973.  12961        let container;
    +
     4974.  12961        let is_dup;
    +
     4975.  12961        let name;
    +
     4976.  12961        let negative;
    +
     4977.  12961        switch (token_nxt.id) {
    +
     4978.   5262        case "(number)":
    +
     4979.   5262            if (!jslint_rgx_json_number.test(token_nxt.value)) {
    +
     4980.   5262
    +
     4981.   5262// test_cause:
    +
     4982.   5262// ["[-.0]", "parse_json", "unexpected_a", ".", 3]
    +
     4983.   5262// ["[-0x0]", "parse_json", "unexpected_a", "0x0", 3]
    +
     4984.   5262// ["[.0]", "parse_json", "unexpected_a", ".", 2]
    +
     4985.   5262// ["[0x0]", "parse_json", "unexpected_a", "0x0", 2]
    +
     4986.   5262
    +
     4987.   5262                warn("unexpected_a");
    +
     4988.   5262            }
    +
     4989.   5262            advance("(number)");
    +
     4990.   5262            return token_now;
    +
     4991.   1767        case "(string)":
    +
     4992.   1767            if (token_nxt.quote !== "\"") {
    +
     4993.   1767
    +
     4994.   1767// test_cause:
    +
     4995.   1767// ["['']", "parse_json", "unexpected_a", "'", 2]
    +
     4996.   1767
    +
     4997.   1767                warn("unexpected_a", token_nxt, token_nxt.quote);
    +
     4998.   1767            }
    +
     4999.   1767            advance("(string)");
    +
     5000.   1767            return token_now;
    +
     5001.      3        case "-":
    +
     5002.      3            negative = token_nxt;
    +
     5003.      3            negative.arity = "unary";
    +
     5004.      3            advance("-");
    +
     5005.      3
    +
     5006.      3// Recurse parse_json().
    +
     5007.      3
    +
     5008.      3            negative.expression = parse_json();
    +
     5009.      3            return negative;
    +
     5010.   1649        case "[":
    +
     5011.   1649
    +
     5012.   1649// test_cause:
    +
     5013.   1649// ["[]", "parse_json", "bracket", "", 0]
    +
     5014.   1649
    +
     5015.   1649            test_cause("bracket");
    +
     5016.   1649            container = token_nxt;
    +
     5017.   1649            container.expression = [];
    +
     5018.   1649            advance("[");
    +
     5019.   1649            if (token_nxt.id !== "]") {
    +
     5020.   3300                while (true) {
    +
     5021.   3300
    +
     5022.   3300// Recurse parse_json().
    +
     5023.   3300
    +
     5024.   3300                    container.expression.push(parse_json());
    +
     5025.   3300                    if (token_nxt.id !== ",") {
    +
     5026.   3300
    +
     5027.   3300// test_cause:
    +
     5028.   3300// ["[0,0]", "parse_json", "comma", "", 0]
    +
     5029.   3300
    +
     5030.   3300                        test_cause("comma");
    +
     5031.   3300                        break;
    +
     5032.   3300                    }
    +
     5033.   3300                    advance(",");
    +
     5034.   3300                }
    +
     5035.   1649            }
    +
     5036.   1649            advance("]", container);
    +
     5037.   1649            return container;
    +
     5038.    509        case "false":
    +
     5039.    511        case "null":
    +
     5040.    896        case "true":
    +
     5041.    896
    +
     5042.    896// test_cause:
    +
     5043.    896// ["[false]", "parse_json", "constant", "", 0]
    +
     5044.    896// ["[null]", "parse_json", "constant", "", 0]
    +
     5045.    896// ["[true]", "parse_json", "constant", "", 0]
    +
     5046.    896
    +
     5047.    896            test_cause("constant");
    +
     5048.    896            advance();
    +
     5049.    896            return token_now;
    +
     5050.   3379        case "{":
    +
     5051.   3379
    +
     5052.   3379// test_cause:
    +
     5053.   3379// ["{}", "parse_json", "brace", "", 0]
    +
     5054.   3379
    +
     5055.   3379            test_cause("brace");
    +
     5056.   3379            container = token_nxt;
    +
     5057.   3379
    +
     5058.   3379// Explicit empty-object required to detect "__proto__".
    +
     5059.   3379
    +
     5060.   3379            is_dup = empty();
    +
     5061.   3379            container.expression = [];
    +
     5062.   3379            advance("{");
    +
     5063.   3379            if (token_nxt.id !== "}") {
    +
     5064.   3379
    +
     5065.   3379// JSON
    +
     5066.   3379// Parse/loop through each property in {...}.
    +
     5067.   3379
    +
     5068.   9636                while (true) {
    +
     5069.   9636                    if (token_nxt.quote !== "\"") {
    +
     5070.   9636
    +
     5071.   9636// test_cause:
    +
     5072.   9636// ["{0:0}", "parse_json", "unexpected_a", "0", 2]
    +
     5073.   9636
    +
     5074.   9636                        warn(
    +
     5075.   9636                            "unexpected_a",
    +
     5076.   9636                            token_nxt,
    +
     5077.   9636                            token_nxt.quote
    +
     5078.   9636                        );
    +
     5079.   9636                    }
    +
     5080.   9636                    name = token_nxt;
    +
     5081.   9636                    advance("(string)");
    +
     5082.   9636                    if (is_dup[token_now.value] !== undefined) {
    +
     5083.   9636
    +
     5084.   9636// test_cause:
    +
     5085.   9636// ["{\"aa\":0,\"aa\":0}", "parse_json", "duplicate_a", "aa", 9]
    +
     5086.   9636
    +
     5087.   9636                        warn("duplicate_a", token_now);
    +
     5088.   9636                    } else if (token_now.value === "__proto__") {
    +
     5089.   9636
    +
     5090.   9636// test_cause:
    +
     5091.   9636// ["{\"__proto__\":0}", "parse_json", "weird_property_a", "__proto__", 2]
    +
     5092.   9636
    +
     5093.   9636                        warn("weird_property_a", token_now);
    +
     5094.   9636                    } else {
    +
     5095.   9636                        is_dup[token_now.value] = token_now;
    +
     5096.   9636                    }
    +
     5097.   9636                    advance(":");
    +
     5098.   9636                    container.expression.push(
    +
     5099.   9636
    +
     5100.   9636// Recurse parse_json().
    +
     5101.   9636
    +
     5102.   9636                        Object.assign(parse_json(), {
    +
     5103.   9636                            label: name
    +
     5104.   9636                        })
    +
     5105.   9636                    );
    +
     5106.   9636                    if (token_nxt.id !== ",") {
    +
     5107.   9636                        break;
    +
     5108.   9636                    }
    +
     5109.   9636                    advance(",");
    +
     5110.   9636                }
    +
     5111.   3379            }
    +
     5112.   3379            advance("}", container);
    +
     5113.   3379            return container;
    +
     5114.      5        default:
    +
     5115.      5
    +
     5116.      5// test_cause:
    +
     5117.      5// ["[undefined]", "parse_json", "unexpected_a", "undefined", 2]
    +
     5118.      5
    +
     5119.      5            stop("unexpected_a");
    +
     5120.  12961        }
    +
     5121.  12961    }
    +
     5122.    631
    +
     5123.  20929    function parse_statement() {
    +
     5124.  20929
    +
     5125.  20929// Parse a statement. Any statement may have a label, but only four statements
    +
     5126.  20929// have use for one. A statement can be one of the standard statements, or
    +
     5127.  20929// an assignment expression, or an invocation expression.
    +
     5128.  20929
    +
     5129.  20929        let first;
    +
     5130.  20929        let the_label;
    +
     5131.  20929        let the_statement;
    +
     5132.  20929        let the_symbol;
    +
     5133.  20929        advance();
    +
     5134.  20742        if (token_now.identifier && token_nxt.id === ":") {
    +
     5135.     13            the_label = token_now;
    +
     5136.     13            if (the_label.id === "ignore") {
    +
     5137.     13
    +
     5138.     13// test_cause:
    +
     5139.     13// ["ignore:", "parse_statement", "unexpected_a", "ignore", 1]
    +
     5140.     13
    +
     5141.     13                warn("unexpected_a", the_label);
    +
     5142.     13            }
    +
     5143.     13            advance(":");
    +
     5144.     13            switch (token_nxt.id) {
    +
     5145.     13            case "do":
    +
     5146.     13            case "for":
    +
     5147.     13            case "switch":
    +
     5148.     13            case "while":
    +
     5149.     13
    +
     5150.     13// test_cause:
    +
     5151.     13// ["aa:do{}", "parse_statement", "the_statement_label", "do", 0]
    +
     5152.     13// ["aa:for{}", "parse_statement", "the_statement_label", "for", 0]
    +
     5153.     13// ["aa:switch{}", "parse_statement", "the_statement_label", "switch", 0]
    +
     5154.     13// ["aa:while{}", "parse_statement", "the_statement_label", "while", 0]
    +
     5155.     13
    +
     5156.     13                test_cause("the_statement_label", token_nxt.id);
    +
     5157.     13                enroll(the_label, "label", true);
    +
     5158.     13                the_label.dead = false;
    +
     5159.     13                the_label.init = true;
    +
     5160.     13                the_statement = parse_statement();
    +
     5161.     13                functionage.statement_prv = the_statement;
    +
     5162.     13                the_statement.label = the_label;
    +
     5163.     13                the_statement.statement = true;
    +
     5164.     13                return the_statement;
    +
     5165.     13            }
    +
     5166.     13            advance();
    +
     5167.     13
    +
     5168.     13// test_cause:
    +
     5169.     13// ["aa:", "parse_statement", "unexpected_label_a", "aa", 1]
    +
     5170.     13
    +
     5171.     13            warn("unexpected_label_a", the_label);
    +
     5172.  20920        }
    +
     5173.  20920
    +
     5174.  20920// Parse the statement.
    +
     5175.  20920
    +
     5176.  20920        first = token_now;
    +
     5177.  20920        first.statement = true;
    +
     5178.  20920        the_symbol = syntax_dict[first.id];
    +
     5179.  20920        if (
    +
     5180.  20920            the_symbol !== undefined
    +
     5181.  20920            && the_symbol.fud_stmt !== undefined
    +
     5182.  20929
    +
     5183.  20929// PR-318 - Bugfix - Fixes issues #316, #317 - dynamic-import().
    +
     5184.  20929
    +
     5185.  10154            && !(the_symbol.id === "import" && token_nxt.id === "(")
    +
     5186.  10152        ) {
    +
     5187.  10152            the_symbol.disrupt = false;
    +
     5188.  10152            the_symbol.statement = true;
    +
     5189.  10152            token_now.arity = "statement";
    +
     5190.  10152            the_statement = the_symbol.fud_stmt();
    +
     5191.  10152            functionage.statement_prv = the_statement;
    +
     5192.  10768        } else {
    +
     5193.  10768
    +
     5194.  10768// It is an expression statement.
    +
     5195.  10768
    +
     5196.  10768            the_statement = parse_expression(0, true);
    +
     5197.  10768            functionage.statement_prv = the_statement;
    +
     5198.  10768            if (the_statement.wrapped && the_statement.id !== "(") {
    +
     5199.  10768
    +
     5200.  10768// test_cause:
    +
     5201.  10768// ["(0)", "parse_statement", "unexpected_a", "(", 1]
    +
     5202.  10768
    +
     5203.  10768                warn("unexpected_a", first);
    +
     5204.  10768            }
    +
     5205.  10768            semicolon();
    +
     5206.  20826        }
    +
     5207.  20826        if (the_label !== undefined) {
    +
     5208.      1            the_label.dead = true;
    +
     5209.  20826        }
    +
     5210.  20826        return the_statement;
    +
     5211.  20826    }
    +
     5212.    631
    +
     5213.   7501    function parse_statements() {
    +
     5214.   7501
    +
     5215.   7501// Parse a list of statements. Give a warning if an unreachable statement
    +
     5216.   7501// follows a disruptive statement.
    +
     5217.   7501
    +
     5218.   7501        const statement_list = [];
    +
     5219.   7501        let a_statement;
    +
     5220.   7501        let disrupt = false;
    +
     5221.   7501
    +
     5222.   7501// Parse/loop each statement until a statement-terminator is reached.
    +
     5223.   7501
    +
     5224.  28003        while (true) {
    +
     5225.  28003            switch (token_nxt.id) {
    +
     5226.  28003            case "(end)":
    +
     5227.  28003            case "case":
    +
     5228.  28003            case "default":
    +
     5229.  28003            case "else":
    +
     5230.  28003            case "}":
    +
     5231.  28003
    +
     5232.  28003// test_cause:
    +
     5233.  28003// [";", "parse_statements", "closer", "", 0]
    +
     5234.  28003// ["case", "parse_statements", "closer", "", 0]
    +
     5235.  28003// ["default", "parse_statements", "closer", "", 0]
    +
     5236.  28003// ["else", "parse_statements", "closer", "", 0]
    +
     5237.  28003// ["}", "parse_statements", "closer", "", 0]
    +
     5238.  28003
    +
     5239.  28003                test_cause("closer");
    +
     5240.  28003                return statement_list;
    +
     5241.  28003            }
    +
     5242.  28003            a_statement = parse_statement();
    +
     5243.  28003            statement_list.push(a_statement);
    +
     5244.  28003            if (disrupt) {
    +
     5245.  28003
    +
     5246.  28003// test_cause:
    +
     5247.  28003// ["while(0){break;0;}", "parse_statements", "unreachable_a", "0", 16]
    +
     5248.  28003
    +
     5249.  28003                warn("unreachable_a", a_statement);
    +
     5250.  28003            }
    +
     5251.  28003            disrupt = a_statement.disrupt;
    +
     5252.  28003        }
    +
     5253.   7501    }
    +
     5254.    631
    +
     5255.   1262    function postassign(id) {
    +
     5256.   1262
    +
     5257.   1262// Create one of the postassign operators.
    +
     5258.   1262
    +
     5259.   1262        const the_symbol = symbol(id, 150);
    +
     5260.      1        the_symbol.led_infix = function (left) {
    +
     5261.      1            token_now.expression = left;
    +
     5262.      1            token_now.arity = "postassign";
    +
     5263.      1            check_mutation(token_now.expression);
    +
     5264.      1            return token_now;
    +
     5265.      1        };
    +
     5266.   1262        return the_symbol;
    +
     5267.   1262    }
    +
     5268.    631
    +
     5269.   1262    function preassign(id) {
    +
     5270.   1262
    +
     5271.   1262// Create one of the preassign operators.
    +
     5272.   1262
    +
     5273.   1262        const the_symbol = symbol(id);
    +
     5274.      2        the_symbol.nud_prefix = function () {
    +
     5275.      2            const the_token = token_now;
    +
     5276.      2            the_token.arity = "preassign";
    +
     5277.      2            the_token.expression = parse_expression(150);
    +
     5278.      2            check_mutation(the_token.expression);
    +
     5279.      2            return the_token;
    +
     5280.      2        };
    +
     5281.   1262        return the_symbol;
    +
     5282.   1262    }
    +
     5283.    631
    +
     5284.  10727    function prefix(id, f) {
    +
     5285.  10727
    +
     5286.  10727// Create a prefix operator.
    +
     5287.  10727
    +
     5288.  10727        const the_symbol = symbol(id);
    +
     5289.   5744        the_symbol.nud_prefix = function () {
    +
     5290.   5744            const the_token = token_now;
    +
     5291.   5744            the_token.arity = "unary";
    +
     5292.   4932            if (typeof f === "function") {
    +
     5293.   4932                return f();
    +
     5294.   4932            }
    +
     5295.    812            the_token.expression = parse_expression(150);
    +
     5296.    812            return the_token;
    +
     5297.    812        };
    +
     5298.  10727        return the_symbol;
    +
     5299.  10727    }
    +
     5300.    631
    +
     5301.      1    function prefix_assign_divide() {
    +
     5302.      1
    +
     5303.      1// test_cause:
    +
     5304.      1// ["/=", "prefix_assign_divide", "expected_a_b", "/=", 1]
    +
     5305.      1
    +
     5306.      1        stop("expected_a_b", token_now, "/\\=", "/=");
    +
     5307.      1    }
    +
     5308.    631
    +
     5309.    136    function prefix_async() {
    +
     5310.    136        let the_async = token_now;
    +
     5311.    136        let the_function;
    +
     5312.    136        token_nxt.arity = the_async.arity;
    +
     5313.    136
    +
     5314.    136// PR-414 - Parse async fart.
    +
     5315.    136
    +
     5316.      3        if (token_nxt.fart) {
    +
     5317.      3            advance("(");
    +
     5318.      3            the_function = Object.assign(token_now.fart, {
    +
     5319.      3                async: 1
    +
     5320.      3            });
    +
     5321.      3            if (!option_dict.fart) {
    +
     5322.      3
    +
     5323.      3// test_cause:
    +
     5324.      3// ["async()=>0", "prefix_async", "use_function_not_fart", "=>", 8]
    +
     5325.      3
    +
     5326.      3                warn("use_function_not_fart", the_function);
    +
     5327.      3            }
    +
     5328.      3            prefix_lparen();
    +
     5329.      3
    +
     5330.      3// Parse async function.
    +
     5331.      3
    +
     5332.    133        } else {
    +
     5333.    133            advance("function");
    +
     5334.    133            the_function = Object.assign(token_now, {
    +
     5335.    133                async: 1
    +
     5336.    133            });
    +
     5337.    133            prefix_function();
    +
     5338.    133        }
    +
     5339.      3        if (the_function.async === 1) {
    +
     5340.      3
    +
     5341.      3// test_cause:
    +
     5342.      3// ["
    +
     5343.      3// async function aa(){}
    +
     5344.      3// ", "prefix_async", "missing_await_statement", "function", 7]
    +
     5345.      3
    +
     5346.      3            warn("missing_await_statement", the_function);
    +
     5347.      3        }
    +
     5348.    136        return the_function;
    +
     5349.    136    }
    +
     5350.    631
    +
     5351.    297    function prefix_await() {
    +
     5352.    297        const the_await = token_now;
    +
     5353.    297
    +
     5354.    297// PR-370 - Add top-level-await support.
    +
     5355.    297
    +
     5356.      4        if (functionage.async === 0 && functionage !== token_global) {
    +
     5357.      2
    +
     5358.      2// test_cause:
    +
     5359.      2// ["function aa(){aa=await 0;}", "prefix_await", "unexpected_a", "await", 18]
    +
     5360.      2// ["function aa(){await 0;}", "prefix_await", "unexpected_a", "await", 15]
    +
     5361.      2
    +
     5362.      2            warn("unexpected_a", the_await);
    +
     5363.    295        } else {
    +
     5364.    295            functionage.async += 1;
    +
     5365.    295        }
    +
     5366.    185        if (the_await.arity === "statement") {
    +
     5367.    185
    +
     5368.    185// PR-405 - Bugfix - fix expression after "await" mis-identified as statement.
    +
     5369.    185
    +
     5370.    185            the_await.expression = parse_expression(150);
    +
     5371.    185            semicolon();
    +
     5372.    185        } else {
    +
     5373.    112            the_await.expression = parse_expression(150);
    +
     5374.    112        }
    +
     5375.    297        return the_await;
    +
     5376.    297    }
    +
     5377.    631
    +
     5378.      1    function prefix_fart() {
    +
     5379.      1
    +
     5380.      1// test_cause:
    +
     5381.      1// ["=>0", "prefix_fart", "expected_a_before_b", "=>", 1]
    +
     5382.      1
    +
     5383.      1        return stop("expected_a_before_b", token_now, "()", "=>");
    +
     5384.      1    }
    +
     5385.    631
    +
     5386.   2005    function prefix_function(the_function) {
    +
     5387.     11        let name = the_function && the_function.name;
    +
     5388.   1994        if (the_function === undefined) {
    +
     5389.   1994            the_function = token_now;
    +
     5390.   1994
    +
     5391.   1994// A function statement must have a name that will be in the parent's scope.
    +
     5392.   1994
    +
     5393.   1994            if (the_function.arity === "statement") {
    +
     5394.   1994                if (!token_nxt.identifier) {
    +
     5395.   1994
    +
     5396.   1994// test_cause:
    +
     5397.   1994// ["function(){}", "prefix_function", "expected_identifier_a", "(", 9]
    +
     5398.   1994// ["function*aa(){}", "prefix_function", "expected_identifier_a", "*", 9]
    +
     5399.   1994
    +
     5400.   1994                    return stop("expected_identifier_a");
    +
     5401.   1994                }
    +
     5402.   1994                name = token_nxt;
    +
     5403.   1994                enroll(name, "variable", true);
    +
     5404.   1994                the_function.name = Object.assign(name, {
    +
     5405.   1994                    calls: empty(),
    +
     5406.   1994
    +
     5407.   1994// PR-331 - Bugfix - Fixes issue #272 - function hoisting not allowed.
    +
     5408.   1994
    +
     5409.   1994                    dead: false,
    +
     5410.   1994                    init: true
    +
     5411.   1994                });
    +
     5412.   1994                advance();
    +
     5413.   1994            } else if (name === undefined) {
    +
     5414.   1994
    +
     5415.   1994// A function expression may have an optional name.
    +
     5416.   1994
    +
     5417.   1994                the_function.name = anon;
    +
     5418.   1994                if (token_nxt.identifier) {
    +
     5419.   1994                    name = token_nxt;
    +
     5420.   1994                    the_function.name = name;
    +
     5421.   1994                    advance();
    +
     5422.   1994                }
    +
     5423.   1994            }
    +
     5424.   2003        }
    +
     5425.   2003
    +
     5426.   2003//  Probably deadcode.
    +
     5427.   2003//  if (mode_mega) {
    +
     5428.   2003//      warn("unexpected_a", the_function);
    +
     5429.   2003//  }
    +
     5430.   2003//  jslint_assert(!mode_mega, `Expected !mode_mega.`);
    +
     5431.   2003
    +
     5432.   2003// PR-378 - Relax warning "function_in_loop".
    +
     5433.   2003//
    +
     5434.   2003// // Don't create functions in loops. It is inefficient, and it can lead to
    +
     5435.   2003// // scoping errors.
    +
     5436.   2003//
    +
     5437.   2003//         if (functionage.loop > 0) {
    +
     5438.   2003//
    +
     5439.   2003// // test_cause:
    +
     5440.   2003// // ["
    +
     5441.   2003// // while(0){aa.map(function(){});}
    +
     5442.   2003// // ", "prefix_function", "function_in_loop", "function", 17]
    +
     5443.   2003//
    +
     5444.   2003//             warn("function_in_loop", the_function);
    +
     5445.   2003//         }
    +
     5446.   2003
    +
     5447.   2003// Give the function properties for storing its names and for observing the
    +
     5448.   2003// depth of loops and switches.
    +
     5449.   2003
    +
     5450.   2003        Object.assign(the_function, {
    +
     5451.   2003            async: the_function.async || 0,
    +
     5452.   2005            context: empty(),
    +
     5453.   2005            finally: 0,
    +
     5454.   2005            level: functionage.level + 1,
    +
     5455.   2005            loop: 0,
    +
     5456.   2005            statement_prv: undefined,
    +
     5457.   2005            switch: 0,
    +
     5458.   2005            try: 0
    +
     5459.   2005        });
    +
     5460.    929        if (the_function.arity !== "statement" && typeof name === "object") {
    +
     5461.     38
    +
     5462.     38// test_cause:
    +
     5463.     38// ["let aa=function bb(){return;};", "prefix_function", "expression", "bb", 0]
    +
     5464.     38
    +
     5465.     38            test_cause("expression", name.id);
    +
     5466.     38            enroll(name, "function", true);
    +
     5467.     38            name.dead = false;
    +
     5468.     38            name.init = true;
    +
     5469.     38            name.used = 1;
    +
     5470.   2003        }
    +
     5471.   2003
    +
     5472.   2003// PR-334 - Bugfix - fix function-redefinition not warned inside function-call.
    +
     5473.   2003// Push the current function context and establish a new one.
    +
     5474.   2003
    +
     5475.   2003        function_list.push(the_function);
    +
     5476.   2003        function_stack.push(functionage);
    +
     5477.   2003        functionage = the_function;
    +
     5478.   2003
    +
     5479.   2003// Parse the parameter list.
    +
     5480.   2003
    +
     5481.   2003        advance("(");
    +
     5482.   2003        token_now.arity = "function";
    +
     5483.   2003        prefix_function_parameter(the_function);
    +
     5484.   2003
    +
     5485.   2003// The function's body is a block.
    +
     5486.   2003
    +
     5487.   2003        the_function.block = block("body");
    +
     5488.   2003        if (
    +
     5489.   2003            the_function.arity === "statement"
    +
     5490.   2003            && token_nxt.line === token_now.line
    +
     5491.      2        ) {
    +
     5492.      2
    +
     5493.      2// test_cause:
    +
     5494.      2// ["function aa(){}0", "prefix_function", "unexpected_a", "0", 16]
    +
     5495.      2
    +
     5496.      2            return stop("unexpected_a");
    +
     5497.   1990        }
    +
     5498.   1990        if (
    +
     5499.   1990            token_nxt.id === "."
    +
     5500.   1990            || token_nxt.id === "?."
    +
     5501.   2005
    +
     5502.   2005// PR-459 - Allow destructuring-assignment after function-definition.
    +
     5503.   2005
    +
     5504.   2005            // || token_nxt.id === "["
    +
     5505.      2        ) {
    +
     5506.      2
    +
     5507.      2// test_cause:
    +
     5508.      2// ["function aa(){}\n.aa", "prefix_function", "unexpected_a", ".", 1]
    +
     5509.      2// ["function aa(){}\n?.aa", "prefix_function", "unexpected_a", "?.", 1]
    +
     5510.      2
    +
     5511.      2            warn("unexpected_a");
    +
     5512.   1990        }
    +
     5513.   1990
    +
     5514.   1990// Check functions are ordered.
    +
     5515.   1990
    +
     5516.   1990        check_ordered(
    +
     5517.   1990            "function",
    +
     5518.   1990            function_list.slice(
    +
     5519.   1990                function_list.indexOf(the_function) + 1
    +
     5520.   2515            ).map(function ({
    +
     5521.   2515                level,
    +
     5522.   2515                name
    +
     5523.   2515            }) {
    +
     5524.   1990                return (level === the_function.level + 1) && name;
    +
     5525.   2515            }).filter(function (name) {
    +
     5526.   2510                return option_dict.beta && name && name.id;
    +
     5527.   2515            })
    +
     5528.   1990        );
    +
     5529.   1990
    +
     5530.   1990// Restore the previous context.
    +
     5531.   1990
    +
     5532.   1990        functionage = function_stack.pop();
    +
     5533.   1990        return the_function;
    +
     5534.   1990    }
    +
     5535.    631
    +
     5536.   2017    function prefix_function_parameter(the_function) {
    +
     5537.   2017
    +
     5538.   2017// This function will parse input <parameters> at beginning of <the_function>
    +
     5539.   2017
    +
     5540.   2017        let optional;
    +
     5541.   2017        let parameters = [];
    +
     5542.   2017        let signature = ["("];
    +
     5543.   2017        let subparam;
    +
     5544.   2781        function param_enroll(name) {
    +
     5545.   2514            if (name.identifier) {
    +
     5546.   2514                enroll(name, "parameter", false);
    +
     5547.   2514            } else {
    +
     5548.    267
    +
     5549.    267// test_cause:
    +
     5550.    267// ["([aa])=>0", "param_enroll", "use_function_not_fart", "=>", 7]
    +
     5551.    267// ["({aa})=>0", "param_enroll", "use_function_not_fart", "=>", 7]
    +
     5552.    267
    +
     5553.    267                if (the_function.id === "=>" && !option_dict.fart) {
    +
     5554.    267                    warn("use_function_not_fart", the_function);
    +
     5555.    267                }
    +
     5556.    267
    +
     5557.    267// Recurse param_enroll().
    +
     5558.    267
    +
     5559.    267                name.names.forEach(param_enroll);
    +
     5560.    267            }
    +
     5561.   2781        }
    +
     5562.   2077        function param_parse() {
    +
     5563.   2077            let ellipsis = false;
    +
     5564.   2077            let param;
    +
     5565.    227            if (token_nxt.id === "{") {
    +
     5566.    227                if (optional !== undefined) {
    +
     5567.    227
    +
     5568.    227// test_cause:
    +
     5569.    227// ["function aa(aa=0,{}){}", "param_parse", "required_a_optional_b", "aa", 18]
    +
     5570.    227
    +
     5571.    227                    warn(
    +
     5572.    227                        "required_a_optional_b",
    +
     5573.    227                        token_nxt,
    +
     5574.    227                        token_nxt.id,
    +
     5575.    227                        optional.id
    +
     5576.    227                    );
    +
     5577.    227                }
    +
     5578.    227                param = token_nxt;
    +
     5579.    227                param.names = [];
    +
     5580.    227                advance("{");
    +
     5581.    227                signature.push("{");
    +
     5582.    625                while (true) {
    +
     5583.    625                    subparam = token_nxt;
    +
     5584.    625                    if (!subparam.identifier) {
    +
     5585.    625
    +
     5586.    625// test_cause:
    +
     5587.    625// ["function aa(aa=0,{}){}", "param_parse", "expected_identifier_a", "}", 19]
    +
     5588.    625// ["function aa({0}){}", "param_parse", "expected_identifier_a", "0", 14]
    +
     5589.    625
    +
     5590.    625                        return stop("expected_identifier_a");
    +
     5591.    625                    }
    +
     5592.    625                    survey(subparam);
    +
     5593.    625                    advance();
    +
     5594.    625                    signature.push(subparam.id);
    +
     5595.    625                    if (token_nxt.id === ":") {
    +
     5596.    625                        advance(":");
    +
     5597.    625                        advance();
    +
     5598.    625                        token_now.label = subparam;
    +
     5599.    625                        subparam = token_now;
    +
     5600.    625                        if (!subparam.identifier) {
    +
     5601.    625
    +
     5602.    625// test_cause:
    +
     5603.    625// ["function aa({aa:0}){}", "param_parse", "expected_identifier_a", "}", 18]
    +
     5604.    625
    +
     5605.    625                            return stop(
    +
     5606.    625                                "expected_identifier_a",
    +
     5607.    625                                token_nxt
    +
     5608.    625                            );
    +
     5609.    625                        }
    +
     5610.    625                    }
    +
     5611.    625
    +
     5612.    625// test_cause:
    +
     5613.    625// ["function aa({aa=aa},aa){}", "param_parse", "equal", "", 0]
    +
     5614.    625
    +
     5615.    625                    test_cause("equal");
    +
     5616.    625                    if (token_nxt.id === "=") {
    +
     5617.    625                        advance("=");
    +
     5618.    625                        subparam.expression = parse_expression();
    +
     5619.    625                        param.open = true;
    +
     5620.    625                    }
    +
     5621.    625                    param.names.push(subparam);
    +
     5622.    625                    if (token_nxt.id === ",") {
    +
     5623.    625                        advance(",");
    +
     5624.    625                        signature.push(", ");
    +
     5625.    625                    } else {
    +
     5626.    625                        break;
    +
     5627.    625                    }
    +
     5628.    625                }
    +
     5629.    227                parameters.push(param);
    +
     5630.    227
    +
     5631.    227// test_cause:
    +
     5632.    227// ["
    +
     5633.    227// function aa({bb,aa}){}
    +
     5634.    227// ", "check_ordered", "expected_a_b_before_c_d", "aa", 17]
    +
     5635.    227
    +
     5636.    227                check_ordered("parameter", param.names);
    +
     5637.    227                advance("}");
    +
     5638.    227                signature.push("}");
    +
     5639.    227                if (token_nxt.id === ",") {
    +
     5640.    227                    advance(",");
    +
     5641.    227                    signature.push(", ");
    +
     5642.    227                    param_parse();
    +
     5643.    227                    return;
    +
     5644.    227                }
    +
     5645.   1850            } else if (token_nxt.id === "[") {
    +
     5646.   1850                if (optional !== undefined) {
    +
     5647.   1850
    +
     5648.   1850// test_cause:
    +
     5649.   1850// ["function aa(aa=0,[]){}", "param_parse", "required_a_optional_b", "aa", 18]
    +
     5650.   1850
    +
     5651.   1850                    warn(
    +
     5652.   1850                        "required_a_optional_b",
    +
     5653.   1850                        token_nxt,
    +
     5654.   1850                        token_nxt.id,
    +
     5655.   1850                        optional.id
    +
     5656.   1850                    );
    +
     5657.   1850                }
    +
     5658.   1850                param = token_nxt;
    +
     5659.   1850                param.names = [];
    +
     5660.   1850                advance("[");
    +
     5661.   1850                signature.push("[]");
    +
     5662.   1850                while (true) {
    +
     5663.   1850                    subparam = token_nxt;
    +
     5664.   1850                    if (!subparam.identifier) {
    +
     5665.   1850
    +
     5666.   1850// test_cause:
    +
     5667.   1850// ["function aa(aa=0,[]){}", "param_parse", "expected_identifier_a", "]", 19]
    +
     5668.   1850
    +
     5669.   1850                        return stop("expected_identifier_a");
    +
     5670.   1850                    }
    +
     5671.   1850                    advance();
    +
     5672.   1850                    param.names.push(subparam);
    +
     5673.   1850
    +
     5674.   1850// test_cause:
    +
     5675.   1850// ["function aa([aa=aa],aa){}", "param_parse", "id", "", 0]
    +
     5676.   1850
    +
     5677.   1850                    test_cause("id");
    +
     5678.   1850                    if (token_nxt.id === "=") {
    +
     5679.   1850                        advance("=");
    +
     5680.   1850                        subparam.expression = parse_expression();
    +
     5681.   1850                        param.open = true;
    +
     5682.   1850                    }
    +
     5683.   1850                    if (token_nxt.id === ",") {
    +
     5684.   1850                        advance(",");
    +
     5685.   1850                    } else {
    +
     5686.   1850                        break;
    +
     5687.   1850                    }
    +
     5688.   1850                }
    +
     5689.   1850                parameters.push(param);
    +
     5690.   1850                advance("]");
    +
     5691.   1850                if (token_nxt.id === ",") {
    +
     5692.   1850                    advance(",");
    +
     5693.   1850                    signature.push(", ");
    +
     5694.   1850                    param_parse();
    +
     5695.   1850                    return;
    +
     5696.   1850                }
    +
     5697.   1850            } else {
    +
     5698.   1850                if (token_nxt.id === "...") {
    +
     5699.   1850                    ellipsis = true;
    +
     5700.   1850                    signature.push("...");
    +
     5701.   1850                    advance("...");
    +
     5702.   1850                    if (optional !== undefined) {
    +
     5703.   1850
    +
     5704.   1850// test_cause:
    +
     5705.   1850// ["function aa(aa=0,...){}", "param_parse", "required_a_optional_b", "aa", 21]
    +
     5706.   1850
    +
     5707.   1850                        warn(
    +
     5708.   1850                            "required_a_optional_b",
    +
     5709.   1850                            token_nxt,
    +
     5710.   1850                            token_nxt.id,
    +
     5711.   1850                            optional.id
    +
     5712.   1850                        );
    +
     5713.   1850                    }
    +
     5714.   1850                }
    +
     5715.   1850                if (!token_nxt.identifier) {
    +
     5716.   1850
    +
     5717.   1850// test_cause:
    +
     5718.   1850// ["function aa(0){}", "param_parse", "expected_identifier_a", "0", 13]
    +
     5719.   1850
    +
     5720.   1850                    return stop("expected_identifier_a");
    +
     5721.   1850                }
    +
     5722.   1850                param = token_nxt;
    +
     5723.   1850                parameters.push(param);
    +
     5724.   1850                advance();
    +
     5725.   1850                signature.push(param.id);
    +
     5726.   1850                if (ellipsis) {
    +
     5727.   1850                    param.ellipsis = true;
    +
     5728.   1850                } else {
    +
     5729.   1850                    if (token_nxt.id === "=") {
    +
     5730.   1850                        optional = param;
    +
     5731.   1850                        advance("=");
    +
     5732.   1850                        param.expression = parse_expression(0);
    +
     5733.   1850                    } else {
    +
     5734.   1850                        if (optional !== undefined) {
    +
     5735.   1850
    +
     5736.   1850// test_cause:
    +
     5737.   1850// ["function aa(aa=0,bb){}", "param_parse", "required_a_optional_b", "aa", 18]
    +
     5738.   1850
    +
     5739.   1850                            warn(
    +
     5740.   1850                                "required_a_optional_b",
    +
     5741.   1850                                param,
    +
     5742.   1850                                param.id,
    +
     5743.   1850                                optional.id
    +
     5744.   1850                            );
    +
     5745.   1850                        }
    +
     5746.   1850                    }
    +
     5747.   1850                    if (token_nxt.id === ",") {
    +
     5748.   1850                        advance(",");
    +
     5749.   1850                        signature.push(", ");
    +
     5750.   1850                        param_parse();
    +
     5751.   1850                        return;
    +
     5752.   1850                    }
    +
     5753.   1850                }
    +
     5754.   1850            }
    +
     5755.   2077        }
    +
     5756.   2017
    +
     5757.   2017// test_cause:
    +
     5758.   2017// ["function aa(){}", "prefix_function_parameter", "opener", "(", 0]
    +
     5759.   2017
    +
     5760.   2017        test_cause("opener", token_now.id);
    +
     5761.   2017        token_now.free = false;
    +
     5762.   1485        if (token_nxt.id !== ")" && token_nxt.id !== "(end)") {
    +
     5763.   1485            param_parse();
    +
     5764.   2009        }
    +
     5765.   2009        advance(")");
    +
     5766.   2009        signature.push(")");
    +
     5767.   2009        parameters.forEach(param_enroll);
    +
     5768.   2009        the_function.parameters = parameters;
    +
     5769.   2009        the_function.signature = signature.join("");
    +
     5770.   2009    }
    +
     5771.    631
    +
     5772.    588    function prefix_lbrace() {
    +
     5773.    588        const seen = empty();
    +
     5774.    588        const the_brace = token_now;
    +
     5775.    588        let extra;
    +
     5776.    588        let full;
    +
     5777.    588        let id;
    +
     5778.    588        let name;
    +
     5779.    588        let the_colon;
    +
     5780.    588        let value;
    +
     5781.    588        the_brace.expression = [];
    +
     5782.    548        if (token_nxt.id !== "}") {
    +
     5783.    548
    +
     5784.    548// Parse/loop through each property in {...}.
    +
     5785.    548
    +
     5786.   1996            while (true) {
    +
     5787.   1996                name = token_nxt;
    +
     5788.   1996                advance();
    +
     5789.   1996                if (
    +
     5790.   1996                    (name.id === "get" || name.id === "set")
    +
     5791.   1996                    && token_nxt.identifier
    +
     5792.   1996                ) {
    +
     5793.   1996                    if (!option_dict.getset) {
    +
     5794.   1996
    +
     5795.   1996// test_cause:
    +
     5796.   1996// ["aa={get aa(){}}", "prefix_lbrace", "unexpected_a", "get", 5]
    +
     5797.   1996
    +
     5798.   1996                        warn("unexpected_a", name);
    +
     5799.   1996                    }
    +
     5800.   1996                    extra = name.id;
    +
     5801.   1996                    full = extra + " " + token_nxt.id;
    +
     5802.   1996                    name = token_nxt;
    +
     5803.   1996                    advance();
    +
     5804.   1996                    id = survey(name);
    +
     5805.   1996                    if (seen[full] === true || seen[id] === true) {
    +
     5806.   1996
    +
     5807.   1996// test_cause:
    +
     5808.   1996// ["aa={get aa(){},get aa(){}}", "prefix_lbrace", "duplicate_a", "aa", 20]
    +
     5809.   1996
    +
     5810.   1996                        warn("duplicate_a", name);
    +
     5811.   1996                    }
    +
     5812.   1996                    seen[id] = false;
    +
     5813.   1996                    seen[full] = true;
    +
     5814.   1996                } else if (name.id === "`") {
    +
     5815.   1996
    +
     5816.   1996// test_cause:
    +
     5817.   1996// ["aa={`aa`:0}", "prefix_lbrace", "unexpected_a", "`", 5]
    +
     5818.   1996
    +
     5819.   1996                    stop("unexpected_a", name);
    +
     5820.   1996
    +
     5821.   1996                } else {
    +
     5822.   1996                    id = survey(name);
    +
     5823.   1996                    if (typeof seen[id] === "boolean") {
    +
     5824.   1996
    +
     5825.   1996// test_cause:
    +
     5826.   1996// ["aa={aa,aa}", "prefix_lbrace", "duplicate_a", "aa", 8]
    +
     5827.   1996
    +
     5828.   1996                        warn("duplicate_a", name);
    +
     5829.   1996                    }
    +
     5830.   1996                    seen[id] = true;
    +
     5831.   1996                }
    +
     5832.   1996                if (name.identifier) {
    +
     5833.   1996                    if (token_nxt.id === "}" || token_nxt.id === ",") {
    +
     5834.   1996                        if (typeof extra === "string") {
    +
     5835.   1996
    +
     5836.   1996// test_cause:
    +
     5837.   1996// ["aa={get aa}", "prefix_lbrace", "closer", "", 0]
    +
     5838.   1996
    +
     5839.   1996                            test_cause("closer");
    +
     5840.   1996                            advance("(");
    +
     5841.   1996                        }
    +
     5842.   1996                        value = parse_expression(Infinity, true);
    +
     5843.   1996                    } else if (token_nxt.id === "(") {
    +
     5844.   1996
    +
     5845.   1996// test_cause:
    +
     5846.   1996// ["aa={aa()}", "prefix_lbrace", "paren", "", 0]
    +
     5847.   1996// ["aa={get aa(){}}", "prefix_lbrace", "paren", "", 0]
    +
     5848.   1996
    +
     5849.   1996                        test_cause("paren");
    +
     5850.   1996                        value = prefix_function({
    +
     5851.   1996                            arity: "unary",
    +
     5852.   1996                            from: name.from,
    +
     5853.   1996                            id: "function",
    +
     5854.   1996                            line: name.line,
    +
     5855.   1996                            name: (
    +
     5856.   1996                                typeof extra === "string"
    +
     5857.   1996                                ? extra
    +
     5858.   1996                                : id
    +
     5859.   1996                            ),
    +
     5860.   1996                            thru: name.from
    +
     5861.   1996                        });
    +
     5862.   1996                    } else {
    +
     5863.   1996                        if (typeof extra === "string") {
    +
     5864.   1996
    +
     5865.   1996// test_cause:
    +
     5866.   1996// ["aa={get aa.aa}", "prefix_lbrace", "paren", "", 0]
    +
     5867.   1996
    +
     5868.   1996                            test_cause("paren");
    +
     5869.   1996                            advance("(");
    +
     5870.   1996                        }
    +
     5871.   1996                        the_colon = token_nxt;
    +
     5872.   1996                        advance(":");
    +
     5873.   1996                        value = parse_expression(0);
    +
     5874.   1996                        if (
    +
     5875.   1996                            value.id === name.id
    +
     5876.   1996                            && value.id !== "function"
    +
     5877.   1996                        ) {
    +
     5878.   1996
    +
     5879.   1996// test_cause:
    +
     5880.   1996// ["aa={aa:aa}", "prefix_lbrace", "unexpected_a", ": aa", 7]
    +
     5881.   1996
    +
     5882.   1996                            warn("unexpected_a", the_colon, ": " + name.id);
    +
     5883.   1996                        }
    +
     5884.   1996                    }
    +
     5885.   1996                    value.label = name;
    +
     5886.   1996                    if (typeof extra === "string") {
    +
     5887.   1996                        value.extra = extra;
    +
     5888.   1996                    }
    +
     5889.   1996                    the_brace.expression.push(value);
    +
     5890.   1996                } else {
    +
     5891.   1996
    +
     5892.   1996// test_cause:
    +
     5893.   1996// ["aa={\"aa\":0}", "prefix_lbrace", "colon", "", 0]
    +
     5894.   1996
    +
     5895.   1996                    test_cause("colon");
    +
     5896.   1996                    advance(":");
    +
     5897.   1996                    value = parse_expression(0);
    +
     5898.   1996                    value.label = name;
    +
     5899.   1996                    the_brace.expression.push(value);
    +
     5900.   1996                }
    +
     5901.   1996                if (token_nxt.id !== ",") {
    +
     5902.   1996                    break;
    +
     5903.   1996                }
    +
     5904.   1996
    +
     5905.   1996// test_cause:
    +
     5906.   1996// ["aa={\"aa\":0,\"bb\":0}", "prefix_lbrace", "comma", "", 0]
    +
     5907.   1996
    +
     5908.   1996                test_cause("comma");
    +
     5909.   1996                advance(",");
    +
     5910.   1996                if (token_nxt.id === "}") {
    +
     5911.   1996
    +
     5912.   1996// test_cause:
    +
     5913.   1996// ["let aa={aa:0,}", "prefix_lbrace", "unexpected_a", ",", 13]
    +
     5914.   1996
    +
     5915.   1996                    warn("unexpected_a", token_now);
    +
     5916.   1996                    break;
    +
     5917.   1996                }
    +
     5918.   1996            }
    +
     5919.    583        }
    +
     5920.    583
    +
     5921.    583// test_cause:
    +
     5922.    583// ["aa={bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8]
    +
     5923.    583
    +
     5924.    583        check_ordered(
    +
     5925.    583            "property",
    +
     5926.   1991            the_brace.expression.map(function ({
    +
     5927.   1991                label
    +
     5928.   1991            }) {
    +
     5929.   1991                return label;
    +
     5930.   1991            })
    +
     5931.    583        );
    +
     5932.    583        advance("}");
    +
     5933.    583        return the_brace;
    +
     5934.    583    }
    +
     5935.    631
    +
     5936.    759    function prefix_lbracket() {
    +
     5937.    759        const the_token = token_now;
    +
     5938.    759        let element;
    +
     5939.    759        let ellipsis;
    +
     5940.    759        the_token.expression = [];
    +
     5941.    392        if (token_nxt.id !== "]") {
    +
     5942.    392
    +
     5943.    392// Parse/loop through each element in [...].
    +
     5944.    392
    +
     5945.   1884            while (true) {
    +
     5946.   1884                ellipsis = false;
    +
     5947.   1884                if (token_nxt.id === "...") {
    +
     5948.   1884                    ellipsis = true;
    +
     5949.   1884                    advance("...");
    +
     5950.   1884                }
    +
     5951.   1884                element = parse_expression(10);
    +
     5952.   1884                if (ellipsis) {
    +
     5953.   1884                    element.ellipsis = true;
    +
     5954.   1884                }
    +
     5955.   1884                the_token.expression.push(element);
    +
     5956.   1884                if (token_nxt.id !== ",") {
    +
     5957.   1884                    break;
    +
     5958.   1884                }
    +
     5959.   1884                advance(",");
    +
     5960.   1884                if (token_nxt.id === "]") {
    +
     5961.   1884
    +
     5962.   1884// test_cause:
    +
     5963.   1884// ["let aa=[0,]", "prefix_lbracket", "unexpected_a", ",", 10]
    +
     5964.   1884
    +
     5965.   1884                    warn("unexpected_a", token_now);
    +
     5966.   1884                    break;
    +
     5967.   1884                }
    +
     5968.   1884            }
    +
     5969.    392        }
    +
     5970.    759        advance("]");
    +
     5971.    759        return the_token;
    +
     5972.    759    }
    +
     5973.    631
    +
     5974.   1616    function prefix_lparen() {
    +
     5975.   1616        let the_paren = token_now;
    +
     5976.   1616        let the_value;
    +
     5977.   1616
    +
     5978.   1616// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart.
    +
     5979.   1616
    +
     5980.     14        if (token_now.fart) {
    +
     5981.     14            return parse_fart(token_now.fart);
    +
     5982.   1602        }
    +
     5983.   1602
    +
     5984.   1602// test_cause:
    +
     5985.   1602// ["(0)", "prefix_lparen", "expr", "", 0]
    +
     5986.   1602
    +
     5987.   1602        test_cause("expr");
    +
     5988.   1602        the_paren.free = true;
    +
     5989.   1602        the_value = parse_expression(0);
    +
     5990.   1602        if (the_value.wrapped === true) {
    +
     5991.      1
    +
     5992.      1// test_cause:
    +
     5993.      1// ["((0))", "prefix_lparen", "unexpected_a", "(", 1]
    +
     5994.      1
    +
     5995.      1            warn("unexpected_a", the_paren);
    +
     5996.   1602        }
    +
     5997.   1602        the_value.wrapped = true;
    +
     5998.   1602        advance(")", the_paren);
    +
     5999.   1602        return the_value;
    +
     6000.   1602    }
    +
     6001.    631
    +
     6002.    155    function prefix_new() {
    +
     6003.    155        const the_new = token_now;
    +
     6004.    155        let right;
    +
     6005.    155        right = parse_expression(160);
    +
     6006.      1        if (token_nxt.id !== "(") {
    +
     6007.      1
    +
     6008.      1// test_cause:
    +
     6009.      1// ["new aa", "prefix_new", "expected_a_before_b", "(end)", 1]
    +
     6010.      1
    +
     6011.      1            warn("expected_a_before_b", token_nxt, "()", artifact());
    +
     6012.      1        }
    +
     6013.    155        the_new.expression = right;
    +
     6014.    155        return the_new;
    +
     6015.    155    }
    +
     6016.    631
    +
     6017.    782    function prefix_tick() {
    +
     6018.    782        const the_tick = token_now;
    +
     6019.    782        the_tick.value = [];
    +
     6020.    782        the_tick.expression = [];
    +
     6021.    782        if (token_nxt.id !== "`") {
    +
     6022.    782
    +
     6023.    782// Parse/loop through each token in `${...}`.
    +
     6024.    782
    +
     6025.   1499            while (true) {
    +
     6026.   1499                advance("(string)");
    +
     6027.   1499                the_tick.value.push(token_now);
    +
     6028.   1499                if (token_nxt.id !== "${") {
    +
     6029.   1499                    break;
    +
     6030.   1499                }
    +
     6031.   1499                advance("${");
    +
     6032.   1499
    +
     6033.   1499// test_cause:
    +
     6034.   1499// ["let aa=`${}`;", "prefix_tick", "${", "", 0]
    +
     6035.   1499
    +
     6036.   1499                test_cause("${");
    +
     6037.   1499                the_tick.expression.push(parse_expression(0));
    +
     6038.   1499                advance("}");
    +
     6039.   1499            }
    +
     6040.    780        }
    +
     6041.    780        advance("`");
    +
     6042.    780        return the_tick;
    +
     6043.    780    }
    +
     6044.    631
    +
     6045.      2    function prefix_void() {
    +
     6046.      2        const the_void = token_now;
    +
     6047.      2
    +
     6048.      2// test_cause:
    +
     6049.      2// ["void 0", "prefix_void", "unexpected_a", "void", 1]
    +
     6050.      2// ["void", "prefix_void", "unexpected_a", "void", 1]
    +
     6051.      2
    +
     6052.      2        warn("unexpected_a", the_void);
    +
     6053.      2        the_void.expression = parse_expression(0);
    +
     6054.      2        return the_void;
    +
     6055.      2    }
    +
     6056.    631
    +
     6057.  13430    function semicolon() {
    +
     6058.  13430
    +
     6059.  13430// Try to match a semicolon.
    +
     6060.  13430
    +
     6061.  13205        if (token_nxt.id === ";") {
    +
     6062.  13205            advance(";");
    +
     6063.  13205        } else {
    +
     6064.    225
    +
     6065.    225// test_cause:
    +
     6066.    225// ["0", "semicolon", "expected_a_b", "(end)", 1]
    +
     6067.    225
    +
     6068.    225            warn_at(
    +
     6069.    225                "expected_a_b",
    +
     6070.    225                token_now.line,
    +
     6071.    225                token_now.thru + 1,
    +
     6072.    225                ";",
    +
     6073.    225                artifact()
    +
     6074.    225            );
    +
     6075.    225        }
    +
     6076.  13430        anon = "anonymous";
    +
     6077.  13430    }
    +
     6078.    631
    +
     6079.  14513    function stmt(id, fud_stmt) {
    +
     6080.  14513
    +
     6081.  14513// Create a statement.
    +
     6082.  14513
    +
     6083.  14513        const the_symbol = symbol(id);
    +
     6084.  14513        the_symbol.fud_stmt = fud_stmt;
    +
     6085.  14513        return the_symbol;
    +
     6086.  14513    }
    +
     6087.    631
    +
     6088.   1023    function stmt_break() {
    +
     6089.   1023        const the_break = token_now;
    +
     6090.   1023        let the_label;
    +
     6091.   1023        if (
    +
     6092.    719            (functionage.loop < 1 && functionage.switch < 1)
    +
     6093.   1017            || functionage.finally > 0
    +
     6094.      6        ) {
    +
     6095.      6
    +
     6096.      6// test_cause:
    +
     6097.      6// ["break", "stmt_break", "unexpected_a", "break", 1]
    +
     6098.      6
    +
     6099.      6            warn("unexpected_a", the_break);
    +
     6100.      6        }
    +
     6101.   1023        the_break.disrupt = true;
    +
     6102.      5        if (token_nxt.identifier && token_now.line === token_nxt.line) {
    +
     6103.      5            the_label = functionage.context[token_nxt.id];
    +
     6104.      5            if (
    +
     6105.      5                the_label === undefined
    +
     6106.      5                || the_label.role !== "label"
    +
     6107.      5                || the_label.dead
    +
     6108.      5            ) {
    +
     6109.      5                if (the_label !== undefined && the_label.dead) {
    +
     6110.      5
    +
     6111.      5// test_cause:
    +
     6112.      5// ["aa:{function aa(aa){break aa;}}", "stmt_break", "out_of_scope_a", "aa", 27]
    +
     6113.      5
    +
     6114.      5                    warn("out_of_scope_a");
    +
     6115.      5                } else {
    +
     6116.      5
    +
     6117.      5// test_cause:
    +
     6118.      5// ["aa:{break aa;}", "stmt_break", "not_label_a", "aa", 11]
    +
     6119.      5
    +
     6120.      5                    warn("not_label_a");
    +
     6121.      5                }
    +
     6122.      5            } else {
    +
     6123.      5                the_label.used += 1;
    +
     6124.      5            }
    +
     6125.      5            the_break.label = token_nxt;
    +
     6126.      5            advance();
    +
     6127.      5        }
    +
     6128.   1023        advance(";");
    +
     6129.   1023        return the_break;
    +
     6130.   1023    }
    +
     6131.    631
    +
     6132.      2    function stmt_continue() {
    +
     6133.      2        const the_continue = token_now;
    +
     6134.      1        if (functionage.loop < 1 || functionage.finally > 0) {
    +
     6135.      2
    +
     6136.      2// test_cause:
    +
     6137.      2// ["continue", "stmt_continue", "unexpected_a", "continue", 1]
    +
     6138.      2// ["
    +
     6139.      2// function aa(){while(0){try{}finally{continue}}}
    +
     6140.      2// ", "stmt_continue", "unexpected_a", "continue", 37]
    +
     6141.      2
    +
     6142.      2            warn("unexpected_a", the_continue);
    +
     6143.      2        }
    +
     6144.      2        check_not_top_level(the_continue);
    +
     6145.      2        the_continue.disrupt = true;
    +
     6146.      2        warn("unexpected_a", the_continue);
    +
     6147.      2        advance(";");
    +
     6148.      2        return the_continue;
    +
     6149.      2    }
    +
     6150.    631
    +
     6151.      3    function stmt_debugger() {
    +
     6152.      3        const the_debug = token_now;
    +
     6153.      1        if (!option_dict.devel) {
    +
     6154.      1
    +
     6155.      1// test_cause:
    +
     6156.      1// ["debugger", "stmt_debugger", "unexpected_a", "debugger", 1]
    +
     6157.      1
    +
     6158.      1            warn("unexpected_a", the_debug);
    +
     6159.      1        }
    +
     6160.      3        semicolon();
    +
     6161.      3        return the_debug;
    +
     6162.      3    }
    +
     6163.    631
    +
     6164.     72    function stmt_delete() {
    +
     6165.     72        const the_token = token_now;
    +
     6166.     72        const the_value = parse_expression(0);
    +
     6167.     72        if (
    +
     6168.      1            (the_value.id !== "." && the_value.id !== "[")
    +
     6169.     71            || the_value.arity !== "binary"
    +
     6170.      1        ) {
    +
     6171.      1
    +
     6172.      1// test_cause:
    +
     6173.      1// ["delete 0", "stmt_delete", "expected_a_b", "0", 8]
    +
     6174.      1
    +
     6175.      1            stop("expected_a_b", the_value, ".", artifact(the_value));
    +
     6176.     71        }
    +
     6177.     71        the_token.expression = the_value;
    +
     6178.     71        semicolon();
    +
     6179.     71        return the_token;
    +
     6180.     71    }
    +
     6181.    631
    +
     6182.      5    function stmt_do() {
    +
     6183.      5        const the_do = token_now;
    +
     6184.      5        check_not_top_level(the_do);
    +
     6185.      5        functionage.loop += 1;
    +
     6186.      5        the_do.block = block();
    +
     6187.      5        advance("while");
    +
     6188.      5        the_do.expression = condition();
    +
     6189.      5        semicolon();
    +
     6190.      1        if (the_do.block.disrupt === true) {
    +
     6191.      1
    +
     6192.      1// test_cause:
    +
     6193.      1// ["function aa(){do{break;}while(0)}", "stmt_do", "weird_loop", "do", 15]
    +
     6194.      1
    +
     6195.      1            warn("weird_loop", the_do);
    +
     6196.      3        }
    +
     6197.      3        functionage.loop -= 1;
    +
     6198.      3        return the_do;
    +
     6199.      3    }
    +
     6200.    631
    +
     6201.     24    function stmt_export() {
    +
     6202.     24        let export_list = [];
    +
     6203.     24        let the_export = token_now;
    +
     6204.     24        let the_id;
    +
     6205.     24        let the_name;
    +
     6206.     24        let the_thing;
    +
     6207.     24
    +
     6208.     24        the_export.expression = [];
    +
     6209.     11        if (token_nxt.id === "default") {
    +
     6210.     11            if (export_dict.default !== undefined) {
    +
     6211.     11
    +
     6212.     11// test_cause:
    +
     6213.     11// ["
    +
     6214.     11// export default 0;export default 0
    +
     6215.     11// ", "stmt_export", "duplicate_a", "default", 25]
    +
     6216.     11
    +
     6217.     11                warn("duplicate_a");
    +
     6218.     11            }
    +
     6219.     11            advance("default");
    +
     6220.     11            the_thing = parse_expression(0);
    +
     6221.     11            if (
    +
     6222.     11                the_thing.id !== "("
    +
     6223.     11                || the_thing.expression[0].id !== "."
    +
     6224.     11                || the_thing.expression[0].expression.id !== "Object"
    +
     6225.     11                || the_thing.expression[0].name.id !== "freeze"
    +
     6226.     11            ) {
    +
     6227.     11
    +
     6228.     11// test_cause:
    +
     6229.     11// ["export default {}", "stmt_export", "freeze_exports", "{", 16]
    +
     6230.     11
    +
     6231.     11                warn("freeze_exports", the_thing);
    +
     6232.     11
    +
     6233.     11// PR-301 - Bugfix - Fixes issues #282 - optional-semicolon.
    +
     6234.     11
    +
     6235.     11            } else {
    +
     6236.     11
    +
     6237.     11// test_cause:
    +
     6238.     11// ["
    +
     6239.     11// export default Object.freeze({})
    +
     6240.     11// ", "semicolon", "expected_a_b", "(end)", 32]
    +
     6241.     11
    +
     6242.     11                semicolon();
    +
     6243.     11            }
    +
     6244.     11            export_dict.default = the_thing;
    +
     6245.     11            the_export.expression.push(the_thing);
    +
     6246.     13        } else {
    +
     6247.     13
    +
     6248.     13// PR-439 - Add grammar for "export async function ...".
    +
     6249.     13
    +
     6250.     13            if (token_nxt.id === "function" || token_nxt.id === "async") {
    +
     6251.     13
    +
     6252.     13// test_cause:
    +
     6253.     13// ["export async function aa(){}", "stmt_export", "freeze_exports", "async", 8]
    +
     6254.     13// ["export function aa(){}", "stmt_export", "freeze_exports", "function", 8]
    +
     6255.     13
    +
     6256.     13                warn("freeze_exports");
    +
     6257.     13                the_thing = parse_statement();
    +
     6258.     13                the_name = the_thing.name;
    +
     6259.     13                the_id = the_name.id;
    +
     6260.     13                the_name.used += 1;
    +
     6261.     13                if (export_dict[the_id] !== undefined) {
    +
     6262.     13
    +
     6263.     13// test_cause:
    +
     6264.     13// ["
    +
     6265.     13// let aa;export{aa};export function aa(){}
    +
     6266.     13// ", "stmt_export", "duplicate_a", "aa", 35]
    +
     6267.     13
    +
     6268.     13                    warn("duplicate_a", the_name);
    +
     6269.     13                }
    +
     6270.     13                export_dict[the_id] = the_thing;
    +
     6271.     13                the_export.expression.push(the_thing);
    +
     6272.     13                the_thing.statement = false;
    +
     6273.     13                the_thing.arity = "unary";
    +
     6274.     13            } else if (
    +
     6275.     13                token_nxt.id === "var"
    +
     6276.     13                || token_nxt.id === "let"
    +
     6277.     13                || token_nxt.id === "const"
    +
     6278.     13            ) {
    +
     6279.     13
    +
     6280.     13// test_cause:
    +
     6281.     13// ["export const", "stmt_export", "unexpected_a", "const", 8]
    +
     6282.     13// ["export let", "stmt_export", "unexpected_a", "let", 8]
    +
     6283.     13// ["export var", "stmt_export", "unexpected_a", "var", 8]
    +
     6284.     13
    +
     6285.     13                warn("unexpected_a");
    +
     6286.     13                parse_statement();
    +
     6287.     13            } else if (token_nxt.id === "{") {
    +
     6288.     13
    +
     6289.     13// test_cause:
    +
     6290.     13// ["export {}", "stmt_export", "advance{", "", 0]
    +
     6291.     13
    +
     6292.     13                test_cause("advance{");
    +
     6293.     13                advance("{");
    +
     6294.     13                while (true) {
    +
     6295.     13                    if (!token_nxt.identifier) {
    +
     6296.     13
    +
     6297.     13// test_cause:
    +
     6298.     13// ["export {}", "stmt_export", "expected_identifier_a", "}", 9]
    +
     6299.     13
    +
     6300.     13                        stop("expected_identifier_a");
    +
     6301.     13                    }
    +
     6302.     13                    the_id = token_nxt.id;
    +
     6303.     13                    export_list.push(token_nxt);
    +
     6304.     13                    the_name = token_global.context[the_id];
    +
     6305.     13                    if (the_name === undefined) {
    +
     6306.     13
    +
     6307.     13// test_cause:
    +
     6308.     13// ["export {aa}", "stmt_export", "unexpected_a", "aa", 9]
    +
     6309.     13
    +
     6310.     13                        warn("unexpected_a");
    +
     6311.     13                    } else {
    +
     6312.     13                        the_name.used += 1;
    +
     6313.     13                        if (export_dict[the_id] !== undefined) {
    +
     6314.     13
    +
     6315.     13// test_cause:
    +
     6316.     13// ["let aa;export{aa,aa}", "stmt_export", "duplicate_a", "aa", 18]
    +
     6317.     13
    +
     6318.     13                            warn("duplicate_a");
    +
     6319.     13                        }
    +
     6320.     13                        export_dict[the_id] = the_name;
    +
     6321.     13                    }
    +
     6322.     13                    advance();
    +
     6323.     13                    the_export.expression.push(the_thing);
    +
     6324.     13                    if (token_nxt.id === ",") {
    +
     6325.     13                        advance(",");
    +
     6326.     13                    } else {
    +
     6327.     13                        break;
    +
     6328.     13                    }
    +
     6329.     13                }
    +
     6330.     13
    +
     6331.     13// PR-439 - Check exported properties are ordered.
    +
     6332.     13
    +
     6333.     13// test_cause:
    +
     6334.     13// ["export {bb, aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 13]
    +
     6335.     13
    +
     6336.     13                check_ordered("export", export_list);
    +
     6337.     13                advance("}");
    +
     6338.     13                semicolon();
    +
     6339.     13            } else {
    +
     6340.     13
    +
     6341.     13// test_cause:
    +
     6342.     13// ["export", "stmt_export", "unexpected_a", "(end)", 1]
    +
     6343.     13
    +
     6344.     13                stop("unexpected_a");
    +
     6345.     13            }
    +
     6346.     18        }
    +
     6347.     18        state.mode_module = true;
    +
     6348.     18        return the_export;
    +
     6349.     18    }
    +
     6350.    631
    +
     6351.     12    function stmt_for() {
    +
     6352.     12        let first;
    +
     6353.     12        let the_for = token_now;
    +
     6354.      7        if (!option_dict.for) {
    +
     6355.      7
    +
     6356.      7// test_cause:
    +
     6357.      7// ["for", "stmt_for", "unexpected_a", "for", 1]
    +
     6358.      7
    +
     6359.      7            warn("unexpected_a", the_for);
    +
     6360.      7        }
    +
     6361.     12        check_not_top_level(the_for);
    +
     6362.     12        functionage.loop += 1;
    +
     6363.     12        advance("(");
    +
     6364.     12        token_now.free = true;
    +
     6365.      1        if (token_nxt.id === ";") {
    +
     6366.      1
    +
     6367.      1// test_cause:
    +
     6368.      1// ["for(;;){}", "stmt_for", "expected_a_b", "for (;", 1]
    +
     6369.      1
    +
     6370.      1            return stop("expected_a_b", the_for, "while (", "for (;");
    +
     6371.      9        }
    +
     6372.      9        switch (token_nxt.id) {
    +
     6373.      9        case "const":
    +
     6374.      1        case "let":
    +
     6375.      1        case "var":
    +
     6376.      1
    +
     6377.      1// test_cause:
    +
     6378.      1// ["for(const aa in aa){}", "stmt_for", "unexpected_a", "const", 5]
    +
     6379.      1
    +
     6380.      1            return stop("unexpected_a");
    +
     6381.      8        }
    +
     6382.      8        first = parse_expression(0);
    +
     6383.      8        if (first.id === "in") {
    +
     6384.      2            if (first.expression[0].arity !== "variable") {
    +
     6385.      2
    +
     6386.      2// test_cause:
    +
     6387.      2// ["for(0 in aa){}", "stmt_for", "bad_assignment_a", "0", 5]
    +
     6388.      2
    +
     6389.      2                warn("bad_assignment_a", first.expression[0]);
    +
     6390.      2            }
    +
     6391.      2            the_for.name = first.expression[0];
    +
     6392.      2            the_for.expression = first.expression[1];
    +
     6393.      2            warn("expected_a_b", the_for, "Object.keys", "for in");
    +
     6394.      6        } else {
    +
     6395.      6            the_for.initial = first;
    +
     6396.      6            advance(";");
    +
     6397.      6            the_for.expression = parse_expression(0);
    +
     6398.      6            advance(";");
    +
     6399.      6            the_for.inc = parse_expression(0);
    +
     6400.      6            if (the_for.inc.id === "++") {
    +
     6401.      6
    +
     6402.      6// test_cause:
    +
     6403.      6// ["for(aa;aa;aa++){}", "stmt_for", "expected_a_b", "++", 13]
    +
     6404.      6
    +
     6405.      6                warn("expected_a_b", the_for.inc, "+= 1", "++");
    +
     6406.      6            }
    +
     6407.      8        }
    +
     6408.      8        advance(")");
    +
     6409.      8        the_for.block = block();
    +
     6410.      8        if (the_for.block.disrupt === true) {
    +
     6411.      1
    +
     6412.      1// test_cause:
    +
     6413.      1// ["
    +
     6414.      1// /*jslint for*/
    +
     6415.      1// function aa(bb,cc){for(0;0;0){break;}}
    +
     6416.      1// ", "stmt_for", "weird_loop", "for", 20]
    +
     6417.      1
    +
     6418.      1            warn("weird_loop", the_for);
    +
     6419.      8        }
    +
     6420.      8        functionage.loop -= 1;
    +
     6421.      8        return the_for;
    +
     6422.      8    }
    +
     6423.    631
    +
     6424.   3051    function stmt_if() {
    +
     6425.   3051        const the_if = token_now;
    +
     6426.   3051        let the_else;
    +
     6427.   3051        the_if.expression = condition();
    +
     6428.   3051        the_if.block = block();
    +
     6429.    642        if (token_nxt.id === "else") {
    +
     6430.    642            advance("else");
    +
     6431.    642            the_else = token_now;
    +
     6432.    642            the_if.else = (
    +
     6433.    642                token_nxt.id === "if"
    +
     6434.    642                ? parse_statement()
    +
     6435.    642                : block()
    +
     6436.    642            );
    +
     6437.    642
    +
     6438.    642// test_cause:
    +
     6439.    642// ["if(0){0}else if(0){0}", "stmt_if", "else", "", 0]
    +
     6440.    642// ["if(0){0}else{0}", "stmt_if", "else", "", 0]
    +
     6441.    642
    +
     6442.    642            test_cause("else");
    +
     6443.    642            if (the_if.block.disrupt === true) {
    +
     6444.    642                if (the_if.else.disrupt === true) {
    +
     6445.    642
    +
     6446.    642// test_cause:
    +
     6447.    642// ["if(0){break;}else{break;}", "stmt_if", "disrupt", "", 0]
    +
     6448.    642
    +
     6449.    642                    test_cause("disrupt");
    +
     6450.    642                    the_if.disrupt = true;
    +
     6451.    642                } else {
    +
     6452.    642
    +
     6453.    642// test_cause:
    +
     6454.    642// ["if(0){break;}else{}", "stmt_if", "unexpected_a", "else", 14]
    +
     6455.    642
    +
     6456.    642                    warn("unexpected_a", the_else);
    +
     6457.    642                }
    +
     6458.    642            }
    +
     6459.   3050        }
    +
     6460.   3050        return the_if;
    +
     6461.   3050    }
    +
     6462.    631
    +
     6463.     62    function stmt_import() {
    +
     6464.     62        const the_import = token_now;
    +
     6465.     62        let name;
    +
     6466.     62        let names;
    +
     6467.     62
    +
     6468.     62// PR-347 - Disable warning "unexpected_directive_a".
    +
     6469.     62//
    +
     6470.     62//         if (typeof state.mode_module === "object") {
    +
     6471.     62//
    +
     6472.     62// // test_cause:
    +
     6473.     62// // ["
    +
     6474.     62// // /*global aa*/
    +
     6475.     62// // import aa from "aa"
    +
     6476.     62// // ", "stmt_import", "unexpected_directive_a", "global", 1]
    +
     6477.     62//
    +
     6478.     62//             warn(
    +
     6479.     62//                 "unexpected_directive_a",
    +
     6480.     62//                 state.mode_module,
    +
     6481.     62//                 state.mode_module.directive
    +
     6482.     62//             );
    +
     6483.     62//         }
    +
     6484.     62
    +
     6485.     62        state.mode_module = true;
    +
     6486.     62
    +
     6487.     62// PR-436 - Add grammar for side-effect import-statement.
    +
     6488.     62
    +
     6489.      1        if (token_nxt.id === "(string)") {
    +
     6490.      1
    +
     6491.      1// test_cause:
    +
     6492.      1// ["import \"./aa.mjs\";", "stmt_import", "import_side_effect", "", 0]
    +
     6493.      1
    +
     6494.      1            test_cause("import_side_effect");
    +
     6495.      1            warn("expected_a_b", token_nxt, "{", artifact());
    +
     6496.      1            advance();
    +
     6497.      1            semicolon();
    +
     6498.      1            return the_import;
    +
     6499.     61        }
    +
     6500.     61        if (token_nxt.identifier) {
    +
     6501.     57            name = token_nxt;
    +
     6502.     57            advance();
    +
     6503.     57            if (name.id === "ignore") {
    +
     6504.     57
    +
     6505.     57// test_cause:
    +
     6506.     57// ["import ignore from \"aa\"", "stmt_import", "unexpected_a", "ignore", 8]
    +
     6507.     57
    +
     6508.     57                warn("unexpected_a", name);
    +
     6509.     57            }
    +
     6510.     57            enroll(name, "variable", true);
    +
     6511.     57            the_import.name = name;
    +
     6512.     57        } else {
    +
     6513.      4            names = [];
    +
     6514.      4            advance("{");
    +
     6515.      4            if (token_nxt.id !== "}") {
    +
     6516.      4                while (true) {
    +
     6517.      4                    if (!token_nxt.identifier) {
    +
     6518.      4
    +
     6519.      4// test_cause:
    +
     6520.      4// ["import {", "stmt_import", "expected_identifier_a", "(end)", 1]
    +
     6521.      4
    +
     6522.      4                        stop("expected_identifier_a");
    +
     6523.      4                    }
    +
     6524.      4                    name = token_nxt;
    +
     6525.      4                    advance();
    +
     6526.      4                    if (name.id === "ignore") {
    +
     6527.      4
    +
     6528.      4// test_cause:
    +
     6529.      4// ["import {ignore} from \"aa\"", "stmt_import", "unexpected_a", "ignore", 9]
    +
     6530.      4
    +
     6531.      4                        warn("unexpected_a", name);
    +
     6532.      4                    }
    +
     6533.      4                    enroll(name, "variable", true);
    +
     6534.      4                    names.push(name);
    +
     6535.      4                    if (token_nxt.id !== ",") {
    +
     6536.      4                        break;
    +
     6537.      4                    }
    +
     6538.      4                    advance(",");
    +
     6539.      4                }
    +
     6540.      4            }
    +
     6541.      4            advance("}");
    +
     6542.      4            the_import.name = names;
    +
     6543.     60        }
    +
     6544.     60        advance("from");
    +
     6545.     60        advance("(string)");
    +
     6546.     60        the_import.import = token_now;
    +
     6547.     60        if (!jslint_rgx_module.test(token_now.value)) {
    +
     6548.      1
    +
     6549.      1// test_cause:
    +
     6550.      1// ["import aa from \"!aa\"", "stmt_import", "bad_module_name_a", "!aa", 16]
    +
     6551.      1
    +
     6552.      1            warn("bad_module_name_a", token_now);
    +
     6553.     60        }
    +
     6554.     60        import_list.push(token_now.value);
    +
     6555.     60        semicolon();
    +
     6556.     60        return the_import;
    +
     6557.     60    }
    +
     6558.    631
    +
     6559.      5    function stmt_lbrace() {
    +
     6560.      5
    +
     6561.      5// test_cause:
    +
     6562.      5// [";{}", "stmt_lbrace", "naked_block", "{", 2]
    +
     6563.      5// ["class aa{}", "stmt_lbrace", "naked_block", "{", 9]
    +
     6564.      5
    +
     6565.      5        warn("naked_block", token_now);
    +
     6566.      5        return block("naked");
    +
     6567.      5    }
    +
     6568.    631
    +
     6569.   1761    function stmt_return() {
    +
     6570.   1761        const the_return = token_now;
    +
     6571.   1761        check_not_top_level(the_return);
    +
     6572.      1        if (functionage.finally > 0) {
    +
     6573.      1
    +
     6574.      1// test_cause:
    +
     6575.      1// ["
    +
     6576.      1// function aa(){try{}finally{return;}}
    +
     6577.      1// ", "stmt_return", "unexpected_a", "return", 28]
    +
     6578.      1
    +
     6579.      1            warn("unexpected_a", the_return);
    +
     6580.      1        }
    +
     6581.   1761        the_return.disrupt = true;
    +
     6582.   1509        if (token_nxt.id !== ";" && the_return.line === token_nxt.line) {
    +
     6583.   1509            the_return.expression = parse_expression(10);
    +
     6584.   1509        }
    +
     6585.   1761        advance(";");
    +
     6586.   1761        return the_return;
    +
     6587.   1761    }
    +
     6588.    631
    +
     6589.      5    function stmt_semicolon() {
    +
     6590.      5
    +
     6591.      5// test_cause:
    +
     6592.      5// [";", "stmt_semicolon", "unexpected_a", ";", 1]
    +
     6593.      5
    +
     6594.      5        warn("unexpected_a", token_now);
    +
     6595.      5        return token_now;
    +
     6596.      5    }
    +
     6597.    631
    +
     6598.    220    function stmt_switch() {
    +
     6599.    220        const the_cases = [];
    +
     6600.    220        const the_switch = token_now;
    +
     6601.    220        let dups = [];
    +
     6602.    220        let exp;
    +
     6603.    220        let last;
    +
     6604.    220        let stmts;
    +
     6605.    220        let the_case;
    +
     6606.    220        let the_default;
    +
     6607.    220        let the_disrupt = true;
    +
     6608.    220        let the_last;
    +
     6609.  23088        function is_dup(thing) {
    +
     6610.  23088            return is_equal(thing, exp);
    +
     6611.  23088        }
    +
     6612.    220        check_not_top_level(the_switch);
    +
     6613.      1        if (functionage.finally > 0) {
    +
     6614.      1
    +
     6615.      1// test_cause:
    +
     6616.      1// ["
    +
     6617.      1// function aa(){try{}finally{switch(0){}}}
    +
     6618.      1// ", "stmt_switch", "unexpected_a", "switch", 28]
    +
     6619.      1
    +
     6620.      1            warn("unexpected_a", the_switch);
    +
     6621.      1        }
    +
     6622.    220        functionage.switch += 1;
    +
     6623.    220        advance("(");
    +
     6624.    220        token_now.free = true;
    +
     6625.    220        the_switch.expression = parse_expression(0);
    +
     6626.    220        the_switch.block = the_cases;
    +
     6627.    220        advance(")");
    +
     6628.    220        advance("{");
    +
     6629.   1085        while (true) {
    +
     6630.   1085
    +
     6631.   1085// Loop through cases with breaks.
    +
     6632.   1085
    +
     6633.   1085            the_case = token_nxt;
    +
     6634.   1085            the_case.arity = "statement";
    +
     6635.   1085            the_case.expression = [];
    +
     6636.   1612            while (true) {
    +
     6637.   1612
    +
     6638.   1612// Loop through fallthrough cases.
    +
     6639.   1612
    +
     6640.   1612                advance("case");
    +
     6641.   1612                token_now.switch = true;
    +
     6642.   1612                exp = parse_expression(0);
    +
     6643.   1612                if (dups.some(is_dup)) {
    +
     6644.   1612
    +
     6645.   1612// test_cause:
    +
     6646.   1612// ["
    +
     6647.   1612// switch(0){case 0:break;case 0:break}
    +
     6648.   1612// ", "stmt_switch", "unexpected_a", "0", 29]
    +
     6649.   1612
    +
     6650.   1612                    warn("unexpected_a", exp);
    +
     6651.   1612                }
    +
     6652.   1612                dups.push(exp);
    +
     6653.   1612                the_case.expression.push(exp);
    +
     6654.   1612                advance(":");
    +
     6655.   1612                if (token_nxt.id !== "case") {
    +
     6656.   1612                    break;
    +
     6657.   1612                }
    +
     6658.   1612            }
    +
     6659.   1085
    +
     6660.   1085// test_cause:
    +
     6661.   1085// ["
    +
     6662.   1085// switch(0){case 1:case 0:break;}
    +
     6663.   1085// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 23]
    +
     6664.   1085// ["
    +
     6665.   1085// switch(0){case "aa":case 0:break;}
    +
     6666.   1085// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 26]
    +
     6667.   1085// ["
    +
     6668.   1085// switch(0){case "bb":case "aa":break;}
    +
     6669.   1085// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 26]
    +
     6670.   1085// ["
    +
     6671.   1085// switch(0){case aa:case "aa":break;}
    +
     6672.   1085// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24]
    +
     6673.   1085// ["
    +
     6674.   1085// switch(0){case bb:case aa:break;}
    +
     6675.   1085// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24]
    +
     6676.   1085
    +
     6677.   1085            check_ordered_case(the_case.expression);
    +
     6678.   1085            stmts = parse_statements();
    +
     6679.   1085            if (stmts.length < 1) {
    +
     6680.   1085
    +
     6681.   1085// test_cause:
    +
     6682.   1085// ["
    +
     6683.   1085// switch(0){case 0:default:}
    +
     6684.   1085// ", "stmt_switch", "expected_statements_a", "default", 18]
    +
     6685.   1085// ["switch(0){case 0:}", "stmt_switch", "expected_statements_a", "}", 18]
    +
     6686.   1085
    +
     6687.   1085                warn("expected_statements_a");
    +
     6688.   1085
    +
     6689.   1085// PR-359 - Bugfix - Fixes issue #358 - switch-statement crashes jslint.
    +
     6690.   1085
    +
     6691.   1085                break;
    +
     6692.   1085            }
    +
     6693.   1085            the_case.block = stmts;
    +
     6694.   1085            the_cases.push(the_case);
    +
     6695.   1085            last = stmts[stmts.length - 1];
    +
     6696.   1085            if (last.disrupt) {
    +
     6697.   1085                if (last.id === "break" && last.label === undefined) {
    +
     6698.   1085                    the_disrupt = false;
    +
     6699.   1085                }
    +
     6700.   1085            } else {
    +
     6701.   1085                warn("expected_a_before_b", token_nxt, "break;", artifact());
    +
     6702.   1085            }
    +
     6703.   1085            if (token_nxt.id !== "case") {
    +
     6704.   1085                break;
    +
     6705.   1085            }
    +
     6706.   1085        }
    +
     6707.    217
    +
     6708.    217// test_cause:
    +
     6709.    217// ["
    +
     6710.    217// switch(0){case 1:break;case 0:break;}
    +
     6711.    217// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 29]
    +
     6712.    217// ["
    +
     6713.    217// switch(0){case "aa":break;case 0:break;}
    +
     6714.    217// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 32]
    +
     6715.    217// ["
    +
     6716.    217// switch(0){case "bb":break;case "aa":break;}
    +
     6717.    217// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 32]
    +
     6718.    217// ["
    +
     6719.    217// switch(0){case aa:break;case "aa":break;}
    +
     6720.    217// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30]
    +
     6721.    217// ["
    +
     6722.    217// switch(0){case bb:break;case aa:break;}
    +
     6723.    217// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30]
    +
     6724.    217
    +
     6725.   1080        check_ordered_case(the_cases.map(function ({
    +
     6726.   1080            expression
    +
     6727.   1080        }) {
    +
     6728.   1080            return expression[0];
    +
     6729.   1080        }));
    +
     6730.    217        dups = undefined;
    +
     6731.    217        if (token_nxt.id === "default") {
    +
     6732.     99            the_default = token_nxt;
    +
     6733.     99            advance("default");
    +
     6734.     99            token_now.switch = true;
    +
     6735.     99            advance(":");
    +
     6736.     99            the_switch.else = parse_statements();
    +
     6737.     99            if (the_switch.else.length < 1) {
    +
     6738.     99
    +
     6739.     99// test_cause:
    +
     6740.     99// ["
    +
     6741.     99// switch(0){case 0:break;default:}
    +
     6742.     99// ", "stmt_switch", "unexpected_a", "default", 24]
    +
     6743.     99
    +
     6744.     99                warn("unexpected_a", the_default);
    +
     6745.     99                the_disrupt = false;
    +
     6746.     99            } else {
    +
     6747.     99                the_last = the_switch.else[
    +
     6748.     99                    the_switch.else.length - 1
    +
     6749.     99                ];
    +
     6750.     99                if (
    +
     6751.     99                    the_last.id === "break"
    +
     6752.     99                    && the_last.label === undefined
    +
     6753.     99                ) {
    +
     6754.     99
    +
     6755.     99// test_cause:
    +
     6756.     99// ["
    +
     6757.     99// switch(0){case 0:break;default:break;}
    +
     6758.     99// ", "stmt_switch", "unexpected_a", "break", 32]
    +
     6759.     99
    +
     6760.     99                    warn("unexpected_a", the_last);
    +
     6761.     99                    the_last.disrupt = false;
    +
     6762.     99                }
    +
     6763.     99                the_disrupt = the_disrupt && the_last.disrupt;
    +
     6764.     99            }
    +
     6765.    118        } else {
    +
     6766.    118            the_disrupt = false;
    +
     6767.    217        }
    +
     6768.    217        advance("}", the_switch);
    +
     6769.    217        functionage.switch -= 1;
    +
     6770.    217        the_switch.disrupt = the_disrupt;
    +
     6771.    217        return the_switch;
    +
     6772.    217    }
    +
     6773.    631
    +
     6774.     40    function stmt_throw() {
    +
     6775.     40        const the_throw = token_now;
    +
     6776.     40        the_throw.disrupt = true;
    +
     6777.     40        the_throw.expression = parse_expression(10);
    +
     6778.     40        semicolon();
    +
     6779.      1        if (functionage.try > 0) {
    +
     6780.      1
    +
     6781.      1// test_cause:
    +
     6782.      1// ["try{throw 0}catch(){}", "stmt_throw", "unexpected_a", "throw", 5]
    +
     6783.      1
    +
     6784.      1            warn("unexpected_a", the_throw);
    +
     6785.      1        }
    +
     6786.     40        return the_throw;
    +
     6787.     40    }
    +
     6788.    631
    +
     6789.     62    function stmt_try() {
    +
     6790.     62        const the_try = token_now;
    +
     6791.     62        let ignored;
    +
     6792.     62        let the_catch;
    +
     6793.     62        let the_disrupt;
    +
     6794.      1        if (functionage.try > 0) {
    +
     6795.      1
    +
     6796.      1// test_cause:
    +
     6797.      1// ["try{try{}catch(){}}catch(){}", "stmt_try", "unexpected_a", "try", 5]
    +
     6798.      1
    +
     6799.      1            warn("unexpected_a", the_try);
    +
     6800.      1        }
    +
     6801.     62        functionage.try += 1;
    +
     6802.     62        the_try.block = block();
    +
     6803.     62        the_disrupt = the_try.block.disrupt;
    +
     6804.     57        if (token_nxt.id === "catch") {
    +
     6805.     57            ignored = "ignore";
    +
     6806.     57            the_catch = token_nxt;
    +
     6807.     57            the_try.catch = the_catch;
    +
     6808.     57            advance("catch");
    +
     6809.     57
    +
     6810.     57// Create new catch-scope for catch-parameter.
    +
     6811.     57
    +
     6812.     57            catch_stack.push(catchage);
    +
     6813.     57            catchage = the_catch;
    +
     6814.     57            catch_list.push(catchage);
    +
     6815.     57            the_catch.context = empty();
    +
     6816.     57            if (token_nxt.id === "(") {
    +
     6817.     57                advance("(");
    +
     6818.     57                if (!token_nxt.identifier) {
    +
     6819.     57
    +
     6820.     57// test_cause:
    +
     6821.     57// ["try{}catch(){}", "stmt_try", "expected_identifier_a", ")", 12]
    +
     6822.     57
    +
     6823.     57                    return stop("expected_identifier_a");
    +
     6824.     57                }
    +
     6825.     57                if (token_nxt.id !== "ignore") {
    +
     6826.     57                    ignored = undefined;
    +
     6827.     57                    the_catch.name = token_nxt;
    +
     6828.     57                    enroll(token_nxt, "exception", true);
    +
     6829.     57                }
    +
     6830.     57                advance();
    +
     6831.     57                advance(")");
    +
     6832.     57            }
    +
     6833.     57            the_catch.block = block(ignored);
    +
     6834.     57            if (the_catch.block.disrupt !== true) {
    +
     6835.     57                the_disrupt = false;
    +
     6836.     57            }
    +
     6837.     57
    +
     6838.     57// Restore previous catch-scope after catch-block.
    +
     6839.     57
    +
     6840.     57            catchage = catch_stack.pop();
    +
     6841.     57
    +
     6842.     57// PR-404 - Relax warning about missing `catch` in `try...finally` statement.
    +
     6843.     57//
    +
     6844.     57//         } else {
    +
     6845.     57//
    +
     6846.     57// // test_cause:
    +
     6847.     57// // ["try{}finally{break;}", "stmt_try", "expected_a_before_b", "finally", 6]
    +
     6848.     57//
    +
     6849.     57//             warn("expected_a_before_b", token_nxt, "catch", artifact());
    +
     6850.     57
    +
     6851.     58        }
    +
     6852.     58        if (token_nxt.id === "finally") {
    +
     6853.      4            functionage.finally += 1;
    +
     6854.      4            advance("finally");
    +
     6855.      4            the_try.else = block();
    +
     6856.      4            the_disrupt = the_try.else.disrupt;
    +
     6857.      4            functionage.finally -= 1;
    +
     6858.     56        }
    +
     6859.     56        the_try.disrupt = the_disrupt;
    +
     6860.     56        functionage.try -= 1;
    +
     6861.     56        return the_try;
    +
     6862.     56    }
    +
     6863.    631
    +
     6864.   2334    function stmt_var() {
    +
     6865.   2334        let ellipsis;
    +
     6866.   2334        let mode_const;
    +
     6867.   2334        let name;
    +
     6868.   2334        let the_brace;
    +
     6869.   2334        let the_bracket;
    +
     6870.   2334        let the_variable = token_now;
    +
     6871.   2334        let variable_prv;
    +
     6872.   2334        mode_const = the_variable.id === "const";
    +
     6873.   2334        the_variable.names = [];
    +
     6874.   2334
    +
     6875.   2334// A program may use var or let, but not both.
    +
     6876.   2334
    +
     6877.   2058        if (!mode_const) {
    +
     6878.   2058            if (mode_var === undefined) {
    +
     6879.   2058                mode_var = the_variable.id;
    +
     6880.   2058            } else if (the_variable.id !== mode_var) {
    +
     6881.   2058
    +
     6882.   2058// test_cause:
    +
     6883.   2058// ["let aa;var aa", "stmt_var", "expected_a_b", "var", 8]
    +
     6884.   2058
    +
     6885.   2058                warn("expected_a_b", the_variable, mode_var, the_variable.id);
    +
     6886.   2058            }
    +
     6887.   2058        }
    +
     6888.   2334
    +
     6889.   2334// We don't expect to see variables created in switch statements.
    +
     6890.   2334
    +
     6891.      1        if (functionage.switch > 0) {
    +
     6892.      1
    +
     6893.      1// test_cause:
    +
     6894.      1// ["switch(0){case 0:var aa}", "stmt_var", "var_switch", "var", 18]
    +
     6895.      1
    +
     6896.      1            warn("var_switch", the_variable);
    +
     6897.      1        }
    +
     6898.   2334        switch (
    +
     6899.   2334            Boolean(functionage.statement_prv)
    +
     6900.   1449            && functionage.statement_prv.id
    +
     6901.   2334        ) {
    +
     6902.    107        case "const":
    +
     6903.   1437        case "let":
    +
     6904.   1439        case "var":
    +
     6905.   1439
    +
     6906.   1439// test_cause:
    +
     6907.   1439// ["const aa=0;const bb=0;", "stmt_var", "var_prv", "const", 0]
    +
     6908.   1439// ["let aa=0;let bb=0;", "stmt_var", "var_prv", "let", 0]
    +
     6909.   1439// ["var aa=0;var bb=0;", "stmt_var", "var_prv", "var", 0]
    +
     6910.   1439
    +
     6911.   1439            test_cause("var_prv", functionage.statement_prv.id);
    +
     6912.   1439            variable_prv = functionage.statement_prv;
    +
     6913.   1439            break;
    +
     6914.      3        case "import":
    +
     6915.      3
    +
     6916.      3// test_cause:
    +
     6917.      3// ["import aa from \"aa\";\nlet bb=0;", "stmt_var", "import_prv", "", 0]
    +
     6918.      3
    +
     6919.      3            test_cause("import_prv");
    +
     6920.      3            break;
    +
     6921.    885        case false:
    +
     6922.    885            break;
    +
     6923.      7        default:
    +
     6924.      7            if (
    +
     6925.      7                (option_dict.beta && !option_dict.variable)
    +
     6926.      7                || the_variable.id === "var"
    +
     6927.      7            ) {
    +
     6928.      7
    +
     6929.      7// test_cause:
    +
     6930.      7// ["console.log();let aa=0;", "stmt_var", "var_on_top", "let", 15]
    +
     6931.      7// ["console.log();var aa=0;", "stmt_var", "var_on_top", "var", 15]
    +
     6932.      7// ["try{aa();}catch(aa){var aa=0;}", "stmt_var", "var_on_top", "var", 21]
    +
     6933.      7// ["while(0){var aa;}", "stmt_var", "var_on_top", "var", 10]
    +
     6934.      7
    +
     6935.      7                warn("var_on_top", token_now);
    +
     6936.      7            }
    +
     6937.   2334        }
    +
     6938.   2335        while (true) {
    +
     6939.   2335            if (token_nxt.id === "{") {
    +
     6940.   2335                if (the_variable.id === "var") {
    +
     6941.   2335
    +
     6942.   2335// test_cause:
    +
     6943.   2335// ["var{aa}=0", "stmt_var", "unexpected_a", "var", 1]
    +
     6944.   2335
    +
     6945.   2335                    warn("unexpected_a", the_variable);
    +
     6946.   2335                }
    +
     6947.   2335                the_brace = token_nxt;
    +
     6948.   2335                advance("{");
    +
     6949.   2335                while (true) {
    +
     6950.   2335                    name = token_nxt;
    +
     6951.   2335                    if (!name.identifier) {
    +
     6952.   2335
    +
     6953.   2335// test_cause:
    +
     6954.   2335// ["let {0}", "stmt_var", "expected_identifier_a", "0", 6]
    +
     6955.   2335
    +
     6956.   2335                        return stop("expected_identifier_a");
    +
     6957.   2335                    }
    +
     6958.   2335                    survey(name);
    +
     6959.   2335                    advance();
    +
     6960.   2335                    if (token_nxt.id === ":") {
    +
     6961.   2335                        advance(":");
    +
     6962.   2335                        if (!token_nxt.identifier) {
    +
     6963.   2335
    +
     6964.   2335// test_cause:
    +
     6965.   2335// ["let {aa:0}", "stmt_var", "expected_identifier_a", "0", 9]
    +
     6966.   2335// ["let {aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 9]
    +
     6967.   2335
    +
     6968.   2335                            return stop("expected_identifier_a");
    +
     6969.   2335                        }
    +
     6970.   2335
    +
     6971.   2335// PR-363 - Bugfix
    +
     6972.   2335// Add test against false-warning <uninitialized 'bb'> in code
    +
     6973.   2335// '/*jslint node*/\nlet {aa:bb} = {}; bb();'.
    +
     6974.   2335//
    +
     6975.   2335//                         token_nxt.label = name;
    +
     6976.   2335//                         the_variable.names.push(token_nxt);
    +
     6977.   2335//                         enroll(token_nxt, "variable", mode_const);
    +
     6978.   2335
    +
     6979.   2335                        name = token_nxt;
    +
     6980.   2335                        the_variable.names.push(name);
    +
     6981.   2335                        survey(name);
    +
     6982.   2335                        enroll(name, "variable", mode_const);
    +
     6983.   2335
    +
     6984.   2335                        advance();
    +
     6985.   2335                        the_brace.open = true;
    +
     6986.   2335                    } else {
    +
     6987.   2335                        the_variable.names.push(name);
    +
     6988.   2335                        enroll(name, "variable", mode_const);
    +
     6989.   2335                    }
    +
     6990.   2335                    name.dead = false;
    +
     6991.   2335                    name.init = true;
    +
     6992.   2335                    if (token_nxt.id === "=") {
    +
     6993.   2335
    +
     6994.   2335// test_cause:
    +
     6995.   2335// ["let {aa=0}", "stmt_var", "assign", "", 0]
    +
     6996.   2335
    +
     6997.   2335                        test_cause("assign");
    +
     6998.   2335                        advance("=");
    +
     6999.   2335                        name.expression = parse_expression();
    +
     7000.   2335                        the_brace.open = true;
    +
     7001.   2335                    }
    +
     7002.   2335                    if (token_nxt.id !== ",") {
    +
     7003.   2335                        break;
    +
     7004.   2335                    }
    +
     7005.   2335                    advance(",");
    +
     7006.   2335                }
    +
     7007.   2335
    +
     7008.   2335// test_cause:
    +
     7009.   2335// ["let{bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8]
    +
     7010.   2335
    +
     7011.   2335                check_ordered(the_variable.id, the_variable.names);
    +
     7012.   2335                advance("}");
    +
     7013.   2335                advance("=");
    +
     7014.   2335                the_variable.expression = parse_expression(0);
    +
     7015.   2335            } else if (token_nxt.id === "[") {
    +
     7016.   2335                if (the_variable.id === "var") {
    +
     7017.   2335
    +
     7018.   2335// test_cause:
    +
     7019.   2335// ["var[aa]=0", "stmt_var", "unexpected_a", "var", 1]
    +
     7020.   2335
    +
     7021.   2335                    warn("unexpected_a", the_variable);
    +
     7022.   2335                }
    +
     7023.   2335                the_bracket = token_nxt;
    +
     7024.   2335                advance("[");
    +
     7025.   2335                while (true) {
    +
     7026.   2335                    ellipsis = false;
    +
     7027.   2335                    if (token_nxt.id === "...") {
    +
     7028.   2335                        ellipsis = true;
    +
     7029.   2335                        advance("...");
    +
     7030.   2335                    }
    +
     7031.   2335                    if (!token_nxt.identifier) {
    +
     7032.   2335
    +
     7033.   2335// test_cause:
    +
     7034.   2335// ["let[]", "stmt_var", "expected_identifier_a", "]", 5]
    +
     7035.   2335
    +
     7036.   2335                        return stop("expected_identifier_a");
    +
     7037.   2335                    }
    +
     7038.   2335                    name = token_nxt;
    +
     7039.   2335                    advance();
    +
     7040.   2335                    the_variable.names.push(name);
    +
     7041.   2335                    enroll(name, "variable", mode_const);
    +
     7042.   2335                    name.dead = false;
    +
     7043.   2335                    name.init = true;
    +
     7044.   2335                    if (ellipsis) {
    +
     7045.   2335                        name.ellipsis = true;
    +
     7046.   2335                        break;
    +
     7047.   2335                    }
    +
     7048.   2335                    if (token_nxt.id === "=") {
    +
     7049.   2335                        advance("=");
    +
     7050.   2335                        name.expression = parse_expression();
    +
     7051.   2335                        the_bracket.open = true;
    +
     7052.   2335                    }
    +
     7053.   2335                    if (token_nxt.id !== ",") {
    +
     7054.   2335                        break;
    +
     7055.   2335                    }
    +
     7056.   2335                    advance(",");
    +
     7057.   2335                }
    +
     7058.   2335                advance("]");
    +
     7059.   2335                advance("=");
    +
     7060.   2335                the_variable.expression = parse_expression(0);
    +
     7061.   2335            } else if (token_nxt.identifier) {
    +
     7062.   2335                name = token_nxt;
    +
     7063.   2335                advance();
    +
     7064.   2335                if (name.id === "ignore") {
    +
     7065.   2335
    +
     7066.   2335// test_cause:
    +
     7067.   2335// ["
    +
     7068.   2335// let ignore;function aa(ignore) {}
    +
     7069.   2335// ", "stmt_var", "unexpected_a", "ignore", 5]
    +
     7070.   2335
    +
     7071.   2335                    warn("unexpected_a", name);
    +
     7072.   2335                }
    +
     7073.   2335                enroll(name, "variable", mode_const);
    +
     7074.   2335                if (token_nxt.id === "=" || mode_const) {
    +
     7075.   2335                    advance("=");
    +
     7076.   2335                    name.dead = false;
    +
     7077.   2335                    name.init = true;
    +
     7078.   2335                    name.expression = parse_expression(0);
    +
     7079.   2335                }
    +
     7080.   2335                the_variable.names.push(name);
    +
     7081.   2335            } else {
    +
     7082.   2335
    +
     7083.   2335// test_cause:
    +
     7084.   2335// ["let 0", "stmt_var", "expected_identifier_a", "0", 5]
    +
     7085.   2335// ["var{aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 8]
    +
     7086.   2335
    +
     7087.   2335                return stop("expected_identifier_a");
    +
     7088.   2335            }
    +
     7089.   2335            if (token_nxt.id !== ",") {
    +
     7090.   2335                break;
    +
     7091.   2335            }
    +
     7092.   2335
    +
     7093.   2335// test_cause:
    +
     7094.   2335// ["let aa,bb;", "stmt_var", "expected_a_b", ",", 7]
    +
     7095.   2335
    +
     7096.   2335            warn("expected_a_b", token_nxt, ";", ",");
    +
     7097.   2335            advance(",");
    +
     7098.   2335        }
    +
     7099.   2320
    +
     7100.   2320// Warn if variable declarations are unordered.
    +
     7101.   2320
    +
     7102.   2320        if (
    +
     7103.   2320            option_dict.beta
    +
     7104.   2320            && !option_dict.unordered
    +
     7105.   2315            && !option_dict.variable
    +
     7106.   2309            && variable_prv
    +
     7107.   1437            && (
    +
     7108.   1437                variable_prv.id + " " + variable_prv.names[0].id
    +
     7109.   1437                > the_variable.id + " " + the_variable.names[0].id
    +
     7110.   1437            )
    +
     7111.      3        ) {
    +
     7112.      3
    +
     7113.      3// test_cause:
    +
     7114.      3// ["const bb=0;const aa=0;", "stmt_var", "expected_a_b_before_c_d", "aa", 12]
    +
     7115.      3// ["let bb;let aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8]
    +
     7116.      3// ["var bb;var aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8]
    +
     7117.      3
    +
     7118.      3            warn(
    +
     7119.      3                "expected_a_b_before_c_d",
    +
     7120.      3                the_variable,
    +
     7121.      3                the_variable.id,
    +
     7122.      3                the_variable.names[0].id,
    +
     7123.      3                variable_prv.id,
    +
     7124.      3                variable_prv.names[0].id
    +
     7125.      3            );
    +
     7126.   2320        }
    +
     7127.   2320        semicolon();
    +
     7128.   2320        return the_variable;
    +
     7129.   2320    }
    +
     7130.    631
    +
     7131.    208    function stmt_while() {
    +
     7132.    208        const the_while = token_now;
    +
     7133.    208        check_not_top_level(the_while);
    +
     7134.    208        functionage.loop += 1;
    +
     7135.    208        the_while.expression = condition();
    +
     7136.    208        the_while.block = block();
    +
     7137.      1        if (the_while.block.disrupt === true) {
    +
     7138.      1
    +
     7139.      1// test_cause:
    +
     7140.      1// ["function aa(){while(0){break;}}", "stmt_while", "weird_loop", "while", 15]
    +
     7141.      1
    +
     7142.      1            warn("weird_loop", the_while);
    +
     7143.    205        }
    +
     7144.    205        functionage.loop -= 1;
    +
     7145.    205        return the_while;
    +
     7146.    205    }
    +
     7147.    631
    +
     7148.      1    function stmt_with() {
    +
     7149.      1
    +
     7150.      1// test_cause:
    +
     7151.      1// ["with", "stmt_with", "unexpected_a", "with", 1]
    +
     7152.      1
    +
     7153.      1        stop("unexpected_a", token_now);
    +
     7154.      1    }
    +
     7155.    631
    +
     7156.  14600    function survey(name) {
    +
     7157.  14600        let id = name.id;
    +
     7158.  14600
    +
     7159.  14600// Tally the property name. If it is a string, only tally strings that conform
    +
     7160.  14600// to the identifier rules.
    +
     7161.  14600
    +
     7162.    183        if (id === "(string)") {
    +
     7163.    183            id = name.value;
    +
     7164.    183            if (!jslint_rgx_identifier.test(id)) {
    +
     7165.    183                return id;
    +
     7166.    183            }
    +
     7167.  14417        } else if (id === "`") {
    +
     7168.  14417            if (name.value.length === 1) {
    +
     7169.  14417                id = name.value[0].value;
    +
     7170.  14417                if (!jslint_rgx_identifier.test(id)) {
    +
     7171.  14417                    return id;
    +
     7172.  14417                }
    +
     7173.  14417            }
    +
     7174.  14417        } else if (!name.identifier) {
    +
     7175.  14417
    +
     7176.  14417// test_cause:
    +
     7177.  14417// ["let aa={0:0}", "survey", "expected_identifier_a", "0", 9]
    +
     7178.  14417
    +
     7179.  14417            return stop("expected_identifier_a", name);
    +
     7180.  14513        }
    +
     7181.  14513
    +
     7182.  14513// If we have seen this name before, increment its count.
    +
     7183.  14513
    +
     7184.  14513        if (typeof property_dict[id] === "number") {
    +
     7185.  12086            property_dict[id] += 1;
    +
     7186.  12086
    +
     7187.  12086// If this is the first time seeing this property name, and if there is a
    +
     7188.  12086// tenure list, then it must be on the list. Otherwise, it must conform to
    +
     7189.  12086// the rules for good property names.
    +
     7190.  12086
    +
     7191.  12086        } else {
    +
     7192.   2427            if (state.mode_property) {
    +
     7193.   2427                if (tenure[id] !== true) {
    +
     7194.   2427
    +
     7195.   2427// test_cause:
    +
     7196.   2427// ["/*property aa*/\naa.bb", "survey", "unregistered_property_a", "bb", 4]
    +
     7197.   2427
    +
     7198.   2427                    warn("unregistered_property_a", name);
    +
     7199.   2427                }
    +
     7200.   2427            } else if (
    +
     7201.   2427                !option_dict.nomen
    +
     7202.   2427                && name.identifier
    +
     7203.   2427                && jslint_rgx_weird_property.test(id)
    +
     7204.   2427            ) {
    +
     7205.   2427
    +
     7206.   2427// test_cause:
    +
     7207.   2427// ["aa.$", "survey", "weird_property_a", "$", 4]
    +
     7208.   2427// ["aa._", "survey", "weird_property_a", "_", 4]
    +
     7209.   2427// ["aa._aa", "survey", "weird_property_a", "_aa", 4]
    +
     7210.   2427// ["aa.aaSync", "survey", "weird_property_a", "aaSync", 4]
    +
     7211.   2427// ["aa.aa_", "survey", "weird_property_a", "aa_", 4]
    +
     7212.   2427
    +
     7213.   2427                warn("weird_property_a", name);
    +
     7214.   2427            }
    +
     7215.   2427            property_dict[id] = 1;
    +
     7216.  14513        }
    +
     7217.  14513        return id;
    +
     7218.  14513    }
    +
     7219.    631
    +
     7220.    631// These functions are used to specify the grammar of our language:
    +
     7221.    631
    +
     7222.  82030    function symbol(id, bp) {
    +
     7223.  82030
    +
     7224.  82030// Create a symbol if it does not already exist in the language's syntax.
    +
     7225.  82030
    +
     7226.  82030        let the_symbol = syntax_dict[id];
    +
     7227.  71303        if (the_symbol === undefined) {
    +
     7228.  71303            the_symbol = empty();
    +
     7229.  71303            the_symbol.id = id;
    +
     7230.  71303            the_symbol.lbp = bp || 0;
    +
     7231.  71303            syntax_dict[id] = the_symbol;
    +
     7232.  71303        }
    +
     7233.  82030        return the_symbol;
    +
     7234.  82030    }
    +
     7235.    631
    +
     7236.    631    function ternary(id1, id2) {
    +
     7237.    631
    +
     7238.    631// Create a ternary operator.
    +
     7239.    631
    +
     7240.    631        const the_symbol = symbol(id1, 30);
    +
     7241.    213        the_symbol.led_infix = function parse_ternary_led(left) {
    +
     7242.    213            const the_token = token_now;
    +
     7243.    213            let second;
    +
     7244.    213            second = parse_expression(20);
    +
     7245.    213            advance(id2);
    +
     7246.    213            token_now.arity = "ternary";
    +
     7247.    213            the_token.arity = "ternary";
    +
     7248.    213            the_token.expression = [left, second, parse_expression(10)];
    +
     7249.      5            if (token_nxt.id !== ")") {
    +
     7250.      5
    +
     7251.      5// test_cause:
    +
     7252.      5// ["0?0:0", "parse_ternary_led", "use_open", "?", 2]
    +
     7253.      5
    +
     7254.      5                warn("use_open", the_token);
    +
     7255.      5            }
    +
     7256.    213            return the_token;
    +
     7257.    213        };
    +
     7258.    631        return the_symbol;
    +
     7259.    631    }
    +
     7260.    631
    +
     7261.    631// Now we parse JavaScript.
    +
     7262.    631// Begin defining the language.
    +
     7263.    631
    +
     7264.    631    assignment("%=");
    +
     7265.    631    assignment("&=");
    +
     7266.    631    assignment("*=");
    +
     7267.    631    assignment("+=");
    +
     7268.    631    assignment("-=");
    +
     7269.    631    assignment("/=");
    +
     7270.    631    assignment("<<=");
    +
     7271.    631    assignment("=");
    +
     7272.    631    assignment(">>=");
    +
     7273.    631    assignment(">>>=");
    +
     7274.    631    assignment("^=");
    +
     7275.    631    assignment("|=");
    +
     7276.    631    constant("(number)", "number");
    +
     7277.    631    constant("(regexp)", "regexp");
    +
     7278.    631    constant("(string)", "string");
    +
     7279.    631    constant("Function", "function", constant_Function);
    +
     7280.    631    constant("Infinity", "number", Infinity);
    +
     7281.    631    constant("NaN", "number", NaN);
    +
     7282.    631    constant("arguments", "object", constant_arguments);
    +
     7283.    631    constant("eval", "function", constant_eval);
    +
     7284.    631    constant("false", "boolean", false);
    +
     7285.    631    constant("ignore", "undefined", constant_ignore);
    +
     7286.    631    constant("isFinite", "function", constant_isInfinite);
    +
     7287.    631    constant("isNaN", "function", constant_isNaN);
    +
     7288.    631    constant("null", "null", null);
    +
     7289.    631    constant("this", "object", constant_this);
    +
     7290.    631    constant("true", "boolean", true);
    +
     7291.    631    constant("undefined", "undefined");
    +
     7292.    631    infix(100, "!=");
    +
     7293.    631    infix(100, "!==");
    +
     7294.    631    infix(100, "==");
    +
     7295.    631    infix(100, "===");
    +
     7296.    631    infix(110, "<");
    +
     7297.    631    infix(110, "<=");
    +
     7298.    631    infix(110, ">");
    +
     7299.    631    infix(110, ">=");
    +
     7300.    631    infix(110, "in");
    +
     7301.    631    infix(110, "instanceof");
    +
     7302.    631    infix(120, "<<");
    +
     7303.    631    infix(120, ">>");
    +
     7304.    631    infix(120, ">>>");
    +
     7305.    631    infix(130, "+");
    +
     7306.    631    infix(130, "-");
    +
     7307.    631    infix(140, "%");
    +
     7308.    631    infix(140, "*");
    +
     7309.    631    infix(140, "/");
    +
     7310.    631    infix(160, "(", infix_lparen);
    +
     7311.    631    infix(160, "`", infix_grave);
    +
     7312.    631    infix(170, ".", infix_dot);
    +
     7313.    631    infix(170, "=>", infix_fart_unwrapped);
    +
     7314.    631    infix(170, "?.", infix_option_chain);
    +
     7315.    631    infix(170, "[", infix_lbracket);
    +
     7316.    631    infix(35, "??");
    +
     7317.    631    infix(40, "||");
    +
     7318.    631    infix(50, "&&");
    +
     7319.    631    infix(70, "|");
    +
     7320.    631    infix(80, "^");
    +
     7321.    631    infix(90, "&");
    +
     7322.    631    infixr(150, "**");
    +
     7323.    631    postassign("++");
    +
     7324.    631    postassign("--");
    +
     7325.    631    preassign("++");
    +
     7326.    631    preassign("--");
    +
     7327.    631    prefix("!!");
    +
     7328.    631    prefix("!");
    +
     7329.    631    prefix("(", prefix_lparen);
    +
     7330.    631    prefix("+");
    +
     7331.    631    prefix("-");
    +
     7332.    631    prefix("/=", prefix_assign_divide);
    +
     7333.    631    prefix("=>", prefix_fart);
    +
     7334.    631    prefix("[", prefix_lbracket);
    +
     7335.    631    prefix("`", prefix_tick);
    +
     7336.    631    prefix("async", prefix_async);
    +
     7337.    631    prefix("await", prefix_await);
    +
     7338.    631    prefix("function", prefix_function);
    +
     7339.    631    prefix("new", prefix_new);
    +
     7340.    631    prefix("typeof");
    +
     7341.    631    prefix("void", prefix_void);
    +
     7342.    631    prefix("{", prefix_lbrace);
    +
     7343.    631    prefix("~");
    +
     7344.    631    stmt(";", stmt_semicolon);
    +
     7345.    631    stmt("async", prefix_async);
    +
     7346.    631    stmt("await", prefix_await);
    +
     7347.    631    stmt("break", stmt_break);
    +
     7348.    631    stmt("const", stmt_var);
    +
     7349.    631    stmt("continue", stmt_continue);
    +
     7350.    631    stmt("debugger", stmt_debugger);
    +
     7351.    631    stmt("delete", stmt_delete);
    +
     7352.    631    stmt("do", stmt_do);
    +
     7353.    631    stmt("export", stmt_export);
    +
     7354.    631    stmt("for", stmt_for);
    +
     7355.    631    stmt("function", prefix_function);
    +
     7356.    631    stmt("if", stmt_if);
    +
     7357.    631    stmt("import", stmt_import);
    +
     7358.    631    stmt("let", stmt_var);
    +
     7359.    631    stmt("return", stmt_return);
    +
     7360.    631    stmt("switch", stmt_switch);
    +
     7361.    631    stmt("throw", stmt_throw);
    +
     7362.    631    stmt("try", stmt_try);
    +
     7363.    631    stmt("var", stmt_var);
    +
     7364.    631    stmt("while", stmt_while);
    +
     7365.    631    stmt("with", stmt_with);
    +
     7366.    631    stmt("{", stmt_lbrace);
    +
     7367.    631    symbol(")");
    +
     7368.    631    symbol("*/");
    +
     7369.    631    symbol(",");
    +
     7370.    631    symbol(":");
    +
     7371.    631    symbol(";");
    +
     7372.    631    symbol("]");
    +
     7373.    631    symbol("async");
    +
     7374.    631    symbol("await");
    +
     7375.    631    symbol("case");
    +
     7376.    631    symbol("catch");
    +
     7377.    631    symbol("class");
    +
     7378.    631    symbol("default");
    +
     7379.    631    symbol("else");
    +
     7380.    631    symbol("enum");
    +
     7381.    631    symbol("finally");
    +
     7382.    631    symbol("implements");
    +
     7383.    631    symbol("interface");
    +
     7384.    631    symbol("package");
    +
     7385.    631    symbol("private");
    +
     7386.    631    symbol("protected");
    +
     7387.    631    symbol("public");
    +
     7388.    631    symbol("static");
    +
     7389.    631    symbol("super");
    +
     7390.    631    symbol("void");
    +
     7391.    631    symbol("yield");
    +
     7392.    631    symbol("}");
    +
     7393.    631    ternary("?", ":");
    +
     7394.    631
    +
     7395.    631// Init token_nxt.
    +
     7396.    631
    +
     7397.    631    advance();
    +
     7398.    631
    +
     7399.    631// Parsing of JSON is simple:
    +
     7400.    631
    +
     7401.     25    if (state.mode_json) {
    +
     7402.     25        state.token_tree = parse_json();
    +
     7403.     25        advance("(end)");
    +
     7404.     25        return;
    +
     7405.    606    }
    +
     7406.    606
    +
     7407.    606// Because browsers encourage combining of script files, the first token might
    +
     7408.    606// be a semicolon to defend against a missing semicolon in the preceding file.
    +
     7409.    606
    +
     7410.    606    if (option_dict.browser) {
    +
     7411.      5        if (token_nxt.id === ";") {
    +
     7412.      5            advance(";");
    +
     7413.      5        }
    +
     7414.      5
    +
     7415.      5// If we are not in a browser, then the file form of strict pragma may be used.
    +
     7416.      5
    +
     7417.    601    } else if (token_nxt.value === "use strict") {
    +
     7418.    601        advance("(string)");
    +
     7419.    601        advance(";");
    +
     7420.    606    }
    +
     7421.    606    state.token_tree = parse_statements();
    +
     7422.    606    advance("(end)");
    +
     7423.    606
    +
     7424.    606// Check global functions are ordered.
    +
     7425.    606
    +
     7426.    606    check_ordered(
    +
     7427.    606        "function",
    +
     7428.   2001        function_list.map(function ({
    +
     7429.   2001            level,
    +
     7430.   2001            name
    +
     7431.   2001        }) {
    +
     7432.    606            return (level === 1) && name;
    +
     7433.   2001        }).filter(function (name) {
    +
     7434.   1992            return option_dict.beta && name && name.id;
    +
     7435.   2001        })
    +
     7436.    606    );
    +
     7437.    606}
    +
     7438.      1
    +
     7439.    518function jslint_phase4_walk(state) {
    +
     7440.    518
    +
     7441.    518// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
    +
     7442.    518//          recursive traversal. Each node may be processed on the way down
    +
     7443.    518//          (preaction) and on the way up (postaction).
    +
     7444.    518
    +
     7445.    518    let {
    +
     7446.    518        artifact,
    +
     7447.    518        catch_stack,
    +
     7448.    518        function_stack,
    +
     7449.    518        global_dict,
    +
     7450.    518        is_equal,
    +
     7451.    518        is_weird,
    +
     7452.    518        option_dict,
    +
     7453.    518        syntax_dict,
    +
     7454.    518        test_cause,
    +
     7455.    518        token_global,
    +
     7456.    518        warn
    +
     7457.    518    } = state;
    +
     7458.    518    let block_stack = [];               // The stack of blocks.
    +
     7459.    518    let blockage = token_global;        // The current block.
    +
     7460.    518    let catchage = catch_stack[0];      // The current catch-block.
    +
     7461.    518    let functionage = token_global;     // The current function.
    +
     7462.    518    let postaction;
    +
     7463.    518    let postamble;
    +
     7464.    518    let posts = empty();
    +
     7465.    518    let preaction;
    +
     7466.    518    let preamble;
    +
     7467.    518    let pres = empty();
    +
     7468.    518
    +
     7469.    518// The relational operators.
    +
     7470.    518
    +
     7471.    518    let relationop = object_assign_from_list(empty(), [
    +
     7472.    518        "!=", "!==", "<", "<=", "==", "===", ">", ">="
    +
     7473.    518    ], true);
    +
     7474.    518
    +
     7475.    518// Ambulation of the parse tree.
    +
     7476.    518
    +
     7477.   1036    function action(when) {
    +
     7478.   1036
    +
     7479.   1036// Produce a function that will register task functions that will be called as
    +
     7480.   1036// the tree is traversed.
    +
     7481.   1036
    +
     7482.  19684        return function (arity, id, task) {
    +
     7483.  19684            let a_set = when[arity];
    +
     7484.  19684            let i_set;
    +
     7485.  19684
    +
     7486.  19684// The id parameter is optional. If excluded, the task will be applied to all
    +
     7487.  19684// ids.
    +
     7488.  19684
    +
     7489.   4144            if (typeof id !== "string") {
    +
     7490.   4144                task = id;
    +
     7491.   4144                id = "(all)";
    +
     7492.   4144            }
    +
     7493.  19684
    +
     7494.  19684// If this arity has no registrations yet, then create a set object to hold
    +
     7495.  19684// them.
    +
     7496.  19684
    +
     7497.   5180            if (a_set === undefined) {
    +
     7498.   5180                a_set = empty();
    +
     7499.   5180                when[arity] = a_set;
    +
     7500.   5180            }
    +
     7501.  19684
    +
     7502.  19684// If this id has no registrations yet, then create a set array to hold them.
    +
     7503.  19684
    +
     7504.  19684            i_set = a_set[id];
    +
     7505.  19166            if (i_set === undefined) {
    +
     7506.  19166                i_set = [];
    +
     7507.  19166                a_set[id] = i_set;
    +
     7508.  19166            }
    +
     7509.  19684
    +
     7510.  19684// Register the task with the arity and the id.
    +
     7511.  19684
    +
     7512.  19684            i_set.push(task);
    +
     7513.  19684        };
    +
     7514.   1036    }
    +
     7515.    518
    +
     7516.   1036    function amble(when) {
    +
     7517.   1036
    +
     7518.   1036// Produce a function that will act on the tasks registered by an action
    +
     7519.   1036// function while walking the tree.
    +
     7520.   1036
    +
     7521. 210942        return function (the_token) {
    +
     7522. 210942
    +
     7523. 210942// Given a task set that was built by an action function, run all
    +
     7524. 210942// relevant tasks on the token.
    +
     7525. 210942
    +
     7526. 210942            let a_set = when[the_token.arity];
    +
     7527. 210942            let i_set;
    +
     7528. 210942
    +
     7529. 210942// If there are tasks associated with the token's arity...
    +
     7530. 210942
    +
     7531. 144702            if (a_set !== undefined) {
    +
     7532. 144702
    +
     7533. 144702// If there are tasks associated with the token's id...
    +
     7534. 144702
    +
     7535. 144702                i_set = a_set[the_token.id];
    +
     7536. 144702                if (i_set !== undefined) {
    +
     7537. 144702                    i_set.forEach(function (task) {
    +
     7538. 144702                        task(the_token);
    +
     7539. 144702                    });
    +
     7540. 144702                }
    +
     7541. 144702
    +
     7542. 144702// If there are tasks for all ids.
    +
     7543. 144702
    +
     7544. 144702                i_set = a_set["(all)"];
    +
     7545. 144702                if (i_set !== undefined) {
    +
     7546. 144702                    i_set.forEach(function (task) {
    +
     7547. 144702                        task(the_token);
    +
     7548. 144702                    });
    +
     7549. 144702                }
    +
     7550. 144702            }
    +
     7551. 210942        };
    +
     7552.   1036    }
    +
     7553.    518
    +
     7554.   2818    function init_variable(name) {
    +
     7555.   2818        let the_variable = lookup(name);
    +
     7556.   2759        if (!the_variable || the_variable.readonly) {
    +
     7557.     61            warn("bad_assignment_a", name);
    +
     7558.     61            return;
    +
     7559.   2757        }
    +
     7560.   2757        the_variable.init = true;
    +
     7561.   2757    }
    +
     7562.    518
    +
     7563.  31509    function lookup(thing) {
    +
     7564.  31509        let id = thing.id;
    +
     7565.  31509        let the_variable;
    +
     7566.      1        if (thing.arity !== "variable") {
    +
     7567.      1            return;
    +
     7568.  31508        }
    +
     7569.  31508
    +
     7570.  31508// Look up the variable in the current context.
    +
     7571.  31508
    +
     7572.  31508        the_variable = functionage.context[id] || catchage.context[id];
    +
     7573.  31509
    +
     7574.  31509// If it isn't local, search all the other contexts. If there are name
    +
     7575.  31509// collisions, take the most recent.
    +
     7576.  31509
    +
     7577.  24629        if (the_variable && the_variable.role === "label") {
    +
     7578.      1
    +
     7579.      1// test_cause:
    +
     7580.      1// ["aa:while(0){aa;}", "lookup", "label_a", "aa", 13]
    +
     7581.      1
    +
     7582.      1            warn("label_a", thing);
    +
     7583.      1            return the_variable;
    +
     7584.  31507        }
    +
     7585.  31507        if (!the_variable) {
    +
     7586.  14775            function_stack.forEach(function ({
    +
     7587.  14775                context
    +
     7588.  14775            }) {
    +
     7589.   6879                if (context[id] && context[id].role !== "label") {
    +
     7590.   6879                    the_variable = context[id];
    +
     7591.   6879                }
    +
     7592.  14775            });
    +
     7593.   6879
    +
     7594.   6879// If it isn't in any of those either, perhaps it is a predefined global.
    +
     7595.   6879// If so, add it to the global context.
    +
     7596.   6879
    +
     7597.   6879            if (!the_variable && global_dict[id] === undefined) {
    +
     7598.   6879
    +
     7599.   6879// test_cause:
    +
     7600.   6879// ["aa", "lookup", "undeclared_a", "aa", 1]
    +
     7601.   6879// ["class aa{}", "lookup", "undeclared_a", "aa", 7]
    +
     7602.   6879// ["
    +
     7603.   6879// let aa=0;try{aa();}catch(bb){bb();}bb();
    +
     7604.   6879// ", "lookup", "undeclared_a", "bb", 36]
    +
     7605.   6879// ["
    +
     7606.   6879// let aa=0;try{aa();}catch(ignore){bb();}
    +
     7607.   6879// ", "lookup", "undeclared_a", "bb", 34]
    +
     7608.   6879
    +
     7609.   6879                warn("undeclared_a", thing);
    +
     7610.   6879                return;
    +
     7611.   6879            }
    +
     7612.   6879            if (!the_variable) {
    +
     7613.   6879                the_variable = {
    +
     7614.   6879                    dead: false,
    +
     7615.   6879                    id,
    +
     7616.   6879                    init: true,
    +
     7617.   6879                    parent: token_global,
    +
     7618.   6879                    readonly: true,
    +
     7619.   6879                    role: "variable",
    +
     7620.   6879                    used: 0
    +
     7621.   6879                };
    +
     7622.   6879                token_global.context[id] = the_variable;
    +
     7623.   6879            }
    +
     7624.   6879            the_variable.closure = true;
    +
     7625.   6879            functionage.context[id] = the_variable;
    +
     7626.  31370        }
    +
     7627.  31370        if (
    +
     7628.  31370            (
    +
     7629.  31370                the_variable.calls === undefined
    +
     7630.  31370                || functionage.name === undefined
    +
     7631.   4364                || the_variable.calls[functionage.name.id] === undefined
    +
     7632.  31509            )
    +
     7633.  31185            && the_variable.dead
    +
     7634.      3        ) {
    +
     7635.      3
    +
     7636.      3// test_cause:
    +
     7637.      3// ["let aa;if(aa){let bb;}bb;", "lookup", "out_of_scope_a", "bb", 23]
    +
     7638.      3
    +
     7639.      3            warn("out_of_scope_a", thing);
    +
     7640.  31370        }
    +
     7641.  31370        return the_variable;
    +
     7642.  31370    }
    +
     7643.    518
    +
     7644.   5009    function post_a(thing) {
    +
     7645.   5009
    +
     7646.   5009// Assignment using = sets the init property of a variable. No other assignment
    +
     7647.   5009// operator can do this. A = token keeps that variable (or array of variables
    +
     7648.   5009// in case of destructuring) in its name property.
    +
     7649.   5009
    +
     7650.   5009        const lvalue = thing.expression[0];
    +
     7651.   5009        let right;
    +
     7652.   4234        if (thing.id === "=") {
    +
     7653.   4234            if (thing.names !== undefined) {
    +
     7654.   4234
    +
     7655.   4234// test_cause:
    +
     7656.   4234// ["if(0){aa=0}", "post_a", "=", "", 0]
    +
     7657.   4234
    +
     7658.   4234                test_cause("=");
    +
     7659.   4234
    +
     7660.   4234// Probably deadcode.
    +
     7661.   4234// if (Array.isArray(thing.names)) {
    +
     7662.   4234//     thing.names.forEach(init_variable);
    +
     7663.   4234// } else {
    +
     7664.   4234//     init_variable(thing.names);
    +
     7665.   4234// }
    +
     7666.   4234
    +
     7667.   4234                jslint_assert(
    +
     7668.   4234                    !Array.isArray(thing.names),
    +
     7669.   4234                    `Expected !Array.isArray(thing.names).`
    +
     7670.   4234                );
    +
     7671.   4234                init_variable(thing.names);
    +
     7672.   4234            } else {
    +
     7673.   4234                if (lvalue.id === "[" || lvalue.id === "{") {
    +
     7674.   4234                    lvalue.expression.forEach(function (thing) {
    +
     7675.   4234                        if (thing.variable) {
    +
     7676.   4234                            thing.variable.init = true;
    +
     7677.   4234                        }
    +
     7678.   4234                    });
    +
     7679.   4234                } else if (
    +
     7680.   4234                    lvalue.id === "."
    +
     7681.   4234                    && thing.expression[1].id === "undefined"
    +
     7682.   4234                ) {
    +
     7683.   4234
    +
     7684.   4234// test_cause:
    +
     7685.   4234// ["aa.aa=undefined", "post_a", "expected_a_b", "undefined", 1]
    +
     7686.   4234
    +
     7687.   4234                    warn(
    +
     7688.   4234                        "expected_a_b",
    +
     7689.   4234                        lvalue.expression,
    +
     7690.   4234                        "delete",
    +
     7691.   4234                        "undefined"
    +
     7692.   4234                    );
    +
     7693.   4234                }
    +
     7694.   4234            }
    +
     7695.   4234        } else {
    +
     7696.    775            if (lvalue.arity === "variable") {
    +
     7697.    775                if (!lvalue.variable || lvalue.variable.readonly) {
    +
     7698.    775                    warn("bad_assignment_a", lvalue);
    +
     7699.    775                }
    +
     7700.    775            }
    +
     7701.    775            right = syntax_dict[thing.expression[1].id];
    +
     7702.    775            if (
    +
     7703.    775                right !== undefined
    +
     7704.    775                && (
    +
     7705.    775                    right.id === "function"
    +
     7706.    775                    || right.id === "=>"
    +
     7707.    775                    || (
    +
     7708.    775                        right.constant
    +
     7709.    775                        && right.id !== "(number)"
    +
     7710.    775                        && (right.id !== "(string)" || thing.id !== "+=")
    +
     7711.    775                    )
    +
     7712.    775                )
    +
     7713.    775            ) {
    +
     7714.    775
    +
     7715.    775// test_cause:
    +
     7716.    775// ["aa+=undefined", "post_a", "unexpected_a", "undefined", 5]
    +
     7717.    775
    +
     7718.    775                warn("unexpected_a", thing.expression[1]);
    +
     7719.    775            }
    +
     7720.    775        }
    +
     7721.   5009    }
    +
     7722.    518
    +
     7723.    706    function post_a_pluseq(thing) {
    +
     7724.    706        const right = thing.expression[1];
    +
     7725.    396        if (right.constant) {
    +
     7726.    396            if (
    +
     7727.    396                right.value === ""
    +
     7728.    396                || (right.id === "(number)" && right.value === "0")
    +
     7729.    396                || right.id === "(boolean)"
    +
     7730.    396                || right.id === "null"
    +
     7731.    396                || right.id === "undefined"
    +
     7732.    396                || Number.isNaN(right.value)
    +
     7733.    396            ) {
    +
     7734.    396                warn("unexpected_a", right);
    +
     7735.    396            }
    +
     7736.    396        }
    +
     7737.    706    }
    +
     7738.    518
    +
     7739.  31933    function post_b(thing) {
    +
     7740.  31933        let right;
    +
     7741.   3960        if (relationop[thing.id]) {
    +
     7742.   3960            if (
    +
     7743.   3960                is_weird(thing.expression[0])
    +
     7744.   3960                || is_weird(thing.expression[1])
    +
     7745.   3960                || is_equal(thing.expression[0], thing.expression[1])
    +
     7746.   3960                || (
    +
     7747.   3960                    thing.expression[0].constant === true
    +
     7748.   3960                    && thing.expression[1].constant === true
    +
     7749.   3960                )
    +
     7750.   3960            ) {
    +
     7751.   3960
    +
     7752.   3960// test_cause:
    +
     7753.   3960// ["if(0===0){0}", "post_b", "weird_relation_a", "===", 5]
    +
     7754.   3960
    +
     7755.   3960                warn("weird_relation_a", thing);
    +
     7756.   3960            }
    +
     7757.   3960        }
    +
     7758.   2023        if (thing.id === "+") {
    +
     7759.   2023            if (!option_dict.convert) {
    +
     7760.   2023                if (thing.expression[0].value === "") {
    +
     7761.   2023
    +
     7762.   2023// test_cause:
    +
     7763.   2023// ["\"\"+0", "post_b", "expected_a_b", "\"\" +", 3]
    +
     7764.   2023
    +
     7765.   2023                    warn("expected_a_b", thing, "String(...)", "\"\" +");
    +
     7766.   2023                } else if (thing.expression[1].value === "") {
    +
     7767.   2023
    +
     7768.   2023// test_cause:
    +
     7769.   2023// ["0+\"\"", "post_b", "expected_a_b", "+ \"\"", 2]
    +
     7770.   2023
    +
     7771.   2023                    warn("expected_a_b", thing, "String(...)", "+ \"\"");
    +
     7772.   2023                }
    +
     7773.   2023            }
    +
     7774.  29910        } else if (thing.id === "[") {
    +
     7775.  29910            if (thing.expression[0].id === "window") {
    +
     7776.  29910
    +
     7777.  29910// test_cause:
    +
     7778.  29910// ["aa=window[0]", "post_b", "weird_expression_a", "window[...]", 10]
    +
     7779.  29910
    +
     7780.  29910                warn("weird_expression_a", thing, "window[...]");
    +
     7781.  29910            }
    +
     7782.  29910            if (thing.expression[0].id === "self") {
    +
     7783.  29910
    +
     7784.  29910// test_cause:
    +
     7785.  29910// ["aa=self[0]", "post_b", "weird_expression_a", "self[...]", 8]
    +
     7786.  29910
    +
     7787.  29910                warn("weird_expression_a", thing, "self[...]");
    +
     7788.  29910            }
    +
     7789.  29910        } else if (thing.id === "." || thing.id === "?.") {
    +
     7790.  29910            if (thing.expression.id === "RegExp") {
    +
     7791.  29910
    +
     7792.  29910// test_cause:
    +
     7793.  29910// ["aa=RegExp.aa", "post_b", "weird_expression_a", ".", 10]
    +
     7794.  29910
    +
     7795.  29910                warn("weird_expression_a", thing);
    +
     7796.  29910            }
    +
     7797.  29910        } else if (thing.id !== "=>" && thing.id !== "(") {
    +
     7798.  29910            right = thing.expression[1];
    +
     7799.  29910            if (
    +
     7800.  29910                (thing.id === "+" || thing.id === "-")
    +
     7801.  29910                && right.id === thing.id
    +
     7802.  29910                && right.arity === "unary"
    +
     7803.  29910                && !right.wrapped
    +
     7804.  29910            ) {
    +
     7805.  29910
    +
     7806.  29910// test_cause:
    +
     7807.  29910// ["0- -0", "post_b", "wrap_unary", "-", 4]
    +
     7808.  29910
    +
     7809.  29910                warn("wrap_unary", right);
    +
     7810.  29910            }
    +
     7811.  29910            if (
    +
     7812.  29910                thing.expression[0].constant === true
    +
     7813.  29910                && right.constant === true
    +
     7814.  29910            ) {
    +
     7815.  29910                thing.constant = true;
    +
     7816.  29910            }
    +
     7817.  29910        }
    +
     7818.  31933    }
    +
     7819.    518
    +
     7820.   1202    function post_b_and(thing) {
    +
     7821.   1202        if (
    +
     7822.   1202            is_weird(thing.expression[0])
    +
     7823.   1197            || is_equal(thing.expression[0], thing.expression[1])
    +
     7824.   1183            || thing.expression[0].constant === true
    +
     7825.   1181            || thing.expression[1].constant === true
    +
     7826.     21        ) {
    +
     7827.     21
    +
     7828.     21// test_cause:
    +
     7829.     21// ["aa=(()=>0)&&(()=>0)", "post_b_and", "weird_condition_a", "&&", 11]
    +
     7830.     21// ["aa=(``?``:``)&&(``?``:``)", "post_b_and", "weird_condition_a", "&&", 14]
    +
     7831.     21// ["aa=/./&&/./", "post_b_and", "weird_condition_a", "&&", 7]
    +
     7832.     21// ["aa=0&&0", "post_b_and", "weird_condition_a", "&&", 5]
    +
     7833.     21// ["aa=[]&&[]", "post_b_and", "weird_condition_a", "&&", 6]
    +
     7834.     21// ["aa=`${0}`&&`${0}`", "post_b_and", "weird_condition_a", "&&", 10]
    +
     7835.     21// ["
    +
     7836.     21// aa=function aa(){}&&function aa(){}
    +
     7837.     21// ", "post_b_and", "weird_condition_a", "&&", 19]
    +
     7838.     21// ["aa={}&&{}", "post_b_and", "weird_condition_a", "&&", 6]
    +
     7839.     21
    +
     7840.     21            warn("weird_condition_a", thing);
    +
     7841.     21        }
    +
     7842.   1202    }
    +
     7843.    518
    +
     7844.   1460    function post_b_lbracket(thing) {
    +
     7845.      1        if (thing.expression[0].id === "RegExp") {
    +
     7846.      1
    +
     7847.      1// test_cause:
    +
     7848.      1// ["aa=RegExp[0]", "post_b_lbracket", "weird_expression_a", "[", 10]
    +
     7849.      1
    +
     7850.      1            warn("weird_expression_a", thing);
    +
     7851.      1        }
    +
     7852.      1        if (is_weird(thing.expression[1])) {
    +
     7853.      1
    +
     7854.      1// test_cause:
    +
     7855.      1// ["aa[[0]]", "post_b_lbracket", "weird_expression_a", "[", 4]
    +
     7856.      1
    +
     7857.      1            warn("weird_expression_a", thing.expression[1]);
    +
     7858.      1        }
    +
     7859.   1460    }
    +
     7860.    518
    +
     7861.  10411    function post_b_lparen(thing) {
    +
     7862.  10411        let arg;
    +
     7863.  10411        let array;
    +
     7864.  10411        let cack;
    +
     7865.  10411        let left = thing.expression[0];
    +
     7866.  10411        let new_date;
    +
     7867.  10411        let paren;
    +
     7868.  10411        let the_new;
    +
     7869.    154        if (left.id === "new") {
    +
     7870.    154            the_new = left;
    +
     7871.    154            left = left.expression;
    +
     7872.    154        }
    +
     7873.     37        if (left.id === "function") {
    +
     7874.     37            if (!thing.wrapped) {
    +
     7875.     37
    +
     7876.     37// test_cause:
    +
     7877.     37// ["aa=function(){}()", "post_b_lparen", "wrap_immediate", "(", 16]
    +
     7878.     37
    +
     7879.     37                warn("wrap_immediate", thing);
    +
     7880.     37            }
    +
     7881.  10374        } else if (left.identifier) {
    +
     7882.  10374            if (the_new !== undefined) {
    +
     7883.  10374                if (
    +
     7884.  10374                    left.id[0] > "Z"
    +
     7885.  10374                    || left.id === "BigInt"
    +
     7886.  10374                    || left.id === "Boolean"
    +
     7887.  10374                    || left.id === "Number"
    +
     7888.  10374                    || left.id === "String"
    +
     7889.  10374                    || left.id === "Symbol"
    +
     7890.  10374                ) {
    +
     7891.  10374
    +
     7892.  10374// test_cause:
    +
     7893.  10374// ["new BigInt()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7894.  10374// ["new Boolean()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7895.  10374// ["new Number()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7896.  10374// ["new String()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7897.  10374// ["new Symbol()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7898.  10374// ["new aa()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7899.  10374
    +
     7900.  10374                    warn("unexpected_a", the_new);
    +
     7901.  10374                } else if (left.id === "Function") {
    +
     7902.  10374                    if (!option_dict.eval) {
    +
     7903.  10374
    +
     7904.  10374// test_cause:
    +
     7905.  10374// ["new Function()", "post_b_lparen", "unexpected_a", "new Function", 5]
    +
     7906.  10374
    +
     7907.  10374                        warn("unexpected_a", left, "new Function");
    +
     7908.  10374                    }
    +
     7909.  10374                } else if (left.id === "Array") {
    +
     7910.  10374                    arg = thing.expression;
    +
     7911.  10374                    if (arg.length !== 2 || arg[1].id === "(string)") {
    +
     7912.  10374
    +
     7913.  10374// test_cause:
    +
     7914.  10374// ["new Array()", "post_b_lparen", "expected_a_b", "new Array", 5]
    +
     7915.  10374
    +
     7916.  10374                        warn("expected_a_b", left, "[]", "new Array");
    +
     7917.  10374                    }
    +
     7918.  10374                } else if (left.id === "Object") {
    +
     7919.  10374
    +
     7920.  10374// test_cause:
    +
     7921.  10374// ["new Object()", "post_b_lparen", "expected_a_b", "new Object", 5]
    +
     7922.  10374
    +
     7923.  10374                    warn(
    +
     7924.  10374                        "expected_a_b",
    +
     7925.  10374                        left,
    +
     7926.  10374                        "Object.create(null)",
    +
     7927.  10374                        "new Object"
    +
     7928.  10374                    );
    +
     7929.  10374                }
    +
     7930.  10374            } else {
    +
     7931.  10374                if (
    +
     7932.  10374                    left.id[0] >= "A"
    +
     7933.  10374                    && left.id[0] <= "Z"
    +
     7934.  10374                    && left.id !== "BigInt"
    +
     7935.  10374                    && left.id !== "Boolean"
    +
     7936.  10374                    && left.id !== "Number"
    +
     7937.  10374                    && left.id !== "String"
    +
     7938.  10374                    && left.id !== "Symbol"
    +
     7939.  10374                ) {
    +
     7940.  10374
    +
     7941.  10374// test_cause:
    +
     7942.  10374// ["let Aa=Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8]
    +
     7943.  10374
    +
     7944.  10374                    warn("expected_a_before_b", left, "new", artifact(left));
    +
     7945.  10374                }
    +
     7946.  10374            }
    +
     7947.  10374        } else if (left.id === ".") {
    +
     7948.  10374            cack = the_new !== undefined;
    +
     7949.  10374            if (left.expression.id === "Date" && left.name.id === "UTC") {
    +
     7950.  10374
    +
     7951.  10374// test_cause:
    +
     7952.  10374// ["new Date.UTC()", "post_b_lparen", "cack", "", 0]
    +
     7953.  10374
    +
     7954.  10374                test_cause("cack");
    +
     7955.  10374                cack = !cack;
    +
     7956.  10374            }
    +
     7957.  10374            if (jslint_rgx_cap.test(left.name.id) !== cack) {
    +
     7958.  10374                if (the_new !== undefined) {
    +
     7959.  10374
    +
     7960.  10374// test_cause:
    +
     7961.  10374// ["new Date.UTC()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7962.  10374
    +
     7963.  10374                    warn("unexpected_a", the_new);
    +
     7964.  10374                } else {
    +
     7965.  10374
    +
     7966.  10374// test_cause:
    +
     7967.  10374// ["let Aa=Aa.Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8]
    +
     7968.  10374
    +
     7969.  10374                    warn(
    +
     7970.  10374                        "expected_a_before_b",
    +
     7971.  10374                        left.expression,
    +
     7972.  10374                        "new",
    +
     7973.  10374                        left.name.id
    +
     7974.  10374                    );
    +
     7975.  10374                }
    +
     7976.  10374            }
    +
     7977.  10374            if (left.name.id === "getTime") {
    +
     7978.  10374                paren = left.expression;
    +
     7979.  10374                if (paren.id === "(") {
    +
     7980.  10374                    array = paren.expression;
    +
     7981.  10374                    if (array.length === 1) {
    +
     7982.  10374                        new_date = array[0];
    +
     7983.  10374                        if (
    +
     7984.  10374                            new_date.id === "new"
    +
     7985.  10374                            && new_date.expression.id === "Date"
    +
     7986.  10374                        ) {
    +
     7987.  10374
    +
     7988.  10374// test_cause:
    +
     7989.  10374// ["
    +
     7990.  10374// new Date().getTime()
    +
     7991.  10374// ", "post_b_lparen", "expected_a_b", "new Date().getTime()", 1]
    +
     7992.  10374
    +
     7993.  10374                            warn(
    +
     7994.  10374                                "expected_a_b",
    +
     7995.  10374                                new_date,
    +
     7996.  10374                                "Date.now()",
    +
     7997.  10374                                "new Date().getTime()"
    +
     7998.  10374                            );
    +
     7999.  10374                        }
    +
     8000.  10374                    }
    +
     8001.  10374                }
    +
     8002.  10374            }
    +
     8003.  10374        }
    +
     8004.  10411    }
    +
     8005.    518
    +
     8006.    974    function post_b_or(thing) {
    +
     8007.    974        if (
    +
     8008.    974            is_weird(thing.expression[0])
    +
     8009.    974            || is_equal(thing.expression[0], thing.expression[1])
    +
     8010.    973            || thing.expression[0].constant === true
    +
     8011.      2        ) {
    +
     8012.      2
    +
     8013.      2// test_cause:
    +
     8014.      2// ["aa=0||0", "post_b_or", "weird_condition_a", "||", 5]
    +
     8015.      2
    +
     8016.      2            warn("weird_condition_a", thing);
    +
     8017.      2        }
    +
     8018.    974    }
    +
     8019.    518
    +
     8020.     78    function post_s_export(the_thing) {
    +
     8021.     78
    +
     8022.     78// Some features must be at the most outermost level.
    +
     8023.     78
    +
     8024.      1        if (blockage !== token_global) {
    +
     8025.      1
    +
     8026.      1// test_cause:
    +
     8027.      1// ["
    +
     8028.      1// if(0){import aa from "aa";}
    +
     8029.      1// ", "post_s_export", "misplaced_a", "import", 7]
    +
     8030.      1
    +
     8031.      1            warn("misplaced_a", the_thing);
    +
     8032.      1        }
    +
     8033.     78    }
    +
     8034.    518
    +
     8035.      8    function post_s_for(thing) {
    +
     8036.      8
    +
     8037.      8// Recurse walk_statement().
    +
     8038.      8
    +
     8039.      8        walk_statement(thing.inc);
    +
     8040.      8    }
    +
     8041.    518
    +
     8042.   2001    function post_s_function(thing) {
    +
     8043.   2001        delete functionage.async;
    +
     8044.   2001        delete functionage.finally;
    +
     8045.   2001        delete functionage.loop;
    +
     8046.   2001        delete functionage.statement_prv;
    +
     8047.   2001        delete functionage.switch;
    +
     8048.   2001        delete functionage.try;
    +
     8049.   2001        functionage = function_stack.pop();
    +
     8050.      3        if (thing.wrapped) {
    +
     8051.      3
    +
     8052.      3// test_cause:
    +
     8053.      3// ["aa=(function(){})", "post_s_function", "unexpected_parens", "function", 5]
    +
     8054.      3
    +
     8055.      3            warn("unexpected_parens", thing);
    +
     8056.      3        }
    +
     8057.   2001        return post_s_lbrace();
    +
     8058.   2001    }
    +
     8059.    518
    +
     8060.     61    function post_s_import(the_thing) {
    +
     8061.     61        const name = the_thing.name;
    +
     8062.     60        if (name) {
    +
     8063.     60            if (Array.isArray(name)) {
    +
     8064.     60                name.forEach(function (name) {
    +
     8065.     60                    name.dead = false;
    +
     8066.     60                    name.init = true;
    +
     8067.     60                    blockage.live.push(name);
    +
     8068.     60                });
    +
     8069.     60            } else {
    +
     8070.     60                name.dead = false;
    +
     8071.     60                name.init = true;
    +
     8072.     60                blockage.live.push(name);
    +
     8073.     60            }
    +
     8074.     60            return post_s_export(the_thing);
    +
     8075.     60        }
    +
     8076.     61    }
    +
     8077.    518
    +
     8078.   7695    function post_s_lbrace() {
    +
     8079.   2987        blockage.live.forEach(function (name) {
    +
     8080.   2987            name.dead = true;
    +
     8081.   2987        });
    +
     8082.   7695        delete blockage.live;
    +
     8083.   7695        blockage = block_stack.pop();
    +
     8084.   7695    }
    +
     8085.    518
    +
     8086.     56    function post_s_try(thing) {
    +
     8087.     54        if (thing.catch) {
    +
     8088.     54            if (thing.catch.name) {
    +
     8089.     54                Object.assign(catchage.context[thing.catch.name.id], {
    +
     8090.     54                    dead: false,
    +
     8091.     54                    init: true
    +
     8092.     54                });
    +
     8093.     54            }
    +
     8094.     54
    +
     8095.     54// Recurse walk_statement().
    +
     8096.     54
    +
     8097.     54            walk_statement(thing.catch.block);
    +
     8098.     54
    +
     8099.     54// Restore previous catch-scope after catch-block.
    +
     8100.     54
    +
     8101.     54            catchage = catch_stack.pop();
    +
     8102.     54        }
    +
     8103.     56    }
    +
     8104.    518
    +
     8105.   2320    function post_s_var(thing) {
    +
     8106.   2635        thing.names.forEach(function (name) {
    +
     8107.   2635            name.dead = false;
    +
     8108.   1250            if (name.expression !== undefined) {
    +
     8109.   1250                walk_expression(name.expression);
    +
     8110.   1250
    +
     8111.   1250// Probably deadcode.
    +
     8112.   1250// if (name.id === "{" || name.id === "[") {
    +
     8113.   1250//     name.names.forEach(subactivate);
    +
     8114.   1250// } else {
    +
     8115.   1250//     name.init = true;
    +
     8116.   1250// }
    +
     8117.   1250
    +
     8118.   1250                jslint_assert(
    +
     8119.   1250                    !(name.id === "{" || name.id === "["),
    +
     8120.   1250                    `Expected !(name.id === "{" || name.id === "[").`
    +
     8121.   1250                );
    +
     8122.   1250                name.init = true;
    +
     8123.   1250            }
    +
     8124.   2635            blockage.live.push(name);
    +
     8125.   2635        });
    +
     8126.   2320    }
    +
     8127.    518
    +
     8128.    213    function post_t(thing) {
    +
     8129.    213        if (
    +
     8130.    213            is_weird(thing.expression[0])
    +
     8131.    213            || thing.expression[0].constant === true
    +
     8132.    206            || is_equal(thing.expression[1], thing.expression[2])
    +
     8133.      9        ) {
    +
     8134.      9
    +
     8135.      9// test_cause:
    +
     8136.      9// ["let aa=(aa?`${0}`:`${0}`);", "post_t", "unexpected_a", "?", 11]
    +
     8137.      9// ["let aa=(aa?`0`:`0`);", "post_t", "unexpected_a", "?", 11]
    +
     8138.      9
    +
     8139.      9            warn("unexpected_a", thing);
    +
     8140.    204        } else if (is_equal(thing.expression[0], thing.expression[1])) {
    +
     8141.    204
    +
     8142.    204// test_cause:
    +
     8143.    204// ["aa?aa:0", "post_t", "expected_a_b", "?", 3]
    +
     8144.    204
    +
     8145.    204            warn("expected_a_b", thing, "||", "?");
    +
     8146.    204        } else if (is_equal(thing.expression[0], thing.expression[2])) {
    +
     8147.    204
    +
     8148.    204// test_cause:
    +
     8149.    204// ["aa?0:aa", "post_t", "expected_a_b", "?", 3]
    +
     8150.    204
    +
     8151.    204            warn("expected_a_b", thing, "&&", "?");
    +
     8152.    204        } else if (
    +
     8153.    204            thing.expression[1].id === "true"
    +
     8154.    204            && thing.expression[2].id === "false"
    +
     8155.    204        ) {
    +
     8156.    204
    +
     8157.    204// test_cause:
    +
     8158.    204// ["aa?true:false", "post_t", "expected_a_b", "?", 3]
    +
     8159.    204
    +
     8160.    204            warn("expected_a_b", thing, "!!", "?");
    +
     8161.    204        } else if (
    +
     8162.    204            thing.expression[1].id === "false"
    +
     8163.    204            && thing.expression[2].id === "true"
    +
     8164.    204        ) {
    +
     8165.    204
    +
     8166.    204// test_cause:
    +
     8167.    204// ["aa?false:true", "post_t", "expected_a_b", "?", 3]
    +
     8168.    204
    +
     8169.    204            warn("expected_a_b", thing, "!", "?");
    +
     8170.    204        } else if (
    +
     8171.    204            thing.expression[0].wrapped !== true
    +
     8172.    204            && (
    +
     8173.    204                thing.expression[0].id === "||"
    +
     8174.    204                || thing.expression[0].id === "&&"
    +
     8175.    204            )
    +
     8176.    204        ) {
    +
     8177.    204
    +
     8178.    204// test_cause:
    +
     8179.    204// ["(aa&&!aa?0:1)", "post_t", "wrap_condition", "&&", 4]
    +
     8180.    204
    +
     8181.    204            warn("wrap_condition", thing.expression[0]);
    +
     8182.    204        }
    +
     8183.    213    }
    +
     8184.    518
    +
     8185.   4106    function post_u(thing) {
    +
     8186.    779        if (thing.id === "`") {
    +
     8187.    779            if (thing.expression.every(function (thing) {
    +
     8188.    779                return thing.constant;
    +
     8189.    779            })) {
    +
     8190.    779                thing.constant = true;
    +
     8191.    779            }
    +
     8192.   3327        } else if (thing.id === "!") {
    +
     8193.   3327            if (thing.expression.constant === true) {
    +
     8194.   3327                warn("unexpected_a", thing);
    +
     8195.   3327            }
    +
     8196.   3327        } else if (thing.id === "!!") {
    +
     8197.   3327            if (!option_dict.convert) {
    +
     8198.   3327
    +
     8199.   3327// test_cause:
    +
     8200.   3327// ["!!0", "post_u", "expected_a_b", "!!", 1]
    +
     8201.   3327
    +
     8202.   3327                warn("expected_a_b", thing, "Boolean(...)", "!!");
    +
     8203.   3327            }
    +
     8204.   3327        } else if (
    +
     8205.   3327            thing.id !== "["
    +
     8206.   3327            && thing.id !== "{"
    +
     8207.   3327            && thing.id !== "function"
    +
     8208.   3327            && thing.id !== "new"
    +
     8209.   3327        ) {
    +
     8210.   3327            if (thing.expression.constant === true) {
    +
     8211.   3327                thing.constant = true;
    +
     8212.   3327            }
    +
     8213.   3327        }
    +
     8214.   4106    }
    +
     8215.    518
    +
     8216.      7    function post_u_plus(thing) {
    +
     8217.      7        const right = thing.expression;
    +
     8218.      7        if (!option_dict.convert) {
    +
     8219.      7
    +
     8220.      7// test_cause:
    +
     8221.      7// ["aa=+0", "post_u_plus", "expected_a_b", "+", 4]
    +
     8222.      7
    +
     8223.      7            warn("expected_a_b", thing, "Number(...)", "+");
    +
     8224.      7        }
    +
     8225.      1        if (right.id === "(" && right.expression[0].id === "new") {
    +
     8226.      1            warn("unexpected_a_before_b", thing, "+", "new");
    +
     8227.      6        } else if (
    +
     8228.      6            right.constant
    +
     8229.      6            || right.id === "{"
    +
     8230.      6            || (right.id === "[" && right.arity !== "binary")
    +
     8231.      6        ) {
    +
     8232.      6            warn("unexpected_a", thing, "+");
    +
     8233.      6        }
    +
     8234.      7    }
    +
     8235.    518
    +
     8236.  36945    function pre_a_bitwise(thing) {
    +
     8237.  36945
    +
     8238.  36945// These are the bitwise operators.
    +
     8239.  36945
    +
     8240.  36943        switch (!option_dict.bitwise && thing.id) {
    +
     8241.      2        case "&":
    +
     8242.      3        case "&=":
    +
     8243.      5        case "<<":
    +
     8244.      6        case "<<=":
    +
     8245.      8        case ">>":
    +
     8246.      9        case ">>=":
    +
     8247.     11        case ">>>":
    +
     8248.     12        case ">>>=":
    +
     8249.     14        case "^":
    +
     8250.     15        case "^=":
    +
     8251.     17        case "|":
    +
     8252.     18        case "|=":
    +
     8253.     21        case "~":
    +
     8254.     21
    +
     8255.     21// test_cause:
    +
     8256.     21// ["0&0", "pre_a_bitwise", "unexpected_a", "&", 2]
    +
     8257.     21// ["0&=0", "pre_a_bitwise", "unexpected_a", "&=", 2]
    +
     8258.     21// ["0<<0", "pre_a_bitwise", "unexpected_a", "<<", 2]
    +
     8259.     21// ["0<<=0", "pre_a_bitwise", "unexpected_a", "<<=", 2]
    +
     8260.     21// ["0>>0", "pre_a_bitwise", "unexpected_a", ">>", 2]
    +
     8261.     21// ["0>>=0", "pre_a_bitwise", "unexpected_a", ">>=", 2]
    +
     8262.     21// ["0>>>0", "pre_a_bitwise", "unexpected_a", ">>>", 2]
    +
     8263.     21// ["0>>>=0", "pre_a_bitwise", "unexpected_a", ">>>=", 2]
    +
     8264.     21// ["0^0", "pre_a_bitwise", "unexpected_a", "^", 2]
    +
     8265.     21// ["0^=0", "pre_a_bitwise", "unexpected_a", "^=", 2]
    +
     8266.     21// ["0|0", "pre_a_bitwise", "unexpected_a", "|", 2]
    +
     8267.     21// ["0|=0", "pre_a_bitwise", "unexpected_a", "|=", 2]
    +
     8268.     21// ["~0", "pre_a_bitwise", "unexpected_a", "~", 1]
    +
     8269.     21
    +
     8270.     21            warn("unexpected_a", thing);
    +
     8271.     21            break;
    +
     8272.  36945        }
    +
     8273.  36945        if (
    +
     8274.  36945            thing.id !== "("
    +
     8275.  26534            && thing.id !== "&&"
    +
     8276.  25332            && thing.id !== "||"
    +
     8277.  24358            && thing.id !== "="
    +
     8278.  20124            && Array.isArray(thing.expression)
    +
     8279.   8506            && thing.expression.length === 2
    +
     8280.   8505            && (
    +
     8281.   8505                relationop[thing.expression[0].id] === true
    +
     8282.   8505                || relationop[thing.expression[1].id] === true
    +
     8283.   8505            )
    +
     8284.      1        ) {
    +
     8285.      1
    +
     8286.      1// test_cause:
    +
     8287.      1// ["0<0<0", "pre_a_bitwise", "unexpected_a", "<", 4]
    +
     8288.      1
    +
     8289.      1            warn("unexpected_a", thing);
    +
     8290.      1        }
    +
     8291.  36945    }
    +
     8292.    518
    +
     8293.  31933    function pre_b(thing) {
    +
     8294.  31933        let left;
    +
     8295.  31933        let right;
    +
     8296.  31933        let value;
    +
     8297.   3960        if (relationop[thing.id] === true) {
    +
     8298.   3960            left = thing.expression[0];
    +
     8299.   3960            right = thing.expression[1];
    +
     8300.   3960            if (left.id === "NaN" || right.id === "NaN") {
    +
     8301.   3960
    +
     8302.   3960// test_cause:
    +
     8303.   3960// ["NaN===NaN", "pre_b", "number_isNaN", "===", 4]
    +
     8304.   3960
    +
     8305.   3960                warn("number_isNaN", thing);
    +
     8306.   3960            } else if (left.id === "typeof") {
    +
     8307.   3960                if (right.id !== "(string)") {
    +
     8308.   3960                    if (right.id !== "typeof") {
    +
     8309.   3960
    +
     8310.   3960// test_cause:
    +
     8311.   3960// ["typeof 0===0", "pre_b", "expected_string_a", "0", 12]
    +
     8312.   3960
    +
     8313.   3960                        warn("expected_string_a", right);
    +
     8314.   3960                    }
    +
     8315.   3960                } else {
    +
     8316.   3960                    value = right.value;
    +
     8317.   3960                    if (value === "null" || value === "undefined") {
    +
     8318.   3960
    +
     8319.   3960// test_cause:
    +
     8320.   3960// ["
    +
     8321.   3960// typeof aa==="undefined"
    +
     8322.   3960// ", "pre_b", "unexpected_typeof_a", "undefined", 13]
    +
     8323.   3960
    +
     8324.   3960                        warn("unexpected_typeof_a", right, value);
    +
     8325.   3960                    } else if (
    +
     8326.   3960                        value !== "bigint"
    +
     8327.   3960                        && value !== "boolean"
    +
     8328.   3960                        && value !== "function"
    +
     8329.   3960                        && value !== "number"
    +
     8330.   3960                        && value !== "object"
    +
     8331.   3960                        && value !== "string"
    +
     8332.   3960                        && value !== "symbol"
    +
     8333.   3960                    ) {
    +
     8334.   3960
    +
     8335.   3960// test_cause:
    +
     8336.   3960// ["typeof 0===\"aa\"", "pre_b", "expected_type_string_a", "aa", 12]
    +
     8337.   3960
    +
     8338.   3960                        warn("expected_type_string_a", right, value);
    +
     8339.   3960                    }
    +
     8340.   3960                }
    +
     8341.   3960            }
    +
     8342.   3960        }
    +
     8343.  31933    }
    +
     8344.    518
    +
     8345.      1    function pre_b_eqeq(thing) {
    +
     8346.      1
    +
     8347.      1// test_cause:
    +
     8348.      1// ["0==0", "pre_b_eqeq", "expected_a_b", "==", 2]
    +
     8349.      1
    +
     8350.      1        warn("expected_a_b", thing, "===", "==");
    +
     8351.      1    }
    +
     8352.    518
    +
     8353.      1    function pre_b_in(thing) {
    +
     8354.      1
    +
     8355.      1// test_cause:
    +
     8356.      1// ["aa in aa", "pre_b_in", "infix_in", "in", 4]
    +
     8357.      1
    +
     8358.      1        warn("infix_in", thing);
    +
     8359.      1    }
    +
     8360.    518
    +
     8361.      1    function pre_b_instanceof(thing) {
    +
     8362.      1
    +
     8363.      1// test_cause:
    +
     8364.      1// ["0 instanceof 0", "pre_b_instanceof", "unexpected_a", "instanceof", 3]
    +
     8365.      1
    +
     8366.      1        warn("unexpected_a", thing);
    +
     8367.      1    }
    +
     8368.    518
    +
     8369.  10411    function pre_b_lparen(thing) {
    +
     8370.  10411        const left = thing.expression[0];
    +
     8371.  10411        let left_variable;
    +
     8372.  10411        let parent;
    +
     8373.  10411        if (
    +
     8374.  10411            left.identifier
    +
     8375.   6658            && functionage.context[left.id] === undefined
    +
     8376.   3034            && typeof functionage.name === "object"
    +
     8377.   2395        ) {
    +
     8378.   2395            parent = functionage.name.parent;
    +
     8379.   2395            if (parent) {
    +
     8380.   2395                left_variable = parent.context[left.id];
    +
     8381.   2395                if (
    +
     8382.   2395                    left_variable !== undefined
    +
     8383.   2395
    +
     8384.   2395// Probably deadcode.
    +
     8385.   2395// && left_variable.dead
    +
     8386.   2395
    +
     8387.   2395                    && left_variable.parent === parent
    +
     8388.   2395                    && left_variable.calls !== undefined
    +
     8389.   2395                    && left_variable.calls[functionage.name.id] !== undefined
    +
     8390.   2395                ) {
    +
     8391.   2395                    left_variable.dead = false;
    +
     8392.   2395                }
    +
     8393.   2395            }
    +
     8394.   2395        }
    +
     8395.  10411    }
    +
     8396.    518
    +
     8397.      1    function pre_b_noteq(thing) {
    +
     8398.      1
    +
     8399.      1// test_cause:
    +
     8400.      1// ["0!=0", "pre_b_noteq", "expected_a_b", "!=", 2]
    +
     8401.      1
    +
     8402.      1        warn("expected_a_b", thing, "!==", "!=");
    +
     8403.      1    }
    +
     8404.    518
    +
     8405.    974    function pre_b_or(thing) {
    +
     8406.   1948        thing.expression.forEach(function (thang) {
    +
     8407.    177            if (thang.id === "&&" && !thang.wrapped) {
    +
     8408.      1
    +
     8409.      1// test_cause:
    +
     8410.      1// ["0&&0||0", "pre_b_or", "and", "&&", 2]
    +
     8411.      1
    +
     8412.      1                warn("and", thang);
    +
     8413.      1            }
    +
     8414.   1948        });
    +
     8415.    974    }
    +
     8416.    518
    +
     8417.      8    function pre_s_for(thing) {
    +
     8418.      8        let the_variable;
    +
     8419.      2        if (thing.name !== undefined) {
    +
     8420.      2            thing.name.dead = false;
    +
     8421.      2            the_variable = lookup(thing.name);
    +
     8422.      2            if (the_variable !== undefined) {
    +
     8423.      2                if (the_variable.init && the_variable.readonly) {
    +
     8424.      2
    +
     8425.      2// test_cause:
    +
     8426.      2// ["const aa=0;for(aa in aa){}", "pre_s_for", "bad_assignment_a", "aa", 16]
    +
     8427.      2
    +
     8428.      2                    warn("bad_assignment_a", thing.name);
    +
     8429.      2                }
    +
     8430.      2                the_variable.init = true;
    +
     8431.      2            }
    +
     8432.      2        }
    +
     8433.      8
    +
     8434.      8// Recurse walk_statement().
    +
     8435.      8
    +
     8436.      8        walk_statement(thing.initial);
    +
     8437.      8    }
    +
     8438.    518
    +
     8439.   2001    function pre_s_function(thing) {
    +
     8440.   2001
    +
     8441.   2001// test_cause:
    +
     8442.   2001// ["()=>0", "pre_s_function", "", "", 0]
    +
     8443.   2001// ["(function (){}())", "pre_s_function", "", "", 0]
    +
     8444.   2001// ["function aa(){}", "pre_s_function", "", "", 0]
    +
     8445.   2001
    +
     8446.   2001        test_cause("");
    +
     8447.   1057        if (thing.arity === "statement" && blockage.body !== true) {
    +
     8448.      1
    +
     8449.      1// test_cause:
    +
     8450.      1// ["if(0){function aa(){}\n}", "pre_s_function", "unexpected_a", "function", 7]
    +
     8451.      1
    +
     8452.      1            warn("unexpected_a", thing);
    +
     8453.      1        }
    +
     8454.   2001        function_stack.push(functionage);
    +
     8455.   2001        block_stack.push(blockage);
    +
     8456.   2001        functionage = thing;
    +
     8457.   2001        blockage = thing;
    +
     8458.   2001        thing.live = [];
    +
     8459.   1098        if (typeof thing.name === "object") {
    +
     8460.   1098            thing.name.dead = false;
    +
     8461.   1098            thing.name.init = true;
    +
     8462.   1098        }
    +
     8463.      7        if (thing.extra === "get") {
    +
     8464.      7            if (thing.parameters.length !== 0) {
    +
     8465.      7
    +
     8466.      7// test_cause:
    +
     8467.      7// ["
    +
     8468.      7// /*jslint getset*/
    +
     8469.      7// aa={get aa(aa){}}
    +
     8470.      7// ", "pre_s_function", "bad_get", "function", 9]
    +
     8471.      7
    +
     8472.      7                warn("bad_get", thing);
    +
     8473.      7            }
    +
     8474.   1994        } else if (thing.extra === "set") {
    +
     8475.   1994            if (thing.parameters.length !== 1) {
    +
     8476.   1994
    +
     8477.   1994// test_cause:
    +
     8478.   1994// ["
    +
     8479.   1994// /*jslint getset*/
    +
     8480.   1994// aa={set aa(){}}
    +
     8481.   1994// ", "pre_s_function", "bad_set", "function", 9]
    +
     8482.   1994
    +
     8483.   1994                warn("bad_set", thing);
    +
     8484.   1994            }
    +
     8485.   1994        }
    +
     8486.   2063        thing.parameters.forEach(function (name) {
    +
     8487.   2063            walk_expression(name.expression);
    +
     8488.   1840            if (name.id === "{" || name.id === "[") {
    +
     8489.    267                name.names.forEach(subactivate);
    +
     8490.   1796            } else {
    +
     8491.   1796                name.dead = false;
    +
     8492.   1796                name.init = true;
    +
     8493.   1796            }
    +
     8494.   2063        });
    +
     8495.   2001    }
    +
     8496.    518
    +
     8497.   5694    function pre_s_lbrace(thing) {
    +
     8498.   5694        block_stack.push(blockage);
    +
     8499.   5694        blockage = thing;
    +
     8500.   5694        thing.live = [];
    +
     8501.   5694    }
    +
     8502.    518
    +
     8503.     56    function pre_try(thing) {
    +
     8504.     54        if (thing.catch !== undefined) {
    +
     8505.     54
    +
     8506.     54// Create new catch-scope for catch-parameter.
    +
     8507.     54
    +
     8508.     54            catch_stack.push(catchage);
    +
     8509.     54            catchage = thing.catch;
    +
     8510.     54        }
    +
     8511.     56    }
    +
     8512.    518
    +
     8513.  28689    function pre_v(thing) {
    +
     8514.  28689        const the_variable = lookup(thing);
    +
     8515.  28611        if (the_variable !== undefined) {
    +
     8516.  28611            thing.variable = the_variable;
    +
     8517.  28611            the_variable.used += 1;
    +
     8518.  28611        }
    +
     8519.  28689    }
    +
     8520.    518
    +
     8521.    717    function subactivate(name) {
    +
     8522.    717        name.init = true;
    +
     8523.    717        name.dead = false;
    +
     8524.    717        blockage.live.push(name);
    +
     8525.    717    }
    +
     8526.    518
    +
     8527. 164534    function walk_expression(thing) {
    +
     8528. 103787        if (thing) {
    +
     8529. 103787            if (Array.isArray(thing)) {
    +
     8530. 103787
    +
     8531. 103787// test_cause:
    +
     8532. 103787// ["(function(){}())", "walk_expression", "isArray", "", 0]
    +
     8533. 103787// ["0&&0", "walk_expression", "isArray", "", 0]
    +
     8534. 103787
    +
     8535. 103787                test_cause("isArray");
    +
     8536. 103787                thing.forEach(walk_expression);
    +
     8537. 103787            } else {
    +
     8538. 103787                preamble(thing);
    +
     8539. 103787                walk_expression(thing.expression);
    +
     8540. 103787
    +
     8541. 103787// PR-414 - Bugfix - fix fart-body not being walked.
    +
     8542. 103787
    +
     8543. 103787                if (thing.id === "function" || thing.id === "=>") {
    +
     8544. 103787
    +
     8545. 103787// test_cause:
    +
     8546. 103787// ["aa=()=>0", "walk_expression", "function", "=>", 0]
    +
     8547. 103787// ["aa=function(){}", "walk_expression", "function", "function", 0]
    +
     8548. 103787
    +
     8549. 103787                    test_cause("function", thing.id);
    +
     8550. 103787
    +
     8551. 103787// Recurse walk_statement().
    +
     8552. 103787
    +
     8553. 103787                    walk_statement(thing.block);
    +
     8554. 103787                }
    +
     8555. 103787                if (
    +
     8556. 103787                    thing.arity === "preassign" || thing.arity === "postassign"
    +
     8557. 103787                ) {
    +
     8558. 103787
    +
     8559. 103787// test_cause:
    +
     8560. 103787// ["aa=++aa", "walk_expression", "unexpected_a", "++", 4]
    +
     8561. 103787// ["aa=--aa", "walk_expression", "unexpected_a", "--", 4]
    +
     8562. 103787
    +
     8563. 103787                    warn("unexpected_a", thing);
    +
     8564. 103787                } else if (
    +
     8565. 103787                    thing.arity === "statement"
    +
     8566. 103787                    || thing.arity === "assignment"
    +
     8567. 103787                ) {
    +
     8568. 103787
    +
     8569. 103787// test_cause:
    +
     8570. 103787// ["aa[aa=0]", "walk_expression", "unexpected_statement_a", "=", 6]
    +
     8571. 103787
    +
     8572. 103787                    warn("unexpected_statement_a", thing);
    +
     8573. 103787                }
    +
     8574. 103787
    +
     8575. 103787// test_cause:
    +
     8576. 103787// ["aa=0", "walk_expression", "default", "", 0]
    +
     8577. 103787
    +
     8578. 103787                test_cause("default");
    +
     8579. 103787                postamble(thing);
    +
     8580. 103787            }
    +
     8581. 103787        }
    +
     8582. 164534    }
    +
     8583.    518
    +
     8584.  78303    function walk_statement(thing) {
    +
     8585.  43095        if (!thing) {
    +
     8586.  43095            return;
    +
     8587.  43095        }
    +
     8588.  35208        if (Array.isArray(thing)) {
    +
     8589.   7608
    +
     8590.   7608// test_cause:
    +
     8591.   7608// ["+[]", "walk_statement", "isArray", "", 0]
    +
     8592.   7608
    +
     8593.   7608            test_cause("isArray");
    +
     8594.   7608
    +
     8595.   7608// Recurse walk_statement().
    +
     8596.   7608
    +
     8597.   7608            thing.forEach(walk_statement);
    +
     8598.   7608            return;
    +
     8599.  27600        }
    +
     8600.  27600        preamble(thing);
    +
     8601.  27600        walk_expression(thing.expression);
    +
     8602.  27600        if (thing.arity === "binary") {
    +
     8603.   5665            if (thing.id !== "(") {
    +
     8604.   5665
    +
     8605.   5665// test_cause:
    +
     8606.   5665// ["0&&0", "walk_statement", "unexpected_expression_a", "&&", 2]
    +
     8607.   5665
    +
     8608.   5665                warn("unexpected_expression_a", thing);
    +
     8609.   5665            }
    +
     8610.  21935        } else if (
    +
     8611.  21935            thing.arity !== "statement"
    +
     8612.  21935            && thing.arity !== "assignment"
    +
     8613.  21935            && thing.id !== "import"
    +
     8614.  21935        ) {
    +
     8615.  21935
    +
     8616.  21935// test_cause:
    +
     8617.  21935// ["!0", "walk_statement", "unexpected_expression_a", "!", 1]
    +
     8618.  21935// ["+[]", "walk_statement", "unexpected_expression_a", "+", 1]
    +
     8619.  21935// ["+new aa()", "walk_statement", "unexpected_expression_a", "+", 1]
    +
     8620.  21935// ["0", "walk_statement", "unexpected_expression_a", "0", 1]
    +
     8621.  21935// ["typeof 0", "walk_statement", "unexpected_expression_a", "typeof", 1]
    +
     8622.  21935
    +
     8623.  21935            warn("unexpected_expression_a", thing);
    +
     8624.  27600        }
    +
     8625.  27600
    +
     8626.  27600// Recurse walk_statement().
    +
     8627.  27600
    +
     8628.  27600        walk_statement(thing.block);
    +
     8629.  27600        walk_statement(thing.else);
    +
     8630.  27600        postamble(thing);
    +
     8631.  27600    }
    +
     8632.    518
    +
     8633.    518    postaction = action(posts);
    +
     8634.    518    postamble = amble(posts);
    +
     8635.    518    preaction = action(pres);
    +
     8636.    518    preamble = amble(pres);
    +
     8637.    518    postaction("assignment", "+=", post_a_pluseq);
    +
     8638.    518    postaction("assignment", post_a);
    +
     8639.    518    postaction("binary", "&&", post_b_and);
    +
     8640.    518    postaction("binary", "(", post_b_lparen);
    +
     8641.    518    postaction("binary", "=>", post_s_function);
    +
     8642.    518    postaction("binary", "[", post_b_lbracket);
    +
     8643.    518    postaction("binary", "||", post_b_or);
    +
     8644.    518    postaction("binary", post_b);
    +
     8645.    518    postaction("statement", "const", post_s_var);
    +
     8646.    518    postaction("statement", "export", post_s_export);
    +
     8647.    518    postaction("statement", "for", post_s_for);
    +
     8648.    518    postaction("statement", "function", post_s_function);
    +
     8649.    518    postaction("statement", "import", post_s_import);
    +
     8650.    518    postaction("statement", "let", post_s_var);
    +
     8651.    518    postaction("statement", "try", post_s_try);
    +
     8652.    518    postaction("statement", "var", post_s_var);
    +
     8653.    518    postaction("statement", "{", post_s_lbrace);
    +
     8654.    518    postaction("ternary", post_t);
    +
     8655.    518    postaction("unary", "+", post_u_plus);
    +
     8656.    518    postaction("unary", "function", post_s_function);
    +
     8657.    518    postaction("unary", post_u);
    +
     8658.    518    preaction("assignment", pre_a_bitwise);
    +
     8659.    518    preaction("binary", "!=", pre_b_noteq);
    +
     8660.    518    preaction("binary", "(", pre_b_lparen);
    +
     8661.    518    preaction("binary", "==", pre_b_eqeq);
    +
     8662.    518    preaction("binary", "=>", pre_s_function);
    +
     8663.    518    preaction("binary", "in", pre_b_in);
    +
     8664.    518    preaction("binary", "instanceof", pre_b_instanceof);
    +
     8665.    518    preaction("binary", "||", pre_b_or);
    +
     8666.    518    preaction("binary", pre_b);
    +
     8667.    518    preaction("binary", pre_a_bitwise);
    +
     8668.    518    preaction("statement", "for", pre_s_for);
    +
     8669.    518    preaction("statement", "function", pre_s_function);
    +
     8670.    518    preaction("statement", "try", pre_try);
    +
     8671.    518    preaction("statement", "{", pre_s_lbrace);
    +
     8672.    518    preaction("unary", "function", pre_s_function);
    +
     8673.    518    preaction("unary", "~", pre_a_bitwise);
    +
     8674.    518    preaction("variable", pre_v);
    +
     8675.    518
    +
     8676.    518    walk_statement(state.token_tree);
    +
     8677.    518}
    +
     8678.      1
    +
     8679.    208function jslint_phase5_whitage(state) {
    +
     8680.    208
    +
     8681.    208// PHASE 5. Check whitespace between tokens in <token_list>.
    +
     8682.    208
    +
     8683.    208    let {
    +
     8684.    208        artifact,
    +
     8685.    208        catch_list,
    +
     8686.    208        function_list,
    +
     8687.    208        function_stack,
    +
     8688.    208        option_dict,
    +
     8689.    208        test_cause,
    +
     8690.    208        token_global,
    +
     8691.    208        token_list,
    +
     8692.    208        warn
    +
     8693.    208    } = state;
    +
     8694.    208    let closer = "(end)";
    +
     8695.    208    let free = false;
    +
     8696.    208
    +
     8697.    208// free = false
    +
     8698.    208
    +
     8699.    208// cause:
    +
     8700.    208// "()=>0"
    +
     8701.    208// "aa()"
    +
     8702.    208// "aa(0,0)"
    +
     8703.    208// "function(){}"
    +
     8704.    208
    +
     8705.    208// free = true
    +
     8706.    208
    +
     8707.    208// cause:
    +
     8708.    208// "(0)"
    +
     8709.    208// "(aa)"
    +
     8710.    208// "aa(0)"
    +
     8711.    208// "do{}while()"
    +
     8712.    208// "for(){}"
    +
     8713.    208// "if(){}"
    +
     8714.    208// "switch(){}"
    +
     8715.    208// "while(){}"
    +
     8716.    208
    +
     8717.    208    let left = token_global;
    +
     8718.    208    let margin = 0;
    +
     8719.    208    let mode_indent = (
    +
     8720.    208
    +
     8721.    208// PR-330 - Allow 2-space indent.
    +
     8722.    208
    +
     8723.    208        option_dict.indent2
    +
     8724.      5        ? 2
    +
     8725.    203        : 4
    +
     8726.    208    );
    +
     8727.    208    let nr_comments_skipped = 0;
    +
     8728.    208    let open = true;
    +
     8729.    208    let opening = true;
    +
     8730.    208    let right;
    +
     8731.    208
    +
     8732.    208// This is the set of infix operators that require a space on each side.
    +
     8733.    208
    +
     8734.    208    let spaceop = object_assign_from_list(empty(), [
    +
     8735.    208        "!=", "!==", "%", "%=", "&", "&&", "&=", "*", "*=", "+=", "-=", "/",
    +
     8736.    208        "/=", "<", "<<", "<<=", "<=", "=", "==", "===", "=>", ">", ">=", ">>",
    +
     8737.    208        ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||"
    +
     8738.    208    ], true);
    +
     8739.    208
    +
     8740.  38092    function at_margin(fit) {
    +
     8741.  38092        const at = margin + fit;
    +
     8742.     21        if (right.from !== at) {
    +
     8743.     21            return expected_at(at);
    +
     8744.     21        }
    +
     8745.  38092    }
    +
     8746.    208
    +
     8747.   2057    function delve(the_function) {
    +
     8748.  13014        Object.keys(the_function.context).forEach(function (id) {
    +
     8749.  13014            const name = the_function.context[id];
    +
     8750.  12979            if (id !== "ignore" && name.parent === the_function) {
    +
     8751.   6286
    +
     8752.   6286// test_cause:
    +
     8753.   6286// ["function aa(aa) {return aa;}", "delve", "id", "", 0]
    +
     8754.   6286
    +
     8755.   6286                test_cause("id");
    +
     8756.   6286                if (
    +
     8757.   6286                    name.used === 0
    +
     8758.   6286
    +
     8759.   6286// Probably deadcode.
    +
     8760.   6286// && (
    +
     8761.   6286//     name.role !== "function"
    +
     8762.   6286//     || name.parent.arity !== "unary"
    +
     8763.   6286// )
    +
     8764.   6286
    +
     8765.   6286                    && jslint_assert(
    +
     8766.   6286                        name.role !== "function",
    +
     8767.   6286                        `Expected name.role !== "function".`
    +
     8768.   6286                    )
    +
     8769.   6286                ) {
    +
     8770.   6286
    +
     8771.   6286// test_cause:
    +
     8772.   6286// ["/*jslint node*/\nlet aa;", "delve", "unused_a", "aa", 5]
    +
     8773.   6286// ["function aa(aa){return;}", "delve", "unused_a", "aa", 13]
    +
     8774.   6286// ["let aa=0;try{aa();}catch(bb){aa();}", "delve", "unused_a", "bb", 26]
    +
     8775.   6286
    +
     8776.   6286                    warn("unused_a", name);
    +
     8777.   6286                } else if (!name.init) {
    +
     8778.   6286
    +
     8779.   6286// test_cause:
    +
     8780.   6286// ["/*jslint node*/\nlet aa;aa();", "delve", "uninitialized_a", "aa", 5]
    +
     8781.   6286
    +
     8782.   6286                    warn("uninitialized_a", name);
    +
     8783.   6286                }
    +
     8784.   6286            }
    +
     8785.  13014        });
    +
     8786.   2057    }
    +
     8787.    208
    +
     8788.     25    function expected_at(at) {
    +
     8789.     25
    +
     8790.     25// Probably deadcode.
    +
     8791.     25// if (right === undefined) {
    +
     8792.     25//     right = token_nxt;
    +
     8793.     25// }
    +
     8794.     25
    +
     8795.     25        jslint_assert(
    +
     8796.     25            !(right === undefined),
    +
     8797.     25            `Expected !(right === undefined).`
    +
     8798.     25        );
    +
     8799.     25        warn(
    +
     8800.     25            "expected_a_at_b_c",
    +
     8801.     25            right,
    +
     8802.     25            artifact(right),
    +
     8803.     25
    +
     8804.     25// Fudge column numbers in warning message.
    +
     8805.     25
    +
     8806.     25            at + jslint_fudge,
    +
     8807.     25            right.from + jslint_fudge
    +
     8808.     25        );
    +
     8809.     25    }
    +
     8810.    208
    +
     8811.   3095    function no_space() {
    +
     8812.   3094        if (left.line === right.line) {
    +
     8813.   3094
    +
     8814.   3094// from:
    +
     8815.   3094// if (left.line === right.line) {
    +
     8816.   3094//     no_space();
    +
     8817.   3094// } else {
    +
     8818.   3094
    +
     8819.   3094            if (left.thru !== right.from && nr_comments_skipped === 0) {
    +
     8820.   3094
    +
     8821.   3094// test_cause:
    +
     8822.   3094// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14]
    +
     8823.   3094
    +
     8824.   3094                warn(
    +
     8825.   3094                    "unexpected_space_a_b",
    +
     8826.   3094                    right,
    +
     8827.   3094                    artifact(left),
    +
     8828.   3094                    artifact(right)
    +
     8829.   3094                );
    +
     8830.   3094            }
    +
     8831.   3094        } else {
    +
     8832.      1
    +
     8833.      1// from:
    +
     8834.      1// } else if (
    +
     8835.      1//     right.arity === "binary"
    +
     8836.      1//     && right.id === "("
    +
     8837.      1//     && free
    +
     8838.      1// ) {
    +
     8839.      1//     no_space();
    +
     8840.      1// } else if (
    +
     8841.      1
    +
     8842.      1// Probably deadcode.
    +
     8843.      1// if (open) {
    +
     8844.      1//     const at = (
    +
     8845.      1//         free
    +
     8846.      1//         ? margin
    +
     8847.      1//         : margin + 8
    +
     8848.      1//     );
    +
     8849.      1//     if (right.from < at) {
    +
     8850.      1//         expected_at(at);
    +
     8851.      1//     }
    +
     8852.      1// } else {
    +
     8853.      1//     if (right.from !== margin + 8) {
    +
     8854.      1//         expected_at(margin + 8);
    +
     8855.      1//     }
    +
     8856.      1// }
    +
     8857.      1
    +
     8858.      1            jslint_assert(open, `Expected open.`);
    +
     8859.      1            jslint_assert(free, `Expected free.`);
    +
     8860.      1            if (right.from < margin) {
    +
     8861.      1
    +
     8862.      1// test_cause:
    +
     8863.      1// ["let aa = aa(\naa\n()\n);", "expected_at", "expected_a_at_b_c", "5", 1]
    +
     8864.      1
    +
     8865.      1                expected_at(margin);
    +
     8866.      1            }
    +
     8867.      1        }
    +
     8868.   3095    }
    +
     8869.    208
    +
     8870.  96059    function no_space_only() {
    +
     8871.  96059        if (
    +
     8872.  96059            left.id !== "(global)"
    +
     8873.  96057            && left.nr + 1 === right.nr
    +
     8874.  96057            && (
    +
     8875.  96057                left.line !== right.line
    +
     8876.  96057                || left.thru !== right.from
    +
     8877.  96057            )
    +
     8878.      5        ) {
    +
     8879.      5            warn(
    +
     8880.      5                "unexpected_space_a_b",
    +
     8881.      5                right,
    +
     8882.      5                artifact(left),
    +
     8883.      5                artifact(right)
    +
     8884.      5            );
    +
     8885.      5        }
    +
     8886.  96059    }
    +
     8887.    208
    +
     8888.  46227    function one_space() {
    +
     8889.  44324        if (left.line === right.line || !open) {
    +
     8890.  44324            if (left.thru + 1 !== right.from && nr_comments_skipped === 0) {
    +
     8891.  44324                warn(
    +
     8892.  44324                    "expected_space_a_b",
    +
     8893.  44324                    right,
    +
     8894.  44324                    artifact(left),
    +
     8895.  44324                    artifact(right)
    +
     8896.  44324                );
    +
     8897.  44324            }
    +
     8898.  44324        } else {
    +
     8899.   1903            if (right.from !== margin) {
    +
     8900.   1903                expected_at(margin);
    +
     8901.   1903            }
    +
     8902.   1903        }
    +
     8903.  46227    }
    +
     8904.    208
    +
     8905.   9344    function one_space_only() {
    +
     8906.      8        if (left.line !== right.line || left.thru + 1 !== right.from) {
    +
     8907.      8            warn("expected_space_a_b", right, artifact(left), artifact(right));
    +
     8908.      8        }
    +
     8909.   9344    }
    +
     8910.    208
    +
     8911.  24667    function pop() {
    +
     8912.  24667        const previous = function_stack.pop();
    +
     8913.  24667        closer = previous.closer;
    +
     8914.  24667        free = previous.free;
    +
     8915.  24667        margin = previous.margin;
    +
     8916.  24667        open = previous.open;
    +
     8917.  24667        opening = previous.opening;
    +
     8918.  24667    }
    +
     8919.    208
    +
     8920.  24667    function push() {
    +
     8921.  24667        function_stack.push({
    +
     8922.  24667            closer,
    +
     8923.  24667            free,
    +
     8924.  24667            margin,
    +
     8925.  24667            open,
    +
     8926.  24667            opening
    +
     8927.  24667        });
    +
     8928.  24667    }
    +
     8929.    208
    +
     8930.    208// uninitialized_and_unused();
    +
     8931.    208// Delve into the functions looking for variables that were not initialized
    +
     8932.    208// or used. If the file imports or exports, then its global object is also
    +
     8933.    208// delved.
    +
     8934.    208
    +
     8935.    174    if (state.mode_module === true || option_dict.node) {
    +
     8936.     51        delve(token_global);
    +
     8937.     51    }
    +
     8938.    208    catch_list.forEach(delve);
    +
     8939.    208    function_list.forEach(delve);
    +
     8940.    208
    +
     8941.      2    if (option_dict.white) {
    +
     8942.      2        return;
    +
     8943.    206    }
    +
     8944.    206
    +
     8945.    206// whitage();
    +
     8946.    206// Go through the token list, looking at usage of whitespace.
    +
     8947.    206
    +
     8948. 207158    token_list.forEach(function whitage(the_token) {
    +
     8949. 207158        right = the_token;
    +
     8950. 195959        if (right.id === "(comment)" || right.id === "(end)") {
    +
     8951.  11407            nr_comments_skipped += 1;
    +
     8952. 195751        } else {
    +
     8953. 195751
    +
     8954. 195751// If left is an opener and right is not the closer, then push the previous
    +
     8955. 195751// state. If the token following the opener is on the next line, then this is
    +
     8956. 195751// an open form. If the tokens are on the same line, then it is a closed form.
    +
     8957. 195751// Open form is more readable, with each item (statement, argument, parameter,
    +
     8958. 195751// etc) starting on its own line. Closed form is more compact. Statement blocks
    +
     8959. 195751// are always in open form.
    +
     8960. 195751
    +
     8961. 195751// The open and close pairs.
    +
     8962. 195751
    +
     8963. 195751            switch (left.id) {
    +
     8964. 195751            case "${":
    +
     8965. 195751            case "(":
    +
     8966. 195751            case "[":
    +
     8967. 195751            case "{":
    +
     8968. 195751
    +
     8969. 195751// test_cause:
    +
     8970. 195751// ["let aa=[];", "whitage", "opener", "", 0]
    +
     8971. 195751// ["let aa=`${0}`;", "whitage", "opener", "", 0]
    +
     8972. 195751// ["let aa=aa();", "whitage", "opener", "", 0]
    +
     8973. 195751// ["let aa={};", "whitage", "opener", "", 0]
    +
     8974. 195751
    +
     8975. 195751                test_cause("opener");
    +
     8976. 195751
    +
     8977. 195751// Probably deadcode.
    +
     8978. 195751// case "${}":
    +
     8979. 195751
    +
     8980. 195751                jslint_assert(
    +
     8981. 195751                    !(left.id + right.id === "${}"),
    +
     8982. 195751                    "Expected !(left.id + right.id === \"${}\")."
    +
     8983. 195751                );
    +
     8984. 195751                switch (left.id + right.id) {
    +
     8985. 195751                case "()":
    +
     8986. 195751                case "[]":
    +
     8987. 195751                case "{}":
    +
     8988. 195751
    +
     8989. 195751// If left and right are opener and closer, then the placement of right depends
    +
     8990. 195751// on the openness. Illegal pairs (like '{]') have already been detected.
    +
     8991. 195751
    +
     8992. 195751// test_cause:
    +
     8993. 195751// ["let aa=[];", "whitage", "opener_closer", "", 0]
    +
     8994. 195751// ["let aa=aa();", "whitage", "opener_closer", "", 0]
    +
     8995. 195751// ["let aa={};", "whitage", "opener_closer", "", 0]
    +
     8996. 195751
    +
     8997. 195751                    test_cause("opener_closer");
    +
     8998. 195751                    if (left.line === right.line) {
    +
     8999. 195751
    +
     9000. 195751// test_cause:
    +
     9001. 195751// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14]
    +
     9002. 195751
    +
     9003. 195751                        no_space();
    +
     9004. 195751                    } else {
    +
     9005. 195751
    +
     9006. 195751// test_cause:
    +
     9007. 195751// ["let aa = aa(\n );", "expected_at", "expected_a_at_b_c", "1", 2]
    +
     9008. 195751
    +
     9009. 195751                        at_margin(0);
    +
     9010. 195751                    }
    +
     9011. 195751                    break;
    +
     9012. 195751                default:
    +
     9013. 195751
    +
     9014. 195751// test_cause:
    +
     9015. 195751// ["let aa=(0);", "whitage", "opener_operand", "", 0]
    +
     9016. 195751// ["let aa=[0];", "whitage", "opener_operand", "", 0]
    +
     9017. 195751// ["let aa=`${0}`;", "whitage", "opener_operand", "", 0]
    +
     9018. 195751// ["let aa=aa(0);", "whitage", "opener_operand", "", 0]
    +
     9019. 195751// ["let aa={aa:0};", "whitage", "opener_operand", "", 0]
    +
     9020. 195751
    +
     9021. 195751                    test_cause("opener_operand");
    +
     9022. 195751                    opening = left.open || (left.line !== right.line);
    +
     9023. 195751                    push();
    +
     9024. 195751                    switch (left.id) {
    +
     9025. 195751                    case "${":
    +
     9026. 195751                        closer = "}";
    +
     9027. 195751                        break;
    +
     9028. 195751                    case "(":
    +
     9029. 195751                        closer = ")";
    +
     9030. 195751                        break;
    +
     9031. 195751                    case "[":
    +
     9032. 195751                        closer = "]";
    +
     9033. 195751                        break;
    +
     9034. 195751                    case "{":
    +
     9035. 195751                        closer = "}";
    +
     9036. 195751                        break;
    +
     9037. 195751                    }
    +
     9038. 195751                    if (opening) {
    +
     9039. 195751
    +
     9040. 195751// test_cause:
    +
     9041. 195751// ["function aa(){\nreturn;\n}", "whitage", "opening", "", 0]
    +
     9042. 195751// ["let aa=(\n0\n);", "whitage", "opening", "", 0]
    +
     9043. 195751// ["let aa=[\n0\n];", "whitage", "opening", "", 0]
    +
     9044. 195751// ["let aa=`${\n0\n}`;", "whitage", "opening", "", 0]
    +
     9045. 195751// ["let aa={\naa:0\n};", "whitage", "opening", "", 0]
    +
     9046. 195751
    +
     9047. 195751                        test_cause("opening");
    +
     9048. 195751                        free = closer === ")" && left.free;
    +
     9049. 195751                        open = true;
    +
     9050. 195751                        margin += mode_indent;
    +
     9051. 195751                        if (right.role === "label") {
    +
     9052. 195751                            if (right.from !== 0) {
    +
     9053. 195751
    +
     9054. 195751// test_cause:
    +
     9055. 195751// ["
    +
     9056. 195751// function aa() {
    +
     9057. 195751//  bb:
    +
     9058. 195751//     while (aa) {
    +
     9059. 195751//         if (aa) {
    +
     9060. 195751//             break bb;
    +
     9061. 195751//         }
    +
     9062. 195751//     }
    +
     9063. 195751// }
    +
     9064. 195751// ", "expected_at", "expected_a_at_b_c", "1", 2]
    +
     9065. 195751
    +
     9066. 195751                                expected_at(0);
    +
     9067. 195751                            }
    +
     9068. 195751                        } else if (right.switch) {
    +
     9069. 195751                            at_margin(-mode_indent);
    +
     9070. 195751                        } else {
    +
     9071. 195751                            at_margin(0);
    +
     9072. 195751                        }
    +
     9073. 195751                    } else {
    +
     9074. 195751                        if (right.statement || right.role === "label") {
    +
     9075. 195751
    +
     9076. 195751// test_cause:
    +
     9077. 195751// ["
    +
     9078. 195751// function aa() {bb:
    +
     9079. 195751//     while (aa) {
    +
     9080. 195751//         aa();
    +
     9081. 195751//     }
    +
     9082. 195751// }
    +
     9083. 195751// ", "whitage", "expected_line_break_a_b", "bb", 16]
    +
     9084. 195751
    +
     9085. 195751                            warn(
    +
     9086. 195751                                "expected_line_break_a_b",
    +
     9087. 195751                                right,
    +
     9088. 195751                                artifact(left),
    +
     9089. 195751                                artifact(right)
    +
     9090. 195751                            );
    +
     9091. 195751                        }
    +
     9092. 195751
    +
     9093. 195751// test_cause:
    +
     9094. 195751// ["let aa=(0);", "whitage", "not_free", "", 0]
    +
     9095. 195751// ["let aa=[0];", "whitage", "not_free", "", 0]
    +
     9096. 195751// ["let aa=`${0}`;", "whitage", "not_free", "", 0]
    +
     9097. 195751// ["let aa={aa:0};", "whitage", "not_free", "", 0]
    +
     9098. 195751
    +
     9099. 195751                        test_cause("not_free");
    +
     9100. 195751                        free = false;
    +
     9101. 195751                        open = false;
    +
     9102. 195751
    +
     9103. 195751// test_cause:
    +
     9104. 195751// ["let aa = ( 0 );", "no_space_only", "unexpected_space_a_b", "0", 12]
    +
     9105. 195751
    +
     9106. 195751                        no_space_only();
    +
     9107. 195751                    }
    +
     9108. 195751                }
    +
     9109. 195751                break;
    +
     9110. 195751            default:
    +
     9111. 195751                if (right.statement === true) {
    +
     9112. 195751                    if (left.id === "else") {
    +
     9113. 195751
    +
     9114. 195751// test_cause:
    +
     9115. 195751// ["
    +
     9116. 195751// let aa = 0;
    +
     9117. 195751// if (aa) {
    +
     9118. 195751//     aa();
    +
     9119. 195751// } else  if (aa) {
    +
     9120. 195751//     aa();
    +
     9121. 195751// }
    +
     9122. 195751// ", "one_space_only", "expected_space_a_b", "if", 9]
    +
     9123. 195751
    +
     9124. 195751                        one_space_only();
    +
     9125. 195751                    } else {
    +
     9126. 195751
    +
     9127. 195751// test_cause:
    +
     9128. 195751// [" let aa = 0;", "expected_at", "expected_a_at_b_c", "1", 2]
    +
     9129. 195751
    +
     9130. 195751                        at_margin(0);
    +
     9131. 195751                        open = false;
    +
     9132. 195751                    }
    +
     9133. 195751
    +
     9134. 195751// If right is a closer, then pop the previous state.
    +
     9135. 195751
    +
     9136. 195751                } else if (right.id === closer) {
    +
     9137. 195751                    pop();
    +
     9138. 195751                    if (opening && right.id !== ";") {
    +
     9139. 195751                        at_margin(0);
    +
     9140. 195751                    } else {
    +
     9141. 195751                        no_space_only();
    +
     9142. 195751                    }
    +
     9143. 195751                } else {
    +
     9144. 195751
    +
     9145. 195751// Left is not an opener, and right is not a closer.
    +
     9146. 195751// The nature of left and right will determine the space between them.
    +
     9147. 195751
    +
     9148. 195751// If left is ',' or ';' or right is a statement then if open,
    +
     9149. 195751// right must go at the margin, or if closed, a space between.
    +
     9150. 195751
    +
     9151. 195751                    if (right.switch) {
    +
     9152. 195751                        at_margin(-mode_indent);
    +
     9153. 195751                    } else if (right.role === "label") {
    +
     9154. 195751                        if (right.from !== 0) {
    +
     9155. 195751
    +
     9156. 195751// test_cause:
    +
     9157. 195751// ["
    +
     9158. 195751// function aa() {
    +
     9159. 195751//     aa();cc:
    +
     9160. 195751//     while (aa) {
    +
     9161. 195751//         if (aa) {
    +
     9162. 195751//             break cc;
    +
     9163. 195751//         }
    +
     9164. 195751//     }
    +
     9165. 195751// }
    +
     9166. 195751// ", "expected_at", "expected_a_at_b_c", "1", 10]
    +
     9167. 195751
    +
     9168. 195751                            expected_at(0);
    +
     9169. 195751                        }
    +
     9170. 195751                    } else if (left.id === ",") {
    +
     9171. 195751                        if (!open || (
    +
     9172. 195751                            (free || closer === "]")
    +
     9173. 195751                            && left.line === right.line
    +
     9174. 195751                        )) {
    +
     9175. 195751
    +
     9176. 195751// test_cause:
    +
     9177. 195751// ["let {aa,bb} = 0;", "one_space", "expected_space_a_b", "bb", 9]
    +
     9178. 195751
    +
     9179. 195751                            one_space();
    +
     9180. 195751                        } else {
    +
     9181. 195751
    +
     9182. 195751// test_cause:
    +
     9183. 195751// ["
    +
     9184. 195751// function aa() {
    +
     9185. 195751//     aa(
    +
     9186. 195751//         0,0
    +
     9187. 195751//     );
    +
     9188. 195751// }
    +
     9189. 195751// ", "expected_at", "expected_a_at_b_c", "9", 11]
    +
     9190. 195751
    +
     9191. 195751                            at_margin(0);
    +
     9192. 195751                        }
    +
     9193. 195751
    +
     9194. 195751// If right is a ternary operator, line it up on the margin.
    +
     9195. 195751
    +
     9196. 195751                    } else if (right.arity === "ternary") {
    +
     9197. 195751                        if (open) {
    +
     9198. 195751
    +
     9199. 195751// test_cause:
    +
     9200. 195751// ["
    +
     9201. 195751// let aa = (
    +
     9202. 195751//     aa
    +
     9203. 195751//     ? 0
    +
     9204. 195751// : 1
    +
     9205. 195751// );
    +
     9206. 195751// ", "expected_at", "expected_a_at_b_c", "5", 1]
    +
     9207. 195751
    +
     9208. 195751                            at_margin(0);
    +
     9209. 195751                        } else {
    +
     9210. 195751
    +
     9211. 195751// test_cause:
    +
     9212. 195751// ["let aa = (aa ? 0 : 1);", "whitage", "use_open", "?", 14]
    +
     9213. 195751
    +
     9214. 195751                            warn("use_open", right);
    +
     9215. 195751                        }
    +
     9216. 195751                    } else if (
    +
     9217. 195751                        right.arity === "binary"
    +
     9218. 195751                        && right.id === "("
    +
     9219. 195751                        && free
    +
     9220. 195751                    ) {
    +
     9221. 195751
    +
     9222. 195751// test_cause:
    +
     9223. 195751// ["let aa = aa(\naa ()\n);", "no_space", "unexpected_space_a_b", "(", 4]
    +
     9224. 195751
    +
     9225. 195751                        no_space();
    +
     9226. 195751                    } else if (
    +
     9227. 195751                        left.id === "."
    +
     9228. 195751                        || left.id === "?."
    +
     9229. 195751                        || left.id === "..."
    +
     9230. 195751                        || right.id === ","
    +
     9231. 195751                        || right.id === ";"
    +
     9232. 195751                        || right.id === ":"
    +
     9233. 195751                        || (
    +
     9234. 195751                            right.arity === "binary"
    +
     9235. 195751                            && (right.id === "(" || right.id === "[")
    +
     9236. 195751                        )
    +
     9237. 195751                        || (
    +
     9238. 195751                            right.arity === "function"
    +
     9239. 195751                            && left.id !== "function"
    +
     9240. 195751                        )
    +
     9241. 195751                        || (right.id === "." || right.id === "?.")
    +
     9242. 195751                    ) {
    +
     9243. 195751
    +
     9244. 195751// test_cause:
    +
     9245. 195751// ["let aa = 0 ;", "no_space_only", "unexpected_space_a_b", ";", 12]
    +
     9246. 195751// ["let aa = aa ?.aa;", "no_space_only", "unexpected_space_a_b", "?.", 13]
    +
     9247. 195751
    +
     9248. 195751                        no_space_only();
    +
     9249. 195751                    } else if (left.id === ";") {
    +
     9250. 195751
    +
     9251. 195751// test_cause:
    +
     9252. 195751// ["
    +
     9253. 195751// /*jslint for*/
    +
     9254. 195751// function aa() {
    +
     9255. 195751//     for (
    +
     9256. 195751//         aa();
    +
     9257. 195751// aa;
    +
     9258. 195751//         aa()
    +
     9259. 195751//     ) {
    +
     9260. 195751//         aa();
    +
     9261. 195751//     }
    +
     9262. 195751// }
    +
     9263. 195751// ", "expected_at", "expected_a_at_b_c", "9", 1]
    +
     9264. 195751
    +
     9265. 195751                        if (open) {
    +
     9266. 195751                            at_margin(0);
    +
     9267. 195751                        }
    +
     9268. 195751                    } else if (
    +
     9269. 195751                        left.arity === "ternary"
    +
     9270. 195751                        || left.id === "case"
    +
     9271. 195751                        || left.id === "catch"
    +
     9272. 195751                        || left.id === "else"
    +
     9273. 195751                        || left.id === "finally"
    +
     9274. 195751                        || left.id === "while"
    +
     9275. 195751                        || left.id === "await"
    +
     9276. 195751                        || right.id === "catch"
    +
     9277. 195751                        || right.id === "else"
    +
     9278. 195751                        || right.id === "finally"
    +
     9279. 195751                        || (right.id === "while" && !right.statement)
    +
     9280. 195751                        || (left.id === ")" && right.id === "{")
    +
     9281. 195751                    ) {
    +
     9282. 195751
    +
     9283. 195751// test_cause:
    +
     9284. 195751// ["
    +
     9285. 195751// function aa() {
    +
     9286. 195751//     do {
    +
     9287. 195751//         aa();
    +
     9288. 195751//     } while(aa());
    +
     9289. 195751// }
    +
     9290. 195751// ", "one_space_only", "expected_space_a_b", "(", 12]
    +
     9291. 195751
    +
     9292. 195751                        one_space_only();
    +
     9293. 195751                    } else if (
    +
     9294. 195751
    +
     9295. 195751// There is a space between left and right.
    +
     9296. 195751
    +
     9297. 195751                        spaceop[left.id] === true
    +
     9298. 195751                        || spaceop[right.id] === true
    +
     9299. 195751                        || (
    +
     9300. 195751                            left.arity === "binary"
    +
     9301. 195751                            && (left.id === "+" || left.id === "-")
    +
     9302. 195751                        )
    +
     9303. 195751                        || (
    +
     9304. 195751                            right.arity === "binary"
    +
     9305. 195751                            && (right.id === "+" || right.id === "-")
    +
     9306. 195751                        )
    +
     9307. 195751                        || left.id === "function"
    +
     9308. 195751                        || left.id === ":"
    +
     9309. 195751                        || left.id === "async"
    +
     9310. 195751                        || (
    +
     9311. 195751                            (
    +
     9312. 195751                                left.identifier
    +
     9313. 195751                                || left.id === "(string)"
    +
     9314. 195751                                || left.id === "(number)"
    +
     9315. 195751                            )
    +
     9316. 195751                            && (
    +
     9317. 195751                                right.identifier
    +
     9318. 195751                                || right.id === "(string)"
    +
     9319. 195751                                || right.id === "(number)"
    +
     9320. 195751                            )
    +
     9321. 195751                        )
    +
     9322. 195751                        || (left.arity === "statement" && right.id !== ";")
    +
     9323. 195751                    ) {
    +
     9324. 195751
    +
     9325. 195751// test_cause:
    +
     9326. 195751// ["let aa=0;", "one_space", "expected_space_a_b", "0", 8]
    +
     9327. 195751// ["let aa={\naa:\n0\n};", "expected_at", "expected_a_at_b_c", "5", 1]
    +
     9328. 195751
    +
     9329. 195751                        one_space();
    +
     9330. 195751                    } else if (left.arity === "unary" && left.id !== "`") {
    +
     9331. 195751                        no_space_only();
    +
     9332. 195751                    }
    +
     9333. 195751                }
    +
     9334. 195751            }
    +
     9335. 195751            nr_comments_skipped = 0;
    +
     9336. 195751            delete left.calls;
    +
     9337. 195751            delete left.dead;
    +
     9338. 195751            delete left.free;
    +
     9339. 195751            delete left.init;
    +
     9340. 195751            delete left.open;
    +
     9341. 195751            delete left.used;
    +
     9342. 195751            left = right;
    +
     9343. 195751        }
    +
     9344. 207158    });
    +
     9345.    206}
    +
     9346.      1
    +
     9347.      6function jslint_report({
    +
     9348.      6    exports,
    +
     9349.      6    froms,
    +
     9350.      6    functions,
    +
     9351.      6    global,
    +
     9352.      6    json,
    +
     9353.      6    module,
    +
     9354.      6    property,
    +
     9355.      6    stop,
    +
     9356.      6    warnings
    +
     9357.      6}) {
    +
     9358.      6
    +
     9359.      6// This function will create human-readable, html-report
    +
     9360.      6// for warnings, properties, and functions from jslint-result-object.
    +
     9361.      6//
    +
     9362.      6// Example usage:
    +
     9363.      6//  let result = jslint("console.log('hello world')");
    +
     9364.      6//  let html = jslint_report(result);
    +
     9365.      6
    +
     9366.      6    let html = "";
    +
     9367.      6    let length_80 = 1111;
    +
     9368.      6
    +
     9369.    328    function address(line = 1, column = 1) {
    +
     9370.    328
    +
     9371.    328// This function will create HTML address element from <line> and <column>
    +
     9372.    328
    +
     9373.    328        return `<address>${Number(line)}: ${Number(column)}</address>`;
    +
     9374.    328
    +
     9375.    328    }
    +
     9376.      6
    +
     9377.   2256    function detail(title, list) {
    +
     9378.   2256        return (
    +
     9379.   2256            (Array.isArray(list) && list.length > 0)
    +
     9380.    781            ? (
    +
     9381.    781
    +
     9382.    781// Google Lighthouse Accessibility - <dl>'s do not contain only properly-ordered
    +
     9383.    781// <dt> and <dd> groups, <script>, <template> or <div> elements.
    +
     9384.    781
    +
     9385.    781                "<dl>"
    +
     9386.    781                + "<dt>" + htmlEscape(title) + "</dt>"
    +
     9387.    781                + "<dd>" + list.join(", ") + "</dd>"
    +
     9388.    781                + "</dl>"
    +
     9389.    781            )
    +
     9390.   1475            : ""
    +
     9391.   2256        );
    +
     9392.   2256    }
    +
     9393.      6
    +
     9394.      6    html += String(`
    +
     9395.      6<style class="JSLINT_REPORT_STYLE">
    +
     9396.      6/* jslint utility2:true */
    +
     9397.      6/*csslint box-model: false, ids:false */
    +
     9398.      6/*csslint ignore:start*/
    +
     9399.      6@font-face {
    +
     9400.      6    font-display: swap;
    +
     9401.      6    font-family: "Daley";
    +
     9402.      6    src: url(
    +
     9403.      6"data:font/woff2;base64,d09GMgABAAAAABy4AA4AAAAAThwAABxiAAEAAAAAAAAAAAAA\
    +
     9404.      6AAAAAAAAAAAAAAAABmAAgiQINAmcDBEICuc41DEBNgIkA4R2C4I+AAQgBYkuByAMgScfYUIF\
    +
     9405.      67NgjsHGAbcDVFkXZ5Jwd+P96IGPc9rl9ETBEaCzCJkvY2UpziRZ7zftZWk8052U9+NqX6vXL\
    +
     9406.      6KDflSQnlJ0bP+QnPQAy744n9mup6H9PaCDFwM5zjf8exB89bZ1cdrYOP0NgnuRDRWlk9u/fE\
    +
     9407.      6llkxqmfH8lmRQ/DAmER9opk9wR6suc1LvTiXNEe1vbhUCH2USgnEwH3vUm05JQqejGvZvOtz\
    +
     9408.      67sIKEGgLdDNl/IrfqWVZG/wr42ekomEm91VA1p4LhHBuFzHF8//u7vvbREHMQqGtNLmiOOD/\
    +
     9409.      6X7WWiwqyCE98qt0jk5JJmgR5WJJElBmzRb1F7a66MmSLTNWZ2XSHfKBSKHoVteSEJ6EOdvVw\
    +
     9410.      6fNZOtXKDe39jXdRlkmMnOWIOFBgeEK/b0mFsgffnPyyAitNyutKky7J8a8MSEkAKGLgfptnS\
    +
     9411.      6/gDRSo7vwdNUmQDB7oP6pK7QF5d9SrY8M/tkrXcurSIQAmX7tz7pd33LIB7GQkBQ/k81s/0D\
    +
     9412.      6gpt4gbw7x0Cn/PocitK5KIGPGQIzQzAMuCeC2ERAidx9TySVqX06goT0SFFOOV9Kuxdi5Rg7\
    +
     9413.      6l6n3c+nKRemidOm2dtFV1jXMk4rP2m6RJ8xEdPYONLTbeMgaJ1nwS2W4su3MHwqkkvJ2PdDU\
    +
     9414.      6r7pgAnVRt4Kh789FXlD0r3p6jUtNO19O1s74U9pnIxqFpw+mBgF+8y30PAyw1dzlknLLVcSB\
    +
     9415.      6J2OuCr9eV5Efew6cOGd47ZEfhrW7HXI+FBNFvWgWnugUU4UvlrV63niv2ZPeKu8M76y/HQaG\
    +
     9416.      6weU+4Gzp+Y+cfb9R9djDWcd1Svr1xG7l+j/yf3eM996548qlC+dOzOqQ8//Lo0uaSEQCFuLD\
    +
     9417.      6/bXyWhJ6aPmyaRonVPxGABFL4/0slcKI6f+PmT0M+QRsplmWnv4F49VT+JsPifoa6aeyr2Hz\
    +
     9418.      6EeLdP1FEOV/ZN+c9sAuoNh0BRS0xgCCc9wME5s0HOKj/wc0fWYsTbFQpsZL5SayJPkL45kDo\
    +
     9419.      6DcJJ10MvD0ZSq7FEIr1TfqZ7NC6s75zSp8viaNO5/PczYCV9z6NTa0KBdnGBg6kbdeBkRLfU\
    +
     9420.      6qRd3D9Pqw5jWCc5WM/i95OE8731MBd1u2EmsXIa5dCvavY32U1Ytza4nfbERg6OVRZka7jq0\
    +
     9421.      6r2FcXNDyEhXheaHtaU1o1kvO9MuBOHqugLUEzN+4jznu0oK9wZPur1lWVFfxl8lZzn2XwcjZ\
    +
     9422.      6Csg/RJy0mAMMmgnqXS8ELhOCRUSLzvsM5gAPudEh2lVoRxGgyUVnArZMruE0YS1PqFMD3upb\
    +
     9423.      6jVoecGj1KpWl6/ZysuyzkG4SGA4bps6FBQSg4e4IxNUgdmosmoDn0TpIex/s1BFau6GBNO4z\
    +
     9424.      6cvWXypm4hEg5k3llelySFqNmUtRZ3PHBA7p4MBX1nK4awwAV6kWzIVbUA67A55QKYbMsgVaH\
    +
     9425.      6c1ZxKuZ0DCyqxCsJjLyCEY36gf0wjAu3t0zemc87PmBCJbU9Lso0YAaYJUx8wsR02hYz5hGy\
    +
     9426.      6Js0+A4uHGZgfuf5SOR9iBQuLhpOExaIFrHj6JlXanebzGHp2ELDh6av09PVE1fmdsj2oHRWs\
    +
     9427.      6fOtYrV6wRCyx7XogHqvpnZiPBBdNcL6kIoS9UI/DOIlumlveSgv9oqMBYp7WZ2fGxAXmZmaG\
    +
     9428.      6OCyJG6+wAszZFCQw/EXVjx+YA2uVyN6bhNWiZhgtYjAwR5U/7uV1scghiTGiAPZbA5ZqHw5u\
    +
     9429.      6Yu1cDjhRwREBFyq2wa0R8GgceDUKPo2BX+MhoAkQ1EQIaZqVHMwH3xM+P32TTA34tmOMNZ4n\
    +
     9430.      6mHXqn49fmE3qX1+wMNYoYetOsPx6wxKzkURImERJIjGSSJwkkiCJJEkiKZJImiSSIYlkSYqK\
    +
     9431.      6UBu0UOopuLMmasiJW0PMFOO2UgbDif2NaQUqkBbyaGjdTUvuyamEQwCq9DWsxsG9qPt+VFqV\
    +
     9432.      66cIsXcyWujWIEtNFdeia9ssNrJUpe3IDMPQZOReC8x+qvt17drPWdcHeL0gTarWwoQ6o828o\
    +
     9433.      60EJzrA20yZsgVyVHdlCJOF3NaACxHbP38TA+MGx3St9c5t2CxbGtunB4J9AF4Px2rSr1wyK9\
    +
     9434.      69KoXBR13vw9Fk9qhTX0ivZoanrvhLa5oiJO8cqR0lX7QtJ2c1a62V3PMtutaaoit+hxtXuC5\
    +
     9435.      6ZUXJePSR6btQlt5g7PqPQ822g7F8D123pc4kaGXz7qYztJxDXCxJr7foKqxwy4rikI/NvINx\
    +
     9436.      6bkArRTTnnMWy6YA8J39LfTweThKsqlt7Mz078NDSOPOGgtGTpeG8ZRBF+xKBjdSoNe8gE6uC\
    +
     9437.      6ucOH98jE4+cv1JEjI555TFjYj4+0KdFlojzJGWp2wc1tCaYGSeO8dBfT0u3lpDY3tazzu4wn\
    +
     9438.      6lF9wzy2nK+sTr/qEVdANoZ0ToBdD+MY4ewOHNnkXPBvKVXLSbEGfGVD0Nzr0Fs3HID3Y1Kqx\
    +
     9439.      6mzJ6p1C1/R6Xneyw/q9YRDLahbnsI1u76XzMLPqsK0yvQDeQ4TMR41709sIssmEgs0XH1lcj\
    +
     9440.      67HLnUG6u2Xpy5vbOowIGqrR6cwF0TLGI5PF7pkbzIVYQU0sIaoNgul3LGAH2B1nREFYXUMia\
    +
     9441.      6prCeAzggGxrC5gIK2dK0exs/AIRKdlIIuxkUspdSsU+rqXagqXaooXakqTiWS/a0E7zA6QIK\
    +
     9442.      6OdMUznMAh+RCQ7hcQCFXmspr3ciuds/6gPsZFPIgpfJhwUIepRAeZ1DIk5Tue4oKfSfKZyNV\
    +
     9443.      6pKU/J7J4Abx1EMV5mXSRDl6lMfU6jfBmBww4k7f6gLzTB+J9od/kA/uGj2mET2nkn7+zQ/JF\
    +
     9444.      6H5Kv+pB804fkOyvwI43wM438V5sdkd/6iPzRR+SvPiL/WIH/aYRxGqMb/Oqe3d54+LWR1vr2\
    +
     9445.      6knnnc467iD247eXBA3YYBAiFfierClXz/8jyL3Qh/zP8y+Y/1eN8jq+SKZAML/lIidjwZ8N4\
    +
     9446.      6aLthvhxGUkGPo+p0eHKZ0sT5FsqJcQCy9UhHIvcJFIlIvANTPFWUTUhSiVdsNRnvwEQxm5uc\
    +
     9447.      6ksjdv5evJfpOgI6c7juH8pnG2RKwlXaDYe9g8rMwYfML3A2SMWeBDopJJsmS5dUE2KttnmQa\
    +
     9448.      6JZlMspvEpJioiEDFNpPUTbwqG3Zjhx2VCeJrIf60s2mI6blZMZVyAyYzI+1a2Y0AIqcbLUgR\
    +
     9449.      66iRbNtnp82GrImXW0YbcbczDgqQDWNdTenvtTAlT9iPHenluV+d3eed1/5MjMBrX2LgrK2ml\
    +
     9450.      6FuoDOz036n/kaHbAeszR3jHoI4NWB3lusTfuVgkMUkLQaH0F6+pSCS11fXRwT421vs9s7axd\
    +
     9451.      6nvtF7/eeIeq9s1aCLsLWdh+w7sXz3IYdEsSQ0LVsebmES/vXDU9k653W4MiNq8bMj5nLioCY\
    +
     9452.      6edGgOT6tmYwqiOW1ugiEmew6iwjvvYb3SaeZJb7XNufOo9oH8FTneWGL+BLiclptpnhPwcui\
    +
     9453.      6T+rzcF34+ycsL7p3AveuML9i9h13beylyg8CzEz5HppadqmmDxKrAquG9L3ztedRoWxEsAYt\
    +
     9454.      6OM1Eu0G0gyTHkxf7cSkHJQRbA4xmlqHWkv1C0KhFhBq1z81Wq1CZoWic8TJ570WfSj5qsM+Q\
    +
     9455.      6nl4k3H5+P+P3zlv9ltQrzv41qyiSwV/gOadyQBchsmwDGu/JI8tXflE8jqUVA0Zw0SKbdDC9\
    +
     9456.      6c4FR+fak95SdF7uqpoRe9z6YRv+85YUzF4qJy6Q8GOVNwUn/ymyjNNbmcuVfXYeH2osLdCte\
    +
     9457.      6ebmZRyUfQQZA1BSCLK4PWA/z1kBvDZm0t+i3or1LkMD6en95pGG0UOa8ZJXgS9TdEA1I2mZw\
    +
     9458.      61JOWWxDu0NEh4rM19H55rvueMBUZV1RjkmB3oxkXhAckpa5gzzxUDA2VLOrWFAXx+4gmfU17\
    +
     9459.      65o3v9H7EYdvGFuM+tDB3TA4ITjVUKduO/R4bXRAcPXZusWkN+t59sFz7Hyi0FkSdzrHXQVFq\
    +
     9460.      6b8c9k9eLRjVlBbNvt4172CanYg/F3Rket1zCTc77UZ61Gq/Be9J8hrKrxbDZMEotf5o8zHDc\
    +
     9461.      6/UJaEtdhgwHEcBEQKM+6NBWIewLmI1sHuWYAedZCw8U1hJfSWcld+2tv3jpCFc5FnosLWC0+\
    +
     9462.      6DnAlnOXUXLoMXrmCVerNQkZHvRm8YtE12vG8+N/vOnPcu3vM1uOnzE3u3VP2ppmLZawm2NuO\
    +
     9463.      6tPa7xwHFCgVKpox5PVrOmaDHrThk1tX864a2+/qhJd3nCFRQ+bfUKI4O+Wgk5byB3saMcUfV\
    +
     9464.      6C8G137yMd16zRm3ZSq+UrDlk5ha3TiAj0b74prWO/vYG+RC+ronP1/McDtefBtY1XhZE0PIB\
    +
     9465.      6wTe7CBTte2U6KPbYd5GffApQlDGssdfmxYGSlnHrQt7++KEwUg3ikkoQyKPixgUDB6Lozjv5\
    +
     9466.      6vM5PBnllt+UzMnP6DStFsOfossbXOefWhQApACCNpkTYGAONIowDfndqDKRFuzn685nthZPe\
    +
     9467.      6vEL7TIWkXAG2yxKBH90+yMzuRzWn3KMmyKGwZWnIErlJ9Vwt8OtR6+4TKad5y9+ViBtTzVG+\
    +
     9468.      6tpv/xiLrcGKJRtYvCUlGeL4Dwy1jo1CSQe0X71EXK1YG44ztxTONjIslL8SwY0Cki0k0vsX/\
    +
     9469.      6/xz7CxkAc9dEdJZhMy/JCGzD2FAGtUcag0tc2e2miJkp477V2qTKB+nFnDl/noxpXJ+yqVdO\
    +
     9470.      6wNjbplmeiuburg9ii1Z1zwtG8QjcJAiVPSOV2mHzq1Qt7p2+YCcIKPmFusE5O+m8s+Wd8o3t\
    +
     9471.      6qO1b1IZF8N0tx6RQnZ9Ux3gXijHlolixst6vhJV6ao0ZFzSprfAc3x0MLvxU0OsmXEVddMVK\
    +
     9472.      629CC6mPgPtXTUW7tVnZxwm0DTJwNOeVRV4axMSPlpgyv1Va1MQhQqWwUOb0s+gVLOecos4Nf\
    +
     9473.      6eqlFW3fLQrlP86R4XRxrDHF0VIx6ArM5/sTWtObY6U2aosgxbN6FUa1iNTUpMThk1sUfJOC6\
    +
     9474.      6s1SKo9D0g1NfiVmavyful/K7nZdDgutV1A26i7FR3r16bv3zz1cGw+ta17IX/+ripyutix3C\
    +
     9475.      6xNmCxs7uiqKu9/Zjjn06tblXpJxlaLF5Od0d5W9QhQrs2u6UN0trQlCyEK2j9VYgCEIDrhQN\
    +
     9476.      6c00rxg/FOfZ1N+nLV7RXDsYP+p0EzqKcuPujzuzEQsu2mFf4nYvf3Yp32rq/RYLetDLuOOTc\
    +
     9477.      60WXBtgoech7AHUxAxPBg81qWCsYlzTofRU5/MpuyNoegR6mCJO5ckrLOhWbG7xo/VGwGgpRb\
    +
     9478.      6+Ch+TmlcuY6Qct/2x3gxzeDUU9u+ltexrjelJ0VRR9KXH/AqrbYxHa0vmQ/kBnE5EORBK1ZH\
    +
     9479.      6mTSy7A8DJMgzzqDsu9ML5J3ufkuUNDCfN5UKAjBgw2I/QlS8MQ6o/ll9dTAdoM7HYtV4cNWE\
    +
     9480.      6U4pOl5Y4SIzdMbNSjXFmsBV1uXXf7GaBZZslpFGFiIpokSzxWj4hjlGl4VKJDACo7ScxQf29\
    +
     9481.      6kM8gHD3nUJkwkN2aW2TGttqwOrygJ7r9nYX2tYqy7Z3TQV5ocWzUI8l871y3LsQLoTgEO76B\
    +
     9482.      6Upp69hy6VKRpZvpvgfQ2T06qgXjxh38eatREitX6bzKggIYmN4sAkA3a5oeJZDK3ahQrVJwa\
    +
     9483.      6AD65cEGBkS/tKH9TtybiREEWCMcKD0HH0gELtjB+KNSk7bspmpr6eb0CscIiFyZpmXu8+gxw\
    +
     9484.      6O7pJNbAK2h9q2c5dMHBaoi5DylbNGdweVVdN3Jm9u6YXXlmx4nYY2vIPfSkrE/vyv9gn/Z+j\
    +
     9485.      6R3HKExaUhdV0Az77YWbQPhNfjw+F0vTteSMin+wIfxyPe0DEoI4uz6o2IXwsZC7sg8MicQ3o\
    +
     9486.      6wys+NJYKVW72YiVQ5LKDVwrEg2jNVM6XdNjbsHlRDcAkD08o5iWtFB2dVoydRmmDRLalE+4t\
    +
     9487.      63gBbAPa7n7qXXXbTZTJXZKy5+1W0K7dgYEcIlu90ovC0C+5gxXiKtZisT14qDJ7f2ksyK59U\
    +
     9488.      6r3QeHtBb24mPz7YDB3rgMTyUZ/fxM8h2i1Z21B8/VA5+9l7BKaOJZ15lWsyPv/z6CjU32ZKq\
    +
     9489.      6+QFeyUywxYnUxUmcQfGc1Sp69oE2n6zFL8BXf5rc3cJMM6S97gagTT1bi7cmAV4MibkC4rz/\
    +
     9490.      6icmmFtMlo5aN1Wp3uxsBfd4+9T42xmxvd79FV/hfuviBcrIaX092PrY5rle9FR4wTnDzrwj4\
    +
     9491.      67frD2d0KsMcdcADQ1Yu1LECg9Wj3yOS8OhrJdQBqXqsam17vmt2wjjjouHE/EO9sGPdqt23v\
    +
     9492.      6j8rL6wid6ulagtNK5p1hjRkFtUxTIaZnIXk63Zb3P0t5MQ+3vxHIFrmgAdWwiDuA67tbVIF6\
    +
     9493.      6wJ53z0uhyhsfH9bgF0kPT9v2hrT3HKIBgUXIYoxsVU+uryemiUiQEwh+BfxP//qLShlumR26\
    +
     9494.      6I8OqjD+x3hHDj/IrEWmvyL6ioG/atfxe+5GzIqRgfaoayWOiTk+YixO15KDO6Os3XACDjboe\
    +
     9495.      6ryXXOuEmTpDsc7czk+H04Kw1PNJazW32CAURHwBldqK0/nqYHtcrtLyyTYmoD8hbcnJUfa3U\
    +
     9496.      63FxWNus7uic3Qm1BzEecJW0MAz+W2CyN9FLIy+EpSy6CjkXsllZw1uBs1SxrQWM97/vnHu7m\
    +
     9497.      6OtrkRl8AtBN3RDxI/fg7dZLLtDFYuCYYPMwXiO6ZIpwJ1GGydI9oUYYgnQQKDKoMTcwsjrfe\
    +
     9498.      6Tcht6y18bLcpNfX41WE27447vLNzHuF+j15co5N7Py8vKUpTCoghHMEYKkM6y02lvX+9XiFg\
    +
     9499.      6xBKMRNiwX69+LJb2Xa5WGqo7Rlk0cxsLVd0l2UXAW5jORg31sFMKYWXsDcRUKRDP8Q87OjiM\
    +
     9500.      6dI1hNEt43netf8rOyfp+L58fq3holY9gxXwRJLY6gahgLQi4hS8w9LS+rFcJtdSCBrQLWsMs\
    +
     9501.      6aDg/n8/P8/N+fcyoLepYr3W/CIUT7HsRQTtkduddbVfbo6Twt6fyJVPRrUGqRkWp3rdry65v\
    +
     9502.      6sPYInyq1mPHrQDrqGJYI/LzA/QAzAXLnx+lu9uxHTEka9xgWgRvqEioskh+UWgD4nDvTAxaz\
    +
     9503.      63v9BqqmFuQwy1wSXye1Df1NXVF7G8bUFxUE4F9axG5fm+vFQJvP8iuYjrFveB6++AqmJTQJ0\
    +
     9504.      69GHjbPhzdSzkZGxokQzONVs0R0FCPJz1hJKbvDKsaj9hT0vp/gH5oiT8pAbWsBChwAbxHgDd\
    +
     9505.      659iJVZE3bAzPRN1RuG+MT7th+J3i6KFwVJvPvsGRDIZW4P2rVfiKjDVBM2Va+w6PgI0c5u3K\
    +
     9506.      6O7MrWryPhXFFdBoAwi2JCaW9sZ3fTagQ4Tld6u4djwcWzeCdiYoeNbfalsRYo740afYQ1Rid\
    +
     9507.      6Bp/E9mbcTemEjoWWXIU7I5nK5H/BEqmZnPMyhDV234BTLQKCe6nhU+frwQo1gNFWf+eQGN62\
    +
     9508.      6aeF7BuzaN/94W2xlKd8t8KMA/3uoxymFt19OlCjYZeaMWbTKM9Yog9zDhptYMOzIQAoO7kn6\
    +
     9509.      6nTao8CxjrRRgjKe7mKa+tzuufhAAZtgjA92THkulWvIzEi0++j1DvXMnupDUS8aVusWain+c\
    +
     9510.      6CcvmR5orC+RcJs3wVahLYyEcqbvAS2e0QJ6BlU36R/IEd9Aol9q+M+UGvlo8EyRzISvqusNS\
    +
     9511.      67ePQ6cQzG1s725db4jNYNHAfF3KFG8wHqDwZDpWDsJ5qRLXR1ulFx85GhkypPubYaCiOQ5DR\
    +
     9512.      6PQUiNpgk4fLJHenSMLMiswXsqW4Cpln1rFoHzpOoBbuZIixmVyhKajeqlFmp8zNAEsbEJz0g\
    +
     9513.      6X0qlQuykZhf82pkhq2hWtCtYUdBODn6iPTBJT5Zk8IqFxqfBeFKjXk/sMeumhT8muOtq2Bgn\
    +
     9514.      6dR4fj6RoOi0zI25kajAXlDZhUhS39jipk39h/69AfDPBLmOxhDj7Lg/WUTbOwJiJ3p7WtOpm\
    +
     9515.      6ypARmhorQifINNm1CNS99GfDcLbD8sn8Fvlmvn7CmW65Pdmu6bKtuE0tn7NglIX1e/JAJP+G\
    +
     9516.      6gB3At7cSOp92rl0lp0pp0xVb5YaQedwGgcJA1pT4cy24lS+jvzDw86YTfb2igJm5MysHmejW\
    +
     9517.      6ZTGXpoAoLBLucUGEz/DwbjqOdzGAl5jy5VoCQws5zNYl4SVt030aZulYMgpDBPZd+kL0wV+w\
    +
     9518.      6nob2LPRDQGEbdUoeFm4fEKio9c/ferVlpSO8Bx7OFZyHip1PIizvoqFe02kpmS17TvIOty42\
    +
     9519.      6+Q0QaCnOpeLsPwwo+vixIeIeUjucUsKejVlez35qyuC0mm5pJJJLEVP2JAe/LTOwUUfKJkNy\
    +
     9520.      6lEe3Kdth241ZNQmkVcAIh6DZJBzvQov5fn3JZA0phBWdNq5iTsm5N2D8gyve3V3X2o3zF3VY\
    +
     9521.      6OqEBgTIADAbC69z7vOKJjGOzHRmUUwLU66iabtIbqR6SPOHCL+fCTfvpKcB/oG2p3wRKErEJ\
    +
     9522.      6v1YOfu9iaKEMLXS3ptdH8fwN2Rdww9bZ7rFa2bwrzcyux3o+hPV6bJZpb71j7lLAdzge3VX7\
    +
     9523.      69uSCdz6f/FDb7+wzWnbbDSPj9R20+PybDUm/lVAsTuF0aycFQwJfPCUwcBvCGWEq6xoTIEOy\
    +
     9524.      6b0bLta20+LYRYdyEceX7ypfezQKIy5OvJTAHCJy/WyOYaDVyPucMsHnZ0GCH75Cd//te1Bv2\
    +
     9525.      6RkMykqYurBiNbuH3Xfuprirr4Dd453O6abAYGb5tw1d6wrBL8p1J1Sx9Lgw7yxqYn0FTrc0y\
    +
     9526.      659yLlV+4zIkLfZlPFRVnanHpTyrIlpn4lGkt269+JXnIWhEQWNvuVsrt531jr+8AHkVZfQU8\
    +
     9527.      68U/4AUZMuOj5iliigFrof/usmloYEI1f8erhJku75snYW7YmFmUcoC7UtG/KfJRuz6j0tWPa\
    +
     9528.      656J5QA0rJHwSIhNT4GWvez19HT2lia+Pz7/+MVEWlvjY6+9P85a0y9qWkTzQ7nF0wDXpQpw3\
    +
     9529.      6K4xnfK2L08b5MrxdeI+DDfVDeV2JY0Fp6KH602tj2MbxxKM8oG+wTkE/dr8jyo4Sfs/IV6uf\
    +
     9530.      6+IIXpH2Nca1+WCJV5qEv193bcUELLR4iFu83xUedKy9353tn+3o01dF2bNEQ3fK9Q5tCXrCi\
    +
     9531.      6La+woCuvEeYrr+PiN2+i2V/eDJck580pyra8BV5ZIZbpe3kr5vJD3pqoGsnbcl/d+ndvR23b\
    +
     9532.      6K41M4dKwaAwDaMA1gZGBoQWAcYE9mYkmQOnAjkaG41FkGkIP2BAIgKvUvzhpE5JbA6lze2iL\
    +
     9533.      65Nr+AwiDo2W4BStvK30dKy0JGNbzAY5akexsrV0xo5K8rY50LOTLvDyukIZNbRLKOCk18mD3\
    +
     9534.      6WxmZGlsCMxNdGFYGNJnetUWyCPgo4BONEL4I9b8UeEBGYXuCdCd+DkctrqVLYXGSfE46kvAu\
    +
     9535.      6+ltK5SRxQPnjUqyJXsSYs6VY6WPKfns9+IXjHhd5wQvGipgFdMwVEQ+A7a2AAS0clQwH7KHW\
    +
     9536.      6SEGjhnklSVRghMtPy6gEtJRIKJeYkpQyQiequQunFOOU4BLdK1yp55olZS6npyPhMnvK7xIa\
    +
     9537.      6pyNj+JctcQLXenBOCms46aMkenIx45WpXqxxVJQLz/vgpmAVa0fmDv6Pue9xVTBPfVxCUGfj\
    +
     9538.      61R8uVi8Zu9nRFqk/t0gR6wmWOlzuKRqk33HpO8qQ+nbGoEZLL/0Va156SJ+u+t86/os7ic49\
    +
     9539.      6/7xoEqvL+2E8VOyCTuT/7j269Zy4jUtN+g4="
    +
     9540.      6    ) format("woff2");
    +
     9541.      6}
    +
     9542.      6.JSLINT_,
    +
     9543.      6.JSLINT_ address,
    +
     9544.      6.JSLINT_ button,
    +
     9545.      6.JSLINT_ cite,
    +
     9546.      6.JSLINT_ dd,
    +
     9547.      6.JSLINT_ dfn,
    +
     9548.      6.JSLINT_ dl,
    +
     9549.      6.JSLINT_ dt,
    +
     9550.      6.JSLINT_ fieldset,
    +
     9551.      6.JSLINT_ fieldset > div,
    +
     9552.      6.JSLINT_ input,
    +
     9553.      6.JSLINT_ label,
    +
     9554.      6.JSLINT_ legend,
    +
     9555.      6.JSLINT_ ol,
    +
     9556.      6.JSLINT_ samp,
    +
     9557.      6.JSLINT_ style,
    +
     9558.      6.JSLINT_ textarea,
    +
     9559.      6.JSLINT_ ul {
    +
     9560.      6    border: 0;
    +
     9561.      6    box-sizing: border-box;
    +
     9562.      6    margin: 0;
    +
     9563.      6    padding: 0;
    +
     9564.      6}
    +
     9565.      6/* disable text inflation algorithm used on some smartphones and tablets */
    +
     9566.      6.JSLINT_ {
    +
     9567.      6    -ms-text-size-adjust: none;
    +
     9568.      6    -webkit-text-size-adjust: none;
    +
     9569.      6    text-size-adjust: none;
    +
     9570.      6}
    +
     9571.      6.JSLINT_REPORT_ div {
    +
     9572.      6    box-sizing: border-box;
    +
     9573.      6}
    +
     9574.      6/*csslint ignore:end*/
    +
     9575.      6
    +
     9576.      6/* css - jslint_report - font */
    +
     9577.      6.JSLINT_,
    +
     9578.      6.JSLINT_ fieldset legend,
    +
     9579.      6.JSLINT_ .center {
    +
     9580.      6    font-family: daley, sans-serif;
    +
     9581.      6    font-size: 14px;
    +
     9582.      6}
    +
     9583.      6.JSLINT_ fieldset textarea,
    +
     9584.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS dt,
    +
     9585.      6.JSLINT_ #JSLINT_REPORT_WARNINGS samp {
    +
     9586.      6    font-size: 12px;
    +
     9587.      6}
    +
     9588.      6.JSLINT_ fieldset textarea,
    +
     9589.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS > div {
    +
     9590.      6    font-family: monospace;
    +
     9591.      6}
    +
     9592.      6.JSLINT_ fieldset > div {
    +
     9593.      6    font-family: sans-serif;
    +
     9594.      6}
    +
     9595.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dfn {
    +
     9596.      6    font-style: normal;
    +
     9597.      6    font-weight: bold;
    +
     9598.      6}
    +
     9599.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dt {
    +
     9600.      6    font-style: italic;
    +
     9601.      6}
    +
     9602.      6.JSLINT_ #JSLINT_REPORT_TITLE {
    +
     9603.      6    font-size: 32px;
    +
     9604.      6}
    +
     9605.      6
    +
     9606.      6/* css - jslint_report - general */
    +
     9607.      6.JSLINT_ {
    +
     9608.      6    background: antiquewhite;
    +
     9609.      6}
    +
     9610.      6.JSLINT_ fieldset {
    +
     9611.      6    background: gainsboro;
    +
     9612.      6    clear: both;
    +
     9613.      6    margin: 16px 40px;
    +
     9614.      6    width: auto;
    +
     9615.      6}
    +
     9616.      6.JSLINT_ fieldset address {
    +
     9617.      6    float: right;
    +
     9618.      6}
    +
     9619.      6.JSLINT_ fieldset legend,
    +
     9620.      6.JSLINT_ .center {
    +
     9621.      6    text-align: center;
    +
     9622.      6}
    +
     9623.      6.JSLINT_ fieldset legend {
    +
     9624.      6    background: darkslategray;
    +
     9625.      6    color: white;
    +
     9626.      6    padding: 4px 0;
    +
     9627.      6    width: 100%;
    +
     9628.      6}
    +
     9629.      6.JSLINT_ fieldset textarea {
    +
     9630.      6    padding: 4px;
    +
     9631.      6    resize: none;
    +
     9632.      6    white-space: pre;
    +
     9633.      6    width: 100%;
    +
     9634.      6}
    +
     9635.      6.JSLINT_ fieldset textarea::selection {
    +
     9636.      6    background: wheat;
    +
     9637.      6}
    +
     9638.      6.JSLINT_ fieldset > div {
    +
     9639.      6    padding: 16px;
    +
     9640.      6    width: 100%;
    +
     9641.      6    word-wrap: break-word;
    +
     9642.      6}
    +
     9643.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level {
    +
     9644.      6    background: cornsilk;
    +
     9645.      6    padding: 8px 16px;
    +
     9646.      6}
    +
     9647.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dd {
    +
     9648.      6    line-height: 20px;
    +
     9649.      6    padding-left: 120px;
    +
     9650.      6}
    +
     9651.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dfn {
    +
     9652.      6    display: block;
    +
     9653.      6    line-height: 20px;
    +
     9654.      6}
    +
     9655.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dl {
    +
     9656.      6    position: relative
    +
     9657.      6}
    +
     9658.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dt {
    +
     9659.      6    line-height: 20px;
    +
     9660.      6    position: absolute;
    +
     9661.      6    text-align: right;
    +
     9662.      6    width: 100px;
    +
     9663.      6}
    +
     9664.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level0 {
    +
     9665.      6    background: white;
    +
     9666.      6}
    +
     9667.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level1 {
    +
     9668.      6    /* yellow */
    +
     9669.      6    background: #ffffe0;
    +
     9670.      6    margin-left: 16px;
    +
     9671.      6}
    +
     9672.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level2 {
    +
     9673.      6    /* green */
    +
     9674.      6    background: #e0ffe0;
    +
     9675.      6    margin-left: 32px;
    +
     9676.      6}
    +
     9677.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level3 {
    +
     9678.      6    /* blue */
    +
     9679.      6    background: #D0D0ff;
    +
     9680.      6    margin-left: 48px;
    +
     9681.      6}
    +
     9682.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level4 {
    +
     9683.      6    /* purple */
    +
     9684.      6    background: #ffe0ff;
    +
     9685.      6    margin-left: 64px;
    +
     9686.      6}
    +
     9687.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level5 {
    +
     9688.      6    /* red */
    +
     9689.      6    background: #ffe0e0;
    +
     9690.      6    margin-left: 80px;
    +
     9691.      6}
    +
     9692.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level6 {
    +
     9693.      6    /* orange */
    +
     9694.      6    background: #ffe390;
    +
     9695.      6    margin-left: 96px;
    +
     9696.      6}
    +
     9697.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level7 {
    +
     9698.      6    /* gray */
    +
     9699.      6    background: #e0e0e0;
    +
     9700.      6    margin-left: 112px;
    +
     9701.      6}
    +
     9702.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level8 {
    +
     9703.      6    margin-left: 128px;
    +
     9704.      6}
    +
     9705.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level9 {
    +
     9706.      6    margin-left: 144px;
    +
     9707.      6}
    +
     9708.      6.JSLINT_ #JSLINT_REPORT_PROPERTIES {
    +
     9709.      6    background: transparent;
    +
     9710.      6}
    +
     9711.      6.JSLINT_ #JSLINT_REPORT_PROPERTIES textarea {
    +
     9712.      6    background: honeydew;
    +
     9713.      6    height: 100px;
    +
     9714.      6}
    +
     9715.      6.JSLINT_ #JSLINT_REPORT_TITLE {
    +
     9716.      6    color: darkslategray;
    +
     9717.      6    padding-top: 16px;
    +
     9718.      6}
    +
     9719.      6.JSLINT_ #JSLINT_REPORT_WARNINGS cite {
    +
     9720.      6    display: block;
    +
     9721.      6    margin: 16px 0 4px 0;
    +
     9722.      6    overflow-x: hidden;
    +
     9723.      6    white-space: pre-line;
    +
     9724.      6}
    +
     9725.      6.JSLINT_ #JSLINT_REPORT_WARNINGS cite:nth-child(1) {
    +
     9726.      6    margin-top: 0;
    +
     9727.      6}
    +
     9728.      6.JSLINT_ #JSLINT_REPORT_WARNINGS samp {
    +
     9729.      6    background: lavenderblush;
    +
     9730.      6    display: block;
    +
     9731.      6    padding: 4px;
    +
     9732.      6    white-space: pre-wrap;
    +
     9733.      6}
    +
     9734.      6.JSLINT_ #JSLINT_REPORT_WARNINGS > div {
    +
     9735.      6    background: pink;
    +
     9736.      6    max-height: 400px;
    +
     9737.      6    overflow-y: auto;
    +
     9738.      6}
    +
     9739.      6.JSLINT_ #JSLINT_REPORT_WARNINGS > legend {
    +
     9740.      6/* Google Lighthouse Accessibility - Background and foreground colors do not */
    +
     9741.      6/* have a sufficient contrast ratio. */
    +
     9742.      6    /* background: indianred; */
    +
     9743.      6    background: #b44;
    +
     9744.      6}
    +
     9745.      6</style>
    +
     9746.      6            `).trim() + "\n";
    +
     9747.      6
    +
     9748.      6// Produce the Title.
    +
     9749.      6
    +
     9750.      6    html += "<div class=\"center\" id=\"JSLINT_REPORT_TITLE\">\n";
    +
     9751.      6    html += "JSLint Report\n";
    +
     9752.      6    html += "</div>\n";
    +
     9753.      6
    +
     9754.      6// Produce the HTML Error Report.
    +
     9755.      6// <cite>
    +
     9756.      6//     <address>LINE_NUMBER</address>
    +
     9757.      6//     MESSAGE
    +
     9758.      6// </cite>
    +
     9759.      6// <samp>EVIDENCE</samp>
    +
     9760.      6
    +
     9761.      6    html += "<fieldset id=\"JSLINT_REPORT_WARNINGS\">\n";
    +
     9762.      6    html += "<legend>Report: Warnings (" + warnings.length + ")</legend>\n";
    +
     9763.      6    html += "<div>\n";
    +
     9764.      1    if (stop) {
    +
     9765.      1        html += "<div class=\"center\">JSLint was unable to finish.</div>\n";
    +
     9766.      1    }
    +
     9767.      7    warnings.forEach(function ({
    +
     9768.      7        column,
    +
     9769.      7        line,
    +
     9770.      7        line_source,
    +
     9771.      7        message,
    +
     9772.      7        stack_trace = ""
    +
     9773.      7    }, ii) {
    +
     9774.      7        html += (
    +
     9775.      7            "<cite>"
    +
     9776.      7            + address(line, column)
    +
     9777.      7            + htmlEscape((ii + 1) + ". " + message)
    +
     9778.      7            + "</cite>"
    +
     9779.      7            + "<samp>"
    +
     9780.      7            + htmlEscape(line_source.slice(0, 400) + "\n" + stack_trace)
    +
     9781.      7            + "</samp>\n"
    +
     9782.      7        );
    +
     9783.      7    });
    +
     9784.      3    if (warnings.length === 0) {
    +
     9785.      3        html += "<div class=\"center\">There are no warnings.</div>\n";
    +
     9786.      3    }
    +
     9787.      6    html += "</div>\n";
    +
     9788.      6    html += "</fieldset>\n";
    +
     9789.      6
    +
     9790.      6// Produce the /*property*/ directive.
    +
     9791.      6
    +
     9792.      6    html += "<fieldset id=\"JSLINT_REPORT_PROPERTIES\">\n";
    +
     9793.      6    html += (
    +
     9794.      6        "<legend>Report: Properties ("
    +
     9795.      6        + Object.keys(property).length
    +
     9796.      6        + ")</legend>\n"
    +
     9797.      6    );
    +
     9798.      6    html += "<label>\n";
    +
     9799.      6    html += "<textarea readonly>";
    +
     9800.      6    html += "/*property";
    +
     9801.    301    Object.keys(property).sort().forEach(function (key, ii) {
    +
     9802.    300        if (ii !== 0) {
    +
     9803.    300            html += ",";
    +
     9804.    300            length_80 += 2;
    +
     9805.    300        }
    +
     9806.     42        if (length_80 + key.length >= 80) {
    +
     9807.     42            length_80 = 4;
    +
     9808.     42            html += "\n   ";
    +
     9809.     42        }
    +
     9810.    301        html += " " + key;
    +
     9811.    301        length_80 += key.length;
    +
     9812.    301    });
    +
     9813.      6    html += "\n*/\n";
    +
     9814.      6    html += "</textarea>\n";
    +
     9815.      6    html += "</label>\n";
    +
     9816.      6    html += "</fieldset>\n";
    +
     9817.      6
    +
     9818.      6// Produce the HTML Function Report.
    +
     9819.      6// <div class=LEVEL>
    +
     9820.      6//     <address>LINE_NUMBER</address>
    +
     9821.      6//     <dfn>FUNCTION_NAME_AND_SIGNATURE</dfn>
    +
     9822.      6//     <dl>
    +
     9823.      6//         <dt>DETAIL</dt>
    +
     9824.      6//         <dd>NAMES</dd>
    +
     9825.      6//     </dl>
    +
     9826.      6// </div>
    +
     9827.      6
    +
     9828.      6    html += "<fieldset id=\"JSLINT_REPORT_FUNCTIONS\">\n";
    +
     9829.      6    html += "<legend>Report: Functions (" + functions.length + ")</legend>\n";
    +
     9830.      6    html += "<div>\n";
    +
     9831.      2    if (json) {
    +
     9832.      2
    +
     9833.      2// Bugfix - fix website crashing when linting pure json-object.
    +
     9834.      2// return (
    +
     9835.      2
    +
     9836.      2        html += (
    +
     9837.      2            warnings.length === 0
    +
     9838.      2            ? "<div class=\"center\">JSON: good.</div>\n"
    +
     9839.      2            : "<div class=\"center\">JSON: bad.</div>\n"
    +
     9840.      2        );
    +
     9841.      4    } else if (functions.length === 0) {
    +
     9842.      4        html += "<div class=\"center\">There are no functions.</div>\n";
    +
     9843.      4    }
    +
     9844.      6    exports = Object.keys(exports).sort();
    +
     9845.      6    froms.sort();
    +
     9846.      6    global = Object.keys(global.context).sort();
    +
     9847.      6    module = (
    +
     9848.      6        module
    +
     9849.      1        ? "module"
    +
     9850.      5        : "global"
    +
     9851.      6    );
    +
     9852.      3    if (global.length + froms.length + exports.length > 0) {
    +
     9853.      3        if (functions.length === 0) {
    +
     9854.      3            html += "<br>\n";
    +
     9855.      3        }
    +
     9856.      3        html += "<div class=\"level level0\">\n";
    +
     9857.      3        html += detail(module, global);
    +
     9858.      3        html += detail("import from", froms);
    +
     9859.      3        html += detail("export", exports);
    +
     9860.      3        html += "</div>\n";
    +
     9861.      3    }
    +
     9862.    321    functions.forEach(function (the_function) {
    +
     9863.    321        let {
    +
     9864.    321            context,
    +
     9865.    321            from,
    +
     9866.    321            id,
    +
     9867.    321            level,
    +
     9868.    321            line,
    +
     9869.    321            name,
    +
     9870.    321
    +
     9871.    321// Bugfix - fix html-report from crashing if parameters is undefined.
    +
     9872.    321
    +
     9873.    321            parameters = [],
    +
     9874.    321            signature
    +
     9875.    321        } = the_function;
    +
     9876.    321        let list = Object.keys(context);
    +
     9877.    321        let params;
    +
     9878.    321        html += (
    +
     9879.    321            "<div class=\"level level" + htmlEscape(level) + "\">"
    +
     9880.    321            + address(line, from + 1)
    +
     9881.    321            + "<dfn>"
    +
     9882.    321            + (
    +
     9883.    321                id === "=>"
    +
     9884.      1                ? (
    +
     9885.      1                    "\u00ab" + htmlEscape(name) + "\u00bb"
    +
     9886.      1                    + htmlEscape(signature)
    +
     9887.      1                    + " =>"
    +
     9888.      1                )
    +
     9889.    320                : (
    +
     9890.    320                    typeof name === "string"
    +
     9891.    320                    ? "\u00ab" + htmlEscape(name) + "\u00bb"
    +
     9892.    320                    : htmlEscape(name.id)
    +
     9893.    320                ) + htmlEscape(signature)
    +
     9894.    321            )
    +
     9895.    321            + "</dfn>"
    +
     9896.    321        );
    +
     9897.    321        params = [];
    +
     9898.    470        parameters.forEach(function extract({
    +
     9899.    470            id,
    +
     9900.    470            names
    +
     9901.    470        }) {
    +
     9902.    470            switch (id) {
    +
     9903.      6            case "[":
    +
     9904.     42            case "{":
    +
     9905.     42
    +
     9906.     42// Recurse extract().
    +
     9907.     42
    +
     9908.     42                names.forEach(extract);
    +
     9909.     42                break;
    +
     9910.      4            case "ignore":
    +
     9911.      4                break;
    +
     9912.    424            default:
    +
     9913.    424                params.push(id);
    +
     9914.    470            }
    +
     9915.    470        });
    +
     9916.    321        html += detail("parameter", params.sort());
    +
     9917.    321        list.sort();
    +
     9918.   2203        html += detail("variable", list.filter(function (id) {
    +
     9919.   2203            return (
    +
     9920.   2203                context[id].role === "variable"
    +
     9921.   1693                && context[id].parent === the_function
    +
     9922.   2203            );
    +
     9923.   2203        }));
    +
     9924.   2203        html += detail("exception", list.filter(function (id) {
    +
     9925.   2203            return context[id].role === "exception";
    +
     9926.   2203        }));
    +
     9927.   2203        html += detail("closure", list.filter(function (id) {
    +
     9928.   2203            return (
    +
     9929.   2203                context[id].closure === true
    +
     9930.   1494                && context[id].parent === the_function
    +
     9931.   2203            );
    +
     9932.   2203        }));
    +
     9933.   2203        html += detail("outer", list.filter(function (id) {
    +
     9934.   2203            return (
    +
     9935.   2203                context[id].parent !== the_function
    +
     9936.   1190                && context[id].parent.id !== "(global)"
    +
     9937.   2203            );
    +
     9938.   2203        }));
    +
     9939.   2203        html += detail(module, list.filter(function (id) {
    +
     9940.   2203            return context[id].parent.id === "(global)";
    +
     9941.   2203        }));
    +
     9942.   2203        html += detail("label", list.filter(function (id) {
    +
     9943.   2203            return context[id].role === "label";
    +
     9944.   2203        }));
    +
     9945.    321        html += "</div>\n";
    +
     9946.    321    });
    +
     9947.      6    html += "</div>\n";
    +
     9948.      6    html += "</fieldset>\n";
    +
     9949.      6    return html;
    +
     9950.      6}
    +
     9951.      1
    +
     9952.     10async function jstestDescribe(description, testFunction) {
    +
     9953.     10
    +
     9954.     10// This function will create-and-run test-group <testFunction>
    +
     9955.     10// with given <description>.
    +
     9956.     10
    +
     9957.     10    let message;
    +
     9958.     10    let result;
    +
     9959.     10    let timerTimeout;
    +
     9960.     10
    +
     9961.     10// Init jstestTimeStart.
    +
     9962.     10
    +
     9963.      1    if (jstestTimeStart === undefined) {
    +
     9964.      1        jstestTimeStart = jstestTimeStart || Date.now();
    +
     9965.      1        process.on("exit", jstestOnExit);
    +
     9966.      1    }
    +
     9967.     10
    +
     9968.     10// PR-457 - Wait awhile for imports to initialize.
    +
     9969.     10
    +
     9970.     10    await new Promise(function (resolve) {
    +
     9971.     10        setTimeout(resolve);
    +
     9972.     10    });
    +
     9973.     10
    +
     9974.     10// Init jstestItList.
    +
     9975.     10
    +
     9976.     10    jstestItList = [];
    +
     9977.     10    testFunction();
    +
     9978.     10
    +
     9979.     10// Wait for jstestItList to resolve.
    +
     9980.     10
    +
     9981.     10    timerTimeout = setTimeout(noop, 0x7fffffff);
    +
     9982.     10    result = await Promise.all(jstestItList);
    +
     9983.     10    clearTimeout(timerTimeout);
    +
     9984.     10
    +
     9985.     10// Print test results.
    +
     9986.     10
    +
     9987.     10    message = (
    +
     9988.     10        "\n  " + (Date.now() - jstestTimeStart) + "ms"
    +
     9989.     10        + " - test describe - " + description + "\n"
    +
     9990.     66        + result.map(function ([
    +
     9991.     66            err, description, mode
    +
     9992.     66        ]) {
    +
     9993.     66            jstestItCount += 1;
    +
     9994.      1            if (err) {
    +
     9995.      1                jstestCountFailed += 1;
    +
     9996.      1                err = (
    +
     9997.      1                    "    \u001b[31m\u2718 " + jstestItCount + ". test it - "
    +
     9998.      1                    + description + "\n" + err.stack + "\u001b[39m"
    +
     9999.      1                );
    +
    10000.      1                if (mode === "pass") {
    +
    10001.      1                    jstestCountFailed -= 1;
    +
    10002.      1                    err = "";
    +
    10003.      1                }
    +
    10004.      1            }
    +
    10005.     66            return err || (
    +
    10006.     66                "    \u001b[32m\u2714 " + jstestItCount + ". test it - "
    +
    10007.     66                + description + "\u001b[39m"
    +
    10008.     66            );
    +
    10009.     66        }).join("\n")
    +
    10010.     10    );
    +
    10011.     10    console.error(message);
    +
    10012.     10}
    +
    10013.      1
    +
    10014.     66function jstestIt(description, testFunction, mode) {
    +
    10015.     66
    +
    10016.     66// This function will create-and-run test-case <testFunction>
    +
    10017.     66// inside current test-group with given <description>.
    +
    10018.     66
    +
    10019.     66    jstestCountTotal += 1;
    +
    10020.     66    jstestItList.push(new Promise(async function (resolve) {
    +
    10021.     66        let err;
    +
    10022.     66        try {
    +
    10023.     65            await testFunction();
    +
    10024.     65        } catch (errCaught) {
    +
    10025.      1            err = errCaught;
    +
    10026.      1        }
    +
    10027.     66        resolve([err, description, mode]);
    +
    10028.     66    }));
    +
    10029.     66}
    +
    10030.      1
    +
    10031.      2function jstestOnExit(exitCode, mode) {
    +
    10032.      2
    +
    10033.      2// This function will on process-exit, print test-report
    +
    10034.      2// and exit with non-zero exit-code if any test failed.
    +
    10035.      2
    +
    10036.      2    let message = (
    +
    10037.      2        (
    +
    10038.      2            (jstestCountFailed || mode === "testsFailed")
    +
    10039.      1            ? "\n\u001b[31m"
    +
    10040.      1            : "\n\u001b[32m"
    +
    10041.      2        )
    +
    10042.      2        + "  tests total  - " + jstestCountTotal + "\n"
    +
    10043.      2        + "  tests failed - " + jstestCountFailed + "\n"
    +
    10044.      2        + "\n"
    +
    10045.      2        + "  time finished - "
    +
    10046.      2        + Number(Date.now() - jstestTimeStart).toLocaleString()
    +
    10047.      2        + " ms\n"
    +
    10048.      2        + "\u001b[39m"
    +
    10049.      2    );
    +
    10050.      1    if (mode !== "testsFailed") {
    +
    10051.      1        console.error(message);
    +
    10052.      1    }
    +
    10053.      2    process.exitCode = exitCode || jstestCountFailed;
    +
    10054.      2    return message;
    +
    10055.      2}
    +
    10056.      1
    +
    10057.    107async function moduleFsInit() {
    +
    10058.    107
    +
    10059.    107// This function will import nodejs builtin-modules if they have not yet been
    +
    10060.    107// imported.
    +
    10061.    107
    +
    10062.    107// State 3 - Modules already imported.
    +
    10063.    107
    +
    10064.    104    if (moduleFs !== undefined) {
    +
    10065.    104        return;
    +
    10066.    104    }
    +
    10067.      3
    +
    10068.      3// State 2 - Wait while modules are importing.
    +
    10069.      3
    +
    10070.      3    if (moduleFsInitResolveList !== undefined) {
    +
    10071.      2        return new Promise(function (resolve) {
    +
    10072.      2            moduleFsInitResolveList.push(resolve);
    +
    10073.      2        });
    +
    10074.      2    }
    +
    10075.      1
    +
    10076.      1// State 1 - Start importing modules.
    +
    10077.      1
    +
    10078.      1    moduleFsInitResolveList = [];
    +
    10079.      1    [
    +
    10080.      1        moduleChildProcess,
    +
    10081.      1        moduleFs,
    +
    10082.      1        modulePath,
    +
    10083.      1        moduleUrl
    +
    10084.      1    ] = await Promise.all([
    +
    10085.      1        import("child_process"),
    +
    10086.      1        import("fs"),
    +
    10087.      1        import("path"),
    +
    10088.      1        import("url")
    +
    10089.      1    ]);
    +
    10090.      2    while (moduleFsInitResolveList.length > 0) {
    +
    10091.      2        moduleFsInitResolveList.shift()();
    +
    10092.      2    }
    +
    10093.    107}
    +
    10094.      1
    +
    10095.   2728function noop(val) {
    +
    10096.   2728
    +
    10097.   2728// This function will do nothing except return <val>.
    +
    10098.   2728
    +
    10099.   2728    return val;
    +
    10100.   2728}
    +
    10101.      1
    +
    10102.  12919function objectDeepCopyWithKeysSorted(obj) {
    +
    10103.  12919
    +
    10104.  12919// This function will recursively deep-copy <obj> with keys sorted.
    +
    10105.  12919
    +
    10106.  12919    let sorted;
    +
    10107.   8995    if (typeof obj !== "object" || !obj) {
    +
    10108.   8995        return obj;
    +
    10109.   8995    }
    +
    10110.   3924
    +
    10111.   3924// Recursively deep-copy list with child-keys sorted.
    +
    10112.   3924
    +
    10113.   3924    if (Array.isArray(obj)) {
    +
    10114.   1338        return obj.map(objectDeepCopyWithKeysSorted);
    +
    10115.   2586    }
    +
    10116.   2586
    +
    10117.   2586// Recursively deep-copy obj with keys sorted.
    +
    10118.   2586
    +
    10119.   2586    sorted = Object.create(null);
    +
    10120.   7457    Object.keys(obj).sort().forEach(function (key) {
    +
    10121.   7457        sorted[key] = objectDeepCopyWithKeysSorted(obj[key]);
    +
    10122.   7457    });
    +
    10123.   2586    return sorted;
    +
    10124.   2586}
    +
    10125.      1
    +
    10126.   2791function object_assign_from_list(dict, list, val) {
    +
    10127.   2791
    +
    10128.   2791// Assign each property-name from <list> to <dict>.
    +
    10129.   2791
    +
    10130.  89862    list.forEach(function (key) {
    +
    10131.  89862        dict[key] = val;
    +
    10132.  89862    });
    +
    10133.   2791    return dict;
    +
    10134.   2791}
    +
    10135.      1
    +
    10136.     97function v8CoverageListMerge(processCovs) {
    +
    10137.     97
    +
    10138.     97// This function is derived from MIT Licensed v8-coverage at
    +
    10139.     97// https://github.com/demurgos/v8-coverage/tree/master/ts
    +
    10140.     97// https://github.com/demurgos/v8-coverage/blob/master/ts/LICENSE.md
    +
    10141.     97//
    +
    10142.     97// Merges a list of v8 process coverages.
    +
    10143.     97// The result is normalized.
    +
    10144.     97// The input values may be mutated, it is not safe to use them after passing
    +
    10145.     97// them to this function.
    +
    10146.     97// The computation is synchronous.
    +
    10147.     97// @param processCovs Process coverages to merge.
    +
    10148.     97// @return Merged process coverage.
    +
    10149.     97
    +
    10150.     97    let resultMerged = [];      // List of merged scripts from processCovs.
    +
    10151.     97    let urlToScriptDict = new Map();    // Map scriptCov.url to scriptCovs.
    +
    10152.     97
    +
    10153.   1094    function compareRangeList(aa, bb) {
    +
    10154.   1094
    +
    10155.   1094// Compares two range coverages.
    +
    10156.   1094// The ranges are first ordered by ascending `startOffset` and then by
    +
    10157.   1094// descending `endOffset`.
    +
    10158.   1094// This corresponds to a pre-order tree traversal.
    +
    10159.   1094
    +
    10160.   1065        if (aa.startOffset !== bb.startOffset) {
    +
    10161.   1065            return aa.startOffset - bb.startOffset;
    +
    10162.   1065        }
    +
    10163.     29        return bb.endOffset - aa.endOffset;
    +
    10164.     29    }
    +
    10165.     97
    +
    10166.   1707    function dictKeyValueAppend(dict, key, val) {
    +
    10167.   1707
    +
    10168.   1707// This function will append <val> to list <dict>[<key>].
    +
    10169.   1707
    +
    10170.   1707        let list = dict.get(key);
    +
    10171.   1165        if (list === undefined) {
    +
    10172.   1165            list = [];
    +
    10173.   1165            dict.set(key, list);
    +
    10174.   1165        }
    +
    10175.   1707        list.push(val);
    +
    10176.   1707    }
    +
    10177.     97
    +
    10178.    384    function mergeTreeList(parentTrees) {
    +
    10179.    384
    +
    10180.    384// This function will return RangeTree object with <parentTrees> merged into
    +
    10181.    384// property-children.
    +
    10182.    384// @precondition Same `start` and `end` for all the parentTrees
    +
    10183.    384
    +
    10184.    119        if (parentTrees.length <= 1) {
    +
    10185.    119            return parentTrees[0];
    +
    10186.    265        }
    +
    10187.    265
    +
    10188.    265// new RangeTree().
    +
    10189.    265
    +
    10190.    265        return {
    +
    10191.    265
    +
    10192.    265// Merge parentTrees into property-children.
    +
    10193.    265
    +
    10194.    265            children: mergeTreeListToChildren(parentTrees),
    +
    10195.    669            delta: parentTrees.reduce(function (aa, bb) {
    +
    10196.    669                return aa + bb.delta;
    +
    10197.    669            }, 0),
    +
    10198.    265            end: parentTrees[0].end,
    +
    10199.    265            start: parentTrees[0].start
    +
    10200.    265        };
    +
    10201.    265    }
    +
    10202.     97
    +
    10203.    265    function mergeTreeListToChildren(parentTrees) {
    +
    10204.    265
    +
    10205.    265// This function will return <resultChildren> with <parentTrees> merged.
    +
    10206.    265
    +
    10207.    265        let openRange;
    +
    10208.    265        let parentToChildDict = new Map();      // Map parent to child.
    +
    10209.    265        let queueList;
    +
    10210.    265        let queueListIi = 0;
    +
    10211.    265        let queueOffset;
    +
    10212.    265        let queueTrees;
    +
    10213.    265        let resultChildren = [];
    +
    10214.    265        let startToTreeDict = new Map();        // Map tree.start to tree.
    +
    10215.    639        function nextXxx() {
    +
    10216.    639
    +
    10217.    639// Increment nextOffset, nextTrees.
    +
    10218.    639
    +
    10219.    639            let [
    +
    10220.    639                nextOffset, nextTrees
    +
    10221.    300            ] = queueList[queueListIi] || [];
    +
    10222.    639            let openRangeEnd;
    +
    10223.    583            if (queueTrees === undefined) {
    +
    10224.    583                queueListIi += 1;
    +
    10225.    583
    +
    10226.    583// Increment nextOffset, nextTrees.
    +
    10227.    583
    +
    10228.    583            } else if (nextOffset === undefined || nextOffset > queueOffset) {
    +
    10229.     56                nextOffset = queueOffset;
    +
    10230.     56                nextTrees = queueTrees;
    +
    10231.     56                queueTrees = undefined;
    +
    10232.     56
    +
    10233.     56// Concat queueTrees to nextTrees.
    +
    10234.     56
    +
    10235.     56            } else {
    +
    10236.     56                if (nextOffset === queueOffset) {
    +
    10237.     56                    queueTrees.forEach(function (tree) {
    +
    10238.     56                        nextTrees.push(tree);
    +
    10239.     56                    });
    +
    10240.     56                    queueTrees = undefined;
    +
    10241.     56                }
    +
    10242.     56                queueListIi += 1;
    +
    10243.     56            }
    +
    10244.    639
    +
    10245.    639// Reached end of queueList.
    +
    10246.    639
    +
    10247.    265            if (nextOffset === undefined) {
    +
    10248.    265                if (openRange !== undefined) {
    +
    10249.    265
    +
    10250.    265// Append nested-children from parentToChildDict (within openRange) to
    +
    10251.    265// resultChildren.
    +
    10252.    265
    +
    10253.    265                    resultAppendNextChild();
    +
    10254.    265                }
    +
    10255.    265                return true;
    +
    10256.    374            }
    +
    10257.    374            if (openRange !== undefined && openRange.end <= nextOffset) {
    +
    10258.    129
    +
    10259.    129// Append nested-children from parentToChildDict (within openRange) to
    +
    10260.    129// resultChildren.
    +
    10261.    129
    +
    10262.    129                resultAppendNextChild();
    +
    10263.    129                openRange = undefined;
    +
    10264.    374            }
    +
    10265.    374            if (openRange === undefined) {
    +
    10266.    292                openRangeEnd = nextOffset + 1;
    +
    10267.    502                nextTrees.forEach(function ({
    +
    10268.    502                    parentIi,
    +
    10269.    502                    tree
    +
    10270.    502                }) {
    +
    10271.    502                    openRangeEnd = Math.max(openRangeEnd, tree.end);
    +
    10272.    502
    +
    10273.    502// Append children from nextTrees to parentToChildDict.
    +
    10274.    502
    +
    10275.    502                    dictKeyValueAppend(parentToChildDict, parentIi, tree);
    +
    10276.    502                });
    +
    10277.    292                queueOffset = openRangeEnd;
    +
    10278.    292                openRange = {
    +
    10279.    292                    end: openRangeEnd,
    +
    10280.    292                    start: nextOffset
    +
    10281.    292                };
    +
    10282.    292            } else {
    +
    10283.    114                nextTrees.forEach(function ({
    +
    10284.    114                    parentIi,
    +
    10285.    114                    tree
    +
    10286.    114                }) {
    +
    10287.    114                    let right;
    +
    10288.     82                    if (tree.end > openRange.end) {
    +
    10289.     82                        right = treeSplit(tree, openRange.end);
    +
    10290.     82                        if (queueTrees === undefined) {
    +
    10291.     82                            queueTrees = [];
    +
    10292.     82                        }
    +
    10293.     82
    +
    10294.     82// new RangeTreeWithParent().
    +
    10295.     82
    +
    10296.     82                        queueTrees.push({
    +
    10297.     82                            parentIi,
    +
    10298.     82                            tree: right
    +
    10299.     82                        });
    +
    10300.     82                    }
    +
    10301.    114
    +
    10302.    114// Append children from nextTrees to parentToChildDict.
    +
    10303.    114
    +
    10304.    114                    dictKeyValueAppend(parentToChildDict, parentIi, tree);
    +
    10305.    114                });
    +
    10306.     82            }
    +
    10307.    639        }
    +
    10308.    292        function resultAppendNextChild() {
    +
    10309.    292
    +
    10310.    292// This function will append next child to <resultChildren>.
    +
    10311.    292
    +
    10312.    292            let treesMatching = [];
    +
    10313.    589            parentToChildDict.forEach(function (nested) {
    +
    10314.    589                if (
    +
    10315.    589                    nested.length === 1
    +
    10316.    563                    && nested[0].start === openRange.start
    +
    10317.    480                    && nested[0].end === openRange.end
    +
    10318.    468                ) {
    +
    10319.    468                    treesMatching.push(nested[0]);
    +
    10320.    468                } else {
    +
    10321.    121
    +
    10322.    121// new rangeTreeCreate().
    +
    10323.    121
    +
    10324.    121                    treesMatching.push({
    +
    10325.    121                        children: nested,
    +
    10326.    121                        delta: 0,
    +
    10327.    121                        end: openRange.end,
    +
    10328.    121                        start: openRange.start
    +
    10329.    121                    });
    +
    10330.    121                }
    +
    10331.    589            });
    +
    10332.    292            parentToChildDict.clear();
    +
    10333.    292
    +
    10334.    292// Recurse mergeTreeList().
    +
    10335.    292
    +
    10336.    292            resultChildren.push(mergeTreeList(treesMatching));
    +
    10337.    292        }
    +
    10338.     75        function treeSplit(tree, offset) {
    +
    10339.     75
    +
    10340.     75// This function will split <tree> along <offset> and return the right-side.
    +
    10341.     75// @precondition `tree.start < offset && offset < tree.end`
    +
    10342.     75// @return RangeTree Right part
    +
    10343.     75
    +
    10344.     75            let child;
    +
    10345.     75            let ii = 0;
    +
    10346.     75            let leftChildLen = tree.children.length;
    +
    10347.     75            let mid;
    +
    10348.     75            let resultTree;
    +
    10349.     75            let rightChildren;
    +
    10350.     75
    +
    10351.     75// TODO(perf): Binary search (check overhead) //jslint-ignore-line
    +
    10352.     75
    +
    10353.     19            while (ii < tree.children.length) {
    +
    10354.     19                child = tree.children[ii];
    +
    10355.     19                if (child.start < offset && offset < child.end) {
    +
    10356.     19
    +
    10357.     19// Recurse treeSplit().
    +
    10358.     19
    +
    10359.     19                    mid = treeSplit(child, offset);
    +
    10360.     19                    leftChildLen = ii + 1;
    +
    10361.     19                    break;
    +
    10362.     19                }
    +
    10363.     19                if (child.start >= offset) {
    +
    10364.     19                    leftChildLen = ii;
    +
    10365.     19                    break;
    +
    10366.     19                }
    +
    10367.     19                ii += 1;
    +
    10368.     19            }
    +
    10369.     75            rightChildren = tree.children.splice(
    +
    10370.     75                leftChildLen,
    +
    10371.     75                tree.children.length - leftChildLen
    +
    10372.     75            );
    +
    10373.      4            if (mid !== undefined) {
    +
    10374.      4                rightChildren.unshift(mid);
    +
    10375.      4            }
    +
    10376.     75
    +
    10377.     75// new rangeTreeCreate().
    +
    10378.     75
    +
    10379.     75            resultTree = {
    +
    10380.     75                children: rightChildren,
    +
    10381.     75                delta: tree.delta,
    +
    10382.     75                end: tree.end,
    +
    10383.     75                start: offset
    +
    10384.     75            };
    +
    10385.     75            tree.end = offset;
    +
    10386.     75            return resultTree;
    +
    10387.     75        }
    +
    10388.    265
    +
    10389.    265// Init startToTreeDict.
    +
    10390.    265
    +
    10391.    669        parentTrees.forEach(function (parentTree, parentIi) {
    +
    10392.    545            parentTree.children.forEach(function (child) {
    +
    10393.    545
    +
    10394.    545// Append child with child.start to startToTreeDict.
    +
    10395.    545
    +
    10396.    545                dictKeyValueAppend(startToTreeDict, child.start, {
    +
    10397.    545                    parentIi,
    +
    10398.    545                    tree: child
    +
    10399.    545                });
    +
    10400.    545            });
    +
    10401.    669        });
    +
    10402.    265
    +
    10403.    265// init queueList.
    +
    10404.    265
    +
    10405.    335        queueList = Array.from(startToTreeDict).map(function ([
    +
    10406.    335            startOffset, trees
    +
    10407.    335        ]) {
    +
    10408.    335
    +
    10409.    335// new StartEvent().
    +
    10410.    335
    +
    10411.    335            return [
    +
    10412.    335                startOffset, trees
    +
    10413.    335            ];
    +
    10414.    217        }).sort(function (aa, bb) {
    +
    10415.    217            return aa[0] - bb[0];
    +
    10416.    217        });
    +
    10417.    639        while (true) {
    +
    10418.    639            if (nextXxx()) {
    +
    10419.    639                break;
    +
    10420.    639            }
    +
    10421.    639        }
    +
    10422.    265        return resultChildren;
    +
    10423.    265    }
    +
    10424.     97
    +
    10425.    689    function sortFunc(funcCov) {
    +
    10426.    689
    +
    10427.    689// This function will normalize-and-sort <funcCov>.ranges.
    +
    10428.    689// Sorts the ranges (pre-order sort).
    +
    10429.    689// TODO: Tree-based normalization of the ranges. //jslint-ignore-line
    +
    10430.    689// @param funcCov Function coverage to normalize.
    +
    10431.    689
    +
    10432.    689        funcCov.ranges = treeToRanges(treeFromSortedRanges(
    +
    10433.    689            funcCov.ranges.sort(compareRangeList)
    +
    10434.    689        ));
    +
    10435.    689        return funcCov;
    +
    10436.    689    }
    +
    10437.     97
    +
    10438.    129    function sortScript(scriptCov) {
    +
    10439.    129
    +
    10440.    129// This function will normalize-and-sort <scriptCov>.functions.
    +
    10441.    129
    +
    10442.    129// Normalize-and-sort functions[xxx].ranges.
    +
    10443.    129
    +
    10444.    688        scriptCov.functions.forEach(function (funcCov) {
    +
    10445.    688            sortFunc(funcCov);
    +
    10446.    688        });
    +
    10447.    129
    +
    10448.    129// Sort functions by root range (pre-order sort).
    +
    10449.    129
    +
    10450.    559        scriptCov.functions.sort(function (aa, bb) {
    +
    10451.    559            return compareRangeList(aa.ranges[0], bb.ranges[0]);
    +
    10452.    559        });
    +
    10453.    129        return scriptCov;
    +
    10454.    129    }
    +
    10455.     97
    +
    10456.    888    function treeFromSortedRanges(ranges) {
    +
    10457.    888
    +
    10458.    888// @precondition `ranges` are well-formed and pre-order sorted
    +
    10459.    888
    +
    10460.    888        let root;
    +
    10461.    888        let stack = [];   // Stack of parent trees and parent counts.
    +
    10462.   1856        ranges.forEach(function (range) {
    +
    10463.   1856
    +
    10464.   1856// new rangeTreeCreate().
    +
    10465.   1856
    +
    10466.   1856            let node = {
    +
    10467.   1856                children: [],
    +
    10468.   1856                delta: range.count,
    +
    10469.   1856                end: range.endOffset,
    +
    10470.   1856                start: range.startOffset
    +
    10471.   1856            };
    +
    10472.   1856            let parent;
    +
    10473.   1856            let parentCount;
    +
    10474.    888            if (root === undefined) {
    +
    10475.    888                root = node;
    +
    10476.    888                stack.push([
    +
    10477.    888                    node, range.count
    +
    10478.    888                ]);
    +
    10479.    888                return;
    +
    10480.    968            }
    +
    10481.   1565            while (true) {
    +
    10482.   1565                [
    +
    10483.   1565                    parent, parentCount
    +
    10484.   1565                ] = stack[stack.length - 1];
    +
    10485.   1565
    +
    10486.   1565// assert: `top !== undefined` (the ranges are sorted)
    +
    10487.   1565
    +
    10488.   1565                if (range.startOffset < parent.end) {
    +
    10489.   1565                    break;
    +
    10490.   1565                }
    +
    10491.   1565                stack.pop();
    +
    10492.   1565            }
    +
    10493.    968            node.delta -= parentCount;
    +
    10494.    968            parent.children.push(node);
    +
    10495.    968            stack.push([
    +
    10496.    968                node, range.count
    +
    10497.    968            ]);
    +
    10498.    968        });
    +
    10499.    888        return root;
    +
    10500.    888    }
    +
    10501.     97
    +
    10502.    781    function treeToRanges(tree) {
    +
    10503.    781
    +
    10504.    781// Get the range coverages corresponding to the tree.
    +
    10505.    781// The ranges are pre-order sorted.
    +
    10506.    781
    +
    10507.    781        let count;
    +
    10508.    781        let cur;
    +
    10509.    781        let ii;
    +
    10510.    781        let parentCount;
    +
    10511.    781        let ranges = [];
    +
    10512.    781        let stack = [           // Stack of parent trees and counts.
    +
    10513.    781            [
    +
    10514.    781                tree, 0
    +
    10515.    781            ]
    +
    10516.    781        ];
    +
    10517.   1630        function normalizeRange(tree) {
    +
    10518.   1630
    +
    10519.   1630// @internal
    +
    10520.   1630
    +
    10521.   1630            let children = [];
    +
    10522.   1630            let curEnd;
    +
    10523.   1630            let head;
    +
    10524.   1630            let tail = [];
    +
    10525.    849            function endChain() {
    +
    10526.     18                if (tail.length !== 0) {
    +
    10527.     18                    head.end = tail[tail.length - 1].end;
    +
    10528.     18                    tail.forEach(function (tailTree) {
    +
    10529.     18                        tailTree.children.forEach(function (subChild) {
    +
    10530.     18                            subChild.delta += tailTree.delta - head.delta;
    +
    10531.     18                            head.children.push(subChild);
    +
    10532.     18                        });
    +
    10533.     18                    });
    +
    10534.     18                    tail.length = 0;
    +
    10535.     18                }
    +
    10536.    849
    +
    10537.    849// Recurse normalizeRange().
    +
    10538.    849
    +
    10539.    849                normalizeRange(head);
    +
    10540.    849                children.push(head);
    +
    10541.    849            }
    +
    10542.    867            tree.children.forEach(function (child) {
    +
    10543.    432                if (head === undefined) {
    +
    10544.    432                    head = child;
    +
    10545.    435                } else if (
    +
    10546.    435                    child.delta === head.delta && child.start === curEnd
    +
    10547.    435                ) {
    +
    10548.    435                    tail.push(child);
    +
    10549.    435                } else {
    +
    10550.    435                    endChain();
    +
    10551.    435                    head = child;
    +
    10552.    435                }
    +
    10553.    867                curEnd = child.end;
    +
    10554.    867            });
    +
    10555.    432            if (head !== undefined) {
    +
    10556.    432                endChain();
    +
    10557.    432            }
    +
    10558.    238            if (children.length === 1) {
    +
    10559.    238                if (
    +
    10560.    238                    children[0].start === tree.start
    +
    10561.    238                    && children[0].end === tree.end
    +
    10562.    238                ) {
    +
    10563.    238                    tree.delta += children[0].delta;
    +
    10564.    238                    tree.children = children[0].children;
    +
    10565.    238
    +
    10566.    238// `.lazyCount` is zero for both (both are after normalization)
    +
    10567.    238
    +
    10568.    238                    return;
    +
    10569.    238                }
    +
    10570.   1624            }
    +
    10571.   1624            tree.children = children;
    +
    10572.   1624        }
    +
    10573.    781        normalizeRange(tree);
    +
    10574.   1624        while (stack.length > 0) {
    +
    10575.   1624            [
    +
    10576.   1624                cur, parentCount
    +
    10577.   1624            ] = stack.pop();
    +
    10578.   1624            count = parentCount + cur.delta;
    +
    10579.   1624            ranges.push({
    +
    10580.   1624                count,
    +
    10581.   1624                endOffset: cur.end,
    +
    10582.   1624                startOffset: cur.start
    +
    10583.   1624            });
    +
    10584.   1624            ii = cur.children.length - 1;
    +
    10585.   1624            while (ii >= 0) {
    +
    10586.   1624                stack.push([
    +
    10587.   1624                    cur.children[ii], count
    +
    10588.   1624                ]);
    +
    10589.   1624                ii -= 1;
    +
    10590.   1624            }
    +
    10591.   1624        }
    +
    10592.    781        return ranges;
    +
    10593.    781    }
    +
    10594.     97
    +
    10595.      1    if (processCovs.length === 0) {
    +
    10596.      1        return {
    +
    10597.      1            result: []
    +
    10598.      1        };
    +
    10599.     96    }
    +
    10600.     96
    +
    10601.     96// Init urlToScriptDict.
    +
    10602.     96
    +
    10603.    234    processCovs.forEach(function ({
    +
    10604.    234        result
    +
    10605.    234    }) {
    +
    10606.    269        result.forEach(function (scriptCov) {
    +
    10607.    269            dictKeyValueAppend(urlToScriptDict, scriptCov.url, scriptCov);
    +
    10608.    269        });
    +
    10609.    234    });
    +
    10610.    129    urlToScriptDict.forEach(function (scriptCovs) {
    +
    10611.    129
    +
    10612.    129// assert: `scriptCovs.length > 0`
    +
    10613.    129
    +
    10614.    129// function mergeScriptList(scriptCovs) {
    +
    10615.    129// Merges a list of matching script coverages.
    +
    10616.    129// Scripts are matching if they have the same `url`.
    +
    10617.    129// The result is normalized.
    +
    10618.    129// The input values may be mutated, it is not safe to use them after passing
    +
    10619.    129// them to this function.
    +
    10620.    129// The computation is synchronous.
    +
    10621.    129// @param scriptCovs Process coverages to merge.
    +
    10622.    129// @return Merged script coverage, or `undefined` if the input list was empty.
    +
    10623.    129
    +
    10624.    129        let functions = [];
    +
    10625.    129
    +
    10626.    129// Map funcCovRoot.startOffset:funcCovRoot.endOffset to funcCov.
    +
    10627.    129
    +
    10628.    129        let rangeToFuncDict = new Map();
    +
    10629.    129
    +
    10630.    129// Probably deadcode.
    +
    10631.    129// if (scriptCovs.length === 0) {
    +
    10632.    129//     return undefined;
    +
    10633.    129// }
    +
    10634.    129
    +
    10635.     96        if (scriptCovs.length === 1) {
    +
    10636.     96            resultMerged.push(sortScript(scriptCovs[0]));
    +
    10637.     96            return;
    +
    10638.     96        }
    +
    10639.     96
    +
    10640.     96// Init rangeToFuncDict.
    +
    10641.     96// Map funcCovRoot.startOffset:funcCovRoot.endOffset to funcCov.
    +
    10642.     96
    +
    10643.    226        scriptCovs.forEach(function ({
    +
    10644.    226            functions
    +
    10645.    226        }) {
    +
    10646.    277            functions.forEach(function (funcCov) {
    +
    10647.    277                dictKeyValueAppend(
    +
    10648.    277                    rangeToFuncDict,
    +
    10649.    277
    +
    10650.    277// This string can be used to match function with same root range.
    +
    10651.    277// The string is derived from the start and end offsets of the root range of
    +
    10652.    277// the function.
    +
    10653.    277// This assumes that `ranges` is non-empty (true for valid function coverages).
    +
    10654.    277
    +
    10655.    277                    (
    +
    10656.    277                        funcCov.ranges[0].startOffset
    +
    10657.    277                        + ";" + funcCov.ranges[0].endOffset
    +
    10658.    277                    ),
    +
    10659.    277                    funcCov
    +
    10660.    277                );
    +
    10661.    277            });
    +
    10662.    226        });
    +
    10663.    112        rangeToFuncDict.forEach(function (funcCovs) {
    +
    10664.    112
    +
    10665.    112// assert: `funcCovs.length > 0`
    +
    10666.    112
    +
    10667.    112// function mergeFuncList(funcCovs) {
    +
    10668.    112// Merges a list of matching function coverages.
    +
    10669.    112// Functions are matching if their root ranges have the same span.
    +
    10670.    112// The result is normalized.
    +
    10671.    112// The input values may be mutated, it is not safe to use them after passing
    +
    10672.    112// them to this function.
    +
    10673.    112// The computation is synchronous.
    +
    10674.    112// @param funcCovs Function coverages to merge.
    +
    10675.    112// @return Merged function coverage, or `undefined` if the input list was empty.
    +
    10676.    112
    +
    10677.    112            let count = 0;
    +
    10678.    112            let isBlockCoverage;
    +
    10679.    112            let merged;
    +
    10680.    112            let ranges;
    +
    10681.    112            let trees = [];
    +
    10682.    112
    +
    10683.    112// Probably deadcode.
    +
    10684.    112// if (funcCovs.length === 0) {
    +
    10685.    112//     return undefined;
    +
    10686.    112// }
    +
    10687.    112
    +
    10688.     96            if (funcCovs.length === 1) {
    +
    10689.     96                functions.push(sortFunc(funcCovs[0]));
    +
    10690.     96                return;
    +
    10691.    111            }
    +
    10692.    111
    +
    10693.    111// assert: `funcCovs[0].ranges.length > 0`
    +
    10694.    111
    +
    10695.    276            funcCovs.forEach(function (funcCov) {
    +
    10696.    276
    +
    10697.    276// assert: `funcCov.ranges.length > 0`
    +
    10698.    276// assert: `funcCov.ranges` is sorted
    +
    10699.    276
    +
    10700.    276                count += (
    +
    10701.    276                    funcCov.count !== undefined
    +
    10702.    111                    ? funcCov.count
    +
    10703.    274                    : funcCov.ranges[0].count
    +
    10704.    276                );
    +
    10705.    199                if (funcCov.isBlockCoverage) {
    +
    10706.    199                    trees.push(treeFromSortedRanges(funcCov.ranges));
    +
    10707.    199                }
    +
    10708.    276            });
    +
    10709.    111            if (trees.length > 0) {
    +
    10710.     96                isBlockCoverage = true;
    +
    10711.     96                ranges = treeToRanges(mergeTreeList(trees));
    +
    10712.     96            } else {
    +
    10713.     96                isBlockCoverage = false;
    +
    10714.     96                ranges = [
    +
    10715.     96                    {
    +
    10716.     96                        count,
    +
    10717.     96                        endOffset: funcCovs[0].ranges[0].endOffset,
    +
    10718.     96                        startOffset: funcCovs[0].ranges[0].startOffset
    +
    10719.     96                    }
    +
    10720.     96                ];
    +
    10721.    111            }
    +
    10722.    111            merged = {
    +
    10723.    111                functionName: funcCovs[0].functionName,
    +
    10724.    111                isBlockCoverage,
    +
    10725.    111                ranges
    +
    10726.    111            };
    +
    10727.    111            if (count !== ranges[0].count) {
    +
    10728.     96                merged.count = count;
    +
    10729.    111            }
    +
    10730.    111
    +
    10731.    111// assert: `merged` is normalized
    +
    10732.    111
    +
    10733.    111            functions.push(merged);
    +
    10734.    111        });
    +
    10735.     96        resultMerged.push(sortScript({
    +
    10736.     96            functions,
    +
    10737.     96            scriptId: scriptCovs[0].scriptId,
    +
    10738.     96            url: scriptCovs[0].url
    +
    10739.     96        }));
    +
    10740.     96    });
    +
    10741.     96
    +
    10742.     96// Sorts the scripts alphabetically by `url`.
    +
    10743.     96// Reassigns script ids: the script at index `0` receives `"0"`, the script at
    +
    10744.     96// index `1` receives `"1"` etc.
    +
    10745.     96
    +
    10746.     96    Object.entries(resultMerged.sort(function (aa, bb) {
    +
    10747.     96        return (
    +
    10748.     96            aa.url > bb.url
    +
    10749.     96            ? 1
    +
    10750.     96            : -1
    +
    10751.     96        );
    +
    10752.    129    })).forEach(function ([
    +
    10753.    129        scriptId, scriptCov
    +
    10754.    129    ]) {
    +
    10755.    129        scriptCov.scriptId = scriptId.toString(10);
    +
    10756.    129    });
    +
    10757.     96    return {
    +
    10758.     96        result: resultMerged
    +
    10759.     96    };
    +
    10760.     96}
    +
    10761.      1
    +
    10762.      8async function v8CoverageReportCreate({
    +
    10763.      8    consoleError,
    +
    10764.      8    coverageDir,
    +
    10765.      8    processArgv = []
    +
    10766.      8}) {
    +
    10767.      8
    +
    10768.      8// This function will create html-coverage-reports directly from
    +
    10769.      8// v8-coverage-files in <coverageDir>.
    +
    10770.      8// 1. Spawn node.js program <processArgv> with NODE_V8_COVERAGE.
    +
    10771.      8// 2. Merge JSON v8-coverage-files in <coverageDir>.
    +
    10772.      8// 3. Create html-coverage-reports in <coverageDir>.
    +
    10773.      8
    +
    10774.      8    let cwd;
    +
    10775.      8    let excludeList = [];
    +
    10776.      8    let exitCode = 0;
    +
    10777.      8    let fileDict;
    +
    10778.      8    let includeList = [];
    +
    10779.      8    let modeIncludeNodeModules;
    +
    10780.      8    let processArgElem;
    +
    10781.      8    let promiseList = [];
    +
    10782.      8    let v8CoverageObj;
    +
    10783.      8
    +
    10784.     13    function htmlRender({
    +
    10785.     13        fileList,
    +
    10786.     13        lineList,
    +
    10787.     13        modeIndex,
    +
    10788.     13        pathname
    +
    10789.     13    }) {
    +
    10790.     13        let html;
    +
    10791.     13        let padLines;
    +
    10792.     13        let padPathname;
    +
    10793.     13        let txt;
    +
    10794.     13        let txtBorder;
    +
    10795.     13        html = "";
    +
    10796.     13        html += String(`
    +
    10797.     13<!DOCTYPE html>
    +
    10798.     13<html lang="en">
    +
    10799.     13<head>
    +
    10800.     13<title>V8 Coverage Report</title>
    +
    10801.     13<style>
    +
    10802.     13/* jslint utility2:true */
    +
    10803.     13/*csslint ignore:start*/
    +
    10804.     13.coverage,
    +
    10805.     13.coverage a,
    +
    10806.     13.coverage div,
    +
    10807.     13.coverage pre,
    +
    10808.     13.coverage span,
    +
    10809.     13.coverage table,
    +
    10810.     13.coverage tbody,
    +
    10811.     13.coverage td,
    +
    10812.     13.coverage th,
    +
    10813.     13.coverage thead,
    +
    10814.     13.coverage tr {
    +
    10815.     13    box-sizing: border-box;
    +
    10816.     13    font-family: monospace;
    +
    10817.     13}
    +
    10818.     13/*csslint ignore:end*/
    +
    10819.     13
    +
    10820.     13/* css - coverage_report - general */
    +
    10821.     13body {
    +
    10822.     13    margin: 0;
    +
    10823.     13}
    +
    10824.     13.coverage pre {
    +
    10825.     13    margin: 5px 0;
    +
    10826.     13}
    +
    10827.     13.coverage table {
    +
    10828.     13    border-collapse: collapse;
    +
    10829.     13}
    +
    10830.     13.coverage td,
    +
    10831.     13.coverage th {
    +
    10832.     13    border: 1px solid #777;
    +
    10833.     13    line-height: 20px;
    +
    10834.     13    margin: 0;
    +
    10835.     13    padding: 5px 10px;
    +
    10836.     13}
    +
    10837.     13.coverage td span {
    +
    10838.     13    display: inline-block;
    +
    10839.     13    width: 100%;
    +
    10840.     13}
    +
    10841.     13.coverage .content {
    +
    10842.     13    padding: 0 5px;
    +
    10843.     13}
    +
    10844.     13.coverage .content a {
    +
    10845.     13    text-decoration: none;
    +
    10846.     13}
    +
    10847.     13.coverage .count {
    +
    10848.     13    margin: 0 5px;
    +
    10849.     13    padding: 0 5px;
    +
    10850.     13}
    +
    10851.     13.coverage .footer,
    +
    10852.     13.coverage .header {
    +
    10853.     13    padding: 20px;
    +
    10854.     13}
    +
    10855.     13.coverage .footer {
    +
    10856.     13    text-align: center;
    +
    10857.     13}
    +
    10858.     13.coverage .percentbar {
    +
    10859.     13    height: 12px;
    +
    10860.     13    margin: 2px 0;
    +
    10861.     13    min-width: 200px;
    +
    10862.     13    position: relative;
    +
    10863.     13    width: 100%;
    +
    10864.     13}
    +
    10865.     13.coverage .percentbar div {
    +
    10866.     13    height: 100%;
    +
    10867.     13    position: absolute;
    +
    10868.     13}
    +
    10869.     13.coverage .title {
    +
    10870.     13    font-size: large;
    +
    10871.     13    font-weight: bold;
    +
    10872.     13    margin-bottom: 10px;
    +
    10873.     13}
    +
    10874.     13
    +
    10875.     13/* css - coverage_report - color */
    +
    10876.     13.coverage td,
    +
    10877.     13.coverage th {
    +
    10878.     13    background: #fff;
    +
    10879.     13}
    +
    10880.     13.coverage .count,
    +
    10881.     13.coverage .coverageHigh {
    +
    10882.     13    background: #9d9;
    +
    10883.     13}
    +
    10884.     13.coverage .count {
    +
    10885.     13    color: #666;
    +
    10886.     13}
    +
    10887.     13.coverage .coverageIgnore {
    +
    10888.     13    background: #ccc;
    +
    10889.     13}
    +
    10890.     13.coverage .coverageLow,
    +
    10891.     13.coverage .uncovered {
    +
    10892.     13    background: #ebb;
    +
    10893.     13}
    +
    10894.     13.coverage .coverageMedium {
    +
    10895.     13    background: #fd7;
    +
    10896.     13}
    +
    10897.     13.coverage .footer,
    +
    10898.     13.coverage .header,
    +
    10899.     13.coverage .lineno {
    +
    10900.     13    background: #ddd;
    +
    10901.     13}
    +
    10902.     13.coverage .percentbar {
    +
    10903.     13    background: #999;
    +
    10904.     13}
    +
    10905.     13.coverage .percentbar div {
    +
    10906.     13    background: #666;
    +
    10907.     13}
    +
    10908.     13
    +
    10909.     13/* css - coverage_report - important */
    +
    10910.     13.coverage pre:hover span,
    +
    10911.     13.coverage tr:hover td {
    +
    10912.     13    background: #7d7;
    +
    10913.     13}
    +
    10914.     13.coverage pre:hover span.uncovered,
    +
    10915.     13.coverage tr:hover td.coverageLow {
    +
    10916.     13    background: #f99;
    +
    10917.     13}
    +
    10918.     13</style>
    +
    10919.     13</head>
    +
    10920.     13<body class="coverage">
    +
    10921.     13<!-- header start -->
    +
    10922.     13<div class="header">
    +
    10923.     13<div class="title">V8 Coverage Report</div>
    +
    10924.     13<table>
    +
    10925.     13<thead>
    +
    10926.     13    <tr>
    +
    10927.     13    <th>Files covered</th>
    +
    10928.     13    <th>Lines</th>
    +
    10929.     13    <th>Remaining</th>
    +
    10930.     13    </tr>
    +
    10931.     13</thead>
    +
    10932.     13<tbody>
    +
    10933.     13        `).trim() + "\n";
    +
    10934.      7        if (modeIndex) {
    +
    10935.      7            padLines = String("(ignore) 100.00 %").length;
    +
    10936.      7            padPathname = 32;
    +
    10937.      7            fileList.unshift({
    +
    10938.      7                linesCovered: 0,
    +
    10939.      7                linesTotal: 0,
    +
    10940.      7                modeCoverageIgnoreFile: "",
    +
    10941.      7                pathname: "./"
    +
    10942.      7            });
    +
    10943.      7            fileList.slice(1).forEach(function ({
    +
    10944.      7                linesCovered,
    +
    10945.      7                linesTotal,
    +
    10946.      7                modeCoverageIgnoreFile,
    +
    10947.      7                pathname
    +
    10948.      7            }) {
    +
    10949.      7                if (!modeCoverageIgnoreFile) {
    +
    10950.      7                    fileList[0].linesCovered += linesCovered;
    +
    10951.      7                    fileList[0].linesTotal += linesTotal;
    +
    10952.      7                }
    +
    10953.      7                padPathname = Math.max(padPathname, pathname.length + 2);
    +
    10954.      7                padLines = Math.max(
    +
    10955.      7                    padLines,
    +
    10956.      7                    String(linesCovered + " / " + linesTotal).length
    +
    10957.      7                );
    +
    10958.      7            });
    +
    10959.      7        }
    +
    10960.     13        txtBorder = (
    +
    10961.     13            "+" + "-".repeat(padPathname + 2) + "+"
    +
    10962.     13            + "-".repeat(padLines + 2) + "+"
    +
    10963.     13            + "-".repeat(padLines + 2) + "+\n"
    +
    10964.     13        );
    +
    10965.     13        txt = "";
    +
    10966.     13        txt += "V8 Coverage Report\n";
    +
    10967.     13        txt += txtBorder;
    +
    10968.     13        txt += (
    +
    10969.     13            "| " + String("Files covered").padEnd(padPathname, " ") + " | "
    +
    10970.     13            + String("Lines").padStart(padLines, " ") + " | "
    +
    10971.     13            + String("Remaining").padStart(padLines, " ") + " |\n"
    +
    10972.     13        );
    +
    10973.     13        txt += txtBorder;
    +
    10974.     19        fileList.forEach(function ({
    +
    10975.     19            linesCovered,
    +
    10976.     19            linesTotal,
    +
    10977.     19            modeCoverageIgnoreFile,
    +
    10978.     19            pathname
    +
    10979.     19        }, ii) {
    +
    10980.     19            let coverageLevel;
    +
    10981.     19            let coveragePct;
    +
    10982.     19            let fill;
    +
    10983.     19            let str1;
    +
    10984.     19            let str2;
    +
    10985.     19            let xx1;
    +
    10986.     19            let xx2;
    +
    10987.      2            coveragePct = Math.floor(10000 * linesCovered / linesTotal || 0);
    +
    10988.     19            coverageLevel = (
    +
    10989.     19                modeCoverageIgnoreFile
    +
    10990.      2                ? "coverageIgnore"
    +
    10991.     17                : coveragePct >= 8000
    +
    10992.     17                ? "coverageHigh"
    +
    10993.     17                : coveragePct >= 5000
    +
    10994.     17                ? "coverageMedium"
    +
    10995.     17                : "coverageLow"
    +
    10996.     19            );
    +
    10997.     19            coveragePct = String(coveragePct).replace((
    +
    10998.     19                /..$/m
    +
    10999.     19            ), ".$&");
    +
    11000.     13            if (modeIndex && ii === 0) {
    +
    11001.      7                fill = (
    +
    11002.      7
    +
    11003.      7// Badge-color rgb-red.
    +
    11004.      7
    +
    11005.      7                    "#" + Math.round(
    +
    11006.      7                        (100 - Number(coveragePct)) * 2.21
    +
    11007.      7                    ).toString(16).padStart(2, "0")
    +
    11008.      7
    +
    11009.      7// Badge-color rgb-green.
    +
    11010.      7
    +
    11011.      7                    + Math.round(
    +
    11012.      7                        Number(coveragePct) * 2.21
    +
    11013.      7                    ).toString(16).padStart(2, "0")
    +
    11014.      7
    +
    11015.      7// Badge-color rgb-blue.
    +
    11016.      7
    +
    11017.      7                    + "00"
    +
    11018.      7                );
    +
    11019.      7                str1 = "coverage";
    +
    11020.      7                str2 = coveragePct + " %";
    +
    11021.      7                xx1 = 6 * str1.length + 20;
    +
    11022.      7                xx2 = 6 * str2.length + 20;
    +
    11023.      7
    +
    11024.      7// Fs - write coverage_badge.svg.
    +
    11025.      7
    +
    11026.      7                promiseList.push(fsWriteFileWithParents((
    +
    11027.      7                    coverageDir + "coverage_badge.svg"
    +
    11028.      7                ), String(`
    +
    11029.      7<svg height="20" width="${xx1 + xx2}" xmlns="http://www.w3.org/2000/svg">
    +
    11030.      7<rect fill="#555" height="20" width="${xx1 + xx2}"/>
    +
    11031.      7<rect fill="${fill}" height="20" width="${xx2}" x="${xx1}"/>
    +
    11032.      7<g
    +
    11033.      7    fill="#fff"
    +
    11034.      7    font-family="verdana, geneva, dejavu sans, sans-serif"
    +
    11035.      7    font-size="11"
    +
    11036.      7    font-weight="bold"
    +
    11037.      7    text-anchor="middle"
    +
    11038.      7>
    +
    11039.      7<text x="${0.5 * xx1}" y="14">${str1}</text>
    +
    11040.      7<text x="${xx1 + 0.5 * xx2}" y="14">${str2}</text>
    +
    11041.      7</g>
    +
    11042.      7</svg>
    +
    11043.      7                `).trim() + "\n"));
    +
    11044.      7                pathname = "";
    +
    11045.      7            }
    +
    11046.     19            txt += (
    +
    11047.     19                "| "
    +
    11048.     19                + String("./" + pathname).padEnd(padPathname, " ") + " | "
    +
    11049.     19                + String(
    +
    11050.     19                    modeCoverageIgnoreFile + " " + coveragePct + " %"
    +
    11051.     19                ).padStart(padLines, " ") + " | "
    +
    11052.     19                + " ".repeat(padLines) + " |\n"
    +
    11053.     19            );
    +
    11054.     19            txt += (
    +
    11055.     19                "| " + "*".repeat(
    +
    11056.     19                    Math.round(0.01 * coveragePct * padPathname)
    +
    11057.     19                ).padEnd(padPathname, "_") + " | "
    +
    11058.     19                + String(
    +
    11059.     19                    linesCovered + " / " + linesTotal
    +
    11060.     19                ).padStart(padLines, " ") + " | "
    +
    11061.     19                + String(
    +
    11062.     19                    (linesTotal - linesCovered) + " / " + linesTotal
    +
    11063.     19                ).padStart(padLines, " ") + " |\n"
    +
    11064.     19            );
    +
    11065.     19            txt += txtBorder;
    +
    11066.     19            pathname = htmlEscape(pathname);
    +
    11067.     19
    +
    11068.     19// CL-37251d17 - Bugfix - Fix incorrect http-link to index.html.
    +
    11069.     19
    +
    11070.     19            html += String(`
    +
    11071.     19    <tr>
    +
    11072.     19    <td class="${coverageLevel}">
    +
    11073.     19            ${(
    +
    11074.     19                modeIndex
    +
    11075.     13                ? (
    +
    11076.     13                    "<a href=\"" + (pathname || "index") + ".html\">. / "
    +
    11077.     13                    + pathname + "</a><br>"
    +
    11078.     13                )
    +
    11079.      6                : (
    +
    11080.      6                    "<a href=\""
    +
    11081.      6                    + "../".repeat(pathname.split("/").length - 1)
    +
    11082.      6                    + "index.html\">. / </a>"
    +
    11083.      6                    + pathname + "<br>"
    +
    11084.      6                )
    +
    11085.     19            )}
    +
    11086.     19        <div class="percentbar">
    +
    11087.     19            <div style="width: ${coveragePct}%;"></div>
    +
    11088.     19        </div>
    +
    11089.     19    </td>
    +
    11090.     19    <td style="text-align: right;">
    +
    11091.     19        ${modeCoverageIgnoreFile} ${coveragePct} %<br>
    +
    11092.     19        ${linesCovered} / ${linesTotal}
    +
    11093.     19    </td>
    +
    11094.     19    <td style="text-align: right;">
    +
    11095.     19        <br>
    +
    11096.     19        ${linesTotal - linesCovered} / ${linesTotal}
    +
    11097.     19    </td>
    +
    11098.     19    </tr>
    +
    11099.     19        `).trim() + "\n";
    +
    11100.     19        });
    +
    11101.     13        html += String(`
    +
    11102.     13</tbody>
    +
    11103.     13</table>
    +
    11104.     13</div>
    +
    11105.     13<!-- header end -->
    +
    11106.     13        `).trim() + "\n";
    +
    11107.      6        if (!modeIndex) {
    +
    11108.      6            html += String(`
    +
    11109.      6<!-- content start -->
    +
    11110.      6<div class="content">
    +
    11111.      6            `).trim() + "\n";
    +
    11112.  11853            lineList.forEach(function ({
    +
    11113.  11853                count,
    +
    11114.  11853                holeList,
    +
    11115.  11853                line,
    +
    11116.  11853                startOffset
    +
    11117.  11853            }, ii) {
    +
    11118.  11853                let chunk;
    +
    11119.  11853                let inHole;
    +
    11120.  11853                let lineHtml;
    +
    11121.  11853                let lineId;
    +
    11122.  11853                lineHtml = "";
    +
    11123.  11853                lineId = "line_" + (ii + 1);
    +
    11124.  11853                switch (count) {
    +
    11125.     32                case -1:
    +
    11126.  11219                case 0:
    +
    11127.  11219                    if (holeList.length === 0) {
    +
    11128.  11219                        lineHtml += "</span>";
    +
    11129.  11219                        lineHtml += "<span class=\"uncovered\">";
    +
    11130.  11219                        lineHtml += htmlEscape(line);
    +
    11131.  11219                        break;
    +
    11132.  11219                    }
    +
    11133.  11219                    line = line.split("").map(function (char) {
    +
    11134.  11219                        return {
    +
    11135.  11219                            char,
    +
    11136.  11219                            isHole: undefined
    +
    11137.  11219                        };
    +
    11138.  11219                    });
    +
    11139.  11219                    holeList.forEach(function ([
    +
    11140.  11219                        aa, bb
    +
    11141.  11219                    ]) {
    +
    11142.  11219                        aa = Math.max(aa - startOffset, 0);
    +
    11143.  11219                        bb = Math.min(bb - startOffset, line.length);
    +
    11144.  11219                        while (aa < bb) {
    +
    11145.  11219                            line[aa].isHole = true;
    +
    11146.  11219                            aa += 1;
    +
    11147.  11219                        }
    +
    11148.  11219                    });
    +
    11149.  11219                    chunk = "";
    +
    11150.  11219                    line.forEach(function ({
    +
    11151.  11219                        char,
    +
    11152.  11219                        isHole
    +
    11153.  11219                    }) {
    +
    11154.  11219                        if (inHole !== isHole) {
    +
    11155.  11219                            lineHtml += htmlEscape(chunk);
    +
    11156.  11219                            lineHtml += "</span><span";
    +
    11157.  11219
    +
    11158.  11219// Coverage-hack - Ugly-hack around possible deadcode where isHole is always
    +
    11159.  11219// true.
    +
    11160.  11219
    +
    11161.  11219                            if (isHole) {
    +
    11162.  11219                                lineHtml += " class=\"uncovered\"";
    +
    11163.  11219                            }
    +
    11164.  11219                            lineHtml += ">";
    +
    11165.  11219                            chunk = "";
    +
    11166.  11219                            inHole = isHole;
    +
    11167.  11219                        }
    +
    11168.  11219                        chunk += char;
    +
    11169.  11219                    });
    +
    11170.  11219                    lineHtml += htmlEscape(chunk);
    +
    11171.  11219                    break;
    +
    11172.    634                default:
    +
    11173.    634                    lineHtml += htmlEscape(line);
    +
    11174.  11853                }
    +
    11175.  11853                html += String(`
    +
    11176.  11853<pre>
    +
    11177.  11853<span class="lineno">
    +
    11178.  11853<a href="#${lineId}" id="${lineId}">${String(ii + 1).padStart(5, " ")}.</a>
    +
    11179.  11853</span>
    +
    11180.  11853<span class="count
    +
    11181.  11853                ${(
    +
    11182.  11853                    count <= 0
    +
    11183.  11219                    ? "uncovered"
    +
    11184.    634                    : ""
    +
    11185.  11853                )}"
    +
    11186.  11853>
    +
    11187.  11187${String(count || "-0").padStart(7, " ")}
    +
    11188.  11853</span>
    +
    11189.  11853<span>${lineHtml}</span>
    +
    11190.  11853</pre>
    +
    11191.  11853                `).replace((
    +
    11192.  11853                    /\n/g
    +
    11193.  11853                ), "").trim() + "\n";
    +
    11194.  11853            });
    +
    11195.      6            html += String(`
    +
    11196.      6</div>
    +
    11197.      6<!-- content end -->
    +
    11198.      6            `).trim() + "\n";
    +
    11199.      6        }
    +
    11200.     13        html += String(`
    +
    11201.     13<div class="footer">
    +
    11202.     13    [
    +
    11203.     13    This document was created with
    +
    11204.     13    <a href="https://github.com/jslint-org/jslint">JSLint</a>
    +
    11205.     13    ]
    +
    11206.     13</div>
    +
    11207.     13</body>
    +
    11208.     13</html>
    +
    11209.     13        `).trim() + "\n";
    +
    11210.     13
    +
    11211.     13// Fs - write <file>.html.
    +
    11212.     13
    +
    11213.     13        promiseList.push(fsWriteFileWithParents(pathname + ".html", html));
    +
    11214.      6        if (!modeIndex) {
    +
    11215.      6            return;
    +
    11216.      7        }
    +
    11217.      7
    +
    11218.      7// Fs - write coverage_report.txt.
    +
    11219.      7
    +
    11220.      7        consoleError("\n" + txt);
    +
    11221.      7        promiseList.push(fsWriteFileWithParents((
    +
    11222.      7            coverageDir + "coverage_report.txt"
    +
    11223.      7        ), txt));
    +
    11224.      7    }
    +
    11225.      8
    +
    11226.      8/*
    +
    11227.      8function sentinel() {}
    +
    11228.      8*/
    +
    11229.      8
    +
    11230.      8    await moduleFsInit();
    +
    11231.      1    consoleError = consoleError || console.error;
    +
    11232.      8    cwd = process.cwd().replace((
    +
    11233.      8        /\\/g
    +
    11234.      8    ), "/") + "/";
    +
    11235.      8
    +
    11236.      8// Init coverageDir.
    +
    11237.      8// Assert coverageDir is subdirectory of cwd.
    +
    11238.      8
    +
    11239.      8    assertOrThrow(coverageDir, "invalid coverageDir " + coverageDir);
    +
    11240.      8
    +
    11241.      8// CL-61b11012 - coverage - Relax requirement for coverageDir to be in cwd.
    +
    11242.      8//     assertOrThrow(
    +
    11243.      8//         pathnameRelativeCwd(coverageDir),
    +
    11244.      8//         "coverageDir " + coverageDir + " is not subdirectory of cwd " + cwd
    +
    11245.      8//     );
    +
    11246.      8
    +
    11247.      8    coverageDir = modulePath.resolve(coverageDir).replace((
    +
    11248.      8        /\\/g
    +
    11249.      8    ), "/") + "/";
    +
    11250.      8
    +
    11251.      8    processArgv = processArgv.slice();
    +
    11252.      9    while (processArgv[0] && processArgv[0][0] === "-") {
    +
    11253.      3        processArgElem = processArgv.shift().split("=");
    +
    11254.      3        processArgElem[1] = processArgElem.slice(1).join("=");
    +
    11255.      3        switch (processArgElem[0]) {
    +
    11256.      3
    +
    11257.      3// PR-371 - Add cli-option `--exclude=...`.
    +
    11258.      3
    +
    11259.      3        case "--exclude":
    +
    11260.      3            excludeList.push(processArgElem[1]);
    +
    11261.      3            break;
    +
    11262.      3
    +
    11263.      3// PR-371 - Add cli-option `--include=...`
    +
    11264.      3
    +
    11265.      3        case "--include":
    +
    11266.      3            includeList.push(processArgElem[1]);
    +
    11267.      3            break;
    +
    11268.      3
    +
    11269.      3// PR-400
    +
    11270.      3// Disable default-coverage of directory `node_modules`,
    +
    11271.      3// but allow override with cli-option `--include-node-modules=1`.
    +
    11272.      3
    +
    11273.      3        case "--include-node-modules":
    +
    11274.      3            modeIncludeNodeModules = !(
    +
    11275.      3                /0|false|null|undefined/
    +
    11276.      3            ).test(processArgElem[1]);
    +
    11277.      3            break;
    +
    11278.      3        }
    +
    11279.      7    }
    +
    11280.      7
    +
    11281.      7// 1. Spawn node.js program <processArgv> with coverage
    +
    11282.      7
    +
    11283.      7    if (processArgv.length > 0) {
    +
    11284.      6
    +
    11285.      6// Remove old coverage-files.
    +
    11286.      6
    +
    11287.      6        await fsWriteFileWithParents(coverageDir + "/touch.txt", "");
    +
    11288.      6        await Promise.all(Array.from(
    +
    11289.      6            await moduleFs.promises.readdir(coverageDir)
    +
    11290.     11        ).map(async function (file) {
    +
    11291.     11            if ((
    +
    11292.     11                /^coverage-\d+?-\d+?-\d+?\.json$/
    +
    11293.      6            ).test(file)) {
    +
    11294.      6                consoleError("rm file " + coverageDir + file);
    +
    11295.      6                await moduleFs.promises.unlink(coverageDir + file);
    +
    11296.      6            }
    +
    11297.     11        }));
    +
    11298.      6        exitCode = await new Promise(function (resolve) {
    +
    11299.      6            let processArgv0 = processArgv[0];
    +
    11300.      6
    +
    11301.      6// If win32 environment, then replace program npm with npm.cmd.
    +
    11302.      6// Coverage-hack - Ugly-hack to get test-coverage under both win32 and linux.
    +
    11303.      6
    +
    11304.      6            if (processArgv0 === "npm") {
    +
    11305.      6                processArgv0 = process.platform.replace(
    +
    11306.      6                    "win32",
    +
    11307.      6                    "npm.cmd"
    +
    11308.      6                ).replace(
    +
    11309.      6                    process.platform,
    +
    11310.      6                    "npm"
    +
    11311.      6                );
    +
    11312.      6            }
    +
    11313.      6            moduleChildProcess.spawn(
    +
    11314.      6                processArgv0,
    +
    11315.      6                processArgv.slice(1),
    +
    11316.      6                {
    +
    11317.      6                    env: Object.assign({}, process.env, {
    +
    11318.      6                        NODE_V8_COVERAGE: coverageDir
    +
    11319.      6                    }),
    +
    11320.      6
    +
    11321.      6// PR-465
    +
    11322.      6// https://nodejs.org/en/blog/vulnerability/april-2024-security-releases-2
    +
    11323.      6// Node.js will now error with EINVAL if a .bat or .cmd file is passed to
    +
    11324.      6// child_process.spawn and child_process.spawnSync without the shell option set.
    +
    11325.      6
    +
    11326.      6                    shell: (
    +
    11327.      6                        processArgv0.endsWith(".bat")
    +
    11328.      6                        || processArgv0.endsWith(".cmd")
    +
    11329.      6                    ),
    +
    11330.      6                    stdio: ["ignore", 1, 2]
    +
    11331.      6                }
    +
    11332.      6            ).on("exit", resolve);
    +
    11333.      6        });
    +
    11334.      6        consoleError(
    +
    11335.      6            `v8CoverageReportCreate - program exited with exitCode=${exitCode}`
    +
    11336.      6        );
    +
    11337.      7    }
    +
    11338.      7
    +
    11339.      7// 2. Merge JSON v8-coverage-files in <coverageDir>.
    +
    11340.      7
    +
    11341.      7    consoleError("v8CoverageReportCreate - merging coverage files...");
    +
    11342.      7    v8CoverageObj = await moduleFs.promises.readdir(coverageDir);
    +
    11343.     18    v8CoverageObj = v8CoverageObj.filter(function (file) {
    +
    11344.     18        return (
    +
    11345.     18            /^coverage-\d+?-\d+?-\d+?\.json$/
    +
    11346.     18        ).test(file);
    +
    11347.     18    });
    +
    11348.      7    v8CoverageObj = await Promise.all(v8CoverageObj.map(async function (file) {
    +
    11349.      7        let data;
    +
    11350.      7        let pathnameDict = Object.create(null);
    +
    11351.      7        data = await moduleFs.promises.readFile(coverageDir + file, "utf8");
    +
    11352.      7        data = JSON.parse(data);
    +
    11353.    473        data.result.forEach(function (scriptCov) {
    +
    11354.    473            let pathname = scriptCov.url;
    +
    11355.    473
    +
    11356.    473// Filter out internal coverages.
    +
    11357.    473
    +
    11358.    393            if (!pathname.startsWith("file:///")) {
    +
    11359.    393                return;
    +
    11360.    393            }
    +
    11361.     80
    +
    11362.     80// Normalize pathname.
    +
    11363.     80
    +
    11364.     80            pathname = moduleUrl.fileURLToPath(pathname);
    +
    11365.     80            pathname = modulePath.resolve(pathname).replace((
    +
    11366.     80                /\\/g
    +
    11367.     80            ), "/");
    +
    11368.     80
    +
    11369.     80// Filter files outside of cwd.
    +
    11370.     80
    +
    11371.     80            if (pathname.indexOf("[") >= 0 || !pathname.startsWith(cwd)) {
    +
    11372.     74                return;
    +
    11373.     74            }
    +
    11374.      7
    +
    11375.      7// Normalize pathname relative to cwd.
    +
    11376.      7
    +
    11377.      7            pathname = pathname.slice(cwd.length);
    +
    11378.      7            scriptCov.url = pathname;
    +
    11379.      7            pathnameDict[pathname] = scriptCov;
    +
    11380.      7        });
    +
    11381.      7
    +
    11382.      7// PR-400 - Filter directory `node_modules`.
    +
    11383.      7
    +
    11384.      7        if (!modeIncludeNodeModules) {
    +
    11385.      7            excludeList.push("node_modules/");
    +
    11386.      7        }
    +
    11387.      7
    +
    11388.      7// PR-400 - Filter files by glob-patterns in excludeList, includeList.
    +
    11389.      7
    +
    11390.      7        data.result = globExclude({
    +
    11391.      7            excludeList,
    +
    11392.      7            includeList,
    +
    11393.      7            pathnameList: Object.keys(pathnameDict)
    +
    11394.      7        }).pathnameList.map(function (pathname) {
    +
    11395.      7            return pathnameDict[pathname];
    +
    11396.      7        });
    +
    11397.      7        return data;
    +
    11398.      7    }));
    +
    11399.      7
    +
    11400.      7// Merge v8CoverageObj.
    +
    11401.      7
    +
    11402.      7    v8CoverageObj = v8CoverageListMerge(v8CoverageObj);
    +
    11403.      7
    +
    11404.      7// Debug v8CoverageObj.
    +
    11405.      7
    +
    11406.      7    await fsWriteFileWithParents(
    +
    11407.      7        coverageDir + "v8_coverage_merged.json",
    +
    11408.      7        JSON.stringify(v8CoverageObj, undefined, 1)
    +
    11409.      7    );
    +
    11410.      7
    +
    11411.      7// 3. Create html-coverage-reports in <coverageDir>.
    +
    11412.      7
    +
    11413.      7    consoleError("v8CoverageReportCreate - creating html-coverage-report...");
    +
    11414.      7    fileDict = Object.create(null);
    +
    11415.      7    await Promise.all(v8CoverageObj.result.map(async function ({
    +
    11416.      7        functions,
    +
    11417.      7        url: pathname
    +
    11418.      7    }) {
    +
    11419.      7        let lineList;
    +
    11420.      7        let linesCovered;
    +
    11421.      7        let linesTotal;
    +
    11422.      7        let source;
    +
    11423.      7        source = await moduleFs.promises.readFile(pathname, "utf8");
    +
    11424.      7        lineList = [{}];
    +
    11425.      7        source.replace((
    +
    11426.      7            /^.*$/gm
    +
    11427.  11853        ), function (line, startOffset) {
    +
    11428.  11853            lineList[lineList.length - 1].endOffset = startOffset - 1;
    +
    11429.  11853            lineList.push({
    +
    11430.  11853                count: -1,
    +
    11431.  11853                endOffset: 0,
    +
    11432.  11853                holeList: [],
    +
    11433.  11853                line,
    +
    11434.  11853                startOffset
    +
    11435.  11853            });
    +
    11436.  11853            return "";
    +
    11437.  11853        });
    +
    11438.      7        lineList.shift();
    +
    11439.      7        lineList[lineList.length - 1].endOffset = source.length;
    +
    11440.     41        functions.reverse().forEach(function ({
    +
    11441.     41            ranges
    +
    11442.     41        }) {
    +
    11443.     65            ranges.reverse().forEach(function ({
    +
    11444.     65                count,
    +
    11445.     65                endOffset,
    +
    11446.     65                startOffset
    +
    11447.     65            }, ii, list) {
    +
    11448. 580047                lineList.forEach(function (elem) {
    +
    11449. 580047                    if (!(
    +
    11450. 580047                        (
    +
    11451. 580047                            elem.startOffset <= startOffset
    +
    11452. 205728                            && startOffset <= elem.endOffset
    +
    11453. 579982                        ) || (
    +
    11454. 579982                            elem.startOffset <= endOffset
    +
    11455. 579982                            && endOffset <= elem.endOffset
    +
    11456. 579982                        ) || (
    +
    11457. 579926                            startOffset <= elem.startOffset
    +
    11458. 579926                            && elem.endOffset <= endOffset
    +
    11459. 579926                        )
    +
    11460. 556497                    )) {
    +
    11461. 556497                        return;
    +
    11462. 556497                    }
    +
    11463.  23550
    +
    11464.  23550// Handle tree-root.
    +
    11465.  23550
    +
    11466.  23550                    if (ii + 1 === list.length) {
    +
    11467.  23281                        if (elem.count === -1) {
    +
    11468.  23281                            elem.count = count;
    +
    11469.  23281                        }
    +
    11470.  23281                        return;
    +
    11471.  23281                    }
    +
    11472.    269
    +
    11473.    269// Handle tree-children.
    +
    11474.    269
    +
    11475.    269                    if (elem.count !== 0) {
    +
    11476.    170                        elem.count = Math.max(count, elem.count);
    +
    11477.    269                    }
    +
    11478.    269                    if (count === 0) {
    +
    11479.    203                        elem.count = 0;
    +
    11480.    203                        elem.holeList.push([
    +
    11481.    203                            startOffset, endOffset
    +
    11482.    203                        ]);
    +
    11483.    203                    }
    +
    11484. 580047                });
    +
    11485.     65            });
    +
    11486.     41        });
    +
    11487.      7        linesTotal = lineList.length;
    +
    11488.  11853        linesCovered = lineList.filter(function ({
    +
    11489.  11853            count
    +
    11490.  11853        }) {
    +
    11491.  11853            return count > 0;
    +
    11492.  11853        }).length;
    +
    11493.      7        await moduleFs.promises.mkdir((
    +
    11494.      7            modulePath.dirname(coverageDir + pathname)
    +
    11495.      7        ), {
    +
    11496.      7            recursive: true
    +
    11497.      7        });
    +
    11498.      7        fileDict[pathname] = {
    +
    11499.      7            lineList,
    +
    11500.      7            linesCovered,
    +
    11501.      7            linesTotal,
    +
    11502.      7            modeCoverageIgnoreFile: (
    +
    11503.      7                (
    +
    11504.      7                    /^\/\*coverage-ignore-file\*\/$/m
    +
    11505.      7                ).test(source.slice(0, 65536))
    +
    11506.      7                ? "(ignore)"
    +
    11507.      7                : ""
    +
    11508.      7            ),
    +
    11509.      7            pathname
    +
    11510.      7        };
    +
    11511.      7        htmlRender({
    +
    11512.      7            fileList: [
    +
    11513.      7                fileDict[pathname]
    +
    11514.      7            ],
    +
    11515.      7            lineList,
    +
    11516.      7            pathname: coverageDir + pathname
    +
    11517.      7        });
    +
    11518.      7    }));
    +
    11519.      7    htmlRender({
    +
    11520.      7        fileList: Object.keys(fileDict).sort().map(function (pathname) {
    +
    11521.      7            return fileDict[pathname];
    +
    11522.      7        }),
    +
    11523.      7        modeIndex: true,
    +
    11524.      7        pathname: coverageDir + "index"
    +
    11525.      7    });
    +
    11526.      7    await Promise.all(promiseList);
    +
    11527.      7    assertOrThrow(
    +
    11528.      7        exitCode === 0,
    +
    11529.      7        "v8CoverageReportCreate - nonzero exitCode " + exitCode
    +
    11530.      7    );
    +
    11531.      7}
    +
    11532.      1
    +
    11533.      1/*
    +
    11534.      1function sentinel() {}
    +
    11535.      1*/
    +
    11536.      1
    +
    11537.      1// Export jslint as cjs/esm.
    +
    11538.      1
    +
    11539.      1jslint_export = Object.freeze(Object.assign(jslint, {
    +
    11540.      1    assertErrorThrownAsync,
    +
    11541.      1    assertJsonEqual,
    +
    11542.      1    assertOrThrow,
    +
    11543.      1    debugInline,
    +
    11544.      1    fsWriteFileWithParents,
    +
    11545.      1    globExclude,
    +
    11546.      1    htmlEscape,
    +
    11547.      1    jslint,
    +
    11548.      1    jslint_apidoc,
    +
    11549.      1    jslint_assert,
    +
    11550.      1    jslint_charset_ascii,
    +
    11551.      1    jslint_cli,
    +
    11552.      1    jslint_edition,
    +
    11553.      1    jslint_phase1_split,
    +
    11554.      1    jslint_phase2_lex,
    +
    11555.      1    jslint_phase3_parse,
    +
    11556.      1    jslint_phase4_walk,
    +
    11557.      1    jslint_phase5_whitage,
    +
    11558.      1    jslint_report,
    +
    11559.      1    jstestDescribe,
    +
    11560.      1    jstestIt,
    +
    11561.      1    jstestOnExit,
    +
    11562.      1    moduleFsInit,
    +
    11563.      1    noop,
    +
    11564.      1    objectDeepCopyWithKeysSorted,
    +
    11565.      1    v8CoverageListMerge,
    +
    11566.      1    v8CoverageReportCreate
    +
    11567.      1}));
    +
    11568.      1// module.exports = jslint_export;              // Export jslint as cjs.
    +
    11569.      1export default Object.freeze(jslint_export);    // Export jslint as esm.
    +
    11570.      1jslint_import_meta_url = import.meta.url;
    +
    11571.      1
    +
    11572.      1// Run jslint_cli.
    +
    11573.      1jslint_cli({});
    +
    11574.      1
    +
    + + + + diff --git a/branch-beta/.artifact/coverage/jslint_wrapper_cjs.cjs.html b/branch-beta/.artifact/coverage/jslint_wrapper_cjs.cjs.html new file mode 100644 index 000000000..7e2512946 --- /dev/null +++ b/branch-beta/.artifact/coverage/jslint_wrapper_cjs.cjs.html @@ -0,0 +1,217 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / jslint_wrapper_cjs.cjs
    +
    +
    +
    +
    + 100.00 %
    + 49 / 49 +
    +
    + 0 / 49 +
    +
    + + +
    +
        1.      1// The Unlicense
    +
        2.      1//
    +
        3.      1// This is free and unencumbered software released into the public domain.
    +
        4.      1//
    +
        5.      1// Anyone is free to copy, modify, publish, use, compile, sell, or
    +
        6.      1// distribute this software, either in source code form or as a compiled
    +
        7.      1// binary, for any purpose, commercial or non-commercial, and by any
    +
        8.      1// means.
    +
        9.      1//
    +
       10.      1// In jurisdictions that recognize copyright laws, the author or authors
    +
       11.      1// of this software dedicate any and all copyright interest in the
    +
       12.      1// software to the public domain. We make this dedication for the benefit
    +
       13.      1// of the public at large and to the detriment of our heirs and
    +
       14.      1// successors. We intend this dedication to be an overt act of
    +
       15.      1// relinquishment in perpetuity of all present and future rights to this
    +
       16.      1// software under copyright law.
    +
       17.      1//
    +
       18.      1// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    +
       19.      1// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    +
       20.      1// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    +
       21.      1// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
    +
       22.      1// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
    +
       23.      1// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    +
       24.      1// OTHER DEALINGS IN THE SOFTWARE.
    +
       25.      1
    +
       26.      1
    +
       27.      1/*jslint beta, node*/
    +
       28.      1/*property
    +
       29.      1    module, readFileSync, replace, runInNewContext
    +
       30.      1*/
    +
       31.      1require("vm").runInNewContext(
    +
       32.      1    (
    +
       33.      1        "\"use strict\";"
    +
       34.      1        + require("fs").readFileSync(
    +
       35.      1            __dirname + "/jslint.mjs",
    +
       36.      1            "utf8"
    +
       37.      1        ).replace(
    +
       38.      1            "\nexport default Object.freeze(jslint_export);",
    +
       39.      1            "\nmodule.exports = jslint_export;"
    +
       40.      1        ).replace(
    +
       41.      1            "\njslint_import_meta_url = import.meta.url;",
    +
       42.      1            "\n// jslint_import_meta_url = import.meta.url;"
    +
       43.      1        )
    +
       44.      1    ),
    +
       45.      1    {
    +
       46.      1        module
    +
       47.      1    }
    +
       48.      1);
    +
       49.      1
    +
    + + + + diff --git a/branch-beta/.artifact/coverage/test.mjs.html b/branch-beta/.artifact/coverage/test.mjs.html new file mode 100644 index 000000000..455e80af5 --- /dev/null +++ b/branch-beta/.artifact/coverage/test.mjs.html @@ -0,0 +1,1744 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test.mjs
    +
    +
    +
    +
    + 100.00 %
    + 1576 / 1576 +
    +
    + 0 / 1576 +
    +
    + + +
    +
        1.      1/*jslint beta, node*/
    +
        2.      1import jslint from "./jslint.mjs";
    +
        3.      1import jslintCjs from "./jslint_wrapper_cjs.cjs";
    +
        4.      1import moduleFs from "fs";
    +
        5.      1import modulePath from "path";
    +
        6.      1
    +
        7.      1let {
    +
        8.      1    assertErrorThrownAsync,
    +
        9.      1    assertJsonEqual,
    +
       10.      1    assertOrThrow,
    +
       11.      1    debugInline,
    +
       12.      1    fsWriteFileWithParents,
    +
       13.      1    globExclude,
    +
       14.      1    jstestDescribe,
    +
       15.      1    jstestIt,
    +
       16.      1    jstestOnExit,
    +
       17.      1    moduleFsInit,
    +
       18.      1    noop,
    +
       19.      1    v8CoverageListMerge,
    +
       20.      1    v8CoverageReportCreate
    +
       21.      1} = jslint;
    +
       22.      1let sourceJslintMjs;
    +
       23.      1let testCoverageMergeData;
    +
       24.      1
    +
       25.      1await (async function init() {
    +
       26.      1
    +
       27.      1// Coverage-hack - Ugly-hack to get test-coverage for all initialization-states.
    +
       28.      1
    +
       29.      1    moduleFsInit();
    +
       30.      1    moduleFsInit();
    +
       31.      1
    +
       32.      1// Cleanup directory .tmp
    +
       33.      1
    +
       34.      1    await moduleFs.promises.rm(".tmp", {
    +
       35.      1        recursive: true
    +
       36.      1    }).catch(noop);
    +
       37.      1
    +
       38.      1// init sourceJslintMjs
    +
       39.      1
    +
       40.      1    sourceJslintMjs = await moduleFs.promises.readFile("jslint.mjs", "utf8");
    +
       41.      1
    +
       42.      1// init testCoverageMergeData
    +
       43.      1
    +
       44.      1    testCoverageMergeData = JSON.parse(
    +
       45.      1        await moduleFs.promises.readFile(
    +
       46.      1            "test_coverage_merge_data.json",
    +
       47.      1            "utf8"
    +
       48.      1        )
    +
       49.      1    );
    +
       50.      1}());
    +
       51.      1
    +
       52.      1jstestDescribe((
    +
       53.      1    "test fsXxx handling-behavior"
    +
       54.      1), function testBehaviorFsXxx() {
    +
       55.      1    jstestIt((
    +
       56.      1        "test fsWriteFileWithParents handling-behavior"
    +
       57.      1    ), async function () {
    +
       58.      1        await Promise.all([
    +
       59.      1            1, 2, 3, 4
    +
       60.      4        ].map(async function () {
    +
       61.      4            await fsWriteFileWithParents(
    +
       62.      4                ".tmp/fsWriteFileWithParents/aa/bb/cc",
    +
       63.      4                "aa"
    +
       64.      4            );
    +
       65.      4        }));
    +
       66.      1        assertJsonEqual(
    +
       67.      1            await moduleFs.promises.readFile(
    +
       68.      1                ".tmp/fsWriteFileWithParents/aa/bb/cc",
    +
       69.      1                "utf8"
    +
       70.      1            ),
    +
       71.      1            "aa"
    +
       72.      1        );
    +
       73.      1    });
    +
       74.      1});
    +
       75.      1
    +
       76.      1jstestDescribe((
    +
       77.      1    "test globXxx handling-behavior"
    +
       78.      1), function testBehaviorGlobXxx() {
    +
       79.      1    jstestIt((
    +
       80.      1        "test globAssertNotWeird-error handling-behavior"
    +
       81.      1    ), async function () {
    +
       82.      1        await Promise.all([
    +
       83.      1            "\n",
    +
       84.      1            "\r",
    +
       85.      1            "\u0000"
    +
       86.      3        ].map(async function (char) {
    +
       87.      3            await assertErrorThrownAsync(function () {
    +
       88.      3                return globExclude({
    +
       89.      3                    pathnameList: [
    +
       90.      3                        "aa",
    +
       91.      3                        `cc/${char}/dd`,
    +
       92.      3                        "bb"
    +
       93.      3                    ]
    +
       94.      3                });
    +
       95.      3            }, (
    +
       96.      3                "Weird character "
    +
       97.      3                + JSON.stringify(char).replace("\\", "\\\\")
    +
       98.      3                + " found in "
    +
       99.      3            ));
    +
      100.      3        }));
    +
      101.      1    });
    +
      102.      1    jstestIt((
    +
      103.      1        "test globExclude handling-behavior"
    +
      104.      1    ), function () {
    +
      105.      1        let pathnameList = [
    +
      106.      1            ".dockerignore",
    +
      107.      1            ".eslintrc.js",
    +
      108.      1            ".gitignore",
    +
      109.      1            ".npmignore",
    +
      110.      1            ".travis.yml",
    +
      111.      1            "/node_modules/aa/bb/cc.js",
    +
      112.      1            "/node_modules/aa/bb/dd.js",
    +
      113.      1            "CHANGELOG.md",
    +
      114.      1            "CONTRIBUTING.md",
    +
      115.      1            "Dockerfile",
    +
      116.      1            "LICENSE",
    +
      117.      1            "Makefile",
    +
      118.      1            "README.md",
    +
      119.      1            "appveyor.yml",
    +
      120.      1            "benchmark/insert-transaction.sql",
    +
      121.      1            "benchmark/insert.js",
    +
      122.      1            "binding.gyp",
    +
      123.      1            "cloudformation/ci.template.js",
    +
      124.      1            "deps/common-sqlite.gypi",
    +
      125.      1            "deps/extract.py",
    +
      126.      1            "deps/sqlite-autoconf-3340000.tar.gz",
    +
      127.      1            "deps/sqlite3.gyp",
    +
      128.      1            "examples/simple-chaining.js",
    +
      129.      1            "lib/index.js",
    +
      130.      1            "lib/sqlite3-binding.js",
    +
      131.      1            "lib/sqlite3.js",
    +
      132.      1            "lib/trace.js",
    +
      133.      1            "node_modules/aa/bb/cc.js",
    +
      134.      1            "node_modules/aa/bb/dd.js",
    +
      135.      1            "package.json",
    +
      136.      1            "scripts/build-appveyor.bat",
    +
      137.      1            "scripts/build-local.bat",
    +
      138.      1            "scripts/build_against_electron.sh",
    +
      139.      1            "scripts/build_against_node.sh",
    +
      140.      1            "scripts/build_against_node_webkit.sh",
    +
      141.      1            "scripts/build_for_node_webkit.cmd",
    +
      142.      1            "scripts/install_node.sh",
    +
      143.      1            "scripts/validate_tag.sh",
    +
      144.      1            "sqlite3.js",
    +
      145.      1            "src/async.h",
    +
      146.      1            "src/backup.cc",
    +
      147.      1            "src/backup.h",
    +
      148.      1            "src/database.cc",
    +
      149.      1            "src/database.h",
    +
      150.      1            "src/gcc-preinclude.h",
    +
      151.      1            "src/macros.h",
    +
      152.      1            "src/node_sqlite3.cc",
    +
      153.      1            "src/statement.cc",
    +
      154.      1            "src/statement.h",
    +
      155.      1            "src/threading.h",
    +
      156.      1            "test/affected.test.js",
    +
      157.      1            "test/backup.test.js",
    +
      158.      1            "test/blob.test.js",
    +
      159.      1            "test/cache.test.js",
    +
      160.      1            "test/constants.test.js",
    +
      161.      1            "test/database_fail.test.js",
    +
      162.      1            "test/each.test.js",
    +
      163.      1            "test/exec.test.js",
    +
      164.      1            "test/extension.test.js",
    +
      165.      1            "test/fts-content.test.js",
    +
      166.      1            "test/interrupt.test.js",
    +
      167.      1            "test/issue-108.test.js",
    +
      168.      1            "test/json.test.js",
    +
      169.      1            "test/map.test.js",
    +
      170.      1            "test/named_columns.test.js",
    +
      171.      1            "test/named_params.test.js",
    +
      172.      1            "test/null_error.test.js",
    +
      173.      1            "test/nw/.gitignore",
    +
      174.      1            "test/nw/Makefile",
    +
      175.      1            "test/nw/index.html",
    +
      176.      1            "test/nw/package.json",
    +
      177.      1            "test/open_close.test.js",
    +
      178.      1            "test/other_objects.test.js",
    +
      179.      1            "test/parallel_insert.test.js",
    +
      180.      1            "test/prepare.test.js",
    +
      181.      1            "test/profile.test.js",
    +
      182.      1            "test/rerun.test.js",
    +
      183.      1            "test/scheduling.test.js",
    +
      184.      1            "test/serialization.test.js",
    +
      185.      1            "test/support/createdb-electron.js",
    +
      186.      1            "test/support/createdb.js",
    +
      187.      1            "test/support/elmo.png",
    +
      188.      1            "test/support/helper.js",
    +
      189.      1            "test/support/prepare.db",
    +
      190.      1            "test/support/script.sql",
    +
      191.      1            "test/trace.test.js",
    +
      192.      1            "test/unicode.test.js",
    +
      193.      1            "test/upsert.test.js",
    +
      194.      1            "test/verbose.test.js",
    +
      195.      1            "tools/docker/architecture/linux-arm/Dockerfile",
    +
      196.      1            "tools/docker/architecture/linux-arm/run.sh",
    +
      197.      1            "tools/docker/architecture/linux-arm64/Dockerfile",
    +
      198.      1            "tools/docker/architecture/linux-arm64/run.sh"
    +
      199.      1        ];
    +
      200.      1        [
    +
      201.      1            "tes?/",
    +
      202.      1            "tes[-t-]/",
    +
      203.      1            "tes[-t]/",
    +
      204.      1            "tes[0-9A-Z_a-z-]/",
    +
      205.      1            "tes[t-]/",
    +
      206.      1            "test/**/*.js"
    +
      207.      6        ].forEach(function (aa) {
    +
      208.      6            [
    +
      209.      6                "li*/*.js",
    +
      210.      6                "li?/*.js",
    +
      211.      6                "lib/",
    +
      212.      6                "lib/*",
    +
      213.      6                "lib/**/*.js",
    +
      214.      6                "lib/*.js"
    +
      215.     36            ].forEach(function (bb) {
    +
      216.     36                [
    +
      217.     36                    "",
    +
      218.     36                    "**/node_modules/",
    +
      219.     36                    "node_modules/"
    +
      220.    108                ].forEach(function (cc) {
    +
      221.    108                    assertJsonEqual(
    +
      222.    108                        globExclude({
    +
      223.    108                            excludeList: [
    +
      224.    108                                "tes[!0-9A-Z_a-z-]/",
    +
      225.    108                                "tes[^0-9A-Z_a-z-]/",
    +
      226.    108                                "test/suppor*/*elper.js",
    +
      227.    108                                "test/suppor?/?elper.js",
    +
      228.    108                                "test/support/helper.js"
    +
      229.    108                            ].concat(aa, cc),
    +
      230.    108                            includeList: [
    +
      231.    108                                "**/*.cjs",
    +
      232.    108                                "**/*.js",
    +
      233.    108                                "**/*.mjs",
    +
      234.    108                                "lib/sqlite3.js"
    +
      235.    108                            ].concat(bb),
    +
      236.    108                            pathnameList
    +
      237.    108                        }).pathnameList,
    +
      238.    108                        [
    +
      239.    108                            ".eslintrc.js",
    +
      240.    108                            "benchmark/insert.js",
    +
      241.    108                            "cloudformation/ci.template.js",
    +
      242.    108                            "examples/simple-chaining.js",
    +
      243.    108                            "lib/index.js",
    +
      244.    108                            "lib/sqlite3-binding.js",
    +
      245.    108                            "lib/sqlite3.js",
    +
      246.    108                            "lib/trace.js",
    +
      247.    108                            "sqlite3.js"
    +
      248.    108                        ].concat(
    +
      249.    108                            cc === "**/node_modules/"
    +
      250.     36                            ? [
    +
      251.     36                                "node_modules/aa/bb/cc.js",
    +
      252.     36                                "node_modules/aa/bb/dd.js"
    +
      253.     36                            ]
    +
      254.     72                            : cc === "node_modules/"
    +
      255.     72                            ? [
    +
      256.     72                                "/node_modules/aa/bb/cc.js",
    +
      257.     72                                "/node_modules/aa/bb/dd.js"
    +
      258.     72                            ]
    +
      259.     72                            : [
    +
      260.     72                                "/node_modules/aa/bb/cc.js",
    +
      261.     72                                "/node_modules/aa/bb/dd.js",
    +
      262.     72                                "node_modules/aa/bb/cc.js",
    +
      263.     72                                "node_modules/aa/bb/dd.js"
    +
      264.     72                            ]
    +
      265.    108                        ).sort()
    +
      266.    108                    );
    +
      267.    108                });
    +
      268.     36            });
    +
      269.      6        });
    +
      270.      1    });
    +
      271.      1    jstestIt((
    +
      272.      1        "test globToRegexp handling-behavior"
    +
      273.      1    ), function () {
    +
      274.      1        Object.entries({
    +
      275.      1            "*": (
    +
      276.      1                /^[^\/]*?$/gm
    +
      277.      1            ),
    +
      278.      1            "**": (
    +
      279.      1                /^.*?$/gm
    +
      280.      1            ),
    +
      281.      1            "***": (
    +
      282.      1                /^.*?$/gm
    +
      283.      1            ),
    +
      284.      1            "****": (
    +
      285.      1                /^.*?$/gm
    +
      286.      1            ),
    +
      287.      1            "****////****": (
    +
      288.      1                /^.*?$/gm
    +
      289.      1            ),
    +
      290.      1            "***///***": (
    +
      291.      1                /^.*?$/gm
    +
      292.      1            ),
    +
      293.      1            "**/*": (
    +
      294.      1                /^.*?$/gm
    +
      295.      1            ),
    +
      296.      1            "**/node_modules/": (
    +
      297.      1                /^.*?\/node_modules\/.*?$/gm
    +
      298.      1            ),
    +
      299.      1            "**/node_modules/**/*": (
    +
      300.      1                /^.*?\/node_modules\/.*?$/gm
    +
      301.      1            ),
    +
      302.      1            "?": (
    +
      303.      1                /^[^\/]$/gm
    +
      304.      1            ),
    +
      305.      1            "[!0-9A-Za-z-]": (
    +
      306.      1                /^[^0-9A-Za-z\-]$/gm
    +
      307.      1            ),
    +
      308.      1            "[0-9A-Za-z-]": (
    +
      309.      1                /^[0-9A-Za-z\-]$/gm
    +
      310.      1            ),
    +
      311.      1            "[[]] ]][[": (
    +
      312.      1                /^[\[]\] \]\][\[]$/gm
    +
      313.      1            ),
    +
      314.      1            "[]": (
    +
      315.      1                /^$/gm
    +
      316.      1            ),
    +
      317.      1            "[^0-9A-Za-z-]": (
    +
      318.      1                /^[^0-9A-Za-z\-]$/gm
    +
      319.      1            ),
    +
      320.      1            "aa/bb/cc": (
    +
      321.      1                /^aa\/bb\/cc$/gm
    +
      322.      1            ),
    +
      323.      1            "aa/bb/cc/": (
    +
      324.      1                /^aa\/bb\/cc\/.*?$/gm
    +
      325.      1            ),
    +
      326.      1            "li*/*": (
    +
      327.      1                /^li[^\/]*?\/[^\/]*?$/gm
    +
      328.      1            ),
    +
      329.      1            "li?/*": (
    +
      330.      1                /^li[^\/]\/[^\/]*?$/gm
    +
      331.      1            ),
    +
      332.      1            "lib/": (
    +
      333.      1                /^lib\/.*?$/gm
    +
      334.      1            ),
    +
      335.      1            "lib/*": (
    +
      336.      1                /^lib\/[^\/]*?$/gm
    +
      337.      1            ),
    +
      338.      1            "lib/**/*.js": (
    +
      339.      1                /^lib\/.*?\.js$/gm
    +
      340.      1            ),
    +
      341.      1            "lib/*.js": (
    +
      342.      1                /^lib\/[^\/]*?\.js$/gm
    +
      343.      1            ),
    +
      344.      1            "node_modules/": (
    +
      345.      1                /^node_modules\/.*?$/gm
    +
      346.      1            ),
    +
      347.      1            "node_modules/**/*": (
    +
      348.      1                /^node_modules\/.*?$/gm
    +
      349.      1            ),
    +
      350.      1            "tes[!0-9A-Z_a-z-]/**/*": (
    +
      351.      1                /^tes[^0-9A-Z_a-z\-]\/.*?$/gm
    +
      352.      1            ),
    +
      353.      1            "tes[0-9A-Z_a-z-]/**/*": (
    +
      354.      1                /^tes[0-9A-Z_a-z\-]\/.*?$/gm
    +
      355.      1            ),
    +
      356.      1            "tes[^0-9A-Z_a-z-]/**/*": (
    +
      357.      1                /^tes[^0-9A-Z_a-z\-]\/.*?$/gm
    +
      358.      1            ),
    +
      359.      1            "test/**/*": (
    +
      360.      1                /^test\/.*?$/gm
    +
      361.      1            ),
    +
      362.      1            "test/**/*.js": (
    +
      363.      1                /^test\/.*?\.js$/gm
    +
      364.      1            ),
    +
      365.      1            "test/suppor*/*elper.js": (
    +
      366.      1                /^test\/suppor[^\/]*?\/[^\/]*?elper\.js$/gm
    +
      367.      1            ),
    +
      368.      1            "test/suppor?/?elper.js": (
    +
      369.      1                /^test\/suppor[^\/]\/[^\/]elper\.js$/gm
    +
      370.      1            ),
    +
      371.      1            "test/support/helper.js": (
    +
      372.      1                /^test\/support\/helper\.js$/gm
    +
      373.      1            )
    +
      374.     33        }).forEach(function ([
    +
      375.     33            pattern, rgx
    +
      376.     33        ]) {
    +
      377.     33            assertJsonEqual(
    +
      378.     33                globExclude({
    +
      379.     33                    excludeList: [
    +
      380.     33                        pattern
    +
      381.     33                    ]
    +
      382.     33                }).excludeList[0].source,
    +
      383.     33                rgx.source
    +
      384.     33            );
    +
      385.     33            assertJsonEqual(
    +
      386.     33                globExclude({
    +
      387.     33                    includeList: [
    +
      388.     33                        pattern
    +
      389.     33                    ]
    +
      390.     33                }).includeList[0].source,
    +
      391.     33                rgx.source
    +
      392.     33            );
    +
      393.     33        });
    +
      394.      1    });
    +
      395.      1});
    +
      396.      1
    +
      397.      1jstestDescribe((
    +
      398.      1    "test jslint's cli handling-behavior"
    +
      399.      1), function testBehaviorJslintCli() {
    +
      400.      5    function processExit0(exitCode) {
    +
      401.      5        assertOrThrow(exitCode === 0, exitCode);
    +
      402.      5    }
    +
      403.      6    function processExit1(exitCode) {
    +
      404.      6        assertOrThrow(exitCode === 1, exitCode);
    +
      405.      6    }
    +
      406.      1    jstestIt((
    +
      407.      1        "test cli-null-case handling-behavior"
    +
      408.      1    ), function () {
    +
      409.      1        jslint.jslint_cli({
    +
      410.      1            mode_noop: true,
    +
      411.      1            process_exit: processExit0
    +
      412.      1        });
    +
      413.      1    });
    +
      414.      1    jstestIt((
    +
      415.      1        "test cli-window-jslint handling-behavior"
    +
      416.      1    ), function () {
    +
      417.      1        [
    +
      418.      1            "&window_jslint=",
    +
      419.      1            "&window_jslint=12",
    +
      420.      1            "&window_jslint=1?",
    +
      421.      1            "&window_jslint=?",
    +
      422.      1            "?window_jslint=",
    +
      423.      1            "?window_jslint=12",
    +
      424.      1            "?window_jslint=1?",
    +
      425.      1            "?window_jslint=?",
    +
      426.      1            "window_jslint=1",
    +
      427.      1            "window_jslint=1&",
    +
      428.      1            "window_jslint=12",
    +
      429.      1            "window_jslint=1?"
    +
      430.     12        ].forEach(function (import_meta_url) {
    +
      431.     12            jslint.jslint_cli({
    +
      432.     12                import_meta_url
    +
      433.     12            });
    +
      434.     12            assertOrThrow(globalThis.jslint === undefined);
    +
      435.     12        });
    +
      436.      1        [
    +
      437.      1            "&window_jslint=1",
    +
      438.      1            "&window_jslint=1&",
    +
      439.      1            "?window_jslint=1",
    +
      440.      1            "?window_jslint=1&"
    +
      441.      4        ].forEach(function (import_meta_url) {
    +
      442.      4            jslint.jslint_cli({
    +
      443.      4                import_meta_url
    +
      444.      4            });
    +
      445.      4            assertOrThrow(globalThis.jslint === jslint);
    +
      446.      4            delete globalThis.jslint;
    +
      447.      4        });
    +
      448.      1    });
    +
      449.      1    jstestIt((
    +
      450.      1        "test cli-cjs-and-invalid-file handling-behavior"
    +
      451.      1    ), async function () {
    +
      452.      1        await fsWriteFileWithParents(".test_dir.cjs/touch.txt", "");
    +
      453.      1        [
    +
      454.      1            ".",            // test dir handling-behavior
    +
      455.      1            "jslint.mjs",   // test file handling-behavior
    +
      456.      1            undefined       // test file-undefined handling-behavior
    +
      457.      3        ].forEach(function (file) {
    +
      458.      3            jslint.jslint_cli({
    +
      459.      3                file,
    +
      460.      3                mode_cli: true,
    +
      461.      3                process_env: {
    +
      462.      3                    JSLINT_BETA: "1"
    +
      463.      3                },
    +
      464.      3                process_exit: processExit0
    +
      465.      3            });
    +
      466.      3        });
    +
      467.      1    });
    +
      468.      1    jstestIt((
    +
      469.      1        "test cli-apidoc handling-behavior"
    +
      470.      1    ), function () {
    +
      471.      1        jslint.jslint_cli({
    +
      472.      1            mode_cli: true,
    +
      473.      1            process_argv: [
    +
      474.      1                "node",
    +
      475.      1                "jslint.mjs",
    +
      476.      1                "jslint_apidoc=.artifact/apidoc.html",
    +
      477.      1                JSON.stringify({
    +
      478.      1                    example_list: [
    +
      479.      1                        "README.md",
    +
      480.      1                        "test.mjs",
    +
      481.      1                        "jslint.mjs"
    +
      482.      1                    ],
    +
      483.      1                    github_repo: "https://github.com/jslint-org/jslint",
    +
      484.      1                    module_list: [
    +
      485.      1                        {
    +
      486.      1                            pathname: "./jslint.mjs"
    +
      487.      1                        }
    +
      488.      1                    ],
    +
      489.      1                    package_name: "JSLint",
    +
      490.      1                    version: jslint.jslint_edition
    +
      491.      1                })
    +
      492.      1            ],
    +
      493.      1            process_exit: processExit0
    +
      494.      1        });
    +
      495.      1    });
    +
      496.      1    jstestIt((
    +
      497.      1        "test cli-file-error handling-behavior"
    +
      498.      1    ), function () {
    +
      499.      1        jslint.jslint_cli({
    +
      500.      1            // suppress error
    +
      501.      1            console_error: noop,
    +
      502.      1            file: "undefined",
    +
      503.      1            mode_cli: true,
    +
      504.      1            process_exit: processExit1
    +
      505.      1        });
    +
      506.      1    });
    +
      507.      1    jstestIt((
    +
      508.      1        "test cli-syntax-error handling-behavior"
    +
      509.      1    ), function () {
    +
      510.      1        jslint.jslint_cli({
    +
      511.      1            // suppress error
    +
      512.      1            console_error: noop,
    +
      513.      1            file: "syntax-error.js",
    +
      514.      1            mode_cli: true,
    +
      515.      1            option: {
    +
      516.      1                trace: true
    +
      517.      1            },
    +
      518.      1            process_exit: processExit1,
    +
      519.      1            source: "syntax error"
    +
      520.      1        });
    +
      521.      1    });
    +
      522.      1    jstestIt((
    +
      523.      1        "test cli-report handling-behavior"
    +
      524.      1    ), function () {
    +
      525.      1        jslint.jslint_cli({
    +
      526.      1            // suppress error
    +
      527.      1            console_error: noop,
    +
      528.      1            mode_cli: true,
    +
      529.      1            process_argv: [
    +
      530.      1                "node",
    +
      531.      1                "jslint.mjs",
    +
      532.      1                "jslint_report=.tmp/jslint_report.html",
    +
      533.      1                "jslint.mjs"
    +
      534.      1            ],
    +
      535.      1            process_exit: processExit0
    +
      536.      1        });
    +
      537.      1    });
    +
      538.      1    jstestIt((
    +
      539.      1        "test cli-report-error handling-behavior"
    +
      540.      1    ), function () {
    +
      541.      1        jslint.jslint_cli({
    +
      542.      1            // suppress error
    +
      543.      1            console_error: noop,
    +
      544.      1            mode_cli: true,
    +
      545.      1            process_argv: [
    +
      546.      1                "node",
    +
      547.      1                "jslint.mjs",
    +
      548.      1                "jslint_report=.tmp/jslint_report.html",
    +
      549.      1                "syntax-error.js"
    +
      550.      1            ],
    +
      551.      1            process_exit: processExit1,
    +
      552.      1            source: "syntax error"
    +
      553.      1        });
    +
      554.      1    });
    +
      555.      1    jstestIt((
    +
      556.      1        "test cli-report-json handling-behavior"
    +
      557.      1    ), function () {
    +
      558.      1        jslint.jslint_cli({
    +
      559.      1            // suppress error
    +
      560.      1            console_error: noop,
    +
      561.      1            mode_cli: true,
    +
      562.      1            process_argv: [
    +
      563.      1                "node",
    +
      564.      1                "jslint.mjs",
    +
      565.      1                "jslint_report=.tmp/jslint_report.html",
    +
      566.      1                "aa.json"
    +
      567.      1            ],
    +
      568.      1            process_exit: processExit0,
    +
      569.      1            source: "[]"
    +
      570.      1        });
    +
      571.      1    });
    +
      572.      1    jstestIt((
    +
      573.      1        "test cli-report-json-error handling-behavior"
    +
      574.      1    ), function () {
    +
      575.      1        jslint.jslint_cli({
    +
      576.      1            // suppress error
    +
      577.      1            console_error: noop,
    +
      578.      1            mode_cli: true,
    +
      579.      1            process_argv: [
    +
      580.      1                "node",
    +
      581.      1                "jslint.mjs",
    +
      582.      1                "jslint_report=.tmp/jslint_report.html",
    +
      583.      1                "aa.json"
    +
      584.      1            ],
    +
      585.      1            process_exit: processExit1,
    +
      586.      1            source: "["
    +
      587.      1        });
    +
      588.      1    });
    +
      589.      1    jstestIt((
    +
      590.      1        "test cli-report-misc handling-behavior"
    +
      591.      1    ), function () {
    +
      592.      1        jslint.jslint_cli({
    +
      593.      1            // suppress error
    +
      594.      1            console_error: noop,
    +
      595.      1            mode_cli: true,
    +
      596.      1            process_argv: [
    +
      597.      1                "node",
    +
      598.      1                "jslint.mjs",
    +
      599.      1                "jslint_report=.tmp/jslint_report.html",
    +
      600.      1                "aa.js"
    +
      601.      1            ],
    +
      602.      1            process_exit: processExit0,
    +
      603.      1            source: "let aa = 0;"
    +
      604.      1        });
    +
      605.      1        jslint.jslint_cli({
    +
      606.      1            // suppress error
    +
      607.      1            console_error: noop,
    +
      608.      1            mode_cli: true,
    +
      609.      1            process_argv: [
    +
      610.      1                "node",
    +
      611.      1                "jslint.mjs",
    +
      612.      1                "jslint_report=.tmp/jslint_report.html",
    +
      613.      1                "aa.js"
    +
      614.      1            ],
    +
      615.      1            process_exit: processExit1,
    +
      616.      1            source: "(aa)=>aa; function aa([aa]){}"
    +
      617.      1        });
    +
      618.      1    });
    +
      619.      1    jstestIt((
    +
      620.      1        "test cli-jslint-wrapper-vim handling-behavior"
    +
      621.      1    ), function () {
    +
      622.      1        jslint.jslint_cli({
    +
      623.      1            // suppress error
    +
      624.      1            console_error: noop,
    +
      625.      1            mode_cli: true,
    +
      626.      1            process_argv: [
    +
      627.      1                "node",
    +
      628.      1                "jslint.mjs",
    +
      629.      1                "jslint_wrapper_vim",
    +
      630.      1                "syntax-error.js"
    +
      631.      1            ],
    +
      632.      1            process_exit: processExit1,
    +
      633.      1            source: "syntax error"
    +
      634.      1        });
    +
      635.      1    });
    +
      636.      1});
    +
      637.      1
    +
      638.      1jstestDescribe((
    +
      639.      1    "test jslint's no-warnings handling-behavior"
    +
      640.      1), function testBehaviorJslintNoWarnings() {
    +
      641.      1    jstestIt((
    +
      642.      1        "test jslint's no-warnings handling-behavior"
    +
      643.      1    ), function () {
    +
      644.      1        Object.values({
    +
      645.      1            array: [
    +
      646.      1                "new Array(0);"
    +
      647.      1            ],
    +
      648.      1            async_await: [
    +
      649.      1                "async function aa() {\n    await aa();\n}",
    +
      650.      1
    +
      651.      1// PR-405 - Bugfix - fix expression after "await" mis-identified as statement.
    +
      652.      1
    +
      653.      1                "async function aa() {\n    await aa;\n}",
    +
      654.      1                (
    +
      655.      1                    "async function aa() {\n"
    +
      656.      1                    + "    try {\n"
    +
      657.      1                    + "        aa();\n"
    +
      658.      1                    + "    } catch (err) {\n"
    +
      659.      1                    + "        await err();\n"
    +
      660.      1                    + "    }\n"
    +
      661.      1                    + "}\n"
    +
      662.      1                ),
    +
      663.      1                (
    +
      664.      1                    "async function aa() {\n"
    +
      665.      1                    + "    try {\n"
    +
      666.      1                    + "        await aa();\n"
    +
      667.      1                    + "    } catch (err) {\n"
    +
      668.      1                    + "        await err();\n"
    +
      669.      1                    + "    }\n"
    +
      670.      1                    + "}\n"
    +
      671.      1                ),
    +
      672.      1
    +
      673.      1// PR-370 - Add top-level-await support.
    +
      674.      1
    +
      675.      1                "await String();\n"
    +
      676.      1            ],
    +
      677.      1
    +
      678.      1// PR-351 - Add BigInt support.
    +
      679.      1
    +
      680.      1            bigint: [
    +
      681.      1                "let aa = 0b0n;\n",
    +
      682.      1                "let aa = 0o0n;\n",
    +
      683.      1                "let aa = 0x0n;\n",
    +
      684.      1                "let aa = BigInt(0n);\n",
    +
      685.      1                "let aa = typeof aa === \"bigint\";\n"
    +
      686.      1            ],
    +
      687.      1            date: [
    +
      688.      1                "Date.getTime();",
    +
      689.      1                "let aa = aa().getTime();",
    +
      690.      1                "let aa = aa.aa().getTime();"
    +
      691.      1            ],
    +
      692.      1            directive: [
    +
      693.      1                "#!\n/*jslint browser:false, node*/\n\"use strict\";",
    +
      694.      1                "/*property aa bb*/"
    +
      695.      1            ],
    +
      696.      1            for: [
    +
      697.      1                (
    +
      698.      1                    "/*jslint for*/\n"
    +
      699.      1                    + "function aa(bb) {\n"
    +
      700.      1                    + "    for (bb = 0; bb < 0; bb += 1) {\n"
    +
      701.      1                    + "        bb();\n"
    +
      702.      1                    + "    }\n"
    +
      703.      1                    + "}\n"
    +
      704.      1                )
    +
      705.      1            ],
    +
      706.      1            jslint_disable: [
    +
      707.      1                "/*jslint-disable*/\n0\n/*jslint-enable*/"
    +
      708.      1            ],
    +
      709.      1            jslint_ignore_line: [
    +
      710.      1                "0 //jslint-ignore-line"
    +
      711.      1            ],
    +
      712.      1            json: [
    +
      713.      1                "{\"aa\":[[],-0,null]}"
    +
      714.      1            ],
    +
      715.      1            label: [
    +
      716.      1                (
    +
      717.      1                    "function aa() {\n"
    +
      718.      1                    + "bb:\n"
    +
      719.      1                    + "    while (true) {\n"
    +
      720.      1                    + "        if (true) {\n"
    +
      721.      1                    + "            break bb;\n"
    +
      722.      1                    + "        }\n"
    +
      723.      1                    + "    }\n"
    +
      724.      1                    + "}\n"
    +
      725.      1                )
    +
      726.      1            ],
    +
      727.      1            loop: [
    +
      728.      1                (
    +
      729.      1                    "function aa() {\n"
    +
      730.      1                    + "    do {\n"
    +
      731.      1                    + "        aa();\n"
    +
      732.      1                    + "    } while (aa());\n"
    +
      733.      1                    + "}\n"
    +
      734.      1                ),
    +
      735.      1
    +
      736.      1// PR-378 - Relax warning "function_in_loop".
    +
      737.      1
    +
      738.      1                (
    +
      739.      1                    "function aa() {\n"
    +
      740.      1                    + "    while (true) {\n"
    +
      741.      1                    + "        (function () {\n"
    +
      742.      1                    + "            return;\n"
    +
      743.      1                    + "        }());\n"
    +
      744.      1                    + "    }\n"
    +
      745.      1                    + "}\n"
    +
      746.      1                )
    +
      747.      1            ],
    +
      748.      1            module: [
    +
      749.      1                "export default Object.freeze();",
    +
      750.      1
    +
      751.      1// PR-439 - Add grammar for "export async function ...".
    +
      752.      1
    +
      753.      1                (
    +
      754.      1                    "export default Object.freeze(async function () {\n"
    +
      755.      1                    + "    return await 0;\n"
    +
      756.      1                    + "});\n"
    +
      757.      1                ),
    +
      758.      1                "import {aa, bb} from \"aa\";\naa(bb);",
    +
      759.      1                "import {} from \"aa\";",
    +
      760.      1                "import(\"aa\").then(function () {\n    return;\n});",
    +
      761.      1                (
    +
      762.      1                    "let aa = 0;\n"
    +
      763.      1                    + "import(aa).then(aa).then(aa)"
    +
      764.      1                    + ".catch(aa).finally(aa);\n"
    +
      765.      1                )
    +
      766.      1            ],
    +
      767.      1            number: [
    +
      768.      1                "let aa = 0.0e0;",
    +
      769.      1                "let aa = 0b0;",
    +
      770.      1                "let aa = 0o0;",
    +
      771.      1                "let aa = 0x0;"
    +
      772.      1            ],
    +
      773.      1
    +
      774.      1// PR-390 - Add numeric-separator support.
    +
      775.      1
    +
      776.      1            numeric_separator: [
    +
      777.      1                "let aa = 0.0_0_0;",
    +
      778.      1                "let aa = 0b0_1111_1111n;\n",
    +
      779.      1                "let aa = 0o0_1234_1234n;\n",
    +
      780.      1                "let aa = 0x0_1234_1234n;\n",
    +
      781.      1                "let aa = 1_234_234.1_234_234E1_234_234;"
    +
      782.      1            ],
    +
      783.      1            optional_chaining: [
    +
      784.      1                "let aa = aa?.bb?.cc;"
    +
      785.      1            ],
    +
      786.      1            param: [
    +
      787.      1                "function aa({aa, bb}) {\n    return {aa, bb};\n}\n",
    +
      788.      1                (
    +
      789.      1                    "function aa({constructor}) {\n"
    +
      790.      1                    + "    return {constructor};\n"
    +
      791.      1                    + "}\n"
    +
      792.      1                )
    +
      793.      1            ],
    +
      794.      1            property: [
    +
      795.      1                "let aa = aa[`!`];"
    +
      796.      1            ],
    +
      797.      1            regexp: [
    +
      798.      1                "function aa() {\n    return /./;\n}",
    +
      799.      1                "let aa = /(?!.)(?:.)(?=.)/;",
    +
      800.      1                "let aa = /./gimuy;",
    +
      801.      1                "let aa = /[\\--\\-]/;"
    +
      802.      1            ],
    +
      803.      1            ternary: [
    +
      804.      1                (
    +
      805.      1                    "let aa = (\n"
    +
      806.      1                    + "    aa()\n"
    +
      807.      1                    + "    ? 0\n"
    +
      808.      1                    + "    : 1\n"
    +
      809.      1                    + ") "
    +
      810.      1                    + "&& (\n"
    +
      811.      1                    + "    aa()\n"
    +
      812.      1                    + "    ? 0\n"
    +
      813.      1                    + "    : 1\n"
    +
      814.      1                    + ");"
    +
      815.      1                ),
    +
      816.      1                (
    +
      817.      1                    "let aa = (\n"
    +
      818.      1                    + "    aa()\n"
    +
      819.      1                    + "    ? `${0}`\n"
    +
      820.      1                    + "    : `${1}`\n"
    +
      821.      1                    + ");"
    +
      822.      1                ),
    +
      823.      1
    +
      824.      1// PR-394 - Bugfix
    +
      825.      1// Fix jslint falsely believing megastring literals `0` and `1` are similar.
    +
      826.      1
    +
      827.      1                (
    +
      828.      1                    "let aa = (\n"
    +
      829.      1                    + "    aa()\n"
    +
      830.      1                    + "    ? `0`\n"
    +
      831.      1                    + "    : `1`\n"
    +
      832.      1                    + ");"
    +
      833.      1                )
    +
      834.      1            ],
    +
      835.      1            try_catch: [
    +
      836.      1                (
    +
      837.      1                    "let aa = 0;\n"
    +
      838.      1                    + "try {\n"
    +
      839.      1                    + "    aa();\n"
    +
      840.      1                    + "} catch (err) {\n"
    +
      841.      1                    + "    aa = err;\n"
    +
      842.      1                    + "}\n"
    +
      843.      1                    + "try {\n"
    +
      844.      1                    + "    aa();\n"
    +
      845.      1                    + "} catch (err) {\n"
    +
      846.      1                    + "    aa = err;\n"
    +
      847.      1                    + "}\n"
    +
      848.      1                    + "aa();\n"
    +
      849.      1                )
    +
      850.      1            ],
    +
      851.      1            try_finally: [
    +
      852.      1                (
    +
      853.      1                    "let aa = 0;\n"
    +
      854.      1                    + "try {\n"
    +
      855.      1                    + "    aa();\n"
    +
      856.      1                    + "} finally {\n"
    +
      857.      1                    + "    aa();\n"
    +
      858.      1                    + "}\n"
    +
      859.      1                )
    +
      860.      1            ],
    +
      861.      1            use_strict: [
    +
      862.      1                (
    +
      863.      1                    "\"use strict\";\n"
    +
      864.      1                    + "let aa = 0;\n"
    +
      865.      1                    + "function bb() {\n"
    +
      866.      1                    + "    \"use strict\";\n"
    +
      867.      1                    + "    return aa;\n"
    +
      868.      1                    + "}\n"
    +
      869.      1                )
    +
      870.      1            ],
    +
      871.      1            var: [
    +
      872.      1
    +
      873.      1// PR-363 - Bugfix
    +
      874.      1// Add test against false-warning <uninitialized 'bb'> in code
    +
      875.      1// '/*jslint node*/\nlet {aa:bb} = {}; bb();'.
    +
      876.      1
    +
      877.      1                "/*jslint node*/\n",
    +
      878.      1                ""
    +
      879.      2            ].map(function (directive) {
    +
      880.      2                return [
    +
      881.      2                    "let [\n    aa, bb = 0\n] = 0;\naa();\nbb();",
    +
      882.      2                    "let aa = 0;\nlet [...bb] = [...aa];\nbb();",
    +
      883.      2
    +
      884.      2// PR-459 - Allow destructuring-assignment after function-definition.
    +
      885.      2
    +
      886.      2                    (
    +
      887.      2                        "let aa;\n"
    +
      888.      2                        + "let bb;\n"
    +
      889.      2                        + "function cc() {\n"
    +
      890.      2                        + "    return;\n"
    +
      891.      2                        + "}\n"
    +
      892.      2                        + "[aa, bb] = cc();\n"
    +
      893.      2                    ),
    +
      894.      2                    "let constructor = 0;\nconstructor();",
    +
      895.      2                    "let {\n    aa: bb\n} = 0;\nbb();",
    +
      896.      2                    "let {\n    aa: bb,\n    bb: cc\n} = 0;\nbb();\ncc();",
    +
      897.      2                    "let {aa, bb} = 0;\naa();\nbb();",
    +
      898.      2                    "let {constructor} = 0;\nconstructor();"
    +
      899.     16                ].map(function (code) {
    +
      900.     16                    return directive + code;
    +
      901.     16                });
    +
      902.      2            }).flat()
    +
      903.     23        }).forEach(function (codeList) {
    +
      904.     23            let elemPrv = "";
    +
      905.     68            codeList.forEach(function (code) {
    +
      906.     68                let warnings;
    +
      907.     68                // Assert codeList is sorted.
    +
      908.     68                assertOrThrow(elemPrv < code, JSON.stringify([
    +
      909.     68                    elemPrv, code
    +
      910.     68                ], undefined, 4));
    +
      911.     68                elemPrv = code;
    +
      912.     68                [
    +
      913.     68                    jslint.jslint,
    +
      914.     68                    jslintCjs.jslint
    +
      915.    136                ].forEach(function (jslint) {
    +
      916.    136                    warnings = jslint(code, {
    +
      917.    136                        beta: true
    +
      918.    136                    }).warnings;
    +
      919.    136                    assertOrThrow(
    +
      920.    136                        warnings.length === 0,
    +
      921.    136                        JSON.stringify([code, warnings])
    +
      922.    136                    );
    +
      923.    136                });
    +
      924.     68            });
    +
      925.     23        });
    +
      926.      1    });
    +
      927.      1});
    +
      928.      1
    +
      929.      1jstestDescribe((
    +
      930.      1    "test jslint's option handling-behavior"
    +
      931.      1), function testBehaviorJslintOption() {
    +
      932.      1    let elemPrv = "";
    +
      933.      1    [
    +
      934.      1        [
    +
      935.      1            "let aa = aa | 0;", {bitwise: true}, []
    +
      936.      1        ], [
    +
      937.      1            ";\naa(new XMLHttpRequest());", {browser: true}, ["aa"]
    +
      938.      1        ], [
    +
      939.      1            "let aa = \"aa\" + 0;", {convert: true}, []
    +
      940.      1        ], [
    +
      941.      1            "registerType();", {couch: true}, []
    +
      942.      1        ], [
    +
      943.      1            "debugger;", {devel: true}, []
    +
      944.      1        ], [
    +
      945.      1
    +
      946.      1// PR-404 - Alias "evil" to jslint-directive "eval" for backwards-compat.
    +
      947.      1
    +
      948.      1            "new Function();\neval();", {eval: true, evil: true}, []
    +
      949.      1        ], [
    +
      950.      1            "let aa = () => 0;", {fart: true}, []
    +
      951.      1        ], [
    +
      952.      1            (
    +
      953.      1                "let aa = async (bb, {cc, dd}, [ee, ff], ...gg) => {\n"
    +
      954.      1                + "    bb += 1;\n"
    +
      955.      1                + "    return await (bb + cc + dd + ee + ff + gg);\n"
    +
      956.      1                + "};\n"
    +
      957.      1            ), {fart: true}, []
    +
      958.      1        ], [
    +
      959.      1            (
    +
      960.      1                "function aa(aa) {\n"
    +
      961.      1                + "    for (aa = 0; aa < 0; aa += 1) {\n"
    +
      962.      1                + "        aa();\n"
    +
      963.      1                + "    }\n"
    +
      964.      1                + "}\n"
    +
      965.      1            ), {for: true}, []
    +
      966.      1        ], [
    +
      967.      1            "let aa = {get aa() {\n    return;\n}};", {getset: true}, []
    +
      968.      1        ], [
    +
      969.      1            "let aa = {set aa(aa) {\n    return aa;\n}};", {getset: true}, []
    +
      970.      1        ], [
    +
      971.      1            sourceJslintMjs.replace((
    +
      972.      1                /    /g
    +
      973.      1            ), "  "), {indent2: true}, []
    +
      974.      1        ], [
    +
      975.      1            "function aa() {\n  return;\n}", {indent2: true}, []
    +
      976.      1        ], [
    +
      977.      1            "/".repeat(100), {long: true}, []
    +
      978.      1        ], [
    +
      979.      1
    +
      980.      1// PR-404 - Alias "nomen" to jslint-directive "name" for backwards-compat.
    +
      981.      1
    +
      982.      1            "let aa = aa._;", {name: true, nomen: true}, []
    +
      983.      1        ], [
    +
      984.      1            "require();", {node: true}, []
    +
      985.      1        ], [
    +
      986.      1            "let aa = 'aa';", {single: true}, []
    +
      987.      1        ], [
    +
      988.      1
    +
      989.      1// PR-404 - Add new directive "subscript" to play nice with Google Closure.
    +
      990.      1
    +
      991.      1            "aa[\"aa\"] = 1;", {subscript: true}, ["aa"]
    +
      992.      1        ], [
    +
      993.      1            "", {test_internal_error: true}, []
    +
      994.      1        ], [
    +
      995.      1            "let aa = this;", {this: true}, []
    +
      996.      1        ], [
    +
      997.      1            "", {trace: true}, []
    +
      998.      1        ], [
    +
      999.      1            (
    +
     1000.      1                "function aa({bb, aa}) {\n"
    +
     1001.      1                + "    switch (aa) {\n"
    +
     1002.      1                + "    case 1:\n"
    +
     1003.      1                + "        break;\n"
    +
     1004.      1                + "    case 0:\n"
    +
     1005.      1                + "        break;\n"
    +
     1006.      1                + "    default:\n"
    +
     1007.      1                + "        return {bb, aa};\n"
    +
     1008.      1                + "    }\n"
    +
     1009.      1                + "}\n"
    +
     1010.      1            ), {unordered: true}, []
    +
     1011.      1        ], [
    +
     1012.      1            "let {bb, aa} = 0;", {unordered: true}, []
    +
     1013.      1        ], [
    +
     1014.      1            (
    +
     1015.      1                "function aa() {\n"
    +
     1016.      1                + "    if (aa) {\n"
    +
     1017.      1                + "        let bb = 0;\n"
    +
     1018.      1                + "        return bb;\n"
    +
     1019.      1                + "    }\n"
    +
     1020.      1                + "}\n"
    +
     1021.      1            ), {variable: true}, []
    +
     1022.      1        ], [
    +
     1023.      1            "let bb = 0;\nlet aa = 0;", {variable: true}, []
    +
     1024.      1        ], [
    +
     1025.      1            "\t", {white: true}, []
    +
     1026.      1        ]
    +
     1027.     26    ].forEach(function ([
    +
     1028.     26        source, option_dict, global_list
    +
     1029.     26    ]) {
    +
     1030.     26        jstestIt((
    +
     1031.     26            `test option=${JSON.stringify(option_dict)} handling-behavior`
    +
     1032.     26        ), function () {
    +
     1033.     26            let elemNow = JSON.stringify([
    +
     1034.     26                option_dict, source, global_list
    +
     1035.     26            ]);
    +
     1036.     26            let warningsLength = (
    +
     1037.     26                option_dict.test_internal_error
    +
     1038.      1                ? 1
    +
     1039.     25                : 0
    +
     1040.     26            );
    +
     1041.     26            // Assert list is sorted.
    +
     1042.     26            assertOrThrow(elemPrv < elemNow, JSON.stringify([
    +
     1043.     26                elemPrv, elemNow
    +
     1044.     26            ], undefined, 4));
    +
     1045.     26            elemPrv = elemNow;
    +
     1046.     26            option_dict.beta = true;
    +
     1047.     26            [
    +
     1048.     26                jslint.jslint,
    +
     1049.     26                jslintCjs.jslint
    +
     1050.     52            ].forEach(function (jslint) {
    +
     1051.     52                // test jslint's option handling-behavior
    +
     1052.     52                assertOrThrow(
    +
     1053.     52                    jslint(
    +
     1054.     52                        source,
    +
     1055.     52                        option_dict,
    +
     1056.     52                        global_list
    +
     1057.     52                    ).warnings.length === warningsLength,
    +
     1058.     52                    "jslint.jslint(" + JSON.stringify([
    +
     1059.     52                        source, option_dict, global_list
    +
     1060.     52                    ]) + ")"
    +
     1061.     52                );
    +
     1062.     52                // test jslint's directive handling-behavior
    +
     1063.     52                source = (
    +
     1064.     52                    "/*jslint " + JSON.stringify(
    +
     1065.     52                        option_dict
    +
     1066.     52                    ).slice(1, -1).replace((
    +
     1067.     52                        /"/g
    +
     1068.     52                    ), "") + "*/\n"
    +
     1069.     52                    + (
    +
     1070.     52                        global_list.length === 0
    +
     1071.     48                        ? ""
    +
     1072.      4                        : "/*global " + global_list.join(",") + "*/\n"
    +
     1073.     52                    )
    +
     1074.     52                    + source.replace((
    +
     1075.     52                        /^#!/
    +
     1076.     52                    ), "//")
    +
     1077.     52                );
    +
     1078.     52                assertOrThrow(
    +
     1079.     52                    jslint(source).warnings.length === warningsLength,
    +
     1080.     52                    source
    +
     1081.     52                );
    +
     1082.     52            });
    +
     1083.     26        });
    +
     1084.     26    });
    +
     1085.      1});
    +
     1086.      1
    +
     1087.      1jstestDescribe((
    +
     1088.      1    "test jslint's warnings handling-behavior"
    +
     1089.      1), function testBehaviorJslintWarnings() {
    +
     1090.      1    jstestIt((
    +
     1091.      1        "test jslint's warning handling-behavior"
    +
     1092.      1    ), function () {
    +
     1093.      1
    +
     1094.      1// this function will validate each jslint <warning> is raised with given
    +
     1095.      1// malformed <code>
    +
     1096.      1
    +
     1097.      1        sourceJslintMjs.replace((
    +
     1098.      1            /(\n\s*?\/\/\s*?test_cause:\s*?)(\S[\S\s]*?\S)(\n\n\s*?) *?\S/g
    +
     1099.    328        ), function (match0, header, causeList, footer) {
    +
     1100.    328            let tmp;
    +
     1101.    328            // console.error(match0);
    +
     1102.    328            // Validate header.
    +
     1103.    328            assertOrThrow(header === "\n\n// test_cause:\n", match0);
    +
     1104.    328            // Validate footer.
    +
     1105.    328            assertOrThrow(footer === "\n\n", match0);
    +
     1106.    328            // Validate causeList.
    +
     1107.    328            causeList = causeList.replace((
    +
     1108.    328                /^\/\/ /gm
    +
     1109.    328            ), "").replace((
    +
     1110.    328                /^\["\n([\S\s]*?)\n"(,.*?)$/gm
    +
     1111.     43            ), function (ignore, source, param) {
    +
     1112.     43                source = "[" + JSON.stringify(source) + param;
    +
     1113.     43                assertOrThrow(source.length > (80 - 3), source);
    +
     1114.     43                return source;
    +
     1115.     43            }).replace((
    +
     1116.    328                / \/\/jslint-ignore-line$/gm
    +
     1117.    328            ), "");
    +
     1118.    505            tmp = causeList.split("\n").map(function (cause) {
    +
     1119.    505                return (
    +
     1120.    505                    "["
    +
     1121.   2525                    + JSON.parse(cause).map(function (elem) {
    +
     1122.   2525                        return JSON.stringify(elem);
    +
     1123.   2525                    }).join(", ")
    +
     1124.    505                    + "]"
    +
     1125.    505                );
    +
     1126.    505            }).sort().join("\n");
    +
     1127.    328            assertOrThrow(
    +
     1128.    328                causeList === tmp,
    +
     1129.    328                "\n" + causeList + "\n\n" + tmp
    +
     1130.    328            );
    +
     1131.    505            causeList.split("\n").forEach(function (cause) {
    +
     1132.    505                cause = JSON.parse(cause);
    +
     1133.    505                tmp = jslint.jslint(cause[0], {
    +
     1134.    505                    beta: true,
    +
     1135.    505                    test_cause: true
    +
     1136.    505                }).causes;
    +
     1137.    505                // Validate cause.
    +
     1138.    505                assertOrThrow(
    +
     1139.    505                    tmp[JSON.stringify(cause.slice(1))],
    +
     1140.    505                    (
    +
     1141.    505                        "\n" + JSON.stringify(cause) + "\n\n"
    +
     1142.    505                        + Object.keys(tmp).sort().join("\n")
    +
     1143.    505                    )
    +
     1144.    505                );
    +
     1145.    505            });
    +
     1146.    328            return "";
    +
     1147.    328        });
    +
     1148.      1    });
    +
     1149.      1});
    +
     1150.      1
    +
     1151.      1jstestDescribe((
    +
     1152.      1    "test jstestXxx handling-behavior"
    +
     1153.      1), function testBehaviorJstestXxx() {
    +
     1154.      1    jstestIt((
    +
     1155.      1        "test jstestDescribe error handling-behavior"
    +
     1156.      1    ), function () {
    +
     1157.      1        throw new Error();
    +
     1158.      1    }, "pass");
    +
     1159.      1    jstestIt((
    +
     1160.      1        "test jstestOnExit tests-failed handling-behavior"
    +
     1161.      1    ), function () {
    +
     1162.      1        jstestOnExit(undefined, "testsFailed");
    +
     1163.      1    });
    +
     1164.      1});
    +
     1165.      1
    +
     1166.      1jstestDescribe((
    +
     1167.      1    "test misc handling-behavior"
    +
     1168.      1), function testBehaviorMisc() {
    +
     1169.      1    jstestIt((
    +
     1170.      1        "test misc handling-behavior"
    +
     1171.      1    ), async function () {
    +
     1172.      1        // test debugInline handling-behavior
    +
     1173.      1        noop(debugInline);
    +
     1174.      1        // test assertErrorThrownAsync error handling-behavior
    +
     1175.      1        await assertErrorThrownAsync(function () {
    +
     1176.      1            return assertErrorThrownAsync(noop);
    +
     1177.      1        });
    +
     1178.      1        // test assertJsonEqual error handling-behavior
    +
     1179.      1        await assertErrorThrownAsync(function () {
    +
     1180.      1            assertJsonEqual(1, 2);
    +
     1181.      1        });
    +
     1182.      1        await assertErrorThrownAsync(function () {
    +
     1183.      1            assertJsonEqual(1, 2, "undefined");
    +
     1184.      1        });
    +
     1185.      1        await assertErrorThrownAsync(function () {
    +
     1186.      1            assertJsonEqual(1, 2, {});
    +
     1187.      1        });
    +
     1188.      1        // test assertOrThrow error handling-behavior
    +
     1189.      1        await assertErrorThrownAsync(function () {
    +
     1190.      1            assertOrThrow(undefined, "undefined");
    +
     1191.      1        });
    +
     1192.      1        await assertErrorThrownAsync(function () {
    +
     1193.      1            assertOrThrow(undefined, new Error());
    +
     1194.      1        });
    +
     1195.      1    });
    +
     1196.      1});
    +
     1197.      1
    +
     1198.      1jstestDescribe((
    +
     1199.      1    "test v8CoverageListMerge handling-behavior"
    +
     1200.      1), function testBehaviorV8CoverageListMerge() {
    +
     1201.      1    let functionsInput = JSON.stringify([
    +
     1202.      1        {
    +
     1203.      1            functionName: "test",
    +
     1204.      1            isBlockCoverage: true,
    +
     1205.      1            ranges: [
    +
     1206.      1                {
    +
     1207.      1                    count: 2,
    +
     1208.      1                    endOffset: 4,
    +
     1209.      1                    startOffset: 0
    +
     1210.      1                },
    +
     1211.      1                {
    +
     1212.      1                    count: 1,
    +
     1213.      1                    endOffset: 2,
    +
     1214.      1                    startOffset: 1
    +
     1215.      1                },
    +
     1216.      1                {
    +
     1217.      1                    count: 1,
    +
     1218.      1                    endOffset: 3,
    +
     1219.      1                    startOffset: 2
    +
     1220.      1                }
    +
     1221.      1            ]
    +
     1222.      1        }
    +
     1223.      1    ]);
    +
     1224.      1    jstestIt((
    +
     1225.      1        "accepts empty arrays for `v8CoverageListMerge`"
    +
     1226.      1    ), function () {
    +
     1227.      1        assertJsonEqual(v8CoverageListMerge([]), {
    +
     1228.      1            result: []
    +
     1229.      1        });
    +
     1230.      1    });
    +
     1231.      1    jstestIt((
    +
     1232.      1        "funcCovs.length === 1"
    +
     1233.      1    ), function () {
    +
     1234.      1        assertJsonEqual(v8CoverageListMerge([
    +
     1235.      1            {
    +
     1236.      1                result: [
    +
     1237.      1                    {
    +
     1238.      1                        functions: [
    +
     1239.      1                            {
    +
     1240.      1                                functionName: "test",
    +
     1241.      1                                isBlockCoverage: true,
    +
     1242.      1                                ranges: [
    +
     1243.      1                                    {
    +
     1244.      1                                        count: 2,
    +
     1245.      1                                        endOffset: 4,
    +
     1246.      1                                        startOffset: 0
    +
     1247.      1                                    }
    +
     1248.      1                                ]
    +
     1249.      1                            }
    +
     1250.      1                        ],
    +
     1251.      1                        moduleUrl: "/lib.js",
    +
     1252.      1                        scriptId: "1"
    +
     1253.      1                    }
    +
     1254.      1                ]
    +
     1255.      1            },
    +
     1256.      1            {
    +
     1257.      1                result: [
    +
     1258.      1                    {
    +
     1259.      1                        functions: [],
    +
     1260.      1                        moduleUrl: "/lib.js",
    +
     1261.      1                        scriptId: "2"
    +
     1262.      1                    }
    +
     1263.      1                ]
    +
     1264.      1            }
    +
     1265.      1        ]), {
    +
     1266.      1            result: [
    +
     1267.      1                {
    +
     1268.      1                    functions: [
    +
     1269.      1                        {
    +
     1270.      1                            functionName: "test",
    +
     1271.      1                            isBlockCoverage: true,
    +
     1272.      1                            ranges: [
    +
     1273.      1                                {
    +
     1274.      1                                    count: 2,
    +
     1275.      1                                    endOffset: 4,
    +
     1276.      1                                    startOffset: 0
    +
     1277.      1                                }
    +
     1278.      1                            ]
    +
     1279.      1                        }
    +
     1280.      1                    ],
    +
     1281.      1                    scriptId: "0"
    +
     1282.      1                }
    +
     1283.      1            ]
    +
     1284.      1        });
    +
     1285.      1    });
    +
     1286.      1    jstestIt((
    +
     1287.      1        "accepts arrays with a single item for `v8CoverageListMerge`"
    +
     1288.      1    ), function () {
    +
     1289.      1        assertJsonEqual(v8CoverageListMerge([
    +
     1290.      1            {
    +
     1291.      1                result: [
    +
     1292.      1                    {
    +
     1293.      1                        functions: JSON.parse(functionsInput),
    +
     1294.      1                        moduleUrl: "/lib.js",
    +
     1295.      1                        scriptId: "123"
    +
     1296.      1                    }
    +
     1297.      1                ]
    +
     1298.      1            }
    +
     1299.      1        ]), {
    +
     1300.      1            result: [
    +
     1301.      1                {
    +
     1302.      1                    functions: [
    +
     1303.      1                        {
    +
     1304.      1                            functionName: "test",
    +
     1305.      1                            isBlockCoverage: true,
    +
     1306.      1                            ranges: [
    +
     1307.      1                                {
    +
     1308.      1                                    count: 2,
    +
     1309.      1                                    endOffset: 4,
    +
     1310.      1                                    startOffset: 0
    +
     1311.      1                                },
    +
     1312.      1                                {
    +
     1313.      1                                    count: 1,
    +
     1314.      1                                    endOffset: 3,
    +
     1315.      1                                    startOffset: 1
    +
     1316.      1                                }
    +
     1317.      1                            ]
    +
     1318.      1                        }
    +
     1319.      1                    ],
    +
     1320.      1                    moduleUrl: "/lib.js",
    +
     1321.      1                    scriptId: "0"
    +
     1322.      1                }
    +
     1323.      1            ]
    +
     1324.      1        });
    +
     1325.      1    });
    +
     1326.      1    jstestIt((
    +
     1327.      1        "accepts arrays with two identical items for"
    +
     1328.      1        + " `v8CoverageListMerge`"
    +
     1329.      1    ), function () {
    +
     1330.      1        assertJsonEqual(v8CoverageListMerge([
    +
     1331.      1            {
    +
     1332.      1                result: [
    +
     1333.      1                    {
    +
     1334.      1                        functions: JSON.parse(functionsInput),
    +
     1335.      1                        scriptId: "123",
    +
     1336.      1                        url: "/lib.js"
    +
     1337.      1                    }, {
    +
     1338.      1                        functions: JSON.parse(functionsInput),
    +
     1339.      1                        scriptId: "123",
    +
     1340.      1                        url: "/lib.js"
    +
     1341.      1                    }
    +
     1342.      1                ]
    +
     1343.      1            }
    +
     1344.      1        ]), {
    +
     1345.      1            result: [
    +
     1346.      1                {
    +
     1347.      1                    functions: [
    +
     1348.      1                        {
    +
     1349.      1                            functionName: "test",
    +
     1350.      1                            isBlockCoverage: true,
    +
     1351.      1                            ranges: [
    +
     1352.      1                                {
    +
     1353.      1                                    count: 4,
    +
     1354.      1                                    endOffset: 4,
    +
     1355.      1                                    startOffset: 0
    +
     1356.      1                                },
    +
     1357.      1                                {
    +
     1358.      1                                    count: 2,
    +
     1359.      1                                    endOffset: 3,
    +
     1360.      1                                    startOffset: 1
    +
     1361.      1                                }
    +
     1362.      1                            ]
    +
     1363.      1                        }
    +
     1364.      1                    ],
    +
     1365.      1                    scriptId: "0",
    +
     1366.      1                    url: "/lib.js"
    +
     1367.      1                }
    +
     1368.      1            ]
    +
     1369.      1        });
    +
     1370.      1    });
    +
     1371.      1    [
    +
     1372.      1        "test_coverage_merge_is_block_coverage_test.json",
    +
     1373.      1        "test_coverage_merge_issue_2_mixed_is_block_coverage_test.json",
    +
     1374.      1        "test_coverage_merge_node_10_internal_errors_one_of_test.json",
    +
     1375.      1        "test_coverage_merge_reduced_test.json",
    +
     1376.      1        "test_coverage_merge_simple_test.json",
    +
     1377.      1        "test_coverage_merge_various_test.json"
    +
     1378.      6    ].forEach(function (file) {
    +
     1379.      6        jstestIt(file, function () {
    +
     1380.      6            file = testCoverageMergeData[file];
    +
     1381.     84            file.forEach(function ({
    +
     1382.     84                expected,
    +
     1383.     84                inputs
    +
     1384.     84            }) {
    +
     1385.     84                assertJsonEqual(v8CoverageListMerge(inputs), expected);
    +
     1386.     84            });
    +
     1387.      6        });
    +
     1388.      6    });
    +
     1389.      1    jstestIt((
    +
     1390.      1        "merge multiple node-sqlite coverage files"
    +
     1391.      1    ), function () {
    +
     1392.      1        let data1 = [
    +
     1393.      1            "test_v8_coverage_node_sqlite_9884_1633662346346_0.json",
    +
     1394.      1            "test_v8_coverage_node_sqlite_13216_1633662333140_0.json"
    +
     1395.      2        ].map(function (file) {
    +
     1396.      2            return testCoverageMergeData[file];
    +
     1397.      2        });
    +
     1398.      1        let data2 = testCoverageMergeData[
    +
     1399.      1            "test_v8_coverage_node_sqlite_merged.json"
    +
     1400.      1        ];
    +
     1401.      1        data1 = v8CoverageListMerge(data1);
    +
     1402.      1        data1 = v8CoverageListMerge([data1]);
    +
     1403.      1
    +
     1404.      1// Debug data1.
    +
     1405.      1// await moduleFs.promises.writeFile(
    +
     1406.      1//     ".test_v8_coverage_node_sqlite_merged.json",
    +
     1407.      1//     JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n"
    +
     1408.      1// );
    +
     1409.      1
    +
     1410.      1        assertJsonEqual(data1, data2);
    +
     1411.      1    });
    +
     1412.      1});
    +
     1413.      1
    +
     1414.      1jstestDescribe((
    +
     1415.      1    "test v8CoverageReportCreate handling-behavior"
    +
     1416.      1), function testBehaviorV8CoverageReportCreate() {
    +
     1417.      1    jstestIt((
    +
     1418.      1        "test null-case handling-behavior"
    +
     1419.      1    ), async function () {
    +
     1420.      1        await assertErrorThrownAsync(function () {
    +
     1421.      1            return v8CoverageReportCreate({});
    +
     1422.      1        }, "invalid coverageDir");
    +
     1423.      1    });
    +
     1424.      1    jstestIt((
    +
     1425.      1        "test coverage-report jslint.mjs handling-behavior"
    +
     1426.      1    ), async function () {
    +
     1427.      1        // test remove-old-coverage handling-behavior
    +
     1428.      1        await fsWriteFileWithParents(
    +
     1429.      1            ".tmp/coverage_jslint/coverage-0-0-0.json",
    +
     1430.      1            ""
    +
     1431.      1        );
    +
     1432.      1        await jslint.jslint_cli({
    +
     1433.      1            console_error: noop, // comment to debug
    +
     1434.      1            mode_cli: true,
    +
     1435.      1            process_argv: [
    +
     1436.      1                "node", "jslint.mjs",
    +
     1437.      1                "v8_coverage_report=.tmp/coverage_jslint",
    +
     1438.      1                "--exclude=aa.js",
    +
     1439.      1                "--include-node-modules=1",
    +
     1440.      1                "--include=jslint.mjs",
    +
     1441.      1                "node", "jslint.mjs"
    +
     1442.      1            ]
    +
     1443.      1        });
    +
     1444.      1    });
    +
     1445.      1    [
    +
     1446.      1        [
    +
     1447.      1            "v8CoverageReportCreate_high.js", (
    +
     1448.      1                "switch(0){\n"
    +
     1449.      1                + "case 0:break;\n"
    +
     1450.      1                + "}\n"
    +
     1451.      1            )
    +
     1452.      1        ], [
    +
     1453.      1            "v8CoverageReportCreate_ignore.js", (
    +
     1454.      1                "/*coverage-ignore-file*/\n"
    +
     1455.      1                + "switch(0){\n"
    +
     1456.      1                + "case 0:break;\n"
    +
     1457.      1                + "}\n"
    +
     1458.      1            )
    +
     1459.      1        ], [
    +
     1460.      1            "v8CoverageReportCreate_low.js", (
    +
     1461.      1                "switch(0){\n"
    +
     1462.      1                + "case 1:break;\n"
    +
     1463.      1                + "case 2:break;\n"
    +
     1464.      1                + "case 3:break;\n"
    +
     1465.      1                + "case 4:break;\n"
    +
     1466.      1                + "}\n"
    +
     1467.      1            )
    +
     1468.      1        ], [
    +
     1469.      1            "v8CoverageReportCreate_medium.js", (
    +
     1470.      1                "switch(0){\n"
    +
     1471.      1                + "case 0:break;\n"
    +
     1472.      1                + "case 1:break;\n"
    +
     1473.      1                + "case 2:break;\n"
    +
     1474.      1                + "}\n"
    +
     1475.      1            )
    +
     1476.      1        ]
    +
     1477.      4    ].forEach(function ([
    +
     1478.      4        file, data
    +
     1479.      4    ], ii) {
    +
     1480.      4        jstestIt(file, async function () {
    +
     1481.      4            let dir = ".tmp/coverage_" + ii + "/";
    +
     1482.      4            file = dir + file;
    +
     1483.      4            await fsWriteFileWithParents(file, data);
    +
     1484.      4            await jslint.jslint_cli({
    +
     1485.      4                console_error: noop, // comment to debug
    +
     1486.      4                mode_cli: true,
    +
     1487.      4                process_argv: [
    +
     1488.      4                    "node", "jslint.mjs",
    +
     1489.      4                    "v8_coverage_report=" + dir,
    +
     1490.      4                    "node",
    +
     1491.      4                    file
    +
     1492.      4                ]
    +
     1493.      4            });
    +
     1494.      4        });
    +
     1495.      4    });
    +
     1496.      1    jstestIt((
    +
     1497.      1        "test npm handling-behavior"
    +
     1498.      1    ), async function () {
    +
     1499.      1        await jslint.jslint_cli({
    +
     1500.      1            console_error: noop, // comment to debug
    +
     1501.      1            mode_cli: true,
    +
     1502.      1            process_argv: [
    +
     1503.      1                "node", "jslint.mjs",
    +
     1504.      1                "v8_coverage_report=.tmp/coverage_npm",
    +
     1505.      1                "npm", "--version"
    +
     1506.      1            ]
    +
     1507.      1        });
    +
     1508.      1    });
    +
     1509.      1    jstestIt((
    +
     1510.      1        "test misc handling-behavior"
    +
     1511.      1    ), async function () {
    +
     1512.      1        await Promise.all([
    +
     1513.      1            [
    +
     1514.      1                ".tmp/coverage_misc/aa.js", "\n".repeat(0x100)
    +
     1515.      1            ], [
    +
     1516.      1                ".tmp/coverage_misc/coverage-0-0-0.json", JSON.stringify({
    +
     1517.      1                    "result": [
    +
     1518.      1                        {
    +
     1519.      1                            "functions": [
    +
     1520.      1                                {
    +
     1521.      1                                    "functionName": "",
    +
     1522.      1                                    "isBlockCoverage": true,
    +
     1523.      1                                    "ranges": [
    +
     1524.      1                                        {
    +
     1525.      1                                            "count": 1,
    +
     1526.      1                                            "endOffset": 0xf0,
    +
     1527.      1                                            "startOffset": 0x10
    +
     1528.      1                                        },
    +
     1529.      1                                        {
    +
     1530.      1                                            "count": 1,
    +
     1531.      1                                            "endOffset": 0x40,
    +
     1532.      1                                            "startOffset": 0x20
    +
     1533.      1                                        },
    +
     1534.      1                                        {
    +
     1535.      1                                            "count": 1,
    +
     1536.      1                                            "endOffset": 0x80,
    +
     1537.      1                                            "startOffset": 0x60
    +
     1538.      1                                        },
    +
     1539.      1                                        {
    +
     1540.      1                                            "count": 0,
    +
     1541.      1                                            "endOffset": 0x45,
    +
     1542.      1                                            "startOffset": 0x25
    +
     1543.      1                                        },
    +
     1544.      1                                        {
    +
     1545.      1                                            "count": 0,
    +
     1546.      1                                            "endOffset": 0x85,
    +
     1547.      1                                            "startOffset": 0x65
    +
     1548.      1                                        }
    +
     1549.      1                                    ]
    +
     1550.      1                                }
    +
     1551.      1                            ],
    +
     1552.      1                            "scriptId": "0",
    +
     1553.      1                            "url": "file:///" + modulePath.resolve(
    +
     1554.      1                                ".tmp/coverage_misc/aa.js"
    +
     1555.      1                            )
    +
     1556.      1                        }
    +
     1557.      1                    ]
    +
     1558.      1                }, undefined, 4)
    +
     1559.      1            ]
    +
     1560.      2        ].map(async function ([
    +
     1561.      2            file, data
    +
     1562.      2        ]) {
    +
     1563.      2            await fsWriteFileWithParents(file, data);
    +
     1564.      2        }));
    +
     1565.      1        await jslint.jslint_cli({
    +
     1566.      1            console_error: noop, // comment to debug
    +
     1567.      1            mode_cli: true,
    +
     1568.      1            process_argv: [
    +
     1569.      1                "node", "jslint.mjs",
    +
     1570.      1                "v8_coverage_report=.tmp/coverage_misc"
    +
     1571.      1                // "node", ".tmp/coverage_misc/aa.js"
    +
     1572.      1            ]
    +
     1573.      1        });
    +
     1574.      1    });
    +
     1575.      1});
    +
     1576.      1
    +
    + + + + diff --git a/branch-beta/.artifact/coverage/touch.txt b/branch-beta/.artifact/coverage/touch.txt new file mode 100644 index 000000000..e69de29bb diff --git a/branch-beta/.artifact/coverage_sqlite3_js/coverage_badge.svg b/branch-beta/.artifact/coverage_sqlite3_js/coverage_badge.svg new file mode 100644 index 000000000..8e28d5ff5 --- /dev/null +++ b/branch-beta/.artifact/coverage_sqlite3_js/coverage_badge.svg @@ -0,0 +1,14 @@ + + + + +coverage +97.62 % + + diff --git a/branch-beta/.artifact/coverage_sqlite3_js/coverage_report.txt b/branch-beta/.artifact/coverage_sqlite3_js/coverage_report.txt new file mode 100644 index 000000000..b19e8684a --- /dev/null +++ b/branch-beta/.artifact/coverage_sqlite3_js/coverage_report.txt @@ -0,0 +1,16 @@ +V8 Coverage Report ++----------------------------------+-------------------+-------------------+ +| Files covered | Lines | Remaining | ++----------------------------------+-------------------+-------------------+ +| ./ | 97.62 % | | +| *******************************_ | 247 / 253 | 6 / 253 | ++----------------------------------+-------------------+-------------------+ +| ./lib/sqlite3-binding.js | 100.00 % | | +| ******************************** | 6 / 6 | 0 / 6 | ++----------------------------------+-------------------+-------------------+ +| ./lib/sqlite3.js | 97.11 % | | +| *******************************_ | 202 / 208 | 6 / 208 | ++----------------------------------+-------------------+-------------------+ +| ./lib/trace.js | 100.00 % | | +| ******************************** | 39 / 39 | 0 / 39 | ++----------------------------------+-------------------+-------------------+ diff --git a/branch-beta/.artifact/coverage_sqlite3_js/index.html b/branch-beta/.artifact/coverage_sqlite3_js/index.html new file mode 100644 index 000000000..83d95a7bd --- /dev/null +++ b/branch-beta/.artifact/coverage_sqlite3_js/index.html @@ -0,0 +1,212 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . /
    +
    +
    +
    +
    + 97.62 %
    + 247 / 253 +
    +
    + 6 / 253 +
    + . / lib/sqlite3-binding.js
    +
    +
    +
    +
    + 100.00 %
    + 6 / 6 +
    +
    + 0 / 6 +
    + . / lib/sqlite3.js
    +
    +
    +
    +
    + 97.11 %
    + 202 / 208 +
    +
    + 6 / 208 +
    + . / lib/trace.js
    +
    +
    +
    +
    + 100.00 %
    + 39 / 39 +
    +
    + 0 / 39 +
    +
    + + + + diff --git a/branch-beta/.artifact/coverage_sqlite3_js/lib/sqlite3-binding.js.html b/branch-beta/.artifact/coverage_sqlite3_js/lib/sqlite3-binding.js.html new file mode 100644 index 000000000..d3fec08a6 --- /dev/null +++ b/branch-beta/.artifact/coverage_sqlite3_js/lib/sqlite3-binding.js.html @@ -0,0 +1,174 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / lib/sqlite3-binding.js
    +
    +
    +
    +
    + 100.00 %
    + 6 / 6 +
    +
    + 0 / 6 +
    +
    + + +
    +
        1.      2const binary = require('@mapbox/node-pre-gyp');
    +
        2.      2const path = require('path');
    +
        3.      2const binding_path = binary.find(path.resolve(path.join(__dirname,'../package.json')));
    +
        4.      2const binding = require(binding_path);
    +
        5.      2module.exports = exports = binding;
    +
        6.      2
    +
    + + + + diff --git a/branch-beta/.artifact/coverage_sqlite3_js/lib/sqlite3.js.html b/branch-beta/.artifact/coverage_sqlite3_js/lib/sqlite3.js.html new file mode 100644 index 000000000..220bcd4a5 --- /dev/null +++ b/branch-beta/.artifact/coverage_sqlite3_js/lib/sqlite3.js.html @@ -0,0 +1,376 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / lib/sqlite3.js
    +
    +
    +
    +
    + 97.11 %
    + 202 / 208 +
    +
    + 6 / 208 +
    +
    + + +
    +
        1.      2const path = require('path');
    +
        2.      2const sqlite3 = require('./sqlite3-binding.js');
    +
        3.      2const EventEmitter = require('events').EventEmitter;
    +
        4.      2module.exports = exports = sqlite3;
    +
        5.      2
    +
        6.     12function normalizeMethod (fn) {
    +
        7.   4203    return function (sql) {
    +
        8.   4203        let errBack;
    +
        9.   4203        const args = Array.prototype.slice.call(arguments, 1);
    +
       10.   4203
    +
       11.   1119        if (typeof args[args.length - 1] === 'function') {
    +
       12.   1119            const callback = args[args.length - 1];
    +
       13.   1119            errBack = function(err) {
    +
       14.   1119                if (err) {
    +
       15.   1119                    callback(err);
    +
       16.   1119                }
    +
       17.   1119            };
    +
       18.   1119        }
    +
       19.   4203        const statement = new Statement(this, sql, errBack);
    +
       20.   4203        return fn.call(this, statement, args);
    +
       21.   4203    };
    +
       22.     12}
    +
       23.      2
    +
       24.      6function inherits(target, source) {
    +
       25.      6    for (const k in source.prototype)
    +
       26.    108        target.prototype[k] = source.prototype[k];
    +
       27.      6}
    +
       28.      2
    +
       29.      2sqlite3.cached = {
    +
       30.      4    Database: function(file, a, b) {
    +
       31.     -0        if (file === '' || file === ':memory:') {
    +
       32.     -0            // Don't cache special databases.
    +
       33.     -0            return new Database(file, a, b);
    +
       34.     -0        }
    +
       35.      4
    +
       36.      4        let db;
    +
       37.      4        file = path.resolve(file);
    +
       38.      4
    +
       39.      2        if (!sqlite3.cached.objects[file]) {
    +
       40.      2            db = sqlite3.cached.objects[file] = new Database(file, a, b);
    +
       41.      2        }
    +
       42.      2        else {
    +
       43.      2            // Make sure the callback is called.
    +
       44.      2            db = sqlite3.cached.objects[file];
    +
       45.     -0            const callback = (typeof a === 'number') ? b : a;
    +
       46.      2            if (typeof callback === 'function') {
    +
       47.      2                function cb() { callback.call(db, null); }
    +
       48.      2                if (db.open) process.nextTick(cb);
    +
       49.      2                else db.once('open', cb);
    +
       50.      2            }
    +
       51.      2        }
    +
       52.      4
    +
       53.      4        return db;
    +
       54.      4    },
    +
       55.      2    objects: {}
    +
       56.      2};
    +
       57.      2
    +
       58.      2
    +
       59.      2const Database = sqlite3.Database;
    +
       60.      2const Statement = sqlite3.Statement;
    +
       61.      2const Backup = sqlite3.Backup;
    +
       62.      2
    +
       63.      2inherits(Database, EventEmitter);
    +
       64.      2inherits(Statement, EventEmitter);
    +
       65.      2inherits(Backup, EventEmitter);
    +
       66.      2
    +
       67.      2// Database#prepare(sql, [bind1, bind2, ...], [callback])
    +
       68.   2076Database.prototype.prepare = normalizeMethod(function(statement, params) {
    +
       69.   2076    return params.length
    +
       70.      7        ? statement.bind.apply(statement, params)
    +
       71.   2069        : statement;
    +
       72.   2076});
    +
       73.      2
    +
       74.      2// Database#run(sql, [bind1, bind2, ...], [callback])
    +
       75.   2074Database.prototype.run = normalizeMethod(function(statement, params) {
    +
       76.   2074    statement.run.apply(statement, params).finalize();
    +
       77.   2074    return this;
    +
       78.   2074});
    +
       79.      2
    +
       80.      2// Database#get(sql, [bind1, bind2, ...], [callback])
    +
       81.     35Database.prototype.get = normalizeMethod(function(statement, params) {
    +
       82.     35    statement.get.apply(statement, params).finalize();
    +
       83.     35    return this;
    +
       84.     35});
    +
       85.      2
    +
       86.      2// Database#all(sql, [bind1, bind2, ...], [callback])
    +
       87.      9Database.prototype.all = normalizeMethod(function(statement, params) {
    +
       88.      9    statement.all.apply(statement, params).finalize();
    +
       89.      9    return this;
    +
       90.      9});
    +
       91.      2
    +
       92.      2// Database#each(sql, [bind1, bind2, ...], [callback], [complete])
    +
       93.      7Database.prototype.each = normalizeMethod(function(statement, params) {
    +
       94.      7    statement.each.apply(statement, params).finalize();
    +
       95.      7    return this;
    +
       96.      7});
    +
       97.      2
    +
       98.      2Database.prototype.map = normalizeMethod(function(statement, params) {
    +
       99.      2    statement.map.apply(statement, params).finalize();
    +
      100.      2    return this;
    +
      101.      2});
    +
      102.      2
    +
      103.      2// Database#backup(filename, [callback])
    +
      104.      2// Database#backup(filename, destName, sourceName, filenameIsDest, [callback])
    +
      105.     15Database.prototype.backup = function() {
    +
      106.     15    let backup;
    +
      107.     13    if (arguments.length <= 2) {
    +
      108.     13        // By default, we write the main database out to the main database of the named file.
    +
      109.     13        // This is the most likely use of the backup api.
    +
      110.     13        backup = new Backup(this, arguments[0], 'main', 'main', true, arguments[1]);
    +
      111.     13    } else {
    +
      112.      2        // Otherwise, give the user full control over the sqlite3_backup_init arguments.
    +
      113.      2        backup = new Backup(this, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]);
    +
      114.      2    }
    +
      115.     15    // Per the sqlite docs, exclude the following errors as non-fatal by default.
    +
      116.     15    backup.retryErrors = [sqlite3.BUSY, sqlite3.LOCKED];
    +
      117.     15    return backup;
    +
      118.     15};
    +
      119.      2
    +
      120.      2Statement.prototype.map = function() {
    +
      121.      2    const params = Array.prototype.slice.call(arguments);
    +
      122.      2    const callback = params.pop();
    +
      123.      2    params.push(function(err, rows) {
    +
      124.     -0        if (err) return callback(err);
    +
      125.      2        const result = {};
    +
      126.      2        if (rows.length) {
    +
      127.      2            const keys = Object.keys(rows[0]);
    +
      128.      2            const key = keys[0];
    +
      129.      1            if (keys.length > 2) {
    +
      130.      1                // Value is an object
    +
      131.      5                for (let i = 0; i < rows.length; i++) {
    +
      132.      5                    result[rows[i][key]] = rows[i];
    +
      133.      5                }
    +
      134.      1            } else {
    +
      135.      1                const value = keys[1];
    +
      136.      1                // Value is a plain value
    +
      137.      5                for (let i = 0; i < rows.length; i++) {
    +
      138.      5                    result[rows[i][key]] = rows[i][value];
    +
      139.      5                }
    +
      140.      1            }
    +
      141.      2        }
    +
      142.      2        callback(err, result);
    +
      143.      2    });
    +
      144.      2    return this.all.apply(this, params);
    +
      145.      2};
    +
      146.      2
    +
      147.      2let isVerbose = false;
    +
      148.      2
    +
      149.      2const supportedEvents = [ 'trace', 'profile', 'insert', 'update', 'delete' ];
    +
      150.      2
    +
      151.      7Database.prototype.addListener = Database.prototype.on = function(type) {
    +
      152.      7    const val = EventEmitter.prototype.addListener.apply(this, arguments);
    +
      153.      4    if (supportedEvents.indexOf(type) >= 0) {
    +
      154.      4        this.configure(type, true);
    +
      155.      4    }
    +
      156.      7    return val;
    +
      157.      7};
    +
      158.      2
    +
      159.      2Database.prototype.removeListener = function(type) {
    +
      160.      2    const val = EventEmitter.prototype.removeListener.apply(this, arguments);
    +
      161.      1    if (supportedEvents.indexOf(type) >= 0 && !this._events[type]) {
    +
      162.      1        this.configure(type, false);
    +
      163.      1    }
    +
      164.      2    return val;
    +
      165.      2};
    +
      166.      2
    +
      167.      1Database.prototype.removeAllListeners = function(type) {
    +
      168.      1    const val = EventEmitter.prototype.removeAllListeners.apply(this, arguments);
    +
      169.      1    if (supportedEvents.indexOf(type) >= 0) {
    +
      170.      1        this.configure(type, false);
    +
      171.      1    }
    +
      172.      1    return val;
    +
      173.      1};
    +
      174.      2
    +
      175.      2// Save the stack trace over EIO callbacks.
    +
      176.      1sqlite3.verbose = function() {
    +
      177.      1    if (!isVerbose) {
    +
      178.      1        const trace = require('./trace');
    +
      179.      1        [
    +
      180.      1            'prepare',
    +
      181.      1            'get',
    +
      182.      1            'run',
    +
      183.      1            'all',
    +
      184.      1            'each',
    +
      185.      1            'map',
    +
      186.      1            'close',
    +
      187.      1            'exec'
    +
      188.      8        ].forEach(function (name) {
    +
      189.      8            trace.extendTrace(Database.prototype, name);
    +
      190.      8        });
    +
      191.      1        [
    +
      192.      1            'bind',
    +
      193.      1            'get',
    +
      194.      1            'run',
    +
      195.      1            'all',
    +
      196.      1            'each',
    +
      197.      1            'map',
    +
      198.      1            'reset',
    +
      199.      1            'finalize',
    +
      200.      8        ].forEach(function (name) {
    +
      201.      8            trace.extendTrace(Statement.prototype, name);
    +
      202.      8        });
    +
      203.      1        isVerbose = true;
    +
      204.      1    }
    +
      205.      1
    +
      206.      1    return this;
    +
      207.      1};
    +
      208.      2
    +
    + + + + diff --git a/branch-beta/.artifact/coverage_sqlite3_js/lib/trace.js.html b/branch-beta/.artifact/coverage_sqlite3_js/lib/trace.js.html new file mode 100644 index 000000000..28608da6d --- /dev/null +++ b/branch-beta/.artifact/coverage_sqlite3_js/lib/trace.js.html @@ -0,0 +1,207 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / lib/trace.js
    +
    +
    +
    +
    + 100.00 %
    + 39 / 39 +
    +
    + 0 / 39 +
    +
    + + +
    +
        1.      1// Inspired by https://github.com/tlrobinson/long-stack-traces
    +
        2.      1const util = require('util');
    +
        3.      1
    +
        4.     16function extendTrace(object, property, pos) {
    +
        5.     16    const old = object[property];
    +
        6.      1    object[property] = function() {
    +
        7.      1        const error = new Error();
    +
        8.      1        const name = object.constructor.name + '#' + property + '(' +
    +
        9.      2            Array.prototype.slice.call(arguments).map(function(el) {
    +
       10.      2                return util.inspect(el, false, 0);
    +
       11.      2            }).join(', ') + ')';
    +
       12.      1
    +
       13.      1        if (typeof pos === 'undefined') pos = -1;
    +
       14.      1        if (pos < 0) pos += arguments.length;
    +
       15.      1        const cb = arguments[pos];
    +
       16.      1        if (typeof arguments[pos] === 'function') {
    +
       17.      1            arguments[pos] = function replacement() {
    +
       18.      1                const err = arguments[0];
    +
       19.      1                if (err && err.stack && !err.__augmented) {
    +
       20.      1                    err.stack = filter(err).join('\n');
    +
       21.      1                    err.stack += '\n--> in ' + name;
    +
       22.      1                    err.stack += '\n' + filter(error).slice(1).join('\n');
    +
       23.      1                    err.__augmented = true;
    +
       24.      1                }
    +
       25.      1                return cb.apply(this, arguments);
    +
       26.      1            };
    +
       27.      1        }
    +
       28.      1        return old.apply(this, arguments);
    +
       29.      1    };
    +
       30.     16}
    +
       31.      1exports.extendTrace = extendTrace;
    +
       32.      1
    +
       33.      1
    +
       34.      2function filter(error) {
    +
       35.     13    return error.stack.split('\n').filter(function(line) {
    +
       36.     13        return line.indexOf(__filename) < 0;
    +
       37.     13    });
    +
       38.      2}
    +
       39.      1
    +
    + + + + diff --git a/branch-beta/.artifact/coverage_sqlite3_js/touch.txt b/branch-beta/.artifact/coverage_sqlite3_js/touch.txt new file mode 100644 index 000000000..e69de29bb diff --git a/branch-beta/.artifact/coverage_sqlite3_sh/coverage_badge.svg b/branch-beta/.artifact/coverage_sqlite3_sh/coverage_badge.svg new file mode 100644 index 000000000..8e28d5ff5 --- /dev/null +++ b/branch-beta/.artifact/coverage_sqlite3_sh/coverage_badge.svg @@ -0,0 +1,14 @@ + + + + +coverage +97.62 % + + diff --git a/branch-beta/.artifact/coverage_sqlite3_sh/coverage_report.txt b/branch-beta/.artifact/coverage_sqlite3_sh/coverage_report.txt new file mode 100644 index 000000000..b19e8684a --- /dev/null +++ b/branch-beta/.artifact/coverage_sqlite3_sh/coverage_report.txt @@ -0,0 +1,16 @@ +V8 Coverage Report ++----------------------------------+-------------------+-------------------+ +| Files covered | Lines | Remaining | ++----------------------------------+-------------------+-------------------+ +| ./ | 97.62 % | | +| *******************************_ | 247 / 253 | 6 / 253 | ++----------------------------------+-------------------+-------------------+ +| ./lib/sqlite3-binding.js | 100.00 % | | +| ******************************** | 6 / 6 | 0 / 6 | ++----------------------------------+-------------------+-------------------+ +| ./lib/sqlite3.js | 97.11 % | | +| *******************************_ | 202 / 208 | 6 / 208 | ++----------------------------------+-------------------+-------------------+ +| ./lib/trace.js | 100.00 % | | +| ******************************** | 39 / 39 | 0 / 39 | ++----------------------------------+-------------------+-------------------+ diff --git a/branch-beta/.artifact/coverage_sqlite3_sh/index.html b/branch-beta/.artifact/coverage_sqlite3_sh/index.html new file mode 100644 index 000000000..83d95a7bd --- /dev/null +++ b/branch-beta/.artifact/coverage_sqlite3_sh/index.html @@ -0,0 +1,212 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . /
    +
    +
    +
    +
    + 97.62 %
    + 247 / 253 +
    +
    + 6 / 253 +
    + . / lib/sqlite3-binding.js
    +
    +
    +
    +
    + 100.00 %
    + 6 / 6 +
    +
    + 0 / 6 +
    + . / lib/sqlite3.js
    +
    +
    +
    +
    + 97.11 %
    + 202 / 208 +
    +
    + 6 / 208 +
    + . / lib/trace.js
    +
    +
    +
    +
    + 100.00 %
    + 39 / 39 +
    +
    + 0 / 39 +
    +
    + + + + diff --git a/branch-beta/.artifact/coverage_sqlite3_sh/lib/sqlite3-binding.js.html b/branch-beta/.artifact/coverage_sqlite3_sh/lib/sqlite3-binding.js.html new file mode 100644 index 000000000..d3fec08a6 --- /dev/null +++ b/branch-beta/.artifact/coverage_sqlite3_sh/lib/sqlite3-binding.js.html @@ -0,0 +1,174 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / lib/sqlite3-binding.js
    +
    +
    +
    +
    + 100.00 %
    + 6 / 6 +
    +
    + 0 / 6 +
    +
    + + +
    +
        1.      2const binary = require('@mapbox/node-pre-gyp');
    +
        2.      2const path = require('path');
    +
        3.      2const binding_path = binary.find(path.resolve(path.join(__dirname,'../package.json')));
    +
        4.      2const binding = require(binding_path);
    +
        5.      2module.exports = exports = binding;
    +
        6.      2
    +
    + + + + diff --git a/branch-beta/.artifact/coverage_sqlite3_sh/lib/sqlite3.js.html b/branch-beta/.artifact/coverage_sqlite3_sh/lib/sqlite3.js.html new file mode 100644 index 000000000..db949263d --- /dev/null +++ b/branch-beta/.artifact/coverage_sqlite3_sh/lib/sqlite3.js.html @@ -0,0 +1,376 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / lib/sqlite3.js
    +
    +
    +
    +
    + 97.11 %
    + 202 / 208 +
    +
    + 6 / 208 +
    +
    + + +
    +
        1.      2const path = require('path');
    +
        2.      2const sqlite3 = require('./sqlite3-binding.js');
    +
        3.      2const EventEmitter = require('events').EventEmitter;
    +
        4.      2module.exports = exports = sqlite3;
    +
        5.      2
    +
        6.     12function normalizeMethod (fn) {
    +
        7.   3938    return function (sql) {
    +
        8.   3938        let errBack;
    +
        9.   3938        const args = Array.prototype.slice.call(arguments, 1);
    +
       10.   3938
    +
       11.   1119        if (typeof args[args.length - 1] === 'function') {
    +
       12.   1119            const callback = args[args.length - 1];
    +
       13.   1119            errBack = function(err) {
    +
       14.   1119                if (err) {
    +
       15.   1119                    callback(err);
    +
       16.   1119                }
    +
       17.   1119            };
    +
       18.   1119        }
    +
       19.   3938        const statement = new Statement(this, sql, errBack);
    +
       20.   3938        return fn.call(this, statement, args);
    +
       21.   3938    };
    +
       22.     12}
    +
       23.      2
    +
       24.      6function inherits(target, source) {
    +
       25.      6    for (const k in source.prototype)
    +
       26.    108        target.prototype[k] = source.prototype[k];
    +
       27.      6}
    +
       28.      2
    +
       29.      2sqlite3.cached = {
    +
       30.      4    Database: function(file, a, b) {
    +
       31.     -0        if (file === '' || file === ':memory:') {
    +
       32.     -0            // Don't cache special databases.
    +
       33.     -0            return new Database(file, a, b);
    +
       34.     -0        }
    +
       35.      4
    +
       36.      4        let db;
    +
       37.      4        file = path.resolve(file);
    +
       38.      4
    +
       39.      2        if (!sqlite3.cached.objects[file]) {
    +
       40.      2            db = sqlite3.cached.objects[file] = new Database(file, a, b);
    +
       41.      2        }
    +
       42.      2        else {
    +
       43.      2            // Make sure the callback is called.
    +
       44.      2            db = sqlite3.cached.objects[file];
    +
       45.     -0            const callback = (typeof a === 'number') ? b : a;
    +
       46.      2            if (typeof callback === 'function') {
    +
       47.      2                function cb() { callback.call(db, null); }
    +
       48.      2                if (db.open) process.nextTick(cb);
    +
       49.      2                else db.once('open', cb);
    +
       50.      2            }
    +
       51.      2        }
    +
       52.      4
    +
       53.      4        return db;
    +
       54.      4    },
    +
       55.      2    objects: {}
    +
       56.      2};
    +
       57.      2
    +
       58.      2
    +
       59.      2const Database = sqlite3.Database;
    +
       60.      2const Statement = sqlite3.Statement;
    +
       61.      2const Backup = sqlite3.Backup;
    +
       62.      2
    +
       63.      2inherits(Database, EventEmitter);
    +
       64.      2inherits(Statement, EventEmitter);
    +
       65.      2inherits(Backup, EventEmitter);
    +
       66.      2
    +
       67.      2// Database#prepare(sql, [bind1, bind2, ...], [callback])
    +
       68.   1811Database.prototype.prepare = normalizeMethod(function(statement, params) {
    +
       69.   1811    return params.length
    +
       70.      7        ? statement.bind.apply(statement, params)
    +
       71.   1804        : statement;
    +
       72.   1811});
    +
       73.      2
    +
       74.      2// Database#run(sql, [bind1, bind2, ...], [callback])
    +
       75.   2074Database.prototype.run = normalizeMethod(function(statement, params) {
    +
       76.   2074    statement.run.apply(statement, params).finalize();
    +
       77.   2074    return this;
    +
       78.   2074});
    +
       79.      2
    +
       80.      2// Database#get(sql, [bind1, bind2, ...], [callback])
    +
       81.     35Database.prototype.get = normalizeMethod(function(statement, params) {
    +
       82.     35    statement.get.apply(statement, params).finalize();
    +
       83.     35    return this;
    +
       84.     35});
    +
       85.      2
    +
       86.      2// Database#all(sql, [bind1, bind2, ...], [callback])
    +
       87.      9Database.prototype.all = normalizeMethod(function(statement, params) {
    +
       88.      9    statement.all.apply(statement, params).finalize();
    +
       89.      9    return this;
    +
       90.      9});
    +
       91.      2
    +
       92.      2// Database#each(sql, [bind1, bind2, ...], [callback], [complete])
    +
       93.      7Database.prototype.each = normalizeMethod(function(statement, params) {
    +
       94.      7    statement.each.apply(statement, params).finalize();
    +
       95.      7    return this;
    +
       96.      7});
    +
       97.      2
    +
       98.      2Database.prototype.map = normalizeMethod(function(statement, params) {
    +
       99.      2    statement.map.apply(statement, params).finalize();
    +
      100.      2    return this;
    +
      101.      2});
    +
      102.      2
    +
      103.      2// Database#backup(filename, [callback])
    +
      104.      2// Database#backup(filename, destName, sourceName, filenameIsDest, [callback])
    +
      105.     15Database.prototype.backup = function() {
    +
      106.     15    let backup;
    +
      107.     13    if (arguments.length <= 2) {
    +
      108.     13        // By default, we write the main database out to the main database of the named file.
    +
      109.     13        // This is the most likely use of the backup api.
    +
      110.     13        backup = new Backup(this, arguments[0], 'main', 'main', true, arguments[1]);
    +
      111.     13    } else {
    +
      112.      2        // Otherwise, give the user full control over the sqlite3_backup_init arguments.
    +
      113.      2        backup = new Backup(this, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]);
    +
      114.      2    }
    +
      115.     15    // Per the sqlite docs, exclude the following errors as non-fatal by default.
    +
      116.     15    backup.retryErrors = [sqlite3.BUSY, sqlite3.LOCKED];
    +
      117.     15    return backup;
    +
      118.     15};
    +
      119.      2
    +
      120.      2Statement.prototype.map = function() {
    +
      121.      2    const params = Array.prototype.slice.call(arguments);
    +
      122.      2    const callback = params.pop();
    +
      123.      2    params.push(function(err, rows) {
    +
      124.     -0        if (err) return callback(err);
    +
      125.      2        const result = {};
    +
      126.      2        if (rows.length) {
    +
      127.      2            const keys = Object.keys(rows[0]);
    +
      128.      2            const key = keys[0];
    +
      129.      1            if (keys.length > 2) {
    +
      130.      1                // Value is an object
    +
      131.      5                for (let i = 0; i < rows.length; i++) {
    +
      132.      5                    result[rows[i][key]] = rows[i];
    +
      133.      5                }
    +
      134.      1            } else {
    +
      135.      1                const value = keys[1];
    +
      136.      1                // Value is a plain value
    +
      137.      5                for (let i = 0; i < rows.length; i++) {
    +
      138.      5                    result[rows[i][key]] = rows[i][value];
    +
      139.      5                }
    +
      140.      1            }
    +
      141.      2        }
    +
      142.      2        callback(err, result);
    +
      143.      2    });
    +
      144.      2    return this.all.apply(this, params);
    +
      145.      2};
    +
      146.      2
    +
      147.      2let isVerbose = false;
    +
      148.      2
    +
      149.      2const supportedEvents = [ 'trace', 'profile', 'insert', 'update', 'delete' ];
    +
      150.      2
    +
      151.      7Database.prototype.addListener = Database.prototype.on = function(type) {
    +
      152.      7    const val = EventEmitter.prototype.addListener.apply(this, arguments);
    +
      153.      4    if (supportedEvents.indexOf(type) >= 0) {
    +
      154.      4        this.configure(type, true);
    +
      155.      4    }
    +
      156.      7    return val;
    +
      157.      7};
    +
      158.      2
    +
      159.      2Database.prototype.removeListener = function(type) {
    +
      160.      2    const val = EventEmitter.prototype.removeListener.apply(this, arguments);
    +
      161.      1    if (supportedEvents.indexOf(type) >= 0 && !this._events[type]) {
    +
      162.      1        this.configure(type, false);
    +
      163.      1    }
    +
      164.      2    return val;
    +
      165.      2};
    +
      166.      2
    +
      167.      1Database.prototype.removeAllListeners = function(type) {
    +
      168.      1    const val = EventEmitter.prototype.removeAllListeners.apply(this, arguments);
    +
      169.      1    if (supportedEvents.indexOf(type) >= 0) {
    +
      170.      1        this.configure(type, false);
    +
      171.      1    }
    +
      172.      1    return val;
    +
      173.      1};
    +
      174.      2
    +
      175.      2// Save the stack trace over EIO callbacks.
    +
      176.      1sqlite3.verbose = function() {
    +
      177.      1    if (!isVerbose) {
    +
      178.      1        const trace = require('./trace');
    +
      179.      1        [
    +
      180.      1            'prepare',
    +
      181.      1            'get',
    +
      182.      1            'run',
    +
      183.      1            'all',
    +
      184.      1            'each',
    +
      185.      1            'map',
    +
      186.      1            'close',
    +
      187.      1            'exec'
    +
      188.      8        ].forEach(function (name) {
    +
      189.      8            trace.extendTrace(Database.prototype, name);
    +
      190.      8        });
    +
      191.      1        [
    +
      192.      1            'bind',
    +
      193.      1            'get',
    +
      194.      1            'run',
    +
      195.      1            'all',
    +
      196.      1            'each',
    +
      197.      1            'map',
    +
      198.      1            'reset',
    +
      199.      1            'finalize',
    +
      200.      8        ].forEach(function (name) {
    +
      201.      8            trace.extendTrace(Statement.prototype, name);
    +
      202.      8        });
    +
      203.      1        isVerbose = true;
    +
      204.      1    }
    +
      205.      1
    +
      206.      1    return this;
    +
      207.      1};
    +
      208.      2
    +
    + + + + diff --git a/branch-beta/.artifact/coverage_sqlite3_sh/lib/trace.js.html b/branch-beta/.artifact/coverage_sqlite3_sh/lib/trace.js.html new file mode 100644 index 000000000..28608da6d --- /dev/null +++ b/branch-beta/.artifact/coverage_sqlite3_sh/lib/trace.js.html @@ -0,0 +1,207 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / lib/trace.js
    +
    +
    +
    +
    + 100.00 %
    + 39 / 39 +
    +
    + 0 / 39 +
    +
    + + +
    +
        1.      1// Inspired by https://github.com/tlrobinson/long-stack-traces
    +
        2.      1const util = require('util');
    +
        3.      1
    +
        4.     16function extendTrace(object, property, pos) {
    +
        5.     16    const old = object[property];
    +
        6.      1    object[property] = function() {
    +
        7.      1        const error = new Error();
    +
        8.      1        const name = object.constructor.name + '#' + property + '(' +
    +
        9.      2            Array.prototype.slice.call(arguments).map(function(el) {
    +
       10.      2                return util.inspect(el, false, 0);
    +
       11.      2            }).join(', ') + ')';
    +
       12.      1
    +
       13.      1        if (typeof pos === 'undefined') pos = -1;
    +
       14.      1        if (pos < 0) pos += arguments.length;
    +
       15.      1        const cb = arguments[pos];
    +
       16.      1        if (typeof arguments[pos] === 'function') {
    +
       17.      1            arguments[pos] = function replacement() {
    +
       18.      1                const err = arguments[0];
    +
       19.      1                if (err && err.stack && !err.__augmented) {
    +
       20.      1                    err.stack = filter(err).join('\n');
    +
       21.      1                    err.stack += '\n--> in ' + name;
    +
       22.      1                    err.stack += '\n' + filter(error).slice(1).join('\n');
    +
       23.      1                    err.__augmented = true;
    +
       24.      1                }
    +
       25.      1                return cb.apply(this, arguments);
    +
       26.      1            };
    +
       27.      1        }
    +
       28.      1        return old.apply(this, arguments);
    +
       29.      1    };
    +
       30.     16}
    +
       31.      1exports.extendTrace = extendTrace;
    +
       32.      1
    +
       33.      1
    +
       34.      2function filter(error) {
    +
       35.     13    return error.stack.split('\n').filter(function(line) {
    +
       36.     13        return line.indexOf(__filename) < 0;
    +
       37.     13    });
    +
       38.      2}
    +
       39.      1
    +
    + + + + diff --git a/branch-beta/.artifact/coverage_sqlite3_sh/touch.txt b/branch-beta/.artifact/coverage_sqlite3_sh/touch.txt new file mode 100644 index 000000000..e69de29bb diff --git a/branch-beta/.artifact/jslint_report_hello.html b/branch-beta/.artifact/jslint_report_hello.html new file mode 100644 index 000000000..ffa15a7ff --- /dev/null +++ b/branch-beta/.artifact/jslint_report_hello.html @@ -0,0 +1,246 @@ + + +
    +JSLint Report +
    +
    +Report: Warnings (2) +
    +
    1: 17
    1. Undeclared 'console'.
    function foo() {console.log('hello world');} + +
    1: 29
    2. Use double quotes, not single quotes.
    function foo() {console.log('hello world');} + +
    +
    +
    +Report: Properties (1) + +
    +
    +Report: Functions (1) +
    +
    +
    global
    foo
    +
    1: 1
    foo()
    +
    +
    + diff --git a/branch-beta/.artifact/jslint_wrapper_vscode/.vscode/launch.json b/branch-beta/.artifact/jslint_wrapper_vscode/.vscode/launch.json new file mode 100644 index 000000000..140e1f4ec --- /dev/null +++ b/branch-beta/.artifact/jslint_wrapper_vscode/.vscode/launch.json @@ -0,0 +1,13 @@ +{ + "configurations": [ + { + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}" + ], + "name": "Run Extension", + "request": "launch", + "type": "extensionHost" + } + ], + "version": "0.0.1" +} diff --git a/branch-beta/.artifact/jslint_wrapper_vscode/.vscodeignore b/branch-beta/.artifact/jslint_wrapper_vscode/.vscodeignore new file mode 100644 index 000000000..b81046e0b --- /dev/null +++ b/branch-beta/.artifact/jslint_wrapper_vscode/.vscodeignore @@ -0,0 +1,12 @@ +* +.* +node_modules +!.npmignore +!CHANGELOG.md +!LICENSE +!README.md + +!asset_* +!jslint.mjs +!jslint_wrapper_cjs.cjs +!jslint_wrapper_vscode.js diff --git a/branch-beta/.artifact/jslint_wrapper_vscode/LICENSE b/branch-beta/.artifact/jslint_wrapper_vscode/LICENSE new file mode 100644 index 000000000..b3dbff00c --- /dev/null +++ b/branch-beta/.artifact/jslint_wrapper_vscode/LICENSE @@ -0,0 +1,22 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/branch-beta/.artifact/jslint_wrapper_vscode/README.md b/branch-beta/.artifact/jslint_wrapper_vscode/README.md new file mode 100644 index 000000000..af9808967 --- /dev/null +++ b/branch-beta/.artifact/jslint_wrapper_vscode/README.md @@ -0,0 +1,9 @@ +# Quickstart JSLint in VSCode +1. In VSCode, search and install extension [`vscode-jslint`](https://marketplace.visualstudio.com/items?itemName=jslint.vscode-jslint) +2. In VSCode, while editing a javascript file: + - right-click context-menu and select `[JSLint - Lint File]` + - or use key-binding `[Ctrl + Shift + J], [L]` + - or use key-binding `[ Cmd + Shift + J], [L]` for Mac +- screenshot + +[![screenshot](https://jslint-org.github.io/jslint/asset_image_jslint_wrapper_vscode.png)](https://marketplace.visualstudio.com/items?itemName=jslint.vscode-jslint) diff --git a/branch-beta/.artifact/jslint_wrapper_vscode/asset_image_logo_512.png b/branch-beta/.artifact/jslint_wrapper_vscode/asset_image_logo_512.png new file mode 100644 index 000000000..19d154d68 Binary files /dev/null and b/branch-beta/.artifact/jslint_wrapper_vscode/asset_image_logo_512.png differ diff --git a/branch-beta/.artifact/jslint_wrapper_vscode/jslint.mjs b/branch-beta/.artifact/jslint_wrapper_vscode/jslint.mjs new file mode 100644 index 000000000..b486c1ce2 --- /dev/null +++ b/branch-beta/.artifact/jslint_wrapper_vscode/jslint.mjs @@ -0,0 +1,11573 @@ +// #!/usr/bin/env node +// JSLint + +// The Unlicense +// +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + + +// jslint(source, option_dict, global_list) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze. +// option_dict An object whose keys correspond to option names. +// global_list An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// PHASE 1. Split by newlines into . +// PHASE 2. Lex into . +// PHASE 3. Parse into using the Pratt-parser. +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// PHASE 5. Check whitespace between tokens in . + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*jslint beta, node*/ +/*property + JSLINT_BETA, NODE_V8_COVERAGE, a, all, argv, arity, artifact, + assertErrorThrownAsync, assertJsonEqual, assertOrThrow, assign, async, b, + beta, bitwise, block, body, browser, c, calls, catch, catch_list, + catch_stack, causes, char, children, clear, closer, closure, code, column, + concat, consoleError, console_error, console_log, constant, context, + convert, count, coverageDir, create, cwd, d, dead, debugInline, default, + delta, devel, directive, directive_ignore_line, directive_list, directives, + dirname, disrupt, dot, edition, elem_list, ellipsis, else, end, endOffset, + endsWith, entries, env, error, eval, every, example_list, excludeList, exec, + execArgv, exit, exitCode, export_dict, exports, expression, extra, fart, + file, fileList, fileURLToPath, filter, finally, flag, floor, for, forEach, + formatted_message, free, freeze, from, froms, fsWriteFileWithParents, + fud_stmt, functionName, function_list, function_stack, functions, get, + getset, github_repo, globExclude, global, global_dict, global_list, + holeList, htmlEscape, id, identifier, import, import_list, import_meta_url, + inc, includeList, indent2, index, indexOf, init, initial, isArray, + isBlockCoverage, isHole, isNaN, is_equal, is_weird, join, jslint, + jslint_apidoc, jslint_assert, jslint_charset_ascii, jslint_cli, + jslint_edition, jslint_phase1_split, jslint_phase2_lex, jslint_phase3_parse, + jslint_phase4_walk, jslint_phase5_whitage, jslint_report, json, + jstestDescribe, jstestIt, jstestOnExit, keys, label, lbp, led_infix, length, + level, line, lineList, line_list, line_offset, line_source, lines, + linesCovered, linesTotal, live, log, long, loop, m, map, margin, match, max, + message, meta, min, mkdir, modeCoverageIgnoreFile, modeIndex, mode_cli, + mode_conditional, mode_json, mode_module, mode_noop, mode_property, + mode_shebang, mode_stop, module, moduleFsInit, moduleName, module_list, + name, names, node, nomen, noop, now, nr, nud_prefix, + objectDeepCopyWithKeysSorted, ok, on, open, opening, option, option_dict, + order, package_name, padEnd, padStart, parameters, parent, parentIi, parse, + pathname, pathnameList, platform, pop, processArgv, process_argv, + process_env, process_exit, promises, property, property_dict, push, quote, + ranges, readFile, readdir, readonly, recursive, reduce, repeat, replace, + resolve, result, reverse, role, round, scriptId, search, set, shebang, + shell, shift, signature, single, slice, some, sort, source, spawn, splice, + split, stack, stack_trace, start, startOffset, startsWith, statement, + statement_prv, stdio, stop, stop_at, stringify, subscript, switch, + syntax_dict, tenure, test, test_cause, test_internal_error, this, thru, + toLocaleString, toString, token, token_global, token_list, token_nxt, + token_tree, tokens, trace, tree, trim, trimEnd, trimRight, try, type, + unlink, unordered, unshift, url, used, v8CoverageListMerge, + v8CoverageReportCreate, value, variable, version, versions, warn, warn_at, + warning, warning_list, warnings, white, wrapped, writeFile +*/ + +// init debugInline +let debugInline = (function () { + let __consoleError = function () { + return; + }; + function debug(...argv) { + +// This function will print to stderr and then return [0]. + + __consoleError("\n\ndebugInline"); + __consoleError(...argv); + __consoleError("\n"); + return argv[0]; + } + debug(); // Coverage-hack. + __consoleError = console.error; //jslint-ignore-line + return debug; +}()); +let jslint_charset_ascii = ( + "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007" + + "\b\t\n\u000b\f\r\u000e\u000f" + + "\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017" + + "\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f" + + " !\"#$%&'()*+,-./0123456789:;<=>?" + + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + + "`abcdefghijklmnopqrstuvwxyz{|}~\u007f" +); +let jslint_edition = "v2024.11.24"; +let jslint_export; // The jslint object to be exported. +let jslint_fudge = 1; // Fudge starting line and starting + // ... column to 1. +let jslint_import_meta_url = ""; // import.meta.url used by cli. +let jslint_rgx_cap = ( + /^[A-Z]/ +); +let jslint_rgx_crlf = ( + /\n|\r\n?/ +); +let jslint_rgx_digits_bits = ( + /^[01_]*/ +); +let jslint_rgx_digits_decimals = ( + /^[0-9_]*/ +); +let jslint_rgx_digits_hexs = ( + /^[0-9A-F_]*/i +); +let jslint_rgx_digits_octals = ( + /^[0-7_]*/ +); +let jslint_rgx_directive = ( + /^(jslint|property|global)\s+(.*)$/ +); +let jslint_rgx_directive_part = ( + /([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*|$/g +); +let jslint_rgx_identifier = ( + /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/ +); +let jslint_rgx_json_number = ( + +// https://datatracker.ietf.org/doc/html/rfc7159#section-6 +// number = [ minus ] int [ frac ] [ exp ] + + /^-?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][\-+]?\d+)?$/ +); +let jslint_rgx_mega = ( + +// Vim-hack - vim-editor has trouble parsing naked '`' in regexp + + /[\u0060\\]|\$\{/ +); +let jslint_rgx_module = ( + /^[a-zA-Z0-9_$:.@\-\/]+$/ +); +let jslint_rgx_numeric_separator_illegal = ( + /__|_$|_n$/m +); +let jslint_rgx_slash_star_or_slash = ( + /\/\*|\/$/ +); +let jslint_rgx_tab = ( + /\t/g +); +let jslint_rgx_todo = ( + /\b(?:todo|TO\s?DO|HACK)\b/ +); +let jslint_rgx_token = new RegExp( + "^(" + + "(\\s+)" + + "|([a-zA-Z_$][a-zA-Z0-9_$]*)" + + "|[(){}\\[\\],:;'\"~\\`]" + + "|\\?[?.]?" + + "|=(?:==?|>)?" + + "|\\.+" + + "|\\*[*\\/=]?" + + "|\\/[*\\/]?" + + "|\\+[=+]?" + + "|-[=\\-]?" + + "|[\\^%]=?" + + "|&[&=]?" + + "|\\" + + "|[|=]?" + + "|>{1,3}=?" + + "|< throws an error. + + let err; + try { + await asyncFunc(); + } catch (errCaught) { + err = errCaught; + } + assertOrThrow(err, "No error thrown."); + assertOrThrow( + !regexp || new RegExp(regexp).test(err.message), + err + ); +} + +function assertJsonEqual(aa, bb, message) { + +// This function will assert JSON.stringify() === JSON.stringify(). + + aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa), undefined, 1); + bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb), undefined, 1); + if (aa !== bb) { + throw new Error( + "\n" + aa + "\n!==\n" + bb + + ( + typeof message === "string" + ? " - " + message + : message + ? " - " + JSON.stringify(message) + : "" + ) + ); + } +} + +function assertOrThrow(condition, message) { + +// This function will throw if is falsy. + + if (!condition) { + throw ( + (!message || typeof message === "string") + ? new Error(String(message).slice(0, 2048)) + : message + ); + } +} + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than '{}' because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +async function fsWriteFileWithParents(pathname, data) { + +// This function will write to and lazy-mkdirp if necessary. + + await moduleFsInit(); + +// Try writing to pathname. + + try { + await moduleFs.promises.writeFile(pathname, data); + } catch (ignore) { + +// Lazy mkdirp. + + await moduleFs.promises.mkdir(modulePath.dirname(pathname), { + recursive: true + }); + +// Retry writing to pathname. + + await moduleFs.promises.writeFile(pathname, data); + } + console.error("wrote file " + pathname); +} + +function globExclude({ + excludeList = [], + includeList = [], + pathnameList = [] +}) { + +// This function will +// 1. Exclude pathnames in that don't match glob-patterns in +// . +// 2. Exclude pathnames in that match glob-patterns in +// . + + function globAssertNotWeird(list, name) { + +// This function will check if of strings contain weird characters. + + [ + [ + "\n", ( + /^.*?([\u0000-\u0007\r]).*/gm + ) + ], + [ + "\r", ( + /^.*?([\n]).*/gm + ) + ] + ].forEach(function ([ + separator, rgx + ]) { + list.join(separator).replace(rgx, function (match0, char) { + throw new Error( + "Weird character " + + JSON.stringify(char) + + " found in " + name + " " + + JSON.stringify(match0) + ); + }); + }); + } + + function globToRegexp(pattern) { + +// This function will translate glob to javascript-regexp, +// which javascript can then use to "glob" pathnames. + + let ii = 0; + let isClass = false; + let strClass = ""; + let strRegex = ""; + pattern = pattern.replace(( + /\/\/+/g + ), "/"); + pattern = pattern.replace(( + /\*\*\*+/g + ), "**"); + pattern.replace(( + /\\\\|\\\[|\\\]|\[|\]|./g + ), function (match0) { + switch (match0) { + case "[": + if (isClass) { + strClass += "["; + return; + } + strClass += "\u0000"; + strRegex += "\u0000"; + isClass = true; + return; + case "]": + if (isClass) { + isClass = false; + return; + } + strRegex += "]"; + return; + default: + if (isClass) { + strClass += match0; + return; + } + strRegex += match0; + } + return ""; + }); + strClass += "\u0000"; + +// An expression "[!...]" matches a single character, namely any character that +// is not matched by the expression obtained by removing the first '!' from it. +// (Thus, "[!a-]" matches any single character except 'a', and '-'.) + + strClass = strClass.replace(( + /\u0000!/g + ), "\u0000^"); + +// One may include '-' in its literal meaning by making it the first or last +// character between the brackets. + + strClass = strClass.replace(( + /\u0000-/g + ), "\u0000\\-"); + strClass = strClass.replace(( + /-\u0000/g + ), "\\-\u0000"); + +// Escape brackets '[', ']' in character class. + + strClass = strClass.replace(( + /[\[\]]/g + ), "\\$&"); + +// https://stackoverflow.com/questions/3561493 +// /is-there-a-regexp-escape-function-in-javascript +// $()*+-./?[\]^{|} + + strRegex = strRegex.replace(( + +// Ignore [-/]. + + /[$()*+.?\[\\\]\^{|}]/g + ), "\\$&"); + +// Expand wildcard '**/*'. + + strRegex = strRegex.replace(( + /\\\*\\\*\/(?:\\\*)+/g + ), ".*?"); + +// Expand wildcard '**'. + + strRegex = strRegex.replace(( + /(^|\/)\\\*\\\*(\/|$)/gm + ), "$1.*?$2"); + +// Expand wildcard '*'. + + strRegex = strRegex.replace(( + /(?:\\\*)+/g + ), "[^\\/]*?"); + +// Expand wildcard '?'. + + strRegex = strRegex.replace(( + /\\\?/g + ), "[^\\/]"); + +// Expand directory-with-trailing-slash '.../'. + + strRegex = strRegex.replace(( + /\/$/gm + ), "\\/.*?"); + +// Merge strClass into strRegex. + + ii = 0; + strClass = strClass.split("\u0000"); + strRegex = strRegex.replace(( + /\u0000/g + ), function () { + ii += 1; + if (strClass[ii] === "") { + return ""; + } + return "[" + strClass[ii] + "]"; + }); + +// Change strRegex from string to regexp. + + strRegex = new RegExp("^" + strRegex + "$", "gm"); + return strRegex; + } + +// Validate excludeList, includeList, pathnameList. + + globAssertNotWeird(excludeList, "pattern"); + globAssertNotWeird(includeList, "pattern"); + globAssertNotWeird(pathnameList, "pathname"); + +// Optimization +// Concat pathnames into a single, newline-separated string, +// whose pathnames can all be filtered with a single, regexp-pass. + + pathnameList = pathnameList.join("\n"); + +// 1. Exclude pathnames in that don't match glob-patterns in +// . + + if (includeList.length > 0) { + includeList = includeList.map(globToRegexp); + includeList.forEach(function (pattern) { + pathnameList = pathnameList.replace(pattern, "\u0000$&"); + }); + pathnameList = pathnameList.replace(( + /^[^\u0000].*/gm + ), ""); + pathnameList = pathnameList.replace(( + /^\u0000+/gm + ), ""); + } + +// 2. Exclude pathnames in that match glob-patterns in +// . + + excludeList = excludeList.map(globToRegexp); + excludeList.forEach(function (pattern) { + pathnameList = pathnameList.replace(pattern, ""); + }); + +// Split newline-separated pathnames back to list. + + pathnameList = pathnameList.split("\n").filter(function (elem) { + return elem; + }); + return { + excludeList, + includeList, + pathnameList + }; +} + +function htmlEscape(str) { + +// This function will make html-safe by escaping & < >. + + return String(str).replace(( + /&/g + ), "&").replace(( + //g + ), ">"); +} + +function jslint( + source = "", // A text to analyze. + option_dict = empty(), // An object whose keys correspond to option + // ... names. + global_list = [] // An array of strings containing global + // ... variables that the file is allowed + // ... readonly access. +) { + +// The jslint function itself. + + let catch_list = []; // The array containing all catch-blocks. + let catch_stack = [ // The stack of catch-blocks. + { + context: empty() + } + ]; + let cause_dict = empty(); // The object of test-causes. + let directive_list = []; // The directive comments. + let export_dict = empty(); // The exported names and values. + let function_list = []; // The array containing all functions. + let function_stack = []; // The stack of functions. + let global_dict = empty(); // The object containing the global + // ... declarations. + let import_list = []; // The array collecting all import-from strings. + let line_list = String( // The array containing source lines. + "\n" + source + ).split(jslint_rgx_crlf).map(function (line_source) { + return { + line_source + }; + }); + let mode_stop = false; // true if JSLint cannot finish. + let property_dict = empty(); // The object containing the tallied + // ... property names. + let state = empty(); // jslint state-object to be passed between + // jslint functions. + let syntax_dict = empty(); // The object containing the parser. + let tenure = empty(); // The predefined property registry. + let token_global = { // The global object; the outermost context. + async: 0, + body: true, + context: empty(), + finally: 0, + from: 0, + id: "(global)", + level: 0, + line: jslint_fudge, + live: [], + loop: 0, + switch: 0, + thru: 0, + try: 0 + }; + let token_list = []; // The array of tokens. + let warning_list = []; // The array collecting all generated warnings. + +// Error reportage functions: + + function artifact(the_token) { + +// Return a string representing an artifact. + + the_token = the_token || state.token_nxt; + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); + } + + function is_equal(aa, bb) { + +// test_cause: +// ["0&&0", "is_equal", "", "", 0] + + test_cause(""); + +// Probably deadcode. +// if (aa === bb) { +// return true; +// } + + jslint_assert(!(aa === bb), `Expected !(aa === bb).`); + if (Array.isArray(aa)) { + return ( + Array.isArray(bb) + && aa.length === bb.length + && aa.every(function (value, index) { + +// test_cause: +// ["`${0}`&&`${0}`", "is_equal", "recurse_isArray", "", 0] +// ["`${0}`&&`${1}`", "is_equal", "recurse_isArray", "", 0] + + test_cause("recurse_isArray"); + return is_equal(value, bb[index]); + }) + ); + } + +// Probably deadcode. +// if (Array.isArray(bb)) { +// return false; +// } + + jslint_assert(!Array.isArray(bb), `Expected !Array.isArray(bb).`); + switch (aa.id === bb.id && aa.id) { + case "(number)": + case "(string)": + return aa.value === bb.value; + +// PR-394 - Bugfix +// Fix jslint falsely believing megastring literals `0` and `1` are similar. + + case "`": + if (!is_equal(aa.value, bb.value)) { + return false; + } + break; + } + if (is_weird(aa) || is_weird(bb)) { + +// test_cause: +// ["aa(/./)||{}", "is_equal", "false", "", 0] + + test_cause("false"); + return false; + } + if (aa.arity === bb.arity && aa.id === bb.id) { + if (aa.id === "." || aa.id === "?.") { + +// test_cause: +// ["aa.bb&&aa.bb", "is_equal", "recurse_arity_id", "", 0] +// ["aa?.bb&&aa?.bb", "is_equal", "recurse_arity_id", "", 0] + + test_cause("recurse_arity_id"); + return ( + is_equal(aa.expression, bb.expression) + && is_equal(aa.name, bb.name) + ); + } + if (aa.arity === "unary") { + +// test_cause: +// ["+0&&+0", "is_equal", "recurse_unary", "", 0] + + test_cause("recurse_unary"); + return is_equal(aa.expression, bb.expression); + } + if (aa.arity === "binary") { + +// test_cause: +// ["aa[0]&&aa[0]", "is_equal", "recurse_binary", "", 0] + + test_cause("recurse_binary"); + return ( + aa.id !== "(" + && is_equal(aa.expression[0], bb.expression[0]) + && is_equal(aa.expression[1], bb.expression[1]) + ); + } + if (aa.arity === "ternary") { + +// test_cause: +// ["aa=(``?``:``)&&(``?``:``)", "is_equal", "recurse_ternary", "", 0] + + test_cause("recurse_ternary"); + return ( + is_equal(aa.expression[0], bb.expression[0]) + && is_equal(aa.expression[1], bb.expression[1]) + && is_equal(aa.expression[2], bb.expression[2]) + ); + } + +// Probably deadcode. +// if (aa.arity === "function" || aa.arity === "regexp") { +// return false; +// } + + jslint_assert( + !(aa.arity === "function" || aa.arity === "regexp"), + `Expected !(aa.arity === "function" || aa.arity === "regexp").` + ); + +// test_cause: +// ["undefined&&undefined", "is_equal", "true", "", 0] + + test_cause("true"); + return true; + } + +// test_cause: +// ["null&&undefined", "is_equal", "false", "", 0] + + test_cause("false"); + return false; + } + + function is_weird(thing) { + switch (thing.id) { + case "(regexp)": + return true; + case "=>": + return true; + case "[": + return thing.arity === "unary"; + case "function": + return true; + case "{": + return true; + default: + return false; + } + } + + function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + the_token = the_token || state.token_nxt; + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); + } + + function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); + } + + function test_cause(code, aa, column) { + +// This function will instrument to for test-purposes. + + if (option_dict.test_cause) { + cause_dict[JSON.stringify([ + String(new Error().stack).replace(( + /^ at (?:file|stop|stop_at|test_cause|warn|warn_at)\b.*?\n/gm + ), "").match( + /\n at ((?:Object\.\w+?_)?\w+?) / + )[1].replace(( + /^Object\./ + ), ""), + code, + String( + (aa === undefined || aa === token_global) + ? "" + : aa + ), + column || 0 + ])] = true; + } + } + + function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + let the_warning; + the_token = the_token || state.token_nxt; + the_warning = warn_at( + code, + the_token.line, + (the_token.from || 0) + jslint_fudge, + a || artifact(the_token), + b, + c, + d + ); + +// Issue #408 +// Warnings that should be ignored sometimes suppress legitimate warnings. + + if (the_warning.directive_ignore_line) { + return the_warning; + } + +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token.warning) { + warning_list.pop(); + return the_warning; + } + the_token.warning = the_warning; + return the_warning; + } + + function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + let mm; + let warning = Object.assign(empty(), { + a, + b, + c, + code, + +// Fudge column numbers in warning message. + + column: column || jslint_fudge, + d, + line, + line_source: "", + name: "JSLintError" + }, line_list[line]); + warning.column = Math.max( + Math.min(warning.column, warning.line_source.length), + jslint_fudge + ); + test_cause(code, b || a, warning.column); + switch (code) { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + case "and": + mm = `The '&&' subexpression should be wrapped in parens.`; + break; + case "bad_assignment_a": + mm = `Bad assignment to '${a}'.`; + break; + case "bad_directive_a": + mm = `Bad directive '${a}'.`; + break; + case "bad_get": + mm = `A get function takes no parameters.`; + break; + case "bad_module_name_a": + mm = `Bad module name '${a}'.`; + break; + case "bad_option_a": + mm = `Bad option '${a}'.`; + break; + case "bad_set": + mm = `A set function takes one parameter.`; + break; + case "duplicate_a": + mm = `Duplicate '${a}'.`; + break; + case "empty_block": + mm = `Empty block.`; + break; + case "expected_a": + mm = `Expected '${a}'.`; + break; + case "expected_a_at_b_c": + mm = `Expected '${a}' at column ${b}, not column ${c}.`; + break; + case "expected_a_b": + mm = `Expected '${a}' and instead saw '${b}'.`; + break; + case "expected_a_b_before_c_d": + mm = `Expected ${a} '${b}' to be ordered before ${c} '${d}'.`; + break; + case "expected_a_b_from_c_d": + mm = ( + `Expected '${a}' to match '${b}' from line ${c}` + + ` and instead saw '${d}'.` + ); + break; + case "expected_a_before_b": + mm = `Expected '${a}' before '${b}'.`; + break; + case "expected_digits_after_a": + mm = `Expected digits after '${a}'.`; + break; + case "expected_four_digits": + mm = `Expected four digits after '\\u'.`; + break; + case "expected_identifier_a": + mm = `Expected an identifier and instead saw '${a}'.`; + break; + case "expected_line_break_a_b": + mm = `Expected a line break between '${a}' and '${b}'.`; + break; + case "expected_regexp_factor_a": + mm = `Expected a regexp factor and instead saw '${a}'.`; + break; + case "expected_space_a_b": + mm = `Expected one space between '${a}' and '${b}'.`; + break; + case "expected_statements_a": + mm = `Expected statements before '${a}'.`; + break; + case "expected_string_a": + mm = `Expected a string and instead saw '${a}'.`; + break; + case "expected_type_string_a": + mm = `Expected a type string and instead saw '${a}'.`; + break; + case "freeze_exports": + mm = ( + `Expected 'Object.freeze('. All export values should be frozen.` + ); + break; + +// PR-378 - Relax warning "function_in_loop". +// +// case "function_in_loop": +// mm = `Don't create functions within a loop.`; +// break; + +// PR-390 - Add numeric-separator check. + + case "illegal_num_separator": + mm = `Illegal numeric separator '_' at column ${column}.`; + break; + case "infix_in": + mm = ( + `Unexpected 'in'. Compare with undefined,` + + ` or use the hasOwnProperty method instead.` + ); + break; + case "label_a": + mm = `'${a}' is a statement label.`; + break; + case "misplaced_a": + mm = `Place '${a}' at the outermost level.`; + break; + case "misplaced_directive_a": + mm = `Place the '/*${a}*/' directive before the first statement.`; + break; + case "missing_await_statement": + mm = `Expected await statement in async function.`; + break; + +// PR-347 - Disable warning "missing_browser". +// +// case "missing_browser": +// mm = `/*global*/ requires the Assume a browser option.`; +// break; + + case "missing_m": + mm = `Expected 'm' flag on a multiline regular expression.`; + break; + case "naked_block": + mm = `Naked block.`; + break; + case "nested_comment": + mm = `Nested comment.`; + break; + case "not_label_a": + mm = `'${a}' is not a label.`; + break; + case "number_isNaN": + mm = `Use Number.isNaN function to compare with NaN.`; + break; + case "out_of_scope_a": + mm = `'${a}' is out of scope.`; + break; + case "redefinition_a_b": + mm = `Redefinition of '${a}' from line ${b}.`; + break; + case "redefinition_global_a_b": + mm = `Redefinition of global ${a} variable '${b}'.`; + break; + case "required_a_optional_b": + mm = `Required parameter '${a}' after optional parameter '${b}'.`; + break; + case "reserved_a": + mm = `Reserved name '${a}'.`; + break; + case "subscript_a": + mm = `['${a}'] is better written in dot notation.`; + break; + case "todo_comment": + mm = `Unexpected TODO comment.`; + break; + case "too_long": + mm = `Line is longer than 80 characters.`; + break; + case "too_many_digits": + mm = `Too many digits.`; + break; + case "unclosed_comment": + mm = `Unclosed comment.`; + break; + case "unclosed_disable": + mm = ( + `Directive '/*jslint-disable*/' was not closed` + + ` with '/*jslint-enable*/'.` + ); + break; + case "unclosed_mega": + mm = `Unclosed mega literal.`; + break; + case "unclosed_string": + mm = `Unclosed string.`; + break; + case "undeclared_a": + mm = `Undeclared '${a}'.`; + break; + case "unexpected_a": + mm = `Unexpected '${a}'.`; + break; + case "unexpected_a_after_b": + mm = `Unexpected '${a}' after '${b}'.`; + break; + case "unexpected_a_before_b": + mm = `Unexpected '${a}' before '${b}'.`; + break; + case "unexpected_at_top_level_a": + mm = `Expected '${a}' to be in a function.`; + break; + case "unexpected_char_a": + mm = `Unexpected character '${a}'.`; + break; + case "unexpected_comment": + mm = `Unexpected comment.`; + break; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// case "unexpected_directive_a": +// mm = `When using modules, don't use directive '/\u002a${a}'.`; +// break; + + case "unexpected_expression_a": + mm = `Unexpected expression '${a}' in statement position.`; + break; + case "unexpected_label_a": + mm = `Unexpected label '${a}'.`; + break; + case "unexpected_parens": + mm = `Don't wrap function literals in parens.`; + break; + case "unexpected_space_a_b": + mm = `Unexpected space between '${a}' and '${b}'.`; + break; + case "unexpected_statement_a": + mm = `Unexpected statement '${a}' in expression position.`; + break; + case "unexpected_trailing_space": + mm = `Unexpected trailing space.`; + break; + case "unexpected_typeof_a": + mm = ( + `Unexpected 'typeof'. Use '===' to compare directly with ${a}.` + ); + break; + case "uninitialized_a": + mm = `Uninitialized '${a}'.`; + break; + case "unopened_enable": + mm = ( + `Directive '/*jslint-enable*/' was not opened` + + ` with '/*jslint-disable*/'.` + ); + break; + case "unreachable_a": + mm = `Unreachable '${a}'.`; + break; + case "unregistered_property_a": + mm = `Unregistered property name '${a}'.`; + break; + case "unused_a": + mm = `Unused '${a}'.`; + break; + case "use_double": + mm = `Use double quotes, not single quotes.`; + break; + +// PR-386 - Fix issue #382 - Make fart-related warnings more readable. + + case "use_function_not_fart": + mm = ( + `Use 'function (...)', not '(...) =>' when arrow functions` + + ` become too complex.` + ); + break; + case "use_open": + mm = ( + `Wrap a ternary expression in parens,` + + ` with a line break after the left paren.` + ); + break; + case "use_spaces": + mm = `Use spaces, not tabs.`; + break; + case "var_on_top": + mm = `Move variable declaration to top of function or script.`; + break; + case "var_switch": + mm = `Don't declare variables in a switch.`; + break; + case "weird_condition_a": + mm = `Weird condition '${a}'.`; + break; + case "weird_expression_a": + mm = `Weird expression '${a}'.`; + break; + case "weird_loop": + mm = `Weird loop.`; + break; + case "weird_property_a": + mm = `Weird property name '${a}'.`; + break; + case "weird_relation_a": + mm = `Weird relation '${a}'.`; + break; + case "wrap_condition": + mm = `Wrap the condition in parens.`; + break; + +// PR-386 - Fix issue #382 - Make fart-related warnings more readable. + + case "wrap_fart_parameter": + mm = `Wrap the parameter before '=>' in parens.`; + break; + case "wrap_immediate": + mm = ( + `Wrap an immediate function invocation in parentheses to assist` + + ` the reader in understanding that the expression is the` + + ` result of a function, and not the function itself.` + ); + break; + case "wrap_regexp": + mm = `Wrap this regexp in parens to avoid confusion.`; + break; + case "wrap_unary": + mm = `Wrap the unary expression in parens.`; + break; + } + +// Validate mm. + + jslint_assert(mm, code); + warning.message = mm; + +// PR-242 - Include stack_trace for jslint to debug itself for errors. + + if (option_dict.trace) { + warning.stack_trace = new Error().stack; + } + if (warning.directive_ignore_line) { + +// test_cause: +// ["0 //jslint-ignore-line", "semicolon", "directive_ignore_line", "", 0] + + test_cause("directive_ignore_line"); + return warning; + } + warning_list.push(warning); + return warning; + } + + try { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + + option_dict = Object.assign(empty(), option_dict); + Object.assign(state, { + artifact, + catch_list, + catch_stack, + directive_list, + export_dict, + function_list, + function_stack, + global_dict, + global_list, + import_list, + is_equal, + is_weird, + line_list, + mode_json: false, // true if parsing JSON. + mode_module: false, // true if import or export was used. + mode_property: false, // true if directive /*property*/ is + // ... used. + mode_shebang: false, // true if #! is seen on the first line. + option_dict, + property_dict, + source, + stop, + stop_at, + syntax_dict, + tenure, + test_cause, + token_global, + token_list, + token_nxt: token_global, + warn, + warn_at, + warning_list + }); + +// PHASE 1. Split by newlines into . + + jslint_phase1_split(state); + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 2. Lex into . + + jslint_phase2_lex(state); + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 3. Parse into using the Pratt-parser. + + jslint_phase3_parse(state); + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). + + if (!state.mode_json) { + jslint_phase4_walk(state); + } + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 5. Check whitespace between tokens in . + + if (!state.mode_json && warning_list.length === 0) { + jslint_phase5_whitage(state); + } + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PR-347 - Disable warning "missing_browser". +// +// if (!option_dict.browser) { +// directive_list.forEach(function (comment) { +// if (comment.directive === "global") { +// +// // test_cause: +// // ["/*global aa*/", "jslint", "missing_browser", "(comment)", 1] +// +// warn("missing_browser", comment); +// } +// }); +// } + + if (option_dict.test_internal_error) { + jslint_assert(undefined, "test_internal_error"); + } + } catch (err) { + mode_stop = true; + err.message = "[JSLint was unable to finish] " + err.message; + err.mode_stop = true; + if (err.name !== "JSLintError") { + Object.assign(err, { + column: jslint_fudge, + line: jslint_fudge, + line_source: "", + stack_trace: err.stack + }); + } + if (warning_list.indexOf(err) === -1) { + warning_list.push(err); + } + } + +// Sort warning_list by mode_stop first, line, column respectively. + + warning_list.sort(function (aa, bb) { + return ( + Boolean(bb.mode_stop) - Boolean(aa.mode_stop) + || aa.line - bb.line + || aa.column - bb.column + ); + +// Update each warning with formatted_message ready-for-use by jslint_cli. + + }).map(function ({ + column, + line, + line_source, + message, + stack_trace = "" + }, ii, list) { + list[ii].formatted_message = String( + String(ii + 1).padStart(2, " ") + + ". \u001b[31m" + message + "\u001b[39m" + + " \u001b[90m\/\/ line " + line + ", column " + column + + "\u001b[39m\n" + + (" " + line_source.trim()).slice(0, 72) + "\n" + + stack_trace + ).trimRight(); + }); + + return { + causes: cause_dict, + directives: directive_list, + edition: jslint_edition, + exports: export_dict, + froms: import_list, + functions: function_list, + global: token_global, + id: "(JSLint)", + json: state.mode_json, + lines: line_list, + module: state.mode_module === true, + ok: warning_list.length === 0 && !mode_stop, + option: option_dict, + property: property_dict, + shebang: ( + state.mode_shebang + ? line_list[jslint_fudge].line_source + : undefined + ), + stop: mode_stop, + tokens: token_list, + tree: state.token_tree, + warnings: warning_list + }; +} + +// PR-362 - Add API Doc. + +async function jslint_apidoc({ + example_list, + github_repo, + module_list, + package_name, + pathname, + version +}) { + +// This function will create API Doc from . + + let elem_ii = 0; + let html; + + function elem_create(moduleObj, key, moduleName) { + +// This function will create a sub API Doc from elem []. + + let example = "N/A"; + let id = encodeURIComponent("apidoc.elem." + moduleName + "." + key); + let name; + let signature; + let source; + name = htmlEscape((typeof moduleObj[key]) + " " + key); + if (typeof moduleObj[key] !== "function") { + return { + name, + signature: (` + +${name} + + `), + source: (` +
  • +

    + + ${name} + +

    +
  • + `) + }; + } + // init source + source = htmlEscape(trim_start(moduleObj[key].toString())); + // init signature + source = source.replace(( + /(\([\S\s]*?\)) \{/ + ), function (match0, match1) { + signature = htmlEscape( + match1.replace(( + / *?\/\*[\S\s]*?\*\/ */g + ), "").replace(( + / *?\/\/.*/g + ), "").replace(( + /\n{2,}/g + ), "\n") + ); + return match0; + }); + // init comment + source = source.replace(( + /\n(?:\/\/.*?\n)+\n/ + ), "$&"); + // init example + example_list.some(function (example2) { + example2.replace( + new RegExp(( + "((?:\\n.*?){8}(function )?)\\b" + + key + + "(\\((?:.*?\\n){8})" + ), "g"), + function (ignore, header, isDeclaration, footer) { + if (!isDeclaration) { + example = "..." + trim_start( + htmlEscape(header) + + "" + + htmlEscape(key) + + "" + + htmlEscape(footer) + ).trimEnd() + "\n..."; + } + return ""; + } + ); + return example !== "N/A"; + }); + if (source.length > 2048) { + source = source.slice(0, 2048) + "...\n}\n"; + } + return { + name, + signature: (` + +${name}${signature} + + `), + source: (` +
  • +

    + + ${name}${signature} + +

    +
  • +
  • Description and source-code:
    ${source}
  • +
  • Example usage:
    ${example}
  • + `) + }; + } + + function trim_start(str) { + +// This function will normalize whitespace before . + + let whitespace = ""; + str.trim().replace(( + /^ */gm + ), function (match0) { + if (whitespace === "" || match0.length < whitespace.length) { + whitespace = match0; + } + return ""; + }); + str = str.replace(new RegExp("^" + whitespace, "gm"), ""); + return str; + } + await moduleFsInit(); + +// Html-escape params. + + github_repo = htmlEscape(github_repo); + package_name = htmlEscape(package_name); + version = htmlEscape(version); + +// Init example_list. + + example_list = await Promise.all(example_list.map(async function (file) { + +// This function will read example from given file. + + let result = await moduleFs.promises.readFile(file, "utf8"); + result = ( + "\n\n\n\n\n\n\n\n" + // bug-workaround - truncate example to manageable size + + result.slice(0, 524288) + + "\n\n\n\n\n\n\n\n" + ); + result = result.replace(( + /\r\n*/g + ), "\n"); + return result; + })); + +// Init module_list. + + module_list = await Promise.all(module_list.map(async function ({ + pathname + }) { + let moduleName = htmlEscape(JSON.stringify(pathname)); + let moduleObj = await import(pathname); + if (moduleObj.default) { + moduleObj = moduleObj.default; + } + return { + elem_list: Object.keys(moduleObj).map(function (key) { + return elem_create(moduleObj, key, moduleName); + }).sort(function (aa, bb) { + return ( + aa.name < bb.name + ? -1 + : 1 + ); + }).map(function (elem) { + elem_ii += 1; + elem.signature = elem.signature.replace( + ">", + ">" + elem_ii + ". " + ); + elem.source = elem.source.replace( + "\">", + "\">" + elem_ii + ". " + ); + return elem; + }), + id: encodeURIComponent("apidoc.module." + moduleName), + moduleName + }; + })); + html = (` + + + + + + +${package_name} apidoc + + + +
    +

    API Doc for ${package_name} (${version})

    +
    + +

    Table of Contents

    +
    +
      + `) + module_list.map(function ({ + elem_list, + id, + moduleName + }) { + return ( + (` +
    • + Module ${moduleName} +
        + `) + + elem_list.map(function ({ + signature + }) { + return "
      • \n" + signature + "\n
      • \n"; + }).join("") + + (` +
      +
    • + `) + ); + }).join("") + (` +
    +
    + `) + module_list.map(function ({ + elem_list, + id, + moduleName + }) { + return ( + (` +
    +

    Module ${moduleName}

    +
      + `) + + elem_list.map(function ({ + source + }) { + return source; + }).join("") + + (` +
    +
    + `) + ); + }).join("") + (` +
    + [ + This document was created with + JSLint + ] +
    +
    + + + `); + html = html.trim().replace(( + / +?$/gm + ), "") + "\n"; + await fsWriteFileWithParents(pathname, html); +} + +function jslint_assert(condition, message) { + +// This function will throw if is falsy. + + if (condition) { + return condition; + } + throw new Error( + `This was caused by a bug in JSLint. +Please open an issue with this stack-trace (and possible example-code) at +https://github.com/jslint-org/jslint/issues. +edition = "${jslint_edition}"; +${String(message).slice(0, 2000)}` + ); +} + +async function jslint_cli({ + console_error, + console_log, + file, + import_meta_url, + mode_cli, + mode_noop, + option, + process_argv, + process_env, + process_exit, + source +}) { + +// This function will run jslint from nodejs-cli. + + let command; + let data; + let exit_code = 0; + let mode_report; + let mode_wrapper_vim; + let result; + + function jslint_from_file({ + code, + file, + line_offset = 0, + mode_conditional, + option = empty() + }) { + let result_from_file; + if ( + mode_conditional + && !( + /^\/\*jslint\b/m + ).test(code.slice(0, 65536)) + ) { + return; + } + option = Object.assign(empty(), option, { + file + }); + switch (( + /\.\w+?$|$/m + ).exec(file)[0]) { + case ".html": + +// Recursively jslint embedded "". + + code.replace(( + /^]*?>\n([\S\s]*?\n)<\/script>$/gm + ), function (ignore, match1, ii) { + jslint_from_file({ + code: match1, + file: file + ". + + import_meta_url = import_meta_url || jslint_import_meta_url; + if ( + jslint_rgx_url_search_window_jslint.test(import_meta_url) + && (typeof globalThis === "object" && globalThis) + ) { + globalThis.jslint = jslint; + } + +// Feature-detect nodejs. + + if (!( + (typeof process === "object" && process) + && process.versions + && typeof process.versions.node === "string" + && !mode_noop + )) { + return exit_code; + } + console_error = console_error || console.error; + console_log = console_log || console.log; + process_argv = process_argv || process.argv; + process_env = process_env || process.env; + process_exit = process_exit || process.exit; + await moduleFsInit(); + if ( + !( + +// Feature-detect nodejs-cli. + + process.execArgv.indexOf("--eval") === -1 + && process.execArgv.indexOf("-e") === -1 + && ( + ( + /[\/|\\]jslint(?:\.[cm]?js)?$/m + ).test(process_argv[1]) + || mode_cli + ) + && ( + moduleUrl.fileURLToPath(import_meta_url) + === + modulePath.resolve(process_argv[1]) + ) + ) + && !mode_cli + ) { + return exit_code; + } + +// init commmand + + command = String(process_argv[2]).split("="); + command[1] = command.slice(1).join("="); + + switch (command[0]) { + +// PR-362 - Add API Doc. + + case "jslint_apidoc": + await jslint_apidoc(Object.assign(JSON.parse(process_argv[3]), { + pathname: command[1] + })); + return; + +// PR-363 - Add command jslint_report. + + case "jslint_report": + mode_report = command[1]; + process_argv = process_argv.slice(1); + break; + +// COMMIT-b26d6df2 - Add command jslint_wrapper_vim. + + case "jslint_wrapper_vim": + mode_wrapper_vim = true; + process_argv = process_argv.slice(1); + break; + +// PR-364 - Add command v8_coverage_report. + + case "v8_coverage_report": + await v8CoverageReportCreate({ + consoleError: console_error, + coverageDir: command[1], + processArgv: process_argv.slice(3) + }); + return; + } + +// Normalize file relative to process.cwd(). + + process_argv.slice(2).some(function (arg) { + if (!arg.startsWith("-")) { + file = file || arg; + return true; + } + }); + if (!file) { + return; + } + file = modulePath.resolve(file) + "/"; + if (file.startsWith(process.cwd() + "/")) { + file = file.replace(process.cwd() + "/", "").slice(0, -1) || "."; + } + file = file.replace(( + /\\/g + ), "/").replace(( + /\/$/g + ), ""); + if (source) { + data = source; + } else { + +// jslint_cli - jslint directory. + + try { + data = await moduleFs.promises.readdir(file, "utf8"); + } catch (ignore) {} + if (data) { + await Promise.all(data.map(async function (file2) { + let code; + let time_start = Date.now(); + file2 = file + "/" + file2; + switch (( + /\.\w+?$|$/m + ).exec(file2)[0]) { + case ".cjs": + case ".html": + case ".js": + case ".json": + case ".md": + case ".mjs": + case ".sh": + break; + default: + return; + } + try { + code = await moduleFs.promises.readFile(file2, "utf8"); + } catch (ignore) { + return; + } + if ( + ( + /(?:\b|_)(?:lock|min|raw|rollup)(?:\b|_)/ + ).test(file2) + || !(code && code.length < 1048576) + ) { + return; + } + jslint_from_file({ + code, + file: file2, + option + }); + console_error( + "jslint - " + (Date.now() - time_start) + "ms - " + file2 + ); + })); + process_exit(exit_code); + return exit_code; + } + +// jslint_cli - jslint file. + + try { + data = await moduleFs.promises.readFile(file, "utf8"); + } catch (err) { + console_error(err); + exit_code = 1; + process_exit(exit_code); + return exit_code; + } + } + result = jslint_from_file({ + code: data, + file, + option + }); + if (mode_report) { + result = jslint.jslint_report(result); + result = `\n${result}\n`; + await fsWriteFileWithParents(mode_report, result); + } + process_exit(exit_code); + return exit_code; +} + +function jslint_phase1_split() { + +// PHASE 1. Split by newlines into . + + return; +} + +function jslint_phase2_lex(state) { + +// PHASE 2. Lex into . + + let { + artifact, + directive_list, + global_dict, + global_list, + line_list, + option_dict, + stop, + stop_at, + tenure, + test_cause, + token_global, + token_list, + warn, + warn_at + } = state; + let char; // The current character being lexed. + let column = 0; // The column number of the next character. + let from; // The starting column number of the token. + let from_mega; // The starting column of megastring. + let line = 0; // The line number of the next character. + let line_disable; // The starting line of "/*jslint-disable*/". + let line_mega; // The starting line of megastring. + let line_source = ""; // The remaining line source string. + let line_whole = ""; // The whole line source string. + let mode_digits_empty_string = 1; + let mode_digits_numeric_separator = 2; + let mode_directive = true; // true if directives are still allowed. + let mode_mega = false; // true if currently parsing a megastring + // ... literal. + let mode_regexp; // true if regular expression literal seen on + // ... this line. + let paren_backtrack_list = []; // List of most recent "(" tokens at any + // ... paren-depth. + let paren_depth = 0; // Keeps track of current paren-depth. + let snippet = ""; // A piece of string. + let token_1; // The first token. + let token_prv = token_global; // The previous token including + // ... comments. + let token_prv_expr = token_global; // The previous token excluding + // ... comments. + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions char_after, char_before, +// read_digits, and char_after_escape help in the parsing of literals. + + function char_after(match) { + +// Get the next character from the source line. Remove it from the line_source, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + +// test_cause: +// ["aa=/[", "char_after", "expected_a", "]", 5] +// ["aa=/aa{/", "char_after", "expected_a_b", "/", 8] + + return ( + char === "" + ? stop_at("expected_a", line, column - 1, match) + : stop_at("expected_a_b", line, column, match, char) + ); + } + char = line_source.slice(0, 1); + line_source = line_source.slice(1); + snippet += char || " "; + column += 1; + return char; + } + + function char_after_escape(extra) { + +// Validate char after escape "\\". + + char_after("\\"); + switch (char) { + case "": + +// test_cause: +// ["\"\\", "char_after_escape", "unclosed_string", "", 2] + + return stop_at("unclosed_string", line, column); + case "/": + return char_after(); + case "\\": + return char_after(); + case "`": + return char_after(); + case "b": + return char_after(); + case "f": + return char_after(); + case "n": + return char_after(); + case "r": + return char_after(); + case "t": + +// test_cause: +// ["\"\\/\\\\\\`\\b\\f\\n\\r\\t\"", "char_after_escape", "char_after", "", 0] + + test_cause("char_after"); + return char_after(); + case "u": + if (char_after("u") === "{") { + if (state.mode_json) { + +// test_cause: +// ["[\"\\u{12345}\"]", "char_after_escape", "unexpected_a", "{", 5] + + warn_at("unexpected_a", line, column, char); + } + if (read_digits("x", undefined) > 5) { + +// test_cause: +// ["\"\\u{123456}\"", "char_after_escape", "too_many_digits", "", 11] + + warn_at("too_many_digits", line, column); + } + if (char !== "}") { + +// test_cause: +// ["\"\\u{12345\"", "char_after_escape", "expected_a_before_b", "\"", 10] + + stop_at("expected_a_before_b", line, column, "}", char); + } + return char_after(); + } + char_before(); + if (read_digits("x", mode_digits_empty_string) < 4) { + +// test_cause: +// ["\"\\u0\"", "char_after_escape", "expected_four_digits", "", 5] + + warn_at("expected_four_digits", line, column); + } + return; + default: + if (extra && extra.indexOf(char) >= 0) { + return char_after(); + } + +// test_cause: +// ["\"\\0\"", "char_after_escape", "unexpected_a_before_b", "0", 3] + + warn_at("unexpected_a_before_b", line, column, "\\", char); + } + } + + function char_before() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the line_source. + + char = snippet.slice(-1); + line_source = char + line_source; + column -= char.length; + +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + return char; + } + + function check_numeric_separator(digits, column) { + +// This function will check for illegal numeric-separator in . + + digits.replace(( + jslint_rgx_numeric_separator_illegal + ), function (ignore, ii) { + +// test_cause: +// ["0x0_0_;", "check_numeric_separator", "illegal_num_separator", "", 6] +// ["0x0_0__0;", "check_numeric_separator", "illegal_num_separator", "", 6] +// ["aa=1_2_;", "check_numeric_separator", "illegal_num_separator", "", 7] +// ["aa=1_2__3;", "check_numeric_separator", "illegal_num_separator", "", 7] +// ["aa=1_2_n;", "check_numeric_separator", "illegal_num_separator", "", 7] + + warn_at("illegal_num_separator", line, column + ii + 1); + return ""; + }); + } + + function lex_comment() { + let body; + let ii = 0; + let jj = 0; + let the_comment; + +// Create a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + +// Create token from comment //.... + + if (snippet === "//") { + snippet = line_source; + line_source = ""; + the_comment = token_create("(comment)", snippet); + if (mode_mega) { + +// test_cause: +// ["`${//}`", "lex_comment", "unexpected_comment", "`", 4] + + warn("unexpected_comment", the_comment, "`"); + } + +// Create token from comment /*...*/. + + } else { + snippet = []; + if (line_source[0] === "/") { + +// test_cause: +// ["/*/", "lex_comment", "unexpected_a", "/", 2] + + warn_at("unexpected_a", line, column + ii, "/"); + } + +// Lex/loop through each line until "*/". + + while (true) { + // jslint_rgx_star_slash + ii = line_source.indexOf("*/"); + if (ii >= 0) { + break; + } + // jslint_rgx_slash_star + ii = line_source.indexOf("/*"); + if (ii >= 0) { + +// test_cause: +// ["/*/*", "lex_comment", "nested_comment", "", 2] + + warn_at("nested_comment", line, column + ii); + } + snippet.push(line_source); + line_source = read_line(); + if (line_source === undefined) { + +// test_cause: +// ["/*", "lex_comment", "unclosed_comment", "", 1] + + return stop_at("unclosed_comment", line, column); + } + } + jj = line_source.slice(0, ii).search( + jslint_rgx_slash_star_or_slash + ); + if (jj >= 0) { + +// test_cause: +// ["/*/**/", "lex_comment", "nested_comment", "", 2] + + warn_at("nested_comment", line, column + jj); + } + snippet.push(line_source.slice(0, ii)); + snippet = snippet.join(" "); + column += ii + 2; + line_source = line_source.slice(ii + 2); + the_comment = token_create("(comment)", snippet); + } + +// Uncompleted work comment. + + if (!option_dict.devel && jslint_rgx_todo.test(snippet)) { + +// test_cause: +// ["//todo", "lex_comment", "todo_comment", "(comment)", 1] //jslint-ignore-line + + warn("todo_comment", the_comment); + } + +// Lex directives in comment. + + [ + the_comment.directive, body + ] = Array.from(snippet.match(jslint_rgx_directive) || []).slice(1); + if (the_comment.directive === undefined) { + return the_comment; + } + directive_list.push(the_comment); + if (!mode_directive) { + +// test_cause: +// ["0\n/*global aa*/", "lex_comment", "misplaced_directive_a", "global", 1] + + warn_at("misplaced_directive_a", line, from, the_comment.directive); + return the_comment; + } + +// lex_directive(); +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + +// Lex/loop through each directive in /*...*/ + + ii = 0; + body.replace(jslint_rgx_directive_part, function ( + match0, + key, + val, + jj + ) { + if (ii !== jj) { + +// test_cause: +// ["/*jslint !*/", "lex_comment", "bad_directive_a", "!", 1] + + return stop("bad_directive_a", the_comment, body.slice(ii)); + } + if (match0 === "") { + return ""; + } + ii += match0.length; + switch (the_comment.directive) { + case "global": + if (val) { + +// test_cause: +// ["/*global aa:false*/", "lex_comment", "bad_option_a", "aa:false", 1] + + warn("bad_option_a", the_comment, key + ":" + val); + } + global_dict[key] = "user-defined"; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// state.mode_module = the_comment; + + break; + case "jslint": + if (!option_set_item(key, val !== "false")) { + +// test_cause: +// ["/*jslint undefined*/", "lex_comment", "bad_option_a", "undefined", 1] + + warn("bad_option_a", the_comment, key); + } + break; + case "property": + state.mode_property = true; + tenure[key] = true; + break; + } + return ""; + }); + return the_comment; + } + + function lex_megastring() { + let id; + let match; + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (mode_mega) { + +// test_cause: +// ["`${`", "lex_megastring", "expected_a_b", "`", 4] + + return stop_at("expected_a_b", line, column, "}", "`"); + } + from_mega = from; + line_mega = line; + mode_mega = true; + snippet = ""; + +// Parsing a mega literal is tricky. First create a ` token. + + token_create("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + while (true) { + match = line_source.match(jslint_rgx_mega) || { + "0": "", + index: 0 + }; + snippet += line_source.slice(0, match.index); + column += match.index; + line_source = line_source.slice(match.index); + match = match[0]; + switch (match) { + case "${": + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + token_create("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then create tokens that will become part of an expression until +// a } token is made. + + column += 2; + token_create("${"); + line_source = line_source.slice(2); + +// Lex/loop through each token inside megastring-expression `${...}`. + + while (true) { + id = lex_token().id; + if (id === "{") { + +// test_cause: +// ["`${{", "lex_megastring", "expected_a_b", "{", 4] + + return stop_at("expected_a_b", line, column, "}", "{"); + } + if (id === "}") { + break; + } + } + break; + case "\\": + snippet += line_source.slice(0, 2); + line_source = line_source.slice(2); + column += 2; + break; + case "`": + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + token_create("(string)", snippet).quote = "`"; + snippet = ""; + +// Terminate megastring with `. + + line_source = line_source.slice(1); + column += 1; + mode_mega = false; + return token_create("`"); + default: + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + snippet += line_source + "\n"; + if (read_line() === undefined) { + +// test_cause: +// ["`", "lex_megastring", "unclosed_mega", "", 1] + + return stop_at("unclosed_mega", line_mega, from_mega); + } + } + } + } + + function lex_number() { + let prefix = snippet; + +// PR-390 - Add numeric-separator check. + + check_numeric_separator(prefix, column - prefix.length); + char_after(); + switch (prefix === "0" && char) { + case "b": + case "o": + case "x": + read_digits(char, mode_digits_numeric_separator); + +// PR-351 - Ignore BigInt suffix 'n'. + + if (char === "n") { + char_after("n"); + } + break; + default: + if (char === ".") { + read_digits("d", mode_digits_numeric_separator); + } + if (char === "E" || char === "e") { + char_after(char); + if (char !== "+" && char !== "-") { + char_before(); + } + read_digits("d", mode_digits_numeric_separator); + } + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + +// test_cause: +// ["0a", "lex_number", "unexpected_a_after_b", "0", 2] + + return stop_at( + "unexpected_a_after_b", + line, + column, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + char_before(); + return token_create("(number)", snippet); + } + + function lex_regexp() { + +// Regexp +// Lex a regular expression literal. + + let flag; + let mode_regexp_multiline; + let result; + let value; + mode_regexp = true; + + function lex_regexp_bracketed() { + let mode_regexp_range; + +// RegExp +// Match a class. + + char_after("["); + if (char === "^") { + char_after("^"); + } + while (true) { + +// RegExp +// Match a character in a character class. + + switch (char) { + case "": + case "]": + +// test_cause: +// ["aa=/[", "lex_regexp_bracketed", "closer", "", 0] +// ["aa=/[]/", "lex_regexp_bracketed", "closer", "", 0] + + test_cause("closer"); + if (mode_regexp_range) { + +// test_cause: +// ["aa=/[0-]/", "lex_regexp_bracketed", "unexpected_a", "-", 7] + + warn_at("unexpected_a", line, column - 1, "-"); + } + return char_after("]"); + +// PR-362 - Relax regexp-warning against using . +// +// case " ": +// +// // test_cause: +// // ["aa=/[ ]/", "lex_regexp_bracketed", "expected_a_b", " ", 6] +// +// warn_at("expected_a_b", line, column, "\\u0020", " "); +// break; + + case "-": + case "/": + case "[": + case "^": + +// test_cause: +// ["aa=/[-]/", "lex_regexp_bracketed", "expected_a_before_b", "-", 6] +// ["aa=/[.^]/", "lex_regexp_bracketed", "expected_a_before_b", "^", 7] +// ["aa=/[/", "lex_regexp_bracketed", "expected_a_before_b", "/", 6] +// ["aa=/[\\\\/]/", "lex_regexp_bracketed", "expected_a_before_b", "/", 8] +// ["aa=/[\\\\[]/", "lex_regexp_bracketed", "expected_a_before_b", "[", 8] + + warn_at("expected_a_before_b", line, column, "\\", char); + break; + case "\\": + char_after_escape("BbDdSsWw-[]^"); + char_before(); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${/[`]/}`", "lex_regexp_bracketed", "unexpected_a", "`", 6] + + warn_at("unexpected_a", line, column, "`"); + } + break; + } + char_after(); + mode_regexp_range = false; + if (char === "-") { + +// RegExp +// Match a range of subclasses. + + mode_regexp_range = true; + char_after("-"); + } + } + } + + function lex_regexp_group() { + +// RegExp +// Lex sequence of characters in regexp. + + switch (char) { + case "": + warn_at("expected_regexp_factor_a", line, column, char); + break; + case ")": + warn_at("expected_regexp_factor_a", line, column, char); + break; + case "]": + +// test_cause: +// ["/ /", "lex_regexp_group", "expected_regexp_factor_a", "", 3] +// ["aa=/)", "lex_regexp_group", "expected_regexp_factor_a", ")", 5] +// ["aa=/]", "lex_regexp_group", "expected_regexp_factor_a", "]", 5] + + warn_at("expected_regexp_factor_a", line, column, char); + break; + } + while (true) { + switch (char) { + case "": + case ")": + case "/": + case "]": + return; + +// PR-362 - Relax regexp-warning against using . +// +// case " ": +// +// // test_cause: +// // ["aa=/ /", "lex_regexp_group", "expected_a_b", " ", 5] +// +// warn_at("expected_a_b", line, column, "\\s", " "); +// char_after(); +// break; + + case "$": + if (line_source[0] !== "/") { + mode_regexp_multiline = true; + } + char_after(); + break; + case "(": + +// RegExp +// Match a group that starts with left paren. + + char_after("("); + switch (char) { + case ":": + +// test_cause: +// ["aa=/(:)/", "lex_regexp_group", "expected_a_before_b", ":", 6] +// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5] + + warn_at("expected_a_before_b", line, column, "?", ":"); + break; + case "?": + char_after("?"); + switch (char) { + case "!": + +// PR-437 - Add grammar for regexp-named-capture-group. + + case "<": + case "=": + char_after(); + break; + default: + char_after(":"); + } + break; + } + +// RegExp +// Recurse lex_regexp_group(). + + lex_regexp_group(); + char_after(")"); + break; + case "*": + case "+": + case "?": + case "{": + case "}": + +// test_cause: +// ["aa=/+/", "lex_regexp_group", "expected_a_before_b", "+", 5] +// ["aa=/.**/", "lex_regexp_group", "expected_a_before_b", "*", 7] +// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5] +// ["aa=/{/", "lex_regexp_group", "expected_a_before_b", "{", 5] +// ["aa=/}/", "lex_regexp_group", "expected_a_before_b", "}", 5] + + warn_at("expected_a_before_b", line, column, "\\", char); + char_after(); + break; + case "[": + lex_regexp_bracketed(); + break; + case "\\": + +// test_cause: +// ["aa=/\\/", "lex_regexp_group", "escape", "", 0] + + test_cause("escape"); + +// PR-437 - Add grammar for regexp-named-backreference. + + char_after_escape("BbDdSsWw^${}[]():=!.|*+?k"); + break; + case "^": + if (snippet !== "^") { + mode_regexp_multiline = true; + } + char_after(); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${/`/}`", "lex_regexp_group", "unexpected_a", "`", 5] + + warn_at("unexpected_a", line, column, "`"); + } + char_after(); + break; + default: + char_after(); + } + +// RegExp +// Match an optional quantifier. + + switch (char) { + case "*": + case "+": + if (char_after(char) === "?") { + +// test_cause: +// ["aa=/.*?/", "lex_regexp_group", "?", "", 0] +// ["aa=/.+?/", "lex_regexp_group", "?", "", 0] + + test_cause("?"); + char_after("?"); + } + break; + case "?": + if (char_after("?") === "?") { + +// test_cause: +// ["aa=/.??/", "lex_regexp_group", "unexpected_a", "?", 7] + + warn_at("unexpected_a", line, column, char); + char_after("?"); + } + break; + case "{": + if (read_digits("d", mode_digits_empty_string) === 0) { + +// test_cause: +// ["aa=/aa{/", "lex_regexp_group", "expected_a_before_b", ",", 8] + + warn_at("expected_a_before_b", line, column, "0", ","); + } + if (char === ",") { + +// test_cause: +// ["aa=/.{,/", "lex_regexp_group", "comma", "", 0] + + test_cause("comma"); + read_digits("d", mode_digits_empty_string); + } + if (char_after("}") === "?") { + +// test_cause: +// ["aa=/.{0}?/", "lex_regexp_group", "unexpected_a", "?", 9] + + warn_at("unexpected_a", line, column, char); + char_after("?"); + } + break; + } + } + } + +// RegExp +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + char_after(); + if (char === "=") { + +// test_cause: +// ["aa=/=/", "lex_regexp", "expected_a_before_b", "=", 5] + + warn_at("expected_a_before_b", line, column, "\\", "="); + } + lex_regexp_group(); + +// RegExp +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + +// RegExp +// Make sure there is a closing slash. + + value = snippet; + char_after("/"); + +// RegExp +// Create flag. + + flag = empty(); + while ( + +// Regexp +// char is a letter. + + (char >= "a" && char <= "z\uffff") + || (char >= "A" && char <= "Z\uffff") + ) { + +// RegExp +// Process dangling flag letters. + + switch (!flag[char] && char) { + case "g": + break; + case "i": + break; + case "m": + break; + case "u": + break; + case "y": + +// test_cause: +// ["aa=/./gimuy", "lex_regexp", "flag", "", 0] + + test_cause("flag"); + break; + default: + +// test_cause: +// ["aa=/./gg", "lex_regexp", "unexpected_a", "g", 8] +// ["aa=/./z", "lex_regexp", "unexpected_a", "z", 7] + + warn_at("unexpected_a", line, column, char); + } + flag[char] = true; + char_after(); + } + char_before(); + if (char === "/" || char === "*") { + +// test_cause: +// ["aa=/.//", "lex_regexp", "unexpected_a", "/", 3] + + return stop_at("unexpected_a", line, from, char); + } + result = token_create("(regexp)", char); + result.flag = flag; + result.value = value; + if (mode_regexp_multiline && !flag.m) { + +// test_cause: +// ["aa=/$^/", "lex_regexp", "missing_m", "", 7] + + warn_at("missing_m", line, column); + } + return result; + } + + function lex_slash_or_regexp() { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + let the_token; + switch ( + token_prv_expr.identifier + && !token_prv_expr.dot + && token_prv_expr.id + ) { + case "case": + case "delete": + case "in": + case "instanceof": + case "new": + case "typeof": + case "void": + case "yield": + the_token = lex_regexp(); + +// test_cause: +// ["case /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6] +// ["delete /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8] +// ["in /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 4] +// ["instanceof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 12] +// ["new /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 5] +// ["typeof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8] +// ["void /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6] +// ["yield /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 7] + + return stop("unexpected_a", the_token); + case "return": + return lex_regexp(); + } + switch (!token_prv_expr.identifier && token_prv_expr.id.slice(-1)) { + case "!": + case "%": + case "&": + case "*": + case "+": + case "-": + case "/": + case ";": + case "<": + case ">": + case "^": + case "{": + case "|": + case "}": + case "~": + the_token = lex_regexp(); + +// test_cause: +// ["!/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["%/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["&/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["+/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["-/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["0 * /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5] +// ["0 / /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5] +// [";/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["^/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["{/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["|/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["}/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["~/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] + + warn("wrap_regexp", the_token); + return the_token; + case "(": + case ",": + case ":": + case "=": + case "?": + case "[": + +// test_cause: +// ["(/./", "lex_slash_or_regexp", "recurse", "", 0] +// [",/./", "lex_slash_or_regexp", "recurse", "", 0] +// [":/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["=/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["?/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["aa[/./", "lex_slash_or_regexp", "recurse", "", 0] + + test_cause("recurse"); + return lex_regexp(); + } + if (line_source[0] === "=") { + column += 1; + line_source = line_source.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + return token_create(snippet); + } + + function lex_string(quote) { + +// Create a string token. + + let the_token; + if (!option_dict.single && quote === "'") { + +// test_cause: +// ["''", "lex_string", "use_double", "", 1] + + warn_at("use_double", line, column); + } + snippet = ""; + char_after(); + +// Lex/loop through each character in "...". + + while (true) { + switch (char) { + case "": + +// test_cause: +// ["\"", "lex_string", "unclosed_string", "", 1] + + return stop_at("unclosed_string", line, column); + case "\\": + char_after_escape(quote); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${\"`\"}`", "lex_string", "unexpected_a", "`", 5] + + warn_at("unexpected_a", line, column, "`"); + } + char_after("`"); + break; + case quote: + +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + the_token = token_create("(string)", snippet); + the_token.quote = quote; + return the_token; + default: + char_after(); + } + } + } + + function lex_token() { + let match; + +// Lex/loop through each whitespace. + + while (true) { + +// Lex/loop through each blank-line. + + while (!line_source) { + line_source = read_line(); + from = 0; + if (line_source === undefined) { + return ( + mode_mega + +// test_cause: +// ["`${//}`", "lex_token", "unclosed_mega", "", 1] + + ? stop_at("unclosed_mega", line_mega, from_mega) + : line_disable !== undefined + +// test_cause: +// ["/*jslint-disable*/", "lex_token", "unclosed_disable", "", 1] + + ? stop_at("unclosed_disable", line_disable) + : token_create("(end)") + ); + } + } + from = column; + match = line_source.match(jslint_rgx_token); + +// match[1] token +// match[2] whitespace +// match[3] identifier +// match[4] number +// match[5] rest + + if (!match) { + +// test_cause: +// ["#", "lex_token", "unexpected_char_a", "#", 1] + + return stop_at( + "unexpected_char_a", + line, + column, + line_source[0] + ); + } + snippet = match[1]; + column += snippet.length; + line_source = match[5]; + if (!match[2]) { + break; + } + } + +// The token is an identifier. + + if (match[3]) { + return token_create(snippet, undefined, true); + } + +// Create token from number. + + if (match[4]) { + return lex_number(); + } + +// Create token from string "..." or '...'. + + if (snippet === "\"" || snippet === "'") { + return lex_string(snippet); + } + +// Create token from megastring `...`. + + if (snippet === "`") { + return lex_megastring(); + } + +// Create token from comment /*...*/ or //.... + + if (snippet === "/*" || snippet === "//") { + return lex_comment(); + } + +// Create token from slash /. + + if (snippet === "/") { + return lex_slash_or_regexp(); + } + return token_create(snippet); + } + + function option_set_item(key, val) { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + switch (key) { + case "beta": // Enable experimental warnings. + case "bitwise": // Allow bitwise operator. + case "browser": // Assume browser environment. + case "convert": // Allow conversion operator. + case "couch": // Assume CouchDb environment. + case "devel": // Allow console.log() and friends. + case "ecma": // Assume ECMAScript environment. + case "eval": // Allow eval(). + case "fart": // Allow complex fat-arrow. + case "for": // Allow for-statement. + case "getset": // Allow get() and set(). + case "indent2": // Use 2-space indent. + case "long": // Allow long lines. + case "node": // Assume Node.js environment. + case "nomen": // Allow weird property name. + case "single": // Allow single-quote strings. + case "subscript": // Allow identifier in subscript-notation. + case "test_cause": // Test jslint's causes. + case "test_internal_error": // Test jslint's internal-error + // ... handling-ability. + case "this": // Allow 'this'. + case "trace": // Include jslint stack-trace in warnings. + case "unordered": // Allow unordered cases, params, properties, + // ... variables, and exports. + case "variable": // Allow unordered const and let declarations + // ... that are not at top of function-scope. + case "white": // Allow messy whitespace. + option_dict[key] = val; + break; + +// PR-404 - Alias "evil" to jslint-directive "eval" for backwards-compat. + + case "evil": + return option_set_item("eval", val); + +// PR-404 - Alias "nomen" to jslint-directive "name" for backwards-compat. + + case "name": + return option_set_item("nomen", val); + default: + return false; + } + +// Initialize global-variables. + + switch (val && key) { + +// Assign global browser variables to global_dict. +/* +// /\*jslint beta, browser, devel*\/ +console.log(JSON.stringify(Object.keys(window).sort(), undefined, 4)); +*/ + + case "browser": + object_assign_from_list(global_dict, [ + +// Shared with Node.js. + + "AbortController", + // "Buffer", + // "Crypto", + // "CryptoKey", + "Event", + "EventTarget", + "MessageChannel", + "MessageEvent", + "MessagePort", + // "Request", + // "Response", + // "SubtleCrypto", + "TextDecoder", + "TextEncoder", + "URL", + "URLSearchParams", + "WebAssembly", + // "__dirname", + // "__filename", + // "atob", + // "btoa", + // "clearImmediate", + "clearInterval", + "clearTimeout", + // "console", + // "crypto", + // "exports", + // "fetch", + // "global", + // "module", + "performance", + // "process", + "queueMicrotask", + // "require", + // "setImmediate", + "setInterval", + "setTimeout", + +// Web worker only. +// https://github.com/mdn/content/blob/main/files/en-us/web/api +// /workerglobalscope/index.md + + "importScripts", + +// Window. + + "Blob", + // "CharacterData", + // "DocumentType", + // "Element", + // "Event", + "FileReader", + // "FontFace", + "FormData", + "IntersectionObserver", + "MutationObserver", + // "Storage", + // "TextDecoder", + // "TextEncoder", + // "URL", + "Worker", + "XMLHttpRequest", + // "caches", + // "clearInterval", + // "clearTimeout", + "document", + // "event", + "fetch", + // "history", + "indexedDb", + "localStorage", + "location", + // "name", + "navigator", + "postMessage", + // "screen", + "sessionStorage", + // "setInterval", + // "setTimeout", + "structuredClone", + "window" + ], "browser"); + break; + +// https://docs.couchdb.org/en/stable/query-server/javascript.html#javascript + + case "couch": + object_assign_from_list(global_dict, [ + "emit", + "getRow", + "isArray", + "log", + "provides", + "registerType", + "require", + "send", + "start", + "sum", + "toJSON" + ], "CouchDb"); + break; + case "devel": + object_assign_from_list(global_dict, [ + "alert", "confirm", "console", "prompt" + ], "development"); + break; + +// These are the globals that are provided by the language standard. +// Assign global ECMAScript variables to global_dict. +/* +node --input-type=module --eval ' +// /\*jslint beta, node*\/ +import https from "https"; +(async function () { + let dict = {import: true}; + let result = ""; + await new Promise(function (resolve) { + https.get(( + "https://raw.githubusercontent.com/mdn/content/main/files" + + "/en-us/web/javascript/reference/global_objects/index.md" + ), function (res) { + res.on("data", function (chunk) { + result += chunk; + }).on("end", resolve).setEncoding("utf8"); + }); + }); + result.replace(( + /\n- \{\{JSxRef\("(?:Global_Objects\/)?([^"\/]+?)"/g + ), function (ignore, key) { + if (globalThis.hasOwnProperty(key)) { + dict[key] = true; + } + return ""; + }); + console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4)); +}()); +' +*/ + + case "ecma": + object_assign_from_list(global_dict, [ + "AggregateError", + "Array", + "ArrayBuffer", + "Atomics", + "BigInt", + "BigInt64Array", + "BigUint64Array", + "Boolean", + "DataView", + "Date", + "Error", + "EvalError", + "Float32Array", + "Float64Array", + "Function", + "Infinity", + "Int16Array", + "Int32Array", + "Int8Array", + "Intl", + "JSON", + "Map", + "Math", + "NaN", + "Number", + "Object", + "Promise", + "Proxy", + "RangeError", + "ReferenceError", + "Reflect", + "RegExp", + "Set", + "SharedArrayBuffer", + "String", + "Symbol", + "SyntaxError", + "TypeError", + "URIError", + "Uint16Array", + "Uint32Array", + "Uint8Array", + "Uint8ClampedArray", + "WeakMap", + "WeakSet", + "WebAssembly", + "decodeURI", + "decodeURIComponent", + "encodeURI", + "encodeURIComponent", + "eval", + "globalThis", + "import", + "isFinite", + "isNaN", + "parseFloat", + "parseInt", + "undefined" + ], "ECMAScript"); + break; + +// Assign global Node.js variables to global_dict. +/* +node --input-type=module --eval ' +// /\*jslint beta, node*\/ +import moduleHttps from "https"; +(async function () { + let dict = Object.create(null); + let result = ""; + await new Promise(function (resolve) { + moduleHttps.get(( + "https://raw.githubusercontent.com/nodejs/node/v16.x/doc/api" + + "/globals.md" + ), function (res) { + res.on("data", function (chunk) { + result += chunk; + }).on("end", resolve).setEncoding("utf8"); + }); + }); + result.replace(( + /\n(?:\* \[`|## |## Class: )`\w+/g + ), function (match0) { + dict[match0.split("`")[1]] = true; + return ""; + }); + console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4)); +}()); +' +*/ + + case "node": + object_assign_from_list(global_dict, [ + "AbortController", + "Buffer", + // "Crypto", + // "CryptoKey", + "Event", + "EventTarget", + "MessageChannel", + "MessageEvent", + "MessagePort", + // "Request", + // "Response", + // "SubtleCrypto", + "TextDecoder", + "TextEncoder", + "URL", + "URLSearchParams", + "WebAssembly", + "__dirname", + "__filename", + // "atob", + // "btoa", + "clearImmediate", + "clearInterval", + "clearTimeout", + "console", + // "crypto", + "exports", + // "fetch", + "global", + "module", + "performance", + "process", + "queueMicrotask", + "require", + "setImmediate", + "setInterval", + "setTimeout" + ], "Node.js"); + break; + } + return true; + } + + function read_digits(base, mode) { + let digits = line_source.match( + base === "b" + ? jslint_rgx_digits_bits + : base === "o" + ? jslint_rgx_digits_octals + : base === "x" + ? jslint_rgx_digits_hexs + : jslint_rgx_digits_decimals + )[0]; + if ( + (mode !== mode_digits_empty_string && digits.length === 0) + || digits[0] === "_" + ) { + +// test_cause: +// ["0x", "read_digits", "expected_digits_after_a", "0x", 2] +// ["0x_", "read_digits", "expected_digits_after_a", "0x", 2] + + warn_at("expected_digits_after_a", line, column, snippet); + } + +// PR-390 - Add numeric-separator check. + + if (mode === mode_digits_numeric_separator) { + check_numeric_separator(digits, column); + } else if (digits.indexOf("_") >= 0) { + +// test_cause: +// ["\"\\u{1_2}\"", "read_digits", "illegal_num_separator", "", 6] + + warn_at( + "illegal_num_separator", + line, + column + digits.indexOf("_") + 1 + ); + } + column += digits.length; + line_source = line_source.slice(digits.length); + snippet += digits; + char_after(); + return digits.length; + } + + function read_line() { + +// Put the next line of source in line_source. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + if ( + !option_dict.long + && line_whole.length > 80 + && line_disable === undefined + && !state.mode_json + && token_1 + && !mode_regexp + ) { + +// test_cause: +// ["/////////////////////////////////////////////////////////////////////////////////", "read_line", "too_long", "", 1] //jslint-ignore-line + + warn_at("too_long", line); + } + column = 0; + line += 1; + mode_regexp = false; + line_source = undefined; + line_whole = ""; + if (line_list[line] === undefined) { + return line_source; + } + line_source = line_list[line].line_source; + line_whole = line_source; + +// Scan each line for following ignore-directives: +// "/*jslint-disable*/" +// "/*jslint-enable*/" +// "//jslint-ignore-line" + + if (line_source === "/*jslint-disable*/") { + +// test_cause: +// ["/*jslint-disable*/", "read_line", "jslint_disable", "", 0] + + test_cause("jslint_disable"); + line_disable = line; + } else if (line_source === "/*jslint-enable*/") { + if (line_disable === undefined) { + +// test_cause: +// ["/*jslint-enable*/", "read_line", "unopened_enable", "", 1] + + stop_at("unopened_enable", line); + } + line_disable = undefined; + } else if ( + line_source.endsWith(" //jslint-ignore-line") + || line_source.endsWith(" //jslint-quiet") + ) { + +// test_cause: +// ["0 //jslint-ignore-line", "read_line", "jslint_ignore_line", "", 0] + + test_cause("jslint_ignore_line"); + line_list[line].directive_ignore_line = true; + } + if (line_disable !== undefined) { + +// test_cause: +// ["/*jslint-disable*/\n0", "read_line", "line_disable", "", 0] + + test_cause("line_disable"); + line_source = ""; + } + // jslint_rgx_tab + if (line_source.indexOf("\t") >= 0) { + if (!option_dict.white) { + +// test_cause: +// ["\t", "read_line", "use_spaces", "", 1] + + warn_at("use_spaces", line, line_source.indexOf("\t") + 1); + } + line_source = line_source.replace(jslint_rgx_tab, " "); + } + if (!option_dict.white && line_source.endsWith(" ")) { + +// test_cause: +// [" ", "read_line", "unexpected_trailing_space", "", 1] + + warn_at("unexpected_trailing_space", line, line_source.length - 1); + } + return line_source; + } + + function token_create(id, value, identifier) { + +// Create the token object and append it to token_list. + + let the_token = { + from, + id, + identifier: Boolean(identifier), + line, + nr: token_list.length, + thru: column, + value + }; + token_list.push(the_token); + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + mode_directive = false; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option_dict.white. + + if ( + token_prv.line === line + && token_prv.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (token_prv.id === "(comment)" || token_prv.id === "(regexp)") + ) { + +// test_cause: +// ["/**//**/", "token_create", "expected_space_a_b", "(comment)", 5] + + warn( + "expected_space_a_b", + the_token, + artifact(token_prv), + artifact(the_token) + ); + } + if (token_prv.id === "." && id === "(number)") { + +// test_cause: +// [".0", "token_create", "expected_a_before_b", ".", 1] + + warn("expected_a_before_b", token_prv, "0", "."); + } + if (token_prv_expr.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart. +// Farts are now detected by keeping a list of most recent "(" tokens at any +// given depth. When a "=>" token is encountered, the most recent "(" token at +// current depth is marked as a fart. + + switch (id) { + case "(": + paren_backtrack_list[paren_depth] = the_token; + paren_depth += 1; + break; + case ")": + paren_depth -= 1; + break; + case "=>": + if ( + token_prv_expr.id === ")" + && paren_backtrack_list[paren_depth] + ) { + paren_backtrack_list[paren_depth].fart = the_token; + } + break; + } + +// The previous token is used to detect adjacency problems. + + token_prv = the_token; + +// The token_prv_expr token is a previous token that was not a comment. +// The token_prv_expr token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (token_prv.id !== "(comment)") { + token_prv_expr = token_prv; + } + return the_token; + } + +// Init global_dict and option_dict. + + option_set_item("ecma", true); + Object.keys(option_dict).sort().forEach(function (key) { + option_set_item(key, option_dict[key] === true); + }); + object_assign_from_list(global_dict, global_list, "user-defined"); + +// Scan first line for "#!" and ignore it. + + if (line_list[jslint_fudge].line_source.startsWith("#!")) { + line += 1; + state.mode_shebang = true; + } + token_1 = lex_token(); + state.mode_json = token_1.id === "{" || token_1.id === "["; + +// Lex/loop through each token until (end). + + while (true) { + if (lex_token().id === "(end)") { + break; + } + } +} + +function jslint_phase3_parse(state) { + +// PHASE 3. Parse into using the Pratt-parser. + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + + let anon = "anonymous"; // The guessed name for anonymous functions. + let { + artifact, + catch_list, + catch_stack, + export_dict, + function_list, + function_stack, + global_dict, + import_list, + is_equal, + option_dict, + property_dict, + stop, + syntax_dict, + tenure, + test_cause, + token_global, + token_list, + warn, + warn_at + } = state; + let catchage = catch_stack[0]; // The current catch-block. + let functionage = token_global; // The current function. + let mode_var; // "var" if using var; "let" if using let. + let token_ii = 0; // The number of the next token. + let token_now = token_global; // The current token being examined in + // ... the parse. + let token_nxt = token_global; // The next token to be examined in + // ... . + + function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if ( + token_now.identifier + && token_now.id !== "function" + && token_now.id !== "async" + ) { + anon = token_now.id; + } else if ( + token_now.id === "(string)" + && jslint_rgx_identifier.test(token_now.value) + ) { + anon = token_now.value; + } + +// Attempt to match token_nxt with an expected id. + + if (id !== undefined && token_nxt.id !== id) { + return ( + match === undefined + +// test_cause: +// ["{0:0}", "advance", "expected_a_b", "0", 2] + + ? stop("expected_a_b", token_nxt, id, artifact()) + +// test_cause: +// ["{\"aa\":0", "advance", "expected_a_b_from_c_d", "{", 1] + + : stop( + "expected_a_b_from_c_d", + token_nxt, + id, + artifact(match), + match.line, + artifact() + ) + ); + } + +// Promote the tokens, skipping comments. + + token_now = token_nxt; + while (true) { + token_nxt = token_list[token_ii]; + state.token_nxt = token_nxt; + token_ii += 1; + if (token_nxt.id !== "(comment)") { + if (token_nxt.id === "(end)") { + token_ii -= 1; + } + break; + } + if (state.mode_json) { + +// test_cause: +// ["[//]", "advance", "unexpected_a", "(comment)", 2] + + warn("unexpected_a"); + } + } + } + + function assignment(id) { + +// Create an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led_infix = function (left) { + const the_token = token_now; + let right; + the_token.arity = "assignment"; + right = parse_expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "preassign" + || right.arity === "postassign" + ) { + warn("unexpected_a", right); + } + check_mutation(left); + return the_token; + }; + return the_symbol; + } + + function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token_now; + if (special !== "body") { + functionage.statement_prv = the_block; + } + the_block.arity = "statement"; + the_block.body = special === "body"; + +// Top level function bodies may include the "use strict" pragma. + + if ( + special === "body" + && function_stack.length === 1 + && token_nxt.value === "use strict" + ) { + token_nxt.statement = true; + advance("(string)"); + advance(";"); + } + stmts = parse_statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option_dict.devel && special !== "ignore") { + +// test_cause: +// ["function aa(){}", "block", "empty_block", "{", 14] + + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; + } + + function check_left(left, right) { + +// Warn if the left is not one of these: +// ?. +// ?: +// e() +// e.b +// e[b] +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !check_left(left.expression[1]) + && !check_left(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "?." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right || token_nxt); + return false; + } + return true; + } + + function check_mutation(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + +// test_cause: +// ["0=0", "check_mutation", "bad_assignment_a", "0", 1] + + warn("bad_assignment_a", the_thing); + return false; + } + return true; + } + + function check_not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === token_global) { + +// test_cause: +// [" +// while(0){} +// ", "check_not_top_level", "unexpected_at_top_level_a", "while", 1] + + warn("unexpected_at_top_level_a", thing); + } + } + + function check_ordered(type, token_list) { + +// This function will warn if is unordered. + + token_list.reduce(function (aa, token) { + const bb = artifact(token); + if (!option_dict.unordered && aa > bb) { + warn("expected_a_b_before_c_d", token, type, bb, type, aa); + } + return bb; + }, ""); + } + + function check_ordered_case(case_list) { + +// This function will warn if is unordered. + + case_list.filter(noop).map(function (token) { + switch (token.identifier || token.id) { + case "(number)": + return { + order: 1, + token, + type: "number", + value: Number(artifact(token)) + }; + case "(string)": + return { + order: 2, + token, + type: "string", + value: artifact(token) + }; + case true: + return { + order: 3, + token, + type: "identifier", + value: artifact(token) + }; + } + }).reduce(function (aa, bb) { + if ( + +// PR-419 - Hide warning about unordered case-statements behind beta-flag. + + option_dict.beta + && !option_dict.unordered + && aa && bb + && ( + aa.order > bb.order + || (aa.order === bb.order && aa.value > bb.value) + ) + ) { + warn( + "expected_a_b_before_c_d", + bb.token, + `case-${bb.type}`, + bb.value, + `case-${aa.type}`, + aa.value + ); + } + return bb; + }, undefined); + } + + function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = token_nxt; + let the_value; + +// test_cause: +// ["do{}while()", "condition", "", "", 0] +// ["if(){}", "condition", "", "", 0] +// ["while(){}", "condition", "", "", 0] + + test_cause(""); + the_paren.free = true; + advance("("); + the_value = parse_expression(0); + advance(")"); + if (the_value.wrapped === true) { + +// test_cause: +// ["while((0)){}", "condition", "unexpected_a", "(", 6] + + warn("unexpected_a", the_paren); + } + +// Check for anticondition. + + switch (the_value.id) { + case "%": + warn("unexpected_a", the_value); + break; + case "&": + warn("unexpected_a", the_value); + break; + case "(number)": + warn("unexpected_a", the_value); + break; + case "(string)": + warn("unexpected_a", the_value); + break; + case "*": + warn("unexpected_a", the_value); + break; + case "+": + warn("unexpected_a", the_value); + break; + case "-": + warn("unexpected_a", the_value); + break; + case "/": + warn("unexpected_a", the_value); + break; + case "<<": + warn("unexpected_a", the_value); + break; + case ">>": + warn("unexpected_a", the_value); + break; + case ">>>": + warn("unexpected_a", the_value); + break; + case "?": + warn("unexpected_a", the_value); + break; + case "^": + warn("unexpected_a", the_value); + break; + case "typeof": + warn("unexpected_a", the_value); + break; + case "|": + warn("unexpected_a", the_value); + break; + case "~": + +// test_cause: +// ["if(0%0){}", "condition", "unexpected_a", "%", 5] +// ["if(0&0){}", "condition", "unexpected_a", "&", 5] +// ["if(0){}", "condition", "unexpected_a", "0", 4] +// ["if(0*0){}", "condition", "unexpected_a", "*", 5] +// ["if(0+0){}", "condition", "unexpected_a", "+", 5] +// ["if(0-0){}", "condition", "unexpected_a", "-", 5] +// ["if(0/0){}", "condition", "unexpected_a", "/", 5] +// ["if(0<<0){}", "condition", "unexpected_a", "<<", 5] +// ["if(0>>0){}", "condition", "unexpected_a", ">>", 5] +// ["if(0>>>0){}", "condition", "unexpected_a", ">>>", 5] +// ["if(0?0:0){}", "condition", "unexpected_a", "?", 5] +// ["if(0^0){}", "condition", "unexpected_a", "^", 5] +// ["if(0|0){}", "condition", "unexpected_a", "|", 5] +// ["if(\"aa\"){}", "condition", "unexpected_a", "aa", 4] +// ["if(typeof 0){}", "condition", "unexpected_a", "typeof", 4] +// ["if(~0){}", "condition", "unexpected_a", "~", 4] + + warn("unexpected_a", the_value); + break; + } + return the_value; + } + + function constant(id, type, value) { + +// Create a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud_prefix = ( + typeof value === "function" + ? value + : function () { + token_now.constant = true; + if (value !== undefined) { + token_now.value = value; + } + return token_now; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; + } + + function constant_Function() { + if (!option_dict.eval) { + +// test_cause: +// ["Function", "constant_Function", "unexpected_a", "Function", 1] + + warn("unexpected_a", token_now); + } else if (token_nxt.id !== "(") { + +// test_cause: +// [" +// /*jslint eval*/ +// Function +// ", "constant_Function", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "(", artifact()); + } + return token_now; + } + + function constant_arguments() { + +// test_cause: +// ["arguments", "constant_arguments", "unexpected_a", "arguments", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function constant_eval() { + if (!option_dict.eval) { + +// test_cause: +// ["eval", "constant_eval", "unexpected_a", "eval", 1] + + warn("unexpected_a", token_now); + } else if (token_nxt.id !== "(") { + +// test_cause: +// ["/*jslint eval*/\neval", "constant_eval", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "(", artifact()); + } + return token_now; + } + + function constant_ignore() { + +// test_cause: +// ["ignore", "constant_ignore", "unexpected_a", "ignore", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function constant_isInfinite() { + +// test_cause: +// ["isFinite", "constant_isInfinite", "expected_a_b", "isFinite", 1] + + warn("expected_a_b", token_now, "Number.isFinite", "isFinite"); + return token_now; + } + + function constant_isNaN() { + +// test_cause: +// ["isNaN(0)", "constant_isNaN", "number_isNaN", "isNaN", 1] + + warn("number_isNaN", token_now); + return token_now; + } + + function constant_this() { + if (!option_dict.this) { + +// test_cause: +// ["this", "constant_this", "unexpected_a", "this", 1] + + warn("unexpected_a", token_now); + } + return token_now; + } + + function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + let earlier; + let id = name.id; + +// Reserved words may not be enrolled. + + if (syntax_dict[id] !== undefined && id !== "ignore") { + +// test_cause: +// ["let undefined", "enroll", "reserved_a", "undefined", 5] + + warn("reserved_a", name); + return; + } + +// Has the name been enrolled in this context? + + earlier = functionage.context[id] || catchage.context[id]; + if (earlier) { + +// test_cause: +// ["let aa;let aa", "enroll", "redefinition_a_b", "1", 12] + + warn("redefinition_a_b", name, id, earlier.line); + return; + } + +// Has the name been enrolled in an outer context? + + function_stack.forEach(function ({ + context + }) { + earlier = context[id] || earlier; + }); + if (earlier && id === "ignore") { + if (earlier.role === "variable") { + +// test_cause: +// ["let ignore;function aa(ignore){}", "enroll", "redefinition_a_b", "1", 24] + + warn("redefinition_a_b", name, id, earlier.line); + } + } else if ( + earlier + && role !== "parameter" && role !== "function" + && (role !== "exception" || earlier.role !== "exception") + ) { + +// test_cause: +// [" +// function aa(){try{aa();}catch(aa){aa();}} +// ", "enroll", "redefinition_a_b", "1", 31] +// ["function aa(){var aa;}", "enroll", "redefinition_a_b", "1", 19] + + warn("redefinition_a_b", name, id, earlier.line); + } else if ( + option_dict.beta + && global_dict[id] + && role !== "parameter" + ) { + +// test_cause: +// ["let Array", "enroll", "redefinition_global_a_b", "Array", 5] + + warn("redefinition_global_a_b", name, global_dict[id], id); + } + +// Enroll it. + + Object.assign(name, { + dead: true, + init: false, + parent: ( + role === "exception" + ? catchage + : functionage + ), + readonly, + role, + used: 0 + }); + name.parent.context[id] = name; + } + + function infix(bp, id, f) { + +// Create an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led_infix = function (left) { + const the_token = token_now; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, parse_expression(bp)]; + return the_token; + }; + return the_symbol; + } + + function infix_dot(left) { + const the_token = token_now; + let name = token_nxt; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "flat" + && name.id !== "flatMap" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + +// test_cause: +// ["\"\".aa", "check_left", "unexpected_a", ".", 3] + + check_left(left, the_token); + } + if (!name.identifier) { + +// test_cause: +// ["aa.0", "infix_dot", "expected_identifier_a", "0", 4] + + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; + } + + function infix_fart_unwrapped() { + +// test_cause: +// ["aa=>0", "infix_fart_unwrapped", "wrap_fart_parameter", "=>", 3] + + return stop("wrap_fart_parameter", token_now); + } + + function infix_grave(left) { + const the_tick = prefix_tick(); + +// test_cause: +// ["0``", "check_left", "unexpected_a", "`", 2] + + check_left(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; + } + + function infix_lbracket(left) { + const the_token = token_now; + let name; + let the_subscript = parse_expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + name = survey(the_subscript); + +// PR-404 - Add new directive "subscript" to play nice with Google Closure. + + if (!option_dict.subscript && jslint_rgx_identifier.test(name)) { + +// test_cause: +// ["aa[`aa`]", "infix_lbracket", "subscript_a", "aa", 4] + + warn("subscript_a", the_subscript, name); + } + } + +// test_cause: +// ["0[0]", "check_left", "unexpected_a", "[", 2] + + check_left(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; + } + + function infix_lparen(left) { + const the_paren = token_now; + let ellipsis; + let the_argument; + if (left.id !== "function") { + +// test_cause: +// ["(0?0:0)()", "check_left", "unexpected_a", "(", 8] +// ["0()", "check_left", "unexpected_a", "(", 2] + + check_left(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (token_nxt.id !== ")") { + +// Parse/loop through each token in expression (...). + + while (true) { + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = parse_expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + +// test_cause: +// ["aa(0)", "infix_lparen", "free", "", 0] + + test_cause("free"); + the_paren.free = true; + if (the_argument.wrapped === true) { + +// test_cause: +// ["aa((0))", "infix_lparen", "unexpected_a", "(", 3] + + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + +// test_cause: +// ["aa()", "infix_lparen", "not_free", "", 0] +// ["aa(0,0)", "infix_lparen", "not_free", "", 0] + + test_cause("not_free"); + the_paren.free = false; + } + return the_paren; + } + + function infix_option_chain(left) { + const the_token = token_now; + let name = token_nxt; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + +// test_cause: +// ["(0+0)?.0", "infix_option_chain", "check_left", "", 0] + + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + test_cause("check_left"); + +// test_cause: +// ["(/./)?.0", "check_left", "unexpected_a", "?.", 6] +// ["\"aa\"?.0", "check_left", "unexpected_a", "?.", 5] +// ["aa=[]?.aa", "check_left", "unexpected_a", "?.", 6] + + check_left(left, the_token); + } + +// Issue #468 - Fix optional dynamic-property/function-call not recognized. + + if (name.id === "[" || name.id === "(") { + test_cause("dyn_prop_or_call"); + +// test_cause: +// ["aa?.(bb)", "infix_option_chain", "dyn_prop_or_call", "", 0] +// ["aa?.[bb]", "infix_option_chain", "dyn_prop_or_call", "", 0] + + return left; + } + if (!name.identifier) { + +// test_cause: +// ["aa?.0", "infix_option_chain", "expected_identifier_a", "0", 5] + + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; + } + + function infixr(bp, id) { + +// Create a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led_infix = function parse_infixr_led(left) { + const the_token = token_now; + +// test_cause: +// ["0**0", "parse_infixr_led", "led_infix", "", 0] + + test_cause("led_infix"); + the_token.arity = "binary"; + the_token.expression = [left, parse_expression(bp - 1)]; + return the_token; + }; + return the_symbol; + } + + function parse_expression(rbp, initial) { + +// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it +// is looking for ad hoc lint patterns. We add .fud_stmt to Pratt's model, which +// is like .nud_prefix except that it is only used on the first token of a +// statement. Having .fud_stmt makes it much easier to define statement-oriented +// languages like JavaScript. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// .nud_prefix Null denotation. The prefix handler. +// .fud_stmt First null denotation. The statement handler. +// .led_infix Left denotation. The infix/postfix handler. +// lbp Left binding power of infix operator. It tells us how strongly +// the operator binds to the argument at its left. +// rbp Right binding power. + +// It processes a nud_prefix (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop (it +// consumes tokens until it meets a token whose lbp <= rbp). Specifically, it +// means that it collects all tokens that bind together before returning to the +// operator that called it. It returns the expression's parse tree. + +// For example, "3 + 1 * 2 * 4 + 5" +// parses into +// { +// "id": "+", +// "expression": [ +// { +// "id": "+", +// "expression": [ +// { +// "id": "(number)", +// "value": "3" +// }, +// { +// "id": "*", +// "expression": [ +// { +// "id": "*", +// "expression": [ +// { +// "id": "(number)", +// "value": "1" +// }, +// { +// "id": "(number)", +// "value": "2" +// } +// ] +// }, +// { +// "id": "(number)", +// "value": "4" +// } +// ] +// } +// ] +// }, +// { +// "id": "(number)", +// "value": "5" +// } +// ] +// } + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement. + + if (!initial) { + advance(); + } + the_symbol = syntax_dict[token_now.id]; + if (the_symbol !== undefined && the_symbol.nud_prefix !== undefined) { + +// test_cause: +// ["0", "parse_expression", "symbol", "", 0] + + test_cause("symbol"); + left = the_symbol.nud_prefix(); + } else if (token_now.identifier) { + +// test_cause: +// ["aa", "parse_expression", "identifier", "", 0] + + test_cause("identifier"); + left = token_now; + left.arity = "variable"; + } else { + +// test_cause: +// ["!", "parse_expression", "unexpected_a", "(end)", 1] +// ["/./", "parse_expression", "unexpected_a", "/", 1] +// ["let aa=`${}`;", "parse_expression", "unexpected_a", "}", 11] + + return stop("unexpected_a", token_now); + } + +// Parse/loop through each symbol in expression. + + while (true) { + the_symbol = syntax_dict[token_nxt.id]; + if ( + the_symbol === undefined + || the_symbol.led_infix === undefined + || the_symbol.lbp <= rbp + ) { + break; + } + advance(); + left = the_symbol.led_infix(left); + } + return left; + } + + function parse_fart(the_fart) { + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + Object.assign(the_fart, { + arity: "binary", + context: empty(), + finally: 0, + level: functionage.level + 1, + loop: 0, + name: anon, + switch: 0, + try: 0 + }); + +// PR-384 - Relax warning "function_in_loop". +// +// if (functionage.loop > 0) { + +// // test_cause: +// // ["while(0){aa.map(()=>0);}", "parse_fart", "function_in_loop", "=>", 19] +// +// warn("function_in_loop", the_fart); +// } + +// Push the current function context and establish a new one. + + function_list.push(the_fart); + function_stack.push(functionage); + functionage = the_fart; + +// Parse the parameter list. + + prefix_function_parameter(the_fart); + advance("=>"); + +// The function's body is a block. + + if (token_nxt.id === "{") { + if (!option_dict.fart) { + +// test_cause: +// ["()=>{}", "parse_fart", "use_function_not_fart", "=>", 3] + + warn("use_function_not_fart", the_fart); + } + the_fart.block = block("body"); + } else if ( + syntax_dict[token_nxt.id] !== undefined + && syntax_dict[token_nxt.id].fud_stmt !== undefined + ) { + +// PR-384 - Bugfix - Fixes issue #379 - warn against naked-statement in fart. + +// test_cause: +// ["()=>delete aa", "parse_fart", "unexpected_a_after_b", "=>", 5] + + stop("unexpected_a_after_b", token_nxt, token_nxt.id, "=>"); + +// The function's body is an expression. + + } else { + the_fart.expression = parse_expression(0); + } + +// Restore the previous context. + + functionage = function_stack.pop(); + return the_fart; + } + + function parse_json() { + let container; + let is_dup; + let name; + let negative; + switch (token_nxt.id) { + case "(number)": + if (!jslint_rgx_json_number.test(token_nxt.value)) { + +// test_cause: +// ["[-.0]", "parse_json", "unexpected_a", ".", 3] +// ["[-0x0]", "parse_json", "unexpected_a", "0x0", 3] +// ["[.0]", "parse_json", "unexpected_a", ".", 2] +// ["[0x0]", "parse_json", "unexpected_a", "0x0", 2] + + warn("unexpected_a"); + } + advance("(number)"); + return token_now; + case "(string)": + if (token_nxt.quote !== "\"") { + +// test_cause: +// ["['']", "parse_json", "unexpected_a", "'", 2] + + warn("unexpected_a", token_nxt, token_nxt.quote); + } + advance("(string)"); + return token_now; + case "-": + negative = token_nxt; + negative.arity = "unary"; + advance("-"); + +// Recurse parse_json(). + + negative.expression = parse_json(); + return negative; + case "[": + +// test_cause: +// ["[]", "parse_json", "bracket", "", 0] + + test_cause("bracket"); + container = token_nxt; + container.expression = []; + advance("["); + if (token_nxt.id !== "]") { + while (true) { + +// Recurse parse_json(). + + container.expression.push(parse_json()); + if (token_nxt.id !== ",") { + +// test_cause: +// ["[0,0]", "parse_json", "comma", "", 0] + + test_cause("comma"); + break; + } + advance(","); + } + } + advance("]", container); + return container; + case "false": + case "null": + case "true": + +// test_cause: +// ["[false]", "parse_json", "constant", "", 0] +// ["[null]", "parse_json", "constant", "", 0] +// ["[true]", "parse_json", "constant", "", 0] + + test_cause("constant"); + advance(); + return token_now; + case "{": + +// test_cause: +// ["{}", "parse_json", "brace", "", 0] + + test_cause("brace"); + container = token_nxt; + +// Explicit empty-object required to detect "__proto__". + + is_dup = empty(); + container.expression = []; + advance("{"); + if (token_nxt.id !== "}") { + +// JSON +// Parse/loop through each property in {...}. + + while (true) { + if (token_nxt.quote !== "\"") { + +// test_cause: +// ["{0:0}", "parse_json", "unexpected_a", "0", 2] + + warn( + "unexpected_a", + token_nxt, + token_nxt.quote + ); + } + name = token_nxt; + advance("(string)"); + if (is_dup[token_now.value] !== undefined) { + +// test_cause: +// ["{\"aa\":0,\"aa\":0}", "parse_json", "duplicate_a", "aa", 9] + + warn("duplicate_a", token_now); + } else if (token_now.value === "__proto__") { + +// test_cause: +// ["{\"__proto__\":0}", "parse_json", "weird_property_a", "__proto__", 2] + + warn("weird_property_a", token_now); + } else { + is_dup[token_now.value] = token_now; + } + advance(":"); + container.expression.push( + +// Recurse parse_json(). + + Object.assign(parse_json(), { + label: name + }) + ); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance("}", container); + return container; + default: + +// test_cause: +// ["[undefined]", "parse_json", "unexpected_a", "undefined", 2] + + stop("unexpected_a"); + } + } + + function parse_statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token_now.identifier && token_nxt.id === ":") { + the_label = token_now; + if (the_label.id === "ignore") { + +// test_cause: +// ["ignore:", "parse_statement", "unexpected_a", "ignore", 1] + + warn("unexpected_a", the_label); + } + advance(":"); + switch (token_nxt.id) { + case "do": + case "for": + case "switch": + case "while": + +// test_cause: +// ["aa:do{}", "parse_statement", "the_statement_label", "do", 0] +// ["aa:for{}", "parse_statement", "the_statement_label", "for", 0] +// ["aa:switch{}", "parse_statement", "the_statement_label", "switch", 0] +// ["aa:while{}", "parse_statement", "the_statement_label", "while", 0] + + test_cause("the_statement_label", token_nxt.id); + enroll(the_label, "label", true); + the_label.dead = false; + the_label.init = true; + the_statement = parse_statement(); + functionage.statement_prv = the_statement; + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + +// test_cause: +// ["aa:", "parse_statement", "unexpected_label_a", "aa", 1] + + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token_now; + first.statement = true; + the_symbol = syntax_dict[first.id]; + if ( + the_symbol !== undefined + && the_symbol.fud_stmt !== undefined + +// PR-318 - Bugfix - Fixes issues #316, #317 - dynamic-import(). + + && !(the_symbol.id === "import" && token_nxt.id === "(") + ) { + the_symbol.disrupt = false; + the_symbol.statement = true; + token_now.arity = "statement"; + the_statement = the_symbol.fud_stmt(); + functionage.statement_prv = the_statement; + } else { + +// It is an expression statement. + + the_statement = parse_expression(0, true); + functionage.statement_prv = the_statement; + if (the_statement.wrapped && the_statement.id !== "(") { + +// test_cause: +// ["(0)", "parse_statement", "unexpected_a", "(", 1] + + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; + } + + function parse_statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const statement_list = []; + let a_statement; + let disrupt = false; + +// Parse/loop each statement until a statement-terminator is reached. + + while (true) { + switch (token_nxt.id) { + case "(end)": + case "case": + case "default": + case "else": + case "}": + +// test_cause: +// [";", "parse_statements", "closer", "", 0] +// ["case", "parse_statements", "closer", "", 0] +// ["default", "parse_statements", "closer", "", 0] +// ["else", "parse_statements", "closer", "", 0] +// ["}", "parse_statements", "closer", "", 0] + + test_cause("closer"); + return statement_list; + } + a_statement = parse_statement(); + statement_list.push(a_statement); + if (disrupt) { + +// test_cause: +// ["while(0){break;0;}", "parse_statements", "unreachable_a", "0", 16] + + warn("unreachable_a", a_statement); + } + disrupt = a_statement.disrupt; + } + } + + function postassign(id) { + +// Create one of the postassign operators. + + const the_symbol = symbol(id, 150); + the_symbol.led_infix = function (left) { + token_now.expression = left; + token_now.arity = "postassign"; + check_mutation(token_now.expression); + return token_now; + }; + return the_symbol; + } + + function preassign(id) { + +// Create one of the preassign operators. + + const the_symbol = symbol(id); + the_symbol.nud_prefix = function () { + const the_token = token_now; + the_token.arity = "preassign"; + the_token.expression = parse_expression(150); + check_mutation(the_token.expression); + return the_token; + }; + return the_symbol; + } + + function prefix(id, f) { + +// Create a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud_prefix = function () { + const the_token = token_now; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = parse_expression(150); + return the_token; + }; + return the_symbol; + } + + function prefix_assign_divide() { + +// test_cause: +// ["/=", "prefix_assign_divide", "expected_a_b", "/=", 1] + + stop("expected_a_b", token_now, "/\\=", "/="); + } + + function prefix_async() { + let the_async = token_now; + let the_function; + token_nxt.arity = the_async.arity; + +// PR-414 - Parse async fart. + + if (token_nxt.fart) { + advance("("); + the_function = Object.assign(token_now.fart, { + async: 1 + }); + if (!option_dict.fart) { + +// test_cause: +// ["async()=>0", "prefix_async", "use_function_not_fart", "=>", 8] + + warn("use_function_not_fart", the_function); + } + prefix_lparen(); + +// Parse async function. + + } else { + advance("function"); + the_function = Object.assign(token_now, { + async: 1 + }); + prefix_function(); + } + if (the_function.async === 1) { + +// test_cause: +// [" +// async function aa(){} +// ", "prefix_async", "missing_await_statement", "function", 7] + + warn("missing_await_statement", the_function); + } + return the_function; + } + + function prefix_await() { + const the_await = token_now; + +// PR-370 - Add top-level-await support. + + if (functionage.async === 0 && functionage !== token_global) { + +// test_cause: +// ["function aa(){aa=await 0;}", "prefix_await", "unexpected_a", "await", 18] +// ["function aa(){await 0;}", "prefix_await", "unexpected_a", "await", 15] + + warn("unexpected_a", the_await); + } else { + functionage.async += 1; + } + if (the_await.arity === "statement") { + +// PR-405 - Bugfix - fix expression after "await" mis-identified as statement. + + the_await.expression = parse_expression(150); + semicolon(); + } else { + the_await.expression = parse_expression(150); + } + return the_await; + } + + function prefix_fart() { + +// test_cause: +// ["=>0", "prefix_fart", "expected_a_before_b", "=>", 1] + + return stop("expected_a_before_b", token_now, "()", "=>"); + } + + function prefix_function(the_function) { + let name = the_function && the_function.name; + if (the_function === undefined) { + the_function = token_now; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!token_nxt.identifier) { + +// test_cause: +// ["function(){}", "prefix_function", "expected_identifier_a", "(", 9] +// ["function*aa(){}", "prefix_function", "expected_identifier_a", "*", 9] + + return stop("expected_identifier_a"); + } + name = token_nxt; + enroll(name, "variable", true); + the_function.name = Object.assign(name, { + calls: empty(), + +// PR-331 - Bugfix - Fixes issue #272 - function hoisting not allowed. + + dead: false, + init: true + }); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + the_function.name = anon; + if (token_nxt.identifier) { + name = token_nxt; + the_function.name = name; + advance(); + } + } + } + +// Probably deadcode. +// if (mode_mega) { +// warn("unexpected_a", the_function); +// } +// jslint_assert(!mode_mega, `Expected !mode_mega.`); + +// PR-378 - Relax warning "function_in_loop". +// +// // Don't create functions in loops. It is inefficient, and it can lead to +// // scoping errors. +// +// if (functionage.loop > 0) { +// +// // test_cause: +// // [" +// // while(0){aa.map(function(){});} +// // ", "prefix_function", "function_in_loop", "function", 17] +// +// warn("function_in_loop", the_function); +// } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + Object.assign(the_function, { + async: the_function.async || 0, + context: empty(), + finally: 0, + level: functionage.level + 1, + loop: 0, + statement_prv: undefined, + switch: 0, + try: 0 + }); + if (the_function.arity !== "statement" && typeof name === "object") { + +// test_cause: +// ["let aa=function bb(){return;};", "prefix_function", "expression", "bb", 0] + + test_cause("expression", name.id); + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// PR-334 - Bugfix - fix function-redefinition not warned inside function-call. +// Push the current function context and establish a new one. + + function_list.push(the_function); + function_stack.push(functionage); + functionage = the_function; + +// Parse the parameter list. + + advance("("); + token_now.arity = "function"; + prefix_function_parameter(the_function); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && token_nxt.line === token_now.line + ) { + +// test_cause: +// ["function aa(){}0", "prefix_function", "unexpected_a", "0", 16] + + return stop("unexpected_a"); + } + if ( + token_nxt.id === "." + || token_nxt.id === "?." + +// PR-459 - Allow destructuring-assignment after function-definition. + + // || token_nxt.id === "[" + ) { + +// test_cause: +// ["function aa(){}\n.aa", "prefix_function", "unexpected_a", ".", 1] +// ["function aa(){}\n?.aa", "prefix_function", "unexpected_a", "?.", 1] + + warn("unexpected_a"); + } + +// Check functions are ordered. + + check_ordered( + "function", + function_list.slice( + function_list.indexOf(the_function) + 1 + ).map(function ({ + level, + name + }) { + return (level === the_function.level + 1) && name; + }).filter(function (name) { + return option_dict.beta && name && name.id; + }) + ); + +// Restore the previous context. + + functionage = function_stack.pop(); + return the_function; + } + + function prefix_function_parameter(the_function) { + +// This function will parse input at beginning of + + let optional; + let parameters = []; + let signature = ["("]; + let subparam; + function param_enroll(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + +// test_cause: +// ["([aa])=>0", "param_enroll", "use_function_not_fart", "=>", 7] +// ["({aa})=>0", "param_enroll", "use_function_not_fart", "=>", 7] + + if (the_function.id === "=>" && !option_dict.fart) { + warn("use_function_not_fart", the_function); + } + +// Recurse param_enroll(). + + name.names.forEach(param_enroll); + } + } + function param_parse() { + let ellipsis = false; + let param; + if (token_nxt.id === "{") { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,{}){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + param = token_nxt; + param.names = []; + advance("{"); + signature.push("{"); + while (true) { + subparam = token_nxt; + if (!subparam.identifier) { + +// test_cause: +// ["function aa(aa=0,{}){}", "param_parse", "expected_identifier_a", "}", 19] +// ["function aa({0}){}", "param_parse", "expected_identifier_a", "0", 14] + + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (token_nxt.id === ":") { + advance(":"); + advance(); + token_now.label = subparam; + subparam = token_now; + if (!subparam.identifier) { + +// test_cause: +// ["function aa({aa:0}){}", "param_parse", "expected_identifier_a", "}", 18] + + return stop( + "expected_identifier_a", + token_nxt + ); + } + } + +// test_cause: +// ["function aa({aa=aa},aa){}", "param_parse", "equal", "", 0] + + test_cause("equal"); + if (token_nxt.id === "=") { + advance("="); + subparam.expression = parse_expression(); + param.open = true; + } + param.names.push(subparam); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + } else { + break; + } + } + parameters.push(param); + +// test_cause: +// [" +// function aa({bb,aa}){} +// ", "check_ordered", "expected_a_b_before_c_d", "aa", 17] + + check_ordered("parameter", param.names); + advance("}"); + signature.push("}"); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } else if (token_nxt.id === "[") { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,[]){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + param = token_nxt; + param.names = []; + advance("["); + signature.push("[]"); + while (true) { + subparam = token_nxt; + if (!subparam.identifier) { + +// test_cause: +// ["function aa(aa=0,[]){}", "param_parse", "expected_identifier_a", "]", 19] + + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + +// test_cause: +// ["function aa([aa=aa],aa){}", "param_parse", "id", "", 0] + + test_cause("id"); + if (token_nxt.id === "=") { + advance("="); + subparam.expression = parse_expression(); + param.open = true; + } + if (token_nxt.id === ",") { + advance(","); + } else { + break; + } + } + parameters.push(param); + advance("]"); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } else { + if (token_nxt.id === "...") { + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,...){}", "param_parse", "required_a_optional_b", "aa", 21] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + } + if (!token_nxt.identifier) { + +// test_cause: +// ["function aa(0){}", "param_parse", "expected_identifier_a", "0", 13] + + return stop("expected_identifier_a"); + } + param = token_nxt; + parameters.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (token_nxt.id === "=") { + optional = param; + advance("="); + param.expression = parse_expression(0); + } else { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,bb){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } + } + } + +// test_cause: +// ["function aa(){}", "prefix_function_parameter", "opener", "(", 0] + + test_cause("opener", token_now.id); + token_now.free = false; + if (token_nxt.id !== ")" && token_nxt.id !== "(end)") { + param_parse(); + } + advance(")"); + signature.push(")"); + parameters.forEach(param_enroll); + the_function.parameters = parameters; + the_function.signature = signature.join(""); + } + + function prefix_lbrace() { + const seen = empty(); + const the_brace = token_now; + let extra; + let full; + let id; + let name; + let the_colon; + let value; + the_brace.expression = []; + if (token_nxt.id !== "}") { + +// Parse/loop through each property in {...}. + + while (true) { + name = token_nxt; + advance(); + if ( + (name.id === "get" || name.id === "set") + && token_nxt.identifier + ) { + if (!option_dict.getset) { + +// test_cause: +// ["aa={get aa(){}}", "prefix_lbrace", "unexpected_a", "get", 5] + + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + token_nxt.id; + name = token_nxt; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + +// test_cause: +// ["aa={get aa(){},get aa(){}}", "prefix_lbrace", "duplicate_a", "aa", 20] + + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else if (name.id === "`") { + +// test_cause: +// ["aa={`aa`:0}", "prefix_lbrace", "unexpected_a", "`", 5] + + stop("unexpected_a", name); + + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + +// test_cause: +// ["aa={aa,aa}", "prefix_lbrace", "duplicate_a", "aa", 8] + + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (token_nxt.id === "}" || token_nxt.id === ",") { + if (typeof extra === "string") { + +// test_cause: +// ["aa={get aa}", "prefix_lbrace", "closer", "", 0] + + test_cause("closer"); + advance("("); + } + value = parse_expression(Infinity, true); + } else if (token_nxt.id === "(") { + +// test_cause: +// ["aa={aa()}", "prefix_lbrace", "paren", "", 0] +// ["aa={get aa(){}}", "prefix_lbrace", "paren", "", 0] + + test_cause("paren"); + value = prefix_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + +// test_cause: +// ["aa={get aa.aa}", "prefix_lbrace", "paren", "", 0] + + test_cause("paren"); + advance("("); + } + the_colon = token_nxt; + advance(":"); + value = parse_expression(0); + if ( + value.id === name.id + && value.id !== "function" + ) { + +// test_cause: +// ["aa={aa:aa}", "prefix_lbrace", "unexpected_a", ": aa", 7] + + warn("unexpected_a", the_colon, ": " + name.id); + } + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + +// test_cause: +// ["aa={\"aa\":0}", "prefix_lbrace", "colon", "", 0] + + test_cause("colon"); + advance(":"); + value = parse_expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (token_nxt.id !== ",") { + break; + } + +// test_cause: +// ["aa={\"aa\":0,\"bb\":0}", "prefix_lbrace", "comma", "", 0] + + test_cause("comma"); + advance(","); + if (token_nxt.id === "}") { + +// test_cause: +// ["let aa={aa:0,}", "prefix_lbrace", "unexpected_a", ",", 13] + + warn("unexpected_a", token_now); + break; + } + } + } + +// test_cause: +// ["aa={bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8] + + check_ordered( + "property", + the_brace.expression.map(function ({ + label + }) { + return label; + }) + ); + advance("}"); + return the_brace; + } + + function prefix_lbracket() { + const the_token = token_now; + let element; + let ellipsis; + the_token.expression = []; + if (token_nxt.id !== "]") { + +// Parse/loop through each element in [...]. + + while (true) { + ellipsis = false; + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + element = parse_expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (token_nxt.id !== ",") { + break; + } + advance(","); + if (token_nxt.id === "]") { + +// test_cause: +// ["let aa=[0,]", "prefix_lbracket", "unexpected_a", ",", 10] + + warn("unexpected_a", token_now); + break; + } + } + } + advance("]"); + return the_token; + } + + function prefix_lparen() { + let the_paren = token_now; + let the_value; + +// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart. + + if (token_now.fart) { + return parse_fart(token_now.fart); + } + +// test_cause: +// ["(0)", "prefix_lparen", "expr", "", 0] + + test_cause("expr"); + the_paren.free = true; + the_value = parse_expression(0); + if (the_value.wrapped === true) { + +// test_cause: +// ["((0))", "prefix_lparen", "unexpected_a", "(", 1] + + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + return the_value; + } + + function prefix_new() { + const the_new = token_now; + let right; + right = parse_expression(160); + if (token_nxt.id !== "(") { + +// test_cause: +// ["new aa", "prefix_new", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "()", artifact()); + } + the_new.expression = right; + return the_new; + } + + function prefix_tick() { + const the_tick = token_now; + the_tick.value = []; + the_tick.expression = []; + if (token_nxt.id !== "`") { + +// Parse/loop through each token in `${...}`. + + while (true) { + advance("(string)"); + the_tick.value.push(token_now); + if (token_nxt.id !== "${") { + break; + } + advance("${"); + +// test_cause: +// ["let aa=`${}`;", "prefix_tick", "${", "", 0] + + test_cause("${"); + the_tick.expression.push(parse_expression(0)); + advance("}"); + } + } + advance("`"); + return the_tick; + } + + function prefix_void() { + const the_void = token_now; + +// test_cause: +// ["void 0", "prefix_void", "unexpected_a", "void", 1] +// ["void", "prefix_void", "unexpected_a", "void", 1] + + warn("unexpected_a", the_void); + the_void.expression = parse_expression(0); + return the_void; + } + + function semicolon() { + +// Try to match a semicolon. + + if (token_nxt.id === ";") { + advance(";"); + } else { + +// test_cause: +// ["0", "semicolon", "expected_a_b", "(end)", 1] + + warn_at( + "expected_a_b", + token_now.line, + token_now.thru + 1, + ";", + artifact() + ); + } + anon = "anonymous"; + } + + function stmt(id, fud_stmt) { + +// Create a statement. + + const the_symbol = symbol(id); + the_symbol.fud_stmt = fud_stmt; + return the_symbol; + } + + function stmt_break() { + const the_break = token_now; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + +// test_cause: +// ["break", "stmt_break", "unexpected_a", "break", 1] + + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (token_nxt.identifier && token_now.line === token_nxt.line) { + the_label = functionage.context[token_nxt.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + if (the_label !== undefined && the_label.dead) { + +// test_cause: +// ["aa:{function aa(aa){break aa;}}", "stmt_break", "out_of_scope_a", "aa", 27] + + warn("out_of_scope_a"); + } else { + +// test_cause: +// ["aa:{break aa;}", "stmt_break", "not_label_a", "aa", 11] + + warn("not_label_a"); + } + } else { + the_label.used += 1; + } + the_break.label = token_nxt; + advance(); + } + advance(";"); + return the_break; + } + + function stmt_continue() { + const the_continue = token_now; + if (functionage.loop < 1 || functionage.finally > 0) { + +// test_cause: +// ["continue", "stmt_continue", "unexpected_a", "continue", 1] +// [" +// function aa(){while(0){try{}finally{continue}}} +// ", "stmt_continue", "unexpected_a", "continue", 37] + + warn("unexpected_a", the_continue); + } + check_not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; + } + + function stmt_debugger() { + const the_debug = token_now; + if (!option_dict.devel) { + +// test_cause: +// ["debugger", "stmt_debugger", "unexpected_a", "debugger", 1] + + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; + } + + function stmt_delete() { + const the_token = token_now; + const the_value = parse_expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + +// test_cause: +// ["delete 0", "stmt_delete", "expected_a_b", "0", 8] + + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; + } + + function stmt_do() { + const the_do = token_now; + check_not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + +// test_cause: +// ["function aa(){do{break;}while(0)}", "stmt_do", "weird_loop", "do", 15] + + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; + } + + function stmt_export() { + let export_list = []; + let the_export = token_now; + let the_id; + let the_name; + let the_thing; + + the_export.expression = []; + if (token_nxt.id === "default") { + if (export_dict.default !== undefined) { + +// test_cause: +// [" +// export default 0;export default 0 +// ", "stmt_export", "duplicate_a", "default", 25] + + warn("duplicate_a"); + } + advance("default"); + the_thing = parse_expression(0); + if ( + the_thing.id !== "(" + || the_thing.expression[0].id !== "." + || the_thing.expression[0].expression.id !== "Object" + || the_thing.expression[0].name.id !== "freeze" + ) { + +// test_cause: +// ["export default {}", "stmt_export", "freeze_exports", "{", 16] + + warn("freeze_exports", the_thing); + +// PR-301 - Bugfix - Fixes issues #282 - optional-semicolon. + + } else { + +// test_cause: +// [" +// export default Object.freeze({}) +// ", "semicolon", "expected_a_b", "(end)", 32] + + semicolon(); + } + export_dict.default = the_thing; + the_export.expression.push(the_thing); + } else { + +// PR-439 - Add grammar for "export async function ...". + + if (token_nxt.id === "function" || token_nxt.id === "async") { + +// test_cause: +// ["export async function aa(){}", "stmt_export", "freeze_exports", "async", 8] +// ["export function aa(){}", "stmt_export", "freeze_exports", "function", 8] + + warn("freeze_exports"); + the_thing = parse_statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (export_dict[the_id] !== undefined) { + +// test_cause: +// [" +// let aa;export{aa};export function aa(){} +// ", "stmt_export", "duplicate_a", "aa", 35] + + warn("duplicate_a", the_name); + } + export_dict[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + token_nxt.id === "var" + || token_nxt.id === "let" + || token_nxt.id === "const" + ) { + +// test_cause: +// ["export const", "stmt_export", "unexpected_a", "const", 8] +// ["export let", "stmt_export", "unexpected_a", "let", 8] +// ["export var", "stmt_export", "unexpected_a", "var", 8] + + warn("unexpected_a"); + parse_statement(); + } else if (token_nxt.id === "{") { + +// test_cause: +// ["export {}", "stmt_export", "advance{", "", 0] + + test_cause("advance{"); + advance("{"); + while (true) { + if (!token_nxt.identifier) { + +// test_cause: +// ["export {}", "stmt_export", "expected_identifier_a", "}", 9] + + stop("expected_identifier_a"); + } + the_id = token_nxt.id; + export_list.push(token_nxt); + the_name = token_global.context[the_id]; + if (the_name === undefined) { + +// test_cause: +// ["export {aa}", "stmt_export", "unexpected_a", "aa", 9] + + warn("unexpected_a"); + } else { + the_name.used += 1; + if (export_dict[the_id] !== undefined) { + +// test_cause: +// ["let aa;export{aa,aa}", "stmt_export", "duplicate_a", "aa", 18] + + warn("duplicate_a"); + } + export_dict[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + if (token_nxt.id === ",") { + advance(","); + } else { + break; + } + } + +// PR-439 - Check exported properties are ordered. + +// test_cause: +// ["export {bb, aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 13] + + check_ordered("export", export_list); + advance("}"); + semicolon(); + } else { + +// test_cause: +// ["export", "stmt_export", "unexpected_a", "(end)", 1] + + stop("unexpected_a"); + } + } + state.mode_module = true; + return the_export; + } + + function stmt_for() { + let first; + let the_for = token_now; + if (!option_dict.for) { + +// test_cause: +// ["for", "stmt_for", "unexpected_a", "for", 1] + + warn("unexpected_a", the_for); + } + check_not_top_level(the_for); + functionage.loop += 1; + advance("("); + token_now.free = true; + if (token_nxt.id === ";") { + +// test_cause: +// ["for(;;){}", "stmt_for", "expected_a_b", "for (;", 1] + + return stop("expected_a_b", the_for, "while (", "for (;"); + } + switch (token_nxt.id) { + case "const": + case "let": + case "var": + +// test_cause: +// ["for(const aa in aa){}", "stmt_for", "unexpected_a", "const", 5] + + return stop("unexpected_a"); + } + first = parse_expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + +// test_cause: +// ["for(0 in aa){}", "stmt_for", "bad_assignment_a", "0", 5] + + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = parse_expression(0); + advance(";"); + the_for.inc = parse_expression(0); + if (the_for.inc.id === "++") { + +// test_cause: +// ["for(aa;aa;aa++){}", "stmt_for", "expected_a_b", "++", 13] + + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + +// test_cause: +// [" +// /*jslint for*/ +// function aa(bb,cc){for(0;0;0){break;}} +// ", "stmt_for", "weird_loop", "for", 20] + + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; + } + + function stmt_if() { + const the_if = token_now; + let the_else; + the_if.expression = condition(); + the_if.block = block(); + if (token_nxt.id === "else") { + advance("else"); + the_else = token_now; + the_if.else = ( + token_nxt.id === "if" + ? parse_statement() + : block() + ); + +// test_cause: +// ["if(0){0}else if(0){0}", "stmt_if", "else", "", 0] +// ["if(0){0}else{0}", "stmt_if", "else", "", 0] + + test_cause("else"); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + +// test_cause: +// ["if(0){break;}else{break;}", "stmt_if", "disrupt", "", 0] + + test_cause("disrupt"); + the_if.disrupt = true; + } else { + +// test_cause: +// ["if(0){break;}else{}", "stmt_if", "unexpected_a", "else", 14] + + warn("unexpected_a", the_else); + } + } + } + return the_if; + } + + function stmt_import() { + const the_import = token_now; + let name; + let names; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// if (typeof state.mode_module === "object") { +// +// // test_cause: +// // [" +// // /*global aa*/ +// // import aa from "aa" +// // ", "stmt_import", "unexpected_directive_a", "global", 1] +// +// warn( +// "unexpected_directive_a", +// state.mode_module, +// state.mode_module.directive +// ); +// } + + state.mode_module = true; + +// PR-436 - Add grammar for side-effect import-statement. + + if (token_nxt.id === "(string)") { + +// test_cause: +// ["import \"./aa.mjs\";", "stmt_import", "import_side_effect", "", 0] + + test_cause("import_side_effect"); + warn("expected_a_b", token_nxt, "{", artifact()); + advance(); + semicolon(); + return the_import; + } + if (token_nxt.identifier) { + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// ["import ignore from \"aa\"", "stmt_import", "unexpected_a", "ignore", 8] + + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + names = []; + advance("{"); + if (token_nxt.id !== "}") { + while (true) { + if (!token_nxt.identifier) { + +// test_cause: +// ["import {", "stmt_import", "expected_identifier_a", "(end)", 1] + + stop("expected_identifier_a"); + } + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// ["import {ignore} from \"aa\"", "stmt_import", "unexpected_a", "ignore", 9] + + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token_now; + if (!jslint_rgx_module.test(token_now.value)) { + +// test_cause: +// ["import aa from \"!aa\"", "stmt_import", "bad_module_name_a", "!aa", 16] + + warn("bad_module_name_a", token_now); + } + import_list.push(token_now.value); + semicolon(); + return the_import; + } + + function stmt_lbrace() { + +// test_cause: +// [";{}", "stmt_lbrace", "naked_block", "{", 2] +// ["class aa{}", "stmt_lbrace", "naked_block", "{", 9] + + warn("naked_block", token_now); + return block("naked"); + } + + function stmt_return() { + const the_return = token_now; + check_not_top_level(the_return); + if (functionage.finally > 0) { + +// test_cause: +// [" +// function aa(){try{}finally{return;}} +// ", "stmt_return", "unexpected_a", "return", 28] + + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (token_nxt.id !== ";" && the_return.line === token_nxt.line) { + the_return.expression = parse_expression(10); + } + advance(";"); + return the_return; + } + + function stmt_semicolon() { + +// test_cause: +// [";", "stmt_semicolon", "unexpected_a", ";", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function stmt_switch() { + const the_cases = []; + const the_switch = token_now; + let dups = []; + let exp; + let last; + let stmts; + let the_case; + let the_default; + let the_disrupt = true; + let the_last; + function is_dup(thing) { + return is_equal(thing, exp); + } + check_not_top_level(the_switch); + if (functionage.finally > 0) { + +// test_cause: +// [" +// function aa(){try{}finally{switch(0){}}} +// ", "stmt_switch", "unexpected_a", "switch", 28] + + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token_now.free = true; + the_switch.expression = parse_expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + while (true) { + +// Loop through cases with breaks. + + the_case = token_nxt; + the_case.arity = "statement"; + the_case.expression = []; + while (true) { + +// Loop through fallthrough cases. + + advance("case"); + token_now.switch = true; + exp = parse_expression(0); + if (dups.some(is_dup)) { + +// test_cause: +// [" +// switch(0){case 0:break;case 0:break} +// ", "stmt_switch", "unexpected_a", "0", 29] + + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (token_nxt.id !== "case") { + break; + } + } + +// test_cause: +// [" +// switch(0){case 1:case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 23] +// [" +// switch(0){case "aa":case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 26] +// [" +// switch(0){case "bb":case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 26] +// [" +// switch(0){case aa:case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24] +// [" +// switch(0){case bb:case aa:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24] + + check_ordered_case(the_case.expression); + stmts = parse_statements(); + if (stmts.length < 1) { + +// test_cause: +// [" +// switch(0){case 0:default:} +// ", "stmt_switch", "expected_statements_a", "default", 18] +// ["switch(0){case 0:}", "stmt_switch", "expected_statements_a", "}", 18] + + warn("expected_statements_a"); + +// PR-359 - Bugfix - Fixes issue #358 - switch-statement crashes jslint. + + break; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn("expected_a_before_b", token_nxt, "break;", artifact()); + } + if (token_nxt.id !== "case") { + break; + } + } + +// test_cause: +// [" +// switch(0){case 1:break;case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 29] +// [" +// switch(0){case "aa":break;case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 32] +// [" +// switch(0){case "bb":break;case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 32] +// [" +// switch(0){case aa:break;case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30] +// [" +// switch(0){case bb:break;case aa:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30] + + check_ordered_case(the_cases.map(function ({ + expression + }) { + return expression[0]; + })); + dups = undefined; + if (token_nxt.id === "default") { + the_default = token_nxt; + advance("default"); + token_now.switch = true; + advance(":"); + the_switch.else = parse_statements(); + if (the_switch.else.length < 1) { + +// test_cause: +// [" +// switch(0){case 0:break;default:} +// ", "stmt_switch", "unexpected_a", "default", 24] + + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + the_last = the_switch.else[ + the_switch.else.length - 1 + ]; + if ( + the_last.id === "break" + && the_last.label === undefined + ) { + +// test_cause: +// [" +// switch(0){case 0:break;default:break;} +// ", "stmt_switch", "unexpected_a", "break", 32] + + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; + } + + function stmt_throw() { + const the_throw = token_now; + the_throw.disrupt = true; + the_throw.expression = parse_expression(10); + semicolon(); + if (functionage.try > 0) { + +// test_cause: +// ["try{throw 0}catch(){}", "stmt_throw", "unexpected_a", "throw", 5] + + warn("unexpected_a", the_throw); + } + return the_throw; + } + + function stmt_try() { + const the_try = token_now; + let ignored; + let the_catch; + let the_disrupt; + if (functionage.try > 0) { + +// test_cause: +// ["try{try{}catch(){}}catch(){}", "stmt_try", "unexpected_a", "try", 5] + + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (token_nxt.id === "catch") { + ignored = "ignore"; + the_catch = token_nxt; + the_try.catch = the_catch; + advance("catch"); + +// Create new catch-scope for catch-parameter. + + catch_stack.push(catchage); + catchage = the_catch; + catch_list.push(catchage); + the_catch.context = empty(); + if (token_nxt.id === "(") { + advance("("); + if (!token_nxt.identifier) { + +// test_cause: +// ["try{}catch(){}", "stmt_try", "expected_identifier_a", ")", 12] + + return stop("expected_identifier_a"); + } + if (token_nxt.id !== "ignore") { + ignored = undefined; + the_catch.name = token_nxt; + enroll(token_nxt, "exception", true); + } + advance(); + advance(")"); + } + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + +// Restore previous catch-scope after catch-block. + + catchage = catch_stack.pop(); + +// PR-404 - Relax warning about missing `catch` in `try...finally` statement. +// +// } else { +// +// // test_cause: +// // ["try{}finally{break;}", "stmt_try", "expected_a_before_b", "finally", 6] +// +// warn("expected_a_before_b", token_nxt, "catch", artifact()); + + } + if (token_nxt.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; + } + + function stmt_var() { + let ellipsis; + let mode_const; + let name; + let the_brace; + let the_bracket; + let the_variable = token_now; + let variable_prv; + mode_const = the_variable.id === "const"; + the_variable.names = []; + +// A program may use var or let, but not both. + + if (!mode_const) { + if (mode_var === undefined) { + mode_var = the_variable.id; + } else if (the_variable.id !== mode_var) { + +// test_cause: +// ["let aa;var aa", "stmt_var", "expected_a_b", "var", 8] + + warn("expected_a_b", the_variable, mode_var, the_variable.id); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + +// test_cause: +// ["switch(0){case 0:var aa}", "stmt_var", "var_switch", "var", 18] + + warn("var_switch", the_variable); + } + switch ( + Boolean(functionage.statement_prv) + && functionage.statement_prv.id + ) { + case "const": + case "let": + case "var": + +// test_cause: +// ["const aa=0;const bb=0;", "stmt_var", "var_prv", "const", 0] +// ["let aa=0;let bb=0;", "stmt_var", "var_prv", "let", 0] +// ["var aa=0;var bb=0;", "stmt_var", "var_prv", "var", 0] + + test_cause("var_prv", functionage.statement_prv.id); + variable_prv = functionage.statement_prv; + break; + case "import": + +// test_cause: +// ["import aa from \"aa\";\nlet bb=0;", "stmt_var", "import_prv", "", 0] + + test_cause("import_prv"); + break; + case false: + break; + default: + if ( + (option_dict.beta && !option_dict.variable) + || the_variable.id === "var" + ) { + +// test_cause: +// ["console.log();let aa=0;", "stmt_var", "var_on_top", "let", 15] +// ["console.log();var aa=0;", "stmt_var", "var_on_top", "var", 15] +// ["try{aa();}catch(aa){var aa=0;}", "stmt_var", "var_on_top", "var", 21] +// ["while(0){var aa;}", "stmt_var", "var_on_top", "var", 10] + + warn("var_on_top", token_now); + } + } + while (true) { + if (token_nxt.id === "{") { + if (the_variable.id === "var") { + +// test_cause: +// ["var{aa}=0", "stmt_var", "unexpected_a", "var", 1] + + warn("unexpected_a", the_variable); + } + the_brace = token_nxt; + advance("{"); + while (true) { + name = token_nxt; + if (!name.identifier) { + +// test_cause: +// ["let {0}", "stmt_var", "expected_identifier_a", "0", 6] + + return stop("expected_identifier_a"); + } + survey(name); + advance(); + if (token_nxt.id === ":") { + advance(":"); + if (!token_nxt.identifier) { + +// test_cause: +// ["let {aa:0}", "stmt_var", "expected_identifier_a", "0", 9] +// ["let {aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 9] + + return stop("expected_identifier_a"); + } + +// PR-363 - Bugfix +// Add test against false-warning in code +// '/*jslint node*/\nlet {aa:bb} = {}; bb();'. +// +// token_nxt.label = name; +// the_variable.names.push(token_nxt); +// enroll(token_nxt, "variable", mode_const); + + name = token_nxt; + the_variable.names.push(name); + survey(name); + enroll(name, "variable", mode_const); + + advance(); + the_brace.open = true; + } else { + the_variable.names.push(name); + enroll(name, "variable", mode_const); + } + name.dead = false; + name.init = true; + if (token_nxt.id === "=") { + +// test_cause: +// ["let {aa=0}", "stmt_var", "assign", "", 0] + + test_cause("assign"); + advance("="); + name.expression = parse_expression(); + the_brace.open = true; + } + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + +// test_cause: +// ["let{bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8] + + check_ordered(the_variable.id, the_variable.names); + advance("}"); + advance("="); + the_variable.expression = parse_expression(0); + } else if (token_nxt.id === "[") { + if (the_variable.id === "var") { + +// test_cause: +// ["var[aa]=0", "stmt_var", "unexpected_a", "var", 1] + + warn("unexpected_a", the_variable); + } + the_bracket = token_nxt; + advance("["); + while (true) { + ellipsis = false; + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + if (!token_nxt.identifier) { + +// test_cause: +// ["let[]", "stmt_var", "expected_identifier_a", "]", 5] + + return stop("expected_identifier_a"); + } + name = token_nxt; + advance(); + the_variable.names.push(name); + enroll(name, "variable", mode_const); + name.dead = false; + name.init = true; + if (ellipsis) { + name.ellipsis = true; + break; + } + if (token_nxt.id === "=") { + advance("="); + name.expression = parse_expression(); + the_bracket.open = true; + } + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + advance("]"); + advance("="); + the_variable.expression = parse_expression(0); + } else if (token_nxt.identifier) { + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// [" +// let ignore;function aa(ignore) {} +// ", "stmt_var", "unexpected_a", "ignore", 5] + + warn("unexpected_a", name); + } + enroll(name, "variable", mode_const); + if (token_nxt.id === "=" || mode_const) { + advance("="); + name.dead = false; + name.init = true; + name.expression = parse_expression(0); + } + the_variable.names.push(name); + } else { + +// test_cause: +// ["let 0", "stmt_var", "expected_identifier_a", "0", 5] +// ["var{aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 8] + + return stop("expected_identifier_a"); + } + if (token_nxt.id !== ",") { + break; + } + +// test_cause: +// ["let aa,bb;", "stmt_var", "expected_a_b", ",", 7] + + warn("expected_a_b", token_nxt, ";", ","); + advance(","); + } + +// Warn if variable declarations are unordered. + + if ( + option_dict.beta + && !option_dict.unordered + && !option_dict.variable + && variable_prv + && ( + variable_prv.id + " " + variable_prv.names[0].id + > the_variable.id + " " + the_variable.names[0].id + ) + ) { + +// test_cause: +// ["const bb=0;const aa=0;", "stmt_var", "expected_a_b_before_c_d", "aa", 12] +// ["let bb;let aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8] +// ["var bb;var aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8] + + warn( + "expected_a_b_before_c_d", + the_variable, + the_variable.id, + the_variable.names[0].id, + variable_prv.id, + variable_prv.names[0].id + ); + } + semicolon(); + return the_variable; + } + + function stmt_while() { + const the_while = token_now; + check_not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + +// test_cause: +// ["function aa(){while(0){break;}}", "stmt_while", "weird_loop", "while", 15] + + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; + } + + function stmt_with() { + +// test_cause: +// ["with", "stmt_with", "unexpected_a", "with", 1] + + stop("unexpected_a", token_now); + } + + function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!jslint_rgx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!jslint_rgx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + +// test_cause: +// ["let aa={0:0}", "survey", "expected_identifier_a", "0", 9] + + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property_dict[id] === "number") { + property_dict[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (state.mode_property) { + if (tenure[id] !== true) { + +// test_cause: +// ["/*property aa*/\naa.bb", "survey", "unregistered_property_a", "bb", 4] + + warn("unregistered_property_a", name); + } + } else if ( + !option_dict.nomen + && name.identifier + && jslint_rgx_weird_property.test(id) + ) { + +// test_cause: +// ["aa.$", "survey", "weird_property_a", "$", 4] +// ["aa._", "survey", "weird_property_a", "_", 4] +// ["aa._aa", "survey", "weird_property_a", "_aa", 4] +// ["aa.aaSync", "survey", "weird_property_a", "aaSync", 4] +// ["aa.aa_", "survey", "weird_property_a", "aa_", 4] + + warn("weird_property_a", name); + } + property_dict[id] = 1; + } + return id; + } + +// These functions are used to specify the grammar of our language: + + function symbol(id, bp) { + +// Create a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax_dict[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax_dict[id] = the_symbol; + } + return the_symbol; + } + + function ternary(id1, id2) { + +// Create a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led_infix = function parse_ternary_led(left) { + const the_token = token_now; + let second; + second = parse_expression(20); + advance(id2); + token_now.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, parse_expression(10)]; + if (token_nxt.id !== ")") { + +// test_cause: +// ["0?0:0", "parse_ternary_led", "use_open", "?", 2] + + warn("use_open", the_token); + } + return the_token; + }; + return the_symbol; + } + +// Now we parse JavaScript. +// Begin defining the language. + + assignment("%="); + assignment("&="); + assignment("*="); + assignment("+="); + assignment("-="); + assignment("/="); + assignment("<<="); + assignment("="); + assignment(">>="); + assignment(">>>="); + assignment("^="); + assignment("|="); + constant("(number)", "number"); + constant("(regexp)", "regexp"); + constant("(string)", "string"); + constant("Function", "function", constant_Function); + constant("Infinity", "number", Infinity); + constant("NaN", "number", NaN); + constant("arguments", "object", constant_arguments); + constant("eval", "function", constant_eval); + constant("false", "boolean", false); + constant("ignore", "undefined", constant_ignore); + constant("isFinite", "function", constant_isInfinite); + constant("isNaN", "function", constant_isNaN); + constant("null", "null", null); + constant("this", "object", constant_this); + constant("true", "boolean", true); + constant("undefined", "undefined"); + infix(100, "!="); + infix(100, "!=="); + infix(100, "=="); + infix(100, "==="); + infix(110, "<"); + infix(110, "<="); + infix(110, ">"); + infix(110, ">="); + infix(110, "in"); + infix(110, "instanceof"); + infix(120, "<<"); + infix(120, ">>"); + infix(120, ">>>"); + infix(130, "+"); + infix(130, "-"); + infix(140, "%"); + infix(140, "*"); + infix(140, "/"); + infix(160, "(", infix_lparen); + infix(160, "`", infix_grave); + infix(170, ".", infix_dot); + infix(170, "=>", infix_fart_unwrapped); + infix(170, "?.", infix_option_chain); + infix(170, "[", infix_lbracket); + infix(35, "??"); + infix(40, "||"); + infix(50, "&&"); + infix(70, "|"); + infix(80, "^"); + infix(90, "&"); + infixr(150, "**"); + postassign("++"); + postassign("--"); + preassign("++"); + preassign("--"); + prefix("!!"); + prefix("!"); + prefix("(", prefix_lparen); + prefix("+"); + prefix("-"); + prefix("/=", prefix_assign_divide); + prefix("=>", prefix_fart); + prefix("[", prefix_lbracket); + prefix("`", prefix_tick); + prefix("async", prefix_async); + prefix("await", prefix_await); + prefix("function", prefix_function); + prefix("new", prefix_new); + prefix("typeof"); + prefix("void", prefix_void); + prefix("{", prefix_lbrace); + prefix("~"); + stmt(";", stmt_semicolon); + stmt("async", prefix_async); + stmt("await", prefix_await); + stmt("break", stmt_break); + stmt("const", stmt_var); + stmt("continue", stmt_continue); + stmt("debugger", stmt_debugger); + stmt("delete", stmt_delete); + stmt("do", stmt_do); + stmt("export", stmt_export); + stmt("for", stmt_for); + stmt("function", prefix_function); + stmt("if", stmt_if); + stmt("import", stmt_import); + stmt("let", stmt_var); + stmt("return", stmt_return); + stmt("switch", stmt_switch); + stmt("throw", stmt_throw); + stmt("try", stmt_try); + stmt("var", stmt_var); + stmt("while", stmt_while); + stmt("with", stmt_with); + stmt("{", stmt_lbrace); + symbol(")"); + symbol("*/"); + symbol(","); + symbol(":"); + symbol(";"); + symbol("]"); + symbol("async"); + symbol("await"); + symbol("case"); + symbol("catch"); + symbol("class"); + symbol("default"); + symbol("else"); + symbol("enum"); + symbol("finally"); + symbol("implements"); + symbol("interface"); + symbol("package"); + symbol("private"); + symbol("protected"); + symbol("public"); + symbol("static"); + symbol("super"); + symbol("void"); + symbol("yield"); + symbol("}"); + ternary("?", ":"); + +// Init token_nxt. + + advance(); + +// Parsing of JSON is simple: + + if (state.mode_json) { + state.token_tree = parse_json(); + advance("(end)"); + return; + } + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option_dict.browser) { + if (token_nxt.id === ";") { + advance(";"); + } + +// If we are not in a browser, then the file form of strict pragma may be used. + + } else if (token_nxt.value === "use strict") { + advance("(string)"); + advance(";"); + } + state.token_tree = parse_statements(); + advance("(end)"); + +// Check global functions are ordered. + + check_ordered( + "function", + function_list.map(function ({ + level, + name + }) { + return (level === 1) && name; + }).filter(function (name) { + return option_dict.beta && name && name.id; + }) + ); +} + +function jslint_phase4_walk(state) { + +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). + + let { + artifact, + catch_stack, + function_stack, + global_dict, + is_equal, + is_weird, + option_dict, + syntax_dict, + test_cause, + token_global, + warn + } = state; + let block_stack = []; // The stack of blocks. + let blockage = token_global; // The current block. + let catchage = catch_stack[0]; // The current catch-block. + let functionage = token_global; // The current function. + let postaction; + let postamble; + let posts = empty(); + let preaction; + let preamble; + let pres = empty(); + +// The relational operators. + + let relationop = object_assign_from_list(empty(), [ + "!=", "!==", "<", "<=", "==", "===", ">", ">=" + ], true); + +// Ambulation of the parse tree. + + function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; + } + + function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + task(the_token); + }); + } + } + }; + } + + function init_variable(name) { + let the_variable = lookup(name); + if (!the_variable || the_variable.readonly) { + warn("bad_assignment_a", name); + return; + } + the_variable.init = true; + } + + function lookup(thing) { + let id = thing.id; + let the_variable; + if (thing.arity !== "variable") { + return; + } + +// Look up the variable in the current context. + + the_variable = functionage.context[id] || catchage.context[id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable && the_variable.role === "label") { + +// test_cause: +// ["aa:while(0){aa;}", "lookup", "label_a", "aa", 13] + + warn("label_a", thing); + return the_variable; + } + if (!the_variable) { + function_stack.forEach(function ({ + context + }) { + if (context[id] && context[id].role !== "label") { + the_variable = context[id]; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (!the_variable && global_dict[id] === undefined) { + +// test_cause: +// ["aa", "lookup", "undeclared_a", "aa", 1] +// ["class aa{}", "lookup", "undeclared_a", "aa", 7] +// [" +// let aa=0;try{aa();}catch(bb){bb();}bb(); +// ", "lookup", "undeclared_a", "bb", 36] +// [" +// let aa=0;try{aa();}catch(ignore){bb();} +// ", "lookup", "undeclared_a", "bb", 34] + + warn("undeclared_a", thing); + return; + } + if (!the_variable) { + the_variable = { + dead: false, + id, + init: true, + parent: token_global, + readonly: true, + role: "variable", + used: 0 + }; + token_global.context[id] = the_variable; + } + the_variable.closure = true; + functionage.context[id] = the_variable; + } + if ( + ( + the_variable.calls === undefined + || functionage.name === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + && the_variable.dead + ) { + +// test_cause: +// ["let aa;if(aa){let bb;}bb;", "lookup", "out_of_scope_a", "bb", 23] + + warn("out_of_scope_a", thing); + } + return the_variable; + } + + function post_a(thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + let right; + if (thing.id === "=") { + if (thing.names !== undefined) { + +// test_cause: +// ["if(0){aa=0}", "post_a", "=", "", 0] + + test_cause("="); + +// Probably deadcode. +// if (Array.isArray(thing.names)) { +// thing.names.forEach(init_variable); +// } else { +// init_variable(thing.names); +// } + + jslint_assert( + !Array.isArray(thing.names), + `Expected !Array.isArray(thing.names).` + ); + init_variable(thing.names); + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + +// test_cause: +// ["aa.aa=undefined", "post_a", "expected_a_b", "undefined", 1] + + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.readonly) { + warn("bad_assignment_a", lvalue); + } + } + right = syntax_dict[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + +// test_cause: +// ["aa+=undefined", "post_a", "unexpected_a", "undefined", 5] + + warn("unexpected_a", thing.expression[1]); + } + } + } + + function post_a_pluseq(thing) { + const right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } + } + + function post_b(thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || is_equal(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + +// test_cause: +// ["if(0===0){0}", "post_b", "weird_relation_a", "===", 5] + + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option_dict.convert) { + if (thing.expression[0].value === "") { + +// test_cause: +// ["\"\"+0", "post_b", "expected_a_b", "\"\" +", 3] + + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + +// test_cause: +// ["0+\"\"", "post_b", "expected_a_b", "+ \"\"", 2] + + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + +// test_cause: +// ["aa=window[0]", "post_b", "weird_expression_a", "window[...]", 10] + + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + +// test_cause: +// ["aa=self[0]", "post_b", "weird_expression_a", "self[...]", 8] + + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === "." || thing.id === "?.") { + if (thing.expression.id === "RegExp") { + +// test_cause: +// ["aa=RegExp.aa", "post_b", "weird_expression_a", ".", 10] + + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + +// test_cause: +// ["0- -0", "post_b", "wrap_unary", "-", 4] + + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } + } + + function post_b_and(thing) { + if ( + is_weird(thing.expression[0]) + || is_equal(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + +// test_cause: +// ["aa=(()=>0)&&(()=>0)", "post_b_and", "weird_condition_a", "&&", 11] +// ["aa=(``?``:``)&&(``?``:``)", "post_b_and", "weird_condition_a", "&&", 14] +// ["aa=/./&&/./", "post_b_and", "weird_condition_a", "&&", 7] +// ["aa=0&&0", "post_b_and", "weird_condition_a", "&&", 5] +// ["aa=[]&&[]", "post_b_and", "weird_condition_a", "&&", 6] +// ["aa=`${0}`&&`${0}`", "post_b_and", "weird_condition_a", "&&", 10] +// [" +// aa=function aa(){}&&function aa(){} +// ", "post_b_and", "weird_condition_a", "&&", 19] +// ["aa={}&&{}", "post_b_and", "weird_condition_a", "&&", 6] + + warn("weird_condition_a", thing); + } + } + + function post_b_lbracket(thing) { + if (thing.expression[0].id === "RegExp") { + +// test_cause: +// ["aa=RegExp[0]", "post_b_lbracket", "weird_expression_a", "[", 10] + + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + +// test_cause: +// ["aa[[0]]", "post_b_lbracket", "weird_expression_a", "[", 4] + + warn("weird_expression_a", thing.expression[1]); + } + } + + function post_b_lparen(thing) { + let arg; + let array; + let cack; + let left = thing.expression[0]; + let new_date; + let paren; + let the_new; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + +// test_cause: +// ["aa=function(){}()", "post_b_lparen", "wrap_immediate", "(", 16] + + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "BigInt" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + +// test_cause: +// ["new BigInt()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Boolean()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Number()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new String()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Symbol()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new aa()", "post_b_lparen", "unexpected_a", "new", 1] + + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option_dict.eval) { + +// test_cause: +// ["new Function()", "post_b_lparen", "unexpected_a", "new Function", 5] + + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + +// test_cause: +// ["new Array()", "post_b_lparen", "expected_a_b", "new Array", 5] + + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + +// test_cause: +// ["new Object()", "post_b_lparen", "expected_a_b", "new Object", 5] + + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "BigInt" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + +// test_cause: +// ["let Aa=Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8] + + warn("expected_a_before_b", left, "new", artifact(left)); + } + } + } else if (left.id === ".") { + cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + +// test_cause: +// ["new Date.UTC()", "post_b_lparen", "cack", "", 0] + + test_cause("cack"); + cack = !cack; + } + if (jslint_rgx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + +// test_cause: +// ["new Date.UTC()", "post_b_lparen", "unexpected_a", "new", 1] + + warn("unexpected_a", the_new); + } else { + +// test_cause: +// ["let Aa=Aa.Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8] + + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + paren = left.expression; + if (paren.id === "(") { + array = paren.expression; + if (array.length === 1) { + new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + +// test_cause: +// [" +// new Date().getTime() +// ", "post_b_lparen", "expected_a_b", "new Date().getTime()", 1] + + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } + } + + function post_b_or(thing) { + if ( + is_weird(thing.expression[0]) + || is_equal(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + +// test_cause: +// ["aa=0||0", "post_b_or", "weird_condition_a", "||", 5] + + warn("weird_condition_a", thing); + } + } + + function post_s_export(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== token_global) { + +// test_cause: +// [" +// if(0){import aa from "aa";} +// ", "post_s_export", "misplaced_a", "import", 7] + + warn("misplaced_a", the_thing); + } + } + + function post_s_for(thing) { + +// Recurse walk_statement(). + + walk_statement(thing.inc); + } + + function post_s_function(thing) { + delete functionage.async; + delete functionage.finally; + delete functionage.loop; + delete functionage.statement_prv; + delete functionage.switch; + delete functionage.try; + functionage = function_stack.pop(); + if (thing.wrapped) { + +// test_cause: +// ["aa=(function(){})", "post_s_function", "unexpected_parens", "function", 5] + + warn("unexpected_parens", thing); + } + return post_s_lbrace(); + } + + function post_s_import(the_thing) { + const name = the_thing.name; + if (name) { + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return post_s_export(the_thing); + } + } + + function post_s_lbrace() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); + } + + function post_s_try(thing) { + if (thing.catch) { + if (thing.catch.name) { + Object.assign(catchage.context[thing.catch.name.id], { + dead: false, + init: true + }); + } + +// Recurse walk_statement(). + + walk_statement(thing.catch.block); + +// Restore previous catch-scope after catch-block. + + catchage = catch_stack.pop(); + } + } + + function post_s_var(thing) { + thing.names.forEach(function (name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + +// Probably deadcode. +// if (name.id === "{" || name.id === "[") { +// name.names.forEach(subactivate); +// } else { +// name.init = true; +// } + + jslint_assert( + !(name.id === "{" || name.id === "["), + `Expected !(name.id === "{" || name.id === "[").` + ); + name.init = true; + } + blockage.live.push(name); + }); + } + + function post_t(thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || is_equal(thing.expression[1], thing.expression[2]) + ) { + +// test_cause: +// ["let aa=(aa?`${0}`:`${0}`);", "post_t", "unexpected_a", "?", 11] +// ["let aa=(aa?`0`:`0`);", "post_t", "unexpected_a", "?", 11] + + warn("unexpected_a", thing); + } else if (is_equal(thing.expression[0], thing.expression[1])) { + +// test_cause: +// ["aa?aa:0", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "||", "?"); + } else if (is_equal(thing.expression[0], thing.expression[2])) { + +// test_cause: +// ["aa?0:aa", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + +// test_cause: +// ["aa?true:false", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + +// test_cause: +// ["aa?false:true", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + +// test_cause: +// ["(aa&&!aa?0:1)", "post_t", "wrap_condition", "&&", 4] + + warn("wrap_condition", thing.expression[0]); + } + } + + function post_u(thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option_dict.convert) { + +// test_cause: +// ["!!0", "post_u", "expected_a_b", "!!", 1] + + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } + } + + function post_u_plus(thing) { + const right = thing.expression; + if (!option_dict.convert) { + +// test_cause: +// ["aa=+0", "post_u_plus", "expected_a_b", "+", 4] + + warn("expected_a_b", thing, "Number(...)", "+"); + } + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } + } + + function pre_a_bitwise(thing) { + +// These are the bitwise operators. + + switch (!option_dict.bitwise && thing.id) { + case "&": + case "&=": + case "<<": + case "<<=": + case ">>": + case ">>=": + case ">>>": + case ">>>=": + case "^": + case "^=": + case "|": + case "|=": + case "~": + +// test_cause: +// ["0&0", "pre_a_bitwise", "unexpected_a", "&", 2] +// ["0&=0", "pre_a_bitwise", "unexpected_a", "&=", 2] +// ["0<<0", "pre_a_bitwise", "unexpected_a", "<<", 2] +// ["0<<=0", "pre_a_bitwise", "unexpected_a", "<<=", 2] +// ["0>>0", "pre_a_bitwise", "unexpected_a", ">>", 2] +// ["0>>=0", "pre_a_bitwise", "unexpected_a", ">>=", 2] +// ["0>>>0", "pre_a_bitwise", "unexpected_a", ">>>", 2] +// ["0>>>=0", "pre_a_bitwise", "unexpected_a", ">>>=", 2] +// ["0^0", "pre_a_bitwise", "unexpected_a", "^", 2] +// ["0^=0", "pre_a_bitwise", "unexpected_a", "^=", 2] +// ["0|0", "pre_a_bitwise", "unexpected_a", "|", 2] +// ["0|=0", "pre_a_bitwise", "unexpected_a", "|=", 2] +// ["~0", "pre_a_bitwise", "unexpected_a", "~", 1] + + warn("unexpected_a", thing); + break; + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + +// test_cause: +// ["0<0<0", "pre_a_bitwise", "unexpected_a", "<", 4] + + warn("unexpected_a", thing); + } + } + + function pre_b(thing) { + let left; + let right; + let value; + if (relationop[thing.id] === true) { + left = thing.expression[0]; + right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + +// test_cause: +// ["NaN===NaN", "pre_b", "number_isNaN", "===", 4] + + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + +// test_cause: +// ["typeof 0===0", "pre_b", "expected_string_a", "0", 12] + + warn("expected_string_a", right); + } + } else { + value = right.value; + if (value === "null" || value === "undefined") { + +// test_cause: +// [" +// typeof aa==="undefined" +// ", "pre_b", "unexpected_typeof_a", "undefined", 13] + + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "bigint" + && value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + +// test_cause: +// ["typeof 0===\"aa\"", "pre_b", "expected_type_string_a", "aa", 12] + + warn("expected_type_string_a", right, value); + } + } + } + } + } + + function pre_b_eqeq(thing) { + +// test_cause: +// ["0==0", "pre_b_eqeq", "expected_a_b", "==", 2] + + warn("expected_a_b", thing, "===", "=="); + } + + function pre_b_in(thing) { + +// test_cause: +// ["aa in aa", "pre_b_in", "infix_in", "in", 4] + + warn("infix_in", thing); + } + + function pre_b_instanceof(thing) { + +// test_cause: +// ["0 instanceof 0", "pre_b_instanceof", "unexpected_a", "instanceof", 3] + + warn("unexpected_a", thing); + } + + function pre_b_lparen(thing) { + const left = thing.expression[0]; + let left_variable; + let parent; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + parent = functionage.name.parent; + if (parent) { + left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + +// Probably deadcode. +// && left_variable.dead + + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } + } + + function pre_b_noteq(thing) { + +// test_cause: +// ["0!=0", "pre_b_noteq", "expected_a_b", "!=", 2] + + warn("expected_a_b", thing, "!==", "!="); + } + + function pre_b_or(thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + +// test_cause: +// ["0&&0||0", "pre_b_or", "and", "&&", 2] + + warn("and", thang); + } + }); + } + + function pre_s_for(thing) { + let the_variable; + if (thing.name !== undefined) { + thing.name.dead = false; + the_variable = lookup(thing.name); + if (the_variable !== undefined) { + if (the_variable.init && the_variable.readonly) { + +// test_cause: +// ["const aa=0;for(aa in aa){}", "pre_s_for", "bad_assignment_a", "aa", 16] + + warn("bad_assignment_a", thing.name); + } + the_variable.init = true; + } + } + +// Recurse walk_statement(). + + walk_statement(thing.initial); + } + + function pre_s_function(thing) { + +// test_cause: +// ["()=>0", "pre_s_function", "", "", 0] +// ["(function (){}())", "pre_s_function", "", "", 0] +// ["function aa(){}", "pre_s_function", "", "", 0] + + test_cause(""); + if (thing.arity === "statement" && blockage.body !== true) { + +// test_cause: +// ["if(0){function aa(){}\n}", "pre_s_function", "unexpected_a", "function", 7] + + warn("unexpected_a", thing); + } + function_stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + +// test_cause: +// [" +// /*jslint getset*/ +// aa={get aa(aa){}} +// ", "pre_s_function", "bad_get", "function", 9] + + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + +// test_cause: +// [" +// /*jslint getset*/ +// aa={set aa(){}} +// ", "pre_s_function", "bad_set", "function", 9] + + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); + } + + function pre_s_lbrace(thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; + } + + function pre_try(thing) { + if (thing.catch !== undefined) { + +// Create new catch-scope for catch-parameter. + + catch_stack.push(catchage); + catchage = thing.catch; + } + } + + function pre_v(thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } + } + + function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); + } + + function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + +// test_cause: +// ["(function(){}())", "walk_expression", "isArray", "", 0] +// ["0&&0", "walk_expression", "isArray", "", 0] + + test_cause("isArray"); + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + +// PR-414 - Bugfix - fix fart-body not being walked. + + if (thing.id === "function" || thing.id === "=>") { + +// test_cause: +// ["aa=()=>0", "walk_expression", "function", "=>", 0] +// ["aa=function(){}", "walk_expression", "function", "function", 0] + + test_cause("function", thing.id); + +// Recurse walk_statement(). + + walk_statement(thing.block); + } + if ( + thing.arity === "preassign" || thing.arity === "postassign" + ) { + +// test_cause: +// ["aa=++aa", "walk_expression", "unexpected_a", "++", 4] +// ["aa=--aa", "walk_expression", "unexpected_a", "--", 4] + + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + +// test_cause: +// ["aa[aa=0]", "walk_expression", "unexpected_statement_a", "=", 6] + + warn("unexpected_statement_a", thing); + } + +// test_cause: +// ["aa=0", "walk_expression", "default", "", 0] + + test_cause("default"); + postamble(thing); + } + } + } + + function walk_statement(thing) { + if (!thing) { + return; + } + if (Array.isArray(thing)) { + +// test_cause: +// ["+[]", "walk_statement", "isArray", "", 0] + + test_cause("isArray"); + +// Recurse walk_statement(). + + thing.forEach(walk_statement); + return; + } + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + +// test_cause: +// ["0&&0", "walk_statement", "unexpected_expression_a", "&&", 2] + + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + && thing.id !== "import" + ) { + +// test_cause: +// ["!0", "walk_statement", "unexpected_expression_a", "!", 1] +// ["+[]", "walk_statement", "unexpected_expression_a", "+", 1] +// ["+new aa()", "walk_statement", "unexpected_expression_a", "+", 1] +// ["0", "walk_statement", "unexpected_expression_a", "0", 1] +// ["typeof 0", "walk_statement", "unexpected_expression_a", "typeof", 1] + + warn("unexpected_expression_a", thing); + } + +// Recurse walk_statement(). + + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + + postaction = action(posts); + postamble = amble(posts); + preaction = action(pres); + preamble = amble(pres); + postaction("assignment", "+=", post_a_pluseq); + postaction("assignment", post_a); + postaction("binary", "&&", post_b_and); + postaction("binary", "(", post_b_lparen); + postaction("binary", "=>", post_s_function); + postaction("binary", "[", post_b_lbracket); + postaction("binary", "||", post_b_or); + postaction("binary", post_b); + postaction("statement", "const", post_s_var); + postaction("statement", "export", post_s_export); + postaction("statement", "for", post_s_for); + postaction("statement", "function", post_s_function); + postaction("statement", "import", post_s_import); + postaction("statement", "let", post_s_var); + postaction("statement", "try", post_s_try); + postaction("statement", "var", post_s_var); + postaction("statement", "{", post_s_lbrace); + postaction("ternary", post_t); + postaction("unary", "+", post_u_plus); + postaction("unary", "function", post_s_function); + postaction("unary", post_u); + preaction("assignment", pre_a_bitwise); + preaction("binary", "!=", pre_b_noteq); + preaction("binary", "(", pre_b_lparen); + preaction("binary", "==", pre_b_eqeq); + preaction("binary", "=>", pre_s_function); + preaction("binary", "in", pre_b_in); + preaction("binary", "instanceof", pre_b_instanceof); + preaction("binary", "||", pre_b_or); + preaction("binary", pre_b); + preaction("binary", pre_a_bitwise); + preaction("statement", "for", pre_s_for); + preaction("statement", "function", pre_s_function); + preaction("statement", "try", pre_try); + preaction("statement", "{", pre_s_lbrace); + preaction("unary", "function", pre_s_function); + preaction("unary", "~", pre_a_bitwise); + preaction("variable", pre_v); + + walk_statement(state.token_tree); +} + +function jslint_phase5_whitage(state) { + +// PHASE 5. Check whitespace between tokens in . + + let { + artifact, + catch_list, + function_list, + function_stack, + option_dict, + test_cause, + token_global, + token_list, + warn + } = state; + let closer = "(end)"; + let free = false; + +// free = false + +// cause: +// "()=>0" +// "aa()" +// "aa(0,0)" +// "function(){}" + +// free = true + +// cause: +// "(0)" +// "(aa)" +// "aa(0)" +// "do{}while()" +// "for(){}" +// "if(){}" +// "switch(){}" +// "while(){}" + + let left = token_global; + let margin = 0; + let mode_indent = ( + +// PR-330 - Allow 2-space indent. + + option_dict.indent2 + ? 2 + : 4 + ); + let nr_comments_skipped = 0; + let open = true; + let opening = true; + let right; + +// This is the set of infix operators that require a space on each side. + + let spaceop = object_assign_from_list(empty(), [ + "!=", "!==", "%", "%=", "&", "&&", "&=", "*", "*=", "+=", "-=", "/", + "/=", "<", "<<", "<<=", "<=", "=", "==", "===", "=>", ">", ">=", ">>", + ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" + ], true); + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + const name = the_function.context[id]; + if (id !== "ignore" && name.parent === the_function) { + +// test_cause: +// ["function aa(aa) {return aa;}", "delve", "id", "", 0] + + test_cause("id"); + if ( + name.used === 0 + +// Probably deadcode. +// && ( +// name.role !== "function" +// || name.parent.arity !== "unary" +// ) + + && jslint_assert( + name.role !== "function", + `Expected name.role !== "function".` + ) + ) { + +// test_cause: +// ["/*jslint node*/\nlet aa;", "delve", "unused_a", "aa", 5] +// ["function aa(aa){return;}", "delve", "unused_a", "aa", 13] +// ["let aa=0;try{aa();}catch(bb){aa();}", "delve", "unused_a", "bb", 26] + + warn("unused_a", name); + } else if (!name.init) { + +// test_cause: +// ["/*jslint node*/\nlet aa;aa();", "delve", "uninitialized_a", "aa", 5] + + warn("uninitialized_a", name); + } + } + }); + } + + function expected_at(at) { + +// Probably deadcode. +// if (right === undefined) { +// right = token_nxt; +// } + + jslint_assert( + !(right === undefined), + `Expected !(right === undefined).` + ); + warn( + "expected_a_at_b_c", + right, + artifact(right), + +// Fudge column numbers in warning message. + + at + jslint_fudge, + right.from + jslint_fudge + ); + } + + function no_space() { + if (left.line === right.line) { + +// from: +// if (left.line === right.line) { +// no_space(); +// } else { + + if (left.thru !== right.from && nr_comments_skipped === 0) { + +// test_cause: +// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14] + + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + +// from: +// } else if ( +// right.arity === "binary" +// && right.id === "(" +// && free +// ) { +// no_space(); +// } else if ( + +// Probably deadcode. +// if (open) { +// const at = ( +// free +// ? margin +// : margin + 8 +// ); +// if (right.from < at) { +// expected_at(at); +// } +// } else { +// if (right.from !== margin + 8) { +// expected_at(margin + 8); +// } +// } + + jslint_assert(open, `Expected open.`); + jslint_assert(free, `Expected free.`); + if (right.from < margin) { + +// test_cause: +// ["let aa = aa(\naa\n()\n);", "expected_at", "expected_a_at_b_c", "5", 1] + + expected_at(margin); + } + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line || !open) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (right.from !== margin) { + expected_at(margin); + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn("expected_space_a_b", right, artifact(left), artifact(right)); + } + } + + function pop() { + const previous = function_stack.pop(); + closer = previous.closer; + free = previous.free; + margin = previous.margin; + open = previous.open; + opening = previous.opening; + } + + function push() { + function_stack.push({ + closer, + free, + margin, + open, + opening + }); + } + +// uninitialized_and_unused(); +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (state.mode_module === true || option_dict.node) { + delve(token_global); + } + catch_list.forEach(delve); + function_list.forEach(delve); + + if (option_dict.white) { + return; + } + +// whitage(); +// Go through the token list, looking at usage of whitespace. + + token_list.forEach(function whitage(the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + +// The open and close pairs. + + switch (left.id) { + case "${": + case "(": + case "[": + case "{": + +// test_cause: +// ["let aa=[];", "whitage", "opener", "", 0] +// ["let aa=`${0}`;", "whitage", "opener", "", 0] +// ["let aa=aa();", "whitage", "opener", "", 0] +// ["let aa={};", "whitage", "opener", "", 0] + + test_cause("opener"); + +// Probably deadcode. +// case "${}": + + jslint_assert( + !(left.id + right.id === "${}"), + "Expected !(left.id + right.id === \"${}\")." + ); + switch (left.id + right.id) { + case "()": + case "[]": + case "{}": + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like '{]') have already been detected. + +// test_cause: +// ["let aa=[];", "whitage", "opener_closer", "", 0] +// ["let aa=aa();", "whitage", "opener_closer", "", 0] +// ["let aa={};", "whitage", "opener_closer", "", 0] + + test_cause("opener_closer"); + if (left.line === right.line) { + +// test_cause: +// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14] + + no_space(); + } else { + +// test_cause: +// ["let aa = aa(\n );", "expected_at", "expected_a_at_b_c", "1", 2] + + at_margin(0); + } + break; + default: + +// test_cause: +// ["let aa=(0);", "whitage", "opener_operand", "", 0] +// ["let aa=[0];", "whitage", "opener_operand", "", 0] +// ["let aa=`${0}`;", "whitage", "opener_operand", "", 0] +// ["let aa=aa(0);", "whitage", "opener_operand", "", 0] +// ["let aa={aa:0};", "whitage", "opener_operand", "", 0] + + test_cause("opener_operand"); + opening = left.open || (left.line !== right.line); + push(); + switch (left.id) { + case "${": + closer = "}"; + break; + case "(": + closer = ")"; + break; + case "[": + closer = "]"; + break; + case "{": + closer = "}"; + break; + } + if (opening) { + +// test_cause: +// ["function aa(){\nreturn;\n}", "whitage", "opening", "", 0] +// ["let aa=(\n0\n);", "whitage", "opening", "", 0] +// ["let aa=[\n0\n];", "whitage", "opening", "", 0] +// ["let aa=`${\n0\n}`;", "whitage", "opening", "", 0] +// ["let aa={\naa:0\n};", "whitage", "opening", "", 0] + + test_cause("opening"); + free = closer === ")" && left.free; + open = true; + margin += mode_indent; + if (right.role === "label") { + if (right.from !== 0) { + +// test_cause: +// [" +// function aa() { +// bb: +// while (aa) { +// if (aa) { +// break bb; +// } +// } +// } +// ", "expected_at", "expected_a_at_b_c", "1", 2] + + expected_at(0); + } + } else if (right.switch) { + at_margin(-mode_indent); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + +// test_cause: +// [" +// function aa() {bb: +// while (aa) { +// aa(); +// } +// } +// ", "whitage", "expected_line_break_a_b", "bb", 16] + + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + +// test_cause: +// ["let aa=(0);", "whitage", "not_free", "", 0] +// ["let aa=[0];", "whitage", "not_free", "", 0] +// ["let aa=`${0}`;", "whitage", "not_free", "", 0] +// ["let aa={aa:0};", "whitage", "not_free", "", 0] + + test_cause("not_free"); + free = false; + open = false; + +// test_cause: +// ["let aa = ( 0 );", "no_space_only", "unexpected_space_a_b", "0", 12] + + no_space_only(); + } + } + break; + default: + if (right.statement === true) { + if (left.id === "else") { + +// test_cause: +// [" +// let aa = 0; +// if (aa) { +// aa(); +// } else if (aa) { +// aa(); +// } +// ", "one_space_only", "expected_space_a_b", "if", 9] + + one_space_only(); + } else { + +// test_cause: +// [" let aa = 0;", "expected_at", "expected_a_at_b_c", "1", 2] + + at_margin(0); + open = false; + } + +// If right is a closer, then pop the previous state. + + } else if (right.id === closer) { + pop(); + if (opening && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-mode_indent); + } else if (right.role === "label") { + if (right.from !== 0) { + +// test_cause: +// [" +// function aa() { +// aa();cc: +// while (aa) { +// if (aa) { +// break cc; +// } +// } +// } +// ", "expected_at", "expected_a_at_b_c", "1", 10] + + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + +// test_cause: +// ["let {aa,bb} = 0;", "one_space", "expected_space_a_b", "bb", 9] + + one_space(); + } else { + +// test_cause: +// [" +// function aa() { +// aa( +// 0,0 +// ); +// } +// ", "expected_at", "expected_a_at_b_c", "9", 11] + + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + +// test_cause: +// [" +// let aa = ( +// aa +// ? 0 +// : 1 +// ); +// ", "expected_at", "expected_a_at_b_c", "5", 1] + + at_margin(0); + } else { + +// test_cause: +// ["let aa = (aa ? 0 : 1);", "whitage", "use_open", "?", 14] + + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + +// test_cause: +// ["let aa = aa(\naa ()\n);", "no_space", "unexpected_space_a_b", "(", 4] + + no_space(); + } else if ( + left.id === "." + || left.id === "?." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + || (right.id === "." || right.id === "?.") + ) { + +// test_cause: +// ["let aa = 0 ;", "no_space_only", "unexpected_space_a_b", ";", 12] +// ["let aa = aa ?.aa;", "no_space_only", "unexpected_space_a_b", "?.", 13] + + no_space_only(); + } else if (left.id === ";") { + +// test_cause: +// [" +// /*jslint for*/ +// function aa() { +// for ( +// aa(); +// aa; +// aa() +// ) { +// aa(); +// } +// } +// ", "expected_at", "expected_a_at_b_c", "9", 1] + + if (open) { + at_margin(0); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || left.id === "await" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + +// test_cause: +// [" +// function aa() { +// do { +// aa(); +// } while(aa()); +// } +// ", "one_space_only", "expected_space_a_b", "(", 12] + + one_space_only(); + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || left.id === "async" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + +// test_cause: +// ["let aa=0;", "one_space", "expected_space_a_b", "0", 8] +// ["let aa={\naa:\n0\n};", "expected_at", "expected_a_at_b_c", "5", 1] + + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +function jslint_report({ + exports, + froms, + functions, + global, + json, + module, + property, + stop, + warnings +}) { + +// This function will create human-readable, html-report +// for warnings, properties, and functions from jslint-result-object. +// +// Example usage: +// let result = jslint("console.log('hello world')"); +// let html = jslint_report(result); + + let html = ""; + let length_80 = 1111; + + function address(line = 1, column = 1) { + +// This function will create HTML address element from and + + return `
    ${Number(line)}: ${Number(column)}
    `; + + } + + function detail(title, list) { + return ( + (Array.isArray(list) && list.length > 0) + ? ( + +// Google Lighthouse Accessibility -
    's do not contain only properly-ordered +//
    and
    groups, + + + + + + + + + + + + +

    CodeMirror: JSLint Demo

    +

    +This demo will auto-lint the code below, and auto-generate a report as you type. +

    + + + + + + + +
    + + + + + +``` +3. Live example at https://www.jslint.com/jslint_wrapper_codemirror.html + +[![screenshot](https://kaizhu256.github.io/jslint/branch-beta/.artifact/screenshot_browser__2fjslint_2fbranch-beta_2fjslint_wrapper_codemirror.html.png)](https://kaizhu256.github.io/jslint/jslint_wrapper_codemirror.html) + + +

    +# Quickstart JSLint in Vim +1. Download and save [`jslint.mjs`](https://www.jslint.com/jslint.mjs), [`jslint_wrapper_vim.vim`](https://www.jslint.com/jslint_wrapper_vim.vim) to directory `~/.vim/` +2. Add vim-command `:source ~/.vim/jslint_wrapper_vim.vim` to file `~/.vimrc` + - If above files were saved to custom-directory, then use that directory instead, e.g.: + - save [`jslint.mjs`](https://www.jslint.com/jslint.mjs), [`jslint_wrapper_vim.vim`](https://www.jslint.com/jslint_wrapper_vim.vim) to directory `~/vimfiles/` + - vim-command `:source ~/vimfiles/jslint_wrapper_vim.vim` +3. Vim can now jslint files (via nodejs): + - with vim-command `:SaveAndJslint` + - with vim-key-combo ` ` +- screenshot + +[![screenshot](asset_image_jslint_wrapper_vim.png)](https://www.jslint.com/jslint_wrapper_vim.vim) + + +

    +# Quickstart JSLint in VSCode +1. In VSCode, search and install extension [`vscode-jslint`](https://marketplace.visualstudio.com/items?itemName=jslint.vscode-jslint) +2. In VSCode, while editing a javascript file: + - right-click context-menu and select `[JSLint - Lint File]` + - or use key-binding `[Ctrl + Shift + J], [L]` + - or use key-binding `[ Cmd + Shift + J], [L]` for Mac +- screenshot + +[![screenshot](https://kaizhu256.github.io/jslint/asset_image_jslint_wrapper_vscode.png)](https://marketplace.visualstudio.com/items?itemName=jslint.vscode-jslint) + + +

    +# Documentation + + +- [jslint.mjs](jslint.mjs) contains the jslint function. It parses and analyzes a source file, returning an object with information about the file. It can also take an object that sets options. + +- [index.html](index.html) runs the jslint.mjs function in a web page. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[https://www.crockford.com/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. + + +

    +### API Doc +- https://www.jslint.com/apidoc.html + +[![screenshot](https://kaizhu256.github.io/jslint/branch-beta/.artifact/screenshot_browser__2f.artifact_2fapidoc.html.png)](https://www.jslint.com/apidoc.html) + + +

    +### Directive `/*jslint*/` + +
    + +##### `/*jslint beta*/` + +```js +/*jslint beta*/ +// Enable experimental warnings. +// Warn if global variables are redefined. +// Warn if const / let statements are not declared at top of function or +// script, similar to var statements. +// Warn if const / let / var statements are not declared in ascii-order. +// Warn if named-functions are not declared in ascii-order. +// Warn if cases in switch-statements are not in ascii-order. +``` + +
    + +##### `/*jslint bitwise*/` + +```js +/*jslint bitwise*/ +// Allow bitwise operator. + +let foo = 0 | 1; +``` + +
    + +##### `/*jslint browser*/` + +```js +/*jslint browser*/ +// Assume browser environment. + +localStorage.getItem("foo"); +``` + +
    + +##### `/*jslint convert*/` + +```js +/*jslint convert*/ +// Allow conversion operator. + +let foo = new Date() + ""; +let bar = !!0; +``` + +
    + +##### `/*jslint couch*/` + +```js +/*jslint couch*/ +// Assume CouchDb environment. + +registerType("text-json", "text/json"); +``` + +
    + +##### `/*jslint devel*/` + +```js +/*jslint devel*/ +// Allow console.log() and friends. + +console.log("hello"); +``` + +
    + +##### `/*jslint eval*/` + +```js +/*jslint eval*/ +// Allow eval(). + +eval("1"); +``` + +
    + +##### `/*jslint fart*/` + +```js +/*jslint fart*/ +// Allow complex fat-arrow. + +let foo = async ({bar, baz}) => { + return await bar(baz); +}; +``` + +
    + +##### `/*jslint for*/` + +```js +/*jslint for*/ +// Allow for-loop. + +function foo() { + let ii; + for (ii = 0; ii < 10; ii += 1) { + foo(); + } +} +``` + +
    + +##### `/*jslint getset*/` + +```js +/*jslint getset, this, devel*/ +// Allow get() and set(). + +let foo = { + bar: 0, + get getBar() { + return this.bar; + }, + set setBar(value) { + this.bar = value; + } +}; +console.log(foo.getBar); // 0 +foo.setBar = 1; +console.log(foo.getBar); // 1 +``` + +
    + +##### `/*jslint indent2*/` + +```js +/*jslint indent2*/ +// Use 2-space indent. + +function foo() { + return; +} +``` + +
    + +##### `/*jslint long*/` + +```js +/*jslint long*/ +// Allow long lines. + +let foo = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; +``` + +
    + +##### `/*jslint node*/` + +```js +/*jslint node*/ +// Assume Node.js environment. + +require("fs"); +``` + +
    + +##### `/*jslint nomen*/` + +```js +/*jslint nomen*/ +// Allow weird property name. + +let foo = {}; +foo._bar = 1; +``` + +
    + +##### `/*jslint single*/` + +```js +/*jslint single*/ +// Allow single-quote strings. + +let foo = ''; +``` + +
    + +##### `/*jslint subscript*/` + +```js +/*jslint subscript*/ +// Allow identifiers in subscript-notation. + +let foo = {}; +foo["bar"] = 1; +``` + +
    + +##### `/*jslint this*/` + +```js +/*jslint this*/ +// Allow 'this'. + +function foo() { + return this; +} +``` + +
    + +##### `/*jslint trace*/` + +```js +/*jslint trace*/ +// Include jslint stack-trace in warnings. + +console.log('hello world'); +/* +1. Undeclared 'console'. +console.log('hello world'); +Error + at warn_at (...) + at warn (...) + at lookup (...) + at pre_v (...) + at jslint.mjs +2. Use double quotes, not single quotes. +console.log(...); +Error + at warn_at (...) + at lex_string (...) + at lex_token (...) + at jslint_phase2_lex (...) + at Function.jslint (...) + at jslint.mjs +*/ +``` + +
    + +##### `/*jslint unordered*/` + +```js +/*jslint unordered*/ +// Allow unordered cases, params, properties, variables, and exports. + +let foo = {bb: 1, aa: 0}; + +function bar({ + bb = 1, + aa = 0 +}) { + return aa + bb; +} + +export { + foo, + bar +}; +``` + +
    + +##### `/*jslint white*/` + +```js +/*jslint white*/ +// Allow messy whitespace. + +let foo = 1; let bar = 2; +``` + + +

    +### Directive `/*global*/` + +```js +/*global foo, bar*/ +// Declare global variables foo, bar. + +foo(); +bar(); +``` + + +

    +### Directive `/*property*/` + +```js +/*property foo, bar*/ +// Restrict property-access to only .foo, .bar. + +let aa = {bar: 1, foo: 2}; +``` + + +

    +### Directive `/*jslint-disable*/.../*jslint-enable*/` + +```js +/*jslint-disable*/ + +JSLint will ignore and treat this region as blank-lines. +Syntax error. + +/*jslint-enable*/ +``` + + +

    +### Directive `//jslint-ignore-line` + +```js +// JSLint will ignore non-fatal warnings at given line. + +eval("1"); //jslint-ignore-line +``` + + +

    +# Package Listing +![screenshot_package_listing.svg](https://kaizhu256.github.io/jslint/branch-beta/.artifact/screenshot_package_listing.svg) + + +

    +# Changelog +- [Full CHANGELOG.md](CHANGELOG.md) + +![screenshot_changelog.svg](https://kaizhu256.github.io/jslint/branch-beta/.artifact/screenshot_changelog.svg) + + +

    +# License +- JSLint is under [Unlicense License](LICENSE). +- CodeMirror editor is under [MIT License](https://github.com/codemirror/codemirror5/blob/d0e3b2e727c41aa4fd89fbad0adfb3815339174c/LICENSE). +- Function `v8CoverageListMerge` is derived from [MIT Licensed v8-coverage](https://github.com/demurgos/v8-coverage/blob/73446087dc38f61b09832c9867122a23f8577099/ts/LICENSE.md). + + +

    +# Devops Instruction + + +

    +### pull-request merge +- find highest issue-number at https://github.com/kaizhu256/jslint/issues/, https://github.com/kaizhu256/jslint/pulls/, and add +1 to it for PR-xxx +- `shGitPullrequest beta beta` + - verify ci-success for origin-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- `git push upstream alpha -f` + - verify ci-success for upstream-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- goto https://github.com/kaizhu256/jslint/compare/beta...kaizhu256:jslint:branch-p2024.11.24 +- click `Create pull request` +- input `Add your description here...` with: +``` +Fixes #xxx. +- + +This PR will ... + +This PR will additionally: +- +... + + +``` +- verify `commit into jslint-org:beta` +- click `Create pull request` + - verify ci-success for pull-request + - https://github.com/kaizhu256/jslint/actions/workflows/on_pull_request.yml +- wait awhile before continuing ... +- click `Rebase and merge` + - verify ci-success for upstream-branch-beta + - https://github.com/kaizhu256/jslint/actions +- `shGitPullrequestCleanup` + - verify ci-success for origin-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- `git push upstream alpha -f` + - verify ci-success for upstream-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- click `Delete branch` + + +

    +### branch-master commit +- `shGitPullrequest master beta` + - verify ci-success for origin-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- `git push upstream alpha -f` + - verify ci-success for upstream-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- goto https://github.com/kaizhu256/jslint/compare/beta...kaizhu256:jslint:branch-v2024.11.24 +- click `Create pull request` +- input `Add a title` with: `# v20yy.mm.dd` +- input `Add a description` with: +``` +- +- +``` +- verify `commit into jslint-org:beta` +- click `Create pull request` + - verify ci-success for pull-request + - https://github.com/kaizhu256/jslint/actions/workflows/on_pull_request.yml +- wait awhile before continuing ... +- click `Rebase and merge` + - verify ci-success for upstream-branch-beta + - https://github.com/kaizhu256/jslint/actions +- `shGitPullrequestCleanup` + - verify ci-success for origin-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- `git push upstream alpha -f` + - verify ci-success for upstream-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- click `Delete branch` +- `git push origin beta:master` + - verify ci-success for origin-branch-master + - https://github.com/kaizhu256/jslint/actions +- `git push upstream beta:master` + - verify ci-success for upstream-branch-master + - https://github.com/kaizhu256/jslint/actions + + +

    +### branch-master publish +- `git push upstream beta:master` + - verify ci-success for upstream-branch-master + - https://github.com/kaizhu256/jslint/actions +- goto https://github.com/kaizhu256/jslint/releases/new +- input `Choose a tag` with: `v20yy.mm.dd` +- click `Create new tag: v20yy.mm.dd on publish` + - verify correct-year `20yy` +- select `Target: master` +- select `Previous tag:auto` +- input `Release title` with: `v20yy.mm.dd - ` +- input `Describe this release` with: +``` +- +- +``` +- click `Generate release notes` +- click `Set as the latest release` +- click `Preview` and review +- click `Publish release` + - verify ci-success for upstream-branch-publish + - https://github.com/kaizhu256/jslint/actions + - verify email-notification `Successfully published @kaizhu256/jslint@20yy.mm.dd` + + +

    +### vscode-jslint publish +- goto https://github.com/kaizhu256/jslint/tree/gh-pages/branch-beta/.artifact/jslint_wrapper_vscode +- click `vscode-jslint-20yy.mm.dd.vsix` +- click `Raw` to download +- goto https://marketplace.visualstudio.com/manage/publishers/jslint +- right-click `Update` +- upload downloaded file `vscode-jslint-20yy.mm.dd.vsix` +- click 'Upload' +- verify email-notification `[Succeeded] Extension publish on Visual Studio Marketplace - vscode-jslint` + + + diff --git a/branch-beta/asset_codemirror_rollup.js b/branch-beta/asset_codemirror_rollup.js new file mode 100644 index 000000000..9b93a0427 --- /dev/null +++ b/branch-beta/asset_codemirror_rollup.js @@ -0,0 +1,11481 @@ +/*jslint-disable*/ +/* +shRollupFetch +{ + "fetchList": [ + { + "comment": true, + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/LICENSE" + }, + { + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/codemirror.js", + "url2": "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.10/codemirror.js" + }, + { + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/addon/edit/matchbrackets.js" + }, + { + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/addon/edit/trailingspace.js" + }, + { + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/addon/lint/lint.js" + }, + { + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/addon/selection/active-line.js" + }, + { + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/mode/javascript/javascript.js" + } + ], + "replaceList": [] +} +*/ + + +/* +repo https://github.com/codemirror/codemirror5/tree/5.65.10 +committed 2022-11-20T15:35:33Z +*/ + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/LICENSE +*/ +/* +MIT License + +Copyright (C) 2017 by Marijn Haverbeke and others + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/codemirror.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +// This is CodeMirror (https://codemirror.net/5), a code editor +// implemented in JavaScript on top of the browser's DOM. +// +// You can find some technical background for some of the code below +// at http://marijnhaverbeke.nl/blog/#cm-internals . + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, global.CodeMirror = factory()); +}(this, (function () { 'use strict'; + + // Kludges for bugs and behavior differences that can't be feature + // detected are enabled based on userAgent etc sniffing. + var userAgent = navigator.userAgent; + var platform = navigator.platform; + + var gecko = /gecko\/\d/i.test(userAgent); + var ie_upto10 = /MSIE \d/.test(userAgent); + var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent); + var edge = /Edge\/(\d+)/.exec(userAgent); + var ie = ie_upto10 || ie_11up || edge; + var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]); + var webkit = !edge && /WebKit\//.test(userAgent); + var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent); + var chrome = !edge && /Chrome\/(\d+)/.exec(userAgent); + var chrome_version = chrome && +chrome[1]; + var presto = /Opera\//.test(userAgent); + var safari = /Apple Computer/.test(navigator.vendor); + var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent); + var phantom = /PhantomJS/.test(userAgent); + + var ios = safari && (/Mobile\/\w+/.test(userAgent) || navigator.maxTouchPoints > 2); + var android = /Android/.test(userAgent); + // This is woefully incomplete. Suggestions for alternative methods welcome. + var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent); + var mac = ios || /Mac/.test(platform); + var chromeOS = /\bCrOS\b/.test(userAgent); + var windows = /win/i.test(platform); + + var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/); + if (presto_version) { presto_version = Number(presto_version[1]); } + if (presto_version && presto_version >= 15) { presto = false; webkit = true; } + // Some browsers use the wrong event properties to signal cmd/ctrl on OS X + var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11)); + var captureRightClick = gecko || (ie && ie_version >= 9); + + function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") } + + var rmClass = function(node, cls) { + var current = node.className; + var match = classTest(cls).exec(current); + if (match) { + var after = current.slice(match.index + match[0].length); + node.className = current.slice(0, match.index) + (after ? match[1] + after : ""); + } + }; + + function removeChildren(e) { + for (var count = e.childNodes.length; count > 0; --count) + { e.removeChild(e.firstChild); } + return e + } + + function removeChildrenAndAdd(parent, e) { + return removeChildren(parent).appendChild(e) + } + + function elt(tag, content, className, style) { + var e = document.createElement(tag); + if (className) { e.className = className; } + if (style) { e.style.cssText = style; } + if (typeof content == "string") { e.appendChild(document.createTextNode(content)); } + else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } } + return e + } + // wrapper for elt, which removes the elt from the accessibility tree + function eltP(tag, content, className, style) { + var e = elt(tag, content, className, style); + e.setAttribute("role", "presentation"); + return e + } + + var range; + if (document.createRange) { range = function(node, start, end, endNode) { + var r = document.createRange(); + r.setEnd(endNode || node, end); + r.setStart(node, start); + return r + }; } + else { range = function(node, start, end) { + var r = document.body.createTextRange(); + try { r.moveToElementText(node.parentNode); } + catch(e) { return r } + r.collapse(true); + r.moveEnd("character", end); + r.moveStart("character", start); + return r + }; } + + function contains(parent, child) { + if (child.nodeType == 3) // Android browser always returns false when child is a textnode + { child = child.parentNode; } + if (parent.contains) + { return parent.contains(child) } + do { + if (child.nodeType == 11) { child = child.host; } + if (child == parent) { return true } + } while (child = child.parentNode) + } + + function activeElt(doc) { + // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement. + // IE < 10 will throw when accessed while the page is loading or in an iframe. + // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable. + var activeElement; + try { + activeElement = doc.activeElement; + } catch(e) { + activeElement = doc.body || null; + } + while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement) + { activeElement = activeElement.shadowRoot.activeElement; } + return activeElement + } + + function addClass(node, cls) { + var current = node.className; + if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; } + } + function joinClasses(a, b) { + var as = a.split(" "); + for (var i = 0; i < as.length; i++) + { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } } + return b + } + + var selectInput = function(node) { node.select(); }; + if (ios) // Mobile Safari apparently has a bug where select() is broken. + { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; } + else if (ie) // Suppress mysterious IE10 errors + { selectInput = function(node) { try { node.select(); } catch(_e) {} }; } + + function doc(cm) { return cm.display.wrapper.ownerDocument } + + function win(cm) { return doc(cm).defaultView } + + function bind(f) { + var args = Array.prototype.slice.call(arguments, 1); + return function(){return f.apply(null, args)} + } + + function copyObj(obj, target, overwrite) { + if (!target) { target = {}; } + for (var prop in obj) + { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop))) + { target[prop] = obj[prop]; } } + return target + } + + // Counts the column offset in a string, taking tabs into account. + // Used mostly to find indentation. + function countColumn(string, end, tabSize, startIndex, startValue) { + if (end == null) { + end = string.search(/[^\s\u00a0]/); + if (end == -1) { end = string.length; } + } + for (var i = startIndex || 0, n = startValue || 0;;) { + var nextTab = string.indexOf("\t", i); + if (nextTab < 0 || nextTab >= end) + { return n + (end - i) } + n += nextTab - i; + n += tabSize - (n % tabSize); + i = nextTab + 1; + } + } + + var Delayed = function() { + this.id = null; + this.f = null; + this.time = 0; + this.handler = bind(this.onTimeout, this); + }; + Delayed.prototype.onTimeout = function (self) { + self.id = 0; + if (self.time <= +new Date) { + self.f(); + } else { + setTimeout(self.handler, self.time - +new Date); + } + }; + Delayed.prototype.set = function (ms, f) { + this.f = f; + var time = +new Date + ms; + if (!this.id || time < this.time) { + clearTimeout(this.id); + this.id = setTimeout(this.handler, ms); + this.time = time; + } + }; + + function indexOf(array, elt) { + for (var i = 0; i < array.length; ++i) + { if (array[i] == elt) { return i } } + return -1 + } + + // Number of pixels added to scroller and sizer to hide scrollbar + var scrollerGap = 50; + + // Returned or thrown by various protocols to signal 'I'm not + // handling this'. + var Pass = {toString: function(){return "CodeMirror.Pass"}}; + + // Reused option objects for setSelection & friends + var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"}; + + // The inverse of countColumn -- find the offset that corresponds to + // a particular column. + function findColumn(string, goal, tabSize) { + for (var pos = 0, col = 0;;) { + var nextTab = string.indexOf("\t", pos); + if (nextTab == -1) { nextTab = string.length; } + var skipped = nextTab - pos; + if (nextTab == string.length || col + skipped >= goal) + { return pos + Math.min(skipped, goal - col) } + col += nextTab - pos; + col += tabSize - (col % tabSize); + pos = nextTab + 1; + if (col >= goal) { return pos } + } + } + + var spaceStrs = [""]; + function spaceStr(n) { + while (spaceStrs.length <= n) + { spaceStrs.push(lst(spaceStrs) + " "); } + return spaceStrs[n] + } + + function lst(arr) { return arr[arr.length-1] } + + function map(array, f) { + var out = []; + for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); } + return out + } + + function insertSorted(array, value, score) { + var pos = 0, priority = score(value); + while (pos < array.length && score(array[pos]) <= priority) { pos++; } + array.splice(pos, 0, value); + } + + function nothing() {} + + function createObj(base, props) { + var inst; + if (Object.create) { + inst = Object.create(base); + } else { + nothing.prototype = base; + inst = new nothing(); + } + if (props) { copyObj(props, inst); } + return inst + } + + var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; + function isWordCharBasic(ch) { + return /\w/.test(ch) || ch > "\x80" && + (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)) + } + function isWordChar(ch, helper) { + if (!helper) { return isWordCharBasic(ch) } + if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true } + return helper.test(ch) + } + + function isEmpty(obj) { + for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } } + return true + } + + // Extending unicode characters. A series of a non-extending char + + // any number of extending chars is treated as a single unit as far + // as editing and measuring is concerned. This is not fully correct, + // since some scripts/fonts/browsers also treat other configurations + // of code points as a group. + var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/; + function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) } + + // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range. + function skipExtendingChars(str, pos, dir) { + while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; } + return pos + } + + // Returns the value from the range [`from`; `to`] that satisfies + // `pred` and is closest to `from`. Assumes that at least `to` + // satisfies `pred`. Supports `from` being greater than `to`. + function findFirst(pred, from, to) { + // At any point we are certain `to` satisfies `pred`, don't know + // whether `from` does. + var dir = from > to ? -1 : 1; + for (;;) { + if (from == to) { return from } + var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF); + if (mid == from) { return pred(mid) ? from : to } + if (pred(mid)) { to = mid; } + else { from = mid + dir; } + } + } + + // BIDI HELPERS + + function iterateBidiSections(order, from, to, f) { + if (!order) { return f(from, to, "ltr", 0) } + var found = false; + for (var i = 0; i < order.length; ++i) { + var part = order[i]; + if (part.from < to && part.to > from || from == to && part.to == from) { + f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i); + found = true; + } + } + if (!found) { f(from, to, "ltr"); } + } + + var bidiOther = null; + function getBidiPartAt(order, ch, sticky) { + var found; + bidiOther = null; + for (var i = 0; i < order.length; ++i) { + var cur = order[i]; + if (cur.from < ch && cur.to > ch) { return i } + if (cur.to == ch) { + if (cur.from != cur.to && sticky == "before") { found = i; } + else { bidiOther = i; } + } + if (cur.from == ch) { + if (cur.from != cur.to && sticky != "before") { found = i; } + else { bidiOther = i; } + } + } + return found != null ? found : bidiOther + } + + // Bidirectional ordering algorithm + // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm + // that this (partially) implements. + + // One-char codes used for character types: + // L (L): Left-to-Right + // R (R): Right-to-Left + // r (AL): Right-to-Left Arabic + // 1 (EN): European Number + // + (ES): European Number Separator + // % (ET): European Number Terminator + // n (AN): Arabic Number + // , (CS): Common Number Separator + // m (NSM): Non-Spacing Mark + // b (BN): Boundary Neutral + // s (B): Paragraph Separator + // t (S): Segment Separator + // w (WS): Whitespace + // N (ON): Other Neutrals + + // Returns null if characters are ordered as they appear + // (left-to-right), or an array of sections ({from, to, level} + // objects) in the order in which they occur visually. + var bidiOrdering = (function() { + // Character types for codepoints 0 to 0xff + var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"; + // Character types for codepoints 0x600 to 0x6f9 + var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111"; + function charType(code) { + if (code <= 0xf7) { return lowTypes.charAt(code) } + else if (0x590 <= code && code <= 0x5f4) { return "R" } + else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) } + else if (0x6ee <= code && code <= 0x8ac) { return "r" } + else if (0x2000 <= code && code <= 0x200b) { return "w" } + else if (code == 0x200c) { return "b" } + else { return "L" } + } + + var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/; + var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/; + + function BidiSpan(level, from, to) { + this.level = level; + this.from = from; this.to = to; + } + + return function(str, direction) { + var outerType = direction == "ltr" ? "L" : "R"; + + if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false } + var len = str.length, types = []; + for (var i = 0; i < len; ++i) + { types.push(charType(str.charCodeAt(i))); } + + // W1. Examine each non-spacing mark (NSM) in the level run, and + // change the type of the NSM to the type of the previous + // character. If the NSM is at the start of the level run, it will + // get the type of sor. + for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) { + var type = types[i$1]; + if (type == "m") { types[i$1] = prev; } + else { prev = type; } + } + + // W2. Search backwards from each instance of a European number + // until the first strong type (R, L, AL, or sor) is found. If an + // AL is found, change the type of the European number to Arabic + // number. + // W3. Change all ALs to R. + for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) { + var type$1 = types[i$2]; + if (type$1 == "1" && cur == "r") { types[i$2] = "n"; } + else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } } + } + + // W4. A single European separator between two European numbers + // changes to a European number. A single common separator between + // two numbers of the same type changes to that type. + for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) { + var type$2 = types[i$3]; + if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; } + else if (type$2 == "," && prev$1 == types[i$3+1] && + (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; } + prev$1 = type$2; + } + + // W5. A sequence of European terminators adjacent to European + // numbers changes to all European numbers. + // W6. Otherwise, separators and terminators change to Other + // Neutral. + for (var i$4 = 0; i$4 < len; ++i$4) { + var type$3 = types[i$4]; + if (type$3 == ",") { types[i$4] = "N"; } + else if (type$3 == "%") { + var end = (void 0); + for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {} + var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"; + for (var j = i$4; j < end; ++j) { types[j] = replace; } + i$4 = end - 1; + } + } + + // W7. Search backwards from each instance of a European number + // until the first strong type (R, L, or sor) is found. If an L is + // found, then change the type of the European number to L. + for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) { + var type$4 = types[i$5]; + if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; } + else if (isStrong.test(type$4)) { cur$1 = type$4; } + } + + // N1. A sequence of neutrals takes the direction of the + // surrounding strong text if the text on both sides has the same + // direction. European and Arabic numbers act as if they were R in + // terms of their influence on neutrals. Start-of-level-run (sor) + // and end-of-level-run (eor) are used at level run boundaries. + // N2. Any remaining neutrals take the embedding direction. + for (var i$6 = 0; i$6 < len; ++i$6) { + if (isNeutral.test(types[i$6])) { + var end$1 = (void 0); + for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {} + var before = (i$6 ? types[i$6-1] : outerType) == "L"; + var after = (end$1 < len ? types[end$1] : outerType) == "L"; + var replace$1 = before == after ? (before ? "L" : "R") : outerType; + for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; } + i$6 = end$1 - 1; + } + } + + // Here we depart from the documented algorithm, in order to avoid + // building up an actual levels array. Since there are only three + // levels (0, 1, 2) in an implementation that doesn't take + // explicit embedding into account, we can build up the order on + // the fly, without following the level-based algorithm. + var order = [], m; + for (var i$7 = 0; i$7 < len;) { + if (countsAsLeft.test(types[i$7])) { + var start = i$7; + for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {} + order.push(new BidiSpan(0, start, i$7)); + } else { + var pos = i$7, at = order.length, isRTL = direction == "rtl" ? 1 : 0; + for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {} + for (var j$2 = pos; j$2 < i$7;) { + if (countsAsNum.test(types[j$2])) { + if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); at += isRTL; } + var nstart = j$2; + for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {} + order.splice(at, 0, new BidiSpan(2, nstart, j$2)); + at += isRTL; + pos = j$2; + } else { ++j$2; } + } + if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); } + } + } + if (direction == "ltr") { + if (order[0].level == 1 && (m = str.match(/^\s+/))) { + order[0].from = m[0].length; + order.unshift(new BidiSpan(0, 0, m[0].length)); + } + if (lst(order).level == 1 && (m = str.match(/\s+$/))) { + lst(order).to -= m[0].length; + order.push(new BidiSpan(0, len - m[0].length, len)); + } + } + + return direction == "rtl" ? order.reverse() : order + } + })(); + + // Get the bidi ordering for the given line (and cache it). Returns + // false for lines that are fully left-to-right, and an array of + // BidiSpan objects otherwise. + function getOrder(line, direction) { + var order = line.order; + if (order == null) { order = line.order = bidiOrdering(line.text, direction); } + return order + } + + // EVENT HANDLING + + // Lightweight event framework. on/off also work on DOM nodes, + // registering native DOM handlers. + + var noHandlers = []; + + var on = function(emitter, type, f) { + if (emitter.addEventListener) { + emitter.addEventListener(type, f, false); + } else if (emitter.attachEvent) { + emitter.attachEvent("on" + type, f); + } else { + var map = emitter._handlers || (emitter._handlers = {}); + map[type] = (map[type] || noHandlers).concat(f); + } + }; + + function getHandlers(emitter, type) { + return emitter._handlers && emitter._handlers[type] || noHandlers + } + + function off(emitter, type, f) { + if (emitter.removeEventListener) { + emitter.removeEventListener(type, f, false); + } else if (emitter.detachEvent) { + emitter.detachEvent("on" + type, f); + } else { + var map = emitter._handlers, arr = map && map[type]; + if (arr) { + var index = indexOf(arr, f); + if (index > -1) + { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)); } + } + } + } + + function signal(emitter, type /*, values...*/) { + var handlers = getHandlers(emitter, type); + if (!handlers.length) { return } + var args = Array.prototype.slice.call(arguments, 2); + for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); } + } + + // The DOM events that CodeMirror handles can be overridden by + // registering a (non-DOM) handler on the editor for the event name, + // and preventDefault-ing the event in that handler. + function signalDOMEvent(cm, e, override) { + if (typeof e == "string") + { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; } + signal(cm, override || e.type, cm, e); + return e_defaultPrevented(e) || e.codemirrorIgnore + } + + function signalCursorActivity(cm) { + var arr = cm._handlers && cm._handlers.cursorActivity; + if (!arr) { return } + var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []); + for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1) + { set.push(arr[i]); } } + } + + function hasHandler(emitter, type) { + return getHandlers(emitter, type).length > 0 + } + + // Add on and off methods to a constructor's prototype, to make + // registering events on such objects more convenient. + function eventMixin(ctor) { + ctor.prototype.on = function(type, f) {on(this, type, f);}; + ctor.prototype.off = function(type, f) {off(this, type, f);}; + } + + // Due to the fact that we still support jurassic IE versions, some + // compatibility wrappers are needed. + + function e_preventDefault(e) { + if (e.preventDefault) { e.preventDefault(); } + else { e.returnValue = false; } + } + function e_stopPropagation(e) { + if (e.stopPropagation) { e.stopPropagation(); } + else { e.cancelBubble = true; } + } + function e_defaultPrevented(e) { + return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false + } + function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);} + + function e_target(e) {return e.target || e.srcElement} + function e_button(e) { + var b = e.which; + if (b == null) { + if (e.button & 1) { b = 1; } + else if (e.button & 2) { b = 3; } + else if (e.button & 4) { b = 2; } + } + if (mac && e.ctrlKey && b == 1) { b = 3; } + return b + } + + // Detect drag-and-drop + var dragAndDrop = function() { + // There is *some* kind of drag-and-drop support in IE6-8, but I + // couldn't get it to work yet. + if (ie && ie_version < 9) { return false } + var div = elt('div'); + return "draggable" in div || "dragDrop" in div + }(); + + var zwspSupported; + function zeroWidthElement(measure) { + if (zwspSupported == null) { + var test = elt("span", "\u200b"); + removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")])); + if (measure.firstChild.offsetHeight != 0) + { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); } + } + var node = zwspSupported ? elt("span", "\u200b") : + elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px"); + node.setAttribute("cm-text", ""); + return node + } + + // Feature-detect IE's crummy client rect reporting for bidi text + var badBidiRects; + function hasBadBidiRects(measure) { + if (badBidiRects != null) { return badBidiRects } + var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA")); + var r0 = range(txt, 0, 1).getBoundingClientRect(); + var r1 = range(txt, 1, 2).getBoundingClientRect(); + removeChildren(measure); + if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780) + return badBidiRects = (r1.right - r0.right < 3) + } + + // See if "".split is the broken IE version, if so, provide an + // alternative way to split lines. + var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) { + var pos = 0, result = [], l = string.length; + while (pos <= l) { + var nl = string.indexOf("\n", pos); + if (nl == -1) { nl = string.length; } + var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl); + var rt = line.indexOf("\r"); + if (rt != -1) { + result.push(line.slice(0, rt)); + pos += rt + 1; + } else { + result.push(line); + pos = nl + 1; + } + } + return result + } : function (string) { return string.split(/\r\n?|\n/); }; + + var hasSelection = window.getSelection ? function (te) { + try { return te.selectionStart != te.selectionEnd } + catch(e) { return false } + } : function (te) { + var range; + try {range = te.ownerDocument.selection.createRange();} + catch(e) {} + if (!range || range.parentElement() != te) { return false } + return range.compareEndPoints("StartToEnd", range) != 0 + }; + + var hasCopyEvent = (function () { + var e = elt("div"); + if ("oncopy" in e) { return true } + e.setAttribute("oncopy", "return;"); + return typeof e.oncopy == "function" + })(); + + var badZoomedRects = null; + function hasBadZoomedRects(measure) { + if (badZoomedRects != null) { return badZoomedRects } + var node = removeChildrenAndAdd(measure, elt("span", "x")); + var normal = node.getBoundingClientRect(); + var fromRange = range(node, 0, 1).getBoundingClientRect(); + return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1 + } + + // Known modes, by name and by MIME + var modes = {}, mimeModes = {}; + + // Extra arguments are stored as the mode's dependencies, which is + // used by (legacy) mechanisms like loadmode.js to automatically + // load a mode. (Preferred mechanism is the require/define calls.) + function defineMode(name, mode) { + if (arguments.length > 2) + { mode.dependencies = Array.prototype.slice.call(arguments, 2); } + modes[name] = mode; + } + + function defineMIME(mime, spec) { + mimeModes[mime] = spec; + } + + // Given a MIME type, a {name, ...options} config object, or a name + // string, return a mode config object. + function resolveMode(spec) { + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { + spec = mimeModes[spec]; + } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { + var found = mimeModes[spec.name]; + if (typeof found == "string") { found = {name: found}; } + spec = createObj(found, spec); + spec.name = found.name; + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { + return resolveMode("application/xml") + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) { + return resolveMode("application/json") + } + if (typeof spec == "string") { return {name: spec} } + else { return spec || {name: "null"} } + } + + // Given a mode spec (anything that resolveMode accepts), find and + // initialize an actual mode object. + function getMode(options, spec) { + spec = resolveMode(spec); + var mfactory = modes[spec.name]; + if (!mfactory) { return getMode(options, "text/plain") } + var modeObj = mfactory(options, spec); + if (modeExtensions.hasOwnProperty(spec.name)) { + var exts = modeExtensions[spec.name]; + for (var prop in exts) { + if (!exts.hasOwnProperty(prop)) { continue } + if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; } + modeObj[prop] = exts[prop]; + } + } + modeObj.name = spec.name; + if (spec.helperType) { modeObj.helperType = spec.helperType; } + if (spec.modeProps) { for (var prop$1 in spec.modeProps) + { modeObj[prop$1] = spec.modeProps[prop$1]; } } + + return modeObj + } + + // This can be used to attach properties to mode objects from + // outside the actual mode definition. + var modeExtensions = {}; + function extendMode(mode, properties) { + var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); + copyObj(properties, exts); + } + + function copyState(mode, state) { + if (state === true) { return state } + if (mode.copyState) { return mode.copyState(state) } + var nstate = {}; + for (var n in state) { + var val = state[n]; + if (val instanceof Array) { val = val.concat([]); } + nstate[n] = val; + } + return nstate + } + + // Given a mode and a state (for that mode), find the inner mode and + // state at the position that the state refers to. + function innerMode(mode, state) { + var info; + while (mode.innerMode) { + info = mode.innerMode(state); + if (!info || info.mode == mode) { break } + state = info.state; + mode = info.mode; + } + return info || {mode: mode, state: state} + } + + function startState(mode, a1, a2) { + return mode.startState ? mode.startState(a1, a2) : true + } + + // STRING STREAM + + // Fed to the mode parsers, provides helper functions to make + // parsers more succinct. + + var StringStream = function(string, tabSize, lineOracle) { + this.pos = this.start = 0; + this.string = string; + this.tabSize = tabSize || 8; + this.lastColumnPos = this.lastColumnValue = 0; + this.lineStart = 0; + this.lineOracle = lineOracle; + }; + + StringStream.prototype.eol = function () {return this.pos >= this.string.length}; + StringStream.prototype.sol = function () {return this.pos == this.lineStart}; + StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined}; + StringStream.prototype.next = function () { + if (this.pos < this.string.length) + { return this.string.charAt(this.pos++) } + }; + StringStream.prototype.eat = function (match) { + var ch = this.string.charAt(this.pos); + var ok; + if (typeof match == "string") { ok = ch == match; } + else { ok = ch && (match.test ? match.test(ch) : match(ch)); } + if (ok) {++this.pos; return ch} + }; + StringStream.prototype.eatWhile = function (match) { + var start = this.pos; + while (this.eat(match)){} + return this.pos > start + }; + StringStream.prototype.eatSpace = function () { + var start = this.pos; + while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; } + return this.pos > start + }; + StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;}; + StringStream.prototype.skipTo = function (ch) { + var found = this.string.indexOf(ch, this.pos); + if (found > -1) {this.pos = found; return true} + }; + StringStream.prototype.backUp = function (n) {this.pos -= n;}; + StringStream.prototype.column = function () { + if (this.lastColumnPos < this.start) { + this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue); + this.lastColumnPos = this.start; + } + return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) + }; + StringStream.prototype.indentation = function () { + return countColumn(this.string, null, this.tabSize) - + (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) + }; + StringStream.prototype.match = function (pattern, consume, caseInsensitive) { + if (typeof pattern == "string") { + var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; }; + var substr = this.string.substr(this.pos, pattern.length); + if (cased(substr) == cased(pattern)) { + if (consume !== false) { this.pos += pattern.length; } + return true + } + } else { + var match = this.string.slice(this.pos).match(pattern); + if (match && match.index > 0) { return null } + if (match && consume !== false) { this.pos += match[0].length; } + return match + } + }; + StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)}; + StringStream.prototype.hideFirstChars = function (n, inner) { + this.lineStart += n; + try { return inner() } + finally { this.lineStart -= n; } + }; + StringStream.prototype.lookAhead = function (n) { + var oracle = this.lineOracle; + return oracle && oracle.lookAhead(n) + }; + StringStream.prototype.baseToken = function () { + var oracle = this.lineOracle; + return oracle && oracle.baseToken(this.pos) + }; + + // Find the line object corresponding to the given line number. + function getLine(doc, n) { + n -= doc.first; + if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") } + var chunk = doc; + while (!chunk.lines) { + for (var i = 0;; ++i) { + var child = chunk.children[i], sz = child.chunkSize(); + if (n < sz) { chunk = child; break } + n -= sz; + } + } + return chunk.lines[n] + } + + // Get the part of a document between two positions, as an array of + // strings. + function getBetween(doc, start, end) { + var out = [], n = start.line; + doc.iter(start.line, end.line + 1, function (line) { + var text = line.text; + if (n == end.line) { text = text.slice(0, end.ch); } + if (n == start.line) { text = text.slice(start.ch); } + out.push(text); + ++n; + }); + return out + } + // Get the lines between from and to, as array of strings. + function getLines(doc, from, to) { + var out = []; + doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value + return out + } + + // Update the height of a line, propagating the height change + // upwards to parent nodes. + function updateLineHeight(line, height) { + var diff = height - line.height; + if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } } + } + + // Given a line object, find its line number by walking up through + // its parent links. + function lineNo(line) { + if (line.parent == null) { return null } + var cur = line.parent, no = indexOf(cur.lines, line); + for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { + for (var i = 0;; ++i) { + if (chunk.children[i] == cur) { break } + no += chunk.children[i].chunkSize(); + } + } + return no + cur.first + } + + // Find the line at the given vertical position, using the height + // information in the document tree. + function lineAtHeight(chunk, h) { + var n = chunk.first; + outer: do { + for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) { + var child = chunk.children[i$1], ch = child.height; + if (h < ch) { chunk = child; continue outer } + h -= ch; + n += child.chunkSize(); + } + return n + } while (!chunk.lines) + var i = 0; + for (; i < chunk.lines.length; ++i) { + var line = chunk.lines[i], lh = line.height; + if (h < lh) { break } + h -= lh; + } + return n + i + } + + function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size} + + function lineNumberFor(options, i) { + return String(options.lineNumberFormatter(i + options.firstLineNumber)) + } + + // A Pos instance represents a position within the text. + function Pos(line, ch, sticky) { + if ( sticky === void 0 ) sticky = null; + + if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) } + this.line = line; + this.ch = ch; + this.sticky = sticky; + } + + // Compare two positions, return 0 if they are the same, a negative + // number when a is less, and a positive number otherwise. + function cmp(a, b) { return a.line - b.line || a.ch - b.ch } + + function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 } + + function copyPos(x) {return Pos(x.line, x.ch)} + function maxPos(a, b) { return cmp(a, b) < 0 ? b : a } + function minPos(a, b) { return cmp(a, b) < 0 ? a : b } + + // Most of the external API clips given positions to make sure they + // actually exist within the document. + function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))} + function clipPos(doc, pos) { + if (pos.line < doc.first) { return Pos(doc.first, 0) } + var last = doc.first + doc.size - 1; + if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) } + return clipToLen(pos, getLine(doc, pos.line).text.length) + } + function clipToLen(pos, linelen) { + var ch = pos.ch; + if (ch == null || ch > linelen) { return Pos(pos.line, linelen) } + else if (ch < 0) { return Pos(pos.line, 0) } + else { return pos } + } + function clipPosArray(doc, array) { + var out = []; + for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); } + return out + } + + var SavedContext = function(state, lookAhead) { + this.state = state; + this.lookAhead = lookAhead; + }; + + var Context = function(doc, state, line, lookAhead) { + this.state = state; + this.doc = doc; + this.line = line; + this.maxLookAhead = lookAhead || 0; + this.baseTokens = null; + this.baseTokenPos = 1; + }; + + Context.prototype.lookAhead = function (n) { + var line = this.doc.getLine(this.line + n); + if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; } + return line + }; + + Context.prototype.baseToken = function (n) { + if (!this.baseTokens) { return null } + while (this.baseTokens[this.baseTokenPos] <= n) + { this.baseTokenPos += 2; } + var type = this.baseTokens[this.baseTokenPos + 1]; + return {type: type && type.replace(/( |^)overlay .*/, ""), + size: this.baseTokens[this.baseTokenPos] - n} + }; + + Context.prototype.nextLine = function () { + this.line++; + if (this.maxLookAhead > 0) { this.maxLookAhead--; } + }; + + Context.fromSaved = function (doc, saved, line) { + if (saved instanceof SavedContext) + { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) } + else + { return new Context(doc, copyState(doc.mode, saved), line) } + }; + + Context.prototype.save = function (copy) { + var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state; + return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state + }; + + + // Compute a style array (an array starting with a mode generation + // -- for invalidation -- followed by pairs of end positions and + // style strings), which is used to highlight the tokens on the + // line. + function highlightLine(cm, line, context, forceToEnd) { + // A styles array always starts with a number identifying the + // mode/overlays that it is based on (for easy invalidation). + var st = [cm.state.modeGen], lineClasses = {}; + // Compute the base array of styles + runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); }, + lineClasses, forceToEnd); + var state = context.state; + + // Run overlays, adjust style array. + var loop = function ( o ) { + context.baseTokens = st; + var overlay = cm.state.overlays[o], i = 1, at = 0; + context.state = true; + runMode(cm, line.text, overlay.mode, context, function (end, style) { + var start = i; + // Ensure there's a token end at the current position, and that i points at it + while (at < end) { + var i_end = st[i]; + if (i_end > end) + { st.splice(i, 1, end, st[i+1], i_end); } + i += 2; + at = Math.min(end, i_end); + } + if (!style) { return } + if (overlay.opaque) { + st.splice(start, i - start, end, "overlay " + style); + i = start + 2; + } else { + for (; start < i; start += 2) { + var cur = st[start+1]; + st[start+1] = (cur ? cur + " " : "") + "overlay " + style; + } + } + }, lineClasses); + context.state = state; + context.baseTokens = null; + context.baseTokenPos = 1; + }; + + for (var o = 0; o < cm.state.overlays.length; ++o) loop( o ); + + return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null} + } + + function getLineStyles(cm, line, updateFrontier) { + if (!line.styles || line.styles[0] != cm.state.modeGen) { + var context = getContextBefore(cm, lineNo(line)); + var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state); + var result = highlightLine(cm, line, context); + if (resetState) { context.state = resetState; } + line.stateAfter = context.save(!resetState); + line.styles = result.styles; + if (result.classes) { line.styleClasses = result.classes; } + else if (line.styleClasses) { line.styleClasses = null; } + if (updateFrontier === cm.doc.highlightFrontier) + { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); } + } + return line.styles + } + + function getContextBefore(cm, n, precise) { + var doc = cm.doc, display = cm.display; + if (!doc.mode.startState) { return new Context(doc, true, n) } + var start = findStartLine(cm, n, precise); + var saved = start > doc.first && getLine(doc, start - 1).stateAfter; + var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start); + + doc.iter(start, n, function (line) { + processLine(cm, line.text, context); + var pos = context.line; + line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null; + context.nextLine(); + }); + if (precise) { doc.modeFrontier = context.line; } + return context + } + + // Lightweight form of highlight -- proceed over this line and + // update state, but don't save a style array. Used for lines that + // aren't currently visible. + function processLine(cm, text, context, startAt) { + var mode = cm.doc.mode; + var stream = new StringStream(text, cm.options.tabSize, context); + stream.start = stream.pos = startAt || 0; + if (text == "") { callBlankLine(mode, context.state); } + while (!stream.eol()) { + readToken(mode, stream, context.state); + stream.start = stream.pos; + } + } + + function callBlankLine(mode, state) { + if (mode.blankLine) { return mode.blankLine(state) } + if (!mode.innerMode) { return } + var inner = innerMode(mode, state); + if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) } + } + + function readToken(mode, stream, state, inner) { + for (var i = 0; i < 10; i++) { + if (inner) { inner[0] = innerMode(mode, state).mode; } + var style = mode.token(stream, state); + if (stream.pos > stream.start) { return style } + } + throw new Error("Mode " + mode.name + " failed to advance stream.") + } + + var Token = function(stream, type, state) { + this.start = stream.start; this.end = stream.pos; + this.string = stream.current(); + this.type = type || null; + this.state = state; + }; + + // Utility for getTokenAt and getLineTokens + function takeToken(cm, pos, precise, asArray) { + var doc = cm.doc, mode = doc.mode, style; + pos = clipPos(doc, pos); + var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise); + var stream = new StringStream(line.text, cm.options.tabSize, context), tokens; + if (asArray) { tokens = []; } + while ((asArray || stream.pos < pos.ch) && !stream.eol()) { + stream.start = stream.pos; + style = readToken(mode, stream, context.state); + if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); } + } + return asArray ? tokens : new Token(stream, style, context.state) + } + + function extractLineClasses(type, output) { + if (type) { for (;;) { + var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/); + if (!lineClass) { break } + type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length); + var prop = lineClass[1] ? "bgClass" : "textClass"; + if (output[prop] == null) + { output[prop] = lineClass[2]; } + else if (!(new RegExp("(?:^|\\s)" + lineClass[2] + "(?:$|\\s)")).test(output[prop])) + { output[prop] += " " + lineClass[2]; } + } } + return type + } + + // Run the given mode's parser over a line, calling f for each token. + function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) { + var flattenSpans = mode.flattenSpans; + if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; } + var curStart = 0, curStyle = null; + var stream = new StringStream(text, cm.options.tabSize, context), style; + var inner = cm.options.addModeClass && [null]; + if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); } + while (!stream.eol()) { + if (stream.pos > cm.options.maxHighlightLength) { + flattenSpans = false; + if (forceToEnd) { processLine(cm, text, context, stream.pos); } + stream.pos = text.length; + style = null; + } else { + style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses); + } + if (inner) { + var mName = inner[0].name; + if (mName) { style = "m-" + (style ? mName + " " + style : mName); } + } + if (!flattenSpans || curStyle != style) { + while (curStart < stream.start) { + curStart = Math.min(stream.start, curStart + 5000); + f(curStart, curStyle); + } + curStyle = style; + } + stream.start = stream.pos; + } + while (curStart < stream.pos) { + // Webkit seems to refuse to render text nodes longer than 57444 + // characters, and returns inaccurate measurements in nodes + // starting around 5000 chars. + var pos = Math.min(stream.pos, curStart + 5000); + f(pos, curStyle); + curStart = pos; + } + } + + // Finds the line to start with when starting a parse. Tries to + // find a line with a stateAfter, so that it can start with a + // valid state. If that fails, it returns the line with the + // smallest indentation, which tends to need the least context to + // parse correctly. + function findStartLine(cm, n, precise) { + var minindent, minline, doc = cm.doc; + var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100); + for (var search = n; search > lim; --search) { + if (search <= doc.first) { return doc.first } + var line = getLine(doc, search - 1), after = line.stateAfter; + if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier)) + { return search } + var indented = countColumn(line.text, null, cm.options.tabSize); + if (minline == null || minindent > indented) { + minline = search - 1; + minindent = indented; + } + } + return minline + } + + function retreatFrontier(doc, n) { + doc.modeFrontier = Math.min(doc.modeFrontier, n); + if (doc.highlightFrontier < n - 10) { return } + var start = doc.first; + for (var line = n - 1; line > start; line--) { + var saved = getLine(doc, line).stateAfter; + // change is on 3 + // state on line 1 looked ahead 2 -- so saw 3 + // test 1 + 2 < 3 should cover this + if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) { + start = line + 1; + break + } + } + doc.highlightFrontier = Math.min(doc.highlightFrontier, start); + } + + // Optimize some code when these features are not used. + var sawReadOnlySpans = false, sawCollapsedSpans = false; + + function seeReadOnlySpans() { + sawReadOnlySpans = true; + } + + function seeCollapsedSpans() { + sawCollapsedSpans = true; + } + + // TEXTMARKER SPANS + + function MarkedSpan(marker, from, to) { + this.marker = marker; + this.from = from; this.to = to; + } + + // Search an array of spans for a span matching the given marker. + function getMarkedSpanFor(spans, marker) { + if (spans) { for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.marker == marker) { return span } + } } + } + + // Remove a span from an array, returning undefined if no spans are + // left (we don't store arrays for lines without spans). + function removeMarkedSpan(spans, span) { + var r; + for (var i = 0; i < spans.length; ++i) + { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } } + return r + } + + // Add a span to a line. + function addMarkedSpan(line, span, op) { + var inThisOp = op && window.WeakSet && (op.markedSpans || (op.markedSpans = new WeakSet)); + if (inThisOp && line.markedSpans && inThisOp.has(line.markedSpans)) { + line.markedSpans.push(span); + } else { + line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]; + if (inThisOp) { inThisOp.add(line.markedSpans); } + } + span.marker.attachLine(line); + } + + // Used for the algorithm that adjusts markers for a change in the + // document. These functions cut an array of spans at a given + // character position, returning an array of remaining chunks (or + // undefined if nothing remains). + function markedSpansBefore(old, startCh, isInsert) { + var nw; + if (old) { for (var i = 0; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh); + if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) { + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh) + ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to)); + } + } } + return nw + } + function markedSpansAfter(old, endCh, isInsert) { + var nw; + if (old) { for (var i = 0; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh); + if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) { + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh) + ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh, + span.to == null ? null : span.to - endCh)); + } + } } + return nw + } + + // Given a change object, compute the new set of marker spans that + // cover the line in which the change took place. Removes spans + // entirely within the change, reconnects spans belonging to the + // same marker that appear on both sides of the change, and cuts off + // spans partially within the change. Returns an array of span + // arrays with one element for each line in (after) the change. + function stretchSpansOverChange(doc, change) { + if (change.full) { return null } + var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans; + var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans; + if (!oldFirst && !oldLast) { return null } + + var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0; + // Get the spans that 'stick out' on both sides + var first = markedSpansBefore(oldFirst, startCh, isInsert); + var last = markedSpansAfter(oldLast, endCh, isInsert); + + // Next, merge those two ends + var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0); + if (first) { + // Fix up .to properties of first + for (var i = 0; i < first.length; ++i) { + var span = first[i]; + if (span.to == null) { + var found = getMarkedSpanFor(last, span.marker); + if (!found) { span.to = startCh; } + else if (sameLine) { span.to = found.to == null ? null : found.to + offset; } + } + } + } + if (last) { + // Fix up .from in last (or move them into first in case of sameLine) + for (var i$1 = 0; i$1 < last.length; ++i$1) { + var span$1 = last[i$1]; + if (span$1.to != null) { span$1.to += offset; } + if (span$1.from == null) { + var found$1 = getMarkedSpanFor(first, span$1.marker); + if (!found$1) { + span$1.from = offset; + if (sameLine) { (first || (first = [])).push(span$1); } + } + } else { + span$1.from += offset; + if (sameLine) { (first || (first = [])).push(span$1); } + } + } + } + // Make sure we didn't create any zero-length spans + if (first) { first = clearEmptySpans(first); } + if (last && last != first) { last = clearEmptySpans(last); } + + var newMarkers = [first]; + if (!sameLine) { + // Fill gap with whole-line-spans + var gap = change.text.length - 2, gapMarkers; + if (gap > 0 && first) + { for (var i$2 = 0; i$2 < first.length; ++i$2) + { if (first[i$2].to == null) + { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } } + for (var i$3 = 0; i$3 < gap; ++i$3) + { newMarkers.push(gapMarkers); } + newMarkers.push(last); + } + return newMarkers + } + + // Remove spans that are empty and don't have a clearWhenEmpty + // option of false. + function clearEmptySpans(spans) { + for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false) + { spans.splice(i--, 1); } + } + if (!spans.length) { return null } + return spans + } + + // Used to 'clip' out readOnly ranges when making a change. + function removeReadOnlyRanges(doc, from, to) { + var markers = null; + doc.iter(from.line, to.line + 1, function (line) { + if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { + var mark = line.markedSpans[i].marker; + if (mark.readOnly && (!markers || indexOf(markers, mark) == -1)) + { (markers || (markers = [])).push(mark); } + } } + }); + if (!markers) { return null } + var parts = [{from: from, to: to}]; + for (var i = 0; i < markers.length; ++i) { + var mk = markers[i], m = mk.find(0); + for (var j = 0; j < parts.length; ++j) { + var p = parts[j]; + if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue } + var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to); + if (dfrom < 0 || !mk.inclusiveLeft && !dfrom) + { newParts.push({from: p.from, to: m.from}); } + if (dto > 0 || !mk.inclusiveRight && !dto) + { newParts.push({from: m.to, to: p.to}); } + parts.splice.apply(parts, newParts); + j += newParts.length - 3; + } + } + return parts + } + + // Connect or disconnect spans from a line. + function detachMarkedSpans(line) { + var spans = line.markedSpans; + if (!spans) { return } + for (var i = 0; i < spans.length; ++i) + { spans[i].marker.detachLine(line); } + line.markedSpans = null; + } + function attachMarkedSpans(line, spans) { + if (!spans) { return } + for (var i = 0; i < spans.length; ++i) + { spans[i].marker.attachLine(line); } + line.markedSpans = spans; + } + + // Helpers used when computing which overlapping collapsed span + // counts as the larger one. + function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 } + function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 } + + // Returns a number indicating which of two overlapping collapsed + // spans is larger (and thus includes the other). Falls back to + // comparing ids when the spans cover exactly the same range. + function compareCollapsedMarkers(a, b) { + var lenDiff = a.lines.length - b.lines.length; + if (lenDiff != 0) { return lenDiff } + var aPos = a.find(), bPos = b.find(); + var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b); + if (fromCmp) { return -fromCmp } + var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b); + if (toCmp) { return toCmp } + return b.id - a.id + } + + // Find out whether a line ends or starts in a collapsed span. If + // so, return the marker for that span. + function collapsedSpanAtSide(line, start) { + var sps = sawCollapsedSpans && line.markedSpans, found; + if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { + sp = sps[i]; + if (sp.marker.collapsed && (start ? sp.from : sp.to) == null && + (!found || compareCollapsedMarkers(found, sp.marker) < 0)) + { found = sp.marker; } + } } + return found + } + function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) } + function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) } + + function collapsedSpanAround(line, ch) { + var sps = sawCollapsedSpans && line.markedSpans, found; + if (sps) { for (var i = 0; i < sps.length; ++i) { + var sp = sps[i]; + if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) && + (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; } + } } + return found + } + + // Test whether there exists a collapsed span that partially + // overlaps (covers the start or end, but not both) of a new span. + // Such overlap is not allowed. + function conflictingCollapsedRange(doc, lineNo, from, to, marker) { + var line = getLine(doc, lineNo); + var sps = sawCollapsedSpans && line.markedSpans; + if (sps) { for (var i = 0; i < sps.length; ++i) { + var sp = sps[i]; + if (!sp.marker.collapsed) { continue } + var found = sp.marker.find(0); + var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker); + var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker); + if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue } + if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) || + fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0)) + { return true } + } } + } + + // A visual line is a line as drawn on the screen. Folding, for + // example, can cause multiple logical lines to appear on the same + // visual line. This finds the start of the visual line that the + // given line is part of (usually that is the line itself). + function visualLine(line) { + var merged; + while (merged = collapsedSpanAtStart(line)) + { line = merged.find(-1, true).line; } + return line + } + + function visualLineEnd(line) { + var merged; + while (merged = collapsedSpanAtEnd(line)) + { line = merged.find(1, true).line; } + return line + } + + // Returns an array of logical lines that continue the visual line + // started by the argument, or undefined if there are no such lines. + function visualLineContinued(line) { + var merged, lines; + while (merged = collapsedSpanAtEnd(line)) { + line = merged.find(1, true).line + ;(lines || (lines = [])).push(line); + } + return lines + } + + // Get the line number of the start of the visual line that the + // given line number is part of. + function visualLineNo(doc, lineN) { + var line = getLine(doc, lineN), vis = visualLine(line); + if (line == vis) { return lineN } + return lineNo(vis) + } + + // Get the line number of the start of the next visual line after + // the given line. + function visualLineEndNo(doc, lineN) { + if (lineN > doc.lastLine()) { return lineN } + var line = getLine(doc, lineN), merged; + if (!lineIsHidden(doc, line)) { return lineN } + while (merged = collapsedSpanAtEnd(line)) + { line = merged.find(1, true).line; } + return lineNo(line) + 1 + } + + // Compute whether a line is hidden. Lines count as hidden when they + // are part of a visual line that starts with another line, or when + // they are entirely covered by collapsed, non-widget span. + function lineIsHidden(doc, line) { + var sps = sawCollapsedSpans && line.markedSpans; + if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { + sp = sps[i]; + if (!sp.marker.collapsed) { continue } + if (sp.from == null) { return true } + if (sp.marker.widgetNode) { continue } + if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp)) + { return true } + } } + } + function lineIsHiddenInner(doc, line, span) { + if (span.to == null) { + var end = span.marker.find(1, true); + return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker)) + } + if (span.marker.inclusiveRight && span.to == line.text.length) + { return true } + for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) { + sp = line.markedSpans[i]; + if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to && + (sp.to == null || sp.to != span.from) && + (sp.marker.inclusiveLeft || span.marker.inclusiveRight) && + lineIsHiddenInner(doc, line, sp)) { return true } + } + } + + // Find the height above the given line. + function heightAtLine(lineObj) { + lineObj = visualLine(lineObj); + + var h = 0, chunk = lineObj.parent; + for (var i = 0; i < chunk.lines.length; ++i) { + var line = chunk.lines[i]; + if (line == lineObj) { break } + else { h += line.height; } + } + for (var p = chunk.parent; p; chunk = p, p = chunk.parent) { + for (var i$1 = 0; i$1 < p.children.length; ++i$1) { + var cur = p.children[i$1]; + if (cur == chunk) { break } + else { h += cur.height; } + } + } + return h + } + + // Compute the character length of a line, taking into account + // collapsed ranges (see markText) that might hide parts, and join + // other lines onto it. + function lineLength(line) { + if (line.height == 0) { return 0 } + var len = line.text.length, merged, cur = line; + while (merged = collapsedSpanAtStart(cur)) { + var found = merged.find(0, true); + cur = found.from.line; + len += found.from.ch - found.to.ch; + } + cur = line; + while (merged = collapsedSpanAtEnd(cur)) { + var found$1 = merged.find(0, true); + len -= cur.text.length - found$1.from.ch; + cur = found$1.to.line; + len += cur.text.length - found$1.to.ch; + } + return len + } + + // Find the longest line in the document. + function findMaxLine(cm) { + var d = cm.display, doc = cm.doc; + d.maxLine = getLine(doc, doc.first); + d.maxLineLength = lineLength(d.maxLine); + d.maxLineChanged = true; + doc.iter(function (line) { + var len = lineLength(line); + if (len > d.maxLineLength) { + d.maxLineLength = len; + d.maxLine = line; + } + }); + } + + // LINE DATA STRUCTURE + + // Line objects. These hold state related to a line, including + // highlighting info (the styles array). + var Line = function(text, markedSpans, estimateHeight) { + this.text = text; + attachMarkedSpans(this, markedSpans); + this.height = estimateHeight ? estimateHeight(this) : 1; + }; + + Line.prototype.lineNo = function () { return lineNo(this) }; + eventMixin(Line); + + // Change the content (text, markers) of a line. Automatically + // invalidates cached information and tries to re-estimate the + // line's height. + function updateLine(line, text, markedSpans, estimateHeight) { + line.text = text; + if (line.stateAfter) { line.stateAfter = null; } + if (line.styles) { line.styles = null; } + if (line.order != null) { line.order = null; } + detachMarkedSpans(line); + attachMarkedSpans(line, markedSpans); + var estHeight = estimateHeight ? estimateHeight(line) : 1; + if (estHeight != line.height) { updateLineHeight(line, estHeight); } + } + + // Detach a line from the document tree and its markers. + function cleanUpLine(line) { + line.parent = null; + detachMarkedSpans(line); + } + + // Convert a style as returned by a mode (either null, or a string + // containing one or more styles) to a CSS style. This is cached, + // and also looks for line-wide styles. + var styleToClassCache = {}, styleToClassCacheWithMode = {}; + function interpretTokenStyle(style, options) { + if (!style || /^\s*$/.test(style)) { return null } + var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache; + return cache[style] || + (cache[style] = style.replace(/\S+/g, "cm-$&")) + } + + // Render the DOM representation of the text of a line. Also builds + // up a 'line map', which points at the DOM nodes that represent + // specific stretches of text, and is used by the measuring code. + // The returned object contains the DOM node, this map, and + // information about line-wide styles that were set by the mode. + function buildLineContent(cm, lineView) { + // The padding-right forces the element to have a 'border', which + // is needed on Webkit to be able to get line-level bounding + // rectangles for it (in measureChar). + var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null); + var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content, + col: 0, pos: 0, cm: cm, + trailingSpace: false, + splitSpaces: cm.getOption("lineWrapping")}; + lineView.measure = {}; + + // Iterate over the logical lines that make up this visual line. + for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) { + var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0); + builder.pos = 0; + builder.addToken = buildToken; + // Optionally wire in some hacks into the token-rendering + // algorithm, to deal with browser quirks. + if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction))) + { builder.addToken = buildTokenBadBidi(builder.addToken, order); } + builder.map = []; + var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line); + insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate)); + if (line.styleClasses) { + if (line.styleClasses.bgClass) + { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); } + if (line.styleClasses.textClass) + { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); } + } + + // Ensure at least a single node is present, for measuring. + if (builder.map.length == 0) + { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); } + + // Store the map and a cache object for the current logical line + if (i == 0) { + lineView.measure.map = builder.map; + lineView.measure.cache = {}; + } else { + (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map) + ;(lineView.measure.caches || (lineView.measure.caches = [])).push({}); + } + } + + // See issue #2901 + if (webkit) { + var last = builder.content.lastChild; + if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab"))) + { builder.content.className = "cm-tab-wrap-hack"; } + } + + signal(cm, "renderLine", cm, lineView.line, builder.pre); + if (builder.pre.className) + { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); } + + return builder + } + + function defaultSpecialCharPlaceholder(ch) { + var token = elt("span", "\u2022", "cm-invalidchar"); + token.title = "\\u" + ch.charCodeAt(0).toString(16); + token.setAttribute("aria-label", token.title); + return token + } + + // Build up the DOM representation for a single token, and add it to + // the line map. Takes care to render special characters separately. + function buildToken(builder, text, style, startStyle, endStyle, css, attributes) { + if (!text) { return } + var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text; + var special = builder.cm.state.specialChars, mustWrap = false; + var content; + if (!special.test(text)) { + builder.col += text.length; + content = document.createTextNode(displayText); + builder.map.push(builder.pos, builder.pos + text.length, content); + if (ie && ie_version < 9) { mustWrap = true; } + builder.pos += text.length; + } else { + content = document.createDocumentFragment(); + var pos = 0; + while (true) { + special.lastIndex = pos; + var m = special.exec(text); + var skipped = m ? m.index - pos : text.length - pos; + if (skipped) { + var txt = document.createTextNode(displayText.slice(pos, pos + skipped)); + if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); } + else { content.appendChild(txt); } + builder.map.push(builder.pos, builder.pos + skipped, txt); + builder.col += skipped; + builder.pos += skipped; + } + if (!m) { break } + pos += skipped + 1; + var txt$1 = (void 0); + if (m[0] == "\t") { + var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize; + txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")); + txt$1.setAttribute("role", "presentation"); + txt$1.setAttribute("cm-text", "\t"); + builder.col += tabWidth; + } else if (m[0] == "\r" || m[0] == "\n") { + txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar")); + txt$1.setAttribute("cm-text", m[0]); + builder.col += 1; + } else { + txt$1 = builder.cm.options.specialCharPlaceholder(m[0]); + txt$1.setAttribute("cm-text", m[0]); + if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); } + else { content.appendChild(txt$1); } + builder.col += 1; + } + builder.map.push(builder.pos, builder.pos + 1, txt$1); + builder.pos++; + } + } + builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32; + if (style || startStyle || endStyle || mustWrap || css || attributes) { + var fullStyle = style || ""; + if (startStyle) { fullStyle += startStyle; } + if (endStyle) { fullStyle += endStyle; } + var token = elt("span", [content], fullStyle, css); + if (attributes) { + for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class") + { token.setAttribute(attr, attributes[attr]); } } + } + return builder.content.appendChild(token) + } + builder.content.appendChild(content); + } + + // Change some spaces to NBSP to prevent the browser from collapsing + // trailing spaces at the end of a line when rendering text (issue #1362). + function splitSpaces(text, trailingBefore) { + if (text.length > 1 && !/ /.test(text)) { return text } + var spaceBefore = trailingBefore, result = ""; + for (var i = 0; i < text.length; i++) { + var ch = text.charAt(i); + if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32)) + { ch = "\u00a0"; } + result += ch; + spaceBefore = ch == " "; + } + return result + } + + // Work around nonsense dimensions being reported for stretches of + // right-to-left text. + function buildTokenBadBidi(inner, order) { + return function (builder, text, style, startStyle, endStyle, css, attributes) { + style = style ? style + " cm-force-border" : "cm-force-border"; + var start = builder.pos, end = start + text.length; + for (;;) { + // Find the part that overlaps with the start of this text + var part = (void 0); + for (var i = 0; i < order.length; i++) { + part = order[i]; + if (part.to > start && part.from <= start) { break } + } + if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) } + inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes); + startStyle = null; + text = text.slice(part.to - start); + start = part.to; + } + } + } + + function buildCollapsedSpan(builder, size, marker, ignoreWidget) { + var widget = !ignoreWidget && marker.widgetNode; + if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); } + if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) { + if (!widget) + { widget = builder.content.appendChild(document.createElement("span")); } + widget.setAttribute("cm-marker", marker.id); + } + if (widget) { + builder.cm.display.input.setUneditable(widget); + builder.content.appendChild(widget); + } + builder.pos += size; + builder.trailingSpace = false; + } + + // Outputs a number of spans to make up a line, taking highlighting + // and marked text into account. + function insertLineContent(line, builder, styles) { + var spans = line.markedSpans, allText = line.text, at = 0; + if (!spans) { + for (var i$1 = 1; i$1 < styles.length; i$1+=2) + { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); } + return + } + + var len = allText.length, pos = 0, i = 1, text = "", style, css; + var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes; + for (;;) { + if (nextChange == pos) { // Update current marker set + spanStyle = spanEndStyle = spanStartStyle = css = ""; + attributes = null; + collapsed = null; nextChange = Infinity; + var foundBookmarks = [], endStyles = (void 0); + for (var j = 0; j < spans.length; ++j) { + var sp = spans[j], m = sp.marker; + if (m.type == "bookmark" && sp.from == pos && m.widgetNode) { + foundBookmarks.push(m); + } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) { + if (sp.to != null && sp.to != pos && nextChange > sp.to) { + nextChange = sp.to; + spanEndStyle = ""; + } + if (m.className) { spanStyle += " " + m.className; } + if (m.css) { css = (css ? css + ";" : "") + m.css; } + if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; } + if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); } + // support for the old title property + // https://github.com/codemirror/CodeMirror/pull/5673 + if (m.title) { (attributes || (attributes = {})).title = m.title; } + if (m.attributes) { + for (var attr in m.attributes) + { (attributes || (attributes = {}))[attr] = m.attributes[attr]; } + } + if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)) + { collapsed = sp; } + } else if (sp.from > pos && nextChange > sp.from) { + nextChange = sp.from; + } + } + if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2) + { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } } + + if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2) + { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } } + if (collapsed && (collapsed.from || 0) == pos) { + buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos, + collapsed.marker, collapsed.from == null); + if (collapsed.to == null) { return } + if (collapsed.to == pos) { collapsed = false; } + } + } + if (pos >= len) { break } + + var upto = Math.min(len, nextChange); + while (true) { + if (text) { + var end = pos + text.length; + if (!collapsed) { + var tokenText = end > upto ? text.slice(0, upto - pos) : text; + builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle, + spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes); + } + if (end >= upto) {text = text.slice(upto - pos); pos = upto; break} + pos = end; + spanStartStyle = ""; + } + text = allText.slice(at, at = styles[i++]); + style = interpretTokenStyle(styles[i++], builder.cm.options); + } + } + } + + + // These objects are used to represent the visible (currently drawn) + // part of the document. A LineView may correspond to multiple + // logical lines, if those are connected by collapsed ranges. + function LineView(doc, line, lineN) { + // The starting line + this.line = line; + // Continuing lines, if any + this.rest = visualLineContinued(line); + // Number of logical lines in this visual line + this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1; + this.node = this.text = null; + this.hidden = lineIsHidden(doc, line); + } + + // Create a range of LineView objects for the given lines. + function buildViewArray(cm, from, to) { + var array = [], nextPos; + for (var pos = from; pos < to; pos = nextPos) { + var view = new LineView(cm.doc, getLine(cm.doc, pos), pos); + nextPos = pos + view.size; + array.push(view); + } + return array + } + + var operationGroup = null; + + function pushOperation(op) { + if (operationGroup) { + operationGroup.ops.push(op); + } else { + op.ownsGroup = operationGroup = { + ops: [op], + delayedCallbacks: [] + }; + } + } + + function fireCallbacksForOps(group) { + // Calls delayed callbacks and cursorActivity handlers until no + // new ones appear + var callbacks = group.delayedCallbacks, i = 0; + do { + for (; i < callbacks.length; i++) + { callbacks[i].call(null); } + for (var j = 0; j < group.ops.length; j++) { + var op = group.ops[j]; + if (op.cursorActivityHandlers) + { while (op.cursorActivityCalled < op.cursorActivityHandlers.length) + { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } } + } + } while (i < callbacks.length) + } + + function finishOperation(op, endCb) { + var group = op.ownsGroup; + if (!group) { return } + + try { fireCallbacksForOps(group); } + finally { + operationGroup = null; + endCb(group); + } + } + + var orphanDelayedCallbacks = null; + + // Often, we want to signal events at a point where we are in the + // middle of some work, but don't want the handler to start calling + // other methods on the editor, which might be in an inconsistent + // state or simply not expect any other events to happen. + // signalLater looks whether there are any handlers, and schedules + // them to be executed when the last operation ends, or, if no + // operation is active, when a timeout fires. + function signalLater(emitter, type /*, values...*/) { + var arr = getHandlers(emitter, type); + if (!arr.length) { return } + var args = Array.prototype.slice.call(arguments, 2), list; + if (operationGroup) { + list = operationGroup.delayedCallbacks; + } else if (orphanDelayedCallbacks) { + list = orphanDelayedCallbacks; + } else { + list = orphanDelayedCallbacks = []; + setTimeout(fireOrphanDelayed, 0); + } + var loop = function ( i ) { + list.push(function () { return arr[i].apply(null, args); }); + }; + + for (var i = 0; i < arr.length; ++i) + loop( i ); + } + + function fireOrphanDelayed() { + var delayed = orphanDelayedCallbacks; + orphanDelayedCallbacks = null; + for (var i = 0; i < delayed.length; ++i) { delayed[i](); } + } + + // When an aspect of a line changes, a string is added to + // lineView.changes. This updates the relevant part of the line's + // DOM structure. + function updateLineForChanges(cm, lineView, lineN, dims) { + for (var j = 0; j < lineView.changes.length; j++) { + var type = lineView.changes[j]; + if (type == "text") { updateLineText(cm, lineView); } + else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); } + else if (type == "class") { updateLineClasses(cm, lineView); } + else if (type == "widget") { updateLineWidgets(cm, lineView, dims); } + } + lineView.changes = null; + } + + // Lines with gutter elements, widgets or a background class need to + // be wrapped, and have the extra elements added to the wrapper div + function ensureLineWrapped(lineView) { + if (lineView.node == lineView.text) { + lineView.node = elt("div", null, null, "position: relative"); + if (lineView.text.parentNode) + { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); } + lineView.node.appendChild(lineView.text); + if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; } + } + return lineView.node + } + + function updateLineBackground(cm, lineView) { + var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass; + if (cls) { cls += " CodeMirror-linebackground"; } + if (lineView.background) { + if (cls) { lineView.background.className = cls; } + else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; } + } else if (cls) { + var wrap = ensureLineWrapped(lineView); + lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild); + cm.display.input.setUneditable(lineView.background); + } + } + + // Wrapper around buildLineContent which will reuse the structure + // in display.externalMeasured when possible. + function getLineContent(cm, lineView) { + var ext = cm.display.externalMeasured; + if (ext && ext.line == lineView.line) { + cm.display.externalMeasured = null; + lineView.measure = ext.measure; + return ext.built + } + return buildLineContent(cm, lineView) + } + + // Redraw the line's text. Interacts with the background and text + // classes because the mode may output tokens that influence these + // classes. + function updateLineText(cm, lineView) { + var cls = lineView.text.className; + var built = getLineContent(cm, lineView); + if (lineView.text == lineView.node) { lineView.node = built.pre; } + lineView.text.parentNode.replaceChild(built.pre, lineView.text); + lineView.text = built.pre; + if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) { + lineView.bgClass = built.bgClass; + lineView.textClass = built.textClass; + updateLineClasses(cm, lineView); + } else if (cls) { + lineView.text.className = cls; + } + } + + function updateLineClasses(cm, lineView) { + updateLineBackground(cm, lineView); + if (lineView.line.wrapClass) + { ensureLineWrapped(lineView).className = lineView.line.wrapClass; } + else if (lineView.node != lineView.text) + { lineView.node.className = ""; } + var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass; + lineView.text.className = textClass || ""; + } + + function updateLineGutter(cm, lineView, lineN, dims) { + if (lineView.gutter) { + lineView.node.removeChild(lineView.gutter); + lineView.gutter = null; + } + if (lineView.gutterBackground) { + lineView.node.removeChild(lineView.gutterBackground); + lineView.gutterBackground = null; + } + if (lineView.line.gutterClass) { + var wrap = ensureLineWrapped(lineView); + lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass, + ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px")); + cm.display.input.setUneditable(lineView.gutterBackground); + wrap.insertBefore(lineView.gutterBackground, lineView.text); + } + var markers = lineView.line.gutterMarkers; + if (cm.options.lineNumbers || markers) { + var wrap$1 = ensureLineWrapped(lineView); + var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px")); + gutterWrap.setAttribute("aria-hidden", "true"); + cm.display.input.setUneditable(gutterWrap); + wrap$1.insertBefore(gutterWrap, lineView.text); + if (lineView.line.gutterClass) + { gutterWrap.className += " " + lineView.line.gutterClass; } + if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"])) + { lineView.lineNumber = gutterWrap.appendChild( + elt("div", lineNumberFor(cm.options, lineN), + "CodeMirror-linenumber CodeMirror-gutter-elt", + ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); } + if (markers) { for (var k = 0; k < cm.display.gutterSpecs.length; ++k) { + var id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id]; + if (found) + { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", + ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); } + } } + } + } + + function updateLineWidgets(cm, lineView, dims) { + if (lineView.alignable) { lineView.alignable = null; } + var isWidget = classTest("CodeMirror-linewidget"); + for (var node = lineView.node.firstChild, next = (void 0); node; node = next) { + next = node.nextSibling; + if (isWidget.test(node.className)) { lineView.node.removeChild(node); } + } + insertLineWidgets(cm, lineView, dims); + } + + // Build a line's DOM representation from scratch + function buildLineElement(cm, lineView, lineN, dims) { + var built = getLineContent(cm, lineView); + lineView.text = lineView.node = built.pre; + if (built.bgClass) { lineView.bgClass = built.bgClass; } + if (built.textClass) { lineView.textClass = built.textClass; } + + updateLineClasses(cm, lineView); + updateLineGutter(cm, lineView, lineN, dims); + insertLineWidgets(cm, lineView, dims); + return lineView.node + } + + // A lineView may contain multiple logical lines (when merged by + // collapsed spans). The widgets for all of them need to be drawn. + function insertLineWidgets(cm, lineView, dims) { + insertLineWidgetsFor(cm, lineView.line, lineView, dims, true); + if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) + { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } } + } + + function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) { + if (!line.widgets) { return } + var wrap = ensureLineWrapped(lineView); + for (var i = 0, ws = line.widgets; i < ws.length; ++i) { + var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget" + (widget.className ? " " + widget.className : "")); + if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); } + positionLineWidget(widget, node, lineView, dims); + cm.display.input.setUneditable(node); + if (allowAbove && widget.above) + { wrap.insertBefore(node, lineView.gutter || lineView.text); } + else + { wrap.appendChild(node); } + signalLater(widget, "redraw"); + } + } + + function positionLineWidget(widget, node, lineView, dims) { + if (widget.noHScroll) { + (lineView.alignable || (lineView.alignable = [])).push(node); + var width = dims.wrapperWidth; + node.style.left = dims.fixedPos + "px"; + if (!widget.coverGutter) { + width -= dims.gutterTotalWidth; + node.style.paddingLeft = dims.gutterTotalWidth + "px"; + } + node.style.width = width + "px"; + } + if (widget.coverGutter) { + node.style.zIndex = 5; + node.style.position = "relative"; + if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; } + } + } + + function widgetHeight(widget) { + if (widget.height != null) { return widget.height } + var cm = widget.doc.cm; + if (!cm) { return 0 } + if (!contains(document.body, widget.node)) { + var parentStyle = "position: relative;"; + if (widget.coverGutter) + { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; } + if (widget.noHScroll) + { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; } + removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle)); + } + return widget.height = widget.node.parentNode.offsetHeight + } + + // Return true when the given mouse event happened in a widget + function eventInWidget(display, e) { + for (var n = e_target(e); n != display.wrapper; n = n.parentNode) { + if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") || + (n.parentNode == display.sizer && n != display.mover)) + { return true } + } + } + + // POSITION MEASUREMENT + + function paddingTop(display) {return display.lineSpace.offsetTop} + function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight} + function paddingH(display) { + if (display.cachedPaddingH) { return display.cachedPaddingH } + var e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-line-like")); + var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle; + var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}; + if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; } + return data + } + + function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth } + function displayWidth(cm) { + return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth + } + function displayHeight(cm) { + return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight + } + + // Ensure the lineView.wrapping.heights array is populated. This is + // an array of bottom offsets for the lines that make up a drawn + // line. When lineWrapping is on, there might be more than one + // height. + function ensureLineHeights(cm, lineView, rect) { + var wrapping = cm.options.lineWrapping; + var curWidth = wrapping && displayWidth(cm); + if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) { + var heights = lineView.measure.heights = []; + if (wrapping) { + lineView.measure.width = curWidth; + var rects = lineView.text.firstChild.getClientRects(); + for (var i = 0; i < rects.length - 1; i++) { + var cur = rects[i], next = rects[i + 1]; + if (Math.abs(cur.bottom - next.bottom) > 2) + { heights.push((cur.bottom + next.top) / 2 - rect.top); } + } + } + heights.push(rect.bottom - rect.top); + } + } + + // Find a line map (mapping character offsets to text nodes) and a + // measurement cache for the given line number. (A line view might + // contain multiple lines when collapsed ranges are present.) + function mapFromLineView(lineView, line, lineN) { + if (lineView.line == line) + { return {map: lineView.measure.map, cache: lineView.measure.cache} } + if (lineView.rest) { + for (var i = 0; i < lineView.rest.length; i++) + { if (lineView.rest[i] == line) + { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } } + for (var i$1 = 0; i$1 < lineView.rest.length; i$1++) + { if (lineNo(lineView.rest[i$1]) > lineN) + { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } } + } + } + + // Render a line into the hidden node display.externalMeasured. Used + // when measurement is needed for a line that's not in the viewport. + function updateExternalMeasurement(cm, line) { + line = visualLine(line); + var lineN = lineNo(line); + var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN); + view.lineN = lineN; + var built = view.built = buildLineContent(cm, view); + view.text = built.pre; + removeChildrenAndAdd(cm.display.lineMeasure, built.pre); + return view + } + + // Get a {top, bottom, left, right} box (in line-local coordinates) + // for a given character. + function measureChar(cm, line, ch, bias) { + return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias) + } + + // Find a line view that corresponds to the given line number. + function findViewForLine(cm, lineN) { + if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo) + { return cm.display.view[findViewIndex(cm, lineN)] } + var ext = cm.display.externalMeasured; + if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size) + { return ext } + } + + // Measurement can be split in two steps, the set-up work that + // applies to the whole line, and the measurement of the actual + // character. Functions like coordsChar, that need to do a lot of + // measurements in a row, can thus ensure that the set-up work is + // only done once. + function prepareMeasureForLine(cm, line) { + var lineN = lineNo(line); + var view = findViewForLine(cm, lineN); + if (view && !view.text) { + view = null; + } else if (view && view.changes) { + updateLineForChanges(cm, view, lineN, getDimensions(cm)); + cm.curOp.forceUpdate = true; + } + if (!view) + { view = updateExternalMeasurement(cm, line); } + + var info = mapFromLineView(view, line, lineN); + return { + line: line, view: view, rect: null, + map: info.map, cache: info.cache, before: info.before, + hasHeights: false + } + } + + // Given a prepared measurement object, measures the position of an + // actual character (or fetches it from the cache). + function measureCharPrepared(cm, prepared, ch, bias, varHeight) { + if (prepared.before) { ch = -1; } + var key = ch + (bias || ""), found; + if (prepared.cache.hasOwnProperty(key)) { + found = prepared.cache[key]; + } else { + if (!prepared.rect) + { prepared.rect = prepared.view.text.getBoundingClientRect(); } + if (!prepared.hasHeights) { + ensureLineHeights(cm, prepared.view, prepared.rect); + prepared.hasHeights = true; + } + found = measureCharInner(cm, prepared, ch, bias); + if (!found.bogus) { prepared.cache[key] = found; } + } + return {left: found.left, right: found.right, + top: varHeight ? found.rtop : found.top, + bottom: varHeight ? found.rbottom : found.bottom} + } + + var nullRect = {left: 0, right: 0, top: 0, bottom: 0}; + + function nodeAndOffsetInLineMap(map, ch, bias) { + var node, start, end, collapse, mStart, mEnd; + // First, search the line map for the text node corresponding to, + // or closest to, the target character. + for (var i = 0; i < map.length; i += 3) { + mStart = map[i]; + mEnd = map[i + 1]; + if (ch < mStart) { + start = 0; end = 1; + collapse = "left"; + } else if (ch < mEnd) { + start = ch - mStart; + end = start + 1; + } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) { + end = mEnd - mStart; + start = end - 1; + if (ch >= mEnd) { collapse = "right"; } + } + if (start != null) { + node = map[i + 2]; + if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right")) + { collapse = bias; } + if (bias == "left" && start == 0) + { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) { + node = map[(i -= 3) + 2]; + collapse = "left"; + } } + if (bias == "right" && start == mEnd - mStart) + { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) { + node = map[(i += 3) + 2]; + collapse = "right"; + } } + break + } + } + return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd} + } + + function getUsefulRect(rects, bias) { + var rect = nullRect; + if (bias == "left") { for (var i = 0; i < rects.length; i++) { + if ((rect = rects[i]).left != rect.right) { break } + } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) { + if ((rect = rects[i$1]).left != rect.right) { break } + } } + return rect + } + + function measureCharInner(cm, prepared, ch, bias) { + var place = nodeAndOffsetInLineMap(prepared.map, ch, bias); + var node = place.node, start = place.start, end = place.end, collapse = place.collapse; + + var rect; + if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates. + for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned + while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; } + while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; } + if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) + { rect = node.parentNode.getBoundingClientRect(); } + else + { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); } + if (rect.left || rect.right || start == 0) { break } + end = start; + start = start - 1; + collapse = "right"; + } + if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); } + } else { // If it is a widget, simply get the box for the whole widget. + if (start > 0) { collapse = bias = "right"; } + var rects; + if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1) + { rect = rects[bias == "right" ? rects.length - 1 : 0]; } + else + { rect = node.getBoundingClientRect(); } + } + if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) { + var rSpan = node.parentNode.getClientRects()[0]; + if (rSpan) + { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; } + else + { rect = nullRect; } + } + + var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top; + var mid = (rtop + rbot) / 2; + var heights = prepared.view.measure.heights; + var i = 0; + for (; i < heights.length - 1; i++) + { if (mid < heights[i]) { break } } + var top = i ? heights[i - 1] : 0, bot = heights[i]; + var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left, + right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left, + top: top, bottom: bot}; + if (!rect.left && !rect.right) { result.bogus = true; } + if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; } + + return result + } + + // Work around problem with bounding client rects on ranges being + // returned incorrectly when zoomed on IE10 and below. + function maybeUpdateRectForZooming(measure, rect) { + if (!window.screen || screen.logicalXDPI == null || + screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure)) + { return rect } + var scaleX = screen.logicalXDPI / screen.deviceXDPI; + var scaleY = screen.logicalYDPI / screen.deviceYDPI; + return {left: rect.left * scaleX, right: rect.right * scaleX, + top: rect.top * scaleY, bottom: rect.bottom * scaleY} + } + + function clearLineMeasurementCacheFor(lineView) { + if (lineView.measure) { + lineView.measure.cache = {}; + lineView.measure.heights = null; + if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) + { lineView.measure.caches[i] = {}; } } + } + } + + function clearLineMeasurementCache(cm) { + cm.display.externalMeasure = null; + removeChildren(cm.display.lineMeasure); + for (var i = 0; i < cm.display.view.length; i++) + { clearLineMeasurementCacheFor(cm.display.view[i]); } + } + + function clearCaches(cm) { + clearLineMeasurementCache(cm); + cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null; + if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; } + cm.display.lineNumChars = null; + } + + function pageScrollX(doc) { + // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206 + // which causes page_Offset and bounding client rects to use + // different reference viewports and invalidate our calculations. + if (chrome && android) { return -(doc.body.getBoundingClientRect().left - parseInt(getComputedStyle(doc.body).marginLeft)) } + return doc.defaultView.pageXOffset || (doc.documentElement || doc.body).scrollLeft + } + function pageScrollY(doc) { + if (chrome && android) { return -(doc.body.getBoundingClientRect().top - parseInt(getComputedStyle(doc.body).marginTop)) } + return doc.defaultView.pageYOffset || (doc.documentElement || doc.body).scrollTop + } + + function widgetTopHeight(lineObj) { + var ref = visualLine(lineObj); + var widgets = ref.widgets; + var height = 0; + if (widgets) { for (var i = 0; i < widgets.length; ++i) { if (widgets[i].above) + { height += widgetHeight(widgets[i]); } } } + return height + } + + // Converts a {top, bottom, left, right} box from line-local + // coordinates into another coordinate system. Context may be one of + // "line", "div" (display.lineDiv), "local"./null (editor), "window", + // or "page". + function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) { + if (!includeWidgets) { + var height = widgetTopHeight(lineObj); + rect.top += height; rect.bottom += height; + } + if (context == "line") { return rect } + if (!context) { context = "local"; } + var yOff = heightAtLine(lineObj); + if (context == "local") { yOff += paddingTop(cm.display); } + else { yOff -= cm.display.viewOffset; } + if (context == "page" || context == "window") { + var lOff = cm.display.lineSpace.getBoundingClientRect(); + yOff += lOff.top + (context == "window" ? 0 : pageScrollY(doc(cm))); + var xOff = lOff.left + (context == "window" ? 0 : pageScrollX(doc(cm))); + rect.left += xOff; rect.right += xOff; + } + rect.top += yOff; rect.bottom += yOff; + return rect + } + + // Coverts a box from "div" coords to another coordinate system. + // Context may be "window", "page", "div", or "local"./null. + function fromCoordSystem(cm, coords, context) { + if (context == "div") { return coords } + var left = coords.left, top = coords.top; + // First move into "page" coordinate system + if (context == "page") { + left -= pageScrollX(doc(cm)); + top -= pageScrollY(doc(cm)); + } else if (context == "local" || !context) { + var localBox = cm.display.sizer.getBoundingClientRect(); + left += localBox.left; + top += localBox.top; + } + + var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect(); + return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top} + } + + function charCoords(cm, pos, context, lineObj, bias) { + if (!lineObj) { lineObj = getLine(cm.doc, pos.line); } + return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context) + } + + // Returns a box for a given cursor position, which may have an + // 'other' property containing the position of the secondary cursor + // on a bidi boundary. + // A cursor Pos(line, char, "before") is on the same visual line as `char - 1` + // and after `char - 1` in writing order of `char - 1` + // A cursor Pos(line, char, "after") is on the same visual line as `char` + // and before `char` in writing order of `char` + // Examples (upper-case letters are RTL, lower-case are LTR): + // Pos(0, 1, ...) + // before after + // ab a|b a|b + // aB a|B aB| + // Ab |Ab A|b + // AB B|A B|A + // Every position after the last character on a line is considered to stick + // to the last character on the line. + function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) { + lineObj = lineObj || getLine(cm.doc, pos.line); + if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); } + function get(ch, right) { + var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight); + if (right) { m.left = m.right; } else { m.right = m.left; } + return intoCoordSystem(cm, lineObj, m, context) + } + var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky; + if (ch >= lineObj.text.length) { + ch = lineObj.text.length; + sticky = "before"; + } else if (ch <= 0) { + ch = 0; + sticky = "after"; + } + if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") } + + function getBidi(ch, partPos, invert) { + var part = order[partPos], right = part.level == 1; + return get(invert ? ch - 1 : ch, right != invert) + } + var partPos = getBidiPartAt(order, ch, sticky); + var other = bidiOther; + var val = getBidi(ch, partPos, sticky == "before"); + if (other != null) { val.other = getBidi(ch, other, sticky != "before"); } + return val + } + + // Used to cheaply estimate the coordinates for a position. Used for + // intermediate scroll updates. + function estimateCoords(cm, pos) { + var left = 0; + pos = clipPos(cm.doc, pos); + if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; } + var lineObj = getLine(cm.doc, pos.line); + var top = heightAtLine(lineObj) + paddingTop(cm.display); + return {left: left, right: left, top: top, bottom: top + lineObj.height} + } + + // Positions returned by coordsChar contain some extra information. + // xRel is the relative x position of the input coordinates compared + // to the found position (so xRel > 0 means the coordinates are to + // the right of the character position, for example). When outside + // is true, that means the coordinates lie outside the line's + // vertical range. + function PosWithInfo(line, ch, sticky, outside, xRel) { + var pos = Pos(line, ch, sticky); + pos.xRel = xRel; + if (outside) { pos.outside = outside; } + return pos + } + + // Compute the character position closest to the given coordinates. + // Input must be lineSpace-local ("div" coordinate system). + function coordsChar(cm, x, y) { + var doc = cm.doc; + y += cm.display.viewOffset; + if (y < 0) { return PosWithInfo(doc.first, 0, null, -1, -1) } + var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1; + if (lineN > last) + { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1) } + if (x < 0) { x = 0; } + + var lineObj = getLine(doc, lineN); + for (;;) { + var found = coordsCharInner(cm, lineObj, lineN, x, y); + var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0)); + if (!collapsed) { return found } + var rangeEnd = collapsed.find(1); + if (rangeEnd.line == lineN) { return rangeEnd } + lineObj = getLine(doc, lineN = rangeEnd.line); + } + } + + function wrappedLineExtent(cm, lineObj, preparedMeasure, y) { + y -= widgetTopHeight(lineObj); + var end = lineObj.text.length; + var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0); + end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end); + return {begin: begin, end: end} + } + + function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) { + if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); } + var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top; + return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop) + } + + // Returns true if the given side of a box is after the given + // coordinates, in top-to-bottom, left-to-right order. + function boxIsAfter(box, x, y, left) { + return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x + } + + function coordsCharInner(cm, lineObj, lineNo, x, y) { + // Move y into line-local coordinate space + y -= heightAtLine(lineObj); + var preparedMeasure = prepareMeasureForLine(cm, lineObj); + // When directly calling `measureCharPrepared`, we have to adjust + // for the widgets at this line. + var widgetHeight = widgetTopHeight(lineObj); + var begin = 0, end = lineObj.text.length, ltr = true; + + var order = getOrder(lineObj, cm.doc.direction); + // If the line isn't plain left-to-right text, first figure out + // which bidi section the coordinates fall into. + if (order) { + var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart) + (cm, lineObj, lineNo, preparedMeasure, order, x, y); + ltr = part.level != 1; + // The awkward -1 offsets are needed because findFirst (called + // on these below) will treat its first bound as inclusive, + // second as exclusive, but we want to actually address the + // characters in the part's range + begin = ltr ? part.from : part.to - 1; + end = ltr ? part.to : part.from - 1; + } + + // A binary search to find the first character whose bounding box + // starts after the coordinates. If we run across any whose box wrap + // the coordinates, store that. + var chAround = null, boxAround = null; + var ch = findFirst(function (ch) { + var box = measureCharPrepared(cm, preparedMeasure, ch); + box.top += widgetHeight; box.bottom += widgetHeight; + if (!boxIsAfter(box, x, y, false)) { return false } + if (box.top <= y && box.left <= x) { + chAround = ch; + boxAround = box; + } + return true + }, begin, end); + + var baseX, sticky, outside = false; + // If a box around the coordinates was found, use that + if (boxAround) { + // Distinguish coordinates nearer to the left or right side of the box + var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr; + ch = chAround + (atStart ? 0 : 1); + sticky = atStart ? "after" : "before"; + baseX = atLeft ? boxAround.left : boxAround.right; + } else { + // (Adjust for extended bound, if necessary.) + if (!ltr && (ch == end || ch == begin)) { ch++; } + // To determine which side to associate with, get the box to the + // left of the character and compare it's vertical position to the + // coordinates + sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" : + (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ? + "after" : "before"; + // Now get accurate coordinates for this place, in order to get a + // base X position + var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure); + baseX = coords.left; + outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0; + } + + ch = skipExtendingChars(lineObj.text, ch, 1); + return PosWithInfo(lineNo, ch, sticky, outside, x - baseX) + } + + function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) { + // Bidi parts are sorted left-to-right, and in a non-line-wrapping + // situation, we can take this ordering to correspond to the visual + // ordering. This finds the first part whose end is after the given + // coordinates. + var index = findFirst(function (i) { + var part = order[i], ltr = part.level != 1; + return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"), + "line", lineObj, preparedMeasure), x, y, true) + }, 0, order.length - 1); + var part = order[index]; + // If this isn't the first part, the part's start is also after + // the coordinates, and the coordinates aren't on the same line as + // that start, move one part back. + if (index > 0) { + var ltr = part.level != 1; + var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"), + "line", lineObj, preparedMeasure); + if (boxIsAfter(start, x, y, true) && start.top > y) + { part = order[index - 1]; } + } + return part + } + + function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) { + // In a wrapped line, rtl text on wrapping boundaries can do things + // that don't correspond to the ordering in our `order` array at + // all, so a binary search doesn't work, and we want to return a + // part that only spans one line so that the binary search in + // coordsCharInner is safe. As such, we first find the extent of the + // wrapped line, and then do a flat search in which we discard any + // spans that aren't on the line. + var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y); + var begin = ref.begin; + var end = ref.end; + if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; } + var part = null, closestDist = null; + for (var i = 0; i < order.length; i++) { + var p = order[i]; + if (p.from >= end || p.to <= begin) { continue } + var ltr = p.level != 1; + var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right; + // Weigh against spans ending before this, so that they are only + // picked if nothing ends after + var dist = endX < x ? x - endX + 1e9 : endX - x; + if (!part || closestDist > dist) { + part = p; + closestDist = dist; + } + } + if (!part) { part = order[order.length - 1]; } + // Clip the part to the wrapped line. + if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; } + if (part.to > end) { part = {from: part.from, to: end, level: part.level}; } + return part + } + + var measureText; + // Compute the default text height. + function textHeight(display) { + if (display.cachedTextHeight != null) { return display.cachedTextHeight } + if (measureText == null) { + measureText = elt("pre", null, "CodeMirror-line-like"); + // Measure a bunch of lines, for browsers that compute + // fractional heights. + for (var i = 0; i < 49; ++i) { + measureText.appendChild(document.createTextNode("x")); + measureText.appendChild(elt("br")); + } + measureText.appendChild(document.createTextNode("x")); + } + removeChildrenAndAdd(display.measure, measureText); + var height = measureText.offsetHeight / 50; + if (height > 3) { display.cachedTextHeight = height; } + removeChildren(display.measure); + return height || 1 + } + + // Compute the default character width. + function charWidth(display) { + if (display.cachedCharWidth != null) { return display.cachedCharWidth } + var anchor = elt("span", "xxxxxxxxxx"); + var pre = elt("pre", [anchor], "CodeMirror-line-like"); + removeChildrenAndAdd(display.measure, pre); + var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10; + if (width > 2) { display.cachedCharWidth = width; } + return width || 10 + } + + // Do a bulk-read of the DOM positions and sizes needed to draw the + // view, so that we don't interleave reading and writing to the DOM. + function getDimensions(cm) { + var d = cm.display, left = {}, width = {}; + var gutterLeft = d.gutters.clientLeft; + for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) { + var id = cm.display.gutterSpecs[i].className; + left[id] = n.offsetLeft + n.clientLeft + gutterLeft; + width[id] = n.clientWidth; + } + return {fixedPos: compensateForHScroll(d), + gutterTotalWidth: d.gutters.offsetWidth, + gutterLeft: left, + gutterWidth: width, + wrapperWidth: d.wrapper.clientWidth} + } + + // Computes display.scroller.scrollLeft + display.gutters.offsetWidth, + // but using getBoundingClientRect to get a sub-pixel-accurate + // result. + function compensateForHScroll(display) { + return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left + } + + // Returns a function that estimates the height of a line, to use as + // first approximation until the line becomes visible (and is thus + // properly measurable). + function estimateHeight(cm) { + var th = textHeight(cm.display), wrapping = cm.options.lineWrapping; + var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3); + return function (line) { + if (lineIsHidden(cm.doc, line)) { return 0 } + + var widgetsHeight = 0; + if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) { + if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; } + } } + + if (wrapping) + { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th } + else + { return widgetsHeight + th } + } + } + + function estimateLineHeights(cm) { + var doc = cm.doc, est = estimateHeight(cm); + doc.iter(function (line) { + var estHeight = est(line); + if (estHeight != line.height) { updateLineHeight(line, estHeight); } + }); + } + + // Given a mouse event, find the corresponding position. If liberal + // is false, it checks whether a gutter or scrollbar was clicked, + // and returns null if it was. forRect is used by rectangular + // selections, and tries to estimate a character position even for + // coordinates beyond the right of the text. + function posFromMouse(cm, e, liberal, forRect) { + var display = cm.display; + if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null } + + var x, y, space = display.lineSpace.getBoundingClientRect(); + // Fails unpredictably on IE[67] when mouse is dragged around quickly. + try { x = e.clientX - space.left; y = e.clientY - space.top; } + catch (e$1) { return null } + var coords = coordsChar(cm, x, y), line; + if (forRect && coords.xRel > 0 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) { + var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length; + coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff)); + } + return coords + } + + // Find the view element corresponding to a given line. Return null + // when the line isn't visible. + function findViewIndex(cm, n) { + if (n >= cm.display.viewTo) { return null } + n -= cm.display.viewFrom; + if (n < 0) { return null } + var view = cm.display.view; + for (var i = 0; i < view.length; i++) { + n -= view[i].size; + if (n < 0) { return i } + } + } + + // Updates the display.view data structure for a given change to the + // document. From and to are in pre-change coordinates. Lendiff is + // the amount of lines added or subtracted by the change. This is + // used for changes that span multiple lines, or change the way + // lines are divided into visual lines. regLineChange (below) + // registers single-line changes. + function regChange(cm, from, to, lendiff) { + if (from == null) { from = cm.doc.first; } + if (to == null) { to = cm.doc.first + cm.doc.size; } + if (!lendiff) { lendiff = 0; } + + var display = cm.display; + if (lendiff && to < display.viewTo && + (display.updateLineNumbers == null || display.updateLineNumbers > from)) + { display.updateLineNumbers = from; } + + cm.curOp.viewChanged = true; + + if (from >= display.viewTo) { // Change after + if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo) + { resetView(cm); } + } else if (to <= display.viewFrom) { // Change before + if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) { + resetView(cm); + } else { + display.viewFrom += lendiff; + display.viewTo += lendiff; + } + } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap + resetView(cm); + } else if (from <= display.viewFrom) { // Top overlap + var cut = viewCuttingPoint(cm, to, to + lendiff, 1); + if (cut) { + display.view = display.view.slice(cut.index); + display.viewFrom = cut.lineN; + display.viewTo += lendiff; + } else { + resetView(cm); + } + } else if (to >= display.viewTo) { // Bottom overlap + var cut$1 = viewCuttingPoint(cm, from, from, -1); + if (cut$1) { + display.view = display.view.slice(0, cut$1.index); + display.viewTo = cut$1.lineN; + } else { + resetView(cm); + } + } else { // Gap in the middle + var cutTop = viewCuttingPoint(cm, from, from, -1); + var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1); + if (cutTop && cutBot) { + display.view = display.view.slice(0, cutTop.index) + .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN)) + .concat(display.view.slice(cutBot.index)); + display.viewTo += lendiff; + } else { + resetView(cm); + } + } + + var ext = display.externalMeasured; + if (ext) { + if (to < ext.lineN) + { ext.lineN += lendiff; } + else if (from < ext.lineN + ext.size) + { display.externalMeasured = null; } + } + } + + // Register a change to a single line. Type must be one of "text", + // "gutter", "class", "widget" + function regLineChange(cm, line, type) { + cm.curOp.viewChanged = true; + var display = cm.display, ext = cm.display.externalMeasured; + if (ext && line >= ext.lineN && line < ext.lineN + ext.size) + { display.externalMeasured = null; } + + if (line < display.viewFrom || line >= display.viewTo) { return } + var lineView = display.view[findViewIndex(cm, line)]; + if (lineView.node == null) { return } + var arr = lineView.changes || (lineView.changes = []); + if (indexOf(arr, type) == -1) { arr.push(type); } + } + + // Clear the view. + function resetView(cm) { + cm.display.viewFrom = cm.display.viewTo = cm.doc.first; + cm.display.view = []; + cm.display.viewOffset = 0; + } + + function viewCuttingPoint(cm, oldN, newN, dir) { + var index = findViewIndex(cm, oldN), diff, view = cm.display.view; + if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size) + { return {index: index, lineN: newN} } + var n = cm.display.viewFrom; + for (var i = 0; i < index; i++) + { n += view[i].size; } + if (n != oldN) { + if (dir > 0) { + if (index == view.length - 1) { return null } + diff = (n + view[index].size) - oldN; + index++; + } else { + diff = n - oldN; + } + oldN += diff; newN += diff; + } + while (visualLineNo(cm.doc, newN) != newN) { + if (index == (dir < 0 ? 0 : view.length - 1)) { return null } + newN += dir * view[index - (dir < 0 ? 1 : 0)].size; + index += dir; + } + return {index: index, lineN: newN} + } + + // Force the view to cover a given range, adding empty view element + // or clipping off existing ones as needed. + function adjustView(cm, from, to) { + var display = cm.display, view = display.view; + if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) { + display.view = buildViewArray(cm, from, to); + display.viewFrom = from; + } else { + if (display.viewFrom > from) + { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); } + else if (display.viewFrom < from) + { display.view = display.view.slice(findViewIndex(cm, from)); } + display.viewFrom = from; + if (display.viewTo < to) + { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); } + else if (display.viewTo > to) + { display.view = display.view.slice(0, findViewIndex(cm, to)); } + } + display.viewTo = to; + } + + // Count the number of lines in the view whose DOM representation is + // out of date (or nonexistent). + function countDirtyView(cm) { + var view = cm.display.view, dirty = 0; + for (var i = 0; i < view.length; i++) { + var lineView = view[i]; + if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; } + } + return dirty + } + + function updateSelection(cm) { + cm.display.input.showSelection(cm.display.input.prepareSelection()); + } + + function prepareSelection(cm, primary) { + if ( primary === void 0 ) primary = true; + + var doc = cm.doc, result = {}; + var curFragment = result.cursors = document.createDocumentFragment(); + var selFragment = result.selection = document.createDocumentFragment(); + + var customCursor = cm.options.$customCursor; + if (customCursor) { primary = true; } + for (var i = 0; i < doc.sel.ranges.length; i++) { + if (!primary && i == doc.sel.primIndex) { continue } + var range = doc.sel.ranges[i]; + if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue } + var collapsed = range.empty(); + if (customCursor) { + var head = customCursor(cm, range); + if (head) { drawSelectionCursor(cm, head, curFragment); } + } else if (collapsed || cm.options.showCursorWhenSelecting) { + drawSelectionCursor(cm, range.head, curFragment); + } + if (!collapsed) + { drawSelectionRange(cm, range, selFragment); } + } + return result + } + + // Draws a cursor for the given range + function drawSelectionCursor(cm, head, output) { + var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine); + + var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")); + cursor.style.left = pos.left + "px"; + cursor.style.top = pos.top + "px"; + cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"; + + if (/\bcm-fat-cursor\b/.test(cm.getWrapperElement().className)) { + var charPos = charCoords(cm, head, "div", null, null); + var width = charPos.right - charPos.left; + cursor.style.width = (width > 0 ? width : cm.defaultCharWidth()) + "px"; + } + + if (pos.other) { + // Secondary cursor, shown when on a 'jump' in bi-directional text + var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor")); + otherCursor.style.display = ""; + otherCursor.style.left = pos.other.left + "px"; + otherCursor.style.top = pos.other.top + "px"; + otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"; + } + } + + function cmpCoords(a, b) { return a.top - b.top || a.left - b.left } + + // Draws the given range as a highlighted selection + function drawSelectionRange(cm, range, output) { + var display = cm.display, doc = cm.doc; + var fragment = document.createDocumentFragment(); + var padding = paddingH(cm.display), leftSide = padding.left; + var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right; + var docLTR = doc.direction == "ltr"; + + function add(left, top, width, bottom) { + if (top < 0) { top = 0; } + top = Math.round(top); + bottom = Math.round(bottom); + fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: absolute; left: " + left + "px;\n top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px;\n height: " + (bottom - top) + "px"))); + } + + function drawForLine(line, fromArg, toArg) { + var lineObj = getLine(doc, line); + var lineLen = lineObj.text.length; + var start, end; + function coords(ch, bias) { + return charCoords(cm, Pos(line, ch), "div", lineObj, bias) + } + + function wrapX(pos, dir, side) { + var extent = wrappedLineExtentChar(cm, lineObj, null, pos); + var prop = (dir == "ltr") == (side == "after") ? "left" : "right"; + var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1); + return coords(ch, prop)[prop] + } + + var order = getOrder(lineObj, doc.direction); + iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) { + var ltr = dir == "ltr"; + var fromPos = coords(from, ltr ? "left" : "right"); + var toPos = coords(to - 1, ltr ? "right" : "left"); + + var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen; + var first = i == 0, last = !order || i == order.length - 1; + if (toPos.top - fromPos.top <= 3) { // Single line + var openLeft = (docLTR ? openStart : openEnd) && first; + var openRight = (docLTR ? openEnd : openStart) && last; + var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left; + var right = openRight ? rightSide : (ltr ? toPos : fromPos).right; + add(left, fromPos.top, right - left, fromPos.bottom); + } else { // Multiple lines + var topLeft, topRight, botLeft, botRight; + if (ltr) { + topLeft = docLTR && openStart && first ? leftSide : fromPos.left; + topRight = docLTR ? rightSide : wrapX(from, dir, "before"); + botLeft = docLTR ? leftSide : wrapX(to, dir, "after"); + botRight = docLTR && openEnd && last ? rightSide : toPos.right; + } else { + topLeft = !docLTR ? leftSide : wrapX(from, dir, "before"); + topRight = !docLTR && openStart && first ? rightSide : fromPos.right; + botLeft = !docLTR && openEnd && last ? leftSide : toPos.left; + botRight = !docLTR ? rightSide : wrapX(to, dir, "after"); + } + add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom); + if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); } + add(botLeft, toPos.top, botRight - botLeft, toPos.bottom); + } + + if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; } + if (cmpCoords(toPos, start) < 0) { start = toPos; } + if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; } + if (cmpCoords(toPos, end) < 0) { end = toPos; } + }); + return {start: start, end: end} + } + + var sFrom = range.from(), sTo = range.to(); + if (sFrom.line == sTo.line) { + drawForLine(sFrom.line, sFrom.ch, sTo.ch); + } else { + var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line); + var singleVLine = visualLine(fromLine) == visualLine(toLine); + var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end; + var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start; + if (singleVLine) { + if (leftEnd.top < rightStart.top - 2) { + add(leftEnd.right, leftEnd.top, null, leftEnd.bottom); + add(leftSide, rightStart.top, rightStart.left, rightStart.bottom); + } else { + add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom); + } + } + if (leftEnd.bottom < rightStart.top) + { add(leftSide, leftEnd.bottom, null, rightStart.top); } + } + + output.appendChild(fragment); + } + + // Cursor-blinking + function restartBlink(cm) { + if (!cm.state.focused) { return } + var display = cm.display; + clearInterval(display.blinker); + var on = true; + display.cursorDiv.style.visibility = ""; + if (cm.options.cursorBlinkRate > 0) + { display.blinker = setInterval(function () { + if (!cm.hasFocus()) { onBlur(cm); } + display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; + }, cm.options.cursorBlinkRate); } + else if (cm.options.cursorBlinkRate < 0) + { display.cursorDiv.style.visibility = "hidden"; } + } + + function ensureFocus(cm) { + if (!cm.hasFocus()) { + cm.display.input.focus(); + if (!cm.state.focused) { onFocus(cm); } + } + } + + function delayBlurEvent(cm) { + cm.state.delayingBlurEvent = true; + setTimeout(function () { if (cm.state.delayingBlurEvent) { + cm.state.delayingBlurEvent = false; + if (cm.state.focused) { onBlur(cm); } + } }, 100); + } + + function onFocus(cm, e) { + if (cm.state.delayingBlurEvent && !cm.state.draggingText) { cm.state.delayingBlurEvent = false; } + + if (cm.options.readOnly == "nocursor") { return } + if (!cm.state.focused) { + signal(cm, "focus", cm, e); + cm.state.focused = true; + addClass(cm.display.wrapper, "CodeMirror-focused"); + // This test prevents this from firing when a context + // menu is closed (since the input reset would kill the + // select-all detection hack) + if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) { + cm.display.input.reset(); + if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730 + } + cm.display.input.receivedFocus(); + } + restartBlink(cm); + } + function onBlur(cm, e) { + if (cm.state.delayingBlurEvent) { return } + + if (cm.state.focused) { + signal(cm, "blur", cm, e); + cm.state.focused = false; + rmClass(cm.display.wrapper, "CodeMirror-focused"); + } + clearInterval(cm.display.blinker); + setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150); + } + + // Read the actual heights of the rendered lines, and update their + // stored heights to match. + function updateHeightsInViewport(cm) { + var display = cm.display; + var prevBottom = display.lineDiv.offsetTop; + var viewTop = Math.max(0, display.scroller.getBoundingClientRect().top); + var oldHeight = display.lineDiv.getBoundingClientRect().top; + var mustScroll = 0; + for (var i = 0; i < display.view.length; i++) { + var cur = display.view[i], wrapping = cm.options.lineWrapping; + var height = (void 0), width = 0; + if (cur.hidden) { continue } + oldHeight += cur.line.height; + if (ie && ie_version < 8) { + var bot = cur.node.offsetTop + cur.node.offsetHeight; + height = bot - prevBottom; + prevBottom = bot; + } else { + var box = cur.node.getBoundingClientRect(); + height = box.bottom - box.top; + // Check that lines don't extend past the right of the current + // editor width + if (!wrapping && cur.text.firstChild) + { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; } + } + var diff = cur.line.height - height; + if (diff > .005 || diff < -.005) { + if (oldHeight < viewTop) { mustScroll -= diff; } + updateLineHeight(cur.line, height); + updateWidgetHeight(cur.line); + if (cur.rest) { for (var j = 0; j < cur.rest.length; j++) + { updateWidgetHeight(cur.rest[j]); } } + } + if (width > cm.display.sizerWidth) { + var chWidth = Math.ceil(width / charWidth(cm.display)); + if (chWidth > cm.display.maxLineLength) { + cm.display.maxLineLength = chWidth; + cm.display.maxLine = cur.line; + cm.display.maxLineChanged = true; + } + } + } + if (Math.abs(mustScroll) > 2) { display.scroller.scrollTop += mustScroll; } + } + + // Read and store the height of line widgets associated with the + // given line. + function updateWidgetHeight(line) { + if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) { + var w = line.widgets[i], parent = w.node.parentNode; + if (parent) { w.height = parent.offsetHeight; } + } } + } + + // Compute the lines that are visible in a given viewport (defaults + // the the current scroll position). viewport may contain top, + // height, and ensure (see op.scrollToPos) properties. + function visibleLines(display, doc, viewport) { + var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop; + top = Math.floor(top - paddingTop(display)); + var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight; + + var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom); + // Ensure is a {from: {line, ch}, to: {line, ch}} object, and + // forces those lines into the viewport (if possible). + if (viewport && viewport.ensure) { + var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line; + if (ensureFrom < from) { + from = ensureFrom; + to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight); + } else if (Math.min(ensureTo, doc.lastLine()) >= to) { + from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight); + to = ensureTo; + } + } + return {from: from, to: Math.max(to, from + 1)} + } + + // SCROLLING THINGS INTO VIEW + + // If an editor sits on the top or bottom of the window, partially + // scrolled out of view, this ensures that the cursor is visible. + function maybeScrollWindow(cm, rect) { + if (signalDOMEvent(cm, "scrollCursorIntoView")) { return } + + var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null; + var doc = display.wrapper.ownerDocument; + if (rect.top + box.top < 0) { doScroll = true; } + else if (rect.bottom + box.top > (doc.defaultView.innerHeight || doc.documentElement.clientHeight)) { doScroll = false; } + if (doScroll != null && !phantom) { + var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n top: " + (rect.top - display.viewOffset - paddingTop(cm.display)) + "px;\n height: " + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + "px;\n left: " + (rect.left) + "px; width: " + (Math.max(2, rect.right - rect.left)) + "px;")); + cm.display.lineSpace.appendChild(scrollNode); + scrollNode.scrollIntoView(doScroll); + cm.display.lineSpace.removeChild(scrollNode); + } + } + + // Scroll a given position into view (immediately), verifying that + // it actually became visible (as line heights are accurately + // measured, the position of something may 'drift' during drawing). + function scrollPosIntoView(cm, pos, end, margin) { + if (margin == null) { margin = 0; } + var rect; + if (!cm.options.lineWrapping && pos == end) { + // Set pos and end to the cursor positions around the character pos sticks to + // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch + // If pos == Pos(_, 0, "before"), pos and end are unchanged + end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos; + pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos; + } + for (var limit = 0; limit < 5; limit++) { + var changed = false; + var coords = cursorCoords(cm, pos); + var endCoords = !end || end == pos ? coords : cursorCoords(cm, end); + rect = {left: Math.min(coords.left, endCoords.left), + top: Math.min(coords.top, endCoords.top) - margin, + right: Math.max(coords.left, endCoords.left), + bottom: Math.max(coords.bottom, endCoords.bottom) + margin}; + var scrollPos = calculateScrollPos(cm, rect); + var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft; + if (scrollPos.scrollTop != null) { + updateScrollTop(cm, scrollPos.scrollTop); + if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; } + } + if (scrollPos.scrollLeft != null) { + setScrollLeft(cm, scrollPos.scrollLeft); + if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; } + } + if (!changed) { break } + } + return rect + } + + // Scroll a given set of coordinates into view (immediately). + function scrollIntoView(cm, rect) { + var scrollPos = calculateScrollPos(cm, rect); + if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); } + if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); } + } + + // Calculate a new scroll position needed to scroll the given + // rectangle into view. Returns an object with scrollTop and + // scrollLeft properties. When these are undefined, the + // vertical/horizontal position does not need to be adjusted. + function calculateScrollPos(cm, rect) { + var display = cm.display, snapMargin = textHeight(cm.display); + if (rect.top < 0) { rect.top = 0; } + var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop; + var screen = displayHeight(cm), result = {}; + if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; } + var docBottom = cm.doc.height + paddingVert(display); + var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin; + if (rect.top < screentop) { + result.scrollTop = atTop ? 0 : rect.top; + } else if (rect.bottom > screentop + screen) { + var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen); + if (newTop != screentop) { result.scrollTop = newTop; } + } + + var gutterSpace = cm.options.fixedGutter ? 0 : display.gutters.offsetWidth; + var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft - gutterSpace; + var screenw = displayWidth(cm) - display.gutters.offsetWidth; + var tooWide = rect.right - rect.left > screenw; + if (tooWide) { rect.right = rect.left + screenw; } + if (rect.left < 10) + { result.scrollLeft = 0; } + else if (rect.left < screenleft) + { result.scrollLeft = Math.max(0, rect.left + gutterSpace - (tooWide ? 0 : 10)); } + else if (rect.right > screenw + screenleft - 3) + { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; } + return result + } + + // Store a relative adjustment to the scroll position in the current + // operation (to be applied when the operation finishes). + function addToScrollTop(cm, top) { + if (top == null) { return } + resolveScrollToPos(cm); + cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top; + } + + // Make sure that at the end of the operation the current cursor is + // shown. + function ensureCursorVisible(cm) { + resolveScrollToPos(cm); + var cur = cm.getCursor(); + cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin}; + } + + function scrollToCoords(cm, x, y) { + if (x != null || y != null) { resolveScrollToPos(cm); } + if (x != null) { cm.curOp.scrollLeft = x; } + if (y != null) { cm.curOp.scrollTop = y; } + } + + function scrollToRange(cm, range) { + resolveScrollToPos(cm); + cm.curOp.scrollToPos = range; + } + + // When an operation has its scrollToPos property set, and another + // scroll action is applied before the end of the operation, this + // 'simulates' scrolling that position into view in a cheap way, so + // that the effect of intermediate scroll commands is not ignored. + function resolveScrollToPos(cm) { + var range = cm.curOp.scrollToPos; + if (range) { + cm.curOp.scrollToPos = null; + var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to); + scrollToCoordsRange(cm, from, to, range.margin); + } + } + + function scrollToCoordsRange(cm, from, to, margin) { + var sPos = calculateScrollPos(cm, { + left: Math.min(from.left, to.left), + top: Math.min(from.top, to.top) - margin, + right: Math.max(from.right, to.right), + bottom: Math.max(from.bottom, to.bottom) + margin + }); + scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop); + } + + // Sync the scrollable area and scrollbars, ensure the viewport + // covers the visible area. + function updateScrollTop(cm, val) { + if (Math.abs(cm.doc.scrollTop - val) < 2) { return } + if (!gecko) { updateDisplaySimple(cm, {top: val}); } + setScrollTop(cm, val, true); + if (gecko) { updateDisplaySimple(cm); } + startWorker(cm, 100); + } + + function setScrollTop(cm, val, forceScroll) { + val = Math.max(0, Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val)); + if (cm.display.scroller.scrollTop == val && !forceScroll) { return } + cm.doc.scrollTop = val; + cm.display.scrollbars.setScrollTop(val); + if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; } + } + + // Sync scroller and scrollbar, ensure the gutter elements are + // aligned. + function setScrollLeft(cm, val, isScroller, forceScroll) { + val = Math.max(0, Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth)); + if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return } + cm.doc.scrollLeft = val; + alignHorizontally(cm); + if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; } + cm.display.scrollbars.setScrollLeft(val); + } + + // SCROLLBARS + + // Prepare DOM reads needed to update the scrollbars. Done in one + // shot to minimize update/measure roundtrips. + function measureForScrollbars(cm) { + var d = cm.display, gutterW = d.gutters.offsetWidth; + var docH = Math.round(cm.doc.height + paddingVert(cm.display)); + return { + clientHeight: d.scroller.clientHeight, + viewHeight: d.wrapper.clientHeight, + scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth, + viewWidth: d.wrapper.clientWidth, + barLeft: cm.options.fixedGutter ? gutterW : 0, + docHeight: docH, + scrollHeight: docH + scrollGap(cm) + d.barHeight, + nativeBarWidth: d.nativeBarWidth, + gutterWidth: gutterW + } + } + + var NativeScrollbars = function(place, scroll, cm) { + this.cm = cm; + var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar"); + var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar"); + vert.tabIndex = horiz.tabIndex = -1; + place(vert); place(horiz); + + on(vert, "scroll", function () { + if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); } + }); + on(horiz, "scroll", function () { + if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); } + }); + + this.checkedZeroWidth = false; + // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). + if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; } + }; + + NativeScrollbars.prototype.update = function (measure) { + var needsH = measure.scrollWidth > measure.clientWidth + 1; + var needsV = measure.scrollHeight > measure.clientHeight + 1; + var sWidth = measure.nativeBarWidth; + + if (needsV) { + this.vert.style.display = "block"; + this.vert.style.bottom = needsH ? sWidth + "px" : "0"; + var totalHeight = measure.viewHeight - (needsH ? sWidth : 0); + // A bug in IE8 can cause this value to be negative, so guard it. + this.vert.firstChild.style.height = + Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"; + } else { + this.vert.scrollTop = 0; + this.vert.style.display = ""; + this.vert.firstChild.style.height = "0"; + } + + if (needsH) { + this.horiz.style.display = "block"; + this.horiz.style.right = needsV ? sWidth + "px" : "0"; + this.horiz.style.left = measure.barLeft + "px"; + var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0); + this.horiz.firstChild.style.width = + Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px"; + } else { + this.horiz.style.display = ""; + this.horiz.firstChild.style.width = "0"; + } + + if (!this.checkedZeroWidth && measure.clientHeight > 0) { + if (sWidth == 0) { this.zeroWidthHack(); } + this.checkedZeroWidth = true; + } + + return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0} + }; + + NativeScrollbars.prototype.setScrollLeft = function (pos) { + if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; } + if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); } + }; + + NativeScrollbars.prototype.setScrollTop = function (pos) { + if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; } + if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); } + }; + + NativeScrollbars.prototype.zeroWidthHack = function () { + var w = mac && !mac_geMountainLion ? "12px" : "18px"; + this.horiz.style.height = this.vert.style.width = w; + this.horiz.style.visibility = this.vert.style.visibility = "hidden"; + this.disableHoriz = new Delayed; + this.disableVert = new Delayed; + }; + + NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) { + bar.style.visibility = ""; + function maybeDisable() { + // To find out whether the scrollbar is still visible, we + // check whether the element under the pixel in the bottom + // right corner of the scrollbar box is the scrollbar box + // itself (when the bar is still visible) or its filler child + // (when the bar is hidden). If it is still visible, we keep + // it enabled, if it's hidden, we disable pointer events. + var box = bar.getBoundingClientRect(); + var elt = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2) + : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1); + if (elt != bar) { bar.style.visibility = "hidden"; } + else { delay.set(1000, maybeDisable); } + } + delay.set(1000, maybeDisable); + }; + + NativeScrollbars.prototype.clear = function () { + var parent = this.horiz.parentNode; + parent.removeChild(this.horiz); + parent.removeChild(this.vert); + }; + + var NullScrollbars = function () {}; + + NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} }; + NullScrollbars.prototype.setScrollLeft = function () {}; + NullScrollbars.prototype.setScrollTop = function () {}; + NullScrollbars.prototype.clear = function () {}; + + function updateScrollbars(cm, measure) { + if (!measure) { measure = measureForScrollbars(cm); } + var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight; + updateScrollbarsInner(cm, measure); + for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) { + if (startWidth != cm.display.barWidth && cm.options.lineWrapping) + { updateHeightsInViewport(cm); } + updateScrollbarsInner(cm, measureForScrollbars(cm)); + startWidth = cm.display.barWidth; startHeight = cm.display.barHeight; + } + } + + // Re-synchronize the fake scrollbars with the actual size of the + // content. + function updateScrollbarsInner(cm, measure) { + var d = cm.display; + var sizes = d.scrollbars.update(measure); + + d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"; + d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"; + d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"; + + if (sizes.right && sizes.bottom) { + d.scrollbarFiller.style.display = "block"; + d.scrollbarFiller.style.height = sizes.bottom + "px"; + d.scrollbarFiller.style.width = sizes.right + "px"; + } else { d.scrollbarFiller.style.display = ""; } + if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) { + d.gutterFiller.style.display = "block"; + d.gutterFiller.style.height = sizes.bottom + "px"; + d.gutterFiller.style.width = measure.gutterWidth + "px"; + } else { d.gutterFiller.style.display = ""; } + } + + var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}; + + function initScrollbars(cm) { + if (cm.display.scrollbars) { + cm.display.scrollbars.clear(); + if (cm.display.scrollbars.addClass) + { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); } + } + + cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) { + cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller); + // Prevent clicks in the scrollbars from killing focus + on(node, "mousedown", function () { + if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); } + }); + node.setAttribute("cm-not-content", "true"); + }, function (pos, axis) { + if (axis == "horizontal") { setScrollLeft(cm, pos); } + else { updateScrollTop(cm, pos); } + }, cm); + if (cm.display.scrollbars.addClass) + { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); } + } + + // Operations are used to wrap a series of changes to the editor + // state in such a way that each change won't have to update the + // cursor and display (which would be awkward, slow, and + // error-prone). Instead, display updates are batched and then all + // combined and executed at once. + + var nextOpId = 0; + // Start a new operation. + function startOperation(cm) { + cm.curOp = { + cm: cm, + viewChanged: false, // Flag that indicates that lines might need to be redrawn + startHeight: cm.doc.height, // Used to detect need to update scrollbar + forceUpdate: false, // Used to force a redraw + updateInput: 0, // Whether to reset the input textarea + typing: false, // Whether this reset should be careful to leave existing text (for compositing) + changeObjs: null, // Accumulated changes, for firing change events + cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on + cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already + selectionChanged: false, // Whether the selection needs to be redrawn + updateMaxLine: false, // Set when the widest line needs to be determined anew + scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet + scrollToPos: null, // Used to scroll to a specific position + focus: false, + id: ++nextOpId, // Unique ID + markArrays: null // Used by addMarkedSpan + }; + pushOperation(cm.curOp); + } + + // Finish an operation, updating the display and signalling delayed events + function endOperation(cm) { + var op = cm.curOp; + if (op) { finishOperation(op, function (group) { + for (var i = 0; i < group.ops.length; i++) + { group.ops[i].cm.curOp = null; } + endOperations(group); + }); } + } + + // The DOM updates done when an operation finishes are batched so + // that the minimum number of relayouts are required. + function endOperations(group) { + var ops = group.ops; + for (var i = 0; i < ops.length; i++) // Read DOM + { endOperation_R1(ops[i]); } + for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe) + { endOperation_W1(ops[i$1]); } + for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM + { endOperation_R2(ops[i$2]); } + for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe) + { endOperation_W2(ops[i$3]); } + for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM + { endOperation_finish(ops[i$4]); } + } + + function endOperation_R1(op) { + var cm = op.cm, display = cm.display; + maybeClipScrollbars(cm); + if (op.updateMaxLine) { findMaxLine(cm); } + + op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null || + op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom || + op.scrollToPos.to.line >= display.viewTo) || + display.maxLineChanged && cm.options.lineWrapping; + op.update = op.mustUpdate && + new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate); + } + + function endOperation_W1(op) { + op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update); + } + + function endOperation_R2(op) { + var cm = op.cm, display = cm.display; + if (op.updatedDisplay) { updateHeightsInViewport(cm); } + + op.barMeasure = measureForScrollbars(cm); + + // If the max line changed since it was last measured, measure it, + // and ensure the document's width matches it. + // updateDisplay_W2 will use these properties to do the actual resizing + if (display.maxLineChanged && !cm.options.lineWrapping) { + op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3; + cm.display.sizerWidth = op.adjustWidthTo; + op.barMeasure.scrollWidth = + Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth); + op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm)); + } + + if (op.updatedDisplay || op.selectionChanged) + { op.preparedSelection = display.input.prepareSelection(); } + } + + function endOperation_W2(op) { + var cm = op.cm; + + if (op.adjustWidthTo != null) { + cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"; + if (op.maxScrollLeft < cm.doc.scrollLeft) + { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); } + cm.display.maxLineChanged = false; + } + + var takeFocus = op.focus && op.focus == activeElt(doc(cm)); + if (op.preparedSelection) + { cm.display.input.showSelection(op.preparedSelection, takeFocus); } + if (op.updatedDisplay || op.startHeight != cm.doc.height) + { updateScrollbars(cm, op.barMeasure); } + if (op.updatedDisplay) + { setDocumentHeight(cm, op.barMeasure); } + + if (op.selectionChanged) { restartBlink(cm); } + + if (cm.state.focused && op.updateInput) + { cm.display.input.reset(op.typing); } + if (takeFocus) { ensureFocus(op.cm); } + } + + function endOperation_finish(op) { + var cm = op.cm, display = cm.display, doc = cm.doc; + + if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); } + + // Abort mouse wheel delta measurement, when scrolling explicitly + if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos)) + { display.wheelStartX = display.wheelStartY = null; } + + // Propagate the scroll position to the actual DOM scroller + if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); } + + if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); } + // If we need to scroll a specific position into view, do so. + if (op.scrollToPos) { + var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from), + clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin); + maybeScrollWindow(cm, rect); + } + + // Fire events for markers that are hidden/unidden by editing or + // undoing + var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers; + if (hidden) { for (var i = 0; i < hidden.length; ++i) + { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } } + if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1) + { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } } + + if (display.wrapper.offsetHeight) + { doc.scrollTop = cm.display.scroller.scrollTop; } + + // Fire change events, and delayed event handlers + if (op.changeObjs) + { signal(cm, "changes", cm, op.changeObjs); } + if (op.update) + { op.update.finish(); } + } + + // Run the given function in an operation + function runInOp(cm, f) { + if (cm.curOp) { return f() } + startOperation(cm); + try { return f() } + finally { endOperation(cm); } + } + // Wraps a function in an operation. Returns the wrapped function. + function operation(cm, f) { + return function() { + if (cm.curOp) { return f.apply(cm, arguments) } + startOperation(cm); + try { return f.apply(cm, arguments) } + finally { endOperation(cm); } + } + } + // Used to add methods to editor and doc instances, wrapping them in + // operations. + function methodOp(f) { + return function() { + if (this.curOp) { return f.apply(this, arguments) } + startOperation(this); + try { return f.apply(this, arguments) } + finally { endOperation(this); } + } + } + function docMethodOp(f) { + return function() { + var cm = this.cm; + if (!cm || cm.curOp) { return f.apply(this, arguments) } + startOperation(cm); + try { return f.apply(this, arguments) } + finally { endOperation(cm); } + } + } + + // HIGHLIGHT WORKER + + function startWorker(cm, time) { + if (cm.doc.highlightFrontier < cm.display.viewTo) + { cm.state.highlight.set(time, bind(highlightWorker, cm)); } + } + + function highlightWorker(cm) { + var doc = cm.doc; + if (doc.highlightFrontier >= cm.display.viewTo) { return } + var end = +new Date + cm.options.workTime; + var context = getContextBefore(cm, doc.highlightFrontier); + var changedLines = []; + + doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) { + if (context.line >= cm.display.viewFrom) { // Visible + var oldStyles = line.styles; + var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null; + var highlighted = highlightLine(cm, line, context, true); + if (resetState) { context.state = resetState; } + line.styles = highlighted.styles; + var oldCls = line.styleClasses, newCls = highlighted.classes; + if (newCls) { line.styleClasses = newCls; } + else if (oldCls) { line.styleClasses = null; } + var ischange = !oldStyles || oldStyles.length != line.styles.length || + oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass); + for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; } + if (ischange) { changedLines.push(context.line); } + line.stateAfter = context.save(); + context.nextLine(); + } else { + if (line.text.length <= cm.options.maxHighlightLength) + { processLine(cm, line.text, context); } + line.stateAfter = context.line % 5 == 0 ? context.save() : null; + context.nextLine(); + } + if (+new Date > end) { + startWorker(cm, cm.options.workDelay); + return true + } + }); + doc.highlightFrontier = context.line; + doc.modeFrontier = Math.max(doc.modeFrontier, context.line); + if (changedLines.length) { runInOp(cm, function () { + for (var i = 0; i < changedLines.length; i++) + { regLineChange(cm, changedLines[i], "text"); } + }); } + } + + // DISPLAY DRAWING + + var DisplayUpdate = function(cm, viewport, force) { + var display = cm.display; + + this.viewport = viewport; + // Store some values that we'll need later (but don't want to force a relayout for) + this.visible = visibleLines(display, cm.doc, viewport); + this.editorIsHidden = !display.wrapper.offsetWidth; + this.wrapperHeight = display.wrapper.clientHeight; + this.wrapperWidth = display.wrapper.clientWidth; + this.oldDisplayWidth = displayWidth(cm); + this.force = force; + this.dims = getDimensions(cm); + this.events = []; + }; + + DisplayUpdate.prototype.signal = function (emitter, type) { + if (hasHandler(emitter, type)) + { this.events.push(arguments); } + }; + DisplayUpdate.prototype.finish = function () { + for (var i = 0; i < this.events.length; i++) + { signal.apply(null, this.events[i]); } + }; + + function maybeClipScrollbars(cm) { + var display = cm.display; + if (!display.scrollbarsClipped && display.scroller.offsetWidth) { + display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth; + display.heightForcer.style.height = scrollGap(cm) + "px"; + display.sizer.style.marginBottom = -display.nativeBarWidth + "px"; + display.sizer.style.borderRightWidth = scrollGap(cm) + "px"; + display.scrollbarsClipped = true; + } + } + + function selectionSnapshot(cm) { + if (cm.hasFocus()) { return null } + var active = activeElt(doc(cm)); + if (!active || !contains(cm.display.lineDiv, active)) { return null } + var result = {activeElt: active}; + if (window.getSelection) { + var sel = win(cm).getSelection(); + if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) { + result.anchorNode = sel.anchorNode; + result.anchorOffset = sel.anchorOffset; + result.focusNode = sel.focusNode; + result.focusOffset = sel.focusOffset; + } + } + return result + } + + function restoreSelection(snapshot) { + if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt(snapshot.activeElt.ownerDocument)) { return } + snapshot.activeElt.focus(); + if (!/^(INPUT|TEXTAREA)$/.test(snapshot.activeElt.nodeName) && + snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) { + var doc = snapshot.activeElt.ownerDocument; + var sel = doc.defaultView.getSelection(), range = doc.createRange(); + range.setEnd(snapshot.anchorNode, snapshot.anchorOffset); + range.collapse(false); + sel.removeAllRanges(); + sel.addRange(range); + sel.extend(snapshot.focusNode, snapshot.focusOffset); + } + } + + // Does the actual updating of the line display. Bails out + // (returning false) when there is nothing to be done and forced is + // false. + function updateDisplayIfNeeded(cm, update) { + var display = cm.display, doc = cm.doc; + + if (update.editorIsHidden) { + resetView(cm); + return false + } + + // Bail out if the visible area is already rendered and nothing changed. + if (!update.force && + update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo && + (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) && + display.renderedView == display.view && countDirtyView(cm) == 0) + { return false } + + if (maybeUpdateLineNumberWidth(cm)) { + resetView(cm); + update.dims = getDimensions(cm); + } + + // Compute a suitable new viewport (from & to) + var end = doc.first + doc.size; + var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first); + var to = Math.min(end, update.visible.to + cm.options.viewportMargin); + if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); } + if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); } + if (sawCollapsedSpans) { + from = visualLineNo(cm.doc, from); + to = visualLineEndNo(cm.doc, to); + } + + var different = from != display.viewFrom || to != display.viewTo || + display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth; + adjustView(cm, from, to); + + display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom)); + // Position the mover div to align with the current scroll position + cm.display.mover.style.top = display.viewOffset + "px"; + + var toUpdate = countDirtyView(cm); + if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view && + (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo)) + { return false } + + // For big changes, we hide the enclosing element during the + // update, since that speeds up the operations on most browsers. + var selSnapshot = selectionSnapshot(cm); + if (toUpdate > 4) { display.lineDiv.style.display = "none"; } + patchDisplay(cm, display.updateLineNumbers, update.dims); + if (toUpdate > 4) { display.lineDiv.style.display = ""; } + display.renderedView = display.view; + // There might have been a widget with a focused element that got + // hidden or updated, if so re-focus it. + restoreSelection(selSnapshot); + + // Prevent selection and cursors from interfering with the scroll + // width and height. + removeChildren(display.cursorDiv); + removeChildren(display.selectionDiv); + display.gutters.style.height = display.sizer.style.minHeight = 0; + + if (different) { + display.lastWrapHeight = update.wrapperHeight; + display.lastWrapWidth = update.wrapperWidth; + startWorker(cm, 400); + } + + display.updateLineNumbers = null; + + return true + } + + function postUpdateDisplay(cm, update) { + var viewport = update.viewport; + + for (var first = true;; first = false) { + if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) { + // Clip forced viewport to actual scrollable area. + if (viewport && viewport.top != null) + { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; } + // Updated line heights might result in the drawn area not + // actually covering the viewport. Keep looping until it does. + update.visible = visibleLines(cm.display, cm.doc, viewport); + if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo) + { break } + } else if (first) { + update.visible = visibleLines(cm.display, cm.doc, viewport); + } + if (!updateDisplayIfNeeded(cm, update)) { break } + updateHeightsInViewport(cm); + var barMeasure = measureForScrollbars(cm); + updateSelection(cm); + updateScrollbars(cm, barMeasure); + setDocumentHeight(cm, barMeasure); + update.force = false; + } + + update.signal(cm, "update", cm); + if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) { + update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo); + cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo; + } + } + + function updateDisplaySimple(cm, viewport) { + var update = new DisplayUpdate(cm, viewport); + if (updateDisplayIfNeeded(cm, update)) { + updateHeightsInViewport(cm); + postUpdateDisplay(cm, update); + var barMeasure = measureForScrollbars(cm); + updateSelection(cm); + updateScrollbars(cm, barMeasure); + setDocumentHeight(cm, barMeasure); + update.finish(); + } + } + + // Sync the actual display DOM structure with display.view, removing + // nodes for lines that are no longer in view, and creating the ones + // that are not there yet, and updating the ones that are out of + // date. + function patchDisplay(cm, updateNumbersFrom, dims) { + var display = cm.display, lineNumbers = cm.options.lineNumbers; + var container = display.lineDiv, cur = container.firstChild; + + function rm(node) { + var next = node.nextSibling; + // Works around a throw-scroll bug in OS X Webkit + if (webkit && mac && cm.display.currentWheelTarget == node) + { node.style.display = "none"; } + else + { node.parentNode.removeChild(node); } + return next + } + + var view = display.view, lineN = display.viewFrom; + // Loop over the elements in the view, syncing cur (the DOM nodes + // in display.lineDiv) with the view as we go. + for (var i = 0; i < view.length; i++) { + var lineView = view[i]; + if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet + var node = buildLineElement(cm, lineView, lineN, dims); + container.insertBefore(node, cur); + } else { // Already drawn + while (cur != lineView.node) { cur = rm(cur); } + var updateNumber = lineNumbers && updateNumbersFrom != null && + updateNumbersFrom <= lineN && lineView.lineNumber; + if (lineView.changes) { + if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; } + updateLineForChanges(cm, lineView, lineN, dims); + } + if (updateNumber) { + removeChildren(lineView.lineNumber); + lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN))); + } + cur = lineView.node.nextSibling; + } + lineN += lineView.size; + } + while (cur) { cur = rm(cur); } + } + + function updateGutterSpace(display) { + var width = display.gutters.offsetWidth; + display.sizer.style.marginLeft = width + "px"; + // Send an event to consumers responding to changes in gutter width. + signalLater(display, "gutterChanged", display); + } + + function setDocumentHeight(cm, measure) { + cm.display.sizer.style.minHeight = measure.docHeight + "px"; + cm.display.heightForcer.style.top = measure.docHeight + "px"; + cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px"; + } + + // Re-align line numbers and gutter marks to compensate for + // horizontal scrolling. + function alignHorizontally(cm) { + var display = cm.display, view = display.view; + if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return } + var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft; + var gutterW = display.gutters.offsetWidth, left = comp + "px"; + for (var i = 0; i < view.length; i++) { if (!view[i].hidden) { + if (cm.options.fixedGutter) { + if (view[i].gutter) + { view[i].gutter.style.left = left; } + if (view[i].gutterBackground) + { view[i].gutterBackground.style.left = left; } + } + var align = view[i].alignable; + if (align) { for (var j = 0; j < align.length; j++) + { align[j].style.left = left; } } + } } + if (cm.options.fixedGutter) + { display.gutters.style.left = (comp + gutterW) + "px"; } + } + + // Used to ensure that the line number gutter is still the right + // size for the current document size. Returns true when an update + // is needed. + function maybeUpdateLineNumberWidth(cm) { + if (!cm.options.lineNumbers) { return false } + var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display; + if (last.length != display.lineNumChars) { + var test = display.measure.appendChild(elt("div", [elt("div", last)], + "CodeMirror-linenumber CodeMirror-gutter-elt")); + var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW; + display.lineGutter.style.width = ""; + display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1; + display.lineNumWidth = display.lineNumInnerWidth + padding; + display.lineNumChars = display.lineNumInnerWidth ? last.length : -1; + display.lineGutter.style.width = display.lineNumWidth + "px"; + updateGutterSpace(cm.display); + return true + } + return false + } + + function getGutters(gutters, lineNumbers) { + var result = [], sawLineNumbers = false; + for (var i = 0; i < gutters.length; i++) { + var name = gutters[i], style = null; + if (typeof name != "string") { style = name.style; name = name.className; } + if (name == "CodeMirror-linenumbers") { + if (!lineNumbers) { continue } + else { sawLineNumbers = true; } + } + result.push({className: name, style: style}); + } + if (lineNumbers && !sawLineNumbers) { result.push({className: "CodeMirror-linenumbers", style: null}); } + return result + } + + // Rebuild the gutter elements, ensure the margin to the left of the + // code matches their width. + function renderGutters(display) { + var gutters = display.gutters, specs = display.gutterSpecs; + removeChildren(gutters); + display.lineGutter = null; + for (var i = 0; i < specs.length; ++i) { + var ref = specs[i]; + var className = ref.className; + var style = ref.style; + var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + className)); + if (style) { gElt.style.cssText = style; } + if (className == "CodeMirror-linenumbers") { + display.lineGutter = gElt; + gElt.style.width = (display.lineNumWidth || 1) + "px"; + } + } + gutters.style.display = specs.length ? "" : "none"; + updateGutterSpace(display); + } + + function updateGutters(cm) { + renderGutters(cm.display); + regChange(cm); + alignHorizontally(cm); + } + + // The display handles the DOM integration, both for input reading + // and content drawing. It holds references to DOM nodes and + // display-related state. + + function Display(place, doc, input, options) { + var d = this; + this.input = input; + + // Covers bottom-right square when both scrollbars are present. + d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); + d.scrollbarFiller.setAttribute("cm-not-content", "true"); + // Covers bottom of gutter when coverGutterNextToScrollbar is on + // and h scrollbar is present. + d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler"); + d.gutterFiller.setAttribute("cm-not-content", "true"); + // Will contain the actual code, positioned to cover the viewport. + d.lineDiv = eltP("div", null, "CodeMirror-code"); + // Elements are added to these to represent selection and cursors. + d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); + d.cursorDiv = elt("div", null, "CodeMirror-cursors"); + // A visibility: hidden element used to find the size of things. + d.measure = elt("div", null, "CodeMirror-measure"); + // When lines outside of the viewport are measured, they are drawn in this. + d.lineMeasure = elt("div", null, "CodeMirror-measure"); + // Wraps everything that needs to exist inside the vertically-padded coordinate system + d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv], + null, "position: relative; outline: none"); + var lines = eltP("div", [d.lineSpace], "CodeMirror-lines"); + // Moved around its parent to cover visible view. + d.mover = elt("div", [lines], null, "position: relative"); + // Set to the height of the document, allowing scrolling. + d.sizer = elt("div", [d.mover], "CodeMirror-sizer"); + d.sizerWidth = null; + // Behavior of elts with overflow: auto and padding is + // inconsistent across browsers. This is used to ensure the + // scrollable area is big enough. + d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;"); + // Will contain the gutters, if any. + d.gutters = elt("div", null, "CodeMirror-gutters"); + d.lineGutter = null; + // Actual scrollable element. + d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll"); + d.scroller.setAttribute("tabIndex", "-1"); + // The element in which the editor lives. + d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror"); + // See #6982. FIXME remove when this has been fixed for a while in Chrome + if (chrome && chrome_version >= 105) { d.wrapper.style.clipPath = "inset(0px)"; } + + // This attribute is respected by automatic translation systems such as Google Translate, + // and may also be respected by tools used by human translators. + d.wrapper.setAttribute('translate', 'no'); + + // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported) + if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; } + if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; } + + if (place) { + if (place.appendChild) { place.appendChild(d.wrapper); } + else { place(d.wrapper); } + } + + // Current rendered range (may be bigger than the view window). + d.viewFrom = d.viewTo = doc.first; + d.reportedViewFrom = d.reportedViewTo = doc.first; + // Information about the rendered lines. + d.view = []; + d.renderedView = null; + // Holds info about a single rendered line when it was rendered + // for measurement, while not in view. + d.externalMeasured = null; + // Empty space (in pixels) above the view + d.viewOffset = 0; + d.lastWrapHeight = d.lastWrapWidth = 0; + d.updateLineNumbers = null; + + d.nativeBarWidth = d.barHeight = d.barWidth = 0; + d.scrollbarsClipped = false; + + // Used to only resize the line number gutter when necessary (when + // the amount of lines crosses a boundary that makes its width change) + d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null; + // Set to true when a non-horizontal-scrolling line widget is + // added. As an optimization, line widget aligning is skipped when + // this is false. + d.alignWidgets = false; + + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; + + // Tracks the maximum line length so that the horizontal scrollbar + // can be kept static when scrolling. + d.maxLine = null; + d.maxLineLength = 0; + d.maxLineChanged = false; + + // Used for measuring wheel scrolling granularity + d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null; + + // True when shift is held down. + d.shift = false; + + // Used to track whether anything happened since the context menu + // was opened. + d.selForContextMenu = null; + + d.activeTouch = null; + + d.gutterSpecs = getGutters(options.gutters, options.lineNumbers); + renderGutters(d); + + input.init(d); + } + + // Since the delta values reported on mouse wheel events are + // unstandardized between browsers and even browser versions, and + // generally horribly unpredictable, this code starts by measuring + // the scroll effect that the first few mouse wheel events have, + // and, from that, detects the way it can convert deltas to pixel + // offsets afterwards. + // + // The reason we want to know the amount a wheel event will scroll + // is that it gives us a chance to update the display before the + // actual scrolling happens, reducing flickering. + + var wheelSamples = 0, wheelPixelsPerUnit = null; + // Fill in a browser-detected starting value on browsers where we + // know one. These don't have to be accurate -- the result of them + // being wrong would just be a slight flicker on the first wheel + // scroll (if it is large enough). + if (ie) { wheelPixelsPerUnit = -.53; } + else if (gecko) { wheelPixelsPerUnit = 15; } + else if (chrome) { wheelPixelsPerUnit = -.7; } + else if (safari) { wheelPixelsPerUnit = -1/3; } + + function wheelEventDelta(e) { + var dx = e.wheelDeltaX, dy = e.wheelDeltaY; + if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; } + if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; } + else if (dy == null) { dy = e.wheelDelta; } + return {x: dx, y: dy} + } + function wheelEventPixels(e) { + var delta = wheelEventDelta(e); + delta.x *= wheelPixelsPerUnit; + delta.y *= wheelPixelsPerUnit; + return delta + } + + function onScrollWheel(cm, e) { + // On Chrome 102, viewport updates somehow stop wheel-based + // scrolling. Turning off pointer events during the scroll seems + // to avoid the issue. + if (chrome && chrome_version == 102) { + if (cm.display.chromeScrollHack == null) { cm.display.sizer.style.pointerEvents = "none"; } + else { clearTimeout(cm.display.chromeScrollHack); } + cm.display.chromeScrollHack = setTimeout(function () { + cm.display.chromeScrollHack = null; + cm.display.sizer.style.pointerEvents = ""; + }, 100); + } + var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y; + var pixelsPerUnit = wheelPixelsPerUnit; + if (e.deltaMode === 0) { + dx = e.deltaX; + dy = e.deltaY; + pixelsPerUnit = 1; + } + + var display = cm.display, scroll = display.scroller; + // Quit if there's nothing to scroll here + var canScrollX = scroll.scrollWidth > scroll.clientWidth; + var canScrollY = scroll.scrollHeight > scroll.clientHeight; + if (!(dx && canScrollX || dy && canScrollY)) { return } + + // Webkit browsers on OS X abort momentum scrolls when the target + // of the scroll event is removed from the scrollable element. + // This hack (see related code in patchDisplay) makes sure the + // element is kept around. + if (dy && mac && webkit) { + outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) { + for (var i = 0; i < view.length; i++) { + if (view[i].node == cur) { + cm.display.currentWheelTarget = cur; + break outer + } + } + } + } + + // On some browsers, horizontal scrolling will cause redraws to + // happen before the gutter has been realigned, causing it to + // wriggle around in a most unseemly way. When we have an + // estimated pixels/delta value, we just handle horizontal + // scrolling entirely here. It'll be slightly off from native, but + // better than glitching out. + if (dx && !gecko && !presto && pixelsPerUnit != null) { + if (dy && canScrollY) + { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * pixelsPerUnit)); } + setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * pixelsPerUnit)); + // Only prevent default scrolling if vertical scrolling is + // actually possible. Otherwise, it causes vertical scroll + // jitter on OSX trackpads when deltaX is small and deltaY + // is large (issue #3579) + if (!dy || (dy && canScrollY)) + { e_preventDefault(e); } + display.wheelStartX = null; // Abort measurement, if in progress + return + } + + // 'Project' the visible viewport to cover the area that is being + // scrolled into view (if we know enough to estimate it). + if (dy && pixelsPerUnit != null) { + var pixels = dy * pixelsPerUnit; + var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight; + if (pixels < 0) { top = Math.max(0, top + pixels - 50); } + else { bot = Math.min(cm.doc.height, bot + pixels + 50); } + updateDisplaySimple(cm, {top: top, bottom: bot}); + } + + if (wheelSamples < 20 && e.deltaMode !== 0) { + if (display.wheelStartX == null) { + display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop; + display.wheelDX = dx; display.wheelDY = dy; + setTimeout(function () { + if (display.wheelStartX == null) { return } + var movedX = scroll.scrollLeft - display.wheelStartX; + var movedY = scroll.scrollTop - display.wheelStartY; + var sample = (movedY && display.wheelDY && movedY / display.wheelDY) || + (movedX && display.wheelDX && movedX / display.wheelDX); + display.wheelStartX = display.wheelStartY = null; + if (!sample) { return } + wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1); + ++wheelSamples; + }, 200); + } else { + display.wheelDX += dx; display.wheelDY += dy; + } + } + } + + // Selection objects are immutable. A new one is created every time + // the selection changes. A selection is one or more non-overlapping + // (and non-touching) ranges, sorted, and an integer that indicates + // which one is the primary selection (the one that's scrolled into + // view, that getCursor returns, etc). + var Selection = function(ranges, primIndex) { + this.ranges = ranges; + this.primIndex = primIndex; + }; + + Selection.prototype.primary = function () { return this.ranges[this.primIndex] }; + + Selection.prototype.equals = function (other) { + if (other == this) { return true } + if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false } + for (var i = 0; i < this.ranges.length; i++) { + var here = this.ranges[i], there = other.ranges[i]; + if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false } + } + return true + }; + + Selection.prototype.deepCopy = function () { + var out = []; + for (var i = 0; i < this.ranges.length; i++) + { out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)); } + return new Selection(out, this.primIndex) + }; + + Selection.prototype.somethingSelected = function () { + for (var i = 0; i < this.ranges.length; i++) + { if (!this.ranges[i].empty()) { return true } } + return false + }; + + Selection.prototype.contains = function (pos, end) { + if (!end) { end = pos; } + for (var i = 0; i < this.ranges.length; i++) { + var range = this.ranges[i]; + if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) + { return i } + } + return -1 + }; + + var Range = function(anchor, head) { + this.anchor = anchor; this.head = head; + }; + + Range.prototype.from = function () { return minPos(this.anchor, this.head) }; + Range.prototype.to = function () { return maxPos(this.anchor, this.head) }; + Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch }; + + // Take an unsorted, potentially overlapping set of ranges, and + // build a selection out of it. 'Consumes' ranges array (modifying + // it). + function normalizeSelection(cm, ranges, primIndex) { + var mayTouch = cm && cm.options.selectionsMayTouch; + var prim = ranges[primIndex]; + ranges.sort(function (a, b) { return cmp(a.from(), b.from()); }); + primIndex = indexOf(ranges, prim); + for (var i = 1; i < ranges.length; i++) { + var cur = ranges[i], prev = ranges[i - 1]; + var diff = cmp(prev.to(), cur.from()); + if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) { + var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to()); + var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head; + if (i <= primIndex) { --primIndex; } + ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to)); + } + } + return new Selection(ranges, primIndex) + } + + function simpleSelection(anchor, head) { + return new Selection([new Range(anchor, head || anchor)], 0) + } + + // Compute the position of the end of a change (its 'to' property + // refers to the pre-change end). + function changeEnd(change) { + if (!change.text) { return change.to } + return Pos(change.from.line + change.text.length - 1, + lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)) + } + + // Adjust a position to refer to the post-change position of the + // same text, or the end of the change if the change covers it. + function adjustForChange(pos, change) { + if (cmp(pos, change.from) < 0) { return pos } + if (cmp(pos, change.to) <= 0) { return changeEnd(change) } + + var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch; + if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; } + return Pos(line, ch) + } + + function computeSelAfterChange(doc, change) { + var out = []; + for (var i = 0; i < doc.sel.ranges.length; i++) { + var range = doc.sel.ranges[i]; + out.push(new Range(adjustForChange(range.anchor, change), + adjustForChange(range.head, change))); + } + return normalizeSelection(doc.cm, out, doc.sel.primIndex) + } + + function offsetPos(pos, old, nw) { + if (pos.line == old.line) + { return Pos(nw.line, pos.ch - old.ch + nw.ch) } + else + { return Pos(nw.line + (pos.line - old.line), pos.ch) } + } + + // Used by replaceSelections to allow moving the selection to the + // start or around the replaced test. Hint may be "start" or "around". + function computeReplacedSel(doc, changes, hint) { + var out = []; + var oldPrev = Pos(doc.first, 0), newPrev = oldPrev; + for (var i = 0; i < changes.length; i++) { + var change = changes[i]; + var from = offsetPos(change.from, oldPrev, newPrev); + var to = offsetPos(changeEnd(change), oldPrev, newPrev); + oldPrev = change.to; + newPrev = to; + if (hint == "around") { + var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0; + out[i] = new Range(inv ? to : from, inv ? from : to); + } else { + out[i] = new Range(from, from); + } + } + return new Selection(out, doc.sel.primIndex) + } + + // Used to get the editor into a consistent state again when options change. + + function loadMode(cm) { + cm.doc.mode = getMode(cm.options, cm.doc.modeOption); + resetModeState(cm); + } + + function resetModeState(cm) { + cm.doc.iter(function (line) { + if (line.stateAfter) { line.stateAfter = null; } + if (line.styles) { line.styles = null; } + }); + cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first; + startWorker(cm, 100); + cm.state.modeGen++; + if (cm.curOp) { regChange(cm); } + } + + // DOCUMENT DATA STRUCTURE + + // By default, updates that start and end at the beginning of a line + // are treated specially, in order to make the association of line + // widgets and marker elements with the text behave more intuitive. + function isWholeLineUpdate(doc, change) { + return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" && + (!doc.cm || doc.cm.options.wholeLineUpdateBefore) + } + + // Perform a change on the document data structure. + function updateDoc(doc, change, markedSpans, estimateHeight) { + function spansFor(n) {return markedSpans ? markedSpans[n] : null} + function update(line, text, spans) { + updateLine(line, text, spans, estimateHeight); + signalLater(line, "change", line, change); + } + function linesFor(start, end) { + var result = []; + for (var i = start; i < end; ++i) + { result.push(new Line(text[i], spansFor(i), estimateHeight)); } + return result + } + + var from = change.from, to = change.to, text = change.text; + var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line); + var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line; + + // Adjust the line structure + if (change.full) { + doc.insert(0, linesFor(0, text.length)); + doc.remove(text.length, doc.size - text.length); + } else if (isWholeLineUpdate(doc, change)) { + // This is a whole-line replace. Treated specially to make + // sure line objects move the way they are supposed to. + var added = linesFor(0, text.length - 1); + update(lastLine, lastLine.text, lastSpans); + if (nlines) { doc.remove(from.line, nlines); } + if (added.length) { doc.insert(from.line, added); } + } else if (firstLine == lastLine) { + if (text.length == 1) { + update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans); + } else { + var added$1 = linesFor(1, text.length - 1); + added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight)); + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); + doc.insert(from.line + 1, added$1); + } + } else if (text.length == 1) { + update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0)); + doc.remove(from.line + 1, nlines); + } else { + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); + update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans); + var added$2 = linesFor(1, text.length - 1); + if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); } + doc.insert(from.line + 1, added$2); + } + + signalLater(doc, "change", doc, change); + } + + // Call f for all linked documents. + function linkedDocs(doc, f, sharedHistOnly) { + function propagate(doc, skip, sharedHist) { + if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) { + var rel = doc.linked[i]; + if (rel.doc == skip) { continue } + var shared = sharedHist && rel.sharedHist; + if (sharedHistOnly && !shared) { continue } + f(rel.doc, shared); + propagate(rel.doc, doc, shared); + } } + } + propagate(doc, null, true); + } + + // Attach a document to an editor. + function attachDoc(cm, doc) { + if (doc.cm) { throw new Error("This document is already in use.") } + cm.doc = doc; + doc.cm = cm; + estimateLineHeights(cm); + loadMode(cm); + setDirectionClass(cm); + cm.options.direction = doc.direction; + if (!cm.options.lineWrapping) { findMaxLine(cm); } + cm.options.mode = doc.modeOption; + regChange(cm); + } + + function setDirectionClass(cm) { + (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl"); + } + + function directionChanged(cm) { + runInOp(cm, function () { + setDirectionClass(cm); + regChange(cm); + }); + } + + function History(prev) { + // Arrays of change events and selections. Doing something adds an + // event to done and clears undo. Undoing moves events from done + // to undone, redoing moves them in the other direction. + this.done = []; this.undone = []; + this.undoDepth = prev ? prev.undoDepth : Infinity; + // Used to track when changes can be merged into a single undo + // event + this.lastModTime = this.lastSelTime = 0; + this.lastOp = this.lastSelOp = null; + this.lastOrigin = this.lastSelOrigin = null; + // Used by the isClean() method + this.generation = this.maxGeneration = prev ? prev.maxGeneration : 1; + } + + // Create a history change event from an updateDoc-style change + // object. + function historyChangeFromChange(doc, change) { + var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}; + attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); + linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true); + return histChange + } + + // Pop all selection events off the end of a history array. Stop at + // a change event. + function clearSelectionEvents(array) { + while (array.length) { + var last = lst(array); + if (last.ranges) { array.pop(); } + else { break } + } + } + + // Find the top change event in the history. Pop off selection + // events that are in the way. + function lastChangeEvent(hist, force) { + if (force) { + clearSelectionEvents(hist.done); + return lst(hist.done) + } else if (hist.done.length && !lst(hist.done).ranges) { + return lst(hist.done) + } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) { + hist.done.pop(); + return lst(hist.done) + } + } + + // Register a change in the history. Merges changes that are within + // a single operation, or are close together with an origin that + // allows merging (starting with "+") into a single event. + function addChangeToHistory(doc, change, selAfter, opId) { + var hist = doc.history; + hist.undone.length = 0; + var time = +new Date, cur; + var last; + + if ((hist.lastOp == opId || + hist.lastOrigin == change.origin && change.origin && + ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) || + change.origin.charAt(0) == "*")) && + (cur = lastChangeEvent(hist, hist.lastOp == opId))) { + // Merge this change into the last event + last = lst(cur.changes); + if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) { + // Optimized case for simple insertion -- don't want to add + // new changesets for every character typed + last.to = changeEnd(change); + } else { + // Add new sub-event + cur.changes.push(historyChangeFromChange(doc, change)); + } + } else { + // Can not be merged, start a new event. + var before = lst(hist.done); + if (!before || !before.ranges) + { pushSelectionToHistory(doc.sel, hist.done); } + cur = {changes: [historyChangeFromChange(doc, change)], + generation: hist.generation}; + hist.done.push(cur); + while (hist.done.length > hist.undoDepth) { + hist.done.shift(); + if (!hist.done[0].ranges) { hist.done.shift(); } + } + } + hist.done.push(selAfter); + hist.generation = ++hist.maxGeneration; + hist.lastModTime = hist.lastSelTime = time; + hist.lastOp = hist.lastSelOp = opId; + hist.lastOrigin = hist.lastSelOrigin = change.origin; + + if (!last) { signal(doc, "historyAdded"); } + } + + function selectionEventCanBeMerged(doc, origin, prev, sel) { + var ch = origin.charAt(0); + return ch == "*" || + ch == "+" && + prev.ranges.length == sel.ranges.length && + prev.somethingSelected() == sel.somethingSelected() && + new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500) + } + + // Called whenever the selection changes, sets the new selection as + // the pending selection in the history, and pushes the old pending + // selection into the 'done' array when it was significantly + // different (in number of selected ranges, emptiness, or time). + function addSelectionToHistory(doc, sel, opId, options) { + var hist = doc.history, origin = options && options.origin; + + // A new event is started when the previous origin does not match + // the current, or the origins don't allow matching. Origins + // starting with * are always merged, those starting with + are + // merged when similar and close together in time. + if (opId == hist.lastSelOp || + (origin && hist.lastSelOrigin == origin && + (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin || + selectionEventCanBeMerged(doc, origin, lst(hist.done), sel)))) + { hist.done[hist.done.length - 1] = sel; } + else + { pushSelectionToHistory(sel, hist.done); } + + hist.lastSelTime = +new Date; + hist.lastSelOrigin = origin; + hist.lastSelOp = opId; + if (options && options.clearRedo !== false) + { clearSelectionEvents(hist.undone); } + } + + function pushSelectionToHistory(sel, dest) { + var top = lst(dest); + if (!(top && top.ranges && top.equals(sel))) + { dest.push(sel); } + } + + // Used to store marked span information in the history. + function attachLocalSpans(doc, change, from, to) { + var existing = change["spans_" + doc.id], n = 0; + doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) { + if (line.markedSpans) + { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; } + ++n; + }); + } + + // When un/re-doing restores text containing marked spans, those + // that have been explicitly cleared should not be restored. + function removeClearedSpans(spans) { + if (!spans) { return null } + var out; + for (var i = 0; i < spans.length; ++i) { + if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } } + else if (out) { out.push(spans[i]); } + } + return !out ? spans : out.length ? out : null + } + + // Retrieve and filter the old marked spans stored in a change event. + function getOldSpans(doc, change) { + var found = change["spans_" + doc.id]; + if (!found) { return null } + var nw = []; + for (var i = 0; i < change.text.length; ++i) + { nw.push(removeClearedSpans(found[i])); } + return nw + } + + // Used for un/re-doing changes from the history. Combines the + // result of computing the existing spans with the set of spans that + // existed in the history (so that deleting around a span and then + // undoing brings back the span). + function mergeOldSpans(doc, change) { + var old = getOldSpans(doc, change); + var stretched = stretchSpansOverChange(doc, change); + if (!old) { return stretched } + if (!stretched) { return old } + + for (var i = 0; i < old.length; ++i) { + var oldCur = old[i], stretchCur = stretched[i]; + if (oldCur && stretchCur) { + spans: for (var j = 0; j < stretchCur.length; ++j) { + var span = stretchCur[j]; + for (var k = 0; k < oldCur.length; ++k) + { if (oldCur[k].marker == span.marker) { continue spans } } + oldCur.push(span); + } + } else if (stretchCur) { + old[i] = stretchCur; + } + } + return old + } + + // Used both to provide a JSON-safe object in .getHistory, and, when + // detaching a document, to split the history in two + function copyHistoryArray(events, newGroup, instantiateSel) { + var copy = []; + for (var i = 0; i < events.length; ++i) { + var event = events[i]; + if (event.ranges) { + copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event); + continue + } + var changes = event.changes, newChanges = []; + copy.push({changes: newChanges}); + for (var j = 0; j < changes.length; ++j) { + var change = changes[j], m = (void 0); + newChanges.push({from: change.from, to: change.to, text: change.text}); + if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) { + if (indexOf(newGroup, Number(m[1])) > -1) { + lst(newChanges)[prop] = change[prop]; + delete change[prop]; + } + } } } + } + } + return copy + } + + // The 'scroll' parameter given to many of these indicated whether + // the new cursor position should be scrolled into view after + // modifying the selection. + + // If shift is held or the extend flag is set, extends a range to + // include a given position (and optionally a second position). + // Otherwise, simply returns the range between the given positions. + // Used for cursor motion and such. + function extendRange(range, head, other, extend) { + if (extend) { + var anchor = range.anchor; + if (other) { + var posBefore = cmp(head, anchor) < 0; + if (posBefore != (cmp(other, anchor) < 0)) { + anchor = head; + head = other; + } else if (posBefore != (cmp(head, other) < 0)) { + head = other; + } + } + return new Range(anchor, head) + } else { + return new Range(other || head, head) + } + } + + // Extend the primary selection range, discard the rest. + function extendSelection(doc, head, other, options, extend) { + if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); } + setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options); + } + + // Extend all selections (pos is an array of selections with length + // equal the number of selections) + function extendSelections(doc, heads, options) { + var out = []; + var extend = doc.cm && (doc.cm.display.shift || doc.extend); + for (var i = 0; i < doc.sel.ranges.length; i++) + { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); } + var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex); + setSelection(doc, newSel, options); + } + + // Updates a single range in the selection. + function replaceOneSelection(doc, i, range, options) { + var ranges = doc.sel.ranges.slice(0); + ranges[i] = range; + setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options); + } + + // Reset the selection to a single range. + function setSimpleSelection(doc, anchor, head, options) { + setSelection(doc, simpleSelection(anchor, head), options); + } + + // Give beforeSelectionChange handlers a change to influence a + // selection update. + function filterSelectionChange(doc, sel, options) { + var obj = { + ranges: sel.ranges, + update: function(ranges) { + this.ranges = []; + for (var i = 0; i < ranges.length; i++) + { this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), + clipPos(doc, ranges[i].head)); } + }, + origin: options && options.origin + }; + signal(doc, "beforeSelectionChange", doc, obj); + if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); } + if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) } + else { return sel } + } + + function setSelectionReplaceHistory(doc, sel, options) { + var done = doc.history.done, last = lst(done); + if (last && last.ranges) { + done[done.length - 1] = sel; + setSelectionNoUndo(doc, sel, options); + } else { + setSelection(doc, sel, options); + } + } + + // Set a new selection. + function setSelection(doc, sel, options) { + setSelectionNoUndo(doc, sel, options); + addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options); + } + + function setSelectionNoUndo(doc, sel, options) { + if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) + { sel = filterSelectionChange(doc, sel, options); } + + var bias = options && options.bias || + (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1); + setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true)); + + if (!(options && options.scroll === false) && doc.cm && doc.cm.getOption("readOnly") != "nocursor") + { ensureCursorVisible(doc.cm); } + } + + function setSelectionInner(doc, sel) { + if (sel.equals(doc.sel)) { return } + + doc.sel = sel; + + if (doc.cm) { + doc.cm.curOp.updateInput = 1; + doc.cm.curOp.selectionChanged = true; + signalCursorActivity(doc.cm); + } + signalLater(doc, "cursorActivity", doc); + } + + // Verify that the selection does not partially select any atomic + // marked ranges. + function reCheckSelection(doc) { + setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false)); + } + + // Return a selection that does not partially select any atomic + // ranges. + function skipAtomicInSelection(doc, sel, bias, mayClear) { + var out; + for (var i = 0; i < sel.ranges.length; i++) { + var range = sel.ranges[i]; + var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]; + var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear); + var newHead = range.head == range.anchor ? newAnchor : skipAtomic(doc, range.head, old && old.head, bias, mayClear); + if (out || newAnchor != range.anchor || newHead != range.head) { + if (!out) { out = sel.ranges.slice(0, i); } + out[i] = new Range(newAnchor, newHead); + } + } + return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel + } + + function skipAtomicInner(doc, pos, oldPos, dir, mayClear) { + var line = getLine(doc, pos.line); + if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { + var sp = line.markedSpans[i], m = sp.marker; + + // Determine if we should prevent the cursor being placed to the left/right of an atomic marker + // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it + // is with selectLeft/Right + var preventCursorLeft = ("selectLeft" in m) ? !m.selectLeft : m.inclusiveLeft; + var preventCursorRight = ("selectRight" in m) ? !m.selectRight : m.inclusiveRight; + + if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) && + (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) { + if (mayClear) { + signal(m, "beforeCursorEnter"); + if (m.explicitlyCleared) { + if (!line.markedSpans) { break } + else {--i; continue} + } + } + if (!m.atomic) { continue } + + if (oldPos) { + var near = m.find(dir < 0 ? 1 : -1), diff = (void 0); + if (dir < 0 ? preventCursorRight : preventCursorLeft) + { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); } + if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0)) + { return skipAtomicInner(doc, near, pos, dir, mayClear) } + } + + var far = m.find(dir < 0 ? -1 : 1); + if (dir < 0 ? preventCursorLeft : preventCursorRight) + { far = movePos(doc, far, dir, far.line == pos.line ? line : null); } + return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null + } + } } + return pos + } + + // Ensure a given position is not inside an atomic range. + function skipAtomic(doc, pos, oldPos, bias, mayClear) { + var dir = bias || 1; + var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) || + (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) || + skipAtomicInner(doc, pos, oldPos, -dir, mayClear) || + (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true)); + if (!found) { + doc.cantEdit = true; + return Pos(doc.first, 0) + } + return found + } + + function movePos(doc, pos, dir, line) { + if (dir < 0 && pos.ch == 0) { + if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) } + else { return null } + } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) { + if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) } + else { return null } + } else { + return new Pos(pos.line, pos.ch + dir) + } + } + + function selectAll(cm) { + cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll); + } + + // UPDATING + + // Allow "beforeChange" event handlers to influence a change + function filterChange(doc, change, update) { + var obj = { + canceled: false, + from: change.from, + to: change.to, + text: change.text, + origin: change.origin, + cancel: function () { return obj.canceled = true; } + }; + if (update) { obj.update = function (from, to, text, origin) { + if (from) { obj.from = clipPos(doc, from); } + if (to) { obj.to = clipPos(doc, to); } + if (text) { obj.text = text; } + if (origin !== undefined) { obj.origin = origin; } + }; } + signal(doc, "beforeChange", doc, obj); + if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); } + + if (obj.canceled) { + if (doc.cm) { doc.cm.curOp.updateInput = 2; } + return null + } + return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin} + } + + // Apply a change to a document, and add it to the document's + // history, and propagating it to all linked documents. + function makeChange(doc, change, ignoreReadOnly) { + if (doc.cm) { + if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) } + if (doc.cm.state.suppressEdits) { return } + } + + if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) { + change = filterChange(doc, change, true); + if (!change) { return } + } + + // Possibly split or suppress the update based on the presence + // of read-only spans in its range. + var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to); + if (split) { + for (var i = split.length - 1; i >= 0; --i) + { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); } + } else { + makeChangeInner(doc, change); + } + } + + function makeChangeInner(doc, change) { + if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return } + var selAfter = computeSelAfterChange(doc, change); + addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN); + + makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)); + var rebased = []; + + linkedDocs(doc, function (doc, sharedHist) { + if (!sharedHist && indexOf(rebased, doc.history) == -1) { + rebaseHist(doc.history, change); + rebased.push(doc.history); + } + makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change)); + }); + } + + // Revert a change stored in a document's history. + function makeChangeFromHistory(doc, type, allowSelectionOnly) { + var suppress = doc.cm && doc.cm.state.suppressEdits; + if (suppress && !allowSelectionOnly) { return } + + var hist = doc.history, event, selAfter = doc.sel; + var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done; + + // Verify that there is a useable event (so that ctrl-z won't + // needlessly clear selection events) + var i = 0; + for (; i < source.length; i++) { + event = source[i]; + if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges) + { break } + } + if (i == source.length) { return } + hist.lastOrigin = hist.lastSelOrigin = null; + + for (;;) { + event = source.pop(); + if (event.ranges) { + pushSelectionToHistory(event, dest); + if (allowSelectionOnly && !event.equals(doc.sel)) { + setSelection(doc, event, {clearRedo: false}); + return + } + selAfter = event; + } else if (suppress) { + source.push(event); + return + } else { break } + } + + // Build up a reverse change object to add to the opposite history + // stack (redo when undoing, and vice versa). + var antiChanges = []; + pushSelectionToHistory(selAfter, dest); + dest.push({changes: antiChanges, generation: hist.generation}); + hist.generation = event.generation || ++hist.maxGeneration; + + var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange"); + + var loop = function ( i ) { + var change = event.changes[i]; + change.origin = type; + if (filter && !filterChange(doc, change, false)) { + source.length = 0; + return {} + } + + antiChanges.push(historyChangeFromChange(doc, change)); + + var after = i ? computeSelAfterChange(doc, change) : lst(source); + makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); + if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); } + var rebased = []; + + // Propagate to the linked documents + linkedDocs(doc, function (doc, sharedHist) { + if (!sharedHist && indexOf(rebased, doc.history) == -1) { + rebaseHist(doc.history, change); + rebased.push(doc.history); + } + makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change)); + }); + }; + + for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) { + var returned = loop( i$1 ); + + if ( returned ) return returned.v; + } + } + + // Sub-views need their line numbers shifted when text is added + // above or below them in the parent document. + function shiftDoc(doc, distance) { + if (distance == 0) { return } + doc.first += distance; + doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range( + Pos(range.anchor.line + distance, range.anchor.ch), + Pos(range.head.line + distance, range.head.ch) + ); }), doc.sel.primIndex); + if (doc.cm) { + regChange(doc.cm, doc.first, doc.first - distance, distance); + for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++) + { regLineChange(doc.cm, l, "gutter"); } + } + } + + // More lower-level change function, handling only a single document + // (not linked ones). + function makeChangeSingleDoc(doc, change, selAfter, spans) { + if (doc.cm && !doc.cm.curOp) + { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) } + + if (change.to.line < doc.first) { + shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line)); + return + } + if (change.from.line > doc.lastLine()) { return } + + // Clip the change to the size of this doc + if (change.from.line < doc.first) { + var shift = change.text.length - 1 - (doc.first - change.from.line); + shiftDoc(doc, shift); + change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch), + text: [lst(change.text)], origin: change.origin}; + } + var last = doc.lastLine(); + if (change.to.line > last) { + change = {from: change.from, to: Pos(last, getLine(doc, last).text.length), + text: [change.text[0]], origin: change.origin}; + } + + change.removed = getBetween(doc, change.from, change.to); + + if (!selAfter) { selAfter = computeSelAfterChange(doc, change); } + if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); } + else { updateDoc(doc, change, spans); } + setSelectionNoUndo(doc, selAfter, sel_dontScroll); + + if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0))) + { doc.cantEdit = false; } + } + + // Handle the interaction of a change to a document with the editor + // that this document is part of. + function makeChangeSingleDocInEditor(cm, change, spans) { + var doc = cm.doc, display = cm.display, from = change.from, to = change.to; + + var recomputeMaxLength = false, checkWidthStart = from.line; + if (!cm.options.lineWrapping) { + checkWidthStart = lineNo(visualLine(getLine(doc, from.line))); + doc.iter(checkWidthStart, to.line + 1, function (line) { + if (line == display.maxLine) { + recomputeMaxLength = true; + return true + } + }); + } + + if (doc.sel.contains(change.from, change.to) > -1) + { signalCursorActivity(cm); } + + updateDoc(doc, change, spans, estimateHeight(cm)); + + if (!cm.options.lineWrapping) { + doc.iter(checkWidthStart, from.line + change.text.length, function (line) { + var len = lineLength(line); + if (len > display.maxLineLength) { + display.maxLine = line; + display.maxLineLength = len; + display.maxLineChanged = true; + recomputeMaxLength = false; + } + }); + if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; } + } + + retreatFrontier(doc, from.line); + startWorker(cm, 400); + + var lendiff = change.text.length - (to.line - from.line) - 1; + // Remember that these lines changed, for updating the display + if (change.full) + { regChange(cm); } + else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change)) + { regLineChange(cm, from.line, "text"); } + else + { regChange(cm, from.line, to.line + 1, lendiff); } + + var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change"); + if (changeHandler || changesHandler) { + var obj = { + from: from, to: to, + text: change.text, + removed: change.removed, + origin: change.origin + }; + if (changeHandler) { signalLater(cm, "change", cm, obj); } + if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); } + } + cm.display.selForContextMenu = null; + } + + function replaceRange(doc, code, from, to, origin) { + var assign; + + if (!to) { to = from; } + if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); } + if (typeof code == "string") { code = doc.splitLines(code); } + makeChange(doc, {from: from, to: to, text: code, origin: origin}); + } + + // Rebasing/resetting history to deal with externally-sourced changes + + function rebaseHistSelSingle(pos, from, to, diff) { + if (to < pos.line) { + pos.line += diff; + } else if (from < pos.line) { + pos.line = from; + pos.ch = 0; + } + } + + // Tries to rebase an array of history events given a change in the + // document. If the change touches the same lines as the event, the + // event, and everything 'behind' it, is discarded. If the change is + // before the event, the event's positions are updated. Uses a + // copy-on-write scheme for the positions, to avoid having to + // reallocate them all on every rebase, but also avoid problems with + // shared position objects being unsafely updated. + function rebaseHistArray(array, from, to, diff) { + for (var i = 0; i < array.length; ++i) { + var sub = array[i], ok = true; + if (sub.ranges) { + if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; } + for (var j = 0; j < sub.ranges.length; j++) { + rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff); + rebaseHistSelSingle(sub.ranges[j].head, from, to, diff); + } + continue + } + for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) { + var cur = sub.changes[j$1]; + if (to < cur.from.line) { + cur.from = Pos(cur.from.line + diff, cur.from.ch); + cur.to = Pos(cur.to.line + diff, cur.to.ch); + } else if (from <= cur.to.line) { + ok = false; + break + } + } + if (!ok) { + array.splice(0, i + 1); + i = 0; + } + } + } + + function rebaseHist(hist, change) { + var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1; + rebaseHistArray(hist.done, from, to, diff); + rebaseHistArray(hist.undone, from, to, diff); + } + + // Utility for applying a change to a line by handle or number, + // returning the number and optionally registering the line as + // changed. + function changeLine(doc, handle, changeType, op) { + var no = handle, line = handle; + if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); } + else { no = lineNo(handle); } + if (no == null) { return null } + if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); } + return line + } + + // The document is represented as a BTree consisting of leaves, with + // chunk of lines in them, and branches, with up to ten leaves or + // other branch nodes below them. The top node is always a branch + // node, and is the document object itself (meaning it has + // additional methods and properties). + // + // All nodes have parent links. The tree is used both to go from + // line numbers to line objects, and to go from objects to numbers. + // It also indexes by height, and is used to convert between height + // and line object, and to find the total height of the document. + // + // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html + + function LeafChunk(lines) { + this.lines = lines; + this.parent = null; + var height = 0; + for (var i = 0; i < lines.length; ++i) { + lines[i].parent = this; + height += lines[i].height; + } + this.height = height; + } + + LeafChunk.prototype = { + chunkSize: function() { return this.lines.length }, + + // Remove the n lines at offset 'at'. + removeInner: function(at, n) { + for (var i = at, e = at + n; i < e; ++i) { + var line = this.lines[i]; + this.height -= line.height; + cleanUpLine(line); + signalLater(line, "delete"); + } + this.lines.splice(at, n); + }, + + // Helper used to collapse a small branch into a single leaf. + collapse: function(lines) { + lines.push.apply(lines, this.lines); + }, + + // Insert the given array of lines at offset 'at', count them as + // having the given height. + insertInner: function(at, lines, height) { + this.height += height; + this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at)); + for (var i = 0; i < lines.length; ++i) { lines[i].parent = this; } + }, + + // Used to iterate over a part of the tree. + iterN: function(at, n, op) { + for (var e = at + n; at < e; ++at) + { if (op(this.lines[at])) { return true } } + } + }; + + function BranchChunk(children) { + this.children = children; + var size = 0, height = 0; + for (var i = 0; i < children.length; ++i) { + var ch = children[i]; + size += ch.chunkSize(); height += ch.height; + ch.parent = this; + } + this.size = size; + this.height = height; + this.parent = null; + } + + BranchChunk.prototype = { + chunkSize: function() { return this.size }, + + removeInner: function(at, n) { + this.size -= n; + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at < sz) { + var rm = Math.min(n, sz - at), oldHeight = child.height; + child.removeInner(at, rm); + this.height -= oldHeight - child.height; + if (sz == rm) { this.children.splice(i--, 1); child.parent = null; } + if ((n -= rm) == 0) { break } + at = 0; + } else { at -= sz; } + } + // If the result is smaller than 25 lines, ensure that it is a + // single leaf node. + if (this.size - n < 25 && + (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) { + var lines = []; + this.collapse(lines); + this.children = [new LeafChunk(lines)]; + this.children[0].parent = this; + } + }, + + collapse: function(lines) { + for (var i = 0; i < this.children.length; ++i) { this.children[i].collapse(lines); } + }, + + insertInner: function(at, lines, height) { + this.size += lines.length; + this.height += height; + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at <= sz) { + child.insertInner(at, lines, height); + if (child.lines && child.lines.length > 50) { + // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced. + // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest. + var remaining = child.lines.length % 25 + 25; + for (var pos = remaining; pos < child.lines.length;) { + var leaf = new LeafChunk(child.lines.slice(pos, pos += 25)); + child.height -= leaf.height; + this.children.splice(++i, 0, leaf); + leaf.parent = this; + } + child.lines = child.lines.slice(0, remaining); + this.maybeSpill(); + } + break + } + at -= sz; + } + }, + + // When a node has grown, check whether it should be split. + maybeSpill: function() { + if (this.children.length <= 10) { return } + var me = this; + do { + var spilled = me.children.splice(me.children.length - 5, 5); + var sibling = new BranchChunk(spilled); + if (!me.parent) { // Become the parent node + var copy = new BranchChunk(me.children); + copy.parent = me; + me.children = [copy, sibling]; + me = copy; + } else { + me.size -= sibling.size; + me.height -= sibling.height; + var myIndex = indexOf(me.parent.children, me); + me.parent.children.splice(myIndex + 1, 0, sibling); + } + sibling.parent = me.parent; + } while (me.children.length > 10) + me.parent.maybeSpill(); + }, + + iterN: function(at, n, op) { + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at < sz) { + var used = Math.min(n, sz - at); + if (child.iterN(at, used, op)) { return true } + if ((n -= used) == 0) { break } + at = 0; + } else { at -= sz; } + } + } + }; + + // Line widgets are block elements displayed above or below a line. + + var LineWidget = function(doc, node, options) { + if (options) { for (var opt in options) { if (options.hasOwnProperty(opt)) + { this[opt] = options[opt]; } } } + this.doc = doc; + this.node = node; + }; + + LineWidget.prototype.clear = function () { + var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line); + if (no == null || !ws) { return } + for (var i = 0; i < ws.length; ++i) { if (ws[i] == this) { ws.splice(i--, 1); } } + if (!ws.length) { line.widgets = null; } + var height = widgetHeight(this); + updateLineHeight(line, Math.max(0, line.height - height)); + if (cm) { + runInOp(cm, function () { + adjustScrollWhenAboveVisible(cm, line, -height); + regLineChange(cm, no, "widget"); + }); + signalLater(cm, "lineWidgetCleared", cm, this, no); + } + }; + + LineWidget.prototype.changed = function () { + var this$1 = this; + + var oldH = this.height, cm = this.doc.cm, line = this.line; + this.height = null; + var diff = widgetHeight(this) - oldH; + if (!diff) { return } + if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); } + if (cm) { + runInOp(cm, function () { + cm.curOp.forceUpdate = true; + adjustScrollWhenAboveVisible(cm, line, diff); + signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line)); + }); + } + }; + eventMixin(LineWidget); + + function adjustScrollWhenAboveVisible(cm, line, diff) { + if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop)) + { addToScrollTop(cm, diff); } + } + + function addLineWidget(doc, handle, node, options) { + var widget = new LineWidget(doc, node, options); + var cm = doc.cm; + if (cm && widget.noHScroll) { cm.display.alignWidgets = true; } + changeLine(doc, handle, "widget", function (line) { + var widgets = line.widgets || (line.widgets = []); + if (widget.insertAt == null) { widgets.push(widget); } + else { widgets.splice(Math.min(widgets.length, Math.max(0, widget.insertAt)), 0, widget); } + widget.line = line; + if (cm && !lineIsHidden(doc, line)) { + var aboveVisible = heightAtLine(line) < doc.scrollTop; + updateLineHeight(line, line.height + widgetHeight(widget)); + if (aboveVisible) { addToScrollTop(cm, widget.height); } + cm.curOp.forceUpdate = true; + } + return true + }); + if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); } + return widget + } + + // TEXTMARKERS + + // Created with markText and setBookmark methods. A TextMarker is a + // handle that can be used to clear or find a marked position in the + // document. Line objects hold arrays (markedSpans) containing + // {from, to, marker} object pointing to such marker objects, and + // indicating that such a marker is present on that line. Multiple + // lines may point to the same marker when it spans across lines. + // The spans will have null for their from/to properties when the + // marker continues beyond the start/end of the line. Markers have + // links back to the lines they currently touch. + + // Collapsed markers have unique ids, in order to be able to order + // them, which is needed for uniquely determining an outer marker + // when they overlap (they may nest, but not partially overlap). + var nextMarkerId = 0; + + var TextMarker = function(doc, type) { + this.lines = []; + this.type = type; + this.doc = doc; + this.id = ++nextMarkerId; + }; + + // Clear the marker. + TextMarker.prototype.clear = function () { + if (this.explicitlyCleared) { return } + var cm = this.doc.cm, withOp = cm && !cm.curOp; + if (withOp) { startOperation(cm); } + if (hasHandler(this, "clear")) { + var found = this.find(); + if (found) { signalLater(this, "clear", found.from, found.to); } + } + var min = null, max = null; + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); + if (cm && !this.collapsed) { regLineChange(cm, lineNo(line), "text"); } + else if (cm) { + if (span.to != null) { max = lineNo(line); } + if (span.from != null) { min = lineNo(line); } + } + line.markedSpans = removeMarkedSpan(line.markedSpans, span); + if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm) + { updateLineHeight(line, textHeight(cm.display)); } + } + if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) { + var visual = visualLine(this.lines[i$1]), len = lineLength(visual); + if (len > cm.display.maxLineLength) { + cm.display.maxLine = visual; + cm.display.maxLineLength = len; + cm.display.maxLineChanged = true; + } + } } + + if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); } + this.lines.length = 0; + this.explicitlyCleared = true; + if (this.atomic && this.doc.cantEdit) { + this.doc.cantEdit = false; + if (cm) { reCheckSelection(cm.doc); } + } + if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); } + if (withOp) { endOperation(cm); } + if (this.parent) { this.parent.clear(); } + }; + + // Find the position of the marker in the document. Returns a {from, + // to} object by default. Side can be passed to get a specific side + // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the + // Pos objects returned contain a line object, rather than a line + // number (used to prevent looking up the same line twice). + TextMarker.prototype.find = function (side, lineObj) { + if (side == null && this.type == "bookmark") { side = 1; } + var from, to; + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); + if (span.from != null) { + from = Pos(lineObj ? line : lineNo(line), span.from); + if (side == -1) { return from } + } + if (span.to != null) { + to = Pos(lineObj ? line : lineNo(line), span.to); + if (side == 1) { return to } + } + } + return from && {from: from, to: to} + }; + + // Signals that the marker's widget changed, and surrounding layout + // should be recomputed. + TextMarker.prototype.changed = function () { + var this$1 = this; + + var pos = this.find(-1, true), widget = this, cm = this.doc.cm; + if (!pos || !cm) { return } + runInOp(cm, function () { + var line = pos.line, lineN = lineNo(pos.line); + var view = findViewForLine(cm, lineN); + if (view) { + clearLineMeasurementCacheFor(view); + cm.curOp.selectionChanged = cm.curOp.forceUpdate = true; + } + cm.curOp.updateMaxLine = true; + if (!lineIsHidden(widget.doc, line) && widget.height != null) { + var oldHeight = widget.height; + widget.height = null; + var dHeight = widgetHeight(widget) - oldHeight; + if (dHeight) + { updateLineHeight(line, line.height + dHeight); } + } + signalLater(cm, "markerChanged", cm, this$1); + }); + }; + + TextMarker.prototype.attachLine = function (line) { + if (!this.lines.length && this.doc.cm) { + var op = this.doc.cm.curOp; + if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) + { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); } + } + this.lines.push(line); + }; + + TextMarker.prototype.detachLine = function (line) { + this.lines.splice(indexOf(this.lines, line), 1); + if (!this.lines.length && this.doc.cm) { + var op = this.doc.cm.curOp + ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this); + } + }; + eventMixin(TextMarker); + + // Create a marker, wire it up to the right lines, and + function markText(doc, from, to, options, type) { + // Shared markers (across linked documents) are handled separately + // (markTextShared will call out to this again, once per + // document). + if (options && options.shared) { return markTextShared(doc, from, to, options, type) } + // Ensure we are in an operation. + if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) } + + var marker = new TextMarker(doc, type), diff = cmp(from, to); + if (options) { copyObj(options, marker, false); } + // Don't connect empty markers unless clearWhenEmpty is false + if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false) + { return marker } + if (marker.replacedWith) { + // Showing up as a widget implies collapsed (widget replaces text) + marker.collapsed = true; + marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget"); + if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); } + if (options.insertLeft) { marker.widgetNode.insertLeft = true; } + } + if (marker.collapsed) { + if (conflictingCollapsedRange(doc, from.line, from, to, marker) || + from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker)) + { throw new Error("Inserting collapsed marker partially overlapping an existing one") } + seeCollapsedSpans(); + } + + if (marker.addToHistory) + { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); } + + var curLine = from.line, cm = doc.cm, updateMaxLine; + doc.iter(curLine, to.line + 1, function (line) { + if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine) + { updateMaxLine = true; } + if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); } + addMarkedSpan(line, new MarkedSpan(marker, + curLine == from.line ? from.ch : null, + curLine == to.line ? to.ch : null), doc.cm && doc.cm.curOp); + ++curLine; + }); + // lineIsHidden depends on the presence of the spans, so needs a second pass + if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) { + if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); } + }); } + + if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); } + + if (marker.readOnly) { + seeReadOnlySpans(); + if (doc.history.done.length || doc.history.undone.length) + { doc.clearHistory(); } + } + if (marker.collapsed) { + marker.id = ++nextMarkerId; + marker.atomic = true; + } + if (cm) { + // Sync editor state + if (updateMaxLine) { cm.curOp.updateMaxLine = true; } + if (marker.collapsed) + { regChange(cm, from.line, to.line + 1); } + else if (marker.className || marker.startStyle || marker.endStyle || marker.css || + marker.attributes || marker.title) + { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } } + if (marker.atomic) { reCheckSelection(cm.doc); } + signalLater(cm, "markerAdded", cm, marker); + } + return marker + } + + // SHARED TEXTMARKERS + + // A shared marker spans multiple linked documents. It is + // implemented as a meta-marker-object controlling multiple normal + // markers. + var SharedTextMarker = function(markers, primary) { + this.markers = markers; + this.primary = primary; + for (var i = 0; i < markers.length; ++i) + { markers[i].parent = this; } + }; + + SharedTextMarker.prototype.clear = function () { + if (this.explicitlyCleared) { return } + this.explicitlyCleared = true; + for (var i = 0; i < this.markers.length; ++i) + { this.markers[i].clear(); } + signalLater(this, "clear"); + }; + + SharedTextMarker.prototype.find = function (side, lineObj) { + return this.primary.find(side, lineObj) + }; + eventMixin(SharedTextMarker); + + function markTextShared(doc, from, to, options, type) { + options = copyObj(options); + options.shared = false; + var markers = [markText(doc, from, to, options, type)], primary = markers[0]; + var widget = options.widgetNode; + linkedDocs(doc, function (doc) { + if (widget) { options.widgetNode = widget.cloneNode(true); } + markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type)); + for (var i = 0; i < doc.linked.length; ++i) + { if (doc.linked[i].isParent) { return } } + primary = lst(markers); + }); + return new SharedTextMarker(markers, primary) + } + + function findSharedMarkers(doc) { + return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; }) + } + + function copySharedMarkers(doc, markers) { + for (var i = 0; i < markers.length; i++) { + var marker = markers[i], pos = marker.find(); + var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to); + if (cmp(mFrom, mTo)) { + var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type); + marker.markers.push(subMark); + subMark.parent = marker; + } + } + } + + function detachSharedMarkers(markers) { + var loop = function ( i ) { + var marker = markers[i], linked = [marker.primary.doc]; + linkedDocs(marker.primary.doc, function (d) { return linked.push(d); }); + for (var j = 0; j < marker.markers.length; j++) { + var subMarker = marker.markers[j]; + if (indexOf(linked, subMarker.doc) == -1) { + subMarker.parent = null; + marker.markers.splice(j--, 1); + } + } + }; + + for (var i = 0; i < markers.length; i++) loop( i ); + } + + var nextDocId = 0; + var Doc = function(text, mode, firstLine, lineSep, direction) { + if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) } + if (firstLine == null) { firstLine = 0; } + + BranchChunk.call(this, [new LeafChunk([new Line("", null)])]); + this.first = firstLine; + this.scrollTop = this.scrollLeft = 0; + this.cantEdit = false; + this.cleanGeneration = 1; + this.modeFrontier = this.highlightFrontier = firstLine; + var start = Pos(firstLine, 0); + this.sel = simpleSelection(start); + this.history = new History(null); + this.id = ++nextDocId; + this.modeOption = mode; + this.lineSep = lineSep; + this.direction = (direction == "rtl") ? "rtl" : "ltr"; + this.extend = false; + + if (typeof text == "string") { text = this.splitLines(text); } + updateDoc(this, {from: start, to: start, text: text}); + setSelection(this, simpleSelection(start), sel_dontScroll); + }; + + Doc.prototype = createObj(BranchChunk.prototype, { + constructor: Doc, + // Iterate over the document. Supports two forms -- with only one + // argument, it calls that for each line in the document. With + // three, it iterates over the range given by the first two (with + // the second being non-inclusive). + iter: function(from, to, op) { + if (op) { this.iterN(from - this.first, to - from, op); } + else { this.iterN(this.first, this.first + this.size, from); } + }, + + // Non-public interface for adding and removing lines. + insert: function(at, lines) { + var height = 0; + for (var i = 0; i < lines.length; ++i) { height += lines[i].height; } + this.insertInner(at - this.first, lines, height); + }, + remove: function(at, n) { this.removeInner(at - this.first, n); }, + + // From here, the methods are part of the public interface. Most + // are also available from CodeMirror (editor) instances. + + getValue: function(lineSep) { + var lines = getLines(this, this.first, this.first + this.size); + if (lineSep === false) { return lines } + return lines.join(lineSep || this.lineSeparator()) + }, + setValue: docMethodOp(function(code) { + var top = Pos(this.first, 0), last = this.first + this.size - 1; + makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length), + text: this.splitLines(code), origin: "setValue", full: true}, true); + if (this.cm) { scrollToCoords(this.cm, 0, 0); } + setSelection(this, simpleSelection(top), sel_dontScroll); + }), + replaceRange: function(code, from, to, origin) { + from = clipPos(this, from); + to = to ? clipPos(this, to) : from; + replaceRange(this, code, from, to, origin); + }, + getRange: function(from, to, lineSep) { + var lines = getBetween(this, clipPos(this, from), clipPos(this, to)); + if (lineSep === false) { return lines } + if (lineSep === '') { return lines.join('') } + return lines.join(lineSep || this.lineSeparator()) + }, + + getLine: function(line) {var l = this.getLineHandle(line); return l && l.text}, + + getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }}, + getLineNumber: function(line) {return lineNo(line)}, + + getLineHandleVisualStart: function(line) { + if (typeof line == "number") { line = getLine(this, line); } + return visualLine(line) + }, + + lineCount: function() {return this.size}, + firstLine: function() {return this.first}, + lastLine: function() {return this.first + this.size - 1}, + + clipPos: function(pos) {return clipPos(this, pos)}, + + getCursor: function(start) { + var range = this.sel.primary(), pos; + if (start == null || start == "head") { pos = range.head; } + else if (start == "anchor") { pos = range.anchor; } + else if (start == "end" || start == "to" || start === false) { pos = range.to(); } + else { pos = range.from(); } + return pos + }, + listSelections: function() { return this.sel.ranges }, + somethingSelected: function() {return this.sel.somethingSelected()}, + + setCursor: docMethodOp(function(line, ch, options) { + setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options); + }), + setSelection: docMethodOp(function(anchor, head, options) { + setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options); + }), + extendSelection: docMethodOp(function(head, other, options) { + extendSelection(this, clipPos(this, head), other && clipPos(this, other), options); + }), + extendSelections: docMethodOp(function(heads, options) { + extendSelections(this, clipPosArray(this, heads), options); + }), + extendSelectionsBy: docMethodOp(function(f, options) { + var heads = map(this.sel.ranges, f); + extendSelections(this, clipPosArray(this, heads), options); + }), + setSelections: docMethodOp(function(ranges, primary, options) { + if (!ranges.length) { return } + var out = []; + for (var i = 0; i < ranges.length; i++) + { out[i] = new Range(clipPos(this, ranges[i].anchor), + clipPos(this, ranges[i].head || ranges[i].anchor)); } + if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); } + setSelection(this, normalizeSelection(this.cm, out, primary), options); + }), + addSelection: docMethodOp(function(anchor, head, options) { + var ranges = this.sel.ranges.slice(0); + ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor))); + setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options); + }), + + getSelection: function(lineSep) { + var ranges = this.sel.ranges, lines; + for (var i = 0; i < ranges.length; i++) { + var sel = getBetween(this, ranges[i].from(), ranges[i].to()); + lines = lines ? lines.concat(sel) : sel; + } + if (lineSep === false) { return lines } + else { return lines.join(lineSep || this.lineSeparator()) } + }, + getSelections: function(lineSep) { + var parts = [], ranges = this.sel.ranges; + for (var i = 0; i < ranges.length; i++) { + var sel = getBetween(this, ranges[i].from(), ranges[i].to()); + if (lineSep !== false) { sel = sel.join(lineSep || this.lineSeparator()); } + parts[i] = sel; + } + return parts + }, + replaceSelection: function(code, collapse, origin) { + var dup = []; + for (var i = 0; i < this.sel.ranges.length; i++) + { dup[i] = code; } + this.replaceSelections(dup, collapse, origin || "+input"); + }, + replaceSelections: docMethodOp(function(code, collapse, origin) { + var changes = [], sel = this.sel; + for (var i = 0; i < sel.ranges.length; i++) { + var range = sel.ranges[i]; + changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin}; + } + var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse); + for (var i$1 = changes.length - 1; i$1 >= 0; i$1--) + { makeChange(this, changes[i$1]); } + if (newSel) { setSelectionReplaceHistory(this, newSel); } + else if (this.cm) { ensureCursorVisible(this.cm); } + }), + undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}), + redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}), + undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}), + redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}), + + setExtending: function(val) {this.extend = val;}, + getExtending: function() {return this.extend}, + + historySize: function() { + var hist = this.history, done = 0, undone = 0; + for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } } + for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } } + return {undo: done, redo: undone} + }, + clearHistory: function() { + var this$1 = this; + + this.history = new History(this.history); + linkedDocs(this, function (doc) { return doc.history = this$1.history; }, true); + }, + + markClean: function() { + this.cleanGeneration = this.changeGeneration(true); + }, + changeGeneration: function(forceSplit) { + if (forceSplit) + { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; } + return this.history.generation + }, + isClean: function (gen) { + return this.history.generation == (gen || this.cleanGeneration) + }, + + getHistory: function() { + return {done: copyHistoryArray(this.history.done), + undone: copyHistoryArray(this.history.undone)} + }, + setHistory: function(histData) { + var hist = this.history = new History(this.history); + hist.done = copyHistoryArray(histData.done.slice(0), null, true); + hist.undone = copyHistoryArray(histData.undone.slice(0), null, true); + }, + + setGutterMarker: docMethodOp(function(line, gutterID, value) { + return changeLine(this, line, "gutter", function (line) { + var markers = line.gutterMarkers || (line.gutterMarkers = {}); + markers[gutterID] = value; + if (!value && isEmpty(markers)) { line.gutterMarkers = null; } + return true + }) + }), + + clearGutter: docMethodOp(function(gutterID) { + var this$1 = this; + + this.iter(function (line) { + if (line.gutterMarkers && line.gutterMarkers[gutterID]) { + changeLine(this$1, line, "gutter", function () { + line.gutterMarkers[gutterID] = null; + if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; } + return true + }); + } + }); + }), + + lineInfo: function(line) { + var n; + if (typeof line == "number") { + if (!isLine(this, line)) { return null } + n = line; + line = getLine(this, line); + if (!line) { return null } + } else { + n = lineNo(line); + if (n == null) { return null } + } + return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, + textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass, + widgets: line.widgets} + }, + + addLineClass: docMethodOp(function(handle, where, cls) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { + var prop = where == "text" ? "textClass" + : where == "background" ? "bgClass" + : where == "gutter" ? "gutterClass" : "wrapClass"; + if (!line[prop]) { line[prop] = cls; } + else if (classTest(cls).test(line[prop])) { return false } + else { line[prop] += " " + cls; } + return true + }) + }), + removeLineClass: docMethodOp(function(handle, where, cls) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { + var prop = where == "text" ? "textClass" + : where == "background" ? "bgClass" + : where == "gutter" ? "gutterClass" : "wrapClass"; + var cur = line[prop]; + if (!cur) { return false } + else if (cls == null) { line[prop] = null; } + else { + var found = cur.match(classTest(cls)); + if (!found) { return false } + var end = found.index + found[0].length; + line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null; + } + return true + }) + }), + + addLineWidget: docMethodOp(function(handle, node, options) { + return addLineWidget(this, handle, node, options) + }), + removeLineWidget: function(widget) { widget.clear(); }, + + markText: function(from, to, options) { + return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range") + }, + setBookmark: function(pos, options) { + var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options), + insertLeft: options && options.insertLeft, + clearWhenEmpty: false, shared: options && options.shared, + handleMouseEvents: options && options.handleMouseEvents}; + pos = clipPos(this, pos); + return markText(this, pos, pos, realOpts, "bookmark") + }, + findMarksAt: function(pos) { + pos = clipPos(this, pos); + var markers = [], spans = getLine(this, pos.line).markedSpans; + if (spans) { for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if ((span.from == null || span.from <= pos.ch) && + (span.to == null || span.to >= pos.ch)) + { markers.push(span.marker.parent || span.marker); } + } } + return markers + }, + findMarks: function(from, to, filter) { + from = clipPos(this, from); to = clipPos(this, to); + var found = [], lineNo = from.line; + this.iter(from.line, to.line + 1, function (line) { + var spans = line.markedSpans; + if (spans) { for (var i = 0; i < spans.length; i++) { + var span = spans[i]; + if (!(span.to != null && lineNo == from.line && from.ch >= span.to || + span.from == null && lineNo != from.line || + span.from != null && lineNo == to.line && span.from >= to.ch) && + (!filter || filter(span.marker))) + { found.push(span.marker.parent || span.marker); } + } } + ++lineNo; + }); + return found + }, + getAllMarks: function() { + var markers = []; + this.iter(function (line) { + var sps = line.markedSpans; + if (sps) { for (var i = 0; i < sps.length; ++i) + { if (sps[i].from != null) { markers.push(sps[i].marker); } } } + }); + return markers + }, + + posFromIndex: function(off) { + var ch, lineNo = this.first, sepSize = this.lineSeparator().length; + this.iter(function (line) { + var sz = line.text.length + sepSize; + if (sz > off) { ch = off; return true } + off -= sz; + ++lineNo; + }); + return clipPos(this, Pos(lineNo, ch)) + }, + indexFromPos: function (coords) { + coords = clipPos(this, coords); + var index = coords.ch; + if (coords.line < this.first || coords.ch < 0) { return 0 } + var sepSize = this.lineSeparator().length; + this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value + index += line.text.length + sepSize; + }); + return index + }, + + copy: function(copyHistory) { + var doc = new Doc(getLines(this, this.first, this.first + this.size), + this.modeOption, this.first, this.lineSep, this.direction); + doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft; + doc.sel = this.sel; + doc.extend = false; + if (copyHistory) { + doc.history.undoDepth = this.history.undoDepth; + doc.setHistory(this.getHistory()); + } + return doc + }, + + linkedDoc: function(options) { + if (!options) { options = {}; } + var from = this.first, to = this.first + this.size; + if (options.from != null && options.from > from) { from = options.from; } + if (options.to != null && options.to < to) { to = options.to; } + var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction); + if (options.sharedHist) { copy.history = this.history + ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist}); + copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]; + copySharedMarkers(copy, findSharedMarkers(this)); + return copy + }, + unlinkDoc: function(other) { + if (other instanceof CodeMirror) { other = other.doc; } + if (this.linked) { for (var i = 0; i < this.linked.length; ++i) { + var link = this.linked[i]; + if (link.doc != other) { continue } + this.linked.splice(i, 1); + other.unlinkDoc(this); + detachSharedMarkers(findSharedMarkers(this)); + break + } } + // If the histories were shared, split them again + if (other.history == this.history) { + var splitIds = [other.id]; + linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true); + other.history = new History(null); + other.history.done = copyHistoryArray(this.history.done, splitIds); + other.history.undone = copyHistoryArray(this.history.undone, splitIds); + } + }, + iterLinkedDocs: function(f) {linkedDocs(this, f);}, + + getMode: function() {return this.mode}, + getEditor: function() {return this.cm}, + + splitLines: function(str) { + if (this.lineSep) { return str.split(this.lineSep) } + return splitLinesAuto(str) + }, + lineSeparator: function() { return this.lineSep || "\n" }, + + setDirection: docMethodOp(function (dir) { + if (dir != "rtl") { dir = "ltr"; } + if (dir == this.direction) { return } + this.direction = dir; + this.iter(function (line) { return line.order = null; }); + if (this.cm) { directionChanged(this.cm); } + }) + }); + + // Public alias. + Doc.prototype.eachLine = Doc.prototype.iter; + + // Kludge to work around strange IE behavior where it'll sometimes + // re-fire a series of drag-related events right after the drop (#1551) + var lastDrop = 0; + + function onDrop(e) { + var cm = this; + clearDragCursor(cm); + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) + { return } + e_preventDefault(e); + if (ie) { lastDrop = +new Date; } + var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files; + if (!pos || cm.isReadOnly()) { return } + // Might be a file drop, in which case we simply extract the text + // and insert it. + if (files && files.length && window.FileReader && window.File) { + var n = files.length, text = Array(n), read = 0; + var markAsReadAndPasteIfAllFilesAreRead = function () { + if (++read == n) { + operation(cm, function () { + pos = clipPos(cm.doc, pos); + var change = {from: pos, to: pos, + text: cm.doc.splitLines( + text.filter(function (t) { return t != null; }).join(cm.doc.lineSeparator())), + origin: "paste"}; + makeChange(cm.doc, change); + setSelectionReplaceHistory(cm.doc, simpleSelection(clipPos(cm.doc, pos), clipPos(cm.doc, changeEnd(change)))); + })(); + } + }; + var readTextFromFile = function (file, i) { + if (cm.options.allowDropFileTypes && + indexOf(cm.options.allowDropFileTypes, file.type) == -1) { + markAsReadAndPasteIfAllFilesAreRead(); + return + } + var reader = new FileReader; + reader.onerror = function () { return markAsReadAndPasteIfAllFilesAreRead(); }; + reader.onload = function () { + var content = reader.result; + if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { + markAsReadAndPasteIfAllFilesAreRead(); + return + } + text[i] = content; + markAsReadAndPasteIfAllFilesAreRead(); + }; + reader.readAsText(file); + }; + for (var i = 0; i < files.length; i++) { readTextFromFile(files[i], i); } + } else { // Normal drop + // Don't do a replace if the drop happened inside of the selected text. + if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) { + cm.state.draggingText(e); + // Ensure the editor is re-focused + setTimeout(function () { return cm.display.input.focus(); }, 20); + return + } + try { + var text$1 = e.dataTransfer.getData("Text"); + if (text$1) { + var selected; + if (cm.state.draggingText && !cm.state.draggingText.copy) + { selected = cm.listSelections(); } + setSelectionNoUndo(cm.doc, simpleSelection(pos, pos)); + if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1) + { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } } + cm.replaceSelection(text$1, "around", "paste"); + cm.display.input.focus(); + } + } + catch(e$1){} + } + } + + function onDragStart(cm, e) { + if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return } + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return } + + e.dataTransfer.setData("Text", cm.getSelection()); + e.dataTransfer.effectAllowed = "copyMove"; + + // Use dummy image instead of default browsers image. + // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there. + if (e.dataTransfer.setDragImage && !safari) { + var img = elt("img", null, null, "position: fixed; left: 0; top: 0;"); + img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; + if (presto) { + img.width = img.height = 1; + cm.display.wrapper.appendChild(img); + // Force a relayout, or Opera won't use our image for some obscure reason + img._top = img.offsetTop; + } + e.dataTransfer.setDragImage(img, 0, 0); + if (presto) { img.parentNode.removeChild(img); } + } + } + + function onDragOver(cm, e) { + var pos = posFromMouse(cm, e); + if (!pos) { return } + var frag = document.createDocumentFragment(); + drawSelectionCursor(cm, pos, frag); + if (!cm.display.dragCursor) { + cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors"); + cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv); + } + removeChildrenAndAdd(cm.display.dragCursor, frag); + } + + function clearDragCursor(cm) { + if (cm.display.dragCursor) { + cm.display.lineSpace.removeChild(cm.display.dragCursor); + cm.display.dragCursor = null; + } + } + + // These must be handled carefully, because naively registering a + // handler for each editor will cause the editors to never be + // garbage collected. + + function forEachCodeMirror(f) { + if (!document.getElementsByClassName) { return } + var byClass = document.getElementsByClassName("CodeMirror"), editors = []; + for (var i = 0; i < byClass.length; i++) { + var cm = byClass[i].CodeMirror; + if (cm) { editors.push(cm); } + } + if (editors.length) { editors[0].operation(function () { + for (var i = 0; i < editors.length; i++) { f(editors[i]); } + }); } + } + + var globalsRegistered = false; + function ensureGlobalHandlers() { + if (globalsRegistered) { return } + registerGlobalHandlers(); + globalsRegistered = true; + } + function registerGlobalHandlers() { + // When the window resizes, we need to refresh active editors. + var resizeTimer; + on(window, "resize", function () { + if (resizeTimer == null) { resizeTimer = setTimeout(function () { + resizeTimer = null; + forEachCodeMirror(onResize); + }, 100); } + }); + // When the window loses focus, we want to show the editor as blurred + on(window, "blur", function () { return forEachCodeMirror(onBlur); }); + } + // Called when the window resizes + function onResize(cm) { + var d = cm.display; + // Might be a text scaling operation, clear size caches. + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; + d.scrollbarsClipped = false; + cm.setSize(); + } + + var keyNames = { + 3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", + 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", + 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", + 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", + 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 145: "ScrollLock", + 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", + 221: "]", 222: "'", 224: "Mod", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", + 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert" + }; + + // Number keys + for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); } + // Alphabetic keys + for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); } + // Function keys + for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; } + + var keyMap = {}; + + keyMap.basic = { + "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", + "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", + "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore", + "Tab": "defaultTab", "Shift-Tab": "indentAuto", + "Enter": "newlineAndIndent", "Insert": "toggleOverwrite", + "Esc": "singleSelection" + }; + // Note that the save and find-related commands aren't defined by + // default. User code or addons can define them. Unknown commands + // are simply ignored. + keyMap.pcDefault = { + "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", + "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown", + "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", + "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find", + "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", + "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", + "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection", + "fallthrough": "basic" + }; + // Very basic readline/emacs-style bindings, which are standard on Mac. + keyMap.emacsy = { + "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", + "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", + "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", + "Ctrl-T": "transposeChars", "Ctrl-O": "openLine" + }; + keyMap.macDefault = { + "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", + "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft", + "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore", + "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find", + "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", + "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight", + "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd", + "fallthrough": ["basic", "emacsy"] + }; + keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault; + + // KEYMAP DISPATCH + + function normalizeKeyName(name) { + var parts = name.split(/-(?!$)/); + name = parts[parts.length - 1]; + var alt, ctrl, shift, cmd; + for (var i = 0; i < parts.length - 1; i++) { + var mod = parts[i]; + if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; } + else if (/^a(lt)?$/i.test(mod)) { alt = true; } + else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; } + else if (/^s(hift)?$/i.test(mod)) { shift = true; } + else { throw new Error("Unrecognized modifier name: " + mod) } + } + if (alt) { name = "Alt-" + name; } + if (ctrl) { name = "Ctrl-" + name; } + if (cmd) { name = "Cmd-" + name; } + if (shift) { name = "Shift-" + name; } + return name + } + + // This is a kludge to keep keymaps mostly working as raw objects + // (backwards compatibility) while at the same time support features + // like normalization and multi-stroke key bindings. It compiles a + // new normalized keymap, and then updates the old object to reflect + // this. + function normalizeKeyMap(keymap) { + var copy = {}; + for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) { + var value = keymap[keyname]; + if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue } + if (value == "...") { delete keymap[keyname]; continue } + + var keys = map(keyname.split(" "), normalizeKeyName); + for (var i = 0; i < keys.length; i++) { + var val = (void 0), name = (void 0); + if (i == keys.length - 1) { + name = keys.join(" "); + val = value; + } else { + name = keys.slice(0, i + 1).join(" "); + val = "..."; + } + var prev = copy[name]; + if (!prev) { copy[name] = val; } + else if (prev != val) { throw new Error("Inconsistent bindings for " + name) } + } + delete keymap[keyname]; + } } + for (var prop in copy) { keymap[prop] = copy[prop]; } + return keymap + } + + function lookupKey(key, map, handle, context) { + map = getKeyMap(map); + var found = map.call ? map.call(key, context) : map[key]; + if (found === false) { return "nothing" } + if (found === "...") { return "multi" } + if (found != null && handle(found)) { return "handled" } + + if (map.fallthrough) { + if (Object.prototype.toString.call(map.fallthrough) != "[object Array]") + { return lookupKey(key, map.fallthrough, handle, context) } + for (var i = 0; i < map.fallthrough.length; i++) { + var result = lookupKey(key, map.fallthrough[i], handle, context); + if (result) { return result } + } + } + } + + // Modifier key presses don't count as 'real' key presses for the + // purpose of keymap fallthrough. + function isModifierKey(value) { + var name = typeof value == "string" ? value : keyNames[value.keyCode]; + return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod" + } + + function addModifierNames(name, event, noShift) { + var base = name; + if (event.altKey && base != "Alt") { name = "Alt-" + name; } + if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; } + if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Mod") { name = "Cmd-" + name; } + if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; } + return name + } + + // Look up the name of a key as indicated by an event object. + function keyName(event, noShift) { + if (presto && event.keyCode == 34 && event["char"]) { return false } + var name = keyNames[event.keyCode]; + if (name == null || event.altGraphKey) { return false } + // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause, + // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+) + if (event.keyCode == 3 && event.code) { name = event.code; } + return addModifierNames(name, event, noShift) + } + + function getKeyMap(val) { + return typeof val == "string" ? keyMap[val] : val + } + + // Helper for deleting text near the selection(s), used to implement + // backspace, delete, and similar functionality. + function deleteNearSelection(cm, compute) { + var ranges = cm.doc.sel.ranges, kill = []; + // Build up a set of ranges to kill first, merging overlapping + // ranges. + for (var i = 0; i < ranges.length; i++) { + var toKill = compute(ranges[i]); + while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) { + var replaced = kill.pop(); + if (cmp(replaced.from, toKill.from) < 0) { + toKill.from = replaced.from; + break + } + } + kill.push(toKill); + } + // Next, remove those actual ranges. + runInOp(cm, function () { + for (var i = kill.length - 1; i >= 0; i--) + { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); } + ensureCursorVisible(cm); + }); + } + + function moveCharLogically(line, ch, dir) { + var target = skipExtendingChars(line.text, ch + dir, dir); + return target < 0 || target > line.text.length ? null : target + } + + function moveLogically(line, start, dir) { + var ch = moveCharLogically(line, start.ch, dir); + return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before") + } + + function endOfLine(visually, cm, lineObj, lineNo, dir) { + if (visually) { + if (cm.doc.direction == "rtl") { dir = -dir; } + var order = getOrder(lineObj, cm.doc.direction); + if (order) { + var part = dir < 0 ? lst(order) : order[0]; + var moveInStorageOrder = (dir < 0) == (part.level == 1); + var sticky = moveInStorageOrder ? "after" : "before"; + var ch; + // With a wrapped rtl chunk (possibly spanning multiple bidi parts), + // it could be that the last bidi part is not on the last visual line, + // since visual lines contain content order-consecutive chunks. + // Thus, in rtl, we are looking for the first (content-order) character + // in the rtl chunk that is on the last line (that is, the same line + // as the last (content-order) character). + if (part.level > 0 || cm.doc.direction == "rtl") { + var prep = prepareMeasureForLine(cm, lineObj); + ch = dir < 0 ? lineObj.text.length - 1 : 0; + var targetTop = measureCharPrepared(cm, prep, ch).top; + ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch); + if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); } + } else { ch = dir < 0 ? part.to : part.from; } + return new Pos(lineNo, ch, sticky) + } + } + return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after") + } + + function moveVisually(cm, line, start, dir) { + var bidi = getOrder(line, cm.doc.direction); + if (!bidi) { return moveLogically(line, start, dir) } + if (start.ch >= line.text.length) { + start.ch = line.text.length; + start.sticky = "before"; + } else if (start.ch <= 0) { + start.ch = 0; + start.sticky = "after"; + } + var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]; + if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) { + // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines, + // nothing interesting happens. + return moveLogically(line, start, dir) + } + + var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }; + var prep; + var getWrappedLineExtent = function (ch) { + if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} } + prep = prep || prepareMeasureForLine(cm, line); + return wrappedLineExtentChar(cm, line, prep, ch) + }; + var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch); + + if (cm.doc.direction == "rtl" || part.level == 1) { + var moveInStorageOrder = (part.level == 1) == (dir < 0); + var ch = mv(start, moveInStorageOrder ? 1 : -1); + if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) { + // Case 2: We move within an rtl part or in an rtl editor on the same visual line + var sticky = moveInStorageOrder ? "before" : "after"; + return new Pos(start.line, ch, sticky) + } + } + + // Case 3: Could not move within this bidi part in this visual line, so leave + // the current bidi part + + var searchInVisualLine = function (partPos, dir, wrappedLineExtent) { + var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder + ? new Pos(start.line, mv(ch, 1), "before") + : new Pos(start.line, ch, "after"); }; + + for (; partPos >= 0 && partPos < bidi.length; partPos += dir) { + var part = bidi[partPos]; + var moveInStorageOrder = (dir > 0) == (part.level != 1); + var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1); + if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) } + ch = moveInStorageOrder ? part.from : mv(part.to, -1); + if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) } + } + }; + + // Case 3a: Look for other bidi parts on the same visual line + var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent); + if (res) { return res } + + // Case 3b: Look for other bidi parts on the next visual line + var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1); + if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) { + res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh)); + if (res) { return res } + } + + // Case 4: Nowhere to move + return null + } + + // Commands are parameter-less actions that can be performed on an + // editor, mostly used for keybindings. + var commands = { + selectAll: selectAll, + singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); }, + killLine: function (cm) { return deleteNearSelection(cm, function (range) { + if (range.empty()) { + var len = getLine(cm.doc, range.head.line).text.length; + if (range.head.ch == len && range.head.line < cm.lastLine()) + { return {from: range.head, to: Pos(range.head.line + 1, 0)} } + else + { return {from: range.head, to: Pos(range.head.line, len)} } + } else { + return {from: range.from(), to: range.to()} + } + }); }, + deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({ + from: Pos(range.from().line, 0), + to: clipPos(cm.doc, Pos(range.to().line + 1, 0)) + }); }); }, + delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({ + from: Pos(range.from().line, 0), to: range.from() + }); }); }, + delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { + var top = cm.charCoords(range.head, "div").top + 5; + var leftPos = cm.coordsChar({left: 0, top: top}, "div"); + return {from: leftPos, to: range.from()} + }); }, + delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) { + var top = cm.charCoords(range.head, "div").top + 5; + var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"); + return {from: range.from(), to: rightPos } + }); }, + undo: function (cm) { return cm.undo(); }, + redo: function (cm) { return cm.redo(); }, + undoSelection: function (cm) { return cm.undoSelection(); }, + redoSelection: function (cm) { return cm.redoSelection(); }, + goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); }, + goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); }, + goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); }, + {origin: "+move", bias: 1} + ); }, + goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); }, + {origin: "+move", bias: 1} + ); }, + goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); }, + {origin: "+move", bias: -1} + ); }, + goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.cursorCoords(range.head, "div").top + 5; + return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") + }, sel_move); }, + goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.cursorCoords(range.head, "div").top + 5; + return cm.coordsChar({left: 0, top: top}, "div") + }, sel_move); }, + goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.cursorCoords(range.head, "div").top + 5; + var pos = cm.coordsChar({left: 0, top: top}, "div"); + if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) } + return pos + }, sel_move); }, + goLineUp: function (cm) { return cm.moveV(-1, "line"); }, + goLineDown: function (cm) { return cm.moveV(1, "line"); }, + goPageUp: function (cm) { return cm.moveV(-1, "page"); }, + goPageDown: function (cm) { return cm.moveV(1, "page"); }, + goCharLeft: function (cm) { return cm.moveH(-1, "char"); }, + goCharRight: function (cm) { return cm.moveH(1, "char"); }, + goColumnLeft: function (cm) { return cm.moveH(-1, "column"); }, + goColumnRight: function (cm) { return cm.moveH(1, "column"); }, + goWordLeft: function (cm) { return cm.moveH(-1, "word"); }, + goGroupRight: function (cm) { return cm.moveH(1, "group"); }, + goGroupLeft: function (cm) { return cm.moveH(-1, "group"); }, + goWordRight: function (cm) { return cm.moveH(1, "word"); }, + delCharBefore: function (cm) { return cm.deleteH(-1, "codepoint"); }, + delCharAfter: function (cm) { return cm.deleteH(1, "char"); }, + delWordBefore: function (cm) { return cm.deleteH(-1, "word"); }, + delWordAfter: function (cm) { return cm.deleteH(1, "word"); }, + delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); }, + delGroupAfter: function (cm) { return cm.deleteH(1, "group"); }, + indentAuto: function (cm) { return cm.indentSelection("smart"); }, + indentMore: function (cm) { return cm.indentSelection("add"); }, + indentLess: function (cm) { return cm.indentSelection("subtract"); }, + insertTab: function (cm) { return cm.replaceSelection("\t"); }, + insertSoftTab: function (cm) { + var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize; + for (var i = 0; i < ranges.length; i++) { + var pos = ranges[i].from(); + var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize); + spaces.push(spaceStr(tabSize - col % tabSize)); + } + cm.replaceSelections(spaces); + }, + defaultTab: function (cm) { + if (cm.somethingSelected()) { cm.indentSelection("add"); } + else { cm.execCommand("insertTab"); } + }, + // Swap the two chars left and right of each selection's head. + // Move cursor behind the two swapped characters afterwards. + // + // Doesn't consider line feeds a character. + // Doesn't scan more than one line above to find a character. + // Doesn't do anything on an empty line. + // Doesn't do anything with non-empty selections. + transposeChars: function (cm) { return runInOp(cm, function () { + var ranges = cm.listSelections(), newSel = []; + for (var i = 0; i < ranges.length; i++) { + if (!ranges[i].empty()) { continue } + var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text; + if (line) { + if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); } + if (cur.ch > 0) { + cur = new Pos(cur.line, cur.ch + 1); + cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), + Pos(cur.line, cur.ch - 2), cur, "+transpose"); + } else if (cur.line > cm.doc.first) { + var prev = getLine(cm.doc, cur.line - 1).text; + if (prev) { + cur = new Pos(cur.line, 1); + cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() + + prev.charAt(prev.length - 1), + Pos(cur.line - 1, prev.length - 1), cur, "+transpose"); + } + } + } + newSel.push(new Range(cur, cur)); + } + cm.setSelections(newSel); + }); }, + newlineAndIndent: function (cm) { return runInOp(cm, function () { + var sels = cm.listSelections(); + for (var i = sels.length - 1; i >= 0; i--) + { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); } + sels = cm.listSelections(); + for (var i$1 = 0; i$1 < sels.length; i$1++) + { cm.indentLine(sels[i$1].from().line, null, true); } + ensureCursorVisible(cm); + }); }, + openLine: function (cm) { return cm.replaceSelection("\n", "start"); }, + toggleOverwrite: function (cm) { return cm.toggleOverwrite(); } + }; + + + function lineStart(cm, lineN) { + var line = getLine(cm.doc, lineN); + var visual = visualLine(line); + if (visual != line) { lineN = lineNo(visual); } + return endOfLine(true, cm, visual, lineN, 1) + } + function lineEnd(cm, lineN) { + var line = getLine(cm.doc, lineN); + var visual = visualLineEnd(line); + if (visual != line) { lineN = lineNo(visual); } + return endOfLine(true, cm, line, lineN, -1) + } + function lineStartSmart(cm, pos) { + var start = lineStart(cm, pos.line); + var line = getLine(cm.doc, start.line); + var order = getOrder(line, cm.doc.direction); + if (!order || order[0].level == 0) { + var firstNonWS = Math.max(start.ch, line.text.search(/\S/)); + var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch; + return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky) + } + return start + } + + // Run a handler that was bound to a key. + function doHandleBinding(cm, bound, dropShift) { + if (typeof bound == "string") { + bound = commands[bound]; + if (!bound) { return false } + } + // Ensure previous input has been read, so that the handler sees a + // consistent view of the document + cm.display.input.ensurePolled(); + var prevShift = cm.display.shift, done = false; + try { + if (cm.isReadOnly()) { cm.state.suppressEdits = true; } + if (dropShift) { cm.display.shift = false; } + done = bound(cm) != Pass; + } finally { + cm.display.shift = prevShift; + cm.state.suppressEdits = false; + } + return done + } + + function lookupKeyForEditor(cm, name, handle) { + for (var i = 0; i < cm.state.keyMaps.length; i++) { + var result = lookupKey(name, cm.state.keyMaps[i], handle, cm); + if (result) { return result } + } + return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm)) + || lookupKey(name, cm.options.keyMap, handle, cm) + } + + // Note that, despite the name, this function is also used to check + // for bound mouse clicks. + + var stopSeq = new Delayed; + + function dispatchKey(cm, name, e, handle) { + var seq = cm.state.keySeq; + if (seq) { + if (isModifierKey(name)) { return "handled" } + if (/\'$/.test(name)) + { cm.state.keySeq = null; } + else + { stopSeq.set(50, function () { + if (cm.state.keySeq == seq) { + cm.state.keySeq = null; + cm.display.input.reset(); + } + }); } + if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true } + } + return dispatchKeyInner(cm, name, e, handle) + } + + function dispatchKeyInner(cm, name, e, handle) { + var result = lookupKeyForEditor(cm, name, handle); + + if (result == "multi") + { cm.state.keySeq = name; } + if (result == "handled") + { signalLater(cm, "keyHandled", cm, name, e); } + + if (result == "handled" || result == "multi") { + e_preventDefault(e); + restartBlink(cm); + } + + return !!result + } + + // Handle a key from the keydown event. + function handleKeyBinding(cm, e) { + var name = keyName(e, true); + if (!name) { return false } + + if (e.shiftKey && !cm.state.keySeq) { + // First try to resolve full name (including 'Shift-'). Failing + // that, see if there is a cursor-motion command (starting with + // 'go') bound to the keyname without 'Shift-'. + return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); }) + || dispatchKey(cm, name, e, function (b) { + if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion) + { return doHandleBinding(cm, b) } + }) + } else { + return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); }) + } + } + + // Handle a key from the keypress event + function handleCharBinding(cm, e, ch) { + return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); }) + } + + var lastStoppedKey = null; + function onKeyDown(e) { + var cm = this; + if (e.target && e.target != cm.display.input.getField()) { return } + cm.curOp.focus = activeElt(doc(cm)); + if (signalDOMEvent(cm, e)) { return } + // IE does strange things with escape. + if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; } + var code = e.keyCode; + cm.display.shift = code == 16 || e.shiftKey; + var handled = handleKeyBinding(cm, e); + if (presto) { + lastStoppedKey = handled ? code : null; + // Opera has no cut event... we try to at least catch the key combo + if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey)) + { cm.replaceSelection("", null, "cut"); } + } + if (gecko && !mac && !handled && code == 46 && e.shiftKey && !e.ctrlKey && document.execCommand) + { document.execCommand("cut"); } + + // Turn mouse into crosshair when Alt is held on Mac. + if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className)) + { showCrossHair(cm); } + } + + function showCrossHair(cm) { + var lineDiv = cm.display.lineDiv; + addClass(lineDiv, "CodeMirror-crosshair"); + + function up(e) { + if (e.keyCode == 18 || !e.altKey) { + rmClass(lineDiv, "CodeMirror-crosshair"); + off(document, "keyup", up); + off(document, "mouseover", up); + } + } + on(document, "keyup", up); + on(document, "mouseover", up); + } + + function onKeyUp(e) { + if (e.keyCode == 16) { this.doc.sel.shift = false; } + signalDOMEvent(this, e); + } + + function onKeyPress(e) { + var cm = this; + if (e.target && e.target != cm.display.input.getField()) { return } + if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return } + var keyCode = e.keyCode, charCode = e.charCode; + if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return} + if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return } + var ch = String.fromCharCode(charCode == null ? keyCode : charCode); + // Some browsers fire keypress events for backspace + if (ch == "\x08") { return } + if (handleCharBinding(cm, e, ch)) { return } + cm.display.input.onKeyPress(e); + } + + var DOUBLECLICK_DELAY = 400; + + var PastClick = function(time, pos, button) { + this.time = time; + this.pos = pos; + this.button = button; + }; + + PastClick.prototype.compare = function (time, pos, button) { + return this.time + DOUBLECLICK_DELAY > time && + cmp(pos, this.pos) == 0 && button == this.button + }; + + var lastClick, lastDoubleClick; + function clickRepeat(pos, button) { + var now = +new Date; + if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) { + lastClick = lastDoubleClick = null; + return "triple" + } else if (lastClick && lastClick.compare(now, pos, button)) { + lastDoubleClick = new PastClick(now, pos, button); + lastClick = null; + return "double" + } else { + lastClick = new PastClick(now, pos, button); + lastDoubleClick = null; + return "single" + } + } + + // A mouse down can be a single click, double click, triple click, + // start of selection drag, start of text drag, new cursor + // (ctrl-click), rectangle drag (alt-drag), or xwin + // middle-click-paste. Or it might be a click on something we should + // not interfere with, such as a scrollbar or widget. + function onMouseDown(e) { + var cm = this, display = cm.display; + if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return } + display.input.ensurePolled(); + display.shift = e.shiftKey; + + if (eventInWidget(display, e)) { + if (!webkit) { + // Briefly turn off draggability, to allow widgets to do + // normal dragging things. + display.scroller.draggable = false; + setTimeout(function () { return display.scroller.draggable = true; }, 100); + } + return + } + if (clickInGutter(cm, e)) { return } + var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single"; + win(cm).focus(); + + // #3261: make sure, that we're not starting a second selection + if (button == 1 && cm.state.selectingText) + { cm.state.selectingText(e); } + + if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return } + + if (button == 1) { + if (pos) { leftButtonDown(cm, pos, repeat, e); } + else if (e_target(e) == display.scroller) { e_preventDefault(e); } + } else if (button == 2) { + if (pos) { extendSelection(cm.doc, pos); } + setTimeout(function () { return display.input.focus(); }, 20); + } else if (button == 3) { + if (captureRightClick) { cm.display.input.onContextMenu(e); } + else { delayBlurEvent(cm); } + } + } + + function handleMappedButton(cm, button, pos, repeat, event) { + var name = "Click"; + if (repeat == "double") { name = "Double" + name; } + else if (repeat == "triple") { name = "Triple" + name; } + name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name; + + return dispatchKey(cm, addModifierNames(name, event), event, function (bound) { + if (typeof bound == "string") { bound = commands[bound]; } + if (!bound) { return false } + var done = false; + try { + if (cm.isReadOnly()) { cm.state.suppressEdits = true; } + done = bound(cm, pos) != Pass; + } finally { + cm.state.suppressEdits = false; + } + return done + }) + } + + function configureMouse(cm, repeat, event) { + var option = cm.getOption("configureMouse"); + var value = option ? option(cm, repeat, event) : {}; + if (value.unit == null) { + var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey; + value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line"; + } + if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; } + if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; } + if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); } + return value + } + + function leftButtonDown(cm, pos, repeat, event) { + if (ie) { setTimeout(bind(ensureFocus, cm), 0); } + else { cm.curOp.focus = activeElt(doc(cm)); } + + var behavior = configureMouse(cm, repeat, event); + + var sel = cm.doc.sel, contained; + if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() && + repeat == "single" && (contained = sel.contains(pos)) > -1 && + (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) && + (cmp(contained.to(), pos) > 0 || pos.xRel < 0)) + { leftButtonStartDrag(cm, event, pos, behavior); } + else + { leftButtonSelect(cm, event, pos, behavior); } + } + + // Start a text drag. When it ends, see if any dragging actually + // happen, and treat as a click if it didn't. + function leftButtonStartDrag(cm, event, pos, behavior) { + var display = cm.display, moved = false; + var dragEnd = operation(cm, function (e) { + if (webkit) { display.scroller.draggable = false; } + cm.state.draggingText = false; + if (cm.state.delayingBlurEvent) { + if (cm.hasFocus()) { cm.state.delayingBlurEvent = false; } + else { delayBlurEvent(cm); } + } + off(display.wrapper.ownerDocument, "mouseup", dragEnd); + off(display.wrapper.ownerDocument, "mousemove", mouseMove); + off(display.scroller, "dragstart", dragStart); + off(display.scroller, "drop", dragEnd); + if (!moved) { + e_preventDefault(e); + if (!behavior.addNew) + { extendSelection(cm.doc, pos, null, null, behavior.extend); } + // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081) + if ((webkit && !safari) || ie && ie_version == 9) + { setTimeout(function () {display.wrapper.ownerDocument.body.focus({preventScroll: true}); display.input.focus();}, 20); } + else + { display.input.focus(); } + } + }); + var mouseMove = function(e2) { + moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10; + }; + var dragStart = function () { return moved = true; }; + // Let the drag handler handle this. + if (webkit) { display.scroller.draggable = true; } + cm.state.draggingText = dragEnd; + dragEnd.copy = !behavior.moveOnDrag; + on(display.wrapper.ownerDocument, "mouseup", dragEnd); + on(display.wrapper.ownerDocument, "mousemove", mouseMove); + on(display.scroller, "dragstart", dragStart); + on(display.scroller, "drop", dragEnd); + + cm.state.delayingBlurEvent = true; + setTimeout(function () { return display.input.focus(); }, 20); + // IE's approach to draggable + if (display.scroller.dragDrop) { display.scroller.dragDrop(); } + } + + function rangeForUnit(cm, pos, unit) { + if (unit == "char") { return new Range(pos, pos) } + if (unit == "word") { return cm.findWordAt(pos) } + if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) } + var result = unit(cm, pos); + return new Range(result.from, result.to) + } + + // Normal selection, as opposed to text dragging. + function leftButtonSelect(cm, event, start, behavior) { + if (ie) { delayBlurEvent(cm); } + var display = cm.display, doc$1 = cm.doc; + e_preventDefault(event); + + var ourRange, ourIndex, startSel = doc$1.sel, ranges = startSel.ranges; + if (behavior.addNew && !behavior.extend) { + ourIndex = doc$1.sel.contains(start); + if (ourIndex > -1) + { ourRange = ranges[ourIndex]; } + else + { ourRange = new Range(start, start); } + } else { + ourRange = doc$1.sel.primary(); + ourIndex = doc$1.sel.primIndex; + } + + if (behavior.unit == "rectangle") { + if (!behavior.addNew) { ourRange = new Range(start, start); } + start = posFromMouse(cm, event, true, true); + ourIndex = -1; + } else { + var range = rangeForUnit(cm, start, behavior.unit); + if (behavior.extend) + { ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend); } + else + { ourRange = range; } + } + + if (!behavior.addNew) { + ourIndex = 0; + setSelection(doc$1, new Selection([ourRange], 0), sel_mouse); + startSel = doc$1.sel; + } else if (ourIndex == -1) { + ourIndex = ranges.length; + setSelection(doc$1, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex), + {scroll: false, origin: "*mouse"}); + } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) { + setSelection(doc$1, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0), + {scroll: false, origin: "*mouse"}); + startSel = doc$1.sel; + } else { + replaceOneSelection(doc$1, ourIndex, ourRange, sel_mouse); + } + + var lastPos = start; + function extendTo(pos) { + if (cmp(lastPos, pos) == 0) { return } + lastPos = pos; + + if (behavior.unit == "rectangle") { + var ranges = [], tabSize = cm.options.tabSize; + var startCol = countColumn(getLine(doc$1, start.line).text, start.ch, tabSize); + var posCol = countColumn(getLine(doc$1, pos.line).text, pos.ch, tabSize); + var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol); + for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line)); + line <= end; line++) { + var text = getLine(doc$1, line).text, leftPos = findColumn(text, left, tabSize); + if (left == right) + { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); } + else if (text.length > leftPos) + { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); } + } + if (!ranges.length) { ranges.push(new Range(start, start)); } + setSelection(doc$1, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex), + {origin: "*mouse", scroll: false}); + cm.scrollIntoView(pos); + } else { + var oldRange = ourRange; + var range = rangeForUnit(cm, pos, behavior.unit); + var anchor = oldRange.anchor, head; + if (cmp(range.anchor, anchor) > 0) { + head = range.head; + anchor = minPos(oldRange.from(), range.anchor); + } else { + head = range.anchor; + anchor = maxPos(oldRange.to(), range.head); + } + var ranges$1 = startSel.ranges.slice(0); + ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc$1, anchor), head)); + setSelection(doc$1, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse); + } + } + + var editorSize = display.wrapper.getBoundingClientRect(); + // Used to ensure timeout re-tries don't fire when another extend + // happened in the meantime (clearTimeout isn't reliable -- at + // least on Chrome, the timeouts still happen even when cleared, + // if the clear happens after their scheduled firing time). + var counter = 0; + + function extend(e) { + var curCount = ++counter; + var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle"); + if (!cur) { return } + if (cmp(cur, lastPos) != 0) { + cm.curOp.focus = activeElt(doc(cm)); + extendTo(cur); + var visible = visibleLines(display, doc$1); + if (cur.line >= visible.to || cur.line < visible.from) + { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); } + } else { + var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0; + if (outside) { setTimeout(operation(cm, function () { + if (counter != curCount) { return } + display.scroller.scrollTop += outside; + extend(e); + }), 50); } + } + } + + function done(e) { + cm.state.selectingText = false; + counter = Infinity; + // If e is null or undefined we interpret this as someone trying + // to explicitly cancel the selection rather than the user + // letting go of the mouse button. + if (e) { + e_preventDefault(e); + display.input.focus(); + } + off(display.wrapper.ownerDocument, "mousemove", move); + off(display.wrapper.ownerDocument, "mouseup", up); + doc$1.history.lastSelOrigin = null; + } + + var move = operation(cm, function (e) { + if (e.buttons === 0 || !e_button(e)) { done(e); } + else { extend(e); } + }); + var up = operation(cm, done); + cm.state.selectingText = up; + on(display.wrapper.ownerDocument, "mousemove", move); + on(display.wrapper.ownerDocument, "mouseup", up); + } + + // Used when mouse-selecting to adjust the anchor to the proper side + // of a bidi jump depending on the visual position of the head. + function bidiSimplify(cm, range) { + var anchor = range.anchor; + var head = range.head; + var anchorLine = getLine(cm.doc, anchor.line); + if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range } + var order = getOrder(anchorLine); + if (!order) { return range } + var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index]; + if (part.from != anchor.ch && part.to != anchor.ch) { return range } + var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1); + if (boundary == 0 || boundary == order.length) { return range } + + // Compute the relative visual position of the head compared to the + // anchor (<0 is to the left, >0 to the right) + var leftSide; + if (head.line != anchor.line) { + leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0; + } else { + var headIndex = getBidiPartAt(order, head.ch, head.sticky); + var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1); + if (headIndex == boundary - 1 || headIndex == boundary) + { leftSide = dir < 0; } + else + { leftSide = dir > 0; } + } + + var usePart = order[boundary + (leftSide ? -1 : 0)]; + var from = leftSide == (usePart.level == 1); + var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before"; + return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head) + } + + + // Determines whether an event happened in the gutter, and fires the + // handlers for the corresponding event. + function gutterEvent(cm, e, type, prevent) { + var mX, mY; + if (e.touches) { + mX = e.touches[0].clientX; + mY = e.touches[0].clientY; + } else { + try { mX = e.clientX; mY = e.clientY; } + catch(e$1) { return false } + } + if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false } + if (prevent) { e_preventDefault(e); } + + var display = cm.display; + var lineBox = display.lineDiv.getBoundingClientRect(); + + if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) } + mY -= lineBox.top - display.viewOffset; + + for (var i = 0; i < cm.display.gutterSpecs.length; ++i) { + var g = display.gutters.childNodes[i]; + if (g && g.getBoundingClientRect().right >= mX) { + var line = lineAtHeight(cm.doc, mY); + var gutter = cm.display.gutterSpecs[i]; + signal(cm, type, cm, line, gutter.className, e); + return e_defaultPrevented(e) + } + } + } + + function clickInGutter(cm, e) { + return gutterEvent(cm, e, "gutterClick", true) + } + + // CONTEXT MENU HANDLING + + // To make the context menu work, we need to briefly unhide the + // textarea (making it as unobtrusive as possible) to let the + // right-click take effect on it. + function onContextMenu(cm, e) { + if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return } + if (signalDOMEvent(cm, e, "contextmenu")) { return } + if (!captureRightClick) { cm.display.input.onContextMenu(e); } + } + + function contextMenuInGutter(cm, e) { + if (!hasHandler(cm, "gutterContextMenu")) { return false } + return gutterEvent(cm, e, "gutterContextMenu", false) + } + + function themeChanged(cm) { + cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") + + cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-"); + clearCaches(cm); + } + + var Init = {toString: function(){return "CodeMirror.Init"}}; + + var defaults = {}; + var optionHandlers = {}; + + function defineOptions(CodeMirror) { + var optionHandlers = CodeMirror.optionHandlers; + + function option(name, deflt, handle, notOnInit) { + CodeMirror.defaults[name] = deflt; + if (handle) { optionHandlers[name] = + notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; } + } + + CodeMirror.defineOption = option; + + // Passed to option handlers when there is no old value. + CodeMirror.Init = Init; + + // These two are, on init, called from the constructor because they + // have to be initialized before the editor can start at all. + option("value", "", function (cm, val) { return cm.setValue(val); }, true); + option("mode", null, function (cm, val) { + cm.doc.modeOption = val; + loadMode(cm); + }, true); + + option("indentUnit", 2, loadMode, true); + option("indentWithTabs", false); + option("smartIndent", true); + option("tabSize", 4, function (cm) { + resetModeState(cm); + clearCaches(cm); + regChange(cm); + }, true); + + option("lineSeparator", null, function (cm, val) { + cm.doc.lineSep = val; + if (!val) { return } + var newBreaks = [], lineNo = cm.doc.first; + cm.doc.iter(function (line) { + for (var pos = 0;;) { + var found = line.text.indexOf(val, pos); + if (found == -1) { break } + pos = found + val.length; + newBreaks.push(Pos(lineNo, found)); + } + lineNo++; + }); + for (var i = newBreaks.length - 1; i >= 0; i--) + { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); } + }); + option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\u202d\u202e\u2066\u2067\u2069\ufeff\ufff9-\ufffc]/g, function (cm, val, old) { + cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g"); + if (old != Init) { cm.refresh(); } + }); + option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true); + option("electricChars", true); + option("inputStyle", mobile ? "contenteditable" : "textarea", function () { + throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME + }, true); + option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true); + option("autocorrect", false, function (cm, val) { return cm.getInputField().autocorrect = val; }, true); + option("autocapitalize", false, function (cm, val) { return cm.getInputField().autocapitalize = val; }, true); + option("rtlMoveVisually", !windows); + option("wholeLineUpdateBefore", true); + + option("theme", "default", function (cm) { + themeChanged(cm); + updateGutters(cm); + }, true); + option("keyMap", "default", function (cm, val, old) { + var next = getKeyMap(val); + var prev = old != Init && getKeyMap(old); + if (prev && prev.detach) { prev.detach(cm, next); } + if (next.attach) { next.attach(cm, prev || null); } + }); + option("extraKeys", null); + option("configureMouse", null); + + option("lineWrapping", false, wrappingChanged, true); + option("gutters", [], function (cm, val) { + cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers); + updateGutters(cm); + }, true); + option("fixedGutter", true, function (cm, val) { + cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"; + cm.refresh(); + }, true); + option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true); + option("scrollbarStyle", "native", function (cm) { + initScrollbars(cm); + updateScrollbars(cm); + cm.display.scrollbars.setScrollTop(cm.doc.scrollTop); + cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft); + }, true); + option("lineNumbers", false, function (cm, val) { + cm.display.gutterSpecs = getGutters(cm.options.gutters, val); + updateGutters(cm); + }, true); + option("firstLineNumber", 1, updateGutters, true); + option("lineNumberFormatter", function (integer) { return integer; }, updateGutters, true); + option("showCursorWhenSelecting", false, updateSelection, true); + + option("resetSelectionOnContextMenu", true); + option("lineWiseCopyCut", true); + option("pasteLinesPerSelection", true); + option("selectionsMayTouch", false); + + option("readOnly", false, function (cm, val) { + if (val == "nocursor") { + onBlur(cm); + cm.display.input.blur(); + } + cm.display.input.readOnlyChanged(val); + }); + + option("screenReaderLabel", null, function (cm, val) { + val = (val === '') ? null : val; + cm.display.input.screenReaderLabelChanged(val); + }); + + option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true); + option("dragDrop", true, dragDropChanged); + option("allowDropFileTypes", null); + + option("cursorBlinkRate", 530); + option("cursorScrollMargin", 0); + option("cursorHeight", 1, updateSelection, true); + option("singleCursorHeightPerLine", true, updateSelection, true); + option("workTime", 100); + option("workDelay", 100); + option("flattenSpans", true, resetModeState, true); + option("addModeClass", false, resetModeState, true); + option("pollInterval", 100); + option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; }); + option("historyEventDelay", 1250); + option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true); + option("maxHighlightLength", 10000, resetModeState, true); + option("moveInputWithCursor", true, function (cm, val) { + if (!val) { cm.display.input.resetPosition(); } + }); + + option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; }); + option("autofocus", null); + option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true); + option("phrases", null); + } + + function dragDropChanged(cm, value, old) { + var wasOn = old && old != Init; + if (!value != !wasOn) { + var funcs = cm.display.dragFunctions; + var toggle = value ? on : off; + toggle(cm.display.scroller, "dragstart", funcs.start); + toggle(cm.display.scroller, "dragenter", funcs.enter); + toggle(cm.display.scroller, "dragover", funcs.over); + toggle(cm.display.scroller, "dragleave", funcs.leave); + toggle(cm.display.scroller, "drop", funcs.drop); + } + } + + function wrappingChanged(cm) { + if (cm.options.lineWrapping) { + addClass(cm.display.wrapper, "CodeMirror-wrap"); + cm.display.sizer.style.minWidth = ""; + cm.display.sizerWidth = null; + } else { + rmClass(cm.display.wrapper, "CodeMirror-wrap"); + findMaxLine(cm); + } + estimateLineHeights(cm); + regChange(cm); + clearCaches(cm); + setTimeout(function () { return updateScrollbars(cm); }, 100); + } + + // A CodeMirror instance represents an editor. This is the object + // that user code is usually dealing with. + + function CodeMirror(place, options) { + var this$1 = this; + + if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) } + + this.options = options = options ? copyObj(options) : {}; + // Determine effective options based on given values and defaults. + copyObj(defaults, options, false); + + var doc = options.value; + if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); } + else if (options.mode) { doc.modeOption = options.mode; } + this.doc = doc; + + var input = new CodeMirror.inputStyles[options.inputStyle](this); + var display = this.display = new Display(place, doc, input, options); + display.wrapper.CodeMirror = this; + themeChanged(this); + if (options.lineWrapping) + { this.display.wrapper.className += " CodeMirror-wrap"; } + initScrollbars(this); + + this.state = { + keyMaps: [], // stores maps added by addKeyMap + overlays: [], // highlighting overlays, as added by addOverlay + modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info + overwrite: false, + delayingBlurEvent: false, + focused: false, + suppressEdits: false, // used to disable editing during key handlers when in readOnly mode + pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll + selectingText: false, + draggingText: false, + highlight: new Delayed(), // stores highlight worker timeout + keySeq: null, // Unfinished key sequence + specialChars: null + }; + + if (options.autofocus && !mobile) { display.input.focus(); } + + // Override magic textarea content restore that IE sometimes does + // on our hidden textarea on reload + if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); } + + registerEventHandlers(this); + ensureGlobalHandlers(); + + startOperation(this); + this.curOp.forceUpdate = true; + attachDoc(this, doc); + + if ((options.autofocus && !mobile) || this.hasFocus()) + { setTimeout(function () { + if (this$1.hasFocus() && !this$1.state.focused) { onFocus(this$1); } + }, 20); } + else + { onBlur(this); } + + for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt)) + { optionHandlers[opt](this, options[opt], Init); } } + maybeUpdateLineNumberWidth(this); + if (options.finishInit) { options.finishInit(this); } + for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this); } + endOperation(this); + // Suppress optimizelegibility in Webkit, since it breaks text + // measuring on line wrapping boundaries. + if (webkit && options.lineWrapping && + getComputedStyle(display.lineDiv).textRendering == "optimizelegibility") + { display.lineDiv.style.textRendering = "auto"; } + } + + // The default configuration options. + CodeMirror.defaults = defaults; + // Functions to run when options are changed. + CodeMirror.optionHandlers = optionHandlers; + + // Attach the necessary event handlers when initializing the editor + function registerEventHandlers(cm) { + var d = cm.display; + on(d.scroller, "mousedown", operation(cm, onMouseDown)); + // Older IE's will not fire a second mousedown for a double click + if (ie && ie_version < 11) + { on(d.scroller, "dblclick", operation(cm, function (e) { + if (signalDOMEvent(cm, e)) { return } + var pos = posFromMouse(cm, e); + if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return } + e_preventDefault(e); + var word = cm.findWordAt(pos); + extendSelection(cm.doc, word.anchor, word.head); + })); } + else + { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); } + // Some browsers fire contextmenu *after* opening the menu, at + // which point we can't mess with it anymore. Context menu is + // handled in onMouseDown for these browsers. + on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }); + on(d.input.getField(), "contextmenu", function (e) { + if (!d.scroller.contains(e.target)) { onContextMenu(cm, e); } + }); + + // Used to suppress mouse event handling when a touch happens + var touchFinished, prevTouch = {end: 0}; + function finishTouch() { + if (d.activeTouch) { + touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000); + prevTouch = d.activeTouch; + prevTouch.end = +new Date; + } + } + function isMouseLikeTouchEvent(e) { + if (e.touches.length != 1) { return false } + var touch = e.touches[0]; + return touch.radiusX <= 1 && touch.radiusY <= 1 + } + function farAway(touch, other) { + if (other.left == null) { return true } + var dx = other.left - touch.left, dy = other.top - touch.top; + return dx * dx + dy * dy > 20 * 20 + } + on(d.scroller, "touchstart", function (e) { + if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) { + d.input.ensurePolled(); + clearTimeout(touchFinished); + var now = +new Date; + d.activeTouch = {start: now, moved: false, + prev: now - prevTouch.end <= 300 ? prevTouch : null}; + if (e.touches.length == 1) { + d.activeTouch.left = e.touches[0].pageX; + d.activeTouch.top = e.touches[0].pageY; + } + } + }); + on(d.scroller, "touchmove", function () { + if (d.activeTouch) { d.activeTouch.moved = true; } + }); + on(d.scroller, "touchend", function (e) { + var touch = d.activeTouch; + if (touch && !eventInWidget(d, e) && touch.left != null && + !touch.moved && new Date - touch.start < 300) { + var pos = cm.coordsChar(d.activeTouch, "page"), range; + if (!touch.prev || farAway(touch, touch.prev)) // Single tap + { range = new Range(pos, pos); } + else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap + { range = cm.findWordAt(pos); } + else // Triple tap + { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); } + cm.setSelection(range.anchor, range.head); + cm.focus(); + e_preventDefault(e); + } + finishTouch(); + }); + on(d.scroller, "touchcancel", finishTouch); + + // Sync scrolling between fake scrollbars and real scrollable + // area, ensure viewport is updated when scrolling. + on(d.scroller, "scroll", function () { + if (d.scroller.clientHeight) { + updateScrollTop(cm, d.scroller.scrollTop); + setScrollLeft(cm, d.scroller.scrollLeft, true); + signal(cm, "scroll", cm); + } + }); + + // Listen to wheel events in order to try and update the viewport on time. + on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); }); + on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); }); + + // Prevent wrapper from ever scrolling + on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); + + d.dragFunctions = { + enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }}, + over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }}, + start: function (e) { return onDragStart(cm, e); }, + drop: operation(cm, onDrop), + leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }} + }; + + var inp = d.input.getField(); + on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); }); + on(inp, "keydown", operation(cm, onKeyDown)); + on(inp, "keypress", operation(cm, onKeyPress)); + on(inp, "focus", function (e) { return onFocus(cm, e); }); + on(inp, "blur", function (e) { return onBlur(cm, e); }); + } + + var initHooks = []; + CodeMirror.defineInitHook = function (f) { return initHooks.push(f); }; + + // Indent the given line. The how parameter can be "smart", + // "add"/null, "subtract", or "prev". When aggressive is false + // (typically set to true for forced single-line indents), empty + // lines are not indented, and places where the mode returns Pass + // are left alone. + function indentLine(cm, n, how, aggressive) { + var doc = cm.doc, state; + if (how == null) { how = "add"; } + if (how == "smart") { + // Fall back to "prev" when the mode doesn't have an indentation + // method. + if (!doc.mode.indent) { how = "prev"; } + else { state = getContextBefore(cm, n).state; } + } + + var tabSize = cm.options.tabSize; + var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize); + if (line.stateAfter) { line.stateAfter = null; } + var curSpaceString = line.text.match(/^\s*/)[0], indentation; + if (!aggressive && !/\S/.test(line.text)) { + indentation = 0; + how = "not"; + } else if (how == "smart") { + indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text); + if (indentation == Pass || indentation > 150) { + if (!aggressive) { return } + how = "prev"; + } + } + if (how == "prev") { + if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); } + else { indentation = 0; } + } else if (how == "add") { + indentation = curSpace + cm.options.indentUnit; + } else if (how == "subtract") { + indentation = curSpace - cm.options.indentUnit; + } else if (typeof how == "number") { + indentation = curSpace + how; + } + indentation = Math.max(0, indentation); + + var indentString = "", pos = 0; + if (cm.options.indentWithTabs) + { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} } + if (pos < indentation) { indentString += spaceStr(indentation - pos); } + + if (indentString != curSpaceString) { + replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); + line.stateAfter = null; + return true + } else { + // Ensure that, if the cursor was in the whitespace at the start + // of the line, it is moved to the end of that space. + for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) { + var range = doc.sel.ranges[i$1]; + if (range.head.line == n && range.head.ch < curSpaceString.length) { + var pos$1 = Pos(n, curSpaceString.length); + replaceOneSelection(doc, i$1, new Range(pos$1, pos$1)); + break + } + } + } + } + + // This will be set to a {lineWise: bool, text: [string]} object, so + // that, when pasting, we know what kind of selections the copied + // text was made out of. + var lastCopied = null; + + function setLastCopied(newLastCopied) { + lastCopied = newLastCopied; + } + + function applyTextInput(cm, inserted, deleted, sel, origin) { + var doc = cm.doc; + cm.display.shift = false; + if (!sel) { sel = doc.sel; } + + var recent = +new Date - 200; + var paste = origin == "paste" || cm.state.pasteIncoming > recent; + var textLines = splitLinesAuto(inserted), multiPaste = null; + // When pasting N lines into N selections, insert one line per selection + if (paste && sel.ranges.length > 1) { + if (lastCopied && lastCopied.text.join("\n") == inserted) { + if (sel.ranges.length % lastCopied.text.length == 0) { + multiPaste = []; + for (var i = 0; i < lastCopied.text.length; i++) + { multiPaste.push(doc.splitLines(lastCopied.text[i])); } + } + } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) { + multiPaste = map(textLines, function (l) { return [l]; }); + } + } + + var updateInput = cm.curOp.updateInput; + // Normal behavior is to insert the new text into every selection + for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) { + var range = sel.ranges[i$1]; + var from = range.from(), to = range.to(); + if (range.empty()) { + if (deleted && deleted > 0) // Handle deletion + { from = Pos(from.line, from.ch - deleted); } + else if (cm.state.overwrite && !paste) // Handle overwrite + { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); } + else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == textLines.join("\n")) + { from = to = Pos(from.line, 0); } + } + var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines, + origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")}; + makeChange(cm.doc, changeEvent); + signalLater(cm, "inputRead", cm, changeEvent); + } + if (inserted && !paste) + { triggerElectric(cm, inserted); } + + ensureCursorVisible(cm); + if (cm.curOp.updateInput < 2) { cm.curOp.updateInput = updateInput; } + cm.curOp.typing = true; + cm.state.pasteIncoming = cm.state.cutIncoming = -1; + } + + function handlePaste(e, cm) { + var pasted = e.clipboardData && e.clipboardData.getData("Text"); + if (pasted) { + e.preventDefault(); + if (!cm.isReadOnly() && !cm.options.disableInput && cm.hasFocus()) + { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); } + return true + } + } + + function triggerElectric(cm, inserted) { + // When an 'electric' character is inserted, immediately trigger a reindent + if (!cm.options.electricChars || !cm.options.smartIndent) { return } + var sel = cm.doc.sel; + + for (var i = sel.ranges.length - 1; i >= 0; i--) { + var range = sel.ranges[i]; + if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) { continue } + var mode = cm.getModeAt(range.head); + var indented = false; + if (mode.electricChars) { + for (var j = 0; j < mode.electricChars.length; j++) + { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) { + indented = indentLine(cm, range.head.line, "smart"); + break + } } + } else if (mode.electricInput) { + if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch))) + { indented = indentLine(cm, range.head.line, "smart"); } + } + if (indented) { signalLater(cm, "electricInput", cm, range.head.line); } + } + } + + function copyableRanges(cm) { + var text = [], ranges = []; + for (var i = 0; i < cm.doc.sel.ranges.length; i++) { + var line = cm.doc.sel.ranges[i].head.line; + var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}; + ranges.push(lineRange); + text.push(cm.getRange(lineRange.anchor, lineRange.head)); + } + return {text: text, ranges: ranges} + } + + function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) { + field.setAttribute("autocorrect", autocorrect ? "" : "off"); + field.setAttribute("autocapitalize", autocapitalize ? "" : "off"); + field.setAttribute("spellcheck", !!spellcheck); + } + + function hiddenTextarea() { + var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; min-height: 1em; outline: none"); + var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); + // The textarea is kept positioned near the cursor to prevent the + // fact that it'll be scrolled into view on input from scrolling + // our fake cursor out of view. On webkit, when wrap=off, paste is + // very slow. So make the area wide instead. + if (webkit) { te.style.width = "1000px"; } + else { te.setAttribute("wrap", "off"); } + // If border: 0; -- iOS fails to open keyboard (issue #1287) + if (ios) { te.style.border = "1px solid black"; } + disableBrowserMagic(te); + return div + } + + // The publicly visible API. Note that methodOp(f) means + // 'wrap f in an operation, performed on its `this` parameter'. + + // This is not the complete set of editor methods. Most of the + // methods defined on the Doc type are also injected into + // CodeMirror.prototype, for backwards compatibility and + // convenience. + + function addEditorMethods(CodeMirror) { + var optionHandlers = CodeMirror.optionHandlers; + + var helpers = CodeMirror.helpers = {}; + + CodeMirror.prototype = { + constructor: CodeMirror, + focus: function(){win(this).focus(); this.display.input.focus();}, + + setOption: function(option, value) { + var options = this.options, old = options[option]; + if (options[option] == value && option != "mode") { return } + options[option] = value; + if (optionHandlers.hasOwnProperty(option)) + { operation(this, optionHandlers[option])(this, value, old); } + signal(this, "optionChange", this, option); + }, + + getOption: function(option) {return this.options[option]}, + getDoc: function() {return this.doc}, + + addKeyMap: function(map, bottom) { + this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map)); + }, + removeKeyMap: function(map) { + var maps = this.state.keyMaps; + for (var i = 0; i < maps.length; ++i) + { if (maps[i] == map || maps[i].name == map) { + maps.splice(i, 1); + return true + } } + }, + + addOverlay: methodOp(function(spec, options) { + var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec); + if (mode.startState) { throw new Error("Overlays may not be stateful.") } + insertSorted(this.state.overlays, + {mode: mode, modeSpec: spec, opaque: options && options.opaque, + priority: (options && options.priority) || 0}, + function (overlay) { return overlay.priority; }); + this.state.modeGen++; + regChange(this); + }), + removeOverlay: methodOp(function(spec) { + var overlays = this.state.overlays; + for (var i = 0; i < overlays.length; ++i) { + var cur = overlays[i].modeSpec; + if (cur == spec || typeof spec == "string" && cur.name == spec) { + overlays.splice(i, 1); + this.state.modeGen++; + regChange(this); + return + } + } + }), + + indentLine: methodOp(function(n, dir, aggressive) { + if (typeof dir != "string" && typeof dir != "number") { + if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; } + else { dir = dir ? "add" : "subtract"; } + } + if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); } + }), + indentSelection: methodOp(function(how) { + var ranges = this.doc.sel.ranges, end = -1; + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i]; + if (!range.empty()) { + var from = range.from(), to = range.to(); + var start = Math.max(end, from.line); + end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; + for (var j = start; j < end; ++j) + { indentLine(this, j, how); } + var newRanges = this.doc.sel.ranges; + if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0) + { replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); } + } else if (range.head.line > end) { + indentLine(this, range.head.line, how, true); + end = range.head.line; + if (i == this.doc.sel.primIndex) { ensureCursorVisible(this); } + } + } + }), + + // Fetch the parser token for a given character. Useful for hacks + // that want to inspect the mode state (say, for completion). + getTokenAt: function(pos, precise) { + return takeToken(this, pos, precise) + }, + + getLineTokens: function(line, precise) { + return takeToken(this, Pos(line), precise, true) + }, + + getTokenTypeAt: function(pos) { + pos = clipPos(this.doc, pos); + var styles = getLineStyles(this, getLine(this.doc, pos.line)); + var before = 0, after = (styles.length - 1) / 2, ch = pos.ch; + var type; + if (ch == 0) { type = styles[2]; } + else { for (;;) { + var mid = (before + after) >> 1; + if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; } + else if (styles[mid * 2 + 1] < ch) { before = mid + 1; } + else { type = styles[mid * 2 + 2]; break } + } } + var cut = type ? type.indexOf("overlay ") : -1; + return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1) + }, + + getModeAt: function(pos) { + var mode = this.doc.mode; + if (!mode.innerMode) { return mode } + return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode + }, + + getHelper: function(pos, type) { + return this.getHelpers(pos, type)[0] + }, + + getHelpers: function(pos, type) { + var found = []; + if (!helpers.hasOwnProperty(type)) { return found } + var help = helpers[type], mode = this.getModeAt(pos); + if (typeof mode[type] == "string") { + if (help[mode[type]]) { found.push(help[mode[type]]); } + } else if (mode[type]) { + for (var i = 0; i < mode[type].length; i++) { + var val = help[mode[type][i]]; + if (val) { found.push(val); } + } + } else if (mode.helperType && help[mode.helperType]) { + found.push(help[mode.helperType]); + } else if (help[mode.name]) { + found.push(help[mode.name]); + } + for (var i$1 = 0; i$1 < help._global.length; i$1++) { + var cur = help._global[i$1]; + if (cur.pred(mode, this) && indexOf(found, cur.val) == -1) + { found.push(cur.val); } + } + return found + }, + + getStateAfter: function(line, precise) { + var doc = this.doc; + line = clipLine(doc, line == null ? doc.first + doc.size - 1: line); + return getContextBefore(this, line + 1, precise).state + }, + + cursorCoords: function(start, mode) { + var pos, range = this.doc.sel.primary(); + if (start == null) { pos = range.head; } + else if (typeof start == "object") { pos = clipPos(this.doc, start); } + else { pos = start ? range.from() : range.to(); } + return cursorCoords(this, pos, mode || "page") + }, + + charCoords: function(pos, mode) { + return charCoords(this, clipPos(this.doc, pos), mode || "page") + }, + + coordsChar: function(coords, mode) { + coords = fromCoordSystem(this, coords, mode || "page"); + return coordsChar(this, coords.left, coords.top) + }, + + lineAtHeight: function(height, mode) { + height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top; + return lineAtHeight(this.doc, height + this.display.viewOffset) + }, + heightAtLine: function(line, mode, includeWidgets) { + var end = false, lineObj; + if (typeof line == "number") { + var last = this.doc.first + this.doc.size - 1; + if (line < this.doc.first) { line = this.doc.first; } + else if (line > last) { line = last; end = true; } + lineObj = getLine(this.doc, line); + } else { + lineObj = line; + } + return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top + + (end ? this.doc.height - heightAtLine(lineObj) : 0) + }, + + defaultTextHeight: function() { return textHeight(this.display) }, + defaultCharWidth: function() { return charWidth(this.display) }, + + getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}}, + + addWidget: function(pos, node, scroll, vert, horiz) { + var display = this.display; + pos = cursorCoords(this, clipPos(this.doc, pos)); + var top = pos.bottom, left = pos.left; + node.style.position = "absolute"; + node.setAttribute("cm-ignore-events", "true"); + this.display.input.setUneditable(node); + display.sizer.appendChild(node); + if (vert == "over") { + top = pos.top; + } else if (vert == "above" || vert == "near") { + var vspace = Math.max(display.wrapper.clientHeight, this.doc.height), + hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth); + // Default to positioning above (if specified and possible); otherwise default to positioning below + if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight) + { top = pos.top - node.offsetHeight; } + else if (pos.bottom + node.offsetHeight <= vspace) + { top = pos.bottom; } + if (left + node.offsetWidth > hspace) + { left = hspace - node.offsetWidth; } + } + node.style.top = top + "px"; + node.style.left = node.style.right = ""; + if (horiz == "right") { + left = display.sizer.clientWidth - node.offsetWidth; + node.style.right = "0px"; + } else { + if (horiz == "left") { left = 0; } + else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; } + node.style.left = left + "px"; + } + if (scroll) + { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); } + }, + + triggerOnKeyDown: methodOp(onKeyDown), + triggerOnKeyPress: methodOp(onKeyPress), + triggerOnKeyUp: onKeyUp, + triggerOnMouseDown: methodOp(onMouseDown), + + execCommand: function(cmd) { + if (commands.hasOwnProperty(cmd)) + { return commands[cmd].call(null, this) } + }, + + triggerElectric: methodOp(function(text) { triggerElectric(this, text); }), + + findPosH: function(from, amount, unit, visually) { + var dir = 1; + if (amount < 0) { dir = -1; amount = -amount; } + var cur = clipPos(this.doc, from); + for (var i = 0; i < amount; ++i) { + cur = findPosH(this.doc, cur, dir, unit, visually); + if (cur.hitSide) { break } + } + return cur + }, + + moveH: methodOp(function(dir, unit) { + var this$1 = this; + + this.extendSelectionsBy(function (range) { + if (this$1.display.shift || this$1.doc.extend || range.empty()) + { return findPosH(this$1.doc, range.head, dir, unit, this$1.options.rtlMoveVisually) } + else + { return dir < 0 ? range.from() : range.to() } + }, sel_move); + }), + + deleteH: methodOp(function(dir, unit) { + var sel = this.doc.sel, doc = this.doc; + if (sel.somethingSelected()) + { doc.replaceSelection("", null, "+delete"); } + else + { deleteNearSelection(this, function (range) { + var other = findPosH(doc, range.head, dir, unit, false); + return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other} + }); } + }), + + findPosV: function(from, amount, unit, goalColumn) { + var dir = 1, x = goalColumn; + if (amount < 0) { dir = -1; amount = -amount; } + var cur = clipPos(this.doc, from); + for (var i = 0; i < amount; ++i) { + var coords = cursorCoords(this, cur, "div"); + if (x == null) { x = coords.left; } + else { coords.left = x; } + cur = findPosV(this, coords, dir, unit); + if (cur.hitSide) { break } + } + return cur + }, + + moveV: methodOp(function(dir, unit) { + var this$1 = this; + + var doc = this.doc, goals = []; + var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected(); + doc.extendSelectionsBy(function (range) { + if (collapse) + { return dir < 0 ? range.from() : range.to() } + var headPos = cursorCoords(this$1, range.head, "div"); + if (range.goalColumn != null) { headPos.left = range.goalColumn; } + goals.push(headPos.left); + var pos = findPosV(this$1, headPos, dir, unit); + if (unit == "page" && range == doc.sel.primary()) + { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); } + return pos + }, sel_move); + if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++) + { doc.sel.ranges[i].goalColumn = goals[i]; } } + }), + + // Find the word at the given position (as returned by coordsChar). + findWordAt: function(pos) { + var doc = this.doc, line = getLine(doc, pos.line).text; + var start = pos.ch, end = pos.ch; + if (line) { + var helper = this.getHelper(pos, "wordChars"); + if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; } + var startChar = line.charAt(start); + var check = isWordChar(startChar, helper) + ? function (ch) { return isWordChar(ch, helper); } + : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); } + : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); }; + while (start > 0 && check(line.charAt(start - 1))) { --start; } + while (end < line.length && check(line.charAt(end))) { ++end; } + } + return new Range(Pos(pos.line, start), Pos(pos.line, end)) + }, + + toggleOverwrite: function(value) { + if (value != null && value == this.state.overwrite) { return } + if (this.state.overwrite = !this.state.overwrite) + { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); } + else + { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); } + + signal(this, "overwriteToggle", this, this.state.overwrite); + }, + hasFocus: function() { return this.display.input.getField() == activeElt(doc(this)) }, + isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) }, + + scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }), + getScrollInfo: function() { + var scroller = this.display.scroller; + return {left: scroller.scrollLeft, top: scroller.scrollTop, + height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight, + width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth, + clientHeight: displayHeight(this), clientWidth: displayWidth(this)} + }, + + scrollIntoView: methodOp(function(range, margin) { + if (range == null) { + range = {from: this.doc.sel.primary().head, to: null}; + if (margin == null) { margin = this.options.cursorScrollMargin; } + } else if (typeof range == "number") { + range = {from: Pos(range, 0), to: null}; + } else if (range.from == null) { + range = {from: range, to: null}; + } + if (!range.to) { range.to = range.from; } + range.margin = margin || 0; + + if (range.from.line != null) { + scrollToRange(this, range); + } else { + scrollToCoordsRange(this, range.from, range.to, range.margin); + } + }), + + setSize: methodOp(function(width, height) { + var this$1 = this; + + var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; }; + if (width != null) { this.display.wrapper.style.width = interpret(width); } + if (height != null) { this.display.wrapper.style.height = interpret(height); } + if (this.options.lineWrapping) { clearLineMeasurementCache(this); } + var lineNo = this.display.viewFrom; + this.doc.iter(lineNo, this.display.viewTo, function (line) { + if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) + { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo, "widget"); break } } } + ++lineNo; + }); + this.curOp.forceUpdate = true; + signal(this, "refresh", this); + }), + + operation: function(f){return runInOp(this, f)}, + startOperation: function(){return startOperation(this)}, + endOperation: function(){return endOperation(this)}, + + refresh: methodOp(function() { + var oldHeight = this.display.cachedTextHeight; + regChange(this); + this.curOp.forceUpdate = true; + clearCaches(this); + scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop); + updateGutterSpace(this.display); + if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5 || this.options.lineWrapping) + { estimateLineHeights(this); } + signal(this, "refresh", this); + }), + + swapDoc: methodOp(function(doc) { + var old = this.doc; + old.cm = null; + // Cancel the current text selection if any (#5821) + if (this.state.selectingText) { this.state.selectingText(); } + attachDoc(this, doc); + clearCaches(this); + this.display.input.reset(); + scrollToCoords(this, doc.scrollLeft, doc.scrollTop); + this.curOp.forceScroll = true; + signalLater(this, "swapDoc", this, old); + return old + }), + + phrase: function(phraseText) { + var phrases = this.options.phrases; + return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText + }, + + getInputField: function(){return this.display.input.getField()}, + getWrapperElement: function(){return this.display.wrapper}, + getScrollerElement: function(){return this.display.scroller}, + getGutterElement: function(){return this.display.gutters} + }; + eventMixin(CodeMirror); + + CodeMirror.registerHelper = function(type, name, value) { + if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; } + helpers[type][name] = value; + }; + CodeMirror.registerGlobalHelper = function(type, name, predicate, value) { + CodeMirror.registerHelper(type, name, value); + helpers[type]._global.push({pred: predicate, val: value}); + }; + } + + // Used for horizontal relative motion. Dir is -1 or 1 (left or + // right), unit can be "codepoint", "char", "column" (like char, but + // doesn't cross line boundaries), "word" (across next word), or + // "group" (to the start of next group of word or + // non-word-non-whitespace chars). The visually param controls + // whether, in right-to-left text, direction 1 means to move towards + // the next index in the string, or towards the character to the right + // of the current position. The resulting position will have a + // hitSide=true property if it reached the end of the document. + function findPosH(doc, pos, dir, unit, visually) { + var oldPos = pos; + var origDir = dir; + var lineObj = getLine(doc, pos.line); + var lineDir = visually && doc.direction == "rtl" ? -dir : dir; + function findNextLine() { + var l = pos.line + lineDir; + if (l < doc.first || l >= doc.first + doc.size) { return false } + pos = new Pos(l, pos.ch, pos.sticky); + return lineObj = getLine(doc, l) + } + function moveOnce(boundToLine) { + var next; + if (unit == "codepoint") { + var ch = lineObj.text.charCodeAt(pos.ch + (dir > 0 ? 0 : -1)); + if (isNaN(ch)) { + next = null; + } else { + var astral = dir > 0 ? ch >= 0xD800 && ch < 0xDC00 : ch >= 0xDC00 && ch < 0xDFFF; + next = new Pos(pos.line, Math.max(0, Math.min(lineObj.text.length, pos.ch + dir * (astral ? 2 : 1))), -dir); + } + } else if (visually) { + next = moveVisually(doc.cm, lineObj, pos, dir); + } else { + next = moveLogically(lineObj, pos, dir); + } + if (next == null) { + if (!boundToLine && findNextLine()) + { pos = endOfLine(visually, doc.cm, lineObj, pos.line, lineDir); } + else + { return false } + } else { + pos = next; + } + return true + } + + if (unit == "char" || unit == "codepoint") { + moveOnce(); + } else if (unit == "column") { + moveOnce(true); + } else if (unit == "word" || unit == "group") { + var sawType = null, group = unit == "group"; + var helper = doc.cm && doc.cm.getHelper(pos, "wordChars"); + for (var first = true;; first = false) { + if (dir < 0 && !moveOnce(!first)) { break } + var cur = lineObj.text.charAt(pos.ch) || "\n"; + var type = isWordChar(cur, helper) ? "w" + : group && cur == "\n" ? "n" + : !group || /\s/.test(cur) ? null + : "p"; + if (group && !first && !type) { type = "s"; } + if (sawType && sawType != type) { + if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";} + break + } + + if (type) { sawType = type; } + if (dir > 0 && !moveOnce(!first)) { break } + } + } + var result = skipAtomic(doc, pos, oldPos, origDir, true); + if (equalCursorPos(oldPos, result)) { result.hitSide = true; } + return result + } + + // For relative vertical movement. Dir may be -1 or 1. Unit can be + // "page" or "line". The resulting position will have a hitSide=true + // property if it reached the end of the document. + function findPosV(cm, pos, dir, unit) { + var doc = cm.doc, x = pos.left, y; + if (unit == "page") { + var pageSize = Math.min(cm.display.wrapper.clientHeight, win(cm).innerHeight || doc(cm).documentElement.clientHeight); + var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3); + y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount; + } else if (unit == "line") { + y = dir > 0 ? pos.bottom + 3 : pos.top - 3; + } + var target; + for (;;) { + target = coordsChar(cm, x, y); + if (!target.outside) { break } + if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break } + y += dir * 5; + } + return target + } + + // CONTENTEDITABLE INPUT STYLE + + var ContentEditableInput = function(cm) { + this.cm = cm; + this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null; + this.polling = new Delayed(); + this.composing = null; + this.gracePeriod = false; + this.readDOMTimeout = null; + }; + + ContentEditableInput.prototype.init = function (display) { + var this$1 = this; + + var input = this, cm = input.cm; + var div = input.div = display.lineDiv; + div.contentEditable = true; + disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize); + + function belongsToInput(e) { + for (var t = e.target; t; t = t.parentNode) { + if (t == div) { return true } + if (/\bCodeMirror-(?:line)?widget\b/.test(t.className)) { break } + } + return false + } + + on(div, "paste", function (e) { + if (!belongsToInput(e) || signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } + // IE doesn't fire input events, so we schedule a read for the pasted content in this way + if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); } + }); + + on(div, "compositionstart", function (e) { + this$1.composing = {data: e.data, done: false}; + }); + on(div, "compositionupdate", function (e) { + if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; } + }); + on(div, "compositionend", function (e) { + if (this$1.composing) { + if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); } + this$1.composing.done = true; + } + }); + + on(div, "touchstart", function () { return input.forceCompositionEnd(); }); + + on(div, "input", function () { + if (!this$1.composing) { this$1.readFromDOMSoon(); } + }); + + function onCopyCut(e) { + if (!belongsToInput(e) || signalDOMEvent(cm, e)) { return } + if (cm.somethingSelected()) { + setLastCopied({lineWise: false, text: cm.getSelections()}); + if (e.type == "cut") { cm.replaceSelection("", null, "cut"); } + } else if (!cm.options.lineWiseCopyCut) { + return + } else { + var ranges = copyableRanges(cm); + setLastCopied({lineWise: true, text: ranges.text}); + if (e.type == "cut") { + cm.operation(function () { + cm.setSelections(ranges.ranges, 0, sel_dontScroll); + cm.replaceSelection("", null, "cut"); + }); + } + } + if (e.clipboardData) { + e.clipboardData.clearData(); + var content = lastCopied.text.join("\n"); + // iOS exposes the clipboard API, but seems to discard content inserted into it + e.clipboardData.setData("Text", content); + if (e.clipboardData.getData("Text") == content) { + e.preventDefault(); + return + } + } + // Old-fashioned briefly-focus-a-textarea hack + var kludge = hiddenTextarea(), te = kludge.firstChild; + cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild); + te.value = lastCopied.text.join("\n"); + var hadFocus = activeElt(div.ownerDocument); + selectInput(te); + setTimeout(function () { + cm.display.lineSpace.removeChild(kludge); + hadFocus.focus(); + if (hadFocus == div) { input.showPrimarySelection(); } + }, 50); + } + on(div, "copy", onCopyCut); + on(div, "cut", onCopyCut); + }; + + ContentEditableInput.prototype.screenReaderLabelChanged = function (label) { + // Label for screenreaders, accessibility + if(label) { + this.div.setAttribute('aria-label', label); + } else { + this.div.removeAttribute('aria-label'); + } + }; + + ContentEditableInput.prototype.prepareSelection = function () { + var result = prepareSelection(this.cm, false); + result.focus = activeElt(this.div.ownerDocument) == this.div; + return result + }; + + ContentEditableInput.prototype.showSelection = function (info, takeFocus) { + if (!info || !this.cm.display.view.length) { return } + if (info.focus || takeFocus) { this.showPrimarySelection(); } + this.showMultipleSelections(info); + }; + + ContentEditableInput.prototype.getSelection = function () { + return this.cm.display.wrapper.ownerDocument.getSelection() + }; + + ContentEditableInput.prototype.showPrimarySelection = function () { + var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary(); + var from = prim.from(), to = prim.to(); + + if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) { + sel.removeAllRanges(); + return + } + + var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); + var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset); + if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad && + cmp(minPos(curAnchor, curFocus), from) == 0 && + cmp(maxPos(curAnchor, curFocus), to) == 0) + { return } + + var view = cm.display.view; + var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) || + {node: view[0].measure.map[2], offset: 0}; + var end = to.line < cm.display.viewTo && posToDOM(cm, to); + if (!end) { + var measure = view[view.length - 1].measure; + var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map; + end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]}; + } + + if (!start || !end) { + sel.removeAllRanges(); + return + } + + var old = sel.rangeCount && sel.getRangeAt(0), rng; + try { rng = range(start.node, start.offset, end.offset, end.node); } + catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible + if (rng) { + if (!gecko && cm.state.focused) { + sel.collapse(start.node, start.offset); + if (!rng.collapsed) { + sel.removeAllRanges(); + sel.addRange(rng); + } + } else { + sel.removeAllRanges(); + sel.addRange(rng); + } + if (old && sel.anchorNode == null) { sel.addRange(old); } + else if (gecko) { this.startGracePeriod(); } + } + this.rememberSelection(); + }; + + ContentEditableInput.prototype.startGracePeriod = function () { + var this$1 = this; + + clearTimeout(this.gracePeriod); + this.gracePeriod = setTimeout(function () { + this$1.gracePeriod = false; + if (this$1.selectionChanged()) + { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); } + }, 20); + }; + + ContentEditableInput.prototype.showMultipleSelections = function (info) { + removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors); + removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection); + }; + + ContentEditableInput.prototype.rememberSelection = function () { + var sel = this.getSelection(); + this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset; + this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset; + }; + + ContentEditableInput.prototype.selectionInEditor = function () { + var sel = this.getSelection(); + if (!sel.rangeCount) { return false } + var node = sel.getRangeAt(0).commonAncestorContainer; + return contains(this.div, node) + }; + + ContentEditableInput.prototype.focus = function () { + if (this.cm.options.readOnly != "nocursor") { + if (!this.selectionInEditor() || activeElt(this.div.ownerDocument) != this.div) + { this.showSelection(this.prepareSelection(), true); } + this.div.focus(); + } + }; + ContentEditableInput.prototype.blur = function () { this.div.blur(); }; + ContentEditableInput.prototype.getField = function () { return this.div }; + + ContentEditableInput.prototype.supportsTouch = function () { return true }; + + ContentEditableInput.prototype.receivedFocus = function () { + var this$1 = this; + + var input = this; + if (this.selectionInEditor()) + { setTimeout(function () { return this$1.pollSelection(); }, 20); } + else + { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); } + + function poll() { + if (input.cm.state.focused) { + input.pollSelection(); + input.polling.set(input.cm.options.pollInterval, poll); + } + } + this.polling.set(this.cm.options.pollInterval, poll); + }; + + ContentEditableInput.prototype.selectionChanged = function () { + var sel = this.getSelection(); + return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset || + sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset + }; + + ContentEditableInput.prototype.pollSelection = function () { + if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return } + var sel = this.getSelection(), cm = this.cm; + // On Android Chrome (version 56, at least), backspacing into an + // uneditable block element will put the cursor in that element, + // and then, because it's not editable, hide the virtual keyboard. + // Because Android doesn't allow us to actually detect backspace + // presses in a sane way, this code checks for when that happens + // and simulates a backspace press in this case. + if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) { + this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs}); + this.blur(); + this.focus(); + return + } + if (this.composing) { return } + this.rememberSelection(); + var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); + var head = domToPos(cm, sel.focusNode, sel.focusOffset); + if (anchor && head) { runInOp(cm, function () { + setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll); + if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; } + }); } + }; + + ContentEditableInput.prototype.pollContent = function () { + if (this.readDOMTimeout != null) { + clearTimeout(this.readDOMTimeout); + this.readDOMTimeout = null; + } + + var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary(); + var from = sel.from(), to = sel.to(); + if (from.ch == 0 && from.line > cm.firstLine()) + { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); } + if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine()) + { to = Pos(to.line + 1, 0); } + if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false } + + var fromIndex, fromLine, fromNode; + if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) { + fromLine = lineNo(display.view[0].line); + fromNode = display.view[0].node; + } else { + fromLine = lineNo(display.view[fromIndex].line); + fromNode = display.view[fromIndex - 1].node.nextSibling; + } + var toIndex = findViewIndex(cm, to.line); + var toLine, toNode; + if (toIndex == display.view.length - 1) { + toLine = display.viewTo - 1; + toNode = display.lineDiv.lastChild; + } else { + toLine = lineNo(display.view[toIndex + 1].line) - 1; + toNode = display.view[toIndex + 1].node.previousSibling; + } + + if (!fromNode) { return false } + var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine)); + var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length)); + while (newText.length > 1 && oldText.length > 1) { + if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; } + else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; } + else { break } + } + + var cutFront = 0, cutEnd = 0; + var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length); + while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront)) + { ++cutFront; } + var newBot = lst(newText), oldBot = lst(oldText); + var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0), + oldBot.length - (oldText.length == 1 ? cutFront : 0)); + while (cutEnd < maxCutEnd && + newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) + { ++cutEnd; } + // Try to move start of change to start of selection if ambiguous + if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) { + while (cutFront && cutFront > from.ch && + newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) { + cutFront--; + cutEnd++; + } + } + + newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, ""); + newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, ""); + + var chFrom = Pos(fromLine, cutFront); + var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0); + if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) { + replaceRange(cm.doc, newText, chFrom, chTo, "+input"); + return true + } + }; + + ContentEditableInput.prototype.ensurePolled = function () { + this.forceCompositionEnd(); + }; + ContentEditableInput.prototype.reset = function () { + this.forceCompositionEnd(); + }; + ContentEditableInput.prototype.forceCompositionEnd = function () { + if (!this.composing) { return } + clearTimeout(this.readDOMTimeout); + this.composing = null; + this.updateFromDOM(); + this.div.blur(); + this.div.focus(); + }; + ContentEditableInput.prototype.readFromDOMSoon = function () { + var this$1 = this; + + if (this.readDOMTimeout != null) { return } + this.readDOMTimeout = setTimeout(function () { + this$1.readDOMTimeout = null; + if (this$1.composing) { + if (this$1.composing.done) { this$1.composing = null; } + else { return } + } + this$1.updateFromDOM(); + }, 80); + }; + + ContentEditableInput.prototype.updateFromDOM = function () { + var this$1 = this; + + if (this.cm.isReadOnly() || !this.pollContent()) + { runInOp(this.cm, function () { return regChange(this$1.cm); }); } + }; + + ContentEditableInput.prototype.setUneditable = function (node) { + node.contentEditable = "false"; + }; + + ContentEditableInput.prototype.onKeyPress = function (e) { + if (e.charCode == 0 || this.composing) { return } + e.preventDefault(); + if (!this.cm.isReadOnly()) + { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); } + }; + + ContentEditableInput.prototype.readOnlyChanged = function (val) { + this.div.contentEditable = String(val != "nocursor"); + }; + + ContentEditableInput.prototype.onContextMenu = function () {}; + ContentEditableInput.prototype.resetPosition = function () {}; + + ContentEditableInput.prototype.needsContentAttribute = true; + + function posToDOM(cm, pos) { + var view = findViewForLine(cm, pos.line); + if (!view || view.hidden) { return null } + var line = getLine(cm.doc, pos.line); + var info = mapFromLineView(view, line, pos.line); + + var order = getOrder(line, cm.doc.direction), side = "left"; + if (order) { + var partPos = getBidiPartAt(order, pos.ch); + side = partPos % 2 ? "right" : "left"; + } + var result = nodeAndOffsetInLineMap(info.map, pos.ch, side); + result.offset = result.collapse == "right" ? result.end : result.start; + return result + } + + function isInGutter(node) { + for (var scan = node; scan; scan = scan.parentNode) + { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } } + return false + } + + function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos } + + function domTextBetween(cm, from, to, fromLine, toLine) { + var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false; + function recognizeMarker(id) { return function (marker) { return marker.id == id; } } + function close() { + if (closing) { + text += lineSep; + if (extraLinebreak) { text += lineSep; } + closing = extraLinebreak = false; + } + } + function addText(str) { + if (str) { + close(); + text += str; + } + } + function walk(node) { + if (node.nodeType == 1) { + var cmText = node.getAttribute("cm-text"); + if (cmText) { + addText(cmText); + return + } + var markerID = node.getAttribute("cm-marker"), range; + if (markerID) { + var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID)); + if (found.length && (range = found[0].find(0))) + { addText(getBetween(cm.doc, range.from, range.to).join(lineSep)); } + return + } + if (node.getAttribute("contenteditable") == "false") { return } + var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName); + if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return } + + if (isBlock) { close(); } + for (var i = 0; i < node.childNodes.length; i++) + { walk(node.childNodes[i]); } + + if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; } + if (isBlock) { closing = true; } + } else if (node.nodeType == 3) { + addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " ")); + } + } + for (;;) { + walk(from); + if (from == to) { break } + from = from.nextSibling; + extraLinebreak = false; + } + return text + } + + function domToPos(cm, node, offset) { + var lineNode; + if (node == cm.display.lineDiv) { + lineNode = cm.display.lineDiv.childNodes[offset]; + if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) } + node = null; offset = 0; + } else { + for (lineNode = node;; lineNode = lineNode.parentNode) { + if (!lineNode || lineNode == cm.display.lineDiv) { return null } + if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break } + } + } + for (var i = 0; i < cm.display.view.length; i++) { + var lineView = cm.display.view[i]; + if (lineView.node == lineNode) + { return locateNodeInLineView(lineView, node, offset) } + } + } + + function locateNodeInLineView(lineView, node, offset) { + var wrapper = lineView.text.firstChild, bad = false; + if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) } + if (node == wrapper) { + bad = true; + node = wrapper.childNodes[offset]; + offset = 0; + if (!node) { + var line = lineView.rest ? lst(lineView.rest) : lineView.line; + return badPos(Pos(lineNo(line), line.text.length), bad) + } + } + + var textNode = node.nodeType == 3 ? node : null, topNode = node; + if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) { + textNode = node.firstChild; + if (offset) { offset = textNode.nodeValue.length; } + } + while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; } + var measure = lineView.measure, maps = measure.maps; + + function find(textNode, topNode, offset) { + for (var i = -1; i < (maps ? maps.length : 0); i++) { + var map = i < 0 ? measure.map : maps[i]; + for (var j = 0; j < map.length; j += 3) { + var curNode = map[j + 2]; + if (curNode == textNode || curNode == topNode) { + var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]); + var ch = map[j] + offset; + if (offset < 0 || curNode != textNode) { ch = map[j + (offset ? 1 : 0)]; } + return Pos(line, ch) + } + } + } + } + var found = find(textNode, topNode, offset); + if (found) { return badPos(found, bad) } + + // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems + for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) { + found = find(after, after.firstChild, 0); + if (found) + { return badPos(Pos(found.line, found.ch - dist), bad) } + else + { dist += after.textContent.length; } + } + for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) { + found = find(before, before.firstChild, -1); + if (found) + { return badPos(Pos(found.line, found.ch + dist$1), bad) } + else + { dist$1 += before.textContent.length; } + } + } + + // TEXTAREA INPUT STYLE + + var TextareaInput = function(cm) { + this.cm = cm; + // See input.poll and input.reset + this.prevInput = ""; + + // Flag that indicates whether we expect input to appear real soon + // now (after some event like 'keypress' or 'input') and are + // polling intensively. + this.pollingFast = false; + // Self-resetting timeout for the poller + this.polling = new Delayed(); + // Used to work around IE issue with selection being forgotten when focus moves away from textarea + this.hasSelection = false; + this.composing = null; + this.resetting = false; + }; + + TextareaInput.prototype.init = function (display) { + var this$1 = this; + + var input = this, cm = this.cm; + this.createField(display); + var te = this.textarea; + + display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild); + + // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore) + if (ios) { te.style.width = "0px"; } + + on(te, "input", function () { + if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; } + input.poll(); + }); + + on(te, "paste", function (e) { + if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } + + cm.state.pasteIncoming = +new Date; + input.fastPoll(); + }); + + function prepareCopyCut(e) { + if (signalDOMEvent(cm, e)) { return } + if (cm.somethingSelected()) { + setLastCopied({lineWise: false, text: cm.getSelections()}); + } else if (!cm.options.lineWiseCopyCut) { + return + } else { + var ranges = copyableRanges(cm); + setLastCopied({lineWise: true, text: ranges.text}); + if (e.type == "cut") { + cm.setSelections(ranges.ranges, null, sel_dontScroll); + } else { + input.prevInput = ""; + te.value = ranges.text.join("\n"); + selectInput(te); + } + } + if (e.type == "cut") { cm.state.cutIncoming = +new Date; } + } + on(te, "cut", prepareCopyCut); + on(te, "copy", prepareCopyCut); + + on(display.scroller, "paste", function (e) { + if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return } + if (!te.dispatchEvent) { + cm.state.pasteIncoming = +new Date; + input.focus(); + return + } + + // Pass the `paste` event to the textarea so it's handled by its event listener. + var event = new Event("paste"); + event.clipboardData = e.clipboardData; + te.dispatchEvent(event); + }); + + // Prevent normal selection in the editor (we handle our own) + on(display.lineSpace, "selectstart", function (e) { + if (!eventInWidget(display, e)) { e_preventDefault(e); } + }); + + on(te, "compositionstart", function () { + var start = cm.getCursor("from"); + if (input.composing) { input.composing.range.clear(); } + input.composing = { + start: start, + range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"}) + }; + }); + on(te, "compositionend", function () { + if (input.composing) { + input.poll(); + input.composing.range.clear(); + input.composing = null; + } + }); + }; + + TextareaInput.prototype.createField = function (_display) { + // Wraps and hides input textarea + this.wrapper = hiddenTextarea(); + // The semihidden textarea that is focused when the editor is + // focused, and receives input. + this.textarea = this.wrapper.firstChild; + }; + + TextareaInput.prototype.screenReaderLabelChanged = function (label) { + // Label for screenreaders, accessibility + if(label) { + this.textarea.setAttribute('aria-label', label); + } else { + this.textarea.removeAttribute('aria-label'); + } + }; + + TextareaInput.prototype.prepareSelection = function () { + // Redraw the selection and/or cursor + var cm = this.cm, display = cm.display, doc = cm.doc; + var result = prepareSelection(cm); + + // Move the hidden textarea near the cursor to prevent scrolling artifacts + if (cm.options.moveInputWithCursor) { + var headPos = cursorCoords(cm, doc.sel.primary().head, "div"); + var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect(); + result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10, + headPos.top + lineOff.top - wrapOff.top)); + result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10, + headPos.left + lineOff.left - wrapOff.left)); + } + + return result + }; + + TextareaInput.prototype.showSelection = function (drawn) { + var cm = this.cm, display = cm.display; + removeChildrenAndAdd(display.cursorDiv, drawn.cursors); + removeChildrenAndAdd(display.selectionDiv, drawn.selection); + if (drawn.teTop != null) { + this.wrapper.style.top = drawn.teTop + "px"; + this.wrapper.style.left = drawn.teLeft + "px"; + } + }; + + // Reset the input to correspond to the selection (or to be empty, + // when not typing and nothing is selected) + TextareaInput.prototype.reset = function (typing) { + if (this.contextMenuPending || this.composing && typing) { return } + var cm = this.cm; + this.resetting = true; + if (cm.somethingSelected()) { + this.prevInput = ""; + var content = cm.getSelection(); + this.textarea.value = content; + if (cm.state.focused) { selectInput(this.textarea); } + if (ie && ie_version >= 9) { this.hasSelection = content; } + } else if (!typing) { + this.prevInput = this.textarea.value = ""; + if (ie && ie_version >= 9) { this.hasSelection = null; } + } + this.resetting = false; + }; + + TextareaInput.prototype.getField = function () { return this.textarea }; + + TextareaInput.prototype.supportsTouch = function () { return false }; + + TextareaInput.prototype.focus = function () { + if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt(this.textarea.ownerDocument) != this.textarea)) { + try { this.textarea.focus(); } + catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM + } + }; + + TextareaInput.prototype.blur = function () { this.textarea.blur(); }; + + TextareaInput.prototype.resetPosition = function () { + this.wrapper.style.top = this.wrapper.style.left = 0; + }; + + TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); }; + + // Poll for input changes, using the normal rate of polling. This + // runs as long as the editor is focused. + TextareaInput.prototype.slowPoll = function () { + var this$1 = this; + + if (this.pollingFast) { return } + this.polling.set(this.cm.options.pollInterval, function () { + this$1.poll(); + if (this$1.cm.state.focused) { this$1.slowPoll(); } + }); + }; + + // When an event has just come in that is likely to add or change + // something in the input textarea, we poll faster, to ensure that + // the change appears on the screen quickly. + TextareaInput.prototype.fastPoll = function () { + var missed = false, input = this; + input.pollingFast = true; + function p() { + var changed = input.poll(); + if (!changed && !missed) {missed = true; input.polling.set(60, p);} + else {input.pollingFast = false; input.slowPoll();} + } + input.polling.set(20, p); + }; + + // Read input from the textarea, and update the document to match. + // When something is selected, it is present in the textarea, and + // selected (unless it is huge, in which case a placeholder is + // used). When nothing is selected, the cursor sits after previously + // seen text (can be empty), which is stored in prevInput (we must + // not reset the textarea when typing, because that breaks IME). + TextareaInput.prototype.poll = function () { + var this$1 = this; + + var cm = this.cm, input = this.textarea, prevInput = this.prevInput; + // Since this is called a *lot*, try to bail out as cheaply as + // possible when it is clear that nothing happened. hasSelection + // will be the case when there is a lot of text in the textarea, + // in which case reading its value would be expensive. + if (this.contextMenuPending || this.resetting || !cm.state.focused || + (hasSelection(input) && !prevInput && !this.composing) || + cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq) + { return false } + + var text = input.value; + // If nothing changed, bail. + if (text == prevInput && !cm.somethingSelected()) { return false } + // Work around nonsensical selection resetting in IE9/10, and + // inexplicable appearance of private area unicode characters on + // some key combos in Mac (#2689). + if (ie && ie_version >= 9 && this.hasSelection === text || + mac && /[\uf700-\uf7ff]/.test(text)) { + cm.display.input.reset(); + return false + } + + if (cm.doc.sel == cm.display.selForContextMenu) { + var first = text.charCodeAt(0); + if (first == 0x200b && !prevInput) { prevInput = "\u200b"; } + if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") } + } + // Find the part of the input that is actually new + var same = 0, l = Math.min(prevInput.length, text.length); + while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; } + + runInOp(cm, function () { + applyTextInput(cm, text.slice(same), prevInput.length - same, + null, this$1.composing ? "*compose" : null); + + // Don't leave long text in the textarea, since it makes further polling slow + if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; } + else { this$1.prevInput = text; } + + if (this$1.composing) { + this$1.composing.range.clear(); + this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"), + {className: "CodeMirror-composing"}); + } + }); + return true + }; + + TextareaInput.prototype.ensurePolled = function () { + if (this.pollingFast && this.poll()) { this.pollingFast = false; } + }; + + TextareaInput.prototype.onKeyPress = function () { + if (ie && ie_version >= 9) { this.hasSelection = null; } + this.fastPoll(); + }; + + TextareaInput.prototype.onContextMenu = function (e) { + var input = this, cm = input.cm, display = cm.display, te = input.textarea; + if (input.contextMenuPending) { input.contextMenuPending(); } + var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop; + if (!pos || presto) { return } // Opera is difficult. + + // Reset the current text selection only if the click is done outside of the selection + // and 'resetSelectionOnContextMenu' option is true. + var reset = cm.options.resetSelectionOnContextMenu; + if (reset && cm.doc.sel.contains(pos) == -1) + { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); } + + var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText; + var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect(); + input.wrapper.style.cssText = "position: static"; + te.style.cssText = "position: absolute; width: 30px; height: 30px;\n top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; + var oldScrollY; + if (webkit) { oldScrollY = te.ownerDocument.defaultView.scrollY; } // Work around Chrome issue (#2712) + display.input.focus(); + if (webkit) { te.ownerDocument.defaultView.scrollTo(null, oldScrollY); } + display.input.reset(); + // Adds "Select all" to context menu in FF + if (!cm.somethingSelected()) { te.value = input.prevInput = " "; } + input.contextMenuPending = rehide; + display.selForContextMenu = cm.doc.sel; + clearTimeout(display.detectingSelectAll); + + // Select-all will be greyed out if there's nothing to select, so + // this adds a zero-width space so that we can later check whether + // it got selected. + function prepareSelectAllHack() { + if (te.selectionStart != null) { + var selected = cm.somethingSelected(); + var extval = "\u200b" + (selected ? te.value : ""); + te.value = "\u21da"; // Used to catch context-menu undo + te.value = extval; + input.prevInput = selected ? "" : "\u200b"; + te.selectionStart = 1; te.selectionEnd = extval.length; + // Re-set this, in case some other handler touched the + // selection in the meantime. + display.selForContextMenu = cm.doc.sel; + } + } + function rehide() { + if (input.contextMenuPending != rehide) { return } + input.contextMenuPending = false; + input.wrapper.style.cssText = oldWrapperCSS; + te.style.cssText = oldCSS; + if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); } + + // Try to detect the user choosing select-all + if (te.selectionStart != null) { + if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); } + var i = 0, poll = function () { + if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 && + te.selectionEnd > 0 && input.prevInput == "\u200b") { + operation(cm, selectAll)(cm); + } else if (i++ < 10) { + display.detectingSelectAll = setTimeout(poll, 500); + } else { + display.selForContextMenu = null; + display.input.reset(); + } + }; + display.detectingSelectAll = setTimeout(poll, 200); + } + } + + if (ie && ie_version >= 9) { prepareSelectAllHack(); } + if (captureRightClick) { + e_stop(e); + var mouseup = function () { + off(window, "mouseup", mouseup); + setTimeout(rehide, 20); + }; + on(window, "mouseup", mouseup); + } else { + setTimeout(rehide, 50); + } + }; + + TextareaInput.prototype.readOnlyChanged = function (val) { + if (!val) { this.reset(); } + this.textarea.disabled = val == "nocursor"; + this.textarea.readOnly = !!val; + }; + + TextareaInput.prototype.setUneditable = function () {}; + + TextareaInput.prototype.needsContentAttribute = false; + + function fromTextArea(textarea, options) { + options = options ? copyObj(options) : {}; + options.value = textarea.value; + if (!options.tabindex && textarea.tabIndex) + { options.tabindex = textarea.tabIndex; } + if (!options.placeholder && textarea.placeholder) + { options.placeholder = textarea.placeholder; } + // Set autofocus to true if this textarea is focused, or if it has + // autofocus and no other element is focused. + if (options.autofocus == null) { + var hasFocus = activeElt(textarea.ownerDocument); + options.autofocus = hasFocus == textarea || + textarea.getAttribute("autofocus") != null && hasFocus == document.body; + } + + function save() {textarea.value = cm.getValue();} + + var realSubmit; + if (textarea.form) { + on(textarea.form, "submit", save); + // Deplorable hack to make the submit method do the right thing. + if (!options.leaveSubmitMethodAlone) { + var form = textarea.form; + realSubmit = form.submit; + try { + var wrappedSubmit = form.submit = function () { + save(); + form.submit = realSubmit; + form.submit(); + form.submit = wrappedSubmit; + }; + } catch(e) {} + } + } + + options.finishInit = function (cm) { + cm.save = save; + cm.getTextArea = function () { return textarea; }; + cm.toTextArea = function () { + cm.toTextArea = isNaN; // Prevent this from being ran twice + save(); + textarea.parentNode.removeChild(cm.getWrapperElement()); + textarea.style.display = ""; + if (textarea.form) { + off(textarea.form, "submit", save); + if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == "function") + { textarea.form.submit = realSubmit; } + } + }; + }; + + textarea.style.display = "none"; + var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); }, + options); + return cm + } + + function addLegacyProps(CodeMirror) { + CodeMirror.off = off; + CodeMirror.on = on; + CodeMirror.wheelEventPixels = wheelEventPixels; + CodeMirror.Doc = Doc; + CodeMirror.splitLines = splitLinesAuto; + CodeMirror.countColumn = countColumn; + CodeMirror.findColumn = findColumn; + CodeMirror.isWordChar = isWordCharBasic; + CodeMirror.Pass = Pass; + CodeMirror.signal = signal; + CodeMirror.Line = Line; + CodeMirror.changeEnd = changeEnd; + CodeMirror.scrollbarModel = scrollbarModel; + CodeMirror.Pos = Pos; + CodeMirror.cmpPos = cmp; + CodeMirror.modes = modes; + CodeMirror.mimeModes = mimeModes; + CodeMirror.resolveMode = resolveMode; + CodeMirror.getMode = getMode; + CodeMirror.modeExtensions = modeExtensions; + CodeMirror.extendMode = extendMode; + CodeMirror.copyState = copyState; + CodeMirror.startState = startState; + CodeMirror.innerMode = innerMode; + CodeMirror.commands = commands; + CodeMirror.keyMap = keyMap; + CodeMirror.keyName = keyName; + CodeMirror.isModifierKey = isModifierKey; + CodeMirror.lookupKey = lookupKey; + CodeMirror.normalizeKeyMap = normalizeKeyMap; + CodeMirror.StringStream = StringStream; + CodeMirror.SharedTextMarker = SharedTextMarker; + CodeMirror.TextMarker = TextMarker; + CodeMirror.LineWidget = LineWidget; + CodeMirror.e_preventDefault = e_preventDefault; + CodeMirror.e_stopPropagation = e_stopPropagation; + CodeMirror.e_stop = e_stop; + CodeMirror.addClass = addClass; + CodeMirror.contains = contains; + CodeMirror.rmClass = rmClass; + CodeMirror.keyNames = keyNames; + } + + // EDITOR CONSTRUCTOR + + defineOptions(CodeMirror); + + addEditorMethods(CodeMirror); + + // Set up methods on CodeMirror's prototype to redirect to the editor's document. + var dontDelegate = "iter insert remove copy getEditor constructor".split(" "); + for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0) + { CodeMirror.prototype[prop] = (function(method) { + return function() {return method.apply(this.doc, arguments)} + })(Doc.prototype[prop]); } } + + eventMixin(Doc); + CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}; + + // Extra arguments are stored as the mode's dependencies, which is + // used by (legacy) mechanisms like loadmode.js to automatically + // load a mode. (Preferred mechanism is the require/define calls.) + CodeMirror.defineMode = function(name/*, mode, …*/) { + if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name; } + defineMode.apply(this, arguments); + }; + + CodeMirror.defineMIME = defineMIME; + + // Minimal default mode. + CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); }); + CodeMirror.defineMIME("text/plain", "null"); + + // EXTENSIONS + + CodeMirror.defineExtension = function (name, func) { + CodeMirror.prototype[name] = func; + }; + CodeMirror.defineDocExtension = function (name, func) { + Doc.prototype[name] = func; + }; + + CodeMirror.fromTextArea = fromTextArea; + + addLegacyProps(CodeMirror); + + CodeMirror.version = "5.65.10"; + + return CodeMirror; +}))); + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/addon/edit/matchbrackets.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + var ie_lt8 = /MSIE \d/.test(navigator.userAgent) && + (document.documentMode == null || document.documentMode < 8); + + var Pos = CodeMirror.Pos; + + var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<", "<": ">>", ">": "<<"}; + + function bracketRegex(config) { + return config && config.bracketRegex || /[(){}[\]]/ + } + + function findMatchingBracket(cm, where, config) { + var line = cm.getLineHandle(where.line), pos = where.ch - 1; + var afterCursor = config && config.afterCursor + if (afterCursor == null) + afterCursor = /(^| )cm-fat-cursor($| )/.test(cm.getWrapperElement().className) + var re = bracketRegex(config) + + // A cursor is defined as between two characters, but in in vim command mode + // (i.e. not insert mode), the cursor is visually represented as a + // highlighted box on top of the 2nd character. Otherwise, we allow matches + // from before or after the cursor. + var match = (!afterCursor && pos >= 0 && re.test(line.text.charAt(pos)) && matching[line.text.charAt(pos)]) || + re.test(line.text.charAt(pos + 1)) && matching[line.text.charAt(++pos)]; + if (!match) return null; + var dir = match.charAt(1) == ">" ? 1 : -1; + if (config && config.strict && (dir > 0) != (pos == where.ch)) return null; + var style = cm.getTokenTypeAt(Pos(where.line, pos + 1)); + + var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style, config); + if (found == null) return null; + return {from: Pos(where.line, pos), to: found && found.pos, + match: found && found.ch == match.charAt(0), forward: dir > 0}; + } + + // bracketRegex is used to specify which type of bracket to scan + // should be a regexp, e.g. /[[\]]/ + // + // Note: If "where" is on an open bracket, then this bracket is ignored. + // + // Returns false when no bracket was found, null when it reached + // maxScanLines and gave up + function scanForBracket(cm, where, dir, style, config) { + var maxScanLen = (config && config.maxScanLineLength) || 10000; + var maxScanLines = (config && config.maxScanLines) || 1000; + + var stack = []; + var re = bracketRegex(config) + var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1) + : Math.max(cm.firstLine() - 1, where.line - maxScanLines); + for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) { + var line = cm.getLine(lineNo); + if (!line) continue; + var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1; + if (line.length > maxScanLen) continue; + if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0); + for (; pos != end; pos += dir) { + var ch = line.charAt(pos); + if (re.test(ch) && (style === undefined || + (cm.getTokenTypeAt(Pos(lineNo, pos + 1)) || "") == (style || ""))) { + var match = matching[ch]; + if (match && (match.charAt(1) == ">") == (dir > 0)) stack.push(ch); + else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch}; + else stack.pop(); + } + } + } + return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null; + } + + function matchBrackets(cm, autoclear, config) { + // Disable brace matching in long lines, since it'll cause hugely slow updates + var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000, + highlightNonMatching = config && config.highlightNonMatching; + var marks = [], ranges = cm.listSelections(); + for (var i = 0; i < ranges.length; i++) { + var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, config); + if (match && (match.match || highlightNonMatching !== false) && cm.getLine(match.from.line).length <= maxHighlightLen) { + var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; + marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style})); + if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen) + marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style})); + } + } + + if (marks.length) { + // Kludge to work around the IE bug from issue #1193, where text + // input stops going to the textarea whenever this fires. + if (ie_lt8 && cm.state.focused) cm.focus(); + + var clear = function() { + cm.operation(function() { + for (var i = 0; i < marks.length; i++) marks[i].clear(); + }); + }; + if (autoclear) setTimeout(clear, 800); + else return clear; + } + } + + function doMatchBrackets(cm) { + cm.operation(function() { + if (cm.state.matchBrackets.currentlyHighlighted) { + cm.state.matchBrackets.currentlyHighlighted(); + cm.state.matchBrackets.currentlyHighlighted = null; + } + cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets); + }); + } + + function clearHighlighted(cm) { + if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) { + cm.state.matchBrackets.currentlyHighlighted(); + cm.state.matchBrackets.currentlyHighlighted = null; + } + } + + CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) { + cm.off("cursorActivity", doMatchBrackets); + cm.off("focus", doMatchBrackets) + cm.off("blur", clearHighlighted) + clearHighlighted(cm); + } + if (val) { + cm.state.matchBrackets = typeof val == "object" ? val : {}; + cm.on("cursorActivity", doMatchBrackets); + cm.on("focus", doMatchBrackets) + cm.on("blur", clearHighlighted) + } + }); + + CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);}); + CodeMirror.defineExtension("findMatchingBracket", function(pos, config, oldConfig){ + // Backwards-compatibility kludge + if (oldConfig || typeof config == "boolean") { + if (!oldConfig) { + config = config ? {strict: true} : null + } else { + oldConfig.strict = config + config = oldConfig + } + } + return findMatchingBracket(this, pos, config) + }); + CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){ + return scanForBracket(this, pos, dir, style, config); + }); +}); + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/addon/edit/trailingspace.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) { + if (prev == CodeMirror.Init) prev = false; + if (prev && !val) + cm.removeOverlay("trailingspace"); + else if (!prev && val) + cm.addOverlay({ + token: function(stream) { + for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {} + if (i > stream.pos) { stream.pos = i; return null; } + stream.pos = l; + return "trailingspace"; + }, + name: "trailingspace" + }); + }); +}); + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/addon/lint/lint.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + var GUTTER_ID = "CodeMirror-lint-markers"; + var LINT_LINE_ID = "CodeMirror-lint-line-"; + + function showTooltip(cm, e, content) { + var tt = document.createElement("div"); + tt.className = "CodeMirror-lint-tooltip cm-s-" + cm.options.theme; + tt.appendChild(content.cloneNode(true)); + if (cm.state.lint.options.selfContain) + cm.getWrapperElement().appendChild(tt); + else + document.body.appendChild(tt); + + function position(e) { + if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position); + tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + "px"; + tt.style.left = (e.clientX + 5) + "px"; + } + CodeMirror.on(document, "mousemove", position); + position(e); + if (tt.style.opacity != null) tt.style.opacity = 1; + return tt; + } + function rm(elt) { + if (elt.parentNode) elt.parentNode.removeChild(elt); + } + function hideTooltip(tt) { + if (!tt.parentNode) return; + if (tt.style.opacity == null) rm(tt); + tt.style.opacity = 0; + setTimeout(function() { rm(tt); }, 600); + } + + function showTooltipFor(cm, e, content, node) { + var tooltip = showTooltip(cm, e, content); + function hide() { + CodeMirror.off(node, "mouseout", hide); + if (tooltip) { hideTooltip(tooltip); tooltip = null; } + } + var poll = setInterval(function() { + if (tooltip) for (var n = node;; n = n.parentNode) { + if (n && n.nodeType == 11) n = n.host; + if (n == document.body) return; + if (!n) { hide(); break; } + } + if (!tooltip) return clearInterval(poll); + }, 400); + CodeMirror.on(node, "mouseout", hide); + } + + function LintState(cm, conf, hasGutter) { + this.marked = []; + if (conf instanceof Function) conf = {getAnnotations: conf}; + if (!conf || conf === true) conf = {}; + this.options = {}; + this.linterOptions = conf.options || {}; + for (var prop in defaults) this.options[prop] = defaults[prop]; + for (var prop in conf) { + if (defaults.hasOwnProperty(prop)) { + if (conf[prop] != null) this.options[prop] = conf[prop]; + } else if (!conf.options) { + this.linterOptions[prop] = conf[prop]; + } + } + this.timeout = null; + this.hasGutter = hasGutter; + this.onMouseOver = function(e) { onMouseOver(cm, e); }; + this.waitingFor = 0 + } + + var defaults = { + highlightLines: false, + tooltips: true, + delay: 500, + lintOnChange: true, + getAnnotations: null, + async: false, + selfContain: null, + formatAnnotation: null, + onUpdateLinting: null + } + + function clearMarks(cm) { + var state = cm.state.lint; + if (state.hasGutter) cm.clearGutter(GUTTER_ID); + if (state.options.highlightLines) clearErrorLines(cm); + for (var i = 0; i < state.marked.length; ++i) + state.marked[i].clear(); + state.marked.length = 0; + } + + function clearErrorLines(cm) { + cm.eachLine(function(line) { + var has = line.wrapClass && /\bCodeMirror-lint-line-\w+\b/.exec(line.wrapClass); + if (has) cm.removeLineClass(line, "wrap", has[0]); + }) + } + + function makeMarker(cm, labels, severity, multiple, tooltips) { + var marker = document.createElement("div"), inner = marker; + marker.className = "CodeMirror-lint-marker CodeMirror-lint-marker-" + severity; + if (multiple) { + inner = marker.appendChild(document.createElement("div")); + inner.className = "CodeMirror-lint-marker CodeMirror-lint-marker-multiple"; + } + + if (tooltips != false) CodeMirror.on(inner, "mouseover", function(e) { + showTooltipFor(cm, e, labels, inner); + }); + + return marker; + } + + function getMaxSeverity(a, b) { + if (a == "error") return a; + else return b; + } + + function groupByLine(annotations) { + var lines = []; + for (var i = 0; i < annotations.length; ++i) { + var ann = annotations[i], line = ann.from.line; + (lines[line] || (lines[line] = [])).push(ann); + } + return lines; + } + + function annotationTooltip(ann) { + var severity = ann.severity; + if (!severity) severity = "error"; + var tip = document.createElement("div"); + tip.className = "CodeMirror-lint-message CodeMirror-lint-message-" + severity; + if (typeof ann.messageHTML != 'undefined') { + tip.innerHTML = ann.messageHTML; + } else { + tip.appendChild(document.createTextNode(ann.message)); + } + return tip; + } + + function lintAsync(cm, getAnnotations) { + var state = cm.state.lint + var id = ++state.waitingFor + function abort() { + id = -1 + cm.off("change", abort) + } + cm.on("change", abort) + getAnnotations(cm.getValue(), function(annotations, arg2) { + cm.off("change", abort) + if (state.waitingFor != id) return + if (arg2 && annotations instanceof CodeMirror) annotations = arg2 + cm.operation(function() {updateLinting(cm, annotations)}) + }, state.linterOptions, cm); + } + + function startLinting(cm) { + var state = cm.state.lint; + if (!state) return; + var options = state.options; + /* + * Passing rules in `options` property prevents JSHint (and other linters) from complaining + * about unrecognized rules like `onUpdateLinting`, `delay`, `lintOnChange`, etc. + */ + var getAnnotations = options.getAnnotations || cm.getHelper(CodeMirror.Pos(0, 0), "lint"); + if (!getAnnotations) return; + if (options.async || getAnnotations.async) { + lintAsync(cm, getAnnotations) + } else { + var annotations = getAnnotations(cm.getValue(), state.linterOptions, cm); + if (!annotations) return; + if (annotations.then) annotations.then(function(issues) { + cm.operation(function() {updateLinting(cm, issues)}) + }); + else cm.operation(function() {updateLinting(cm, annotations)}) + } + } + + function updateLinting(cm, annotationsNotSorted) { + var state = cm.state.lint; + if (!state) return; + var options = state.options; + clearMarks(cm); + + var annotations = groupByLine(annotationsNotSorted); + + for (var line = 0; line < annotations.length; ++line) { + var anns = annotations[line]; + if (!anns) continue; + + // filter out duplicate messages + var message = []; + anns = anns.filter(function(item) { return message.indexOf(item.message) > -1 ? false : message.push(item.message) }); + + var maxSeverity = null; + var tipLabel = state.hasGutter && document.createDocumentFragment(); + + for (var i = 0; i < anns.length; ++i) { + var ann = anns[i]; + var severity = ann.severity; + if (!severity) severity = "error"; + maxSeverity = getMaxSeverity(maxSeverity, severity); + + if (options.formatAnnotation) ann = options.formatAnnotation(ann); + if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann)); + + if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, { + className: "CodeMirror-lint-mark CodeMirror-lint-mark-" + severity, + __annotation: ann + })); + } + // use original annotations[line] to show multiple messages + if (state.hasGutter) + cm.setGutterMarker(line, GUTTER_ID, makeMarker(cm, tipLabel, maxSeverity, annotations[line].length > 1, + options.tooltips)); + + if (options.highlightLines) + cm.addLineClass(line, "wrap", LINT_LINE_ID + maxSeverity); + } + if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm); + } + + function onChange(cm) { + var state = cm.state.lint; + if (!state) return; + clearTimeout(state.timeout); + state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay); + } + + function popupTooltips(cm, annotations, e) { + var target = e.target || e.srcElement; + var tooltip = document.createDocumentFragment(); + for (var i = 0; i < annotations.length; i++) { + var ann = annotations[i]; + tooltip.appendChild(annotationTooltip(ann)); + } + showTooltipFor(cm, e, tooltip, target); + } + + function onMouseOver(cm, e) { + var target = e.target || e.srcElement; + if (!/\bCodeMirror-lint-mark-/.test(target.className)) return; + var box = target.getBoundingClientRect(), x = (box.left + box.right) / 2, y = (box.top + box.bottom) / 2; + var spans = cm.findMarksAt(cm.coordsChar({left: x, top: y}, "client")); + + var annotations = []; + for (var i = 0; i < spans.length; ++i) { + var ann = spans[i].__annotation; + if (ann) annotations.push(ann); + } + if (annotations.length) popupTooltips(cm, annotations, e); + } + + CodeMirror.defineOption("lint", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) { + clearMarks(cm); + if (cm.state.lint.options.lintOnChange !== false) + cm.off("change", onChange); + CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver); + clearTimeout(cm.state.lint.timeout); + delete cm.state.lint; + } + + if (val) { + var gutters = cm.getOption("gutters"), hasLintGutter = false; + for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true; + var state = cm.state.lint = new LintState(cm, val, hasLintGutter); + if (state.options.lintOnChange) + cm.on("change", onChange); + if (state.options.tooltips != false && state.options.tooltips != "gutter") + CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver); + + startLinting(cm); + } + }); + + CodeMirror.defineExtension("performLint", function() { + startLinting(this); + }); +}); + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/addon/selection/active-line.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + var WRAP_CLASS = "CodeMirror-activeline"; + var BACK_CLASS = "CodeMirror-activeline-background"; + var GUTT_CLASS = "CodeMirror-activeline-gutter"; + + CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) { + var prev = old == CodeMirror.Init ? false : old; + if (val == prev) return + if (prev) { + cm.off("beforeSelectionChange", selectionChange); + clearActiveLines(cm); + delete cm.state.activeLines; + } + if (val) { + cm.state.activeLines = []; + updateActiveLines(cm, cm.listSelections()); + cm.on("beforeSelectionChange", selectionChange); + } + }); + + function clearActiveLines(cm) { + for (var i = 0; i < cm.state.activeLines.length; i++) { + cm.removeLineClass(cm.state.activeLines[i], "wrap", WRAP_CLASS); + cm.removeLineClass(cm.state.activeLines[i], "background", BACK_CLASS); + cm.removeLineClass(cm.state.activeLines[i], "gutter", GUTT_CLASS); + } + } + + function sameArray(a, b) { + if (a.length != b.length) return false; + for (var i = 0; i < a.length; i++) + if (a[i] != b[i]) return false; + return true; + } + + function updateActiveLines(cm, ranges) { + var active = []; + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i]; + var option = cm.getOption("styleActiveLine"); + if (typeof option == "object" && option.nonEmpty ? range.anchor.line != range.head.line : !range.empty()) + continue + var line = cm.getLineHandleVisualStart(range.head.line); + if (active[active.length - 1] != line) active.push(line); + } + if (sameArray(cm.state.activeLines, active)) return; + cm.operation(function() { + clearActiveLines(cm); + for (var i = 0; i < active.length; i++) { + cm.addLineClass(active[i], "wrap", WRAP_CLASS); + cm.addLineClass(active[i], "background", BACK_CLASS); + cm.addLineClass(active[i], "gutter", GUTT_CLASS); + } + cm.state.activeLines = active; + }); + } + + function selectionChange(cm, sel) { + updateActiveLines(cm, sel.ranges); + } +}); + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/mode/javascript/javascript.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("javascript", function(config, parserConfig) { + var indentUnit = config.indentUnit; + var statementIndent = parserConfig.statementIndent; + var jsonldMode = parserConfig.jsonld; + var jsonMode = parserConfig.json || jsonldMode; + var trackScope = parserConfig.trackScope !== false + var isTS = parserConfig.typescript; + var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/; + + // Tokenizer + + var keywords = function(){ + function kw(type) {return {type: type, style: "keyword"};} + var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d"); + var operator = kw("operator"), atom = {type: "atom", style: "atom"}; + + return { + "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, + "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C, + "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"), + "function": kw("function"), "catch": kw("catch"), + "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"), + "in": operator, "typeof": operator, "instanceof": operator, + "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom, + "this": kw("this"), "class": kw("class"), "super": kw("atom"), + "yield": C, "export": kw("export"), "import": kw("import"), "extends": C, + "await": C + }; + }(); + + var isOperatorChar = /[+\-*&%=<>!?|~^@]/; + var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/; + + function readRegexp(stream) { + var escaped = false, next, inSet = false; + while ((next = stream.next()) != null) { + if (!escaped) { + if (next == "/" && !inSet) return; + if (next == "[") inSet = true; + else if (inSet && next == "]") inSet = false; + } + escaped = !escaped && next == "\\"; + } + } + + // Used as scratch variables to communicate multiple values without + // consing up tons of objects. + var type, content; + function ret(tp, style, cont) { + type = tp; content = cont; + return style; + } + function tokenBase(stream, state) { + var ch = stream.next(); + if (ch == '"' || ch == "'") { + state.tokenize = tokenString(ch); + return state.tokenize(stream, state); + } else if (ch == "." && stream.match(/^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/)) { + return ret("number", "number"); + } else if (ch == "." && stream.match("..")) { + return ret("spread", "meta"); + } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) { + return ret(ch); + } else if (ch == "=" && stream.eat(">")) { + return ret("=>", "operator"); + } else if (ch == "0" && stream.match(/^(?:x[\dA-Fa-f_]+|o[0-7_]+|b[01_]+)n?/)) { + return ret("number", "number"); + } else if (/\d/.test(ch)) { + stream.match(/^[\d_]*(?:n|(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)?/); + return ret("number", "number"); + } else if (ch == "/") { + if (stream.eat("*")) { + state.tokenize = tokenComment; + return tokenComment(stream, state); + } else if (stream.eat("/")) { + stream.skipToEnd(); + return ret("comment", "comment"); + } else if (expressionAllowed(stream, state, 1)) { + readRegexp(stream); + stream.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/); + return ret("regexp", "string-2"); + } else { + stream.eat("="); + return ret("operator", "operator", stream.current()); + } + } else if (ch == "`") { + state.tokenize = tokenQuasi; + return tokenQuasi(stream, state); + } else if (ch == "#" && stream.peek() == "!") { + stream.skipToEnd(); + return ret("meta", "meta"); + } else if (ch == "#" && stream.eatWhile(wordRE)) { + return ret("variable", "property") + } else if (ch == "<" && stream.match("!--") || + (ch == "-" && stream.match("->") && !/\S/.test(stream.string.slice(0, stream.start)))) { + stream.skipToEnd() + return ret("comment", "comment") + } else if (isOperatorChar.test(ch)) { + if (ch != ">" || !state.lexical || state.lexical.type != ">") { + if (stream.eat("=")) { + if (ch == "!" || ch == "=") stream.eat("=") + } else if (/[<>*+\-|&?]/.test(ch)) { + stream.eat(ch) + if (ch == ">") stream.eat(ch) + } + } + if (ch == "?" && stream.eat(".")) return ret(".") + return ret("operator", "operator", stream.current()); + } else if (wordRE.test(ch)) { + stream.eatWhile(wordRE); + var word = stream.current() + if (state.lastType != ".") { + if (keywords.propertyIsEnumerable(word)) { + var kw = keywords[word] + return ret(kw.type, kw.style, word) + } + if (word == "async" && stream.match(/^(\s|\/\*([^*]|\*(?!\/))*?\*\/)*[\[\(\w]/, false)) + return ret("async", "keyword", word) + } + return ret("variable", "variable", word) + } + } + + function tokenString(quote) { + return function(stream, state) { + var escaped = false, next; + if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){ + state.tokenize = tokenBase; + return ret("jsonld-keyword", "meta"); + } + while ((next = stream.next()) != null) { + if (next == quote && !escaped) break; + escaped = !escaped && next == "\\"; + } + if (!escaped) state.tokenize = tokenBase; + return ret("string", "string"); + }; + } + + function tokenComment(stream, state) { + var maybeEnd = false, ch; + while (ch = stream.next()) { + if (ch == "/" && maybeEnd) { + state.tokenize = tokenBase; + break; + } + maybeEnd = (ch == "*"); + } + return ret("comment", "comment"); + } + + function tokenQuasi(stream, state) { + var escaped = false, next; + while ((next = stream.next()) != null) { + if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) { + state.tokenize = tokenBase; + break; + } + escaped = !escaped && next == "\\"; + } + return ret("quasi", "string-2", stream.current()); + } + + var brackets = "([{}])"; + // This is a crude lookahead trick to try and notice that we're + // parsing the argument patterns for a fat-arrow function before we + // actually hit the arrow token. It only works if the arrow is on + // the same line as the arguments and there's no strange noise + // (comments) in between. Fallback is to only notice when we hit the + // arrow, and not declare the arguments as locals for the arrow + // body. + function findFatArrow(stream, state) { + if (state.fatArrowAt) state.fatArrowAt = null; + var arrow = stream.string.indexOf("=>", stream.start); + if (arrow < 0) return; + + if (isTS) { // Try to skip TypeScript return type declarations after the arguments + var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow)) + if (m) arrow = m.index + } + + var depth = 0, sawSomething = false; + for (var pos = arrow - 1; pos >= 0; --pos) { + var ch = stream.string.charAt(pos); + var bracket = brackets.indexOf(ch); + if (bracket >= 0 && bracket < 3) { + if (!depth) { ++pos; break; } + if (--depth == 0) { if (ch == "(") sawSomething = true; break; } + } else if (bracket >= 3 && bracket < 6) { + ++depth; + } else if (wordRE.test(ch)) { + sawSomething = true; + } else if (/["'\/`]/.test(ch)) { + for (;; --pos) { + if (pos == 0) return + var next = stream.string.charAt(pos - 1) + if (next == ch && stream.string.charAt(pos - 2) != "\\") { pos--; break } + } + } else if (sawSomething && !depth) { + ++pos; + break; + } + } + if (sawSomething && !depth) state.fatArrowAt = pos; + } + + // Parser + + var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, + "regexp": true, "this": true, "import": true, "jsonld-keyword": true}; + + function JSLexical(indented, column, type, align, prev, info) { + this.indented = indented; + this.column = column; + this.type = type; + this.prev = prev; + this.info = info; + if (align != null) this.align = align; + } + + function inScope(state, varname) { + if (!trackScope) return false + for (var v = state.localVars; v; v = v.next) + if (v.name == varname) return true; + for (var cx = state.context; cx; cx = cx.prev) { + for (var v = cx.vars; v; v = v.next) + if (v.name == varname) return true; + } + } + + function parseJS(state, style, type, content, stream) { + var cc = state.cc; + // Communicate our context to the combinators. + // (Less wasteful than consing up a hundred closures on every call.) + cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style; + + if (!state.lexical.hasOwnProperty("align")) + state.lexical.align = true; + + while(true) { + var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement; + if (combinator(type, content)) { + while(cc.length && cc[cc.length - 1].lex) + cc.pop()(); + if (cx.marked) return cx.marked; + if (type == "variable" && inScope(state, content)) return "variable-2"; + return style; + } + } + } + + // Combinator utils + + var cx = {state: null, column: null, marked: null, cc: null}; + function pass() { + for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]); + } + function cont() { + pass.apply(null, arguments); + return true; + } + function inList(name, list) { + for (var v = list; v; v = v.next) if (v.name == name) return true + return false; + } + function register(varname) { + var state = cx.state; + cx.marked = "def"; + if (!trackScope) return + if (state.context) { + if (state.lexical.info == "var" && state.context && state.context.block) { + // FIXME function decls are also not block scoped + var newContext = registerVarScoped(varname, state.context) + if (newContext != null) { + state.context = newContext + return + } + } else if (!inList(varname, state.localVars)) { + state.localVars = new Var(varname, state.localVars) + return + } + } + // Fall through means this is global + if (parserConfig.globalVars && !inList(varname, state.globalVars)) + state.globalVars = new Var(varname, state.globalVars) + } + function registerVarScoped(varname, context) { + if (!context) { + return null + } else if (context.block) { + var inner = registerVarScoped(varname, context.prev) + if (!inner) return null + if (inner == context.prev) return context + return new Context(inner, context.vars, true) + } else if (inList(varname, context.vars)) { + return context + } else { + return new Context(context.prev, new Var(varname, context.vars), false) + } + } + + function isModifier(name) { + return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly" + } + + // Combinators + + function Context(prev, vars, block) { this.prev = prev; this.vars = vars; this.block = block } + function Var(name, next) { this.name = name; this.next = next } + + var defaultVars = new Var("this", new Var("arguments", null)) + function pushcontext() { + cx.state.context = new Context(cx.state.context, cx.state.localVars, false) + cx.state.localVars = defaultVars + } + function pushblockcontext() { + cx.state.context = new Context(cx.state.context, cx.state.localVars, true) + cx.state.localVars = null + } + pushcontext.lex = pushblockcontext.lex = true + function popcontext() { + cx.state.localVars = cx.state.context.vars + cx.state.context = cx.state.context.prev + } + popcontext.lex = true + function pushlex(type, info) { + var result = function() { + var state = cx.state, indent = state.indented; + if (state.lexical.type == "stat") indent = state.lexical.indented; + else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev) + indent = outer.indented; + state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info); + }; + result.lex = true; + return result; + } + function poplex() { + var state = cx.state; + if (state.lexical.prev) { + if (state.lexical.type == ")") + state.indented = state.lexical.indented; + state.lexical = state.lexical.prev; + } + } + poplex.lex = true; + + function expect(wanted) { + function exp(type) { + if (type == wanted) return cont(); + else if (wanted == ";" || type == "}" || type == ")" || type == "]") return pass(); + else return cont(exp); + }; + return exp; + } + + function statement(type, value) { + if (type == "var") return cont(pushlex("vardef", value), vardef, expect(";"), poplex); + if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex); + if (type == "keyword b") return cont(pushlex("form"), statement, poplex); + if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex); + if (type == "debugger") return cont(expect(";")); + if (type == "{") return cont(pushlex("}"), pushblockcontext, block, poplex, popcontext); + if (type == ";") return cont(); + if (type == "if") { + if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex) + cx.state.cc.pop()(); + return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse); + } + if (type == "function") return cont(functiondef); + if (type == "for") return cont(pushlex("form"), pushblockcontext, forspec, statement, popcontext, poplex); + if (type == "class" || (isTS && value == "interface")) { + cx.marked = "keyword" + return cont(pushlex("form", type == "class" ? type : value), className, poplex) + } + if (type == "variable") { + if (isTS && value == "declare") { + cx.marked = "keyword" + return cont(statement) + } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) { + cx.marked = "keyword" + if (value == "enum") return cont(enumdef); + else if (value == "type") return cont(typename, expect("operator"), typeexpr, expect(";")); + else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex) + } else if (isTS && value == "namespace") { + cx.marked = "keyword" + return cont(pushlex("form"), expression, statement, poplex) + } else if (isTS && value == "abstract") { + cx.marked = "keyword" + return cont(statement) + } else { + return cont(pushlex("stat"), maybelabel); + } + } + if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"), pushblockcontext, + block, poplex, poplex, popcontext); + if (type == "case") return cont(expression, expect(":")); + if (type == "default") return cont(expect(":")); + if (type == "catch") return cont(pushlex("form"), pushcontext, maybeCatchBinding, statement, poplex, popcontext); + if (type == "export") return cont(pushlex("stat"), afterExport, poplex); + if (type == "import") return cont(pushlex("stat"), afterImport, poplex); + if (type == "async") return cont(statement) + if (value == "@") return cont(expression, statement) + return pass(pushlex("stat"), expression, expect(";"), poplex); + } + function maybeCatchBinding(type) { + if (type == "(") return cont(funarg, expect(")")) + } + function expression(type, value) { + return expressionInner(type, value, false); + } + function expressionNoComma(type, value) { + return expressionInner(type, value, true); + } + function parenExpr(type) { + if (type != "(") return pass() + return cont(pushlex(")"), maybeexpression, expect(")"), poplex) + } + function expressionInner(type, value, noComma) { + if (cx.state.fatArrowAt == cx.stream.start) { + var body = noComma ? arrowBodyNoComma : arrowBody; + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext); + else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext); + } + + var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma; + if (atomicTypes.hasOwnProperty(type)) return cont(maybeop); + if (type == "function") return cont(functiondef, maybeop); + if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); } + if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression); + if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop); + if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression); + if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop); + if (type == "{") return contCommasep(objprop, "}", null, maybeop); + if (type == "quasi") return pass(quasi, maybeop); + if (type == "new") return cont(maybeTarget(noComma)); + return cont(); + } + function maybeexpression(type) { + if (type.match(/[;\}\)\],]/)) return pass(); + return pass(expression); + } + + function maybeoperatorComma(type, value) { + if (type == ",") return cont(maybeexpression); + return maybeoperatorNoComma(type, value, false); + } + function maybeoperatorNoComma(type, value, noComma) { + var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma; + var expr = noComma == false ? expression : expressionNoComma; + if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext); + if (type == "operator") { + if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me); + if (isTS && value == "<" && cx.stream.match(/^([^<>]|<[^<>]*>)*>\s*\(/, false)) + return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me); + if (value == "?") return cont(expression, expect(":"), expr); + return cont(expr); + } + if (type == "quasi") { return pass(quasi, me); } + if (type == ";") return; + if (type == "(") return contCommasep(expressionNoComma, ")", "call", me); + if (type == ".") return cont(property, me); + if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me); + if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) } + if (type == "regexp") { + cx.state.lastType = cx.marked = "operator" + cx.stream.backUp(cx.stream.pos - cx.stream.start - 1) + return cont(expr) + } + } + function quasi(type, value) { + if (type != "quasi") return pass(); + if (value.slice(value.length - 2) != "${") return cont(quasi); + return cont(maybeexpression, continueQuasi); + } + function continueQuasi(type) { + if (type == "}") { + cx.marked = "string-2"; + cx.state.tokenize = tokenQuasi; + return cont(quasi); + } + } + function arrowBody(type) { + findFatArrow(cx.stream, cx.state); + return pass(type == "{" ? statement : expression); + } + function arrowBodyNoComma(type) { + findFatArrow(cx.stream, cx.state); + return pass(type == "{" ? statement : expressionNoComma); + } + function maybeTarget(noComma) { + return function(type) { + if (type == ".") return cont(noComma ? targetNoComma : target); + else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma) + else return pass(noComma ? expressionNoComma : expression); + }; + } + function target(_, value) { + if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); } + } + function targetNoComma(_, value) { + if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); } + } + function maybelabel(type) { + if (type == ":") return cont(poplex, statement); + return pass(maybeoperatorComma, expect(";"), poplex); + } + function property(type) { + if (type == "variable") {cx.marked = "property"; return cont();} + } + function objprop(type, value) { + if (type == "async") { + cx.marked = "property"; + return cont(objprop); + } else if (type == "variable" || cx.style == "keyword") { + cx.marked = "property"; + if (value == "get" || value == "set") return cont(getterSetter); + var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params + if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false))) + cx.state.fatArrowAt = cx.stream.pos + m[0].length + return cont(afterprop); + } else if (type == "number" || type == "string") { + cx.marked = jsonldMode ? "property" : (cx.style + " property"); + return cont(afterprop); + } else if (type == "jsonld-keyword") { + return cont(afterprop); + } else if (isTS && isModifier(value)) { + cx.marked = "keyword" + return cont(objprop) + } else if (type == "[") { + return cont(expression, maybetype, expect("]"), afterprop); + } else if (type == "spread") { + return cont(expressionNoComma, afterprop); + } else if (value == "*") { + cx.marked = "keyword"; + return cont(objprop); + } else if (type == ":") { + return pass(afterprop) + } + } + function getterSetter(type) { + if (type != "variable") return pass(afterprop); + cx.marked = "property"; + return cont(functiondef); + } + function afterprop(type) { + if (type == ":") return cont(expressionNoComma); + if (type == "(") return pass(functiondef); + } + function commasep(what, end, sep) { + function proceed(type, value) { + if (sep ? sep.indexOf(type) > -1 : type == ",") { + var lex = cx.state.lexical; + if (lex.info == "call") lex.pos = (lex.pos || 0) + 1; + return cont(function(type, value) { + if (type == end || value == end) return pass() + return pass(what) + }, proceed); + } + if (type == end || value == end) return cont(); + if (sep && sep.indexOf(";") > -1) return pass(what) + return cont(expect(end)); + } + return function(type, value) { + if (type == end || value == end) return cont(); + return pass(what, proceed); + }; + } + function contCommasep(what, end, info) { + for (var i = 3; i < arguments.length; i++) + cx.cc.push(arguments[i]); + return cont(pushlex(end, info), commasep(what, end), poplex); + } + function block(type) { + if (type == "}") return cont(); + return pass(statement, block); + } + function maybetype(type, value) { + if (isTS) { + if (type == ":") return cont(typeexpr); + if (value == "?") return cont(maybetype); + } + } + function maybetypeOrIn(type, value) { + if (isTS && (type == ":" || value == "in")) return cont(typeexpr) + } + function mayberettype(type) { + if (isTS && type == ":") { + if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr) + else return cont(typeexpr) + } + } + function isKW(_, value) { + if (value == "is") { + cx.marked = "keyword" + return cont() + } + } + function typeexpr(type, value) { + if (value == "keyof" || value == "typeof" || value == "infer" || value == "readonly") { + cx.marked = "keyword" + return cont(value == "typeof" ? expressionNoComma : typeexpr) + } + if (type == "variable" || value == "void") { + cx.marked = "type" + return cont(afterType) + } + if (value == "|" || value == "&") return cont(typeexpr) + if (type == "string" || type == "number" || type == "atom") return cont(afterType); + if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType) + if (type == "{") return cont(pushlex("}"), typeprops, poplex, afterType) + if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType, afterType) + if (type == "<") return cont(commasep(typeexpr, ">"), typeexpr) + if (type == "quasi") { return pass(quasiType, afterType); } + } + function maybeReturnType(type) { + if (type == "=>") return cont(typeexpr) + } + function typeprops(type) { + if (type.match(/[\}\)\]]/)) return cont() + if (type == "," || type == ";") return cont(typeprops) + return pass(typeprop, typeprops) + } + function typeprop(type, value) { + if (type == "variable" || cx.style == "keyword") { + cx.marked = "property" + return cont(typeprop) + } else if (value == "?" || type == "number" || type == "string") { + return cont(typeprop) + } else if (type == ":") { + return cont(typeexpr) + } else if (type == "[") { + return cont(expect("variable"), maybetypeOrIn, expect("]"), typeprop) + } else if (type == "(") { + return pass(functiondecl, typeprop) + } else if (!type.match(/[;\}\)\],]/)) { + return cont() + } + } + function quasiType(type, value) { + if (type != "quasi") return pass(); + if (value.slice(value.length - 2) != "${") return cont(quasiType); + return cont(typeexpr, continueQuasiType); + } + function continueQuasiType(type) { + if (type == "}") { + cx.marked = "string-2"; + cx.state.tokenize = tokenQuasi; + return cont(quasiType); + } + } + function typearg(type, value) { + if (type == "variable" && cx.stream.match(/^\s*[?:]/, false) || value == "?") return cont(typearg) + if (type == ":") return cont(typeexpr) + if (type == "spread") return cont(typearg) + return pass(typeexpr) + } + function afterType(type, value) { + if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) + if (value == "|" || type == "." || value == "&") return cont(typeexpr) + if (type == "[") return cont(typeexpr, expect("]"), afterType) + if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) } + if (value == "?") return cont(typeexpr, expect(":"), typeexpr) + } + function maybeTypeArgs(_, value) { + if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) + } + function typeparam() { + return pass(typeexpr, maybeTypeDefault) + } + function maybeTypeDefault(_, value) { + if (value == "=") return cont(typeexpr) + } + function vardef(_, value) { + if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)} + return pass(pattern, maybetype, maybeAssign, vardefCont); + } + function pattern(type, value) { + if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) } + if (type == "variable") { register(value); return cont(); } + if (type == "spread") return cont(pattern); + if (type == "[") return contCommasep(eltpattern, "]"); + if (type == "{") return contCommasep(proppattern, "}"); + } + function proppattern(type, value) { + if (type == "variable" && !cx.stream.match(/^\s*:/, false)) { + register(value); + return cont(maybeAssign); + } + if (type == "variable") cx.marked = "property"; + if (type == "spread") return cont(pattern); + if (type == "}") return pass(); + if (type == "[") return cont(expression, expect(']'), expect(':'), proppattern); + return cont(expect(":"), pattern, maybeAssign); + } + function eltpattern() { + return pass(pattern, maybeAssign) + } + function maybeAssign(_type, value) { + if (value == "=") return cont(expressionNoComma); + } + function vardefCont(type) { + if (type == ",") return cont(vardef); + } + function maybeelse(type, value) { + if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex); + } + function forspec(type, value) { + if (value == "await") return cont(forspec); + if (type == "(") return cont(pushlex(")"), forspec1, poplex); + } + function forspec1(type) { + if (type == "var") return cont(vardef, forspec2); + if (type == "variable") return cont(forspec2); + return pass(forspec2) + } + function forspec2(type, value) { + if (type == ")") return cont() + if (type == ";") return cont(forspec2) + if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression, forspec2) } + return pass(expression, forspec2) + } + function functiondef(type, value) { + if (value == "*") {cx.marked = "keyword"; return cont(functiondef);} + if (type == "variable") {register(value); return cont(functiondef);} + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext); + if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef) + } + function functiondecl(type, value) { + if (value == "*") {cx.marked = "keyword"; return cont(functiondecl);} + if (type == "variable") {register(value); return cont(functiondecl);} + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, popcontext); + if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondecl) + } + function typename(type, value) { + if (type == "keyword" || type == "variable") { + cx.marked = "type" + return cont(typename) + } else if (value == "<") { + return cont(pushlex(">"), commasep(typeparam, ">"), poplex) + } + } + function funarg(type, value) { + if (value == "@") cont(expression, funarg) + if (type == "spread") return cont(funarg); + if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); } + if (isTS && type == "this") return cont(maybetype, maybeAssign) + return pass(pattern, maybetype, maybeAssign); + } + function classExpression(type, value) { + // Class expressions may have an optional name. + if (type == "variable") return className(type, value); + return classNameAfter(type, value); + } + function className(type, value) { + if (type == "variable") {register(value); return cont(classNameAfter);} + } + function classNameAfter(type, value) { + if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter) + if (value == "extends" || value == "implements" || (isTS && type == ",")) { + if (value == "implements") cx.marked = "keyword"; + return cont(isTS ? typeexpr : expression, classNameAfter); + } + if (type == "{") return cont(pushlex("}"), classBody, poplex); + } + function classBody(type, value) { + if (type == "async" || + (type == "variable" && + (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) && + cx.stream.match(/^\s+#?[\w$\xa1-\uffff]/, false))) { + cx.marked = "keyword"; + return cont(classBody); + } + if (type == "variable" || cx.style == "keyword") { + cx.marked = "property"; + return cont(classfield, classBody); + } + if (type == "number" || type == "string") return cont(classfield, classBody); + if (type == "[") + return cont(expression, maybetype, expect("]"), classfield, classBody) + if (value == "*") { + cx.marked = "keyword"; + return cont(classBody); + } + if (isTS && type == "(") return pass(functiondecl, classBody) + if (type == ";" || type == ",") return cont(classBody); + if (type == "}") return cont(); + if (value == "@") return cont(expression, classBody) + } + function classfield(type, value) { + if (value == "!") return cont(classfield) + if (value == "?") return cont(classfield) + if (type == ":") return cont(typeexpr, maybeAssign) + if (value == "=") return cont(expressionNoComma) + var context = cx.state.lexical.prev, isInterface = context && context.info == "interface" + return pass(isInterface ? functiondecl : functiondef) + } + function afterExport(type, value) { + if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); } + if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); } + if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";")); + return pass(statement); + } + function exportField(type, value) { + if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); } + if (type == "variable") return pass(expressionNoComma, exportField); + } + function afterImport(type) { + if (type == "string") return cont(); + if (type == "(") return pass(expression); + if (type == ".") return pass(maybeoperatorComma); + return pass(importSpec, maybeMoreImports, maybeFrom); + } + function importSpec(type, value) { + if (type == "{") return contCommasep(importSpec, "}"); + if (type == "variable") register(value); + if (value == "*") cx.marked = "keyword"; + return cont(maybeAs); + } + function maybeMoreImports(type) { + if (type == ",") return cont(importSpec, maybeMoreImports) + } + function maybeAs(_type, value) { + if (value == "as") { cx.marked = "keyword"; return cont(importSpec); } + } + function maybeFrom(_type, value) { + if (value == "from") { cx.marked = "keyword"; return cont(expression); } + } + function arrayLiteral(type) { + if (type == "]") return cont(); + return pass(commasep(expressionNoComma, "]")); + } + function enumdef() { + return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex) + } + function enummember() { + return pass(pattern, maybeAssign); + } + + function isContinuedStatement(state, textAfter) { + return state.lastType == "operator" || state.lastType == "," || + isOperatorChar.test(textAfter.charAt(0)) || + /[,.]/.test(textAfter.charAt(0)); + } + + function expressionAllowed(stream, state, backUp) { + return state.tokenize == tokenBase && + /^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) || + (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0)))) + } + + // Interface + + return { + startState: function(basecolumn) { + var state = { + tokenize: tokenBase, + lastType: "sof", + cc: [], + lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false), + localVars: parserConfig.localVars, + context: parserConfig.localVars && new Context(null, null, false), + indented: basecolumn || 0 + }; + if (parserConfig.globalVars && typeof parserConfig.globalVars == "object") + state.globalVars = parserConfig.globalVars; + return state; + }, + + token: function(stream, state) { + if (stream.sol()) { + if (!state.lexical.hasOwnProperty("align")) + state.lexical.align = false; + state.indented = stream.indentation(); + findFatArrow(stream, state); + } + if (state.tokenize != tokenComment && stream.eatSpace()) return null; + var style = state.tokenize(stream, state); + if (type == "comment") return style; + state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type; + return parseJS(state, style, type, content, stream); + }, + + indent: function(state, textAfter) { + if (state.tokenize == tokenComment || state.tokenize == tokenQuasi) return CodeMirror.Pass; + if (state.tokenize != tokenBase) return 0; + var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top + // Kludge to prevent 'maybelse' from blocking lexical scope pops + if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) { + var c = state.cc[i]; + if (c == poplex) lexical = lexical.prev; + else if (c != maybeelse && c != popcontext) break; + } + while ((lexical.type == "stat" || lexical.type == "form") && + (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) && + (top == maybeoperatorComma || top == maybeoperatorNoComma) && + !/^[,\.=+\-*:?[\(]/.test(textAfter)))) + lexical = lexical.prev; + if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat") + lexical = lexical.prev; + var type = lexical.type, closing = firstChar == type; + + if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info.length + 1 : 0); + else if (type == "form" && firstChar == "{") return lexical.indented; + else if (type == "form") return lexical.indented + indentUnit; + else if (type == "stat") + return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0); + else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false) + return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit); + else if (lexical.align) return lexical.column + (closing ? 0 : 1); + else return lexical.indented + (closing ? 0 : indentUnit); + }, + + electricInput: /^\s*(?:case .*?:|default:|\{|\})$/, + blockCommentStart: jsonMode ? null : "/*", + blockCommentEnd: jsonMode ? null : "*/", + blockCommentContinue: jsonMode ? null : " * ", + lineComment: jsonMode ? null : "//", + fold: "brace", + closeBrackets: "()[]{}''\"\"``", + + helperType: jsonMode ? "json" : "javascript", + jsonldMode: jsonldMode, + jsonMode: jsonMode, + + expressionAllowed: expressionAllowed, + + skipExpression: function(state) { + parseJS(state, "atom", "atom", "true", new CodeMirror.StringStream("", 2, null)) + } + }; +}); + +CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/); + +CodeMirror.defineMIME("text/javascript", "javascript"); +CodeMirror.defineMIME("text/ecmascript", "javascript"); +CodeMirror.defineMIME("application/javascript", "javascript"); +CodeMirror.defineMIME("application/x-javascript", "javascript"); +CodeMirror.defineMIME("application/ecmascript", "javascript"); +CodeMirror.defineMIME("application/json", { name: "javascript", json: true }); +CodeMirror.defineMIME("application/x-json", { name: "javascript", json: true }); +CodeMirror.defineMIME("application/manifest+json", { name: "javascript", json: true }) +CodeMirror.defineMIME("application/ld+json", { name: "javascript", jsonld: true }); +CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true }); +CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true }); +}); + + +/* +file none +*/ +/*jslint-enable*/ diff --git a/branch-beta/asset_font_daley_bold.woff2 b/branch-beta/asset_font_daley_bold.woff2 new file mode 100644 index 000000000..783bdd697 Binary files /dev/null and b/branch-beta/asset_font_daley_bold.woff2 differ diff --git a/branch-beta/asset_image_folder_open_solid.svg b/branch-beta/asset_image_folder_open_solid.svg new file mode 100644 index 000000000..99d403c81 --- /dev/null +++ b/branch-beta/asset_image_folder_open_solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/branch-beta/asset_image_github_brands.svg b/branch-beta/asset_image_github_brands.svg new file mode 100644 index 000000000..7870c06dc --- /dev/null +++ b/branch-beta/asset_image_github_brands.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/branch-beta/asset_image_jslint_wrapper_vim.png b/branch-beta/asset_image_jslint_wrapper_vim.png new file mode 100644 index 000000000..c987b46b6 Binary files /dev/null and b/branch-beta/asset_image_jslint_wrapper_vim.png differ diff --git a/branch-beta/asset_image_jslint_wrapper_vscode.png b/branch-beta/asset_image_jslint_wrapper_vscode.png new file mode 100644 index 000000000..1f4297171 Binary files /dev/null and b/branch-beta/asset_image_jslint_wrapper_vscode.png differ diff --git a/branch-beta/asset_image_json_160.svg b/branch-beta/asset_image_json_160.svg new file mode 100644 index 000000000..fca9b8749 --- /dev/null +++ b/branch-beta/asset_image_json_160.svg @@ -0,0 +1,104 @@ + + + + + JSON logo + + + + + + + + + + + + diff --git a/branch-beta/asset_image_logo_512.html b/branch-beta/asset_image_logo_512.html new file mode 100644 index 000000000..b538e29dd --- /dev/null +++ b/branch-beta/asset_image_logo_512.html @@ -0,0 +1,199 @@ + + + +logo + + + +
    +
    JS
    +
    Lint
    +
    + + diff --git a/branch-beta/asset_image_logo_512.png b/branch-beta/asset_image_logo_512.png new file mode 100644 index 000000000..19d154d68 Binary files /dev/null and b/branch-beta/asset_image_logo_512.png differ diff --git a/branch-beta/asset_image_logo_512.svg b/branch-beta/asset_image_logo_512.svg new file mode 100644 index 000000000..61872c4ae --- /dev/null +++ b/branch-beta/asset_image_logo_512.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + diff --git a/branch-beta/index.html b/branch-beta/index.html new file mode 100644 index 000000000..b3f6bc9b3 --- /dev/null +++ b/branch-beta/index.html @@ -0,0 +1,1649 @@ + + + + + + + + + JSLint: The JavaScript Code Quality and Coverage Tool + + + + + + + + + + + + +
    +
    Loading modules
    +   + + . + . + . + +
    +
    + +
    + Source + +
    +
    + + + +
    +
    + Options +
    +
    + Env... +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    + Allow... +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
      +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
      +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
      +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    +
    + + diff --git a/branch-beta/jslint.js b/branch-beta/jslint.js new file mode 100644 index 000000000..b486c1ce2 --- /dev/null +++ b/branch-beta/jslint.js @@ -0,0 +1,11573 @@ +// #!/usr/bin/env node +// JSLint + +// The Unlicense +// +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + + +// jslint(source, option_dict, global_list) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze. +// option_dict An object whose keys correspond to option names. +// global_list An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// PHASE 1. Split by newlines into . +// PHASE 2. Lex into . +// PHASE 3. Parse into using the Pratt-parser. +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// PHASE 5. Check whitespace between tokens in . + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*jslint beta, node*/ +/*property + JSLINT_BETA, NODE_V8_COVERAGE, a, all, argv, arity, artifact, + assertErrorThrownAsync, assertJsonEqual, assertOrThrow, assign, async, b, + beta, bitwise, block, body, browser, c, calls, catch, catch_list, + catch_stack, causes, char, children, clear, closer, closure, code, column, + concat, consoleError, console_error, console_log, constant, context, + convert, count, coverageDir, create, cwd, d, dead, debugInline, default, + delta, devel, directive, directive_ignore_line, directive_list, directives, + dirname, disrupt, dot, edition, elem_list, ellipsis, else, end, endOffset, + endsWith, entries, env, error, eval, every, example_list, excludeList, exec, + execArgv, exit, exitCode, export_dict, exports, expression, extra, fart, + file, fileList, fileURLToPath, filter, finally, flag, floor, for, forEach, + formatted_message, free, freeze, from, froms, fsWriteFileWithParents, + fud_stmt, functionName, function_list, function_stack, functions, get, + getset, github_repo, globExclude, global, global_dict, global_list, + holeList, htmlEscape, id, identifier, import, import_list, import_meta_url, + inc, includeList, indent2, index, indexOf, init, initial, isArray, + isBlockCoverage, isHole, isNaN, is_equal, is_weird, join, jslint, + jslint_apidoc, jslint_assert, jslint_charset_ascii, jslint_cli, + jslint_edition, jslint_phase1_split, jslint_phase2_lex, jslint_phase3_parse, + jslint_phase4_walk, jslint_phase5_whitage, jslint_report, json, + jstestDescribe, jstestIt, jstestOnExit, keys, label, lbp, led_infix, length, + level, line, lineList, line_list, line_offset, line_source, lines, + linesCovered, linesTotal, live, log, long, loop, m, map, margin, match, max, + message, meta, min, mkdir, modeCoverageIgnoreFile, modeIndex, mode_cli, + mode_conditional, mode_json, mode_module, mode_noop, mode_property, + mode_shebang, mode_stop, module, moduleFsInit, moduleName, module_list, + name, names, node, nomen, noop, now, nr, nud_prefix, + objectDeepCopyWithKeysSorted, ok, on, open, opening, option, option_dict, + order, package_name, padEnd, padStart, parameters, parent, parentIi, parse, + pathname, pathnameList, platform, pop, processArgv, process_argv, + process_env, process_exit, promises, property, property_dict, push, quote, + ranges, readFile, readdir, readonly, recursive, reduce, repeat, replace, + resolve, result, reverse, role, round, scriptId, search, set, shebang, + shell, shift, signature, single, slice, some, sort, source, spawn, splice, + split, stack, stack_trace, start, startOffset, startsWith, statement, + statement_prv, stdio, stop, stop_at, stringify, subscript, switch, + syntax_dict, tenure, test, test_cause, test_internal_error, this, thru, + toLocaleString, toString, token, token_global, token_list, token_nxt, + token_tree, tokens, trace, tree, trim, trimEnd, trimRight, try, type, + unlink, unordered, unshift, url, used, v8CoverageListMerge, + v8CoverageReportCreate, value, variable, version, versions, warn, warn_at, + warning, warning_list, warnings, white, wrapped, writeFile +*/ + +// init debugInline +let debugInline = (function () { + let __consoleError = function () { + return; + }; + function debug(...argv) { + +// This function will print to stderr and then return [0]. + + __consoleError("\n\ndebugInline"); + __consoleError(...argv); + __consoleError("\n"); + return argv[0]; + } + debug(); // Coverage-hack. + __consoleError = console.error; //jslint-ignore-line + return debug; +}()); +let jslint_charset_ascii = ( + "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007" + + "\b\t\n\u000b\f\r\u000e\u000f" + + "\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017" + + "\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f" + + " !\"#$%&'()*+,-./0123456789:;<=>?" + + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + + "`abcdefghijklmnopqrstuvwxyz{|}~\u007f" +); +let jslint_edition = "v2024.11.24"; +let jslint_export; // The jslint object to be exported. +let jslint_fudge = 1; // Fudge starting line and starting + // ... column to 1. +let jslint_import_meta_url = ""; // import.meta.url used by cli. +let jslint_rgx_cap = ( + /^[A-Z]/ +); +let jslint_rgx_crlf = ( + /\n|\r\n?/ +); +let jslint_rgx_digits_bits = ( + /^[01_]*/ +); +let jslint_rgx_digits_decimals = ( + /^[0-9_]*/ +); +let jslint_rgx_digits_hexs = ( + /^[0-9A-F_]*/i +); +let jslint_rgx_digits_octals = ( + /^[0-7_]*/ +); +let jslint_rgx_directive = ( + /^(jslint|property|global)\s+(.*)$/ +); +let jslint_rgx_directive_part = ( + /([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*|$/g +); +let jslint_rgx_identifier = ( + /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/ +); +let jslint_rgx_json_number = ( + +// https://datatracker.ietf.org/doc/html/rfc7159#section-6 +// number = [ minus ] int [ frac ] [ exp ] + + /^-?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][\-+]?\d+)?$/ +); +let jslint_rgx_mega = ( + +// Vim-hack - vim-editor has trouble parsing naked '`' in regexp + + /[\u0060\\]|\$\{/ +); +let jslint_rgx_module = ( + /^[a-zA-Z0-9_$:.@\-\/]+$/ +); +let jslint_rgx_numeric_separator_illegal = ( + /__|_$|_n$/m +); +let jslint_rgx_slash_star_or_slash = ( + /\/\*|\/$/ +); +let jslint_rgx_tab = ( + /\t/g +); +let jslint_rgx_todo = ( + /\b(?:todo|TO\s?DO|HACK)\b/ +); +let jslint_rgx_token = new RegExp( + "^(" + + "(\\s+)" + + "|([a-zA-Z_$][a-zA-Z0-9_$]*)" + + "|[(){}\\[\\],:;'\"~\\`]" + + "|\\?[?.]?" + + "|=(?:==?|>)?" + + "|\\.+" + + "|\\*[*\\/=]?" + + "|\\/[*\\/]?" + + "|\\+[=+]?" + + "|-[=\\-]?" + + "|[\\^%]=?" + + "|&[&=]?" + + "|\\" + + "|[|=]?" + + "|>{1,3}=?" + + "|< throws an error. + + let err; + try { + await asyncFunc(); + } catch (errCaught) { + err = errCaught; + } + assertOrThrow(err, "No error thrown."); + assertOrThrow( + !regexp || new RegExp(regexp).test(err.message), + err + ); +} + +function assertJsonEqual(aa, bb, message) { + +// This function will assert JSON.stringify() === JSON.stringify(). + + aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa), undefined, 1); + bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb), undefined, 1); + if (aa !== bb) { + throw new Error( + "\n" + aa + "\n!==\n" + bb + + ( + typeof message === "string" + ? " - " + message + : message + ? " - " + JSON.stringify(message) + : "" + ) + ); + } +} + +function assertOrThrow(condition, message) { + +// This function will throw if is falsy. + + if (!condition) { + throw ( + (!message || typeof message === "string") + ? new Error(String(message).slice(0, 2048)) + : message + ); + } +} + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than '{}' because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +async function fsWriteFileWithParents(pathname, data) { + +// This function will write to and lazy-mkdirp if necessary. + + await moduleFsInit(); + +// Try writing to pathname. + + try { + await moduleFs.promises.writeFile(pathname, data); + } catch (ignore) { + +// Lazy mkdirp. + + await moduleFs.promises.mkdir(modulePath.dirname(pathname), { + recursive: true + }); + +// Retry writing to pathname. + + await moduleFs.promises.writeFile(pathname, data); + } + console.error("wrote file " + pathname); +} + +function globExclude({ + excludeList = [], + includeList = [], + pathnameList = [] +}) { + +// This function will +// 1. Exclude pathnames in that don't match glob-patterns in +// . +// 2. Exclude pathnames in that match glob-patterns in +// . + + function globAssertNotWeird(list, name) { + +// This function will check if of strings contain weird characters. + + [ + [ + "\n", ( + /^.*?([\u0000-\u0007\r]).*/gm + ) + ], + [ + "\r", ( + /^.*?([\n]).*/gm + ) + ] + ].forEach(function ([ + separator, rgx + ]) { + list.join(separator).replace(rgx, function (match0, char) { + throw new Error( + "Weird character " + + JSON.stringify(char) + + " found in " + name + " " + + JSON.stringify(match0) + ); + }); + }); + } + + function globToRegexp(pattern) { + +// This function will translate glob to javascript-regexp, +// which javascript can then use to "glob" pathnames. + + let ii = 0; + let isClass = false; + let strClass = ""; + let strRegex = ""; + pattern = pattern.replace(( + /\/\/+/g + ), "/"); + pattern = pattern.replace(( + /\*\*\*+/g + ), "**"); + pattern.replace(( + /\\\\|\\\[|\\\]|\[|\]|./g + ), function (match0) { + switch (match0) { + case "[": + if (isClass) { + strClass += "["; + return; + } + strClass += "\u0000"; + strRegex += "\u0000"; + isClass = true; + return; + case "]": + if (isClass) { + isClass = false; + return; + } + strRegex += "]"; + return; + default: + if (isClass) { + strClass += match0; + return; + } + strRegex += match0; + } + return ""; + }); + strClass += "\u0000"; + +// An expression "[!...]" matches a single character, namely any character that +// is not matched by the expression obtained by removing the first '!' from it. +// (Thus, "[!a-]" matches any single character except 'a', and '-'.) + + strClass = strClass.replace(( + /\u0000!/g + ), "\u0000^"); + +// One may include '-' in its literal meaning by making it the first or last +// character between the brackets. + + strClass = strClass.replace(( + /\u0000-/g + ), "\u0000\\-"); + strClass = strClass.replace(( + /-\u0000/g + ), "\\-\u0000"); + +// Escape brackets '[', ']' in character class. + + strClass = strClass.replace(( + /[\[\]]/g + ), "\\$&"); + +// https://stackoverflow.com/questions/3561493 +// /is-there-a-regexp-escape-function-in-javascript +// $()*+-./?[\]^{|} + + strRegex = strRegex.replace(( + +// Ignore [-/]. + + /[$()*+.?\[\\\]\^{|}]/g + ), "\\$&"); + +// Expand wildcard '**/*'. + + strRegex = strRegex.replace(( + /\\\*\\\*\/(?:\\\*)+/g + ), ".*?"); + +// Expand wildcard '**'. + + strRegex = strRegex.replace(( + /(^|\/)\\\*\\\*(\/|$)/gm + ), "$1.*?$2"); + +// Expand wildcard '*'. + + strRegex = strRegex.replace(( + /(?:\\\*)+/g + ), "[^\\/]*?"); + +// Expand wildcard '?'. + + strRegex = strRegex.replace(( + /\\\?/g + ), "[^\\/]"); + +// Expand directory-with-trailing-slash '.../'. + + strRegex = strRegex.replace(( + /\/$/gm + ), "\\/.*?"); + +// Merge strClass into strRegex. + + ii = 0; + strClass = strClass.split("\u0000"); + strRegex = strRegex.replace(( + /\u0000/g + ), function () { + ii += 1; + if (strClass[ii] === "") { + return ""; + } + return "[" + strClass[ii] + "]"; + }); + +// Change strRegex from string to regexp. + + strRegex = new RegExp("^" + strRegex + "$", "gm"); + return strRegex; + } + +// Validate excludeList, includeList, pathnameList. + + globAssertNotWeird(excludeList, "pattern"); + globAssertNotWeird(includeList, "pattern"); + globAssertNotWeird(pathnameList, "pathname"); + +// Optimization +// Concat pathnames into a single, newline-separated string, +// whose pathnames can all be filtered with a single, regexp-pass. + + pathnameList = pathnameList.join("\n"); + +// 1. Exclude pathnames in that don't match glob-patterns in +// . + + if (includeList.length > 0) { + includeList = includeList.map(globToRegexp); + includeList.forEach(function (pattern) { + pathnameList = pathnameList.replace(pattern, "\u0000$&"); + }); + pathnameList = pathnameList.replace(( + /^[^\u0000].*/gm + ), ""); + pathnameList = pathnameList.replace(( + /^\u0000+/gm + ), ""); + } + +// 2. Exclude pathnames in that match glob-patterns in +// . + + excludeList = excludeList.map(globToRegexp); + excludeList.forEach(function (pattern) { + pathnameList = pathnameList.replace(pattern, ""); + }); + +// Split newline-separated pathnames back to list. + + pathnameList = pathnameList.split("\n").filter(function (elem) { + return elem; + }); + return { + excludeList, + includeList, + pathnameList + }; +} + +function htmlEscape(str) { + +// This function will make html-safe by escaping & < >. + + return String(str).replace(( + /&/g + ), "&").replace(( + //g + ), ">"); +} + +function jslint( + source = "", // A text to analyze. + option_dict = empty(), // An object whose keys correspond to option + // ... names. + global_list = [] // An array of strings containing global + // ... variables that the file is allowed + // ... readonly access. +) { + +// The jslint function itself. + + let catch_list = []; // The array containing all catch-blocks. + let catch_stack = [ // The stack of catch-blocks. + { + context: empty() + } + ]; + let cause_dict = empty(); // The object of test-causes. + let directive_list = []; // The directive comments. + let export_dict = empty(); // The exported names and values. + let function_list = []; // The array containing all functions. + let function_stack = []; // The stack of functions. + let global_dict = empty(); // The object containing the global + // ... declarations. + let import_list = []; // The array collecting all import-from strings. + let line_list = String( // The array containing source lines. + "\n" + source + ).split(jslint_rgx_crlf).map(function (line_source) { + return { + line_source + }; + }); + let mode_stop = false; // true if JSLint cannot finish. + let property_dict = empty(); // The object containing the tallied + // ... property names. + let state = empty(); // jslint state-object to be passed between + // jslint functions. + let syntax_dict = empty(); // The object containing the parser. + let tenure = empty(); // The predefined property registry. + let token_global = { // The global object; the outermost context. + async: 0, + body: true, + context: empty(), + finally: 0, + from: 0, + id: "(global)", + level: 0, + line: jslint_fudge, + live: [], + loop: 0, + switch: 0, + thru: 0, + try: 0 + }; + let token_list = []; // The array of tokens. + let warning_list = []; // The array collecting all generated warnings. + +// Error reportage functions: + + function artifact(the_token) { + +// Return a string representing an artifact. + + the_token = the_token || state.token_nxt; + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); + } + + function is_equal(aa, bb) { + +// test_cause: +// ["0&&0", "is_equal", "", "", 0] + + test_cause(""); + +// Probably deadcode. +// if (aa === bb) { +// return true; +// } + + jslint_assert(!(aa === bb), `Expected !(aa === bb).`); + if (Array.isArray(aa)) { + return ( + Array.isArray(bb) + && aa.length === bb.length + && aa.every(function (value, index) { + +// test_cause: +// ["`${0}`&&`${0}`", "is_equal", "recurse_isArray", "", 0] +// ["`${0}`&&`${1}`", "is_equal", "recurse_isArray", "", 0] + + test_cause("recurse_isArray"); + return is_equal(value, bb[index]); + }) + ); + } + +// Probably deadcode. +// if (Array.isArray(bb)) { +// return false; +// } + + jslint_assert(!Array.isArray(bb), `Expected !Array.isArray(bb).`); + switch (aa.id === bb.id && aa.id) { + case "(number)": + case "(string)": + return aa.value === bb.value; + +// PR-394 - Bugfix +// Fix jslint falsely believing megastring literals `0` and `1` are similar. + + case "`": + if (!is_equal(aa.value, bb.value)) { + return false; + } + break; + } + if (is_weird(aa) || is_weird(bb)) { + +// test_cause: +// ["aa(/./)||{}", "is_equal", "false", "", 0] + + test_cause("false"); + return false; + } + if (aa.arity === bb.arity && aa.id === bb.id) { + if (aa.id === "." || aa.id === "?.") { + +// test_cause: +// ["aa.bb&&aa.bb", "is_equal", "recurse_arity_id", "", 0] +// ["aa?.bb&&aa?.bb", "is_equal", "recurse_arity_id", "", 0] + + test_cause("recurse_arity_id"); + return ( + is_equal(aa.expression, bb.expression) + && is_equal(aa.name, bb.name) + ); + } + if (aa.arity === "unary") { + +// test_cause: +// ["+0&&+0", "is_equal", "recurse_unary", "", 0] + + test_cause("recurse_unary"); + return is_equal(aa.expression, bb.expression); + } + if (aa.arity === "binary") { + +// test_cause: +// ["aa[0]&&aa[0]", "is_equal", "recurse_binary", "", 0] + + test_cause("recurse_binary"); + return ( + aa.id !== "(" + && is_equal(aa.expression[0], bb.expression[0]) + && is_equal(aa.expression[1], bb.expression[1]) + ); + } + if (aa.arity === "ternary") { + +// test_cause: +// ["aa=(``?``:``)&&(``?``:``)", "is_equal", "recurse_ternary", "", 0] + + test_cause("recurse_ternary"); + return ( + is_equal(aa.expression[0], bb.expression[0]) + && is_equal(aa.expression[1], bb.expression[1]) + && is_equal(aa.expression[2], bb.expression[2]) + ); + } + +// Probably deadcode. +// if (aa.arity === "function" || aa.arity === "regexp") { +// return false; +// } + + jslint_assert( + !(aa.arity === "function" || aa.arity === "regexp"), + `Expected !(aa.arity === "function" || aa.arity === "regexp").` + ); + +// test_cause: +// ["undefined&&undefined", "is_equal", "true", "", 0] + + test_cause("true"); + return true; + } + +// test_cause: +// ["null&&undefined", "is_equal", "false", "", 0] + + test_cause("false"); + return false; + } + + function is_weird(thing) { + switch (thing.id) { + case "(regexp)": + return true; + case "=>": + return true; + case "[": + return thing.arity === "unary"; + case "function": + return true; + case "{": + return true; + default: + return false; + } + } + + function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + the_token = the_token || state.token_nxt; + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); + } + + function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); + } + + function test_cause(code, aa, column) { + +// This function will instrument to for test-purposes. + + if (option_dict.test_cause) { + cause_dict[JSON.stringify([ + String(new Error().stack).replace(( + /^ at (?:file|stop|stop_at|test_cause|warn|warn_at)\b.*?\n/gm + ), "").match( + /\n at ((?:Object\.\w+?_)?\w+?) / + )[1].replace(( + /^Object\./ + ), ""), + code, + String( + (aa === undefined || aa === token_global) + ? "" + : aa + ), + column || 0 + ])] = true; + } + } + + function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + let the_warning; + the_token = the_token || state.token_nxt; + the_warning = warn_at( + code, + the_token.line, + (the_token.from || 0) + jslint_fudge, + a || artifact(the_token), + b, + c, + d + ); + +// Issue #408 +// Warnings that should be ignored sometimes suppress legitimate warnings. + + if (the_warning.directive_ignore_line) { + return the_warning; + } + +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token.warning) { + warning_list.pop(); + return the_warning; + } + the_token.warning = the_warning; + return the_warning; + } + + function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + let mm; + let warning = Object.assign(empty(), { + a, + b, + c, + code, + +// Fudge column numbers in warning message. + + column: column || jslint_fudge, + d, + line, + line_source: "", + name: "JSLintError" + }, line_list[line]); + warning.column = Math.max( + Math.min(warning.column, warning.line_source.length), + jslint_fudge + ); + test_cause(code, b || a, warning.column); + switch (code) { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + case "and": + mm = `The '&&' subexpression should be wrapped in parens.`; + break; + case "bad_assignment_a": + mm = `Bad assignment to '${a}'.`; + break; + case "bad_directive_a": + mm = `Bad directive '${a}'.`; + break; + case "bad_get": + mm = `A get function takes no parameters.`; + break; + case "bad_module_name_a": + mm = `Bad module name '${a}'.`; + break; + case "bad_option_a": + mm = `Bad option '${a}'.`; + break; + case "bad_set": + mm = `A set function takes one parameter.`; + break; + case "duplicate_a": + mm = `Duplicate '${a}'.`; + break; + case "empty_block": + mm = `Empty block.`; + break; + case "expected_a": + mm = `Expected '${a}'.`; + break; + case "expected_a_at_b_c": + mm = `Expected '${a}' at column ${b}, not column ${c}.`; + break; + case "expected_a_b": + mm = `Expected '${a}' and instead saw '${b}'.`; + break; + case "expected_a_b_before_c_d": + mm = `Expected ${a} '${b}' to be ordered before ${c} '${d}'.`; + break; + case "expected_a_b_from_c_d": + mm = ( + `Expected '${a}' to match '${b}' from line ${c}` + + ` and instead saw '${d}'.` + ); + break; + case "expected_a_before_b": + mm = `Expected '${a}' before '${b}'.`; + break; + case "expected_digits_after_a": + mm = `Expected digits after '${a}'.`; + break; + case "expected_four_digits": + mm = `Expected four digits after '\\u'.`; + break; + case "expected_identifier_a": + mm = `Expected an identifier and instead saw '${a}'.`; + break; + case "expected_line_break_a_b": + mm = `Expected a line break between '${a}' and '${b}'.`; + break; + case "expected_regexp_factor_a": + mm = `Expected a regexp factor and instead saw '${a}'.`; + break; + case "expected_space_a_b": + mm = `Expected one space between '${a}' and '${b}'.`; + break; + case "expected_statements_a": + mm = `Expected statements before '${a}'.`; + break; + case "expected_string_a": + mm = `Expected a string and instead saw '${a}'.`; + break; + case "expected_type_string_a": + mm = `Expected a type string and instead saw '${a}'.`; + break; + case "freeze_exports": + mm = ( + `Expected 'Object.freeze('. All export values should be frozen.` + ); + break; + +// PR-378 - Relax warning "function_in_loop". +// +// case "function_in_loop": +// mm = `Don't create functions within a loop.`; +// break; + +// PR-390 - Add numeric-separator check. + + case "illegal_num_separator": + mm = `Illegal numeric separator '_' at column ${column}.`; + break; + case "infix_in": + mm = ( + `Unexpected 'in'. Compare with undefined,` + + ` or use the hasOwnProperty method instead.` + ); + break; + case "label_a": + mm = `'${a}' is a statement label.`; + break; + case "misplaced_a": + mm = `Place '${a}' at the outermost level.`; + break; + case "misplaced_directive_a": + mm = `Place the '/*${a}*/' directive before the first statement.`; + break; + case "missing_await_statement": + mm = `Expected await statement in async function.`; + break; + +// PR-347 - Disable warning "missing_browser". +// +// case "missing_browser": +// mm = `/*global*/ requires the Assume a browser option.`; +// break; + + case "missing_m": + mm = `Expected 'm' flag on a multiline regular expression.`; + break; + case "naked_block": + mm = `Naked block.`; + break; + case "nested_comment": + mm = `Nested comment.`; + break; + case "not_label_a": + mm = `'${a}' is not a label.`; + break; + case "number_isNaN": + mm = `Use Number.isNaN function to compare with NaN.`; + break; + case "out_of_scope_a": + mm = `'${a}' is out of scope.`; + break; + case "redefinition_a_b": + mm = `Redefinition of '${a}' from line ${b}.`; + break; + case "redefinition_global_a_b": + mm = `Redefinition of global ${a} variable '${b}'.`; + break; + case "required_a_optional_b": + mm = `Required parameter '${a}' after optional parameter '${b}'.`; + break; + case "reserved_a": + mm = `Reserved name '${a}'.`; + break; + case "subscript_a": + mm = `['${a}'] is better written in dot notation.`; + break; + case "todo_comment": + mm = `Unexpected TODO comment.`; + break; + case "too_long": + mm = `Line is longer than 80 characters.`; + break; + case "too_many_digits": + mm = `Too many digits.`; + break; + case "unclosed_comment": + mm = `Unclosed comment.`; + break; + case "unclosed_disable": + mm = ( + `Directive '/*jslint-disable*/' was not closed` + + ` with '/*jslint-enable*/'.` + ); + break; + case "unclosed_mega": + mm = `Unclosed mega literal.`; + break; + case "unclosed_string": + mm = `Unclosed string.`; + break; + case "undeclared_a": + mm = `Undeclared '${a}'.`; + break; + case "unexpected_a": + mm = `Unexpected '${a}'.`; + break; + case "unexpected_a_after_b": + mm = `Unexpected '${a}' after '${b}'.`; + break; + case "unexpected_a_before_b": + mm = `Unexpected '${a}' before '${b}'.`; + break; + case "unexpected_at_top_level_a": + mm = `Expected '${a}' to be in a function.`; + break; + case "unexpected_char_a": + mm = `Unexpected character '${a}'.`; + break; + case "unexpected_comment": + mm = `Unexpected comment.`; + break; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// case "unexpected_directive_a": +// mm = `When using modules, don't use directive '/\u002a${a}'.`; +// break; + + case "unexpected_expression_a": + mm = `Unexpected expression '${a}' in statement position.`; + break; + case "unexpected_label_a": + mm = `Unexpected label '${a}'.`; + break; + case "unexpected_parens": + mm = `Don't wrap function literals in parens.`; + break; + case "unexpected_space_a_b": + mm = `Unexpected space between '${a}' and '${b}'.`; + break; + case "unexpected_statement_a": + mm = `Unexpected statement '${a}' in expression position.`; + break; + case "unexpected_trailing_space": + mm = `Unexpected trailing space.`; + break; + case "unexpected_typeof_a": + mm = ( + `Unexpected 'typeof'. Use '===' to compare directly with ${a}.` + ); + break; + case "uninitialized_a": + mm = `Uninitialized '${a}'.`; + break; + case "unopened_enable": + mm = ( + `Directive '/*jslint-enable*/' was not opened` + + ` with '/*jslint-disable*/'.` + ); + break; + case "unreachable_a": + mm = `Unreachable '${a}'.`; + break; + case "unregistered_property_a": + mm = `Unregistered property name '${a}'.`; + break; + case "unused_a": + mm = `Unused '${a}'.`; + break; + case "use_double": + mm = `Use double quotes, not single quotes.`; + break; + +// PR-386 - Fix issue #382 - Make fart-related warnings more readable. + + case "use_function_not_fart": + mm = ( + `Use 'function (...)', not '(...) =>' when arrow functions` + + ` become too complex.` + ); + break; + case "use_open": + mm = ( + `Wrap a ternary expression in parens,` + + ` with a line break after the left paren.` + ); + break; + case "use_spaces": + mm = `Use spaces, not tabs.`; + break; + case "var_on_top": + mm = `Move variable declaration to top of function or script.`; + break; + case "var_switch": + mm = `Don't declare variables in a switch.`; + break; + case "weird_condition_a": + mm = `Weird condition '${a}'.`; + break; + case "weird_expression_a": + mm = `Weird expression '${a}'.`; + break; + case "weird_loop": + mm = `Weird loop.`; + break; + case "weird_property_a": + mm = `Weird property name '${a}'.`; + break; + case "weird_relation_a": + mm = `Weird relation '${a}'.`; + break; + case "wrap_condition": + mm = `Wrap the condition in parens.`; + break; + +// PR-386 - Fix issue #382 - Make fart-related warnings more readable. + + case "wrap_fart_parameter": + mm = `Wrap the parameter before '=>' in parens.`; + break; + case "wrap_immediate": + mm = ( + `Wrap an immediate function invocation in parentheses to assist` + + ` the reader in understanding that the expression is the` + + ` result of a function, and not the function itself.` + ); + break; + case "wrap_regexp": + mm = `Wrap this regexp in parens to avoid confusion.`; + break; + case "wrap_unary": + mm = `Wrap the unary expression in parens.`; + break; + } + +// Validate mm. + + jslint_assert(mm, code); + warning.message = mm; + +// PR-242 - Include stack_trace for jslint to debug itself for errors. + + if (option_dict.trace) { + warning.stack_trace = new Error().stack; + } + if (warning.directive_ignore_line) { + +// test_cause: +// ["0 //jslint-ignore-line", "semicolon", "directive_ignore_line", "", 0] + + test_cause("directive_ignore_line"); + return warning; + } + warning_list.push(warning); + return warning; + } + + try { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + + option_dict = Object.assign(empty(), option_dict); + Object.assign(state, { + artifact, + catch_list, + catch_stack, + directive_list, + export_dict, + function_list, + function_stack, + global_dict, + global_list, + import_list, + is_equal, + is_weird, + line_list, + mode_json: false, // true if parsing JSON. + mode_module: false, // true if import or export was used. + mode_property: false, // true if directive /*property*/ is + // ... used. + mode_shebang: false, // true if #! is seen on the first line. + option_dict, + property_dict, + source, + stop, + stop_at, + syntax_dict, + tenure, + test_cause, + token_global, + token_list, + token_nxt: token_global, + warn, + warn_at, + warning_list + }); + +// PHASE 1. Split by newlines into . + + jslint_phase1_split(state); + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 2. Lex into . + + jslint_phase2_lex(state); + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 3. Parse into using the Pratt-parser. + + jslint_phase3_parse(state); + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). + + if (!state.mode_json) { + jslint_phase4_walk(state); + } + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 5. Check whitespace between tokens in . + + if (!state.mode_json && warning_list.length === 0) { + jslint_phase5_whitage(state); + } + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PR-347 - Disable warning "missing_browser". +// +// if (!option_dict.browser) { +// directive_list.forEach(function (comment) { +// if (comment.directive === "global") { +// +// // test_cause: +// // ["/*global aa*/", "jslint", "missing_browser", "(comment)", 1] +// +// warn("missing_browser", comment); +// } +// }); +// } + + if (option_dict.test_internal_error) { + jslint_assert(undefined, "test_internal_error"); + } + } catch (err) { + mode_stop = true; + err.message = "[JSLint was unable to finish] " + err.message; + err.mode_stop = true; + if (err.name !== "JSLintError") { + Object.assign(err, { + column: jslint_fudge, + line: jslint_fudge, + line_source: "", + stack_trace: err.stack + }); + } + if (warning_list.indexOf(err) === -1) { + warning_list.push(err); + } + } + +// Sort warning_list by mode_stop first, line, column respectively. + + warning_list.sort(function (aa, bb) { + return ( + Boolean(bb.mode_stop) - Boolean(aa.mode_stop) + || aa.line - bb.line + || aa.column - bb.column + ); + +// Update each warning with formatted_message ready-for-use by jslint_cli. + + }).map(function ({ + column, + line, + line_source, + message, + stack_trace = "" + }, ii, list) { + list[ii].formatted_message = String( + String(ii + 1).padStart(2, " ") + + ". \u001b[31m" + message + "\u001b[39m" + + " \u001b[90m\/\/ line " + line + ", column " + column + + "\u001b[39m\n" + + (" " + line_source.trim()).slice(0, 72) + "\n" + + stack_trace + ).trimRight(); + }); + + return { + causes: cause_dict, + directives: directive_list, + edition: jslint_edition, + exports: export_dict, + froms: import_list, + functions: function_list, + global: token_global, + id: "(JSLint)", + json: state.mode_json, + lines: line_list, + module: state.mode_module === true, + ok: warning_list.length === 0 && !mode_stop, + option: option_dict, + property: property_dict, + shebang: ( + state.mode_shebang + ? line_list[jslint_fudge].line_source + : undefined + ), + stop: mode_stop, + tokens: token_list, + tree: state.token_tree, + warnings: warning_list + }; +} + +// PR-362 - Add API Doc. + +async function jslint_apidoc({ + example_list, + github_repo, + module_list, + package_name, + pathname, + version +}) { + +// This function will create API Doc from . + + let elem_ii = 0; + let html; + + function elem_create(moduleObj, key, moduleName) { + +// This function will create a sub API Doc from elem []. + + let example = "N/A"; + let id = encodeURIComponent("apidoc.elem." + moduleName + "." + key); + let name; + let signature; + let source; + name = htmlEscape((typeof moduleObj[key]) + " " + key); + if (typeof moduleObj[key] !== "function") { + return { + name, + signature: (` + +${name} + + `), + source: (` +
  • +

    + + ${name} + +

    +
  • + `) + }; + } + // init source + source = htmlEscape(trim_start(moduleObj[key].toString())); + // init signature + source = source.replace(( + /(\([\S\s]*?\)) \{/ + ), function (match0, match1) { + signature = htmlEscape( + match1.replace(( + / *?\/\*[\S\s]*?\*\/ */g + ), "").replace(( + / *?\/\/.*/g + ), "").replace(( + /\n{2,}/g + ), "\n") + ); + return match0; + }); + // init comment + source = source.replace(( + /\n(?:\/\/.*?\n)+\n/ + ), "$&"); + // init example + example_list.some(function (example2) { + example2.replace( + new RegExp(( + "((?:\\n.*?){8}(function )?)\\b" + + key + + "(\\((?:.*?\\n){8})" + ), "g"), + function (ignore, header, isDeclaration, footer) { + if (!isDeclaration) { + example = "..." + trim_start( + htmlEscape(header) + + "" + + htmlEscape(key) + + "" + + htmlEscape(footer) + ).trimEnd() + "\n..."; + } + return ""; + } + ); + return example !== "N/A"; + }); + if (source.length > 2048) { + source = source.slice(0, 2048) + "...\n}\n"; + } + return { + name, + signature: (` + +${name}${signature} + + `), + source: (` +
  • +

    + + ${name}${signature} + +

    +
  • +
  • Description and source-code:
    ${source}
  • +
  • Example usage:
    ${example}
  • + `) + }; + } + + function trim_start(str) { + +// This function will normalize whitespace before . + + let whitespace = ""; + str.trim().replace(( + /^ */gm + ), function (match0) { + if (whitespace === "" || match0.length < whitespace.length) { + whitespace = match0; + } + return ""; + }); + str = str.replace(new RegExp("^" + whitespace, "gm"), ""); + return str; + } + await moduleFsInit(); + +// Html-escape params. + + github_repo = htmlEscape(github_repo); + package_name = htmlEscape(package_name); + version = htmlEscape(version); + +// Init example_list. + + example_list = await Promise.all(example_list.map(async function (file) { + +// This function will read example from given file. + + let result = await moduleFs.promises.readFile(file, "utf8"); + result = ( + "\n\n\n\n\n\n\n\n" + // bug-workaround - truncate example to manageable size + + result.slice(0, 524288) + + "\n\n\n\n\n\n\n\n" + ); + result = result.replace(( + /\r\n*/g + ), "\n"); + return result; + })); + +// Init module_list. + + module_list = await Promise.all(module_list.map(async function ({ + pathname + }) { + let moduleName = htmlEscape(JSON.stringify(pathname)); + let moduleObj = await import(pathname); + if (moduleObj.default) { + moduleObj = moduleObj.default; + } + return { + elem_list: Object.keys(moduleObj).map(function (key) { + return elem_create(moduleObj, key, moduleName); + }).sort(function (aa, bb) { + return ( + aa.name < bb.name + ? -1 + : 1 + ); + }).map(function (elem) { + elem_ii += 1; + elem.signature = elem.signature.replace( + ">", + ">" + elem_ii + ". " + ); + elem.source = elem.source.replace( + "\">", + "\">" + elem_ii + ". " + ); + return elem; + }), + id: encodeURIComponent("apidoc.module." + moduleName), + moduleName + }; + })); + html = (` + + + + + + +${package_name} apidoc + + + +
    +

    API Doc for ${package_name} (${version})

    +
    + +

    Table of Contents

    +
    +
      + `) + module_list.map(function ({ + elem_list, + id, + moduleName + }) { + return ( + (` +
    • + Module ${moduleName} +
        + `) + + elem_list.map(function ({ + signature + }) { + return "
      • \n" + signature + "\n
      • \n"; + }).join("") + + (` +
      +
    • + `) + ); + }).join("") + (` +
    +
    + `) + module_list.map(function ({ + elem_list, + id, + moduleName + }) { + return ( + (` +
    +

    Module ${moduleName}

    +
      + `) + + elem_list.map(function ({ + source + }) { + return source; + }).join("") + + (` +
    +
    + `) + ); + }).join("") + (` +
    + [ + This document was created with + JSLint + ] +
    +
    + + + `); + html = html.trim().replace(( + / +?$/gm + ), "") + "\n"; + await fsWriteFileWithParents(pathname, html); +} + +function jslint_assert(condition, message) { + +// This function will throw if is falsy. + + if (condition) { + return condition; + } + throw new Error( + `This was caused by a bug in JSLint. +Please open an issue with this stack-trace (and possible example-code) at +https://github.com/jslint-org/jslint/issues. +edition = "${jslint_edition}"; +${String(message).slice(0, 2000)}` + ); +} + +async function jslint_cli({ + console_error, + console_log, + file, + import_meta_url, + mode_cli, + mode_noop, + option, + process_argv, + process_env, + process_exit, + source +}) { + +// This function will run jslint from nodejs-cli. + + let command; + let data; + let exit_code = 0; + let mode_report; + let mode_wrapper_vim; + let result; + + function jslint_from_file({ + code, + file, + line_offset = 0, + mode_conditional, + option = empty() + }) { + let result_from_file; + if ( + mode_conditional + && !( + /^\/\*jslint\b/m + ).test(code.slice(0, 65536)) + ) { + return; + } + option = Object.assign(empty(), option, { + file + }); + switch (( + /\.\w+?$|$/m + ).exec(file)[0]) { + case ".html": + +// Recursively jslint embedded "". + + code.replace(( + /^]*?>\n([\S\s]*?\n)<\/script>$/gm + ), function (ignore, match1, ii) { + jslint_from_file({ + code: match1, + file: file + ". + + import_meta_url = import_meta_url || jslint_import_meta_url; + if ( + jslint_rgx_url_search_window_jslint.test(import_meta_url) + && (typeof globalThis === "object" && globalThis) + ) { + globalThis.jslint = jslint; + } + +// Feature-detect nodejs. + + if (!( + (typeof process === "object" && process) + && process.versions + && typeof process.versions.node === "string" + && !mode_noop + )) { + return exit_code; + } + console_error = console_error || console.error; + console_log = console_log || console.log; + process_argv = process_argv || process.argv; + process_env = process_env || process.env; + process_exit = process_exit || process.exit; + await moduleFsInit(); + if ( + !( + +// Feature-detect nodejs-cli. + + process.execArgv.indexOf("--eval") === -1 + && process.execArgv.indexOf("-e") === -1 + && ( + ( + /[\/|\\]jslint(?:\.[cm]?js)?$/m + ).test(process_argv[1]) + || mode_cli + ) + && ( + moduleUrl.fileURLToPath(import_meta_url) + === + modulePath.resolve(process_argv[1]) + ) + ) + && !mode_cli + ) { + return exit_code; + } + +// init commmand + + command = String(process_argv[2]).split("="); + command[1] = command.slice(1).join("="); + + switch (command[0]) { + +// PR-362 - Add API Doc. + + case "jslint_apidoc": + await jslint_apidoc(Object.assign(JSON.parse(process_argv[3]), { + pathname: command[1] + })); + return; + +// PR-363 - Add command jslint_report. + + case "jslint_report": + mode_report = command[1]; + process_argv = process_argv.slice(1); + break; + +// COMMIT-b26d6df2 - Add command jslint_wrapper_vim. + + case "jslint_wrapper_vim": + mode_wrapper_vim = true; + process_argv = process_argv.slice(1); + break; + +// PR-364 - Add command v8_coverage_report. + + case "v8_coverage_report": + await v8CoverageReportCreate({ + consoleError: console_error, + coverageDir: command[1], + processArgv: process_argv.slice(3) + }); + return; + } + +// Normalize file relative to process.cwd(). + + process_argv.slice(2).some(function (arg) { + if (!arg.startsWith("-")) { + file = file || arg; + return true; + } + }); + if (!file) { + return; + } + file = modulePath.resolve(file) + "/"; + if (file.startsWith(process.cwd() + "/")) { + file = file.replace(process.cwd() + "/", "").slice(0, -1) || "."; + } + file = file.replace(( + /\\/g + ), "/").replace(( + /\/$/g + ), ""); + if (source) { + data = source; + } else { + +// jslint_cli - jslint directory. + + try { + data = await moduleFs.promises.readdir(file, "utf8"); + } catch (ignore) {} + if (data) { + await Promise.all(data.map(async function (file2) { + let code; + let time_start = Date.now(); + file2 = file + "/" + file2; + switch (( + /\.\w+?$|$/m + ).exec(file2)[0]) { + case ".cjs": + case ".html": + case ".js": + case ".json": + case ".md": + case ".mjs": + case ".sh": + break; + default: + return; + } + try { + code = await moduleFs.promises.readFile(file2, "utf8"); + } catch (ignore) { + return; + } + if ( + ( + /(?:\b|_)(?:lock|min|raw|rollup)(?:\b|_)/ + ).test(file2) + || !(code && code.length < 1048576) + ) { + return; + } + jslint_from_file({ + code, + file: file2, + option + }); + console_error( + "jslint - " + (Date.now() - time_start) + "ms - " + file2 + ); + })); + process_exit(exit_code); + return exit_code; + } + +// jslint_cli - jslint file. + + try { + data = await moduleFs.promises.readFile(file, "utf8"); + } catch (err) { + console_error(err); + exit_code = 1; + process_exit(exit_code); + return exit_code; + } + } + result = jslint_from_file({ + code: data, + file, + option + }); + if (mode_report) { + result = jslint.jslint_report(result); + result = `\n${result}\n`; + await fsWriteFileWithParents(mode_report, result); + } + process_exit(exit_code); + return exit_code; +} + +function jslint_phase1_split() { + +// PHASE 1. Split by newlines into . + + return; +} + +function jslint_phase2_lex(state) { + +// PHASE 2. Lex into . + + let { + artifact, + directive_list, + global_dict, + global_list, + line_list, + option_dict, + stop, + stop_at, + tenure, + test_cause, + token_global, + token_list, + warn, + warn_at + } = state; + let char; // The current character being lexed. + let column = 0; // The column number of the next character. + let from; // The starting column number of the token. + let from_mega; // The starting column of megastring. + let line = 0; // The line number of the next character. + let line_disable; // The starting line of "/*jslint-disable*/". + let line_mega; // The starting line of megastring. + let line_source = ""; // The remaining line source string. + let line_whole = ""; // The whole line source string. + let mode_digits_empty_string = 1; + let mode_digits_numeric_separator = 2; + let mode_directive = true; // true if directives are still allowed. + let mode_mega = false; // true if currently parsing a megastring + // ... literal. + let mode_regexp; // true if regular expression literal seen on + // ... this line. + let paren_backtrack_list = []; // List of most recent "(" tokens at any + // ... paren-depth. + let paren_depth = 0; // Keeps track of current paren-depth. + let snippet = ""; // A piece of string. + let token_1; // The first token. + let token_prv = token_global; // The previous token including + // ... comments. + let token_prv_expr = token_global; // The previous token excluding + // ... comments. + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions char_after, char_before, +// read_digits, and char_after_escape help in the parsing of literals. + + function char_after(match) { + +// Get the next character from the source line. Remove it from the line_source, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + +// test_cause: +// ["aa=/[", "char_after", "expected_a", "]", 5] +// ["aa=/aa{/", "char_after", "expected_a_b", "/", 8] + + return ( + char === "" + ? stop_at("expected_a", line, column - 1, match) + : stop_at("expected_a_b", line, column, match, char) + ); + } + char = line_source.slice(0, 1); + line_source = line_source.slice(1); + snippet += char || " "; + column += 1; + return char; + } + + function char_after_escape(extra) { + +// Validate char after escape "\\". + + char_after("\\"); + switch (char) { + case "": + +// test_cause: +// ["\"\\", "char_after_escape", "unclosed_string", "", 2] + + return stop_at("unclosed_string", line, column); + case "/": + return char_after(); + case "\\": + return char_after(); + case "`": + return char_after(); + case "b": + return char_after(); + case "f": + return char_after(); + case "n": + return char_after(); + case "r": + return char_after(); + case "t": + +// test_cause: +// ["\"\\/\\\\\\`\\b\\f\\n\\r\\t\"", "char_after_escape", "char_after", "", 0] + + test_cause("char_after"); + return char_after(); + case "u": + if (char_after("u") === "{") { + if (state.mode_json) { + +// test_cause: +// ["[\"\\u{12345}\"]", "char_after_escape", "unexpected_a", "{", 5] + + warn_at("unexpected_a", line, column, char); + } + if (read_digits("x", undefined) > 5) { + +// test_cause: +// ["\"\\u{123456}\"", "char_after_escape", "too_many_digits", "", 11] + + warn_at("too_many_digits", line, column); + } + if (char !== "}") { + +// test_cause: +// ["\"\\u{12345\"", "char_after_escape", "expected_a_before_b", "\"", 10] + + stop_at("expected_a_before_b", line, column, "}", char); + } + return char_after(); + } + char_before(); + if (read_digits("x", mode_digits_empty_string) < 4) { + +// test_cause: +// ["\"\\u0\"", "char_after_escape", "expected_four_digits", "", 5] + + warn_at("expected_four_digits", line, column); + } + return; + default: + if (extra && extra.indexOf(char) >= 0) { + return char_after(); + } + +// test_cause: +// ["\"\\0\"", "char_after_escape", "unexpected_a_before_b", "0", 3] + + warn_at("unexpected_a_before_b", line, column, "\\", char); + } + } + + function char_before() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the line_source. + + char = snippet.slice(-1); + line_source = char + line_source; + column -= char.length; + +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + return char; + } + + function check_numeric_separator(digits, column) { + +// This function will check for illegal numeric-separator in . + + digits.replace(( + jslint_rgx_numeric_separator_illegal + ), function (ignore, ii) { + +// test_cause: +// ["0x0_0_;", "check_numeric_separator", "illegal_num_separator", "", 6] +// ["0x0_0__0;", "check_numeric_separator", "illegal_num_separator", "", 6] +// ["aa=1_2_;", "check_numeric_separator", "illegal_num_separator", "", 7] +// ["aa=1_2__3;", "check_numeric_separator", "illegal_num_separator", "", 7] +// ["aa=1_2_n;", "check_numeric_separator", "illegal_num_separator", "", 7] + + warn_at("illegal_num_separator", line, column + ii + 1); + return ""; + }); + } + + function lex_comment() { + let body; + let ii = 0; + let jj = 0; + let the_comment; + +// Create a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + +// Create token from comment //.... + + if (snippet === "//") { + snippet = line_source; + line_source = ""; + the_comment = token_create("(comment)", snippet); + if (mode_mega) { + +// test_cause: +// ["`${//}`", "lex_comment", "unexpected_comment", "`", 4] + + warn("unexpected_comment", the_comment, "`"); + } + +// Create token from comment /*...*/. + + } else { + snippet = []; + if (line_source[0] === "/") { + +// test_cause: +// ["/*/", "lex_comment", "unexpected_a", "/", 2] + + warn_at("unexpected_a", line, column + ii, "/"); + } + +// Lex/loop through each line until "*/". + + while (true) { + // jslint_rgx_star_slash + ii = line_source.indexOf("*/"); + if (ii >= 0) { + break; + } + // jslint_rgx_slash_star + ii = line_source.indexOf("/*"); + if (ii >= 0) { + +// test_cause: +// ["/*/*", "lex_comment", "nested_comment", "", 2] + + warn_at("nested_comment", line, column + ii); + } + snippet.push(line_source); + line_source = read_line(); + if (line_source === undefined) { + +// test_cause: +// ["/*", "lex_comment", "unclosed_comment", "", 1] + + return stop_at("unclosed_comment", line, column); + } + } + jj = line_source.slice(0, ii).search( + jslint_rgx_slash_star_or_slash + ); + if (jj >= 0) { + +// test_cause: +// ["/*/**/", "lex_comment", "nested_comment", "", 2] + + warn_at("nested_comment", line, column + jj); + } + snippet.push(line_source.slice(0, ii)); + snippet = snippet.join(" "); + column += ii + 2; + line_source = line_source.slice(ii + 2); + the_comment = token_create("(comment)", snippet); + } + +// Uncompleted work comment. + + if (!option_dict.devel && jslint_rgx_todo.test(snippet)) { + +// test_cause: +// ["//todo", "lex_comment", "todo_comment", "(comment)", 1] //jslint-ignore-line + + warn("todo_comment", the_comment); + } + +// Lex directives in comment. + + [ + the_comment.directive, body + ] = Array.from(snippet.match(jslint_rgx_directive) || []).slice(1); + if (the_comment.directive === undefined) { + return the_comment; + } + directive_list.push(the_comment); + if (!mode_directive) { + +// test_cause: +// ["0\n/*global aa*/", "lex_comment", "misplaced_directive_a", "global", 1] + + warn_at("misplaced_directive_a", line, from, the_comment.directive); + return the_comment; + } + +// lex_directive(); +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + +// Lex/loop through each directive in /*...*/ + + ii = 0; + body.replace(jslint_rgx_directive_part, function ( + match0, + key, + val, + jj + ) { + if (ii !== jj) { + +// test_cause: +// ["/*jslint !*/", "lex_comment", "bad_directive_a", "!", 1] + + return stop("bad_directive_a", the_comment, body.slice(ii)); + } + if (match0 === "") { + return ""; + } + ii += match0.length; + switch (the_comment.directive) { + case "global": + if (val) { + +// test_cause: +// ["/*global aa:false*/", "lex_comment", "bad_option_a", "aa:false", 1] + + warn("bad_option_a", the_comment, key + ":" + val); + } + global_dict[key] = "user-defined"; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// state.mode_module = the_comment; + + break; + case "jslint": + if (!option_set_item(key, val !== "false")) { + +// test_cause: +// ["/*jslint undefined*/", "lex_comment", "bad_option_a", "undefined", 1] + + warn("bad_option_a", the_comment, key); + } + break; + case "property": + state.mode_property = true; + tenure[key] = true; + break; + } + return ""; + }); + return the_comment; + } + + function lex_megastring() { + let id; + let match; + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (mode_mega) { + +// test_cause: +// ["`${`", "lex_megastring", "expected_a_b", "`", 4] + + return stop_at("expected_a_b", line, column, "}", "`"); + } + from_mega = from; + line_mega = line; + mode_mega = true; + snippet = ""; + +// Parsing a mega literal is tricky. First create a ` token. + + token_create("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + while (true) { + match = line_source.match(jslint_rgx_mega) || { + "0": "", + index: 0 + }; + snippet += line_source.slice(0, match.index); + column += match.index; + line_source = line_source.slice(match.index); + match = match[0]; + switch (match) { + case "${": + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + token_create("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then create tokens that will become part of an expression until +// a } token is made. + + column += 2; + token_create("${"); + line_source = line_source.slice(2); + +// Lex/loop through each token inside megastring-expression `${...}`. + + while (true) { + id = lex_token().id; + if (id === "{") { + +// test_cause: +// ["`${{", "lex_megastring", "expected_a_b", "{", 4] + + return stop_at("expected_a_b", line, column, "}", "{"); + } + if (id === "}") { + break; + } + } + break; + case "\\": + snippet += line_source.slice(0, 2); + line_source = line_source.slice(2); + column += 2; + break; + case "`": + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + token_create("(string)", snippet).quote = "`"; + snippet = ""; + +// Terminate megastring with `. + + line_source = line_source.slice(1); + column += 1; + mode_mega = false; + return token_create("`"); + default: + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + snippet += line_source + "\n"; + if (read_line() === undefined) { + +// test_cause: +// ["`", "lex_megastring", "unclosed_mega", "", 1] + + return stop_at("unclosed_mega", line_mega, from_mega); + } + } + } + } + + function lex_number() { + let prefix = snippet; + +// PR-390 - Add numeric-separator check. + + check_numeric_separator(prefix, column - prefix.length); + char_after(); + switch (prefix === "0" && char) { + case "b": + case "o": + case "x": + read_digits(char, mode_digits_numeric_separator); + +// PR-351 - Ignore BigInt suffix 'n'. + + if (char === "n") { + char_after("n"); + } + break; + default: + if (char === ".") { + read_digits("d", mode_digits_numeric_separator); + } + if (char === "E" || char === "e") { + char_after(char); + if (char !== "+" && char !== "-") { + char_before(); + } + read_digits("d", mode_digits_numeric_separator); + } + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + +// test_cause: +// ["0a", "lex_number", "unexpected_a_after_b", "0", 2] + + return stop_at( + "unexpected_a_after_b", + line, + column, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + char_before(); + return token_create("(number)", snippet); + } + + function lex_regexp() { + +// Regexp +// Lex a regular expression literal. + + let flag; + let mode_regexp_multiline; + let result; + let value; + mode_regexp = true; + + function lex_regexp_bracketed() { + let mode_regexp_range; + +// RegExp +// Match a class. + + char_after("["); + if (char === "^") { + char_after("^"); + } + while (true) { + +// RegExp +// Match a character in a character class. + + switch (char) { + case "": + case "]": + +// test_cause: +// ["aa=/[", "lex_regexp_bracketed", "closer", "", 0] +// ["aa=/[]/", "lex_regexp_bracketed", "closer", "", 0] + + test_cause("closer"); + if (mode_regexp_range) { + +// test_cause: +// ["aa=/[0-]/", "lex_regexp_bracketed", "unexpected_a", "-", 7] + + warn_at("unexpected_a", line, column - 1, "-"); + } + return char_after("]"); + +// PR-362 - Relax regexp-warning against using . +// +// case " ": +// +// // test_cause: +// // ["aa=/[ ]/", "lex_regexp_bracketed", "expected_a_b", " ", 6] +// +// warn_at("expected_a_b", line, column, "\\u0020", " "); +// break; + + case "-": + case "/": + case "[": + case "^": + +// test_cause: +// ["aa=/[-]/", "lex_regexp_bracketed", "expected_a_before_b", "-", 6] +// ["aa=/[.^]/", "lex_regexp_bracketed", "expected_a_before_b", "^", 7] +// ["aa=/[/", "lex_regexp_bracketed", "expected_a_before_b", "/", 6] +// ["aa=/[\\\\/]/", "lex_regexp_bracketed", "expected_a_before_b", "/", 8] +// ["aa=/[\\\\[]/", "lex_regexp_bracketed", "expected_a_before_b", "[", 8] + + warn_at("expected_a_before_b", line, column, "\\", char); + break; + case "\\": + char_after_escape("BbDdSsWw-[]^"); + char_before(); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${/[`]/}`", "lex_regexp_bracketed", "unexpected_a", "`", 6] + + warn_at("unexpected_a", line, column, "`"); + } + break; + } + char_after(); + mode_regexp_range = false; + if (char === "-") { + +// RegExp +// Match a range of subclasses. + + mode_regexp_range = true; + char_after("-"); + } + } + } + + function lex_regexp_group() { + +// RegExp +// Lex sequence of characters in regexp. + + switch (char) { + case "": + warn_at("expected_regexp_factor_a", line, column, char); + break; + case ")": + warn_at("expected_regexp_factor_a", line, column, char); + break; + case "]": + +// test_cause: +// ["/ /", "lex_regexp_group", "expected_regexp_factor_a", "", 3] +// ["aa=/)", "lex_regexp_group", "expected_regexp_factor_a", ")", 5] +// ["aa=/]", "lex_regexp_group", "expected_regexp_factor_a", "]", 5] + + warn_at("expected_regexp_factor_a", line, column, char); + break; + } + while (true) { + switch (char) { + case "": + case ")": + case "/": + case "]": + return; + +// PR-362 - Relax regexp-warning against using . +// +// case " ": +// +// // test_cause: +// // ["aa=/ /", "lex_regexp_group", "expected_a_b", " ", 5] +// +// warn_at("expected_a_b", line, column, "\\s", " "); +// char_after(); +// break; + + case "$": + if (line_source[0] !== "/") { + mode_regexp_multiline = true; + } + char_after(); + break; + case "(": + +// RegExp +// Match a group that starts with left paren. + + char_after("("); + switch (char) { + case ":": + +// test_cause: +// ["aa=/(:)/", "lex_regexp_group", "expected_a_before_b", ":", 6] +// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5] + + warn_at("expected_a_before_b", line, column, "?", ":"); + break; + case "?": + char_after("?"); + switch (char) { + case "!": + +// PR-437 - Add grammar for regexp-named-capture-group. + + case "<": + case "=": + char_after(); + break; + default: + char_after(":"); + } + break; + } + +// RegExp +// Recurse lex_regexp_group(). + + lex_regexp_group(); + char_after(")"); + break; + case "*": + case "+": + case "?": + case "{": + case "}": + +// test_cause: +// ["aa=/+/", "lex_regexp_group", "expected_a_before_b", "+", 5] +// ["aa=/.**/", "lex_regexp_group", "expected_a_before_b", "*", 7] +// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5] +// ["aa=/{/", "lex_regexp_group", "expected_a_before_b", "{", 5] +// ["aa=/}/", "lex_regexp_group", "expected_a_before_b", "}", 5] + + warn_at("expected_a_before_b", line, column, "\\", char); + char_after(); + break; + case "[": + lex_regexp_bracketed(); + break; + case "\\": + +// test_cause: +// ["aa=/\\/", "lex_regexp_group", "escape", "", 0] + + test_cause("escape"); + +// PR-437 - Add grammar for regexp-named-backreference. + + char_after_escape("BbDdSsWw^${}[]():=!.|*+?k"); + break; + case "^": + if (snippet !== "^") { + mode_regexp_multiline = true; + } + char_after(); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${/`/}`", "lex_regexp_group", "unexpected_a", "`", 5] + + warn_at("unexpected_a", line, column, "`"); + } + char_after(); + break; + default: + char_after(); + } + +// RegExp +// Match an optional quantifier. + + switch (char) { + case "*": + case "+": + if (char_after(char) === "?") { + +// test_cause: +// ["aa=/.*?/", "lex_regexp_group", "?", "", 0] +// ["aa=/.+?/", "lex_regexp_group", "?", "", 0] + + test_cause("?"); + char_after("?"); + } + break; + case "?": + if (char_after("?") === "?") { + +// test_cause: +// ["aa=/.??/", "lex_regexp_group", "unexpected_a", "?", 7] + + warn_at("unexpected_a", line, column, char); + char_after("?"); + } + break; + case "{": + if (read_digits("d", mode_digits_empty_string) === 0) { + +// test_cause: +// ["aa=/aa{/", "lex_regexp_group", "expected_a_before_b", ",", 8] + + warn_at("expected_a_before_b", line, column, "0", ","); + } + if (char === ",") { + +// test_cause: +// ["aa=/.{,/", "lex_regexp_group", "comma", "", 0] + + test_cause("comma"); + read_digits("d", mode_digits_empty_string); + } + if (char_after("}") === "?") { + +// test_cause: +// ["aa=/.{0}?/", "lex_regexp_group", "unexpected_a", "?", 9] + + warn_at("unexpected_a", line, column, char); + char_after("?"); + } + break; + } + } + } + +// RegExp +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + char_after(); + if (char === "=") { + +// test_cause: +// ["aa=/=/", "lex_regexp", "expected_a_before_b", "=", 5] + + warn_at("expected_a_before_b", line, column, "\\", "="); + } + lex_regexp_group(); + +// RegExp +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + +// RegExp +// Make sure there is a closing slash. + + value = snippet; + char_after("/"); + +// RegExp +// Create flag. + + flag = empty(); + while ( + +// Regexp +// char is a letter. + + (char >= "a" && char <= "z\uffff") + || (char >= "A" && char <= "Z\uffff") + ) { + +// RegExp +// Process dangling flag letters. + + switch (!flag[char] && char) { + case "g": + break; + case "i": + break; + case "m": + break; + case "u": + break; + case "y": + +// test_cause: +// ["aa=/./gimuy", "lex_regexp", "flag", "", 0] + + test_cause("flag"); + break; + default: + +// test_cause: +// ["aa=/./gg", "lex_regexp", "unexpected_a", "g", 8] +// ["aa=/./z", "lex_regexp", "unexpected_a", "z", 7] + + warn_at("unexpected_a", line, column, char); + } + flag[char] = true; + char_after(); + } + char_before(); + if (char === "/" || char === "*") { + +// test_cause: +// ["aa=/.//", "lex_regexp", "unexpected_a", "/", 3] + + return stop_at("unexpected_a", line, from, char); + } + result = token_create("(regexp)", char); + result.flag = flag; + result.value = value; + if (mode_regexp_multiline && !flag.m) { + +// test_cause: +// ["aa=/$^/", "lex_regexp", "missing_m", "", 7] + + warn_at("missing_m", line, column); + } + return result; + } + + function lex_slash_or_regexp() { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + let the_token; + switch ( + token_prv_expr.identifier + && !token_prv_expr.dot + && token_prv_expr.id + ) { + case "case": + case "delete": + case "in": + case "instanceof": + case "new": + case "typeof": + case "void": + case "yield": + the_token = lex_regexp(); + +// test_cause: +// ["case /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6] +// ["delete /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8] +// ["in /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 4] +// ["instanceof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 12] +// ["new /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 5] +// ["typeof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8] +// ["void /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6] +// ["yield /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 7] + + return stop("unexpected_a", the_token); + case "return": + return lex_regexp(); + } + switch (!token_prv_expr.identifier && token_prv_expr.id.slice(-1)) { + case "!": + case "%": + case "&": + case "*": + case "+": + case "-": + case "/": + case ";": + case "<": + case ">": + case "^": + case "{": + case "|": + case "}": + case "~": + the_token = lex_regexp(); + +// test_cause: +// ["!/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["%/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["&/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["+/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["-/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["0 * /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5] +// ["0 / /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5] +// [";/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["^/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["{/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["|/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["}/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["~/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] + + warn("wrap_regexp", the_token); + return the_token; + case "(": + case ",": + case ":": + case "=": + case "?": + case "[": + +// test_cause: +// ["(/./", "lex_slash_or_regexp", "recurse", "", 0] +// [",/./", "lex_slash_or_regexp", "recurse", "", 0] +// [":/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["=/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["?/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["aa[/./", "lex_slash_or_regexp", "recurse", "", 0] + + test_cause("recurse"); + return lex_regexp(); + } + if (line_source[0] === "=") { + column += 1; + line_source = line_source.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + return token_create(snippet); + } + + function lex_string(quote) { + +// Create a string token. + + let the_token; + if (!option_dict.single && quote === "'") { + +// test_cause: +// ["''", "lex_string", "use_double", "", 1] + + warn_at("use_double", line, column); + } + snippet = ""; + char_after(); + +// Lex/loop through each character in "...". + + while (true) { + switch (char) { + case "": + +// test_cause: +// ["\"", "lex_string", "unclosed_string", "", 1] + + return stop_at("unclosed_string", line, column); + case "\\": + char_after_escape(quote); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${\"`\"}`", "lex_string", "unexpected_a", "`", 5] + + warn_at("unexpected_a", line, column, "`"); + } + char_after("`"); + break; + case quote: + +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + the_token = token_create("(string)", snippet); + the_token.quote = quote; + return the_token; + default: + char_after(); + } + } + } + + function lex_token() { + let match; + +// Lex/loop through each whitespace. + + while (true) { + +// Lex/loop through each blank-line. + + while (!line_source) { + line_source = read_line(); + from = 0; + if (line_source === undefined) { + return ( + mode_mega + +// test_cause: +// ["`${//}`", "lex_token", "unclosed_mega", "", 1] + + ? stop_at("unclosed_mega", line_mega, from_mega) + : line_disable !== undefined + +// test_cause: +// ["/*jslint-disable*/", "lex_token", "unclosed_disable", "", 1] + + ? stop_at("unclosed_disable", line_disable) + : token_create("(end)") + ); + } + } + from = column; + match = line_source.match(jslint_rgx_token); + +// match[1] token +// match[2] whitespace +// match[3] identifier +// match[4] number +// match[5] rest + + if (!match) { + +// test_cause: +// ["#", "lex_token", "unexpected_char_a", "#", 1] + + return stop_at( + "unexpected_char_a", + line, + column, + line_source[0] + ); + } + snippet = match[1]; + column += snippet.length; + line_source = match[5]; + if (!match[2]) { + break; + } + } + +// The token is an identifier. + + if (match[3]) { + return token_create(snippet, undefined, true); + } + +// Create token from number. + + if (match[4]) { + return lex_number(); + } + +// Create token from string "..." or '...'. + + if (snippet === "\"" || snippet === "'") { + return lex_string(snippet); + } + +// Create token from megastring `...`. + + if (snippet === "`") { + return lex_megastring(); + } + +// Create token from comment /*...*/ or //.... + + if (snippet === "/*" || snippet === "//") { + return lex_comment(); + } + +// Create token from slash /. + + if (snippet === "/") { + return lex_slash_or_regexp(); + } + return token_create(snippet); + } + + function option_set_item(key, val) { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + switch (key) { + case "beta": // Enable experimental warnings. + case "bitwise": // Allow bitwise operator. + case "browser": // Assume browser environment. + case "convert": // Allow conversion operator. + case "couch": // Assume CouchDb environment. + case "devel": // Allow console.log() and friends. + case "ecma": // Assume ECMAScript environment. + case "eval": // Allow eval(). + case "fart": // Allow complex fat-arrow. + case "for": // Allow for-statement. + case "getset": // Allow get() and set(). + case "indent2": // Use 2-space indent. + case "long": // Allow long lines. + case "node": // Assume Node.js environment. + case "nomen": // Allow weird property name. + case "single": // Allow single-quote strings. + case "subscript": // Allow identifier in subscript-notation. + case "test_cause": // Test jslint's causes. + case "test_internal_error": // Test jslint's internal-error + // ... handling-ability. + case "this": // Allow 'this'. + case "trace": // Include jslint stack-trace in warnings. + case "unordered": // Allow unordered cases, params, properties, + // ... variables, and exports. + case "variable": // Allow unordered const and let declarations + // ... that are not at top of function-scope. + case "white": // Allow messy whitespace. + option_dict[key] = val; + break; + +// PR-404 - Alias "evil" to jslint-directive "eval" for backwards-compat. + + case "evil": + return option_set_item("eval", val); + +// PR-404 - Alias "nomen" to jslint-directive "name" for backwards-compat. + + case "name": + return option_set_item("nomen", val); + default: + return false; + } + +// Initialize global-variables. + + switch (val && key) { + +// Assign global browser variables to global_dict. +/* +// /\*jslint beta, browser, devel*\/ +console.log(JSON.stringify(Object.keys(window).sort(), undefined, 4)); +*/ + + case "browser": + object_assign_from_list(global_dict, [ + +// Shared with Node.js. + + "AbortController", + // "Buffer", + // "Crypto", + // "CryptoKey", + "Event", + "EventTarget", + "MessageChannel", + "MessageEvent", + "MessagePort", + // "Request", + // "Response", + // "SubtleCrypto", + "TextDecoder", + "TextEncoder", + "URL", + "URLSearchParams", + "WebAssembly", + // "__dirname", + // "__filename", + // "atob", + // "btoa", + // "clearImmediate", + "clearInterval", + "clearTimeout", + // "console", + // "crypto", + // "exports", + // "fetch", + // "global", + // "module", + "performance", + // "process", + "queueMicrotask", + // "require", + // "setImmediate", + "setInterval", + "setTimeout", + +// Web worker only. +// https://github.com/mdn/content/blob/main/files/en-us/web/api +// /workerglobalscope/index.md + + "importScripts", + +// Window. + + "Blob", + // "CharacterData", + // "DocumentType", + // "Element", + // "Event", + "FileReader", + // "FontFace", + "FormData", + "IntersectionObserver", + "MutationObserver", + // "Storage", + // "TextDecoder", + // "TextEncoder", + // "URL", + "Worker", + "XMLHttpRequest", + // "caches", + // "clearInterval", + // "clearTimeout", + "document", + // "event", + "fetch", + // "history", + "indexedDb", + "localStorage", + "location", + // "name", + "navigator", + "postMessage", + // "screen", + "sessionStorage", + // "setInterval", + // "setTimeout", + "structuredClone", + "window" + ], "browser"); + break; + +// https://docs.couchdb.org/en/stable/query-server/javascript.html#javascript + + case "couch": + object_assign_from_list(global_dict, [ + "emit", + "getRow", + "isArray", + "log", + "provides", + "registerType", + "require", + "send", + "start", + "sum", + "toJSON" + ], "CouchDb"); + break; + case "devel": + object_assign_from_list(global_dict, [ + "alert", "confirm", "console", "prompt" + ], "development"); + break; + +// These are the globals that are provided by the language standard. +// Assign global ECMAScript variables to global_dict. +/* +node --input-type=module --eval ' +// /\*jslint beta, node*\/ +import https from "https"; +(async function () { + let dict = {import: true}; + let result = ""; + await new Promise(function (resolve) { + https.get(( + "https://raw.githubusercontent.com/mdn/content/main/files" + + "/en-us/web/javascript/reference/global_objects/index.md" + ), function (res) { + res.on("data", function (chunk) { + result += chunk; + }).on("end", resolve).setEncoding("utf8"); + }); + }); + result.replace(( + /\n- \{\{JSxRef\("(?:Global_Objects\/)?([^"\/]+?)"/g + ), function (ignore, key) { + if (globalThis.hasOwnProperty(key)) { + dict[key] = true; + } + return ""; + }); + console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4)); +}()); +' +*/ + + case "ecma": + object_assign_from_list(global_dict, [ + "AggregateError", + "Array", + "ArrayBuffer", + "Atomics", + "BigInt", + "BigInt64Array", + "BigUint64Array", + "Boolean", + "DataView", + "Date", + "Error", + "EvalError", + "Float32Array", + "Float64Array", + "Function", + "Infinity", + "Int16Array", + "Int32Array", + "Int8Array", + "Intl", + "JSON", + "Map", + "Math", + "NaN", + "Number", + "Object", + "Promise", + "Proxy", + "RangeError", + "ReferenceError", + "Reflect", + "RegExp", + "Set", + "SharedArrayBuffer", + "String", + "Symbol", + "SyntaxError", + "TypeError", + "URIError", + "Uint16Array", + "Uint32Array", + "Uint8Array", + "Uint8ClampedArray", + "WeakMap", + "WeakSet", + "WebAssembly", + "decodeURI", + "decodeURIComponent", + "encodeURI", + "encodeURIComponent", + "eval", + "globalThis", + "import", + "isFinite", + "isNaN", + "parseFloat", + "parseInt", + "undefined" + ], "ECMAScript"); + break; + +// Assign global Node.js variables to global_dict. +/* +node --input-type=module --eval ' +// /\*jslint beta, node*\/ +import moduleHttps from "https"; +(async function () { + let dict = Object.create(null); + let result = ""; + await new Promise(function (resolve) { + moduleHttps.get(( + "https://raw.githubusercontent.com/nodejs/node/v16.x/doc/api" + + "/globals.md" + ), function (res) { + res.on("data", function (chunk) { + result += chunk; + }).on("end", resolve).setEncoding("utf8"); + }); + }); + result.replace(( + /\n(?:\* \[`|## |## Class: )`\w+/g + ), function (match0) { + dict[match0.split("`")[1]] = true; + return ""; + }); + console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4)); +}()); +' +*/ + + case "node": + object_assign_from_list(global_dict, [ + "AbortController", + "Buffer", + // "Crypto", + // "CryptoKey", + "Event", + "EventTarget", + "MessageChannel", + "MessageEvent", + "MessagePort", + // "Request", + // "Response", + // "SubtleCrypto", + "TextDecoder", + "TextEncoder", + "URL", + "URLSearchParams", + "WebAssembly", + "__dirname", + "__filename", + // "atob", + // "btoa", + "clearImmediate", + "clearInterval", + "clearTimeout", + "console", + // "crypto", + "exports", + // "fetch", + "global", + "module", + "performance", + "process", + "queueMicrotask", + "require", + "setImmediate", + "setInterval", + "setTimeout" + ], "Node.js"); + break; + } + return true; + } + + function read_digits(base, mode) { + let digits = line_source.match( + base === "b" + ? jslint_rgx_digits_bits + : base === "o" + ? jslint_rgx_digits_octals + : base === "x" + ? jslint_rgx_digits_hexs + : jslint_rgx_digits_decimals + )[0]; + if ( + (mode !== mode_digits_empty_string && digits.length === 0) + || digits[0] === "_" + ) { + +// test_cause: +// ["0x", "read_digits", "expected_digits_after_a", "0x", 2] +// ["0x_", "read_digits", "expected_digits_after_a", "0x", 2] + + warn_at("expected_digits_after_a", line, column, snippet); + } + +// PR-390 - Add numeric-separator check. + + if (mode === mode_digits_numeric_separator) { + check_numeric_separator(digits, column); + } else if (digits.indexOf("_") >= 0) { + +// test_cause: +// ["\"\\u{1_2}\"", "read_digits", "illegal_num_separator", "", 6] + + warn_at( + "illegal_num_separator", + line, + column + digits.indexOf("_") + 1 + ); + } + column += digits.length; + line_source = line_source.slice(digits.length); + snippet += digits; + char_after(); + return digits.length; + } + + function read_line() { + +// Put the next line of source in line_source. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + if ( + !option_dict.long + && line_whole.length > 80 + && line_disable === undefined + && !state.mode_json + && token_1 + && !mode_regexp + ) { + +// test_cause: +// ["/////////////////////////////////////////////////////////////////////////////////", "read_line", "too_long", "", 1] //jslint-ignore-line + + warn_at("too_long", line); + } + column = 0; + line += 1; + mode_regexp = false; + line_source = undefined; + line_whole = ""; + if (line_list[line] === undefined) { + return line_source; + } + line_source = line_list[line].line_source; + line_whole = line_source; + +// Scan each line for following ignore-directives: +// "/*jslint-disable*/" +// "/*jslint-enable*/" +// "//jslint-ignore-line" + + if (line_source === "/*jslint-disable*/") { + +// test_cause: +// ["/*jslint-disable*/", "read_line", "jslint_disable", "", 0] + + test_cause("jslint_disable"); + line_disable = line; + } else if (line_source === "/*jslint-enable*/") { + if (line_disable === undefined) { + +// test_cause: +// ["/*jslint-enable*/", "read_line", "unopened_enable", "", 1] + + stop_at("unopened_enable", line); + } + line_disable = undefined; + } else if ( + line_source.endsWith(" //jslint-ignore-line") + || line_source.endsWith(" //jslint-quiet") + ) { + +// test_cause: +// ["0 //jslint-ignore-line", "read_line", "jslint_ignore_line", "", 0] + + test_cause("jslint_ignore_line"); + line_list[line].directive_ignore_line = true; + } + if (line_disable !== undefined) { + +// test_cause: +// ["/*jslint-disable*/\n0", "read_line", "line_disable", "", 0] + + test_cause("line_disable"); + line_source = ""; + } + // jslint_rgx_tab + if (line_source.indexOf("\t") >= 0) { + if (!option_dict.white) { + +// test_cause: +// ["\t", "read_line", "use_spaces", "", 1] + + warn_at("use_spaces", line, line_source.indexOf("\t") + 1); + } + line_source = line_source.replace(jslint_rgx_tab, " "); + } + if (!option_dict.white && line_source.endsWith(" ")) { + +// test_cause: +// [" ", "read_line", "unexpected_trailing_space", "", 1] + + warn_at("unexpected_trailing_space", line, line_source.length - 1); + } + return line_source; + } + + function token_create(id, value, identifier) { + +// Create the token object and append it to token_list. + + let the_token = { + from, + id, + identifier: Boolean(identifier), + line, + nr: token_list.length, + thru: column, + value + }; + token_list.push(the_token); + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + mode_directive = false; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option_dict.white. + + if ( + token_prv.line === line + && token_prv.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (token_prv.id === "(comment)" || token_prv.id === "(regexp)") + ) { + +// test_cause: +// ["/**//**/", "token_create", "expected_space_a_b", "(comment)", 5] + + warn( + "expected_space_a_b", + the_token, + artifact(token_prv), + artifact(the_token) + ); + } + if (token_prv.id === "." && id === "(number)") { + +// test_cause: +// [".0", "token_create", "expected_a_before_b", ".", 1] + + warn("expected_a_before_b", token_prv, "0", "."); + } + if (token_prv_expr.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart. +// Farts are now detected by keeping a list of most recent "(" tokens at any +// given depth. When a "=>" token is encountered, the most recent "(" token at +// current depth is marked as a fart. + + switch (id) { + case "(": + paren_backtrack_list[paren_depth] = the_token; + paren_depth += 1; + break; + case ")": + paren_depth -= 1; + break; + case "=>": + if ( + token_prv_expr.id === ")" + && paren_backtrack_list[paren_depth] + ) { + paren_backtrack_list[paren_depth].fart = the_token; + } + break; + } + +// The previous token is used to detect adjacency problems. + + token_prv = the_token; + +// The token_prv_expr token is a previous token that was not a comment. +// The token_prv_expr token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (token_prv.id !== "(comment)") { + token_prv_expr = token_prv; + } + return the_token; + } + +// Init global_dict and option_dict. + + option_set_item("ecma", true); + Object.keys(option_dict).sort().forEach(function (key) { + option_set_item(key, option_dict[key] === true); + }); + object_assign_from_list(global_dict, global_list, "user-defined"); + +// Scan first line for "#!" and ignore it. + + if (line_list[jslint_fudge].line_source.startsWith("#!")) { + line += 1; + state.mode_shebang = true; + } + token_1 = lex_token(); + state.mode_json = token_1.id === "{" || token_1.id === "["; + +// Lex/loop through each token until (end). + + while (true) { + if (lex_token().id === "(end)") { + break; + } + } +} + +function jslint_phase3_parse(state) { + +// PHASE 3. Parse into using the Pratt-parser. + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + + let anon = "anonymous"; // The guessed name for anonymous functions. + let { + artifact, + catch_list, + catch_stack, + export_dict, + function_list, + function_stack, + global_dict, + import_list, + is_equal, + option_dict, + property_dict, + stop, + syntax_dict, + tenure, + test_cause, + token_global, + token_list, + warn, + warn_at + } = state; + let catchage = catch_stack[0]; // The current catch-block. + let functionage = token_global; // The current function. + let mode_var; // "var" if using var; "let" if using let. + let token_ii = 0; // The number of the next token. + let token_now = token_global; // The current token being examined in + // ... the parse. + let token_nxt = token_global; // The next token to be examined in + // ... . + + function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if ( + token_now.identifier + && token_now.id !== "function" + && token_now.id !== "async" + ) { + anon = token_now.id; + } else if ( + token_now.id === "(string)" + && jslint_rgx_identifier.test(token_now.value) + ) { + anon = token_now.value; + } + +// Attempt to match token_nxt with an expected id. + + if (id !== undefined && token_nxt.id !== id) { + return ( + match === undefined + +// test_cause: +// ["{0:0}", "advance", "expected_a_b", "0", 2] + + ? stop("expected_a_b", token_nxt, id, artifact()) + +// test_cause: +// ["{\"aa\":0", "advance", "expected_a_b_from_c_d", "{", 1] + + : stop( + "expected_a_b_from_c_d", + token_nxt, + id, + artifact(match), + match.line, + artifact() + ) + ); + } + +// Promote the tokens, skipping comments. + + token_now = token_nxt; + while (true) { + token_nxt = token_list[token_ii]; + state.token_nxt = token_nxt; + token_ii += 1; + if (token_nxt.id !== "(comment)") { + if (token_nxt.id === "(end)") { + token_ii -= 1; + } + break; + } + if (state.mode_json) { + +// test_cause: +// ["[//]", "advance", "unexpected_a", "(comment)", 2] + + warn("unexpected_a"); + } + } + } + + function assignment(id) { + +// Create an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led_infix = function (left) { + const the_token = token_now; + let right; + the_token.arity = "assignment"; + right = parse_expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "preassign" + || right.arity === "postassign" + ) { + warn("unexpected_a", right); + } + check_mutation(left); + return the_token; + }; + return the_symbol; + } + + function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token_now; + if (special !== "body") { + functionage.statement_prv = the_block; + } + the_block.arity = "statement"; + the_block.body = special === "body"; + +// Top level function bodies may include the "use strict" pragma. + + if ( + special === "body" + && function_stack.length === 1 + && token_nxt.value === "use strict" + ) { + token_nxt.statement = true; + advance("(string)"); + advance(";"); + } + stmts = parse_statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option_dict.devel && special !== "ignore") { + +// test_cause: +// ["function aa(){}", "block", "empty_block", "{", 14] + + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; + } + + function check_left(left, right) { + +// Warn if the left is not one of these: +// ?. +// ?: +// e() +// e.b +// e[b] +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !check_left(left.expression[1]) + && !check_left(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "?." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right || token_nxt); + return false; + } + return true; + } + + function check_mutation(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + +// test_cause: +// ["0=0", "check_mutation", "bad_assignment_a", "0", 1] + + warn("bad_assignment_a", the_thing); + return false; + } + return true; + } + + function check_not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === token_global) { + +// test_cause: +// [" +// while(0){} +// ", "check_not_top_level", "unexpected_at_top_level_a", "while", 1] + + warn("unexpected_at_top_level_a", thing); + } + } + + function check_ordered(type, token_list) { + +// This function will warn if is unordered. + + token_list.reduce(function (aa, token) { + const bb = artifact(token); + if (!option_dict.unordered && aa > bb) { + warn("expected_a_b_before_c_d", token, type, bb, type, aa); + } + return bb; + }, ""); + } + + function check_ordered_case(case_list) { + +// This function will warn if is unordered. + + case_list.filter(noop).map(function (token) { + switch (token.identifier || token.id) { + case "(number)": + return { + order: 1, + token, + type: "number", + value: Number(artifact(token)) + }; + case "(string)": + return { + order: 2, + token, + type: "string", + value: artifact(token) + }; + case true: + return { + order: 3, + token, + type: "identifier", + value: artifact(token) + }; + } + }).reduce(function (aa, bb) { + if ( + +// PR-419 - Hide warning about unordered case-statements behind beta-flag. + + option_dict.beta + && !option_dict.unordered + && aa && bb + && ( + aa.order > bb.order + || (aa.order === bb.order && aa.value > bb.value) + ) + ) { + warn( + "expected_a_b_before_c_d", + bb.token, + `case-${bb.type}`, + bb.value, + `case-${aa.type}`, + aa.value + ); + } + return bb; + }, undefined); + } + + function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = token_nxt; + let the_value; + +// test_cause: +// ["do{}while()", "condition", "", "", 0] +// ["if(){}", "condition", "", "", 0] +// ["while(){}", "condition", "", "", 0] + + test_cause(""); + the_paren.free = true; + advance("("); + the_value = parse_expression(0); + advance(")"); + if (the_value.wrapped === true) { + +// test_cause: +// ["while((0)){}", "condition", "unexpected_a", "(", 6] + + warn("unexpected_a", the_paren); + } + +// Check for anticondition. + + switch (the_value.id) { + case "%": + warn("unexpected_a", the_value); + break; + case "&": + warn("unexpected_a", the_value); + break; + case "(number)": + warn("unexpected_a", the_value); + break; + case "(string)": + warn("unexpected_a", the_value); + break; + case "*": + warn("unexpected_a", the_value); + break; + case "+": + warn("unexpected_a", the_value); + break; + case "-": + warn("unexpected_a", the_value); + break; + case "/": + warn("unexpected_a", the_value); + break; + case "<<": + warn("unexpected_a", the_value); + break; + case ">>": + warn("unexpected_a", the_value); + break; + case ">>>": + warn("unexpected_a", the_value); + break; + case "?": + warn("unexpected_a", the_value); + break; + case "^": + warn("unexpected_a", the_value); + break; + case "typeof": + warn("unexpected_a", the_value); + break; + case "|": + warn("unexpected_a", the_value); + break; + case "~": + +// test_cause: +// ["if(0%0){}", "condition", "unexpected_a", "%", 5] +// ["if(0&0){}", "condition", "unexpected_a", "&", 5] +// ["if(0){}", "condition", "unexpected_a", "0", 4] +// ["if(0*0){}", "condition", "unexpected_a", "*", 5] +// ["if(0+0){}", "condition", "unexpected_a", "+", 5] +// ["if(0-0){}", "condition", "unexpected_a", "-", 5] +// ["if(0/0){}", "condition", "unexpected_a", "/", 5] +// ["if(0<<0){}", "condition", "unexpected_a", "<<", 5] +// ["if(0>>0){}", "condition", "unexpected_a", ">>", 5] +// ["if(0>>>0){}", "condition", "unexpected_a", ">>>", 5] +// ["if(0?0:0){}", "condition", "unexpected_a", "?", 5] +// ["if(0^0){}", "condition", "unexpected_a", "^", 5] +// ["if(0|0){}", "condition", "unexpected_a", "|", 5] +// ["if(\"aa\"){}", "condition", "unexpected_a", "aa", 4] +// ["if(typeof 0){}", "condition", "unexpected_a", "typeof", 4] +// ["if(~0){}", "condition", "unexpected_a", "~", 4] + + warn("unexpected_a", the_value); + break; + } + return the_value; + } + + function constant(id, type, value) { + +// Create a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud_prefix = ( + typeof value === "function" + ? value + : function () { + token_now.constant = true; + if (value !== undefined) { + token_now.value = value; + } + return token_now; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; + } + + function constant_Function() { + if (!option_dict.eval) { + +// test_cause: +// ["Function", "constant_Function", "unexpected_a", "Function", 1] + + warn("unexpected_a", token_now); + } else if (token_nxt.id !== "(") { + +// test_cause: +// [" +// /*jslint eval*/ +// Function +// ", "constant_Function", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "(", artifact()); + } + return token_now; + } + + function constant_arguments() { + +// test_cause: +// ["arguments", "constant_arguments", "unexpected_a", "arguments", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function constant_eval() { + if (!option_dict.eval) { + +// test_cause: +// ["eval", "constant_eval", "unexpected_a", "eval", 1] + + warn("unexpected_a", token_now); + } else if (token_nxt.id !== "(") { + +// test_cause: +// ["/*jslint eval*/\neval", "constant_eval", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "(", artifact()); + } + return token_now; + } + + function constant_ignore() { + +// test_cause: +// ["ignore", "constant_ignore", "unexpected_a", "ignore", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function constant_isInfinite() { + +// test_cause: +// ["isFinite", "constant_isInfinite", "expected_a_b", "isFinite", 1] + + warn("expected_a_b", token_now, "Number.isFinite", "isFinite"); + return token_now; + } + + function constant_isNaN() { + +// test_cause: +// ["isNaN(0)", "constant_isNaN", "number_isNaN", "isNaN", 1] + + warn("number_isNaN", token_now); + return token_now; + } + + function constant_this() { + if (!option_dict.this) { + +// test_cause: +// ["this", "constant_this", "unexpected_a", "this", 1] + + warn("unexpected_a", token_now); + } + return token_now; + } + + function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + let earlier; + let id = name.id; + +// Reserved words may not be enrolled. + + if (syntax_dict[id] !== undefined && id !== "ignore") { + +// test_cause: +// ["let undefined", "enroll", "reserved_a", "undefined", 5] + + warn("reserved_a", name); + return; + } + +// Has the name been enrolled in this context? + + earlier = functionage.context[id] || catchage.context[id]; + if (earlier) { + +// test_cause: +// ["let aa;let aa", "enroll", "redefinition_a_b", "1", 12] + + warn("redefinition_a_b", name, id, earlier.line); + return; + } + +// Has the name been enrolled in an outer context? + + function_stack.forEach(function ({ + context + }) { + earlier = context[id] || earlier; + }); + if (earlier && id === "ignore") { + if (earlier.role === "variable") { + +// test_cause: +// ["let ignore;function aa(ignore){}", "enroll", "redefinition_a_b", "1", 24] + + warn("redefinition_a_b", name, id, earlier.line); + } + } else if ( + earlier + && role !== "parameter" && role !== "function" + && (role !== "exception" || earlier.role !== "exception") + ) { + +// test_cause: +// [" +// function aa(){try{aa();}catch(aa){aa();}} +// ", "enroll", "redefinition_a_b", "1", 31] +// ["function aa(){var aa;}", "enroll", "redefinition_a_b", "1", 19] + + warn("redefinition_a_b", name, id, earlier.line); + } else if ( + option_dict.beta + && global_dict[id] + && role !== "parameter" + ) { + +// test_cause: +// ["let Array", "enroll", "redefinition_global_a_b", "Array", 5] + + warn("redefinition_global_a_b", name, global_dict[id], id); + } + +// Enroll it. + + Object.assign(name, { + dead: true, + init: false, + parent: ( + role === "exception" + ? catchage + : functionage + ), + readonly, + role, + used: 0 + }); + name.parent.context[id] = name; + } + + function infix(bp, id, f) { + +// Create an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led_infix = function (left) { + const the_token = token_now; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, parse_expression(bp)]; + return the_token; + }; + return the_symbol; + } + + function infix_dot(left) { + const the_token = token_now; + let name = token_nxt; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "flat" + && name.id !== "flatMap" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + +// test_cause: +// ["\"\".aa", "check_left", "unexpected_a", ".", 3] + + check_left(left, the_token); + } + if (!name.identifier) { + +// test_cause: +// ["aa.0", "infix_dot", "expected_identifier_a", "0", 4] + + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; + } + + function infix_fart_unwrapped() { + +// test_cause: +// ["aa=>0", "infix_fart_unwrapped", "wrap_fart_parameter", "=>", 3] + + return stop("wrap_fart_parameter", token_now); + } + + function infix_grave(left) { + const the_tick = prefix_tick(); + +// test_cause: +// ["0``", "check_left", "unexpected_a", "`", 2] + + check_left(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; + } + + function infix_lbracket(left) { + const the_token = token_now; + let name; + let the_subscript = parse_expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + name = survey(the_subscript); + +// PR-404 - Add new directive "subscript" to play nice with Google Closure. + + if (!option_dict.subscript && jslint_rgx_identifier.test(name)) { + +// test_cause: +// ["aa[`aa`]", "infix_lbracket", "subscript_a", "aa", 4] + + warn("subscript_a", the_subscript, name); + } + } + +// test_cause: +// ["0[0]", "check_left", "unexpected_a", "[", 2] + + check_left(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; + } + + function infix_lparen(left) { + const the_paren = token_now; + let ellipsis; + let the_argument; + if (left.id !== "function") { + +// test_cause: +// ["(0?0:0)()", "check_left", "unexpected_a", "(", 8] +// ["0()", "check_left", "unexpected_a", "(", 2] + + check_left(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (token_nxt.id !== ")") { + +// Parse/loop through each token in expression (...). + + while (true) { + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = parse_expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + +// test_cause: +// ["aa(0)", "infix_lparen", "free", "", 0] + + test_cause("free"); + the_paren.free = true; + if (the_argument.wrapped === true) { + +// test_cause: +// ["aa((0))", "infix_lparen", "unexpected_a", "(", 3] + + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + +// test_cause: +// ["aa()", "infix_lparen", "not_free", "", 0] +// ["aa(0,0)", "infix_lparen", "not_free", "", 0] + + test_cause("not_free"); + the_paren.free = false; + } + return the_paren; + } + + function infix_option_chain(left) { + const the_token = token_now; + let name = token_nxt; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + +// test_cause: +// ["(0+0)?.0", "infix_option_chain", "check_left", "", 0] + + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + test_cause("check_left"); + +// test_cause: +// ["(/./)?.0", "check_left", "unexpected_a", "?.", 6] +// ["\"aa\"?.0", "check_left", "unexpected_a", "?.", 5] +// ["aa=[]?.aa", "check_left", "unexpected_a", "?.", 6] + + check_left(left, the_token); + } + +// Issue #468 - Fix optional dynamic-property/function-call not recognized. + + if (name.id === "[" || name.id === "(") { + test_cause("dyn_prop_or_call"); + +// test_cause: +// ["aa?.(bb)", "infix_option_chain", "dyn_prop_or_call", "", 0] +// ["aa?.[bb]", "infix_option_chain", "dyn_prop_or_call", "", 0] + + return left; + } + if (!name.identifier) { + +// test_cause: +// ["aa?.0", "infix_option_chain", "expected_identifier_a", "0", 5] + + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; + } + + function infixr(bp, id) { + +// Create a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led_infix = function parse_infixr_led(left) { + const the_token = token_now; + +// test_cause: +// ["0**0", "parse_infixr_led", "led_infix", "", 0] + + test_cause("led_infix"); + the_token.arity = "binary"; + the_token.expression = [left, parse_expression(bp - 1)]; + return the_token; + }; + return the_symbol; + } + + function parse_expression(rbp, initial) { + +// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it +// is looking for ad hoc lint patterns. We add .fud_stmt to Pratt's model, which +// is like .nud_prefix except that it is only used on the first token of a +// statement. Having .fud_stmt makes it much easier to define statement-oriented +// languages like JavaScript. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// .nud_prefix Null denotation. The prefix handler. +// .fud_stmt First null denotation. The statement handler. +// .led_infix Left denotation. The infix/postfix handler. +// lbp Left binding power of infix operator. It tells us how strongly +// the operator binds to the argument at its left. +// rbp Right binding power. + +// It processes a nud_prefix (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop (it +// consumes tokens until it meets a token whose lbp <= rbp). Specifically, it +// means that it collects all tokens that bind together before returning to the +// operator that called it. It returns the expression's parse tree. + +// For example, "3 + 1 * 2 * 4 + 5" +// parses into +// { +// "id": "+", +// "expression": [ +// { +// "id": "+", +// "expression": [ +// { +// "id": "(number)", +// "value": "3" +// }, +// { +// "id": "*", +// "expression": [ +// { +// "id": "*", +// "expression": [ +// { +// "id": "(number)", +// "value": "1" +// }, +// { +// "id": "(number)", +// "value": "2" +// } +// ] +// }, +// { +// "id": "(number)", +// "value": "4" +// } +// ] +// } +// ] +// }, +// { +// "id": "(number)", +// "value": "5" +// } +// ] +// } + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement. + + if (!initial) { + advance(); + } + the_symbol = syntax_dict[token_now.id]; + if (the_symbol !== undefined && the_symbol.nud_prefix !== undefined) { + +// test_cause: +// ["0", "parse_expression", "symbol", "", 0] + + test_cause("symbol"); + left = the_symbol.nud_prefix(); + } else if (token_now.identifier) { + +// test_cause: +// ["aa", "parse_expression", "identifier", "", 0] + + test_cause("identifier"); + left = token_now; + left.arity = "variable"; + } else { + +// test_cause: +// ["!", "parse_expression", "unexpected_a", "(end)", 1] +// ["/./", "parse_expression", "unexpected_a", "/", 1] +// ["let aa=`${}`;", "parse_expression", "unexpected_a", "}", 11] + + return stop("unexpected_a", token_now); + } + +// Parse/loop through each symbol in expression. + + while (true) { + the_symbol = syntax_dict[token_nxt.id]; + if ( + the_symbol === undefined + || the_symbol.led_infix === undefined + || the_symbol.lbp <= rbp + ) { + break; + } + advance(); + left = the_symbol.led_infix(left); + } + return left; + } + + function parse_fart(the_fart) { + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + Object.assign(the_fart, { + arity: "binary", + context: empty(), + finally: 0, + level: functionage.level + 1, + loop: 0, + name: anon, + switch: 0, + try: 0 + }); + +// PR-384 - Relax warning "function_in_loop". +// +// if (functionage.loop > 0) { + +// // test_cause: +// // ["while(0){aa.map(()=>0);}", "parse_fart", "function_in_loop", "=>", 19] +// +// warn("function_in_loop", the_fart); +// } + +// Push the current function context and establish a new one. + + function_list.push(the_fart); + function_stack.push(functionage); + functionage = the_fart; + +// Parse the parameter list. + + prefix_function_parameter(the_fart); + advance("=>"); + +// The function's body is a block. + + if (token_nxt.id === "{") { + if (!option_dict.fart) { + +// test_cause: +// ["()=>{}", "parse_fart", "use_function_not_fart", "=>", 3] + + warn("use_function_not_fart", the_fart); + } + the_fart.block = block("body"); + } else if ( + syntax_dict[token_nxt.id] !== undefined + && syntax_dict[token_nxt.id].fud_stmt !== undefined + ) { + +// PR-384 - Bugfix - Fixes issue #379 - warn against naked-statement in fart. + +// test_cause: +// ["()=>delete aa", "parse_fart", "unexpected_a_after_b", "=>", 5] + + stop("unexpected_a_after_b", token_nxt, token_nxt.id, "=>"); + +// The function's body is an expression. + + } else { + the_fart.expression = parse_expression(0); + } + +// Restore the previous context. + + functionage = function_stack.pop(); + return the_fart; + } + + function parse_json() { + let container; + let is_dup; + let name; + let negative; + switch (token_nxt.id) { + case "(number)": + if (!jslint_rgx_json_number.test(token_nxt.value)) { + +// test_cause: +// ["[-.0]", "parse_json", "unexpected_a", ".", 3] +// ["[-0x0]", "parse_json", "unexpected_a", "0x0", 3] +// ["[.0]", "parse_json", "unexpected_a", ".", 2] +// ["[0x0]", "parse_json", "unexpected_a", "0x0", 2] + + warn("unexpected_a"); + } + advance("(number)"); + return token_now; + case "(string)": + if (token_nxt.quote !== "\"") { + +// test_cause: +// ["['']", "parse_json", "unexpected_a", "'", 2] + + warn("unexpected_a", token_nxt, token_nxt.quote); + } + advance("(string)"); + return token_now; + case "-": + negative = token_nxt; + negative.arity = "unary"; + advance("-"); + +// Recurse parse_json(). + + negative.expression = parse_json(); + return negative; + case "[": + +// test_cause: +// ["[]", "parse_json", "bracket", "", 0] + + test_cause("bracket"); + container = token_nxt; + container.expression = []; + advance("["); + if (token_nxt.id !== "]") { + while (true) { + +// Recurse parse_json(). + + container.expression.push(parse_json()); + if (token_nxt.id !== ",") { + +// test_cause: +// ["[0,0]", "parse_json", "comma", "", 0] + + test_cause("comma"); + break; + } + advance(","); + } + } + advance("]", container); + return container; + case "false": + case "null": + case "true": + +// test_cause: +// ["[false]", "parse_json", "constant", "", 0] +// ["[null]", "parse_json", "constant", "", 0] +// ["[true]", "parse_json", "constant", "", 0] + + test_cause("constant"); + advance(); + return token_now; + case "{": + +// test_cause: +// ["{}", "parse_json", "brace", "", 0] + + test_cause("brace"); + container = token_nxt; + +// Explicit empty-object required to detect "__proto__". + + is_dup = empty(); + container.expression = []; + advance("{"); + if (token_nxt.id !== "}") { + +// JSON +// Parse/loop through each property in {...}. + + while (true) { + if (token_nxt.quote !== "\"") { + +// test_cause: +// ["{0:0}", "parse_json", "unexpected_a", "0", 2] + + warn( + "unexpected_a", + token_nxt, + token_nxt.quote + ); + } + name = token_nxt; + advance("(string)"); + if (is_dup[token_now.value] !== undefined) { + +// test_cause: +// ["{\"aa\":0,\"aa\":0}", "parse_json", "duplicate_a", "aa", 9] + + warn("duplicate_a", token_now); + } else if (token_now.value === "__proto__") { + +// test_cause: +// ["{\"__proto__\":0}", "parse_json", "weird_property_a", "__proto__", 2] + + warn("weird_property_a", token_now); + } else { + is_dup[token_now.value] = token_now; + } + advance(":"); + container.expression.push( + +// Recurse parse_json(). + + Object.assign(parse_json(), { + label: name + }) + ); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance("}", container); + return container; + default: + +// test_cause: +// ["[undefined]", "parse_json", "unexpected_a", "undefined", 2] + + stop("unexpected_a"); + } + } + + function parse_statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token_now.identifier && token_nxt.id === ":") { + the_label = token_now; + if (the_label.id === "ignore") { + +// test_cause: +// ["ignore:", "parse_statement", "unexpected_a", "ignore", 1] + + warn("unexpected_a", the_label); + } + advance(":"); + switch (token_nxt.id) { + case "do": + case "for": + case "switch": + case "while": + +// test_cause: +// ["aa:do{}", "parse_statement", "the_statement_label", "do", 0] +// ["aa:for{}", "parse_statement", "the_statement_label", "for", 0] +// ["aa:switch{}", "parse_statement", "the_statement_label", "switch", 0] +// ["aa:while{}", "parse_statement", "the_statement_label", "while", 0] + + test_cause("the_statement_label", token_nxt.id); + enroll(the_label, "label", true); + the_label.dead = false; + the_label.init = true; + the_statement = parse_statement(); + functionage.statement_prv = the_statement; + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + +// test_cause: +// ["aa:", "parse_statement", "unexpected_label_a", "aa", 1] + + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token_now; + first.statement = true; + the_symbol = syntax_dict[first.id]; + if ( + the_symbol !== undefined + && the_symbol.fud_stmt !== undefined + +// PR-318 - Bugfix - Fixes issues #316, #317 - dynamic-import(). + + && !(the_symbol.id === "import" && token_nxt.id === "(") + ) { + the_symbol.disrupt = false; + the_symbol.statement = true; + token_now.arity = "statement"; + the_statement = the_symbol.fud_stmt(); + functionage.statement_prv = the_statement; + } else { + +// It is an expression statement. + + the_statement = parse_expression(0, true); + functionage.statement_prv = the_statement; + if (the_statement.wrapped && the_statement.id !== "(") { + +// test_cause: +// ["(0)", "parse_statement", "unexpected_a", "(", 1] + + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; + } + + function parse_statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const statement_list = []; + let a_statement; + let disrupt = false; + +// Parse/loop each statement until a statement-terminator is reached. + + while (true) { + switch (token_nxt.id) { + case "(end)": + case "case": + case "default": + case "else": + case "}": + +// test_cause: +// [";", "parse_statements", "closer", "", 0] +// ["case", "parse_statements", "closer", "", 0] +// ["default", "parse_statements", "closer", "", 0] +// ["else", "parse_statements", "closer", "", 0] +// ["}", "parse_statements", "closer", "", 0] + + test_cause("closer"); + return statement_list; + } + a_statement = parse_statement(); + statement_list.push(a_statement); + if (disrupt) { + +// test_cause: +// ["while(0){break;0;}", "parse_statements", "unreachable_a", "0", 16] + + warn("unreachable_a", a_statement); + } + disrupt = a_statement.disrupt; + } + } + + function postassign(id) { + +// Create one of the postassign operators. + + const the_symbol = symbol(id, 150); + the_symbol.led_infix = function (left) { + token_now.expression = left; + token_now.arity = "postassign"; + check_mutation(token_now.expression); + return token_now; + }; + return the_symbol; + } + + function preassign(id) { + +// Create one of the preassign operators. + + const the_symbol = symbol(id); + the_symbol.nud_prefix = function () { + const the_token = token_now; + the_token.arity = "preassign"; + the_token.expression = parse_expression(150); + check_mutation(the_token.expression); + return the_token; + }; + return the_symbol; + } + + function prefix(id, f) { + +// Create a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud_prefix = function () { + const the_token = token_now; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = parse_expression(150); + return the_token; + }; + return the_symbol; + } + + function prefix_assign_divide() { + +// test_cause: +// ["/=", "prefix_assign_divide", "expected_a_b", "/=", 1] + + stop("expected_a_b", token_now, "/\\=", "/="); + } + + function prefix_async() { + let the_async = token_now; + let the_function; + token_nxt.arity = the_async.arity; + +// PR-414 - Parse async fart. + + if (token_nxt.fart) { + advance("("); + the_function = Object.assign(token_now.fart, { + async: 1 + }); + if (!option_dict.fart) { + +// test_cause: +// ["async()=>0", "prefix_async", "use_function_not_fart", "=>", 8] + + warn("use_function_not_fart", the_function); + } + prefix_lparen(); + +// Parse async function. + + } else { + advance("function"); + the_function = Object.assign(token_now, { + async: 1 + }); + prefix_function(); + } + if (the_function.async === 1) { + +// test_cause: +// [" +// async function aa(){} +// ", "prefix_async", "missing_await_statement", "function", 7] + + warn("missing_await_statement", the_function); + } + return the_function; + } + + function prefix_await() { + const the_await = token_now; + +// PR-370 - Add top-level-await support. + + if (functionage.async === 0 && functionage !== token_global) { + +// test_cause: +// ["function aa(){aa=await 0;}", "prefix_await", "unexpected_a", "await", 18] +// ["function aa(){await 0;}", "prefix_await", "unexpected_a", "await", 15] + + warn("unexpected_a", the_await); + } else { + functionage.async += 1; + } + if (the_await.arity === "statement") { + +// PR-405 - Bugfix - fix expression after "await" mis-identified as statement. + + the_await.expression = parse_expression(150); + semicolon(); + } else { + the_await.expression = parse_expression(150); + } + return the_await; + } + + function prefix_fart() { + +// test_cause: +// ["=>0", "prefix_fart", "expected_a_before_b", "=>", 1] + + return stop("expected_a_before_b", token_now, "()", "=>"); + } + + function prefix_function(the_function) { + let name = the_function && the_function.name; + if (the_function === undefined) { + the_function = token_now; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!token_nxt.identifier) { + +// test_cause: +// ["function(){}", "prefix_function", "expected_identifier_a", "(", 9] +// ["function*aa(){}", "prefix_function", "expected_identifier_a", "*", 9] + + return stop("expected_identifier_a"); + } + name = token_nxt; + enroll(name, "variable", true); + the_function.name = Object.assign(name, { + calls: empty(), + +// PR-331 - Bugfix - Fixes issue #272 - function hoisting not allowed. + + dead: false, + init: true + }); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + the_function.name = anon; + if (token_nxt.identifier) { + name = token_nxt; + the_function.name = name; + advance(); + } + } + } + +// Probably deadcode. +// if (mode_mega) { +// warn("unexpected_a", the_function); +// } +// jslint_assert(!mode_mega, `Expected !mode_mega.`); + +// PR-378 - Relax warning "function_in_loop". +// +// // Don't create functions in loops. It is inefficient, and it can lead to +// // scoping errors. +// +// if (functionage.loop > 0) { +// +// // test_cause: +// // [" +// // while(0){aa.map(function(){});} +// // ", "prefix_function", "function_in_loop", "function", 17] +// +// warn("function_in_loop", the_function); +// } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + Object.assign(the_function, { + async: the_function.async || 0, + context: empty(), + finally: 0, + level: functionage.level + 1, + loop: 0, + statement_prv: undefined, + switch: 0, + try: 0 + }); + if (the_function.arity !== "statement" && typeof name === "object") { + +// test_cause: +// ["let aa=function bb(){return;};", "prefix_function", "expression", "bb", 0] + + test_cause("expression", name.id); + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// PR-334 - Bugfix - fix function-redefinition not warned inside function-call. +// Push the current function context and establish a new one. + + function_list.push(the_function); + function_stack.push(functionage); + functionage = the_function; + +// Parse the parameter list. + + advance("("); + token_now.arity = "function"; + prefix_function_parameter(the_function); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && token_nxt.line === token_now.line + ) { + +// test_cause: +// ["function aa(){}0", "prefix_function", "unexpected_a", "0", 16] + + return stop("unexpected_a"); + } + if ( + token_nxt.id === "." + || token_nxt.id === "?." + +// PR-459 - Allow destructuring-assignment after function-definition. + + // || token_nxt.id === "[" + ) { + +// test_cause: +// ["function aa(){}\n.aa", "prefix_function", "unexpected_a", ".", 1] +// ["function aa(){}\n?.aa", "prefix_function", "unexpected_a", "?.", 1] + + warn("unexpected_a"); + } + +// Check functions are ordered. + + check_ordered( + "function", + function_list.slice( + function_list.indexOf(the_function) + 1 + ).map(function ({ + level, + name + }) { + return (level === the_function.level + 1) && name; + }).filter(function (name) { + return option_dict.beta && name && name.id; + }) + ); + +// Restore the previous context. + + functionage = function_stack.pop(); + return the_function; + } + + function prefix_function_parameter(the_function) { + +// This function will parse input at beginning of + + let optional; + let parameters = []; + let signature = ["("]; + let subparam; + function param_enroll(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + +// test_cause: +// ["([aa])=>0", "param_enroll", "use_function_not_fart", "=>", 7] +// ["({aa})=>0", "param_enroll", "use_function_not_fart", "=>", 7] + + if (the_function.id === "=>" && !option_dict.fart) { + warn("use_function_not_fart", the_function); + } + +// Recurse param_enroll(). + + name.names.forEach(param_enroll); + } + } + function param_parse() { + let ellipsis = false; + let param; + if (token_nxt.id === "{") { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,{}){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + param = token_nxt; + param.names = []; + advance("{"); + signature.push("{"); + while (true) { + subparam = token_nxt; + if (!subparam.identifier) { + +// test_cause: +// ["function aa(aa=0,{}){}", "param_parse", "expected_identifier_a", "}", 19] +// ["function aa({0}){}", "param_parse", "expected_identifier_a", "0", 14] + + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (token_nxt.id === ":") { + advance(":"); + advance(); + token_now.label = subparam; + subparam = token_now; + if (!subparam.identifier) { + +// test_cause: +// ["function aa({aa:0}){}", "param_parse", "expected_identifier_a", "}", 18] + + return stop( + "expected_identifier_a", + token_nxt + ); + } + } + +// test_cause: +// ["function aa({aa=aa},aa){}", "param_parse", "equal", "", 0] + + test_cause("equal"); + if (token_nxt.id === "=") { + advance("="); + subparam.expression = parse_expression(); + param.open = true; + } + param.names.push(subparam); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + } else { + break; + } + } + parameters.push(param); + +// test_cause: +// [" +// function aa({bb,aa}){} +// ", "check_ordered", "expected_a_b_before_c_d", "aa", 17] + + check_ordered("parameter", param.names); + advance("}"); + signature.push("}"); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } else if (token_nxt.id === "[") { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,[]){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + param = token_nxt; + param.names = []; + advance("["); + signature.push("[]"); + while (true) { + subparam = token_nxt; + if (!subparam.identifier) { + +// test_cause: +// ["function aa(aa=0,[]){}", "param_parse", "expected_identifier_a", "]", 19] + + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + +// test_cause: +// ["function aa([aa=aa],aa){}", "param_parse", "id", "", 0] + + test_cause("id"); + if (token_nxt.id === "=") { + advance("="); + subparam.expression = parse_expression(); + param.open = true; + } + if (token_nxt.id === ",") { + advance(","); + } else { + break; + } + } + parameters.push(param); + advance("]"); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } else { + if (token_nxt.id === "...") { + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,...){}", "param_parse", "required_a_optional_b", "aa", 21] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + } + if (!token_nxt.identifier) { + +// test_cause: +// ["function aa(0){}", "param_parse", "expected_identifier_a", "0", 13] + + return stop("expected_identifier_a"); + } + param = token_nxt; + parameters.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (token_nxt.id === "=") { + optional = param; + advance("="); + param.expression = parse_expression(0); + } else { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,bb){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } + } + } + +// test_cause: +// ["function aa(){}", "prefix_function_parameter", "opener", "(", 0] + + test_cause("opener", token_now.id); + token_now.free = false; + if (token_nxt.id !== ")" && token_nxt.id !== "(end)") { + param_parse(); + } + advance(")"); + signature.push(")"); + parameters.forEach(param_enroll); + the_function.parameters = parameters; + the_function.signature = signature.join(""); + } + + function prefix_lbrace() { + const seen = empty(); + const the_brace = token_now; + let extra; + let full; + let id; + let name; + let the_colon; + let value; + the_brace.expression = []; + if (token_nxt.id !== "}") { + +// Parse/loop through each property in {...}. + + while (true) { + name = token_nxt; + advance(); + if ( + (name.id === "get" || name.id === "set") + && token_nxt.identifier + ) { + if (!option_dict.getset) { + +// test_cause: +// ["aa={get aa(){}}", "prefix_lbrace", "unexpected_a", "get", 5] + + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + token_nxt.id; + name = token_nxt; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + +// test_cause: +// ["aa={get aa(){},get aa(){}}", "prefix_lbrace", "duplicate_a", "aa", 20] + + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else if (name.id === "`") { + +// test_cause: +// ["aa={`aa`:0}", "prefix_lbrace", "unexpected_a", "`", 5] + + stop("unexpected_a", name); + + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + +// test_cause: +// ["aa={aa,aa}", "prefix_lbrace", "duplicate_a", "aa", 8] + + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (token_nxt.id === "}" || token_nxt.id === ",") { + if (typeof extra === "string") { + +// test_cause: +// ["aa={get aa}", "prefix_lbrace", "closer", "", 0] + + test_cause("closer"); + advance("("); + } + value = parse_expression(Infinity, true); + } else if (token_nxt.id === "(") { + +// test_cause: +// ["aa={aa()}", "prefix_lbrace", "paren", "", 0] +// ["aa={get aa(){}}", "prefix_lbrace", "paren", "", 0] + + test_cause("paren"); + value = prefix_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + +// test_cause: +// ["aa={get aa.aa}", "prefix_lbrace", "paren", "", 0] + + test_cause("paren"); + advance("("); + } + the_colon = token_nxt; + advance(":"); + value = parse_expression(0); + if ( + value.id === name.id + && value.id !== "function" + ) { + +// test_cause: +// ["aa={aa:aa}", "prefix_lbrace", "unexpected_a", ": aa", 7] + + warn("unexpected_a", the_colon, ": " + name.id); + } + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + +// test_cause: +// ["aa={\"aa\":0}", "prefix_lbrace", "colon", "", 0] + + test_cause("colon"); + advance(":"); + value = parse_expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (token_nxt.id !== ",") { + break; + } + +// test_cause: +// ["aa={\"aa\":0,\"bb\":0}", "prefix_lbrace", "comma", "", 0] + + test_cause("comma"); + advance(","); + if (token_nxt.id === "}") { + +// test_cause: +// ["let aa={aa:0,}", "prefix_lbrace", "unexpected_a", ",", 13] + + warn("unexpected_a", token_now); + break; + } + } + } + +// test_cause: +// ["aa={bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8] + + check_ordered( + "property", + the_brace.expression.map(function ({ + label + }) { + return label; + }) + ); + advance("}"); + return the_brace; + } + + function prefix_lbracket() { + const the_token = token_now; + let element; + let ellipsis; + the_token.expression = []; + if (token_nxt.id !== "]") { + +// Parse/loop through each element in [...]. + + while (true) { + ellipsis = false; + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + element = parse_expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (token_nxt.id !== ",") { + break; + } + advance(","); + if (token_nxt.id === "]") { + +// test_cause: +// ["let aa=[0,]", "prefix_lbracket", "unexpected_a", ",", 10] + + warn("unexpected_a", token_now); + break; + } + } + } + advance("]"); + return the_token; + } + + function prefix_lparen() { + let the_paren = token_now; + let the_value; + +// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart. + + if (token_now.fart) { + return parse_fart(token_now.fart); + } + +// test_cause: +// ["(0)", "prefix_lparen", "expr", "", 0] + + test_cause("expr"); + the_paren.free = true; + the_value = parse_expression(0); + if (the_value.wrapped === true) { + +// test_cause: +// ["((0))", "prefix_lparen", "unexpected_a", "(", 1] + + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + return the_value; + } + + function prefix_new() { + const the_new = token_now; + let right; + right = parse_expression(160); + if (token_nxt.id !== "(") { + +// test_cause: +// ["new aa", "prefix_new", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "()", artifact()); + } + the_new.expression = right; + return the_new; + } + + function prefix_tick() { + const the_tick = token_now; + the_tick.value = []; + the_tick.expression = []; + if (token_nxt.id !== "`") { + +// Parse/loop through each token in `${...}`. + + while (true) { + advance("(string)"); + the_tick.value.push(token_now); + if (token_nxt.id !== "${") { + break; + } + advance("${"); + +// test_cause: +// ["let aa=`${}`;", "prefix_tick", "${", "", 0] + + test_cause("${"); + the_tick.expression.push(parse_expression(0)); + advance("}"); + } + } + advance("`"); + return the_tick; + } + + function prefix_void() { + const the_void = token_now; + +// test_cause: +// ["void 0", "prefix_void", "unexpected_a", "void", 1] +// ["void", "prefix_void", "unexpected_a", "void", 1] + + warn("unexpected_a", the_void); + the_void.expression = parse_expression(0); + return the_void; + } + + function semicolon() { + +// Try to match a semicolon. + + if (token_nxt.id === ";") { + advance(";"); + } else { + +// test_cause: +// ["0", "semicolon", "expected_a_b", "(end)", 1] + + warn_at( + "expected_a_b", + token_now.line, + token_now.thru + 1, + ";", + artifact() + ); + } + anon = "anonymous"; + } + + function stmt(id, fud_stmt) { + +// Create a statement. + + const the_symbol = symbol(id); + the_symbol.fud_stmt = fud_stmt; + return the_symbol; + } + + function stmt_break() { + const the_break = token_now; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + +// test_cause: +// ["break", "stmt_break", "unexpected_a", "break", 1] + + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (token_nxt.identifier && token_now.line === token_nxt.line) { + the_label = functionage.context[token_nxt.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + if (the_label !== undefined && the_label.dead) { + +// test_cause: +// ["aa:{function aa(aa){break aa;}}", "stmt_break", "out_of_scope_a", "aa", 27] + + warn("out_of_scope_a"); + } else { + +// test_cause: +// ["aa:{break aa;}", "stmt_break", "not_label_a", "aa", 11] + + warn("not_label_a"); + } + } else { + the_label.used += 1; + } + the_break.label = token_nxt; + advance(); + } + advance(";"); + return the_break; + } + + function stmt_continue() { + const the_continue = token_now; + if (functionage.loop < 1 || functionage.finally > 0) { + +// test_cause: +// ["continue", "stmt_continue", "unexpected_a", "continue", 1] +// [" +// function aa(){while(0){try{}finally{continue}}} +// ", "stmt_continue", "unexpected_a", "continue", 37] + + warn("unexpected_a", the_continue); + } + check_not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; + } + + function stmt_debugger() { + const the_debug = token_now; + if (!option_dict.devel) { + +// test_cause: +// ["debugger", "stmt_debugger", "unexpected_a", "debugger", 1] + + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; + } + + function stmt_delete() { + const the_token = token_now; + const the_value = parse_expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + +// test_cause: +// ["delete 0", "stmt_delete", "expected_a_b", "0", 8] + + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; + } + + function stmt_do() { + const the_do = token_now; + check_not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + +// test_cause: +// ["function aa(){do{break;}while(0)}", "stmt_do", "weird_loop", "do", 15] + + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; + } + + function stmt_export() { + let export_list = []; + let the_export = token_now; + let the_id; + let the_name; + let the_thing; + + the_export.expression = []; + if (token_nxt.id === "default") { + if (export_dict.default !== undefined) { + +// test_cause: +// [" +// export default 0;export default 0 +// ", "stmt_export", "duplicate_a", "default", 25] + + warn("duplicate_a"); + } + advance("default"); + the_thing = parse_expression(0); + if ( + the_thing.id !== "(" + || the_thing.expression[0].id !== "." + || the_thing.expression[0].expression.id !== "Object" + || the_thing.expression[0].name.id !== "freeze" + ) { + +// test_cause: +// ["export default {}", "stmt_export", "freeze_exports", "{", 16] + + warn("freeze_exports", the_thing); + +// PR-301 - Bugfix - Fixes issues #282 - optional-semicolon. + + } else { + +// test_cause: +// [" +// export default Object.freeze({}) +// ", "semicolon", "expected_a_b", "(end)", 32] + + semicolon(); + } + export_dict.default = the_thing; + the_export.expression.push(the_thing); + } else { + +// PR-439 - Add grammar for "export async function ...". + + if (token_nxt.id === "function" || token_nxt.id === "async") { + +// test_cause: +// ["export async function aa(){}", "stmt_export", "freeze_exports", "async", 8] +// ["export function aa(){}", "stmt_export", "freeze_exports", "function", 8] + + warn("freeze_exports"); + the_thing = parse_statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (export_dict[the_id] !== undefined) { + +// test_cause: +// [" +// let aa;export{aa};export function aa(){} +// ", "stmt_export", "duplicate_a", "aa", 35] + + warn("duplicate_a", the_name); + } + export_dict[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + token_nxt.id === "var" + || token_nxt.id === "let" + || token_nxt.id === "const" + ) { + +// test_cause: +// ["export const", "stmt_export", "unexpected_a", "const", 8] +// ["export let", "stmt_export", "unexpected_a", "let", 8] +// ["export var", "stmt_export", "unexpected_a", "var", 8] + + warn("unexpected_a"); + parse_statement(); + } else if (token_nxt.id === "{") { + +// test_cause: +// ["export {}", "stmt_export", "advance{", "", 0] + + test_cause("advance{"); + advance("{"); + while (true) { + if (!token_nxt.identifier) { + +// test_cause: +// ["export {}", "stmt_export", "expected_identifier_a", "}", 9] + + stop("expected_identifier_a"); + } + the_id = token_nxt.id; + export_list.push(token_nxt); + the_name = token_global.context[the_id]; + if (the_name === undefined) { + +// test_cause: +// ["export {aa}", "stmt_export", "unexpected_a", "aa", 9] + + warn("unexpected_a"); + } else { + the_name.used += 1; + if (export_dict[the_id] !== undefined) { + +// test_cause: +// ["let aa;export{aa,aa}", "stmt_export", "duplicate_a", "aa", 18] + + warn("duplicate_a"); + } + export_dict[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + if (token_nxt.id === ",") { + advance(","); + } else { + break; + } + } + +// PR-439 - Check exported properties are ordered. + +// test_cause: +// ["export {bb, aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 13] + + check_ordered("export", export_list); + advance("}"); + semicolon(); + } else { + +// test_cause: +// ["export", "stmt_export", "unexpected_a", "(end)", 1] + + stop("unexpected_a"); + } + } + state.mode_module = true; + return the_export; + } + + function stmt_for() { + let first; + let the_for = token_now; + if (!option_dict.for) { + +// test_cause: +// ["for", "stmt_for", "unexpected_a", "for", 1] + + warn("unexpected_a", the_for); + } + check_not_top_level(the_for); + functionage.loop += 1; + advance("("); + token_now.free = true; + if (token_nxt.id === ";") { + +// test_cause: +// ["for(;;){}", "stmt_for", "expected_a_b", "for (;", 1] + + return stop("expected_a_b", the_for, "while (", "for (;"); + } + switch (token_nxt.id) { + case "const": + case "let": + case "var": + +// test_cause: +// ["for(const aa in aa){}", "stmt_for", "unexpected_a", "const", 5] + + return stop("unexpected_a"); + } + first = parse_expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + +// test_cause: +// ["for(0 in aa){}", "stmt_for", "bad_assignment_a", "0", 5] + + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = parse_expression(0); + advance(";"); + the_for.inc = parse_expression(0); + if (the_for.inc.id === "++") { + +// test_cause: +// ["for(aa;aa;aa++){}", "stmt_for", "expected_a_b", "++", 13] + + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + +// test_cause: +// [" +// /*jslint for*/ +// function aa(bb,cc){for(0;0;0){break;}} +// ", "stmt_for", "weird_loop", "for", 20] + + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; + } + + function stmt_if() { + const the_if = token_now; + let the_else; + the_if.expression = condition(); + the_if.block = block(); + if (token_nxt.id === "else") { + advance("else"); + the_else = token_now; + the_if.else = ( + token_nxt.id === "if" + ? parse_statement() + : block() + ); + +// test_cause: +// ["if(0){0}else if(0){0}", "stmt_if", "else", "", 0] +// ["if(0){0}else{0}", "stmt_if", "else", "", 0] + + test_cause("else"); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + +// test_cause: +// ["if(0){break;}else{break;}", "stmt_if", "disrupt", "", 0] + + test_cause("disrupt"); + the_if.disrupt = true; + } else { + +// test_cause: +// ["if(0){break;}else{}", "stmt_if", "unexpected_a", "else", 14] + + warn("unexpected_a", the_else); + } + } + } + return the_if; + } + + function stmt_import() { + const the_import = token_now; + let name; + let names; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// if (typeof state.mode_module === "object") { +// +// // test_cause: +// // [" +// // /*global aa*/ +// // import aa from "aa" +// // ", "stmt_import", "unexpected_directive_a", "global", 1] +// +// warn( +// "unexpected_directive_a", +// state.mode_module, +// state.mode_module.directive +// ); +// } + + state.mode_module = true; + +// PR-436 - Add grammar for side-effect import-statement. + + if (token_nxt.id === "(string)") { + +// test_cause: +// ["import \"./aa.mjs\";", "stmt_import", "import_side_effect", "", 0] + + test_cause("import_side_effect"); + warn("expected_a_b", token_nxt, "{", artifact()); + advance(); + semicolon(); + return the_import; + } + if (token_nxt.identifier) { + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// ["import ignore from \"aa\"", "stmt_import", "unexpected_a", "ignore", 8] + + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + names = []; + advance("{"); + if (token_nxt.id !== "}") { + while (true) { + if (!token_nxt.identifier) { + +// test_cause: +// ["import {", "stmt_import", "expected_identifier_a", "(end)", 1] + + stop("expected_identifier_a"); + } + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// ["import {ignore} from \"aa\"", "stmt_import", "unexpected_a", "ignore", 9] + + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token_now; + if (!jslint_rgx_module.test(token_now.value)) { + +// test_cause: +// ["import aa from \"!aa\"", "stmt_import", "bad_module_name_a", "!aa", 16] + + warn("bad_module_name_a", token_now); + } + import_list.push(token_now.value); + semicolon(); + return the_import; + } + + function stmt_lbrace() { + +// test_cause: +// [";{}", "stmt_lbrace", "naked_block", "{", 2] +// ["class aa{}", "stmt_lbrace", "naked_block", "{", 9] + + warn("naked_block", token_now); + return block("naked"); + } + + function stmt_return() { + const the_return = token_now; + check_not_top_level(the_return); + if (functionage.finally > 0) { + +// test_cause: +// [" +// function aa(){try{}finally{return;}} +// ", "stmt_return", "unexpected_a", "return", 28] + + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (token_nxt.id !== ";" && the_return.line === token_nxt.line) { + the_return.expression = parse_expression(10); + } + advance(";"); + return the_return; + } + + function stmt_semicolon() { + +// test_cause: +// [";", "stmt_semicolon", "unexpected_a", ";", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function stmt_switch() { + const the_cases = []; + const the_switch = token_now; + let dups = []; + let exp; + let last; + let stmts; + let the_case; + let the_default; + let the_disrupt = true; + let the_last; + function is_dup(thing) { + return is_equal(thing, exp); + } + check_not_top_level(the_switch); + if (functionage.finally > 0) { + +// test_cause: +// [" +// function aa(){try{}finally{switch(0){}}} +// ", "stmt_switch", "unexpected_a", "switch", 28] + + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token_now.free = true; + the_switch.expression = parse_expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + while (true) { + +// Loop through cases with breaks. + + the_case = token_nxt; + the_case.arity = "statement"; + the_case.expression = []; + while (true) { + +// Loop through fallthrough cases. + + advance("case"); + token_now.switch = true; + exp = parse_expression(0); + if (dups.some(is_dup)) { + +// test_cause: +// [" +// switch(0){case 0:break;case 0:break} +// ", "stmt_switch", "unexpected_a", "0", 29] + + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (token_nxt.id !== "case") { + break; + } + } + +// test_cause: +// [" +// switch(0){case 1:case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 23] +// [" +// switch(0){case "aa":case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 26] +// [" +// switch(0){case "bb":case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 26] +// [" +// switch(0){case aa:case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24] +// [" +// switch(0){case bb:case aa:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24] + + check_ordered_case(the_case.expression); + stmts = parse_statements(); + if (stmts.length < 1) { + +// test_cause: +// [" +// switch(0){case 0:default:} +// ", "stmt_switch", "expected_statements_a", "default", 18] +// ["switch(0){case 0:}", "stmt_switch", "expected_statements_a", "}", 18] + + warn("expected_statements_a"); + +// PR-359 - Bugfix - Fixes issue #358 - switch-statement crashes jslint. + + break; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn("expected_a_before_b", token_nxt, "break;", artifact()); + } + if (token_nxt.id !== "case") { + break; + } + } + +// test_cause: +// [" +// switch(0){case 1:break;case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 29] +// [" +// switch(0){case "aa":break;case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 32] +// [" +// switch(0){case "bb":break;case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 32] +// [" +// switch(0){case aa:break;case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30] +// [" +// switch(0){case bb:break;case aa:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30] + + check_ordered_case(the_cases.map(function ({ + expression + }) { + return expression[0]; + })); + dups = undefined; + if (token_nxt.id === "default") { + the_default = token_nxt; + advance("default"); + token_now.switch = true; + advance(":"); + the_switch.else = parse_statements(); + if (the_switch.else.length < 1) { + +// test_cause: +// [" +// switch(0){case 0:break;default:} +// ", "stmt_switch", "unexpected_a", "default", 24] + + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + the_last = the_switch.else[ + the_switch.else.length - 1 + ]; + if ( + the_last.id === "break" + && the_last.label === undefined + ) { + +// test_cause: +// [" +// switch(0){case 0:break;default:break;} +// ", "stmt_switch", "unexpected_a", "break", 32] + + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; + } + + function stmt_throw() { + const the_throw = token_now; + the_throw.disrupt = true; + the_throw.expression = parse_expression(10); + semicolon(); + if (functionage.try > 0) { + +// test_cause: +// ["try{throw 0}catch(){}", "stmt_throw", "unexpected_a", "throw", 5] + + warn("unexpected_a", the_throw); + } + return the_throw; + } + + function stmt_try() { + const the_try = token_now; + let ignored; + let the_catch; + let the_disrupt; + if (functionage.try > 0) { + +// test_cause: +// ["try{try{}catch(){}}catch(){}", "stmt_try", "unexpected_a", "try", 5] + + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (token_nxt.id === "catch") { + ignored = "ignore"; + the_catch = token_nxt; + the_try.catch = the_catch; + advance("catch"); + +// Create new catch-scope for catch-parameter. + + catch_stack.push(catchage); + catchage = the_catch; + catch_list.push(catchage); + the_catch.context = empty(); + if (token_nxt.id === "(") { + advance("("); + if (!token_nxt.identifier) { + +// test_cause: +// ["try{}catch(){}", "stmt_try", "expected_identifier_a", ")", 12] + + return stop("expected_identifier_a"); + } + if (token_nxt.id !== "ignore") { + ignored = undefined; + the_catch.name = token_nxt; + enroll(token_nxt, "exception", true); + } + advance(); + advance(")"); + } + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + +// Restore previous catch-scope after catch-block. + + catchage = catch_stack.pop(); + +// PR-404 - Relax warning about missing `catch` in `try...finally` statement. +// +// } else { +// +// // test_cause: +// // ["try{}finally{break;}", "stmt_try", "expected_a_before_b", "finally", 6] +// +// warn("expected_a_before_b", token_nxt, "catch", artifact()); + + } + if (token_nxt.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; + } + + function stmt_var() { + let ellipsis; + let mode_const; + let name; + let the_brace; + let the_bracket; + let the_variable = token_now; + let variable_prv; + mode_const = the_variable.id === "const"; + the_variable.names = []; + +// A program may use var or let, but not both. + + if (!mode_const) { + if (mode_var === undefined) { + mode_var = the_variable.id; + } else if (the_variable.id !== mode_var) { + +// test_cause: +// ["let aa;var aa", "stmt_var", "expected_a_b", "var", 8] + + warn("expected_a_b", the_variable, mode_var, the_variable.id); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + +// test_cause: +// ["switch(0){case 0:var aa}", "stmt_var", "var_switch", "var", 18] + + warn("var_switch", the_variable); + } + switch ( + Boolean(functionage.statement_prv) + && functionage.statement_prv.id + ) { + case "const": + case "let": + case "var": + +// test_cause: +// ["const aa=0;const bb=0;", "stmt_var", "var_prv", "const", 0] +// ["let aa=0;let bb=0;", "stmt_var", "var_prv", "let", 0] +// ["var aa=0;var bb=0;", "stmt_var", "var_prv", "var", 0] + + test_cause("var_prv", functionage.statement_prv.id); + variable_prv = functionage.statement_prv; + break; + case "import": + +// test_cause: +// ["import aa from \"aa\";\nlet bb=0;", "stmt_var", "import_prv", "", 0] + + test_cause("import_prv"); + break; + case false: + break; + default: + if ( + (option_dict.beta && !option_dict.variable) + || the_variable.id === "var" + ) { + +// test_cause: +// ["console.log();let aa=0;", "stmt_var", "var_on_top", "let", 15] +// ["console.log();var aa=0;", "stmt_var", "var_on_top", "var", 15] +// ["try{aa();}catch(aa){var aa=0;}", "stmt_var", "var_on_top", "var", 21] +// ["while(0){var aa;}", "stmt_var", "var_on_top", "var", 10] + + warn("var_on_top", token_now); + } + } + while (true) { + if (token_nxt.id === "{") { + if (the_variable.id === "var") { + +// test_cause: +// ["var{aa}=0", "stmt_var", "unexpected_a", "var", 1] + + warn("unexpected_a", the_variable); + } + the_brace = token_nxt; + advance("{"); + while (true) { + name = token_nxt; + if (!name.identifier) { + +// test_cause: +// ["let {0}", "stmt_var", "expected_identifier_a", "0", 6] + + return stop("expected_identifier_a"); + } + survey(name); + advance(); + if (token_nxt.id === ":") { + advance(":"); + if (!token_nxt.identifier) { + +// test_cause: +// ["let {aa:0}", "stmt_var", "expected_identifier_a", "0", 9] +// ["let {aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 9] + + return stop("expected_identifier_a"); + } + +// PR-363 - Bugfix +// Add test against false-warning in code +// '/*jslint node*/\nlet {aa:bb} = {}; bb();'. +// +// token_nxt.label = name; +// the_variable.names.push(token_nxt); +// enroll(token_nxt, "variable", mode_const); + + name = token_nxt; + the_variable.names.push(name); + survey(name); + enroll(name, "variable", mode_const); + + advance(); + the_brace.open = true; + } else { + the_variable.names.push(name); + enroll(name, "variable", mode_const); + } + name.dead = false; + name.init = true; + if (token_nxt.id === "=") { + +// test_cause: +// ["let {aa=0}", "stmt_var", "assign", "", 0] + + test_cause("assign"); + advance("="); + name.expression = parse_expression(); + the_brace.open = true; + } + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + +// test_cause: +// ["let{bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8] + + check_ordered(the_variable.id, the_variable.names); + advance("}"); + advance("="); + the_variable.expression = parse_expression(0); + } else if (token_nxt.id === "[") { + if (the_variable.id === "var") { + +// test_cause: +// ["var[aa]=0", "stmt_var", "unexpected_a", "var", 1] + + warn("unexpected_a", the_variable); + } + the_bracket = token_nxt; + advance("["); + while (true) { + ellipsis = false; + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + if (!token_nxt.identifier) { + +// test_cause: +// ["let[]", "stmt_var", "expected_identifier_a", "]", 5] + + return stop("expected_identifier_a"); + } + name = token_nxt; + advance(); + the_variable.names.push(name); + enroll(name, "variable", mode_const); + name.dead = false; + name.init = true; + if (ellipsis) { + name.ellipsis = true; + break; + } + if (token_nxt.id === "=") { + advance("="); + name.expression = parse_expression(); + the_bracket.open = true; + } + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + advance("]"); + advance("="); + the_variable.expression = parse_expression(0); + } else if (token_nxt.identifier) { + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// [" +// let ignore;function aa(ignore) {} +// ", "stmt_var", "unexpected_a", "ignore", 5] + + warn("unexpected_a", name); + } + enroll(name, "variable", mode_const); + if (token_nxt.id === "=" || mode_const) { + advance("="); + name.dead = false; + name.init = true; + name.expression = parse_expression(0); + } + the_variable.names.push(name); + } else { + +// test_cause: +// ["let 0", "stmt_var", "expected_identifier_a", "0", 5] +// ["var{aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 8] + + return stop("expected_identifier_a"); + } + if (token_nxt.id !== ",") { + break; + } + +// test_cause: +// ["let aa,bb;", "stmt_var", "expected_a_b", ",", 7] + + warn("expected_a_b", token_nxt, ";", ","); + advance(","); + } + +// Warn if variable declarations are unordered. + + if ( + option_dict.beta + && !option_dict.unordered + && !option_dict.variable + && variable_prv + && ( + variable_prv.id + " " + variable_prv.names[0].id + > the_variable.id + " " + the_variable.names[0].id + ) + ) { + +// test_cause: +// ["const bb=0;const aa=0;", "stmt_var", "expected_a_b_before_c_d", "aa", 12] +// ["let bb;let aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8] +// ["var bb;var aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8] + + warn( + "expected_a_b_before_c_d", + the_variable, + the_variable.id, + the_variable.names[0].id, + variable_prv.id, + variable_prv.names[0].id + ); + } + semicolon(); + return the_variable; + } + + function stmt_while() { + const the_while = token_now; + check_not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + +// test_cause: +// ["function aa(){while(0){break;}}", "stmt_while", "weird_loop", "while", 15] + + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; + } + + function stmt_with() { + +// test_cause: +// ["with", "stmt_with", "unexpected_a", "with", 1] + + stop("unexpected_a", token_now); + } + + function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!jslint_rgx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!jslint_rgx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + +// test_cause: +// ["let aa={0:0}", "survey", "expected_identifier_a", "0", 9] + + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property_dict[id] === "number") { + property_dict[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (state.mode_property) { + if (tenure[id] !== true) { + +// test_cause: +// ["/*property aa*/\naa.bb", "survey", "unregistered_property_a", "bb", 4] + + warn("unregistered_property_a", name); + } + } else if ( + !option_dict.nomen + && name.identifier + && jslint_rgx_weird_property.test(id) + ) { + +// test_cause: +// ["aa.$", "survey", "weird_property_a", "$", 4] +// ["aa._", "survey", "weird_property_a", "_", 4] +// ["aa._aa", "survey", "weird_property_a", "_aa", 4] +// ["aa.aaSync", "survey", "weird_property_a", "aaSync", 4] +// ["aa.aa_", "survey", "weird_property_a", "aa_", 4] + + warn("weird_property_a", name); + } + property_dict[id] = 1; + } + return id; + } + +// These functions are used to specify the grammar of our language: + + function symbol(id, bp) { + +// Create a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax_dict[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax_dict[id] = the_symbol; + } + return the_symbol; + } + + function ternary(id1, id2) { + +// Create a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led_infix = function parse_ternary_led(left) { + const the_token = token_now; + let second; + second = parse_expression(20); + advance(id2); + token_now.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, parse_expression(10)]; + if (token_nxt.id !== ")") { + +// test_cause: +// ["0?0:0", "parse_ternary_led", "use_open", "?", 2] + + warn("use_open", the_token); + } + return the_token; + }; + return the_symbol; + } + +// Now we parse JavaScript. +// Begin defining the language. + + assignment("%="); + assignment("&="); + assignment("*="); + assignment("+="); + assignment("-="); + assignment("/="); + assignment("<<="); + assignment("="); + assignment(">>="); + assignment(">>>="); + assignment("^="); + assignment("|="); + constant("(number)", "number"); + constant("(regexp)", "regexp"); + constant("(string)", "string"); + constant("Function", "function", constant_Function); + constant("Infinity", "number", Infinity); + constant("NaN", "number", NaN); + constant("arguments", "object", constant_arguments); + constant("eval", "function", constant_eval); + constant("false", "boolean", false); + constant("ignore", "undefined", constant_ignore); + constant("isFinite", "function", constant_isInfinite); + constant("isNaN", "function", constant_isNaN); + constant("null", "null", null); + constant("this", "object", constant_this); + constant("true", "boolean", true); + constant("undefined", "undefined"); + infix(100, "!="); + infix(100, "!=="); + infix(100, "=="); + infix(100, "==="); + infix(110, "<"); + infix(110, "<="); + infix(110, ">"); + infix(110, ">="); + infix(110, "in"); + infix(110, "instanceof"); + infix(120, "<<"); + infix(120, ">>"); + infix(120, ">>>"); + infix(130, "+"); + infix(130, "-"); + infix(140, "%"); + infix(140, "*"); + infix(140, "/"); + infix(160, "(", infix_lparen); + infix(160, "`", infix_grave); + infix(170, ".", infix_dot); + infix(170, "=>", infix_fart_unwrapped); + infix(170, "?.", infix_option_chain); + infix(170, "[", infix_lbracket); + infix(35, "??"); + infix(40, "||"); + infix(50, "&&"); + infix(70, "|"); + infix(80, "^"); + infix(90, "&"); + infixr(150, "**"); + postassign("++"); + postassign("--"); + preassign("++"); + preassign("--"); + prefix("!!"); + prefix("!"); + prefix("(", prefix_lparen); + prefix("+"); + prefix("-"); + prefix("/=", prefix_assign_divide); + prefix("=>", prefix_fart); + prefix("[", prefix_lbracket); + prefix("`", prefix_tick); + prefix("async", prefix_async); + prefix("await", prefix_await); + prefix("function", prefix_function); + prefix("new", prefix_new); + prefix("typeof"); + prefix("void", prefix_void); + prefix("{", prefix_lbrace); + prefix("~"); + stmt(";", stmt_semicolon); + stmt("async", prefix_async); + stmt("await", prefix_await); + stmt("break", stmt_break); + stmt("const", stmt_var); + stmt("continue", stmt_continue); + stmt("debugger", stmt_debugger); + stmt("delete", stmt_delete); + stmt("do", stmt_do); + stmt("export", stmt_export); + stmt("for", stmt_for); + stmt("function", prefix_function); + stmt("if", stmt_if); + stmt("import", stmt_import); + stmt("let", stmt_var); + stmt("return", stmt_return); + stmt("switch", stmt_switch); + stmt("throw", stmt_throw); + stmt("try", stmt_try); + stmt("var", stmt_var); + stmt("while", stmt_while); + stmt("with", stmt_with); + stmt("{", stmt_lbrace); + symbol(")"); + symbol("*/"); + symbol(","); + symbol(":"); + symbol(";"); + symbol("]"); + symbol("async"); + symbol("await"); + symbol("case"); + symbol("catch"); + symbol("class"); + symbol("default"); + symbol("else"); + symbol("enum"); + symbol("finally"); + symbol("implements"); + symbol("interface"); + symbol("package"); + symbol("private"); + symbol("protected"); + symbol("public"); + symbol("static"); + symbol("super"); + symbol("void"); + symbol("yield"); + symbol("}"); + ternary("?", ":"); + +// Init token_nxt. + + advance(); + +// Parsing of JSON is simple: + + if (state.mode_json) { + state.token_tree = parse_json(); + advance("(end)"); + return; + } + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option_dict.browser) { + if (token_nxt.id === ";") { + advance(";"); + } + +// If we are not in a browser, then the file form of strict pragma may be used. + + } else if (token_nxt.value === "use strict") { + advance("(string)"); + advance(";"); + } + state.token_tree = parse_statements(); + advance("(end)"); + +// Check global functions are ordered. + + check_ordered( + "function", + function_list.map(function ({ + level, + name + }) { + return (level === 1) && name; + }).filter(function (name) { + return option_dict.beta && name && name.id; + }) + ); +} + +function jslint_phase4_walk(state) { + +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). + + let { + artifact, + catch_stack, + function_stack, + global_dict, + is_equal, + is_weird, + option_dict, + syntax_dict, + test_cause, + token_global, + warn + } = state; + let block_stack = []; // The stack of blocks. + let blockage = token_global; // The current block. + let catchage = catch_stack[0]; // The current catch-block. + let functionage = token_global; // The current function. + let postaction; + let postamble; + let posts = empty(); + let preaction; + let preamble; + let pres = empty(); + +// The relational operators. + + let relationop = object_assign_from_list(empty(), [ + "!=", "!==", "<", "<=", "==", "===", ">", ">=" + ], true); + +// Ambulation of the parse tree. + + function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; + } + + function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + task(the_token); + }); + } + } + }; + } + + function init_variable(name) { + let the_variable = lookup(name); + if (!the_variable || the_variable.readonly) { + warn("bad_assignment_a", name); + return; + } + the_variable.init = true; + } + + function lookup(thing) { + let id = thing.id; + let the_variable; + if (thing.arity !== "variable") { + return; + } + +// Look up the variable in the current context. + + the_variable = functionage.context[id] || catchage.context[id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable && the_variable.role === "label") { + +// test_cause: +// ["aa:while(0){aa;}", "lookup", "label_a", "aa", 13] + + warn("label_a", thing); + return the_variable; + } + if (!the_variable) { + function_stack.forEach(function ({ + context + }) { + if (context[id] && context[id].role !== "label") { + the_variable = context[id]; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (!the_variable && global_dict[id] === undefined) { + +// test_cause: +// ["aa", "lookup", "undeclared_a", "aa", 1] +// ["class aa{}", "lookup", "undeclared_a", "aa", 7] +// [" +// let aa=0;try{aa();}catch(bb){bb();}bb(); +// ", "lookup", "undeclared_a", "bb", 36] +// [" +// let aa=0;try{aa();}catch(ignore){bb();} +// ", "lookup", "undeclared_a", "bb", 34] + + warn("undeclared_a", thing); + return; + } + if (!the_variable) { + the_variable = { + dead: false, + id, + init: true, + parent: token_global, + readonly: true, + role: "variable", + used: 0 + }; + token_global.context[id] = the_variable; + } + the_variable.closure = true; + functionage.context[id] = the_variable; + } + if ( + ( + the_variable.calls === undefined + || functionage.name === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + && the_variable.dead + ) { + +// test_cause: +// ["let aa;if(aa){let bb;}bb;", "lookup", "out_of_scope_a", "bb", 23] + + warn("out_of_scope_a", thing); + } + return the_variable; + } + + function post_a(thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + let right; + if (thing.id === "=") { + if (thing.names !== undefined) { + +// test_cause: +// ["if(0){aa=0}", "post_a", "=", "", 0] + + test_cause("="); + +// Probably deadcode. +// if (Array.isArray(thing.names)) { +// thing.names.forEach(init_variable); +// } else { +// init_variable(thing.names); +// } + + jslint_assert( + !Array.isArray(thing.names), + `Expected !Array.isArray(thing.names).` + ); + init_variable(thing.names); + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + +// test_cause: +// ["aa.aa=undefined", "post_a", "expected_a_b", "undefined", 1] + + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.readonly) { + warn("bad_assignment_a", lvalue); + } + } + right = syntax_dict[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + +// test_cause: +// ["aa+=undefined", "post_a", "unexpected_a", "undefined", 5] + + warn("unexpected_a", thing.expression[1]); + } + } + } + + function post_a_pluseq(thing) { + const right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } + } + + function post_b(thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || is_equal(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + +// test_cause: +// ["if(0===0){0}", "post_b", "weird_relation_a", "===", 5] + + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option_dict.convert) { + if (thing.expression[0].value === "") { + +// test_cause: +// ["\"\"+0", "post_b", "expected_a_b", "\"\" +", 3] + + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + +// test_cause: +// ["0+\"\"", "post_b", "expected_a_b", "+ \"\"", 2] + + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + +// test_cause: +// ["aa=window[0]", "post_b", "weird_expression_a", "window[...]", 10] + + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + +// test_cause: +// ["aa=self[0]", "post_b", "weird_expression_a", "self[...]", 8] + + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === "." || thing.id === "?.") { + if (thing.expression.id === "RegExp") { + +// test_cause: +// ["aa=RegExp.aa", "post_b", "weird_expression_a", ".", 10] + + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + +// test_cause: +// ["0- -0", "post_b", "wrap_unary", "-", 4] + + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } + } + + function post_b_and(thing) { + if ( + is_weird(thing.expression[0]) + || is_equal(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + +// test_cause: +// ["aa=(()=>0)&&(()=>0)", "post_b_and", "weird_condition_a", "&&", 11] +// ["aa=(``?``:``)&&(``?``:``)", "post_b_and", "weird_condition_a", "&&", 14] +// ["aa=/./&&/./", "post_b_and", "weird_condition_a", "&&", 7] +// ["aa=0&&0", "post_b_and", "weird_condition_a", "&&", 5] +// ["aa=[]&&[]", "post_b_and", "weird_condition_a", "&&", 6] +// ["aa=`${0}`&&`${0}`", "post_b_and", "weird_condition_a", "&&", 10] +// [" +// aa=function aa(){}&&function aa(){} +// ", "post_b_and", "weird_condition_a", "&&", 19] +// ["aa={}&&{}", "post_b_and", "weird_condition_a", "&&", 6] + + warn("weird_condition_a", thing); + } + } + + function post_b_lbracket(thing) { + if (thing.expression[0].id === "RegExp") { + +// test_cause: +// ["aa=RegExp[0]", "post_b_lbracket", "weird_expression_a", "[", 10] + + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + +// test_cause: +// ["aa[[0]]", "post_b_lbracket", "weird_expression_a", "[", 4] + + warn("weird_expression_a", thing.expression[1]); + } + } + + function post_b_lparen(thing) { + let arg; + let array; + let cack; + let left = thing.expression[0]; + let new_date; + let paren; + let the_new; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + +// test_cause: +// ["aa=function(){}()", "post_b_lparen", "wrap_immediate", "(", 16] + + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "BigInt" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + +// test_cause: +// ["new BigInt()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Boolean()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Number()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new String()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Symbol()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new aa()", "post_b_lparen", "unexpected_a", "new", 1] + + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option_dict.eval) { + +// test_cause: +// ["new Function()", "post_b_lparen", "unexpected_a", "new Function", 5] + + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + +// test_cause: +// ["new Array()", "post_b_lparen", "expected_a_b", "new Array", 5] + + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + +// test_cause: +// ["new Object()", "post_b_lparen", "expected_a_b", "new Object", 5] + + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "BigInt" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + +// test_cause: +// ["let Aa=Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8] + + warn("expected_a_before_b", left, "new", artifact(left)); + } + } + } else if (left.id === ".") { + cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + +// test_cause: +// ["new Date.UTC()", "post_b_lparen", "cack", "", 0] + + test_cause("cack"); + cack = !cack; + } + if (jslint_rgx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + +// test_cause: +// ["new Date.UTC()", "post_b_lparen", "unexpected_a", "new", 1] + + warn("unexpected_a", the_new); + } else { + +// test_cause: +// ["let Aa=Aa.Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8] + + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + paren = left.expression; + if (paren.id === "(") { + array = paren.expression; + if (array.length === 1) { + new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + +// test_cause: +// [" +// new Date().getTime() +// ", "post_b_lparen", "expected_a_b", "new Date().getTime()", 1] + + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } + } + + function post_b_or(thing) { + if ( + is_weird(thing.expression[0]) + || is_equal(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + +// test_cause: +// ["aa=0||0", "post_b_or", "weird_condition_a", "||", 5] + + warn("weird_condition_a", thing); + } + } + + function post_s_export(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== token_global) { + +// test_cause: +// [" +// if(0){import aa from "aa";} +// ", "post_s_export", "misplaced_a", "import", 7] + + warn("misplaced_a", the_thing); + } + } + + function post_s_for(thing) { + +// Recurse walk_statement(). + + walk_statement(thing.inc); + } + + function post_s_function(thing) { + delete functionage.async; + delete functionage.finally; + delete functionage.loop; + delete functionage.statement_prv; + delete functionage.switch; + delete functionage.try; + functionage = function_stack.pop(); + if (thing.wrapped) { + +// test_cause: +// ["aa=(function(){})", "post_s_function", "unexpected_parens", "function", 5] + + warn("unexpected_parens", thing); + } + return post_s_lbrace(); + } + + function post_s_import(the_thing) { + const name = the_thing.name; + if (name) { + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return post_s_export(the_thing); + } + } + + function post_s_lbrace() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); + } + + function post_s_try(thing) { + if (thing.catch) { + if (thing.catch.name) { + Object.assign(catchage.context[thing.catch.name.id], { + dead: false, + init: true + }); + } + +// Recurse walk_statement(). + + walk_statement(thing.catch.block); + +// Restore previous catch-scope after catch-block. + + catchage = catch_stack.pop(); + } + } + + function post_s_var(thing) { + thing.names.forEach(function (name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + +// Probably deadcode. +// if (name.id === "{" || name.id === "[") { +// name.names.forEach(subactivate); +// } else { +// name.init = true; +// } + + jslint_assert( + !(name.id === "{" || name.id === "["), + `Expected !(name.id === "{" || name.id === "[").` + ); + name.init = true; + } + blockage.live.push(name); + }); + } + + function post_t(thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || is_equal(thing.expression[1], thing.expression[2]) + ) { + +// test_cause: +// ["let aa=(aa?`${0}`:`${0}`);", "post_t", "unexpected_a", "?", 11] +// ["let aa=(aa?`0`:`0`);", "post_t", "unexpected_a", "?", 11] + + warn("unexpected_a", thing); + } else if (is_equal(thing.expression[0], thing.expression[1])) { + +// test_cause: +// ["aa?aa:0", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "||", "?"); + } else if (is_equal(thing.expression[0], thing.expression[2])) { + +// test_cause: +// ["aa?0:aa", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + +// test_cause: +// ["aa?true:false", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + +// test_cause: +// ["aa?false:true", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + +// test_cause: +// ["(aa&&!aa?0:1)", "post_t", "wrap_condition", "&&", 4] + + warn("wrap_condition", thing.expression[0]); + } + } + + function post_u(thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option_dict.convert) { + +// test_cause: +// ["!!0", "post_u", "expected_a_b", "!!", 1] + + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } + } + + function post_u_plus(thing) { + const right = thing.expression; + if (!option_dict.convert) { + +// test_cause: +// ["aa=+0", "post_u_plus", "expected_a_b", "+", 4] + + warn("expected_a_b", thing, "Number(...)", "+"); + } + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } + } + + function pre_a_bitwise(thing) { + +// These are the bitwise operators. + + switch (!option_dict.bitwise && thing.id) { + case "&": + case "&=": + case "<<": + case "<<=": + case ">>": + case ">>=": + case ">>>": + case ">>>=": + case "^": + case "^=": + case "|": + case "|=": + case "~": + +// test_cause: +// ["0&0", "pre_a_bitwise", "unexpected_a", "&", 2] +// ["0&=0", "pre_a_bitwise", "unexpected_a", "&=", 2] +// ["0<<0", "pre_a_bitwise", "unexpected_a", "<<", 2] +// ["0<<=0", "pre_a_bitwise", "unexpected_a", "<<=", 2] +// ["0>>0", "pre_a_bitwise", "unexpected_a", ">>", 2] +// ["0>>=0", "pre_a_bitwise", "unexpected_a", ">>=", 2] +// ["0>>>0", "pre_a_bitwise", "unexpected_a", ">>>", 2] +// ["0>>>=0", "pre_a_bitwise", "unexpected_a", ">>>=", 2] +// ["0^0", "pre_a_bitwise", "unexpected_a", "^", 2] +// ["0^=0", "pre_a_bitwise", "unexpected_a", "^=", 2] +// ["0|0", "pre_a_bitwise", "unexpected_a", "|", 2] +// ["0|=0", "pre_a_bitwise", "unexpected_a", "|=", 2] +// ["~0", "pre_a_bitwise", "unexpected_a", "~", 1] + + warn("unexpected_a", thing); + break; + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + +// test_cause: +// ["0<0<0", "pre_a_bitwise", "unexpected_a", "<", 4] + + warn("unexpected_a", thing); + } + } + + function pre_b(thing) { + let left; + let right; + let value; + if (relationop[thing.id] === true) { + left = thing.expression[0]; + right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + +// test_cause: +// ["NaN===NaN", "pre_b", "number_isNaN", "===", 4] + + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + +// test_cause: +// ["typeof 0===0", "pre_b", "expected_string_a", "0", 12] + + warn("expected_string_a", right); + } + } else { + value = right.value; + if (value === "null" || value === "undefined") { + +// test_cause: +// [" +// typeof aa==="undefined" +// ", "pre_b", "unexpected_typeof_a", "undefined", 13] + + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "bigint" + && value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + +// test_cause: +// ["typeof 0===\"aa\"", "pre_b", "expected_type_string_a", "aa", 12] + + warn("expected_type_string_a", right, value); + } + } + } + } + } + + function pre_b_eqeq(thing) { + +// test_cause: +// ["0==0", "pre_b_eqeq", "expected_a_b", "==", 2] + + warn("expected_a_b", thing, "===", "=="); + } + + function pre_b_in(thing) { + +// test_cause: +// ["aa in aa", "pre_b_in", "infix_in", "in", 4] + + warn("infix_in", thing); + } + + function pre_b_instanceof(thing) { + +// test_cause: +// ["0 instanceof 0", "pre_b_instanceof", "unexpected_a", "instanceof", 3] + + warn("unexpected_a", thing); + } + + function pre_b_lparen(thing) { + const left = thing.expression[0]; + let left_variable; + let parent; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + parent = functionage.name.parent; + if (parent) { + left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + +// Probably deadcode. +// && left_variable.dead + + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } + } + + function pre_b_noteq(thing) { + +// test_cause: +// ["0!=0", "pre_b_noteq", "expected_a_b", "!=", 2] + + warn("expected_a_b", thing, "!==", "!="); + } + + function pre_b_or(thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + +// test_cause: +// ["0&&0||0", "pre_b_or", "and", "&&", 2] + + warn("and", thang); + } + }); + } + + function pre_s_for(thing) { + let the_variable; + if (thing.name !== undefined) { + thing.name.dead = false; + the_variable = lookup(thing.name); + if (the_variable !== undefined) { + if (the_variable.init && the_variable.readonly) { + +// test_cause: +// ["const aa=0;for(aa in aa){}", "pre_s_for", "bad_assignment_a", "aa", 16] + + warn("bad_assignment_a", thing.name); + } + the_variable.init = true; + } + } + +// Recurse walk_statement(). + + walk_statement(thing.initial); + } + + function pre_s_function(thing) { + +// test_cause: +// ["()=>0", "pre_s_function", "", "", 0] +// ["(function (){}())", "pre_s_function", "", "", 0] +// ["function aa(){}", "pre_s_function", "", "", 0] + + test_cause(""); + if (thing.arity === "statement" && blockage.body !== true) { + +// test_cause: +// ["if(0){function aa(){}\n}", "pre_s_function", "unexpected_a", "function", 7] + + warn("unexpected_a", thing); + } + function_stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + +// test_cause: +// [" +// /*jslint getset*/ +// aa={get aa(aa){}} +// ", "pre_s_function", "bad_get", "function", 9] + + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + +// test_cause: +// [" +// /*jslint getset*/ +// aa={set aa(){}} +// ", "pre_s_function", "bad_set", "function", 9] + + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); + } + + function pre_s_lbrace(thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; + } + + function pre_try(thing) { + if (thing.catch !== undefined) { + +// Create new catch-scope for catch-parameter. + + catch_stack.push(catchage); + catchage = thing.catch; + } + } + + function pre_v(thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } + } + + function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); + } + + function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + +// test_cause: +// ["(function(){}())", "walk_expression", "isArray", "", 0] +// ["0&&0", "walk_expression", "isArray", "", 0] + + test_cause("isArray"); + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + +// PR-414 - Bugfix - fix fart-body not being walked. + + if (thing.id === "function" || thing.id === "=>") { + +// test_cause: +// ["aa=()=>0", "walk_expression", "function", "=>", 0] +// ["aa=function(){}", "walk_expression", "function", "function", 0] + + test_cause("function", thing.id); + +// Recurse walk_statement(). + + walk_statement(thing.block); + } + if ( + thing.arity === "preassign" || thing.arity === "postassign" + ) { + +// test_cause: +// ["aa=++aa", "walk_expression", "unexpected_a", "++", 4] +// ["aa=--aa", "walk_expression", "unexpected_a", "--", 4] + + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + +// test_cause: +// ["aa[aa=0]", "walk_expression", "unexpected_statement_a", "=", 6] + + warn("unexpected_statement_a", thing); + } + +// test_cause: +// ["aa=0", "walk_expression", "default", "", 0] + + test_cause("default"); + postamble(thing); + } + } + } + + function walk_statement(thing) { + if (!thing) { + return; + } + if (Array.isArray(thing)) { + +// test_cause: +// ["+[]", "walk_statement", "isArray", "", 0] + + test_cause("isArray"); + +// Recurse walk_statement(). + + thing.forEach(walk_statement); + return; + } + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + +// test_cause: +// ["0&&0", "walk_statement", "unexpected_expression_a", "&&", 2] + + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + && thing.id !== "import" + ) { + +// test_cause: +// ["!0", "walk_statement", "unexpected_expression_a", "!", 1] +// ["+[]", "walk_statement", "unexpected_expression_a", "+", 1] +// ["+new aa()", "walk_statement", "unexpected_expression_a", "+", 1] +// ["0", "walk_statement", "unexpected_expression_a", "0", 1] +// ["typeof 0", "walk_statement", "unexpected_expression_a", "typeof", 1] + + warn("unexpected_expression_a", thing); + } + +// Recurse walk_statement(). + + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + + postaction = action(posts); + postamble = amble(posts); + preaction = action(pres); + preamble = amble(pres); + postaction("assignment", "+=", post_a_pluseq); + postaction("assignment", post_a); + postaction("binary", "&&", post_b_and); + postaction("binary", "(", post_b_lparen); + postaction("binary", "=>", post_s_function); + postaction("binary", "[", post_b_lbracket); + postaction("binary", "||", post_b_or); + postaction("binary", post_b); + postaction("statement", "const", post_s_var); + postaction("statement", "export", post_s_export); + postaction("statement", "for", post_s_for); + postaction("statement", "function", post_s_function); + postaction("statement", "import", post_s_import); + postaction("statement", "let", post_s_var); + postaction("statement", "try", post_s_try); + postaction("statement", "var", post_s_var); + postaction("statement", "{", post_s_lbrace); + postaction("ternary", post_t); + postaction("unary", "+", post_u_plus); + postaction("unary", "function", post_s_function); + postaction("unary", post_u); + preaction("assignment", pre_a_bitwise); + preaction("binary", "!=", pre_b_noteq); + preaction("binary", "(", pre_b_lparen); + preaction("binary", "==", pre_b_eqeq); + preaction("binary", "=>", pre_s_function); + preaction("binary", "in", pre_b_in); + preaction("binary", "instanceof", pre_b_instanceof); + preaction("binary", "||", pre_b_or); + preaction("binary", pre_b); + preaction("binary", pre_a_bitwise); + preaction("statement", "for", pre_s_for); + preaction("statement", "function", pre_s_function); + preaction("statement", "try", pre_try); + preaction("statement", "{", pre_s_lbrace); + preaction("unary", "function", pre_s_function); + preaction("unary", "~", pre_a_bitwise); + preaction("variable", pre_v); + + walk_statement(state.token_tree); +} + +function jslint_phase5_whitage(state) { + +// PHASE 5. Check whitespace between tokens in . + + let { + artifact, + catch_list, + function_list, + function_stack, + option_dict, + test_cause, + token_global, + token_list, + warn + } = state; + let closer = "(end)"; + let free = false; + +// free = false + +// cause: +// "()=>0" +// "aa()" +// "aa(0,0)" +// "function(){}" + +// free = true + +// cause: +// "(0)" +// "(aa)" +// "aa(0)" +// "do{}while()" +// "for(){}" +// "if(){}" +// "switch(){}" +// "while(){}" + + let left = token_global; + let margin = 0; + let mode_indent = ( + +// PR-330 - Allow 2-space indent. + + option_dict.indent2 + ? 2 + : 4 + ); + let nr_comments_skipped = 0; + let open = true; + let opening = true; + let right; + +// This is the set of infix operators that require a space on each side. + + let spaceop = object_assign_from_list(empty(), [ + "!=", "!==", "%", "%=", "&", "&&", "&=", "*", "*=", "+=", "-=", "/", + "/=", "<", "<<", "<<=", "<=", "=", "==", "===", "=>", ">", ">=", ">>", + ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" + ], true); + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + const name = the_function.context[id]; + if (id !== "ignore" && name.parent === the_function) { + +// test_cause: +// ["function aa(aa) {return aa;}", "delve", "id", "", 0] + + test_cause("id"); + if ( + name.used === 0 + +// Probably deadcode. +// && ( +// name.role !== "function" +// || name.parent.arity !== "unary" +// ) + + && jslint_assert( + name.role !== "function", + `Expected name.role !== "function".` + ) + ) { + +// test_cause: +// ["/*jslint node*/\nlet aa;", "delve", "unused_a", "aa", 5] +// ["function aa(aa){return;}", "delve", "unused_a", "aa", 13] +// ["let aa=0;try{aa();}catch(bb){aa();}", "delve", "unused_a", "bb", 26] + + warn("unused_a", name); + } else if (!name.init) { + +// test_cause: +// ["/*jslint node*/\nlet aa;aa();", "delve", "uninitialized_a", "aa", 5] + + warn("uninitialized_a", name); + } + } + }); + } + + function expected_at(at) { + +// Probably deadcode. +// if (right === undefined) { +// right = token_nxt; +// } + + jslint_assert( + !(right === undefined), + `Expected !(right === undefined).` + ); + warn( + "expected_a_at_b_c", + right, + artifact(right), + +// Fudge column numbers in warning message. + + at + jslint_fudge, + right.from + jslint_fudge + ); + } + + function no_space() { + if (left.line === right.line) { + +// from: +// if (left.line === right.line) { +// no_space(); +// } else { + + if (left.thru !== right.from && nr_comments_skipped === 0) { + +// test_cause: +// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14] + + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + +// from: +// } else if ( +// right.arity === "binary" +// && right.id === "(" +// && free +// ) { +// no_space(); +// } else if ( + +// Probably deadcode. +// if (open) { +// const at = ( +// free +// ? margin +// : margin + 8 +// ); +// if (right.from < at) { +// expected_at(at); +// } +// } else { +// if (right.from !== margin + 8) { +// expected_at(margin + 8); +// } +// } + + jslint_assert(open, `Expected open.`); + jslint_assert(free, `Expected free.`); + if (right.from < margin) { + +// test_cause: +// ["let aa = aa(\naa\n()\n);", "expected_at", "expected_a_at_b_c", "5", 1] + + expected_at(margin); + } + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line || !open) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (right.from !== margin) { + expected_at(margin); + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn("expected_space_a_b", right, artifact(left), artifact(right)); + } + } + + function pop() { + const previous = function_stack.pop(); + closer = previous.closer; + free = previous.free; + margin = previous.margin; + open = previous.open; + opening = previous.opening; + } + + function push() { + function_stack.push({ + closer, + free, + margin, + open, + opening + }); + } + +// uninitialized_and_unused(); +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (state.mode_module === true || option_dict.node) { + delve(token_global); + } + catch_list.forEach(delve); + function_list.forEach(delve); + + if (option_dict.white) { + return; + } + +// whitage(); +// Go through the token list, looking at usage of whitespace. + + token_list.forEach(function whitage(the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + +// The open and close pairs. + + switch (left.id) { + case "${": + case "(": + case "[": + case "{": + +// test_cause: +// ["let aa=[];", "whitage", "opener", "", 0] +// ["let aa=`${0}`;", "whitage", "opener", "", 0] +// ["let aa=aa();", "whitage", "opener", "", 0] +// ["let aa={};", "whitage", "opener", "", 0] + + test_cause("opener"); + +// Probably deadcode. +// case "${}": + + jslint_assert( + !(left.id + right.id === "${}"), + "Expected !(left.id + right.id === \"${}\")." + ); + switch (left.id + right.id) { + case "()": + case "[]": + case "{}": + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like '{]') have already been detected. + +// test_cause: +// ["let aa=[];", "whitage", "opener_closer", "", 0] +// ["let aa=aa();", "whitage", "opener_closer", "", 0] +// ["let aa={};", "whitage", "opener_closer", "", 0] + + test_cause("opener_closer"); + if (left.line === right.line) { + +// test_cause: +// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14] + + no_space(); + } else { + +// test_cause: +// ["let aa = aa(\n );", "expected_at", "expected_a_at_b_c", "1", 2] + + at_margin(0); + } + break; + default: + +// test_cause: +// ["let aa=(0);", "whitage", "opener_operand", "", 0] +// ["let aa=[0];", "whitage", "opener_operand", "", 0] +// ["let aa=`${0}`;", "whitage", "opener_operand", "", 0] +// ["let aa=aa(0);", "whitage", "opener_operand", "", 0] +// ["let aa={aa:0};", "whitage", "opener_operand", "", 0] + + test_cause("opener_operand"); + opening = left.open || (left.line !== right.line); + push(); + switch (left.id) { + case "${": + closer = "}"; + break; + case "(": + closer = ")"; + break; + case "[": + closer = "]"; + break; + case "{": + closer = "}"; + break; + } + if (opening) { + +// test_cause: +// ["function aa(){\nreturn;\n}", "whitage", "opening", "", 0] +// ["let aa=(\n0\n);", "whitage", "opening", "", 0] +// ["let aa=[\n0\n];", "whitage", "opening", "", 0] +// ["let aa=`${\n0\n}`;", "whitage", "opening", "", 0] +// ["let aa={\naa:0\n};", "whitage", "opening", "", 0] + + test_cause("opening"); + free = closer === ")" && left.free; + open = true; + margin += mode_indent; + if (right.role === "label") { + if (right.from !== 0) { + +// test_cause: +// [" +// function aa() { +// bb: +// while (aa) { +// if (aa) { +// break bb; +// } +// } +// } +// ", "expected_at", "expected_a_at_b_c", "1", 2] + + expected_at(0); + } + } else if (right.switch) { + at_margin(-mode_indent); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + +// test_cause: +// [" +// function aa() {bb: +// while (aa) { +// aa(); +// } +// } +// ", "whitage", "expected_line_break_a_b", "bb", 16] + + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + +// test_cause: +// ["let aa=(0);", "whitage", "not_free", "", 0] +// ["let aa=[0];", "whitage", "not_free", "", 0] +// ["let aa=`${0}`;", "whitage", "not_free", "", 0] +// ["let aa={aa:0};", "whitage", "not_free", "", 0] + + test_cause("not_free"); + free = false; + open = false; + +// test_cause: +// ["let aa = ( 0 );", "no_space_only", "unexpected_space_a_b", "0", 12] + + no_space_only(); + } + } + break; + default: + if (right.statement === true) { + if (left.id === "else") { + +// test_cause: +// [" +// let aa = 0; +// if (aa) { +// aa(); +// } else if (aa) { +// aa(); +// } +// ", "one_space_only", "expected_space_a_b", "if", 9] + + one_space_only(); + } else { + +// test_cause: +// [" let aa = 0;", "expected_at", "expected_a_at_b_c", "1", 2] + + at_margin(0); + open = false; + } + +// If right is a closer, then pop the previous state. + + } else if (right.id === closer) { + pop(); + if (opening && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-mode_indent); + } else if (right.role === "label") { + if (right.from !== 0) { + +// test_cause: +// [" +// function aa() { +// aa();cc: +// while (aa) { +// if (aa) { +// break cc; +// } +// } +// } +// ", "expected_at", "expected_a_at_b_c", "1", 10] + + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + +// test_cause: +// ["let {aa,bb} = 0;", "one_space", "expected_space_a_b", "bb", 9] + + one_space(); + } else { + +// test_cause: +// [" +// function aa() { +// aa( +// 0,0 +// ); +// } +// ", "expected_at", "expected_a_at_b_c", "9", 11] + + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + +// test_cause: +// [" +// let aa = ( +// aa +// ? 0 +// : 1 +// ); +// ", "expected_at", "expected_a_at_b_c", "5", 1] + + at_margin(0); + } else { + +// test_cause: +// ["let aa = (aa ? 0 : 1);", "whitage", "use_open", "?", 14] + + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + +// test_cause: +// ["let aa = aa(\naa ()\n);", "no_space", "unexpected_space_a_b", "(", 4] + + no_space(); + } else if ( + left.id === "." + || left.id === "?." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + || (right.id === "." || right.id === "?.") + ) { + +// test_cause: +// ["let aa = 0 ;", "no_space_only", "unexpected_space_a_b", ";", 12] +// ["let aa = aa ?.aa;", "no_space_only", "unexpected_space_a_b", "?.", 13] + + no_space_only(); + } else if (left.id === ";") { + +// test_cause: +// [" +// /*jslint for*/ +// function aa() { +// for ( +// aa(); +// aa; +// aa() +// ) { +// aa(); +// } +// } +// ", "expected_at", "expected_a_at_b_c", "9", 1] + + if (open) { + at_margin(0); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || left.id === "await" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + +// test_cause: +// [" +// function aa() { +// do { +// aa(); +// } while(aa()); +// } +// ", "one_space_only", "expected_space_a_b", "(", 12] + + one_space_only(); + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || left.id === "async" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + +// test_cause: +// ["let aa=0;", "one_space", "expected_space_a_b", "0", 8] +// ["let aa={\naa:\n0\n};", "expected_at", "expected_a_at_b_c", "5", 1] + + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +function jslint_report({ + exports, + froms, + functions, + global, + json, + module, + property, + stop, + warnings +}) { + +// This function will create human-readable, html-report +// for warnings, properties, and functions from jslint-result-object. +// +// Example usage: +// let result = jslint("console.log('hello world')"); +// let html = jslint_report(result); + + let html = ""; + let length_80 = 1111; + + function address(line = 1, column = 1) { + +// This function will create HTML address element from and + + return `
    ${Number(line)}: ${Number(column)}
    `; + + } + + function detail(title, list) { + return ( + (Array.isArray(list) && list.length > 0) + ? ( + +// Google Lighthouse Accessibility -
    's do not contain only properly-ordered +//
    and
    groups, ". + + code.replace(( + /^]*?>\n([\S\s]*?\n)<\/script>$/gm + ), function (ignore, match1, ii) { + jslint_from_file({ + code: match1, + file: file + ". + + import_meta_url = import_meta_url || jslint_import_meta_url; + if ( + jslint_rgx_url_search_window_jslint.test(import_meta_url) + && (typeof globalThis === "object" && globalThis) + ) { + globalThis.jslint = jslint; + } + +// Feature-detect nodejs. + + if (!( + (typeof process === "object" && process) + && process.versions + && typeof process.versions.node === "string" + && !mode_noop + )) { + return exit_code; + } + console_error = console_error || console.error; + console_log = console_log || console.log; + process_argv = process_argv || process.argv; + process_env = process_env || process.env; + process_exit = process_exit || process.exit; + await moduleFsInit(); + if ( + !( + +// Feature-detect nodejs-cli. + + process.execArgv.indexOf("--eval") === -1 + && process.execArgv.indexOf("-e") === -1 + && ( + ( + /[\/|\\]jslint(?:\.[cm]?js)?$/m + ).test(process_argv[1]) + || mode_cli + ) + && ( + moduleUrl.fileURLToPath(import_meta_url) + === + modulePath.resolve(process_argv[1]) + ) + ) + && !mode_cli + ) { + return exit_code; + } + +// init commmand + + command = String(process_argv[2]).split("="); + command[1] = command.slice(1).join("="); + + switch (command[0]) { + +// PR-362 - Add API Doc. + + case "jslint_apidoc": + await jslint_apidoc(Object.assign(JSON.parse(process_argv[3]), { + pathname: command[1] + })); + return; + +// PR-363 - Add command jslint_report. + + case "jslint_report": + mode_report = command[1]; + process_argv = process_argv.slice(1); + break; + +// COMMIT-b26d6df2 - Add command jslint_wrapper_vim. + + case "jslint_wrapper_vim": + mode_wrapper_vim = true; + process_argv = process_argv.slice(1); + break; + +// PR-364 - Add command v8_coverage_report. + + case "v8_coverage_report": + await v8CoverageReportCreate({ + consoleError: console_error, + coverageDir: command[1], + processArgv: process_argv.slice(3) + }); + return; + } + +// Normalize file relative to process.cwd(). + + process_argv.slice(2).some(function (arg) { + if (!arg.startsWith("-")) { + file = file || arg; + return true; + } + }); + if (!file) { + return; + } + file = modulePath.resolve(file) + "/"; + if (file.startsWith(process.cwd() + "/")) { + file = file.replace(process.cwd() + "/", "").slice(0, -1) || "."; + } + file = file.replace(( + /\\/g + ), "/").replace(( + /\/$/g + ), ""); + if (source) { + data = source; + } else { + +// jslint_cli - jslint directory. + + try { + data = await moduleFs.promises.readdir(file, "utf8"); + } catch (ignore) {} + if (data) { + await Promise.all(data.map(async function (file2) { + let code; + let time_start = Date.now(); + file2 = file + "/" + file2; + switch (( + /\.\w+?$|$/m + ).exec(file2)[0]) { + case ".cjs": + case ".html": + case ".js": + case ".json": + case ".md": + case ".mjs": + case ".sh": + break; + default: + return; + } + try { + code = await moduleFs.promises.readFile(file2, "utf8"); + } catch (ignore) { + return; + } + if ( + ( + /(?:\b|_)(?:lock|min|raw|rollup)(?:\b|_)/ + ).test(file2) + || !(code && code.length < 1048576) + ) { + return; + } + jslint_from_file({ + code, + file: file2, + option + }); + console_error( + "jslint - " + (Date.now() - time_start) + "ms - " + file2 + ); + })); + process_exit(exit_code); + return exit_code; + } + +// jslint_cli - jslint file. + + try { + data = await moduleFs.promises.readFile(file, "utf8"); + } catch (err) { + console_error(err); + exit_code = 1; + process_exit(exit_code); + return exit_code; + } + } + result = jslint_from_file({ + code: data, + file, + option + }); + if (mode_report) { + result = jslint.jslint_report(result); + result = `\n${result}\n`; + await fsWriteFileWithParents(mode_report, result); + } + process_exit(exit_code); + return exit_code; +} + +function jslint_phase1_split() { + +// PHASE 1. Split by newlines into . + + return; +} + +function jslint_phase2_lex(state) { + +// PHASE 2. Lex into . + + let { + artifact, + directive_list, + global_dict, + global_list, + line_list, + option_dict, + stop, + stop_at, + tenure, + test_cause, + token_global, + token_list, + warn, + warn_at + } = state; + let char; // The current character being lexed. + let column = 0; // The column number of the next character. + let from; // The starting column number of the token. + let from_mega; // The starting column of megastring. + let line = 0; // The line number of the next character. + let line_disable; // The starting line of "/*jslint-disable*/". + let line_mega; // The starting line of megastring. + let line_source = ""; // The remaining line source string. + let line_whole = ""; // The whole line source string. + let mode_digits_empty_string = 1; + let mode_digits_numeric_separator = 2; + let mode_directive = true; // true if directives are still allowed. + let mode_mega = false; // true if currently parsing a megastring + // ... literal. + let mode_regexp; // true if regular expression literal seen on + // ... this line. + let paren_backtrack_list = []; // List of most recent "(" tokens at any + // ... paren-depth. + let paren_depth = 0; // Keeps track of current paren-depth. + let snippet = ""; // A piece of string. + let token_1; // The first token. + let token_prv = token_global; // The previous token including + // ... comments. + let token_prv_expr = token_global; // The previous token excluding + // ... comments. + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions char_after, char_before, +// read_digits, and char_after_escape help in the parsing of literals. + + function char_after(match) { + +// Get the next character from the source line. Remove it from the line_source, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + +// test_cause: +// ["aa=/[", "char_after", "expected_a", "]", 5] +// ["aa=/aa{/", "char_after", "expected_a_b", "/", 8] + + return ( + char === "" + ? stop_at("expected_a", line, column - 1, match) + : stop_at("expected_a_b", line, column, match, char) + ); + } + char = line_source.slice(0, 1); + line_source = line_source.slice(1); + snippet += char || " "; + column += 1; + return char; + } + + function char_after_escape(extra) { + +// Validate char after escape "\\". + + char_after("\\"); + switch (char) { + case "": + +// test_cause: +// ["\"\\", "char_after_escape", "unclosed_string", "", 2] + + return stop_at("unclosed_string", line, column); + case "/": + return char_after(); + case "\\": + return char_after(); + case "`": + return char_after(); + case "b": + return char_after(); + case "f": + return char_after(); + case "n": + return char_after(); + case "r": + return char_after(); + case "t": + +// test_cause: +// ["\"\\/\\\\\\`\\b\\f\\n\\r\\t\"", "char_after_escape", "char_after", "", 0] + + test_cause("char_after"); + return char_after(); + case "u": + if (char_after("u") === "{") { + if (state.mode_json) { + +// test_cause: +// ["[\"\\u{12345}\"]", "char_after_escape", "unexpected_a", "{", 5] + + warn_at("unexpected_a", line, column, char); + } + if (read_digits("x", undefined) > 5) { + +// test_cause: +// ["\"\\u{123456}\"", "char_after_escape", "too_many_digits", "", 11] + + warn_at("too_many_digits", line, column); + } + if (char !== "}") { + +// test_cause: +// ["\"\\u{12345\"", "char_after_escape", "expected_a_before_b", "\"", 10] + + stop_at("expected_a_before_b", line, column, "}", char); + } + return char_after(); + } + char_before(); + if (read_digits("x", mode_digits_empty_string) < 4) { + +// test_cause: +// ["\"\\u0\"", "char_after_escape", "expected_four_digits", "", 5] + + warn_at("expected_four_digits", line, column); + } + return; + default: + if (extra && extra.indexOf(char) >= 0) { + return char_after(); + } + +// test_cause: +// ["\"\\0\"", "char_after_escape", "unexpected_a_before_b", "0", 3] + + warn_at("unexpected_a_before_b", line, column, "\\", char); + } + } + + function char_before() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the line_source. + + char = snippet.slice(-1); + line_source = char + line_source; + column -= char.length; + +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + return char; + } + + function check_numeric_separator(digits, column) { + +// This function will check for illegal numeric-separator in . + + digits.replace(( + jslint_rgx_numeric_separator_illegal + ), function (ignore, ii) { + +// test_cause: +// ["0x0_0_;", "check_numeric_separator", "illegal_num_separator", "", 6] +// ["0x0_0__0;", "check_numeric_separator", "illegal_num_separator", "", 6] +// ["aa=1_2_;", "check_numeric_separator", "illegal_num_separator", "", 7] +// ["aa=1_2__3;", "check_numeric_separator", "illegal_num_separator", "", 7] +// ["aa=1_2_n;", "check_numeric_separator", "illegal_num_separator", "", 7] + + warn_at("illegal_num_separator", line, column + ii + 1); + return ""; + }); + } + + function lex_comment() { + let body; + let ii = 0; + let jj = 0; + let the_comment; + +// Create a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + +// Create token from comment //.... + + if (snippet === "//") { + snippet = line_source; + line_source = ""; + the_comment = token_create("(comment)", snippet); + if (mode_mega) { + +// test_cause: +// ["`${//}`", "lex_comment", "unexpected_comment", "`", 4] + + warn("unexpected_comment", the_comment, "`"); + } + +// Create token from comment /*...*/. + + } else { + snippet = []; + if (line_source[0] === "/") { + +// test_cause: +// ["/*/", "lex_comment", "unexpected_a", "/", 2] + + warn_at("unexpected_a", line, column + ii, "/"); + } + +// Lex/loop through each line until "*/". + + while (true) { + // jslint_rgx_star_slash + ii = line_source.indexOf("*/"); + if (ii >= 0) { + break; + } + // jslint_rgx_slash_star + ii = line_source.indexOf("/*"); + if (ii >= 0) { + +// test_cause: +// ["/*/*", "lex_comment", "nested_comment", "", 2] + + warn_at("nested_comment", line, column + ii); + } + snippet.push(line_source); + line_source = read_line(); + if (line_source === undefined) { + +// test_cause: +// ["/*", "lex_comment", "unclosed_comment", "", 1] + + return stop_at("unclosed_comment", line, column); + } + } + jj = line_source.slice(0, ii).search( + jslint_rgx_slash_star_or_slash + ); + if (jj >= 0) { + +// test_cause: +// ["/*/**/", "lex_comment", "nested_comment", "", 2] + + warn_at("nested_comment", line, column + jj); + } + snippet.push(line_source.slice(0, ii)); + snippet = snippet.join(" "); + column += ii + 2; + line_source = line_source.slice(ii + 2); + the_comment = token_create("(comment)", snippet); + } + +// Uncompleted work comment. + + if (!option_dict.devel && jslint_rgx_todo.test(snippet)) { + +// test_cause: +// ["//todo", "lex_comment", "todo_comment", "(comment)", 1] //jslint-ignore-line + + warn("todo_comment", the_comment); + } + +// Lex directives in comment. + + [ + the_comment.directive, body + ] = Array.from(snippet.match(jslint_rgx_directive) || []).slice(1); + if (the_comment.directive === undefined) { + return the_comment; + } + directive_list.push(the_comment); + if (!mode_directive) { + +// test_cause: +// ["0\n/*global aa*/", "lex_comment", "misplaced_directive_a", "global", 1] + + warn_at("misplaced_directive_a", line, from, the_comment.directive); + return the_comment; + } + +// lex_directive(); +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + +// Lex/loop through each directive in /*...*/ + + ii = 0; + body.replace(jslint_rgx_directive_part, function ( + match0, + key, + val, + jj + ) { + if (ii !== jj) { + +// test_cause: +// ["/*jslint !*/", "lex_comment", "bad_directive_a", "!", 1] + + return stop("bad_directive_a", the_comment, body.slice(ii)); + } + if (match0 === "") { + return ""; + } + ii += match0.length; + switch (the_comment.directive) { + case "global": + if (val) { + +// test_cause: +// ["/*global aa:false*/", "lex_comment", "bad_option_a", "aa:false", 1] + + warn("bad_option_a", the_comment, key + ":" + val); + } + global_dict[key] = "user-defined"; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// state.mode_module = the_comment; + + break; + case "jslint": + if (!option_set_item(key, val !== "false")) { + +// test_cause: +// ["/*jslint undefined*/", "lex_comment", "bad_option_a", "undefined", 1] + + warn("bad_option_a", the_comment, key); + } + break; + case "property": + state.mode_property = true; + tenure[key] = true; + break; + } + return ""; + }); + return the_comment; + } + + function lex_megastring() { + let id; + let match; + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (mode_mega) { + +// test_cause: +// ["`${`", "lex_megastring", "expected_a_b", "`", 4] + + return stop_at("expected_a_b", line, column, "}", "`"); + } + from_mega = from; + line_mega = line; + mode_mega = true; + snippet = ""; + +// Parsing a mega literal is tricky. First create a ` token. + + token_create("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + while (true) { + match = line_source.match(jslint_rgx_mega) || { + "0": "", + index: 0 + }; + snippet += line_source.slice(0, match.index); + column += match.index; + line_source = line_source.slice(match.index); + match = match[0]; + switch (match) { + case "${": + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + token_create("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then create tokens that will become part of an expression until +// a } token is made. + + column += 2; + token_create("${"); + line_source = line_source.slice(2); + +// Lex/loop through each token inside megastring-expression `${...}`. + + while (true) { + id = lex_token().id; + if (id === "{") { + +// test_cause: +// ["`${{", "lex_megastring", "expected_a_b", "{", 4] + + return stop_at("expected_a_b", line, column, "}", "{"); + } + if (id === "}") { + break; + } + } + break; + case "\\": + snippet += line_source.slice(0, 2); + line_source = line_source.slice(2); + column += 2; + break; + case "`": + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + token_create("(string)", snippet).quote = "`"; + snippet = ""; + +// Terminate megastring with `. + + line_source = line_source.slice(1); + column += 1; + mode_mega = false; + return token_create("`"); + default: + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + snippet += line_source + "\n"; + if (read_line() === undefined) { + +// test_cause: +// ["`", "lex_megastring", "unclosed_mega", "", 1] + + return stop_at("unclosed_mega", line_mega, from_mega); + } + } + } + } + + function lex_number() { + let prefix = snippet; + +// PR-390 - Add numeric-separator check. + + check_numeric_separator(prefix, column - prefix.length); + char_after(); + switch (prefix === "0" && char) { + case "b": + case "o": + case "x": + read_digits(char, mode_digits_numeric_separator); + +// PR-351 - Ignore BigInt suffix 'n'. + + if (char === "n") { + char_after("n"); + } + break; + default: + if (char === ".") { + read_digits("d", mode_digits_numeric_separator); + } + if (char === "E" || char === "e") { + char_after(char); + if (char !== "+" && char !== "-") { + char_before(); + } + read_digits("d", mode_digits_numeric_separator); + } + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + +// test_cause: +// ["0a", "lex_number", "unexpected_a_after_b", "0", 2] + + return stop_at( + "unexpected_a_after_b", + line, + column, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + char_before(); + return token_create("(number)", snippet); + } + + function lex_regexp() { + +// Regexp +// Lex a regular expression literal. + + let flag; + let mode_regexp_multiline; + let result; + let value; + mode_regexp = true; + + function lex_regexp_bracketed() { + let mode_regexp_range; + +// RegExp +// Match a class. + + char_after("["); + if (char === "^") { + char_after("^"); + } + while (true) { + +// RegExp +// Match a character in a character class. + + switch (char) { + case "": + case "]": + +// test_cause: +// ["aa=/[", "lex_regexp_bracketed", "closer", "", 0] +// ["aa=/[]/", "lex_regexp_bracketed", "closer", "", 0] + + test_cause("closer"); + if (mode_regexp_range) { + +// test_cause: +// ["aa=/[0-]/", "lex_regexp_bracketed", "unexpected_a", "-", 7] + + warn_at("unexpected_a", line, column - 1, "-"); + } + return char_after("]"); + +// PR-362 - Relax regexp-warning against using . +// +// case " ": +// +// // test_cause: +// // ["aa=/[ ]/", "lex_regexp_bracketed", "expected_a_b", " ", 6] +// +// warn_at("expected_a_b", line, column, "\\u0020", " "); +// break; + + case "-": + case "/": + case "[": + case "^": + +// test_cause: +// ["aa=/[-]/", "lex_regexp_bracketed", "expected_a_before_b", "-", 6] +// ["aa=/[.^]/", "lex_regexp_bracketed", "expected_a_before_b", "^", 7] +// ["aa=/[/", "lex_regexp_bracketed", "expected_a_before_b", "/", 6] +// ["aa=/[\\\\/]/", "lex_regexp_bracketed", "expected_a_before_b", "/", 8] +// ["aa=/[\\\\[]/", "lex_regexp_bracketed", "expected_a_before_b", "[", 8] + + warn_at("expected_a_before_b", line, column, "\\", char); + break; + case "\\": + char_after_escape("BbDdSsWw-[]^"); + char_before(); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${/[`]/}`", "lex_regexp_bracketed", "unexpected_a", "`", 6] + + warn_at("unexpected_a", line, column, "`"); + } + break; + } + char_after(); + mode_regexp_range = false; + if (char === "-") { + +// RegExp +// Match a range of subclasses. + + mode_regexp_range = true; + char_after("-"); + } + } + } + + function lex_regexp_group() { + +// RegExp +// Lex sequence of characters in regexp. + + switch (char) { + case "": + warn_at("expected_regexp_factor_a", line, column, char); + break; + case ")": + warn_at("expected_regexp_factor_a", line, column, char); + break; + case "]": + +// test_cause: +// ["/ /", "lex_regexp_group", "expected_regexp_factor_a", "", 3] +// ["aa=/)", "lex_regexp_group", "expected_regexp_factor_a", ")", 5] +// ["aa=/]", "lex_regexp_group", "expected_regexp_factor_a", "]", 5] + + warn_at("expected_regexp_factor_a", line, column, char); + break; + } + while (true) { + switch (char) { + case "": + case ")": + case "/": + case "]": + return; + +// PR-362 - Relax regexp-warning against using . +// +// case " ": +// +// // test_cause: +// // ["aa=/ /", "lex_regexp_group", "expected_a_b", " ", 5] +// +// warn_at("expected_a_b", line, column, "\\s", " "); +// char_after(); +// break; + + case "$": + if (line_source[0] !== "/") { + mode_regexp_multiline = true; + } + char_after(); + break; + case "(": + +// RegExp +// Match a group that starts with left paren. + + char_after("("); + switch (char) { + case ":": + +// test_cause: +// ["aa=/(:)/", "lex_regexp_group", "expected_a_before_b", ":", 6] +// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5] + + warn_at("expected_a_before_b", line, column, "?", ":"); + break; + case "?": + char_after("?"); + switch (char) { + case "!": + +// PR-437 - Add grammar for regexp-named-capture-group. + + case "<": + case "=": + char_after(); + break; + default: + char_after(":"); + } + break; + } + +// RegExp +// Recurse lex_regexp_group(). + + lex_regexp_group(); + char_after(")"); + break; + case "*": + case "+": + case "?": + case "{": + case "}": + +// test_cause: +// ["aa=/+/", "lex_regexp_group", "expected_a_before_b", "+", 5] +// ["aa=/.**/", "lex_regexp_group", "expected_a_before_b", "*", 7] +// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5] +// ["aa=/{/", "lex_regexp_group", "expected_a_before_b", "{", 5] +// ["aa=/}/", "lex_regexp_group", "expected_a_before_b", "}", 5] + + warn_at("expected_a_before_b", line, column, "\\", char); + char_after(); + break; + case "[": + lex_regexp_bracketed(); + break; + case "\\": + +// test_cause: +// ["aa=/\\/", "lex_regexp_group", "escape", "", 0] + + test_cause("escape"); + +// PR-437 - Add grammar for regexp-named-backreference. + + char_after_escape("BbDdSsWw^${}[]():=!.|*+?k"); + break; + case "^": + if (snippet !== "^") { + mode_regexp_multiline = true; + } + char_after(); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${/`/}`", "lex_regexp_group", "unexpected_a", "`", 5] + + warn_at("unexpected_a", line, column, "`"); + } + char_after(); + break; + default: + char_after(); + } + +// RegExp +// Match an optional quantifier. + + switch (char) { + case "*": + case "+": + if (char_after(char) === "?") { + +// test_cause: +// ["aa=/.*?/", "lex_regexp_group", "?", "", 0] +// ["aa=/.+?/", "lex_regexp_group", "?", "", 0] + + test_cause("?"); + char_after("?"); + } + break; + case "?": + if (char_after("?") === "?") { + +// test_cause: +// ["aa=/.??/", "lex_regexp_group", "unexpected_a", "?", 7] + + warn_at("unexpected_a", line, column, char); + char_after("?"); + } + break; + case "{": + if (read_digits("d", mode_digits_empty_string) === 0) { + +// test_cause: +// ["aa=/aa{/", "lex_regexp_group", "expected_a_before_b", ",", 8] + + warn_at("expected_a_before_b", line, column, "0", ","); + } + if (char === ",") { + +// test_cause: +// ["aa=/.{,/", "lex_regexp_group", "comma", "", 0] + + test_cause("comma"); + read_digits("d", mode_digits_empty_string); + } + if (char_after("}") === "?") { + +// test_cause: +// ["aa=/.{0}?/", "lex_regexp_group", "unexpected_a", "?", 9] + + warn_at("unexpected_a", line, column, char); + char_after("?"); + } + break; + } + } + } + +// RegExp +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + char_after(); + if (char === "=") { + +// test_cause: +// ["aa=/=/", "lex_regexp", "expected_a_before_b", "=", 5] + + warn_at("expected_a_before_b", line, column, "\\", "="); + } + lex_regexp_group(); + +// RegExp +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + +// RegExp +// Make sure there is a closing slash. + + value = snippet; + char_after("/"); + +// RegExp +// Create flag. + + flag = empty(); + while ( + +// Regexp +// char is a letter. + + (char >= "a" && char <= "z\uffff") + || (char >= "A" && char <= "Z\uffff") + ) { + +// RegExp +// Process dangling flag letters. + + switch (!flag[char] && char) { + case "g": + break; + case "i": + break; + case "m": + break; + case "u": + break; + case "y": + +// test_cause: +// ["aa=/./gimuy", "lex_regexp", "flag", "", 0] + + test_cause("flag"); + break; + default: + +// test_cause: +// ["aa=/./gg", "lex_regexp", "unexpected_a", "g", 8] +// ["aa=/./z", "lex_regexp", "unexpected_a", "z", 7] + + warn_at("unexpected_a", line, column, char); + } + flag[char] = true; + char_after(); + } + char_before(); + if (char === "/" || char === "*") { + +// test_cause: +// ["aa=/.//", "lex_regexp", "unexpected_a", "/", 3] + + return stop_at("unexpected_a", line, from, char); + } + result = token_create("(regexp)", char); + result.flag = flag; + result.value = value; + if (mode_regexp_multiline && !flag.m) { + +// test_cause: +// ["aa=/$^/", "lex_regexp", "missing_m", "", 7] + + warn_at("missing_m", line, column); + } + return result; + } + + function lex_slash_or_regexp() { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + let the_token; + switch ( + token_prv_expr.identifier + && !token_prv_expr.dot + && token_prv_expr.id + ) { + case "case": + case "delete": + case "in": + case "instanceof": + case "new": + case "typeof": + case "void": + case "yield": + the_token = lex_regexp(); + +// test_cause: +// ["case /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6] +// ["delete /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8] +// ["in /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 4] +// ["instanceof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 12] +// ["new /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 5] +// ["typeof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8] +// ["void /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6] +// ["yield /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 7] + + return stop("unexpected_a", the_token); + case "return": + return lex_regexp(); + } + switch (!token_prv_expr.identifier && token_prv_expr.id.slice(-1)) { + case "!": + case "%": + case "&": + case "*": + case "+": + case "-": + case "/": + case ";": + case "<": + case ">": + case "^": + case "{": + case "|": + case "}": + case "~": + the_token = lex_regexp(); + +// test_cause: +// ["!/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["%/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["&/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["+/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["-/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["0 * /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5] +// ["0 / /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5] +// [";/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["^/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["{/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["|/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["}/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["~/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] + + warn("wrap_regexp", the_token); + return the_token; + case "(": + case ",": + case ":": + case "=": + case "?": + case "[": + +// test_cause: +// ["(/./", "lex_slash_or_regexp", "recurse", "", 0] +// [",/./", "lex_slash_or_regexp", "recurse", "", 0] +// [":/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["=/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["?/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["aa[/./", "lex_slash_or_regexp", "recurse", "", 0] + + test_cause("recurse"); + return lex_regexp(); + } + if (line_source[0] === "=") { + column += 1; + line_source = line_source.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + return token_create(snippet); + } + + function lex_string(quote) { + +// Create a string token. + + let the_token; + if (!option_dict.single && quote === "'") { + +// test_cause: +// ["''", "lex_string", "use_double", "", 1] + + warn_at("use_double", line, column); + } + snippet = ""; + char_after(); + +// Lex/loop through each character in "...". + + while (true) { + switch (char) { + case "": + +// test_cause: +// ["\"", "lex_string", "unclosed_string", "", 1] + + return stop_at("unclosed_string", line, column); + case "\\": + char_after_escape(quote); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${\"`\"}`", "lex_string", "unexpected_a", "`", 5] + + warn_at("unexpected_a", line, column, "`"); + } + char_after("`"); + break; + case quote: + +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + the_token = token_create("(string)", snippet); + the_token.quote = quote; + return the_token; + default: + char_after(); + } + } + } + + function lex_token() { + let match; + +// Lex/loop through each whitespace. + + while (true) { + +// Lex/loop through each blank-line. + + while (!line_source) { + line_source = read_line(); + from = 0; + if (line_source === undefined) { + return ( + mode_mega + +// test_cause: +// ["`${//}`", "lex_token", "unclosed_mega", "", 1] + + ? stop_at("unclosed_mega", line_mega, from_mega) + : line_disable !== undefined + +// test_cause: +// ["/*jslint-disable*/", "lex_token", "unclosed_disable", "", 1] + + ? stop_at("unclosed_disable", line_disable) + : token_create("(end)") + ); + } + } + from = column; + match = line_source.match(jslint_rgx_token); + +// match[1] token +// match[2] whitespace +// match[3] identifier +// match[4] number +// match[5] rest + + if (!match) { + +// test_cause: +// ["#", "lex_token", "unexpected_char_a", "#", 1] + + return stop_at( + "unexpected_char_a", + line, + column, + line_source[0] + ); + } + snippet = match[1]; + column += snippet.length; + line_source = match[5]; + if (!match[2]) { + break; + } + } + +// The token is an identifier. + + if (match[3]) { + return token_create(snippet, undefined, true); + } + +// Create token from number. + + if (match[4]) { + return lex_number(); + } + +// Create token from string "..." or '...'. + + if (snippet === "\"" || snippet === "'") { + return lex_string(snippet); + } + +// Create token from megastring `...`. + + if (snippet === "`") { + return lex_megastring(); + } + +// Create token from comment /*...*/ or //.... + + if (snippet === "/*" || snippet === "//") { + return lex_comment(); + } + +// Create token from slash /. + + if (snippet === "/") { + return lex_slash_or_regexp(); + } + return token_create(snippet); + } + + function option_set_item(key, val) { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + switch (key) { + case "beta": // Enable experimental warnings. + case "bitwise": // Allow bitwise operator. + case "browser": // Assume browser environment. + case "convert": // Allow conversion operator. + case "couch": // Assume CouchDb environment. + case "devel": // Allow console.log() and friends. + case "ecma": // Assume ECMAScript environment. + case "eval": // Allow eval(). + case "fart": // Allow complex fat-arrow. + case "for": // Allow for-statement. + case "getset": // Allow get() and set(). + case "indent2": // Use 2-space indent. + case "long": // Allow long lines. + case "node": // Assume Node.js environment. + case "nomen": // Allow weird property name. + case "single": // Allow single-quote strings. + case "subscript": // Allow identifier in subscript-notation. + case "test_cause": // Test jslint's causes. + case "test_internal_error": // Test jslint's internal-error + // ... handling-ability. + case "this": // Allow 'this'. + case "trace": // Include jslint stack-trace in warnings. + case "unordered": // Allow unordered cases, params, properties, + // ... variables, and exports. + case "variable": // Allow unordered const and let declarations + // ... that are not at top of function-scope. + case "white": // Allow messy whitespace. + option_dict[key] = val; + break; + +// PR-404 - Alias "evil" to jslint-directive "eval" for backwards-compat. + + case "evil": + return option_set_item("eval", val); + +// PR-404 - Alias "nomen" to jslint-directive "name" for backwards-compat. + + case "name": + return option_set_item("nomen", val); + default: + return false; + } + +// Initialize global-variables. + + switch (val && key) { + +// Assign global browser variables to global_dict. +/* +// /\*jslint beta, browser, devel*\/ +console.log(JSON.stringify(Object.keys(window).sort(), undefined, 4)); +*/ + + case "browser": + object_assign_from_list(global_dict, [ + +// Shared with Node.js. + + "AbortController", + // "Buffer", + // "Crypto", + // "CryptoKey", + "Event", + "EventTarget", + "MessageChannel", + "MessageEvent", + "MessagePort", + // "Request", + // "Response", + // "SubtleCrypto", + "TextDecoder", + "TextEncoder", + "URL", + "URLSearchParams", + "WebAssembly", + // "__dirname", + // "__filename", + // "atob", + // "btoa", + // "clearImmediate", + "clearInterval", + "clearTimeout", + // "console", + // "crypto", + // "exports", + // "fetch", + // "global", + // "module", + "performance", + // "process", + "queueMicrotask", + // "require", + // "setImmediate", + "setInterval", + "setTimeout", + +// Web worker only. +// https://github.com/mdn/content/blob/main/files/en-us/web/api +// /workerglobalscope/index.md + + "importScripts", + +// Window. + + "Blob", + // "CharacterData", + // "DocumentType", + // "Element", + // "Event", + "FileReader", + // "FontFace", + "FormData", + "IntersectionObserver", + "MutationObserver", + // "Storage", + // "TextDecoder", + // "TextEncoder", + // "URL", + "Worker", + "XMLHttpRequest", + // "caches", + // "clearInterval", + // "clearTimeout", + "document", + // "event", + "fetch", + // "history", + "indexedDb", + "localStorage", + "location", + // "name", + "navigator", + "postMessage", + // "screen", + "sessionStorage", + // "setInterval", + // "setTimeout", + "structuredClone", + "window" + ], "browser"); + break; + +// https://docs.couchdb.org/en/stable/query-server/javascript.html#javascript + + case "couch": + object_assign_from_list(global_dict, [ + "emit", + "getRow", + "isArray", + "log", + "provides", + "registerType", + "require", + "send", + "start", + "sum", + "toJSON" + ], "CouchDb"); + break; + case "devel": + object_assign_from_list(global_dict, [ + "alert", "confirm", "console", "prompt" + ], "development"); + break; + +// These are the globals that are provided by the language standard. +// Assign global ECMAScript variables to global_dict. +/* +node --input-type=module --eval ' +// /\*jslint beta, node*\/ +import https from "https"; +(async function () { + let dict = {import: true}; + let result = ""; + await new Promise(function (resolve) { + https.get(( + "https://raw.githubusercontent.com/mdn/content/main/files" + + "/en-us/web/javascript/reference/global_objects/index.md" + ), function (res) { + res.on("data", function (chunk) { + result += chunk; + }).on("end", resolve).setEncoding("utf8"); + }); + }); + result.replace(( + /\n- \{\{JSxRef\("(?:Global_Objects\/)?([^"\/]+?)"/g + ), function (ignore, key) { + if (globalThis.hasOwnProperty(key)) { + dict[key] = true; + } + return ""; + }); + console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4)); +}()); +' +*/ + + case "ecma": + object_assign_from_list(global_dict, [ + "AggregateError", + "Array", + "ArrayBuffer", + "Atomics", + "BigInt", + "BigInt64Array", + "BigUint64Array", + "Boolean", + "DataView", + "Date", + "Error", + "EvalError", + "Float32Array", + "Float64Array", + "Function", + "Infinity", + "Int16Array", + "Int32Array", + "Int8Array", + "Intl", + "JSON", + "Map", + "Math", + "NaN", + "Number", + "Object", + "Promise", + "Proxy", + "RangeError", + "ReferenceError", + "Reflect", + "RegExp", + "Set", + "SharedArrayBuffer", + "String", + "Symbol", + "SyntaxError", + "TypeError", + "URIError", + "Uint16Array", + "Uint32Array", + "Uint8Array", + "Uint8ClampedArray", + "WeakMap", + "WeakSet", + "WebAssembly", + "decodeURI", + "decodeURIComponent", + "encodeURI", + "encodeURIComponent", + "eval", + "globalThis", + "import", + "isFinite", + "isNaN", + "parseFloat", + "parseInt", + "undefined" + ], "ECMAScript"); + break; + +// Assign global Node.js variables to global_dict. +/* +node --input-type=module --eval ' +// /\*jslint beta, node*\/ +import moduleHttps from "https"; +(async function () { + let dict = Object.create(null); + let result = ""; + await new Promise(function (resolve) { + moduleHttps.get(( + "https://raw.githubusercontent.com/nodejs/node/v16.x/doc/api" + + "/globals.md" + ), function (res) { + res.on("data", function (chunk) { + result += chunk; + }).on("end", resolve).setEncoding("utf8"); + }); + }); + result.replace(( + /\n(?:\* \[`|## |## Class: )`\w+/g + ), function (match0) { + dict[match0.split("`")[1]] = true; + return ""; + }); + console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4)); +}()); +' +*/ + + case "node": + object_assign_from_list(global_dict, [ + "AbortController", + "Buffer", + // "Crypto", + // "CryptoKey", + "Event", + "EventTarget", + "MessageChannel", + "MessageEvent", + "MessagePort", + // "Request", + // "Response", + // "SubtleCrypto", + "TextDecoder", + "TextEncoder", + "URL", + "URLSearchParams", + "WebAssembly", + "__dirname", + "__filename", + // "atob", + // "btoa", + "clearImmediate", + "clearInterval", + "clearTimeout", + "console", + // "crypto", + "exports", + // "fetch", + "global", + "module", + "performance", + "process", + "queueMicrotask", + "require", + "setImmediate", + "setInterval", + "setTimeout" + ], "Node.js"); + break; + } + return true; + } + + function read_digits(base, mode) { + let digits = line_source.match( + base === "b" + ? jslint_rgx_digits_bits + : base === "o" + ? jslint_rgx_digits_octals + : base === "x" + ? jslint_rgx_digits_hexs + : jslint_rgx_digits_decimals + )[0]; + if ( + (mode !== mode_digits_empty_string && digits.length === 0) + || digits[0] === "_" + ) { + +// test_cause: +// ["0x", "read_digits", "expected_digits_after_a", "0x", 2] +// ["0x_", "read_digits", "expected_digits_after_a", "0x", 2] + + warn_at("expected_digits_after_a", line, column, snippet); + } + +// PR-390 - Add numeric-separator check. + + if (mode === mode_digits_numeric_separator) { + check_numeric_separator(digits, column); + } else if (digits.indexOf("_") >= 0) { + +// test_cause: +// ["\"\\u{1_2}\"", "read_digits", "illegal_num_separator", "", 6] + + warn_at( + "illegal_num_separator", + line, + column + digits.indexOf("_") + 1 + ); + } + column += digits.length; + line_source = line_source.slice(digits.length); + snippet += digits; + char_after(); + return digits.length; + } + + function read_line() { + +// Put the next line of source in line_source. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + if ( + !option_dict.long + && line_whole.length > 80 + && line_disable === undefined + && !state.mode_json + && token_1 + && !mode_regexp + ) { + +// test_cause: +// ["/////////////////////////////////////////////////////////////////////////////////", "read_line", "too_long", "", 1] //jslint-ignore-line + + warn_at("too_long", line); + } + column = 0; + line += 1; + mode_regexp = false; + line_source = undefined; + line_whole = ""; + if (line_list[line] === undefined) { + return line_source; + } + line_source = line_list[line].line_source; + line_whole = line_source; + +// Scan each line for following ignore-directives: +// "/*jslint-disable*/" +// "/*jslint-enable*/" +// "//jslint-ignore-line" + + if (line_source === "/*jslint-disable*/") { + +// test_cause: +// ["/*jslint-disable*/", "read_line", "jslint_disable", "", 0] + + test_cause("jslint_disable"); + line_disable = line; + } else if (line_source === "/*jslint-enable*/") { + if (line_disable === undefined) { + +// test_cause: +// ["/*jslint-enable*/", "read_line", "unopened_enable", "", 1] + + stop_at("unopened_enable", line); + } + line_disable = undefined; + } else if ( + line_source.endsWith(" //jslint-ignore-line") + || line_source.endsWith(" //jslint-quiet") + ) { + +// test_cause: +// ["0 //jslint-ignore-line", "read_line", "jslint_ignore_line", "", 0] + + test_cause("jslint_ignore_line"); + line_list[line].directive_ignore_line = true; + } + if (line_disable !== undefined) { + +// test_cause: +// ["/*jslint-disable*/\n0", "read_line", "line_disable", "", 0] + + test_cause("line_disable"); + line_source = ""; + } + // jslint_rgx_tab + if (line_source.indexOf("\t") >= 0) { + if (!option_dict.white) { + +// test_cause: +// ["\t", "read_line", "use_spaces", "", 1] + + warn_at("use_spaces", line, line_source.indexOf("\t") + 1); + } + line_source = line_source.replace(jslint_rgx_tab, " "); + } + if (!option_dict.white && line_source.endsWith(" ")) { + +// test_cause: +// [" ", "read_line", "unexpected_trailing_space", "", 1] + + warn_at("unexpected_trailing_space", line, line_source.length - 1); + } + return line_source; + } + + function token_create(id, value, identifier) { + +// Create the token object and append it to token_list. + + let the_token = { + from, + id, + identifier: Boolean(identifier), + line, + nr: token_list.length, + thru: column, + value + }; + token_list.push(the_token); + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + mode_directive = false; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option_dict.white. + + if ( + token_prv.line === line + && token_prv.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (token_prv.id === "(comment)" || token_prv.id === "(regexp)") + ) { + +// test_cause: +// ["/**//**/", "token_create", "expected_space_a_b", "(comment)", 5] + + warn( + "expected_space_a_b", + the_token, + artifact(token_prv), + artifact(the_token) + ); + } + if (token_prv.id === "." && id === "(number)") { + +// test_cause: +// [".0", "token_create", "expected_a_before_b", ".", 1] + + warn("expected_a_before_b", token_prv, "0", "."); + } + if (token_prv_expr.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart. +// Farts are now detected by keeping a list of most recent "(" tokens at any +// given depth. When a "=>" token is encountered, the most recent "(" token at +// current depth is marked as a fart. + + switch (id) { + case "(": + paren_backtrack_list[paren_depth] = the_token; + paren_depth += 1; + break; + case ")": + paren_depth -= 1; + break; + case "=>": + if ( + token_prv_expr.id === ")" + && paren_backtrack_list[paren_depth] + ) { + paren_backtrack_list[paren_depth].fart = the_token; + } + break; + } + +// The previous token is used to detect adjacency problems. + + token_prv = the_token; + +// The token_prv_expr token is a previous token that was not a comment. +// The token_prv_expr token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (token_prv.id !== "(comment)") { + token_prv_expr = token_prv; + } + return the_token; + } + +// Init global_dict and option_dict. + + option_set_item("ecma", true); + Object.keys(option_dict).sort().forEach(function (key) { + option_set_item(key, option_dict[key] === true); + }); + object_assign_from_list(global_dict, global_list, "user-defined"); + +// Scan first line for "#!" and ignore it. + + if (line_list[jslint_fudge].line_source.startsWith("#!")) { + line += 1; + state.mode_shebang = true; + } + token_1 = lex_token(); + state.mode_json = token_1.id === "{" || token_1.id === "["; + +// Lex/loop through each token until (end). + + while (true) { + if (lex_token().id === "(end)") { + break; + } + } +} + +function jslint_phase3_parse(state) { + +// PHASE 3. Parse into using the Pratt-parser. + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + + let anon = "anonymous"; // The guessed name for anonymous functions. + let { + artifact, + catch_list, + catch_stack, + export_dict, + function_list, + function_stack, + global_dict, + import_list, + is_equal, + option_dict, + property_dict, + stop, + syntax_dict, + tenure, + test_cause, + token_global, + token_list, + warn, + warn_at + } = state; + let catchage = catch_stack[0]; // The current catch-block. + let functionage = token_global; // The current function. + let mode_var; // "var" if using var; "let" if using let. + let token_ii = 0; // The number of the next token. + let token_now = token_global; // The current token being examined in + // ... the parse. + let token_nxt = token_global; // The next token to be examined in + // ... . + + function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if ( + token_now.identifier + && token_now.id !== "function" + && token_now.id !== "async" + ) { + anon = token_now.id; + } else if ( + token_now.id === "(string)" + && jslint_rgx_identifier.test(token_now.value) + ) { + anon = token_now.value; + } + +// Attempt to match token_nxt with an expected id. + + if (id !== undefined && token_nxt.id !== id) { + return ( + match === undefined + +// test_cause: +// ["{0:0}", "advance", "expected_a_b", "0", 2] + + ? stop("expected_a_b", token_nxt, id, artifact()) + +// test_cause: +// ["{\"aa\":0", "advance", "expected_a_b_from_c_d", "{", 1] + + : stop( + "expected_a_b_from_c_d", + token_nxt, + id, + artifact(match), + match.line, + artifact() + ) + ); + } + +// Promote the tokens, skipping comments. + + token_now = token_nxt; + while (true) { + token_nxt = token_list[token_ii]; + state.token_nxt = token_nxt; + token_ii += 1; + if (token_nxt.id !== "(comment)") { + if (token_nxt.id === "(end)") { + token_ii -= 1; + } + break; + } + if (state.mode_json) { + +// test_cause: +// ["[//]", "advance", "unexpected_a", "(comment)", 2] + + warn("unexpected_a"); + } + } + } + + function assignment(id) { + +// Create an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led_infix = function (left) { + const the_token = token_now; + let right; + the_token.arity = "assignment"; + right = parse_expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "preassign" + || right.arity === "postassign" + ) { + warn("unexpected_a", right); + } + check_mutation(left); + return the_token; + }; + return the_symbol; + } + + function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token_now; + if (special !== "body") { + functionage.statement_prv = the_block; + } + the_block.arity = "statement"; + the_block.body = special === "body"; + +// Top level function bodies may include the "use strict" pragma. + + if ( + special === "body" + && function_stack.length === 1 + && token_nxt.value === "use strict" + ) { + token_nxt.statement = true; + advance("(string)"); + advance(";"); + } + stmts = parse_statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option_dict.devel && special !== "ignore") { + +// test_cause: +// ["function aa(){}", "block", "empty_block", "{", 14] + + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; + } + + function check_left(left, right) { + +// Warn if the left is not one of these: +// ?. +// ?: +// e() +// e.b +// e[b] +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !check_left(left.expression[1]) + && !check_left(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "?." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right || token_nxt); + return false; + } + return true; + } + + function check_mutation(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + +// test_cause: +// ["0=0", "check_mutation", "bad_assignment_a", "0", 1] + + warn("bad_assignment_a", the_thing); + return false; + } + return true; + } + + function check_not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === token_global) { + +// test_cause: +// [" +// while(0){} +// ", "check_not_top_level", "unexpected_at_top_level_a", "while", 1] + + warn("unexpected_at_top_level_a", thing); + } + } + + function check_ordered(type, token_list) { + +// This function will warn if is unordered. + + token_list.reduce(function (aa, token) { + const bb = artifact(token); + if (!option_dict.unordered && aa > bb) { + warn("expected_a_b_before_c_d", token, type, bb, type, aa); + } + return bb; + }, ""); + } + + function check_ordered_case(case_list) { + +// This function will warn if is unordered. + + case_list.filter(noop).map(function (token) { + switch (token.identifier || token.id) { + case "(number)": + return { + order: 1, + token, + type: "number", + value: Number(artifact(token)) + }; + case "(string)": + return { + order: 2, + token, + type: "string", + value: artifact(token) + }; + case true: + return { + order: 3, + token, + type: "identifier", + value: artifact(token) + }; + } + }).reduce(function (aa, bb) { + if ( + +// PR-419 - Hide warning about unordered case-statements behind beta-flag. + + option_dict.beta + && !option_dict.unordered + && aa && bb + && ( + aa.order > bb.order + || (aa.order === bb.order && aa.value > bb.value) + ) + ) { + warn( + "expected_a_b_before_c_d", + bb.token, + `case-${bb.type}`, + bb.value, + `case-${aa.type}`, + aa.value + ); + } + return bb; + }, undefined); + } + + function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = token_nxt; + let the_value; + +// test_cause: +// ["do{}while()", "condition", "", "", 0] +// ["if(){}", "condition", "", "", 0] +// ["while(){}", "condition", "", "", 0] + + test_cause(""); + the_paren.free = true; + advance("("); + the_value = parse_expression(0); + advance(")"); + if (the_value.wrapped === true) { + +// test_cause: +// ["while((0)){}", "condition", "unexpected_a", "(", 6] + + warn("unexpected_a", the_paren); + } + +// Check for anticondition. + + switch (the_value.id) { + case "%": + warn("unexpected_a", the_value); + break; + case "&": + warn("unexpected_a", the_value); + break; + case "(number)": + warn("unexpected_a", the_value); + break; + case "(string)": + warn("unexpected_a", the_value); + break; + case "*": + warn("unexpected_a", the_value); + break; + case "+": + warn("unexpected_a", the_value); + break; + case "-": + warn("unexpected_a", the_value); + break; + case "/": + warn("unexpected_a", the_value); + break; + case "<<": + warn("unexpected_a", the_value); + break; + case ">>": + warn("unexpected_a", the_value); + break; + case ">>>": + warn("unexpected_a", the_value); + break; + case "?": + warn("unexpected_a", the_value); + break; + case "^": + warn("unexpected_a", the_value); + break; + case "typeof": + warn("unexpected_a", the_value); + break; + case "|": + warn("unexpected_a", the_value); + break; + case "~": + +// test_cause: +// ["if(0%0){}", "condition", "unexpected_a", "%", 5] +// ["if(0&0){}", "condition", "unexpected_a", "&", 5] +// ["if(0){}", "condition", "unexpected_a", "0", 4] +// ["if(0*0){}", "condition", "unexpected_a", "*", 5] +// ["if(0+0){}", "condition", "unexpected_a", "+", 5] +// ["if(0-0){}", "condition", "unexpected_a", "-", 5] +// ["if(0/0){}", "condition", "unexpected_a", "/", 5] +// ["if(0<<0){}", "condition", "unexpected_a", "<<", 5] +// ["if(0>>0){}", "condition", "unexpected_a", ">>", 5] +// ["if(0>>>0){}", "condition", "unexpected_a", ">>>", 5] +// ["if(0?0:0){}", "condition", "unexpected_a", "?", 5] +// ["if(0^0){}", "condition", "unexpected_a", "^", 5] +// ["if(0|0){}", "condition", "unexpected_a", "|", 5] +// ["if(\"aa\"){}", "condition", "unexpected_a", "aa", 4] +// ["if(typeof 0){}", "condition", "unexpected_a", "typeof", 4] +// ["if(~0){}", "condition", "unexpected_a", "~", 4] + + warn("unexpected_a", the_value); + break; + } + return the_value; + } + + function constant(id, type, value) { + +// Create a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud_prefix = ( + typeof value === "function" + ? value + : function () { + token_now.constant = true; + if (value !== undefined) { + token_now.value = value; + } + return token_now; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; + } + + function constant_Function() { + if (!option_dict.eval) { + +// test_cause: +// ["Function", "constant_Function", "unexpected_a", "Function", 1] + + warn("unexpected_a", token_now); + } else if (token_nxt.id !== "(") { + +// test_cause: +// [" +// /*jslint eval*/ +// Function +// ", "constant_Function", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "(", artifact()); + } + return token_now; + } + + function constant_arguments() { + +// test_cause: +// ["arguments", "constant_arguments", "unexpected_a", "arguments", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function constant_eval() { + if (!option_dict.eval) { + +// test_cause: +// ["eval", "constant_eval", "unexpected_a", "eval", 1] + + warn("unexpected_a", token_now); + } else if (token_nxt.id !== "(") { + +// test_cause: +// ["/*jslint eval*/\neval", "constant_eval", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "(", artifact()); + } + return token_now; + } + + function constant_ignore() { + +// test_cause: +// ["ignore", "constant_ignore", "unexpected_a", "ignore", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function constant_isInfinite() { + +// test_cause: +// ["isFinite", "constant_isInfinite", "expected_a_b", "isFinite", 1] + + warn("expected_a_b", token_now, "Number.isFinite", "isFinite"); + return token_now; + } + + function constant_isNaN() { + +// test_cause: +// ["isNaN(0)", "constant_isNaN", "number_isNaN", "isNaN", 1] + + warn("number_isNaN", token_now); + return token_now; + } + + function constant_this() { + if (!option_dict.this) { + +// test_cause: +// ["this", "constant_this", "unexpected_a", "this", 1] + + warn("unexpected_a", token_now); + } + return token_now; + } + + function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + let earlier; + let id = name.id; + +// Reserved words may not be enrolled. + + if (syntax_dict[id] !== undefined && id !== "ignore") { + +// test_cause: +// ["let undefined", "enroll", "reserved_a", "undefined", 5] + + warn("reserved_a", name); + return; + } + +// Has the name been enrolled in this context? + + earlier = functionage.context[id] || catchage.context[id]; + if (earlier) { + +// test_cause: +// ["let aa;let aa", "enroll", "redefinition_a_b", "1", 12] + + warn("redefinition_a_b", name, id, earlier.line); + return; + } + +// Has the name been enrolled in an outer context? + + function_stack.forEach(function ({ + context + }) { + earlier = context[id] || earlier; + }); + if (earlier && id === "ignore") { + if (earlier.role === "variable") { + +// test_cause: +// ["let ignore;function aa(ignore){}", "enroll", "redefinition_a_b", "1", 24] + + warn("redefinition_a_b", name, id, earlier.line); + } + } else if ( + earlier + && role !== "parameter" && role !== "function" + && (role !== "exception" || earlier.role !== "exception") + ) { + +// test_cause: +// [" +// function aa(){try{aa();}catch(aa){aa();}} +// ", "enroll", "redefinition_a_b", "1", 31] +// ["function aa(){var aa;}", "enroll", "redefinition_a_b", "1", 19] + + warn("redefinition_a_b", name, id, earlier.line); + } else if ( + option_dict.beta + && global_dict[id] + && role !== "parameter" + ) { + +// test_cause: +// ["let Array", "enroll", "redefinition_global_a_b", "Array", 5] + + warn("redefinition_global_a_b", name, global_dict[id], id); + } + +// Enroll it. + + Object.assign(name, { + dead: true, + init: false, + parent: ( + role === "exception" + ? catchage + : functionage + ), + readonly, + role, + used: 0 + }); + name.parent.context[id] = name; + } + + function infix(bp, id, f) { + +// Create an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led_infix = function (left) { + const the_token = token_now; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, parse_expression(bp)]; + return the_token; + }; + return the_symbol; + } + + function infix_dot(left) { + const the_token = token_now; + let name = token_nxt; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "flat" + && name.id !== "flatMap" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + +// test_cause: +// ["\"\".aa", "check_left", "unexpected_a", ".", 3] + + check_left(left, the_token); + } + if (!name.identifier) { + +// test_cause: +// ["aa.0", "infix_dot", "expected_identifier_a", "0", 4] + + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; + } + + function infix_fart_unwrapped() { + +// test_cause: +// ["aa=>0", "infix_fart_unwrapped", "wrap_fart_parameter", "=>", 3] + + return stop("wrap_fart_parameter", token_now); + } + + function infix_grave(left) { + const the_tick = prefix_tick(); + +// test_cause: +// ["0``", "check_left", "unexpected_a", "`", 2] + + check_left(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; + } + + function infix_lbracket(left) { + const the_token = token_now; + let name; + let the_subscript = parse_expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + name = survey(the_subscript); + +// PR-404 - Add new directive "subscript" to play nice with Google Closure. + + if (!option_dict.subscript && jslint_rgx_identifier.test(name)) { + +// test_cause: +// ["aa[`aa`]", "infix_lbracket", "subscript_a", "aa", 4] + + warn("subscript_a", the_subscript, name); + } + } + +// test_cause: +// ["0[0]", "check_left", "unexpected_a", "[", 2] + + check_left(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; + } + + function infix_lparen(left) { + const the_paren = token_now; + let ellipsis; + let the_argument; + if (left.id !== "function") { + +// test_cause: +// ["(0?0:0)()", "check_left", "unexpected_a", "(", 8] +// ["0()", "check_left", "unexpected_a", "(", 2] + + check_left(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (token_nxt.id !== ")") { + +// Parse/loop through each token in expression (...). + + while (true) { + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = parse_expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + +// test_cause: +// ["aa(0)", "infix_lparen", "free", "", 0] + + test_cause("free"); + the_paren.free = true; + if (the_argument.wrapped === true) { + +// test_cause: +// ["aa((0))", "infix_lparen", "unexpected_a", "(", 3] + + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + +// test_cause: +// ["aa()", "infix_lparen", "not_free", "", 0] +// ["aa(0,0)", "infix_lparen", "not_free", "", 0] + + test_cause("not_free"); + the_paren.free = false; + } + return the_paren; + } + + function infix_option_chain(left) { + const the_token = token_now; + let name = token_nxt; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + +// test_cause: +// ["(0+0)?.0", "infix_option_chain", "check_left", "", 0] + + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + test_cause("check_left"); + +// test_cause: +// ["(/./)?.0", "check_left", "unexpected_a", "?.", 6] +// ["\"aa\"?.0", "check_left", "unexpected_a", "?.", 5] +// ["aa=[]?.aa", "check_left", "unexpected_a", "?.", 6] + + check_left(left, the_token); + } + +// Issue #468 - Fix optional dynamic-property/function-call not recognized. + + if (name.id === "[" || name.id === "(") { + test_cause("dyn_prop_or_call"); + +// test_cause: +// ["aa?.(bb)", "infix_option_chain", "dyn_prop_or_call", "", 0] +// ["aa?.[bb]", "infix_option_chain", "dyn_prop_or_call", "", 0] + + return left; + } + if (!name.identifier) { + +// test_cause: +// ["aa?.0", "infix_option_chain", "expected_identifier_a", "0", 5] + + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; + } + + function infixr(bp, id) { + +// Create a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led_infix = function parse_infixr_led(left) { + const the_token = token_now; + +// test_cause: +// ["0**0", "parse_infixr_led", "led_infix", "", 0] + + test_cause("led_infix"); + the_token.arity = "binary"; + the_token.expression = [left, parse_expression(bp - 1)]; + return the_token; + }; + return the_symbol; + } + + function parse_expression(rbp, initial) { + +// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it +// is looking for ad hoc lint patterns. We add .fud_stmt to Pratt's model, which +// is like .nud_prefix except that it is only used on the first token of a +// statement. Having .fud_stmt makes it much easier to define statement-oriented +// languages like JavaScript. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// .nud_prefix Null denotation. The prefix handler. +// .fud_stmt First null denotation. The statement handler. +// .led_infix Left denotation. The infix/postfix handler. +// lbp Left binding power of infix operator. It tells us how strongly +// the operator binds to the argument at its left. +// rbp Right binding power. + +// It processes a nud_prefix (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop (it +// consumes tokens until it meets a token whose lbp <= rbp). Specifically, it +// means that it collects all tokens that bind together before returning to the +// operator that called it. It returns the expression's parse tree. + +// For example, "3 + 1 * 2 * 4 + 5" +// parses into +// { +// "id": "+", +// "expression": [ +// { +// "id": "+", +// "expression": [ +// { +// "id": "(number)", +// "value": "3" +// }, +// { +// "id": "*", +// "expression": [ +// { +// "id": "*", +// "expression": [ +// { +// "id": "(number)", +// "value": "1" +// }, +// { +// "id": "(number)", +// "value": "2" +// } +// ] +// }, +// { +// "id": "(number)", +// "value": "4" +// } +// ] +// } +// ] +// }, +// { +// "id": "(number)", +// "value": "5" +// } +// ] +// } + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement. + + if (!initial) { + advance(); + } + the_symbol = syntax_dict[token_now.id]; + if (the_symbol !== undefined && the_symbol.nud_prefix !== undefined) { + +// test_cause: +// ["0", "parse_expression", "symbol", "", 0] + + test_cause("symbol"); + left = the_symbol.nud_prefix(); + } else if (token_now.identifier) { + +// test_cause: +// ["aa", "parse_expression", "identifier", "", 0] + + test_cause("identifier"); + left = token_now; + left.arity = "variable"; + } else { + +// test_cause: +// ["!", "parse_expression", "unexpected_a", "(end)", 1] +// ["/./", "parse_expression", "unexpected_a", "/", 1] +// ["let aa=`${}`;", "parse_expression", "unexpected_a", "}", 11] + + return stop("unexpected_a", token_now); + } + +// Parse/loop through each symbol in expression. + + while (true) { + the_symbol = syntax_dict[token_nxt.id]; + if ( + the_symbol === undefined + || the_symbol.led_infix === undefined + || the_symbol.lbp <= rbp + ) { + break; + } + advance(); + left = the_symbol.led_infix(left); + } + return left; + } + + function parse_fart(the_fart) { + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + Object.assign(the_fart, { + arity: "binary", + context: empty(), + finally: 0, + level: functionage.level + 1, + loop: 0, + name: anon, + switch: 0, + try: 0 + }); + +// PR-384 - Relax warning "function_in_loop". +// +// if (functionage.loop > 0) { + +// // test_cause: +// // ["while(0){aa.map(()=>0);}", "parse_fart", "function_in_loop", "=>", 19] +// +// warn("function_in_loop", the_fart); +// } + +// Push the current function context and establish a new one. + + function_list.push(the_fart); + function_stack.push(functionage); + functionage = the_fart; + +// Parse the parameter list. + + prefix_function_parameter(the_fart); + advance("=>"); + +// The function's body is a block. + + if (token_nxt.id === "{") { + if (!option_dict.fart) { + +// test_cause: +// ["()=>{}", "parse_fart", "use_function_not_fart", "=>", 3] + + warn("use_function_not_fart", the_fart); + } + the_fart.block = block("body"); + } else if ( + syntax_dict[token_nxt.id] !== undefined + && syntax_dict[token_nxt.id].fud_stmt !== undefined + ) { + +// PR-384 - Bugfix - Fixes issue #379 - warn against naked-statement in fart. + +// test_cause: +// ["()=>delete aa", "parse_fart", "unexpected_a_after_b", "=>", 5] + + stop("unexpected_a_after_b", token_nxt, token_nxt.id, "=>"); + +// The function's body is an expression. + + } else { + the_fart.expression = parse_expression(0); + } + +// Restore the previous context. + + functionage = function_stack.pop(); + return the_fart; + } + + function parse_json() { + let container; + let is_dup; + let name; + let negative; + switch (token_nxt.id) { + case "(number)": + if (!jslint_rgx_json_number.test(token_nxt.value)) { + +// test_cause: +// ["[-.0]", "parse_json", "unexpected_a", ".", 3] +// ["[-0x0]", "parse_json", "unexpected_a", "0x0", 3] +// ["[.0]", "parse_json", "unexpected_a", ".", 2] +// ["[0x0]", "parse_json", "unexpected_a", "0x0", 2] + + warn("unexpected_a"); + } + advance("(number)"); + return token_now; + case "(string)": + if (token_nxt.quote !== "\"") { + +// test_cause: +// ["['']", "parse_json", "unexpected_a", "'", 2] + + warn("unexpected_a", token_nxt, token_nxt.quote); + } + advance("(string)"); + return token_now; + case "-": + negative = token_nxt; + negative.arity = "unary"; + advance("-"); + +// Recurse parse_json(). + + negative.expression = parse_json(); + return negative; + case "[": + +// test_cause: +// ["[]", "parse_json", "bracket", "", 0] + + test_cause("bracket"); + container = token_nxt; + container.expression = []; + advance("["); + if (token_nxt.id !== "]") { + while (true) { + +// Recurse parse_json(). + + container.expression.push(parse_json()); + if (token_nxt.id !== ",") { + +// test_cause: +// ["[0,0]", "parse_json", "comma", "", 0] + + test_cause("comma"); + break; + } + advance(","); + } + } + advance("]", container); + return container; + case "false": + case "null": + case "true": + +// test_cause: +// ["[false]", "parse_json", "constant", "", 0] +// ["[null]", "parse_json", "constant", "", 0] +// ["[true]", "parse_json", "constant", "", 0] + + test_cause("constant"); + advance(); + return token_now; + case "{": + +// test_cause: +// ["{}", "parse_json", "brace", "", 0] + + test_cause("brace"); + container = token_nxt; + +// Explicit empty-object required to detect "__proto__". + + is_dup = empty(); + container.expression = []; + advance("{"); + if (token_nxt.id !== "}") { + +// JSON +// Parse/loop through each property in {...}. + + while (true) { + if (token_nxt.quote !== "\"") { + +// test_cause: +// ["{0:0}", "parse_json", "unexpected_a", "0", 2] + + warn( + "unexpected_a", + token_nxt, + token_nxt.quote + ); + } + name = token_nxt; + advance("(string)"); + if (is_dup[token_now.value] !== undefined) { + +// test_cause: +// ["{\"aa\":0,\"aa\":0}", "parse_json", "duplicate_a", "aa", 9] + + warn("duplicate_a", token_now); + } else if (token_now.value === "__proto__") { + +// test_cause: +// ["{\"__proto__\":0}", "parse_json", "weird_property_a", "__proto__", 2] + + warn("weird_property_a", token_now); + } else { + is_dup[token_now.value] = token_now; + } + advance(":"); + container.expression.push( + +// Recurse parse_json(). + + Object.assign(parse_json(), { + label: name + }) + ); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance("}", container); + return container; + default: + +// test_cause: +// ["[undefined]", "parse_json", "unexpected_a", "undefined", 2] + + stop("unexpected_a"); + } + } + + function parse_statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token_now.identifier && token_nxt.id === ":") { + the_label = token_now; + if (the_label.id === "ignore") { + +// test_cause: +// ["ignore:", "parse_statement", "unexpected_a", "ignore", 1] + + warn("unexpected_a", the_label); + } + advance(":"); + switch (token_nxt.id) { + case "do": + case "for": + case "switch": + case "while": + +// test_cause: +// ["aa:do{}", "parse_statement", "the_statement_label", "do", 0] +// ["aa:for{}", "parse_statement", "the_statement_label", "for", 0] +// ["aa:switch{}", "parse_statement", "the_statement_label", "switch", 0] +// ["aa:while{}", "parse_statement", "the_statement_label", "while", 0] + + test_cause("the_statement_label", token_nxt.id); + enroll(the_label, "label", true); + the_label.dead = false; + the_label.init = true; + the_statement = parse_statement(); + functionage.statement_prv = the_statement; + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + +// test_cause: +// ["aa:", "parse_statement", "unexpected_label_a", "aa", 1] + + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token_now; + first.statement = true; + the_symbol = syntax_dict[first.id]; + if ( + the_symbol !== undefined + && the_symbol.fud_stmt !== undefined + +// PR-318 - Bugfix - Fixes issues #316, #317 - dynamic-import(). + + && !(the_symbol.id === "import" && token_nxt.id === "(") + ) { + the_symbol.disrupt = false; + the_symbol.statement = true; + token_now.arity = "statement"; + the_statement = the_symbol.fud_stmt(); + functionage.statement_prv = the_statement; + } else { + +// It is an expression statement. + + the_statement = parse_expression(0, true); + functionage.statement_prv = the_statement; + if (the_statement.wrapped && the_statement.id !== "(") { + +// test_cause: +// ["(0)", "parse_statement", "unexpected_a", "(", 1] + + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; + } + + function parse_statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const statement_list = []; + let a_statement; + let disrupt = false; + +// Parse/loop each statement until a statement-terminator is reached. + + while (true) { + switch (token_nxt.id) { + case "(end)": + case "case": + case "default": + case "else": + case "}": + +// test_cause: +// [";", "parse_statements", "closer", "", 0] +// ["case", "parse_statements", "closer", "", 0] +// ["default", "parse_statements", "closer", "", 0] +// ["else", "parse_statements", "closer", "", 0] +// ["}", "parse_statements", "closer", "", 0] + + test_cause("closer"); + return statement_list; + } + a_statement = parse_statement(); + statement_list.push(a_statement); + if (disrupt) { + +// test_cause: +// ["while(0){break;0;}", "parse_statements", "unreachable_a", "0", 16] + + warn("unreachable_a", a_statement); + } + disrupt = a_statement.disrupt; + } + } + + function postassign(id) { + +// Create one of the postassign operators. + + const the_symbol = symbol(id, 150); + the_symbol.led_infix = function (left) { + token_now.expression = left; + token_now.arity = "postassign"; + check_mutation(token_now.expression); + return token_now; + }; + return the_symbol; + } + + function preassign(id) { + +// Create one of the preassign operators. + + const the_symbol = symbol(id); + the_symbol.nud_prefix = function () { + const the_token = token_now; + the_token.arity = "preassign"; + the_token.expression = parse_expression(150); + check_mutation(the_token.expression); + return the_token; + }; + return the_symbol; + } + + function prefix(id, f) { + +// Create a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud_prefix = function () { + const the_token = token_now; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = parse_expression(150); + return the_token; + }; + return the_symbol; + } + + function prefix_assign_divide() { + +// test_cause: +// ["/=", "prefix_assign_divide", "expected_a_b", "/=", 1] + + stop("expected_a_b", token_now, "/\\=", "/="); + } + + function prefix_async() { + let the_async = token_now; + let the_function; + token_nxt.arity = the_async.arity; + +// PR-414 - Parse async fart. + + if (token_nxt.fart) { + advance("("); + the_function = Object.assign(token_now.fart, { + async: 1 + }); + if (!option_dict.fart) { + +// test_cause: +// ["async()=>0", "prefix_async", "use_function_not_fart", "=>", 8] + + warn("use_function_not_fart", the_function); + } + prefix_lparen(); + +// Parse async function. + + } else { + advance("function"); + the_function = Object.assign(token_now, { + async: 1 + }); + prefix_function(); + } + if (the_function.async === 1) { + +// test_cause: +// [" +// async function aa(){} +// ", "prefix_async", "missing_await_statement", "function", 7] + + warn("missing_await_statement", the_function); + } + return the_function; + } + + function prefix_await() { + const the_await = token_now; + +// PR-370 - Add top-level-await support. + + if (functionage.async === 0 && functionage !== token_global) { + +// test_cause: +// ["function aa(){aa=await 0;}", "prefix_await", "unexpected_a", "await", 18] +// ["function aa(){await 0;}", "prefix_await", "unexpected_a", "await", 15] + + warn("unexpected_a", the_await); + } else { + functionage.async += 1; + } + if (the_await.arity === "statement") { + +// PR-405 - Bugfix - fix expression after "await" mis-identified as statement. + + the_await.expression = parse_expression(150); + semicolon(); + } else { + the_await.expression = parse_expression(150); + } + return the_await; + } + + function prefix_fart() { + +// test_cause: +// ["=>0", "prefix_fart", "expected_a_before_b", "=>", 1] + + return stop("expected_a_before_b", token_now, "()", "=>"); + } + + function prefix_function(the_function) { + let name = the_function && the_function.name; + if (the_function === undefined) { + the_function = token_now; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!token_nxt.identifier) { + +// test_cause: +// ["function(){}", "prefix_function", "expected_identifier_a", "(", 9] +// ["function*aa(){}", "prefix_function", "expected_identifier_a", "*", 9] + + return stop("expected_identifier_a"); + } + name = token_nxt; + enroll(name, "variable", true); + the_function.name = Object.assign(name, { + calls: empty(), + +// PR-331 - Bugfix - Fixes issue #272 - function hoisting not allowed. + + dead: false, + init: true + }); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + the_function.name = anon; + if (token_nxt.identifier) { + name = token_nxt; + the_function.name = name; + advance(); + } + } + } + +// Probably deadcode. +// if (mode_mega) { +// warn("unexpected_a", the_function); +// } +// jslint_assert(!mode_mega, `Expected !mode_mega.`); + +// PR-378 - Relax warning "function_in_loop". +// +// // Don't create functions in loops. It is inefficient, and it can lead to +// // scoping errors. +// +// if (functionage.loop > 0) { +// +// // test_cause: +// // [" +// // while(0){aa.map(function(){});} +// // ", "prefix_function", "function_in_loop", "function", 17] +// +// warn("function_in_loop", the_function); +// } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + Object.assign(the_function, { + async: the_function.async || 0, + context: empty(), + finally: 0, + level: functionage.level + 1, + loop: 0, + statement_prv: undefined, + switch: 0, + try: 0 + }); + if (the_function.arity !== "statement" && typeof name === "object") { + +// test_cause: +// ["let aa=function bb(){return;};", "prefix_function", "expression", "bb", 0] + + test_cause("expression", name.id); + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// PR-334 - Bugfix - fix function-redefinition not warned inside function-call. +// Push the current function context and establish a new one. + + function_list.push(the_function); + function_stack.push(functionage); + functionage = the_function; + +// Parse the parameter list. + + advance("("); + token_now.arity = "function"; + prefix_function_parameter(the_function); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && token_nxt.line === token_now.line + ) { + +// test_cause: +// ["function aa(){}0", "prefix_function", "unexpected_a", "0", 16] + + return stop("unexpected_a"); + } + if ( + token_nxt.id === "." + || token_nxt.id === "?." + +// PR-459 - Allow destructuring-assignment after function-definition. + + // || token_nxt.id === "[" + ) { + +// test_cause: +// ["function aa(){}\n.aa", "prefix_function", "unexpected_a", ".", 1] +// ["function aa(){}\n?.aa", "prefix_function", "unexpected_a", "?.", 1] + + warn("unexpected_a"); + } + +// Check functions are ordered. + + check_ordered( + "function", + function_list.slice( + function_list.indexOf(the_function) + 1 + ).map(function ({ + level, + name + }) { + return (level === the_function.level + 1) && name; + }).filter(function (name) { + return option_dict.beta && name && name.id; + }) + ); + +// Restore the previous context. + + functionage = function_stack.pop(); + return the_function; + } + + function prefix_function_parameter(the_function) { + +// This function will parse input at beginning of + + let optional; + let parameters = []; + let signature = ["("]; + let subparam; + function param_enroll(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + +// test_cause: +// ["([aa])=>0", "param_enroll", "use_function_not_fart", "=>", 7] +// ["({aa})=>0", "param_enroll", "use_function_not_fart", "=>", 7] + + if (the_function.id === "=>" && !option_dict.fart) { + warn("use_function_not_fart", the_function); + } + +// Recurse param_enroll(). + + name.names.forEach(param_enroll); + } + } + function param_parse() { + let ellipsis = false; + let param; + if (token_nxt.id === "{") { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,{}){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + param = token_nxt; + param.names = []; + advance("{"); + signature.push("{"); + while (true) { + subparam = token_nxt; + if (!subparam.identifier) { + +// test_cause: +// ["function aa(aa=0,{}){}", "param_parse", "expected_identifier_a", "}", 19] +// ["function aa({0}){}", "param_parse", "expected_identifier_a", "0", 14] + + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (token_nxt.id === ":") { + advance(":"); + advance(); + token_now.label = subparam; + subparam = token_now; + if (!subparam.identifier) { + +// test_cause: +// ["function aa({aa:0}){}", "param_parse", "expected_identifier_a", "}", 18] + + return stop( + "expected_identifier_a", + token_nxt + ); + } + } + +// test_cause: +// ["function aa({aa=aa},aa){}", "param_parse", "equal", "", 0] + + test_cause("equal"); + if (token_nxt.id === "=") { + advance("="); + subparam.expression = parse_expression(); + param.open = true; + } + param.names.push(subparam); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + } else { + break; + } + } + parameters.push(param); + +// test_cause: +// [" +// function aa({bb,aa}){} +// ", "check_ordered", "expected_a_b_before_c_d", "aa", 17] + + check_ordered("parameter", param.names); + advance("}"); + signature.push("}"); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } else if (token_nxt.id === "[") { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,[]){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + param = token_nxt; + param.names = []; + advance("["); + signature.push("[]"); + while (true) { + subparam = token_nxt; + if (!subparam.identifier) { + +// test_cause: +// ["function aa(aa=0,[]){}", "param_parse", "expected_identifier_a", "]", 19] + + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + +// test_cause: +// ["function aa([aa=aa],aa){}", "param_parse", "id", "", 0] + + test_cause("id"); + if (token_nxt.id === "=") { + advance("="); + subparam.expression = parse_expression(); + param.open = true; + } + if (token_nxt.id === ",") { + advance(","); + } else { + break; + } + } + parameters.push(param); + advance("]"); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } else { + if (token_nxt.id === "...") { + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,...){}", "param_parse", "required_a_optional_b", "aa", 21] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + } + if (!token_nxt.identifier) { + +// test_cause: +// ["function aa(0){}", "param_parse", "expected_identifier_a", "0", 13] + + return stop("expected_identifier_a"); + } + param = token_nxt; + parameters.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (token_nxt.id === "=") { + optional = param; + advance("="); + param.expression = parse_expression(0); + } else { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,bb){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } + } + } + +// test_cause: +// ["function aa(){}", "prefix_function_parameter", "opener", "(", 0] + + test_cause("opener", token_now.id); + token_now.free = false; + if (token_nxt.id !== ")" && token_nxt.id !== "(end)") { + param_parse(); + } + advance(")"); + signature.push(")"); + parameters.forEach(param_enroll); + the_function.parameters = parameters; + the_function.signature = signature.join(""); + } + + function prefix_lbrace() { + const seen = empty(); + const the_brace = token_now; + let extra; + let full; + let id; + let name; + let the_colon; + let value; + the_brace.expression = []; + if (token_nxt.id !== "}") { + +// Parse/loop through each property in {...}. + + while (true) { + name = token_nxt; + advance(); + if ( + (name.id === "get" || name.id === "set") + && token_nxt.identifier + ) { + if (!option_dict.getset) { + +// test_cause: +// ["aa={get aa(){}}", "prefix_lbrace", "unexpected_a", "get", 5] + + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + token_nxt.id; + name = token_nxt; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + +// test_cause: +// ["aa={get aa(){},get aa(){}}", "prefix_lbrace", "duplicate_a", "aa", 20] + + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else if (name.id === "`") { + +// test_cause: +// ["aa={`aa`:0}", "prefix_lbrace", "unexpected_a", "`", 5] + + stop("unexpected_a", name); + + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + +// test_cause: +// ["aa={aa,aa}", "prefix_lbrace", "duplicate_a", "aa", 8] + + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (token_nxt.id === "}" || token_nxt.id === ",") { + if (typeof extra === "string") { + +// test_cause: +// ["aa={get aa}", "prefix_lbrace", "closer", "", 0] + + test_cause("closer"); + advance("("); + } + value = parse_expression(Infinity, true); + } else if (token_nxt.id === "(") { + +// test_cause: +// ["aa={aa()}", "prefix_lbrace", "paren", "", 0] +// ["aa={get aa(){}}", "prefix_lbrace", "paren", "", 0] + + test_cause("paren"); + value = prefix_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + +// test_cause: +// ["aa={get aa.aa}", "prefix_lbrace", "paren", "", 0] + + test_cause("paren"); + advance("("); + } + the_colon = token_nxt; + advance(":"); + value = parse_expression(0); + if ( + value.id === name.id + && value.id !== "function" + ) { + +// test_cause: +// ["aa={aa:aa}", "prefix_lbrace", "unexpected_a", ": aa", 7] + + warn("unexpected_a", the_colon, ": " + name.id); + } + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + +// test_cause: +// ["aa={\"aa\":0}", "prefix_lbrace", "colon", "", 0] + + test_cause("colon"); + advance(":"); + value = parse_expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (token_nxt.id !== ",") { + break; + } + +// test_cause: +// ["aa={\"aa\":0,\"bb\":0}", "prefix_lbrace", "comma", "", 0] + + test_cause("comma"); + advance(","); + if (token_nxt.id === "}") { + +// test_cause: +// ["let aa={aa:0,}", "prefix_lbrace", "unexpected_a", ",", 13] + + warn("unexpected_a", token_now); + break; + } + } + } + +// test_cause: +// ["aa={bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8] + + check_ordered( + "property", + the_brace.expression.map(function ({ + label + }) { + return label; + }) + ); + advance("}"); + return the_brace; + } + + function prefix_lbracket() { + const the_token = token_now; + let element; + let ellipsis; + the_token.expression = []; + if (token_nxt.id !== "]") { + +// Parse/loop through each element in [...]. + + while (true) { + ellipsis = false; + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + element = parse_expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (token_nxt.id !== ",") { + break; + } + advance(","); + if (token_nxt.id === "]") { + +// test_cause: +// ["let aa=[0,]", "prefix_lbracket", "unexpected_a", ",", 10] + + warn("unexpected_a", token_now); + break; + } + } + } + advance("]"); + return the_token; + } + + function prefix_lparen() { + let the_paren = token_now; + let the_value; + +// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart. + + if (token_now.fart) { + return parse_fart(token_now.fart); + } + +// test_cause: +// ["(0)", "prefix_lparen", "expr", "", 0] + + test_cause("expr"); + the_paren.free = true; + the_value = parse_expression(0); + if (the_value.wrapped === true) { + +// test_cause: +// ["((0))", "prefix_lparen", "unexpected_a", "(", 1] + + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + return the_value; + } + + function prefix_new() { + const the_new = token_now; + let right; + right = parse_expression(160); + if (token_nxt.id !== "(") { + +// test_cause: +// ["new aa", "prefix_new", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "()", artifact()); + } + the_new.expression = right; + return the_new; + } + + function prefix_tick() { + const the_tick = token_now; + the_tick.value = []; + the_tick.expression = []; + if (token_nxt.id !== "`") { + +// Parse/loop through each token in `${...}`. + + while (true) { + advance("(string)"); + the_tick.value.push(token_now); + if (token_nxt.id !== "${") { + break; + } + advance("${"); + +// test_cause: +// ["let aa=`${}`;", "prefix_tick", "${", "", 0] + + test_cause("${"); + the_tick.expression.push(parse_expression(0)); + advance("}"); + } + } + advance("`"); + return the_tick; + } + + function prefix_void() { + const the_void = token_now; + +// test_cause: +// ["void 0", "prefix_void", "unexpected_a", "void", 1] +// ["void", "prefix_void", "unexpected_a", "void", 1] + + warn("unexpected_a", the_void); + the_void.expression = parse_expression(0); + return the_void; + } + + function semicolon() { + +// Try to match a semicolon. + + if (token_nxt.id === ";") { + advance(";"); + } else { + +// test_cause: +// ["0", "semicolon", "expected_a_b", "(end)", 1] + + warn_at( + "expected_a_b", + token_now.line, + token_now.thru + 1, + ";", + artifact() + ); + } + anon = "anonymous"; + } + + function stmt(id, fud_stmt) { + +// Create a statement. + + const the_symbol = symbol(id); + the_symbol.fud_stmt = fud_stmt; + return the_symbol; + } + + function stmt_break() { + const the_break = token_now; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + +// test_cause: +// ["break", "stmt_break", "unexpected_a", "break", 1] + + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (token_nxt.identifier && token_now.line === token_nxt.line) { + the_label = functionage.context[token_nxt.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + if (the_label !== undefined && the_label.dead) { + +// test_cause: +// ["aa:{function aa(aa){break aa;}}", "stmt_break", "out_of_scope_a", "aa", 27] + + warn("out_of_scope_a"); + } else { + +// test_cause: +// ["aa:{break aa;}", "stmt_break", "not_label_a", "aa", 11] + + warn("not_label_a"); + } + } else { + the_label.used += 1; + } + the_break.label = token_nxt; + advance(); + } + advance(";"); + return the_break; + } + + function stmt_continue() { + const the_continue = token_now; + if (functionage.loop < 1 || functionage.finally > 0) { + +// test_cause: +// ["continue", "stmt_continue", "unexpected_a", "continue", 1] +// [" +// function aa(){while(0){try{}finally{continue}}} +// ", "stmt_continue", "unexpected_a", "continue", 37] + + warn("unexpected_a", the_continue); + } + check_not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; + } + + function stmt_debugger() { + const the_debug = token_now; + if (!option_dict.devel) { + +// test_cause: +// ["debugger", "stmt_debugger", "unexpected_a", "debugger", 1] + + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; + } + + function stmt_delete() { + const the_token = token_now; + const the_value = parse_expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + +// test_cause: +// ["delete 0", "stmt_delete", "expected_a_b", "0", 8] + + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; + } + + function stmt_do() { + const the_do = token_now; + check_not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + +// test_cause: +// ["function aa(){do{break;}while(0)}", "stmt_do", "weird_loop", "do", 15] + + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; + } + + function stmt_export() { + let export_list = []; + let the_export = token_now; + let the_id; + let the_name; + let the_thing; + + the_export.expression = []; + if (token_nxt.id === "default") { + if (export_dict.default !== undefined) { + +// test_cause: +// [" +// export default 0;export default 0 +// ", "stmt_export", "duplicate_a", "default", 25] + + warn("duplicate_a"); + } + advance("default"); + the_thing = parse_expression(0); + if ( + the_thing.id !== "(" + || the_thing.expression[0].id !== "." + || the_thing.expression[0].expression.id !== "Object" + || the_thing.expression[0].name.id !== "freeze" + ) { + +// test_cause: +// ["export default {}", "stmt_export", "freeze_exports", "{", 16] + + warn("freeze_exports", the_thing); + +// PR-301 - Bugfix - Fixes issues #282 - optional-semicolon. + + } else { + +// test_cause: +// [" +// export default Object.freeze({}) +// ", "semicolon", "expected_a_b", "(end)", 32] + + semicolon(); + } + export_dict.default = the_thing; + the_export.expression.push(the_thing); + } else { + +// PR-439 - Add grammar for "export async function ...". + + if (token_nxt.id === "function" || token_nxt.id === "async") { + +// test_cause: +// ["export async function aa(){}", "stmt_export", "freeze_exports", "async", 8] +// ["export function aa(){}", "stmt_export", "freeze_exports", "function", 8] + + warn("freeze_exports"); + the_thing = parse_statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (export_dict[the_id] !== undefined) { + +// test_cause: +// [" +// let aa;export{aa};export function aa(){} +// ", "stmt_export", "duplicate_a", "aa", 35] + + warn("duplicate_a", the_name); + } + export_dict[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + token_nxt.id === "var" + || token_nxt.id === "let" + || token_nxt.id === "const" + ) { + +// test_cause: +// ["export const", "stmt_export", "unexpected_a", "const", 8] +// ["export let", "stmt_export", "unexpected_a", "let", 8] +// ["export var", "stmt_export", "unexpected_a", "var", 8] + + warn("unexpected_a"); + parse_statement(); + } else if (token_nxt.id === "{") { + +// test_cause: +// ["export {}", "stmt_export", "advance{", "", 0] + + test_cause("advance{"); + advance("{"); + while (true) { + if (!token_nxt.identifier) { + +// test_cause: +// ["export {}", "stmt_export", "expected_identifier_a", "}", 9] + + stop("expected_identifier_a"); + } + the_id = token_nxt.id; + export_list.push(token_nxt); + the_name = token_global.context[the_id]; + if (the_name === undefined) { + +// test_cause: +// ["export {aa}", "stmt_export", "unexpected_a", "aa", 9] + + warn("unexpected_a"); + } else { + the_name.used += 1; + if (export_dict[the_id] !== undefined) { + +// test_cause: +// ["let aa;export{aa,aa}", "stmt_export", "duplicate_a", "aa", 18] + + warn("duplicate_a"); + } + export_dict[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + if (token_nxt.id === ",") { + advance(","); + } else { + break; + } + } + +// PR-439 - Check exported properties are ordered. + +// test_cause: +// ["export {bb, aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 13] + + check_ordered("export", export_list); + advance("}"); + semicolon(); + } else { + +// test_cause: +// ["export", "stmt_export", "unexpected_a", "(end)", 1] + + stop("unexpected_a"); + } + } + state.mode_module = true; + return the_export; + } + + function stmt_for() { + let first; + let the_for = token_now; + if (!option_dict.for) { + +// test_cause: +// ["for", "stmt_for", "unexpected_a", "for", 1] + + warn("unexpected_a", the_for); + } + check_not_top_level(the_for); + functionage.loop += 1; + advance("("); + token_now.free = true; + if (token_nxt.id === ";") { + +// test_cause: +// ["for(;;){}", "stmt_for", "expected_a_b", "for (;", 1] + + return stop("expected_a_b", the_for, "while (", "for (;"); + } + switch (token_nxt.id) { + case "const": + case "let": + case "var": + +// test_cause: +// ["for(const aa in aa){}", "stmt_for", "unexpected_a", "const", 5] + + return stop("unexpected_a"); + } + first = parse_expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + +// test_cause: +// ["for(0 in aa){}", "stmt_for", "bad_assignment_a", "0", 5] + + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = parse_expression(0); + advance(";"); + the_for.inc = parse_expression(0); + if (the_for.inc.id === "++") { + +// test_cause: +// ["for(aa;aa;aa++){}", "stmt_for", "expected_a_b", "++", 13] + + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + +// test_cause: +// [" +// /*jslint for*/ +// function aa(bb,cc){for(0;0;0){break;}} +// ", "stmt_for", "weird_loop", "for", 20] + + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; + } + + function stmt_if() { + const the_if = token_now; + let the_else; + the_if.expression = condition(); + the_if.block = block(); + if (token_nxt.id === "else") { + advance("else"); + the_else = token_now; + the_if.else = ( + token_nxt.id === "if" + ? parse_statement() + : block() + ); + +// test_cause: +// ["if(0){0}else if(0){0}", "stmt_if", "else", "", 0] +// ["if(0){0}else{0}", "stmt_if", "else", "", 0] + + test_cause("else"); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + +// test_cause: +// ["if(0){break;}else{break;}", "stmt_if", "disrupt", "", 0] + + test_cause("disrupt"); + the_if.disrupt = true; + } else { + +// test_cause: +// ["if(0){break;}else{}", "stmt_if", "unexpected_a", "else", 14] + + warn("unexpected_a", the_else); + } + } + } + return the_if; + } + + function stmt_import() { + const the_import = token_now; + let name; + let names; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// if (typeof state.mode_module === "object") { +// +// // test_cause: +// // [" +// // /*global aa*/ +// // import aa from "aa" +// // ", "stmt_import", "unexpected_directive_a", "global", 1] +// +// warn( +// "unexpected_directive_a", +// state.mode_module, +// state.mode_module.directive +// ); +// } + + state.mode_module = true; + +// PR-436 - Add grammar for side-effect import-statement. + + if (token_nxt.id === "(string)") { + +// test_cause: +// ["import \"./aa.mjs\";", "stmt_import", "import_side_effect", "", 0] + + test_cause("import_side_effect"); + warn("expected_a_b", token_nxt, "{", artifact()); + advance(); + semicolon(); + return the_import; + } + if (token_nxt.identifier) { + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// ["import ignore from \"aa\"", "stmt_import", "unexpected_a", "ignore", 8] + + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + names = []; + advance("{"); + if (token_nxt.id !== "}") { + while (true) { + if (!token_nxt.identifier) { + +// test_cause: +// ["import {", "stmt_import", "expected_identifier_a", "(end)", 1] + + stop("expected_identifier_a"); + } + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// ["import {ignore} from \"aa\"", "stmt_import", "unexpected_a", "ignore", 9] + + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token_now; + if (!jslint_rgx_module.test(token_now.value)) { + +// test_cause: +// ["import aa from \"!aa\"", "stmt_import", "bad_module_name_a", "!aa", 16] + + warn("bad_module_name_a", token_now); + } + import_list.push(token_now.value); + semicolon(); + return the_import; + } + + function stmt_lbrace() { + +// test_cause: +// [";{}", "stmt_lbrace", "naked_block", "{", 2] +// ["class aa{}", "stmt_lbrace", "naked_block", "{", 9] + + warn("naked_block", token_now); + return block("naked"); + } + + function stmt_return() { + const the_return = token_now; + check_not_top_level(the_return); + if (functionage.finally > 0) { + +// test_cause: +// [" +// function aa(){try{}finally{return;}} +// ", "stmt_return", "unexpected_a", "return", 28] + + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (token_nxt.id !== ";" && the_return.line === token_nxt.line) { + the_return.expression = parse_expression(10); + } + advance(";"); + return the_return; + } + + function stmt_semicolon() { + +// test_cause: +// [";", "stmt_semicolon", "unexpected_a", ";", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function stmt_switch() { + const the_cases = []; + const the_switch = token_now; + let dups = []; + let exp; + let last; + let stmts; + let the_case; + let the_default; + let the_disrupt = true; + let the_last; + function is_dup(thing) { + return is_equal(thing, exp); + } + check_not_top_level(the_switch); + if (functionage.finally > 0) { + +// test_cause: +// [" +// function aa(){try{}finally{switch(0){}}} +// ", "stmt_switch", "unexpected_a", "switch", 28] + + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token_now.free = true; + the_switch.expression = parse_expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + while (true) { + +// Loop through cases with breaks. + + the_case = token_nxt; + the_case.arity = "statement"; + the_case.expression = []; + while (true) { + +// Loop through fallthrough cases. + + advance("case"); + token_now.switch = true; + exp = parse_expression(0); + if (dups.some(is_dup)) { + +// test_cause: +// [" +// switch(0){case 0:break;case 0:break} +// ", "stmt_switch", "unexpected_a", "0", 29] + + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (token_nxt.id !== "case") { + break; + } + } + +// test_cause: +// [" +// switch(0){case 1:case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 23] +// [" +// switch(0){case "aa":case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 26] +// [" +// switch(0){case "bb":case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 26] +// [" +// switch(0){case aa:case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24] +// [" +// switch(0){case bb:case aa:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24] + + check_ordered_case(the_case.expression); + stmts = parse_statements(); + if (stmts.length < 1) { + +// test_cause: +// [" +// switch(0){case 0:default:} +// ", "stmt_switch", "expected_statements_a", "default", 18] +// ["switch(0){case 0:}", "stmt_switch", "expected_statements_a", "}", 18] + + warn("expected_statements_a"); + +// PR-359 - Bugfix - Fixes issue #358 - switch-statement crashes jslint. + + break; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn("expected_a_before_b", token_nxt, "break;", artifact()); + } + if (token_nxt.id !== "case") { + break; + } + } + +// test_cause: +// [" +// switch(0){case 1:break;case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 29] +// [" +// switch(0){case "aa":break;case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 32] +// [" +// switch(0){case "bb":break;case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 32] +// [" +// switch(0){case aa:break;case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30] +// [" +// switch(0){case bb:break;case aa:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30] + + check_ordered_case(the_cases.map(function ({ + expression + }) { + return expression[0]; + })); + dups = undefined; + if (token_nxt.id === "default") { + the_default = token_nxt; + advance("default"); + token_now.switch = true; + advance(":"); + the_switch.else = parse_statements(); + if (the_switch.else.length < 1) { + +// test_cause: +// [" +// switch(0){case 0:break;default:} +// ", "stmt_switch", "unexpected_a", "default", 24] + + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + the_last = the_switch.else[ + the_switch.else.length - 1 + ]; + if ( + the_last.id === "break" + && the_last.label === undefined + ) { + +// test_cause: +// [" +// switch(0){case 0:break;default:break;} +// ", "stmt_switch", "unexpected_a", "break", 32] + + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; + } + + function stmt_throw() { + const the_throw = token_now; + the_throw.disrupt = true; + the_throw.expression = parse_expression(10); + semicolon(); + if (functionage.try > 0) { + +// test_cause: +// ["try{throw 0}catch(){}", "stmt_throw", "unexpected_a", "throw", 5] + + warn("unexpected_a", the_throw); + } + return the_throw; + } + + function stmt_try() { + const the_try = token_now; + let ignored; + let the_catch; + let the_disrupt; + if (functionage.try > 0) { + +// test_cause: +// ["try{try{}catch(){}}catch(){}", "stmt_try", "unexpected_a", "try", 5] + + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (token_nxt.id === "catch") { + ignored = "ignore"; + the_catch = token_nxt; + the_try.catch = the_catch; + advance("catch"); + +// Create new catch-scope for catch-parameter. + + catch_stack.push(catchage); + catchage = the_catch; + catch_list.push(catchage); + the_catch.context = empty(); + if (token_nxt.id === "(") { + advance("("); + if (!token_nxt.identifier) { + +// test_cause: +// ["try{}catch(){}", "stmt_try", "expected_identifier_a", ")", 12] + + return stop("expected_identifier_a"); + } + if (token_nxt.id !== "ignore") { + ignored = undefined; + the_catch.name = token_nxt; + enroll(token_nxt, "exception", true); + } + advance(); + advance(")"); + } + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + +// Restore previous catch-scope after catch-block. + + catchage = catch_stack.pop(); + +// PR-404 - Relax warning about missing `catch` in `try...finally` statement. +// +// } else { +// +// // test_cause: +// // ["try{}finally{break;}", "stmt_try", "expected_a_before_b", "finally", 6] +// +// warn("expected_a_before_b", token_nxt, "catch", artifact()); + + } + if (token_nxt.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; + } + + function stmt_var() { + let ellipsis; + let mode_const; + let name; + let the_brace; + let the_bracket; + let the_variable = token_now; + let variable_prv; + mode_const = the_variable.id === "const"; + the_variable.names = []; + +// A program may use var or let, but not both. + + if (!mode_const) { + if (mode_var === undefined) { + mode_var = the_variable.id; + } else if (the_variable.id !== mode_var) { + +// test_cause: +// ["let aa;var aa", "stmt_var", "expected_a_b", "var", 8] + + warn("expected_a_b", the_variable, mode_var, the_variable.id); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + +// test_cause: +// ["switch(0){case 0:var aa}", "stmt_var", "var_switch", "var", 18] + + warn("var_switch", the_variable); + } + switch ( + Boolean(functionage.statement_prv) + && functionage.statement_prv.id + ) { + case "const": + case "let": + case "var": + +// test_cause: +// ["const aa=0;const bb=0;", "stmt_var", "var_prv", "const", 0] +// ["let aa=0;let bb=0;", "stmt_var", "var_prv", "let", 0] +// ["var aa=0;var bb=0;", "stmt_var", "var_prv", "var", 0] + + test_cause("var_prv", functionage.statement_prv.id); + variable_prv = functionage.statement_prv; + break; + case "import": + +// test_cause: +// ["import aa from \"aa\";\nlet bb=0;", "stmt_var", "import_prv", "", 0] + + test_cause("import_prv"); + break; + case false: + break; + default: + if ( + (option_dict.beta && !option_dict.variable) + || the_variable.id === "var" + ) { + +// test_cause: +// ["console.log();let aa=0;", "stmt_var", "var_on_top", "let", 15] +// ["console.log();var aa=0;", "stmt_var", "var_on_top", "var", 15] +// ["try{aa();}catch(aa){var aa=0;}", "stmt_var", "var_on_top", "var", 21] +// ["while(0){var aa;}", "stmt_var", "var_on_top", "var", 10] + + warn("var_on_top", token_now); + } + } + while (true) { + if (token_nxt.id === "{") { + if (the_variable.id === "var") { + +// test_cause: +// ["var{aa}=0", "stmt_var", "unexpected_a", "var", 1] + + warn("unexpected_a", the_variable); + } + the_brace = token_nxt; + advance("{"); + while (true) { + name = token_nxt; + if (!name.identifier) { + +// test_cause: +// ["let {0}", "stmt_var", "expected_identifier_a", "0", 6] + + return stop("expected_identifier_a"); + } + survey(name); + advance(); + if (token_nxt.id === ":") { + advance(":"); + if (!token_nxt.identifier) { + +// test_cause: +// ["let {aa:0}", "stmt_var", "expected_identifier_a", "0", 9] +// ["let {aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 9] + + return stop("expected_identifier_a"); + } + +// PR-363 - Bugfix +// Add test against false-warning in code +// '/*jslint node*/\nlet {aa:bb} = {}; bb();'. +// +// token_nxt.label = name; +// the_variable.names.push(token_nxt); +// enroll(token_nxt, "variable", mode_const); + + name = token_nxt; + the_variable.names.push(name); + survey(name); + enroll(name, "variable", mode_const); + + advance(); + the_brace.open = true; + } else { + the_variable.names.push(name); + enroll(name, "variable", mode_const); + } + name.dead = false; + name.init = true; + if (token_nxt.id === "=") { + +// test_cause: +// ["let {aa=0}", "stmt_var", "assign", "", 0] + + test_cause("assign"); + advance("="); + name.expression = parse_expression(); + the_brace.open = true; + } + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + +// test_cause: +// ["let{bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8] + + check_ordered(the_variable.id, the_variable.names); + advance("}"); + advance("="); + the_variable.expression = parse_expression(0); + } else if (token_nxt.id === "[") { + if (the_variable.id === "var") { + +// test_cause: +// ["var[aa]=0", "stmt_var", "unexpected_a", "var", 1] + + warn("unexpected_a", the_variable); + } + the_bracket = token_nxt; + advance("["); + while (true) { + ellipsis = false; + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + if (!token_nxt.identifier) { + +// test_cause: +// ["let[]", "stmt_var", "expected_identifier_a", "]", 5] + + return stop("expected_identifier_a"); + } + name = token_nxt; + advance(); + the_variable.names.push(name); + enroll(name, "variable", mode_const); + name.dead = false; + name.init = true; + if (ellipsis) { + name.ellipsis = true; + break; + } + if (token_nxt.id === "=") { + advance("="); + name.expression = parse_expression(); + the_bracket.open = true; + } + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + advance("]"); + advance("="); + the_variable.expression = parse_expression(0); + } else if (token_nxt.identifier) { + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// [" +// let ignore;function aa(ignore) {} +// ", "stmt_var", "unexpected_a", "ignore", 5] + + warn("unexpected_a", name); + } + enroll(name, "variable", mode_const); + if (token_nxt.id === "=" || mode_const) { + advance("="); + name.dead = false; + name.init = true; + name.expression = parse_expression(0); + } + the_variable.names.push(name); + } else { + +// test_cause: +// ["let 0", "stmt_var", "expected_identifier_a", "0", 5] +// ["var{aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 8] + + return stop("expected_identifier_a"); + } + if (token_nxt.id !== ",") { + break; + } + +// test_cause: +// ["let aa,bb;", "stmt_var", "expected_a_b", ",", 7] + + warn("expected_a_b", token_nxt, ";", ","); + advance(","); + } + +// Warn if variable declarations are unordered. + + if ( + option_dict.beta + && !option_dict.unordered + && !option_dict.variable + && variable_prv + && ( + variable_prv.id + " " + variable_prv.names[0].id + > the_variable.id + " " + the_variable.names[0].id + ) + ) { + +// test_cause: +// ["const bb=0;const aa=0;", "stmt_var", "expected_a_b_before_c_d", "aa", 12] +// ["let bb;let aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8] +// ["var bb;var aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8] + + warn( + "expected_a_b_before_c_d", + the_variable, + the_variable.id, + the_variable.names[0].id, + variable_prv.id, + variable_prv.names[0].id + ); + } + semicolon(); + return the_variable; + } + + function stmt_while() { + const the_while = token_now; + check_not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + +// test_cause: +// ["function aa(){while(0){break;}}", "stmt_while", "weird_loop", "while", 15] + + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; + } + + function stmt_with() { + +// test_cause: +// ["with", "stmt_with", "unexpected_a", "with", 1] + + stop("unexpected_a", token_now); + } + + function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!jslint_rgx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!jslint_rgx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + +// test_cause: +// ["let aa={0:0}", "survey", "expected_identifier_a", "0", 9] + + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property_dict[id] === "number") { + property_dict[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (state.mode_property) { + if (tenure[id] !== true) { + +// test_cause: +// ["/*property aa*/\naa.bb", "survey", "unregistered_property_a", "bb", 4] + + warn("unregistered_property_a", name); + } + } else if ( + !option_dict.nomen + && name.identifier + && jslint_rgx_weird_property.test(id) + ) { + +// test_cause: +// ["aa.$", "survey", "weird_property_a", "$", 4] +// ["aa._", "survey", "weird_property_a", "_", 4] +// ["aa._aa", "survey", "weird_property_a", "_aa", 4] +// ["aa.aaSync", "survey", "weird_property_a", "aaSync", 4] +// ["aa.aa_", "survey", "weird_property_a", "aa_", 4] + + warn("weird_property_a", name); + } + property_dict[id] = 1; + } + return id; + } + +// These functions are used to specify the grammar of our language: + + function symbol(id, bp) { + +// Create a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax_dict[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax_dict[id] = the_symbol; + } + return the_symbol; + } + + function ternary(id1, id2) { + +// Create a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led_infix = function parse_ternary_led(left) { + const the_token = token_now; + let second; + second = parse_expression(20); + advance(id2); + token_now.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, parse_expression(10)]; + if (token_nxt.id !== ")") { + +// test_cause: +// ["0?0:0", "parse_ternary_led", "use_open", "?", 2] + + warn("use_open", the_token); + } + return the_token; + }; + return the_symbol; + } + +// Now we parse JavaScript. +// Begin defining the language. + + assignment("%="); + assignment("&="); + assignment("*="); + assignment("+="); + assignment("-="); + assignment("/="); + assignment("<<="); + assignment("="); + assignment(">>="); + assignment(">>>="); + assignment("^="); + assignment("|="); + constant("(number)", "number"); + constant("(regexp)", "regexp"); + constant("(string)", "string"); + constant("Function", "function", constant_Function); + constant("Infinity", "number", Infinity); + constant("NaN", "number", NaN); + constant("arguments", "object", constant_arguments); + constant("eval", "function", constant_eval); + constant("false", "boolean", false); + constant("ignore", "undefined", constant_ignore); + constant("isFinite", "function", constant_isInfinite); + constant("isNaN", "function", constant_isNaN); + constant("null", "null", null); + constant("this", "object", constant_this); + constant("true", "boolean", true); + constant("undefined", "undefined"); + infix(100, "!="); + infix(100, "!=="); + infix(100, "=="); + infix(100, "==="); + infix(110, "<"); + infix(110, "<="); + infix(110, ">"); + infix(110, ">="); + infix(110, "in"); + infix(110, "instanceof"); + infix(120, "<<"); + infix(120, ">>"); + infix(120, ">>>"); + infix(130, "+"); + infix(130, "-"); + infix(140, "%"); + infix(140, "*"); + infix(140, "/"); + infix(160, "(", infix_lparen); + infix(160, "`", infix_grave); + infix(170, ".", infix_dot); + infix(170, "=>", infix_fart_unwrapped); + infix(170, "?.", infix_option_chain); + infix(170, "[", infix_lbracket); + infix(35, "??"); + infix(40, "||"); + infix(50, "&&"); + infix(70, "|"); + infix(80, "^"); + infix(90, "&"); + infixr(150, "**"); + postassign("++"); + postassign("--"); + preassign("++"); + preassign("--"); + prefix("!!"); + prefix("!"); + prefix("(", prefix_lparen); + prefix("+"); + prefix("-"); + prefix("/=", prefix_assign_divide); + prefix("=>", prefix_fart); + prefix("[", prefix_lbracket); + prefix("`", prefix_tick); + prefix("async", prefix_async); + prefix("await", prefix_await); + prefix("function", prefix_function); + prefix("new", prefix_new); + prefix("typeof"); + prefix("void", prefix_void); + prefix("{", prefix_lbrace); + prefix("~"); + stmt(";", stmt_semicolon); + stmt("async", prefix_async); + stmt("await", prefix_await); + stmt("break", stmt_break); + stmt("const", stmt_var); + stmt("continue", stmt_continue); + stmt("debugger", stmt_debugger); + stmt("delete", stmt_delete); + stmt("do", stmt_do); + stmt("export", stmt_export); + stmt("for", stmt_for); + stmt("function", prefix_function); + stmt("if", stmt_if); + stmt("import", stmt_import); + stmt("let", stmt_var); + stmt("return", stmt_return); + stmt("switch", stmt_switch); + stmt("throw", stmt_throw); + stmt("try", stmt_try); + stmt("var", stmt_var); + stmt("while", stmt_while); + stmt("with", stmt_with); + stmt("{", stmt_lbrace); + symbol(")"); + symbol("*/"); + symbol(","); + symbol(":"); + symbol(";"); + symbol("]"); + symbol("async"); + symbol("await"); + symbol("case"); + symbol("catch"); + symbol("class"); + symbol("default"); + symbol("else"); + symbol("enum"); + symbol("finally"); + symbol("implements"); + symbol("interface"); + symbol("package"); + symbol("private"); + symbol("protected"); + symbol("public"); + symbol("static"); + symbol("super"); + symbol("void"); + symbol("yield"); + symbol("}"); + ternary("?", ":"); + +// Init token_nxt. + + advance(); + +// Parsing of JSON is simple: + + if (state.mode_json) { + state.token_tree = parse_json(); + advance("(end)"); + return; + } + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option_dict.browser) { + if (token_nxt.id === ";") { + advance(";"); + } + +// If we are not in a browser, then the file form of strict pragma may be used. + + } else if (token_nxt.value === "use strict") { + advance("(string)"); + advance(";"); + } + state.token_tree = parse_statements(); + advance("(end)"); + +// Check global functions are ordered. + + check_ordered( + "function", + function_list.map(function ({ + level, + name + }) { + return (level === 1) && name; + }).filter(function (name) { + return option_dict.beta && name && name.id; + }) + ); +} + +function jslint_phase4_walk(state) { + +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). + + let { + artifact, + catch_stack, + function_stack, + global_dict, + is_equal, + is_weird, + option_dict, + syntax_dict, + test_cause, + token_global, + warn + } = state; + let block_stack = []; // The stack of blocks. + let blockage = token_global; // The current block. + let catchage = catch_stack[0]; // The current catch-block. + let functionage = token_global; // The current function. + let postaction; + let postamble; + let posts = empty(); + let preaction; + let preamble; + let pres = empty(); + +// The relational operators. + + let relationop = object_assign_from_list(empty(), [ + "!=", "!==", "<", "<=", "==", "===", ">", ">=" + ], true); + +// Ambulation of the parse tree. + + function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; + } + + function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + task(the_token); + }); + } + } + }; + } + + function init_variable(name) { + let the_variable = lookup(name); + if (!the_variable || the_variable.readonly) { + warn("bad_assignment_a", name); + return; + } + the_variable.init = true; + } + + function lookup(thing) { + let id = thing.id; + let the_variable; + if (thing.arity !== "variable") { + return; + } + +// Look up the variable in the current context. + + the_variable = functionage.context[id] || catchage.context[id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable && the_variable.role === "label") { + +// test_cause: +// ["aa:while(0){aa;}", "lookup", "label_a", "aa", 13] + + warn("label_a", thing); + return the_variable; + } + if (!the_variable) { + function_stack.forEach(function ({ + context + }) { + if (context[id] && context[id].role !== "label") { + the_variable = context[id]; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (!the_variable && global_dict[id] === undefined) { + +// test_cause: +// ["aa", "lookup", "undeclared_a", "aa", 1] +// ["class aa{}", "lookup", "undeclared_a", "aa", 7] +// [" +// let aa=0;try{aa();}catch(bb){bb();}bb(); +// ", "lookup", "undeclared_a", "bb", 36] +// [" +// let aa=0;try{aa();}catch(ignore){bb();} +// ", "lookup", "undeclared_a", "bb", 34] + + warn("undeclared_a", thing); + return; + } + if (!the_variable) { + the_variable = { + dead: false, + id, + init: true, + parent: token_global, + readonly: true, + role: "variable", + used: 0 + }; + token_global.context[id] = the_variable; + } + the_variable.closure = true; + functionage.context[id] = the_variable; + } + if ( + ( + the_variable.calls === undefined + || functionage.name === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + && the_variable.dead + ) { + +// test_cause: +// ["let aa;if(aa){let bb;}bb;", "lookup", "out_of_scope_a", "bb", 23] + + warn("out_of_scope_a", thing); + } + return the_variable; + } + + function post_a(thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + let right; + if (thing.id === "=") { + if (thing.names !== undefined) { + +// test_cause: +// ["if(0){aa=0}", "post_a", "=", "", 0] + + test_cause("="); + +// Probably deadcode. +// if (Array.isArray(thing.names)) { +// thing.names.forEach(init_variable); +// } else { +// init_variable(thing.names); +// } + + jslint_assert( + !Array.isArray(thing.names), + `Expected !Array.isArray(thing.names).` + ); + init_variable(thing.names); + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + +// test_cause: +// ["aa.aa=undefined", "post_a", "expected_a_b", "undefined", 1] + + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.readonly) { + warn("bad_assignment_a", lvalue); + } + } + right = syntax_dict[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + +// test_cause: +// ["aa+=undefined", "post_a", "unexpected_a", "undefined", 5] + + warn("unexpected_a", thing.expression[1]); + } + } + } + + function post_a_pluseq(thing) { + const right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } + } + + function post_b(thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || is_equal(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + +// test_cause: +// ["if(0===0){0}", "post_b", "weird_relation_a", "===", 5] + + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option_dict.convert) { + if (thing.expression[0].value === "") { + +// test_cause: +// ["\"\"+0", "post_b", "expected_a_b", "\"\" +", 3] + + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + +// test_cause: +// ["0+\"\"", "post_b", "expected_a_b", "+ \"\"", 2] + + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + +// test_cause: +// ["aa=window[0]", "post_b", "weird_expression_a", "window[...]", 10] + + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + +// test_cause: +// ["aa=self[0]", "post_b", "weird_expression_a", "self[...]", 8] + + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === "." || thing.id === "?.") { + if (thing.expression.id === "RegExp") { + +// test_cause: +// ["aa=RegExp.aa", "post_b", "weird_expression_a", ".", 10] + + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + +// test_cause: +// ["0- -0", "post_b", "wrap_unary", "-", 4] + + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } + } + + function post_b_and(thing) { + if ( + is_weird(thing.expression[0]) + || is_equal(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + +// test_cause: +// ["aa=(()=>0)&&(()=>0)", "post_b_and", "weird_condition_a", "&&", 11] +// ["aa=(``?``:``)&&(``?``:``)", "post_b_and", "weird_condition_a", "&&", 14] +// ["aa=/./&&/./", "post_b_and", "weird_condition_a", "&&", 7] +// ["aa=0&&0", "post_b_and", "weird_condition_a", "&&", 5] +// ["aa=[]&&[]", "post_b_and", "weird_condition_a", "&&", 6] +// ["aa=`${0}`&&`${0}`", "post_b_and", "weird_condition_a", "&&", 10] +// [" +// aa=function aa(){}&&function aa(){} +// ", "post_b_and", "weird_condition_a", "&&", 19] +// ["aa={}&&{}", "post_b_and", "weird_condition_a", "&&", 6] + + warn("weird_condition_a", thing); + } + } + + function post_b_lbracket(thing) { + if (thing.expression[0].id === "RegExp") { + +// test_cause: +// ["aa=RegExp[0]", "post_b_lbracket", "weird_expression_a", "[", 10] + + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + +// test_cause: +// ["aa[[0]]", "post_b_lbracket", "weird_expression_a", "[", 4] + + warn("weird_expression_a", thing.expression[1]); + } + } + + function post_b_lparen(thing) { + let arg; + let array; + let cack; + let left = thing.expression[0]; + let new_date; + let paren; + let the_new; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + +// test_cause: +// ["aa=function(){}()", "post_b_lparen", "wrap_immediate", "(", 16] + + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "BigInt" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + +// test_cause: +// ["new BigInt()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Boolean()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Number()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new String()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Symbol()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new aa()", "post_b_lparen", "unexpected_a", "new", 1] + + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option_dict.eval) { + +// test_cause: +// ["new Function()", "post_b_lparen", "unexpected_a", "new Function", 5] + + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + +// test_cause: +// ["new Array()", "post_b_lparen", "expected_a_b", "new Array", 5] + + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + +// test_cause: +// ["new Object()", "post_b_lparen", "expected_a_b", "new Object", 5] + + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "BigInt" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + +// test_cause: +// ["let Aa=Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8] + + warn("expected_a_before_b", left, "new", artifact(left)); + } + } + } else if (left.id === ".") { + cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + +// test_cause: +// ["new Date.UTC()", "post_b_lparen", "cack", "", 0] + + test_cause("cack"); + cack = !cack; + } + if (jslint_rgx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + +// test_cause: +// ["new Date.UTC()", "post_b_lparen", "unexpected_a", "new", 1] + + warn("unexpected_a", the_new); + } else { + +// test_cause: +// ["let Aa=Aa.Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8] + + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + paren = left.expression; + if (paren.id === "(") { + array = paren.expression; + if (array.length === 1) { + new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + +// test_cause: +// [" +// new Date().getTime() +// ", "post_b_lparen", "expected_a_b", "new Date().getTime()", 1] + + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } + } + + function post_b_or(thing) { + if ( + is_weird(thing.expression[0]) + || is_equal(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + +// test_cause: +// ["aa=0||0", "post_b_or", "weird_condition_a", "||", 5] + + warn("weird_condition_a", thing); + } + } + + function post_s_export(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== token_global) { + +// test_cause: +// [" +// if(0){import aa from "aa";} +// ", "post_s_export", "misplaced_a", "import", 7] + + warn("misplaced_a", the_thing); + } + } + + function post_s_for(thing) { + +// Recurse walk_statement(). + + walk_statement(thing.inc); + } + + function post_s_function(thing) { + delete functionage.async; + delete functionage.finally; + delete functionage.loop; + delete functionage.statement_prv; + delete functionage.switch; + delete functionage.try; + functionage = function_stack.pop(); + if (thing.wrapped) { + +// test_cause: +// ["aa=(function(){})", "post_s_function", "unexpected_parens", "function", 5] + + warn("unexpected_parens", thing); + } + return post_s_lbrace(); + } + + function post_s_import(the_thing) { + const name = the_thing.name; + if (name) { + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return post_s_export(the_thing); + } + } + + function post_s_lbrace() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); + } + + function post_s_try(thing) { + if (thing.catch) { + if (thing.catch.name) { + Object.assign(catchage.context[thing.catch.name.id], { + dead: false, + init: true + }); + } + +// Recurse walk_statement(). + + walk_statement(thing.catch.block); + +// Restore previous catch-scope after catch-block. + + catchage = catch_stack.pop(); + } + } + + function post_s_var(thing) { + thing.names.forEach(function (name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + +// Probably deadcode. +// if (name.id === "{" || name.id === "[") { +// name.names.forEach(subactivate); +// } else { +// name.init = true; +// } + + jslint_assert( + !(name.id === "{" || name.id === "["), + `Expected !(name.id === "{" || name.id === "[").` + ); + name.init = true; + } + blockage.live.push(name); + }); + } + + function post_t(thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || is_equal(thing.expression[1], thing.expression[2]) + ) { + +// test_cause: +// ["let aa=(aa?`${0}`:`${0}`);", "post_t", "unexpected_a", "?", 11] +// ["let aa=(aa?`0`:`0`);", "post_t", "unexpected_a", "?", 11] + + warn("unexpected_a", thing); + } else if (is_equal(thing.expression[0], thing.expression[1])) { + +// test_cause: +// ["aa?aa:0", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "||", "?"); + } else if (is_equal(thing.expression[0], thing.expression[2])) { + +// test_cause: +// ["aa?0:aa", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + +// test_cause: +// ["aa?true:false", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + +// test_cause: +// ["aa?false:true", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + +// test_cause: +// ["(aa&&!aa?0:1)", "post_t", "wrap_condition", "&&", 4] + + warn("wrap_condition", thing.expression[0]); + } + } + + function post_u(thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option_dict.convert) { + +// test_cause: +// ["!!0", "post_u", "expected_a_b", "!!", 1] + + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } + } + + function post_u_plus(thing) { + const right = thing.expression; + if (!option_dict.convert) { + +// test_cause: +// ["aa=+0", "post_u_plus", "expected_a_b", "+", 4] + + warn("expected_a_b", thing, "Number(...)", "+"); + } + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } + } + + function pre_a_bitwise(thing) { + +// These are the bitwise operators. + + switch (!option_dict.bitwise && thing.id) { + case "&": + case "&=": + case "<<": + case "<<=": + case ">>": + case ">>=": + case ">>>": + case ">>>=": + case "^": + case "^=": + case "|": + case "|=": + case "~": + +// test_cause: +// ["0&0", "pre_a_bitwise", "unexpected_a", "&", 2] +// ["0&=0", "pre_a_bitwise", "unexpected_a", "&=", 2] +// ["0<<0", "pre_a_bitwise", "unexpected_a", "<<", 2] +// ["0<<=0", "pre_a_bitwise", "unexpected_a", "<<=", 2] +// ["0>>0", "pre_a_bitwise", "unexpected_a", ">>", 2] +// ["0>>=0", "pre_a_bitwise", "unexpected_a", ">>=", 2] +// ["0>>>0", "pre_a_bitwise", "unexpected_a", ">>>", 2] +// ["0>>>=0", "pre_a_bitwise", "unexpected_a", ">>>=", 2] +// ["0^0", "pre_a_bitwise", "unexpected_a", "^", 2] +// ["0^=0", "pre_a_bitwise", "unexpected_a", "^=", 2] +// ["0|0", "pre_a_bitwise", "unexpected_a", "|", 2] +// ["0|=0", "pre_a_bitwise", "unexpected_a", "|=", 2] +// ["~0", "pre_a_bitwise", "unexpected_a", "~", 1] + + warn("unexpected_a", thing); + break; + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + +// test_cause: +// ["0<0<0", "pre_a_bitwise", "unexpected_a", "<", 4] + + warn("unexpected_a", thing); + } + } + + function pre_b(thing) { + let left; + let right; + let value; + if (relationop[thing.id] === true) { + left = thing.expression[0]; + right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + +// test_cause: +// ["NaN===NaN", "pre_b", "number_isNaN", "===", 4] + + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + +// test_cause: +// ["typeof 0===0", "pre_b", "expected_string_a", "0", 12] + + warn("expected_string_a", right); + } + } else { + value = right.value; + if (value === "null" || value === "undefined") { + +// test_cause: +// [" +// typeof aa==="undefined" +// ", "pre_b", "unexpected_typeof_a", "undefined", 13] + + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "bigint" + && value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + +// test_cause: +// ["typeof 0===\"aa\"", "pre_b", "expected_type_string_a", "aa", 12] + + warn("expected_type_string_a", right, value); + } + } + } + } + } + + function pre_b_eqeq(thing) { + +// test_cause: +// ["0==0", "pre_b_eqeq", "expected_a_b", "==", 2] + + warn("expected_a_b", thing, "===", "=="); + } + + function pre_b_in(thing) { + +// test_cause: +// ["aa in aa", "pre_b_in", "infix_in", "in", 4] + + warn("infix_in", thing); + } + + function pre_b_instanceof(thing) { + +// test_cause: +// ["0 instanceof 0", "pre_b_instanceof", "unexpected_a", "instanceof", 3] + + warn("unexpected_a", thing); + } + + function pre_b_lparen(thing) { + const left = thing.expression[0]; + let left_variable; + let parent; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + parent = functionage.name.parent; + if (parent) { + left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + +// Probably deadcode. +// && left_variable.dead + + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } + } + + function pre_b_noteq(thing) { + +// test_cause: +// ["0!=0", "pre_b_noteq", "expected_a_b", "!=", 2] + + warn("expected_a_b", thing, "!==", "!="); + } + + function pre_b_or(thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + +// test_cause: +// ["0&&0||0", "pre_b_or", "and", "&&", 2] + + warn("and", thang); + } + }); + } + + function pre_s_for(thing) { + let the_variable; + if (thing.name !== undefined) { + thing.name.dead = false; + the_variable = lookup(thing.name); + if (the_variable !== undefined) { + if (the_variable.init && the_variable.readonly) { + +// test_cause: +// ["const aa=0;for(aa in aa){}", "pre_s_for", "bad_assignment_a", "aa", 16] + + warn("bad_assignment_a", thing.name); + } + the_variable.init = true; + } + } + +// Recurse walk_statement(). + + walk_statement(thing.initial); + } + + function pre_s_function(thing) { + +// test_cause: +// ["()=>0", "pre_s_function", "", "", 0] +// ["(function (){}())", "pre_s_function", "", "", 0] +// ["function aa(){}", "pre_s_function", "", "", 0] + + test_cause(""); + if (thing.arity === "statement" && blockage.body !== true) { + +// test_cause: +// ["if(0){function aa(){}\n}", "pre_s_function", "unexpected_a", "function", 7] + + warn("unexpected_a", thing); + } + function_stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + +// test_cause: +// [" +// /*jslint getset*/ +// aa={get aa(aa){}} +// ", "pre_s_function", "bad_get", "function", 9] + + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + +// test_cause: +// [" +// /*jslint getset*/ +// aa={set aa(){}} +// ", "pre_s_function", "bad_set", "function", 9] + + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); + } + + function pre_s_lbrace(thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; + } + + function pre_try(thing) { + if (thing.catch !== undefined) { + +// Create new catch-scope for catch-parameter. + + catch_stack.push(catchage); + catchage = thing.catch; + } + } + + function pre_v(thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } + } + + function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); + } + + function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + +// test_cause: +// ["(function(){}())", "walk_expression", "isArray", "", 0] +// ["0&&0", "walk_expression", "isArray", "", 0] + + test_cause("isArray"); + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + +// PR-414 - Bugfix - fix fart-body not being walked. + + if (thing.id === "function" || thing.id === "=>") { + +// test_cause: +// ["aa=()=>0", "walk_expression", "function", "=>", 0] +// ["aa=function(){}", "walk_expression", "function", "function", 0] + + test_cause("function", thing.id); + +// Recurse walk_statement(). + + walk_statement(thing.block); + } + if ( + thing.arity === "preassign" || thing.arity === "postassign" + ) { + +// test_cause: +// ["aa=++aa", "walk_expression", "unexpected_a", "++", 4] +// ["aa=--aa", "walk_expression", "unexpected_a", "--", 4] + + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + +// test_cause: +// ["aa[aa=0]", "walk_expression", "unexpected_statement_a", "=", 6] + + warn("unexpected_statement_a", thing); + } + +// test_cause: +// ["aa=0", "walk_expression", "default", "", 0] + + test_cause("default"); + postamble(thing); + } + } + } + + function walk_statement(thing) { + if (!thing) { + return; + } + if (Array.isArray(thing)) { + +// test_cause: +// ["+[]", "walk_statement", "isArray", "", 0] + + test_cause("isArray"); + +// Recurse walk_statement(). + + thing.forEach(walk_statement); + return; + } + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + +// test_cause: +// ["0&&0", "walk_statement", "unexpected_expression_a", "&&", 2] + + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + && thing.id !== "import" + ) { + +// test_cause: +// ["!0", "walk_statement", "unexpected_expression_a", "!", 1] +// ["+[]", "walk_statement", "unexpected_expression_a", "+", 1] +// ["+new aa()", "walk_statement", "unexpected_expression_a", "+", 1] +// ["0", "walk_statement", "unexpected_expression_a", "0", 1] +// ["typeof 0", "walk_statement", "unexpected_expression_a", "typeof", 1] + + warn("unexpected_expression_a", thing); + } + +// Recurse walk_statement(). + + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + + postaction = action(posts); + postamble = amble(posts); + preaction = action(pres); + preamble = amble(pres); + postaction("assignment", "+=", post_a_pluseq); + postaction("assignment", post_a); + postaction("binary", "&&", post_b_and); + postaction("binary", "(", post_b_lparen); + postaction("binary", "=>", post_s_function); + postaction("binary", "[", post_b_lbracket); + postaction("binary", "||", post_b_or); + postaction("binary", post_b); + postaction("statement", "const", post_s_var); + postaction("statement", "export", post_s_export); + postaction("statement", "for", post_s_for); + postaction("statement", "function", post_s_function); + postaction("statement", "import", post_s_import); + postaction("statement", "let", post_s_var); + postaction("statement", "try", post_s_try); + postaction("statement", "var", post_s_var); + postaction("statement", "{", post_s_lbrace); + postaction("ternary", post_t); + postaction("unary", "+", post_u_plus); + postaction("unary", "function", post_s_function); + postaction("unary", post_u); + preaction("assignment", pre_a_bitwise); + preaction("binary", "!=", pre_b_noteq); + preaction("binary", "(", pre_b_lparen); + preaction("binary", "==", pre_b_eqeq); + preaction("binary", "=>", pre_s_function); + preaction("binary", "in", pre_b_in); + preaction("binary", "instanceof", pre_b_instanceof); + preaction("binary", "||", pre_b_or); + preaction("binary", pre_b); + preaction("binary", pre_a_bitwise); + preaction("statement", "for", pre_s_for); + preaction("statement", "function", pre_s_function); + preaction("statement", "try", pre_try); + preaction("statement", "{", pre_s_lbrace); + preaction("unary", "function", pre_s_function); + preaction("unary", "~", pre_a_bitwise); + preaction("variable", pre_v); + + walk_statement(state.token_tree); +} + +function jslint_phase5_whitage(state) { + +// PHASE 5. Check whitespace between tokens in . + + let { + artifact, + catch_list, + function_list, + function_stack, + option_dict, + test_cause, + token_global, + token_list, + warn + } = state; + let closer = "(end)"; + let free = false; + +// free = false + +// cause: +// "()=>0" +// "aa()" +// "aa(0,0)" +// "function(){}" + +// free = true + +// cause: +// "(0)" +// "(aa)" +// "aa(0)" +// "do{}while()" +// "for(){}" +// "if(){}" +// "switch(){}" +// "while(){}" + + let left = token_global; + let margin = 0; + let mode_indent = ( + +// PR-330 - Allow 2-space indent. + + option_dict.indent2 + ? 2 + : 4 + ); + let nr_comments_skipped = 0; + let open = true; + let opening = true; + let right; + +// This is the set of infix operators that require a space on each side. + + let spaceop = object_assign_from_list(empty(), [ + "!=", "!==", "%", "%=", "&", "&&", "&=", "*", "*=", "+=", "-=", "/", + "/=", "<", "<<", "<<=", "<=", "=", "==", "===", "=>", ">", ">=", ">>", + ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" + ], true); + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + const name = the_function.context[id]; + if (id !== "ignore" && name.parent === the_function) { + +// test_cause: +// ["function aa(aa) {return aa;}", "delve", "id", "", 0] + + test_cause("id"); + if ( + name.used === 0 + +// Probably deadcode. +// && ( +// name.role !== "function" +// || name.parent.arity !== "unary" +// ) + + && jslint_assert( + name.role !== "function", + `Expected name.role !== "function".` + ) + ) { + +// test_cause: +// ["/*jslint node*/\nlet aa;", "delve", "unused_a", "aa", 5] +// ["function aa(aa){return;}", "delve", "unused_a", "aa", 13] +// ["let aa=0;try{aa();}catch(bb){aa();}", "delve", "unused_a", "bb", 26] + + warn("unused_a", name); + } else if (!name.init) { + +// test_cause: +// ["/*jslint node*/\nlet aa;aa();", "delve", "uninitialized_a", "aa", 5] + + warn("uninitialized_a", name); + } + } + }); + } + + function expected_at(at) { + +// Probably deadcode. +// if (right === undefined) { +// right = token_nxt; +// } + + jslint_assert( + !(right === undefined), + `Expected !(right === undefined).` + ); + warn( + "expected_a_at_b_c", + right, + artifact(right), + +// Fudge column numbers in warning message. + + at + jslint_fudge, + right.from + jslint_fudge + ); + } + + function no_space() { + if (left.line === right.line) { + +// from: +// if (left.line === right.line) { +// no_space(); +// } else { + + if (left.thru !== right.from && nr_comments_skipped === 0) { + +// test_cause: +// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14] + + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + +// from: +// } else if ( +// right.arity === "binary" +// && right.id === "(" +// && free +// ) { +// no_space(); +// } else if ( + +// Probably deadcode. +// if (open) { +// const at = ( +// free +// ? margin +// : margin + 8 +// ); +// if (right.from < at) { +// expected_at(at); +// } +// } else { +// if (right.from !== margin + 8) { +// expected_at(margin + 8); +// } +// } + + jslint_assert(open, `Expected open.`); + jslint_assert(free, `Expected free.`); + if (right.from < margin) { + +// test_cause: +// ["let aa = aa(\naa\n()\n);", "expected_at", "expected_a_at_b_c", "5", 1] + + expected_at(margin); + } + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line || !open) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (right.from !== margin) { + expected_at(margin); + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn("expected_space_a_b", right, artifact(left), artifact(right)); + } + } + + function pop() { + const previous = function_stack.pop(); + closer = previous.closer; + free = previous.free; + margin = previous.margin; + open = previous.open; + opening = previous.opening; + } + + function push() { + function_stack.push({ + closer, + free, + margin, + open, + opening + }); + } + +// uninitialized_and_unused(); +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (state.mode_module === true || option_dict.node) { + delve(token_global); + } + catch_list.forEach(delve); + function_list.forEach(delve); + + if (option_dict.white) { + return; + } + +// whitage(); +// Go through the token list, looking at usage of whitespace. + + token_list.forEach(function whitage(the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + +// The open and close pairs. + + switch (left.id) { + case "${": + case "(": + case "[": + case "{": + +// test_cause: +// ["let aa=[];", "whitage", "opener", "", 0] +// ["let aa=`${0}`;", "whitage", "opener", "", 0] +// ["let aa=aa();", "whitage", "opener", "", 0] +// ["let aa={};", "whitage", "opener", "", 0] + + test_cause("opener"); + +// Probably deadcode. +// case "${}": + + jslint_assert( + !(left.id + right.id === "${}"), + "Expected !(left.id + right.id === \"${}\")." + ); + switch (left.id + right.id) { + case "()": + case "[]": + case "{}": + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like '{]') have already been detected. + +// test_cause: +// ["let aa=[];", "whitage", "opener_closer", "", 0] +// ["let aa=aa();", "whitage", "opener_closer", "", 0] +// ["let aa={};", "whitage", "opener_closer", "", 0] + + test_cause("opener_closer"); + if (left.line === right.line) { + +// test_cause: +// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14] + + no_space(); + } else { + +// test_cause: +// ["let aa = aa(\n );", "expected_at", "expected_a_at_b_c", "1", 2] + + at_margin(0); + } + break; + default: + +// test_cause: +// ["let aa=(0);", "whitage", "opener_operand", "", 0] +// ["let aa=[0];", "whitage", "opener_operand", "", 0] +// ["let aa=`${0}`;", "whitage", "opener_operand", "", 0] +// ["let aa=aa(0);", "whitage", "opener_operand", "", 0] +// ["let aa={aa:0};", "whitage", "opener_operand", "", 0] + + test_cause("opener_operand"); + opening = left.open || (left.line !== right.line); + push(); + switch (left.id) { + case "${": + closer = "}"; + break; + case "(": + closer = ")"; + break; + case "[": + closer = "]"; + break; + case "{": + closer = "}"; + break; + } + if (opening) { + +// test_cause: +// ["function aa(){\nreturn;\n}", "whitage", "opening", "", 0] +// ["let aa=(\n0\n);", "whitage", "opening", "", 0] +// ["let aa=[\n0\n];", "whitage", "opening", "", 0] +// ["let aa=`${\n0\n}`;", "whitage", "opening", "", 0] +// ["let aa={\naa:0\n};", "whitage", "opening", "", 0] + + test_cause("opening"); + free = closer === ")" && left.free; + open = true; + margin += mode_indent; + if (right.role === "label") { + if (right.from !== 0) { + +// test_cause: +// [" +// function aa() { +// bb: +// while (aa) { +// if (aa) { +// break bb; +// } +// } +// } +// ", "expected_at", "expected_a_at_b_c", "1", 2] + + expected_at(0); + } + } else if (right.switch) { + at_margin(-mode_indent); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + +// test_cause: +// [" +// function aa() {bb: +// while (aa) { +// aa(); +// } +// } +// ", "whitage", "expected_line_break_a_b", "bb", 16] + + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + +// test_cause: +// ["let aa=(0);", "whitage", "not_free", "", 0] +// ["let aa=[0];", "whitage", "not_free", "", 0] +// ["let aa=`${0}`;", "whitage", "not_free", "", 0] +// ["let aa={aa:0};", "whitage", "not_free", "", 0] + + test_cause("not_free"); + free = false; + open = false; + +// test_cause: +// ["let aa = ( 0 );", "no_space_only", "unexpected_space_a_b", "0", 12] + + no_space_only(); + } + } + break; + default: + if (right.statement === true) { + if (left.id === "else") { + +// test_cause: +// [" +// let aa = 0; +// if (aa) { +// aa(); +// } else if (aa) { +// aa(); +// } +// ", "one_space_only", "expected_space_a_b", "if", 9] + + one_space_only(); + } else { + +// test_cause: +// [" let aa = 0;", "expected_at", "expected_a_at_b_c", "1", 2] + + at_margin(0); + open = false; + } + +// If right is a closer, then pop the previous state. + + } else if (right.id === closer) { + pop(); + if (opening && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-mode_indent); + } else if (right.role === "label") { + if (right.from !== 0) { + +// test_cause: +// [" +// function aa() { +// aa();cc: +// while (aa) { +// if (aa) { +// break cc; +// } +// } +// } +// ", "expected_at", "expected_a_at_b_c", "1", 10] + + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + +// test_cause: +// ["let {aa,bb} = 0;", "one_space", "expected_space_a_b", "bb", 9] + + one_space(); + } else { + +// test_cause: +// [" +// function aa() { +// aa( +// 0,0 +// ); +// } +// ", "expected_at", "expected_a_at_b_c", "9", 11] + + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + +// test_cause: +// [" +// let aa = ( +// aa +// ? 0 +// : 1 +// ); +// ", "expected_at", "expected_a_at_b_c", "5", 1] + + at_margin(0); + } else { + +// test_cause: +// ["let aa = (aa ? 0 : 1);", "whitage", "use_open", "?", 14] + + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + +// test_cause: +// ["let aa = aa(\naa ()\n);", "no_space", "unexpected_space_a_b", "(", 4] + + no_space(); + } else if ( + left.id === "." + || left.id === "?." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + || (right.id === "." || right.id === "?.") + ) { + +// test_cause: +// ["let aa = 0 ;", "no_space_only", "unexpected_space_a_b", ";", 12] +// ["let aa = aa ?.aa;", "no_space_only", "unexpected_space_a_b", "?.", 13] + + no_space_only(); + } else if (left.id === ";") { + +// test_cause: +// [" +// /*jslint for*/ +// function aa() { +// for ( +// aa(); +// aa; +// aa() +// ) { +// aa(); +// } +// } +// ", "expected_at", "expected_a_at_b_c", "9", 1] + + if (open) { + at_margin(0); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || left.id === "await" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + +// test_cause: +// [" +// function aa() { +// do { +// aa(); +// } while(aa()); +// } +// ", "one_space_only", "expected_space_a_b", "(", 12] + + one_space_only(); + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || left.id === "async" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + +// test_cause: +// ["let aa=0;", "one_space", "expected_space_a_b", "0", 8] +// ["let aa={\naa:\n0\n};", "expected_at", "expected_a_at_b_c", "5", 1] + + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +function jslint_report({ + exports, + froms, + functions, + global, + json, + module, + property, + stop, + warnings +}) { + +// This function will create human-readable, html-report +// for warnings, properties, and functions from jslint-result-object. +// +// Example usage: +// let result = jslint("console.log('hello world')"); +// let html = jslint_report(result); + + let html = ""; + let length_80 = 1111; + + function address(line = 1, column = 1) { + +// This function will create HTML address element from and + + return `
    ${Number(line)}: ${Number(column)}
    `; + + } + + function detail(title, list) { + return ( + (Array.isArray(list) && list.length > 0) + ? ( + +// Google Lighthouse Accessibility -
    's do not contain only properly-ordered +//
    and
    groups, + + + + + + + + + + + + +

    CodeMirror: JSLint Demo

    +

    +This demo will auto-lint the code below, and auto-generate a report as you type. +

    + + + + + + + +
    + + + + + diff --git a/branch-beta/jslint_wrapper_codemirror.js b/branch-beta/jslint_wrapper_codemirror.js new file mode 100644 index 000000000..0cdc0950c --- /dev/null +++ b/branch-beta/jslint_wrapper_codemirror.js @@ -0,0 +1,146 @@ +// The Unlicense +// +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + + +// This wrapper will integrate JSLint with CodeMirror's lint.js addon. +// Requires CodeMirror and JSLint. +// +// Example usage: +/* + + + + + + + + + + + +*/ + +/*jslint browser, devel*/ +/*global CodeMirror define exports jslint module require*/ +/*property + Pos, amd, column, error, from, globals, jslint, line, map, message, + mode_stop, registerHelper, result, severity, signal, source, to, warnings +*/ + +(function (mod) { + "use strict"; + +// CommonJS + + if (typeof exports === "object" && typeof module === "object") { + mod(require("../../lib/codemirror")); + +// AMD + + } else if (typeof define === "function" && define.amd) { + define(["../../lib/codemirror"], mod); + +// Plain browser env + + } else { + mod(CodeMirror); + } +}(function (CodeMirror) { + "use strict"; + if (!window.jslint) { + console.error( + "Error: window.jslint not defined," + + " CodeMirror JavaScript linting cannot run." + ); + return []; + } + CodeMirror.registerHelper("lint", "javascript", function ( + source, + options, + editor + ) { + let result; + +// Emit before linter is run, so it can be modified +// before passing to jslint. +// +// Example usage: +// editor.on("lintJslintBefore", function (options) { +// options.browser = true; +// options.node = true; +// options.globals = ["caches", "indexedDb"]; +// }); + + options.source = source; + CodeMirror.signal(editor, "lintJslintBefore", options); + +// Run jslint. + + result = jslint.jslint(source, options, options.globals); + +// Emit after linter is run, so it can be used to generate reports. +// +// Example usage: +// editor.on("lintJslintAfter", function (options) { +// divReport.innerHTML = jslint.jslint_report(options.result); +// }); + + options.result = result; + CodeMirror.signal(editor, "lintJslintAfter", options); + +// Return warnings. + + return result.warnings.map(function ({ + column, + line, + message, + mode_stop + }) { + return { + from: CodeMirror.Pos(line - 1, column - 1), //jslint-ignore-line + message, + severity: ( + mode_stop + ? "error" + : "warning" + ), + to: CodeMirror.Pos(line - 1, column) //jslint-ignore-line + }; + }); + }); +})); diff --git a/branch-beta/jslint_wrapper_vim.vim b/branch-beta/jslint_wrapper_vim.vim new file mode 100644 index 000000000..ce6700c1c --- /dev/null +++ b/branch-beta/jslint_wrapper_vim.vim @@ -0,0 +1,59 @@ +"" The Unlicense +"" +"" This is free and unencumbered software released into the public domain. +"" +"" Anyone is free to copy, modify, publish, use, compile, sell, or +"" distribute this software, either in source code form or as a compiled +"" binary, for any purpose, commercial or non-commercial, and by any +"" means. +"" +"" In jurisdictions that recognize copyright laws, the author or authors +"" of this software dedicate any and all copyright interest in the +"" software to the public domain. We make this dedication for the benefit +"" of the public at large and to the detriment of our heirs and +"" successors. We intend this dedication to be an overt act of +"" relinquishment in perpetuity of all present and future rights to this +"" software under copyright law. +"" +"" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +"" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +"" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +"" IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +"" OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +"" ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +"" OTHER DEALINGS IN THE SOFTWARE. + + +"" jslint_wrapper_vim.vim +"" +"" jslint wrapper for vim +"" +"" 1. Save this file and "jslint.mjs" to directory "~/.vim/" +"" 2. Add vim-command ":source ~/.vim/jslint_wrapper_vim.vim" to file "~/.vimrc" +"" 3. Vim can now jslint files (via nodejs): +"" - with vim-command ":SaveAndJslint" +"" - with vim-key-combo " " + +let s:dir = expand(":p:h") + +"" this function will save current file and jslint it (via nodejs) +function! SaveAndJslint(bang) + "" save file + if a:bang == "!" | write! | else | write | endif + "" jslint file (via nodejs) + let &l:errorformat = + \ "%f..js:%n:%l:%c:%m," . + \ "%f:%n:%l:%c:%m" + let &l:makeprg = "node" + \ . " \"" . s:dir . "/jslint.mjs\"" + \ . " jslint_wrapper_vim" + \ . " \"" . fnamemodify(bufname("%"), ":p") . "\"" + silent make! | cwindow | redraw! +endfunction + +"" create vim-command ":SaveAndJslint" +command! -nargs=* -bang SaveAndJslint call SaveAndJslint("") + +"" map vim-key-combo " " to ":SaveAndJslint" +inoremap :SaveAndJslint +nnoremap :SaveAndJslint diff --git a/branch-beta/jslint_wrapper_vscode.js b/branch-beta/jslint_wrapper_vscode.js new file mode 100644 index 000000000..3ecf2a54e --- /dev/null +++ b/branch-beta/jslint_wrapper_vscode.js @@ -0,0 +1,243 @@ +// The Unlicense +// +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + + +/*jslint beta, node*/ +/*property + Diagnostic, DiagnosticSeverity, ProgressLocation, Warning, Window, activate, + cancellable, character, clear, column, commands, createDiagnosticCollection, + document, end, endsWith, exports, fsPath, getText, increment, insert, + isEmpty, jslint, languages, line, lineAt, location, map, message, module, + promises, push, range, rangeIncludingLineBreak, readFileSync, + registerTextEditorCommand, replace, report, runInNewContext, selection, set, + slice, start, subscriptions, title, uri, warnings, window, withProgress, + writeFile +*/ + +"use strict"; + +function activate({ + subscriptions +}) { +/** + * @param {vscode.ExtensionContext} context + */ +// This method is called when your extension is activated. +// Your extension is activated the very first time the command is executed. + +// Directly print diagnostic without language-server. +// https://stackoverflow.com/questions/35581332 +// /create-diagnostics-entries-without-language-server-in-visual-studio-code + + let diagnosticCollection; + let jslint; + let vscode; + + function jslintClear() { + return vscode.window.withProgress({ + cancellable: false, + location: vscode.ProgressLocation.Window, + title: "JSLint clear warnings ..." + }, async function (progress) { + progress.report({ + increment: 0 + }); + +// Clear "Problems" tab. + + diagnosticCollection.clear(); + +// Wait awhile for flicker to give user visual confirmation "Problems" tab +// is refreshed. + + await new Promise(function (resolve) { + setTimeout(resolve, 500); + }); + + progress.report({ + increment: 100 + }); + }); + } + + function jslintDisableRegion({ + document, + selection + }, edit) { + let range; + let text; + edit.insert({ + character: 0, + line: selection.start.line + }, "/*jslint-disable*/\n"); + range = document.lineAt(selection.end).rangeIncludingLineBreak; + text = document.getText(range); + +// If selection-end is EOL without preceding line-break, +// then prepend line-break before directive. + + if (!text.endsWith("\n")) { + text += "\n/*jslint-enable*/"; + +// If selection-end is start of a new line, then prepend directive before it. + + } else if (!selection.isEmpty && selection.end.character === 0) { + text = "/*jslint-enable*/\n" + text; + +// Append directive to selection-end. + + } else { + text += "/*jslint-enable*/\n"; + } + edit.replace(range, text); + } + + function jslintIgnoreLine({ + document, + selection + }, edit) { + edit.insert({ + character: document.lineAt(selection.end).range.end.character, + line: selection.end.line + }, " //jslint-ignore-line"); + } + + function jslintLint({ + document + }) { + return vscode.window.withProgress({ + cancellable: false, + location: vscode.ProgressLocation.Window, + title: "JSLint lint file ..." + }, async function (progress) { + let result; + progress.report({ + increment: 0 + }); + +// Clear "Problems" tab. + + diagnosticCollection.clear(); + result = document.getText(); + result = jslint.jslint(result); + result = result.warnings.slice(0, 100).map(function ({ + column, + line, + // line_source, + message + }) { + return new vscode.Diagnostic( + // code: line_source, + { + end: { + character: column - 1, + line: line - 1 + }, + start: { + character: column - 1, + line: line - 1 + } + }, + `JSLint - ${message}`, + vscode.DiagnosticSeverity.Warning + ); + }); + +// Wait awhile for flicker to give user visual confirmation "Problems" tab +// is refreshed. + + await new Promise(function (resolve) { + setTimeout(resolve, 100); + }); + +// Update "Problems" tab. + + diagnosticCollection.set(document.uri, result); + progress.report({ + increment: 100 + }); + }); + } + +// PR-429 - Add manual lint-on-save command. + + async function jslintLintAndSave({ + document + }) { + jslintLint({ + document + }); + await require("fs").promises.writeFile( + document.uri.fsPath, + document.getText() + ); + } + +// Initialize vscode and jslint. + + vscode = require("vscode"); + diagnosticCollection = vscode.languages.createDiagnosticCollection( + "jslint" + ); + require("vm").runInNewContext( + ( + "\"use strict\";" + + require("fs").readFileSync( //jslint-ignore-line + __dirname + "/jslint.mjs", + "utf8" + ).replace( + "\nexport default Object.freeze(jslint_export);", + "\nmodule.exports = jslint_export;" + ).replace( + "\njslint_import_meta_url = import.meta.url;", + "\n// jslint_import_meta_url = import.meta.url;" + ) + ), + { + module + } + ); + jslint = module.exports; + +// Register extension commands. + + subscriptions.push(vscode.commands.registerTextEditorCommand(( + "jslint.clear" + ), jslintClear)); + subscriptions.push(vscode.commands.registerTextEditorCommand(( + "jslint.disableRegion" + ), jslintDisableRegion)); + subscriptions.push(vscode.commands.registerTextEditorCommand(( + "jslint.ignoreLine" + ), jslintIgnoreLine)); + subscriptions.push(vscode.commands.registerTextEditorCommand(( + "jslint.lint" + ), jslintLint)); + subscriptions.push(vscode.commands.registerTextEditorCommand(( + "jslint.lintAndSave" + ), jslintLintAndSave)); +} + +exports.activate = activate; diff --git a/branch-beta/package.json b/branch-beta/package.json new file mode 100644 index 000000000..d1f5d2207 --- /dev/null +++ b/branch-beta/package.json @@ -0,0 +1,39 @@ +{ + "bin": { + "jslint": "jslint.mjs" + }, + "bugs": { + "url": "https://github.com/jslint-org/jslint/issues" + }, + "counter": 0, + "description": "JSLint, The JavaScript Code Quality and Coverage Tool", + "exports": { + "default": "./jslint_wrapper_cjs.cjs", + "import": "./jslint.mjs" + }, + "fileCount": 34, + "keywords": [ + "coverage-report", + "javascript", + "jslint", + "linter", + "zero-config", + "zero-dependency" + ], + "license": "UNLICENSE", + "main": "./jslint_wrapper_cjs.cjs", + "module": "./jslint.mjs", + "name": "@jslint-org/jslint", + "repository": { + "type": "git", + "url": "git+https://github.com/jslint-org/jslint.git" + }, + "scripts": { + "test": "node jslint.mjs v8_coverage_report=.artifact/coverage node test.mjs", + "test2": "sh jslint_ci.sh shCiBase" + }, + "shCiArtifactUpload": 1, + "shCiPublishNpm": 1, + "type": "module", + "version": "2024.11.24" +} diff --git a/branch-beta/test.mjs b/branch-beta/test.mjs new file mode 100644 index 000000000..24967f887 --- /dev/null +++ b/branch-beta/test.mjs @@ -0,0 +1,1575 @@ +/*jslint beta, node*/ +import jslint from "./jslint.mjs"; +import jslintCjs from "./jslint_wrapper_cjs.cjs"; +import moduleFs from "fs"; +import modulePath from "path"; + +let { + assertErrorThrownAsync, + assertJsonEqual, + assertOrThrow, + debugInline, + fsWriteFileWithParents, + globExclude, + jstestDescribe, + jstestIt, + jstestOnExit, + moduleFsInit, + noop, + v8CoverageListMerge, + v8CoverageReportCreate +} = jslint; +let sourceJslintMjs; +let testCoverageMergeData; + +await (async function init() { + +// Coverage-hack - Ugly-hack to get test-coverage for all initialization-states. + + moduleFsInit(); + moduleFsInit(); + +// Cleanup directory .tmp + + await moduleFs.promises.rm(".tmp", { + recursive: true + }).catch(noop); + +// init sourceJslintMjs + + sourceJslintMjs = await moduleFs.promises.readFile("jslint.mjs", "utf8"); + +// init testCoverageMergeData + + testCoverageMergeData = JSON.parse( + await moduleFs.promises.readFile( + "test_coverage_merge_data.json", + "utf8" + ) + ); +}()); + +jstestDescribe(( + "test fsXxx handling-behavior" +), function testBehaviorFsXxx() { + jstestIt(( + "test fsWriteFileWithParents handling-behavior" + ), async function () { + await Promise.all([ + 1, 2, 3, 4 + ].map(async function () { + await fsWriteFileWithParents( + ".tmp/fsWriteFileWithParents/aa/bb/cc", + "aa" + ); + })); + assertJsonEqual( + await moduleFs.promises.readFile( + ".tmp/fsWriteFileWithParents/aa/bb/cc", + "utf8" + ), + "aa" + ); + }); +}); + +jstestDescribe(( + "test globXxx handling-behavior" +), function testBehaviorGlobXxx() { + jstestIt(( + "test globAssertNotWeird-error handling-behavior" + ), async function () { + await Promise.all([ + "\n", + "\r", + "\u0000" + ].map(async function (char) { + await assertErrorThrownAsync(function () { + return globExclude({ + pathnameList: [ + "aa", + `cc/${char}/dd`, + "bb" + ] + }); + }, ( + "Weird character " + + JSON.stringify(char).replace("\\", "\\\\") + + " found in " + )); + })); + }); + jstestIt(( + "test globExclude handling-behavior" + ), function () { + let pathnameList = [ + ".dockerignore", + ".eslintrc.js", + ".gitignore", + ".npmignore", + ".travis.yml", + "/node_modules/aa/bb/cc.js", + "/node_modules/aa/bb/dd.js", + "CHANGELOG.md", + "CONTRIBUTING.md", + "Dockerfile", + "LICENSE", + "Makefile", + "README.md", + "appveyor.yml", + "benchmark/insert-transaction.sql", + "benchmark/insert.js", + "binding.gyp", + "cloudformation/ci.template.js", + "deps/common-sqlite.gypi", + "deps/extract.py", + "deps/sqlite-autoconf-3340000.tar.gz", + "deps/sqlite3.gyp", + "examples/simple-chaining.js", + "lib/index.js", + "lib/sqlite3-binding.js", + "lib/sqlite3.js", + "lib/trace.js", + "node_modules/aa/bb/cc.js", + "node_modules/aa/bb/dd.js", + "package.json", + "scripts/build-appveyor.bat", + "scripts/build-local.bat", + "scripts/build_against_electron.sh", + "scripts/build_against_node.sh", + "scripts/build_against_node_webkit.sh", + "scripts/build_for_node_webkit.cmd", + "scripts/install_node.sh", + "scripts/validate_tag.sh", + "sqlite3.js", + "src/async.h", + "src/backup.cc", + "src/backup.h", + "src/database.cc", + "src/database.h", + "src/gcc-preinclude.h", + "src/macros.h", + "src/node_sqlite3.cc", + "src/statement.cc", + "src/statement.h", + "src/threading.h", + "test/affected.test.js", + "test/backup.test.js", + "test/blob.test.js", + "test/cache.test.js", + "test/constants.test.js", + "test/database_fail.test.js", + "test/each.test.js", + "test/exec.test.js", + "test/extension.test.js", + "test/fts-content.test.js", + "test/interrupt.test.js", + "test/issue-108.test.js", + "test/json.test.js", + "test/map.test.js", + "test/named_columns.test.js", + "test/named_params.test.js", + "test/null_error.test.js", + "test/nw/.gitignore", + "test/nw/Makefile", + "test/nw/index.html", + "test/nw/package.json", + "test/open_close.test.js", + "test/other_objects.test.js", + "test/parallel_insert.test.js", + "test/prepare.test.js", + "test/profile.test.js", + "test/rerun.test.js", + "test/scheduling.test.js", + "test/serialization.test.js", + "test/support/createdb-electron.js", + "test/support/createdb.js", + "test/support/elmo.png", + "test/support/helper.js", + "test/support/prepare.db", + "test/support/script.sql", + "test/trace.test.js", + "test/unicode.test.js", + "test/upsert.test.js", + "test/verbose.test.js", + "tools/docker/architecture/linux-arm/Dockerfile", + "tools/docker/architecture/linux-arm/run.sh", + "tools/docker/architecture/linux-arm64/Dockerfile", + "tools/docker/architecture/linux-arm64/run.sh" + ]; + [ + "tes?/", + "tes[-t-]/", + "tes[-t]/", + "tes[0-9A-Z_a-z-]/", + "tes[t-]/", + "test/**/*.js" + ].forEach(function (aa) { + [ + "li*/*.js", + "li?/*.js", + "lib/", + "lib/*", + "lib/**/*.js", + "lib/*.js" + ].forEach(function (bb) { + [ + "", + "**/node_modules/", + "node_modules/" + ].forEach(function (cc) { + assertJsonEqual( + globExclude({ + excludeList: [ + "tes[!0-9A-Z_a-z-]/", + "tes[^0-9A-Z_a-z-]/", + "test/suppor*/*elper.js", + "test/suppor?/?elper.js", + "test/support/helper.js" + ].concat(aa, cc), + includeList: [ + "**/*.cjs", + "**/*.js", + "**/*.mjs", + "lib/sqlite3.js" + ].concat(bb), + pathnameList + }).pathnameList, + [ + ".eslintrc.js", + "benchmark/insert.js", + "cloudformation/ci.template.js", + "examples/simple-chaining.js", + "lib/index.js", + "lib/sqlite3-binding.js", + "lib/sqlite3.js", + "lib/trace.js", + "sqlite3.js" + ].concat( + cc === "**/node_modules/" + ? [ + "node_modules/aa/bb/cc.js", + "node_modules/aa/bb/dd.js" + ] + : cc === "node_modules/" + ? [ + "/node_modules/aa/bb/cc.js", + "/node_modules/aa/bb/dd.js" + ] + : [ + "/node_modules/aa/bb/cc.js", + "/node_modules/aa/bb/dd.js", + "node_modules/aa/bb/cc.js", + "node_modules/aa/bb/dd.js" + ] + ).sort() + ); + }); + }); + }); + }); + jstestIt(( + "test globToRegexp handling-behavior" + ), function () { + Object.entries({ + "*": ( + /^[^\/]*?$/gm + ), + "**": ( + /^.*?$/gm + ), + "***": ( + /^.*?$/gm + ), + "****": ( + /^.*?$/gm + ), + "****////****": ( + /^.*?$/gm + ), + "***///***": ( + /^.*?$/gm + ), + "**/*": ( + /^.*?$/gm + ), + "**/node_modules/": ( + /^.*?\/node_modules\/.*?$/gm + ), + "**/node_modules/**/*": ( + /^.*?\/node_modules\/.*?$/gm + ), + "?": ( + /^[^\/]$/gm + ), + "[!0-9A-Za-z-]": ( + /^[^0-9A-Za-z\-]$/gm + ), + "[0-9A-Za-z-]": ( + /^[0-9A-Za-z\-]$/gm + ), + "[[]] ]][[": ( + /^[\[]\] \]\][\[]$/gm + ), + "[]": ( + /^$/gm + ), + "[^0-9A-Za-z-]": ( + /^[^0-9A-Za-z\-]$/gm + ), + "aa/bb/cc": ( + /^aa\/bb\/cc$/gm + ), + "aa/bb/cc/": ( + /^aa\/bb\/cc\/.*?$/gm + ), + "li*/*": ( + /^li[^\/]*?\/[^\/]*?$/gm + ), + "li?/*": ( + /^li[^\/]\/[^\/]*?$/gm + ), + "lib/": ( + /^lib\/.*?$/gm + ), + "lib/*": ( + /^lib\/[^\/]*?$/gm + ), + "lib/**/*.js": ( + /^lib\/.*?\.js$/gm + ), + "lib/*.js": ( + /^lib\/[^\/]*?\.js$/gm + ), + "node_modules/": ( + /^node_modules\/.*?$/gm + ), + "node_modules/**/*": ( + /^node_modules\/.*?$/gm + ), + "tes[!0-9A-Z_a-z-]/**/*": ( + /^tes[^0-9A-Z_a-z\-]\/.*?$/gm + ), + "tes[0-9A-Z_a-z-]/**/*": ( + /^tes[0-9A-Z_a-z\-]\/.*?$/gm + ), + "tes[^0-9A-Z_a-z-]/**/*": ( + /^tes[^0-9A-Z_a-z\-]\/.*?$/gm + ), + "test/**/*": ( + /^test\/.*?$/gm + ), + "test/**/*.js": ( + /^test\/.*?\.js$/gm + ), + "test/suppor*/*elper.js": ( + /^test\/suppor[^\/]*?\/[^\/]*?elper\.js$/gm + ), + "test/suppor?/?elper.js": ( + /^test\/suppor[^\/]\/[^\/]elper\.js$/gm + ), + "test/support/helper.js": ( + /^test\/support\/helper\.js$/gm + ) + }).forEach(function ([ + pattern, rgx + ]) { + assertJsonEqual( + globExclude({ + excludeList: [ + pattern + ] + }).excludeList[0].source, + rgx.source + ); + assertJsonEqual( + globExclude({ + includeList: [ + pattern + ] + }).includeList[0].source, + rgx.source + ); + }); + }); +}); + +jstestDescribe(( + "test jslint's cli handling-behavior" +), function testBehaviorJslintCli() { + function processExit0(exitCode) { + assertOrThrow(exitCode === 0, exitCode); + } + function processExit1(exitCode) { + assertOrThrow(exitCode === 1, exitCode); + } + jstestIt(( + "test cli-null-case handling-behavior" + ), function () { + jslint.jslint_cli({ + mode_noop: true, + process_exit: processExit0 + }); + }); + jstestIt(( + "test cli-window-jslint handling-behavior" + ), function () { + [ + "&window_jslint=", + "&window_jslint=12", + "&window_jslint=1?", + "&window_jslint=?", + "?window_jslint=", + "?window_jslint=12", + "?window_jslint=1?", + "?window_jslint=?", + "window_jslint=1", + "window_jslint=1&", + "window_jslint=12", + "window_jslint=1?" + ].forEach(function (import_meta_url) { + jslint.jslint_cli({ + import_meta_url + }); + assertOrThrow(globalThis.jslint === undefined); + }); + [ + "&window_jslint=1", + "&window_jslint=1&", + "?window_jslint=1", + "?window_jslint=1&" + ].forEach(function (import_meta_url) { + jslint.jslint_cli({ + import_meta_url + }); + assertOrThrow(globalThis.jslint === jslint); + delete globalThis.jslint; + }); + }); + jstestIt(( + "test cli-cjs-and-invalid-file handling-behavior" + ), async function () { + await fsWriteFileWithParents(".test_dir.cjs/touch.txt", ""); + [ + ".", // test dir handling-behavior + "jslint.mjs", // test file handling-behavior + undefined // test file-undefined handling-behavior + ].forEach(function (file) { + jslint.jslint_cli({ + file, + mode_cli: true, + process_env: { + JSLINT_BETA: "1" + }, + process_exit: processExit0 + }); + }); + }); + jstestIt(( + "test cli-apidoc handling-behavior" + ), function () { + jslint.jslint_cli({ + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_apidoc=.artifact/apidoc.html", + JSON.stringify({ + example_list: [ + "README.md", + "test.mjs", + "jslint.mjs" + ], + github_repo: "https://github.com/jslint-org/jslint", + module_list: [ + { + pathname: "./jslint.mjs" + } + ], + package_name: "JSLint", + version: jslint.jslint_edition + }) + ], + process_exit: processExit0 + }); + }); + jstestIt(( + "test cli-file-error handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + file: "undefined", + mode_cli: true, + process_exit: processExit1 + }); + }); + jstestIt(( + "test cli-syntax-error handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + file: "syntax-error.js", + mode_cli: true, + option: { + trace: true + }, + process_exit: processExit1, + source: "syntax error" + }); + }); + jstestIt(( + "test cli-report handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_report=.tmp/jslint_report.html", + "jslint.mjs" + ], + process_exit: processExit0 + }); + }); + jstestIt(( + "test cli-report-error handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_report=.tmp/jslint_report.html", + "syntax-error.js" + ], + process_exit: processExit1, + source: "syntax error" + }); + }); + jstestIt(( + "test cli-report-json handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_report=.tmp/jslint_report.html", + "aa.json" + ], + process_exit: processExit0, + source: "[]" + }); + }); + jstestIt(( + "test cli-report-json-error handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_report=.tmp/jslint_report.html", + "aa.json" + ], + process_exit: processExit1, + source: "[" + }); + }); + jstestIt(( + "test cli-report-misc handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_report=.tmp/jslint_report.html", + "aa.js" + ], + process_exit: processExit0, + source: "let aa = 0;" + }); + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_report=.tmp/jslint_report.html", + "aa.js" + ], + process_exit: processExit1, + source: "(aa)=>aa; function aa([aa]){}" + }); + }); + jstestIt(( + "test cli-jslint-wrapper-vim handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_wrapper_vim", + "syntax-error.js" + ], + process_exit: processExit1, + source: "syntax error" + }); + }); +}); + +jstestDescribe(( + "test jslint's no-warnings handling-behavior" +), function testBehaviorJslintNoWarnings() { + jstestIt(( + "test jslint's no-warnings handling-behavior" + ), function () { + Object.values({ + array: [ + "new Array(0);" + ], + async_await: [ + "async function aa() {\n await aa();\n}", + +// PR-405 - Bugfix - fix expression after "await" mis-identified as statement. + + "async function aa() {\n await aa;\n}", + ( + "async function aa() {\n" + + " try {\n" + + " aa();\n" + + " } catch (err) {\n" + + " await err();\n" + + " }\n" + + "}\n" + ), + ( + "async function aa() {\n" + + " try {\n" + + " await aa();\n" + + " } catch (err) {\n" + + " await err();\n" + + " }\n" + + "}\n" + ), + +// PR-370 - Add top-level-await support. + + "await String();\n" + ], + +// PR-351 - Add BigInt support. + + bigint: [ + "let aa = 0b0n;\n", + "let aa = 0o0n;\n", + "let aa = 0x0n;\n", + "let aa = BigInt(0n);\n", + "let aa = typeof aa === \"bigint\";\n" + ], + date: [ + "Date.getTime();", + "let aa = aa().getTime();", + "let aa = aa.aa().getTime();" + ], + directive: [ + "#!\n/*jslint browser:false, node*/\n\"use strict\";", + "/*property aa bb*/" + ], + for: [ + ( + "/*jslint for*/\n" + + "function aa(bb) {\n" + + " for (bb = 0; bb < 0; bb += 1) {\n" + + " bb();\n" + + " }\n" + + "}\n" + ) + ], + jslint_disable: [ + "/*jslint-disable*/\n0\n/*jslint-enable*/" + ], + jslint_ignore_line: [ + "0 //jslint-ignore-line" + ], + json: [ + "{\"aa\":[[],-0,null]}" + ], + label: [ + ( + "function aa() {\n" + + "bb:\n" + + " while (true) {\n" + + " if (true) {\n" + + " break bb;\n" + + " }\n" + + " }\n" + + "}\n" + ) + ], + loop: [ + ( + "function aa() {\n" + + " do {\n" + + " aa();\n" + + " } while (aa());\n" + + "}\n" + ), + +// PR-378 - Relax warning "function_in_loop". + + ( + "function aa() {\n" + + " while (true) {\n" + + " (function () {\n" + + " return;\n" + + " }());\n" + + " }\n" + + "}\n" + ) + ], + module: [ + "export default Object.freeze();", + +// PR-439 - Add grammar for "export async function ...". + + ( + "export default Object.freeze(async function () {\n" + + " return await 0;\n" + + "});\n" + ), + "import {aa, bb} from \"aa\";\naa(bb);", + "import {} from \"aa\";", + "import(\"aa\").then(function () {\n return;\n});", + ( + "let aa = 0;\n" + + "import(aa).then(aa).then(aa)" + + ".catch(aa).finally(aa);\n" + ) + ], + number: [ + "let aa = 0.0e0;", + "let aa = 0b0;", + "let aa = 0o0;", + "let aa = 0x0;" + ], + +// PR-390 - Add numeric-separator support. + + numeric_separator: [ + "let aa = 0.0_0_0;", + "let aa = 0b0_1111_1111n;\n", + "let aa = 0o0_1234_1234n;\n", + "let aa = 0x0_1234_1234n;\n", + "let aa = 1_234_234.1_234_234E1_234_234;" + ], + optional_chaining: [ + "let aa = aa?.bb?.cc;" + ], + param: [ + "function aa({aa, bb}) {\n return {aa, bb};\n}\n", + ( + "function aa({constructor}) {\n" + + " return {constructor};\n" + + "}\n" + ) + ], + property: [ + "let aa = aa[`!`];" + ], + regexp: [ + "function aa() {\n return /./;\n}", + "let aa = /(?!.)(?:.)(?=.)/;", + "let aa = /./gimuy;", + "let aa = /[\\--\\-]/;" + ], + ternary: [ + ( + "let aa = (\n" + + " aa()\n" + + " ? 0\n" + + " : 1\n" + + ") " + + "&& (\n" + + " aa()\n" + + " ? 0\n" + + " : 1\n" + + ");" + ), + ( + "let aa = (\n" + + " aa()\n" + + " ? `${0}`\n" + + " : `${1}`\n" + + ");" + ), + +// PR-394 - Bugfix +// Fix jslint falsely believing megastring literals `0` and `1` are similar. + + ( + "let aa = (\n" + + " aa()\n" + + " ? `0`\n" + + " : `1`\n" + + ");" + ) + ], + try_catch: [ + ( + "let aa = 0;\n" + + "try {\n" + + " aa();\n" + + "} catch (err) {\n" + + " aa = err;\n" + + "}\n" + + "try {\n" + + " aa();\n" + + "} catch (err) {\n" + + " aa = err;\n" + + "}\n" + + "aa();\n" + ) + ], + try_finally: [ + ( + "let aa = 0;\n" + + "try {\n" + + " aa();\n" + + "} finally {\n" + + " aa();\n" + + "}\n" + ) + ], + use_strict: [ + ( + "\"use strict\";\n" + + "let aa = 0;\n" + + "function bb() {\n" + + " \"use strict\";\n" + + " return aa;\n" + + "}\n" + ) + ], + var: [ + +// PR-363 - Bugfix +// Add test against false-warning in code +// '/*jslint node*/\nlet {aa:bb} = {}; bb();'. + + "/*jslint node*/\n", + "" + ].map(function (directive) { + return [ + "let [\n aa, bb = 0\n] = 0;\naa();\nbb();", + "let aa = 0;\nlet [...bb] = [...aa];\nbb();", + +// PR-459 - Allow destructuring-assignment after function-definition. + + ( + "let aa;\n" + + "let bb;\n" + + "function cc() {\n" + + " return;\n" + + "}\n" + + "[aa, bb] = cc();\n" + ), + "let constructor = 0;\nconstructor();", + "let {\n aa: bb\n} = 0;\nbb();", + "let {\n aa: bb,\n bb: cc\n} = 0;\nbb();\ncc();", + "let {aa, bb} = 0;\naa();\nbb();", + "let {constructor} = 0;\nconstructor();" + ].map(function (code) { + return directive + code; + }); + }).flat() + }).forEach(function (codeList) { + let elemPrv = ""; + codeList.forEach(function (code) { + let warnings; + // Assert codeList is sorted. + assertOrThrow(elemPrv < code, JSON.stringify([ + elemPrv, code + ], undefined, 4)); + elemPrv = code; + [ + jslint.jslint, + jslintCjs.jslint + ].forEach(function (jslint) { + warnings = jslint(code, { + beta: true + }).warnings; + assertOrThrow( + warnings.length === 0, + JSON.stringify([code, warnings]) + ); + }); + }); + }); + }); +}); + +jstestDescribe(( + "test jslint's option handling-behavior" +), function testBehaviorJslintOption() { + let elemPrv = ""; + [ + [ + "let aa = aa | 0;", {bitwise: true}, [] + ], [ + ";\naa(new XMLHttpRequest());", {browser: true}, ["aa"] + ], [ + "let aa = \"aa\" + 0;", {convert: true}, [] + ], [ + "registerType();", {couch: true}, [] + ], [ + "debugger;", {devel: true}, [] + ], [ + +// PR-404 - Alias "evil" to jslint-directive "eval" for backwards-compat. + + "new Function();\neval();", {eval: true, evil: true}, [] + ], [ + "let aa = () => 0;", {fart: true}, [] + ], [ + ( + "let aa = async (bb, {cc, dd}, [ee, ff], ...gg) => {\n" + + " bb += 1;\n" + + " return await (bb + cc + dd + ee + ff + gg);\n" + + "};\n" + ), {fart: true}, [] + ], [ + ( + "function aa(aa) {\n" + + " for (aa = 0; aa < 0; aa += 1) {\n" + + " aa();\n" + + " }\n" + + "}\n" + ), {for: true}, [] + ], [ + "let aa = {get aa() {\n return;\n}};", {getset: true}, [] + ], [ + "let aa = {set aa(aa) {\n return aa;\n}};", {getset: true}, [] + ], [ + sourceJslintMjs.replace(( + / /g + ), " "), {indent2: true}, [] + ], [ + "function aa() {\n return;\n}", {indent2: true}, [] + ], [ + "/".repeat(100), {long: true}, [] + ], [ + +// PR-404 - Alias "nomen" to jslint-directive "name" for backwards-compat. + + "let aa = aa._;", {name: true, nomen: true}, [] + ], [ + "require();", {node: true}, [] + ], [ + "let aa = 'aa';", {single: true}, [] + ], [ + +// PR-404 - Add new directive "subscript" to play nice with Google Closure. + + "aa[\"aa\"] = 1;", {subscript: true}, ["aa"] + ], [ + "", {test_internal_error: true}, [] + ], [ + "let aa = this;", {this: true}, [] + ], [ + "", {trace: true}, [] + ], [ + ( + "function aa({bb, aa}) {\n" + + " switch (aa) {\n" + + " case 1:\n" + + " break;\n" + + " case 0:\n" + + " break;\n" + + " default:\n" + + " return {bb, aa};\n" + + " }\n" + + "}\n" + ), {unordered: true}, [] + ], [ + "let {bb, aa} = 0;", {unordered: true}, [] + ], [ + ( + "function aa() {\n" + + " if (aa) {\n" + + " let bb = 0;\n" + + " return bb;\n" + + " }\n" + + "}\n" + ), {variable: true}, [] + ], [ + "let bb = 0;\nlet aa = 0;", {variable: true}, [] + ], [ + "\t", {white: true}, [] + ] + ].forEach(function ([ + source, option_dict, global_list + ]) { + jstestIt(( + `test option=${JSON.stringify(option_dict)} handling-behavior` + ), function () { + let elemNow = JSON.stringify([ + option_dict, source, global_list + ]); + let warningsLength = ( + option_dict.test_internal_error + ? 1 + : 0 + ); + // Assert list is sorted. + assertOrThrow(elemPrv < elemNow, JSON.stringify([ + elemPrv, elemNow + ], undefined, 4)); + elemPrv = elemNow; + option_dict.beta = true; + [ + jslint.jslint, + jslintCjs.jslint + ].forEach(function (jslint) { + // test jslint's option handling-behavior + assertOrThrow( + jslint( + source, + option_dict, + global_list + ).warnings.length === warningsLength, + "jslint.jslint(" + JSON.stringify([ + source, option_dict, global_list + ]) + ")" + ); + // test jslint's directive handling-behavior + source = ( + "/*jslint " + JSON.stringify( + option_dict + ).slice(1, -1).replace(( + /"/g + ), "") + "*/\n" + + ( + global_list.length === 0 + ? "" + : "/*global " + global_list.join(",") + "*/\n" + ) + + source.replace(( + /^#!/ + ), "//") + ); + assertOrThrow( + jslint(source).warnings.length === warningsLength, + source + ); + }); + }); + }); +}); + +jstestDescribe(( + "test jslint's warnings handling-behavior" +), function testBehaviorJslintWarnings() { + jstestIt(( + "test jslint's warning handling-behavior" + ), function () { + +// this function will validate each jslint is raised with given +// malformed + + sourceJslintMjs.replace(( + /(\n\s*?\/\/\s*?test_cause:\s*?)(\S[\S\s]*?\S)(\n\n\s*?) *?\S/g + ), function (match0, header, causeList, footer) { + let tmp; + // console.error(match0); + // Validate header. + assertOrThrow(header === "\n\n// test_cause:\n", match0); + // Validate footer. + assertOrThrow(footer === "\n\n", match0); + // Validate causeList. + causeList = causeList.replace(( + /^\/\/ /gm + ), "").replace(( + /^\["\n([\S\s]*?)\n"(,.*?)$/gm + ), function (ignore, source, param) { + source = "[" + JSON.stringify(source) + param; + assertOrThrow(source.length > (80 - 3), source); + return source; + }).replace(( + / \/\/jslint-ignore-line$/gm + ), ""); + tmp = causeList.split("\n").map(function (cause) { + return ( + "[" + + JSON.parse(cause).map(function (elem) { + return JSON.stringify(elem); + }).join(", ") + + "]" + ); + }).sort().join("\n"); + assertOrThrow( + causeList === tmp, + "\n" + causeList + "\n\n" + tmp + ); + causeList.split("\n").forEach(function (cause) { + cause = JSON.parse(cause); + tmp = jslint.jslint(cause[0], { + beta: true, + test_cause: true + }).causes; + // Validate cause. + assertOrThrow( + tmp[JSON.stringify(cause.slice(1))], + ( + "\n" + JSON.stringify(cause) + "\n\n" + + Object.keys(tmp).sort().join("\n") + ) + ); + }); + return ""; + }); + }); +}); + +jstestDescribe(( + "test jstestXxx handling-behavior" +), function testBehaviorJstestXxx() { + jstestIt(( + "test jstestDescribe error handling-behavior" + ), function () { + throw new Error(); + }, "pass"); + jstestIt(( + "test jstestOnExit tests-failed handling-behavior" + ), function () { + jstestOnExit(undefined, "testsFailed"); + }); +}); + +jstestDescribe(( + "test misc handling-behavior" +), function testBehaviorMisc() { + jstestIt(( + "test misc handling-behavior" + ), async function () { + // test debugInline handling-behavior + noop(debugInline); + // test assertErrorThrownAsync error handling-behavior + await assertErrorThrownAsync(function () { + return assertErrorThrownAsync(noop); + }); + // test assertJsonEqual error handling-behavior + await assertErrorThrownAsync(function () { + assertJsonEqual(1, 2); + }); + await assertErrorThrownAsync(function () { + assertJsonEqual(1, 2, "undefined"); + }); + await assertErrorThrownAsync(function () { + assertJsonEqual(1, 2, {}); + }); + // test assertOrThrow error handling-behavior + await assertErrorThrownAsync(function () { + assertOrThrow(undefined, "undefined"); + }); + await assertErrorThrownAsync(function () { + assertOrThrow(undefined, new Error()); + }); + }); +}); + +jstestDescribe(( + "test v8CoverageListMerge handling-behavior" +), function testBehaviorV8CoverageListMerge() { + let functionsInput = JSON.stringify([ + { + functionName: "test", + isBlockCoverage: true, + ranges: [ + { + count: 2, + endOffset: 4, + startOffset: 0 + }, + { + count: 1, + endOffset: 2, + startOffset: 1 + }, + { + count: 1, + endOffset: 3, + startOffset: 2 + } + ] + } + ]); + jstestIt(( + "accepts empty arrays for `v8CoverageListMerge`" + ), function () { + assertJsonEqual(v8CoverageListMerge([]), { + result: [] + }); + }); + jstestIt(( + "funcCovs.length === 1" + ), function () { + assertJsonEqual(v8CoverageListMerge([ + { + result: [ + { + functions: [ + { + functionName: "test", + isBlockCoverage: true, + ranges: [ + { + count: 2, + endOffset: 4, + startOffset: 0 + } + ] + } + ], + moduleUrl: "/lib.js", + scriptId: "1" + } + ] + }, + { + result: [ + { + functions: [], + moduleUrl: "/lib.js", + scriptId: "2" + } + ] + } + ]), { + result: [ + { + functions: [ + { + functionName: "test", + isBlockCoverage: true, + ranges: [ + { + count: 2, + endOffset: 4, + startOffset: 0 + } + ] + } + ], + scriptId: "0" + } + ] + }); + }); + jstestIt(( + "accepts arrays with a single item for `v8CoverageListMerge`" + ), function () { + assertJsonEqual(v8CoverageListMerge([ + { + result: [ + { + functions: JSON.parse(functionsInput), + moduleUrl: "/lib.js", + scriptId: "123" + } + ] + } + ]), { + result: [ + { + functions: [ + { + functionName: "test", + isBlockCoverage: true, + ranges: [ + { + count: 2, + endOffset: 4, + startOffset: 0 + }, + { + count: 1, + endOffset: 3, + startOffset: 1 + } + ] + } + ], + moduleUrl: "/lib.js", + scriptId: "0" + } + ] + }); + }); + jstestIt(( + "accepts arrays with two identical items for" + + " `v8CoverageListMerge`" + ), function () { + assertJsonEqual(v8CoverageListMerge([ + { + result: [ + { + functions: JSON.parse(functionsInput), + scriptId: "123", + url: "/lib.js" + }, { + functions: JSON.parse(functionsInput), + scriptId: "123", + url: "/lib.js" + } + ] + } + ]), { + result: [ + { + functions: [ + { + functionName: "test", + isBlockCoverage: true, + ranges: [ + { + count: 4, + endOffset: 4, + startOffset: 0 + }, + { + count: 2, + endOffset: 3, + startOffset: 1 + } + ] + } + ], + scriptId: "0", + url: "/lib.js" + } + ] + }); + }); + [ + "test_coverage_merge_is_block_coverage_test.json", + "test_coverage_merge_issue_2_mixed_is_block_coverage_test.json", + "test_coverage_merge_node_10_internal_errors_one_of_test.json", + "test_coverage_merge_reduced_test.json", + "test_coverage_merge_simple_test.json", + "test_coverage_merge_various_test.json" + ].forEach(function (file) { + jstestIt(file, function () { + file = testCoverageMergeData[file]; + file.forEach(function ({ + expected, + inputs + }) { + assertJsonEqual(v8CoverageListMerge(inputs), expected); + }); + }); + }); + jstestIt(( + "merge multiple node-sqlite coverage files" + ), function () { + let data1 = [ + "test_v8_coverage_node_sqlite_9884_1633662346346_0.json", + "test_v8_coverage_node_sqlite_13216_1633662333140_0.json" + ].map(function (file) { + return testCoverageMergeData[file]; + }); + let data2 = testCoverageMergeData[ + "test_v8_coverage_node_sqlite_merged.json" + ]; + data1 = v8CoverageListMerge(data1); + data1 = v8CoverageListMerge([data1]); + +// Debug data1. +// await moduleFs.promises.writeFile( +// ".test_v8_coverage_node_sqlite_merged.json", +// JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n" +// ); + + assertJsonEqual(data1, data2); + }); +}); + +jstestDescribe(( + "test v8CoverageReportCreate handling-behavior" +), function testBehaviorV8CoverageReportCreate() { + jstestIt(( + "test null-case handling-behavior" + ), async function () { + await assertErrorThrownAsync(function () { + return v8CoverageReportCreate({}); + }, "invalid coverageDir"); + }); + jstestIt(( + "test coverage-report jslint.mjs handling-behavior" + ), async function () { + // test remove-old-coverage handling-behavior + await fsWriteFileWithParents( + ".tmp/coverage_jslint/coverage-0-0-0.json", + "" + ); + await jslint.jslint_cli({ + console_error: noop, // comment to debug + mode_cli: true, + process_argv: [ + "node", "jslint.mjs", + "v8_coverage_report=.tmp/coverage_jslint", + "--exclude=aa.js", + "--include-node-modules=1", + "--include=jslint.mjs", + "node", "jslint.mjs" + ] + }); + }); + [ + [ + "v8CoverageReportCreate_high.js", ( + "switch(0){\n" + + "case 0:break;\n" + + "}\n" + ) + ], [ + "v8CoverageReportCreate_ignore.js", ( + "/*coverage-ignore-file*/\n" + + "switch(0){\n" + + "case 0:break;\n" + + "}\n" + ) + ], [ + "v8CoverageReportCreate_low.js", ( + "switch(0){\n" + + "case 1:break;\n" + + "case 2:break;\n" + + "case 3:break;\n" + + "case 4:break;\n" + + "}\n" + ) + ], [ + "v8CoverageReportCreate_medium.js", ( + "switch(0){\n" + + "case 0:break;\n" + + "case 1:break;\n" + + "case 2:break;\n" + + "}\n" + ) + ] + ].forEach(function ([ + file, data + ], ii) { + jstestIt(file, async function () { + let dir = ".tmp/coverage_" + ii + "/"; + file = dir + file; + await fsWriteFileWithParents(file, data); + await jslint.jslint_cli({ + console_error: noop, // comment to debug + mode_cli: true, + process_argv: [ + "node", "jslint.mjs", + "v8_coverage_report=" + dir, + "node", + file + ] + }); + }); + }); + jstestIt(( + "test npm handling-behavior" + ), async function () { + await jslint.jslint_cli({ + console_error: noop, // comment to debug + mode_cli: true, + process_argv: [ + "node", "jslint.mjs", + "v8_coverage_report=.tmp/coverage_npm", + "npm", "--version" + ] + }); + }); + jstestIt(( + "test misc handling-behavior" + ), async function () { + await Promise.all([ + [ + ".tmp/coverage_misc/aa.js", "\n".repeat(0x100) + ], [ + ".tmp/coverage_misc/coverage-0-0-0.json", JSON.stringify({ + "result": [ + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 0xf0, + "startOffset": 0x10 + }, + { + "count": 1, + "endOffset": 0x40, + "startOffset": 0x20 + }, + { + "count": 1, + "endOffset": 0x80, + "startOffset": 0x60 + }, + { + "count": 0, + "endOffset": 0x45, + "startOffset": 0x25 + }, + { + "count": 0, + "endOffset": 0x85, + "startOffset": 0x65 + } + ] + } + ], + "scriptId": "0", + "url": "file:///" + modulePath.resolve( + ".tmp/coverage_misc/aa.js" + ) + } + ] + }, undefined, 4) + ] + ].map(async function ([ + file, data + ]) { + await fsWriteFileWithParents(file, data); + })); + await jslint.jslint_cli({ + console_error: noop, // comment to debug + mode_cli: true, + process_argv: [ + "node", "jslint.mjs", + "v8_coverage_report=.tmp/coverage_misc" + // "node", ".tmp/coverage_misc/aa.js" + ] + }); + }); +}); diff --git a/branch-beta/test_coverage_merge_data.json b/branch-beta/test_coverage_merge_data.json new file mode 100644 index 000000000..bc0bb0e48 --- /dev/null +++ b/branch-beta/test_coverage_merge_data.json @@ -0,0 +1,17877 @@ +{ + "test_coverage_merge_is_block_coverage_test.json": [ + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Identity preserves 'isBlockCoverage: true'", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Identity preserves 'isBlockCoverage: false'", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a flat block-coverage (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a flat block-coverage (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a flat block-coverage (permutation 2)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a flat block-coverage (permutation 3)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a flat block-coverage (permutation 4)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a flat block-coverage (permutation 5)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a block-coverage with a child (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a block-coverage with a child (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a block-coverage with a child (permutation 2)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a block-coverage with a child (permutation 3)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a block-coverage with a child (permutation 4)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a block-coverage with a child (permutation 5)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage and two flat block-coverages (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage and two flat block-coverages (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage and two flat block-coverages (permutation 2)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage and two flat block-coverages (permutation 3)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage and two flat block-coverages (permutation 4)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage and two flat block-coverages (permutation 5)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 24, + "endOffset": 6, + "startOffset": 2 + }, + { + "count": 6, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 42, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverages and two block-coverages with children (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 24, + "endOffset": 6, + "startOffset": 2 + }, + { + "count": 6, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 42, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverages and two block-coverages with children (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 24, + "endOffset": 6, + "startOffset": 2 + }, + { + "count": 6, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 42, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverages and two block-coverages with children (permutation 2)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 24, + "endOffset": 6, + "startOffset": 2 + }, + { + "count": 6, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 42, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverages and two block-coverages with children (permutation 3)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 24, + "endOffset": 6, + "startOffset": 2 + }, + { + "count": 6, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 42, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverages and two block-coverages with children (permutation 4)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 24, + "endOffset": 6, + "startOffset": 2 + }, + { + "count": 6, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 42, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverages and two block-coverages with children (permutation 5)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 50, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "count": 40, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage with a flat block-coverage with count (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 50, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "count": 40, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage with a flat block-coverage with count (permutation 1)", + "status": "run" + } + ], + "test_coverage_merge_issue_2_mixed_is_block_coverage_test.json": [ + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 23, + "functionName": "realpathSync", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 42951, + "startOffset": 39013 + }, + { + "count": 15, + "endOffset": 39088, + "startOffset": 39069 + }, + { + "count": 5, + "endOffset": 39140, + "startOffset": 39088 + }, + { + "count": 0, + "endOffset": 39214, + "startOffset": 39196 + }, + { + "count": 5, + "endOffset": 39356, + "startOffset": 39341 + }, + { + "count": 1, + "endOffset": 39418, + "startOffset": 39383 + }, + { + "count": 19, + "endOffset": 39991, + "startOffset": 39418 + }, + { + "count": 0, + "endOffset": 40010, + "startOffset": 39991 + }, + { + "count": 0, + "endOffset": 40187, + "startOffset": 40012 + }, + { + "count": 19, + "endOffset": 40324, + "startOffset": 40187 + }, + { + "count": 427, + "endOffset": 42868, + "startOffset": 40324 + }, + { + "count": 34, + "endOffset": 40551, + "startOffset": 40436 + }, + { + "count": 393, + "endOffset": 40677, + "startOffset": 40551 + }, + { + "count": 187, + "endOffset": 40798, + "startOffset": 40760 + }, + { + "count": 28, + "endOffset": 40797, + "startOffset": 40770 + }, + { + "count": 257, + "endOffset": 40937, + "startOffset": 40800 + }, + { + "count": 0, + "endOffset": 40915, + "startOffset": 40891 + }, + { + "count": 170, + "endOffset": 40999, + "startOffset": 40937 + }, + { + "count": 11, + "endOffset": 41017, + "startOffset": 40999 + }, + { + "count": 0, + "endOffset": 41097, + "startOffset": 41048 + }, + { + "count": 170, + "endOffset": 42382, + "startOffset": 41097 + }, + { + "count": 135, + "endOffset": 41551, + "startOffset": 41450 + }, + { + "count": 11, + "endOffset": 41525, + "startOffset": 41503 + }, + { + "count": 35, + "endOffset": 41964, + "startOffset": 41551 + }, + { + "count": 13, + "endOffset": 41924, + "startOffset": 41875 + }, + { + "count": 22, + "endOffset": 42214, + "startOffset": 41964 + }, + { + "count": 34, + "endOffset": 42296, + "startOffset": 42214 + }, + { + "count": 0, + "endOffset": 42326, + "startOffset": 42296 + }, + { + "count": 34, + "endOffset": 42376, + "startOffset": 42326 + }, + { + "count": 34, + "endOffset": 42658, + "startOffset": 42382 + }, + { + "count": 0, + "endOffset": 42677, + "startOffset": 42658 + }, + { + "count": 0, + "endOffset": 42864, + "startOffset": 42679 + }, + { + "count": 18, + "endOffset": 42883, + "startOffset": 42868 + }, + { + "count": 4, + "endOffset": 42906, + "startOffset": 42883 + }, + { + "count": 18, + "endOffset": 42950, + "startOffset": 42906 + } + ] + } + ], + "scriptId": "0", + "url": "fs.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "realpathSync", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 42951, + "startOffset": 39013 + }, + { + "count": 15, + "endOffset": 39088, + "startOffset": 39069 + }, + { + "count": 5, + "endOffset": 39140, + "startOffset": 39088 + }, + { + "count": 0, + "endOffset": 39214, + "startOffset": 39196 + }, + { + "count": 5, + "endOffset": 39356, + "startOffset": 39341 + }, + { + "count": 1, + "endOffset": 39418, + "startOffset": 39383 + }, + { + "count": 19, + "endOffset": 39991, + "startOffset": 39418 + }, + { + "count": 0, + "endOffset": 40010, + "startOffset": 39991 + }, + { + "count": 0, + "endOffset": 40187, + "startOffset": 40012 + }, + { + "count": 19, + "endOffset": 40324, + "startOffset": 40187 + }, + { + "count": 427, + "endOffset": 42868, + "startOffset": 40324 + }, + { + "count": 34, + "endOffset": 40551, + "startOffset": 40436 + }, + { + "count": 393, + "endOffset": 40677, + "startOffset": 40551 + }, + { + "count": 187, + "endOffset": 40798, + "startOffset": 40760 + }, + { + "count": 28, + "endOffset": 40797, + "startOffset": 40770 + }, + { + "count": 257, + "endOffset": 40937, + "startOffset": 40800 + }, + { + "count": 0, + "endOffset": 40915, + "startOffset": 40891 + }, + { + "count": 170, + "endOffset": 40999, + "startOffset": 40937 + }, + { + "count": 11, + "endOffset": 41017, + "startOffset": 40999 + }, + { + "count": 0, + "endOffset": 41097, + "startOffset": 41048 + }, + { + "count": 170, + "endOffset": 42382, + "startOffset": 41097 + }, + { + "count": 135, + "endOffset": 41551, + "startOffset": 41450 + }, + { + "count": 11, + "endOffset": 41525, + "startOffset": 41503 + }, + { + "count": 35, + "endOffset": 41932, + "startOffset": 41551 + }, + { + "count": 13, + "endOffset": 41924, + "startOffset": 41875 + }, + { + "count": 35, + "endOffset": 41964, + "startOffset": 41932 + }, + { + "count": 22, + "endOffset": 42214, + "startOffset": 41964 + }, + { + "count": 34, + "endOffset": 42296, + "startOffset": 42214 + }, + { + "count": 0, + "endOffset": 42326, + "startOffset": 42296 + }, + { + "count": 34, + "endOffset": 42376, + "startOffset": 42326 + }, + { + "count": 34, + "endOffset": 42658, + "startOffset": 42382 + }, + { + "count": 0, + "endOffset": 42677, + "startOffset": 42658 + }, + { + "count": 0, + "endOffset": 42864, + "startOffset": 42679 + }, + { + "count": 18, + "endOffset": 42883, + "startOffset": 42868 + }, + { + "count": 4, + "endOffset": 42906, + "startOffset": 42883 + }, + { + "count": 18, + "endOffset": 42950, + "startOffset": 42906 + } + ] + } + ], + "scriptId": "48", + "url": "fs.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "realpathSync", + "isBlockCoverage": false, + "ranges": [ + { + "count": 3, + "endOffset": 42951, + "startOffset": 39013 + } + ] + } + ], + "scriptId": "48", + "url": "fs.js" + } + ] + } + ], + "name": "Issue 2: Mixed isBlockCoverage (https://github.com/demurgos/v8-coverage/issues/2)", + "status": "run" + } + ], + "test_coverage_merge_node_10_internal_errors_one_of_test.json": [ + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2516, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 719, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 718, + "endOffset": 148299, + "startOffset": 147892 + }, + { + "count": 537, + "endOffset": 148155, + "startOffset": 147892 + }, + { + "count": 530, + "endOffset": 148061, + "startOffset": 147892 + }, + { + "count": 2, + "endOffset": 148061, + "startOffset": 148055 + }, + { + "count": 2, + "endOffset": 148155, + "startOffset": 148061 + }, + { + "count": 188, + "endOffset": 148299, + "startOffset": 148155 + }, + { + "count": 2, + "endOffset": 148299, + "startOffset": 148291 + }, + { + "count": 38, + "endOffset": 148437, + "startOffset": 148299 + }, + { + "count": 5, + "endOffset": 148433, + "startOffset": 148299 + }, + { + "count": 4, + "endOffset": 148427, + "startOffset": 148299 + }, + { + "count": 2, + "endOffset": 148433, + "startOffset": 148427 + }, + { + "count": 2, + "endOffset": 148437, + "startOffset": 148433 + }, + { + "count": 2019, + "endOffset": 148555, + "startOffset": 148437 + }, + { + "count": 1799, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 2, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 148556, + "startOffset": 147273 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1453, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 4, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 1, + "endOffset": 148061, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148061, + "startOffset": 148055 + }, + { + "count": 2, + "endOffset": 148433, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148155, + "startOffset": 148061 + }, + { + "count": 1, + "endOffset": 148299, + "startOffset": 148155 + }, + { + "count": 0, + "endOffset": 148299, + "startOffset": 148291 + }, + { + "count": 1, + "endOffset": 148427, + "startOffset": 148299 + }, + { + "count": 0, + "endOffset": 148433, + "startOffset": 148427 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 148433 + }, + { + "count": 1, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 28, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 2, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 1, + "endOffset": 148061, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148061, + "startOffset": 148055 + }, + { + "count": 1, + "endOffset": 148433, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148155, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148433, + "startOffset": 148291 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 148433 + }, + { + "count": 26, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 1, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 0, + "endOffset": 148299, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 148427 + }, + { + "count": 1, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 173, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 33, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 0, + "endOffset": 148155, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 148291 + }, + { + "count": 140, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 313, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 154, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 148055 + }, + { + "count": 159, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 43, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 26, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 24, + "endOffset": 148061, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148061, + "startOffset": 148055 + }, + { + "count": 2, + "endOffset": 148433, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148155, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148433, + "startOffset": 148291 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 148433 + }, + { + "count": 17, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 148, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 0, + "endOffset": 148155, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148291 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 2, + "endOffset": 148061, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148061, + "startOffset": 148055 + }, + { + "count": 1, + "endOffset": 148433, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148155, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148433, + "startOffset": 148291 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148433 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 346, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148055 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "node@10.11.0: internal/errors.js#oneOf", + "status": "run" + } + ], + "test_coverage_merge_reduced_test.json": [ + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1962, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 332, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 331, + "endOffset": 4, + "startOffset": 2 + }, + { + "count": 329, + "endOffset": 4, + "startOffset": 3 + }, + { + "count": 30, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 4 + }, + { + "count": 29, + "endOffset": 6, + "startOffset": 5 + }, + { + "count": 1814, + "endOffset": 7, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1811, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 181, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 180, + "endOffset": 4, + "startOffset": 2 + }, + { + "count": 27, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 4 + }, + { + "count": 26, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 148, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 0, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "node@10.11.0: internal/errors.js#oneOf: reduced", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1962, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 332, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 331, + "endOffset": 4, + "startOffset": 2 + }, + { + "count": 329, + "endOffset": 4, + "startOffset": 3 + }, + { + "count": 30, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 4 + }, + { + "count": 29, + "endOffset": 6, + "startOffset": 5 + }, + { + "count": 1814, + "endOffset": 7, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1811, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 181, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 180, + "endOffset": 4, + "startOffset": 2 + }, + { + "count": 27, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 4 + }, + { + "count": 26, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 148, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 0, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "node@10.11.0: internal/errors.js#oneOf: reduced with hint", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 66, + "endOffset": 4, + "startOffset": 2 + }, + { + "count": 30, + "endOffset": 4, + "startOffset": 3 + }, + { + "count": 43, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 4 + }, + { + "count": 52, + "endOffset": 7, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 6, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "node@10.11.0: internal/errors.js#oneOf: minimal", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 66, + "endOffset": 4, + "startOffset": 2 + }, + { + "count": 30, + "endOffset": 4, + "startOffset": 3 + }, + { + "count": 43, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 4 + }, + { + "count": 52, + "endOffset": 7, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 6, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 40, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "node@10.11.0: internal/errors.js#oneOf: minimal with hint", + "status": "run" + } + ], + "test_coverage_merge_simple_test.json": [ + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "No children", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "One child", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Equal", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 3, + "startOffset": 1 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 3, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (BeforeAdjacent)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 2, + "startOffset": 1 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 2, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (BeforeStrict)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 12, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (AfterAdjacent)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 12, + "endOffset": 8, + "startOffset": 7 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 7 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (AfterStrict)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Nested (OverlapStrict)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 6, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Nested (OverlapEqualEnd)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 8, + "startOffset": 3 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Nested (OverlapEqualStart)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Nested (ContainedEqualStart)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 6, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Nested (ContainedEqualEnd)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Nested (ContainedStrict)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Partial overlap (PartialOverlapStart)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 12, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Partial overlap (PartialOverlapEnd)", + "status": "run" + } + ], + "test_coverage_merge_various_test.json": [ + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three empty trees", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 3, + "endOffset": 3, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 3, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 3, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Equal children, same startOffset as parents", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 3, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Equal children, same endOffset as parents", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 3, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 3, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (strict) children, offsets matching parents (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 3, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 3, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (strict) children, offsets matching parents (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 4, + "startOffset": 1 + }, + { + "count": 12, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (adjacent) children (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 4, + "startOffset": 1 + }, + { + "count": 12, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (adjacent) children (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 2 left & 1 right (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 2 left & 1 right (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 2 left & 1 right (permutation 2)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 2 left & 1 right (permutation 3)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 2 left & 1 right (permutation 4)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 2 left & 1 right (permutation 5)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 7, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 16, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 1 left & 2 right (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 7, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 16, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 1 left & 2 right (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 7, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 16, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 1 left & 2 right (permutation 2)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 7, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 16, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 1 left & 2 right (permutation 3)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 7, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 16, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 1 left & 2 right (permutation 4)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 7, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 16, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 1 left & 2 right (permutation 5)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 13, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 10, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 9, + "endOffset": 8, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 6, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 7, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 8, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Normalization: two trees with complementary children (not summing to the same count)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 14, + "endOffset": 8, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 6, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 5, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 8, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 8, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 9, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 8, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Normalization: two trees with complementary children (summing to the same count, same as parent)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 10, + "endOffset": 8, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 6, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 7, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Normalization: two trees with complementary children (summing to the same count, different from parent)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 5, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 4, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: a+b", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 5, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 4, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: b+a", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 6, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: a+c", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: b+c", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 5, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 4, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: (a+b)+c", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 6, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: (a+c)+b", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: a+(b+c)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: a+b+c", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 40, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 31, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 22, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 13, + "endOffset": 5, + "startOffset": 2 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 7, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: a+b+c+d", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 4, + "startOffset": 0 + }, + { + "count": 3, + "endOffset": 4, + "startOffset": 1 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a tagged sliding chain: a+b", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 4, + "startOffset": 0 + }, + { + "count": 3, + "endOffset": 4, + "startOffset": 1 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a tagged sliding chain: a+b", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 15, + "endOffset": 16, + "startOffset": 0 + }, + { + "count": 20, + "endOffset": 4, + "startOffset": 0 + }, + { + "count": 18, + "endOffset": 1, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 2, + "startOffset": 1 + }, + { + "count": 17, + "endOffset": 4, + "startOffset": 3 + }, + { + "count": 12, + "endOffset": 11, + "startOffset": 4 + }, + { + "count": 13, + "endOffset": 8, + "startOffset": 5 + }, + { + "count": 16, + "endOffset": 7, + "startOffset": 6 + }, + { + "count": 9, + "endOffset": 8, + "startOffset": 7 + }, + { + "count": 11, + "endOffset": 11, + "startOffset": 9 + }, + { + "count": 19, + "endOffset": 11, + "startOffset": 10 + }, + { + "count": 23, + "endOffset": 15, + "startOffset": 11 + }, + { + "count": 22, + "endOffset": 12, + "startOffset": 11 + }, + { + "count": 16, + "endOffset": 14, + "startOffset": 13 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 16, + "startOffset": 0 + }, + { + "count": 6, + "endOffset": 4, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 2, + "startOffset": 1 + }, + { + "count": 4, + "endOffset": 7, + "startOffset": 6 + }, + { + "count": 9, + "endOffset": 15, + "startOffset": 10 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 16, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 1, + "startOffset": 0 + }, + { + "count": 5, + "endOffset": 8, + "startOffset": 5 + }, + { + "count": 3, + "endOffset": 12, + "startOffset": 9 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 16, + "startOffset": 0 + }, + { + "count": 7, + "endOffset": 11, + "startOffset": 3 + }, + { + "count": 3, + "endOffset": 8, + "startOffset": 7 + }, + { + "count": 3, + "endOffset": 14, + "startOffset": 13 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges three trees with a complex relation (chains, nesting)", + "status": "run" + } + ], + "test_v8_coverage_node_sqlite_13216_1633662333140_0.json": { + "result": [ + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 20604, + "startOffset": 0 + } + ] + }, + { + "functionName": "Console", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4925, + "startOffset": 2686 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5200, + "startOffset": 5144 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5669, + "startOffset": 5458 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 6489, + "startOffset": 5878 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 6165, + "startOffset": 6067 + }, + { + "count": 1, + "endOffset": 6124, + "startOffset": 6101 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6208, + "startOffset": 6178 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6417, + "startOffset": 6315 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6460, + "startOffset": 6430 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 7776, + "startOffset": 6563 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9334, + "startOffset": 7850 + }, + { + "count": 0, + "endOffset": 8095, + "startOffset": 8081 + }, + { + "count": 0, + "endOffset": 8197, + "startOffset": 8171 + }, + { + "count": 0, + "endOffset": 8432, + "startOffset": 8238 + }, + { + "count": 0, + "endOffset": 8521, + "startOffset": 8493 + }, + { + "count": 0, + "endOffset": 9261, + "startOffset": 8971 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9918, + "startOffset": 9411 + }, + { + "count": 0, + "endOffset": 9635, + "startOffset": 9629 + }, + { + "count": 0, + "endOffset": 9840, + "startOffset": 9714 + }, + { + "count": 0, + "endOffset": 9910, + "startOffset": 9886 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 10124, + "startOffset": 9993 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10330, + "startOffset": 10199 + } + ] + }, + { + "functionName": "createWriteErrorHandler", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 11331, + "startOffset": 10424 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 11327, + "startOffset": 10493 + }, + { + "count": 0, + "endOffset": 10786, + "startOffset": 10768 + } + ] + }, + { + "functionName": "log", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 11452, + "startOffset": 11363 + } + ] + }, + { + "functionName": "warn", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11551, + "startOffset": 11461 + } + ] + }, + { + "functionName": "dir", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11751, + "startOffset": 11560 + } + ] + }, + { + "functionName": "time", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12125, + "startOffset": 11758 + } + ] + }, + { + "functionName": "timeEnd", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12429, + "startOffset": 12132 + } + ] + }, + { + "functionName": "timeLog", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12679, + "startOffset": 12436 + } + ] + }, + { + "functionName": "trace", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12886, + "startOffset": 12693 + } + ] + }, + { + "functionName": "assert", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 13116, + "startOffset": 12893 + } + ] + }, + { + "functionName": "clear", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 13620, + "startOffset": 13180 + } + ] + }, + { + "functionName": "count", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14153, + "startOffset": 13684 + } + ] + }, + { + "functionName": "countReset", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14518, + "startOffset": 14222 + } + ] + }, + { + "functionName": "group", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14699, + "startOffset": 14525 + } + ] + }, + { + "functionName": "groupEnd", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14880, + "startOffset": 14706 + } + ] + }, + { + "functionName": "table", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18454, + "startOffset": 14932 + } + ] + }, + { + "functionName": "timeLogImpl", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19012, + "startOffset": 18499 + } + ] + }, + { + "functionName": "pad", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19095, + "startOffset": 19016 + } + ] + }, + { + "functionName": "formatTime", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19892, + "startOffset": 19099 + } + ] + }, + { + "functionName": "isArray", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20089, + "startOffset": 20033 + } + ] + }, + { + "functionName": "noop", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20112, + "startOffset": 20094 + } + ] + } + ], + "scriptId": "28", + "url": "internal/console/constructor.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1638, + "startOffset": 0 + } + ] + } + ], + "scriptId": "29", + "url": "internal/constants.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 2283, + "startOffset": 0 + } + ] + }, + { + "functionName": "sendInspectorCommand", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 453, + "startOffset": 100 + } + ] + }, + { + "functionName": "installConsoleExtensions", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1094, + "startOffset": 530 + } + ] + }, + { + "functionName": "wrapConsole", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 1984, + "startOffset": 1176 + }, + { + "count": 23, + "endOffset": 1981, + "startOffset": 1336 + }, + { + "count": 19, + "endOffset": 1855, + "startOffset": 1555 + }, + { + "count": 4, + "endOffset": 1976, + "startOffset": 1855 + } + ] + }, + { + "functionName": "get consoleFromVM", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2217, + "startOffset": 2164 + } + ] + }, + { + "functionName": "set consoleFromVM", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 2277, + "startOffset": 2222 + } + ] + } + ], + "scriptId": "30", + "url": "internal/util/inspector.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 42795, + "startOffset": 0 + } + ] + }, + { + "functionName": "toUSVString", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2627, + "startOffset": 2323 + } + ] + }, + { + "functionName": "serializeTupleOrigin", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2965, + "startOffset": 2845 + } + ] + }, + { + "functionName": "URLContext", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 3611, + "startOffset": 3378 + } + ] + }, + { + "functionName": "URLSearchParams", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 6333, + "startOffset": 3909 + }, + { + "count": 0, + "endOffset": 6267, + "startOffset": 4027 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7439, + "startOffset": 6339 + } + ] + }, + { + "functionName": "onParseComplete", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 8156, + "startOffset": 7446 + }, + { + "count": 0, + "endOffset": 7716, + "startOffset": 7706 + }, + { + "count": 0, + "endOffset": 7790, + "startOffset": 7780 + }, + { + "count": 0, + "endOffset": 7877, + "startOffset": 7873 + } + ] + }, + { + "functionName": "onParseError", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8237, + "startOffset": 8160 + } + ] + }, + { + "functionName": "onParseProtocolComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8596, + "startOffset": 8241 + } + ] + }, + { + "functionName": "onParseHostnameComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8956, + "startOffset": 8600 + } + ] + }, + { + "functionName": "onParsePortComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9125, + "startOffset": 8960 + } + ] + }, + { + "functionName": "onParseHostComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9440, + "startOffset": 9129 + } + ] + }, + { + "functionName": "onParsePathComplete", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 9954, + "startOffset": 9444 + }, + { + "count": 0, + "endOffset": 9787, + "startOffset": 9716 + } + ] + }, + { + "functionName": "onParseSearchComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10129, + "startOffset": 9958 + } + ] + }, + { + "functionName": "onParseHashComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10306, + "startOffset": 10133 + } + ] + }, + { + "functionName": "URL", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 10663, + "startOffset": 10325 + }, + { + "count": 0, + "endOffset": 10518, + "startOffset": 10464 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10752, + "startOffset": 10669 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 327, + "endOffset": 10853, + "startOffset": 10758 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11136, + "startOffset": 10931 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12107, + "startOffset": 11142 + } + ] + }, + { + "functionName": "format", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 13502, + "startOffset": 12284 + }, + { + "count": 0, + "endOffset": 12432, + "startOffset": 12371 + }, + { + "count": 0, + "endOffset": 13023, + "startOffset": 12856 + }, + { + "count": 0, + "endOffset": 13094, + "startOffset": 13056 + }, + { + "count": 0, + "endOffset": 13172, + "startOffset": 13150 + }, + { + "count": 0, + "endOffset": 13247, + "startOffset": 13181 + }, + { + "count": 0, + "endOffset": 13386, + "startOffset": 13363 + }, + { + "count": 0, + "endOffset": 13476, + "startOffset": 13450 + } + ] + }, + { + "functionName": "toString", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 13849, + "startOffset": 13788 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 13967, + "startOffset": 13920 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14159, + "startOffset": 13974 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14895, + "startOffset": 14245 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15020, + "startOffset": 14970 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15408, + "startOffset": 15027 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15535, + "startOffset": 15483 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15953, + "startOffset": 15542 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16080, + "startOffset": 16028 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16498, + "startOffset": 16087 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16731, + "startOffset": 16569 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17038, + "startOffset": 16738 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17167, + "startOffset": 17113 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17482, + "startOffset": 17174 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17656, + "startOffset": 17553 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17991, + "startOffset": 17663 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": true, + "ranges": [ + { + "count": 218, + "endOffset": 18268, + "startOffset": 18066 + }, + { + "count": 0, + "endOffset": 18167, + "startOffset": 18148 + }, + { + "count": 0, + "endOffset": 18221, + "startOffset": 18211 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 18501, + "startOffset": 18275 + }, + { + "count": 0, + "endOffset": 18396, + "startOffset": 18389 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18718, + "startOffset": 18574 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19232, + "startOffset": 18725 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19372, + "startOffset": 19324 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19599, + "startOffset": 19443 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20037, + "startOffset": 19606 + } + ] + }, + { + "functionName": "toJSON", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20249, + "startOffset": 20190 + } + ] + }, + { + "functionName": "update", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20581, + "startOffset": 20263 + } + ] + }, + { + "functionName": "initSearchParams", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 20731, + "startOffset": 20585 + }, + { + "count": 0, + "endOffset": 20730, + "startOffset": 20686 + } + ] + }, + { + "functionName": "parseParams", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 23241, + "startOffset": 20844 + } + ] + }, + { + "functionName": "serializeParams", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 24820, + "startOffset": 24244 + } + ] + }, + { + "functionName": "defineIDLClass", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 25592, + "startOffset": 24878 + }, + { + "count": 13, + "endOffset": 25379, + "startOffset": 25226 + }, + { + "count": 1, + "endOffset": 25589, + "startOffset": 25435 + } + ] + }, + { + "functionName": "merge", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 26271, + "startOffset": 25615 + } + ] + }, + { + "functionName": "append", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 26740, + "startOffset": 26341 + } + ] + }, + { + "functionName": "delete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27265, + "startOffset": 26747 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27722, + "startOffset": 27272 + } + ] + }, + { + "functionName": "getAll", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 28214, + "startOffset": 27729 + } + ] + }, + { + "functionName": "has", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 28665, + "startOffset": 28221 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 29736, + "startOffset": 28672 + } + ] + }, + { + "functionName": "sort", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 30962, + "startOffset": 29743 + } + ] + }, + { + "functionName": "entries", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31345, + "startOffset": 31130 + } + ] + }, + { + "functionName": "forEach", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31943, + "startOffset": 31352 + } + ] + }, + { + "functionName": "keys", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 32207, + "startOffset": 32001 + } + ] + }, + { + "functionName": "values", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 32424, + "startOffset": 32214 + } + ] + }, + { + "functionName": "toString", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 32767, + "startOffset": 32561 + } + ] + }, + { + "functionName": "createSearchParamsIterator", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33269, + "startOffset": 33058 + } + ] + }, + { + "functionName": "next", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 34289, + "startOffset": 33499 + } + ] + }, + { + "functionName": "defineIDLClass", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 35544, + "startOffset": 34294 + } + ] + }, + { + "functionName": "domainToASCII", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 35734, + "startOffset": 35553 + } + ] + }, + { + "functionName": "domainToUnicode", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 35923, + "startOffset": 35738 + } + ] + }, + { + "functionName": "urlToOptions", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36633, + "startOffset": 36071 + } + ] + }, + { + "functionName": "getPathFromURLWin32", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 38099, + "startOffset": 36673 + } + ] + }, + { + "functionName": "getPathFromURLPosix", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 38623, + "startOffset": 38103 + } + ] + }, + { + "functionName": "fileURLToPath", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 38982, + "startOffset": 38627 + } + ] + }, + { + "functionName": "encodePathChars", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 40330, + "startOffset": 39761 + }, + { + "count": 0, + "endOffset": 39883, + "startOffset": 39834 + }, + { + "count": 0, + "endOffset": 39985, + "startOffset": 39959 + }, + { + "count": 0, + "endOffset": 40043, + "startOffset": 39992 + }, + { + "count": 0, + "endOffset": 40130, + "startOffset": 40081 + }, + { + "count": 0, + "endOffset": 40224, + "startOffset": 40168 + }, + { + "count": 0, + "endOffset": 40307, + "startOffset": 40262 + } + ] + }, + { + "functionName": "pathToFileURL", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 41482, + "startOffset": 40334 + }, + { + "count": 0, + "endOffset": 41025, + "startOffset": 40456 + }, + { + "count": 0, + "endOffset": 41381, + "startOffset": 41327 + }, + { + "count": 0, + "endOffset": 41406, + "startOffset": 41390 + } + ] + }, + { + "functionName": "isURLInstance", + "isBlockCoverage": true, + "ranges": [ + { + "count": 277, + "endOffset": 41607, + "startOffset": 41486 + }, + { + "count": 0, + "endOffset": 41603, + "startOffset": 41580 + } + ] + }, + { + "functionName": "toPathIfFileURL", + "isBlockCoverage": true, + "ranges": [ + { + "count": 277, + "endOffset": 41760, + "startOffset": 41611 + }, + { + "count": 0, + "endOffset": 41759, + "startOffset": 41717 + } + ] + }, + { + "functionName": "constructUrl", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 42484, + "startOffset": 41764 + } + ] + } + ], + "scriptId": "31", + "url": "internal/url.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 3134, + "startOffset": 0 + } + ] + }, + { + "functionName": "encodeStr", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3065, + "startOffset": 1374 + } + ] + } + ], + "scriptId": "32", + "url": "internal/querystring.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 44873, + "startOffset": 0 + } + ] + }, + { + "functionName": "isPathSeparator", + "isBlockCoverage": true, + "ranges": [ + { + "count": 144876, + "endOffset": 1635, + "startOffset": 1529 + }, + { + "count": 144646, + "endOffset": 1631, + "startOffset": 1600 + } + ] + }, + { + "functionName": "isPosixPathSeparator", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1718, + "startOffset": 1639 + } + ] + }, + { + "functionName": "isWindowsDeviceRoot", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5087, + "endOffset": 1895, + "startOffset": 1722 + }, + { + "count": 4801, + "endOffset": 1822, + "startOffset": 1795 + }, + { + "count": 1027, + "endOffset": 1891, + "startOffset": 1824 + }, + { + "count": 741, + "endOffset": 1890, + "startOffset": 1863 + } + ] + }, + { + "functionName": "normalizeString", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2284, + "endOffset": 3826, + "startOffset": 1961 + }, + { + "count": 131113, + "endOffset": 3808, + "startOffset": 2184 + }, + { + "count": 128829, + "endOffset": 2245, + "startOffset": 2219 + }, + { + "count": 2284, + "endOffset": 2340, + "startOffset": 2245 + }, + { + "count": 2279, + "endOffset": 2296, + "startOffset": 2290 + }, + { + "count": 5, + "endOffset": 2340, + "startOffset": 2296 + }, + { + "count": 128834, + "endOffset": 2375, + "startOffset": 2340 + }, + { + "count": 12268, + "endOffset": 3704, + "startOffset": 2375 + }, + { + "count": 11644, + "endOffset": 2421, + "startOffset": 2408 + }, + { + "count": 724, + "endOffset": 2450, + "startOffset": 2423 + }, + { + "count": 11544, + "endOffset": 3658, + "startOffset": 2450 + }, + { + "count": 31, + "endOffset": 3439, + "startOffset": 2472 + }, + { + "count": 0, + "endOffset": 2588, + "startOffset": 2529 + }, + { + "count": 0, + "endOffset": 2648, + "startOffset": 2589 + }, + { + "count": 0, + "endOffset": 2868, + "startOffset": 2789 + }, + { + "count": 0, + "endOffset": 3283, + "startOffset": 3108 + }, + { + "count": 0, + "endOffset": 3430, + "startOffset": 3294 + }, + { + "count": 11513, + "endOffset": 3658, + "startOffset": 3439 + }, + { + "count": 9341, + "endOffset": 3540, + "startOffset": 3487 + }, + { + "count": 2172, + "endOffset": 3601, + "startOffset": 3540 + }, + { + "count": 12237, + "endOffset": 3704, + "startOffset": 3658 + }, + { + "count": 116566, + "endOffset": 3803, + "startOffset": 3704 + }, + { + "count": 1642, + "endOffset": 3746, + "startOffset": 3732 + }, + { + "count": 165, + "endOffset": 3771, + "startOffset": 3748 + }, + { + "count": 116401, + "endOffset": 3803, + "startOffset": 3771 + } + ] + }, + { + "functionName": "_format", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4453, + "startOffset": 4017 + } + ] + }, + { + "functionName": "resolve", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2279, + "endOffset": 9115, + "startOffset": 4579 + }, + { + "count": 3106, + "endOffset": 8648, + "startOffset": 4742 + }, + { + "count": 3105, + "endOffset": 4953, + "startOffset": 4780 + }, + { + "count": 0, + "endOffset": 4944, + "startOffset": 4911 + }, + { + "count": 1, + "endOffset": 5845, + "startOffset": 4953 + }, + { + "count": 0, + "endOffset": 5845, + "startOffset": 5033 + }, + { + "count": 0, + "endOffset": 6218, + "startOffset": 6053 + }, + { + "count": 0, + "endOffset": 7598, + "startOffset": 6251 + }, + { + "count": 2975, + "endOffset": 7689, + "startOffset": 7634 + }, + { + "count": 2279, + "endOffset": 8015, + "startOffset": 7691 + }, + { + "count": 2279, + "endOffset": 8333, + "startOffset": 8048 + }, + { + "count": 0, + "endOffset": 8270, + "startOffset": 8090 + }, + { + "count": 0, + "endOffset": 8433, + "startOffset": 8365 + }, + { + "count": 2279, + "endOffset": 8600, + "startOffset": 8572 + }, + { + "count": 2279, + "endOffset": 8632, + "startOffset": 8602 + }, + { + "count": 0, + "endOffset": 9109, + "startOffset": 9059 + } + ] + }, + { + "functionName": "normalize", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 12023, + "startOffset": 9187 + }, + { + "count": 0, + "endOffset": 9308, + "startOffset": 9297 + }, + { + "count": 0, + "endOffset": 9622, + "startOffset": 9465 + }, + { + "count": 0, + "endOffset": 11204, + "startOffset": 9655 + }, + { + "count": 4, + "endOffset": 11582, + "startOffset": 11278 + }, + { + "count": 0, + "endOffset": 11708, + "startOffset": 11697 + }, + { + "count": 0, + "endOffset": 11751, + "startOffset": 11737 + }, + { + "count": 0, + "endOffset": 11771, + "startOffset": 11760 + }, + { + "count": 0, + "endOffset": 11863, + "startOffset": 11850 + }, + { + "count": 1, + "endOffset": 11950, + "startOffset": 11895 + }, + { + "count": 0, + "endOffset": 11935, + "startOffset": 11922 + }, + { + "count": 4, + "endOffset": 11996, + "startOffset": 11950 + }, + { + "count": 0, + "endOffset": 12017, + "startOffset": 11997 + } + ] + }, + { + "functionName": "isAbsolute", + "isBlockCoverage": true, + "ranges": [ + { + "count": 312, + "endOffset": 12477, + "startOffset": 12096 + }, + { + "count": 0, + "endOffset": 12220, + "startOffset": 12207 + }, + { + "count": 311, + "endOffset": 12380, + "startOffset": 12345 + }, + { + "count": 156, + "endOffset": 12424, + "startOffset": 12381 + }, + { + "count": 112, + "endOffset": 12470, + "startOffset": 12425 + } + ] + }, + { + "functionName": "join", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 14671, + "startOffset": 12552 + }, + { + "count": 0, + "endOffset": 12614, + "startOffset": 12603 + }, + { + "count": 11, + "endOffset": 12926, + "startOffset": 12697 + }, + { + "count": 5, + "endOffset": 12863, + "startOffset": 12838 + }, + { + "count": 6, + "endOffset": 12910, + "startOffset": 12863 + }, + { + "count": 0, + "endOffset": 12978, + "startOffset": 12967 + }, + { + "count": 0, + "endOffset": 14271, + "startOffset": 13851 + }, + { + "count": 0, + "endOffset": 14499, + "startOffset": 14466 + }, + { + "count": 0, + "endOffset": 14620, + "startOffset": 14579 + } + ] + }, + { + "functionName": "relative", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18564, + "startOffset": 14979 + } + ] + }, + { + "functionName": "toNamespacedPath", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1064, + "endOffset": 19630, + "startOffset": 18571 + }, + { + "count": 0, + "endOffset": 18702, + "startOffset": 18690 + }, + { + "count": 0, + "endOffset": 18759, + "startOffset": 18733 + }, + { + "count": 0, + "endOffset": 18865, + "startOffset": 18853 + }, + { + "count": 0, + "endOffset": 19300, + "startOffset": 18929 + }, + { + "count": 0, + "endOffset": 19629, + "startOffset": 19605 + } + ] + }, + { + "functionName": "dirname", + "isBlockCoverage": true, + "ranges": [ + { + "count": 489, + "endOffset": 22114, + "startOffset": 19702 + }, + { + "count": 0, + "endOffset": 19821, + "startOffset": 19810 + }, + { + "count": 0, + "endOffset": 20090, + "startOffset": 19926 + }, + { + "count": 0, + "endOffset": 21455, + "startOffset": 20153 + }, + { + "count": 0, + "endOffset": 21602, + "startOffset": 21599 + }, + { + "count": 5638, + "endOffset": 21973, + "startOffset": 21732 + }, + { + "count": 489, + "endOffset": 21871, + "startOffset": 21782 + }, + { + "count": 5149, + "endOffset": 21966, + "startOffset": 21871 + }, + { + "count": 0, + "endOffset": 22077, + "startOffset": 21997 + } + ] + }, + { + "functionName": "basename", + "isBlockCoverage": true, + "ranges": [ + { + "count": 112, + "endOffset": 24930, + "startOffset": 22214 + }, + { + "count": 0, + "endOffset": 22298, + "startOffset": 22271 + }, + { + "count": 0, + "endOffset": 22778, + "startOffset": 22761 + }, + { + "count": 0, + "endOffset": 22807, + "startOffset": 22779 + }, + { + "count": 0, + "endOffset": 24318, + "startOffset": 22809 + }, + { + "count": 1309, + "endOffset": 24848, + "startOffset": 24371 + }, + { + "count": 112, + "endOffset": 24652, + "startOffset": 24421 + }, + { + "count": 1197, + "endOffset": 24841, + "startOffset": 24652 + }, + { + "count": 112, + "endOffset": 24841, + "startOffset": 24674 + }, + { + "count": 0, + "endOffset": 24889, + "startOffset": 24879 + } + ] + }, + { + "functionName": "extname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27073, + "startOffset": 25002 + } + ] + }, + { + "functionName": "parse", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31903, + "startOffset": 27291 + } + ] + }, + { + "functionName": "resolve", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33025, + "startOffset": 32100 + } + ] + }, + { + "functionName": "normalize", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33694, + "startOffset": 33097 + } + ] + }, + { + "functionName": "isAbsolute", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33899, + "startOffset": 33767 + } + ] + }, + { + "functionName": "join", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 34405, + "startOffset": 33974 + } + ] + }, + { + "functionName": "relative", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36780, + "startOffset": 34502 + } + ] + }, + { + "functionName": "toNamespacedPath", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36866, + "startOffset": 36787 + } + ] + }, + { + "functionName": "dirname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 37590, + "startOffset": 36938 + } + ] + }, + { + "functionName": "basename", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 40083, + "startOffset": 37690 + } + ] + }, + { + "functionName": "extname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 41863, + "startOffset": 40155 + } + ] + }, + { + "functionName": "parse", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 44518, + "startOffset": 42086 + } + ] + } + ], + "scriptId": "33", + "url": "path.js" + } + ], + "timestamp": 967695.999409 + }, + "test_v8_coverage_node_sqlite_9884_1633662346346_0.json": { + "result": [ + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 3134, + "startOffset": 0 + } + ] + }, + { + "functionName": "encodeStr", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3065, + "startOffset": 1374 + } + ] + } + ], + "scriptId": "32", + "url": "internal/querystring.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 44873, + "startOffset": 0 + } + ] + }, + { + "functionName": "isPathSeparator", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2111, + "endOffset": 1635, + "startOffset": 1529 + }, + { + "count": 2108, + "endOffset": 1631, + "startOffset": 1600 + } + ] + }, + { + "functionName": "isPosixPathSeparator", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1718, + "startOffset": 1639 + } + ] + }, + { + "functionName": "isWindowsDeviceRoot", + "isBlockCoverage": true, + "ranges": [ + { + "count": 86, + "endOffset": 1895, + "startOffset": 1722 + }, + { + "count": 81, + "endOffset": 1822, + "startOffset": 1795 + }, + { + "count": 14, + "endOffset": 1891, + "startOffset": 1824 + }, + { + "count": 9, + "endOffset": 1890, + "startOffset": 1863 + } + ] + }, + { + "functionName": "normalizeString", + "isBlockCoverage": true, + "ranges": [ + { + "count": 41, + "endOffset": 3826, + "startOffset": 1961 + }, + { + "count": 1915, + "endOffset": 3808, + "startOffset": 2184 + }, + { + "count": 1874, + "endOffset": 2245, + "startOffset": 2219 + }, + { + "count": 41, + "endOffset": 2340, + "startOffset": 2245 + }, + { + "count": 40, + "endOffset": 2296, + "startOffset": 2290 + }, + { + "count": 1, + "endOffset": 2340, + "startOffset": 2296 + }, + { + "count": 1875, + "endOffset": 2375, + "startOffset": 2340 + }, + { + "count": 209, + "endOffset": 3704, + "startOffset": 2375 + }, + { + "count": 200, + "endOffset": 2421, + "startOffset": 2408 + }, + { + "count": 10, + "endOffset": 2450, + "startOffset": 2423 + }, + { + "count": 199, + "endOffset": 3658, + "startOffset": 2450 + }, + { + "count": 2, + "endOffset": 3439, + "startOffset": 2472 + }, + { + "count": 0, + "endOffset": 2588, + "startOffset": 2529 + }, + { + "count": 0, + "endOffset": 2648, + "startOffset": 2589 + }, + { + "count": 0, + "endOffset": 2868, + "startOffset": 2789 + }, + { + "count": 0, + "endOffset": 3283, + "startOffset": 3108 + }, + { + "count": 0, + "endOffset": 3430, + "startOffset": 3294 + }, + { + "count": 197, + "endOffset": 3658, + "startOffset": 3439 + }, + { + "count": 158, + "endOffset": 3540, + "startOffset": 3487 + }, + { + "count": 39, + "endOffset": 3601, + "startOffset": 3540 + }, + { + "count": 207, + "endOffset": 3704, + "startOffset": 3658 + }, + { + "count": 1666, + "endOffset": 3803, + "startOffset": 3704 + }, + { + "count": 24, + "endOffset": 3746, + "startOffset": 3732 + }, + { + "count": 9, + "endOffset": 3771, + "startOffset": 3748 + }, + { + "count": 1657, + "endOffset": 3803, + "startOffset": 3771 + } + ] + }, + { + "functionName": "_format", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4453, + "startOffset": 4017 + } + ] + }, + { + "functionName": "resolve", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9115, + "startOffset": 4579 + }, + { + "count": 53, + "endOffset": 8648, + "startOffset": 4742 + }, + { + "count": 52, + "endOffset": 4953, + "startOffset": 4780 + }, + { + "count": 0, + "endOffset": 4944, + "startOffset": 4911 + }, + { + "count": 1, + "endOffset": 5845, + "startOffset": 4953 + }, + { + "count": 0, + "endOffset": 5845, + "startOffset": 5033 + }, + { + "count": 0, + "endOffset": 6218, + "startOffset": 6053 + }, + { + "count": 0, + "endOffset": 7598, + "startOffset": 6251 + }, + { + "count": 49, + "endOffset": 7689, + "startOffset": 7634 + }, + { + "count": 40, + "endOffset": 8015, + "startOffset": 7691 + }, + { + "count": 40, + "endOffset": 8333, + "startOffset": 8048 + }, + { + "count": 0, + "endOffset": 8270, + "startOffset": 8090 + }, + { + "count": 0, + "endOffset": 8433, + "startOffset": 8365 + }, + { + "count": 40, + "endOffset": 8600, + "startOffset": 8572 + }, + { + "count": 40, + "endOffset": 8632, + "startOffset": 8602 + }, + { + "count": 0, + "endOffset": 9109, + "startOffset": 9059 + } + ] + }, + { + "functionName": "normalize", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 12023, + "startOffset": 9187 + }, + { + "count": 0, + "endOffset": 9308, + "startOffset": 9297 + }, + { + "count": 0, + "endOffset": 9622, + "startOffset": 9465 + }, + { + "count": 0, + "endOffset": 11204, + "startOffset": 9655 + }, + { + "count": 0, + "endOffset": 11708, + "startOffset": 11697 + }, + { + "count": 0, + "endOffset": 11751, + "startOffset": 11737 + }, + { + "count": 0, + "endOffset": 11771, + "startOffset": 11760 + }, + { + "count": 0, + "endOffset": 11863, + "startOffset": 11850 + }, + { + "count": 0, + "endOffset": 11950, + "startOffset": 11895 + }, + { + "count": 0, + "endOffset": 12017, + "startOffset": 11997 + } + ] + }, + { + "functionName": "isAbsolute", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 12477, + "startOffset": 12096 + }, + { + "count": 0, + "endOffset": 12220, + "startOffset": 12207 + }, + { + "count": 4, + "endOffset": 12424, + "startOffset": 12381 + }, + { + "count": 4, + "endOffset": 12470, + "startOffset": 12425 + } + ] + }, + { + "functionName": "join", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 14671, + "startOffset": 12552 + }, + { + "count": 0, + "endOffset": 12614, + "startOffset": 12603 + }, + { + "count": 2, + "endOffset": 12926, + "startOffset": 12697 + }, + { + "count": 1, + "endOffset": 12910, + "startOffset": 12838 + }, + { + "count": 0, + "endOffset": 12978, + "startOffset": 12967 + }, + { + "count": 0, + "endOffset": 14271, + "startOffset": 13851 + }, + { + "count": 0, + "endOffset": 14499, + "startOffset": 14466 + }, + { + "count": 0, + "endOffset": 14620, + "startOffset": 14579 + } + ] + }, + { + "functionName": "relative", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18564, + "startOffset": 14979 + } + ] + }, + { + "functionName": "toNamespacedPath", + "isBlockCoverage": true, + "ranges": [ + { + "count": 18, + "endOffset": 19630, + "startOffset": 18571 + }, + { + "count": 0, + "endOffset": 18702, + "startOffset": 18690 + }, + { + "count": 0, + "endOffset": 18759, + "startOffset": 18733 + }, + { + "count": 0, + "endOffset": 18865, + "startOffset": 18853 + }, + { + "count": 0, + "endOffset": 19300, + "startOffset": 18929 + }, + { + "count": 0, + "endOffset": 19629, + "startOffset": 19605 + } + ] + }, + { + "functionName": "dirname", + "isBlockCoverage": true, + "ranges": [ + { + "count": 7, + "endOffset": 22114, + "startOffset": 19702 + }, + { + "count": 0, + "endOffset": 19821, + "startOffset": 19810 + }, + { + "count": 0, + "endOffset": 20090, + "startOffset": 19926 + }, + { + "count": 0, + "endOffset": 21455, + "startOffset": 20153 + }, + { + "count": 0, + "endOffset": 21602, + "startOffset": 21599 + }, + { + "count": 57, + "endOffset": 21973, + "startOffset": 21732 + }, + { + "count": 7, + "endOffset": 21871, + "startOffset": 21782 + }, + { + "count": 50, + "endOffset": 21966, + "startOffset": 21871 + }, + { + "count": 0, + "endOffset": 22077, + "startOffset": 21997 + } + ] + }, + { + "functionName": "basename", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 24930, + "startOffset": 22214 + }, + { + "count": 0, + "endOffset": 22298, + "startOffset": 22271 + }, + { + "count": 0, + "endOffset": 22778, + "startOffset": 22761 + }, + { + "count": 0, + "endOffset": 22807, + "startOffset": 22779 + }, + { + "count": 0, + "endOffset": 24318, + "startOffset": 22809 + }, + { + "count": 17, + "endOffset": 24848, + "startOffset": 24371 + }, + { + "count": 2, + "endOffset": 24652, + "startOffset": 24421 + }, + { + "count": 15, + "endOffset": 24841, + "startOffset": 24652 + }, + { + "count": 2, + "endOffset": 24841, + "startOffset": 24674 + }, + { + "count": 0, + "endOffset": 24889, + "startOffset": 24879 + } + ] + }, + { + "functionName": "extname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27073, + "startOffset": 25002 + } + ] + }, + { + "functionName": "parse", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31903, + "startOffset": 27291 + } + ] + }, + { + "functionName": "resolve", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33025, + "startOffset": 32100 + } + ] + }, + { + "functionName": "normalize", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33694, + "startOffset": 33097 + } + ] + }, + { + "functionName": "isAbsolute", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33899, + "startOffset": 33767 + } + ] + }, + { + "functionName": "join", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 34405, + "startOffset": 33974 + } + ] + }, + { + "functionName": "relative", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36780, + "startOffset": 34502 + } + ] + }, + { + "functionName": "toNamespacedPath", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36866, + "startOffset": 36787 + } + ] + }, + { + "functionName": "dirname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 37590, + "startOffset": 36938 + } + ] + }, + { + "functionName": "basename", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 40083, + "startOffset": 37690 + } + ] + }, + { + "functionName": "extname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 41863, + "startOffset": 40155 + } + ] + }, + { + "functionName": "parse", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 44518, + "startOffset": 42086 + } + ] + } + ], + "scriptId": "33", + "url": "path.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 16488, + "startOffset": 0 + } + ] + }, + { + "functionName": "lazyBuffer", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1119, + "startOffset": 1006 + } + ] + }, + { + "functionName": "validateEncoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1251, + "startOffset": 1123 + } + ] + }, + { + "functionName": "validateDecoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1383, + "startOffset": 1255 + } + ] + }, + { + "functionName": "validateArgument", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1601, + "startOffset": 1387 + } + ] + }, + { + "functionName": "trimAsciiWhitespace", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9068, + "startOffset": 8566 + } + ] + }, + { + "functionName": "getEncodingFromLabel", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9255, + "startOffset": 9072 + } + ] + }, + { + "functionName": "TextEncoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9379, + "startOffset": 9331 + } + ] + }, + { + "functionName": "get encoding", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9455, + "startOffset": 9385 + } + ] + }, + { + "functionName": "encode", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9556, + "startOffset": 9461 + } + ] + }, + { + "functionName": "encodeInto", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9879, + "startOffset": 9562 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10291, + "startOffset": 9885 + } + ] + }, + { + "functionName": "makeTextDecoderICU", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 12397, + "startOffset": 10680 + } + ] + }, + { + "functionName": "TextDecoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11574, + "startOffset": 10825 + } + ] + }, + { + "functionName": "decode", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12364, + "startOffset": 11584 + } + ] + }, + { + "functionName": "makeTextDecoderJS", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15025, + "startOffset": 12401 + } + ] + }, + { + "functionName": "get encoding", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15240, + "startOffset": 15156 + } + ] + }, + { + "functionName": "get fatal", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15379, + "startOffset": 15249 + } + ] + }, + { + "functionName": "get ignoreBOM", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15547, + "startOffset": 15388 + } + ] + }, + { + "functionName": "ObjectGetOwnPropertyDescriptors", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16186, + "startOffset": 15556 + } + ] + } + ], + "scriptId": "34", + "url": "internal/encoding.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 8643, + "startOffset": 0 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1882, + "startOffset": 1855 + } + ] + }, + { + "functionName": "unenroll", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3615, + "startOffset": 2396 + } + ] + }, + { + "functionName": "enroll", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4089, + "startOffset": 3827 + } + ] + }, + { + "functionName": "setTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4800, + "startOffset": 4127 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5019, + "startOffset": 4881 + } + ] + }, + { + "functionName": "clearTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5407, + "startOffset": 5028 + } + ] + }, + { + "functionName": "setInterval", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6086, + "startOffset": 5411 + } + ] + }, + { + "functionName": "clearInterval", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6393, + "startOffset": 6090 + } + ] + }, + { + "functionName": "Timeout.close", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6477, + "startOffset": 6423 + } + ] + }, + { + "functionName": "Timeout.", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6689, + "startOffset": 6521 + } + ] + }, + { + "functionName": "setImmediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7259, + "startOffset": 6694 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7482, + "startOffset": 7342 + } + ] + }, + { + "functionName": "clearImmediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7977, + "startOffset": 7493 + } + ] + } + ], + "scriptId": "35", + "url": "timers.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1166, + "startOffset": 0 + } + ] + }, + { + "functionName": "init", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 93, + "startOffset": 17 + } + ] + }, + { + "functionName": "peek", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 221, + "startOffset": 126 + } + ] + }, + { + "functionName": "remove", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 497, + "startOffset": 259 + } + ] + }, + { + "functionName": "append", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1021, + "startOffset": 556 + } + ] + }, + { + "functionName": "isEmpty", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1087, + "startOffset": 1025 + } + ] + } + ], + "scriptId": "36", + "url": "internal/linkedlist.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 19158, + "startOffset": 0 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4543, + "startOffset": 4516 + } + ] + }, + { + "functionName": "initAsyncResource", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5802, + "startOffset": 5518 + } + ] + }, + { + "functionName": "Timeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6918, + "startOffset": 5891 + } + ] + }, + { + "functionName": "Timeout.", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7213, + "startOffset": 7034 + } + ] + }, + { + "functionName": "Timeout.refresh", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7351, + "startOffset": 7246 + } + ] + }, + { + "functionName": "Timeout.unref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7517, + "startOffset": 7382 + } + ] + }, + { + "functionName": "Timeout.ref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7681, + "startOffset": 7546 + } + ] + }, + { + "functionName": "Timeout.hasRef", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7752, + "startOffset": 7713 + } + ] + }, + { + "functionName": "TimersList", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8064, + "startOffset": 7757 + } + ] + }, + { + "functionName": "TimersList.", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8362, + "startOffset": 8183 + } + ] + }, + { + "functionName": "ImmediateList", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 8494, + "startOffset": 8423 + } + ] + }, + { + "functionName": "ImmediateList.append", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8852, + "startOffset": 8677 + } + ] + }, + { + "functionName": "ImmediateList.remove", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9387, + "startOffset": 9034 + } + ] + }, + { + "functionName": "incRefCount", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9471, + "startOffset": 9392 + } + ] + }, + { + "functionName": "decRefCount", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9555, + "startOffset": 9475 + } + ] + }, + { + "functionName": "active", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9698, + "startOffset": 9642 + } + ] + }, + { + "functionName": "unrefActive", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9911, + "startOffset": 9849 + } + ] + }, + { + "functionName": "insertGuarded", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10677, + "startOffset": 10138 + } + ] + }, + { + "functionName": "insert", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11352, + "startOffset": 10681 + } + ] + }, + { + "functionName": "setUnrefTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11672, + "startOffset": 11356 + } + ] + }, + { + "functionName": "getTimerDuration", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12279, + "startOffset": 11742 + } + ] + }, + { + "functionName": "compareTimersLists", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12497, + "startOffset": 12283 + } + ] + }, + { + "functionName": "setPosition", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12574, + "startOffset": 12501 + } + ] + }, + { + "functionName": "getTimerCallbacks", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 17857, + "startOffset": 12578 + } + ] + }, + { + "functionName": "processImmediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14758, + "startOffset": 12896 + } + ] + }, + { + "functionName": "processTimers", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15258, + "startOffset": 14766 + } + ] + }, + { + "functionName": "listOnTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17792, + "startOffset": 15264 + } + ] + }, + { + "functionName": "Immediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18218, + "startOffset": 17882 + } + ] + }, + { + "functionName": "ref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18404, + "startOffset": 18224 + } + ] + }, + { + "functionName": "unref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18593, + "startOffset": 18410 + } + ] + }, + { + "functionName": "hasRef", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18642, + "startOffset": 18599 + } + ] + } + ], + "scriptId": "37", + "url": "internal/timers.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 3052, + "startOffset": 0 + } + ] + }, + { + "functionName": "PriorityQueue", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 838, + "startOffset": 589 + } + ] + }, + { + "functionName": "module.exports", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 886, + "startOffset": 844 + } + ] + }, + { + "functionName": "insert", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1086, + "startOffset": 892 + } + ] + }, + { + "functionName": "peek", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1133, + "startOffset": 1092 + } + ] + }, + { + "functionName": "percolateDown", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1829, + "startOffset": 1139 + } + ] + }, + { + "functionName": "percolateUp", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2344, + "startOffset": 1835 + } + ] + }, + { + "functionName": "removeAt", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2695, + "startOffset": 2350 + } + ] + }, + { + "functionName": "remove", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2876, + "startOffset": 2701 + } + ] + }, + { + "functionName": "shift", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3046, + "startOffset": 2882 + } + ] + } + ], + "scriptId": "38", + "url": "internal/priority_queue.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 2918, + "startOffset": 0 + } + ] + }, + { + "functionName": "initializeDebugEnv", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 904, + "startOffset": 521 + }, + { + "count": 0, + "endOffset": 819, + "startOffset": 614 + } + ] + }, + { + "functionName": "emitWarningIfNeeded", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1314, + "startOffset": 982 + } + ] + }, + { + "functionName": "noop", + "isBlockCoverage": true, + "ranges": [ + { + "count": 13, + "endOffset": 1336, + "startOffset": 1318 + } + ] + }, + { + "functionName": "debuglogImpl", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 1921, + "startOffset": 1340 + }, + { + "count": 2, + "endOffset": 1891, + "startOffset": 1416 + }, + { + "count": 0, + "endOffset": 1841, + "startOffset": 1436 + } + ] + }, + { + "functionName": "debug", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1833, + "startOffset": 1528 + } + ] + }, + { + "functionName": "debuglog", + "isBlockCoverage": true, + "ranges": [ + { + "count": 12, + "endOffset": 2855, + "startOffset": 2147 + } + ] + }, + { + "functionName": "init", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 2278, + "startOffset": 2179 + } + ] + }, + { + "functionName": "debug", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 2539, + "startOffset": 2294 + } + ] + }, + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2644, + "startOffset": 2571 + } + ] + }, + { + "functionName": "logger", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 2691, + "startOffset": 2664 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2779, + "startOffset": 2743 + } + ] + } + ], + "scriptId": "39", + "url": "internal/util/debuglog.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 7161, + "startOffset": 0 + } + ] + }, + { + "functionName": "tryGetCwd", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 890, + "startOffset": 551 + }, + { + "count": 0, + "endOffset": 887, + "startOffset": 615 + } + ] + }, + { + "functionName": "evalModule", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1347, + "startOffset": 894 + } + ] + }, + { + "functionName": "evalScript", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2774, + "startOffset": 1351 + } + ] + }, + { + "functionName": "setUncaughtExceptionCaptureCallback", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3570, + "startOffset": 2858 + } + ] + }, + { + "functionName": "hasUncaughtExceptionCaptureCallback", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3677, + "startOffset": 3574 + } + ] + }, + { + "functionName": "noop", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3699, + "startOffset": 3681 + } + ] + }, + { + "functionName": "createOnGlobalUncaughtException", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 6711, + "startOffset": 4252 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6707, + "startOffset": 4525 + } + ] + }, + { + "functionName": "readStdin", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6931, + "startOffset": 6715 + } + ] + } + ], + "scriptId": "40", + "url": "internal/process/execution.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 5002, + "startOffset": 0 + } + ] + }, + { + "functionName": "lazyOption", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 864, + "startOffset": 295 + } + ] + }, + { + "functionName": "writeOut", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1097, + "startOffset": 970 + } + ] + }, + { + "functionName": "writeToFile", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1504, + "startOffset": 1101 + } + ] + }, + { + "functionName": "doEmitWarning", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1581, + "startOffset": 1508 + } + ] + }, + { + "functionName": "onWarning", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2833, + "startOffset": 1623 + } + ] + }, + { + "functionName": "emitWarning", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4139, + "startOffset": 2961 + } + ] + }, + { + "functionName": "emitWarningSync", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4239, + "startOffset": 4143 + } + ] + }, + { + "functionName": "createWarningObject", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4924, + "startOffset": 4243 + } + ] + } + ], + "scriptId": "41", + "url": "internal/process/warning.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 6632, + "startOffset": 0 + } + ] + }, + { + "functionName": "process._startProfilerIdleNotifier", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 533, + "startOffset": 525 + } + ] + }, + { + "functionName": "process._stopProfilerIdleNotifier", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 580, + "startOffset": 572 + } + ] + }, + { + "functionName": "defineStream", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 738, + "startOffset": 585 + } + ] + }, + { + "functionName": "createWritableStdioStream", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2943, + "startOffset": 1318 + } + ] + }, + { + "functionName": "dummyDestroy", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3343, + "startOffset": 2947 + } + ] + }, + { + "functionName": "getStdout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3728, + "startOffset": 3387 + } + ] + }, + { + "functionName": "getStderr", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4073, + "startOffset": 3732 + } + ] + }, + { + "functionName": "getStdin", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6480, + "startOffset": 4077 + } + ] + }, + { + "functionName": "rawMethods.resetStdioForTesting", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6629, + "startOffset": 6546 + } + ] + } + ], + "scriptId": "42", + "url": "internal/bootstrap/switches/is_main_thread.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1177, + "startOffset": 0 + } + ] + }, + { + "functionName": "isSignal", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 318, + "startOffset": 220 + } + ] + }, + { + "functionName": "startListeningIfSignal", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 892, + "startOffset": 385 + }, + { + "count": 1, + "endOffset": 472, + "startOffset": 447 + }, + { + "count": 1, + "endOffset": 889, + "startOffset": 474 + }, + { + "count": 0, + "endOffset": 848, + "startOffset": 766 + } + ] + }, + { + "functionName": "stopListeningIfSignal", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1097, + "startOffset": 896 + } + ] + } + ], + "scriptId": "43", + "url": "internal/process/signal.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 3655, + "startOffset": 0 + }, + { + "count": 0, + "endOffset": 598, + "startOffset": 298 + } + ] + }, + { + "functionName": "wrapPosixCredentialSetters", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3064, + "startOffset": 846 + } + ] + }, + { + "functionName": "wrappedChdir", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3397, + "startOffset": 3221 + } + ] + }, + { + "functionName": "wrappedUmask", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3542, + "startOffset": 3401 + } + ] + }, + { + "functionName": "wrappedCwd", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 3653, + "startOffset": 3546 + }, + { + "count": 1, + "endOffset": 3629, + "startOffset": 3600 + } + ] + } + ], + "scriptId": "44", + "url": "internal/bootstrap/switches/does_own_process_state.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 649, + "startOffset": 0 + } + ] + } + ], + "scriptId": "45", + "url": "internal/main/run_main_module.js" + } + ], + "timestamp": 967709.209879 + }, + "test_v8_coverage_node_sqlite_merged.json": { + "result": [ + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 3655, + "startOffset": 0 + }, + { + "count": 0, + "endOffset": 598, + "startOffset": 298 + } + ] + }, + { + "functionName": "wrapPosixCredentialSetters", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3064, + "startOffset": 846 + } + ] + }, + { + "functionName": "wrappedChdir", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3397, + "startOffset": 3221 + } + ] + }, + { + "functionName": "wrappedUmask", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3542, + "startOffset": 3401 + } + ] + }, + { + "functionName": "wrappedCwd", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 3653, + "startOffset": 3546 + }, + { + "count": 1, + "endOffset": 3629, + "startOffset": 3600 + } + ] + } + ], + "scriptId": "0", + "url": "internal/bootstrap/switches/does_own_process_state.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 6632, + "startOffset": 0 + } + ] + }, + { + "functionName": "process._startProfilerIdleNotifier", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 533, + "startOffset": 525 + } + ] + }, + { + "functionName": "process._stopProfilerIdleNotifier", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 580, + "startOffset": 572 + } + ] + }, + { + "functionName": "defineStream", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 738, + "startOffset": 585 + } + ] + }, + { + "functionName": "createWritableStdioStream", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2943, + "startOffset": 1318 + } + ] + }, + { + "functionName": "dummyDestroy", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3343, + "startOffset": 2947 + } + ] + }, + { + "functionName": "getStdout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3728, + "startOffset": 3387 + } + ] + }, + { + "functionName": "getStderr", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4073, + "startOffset": 3732 + } + ] + }, + { + "functionName": "getStdin", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6480, + "startOffset": 4077 + } + ] + }, + { + "functionName": "rawMethods.resetStdioForTesting", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6629, + "startOffset": 6546 + } + ] + } + ], + "scriptId": "1", + "url": "internal/bootstrap/switches/is_main_thread.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 20604, + "startOffset": 0 + } + ] + }, + { + "functionName": "Console", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4925, + "startOffset": 2686 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5200, + "startOffset": 5144 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5669, + "startOffset": 5458 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 6489, + "startOffset": 5878 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 6165, + "startOffset": 6067 + }, + { + "count": 1, + "endOffset": 6124, + "startOffset": 6101 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6208, + "startOffset": 6178 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6417, + "startOffset": 6315 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6460, + "startOffset": 6430 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 7776, + "startOffset": 6563 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9334, + "startOffset": 7850 + }, + { + "count": 0, + "endOffset": 8095, + "startOffset": 8081 + }, + { + "count": 0, + "endOffset": 8197, + "startOffset": 8171 + }, + { + "count": 0, + "endOffset": 8432, + "startOffset": 8238 + }, + { + "count": 0, + "endOffset": 8521, + "startOffset": 8493 + }, + { + "count": 0, + "endOffset": 9261, + "startOffset": 8971 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9918, + "startOffset": 9411 + }, + { + "count": 0, + "endOffset": 9635, + "startOffset": 9629 + }, + { + "count": 0, + "endOffset": 9840, + "startOffset": 9714 + }, + { + "count": 0, + "endOffset": 9910, + "startOffset": 9886 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 10124, + "startOffset": 9993 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10330, + "startOffset": 10199 + } + ] + }, + { + "functionName": "createWriteErrorHandler", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 11331, + "startOffset": 10424 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 11327, + "startOffset": 10493 + }, + { + "count": 0, + "endOffset": 10786, + "startOffset": 10768 + } + ] + }, + { + "functionName": "log", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 11452, + "startOffset": 11363 + } + ] + }, + { + "functionName": "warn", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11551, + "startOffset": 11461 + } + ] + }, + { + "functionName": "dir", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11751, + "startOffset": 11560 + } + ] + }, + { + "functionName": "time", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12125, + "startOffset": 11758 + } + ] + }, + { + "functionName": "timeEnd", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12429, + "startOffset": 12132 + } + ] + }, + { + "functionName": "timeLog", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12679, + "startOffset": 12436 + } + ] + }, + { + "functionName": "trace", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12886, + "startOffset": 12693 + } + ] + }, + { + "functionName": "assert", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 13116, + "startOffset": 12893 + } + ] + }, + { + "functionName": "clear", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 13620, + "startOffset": 13180 + } + ] + }, + { + "functionName": "count", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14153, + "startOffset": 13684 + } + ] + }, + { + "functionName": "countReset", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14518, + "startOffset": 14222 + } + ] + }, + { + "functionName": "group", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14699, + "startOffset": 14525 + } + ] + }, + { + "functionName": "groupEnd", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14880, + "startOffset": 14706 + } + ] + }, + { + "functionName": "table", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18454, + "startOffset": 14932 + } + ] + }, + { + "functionName": "timeLogImpl", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19012, + "startOffset": 18499 + } + ] + }, + { + "functionName": "pad", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19095, + "startOffset": 19016 + } + ] + }, + { + "functionName": "formatTime", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19892, + "startOffset": 19099 + } + ] + }, + { + "functionName": "isArray", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20089, + "startOffset": 20033 + } + ] + }, + { + "functionName": "noop", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20112, + "startOffset": 20094 + } + ] + } + ], + "scriptId": "2", + "url": "internal/console/constructor.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1638, + "startOffset": 0 + } + ] + } + ], + "scriptId": "3", + "url": "internal/constants.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 16488, + "startOffset": 0 + } + ] + }, + { + "functionName": "lazyBuffer", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1119, + "startOffset": 1006 + } + ] + }, + { + "functionName": "validateEncoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1251, + "startOffset": 1123 + } + ] + }, + { + "functionName": "validateDecoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1383, + "startOffset": 1255 + } + ] + }, + { + "functionName": "validateArgument", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1601, + "startOffset": 1387 + } + ] + }, + { + "functionName": "trimAsciiWhitespace", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9068, + "startOffset": 8566 + } + ] + }, + { + "functionName": "getEncodingFromLabel", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9255, + "startOffset": 9072 + } + ] + }, + { + "functionName": "TextEncoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9379, + "startOffset": 9331 + } + ] + }, + { + "functionName": "get encoding", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9455, + "startOffset": 9385 + } + ] + }, + { + "functionName": "encode", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9556, + "startOffset": 9461 + } + ] + }, + { + "functionName": "encodeInto", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9879, + "startOffset": 9562 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10291, + "startOffset": 9885 + } + ] + }, + { + "functionName": "makeTextDecoderICU", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 12397, + "startOffset": 10680 + } + ] + }, + { + "functionName": "TextDecoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11574, + "startOffset": 10825 + } + ] + }, + { + "functionName": "decode", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12364, + "startOffset": 11584 + } + ] + }, + { + "functionName": "makeTextDecoderJS", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15025, + "startOffset": 12401 + } + ] + }, + { + "functionName": "get encoding", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15240, + "startOffset": 15156 + } + ] + }, + { + "functionName": "get fatal", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15379, + "startOffset": 15249 + } + ] + }, + { + "functionName": "get ignoreBOM", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15547, + "startOffset": 15388 + } + ] + }, + { + "functionName": "ObjectGetOwnPropertyDescriptors", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16186, + "startOffset": 15556 + } + ] + } + ], + "scriptId": "4", + "url": "internal/encoding.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1166, + "startOffset": 0 + } + ] + }, + { + "functionName": "init", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 93, + "startOffset": 17 + } + ] + }, + { + "functionName": "peek", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 221, + "startOffset": 126 + } + ] + }, + { + "functionName": "remove", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 497, + "startOffset": 259 + } + ] + }, + { + "functionName": "append", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1021, + "startOffset": 556 + } + ] + }, + { + "functionName": "isEmpty", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1087, + "startOffset": 1025 + } + ] + } + ], + "scriptId": "5", + "url": "internal/linkedlist.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 649, + "startOffset": 0 + } + ] + } + ], + "scriptId": "6", + "url": "internal/main/run_main_module.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 3052, + "startOffset": 0 + } + ] + }, + { + "functionName": "PriorityQueue", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 838, + "startOffset": 589 + } + ] + }, + { + "functionName": "module.exports", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 886, + "startOffset": 844 + } + ] + }, + { + "functionName": "insert", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1086, + "startOffset": 892 + } + ] + }, + { + "functionName": "peek", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1133, + "startOffset": 1092 + } + ] + }, + { + "functionName": "percolateDown", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1829, + "startOffset": 1139 + } + ] + }, + { + "functionName": "percolateUp", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2344, + "startOffset": 1835 + } + ] + }, + { + "functionName": "removeAt", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2695, + "startOffset": 2350 + } + ] + }, + { + "functionName": "remove", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2876, + "startOffset": 2701 + } + ] + }, + { + "functionName": "shift", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3046, + "startOffset": 2882 + } + ] + } + ], + "scriptId": "7", + "url": "internal/priority_queue.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 7161, + "startOffset": 0 + } + ] + }, + { + "functionName": "tryGetCwd", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 890, + "startOffset": 551 + }, + { + "count": 0, + "endOffset": 887, + "startOffset": 615 + } + ] + }, + { + "functionName": "evalModule", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1347, + "startOffset": 894 + } + ] + }, + { + "functionName": "evalScript", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2774, + "startOffset": 1351 + } + ] + }, + { + "functionName": "setUncaughtExceptionCaptureCallback", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3570, + "startOffset": 2858 + } + ] + }, + { + "functionName": "hasUncaughtExceptionCaptureCallback", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3677, + "startOffset": 3574 + } + ] + }, + { + "functionName": "noop", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3699, + "startOffset": 3681 + } + ] + }, + { + "functionName": "createOnGlobalUncaughtException", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 6711, + "startOffset": 4252 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6707, + "startOffset": 4525 + } + ] + }, + { + "functionName": "readStdin", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6931, + "startOffset": 6715 + } + ] + } + ], + "scriptId": "8", + "url": "internal/process/execution.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1177, + "startOffset": 0 + } + ] + }, + { + "functionName": "isSignal", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 318, + "startOffset": 220 + } + ] + }, + { + "functionName": "startListeningIfSignal", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 892, + "startOffset": 385 + }, + { + "count": 1, + "endOffset": 472, + "startOffset": 447 + }, + { + "count": 1, + "endOffset": 889, + "startOffset": 474 + }, + { + "count": 0, + "endOffset": 848, + "startOffset": 766 + } + ] + }, + { + "functionName": "stopListeningIfSignal", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1097, + "startOffset": 896 + } + ] + } + ], + "scriptId": "9", + "url": "internal/process/signal.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 5002, + "startOffset": 0 + } + ] + }, + { + "functionName": "lazyOption", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 864, + "startOffset": 295 + } + ] + }, + { + "functionName": "writeOut", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1097, + "startOffset": 970 + } + ] + }, + { + "functionName": "writeToFile", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1504, + "startOffset": 1101 + } + ] + }, + { + "functionName": "doEmitWarning", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1581, + "startOffset": 1508 + } + ] + }, + { + "functionName": "onWarning", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2833, + "startOffset": 1623 + } + ] + }, + { + "functionName": "emitWarning", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4139, + "startOffset": 2961 + } + ] + }, + { + "functionName": "emitWarningSync", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4239, + "startOffset": 4143 + } + ] + }, + { + "functionName": "createWarningObject", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4924, + "startOffset": 4243 + } + ] + } + ], + "scriptId": "10", + "url": "internal/process/warning.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 2, + "endOffset": 3134, + "startOffset": 0 + } + ] + }, + { + "functionName": "encodeStr", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3065, + "startOffset": 1374 + } + ] + } + ], + "scriptId": "11", + "url": "internal/querystring.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 19158, + "startOffset": 0 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4543, + "startOffset": 4516 + } + ] + }, + { + "functionName": "initAsyncResource", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5802, + "startOffset": 5518 + } + ] + }, + { + "functionName": "Timeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6918, + "startOffset": 5891 + } + ] + }, + { + "functionName": "Timeout.", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7213, + "startOffset": 7034 + } + ] + }, + { + "functionName": "Timeout.refresh", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7351, + "startOffset": 7246 + } + ] + }, + { + "functionName": "Timeout.unref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7517, + "startOffset": 7382 + } + ] + }, + { + "functionName": "Timeout.ref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7681, + "startOffset": 7546 + } + ] + }, + { + "functionName": "Timeout.hasRef", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7752, + "startOffset": 7713 + } + ] + }, + { + "functionName": "TimersList", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8064, + "startOffset": 7757 + } + ] + }, + { + "functionName": "TimersList.", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8362, + "startOffset": 8183 + } + ] + }, + { + "functionName": "ImmediateList", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 8494, + "startOffset": 8423 + } + ] + }, + { + "functionName": "ImmediateList.append", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8852, + "startOffset": 8677 + } + ] + }, + { + "functionName": "ImmediateList.remove", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9387, + "startOffset": 9034 + } + ] + }, + { + "functionName": "incRefCount", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9471, + "startOffset": 9392 + } + ] + }, + { + "functionName": "decRefCount", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9555, + "startOffset": 9475 + } + ] + }, + { + "functionName": "active", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9698, + "startOffset": 9642 + } + ] + }, + { + "functionName": "unrefActive", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9911, + "startOffset": 9849 + } + ] + }, + { + "functionName": "insertGuarded", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10677, + "startOffset": 10138 + } + ] + }, + { + "functionName": "insert", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11352, + "startOffset": 10681 + } + ] + }, + { + "functionName": "setUnrefTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11672, + "startOffset": 11356 + } + ] + }, + { + "functionName": "getTimerDuration", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12279, + "startOffset": 11742 + } + ] + }, + { + "functionName": "compareTimersLists", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12497, + "startOffset": 12283 + } + ] + }, + { + "functionName": "setPosition", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12574, + "startOffset": 12501 + } + ] + }, + { + "functionName": "getTimerCallbacks", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 17857, + "startOffset": 12578 + } + ] + }, + { + "functionName": "processImmediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14758, + "startOffset": 12896 + } + ] + }, + { + "functionName": "processTimers", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15258, + "startOffset": 14766 + } + ] + }, + { + "functionName": "listOnTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17792, + "startOffset": 15264 + } + ] + }, + { + "functionName": "Immediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18218, + "startOffset": 17882 + } + ] + }, + { + "functionName": "ref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18404, + "startOffset": 18224 + } + ] + }, + { + "functionName": "unref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18593, + "startOffset": 18410 + } + ] + }, + { + "functionName": "hasRef", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18642, + "startOffset": 18599 + } + ] + } + ], + "scriptId": "12", + "url": "internal/timers.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 42795, + "startOffset": 0 + } + ] + }, + { + "functionName": "toUSVString", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2627, + "startOffset": 2323 + } + ] + }, + { + "functionName": "serializeTupleOrigin", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2965, + "startOffset": 2845 + } + ] + }, + { + "functionName": "URLContext", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 3611, + "startOffset": 3378 + } + ] + }, + { + "functionName": "URLSearchParams", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 6333, + "startOffset": 3909 + }, + { + "count": 0, + "endOffset": 6267, + "startOffset": 4027 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7439, + "startOffset": 6339 + } + ] + }, + { + "functionName": "onParseComplete", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 8156, + "startOffset": 7446 + }, + { + "count": 0, + "endOffset": 7716, + "startOffset": 7706 + }, + { + "count": 0, + "endOffset": 7790, + "startOffset": 7780 + }, + { + "count": 0, + "endOffset": 7877, + "startOffset": 7873 + } + ] + }, + { + "functionName": "onParseError", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8237, + "startOffset": 8160 + } + ] + }, + { + "functionName": "onParseProtocolComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8596, + "startOffset": 8241 + } + ] + }, + { + "functionName": "onParseHostnameComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8956, + "startOffset": 8600 + } + ] + }, + { + "functionName": "onParsePortComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9125, + "startOffset": 8960 + } + ] + }, + { + "functionName": "onParseHostComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9440, + "startOffset": 9129 + } + ] + }, + { + "functionName": "onParsePathComplete", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 9954, + "startOffset": 9444 + }, + { + "count": 0, + "endOffset": 9787, + "startOffset": 9716 + } + ] + }, + { + "functionName": "onParseSearchComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10129, + "startOffset": 9958 + } + ] + }, + { + "functionName": "onParseHashComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10306, + "startOffset": 10133 + } + ] + }, + { + "functionName": "URL", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 10663, + "startOffset": 10325 + }, + { + "count": 0, + "endOffset": 10518, + "startOffset": 10464 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10752, + "startOffset": 10669 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 327, + "endOffset": 10853, + "startOffset": 10758 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11136, + "startOffset": 10931 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12107, + "startOffset": 11142 + } + ] + }, + { + "functionName": "format", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 13502, + "startOffset": 12284 + }, + { + "count": 0, + "endOffset": 12432, + "startOffset": 12371 + }, + { + "count": 0, + "endOffset": 13023, + "startOffset": 12856 + }, + { + "count": 0, + "endOffset": 13094, + "startOffset": 13056 + }, + { + "count": 0, + "endOffset": 13172, + "startOffset": 13150 + }, + { + "count": 0, + "endOffset": 13247, + "startOffset": 13181 + }, + { + "count": 0, + "endOffset": 13386, + "startOffset": 13363 + }, + { + "count": 0, + "endOffset": 13476, + "startOffset": 13450 + } + ] + }, + { + "functionName": "toString", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 13849, + "startOffset": 13788 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 13967, + "startOffset": 13920 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14159, + "startOffset": 13974 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14895, + "startOffset": 14245 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15020, + "startOffset": 14970 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15408, + "startOffset": 15027 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15535, + "startOffset": 15483 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15953, + "startOffset": 15542 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16080, + "startOffset": 16028 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16498, + "startOffset": 16087 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16731, + "startOffset": 16569 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17038, + "startOffset": 16738 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17167, + "startOffset": 17113 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17482, + "startOffset": 17174 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17656, + "startOffset": 17553 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17991, + "startOffset": 17663 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": true, + "ranges": [ + { + "count": 218, + "endOffset": 18268, + "startOffset": 18066 + }, + { + "count": 0, + "endOffset": 18167, + "startOffset": 18148 + }, + { + "count": 0, + "endOffset": 18221, + "startOffset": 18211 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 18501, + "startOffset": 18275 + }, + { + "count": 0, + "endOffset": 18396, + "startOffset": 18389 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18718, + "startOffset": 18574 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19232, + "startOffset": 18725 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19372, + "startOffset": 19324 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19599, + "startOffset": 19443 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20037, + "startOffset": 19606 + } + ] + }, + { + "functionName": "toJSON", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20249, + "startOffset": 20190 + } + ] + }, + { + "functionName": "update", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20581, + "startOffset": 20263 + } + ] + }, + { + "functionName": "initSearchParams", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 20731, + "startOffset": 20585 + }, + { + "count": 0, + "endOffset": 20730, + "startOffset": 20686 + } + ] + }, + { + "functionName": "parseParams", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 23241, + "startOffset": 20844 + } + ] + }, + { + "functionName": "serializeParams", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 24820, + "startOffset": 24244 + } + ] + }, + { + "functionName": "defineIDLClass", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 25592, + "startOffset": 24878 + }, + { + "count": 13, + "endOffset": 25379, + "startOffset": 25226 + }, + { + "count": 1, + "endOffset": 25589, + "startOffset": 25435 + } + ] + }, + { + "functionName": "merge", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 26271, + "startOffset": 25615 + } + ] + }, + { + "functionName": "append", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 26740, + "startOffset": 26341 + } + ] + }, + { + "functionName": "delete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27265, + "startOffset": 26747 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27722, + "startOffset": 27272 + } + ] + }, + { + "functionName": "getAll", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 28214, + "startOffset": 27729 + } + ] + }, + { + "functionName": "has", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 28665, + "startOffset": 28221 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 29736, + "startOffset": 28672 + } + ] + }, + { + "functionName": "sort", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 30962, + "startOffset": 29743 + } + ] + }, + { + "functionName": "entries", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31345, + "startOffset": 31130 + } + ] + }, + { + "functionName": "forEach", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31943, + "startOffset": 31352 + } + ] + }, + { + "functionName": "keys", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 32207, + "startOffset": 32001 + } + ] + }, + { + "functionName": "values", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 32424, + "startOffset": 32214 + } + ] + }, + { + "functionName": "toString", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 32767, + "startOffset": 32561 + } + ] + }, + { + "functionName": "createSearchParamsIterator", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33269, + "startOffset": 33058 + } + ] + }, + { + "functionName": "next", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 34289, + "startOffset": 33499 + } + ] + }, + { + "functionName": "defineIDLClass", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 35544, + "startOffset": 34294 + } + ] + }, + { + "functionName": "domainToASCII", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 35734, + "startOffset": 35553 + } + ] + }, + { + "functionName": "domainToUnicode", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 35923, + "startOffset": 35738 + } + ] + }, + { + "functionName": "urlToOptions", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36633, + "startOffset": 36071 + } + ] + }, + { + "functionName": "getPathFromURLWin32", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 38099, + "startOffset": 36673 + } + ] + }, + { + "functionName": "getPathFromURLPosix", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 38623, + "startOffset": 38103 + } + ] + }, + { + "functionName": "fileURLToPath", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 38982, + "startOffset": 38627 + } + ] + }, + { + "functionName": "encodePathChars", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 40330, + "startOffset": 39761 + }, + { + "count": 0, + "endOffset": 39883, + "startOffset": 39834 + }, + { + "count": 0, + "endOffset": 39985, + "startOffset": 39959 + }, + { + "count": 0, + "endOffset": 40043, + "startOffset": 39992 + }, + { + "count": 0, + "endOffset": 40130, + "startOffset": 40081 + }, + { + "count": 0, + "endOffset": 40224, + "startOffset": 40168 + }, + { + "count": 0, + "endOffset": 40307, + "startOffset": 40262 + } + ] + }, + { + "functionName": "pathToFileURL", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 41482, + "startOffset": 40334 + }, + { + "count": 0, + "endOffset": 41025, + "startOffset": 40456 + }, + { + "count": 0, + "endOffset": 41381, + "startOffset": 41327 + }, + { + "count": 0, + "endOffset": 41406, + "startOffset": 41390 + } + ] + }, + { + "functionName": "isURLInstance", + "isBlockCoverage": true, + "ranges": [ + { + "count": 277, + "endOffset": 41607, + "startOffset": 41486 + }, + { + "count": 0, + "endOffset": 41603, + "startOffset": 41580 + } + ] + }, + { + "functionName": "toPathIfFileURL", + "isBlockCoverage": true, + "ranges": [ + { + "count": 277, + "endOffset": 41760, + "startOffset": 41611 + }, + { + "count": 0, + "endOffset": 41759, + "startOffset": 41717 + } + ] + }, + { + "functionName": "constructUrl", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 42484, + "startOffset": 41764 + } + ] + } + ], + "scriptId": "13", + "url": "internal/url.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 2918, + "startOffset": 0 + } + ] + }, + { + "functionName": "initializeDebugEnv", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 904, + "startOffset": 521 + }, + { + "count": 0, + "endOffset": 819, + "startOffset": 614 + } + ] + }, + { + "functionName": "emitWarningIfNeeded", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1314, + "startOffset": 982 + } + ] + }, + { + "functionName": "noop", + "isBlockCoverage": true, + "ranges": [ + { + "count": 13, + "endOffset": 1336, + "startOffset": 1318 + } + ] + }, + { + "functionName": "debuglogImpl", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 1921, + "startOffset": 1340 + }, + { + "count": 2, + "endOffset": 1891, + "startOffset": 1416 + }, + { + "count": 0, + "endOffset": 1841, + "startOffset": 1436 + } + ] + }, + { + "functionName": "debug", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1833, + "startOffset": 1528 + } + ] + }, + { + "functionName": "debuglog", + "isBlockCoverage": true, + "ranges": [ + { + "count": 12, + "endOffset": 2855, + "startOffset": 2147 + } + ] + }, + { + "functionName": "init", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 2278, + "startOffset": 2179 + } + ] + }, + { + "functionName": "debug", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 2539, + "startOffset": 2294 + } + ] + }, + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2644, + "startOffset": 2571 + } + ] + }, + { + "functionName": "logger", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 2691, + "startOffset": 2664 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2779, + "startOffset": 2743 + } + ] + } + ], + "scriptId": "14", + "url": "internal/util/debuglog.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 2283, + "startOffset": 0 + } + ] + }, + { + "functionName": "sendInspectorCommand", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 453, + "startOffset": 100 + } + ] + }, + { + "functionName": "installConsoleExtensions", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1094, + "startOffset": 530 + } + ] + }, + { + "functionName": "wrapConsole", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 1984, + "startOffset": 1176 + }, + { + "count": 23, + "endOffset": 1981, + "startOffset": 1336 + }, + { + "count": 19, + "endOffset": 1855, + "startOffset": 1555 + }, + { + "count": 4, + "endOffset": 1976, + "startOffset": 1855 + } + ] + }, + { + "functionName": "get consoleFromVM", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2217, + "startOffset": 2164 + } + ] + }, + { + "functionName": "set consoleFromVM", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 2277, + "startOffset": 2222 + } + ] + } + ], + "scriptId": "15", + "url": "internal/util/inspector.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 2, + "endOffset": 44873, + "startOffset": 0 + } + ] + }, + { + "functionName": "isPathSeparator", + "isBlockCoverage": true, + "ranges": [ + { + "count": 146987, + "endOffset": 1635, + "startOffset": 1529 + }, + { + "count": 146754, + "endOffset": 1631, + "startOffset": 1600 + } + ] + }, + { + "functionName": "isPosixPathSeparator", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1718, + "startOffset": 1639 + } + ] + }, + { + "functionName": "isWindowsDeviceRoot", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5173, + "endOffset": 1895, + "startOffset": 1722 + }, + { + "count": 4882, + "endOffset": 1822, + "startOffset": 1795 + }, + { + "count": 1041, + "endOffset": 1891, + "startOffset": 1824 + }, + { + "count": 750, + "endOffset": 1890, + "startOffset": 1863 + } + ] + }, + { + "functionName": "normalizeString", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2325, + "endOffset": 3826, + "startOffset": 1961 + }, + { + "count": 133028, + "endOffset": 3808, + "startOffset": 2184 + }, + { + "count": 130703, + "endOffset": 2245, + "startOffset": 2219 + }, + { + "count": 2325, + "endOffset": 2340, + "startOffset": 2245 + }, + { + "count": 2319, + "endOffset": 2296, + "startOffset": 2290 + }, + { + "count": 6, + "endOffset": 2340, + "startOffset": 2296 + }, + { + "count": 130709, + "endOffset": 2375, + "startOffset": 2340 + }, + { + "count": 12477, + "endOffset": 3704, + "startOffset": 2375 + }, + { + "count": 11844, + "endOffset": 2421, + "startOffset": 2408 + }, + { + "count": 734, + "endOffset": 2450, + "startOffset": 2423 + }, + { + "count": 11743, + "endOffset": 3658, + "startOffset": 2450 + }, + { + "count": 33, + "endOffset": 3439, + "startOffset": 2472 + }, + { + "count": 0, + "endOffset": 2588, + "startOffset": 2529 + }, + { + "count": 0, + "endOffset": 2648, + "startOffset": 2589 + }, + { + "count": 0, + "endOffset": 2868, + "startOffset": 2789 + }, + { + "count": 0, + "endOffset": 3283, + "startOffset": 3108 + }, + { + "count": 0, + "endOffset": 3430, + "startOffset": 3294 + }, + { + "count": 11710, + "endOffset": 3658, + "startOffset": 3439 + }, + { + "count": 9499, + "endOffset": 3540, + "startOffset": 3487 + }, + { + "count": 2211, + "endOffset": 3601, + "startOffset": 3540 + }, + { + "count": 12444, + "endOffset": 3704, + "startOffset": 3658 + }, + { + "count": 118232, + "endOffset": 3803, + "startOffset": 3704 + }, + { + "count": 1666, + "endOffset": 3746, + "startOffset": 3732 + }, + { + "count": 174, + "endOffset": 3771, + "startOffset": 3748 + }, + { + "count": 118058, + "endOffset": 3803, + "startOffset": 3771 + } + ] + }, + { + "functionName": "_format", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4453, + "startOffset": 4017 + } + ] + }, + { + "functionName": "resolve", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2319, + "endOffset": 9115, + "startOffset": 4579 + }, + { + "count": 3159, + "endOffset": 8648, + "startOffset": 4742 + }, + { + "count": 3157, + "endOffset": 4953, + "startOffset": 4780 + }, + { + "count": 0, + "endOffset": 4944, + "startOffset": 4911 + }, + { + "count": 2, + "endOffset": 5845, + "startOffset": 4953 + }, + { + "count": 0, + "endOffset": 5845, + "startOffset": 5033 + }, + { + "count": 0, + "endOffset": 6218, + "startOffset": 6053 + }, + { + "count": 0, + "endOffset": 7598, + "startOffset": 6251 + }, + { + "count": 3024, + "endOffset": 7689, + "startOffset": 7634 + }, + { + "count": 2319, + "endOffset": 8015, + "startOffset": 7691 + }, + { + "count": 2319, + "endOffset": 8333, + "startOffset": 8048 + }, + { + "count": 0, + "endOffset": 8270, + "startOffset": 8090 + }, + { + "count": 0, + "endOffset": 8433, + "startOffset": 8365 + }, + { + "count": 2319, + "endOffset": 8600, + "startOffset": 8572 + }, + { + "count": 2319, + "endOffset": 8632, + "startOffset": 8602 + }, + { + "count": 0, + "endOffset": 9109, + "startOffset": 9059 + } + ] + }, + { + "functionName": "normalize", + "isBlockCoverage": true, + "ranges": [ + { + "count": 6, + "endOffset": 12023, + "startOffset": 9187 + }, + { + "count": 0, + "endOffset": 9308, + "startOffset": 9297 + }, + { + "count": 0, + "endOffset": 9622, + "startOffset": 9465 + }, + { + "count": 0, + "endOffset": 11204, + "startOffset": 9655 + }, + { + "count": 5, + "endOffset": 11582, + "startOffset": 11278 + }, + { + "count": 0, + "endOffset": 11708, + "startOffset": 11697 + }, + { + "count": 0, + "endOffset": 11751, + "startOffset": 11737 + }, + { + "count": 0, + "endOffset": 11771, + "startOffset": 11760 + }, + { + "count": 0, + "endOffset": 11863, + "startOffset": 11850 + }, + { + "count": 1, + "endOffset": 11950, + "startOffset": 11895 + }, + { + "count": 0, + "endOffset": 11935, + "startOffset": 11922 + }, + { + "count": 5, + "endOffset": 11996, + "startOffset": 11950 + }, + { + "count": 0, + "endOffset": 12017, + "startOffset": 11997 + } + ] + }, + { + "functionName": "isAbsolute", + "isBlockCoverage": true, + "ranges": [ + { + "count": 317, + "endOffset": 12477, + "startOffset": 12096 + }, + { + "count": 0, + "endOffset": 12220, + "startOffset": 12207 + }, + { + "count": 316, + "endOffset": 12380, + "startOffset": 12345 + }, + { + "count": 160, + "endOffset": 12424, + "startOffset": 12381 + }, + { + "count": 116, + "endOffset": 12470, + "startOffset": 12425 + } + ] + }, + { + "functionName": "join", + "isBlockCoverage": true, + "ranges": [ + { + "count": 6, + "endOffset": 14671, + "startOffset": 12552 + }, + { + "count": 0, + "endOffset": 12614, + "startOffset": 12603 + }, + { + "count": 13, + "endOffset": 12926, + "startOffset": 12697 + }, + { + "count": 12, + "endOffset": 12910, + "startOffset": 12838 + }, + { + "count": 6, + "endOffset": 12863, + "startOffset": 12838 + }, + { + "count": 7, + "endOffset": 12910, + "startOffset": 12863 + }, + { + "count": 0, + "endOffset": 12978, + "startOffset": 12967 + }, + { + "count": 0, + "endOffset": 14271, + "startOffset": 13851 + }, + { + "count": 0, + "endOffset": 14499, + "startOffset": 14466 + }, + { + "count": 0, + "endOffset": 14620, + "startOffset": 14579 + } + ] + }, + { + "functionName": "relative", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18564, + "startOffset": 14979 + } + ] + }, + { + "functionName": "toNamespacedPath", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1082, + "endOffset": 19630, + "startOffset": 18571 + }, + { + "count": 0, + "endOffset": 18702, + "startOffset": 18690 + }, + { + "count": 0, + "endOffset": 18759, + "startOffset": 18733 + }, + { + "count": 0, + "endOffset": 18865, + "startOffset": 18853 + }, + { + "count": 0, + "endOffset": 19300, + "startOffset": 18929 + }, + { + "count": 0, + "endOffset": 19629, + "startOffset": 19605 + } + ] + }, + { + "functionName": "dirname", + "isBlockCoverage": true, + "ranges": [ + { + "count": 496, + "endOffset": 22114, + "startOffset": 19702 + }, + { + "count": 0, + "endOffset": 19821, + "startOffset": 19810 + }, + { + "count": 0, + "endOffset": 20090, + "startOffset": 19926 + }, + { + "count": 0, + "endOffset": 21455, + "startOffset": 20153 + }, + { + "count": 0, + "endOffset": 21602, + "startOffset": 21599 + }, + { + "count": 5695, + "endOffset": 21973, + "startOffset": 21732 + }, + { + "count": 496, + "endOffset": 21871, + "startOffset": 21782 + }, + { + "count": 5199, + "endOffset": 21966, + "startOffset": 21871 + }, + { + "count": 0, + "endOffset": 22077, + "startOffset": 21997 + } + ] + }, + { + "functionName": "basename", + "isBlockCoverage": true, + "ranges": [ + { + "count": 114, + "endOffset": 24930, + "startOffset": 22214 + }, + { + "count": 0, + "endOffset": 22298, + "startOffset": 22271 + }, + { + "count": 0, + "endOffset": 22778, + "startOffset": 22761 + }, + { + "count": 0, + "endOffset": 22807, + "startOffset": 22779 + }, + { + "count": 0, + "endOffset": 24318, + "startOffset": 22809 + }, + { + "count": 1326, + "endOffset": 24848, + "startOffset": 24371 + }, + { + "count": 114, + "endOffset": 24652, + "startOffset": 24421 + }, + { + "count": 1212, + "endOffset": 24841, + "startOffset": 24652 + }, + { + "count": 114, + "endOffset": 24841, + "startOffset": 24674 + }, + { + "count": 0, + "endOffset": 24889, + "startOffset": 24879 + } + ] + }, + { + "functionName": "extname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27073, + "startOffset": 25002 + } + ] + }, + { + "functionName": "parse", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31903, + "startOffset": 27291 + } + ] + }, + { + "functionName": "resolve", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33025, + "startOffset": 32100 + } + ] + }, + { + "functionName": "normalize", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33694, + "startOffset": 33097 + } + ] + }, + { + "functionName": "isAbsolute", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33899, + "startOffset": 33767 + } + ] + }, + { + "functionName": "join", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 34405, + "startOffset": 33974 + } + ] + }, + { + "functionName": "relative", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36780, + "startOffset": 34502 + } + ] + }, + { + "functionName": "toNamespacedPath", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36866, + "startOffset": 36787 + } + ] + }, + { + "functionName": "dirname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 37590, + "startOffset": 36938 + } + ] + }, + { + "functionName": "basename", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 40083, + "startOffset": 37690 + } + ] + }, + { + "functionName": "extname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 41863, + "startOffset": 40155 + } + ] + }, + { + "functionName": "parse", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 44518, + "startOffset": 42086 + } + ] + } + ], + "scriptId": "16", + "url": "path.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 8643, + "startOffset": 0 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1882, + "startOffset": 1855 + } + ] + }, + { + "functionName": "unenroll", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3615, + "startOffset": 2396 + } + ] + }, + { + "functionName": "enroll", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4089, + "startOffset": 3827 + } + ] + }, + { + "functionName": "setTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4800, + "startOffset": 4127 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5019, + "startOffset": 4881 + } + ] + }, + { + "functionName": "clearTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5407, + "startOffset": 5028 + } + ] + }, + { + "functionName": "setInterval", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6086, + "startOffset": 5411 + } + ] + }, + { + "functionName": "clearInterval", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6393, + "startOffset": 6090 + } + ] + }, + { + "functionName": "Timeout.close", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6477, + "startOffset": 6423 + } + ] + }, + { + "functionName": "Timeout.", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6689, + "startOffset": 6521 + } + ] + }, + { + "functionName": "setImmediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7259, + "startOffset": 6694 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7482, + "startOffset": 7342 + } + ] + }, + { + "functionName": "clearImmediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7977, + "startOffset": 7493 + } + ] + } + ], + "scriptId": "17", + "url": "timers.js" + } + ] + } +} diff --git a/branch-master/.artifact/apidoc.html b/branch-master/.artifact/apidoc.html new file mode 100644 index 000000000..c6101547c --- /dev/null +++ b/branch-master/.artifact/apidoc.html @@ -0,0 +1,2076 @@ + + + + + + +JSLint apidoc + + + +
    +

    API Doc for JSLint (v2024.11.24)

    +
    + +

    Table of Contents

    +
    + +
    + +
    +

    Module "./jslint.mjs"

    +
      + +
    • +

      + 1. + function assertErrorThrownAsync(asyncFunc, regexp) + +

      +
    • +
    • Description and source-code:
      async function assertErrorThrownAsync(asyncFunc, regexp) {
      +
      +// This function will assert calling <asyncFunc> throws an error.
      +
      +    let err;
      +    try {
      +        await asyncFunc();
      +    } catch (errCaught) {
      +        err = errCaught;
      +    }
      +    assertOrThrow(err, "No error thrown.");
      +    assertOrThrow(
      +        !regexp || new RegExp(regexp).test(err.message),
      +        err
      +    );
      +}
    • +
    • Example usage:
      ...
      +
      +jstestDescribe((
      +"test v8CoverageReportCreate handling-behavior"
      +), function testBehaviorV8CoverageReportCreate() {
      +jstestIt((
      +    "test null-case handling-behavior"
      +), async function () {
      +    await assertErrorThrownAsync(function () {
      +        return v8CoverageReportCreate({});
      +    }, "invalid coverageDir");
      +});
      +jstestIt((
      +    "test coverage-report jslint.mjs handling-behavior"
      +), async function () {
      +    // test remove-old-coverage handling-behavior
      +...
    • + +
    • +

      + 2. + function assertJsonEqual(aa, bb, message) + +

      +
    • +
    • Description and source-code:
      function assertJsonEqual(aa, bb, message) {
      +
      +// This function will assert JSON.stringify(<aa>) === JSON.stringify(<bb>).
      +
      +    aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa), undefined, 1);
      +    bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb), undefined, 1);
      +    if (aa !== bb) {
      +        throw new Error(
      +            "\n" + aa + "\n!==\n" + bb
      +            + (
      +                typeof message === "string"
      +                ? " - " + message
      +                : message
      +                ? " - " + JSON.stringify(message)
      +                : ""
      +            )
      +        );
      +    }
      +}
    • +
    • Example usage:
      ...
      +
      +// Debug data1.
      +// await moduleFs.promises.writeFile(
      +//     ".test_v8_coverage_node_sqlite_merged.json",
      +//     JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n"
      +// );
      +
      +    assertJsonEqual(data1, data2);
      +});
      +});
      +
      +jstestDescribe((
      +"test v8CoverageReportCreate handling-behavior"
      +), function testBehaviorV8CoverageReportCreate() {
      +jstestIt((
      +...
    • + +
    • +

      + 3. + function assertOrThrow(condition, message) + +

      +
    • +
    • Description and source-code:
      function assertOrThrow(condition, message) {
      +
      +// This function will throw <message> if <condition> is falsy.
      +
      +    if (!condition) {
      +        throw (
      +            (!message || typeof message === "string")
      +            ? new Error(String(message).slice(0, 2048))
      +            : message
      +        );
      +    }
      +}
    • +
    • Example usage:
      ...
      +            assertJsonEqual(1, 2, "undefined");
      +        });
      +        await assertErrorThrownAsync(function () {
      +            assertJsonEqual(1, 2, {});
      +        });
      +        // test assertOrThrow error handling-behavior
      +        await assertErrorThrownAsync(function () {
      +            assertOrThrow(undefined, "undefined");
      +        });
      +        await assertErrorThrownAsync(function () {
      +            assertOrThrow(undefined, new Error());
      +        });
      +    });
      +});
      +...
    • + +
    • +

      + 4. + function debugInline(...argv) + +

      +
    • +
    • Description and source-code:
      function debug(...argv) {
      +
      +// This function will print <argv> to stderr and then return <argv>[0].
      +
      +    __consoleError("\n\ndebugInline");
      +    __consoleError(...argv);
      +    __consoleError("\n");
      +    return argv[0];
      +}
    • +
    • Example usage:
      N/A
    • + +
    • +

      + 5. + function fsWriteFileWithParents(pathname, data) + +

      +
    • +
    • Description and source-code:
      async function fsWriteFileWithParents(pathname, data) {
      +
      +// This function will write <data> to <pathname> and lazy-mkdirp if necessary.
      +
      +    await moduleFsInit();
      +
      +// Try writing to pathname.
      +
      +    try {
      +        await moduleFs.promises.writeFile(pathname, data);
      +    } catch (ignore) {
      +
      +// Lazy mkdirp.
      +
      +        await moduleFs.promises.mkdir(modulePath.dirname(pathname), {
      +            recursive: true
      +        });
      +
      +// Retry writing to pathname.
      +
      +        await moduleFs.promises.writeFile(pathname, data);
      +    }
      +    console.error("wrote file " + pathname);
      +}
    • +
    • Example usage:
      ...
      +                }
      +            ]
      +        }, undefined, 4)
      +    ]
      +].map(async function ([
      +    file, data
      +]) {
      +    await fsWriteFileWithParents(file, data);
      +}));
      +await jslint.jslint_cli({
      +    console_error: noop, // comment to debug
      +    mode_cli: true,
      +    process_argv: [
      +        "node", "jslint.mjs",
      +        "v8_coverage_report=.tmp/coverage_misc"
      +...
    • + +
    • +

      + 6. + function globExclude({ + excludeList = [], + includeList = [], + pathnameList = [] +}) + +

      +
    • +
    • Description and source-code:
      function globExclude({
      +    excludeList = [],
      +    includeList = [],
      +    pathnameList = []
      +}) {
      +
      +// This function will
      +// 1. Exclude pathnames in <pathnameList> that don't match glob-patterns in
      +//    <includeList>.
      +// 2. Exclude pathnames in <pathnameList> that match glob-patterns in
      +//    <excludeList>.
      +
      +    function globAssertNotWeird(list, name) {
      +
      +// This function will check if <list> of strings contain weird characters.
      +
      +        [
      +            [
      +                "\n", (
      +                    /^.*?([\u0000-\u0007\r]).*/gm
      +                )
      +            ],
      +            [
      +                "\r", (
      +                    /^.*?([\n]).*/gm
      +                )
      +            ]
      +        ].forEach(function ([
      +            separator, rgx
      +        ]) {
      +            list.join(separator).replace(rgx, function (match0, char) {
      +                throw new Error(
      +                    "Weird character "
      +                    + JSON.stringify(char)
      +                    + " found in " + name + " "
      +                    + JSON.stringify(match0)
      +                );
      +            });
      +        });
      +    }
      +
      +    function globToRegexp(pattern) {
      +
      +// This function will translate glob <pattern> to javascript-regexp,
      +// which javascript can then use to "glob" pathnames.
      +
      +        let ii = 0;
      +        let isClass = false;
      +        let strClass = "";
      +        let strRegex = "";
      +        pattern = pattern.replace((
      +            /\/\/+/g
      +        ), "/");
      +        pattern = pattern.replace((
      +            /\*\*\*+/g
      +        ), "**");
      +        pattern.replace((
      +            /\\\\|\\\[|\\\]|\[|\]|./g
      +        ), function (match0) {
      +            switch (match0) {
      +            case "[":
      +                if (isClass) {
      +                    strClass += "[";
      +                    return;
      +                }
      +                strClass += "\u0000";
      +                strRegex += "\u0000";
      +                isClass = true;
      +                return;
      +            case "]":
      +                if (isClass) {
      +                    isClass = false;...
      +}
      +
    • +
    • Example usage:
      ...
      +    "test/support/helper.js": (
      +        /^test\/support\/helper\.js$/gm
      +    )
      +}).forEach(function ([
      +    pattern, rgx
      +]) {
      +    assertJsonEqual(
      +        globExclude({
      +            excludeList: [
      +                pattern
      +            ]
      +        }).excludeList[0].source,
      +        rgx.source
      +    );
      +    assertJsonEqual(
      +...
    • + +
    • +

      + 7. + function htmlEscape(str) + +

      +
    • +
    • Description and source-code:
      function htmlEscape(str) {
      +
      +// This function will make <str> html-safe by escaping & < >.
      +
      +    return String(str).replace((
      +        /&/g
      +    ), "&amp;").replace((
      +        /</g
      +    ), "&lt;").replace((
      +        />/g
      +    ), "&gt;");
      +}
    • +
    • Example usage:
      ...
      +                            inHole = isHole;
      +                        }
      +                        chunk += char;
      +                    });
      +                    lineHtml += htmlEscape(chunk);
      +                    break;
      +                default:
      +                    lineHtml += htmlEscape(line);
      +                }
      +                html += String(`
      +<pre>
      +<span class="lineno">
      +<a href="#${lineId}" id="${lineId}">${String(ii + 1).padStart(5, " ")}.</a>
      +</span>
      +<span class="count
      +...
    • + +
    • +

      + 8. + function jslint( + source = "", + option_dict = empty(), + global_list = [] +) + +

      +
    • +
    • Description and source-code:
      function jslint(
      +    source = "",                // A text to analyze.
      +    option_dict = empty(),      // An object whose keys correspond to option
      +                                // ... names.
      +    global_list = []            // An array of strings containing global
      +                                // ... variables that the file is allowed
      +                                // ... readonly access.
      +) {
      +
      +// The jslint function itself.
      +
      +    let catch_list = [];        // The array containing all catch-blocks.
      +    let catch_stack = [         // The stack of catch-blocks.
      +        {
      +            context: empty()
      +        }
      +    ];
      +    let cause_dict = empty();   // The object of test-causes.
      +    let directive_list = [];    // The directive comments.
      +    let export_dict = empty();  // The exported names and values.
      +    let function_list = [];     // The array containing all functions.
      +    let function_stack = [];    // The stack of functions.
      +    let global_dict = empty();  // The object containing the global
      +                                // ... declarations.
      +    let import_list = [];       // The array collecting all import-from strings.
      +    let line_list = String(     // The array containing source lines.
      +        "\n" + source
      +    ).split(jslint_rgx_crlf).map(function (line_source) {
      +        return {
      +            line_source
      +        };
      +    });
      +    let mode_stop = false;      // true if JSLint cannot finish.
      +    let property_dict = empty();        // The object containing the tallied
      +                                        // ... property names.
      +    let state = empty();        // jslint state-object to be passed between
      +                                // jslint functions.
      +    let syntax_dict = empty();  // The object containing the parser.
      +    let tenure = empty();       // The predefined property registry.
      +    let token_global = {        // The global object; the outermost context.
      +        async: 0,
      +        body: true,
      +        context: empty(),
      +        finally: 0,
      +        from: 0,
      +...
      +}
      +
    • +
    • Example usage:
      ...
      +import fs from "fs";
      +(async function () {
      +    let result;
      +    let source = "function foo() {console.log(\u0027hello world\u0027);}\n";
      +
      +// Create JSLint report from <source> in javascript.
      +
      +    result = jslint.jslint(source);
      +    result = jslint.jslint_report(result);
      +    result = `<body class="JSLINT_ JSLINT_REPORT_">\n${result}</body>\n`;
      +
      +    await fs.promises.mkdir(".artifact/", {recursive: true});
      +    await fs.promises.writeFile(".artifact/jslint_report_hello.html", result);
      +    console.error("wrote file .artifact/jslint_report_hello.html");
      +}());
      +...
    • + +
    • +

      + 9. + function jslint_apidoc({ + example_list, + github_repo, + module_list, + package_name, + pathname, + version +}) + +

      +
    • +
    • Description and source-code:
      async function jslint_apidoc({
      +    example_list,
      +    github_repo,
      +    module_list,
      +    package_name,
      +    pathname,
      +    version
      +}) {
      +
      +// This function will create API Doc from <module_list>.
      +
      +    let elem_ii = 0;
      +    let html;
      +
      +    function elem_create(moduleObj, key, moduleName) {
      +
      +// This function will create a sub API Doc from elem <moduleObj>[<key>].
      +
      +        let example = "N/A";
      +        let id = encodeURIComponent("apidoc.elem." + moduleName + "." + key);
      +        let name;
      +        let signature;
      +        let source;
      +        name = htmlEscape((typeof moduleObj[key]) + " " + key);
      +        if (typeof moduleObj[key] !== "function") {
      +            return {
      +                name,
      +                signature: (`
      +<a class="apidocElementLiA" href="#${id}">
      +${name}
      +</a>
      +                `),
      +                source: (`
      +<li>
      +    <h2>
      +    <a href="#${id}" id="${id}">
      +    ${name}
      +    </a>
      +    </h2>
      +</li>
      +                `)
      +            };
      +        }
      +        // init source
      +        source = htmlEscape(trim_start(moduleObj[key].toString()));
      +        // init signature
      +        source = source.replace((
      +            /(\([\S\s]*?\)) \{/
      +        ), function (match0, match1) {
      +            signature = htmlEscape(
      +                match1.replace((
      +                    / *?\/\*[\S\s]*?\*\/ */g
      +                ), "").replace((
      +                    / *?\/\/.*/g
      +                ), "").replace((
      +                    /\n{2,}/g
      +                ), "\n")
      +            );
      +            return match0;
      +        });
      +        // init comment
      +        source = source.replace((
      +            /\n(?:\/\/.*?\n)+\n/
      +        ), "<span class=\"apidocCodeCommentSpan\">$&</span>");
      +        // init example
      +        example_list.some(function (example2) {
      +            example2.replace(
      +                new RegExp((
      +                    "((?:\\n.*?){8}(function )?)\\b"
      +                    + key
      +                    + "(\\((?:.*?\\n){8})"
      +                ), "g"),
      +  ...
      +}
      +
    • +
    • Example usage:
      ...
      +command[1] = command.slice(1).join("=");
      +
      +switch (command[0]) {
      +
      +// PR-362 - Add API Doc.
      +
      +case "jslint_apidoc":
      +    await jslint_apidoc(Object.assign(JSON.parse(process_argv[3]), {
      +        pathname: command[1]
      +    }));
      +    return;
      +
      +// PR-363 - Add command jslint_report.
      +
      +case "jslint_report":
      +...
    • + +
    • +

      + 10. + function jslint_assert(condition, message) + +

      +
    • +
    • Description and source-code:
      function jslint_assert(condition, message) {
      +
      +// This function will throw <message> if <condition> is falsy.
      +
      +    if (condition) {
      +        return condition;
      +    }
      +    throw new Error(
      +        `This was caused by a bug in JSLint.
      +Please open an issue with this stack-trace (and possible example-code) at
      +https://github.com/jslint-org/jslint/issues.
      +edition = "${jslint_edition}";
      +${String(message).slice(0, 2000)}`
      +    );
      +}
    • +
    • Example usage:
      ...
      +// ["let aa={};", "whitage", "opener", "", 0]
      +
      +test_cause("opener");
      +
      +// Probably deadcode.
      +// case "${}":
      +
      +jslint_assert(
      +    !(left.id + right.id === "${}"),
      +    "Expected !(left.id + right.id === \"${}\")."
      +);
      +switch (left.id + right.id) {
      +case "()":
      +case "[]":
      +case "{}":
      +...
    • + +
    • +

      + 11. + function jslint_cli({ + console_error, + console_log, + file, + import_meta_url, + mode_cli, + mode_noop, + option, + process_argv, + process_env, + process_exit, + source +}) + +

      +
    • +
    • Description and source-code:
      async function jslint_cli({
      +    console_error,
      +    console_log,
      +    file,
      +    import_meta_url,
      +    mode_cli,
      +    mode_noop,
      +    option,
      +    process_argv,
      +    process_env,
      +    process_exit,
      +    source
      +}) {
      +
      +// This function will run jslint from nodejs-cli.
      +
      +    let command;
      +    let data;
      +    let exit_code = 0;
      +    let mode_report;
      +    let mode_wrapper_vim;
      +    let result;
      +
      +    function jslint_from_file({
      +        code,
      +        file,
      +        line_offset = 0,
      +        mode_conditional,
      +        option = empty()
      +    }) {
      +        let result_from_file;
      +        if (
      +            mode_conditional
      +            && !(
      +                /^\/\*jslint\b/m
      +            ).test(code.slice(0, 65536))
      +        ) {
      +            return;
      +        }
      +        option = Object.assign(empty(), option, {
      +            file
      +        });
      +        switch ((
      +            /\.\w+?$|$/m
      +        ).exec(file)[0]) {
      +        case ".html":
      +
      +// Recursively jslint embedded "<script>\n...\n</script>".
      +
      +            code.replace((
      +                /^<script\b[^>]*?>\n([\S\s]*?\n)<\/script>$/gm
      +            ), function (ignore, match1, ii) {
      +                jslint_from_file({
      +                    code: match1,
      +                    file: file + ".<script>.js",
      +                    line_offset: string_line_count(code.slice(0, ii)) + 1,
      +                    option: Object.assign(empty(), {
      +                        browser: true
      +                    }, option)
      +                });
      +                return "";
      +            });
      +            return;
      +        case ".md":
      +
      +// Recursively jslint embedded "node --eval '\n...\n'".
      +
      +            jslint_node_eval({
      +                code,
      +                file,
      +                mode_conditional: true,
      +                option
      +            });
      +            return;
      +        case ".sh":
      +
      +// Recursively jslint embedded "node --eval '\n...\n'".
      +
      +            jslint_node_eval({
      +                code,
      +                file,
      +                option
      +            });
      +            return;
      +       ...
      +}
      +
    • +
    • Example usage:
      ...
      +        }, undefined, 4)
      +    ]
      +].map(async function ([
      +    file, data
      +]) {
      +    await fsWriteFileWithParents(file, data);
      +}));
      +await jslint.jslint_cli({
      +    console_error: noop, // comment to debug
      +    mode_cli: true,
      +    process_argv: [
      +        "node", "jslint.mjs",
      +        "v8_coverage_report=.tmp/coverage_misc"
      +        // "node", ".tmp/coverage_misc/aa.js"
      +    ]
      +...
    • + +
    • +

      + 12. + function jslint_phase1_split() + +

      +
    • +
    • Description and source-code:
      function jslint_phase1_split() {
      +
      +// PHASE 1. Split <source> by newlines into <line_list>.
      +
      +    return;
      +}
    • +
    • Example usage:
      ...
      +            warn,
      +            warn_at,
      +            warning_list
      +        });
      +
      +// PHASE 1. Split <source> by newlines into <line_list>.
      +
      +        jslint_phase1_split(state);
      +        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
      +        jslint_assert(
      +            function_stack.length === 0,
      +            `function_stack.length === 0.`
      +        );
      +
      +// PHASE 2. Lex <line_list> into <token_list>.
      +...
    • + +
    • +

      + 13. + function jslint_phase2_lex(state) + +

      +
    • +
    • Description and source-code:
      function jslint_phase2_lex(state) {
      +
      +// PHASE 2. Lex <line_list> into <token_list>.
      +
      +    let {
      +        artifact,
      +        directive_list,
      +        global_dict,
      +        global_list,
      +        line_list,
      +        option_dict,
      +        stop,
      +        stop_at,
      +        tenure,
      +        test_cause,
      +        token_global,
      +        token_list,
      +        warn,
      +        warn_at
      +    } = state;
      +    let char;                   // The current character being lexed.
      +    let column = 0;             // The column number of the next character.
      +    let from;                   // The starting column number of the token.
      +    let from_mega;              // The starting column of megastring.
      +    let line = 0;               // The line number of the next character.
      +    let line_disable;           // The starting line of "/*jslint-disable*/".
      +    let line_mega;              // The starting line of megastring.
      +    let line_source = "";       // The remaining line source string.
      +    let line_whole = "";        // The whole line source string.
      +    let mode_digits_empty_string = 1;
      +    let mode_digits_numeric_separator = 2;
      +    let mode_directive = true;  // true if directives are still allowed.
      +    let mode_mega = false;      // true if currently parsing a megastring
      +                                // ... literal.
      +    let mode_regexp;            // true if regular expression literal seen on
      +                                // ... this line.
      +    let paren_backtrack_list = [];      // List of most recent "(" tokens at any
      +                                        // ... paren-depth.
      +    let paren_depth = 0;        // Keeps track of current paren-depth.
      +    let snippet = "";           // A piece of string.
      +    let token_1;                // The first token.
      +    let token_prv = token_global;       // The previous token including
      +                                        // ... comments.
      +    let token_prv_expr = token_global;  // The previous token excluding
      +                                        // ... comm...
      +}
      +
    • +
    • Example usage:
      ...
      +        jslint_assert(
      +            function_stack.length === 0,
      +            `function_stack.length === 0.`
      +        );
      +
      +// PHASE 2. Lex <line_list> into <token_list>.
      +
      +        jslint_phase2_lex(state);
      +        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
      +        jslint_assert(
      +            function_stack.length === 0,
      +            `function_stack.length === 0.`
      +        );
      +
      +// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
      +...
    • + +
    • +

      + 14. + function jslint_phase3_parse(state) + +

      +
    • +
    • Description and source-code:
      function jslint_phase3_parse(state) {
      +
      +// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
      +
      +// Parsing:
      +
      +// Parsing weaves the tokens into an abstract syntax tree. During that process,
      +// a token may be given any of these properties:
      +
      +//      arity       string
      +//      label       identifier
      +//      name        identifier
      +//      expression  expressions
      +//      block       statements
      +//      else        statements (else, default, catch)
      +
      +// Specialized tokens may have additional properties.
      +
      +    let anon = "anonymous";     // The guessed name for anonymous functions.
      +    let {
      +        artifact,
      +        catch_list,
      +        catch_stack,
      +        export_dict,
      +        function_list,
      +        function_stack,
      +        global_dict,
      +        import_list,
      +        is_equal,
      +        option_dict,
      +        property_dict,
      +        stop,
      +        syntax_dict,
      +        tenure,
      +        test_cause,
      +        token_global,
      +        token_list,
      +        warn,
      +        warn_at
      +    } = state;
      +    let catchage = catch_stack[0];      // The current catch-block.
      +    let functionage = token_global;     // The current function.
      +    let mode_var;               // "var" if using var; "let" if using let.
      +    let token_ii = 0;           // The number of the next token.
      +    let token_now = token_global;       // The current token being examined in
      +                                        // ... the parse.
      +    let token_nxt = token_global;       // The next token to be examined in
      +                                        // ... <token_list>.
      +
      +    function advance(id, match) {
      +
      +// Produce the next token.
      +
      +// Attempt to give helpful names to anonymous functions.
      +
      +        if (
      +            token_now.identifier
      +            && token_now.id !== "function"
      +            && token_now.id !== "async"
      +        ) {
      +            anon = token_now.id;
      +        } else if (
      +            token_now.id === "(string)"
      +            && jslint_rgx_identifier.test(token_now.value)
      +       ...
      +}
      +
    • +
    • Example usage:
      ...
      +        jslint_assert(
      +            function_stack.length === 0,
      +            `function_stack.length === 0.`
      +        );
      +
      +// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
      +
      +        jslint_phase3_parse(state);
      +        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
      +        jslint_assert(
      +            function_stack.length === 0,
      +            `function_stack.length === 0.`
      +        );
      +
      +// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
      +...
    • + +
    • +

      + 15. + function jslint_phase4_walk(state) + +

      +
    • +
    • Description and source-code:
      function jslint_phase4_walk(state) {
      +
      +// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
      +//          recursive traversal. Each node may be processed on the way down
      +//          (preaction) and on the way up (postaction).
      +
      +    let {
      +        artifact,
      +        catch_stack,
      +        function_stack,
      +        global_dict,
      +        is_equal,
      +        is_weird,
      +        option_dict,
      +        syntax_dict,
      +        test_cause,
      +        token_global,
      +        warn
      +    } = state;
      +    let block_stack = [];               // The stack of blocks.
      +    let blockage = token_global;        // The current block.
      +    let catchage = catch_stack[0];      // The current catch-block.
      +    let functionage = token_global;     // The current function.
      +    let postaction;
      +    let postamble;
      +    let posts = empty();
      +    let preaction;
      +    let preamble;
      +    let pres = empty();
      +
      +// The relational operators.
      +
      +    let relationop = object_assign_from_list(empty(), [
      +        "!=", "!==", "<", "<=", "==", "===", ">", ">="
      +    ], true);
      +
      +// Ambulation of the parse tree.
      +
      +    function action(when) {
      +
      +// Produce a function that will register task functions that will be called as
      +// the tree is traversed.
      +
      +        return function (arity, id, task) {
      +            let a_set = when[arity];
      +            let i_set;
      +
      +// The id parameter is optional. If excluded, the task will be applied to all
      +// ids.
      +
      +            if (typeof id !== "string") {
      +                task = id;
      +                id = "(all)";
      +            }
      +
      +// If this arity has no registrations yet, then create a set object to hold
      +// them.
      +
      +            if (a_set === undefined) {
      +                a_set = empty();
      +                when[arity] = a_set;
      +            }
      +
      +// If this id has no registrations yet, then create a set array to hold them.
      +
      +            i_set = a_set[id];
      +            if (i_set === undefined) {
      +                i_set = [];
      +                a_set[id] = i_set;
      +            }
      +
      +// Register the task with the arity and the id.
      +...
      +}
      +
    • +
    • Example usage:
      ...
      +);
      +
      +// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
      +//          recursive traversal. Each node may be processed on the way down
      +//          (preaction) and on the way up (postaction).
      +
      +if (!state.mode_json) {
      +    jslint_phase4_walk(state);
      +}
      +jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
      +jslint_assert(
      +    function_stack.length === 0,
      +    `function_stack.length === 0.`
      +);
      +...
    • + +
    • +

      + 16. + function jslint_phase5_whitage(state) + +

      +
    • +
    • Description and source-code:
      function jslint_phase5_whitage(state) {
      +
      +// PHASE 5. Check whitespace between tokens in <token_list>.
      +
      +    let {
      +        artifact,
      +        catch_list,
      +        function_list,
      +        function_stack,
      +        option_dict,
      +        test_cause,
      +        token_global,
      +        token_list,
      +        warn
      +    } = state;
      +    let closer = "(end)";
      +    let free = false;
      +
      +// free = false
      +
      +// cause:
      +// "()=>0"
      +// "aa()"
      +// "aa(0,0)"
      +// "function(){}"
      +
      +// free = true
      +
      +// cause:
      +// "(0)"
      +// "(aa)"
      +// "aa(0)"
      +// "do{}while()"
      +// "for(){}"
      +// "if(){}"
      +// "switch(){}"
      +// "while(){}"
      +
      +    let left = token_global;
      +    let margin = 0;
      +    let mode_indent = (
      +
      +// PR-330 - Allow 2-space indent.
      +
      +        option_dict.indent2
      +        ? 2
      +        : 4
      +    );
      +    let nr_comments_skipped = 0;
      +    let open = true;
      +    let opening = true;
      +    let right;
      +
      +// This is the set of infix operators that require a space on each side.
      +
      +    let spaceop = object_assign_from_list(empty(), [
      +        "!=", "!==", "%", "%=", "&", "&&", "&=", "*", "*=", "+=", "-=", "/",
      +        "/=", "<", "<<", "<<=", "<=", "=", "==", "===", "=>", ">", ">=", ">>",
      +        ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||"
      +    ], true);
      +
      +    function at_margin(fit) {
      +        const at = margin + fit;
      +        if (right.from !== at) {
      +            return expected_at(at);
      +        }
      +    }
      +
      +    function delve(the_function) {
      +        Object.keys(the_function.context).forEach(function (id) {
      +            const name = the_function.context[id];
      +            if (id !== "ignore" && name.parent === the_function) {
      +
      +// test_cause:
      +// ["function aa(aa) {return aa;}", "delve", "id", "", 0]
      +
      +                test_cause("id");
      +                if (
      +                    name.used === 0
      +
      +// Probably deadcode.
      +// && (
      +//     name.role !== "function"
      +//     || name.parent.arity !== "unary"
      +// )
      +
      +                    && jslint_assert(
      +                        name.role...
      +}
      +
    • +
    • Example usage:
      ...
      +    function_stack.length === 0,
      +    `function_stack.length === 0.`
      +);
      +
      +// PHASE 5. Check whitespace between tokens in <token_list>.
      +
      +if (!state.mode_json && warning_list.length === 0) {
      +    jslint_phase5_whitage(state);
      +}
      +jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
      +jslint_assert(
      +    function_stack.length === 0,
      +    `function_stack.length === 0.`
      +);
      +...
    • + +
    • +

      + 17. + function jslint_report({ + exports, + froms, + functions, + global, + json, + module, + property, + stop, + warnings +}) + +

      +
    • +
    • Description and source-code:
      function jslint_report({
      +    exports,
      +    froms,
      +    functions,
      +    global,
      +    json,
      +    module,
      +    property,
      +    stop,
      +    warnings
      +}) {
      +
      +// This function will create human-readable, html-report
      +// for warnings, properties, and functions from jslint-result-object.
      +//
      +// Example usage:
      +//  let result = jslint("console.log('hello world')");
      +//  let html = jslint_report(result);
      +
      +    let html = "";
      +    let length_80 = 1111;
      +
      +    function address(line = 1, column = 1) {
      +
      +// This function will create HTML address element from <line> and <column>
      +
      +        return `<address>${Number(line)}: ${Number(column)}</address>`;
      +
      +    }
      +
      +    function detail(title, list) {
      +        return (
      +            (Array.isArray(list) && list.length > 0)
      +            ? (
      +
      +// Google Lighthouse Accessibility - <dl>'s do not contain only properly-ordered
      +// <dt> and <dd> groups, <script>, <template> or <div> elements.
      +
      +                "<dl>"
      +                + "<dt>" + htmlEscape(title) + "</dt>"
      +                + "<dd>" + list.join(", ") + "</dd>"
      +                + "</dl>"
      +            )
      +            : ""
      +        );
      +    }
      +
      +    html += String(`
      +<style class="JSLINT_REPORT_STYLE">
      +/* jslint utility2:true */
      +/*csslint box-model: false, ids:false */
      +/*csslint ignore:start*/
      +@font-face {
      +    font-display: swap;
      +    font-family: "Daley";
      +    src: url(
      +"data:font/woff2;base64,d09GMgABAAAAABy4AA4AAAAAThwAABxiAAEAAAAAAAAAAAAA\
      +AAAAAAAAAAAAAAAABmAAgiQINAmcDBEICuc41DEBNgIkA4R2C4I+AAQgBYkuByAMgScfYUIF\
      +7NgjsHGAbcDVFkXZ5Jwd+P96IGPc9rl9ETBEaCzCJkvY2UpziRZ7zftZWk8052U9+NqX6vXL\
      +KDflSQnlJ0bP+QnPQAy744n9mup6H9PaCDFwM5zjf8exB89bZ1cdrYOP0NgnuRDRWlk9u/fE\
      +llkxqmfH8lmRQ/DAmER9opk9wR6suc1LvTiXNEe1vbhUCH2USgnEwH3vUm05JQqejGvZvOtz\
      +7sIKEGgLdDNl/IrfqWVZG/wr42ekomEm91VA1p4LhHBuFzHF8//u7vvbREHMQqGtNLmiOOD/\
      +X7WWiwqyCE98qt0jk5JJmgR5WJJElBmzRb1F7a66MmSLTNWZ2XSHfKBSKHoVteSEJ6EOdvVw\
      +fNZOtXKDe39jXdRlkmMnOWIOFBgeEK/b0mFsgffnP...
      +}
      +
    • +
    • Example usage:
      ...
      +
      +    editor.on("lintJslintAfter", function (options) {
      +
      +// Generate jslint-report from options.result.
      +
      +        document.querySelector(
      +            ".JSLINT_REPORT_"
      +        ).innerHTML = window.jslint.jslint_report(options.result);
      +    });
      +
      +// Manually trigger linter.
      +
      +    editor.performLint();
      +});
      +</script>
      +...
    • + +
    • +

      + 18. + function jstestDescribe(description, testFunction) + +

      +
    • +
    • Description and source-code:
      async function jstestDescribe(description, testFunction) {
      +
      +// This function will create-and-run test-group <testFunction>
      +// with given <description>.
      +
      +    let message;
      +    let result;
      +    let timerTimeout;
      +
      +// Init jstestTimeStart.
      +
      +    if (jstestTimeStart === undefined) {
      +        jstestTimeStart = jstestTimeStart || Date.now();
      +        process.on("exit", jstestOnExit);
      +    }
      +
      +// PR-457 - Wait awhile for imports to initialize.
      +
      +    await new Promise(function (resolve) {
      +        setTimeout(resolve);
      +    });
      +
      +// Init jstestItList.
      +
      +    jstestItList = [];
      +    testFunction();
      +
      +// Wait for jstestItList to resolve.
      +
      +    timerTimeout = setTimeout(noop, 0x7fffffff);
      +    result = await Promise.all(jstestItList);
      +    clearTimeout(timerTimeout);
      +
      +// Print test results.
      +
      +    message = (
      +        "\n  " + (Date.now() - jstestTimeStart) + "ms"
      +        + " - test describe - " + description + "\n"
      +        + result.map(function ([
      +            err, description, mode
      +        ]) {
      +            jstestItCount += 1;
      +            if (err) {
      +                jstestCountFailed += 1;
      +                err = (
      +                    "    \u001b[31m\u2718 " + jstestItCount + ". test it - "
      +                    + description + "\n" + err.stack + "\u001b[39m"
      +                );
      +                if (mode === "pass") {
      +                    jstestCountFailed -= 1;
      +                    err = "";
      +                }
      +            }
      +            return err || (
      +                "    \u001b[32m\u2714 " + jstestItCount + ". test it - "
      +                + description + "\u001b[39m"
      +            );
      +        }).join("\n")
      +    );
      +    console.error(message);
      +}
    • +
    • Example usage:
      ...
      +//     JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n"
      +// );
      +
      +    assertJsonEqual(data1, data2);
      +});
      +});
      +
      +jstestDescribe((
      +"test v8CoverageReportCreate handling-behavior"
      +), function testBehaviorV8CoverageReportCreate() {
      +jstestIt((
      +    "test null-case handling-behavior"
      +), async function () {
      +    await assertErrorThrownAsync(function () {
      +        return v8CoverageReportCreate({});
      +...
    • + +
    • +

      + 19. + function jstestIt(description, testFunction, mode) + +

      +
    • +
    • Description and source-code:
      function jstestIt(description, testFunction, mode) {
      +
      +// This function will create-and-run test-case <testFunction>
      +// inside current test-group with given <description>.
      +
      +    jstestCountTotal += 1;
      +    jstestItList.push(new Promise(async function (resolve) {
      +        let err;
      +        try {
      +            await testFunction();
      +        } catch (errCaught) {
      +            err = errCaught;
      +        }
      +        resolve([err, description, mode]);
      +    }));
      +}
    • +
    • Example usage:
      ...
      +                "v8_coverage_report=" + dir,
      +                "node",
      +                file
      +            ]
      +        });
      +    });
      +});
      +jstestIt((
      +    "test npm handling-behavior"
      +), async function () {
      +    await jslint.jslint_cli({
      +        console_error: noop, // comment to debug
      +        mode_cli: true,
      +        process_argv: [
      +            "node", "jslint.mjs",
      +...
    • + +
    • +

      + 20. + function jstestOnExit(exitCode, mode) + +

      +
    • +
    • Description and source-code:
      function jstestOnExit(exitCode, mode) {
      +
      +// This function will on process-exit, print test-report
      +// and exit with non-zero exit-code if any test failed.
      +
      +    let message = (
      +        (
      +            (jstestCountFailed || mode === "testsFailed")
      +            ? "\n\u001b[31m"
      +            : "\n\u001b[32m"
      +        )
      +        + "  tests total  - " + jstestCountTotal + "\n"
      +        + "  tests failed - " + jstestCountFailed + "\n"
      +        + "\n"
      +        + "  time finished - "
      +        + Number(Date.now() - jstestTimeStart).toLocaleString()
      +        + " ms\n"
      +        + "\u001b[39m"
      +    );
      +    if (mode !== "testsFailed") {
      +        console.error(message);
      +    }
      +    process.exitCode = exitCode || jstestCountFailed;
      +    return message;
      +}
    • +
    • Example usage:
      ...
      +    "test jstestDescribe error handling-behavior"
      +), function () {
      +    throw new Error();
      +}, "pass");
      +jstestIt((
      +    "test jstestOnExit tests-failed handling-behavior"
      +), function () {
      +    jstestOnExit(undefined, "testsFailed");
      +});
      +});
      +
      +jstestDescribe((
      +"test misc handling-behavior"
      +), function testBehaviorMisc() {
      +jstestIt((
      +...
    • + +
    • +

      + 21. + function moduleFsInit() + +

      +
    • +
    • Description and source-code:
      async function moduleFsInit() {
      +
      +// This function will import nodejs builtin-modules if they have not yet been
      +// imported.
      +
      +// State 3 - Modules already imported.
      +
      +    if (moduleFs !== undefined) {
      +        return;
      +    }
      +
      +// State 2 - Wait while modules are importing.
      +
      +    if (moduleFsInitResolveList !== undefined) {
      +        return new Promise(function (resolve) {
      +            moduleFsInitResolveList.push(resolve);
      +        });
      +    }
      +
      +// State 1 - Start importing modules.
      +
      +    moduleFsInitResolveList = [];
      +    [
      +        moduleChildProcess,
      +        moduleFs,
      +        modulePath,
      +        moduleUrl
      +    ] = await Promise.all([
      +        import("child_process"),
      +        import("fs"),
      +        import("path"),
      +        import("url")
      +    ]);
      +    while (moduleFsInitResolveList.length > 0) {
      +        moduleFsInitResolveList.shift()();
      +    }
      +}
    • +
    • Example usage:
      ...
      +let sourceJslintMjs;
      +let testCoverageMergeData;
      +
      +await (async function init() {
      +
      +// Coverage-hack - Ugly-hack to get test-coverage for all initialization-states.
      +
      +moduleFsInit();
      +moduleFsInit();
      +
      +// Cleanup directory .tmp
      +
      +await moduleFs.promises.rm(".tmp", {
      +    recursive: true
      +}).catch(noop);
      +...
    • + +
    • +

      + 22. + function noop(val) + +

      +
    • +
    • Description and source-code:
      function noop(val) {
      +
      +// This function will do nothing except return <val>.
      +
      +    return val;
      +}
    • +
    • Example usage:
      ...
      +jstestDescribe((
      +"test misc handling-behavior"
      +), function testBehaviorMisc() {
      +jstestIt((
      +    "test misc handling-behavior"
      +), async function () {
      +    // test debugInline handling-behavior
      +    noop(debugInline);
      +    // test assertErrorThrownAsync error handling-behavior
      +    await assertErrorThrownAsync(function () {
      +        return assertErrorThrownAsync(noop);
      +    });
      +    // test assertJsonEqual error handling-behavior
      +    await assertErrorThrownAsync(function () {
      +        assertJsonEqual(1, 2);
      +...
    • + +
    • +

      + 23. + function objectDeepCopyWithKeysSorted(obj) + +

      +
    • +
    • Description and source-code:
      function objectDeepCopyWithKeysSorted(obj) {
      +
      +// This function will recursively deep-copy <obj> with keys sorted.
      +
      +    let sorted;
      +    if (typeof obj !== "object" || !obj) {
      +        return obj;
      +    }
      +
      +// Recursively deep-copy list with child-keys sorted.
      +
      +    if (Array.isArray(obj)) {
      +        return obj.map(objectDeepCopyWithKeysSorted);
      +    }
      +
      +// Recursively deep-copy obj with keys sorted.
      +
      +    sorted = Object.create(null);
      +    Object.keys(obj).sort().forEach(function (key) {
      +        sorted[key] = objectDeepCopyWithKeysSorted(obj[key]);
      +    });
      +    return sorted;
      +}
    • +
    • Example usage:
      ...
      +        ];
      +        data1 = v8CoverageListMerge(data1);
      +        data1 = v8CoverageListMerge([data1]);
      +
      +// Debug data1.
      +// await moduleFs.promises.writeFile(
      +//     ".test_v8_coverage_node_sqlite_merged.json",
      +//     JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n"
      +// );
      +
      +        assertJsonEqual(data1, data2);
      +    });
      +});
      +
      +jstestDescribe((
      +...
    • + +
    • +

      + 24. + function v8CoverageListMerge(processCovs) + +

      +
    • +
    • Description and source-code:
      function v8CoverageListMerge(processCovs) {
      +
      +// This function is derived from MIT Licensed v8-coverage at
      +// https://github.com/demurgos/v8-coverage/tree/master/ts
      +// https://github.com/demurgos/v8-coverage/blob/master/ts/LICENSE.md
      +//
      +// Merges a list of v8 process coverages.
      +// The result is normalized.
      +// The input values may be mutated, it is not safe to use them after passing
      +// them to this function.
      +// The computation is synchronous.
      +// @param processCovs Process coverages to merge.
      +// @return Merged process coverage.
      +
      +    let resultMerged = [];      // List of merged scripts from processCovs.
      +    let urlToScriptDict = new Map();    // Map scriptCov.url to scriptCovs.
      +
      +    function compareRangeList(aa, bb) {
      +
      +// Compares two range coverages.
      +// The ranges are first ordered by ascending `startOffset` and then by
      +// descending `endOffset`.
      +// This corresponds to a pre-order tree traversal.
      +
      +        if (aa.startOffset !== bb.startOffset) {
      +            return aa.startOffset - bb.startOffset;
      +        }
      +        return bb.endOffset - aa.endOffset;
      +    }
      +
      +    function dictKeyValueAppend(dict, key, val) {
      +
      +// This function will append <val> to list <dict>[<key>].
      +
      +        let list = dict.get(key);
      +        if (list === undefined) {
      +            list = [];
      +            dict.set(key, list);
      +        }
      +        list.push(val);
      +    }
      +
      +    function mergeTreeList(parentTrees) {
      +
      +// This function will return RangeTree object with <parentTrees> merged into
      +// property-children.
      +// @precondition Same `start` and `end` for all the parentTrees
      +
      +        if (parentTrees.length <= 1) {
      +            return parentTrees[0];
      +        }
      +
      +// new RangeTree().
      +
      +        return {
      +
      +// Merge parentTrees into property-children.
      +
      +            children: mergeTreeListToChildren(parentTrees),
      +            delta: parentTrees.reduce(function (aa, bb) {
      +                return aa + bb.delta;
      +            }, 0),
      +            end: parentTrees[0].end,
      +            start: parentTrees[0].start
      +   ...
      +}
      +
    • +
    • Example usage:
      ...
      +            "test_v8_coverage_node_sqlite_13216_1633662333140_0.json"
      +        ].map(function (file) {
      +            return testCoverageMergeData[file];
      +        });
      +        let data2 = testCoverageMergeData[
      +            "test_v8_coverage_node_sqlite_merged.json"
      +        ];
      +        data1 = v8CoverageListMerge(data1);
      +        data1 = v8CoverageListMerge([data1]);
      +
      +// Debug data1.
      +// await moduleFs.promises.writeFile(
      +//     ".test_v8_coverage_node_sqlite_merged.json",
      +//     JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n"
      +// );
      +...
    • + +
    • +

      + 25. + function v8CoverageReportCreate({ + consoleError, + coverageDir, + processArgv = [] +}) + +

      +
    • +
    • Description and source-code:
      async function v8CoverageReportCreate({
      +    consoleError,
      +    coverageDir,
      +    processArgv = []
      +}) {
      +
      +// This function will create html-coverage-reports directly from
      +// v8-coverage-files in <coverageDir>.
      +// 1. Spawn node.js program <processArgv> with NODE_V8_COVERAGE.
      +// 2. Merge JSON v8-coverage-files in <coverageDir>.
      +// 3. Create html-coverage-reports in <coverageDir>.
      +
      +    let cwd;
      +    let excludeList = [];
      +    let exitCode = 0;
      +    let fileDict;
      +    let includeList = [];
      +    let modeIncludeNodeModules;
      +    let processArgElem;
      +    let promiseList = [];
      +    let v8CoverageObj;
      +
      +    function htmlRender({
      +        fileList,
      +        lineList,
      +        modeIndex,
      +        pathname
      +    }) {
      +        let html;
      +        let padLines;
      +        let padPathname;
      +        let txt;
      +        let txtBorder;
      +        html = "";
      +        html += String(`
      +<!DOCTYPE html>
      +<html lang="en">
      +<head>
      +<title>V8 Coverage Report</title>
      +<style>
      +/* jslint utility2:true */
      +/*csslint ignore:start*/
      +.coverage,
      +.coverage a,
      +.coverage div,
      +.coverage pre,
      +.coverage span,
      +.coverage table,
      +.coverage tbody,
      +.coverage td,
      +.coverage th,
      +.coverage thead,
      +.coverage tr {
      +    box-sizing: border-box;
      +    font-family: monospace;
      +}
      +/*csslint ignore:end*/
      +
      +/* css - coverage_report - general */
      +body {
      +    margin: 0;
      +}
      +.coverage pre {
      +    margin: 5px 0;
      +}
      +.coverage table {
      +    border-collapse: collapse;
      +}
      +.coverage td,
      +.coverage th {
      +    border: 1px solid #777;
      +    line-height: 20px;
      +    margin: 0;
      +    padding: 5px 10px;
      +}
      +.coverage td span {
      +    display: inline-block;
      +    width: 100%;
      +}
      +.coverage .content {
      +    padding: 0 5px;
      +}
      +.coverage .content a {
      +    text-decoration: none;
      +}
      +.coverage .count {
      +    margin: 0 5px;
      +    padding: 0 5px;
      +}
      +.coverage .footer,
      +.coverage .header {
      +    padding: 20px;
      +}
      +.coverage .footer {
      +    text-align: center;
      +}
      +.coverage .percentbar {
      +    height: 12px;
      +    margin: 2px 0;
      +    min-width: 200px;
      +    position: relative;
      +    width: 100%;...
      +}
      +
    • +
    • Example usage:
      ...
      +
      +/*jslint node*/
      +import jslint from "../jslint.mjs";
      +(async function () {
      +
      +// Create V8 coverage report from program `npm run test` in javascript.
      +
      +await jslint.v8CoverageReportCreate({
      +    coverageDir: "../.artifact/coverage_sqlite3_js/",
      +    processArgv: [
      +        "--exclude=tes?/",
      +        "--exclude=tes[!0-9A-Z_a-z-]/",
      +        "--exclude=tes[0-9A-Z_a-z-]/",
      +        "--exclude=tes[^0-9A-Z_a-z-]/",
      +        "--exclude=test/**/*.js",
      +...
    • + +
    • +

      + 26. + string jslint_charset_ascii + +

      +
    • + +
    • +

      + 27. + string jslint_edition + +

      +
    • + +
    +
    + +
    + [ + This document was created with + JSLint + ] +
    +
    + + diff --git a/branch-master/.artifact/asset_image_logo_128.png b/branch-master/.artifact/asset_image_logo_128.png new file mode 100644 index 000000000..459607cd2 Binary files /dev/null and b/branch-master/.artifact/asset_image_logo_128.png differ diff --git a/branch-master/.artifact/asset_image_logo_256.png b/branch-master/.artifact/asset_image_logo_256.png new file mode 100644 index 000000000..8bce5e201 Binary files /dev/null and b/branch-master/.artifact/asset_image_logo_256.png differ diff --git a/branch-master/.artifact/asset_image_logo_32.png b/branch-master/.artifact/asset_image_logo_32.png new file mode 100644 index 000000000..6720f7eb9 Binary files /dev/null and b/branch-master/.artifact/asset_image_logo_32.png differ diff --git a/branch-master/.artifact/asset_image_logo_512.png b/branch-master/.artifact/asset_image_logo_512.png new file mode 100644 index 000000000..585908e3a Binary files /dev/null and b/branch-master/.artifact/asset_image_logo_512.png differ diff --git a/branch-master/.artifact/asset_image_logo_64.png b/branch-master/.artifact/asset_image_logo_64.png new file mode 100644 index 000000000..1971ed8fd Binary files /dev/null and b/branch-master/.artifact/asset_image_logo_64.png differ diff --git a/branch-master/.artifact/coverage/coverage_badge.svg b/branch-master/.artifact/coverage/coverage_badge.svg new file mode 100644 index 000000000..5e137a1ce --- /dev/null +++ b/branch-master/.artifact/coverage/coverage_badge.svg @@ -0,0 +1,14 @@ + + + + +coverage +100.00 % + + diff --git a/branch-master/.artifact/coverage/coverage_report.txt b/branch-master/.artifact/coverage/coverage_report.txt new file mode 100644 index 000000000..18fd858a1 --- /dev/null +++ b/branch-master/.artifact/coverage/coverage_report.txt @@ -0,0 +1,16 @@ +V8 Coverage Report ++----------------------------------+-------------------+-------------------+ +| Files covered | Lines | Remaining | ++----------------------------------+-------------------+-------------------+ +| ./ | 100.00 % | | +| ******************************** | 13199 / 13199 | 0 / 13199 | ++----------------------------------+-------------------+-------------------+ +| ./jslint.mjs | 100.00 % | | +| ******************************** | 11574 / 11574 | 0 / 11574 | ++----------------------------------+-------------------+-------------------+ +| ./jslint_wrapper_cjs.cjs | 100.00 % | | +| ******************************** | 49 / 49 | 0 / 49 | ++----------------------------------+-------------------+-------------------+ +| ./test.mjs | 100.00 % | | +| ******************************** | 1576 / 1576 | 0 / 1576 | ++----------------------------------+-------------------+-------------------+ diff --git a/branch-master/.artifact/coverage/index.html b/branch-master/.artifact/coverage/index.html new file mode 100644 index 000000000..38a80ec05 --- /dev/null +++ b/branch-master/.artifact/coverage/index.html @@ -0,0 +1,212 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . /
    +
    +
    +
    +
    + 100.00 %
    + 13199 / 13199 +
    +
    + 0 / 13199 +
    + . / jslint.mjs
    +
    +
    +
    +
    + 100.00 %
    + 11574 / 11574 +
    +
    + 0 / 11574 +
    + . / jslint_wrapper_cjs.cjs
    +
    +
    +
    +
    + 100.00 %
    + 49 / 49 +
    +
    + 0 / 49 +
    + . / test.mjs
    +
    +
    +
    +
    + 100.00 %
    + 1576 / 1576 +
    +
    + 0 / 1576 +
    +
    + + + + diff --git a/branch-master/.artifact/coverage/jslint.mjs.html b/branch-master/.artifact/coverage/jslint.mjs.html new file mode 100644 index 000000000..a140d315f --- /dev/null +++ b/branch-master/.artifact/coverage/jslint.mjs.html @@ -0,0 +1,11742 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / jslint.mjs
    +
    +
    +
    +
    + 100.00 %
    + 11574 / 11574 +
    +
    + 0 / 11574 +
    +
    + + +
    +
        1.      1// #!/usr/bin/env node
    +
        2.      1// JSLint
    +
        3.      1
    +
        4.      1// The Unlicense
    +
        5.      1//
    +
        6.      1// This is free and unencumbered software released into the public domain.
    +
        7.      1//
    +
        8.      1// Anyone is free to copy, modify, publish, use, compile, sell, or
    +
        9.      1// distribute this software, either in source code form or as a compiled
    +
       10.      1// binary, for any purpose, commercial or non-commercial, and by any
    +
       11.      1// means.
    +
       12.      1//
    +
       13.      1// In jurisdictions that recognize copyright laws, the author or authors
    +
       14.      1// of this software dedicate any and all copyright interest in the
    +
       15.      1// software to the public domain. We make this dedication for the benefit
    +
       16.      1// of the public at large and to the detriment of our heirs and
    +
       17.      1// successors. We intend this dedication to be an overt act of
    +
       18.      1// relinquishment in perpetuity of all present and future rights to this
    +
       19.      1// software under copyright law.
    +
       20.      1//
    +
       21.      1// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    +
       22.      1// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    +
       23.      1// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    +
       24.      1// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
    +
       25.      1// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
    +
       26.      1// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    +
       27.      1// OTHER DEALINGS IN THE SOFTWARE.
    +
       28.      1
    +
       29.      1
    +
       30.      1// jslint(source, option_dict, global_list) is a function that takes 3
    +
       31.      1// arguments. The second two arguments are optional.
    +
       32.      1
    +
       33.      1//      source          A text to analyze.
    +
       34.      1//      option_dict     An object whose keys correspond to option names.
    +
       35.      1//      global_list     An array of strings containing global variables that
    +
       36.      1//                      the file is allowed readonly access.
    +
       37.      1
    +
       38.      1// jslint returns an object containing its results. The object contains a lot
    +
       39.      1// of valuable information. It can be used to generate reports. The object
    +
       40.      1// contains:
    +
       41.      1
    +
       42.      1//      directives: an array of directive comment tokens.
    +
       43.      1//      edition: the version of JSLint that did the analysis.
    +
       44.      1//      exports: the names exported from the module.
    +
       45.      1//      froms: an array of strings representing each of the imports.
    +
       46.      1//      functions: an array of objects that represent all functions
    +
       47.      1//              declared in the file.
    +
       48.      1//      global: an object representing the global object. Its .context property
    +
       49.      1//              is an object containing a property for each global variable.
    +
       50.      1//      id: "(JSLint)"
    +
       51.      1//      json: true if the file is a JSON text.
    +
       52.      1//      lines: an array of strings, the source.
    +
       53.      1//      module: true if an import or export statement was used.
    +
       54.      1//      ok: true if no warnings were generated. This is what you want.
    +
       55.      1//      option: the option argument.
    +
       56.      1//      property: a property object.
    +
       57.      1//      stop: true if JSLint was unable to finish. You don't want this.
    +
       58.      1//      tokens: an array of objects representing the tokens in the file.
    +
       59.      1//      tree: the token objects arranged in a tree.
    +
       60.      1//      warnings: an array of warning objects. A warning object can contain:
    +
       61.      1//          name: "JSLintError"
    +
       62.      1//          column: A column number in the file.
    +
       63.      1//          line: A line number in the file.
    +
       64.      1//          code: A warning code string.
    +
       65.      1//          message: The warning message string.
    +
       66.      1//          a: Exhibit A.
    +
       67.      1//          b: Exhibit B.
    +
       68.      1//          c: Exhibit C.
    +
       69.      1//          d: Exhibit D.
    +
       70.      1
    +
       71.      1// jslint works in several phases. In any of these phases, errors might be
    +
       72.      1// found. Sometimes JSLint is able to recover from an error and continue
    +
       73.      1// parsing. In some cases, it cannot and will stop early. If that should happen,
    +
       74.      1// repair your code and try again.
    +
       75.      1
    +
       76.      1// Phases:
    +
       77.      1
    +
       78.      1// PHASE 1. Split <source> by newlines into <line_list>.
    +
       79.      1// PHASE 2. Lex <line_list> into <token_list>.
    +
       80.      1// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
    +
       81.      1// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
    +
       82.      1//          recursive traversal. Each node may be processed on the way down
    +
       83.      1//          (preaction) and on the way up (postaction).
    +
       84.      1// PHASE 5. Check whitespace between tokens in <token_list>.
    +
       85.      1
    +
       86.      1// jslint can also examine JSON text. It decides that a file is JSON text if
    +
       87.      1// the first token is "[" or "{". Processing of JSON text is much simpler than
    +
       88.      1// the processing of JavaScript programs. Only the first three phases are
    +
       89.      1// required.
    +
       90.      1
    +
       91.      1// WARNING: JSLint will hurt your feelings.
    +
       92.      1
    +
       93.      1/*jslint beta, node*/
    +
       94.      1/*property
    +
       95.      1    JSLINT_BETA, NODE_V8_COVERAGE, a, all, argv, arity, artifact,
    +
       96.      1    assertErrorThrownAsync, assertJsonEqual, assertOrThrow, assign, async, b,
    +
       97.      1    beta, bitwise, block, body, browser, c, calls, catch, catch_list,
    +
       98.      1    catch_stack, causes, char, children, clear, closer, closure, code, column,
    +
       99.      1    concat, consoleError, console_error, console_log, constant, context,
    +
      100.      1    convert, count, coverageDir, create, cwd, d, dead, debugInline, default,
    +
      101.      1    delta, devel, directive, directive_ignore_line, directive_list, directives,
    +
      102.      1    dirname, disrupt, dot, edition, elem_list, ellipsis, else, end, endOffset,
    +
      103.      1    endsWith, entries, env, error, eval, every, example_list, excludeList, exec,
    +
      104.      1    execArgv, exit, exitCode, export_dict, exports, expression, extra, fart,
    +
      105.      1    file, fileList, fileURLToPath, filter, finally, flag, floor, for, forEach,
    +
      106.      1    formatted_message, free, freeze, from, froms, fsWriteFileWithParents,
    +
      107.      1    fud_stmt, functionName, function_list, function_stack, functions, get,
    +
      108.      1    getset, github_repo, globExclude, global, global_dict, global_list,
    +
      109.      1    holeList, htmlEscape, id, identifier, import, import_list, import_meta_url,
    +
      110.      1    inc, includeList, indent2, index, indexOf, init, initial, isArray,
    +
      111.      1    isBlockCoverage, isHole, isNaN, is_equal, is_weird, join, jslint,
    +
      112.      1    jslint_apidoc, jslint_assert, jslint_charset_ascii, jslint_cli,
    +
      113.      1    jslint_edition, jslint_phase1_split, jslint_phase2_lex, jslint_phase3_parse,
    +
      114.      1    jslint_phase4_walk, jslint_phase5_whitage, jslint_report, json,
    +
      115.      1    jstestDescribe, jstestIt, jstestOnExit, keys, label, lbp, led_infix, length,
    +
      116.      1    level, line, lineList, line_list, line_offset, line_source, lines,
    +
      117.      1    linesCovered, linesTotal, live, log, long, loop, m, map, margin, match, max,
    +
      118.      1    message, meta, min, mkdir, modeCoverageIgnoreFile, modeIndex, mode_cli,
    +
      119.      1    mode_conditional, mode_json, mode_module, mode_noop, mode_property,
    +
      120.      1    mode_shebang, mode_stop, module, moduleFsInit, moduleName, module_list,
    +
      121.      1    name, names, node, nomen, noop, now, nr, nud_prefix,
    +
      122.      1    objectDeepCopyWithKeysSorted, ok, on, open, opening, option, option_dict,
    +
      123.      1    order, package_name, padEnd, padStart, parameters, parent, parentIi, parse,
    +
      124.      1    pathname, pathnameList, platform, pop, processArgv, process_argv,
    +
      125.      1    process_env, process_exit, promises, property, property_dict, push, quote,
    +
      126.      1    ranges, readFile, readdir, readonly, recursive, reduce, repeat, replace,
    +
      127.      1    resolve, result, reverse, role, round, scriptId, search, set, shebang,
    +
      128.      1    shell, shift, signature, single, slice, some, sort, source, spawn, splice,
    +
      129.      1    split, stack, stack_trace, start, startOffset, startsWith, statement,
    +
      130.      1    statement_prv, stdio, stop, stop_at, stringify, subscript, switch,
    +
      131.      1    syntax_dict, tenure, test, test_cause, test_internal_error, this, thru,
    +
      132.      1    toLocaleString, toString, token, token_global, token_list, token_nxt,
    +
      133.      1    token_tree, tokens, trace, tree, trim, trimEnd, trimRight, try, type,
    +
      134.      1    unlink, unordered, unshift, url, used, v8CoverageListMerge,
    +
      135.      1    v8CoverageReportCreate, value, variable, version, versions, warn, warn_at,
    +
      136.      1    warning, warning_list, warnings, white, wrapped, writeFile
    +
      137.      1*/
    +
      138.      1
    +
      139.      1// init debugInline
    +
      140.      1let debugInline = (function () {
    +
      141.      3    let __consoleError = function () {
    +
      142.      3        return;
    +
      143.      3    };
    +
      144.      1    function debug(...argv) {
    +
      145.      1
    +
      146.      1// This function will print <argv> to stderr and then return <argv>[0].
    +
      147.      1
    +
      148.      1        __consoleError("\n\ndebugInline");
    +
      149.      1        __consoleError(...argv);
    +
      150.      1        __consoleError("\n");
    +
      151.      1        return argv[0];
    +
      152.      1    }
    +
      153.      1    debug(); // Coverage-hack.
    +
      154.      1    __consoleError = console.error; //jslint-ignore-line
    +
      155.      1    return debug;
    +
      156.      1}());
    +
      157.      1let jslint_charset_ascii = (
    +
      158.      1    "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007"
    +
      159.      1    + "\b\t\n\u000b\f\r\u000e\u000f"
    +
      160.      1    + "\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017"
    +
      161.      1    + "\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f"
    +
      162.      1    + " !\"#$%&'()*+,-./0123456789:;<=>?"
    +
      163.      1    + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
    +
      164.      1    + "`abcdefghijklmnopqrstuvwxyz{|}~\u007f"
    +
      165.      1);
    +
      166.      1let jslint_edition = "v2024.11.24";
    +
      167.      1let jslint_export;                      // The jslint object to be exported.
    +
      168.      1let jslint_fudge = 1;                   // Fudge starting line and starting
    +
      169.      1                                        // ... column to 1.
    +
      170.      1let jslint_import_meta_url = "";        // import.meta.url used by cli.
    +
      171.      1let jslint_rgx_cap = (
    +
      172.      1    /^[A-Z]/
    +
      173.      1);
    +
      174.      1let jslint_rgx_crlf = (
    +
      175.      1    /\n|\r\n?/
    +
      176.      1);
    +
      177.      1let jslint_rgx_digits_bits = (
    +
      178.      1    /^[01_]*/
    +
      179.      1);
    +
      180.      1let jslint_rgx_digits_decimals = (
    +
      181.      1    /^[0-9_]*/
    +
      182.      1);
    +
      183.      1let jslint_rgx_digits_hexs = (
    +
      184.      1    /^[0-9A-F_]*/i
    +
      185.      1);
    +
      186.      1let jslint_rgx_digits_octals = (
    +
      187.      1    /^[0-7_]*/
    +
      188.      1);
    +
      189.      1let jslint_rgx_directive = (
    +
      190.      1    /^(jslint|property|global)\s+(.*)$/
    +
      191.      1);
    +
      192.      1let jslint_rgx_directive_part = (
    +
      193.      1    /([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*|$/g
    +
      194.      1);
    +
      195.      1let jslint_rgx_identifier = (
    +
      196.      1    /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/
    +
      197.      1);
    +
      198.      1let jslint_rgx_json_number = (
    +
      199.      1
    +
      200.      1// https://datatracker.ietf.org/doc/html/rfc7159#section-6
    +
      201.      1// number = [ minus ] int [ frac ] [ exp ]
    +
      202.      1
    +
      203.      1    /^-?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][\-+]?\d+)?$/
    +
      204.      1);
    +
      205.      1let jslint_rgx_mega = (
    +
      206.      1
    +
      207.      1// Vim-hack - vim-editor has trouble parsing naked '`' in regexp
    +
      208.      1
    +
      209.      1    /[\u0060\\]|\$\{/
    +
      210.      1);
    +
      211.      1let jslint_rgx_module = (
    +
      212.      1    /^[a-zA-Z0-9_$:.@\-\/]+$/
    +
      213.      1);
    +
      214.      1let jslint_rgx_numeric_separator_illegal = (
    +
      215.      1    /__|_$|_n$/m
    +
      216.      1);
    +
      217.      1let jslint_rgx_slash_star_or_slash = (
    +
      218.      1    /\/\*|\/$/
    +
      219.      1);
    +
      220.      1let jslint_rgx_tab = (
    +
      221.      1    /\t/g
    +
      222.      1);
    +
      223.      1let jslint_rgx_todo = (
    +
      224.      1    /\b(?:todo|TO\s?DO|HACK)\b/
    +
      225.      1);
    +
      226.      1let jslint_rgx_token = new RegExp(
    +
      227.      1    "^("
    +
      228.      1    + "(\\s+)"
    +
      229.      1    + "|([a-zA-Z_$][a-zA-Z0-9_$]*)"
    +
      230.      1    + "|[(){}\\[\\],:;'\"~\\`]"
    +
      231.      1    + "|\\?[?.]?"
    +
      232.      1    + "|=(?:==?|>)?"
    +
      233.      1    + "|\\.+"
    +
      234.      1    + "|\\*[*\\/=]?"
    +
      235.      1    + "|\\/[*\\/]?"
    +
      236.      1    + "|\\+[=+]?"
    +
      237.      1    + "|-[=\\-]?"
    +
      238.      1    + "|[\\^%]=?"
    +
      239.      1    + "|&[&=]?"
    +
      240.      1    + "|\\"
    +
      241.      1    + "|[|=]?"
    +
      242.      1    + "|>{1,3}=?"
    +
      243.      1    + "|<<?=?"
    +
      244.      1    + "|!(?:!|==?)?"
    +
      245.      1
    +
      246.      1// PR-351 - Add BigInt support.
    +
      247.      1// PR-390 - Add numeric-separator support.
    +
      248.      1
    +
      249.      1    + "|((?:0_?|[1-9][0-9_]*)n?)"
    +
      250.      1    + ")"
    +
      251.      1    + "(.*)$"
    +
      252.      1);
    +
      253.      1let jslint_rgx_url_search_window_jslint = (
    +
      254.      1    /[&?]window_jslint=1(?:$|&)/m
    +
      255.      1);
    +
      256.      1let jslint_rgx_weird_property = (
    +
      257.      1    /^_|\$|Sync$|_$/m
    +
      258.      1);
    +
      259.      1let jstestCountFailed = 0;
    +
      260.      1let jstestCountTotal = 0;
    +
      261.      1let jstestItCount = 0;
    +
      262.      1let jstestItList = [];
    +
      263.      1let jstestTimeStart;
    +
      264.      1let moduleChildProcess;
    +
      265.      1let moduleFs;
    +
      266.      1let moduleFsInitResolveList;
    +
      267.      1let modulePath;
    +
      268.      1let moduleUrl;
    +
      269.      1
    +
      270.     11async function assertErrorThrownAsync(asyncFunc, regexp) {
    +
      271.     11
    +
      272.     11// This function will assert calling <asyncFunc> throws an error.
    +
      273.     11
    +
      274.     11    let err;
    +
      275.     11    try {
    +
      276.      1        await asyncFunc();
    +
      277.     10    } catch (errCaught) {
    +
      278.     10        err = errCaught;
    +
      279.     10    }
    +
      280.     11    assertOrThrow(err, "No error thrown.");
    +
      281.     11    assertOrThrow(
    +
      282.      4        !regexp || new RegExp(regexp).test(err.message),
    +
      283.     11        err
    +
      284.     11    );
    +
      285.     11}
    +
      286.      1
    +
      287.    267function assertJsonEqual(aa, bb, message) {
    +
      288.    267
    +
      289.    267// This function will assert JSON.stringify(<aa>) === JSON.stringify(<bb>).
    +
      290.    267
    +
      291.    267    aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa), undefined, 1);
    +
      292.    267    bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb), undefined, 1);
    +
      293.      3    if (aa !== bb) {
    +
      294.      3        throw new Error(
    +
      295.      3            "\n" + aa + "\n!==\n" + bb
    +
      296.      3            + (
    +
      297.      3                typeof message === "string"
    +
      298.      3                ? " - " + message
    +
      299.      3                : message
    +
      300.      3                ? " - " + JSON.stringify(message)
    +
      301.      3                : ""
    +
      302.      3            )
    +
      303.      3        );
    +
      304.      3    }
    +
      305.    267}
    +
      306.      1
    +
      307.   1931function assertOrThrow(condition, message) {
    +
      308.   1931
    +
      309.   1931// This function will throw <message> if <condition> is falsy.
    +
      310.   1931
    +
      311.      4    if (!condition) {
    +
      312.      4        throw (
    +
      313.      4            (!message || typeof message === "string")
    +
      314.      4            ? new Error(String(message).slice(0, 2048))
    +
      315.      4            : message
    +
      316.      4        );
    +
      317.      4    }
    +
      318.   1931}
    +
      319.      1
    +
      320.  94133function empty() {
    +
      321.  94133
    +
      322.  94133// The empty function produces a new empty object that inherits nothing. This is
    +
      323.  94133// much better than '{}' because confusions around accidental method names like
    +
      324.  94133// 'constructor' are completely avoided.
    +
      325.  94133
    +
      326.  94133    return Object.create(null);
    +
      327.  94133}
    +
      328.      1
    +
      329.     59async function fsWriteFileWithParents(pathname, data) {
    +
      330.     59
    +
      331.     59// This function will write <data> to <pathname> and lazy-mkdirp if necessary.
    +
      332.     59
    +
      333.     59    await moduleFsInit();
    +
      334.     59
    +
      335.     59// Try writing to pathname.
    +
      336.     59
    +
      337.     59    try {
    +
      338.     41        await moduleFs.promises.writeFile(pathname, data);
    +
      339.     41    } catch (ignore) {
    +
      340.     18
    +
      341.     18// Lazy mkdirp.
    +
      342.     18
    +
      343.     18        await moduleFs.promises.mkdir(modulePath.dirname(pathname), {
    +
      344.     18            recursive: true
    +
      345.     18        });
    +
      346.     18
    +
      347.     18// Retry writing to pathname.
    +
      348.     18
    +
      349.     18        await moduleFs.promises.writeFile(pathname, data);
    +
      350.     18    }
    +
      351.     59    console.error("wrote file " + pathname);
    +
      352.     59}
    +
      353.      1
    +
      354.    184function globExclude({
    +
      355.    184    excludeList = [],
    +
      356.    184    includeList = [],
    +
      357.    184    pathnameList = []
    +
      358.    184}) {
    +
      359.    184
    +
      360.    184// This function will
    +
      361.    184// 1. Exclude pathnames in <pathnameList> that don't match glob-patterns in
    +
      362.    184//    <includeList>.
    +
      363.    184// 2. Exclude pathnames in <pathnameList> that match glob-patterns in
    +
      364.    184//    <excludeList>.
    +
      365.    184
    +
      366.    552    function globAssertNotWeird(list, name) {
    +
      367.    552
    +
      368.    552// This function will check if <list> of strings contain weird characters.
    +
      369.    552
    +
      370.    552        [
    +
      371.    552            [
    +
      372.    552                "\n", (
    +
      373.    552                    /^.*?([\u0000-\u0007\r]).*/gm
    +
      374.    552                )
    +
      375.    552            ],
    +
      376.    552            [
    +
      377.    552                "\r", (
    +
      378.    552                    /^.*?([\n]).*/gm
    +
      379.    552                )
    +
      380.    552            ]
    +
      381.   1102        ].forEach(function ([
    +
      382.   1102            separator, rgx
    +
      383.   1102        ]) {
    +
      384.      3            list.join(separator).replace(rgx, function (match0, char) {
    +
      385.      3                throw new Error(
    +
      386.      3                    "Weird character "
    +
      387.      3                    + JSON.stringify(char)
    +
      388.      3                    + " found in " + name + " "
    +
      389.      3                    + JSON.stringify(match0)
    +
      390.      3                );
    +
      391.      3            });
    +
      392.   1102        });
    +
      393.    552    }
    +
      394.    184
    +
      395.   1370    function globToRegexp(pattern) {
    +
      396.   1370
    +
      397.   1370// This function will translate glob <pattern> to javascript-regexp,
    +
      398.   1370// which javascript can then use to "glob" pathnames.
    +
      399.   1370
    +
      400.   1370        let ii = 0;
    +
      401.   1370        let isClass = false;
    +
      402.   1370        let strClass = "";
    +
      403.   1370        let strRegex = "";
    +
      404.   1370        pattern = pattern.replace((
    +
      405.   1370            /\/\/+/g
    +
      406.   1370        ), "/");
    +
      407.   1370        pattern = pattern.replace((
    +
      408.   1370            /\*\*\*+/g
    +
      409.   1370        ), "**");
    +
      410.   1370        pattern.replace((
    +
      411.   1370            /\\\\|\\\[|\\\]|\[|\]|./g
    +
      412.  18691        ), function (match0) {
    +
      413.  18691            switch (match0) {
    +
      414.    310            case "[":
    +
      415.    310                if (isClass) {
    +
      416.    310                    strClass += "[";
    +
      417.    310                    return;
    +
      418.    310                }
    +
      419.    310                strClass += "\u0000";
    +
      420.    310                strRegex += "\u0000";
    +
      421.    310                isClass = true;
    +
      422.    310                return;
    +
      423.    310            case "]":
    +
      424.    310                if (isClass) {
    +
      425.    310                    isClass = false;
    +
      426.    310                    return;
    +
      427.    310                }
    +
      428.    310                strRegex += "]";
    +
      429.    310                return;
    +
      430.  18071            default:
    +
      431.  18071                if (isClass) {
    +
      432.  18071                    strClass += match0;
    +
      433.  18071                    return;
    +
      434.  18071                }
    +
      435.  18071                strRegex += match0;
    +
      436.  15021            }
    +
      437.  15021            return "";
    +
      438.  15021        });
    +
      439.   1370        strClass += "\u0000";
    +
      440.   1370
    +
      441.   1370// An expression "[!...]" matches a single character, namely any character that
    +
      442.   1370// is not matched by the expression obtained by removing the first '!' from it.
    +
      443.   1370// (Thus, "[!a-]" matches any single character except 'a', and '-'.)
    +
      444.   1370
    +
      445.   1370        strClass = strClass.replace((
    +
      446.   1370            /\u0000!/g
    +
      447.   1370        ), "\u0000^");
    +
      448.   1370
    +
      449.   1370// One may include '-' in its literal meaning by making it the first or last
    +
      450.   1370// character between the brackets.
    +
      451.   1370
    +
      452.   1370        strClass = strClass.replace((
    +
      453.   1370            /\u0000-/g
    +
      454.   1370        ), "\u0000\\-");
    +
      455.   1370        strClass = strClass.replace((
    +
      456.   1370            /-\u0000/g
    +
      457.   1370        ), "\\-\u0000");
    +
      458.   1370
    +
      459.   1370// Escape brackets '[', ']' in character class.
    +
      460.   1370
    +
      461.   1370        strClass = strClass.replace((
    +
      462.   1370            /[\[\]]/g
    +
      463.   1370        ), "\\$&");
    +
      464.   1370
    +
      465.   1370// https://stackoverflow.com/questions/3561493
    +
      466.   1370// /is-there-a-regexp-escape-function-in-javascript
    +
      467.   1370// $()*+-./?[\]^{|}
    +
      468.   1370
    +
      469.   1370        strRegex = strRegex.replace((
    +
      470.   1370
    +
      471.   1370// Ignore [-/].
    +
      472.   1370
    +
      473.   1370            /[$()*+.?\[\\\]\^{|}]/g
    +
      474.   1370        ), "\\$&");
    +
      475.   1370
    +
      476.   1370// Expand wildcard '**/*'.
    +
      477.   1370
    +
      478.   1370        strRegex = strRegex.replace((
    +
      479.   1370            /\\\*\\\*\/(?:\\\*)+/g
    +
      480.   1370        ), ".*?");
    +
      481.   1370
    +
      482.   1370// Expand wildcard '**'.
    +
      483.   1370
    +
      484.   1370        strRegex = strRegex.replace((
    +
      485.   1370            /(^|\/)\\\*\\\*(\/|$)/gm
    +
      486.   1370        ), "$1.*?$2");
    +
      487.   1370
    +
      488.   1370// Expand wildcard '*'.
    +
      489.   1370
    +
      490.   1370        strRegex = strRegex.replace((
    +
      491.   1370            /(?:\\\*)+/g
    +
      492.   1370        ), "[^\\/]*?");
    +
      493.   1370
    +
      494.   1370// Expand wildcard '?'.
    +
      495.   1370
    +
      496.   1370        strRegex = strRegex.replace((
    +
      497.   1370            /\\\?/g
    +
      498.   1370        ), "[^\\/]");
    +
      499.   1370
    +
      500.   1370// Expand directory-with-trailing-slash '.../'.
    +
      501.   1370
    +
      502.   1370        strRegex = strRegex.replace((
    +
      503.   1370            /\/$/gm
    +
      504.   1370        ), "\\/.*?");
    +
      505.   1370
    +
      506.   1370// Merge strClass into strRegex.
    +
      507.   1370
    +
      508.   1370        ii = 0;
    +
      509.   1370        strClass = strClass.split("\u0000");
    +
      510.   1370        strRegex = strRegex.replace((
    +
      511.   1370            /\u0000/g
    +
      512.    306        ), function () {
    +
      513.    306            ii += 1;
    +
      514.      2            if (strClass[ii] === "") {
    +
      515.      2                return "";
    +
      516.    304            }
    +
      517.    304            return "[" + strClass[ii] + "]";
    +
      518.    304        });
    +
      519.   1370
    +
      520.   1370// Change strRegex from string to regexp.
    +
      521.   1370
    +
      522.   1370        strRegex = new RegExp("^" + strRegex + "$", "gm");
    +
      523.   1370        return strRegex;
    +
      524.   1370    }
    +
      525.    184
    +
      526.    184// Validate excludeList, includeList, pathnameList.
    +
      527.    184
    +
      528.    184    globAssertNotWeird(excludeList, "pattern");
    +
      529.    184    globAssertNotWeird(includeList, "pattern");
    +
      530.    184    globAssertNotWeird(pathnameList, "pathname");
    +
      531.    184
    +
      532.    184// Optimization
    +
      533.    184// Concat pathnames into a single, newline-separated string,
    +
      534.    184// whose pathnames can all be filtered with a single, regexp-pass.
    +
      535.    184
    +
      536.    184    pathnameList = pathnameList.join("\n");
    +
      537.    184
    +
      538.    184// 1. Exclude pathnames in <pathnameList> that don't match glob-patterns in
    +
      539.    184//    <includeList>.
    +
      540.    184
    +
      541.    142    if (includeList.length > 0) {
    +
      542.    142        includeList = includeList.map(globToRegexp);
    +
      543.    574        includeList.forEach(function (pattern) {
    +
      544.    574            pathnameList = pathnameList.replace(pattern, "\u0000$&");
    +
      545.    574        });
    +
      546.    142        pathnameList = pathnameList.replace((
    +
      547.    142            /^[^\u0000].*/gm
    +
      548.    142        ), "");
    +
      549.    142        pathnameList = pathnameList.replace((
    +
      550.    142            /^\u0000+/gm
    +
      551.    142        ), "");
    +
      552.    181    }
    +
      553.    181
    +
      554.    181// 2. Exclude pathnames in <pathnameList> that match glob-patterns in
    +
      555.    181//    <excludeList>.
    +
      556.    181
    +
      557.    181    excludeList = excludeList.map(globToRegexp);
    +
      558.    796    excludeList.forEach(function (pattern) {
    +
      559.    796        pathnameList = pathnameList.replace(pattern, "");
    +
      560.    796    });
    +
      561.    181
    +
      562.    181// Split newline-separated pathnames back to list.
    +
      563.    181
    +
      564.  10117    pathnameList = pathnameList.split("\n").filter(function (elem) {
    +
      565.  10117        return elem;
    +
      566.  10117    });
    +
      567.    181    return {
    +
      568.    181        excludeList,
    +
      569.    181        includeList,
    +
      570.    181        pathnameList
    +
      571.    181    };
    +
      572.    181}
    +
      573.      1
    +
      574.  14214function htmlEscape(str) {
    +
      575.  14214
    +
      576.  14214// This function will make <str> html-safe by escaping & < >.
    +
      577.  14214
    +
      578.  14214    return String(str).replace((
    +
      579.  14214        /&/g
    +
      580.  14214    ), "&amp;").replace((
    +
      581.  14214        /</g
    +
      582.  14214    ), "&lt;").replace((
    +
      583.  14214        />/g
    +
      584.  14214    ), "&gt;");
    +
      585.  14214}
    +
      586.      1
    +
      587.    668function jslint(
    +
      588.    668    source = "",                // A text to analyze.
    +
      589.    668    option_dict = empty(),      // An object whose keys correspond to option
    +
      590.    668                                // ... names.
    +
      591.    668    global_list = []            // An array of strings containing global
    +
      592.    668                                // ... variables that the file is allowed
    +
      593.    668                                // ... readonly access.
    +
      594.    668) {
    +
      595.    668
    +
      596.    668// The jslint function itself.
    +
      597.    668
    +
      598.    668    let catch_list = [];        // The array containing all catch-blocks.
    +
      599.    668    let catch_stack = [         // The stack of catch-blocks.
    +
      600.    668        {
    +
      601.    668            context: empty()
    +
      602.    668        }
    +
      603.    668    ];
    +
      604.    668    let cause_dict = empty();   // The object of test-causes.
    +
      605.    668    let directive_list = [];    // The directive comments.
    +
      606.    668    let export_dict = empty();  // The exported names and values.
    +
      607.    668    let function_list = [];     // The array containing all functions.
    +
      608.    668    let function_stack = [];    // The stack of functions.
    +
      609.    668    let global_dict = empty();  // The object containing the global
    +
      610.    668                                // ... declarations.
    +
      611.    668    let import_list = [];       // The array collecting all import-from strings.
    +
      612.    668    let line_list = String(     // The array containing source lines.
    +
      613.    668        "\n" + source
    +
      614. 105217    ).split(jslint_rgx_crlf).map(function (line_source) {
    +
      615. 105217        return {
    +
      616. 105217            line_source
    +
      617. 105217        };
    +
      618. 105217    });
    +
      619.    668    let mode_stop = false;      // true if JSLint cannot finish.
    +
      620.    668    let property_dict = empty();        // The object containing the tallied
    +
      621.    668                                        // ... property names.
    +
      622.    668    let state = empty();        // jslint state-object to be passed between
    +
      623.    668                                // jslint functions.
    +
      624.    668    let syntax_dict = empty();  // The object containing the parser.
    +
      625.    668    let tenure = empty();       // The predefined property registry.
    +
      626.    668    let token_global = {        // The global object; the outermost context.
    +
      627.    668        async: 0,
    +
      628.    668        body: true,
    +
      629.    668        context: empty(),
    +
      630.    668        finally: 0,
    +
      631.    668        from: 0,
    +
      632.    668        id: "(global)",
    +
      633.    668        level: 0,
    +
      634.    668        line: jslint_fudge,
    +
      635.    668        live: [],
    +
      636.    668        loop: 0,
    +
      637.    668        switch: 0,
    +
      638.    668        thru: 0,
    +
      639.    668        try: 0
    +
      640.    668    };
    +
      641.    668    let token_list = [];        // The array of tokens.
    +
      642.    668    let warning_list = [];      // The array collecting all generated warnings.
    +
      643.    668
    +
      644.    668// Error reportage functions:
    +
      645.    668
    +
      646.   8041    function artifact(the_token) {
    +
      647.   8041
    +
      648.   8041// Return a string representing an artifact.
    +
      649.   8041
    +
      650.    256        the_token = the_token || state.token_nxt;
    +
      651.   8041        return (
    +
      652.   5266            (the_token.id === "(string)" || the_token.id === "(number)")
    +
      653.   2900            ? String(the_token.value)
    +
      654.   5141            : the_token.id
    +
      655.   8041        );
    +
      656.   8041    }
    +
      657.    668
    +
      658.  31957    function is_equal(aa, bb) {
    +
      659.  31957
    +
      660.  31957// test_cause:
    +
      661.  31957// ["0&&0", "is_equal", "", "", 0]
    +
      662.  31957
    +
      663.  31957        test_cause("");
    +
      664.  31957
    +
      665.  31957// Probably deadcode.
    +
      666.  31957// if (aa === bb) {
    +
      667.  31957//     return true;
    +
      668.  31957// }
    +
      669.  31957
    +
      670.  31957        jslint_assert(!(aa === bb), `Expected !(aa === bb).`);
    +
      671.     27        if (Array.isArray(aa)) {
    +
      672.     27            return (
    +
      673.     27                Array.isArray(bb)
    +
      674.     27                && aa.length === bb.length
    +
      675.     27                && aa.every(function (value, index) {
    +
      676.     27
    +
      677.     27// test_cause:
    +
      678.     27// ["`${0}`&&`${0}`", "is_equal", "recurse_isArray", "", 0]
    +
      679.     27// ["`${0}`&&`${1}`", "is_equal", "recurse_isArray", "", 0]
    +
      680.     27
    +
      681.     27                    test_cause("recurse_isArray");
    +
      682.     27                    return is_equal(value, bb[index]);
    +
      683.     27                })
    +
      684.     27            );
    +
      685.  31930        }
    +
      686.  31930
    +
      687.  31930// Probably deadcode.
    +
      688.  31930// if (Array.isArray(bb)) {
    +
      689.  31930//     return false;
    +
      690.  31930// }
    +
      691.  31930
    +
      692.  31930        jslint_assert(!Array.isArray(bb), `Expected !Array.isArray(bb).`);
    +
      693.  31930        switch (aa.id === bb.id && aa.id) {
    +
      694.     65        case "(number)":
    +
      695.  23429        case "(string)":
    +
      696.  23429            return aa.value === bb.value;
    +
      697.  31957
    +
      698.  31957// PR-394 - Bugfix
    +
      699.  31957// Fix jslint falsely believing megastring literals `0` and `1` are similar.
    +
      700.  31957
    +
      701.     15        case "`":
    +
      702.     15            if (!is_equal(aa.value, bb.value)) {
    +
      703.     15                return false;
    +
      704.     15            }
    +
      705.     15            break;
    +
      706.   8498        }
    +
      707.   8498        if (is_weird(aa) || is_weird(bb)) {
    +
      708.     34
    +
      709.     34// test_cause:
    +
      710.     34// ["aa(/./)||{}", "is_equal", "false", "", 0]
    +
      711.     34
    +
      712.     34            test_cause("false");
    +
      713.     34            return false;
    +
      714.   8464        }
    +
      715.   8464        if (aa.arity === bb.arity && aa.id === bb.id) {
    +
      716.   2147            if (aa.id === "." || aa.id === "?.") {
    +
      717.   2147
    +
      718.   2147// test_cause:
    +
      719.   2147// ["aa.bb&&aa.bb", "is_equal", "recurse_arity_id", "", 0]
    +
      720.   2147// ["aa?.bb&&aa?.bb", "is_equal", "recurse_arity_id", "", 0]
    +
      721.   2147
    +
      722.   2147                test_cause("recurse_arity_id");
    +
      723.   2147                return (
    +
      724.   2147                    is_equal(aa.expression, bb.expression)
    +
      725.   2147                    && is_equal(aa.name, bb.name)
    +
      726.   2147                );
    +
      727.   2147            }
    +
      728.   2147            if (aa.arity === "unary") {
    +
      729.   2147
    +
      730.   2147// test_cause:
    +
      731.   2147// ["+0&&+0", "is_equal", "recurse_unary", "", 0]
    +
      732.   2147
    +
      733.   2147                test_cause("recurse_unary");
    +
      734.   2147                return is_equal(aa.expression, bb.expression);
    +
      735.   2147            }
    +
      736.   2147            if (aa.arity === "binary") {
    +
      737.   2147
    +
      738.   2147// test_cause:
    +
      739.   2147// ["aa[0]&&aa[0]", "is_equal", "recurse_binary", "", 0]
    +
      740.   2147
    +
      741.   2147                test_cause("recurse_binary");
    +
      742.   2147                return (
    +
      743.   2147                    aa.id !== "("
    +
      744.   2147                    && is_equal(aa.expression[0], bb.expression[0])
    +
      745.   2147                    && is_equal(aa.expression[1], bb.expression[1])
    +
      746.   2147                );
    +
      747.   2147            }
    +
      748.   2147            if (aa.arity === "ternary") {
    +
      749.   2147
    +
      750.   2147// test_cause:
    +
      751.   2147// ["aa=(``?``:``)&&(``?``:``)", "is_equal", "recurse_ternary", "", 0]
    +
      752.   2147
    +
      753.   2147                test_cause("recurse_ternary");
    +
      754.   2147                return (
    +
      755.   2147                    is_equal(aa.expression[0], bb.expression[0])
    +
      756.   2147                    && is_equal(aa.expression[1], bb.expression[1])
    +
      757.   2147                    && is_equal(aa.expression[2], bb.expression[2])
    +
      758.   2147                );
    +
      759.   2147            }
    +
      760.   2147
    +
      761.   2147// Probably deadcode.
    +
      762.   2147// if (aa.arity === "function" || aa.arity === "regexp") {
    +
      763.   2147//     return false;
    +
      764.   2147// }
    +
      765.   2147
    +
      766.   2147            jslint_assert(
    +
      767.   2147                !(aa.arity === "function" || aa.arity === "regexp"),
    +
      768.   2147                `Expected !(aa.arity === "function" || aa.arity === "regexp").`
    +
      769.   2147            );
    +
      770.   2147
    +
      771.   2147// test_cause:
    +
      772.   2147// ["undefined&&undefined", "is_equal", "true", "", 0]
    +
      773.   2147
    +
      774.   2147            test_cause("true");
    +
      775.   2147            return true;
    +
      776.   6317        }
    +
      777.   6317
    +
      778.   6317// test_cause:
    +
      779.   6317// ["null&&undefined", "is_equal", "false", "", 0]
    +
      780.   6317
    +
      781.   6317        test_cause("false");
    +
      782.   6317        return false;
    +
      783.   6317    }
    +
      784.    668
    +
      785.  28763    function is_weird(thing) {
    +
      786.  28763        switch (thing.id) {
    +
      787.      1        case "(regexp)":
    +
      788.      1            return true;
    +
      789.      1        case "=>":
    +
      790.      1            return true;
    +
      791.    593        case "[":
    +
      792.    593            return thing.arity === "unary";
    +
      793.     12        case "function":
    +
      794.     12            return true;
    +
      795.      8        case "{":
    +
      796.      8            return true;
    +
      797.  28148        default:
    +
      798.  28148            return false;
    +
      799.  28763        }
    +
      800.  28763    }
    +
      801.    668
    +
      802.    106    function stop(code, the_token, a, b, c, d) {
    +
      803.    106
    +
      804.    106// Similar to warn and stop_at. If the token already had a warning, that
    +
      805.    106// warning will be replaced with this new one. It is likely that the stopping
    +
      806.    106// warning will be the more meaningful.
    +
      807.    106
    +
      808.     38        the_token = the_token || state.token_nxt;
    +
      809.    106        delete the_token.warning;
    +
      810.    106        throw warn(code, the_token, a, b, c, d);
    +
      811.    106    }
    +
      812.    668
    +
      813.     28    function stop_at(code, line, column, a, b, c, d) {
    +
      814.     28
    +
      815.     28// Same as warn_at, except that it stops the analysis.
    +
      816.     28
    +
      817.     28        throw warn_at(code, line, column, a, b, c, d);
    +
      818.     28    }
    +
      819.    668
    +
      820. 339547    function test_cause(code, aa, column) {
    +
      821. 339547
    +
      822. 339547// This function will instrument <cause> to <cause_dict> for test-purposes.
    +
      823. 339547
    +
      824.   4882        if (option_dict.test_cause) {
    +
      825.   4882            cause_dict[JSON.stringify([
    +
      826.   4882                String(new Error().stack).replace((
    +
      827.   4882                    /^    at (?:file|stop|stop_at|test_cause|warn|warn_at)\b.*?\n/gm
    +
      828.   4882                ), "").match(
    +
      829.   4882                    /\n    at ((?:Object\.\w+?_)?\w+?) /
    +
      830.   4882                )[1].replace((
    +
      831.   4882                    /^Object\./
    +
      832.   4882                ), ""),
    +
      833.   4882                code,
    +
      834.   4882                String(
    +
      835.   4882                    (aa === undefined || aa === token_global)
    +
      836.   4882                    ? ""
    +
      837.   4882                    : aa
    +
      838.   4882                ),
    +
      839.   4882                column || 0
    +
      840.   4882            ])] = true;
    +
      841.   4882        }
    +
      842. 339547    }
    +
      843.    668
    +
      844.   1075    function warn(code, the_token, a, b, c, d) {
    +
      845.   1075
    +
      846.   1075// Same as warn_at, except the warning will be associated with a specific token.
    +
      847.   1075// If there is already a warning on this token, suppress the new one. It is
    +
      848.   1075// likely that the first warning will be the most meaningful.
    +
      849.   1075
    +
      850.   1075        let the_warning;
    +
      851.     20        the_token = the_token || state.token_nxt;
    +
      852.   1075        the_warning = warn_at(
    +
      853.   1075            code,
    +
      854.   1075            the_token.line,
    +
      855.    376            (the_token.from || 0) + jslint_fudge,
    +
      856.    835            a || artifact(the_token),
    +
      857.   1075            b,
    +
      858.   1075            c,
    +
      859.   1075            d
    +
      860.   1075        );
    +
      861.   1075
    +
      862.   1075// Issue #408
    +
      863.   1075// Warnings that should be ignored sometimes suppress legitimate warnings.
    +
      864.   1075
    +
      865.     26        if (the_warning.directive_ignore_line) {
    +
      866.     26            return the_warning;
    +
      867.   1049        }
    +
      868.   1049
    +
      869.   1049// If there is already a warning on this token, suppress the new one. It is
    +
      870.   1049// likely that the first warning will be the most meaningful.
    +
      871.   1049
    +
      872.   1049        if (the_token.warning) {
    +
      873.    192            warning_list.pop();
    +
      874.    192            return the_warning;
    +
      875.    857        }
    +
      876.    857        the_token.warning = the_warning;
    +
      877.    857        return the_warning;
    +
      878.    857    }
    +
      879.    668
    +
      880.   1394    function warn_at(code, line, column, a, b, c, d) {
    +
      881.   1394
    +
      882.   1394// Report an error at some line and column of the program. The warning object
    +
      883.   1394// resembles an exception.
    +
      884.   1394
    +
      885.   1394        let mm;
    +
      886.   1394        let warning = Object.assign(empty(), {
    +
      887.   1394            a,
    +
      888.   1394            b,
    +
      889.   1394            c,
    +
      890.   1394            code,
    +
      891.   1394
    +
      892.   1394// Fudge column numbers in warning message.
    +
      893.   1394
    +
      894.     27            column: column || jslint_fudge,
    +
      895.   1394            d,
    +
      896.   1394            line,
    +
      897.   1394            line_source: "",
    +
      898.   1394            name: "JSLintError"
    +
      899.   1394        }, line_list[line]);
    +
      900.   1394        warning.column = Math.max(
    +
      901.   1394            Math.min(warning.column, warning.line_source.length),
    +
      902.   1394            jslint_fudge
    +
      903.   1394        );
    +
      904.    926        test_cause(code, b || a, warning.column);
    +
      905.   1394        switch (code) {
    +
      906.   1394
    +
      907.   1394// The bundle contains the raw text messages that are generated by jslint. It
    +
      908.   1394// seems that they are all error messages and warnings. There are no "Atta
    +
      909.   1394// boy!" or "You are so awesome!" messages. There is no positive reinforcement
    +
      910.   1394// or encouragement. This relentless negativity can undermine self-esteem and
    +
      911.   1394// wound the inner child. But if you accept it as sound advice rather than as
    +
      912.   1394// personal criticism, it can make your programs better.
    +
      913.   1394
    +
      914.      1        case "and":
    +
      915.      1            mm = `The '&&' subexpression should be wrapped in parens.`;
    +
      916.      1            break;
    +
      917.     71        case "bad_assignment_a":
    +
      918.     71            mm = `Bad assignment to '${a}'.`;
    +
      919.     71            break;
    +
      920.      1        case "bad_directive_a":
    +
      921.      1            mm = `Bad directive '${a}'.`;
    +
      922.      1            break;
    +
      923.      1        case "bad_get":
    +
      924.      1            mm = `A get function takes no parameters.`;
    +
      925.      1            break;
    +
      926.      1        case "bad_module_name_a":
    +
      927.      1            mm = `Bad module name '${a}'.`;
    +
      928.      1            break;
    +
      929.      2        case "bad_option_a":
    +
      930.      2            mm = `Bad option '${a}'.`;
    +
      931.      2            break;
    +
      932.      1        case "bad_set":
    +
      933.      1            mm = `A set function takes one parameter.`;
    +
      934.      1            break;
    +
      935.      6        case "duplicate_a":
    +
      936.      6            mm = `Duplicate '${a}'.`;
    +
      937.      6            break;
    +
      938.     64        case "empty_block":
    +
      939.     64            mm = `Empty block.`;
    +
      940.     64            break;
    +
      941.      5        case "expected_a":
    +
      942.      5            mm = `Expected '${a}'.`;
    +
      943.      5            break;
    +
      944.     25        case "expected_a_at_b_c":
    +
      945.     25            mm = `Expected '${a}' at column ${b}, not column ${c}.`;
    +
      946.     25            break;
    +
      947.    286        case "expected_a_b":
    +
      948.    286            mm = `Expected '${a}' and instead saw '${b}'.`;
    +
      949.    286            break;
    +
      950.     17        case "expected_a_b_before_c_d":
    +
      951.     17            mm = `Expected ${a} '${b}' to be ordered before ${c} '${d}'.`;
    +
      952.     17            break;
    +
      953.      2        case "expected_a_b_from_c_d":
    +
      954.      2            mm = (
    +
      955.      2                `Expected '${a}' to match '${b}' from line ${c}`
    +
      956.      2                + ` and instead saw '${d}'.`
    +
      957.      2            );
    +
      958.      2            break;
    +
      959.     30        case "expected_a_before_b":
    +
      960.     30            mm = `Expected '${a}' before '${b}'.`;
    +
      961.     30            break;
    +
      962.      2        case "expected_digits_after_a":
    +
      963.      2            mm = `Expected digits after '${a}'.`;
    +
      964.      2            break;
    +
      965.      1        case "expected_four_digits":
    +
      966.      1            mm = `Expected four digits after '\\u'.`;
    +
      967.      1            break;
    +
      968.     31        case "expected_identifier_a":
    +
      969.     31            mm = `Expected an identifier and instead saw '${a}'.`;
    +
      970.     31            break;
    +
      971.      6        case "expected_line_break_a_b":
    +
      972.      6            mm = `Expected a line break between '${a}' and '${b}'.`;
    +
      973.      6            break;
    +
      974.      3        case "expected_regexp_factor_a":
    +
      975.      3            mm = `Expected a regexp factor and instead saw '${a}'.`;
    +
      976.      3            break;
    +
      977.     76        case "expected_space_a_b":
    +
      978.     76            mm = `Expected one space between '${a}' and '${b}'.`;
    +
      979.     76            break;
    +
      980.      2        case "expected_statements_a":
    +
      981.      2            mm = `Expected statements before '${a}'.`;
    +
      982.      2            break;
    +
      983.      1        case "expected_string_a":
    +
      984.      1            mm = `Expected a string and instead saw '${a}'.`;
    +
      985.      1            break;
    +
      986.      1        case "expected_type_string_a":
    +
      987.      1            mm = `Expected a type string and instead saw '${a}'.`;
    +
      988.      1            break;
    +
      989.      6        case "freeze_exports":
    +
      990.      6            mm = (
    +
      991.      6                `Expected 'Object.freeze('. All export values should be frozen.`
    +
      992.      6            );
    +
      993.      6            break;
    +
      994.   1394
    +
      995.   1394// PR-378 - Relax warning "function_in_loop".
    +
      996.   1394//
    +
      997.   1394//         case "function_in_loop":
    +
      998.   1394//             mm = `Don't create functions within a loop.`;
    +
      999.   1394//             break;
    +
     1000.   1394
    +
     1001.   1394// PR-390 - Add numeric-separator check.
    +
     1002.   1394
    +
     1003.      7        case "illegal_num_separator":
    +
     1004.      7            mm = `Illegal numeric separator '_' at column ${column}.`;
    +
     1005.      7            break;
    +
     1006.      1        case "infix_in":
    +
     1007.      1            mm = (
    +
     1008.      1                `Unexpected 'in'. Compare with undefined,`
    +
     1009.      1                + ` or use the hasOwnProperty method instead.`
    +
     1010.      1            );
    +
     1011.      1            break;
    +
     1012.      1        case "label_a":
    +
     1013.      1            mm = `'${a}' is a statement label.`;
    +
     1014.      1            break;
    +
     1015.      1        case "misplaced_a":
    +
     1016.      1            mm = `Place '${a}' at the outermost level.`;
    +
     1017.      1            break;
    +
     1018.      1        case "misplaced_directive_a":
    +
     1019.      1            mm = `Place the '/*${a}*/' directive before the first statement.`;
    +
     1020.      1            break;
    +
     1021.      3        case "missing_await_statement":
    +
     1022.      3            mm = `Expected await statement in async function.`;
    +
     1023.      3            break;
    +
     1024.   1394
    +
     1025.   1394// PR-347 - Disable warning "missing_browser".
    +
     1026.   1394//
    +
     1027.   1394//         case "missing_browser":
    +
     1028.   1394//             mm = `/*global*/ requires the Assume a browser option.`;
    +
     1029.   1394//             break;
    +
     1030.   1394
    +
     1031.      1        case "missing_m":
    +
     1032.      1            mm = `Expected 'm' flag on a multiline regular expression.`;
    +
     1033.      1            break;
    +
     1034.      5        case "naked_block":
    +
     1035.      5            mm = `Naked block.`;
    +
     1036.      5            break;
    +
     1037.      2        case "nested_comment":
    +
     1038.      2            mm = `Nested comment.`;
    +
     1039.      2            break;
    +
     1040.      1        case "not_label_a":
    +
     1041.      1            mm = `'${a}' is not a label.`;
    +
     1042.      1            break;
    +
     1043.      2        case "number_isNaN":
    +
     1044.      2            mm = `Use Number.isNaN function to compare with NaN.`;
    +
     1045.      2            break;
    +
     1046.      4        case "out_of_scope_a":
    +
     1047.      4            mm = `'${a}' is out of scope.`;
    +
     1048.      4            break;
    +
     1049.     11        case "redefinition_a_b":
    +
     1050.     11            mm = `Redefinition of '${a}' from line ${b}.`;
    +
     1051.     11            break;
    +
     1052.      1        case "redefinition_global_a_b":
    +
     1053.      1            mm = `Redefinition of global ${a} variable '${b}'.`;
    +
     1054.      1            break;
    +
     1055.      6        case "required_a_optional_b":
    +
     1056.      6            mm = `Required parameter '${a}' after optional parameter '${b}'.`;
    +
     1057.      6            break;
    +
     1058.      1        case "reserved_a":
    +
     1059.      1            mm = `Reserved name '${a}'.`;
    +
     1060.      1            break;
    +
     1061.      1        case "subscript_a":
    +
     1062.      1            mm = `['${a}'] is better written in dot notation.`;
    +
     1063.      1            break;
    +
     1064.     16        case "todo_comment":
    +
     1065.     16            mm = `Unexpected TODO comment.`;
    +
     1066.     16            break;
    +
     1067.     13        case "too_long":
    +
     1068.     13            mm = `Line is longer than 80 characters.`;
    +
     1069.     13            break;
    +
     1070.      1        case "too_many_digits":
    +
     1071.      1            mm = `Too many digits.`;
    +
     1072.      1            break;
    +
     1073.      3        case "unclosed_comment":
    +
     1074.      3            mm = `Unclosed comment.`;
    +
     1075.      3            break;
    +
     1076.      3        case "unclosed_disable":
    +
     1077.      3            mm = (
    +
     1078.      3                `Directive '/*jslint-disable*/' was not closed`
    +
     1079.      3                + ` with '/*jslint-enable*/'.`
    +
     1080.      3            );
    +
     1081.      3            break;
    +
     1082.      3        case "unclosed_mega":
    +
     1083.      3            mm = `Unclosed mega literal.`;
    +
     1084.      3            break;
    +
     1085.      2        case "unclosed_string":
    +
     1086.      2            mm = `Unclosed string.`;
    +
     1087.      2            break;
    +
     1088.    137        case "undeclared_a":
    +
     1089.    137            mm = `Undeclared '${a}'.`;
    +
     1090.    137            break;
    +
     1091.    237        case "unexpected_a":
    +
     1092.    237            mm = `Unexpected '${a}'.`;
    +
     1093.    237            break;
    +
     1094.      2        case "unexpected_a_after_b":
    +
     1095.      2            mm = `Unexpected '${a}' after '${b}'.`;
    +
     1096.      2            break;
    +
     1097.      2        case "unexpected_a_before_b":
    +
     1098.      2            mm = `Unexpected '${a}' before '${b}'.`;
    +
     1099.      2            break;
    +
     1100.     34        case "unexpected_at_top_level_a":
    +
     1101.     34            mm = `Expected '${a}' to be in a function.`;
    +
     1102.     34            break;
    +
     1103.      1        case "unexpected_char_a":
    +
     1104.      1            mm = `Unexpected character '${a}'.`;
    +
     1105.      1            break;
    +
     1106.      2        case "unexpected_comment":
    +
     1107.      2            mm = `Unexpected comment.`;
    +
     1108.      2            break;
    +
     1109.   1394
    +
     1110.   1394// PR-347 - Disable warning "unexpected_directive_a".
    +
     1111.   1394//
    +
     1112.   1394//         case "unexpected_directive_a":
    +
     1113.   1394//             mm = `When using modules, don't use directive '/\u002a${a}'.`;
    +
     1114.   1394//             break;
    +
     1115.   1394
    +
     1116.    128        case "unexpected_expression_a":
    +
     1117.    128            mm = `Unexpected expression '${a}' in statement position.`;
    +
     1118.    128            break;
    +
     1119.      4        case "unexpected_label_a":
    +
     1120.      4            mm = `Unexpected label '${a}'.`;
    +
     1121.      4            break;
    +
     1122.      3        case "unexpected_parens":
    +
     1123.      3            mm = `Don't wrap function literals in parens.`;
    +
     1124.      3            break;
    +
     1125.      8        case "unexpected_space_a_b":
    +
     1126.      8            mm = `Unexpected space between '${a}' and '${b}'.`;
    +
     1127.      8            break;
    +
     1128.      1        case "unexpected_statement_a":
    +
     1129.      1            mm = `Unexpected statement '${a}' in expression position.`;
    +
     1130.      1            break;
    +
     1131.      2        case "unexpected_trailing_space":
    +
     1132.      2            mm = `Unexpected trailing space.`;
    +
     1133.      2            break;
    +
     1134.      1        case "unexpected_typeof_a":
    +
     1135.      1            mm = (
    +
     1136.      1                `Unexpected 'typeof'. Use '===' to compare directly with ${a}.`
    +
     1137.      1            );
    +
     1138.      1            break;
    +
     1139.      1        case "uninitialized_a":
    +
     1140.      1            mm = `Uninitialized '${a}'.`;
    +
     1141.      1            break;
    +
     1142.      1        case "unopened_enable":
    +
     1143.      1            mm = (
    +
     1144.      1                `Directive '/*jslint-enable*/' was not opened`
    +
     1145.      1                + ` with '/*jslint-disable*/'.`
    +
     1146.      1            );
    +
     1147.      1            break;
    +
     1148.      1        case "unreachable_a":
    +
     1149.      1            mm = `Unreachable '${a}'.`;
    +
     1150.      1            break;
    +
     1151.      1        case "unregistered_property_a":
    +
     1152.      1            mm = `Unregistered property name '${a}'.`;
    +
     1153.      1            break;
    +
     1154.      6        case "unused_a":
    +
     1155.      6            mm = `Unused '${a}'.`;
    +
     1156.      6            break;
    +
     1157.      2        case "use_double":
    +
     1158.      2            mm = `Use double quotes, not single quotes.`;
    +
     1159.      2            break;
    +
     1160.   1394
    +
     1161.   1394// PR-386 - Fix issue #382 - Make fart-related warnings more readable.
    +
     1162.   1394
    +
     1163.      4        case "use_function_not_fart":
    +
     1164.      4            mm = (
    +
     1165.      4                `Use 'function (...)', not '(...) =>' when arrow functions`
    +
     1166.      4                + ` become too complex.`
    +
     1167.      4            );
    +
     1168.      4            break;
    +
     1169.      7        case "use_open":
    +
     1170.      7            mm = (
    +
     1171.      7                `Wrap a ternary expression in parens,`
    +
     1172.      7                + ` with a line break after the left paren.`
    +
     1173.      7            );
    +
     1174.      7            break;
    +
     1175.      1        case "use_spaces":
    +
     1176.      1            mm = `Use spaces, not tabs.`;
    +
     1177.      1            break;
    +
     1178.      5        case "var_on_top":
    +
     1179.      5            mm = `Move variable declaration to top of function or script.`;
    +
     1180.      5            break;
    +
     1181.      1        case "var_switch":
    +
     1182.      1            mm = `Don't declare variables in a switch.`;
    +
     1183.      1            break;
    +
     1184.     23        case "weird_condition_a":
    +
     1185.     23            mm = `Weird condition '${a}'.`;
    +
     1186.     23            break;
    +
     1187.      5        case "weird_expression_a":
    +
     1188.      5            mm = `Weird expression '${a}'.`;
    +
     1189.      5            break;
    +
     1190.      3        case "weird_loop":
    +
     1191.      3            mm = `Weird loop.`;
    +
     1192.      3            break;
    +
     1193.      9        case "weird_property_a":
    +
     1194.      9            mm = `Weird property name '${a}'.`;
    +
     1195.      9            break;
    +
     1196.      8        case "weird_relation_a":
    +
     1197.      8            mm = `Weird relation '${a}'.`;
    +
     1198.      8            break;
    +
     1199.      1        case "wrap_condition":
    +
     1200.      1            mm = `Wrap the condition in parens.`;
    +
     1201.      1            break;
    +
     1202.   1394
    +
     1203.   1394// PR-386 - Fix issue #382 - Make fart-related warnings more readable.
    +
     1204.   1394
    +
     1205.      1        case "wrap_fart_parameter":
    +
     1206.      1            mm = `Wrap the parameter before '=>' in parens.`;
    +
     1207.      1            break;
    +
     1208.      1        case "wrap_immediate":
    +
     1209.      1            mm = (
    +
     1210.      1                `Wrap an immediate function invocation in parentheses to assist`
    +
     1211.      1                + ` the reader in understanding that the expression is the`
    +
     1212.      1                + ` result of a function, and not the function itself.`
    +
     1213.      1            );
    +
     1214.      1            break;
    +
     1215.     18        case "wrap_regexp":
    +
     1216.     18            mm = `Wrap this regexp in parens to avoid confusion.`;
    +
     1217.     18            break;
    +
     1218.      1        case "wrap_unary":
    +
     1219.      1            mm = `Wrap the unary expression in parens.`;
    +
     1220.      1            break;
    +
     1221.   1394        }
    +
     1222.   1394
    +
     1223.   1394// Validate mm.
    +
     1224.   1394
    +
     1225.   1394        jslint_assert(mm, code);
    +
     1226.   1394        warning.message = mm;
    +
     1227.   1394
    +
     1228.   1394// PR-242 - Include stack_trace for jslint to debug itself for errors.
    +
     1229.   1394
    +
     1230.      6        if (option_dict.trace) {
    +
     1231.      6            warning.stack_trace = new Error().stack;
    +
     1232.      6        }
    +
     1233.     41        if (warning.directive_ignore_line) {
    +
     1234.     41
    +
     1235.     41// test_cause:
    +
     1236.     41// ["0 //jslint-ignore-line", "semicolon", "directive_ignore_line", "", 0]
    +
     1237.     41
    +
     1238.     41            test_cause("directive_ignore_line");
    +
     1239.     41            return warning;
    +
     1240.   1353        }
    +
     1241.   1353        warning_list.push(warning);
    +
     1242.   1353        return warning;
    +
     1243.   1353    }
    +
     1244.    668
    +
     1245.    668    try {
    +
     1246.    668
    +
     1247.    668// tokenize takes a source and produces from it an array of token objects.
    +
     1248.    668// JavaScript is notoriously difficult to tokenize because of the horrible
    +
     1249.    668// interactions between automatic semicolon insertion, regular expression
    +
     1250.    668// literals, and now megastring literals. JSLint benefits from eliminating
    +
     1251.    668// automatic semicolon insertion and nested megastring literals, which allows
    +
     1252.    668// full tokenization to precede parsing.
    +
     1253.    668
    +
     1254.    668        option_dict = Object.assign(empty(), option_dict);
    +
     1255.    668        Object.assign(state, {
    +
     1256.    668            artifact,
    +
     1257.    668            catch_list,
    +
     1258.    668            catch_stack,
    +
     1259.    668            directive_list,
    +
     1260.    668            export_dict,
    +
     1261.    668            function_list,
    +
     1262.    668            function_stack,
    +
     1263.    668            global_dict,
    +
     1264.    668            global_list,
    +
     1265.    668            import_list,
    +
     1266.    668            is_equal,
    +
     1267.    668            is_weird,
    +
     1268.    668            line_list,
    +
     1269.    668            mode_json: false,           // true if parsing JSON.
    +
     1270.    668            mode_module: false,         // true if import or export was used.
    +
     1271.    668            mode_property: false,       // true if directive /*property*/ is
    +
     1272.    668                                        // ... used.
    +
     1273.    668            mode_shebang: false,        // true if #! is seen on the first line.
    +
     1274.    668            option_dict,
    +
     1275.    668            property_dict,
    +
     1276.    668            source,
    +
     1277.    668            stop,
    +
     1278.    668            stop_at,
    +
     1279.    668            syntax_dict,
    +
     1280.    668            tenure,
    +
     1281.    668            test_cause,
    +
     1282.    668            token_global,
    +
     1283.    668            token_list,
    +
     1284.    668            token_nxt: token_global,
    +
     1285.    668            warn,
    +
     1286.    668            warn_at,
    +
     1287.    668            warning_list
    +
     1288.    668        });
    +
     1289.    668
    +
     1290.    668// PHASE 1. Split <source> by newlines into <line_list>.
    +
     1291.    668
    +
     1292.    668        jslint_phase1_split(state);
    +
     1293.    668        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
    +
     1294.    668        jslint_assert(
    +
     1295.    668            function_stack.length === 0,
    +
     1296.    668            `function_stack.length === 0.`
    +
     1297.    668        );
    +
     1298.    668
    +
     1299.    668// PHASE 2. Lex <line_list> into <token_list>.
    +
     1300.    668
    +
     1301.    668        jslint_phase2_lex(state);
    +
     1302.    668        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
    +
     1303.    668        jslint_assert(
    +
     1304.    668            function_stack.length === 0,
    +
     1305.    668            `function_stack.length === 0.`
    +
     1306.    668        );
    +
     1307.    668
    +
     1308.    668// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
    +
     1309.    668
    +
     1310.    668        jslint_phase3_parse(state);
    +
     1311.    668        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
    +
     1312.    668        jslint_assert(
    +
     1313.    668            function_stack.length === 0,
    +
     1314.    668            `function_stack.length === 0.`
    +
     1315.    668        );
    +
     1316.    668
    +
     1317.    668// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
    +
     1318.    668//          recursive traversal. Each node may be processed on the way down
    +
     1319.    668//          (preaction) and on the way up (postaction).
    +
     1320.    668
    +
     1321.    518        if (!state.mode_json) {
    +
     1322.    518            jslint_phase4_walk(state);
    +
     1323.    533        }
    +
     1324.    533        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
    +
     1325.    533        jslint_assert(
    +
     1326.    533            function_stack.length === 0,
    +
     1327.    533            `function_stack.length === 0.`
    +
     1328.    533        );
    +
     1329.    533
    +
     1330.    533// PHASE 5. Check whitespace between tokens in <token_list>.
    +
     1331.    533
    +
     1332.    533        if (!state.mode_json && warning_list.length === 0) {
    +
     1333.    208            jslint_phase5_whitage(state);
    +
     1334.    533        }
    +
     1335.    533        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
    +
     1336.    533        jslint_assert(
    +
     1337.    533            function_stack.length === 0,
    +
     1338.    533            `function_stack.length === 0.`
    +
     1339.    533        );
    +
     1340.    533
    +
     1341.    533// PR-347 - Disable warning "missing_browser".
    +
     1342.    533//
    +
     1343.    533//         if (!option_dict.browser) {
    +
     1344.    533//             directive_list.forEach(function (comment) {
    +
     1345.    533//                 if (comment.directive === "global") {
    +
     1346.    533//
    +
     1347.    533// // test_cause:
    +
     1348.    533// // ["/*global aa*/", "jslint", "missing_browser", "(comment)", 1]
    +
     1349.    533//
    +
     1350.    533//                     warn("missing_browser", comment);
    +
     1351.    533//                 }
    +
     1352.    533//             });
    +
     1353.    533//         }
    +
     1354.    533
    +
     1355.    533        if (option_dict.test_internal_error) {
    +
     1356.      2            jslint_assert(undefined, "test_internal_error");
    +
     1357.      2        }
    +
     1358.    137    } catch (err) {
    +
     1359.    137        mode_stop = true;
    +
     1360.    137        err.message = "[JSLint was unable to finish] " + err.message;
    +
     1361.    137        err.mode_stop = true;
    +
     1362.    137        if (err.name !== "JSLintError") {
    +
     1363.    137            Object.assign(err, {
    +
     1364.    137                column: jslint_fudge,
    +
     1365.    137                line: jslint_fudge,
    +
     1366.    137                line_source: "",
    +
     1367.    137                stack_trace: err.stack
    +
     1368.    137            });
    +
     1369.    137        }
    +
     1370.    137        if (warning_list.indexOf(err) === -1) {
    +
     1371.    137            warning_list.push(err);
    +
     1372.    137        }
    +
     1373.    137    }
    +
     1374.    668
    +
     1375.    668// Sort warning_list by mode_stop first, line, column respectively.
    +
     1376.    668
    +
     1377.    986    warning_list.sort(function (aa, bb) {
    +
     1378.    986        return (
    +
     1379.    986            Boolean(bb.mode_stop) - Boolean(aa.mode_stop)
    +
     1380.    896            || aa.line - bb.line
    +
     1381.    876            || aa.column - bb.column
    +
     1382.    986        );
    +
     1383.    986
    +
     1384.    986// Update each warning with formatted_message ready-for-use by jslint_cli.
    +
     1385.    986
    +
     1386.   1164    }).map(function ({
    +
     1387.   1164        column,
    +
     1388.   1164        line,
    +
     1389.   1164        line_source,
    +
     1390.   1164        message,
    +
     1391.   1164        stack_trace = ""
    +
     1392.   1164    }, ii, list) {
    +
     1393.   1164        list[ii].formatted_message = String(
    +
     1394.   1164            String(ii + 1).padStart(2, " ")
    +
     1395.   1164            + ". \u001b[31m" + message + "\u001b[39m"
    +
     1396.   1164            + " \u001b[90m\/\/ line " + line + ", column " + column
    +
     1397.   1164            + "\u001b[39m\n"
    +
     1398.   1164            + ("    " + line_source.trim()).slice(0, 72) + "\n"
    +
     1399.   1164            + stack_trace
    +
     1400.   1164        ).trimRight();
    +
     1401.   1164    });
    +
     1402.    668
    +
     1403.    668    return {
    +
     1404.    668        causes: cause_dict,
    +
     1405.    668        directives: directive_list,
    +
     1406.    668        edition: jslint_edition,
    +
     1407.    668        exports: export_dict,
    +
     1408.    668        froms: import_list,
    +
     1409.    668        functions: function_list,
    +
     1410.    668        global: token_global,
    +
     1411.    668        id: "(JSLint)",
    +
     1412.    668        json: state.mode_json,
    +
     1413.    668        lines: line_list,
    +
     1414.    668        module: state.mode_module === true,
    +
     1415.    164        ok: warning_list.length === 0 && !mode_stop,
    +
     1416.    668        option: option_dict,
    +
     1417.    668        property: property_dict,
    +
     1418.    668        shebang: (
    +
     1419.    668            state.mode_shebang
    +
     1420.      1            ? line_list[jslint_fudge].line_source
    +
     1421.    667            : undefined
    +
     1422.    668        ),
    +
     1423.    668        stop: mode_stop,
    +
     1424.    668        tokens: token_list,
    +
     1425.    668        tree: state.token_tree,
    +
     1426.    668        warnings: warning_list
    +
     1427.    668    };
    +
     1428.    668}
    +
     1429.      1
    +
     1430.      1// PR-362 - Add API Doc.
    +
     1431.      1
    +
     1432.      1async function jslint_apidoc({
    +
     1433.      1    example_list,
    +
     1434.      1    github_repo,
    +
     1435.      1    module_list,
    +
     1436.      1    package_name,
    +
     1437.      1    pathname,
    +
     1438.      1    version
    +
     1439.      1}) {
    +
     1440.      1
    +
     1441.      1// This function will create API Doc from <module_list>.
    +
     1442.      1
    +
     1443.      1    let elem_ii = 0;
    +
     1444.      1    let html;
    +
     1445.      1
    +
     1446.     27    function elem_create(moduleObj, key, moduleName) {
    +
     1447.     27
    +
     1448.     27// This function will create a sub API Doc from elem <moduleObj>[<key>].
    +
     1449.     27
    +
     1450.     27        let example = "N/A";
    +
     1451.     27        let id = encodeURIComponent("apidoc.elem." + moduleName + "." + key);
    +
     1452.     27        let name;
    +
     1453.     27        let signature;
    +
     1454.     27        let source;
    +
     1455.     27        name = htmlEscape((typeof moduleObj[key]) + " " + key);
    +
     1456.      2        if (typeof moduleObj[key] !== "function") {
    +
     1457.      2            return {
    +
     1458.      2                name,
    +
     1459.      2                signature: (`
    +
     1460.      2<a class="apidocElementLiA" href="#${id}">
    +
     1461.      2${name}
    +
     1462.      2</a>
    +
     1463.      2                `),
    +
     1464.      2                source: (`
    +
     1465.      2<li>
    +
     1466.      2    <h2>
    +
     1467.      2    <a href="#${id}" id="${id}">
    +
     1468.      2    ${name}
    +
     1469.      2    </a>
    +
     1470.      2    </h2>
    +
     1471.      2</li>
    +
     1472.      2                `)
    +
     1473.      2            };
    +
     1474.     25        }
    +
     1475.     25        // init source
    +
     1476.     25        source = htmlEscape(trim_start(moduleObj[key].toString()));
    +
     1477.     25        // init signature
    +
     1478.     25        source = source.replace((
    +
     1479.     25            /(\([\S\s]*?\)) \{/
    +
     1480.     25        ), function (match0, match1) {
    +
     1481.     25            signature = htmlEscape(
    +
     1482.     25                match1.replace((
    +
     1483.     25                    / *?\/\*[\S\s]*?\*\/ */g
    +
     1484.     25                ), "").replace((
    +
     1485.     25                    / *?\/\/.*/g
    +
     1486.     25                ), "").replace((
    +
     1487.     25                    /\n{2,}/g
    +
     1488.     25                ), "\n")
    +
     1489.     25            );
    +
     1490.     25            return match0;
    +
     1491.     25        });
    +
     1492.     25        // init comment
    +
     1493.     25        source = source.replace((
    +
     1494.     25            /\n(?:\/\/.*?\n)+\n/
    +
     1495.     25        ), "<span class=\"apidocCodeCommentSpan\">$&</span>");
    +
     1496.     25        // init example
    +
     1497.     56        example_list.some(function (example2) {
    +
     1498.     56            example2.replace(
    +
     1499.     56                new RegExp((
    +
     1500.     56                    "((?:\\n.*?){8}(function )?)\\b"
    +
     1501.     56                    + key
    +
     1502.     56                    + "(\\((?:.*?\\n){8})"
    +
     1503.     56                ), "g"),
    +
     1504.    132                function (ignore, header, isDeclaration, footer) {
    +
     1505.    124                    if (!isDeclaration) {
    +
     1506.    124                        example = "..." + trim_start(
    +
     1507.    124                            htmlEscape(header)
    +
     1508.    124                            + "<span class=\"apidocCodeKeywordSpan\">"
    +
     1509.    124                            + htmlEscape(key)
    +
     1510.    124                            + "</span>"
    +
     1511.    124                            + htmlEscape(footer)
    +
     1512.    124                        ).trimEnd() + "\n...";
    +
     1513.    124                    }
    +
     1514.    132                    return "";
    +
     1515.    132                }
    +
     1516.     56            );
    +
     1517.     56            return example !== "N/A";
    +
     1518.     56        });
    +
     1519.     25        if (source.length > 2048) {
    +
     1520.     11            source = source.slice(0, 2048) + "...\n}\n";
    +
     1521.     25        }
    +
     1522.     25        return {
    +
     1523.     25            name,
    +
     1524.     25            signature: (`
    +
     1525.     25<a class="apidocElementLiA" href="#${id}">
    +
     1526.     25${name}<span class="apidocSignatureSpan">${signature}</span>
    +
     1527.     25</a>
    +
     1528.     25            `),
    +
     1529.     25            source: (`
    +
     1530.     25<li>
    +
     1531.     25    <h2>
    +
     1532.     25    <a href="#${id}" id="${id}">
    +
     1533.     25    ${name}<span class="apidocSignatureSpan">${signature}</span>
    +
     1534.     25    </a>
    +
     1535.     25    </h2>
    +
     1536.     25</li>
    +
     1537.     25<li>Description and source-code:<pre class="apidocCodePre">${source}</pre></li>
    +
     1538.     25<li>Example usage:<pre class="apidocCodePre">${example}</pre></li>
    +
     1539.     25            `)
    +
     1540.     25        };
    +
     1541.     25    }
    +
     1542.      1
    +
     1543.    149    function trim_start(str) {
    +
     1544.    149
    +
     1545.    149// This function will normalize whitespace before <str>.
    +
     1546.    149
    +
     1547.    149        let whitespace = "";
    +
     1548.    149        str.trim().replace((
    +
     1549.    149            /^ */gm
    +
     1550.  13071        ), function (match0) {
    +
     1551.   8412            if (whitespace === "" || match0.length < whitespace.length) {
    +
     1552.   6610                whitespace = match0;
    +
     1553.   6610            }
    +
     1554.  13071            return "";
    +
     1555.  13071        });
    +
     1556.    149        str = str.replace(new RegExp("^" + whitespace, "gm"), "");
    +
     1557.    149        return str;
    +
     1558.    149    }
    +
     1559.      1    await moduleFsInit();
    +
     1560.      1
    +
     1561.      1// Html-escape params.
    +
     1562.      1
    +
     1563.      1    github_repo = htmlEscape(github_repo);
    +
     1564.      1    package_name = htmlEscape(package_name);
    +
     1565.      1    version = htmlEscape(version);
    +
     1566.      1
    +
     1567.      1// Init example_list.
    +
     1568.      1
    +
     1569.      3    example_list = await Promise.all(example_list.map(async function (file) {
    +
     1570.      3
    +
     1571.      3// This function will read example from given file.
    +
     1572.      3
    +
     1573.      3        let result = await moduleFs.promises.readFile(file, "utf8");
    +
     1574.      3        result = (
    +
     1575.      3            "\n\n\n\n\n\n\n\n"
    +
     1576.      3            // bug-workaround - truncate example to manageable size
    +
     1577.      3            + result.slice(0, 524288)
    +
     1578.      3            + "\n\n\n\n\n\n\n\n"
    +
     1579.      3        );
    +
     1580.      3        result = result.replace((
    +
     1581.      3            /\r\n*/g
    +
     1582.      3        ), "\n");
    +
     1583.      3        return result;
    +
     1584.      3    }));
    +
     1585.      1
    +
     1586.      1// Init module_list.
    +
     1587.      1
    +
     1588.      1    module_list = await Promise.all(module_list.map(async function ({
    +
     1589.      1        pathname
    +
     1590.      1    }) {
    +
     1591.      1        let moduleName = htmlEscape(JSON.stringify(pathname));
    +
     1592.      1        let moduleObj = await import(pathname);
    +
     1593.      1        if (moduleObj.default) {
    +
     1594.      1            moduleObj = moduleObj.default;
    +
     1595.      1        }
    +
     1596.      1        return {
    +
     1597.     27            elem_list: Object.keys(moduleObj).map(function (key) {
    +
     1598.     27                return elem_create(moduleObj, key, moduleName);
    +
     1599.     78            }).sort(function (aa, bb) {
    +
     1600.     78                return (
    +
     1601.     78                    aa.name < bb.name
    +
     1602.     22                    ? -1
    +
     1603.     56                    : 1
    +
     1604.     78                );
    +
     1605.     27            }).map(function (elem) {
    +
     1606.     27                elem_ii += 1;
    +
     1607.     27                elem.signature = elem.signature.replace(
    +
     1608.     27                    ">",
    +
     1609.     27                    ">" + elem_ii + ". "
    +
     1610.     27                );
    +
     1611.     27                elem.source = elem.source.replace(
    +
     1612.     27                    "\">",
    +
     1613.     27                    "\">" + elem_ii + ". "
    +
     1614.     27                );
    +
     1615.     27                return elem;
    +
     1616.     27            }),
    +
     1617.      1            id: encodeURIComponent("apidoc.module." + moduleName),
    +
     1618.      1            moduleName
    +
     1619.      1        };
    +
     1620.      1    }));
    +
     1621.      1    html = (`
    +
     1622.      1<!DOCTYPE html>
    +
     1623.      1<html lang="en">
    +
     1624.      1<head>
    +
     1625.      1<meta charset="utf-8">
    +
     1626.      1<meta name="viewport" content="width=device-width, initial-scale=1">
    +
     1627.      1<meta name="description" content="${package_name} API Doc">
    +
     1628.      1<title>${package_name} apidoc</title>
    +
     1629.      1<style>
    +
     1630.      1/* jslint utility2:true */
    +
     1631.      1/*csslint*/
    +
     1632.      1body {
    +
     1633.      1    margin: 0;
    +
     1634.      1    padding: 20px;
    +
     1635.      1}
    +
     1636.      1.apidocCodeCommentSpan,
    +
     1637.      1.apidocCodeKeywordSpan {
    +
     1638.      1    background: royalblue;
    +
     1639.      1    color: white;
    +
     1640.      1}
    +
     1641.      1.apidocCodeCommentSpan {
    +
     1642.      1    display: block;
    +
     1643.      1}
    +
     1644.      1.apidocCodePre {
    +
     1645.      1    background: #eef;
    +
     1646.      1    border: 1px solid;
    +
     1647.      1    font-size: 14px;
    +
     1648.      1    overflow-wrap: break-word;
    +
     1649.      1    padding: 5px;
    +
     1650.      1    white-space: pre-wrap;
    +
     1651.      1}
    +
     1652.      1.apidocDiv {
    +
     1653.      1    color: #555;
    +
     1654.      1    font-family: sans-serif;
    +
     1655.      1}
    +
     1656.      1.apidocDiv a[href] {
    +
     1657.      1    color: royalblue;
    +
     1658.      1    text-decoration: none;
    +
     1659.      1}
    +
     1660.      1.apidocDiv a[href]:hover {
    +
     1661.      1    text-decoration: underline;
    +
     1662.      1}
    +
     1663.      1.apidocDiv li a {
    +
     1664.      1    display: inline-block;
    +
     1665.      1    padding: 8px 0;
    +
     1666.      1}
    +
     1667.      1.apidocDiv ul {
    +
     1668.      1    list-style: none;
    +
     1669.      1    padding-left: 20px;
    +
     1670.      1}
    +
     1671.      1.apidocFooterDiv {
    +
     1672.      1    margin-top: 20px;
    +
     1673.      1    text-align: center;
    +
     1674.      1}
    +
     1675.      1.apidocModuleA {
    +
     1676.      1    font-size: 24px;
    +
     1677.      1    font-weight: bold;
    +
     1678.      1}
    +
     1679.      1.apidocSectionDiv {
    +
     1680.      1    border-top: 1px solid;
    +
     1681.      1    margin-top: 20px;
    +
     1682.      1}
    +
     1683.      1.apidocSignatureSpan {
    +
     1684.      1    color: #666;
    +
     1685.      1    white-space: pre-wrap;
    +
     1686.      1}
    +
     1687.      1</style>
    +
     1688.      1</head>
    +
     1689.      1<body>
    +
     1690.      1<div class="apidocDiv">
    +
     1691.      1<h1>API Doc for <a href="${github_repo}">${package_name} (${version})</a></h1>
    +
     1692.      1<div class="apidocSectionDiv">
    +
     1693.      1    <a href="#apidocTableOfContents1" id="apidocTableOfContents1">
    +
     1694.      1        <h1>Table of Contents</h1>
    +
     1695.      1    </a>
    +
     1696.      1    <ul>
    +
     1697.      1    `) + module_list.map(function ({
    +
     1698.      1        elem_list,
    +
     1699.      1        id,
    +
     1700.      1        moduleName
    +
     1701.      1    }) {
    +
     1702.      1        return (
    +
     1703.      1            (`
    +
     1704.      1        <li>
    +
     1705.      1            <a class="apidocModuleA" href="#${id}">Module ${moduleName}</a>
    +
     1706.      1            <ul>
    +
     1707.      1            `)
    +
     1708.     27            + elem_list.map(function ({
    +
     1709.     27                signature
    +
     1710.     27            }) {
    +
     1711.     27                return "<li>\n" + signature + "\n</li>\n";
    +
     1712.     27            }).join("")
    +
     1713.      1            + (`
    +
     1714.      1            </ul>
    +
     1715.      1        </li>
    +
     1716.      1            `)
    +
     1717.      1        );
    +
     1718.      1    }).join("") + (`
    +
     1719.      1    </ul>
    +
     1720.      1</div>
    +
     1721.      1    `) + module_list.map(function ({
    +
     1722.      1        elem_list,
    +
     1723.      1        id,
    +
     1724.      1        moduleName
    +
     1725.      1    }) {
    +
     1726.      1        return (
    +
     1727.      1            (`
    +
     1728.      1<div class="apidocSectionDiv">
    +
     1729.      1    <h1><a href="#${id}" id="${id}">Module ${moduleName}</a></h1>
    +
     1730.      1    <ul>
    +
     1731.      1            `)
    +
     1732.     27            + elem_list.map(function ({
    +
     1733.     27                source
    +
     1734.     27            }) {
    +
     1735.     27                return source;
    +
     1736.     27            }).join("")
    +
     1737.      1            + (`
    +
     1738.      1    </ul>
    +
     1739.      1</div>
    +
     1740.      1            `)
    +
     1741.      1        );
    +
     1742.      1    }).join("") + (`
    +
     1743.      1<div class="apidocFooterDiv">
    +
     1744.      1    [
    +
     1745.      1    This document was created with
    +
     1746.      1    <a href="https://github.com/jslint-org/jslint">JSLint</a>
    +
     1747.      1    ]
    +
     1748.      1</div>
    +
     1749.      1</div>
    +
     1750.      1</body>
    +
     1751.      1</html>
    +
     1752.      1    `);
    +
     1753.      1    html = html.trim().replace((
    +
     1754.      1        / +?$/gm
    +
     1755.      1    ), "") + "\n";
    +
     1756.      1    await fsWriteFileWithParents(pathname, html);
    +
     1757.      1}
    +
     1758.      1
    +
     1759. 102849function jslint_assert(condition, message) {
    +
     1760. 102849
    +
     1761. 102849// This function will throw <message> if <condition> is falsy.
    +
     1762. 102849
    +
     1763. 102847    if (condition) {
    +
     1764. 102847        return condition;
    +
     1765. 102847    }
    +
     1766.      2    throw new Error(
    +
     1767.      2        `This was caused by a bug in JSLint.
    +
     1768.      2Please open an issue with this stack-trace (and possible example-code) at
    +
     1769.      2https://github.com/jslint-org/jslint/issues.
    +
     1770.      2edition = "${jslint_edition}";
    +
     1771.      2${String(message).slice(0, 2000)}`
    +
     1772.      2    );
    +
     1773.      2}
    +
     1774.      1
    +
     1775.     38async function jslint_cli({
    +
     1776.     38    console_error,
    +
     1777.     38    console_log,
    +
     1778.     38    file,
    +
     1779.     38    import_meta_url,
    +
     1780.     38    mode_cli,
    +
     1781.     38    mode_noop,
    +
     1782.     38    option,
    +
     1783.     38    process_argv,
    +
     1784.     38    process_env,
    +
     1785.     38    process_exit,
    +
     1786.     38    source
    +
     1787.     38}) {
    +
     1788.     38
    +
     1789.     38// This function will run jslint from nodejs-cli.
    +
     1790.     38
    +
     1791.     38    let command;
    +
     1792.     38    let data;
    +
     1793.     38    let exit_code = 0;
    +
     1794.     38    let mode_report;
    +
     1795.     38    let mode_wrapper_vim;
    +
     1796.     38    let result;
    +
     1797.     38
    +
     1798.     51    function jslint_from_file({
    +
     1799.     51        code,
    +
     1800.     51        file,
    +
     1801.     51        line_offset = 0,
    +
     1802.     51        mode_conditional,
    +
     1803.     51        option = empty()
    +
     1804.     51    }) {
    +
     1805.     51        let result_from_file;
    +
     1806.     51        if (
    +
     1807.     51            mode_conditional
    +
     1808.      5            && !(
    +
     1809.      5                /^\/\*jslint\b/m
    +
     1810.      5            ).test(code.slice(0, 65536))
    +
     1811.      1        ) {
    +
     1812.      1            return;
    +
     1813.     50        }
    +
     1814.     50        option = Object.assign(empty(), option, {
    +
     1815.     50            file
    +
     1816.     50        });
    +
     1817.     50        switch ((
    +
     1818.     50            /\.\w+?$|$/m
    +
     1819.     50        ).exec(file)[0]) {
    +
     1820.     50        case ".html":
    +
     1821.      3
    +
     1822.      3// Recursively jslint embedded "<script>\n...\n</script>".
    +
     1823.      3
    +
     1824.      3            code.replace((
    +
     1825.      3                /^<script\b[^>]*?>\n([\S\s]*?\n)<\/script>$/gm
    +
     1826.      3            ), function (ignore, match1, ii) {
    +
     1827.      3                jslint_from_file({
    +
     1828.      3                    code: match1,
    +
     1829.      3                    file: file + ".<script>.js",
    +
     1830.      3                    line_offset: string_line_count(code.slice(0, ii)) + 1,
    +
     1831.      3                    option: Object.assign(empty(), {
    +
     1832.      3                        browser: true
    +
     1833.      3                    }, option)
    +
     1834.      3                });
    +
     1835.      3                return "";
    +
     1836.      3            });
    +
     1837.      3            return;
    +
     1838.      2        case ".md":
    +
     1839.      2
    +
     1840.      2// Recursively jslint embedded "node --eval '\n...\n'".
    +
     1841.      2
    +
     1842.      2            jslint_node_eval({
    +
     1843.      2                code,
    +
     1844.      2                file,
    +
     1845.      2                mode_conditional: true,
    +
     1846.      2                option
    +
     1847.      2            });
    +
     1848.      2            return;
    +
     1849.      2        case ".sh":
    +
     1850.      2
    +
     1851.      2// Recursively jslint embedded "node --eval '\n...\n'".
    +
     1852.      2
    +
     1853.      2            jslint_node_eval({
    +
     1854.      2                code,
    +
     1855.      2                file,
    +
     1856.      2                option
    +
     1857.      2            });
    +
     1858.      2            return;
    +
     1859.     43        default:
    +
     1860.     43            result_from_file = jslint("\n".repeat(line_offset) + code, option);
    +
     1861.     43        }
    +
     1862.     43
    +
     1863.     43// Print only first 10 warnings to stderr.
    +
     1864.     43
    +
     1865.     43        if (result_from_file.warnings.length > 0) {
    +
     1866.      5            exit_code = 1;
    +
     1867.      5            console_error(
    +
     1868.      5                mode_wrapper_vim
    +
     1869.      5
    +
     1870.      5// PR-349 - Print warnings in format readable by vim.
    +
     1871.      5
    +
     1872.      5                ? result_from_file.warnings.slice(0, 10).map(function ({
    +
     1873.      5                    column,
    +
     1874.      5                    line,
    +
     1875.      5                    message
    +
     1876.      5                }, ii) {
    +
     1877.      5                    return (
    +
     1878.      5                        file
    +
     1879.      5                        + ":" + ii
    +
     1880.      5                        + ":" + line
    +
     1881.      5                        + ":" + column
    +
     1882.      5                        + ":" + message
    +
     1883.      5                    );
    +
     1884.      5                }).join("\n")
    +
     1885.      5
    +
     1886.      5// Print warnings in format readable by human.
    +
     1887.      5
    +
     1888.      5                : "\u001b[1mjslint " + file + "\u001b[22m\n"
    +
     1889.     11                + result_from_file.warnings.slice(0, 10).map(function ({
    +
     1890.     11                    formatted_message
    +
     1891.     11                }) {
    +
     1892.     11                    return formatted_message;
    +
     1893.     11                }).join("\n")
    +
     1894.      5            );
    +
     1895.     43        }
    +
     1896.     43        return result_from_file;
    +
     1897.     43    }
    +
     1898.     38
    +
     1899.      4    function jslint_node_eval({
    +
     1900.      4        code,
    +
     1901.      4        file,
    +
     1902.      4        mode_conditional,
    +
     1903.      4        option = empty()
    +
     1904.      4    }) {
    +
     1905.      4        code.replace((
    +
     1906.      4            /\bnode\b.*? (?:--eval|-e) '\n([\S\s]*?\n)'/gm
    +
     1907.     26        ), function (ignore, match1, ii) {
    +
     1908.     26            jslint_from_file({
    +
     1909.     26                code: match1,
    +
     1910.     26                file: file + ".<node -e>.js",
    +
     1911.     26                line_offset: string_line_count(code.slice(0, ii)) + 1,
    +
     1912.     26                mode_conditional,
    +
     1913.     26                option: Object.assign(empty(), {
    +
     1914.     26                    beta: Boolean(
    +
     1915.     26                        process_env.JSLINT_BETA
    +
     1916.     26                        && !(
    +
     1917.     26                            /0|false|null|undefined/
    +
     1918.     26                        ).test(process_env.JSLINT_BETA)
    +
     1919.     26                    ),
    +
     1920.     26                    node: true
    +
     1921.     26                }, option)
    +
     1922.     26            });
    +
     1923.     26            return "";
    +
     1924.     26        });
    +
     1925.      4    }
    +
     1926.     38
    +
     1927.     28    function string_line_count(code) {
    +
     1928.     28
    +
     1929.     28// This function will count number of newlines in <code>.
    +
     1930.     28
    +
     1931.     28        let count;
    +
     1932.     28        let ii;
    +
     1933.     28
    +
     1934.     28// https://jsperf.com/regexp-counting-2/8
    +
     1935.     28
    +
     1936.     28        count = 0;
    +
     1937.     28        ii = 0;
    +
     1938.  23104        while (true) {
    +
     1939.  23104            ii = code.indexOf("\n", ii) + 1;
    +
     1940.  23104            if (ii === 0) {
    +
     1941.  23104                break;
    +
     1942.  23104            }
    +
     1943.  23104            count += 1;
    +
     1944.  23104        }
    +
     1945.     28        return count;
    +
     1946.     28    }
    +
     1947.     38
    +
     1948.     38// PR-396 - window.jslint
    +
     1949.     38// Check import.meta.url for directive to export jslint to window-object.
    +
     1950.     38// Useful for ES5-era browser-scripts that rely on window.jslint,
    +
     1951.     38// like CodeMirror.
    +
     1952.     38//
    +
     1953.     38// Example usage:
    +
     1954.     38// <script type="module" src="./jslint.mjs?window_jslint=1"></script>
    +
     1955.     38
    +
     1956.     22    import_meta_url = import_meta_url || jslint_import_meta_url;
    +
     1957.     38    if (
    +
     1958.     38        jslint_rgx_url_search_window_jslint.test(import_meta_url)
    +
     1959.      4        && (typeof globalThis === "object" && globalThis)
    +
     1960.      4    ) {
    +
     1961.      4        globalThis.jslint = jslint;
    +
     1962.      4    }
    +
     1963.     38
    +
     1964.     38// Feature-detect nodejs.
    +
     1965.     38
    +
     1966.     38    if (!(
    +
     1967.     38        (typeof process === "object" && process)
    +
     1968.     38        && process.versions
    +
     1969.     38        && typeof process.versions.node === "string"
    +
     1970.     38        && !mode_noop
    +
     1971.      1    )) {
    +
     1972.      1        return exit_code;
    +
     1973.     37    }
    +
     1974.     37    console_error = console_error || console.error;
    +
     1975.     37    console_log = console_log || console.log;
    +
     1976.     22    process_argv = process_argv || process.argv;
    +
     1977.     34    process_env = process_env || process.env;
    +
     1978.     24    process_exit = process_exit || process.exit;
    +
     1979.     37    await moduleFsInit();
    +
     1980.     37    if (
    +
     1981.     37        !(
    +
     1982.     37
    +
     1983.     37// Feature-detect nodejs-cli.
    +
     1984.     37
    +
     1985.     37            process.execArgv.indexOf("--eval") === -1
    +
     1986.     37            && process.execArgv.indexOf("-e") === -1
    +
     1987.     37            && (
    +
     1988.     37                (
    +
     1989.     37                    /[\/|\\]jslint(?:\.[cm]?js)?$/m
    +
     1990.     37                ).test(process_argv[1])
    +
     1991.     37                || mode_cli
    +
     1992.     37            )
    +
     1993.     20            && (
    +
     1994.     20                moduleUrl.fileURLToPath(import_meta_url)
    +
     1995.     20                ===
    +
     1996.     20                modulePath.resolve(process_argv[1])
    +
     1997.     20            )
    +
     1998.     38        )
    +
     1999.     22        && !mode_cli
    +
     2000.     17    ) {
    +
     2001.     17        return exit_code;
    +
     2002.     20    }
    +
     2003.     20
    +
     2004.     20// init commmand
    +
     2005.     20
    +
     2006.     20    command = String(process_argv[2]).split("=");
    +
     2007.     20    command[1] = command.slice(1).join("=");
    +
     2008.     20
    +
     2009.     20    switch (command[0]) {
    +
     2010.     20
    +
     2011.     20// PR-362 - Add API Doc.
    +
     2012.     20
    +
     2013.     20    case "jslint_apidoc":
    +
     2014.      1        await jslint_apidoc(Object.assign(JSON.parse(process_argv[3]), {
    +
     2015.      1            pathname: command[1]
    +
     2016.      1        }));
    +
     2017.      1        return;
    +
     2018.     38
    +
     2019.     38// PR-363 - Add command jslint_report.
    +
     2020.     38
    +
     2021.      6    case "jslint_report":
    +
     2022.      6        mode_report = command[1];
    +
     2023.      6        process_argv = process_argv.slice(1);
    +
     2024.      6        break;
    +
     2025.     38
    +
     2026.     38// COMMIT-b26d6df2 - Add command jslint_wrapper_vim.
    +
     2027.     38
    +
     2028.      1    case "jslint_wrapper_vim":
    +
     2029.      1        mode_wrapper_vim = true;
    +
     2030.      1        process_argv = process_argv.slice(1);
    +
     2031.      1        break;
    +
     2032.     38
    +
     2033.     38// PR-364 - Add command v8_coverage_report.
    +
     2034.     38
    +
     2035.      7    case "v8_coverage_report":
    +
     2036.      7        await v8CoverageReportCreate({
    +
     2037.      7            consoleError: console_error,
    +
     2038.      7            coverageDir: command[1],
    +
     2039.      7            processArgv: process_argv.slice(3)
    +
     2040.      7        });
    +
     2041.      7        return;
    +
     2042.     12    }
    +
     2043.     12
    +
     2044.     12// Normalize file relative to process.cwd().
    +
     2045.     12
    +
     2046.     12    process_argv.slice(2).some(function (arg) {
    +
     2047.     12        if (!arg.startsWith("-")) {
    +
     2048.     12            file = file || arg;
    +
     2049.     12            return true;
    +
     2050.     12        }
    +
     2051.     12    });
    +
     2052.     12    if (!file) {
    +
     2053.      1        return;
    +
     2054.     11    }
    +
     2055.     11    file = modulePath.resolve(file) + "/";
    +
     2056.     11    if (file.startsWith(process.cwd() + "/")) {
    +
     2057.     11        file = file.replace(process.cwd() + "/", "").slice(0, -1) || ".";
    +
     2058.     11    }
    +
     2059.     11    file = file.replace((
    +
     2060.     11        /\\/g
    +
     2061.     11    ), "/").replace((
    +
     2062.     11        /\/$/g
    +
     2063.     11    ), "");
    +
     2064.     11    if (source) {
    +
     2065.      7        data = source;
    +
     2066.      7    } else {
    +
     2067.      4
    +
     2068.      4// jslint_cli - jslint directory.
    +
     2069.      4
    +
     2070.      4        try {
    +
     2071.      4            data = await moduleFs.promises.readdir(file, "utf8");
    +
     2072.      4        } catch (ignore) {}
    +
     2073.      4        if (data) {
    +
     2074.     34            await Promise.all(data.map(async function (file2) {
    +
     2075.     34                let code;
    +
     2076.     34                let time_start = Date.now();
    +
     2077.     34                file2 = file + "/" + file2;
    +
     2078.     34                switch ((
    +
     2079.     34                    /\.\w+?$|$/m
    +
     2080.     34                ).exec(file2)[0]) {
    +
     2081.      4                case ".cjs":
    +
     2082.      5                case ".html":
    +
     2083.      8                case ".js":
    +
     2084.     10                case ".json":
    +
     2085.     12                case ".md":
    +
     2086.     14                case ".mjs":
    +
     2087.     16                case ".sh":
    +
     2088.     16                    break;
    +
     2089.     18                default:
    +
     2090.     18                    return;
    +
     2091.     16                }
    +
     2092.     16                try {
    +
     2093.     16                    code = await moduleFs.promises.readFile(file2, "utf8");
    +
     2094.     15                } catch (ignore) {
    +
     2095.      4                    return;
    +
     2096.     15                }
    +
     2097.     15                if (
    +
     2098.     15                    (
    +
     2099.     15                        /(?:\b|_)(?:lock|min|raw|rollup)(?:\b|_)/
    +
     2100.     15                    ).test(file2)
    +
     2101.     15                    || !(code && code.length < 1048576)
    +
     2102.      4                ) {
    +
     2103.      4                    return;
    +
     2104.     14                }
    +
     2105.     14                jslint_from_file({
    +
     2106.     14                    code,
    +
     2107.     14                    file: file2,
    +
     2108.     14                    option
    +
     2109.     14                });
    +
     2110.     14                console_error(
    +
     2111.     14                    "jslint - " + (Date.now() - time_start) + "ms - " + file2
    +
     2112.     14                );
    +
     2113.     14            }));
    +
     2114.      4            process_exit(exit_code);
    +
     2115.      4            return exit_code;
    +
     2116.      4        }
    +
     2117.      4
    +
     2118.      4// jslint_cli - jslint file.
    +
     2119.      4
    +
     2120.      4        try {
    +
     2121.      4            data = await moduleFs.promises.readFile(file, "utf8");
    +
     2122.      4        } catch (err) {
    +
     2123.      4            console_error(err);
    +
     2124.      4            exit_code = 1;
    +
     2125.      4            process_exit(exit_code);
    +
     2126.      4            return exit_code;
    +
     2127.      4        }
    +
     2128.      9    }
    +
     2129.      9    result = jslint_from_file({
    +
     2130.      9        code: data,
    +
     2131.      9        file,
    +
     2132.      9        option
    +
     2133.      9    });
    +
     2134.      9    if (mode_report) {
    +
     2135.      6        result = jslint.jslint_report(result);
    +
     2136.      6        result = `<body class="JSLINT_ JSLINT_REPORT_">\n${result}</body>\n`;
    +
     2137.      6        await fsWriteFileWithParents(mode_report, result);
    +
     2138.      9    }
    +
     2139.      9    process_exit(exit_code);
    +
     2140.      9    return exit_code;
    +
     2141.      9}
    +
     2142.      1
    +
     2143.    668function jslint_phase1_split() {
    +
     2144.    668
    +
     2145.    668// PHASE 1. Split <source> by newlines into <line_list>.
    +
     2146.    668
    +
     2147.    668    return;
    +
     2148.    668}
    +
     2149.      1
    +
     2150.    668function jslint_phase2_lex(state) {
    +
     2151.    668
    +
     2152.    668// PHASE 2. Lex <line_list> into <token_list>.
    +
     2153.    668
    +
     2154.    668    let {
    +
     2155.    668        artifact,
    +
     2156.    668        directive_list,
    +
     2157.    668        global_dict,
    +
     2158.    668        global_list,
    +
     2159.    668        line_list,
    +
     2160.    668        option_dict,
    +
     2161.    668        stop,
    +
     2162.    668        stop_at,
    +
     2163.    668        tenure,
    +
     2164.    668        test_cause,
    +
     2165.    668        token_global,
    +
     2166.    668        token_list,
    +
     2167.    668        warn,
    +
     2168.    668        warn_at
    +
     2169.    668    } = state;
    +
     2170.    668    let char;                   // The current character being lexed.
    +
     2171.    668    let column = 0;             // The column number of the next character.
    +
     2172.    668    let from;                   // The starting column number of the token.
    +
     2173.    668    let from_mega;              // The starting column of megastring.
    +
     2174.    668    let line = 0;               // The line number of the next character.
    +
     2175.    668    let line_disable;           // The starting line of "/*jslint-disable*/".
    +
     2176.    668    let line_mega;              // The starting line of megastring.
    +
     2177.    668    let line_source = "";       // The remaining line source string.
    +
     2178.    668    let line_whole = "";        // The whole line source string.
    +
     2179.    668    let mode_digits_empty_string = 1;
    +
     2180.    668    let mode_digits_numeric_separator = 2;
    +
     2181.    668    let mode_directive = true;  // true if directives are still allowed.
    +
     2182.    668    let mode_mega = false;      // true if currently parsing a megastring
    +
     2183.    668                                // ... literal.
    +
     2184.    668    let mode_regexp;            // true if regular expression literal seen on
    +
     2185.    668                                // ... this line.
    +
     2186.    668    let paren_backtrack_list = [];      // List of most recent "(" tokens at any
    +
     2187.    668                                        // ... paren-depth.
    +
     2188.    668    let paren_depth = 0;        // Keeps track of current paren-depth.
    +
     2189.    668    let snippet = "";           // A piece of string.
    +
     2190.    668    let token_1;                // The first token.
    +
     2191.    668    let token_prv = token_global;       // The previous token including
    +
     2192.    668                                        // ... comments.
    +
     2193.    668    let token_prv_expr = token_global;  // The previous token excluding
    +
     2194.    668                                        // ... comments.
    +
     2195.    668
    +
     2196.    668// Most tokens, including the identifiers, operators, and punctuators, can be
    +
     2197.    668// found with a regular expression. Regular expressions cannot correctly match
    +
     2198.    668// regular expression literals, so we will match those the hard way. String
    +
     2199.    668// literals and number literals can be matched by regular expressions, but they
    +
     2200.    668// don't provide good warnings. The functions char_after, char_before,
    +
     2201.    668// read_digits, and char_after_escape help in the parsing of literals.
    +
     2202.    668
    +
     2203. 238213    function char_after(match) {
    +
     2204. 238213
    +
     2205. 238213// Get the next character from the source line. Remove it from the line_source,
    +
     2206. 238213// and append it to the snippet. Optionally check that the previous character
    +
     2207. 238213// matched an expected value.
    +
     2208. 238213
    +
     2209.   5986        if (match !== undefined && char !== match) {
    +
     2210.     10
    +
     2211.     10// test_cause:
    +
     2212.     10// ["aa=/[", "char_after", "expected_a", "]", 5]
    +
     2213.     10// ["aa=/aa{/", "char_after", "expected_a_b", "/", 8]
    +
     2214.     10
    +
     2215.     10            return (
    +
     2216.     10                char === ""
    +
     2217.     10                ? stop_at("expected_a", line, column - 1, match)
    +
     2218.     10                : stop_at("expected_a_b", line, column, match, char)
    +
     2219.     10            );
    +
     2220. 238203        }
    +
     2221. 238203        char = line_source.slice(0, 1);
    +
     2222. 238203        line_source = line_source.slice(1);
    +
     2223. 238203        snippet += char || " ";
    +
     2224. 238213        column += 1;
    +
     2225. 238213        return char;
    +
     2226. 238213    }
    +
     2227.    668
    +
     2228.   2953    function char_after_escape(extra) {
    +
     2229.   2953
    +
     2230.   2953// Validate char after escape "\\".
    +
     2231.   2953
    +
     2232.   2953        char_after("\\");
    +
     2233.   2953        switch (char) {
    +
     2234.      1        case "":
    +
     2235.      1
    +
     2236.      1// test_cause:
    +
     2237.      1// ["\"\\", "char_after_escape", "unclosed_string", "", 2]
    +
     2238.      1
    +
     2239.      1            return stop_at("unclosed_string", line, column);
    +
     2240.    219        case "/":
    +
     2241.    219            return char_after();
    +
     2242.    355        case "\\":
    +
     2243.    355            return char_after();
    +
     2244.      1        case "`":
    +
     2245.      1            return char_after();
    +
     2246.     62        case "b":
    +
     2247.     62            return char_after();
    +
     2248.      6        case "f":
    +
     2249.      6            return char_after();
    +
     2250.   1015        case "n":
    +
     2251.   1015            return char_after();
    +
     2252.     32        case "r":
    +
     2253.     32            return char_after();
    +
     2254.     24        case "t":
    +
     2255.     24
    +
     2256.     24// test_cause:
    +
     2257.     24// ["\"\\/\\\\\\`\\b\\f\\n\\r\\t\"", "char_after_escape", "char_after", "", 0]
    +
     2258.     24
    +
     2259.     24            test_cause("char_after");
    +
     2260.     24            return char_after();
    +
     2261.    376        case "u":
    +
     2262.    376            if (char_after("u") === "{") {
    +
     2263.    376                if (state.mode_json) {
    +
     2264.    376
    +
     2265.    376// test_cause:
    +
     2266.    376// ["[\"\\u{12345}\"]", "char_after_escape", "unexpected_a", "{", 5]
    +
     2267.    376
    +
     2268.    376                    warn_at("unexpected_a", line, column, char);
    +
     2269.    376                }
    +
     2270.    376                if (read_digits("x", undefined) > 5) {
    +
     2271.    376
    +
     2272.    376// test_cause:
    +
     2273.    376// ["\"\\u{123456}\"", "char_after_escape", "too_many_digits", "", 11]
    +
     2274.    376
    +
     2275.    376                    warn_at("too_many_digits", line, column);
    +
     2276.    376                }
    +
     2277.    376                if (char !== "}") {
    +
     2278.    376
    +
     2279.    376// test_cause:
    +
     2280.    376// ["\"\\u{12345\"", "char_after_escape", "expected_a_before_b", "\"", 10]
    +
     2281.    376
    +
     2282.    376                    stop_at("expected_a_before_b", line, column, "}", char);
    +
     2283.    376                }
    +
     2284.    376                return char_after();
    +
     2285.    376            }
    +
     2286.    376            char_before();
    +
     2287.    376            if (read_digits("x", mode_digits_empty_string) < 4) {
    +
     2288.    376
    +
     2289.    376// test_cause:
    +
     2290.    376// ["\"\\u0\"", "char_after_escape", "expected_four_digits", "", 5]
    +
     2291.    376
    +
     2292.    376                warn_at("expected_four_digits", line, column);
    +
     2293.    376            }
    +
     2294.    376            return;
    +
     2295.    862        default:
    +
     2296.    862            if (extra && extra.indexOf(char) >= 0) {
    +
     2297.    862                return char_after();
    +
     2298.    862            }
    +
     2299.    862
    +
     2300.    862// test_cause:
    +
     2301.    862// ["\"\\0\"", "char_after_escape", "unexpected_a_before_b", "0", 3]
    +
     2302.    862
    +
     2303.    862            warn_at("unexpected_a_before_b", line, column, "\\", char);
    +
     2304.   2953        }
    +
     2305.   2953    }
    +
     2306.    668
    +
     2307.  10013    function char_before() {
    +
     2308.  10013
    +
     2309.  10013// Back up one character by moving a character from the end of the snippet to
    +
     2310.  10013// the front of the line_source.
    +
     2311.  10013
    +
     2312.  10013        char = snippet.slice(-1);
    +
     2313.  10013        line_source = char + line_source;
    +
     2314.  10013        column -= char.length;
    +
     2315.  10013
    +
     2316.  10013// Remove last character from snippet.
    +
     2317.  10013
    +
     2318.  10013        snippet = snippet.slice(0, -1);
    +
     2319.  10013        return char;
    +
     2320.  10013    }
    +
     2321.    668
    +
     2322.   8924    function check_numeric_separator(digits, column) {
    +
     2323.   8924
    +
     2324.   8924// This function will check for illegal numeric-separator in <digits>.
    +
     2325.   8924
    +
     2326.   8924        digits.replace((
    +
     2327.   8924            jslint_rgx_numeric_separator_illegal
    +
     2328.      6        ), function (ignore, ii) {
    +
     2329.      6
    +
     2330.      6// test_cause:
    +
     2331.      6// ["0x0_0_;", "check_numeric_separator", "illegal_num_separator", "", 6]
    +
     2332.      6// ["0x0_0__0;", "check_numeric_separator", "illegal_num_separator", "", 6]
    +
     2333.      6// ["aa=1_2_;", "check_numeric_separator", "illegal_num_separator", "", 7]
    +
     2334.      6// ["aa=1_2__3;", "check_numeric_separator", "illegal_num_separator", "", 7]
    +
     2335.      6// ["aa=1_2_n;", "check_numeric_separator", "illegal_num_separator", "", 7]
    +
     2336.      6
    +
     2337.      6            warn_at("illegal_num_separator", line, column + ii + 1);
    +
     2338.      6            return "";
    +
     2339.      6        });
    +
     2340.   8924    }
    +
     2341.    668
    +
     2342.  11221    function lex_comment() {
    +
     2343.  11221        let body;
    +
     2344.  11221        let ii = 0;
    +
     2345.  11221        let jj = 0;
    +
     2346.  11221        let the_comment;
    +
     2347.  11221
    +
     2348.  11221// Create a comment object. Comments are not allowed in JSON text. Comments can
    +
     2349.  11221// include directives and notices of incompletion.
    +
     2350.  11221
    +
     2351.  11221// Create token from comment //....
    +
     2352.  11221
    +
     2353.  11107        if (snippet === "//") {
    +
     2354.  11107            snippet = line_source;
    +
     2355.  11107            line_source = "";
    +
     2356.  11107            the_comment = token_create("(comment)", snippet);
    +
     2357.  11107            if (mode_mega) {
    +
     2358.  11107
    +
     2359.  11107// test_cause:
    +
     2360.  11107// ["`${//}`", "lex_comment", "unexpected_comment", "`", 4]
    +
     2361.  11107
    +
     2362.  11107                warn("unexpected_comment", the_comment, "`");
    +
     2363.  11107            }
    +
     2364.  11107
    +
     2365.  11107// Create token from comment /*...*/.
    +
     2366.  11107
    +
     2367.  11107        } else {
    +
     2368.    114            snippet = [];
    +
     2369.    114            if (line_source[0] === "/") {
    +
     2370.    114
    +
     2371.    114// test_cause:
    +
     2372.    114// ["/*/", "lex_comment", "unexpected_a", "/", 2]
    +
     2373.    114
    +
     2374.    114                warn_at("unexpected_a", line, column + ii, "/");
    +
     2375.    114            }
    +
     2376.    114
    +
     2377.    114// Lex/loop through each line until "*/".
    +
     2378.    114
    +
     2379.    696            while (true) {
    +
     2380.    696                // jslint_rgx_star_slash
    +
     2381.    696                ii = line_source.indexOf("*/");
    +
     2382.    696                if (ii >= 0) {
    +
     2383.    696                    break;
    +
     2384.    696                }
    +
     2385.    696                // jslint_rgx_slash_star
    +
     2386.    696                ii = line_source.indexOf("/*");
    +
     2387.    696                if (ii >= 0) {
    +
     2388.    696
    +
     2389.    696// test_cause:
    +
     2390.    696// ["/*/*", "lex_comment", "nested_comment", "", 2]
    +
     2391.    696
    +
     2392.    696                    warn_at("nested_comment", line, column + ii);
    +
     2393.    696                }
    +
     2394.    696                snippet.push(line_source);
    +
     2395.    696                line_source = read_line();
    +
     2396.    696                if (line_source === undefined) {
    +
     2397.    696
    +
     2398.    696// test_cause:
    +
     2399.    696// ["/*", "lex_comment", "unclosed_comment", "", 1]
    +
     2400.    696
    +
     2401.    696                    return stop_at("unclosed_comment", line, column);
    +
     2402.    696                }
    +
     2403.    696            }
    +
     2404.    114            jj = line_source.slice(0, ii).search(
    +
     2405.    114                jslint_rgx_slash_star_or_slash
    +
     2406.    114            );
    +
     2407.    114            if (jj >= 0) {
    +
     2408.    114
    +
     2409.    114// test_cause:
    +
     2410.    114// ["/*/**/", "lex_comment", "nested_comment", "", 2]
    +
     2411.    114
    +
     2412.    114                warn_at("nested_comment", line, column + jj);
    +
     2413.    114            }
    +
     2414.    114            snippet.push(line_source.slice(0, ii));
    +
     2415.    114            snippet = snippet.join(" ");
    +
     2416.    114            column += ii + 2;
    +
     2417.    114            line_source = line_source.slice(ii + 2);
    +
     2418.    114            the_comment = token_create("(comment)", snippet);
    +
     2419.  11218        }
    +
     2420.  11218
    +
     2421.  11218// Uncompleted work comment.
    +
     2422.  11218
    +
     2423.  11218        if (!option_dict.devel && jslint_rgx_todo.test(snippet)) {
    +
     2424.     16
    +
     2425.     16// test_cause:
    +
     2426.     16// ["//todo", "lex_comment", "todo_comment", "(comment)", 1] //jslint-ignore-line
    +
     2427.     16
    +
     2428.     16            warn("todo_comment", the_comment);
    +
     2429.  11218        }
    +
     2430.  11218
    +
     2431.  11218// Lex directives in comment.
    +
     2432.  11218
    +
     2433.  11218        [
    +
     2434.  11218            the_comment.directive, body
    +
     2435.  11218        ] = Array.from(snippet.match(jslint_rgx_directive) || []).slice(1);
    +
     2436.  11141        if (the_comment.directive === undefined) {
    +
     2437.  11141            return the_comment;
    +
     2438.  11141        }
    +
     2439.     77        directive_list.push(the_comment);
    +
     2440.     77        if (!mode_directive) {
    +
     2441.      1
    +
     2442.      1// test_cause:
    +
     2443.      1// ["0\n/*global aa*/", "lex_comment", "misplaced_directive_a", "global", 1]
    +
     2444.      1
    +
     2445.      1            warn_at("misplaced_directive_a", line, from, the_comment.directive);
    +
     2446.      1            return the_comment;
    +
     2447.     76        }
    +
     2448.     76
    +
     2449.     76// lex_directive();
    +
     2450.     76// JSLint recognizes three directives that can be encoded in comments. This
    +
     2451.     76// function processes one item, and calls itself recursively to process the
    +
     2452.     76// next one.
    +
     2453.     76
    +
     2454.     76// Lex/loop through each directive in /*...*/
    +
     2455.     76
    +
     2456.     76        ii = 0;
    +
     2457.   1835        body.replace(jslint_rgx_directive_part, function (
    +
     2458.   1835            match0,
    +
     2459.   1835            key,
    +
     2460.   1835            val,
    +
     2461.   1835            jj
    +
     2462.   1835        ) {
    +
     2463.     76            if (ii !== jj) {
    +
     2464.     76
    +
     2465.     76// test_cause:
    +
     2466.     76// ["/*jslint !*/", "lex_comment", "bad_directive_a", "!", 1]
    +
     2467.     76
    +
     2468.     76                return stop("bad_directive_a", the_comment, body.slice(ii));
    +
     2469.   1834            }
    +
     2470.   1834            if (match0 === "") {
    +
     2471.     76                return "";
    +
     2472.   1759            }
    +
     2473.   1759            ii += match0.length;
    +
     2474.   1759            switch (the_comment.directive) {
    +
     2475.   1759            case "global":
    +
     2476.     76                if (val) {
    +
     2477.     76
    +
     2478.     76// test_cause:
    +
     2479.     76// ["/*global aa:false*/", "lex_comment", "bad_option_a", "aa:false", 1]
    +
     2480.     76
    +
     2481.     76                    warn("bad_option_a", the_comment, key + ":" + val);
    +
     2482.     76                }
    +
     2483.     76                global_dict[key] = "user-defined";
    +
     2484.     76
    +
     2485.     76// PR-347 - Disable warning "unexpected_directive_a".
    +
     2486.     76//
    +
     2487.     76//                 state.mode_module = the_comment;
    +
     2488.     76
    +
     2489.     76                break;
    +
     2490.     99            case "jslint":
    +
     2491.     99                if (!option_set_item(key, val !== "false")) {
    +
     2492.     99
    +
     2493.     99// test_cause:
    +
     2494.     99// ["/*jslint undefined*/", "lex_comment", "bad_option_a", "undefined", 1]
    +
     2495.     99
    +
     2496.     99                    warn("bad_option_a", the_comment, key);
    +
     2497.     99                }
    +
     2498.     99                break;
    +
     2499.   1651            case "property":
    +
     2500.   1651                state.mode_property = true;
    +
     2501.   1651                tenure[key] = true;
    +
     2502.   1651                break;
    +
     2503.   1759            }
    +
     2504.   1759            return "";
    +
     2505.   1759        });
    +
     2506.     76        return the_comment;
    +
     2507.     76    }
    +
     2508.    668
    +
     2509.    789    function lex_megastring() {
    +
     2510.    789        let id;
    +
     2511.    789        let match;
    +
     2512.    789
    +
     2513.    789// The token is a megastring. We don't allow any kind of mega nesting.
    +
     2514.    789
    +
     2515.      1        if (mode_mega) {
    +
     2516.      1
    +
     2517.      1// test_cause:
    +
     2518.      1// ["`${`", "lex_megastring", "expected_a_b", "`", 4]
    +
     2519.      1
    +
     2520.      1            return stop_at("expected_a_b", line, column, "}", "`");
    +
     2521.    788        }
    +
     2522.    788        from_mega = from;
    +
     2523.    788        line_mega = line;
    +
     2524.    788        mode_mega = true;
    +
     2525.    788        snippet = "";
    +
     2526.    788
    +
     2527.    788// Parsing a mega literal is tricky. First create a ` token.
    +
     2528.    788
    +
     2529.    788        token_create("`");
    +
     2530.    788        from += 1;
    +
     2531.    788
    +
     2532.    788// Then loop, building up a string, possibly from many lines, until seeing
    +
     2533.    788// the end of file, a closing `, or a ${ indicting an expression within the
    +
     2534.    788// string.
    +
     2535.    788
    +
     2536.   5963        while (true) {
    +
     2537.   5963            match = line_source.match(jslint_rgx_mega) || {
    +
     2538.   5963                "0": "",
    +
     2539.   5963                index: 0
    +
     2540.   5963            };
    +
     2541.   5963            snippet += line_source.slice(0, match.index);
    +
     2542.   5963            column += match.index;
    +
     2543.   5963            line_source = line_source.slice(match.index);
    +
     2544.   5963            match = match[0];
    +
     2545.   5963            switch (match) {
    +
     2546.   5963            case "${":
    +
     2547.   5963
    +
     2548.   5963// if either ` or ${ was found, then the preceding joins the snippet to become
    +
     2549.   5963// a string token.
    +
     2550.   5963
    +
     2551.   5963                token_create("(string)", snippet).quote = "`";
    +
     2552.   5963                snippet = "";
    +
     2553.   5963
    +
     2554.   5963// If ${, then create tokens that will become part of an expression until
    +
     2555.   5963// a } token is made.
    +
     2556.   5963
    +
     2557.   5963                column += 2;
    +
     2558.   5963                token_create("${");
    +
     2559.   5963                line_source = line_source.slice(2);
    +
     2560.   5963
    +
     2561.   5963// Lex/loop through each token inside megastring-expression `${...}`.
    +
     2562.   5963
    +
     2563.   5963                while (true) {
    +
     2564.   5963                    id = lex_token().id;
    +
     2565.   5963                    if (id === "{") {
    +
     2566.   5963
    +
     2567.   5963// test_cause:
    +
     2568.   5963// ["`${{", "lex_megastring", "expected_a_b", "{", 4]
    +
     2569.   5963
    +
     2570.   5963                        return stop_at("expected_a_b", line, column, "}", "{");
    +
     2571.   5963                    }
    +
     2572.   5963                    if (id === "}") {
    +
     2573.   5963                        break;
    +
     2574.   5963                    }
    +
     2575.   5963                }
    +
     2576.   5963                break;
    +
     2577.   5963            case "\\":
    +
     2578.   5963                snippet += line_source.slice(0, 2);
    +
     2579.   5963                line_source = line_source.slice(2);
    +
     2580.   5963                column += 2;
    +
     2581.   5963                break;
    +
     2582.   5963            case "`":
    +
     2583.   5963
    +
     2584.   5963// if either ` or ${ was found, then the preceding joins the snippet to become
    +
     2585.   5963// a string token.
    +
     2586.   5963
    +
     2587.   5963                token_create("(string)", snippet).quote = "`";
    +
     2588.   5963                snippet = "";
    +
     2589.   5963
    +
     2590.   5963// Terminate megastring with `.
    +
     2591.   5963
    +
     2592.   5963                line_source = line_source.slice(1);
    +
     2593.   5963                column += 1;
    +
     2594.   5963                mode_mega = false;
    +
     2595.   5963                return token_create("`");
    +
     2596.   5963            default:
    +
     2597.   5963
    +
     2598.   5963// If neither ` nor ${ is seen, then the whole line joins the snippet.
    +
     2599.   5963
    +
     2600.   5963                snippet += line_source + "\n";
    +
     2601.   5963                if (read_line() === undefined) {
    +
     2602.   5963
    +
     2603.   5963// test_cause:
    +
     2604.   5963// ["`", "lex_megastring", "unclosed_mega", "", 1]
    +
     2605.   5963
    +
     2606.   5963                    return stop_at("unclosed_mega", line_mega, from_mega);
    +
     2607.   5963                }
    +
     2608.   5963            }
    +
     2609.   5963        }
    +
     2610.    789    }
    +
     2611.    668
    +
     2612.   8851    function lex_number() {
    +
     2613.   8851        let prefix = snippet;
    +
     2614.   8851
    +
     2615.   8851// PR-390 - Add numeric-separator check.
    +
     2616.   8851
    +
     2617.   8851        check_numeric_separator(prefix, column - prefix.length);
    +
     2618.   8851        char_after();
    +
     2619.   2880        switch (prefix === "0" && char) {
    +
     2620.      3        case "b":
    +
     2621.      6        case "o":
    +
     2622.     33        case "x":
    +
     2623.     33            read_digits(char, mode_digits_numeric_separator);
    +
     2624.     33
    +
     2625.     33// PR-351 - Ignore BigInt suffix 'n'.
    +
     2626.     33
    +
     2627.     33            if (char === "n") {
    +
     2628.     33                char_after("n");
    +
     2629.     33            }
    +
     2630.     33            break;
    +
     2631.   8818        default:
    +
     2632.   8818            if (char === ".") {
    +
     2633.   8818                read_digits("d", mode_digits_numeric_separator);
    +
     2634.   8818            }
    +
     2635.   8818            if (char === "E" || char === "e") {
    +
     2636.   8818                char_after(char);
    +
     2637.   8818                if (char !== "+" && char !== "-") {
    +
     2638.   8818                    char_before();
    +
     2639.   8818                }
    +
     2640.   8818                read_digits("d", mode_digits_numeric_separator);
    +
     2641.   8818            }
    +
     2642.   8851        }
    +
     2643.   8851
    +
     2644.   8851// If the next character after a number is a digit or letter, then something
    +
     2645.   8851// unexpected is going on.
    +
     2646.   8851
    +
     2647.   8851        if (
    +
     2648.   1567            (char >= "0" && char <= "9")
    +
     2649.     44            || (char >= "a" && char <= "z")
    +
     2650.   8850            || (char >= "A" && char <= "Z")
    +
     2651.      1        ) {
    +
     2652.      1
    +
     2653.      1// test_cause:
    +
     2654.      1// ["0a", "lex_number", "unexpected_a_after_b", "0", 2]
    +
     2655.      1
    +
     2656.      1            return stop_at(
    +
     2657.      1                "unexpected_a_after_b",
    +
     2658.      1                line,
    +
     2659.      1                column,
    +
     2660.      1                snippet.slice(-1),
    +
     2661.      1                snippet.slice(0, -1)
    +
     2662.      1            );
    +
     2663.   8850        }
    +
     2664.   8850        char_before();
    +
     2665.   8850        return token_create("(number)", snippet);
    +
     2666.   8850    }
    +
     2667.    668
    +
     2668.    583    function lex_regexp() {
    +
     2669.    583
    +
     2670.    583// Regexp
    +
     2671.    583// Lex a regular expression literal.
    +
     2672.    583
    +
     2673.    583        let flag;
    +
     2674.    583        let mode_regexp_multiline;
    +
     2675.    583        let result;
    +
     2676.    583        let value;
    +
     2677.    583        mode_regexp = true;
    +
     2678.    583
    +
     2679.    209        function lex_regexp_bracketed() {
    +
     2680.    209            let mode_regexp_range;
    +
     2681.    209
    +
     2682.    209// RegExp
    +
     2683.    209// Match a class.
    +
     2684.    209
    +
     2685.    209            char_after("[");
    +
     2686.     35            if (char === "^") {
    +
     2687.     35                char_after("^");
    +
     2688.     35            }
    +
     2689.    902            while (true) {
    +
     2690.    902
    +
     2691.    902// RegExp
    +
     2692.    902// Match a character in a character class.
    +
     2693.    902
    +
     2694.    902                switch (char) {
    +
     2695.    902                case "":
    +
     2696.    902                case "]":
    +
     2697.    902
    +
     2698.    902// test_cause:
    +
     2699.    902// ["aa=/[", "lex_regexp_bracketed", "closer", "", 0]
    +
     2700.    902// ["aa=/[]/", "lex_regexp_bracketed", "closer", "", 0]
    +
     2701.    902
    +
     2702.    902                    test_cause("closer");
    +
     2703.    902                    if (mode_regexp_range) {
    +
     2704.    902
    +
     2705.    902// test_cause:
    +
     2706.    902// ["aa=/[0-]/", "lex_regexp_bracketed", "unexpected_a", "-", 7]
    +
     2707.    902
    +
     2708.    902                        warn_at("unexpected_a", line, column - 1, "-");
    +
     2709.    902                    }
    +
     2710.    902                    return char_after("]");
    +
     2711.    902
    +
     2712.    902// PR-362 - Relax regexp-warning against using <space>.
    +
     2713.    902//
    +
     2714.    902//                 case " ":
    +
     2715.    902//
    +
     2716.    902// // test_cause:
    +
     2717.    902// // ["aa=/[ ]/", "lex_regexp_bracketed", "expected_a_b", " ", 6]
    +
     2718.    902//
    +
     2719.    902//                     warn_at("expected_a_b", line, column, "\\u0020", " ");
    +
     2720.    902//                     break;
    +
     2721.    902
    +
     2722.    902                case "-":
    +
     2723.    902                case "/":
    +
     2724.    902                case "[":
    +
     2725.    902                case "^":
    +
     2726.    902
    +
     2727.    902// test_cause:
    +
     2728.    902// ["aa=/[-]/", "lex_regexp_bracketed", "expected_a_before_b", "-", 6]
    +
     2729.    902// ["aa=/[.^]/", "lex_regexp_bracketed", "expected_a_before_b", "^", 7]
    +
     2730.    902// ["aa=/[/", "lex_regexp_bracketed", "expected_a_before_b", "/", 6]
    +
     2731.    902// ["aa=/[\\\\/]/", "lex_regexp_bracketed", "expected_a_before_b", "/", 8]
    +
     2732.    902// ["aa=/[\\\\[]/", "lex_regexp_bracketed", "expected_a_before_b", "[", 8]
    +
     2733.    902
    +
     2734.    902                    warn_at("expected_a_before_b", line, column, "\\", char);
    +
     2735.    902                    break;
    +
     2736.    902                case "\\":
    +
     2737.    902                    char_after_escape("BbDdSsWw-[]^");
    +
     2738.    902                    char_before();
    +
     2739.    902                    break;
    +
     2740.    902                case "`":
    +
     2741.    902                    if (mode_mega) {
    +
     2742.    902
    +
     2743.    902// test_cause:
    +
     2744.    902// ["`${/[`]/}`", "lex_regexp_bracketed", "unexpected_a", "`", 6]
    +
     2745.    902
    +
     2746.    902                        warn_at("unexpected_a", line, column, "`");
    +
     2747.    902                    }
    +
     2748.    902                    break;
    +
     2749.    902                }
    +
     2750.    902                char_after();
    +
     2751.    902                mode_regexp_range = false;
    +
     2752.    902                if (char === "-") {
    +
     2753.    902
    +
     2754.    902// RegExp
    +
     2755.    902// Match a range of subclasses.
    +
     2756.    902
    +
     2757.    902                    mode_regexp_range = true;
    +
     2758.    902                    char_after("-");
    +
     2759.    902                }
    +
     2760.    902            }
    +
     2761.    209        }
    +
     2762.    583
    +
     2763.    794        function lex_regexp_group() {
    +
     2764.    794
    +
     2765.    794// RegExp
    +
     2766.    794// Lex sequence of characters in regexp.
    +
     2767.    794
    +
     2768.    794            switch (char) {
    +
     2769.      1            case "":
    +
     2770.      1                warn_at("expected_regexp_factor_a", line, column, char);
    +
     2771.      1                break;
    +
     2772.      1            case ")":
    +
     2773.      1                warn_at("expected_regexp_factor_a", line, column, char);
    +
     2774.      1                break;
    +
     2775.      1            case "]":
    +
     2776.      1
    +
     2777.      1// test_cause:
    +
     2778.      1// ["/ /", "lex_regexp_group", "expected_regexp_factor_a", "", 3]
    +
     2779.      1// ["aa=/)", "lex_regexp_group", "expected_regexp_factor_a", ")", 5]
    +
     2780.      1// ["aa=/]", "lex_regexp_group", "expected_regexp_factor_a", "]", 5]
    +
     2781.      1
    +
     2782.      1                warn_at("expected_regexp_factor_a", line, column, char);
    +
     2783.      1                break;
    +
     2784.    794            }
    +
     2785.   5592            while (true) {
    +
     2786.   5592                switch (char) {
    +
     2787.   5592                case "":
    +
     2788.   5592                case ")":
    +
     2789.   5592                case "/":
    +
     2790.   5592                case "]":
    +
     2791.   5592                    return;
    +
     2792.   5592
    +
     2793.   5592// PR-362 - Relax regexp-warning against using <space>.
    +
     2794.   5592//
    +
     2795.   5592//                 case " ":
    +
     2796.   5592//
    +
     2797.   5592// // test_cause:
    +
     2798.   5592// // ["aa=/ /", "lex_regexp_group", "expected_a_b", " ", 5]
    +
     2799.   5592//
    +
     2800.   5592//                     warn_at("expected_a_b", line, column, "\\s", " ");
    +
     2801.   5592//                     char_after();
    +
     2802.   5592//                     break;
    +
     2803.   5592
    +
     2804.   5592                case "$":
    +
     2805.   5592                    if (line_source[0] !== "/") {
    +
     2806.   5592                        mode_regexp_multiline = true;
    +
     2807.   5592                    }
    +
     2808.   5592                    char_after();
    +
     2809.   5592                    break;
    +
     2810.   5592                case "(":
    +
     2811.   5592
    +
     2812.   5592// RegExp
    +
     2813.   5592// Match a group that starts with left paren.
    +
     2814.   5592
    +
     2815.   5592                    char_after("(");
    +
     2816.   5592                    switch (char) {
    +
     2817.   5592                    case ":":
    +
     2818.   5592
    +
     2819.   5592// test_cause:
    +
     2820.   5592// ["aa=/(:)/", "lex_regexp_group", "expected_a_before_b", ":", 6]
    +
     2821.   5592// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5]
    +
     2822.   5592
    +
     2823.   5592                        warn_at("expected_a_before_b", line, column, "?", ":");
    +
     2824.   5592                        break;
    +
     2825.   5592                    case "?":
    +
     2826.   5592                        char_after("?");
    +
     2827.   5592                        switch (char) {
    +
     2828.   5592                        case "!":
    +
     2829.   5592
    +
     2830.   5592// PR-437 - Add grammar for regexp-named-capture-group.
    +
     2831.   5592
    +
     2832.   5592                        case "<":
    +
     2833.   5592                        case "=":
    +
     2834.   5592                            char_after();
    +
     2835.   5592                            break;
    +
     2836.   5592                        default:
    +
     2837.   5592                            char_after(":");
    +
     2838.   5592                        }
    +
     2839.   5592                        break;
    +
     2840.   5592                    }
    +
     2841.   5592
    +
     2842.   5592// RegExp
    +
     2843.   5592// Recurse lex_regexp_group().
    +
     2844.   5592
    +
     2845.   5592                    lex_regexp_group();
    +
     2846.   5592                    char_after(")");
    +
     2847.   5592                    break;
    +
     2848.   5592                case "*":
    +
     2849.   5592                case "+":
    +
     2850.   5592                case "?":
    +
     2851.   5592                case "{":
    +
     2852.   5592                case "}":
    +
     2853.   5592
    +
     2854.   5592// test_cause:
    +
     2855.   5592// ["aa=/+/", "lex_regexp_group", "expected_a_before_b", "+", 5]
    +
     2856.   5592// ["aa=/.**/", "lex_regexp_group", "expected_a_before_b", "*", 7]
    +
     2857.   5592// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5]
    +
     2858.   5592// ["aa=/{/", "lex_regexp_group", "expected_a_before_b", "{", 5]
    +
     2859.   5592// ["aa=/}/", "lex_regexp_group", "expected_a_before_b", "}", 5]
    +
     2860.   5592
    +
     2861.   5592                    warn_at("expected_a_before_b", line, column, "\\", char);
    +
     2862.   5592                    char_after();
    +
     2863.   5592                    break;
    +
     2864.   5592                case "[":
    +
     2865.   5592                    lex_regexp_bracketed();
    +
     2866.   5592                    break;
    +
     2867.   5592                case "\\":
    +
     2868.   5592
    +
     2869.   5592// test_cause:
    +
     2870.   5592// ["aa=/\\/", "lex_regexp_group", "escape", "", 0]
    +
     2871.   5592
    +
     2872.   5592                    test_cause("escape");
    +
     2873.   5592
    +
     2874.   5592// PR-437 - Add grammar for regexp-named-backreference.
    +
     2875.   5592
    +
     2876.   5592                    char_after_escape("BbDdSsWw^${}[]():=!.|*+?k");
    +
     2877.   5592                    break;
    +
     2878.   5592                case "^":
    +
     2879.   5592                    if (snippet !== "^") {
    +
     2880.   5592                        mode_regexp_multiline = true;
    +
     2881.   5592                    }
    +
     2882.   5592                    char_after();
    +
     2883.   5592                    break;
    +
     2884.   5592                case "`":
    +
     2885.   5592                    if (mode_mega) {
    +
     2886.   5592
    +
     2887.   5592// test_cause:
    +
     2888.   5592// ["`${/`/}`", "lex_regexp_group", "unexpected_a", "`", 5]
    +
     2889.   5592
    +
     2890.   5592                        warn_at("unexpected_a", line, column, "`");
    +
     2891.   5592                    }
    +
     2892.   5592                    char_after();
    +
     2893.   5592                    break;
    +
     2894.   5592                default:
    +
     2895.   5592                    char_after();
    +
     2896.   5592                }
    +
     2897.   5592
    +
     2898.   5592// RegExp
    +
     2899.   5592// Match an optional quantifier.
    +
     2900.   5592
    +
     2901.   5592                switch (char) {
    +
     2902.   5592                case "*":
    +
     2903.   5592                case "+":
    +
     2904.   5592                    if (char_after(char) === "?") {
    +
     2905.   5592
    +
     2906.   5592// test_cause:
    +
     2907.   5592// ["aa=/.*?/", "lex_regexp_group", "?", "", 0]
    +
     2908.   5592// ["aa=/.+?/", "lex_regexp_group", "?", "", 0]
    +
     2909.   5592
    +
     2910.   5592                        test_cause("?");
    +
     2911.   5592                        char_after("?");
    +
     2912.   5592                    }
    +
     2913.   5592                    break;
    +
     2914.   5592                case "?":
    +
     2915.   5592                    if (char_after("?") === "?") {
    +
     2916.   5592
    +
     2917.   5592// test_cause:
    +
     2918.   5592// ["aa=/.??/", "lex_regexp_group", "unexpected_a", "?", 7]
    +
     2919.   5592
    +
     2920.   5592                        warn_at("unexpected_a", line, column, char);
    +
     2921.   5592                        char_after("?");
    +
     2922.   5592                    }
    +
     2923.   5592                    break;
    +
     2924.   5592                case "{":
    +
     2925.   5592                    if (read_digits("d", mode_digits_empty_string) === 0) {
    +
     2926.   5592
    +
     2927.   5592// test_cause:
    +
     2928.   5592// ["aa=/aa{/", "lex_regexp_group", "expected_a_before_b", ",", 8]
    +
     2929.   5592
    +
     2930.   5592                        warn_at("expected_a_before_b", line, column, "0", ",");
    +
     2931.   5592                    }
    +
     2932.   5592                    if (char === ",") {
    +
     2933.   5592
    +
     2934.   5592// test_cause:
    +
     2935.   5592// ["aa=/.{,/", "lex_regexp_group", "comma", "", 0]
    +
     2936.   5592
    +
     2937.   5592                        test_cause("comma");
    +
     2938.   5592                        read_digits("d", mode_digits_empty_string);
    +
     2939.   5592                    }
    +
     2940.   5592                    if (char_after("}") === "?") {
    +
     2941.   5592
    +
     2942.   5592// test_cause:
    +
     2943.   5592// ["aa=/.{0}?/", "lex_regexp_group", "unexpected_a", "?", 9]
    +
     2944.   5592
    +
     2945.   5592                        warn_at("unexpected_a", line, column, char);
    +
     2946.   5592                        char_after("?");
    +
     2947.   5592                    }
    +
     2948.   5592                    break;
    +
     2949.   5592                }
    +
     2950.   5592            }
    +
     2951.    794        }
    +
     2952.    583
    +
     2953.    583// RegExp
    +
     2954.    583// Scan the regexp literal. Give a warning if the first character is = because
    +
     2955.    583// /= looks like a division assignment operator.
    +
     2956.    583
    +
     2957.    583        snippet = "";
    +
     2958.    583        char_after();
    +
     2959.      1        if (char === "=") {
    +
     2960.      1
    +
     2961.      1// test_cause:
    +
     2962.      1// ["aa=/=/", "lex_regexp", "expected_a_before_b", "=", 5]
    +
     2963.      1
    +
     2964.      1            warn_at("expected_a_before_b", line, column, "\\", "=");
    +
     2965.      1        }
    +
     2966.    583        lex_regexp_group();
    +
     2967.    583
    +
     2968.    583// RegExp
    +
     2969.    583// Remove last character from snippet.
    +
     2970.    583
    +
     2971.    583        snippet = snippet.slice(0, -1);
    +
     2972.    583
    +
     2973.    583// RegExp
    +
     2974.    583// Make sure there is a closing slash.
    +
     2975.    583
    +
     2976.    583        value = snippet;
    +
     2977.    583        char_after("/");
    +
     2978.    583
    +
     2979.    583// RegExp
    +
     2980.    583// Create flag.
    +
     2981.    583
    +
     2982.    583        flag = empty();
    +
     2983.    583        while (
    +
     2984.    583
    +
     2985.    583// Regexp
    +
     2986.    583// char is a letter.
    +
     2987.    583
    +
     2988.    512            (char >= "a" && char <= "z\uffff")
    +
     2989.    573            || (char >= "A" && char <= "Z\uffff")
    +
     2990.    510        ) {
    +
     2991.    510
    +
     2992.    510// RegExp
    +
     2993.    510// Process dangling flag letters.
    +
     2994.    510
    +
     2995.    510            switch (!flag[char] && char) {
    +
     2996.    510            case "g":
    +
     2997.    510                break;
    +
     2998.    510            case "i":
    +
     2999.    510                break;
    +
     3000.    510            case "m":
    +
     3001.    510                break;
    +
     3002.    510            case "u":
    +
     3003.    510                break;
    +
     3004.    510            case "y":
    +
     3005.    510
    +
     3006.    510// test_cause:
    +
     3007.    510// ["aa=/./gimuy", "lex_regexp", "flag", "", 0]
    +
     3008.    510
    +
     3009.    510                test_cause("flag");
    +
     3010.    510                break;
    +
     3011.    510            default:
    +
     3012.    510
    +
     3013.    510// test_cause:
    +
     3014.    510// ["aa=/./gg", "lex_regexp", "unexpected_a", "g", 8]
    +
     3015.    510// ["aa=/./z", "lex_regexp", "unexpected_a", "z", 7]
    +
     3016.    510
    +
     3017.    510                warn_at("unexpected_a", line, column, char);
    +
     3018.    510            }
    +
     3019.    510            flag[char] = true;
    +
     3020.    510            char_after();
    +
     3021.    573        }
    +
     3022.    573        char_before();
    +
     3023.    573        if (char === "/" || char === "*") {
    +
     3024.      1
    +
     3025.      1// test_cause:
    +
     3026.      1// ["aa=/.//", "lex_regexp", "unexpected_a", "/", 3]
    +
     3027.      1
    +
     3028.      1            return stop_at("unexpected_a", line, from, char);
    +
     3029.    572        }
    +
     3030.    572        result = token_create("(regexp)", char);
    +
     3031.    572        result.flag = flag;
    +
     3032.    572        result.value = value;
    +
     3033.    572        if (mode_regexp_multiline && !flag.m) {
    +
     3034.      1
    +
     3035.      1// test_cause:
    +
     3036.      1// ["aa=/$^/", "lex_regexp", "missing_m", "", 7]
    +
     3037.      1
    +
     3038.      1            warn_at("missing_m", line, column);
    +
     3039.    572        }
    +
     3040.    572        return result;
    +
     3041.    572    }
    +
     3042.    668
    +
     3043.    598    function lex_slash_or_regexp() {
    +
     3044.    598
    +
     3045.    598// The / can be a division operator or the beginning of a regular expression
    +
     3046.    598// literal. It is not possible to know which without doing a complete parse.
    +
     3047.    598// We want to complete the tokenization before we begin to parse, so we will
    +
     3048.    598// estimate. This estimator can fail in some cases. For example, it cannot
    +
     3049.    598// know if "}" is ending a block or ending an object literal, so it can
    +
     3050.    598// behave incorrectly in that case; it is not meaningful to divide an
    +
     3051.    598// object, so it is likely that we can get away with it. We avoided the worst
    +
     3052.    598// cases by eliminating automatic semicolon insertion.
    +
     3053.    598
    +
     3054.    598        let the_token;
    +
     3055.    598        switch (
    +
     3056.    598            token_prv_expr.identifier
    +
     3057.     18            && !token_prv_expr.dot
    +
     3058.     15            && token_prv_expr.id
    +
     3059.    598        ) {
    +
     3060.      1        case "case":
    +
     3061.      2        case "delete":
    +
     3062.      3        case "in":
    +
     3063.      4        case "instanceof":
    +
     3064.      5        case "new":
    +
     3065.      6        case "typeof":
    +
     3066.      7        case "void":
    +
     3067.      8        case "yield":
    +
     3068.      8            the_token = lex_regexp();
    +
     3069.      8
    +
     3070.      8// test_cause:
    +
     3071.      8// ["case /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6]
    +
     3072.      8// ["delete /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8]
    +
     3073.      8// ["in /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 4]
    +
     3074.      8// ["instanceof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 12]
    +
     3075.      8// ["new /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 5]
    +
     3076.      8// ["typeof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8]
    +
     3077.      8// ["void /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6]
    +
     3078.      8// ["yield /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 7]
    +
     3079.      8
    +
     3080.      8            return stop("unexpected_a", the_token);
    +
     3081.      1        case "return":
    +
     3082.      1            return lex_regexp();
    +
     3083.    589        }
    +
     3084.    589        switch (!token_prv_expr.identifier && token_prv_expr.id.slice(-1)) {
    +
     3085.      1        case "!":
    +
     3086.      2        case "%":
    +
     3087.      4        case "&":
    +
     3088.      5        case "*":
    +
     3089.      6        case "+":
    +
     3090.      7        case "-":
    +
     3091.      9        case "/":
    +
     3092.     10        case ";":
    +
     3093.     11        case "<":
    +
     3094.     12        case ">":
    +
     3095.     13        case "^":
    +
     3096.     16        case "{":
    +
     3097.     17        case "|":
    +
     3098.     18        case "}":
    +
     3099.     19        case "~":
    +
     3100.     19            the_token = lex_regexp();
    +
     3101.     19
    +
     3102.     19// test_cause:
    +
     3103.     19// ["!/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3104.     19// ["%/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3105.     19// ["&/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3106.     19// ["+/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3107.     19// ["-/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3108.     19// ["0 * /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5]
    +
     3109.     19// ["0 / /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5]
    +
     3110.     19// [";/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3111.     19// ["</./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3112.     19// [">/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3113.     19// ["^/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3114.     19// ["{/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3115.     19// ["|/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3116.     19// ["}/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3117.     19// ["~/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     3118.     19
    +
     3119.     19            warn("wrap_regexp", the_token);
    +
     3120.     19            return the_token;
    +
     3121.    514        case "(":
    +
     3122.    515        case ",":
    +
     3123.    516        case ":":
    +
     3124.    553        case "=":
    +
     3125.    554        case "?":
    +
     3126.    555        case "[":
    +
     3127.    555
    +
     3128.    555// test_cause:
    +
     3129.    555// ["(/./", "lex_slash_or_regexp", "recurse", "", 0]
    +
     3130.    555// [",/./", "lex_slash_or_regexp", "recurse", "", 0]
    +
     3131.    555// [":/./", "lex_slash_or_regexp", "recurse", "", 0]
    +
     3132.    555// ["=/./", "lex_slash_or_regexp", "recurse", "", 0]
    +
     3133.    555// ["?/./", "lex_slash_or_regexp", "recurse", "", 0]
    +
     3134.    555// ["aa[/./", "lex_slash_or_regexp", "recurse", "", 0]
    +
     3135.    555
    +
     3136.    555            test_cause("recurse");
    +
     3137.    555            return lex_regexp();
    +
     3138.     15        }
    +
     3139.     15        if (line_source[0] === "=") {
    +
     3140.      1            column += 1;
    +
     3141.      1            line_source = line_source.slice(1);
    +
     3142.      1            snippet = "/=";
    +
     3143.      1            warn_at("unexpected_a", line, column, "/=");
    +
     3144.     15        }
    +
     3145.     15        return token_create(snippet);
    +
     3146.     15    }
    +
     3147.    668
    +
     3148.  24335    function lex_string(quote) {
    +
     3149.  24335
    +
     3150.  24335// Create a string token.
    +
     3151.  24335
    +
     3152.  24335        let the_token;
    +
     3153.  24333        if (!option_dict.single && quote === "'") {
    +
     3154.      2
    +
     3155.      2// test_cause:
    +
     3156.      2// ["''", "lex_string", "use_double", "", 1]
    +
     3157.      2
    +
     3158.      2            warn_at("use_double", line, column);
    +
     3159.      2        }
    +
     3160.  24335        snippet = "";
    +
     3161.  24335        char_after();
    +
     3162.  24335
    +
     3163.  24335// Lex/loop through each character in "...".
    +
     3164.  24335
    +
     3165. 217055        while (true) {
    +
     3166. 217055            switch (char) {
    +
     3167. 217055            case "":
    +
     3168. 217055
    +
     3169. 217055// test_cause:
    +
     3170. 217055// ["\"", "lex_string", "unclosed_string", "", 1]
    +
     3171. 217055
    +
     3172. 217055                return stop_at("unclosed_string", line, column);
    +
     3173. 217055            case "\\":
    +
     3174. 217055                char_after_escape(quote);
    +
     3175. 217055                break;
    +
     3176. 217055            case "`":
    +
     3177. 217055                if (mode_mega) {
    +
     3178. 217055
    +
     3179. 217055// test_cause:
    +
     3180. 217055// ["`${\"`\"}`", "lex_string", "unexpected_a", "`", 5]
    +
     3181. 217055
    +
     3182. 217055                    warn_at("unexpected_a", line, column, "`");
    +
     3183. 217055                }
    +
     3184. 217055                char_after("`");
    +
     3185. 217055                break;
    +
     3186. 217055            case quote:
    +
     3187. 217055
    +
     3188. 217055// Remove last character from snippet.
    +
     3189. 217055
    +
     3190. 217055                snippet = snippet.slice(0, -1);
    +
     3191. 217055                the_token = token_create("(string)", snippet);
    +
     3192. 217055                the_token.quote = quote;
    +
     3193. 217055                return the_token;
    +
     3194. 217055            default:
    +
     3195. 217055                char_after();
    +
     3196. 217055            }
    +
     3197. 217055        }
    +
     3198.  24335    }
    +
     3199.    668
    +
     3200. 252255    function lex_token() {
    +
     3201. 252255        let match;
    +
     3202. 252255
    +
     3203. 252255// Lex/loop through each whitespace.
    +
     3204. 252255
    +
     3205. 376039        while (true) {
    +
     3206. 376039
    +
     3207. 376039// Lex/loop through each blank-line.
    +
     3208. 376039
    +
     3209. 376039            while (!line_source) {
    +
     3210. 376039                line_source = read_line();
    +
     3211. 376039                from = 0;
    +
     3212. 376039                if (line_source === undefined) {
    +
     3213. 376039                    return (
    +
     3214. 376039                        mode_mega
    +
     3215. 376039
    +
     3216. 376039// test_cause:
    +
     3217. 376039// ["`${//}`", "lex_token", "unclosed_mega", "", 1]
    +
     3218. 376039
    +
     3219. 376039                        ? stop_at("unclosed_mega", line_mega, from_mega)
    +
     3220. 376039                        : line_disable !== undefined
    +
     3221. 376039
    +
     3222. 376039// test_cause:
    +
     3223. 376039// ["/*jslint-disable*/", "lex_token", "unclosed_disable", "", 1]
    +
     3224. 376039
    +
     3225. 376039                        ? stop_at("unclosed_disable", line_disable)
    +
     3226. 376039                        : token_create("(end)")
    +
     3227. 376039                    );
    +
     3228. 376039                }
    +
     3229. 376039            }
    +
     3230. 376039            from = column;
    +
     3231. 376039            match = line_source.match(jslint_rgx_token);
    +
     3232. 376039
    +
     3233. 376039// match[1] token
    +
     3234. 376039// match[2] whitespace
    +
     3235. 376039// match[3] identifier
    +
     3236. 376039// match[4] number
    +
     3237. 376039// match[5] rest
    +
     3238. 376039
    +
     3239. 376039            if (!match) {
    +
     3240. 376039
    +
     3241. 376039// test_cause:
    +
     3242. 376039// ["#", "lex_token", "unexpected_char_a", "#", 1]
    +
     3243. 376039
    +
     3244. 376039                return stop_at(
    +
     3245. 376039                    "unexpected_char_a",
    +
     3246. 376039                    line,
    +
     3247. 376039                    column,
    +
     3248. 376039                    line_source[0]
    +
     3249. 376039                );
    +
     3250. 376039            }
    +
     3251. 376039            snippet = match[1];
    +
     3252. 376039            column += snippet.length;
    +
     3253. 376039            line_source = match[5];
    +
     3254. 376039            if (!match[2]) {
    +
     3255. 376039                break;
    +
     3256. 376039            }
    +
     3257. 376039        }
    +
     3258. 251612
    +
     3259. 251612// The token is an identifier.
    +
     3260. 251612
    +
     3261. 251612        if (match[3]) {
    +
     3262.  67699            return token_create(snippet, undefined, true);
    +
     3263. 183913        }
    +
     3264. 183913
    +
     3265. 183913// Create token from number.
    +
     3266. 183913
    +
     3267. 183913        if (match[4]) {
    +
     3268.   8851            return lex_number();
    +
     3269. 175062        }
    +
     3270. 175062
    +
     3271. 175062// Create token from string "..." or '...'.
    +
     3272. 175062
    +
     3273. 175062        if (snippet === "\"" || snippet === "'") {
    +
     3274.  24335            return lex_string(snippet);
    +
     3275. 150727        }
    +
     3276. 150727
    +
     3277. 150727// Create token from megastring `...`.
    +
     3278. 150727
    +
     3279. 150727        if (snippet === "`") {
    +
     3280.    789            return lex_megastring();
    +
     3281. 149938        }
    +
     3282. 149938
    +
     3283. 149938// Create token from comment /*...*/ or //....
    +
     3284. 149938
    +
     3285. 149938        if (snippet === "/*" || snippet === "//") {
    +
     3286.  11221            return lex_comment();
    +
     3287. 138717        }
    +
     3288. 138717
    +
     3289. 138717// Create token from slash /.
    +
     3290. 138717
    +
     3291. 138717        if (snippet === "/") {
    +
     3292.    598            return lex_slash_or_regexp();
    +
     3293. 138119        }
    +
     3294. 138119        return token_create(snippet);
    +
     3295. 138119    }
    +
     3296.    668
    +
     3297.   2667    function option_set_item(key, val) {
    +
     3298.   2667
    +
     3299.   2667// These are the options that are recognized in the option object or that may
    +
     3300.   2667// appear in a /*jslint*/ directive. Most options will have a boolean value,
    +
     3301.   2667// usually true. Some options will also predefine some number of global
    +
     3302.   2667// variables.
    +
     3303.   2667
    +
     3304.   2667        switch (key) {
    +
     3305.    659        case "beta":            // Enable experimental warnings.
    +
     3306.    661        case "bitwise":         // Allow bitwise operator.
    +
     3307.    668        case "browser":         // Assume browser environment.
    +
     3308.    670        case "convert":         // Allow conversion operator.
    +
     3309.    672        case "couch":           // Assume CouchDb environment.
    +
     3310.    678        case "devel":           // Allow console.log() and friends.
    +
     3311.   2014        case "ecma":            // Assume ECMAScript environment.
    +
     3312.   2020        case "eval":            // Allow eval().
    +
     3313.   2024        case "fart":            // Allow complex fat-arrow.
    +
     3314.   2029        case "for":             // Allow for-statement.
    +
     3315.   2035        case "getset":          // Allow get() and set().
    +
     3316.   2040        case "indent2":         // Use 2-space indent.
    +
     3317.   2042        case "long":            // Allow long lines.
    +
     3318.   2089        case "node":            // Assume Node.js environment.
    +
     3319.   2093        case "nomen":           // Allow weird property name.
    +
     3320.   2095        case "single":          // Allow single-quote strings.
    +
     3321.   2097        case "subscript":       // Allow identifier in subscript-notation.
    +
     3322.   2602        case "test_cause":      // Test jslint's causes.
    +
     3323.   2604        case "test_internal_error":     // Test jslint's internal-error
    +
     3324.   2667                                        // ... handling-ability.
    +
     3325.   2606        case "this":            // Allow 'this'.
    +
     3326.   2609        case "trace":           // Include jslint stack-trace in warnings.
    +
     3327.   2613        case "unordered":       // Allow unordered cases, params, properties,
    +
     3328.   2667                                // ... variables, and exports.
    +
     3329.   2617        case "variable":        // Allow unordered const and let declarations
    +
     3330.   2667                                // ... that are not at top of function-scope.
    +
     3331.   2619        case "white":           // Allow messy whitespace.
    +
     3332.   2619            option_dict[key] = val;
    +
     3333.   2619            break;
    +
     3334.   2667
    +
     3335.   2667// PR-404 - Alias "evil" to jslint-directive "eval" for backwards-compat.
    +
     3336.   2667
    +
     3337.      2        case "evil":
    +
     3338.      2            return option_set_item("eval", val);
    +
     3339.   2667
    +
     3340.   2667// PR-404 - Alias "nomen" to jslint-directive "name" for backwards-compat.
    +
     3341.   2667
    +
     3342.      2        case "name":
    +
     3343.      2            return option_set_item("nomen", val);
    +
     3344.     44        default:
    +
     3345.     44            return false;
    +
     3346.   2619        }
    +
     3347.   2619
    +
     3348.   2619// Initialize global-variables.
    +
     3349.   2619
    +
     3350.   2619        switch (val && key) {
    +
     3351.   2667
    +
     3352.   2667// Assign global browser variables to global_dict.
    +
     3353.   2667/*
    +
     3354.   2667// /\*jslint beta, browser, devel*\/
    +
     3355.   2667console.log(JSON.stringify(Object.keys(window).sort(), undefined, 4));
    +
     3356.   2667*/
    +
     3357.   2667
    +
     3358.      6        case "browser":
    +
     3359.      6            object_assign_from_list(global_dict, [
    +
     3360.      6
    +
     3361.      6// Shared with Node.js.
    +
     3362.      6
    +
     3363.      6                "AbortController",
    +
     3364.      6                // "Buffer",
    +
     3365.      6                // "Crypto",
    +
     3366.      6                // "CryptoKey",
    +
     3367.      6                "Event",
    +
     3368.      6                "EventTarget",
    +
     3369.      6                "MessageChannel",
    +
     3370.      6                "MessageEvent",
    +
     3371.      6                "MessagePort",
    +
     3372.      6                // "Request",
    +
     3373.      6                // "Response",
    +
     3374.      6                // "SubtleCrypto",
    +
     3375.      6                "TextDecoder",
    +
     3376.      6                "TextEncoder",
    +
     3377.      6                "URL",
    +
     3378.      6                "URLSearchParams",
    +
     3379.      6                "WebAssembly",
    +
     3380.      6                // "__dirname",
    +
     3381.      6                // "__filename",
    +
     3382.      6                // "atob",
    +
     3383.      6                // "btoa",
    +
     3384.      6                // "clearImmediate",
    +
     3385.      6                "clearInterval",
    +
     3386.      6                "clearTimeout",
    +
     3387.      6                // "console",
    +
     3388.      6                // "crypto",
    +
     3389.      6                // "exports",
    +
     3390.      6                // "fetch",
    +
     3391.      6                // "global",
    +
     3392.      6                // "module",
    +
     3393.      6                "performance",
    +
     3394.      6                // "process",
    +
     3395.      6                "queueMicrotask",
    +
     3396.      6                // "require",
    +
     3397.      6                // "setImmediate",
    +
     3398.      6                "setInterval",
    +
     3399.      6                "setTimeout",
    +
     3400.      6
    +
     3401.      6// Web worker only.
    +
     3402.      6// https://github.com/mdn/content/blob/main/files/en-us/web/api
    +
     3403.      6// /workerglobalscope/index.md
    +
     3404.      6
    +
     3405.      6                "importScripts",
    +
     3406.      6
    +
     3407.      6// Window.
    +
     3408.      6
    +
     3409.      6                "Blob",
    +
     3410.      6                // "CharacterData",
    +
     3411.      6                // "DocumentType",
    +
     3412.      6                // "Element",
    +
     3413.      6                // "Event",
    +
     3414.      6                "FileReader",
    +
     3415.      6                // "FontFace",
    +
     3416.      6                "FormData",
    +
     3417.      6                "IntersectionObserver",
    +
     3418.      6                "MutationObserver",
    +
     3419.      6                // "Storage",
    +
     3420.      6                // "TextDecoder",
    +
     3421.      6                // "TextEncoder",
    +
     3422.      6                // "URL",
    +
     3423.      6                "Worker",
    +
     3424.      6                "XMLHttpRequest",
    +
     3425.      6                // "caches",
    +
     3426.      6                // "clearInterval",
    +
     3427.      6                // "clearTimeout",
    +
     3428.      6                "document",
    +
     3429.      6                // "event",
    +
     3430.      6                "fetch",
    +
     3431.      6                // "history",
    +
     3432.      6                "indexedDb",
    +
     3433.      6                "localStorage",
    +
     3434.      6                "location",
    +
     3435.      6                // "name",
    +
     3436.      6                "navigator",
    +
     3437.      6                "postMessage",
    +
     3438.      6                // "screen",
    +
     3439.      6                "sessionStorage",
    +
     3440.      6                // "setInterval",
    +
     3441.      6                // "setTimeout",
    +
     3442.      6                "structuredClone",
    +
     3443.      6                "window"
    +
     3444.      6            ], "browser");
    +
     3445.      6            break;
    +
     3446.   2667
    +
     3447.   2667// https://docs.couchdb.org/en/stable/query-server/javascript.html#javascript
    +
     3448.   2667
    +
     3449.      2        case "couch":
    +
     3450.      2            object_assign_from_list(global_dict, [
    +
     3451.      2                "emit",
    +
     3452.      2                "getRow",
    +
     3453.      2                "isArray",
    +
     3454.      2                "log",
    +
     3455.      2                "provides",
    +
     3456.      2                "registerType",
    +
     3457.      2                "require",
    +
     3458.      2                "send",
    +
     3459.      2                "start",
    +
     3460.      2                "sum",
    +
     3461.      2                "toJSON"
    +
     3462.      2            ], "CouchDb");
    +
     3463.      2            break;
    +
     3464.      6        case "devel":
    +
     3465.      6            object_assign_from_list(global_dict, [
    +
     3466.      6                "alert", "confirm", "console", "prompt"
    +
     3467.      6            ], "development");
    +
     3468.      6            break;
    +
     3469.   2667
    +
     3470.   2667// These are the globals that are provided by the language standard.
    +
     3471.   2667// Assign global ECMAScript variables to global_dict.
    +
     3472.   2667/*
    +
     3473.   2667node --input-type=module --eval '
    +
     3474.   2667// /\*jslint beta, node*\/
    +
     3475.   2667import https from "https";
    +
     3476.   2667(async function () {
    +
     3477.   2667    let dict = {import: true};
    +
     3478.   2667    let result = "";
    +
     3479.   2667    await new Promise(function (resolve) {
    +
     3480.   2667        https.get((
    +
     3481.   2667            "https://raw.githubusercontent.com/mdn/content/main/files"
    +
     3482.   2667            + "/en-us/web/javascript/reference/global_objects/index.md"
    +
     3483.   2667        ), function (res) {
    +
     3484.   2667            res.on("data", function (chunk) {
    +
     3485.   2667                result += chunk;
    +
     3486.   2667            }).on("end", resolve).setEncoding("utf8");
    +
     3487.   2667        });
    +
     3488.   2667    });
    +
     3489.   2667    result.replace((
    +
     3490.   2667        /\n- \{\{JSxRef\("(?:Global_Objects\/)?([^"\/]+?)"/g
    +
     3491.   2667    ), function (ignore, key) {
    +
     3492.   2667        if (globalThis.hasOwnProperty(key)) {
    +
     3493.   2667            dict[key] = true;
    +
     3494.   2667        }
    +
     3495.   2667        return "";
    +
     3496.   2667    });
    +
     3497.   2667    console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4));
    +
     3498.   2667}());
    +
     3499.   2667'
    +
     3500.   2667*/
    +
     3501.   2667
    +
     3502.   1336        case "ecma":
    +
     3503.   1336            object_assign_from_list(global_dict, [
    +
     3504.   1336                "AggregateError",
    +
     3505.   1336                "Array",
    +
     3506.   1336                "ArrayBuffer",
    +
     3507.   1336                "Atomics",
    +
     3508.   1336                "BigInt",
    +
     3509.   1336                "BigInt64Array",
    +
     3510.   1336                "BigUint64Array",
    +
     3511.   1336                "Boolean",
    +
     3512.   1336                "DataView",
    +
     3513.   1336                "Date",
    +
     3514.   1336                "Error",
    +
     3515.   1336                "EvalError",
    +
     3516.   1336                "Float32Array",
    +
     3517.   1336                "Float64Array",
    +
     3518.   1336                "Function",
    +
     3519.   1336                "Infinity",
    +
     3520.   1336                "Int16Array",
    +
     3521.   1336                "Int32Array",
    +
     3522.   1336                "Int8Array",
    +
     3523.   1336                "Intl",
    +
     3524.   1336                "JSON",
    +
     3525.   1336                "Map",
    +
     3526.   1336                "Math",
    +
     3527.   1336                "NaN",
    +
     3528.   1336                "Number",
    +
     3529.   1336                "Object",
    +
     3530.   1336                "Promise",
    +
     3531.   1336                "Proxy",
    +
     3532.   1336                "RangeError",
    +
     3533.   1336                "ReferenceError",
    +
     3534.   1336                "Reflect",
    +
     3535.   1336                "RegExp",
    +
     3536.   1336                "Set",
    +
     3537.   1336                "SharedArrayBuffer",
    +
     3538.   1336                "String",
    +
     3539.   1336                "Symbol",
    +
     3540.   1336                "SyntaxError",
    +
     3541.   1336                "TypeError",
    +
     3542.   1336                "URIError",
    +
     3543.   1336                "Uint16Array",
    +
     3544.   1336                "Uint32Array",
    +
     3545.   1336                "Uint8Array",
    +
     3546.   1336                "Uint8ClampedArray",
    +
     3547.   1336                "WeakMap",
    +
     3548.   1336                "WeakSet",
    +
     3549.   1336                "WebAssembly",
    +
     3550.   1336                "decodeURI",
    +
     3551.   1336                "decodeURIComponent",
    +
     3552.   1336                "encodeURI",
    +
     3553.   1336                "encodeURIComponent",
    +
     3554.   1336                "eval",
    +
     3555.   1336                "globalThis",
    +
     3556.   1336                "import",
    +
     3557.   1336                "isFinite",
    +
     3558.   1336                "isNaN",
    +
     3559.   1336                "parseFloat",
    +
     3560.   1336                "parseInt",
    +
     3561.   1336                "undefined"
    +
     3562.   1336            ], "ECMAScript");
    +
     3563.   1336            break;
    +
     3564.   2667
    +
     3565.   2667// Assign global Node.js variables to global_dict.
    +
     3566.   2667/*
    +
     3567.   2667node --input-type=module --eval '
    +
     3568.   2667// /\*jslint beta, node*\/
    +
     3569.   2667import moduleHttps from "https";
    +
     3570.   2667(async function () {
    +
     3571.   2667    let dict = Object.create(null);
    +
     3572.   2667    let result = "";
    +
     3573.   2667    await new Promise(function (resolve) {
    +
     3574.   2667        moduleHttps.get((
    +
     3575.   2667            "https://raw.githubusercontent.com/nodejs/node/v16.x/doc/api"
    +
     3576.   2667            + "/globals.md"
    +
     3577.   2667        ), function (res) {
    +
     3578.   2667            res.on("data", function (chunk) {
    +
     3579.   2667                result += chunk;
    +
     3580.   2667            }).on("end", resolve).setEncoding("utf8");
    +
     3581.   2667        });
    +
     3582.   2667    });
    +
     3583.   2667    result.replace((
    +
     3584.   2667        /\n(?:\* \[`|## |## Class: )`\w+/g
    +
     3585.   2667    ), function (match0) {
    +
     3586.   2667        dict[match0.split("`")[1]] = true;
    +
     3587.   2667        return "";
    +
     3588.   2667    });
    +
     3589.   2667    console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4));
    +
     3590.   2667}());
    +
     3591.   2667'
    +
     3592.   2667*/
    +
     3593.   2667
    +
     3594.     47        case "node":
    +
     3595.     47            object_assign_from_list(global_dict, [
    +
     3596.     47                "AbortController",
    +
     3597.     47                "Buffer",
    +
     3598.     47                // "Crypto",
    +
     3599.     47                // "CryptoKey",
    +
     3600.     47                "Event",
    +
     3601.     47                "EventTarget",
    +
     3602.     47                "MessageChannel",
    +
     3603.     47                "MessageEvent",
    +
     3604.     47                "MessagePort",
    +
     3605.     47                // "Request",
    +
     3606.     47                // "Response",
    +
     3607.     47                // "SubtleCrypto",
    +
     3608.     47                "TextDecoder",
    +
     3609.     47                "TextEncoder",
    +
     3610.     47                "URL",
    +
     3611.     47                "URLSearchParams",
    +
     3612.     47                "WebAssembly",
    +
     3613.     47                "__dirname",
    +
     3614.     47                "__filename",
    +
     3615.     47                // "atob",
    +
     3616.     47                // "btoa",
    +
     3617.     47                "clearImmediate",
    +
     3618.     47                "clearInterval",
    +
     3619.     47                "clearTimeout",
    +
     3620.     47                "console",
    +
     3621.     47                // "crypto",
    +
     3622.     47                "exports",
    +
     3623.     47                // "fetch",
    +
     3624.     47                "global",
    +
     3625.     47                "module",
    +
     3626.     47                "performance",
    +
     3627.     47                "process",
    +
     3628.     47                "queueMicrotask",
    +
     3629.     47                "require",
    +
     3630.     47                "setImmediate",
    +
     3631.     47                "setInterval",
    +
     3632.     47                "setTimeout"
    +
     3633.     47            ], "Node.js");
    +
     3634.     47            break;
    +
     3635.   2619        }
    +
     3636.   2619        return true;
    +
     3637.   2619    }
    +
     3638.    668
    +
     3639.    469    function read_digits(base, mode) {
    +
     3640.    469        let digits = line_source.match(
    +
     3641.    469            base === "b"
    +
     3642.      3            ? jslint_rgx_digits_bits
    +
     3643.    466            : base === "o"
    +
     3644.    466            ? jslint_rgx_digits_octals
    +
     3645.    466            : base === "x"
    +
     3646.    466            ? jslint_rgx_digits_hexs
    +
     3647.    466            : jslint_rgx_digits_decimals
    +
     3648.    469        )[0];
    +
     3649.    469        if (
    +
     3650.     77            (mode !== mode_digits_empty_string && digits.length === 0)
    +
     3651.    468            || digits[0] === "_"
    +
     3652.      2        ) {
    +
     3653.      2
    +
     3654.      2// test_cause:
    +
     3655.      2// ["0x", "read_digits", "expected_digits_after_a", "0x", 2]
    +
     3656.      2// ["0x_", "read_digits", "expected_digits_after_a", "0x", 2]
    +
     3657.      2
    +
     3658.      2            warn_at("expected_digits_after_a", line, column, snippet);
    +
     3659.      2        }
    +
     3660.    469
    +
     3661.    469// PR-390 - Add numeric-separator check.
    +
     3662.    469
    +
     3663.     73        if (mode === mode_digits_numeric_separator) {
    +
     3664.     73            check_numeric_separator(digits, column);
    +
     3665.    396        } else if (digits.indexOf("_") >= 0) {
    +
     3666.    396
    +
     3667.    396// test_cause:
    +
     3668.    396// ["\"\\u{1_2}\"", "read_digits", "illegal_num_separator", "", 6]
    +
     3669.    396
    +
     3670.    396            warn_at(
    +
     3671.    396                "illegal_num_separator",
    +
     3672.    396                line,
    +
     3673.    396                column + digits.indexOf("_") + 1
    +
     3674.    396            );
    +
     3675.    396        }
    +
     3676.    469        column += digits.length;
    +
     3677.    469        line_source = line_source.slice(digits.length);
    +
     3678.    469        snippet += digits;
    +
     3679.    469        char_after();
    +
     3680.    469        return digits.length;
    +
     3681.    469    }
    +
     3682.    668
    +
     3683. 105193    function read_line() {
    +
     3684. 105193
    +
     3685. 105193// Put the next line of source in line_source. If the line contains tabs,
    +
     3686. 105193// replace them with spaces and give a warning. Also warn if the line contains
    +
     3687. 105193// unsafe characters or is too damn long.
    +
     3688. 105193
    +
     3689. 105193        if (
    +
     3690. 105193            !option_dict.long
    +
     3691. 105189            && line_whole.length > 80
    +
     3692.     56            && line_disable === undefined
    +
     3693.     56            && !state.mode_json
    +
     3694.     23            && token_1
    +
     3695.     23            && !mode_regexp
    +
     3696.     13        ) {
    +
     3697.     13
    +
     3698.     13// test_cause:
    +
     3699.     13// ["/////////////////////////////////////////////////////////////////////////////////", "read_line", "too_long", "", 1] //jslint-ignore-line
    +
     3700.     13
    +
     3701.     13            warn_at("too_long", line);
    +
     3702.     13        }
    +
     3703. 105193        column = 0;
    +
     3704. 105193        line += 1;
    +
     3705. 105193        mode_regexp = false;
    +
     3706. 105193        line_source = undefined;
    +
     3707. 105193        line_whole = "";
    +
     3708.    645        if (line_list[line] === undefined) {
    +
     3709.    645            return line_source;
    +
     3710. 104548        }
    +
     3711. 104548        line_source = line_list[line].line_source;
    +
     3712. 104548        line_whole = line_source;
    +
     3713. 104548
    +
     3714. 104548// Scan each line for following ignore-directives:
    +
     3715. 104548// "/*jslint-disable*/"
    +
     3716. 104548// "/*jslint-enable*/"
    +
     3717. 104548// "//jslint-ignore-line"
    +
     3718. 104548
    +
     3719. 104548        if (line_source === "/*jslint-disable*/") {
    +
     3720.      5
    +
     3721.      5// test_cause:
    +
     3722.      5// ["/*jslint-disable*/", "read_line", "jslint_disable", "", 0]
    +
     3723.      5
    +
     3724.      5            test_cause("jslint_disable");
    +
     3725.      5            line_disable = line;
    +
     3726. 104543        } else if (line_source === "/*jslint-enable*/") {
    +
     3727. 104543            if (line_disable === undefined) {
    +
     3728. 104543
    +
     3729. 104543// test_cause:
    +
     3730. 104543// ["/*jslint-enable*/", "read_line", "unopened_enable", "", 1]
    +
     3731. 104543
    +
     3732. 104543                stop_at("unopened_enable", line);
    +
     3733. 104543            }
    +
     3734. 104543            line_disable = undefined;
    +
     3735. 104543        } else if (
    +
     3736. 104543            line_source.endsWith(" //jslint-ignore-line")
    +
     3737. 104543            || line_source.endsWith(" //jslint-quiet")
    +
     3738. 104543        ) {
    +
     3739. 104543
    +
     3740. 104543// test_cause:
    +
     3741. 104543// ["0 //jslint-ignore-line", "read_line", "jslint_ignore_line", "", 0]
    +
     3742. 104543
    +
     3743. 104543            test_cause("jslint_ignore_line");
    +
     3744. 104543            line_list[line].directive_ignore_line = true;
    +
     3745. 104547        }
    +
     3746. 104547        if (line_disable !== undefined) {
    +
     3747.      9
    +
     3748.      9// test_cause:
    +
     3749.      9// ["/*jslint-disable*/\n0", "read_line", "line_disable", "", 0]
    +
     3750.      9
    +
     3751.      9            test_cause("line_disable");
    +
     3752.      9            line_source = "";
    +
     3753. 104547        }
    +
     3754. 104547        // jslint_rgx_tab
    +
     3755. 104547        if (line_source.indexOf("\t") >= 0) {
    +
     3756.      3            if (!option_dict.white) {
    +
     3757.      3
    +
     3758.      3// test_cause:
    +
     3759.      3// ["\t", "read_line", "use_spaces", "", 1]
    +
     3760.      3
    +
     3761.      3                warn_at("use_spaces", line, line_source.indexOf("\t") + 1);
    +
     3762.      3            }
    +
     3763.      3            line_source = line_source.replace(jslint_rgx_tab, " ");
    +
     3764. 104547        }
    +
     3765. 104547        if (!option_dict.white && line_source.endsWith(" ")) {
    +
     3766.      2
    +
     3767.      2// test_cause:
    +
     3768.      2// [" ", "read_line", "unexpected_trailing_space", "", 1]
    +
     3769.      2
    +
     3770.      2            warn_at("unexpected_trailing_space", line, line_source.length - 1);
    +
     3771. 104547        }
    +
     3772. 104547        return line_source;
    +
     3773. 104547    }
    +
     3774.    668
    +
     3775. 255241    function token_create(id, value, identifier) {
    +
     3776. 255241
    +
     3777. 255241// Create the token object and append it to token_list.
    +
     3778. 255241
    +
     3779. 255241        let the_token = {
    +
     3780. 255241            from,
    +
     3781. 255241            id,
    +
     3782. 255241            identifier: Boolean(identifier),
    +
     3783. 255241            line,
    +
     3784. 255241            nr: token_list.length,
    +
     3785. 255241            thru: column,
    +
     3786. 255241            value
    +
     3787. 255241        };
    +
     3788. 255241        token_list.push(the_token);
    +
     3789. 255241
    +
     3790. 255241// Directives must appear before the first statement.
    +
     3791. 255241
    +
     3792. 244023        if (id !== "(comment)" && id !== ";") {
    +
     3793. 228006            mode_directive = false;
    +
     3794. 228006        }
    +
     3795. 255241
    +
     3796. 255241// If this token is an identifier that touches a preceding number, or
    +
     3797. 255241// a "/", comment, or regular expression literal that touches a preceding
    +
     3798. 255241// comment or regular expression literal, then give a missing space warning.
    +
     3799. 255241// This warning is not suppressed by option_dict.white.
    +
     3800. 255241
    +
     3801. 255241        if (
    +
     3802. 255241            token_prv.line === line
    +
     3803. 185838            && token_prv.thru === from
    +
     3804. 119910            && (id === "(comment)" || id === "(regexp)" || id === "/")
    +
     3805.    125            && (token_prv.id === "(comment)" || token_prv.id === "(regexp)")
    +
     3806.      1        ) {
    +
     3807.      1
    +
     3808.      1// test_cause:
    +
     3809.      1// ["/**//**/", "token_create", "expected_space_a_b", "(comment)", 5]
    +
     3810.      1
    +
     3811.      1            warn(
    +
     3812.      1                "expected_space_a_b",
    +
     3813.      1                the_token,
    +
     3814.      1                artifact(token_prv),
    +
     3815.      1                artifact(the_token)
    +
     3816.      1            );
    +
     3817.      1        }
    +
     3818.  11603        if (token_prv.id === "." && id === "(number)") {
    +
     3819.      4
    +
     3820.      4// test_cause:
    +
     3821.      4// [".0", "token_create", "expected_a_before_b", ".", 1]
    +
     3822.      4
    +
     3823.      4            warn("expected_a_before_b", token_prv, "0", ".");
    +
     3824.      4        }
    +
     3825.  11603        if (token_prv_expr.id === "." && the_token.identifier) {
    +
     3826.  11598            the_token.dot = true;
    +
     3827.  11598        }
    +
     3828. 255241
    +
     3829. 255241// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart.
    +
     3830. 255241// Farts are now detected by keeping a list of most recent "(" tokens at any
    +
     3831. 255241// given depth. When a "=>" token is encountered, the most recent "(" token at
    +
     3832. 255241// current depth is marked as a fart.
    +
     3833. 255241
    +
     3834. 255241        switch (id) {
    +
     3835.  17591        case "(":
    +
     3836.  17591            paren_backtrack_list[paren_depth] = the_token;
    +
     3837.  17591            paren_depth += 1;
    +
     3838.  17591            break;
    +
     3839.  17590        case ")":
    +
     3840.  17590            paren_depth -= 1;
    +
     3841.  17590            break;
    +
     3842.     16        case "=>":
    +
     3843.     16            if (
    +
     3844.     16                token_prv_expr.id === ")"
    +
     3845.     16                && paren_backtrack_list[paren_depth]
    +
     3846.     16            ) {
    +
     3847.     16                paren_backtrack_list[paren_depth].fart = the_token;
    +
     3848.     16            }
    +
     3849.     16            break;
    +
     3850. 255241        }
    +
     3851. 255241
    +
     3852. 255241// The previous token is used to detect adjacency problems.
    +
     3853. 255241
    +
     3854. 255241        token_prv = the_token;
    +
     3855. 255241
    +
     3856. 255241// The token_prv_expr token is a previous token that was not a comment.
    +
     3857. 255241// The token_prv_expr token
    +
     3858. 255241// is used to disambiguate "/", which can mean division or regular expression
    +
     3859. 255241// literal.
    +
     3860. 255241
    +
     3861. 244023        if (token_prv.id !== "(comment)") {
    +
     3862. 244023            token_prv_expr = token_prv;
    +
     3863. 244023        }
    +
     3864. 255241        return the_token;
    +
     3865. 255241    }
    +
     3866.    668
    +
     3867.    668// Init global_dict and option_dict.
    +
     3868.    668
    +
     3869.    668    option_set_item("ecma", true);
    +
     3870.   1896    Object.keys(option_dict).sort().forEach(function (key) {
    +
     3871.   1896        option_set_item(key, option_dict[key] === true);
    +
     3872.   1896    });
    +
     3873.    668    object_assign_from_list(global_dict, global_list, "user-defined");
    +
     3874.    668
    +
     3875.    668// Scan first line for "#!" and ignore it.
    +
     3876.    668
    +
     3877.      1    if (line_list[jslint_fudge].line_source.startsWith("#!")) {
    +
     3878.      1        line += 1;
    +
     3879.      1        state.mode_shebang = true;
    +
     3880.      1    }
    +
     3881.    668    token_1 = lex_token();
    +
     3882.    640    state.mode_json = token_1.id === "{" || token_1.id === "[";
    +
     3883.    668
    +
     3884.    668// Lex/loop through each token until (end).
    +
     3885.    668
    +
     3886. 249474    while (true) {
    +
     3887. 249474        if (lex_token().id === "(end)") {
    +
     3888. 249474            break;
    +
     3889. 249474        }
    +
     3890. 249474    }
    +
     3891.    668}
    +
     3892.      1
    +
     3893.    631function jslint_phase3_parse(state) {
    +
     3894.    631
    +
     3895.    631// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
    +
     3896.    631
    +
     3897.    631// Parsing:
    +
     3898.    631
    +
     3899.    631// Parsing weaves the tokens into an abstract syntax tree. During that process,
    +
     3900.    631// a token may be given any of these properties:
    +
     3901.    631
    +
     3902.    631//      arity       string
    +
     3903.    631//      label       identifier
    +
     3904.    631//      name        identifier
    +
     3905.    631//      expression  expressions
    +
     3906.    631//      block       statements
    +
     3907.    631//      else        statements (else, default, catch)
    +
     3908.    631
    +
     3909.    631// Specialized tokens may have additional properties.
    +
     3910.    631
    +
     3911.    631    let anon = "anonymous";     // The guessed name for anonymous functions.
    +
     3912.    631    let {
    +
     3913.    631        artifact,
    +
     3914.    631        catch_list,
    +
     3915.    631        catch_stack,
    +
     3916.    631        export_dict,
    +
     3917.    631        function_list,
    +
     3918.    631        function_stack,
    +
     3919.    631        global_dict,
    +
     3920.    631        import_list,
    +
     3921.    631        is_equal,
    +
     3922.    631        option_dict,
    +
     3923.    631        property_dict,
    +
     3924.    631        stop,
    +
     3925.    631        syntax_dict,
    +
     3926.    631        tenure,
    +
     3927.    631        test_cause,
    +
     3928.    631        token_global,
    +
     3929.    631        token_list,
    +
     3930.    631        warn,
    +
     3931.    631        warn_at
    +
     3932.    631    } = state;
    +
     3933.    631    let catchage = catch_stack[0];      // The current catch-block.
    +
     3934.    631    let functionage = token_global;     // The current function.
    +
     3935.    631    let mode_var;               // "var" if using var; "let" if using let.
    +
     3936.    631    let token_ii = 0;           // The number of the next token.
    +
     3937.    631    let token_now = token_global;       // The current token being examined in
    +
     3938.    631                                        // ... the parse.
    +
     3939.    631    let token_nxt = token_global;       // The next token to be examined in
    +
     3940.    631                                        // ... <token_list>.
    +
     3941.    631
    +
     3942. 244361    function advance(id, match) {
    +
     3943. 244361
    +
     3944. 244361// Produce the next token.
    +
     3945. 244361
    +
     3946. 244361// Attempt to give helpful names to anonymous functions.
    +
     3947. 244361
    +
     3948. 244361        if (
    +
     3949. 244361            token_now.identifier
    +
     3950.  67656            && token_now.id !== "function"
    +
     3951.  65664            && token_now.id !== "async"
    +
     3952.  65483        ) {
    +
     3953.  65483            anon = token_now.id;
    +
     3954. 178878        } else if (
    +
     3955. 178878            token_now.id === "(string)"
    +
     3956. 178878            && jslint_rgx_identifier.test(token_now.value)
    +
     3957. 178878        ) {
    +
     3958. 178878            anon = token_now.value;
    +
     3959. 178878        }
    +
     3960. 244361
    +
     3961. 244361// Attempt to match token_nxt with an expected id.
    +
     3962. 244361
    +
     3963. 120065        if (id !== undefined && token_nxt.id !== id) {
    +
     3964.     26            return (
    +
     3965.     26                match === undefined
    +
     3966.     26
    +
     3967.     26// test_cause:
    +
     3968.     26// ["{0:0}", "advance", "expected_a_b", "0", 2]
    +
     3969.     26
    +
     3970.     26                ? stop("expected_a_b", token_nxt, id, artifact())
    +
     3971.     26
    +
     3972.     26// test_cause:
    +
     3973.     26// ["{\"aa\":0", "advance", "expected_a_b_from_c_d", "{", 1]
    +
     3974.     26
    +
     3975.     26                : stop(
    +
     3976.     26                    "expected_a_b_from_c_d",
    +
     3977.     26                    token_nxt,
    +
     3978.     26                    id,
    +
     3979.     26                    artifact(match),
    +
     3980.     26                    match.line,
    +
     3981.     26                    artifact()
    +
     3982.     26                )
    +
     3983.     26            );
    +
     3984. 244335        }
    +
     3985. 244335
    +
     3986. 244335// Promote the tokens, skipping comments.
    +
     3987. 244335
    +
     3988. 244335        token_now = token_nxt;
    +
     3989. 255550        while (true) {
    +
     3990. 255550            token_nxt = token_list[token_ii];
    +
     3991. 255550            state.token_nxt = token_nxt;
    +
     3992. 255550            token_ii += 1;
    +
     3993. 255550            if (token_nxt.id !== "(comment)") {
    +
     3994. 255550                if (token_nxt.id === "(end)") {
    +
     3995. 255550                    token_ii -= 1;
    +
     3996. 255550                }
    +
     3997. 255550                break;
    +
     3998. 255550            }
    +
     3999. 255550            if (state.mode_json) {
    +
     4000. 255550
    +
     4001. 255550// test_cause:
    +
     4002. 255550// ["[//]", "advance", "unexpected_a", "(comment)", 2]
    +
     4003. 255550
    +
     4004. 255550                warn("unexpected_a");
    +
     4005. 255550            }
    +
     4006. 255550        }
    +
     4007. 244361    }
    +
     4008.    631
    +
     4009.   7572    function assignment(id) {
    +
     4010.   7572
    +
     4011.   7572// Create an assignment operator. The one true assignment is different because
    +
     4012.   7572// its left side, when it is a variable, is not treated as an expression.
    +
     4013.   7572// That case is special because that is when a variable gets initialized. The
    +
     4014.   7572// other assignment operators can modify, but they cannot initialize.
    +
     4015.   7572
    +
     4016.   7572        const the_symbol = symbol(id, 20);
    +
     4017.   5013        the_symbol.led_infix = function (left) {
    +
     4018.   5013            const the_token = token_now;
    +
     4019.   5013            let right;
    +
     4020.   5013            the_token.arity = "assignment";
    +
     4021.   5013            right = parse_expression(20 - 1);
    +
     4022.   4234            if (id === "=" && left.arity === "variable") {
    +
     4023.   2818                the_token.names = left;
    +
     4024.   2818                the_token.expression = right;
    +
     4025.   2818            } else {
    +
     4026.   2191                the_token.expression = [left, right];
    +
     4027.   5009            }
    +
     4028.   5009            if (
    +
     4029.   5009                right.arity === "assignment"
    +
     4030.   5009                || right.arity === "preassign"
    +
     4031.   5007                || right.arity === "postassign"
    +
     4032.      2            ) {
    +
     4033.      2                warn("unexpected_a", right);
    +
     4034.   5009            }
    +
     4035.   5009            check_mutation(left);
    +
     4036.   5009            return the_token;
    +
     4037.   5009        };
    +
     4038.   7572        return the_symbol;
    +
     4039.   7572    }
    +
     4040.    631
    +
     4041.   5713    function block(special) {
    +
     4042.   5713
    +
     4043.   5713// Parse a block, a sequence of statements wrapped in braces.
    +
     4044.   5713//  special "body"      The block is a function body.
    +
     4045.   5713//          "ignore"    No warning on an empty block.
    +
     4046.   5713//          "naked"     No advance.
    +
     4047.   5713//          undefined   An ordinary block.
    +
     4048.   5713
    +
     4049.   5713        let stmts;
    +
     4050.   5713        let the_block;
    +
     4051.   5708        if (special !== "naked") {
    +
     4052.   5708            advance("{");
    +
     4053.   5712        }
    +
     4054.   5712        the_block = token_now;
    +
     4055.   5712        if (special !== "body") {
    +
     4056.   3715            functionage.statement_prv = the_block;
    +
     4057.   5712        }
    +
     4058.   5712        the_block.arity = "statement";
    +
     4059.   5712        the_block.body = special === "body";
    +
     4060.   5712
    +
     4061.   5712// Top level function bodies may include the "use strict" pragma.
    +
     4062.   5712
    +
     4063.   5712        if (
    +
     4064.   5712            special === "body"
    +
     4065.   5712            && function_stack.length === 1
    +
     4066.    281            && token_nxt.value === "use strict"
    +
     4067.      4        ) {
    +
     4068.      4            token_nxt.statement = true;
    +
     4069.      4            advance("(string)");
    +
     4070.      4            advance(";");
    +
     4071.   5712        }
    +
     4072.   5712        stmts = parse_statements();
    +
     4073.   5712        the_block.block = stmts;
    +
     4074.   5712        if (stmts.length === 0) {
    +
     4075.     72            if (!option_dict.devel && special !== "ignore") {
    +
     4076.     72
    +
     4077.     72// test_cause:
    +
     4078.     72// ["function aa(){}", "block", "empty_block", "{", 14]
    +
     4079.     72
    +
     4080.     72                warn("empty_block", the_block);
    +
     4081.     72            }
    +
     4082.     72            the_block.disrupt = false;
    +
     4083.   5633        } else {
    +
     4084.   5633            the_block.disrupt = stmts[stmts.length - 1].disrupt;
    +
     4085.   5705        }
    +
     4086.   5705        advance("}");
    +
     4087.   5705        return the_block;
    +
     4088.   5705    }
    +
     4089.    631
    +
     4090.  23297    function check_left(left, right) {
    +
     4091.  23297
    +
     4092.  23297// Warn if the left is not one of these:
    +
     4093.  23297//      ?.
    +
     4094.  23297//      ?:
    +
     4095.  23297//      e()
    +
     4096.  23297//      e.b
    +
     4097.  23297//      e[b]
    +
     4098.  23297//      identifier
    +
     4099.  23297
    +
     4100.  23297        const id = left.id;
    +
     4101.  23297        if (
    +
     4102.  23297            !left.identifier
    +
     4103.   6298            && (
    +
     4104.   6298                left.arity !== "ternary"
    +
     4105.   6298                || (
    +
     4106.   6298                    !check_left(left.expression[1])
    +
     4107.   6298                    && !check_left(left.expression[2])
    +
     4108.   6298                )
    +
     4109.   6298            )
    +
     4110.   6298            && (
    +
     4111.   6298                left.arity !== "binary"
    +
     4112.   6298                || (id !== "." && id !== "?." && id !== "(" && id !== "[")
    +
     4113.   6298            )
    +
     4114.     11        ) {
    +
     4115.     11            warn("unexpected_a", right || token_nxt);
    +
     4116.     11            return false;
    +
     4117.  23286        }
    +
     4118.  23286        return true;
    +
     4119.  23286    }
    +
     4120.    631
    +
     4121.   5012    function check_mutation(the_thing) {
    +
     4122.   5012
    +
     4123.   5012// The only expressions that may be assigned to are
    +
     4124.   5012//      e.b
    +
     4125.   5012//      e[b]
    +
     4126.   5012//      v
    +
     4127.   5012//      [destructure]
    +
     4128.   5012//      {destructure}
    +
     4129.   5012
    +
     4130.   5012        if (
    +
     4131.   5012            the_thing.arity !== "variable"
    +
     4132.   1543            && the_thing.id !== "."
    +
     4133.    192            && the_thing.id !== "["
    +
     4134.      7            && the_thing.id !== "{"
    +
     4135.      7        ) {
    +
     4136.      7
    +
     4137.      7// test_cause:
    +
     4138.      7// ["0=0", "check_mutation", "bad_assignment_a", "0", 1]
    +
     4139.      7
    +
     4140.      7            warn("bad_assignment_a", the_thing);
    +
     4141.      7            return false;
    +
     4142.   5005        }
    +
     4143.   5005        return true;
    +
     4144.   5005    }
    +
     4145.    631
    +
     4146.   2208    function check_not_top_level(thing) {
    +
     4147.   2208
    +
     4148.   2208// Some features should not be at the outermost level.
    +
     4149.   2208
    +
     4150.     34        if (functionage === token_global) {
    +
     4151.     34
    +
     4152.     34// test_cause:
    +
     4153.     34// ["
    +
     4154.     34// while(0){}
    +
     4155.     34// ", "check_not_top_level", "unexpected_at_top_level_a", "while", 1]
    +
     4156.     34
    +
     4157.     34            warn("unexpected_at_top_level_a", thing);
    +
     4158.     34        }
    +
     4159.   2208    }
    +
     4160.    631
    +
     4161.   3360    function check_ordered(type, token_list) {
    +
     4162.   3360
    +
     4163.   3360// This function will warn if <token_list> is unordered.
    +
     4164.   3360
    +
     4165.   4062        token_list.reduce(function (aa, token) {
    +
     4166.   4062            const bb = artifact(token);
    +
     4167.   4048            if (!option_dict.unordered && aa > bb) {
    +
     4168.      4                warn("expected_a_b_before_c_d", token, type, bb, type, aa);
    +
     4169.      4            }
    +
     4170.   4062            return bb;
    +
     4171.   4062        }, "");
    +
     4172.   3360    }
    +
     4173.    631
    +
     4174.   1301    function check_ordered_case(case_list) {
    +
     4175.   1301
    +
     4176.   1301// This function will warn if <case_list> is unordered.
    +
     4177.   1301
    +
     4178.   2691        case_list.filter(noop).map(function (token) {
    +
     4179.   2650            switch (token.identifier || token.id) {
    +
     4180.     34            case "(number)":
    +
     4181.     34                return {
    +
     4182.     34                    order: 1,
    +
     4183.     34                    token,
    +
     4184.     34                    type: "number",
    +
     4185.     34                    value: Number(artifact(token))
    +
     4186.     34                };
    +
     4187.   2604            case "(string)":
    +
     4188.   2604                return {
    +
     4189.   2604                    order: 2,
    +
     4190.   2604                    token,
    +
     4191.   2604                    type: "string",
    +
     4192.   2604                    value: artifact(token)
    +
     4193.   2604                };
    +
     4194.     41            case true:
    +
     4195.     41                return {
    +
     4196.     41                    order: 3,
    +
     4197.     41                    token,
    +
     4198.     41                    type: "identifier",
    +
     4199.     41                    value: artifact(token)
    +
     4200.     41                };
    +
     4201.   2691            }
    +
     4202.   2691        }).reduce(function (aa, bb) {
    +
     4203.   2691            if (
    +
     4204.   2691
    +
     4205.   2691// PR-419 - Hide warning about unordered case-statements behind beta-flag.
    +
     4206.   2691
    +
     4207.   2691                option_dict.beta
    +
     4208.   2691                && !option_dict.unordered
    +
     4209.   2683                && aa && bb
    +
     4210.   1384                && (
    +
     4211.   1384                    aa.order > bb.order
    +
     4212.   1384                    || (aa.order === bb.order && aa.value > bb.value)
    +
     4213.   1384                )
    +
     4214.     10            ) {
    +
     4215.     10                warn(
    +
     4216.     10                    "expected_a_b_before_c_d",
    +
     4217.     10                    bb.token,
    +
     4218.     10                    `case-${bb.type}`,
    +
     4219.     10                    bb.value,
    +
     4220.     10                    `case-${aa.type}`,
    +
     4221.     10                    aa.value
    +
     4222.     10                );
    +
     4223.     10            }
    +
     4224.   2691            return bb;
    +
     4225.   2691        }, undefined);
    +
     4226.   1301    }
    +
     4227.    631
    +
     4228.   3263    function condition() {
    +
     4229.   3263
    +
     4230.   3263// Parse the condition part of a do, if, while.
    +
     4231.   3263
    +
     4232.   3263        const the_paren = token_nxt;
    +
     4233.   3263        let the_value;
    +
     4234.   3263
    +
     4235.   3263// test_cause:
    +
     4236.   3263// ["do{}while()", "condition", "", "", 0]
    +
     4237.   3263// ["if(){}", "condition", "", "", 0]
    +
     4238.   3263// ["while(){}", "condition", "", "", 0]
    +
     4239.   3263
    +
     4240.   3263        test_cause("");
    +
     4241.   3263        the_paren.free = true;
    +
     4242.   3263        advance("(");
    +
     4243.   3263        the_value = parse_expression(0);
    +
     4244.   3263        advance(")");
    +
     4245.      1        if (the_value.wrapped === true) {
    +
     4246.      1
    +
     4247.      1// test_cause:
    +
     4248.      1// ["while((0)){}", "condition", "unexpected_a", "(", 6]
    +
     4249.      1
    +
     4250.      1            warn("unexpected_a", the_paren);
    +
     4251.   3259        }
    +
     4252.   3259
    +
     4253.   3259// Check for anticondition.
    +
     4254.   3259
    +
     4255.   3259        switch (the_value.id) {
    +
     4256.   3259        case "%":
    +
     4257.      1            warn("unexpected_a", the_value);
    +
     4258.      1            break;
    +
     4259.      1        case "&":
    +
     4260.      1            warn("unexpected_a", the_value);
    +
     4261.      1            break;
    +
     4262.     17        case "(number)":
    +
     4263.     17            warn("unexpected_a", the_value);
    +
     4264.     17            break;
    +
     4265.      1        case "(string)":
    +
     4266.      1            warn("unexpected_a", the_value);
    +
     4267.      1            break;
    +
     4268.      1        case "*":
    +
     4269.      1            warn("unexpected_a", the_value);
    +
     4270.      1            break;
    +
     4271.      1        case "+":
    +
     4272.      1            warn("unexpected_a", the_value);
    +
     4273.      1            break;
    +
     4274.      1        case "-":
    +
     4275.      1            warn("unexpected_a", the_value);
    +
     4276.      1            break;
    +
     4277.      1        case "/":
    +
     4278.      1            warn("unexpected_a", the_value);
    +
     4279.      1            break;
    +
     4280.      1        case "<<":
    +
     4281.      1            warn("unexpected_a", the_value);
    +
     4282.      1            break;
    +
     4283.      1        case ">>":
    +
     4284.      1            warn("unexpected_a", the_value);
    +
     4285.      1            break;
    +
     4286.      1        case ">>>":
    +
     4287.      1            warn("unexpected_a", the_value);
    +
     4288.      1            break;
    +
     4289.      1        case "?":
    +
     4290.      1            warn("unexpected_a", the_value);
    +
     4291.      1            break;
    +
     4292.      1        case "^":
    +
     4293.      1            warn("unexpected_a", the_value);
    +
     4294.      1            break;
    +
     4295.      1        case "typeof":
    +
     4296.      1            warn("unexpected_a", the_value);
    +
     4297.      1            break;
    +
     4298.      1        case "|":
    +
     4299.      1            warn("unexpected_a", the_value);
    +
     4300.      1            break;
    +
     4301.      1        case "~":
    +
     4302.      1
    +
     4303.      1// test_cause:
    +
     4304.      1// ["if(0%0){}", "condition", "unexpected_a", "%", 5]
    +
     4305.      1// ["if(0&0){}", "condition", "unexpected_a", "&", 5]
    +
     4306.      1// ["if(0){}", "condition", "unexpected_a", "0", 4]
    +
     4307.      1// ["if(0*0){}", "condition", "unexpected_a", "*", 5]
    +
     4308.      1// ["if(0+0){}", "condition", "unexpected_a", "+", 5]
    +
     4309.      1// ["if(0-0){}", "condition", "unexpected_a", "-", 5]
    +
     4310.      1// ["if(0/0){}", "condition", "unexpected_a", "/", 5]
    +
     4311.      1// ["if(0<<0){}", "condition", "unexpected_a", "<<", 5]
    +
     4312.      1// ["if(0>>0){}", "condition", "unexpected_a", ">>", 5]
    +
     4313.      1// ["if(0>>>0){}", "condition", "unexpected_a", ">>>", 5]
    +
     4314.      1// ["if(0?0:0){}", "condition", "unexpected_a", "?", 5]
    +
     4315.      1// ["if(0^0){}", "condition", "unexpected_a", "^", 5]
    +
     4316.      1// ["if(0|0){}", "condition", "unexpected_a", "|", 5]
    +
     4317.      1// ["if(\"aa\"){}", "condition", "unexpected_a", "aa", 4]
    +
     4318.      1// ["if(typeof 0){}", "condition", "unexpected_a", "typeof", 4]
    +
     4319.      1// ["if(~0){}", "condition", "unexpected_a", "~", 4]
    +
     4320.      1
    +
     4321.      1            warn("unexpected_a", the_value);
    +
     4322.      1            break;
    +
     4323.   3259        }
    +
     4324.   3259        return the_value;
    +
     4325.   3259    }
    +
     4326.    631
    +
     4327.  10096    function constant(id, type, value) {
    +
     4328.  10096
    +
     4329.  10096// Create a constant symbol.
    +
     4330.  10096
    +
     4331.  10096        const the_symbol = symbol(id);
    +
     4332.  10096        the_symbol.constant = true;
    +
     4333.  10096        the_symbol.nud_prefix = (
    +
     4334.  10096            typeof value === "function"
    +
     4335.   4417            ? value
    +
     4336.  18689            : function () {
    +
     4337.  18689                token_now.constant = true;
    +
     4338.   5679                if (value !== undefined) {
    +
     4339.   5679                    token_now.value = value;
    +
     4340.   5679                }
    +
     4341.  18689                return token_now;
    +
     4342.  18689            }
    +
     4343.  10096        );
    +
     4344.  10096        the_symbol.type = type;
    +
     4345.  10096        the_symbol.value = value;
    +
     4346.  10096        return the_symbol;
    +
     4347.  10096    }
    +
     4348.    631
    +
     4349.      5    function constant_Function() {
    +
     4350.      2        if (!option_dict.eval) {
    +
     4351.      2
    +
     4352.      2// test_cause:
    +
     4353.      2// ["Function", "constant_Function", "unexpected_a", "Function", 1]
    +
     4354.      2
    +
     4355.      2            warn("unexpected_a", token_now);
    +
     4356.      3        } else if (token_nxt.id !== "(") {
    +
     4357.      3
    +
     4358.      3// test_cause:
    +
     4359.      3// ["
    +
     4360.      3// /*jslint eval*/
    +
     4361.      3// Function
    +
     4362.      3// ", "constant_Function", "expected_a_before_b", "(end)", 1]
    +
     4363.      3
    +
     4364.      3            warn("expected_a_before_b", token_nxt, "(", artifact());
    +
     4365.      3        }
    +
     4366.      5        return token_now;
    +
     4367.      5    }
    +
     4368.    631
    +
     4369.      1    function constant_arguments() {
    +
     4370.      1
    +
     4371.      1// test_cause:
    +
     4372.      1// ["arguments", "constant_arguments", "unexpected_a", "arguments", 1]
    +
     4373.      1
    +
     4374.      1        warn("unexpected_a", token_now);
    +
     4375.      1        return token_now;
    +
     4376.      1    }
    +
     4377.    631
    +
     4378.      4    function constant_eval() {
    +
     4379.      1        if (!option_dict.eval) {
    +
     4380.      1
    +
     4381.      1// test_cause:
    +
     4382.      1// ["eval", "constant_eval", "unexpected_a", "eval", 1]
    +
     4383.      1
    +
     4384.      1            warn("unexpected_a", token_now);
    +
     4385.      3        } else if (token_nxt.id !== "(") {
    +
     4386.      3
    +
     4387.      3// test_cause:
    +
     4388.      3// ["/*jslint eval*/\neval", "constant_eval", "expected_a_before_b", "(end)", 1]
    +
     4389.      3
    +
     4390.      3            warn("expected_a_before_b", token_nxt, "(", artifact());
    +
     4391.      3        }
    +
     4392.      4        return token_now;
    +
     4393.      4    }
    +
     4394.    631
    +
     4395.      1    function constant_ignore() {
    +
     4396.      1
    +
     4397.      1// test_cause:
    +
     4398.      1// ["ignore", "constant_ignore", "unexpected_a", "ignore", 1]
    +
     4399.      1
    +
     4400.      1        warn("unexpected_a", token_now);
    +
     4401.      1        return token_now;
    +
     4402.      1    }
    +
     4403.    631
    +
     4404.      1    function constant_isInfinite() {
    +
     4405.      1
    +
     4406.      1// test_cause:
    +
     4407.      1// ["isFinite", "constant_isInfinite", "expected_a_b", "isFinite", 1]
    +
     4408.      1
    +
     4409.      1        warn("expected_a_b", token_now, "Number.isFinite", "isFinite");
    +
     4410.      1        return token_now;
    +
     4411.      1    }
    +
     4412.    631
    +
     4413.      1    function constant_isNaN() {
    +
     4414.      1
    +
     4415.      1// test_cause:
    +
     4416.      1// ["isNaN(0)", "constant_isNaN", "number_isNaN", "isNaN", 1]
    +
     4417.      1
    +
     4418.      1        warn("number_isNaN", token_now);
    +
     4419.      1        return token_now;
    +
     4420.      1    }
    +
     4421.    631
    +
     4422.      3    function constant_this() {
    +
     4423.      1        if (!option_dict.this) {
    +
     4424.      1
    +
     4425.      1// test_cause:
    +
     4426.      1// ["this", "constant_this", "unexpected_a", "this", 1]
    +
     4427.      1
    +
     4428.      1            warn("unexpected_a", token_now);
    +
     4429.      1        }
    +
     4430.      3        return token_now;
    +
     4431.      3    }
    +
     4432.    631
    +
     4433.   6366    function enroll(name, role, readonly) {
    +
     4434.   6366
    +
     4435.   6366// Enroll a name into the current function context. The role can be exception,
    +
     4436.   6366// function, label, parameter, or variable. We look for variable redefinition
    +
     4437.   6366// because it causes confusion.
    +
     4438.   6366
    +
     4439.   6366        let earlier;
    +
     4440.   6366        let id = name.id;
    +
     4441.   6366
    +
     4442.   6366// Reserved words may not be enrolled.
    +
     4443.   6366
    +
     4444.     42        if (syntax_dict[id] !== undefined && id !== "ignore") {
    +
     4445.      1
    +
     4446.      1// test_cause:
    +
     4447.      1// ["let undefined", "enroll", "reserved_a", "undefined", 5]
    +
     4448.      1
    +
     4449.      1            warn("reserved_a", name);
    +
     4450.      1            return;
    +
     4451.   6365        }
    +
     4452.   6365
    +
     4453.   6365// Has the name been enrolled in this context?
    +
     4454.   6365
    +
     4455.   6365        earlier = functionage.context[id] || catchage.context[id];
    +
     4456.      7        if (earlier) {
    +
     4457.      7
    +
     4458.      7// test_cause:
    +
     4459.      7// ["let aa;let aa", "enroll", "redefinition_a_b", "1", 12]
    +
     4460.      7
    +
     4461.      7            warn("redefinition_a_b", name, id, earlier.line);
    +
     4462.      7            return;
    +
     4463.   6358        }
    +
     4464.   6358
    +
     4465.   6358// Has the name been enrolled in an outer context?
    +
     4466.   6358
    +
     4467.  10756        function_stack.forEach(function ({
    +
     4468.  10756            context
    +
     4469.  10756        }) {
    +
     4470.  10601            earlier = context[id] || earlier;
    +
     4471.  10756        });
    +
     4472.   6358        if (earlier && id === "ignore") {
    +
     4473.      4            if (earlier.role === "variable") {
    +
     4474.      4
    +
     4475.      4// test_cause:
    +
     4476.      4// ["let ignore;function aa(ignore){}", "enroll", "redefinition_a_b", "1", 24]
    +
     4477.      4
    +
     4478.      4                warn("redefinition_a_b", name, id, earlier.line);
    +
     4479.      4            }
    +
     4480.   6354        } else if (
    +
     4481.   6354            earlier
    +
     4482.   6354            && role !== "parameter" && role !== "function"
    +
     4483.   6354            && (role !== "exception" || earlier.role !== "exception")
    +
     4484.   6354        ) {
    +
     4485.   6354
    +
     4486.   6354// test_cause:
    +
     4487.   6354// ["
    +
     4488.   6354// function aa(){try{aa();}catch(aa){aa();}}
    +
     4489.   6354// ", "enroll", "redefinition_a_b", "1", 31]
    +
     4490.   6354// ["function aa(){var aa;}", "enroll", "redefinition_a_b", "1", 19]
    +
     4491.   6354
    +
     4492.   6354            warn("redefinition_a_b", name, id, earlier.line);
    +
     4493.   6354        } else if (
    +
     4494.   6354            option_dict.beta
    +
     4495.   6354            && global_dict[id]
    +
     4496.   6354            && role !== "parameter"
    +
     4497.   6354        ) {
    +
     4498.   6354
    +
     4499.   6354// test_cause:
    +
     4500.   6354// ["let Array", "enroll", "redefinition_global_a_b", "Array", 5]
    +
     4501.   6354
    +
     4502.   6354            warn("redefinition_global_a_b", name, global_dict[id], id);
    +
     4503.   6358        }
    +
     4504.   6358
    +
     4505.   6358// Enroll it.
    +
     4506.   6358
    +
     4507.   6358        Object.assign(name, {
    +
     4508.   6358            dead: true,
    +
     4509.   6358            init: false,
    +
     4510.   6358            parent: (
    +
     4511.   6358                role === "exception"
    +
     4512.   6358                ? catchage
    +
     4513.   6328                : functionage
    +
     4514.   6366            ),
    +
     4515.   6366            readonly,
    +
     4516.   6366            role,
    +
     4517.   6366            used: 0
    +
     4518.   6366        });
    +
     4519.   6366        name.parent.context[id] = name;
    +
     4520.   6366    }
    +
     4521.    631
    +
     4522.  18930    function infix(bp, id, f) {
    +
     4523.  18930
    +
     4524.  18930// Create an infix operator.
    +
     4525.  18930
    +
     4526.  18930        const the_symbol = symbol(id, bp);
    +
     4527.  31941        the_symbol.led_infix = function (left) {
    +
     4528.  31941            const the_token = token_now;
    +
     4529.  31941            the_token.arity = "binary";
    +
     4530.  23493            if (f !== undefined) {
    +
     4531.  23493                return f(left);
    +
     4532.  23493            }
    +
     4533.   8448            the_token.expression = [left, parse_expression(bp)];
    +
     4534.   8448            return the_token;
    +
     4535.   8448        };
    +
     4536.  18930        return the_symbol;
    +
     4537.  18930    }
    +
     4538.    631
    +
     4539.  11597    function infix_dot(left) {
    +
     4540.  11597        const the_token = token_now;
    +
     4541.  11597        let name = token_nxt;
    +
     4542.  11597        if (
    +
     4543.  11597            (
    +
     4544.  11597                left.id !== "(string)"
    +
     4545.     44                || (name.id !== "indexOf" && name.id !== "repeat")
    +
     4546.  11597            )
    +
     4547.  11554            && (
    +
     4548.  11554                left.id !== "["
    +
     4549.  11554                || (
    +
     4550.  11554                    name.id !== "concat"
    +
     4551.  11554                    && name.id !== "flat"
    +
     4552.  11554                    && name.id !== "flatMap"
    +
     4553.  11554                    && name.id !== "forEach"
    +
     4554.  11554                    && name.id !== "join"
    +
     4555.  11554                    && name.id !== "map"
    +
     4556.  11554                )
    +
     4557.  11554            )
    +
     4558.  11509            && (left.id !== "+" || name.id !== "slice")
    +
     4559.  11504            && (
    +
     4560.  11504                left.id !== "(regexp)"
    +
     4561.  11504                || (name.id !== "exec" && name.id !== "test")
    +
     4562.  11504            )
    +
     4563.  11437        ) {
    +
     4564.  11437
    +
     4565.  11437// test_cause:
    +
     4566.  11437// ["\"\".aa", "check_left", "unexpected_a", ".", 3]
    +
     4567.  11437
    +
     4568.  11437            check_left(left, the_token);
    +
     4569.  11437        }
    +
     4570.      1        if (!name.identifier) {
    +
     4571.      1
    +
     4572.      1// test_cause:
    +
     4573.      1// ["aa.0", "infix_dot", "expected_identifier_a", "0", 4]
    +
     4574.      1
    +
     4575.      1            stop("expected_identifier_a");
    +
     4576.  11596        }
    +
     4577.  11596        advance();
    +
     4578.  11596        survey(name);
    +
     4579.  11596
    +
     4580.  11596// The property name is not an expression.
    +
     4581.  11596
    +
     4582.  11596        the_token.name = name;
    +
     4583.  11596        the_token.expression = left;
    +
     4584.  11596        return the_token;
    +
     4585.  11596    }
    +
     4586.    631
    +
     4587.      1    function infix_fart_unwrapped() {
    +
     4588.      1
    +
     4589.      1// test_cause:
    +
     4590.      1// ["aa=>0", "infix_fart_unwrapped", "wrap_fart_parameter", "=>", 3]
    +
     4591.      1
    +
     4592.      1        return stop("wrap_fart_parameter", token_now);
    +
     4593.      1    }
    +
     4594.    631
    +
     4595.      1    function infix_grave(left) {
    +
     4596.      1        const the_tick = prefix_tick();
    +
     4597.      1
    +
     4598.      1// test_cause:
    +
     4599.      1// ["0``", "check_left", "unexpected_a", "`", 2]
    +
     4600.      1
    +
     4601.      1        check_left(left, the_tick);
    +
     4602.      1        the_tick.expression = [left].concat(the_tick.expression);
    +
     4603.      1        return the_tick;
    +
     4604.      1    }
    +
     4605.    631
    +
     4606.   1461    function infix_lbracket(left) {
    +
     4607.   1461        const the_token = token_now;
    +
     4608.   1461        let name;
    +
     4609.   1461        let the_subscript = parse_expression(0);
    +
     4610.   1438        if (the_subscript.id === "(string)" || the_subscript.id === "`") {
    +
     4611.     25            name = survey(the_subscript);
    +
     4612.     25
    +
     4613.     25// PR-404 - Add new directive "subscript" to play nice with Google Closure.
    +
     4614.     25
    +
     4615.     25            if (!option_dict.subscript && jslint_rgx_identifier.test(name)) {
    +
     4616.     25
    +
     4617.     25// test_cause:
    +
     4618.     25// ["aa[`aa`]", "infix_lbracket", "subscript_a", "aa", 4]
    +
     4619.     25
    +
     4620.     25                warn("subscript_a", the_subscript, name);
    +
     4621.     25            }
    +
     4622.     25        }
    +
     4623.   1461
    +
     4624.   1461// test_cause:
    +
     4625.   1461// ["0[0]", "check_left", "unexpected_a", "[", 2]
    +
     4626.   1461
    +
     4627.   1461        check_left(left, the_token);
    +
     4628.   1461        the_token.expression = [left, the_subscript];
    +
     4629.   1461        advance("]");
    +
     4630.   1461        return the_token;
    +
     4631.   1461    }
    +
     4632.    631
    +
     4633.  10421    function infix_lparen(left) {
    +
     4634.  10421        const the_paren = token_now;
    +
     4635.  10421        let ellipsis;
    +
     4636.  10421        let the_argument;
    +
     4637.  10384        if (left.id !== "function") {
    +
     4638.  10384
    +
     4639.  10384// test_cause:
    +
     4640.  10384// ["(0?0:0)()", "check_left", "unexpected_a", "(", 8]
    +
     4641.  10384// ["0()", "check_left", "unexpected_a", "(", 2]
    +
     4642.  10384
    +
     4643.  10384            check_left(left, the_paren);
    +
     4644.  10384        }
    +
     4645.   7699        if (functionage.arity === "statement" && left.identifier) {
    +
     4646.   5503            functionage.name.calls[left.id] = left;
    +
     4647.   5503        }
    +
     4648.  10421        the_paren.expression = [left];
    +
     4649.   8962        if (token_nxt.id !== ")") {
    +
     4650.   8962
    +
     4651.   8962// Parse/loop through each token in expression (...).
    +
     4652.   8962
    +
     4653.  14286            while (true) {
    +
     4654.  14286                if (token_nxt.id === "...") {
    +
     4655.  14286                    ellipsis = true;
    +
     4656.  14286                    advance("...");
    +
     4657.  14286                }
    +
     4658.  14286                the_argument = parse_expression(10);
    +
     4659.  14286                if (ellipsis) {
    +
     4660.  14286                    the_argument.ellipsis = true;
    +
     4661.  14286                }
    +
     4662.  14286                the_paren.expression.push(the_argument);
    +
     4663.  14286                if (token_nxt.id !== ",") {
    +
     4664.  14286                    break;
    +
     4665.  14286                }
    +
     4666.  14286                advance(",");
    +
     4667.  14286            }
    +
     4668.   8962        }
    +
     4669.  10421        advance(")", the_paren);
    +
     4670.   5260        if (the_paren.expression.length === 2) {
    +
     4671.   5260
    +
     4672.   5260// test_cause:
    +
     4673.   5260// ["aa(0)", "infix_lparen", "free", "", 0]
    +
     4674.   5260
    +
     4675.   5260            test_cause("free");
    +
     4676.   5260            the_paren.free = true;
    +
     4677.   5260            if (the_argument.wrapped === true) {
    +
     4678.   5260
    +
     4679.   5260// test_cause:
    +
     4680.   5260// ["aa((0))", "infix_lparen", "unexpected_a", "(", 3]
    +
     4681.   5260
    +
     4682.   5260                warn("unexpected_a", the_paren);
    +
     4683.   5260            }
    +
     4684.   5260            if (the_argument.id === "(") {
    +
     4685.   5260                the_argument.wrapped = true;
    +
     4686.   5260            }
    +
     4687.   5260        } else {
    +
     4688.   5161
    +
     4689.   5161// test_cause:
    +
     4690.   5161// ["aa()", "infix_lparen", "not_free", "", 0]
    +
     4691.   5161// ["aa(0,0)", "infix_lparen", "not_free", "", 0]
    +
     4692.   5161
    +
     4693.   5161            test_cause("not_free");
    +
     4694.   5161            the_paren.free = false;
    +
     4695.   5161        }
    +
     4696.  10421        return the_paren;
    +
     4697.  10421    }
    +
     4698.    631
    +
     4699.     12    function infix_option_chain(left) {
    +
     4700.     12        const the_token = token_now;
    +
     4701.     12        let name = token_nxt;
    +
     4702.     12        if (
    +
     4703.     12            (
    +
     4704.     12                left.id !== "(string)"
    +
     4705.      1                || (name.id !== "indexOf" && name.id !== "repeat")
    +
     4706.     12            )
    +
     4707.     12            && (
    +
     4708.     12                left.id !== "["
    +
     4709.      1                || (
    +
     4710.      1                    name.id !== "concat"
    +
     4711.      1                    && name.id !== "forEach"
    +
     4712.      1                    && name.id !== "join"
    +
     4713.      1                    && name.id !== "map"
    +
     4714.      1                )
    +
     4715.     12            )
    +
     4716.     12
    +
     4717.     12// test_cause:
    +
     4718.     12// ["(0+0)?.0", "infix_option_chain", "check_left", "", 0]
    +
     4719.     12
    +
     4720.      1            && (left.id !== "+" || name.id !== "slice")
    +
     4721.     12            && (
    +
     4722.     12                left.id !== "(regexp)"
    +
     4723.      1                || (name.id !== "exec" && name.id !== "test")
    +
     4724.     12            )
    +
     4725.     12        ) {
    +
     4726.     12            test_cause("check_left");
    +
     4727.     12
    +
     4728.     12// test_cause:
    +
     4729.     12// ["(/./)?.0", "check_left", "unexpected_a", "?.", 6]
    +
     4730.     12// ["\"aa\"?.0", "check_left", "unexpected_a", "?.", 5]
    +
     4731.     12// ["aa=[]?.aa", "check_left", "unexpected_a", "?.", 6]
    +
     4732.     12
    +
     4733.     12            check_left(left, the_token);
    +
     4734.     12        }
    +
     4735.     12
    +
     4736.     12// Issue #468 - Fix optional dynamic-property/function-call not recognized.
    +
     4737.     12
    +
     4738.     11        if (name.id === "[" || name.id === "(") {
    +
     4739.      2            test_cause("dyn_prop_or_call");
    +
     4740.      2
    +
     4741.      2// test_cause:
    +
     4742.      2// ["aa?.(bb)", "infix_option_chain", "dyn_prop_or_call", "", 0]
    +
     4743.      2// ["aa?.[bb]", "infix_option_chain", "dyn_prop_or_call", "", 0]
    +
     4744.      2
    +
     4745.      2            return left;
    +
     4746.     10        }
    +
     4747.     10        if (!name.identifier) {
    +
     4748.      4
    +
     4749.      4// test_cause:
    +
     4750.      4// ["aa?.0", "infix_option_chain", "expected_identifier_a", "0", 5]
    +
     4751.      4
    +
     4752.      4            stop("expected_identifier_a");
    +
     4753.      6        }
    +
     4754.      6        advance();
    +
     4755.      6        survey(name);
    +
     4756.      6
    +
     4757.      6// The property name is not an expression.
    +
     4758.      6
    +
     4759.      6        the_token.name = name;
    +
     4760.      6        the_token.expression = left;
    +
     4761.      6        return the_token;
    +
     4762.      6    }
    +
     4763.    631
    +
     4764.    631    function infixr(bp, id) {
    +
     4765.    631
    +
     4766.    631// Create a right associative infix operator.
    +
     4767.    631
    +
     4768.    631        const the_symbol = symbol(id, bp);
    +
     4769.      1        the_symbol.led_infix = function parse_infixr_led(left) {
    +
     4770.      1            const the_token = token_now;
    +
     4771.      1
    +
     4772.      1// test_cause:
    +
     4773.      1// ["0**0", "parse_infixr_led", "led_infix", "", 0]
    +
     4774.      1
    +
     4775.      1            test_cause("led_infix");
    +
     4776.      1            the_token.arity = "binary";
    +
     4777.      1            the_token.expression = [left, parse_expression(bp - 1)];
    +
     4778.      1            return the_token;
    +
     4779.      1        };
    +
     4780.    631        return the_symbol;
    +
     4781.    631    }
    +
     4782.    631
    +
     4783.  56002    function parse_expression(rbp, initial) {
    +
     4784.  56002
    +
     4785.  56002// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it
    +
     4786.  56002// is looking for ad hoc lint patterns. We add .fud_stmt to Pratt's model, which
    +
     4787.  56002// is like .nud_prefix except that it is only used on the first token of a
    +
     4788.  56002// statement. Having .fud_stmt makes it much easier to define statement-oriented
    +
     4789.  56002// languages like JavaScript. I retained Pratt's nomenclature.
    +
     4790.  56002// They are elements of the parsing method called Top Down Operator Precedence.
    +
     4791.  56002
    +
     4792.  56002// .nud_prefix  Null denotation. The prefix handler.
    +
     4793.  56002// .fud_stmt    First null denotation. The statement handler.
    +
     4794.  56002// .led_infix   Left denotation. The infix/postfix handler.
    +
     4795.  56002//  lbp         Left binding power of infix operator. It tells us how strongly
    +
     4796.  56002//              the operator binds to the argument at its left.
    +
     4797.  56002//  rbp         Right binding power.
    +
     4798.  56002
    +
     4799.  56002// It processes a nud_prefix (variable, constant, prefix operator). It will then
    +
     4800.  56002// process leds (infix operators) until the bind powers cause it to stop (it
    +
     4801.  56002// consumes tokens until it meets a token whose lbp <= rbp). Specifically, it
    +
     4802.  56002// means that it collects all tokens that bind together before returning to the
    +
     4803.  56002// operator that called it. It returns the expression's parse tree.
    +
     4804.  56002
    +
     4805.  56002// For example, "3 + 1 * 2 * 4 + 5"
    +
     4806.  56002// parses into
    +
     4807.  56002// {
    +
     4808.  56002//     "id": "+",
    +
     4809.  56002//     "expression": [
    +
     4810.  56002//         {
    +
     4811.  56002//             "id": "+",
    +
     4812.  56002//             "expression": [
    +
     4813.  56002//                 {
    +
     4814.  56002//                     "id": "(number)",
    +
     4815.  56002//                     "value": "3"
    +
     4816.  56002//                 },
    +
     4817.  56002//                 {
    +
     4818.  56002//                     "id": "*",
    +
     4819.  56002//                     "expression": [
    +
     4820.  56002//                         {
    +
     4821.  56002//                             "id": "*",
    +
     4822.  56002//                             "expression": [
    +
     4823.  56002//                                 {
    +
     4824.  56002//                                     "id": "(number)",
    +
     4825.  56002//                                     "value": "1"
    +
     4826.  56002//                                 },
    +
     4827.  56002//                                 {
    +
     4828.  56002//                                     "id": "(number)",
    +
     4829.  56002//                                     "value": "2"
    +
     4830.  56002//                                 }
    +
     4831.  56002//                             ]
    +
     4832.  56002//                         },
    +
     4833.  56002//                         {
    +
     4834.  56002//                             "id": "(number)",
    +
     4835.  56002//                             "value": "4"
    +
     4836.  56002//                         }
    +
     4837.  56002//                     ]
    +
     4838.  56002//                 }
    +
     4839.  56002//             ]
    +
     4840.  56002//         },
    +
     4841.  56002//         {
    +
     4842.  56002//             "id": "(number)",
    +
     4843.  56002//             "value": "5"
    +
     4844.  56002//         }
    +
     4845.  56002//     ]
    +
     4846.  56002// }
    +
     4847.  56002
    +
     4848.  56002        let left;
    +
     4849.  56002        let the_symbol;
    +
     4850.  56002
    +
     4851.  56002// Statements will have already advanced, so advance now only if the token is
    +
     4852.  56002// not the first of a statement.
    +
     4853.  56002
    +
     4854.  44642        if (!initial) {
    +
     4855.  44642            advance();
    +
     4856.  44642        }
    +
     4857.  56002        the_symbol = syntax_dict[token_now.id];
    +
     4858.  24510        if (the_symbol !== undefined && the_symbol.nud_prefix !== undefined) {
    +
     4859.  24451
    +
     4860.  24451// test_cause:
    +
     4861.  24451// ["0", "parse_expression", "symbol", "", 0]
    +
     4862.  24451
    +
     4863.  24451            test_cause("symbol");
    +
     4864.  24451            left = the_symbol.nud_prefix();
    +
     4865.  31551        } else if (token_now.identifier) {
    +
     4866.  31551
    +
     4867.  31551// test_cause:
    +
     4868.  31551// ["aa", "parse_expression", "identifier", "", 0]
    +
     4869.  31551
    +
     4870.  31551            test_cause("identifier");
    +
     4871.  31551            left = token_now;
    +
     4872.  31551            left.arity = "variable";
    +
     4873.  31551        } else {
    +
     4874.  31551
    +
     4875.  31551// test_cause:
    +
     4876.  31551// ["!", "parse_expression", "unexpected_a", "(end)", 1]
    +
     4877.  31551// ["/./", "parse_expression", "unexpected_a", "/", 1]
    +
     4878.  31551// ["let aa=`${}`;", "parse_expression", "unexpected_a", "}", 11]
    +
     4879.  31551
    +
     4880.  31551            return stop("unexpected_a", token_now);
    +
     4881.  55966        }
    +
     4882.  55966
    +
     4883.  55966// Parse/loop through each symbol in expression.
    +
     4884.  55966
    +
     4885.  93124        while (true) {
    +
     4886.  93124            the_symbol = syntax_dict[token_nxt.id];
    +
     4887.  93124            if (
    +
     4888.  93124                the_symbol === undefined
    +
     4889.  93124                || the_symbol.led_infix === undefined
    +
     4890.  93124                || the_symbol.lbp <= rbp
    +
     4891.  93124            ) {
    +
     4892.  93124                break;
    +
     4893.  93124            }
    +
     4894.  93124            advance();
    +
     4895.  93124            left = the_symbol.led_infix(left);
    +
     4896.  93124        }
    +
     4897.  55955        return left;
    +
     4898.  55955    }
    +
     4899.    631
    +
     4900.     14    function parse_fart(the_fart) {
    +
     4901.     14
    +
     4902.     14// Give the function properties storing its names and for observing the depth
    +
     4903.     14// of loops and switches.
    +
     4904.     14
    +
     4905.     14        Object.assign(the_fart, {
    +
     4906.     14            arity: "binary",
    +
     4907.     14            context: empty(),
    +
     4908.     14            finally: 0,
    +
     4909.     14            level: functionage.level + 1,
    +
     4910.     14            loop: 0,
    +
     4911.     14            name: anon,
    +
     4912.     14            switch: 0,
    +
     4913.     14            try: 0
    +
     4914.     14        });
    +
     4915.     14
    +
     4916.     14// PR-384 - Relax warning "function_in_loop".
    +
     4917.     14//
    +
     4918.     14//         if (functionage.loop > 0) {
    +
     4919.     14
    +
     4920.     14// // test_cause:
    +
     4921.     14// // ["while(0){aa.map(()=>0);}", "parse_fart", "function_in_loop", "=>", 19]
    +
     4922.     14//
    +
     4923.     14//             warn("function_in_loop", the_fart);
    +
     4924.     14//         }
    +
     4925.     14
    +
     4926.     14// Push the current function context and establish a new one.
    +
     4927.     14
    +
     4928.     14        function_list.push(the_fart);
    +
     4929.     14        function_stack.push(functionage);
    +
     4930.     14        functionage = the_fart;
    +
     4931.     14
    +
     4932.     14// Parse the parameter list.
    +
     4933.     14
    +
     4934.     14        prefix_function_parameter(the_fart);
    +
     4935.     14        advance("=>");
    +
     4936.     14
    +
     4937.     14// The function's body is a block.
    +
     4938.     14
    +
     4939.      3        if (token_nxt.id === "{") {
    +
     4940.      3            if (!option_dict.fart) {
    +
     4941.      3
    +
     4942.      3// test_cause:
    +
     4943.      3// ["()=>{}", "parse_fart", "use_function_not_fart", "=>", 3]
    +
     4944.      3
    +
     4945.      3                warn("use_function_not_fart", the_fart);
    +
     4946.      3            }
    +
     4947.      3            the_fart.block = block("body");
    +
     4948.     11        } else if (
    +
     4949.     11            syntax_dict[token_nxt.id] !== undefined
    +
     4950.     11            && syntax_dict[token_nxt.id].fud_stmt !== undefined
    +
     4951.     11        ) {
    +
     4952.     11
    +
     4953.     11// PR-384 - Bugfix - Fixes issue #379 - warn against naked-statement in fart.
    +
     4954.     11
    +
     4955.     11// test_cause:
    +
     4956.     11// ["()=>delete aa", "parse_fart", "unexpected_a_after_b", "=>", 5]
    +
     4957.     11
    +
     4958.     11            stop("unexpected_a_after_b", token_nxt, token_nxt.id, "=>");
    +
     4959.     11
    +
     4960.     11// The function's body is an expression.
    +
     4961.     11
    +
     4962.     11        } else {
    +
     4963.     11            the_fart.expression = parse_expression(0);
    +
     4964.     13        }
    +
     4965.     13
    +
     4966.     13// Restore the previous context.
    +
     4967.     13
    +
     4968.     13        functionage = function_stack.pop();
    +
     4969.     13        return the_fart;
    +
     4970.     13    }
    +
     4971.    631
    +
     4972.  12961    function parse_json() {
    +
     4973.  12961        let container;
    +
     4974.  12961        let is_dup;
    +
     4975.  12961        let name;
    +
     4976.  12961        let negative;
    +
     4977.  12961        switch (token_nxt.id) {
    +
     4978.   5262        case "(number)":
    +
     4979.   5262            if (!jslint_rgx_json_number.test(token_nxt.value)) {
    +
     4980.   5262
    +
     4981.   5262// test_cause:
    +
     4982.   5262// ["[-.0]", "parse_json", "unexpected_a", ".", 3]
    +
     4983.   5262// ["[-0x0]", "parse_json", "unexpected_a", "0x0", 3]
    +
     4984.   5262// ["[.0]", "parse_json", "unexpected_a", ".", 2]
    +
     4985.   5262// ["[0x0]", "parse_json", "unexpected_a", "0x0", 2]
    +
     4986.   5262
    +
     4987.   5262                warn("unexpected_a");
    +
     4988.   5262            }
    +
     4989.   5262            advance("(number)");
    +
     4990.   5262            return token_now;
    +
     4991.   1767        case "(string)":
    +
     4992.   1767            if (token_nxt.quote !== "\"") {
    +
     4993.   1767
    +
     4994.   1767// test_cause:
    +
     4995.   1767// ["['']", "parse_json", "unexpected_a", "'", 2]
    +
     4996.   1767
    +
     4997.   1767                warn("unexpected_a", token_nxt, token_nxt.quote);
    +
     4998.   1767            }
    +
     4999.   1767            advance("(string)");
    +
     5000.   1767            return token_now;
    +
     5001.      3        case "-":
    +
     5002.      3            negative = token_nxt;
    +
     5003.      3            negative.arity = "unary";
    +
     5004.      3            advance("-");
    +
     5005.      3
    +
     5006.      3// Recurse parse_json().
    +
     5007.      3
    +
     5008.      3            negative.expression = parse_json();
    +
     5009.      3            return negative;
    +
     5010.   1649        case "[":
    +
     5011.   1649
    +
     5012.   1649// test_cause:
    +
     5013.   1649// ["[]", "parse_json", "bracket", "", 0]
    +
     5014.   1649
    +
     5015.   1649            test_cause("bracket");
    +
     5016.   1649            container = token_nxt;
    +
     5017.   1649            container.expression = [];
    +
     5018.   1649            advance("[");
    +
     5019.   1649            if (token_nxt.id !== "]") {
    +
     5020.   3300                while (true) {
    +
     5021.   3300
    +
     5022.   3300// Recurse parse_json().
    +
     5023.   3300
    +
     5024.   3300                    container.expression.push(parse_json());
    +
     5025.   3300                    if (token_nxt.id !== ",") {
    +
     5026.   3300
    +
     5027.   3300// test_cause:
    +
     5028.   3300// ["[0,0]", "parse_json", "comma", "", 0]
    +
     5029.   3300
    +
     5030.   3300                        test_cause("comma");
    +
     5031.   3300                        break;
    +
     5032.   3300                    }
    +
     5033.   3300                    advance(",");
    +
     5034.   3300                }
    +
     5035.   1649            }
    +
     5036.   1649            advance("]", container);
    +
     5037.   1649            return container;
    +
     5038.    509        case "false":
    +
     5039.    511        case "null":
    +
     5040.    896        case "true":
    +
     5041.    896
    +
     5042.    896// test_cause:
    +
     5043.    896// ["[false]", "parse_json", "constant", "", 0]
    +
     5044.    896// ["[null]", "parse_json", "constant", "", 0]
    +
     5045.    896// ["[true]", "parse_json", "constant", "", 0]
    +
     5046.    896
    +
     5047.    896            test_cause("constant");
    +
     5048.    896            advance();
    +
     5049.    896            return token_now;
    +
     5050.   3379        case "{":
    +
     5051.   3379
    +
     5052.   3379// test_cause:
    +
     5053.   3379// ["{}", "parse_json", "brace", "", 0]
    +
     5054.   3379
    +
     5055.   3379            test_cause("brace");
    +
     5056.   3379            container = token_nxt;
    +
     5057.   3379
    +
     5058.   3379// Explicit empty-object required to detect "__proto__".
    +
     5059.   3379
    +
     5060.   3379            is_dup = empty();
    +
     5061.   3379            container.expression = [];
    +
     5062.   3379            advance("{");
    +
     5063.   3379            if (token_nxt.id !== "}") {
    +
     5064.   3379
    +
     5065.   3379// JSON
    +
     5066.   3379// Parse/loop through each property in {...}.
    +
     5067.   3379
    +
     5068.   9636                while (true) {
    +
     5069.   9636                    if (token_nxt.quote !== "\"") {
    +
     5070.   9636
    +
     5071.   9636// test_cause:
    +
     5072.   9636// ["{0:0}", "parse_json", "unexpected_a", "0", 2]
    +
     5073.   9636
    +
     5074.   9636                        warn(
    +
     5075.   9636                            "unexpected_a",
    +
     5076.   9636                            token_nxt,
    +
     5077.   9636                            token_nxt.quote
    +
     5078.   9636                        );
    +
     5079.   9636                    }
    +
     5080.   9636                    name = token_nxt;
    +
     5081.   9636                    advance("(string)");
    +
     5082.   9636                    if (is_dup[token_now.value] !== undefined) {
    +
     5083.   9636
    +
     5084.   9636// test_cause:
    +
     5085.   9636// ["{\"aa\":0,\"aa\":0}", "parse_json", "duplicate_a", "aa", 9]
    +
     5086.   9636
    +
     5087.   9636                        warn("duplicate_a", token_now);
    +
     5088.   9636                    } else if (token_now.value === "__proto__") {
    +
     5089.   9636
    +
     5090.   9636// test_cause:
    +
     5091.   9636// ["{\"__proto__\":0}", "parse_json", "weird_property_a", "__proto__", 2]
    +
     5092.   9636
    +
     5093.   9636                        warn("weird_property_a", token_now);
    +
     5094.   9636                    } else {
    +
     5095.   9636                        is_dup[token_now.value] = token_now;
    +
     5096.   9636                    }
    +
     5097.   9636                    advance(":");
    +
     5098.   9636                    container.expression.push(
    +
     5099.   9636
    +
     5100.   9636// Recurse parse_json().
    +
     5101.   9636
    +
     5102.   9636                        Object.assign(parse_json(), {
    +
     5103.   9636                            label: name
    +
     5104.   9636                        })
    +
     5105.   9636                    );
    +
     5106.   9636                    if (token_nxt.id !== ",") {
    +
     5107.   9636                        break;
    +
     5108.   9636                    }
    +
     5109.   9636                    advance(",");
    +
     5110.   9636                }
    +
     5111.   3379            }
    +
     5112.   3379            advance("}", container);
    +
     5113.   3379            return container;
    +
     5114.      5        default:
    +
     5115.      5
    +
     5116.      5// test_cause:
    +
     5117.      5// ["[undefined]", "parse_json", "unexpected_a", "undefined", 2]
    +
     5118.      5
    +
     5119.      5            stop("unexpected_a");
    +
     5120.  12961        }
    +
     5121.  12961    }
    +
     5122.    631
    +
     5123.  20929    function parse_statement() {
    +
     5124.  20929
    +
     5125.  20929// Parse a statement. Any statement may have a label, but only four statements
    +
     5126.  20929// have use for one. A statement can be one of the standard statements, or
    +
     5127.  20929// an assignment expression, or an invocation expression.
    +
     5128.  20929
    +
     5129.  20929        let first;
    +
     5130.  20929        let the_label;
    +
     5131.  20929        let the_statement;
    +
     5132.  20929        let the_symbol;
    +
     5133.  20929        advance();
    +
     5134.  20742        if (token_now.identifier && token_nxt.id === ":") {
    +
     5135.     13            the_label = token_now;
    +
     5136.     13            if (the_label.id === "ignore") {
    +
     5137.     13
    +
     5138.     13// test_cause:
    +
     5139.     13// ["ignore:", "parse_statement", "unexpected_a", "ignore", 1]
    +
     5140.     13
    +
     5141.     13                warn("unexpected_a", the_label);
    +
     5142.     13            }
    +
     5143.     13            advance(":");
    +
     5144.     13            switch (token_nxt.id) {
    +
     5145.     13            case "do":
    +
     5146.     13            case "for":
    +
     5147.     13            case "switch":
    +
     5148.     13            case "while":
    +
     5149.     13
    +
     5150.     13// test_cause:
    +
     5151.     13// ["aa:do{}", "parse_statement", "the_statement_label", "do", 0]
    +
     5152.     13// ["aa:for{}", "parse_statement", "the_statement_label", "for", 0]
    +
     5153.     13// ["aa:switch{}", "parse_statement", "the_statement_label", "switch", 0]
    +
     5154.     13// ["aa:while{}", "parse_statement", "the_statement_label", "while", 0]
    +
     5155.     13
    +
     5156.     13                test_cause("the_statement_label", token_nxt.id);
    +
     5157.     13                enroll(the_label, "label", true);
    +
     5158.     13                the_label.dead = false;
    +
     5159.     13                the_label.init = true;
    +
     5160.     13                the_statement = parse_statement();
    +
     5161.     13                functionage.statement_prv = the_statement;
    +
     5162.     13                the_statement.label = the_label;
    +
     5163.     13                the_statement.statement = true;
    +
     5164.     13                return the_statement;
    +
     5165.     13            }
    +
     5166.     13            advance();
    +
     5167.     13
    +
     5168.     13// test_cause:
    +
     5169.     13// ["aa:", "parse_statement", "unexpected_label_a", "aa", 1]
    +
     5170.     13
    +
     5171.     13            warn("unexpected_label_a", the_label);
    +
     5172.  20920        }
    +
     5173.  20920
    +
     5174.  20920// Parse the statement.
    +
     5175.  20920
    +
     5176.  20920        first = token_now;
    +
     5177.  20920        first.statement = true;
    +
     5178.  20920        the_symbol = syntax_dict[first.id];
    +
     5179.  20920        if (
    +
     5180.  20920            the_symbol !== undefined
    +
     5181.  20920            && the_symbol.fud_stmt !== undefined
    +
     5182.  20929
    +
     5183.  20929// PR-318 - Bugfix - Fixes issues #316, #317 - dynamic-import().
    +
     5184.  20929
    +
     5185.  10154            && !(the_symbol.id === "import" && token_nxt.id === "(")
    +
     5186.  10152        ) {
    +
     5187.  10152            the_symbol.disrupt = false;
    +
     5188.  10152            the_symbol.statement = true;
    +
     5189.  10152            token_now.arity = "statement";
    +
     5190.  10152            the_statement = the_symbol.fud_stmt();
    +
     5191.  10152            functionage.statement_prv = the_statement;
    +
     5192.  10768        } else {
    +
     5193.  10768
    +
     5194.  10768// It is an expression statement.
    +
     5195.  10768
    +
     5196.  10768            the_statement = parse_expression(0, true);
    +
     5197.  10768            functionage.statement_prv = the_statement;
    +
     5198.  10768            if (the_statement.wrapped && the_statement.id !== "(") {
    +
     5199.  10768
    +
     5200.  10768// test_cause:
    +
     5201.  10768// ["(0)", "parse_statement", "unexpected_a", "(", 1]
    +
     5202.  10768
    +
     5203.  10768                warn("unexpected_a", first);
    +
     5204.  10768            }
    +
     5205.  10768            semicolon();
    +
     5206.  20826        }
    +
     5207.  20826        if (the_label !== undefined) {
    +
     5208.      1            the_label.dead = true;
    +
     5209.  20826        }
    +
     5210.  20826        return the_statement;
    +
     5211.  20826    }
    +
     5212.    631
    +
     5213.   7501    function parse_statements() {
    +
     5214.   7501
    +
     5215.   7501// Parse a list of statements. Give a warning if an unreachable statement
    +
     5216.   7501// follows a disruptive statement.
    +
     5217.   7501
    +
     5218.   7501        const statement_list = [];
    +
     5219.   7501        let a_statement;
    +
     5220.   7501        let disrupt = false;
    +
     5221.   7501
    +
     5222.   7501// Parse/loop each statement until a statement-terminator is reached.
    +
     5223.   7501
    +
     5224.  28003        while (true) {
    +
     5225.  28003            switch (token_nxt.id) {
    +
     5226.  28003            case "(end)":
    +
     5227.  28003            case "case":
    +
     5228.  28003            case "default":
    +
     5229.  28003            case "else":
    +
     5230.  28003            case "}":
    +
     5231.  28003
    +
     5232.  28003// test_cause:
    +
     5233.  28003// [";", "parse_statements", "closer", "", 0]
    +
     5234.  28003// ["case", "parse_statements", "closer", "", 0]
    +
     5235.  28003// ["default", "parse_statements", "closer", "", 0]
    +
     5236.  28003// ["else", "parse_statements", "closer", "", 0]
    +
     5237.  28003// ["}", "parse_statements", "closer", "", 0]
    +
     5238.  28003
    +
     5239.  28003                test_cause("closer");
    +
     5240.  28003                return statement_list;
    +
     5241.  28003            }
    +
     5242.  28003            a_statement = parse_statement();
    +
     5243.  28003            statement_list.push(a_statement);
    +
     5244.  28003            if (disrupt) {
    +
     5245.  28003
    +
     5246.  28003// test_cause:
    +
     5247.  28003// ["while(0){break;0;}", "parse_statements", "unreachable_a", "0", 16]
    +
     5248.  28003
    +
     5249.  28003                warn("unreachable_a", a_statement);
    +
     5250.  28003            }
    +
     5251.  28003            disrupt = a_statement.disrupt;
    +
     5252.  28003        }
    +
     5253.   7501    }
    +
     5254.    631
    +
     5255.   1262    function postassign(id) {
    +
     5256.   1262
    +
     5257.   1262// Create one of the postassign operators.
    +
     5258.   1262
    +
     5259.   1262        const the_symbol = symbol(id, 150);
    +
     5260.      1        the_symbol.led_infix = function (left) {
    +
     5261.      1            token_now.expression = left;
    +
     5262.      1            token_now.arity = "postassign";
    +
     5263.      1            check_mutation(token_now.expression);
    +
     5264.      1            return token_now;
    +
     5265.      1        };
    +
     5266.   1262        return the_symbol;
    +
     5267.   1262    }
    +
     5268.    631
    +
     5269.   1262    function preassign(id) {
    +
     5270.   1262
    +
     5271.   1262// Create one of the preassign operators.
    +
     5272.   1262
    +
     5273.   1262        const the_symbol = symbol(id);
    +
     5274.      2        the_symbol.nud_prefix = function () {
    +
     5275.      2            const the_token = token_now;
    +
     5276.      2            the_token.arity = "preassign";
    +
     5277.      2            the_token.expression = parse_expression(150);
    +
     5278.      2            check_mutation(the_token.expression);
    +
     5279.      2            return the_token;
    +
     5280.      2        };
    +
     5281.   1262        return the_symbol;
    +
     5282.   1262    }
    +
     5283.    631
    +
     5284.  10727    function prefix(id, f) {
    +
     5285.  10727
    +
     5286.  10727// Create a prefix operator.
    +
     5287.  10727
    +
     5288.  10727        const the_symbol = symbol(id);
    +
     5289.   5744        the_symbol.nud_prefix = function () {
    +
     5290.   5744            const the_token = token_now;
    +
     5291.   5744            the_token.arity = "unary";
    +
     5292.   4932            if (typeof f === "function") {
    +
     5293.   4932                return f();
    +
     5294.   4932            }
    +
     5295.    812            the_token.expression = parse_expression(150);
    +
     5296.    812            return the_token;
    +
     5297.    812        };
    +
     5298.  10727        return the_symbol;
    +
     5299.  10727    }
    +
     5300.    631
    +
     5301.      1    function prefix_assign_divide() {
    +
     5302.      1
    +
     5303.      1// test_cause:
    +
     5304.      1// ["/=", "prefix_assign_divide", "expected_a_b", "/=", 1]
    +
     5305.      1
    +
     5306.      1        stop("expected_a_b", token_now, "/\\=", "/=");
    +
     5307.      1    }
    +
     5308.    631
    +
     5309.    136    function prefix_async() {
    +
     5310.    136        let the_async = token_now;
    +
     5311.    136        let the_function;
    +
     5312.    136        token_nxt.arity = the_async.arity;
    +
     5313.    136
    +
     5314.    136// PR-414 - Parse async fart.
    +
     5315.    136
    +
     5316.      3        if (token_nxt.fart) {
    +
     5317.      3            advance("(");
    +
     5318.      3            the_function = Object.assign(token_now.fart, {
    +
     5319.      3                async: 1
    +
     5320.      3            });
    +
     5321.      3            if (!option_dict.fart) {
    +
     5322.      3
    +
     5323.      3// test_cause:
    +
     5324.      3// ["async()=>0", "prefix_async", "use_function_not_fart", "=>", 8]
    +
     5325.      3
    +
     5326.      3                warn("use_function_not_fart", the_function);
    +
     5327.      3            }
    +
     5328.      3            prefix_lparen();
    +
     5329.      3
    +
     5330.      3// Parse async function.
    +
     5331.      3
    +
     5332.    133        } else {
    +
     5333.    133            advance("function");
    +
     5334.    133            the_function = Object.assign(token_now, {
    +
     5335.    133                async: 1
    +
     5336.    133            });
    +
     5337.    133            prefix_function();
    +
     5338.    133        }
    +
     5339.      3        if (the_function.async === 1) {
    +
     5340.      3
    +
     5341.      3// test_cause:
    +
     5342.      3// ["
    +
     5343.      3// async function aa(){}
    +
     5344.      3// ", "prefix_async", "missing_await_statement", "function", 7]
    +
     5345.      3
    +
     5346.      3            warn("missing_await_statement", the_function);
    +
     5347.      3        }
    +
     5348.    136        return the_function;
    +
     5349.    136    }
    +
     5350.    631
    +
     5351.    297    function prefix_await() {
    +
     5352.    297        const the_await = token_now;
    +
     5353.    297
    +
     5354.    297// PR-370 - Add top-level-await support.
    +
     5355.    297
    +
     5356.      4        if (functionage.async === 0 && functionage !== token_global) {
    +
     5357.      2
    +
     5358.      2// test_cause:
    +
     5359.      2// ["function aa(){aa=await 0;}", "prefix_await", "unexpected_a", "await", 18]
    +
     5360.      2// ["function aa(){await 0;}", "prefix_await", "unexpected_a", "await", 15]
    +
     5361.      2
    +
     5362.      2            warn("unexpected_a", the_await);
    +
     5363.    295        } else {
    +
     5364.    295            functionage.async += 1;
    +
     5365.    295        }
    +
     5366.    185        if (the_await.arity === "statement") {
    +
     5367.    185
    +
     5368.    185// PR-405 - Bugfix - fix expression after "await" mis-identified as statement.
    +
     5369.    185
    +
     5370.    185            the_await.expression = parse_expression(150);
    +
     5371.    185            semicolon();
    +
     5372.    185        } else {
    +
     5373.    112            the_await.expression = parse_expression(150);
    +
     5374.    112        }
    +
     5375.    297        return the_await;
    +
     5376.    297    }
    +
     5377.    631
    +
     5378.      1    function prefix_fart() {
    +
     5379.      1
    +
     5380.      1// test_cause:
    +
     5381.      1// ["=>0", "prefix_fart", "expected_a_before_b", "=>", 1]
    +
     5382.      1
    +
     5383.      1        return stop("expected_a_before_b", token_now, "()", "=>");
    +
     5384.      1    }
    +
     5385.    631
    +
     5386.   2005    function prefix_function(the_function) {
    +
     5387.     11        let name = the_function && the_function.name;
    +
     5388.   1994        if (the_function === undefined) {
    +
     5389.   1994            the_function = token_now;
    +
     5390.   1994
    +
     5391.   1994// A function statement must have a name that will be in the parent's scope.
    +
     5392.   1994
    +
     5393.   1994            if (the_function.arity === "statement") {
    +
     5394.   1994                if (!token_nxt.identifier) {
    +
     5395.   1994
    +
     5396.   1994// test_cause:
    +
     5397.   1994// ["function(){}", "prefix_function", "expected_identifier_a", "(", 9]
    +
     5398.   1994// ["function*aa(){}", "prefix_function", "expected_identifier_a", "*", 9]
    +
     5399.   1994
    +
     5400.   1994                    return stop("expected_identifier_a");
    +
     5401.   1994                }
    +
     5402.   1994                name = token_nxt;
    +
     5403.   1994                enroll(name, "variable", true);
    +
     5404.   1994                the_function.name = Object.assign(name, {
    +
     5405.   1994                    calls: empty(),
    +
     5406.   1994
    +
     5407.   1994// PR-331 - Bugfix - Fixes issue #272 - function hoisting not allowed.
    +
     5408.   1994
    +
     5409.   1994                    dead: false,
    +
     5410.   1994                    init: true
    +
     5411.   1994                });
    +
     5412.   1994                advance();
    +
     5413.   1994            } else if (name === undefined) {
    +
     5414.   1994
    +
     5415.   1994// A function expression may have an optional name.
    +
     5416.   1994
    +
     5417.   1994                the_function.name = anon;
    +
     5418.   1994                if (token_nxt.identifier) {
    +
     5419.   1994                    name = token_nxt;
    +
     5420.   1994                    the_function.name = name;
    +
     5421.   1994                    advance();
    +
     5422.   1994                }
    +
     5423.   1994            }
    +
     5424.   2003        }
    +
     5425.   2003
    +
     5426.   2003//  Probably deadcode.
    +
     5427.   2003//  if (mode_mega) {
    +
     5428.   2003//      warn("unexpected_a", the_function);
    +
     5429.   2003//  }
    +
     5430.   2003//  jslint_assert(!mode_mega, `Expected !mode_mega.`);
    +
     5431.   2003
    +
     5432.   2003// PR-378 - Relax warning "function_in_loop".
    +
     5433.   2003//
    +
     5434.   2003// // Don't create functions in loops. It is inefficient, and it can lead to
    +
     5435.   2003// // scoping errors.
    +
     5436.   2003//
    +
     5437.   2003//         if (functionage.loop > 0) {
    +
     5438.   2003//
    +
     5439.   2003// // test_cause:
    +
     5440.   2003// // ["
    +
     5441.   2003// // while(0){aa.map(function(){});}
    +
     5442.   2003// // ", "prefix_function", "function_in_loop", "function", 17]
    +
     5443.   2003//
    +
     5444.   2003//             warn("function_in_loop", the_function);
    +
     5445.   2003//         }
    +
     5446.   2003
    +
     5447.   2003// Give the function properties for storing its names and for observing the
    +
     5448.   2003// depth of loops and switches.
    +
     5449.   2003
    +
     5450.   2003        Object.assign(the_function, {
    +
     5451.   2003            async: the_function.async || 0,
    +
     5452.   2005            context: empty(),
    +
     5453.   2005            finally: 0,
    +
     5454.   2005            level: functionage.level + 1,
    +
     5455.   2005            loop: 0,
    +
     5456.   2005            statement_prv: undefined,
    +
     5457.   2005            switch: 0,
    +
     5458.   2005            try: 0
    +
     5459.   2005        });
    +
     5460.    929        if (the_function.arity !== "statement" && typeof name === "object") {
    +
     5461.     38
    +
     5462.     38// test_cause:
    +
     5463.     38// ["let aa=function bb(){return;};", "prefix_function", "expression", "bb", 0]
    +
     5464.     38
    +
     5465.     38            test_cause("expression", name.id);
    +
     5466.     38            enroll(name, "function", true);
    +
     5467.     38            name.dead = false;
    +
     5468.     38            name.init = true;
    +
     5469.     38            name.used = 1;
    +
     5470.   2003        }
    +
     5471.   2003
    +
     5472.   2003// PR-334 - Bugfix - fix function-redefinition not warned inside function-call.
    +
     5473.   2003// Push the current function context and establish a new one.
    +
     5474.   2003
    +
     5475.   2003        function_list.push(the_function);
    +
     5476.   2003        function_stack.push(functionage);
    +
     5477.   2003        functionage = the_function;
    +
     5478.   2003
    +
     5479.   2003// Parse the parameter list.
    +
     5480.   2003
    +
     5481.   2003        advance("(");
    +
     5482.   2003        token_now.arity = "function";
    +
     5483.   2003        prefix_function_parameter(the_function);
    +
     5484.   2003
    +
     5485.   2003// The function's body is a block.
    +
     5486.   2003
    +
     5487.   2003        the_function.block = block("body");
    +
     5488.   2003        if (
    +
     5489.   2003            the_function.arity === "statement"
    +
     5490.   2003            && token_nxt.line === token_now.line
    +
     5491.      2        ) {
    +
     5492.      2
    +
     5493.      2// test_cause:
    +
     5494.      2// ["function aa(){}0", "prefix_function", "unexpected_a", "0", 16]
    +
     5495.      2
    +
     5496.      2            return stop("unexpected_a");
    +
     5497.   1990        }
    +
     5498.   1990        if (
    +
     5499.   1990            token_nxt.id === "."
    +
     5500.   1990            || token_nxt.id === "?."
    +
     5501.   2005
    +
     5502.   2005// PR-459 - Allow destructuring-assignment after function-definition.
    +
     5503.   2005
    +
     5504.   2005            // || token_nxt.id === "["
    +
     5505.      2        ) {
    +
     5506.      2
    +
     5507.      2// test_cause:
    +
     5508.      2// ["function aa(){}\n.aa", "prefix_function", "unexpected_a", ".", 1]
    +
     5509.      2// ["function aa(){}\n?.aa", "prefix_function", "unexpected_a", "?.", 1]
    +
     5510.      2
    +
     5511.      2            warn("unexpected_a");
    +
     5512.   1990        }
    +
     5513.   1990
    +
     5514.   1990// Check functions are ordered.
    +
     5515.   1990
    +
     5516.   1990        check_ordered(
    +
     5517.   1990            "function",
    +
     5518.   1990            function_list.slice(
    +
     5519.   1990                function_list.indexOf(the_function) + 1
    +
     5520.   2515            ).map(function ({
    +
     5521.   2515                level,
    +
     5522.   2515                name
    +
     5523.   2515            }) {
    +
     5524.   1990                return (level === the_function.level + 1) && name;
    +
     5525.   2515            }).filter(function (name) {
    +
     5526.   2510                return option_dict.beta && name && name.id;
    +
     5527.   2515            })
    +
     5528.   1990        );
    +
     5529.   1990
    +
     5530.   1990// Restore the previous context.
    +
     5531.   1990
    +
     5532.   1990        functionage = function_stack.pop();
    +
     5533.   1990        return the_function;
    +
     5534.   1990    }
    +
     5535.    631
    +
     5536.   2017    function prefix_function_parameter(the_function) {
    +
     5537.   2017
    +
     5538.   2017// This function will parse input <parameters> at beginning of <the_function>
    +
     5539.   2017
    +
     5540.   2017        let optional;
    +
     5541.   2017        let parameters = [];
    +
     5542.   2017        let signature = ["("];
    +
     5543.   2017        let subparam;
    +
     5544.   2781        function param_enroll(name) {
    +
     5545.   2514            if (name.identifier) {
    +
     5546.   2514                enroll(name, "parameter", false);
    +
     5547.   2514            } else {
    +
     5548.    267
    +
     5549.    267// test_cause:
    +
     5550.    267// ["([aa])=>0", "param_enroll", "use_function_not_fart", "=>", 7]
    +
     5551.    267// ["({aa})=>0", "param_enroll", "use_function_not_fart", "=>", 7]
    +
     5552.    267
    +
     5553.    267                if (the_function.id === "=>" && !option_dict.fart) {
    +
     5554.    267                    warn("use_function_not_fart", the_function);
    +
     5555.    267                }
    +
     5556.    267
    +
     5557.    267// Recurse param_enroll().
    +
     5558.    267
    +
     5559.    267                name.names.forEach(param_enroll);
    +
     5560.    267            }
    +
     5561.   2781        }
    +
     5562.   2077        function param_parse() {
    +
     5563.   2077            let ellipsis = false;
    +
     5564.   2077            let param;
    +
     5565.    227            if (token_nxt.id === "{") {
    +
     5566.    227                if (optional !== undefined) {
    +
     5567.    227
    +
     5568.    227// test_cause:
    +
     5569.    227// ["function aa(aa=0,{}){}", "param_parse", "required_a_optional_b", "aa", 18]
    +
     5570.    227
    +
     5571.    227                    warn(
    +
     5572.    227                        "required_a_optional_b",
    +
     5573.    227                        token_nxt,
    +
     5574.    227                        token_nxt.id,
    +
     5575.    227                        optional.id
    +
     5576.    227                    );
    +
     5577.    227                }
    +
     5578.    227                param = token_nxt;
    +
     5579.    227                param.names = [];
    +
     5580.    227                advance("{");
    +
     5581.    227                signature.push("{");
    +
     5582.    625                while (true) {
    +
     5583.    625                    subparam = token_nxt;
    +
     5584.    625                    if (!subparam.identifier) {
    +
     5585.    625
    +
     5586.    625// test_cause:
    +
     5587.    625// ["function aa(aa=0,{}){}", "param_parse", "expected_identifier_a", "}", 19]
    +
     5588.    625// ["function aa({0}){}", "param_parse", "expected_identifier_a", "0", 14]
    +
     5589.    625
    +
     5590.    625                        return stop("expected_identifier_a");
    +
     5591.    625                    }
    +
     5592.    625                    survey(subparam);
    +
     5593.    625                    advance();
    +
     5594.    625                    signature.push(subparam.id);
    +
     5595.    625                    if (token_nxt.id === ":") {
    +
     5596.    625                        advance(":");
    +
     5597.    625                        advance();
    +
     5598.    625                        token_now.label = subparam;
    +
     5599.    625                        subparam = token_now;
    +
     5600.    625                        if (!subparam.identifier) {
    +
     5601.    625
    +
     5602.    625// test_cause:
    +
     5603.    625// ["function aa({aa:0}){}", "param_parse", "expected_identifier_a", "}", 18]
    +
     5604.    625
    +
     5605.    625                            return stop(
    +
     5606.    625                                "expected_identifier_a",
    +
     5607.    625                                token_nxt
    +
     5608.    625                            );
    +
     5609.    625                        }
    +
     5610.    625                    }
    +
     5611.    625
    +
     5612.    625// test_cause:
    +
     5613.    625// ["function aa({aa=aa},aa){}", "param_parse", "equal", "", 0]
    +
     5614.    625
    +
     5615.    625                    test_cause("equal");
    +
     5616.    625                    if (token_nxt.id === "=") {
    +
     5617.    625                        advance("=");
    +
     5618.    625                        subparam.expression = parse_expression();
    +
     5619.    625                        param.open = true;
    +
     5620.    625                    }
    +
     5621.    625                    param.names.push(subparam);
    +
     5622.    625                    if (token_nxt.id === ",") {
    +
     5623.    625                        advance(",");
    +
     5624.    625                        signature.push(", ");
    +
     5625.    625                    } else {
    +
     5626.    625                        break;
    +
     5627.    625                    }
    +
     5628.    625                }
    +
     5629.    227                parameters.push(param);
    +
     5630.    227
    +
     5631.    227// test_cause:
    +
     5632.    227// ["
    +
     5633.    227// function aa({bb,aa}){}
    +
     5634.    227// ", "check_ordered", "expected_a_b_before_c_d", "aa", 17]
    +
     5635.    227
    +
     5636.    227                check_ordered("parameter", param.names);
    +
     5637.    227                advance("}");
    +
     5638.    227                signature.push("}");
    +
     5639.    227                if (token_nxt.id === ",") {
    +
     5640.    227                    advance(",");
    +
     5641.    227                    signature.push(", ");
    +
     5642.    227                    param_parse();
    +
     5643.    227                    return;
    +
     5644.    227                }
    +
     5645.   1850            } else if (token_nxt.id === "[") {
    +
     5646.   1850                if (optional !== undefined) {
    +
     5647.   1850
    +
     5648.   1850// test_cause:
    +
     5649.   1850// ["function aa(aa=0,[]){}", "param_parse", "required_a_optional_b", "aa", 18]
    +
     5650.   1850
    +
     5651.   1850                    warn(
    +
     5652.   1850                        "required_a_optional_b",
    +
     5653.   1850                        token_nxt,
    +
     5654.   1850                        token_nxt.id,
    +
     5655.   1850                        optional.id
    +
     5656.   1850                    );
    +
     5657.   1850                }
    +
     5658.   1850                param = token_nxt;
    +
     5659.   1850                param.names = [];
    +
     5660.   1850                advance("[");
    +
     5661.   1850                signature.push("[]");
    +
     5662.   1850                while (true) {
    +
     5663.   1850                    subparam = token_nxt;
    +
     5664.   1850                    if (!subparam.identifier) {
    +
     5665.   1850
    +
     5666.   1850// test_cause:
    +
     5667.   1850// ["function aa(aa=0,[]){}", "param_parse", "expected_identifier_a", "]", 19]
    +
     5668.   1850
    +
     5669.   1850                        return stop("expected_identifier_a");
    +
     5670.   1850                    }
    +
     5671.   1850                    advance();
    +
     5672.   1850                    param.names.push(subparam);
    +
     5673.   1850
    +
     5674.   1850// test_cause:
    +
     5675.   1850// ["function aa([aa=aa],aa){}", "param_parse", "id", "", 0]
    +
     5676.   1850
    +
     5677.   1850                    test_cause("id");
    +
     5678.   1850                    if (token_nxt.id === "=") {
    +
     5679.   1850                        advance("=");
    +
     5680.   1850                        subparam.expression = parse_expression();
    +
     5681.   1850                        param.open = true;
    +
     5682.   1850                    }
    +
     5683.   1850                    if (token_nxt.id === ",") {
    +
     5684.   1850                        advance(",");
    +
     5685.   1850                    } else {
    +
     5686.   1850                        break;
    +
     5687.   1850                    }
    +
     5688.   1850                }
    +
     5689.   1850                parameters.push(param);
    +
     5690.   1850                advance("]");
    +
     5691.   1850                if (token_nxt.id === ",") {
    +
     5692.   1850                    advance(",");
    +
     5693.   1850                    signature.push(", ");
    +
     5694.   1850                    param_parse();
    +
     5695.   1850                    return;
    +
     5696.   1850                }
    +
     5697.   1850            } else {
    +
     5698.   1850                if (token_nxt.id === "...") {
    +
     5699.   1850                    ellipsis = true;
    +
     5700.   1850                    signature.push("...");
    +
     5701.   1850                    advance("...");
    +
     5702.   1850                    if (optional !== undefined) {
    +
     5703.   1850
    +
     5704.   1850// test_cause:
    +
     5705.   1850// ["function aa(aa=0,...){}", "param_parse", "required_a_optional_b", "aa", 21]
    +
     5706.   1850
    +
     5707.   1850                        warn(
    +
     5708.   1850                            "required_a_optional_b",
    +
     5709.   1850                            token_nxt,
    +
     5710.   1850                            token_nxt.id,
    +
     5711.   1850                            optional.id
    +
     5712.   1850                        );
    +
     5713.   1850                    }
    +
     5714.   1850                }
    +
     5715.   1850                if (!token_nxt.identifier) {
    +
     5716.   1850
    +
     5717.   1850// test_cause:
    +
     5718.   1850// ["function aa(0){}", "param_parse", "expected_identifier_a", "0", 13]
    +
     5719.   1850
    +
     5720.   1850                    return stop("expected_identifier_a");
    +
     5721.   1850                }
    +
     5722.   1850                param = token_nxt;
    +
     5723.   1850                parameters.push(param);
    +
     5724.   1850                advance();
    +
     5725.   1850                signature.push(param.id);
    +
     5726.   1850                if (ellipsis) {
    +
     5727.   1850                    param.ellipsis = true;
    +
     5728.   1850                } else {
    +
     5729.   1850                    if (token_nxt.id === "=") {
    +
     5730.   1850                        optional = param;
    +
     5731.   1850                        advance("=");
    +
     5732.   1850                        param.expression = parse_expression(0);
    +
     5733.   1850                    } else {
    +
     5734.   1850                        if (optional !== undefined) {
    +
     5735.   1850
    +
     5736.   1850// test_cause:
    +
     5737.   1850// ["function aa(aa=0,bb){}", "param_parse", "required_a_optional_b", "aa", 18]
    +
     5738.   1850
    +
     5739.   1850                            warn(
    +
     5740.   1850                                "required_a_optional_b",
    +
     5741.   1850                                param,
    +
     5742.   1850                                param.id,
    +
     5743.   1850                                optional.id
    +
     5744.   1850                            );
    +
     5745.   1850                        }
    +
     5746.   1850                    }
    +
     5747.   1850                    if (token_nxt.id === ",") {
    +
     5748.   1850                        advance(",");
    +
     5749.   1850                        signature.push(", ");
    +
     5750.   1850                        param_parse();
    +
     5751.   1850                        return;
    +
     5752.   1850                    }
    +
     5753.   1850                }
    +
     5754.   1850            }
    +
     5755.   2077        }
    +
     5756.   2017
    +
     5757.   2017// test_cause:
    +
     5758.   2017// ["function aa(){}", "prefix_function_parameter", "opener", "(", 0]
    +
     5759.   2017
    +
     5760.   2017        test_cause("opener", token_now.id);
    +
     5761.   2017        token_now.free = false;
    +
     5762.   1485        if (token_nxt.id !== ")" && token_nxt.id !== "(end)") {
    +
     5763.   1485            param_parse();
    +
     5764.   2009        }
    +
     5765.   2009        advance(")");
    +
     5766.   2009        signature.push(")");
    +
     5767.   2009        parameters.forEach(param_enroll);
    +
     5768.   2009        the_function.parameters = parameters;
    +
     5769.   2009        the_function.signature = signature.join("");
    +
     5770.   2009    }
    +
     5771.    631
    +
     5772.    588    function prefix_lbrace() {
    +
     5773.    588        const seen = empty();
    +
     5774.    588        const the_brace = token_now;
    +
     5775.    588        let extra;
    +
     5776.    588        let full;
    +
     5777.    588        let id;
    +
     5778.    588        let name;
    +
     5779.    588        let the_colon;
    +
     5780.    588        let value;
    +
     5781.    588        the_brace.expression = [];
    +
     5782.    548        if (token_nxt.id !== "}") {
    +
     5783.    548
    +
     5784.    548// Parse/loop through each property in {...}.
    +
     5785.    548
    +
     5786.   1996            while (true) {
    +
     5787.   1996                name = token_nxt;
    +
     5788.   1996                advance();
    +
     5789.   1996                if (
    +
     5790.   1996                    (name.id === "get" || name.id === "set")
    +
     5791.   1996                    && token_nxt.identifier
    +
     5792.   1996                ) {
    +
     5793.   1996                    if (!option_dict.getset) {
    +
     5794.   1996
    +
     5795.   1996// test_cause:
    +
     5796.   1996// ["aa={get aa(){}}", "prefix_lbrace", "unexpected_a", "get", 5]
    +
     5797.   1996
    +
     5798.   1996                        warn("unexpected_a", name);
    +
     5799.   1996                    }
    +
     5800.   1996                    extra = name.id;
    +
     5801.   1996                    full = extra + " " + token_nxt.id;
    +
     5802.   1996                    name = token_nxt;
    +
     5803.   1996                    advance();
    +
     5804.   1996                    id = survey(name);
    +
     5805.   1996                    if (seen[full] === true || seen[id] === true) {
    +
     5806.   1996
    +
     5807.   1996// test_cause:
    +
     5808.   1996// ["aa={get aa(){},get aa(){}}", "prefix_lbrace", "duplicate_a", "aa", 20]
    +
     5809.   1996
    +
     5810.   1996                        warn("duplicate_a", name);
    +
     5811.   1996                    }
    +
     5812.   1996                    seen[id] = false;
    +
     5813.   1996                    seen[full] = true;
    +
     5814.   1996                } else if (name.id === "`") {
    +
     5815.   1996
    +
     5816.   1996// test_cause:
    +
     5817.   1996// ["aa={`aa`:0}", "prefix_lbrace", "unexpected_a", "`", 5]
    +
     5818.   1996
    +
     5819.   1996                    stop("unexpected_a", name);
    +
     5820.   1996
    +
     5821.   1996                } else {
    +
     5822.   1996                    id = survey(name);
    +
     5823.   1996                    if (typeof seen[id] === "boolean") {
    +
     5824.   1996
    +
     5825.   1996// test_cause:
    +
     5826.   1996// ["aa={aa,aa}", "prefix_lbrace", "duplicate_a", "aa", 8]
    +
     5827.   1996
    +
     5828.   1996                        warn("duplicate_a", name);
    +
     5829.   1996                    }
    +
     5830.   1996                    seen[id] = true;
    +
     5831.   1996                }
    +
     5832.   1996                if (name.identifier) {
    +
     5833.   1996                    if (token_nxt.id === "}" || token_nxt.id === ",") {
    +
     5834.   1996                        if (typeof extra === "string") {
    +
     5835.   1996
    +
     5836.   1996// test_cause:
    +
     5837.   1996// ["aa={get aa}", "prefix_lbrace", "closer", "", 0]
    +
     5838.   1996
    +
     5839.   1996                            test_cause("closer");
    +
     5840.   1996                            advance("(");
    +
     5841.   1996                        }
    +
     5842.   1996                        value = parse_expression(Infinity, true);
    +
     5843.   1996                    } else if (token_nxt.id === "(") {
    +
     5844.   1996
    +
     5845.   1996// test_cause:
    +
     5846.   1996// ["aa={aa()}", "prefix_lbrace", "paren", "", 0]
    +
     5847.   1996// ["aa={get aa(){}}", "prefix_lbrace", "paren", "", 0]
    +
     5848.   1996
    +
     5849.   1996                        test_cause("paren");
    +
     5850.   1996                        value = prefix_function({
    +
     5851.   1996                            arity: "unary",
    +
     5852.   1996                            from: name.from,
    +
     5853.   1996                            id: "function",
    +
     5854.   1996                            line: name.line,
    +
     5855.   1996                            name: (
    +
     5856.   1996                                typeof extra === "string"
    +
     5857.   1996                                ? extra
    +
     5858.   1996                                : id
    +
     5859.   1996                            ),
    +
     5860.   1996                            thru: name.from
    +
     5861.   1996                        });
    +
     5862.   1996                    } else {
    +
     5863.   1996                        if (typeof extra === "string") {
    +
     5864.   1996
    +
     5865.   1996// test_cause:
    +
     5866.   1996// ["aa={get aa.aa}", "prefix_lbrace", "paren", "", 0]
    +
     5867.   1996
    +
     5868.   1996                            test_cause("paren");
    +
     5869.   1996                            advance("(");
    +
     5870.   1996                        }
    +
     5871.   1996                        the_colon = token_nxt;
    +
     5872.   1996                        advance(":");
    +
     5873.   1996                        value = parse_expression(0);
    +
     5874.   1996                        if (
    +
     5875.   1996                            value.id === name.id
    +
     5876.   1996                            && value.id !== "function"
    +
     5877.   1996                        ) {
    +
     5878.   1996
    +
     5879.   1996// test_cause:
    +
     5880.   1996// ["aa={aa:aa}", "prefix_lbrace", "unexpected_a", ": aa", 7]
    +
     5881.   1996
    +
     5882.   1996                            warn("unexpected_a", the_colon, ": " + name.id);
    +
     5883.   1996                        }
    +
     5884.   1996                    }
    +
     5885.   1996                    value.label = name;
    +
     5886.   1996                    if (typeof extra === "string") {
    +
     5887.   1996                        value.extra = extra;
    +
     5888.   1996                    }
    +
     5889.   1996                    the_brace.expression.push(value);
    +
     5890.   1996                } else {
    +
     5891.   1996
    +
     5892.   1996// test_cause:
    +
     5893.   1996// ["aa={\"aa\":0}", "prefix_lbrace", "colon", "", 0]
    +
     5894.   1996
    +
     5895.   1996                    test_cause("colon");
    +
     5896.   1996                    advance(":");
    +
     5897.   1996                    value = parse_expression(0);
    +
     5898.   1996                    value.label = name;
    +
     5899.   1996                    the_brace.expression.push(value);
    +
     5900.   1996                }
    +
     5901.   1996                if (token_nxt.id !== ",") {
    +
     5902.   1996                    break;
    +
     5903.   1996                }
    +
     5904.   1996
    +
     5905.   1996// test_cause:
    +
     5906.   1996// ["aa={\"aa\":0,\"bb\":0}", "prefix_lbrace", "comma", "", 0]
    +
     5907.   1996
    +
     5908.   1996                test_cause("comma");
    +
     5909.   1996                advance(",");
    +
     5910.   1996                if (token_nxt.id === "}") {
    +
     5911.   1996
    +
     5912.   1996// test_cause:
    +
     5913.   1996// ["let aa={aa:0,}", "prefix_lbrace", "unexpected_a", ",", 13]
    +
     5914.   1996
    +
     5915.   1996                    warn("unexpected_a", token_now);
    +
     5916.   1996                    break;
    +
     5917.   1996                }
    +
     5918.   1996            }
    +
     5919.    583        }
    +
     5920.    583
    +
     5921.    583// test_cause:
    +
     5922.    583// ["aa={bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8]
    +
     5923.    583
    +
     5924.    583        check_ordered(
    +
     5925.    583            "property",
    +
     5926.   1991            the_brace.expression.map(function ({
    +
     5927.   1991                label
    +
     5928.   1991            }) {
    +
     5929.   1991                return label;
    +
     5930.   1991            })
    +
     5931.    583        );
    +
     5932.    583        advance("}");
    +
     5933.    583        return the_brace;
    +
     5934.    583    }
    +
     5935.    631
    +
     5936.    759    function prefix_lbracket() {
    +
     5937.    759        const the_token = token_now;
    +
     5938.    759        let element;
    +
     5939.    759        let ellipsis;
    +
     5940.    759        the_token.expression = [];
    +
     5941.    392        if (token_nxt.id !== "]") {
    +
     5942.    392
    +
     5943.    392// Parse/loop through each element in [...].
    +
     5944.    392
    +
     5945.   1884            while (true) {
    +
     5946.   1884                ellipsis = false;
    +
     5947.   1884                if (token_nxt.id === "...") {
    +
     5948.   1884                    ellipsis = true;
    +
     5949.   1884                    advance("...");
    +
     5950.   1884                }
    +
     5951.   1884                element = parse_expression(10);
    +
     5952.   1884                if (ellipsis) {
    +
     5953.   1884                    element.ellipsis = true;
    +
     5954.   1884                }
    +
     5955.   1884                the_token.expression.push(element);
    +
     5956.   1884                if (token_nxt.id !== ",") {
    +
     5957.   1884                    break;
    +
     5958.   1884                }
    +
     5959.   1884                advance(",");
    +
     5960.   1884                if (token_nxt.id === "]") {
    +
     5961.   1884
    +
     5962.   1884// test_cause:
    +
     5963.   1884// ["let aa=[0,]", "prefix_lbracket", "unexpected_a", ",", 10]
    +
     5964.   1884
    +
     5965.   1884                    warn("unexpected_a", token_now);
    +
     5966.   1884                    break;
    +
     5967.   1884                }
    +
     5968.   1884            }
    +
     5969.    392        }
    +
     5970.    759        advance("]");
    +
     5971.    759        return the_token;
    +
     5972.    759    }
    +
     5973.    631
    +
     5974.   1616    function prefix_lparen() {
    +
     5975.   1616        let the_paren = token_now;
    +
     5976.   1616        let the_value;
    +
     5977.   1616
    +
     5978.   1616// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart.
    +
     5979.   1616
    +
     5980.     14        if (token_now.fart) {
    +
     5981.     14            return parse_fart(token_now.fart);
    +
     5982.   1602        }
    +
     5983.   1602
    +
     5984.   1602// test_cause:
    +
     5985.   1602// ["(0)", "prefix_lparen", "expr", "", 0]
    +
     5986.   1602
    +
     5987.   1602        test_cause("expr");
    +
     5988.   1602        the_paren.free = true;
    +
     5989.   1602        the_value = parse_expression(0);
    +
     5990.   1602        if (the_value.wrapped === true) {
    +
     5991.      1
    +
     5992.      1// test_cause:
    +
     5993.      1// ["((0))", "prefix_lparen", "unexpected_a", "(", 1]
    +
     5994.      1
    +
     5995.      1            warn("unexpected_a", the_paren);
    +
     5996.   1602        }
    +
     5997.   1602        the_value.wrapped = true;
    +
     5998.   1602        advance(")", the_paren);
    +
     5999.   1602        return the_value;
    +
     6000.   1602    }
    +
     6001.    631
    +
     6002.    155    function prefix_new() {
    +
     6003.    155        const the_new = token_now;
    +
     6004.    155        let right;
    +
     6005.    155        right = parse_expression(160);
    +
     6006.      1        if (token_nxt.id !== "(") {
    +
     6007.      1
    +
     6008.      1// test_cause:
    +
     6009.      1// ["new aa", "prefix_new", "expected_a_before_b", "(end)", 1]
    +
     6010.      1
    +
     6011.      1            warn("expected_a_before_b", token_nxt, "()", artifact());
    +
     6012.      1        }
    +
     6013.    155        the_new.expression = right;
    +
     6014.    155        return the_new;
    +
     6015.    155    }
    +
     6016.    631
    +
     6017.    782    function prefix_tick() {
    +
     6018.    782        const the_tick = token_now;
    +
     6019.    782        the_tick.value = [];
    +
     6020.    782        the_tick.expression = [];
    +
     6021.    782        if (token_nxt.id !== "`") {
    +
     6022.    782
    +
     6023.    782// Parse/loop through each token in `${...}`.
    +
     6024.    782
    +
     6025.   1499            while (true) {
    +
     6026.   1499                advance("(string)");
    +
     6027.   1499                the_tick.value.push(token_now);
    +
     6028.   1499                if (token_nxt.id !== "${") {
    +
     6029.   1499                    break;
    +
     6030.   1499                }
    +
     6031.   1499                advance("${");
    +
     6032.   1499
    +
     6033.   1499// test_cause:
    +
     6034.   1499// ["let aa=`${}`;", "prefix_tick", "${", "", 0]
    +
     6035.   1499
    +
     6036.   1499                test_cause("${");
    +
     6037.   1499                the_tick.expression.push(parse_expression(0));
    +
     6038.   1499                advance("}");
    +
     6039.   1499            }
    +
     6040.    780        }
    +
     6041.    780        advance("`");
    +
     6042.    780        return the_tick;
    +
     6043.    780    }
    +
     6044.    631
    +
     6045.      2    function prefix_void() {
    +
     6046.      2        const the_void = token_now;
    +
     6047.      2
    +
     6048.      2// test_cause:
    +
     6049.      2// ["void 0", "prefix_void", "unexpected_a", "void", 1]
    +
     6050.      2// ["void", "prefix_void", "unexpected_a", "void", 1]
    +
     6051.      2
    +
     6052.      2        warn("unexpected_a", the_void);
    +
     6053.      2        the_void.expression = parse_expression(0);
    +
     6054.      2        return the_void;
    +
     6055.      2    }
    +
     6056.    631
    +
     6057.  13430    function semicolon() {
    +
     6058.  13430
    +
     6059.  13430// Try to match a semicolon.
    +
     6060.  13430
    +
     6061.  13205        if (token_nxt.id === ";") {
    +
     6062.  13205            advance(";");
    +
     6063.  13205        } else {
    +
     6064.    225
    +
     6065.    225// test_cause:
    +
     6066.    225// ["0", "semicolon", "expected_a_b", "(end)", 1]
    +
     6067.    225
    +
     6068.    225            warn_at(
    +
     6069.    225                "expected_a_b",
    +
     6070.    225                token_now.line,
    +
     6071.    225                token_now.thru + 1,
    +
     6072.    225                ";",
    +
     6073.    225                artifact()
    +
     6074.    225            );
    +
     6075.    225        }
    +
     6076.  13430        anon = "anonymous";
    +
     6077.  13430    }
    +
     6078.    631
    +
     6079.  14513    function stmt(id, fud_stmt) {
    +
     6080.  14513
    +
     6081.  14513// Create a statement.
    +
     6082.  14513
    +
     6083.  14513        const the_symbol = symbol(id);
    +
     6084.  14513        the_symbol.fud_stmt = fud_stmt;
    +
     6085.  14513        return the_symbol;
    +
     6086.  14513    }
    +
     6087.    631
    +
     6088.   1023    function stmt_break() {
    +
     6089.   1023        const the_break = token_now;
    +
     6090.   1023        let the_label;
    +
     6091.   1023        if (
    +
     6092.    719            (functionage.loop < 1 && functionage.switch < 1)
    +
     6093.   1017            || functionage.finally > 0
    +
     6094.      6        ) {
    +
     6095.      6
    +
     6096.      6// test_cause:
    +
     6097.      6// ["break", "stmt_break", "unexpected_a", "break", 1]
    +
     6098.      6
    +
     6099.      6            warn("unexpected_a", the_break);
    +
     6100.      6        }
    +
     6101.   1023        the_break.disrupt = true;
    +
     6102.      5        if (token_nxt.identifier && token_now.line === token_nxt.line) {
    +
     6103.      5            the_label = functionage.context[token_nxt.id];
    +
     6104.      5            if (
    +
     6105.      5                the_label === undefined
    +
     6106.      5                || the_label.role !== "label"
    +
     6107.      5                || the_label.dead
    +
     6108.      5            ) {
    +
     6109.      5                if (the_label !== undefined && the_label.dead) {
    +
     6110.      5
    +
     6111.      5// test_cause:
    +
     6112.      5// ["aa:{function aa(aa){break aa;}}", "stmt_break", "out_of_scope_a", "aa", 27]
    +
     6113.      5
    +
     6114.      5                    warn("out_of_scope_a");
    +
     6115.      5                } else {
    +
     6116.      5
    +
     6117.      5// test_cause:
    +
     6118.      5// ["aa:{break aa;}", "stmt_break", "not_label_a", "aa", 11]
    +
     6119.      5
    +
     6120.      5                    warn("not_label_a");
    +
     6121.      5                }
    +
     6122.      5            } else {
    +
     6123.      5                the_label.used += 1;
    +
     6124.      5            }
    +
     6125.      5            the_break.label = token_nxt;
    +
     6126.      5            advance();
    +
     6127.      5        }
    +
     6128.   1023        advance(";");
    +
     6129.   1023        return the_break;
    +
     6130.   1023    }
    +
     6131.    631
    +
     6132.      2    function stmt_continue() {
    +
     6133.      2        const the_continue = token_now;
    +
     6134.      1        if (functionage.loop < 1 || functionage.finally > 0) {
    +
     6135.      2
    +
     6136.      2// test_cause:
    +
     6137.      2// ["continue", "stmt_continue", "unexpected_a", "continue", 1]
    +
     6138.      2// ["
    +
     6139.      2// function aa(){while(0){try{}finally{continue}}}
    +
     6140.      2// ", "stmt_continue", "unexpected_a", "continue", 37]
    +
     6141.      2
    +
     6142.      2            warn("unexpected_a", the_continue);
    +
     6143.      2        }
    +
     6144.      2        check_not_top_level(the_continue);
    +
     6145.      2        the_continue.disrupt = true;
    +
     6146.      2        warn("unexpected_a", the_continue);
    +
     6147.      2        advance(";");
    +
     6148.      2        return the_continue;
    +
     6149.      2    }
    +
     6150.    631
    +
     6151.      3    function stmt_debugger() {
    +
     6152.      3        const the_debug = token_now;
    +
     6153.      1        if (!option_dict.devel) {
    +
     6154.      1
    +
     6155.      1// test_cause:
    +
     6156.      1// ["debugger", "stmt_debugger", "unexpected_a", "debugger", 1]
    +
     6157.      1
    +
     6158.      1            warn("unexpected_a", the_debug);
    +
     6159.      1        }
    +
     6160.      3        semicolon();
    +
     6161.      3        return the_debug;
    +
     6162.      3    }
    +
     6163.    631
    +
     6164.     72    function stmt_delete() {
    +
     6165.     72        const the_token = token_now;
    +
     6166.     72        const the_value = parse_expression(0);
    +
     6167.     72        if (
    +
     6168.      1            (the_value.id !== "." && the_value.id !== "[")
    +
     6169.     71            || the_value.arity !== "binary"
    +
     6170.      1        ) {
    +
     6171.      1
    +
     6172.      1// test_cause:
    +
     6173.      1// ["delete 0", "stmt_delete", "expected_a_b", "0", 8]
    +
     6174.      1
    +
     6175.      1            stop("expected_a_b", the_value, ".", artifact(the_value));
    +
     6176.     71        }
    +
     6177.     71        the_token.expression = the_value;
    +
     6178.     71        semicolon();
    +
     6179.     71        return the_token;
    +
     6180.     71    }
    +
     6181.    631
    +
     6182.      5    function stmt_do() {
    +
     6183.      5        const the_do = token_now;
    +
     6184.      5        check_not_top_level(the_do);
    +
     6185.      5        functionage.loop += 1;
    +
     6186.      5        the_do.block = block();
    +
     6187.      5        advance("while");
    +
     6188.      5        the_do.expression = condition();
    +
     6189.      5        semicolon();
    +
     6190.      1        if (the_do.block.disrupt === true) {
    +
     6191.      1
    +
     6192.      1// test_cause:
    +
     6193.      1// ["function aa(){do{break;}while(0)}", "stmt_do", "weird_loop", "do", 15]
    +
     6194.      1
    +
     6195.      1            warn("weird_loop", the_do);
    +
     6196.      3        }
    +
     6197.      3        functionage.loop -= 1;
    +
     6198.      3        return the_do;
    +
     6199.      3    }
    +
     6200.    631
    +
     6201.     24    function stmt_export() {
    +
     6202.     24        let export_list = [];
    +
     6203.     24        let the_export = token_now;
    +
     6204.     24        let the_id;
    +
     6205.     24        let the_name;
    +
     6206.     24        let the_thing;
    +
     6207.     24
    +
     6208.     24        the_export.expression = [];
    +
     6209.     11        if (token_nxt.id === "default") {
    +
     6210.     11            if (export_dict.default !== undefined) {
    +
     6211.     11
    +
     6212.     11// test_cause:
    +
     6213.     11// ["
    +
     6214.     11// export default 0;export default 0
    +
     6215.     11// ", "stmt_export", "duplicate_a", "default", 25]
    +
     6216.     11
    +
     6217.     11                warn("duplicate_a");
    +
     6218.     11            }
    +
     6219.     11            advance("default");
    +
     6220.     11            the_thing = parse_expression(0);
    +
     6221.     11            if (
    +
     6222.     11                the_thing.id !== "("
    +
     6223.     11                || the_thing.expression[0].id !== "."
    +
     6224.     11                || the_thing.expression[0].expression.id !== "Object"
    +
     6225.     11                || the_thing.expression[0].name.id !== "freeze"
    +
     6226.     11            ) {
    +
     6227.     11
    +
     6228.     11// test_cause:
    +
     6229.     11// ["export default {}", "stmt_export", "freeze_exports", "{", 16]
    +
     6230.     11
    +
     6231.     11                warn("freeze_exports", the_thing);
    +
     6232.     11
    +
     6233.     11// PR-301 - Bugfix - Fixes issues #282 - optional-semicolon.
    +
     6234.     11
    +
     6235.     11            } else {
    +
     6236.     11
    +
     6237.     11// test_cause:
    +
     6238.     11// ["
    +
     6239.     11// export default Object.freeze({})
    +
     6240.     11// ", "semicolon", "expected_a_b", "(end)", 32]
    +
     6241.     11
    +
     6242.     11                semicolon();
    +
     6243.     11            }
    +
     6244.     11            export_dict.default = the_thing;
    +
     6245.     11            the_export.expression.push(the_thing);
    +
     6246.     13        } else {
    +
     6247.     13
    +
     6248.     13// PR-439 - Add grammar for "export async function ...".
    +
     6249.     13
    +
     6250.     13            if (token_nxt.id === "function" || token_nxt.id === "async") {
    +
     6251.     13
    +
     6252.     13// test_cause:
    +
     6253.     13// ["export async function aa(){}", "stmt_export", "freeze_exports", "async", 8]
    +
     6254.     13// ["export function aa(){}", "stmt_export", "freeze_exports", "function", 8]
    +
     6255.     13
    +
     6256.     13                warn("freeze_exports");
    +
     6257.     13                the_thing = parse_statement();
    +
     6258.     13                the_name = the_thing.name;
    +
     6259.     13                the_id = the_name.id;
    +
     6260.     13                the_name.used += 1;
    +
     6261.     13                if (export_dict[the_id] !== undefined) {
    +
     6262.     13
    +
     6263.     13// test_cause:
    +
     6264.     13// ["
    +
     6265.     13// let aa;export{aa};export function aa(){}
    +
     6266.     13// ", "stmt_export", "duplicate_a", "aa", 35]
    +
     6267.     13
    +
     6268.     13                    warn("duplicate_a", the_name);
    +
     6269.     13                }
    +
     6270.     13                export_dict[the_id] = the_thing;
    +
     6271.     13                the_export.expression.push(the_thing);
    +
     6272.     13                the_thing.statement = false;
    +
     6273.     13                the_thing.arity = "unary";
    +
     6274.     13            } else if (
    +
     6275.     13                token_nxt.id === "var"
    +
     6276.     13                || token_nxt.id === "let"
    +
     6277.     13                || token_nxt.id === "const"
    +
     6278.     13            ) {
    +
     6279.     13
    +
     6280.     13// test_cause:
    +
     6281.     13// ["export const", "stmt_export", "unexpected_a", "const", 8]
    +
     6282.     13// ["export let", "stmt_export", "unexpected_a", "let", 8]
    +
     6283.     13// ["export var", "stmt_export", "unexpected_a", "var", 8]
    +
     6284.     13
    +
     6285.     13                warn("unexpected_a");
    +
     6286.     13                parse_statement();
    +
     6287.     13            } else if (token_nxt.id === "{") {
    +
     6288.     13
    +
     6289.     13// test_cause:
    +
     6290.     13// ["export {}", "stmt_export", "advance{", "", 0]
    +
     6291.     13
    +
     6292.     13                test_cause("advance{");
    +
     6293.     13                advance("{");
    +
     6294.     13                while (true) {
    +
     6295.     13                    if (!token_nxt.identifier) {
    +
     6296.     13
    +
     6297.     13// test_cause:
    +
     6298.     13// ["export {}", "stmt_export", "expected_identifier_a", "}", 9]
    +
     6299.     13
    +
     6300.     13                        stop("expected_identifier_a");
    +
     6301.     13                    }
    +
     6302.     13                    the_id = token_nxt.id;
    +
     6303.     13                    export_list.push(token_nxt);
    +
     6304.     13                    the_name = token_global.context[the_id];
    +
     6305.     13                    if (the_name === undefined) {
    +
     6306.     13
    +
     6307.     13// test_cause:
    +
     6308.     13// ["export {aa}", "stmt_export", "unexpected_a", "aa", 9]
    +
     6309.     13
    +
     6310.     13                        warn("unexpected_a");
    +
     6311.     13                    } else {
    +
     6312.     13                        the_name.used += 1;
    +
     6313.     13                        if (export_dict[the_id] !== undefined) {
    +
     6314.     13
    +
     6315.     13// test_cause:
    +
     6316.     13// ["let aa;export{aa,aa}", "stmt_export", "duplicate_a", "aa", 18]
    +
     6317.     13
    +
     6318.     13                            warn("duplicate_a");
    +
     6319.     13                        }
    +
     6320.     13                        export_dict[the_id] = the_name;
    +
     6321.     13                    }
    +
     6322.     13                    advance();
    +
     6323.     13                    the_export.expression.push(the_thing);
    +
     6324.     13                    if (token_nxt.id === ",") {
    +
     6325.     13                        advance(",");
    +
     6326.     13                    } else {
    +
     6327.     13                        break;
    +
     6328.     13                    }
    +
     6329.     13                }
    +
     6330.     13
    +
     6331.     13// PR-439 - Check exported properties are ordered.
    +
     6332.     13
    +
     6333.     13// test_cause:
    +
     6334.     13// ["export {bb, aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 13]
    +
     6335.     13
    +
     6336.     13                check_ordered("export", export_list);
    +
     6337.     13                advance("}");
    +
     6338.     13                semicolon();
    +
     6339.     13            } else {
    +
     6340.     13
    +
     6341.     13// test_cause:
    +
     6342.     13// ["export", "stmt_export", "unexpected_a", "(end)", 1]
    +
     6343.     13
    +
     6344.     13                stop("unexpected_a");
    +
     6345.     13            }
    +
     6346.     18        }
    +
     6347.     18        state.mode_module = true;
    +
     6348.     18        return the_export;
    +
     6349.     18    }
    +
     6350.    631
    +
     6351.     12    function stmt_for() {
    +
     6352.     12        let first;
    +
     6353.     12        let the_for = token_now;
    +
     6354.      7        if (!option_dict.for) {
    +
     6355.      7
    +
     6356.      7// test_cause:
    +
     6357.      7// ["for", "stmt_for", "unexpected_a", "for", 1]
    +
     6358.      7
    +
     6359.      7            warn("unexpected_a", the_for);
    +
     6360.      7        }
    +
     6361.     12        check_not_top_level(the_for);
    +
     6362.     12        functionage.loop += 1;
    +
     6363.     12        advance("(");
    +
     6364.     12        token_now.free = true;
    +
     6365.      1        if (token_nxt.id === ";") {
    +
     6366.      1
    +
     6367.      1// test_cause:
    +
     6368.      1// ["for(;;){}", "stmt_for", "expected_a_b", "for (;", 1]
    +
     6369.      1
    +
     6370.      1            return stop("expected_a_b", the_for, "while (", "for (;");
    +
     6371.      9        }
    +
     6372.      9        switch (token_nxt.id) {
    +
     6373.      9        case "const":
    +
     6374.      1        case "let":
    +
     6375.      1        case "var":
    +
     6376.      1
    +
     6377.      1// test_cause:
    +
     6378.      1// ["for(const aa in aa){}", "stmt_for", "unexpected_a", "const", 5]
    +
     6379.      1
    +
     6380.      1            return stop("unexpected_a");
    +
     6381.      8        }
    +
     6382.      8        first = parse_expression(0);
    +
     6383.      8        if (first.id === "in") {
    +
     6384.      2            if (first.expression[0].arity !== "variable") {
    +
     6385.      2
    +
     6386.      2// test_cause:
    +
     6387.      2// ["for(0 in aa){}", "stmt_for", "bad_assignment_a", "0", 5]
    +
     6388.      2
    +
     6389.      2                warn("bad_assignment_a", first.expression[0]);
    +
     6390.      2            }
    +
     6391.      2            the_for.name = first.expression[0];
    +
     6392.      2            the_for.expression = first.expression[1];
    +
     6393.      2            warn("expected_a_b", the_for, "Object.keys", "for in");
    +
     6394.      6        } else {
    +
     6395.      6            the_for.initial = first;
    +
     6396.      6            advance(";");
    +
     6397.      6            the_for.expression = parse_expression(0);
    +
     6398.      6            advance(";");
    +
     6399.      6            the_for.inc = parse_expression(0);
    +
     6400.      6            if (the_for.inc.id === "++") {
    +
     6401.      6
    +
     6402.      6// test_cause:
    +
     6403.      6// ["for(aa;aa;aa++){}", "stmt_for", "expected_a_b", "++", 13]
    +
     6404.      6
    +
     6405.      6                warn("expected_a_b", the_for.inc, "+= 1", "++");
    +
     6406.      6            }
    +
     6407.      8        }
    +
     6408.      8        advance(")");
    +
     6409.      8        the_for.block = block();
    +
     6410.      8        if (the_for.block.disrupt === true) {
    +
     6411.      1
    +
     6412.      1// test_cause:
    +
     6413.      1// ["
    +
     6414.      1// /*jslint for*/
    +
     6415.      1// function aa(bb,cc){for(0;0;0){break;}}
    +
     6416.      1// ", "stmt_for", "weird_loop", "for", 20]
    +
     6417.      1
    +
     6418.      1            warn("weird_loop", the_for);
    +
     6419.      8        }
    +
     6420.      8        functionage.loop -= 1;
    +
     6421.      8        return the_for;
    +
     6422.      8    }
    +
     6423.    631
    +
     6424.   3051    function stmt_if() {
    +
     6425.   3051        const the_if = token_now;
    +
     6426.   3051        let the_else;
    +
     6427.   3051        the_if.expression = condition();
    +
     6428.   3051        the_if.block = block();
    +
     6429.    642        if (token_nxt.id === "else") {
    +
     6430.    642            advance("else");
    +
     6431.    642            the_else = token_now;
    +
     6432.    642            the_if.else = (
    +
     6433.    642                token_nxt.id === "if"
    +
     6434.    642                ? parse_statement()
    +
     6435.    642                : block()
    +
     6436.    642            );
    +
     6437.    642
    +
     6438.    642// test_cause:
    +
     6439.    642// ["if(0){0}else if(0){0}", "stmt_if", "else", "", 0]
    +
     6440.    642// ["if(0){0}else{0}", "stmt_if", "else", "", 0]
    +
     6441.    642
    +
     6442.    642            test_cause("else");
    +
     6443.    642            if (the_if.block.disrupt === true) {
    +
     6444.    642                if (the_if.else.disrupt === true) {
    +
     6445.    642
    +
     6446.    642// test_cause:
    +
     6447.    642// ["if(0){break;}else{break;}", "stmt_if", "disrupt", "", 0]
    +
     6448.    642
    +
     6449.    642                    test_cause("disrupt");
    +
     6450.    642                    the_if.disrupt = true;
    +
     6451.    642                } else {
    +
     6452.    642
    +
     6453.    642// test_cause:
    +
     6454.    642// ["if(0){break;}else{}", "stmt_if", "unexpected_a", "else", 14]
    +
     6455.    642
    +
     6456.    642                    warn("unexpected_a", the_else);
    +
     6457.    642                }
    +
     6458.    642            }
    +
     6459.   3050        }
    +
     6460.   3050        return the_if;
    +
     6461.   3050    }
    +
     6462.    631
    +
     6463.     62    function stmt_import() {
    +
     6464.     62        const the_import = token_now;
    +
     6465.     62        let name;
    +
     6466.     62        let names;
    +
     6467.     62
    +
     6468.     62// PR-347 - Disable warning "unexpected_directive_a".
    +
     6469.     62//
    +
     6470.     62//         if (typeof state.mode_module === "object") {
    +
     6471.     62//
    +
     6472.     62// // test_cause:
    +
     6473.     62// // ["
    +
     6474.     62// // /*global aa*/
    +
     6475.     62// // import aa from "aa"
    +
     6476.     62// // ", "stmt_import", "unexpected_directive_a", "global", 1]
    +
     6477.     62//
    +
     6478.     62//             warn(
    +
     6479.     62//                 "unexpected_directive_a",
    +
     6480.     62//                 state.mode_module,
    +
     6481.     62//                 state.mode_module.directive
    +
     6482.     62//             );
    +
     6483.     62//         }
    +
     6484.     62
    +
     6485.     62        state.mode_module = true;
    +
     6486.     62
    +
     6487.     62// PR-436 - Add grammar for side-effect import-statement.
    +
     6488.     62
    +
     6489.      1        if (token_nxt.id === "(string)") {
    +
     6490.      1
    +
     6491.      1// test_cause:
    +
     6492.      1// ["import \"./aa.mjs\";", "stmt_import", "import_side_effect", "", 0]
    +
     6493.      1
    +
     6494.      1            test_cause("import_side_effect");
    +
     6495.      1            warn("expected_a_b", token_nxt, "{", artifact());
    +
     6496.      1            advance();
    +
     6497.      1            semicolon();
    +
     6498.      1            return the_import;
    +
     6499.     61        }
    +
     6500.     61        if (token_nxt.identifier) {
    +
     6501.     57            name = token_nxt;
    +
     6502.     57            advance();
    +
     6503.     57            if (name.id === "ignore") {
    +
     6504.     57
    +
     6505.     57// test_cause:
    +
     6506.     57// ["import ignore from \"aa\"", "stmt_import", "unexpected_a", "ignore", 8]
    +
     6507.     57
    +
     6508.     57                warn("unexpected_a", name);
    +
     6509.     57            }
    +
     6510.     57            enroll(name, "variable", true);
    +
     6511.     57            the_import.name = name;
    +
     6512.     57        } else {
    +
     6513.      4            names = [];
    +
     6514.      4            advance("{");
    +
     6515.      4            if (token_nxt.id !== "}") {
    +
     6516.      4                while (true) {
    +
     6517.      4                    if (!token_nxt.identifier) {
    +
     6518.      4
    +
     6519.      4// test_cause:
    +
     6520.      4// ["import {", "stmt_import", "expected_identifier_a", "(end)", 1]
    +
     6521.      4
    +
     6522.      4                        stop("expected_identifier_a");
    +
     6523.      4                    }
    +
     6524.      4                    name = token_nxt;
    +
     6525.      4                    advance();
    +
     6526.      4                    if (name.id === "ignore") {
    +
     6527.      4
    +
     6528.      4// test_cause:
    +
     6529.      4// ["import {ignore} from \"aa\"", "stmt_import", "unexpected_a", "ignore", 9]
    +
     6530.      4
    +
     6531.      4                        warn("unexpected_a", name);
    +
     6532.      4                    }
    +
     6533.      4                    enroll(name, "variable", true);
    +
     6534.      4                    names.push(name);
    +
     6535.      4                    if (token_nxt.id !== ",") {
    +
     6536.      4                        break;
    +
     6537.      4                    }
    +
     6538.      4                    advance(",");
    +
     6539.      4                }
    +
     6540.      4            }
    +
     6541.      4            advance("}");
    +
     6542.      4            the_import.name = names;
    +
     6543.     60        }
    +
     6544.     60        advance("from");
    +
     6545.     60        advance("(string)");
    +
     6546.     60        the_import.import = token_now;
    +
     6547.     60        if (!jslint_rgx_module.test(token_now.value)) {
    +
     6548.      1
    +
     6549.      1// test_cause:
    +
     6550.      1// ["import aa from \"!aa\"", "stmt_import", "bad_module_name_a", "!aa", 16]
    +
     6551.      1
    +
     6552.      1            warn("bad_module_name_a", token_now);
    +
     6553.     60        }
    +
     6554.     60        import_list.push(token_now.value);
    +
     6555.     60        semicolon();
    +
     6556.     60        return the_import;
    +
     6557.     60    }
    +
     6558.    631
    +
     6559.      5    function stmt_lbrace() {
    +
     6560.      5
    +
     6561.      5// test_cause:
    +
     6562.      5// [";{}", "stmt_lbrace", "naked_block", "{", 2]
    +
     6563.      5// ["class aa{}", "stmt_lbrace", "naked_block", "{", 9]
    +
     6564.      5
    +
     6565.      5        warn("naked_block", token_now);
    +
     6566.      5        return block("naked");
    +
     6567.      5    }
    +
     6568.    631
    +
     6569.   1761    function stmt_return() {
    +
     6570.   1761        const the_return = token_now;
    +
     6571.   1761        check_not_top_level(the_return);
    +
     6572.      1        if (functionage.finally > 0) {
    +
     6573.      1
    +
     6574.      1// test_cause:
    +
     6575.      1// ["
    +
     6576.      1// function aa(){try{}finally{return;}}
    +
     6577.      1// ", "stmt_return", "unexpected_a", "return", 28]
    +
     6578.      1
    +
     6579.      1            warn("unexpected_a", the_return);
    +
     6580.      1        }
    +
     6581.   1761        the_return.disrupt = true;
    +
     6582.   1509        if (token_nxt.id !== ";" && the_return.line === token_nxt.line) {
    +
     6583.   1509            the_return.expression = parse_expression(10);
    +
     6584.   1509        }
    +
     6585.   1761        advance(";");
    +
     6586.   1761        return the_return;
    +
     6587.   1761    }
    +
     6588.    631
    +
     6589.      5    function stmt_semicolon() {
    +
     6590.      5
    +
     6591.      5// test_cause:
    +
     6592.      5// [";", "stmt_semicolon", "unexpected_a", ";", 1]
    +
     6593.      5
    +
     6594.      5        warn("unexpected_a", token_now);
    +
     6595.      5        return token_now;
    +
     6596.      5    }
    +
     6597.    631
    +
     6598.    220    function stmt_switch() {
    +
     6599.    220        const the_cases = [];
    +
     6600.    220        const the_switch = token_now;
    +
     6601.    220        let dups = [];
    +
     6602.    220        let exp;
    +
     6603.    220        let last;
    +
     6604.    220        let stmts;
    +
     6605.    220        let the_case;
    +
     6606.    220        let the_default;
    +
     6607.    220        let the_disrupt = true;
    +
     6608.    220        let the_last;
    +
     6609.  23088        function is_dup(thing) {
    +
     6610.  23088            return is_equal(thing, exp);
    +
     6611.  23088        }
    +
     6612.    220        check_not_top_level(the_switch);
    +
     6613.      1        if (functionage.finally > 0) {
    +
     6614.      1
    +
     6615.      1// test_cause:
    +
     6616.      1// ["
    +
     6617.      1// function aa(){try{}finally{switch(0){}}}
    +
     6618.      1// ", "stmt_switch", "unexpected_a", "switch", 28]
    +
     6619.      1
    +
     6620.      1            warn("unexpected_a", the_switch);
    +
     6621.      1        }
    +
     6622.    220        functionage.switch += 1;
    +
     6623.    220        advance("(");
    +
     6624.    220        token_now.free = true;
    +
     6625.    220        the_switch.expression = parse_expression(0);
    +
     6626.    220        the_switch.block = the_cases;
    +
     6627.    220        advance(")");
    +
     6628.    220        advance("{");
    +
     6629.   1085        while (true) {
    +
     6630.   1085
    +
     6631.   1085// Loop through cases with breaks.
    +
     6632.   1085
    +
     6633.   1085            the_case = token_nxt;
    +
     6634.   1085            the_case.arity = "statement";
    +
     6635.   1085            the_case.expression = [];
    +
     6636.   1612            while (true) {
    +
     6637.   1612
    +
     6638.   1612// Loop through fallthrough cases.
    +
     6639.   1612
    +
     6640.   1612                advance("case");
    +
     6641.   1612                token_now.switch = true;
    +
     6642.   1612                exp = parse_expression(0);
    +
     6643.   1612                if (dups.some(is_dup)) {
    +
     6644.   1612
    +
     6645.   1612// test_cause:
    +
     6646.   1612// ["
    +
     6647.   1612// switch(0){case 0:break;case 0:break}
    +
     6648.   1612// ", "stmt_switch", "unexpected_a", "0", 29]
    +
     6649.   1612
    +
     6650.   1612                    warn("unexpected_a", exp);
    +
     6651.   1612                }
    +
     6652.   1612                dups.push(exp);
    +
     6653.   1612                the_case.expression.push(exp);
    +
     6654.   1612                advance(":");
    +
     6655.   1612                if (token_nxt.id !== "case") {
    +
     6656.   1612                    break;
    +
     6657.   1612                }
    +
     6658.   1612            }
    +
     6659.   1085
    +
     6660.   1085// test_cause:
    +
     6661.   1085// ["
    +
     6662.   1085// switch(0){case 1:case 0:break;}
    +
     6663.   1085// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 23]
    +
     6664.   1085// ["
    +
     6665.   1085// switch(0){case "aa":case 0:break;}
    +
     6666.   1085// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 26]
    +
     6667.   1085// ["
    +
     6668.   1085// switch(0){case "bb":case "aa":break;}
    +
     6669.   1085// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 26]
    +
     6670.   1085// ["
    +
     6671.   1085// switch(0){case aa:case "aa":break;}
    +
     6672.   1085// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24]
    +
     6673.   1085// ["
    +
     6674.   1085// switch(0){case bb:case aa:break;}
    +
     6675.   1085// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24]
    +
     6676.   1085
    +
     6677.   1085            check_ordered_case(the_case.expression);
    +
     6678.   1085            stmts = parse_statements();
    +
     6679.   1085            if (stmts.length < 1) {
    +
     6680.   1085
    +
     6681.   1085// test_cause:
    +
     6682.   1085// ["
    +
     6683.   1085// switch(0){case 0:default:}
    +
     6684.   1085// ", "stmt_switch", "expected_statements_a", "default", 18]
    +
     6685.   1085// ["switch(0){case 0:}", "stmt_switch", "expected_statements_a", "}", 18]
    +
     6686.   1085
    +
     6687.   1085                warn("expected_statements_a");
    +
     6688.   1085
    +
     6689.   1085// PR-359 - Bugfix - Fixes issue #358 - switch-statement crashes jslint.
    +
     6690.   1085
    +
     6691.   1085                break;
    +
     6692.   1085            }
    +
     6693.   1085            the_case.block = stmts;
    +
     6694.   1085            the_cases.push(the_case);
    +
     6695.   1085            last = stmts[stmts.length - 1];
    +
     6696.   1085            if (last.disrupt) {
    +
     6697.   1085                if (last.id === "break" && last.label === undefined) {
    +
     6698.   1085                    the_disrupt = false;
    +
     6699.   1085                }
    +
     6700.   1085            } else {
    +
     6701.   1085                warn("expected_a_before_b", token_nxt, "break;", artifact());
    +
     6702.   1085            }
    +
     6703.   1085            if (token_nxt.id !== "case") {
    +
     6704.   1085                break;
    +
     6705.   1085            }
    +
     6706.   1085        }
    +
     6707.    217
    +
     6708.    217// test_cause:
    +
     6709.    217// ["
    +
     6710.    217// switch(0){case 1:break;case 0:break;}
    +
     6711.    217// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 29]
    +
     6712.    217// ["
    +
     6713.    217// switch(0){case "aa":break;case 0:break;}
    +
     6714.    217// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 32]
    +
     6715.    217// ["
    +
     6716.    217// switch(0){case "bb":break;case "aa":break;}
    +
     6717.    217// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 32]
    +
     6718.    217// ["
    +
     6719.    217// switch(0){case aa:break;case "aa":break;}
    +
     6720.    217// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30]
    +
     6721.    217// ["
    +
     6722.    217// switch(0){case bb:break;case aa:break;}
    +
     6723.    217// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30]
    +
     6724.    217
    +
     6725.   1080        check_ordered_case(the_cases.map(function ({
    +
     6726.   1080            expression
    +
     6727.   1080        }) {
    +
     6728.   1080            return expression[0];
    +
     6729.   1080        }));
    +
     6730.    217        dups = undefined;
    +
     6731.    217        if (token_nxt.id === "default") {
    +
     6732.     99            the_default = token_nxt;
    +
     6733.     99            advance("default");
    +
     6734.     99            token_now.switch = true;
    +
     6735.     99            advance(":");
    +
     6736.     99            the_switch.else = parse_statements();
    +
     6737.     99            if (the_switch.else.length < 1) {
    +
     6738.     99
    +
     6739.     99// test_cause:
    +
     6740.     99// ["
    +
     6741.     99// switch(0){case 0:break;default:}
    +
     6742.     99// ", "stmt_switch", "unexpected_a", "default", 24]
    +
     6743.     99
    +
     6744.     99                warn("unexpected_a", the_default);
    +
     6745.     99                the_disrupt = false;
    +
     6746.     99            } else {
    +
     6747.     99                the_last = the_switch.else[
    +
     6748.     99                    the_switch.else.length - 1
    +
     6749.     99                ];
    +
     6750.     99                if (
    +
     6751.     99                    the_last.id === "break"
    +
     6752.     99                    && the_last.label === undefined
    +
     6753.     99                ) {
    +
     6754.     99
    +
     6755.     99// test_cause:
    +
     6756.     99// ["
    +
     6757.     99// switch(0){case 0:break;default:break;}
    +
     6758.     99// ", "stmt_switch", "unexpected_a", "break", 32]
    +
     6759.     99
    +
     6760.     99                    warn("unexpected_a", the_last);
    +
     6761.     99                    the_last.disrupt = false;
    +
     6762.     99                }
    +
     6763.     99                the_disrupt = the_disrupt && the_last.disrupt;
    +
     6764.     99            }
    +
     6765.    118        } else {
    +
     6766.    118            the_disrupt = false;
    +
     6767.    217        }
    +
     6768.    217        advance("}", the_switch);
    +
     6769.    217        functionage.switch -= 1;
    +
     6770.    217        the_switch.disrupt = the_disrupt;
    +
     6771.    217        return the_switch;
    +
     6772.    217    }
    +
     6773.    631
    +
     6774.     40    function stmt_throw() {
    +
     6775.     40        const the_throw = token_now;
    +
     6776.     40        the_throw.disrupt = true;
    +
     6777.     40        the_throw.expression = parse_expression(10);
    +
     6778.     40        semicolon();
    +
     6779.      1        if (functionage.try > 0) {
    +
     6780.      1
    +
     6781.      1// test_cause:
    +
     6782.      1// ["try{throw 0}catch(){}", "stmt_throw", "unexpected_a", "throw", 5]
    +
     6783.      1
    +
     6784.      1            warn("unexpected_a", the_throw);
    +
     6785.      1        }
    +
     6786.     40        return the_throw;
    +
     6787.     40    }
    +
     6788.    631
    +
     6789.     62    function stmt_try() {
    +
     6790.     62        const the_try = token_now;
    +
     6791.     62        let ignored;
    +
     6792.     62        let the_catch;
    +
     6793.     62        let the_disrupt;
    +
     6794.      1        if (functionage.try > 0) {
    +
     6795.      1
    +
     6796.      1// test_cause:
    +
     6797.      1// ["try{try{}catch(){}}catch(){}", "stmt_try", "unexpected_a", "try", 5]
    +
     6798.      1
    +
     6799.      1            warn("unexpected_a", the_try);
    +
     6800.      1        }
    +
     6801.     62        functionage.try += 1;
    +
     6802.     62        the_try.block = block();
    +
     6803.     62        the_disrupt = the_try.block.disrupt;
    +
     6804.     57        if (token_nxt.id === "catch") {
    +
     6805.     57            ignored = "ignore";
    +
     6806.     57            the_catch = token_nxt;
    +
     6807.     57            the_try.catch = the_catch;
    +
     6808.     57            advance("catch");
    +
     6809.     57
    +
     6810.     57// Create new catch-scope for catch-parameter.
    +
     6811.     57
    +
     6812.     57            catch_stack.push(catchage);
    +
     6813.     57            catchage = the_catch;
    +
     6814.     57            catch_list.push(catchage);
    +
     6815.     57            the_catch.context = empty();
    +
     6816.     57            if (token_nxt.id === "(") {
    +
     6817.     57                advance("(");
    +
     6818.     57                if (!token_nxt.identifier) {
    +
     6819.     57
    +
     6820.     57// test_cause:
    +
     6821.     57// ["try{}catch(){}", "stmt_try", "expected_identifier_a", ")", 12]
    +
     6822.     57
    +
     6823.     57                    return stop("expected_identifier_a");
    +
     6824.     57                }
    +
     6825.     57                if (token_nxt.id !== "ignore") {
    +
     6826.     57                    ignored = undefined;
    +
     6827.     57                    the_catch.name = token_nxt;
    +
     6828.     57                    enroll(token_nxt, "exception", true);
    +
     6829.     57                }
    +
     6830.     57                advance();
    +
     6831.     57                advance(")");
    +
     6832.     57            }
    +
     6833.     57            the_catch.block = block(ignored);
    +
     6834.     57            if (the_catch.block.disrupt !== true) {
    +
     6835.     57                the_disrupt = false;
    +
     6836.     57            }
    +
     6837.     57
    +
     6838.     57// Restore previous catch-scope after catch-block.
    +
     6839.     57
    +
     6840.     57            catchage = catch_stack.pop();
    +
     6841.     57
    +
     6842.     57// PR-404 - Relax warning about missing `catch` in `try...finally` statement.
    +
     6843.     57//
    +
     6844.     57//         } else {
    +
     6845.     57//
    +
     6846.     57// // test_cause:
    +
     6847.     57// // ["try{}finally{break;}", "stmt_try", "expected_a_before_b", "finally", 6]
    +
     6848.     57//
    +
     6849.     57//             warn("expected_a_before_b", token_nxt, "catch", artifact());
    +
     6850.     57
    +
     6851.     58        }
    +
     6852.     58        if (token_nxt.id === "finally") {
    +
     6853.      4            functionage.finally += 1;
    +
     6854.      4            advance("finally");
    +
     6855.      4            the_try.else = block();
    +
     6856.      4            the_disrupt = the_try.else.disrupt;
    +
     6857.      4            functionage.finally -= 1;
    +
     6858.     56        }
    +
     6859.     56        the_try.disrupt = the_disrupt;
    +
     6860.     56        functionage.try -= 1;
    +
     6861.     56        return the_try;
    +
     6862.     56    }
    +
     6863.    631
    +
     6864.   2334    function stmt_var() {
    +
     6865.   2334        let ellipsis;
    +
     6866.   2334        let mode_const;
    +
     6867.   2334        let name;
    +
     6868.   2334        let the_brace;
    +
     6869.   2334        let the_bracket;
    +
     6870.   2334        let the_variable = token_now;
    +
     6871.   2334        let variable_prv;
    +
     6872.   2334        mode_const = the_variable.id === "const";
    +
     6873.   2334        the_variable.names = [];
    +
     6874.   2334
    +
     6875.   2334// A program may use var or let, but not both.
    +
     6876.   2334
    +
     6877.   2058        if (!mode_const) {
    +
     6878.   2058            if (mode_var === undefined) {
    +
     6879.   2058                mode_var = the_variable.id;
    +
     6880.   2058            } else if (the_variable.id !== mode_var) {
    +
     6881.   2058
    +
     6882.   2058// test_cause:
    +
     6883.   2058// ["let aa;var aa", "stmt_var", "expected_a_b", "var", 8]
    +
     6884.   2058
    +
     6885.   2058                warn("expected_a_b", the_variable, mode_var, the_variable.id);
    +
     6886.   2058            }
    +
     6887.   2058        }
    +
     6888.   2334
    +
     6889.   2334// We don't expect to see variables created in switch statements.
    +
     6890.   2334
    +
     6891.      1        if (functionage.switch > 0) {
    +
     6892.      1
    +
     6893.      1// test_cause:
    +
     6894.      1// ["switch(0){case 0:var aa}", "stmt_var", "var_switch", "var", 18]
    +
     6895.      1
    +
     6896.      1            warn("var_switch", the_variable);
    +
     6897.      1        }
    +
     6898.   2334        switch (
    +
     6899.   2334            Boolean(functionage.statement_prv)
    +
     6900.   1449            && functionage.statement_prv.id
    +
     6901.   2334        ) {
    +
     6902.    107        case "const":
    +
     6903.   1437        case "let":
    +
     6904.   1439        case "var":
    +
     6905.   1439
    +
     6906.   1439// test_cause:
    +
     6907.   1439// ["const aa=0;const bb=0;", "stmt_var", "var_prv", "const", 0]
    +
     6908.   1439// ["let aa=0;let bb=0;", "stmt_var", "var_prv", "let", 0]
    +
     6909.   1439// ["var aa=0;var bb=0;", "stmt_var", "var_prv", "var", 0]
    +
     6910.   1439
    +
     6911.   1439            test_cause("var_prv", functionage.statement_prv.id);
    +
     6912.   1439            variable_prv = functionage.statement_prv;
    +
     6913.   1439            break;
    +
     6914.      3        case "import":
    +
     6915.      3
    +
     6916.      3// test_cause:
    +
     6917.      3// ["import aa from \"aa\";\nlet bb=0;", "stmt_var", "import_prv", "", 0]
    +
     6918.      3
    +
     6919.      3            test_cause("import_prv");
    +
     6920.      3            break;
    +
     6921.    885        case false:
    +
     6922.    885            break;
    +
     6923.      7        default:
    +
     6924.      7            if (
    +
     6925.      7                (option_dict.beta && !option_dict.variable)
    +
     6926.      7                || the_variable.id === "var"
    +
     6927.      7            ) {
    +
     6928.      7
    +
     6929.      7// test_cause:
    +
     6930.      7// ["console.log();let aa=0;", "stmt_var", "var_on_top", "let", 15]
    +
     6931.      7// ["console.log();var aa=0;", "stmt_var", "var_on_top", "var", 15]
    +
     6932.      7// ["try{aa();}catch(aa){var aa=0;}", "stmt_var", "var_on_top", "var", 21]
    +
     6933.      7// ["while(0){var aa;}", "stmt_var", "var_on_top", "var", 10]
    +
     6934.      7
    +
     6935.      7                warn("var_on_top", token_now);
    +
     6936.      7            }
    +
     6937.   2334        }
    +
     6938.   2335        while (true) {
    +
     6939.   2335            if (token_nxt.id === "{") {
    +
     6940.   2335                if (the_variable.id === "var") {
    +
     6941.   2335
    +
     6942.   2335// test_cause:
    +
     6943.   2335// ["var{aa}=0", "stmt_var", "unexpected_a", "var", 1]
    +
     6944.   2335
    +
     6945.   2335                    warn("unexpected_a", the_variable);
    +
     6946.   2335                }
    +
     6947.   2335                the_brace = token_nxt;
    +
     6948.   2335                advance("{");
    +
     6949.   2335                while (true) {
    +
     6950.   2335                    name = token_nxt;
    +
     6951.   2335                    if (!name.identifier) {
    +
     6952.   2335
    +
     6953.   2335// test_cause:
    +
     6954.   2335// ["let {0}", "stmt_var", "expected_identifier_a", "0", 6]
    +
     6955.   2335
    +
     6956.   2335                        return stop("expected_identifier_a");
    +
     6957.   2335                    }
    +
     6958.   2335                    survey(name);
    +
     6959.   2335                    advance();
    +
     6960.   2335                    if (token_nxt.id === ":") {
    +
     6961.   2335                        advance(":");
    +
     6962.   2335                        if (!token_nxt.identifier) {
    +
     6963.   2335
    +
     6964.   2335// test_cause:
    +
     6965.   2335// ["let {aa:0}", "stmt_var", "expected_identifier_a", "0", 9]
    +
     6966.   2335// ["let {aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 9]
    +
     6967.   2335
    +
     6968.   2335                            return stop("expected_identifier_a");
    +
     6969.   2335                        }
    +
     6970.   2335
    +
     6971.   2335// PR-363 - Bugfix
    +
     6972.   2335// Add test against false-warning <uninitialized 'bb'> in code
    +
     6973.   2335// '/*jslint node*/\nlet {aa:bb} = {}; bb();'.
    +
     6974.   2335//
    +
     6975.   2335//                         token_nxt.label = name;
    +
     6976.   2335//                         the_variable.names.push(token_nxt);
    +
     6977.   2335//                         enroll(token_nxt, "variable", mode_const);
    +
     6978.   2335
    +
     6979.   2335                        name = token_nxt;
    +
     6980.   2335                        the_variable.names.push(name);
    +
     6981.   2335                        survey(name);
    +
     6982.   2335                        enroll(name, "variable", mode_const);
    +
     6983.   2335
    +
     6984.   2335                        advance();
    +
     6985.   2335                        the_brace.open = true;
    +
     6986.   2335                    } else {
    +
     6987.   2335                        the_variable.names.push(name);
    +
     6988.   2335                        enroll(name, "variable", mode_const);
    +
     6989.   2335                    }
    +
     6990.   2335                    name.dead = false;
    +
     6991.   2335                    name.init = true;
    +
     6992.   2335                    if (token_nxt.id === "=") {
    +
     6993.   2335
    +
     6994.   2335// test_cause:
    +
     6995.   2335// ["let {aa=0}", "stmt_var", "assign", "", 0]
    +
     6996.   2335
    +
     6997.   2335                        test_cause("assign");
    +
     6998.   2335                        advance("=");
    +
     6999.   2335                        name.expression = parse_expression();
    +
     7000.   2335                        the_brace.open = true;
    +
     7001.   2335                    }
    +
     7002.   2335                    if (token_nxt.id !== ",") {
    +
     7003.   2335                        break;
    +
     7004.   2335                    }
    +
     7005.   2335                    advance(",");
    +
     7006.   2335                }
    +
     7007.   2335
    +
     7008.   2335// test_cause:
    +
     7009.   2335// ["let{bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8]
    +
     7010.   2335
    +
     7011.   2335                check_ordered(the_variable.id, the_variable.names);
    +
     7012.   2335                advance("}");
    +
     7013.   2335                advance("=");
    +
     7014.   2335                the_variable.expression = parse_expression(0);
    +
     7015.   2335            } else if (token_nxt.id === "[") {
    +
     7016.   2335                if (the_variable.id === "var") {
    +
     7017.   2335
    +
     7018.   2335// test_cause:
    +
     7019.   2335// ["var[aa]=0", "stmt_var", "unexpected_a", "var", 1]
    +
     7020.   2335
    +
     7021.   2335                    warn("unexpected_a", the_variable);
    +
     7022.   2335                }
    +
     7023.   2335                the_bracket = token_nxt;
    +
     7024.   2335                advance("[");
    +
     7025.   2335                while (true) {
    +
     7026.   2335                    ellipsis = false;
    +
     7027.   2335                    if (token_nxt.id === "...") {
    +
     7028.   2335                        ellipsis = true;
    +
     7029.   2335                        advance("...");
    +
     7030.   2335                    }
    +
     7031.   2335                    if (!token_nxt.identifier) {
    +
     7032.   2335
    +
     7033.   2335// test_cause:
    +
     7034.   2335// ["let[]", "stmt_var", "expected_identifier_a", "]", 5]
    +
     7035.   2335
    +
     7036.   2335                        return stop("expected_identifier_a");
    +
     7037.   2335                    }
    +
     7038.   2335                    name = token_nxt;
    +
     7039.   2335                    advance();
    +
     7040.   2335                    the_variable.names.push(name);
    +
     7041.   2335                    enroll(name, "variable", mode_const);
    +
     7042.   2335                    name.dead = false;
    +
     7043.   2335                    name.init = true;
    +
     7044.   2335                    if (ellipsis) {
    +
     7045.   2335                        name.ellipsis = true;
    +
     7046.   2335                        break;
    +
     7047.   2335                    }
    +
     7048.   2335                    if (token_nxt.id === "=") {
    +
     7049.   2335                        advance("=");
    +
     7050.   2335                        name.expression = parse_expression();
    +
     7051.   2335                        the_bracket.open = true;
    +
     7052.   2335                    }
    +
     7053.   2335                    if (token_nxt.id !== ",") {
    +
     7054.   2335                        break;
    +
     7055.   2335                    }
    +
     7056.   2335                    advance(",");
    +
     7057.   2335                }
    +
     7058.   2335                advance("]");
    +
     7059.   2335                advance("=");
    +
     7060.   2335                the_variable.expression = parse_expression(0);
    +
     7061.   2335            } else if (token_nxt.identifier) {
    +
     7062.   2335                name = token_nxt;
    +
     7063.   2335                advance();
    +
     7064.   2335                if (name.id === "ignore") {
    +
     7065.   2335
    +
     7066.   2335// test_cause:
    +
     7067.   2335// ["
    +
     7068.   2335// let ignore;function aa(ignore) {}
    +
     7069.   2335// ", "stmt_var", "unexpected_a", "ignore", 5]
    +
     7070.   2335
    +
     7071.   2335                    warn("unexpected_a", name);
    +
     7072.   2335                }
    +
     7073.   2335                enroll(name, "variable", mode_const);
    +
     7074.   2335                if (token_nxt.id === "=" || mode_const) {
    +
     7075.   2335                    advance("=");
    +
     7076.   2335                    name.dead = false;
    +
     7077.   2335                    name.init = true;
    +
     7078.   2335                    name.expression = parse_expression(0);
    +
     7079.   2335                }
    +
     7080.   2335                the_variable.names.push(name);
    +
     7081.   2335            } else {
    +
     7082.   2335
    +
     7083.   2335// test_cause:
    +
     7084.   2335// ["let 0", "stmt_var", "expected_identifier_a", "0", 5]
    +
     7085.   2335// ["var{aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 8]
    +
     7086.   2335
    +
     7087.   2335                return stop("expected_identifier_a");
    +
     7088.   2335            }
    +
     7089.   2335            if (token_nxt.id !== ",") {
    +
     7090.   2335                break;
    +
     7091.   2335            }
    +
     7092.   2335
    +
     7093.   2335// test_cause:
    +
     7094.   2335// ["let aa,bb;", "stmt_var", "expected_a_b", ",", 7]
    +
     7095.   2335
    +
     7096.   2335            warn("expected_a_b", token_nxt, ";", ",");
    +
     7097.   2335            advance(",");
    +
     7098.   2335        }
    +
     7099.   2320
    +
     7100.   2320// Warn if variable declarations are unordered.
    +
     7101.   2320
    +
     7102.   2320        if (
    +
     7103.   2320            option_dict.beta
    +
     7104.   2320            && !option_dict.unordered
    +
     7105.   2315            && !option_dict.variable
    +
     7106.   2309            && variable_prv
    +
     7107.   1437            && (
    +
     7108.   1437                variable_prv.id + " " + variable_prv.names[0].id
    +
     7109.   1437                > the_variable.id + " " + the_variable.names[0].id
    +
     7110.   1437            )
    +
     7111.      3        ) {
    +
     7112.      3
    +
     7113.      3// test_cause:
    +
     7114.      3// ["const bb=0;const aa=0;", "stmt_var", "expected_a_b_before_c_d", "aa", 12]
    +
     7115.      3// ["let bb;let aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8]
    +
     7116.      3// ["var bb;var aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8]
    +
     7117.      3
    +
     7118.      3            warn(
    +
     7119.      3                "expected_a_b_before_c_d",
    +
     7120.      3                the_variable,
    +
     7121.      3                the_variable.id,
    +
     7122.      3                the_variable.names[0].id,
    +
     7123.      3                variable_prv.id,
    +
     7124.      3                variable_prv.names[0].id
    +
     7125.      3            );
    +
     7126.   2320        }
    +
     7127.   2320        semicolon();
    +
     7128.   2320        return the_variable;
    +
     7129.   2320    }
    +
     7130.    631
    +
     7131.    208    function stmt_while() {
    +
     7132.    208        const the_while = token_now;
    +
     7133.    208        check_not_top_level(the_while);
    +
     7134.    208        functionage.loop += 1;
    +
     7135.    208        the_while.expression = condition();
    +
     7136.    208        the_while.block = block();
    +
     7137.      1        if (the_while.block.disrupt === true) {
    +
     7138.      1
    +
     7139.      1// test_cause:
    +
     7140.      1// ["function aa(){while(0){break;}}", "stmt_while", "weird_loop", "while", 15]
    +
     7141.      1
    +
     7142.      1            warn("weird_loop", the_while);
    +
     7143.    205        }
    +
     7144.    205        functionage.loop -= 1;
    +
     7145.    205        return the_while;
    +
     7146.    205    }
    +
     7147.    631
    +
     7148.      1    function stmt_with() {
    +
     7149.      1
    +
     7150.      1// test_cause:
    +
     7151.      1// ["with", "stmt_with", "unexpected_a", "with", 1]
    +
     7152.      1
    +
     7153.      1        stop("unexpected_a", token_now);
    +
     7154.      1    }
    +
     7155.    631
    +
     7156.  14600    function survey(name) {
    +
     7157.  14600        let id = name.id;
    +
     7158.  14600
    +
     7159.  14600// Tally the property name. If it is a string, only tally strings that conform
    +
     7160.  14600// to the identifier rules.
    +
     7161.  14600
    +
     7162.    183        if (id === "(string)") {
    +
     7163.    183            id = name.value;
    +
     7164.    183            if (!jslint_rgx_identifier.test(id)) {
    +
     7165.    183                return id;
    +
     7166.    183            }
    +
     7167.  14417        } else if (id === "`") {
    +
     7168.  14417            if (name.value.length === 1) {
    +
     7169.  14417                id = name.value[0].value;
    +
     7170.  14417                if (!jslint_rgx_identifier.test(id)) {
    +
     7171.  14417                    return id;
    +
     7172.  14417                }
    +
     7173.  14417            }
    +
     7174.  14417        } else if (!name.identifier) {
    +
     7175.  14417
    +
     7176.  14417// test_cause:
    +
     7177.  14417// ["let aa={0:0}", "survey", "expected_identifier_a", "0", 9]
    +
     7178.  14417
    +
     7179.  14417            return stop("expected_identifier_a", name);
    +
     7180.  14513        }
    +
     7181.  14513
    +
     7182.  14513// If we have seen this name before, increment its count.
    +
     7183.  14513
    +
     7184.  14513        if (typeof property_dict[id] === "number") {
    +
     7185.  12086            property_dict[id] += 1;
    +
     7186.  12086
    +
     7187.  12086// If this is the first time seeing this property name, and if there is a
    +
     7188.  12086// tenure list, then it must be on the list. Otherwise, it must conform to
    +
     7189.  12086// the rules for good property names.
    +
     7190.  12086
    +
     7191.  12086        } else {
    +
     7192.   2427            if (state.mode_property) {
    +
     7193.   2427                if (tenure[id] !== true) {
    +
     7194.   2427
    +
     7195.   2427// test_cause:
    +
     7196.   2427// ["/*property aa*/\naa.bb", "survey", "unregistered_property_a", "bb", 4]
    +
     7197.   2427
    +
     7198.   2427                    warn("unregistered_property_a", name);
    +
     7199.   2427                }
    +
     7200.   2427            } else if (
    +
     7201.   2427                !option_dict.nomen
    +
     7202.   2427                && name.identifier
    +
     7203.   2427                && jslint_rgx_weird_property.test(id)
    +
     7204.   2427            ) {
    +
     7205.   2427
    +
     7206.   2427// test_cause:
    +
     7207.   2427// ["aa.$", "survey", "weird_property_a", "$", 4]
    +
     7208.   2427// ["aa._", "survey", "weird_property_a", "_", 4]
    +
     7209.   2427// ["aa._aa", "survey", "weird_property_a", "_aa", 4]
    +
     7210.   2427// ["aa.aaSync", "survey", "weird_property_a", "aaSync", 4]
    +
     7211.   2427// ["aa.aa_", "survey", "weird_property_a", "aa_", 4]
    +
     7212.   2427
    +
     7213.   2427                warn("weird_property_a", name);
    +
     7214.   2427            }
    +
     7215.   2427            property_dict[id] = 1;
    +
     7216.  14513        }
    +
     7217.  14513        return id;
    +
     7218.  14513    }
    +
     7219.    631
    +
     7220.    631// These functions are used to specify the grammar of our language:
    +
     7221.    631
    +
     7222.  82030    function symbol(id, bp) {
    +
     7223.  82030
    +
     7224.  82030// Create a symbol if it does not already exist in the language's syntax.
    +
     7225.  82030
    +
     7226.  82030        let the_symbol = syntax_dict[id];
    +
     7227.  71303        if (the_symbol === undefined) {
    +
     7228.  71303            the_symbol = empty();
    +
     7229.  71303            the_symbol.id = id;
    +
     7230.  71303            the_symbol.lbp = bp || 0;
    +
     7231.  71303            syntax_dict[id] = the_symbol;
    +
     7232.  71303        }
    +
     7233.  82030        return the_symbol;
    +
     7234.  82030    }
    +
     7235.    631
    +
     7236.    631    function ternary(id1, id2) {
    +
     7237.    631
    +
     7238.    631// Create a ternary operator.
    +
     7239.    631
    +
     7240.    631        const the_symbol = symbol(id1, 30);
    +
     7241.    213        the_symbol.led_infix = function parse_ternary_led(left) {
    +
     7242.    213            const the_token = token_now;
    +
     7243.    213            let second;
    +
     7244.    213            second = parse_expression(20);
    +
     7245.    213            advance(id2);
    +
     7246.    213            token_now.arity = "ternary";
    +
     7247.    213            the_token.arity = "ternary";
    +
     7248.    213            the_token.expression = [left, second, parse_expression(10)];
    +
     7249.      5            if (token_nxt.id !== ")") {
    +
     7250.      5
    +
     7251.      5// test_cause:
    +
     7252.      5// ["0?0:0", "parse_ternary_led", "use_open", "?", 2]
    +
     7253.      5
    +
     7254.      5                warn("use_open", the_token);
    +
     7255.      5            }
    +
     7256.    213            return the_token;
    +
     7257.    213        };
    +
     7258.    631        return the_symbol;
    +
     7259.    631    }
    +
     7260.    631
    +
     7261.    631// Now we parse JavaScript.
    +
     7262.    631// Begin defining the language.
    +
     7263.    631
    +
     7264.    631    assignment("%=");
    +
     7265.    631    assignment("&=");
    +
     7266.    631    assignment("*=");
    +
     7267.    631    assignment("+=");
    +
     7268.    631    assignment("-=");
    +
     7269.    631    assignment("/=");
    +
     7270.    631    assignment("<<=");
    +
     7271.    631    assignment("=");
    +
     7272.    631    assignment(">>=");
    +
     7273.    631    assignment(">>>=");
    +
     7274.    631    assignment("^=");
    +
     7275.    631    assignment("|=");
    +
     7276.    631    constant("(number)", "number");
    +
     7277.    631    constant("(regexp)", "regexp");
    +
     7278.    631    constant("(string)", "string");
    +
     7279.    631    constant("Function", "function", constant_Function);
    +
     7280.    631    constant("Infinity", "number", Infinity);
    +
     7281.    631    constant("NaN", "number", NaN);
    +
     7282.    631    constant("arguments", "object", constant_arguments);
    +
     7283.    631    constant("eval", "function", constant_eval);
    +
     7284.    631    constant("false", "boolean", false);
    +
     7285.    631    constant("ignore", "undefined", constant_ignore);
    +
     7286.    631    constant("isFinite", "function", constant_isInfinite);
    +
     7287.    631    constant("isNaN", "function", constant_isNaN);
    +
     7288.    631    constant("null", "null", null);
    +
     7289.    631    constant("this", "object", constant_this);
    +
     7290.    631    constant("true", "boolean", true);
    +
     7291.    631    constant("undefined", "undefined");
    +
     7292.    631    infix(100, "!=");
    +
     7293.    631    infix(100, "!==");
    +
     7294.    631    infix(100, "==");
    +
     7295.    631    infix(100, "===");
    +
     7296.    631    infix(110, "<");
    +
     7297.    631    infix(110, "<=");
    +
     7298.    631    infix(110, ">");
    +
     7299.    631    infix(110, ">=");
    +
     7300.    631    infix(110, "in");
    +
     7301.    631    infix(110, "instanceof");
    +
     7302.    631    infix(120, "<<");
    +
     7303.    631    infix(120, ">>");
    +
     7304.    631    infix(120, ">>>");
    +
     7305.    631    infix(130, "+");
    +
     7306.    631    infix(130, "-");
    +
     7307.    631    infix(140, "%");
    +
     7308.    631    infix(140, "*");
    +
     7309.    631    infix(140, "/");
    +
     7310.    631    infix(160, "(", infix_lparen);
    +
     7311.    631    infix(160, "`", infix_grave);
    +
     7312.    631    infix(170, ".", infix_dot);
    +
     7313.    631    infix(170, "=>", infix_fart_unwrapped);
    +
     7314.    631    infix(170, "?.", infix_option_chain);
    +
     7315.    631    infix(170, "[", infix_lbracket);
    +
     7316.    631    infix(35, "??");
    +
     7317.    631    infix(40, "||");
    +
     7318.    631    infix(50, "&&");
    +
     7319.    631    infix(70, "|");
    +
     7320.    631    infix(80, "^");
    +
     7321.    631    infix(90, "&");
    +
     7322.    631    infixr(150, "**");
    +
     7323.    631    postassign("++");
    +
     7324.    631    postassign("--");
    +
     7325.    631    preassign("++");
    +
     7326.    631    preassign("--");
    +
     7327.    631    prefix("!!");
    +
     7328.    631    prefix("!");
    +
     7329.    631    prefix("(", prefix_lparen);
    +
     7330.    631    prefix("+");
    +
     7331.    631    prefix("-");
    +
     7332.    631    prefix("/=", prefix_assign_divide);
    +
     7333.    631    prefix("=>", prefix_fart);
    +
     7334.    631    prefix("[", prefix_lbracket);
    +
     7335.    631    prefix("`", prefix_tick);
    +
     7336.    631    prefix("async", prefix_async);
    +
     7337.    631    prefix("await", prefix_await);
    +
     7338.    631    prefix("function", prefix_function);
    +
     7339.    631    prefix("new", prefix_new);
    +
     7340.    631    prefix("typeof");
    +
     7341.    631    prefix("void", prefix_void);
    +
     7342.    631    prefix("{", prefix_lbrace);
    +
     7343.    631    prefix("~");
    +
     7344.    631    stmt(";", stmt_semicolon);
    +
     7345.    631    stmt("async", prefix_async);
    +
     7346.    631    stmt("await", prefix_await);
    +
     7347.    631    stmt("break", stmt_break);
    +
     7348.    631    stmt("const", stmt_var);
    +
     7349.    631    stmt("continue", stmt_continue);
    +
     7350.    631    stmt("debugger", stmt_debugger);
    +
     7351.    631    stmt("delete", stmt_delete);
    +
     7352.    631    stmt("do", stmt_do);
    +
     7353.    631    stmt("export", stmt_export);
    +
     7354.    631    stmt("for", stmt_for);
    +
     7355.    631    stmt("function", prefix_function);
    +
     7356.    631    stmt("if", stmt_if);
    +
     7357.    631    stmt("import", stmt_import);
    +
     7358.    631    stmt("let", stmt_var);
    +
     7359.    631    stmt("return", stmt_return);
    +
     7360.    631    stmt("switch", stmt_switch);
    +
     7361.    631    stmt("throw", stmt_throw);
    +
     7362.    631    stmt("try", stmt_try);
    +
     7363.    631    stmt("var", stmt_var);
    +
     7364.    631    stmt("while", stmt_while);
    +
     7365.    631    stmt("with", stmt_with);
    +
     7366.    631    stmt("{", stmt_lbrace);
    +
     7367.    631    symbol(")");
    +
     7368.    631    symbol("*/");
    +
     7369.    631    symbol(",");
    +
     7370.    631    symbol(":");
    +
     7371.    631    symbol(";");
    +
     7372.    631    symbol("]");
    +
     7373.    631    symbol("async");
    +
     7374.    631    symbol("await");
    +
     7375.    631    symbol("case");
    +
     7376.    631    symbol("catch");
    +
     7377.    631    symbol("class");
    +
     7378.    631    symbol("default");
    +
     7379.    631    symbol("else");
    +
     7380.    631    symbol("enum");
    +
     7381.    631    symbol("finally");
    +
     7382.    631    symbol("implements");
    +
     7383.    631    symbol("interface");
    +
     7384.    631    symbol("package");
    +
     7385.    631    symbol("private");
    +
     7386.    631    symbol("protected");
    +
     7387.    631    symbol("public");
    +
     7388.    631    symbol("static");
    +
     7389.    631    symbol("super");
    +
     7390.    631    symbol("void");
    +
     7391.    631    symbol("yield");
    +
     7392.    631    symbol("}");
    +
     7393.    631    ternary("?", ":");
    +
     7394.    631
    +
     7395.    631// Init token_nxt.
    +
     7396.    631
    +
     7397.    631    advance();
    +
     7398.    631
    +
     7399.    631// Parsing of JSON is simple:
    +
     7400.    631
    +
     7401.     25    if (state.mode_json) {
    +
     7402.     25        state.token_tree = parse_json();
    +
     7403.     25        advance("(end)");
    +
     7404.     25        return;
    +
     7405.    606    }
    +
     7406.    606
    +
     7407.    606// Because browsers encourage combining of script files, the first token might
    +
     7408.    606// be a semicolon to defend against a missing semicolon in the preceding file.
    +
     7409.    606
    +
     7410.    606    if (option_dict.browser) {
    +
     7411.      5        if (token_nxt.id === ";") {
    +
     7412.      5            advance(";");
    +
     7413.      5        }
    +
     7414.      5
    +
     7415.      5// If we are not in a browser, then the file form of strict pragma may be used.
    +
     7416.      5
    +
     7417.    601    } else if (token_nxt.value === "use strict") {
    +
     7418.    601        advance("(string)");
    +
     7419.    601        advance(";");
    +
     7420.    606    }
    +
     7421.    606    state.token_tree = parse_statements();
    +
     7422.    606    advance("(end)");
    +
     7423.    606
    +
     7424.    606// Check global functions are ordered.
    +
     7425.    606
    +
     7426.    606    check_ordered(
    +
     7427.    606        "function",
    +
     7428.   2001        function_list.map(function ({
    +
     7429.   2001            level,
    +
     7430.   2001            name
    +
     7431.   2001        }) {
    +
     7432.    606            return (level === 1) && name;
    +
     7433.   2001        }).filter(function (name) {
    +
     7434.   1992            return option_dict.beta && name && name.id;
    +
     7435.   2001        })
    +
     7436.    606    );
    +
     7437.    606}
    +
     7438.      1
    +
     7439.    518function jslint_phase4_walk(state) {
    +
     7440.    518
    +
     7441.    518// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
    +
     7442.    518//          recursive traversal. Each node may be processed on the way down
    +
     7443.    518//          (preaction) and on the way up (postaction).
    +
     7444.    518
    +
     7445.    518    let {
    +
     7446.    518        artifact,
    +
     7447.    518        catch_stack,
    +
     7448.    518        function_stack,
    +
     7449.    518        global_dict,
    +
     7450.    518        is_equal,
    +
     7451.    518        is_weird,
    +
     7452.    518        option_dict,
    +
     7453.    518        syntax_dict,
    +
     7454.    518        test_cause,
    +
     7455.    518        token_global,
    +
     7456.    518        warn
    +
     7457.    518    } = state;
    +
     7458.    518    let block_stack = [];               // The stack of blocks.
    +
     7459.    518    let blockage = token_global;        // The current block.
    +
     7460.    518    let catchage = catch_stack[0];      // The current catch-block.
    +
     7461.    518    let functionage = token_global;     // The current function.
    +
     7462.    518    let postaction;
    +
     7463.    518    let postamble;
    +
     7464.    518    let posts = empty();
    +
     7465.    518    let preaction;
    +
     7466.    518    let preamble;
    +
     7467.    518    let pres = empty();
    +
     7468.    518
    +
     7469.    518// The relational operators.
    +
     7470.    518
    +
     7471.    518    let relationop = object_assign_from_list(empty(), [
    +
     7472.    518        "!=", "!==", "<", "<=", "==", "===", ">", ">="
    +
     7473.    518    ], true);
    +
     7474.    518
    +
     7475.    518// Ambulation of the parse tree.
    +
     7476.    518
    +
     7477.   1036    function action(when) {
    +
     7478.   1036
    +
     7479.   1036// Produce a function that will register task functions that will be called as
    +
     7480.   1036// the tree is traversed.
    +
     7481.   1036
    +
     7482.  19684        return function (arity, id, task) {
    +
     7483.  19684            let a_set = when[arity];
    +
     7484.  19684            let i_set;
    +
     7485.  19684
    +
     7486.  19684// The id parameter is optional. If excluded, the task will be applied to all
    +
     7487.  19684// ids.
    +
     7488.  19684
    +
     7489.   4144            if (typeof id !== "string") {
    +
     7490.   4144                task = id;
    +
     7491.   4144                id = "(all)";
    +
     7492.   4144            }
    +
     7493.  19684
    +
     7494.  19684// If this arity has no registrations yet, then create a set object to hold
    +
     7495.  19684// them.
    +
     7496.  19684
    +
     7497.   5180            if (a_set === undefined) {
    +
     7498.   5180                a_set = empty();
    +
     7499.   5180                when[arity] = a_set;
    +
     7500.   5180            }
    +
     7501.  19684
    +
     7502.  19684// If this id has no registrations yet, then create a set array to hold them.
    +
     7503.  19684
    +
     7504.  19684            i_set = a_set[id];
    +
     7505.  19166            if (i_set === undefined) {
    +
     7506.  19166                i_set = [];
    +
     7507.  19166                a_set[id] = i_set;
    +
     7508.  19166            }
    +
     7509.  19684
    +
     7510.  19684// Register the task with the arity and the id.
    +
     7511.  19684
    +
     7512.  19684            i_set.push(task);
    +
     7513.  19684        };
    +
     7514.   1036    }
    +
     7515.    518
    +
     7516.   1036    function amble(when) {
    +
     7517.   1036
    +
     7518.   1036// Produce a function that will act on the tasks registered by an action
    +
     7519.   1036// function while walking the tree.
    +
     7520.   1036
    +
     7521. 210942        return function (the_token) {
    +
     7522. 210942
    +
     7523. 210942// Given a task set that was built by an action function, run all
    +
     7524. 210942// relevant tasks on the token.
    +
     7525. 210942
    +
     7526. 210942            let a_set = when[the_token.arity];
    +
     7527. 210942            let i_set;
    +
     7528. 210942
    +
     7529. 210942// If there are tasks associated with the token's arity...
    +
     7530. 210942
    +
     7531. 144702            if (a_set !== undefined) {
    +
     7532. 144702
    +
     7533. 144702// If there are tasks associated with the token's id...
    +
     7534. 144702
    +
     7535. 144702                i_set = a_set[the_token.id];
    +
     7536. 144702                if (i_set !== undefined) {
    +
     7537. 144702                    i_set.forEach(function (task) {
    +
     7538. 144702                        task(the_token);
    +
     7539. 144702                    });
    +
     7540. 144702                }
    +
     7541. 144702
    +
     7542. 144702// If there are tasks for all ids.
    +
     7543. 144702
    +
     7544. 144702                i_set = a_set["(all)"];
    +
     7545. 144702                if (i_set !== undefined) {
    +
     7546. 144702                    i_set.forEach(function (task) {
    +
     7547. 144702                        task(the_token);
    +
     7548. 144702                    });
    +
     7549. 144702                }
    +
     7550. 144702            }
    +
     7551. 210942        };
    +
     7552.   1036    }
    +
     7553.    518
    +
     7554.   2818    function init_variable(name) {
    +
     7555.   2818        let the_variable = lookup(name);
    +
     7556.   2759        if (!the_variable || the_variable.readonly) {
    +
     7557.     61            warn("bad_assignment_a", name);
    +
     7558.     61            return;
    +
     7559.   2757        }
    +
     7560.   2757        the_variable.init = true;
    +
     7561.   2757    }
    +
     7562.    518
    +
     7563.  31509    function lookup(thing) {
    +
     7564.  31509        let id = thing.id;
    +
     7565.  31509        let the_variable;
    +
     7566.      1        if (thing.arity !== "variable") {
    +
     7567.      1            return;
    +
     7568.  31508        }
    +
     7569.  31508
    +
     7570.  31508// Look up the variable in the current context.
    +
     7571.  31508
    +
     7572.  31508        the_variable = functionage.context[id] || catchage.context[id];
    +
     7573.  31509
    +
     7574.  31509// If it isn't local, search all the other contexts. If there are name
    +
     7575.  31509// collisions, take the most recent.
    +
     7576.  31509
    +
     7577.  24629        if (the_variable && the_variable.role === "label") {
    +
     7578.      1
    +
     7579.      1// test_cause:
    +
     7580.      1// ["aa:while(0){aa;}", "lookup", "label_a", "aa", 13]
    +
     7581.      1
    +
     7582.      1            warn("label_a", thing);
    +
     7583.      1            return the_variable;
    +
     7584.  31507        }
    +
     7585.  31507        if (!the_variable) {
    +
     7586.  14775            function_stack.forEach(function ({
    +
     7587.  14775                context
    +
     7588.  14775            }) {
    +
     7589.   6879                if (context[id] && context[id].role !== "label") {
    +
     7590.   6879                    the_variable = context[id];
    +
     7591.   6879                }
    +
     7592.  14775            });
    +
     7593.   6879
    +
     7594.   6879// If it isn't in any of those either, perhaps it is a predefined global.
    +
     7595.   6879// If so, add it to the global context.
    +
     7596.   6879
    +
     7597.   6879            if (!the_variable && global_dict[id] === undefined) {
    +
     7598.   6879
    +
     7599.   6879// test_cause:
    +
     7600.   6879// ["aa", "lookup", "undeclared_a", "aa", 1]
    +
     7601.   6879// ["class aa{}", "lookup", "undeclared_a", "aa", 7]
    +
     7602.   6879// ["
    +
     7603.   6879// let aa=0;try{aa();}catch(bb){bb();}bb();
    +
     7604.   6879// ", "lookup", "undeclared_a", "bb", 36]
    +
     7605.   6879// ["
    +
     7606.   6879// let aa=0;try{aa();}catch(ignore){bb();}
    +
     7607.   6879// ", "lookup", "undeclared_a", "bb", 34]
    +
     7608.   6879
    +
     7609.   6879                warn("undeclared_a", thing);
    +
     7610.   6879                return;
    +
     7611.   6879            }
    +
     7612.   6879            if (!the_variable) {
    +
     7613.   6879                the_variable = {
    +
     7614.   6879                    dead: false,
    +
     7615.   6879                    id,
    +
     7616.   6879                    init: true,
    +
     7617.   6879                    parent: token_global,
    +
     7618.   6879                    readonly: true,
    +
     7619.   6879                    role: "variable",
    +
     7620.   6879                    used: 0
    +
     7621.   6879                };
    +
     7622.   6879                token_global.context[id] = the_variable;
    +
     7623.   6879            }
    +
     7624.   6879            the_variable.closure = true;
    +
     7625.   6879            functionage.context[id] = the_variable;
    +
     7626.  31370        }
    +
     7627.  31370        if (
    +
     7628.  31370            (
    +
     7629.  31370                the_variable.calls === undefined
    +
     7630.  31370                || functionage.name === undefined
    +
     7631.   4364                || the_variable.calls[functionage.name.id] === undefined
    +
     7632.  31509            )
    +
     7633.  31185            && the_variable.dead
    +
     7634.      3        ) {
    +
     7635.      3
    +
     7636.      3// test_cause:
    +
     7637.      3// ["let aa;if(aa){let bb;}bb;", "lookup", "out_of_scope_a", "bb", 23]
    +
     7638.      3
    +
     7639.      3            warn("out_of_scope_a", thing);
    +
     7640.  31370        }
    +
     7641.  31370        return the_variable;
    +
     7642.  31370    }
    +
     7643.    518
    +
     7644.   5009    function post_a(thing) {
    +
     7645.   5009
    +
     7646.   5009// Assignment using = sets the init property of a variable. No other assignment
    +
     7647.   5009// operator can do this. A = token keeps that variable (or array of variables
    +
     7648.   5009// in case of destructuring) in its name property.
    +
     7649.   5009
    +
     7650.   5009        const lvalue = thing.expression[0];
    +
     7651.   5009        let right;
    +
     7652.   4234        if (thing.id === "=") {
    +
     7653.   4234            if (thing.names !== undefined) {
    +
     7654.   4234
    +
     7655.   4234// test_cause:
    +
     7656.   4234// ["if(0){aa=0}", "post_a", "=", "", 0]
    +
     7657.   4234
    +
     7658.   4234                test_cause("=");
    +
     7659.   4234
    +
     7660.   4234// Probably deadcode.
    +
     7661.   4234// if (Array.isArray(thing.names)) {
    +
     7662.   4234//     thing.names.forEach(init_variable);
    +
     7663.   4234// } else {
    +
     7664.   4234//     init_variable(thing.names);
    +
     7665.   4234// }
    +
     7666.   4234
    +
     7667.   4234                jslint_assert(
    +
     7668.   4234                    !Array.isArray(thing.names),
    +
     7669.   4234                    `Expected !Array.isArray(thing.names).`
    +
     7670.   4234                );
    +
     7671.   4234                init_variable(thing.names);
    +
     7672.   4234            } else {
    +
     7673.   4234                if (lvalue.id === "[" || lvalue.id === "{") {
    +
     7674.   4234                    lvalue.expression.forEach(function (thing) {
    +
     7675.   4234                        if (thing.variable) {
    +
     7676.   4234                            thing.variable.init = true;
    +
     7677.   4234                        }
    +
     7678.   4234                    });
    +
     7679.   4234                } else if (
    +
     7680.   4234                    lvalue.id === "."
    +
     7681.   4234                    && thing.expression[1].id === "undefined"
    +
     7682.   4234                ) {
    +
     7683.   4234
    +
     7684.   4234// test_cause:
    +
     7685.   4234// ["aa.aa=undefined", "post_a", "expected_a_b", "undefined", 1]
    +
     7686.   4234
    +
     7687.   4234                    warn(
    +
     7688.   4234                        "expected_a_b",
    +
     7689.   4234                        lvalue.expression,
    +
     7690.   4234                        "delete",
    +
     7691.   4234                        "undefined"
    +
     7692.   4234                    );
    +
     7693.   4234                }
    +
     7694.   4234            }
    +
     7695.   4234        } else {
    +
     7696.    775            if (lvalue.arity === "variable") {
    +
     7697.    775                if (!lvalue.variable || lvalue.variable.readonly) {
    +
     7698.    775                    warn("bad_assignment_a", lvalue);
    +
     7699.    775                }
    +
     7700.    775            }
    +
     7701.    775            right = syntax_dict[thing.expression[1].id];
    +
     7702.    775            if (
    +
     7703.    775                right !== undefined
    +
     7704.    775                && (
    +
     7705.    775                    right.id === "function"
    +
     7706.    775                    || right.id === "=>"
    +
     7707.    775                    || (
    +
     7708.    775                        right.constant
    +
     7709.    775                        && right.id !== "(number)"
    +
     7710.    775                        && (right.id !== "(string)" || thing.id !== "+=")
    +
     7711.    775                    )
    +
     7712.    775                )
    +
     7713.    775            ) {
    +
     7714.    775
    +
     7715.    775// test_cause:
    +
     7716.    775// ["aa+=undefined", "post_a", "unexpected_a", "undefined", 5]
    +
     7717.    775
    +
     7718.    775                warn("unexpected_a", thing.expression[1]);
    +
     7719.    775            }
    +
     7720.    775        }
    +
     7721.   5009    }
    +
     7722.    518
    +
     7723.    706    function post_a_pluseq(thing) {
    +
     7724.    706        const right = thing.expression[1];
    +
     7725.    396        if (right.constant) {
    +
     7726.    396            if (
    +
     7727.    396                right.value === ""
    +
     7728.    396                || (right.id === "(number)" && right.value === "0")
    +
     7729.    396                || right.id === "(boolean)"
    +
     7730.    396                || right.id === "null"
    +
     7731.    396                || right.id === "undefined"
    +
     7732.    396                || Number.isNaN(right.value)
    +
     7733.    396            ) {
    +
     7734.    396                warn("unexpected_a", right);
    +
     7735.    396            }
    +
     7736.    396        }
    +
     7737.    706    }
    +
     7738.    518
    +
     7739.  31933    function post_b(thing) {
    +
     7740.  31933        let right;
    +
     7741.   3960        if (relationop[thing.id]) {
    +
     7742.   3960            if (
    +
     7743.   3960                is_weird(thing.expression[0])
    +
     7744.   3960                || is_weird(thing.expression[1])
    +
     7745.   3960                || is_equal(thing.expression[0], thing.expression[1])
    +
     7746.   3960                || (
    +
     7747.   3960                    thing.expression[0].constant === true
    +
     7748.   3960                    && thing.expression[1].constant === true
    +
     7749.   3960                )
    +
     7750.   3960            ) {
    +
     7751.   3960
    +
     7752.   3960// test_cause:
    +
     7753.   3960// ["if(0===0){0}", "post_b", "weird_relation_a", "===", 5]
    +
     7754.   3960
    +
     7755.   3960                warn("weird_relation_a", thing);
    +
     7756.   3960            }
    +
     7757.   3960        }
    +
     7758.   2023        if (thing.id === "+") {
    +
     7759.   2023            if (!option_dict.convert) {
    +
     7760.   2023                if (thing.expression[0].value === "") {
    +
     7761.   2023
    +
     7762.   2023// test_cause:
    +
     7763.   2023// ["\"\"+0", "post_b", "expected_a_b", "\"\" +", 3]
    +
     7764.   2023
    +
     7765.   2023                    warn("expected_a_b", thing, "String(...)", "\"\" +");
    +
     7766.   2023                } else if (thing.expression[1].value === "") {
    +
     7767.   2023
    +
     7768.   2023// test_cause:
    +
     7769.   2023// ["0+\"\"", "post_b", "expected_a_b", "+ \"\"", 2]
    +
     7770.   2023
    +
     7771.   2023                    warn("expected_a_b", thing, "String(...)", "+ \"\"");
    +
     7772.   2023                }
    +
     7773.   2023            }
    +
     7774.  29910        } else if (thing.id === "[") {
    +
     7775.  29910            if (thing.expression[0].id === "window") {
    +
     7776.  29910
    +
     7777.  29910// test_cause:
    +
     7778.  29910// ["aa=window[0]", "post_b", "weird_expression_a", "window[...]", 10]
    +
     7779.  29910
    +
     7780.  29910                warn("weird_expression_a", thing, "window[...]");
    +
     7781.  29910            }
    +
     7782.  29910            if (thing.expression[0].id === "self") {
    +
     7783.  29910
    +
     7784.  29910// test_cause:
    +
     7785.  29910// ["aa=self[0]", "post_b", "weird_expression_a", "self[...]", 8]
    +
     7786.  29910
    +
     7787.  29910                warn("weird_expression_a", thing, "self[...]");
    +
     7788.  29910            }
    +
     7789.  29910        } else if (thing.id === "." || thing.id === "?.") {
    +
     7790.  29910            if (thing.expression.id === "RegExp") {
    +
     7791.  29910
    +
     7792.  29910// test_cause:
    +
     7793.  29910// ["aa=RegExp.aa", "post_b", "weird_expression_a", ".", 10]
    +
     7794.  29910
    +
     7795.  29910                warn("weird_expression_a", thing);
    +
     7796.  29910            }
    +
     7797.  29910        } else if (thing.id !== "=>" && thing.id !== "(") {
    +
     7798.  29910            right = thing.expression[1];
    +
     7799.  29910            if (
    +
     7800.  29910                (thing.id === "+" || thing.id === "-")
    +
     7801.  29910                && right.id === thing.id
    +
     7802.  29910                && right.arity === "unary"
    +
     7803.  29910                && !right.wrapped
    +
     7804.  29910            ) {
    +
     7805.  29910
    +
     7806.  29910// test_cause:
    +
     7807.  29910// ["0- -0", "post_b", "wrap_unary", "-", 4]
    +
     7808.  29910
    +
     7809.  29910                warn("wrap_unary", right);
    +
     7810.  29910            }
    +
     7811.  29910            if (
    +
     7812.  29910                thing.expression[0].constant === true
    +
     7813.  29910                && right.constant === true
    +
     7814.  29910            ) {
    +
     7815.  29910                thing.constant = true;
    +
     7816.  29910            }
    +
     7817.  29910        }
    +
     7818.  31933    }
    +
     7819.    518
    +
     7820.   1202    function post_b_and(thing) {
    +
     7821.   1202        if (
    +
     7822.   1202            is_weird(thing.expression[0])
    +
     7823.   1197            || is_equal(thing.expression[0], thing.expression[1])
    +
     7824.   1183            || thing.expression[0].constant === true
    +
     7825.   1181            || thing.expression[1].constant === true
    +
     7826.     21        ) {
    +
     7827.     21
    +
     7828.     21// test_cause:
    +
     7829.     21// ["aa=(()=>0)&&(()=>0)", "post_b_and", "weird_condition_a", "&&", 11]
    +
     7830.     21// ["aa=(``?``:``)&&(``?``:``)", "post_b_and", "weird_condition_a", "&&", 14]
    +
     7831.     21// ["aa=/./&&/./", "post_b_and", "weird_condition_a", "&&", 7]
    +
     7832.     21// ["aa=0&&0", "post_b_and", "weird_condition_a", "&&", 5]
    +
     7833.     21// ["aa=[]&&[]", "post_b_and", "weird_condition_a", "&&", 6]
    +
     7834.     21// ["aa=`${0}`&&`${0}`", "post_b_and", "weird_condition_a", "&&", 10]
    +
     7835.     21// ["
    +
     7836.     21// aa=function aa(){}&&function aa(){}
    +
     7837.     21// ", "post_b_and", "weird_condition_a", "&&", 19]
    +
     7838.     21// ["aa={}&&{}", "post_b_and", "weird_condition_a", "&&", 6]
    +
     7839.     21
    +
     7840.     21            warn("weird_condition_a", thing);
    +
     7841.     21        }
    +
     7842.   1202    }
    +
     7843.    518
    +
     7844.   1460    function post_b_lbracket(thing) {
    +
     7845.      1        if (thing.expression[0].id === "RegExp") {
    +
     7846.      1
    +
     7847.      1// test_cause:
    +
     7848.      1// ["aa=RegExp[0]", "post_b_lbracket", "weird_expression_a", "[", 10]
    +
     7849.      1
    +
     7850.      1            warn("weird_expression_a", thing);
    +
     7851.      1        }
    +
     7852.      1        if (is_weird(thing.expression[1])) {
    +
     7853.      1
    +
     7854.      1// test_cause:
    +
     7855.      1// ["aa[[0]]", "post_b_lbracket", "weird_expression_a", "[", 4]
    +
     7856.      1
    +
     7857.      1            warn("weird_expression_a", thing.expression[1]);
    +
     7858.      1        }
    +
     7859.   1460    }
    +
     7860.    518
    +
     7861.  10411    function post_b_lparen(thing) {
    +
     7862.  10411        let arg;
    +
     7863.  10411        let array;
    +
     7864.  10411        let cack;
    +
     7865.  10411        let left = thing.expression[0];
    +
     7866.  10411        let new_date;
    +
     7867.  10411        let paren;
    +
     7868.  10411        let the_new;
    +
     7869.    154        if (left.id === "new") {
    +
     7870.    154            the_new = left;
    +
     7871.    154            left = left.expression;
    +
     7872.    154        }
    +
     7873.     37        if (left.id === "function") {
    +
     7874.     37            if (!thing.wrapped) {
    +
     7875.     37
    +
     7876.     37// test_cause:
    +
     7877.     37// ["aa=function(){}()", "post_b_lparen", "wrap_immediate", "(", 16]
    +
     7878.     37
    +
     7879.     37                warn("wrap_immediate", thing);
    +
     7880.     37            }
    +
     7881.  10374        } else if (left.identifier) {
    +
     7882.  10374            if (the_new !== undefined) {
    +
     7883.  10374                if (
    +
     7884.  10374                    left.id[0] > "Z"
    +
     7885.  10374                    || left.id === "BigInt"
    +
     7886.  10374                    || left.id === "Boolean"
    +
     7887.  10374                    || left.id === "Number"
    +
     7888.  10374                    || left.id === "String"
    +
     7889.  10374                    || left.id === "Symbol"
    +
     7890.  10374                ) {
    +
     7891.  10374
    +
     7892.  10374// test_cause:
    +
     7893.  10374// ["new BigInt()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7894.  10374// ["new Boolean()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7895.  10374// ["new Number()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7896.  10374// ["new String()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7897.  10374// ["new Symbol()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7898.  10374// ["new aa()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7899.  10374
    +
     7900.  10374                    warn("unexpected_a", the_new);
    +
     7901.  10374                } else if (left.id === "Function") {
    +
     7902.  10374                    if (!option_dict.eval) {
    +
     7903.  10374
    +
     7904.  10374// test_cause:
    +
     7905.  10374// ["new Function()", "post_b_lparen", "unexpected_a", "new Function", 5]
    +
     7906.  10374
    +
     7907.  10374                        warn("unexpected_a", left, "new Function");
    +
     7908.  10374                    }
    +
     7909.  10374                } else if (left.id === "Array") {
    +
     7910.  10374                    arg = thing.expression;
    +
     7911.  10374                    if (arg.length !== 2 || arg[1].id === "(string)") {
    +
     7912.  10374
    +
     7913.  10374// test_cause:
    +
     7914.  10374// ["new Array()", "post_b_lparen", "expected_a_b", "new Array", 5]
    +
     7915.  10374
    +
     7916.  10374                        warn("expected_a_b", left, "[]", "new Array");
    +
     7917.  10374                    }
    +
     7918.  10374                } else if (left.id === "Object") {
    +
     7919.  10374
    +
     7920.  10374// test_cause:
    +
     7921.  10374// ["new Object()", "post_b_lparen", "expected_a_b", "new Object", 5]
    +
     7922.  10374
    +
     7923.  10374                    warn(
    +
     7924.  10374                        "expected_a_b",
    +
     7925.  10374                        left,
    +
     7926.  10374                        "Object.create(null)",
    +
     7927.  10374                        "new Object"
    +
     7928.  10374                    );
    +
     7929.  10374                }
    +
     7930.  10374            } else {
    +
     7931.  10374                if (
    +
     7932.  10374                    left.id[0] >= "A"
    +
     7933.  10374                    && left.id[0] <= "Z"
    +
     7934.  10374                    && left.id !== "BigInt"
    +
     7935.  10374                    && left.id !== "Boolean"
    +
     7936.  10374                    && left.id !== "Number"
    +
     7937.  10374                    && left.id !== "String"
    +
     7938.  10374                    && left.id !== "Symbol"
    +
     7939.  10374                ) {
    +
     7940.  10374
    +
     7941.  10374// test_cause:
    +
     7942.  10374// ["let Aa=Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8]
    +
     7943.  10374
    +
     7944.  10374                    warn("expected_a_before_b", left, "new", artifact(left));
    +
     7945.  10374                }
    +
     7946.  10374            }
    +
     7947.  10374        } else if (left.id === ".") {
    +
     7948.  10374            cack = the_new !== undefined;
    +
     7949.  10374            if (left.expression.id === "Date" && left.name.id === "UTC") {
    +
     7950.  10374
    +
     7951.  10374// test_cause:
    +
     7952.  10374// ["new Date.UTC()", "post_b_lparen", "cack", "", 0]
    +
     7953.  10374
    +
     7954.  10374                test_cause("cack");
    +
     7955.  10374                cack = !cack;
    +
     7956.  10374            }
    +
     7957.  10374            if (jslint_rgx_cap.test(left.name.id) !== cack) {
    +
     7958.  10374                if (the_new !== undefined) {
    +
     7959.  10374
    +
     7960.  10374// test_cause:
    +
     7961.  10374// ["new Date.UTC()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7962.  10374
    +
     7963.  10374                    warn("unexpected_a", the_new);
    +
     7964.  10374                } else {
    +
     7965.  10374
    +
     7966.  10374// test_cause:
    +
     7967.  10374// ["let Aa=Aa.Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8]
    +
     7968.  10374
    +
     7969.  10374                    warn(
    +
     7970.  10374                        "expected_a_before_b",
    +
     7971.  10374                        left.expression,
    +
     7972.  10374                        "new",
    +
     7973.  10374                        left.name.id
    +
     7974.  10374                    );
    +
     7975.  10374                }
    +
     7976.  10374            }
    +
     7977.  10374            if (left.name.id === "getTime") {
    +
     7978.  10374                paren = left.expression;
    +
     7979.  10374                if (paren.id === "(") {
    +
     7980.  10374                    array = paren.expression;
    +
     7981.  10374                    if (array.length === 1) {
    +
     7982.  10374                        new_date = array[0];
    +
     7983.  10374                        if (
    +
     7984.  10374                            new_date.id === "new"
    +
     7985.  10374                            && new_date.expression.id === "Date"
    +
     7986.  10374                        ) {
    +
     7987.  10374
    +
     7988.  10374// test_cause:
    +
     7989.  10374// ["
    +
     7990.  10374// new Date().getTime()
    +
     7991.  10374// ", "post_b_lparen", "expected_a_b", "new Date().getTime()", 1]
    +
     7992.  10374
    +
     7993.  10374                            warn(
    +
     7994.  10374                                "expected_a_b",
    +
     7995.  10374                                new_date,
    +
     7996.  10374                                "Date.now()",
    +
     7997.  10374                                "new Date().getTime()"
    +
     7998.  10374                            );
    +
     7999.  10374                        }
    +
     8000.  10374                    }
    +
     8001.  10374                }
    +
     8002.  10374            }
    +
     8003.  10374        }
    +
     8004.  10411    }
    +
     8005.    518
    +
     8006.    974    function post_b_or(thing) {
    +
     8007.    974        if (
    +
     8008.    974            is_weird(thing.expression[0])
    +
     8009.    974            || is_equal(thing.expression[0], thing.expression[1])
    +
     8010.    973            || thing.expression[0].constant === true
    +
     8011.      2        ) {
    +
     8012.      2
    +
     8013.      2// test_cause:
    +
     8014.      2// ["aa=0||0", "post_b_or", "weird_condition_a", "||", 5]
    +
     8015.      2
    +
     8016.      2            warn("weird_condition_a", thing);
    +
     8017.      2        }
    +
     8018.    974    }
    +
     8019.    518
    +
     8020.     78    function post_s_export(the_thing) {
    +
     8021.     78
    +
     8022.     78// Some features must be at the most outermost level.
    +
     8023.     78
    +
     8024.      1        if (blockage !== token_global) {
    +
     8025.      1
    +
     8026.      1// test_cause:
    +
     8027.      1// ["
    +
     8028.      1// if(0){import aa from "aa";}
    +
     8029.      1// ", "post_s_export", "misplaced_a", "import", 7]
    +
     8030.      1
    +
     8031.      1            warn("misplaced_a", the_thing);
    +
     8032.      1        }
    +
     8033.     78    }
    +
     8034.    518
    +
     8035.      8    function post_s_for(thing) {
    +
     8036.      8
    +
     8037.      8// Recurse walk_statement().
    +
     8038.      8
    +
     8039.      8        walk_statement(thing.inc);
    +
     8040.      8    }
    +
     8041.    518
    +
     8042.   2001    function post_s_function(thing) {
    +
     8043.   2001        delete functionage.async;
    +
     8044.   2001        delete functionage.finally;
    +
     8045.   2001        delete functionage.loop;
    +
     8046.   2001        delete functionage.statement_prv;
    +
     8047.   2001        delete functionage.switch;
    +
     8048.   2001        delete functionage.try;
    +
     8049.   2001        functionage = function_stack.pop();
    +
     8050.      3        if (thing.wrapped) {
    +
     8051.      3
    +
     8052.      3// test_cause:
    +
     8053.      3// ["aa=(function(){})", "post_s_function", "unexpected_parens", "function", 5]
    +
     8054.      3
    +
     8055.      3            warn("unexpected_parens", thing);
    +
     8056.      3        }
    +
     8057.   2001        return post_s_lbrace();
    +
     8058.   2001    }
    +
     8059.    518
    +
     8060.     61    function post_s_import(the_thing) {
    +
     8061.     61        const name = the_thing.name;
    +
     8062.     60        if (name) {
    +
     8063.     60            if (Array.isArray(name)) {
    +
     8064.     60                name.forEach(function (name) {
    +
     8065.     60                    name.dead = false;
    +
     8066.     60                    name.init = true;
    +
     8067.     60                    blockage.live.push(name);
    +
     8068.     60                });
    +
     8069.     60            } else {
    +
     8070.     60                name.dead = false;
    +
     8071.     60                name.init = true;
    +
     8072.     60                blockage.live.push(name);
    +
     8073.     60            }
    +
     8074.     60            return post_s_export(the_thing);
    +
     8075.     60        }
    +
     8076.     61    }
    +
     8077.    518
    +
     8078.   7695    function post_s_lbrace() {
    +
     8079.   2987        blockage.live.forEach(function (name) {
    +
     8080.   2987            name.dead = true;
    +
     8081.   2987        });
    +
     8082.   7695        delete blockage.live;
    +
     8083.   7695        blockage = block_stack.pop();
    +
     8084.   7695    }
    +
     8085.    518
    +
     8086.     56    function post_s_try(thing) {
    +
     8087.     54        if (thing.catch) {
    +
     8088.     54            if (thing.catch.name) {
    +
     8089.     54                Object.assign(catchage.context[thing.catch.name.id], {
    +
     8090.     54                    dead: false,
    +
     8091.     54                    init: true
    +
     8092.     54                });
    +
     8093.     54            }
    +
     8094.     54
    +
     8095.     54// Recurse walk_statement().
    +
     8096.     54
    +
     8097.     54            walk_statement(thing.catch.block);
    +
     8098.     54
    +
     8099.     54// Restore previous catch-scope after catch-block.
    +
     8100.     54
    +
     8101.     54            catchage = catch_stack.pop();
    +
     8102.     54        }
    +
     8103.     56    }
    +
     8104.    518
    +
     8105.   2320    function post_s_var(thing) {
    +
     8106.   2635        thing.names.forEach(function (name) {
    +
     8107.   2635            name.dead = false;
    +
     8108.   1250            if (name.expression !== undefined) {
    +
     8109.   1250                walk_expression(name.expression);
    +
     8110.   1250
    +
     8111.   1250// Probably deadcode.
    +
     8112.   1250// if (name.id === "{" || name.id === "[") {
    +
     8113.   1250//     name.names.forEach(subactivate);
    +
     8114.   1250// } else {
    +
     8115.   1250//     name.init = true;
    +
     8116.   1250// }
    +
     8117.   1250
    +
     8118.   1250                jslint_assert(
    +
     8119.   1250                    !(name.id === "{" || name.id === "["),
    +
     8120.   1250                    `Expected !(name.id === "{" || name.id === "[").`
    +
     8121.   1250                );
    +
     8122.   1250                name.init = true;
    +
     8123.   1250            }
    +
     8124.   2635            blockage.live.push(name);
    +
     8125.   2635        });
    +
     8126.   2320    }
    +
     8127.    518
    +
     8128.    213    function post_t(thing) {
    +
     8129.    213        if (
    +
     8130.    213            is_weird(thing.expression[0])
    +
     8131.    213            || thing.expression[0].constant === true
    +
     8132.    206            || is_equal(thing.expression[1], thing.expression[2])
    +
     8133.      9        ) {
    +
     8134.      9
    +
     8135.      9// test_cause:
    +
     8136.      9// ["let aa=(aa?`${0}`:`${0}`);", "post_t", "unexpected_a", "?", 11]
    +
     8137.      9// ["let aa=(aa?`0`:`0`);", "post_t", "unexpected_a", "?", 11]
    +
     8138.      9
    +
     8139.      9            warn("unexpected_a", thing);
    +
     8140.    204        } else if (is_equal(thing.expression[0], thing.expression[1])) {
    +
     8141.    204
    +
     8142.    204// test_cause:
    +
     8143.    204// ["aa?aa:0", "post_t", "expected_a_b", "?", 3]
    +
     8144.    204
    +
     8145.    204            warn("expected_a_b", thing, "||", "?");
    +
     8146.    204        } else if (is_equal(thing.expression[0], thing.expression[2])) {
    +
     8147.    204
    +
     8148.    204// test_cause:
    +
     8149.    204// ["aa?0:aa", "post_t", "expected_a_b", "?", 3]
    +
     8150.    204
    +
     8151.    204            warn("expected_a_b", thing, "&&", "?");
    +
     8152.    204        } else if (
    +
     8153.    204            thing.expression[1].id === "true"
    +
     8154.    204            && thing.expression[2].id === "false"
    +
     8155.    204        ) {
    +
     8156.    204
    +
     8157.    204// test_cause:
    +
     8158.    204// ["aa?true:false", "post_t", "expected_a_b", "?", 3]
    +
     8159.    204
    +
     8160.    204            warn("expected_a_b", thing, "!!", "?");
    +
     8161.    204        } else if (
    +
     8162.    204            thing.expression[1].id === "false"
    +
     8163.    204            && thing.expression[2].id === "true"
    +
     8164.    204        ) {
    +
     8165.    204
    +
     8166.    204// test_cause:
    +
     8167.    204// ["aa?false:true", "post_t", "expected_a_b", "?", 3]
    +
     8168.    204
    +
     8169.    204            warn("expected_a_b", thing, "!", "?");
    +
     8170.    204        } else if (
    +
     8171.    204            thing.expression[0].wrapped !== true
    +
     8172.    204            && (
    +
     8173.    204                thing.expression[0].id === "||"
    +
     8174.    204                || thing.expression[0].id === "&&"
    +
     8175.    204            )
    +
     8176.    204        ) {
    +
     8177.    204
    +
     8178.    204// test_cause:
    +
     8179.    204// ["(aa&&!aa?0:1)", "post_t", "wrap_condition", "&&", 4]
    +
     8180.    204
    +
     8181.    204            warn("wrap_condition", thing.expression[0]);
    +
     8182.    204        }
    +
     8183.    213    }
    +
     8184.    518
    +
     8185.   4106    function post_u(thing) {
    +
     8186.    779        if (thing.id === "`") {
    +
     8187.    779            if (thing.expression.every(function (thing) {
    +
     8188.    779                return thing.constant;
    +
     8189.    779            })) {
    +
     8190.    779                thing.constant = true;
    +
     8191.    779            }
    +
     8192.   3327        } else if (thing.id === "!") {
    +
     8193.   3327            if (thing.expression.constant === true) {
    +
     8194.   3327                warn("unexpected_a", thing);
    +
     8195.   3327            }
    +
     8196.   3327        } else if (thing.id === "!!") {
    +
     8197.   3327            if (!option_dict.convert) {
    +
     8198.   3327
    +
     8199.   3327// test_cause:
    +
     8200.   3327// ["!!0", "post_u", "expected_a_b", "!!", 1]
    +
     8201.   3327
    +
     8202.   3327                warn("expected_a_b", thing, "Boolean(...)", "!!");
    +
     8203.   3327            }
    +
     8204.   3327        } else if (
    +
     8205.   3327            thing.id !== "["
    +
     8206.   3327            && thing.id !== "{"
    +
     8207.   3327            && thing.id !== "function"
    +
     8208.   3327            && thing.id !== "new"
    +
     8209.   3327        ) {
    +
     8210.   3327            if (thing.expression.constant === true) {
    +
     8211.   3327                thing.constant = true;
    +
     8212.   3327            }
    +
     8213.   3327        }
    +
     8214.   4106    }
    +
     8215.    518
    +
     8216.      7    function post_u_plus(thing) {
    +
     8217.      7        const right = thing.expression;
    +
     8218.      7        if (!option_dict.convert) {
    +
     8219.      7
    +
     8220.      7// test_cause:
    +
     8221.      7// ["aa=+0", "post_u_plus", "expected_a_b", "+", 4]
    +
     8222.      7
    +
     8223.      7            warn("expected_a_b", thing, "Number(...)", "+");
    +
     8224.      7        }
    +
     8225.      1        if (right.id === "(" && right.expression[0].id === "new") {
    +
     8226.      1            warn("unexpected_a_before_b", thing, "+", "new");
    +
     8227.      6        } else if (
    +
     8228.      6            right.constant
    +
     8229.      6            || right.id === "{"
    +
     8230.      6            || (right.id === "[" && right.arity !== "binary")
    +
     8231.      6        ) {
    +
     8232.      6            warn("unexpected_a", thing, "+");
    +
     8233.      6        }
    +
     8234.      7    }
    +
     8235.    518
    +
     8236.  36945    function pre_a_bitwise(thing) {
    +
     8237.  36945
    +
     8238.  36945// These are the bitwise operators.
    +
     8239.  36945
    +
     8240.  36943        switch (!option_dict.bitwise && thing.id) {
    +
     8241.      2        case "&":
    +
     8242.      3        case "&=":
    +
     8243.      5        case "<<":
    +
     8244.      6        case "<<=":
    +
     8245.      8        case ">>":
    +
     8246.      9        case ">>=":
    +
     8247.     11        case ">>>":
    +
     8248.     12        case ">>>=":
    +
     8249.     14        case "^":
    +
     8250.     15        case "^=":
    +
     8251.     17        case "|":
    +
     8252.     18        case "|=":
    +
     8253.     21        case "~":
    +
     8254.     21
    +
     8255.     21// test_cause:
    +
     8256.     21// ["0&0", "pre_a_bitwise", "unexpected_a", "&", 2]
    +
     8257.     21// ["0&=0", "pre_a_bitwise", "unexpected_a", "&=", 2]
    +
     8258.     21// ["0<<0", "pre_a_bitwise", "unexpected_a", "<<", 2]
    +
     8259.     21// ["0<<=0", "pre_a_bitwise", "unexpected_a", "<<=", 2]
    +
     8260.     21// ["0>>0", "pre_a_bitwise", "unexpected_a", ">>", 2]
    +
     8261.     21// ["0>>=0", "pre_a_bitwise", "unexpected_a", ">>=", 2]
    +
     8262.     21// ["0>>>0", "pre_a_bitwise", "unexpected_a", ">>>", 2]
    +
     8263.     21// ["0>>>=0", "pre_a_bitwise", "unexpected_a", ">>>=", 2]
    +
     8264.     21// ["0^0", "pre_a_bitwise", "unexpected_a", "^", 2]
    +
     8265.     21// ["0^=0", "pre_a_bitwise", "unexpected_a", "^=", 2]
    +
     8266.     21// ["0|0", "pre_a_bitwise", "unexpected_a", "|", 2]
    +
     8267.     21// ["0|=0", "pre_a_bitwise", "unexpected_a", "|=", 2]
    +
     8268.     21// ["~0", "pre_a_bitwise", "unexpected_a", "~", 1]
    +
     8269.     21
    +
     8270.     21            warn("unexpected_a", thing);
    +
     8271.     21            break;
    +
     8272.  36945        }
    +
     8273.  36945        if (
    +
     8274.  36945            thing.id !== "("
    +
     8275.  26534            && thing.id !== "&&"
    +
     8276.  25332            && thing.id !== "||"
    +
     8277.  24358            && thing.id !== "="
    +
     8278.  20124            && Array.isArray(thing.expression)
    +
     8279.   8506            && thing.expression.length === 2
    +
     8280.   8505            && (
    +
     8281.   8505                relationop[thing.expression[0].id] === true
    +
     8282.   8505                || relationop[thing.expression[1].id] === true
    +
     8283.   8505            )
    +
     8284.      1        ) {
    +
     8285.      1
    +
     8286.      1// test_cause:
    +
     8287.      1// ["0<0<0", "pre_a_bitwise", "unexpected_a", "<", 4]
    +
     8288.      1
    +
     8289.      1            warn("unexpected_a", thing);
    +
     8290.      1        }
    +
     8291.  36945    }
    +
     8292.    518
    +
     8293.  31933    function pre_b(thing) {
    +
     8294.  31933        let left;
    +
     8295.  31933        let right;
    +
     8296.  31933        let value;
    +
     8297.   3960        if (relationop[thing.id] === true) {
    +
     8298.   3960            left = thing.expression[0];
    +
     8299.   3960            right = thing.expression[1];
    +
     8300.   3960            if (left.id === "NaN" || right.id === "NaN") {
    +
     8301.   3960
    +
     8302.   3960// test_cause:
    +
     8303.   3960// ["NaN===NaN", "pre_b", "number_isNaN", "===", 4]
    +
     8304.   3960
    +
     8305.   3960                warn("number_isNaN", thing);
    +
     8306.   3960            } else if (left.id === "typeof") {
    +
     8307.   3960                if (right.id !== "(string)") {
    +
     8308.   3960                    if (right.id !== "typeof") {
    +
     8309.   3960
    +
     8310.   3960// test_cause:
    +
     8311.   3960// ["typeof 0===0", "pre_b", "expected_string_a", "0", 12]
    +
     8312.   3960
    +
     8313.   3960                        warn("expected_string_a", right);
    +
     8314.   3960                    }
    +
     8315.   3960                } else {
    +
     8316.   3960                    value = right.value;
    +
     8317.   3960                    if (value === "null" || value === "undefined") {
    +
     8318.   3960
    +
     8319.   3960// test_cause:
    +
     8320.   3960// ["
    +
     8321.   3960// typeof aa==="undefined"
    +
     8322.   3960// ", "pre_b", "unexpected_typeof_a", "undefined", 13]
    +
     8323.   3960
    +
     8324.   3960                        warn("unexpected_typeof_a", right, value);
    +
     8325.   3960                    } else if (
    +
     8326.   3960                        value !== "bigint"
    +
     8327.   3960                        && value !== "boolean"
    +
     8328.   3960                        && value !== "function"
    +
     8329.   3960                        && value !== "number"
    +
     8330.   3960                        && value !== "object"
    +
     8331.   3960                        && value !== "string"
    +
     8332.   3960                        && value !== "symbol"
    +
     8333.   3960                    ) {
    +
     8334.   3960
    +
     8335.   3960// test_cause:
    +
     8336.   3960// ["typeof 0===\"aa\"", "pre_b", "expected_type_string_a", "aa", 12]
    +
     8337.   3960
    +
     8338.   3960                        warn("expected_type_string_a", right, value);
    +
     8339.   3960                    }
    +
     8340.   3960                }
    +
     8341.   3960            }
    +
     8342.   3960        }
    +
     8343.  31933    }
    +
     8344.    518
    +
     8345.      1    function pre_b_eqeq(thing) {
    +
     8346.      1
    +
     8347.      1// test_cause:
    +
     8348.      1// ["0==0", "pre_b_eqeq", "expected_a_b", "==", 2]
    +
     8349.      1
    +
     8350.      1        warn("expected_a_b", thing, "===", "==");
    +
     8351.      1    }
    +
     8352.    518
    +
     8353.      1    function pre_b_in(thing) {
    +
     8354.      1
    +
     8355.      1// test_cause:
    +
     8356.      1// ["aa in aa", "pre_b_in", "infix_in", "in", 4]
    +
     8357.      1
    +
     8358.      1        warn("infix_in", thing);
    +
     8359.      1    }
    +
     8360.    518
    +
     8361.      1    function pre_b_instanceof(thing) {
    +
     8362.      1
    +
     8363.      1// test_cause:
    +
     8364.      1// ["0 instanceof 0", "pre_b_instanceof", "unexpected_a", "instanceof", 3]
    +
     8365.      1
    +
     8366.      1        warn("unexpected_a", thing);
    +
     8367.      1    }
    +
     8368.    518
    +
     8369.  10411    function pre_b_lparen(thing) {
    +
     8370.  10411        const left = thing.expression[0];
    +
     8371.  10411        let left_variable;
    +
     8372.  10411        let parent;
    +
     8373.  10411        if (
    +
     8374.  10411            left.identifier
    +
     8375.   6658            && functionage.context[left.id] === undefined
    +
     8376.   3034            && typeof functionage.name === "object"
    +
     8377.   2395        ) {
    +
     8378.   2395            parent = functionage.name.parent;
    +
     8379.   2395            if (parent) {
    +
     8380.   2395                left_variable = parent.context[left.id];
    +
     8381.   2395                if (
    +
     8382.   2395                    left_variable !== undefined
    +
     8383.   2395
    +
     8384.   2395// Probably deadcode.
    +
     8385.   2395// && left_variable.dead
    +
     8386.   2395
    +
     8387.   2395                    && left_variable.parent === parent
    +
     8388.   2395                    && left_variable.calls !== undefined
    +
     8389.   2395                    && left_variable.calls[functionage.name.id] !== undefined
    +
     8390.   2395                ) {
    +
     8391.   2395                    left_variable.dead = false;
    +
     8392.   2395                }
    +
     8393.   2395            }
    +
     8394.   2395        }
    +
     8395.  10411    }
    +
     8396.    518
    +
     8397.      1    function pre_b_noteq(thing) {
    +
     8398.      1
    +
     8399.      1// test_cause:
    +
     8400.      1// ["0!=0", "pre_b_noteq", "expected_a_b", "!=", 2]
    +
     8401.      1
    +
     8402.      1        warn("expected_a_b", thing, "!==", "!=");
    +
     8403.      1    }
    +
     8404.    518
    +
     8405.    974    function pre_b_or(thing) {
    +
     8406.   1948        thing.expression.forEach(function (thang) {
    +
     8407.    177            if (thang.id === "&&" && !thang.wrapped) {
    +
     8408.      1
    +
     8409.      1// test_cause:
    +
     8410.      1// ["0&&0||0", "pre_b_or", "and", "&&", 2]
    +
     8411.      1
    +
     8412.      1                warn("and", thang);
    +
     8413.      1            }
    +
     8414.   1948        });
    +
     8415.    974    }
    +
     8416.    518
    +
     8417.      8    function pre_s_for(thing) {
    +
     8418.      8        let the_variable;
    +
     8419.      2        if (thing.name !== undefined) {
    +
     8420.      2            thing.name.dead = false;
    +
     8421.      2            the_variable = lookup(thing.name);
    +
     8422.      2            if (the_variable !== undefined) {
    +
     8423.      2                if (the_variable.init && the_variable.readonly) {
    +
     8424.      2
    +
     8425.      2// test_cause:
    +
     8426.      2// ["const aa=0;for(aa in aa){}", "pre_s_for", "bad_assignment_a", "aa", 16]
    +
     8427.      2
    +
     8428.      2                    warn("bad_assignment_a", thing.name);
    +
     8429.      2                }
    +
     8430.      2                the_variable.init = true;
    +
     8431.      2            }
    +
     8432.      2        }
    +
     8433.      8
    +
     8434.      8// Recurse walk_statement().
    +
     8435.      8
    +
     8436.      8        walk_statement(thing.initial);
    +
     8437.      8    }
    +
     8438.    518
    +
     8439.   2001    function pre_s_function(thing) {
    +
     8440.   2001
    +
     8441.   2001// test_cause:
    +
     8442.   2001// ["()=>0", "pre_s_function", "", "", 0]
    +
     8443.   2001// ["(function (){}())", "pre_s_function", "", "", 0]
    +
     8444.   2001// ["function aa(){}", "pre_s_function", "", "", 0]
    +
     8445.   2001
    +
     8446.   2001        test_cause("");
    +
     8447.   1057        if (thing.arity === "statement" && blockage.body !== true) {
    +
     8448.      1
    +
     8449.      1// test_cause:
    +
     8450.      1// ["if(0){function aa(){}\n}", "pre_s_function", "unexpected_a", "function", 7]
    +
     8451.      1
    +
     8452.      1            warn("unexpected_a", thing);
    +
     8453.      1        }
    +
     8454.   2001        function_stack.push(functionage);
    +
     8455.   2001        block_stack.push(blockage);
    +
     8456.   2001        functionage = thing;
    +
     8457.   2001        blockage = thing;
    +
     8458.   2001        thing.live = [];
    +
     8459.   1098        if (typeof thing.name === "object") {
    +
     8460.   1098            thing.name.dead = false;
    +
     8461.   1098            thing.name.init = true;
    +
     8462.   1098        }
    +
     8463.      7        if (thing.extra === "get") {
    +
     8464.      7            if (thing.parameters.length !== 0) {
    +
     8465.      7
    +
     8466.      7// test_cause:
    +
     8467.      7// ["
    +
     8468.      7// /*jslint getset*/
    +
     8469.      7// aa={get aa(aa){}}
    +
     8470.      7// ", "pre_s_function", "bad_get", "function", 9]
    +
     8471.      7
    +
     8472.      7                warn("bad_get", thing);
    +
     8473.      7            }
    +
     8474.   1994        } else if (thing.extra === "set") {
    +
     8475.   1994            if (thing.parameters.length !== 1) {
    +
     8476.   1994
    +
     8477.   1994// test_cause:
    +
     8478.   1994// ["
    +
     8479.   1994// /*jslint getset*/
    +
     8480.   1994// aa={set aa(){}}
    +
     8481.   1994// ", "pre_s_function", "bad_set", "function", 9]
    +
     8482.   1994
    +
     8483.   1994                warn("bad_set", thing);
    +
     8484.   1994            }
    +
     8485.   1994        }
    +
     8486.   2063        thing.parameters.forEach(function (name) {
    +
     8487.   2063            walk_expression(name.expression);
    +
     8488.   1840            if (name.id === "{" || name.id === "[") {
    +
     8489.    267                name.names.forEach(subactivate);
    +
     8490.   1796            } else {
    +
     8491.   1796                name.dead = false;
    +
     8492.   1796                name.init = true;
    +
     8493.   1796            }
    +
     8494.   2063        });
    +
     8495.   2001    }
    +
     8496.    518
    +
     8497.   5694    function pre_s_lbrace(thing) {
    +
     8498.   5694        block_stack.push(blockage);
    +
     8499.   5694        blockage = thing;
    +
     8500.   5694        thing.live = [];
    +
     8501.   5694    }
    +
     8502.    518
    +
     8503.     56    function pre_try(thing) {
    +
     8504.     54        if (thing.catch !== undefined) {
    +
     8505.     54
    +
     8506.     54// Create new catch-scope for catch-parameter.
    +
     8507.     54
    +
     8508.     54            catch_stack.push(catchage);
    +
     8509.     54            catchage = thing.catch;
    +
     8510.     54        }
    +
     8511.     56    }
    +
     8512.    518
    +
     8513.  28689    function pre_v(thing) {
    +
     8514.  28689        const the_variable = lookup(thing);
    +
     8515.  28611        if (the_variable !== undefined) {
    +
     8516.  28611            thing.variable = the_variable;
    +
     8517.  28611            the_variable.used += 1;
    +
     8518.  28611        }
    +
     8519.  28689    }
    +
     8520.    518
    +
     8521.    717    function subactivate(name) {
    +
     8522.    717        name.init = true;
    +
     8523.    717        name.dead = false;
    +
     8524.    717        blockage.live.push(name);
    +
     8525.    717    }
    +
     8526.    518
    +
     8527. 164534    function walk_expression(thing) {
    +
     8528. 103787        if (thing) {
    +
     8529. 103787            if (Array.isArray(thing)) {
    +
     8530. 103787
    +
     8531. 103787// test_cause:
    +
     8532. 103787// ["(function(){}())", "walk_expression", "isArray", "", 0]
    +
     8533. 103787// ["0&&0", "walk_expression", "isArray", "", 0]
    +
     8534. 103787
    +
     8535. 103787                test_cause("isArray");
    +
     8536. 103787                thing.forEach(walk_expression);
    +
     8537. 103787            } else {
    +
     8538. 103787                preamble(thing);
    +
     8539. 103787                walk_expression(thing.expression);
    +
     8540. 103787
    +
     8541. 103787// PR-414 - Bugfix - fix fart-body not being walked.
    +
     8542. 103787
    +
     8543. 103787                if (thing.id === "function" || thing.id === "=>") {
    +
     8544. 103787
    +
     8545. 103787// test_cause:
    +
     8546. 103787// ["aa=()=>0", "walk_expression", "function", "=>", 0]
    +
     8547. 103787// ["aa=function(){}", "walk_expression", "function", "function", 0]
    +
     8548. 103787
    +
     8549. 103787                    test_cause("function", thing.id);
    +
     8550. 103787
    +
     8551. 103787// Recurse walk_statement().
    +
     8552. 103787
    +
     8553. 103787                    walk_statement(thing.block);
    +
     8554. 103787                }
    +
     8555. 103787                if (
    +
     8556. 103787                    thing.arity === "preassign" || thing.arity === "postassign"
    +
     8557. 103787                ) {
    +
     8558. 103787
    +
     8559. 103787// test_cause:
    +
     8560. 103787// ["aa=++aa", "walk_expression", "unexpected_a", "++", 4]
    +
     8561. 103787// ["aa=--aa", "walk_expression", "unexpected_a", "--", 4]
    +
     8562. 103787
    +
     8563. 103787                    warn("unexpected_a", thing);
    +
     8564. 103787                } else if (
    +
     8565. 103787                    thing.arity === "statement"
    +
     8566. 103787                    || thing.arity === "assignment"
    +
     8567. 103787                ) {
    +
     8568. 103787
    +
     8569. 103787// test_cause:
    +
     8570. 103787// ["aa[aa=0]", "walk_expression", "unexpected_statement_a", "=", 6]
    +
     8571. 103787
    +
     8572. 103787                    warn("unexpected_statement_a", thing);
    +
     8573. 103787                }
    +
     8574. 103787
    +
     8575. 103787// test_cause:
    +
     8576. 103787// ["aa=0", "walk_expression", "default", "", 0]
    +
     8577. 103787
    +
     8578. 103787                test_cause("default");
    +
     8579. 103787                postamble(thing);
    +
     8580. 103787            }
    +
     8581. 103787        }
    +
     8582. 164534    }
    +
     8583.    518
    +
     8584.  78303    function walk_statement(thing) {
    +
     8585.  43095        if (!thing) {
    +
     8586.  43095            return;
    +
     8587.  43095        }
    +
     8588.  35208        if (Array.isArray(thing)) {
    +
     8589.   7608
    +
     8590.   7608// test_cause:
    +
     8591.   7608// ["+[]", "walk_statement", "isArray", "", 0]
    +
     8592.   7608
    +
     8593.   7608            test_cause("isArray");
    +
     8594.   7608
    +
     8595.   7608// Recurse walk_statement().
    +
     8596.   7608
    +
     8597.   7608            thing.forEach(walk_statement);
    +
     8598.   7608            return;
    +
     8599.  27600        }
    +
     8600.  27600        preamble(thing);
    +
     8601.  27600        walk_expression(thing.expression);
    +
     8602.  27600        if (thing.arity === "binary") {
    +
     8603.   5665            if (thing.id !== "(") {
    +
     8604.   5665
    +
     8605.   5665// test_cause:
    +
     8606.   5665// ["0&&0", "walk_statement", "unexpected_expression_a", "&&", 2]
    +
     8607.   5665
    +
     8608.   5665                warn("unexpected_expression_a", thing);
    +
     8609.   5665            }
    +
     8610.  21935        } else if (
    +
     8611.  21935            thing.arity !== "statement"
    +
     8612.  21935            && thing.arity !== "assignment"
    +
     8613.  21935            && thing.id !== "import"
    +
     8614.  21935        ) {
    +
     8615.  21935
    +
     8616.  21935// test_cause:
    +
     8617.  21935// ["!0", "walk_statement", "unexpected_expression_a", "!", 1]
    +
     8618.  21935// ["+[]", "walk_statement", "unexpected_expression_a", "+", 1]
    +
     8619.  21935// ["+new aa()", "walk_statement", "unexpected_expression_a", "+", 1]
    +
     8620.  21935// ["0", "walk_statement", "unexpected_expression_a", "0", 1]
    +
     8621.  21935// ["typeof 0", "walk_statement", "unexpected_expression_a", "typeof", 1]
    +
     8622.  21935
    +
     8623.  21935            warn("unexpected_expression_a", thing);
    +
     8624.  27600        }
    +
     8625.  27600
    +
     8626.  27600// Recurse walk_statement().
    +
     8627.  27600
    +
     8628.  27600        walk_statement(thing.block);
    +
     8629.  27600        walk_statement(thing.else);
    +
     8630.  27600        postamble(thing);
    +
     8631.  27600    }
    +
     8632.    518
    +
     8633.    518    postaction = action(posts);
    +
     8634.    518    postamble = amble(posts);
    +
     8635.    518    preaction = action(pres);
    +
     8636.    518    preamble = amble(pres);
    +
     8637.    518    postaction("assignment", "+=", post_a_pluseq);
    +
     8638.    518    postaction("assignment", post_a);
    +
     8639.    518    postaction("binary", "&&", post_b_and);
    +
     8640.    518    postaction("binary", "(", post_b_lparen);
    +
     8641.    518    postaction("binary", "=>", post_s_function);
    +
     8642.    518    postaction("binary", "[", post_b_lbracket);
    +
     8643.    518    postaction("binary", "||", post_b_or);
    +
     8644.    518    postaction("binary", post_b);
    +
     8645.    518    postaction("statement", "const", post_s_var);
    +
     8646.    518    postaction("statement", "export", post_s_export);
    +
     8647.    518    postaction("statement", "for", post_s_for);
    +
     8648.    518    postaction("statement", "function", post_s_function);
    +
     8649.    518    postaction("statement", "import", post_s_import);
    +
     8650.    518    postaction("statement", "let", post_s_var);
    +
     8651.    518    postaction("statement", "try", post_s_try);
    +
     8652.    518    postaction("statement", "var", post_s_var);
    +
     8653.    518    postaction("statement", "{", post_s_lbrace);
    +
     8654.    518    postaction("ternary", post_t);
    +
     8655.    518    postaction("unary", "+", post_u_plus);
    +
     8656.    518    postaction("unary", "function", post_s_function);
    +
     8657.    518    postaction("unary", post_u);
    +
     8658.    518    preaction("assignment", pre_a_bitwise);
    +
     8659.    518    preaction("binary", "!=", pre_b_noteq);
    +
     8660.    518    preaction("binary", "(", pre_b_lparen);
    +
     8661.    518    preaction("binary", "==", pre_b_eqeq);
    +
     8662.    518    preaction("binary", "=>", pre_s_function);
    +
     8663.    518    preaction("binary", "in", pre_b_in);
    +
     8664.    518    preaction("binary", "instanceof", pre_b_instanceof);
    +
     8665.    518    preaction("binary", "||", pre_b_or);
    +
     8666.    518    preaction("binary", pre_b);
    +
     8667.    518    preaction("binary", pre_a_bitwise);
    +
     8668.    518    preaction("statement", "for", pre_s_for);
    +
     8669.    518    preaction("statement", "function", pre_s_function);
    +
     8670.    518    preaction("statement", "try", pre_try);
    +
     8671.    518    preaction("statement", "{", pre_s_lbrace);
    +
     8672.    518    preaction("unary", "function", pre_s_function);
    +
     8673.    518    preaction("unary", "~", pre_a_bitwise);
    +
     8674.    518    preaction("variable", pre_v);
    +
     8675.    518
    +
     8676.    518    walk_statement(state.token_tree);
    +
     8677.    518}
    +
     8678.      1
    +
     8679.    208function jslint_phase5_whitage(state) {
    +
     8680.    208
    +
     8681.    208// PHASE 5. Check whitespace between tokens in <token_list>.
    +
     8682.    208
    +
     8683.    208    let {
    +
     8684.    208        artifact,
    +
     8685.    208        catch_list,
    +
     8686.    208        function_list,
    +
     8687.    208        function_stack,
    +
     8688.    208        option_dict,
    +
     8689.    208        test_cause,
    +
     8690.    208        token_global,
    +
     8691.    208        token_list,
    +
     8692.    208        warn
    +
     8693.    208    } = state;
    +
     8694.    208    let closer = "(end)";
    +
     8695.    208    let free = false;
    +
     8696.    208
    +
     8697.    208// free = false
    +
     8698.    208
    +
     8699.    208// cause:
    +
     8700.    208// "()=>0"
    +
     8701.    208// "aa()"
    +
     8702.    208// "aa(0,0)"
    +
     8703.    208// "function(){}"
    +
     8704.    208
    +
     8705.    208// free = true
    +
     8706.    208
    +
     8707.    208// cause:
    +
     8708.    208// "(0)"
    +
     8709.    208// "(aa)"
    +
     8710.    208// "aa(0)"
    +
     8711.    208// "do{}while()"
    +
     8712.    208// "for(){}"
    +
     8713.    208// "if(){}"
    +
     8714.    208// "switch(){}"
    +
     8715.    208// "while(){}"
    +
     8716.    208
    +
     8717.    208    let left = token_global;
    +
     8718.    208    let margin = 0;
    +
     8719.    208    let mode_indent = (
    +
     8720.    208
    +
     8721.    208// PR-330 - Allow 2-space indent.
    +
     8722.    208
    +
     8723.    208        option_dict.indent2
    +
     8724.      5        ? 2
    +
     8725.    203        : 4
    +
     8726.    208    );
    +
     8727.    208    let nr_comments_skipped = 0;
    +
     8728.    208    let open = true;
    +
     8729.    208    let opening = true;
    +
     8730.    208    let right;
    +
     8731.    208
    +
     8732.    208// This is the set of infix operators that require a space on each side.
    +
     8733.    208
    +
     8734.    208    let spaceop = object_assign_from_list(empty(), [
    +
     8735.    208        "!=", "!==", "%", "%=", "&", "&&", "&=", "*", "*=", "+=", "-=", "/",
    +
     8736.    208        "/=", "<", "<<", "<<=", "<=", "=", "==", "===", "=>", ">", ">=", ">>",
    +
     8737.    208        ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||"
    +
     8738.    208    ], true);
    +
     8739.    208
    +
     8740.  38092    function at_margin(fit) {
    +
     8741.  38092        const at = margin + fit;
    +
     8742.     21        if (right.from !== at) {
    +
     8743.     21            return expected_at(at);
    +
     8744.     21        }
    +
     8745.  38092    }
    +
     8746.    208
    +
     8747.   2057    function delve(the_function) {
    +
     8748.  13014        Object.keys(the_function.context).forEach(function (id) {
    +
     8749.  13014            const name = the_function.context[id];
    +
     8750.  12979            if (id !== "ignore" && name.parent === the_function) {
    +
     8751.   6286
    +
     8752.   6286// test_cause:
    +
     8753.   6286// ["function aa(aa) {return aa;}", "delve", "id", "", 0]
    +
     8754.   6286
    +
     8755.   6286                test_cause("id");
    +
     8756.   6286                if (
    +
     8757.   6286                    name.used === 0
    +
     8758.   6286
    +
     8759.   6286// Probably deadcode.
    +
     8760.   6286// && (
    +
     8761.   6286//     name.role !== "function"
    +
     8762.   6286//     || name.parent.arity !== "unary"
    +
     8763.   6286// )
    +
     8764.   6286
    +
     8765.   6286                    && jslint_assert(
    +
     8766.   6286                        name.role !== "function",
    +
     8767.   6286                        `Expected name.role !== "function".`
    +
     8768.   6286                    )
    +
     8769.   6286                ) {
    +
     8770.   6286
    +
     8771.   6286// test_cause:
    +
     8772.   6286// ["/*jslint node*/\nlet aa;", "delve", "unused_a", "aa", 5]
    +
     8773.   6286// ["function aa(aa){return;}", "delve", "unused_a", "aa", 13]
    +
     8774.   6286// ["let aa=0;try{aa();}catch(bb){aa();}", "delve", "unused_a", "bb", 26]
    +
     8775.   6286
    +
     8776.   6286                    warn("unused_a", name);
    +
     8777.   6286                } else if (!name.init) {
    +
     8778.   6286
    +
     8779.   6286// test_cause:
    +
     8780.   6286// ["/*jslint node*/\nlet aa;aa();", "delve", "uninitialized_a", "aa", 5]
    +
     8781.   6286
    +
     8782.   6286                    warn("uninitialized_a", name);
    +
     8783.   6286                }
    +
     8784.   6286            }
    +
     8785.  13014        });
    +
     8786.   2057    }
    +
     8787.    208
    +
     8788.     25    function expected_at(at) {
    +
     8789.     25
    +
     8790.     25// Probably deadcode.
    +
     8791.     25// if (right === undefined) {
    +
     8792.     25//     right = token_nxt;
    +
     8793.     25// }
    +
     8794.     25
    +
     8795.     25        jslint_assert(
    +
     8796.     25            !(right === undefined),
    +
     8797.     25            `Expected !(right === undefined).`
    +
     8798.     25        );
    +
     8799.     25        warn(
    +
     8800.     25            "expected_a_at_b_c",
    +
     8801.     25            right,
    +
     8802.     25            artifact(right),
    +
     8803.     25
    +
     8804.     25// Fudge column numbers in warning message.
    +
     8805.     25
    +
     8806.     25            at + jslint_fudge,
    +
     8807.     25            right.from + jslint_fudge
    +
     8808.     25        );
    +
     8809.     25    }
    +
     8810.    208
    +
     8811.   3095    function no_space() {
    +
     8812.   3094        if (left.line === right.line) {
    +
     8813.   3094
    +
     8814.   3094// from:
    +
     8815.   3094// if (left.line === right.line) {
    +
     8816.   3094//     no_space();
    +
     8817.   3094// } else {
    +
     8818.   3094
    +
     8819.   3094            if (left.thru !== right.from && nr_comments_skipped === 0) {
    +
     8820.   3094
    +
     8821.   3094// test_cause:
    +
     8822.   3094// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14]
    +
     8823.   3094
    +
     8824.   3094                warn(
    +
     8825.   3094                    "unexpected_space_a_b",
    +
     8826.   3094                    right,
    +
     8827.   3094                    artifact(left),
    +
     8828.   3094                    artifact(right)
    +
     8829.   3094                );
    +
     8830.   3094            }
    +
     8831.   3094        } else {
    +
     8832.      1
    +
     8833.      1// from:
    +
     8834.      1// } else if (
    +
     8835.      1//     right.arity === "binary"
    +
     8836.      1//     && right.id === "("
    +
     8837.      1//     && free
    +
     8838.      1// ) {
    +
     8839.      1//     no_space();
    +
     8840.      1// } else if (
    +
     8841.      1
    +
     8842.      1// Probably deadcode.
    +
     8843.      1// if (open) {
    +
     8844.      1//     const at = (
    +
     8845.      1//         free
    +
     8846.      1//         ? margin
    +
     8847.      1//         : margin + 8
    +
     8848.      1//     );
    +
     8849.      1//     if (right.from < at) {
    +
     8850.      1//         expected_at(at);
    +
     8851.      1//     }
    +
     8852.      1// } else {
    +
     8853.      1//     if (right.from !== margin + 8) {
    +
     8854.      1//         expected_at(margin + 8);
    +
     8855.      1//     }
    +
     8856.      1// }
    +
     8857.      1
    +
     8858.      1            jslint_assert(open, `Expected open.`);
    +
     8859.      1            jslint_assert(free, `Expected free.`);
    +
     8860.      1            if (right.from < margin) {
    +
     8861.      1
    +
     8862.      1// test_cause:
    +
     8863.      1// ["let aa = aa(\naa\n()\n);", "expected_at", "expected_a_at_b_c", "5", 1]
    +
     8864.      1
    +
     8865.      1                expected_at(margin);
    +
     8866.      1            }
    +
     8867.      1        }
    +
     8868.   3095    }
    +
     8869.    208
    +
     8870.  96059    function no_space_only() {
    +
     8871.  96059        if (
    +
     8872.  96059            left.id !== "(global)"
    +
     8873.  96057            && left.nr + 1 === right.nr
    +
     8874.  96057            && (
    +
     8875.  96057                left.line !== right.line
    +
     8876.  96057                || left.thru !== right.from
    +
     8877.  96057            )
    +
     8878.      5        ) {
    +
     8879.      5            warn(
    +
     8880.      5                "unexpected_space_a_b",
    +
     8881.      5                right,
    +
     8882.      5                artifact(left),
    +
     8883.      5                artifact(right)
    +
     8884.      5            );
    +
     8885.      5        }
    +
     8886.  96059    }
    +
     8887.    208
    +
     8888.  46227    function one_space() {
    +
     8889.  44324        if (left.line === right.line || !open) {
    +
     8890.  44324            if (left.thru + 1 !== right.from && nr_comments_skipped === 0) {
    +
     8891.  44324                warn(
    +
     8892.  44324                    "expected_space_a_b",
    +
     8893.  44324                    right,
    +
     8894.  44324                    artifact(left),
    +
     8895.  44324                    artifact(right)
    +
     8896.  44324                );
    +
     8897.  44324            }
    +
     8898.  44324        } else {
    +
     8899.   1903            if (right.from !== margin) {
    +
     8900.   1903                expected_at(margin);
    +
     8901.   1903            }
    +
     8902.   1903        }
    +
     8903.  46227    }
    +
     8904.    208
    +
     8905.   9344    function one_space_only() {
    +
     8906.      8        if (left.line !== right.line || left.thru + 1 !== right.from) {
    +
     8907.      8            warn("expected_space_a_b", right, artifact(left), artifact(right));
    +
     8908.      8        }
    +
     8909.   9344    }
    +
     8910.    208
    +
     8911.  24667    function pop() {
    +
     8912.  24667        const previous = function_stack.pop();
    +
     8913.  24667        closer = previous.closer;
    +
     8914.  24667        free = previous.free;
    +
     8915.  24667        margin = previous.margin;
    +
     8916.  24667        open = previous.open;
    +
     8917.  24667        opening = previous.opening;
    +
     8918.  24667    }
    +
     8919.    208
    +
     8920.  24667    function push() {
    +
     8921.  24667        function_stack.push({
    +
     8922.  24667            closer,
    +
     8923.  24667            free,
    +
     8924.  24667            margin,
    +
     8925.  24667            open,
    +
     8926.  24667            opening
    +
     8927.  24667        });
    +
     8928.  24667    }
    +
     8929.    208
    +
     8930.    208// uninitialized_and_unused();
    +
     8931.    208// Delve into the functions looking for variables that were not initialized
    +
     8932.    208// or used. If the file imports or exports, then its global object is also
    +
     8933.    208// delved.
    +
     8934.    208
    +
     8935.    174    if (state.mode_module === true || option_dict.node) {
    +
     8936.     51        delve(token_global);
    +
     8937.     51    }
    +
     8938.    208    catch_list.forEach(delve);
    +
     8939.    208    function_list.forEach(delve);
    +
     8940.    208
    +
     8941.      2    if (option_dict.white) {
    +
     8942.      2        return;
    +
     8943.    206    }
    +
     8944.    206
    +
     8945.    206// whitage();
    +
     8946.    206// Go through the token list, looking at usage of whitespace.
    +
     8947.    206
    +
     8948. 207158    token_list.forEach(function whitage(the_token) {
    +
     8949. 207158        right = the_token;
    +
     8950. 195959        if (right.id === "(comment)" || right.id === "(end)") {
    +
     8951.  11407            nr_comments_skipped += 1;
    +
     8952. 195751        } else {
    +
     8953. 195751
    +
     8954. 195751// If left is an opener and right is not the closer, then push the previous
    +
     8955. 195751// state. If the token following the opener is on the next line, then this is
    +
     8956. 195751// an open form. If the tokens are on the same line, then it is a closed form.
    +
     8957. 195751// Open form is more readable, with each item (statement, argument, parameter,
    +
     8958. 195751// etc) starting on its own line. Closed form is more compact. Statement blocks
    +
     8959. 195751// are always in open form.
    +
     8960. 195751
    +
     8961. 195751// The open and close pairs.
    +
     8962. 195751
    +
     8963. 195751            switch (left.id) {
    +
     8964. 195751            case "${":
    +
     8965. 195751            case "(":
    +
     8966. 195751            case "[":
    +
     8967. 195751            case "{":
    +
     8968. 195751
    +
     8969. 195751// test_cause:
    +
     8970. 195751// ["let aa=[];", "whitage", "opener", "", 0]
    +
     8971. 195751// ["let aa=`${0}`;", "whitage", "opener", "", 0]
    +
     8972. 195751// ["let aa=aa();", "whitage", "opener", "", 0]
    +
     8973. 195751// ["let aa={};", "whitage", "opener", "", 0]
    +
     8974. 195751
    +
     8975. 195751                test_cause("opener");
    +
     8976. 195751
    +
     8977. 195751// Probably deadcode.
    +
     8978. 195751// case "${}":
    +
     8979. 195751
    +
     8980. 195751                jslint_assert(
    +
     8981. 195751                    !(left.id + right.id === "${}"),
    +
     8982. 195751                    "Expected !(left.id + right.id === \"${}\")."
    +
     8983. 195751                );
    +
     8984. 195751                switch (left.id + right.id) {
    +
     8985. 195751                case "()":
    +
     8986. 195751                case "[]":
    +
     8987. 195751                case "{}":
    +
     8988. 195751
    +
     8989. 195751// If left and right are opener and closer, then the placement of right depends
    +
     8990. 195751// on the openness. Illegal pairs (like '{]') have already been detected.
    +
     8991. 195751
    +
     8992. 195751// test_cause:
    +
     8993. 195751// ["let aa=[];", "whitage", "opener_closer", "", 0]
    +
     8994. 195751// ["let aa=aa();", "whitage", "opener_closer", "", 0]
    +
     8995. 195751// ["let aa={};", "whitage", "opener_closer", "", 0]
    +
     8996. 195751
    +
     8997. 195751                    test_cause("opener_closer");
    +
     8998. 195751                    if (left.line === right.line) {
    +
     8999. 195751
    +
     9000. 195751// test_cause:
    +
     9001. 195751// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14]
    +
     9002. 195751
    +
     9003. 195751                        no_space();
    +
     9004. 195751                    } else {
    +
     9005. 195751
    +
     9006. 195751// test_cause:
    +
     9007. 195751// ["let aa = aa(\n );", "expected_at", "expected_a_at_b_c", "1", 2]
    +
     9008. 195751
    +
     9009. 195751                        at_margin(0);
    +
     9010. 195751                    }
    +
     9011. 195751                    break;
    +
     9012. 195751                default:
    +
     9013. 195751
    +
     9014. 195751// test_cause:
    +
     9015. 195751// ["let aa=(0);", "whitage", "opener_operand", "", 0]
    +
     9016. 195751// ["let aa=[0];", "whitage", "opener_operand", "", 0]
    +
     9017. 195751// ["let aa=`${0}`;", "whitage", "opener_operand", "", 0]
    +
     9018. 195751// ["let aa=aa(0);", "whitage", "opener_operand", "", 0]
    +
     9019. 195751// ["let aa={aa:0};", "whitage", "opener_operand", "", 0]
    +
     9020. 195751
    +
     9021. 195751                    test_cause("opener_operand");
    +
     9022. 195751                    opening = left.open || (left.line !== right.line);
    +
     9023. 195751                    push();
    +
     9024. 195751                    switch (left.id) {
    +
     9025. 195751                    case "${":
    +
     9026. 195751                        closer = "}";
    +
     9027. 195751                        break;
    +
     9028. 195751                    case "(":
    +
     9029. 195751                        closer = ")";
    +
     9030. 195751                        break;
    +
     9031. 195751                    case "[":
    +
     9032. 195751                        closer = "]";
    +
     9033. 195751                        break;
    +
     9034. 195751                    case "{":
    +
     9035. 195751                        closer = "}";
    +
     9036. 195751                        break;
    +
     9037. 195751                    }
    +
     9038. 195751                    if (opening) {
    +
     9039. 195751
    +
     9040. 195751// test_cause:
    +
     9041. 195751// ["function aa(){\nreturn;\n}", "whitage", "opening", "", 0]
    +
     9042. 195751// ["let aa=(\n0\n);", "whitage", "opening", "", 0]
    +
     9043. 195751// ["let aa=[\n0\n];", "whitage", "opening", "", 0]
    +
     9044. 195751// ["let aa=`${\n0\n}`;", "whitage", "opening", "", 0]
    +
     9045. 195751// ["let aa={\naa:0\n};", "whitage", "opening", "", 0]
    +
     9046. 195751
    +
     9047. 195751                        test_cause("opening");
    +
     9048. 195751                        free = closer === ")" && left.free;
    +
     9049. 195751                        open = true;
    +
     9050. 195751                        margin += mode_indent;
    +
     9051. 195751                        if (right.role === "label") {
    +
     9052. 195751                            if (right.from !== 0) {
    +
     9053. 195751
    +
     9054. 195751// test_cause:
    +
     9055. 195751// ["
    +
     9056. 195751// function aa() {
    +
     9057. 195751//  bb:
    +
     9058. 195751//     while (aa) {
    +
     9059. 195751//         if (aa) {
    +
     9060. 195751//             break bb;
    +
     9061. 195751//         }
    +
     9062. 195751//     }
    +
     9063. 195751// }
    +
     9064. 195751// ", "expected_at", "expected_a_at_b_c", "1", 2]
    +
     9065. 195751
    +
     9066. 195751                                expected_at(0);
    +
     9067. 195751                            }
    +
     9068. 195751                        } else if (right.switch) {
    +
     9069. 195751                            at_margin(-mode_indent);
    +
     9070. 195751                        } else {
    +
     9071. 195751                            at_margin(0);
    +
     9072. 195751                        }
    +
     9073. 195751                    } else {
    +
     9074. 195751                        if (right.statement || right.role === "label") {
    +
     9075. 195751
    +
     9076. 195751// test_cause:
    +
     9077. 195751// ["
    +
     9078. 195751// function aa() {bb:
    +
     9079. 195751//     while (aa) {
    +
     9080. 195751//         aa();
    +
     9081. 195751//     }
    +
     9082. 195751// }
    +
     9083. 195751// ", "whitage", "expected_line_break_a_b", "bb", 16]
    +
     9084. 195751
    +
     9085. 195751                            warn(
    +
     9086. 195751                                "expected_line_break_a_b",
    +
     9087. 195751                                right,
    +
     9088. 195751                                artifact(left),
    +
     9089. 195751                                artifact(right)
    +
     9090. 195751                            );
    +
     9091. 195751                        }
    +
     9092. 195751
    +
     9093. 195751// test_cause:
    +
     9094. 195751// ["let aa=(0);", "whitage", "not_free", "", 0]
    +
     9095. 195751// ["let aa=[0];", "whitage", "not_free", "", 0]
    +
     9096. 195751// ["let aa=`${0}`;", "whitage", "not_free", "", 0]
    +
     9097. 195751// ["let aa={aa:0};", "whitage", "not_free", "", 0]
    +
     9098. 195751
    +
     9099. 195751                        test_cause("not_free");
    +
     9100. 195751                        free = false;
    +
     9101. 195751                        open = false;
    +
     9102. 195751
    +
     9103. 195751// test_cause:
    +
     9104. 195751// ["let aa = ( 0 );", "no_space_only", "unexpected_space_a_b", "0", 12]
    +
     9105. 195751
    +
     9106. 195751                        no_space_only();
    +
     9107. 195751                    }
    +
     9108. 195751                }
    +
     9109. 195751                break;
    +
     9110. 195751            default:
    +
     9111. 195751                if (right.statement === true) {
    +
     9112. 195751                    if (left.id === "else") {
    +
     9113. 195751
    +
     9114. 195751// test_cause:
    +
     9115. 195751// ["
    +
     9116. 195751// let aa = 0;
    +
     9117. 195751// if (aa) {
    +
     9118. 195751//     aa();
    +
     9119. 195751// } else  if (aa) {
    +
     9120. 195751//     aa();
    +
     9121. 195751// }
    +
     9122. 195751// ", "one_space_only", "expected_space_a_b", "if", 9]
    +
     9123. 195751
    +
     9124. 195751                        one_space_only();
    +
     9125. 195751                    } else {
    +
     9126. 195751
    +
     9127. 195751// test_cause:
    +
     9128. 195751// [" let aa = 0;", "expected_at", "expected_a_at_b_c", "1", 2]
    +
     9129. 195751
    +
     9130. 195751                        at_margin(0);
    +
     9131. 195751                        open = false;
    +
     9132. 195751                    }
    +
     9133. 195751
    +
     9134. 195751// If right is a closer, then pop the previous state.
    +
     9135. 195751
    +
     9136. 195751                } else if (right.id === closer) {
    +
     9137. 195751                    pop();
    +
     9138. 195751                    if (opening && right.id !== ";") {
    +
     9139. 195751                        at_margin(0);
    +
     9140. 195751                    } else {
    +
     9141. 195751                        no_space_only();
    +
     9142. 195751                    }
    +
     9143. 195751                } else {
    +
     9144. 195751
    +
     9145. 195751// Left is not an opener, and right is not a closer.
    +
     9146. 195751// The nature of left and right will determine the space between them.
    +
     9147. 195751
    +
     9148. 195751// If left is ',' or ';' or right is a statement then if open,
    +
     9149. 195751// right must go at the margin, or if closed, a space between.
    +
     9150. 195751
    +
     9151. 195751                    if (right.switch) {
    +
     9152. 195751                        at_margin(-mode_indent);
    +
     9153. 195751                    } else if (right.role === "label") {
    +
     9154. 195751                        if (right.from !== 0) {
    +
     9155. 195751
    +
     9156. 195751// test_cause:
    +
     9157. 195751// ["
    +
     9158. 195751// function aa() {
    +
     9159. 195751//     aa();cc:
    +
     9160. 195751//     while (aa) {
    +
     9161. 195751//         if (aa) {
    +
     9162. 195751//             break cc;
    +
     9163. 195751//         }
    +
     9164. 195751//     }
    +
     9165. 195751// }
    +
     9166. 195751// ", "expected_at", "expected_a_at_b_c", "1", 10]
    +
     9167. 195751
    +
     9168. 195751                            expected_at(0);
    +
     9169. 195751                        }
    +
     9170. 195751                    } else if (left.id === ",") {
    +
     9171. 195751                        if (!open || (
    +
     9172. 195751                            (free || closer === "]")
    +
     9173. 195751                            && left.line === right.line
    +
     9174. 195751                        )) {
    +
     9175. 195751
    +
     9176. 195751// test_cause:
    +
     9177. 195751// ["let {aa,bb} = 0;", "one_space", "expected_space_a_b", "bb", 9]
    +
     9178. 195751
    +
     9179. 195751                            one_space();
    +
     9180. 195751                        } else {
    +
     9181. 195751
    +
     9182. 195751// test_cause:
    +
     9183. 195751// ["
    +
     9184. 195751// function aa() {
    +
     9185. 195751//     aa(
    +
     9186. 195751//         0,0
    +
     9187. 195751//     );
    +
     9188. 195751// }
    +
     9189. 195751// ", "expected_at", "expected_a_at_b_c", "9", 11]
    +
     9190. 195751
    +
     9191. 195751                            at_margin(0);
    +
     9192. 195751                        }
    +
     9193. 195751
    +
     9194. 195751// If right is a ternary operator, line it up on the margin.
    +
     9195. 195751
    +
     9196. 195751                    } else if (right.arity === "ternary") {
    +
     9197. 195751                        if (open) {
    +
     9198. 195751
    +
     9199. 195751// test_cause:
    +
     9200. 195751// ["
    +
     9201. 195751// let aa = (
    +
     9202. 195751//     aa
    +
     9203. 195751//     ? 0
    +
     9204. 195751// : 1
    +
     9205. 195751// );
    +
     9206. 195751// ", "expected_at", "expected_a_at_b_c", "5", 1]
    +
     9207. 195751
    +
     9208. 195751                            at_margin(0);
    +
     9209. 195751                        } else {
    +
     9210. 195751
    +
     9211. 195751// test_cause:
    +
     9212. 195751// ["let aa = (aa ? 0 : 1);", "whitage", "use_open", "?", 14]
    +
     9213. 195751
    +
     9214. 195751                            warn("use_open", right);
    +
     9215. 195751                        }
    +
     9216. 195751                    } else if (
    +
     9217. 195751                        right.arity === "binary"
    +
     9218. 195751                        && right.id === "("
    +
     9219. 195751                        && free
    +
     9220. 195751                    ) {
    +
     9221. 195751
    +
     9222. 195751// test_cause:
    +
     9223. 195751// ["let aa = aa(\naa ()\n);", "no_space", "unexpected_space_a_b", "(", 4]
    +
     9224. 195751
    +
     9225. 195751                        no_space();
    +
     9226. 195751                    } else if (
    +
     9227. 195751                        left.id === "."
    +
     9228. 195751                        || left.id === "?."
    +
     9229. 195751                        || left.id === "..."
    +
     9230. 195751                        || right.id === ","
    +
     9231. 195751                        || right.id === ";"
    +
     9232. 195751                        || right.id === ":"
    +
     9233. 195751                        || (
    +
     9234. 195751                            right.arity === "binary"
    +
     9235. 195751                            && (right.id === "(" || right.id === "[")
    +
     9236. 195751                        )
    +
     9237. 195751                        || (
    +
     9238. 195751                            right.arity === "function"
    +
     9239. 195751                            && left.id !== "function"
    +
     9240. 195751                        )
    +
     9241. 195751                        || (right.id === "." || right.id === "?.")
    +
     9242. 195751                    ) {
    +
     9243. 195751
    +
     9244. 195751// test_cause:
    +
     9245. 195751// ["let aa = 0 ;", "no_space_only", "unexpected_space_a_b", ";", 12]
    +
     9246. 195751// ["let aa = aa ?.aa;", "no_space_only", "unexpected_space_a_b", "?.", 13]
    +
     9247. 195751
    +
     9248. 195751                        no_space_only();
    +
     9249. 195751                    } else if (left.id === ";") {
    +
     9250. 195751
    +
     9251. 195751// test_cause:
    +
     9252. 195751// ["
    +
     9253. 195751// /*jslint for*/
    +
     9254. 195751// function aa() {
    +
     9255. 195751//     for (
    +
     9256. 195751//         aa();
    +
     9257. 195751// aa;
    +
     9258. 195751//         aa()
    +
     9259. 195751//     ) {
    +
     9260. 195751//         aa();
    +
     9261. 195751//     }
    +
     9262. 195751// }
    +
     9263. 195751// ", "expected_at", "expected_a_at_b_c", "9", 1]
    +
     9264. 195751
    +
     9265. 195751                        if (open) {
    +
     9266. 195751                            at_margin(0);
    +
     9267. 195751                        }
    +
     9268. 195751                    } else if (
    +
     9269. 195751                        left.arity === "ternary"
    +
     9270. 195751                        || left.id === "case"
    +
     9271. 195751                        || left.id === "catch"
    +
     9272. 195751                        || left.id === "else"
    +
     9273. 195751                        || left.id === "finally"
    +
     9274. 195751                        || left.id === "while"
    +
     9275. 195751                        || left.id === "await"
    +
     9276. 195751                        || right.id === "catch"
    +
     9277. 195751                        || right.id === "else"
    +
     9278. 195751                        || right.id === "finally"
    +
     9279. 195751                        || (right.id === "while" && !right.statement)
    +
     9280. 195751                        || (left.id === ")" && right.id === "{")
    +
     9281. 195751                    ) {
    +
     9282. 195751
    +
     9283. 195751// test_cause:
    +
     9284. 195751// ["
    +
     9285. 195751// function aa() {
    +
     9286. 195751//     do {
    +
     9287. 195751//         aa();
    +
     9288. 195751//     } while(aa());
    +
     9289. 195751// }
    +
     9290. 195751// ", "one_space_only", "expected_space_a_b", "(", 12]
    +
     9291. 195751
    +
     9292. 195751                        one_space_only();
    +
     9293. 195751                    } else if (
    +
     9294. 195751
    +
     9295. 195751// There is a space between left and right.
    +
     9296. 195751
    +
     9297. 195751                        spaceop[left.id] === true
    +
     9298. 195751                        || spaceop[right.id] === true
    +
     9299. 195751                        || (
    +
     9300. 195751                            left.arity === "binary"
    +
     9301. 195751                            && (left.id === "+" || left.id === "-")
    +
     9302. 195751                        )
    +
     9303. 195751                        || (
    +
     9304. 195751                            right.arity === "binary"
    +
     9305. 195751                            && (right.id === "+" || right.id === "-")
    +
     9306. 195751                        )
    +
     9307. 195751                        || left.id === "function"
    +
     9308. 195751                        || left.id === ":"
    +
     9309. 195751                        || left.id === "async"
    +
     9310. 195751                        || (
    +
     9311. 195751                            (
    +
     9312. 195751                                left.identifier
    +
     9313. 195751                                || left.id === "(string)"
    +
     9314. 195751                                || left.id === "(number)"
    +
     9315. 195751                            )
    +
     9316. 195751                            && (
    +
     9317. 195751                                right.identifier
    +
     9318. 195751                                || right.id === "(string)"
    +
     9319. 195751                                || right.id === "(number)"
    +
     9320. 195751                            )
    +
     9321. 195751                        )
    +
     9322. 195751                        || (left.arity === "statement" && right.id !== ";")
    +
     9323. 195751                    ) {
    +
     9324. 195751
    +
     9325. 195751// test_cause:
    +
     9326. 195751// ["let aa=0;", "one_space", "expected_space_a_b", "0", 8]
    +
     9327. 195751// ["let aa={\naa:\n0\n};", "expected_at", "expected_a_at_b_c", "5", 1]
    +
     9328. 195751
    +
     9329. 195751                        one_space();
    +
     9330. 195751                    } else if (left.arity === "unary" && left.id !== "`") {
    +
     9331. 195751                        no_space_only();
    +
     9332. 195751                    }
    +
     9333. 195751                }
    +
     9334. 195751            }
    +
     9335. 195751            nr_comments_skipped = 0;
    +
     9336. 195751            delete left.calls;
    +
     9337. 195751            delete left.dead;
    +
     9338. 195751            delete left.free;
    +
     9339. 195751            delete left.init;
    +
     9340. 195751            delete left.open;
    +
     9341. 195751            delete left.used;
    +
     9342. 195751            left = right;
    +
     9343. 195751        }
    +
     9344. 207158    });
    +
     9345.    206}
    +
     9346.      1
    +
     9347.      6function jslint_report({
    +
     9348.      6    exports,
    +
     9349.      6    froms,
    +
     9350.      6    functions,
    +
     9351.      6    global,
    +
     9352.      6    json,
    +
     9353.      6    module,
    +
     9354.      6    property,
    +
     9355.      6    stop,
    +
     9356.      6    warnings
    +
     9357.      6}) {
    +
     9358.      6
    +
     9359.      6// This function will create human-readable, html-report
    +
     9360.      6// for warnings, properties, and functions from jslint-result-object.
    +
     9361.      6//
    +
     9362.      6// Example usage:
    +
     9363.      6//  let result = jslint("console.log('hello world')");
    +
     9364.      6//  let html = jslint_report(result);
    +
     9365.      6
    +
     9366.      6    let html = "";
    +
     9367.      6    let length_80 = 1111;
    +
     9368.      6
    +
     9369.    328    function address(line = 1, column = 1) {
    +
     9370.    328
    +
     9371.    328// This function will create HTML address element from <line> and <column>
    +
     9372.    328
    +
     9373.    328        return `<address>${Number(line)}: ${Number(column)}</address>`;
    +
     9374.    328
    +
     9375.    328    }
    +
     9376.      6
    +
     9377.   2256    function detail(title, list) {
    +
     9378.   2256        return (
    +
     9379.   2256            (Array.isArray(list) && list.length > 0)
    +
     9380.    781            ? (
    +
     9381.    781
    +
     9382.    781// Google Lighthouse Accessibility - <dl>'s do not contain only properly-ordered
    +
     9383.    781// <dt> and <dd> groups, <script>, <template> or <div> elements.
    +
     9384.    781
    +
     9385.    781                "<dl>"
    +
     9386.    781                + "<dt>" + htmlEscape(title) + "</dt>"
    +
     9387.    781                + "<dd>" + list.join(", ") + "</dd>"
    +
     9388.    781                + "</dl>"
    +
     9389.    781            )
    +
     9390.   1475            : ""
    +
     9391.   2256        );
    +
     9392.   2256    }
    +
     9393.      6
    +
     9394.      6    html += String(`
    +
     9395.      6<style class="JSLINT_REPORT_STYLE">
    +
     9396.      6/* jslint utility2:true */
    +
     9397.      6/*csslint box-model: false, ids:false */
    +
     9398.      6/*csslint ignore:start*/
    +
     9399.      6@font-face {
    +
     9400.      6    font-display: swap;
    +
     9401.      6    font-family: "Daley";
    +
     9402.      6    src: url(
    +
     9403.      6"data:font/woff2;base64,d09GMgABAAAAABy4AA4AAAAAThwAABxiAAEAAAAAAAAAAAAA\
    +
     9404.      6AAAAAAAAAAAAAAAABmAAgiQINAmcDBEICuc41DEBNgIkA4R2C4I+AAQgBYkuByAMgScfYUIF\
    +
     9405.      67NgjsHGAbcDVFkXZ5Jwd+P96IGPc9rl9ETBEaCzCJkvY2UpziRZ7zftZWk8052U9+NqX6vXL\
    +
     9406.      6KDflSQnlJ0bP+QnPQAy744n9mup6H9PaCDFwM5zjf8exB89bZ1cdrYOP0NgnuRDRWlk9u/fE\
    +
     9407.      6llkxqmfH8lmRQ/DAmER9opk9wR6suc1LvTiXNEe1vbhUCH2USgnEwH3vUm05JQqejGvZvOtz\
    +
     9408.      67sIKEGgLdDNl/IrfqWVZG/wr42ekomEm91VA1p4LhHBuFzHF8//u7vvbREHMQqGtNLmiOOD/\
    +
     9409.      6X7WWiwqyCE98qt0jk5JJmgR5WJJElBmzRb1F7a66MmSLTNWZ2XSHfKBSKHoVteSEJ6EOdvVw\
    +
     9410.      6fNZOtXKDe39jXdRlkmMnOWIOFBgeEK/b0mFsgffnPyyAitNyutKky7J8a8MSEkAKGLgfptnS\
    +
     9411.      6/gDRSo7vwdNUmQDB7oP6pK7QF5d9SrY8M/tkrXcurSIQAmX7tz7pd33LIB7GQkBQ/k81s/0D\
    +
     9412.      6gpt4gbw7x0Cn/PocitK5KIGPGQIzQzAMuCeC2ERAidx9TySVqX06goT0SFFOOV9Kuxdi5Rg7\
    +
     9413.      6l6n3c+nKRemidOm2dtFV1jXMk4rP2m6RJ8xEdPYONLTbeMgaJ1nwS2W4su3MHwqkkvJ2PdDU\
    +
     9414.      6r7pgAnVRt4Kh789FXlD0r3p6jUtNO19O1s74U9pnIxqFpw+mBgF+8y30PAyw1dzlknLLVcSB\
    +
     9415.      6J2OuCr9eV5Efew6cOGd47ZEfhrW7HXI+FBNFvWgWnugUU4UvlrV63niv2ZPeKu8M76y/HQaG\
    +
     9416.      6weU+4Gzp+Y+cfb9R9djDWcd1Svr1xG7l+j/yf3eM996548qlC+dOzOqQ8//Lo0uaSEQCFuLD\
    +
     9417.      6/bXyWhJ6aPmyaRonVPxGABFL4/0slcKI6f+PmT0M+QRsplmWnv4F49VT+JsPifoa6aeyr2Hz\
    +
     9418.      6EeLdP1FEOV/ZN+c9sAuoNh0BRS0xgCCc9wME5s0HOKj/wc0fWYsTbFQpsZL5SayJPkL45kDo\
    +
     9419.      6DcJJ10MvD0ZSq7FEIr1TfqZ7NC6s75zSp8viaNO5/PczYCV9z6NTa0KBdnGBg6kbdeBkRLfU\
    +
     9420.      6qRd3D9Pqw5jWCc5WM/i95OE8731MBd1u2EmsXIa5dCvavY32U1Ytza4nfbERg6OVRZka7jq0\
    +
     9421.      6r2FcXNDyEhXheaHtaU1o1kvO9MuBOHqugLUEzN+4jznu0oK9wZPur1lWVFfxl8lZzn2XwcjZ\
    +
     9422.      6Csg/RJy0mAMMmgnqXS8ELhOCRUSLzvsM5gAPudEh2lVoRxGgyUVnArZMruE0YS1PqFMD3upb\
    +
     9423.      6jVoecGj1KpWl6/ZysuyzkG4SGA4bps6FBQSg4e4IxNUgdmosmoDn0TpIex/s1BFau6GBNO4z\
    +
     9424.      6cvWXypm4hEg5k3llelySFqNmUtRZ3PHBA7p4MBX1nK4awwAV6kWzIVbUA67A55QKYbMsgVaH\
    +
     9425.      6c1ZxKuZ0DCyqxCsJjLyCEY36gf0wjAu3t0zemc87PmBCJbU9Lso0YAaYJUx8wsR02hYz5hGy\
    +
     9426.      6Js0+A4uHGZgfuf5SOR9iBQuLhpOExaIFrHj6JlXanebzGHp2ELDh6av09PVE1fmdsj2oHRWs\
    +
     9427.      6fOtYrV6wRCyx7XogHqvpnZiPBBdNcL6kIoS9UI/DOIlumlveSgv9oqMBYp7WZ2fGxAXmZmaG\
    +
     9428.      6OCyJG6+wAszZFCQw/EXVjx+YA2uVyN6bhNWiZhgtYjAwR5U/7uV1scghiTGiAPZbA5ZqHw5u\
    +
     9429.      6Yu1cDjhRwREBFyq2wa0R8GgceDUKPo2BX+MhoAkQ1EQIaZqVHMwH3xM+P32TTA34tmOMNZ4n\
    +
     9430.      6mHXqn49fmE3qX1+wMNYoYetOsPx6wxKzkURImERJIjGSSJwkkiCJJEkiKZJImiSSIYlkSYqK\
    +
     9431.      6UBu0UOopuLMmasiJW0PMFOO2UgbDif2NaQUqkBbyaGjdTUvuyamEQwCq9DWsxsG9qPt+VFqV\
    +
     9432.      66cIsXcyWujWIEtNFdeia9ssNrJUpe3IDMPQZOReC8x+qvt17drPWdcHeL0gTarWwoQ6o828o\
    +
     9433.      60EJzrA20yZsgVyVHdlCJOF3NaACxHbP38TA+MGx3St9c5t2CxbGtunB4J9AF4Px2rSr1wyK9\
    +
     9434.      69KoXBR13vw9Fk9qhTX0ivZoanrvhLa5oiJO8cqR0lX7QtJ2c1a62V3PMtutaaoit+hxtXuC5\
    +
     9435.      6ZUXJePSR6btQlt5g7PqPQ822g7F8D123pc4kaGXz7qYztJxDXCxJr7foKqxwy4rikI/NvINx\
    +
     9436.      6bkArRTTnnMWy6YA8J39LfTweThKsqlt7Mz078NDSOPOGgtGTpeG8ZRBF+xKBjdSoNe8gE6uC\
    +
     9437.      6ucOH98jE4+cv1JEjI555TFjYj4+0KdFlojzJGWp2wc1tCaYGSeO8dBfT0u3lpDY3tazzu4wn\
    +
     9438.      6lF9wzy2nK+sTr/qEVdANoZ0ToBdD+MY4ewOHNnkXPBvKVXLSbEGfGVD0Nzr0Fs3HID3Y1Kqx\
    +
     9439.      6mzJ6p1C1/R6Xneyw/q9YRDLahbnsI1u76XzMLPqsK0yvQDeQ4TMR41709sIssmEgs0XH1lcj\
    +
     9440.      67HLnUG6u2Xpy5vbOowIGqrR6cwF0TLGI5PF7pkbzIVYQU0sIaoNgul3LGAH2B1nREFYXUMia\
    +
     9441.      6prCeAzggGxrC5gIK2dK0exs/AIRKdlIIuxkUspdSsU+rqXagqXaooXakqTiWS/a0E7zA6QIK\
    +
     9442.      6OdMUznMAh+RCQ7hcQCFXmspr3ciuds/6gPsZFPIgpfJhwUIepRAeZ1DIk5Tue4oKfSfKZyNV\
    +
     9443.      6pKU/J7J4Abx1EMV5mXSRDl6lMfU6jfBmBww4k7f6gLzTB+J9od/kA/uGj2mET2nkn7+zQ/JF\
    +
     9444.      6H5Kv+pB804fkOyvwI43wM438V5sdkd/6iPzRR+SvPiL/WIH/aYRxGqMb/Oqe3d54+LWR1vr2\
    +
     9445.      6knnnc467iD247eXBA3YYBAiFfierClXz/8jyL3Qh/zP8y+Y/1eN8jq+SKZAML/lIidjwZ8N4\
    +
     9446.      6aLthvhxGUkGPo+p0eHKZ0sT5FsqJcQCy9UhHIvcJFIlIvANTPFWUTUhSiVdsNRnvwEQxm5uc\
    +
     9447.      6ksjdv5evJfpOgI6c7juH8pnG2RKwlXaDYe9g8rMwYfML3A2SMWeBDopJJsmS5dUE2KttnmQa\
    +
     9448.      6JZlMspvEpJioiEDFNpPUTbwqG3Zjhx2VCeJrIf60s2mI6blZMZVyAyYzI+1a2Y0AIqcbLUgR\
    +
     9449.      66iRbNtnp82GrImXW0YbcbczDgqQDWNdTenvtTAlT9iPHenluV+d3eed1/5MjMBrX2LgrK2ml\
    +
     9450.      6FuoDOz036n/kaHbAeszR3jHoI4NWB3lusTfuVgkMUkLQaH0F6+pSCS11fXRwT421vs9s7axd\
    +
     9451.      6nvtF7/eeIeq9s1aCLsLWdh+w7sXz3IYdEsSQ0LVsebmES/vXDU9k653W4MiNq8bMj5nLioCY\
    +
     9452.      6edGgOT6tmYwqiOW1ugiEmew6iwjvvYb3SaeZJb7XNufOo9oH8FTneWGL+BLiclptpnhPwcui\
    +
     9453.      6T+rzcF34+ycsL7p3AveuML9i9h13beylyg8CzEz5HppadqmmDxKrAquG9L3ztedRoWxEsAYt\
    +
     9454.      6OM1Eu0G0gyTHkxf7cSkHJQRbA4xmlqHWkv1C0KhFhBq1z81Wq1CZoWic8TJ570WfSj5qsM+Q\
    +
     9455.      6nl4k3H5+P+P3zlv9ltQrzv41qyiSwV/gOadyQBchsmwDGu/JI8tXflE8jqUVA0Zw0SKbdDC9\
    +
     9456.      6c4FR+fak95SdF7uqpoRe9z6YRv+85YUzF4qJy6Q8GOVNwUn/ymyjNNbmcuVfXYeH2osLdCte\
    +
     9457.      6ebmZRyUfQQZA1BSCLK4PWA/z1kBvDZm0t+i3or1LkMD6en95pGG0UOa8ZJXgS9TdEA1I2mZw\
    +
     9458.      61JOWWxDu0NEh4rM19H55rvueMBUZV1RjkmB3oxkXhAckpa5gzzxUDA2VLOrWFAXx+4gmfU17\
    +
     9459.      65o3v9H7EYdvGFuM+tDB3TA4ITjVUKduO/R4bXRAcPXZusWkN+t59sFz7Hyi0FkSdzrHXQVFq\
    +
     9460.      6b8c9k9eLRjVlBbNvt4172CanYg/F3Rket1zCTc77UZ61Gq/Be9J8hrKrxbDZMEotf5o8zHDc\
    +
     9461.      6/UJaEtdhgwHEcBEQKM+6NBWIewLmI1sHuWYAedZCw8U1hJfSWcld+2tv3jpCFc5FnosLWC0+\
    +
     9462.      6DnAlnOXUXLoMXrmCVerNQkZHvRm8YtE12vG8+N/vOnPcu3vM1uOnzE3u3VP2ppmLZawm2NuO\
    +
     9463.      6tPa7xwHFCgVKpox5PVrOmaDHrThk1tX864a2+/qhJd3nCFRQ+bfUKI4O+Wgk5byB3saMcUfV\
    +
     9464.      6C8G137yMd16zRm3ZSq+UrDlk5ha3TiAj0b74prWO/vYG+RC+ronP1/McDtefBtY1XhZE0PIB\
    +
     9465.      6wTe7CBTte2U6KPbYd5GffApQlDGssdfmxYGSlnHrQt7++KEwUg3ikkoQyKPixgUDB6Lozjv5\
    +
     9466.      6vM5PBnllt+UzMnP6DStFsOfossbXOefWhQApACCNpkTYGAONIowDfndqDKRFuzn685nthZPe\
    +
     9467.      6vEL7TIWkXAG2yxKBH90+yMzuRzWn3KMmyKGwZWnIErlJ9Vwt8OtR6+4TKad5y9+ViBtTzVG+\
    +
     9468.      6tpv/xiLrcGKJRtYvCUlGeL4Dwy1jo1CSQe0X71EXK1YG44ztxTONjIslL8SwY0Cki0k0vsX/\
    +
     9469.      6/xz7CxkAc9dEdJZhMy/JCGzD2FAGtUcag0tc2e2miJkp477V2qTKB+nFnDl/noxpXJ+yqVdO\
    +
     9470.      6wNjbplmeiuburg9ii1Z1zwtG8QjcJAiVPSOV2mHzq1Qt7p2+YCcIKPmFusE5O+m8s+Wd8o3t\
    +
     9471.      6qO1b1IZF8N0tx6RQnZ9Ux3gXijHlolixst6vhJV6ao0ZFzSprfAc3x0MLvxU0OsmXEVddMVK\
    +
     9472.      629CC6mPgPtXTUW7tVnZxwm0DTJwNOeVRV4axMSPlpgyv1Va1MQhQqWwUOb0s+gVLOecos4Nf\
    +
     9473.      6eqlFW3fLQrlP86R4XRxrDHF0VIx6ArM5/sTWtObY6U2aosgxbN6FUa1iNTUpMThk1sUfJOC6\
    +
     9474.      6s1SKo9D0g1NfiVmavyful/K7nZdDgutV1A26i7FR3r16bv3zz1cGw+ta17IX/+ripyutix3C\
    +
     9475.      6xNmCxs7uiqKu9/Zjjn06tblXpJxlaLF5Od0d5W9QhQrs2u6UN0trQlCyEK2j9VYgCEIDrhQN\
    +
     9476.      6c00rxg/FOfZ1N+nLV7RXDsYP+p0EzqKcuPujzuzEQsu2mFf4nYvf3Yp32rq/RYLetDLuOOTc\
    +
     9477.      60WXBtgoech7AHUxAxPBg81qWCsYlzTofRU5/MpuyNoegR6mCJO5ckrLOhWbG7xo/VGwGgpRb\
    +
     9478.      6+Ch+TmlcuY6Qct/2x3gxzeDUU9u+ltexrjelJ0VRR9KXH/AqrbYxHa0vmQ/kBnE5EORBK1ZH\
    +
     9479.      6mTSy7A8DJMgzzqDsu9ML5J3ufkuUNDCfN5UKAjBgw2I/QlS8MQ6o/ll9dTAdoM7HYtV4cNWE\
    +
     9480.      6U4pOl5Y4SIzdMbNSjXFmsBV1uXXf7GaBZZslpFGFiIpokSzxWj4hjlGl4VKJDACo7ScxQf29\
    +
     9481.      6kM8gHD3nUJkwkN2aW2TGttqwOrygJ7r9nYX2tYqy7Z3TQV5ocWzUI8l871y3LsQLoTgEO76B\
    +
     9482.      6Upp69hy6VKRpZvpvgfQ2T06qgXjxh38eatREitX6bzKggIYmN4sAkA3a5oeJZDK3ahQrVJwa\
    +
     9483.      6AD65cEGBkS/tKH9TtybiREEWCMcKD0HH0gELtjB+KNSk7bspmpr6eb0CscIiFyZpmXu8+gxw\
    +
     9484.      6O7pJNbAK2h9q2c5dMHBaoi5DylbNGdweVVdN3Jm9u6YXXlmx4nYY2vIPfSkrE/vyv9gn/Z+j\
    +
     9485.      6R3HKExaUhdV0Az77YWbQPhNfjw+F0vTteSMin+wIfxyPe0DEoI4uz6o2IXwsZC7sg8MicQ3o\
    +
     9486.      6wys+NJYKVW72YiVQ5LKDVwrEg2jNVM6XdNjbsHlRDcAkD08o5iWtFB2dVoydRmmDRLalE+4t\
    +
     9487.      63gBbAPa7n7qXXXbTZTJXZKy5+1W0K7dgYEcIlu90ovC0C+5gxXiKtZisT14qDJ7f2ksyK59U\
    +
     9488.      6r3QeHtBb24mPz7YDB3rgMTyUZ/fxM8h2i1Z21B8/VA5+9l7BKaOJZ15lWsyPv/z6CjU32ZKq\
    +
     9489.      6+QFeyUywxYnUxUmcQfGc1Sp69oE2n6zFL8BXf5rc3cJMM6S97gagTT1bi7cmAV4MibkC4rz/\
    +
     9490.      6icmmFtMlo5aN1Wp3uxsBfd4+9T42xmxvd79FV/hfuviBcrIaX092PrY5rle9FR4wTnDzrwj4\
    +
     9491.      67frD2d0KsMcdcADQ1Yu1LECg9Wj3yOS8OhrJdQBqXqsam17vmt2wjjjouHE/EO9sGPdqt23v\
    +
     9492.      6j8rL6wid6ulagtNK5p1hjRkFtUxTIaZnIXk63Zb3P0t5MQ+3vxHIFrmgAdWwiDuA67tbVIF6\
    +
     9493.      6wJ53z0uhyhsfH9bgF0kPT9v2hrT3HKIBgUXIYoxsVU+uryemiUiQEwh+BfxP//qLShlumR26\
    +
     9494.      6I8OqjD+x3hHDj/IrEWmvyL6ioG/atfxe+5GzIqRgfaoayWOiTk+YixO15KDO6Os3XACDjboe\
    +
     9495.      6ryXXOuEmTpDsc7czk+H04Kw1PNJazW32CAURHwBldqK0/nqYHtcrtLyyTYmoD8hbcnJUfa3U\
    +
     9496.      63FxWNus7uic3Qm1BzEecJW0MAz+W2CyN9FLIy+EpSy6CjkXsllZw1uBs1SxrQWM97/vnHu7m\
    +
     9497.      6OtrkRl8AtBN3RDxI/fg7dZLLtDFYuCYYPMwXiO6ZIpwJ1GGydI9oUYYgnQQKDKoMTcwsjrfe\
    +
     9498.      6Tcht6y18bLcpNfX41WE27447vLNzHuF+j15co5N7Py8vKUpTCoghHMEYKkM6y02lvX+9XiFg\
    +
     9499.      6xBKMRNiwX69+LJb2Xa5WGqo7Rlk0cxsLVd0l2UXAW5jORg31sFMKYWXsDcRUKRDP8Q87OjiM\
    +
     9500.      6dI1hNEt43netf8rOyfp+L58fq3holY9gxXwRJLY6gahgLQi4hS8w9LS+rFcJtdSCBrQLWsMs\
    +
     9501.      6aDg/n8/P8/N+fcyoLepYr3W/CIUT7HsRQTtkduddbVfbo6Twt6fyJVPRrUGqRkWp3rdry65v\
    +
     9502.      6sPYInyq1mPHrQDrqGJYI/LzA/QAzAXLnx+lu9uxHTEka9xgWgRvqEioskh+UWgD4nDvTAxaz\
    +
     9503.      63v9BqqmFuQwy1wSXye1Df1NXVF7G8bUFxUE4F9axG5fm+vFQJvP8iuYjrFveB6++AqmJTQJ0\
    +
     9504.      69GHjbPhzdSzkZGxokQzONVs0R0FCPJz1hJKbvDKsaj9hT0vp/gH5oiT8pAbWsBChwAbxHgDd\
    +
     9505.      659iJVZE3bAzPRN1RuG+MT7th+J3i6KFwVJvPvsGRDIZW4P2rVfiKjDVBM2Va+w6PgI0c5u3K\
    +
     9506.      6O7MrWryPhXFFdBoAwi2JCaW9sZ3fTagQ4Tld6u4djwcWzeCdiYoeNbfalsRYo740afYQ1Rid\
    +
     9507.      6Bp/E9mbcTemEjoWWXIU7I5nK5H/BEqmZnPMyhDV234BTLQKCe6nhU+frwQo1gNFWf+eQGN62\
    +
     9508.      6aeF7BuzaN/94W2xlKd8t8KMA/3uoxymFt19OlCjYZeaMWbTKM9Yog9zDhptYMOzIQAoO7kn6\
    +
     9509.      6nTao8CxjrRRgjKe7mKa+tzuufhAAZtgjA92THkulWvIzEi0++j1DvXMnupDUS8aVusWain+c\
    +
     9510.      6CcvmR5orC+RcJs3wVahLYyEcqbvAS2e0QJ6BlU36R/IEd9Aol9q+M+UGvlo8EyRzISvqusNS\
    +
     9511.      67ePQ6cQzG1s725db4jNYNHAfF3KFG8wHqDwZDpWDsJ5qRLXR1ulFx85GhkypPubYaCiOQ5DR\
    +
     9512.      6PQUiNpgk4fLJHenSMLMiswXsqW4Cpln1rFoHzpOoBbuZIixmVyhKajeqlFmp8zNAEsbEJz0g\
    +
     9513.      6X0qlQuykZhf82pkhq2hWtCtYUdBODn6iPTBJT5Zk8IqFxqfBeFKjXk/sMeumhT8muOtq2Bgn\
    +
     9514.      6dR4fj6RoOi0zI25kajAXlDZhUhS39jipk39h/69AfDPBLmOxhDj7Lg/WUTbOwJiJ3p7WtOpm\
    +
     9515.      6ypARmhorQifINNm1CNS99GfDcLbD8sn8Fvlmvn7CmW65Pdmu6bKtuE0tn7NglIX1e/JAJP+G\
    +
     9516.      6gB3At7cSOp92rl0lp0pp0xVb5YaQedwGgcJA1pT4cy24lS+jvzDw86YTfb2igJm5MysHmejW\
    +
     9517.      6ZTGXpoAoLBLucUGEz/DwbjqOdzGAl5jy5VoCQws5zNYl4SVt030aZulYMgpDBPZd+kL0wV+w\
    +
     9518.      6nob2LPRDQGEbdUoeFm4fEKio9c/ferVlpSO8Bx7OFZyHip1PIizvoqFe02kpmS17TvIOty42\
    +
     9519.      6+Q0QaCnOpeLsPwwo+vixIeIeUjucUsKejVlez35qyuC0mm5pJJJLEVP2JAe/LTOwUUfKJkNy\
    +
     9520.      6lEe3Kdth241ZNQmkVcAIh6DZJBzvQov5fn3JZA0phBWdNq5iTsm5N2D8gyve3V3X2o3zF3VY\
    +
     9521.      6OqEBgTIADAbC69z7vOKJjGOzHRmUUwLU66iabtIbqR6SPOHCL+fCTfvpKcB/oG2p3wRKErEJ\
    +
     9522.      6v1YOfu9iaKEMLXS3ptdH8fwN2Rdww9bZ7rFa2bwrzcyux3o+hPV6bJZpb71j7lLAdzge3VX7\
    +
     9523.      69uSCdz6f/FDb7+wzWnbbDSPj9R20+PybDUm/lVAsTuF0aycFQwJfPCUwcBvCGWEq6xoTIEOy\
    +
     9524.      6b0bLta20+LYRYdyEceX7ypfezQKIy5OvJTAHCJy/WyOYaDVyPucMsHnZ0GCH75Cd//te1Bv2\
    +
     9525.      6RkMykqYurBiNbuH3Xfuprirr4Dd453O6abAYGb5tw1d6wrBL8p1J1Sx9Lgw7yxqYn0FTrc0y\
    +
     9526.      659yLlV+4zIkLfZlPFRVnanHpTyrIlpn4lGkt269+JXnIWhEQWNvuVsrt531jr+8AHkVZfQU8\
    +
     9527.      68U/4AUZMuOj5iliigFrof/usmloYEI1f8erhJku75snYW7YmFmUcoC7UtG/KfJRuz6j0tWPa\
    +
     9528.      656J5QA0rJHwSIhNT4GWvez19HT2lia+Pz7/+MVEWlvjY6+9P85a0y9qWkTzQ7nF0wDXpQpw3\
    +
     9529.      6K4xnfK2L08b5MrxdeI+DDfVDeV2JY0Fp6KH602tj2MbxxKM8oG+wTkE/dr8jyo4Sfs/IV6uf\
    +
     9530.      6+IIXpH2Nca1+WCJV5qEv193bcUELLR4iFu83xUedKy9353tn+3o01dF2bNEQ3fK9Q5tCXrCi\
    +
     9531.      6La+woCuvEeYrr+PiN2+i2V/eDJck580pyra8BV5ZIZbpe3kr5vJD3pqoGsnbcl/d+ndvR23b\
    +
     9532.      6K41M4dKwaAwDaMA1gZGBoQWAcYE9mYkmQOnAjkaG41FkGkIP2BAIgKvUvzhpE5JbA6lze2iL\
    +
     9533.      65Nr+AwiDo2W4BStvK30dKy0JGNbzAY5akexsrV0xo5K8rY50LOTLvDyukIZNbRLKOCk18mD3\
    +
     9534.      6WxmZGlsCMxNdGFYGNJnetUWyCPgo4BONEL4I9b8UeEBGYXuCdCd+DkctrqVLYXGSfE46kvAu\
    +
     9535.      6+ltK5SRxQPnjUqyJXsSYs6VY6WPKfns9+IXjHhd5wQvGipgFdMwVEQ+A7a2AAS0clQwH7KHW\
    +
     9536.      6SEGjhnklSVRghMtPy6gEtJRIKJeYkpQyQiequQunFOOU4BLdK1yp55olZS6npyPhMnvK7xIa\
    +
     9537.      6pyNj+JctcQLXenBOCms46aMkenIx45WpXqxxVJQLz/vgpmAVa0fmDv6Pue9xVTBPfVxCUGfj\
    +
     9538.      61R8uVi8Zu9nRFqk/t0gR6wmWOlzuKRqk33HpO8qQ+nbGoEZLL/0Va156SJ+u+t86/os7ic49\
    +
     9539.      6/7xoEqvL+2E8VOyCTuT/7j269Zy4jUtN+g4="
    +
     9540.      6    ) format("woff2");
    +
     9541.      6}
    +
     9542.      6.JSLINT_,
    +
     9543.      6.JSLINT_ address,
    +
     9544.      6.JSLINT_ button,
    +
     9545.      6.JSLINT_ cite,
    +
     9546.      6.JSLINT_ dd,
    +
     9547.      6.JSLINT_ dfn,
    +
     9548.      6.JSLINT_ dl,
    +
     9549.      6.JSLINT_ dt,
    +
     9550.      6.JSLINT_ fieldset,
    +
     9551.      6.JSLINT_ fieldset > div,
    +
     9552.      6.JSLINT_ input,
    +
     9553.      6.JSLINT_ label,
    +
     9554.      6.JSLINT_ legend,
    +
     9555.      6.JSLINT_ ol,
    +
     9556.      6.JSLINT_ samp,
    +
     9557.      6.JSLINT_ style,
    +
     9558.      6.JSLINT_ textarea,
    +
     9559.      6.JSLINT_ ul {
    +
     9560.      6    border: 0;
    +
     9561.      6    box-sizing: border-box;
    +
     9562.      6    margin: 0;
    +
     9563.      6    padding: 0;
    +
     9564.      6}
    +
     9565.      6/* disable text inflation algorithm used on some smartphones and tablets */
    +
     9566.      6.JSLINT_ {
    +
     9567.      6    -ms-text-size-adjust: none;
    +
     9568.      6    -webkit-text-size-adjust: none;
    +
     9569.      6    text-size-adjust: none;
    +
     9570.      6}
    +
     9571.      6.JSLINT_REPORT_ div {
    +
     9572.      6    box-sizing: border-box;
    +
     9573.      6}
    +
     9574.      6/*csslint ignore:end*/
    +
     9575.      6
    +
     9576.      6/* css - jslint_report - font */
    +
     9577.      6.JSLINT_,
    +
     9578.      6.JSLINT_ fieldset legend,
    +
     9579.      6.JSLINT_ .center {
    +
     9580.      6    font-family: daley, sans-serif;
    +
     9581.      6    font-size: 14px;
    +
     9582.      6}
    +
     9583.      6.JSLINT_ fieldset textarea,
    +
     9584.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS dt,
    +
     9585.      6.JSLINT_ #JSLINT_REPORT_WARNINGS samp {
    +
     9586.      6    font-size: 12px;
    +
     9587.      6}
    +
     9588.      6.JSLINT_ fieldset textarea,
    +
     9589.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS > div {
    +
     9590.      6    font-family: monospace;
    +
     9591.      6}
    +
     9592.      6.JSLINT_ fieldset > div {
    +
     9593.      6    font-family: sans-serif;
    +
     9594.      6}
    +
     9595.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dfn {
    +
     9596.      6    font-style: normal;
    +
     9597.      6    font-weight: bold;
    +
     9598.      6}
    +
     9599.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dt {
    +
     9600.      6    font-style: italic;
    +
     9601.      6}
    +
     9602.      6.JSLINT_ #JSLINT_REPORT_TITLE {
    +
     9603.      6    font-size: 32px;
    +
     9604.      6}
    +
     9605.      6
    +
     9606.      6/* css - jslint_report - general */
    +
     9607.      6.JSLINT_ {
    +
     9608.      6    background: antiquewhite;
    +
     9609.      6}
    +
     9610.      6.JSLINT_ fieldset {
    +
     9611.      6    background: gainsboro;
    +
     9612.      6    clear: both;
    +
     9613.      6    margin: 16px 40px;
    +
     9614.      6    width: auto;
    +
     9615.      6}
    +
     9616.      6.JSLINT_ fieldset address {
    +
     9617.      6    float: right;
    +
     9618.      6}
    +
     9619.      6.JSLINT_ fieldset legend,
    +
     9620.      6.JSLINT_ .center {
    +
     9621.      6    text-align: center;
    +
     9622.      6}
    +
     9623.      6.JSLINT_ fieldset legend {
    +
     9624.      6    background: darkslategray;
    +
     9625.      6    color: white;
    +
     9626.      6    padding: 4px 0;
    +
     9627.      6    width: 100%;
    +
     9628.      6}
    +
     9629.      6.JSLINT_ fieldset textarea {
    +
     9630.      6    padding: 4px;
    +
     9631.      6    resize: none;
    +
     9632.      6    white-space: pre;
    +
     9633.      6    width: 100%;
    +
     9634.      6}
    +
     9635.      6.JSLINT_ fieldset textarea::selection {
    +
     9636.      6    background: wheat;
    +
     9637.      6}
    +
     9638.      6.JSLINT_ fieldset > div {
    +
     9639.      6    padding: 16px;
    +
     9640.      6    width: 100%;
    +
     9641.      6    word-wrap: break-word;
    +
     9642.      6}
    +
     9643.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level {
    +
     9644.      6    background: cornsilk;
    +
     9645.      6    padding: 8px 16px;
    +
     9646.      6}
    +
     9647.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dd {
    +
     9648.      6    line-height: 20px;
    +
     9649.      6    padding-left: 120px;
    +
     9650.      6}
    +
     9651.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dfn {
    +
     9652.      6    display: block;
    +
     9653.      6    line-height: 20px;
    +
     9654.      6}
    +
     9655.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dl {
    +
     9656.      6    position: relative
    +
     9657.      6}
    +
     9658.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dt {
    +
     9659.      6    line-height: 20px;
    +
     9660.      6    position: absolute;
    +
     9661.      6    text-align: right;
    +
     9662.      6    width: 100px;
    +
     9663.      6}
    +
     9664.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level0 {
    +
     9665.      6    background: white;
    +
     9666.      6}
    +
     9667.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level1 {
    +
     9668.      6    /* yellow */
    +
     9669.      6    background: #ffffe0;
    +
     9670.      6    margin-left: 16px;
    +
     9671.      6}
    +
     9672.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level2 {
    +
     9673.      6    /* green */
    +
     9674.      6    background: #e0ffe0;
    +
     9675.      6    margin-left: 32px;
    +
     9676.      6}
    +
     9677.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level3 {
    +
     9678.      6    /* blue */
    +
     9679.      6    background: #D0D0ff;
    +
     9680.      6    margin-left: 48px;
    +
     9681.      6}
    +
     9682.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level4 {
    +
     9683.      6    /* purple */
    +
     9684.      6    background: #ffe0ff;
    +
     9685.      6    margin-left: 64px;
    +
     9686.      6}
    +
     9687.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level5 {
    +
     9688.      6    /* red */
    +
     9689.      6    background: #ffe0e0;
    +
     9690.      6    margin-left: 80px;
    +
     9691.      6}
    +
     9692.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level6 {
    +
     9693.      6    /* orange */
    +
     9694.      6    background: #ffe390;
    +
     9695.      6    margin-left: 96px;
    +
     9696.      6}
    +
     9697.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level7 {
    +
     9698.      6    /* gray */
    +
     9699.      6    background: #e0e0e0;
    +
     9700.      6    margin-left: 112px;
    +
     9701.      6}
    +
     9702.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level8 {
    +
     9703.      6    margin-left: 128px;
    +
     9704.      6}
    +
     9705.      6.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level9 {
    +
     9706.      6    margin-left: 144px;
    +
     9707.      6}
    +
     9708.      6.JSLINT_ #JSLINT_REPORT_PROPERTIES {
    +
     9709.      6    background: transparent;
    +
     9710.      6}
    +
     9711.      6.JSLINT_ #JSLINT_REPORT_PROPERTIES textarea {
    +
     9712.      6    background: honeydew;
    +
     9713.      6    height: 100px;
    +
     9714.      6}
    +
     9715.      6.JSLINT_ #JSLINT_REPORT_TITLE {
    +
     9716.      6    color: darkslategray;
    +
     9717.      6    padding-top: 16px;
    +
     9718.      6}
    +
     9719.      6.JSLINT_ #JSLINT_REPORT_WARNINGS cite {
    +
     9720.      6    display: block;
    +
     9721.      6    margin: 16px 0 4px 0;
    +
     9722.      6    overflow-x: hidden;
    +
     9723.      6    white-space: pre-line;
    +
     9724.      6}
    +
     9725.      6.JSLINT_ #JSLINT_REPORT_WARNINGS cite:nth-child(1) {
    +
     9726.      6    margin-top: 0;
    +
     9727.      6}
    +
     9728.      6.JSLINT_ #JSLINT_REPORT_WARNINGS samp {
    +
     9729.      6    background: lavenderblush;
    +
     9730.      6    display: block;
    +
     9731.      6    padding: 4px;
    +
     9732.      6    white-space: pre-wrap;
    +
     9733.      6}
    +
     9734.      6.JSLINT_ #JSLINT_REPORT_WARNINGS > div {
    +
     9735.      6    background: pink;
    +
     9736.      6    max-height: 400px;
    +
     9737.      6    overflow-y: auto;
    +
     9738.      6}
    +
     9739.      6.JSLINT_ #JSLINT_REPORT_WARNINGS > legend {
    +
     9740.      6/* Google Lighthouse Accessibility - Background and foreground colors do not */
    +
     9741.      6/* have a sufficient contrast ratio. */
    +
     9742.      6    /* background: indianred; */
    +
     9743.      6    background: #b44;
    +
     9744.      6}
    +
     9745.      6</style>
    +
     9746.      6            `).trim() + "\n";
    +
     9747.      6
    +
     9748.      6// Produce the Title.
    +
     9749.      6
    +
     9750.      6    html += "<div class=\"center\" id=\"JSLINT_REPORT_TITLE\">\n";
    +
     9751.      6    html += "JSLint Report\n";
    +
     9752.      6    html += "</div>\n";
    +
     9753.      6
    +
     9754.      6// Produce the HTML Error Report.
    +
     9755.      6// <cite>
    +
     9756.      6//     <address>LINE_NUMBER</address>
    +
     9757.      6//     MESSAGE
    +
     9758.      6// </cite>
    +
     9759.      6// <samp>EVIDENCE</samp>
    +
     9760.      6
    +
     9761.      6    html += "<fieldset id=\"JSLINT_REPORT_WARNINGS\">\n";
    +
     9762.      6    html += "<legend>Report: Warnings (" + warnings.length + ")</legend>\n";
    +
     9763.      6    html += "<div>\n";
    +
     9764.      1    if (stop) {
    +
     9765.      1        html += "<div class=\"center\">JSLint was unable to finish.</div>\n";
    +
     9766.      1    }
    +
     9767.      7    warnings.forEach(function ({
    +
     9768.      7        column,
    +
     9769.      7        line,
    +
     9770.      7        line_source,
    +
     9771.      7        message,
    +
     9772.      7        stack_trace = ""
    +
     9773.      7    }, ii) {
    +
     9774.      7        html += (
    +
     9775.      7            "<cite>"
    +
     9776.      7            + address(line, column)
    +
     9777.      7            + htmlEscape((ii + 1) + ". " + message)
    +
     9778.      7            + "</cite>"
    +
     9779.      7            + "<samp>"
    +
     9780.      7            + htmlEscape(line_source.slice(0, 400) + "\n" + stack_trace)
    +
     9781.      7            + "</samp>\n"
    +
     9782.      7        );
    +
     9783.      7    });
    +
     9784.      3    if (warnings.length === 0) {
    +
     9785.      3        html += "<div class=\"center\">There are no warnings.</div>\n";
    +
     9786.      3    }
    +
     9787.      6    html += "</div>\n";
    +
     9788.      6    html += "</fieldset>\n";
    +
     9789.      6
    +
     9790.      6// Produce the /*property*/ directive.
    +
     9791.      6
    +
     9792.      6    html += "<fieldset id=\"JSLINT_REPORT_PROPERTIES\">\n";
    +
     9793.      6    html += (
    +
     9794.      6        "<legend>Report: Properties ("
    +
     9795.      6        + Object.keys(property).length
    +
     9796.      6        + ")</legend>\n"
    +
     9797.      6    );
    +
     9798.      6    html += "<label>\n";
    +
     9799.      6    html += "<textarea readonly>";
    +
     9800.      6    html += "/*property";
    +
     9801.    301    Object.keys(property).sort().forEach(function (key, ii) {
    +
     9802.    300        if (ii !== 0) {
    +
     9803.    300            html += ",";
    +
     9804.    300            length_80 += 2;
    +
     9805.    300        }
    +
     9806.     42        if (length_80 + key.length >= 80) {
    +
     9807.     42            length_80 = 4;
    +
     9808.     42            html += "\n   ";
    +
     9809.     42        }
    +
     9810.    301        html += " " + key;
    +
     9811.    301        length_80 += key.length;
    +
     9812.    301    });
    +
     9813.      6    html += "\n*/\n";
    +
     9814.      6    html += "</textarea>\n";
    +
     9815.      6    html += "</label>\n";
    +
     9816.      6    html += "</fieldset>\n";
    +
     9817.      6
    +
     9818.      6// Produce the HTML Function Report.
    +
     9819.      6// <div class=LEVEL>
    +
     9820.      6//     <address>LINE_NUMBER</address>
    +
     9821.      6//     <dfn>FUNCTION_NAME_AND_SIGNATURE</dfn>
    +
     9822.      6//     <dl>
    +
     9823.      6//         <dt>DETAIL</dt>
    +
     9824.      6//         <dd>NAMES</dd>
    +
     9825.      6//     </dl>
    +
     9826.      6// </div>
    +
     9827.      6
    +
     9828.      6    html += "<fieldset id=\"JSLINT_REPORT_FUNCTIONS\">\n";
    +
     9829.      6    html += "<legend>Report: Functions (" + functions.length + ")</legend>\n";
    +
     9830.      6    html += "<div>\n";
    +
     9831.      2    if (json) {
    +
     9832.      2
    +
     9833.      2// Bugfix - fix website crashing when linting pure json-object.
    +
     9834.      2// return (
    +
     9835.      2
    +
     9836.      2        html += (
    +
     9837.      2            warnings.length === 0
    +
     9838.      2            ? "<div class=\"center\">JSON: good.</div>\n"
    +
     9839.      2            : "<div class=\"center\">JSON: bad.</div>\n"
    +
     9840.      2        );
    +
     9841.      4    } else if (functions.length === 0) {
    +
     9842.      4        html += "<div class=\"center\">There are no functions.</div>\n";
    +
     9843.      4    }
    +
     9844.      6    exports = Object.keys(exports).sort();
    +
     9845.      6    froms.sort();
    +
     9846.      6    global = Object.keys(global.context).sort();
    +
     9847.      6    module = (
    +
     9848.      6        module
    +
     9849.      1        ? "module"
    +
     9850.      5        : "global"
    +
     9851.      6    );
    +
     9852.      3    if (global.length + froms.length + exports.length > 0) {
    +
     9853.      3        if (functions.length === 0) {
    +
     9854.      3            html += "<br>\n";
    +
     9855.      3        }
    +
     9856.      3        html += "<div class=\"level level0\">\n";
    +
     9857.      3        html += detail(module, global);
    +
     9858.      3        html += detail("import from", froms);
    +
     9859.      3        html += detail("export", exports);
    +
     9860.      3        html += "</div>\n";
    +
     9861.      3    }
    +
     9862.    321    functions.forEach(function (the_function) {
    +
     9863.    321        let {
    +
     9864.    321            context,
    +
     9865.    321            from,
    +
     9866.    321            id,
    +
     9867.    321            level,
    +
     9868.    321            line,
    +
     9869.    321            name,
    +
     9870.    321
    +
     9871.    321// Bugfix - fix html-report from crashing if parameters is undefined.
    +
     9872.    321
    +
     9873.    321            parameters = [],
    +
     9874.    321            signature
    +
     9875.    321        } = the_function;
    +
     9876.    321        let list = Object.keys(context);
    +
     9877.    321        let params;
    +
     9878.    321        html += (
    +
     9879.    321            "<div class=\"level level" + htmlEscape(level) + "\">"
    +
     9880.    321            + address(line, from + 1)
    +
     9881.    321            + "<dfn>"
    +
     9882.    321            + (
    +
     9883.    321                id === "=>"
    +
     9884.      1                ? (
    +
     9885.      1                    "\u00ab" + htmlEscape(name) + "\u00bb"
    +
     9886.      1                    + htmlEscape(signature)
    +
     9887.      1                    + " =>"
    +
     9888.      1                )
    +
     9889.    320                : (
    +
     9890.    320                    typeof name === "string"
    +
     9891.    320                    ? "\u00ab" + htmlEscape(name) + "\u00bb"
    +
     9892.    320                    : htmlEscape(name.id)
    +
     9893.    320                ) + htmlEscape(signature)
    +
     9894.    321            )
    +
     9895.    321            + "</dfn>"
    +
     9896.    321        );
    +
     9897.    321        params = [];
    +
     9898.    470        parameters.forEach(function extract({
    +
     9899.    470            id,
    +
     9900.    470            names
    +
     9901.    470        }) {
    +
     9902.    470            switch (id) {
    +
     9903.      6            case "[":
    +
     9904.     42            case "{":
    +
     9905.     42
    +
     9906.     42// Recurse extract().
    +
     9907.     42
    +
     9908.     42                names.forEach(extract);
    +
     9909.     42                break;
    +
     9910.      4            case "ignore":
    +
     9911.      4                break;
    +
     9912.    424            default:
    +
     9913.    424                params.push(id);
    +
     9914.    470            }
    +
     9915.    470        });
    +
     9916.    321        html += detail("parameter", params.sort());
    +
     9917.    321        list.sort();
    +
     9918.   2203        html += detail("variable", list.filter(function (id) {
    +
     9919.   2203            return (
    +
     9920.   2203                context[id].role === "variable"
    +
     9921.   1693                && context[id].parent === the_function
    +
     9922.   2203            );
    +
     9923.   2203        }));
    +
     9924.   2203        html += detail("exception", list.filter(function (id) {
    +
     9925.   2203            return context[id].role === "exception";
    +
     9926.   2203        }));
    +
     9927.   2203        html += detail("closure", list.filter(function (id) {
    +
     9928.   2203            return (
    +
     9929.   2203                context[id].closure === true
    +
     9930.   1494                && context[id].parent === the_function
    +
     9931.   2203            );
    +
     9932.   2203        }));
    +
     9933.   2203        html += detail("outer", list.filter(function (id) {
    +
     9934.   2203            return (
    +
     9935.   2203                context[id].parent !== the_function
    +
     9936.   1190                && context[id].parent.id !== "(global)"
    +
     9937.   2203            );
    +
     9938.   2203        }));
    +
     9939.   2203        html += detail(module, list.filter(function (id) {
    +
     9940.   2203            return context[id].parent.id === "(global)";
    +
     9941.   2203        }));
    +
     9942.   2203        html += detail("label", list.filter(function (id) {
    +
     9943.   2203            return context[id].role === "label";
    +
     9944.   2203        }));
    +
     9945.    321        html += "</div>\n";
    +
     9946.    321    });
    +
     9947.      6    html += "</div>\n";
    +
     9948.      6    html += "</fieldset>\n";
    +
     9949.      6    return html;
    +
     9950.      6}
    +
     9951.      1
    +
     9952.     10async function jstestDescribe(description, testFunction) {
    +
     9953.     10
    +
     9954.     10// This function will create-and-run test-group <testFunction>
    +
     9955.     10// with given <description>.
    +
     9956.     10
    +
     9957.     10    let message;
    +
     9958.     10    let result;
    +
     9959.     10    let timerTimeout;
    +
     9960.     10
    +
     9961.     10// Init jstestTimeStart.
    +
     9962.     10
    +
     9963.      1    if (jstestTimeStart === undefined) {
    +
     9964.      1        jstestTimeStart = jstestTimeStart || Date.now();
    +
     9965.      1        process.on("exit", jstestOnExit);
    +
     9966.      1    }
    +
     9967.     10
    +
     9968.     10// PR-457 - Wait awhile for imports to initialize.
    +
     9969.     10
    +
     9970.     10    await new Promise(function (resolve) {
    +
     9971.     10        setTimeout(resolve);
    +
     9972.     10    });
    +
     9973.     10
    +
     9974.     10// Init jstestItList.
    +
     9975.     10
    +
     9976.     10    jstestItList = [];
    +
     9977.     10    testFunction();
    +
     9978.     10
    +
     9979.     10// Wait for jstestItList to resolve.
    +
     9980.     10
    +
     9981.     10    timerTimeout = setTimeout(noop, 0x7fffffff);
    +
     9982.     10    result = await Promise.all(jstestItList);
    +
     9983.     10    clearTimeout(timerTimeout);
    +
     9984.     10
    +
     9985.     10// Print test results.
    +
     9986.     10
    +
     9987.     10    message = (
    +
     9988.     10        "\n  " + (Date.now() - jstestTimeStart) + "ms"
    +
     9989.     10        + " - test describe - " + description + "\n"
    +
     9990.     66        + result.map(function ([
    +
     9991.     66            err, description, mode
    +
     9992.     66        ]) {
    +
     9993.     66            jstestItCount += 1;
    +
     9994.      1            if (err) {
    +
     9995.      1                jstestCountFailed += 1;
    +
     9996.      1                err = (
    +
     9997.      1                    "    \u001b[31m\u2718 " + jstestItCount + ". test it - "
    +
     9998.      1                    + description + "\n" + err.stack + "\u001b[39m"
    +
     9999.      1                );
    +
    10000.      1                if (mode === "pass") {
    +
    10001.      1                    jstestCountFailed -= 1;
    +
    10002.      1                    err = "";
    +
    10003.      1                }
    +
    10004.      1            }
    +
    10005.     66            return err || (
    +
    10006.     66                "    \u001b[32m\u2714 " + jstestItCount + ". test it - "
    +
    10007.     66                + description + "\u001b[39m"
    +
    10008.     66            );
    +
    10009.     66        }).join("\n")
    +
    10010.     10    );
    +
    10011.     10    console.error(message);
    +
    10012.     10}
    +
    10013.      1
    +
    10014.     66function jstestIt(description, testFunction, mode) {
    +
    10015.     66
    +
    10016.     66// This function will create-and-run test-case <testFunction>
    +
    10017.     66// inside current test-group with given <description>.
    +
    10018.     66
    +
    10019.     66    jstestCountTotal += 1;
    +
    10020.     66    jstestItList.push(new Promise(async function (resolve) {
    +
    10021.     66        let err;
    +
    10022.     66        try {
    +
    10023.     65            await testFunction();
    +
    10024.     65        } catch (errCaught) {
    +
    10025.      1            err = errCaught;
    +
    10026.      1        }
    +
    10027.     66        resolve([err, description, mode]);
    +
    10028.     66    }));
    +
    10029.     66}
    +
    10030.      1
    +
    10031.      2function jstestOnExit(exitCode, mode) {
    +
    10032.      2
    +
    10033.      2// This function will on process-exit, print test-report
    +
    10034.      2// and exit with non-zero exit-code if any test failed.
    +
    10035.      2
    +
    10036.      2    let message = (
    +
    10037.      2        (
    +
    10038.      2            (jstestCountFailed || mode === "testsFailed")
    +
    10039.      1            ? "\n\u001b[31m"
    +
    10040.      1            : "\n\u001b[32m"
    +
    10041.      2        )
    +
    10042.      2        + "  tests total  - " + jstestCountTotal + "\n"
    +
    10043.      2        + "  tests failed - " + jstestCountFailed + "\n"
    +
    10044.      2        + "\n"
    +
    10045.      2        + "  time finished - "
    +
    10046.      2        + Number(Date.now() - jstestTimeStart).toLocaleString()
    +
    10047.      2        + " ms\n"
    +
    10048.      2        + "\u001b[39m"
    +
    10049.      2    );
    +
    10050.      1    if (mode !== "testsFailed") {
    +
    10051.      1        console.error(message);
    +
    10052.      1    }
    +
    10053.      2    process.exitCode = exitCode || jstestCountFailed;
    +
    10054.      2    return message;
    +
    10055.      2}
    +
    10056.      1
    +
    10057.    107async function moduleFsInit() {
    +
    10058.    107
    +
    10059.    107// This function will import nodejs builtin-modules if they have not yet been
    +
    10060.    107// imported.
    +
    10061.    107
    +
    10062.    107// State 3 - Modules already imported.
    +
    10063.    107
    +
    10064.    104    if (moduleFs !== undefined) {
    +
    10065.    104        return;
    +
    10066.    104    }
    +
    10067.      3
    +
    10068.      3// State 2 - Wait while modules are importing.
    +
    10069.      3
    +
    10070.      3    if (moduleFsInitResolveList !== undefined) {
    +
    10071.      2        return new Promise(function (resolve) {
    +
    10072.      2            moduleFsInitResolveList.push(resolve);
    +
    10073.      2        });
    +
    10074.      2    }
    +
    10075.      1
    +
    10076.      1// State 1 - Start importing modules.
    +
    10077.      1
    +
    10078.      1    moduleFsInitResolveList = [];
    +
    10079.      1    [
    +
    10080.      1        moduleChildProcess,
    +
    10081.      1        moduleFs,
    +
    10082.      1        modulePath,
    +
    10083.      1        moduleUrl
    +
    10084.      1    ] = await Promise.all([
    +
    10085.      1        import("child_process"),
    +
    10086.      1        import("fs"),
    +
    10087.      1        import("path"),
    +
    10088.      1        import("url")
    +
    10089.      1    ]);
    +
    10090.      2    while (moduleFsInitResolveList.length > 0) {
    +
    10091.      2        moduleFsInitResolveList.shift()();
    +
    10092.      2    }
    +
    10093.    107}
    +
    10094.      1
    +
    10095.   2728function noop(val) {
    +
    10096.   2728
    +
    10097.   2728// This function will do nothing except return <val>.
    +
    10098.   2728
    +
    10099.   2728    return val;
    +
    10100.   2728}
    +
    10101.      1
    +
    10102.  12919function objectDeepCopyWithKeysSorted(obj) {
    +
    10103.  12919
    +
    10104.  12919// This function will recursively deep-copy <obj> with keys sorted.
    +
    10105.  12919
    +
    10106.  12919    let sorted;
    +
    10107.   8995    if (typeof obj !== "object" || !obj) {
    +
    10108.   8995        return obj;
    +
    10109.   8995    }
    +
    10110.   3924
    +
    10111.   3924// Recursively deep-copy list with child-keys sorted.
    +
    10112.   3924
    +
    10113.   3924    if (Array.isArray(obj)) {
    +
    10114.   1338        return obj.map(objectDeepCopyWithKeysSorted);
    +
    10115.   2586    }
    +
    10116.   2586
    +
    10117.   2586// Recursively deep-copy obj with keys sorted.
    +
    10118.   2586
    +
    10119.   2586    sorted = Object.create(null);
    +
    10120.   7457    Object.keys(obj).sort().forEach(function (key) {
    +
    10121.   7457        sorted[key] = objectDeepCopyWithKeysSorted(obj[key]);
    +
    10122.   7457    });
    +
    10123.   2586    return sorted;
    +
    10124.   2586}
    +
    10125.      1
    +
    10126.   2791function object_assign_from_list(dict, list, val) {
    +
    10127.   2791
    +
    10128.   2791// Assign each property-name from <list> to <dict>.
    +
    10129.   2791
    +
    10130.  89862    list.forEach(function (key) {
    +
    10131.  89862        dict[key] = val;
    +
    10132.  89862    });
    +
    10133.   2791    return dict;
    +
    10134.   2791}
    +
    10135.      1
    +
    10136.     97function v8CoverageListMerge(processCovs) {
    +
    10137.     97
    +
    10138.     97// This function is derived from MIT Licensed v8-coverage at
    +
    10139.     97// https://github.com/demurgos/v8-coverage/tree/master/ts
    +
    10140.     97// https://github.com/demurgos/v8-coverage/blob/master/ts/LICENSE.md
    +
    10141.     97//
    +
    10142.     97// Merges a list of v8 process coverages.
    +
    10143.     97// The result is normalized.
    +
    10144.     97// The input values may be mutated, it is not safe to use them after passing
    +
    10145.     97// them to this function.
    +
    10146.     97// The computation is synchronous.
    +
    10147.     97// @param processCovs Process coverages to merge.
    +
    10148.     97// @return Merged process coverage.
    +
    10149.     97
    +
    10150.     97    let resultMerged = [];      // List of merged scripts from processCovs.
    +
    10151.     97    let urlToScriptDict = new Map();    // Map scriptCov.url to scriptCovs.
    +
    10152.     97
    +
    10153.   1094    function compareRangeList(aa, bb) {
    +
    10154.   1094
    +
    10155.   1094// Compares two range coverages.
    +
    10156.   1094// The ranges are first ordered by ascending `startOffset` and then by
    +
    10157.   1094// descending `endOffset`.
    +
    10158.   1094// This corresponds to a pre-order tree traversal.
    +
    10159.   1094
    +
    10160.   1065        if (aa.startOffset !== bb.startOffset) {
    +
    10161.   1065            return aa.startOffset - bb.startOffset;
    +
    10162.   1065        }
    +
    10163.     29        return bb.endOffset - aa.endOffset;
    +
    10164.     29    }
    +
    10165.     97
    +
    10166.   1707    function dictKeyValueAppend(dict, key, val) {
    +
    10167.   1707
    +
    10168.   1707// This function will append <val> to list <dict>[<key>].
    +
    10169.   1707
    +
    10170.   1707        let list = dict.get(key);
    +
    10171.   1165        if (list === undefined) {
    +
    10172.   1165            list = [];
    +
    10173.   1165            dict.set(key, list);
    +
    10174.   1165        }
    +
    10175.   1707        list.push(val);
    +
    10176.   1707    }
    +
    10177.     97
    +
    10178.    384    function mergeTreeList(parentTrees) {
    +
    10179.    384
    +
    10180.    384// This function will return RangeTree object with <parentTrees> merged into
    +
    10181.    384// property-children.
    +
    10182.    384// @precondition Same `start` and `end` for all the parentTrees
    +
    10183.    384
    +
    10184.    119        if (parentTrees.length <= 1) {
    +
    10185.    119            return parentTrees[0];
    +
    10186.    265        }
    +
    10187.    265
    +
    10188.    265// new RangeTree().
    +
    10189.    265
    +
    10190.    265        return {
    +
    10191.    265
    +
    10192.    265// Merge parentTrees into property-children.
    +
    10193.    265
    +
    10194.    265            children: mergeTreeListToChildren(parentTrees),
    +
    10195.    669            delta: parentTrees.reduce(function (aa, bb) {
    +
    10196.    669                return aa + bb.delta;
    +
    10197.    669            }, 0),
    +
    10198.    265            end: parentTrees[0].end,
    +
    10199.    265            start: parentTrees[0].start
    +
    10200.    265        };
    +
    10201.    265    }
    +
    10202.     97
    +
    10203.    265    function mergeTreeListToChildren(parentTrees) {
    +
    10204.    265
    +
    10205.    265// This function will return <resultChildren> with <parentTrees> merged.
    +
    10206.    265
    +
    10207.    265        let openRange;
    +
    10208.    265        let parentToChildDict = new Map();      // Map parent to child.
    +
    10209.    265        let queueList;
    +
    10210.    265        let queueListIi = 0;
    +
    10211.    265        let queueOffset;
    +
    10212.    265        let queueTrees;
    +
    10213.    265        let resultChildren = [];
    +
    10214.    265        let startToTreeDict = new Map();        // Map tree.start to tree.
    +
    10215.    639        function nextXxx() {
    +
    10216.    639
    +
    10217.    639// Increment nextOffset, nextTrees.
    +
    10218.    639
    +
    10219.    639            let [
    +
    10220.    639                nextOffset, nextTrees
    +
    10221.    300            ] = queueList[queueListIi] || [];
    +
    10222.    639            let openRangeEnd;
    +
    10223.    583            if (queueTrees === undefined) {
    +
    10224.    583                queueListIi += 1;
    +
    10225.    583
    +
    10226.    583// Increment nextOffset, nextTrees.
    +
    10227.    583
    +
    10228.    583            } else if (nextOffset === undefined || nextOffset > queueOffset) {
    +
    10229.     56                nextOffset = queueOffset;
    +
    10230.     56                nextTrees = queueTrees;
    +
    10231.     56                queueTrees = undefined;
    +
    10232.     56
    +
    10233.     56// Concat queueTrees to nextTrees.
    +
    10234.     56
    +
    10235.     56            } else {
    +
    10236.     56                if (nextOffset === queueOffset) {
    +
    10237.     56                    queueTrees.forEach(function (tree) {
    +
    10238.     56                        nextTrees.push(tree);
    +
    10239.     56                    });
    +
    10240.     56                    queueTrees = undefined;
    +
    10241.     56                }
    +
    10242.     56                queueListIi += 1;
    +
    10243.     56            }
    +
    10244.    639
    +
    10245.    639// Reached end of queueList.
    +
    10246.    639
    +
    10247.    265            if (nextOffset === undefined) {
    +
    10248.    265                if (openRange !== undefined) {
    +
    10249.    265
    +
    10250.    265// Append nested-children from parentToChildDict (within openRange) to
    +
    10251.    265// resultChildren.
    +
    10252.    265
    +
    10253.    265                    resultAppendNextChild();
    +
    10254.    265                }
    +
    10255.    265                return true;
    +
    10256.    374            }
    +
    10257.    374            if (openRange !== undefined && openRange.end <= nextOffset) {
    +
    10258.    129
    +
    10259.    129// Append nested-children from parentToChildDict (within openRange) to
    +
    10260.    129// resultChildren.
    +
    10261.    129
    +
    10262.    129                resultAppendNextChild();
    +
    10263.    129                openRange = undefined;
    +
    10264.    374            }
    +
    10265.    374            if (openRange === undefined) {
    +
    10266.    292                openRangeEnd = nextOffset + 1;
    +
    10267.    502                nextTrees.forEach(function ({
    +
    10268.    502                    parentIi,
    +
    10269.    502                    tree
    +
    10270.    502                }) {
    +
    10271.    502                    openRangeEnd = Math.max(openRangeEnd, tree.end);
    +
    10272.    502
    +
    10273.    502// Append children from nextTrees to parentToChildDict.
    +
    10274.    502
    +
    10275.    502                    dictKeyValueAppend(parentToChildDict, parentIi, tree);
    +
    10276.    502                });
    +
    10277.    292                queueOffset = openRangeEnd;
    +
    10278.    292                openRange = {
    +
    10279.    292                    end: openRangeEnd,
    +
    10280.    292                    start: nextOffset
    +
    10281.    292                };
    +
    10282.    292            } else {
    +
    10283.    114                nextTrees.forEach(function ({
    +
    10284.    114                    parentIi,
    +
    10285.    114                    tree
    +
    10286.    114                }) {
    +
    10287.    114                    let right;
    +
    10288.     82                    if (tree.end > openRange.end) {
    +
    10289.     82                        right = treeSplit(tree, openRange.end);
    +
    10290.     82                        if (queueTrees === undefined) {
    +
    10291.     82                            queueTrees = [];
    +
    10292.     82                        }
    +
    10293.     82
    +
    10294.     82// new RangeTreeWithParent().
    +
    10295.     82
    +
    10296.     82                        queueTrees.push({
    +
    10297.     82                            parentIi,
    +
    10298.     82                            tree: right
    +
    10299.     82                        });
    +
    10300.     82                    }
    +
    10301.    114
    +
    10302.    114// Append children from nextTrees to parentToChildDict.
    +
    10303.    114
    +
    10304.    114                    dictKeyValueAppend(parentToChildDict, parentIi, tree);
    +
    10305.    114                });
    +
    10306.     82            }
    +
    10307.    639        }
    +
    10308.    292        function resultAppendNextChild() {
    +
    10309.    292
    +
    10310.    292// This function will append next child to <resultChildren>.
    +
    10311.    292
    +
    10312.    292            let treesMatching = [];
    +
    10313.    589            parentToChildDict.forEach(function (nested) {
    +
    10314.    589                if (
    +
    10315.    589                    nested.length === 1
    +
    10316.    563                    && nested[0].start === openRange.start
    +
    10317.    480                    && nested[0].end === openRange.end
    +
    10318.    468                ) {
    +
    10319.    468                    treesMatching.push(nested[0]);
    +
    10320.    468                } else {
    +
    10321.    121
    +
    10322.    121// new rangeTreeCreate().
    +
    10323.    121
    +
    10324.    121                    treesMatching.push({
    +
    10325.    121                        children: nested,
    +
    10326.    121                        delta: 0,
    +
    10327.    121                        end: openRange.end,
    +
    10328.    121                        start: openRange.start
    +
    10329.    121                    });
    +
    10330.    121                }
    +
    10331.    589            });
    +
    10332.    292            parentToChildDict.clear();
    +
    10333.    292
    +
    10334.    292// Recurse mergeTreeList().
    +
    10335.    292
    +
    10336.    292            resultChildren.push(mergeTreeList(treesMatching));
    +
    10337.    292        }
    +
    10338.     75        function treeSplit(tree, offset) {
    +
    10339.     75
    +
    10340.     75// This function will split <tree> along <offset> and return the right-side.
    +
    10341.     75// @precondition `tree.start < offset && offset < tree.end`
    +
    10342.     75// @return RangeTree Right part
    +
    10343.     75
    +
    10344.     75            let child;
    +
    10345.     75            let ii = 0;
    +
    10346.     75            let leftChildLen = tree.children.length;
    +
    10347.     75            let mid;
    +
    10348.     75            let resultTree;
    +
    10349.     75            let rightChildren;
    +
    10350.     75
    +
    10351.     75// TODO(perf): Binary search (check overhead) //jslint-ignore-line
    +
    10352.     75
    +
    10353.     19            while (ii < tree.children.length) {
    +
    10354.     19                child = tree.children[ii];
    +
    10355.     19                if (child.start < offset && offset < child.end) {
    +
    10356.     19
    +
    10357.     19// Recurse treeSplit().
    +
    10358.     19
    +
    10359.     19                    mid = treeSplit(child, offset);
    +
    10360.     19                    leftChildLen = ii + 1;
    +
    10361.     19                    break;
    +
    10362.     19                }
    +
    10363.     19                if (child.start >= offset) {
    +
    10364.     19                    leftChildLen = ii;
    +
    10365.     19                    break;
    +
    10366.     19                }
    +
    10367.     19                ii += 1;
    +
    10368.     19            }
    +
    10369.     75            rightChildren = tree.children.splice(
    +
    10370.     75                leftChildLen,
    +
    10371.     75                tree.children.length - leftChildLen
    +
    10372.     75            );
    +
    10373.      4            if (mid !== undefined) {
    +
    10374.      4                rightChildren.unshift(mid);
    +
    10375.      4            }
    +
    10376.     75
    +
    10377.     75// new rangeTreeCreate().
    +
    10378.     75
    +
    10379.     75            resultTree = {
    +
    10380.     75                children: rightChildren,
    +
    10381.     75                delta: tree.delta,
    +
    10382.     75                end: tree.end,
    +
    10383.     75                start: offset
    +
    10384.     75            };
    +
    10385.     75            tree.end = offset;
    +
    10386.     75            return resultTree;
    +
    10387.     75        }
    +
    10388.    265
    +
    10389.    265// Init startToTreeDict.
    +
    10390.    265
    +
    10391.    669        parentTrees.forEach(function (parentTree, parentIi) {
    +
    10392.    545            parentTree.children.forEach(function (child) {
    +
    10393.    545
    +
    10394.    545// Append child with child.start to startToTreeDict.
    +
    10395.    545
    +
    10396.    545                dictKeyValueAppend(startToTreeDict, child.start, {
    +
    10397.    545                    parentIi,
    +
    10398.    545                    tree: child
    +
    10399.    545                });
    +
    10400.    545            });
    +
    10401.    669        });
    +
    10402.    265
    +
    10403.    265// init queueList.
    +
    10404.    265
    +
    10405.    335        queueList = Array.from(startToTreeDict).map(function ([
    +
    10406.    335            startOffset, trees
    +
    10407.    335        ]) {
    +
    10408.    335
    +
    10409.    335// new StartEvent().
    +
    10410.    335
    +
    10411.    335            return [
    +
    10412.    335                startOffset, trees
    +
    10413.    335            ];
    +
    10414.    217        }).sort(function (aa, bb) {
    +
    10415.    217            return aa[0] - bb[0];
    +
    10416.    217        });
    +
    10417.    639        while (true) {
    +
    10418.    639            if (nextXxx()) {
    +
    10419.    639                break;
    +
    10420.    639            }
    +
    10421.    639        }
    +
    10422.    265        return resultChildren;
    +
    10423.    265    }
    +
    10424.     97
    +
    10425.    689    function sortFunc(funcCov) {
    +
    10426.    689
    +
    10427.    689// This function will normalize-and-sort <funcCov>.ranges.
    +
    10428.    689// Sorts the ranges (pre-order sort).
    +
    10429.    689// TODO: Tree-based normalization of the ranges. //jslint-ignore-line
    +
    10430.    689// @param funcCov Function coverage to normalize.
    +
    10431.    689
    +
    10432.    689        funcCov.ranges = treeToRanges(treeFromSortedRanges(
    +
    10433.    689            funcCov.ranges.sort(compareRangeList)
    +
    10434.    689        ));
    +
    10435.    689        return funcCov;
    +
    10436.    689    }
    +
    10437.     97
    +
    10438.    129    function sortScript(scriptCov) {
    +
    10439.    129
    +
    10440.    129// This function will normalize-and-sort <scriptCov>.functions.
    +
    10441.    129
    +
    10442.    129// Normalize-and-sort functions[xxx].ranges.
    +
    10443.    129
    +
    10444.    688        scriptCov.functions.forEach(function (funcCov) {
    +
    10445.    688            sortFunc(funcCov);
    +
    10446.    688        });
    +
    10447.    129
    +
    10448.    129// Sort functions by root range (pre-order sort).
    +
    10449.    129
    +
    10450.    559        scriptCov.functions.sort(function (aa, bb) {
    +
    10451.    559            return compareRangeList(aa.ranges[0], bb.ranges[0]);
    +
    10452.    559        });
    +
    10453.    129        return scriptCov;
    +
    10454.    129    }
    +
    10455.     97
    +
    10456.    888    function treeFromSortedRanges(ranges) {
    +
    10457.    888
    +
    10458.    888// @precondition `ranges` are well-formed and pre-order sorted
    +
    10459.    888
    +
    10460.    888        let root;
    +
    10461.    888        let stack = [];   // Stack of parent trees and parent counts.
    +
    10462.   1856        ranges.forEach(function (range) {
    +
    10463.   1856
    +
    10464.   1856// new rangeTreeCreate().
    +
    10465.   1856
    +
    10466.   1856            let node = {
    +
    10467.   1856                children: [],
    +
    10468.   1856                delta: range.count,
    +
    10469.   1856                end: range.endOffset,
    +
    10470.   1856                start: range.startOffset
    +
    10471.   1856            };
    +
    10472.   1856            let parent;
    +
    10473.   1856            let parentCount;
    +
    10474.    888            if (root === undefined) {
    +
    10475.    888                root = node;
    +
    10476.    888                stack.push([
    +
    10477.    888                    node, range.count
    +
    10478.    888                ]);
    +
    10479.    888                return;
    +
    10480.    968            }
    +
    10481.   1565            while (true) {
    +
    10482.   1565                [
    +
    10483.   1565                    parent, parentCount
    +
    10484.   1565                ] = stack[stack.length - 1];
    +
    10485.   1565
    +
    10486.   1565// assert: `top !== undefined` (the ranges are sorted)
    +
    10487.   1565
    +
    10488.   1565                if (range.startOffset < parent.end) {
    +
    10489.   1565                    break;
    +
    10490.   1565                }
    +
    10491.   1565                stack.pop();
    +
    10492.   1565            }
    +
    10493.    968            node.delta -= parentCount;
    +
    10494.    968            parent.children.push(node);
    +
    10495.    968            stack.push([
    +
    10496.    968                node, range.count
    +
    10497.    968            ]);
    +
    10498.    968        });
    +
    10499.    888        return root;
    +
    10500.    888    }
    +
    10501.     97
    +
    10502.    781    function treeToRanges(tree) {
    +
    10503.    781
    +
    10504.    781// Get the range coverages corresponding to the tree.
    +
    10505.    781// The ranges are pre-order sorted.
    +
    10506.    781
    +
    10507.    781        let count;
    +
    10508.    781        let cur;
    +
    10509.    781        let ii;
    +
    10510.    781        let parentCount;
    +
    10511.    781        let ranges = [];
    +
    10512.    781        let stack = [           // Stack of parent trees and counts.
    +
    10513.    781            [
    +
    10514.    781                tree, 0
    +
    10515.    781            ]
    +
    10516.    781        ];
    +
    10517.   1630        function normalizeRange(tree) {
    +
    10518.   1630
    +
    10519.   1630// @internal
    +
    10520.   1630
    +
    10521.   1630            let children = [];
    +
    10522.   1630            let curEnd;
    +
    10523.   1630            let head;
    +
    10524.   1630            let tail = [];
    +
    10525.    849            function endChain() {
    +
    10526.     18                if (tail.length !== 0) {
    +
    10527.     18                    head.end = tail[tail.length - 1].end;
    +
    10528.     18                    tail.forEach(function (tailTree) {
    +
    10529.     18                        tailTree.children.forEach(function (subChild) {
    +
    10530.     18                            subChild.delta += tailTree.delta - head.delta;
    +
    10531.     18                            head.children.push(subChild);
    +
    10532.     18                        });
    +
    10533.     18                    });
    +
    10534.     18                    tail.length = 0;
    +
    10535.     18                }
    +
    10536.    849
    +
    10537.    849// Recurse normalizeRange().
    +
    10538.    849
    +
    10539.    849                normalizeRange(head);
    +
    10540.    849                children.push(head);
    +
    10541.    849            }
    +
    10542.    867            tree.children.forEach(function (child) {
    +
    10543.    432                if (head === undefined) {
    +
    10544.    432                    head = child;
    +
    10545.    435                } else if (
    +
    10546.    435                    child.delta === head.delta && child.start === curEnd
    +
    10547.    435                ) {
    +
    10548.    435                    tail.push(child);
    +
    10549.    435                } else {
    +
    10550.    435                    endChain();
    +
    10551.    435                    head = child;
    +
    10552.    435                }
    +
    10553.    867                curEnd = child.end;
    +
    10554.    867            });
    +
    10555.    432            if (head !== undefined) {
    +
    10556.    432                endChain();
    +
    10557.    432            }
    +
    10558.    238            if (children.length === 1) {
    +
    10559.    238                if (
    +
    10560.    238                    children[0].start === tree.start
    +
    10561.    238                    && children[0].end === tree.end
    +
    10562.    238                ) {
    +
    10563.    238                    tree.delta += children[0].delta;
    +
    10564.    238                    tree.children = children[0].children;
    +
    10565.    238
    +
    10566.    238// `.lazyCount` is zero for both (both are after normalization)
    +
    10567.    238
    +
    10568.    238                    return;
    +
    10569.    238                }
    +
    10570.   1624            }
    +
    10571.   1624            tree.children = children;
    +
    10572.   1624        }
    +
    10573.    781        normalizeRange(tree);
    +
    10574.   1624        while (stack.length > 0) {
    +
    10575.   1624            [
    +
    10576.   1624                cur, parentCount
    +
    10577.   1624            ] = stack.pop();
    +
    10578.   1624            count = parentCount + cur.delta;
    +
    10579.   1624            ranges.push({
    +
    10580.   1624                count,
    +
    10581.   1624                endOffset: cur.end,
    +
    10582.   1624                startOffset: cur.start
    +
    10583.   1624            });
    +
    10584.   1624            ii = cur.children.length - 1;
    +
    10585.   1624            while (ii >= 0) {
    +
    10586.   1624                stack.push([
    +
    10587.   1624                    cur.children[ii], count
    +
    10588.   1624                ]);
    +
    10589.   1624                ii -= 1;
    +
    10590.   1624            }
    +
    10591.   1624        }
    +
    10592.    781        return ranges;
    +
    10593.    781    }
    +
    10594.     97
    +
    10595.      1    if (processCovs.length === 0) {
    +
    10596.      1        return {
    +
    10597.      1            result: []
    +
    10598.      1        };
    +
    10599.     96    }
    +
    10600.     96
    +
    10601.     96// Init urlToScriptDict.
    +
    10602.     96
    +
    10603.    234    processCovs.forEach(function ({
    +
    10604.    234        result
    +
    10605.    234    }) {
    +
    10606.    269        result.forEach(function (scriptCov) {
    +
    10607.    269            dictKeyValueAppend(urlToScriptDict, scriptCov.url, scriptCov);
    +
    10608.    269        });
    +
    10609.    234    });
    +
    10610.    129    urlToScriptDict.forEach(function (scriptCovs) {
    +
    10611.    129
    +
    10612.    129// assert: `scriptCovs.length > 0`
    +
    10613.    129
    +
    10614.    129// function mergeScriptList(scriptCovs) {
    +
    10615.    129// Merges a list of matching script coverages.
    +
    10616.    129// Scripts are matching if they have the same `url`.
    +
    10617.    129// The result is normalized.
    +
    10618.    129// The input values may be mutated, it is not safe to use them after passing
    +
    10619.    129// them to this function.
    +
    10620.    129// The computation is synchronous.
    +
    10621.    129// @param scriptCovs Process coverages to merge.
    +
    10622.    129// @return Merged script coverage, or `undefined` if the input list was empty.
    +
    10623.    129
    +
    10624.    129        let functions = [];
    +
    10625.    129
    +
    10626.    129// Map funcCovRoot.startOffset:funcCovRoot.endOffset to funcCov.
    +
    10627.    129
    +
    10628.    129        let rangeToFuncDict = new Map();
    +
    10629.    129
    +
    10630.    129// Probably deadcode.
    +
    10631.    129// if (scriptCovs.length === 0) {
    +
    10632.    129//     return undefined;
    +
    10633.    129// }
    +
    10634.    129
    +
    10635.     96        if (scriptCovs.length === 1) {
    +
    10636.     96            resultMerged.push(sortScript(scriptCovs[0]));
    +
    10637.     96            return;
    +
    10638.     96        }
    +
    10639.     96
    +
    10640.     96// Init rangeToFuncDict.
    +
    10641.     96// Map funcCovRoot.startOffset:funcCovRoot.endOffset to funcCov.
    +
    10642.     96
    +
    10643.    226        scriptCovs.forEach(function ({
    +
    10644.    226            functions
    +
    10645.    226        }) {
    +
    10646.    277            functions.forEach(function (funcCov) {
    +
    10647.    277                dictKeyValueAppend(
    +
    10648.    277                    rangeToFuncDict,
    +
    10649.    277
    +
    10650.    277// This string can be used to match function with same root range.
    +
    10651.    277// The string is derived from the start and end offsets of the root range of
    +
    10652.    277// the function.
    +
    10653.    277// This assumes that `ranges` is non-empty (true for valid function coverages).
    +
    10654.    277
    +
    10655.    277                    (
    +
    10656.    277                        funcCov.ranges[0].startOffset
    +
    10657.    277                        + ";" + funcCov.ranges[0].endOffset
    +
    10658.    277                    ),
    +
    10659.    277                    funcCov
    +
    10660.    277                );
    +
    10661.    277            });
    +
    10662.    226        });
    +
    10663.    112        rangeToFuncDict.forEach(function (funcCovs) {
    +
    10664.    112
    +
    10665.    112// assert: `funcCovs.length > 0`
    +
    10666.    112
    +
    10667.    112// function mergeFuncList(funcCovs) {
    +
    10668.    112// Merges a list of matching function coverages.
    +
    10669.    112// Functions are matching if their root ranges have the same span.
    +
    10670.    112// The result is normalized.
    +
    10671.    112// The input values may be mutated, it is not safe to use them after passing
    +
    10672.    112// them to this function.
    +
    10673.    112// The computation is synchronous.
    +
    10674.    112// @param funcCovs Function coverages to merge.
    +
    10675.    112// @return Merged function coverage, or `undefined` if the input list was empty.
    +
    10676.    112
    +
    10677.    112            let count = 0;
    +
    10678.    112            let isBlockCoverage;
    +
    10679.    112            let merged;
    +
    10680.    112            let ranges;
    +
    10681.    112            let trees = [];
    +
    10682.    112
    +
    10683.    112// Probably deadcode.
    +
    10684.    112// if (funcCovs.length === 0) {
    +
    10685.    112//     return undefined;
    +
    10686.    112// }
    +
    10687.    112
    +
    10688.     96            if (funcCovs.length === 1) {
    +
    10689.     96                functions.push(sortFunc(funcCovs[0]));
    +
    10690.     96                return;
    +
    10691.    111            }
    +
    10692.    111
    +
    10693.    111// assert: `funcCovs[0].ranges.length > 0`
    +
    10694.    111
    +
    10695.    276            funcCovs.forEach(function (funcCov) {
    +
    10696.    276
    +
    10697.    276// assert: `funcCov.ranges.length > 0`
    +
    10698.    276// assert: `funcCov.ranges` is sorted
    +
    10699.    276
    +
    10700.    276                count += (
    +
    10701.    276                    funcCov.count !== undefined
    +
    10702.    111                    ? funcCov.count
    +
    10703.    274                    : funcCov.ranges[0].count
    +
    10704.    276                );
    +
    10705.    199                if (funcCov.isBlockCoverage) {
    +
    10706.    199                    trees.push(treeFromSortedRanges(funcCov.ranges));
    +
    10707.    199                }
    +
    10708.    276            });
    +
    10709.    111            if (trees.length > 0) {
    +
    10710.     96                isBlockCoverage = true;
    +
    10711.     96                ranges = treeToRanges(mergeTreeList(trees));
    +
    10712.     96            } else {
    +
    10713.     96                isBlockCoverage = false;
    +
    10714.     96                ranges = [
    +
    10715.     96                    {
    +
    10716.     96                        count,
    +
    10717.     96                        endOffset: funcCovs[0].ranges[0].endOffset,
    +
    10718.     96                        startOffset: funcCovs[0].ranges[0].startOffset
    +
    10719.     96                    }
    +
    10720.     96                ];
    +
    10721.    111            }
    +
    10722.    111            merged = {
    +
    10723.    111                functionName: funcCovs[0].functionName,
    +
    10724.    111                isBlockCoverage,
    +
    10725.    111                ranges
    +
    10726.    111            };
    +
    10727.    111            if (count !== ranges[0].count) {
    +
    10728.     96                merged.count = count;
    +
    10729.    111            }
    +
    10730.    111
    +
    10731.    111// assert: `merged` is normalized
    +
    10732.    111
    +
    10733.    111            functions.push(merged);
    +
    10734.    111        });
    +
    10735.     96        resultMerged.push(sortScript({
    +
    10736.     96            functions,
    +
    10737.     96            scriptId: scriptCovs[0].scriptId,
    +
    10738.     96            url: scriptCovs[0].url
    +
    10739.     96        }));
    +
    10740.     96    });
    +
    10741.     96
    +
    10742.     96// Sorts the scripts alphabetically by `url`.
    +
    10743.     96// Reassigns script ids: the script at index `0` receives `"0"`, the script at
    +
    10744.     96// index `1` receives `"1"` etc.
    +
    10745.     96
    +
    10746.     96    Object.entries(resultMerged.sort(function (aa, bb) {
    +
    10747.     96        return (
    +
    10748.     96            aa.url > bb.url
    +
    10749.     96            ? 1
    +
    10750.     96            : -1
    +
    10751.     96        );
    +
    10752.    129    })).forEach(function ([
    +
    10753.    129        scriptId, scriptCov
    +
    10754.    129    ]) {
    +
    10755.    129        scriptCov.scriptId = scriptId.toString(10);
    +
    10756.    129    });
    +
    10757.     96    return {
    +
    10758.     96        result: resultMerged
    +
    10759.     96    };
    +
    10760.     96}
    +
    10761.      1
    +
    10762.      8async function v8CoverageReportCreate({
    +
    10763.      8    consoleError,
    +
    10764.      8    coverageDir,
    +
    10765.      8    processArgv = []
    +
    10766.      8}) {
    +
    10767.      8
    +
    10768.      8// This function will create html-coverage-reports directly from
    +
    10769.      8// v8-coverage-files in <coverageDir>.
    +
    10770.      8// 1. Spawn node.js program <processArgv> with NODE_V8_COVERAGE.
    +
    10771.      8// 2. Merge JSON v8-coverage-files in <coverageDir>.
    +
    10772.      8// 3. Create html-coverage-reports in <coverageDir>.
    +
    10773.      8
    +
    10774.      8    let cwd;
    +
    10775.      8    let excludeList = [];
    +
    10776.      8    let exitCode = 0;
    +
    10777.      8    let fileDict;
    +
    10778.      8    let includeList = [];
    +
    10779.      8    let modeIncludeNodeModules;
    +
    10780.      8    let processArgElem;
    +
    10781.      8    let promiseList = [];
    +
    10782.      8    let v8CoverageObj;
    +
    10783.      8
    +
    10784.     13    function htmlRender({
    +
    10785.     13        fileList,
    +
    10786.     13        lineList,
    +
    10787.     13        modeIndex,
    +
    10788.     13        pathname
    +
    10789.     13    }) {
    +
    10790.     13        let html;
    +
    10791.     13        let padLines;
    +
    10792.     13        let padPathname;
    +
    10793.     13        let txt;
    +
    10794.     13        let txtBorder;
    +
    10795.     13        html = "";
    +
    10796.     13        html += String(`
    +
    10797.     13<!DOCTYPE html>
    +
    10798.     13<html lang="en">
    +
    10799.     13<head>
    +
    10800.     13<title>V8 Coverage Report</title>
    +
    10801.     13<style>
    +
    10802.     13/* jslint utility2:true */
    +
    10803.     13/*csslint ignore:start*/
    +
    10804.     13.coverage,
    +
    10805.     13.coverage a,
    +
    10806.     13.coverage div,
    +
    10807.     13.coverage pre,
    +
    10808.     13.coverage span,
    +
    10809.     13.coverage table,
    +
    10810.     13.coverage tbody,
    +
    10811.     13.coverage td,
    +
    10812.     13.coverage th,
    +
    10813.     13.coverage thead,
    +
    10814.     13.coverage tr {
    +
    10815.     13    box-sizing: border-box;
    +
    10816.     13    font-family: monospace;
    +
    10817.     13}
    +
    10818.     13/*csslint ignore:end*/
    +
    10819.     13
    +
    10820.     13/* css - coverage_report - general */
    +
    10821.     13body {
    +
    10822.     13    margin: 0;
    +
    10823.     13}
    +
    10824.     13.coverage pre {
    +
    10825.     13    margin: 5px 0;
    +
    10826.     13}
    +
    10827.     13.coverage table {
    +
    10828.     13    border-collapse: collapse;
    +
    10829.     13}
    +
    10830.     13.coverage td,
    +
    10831.     13.coverage th {
    +
    10832.     13    border: 1px solid #777;
    +
    10833.     13    line-height: 20px;
    +
    10834.     13    margin: 0;
    +
    10835.     13    padding: 5px 10px;
    +
    10836.     13}
    +
    10837.     13.coverage td span {
    +
    10838.     13    display: inline-block;
    +
    10839.     13    width: 100%;
    +
    10840.     13}
    +
    10841.     13.coverage .content {
    +
    10842.     13    padding: 0 5px;
    +
    10843.     13}
    +
    10844.     13.coverage .content a {
    +
    10845.     13    text-decoration: none;
    +
    10846.     13}
    +
    10847.     13.coverage .count {
    +
    10848.     13    margin: 0 5px;
    +
    10849.     13    padding: 0 5px;
    +
    10850.     13}
    +
    10851.     13.coverage .footer,
    +
    10852.     13.coverage .header {
    +
    10853.     13    padding: 20px;
    +
    10854.     13}
    +
    10855.     13.coverage .footer {
    +
    10856.     13    text-align: center;
    +
    10857.     13}
    +
    10858.     13.coverage .percentbar {
    +
    10859.     13    height: 12px;
    +
    10860.     13    margin: 2px 0;
    +
    10861.     13    min-width: 200px;
    +
    10862.     13    position: relative;
    +
    10863.     13    width: 100%;
    +
    10864.     13}
    +
    10865.     13.coverage .percentbar div {
    +
    10866.     13    height: 100%;
    +
    10867.     13    position: absolute;
    +
    10868.     13}
    +
    10869.     13.coverage .title {
    +
    10870.     13    font-size: large;
    +
    10871.     13    font-weight: bold;
    +
    10872.     13    margin-bottom: 10px;
    +
    10873.     13}
    +
    10874.     13
    +
    10875.     13/* css - coverage_report - color */
    +
    10876.     13.coverage td,
    +
    10877.     13.coverage th {
    +
    10878.     13    background: #fff;
    +
    10879.     13}
    +
    10880.     13.coverage .count,
    +
    10881.     13.coverage .coverageHigh {
    +
    10882.     13    background: #9d9;
    +
    10883.     13}
    +
    10884.     13.coverage .count {
    +
    10885.     13    color: #666;
    +
    10886.     13}
    +
    10887.     13.coverage .coverageIgnore {
    +
    10888.     13    background: #ccc;
    +
    10889.     13}
    +
    10890.     13.coverage .coverageLow,
    +
    10891.     13.coverage .uncovered {
    +
    10892.     13    background: #ebb;
    +
    10893.     13}
    +
    10894.     13.coverage .coverageMedium {
    +
    10895.     13    background: #fd7;
    +
    10896.     13}
    +
    10897.     13.coverage .footer,
    +
    10898.     13.coverage .header,
    +
    10899.     13.coverage .lineno {
    +
    10900.     13    background: #ddd;
    +
    10901.     13}
    +
    10902.     13.coverage .percentbar {
    +
    10903.     13    background: #999;
    +
    10904.     13}
    +
    10905.     13.coverage .percentbar div {
    +
    10906.     13    background: #666;
    +
    10907.     13}
    +
    10908.     13
    +
    10909.     13/* css - coverage_report - important */
    +
    10910.     13.coverage pre:hover span,
    +
    10911.     13.coverage tr:hover td {
    +
    10912.     13    background: #7d7;
    +
    10913.     13}
    +
    10914.     13.coverage pre:hover span.uncovered,
    +
    10915.     13.coverage tr:hover td.coverageLow {
    +
    10916.     13    background: #f99;
    +
    10917.     13}
    +
    10918.     13</style>
    +
    10919.     13</head>
    +
    10920.     13<body class="coverage">
    +
    10921.     13<!-- header start -->
    +
    10922.     13<div class="header">
    +
    10923.     13<div class="title">V8 Coverage Report</div>
    +
    10924.     13<table>
    +
    10925.     13<thead>
    +
    10926.     13    <tr>
    +
    10927.     13    <th>Files covered</th>
    +
    10928.     13    <th>Lines</th>
    +
    10929.     13    <th>Remaining</th>
    +
    10930.     13    </tr>
    +
    10931.     13</thead>
    +
    10932.     13<tbody>
    +
    10933.     13        `).trim() + "\n";
    +
    10934.      7        if (modeIndex) {
    +
    10935.      7            padLines = String("(ignore) 100.00 %").length;
    +
    10936.      7            padPathname = 32;
    +
    10937.      7            fileList.unshift({
    +
    10938.      7                linesCovered: 0,
    +
    10939.      7                linesTotal: 0,
    +
    10940.      7                modeCoverageIgnoreFile: "",
    +
    10941.      7                pathname: "./"
    +
    10942.      7            });
    +
    10943.      7            fileList.slice(1).forEach(function ({
    +
    10944.      7                linesCovered,
    +
    10945.      7                linesTotal,
    +
    10946.      7                modeCoverageIgnoreFile,
    +
    10947.      7                pathname
    +
    10948.      7            }) {
    +
    10949.      7                if (!modeCoverageIgnoreFile) {
    +
    10950.      7                    fileList[0].linesCovered += linesCovered;
    +
    10951.      7                    fileList[0].linesTotal += linesTotal;
    +
    10952.      7                }
    +
    10953.      7                padPathname = Math.max(padPathname, pathname.length + 2);
    +
    10954.      7                padLines = Math.max(
    +
    10955.      7                    padLines,
    +
    10956.      7                    String(linesCovered + " / " + linesTotal).length
    +
    10957.      7                );
    +
    10958.      7            });
    +
    10959.      7        }
    +
    10960.     13        txtBorder = (
    +
    10961.     13            "+" + "-".repeat(padPathname + 2) + "+"
    +
    10962.     13            + "-".repeat(padLines + 2) + "+"
    +
    10963.     13            + "-".repeat(padLines + 2) + "+\n"
    +
    10964.     13        );
    +
    10965.     13        txt = "";
    +
    10966.     13        txt += "V8 Coverage Report\n";
    +
    10967.     13        txt += txtBorder;
    +
    10968.     13        txt += (
    +
    10969.     13            "| " + String("Files covered").padEnd(padPathname, " ") + " | "
    +
    10970.     13            + String("Lines").padStart(padLines, " ") + " | "
    +
    10971.     13            + String("Remaining").padStart(padLines, " ") + " |\n"
    +
    10972.     13        );
    +
    10973.     13        txt += txtBorder;
    +
    10974.     19        fileList.forEach(function ({
    +
    10975.     19            linesCovered,
    +
    10976.     19            linesTotal,
    +
    10977.     19            modeCoverageIgnoreFile,
    +
    10978.     19            pathname
    +
    10979.     19        }, ii) {
    +
    10980.     19            let coverageLevel;
    +
    10981.     19            let coveragePct;
    +
    10982.     19            let fill;
    +
    10983.     19            let str1;
    +
    10984.     19            let str2;
    +
    10985.     19            let xx1;
    +
    10986.     19            let xx2;
    +
    10987.      2            coveragePct = Math.floor(10000 * linesCovered / linesTotal || 0);
    +
    10988.     19            coverageLevel = (
    +
    10989.     19                modeCoverageIgnoreFile
    +
    10990.      2                ? "coverageIgnore"
    +
    10991.     17                : coveragePct >= 8000
    +
    10992.     17                ? "coverageHigh"
    +
    10993.     17                : coveragePct >= 5000
    +
    10994.     17                ? "coverageMedium"
    +
    10995.     17                : "coverageLow"
    +
    10996.     19            );
    +
    10997.     19            coveragePct = String(coveragePct).replace((
    +
    10998.     19                /..$/m
    +
    10999.     19            ), ".$&");
    +
    11000.     13            if (modeIndex && ii === 0) {
    +
    11001.      7                fill = (
    +
    11002.      7
    +
    11003.      7// Badge-color rgb-red.
    +
    11004.      7
    +
    11005.      7                    "#" + Math.round(
    +
    11006.      7                        (100 - Number(coveragePct)) * 2.21
    +
    11007.      7                    ).toString(16).padStart(2, "0")
    +
    11008.      7
    +
    11009.      7// Badge-color rgb-green.
    +
    11010.      7
    +
    11011.      7                    + Math.round(
    +
    11012.      7                        Number(coveragePct) * 2.21
    +
    11013.      7                    ).toString(16).padStart(2, "0")
    +
    11014.      7
    +
    11015.      7// Badge-color rgb-blue.
    +
    11016.      7
    +
    11017.      7                    + "00"
    +
    11018.      7                );
    +
    11019.      7                str1 = "coverage";
    +
    11020.      7                str2 = coveragePct + " %";
    +
    11021.      7                xx1 = 6 * str1.length + 20;
    +
    11022.      7                xx2 = 6 * str2.length + 20;
    +
    11023.      7
    +
    11024.      7// Fs - write coverage_badge.svg.
    +
    11025.      7
    +
    11026.      7                promiseList.push(fsWriteFileWithParents((
    +
    11027.      7                    coverageDir + "coverage_badge.svg"
    +
    11028.      7                ), String(`
    +
    11029.      7<svg height="20" width="${xx1 + xx2}" xmlns="http://www.w3.org/2000/svg">
    +
    11030.      7<rect fill="#555" height="20" width="${xx1 + xx2}"/>
    +
    11031.      7<rect fill="${fill}" height="20" width="${xx2}" x="${xx1}"/>
    +
    11032.      7<g
    +
    11033.      7    fill="#fff"
    +
    11034.      7    font-family="verdana, geneva, dejavu sans, sans-serif"
    +
    11035.      7    font-size="11"
    +
    11036.      7    font-weight="bold"
    +
    11037.      7    text-anchor="middle"
    +
    11038.      7>
    +
    11039.      7<text x="${0.5 * xx1}" y="14">${str1}</text>
    +
    11040.      7<text x="${xx1 + 0.5 * xx2}" y="14">${str2}</text>
    +
    11041.      7</g>
    +
    11042.      7</svg>
    +
    11043.      7                `).trim() + "\n"));
    +
    11044.      7                pathname = "";
    +
    11045.      7            }
    +
    11046.     19            txt += (
    +
    11047.     19                "| "
    +
    11048.     19                + String("./" + pathname).padEnd(padPathname, " ") + " | "
    +
    11049.     19                + String(
    +
    11050.     19                    modeCoverageIgnoreFile + " " + coveragePct + " %"
    +
    11051.     19                ).padStart(padLines, " ") + " | "
    +
    11052.     19                + " ".repeat(padLines) + " |\n"
    +
    11053.     19            );
    +
    11054.     19            txt += (
    +
    11055.     19                "| " + "*".repeat(
    +
    11056.     19                    Math.round(0.01 * coveragePct * padPathname)
    +
    11057.     19                ).padEnd(padPathname, "_") + " | "
    +
    11058.     19                + String(
    +
    11059.     19                    linesCovered + " / " + linesTotal
    +
    11060.     19                ).padStart(padLines, " ") + " | "
    +
    11061.     19                + String(
    +
    11062.     19                    (linesTotal - linesCovered) + " / " + linesTotal
    +
    11063.     19                ).padStart(padLines, " ") + " |\n"
    +
    11064.     19            );
    +
    11065.     19            txt += txtBorder;
    +
    11066.     19            pathname = htmlEscape(pathname);
    +
    11067.     19
    +
    11068.     19// CL-37251d17 - Bugfix - Fix incorrect http-link to index.html.
    +
    11069.     19
    +
    11070.     19            html += String(`
    +
    11071.     19    <tr>
    +
    11072.     19    <td class="${coverageLevel}">
    +
    11073.     19            ${(
    +
    11074.     19                modeIndex
    +
    11075.     13                ? (
    +
    11076.     13                    "<a href=\"" + (pathname || "index") + ".html\">. / "
    +
    11077.     13                    + pathname + "</a><br>"
    +
    11078.     13                )
    +
    11079.      6                : (
    +
    11080.      6                    "<a href=\""
    +
    11081.      6                    + "../".repeat(pathname.split("/").length - 1)
    +
    11082.      6                    + "index.html\">. / </a>"
    +
    11083.      6                    + pathname + "<br>"
    +
    11084.      6                )
    +
    11085.     19            )}
    +
    11086.     19        <div class="percentbar">
    +
    11087.     19            <div style="width: ${coveragePct}%;"></div>
    +
    11088.     19        </div>
    +
    11089.     19    </td>
    +
    11090.     19    <td style="text-align: right;">
    +
    11091.     19        ${modeCoverageIgnoreFile} ${coveragePct} %<br>
    +
    11092.     19        ${linesCovered} / ${linesTotal}
    +
    11093.     19    </td>
    +
    11094.     19    <td style="text-align: right;">
    +
    11095.     19        <br>
    +
    11096.     19        ${linesTotal - linesCovered} / ${linesTotal}
    +
    11097.     19    </td>
    +
    11098.     19    </tr>
    +
    11099.     19        `).trim() + "\n";
    +
    11100.     19        });
    +
    11101.     13        html += String(`
    +
    11102.     13</tbody>
    +
    11103.     13</table>
    +
    11104.     13</div>
    +
    11105.     13<!-- header end -->
    +
    11106.     13        `).trim() + "\n";
    +
    11107.      6        if (!modeIndex) {
    +
    11108.      6            html += String(`
    +
    11109.      6<!-- content start -->
    +
    11110.      6<div class="content">
    +
    11111.      6            `).trim() + "\n";
    +
    11112.  11853            lineList.forEach(function ({
    +
    11113.  11853                count,
    +
    11114.  11853                holeList,
    +
    11115.  11853                line,
    +
    11116.  11853                startOffset
    +
    11117.  11853            }, ii) {
    +
    11118.  11853                let chunk;
    +
    11119.  11853                let inHole;
    +
    11120.  11853                let lineHtml;
    +
    11121.  11853                let lineId;
    +
    11122.  11853                lineHtml = "";
    +
    11123.  11853                lineId = "line_" + (ii + 1);
    +
    11124.  11853                switch (count) {
    +
    11125.     32                case -1:
    +
    11126.  11219                case 0:
    +
    11127.  11219                    if (holeList.length === 0) {
    +
    11128.  11219                        lineHtml += "</span>";
    +
    11129.  11219                        lineHtml += "<span class=\"uncovered\">";
    +
    11130.  11219                        lineHtml += htmlEscape(line);
    +
    11131.  11219                        break;
    +
    11132.  11219                    }
    +
    11133.  11219                    line = line.split("").map(function (char) {
    +
    11134.  11219                        return {
    +
    11135.  11219                            char,
    +
    11136.  11219                            isHole: undefined
    +
    11137.  11219                        };
    +
    11138.  11219                    });
    +
    11139.  11219                    holeList.forEach(function ([
    +
    11140.  11219                        aa, bb
    +
    11141.  11219                    ]) {
    +
    11142.  11219                        aa = Math.max(aa - startOffset, 0);
    +
    11143.  11219                        bb = Math.min(bb - startOffset, line.length);
    +
    11144.  11219                        while (aa < bb) {
    +
    11145.  11219                            line[aa].isHole = true;
    +
    11146.  11219                            aa += 1;
    +
    11147.  11219                        }
    +
    11148.  11219                    });
    +
    11149.  11219                    chunk = "";
    +
    11150.  11219                    line.forEach(function ({
    +
    11151.  11219                        char,
    +
    11152.  11219                        isHole
    +
    11153.  11219                    }) {
    +
    11154.  11219                        if (inHole !== isHole) {
    +
    11155.  11219                            lineHtml += htmlEscape(chunk);
    +
    11156.  11219                            lineHtml += "</span><span";
    +
    11157.  11219
    +
    11158.  11219// Coverage-hack - Ugly-hack around possible deadcode where isHole is always
    +
    11159.  11219// true.
    +
    11160.  11219
    +
    11161.  11219                            if (isHole) {
    +
    11162.  11219                                lineHtml += " class=\"uncovered\"";
    +
    11163.  11219                            }
    +
    11164.  11219                            lineHtml += ">";
    +
    11165.  11219                            chunk = "";
    +
    11166.  11219                            inHole = isHole;
    +
    11167.  11219                        }
    +
    11168.  11219                        chunk += char;
    +
    11169.  11219                    });
    +
    11170.  11219                    lineHtml += htmlEscape(chunk);
    +
    11171.  11219                    break;
    +
    11172.    634                default:
    +
    11173.    634                    lineHtml += htmlEscape(line);
    +
    11174.  11853                }
    +
    11175.  11853                html += String(`
    +
    11176.  11853<pre>
    +
    11177.  11853<span class="lineno">
    +
    11178.  11853<a href="#${lineId}" id="${lineId}">${String(ii + 1).padStart(5, " ")}.</a>
    +
    11179.  11853</span>
    +
    11180.  11853<span class="count
    +
    11181.  11853                ${(
    +
    11182.  11853                    count <= 0
    +
    11183.  11219                    ? "uncovered"
    +
    11184.    634                    : ""
    +
    11185.  11853                )}"
    +
    11186.  11853>
    +
    11187.  11187${String(count || "-0").padStart(7, " ")}
    +
    11188.  11853</span>
    +
    11189.  11853<span>${lineHtml}</span>
    +
    11190.  11853</pre>
    +
    11191.  11853                `).replace((
    +
    11192.  11853                    /\n/g
    +
    11193.  11853                ), "").trim() + "\n";
    +
    11194.  11853            });
    +
    11195.      6            html += String(`
    +
    11196.      6</div>
    +
    11197.      6<!-- content end -->
    +
    11198.      6            `).trim() + "\n";
    +
    11199.      6        }
    +
    11200.     13        html += String(`
    +
    11201.     13<div class="footer">
    +
    11202.     13    [
    +
    11203.     13    This document was created with
    +
    11204.     13    <a href="https://github.com/jslint-org/jslint">JSLint</a>
    +
    11205.     13    ]
    +
    11206.     13</div>
    +
    11207.     13</body>
    +
    11208.     13</html>
    +
    11209.     13        `).trim() + "\n";
    +
    11210.     13
    +
    11211.     13// Fs - write <file>.html.
    +
    11212.     13
    +
    11213.     13        promiseList.push(fsWriteFileWithParents(pathname + ".html", html));
    +
    11214.      6        if (!modeIndex) {
    +
    11215.      6            return;
    +
    11216.      7        }
    +
    11217.      7
    +
    11218.      7// Fs - write coverage_report.txt.
    +
    11219.      7
    +
    11220.      7        consoleError("\n" + txt);
    +
    11221.      7        promiseList.push(fsWriteFileWithParents((
    +
    11222.      7            coverageDir + "coverage_report.txt"
    +
    11223.      7        ), txt));
    +
    11224.      7    }
    +
    11225.      8
    +
    11226.      8/*
    +
    11227.      8function sentinel() {}
    +
    11228.      8*/
    +
    11229.      8
    +
    11230.      8    await moduleFsInit();
    +
    11231.      1    consoleError = consoleError || console.error;
    +
    11232.      8    cwd = process.cwd().replace((
    +
    11233.      8        /\\/g
    +
    11234.      8    ), "/") + "/";
    +
    11235.      8
    +
    11236.      8// Init coverageDir.
    +
    11237.      8// Assert coverageDir is subdirectory of cwd.
    +
    11238.      8
    +
    11239.      8    assertOrThrow(coverageDir, "invalid coverageDir " + coverageDir);
    +
    11240.      8
    +
    11241.      8// CL-61b11012 - coverage - Relax requirement for coverageDir to be in cwd.
    +
    11242.      8//     assertOrThrow(
    +
    11243.      8//         pathnameRelativeCwd(coverageDir),
    +
    11244.      8//         "coverageDir " + coverageDir + " is not subdirectory of cwd " + cwd
    +
    11245.      8//     );
    +
    11246.      8
    +
    11247.      8    coverageDir = modulePath.resolve(coverageDir).replace((
    +
    11248.      8        /\\/g
    +
    11249.      8    ), "/") + "/";
    +
    11250.      8
    +
    11251.      8    processArgv = processArgv.slice();
    +
    11252.      9    while (processArgv[0] && processArgv[0][0] === "-") {
    +
    11253.      3        processArgElem = processArgv.shift().split("=");
    +
    11254.      3        processArgElem[1] = processArgElem.slice(1).join("=");
    +
    11255.      3        switch (processArgElem[0]) {
    +
    11256.      3
    +
    11257.      3// PR-371 - Add cli-option `--exclude=...`.
    +
    11258.      3
    +
    11259.      3        case "--exclude":
    +
    11260.      3            excludeList.push(processArgElem[1]);
    +
    11261.      3            break;
    +
    11262.      3
    +
    11263.      3// PR-371 - Add cli-option `--include=...`
    +
    11264.      3
    +
    11265.      3        case "--include":
    +
    11266.      3            includeList.push(processArgElem[1]);
    +
    11267.      3            break;
    +
    11268.      3
    +
    11269.      3// PR-400
    +
    11270.      3// Disable default-coverage of directory `node_modules`,
    +
    11271.      3// but allow override with cli-option `--include-node-modules=1`.
    +
    11272.      3
    +
    11273.      3        case "--include-node-modules":
    +
    11274.      3            modeIncludeNodeModules = !(
    +
    11275.      3                /0|false|null|undefined/
    +
    11276.      3            ).test(processArgElem[1]);
    +
    11277.      3            break;
    +
    11278.      3        }
    +
    11279.      7    }
    +
    11280.      7
    +
    11281.      7// 1. Spawn node.js program <processArgv> with coverage
    +
    11282.      7
    +
    11283.      7    if (processArgv.length > 0) {
    +
    11284.      6
    +
    11285.      6// Remove old coverage-files.
    +
    11286.      6
    +
    11287.      6        await fsWriteFileWithParents(coverageDir + "/touch.txt", "");
    +
    11288.      6        await Promise.all(Array.from(
    +
    11289.      6            await moduleFs.promises.readdir(coverageDir)
    +
    11290.     11        ).map(async function (file) {
    +
    11291.     11            if ((
    +
    11292.     11                /^coverage-\d+?-\d+?-\d+?\.json$/
    +
    11293.      6            ).test(file)) {
    +
    11294.      6                consoleError("rm file " + coverageDir + file);
    +
    11295.      6                await moduleFs.promises.unlink(coverageDir + file);
    +
    11296.      6            }
    +
    11297.     11        }));
    +
    11298.      6        exitCode = await new Promise(function (resolve) {
    +
    11299.      6            let processArgv0 = processArgv[0];
    +
    11300.      6
    +
    11301.      6// If win32 environment, then replace program npm with npm.cmd.
    +
    11302.      6// Coverage-hack - Ugly-hack to get test-coverage under both win32 and linux.
    +
    11303.      6
    +
    11304.      6            if (processArgv0 === "npm") {
    +
    11305.      6                processArgv0 = process.platform.replace(
    +
    11306.      6                    "win32",
    +
    11307.      6                    "npm.cmd"
    +
    11308.      6                ).replace(
    +
    11309.      6                    process.platform,
    +
    11310.      6                    "npm"
    +
    11311.      6                );
    +
    11312.      6            }
    +
    11313.      6            moduleChildProcess.spawn(
    +
    11314.      6                processArgv0,
    +
    11315.      6                processArgv.slice(1),
    +
    11316.      6                {
    +
    11317.      6                    env: Object.assign({}, process.env, {
    +
    11318.      6                        NODE_V8_COVERAGE: coverageDir
    +
    11319.      6                    }),
    +
    11320.      6
    +
    11321.      6// PR-465
    +
    11322.      6// https://nodejs.org/en/blog/vulnerability/april-2024-security-releases-2
    +
    11323.      6// Node.js will now error with EINVAL if a .bat or .cmd file is passed to
    +
    11324.      6// child_process.spawn and child_process.spawnSync without the shell option set.
    +
    11325.      6
    +
    11326.      6                    shell: (
    +
    11327.      6                        processArgv0.endsWith(".bat")
    +
    11328.      6                        || processArgv0.endsWith(".cmd")
    +
    11329.      6                    ),
    +
    11330.      6                    stdio: ["ignore", 1, 2]
    +
    11331.      6                }
    +
    11332.      6            ).on("exit", resolve);
    +
    11333.      6        });
    +
    11334.      6        consoleError(
    +
    11335.      6            `v8CoverageReportCreate - program exited with exitCode=${exitCode}`
    +
    11336.      6        );
    +
    11337.      7    }
    +
    11338.      7
    +
    11339.      7// 2. Merge JSON v8-coverage-files in <coverageDir>.
    +
    11340.      7
    +
    11341.      7    consoleError("v8CoverageReportCreate - merging coverage files...");
    +
    11342.      7    v8CoverageObj = await moduleFs.promises.readdir(coverageDir);
    +
    11343.     18    v8CoverageObj = v8CoverageObj.filter(function (file) {
    +
    11344.     18        return (
    +
    11345.     18            /^coverage-\d+?-\d+?-\d+?\.json$/
    +
    11346.     18        ).test(file);
    +
    11347.     18    });
    +
    11348.      7    v8CoverageObj = await Promise.all(v8CoverageObj.map(async function (file) {
    +
    11349.      7        let data;
    +
    11350.      7        let pathnameDict = Object.create(null);
    +
    11351.      7        data = await moduleFs.promises.readFile(coverageDir + file, "utf8");
    +
    11352.      7        data = JSON.parse(data);
    +
    11353.    473        data.result.forEach(function (scriptCov) {
    +
    11354.    473            let pathname = scriptCov.url;
    +
    11355.    473
    +
    11356.    473// Filter out internal coverages.
    +
    11357.    473
    +
    11358.    393            if (!pathname.startsWith("file:///")) {
    +
    11359.    393                return;
    +
    11360.    393            }
    +
    11361.     80
    +
    11362.     80// Normalize pathname.
    +
    11363.     80
    +
    11364.     80            pathname = moduleUrl.fileURLToPath(pathname);
    +
    11365.     80            pathname = modulePath.resolve(pathname).replace((
    +
    11366.     80                /\\/g
    +
    11367.     80            ), "/");
    +
    11368.     80
    +
    11369.     80// Filter files outside of cwd.
    +
    11370.     80
    +
    11371.     80            if (pathname.indexOf("[") >= 0 || !pathname.startsWith(cwd)) {
    +
    11372.     74                return;
    +
    11373.     74            }
    +
    11374.      7
    +
    11375.      7// Normalize pathname relative to cwd.
    +
    11376.      7
    +
    11377.      7            pathname = pathname.slice(cwd.length);
    +
    11378.      7            scriptCov.url = pathname;
    +
    11379.      7            pathnameDict[pathname] = scriptCov;
    +
    11380.      7        });
    +
    11381.      7
    +
    11382.      7// PR-400 - Filter directory `node_modules`.
    +
    11383.      7
    +
    11384.      7        if (!modeIncludeNodeModules) {
    +
    11385.      7            excludeList.push("node_modules/");
    +
    11386.      7        }
    +
    11387.      7
    +
    11388.      7// PR-400 - Filter files by glob-patterns in excludeList, includeList.
    +
    11389.      7
    +
    11390.      7        data.result = globExclude({
    +
    11391.      7            excludeList,
    +
    11392.      7            includeList,
    +
    11393.      7            pathnameList: Object.keys(pathnameDict)
    +
    11394.      7        }).pathnameList.map(function (pathname) {
    +
    11395.      7            return pathnameDict[pathname];
    +
    11396.      7        });
    +
    11397.      7        return data;
    +
    11398.      7    }));
    +
    11399.      7
    +
    11400.      7// Merge v8CoverageObj.
    +
    11401.      7
    +
    11402.      7    v8CoverageObj = v8CoverageListMerge(v8CoverageObj);
    +
    11403.      7
    +
    11404.      7// Debug v8CoverageObj.
    +
    11405.      7
    +
    11406.      7    await fsWriteFileWithParents(
    +
    11407.      7        coverageDir + "v8_coverage_merged.json",
    +
    11408.      7        JSON.stringify(v8CoverageObj, undefined, 1)
    +
    11409.      7    );
    +
    11410.      7
    +
    11411.      7// 3. Create html-coverage-reports in <coverageDir>.
    +
    11412.      7
    +
    11413.      7    consoleError("v8CoverageReportCreate - creating html-coverage-report...");
    +
    11414.      7    fileDict = Object.create(null);
    +
    11415.      7    await Promise.all(v8CoverageObj.result.map(async function ({
    +
    11416.      7        functions,
    +
    11417.      7        url: pathname
    +
    11418.      7    }) {
    +
    11419.      7        let lineList;
    +
    11420.      7        let linesCovered;
    +
    11421.      7        let linesTotal;
    +
    11422.      7        let source;
    +
    11423.      7        source = await moduleFs.promises.readFile(pathname, "utf8");
    +
    11424.      7        lineList = [{}];
    +
    11425.      7        source.replace((
    +
    11426.      7            /^.*$/gm
    +
    11427.  11853        ), function (line, startOffset) {
    +
    11428.  11853            lineList[lineList.length - 1].endOffset = startOffset - 1;
    +
    11429.  11853            lineList.push({
    +
    11430.  11853                count: -1,
    +
    11431.  11853                endOffset: 0,
    +
    11432.  11853                holeList: [],
    +
    11433.  11853                line,
    +
    11434.  11853                startOffset
    +
    11435.  11853            });
    +
    11436.  11853            return "";
    +
    11437.  11853        });
    +
    11438.      7        lineList.shift();
    +
    11439.      7        lineList[lineList.length - 1].endOffset = source.length;
    +
    11440.     41        functions.reverse().forEach(function ({
    +
    11441.     41            ranges
    +
    11442.     41        }) {
    +
    11443.     65            ranges.reverse().forEach(function ({
    +
    11444.     65                count,
    +
    11445.     65                endOffset,
    +
    11446.     65                startOffset
    +
    11447.     65            }, ii, list) {
    +
    11448. 580047                lineList.forEach(function (elem) {
    +
    11449. 580047                    if (!(
    +
    11450. 580047                        (
    +
    11451. 580047                            elem.startOffset <= startOffset
    +
    11452. 205728                            && startOffset <= elem.endOffset
    +
    11453. 579982                        ) || (
    +
    11454. 579982                            elem.startOffset <= endOffset
    +
    11455. 579982                            && endOffset <= elem.endOffset
    +
    11456. 579982                        ) || (
    +
    11457. 579926                            startOffset <= elem.startOffset
    +
    11458. 579926                            && elem.endOffset <= endOffset
    +
    11459. 579926                        )
    +
    11460. 556497                    )) {
    +
    11461. 556497                        return;
    +
    11462. 556497                    }
    +
    11463.  23550
    +
    11464.  23550// Handle tree-root.
    +
    11465.  23550
    +
    11466.  23550                    if (ii + 1 === list.length) {
    +
    11467.  23281                        if (elem.count === -1) {
    +
    11468.  23281                            elem.count = count;
    +
    11469.  23281                        }
    +
    11470.  23281                        return;
    +
    11471.  23281                    }
    +
    11472.    269
    +
    11473.    269// Handle tree-children.
    +
    11474.    269
    +
    11475.    269                    if (elem.count !== 0) {
    +
    11476.    170                        elem.count = Math.max(count, elem.count);
    +
    11477.    269                    }
    +
    11478.    269                    if (count === 0) {
    +
    11479.    203                        elem.count = 0;
    +
    11480.    203                        elem.holeList.push([
    +
    11481.    203                            startOffset, endOffset
    +
    11482.    203                        ]);
    +
    11483.    203                    }
    +
    11484. 580047                });
    +
    11485.     65            });
    +
    11486.     41        });
    +
    11487.      7        linesTotal = lineList.length;
    +
    11488.  11853        linesCovered = lineList.filter(function ({
    +
    11489.  11853            count
    +
    11490.  11853        }) {
    +
    11491.  11853            return count > 0;
    +
    11492.  11853        }).length;
    +
    11493.      7        await moduleFs.promises.mkdir((
    +
    11494.      7            modulePath.dirname(coverageDir + pathname)
    +
    11495.      7        ), {
    +
    11496.      7            recursive: true
    +
    11497.      7        });
    +
    11498.      7        fileDict[pathname] = {
    +
    11499.      7            lineList,
    +
    11500.      7            linesCovered,
    +
    11501.      7            linesTotal,
    +
    11502.      7            modeCoverageIgnoreFile: (
    +
    11503.      7                (
    +
    11504.      7                    /^\/\*coverage-ignore-file\*\/$/m
    +
    11505.      7                ).test(source.slice(0, 65536))
    +
    11506.      7                ? "(ignore)"
    +
    11507.      7                : ""
    +
    11508.      7            ),
    +
    11509.      7            pathname
    +
    11510.      7        };
    +
    11511.      7        htmlRender({
    +
    11512.      7            fileList: [
    +
    11513.      7                fileDict[pathname]
    +
    11514.      7            ],
    +
    11515.      7            lineList,
    +
    11516.      7            pathname: coverageDir + pathname
    +
    11517.      7        });
    +
    11518.      7    }));
    +
    11519.      7    htmlRender({
    +
    11520.      7        fileList: Object.keys(fileDict).sort().map(function (pathname) {
    +
    11521.      7            return fileDict[pathname];
    +
    11522.      7        }),
    +
    11523.      7        modeIndex: true,
    +
    11524.      7        pathname: coverageDir + "index"
    +
    11525.      7    });
    +
    11526.      7    await Promise.all(promiseList);
    +
    11527.      7    assertOrThrow(
    +
    11528.      7        exitCode === 0,
    +
    11529.      7        "v8CoverageReportCreate - nonzero exitCode " + exitCode
    +
    11530.      7    );
    +
    11531.      7}
    +
    11532.      1
    +
    11533.      1/*
    +
    11534.      1function sentinel() {}
    +
    11535.      1*/
    +
    11536.      1
    +
    11537.      1// Export jslint as cjs/esm.
    +
    11538.      1
    +
    11539.      1jslint_export = Object.freeze(Object.assign(jslint, {
    +
    11540.      1    assertErrorThrownAsync,
    +
    11541.      1    assertJsonEqual,
    +
    11542.      1    assertOrThrow,
    +
    11543.      1    debugInline,
    +
    11544.      1    fsWriteFileWithParents,
    +
    11545.      1    globExclude,
    +
    11546.      1    htmlEscape,
    +
    11547.      1    jslint,
    +
    11548.      1    jslint_apidoc,
    +
    11549.      1    jslint_assert,
    +
    11550.      1    jslint_charset_ascii,
    +
    11551.      1    jslint_cli,
    +
    11552.      1    jslint_edition,
    +
    11553.      1    jslint_phase1_split,
    +
    11554.      1    jslint_phase2_lex,
    +
    11555.      1    jslint_phase3_parse,
    +
    11556.      1    jslint_phase4_walk,
    +
    11557.      1    jslint_phase5_whitage,
    +
    11558.      1    jslint_report,
    +
    11559.      1    jstestDescribe,
    +
    11560.      1    jstestIt,
    +
    11561.      1    jstestOnExit,
    +
    11562.      1    moduleFsInit,
    +
    11563.      1    noop,
    +
    11564.      1    objectDeepCopyWithKeysSorted,
    +
    11565.      1    v8CoverageListMerge,
    +
    11566.      1    v8CoverageReportCreate
    +
    11567.      1}));
    +
    11568.      1// module.exports = jslint_export;              // Export jslint as cjs.
    +
    11569.      1export default Object.freeze(jslint_export);    // Export jslint as esm.
    +
    11570.      1jslint_import_meta_url = import.meta.url;
    +
    11571.      1
    +
    11572.      1// Run jslint_cli.
    +
    11573.      1jslint_cli({});
    +
    11574.      1
    +
    + + + + diff --git a/branch-master/.artifact/coverage/jslint_wrapper_cjs.cjs.html b/branch-master/.artifact/coverage/jslint_wrapper_cjs.cjs.html new file mode 100644 index 000000000..7e2512946 --- /dev/null +++ b/branch-master/.artifact/coverage/jslint_wrapper_cjs.cjs.html @@ -0,0 +1,217 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / jslint_wrapper_cjs.cjs
    +
    +
    +
    +
    + 100.00 %
    + 49 / 49 +
    +
    + 0 / 49 +
    +
    + + +
    +
        1.      1// The Unlicense
    +
        2.      1//
    +
        3.      1// This is free and unencumbered software released into the public domain.
    +
        4.      1//
    +
        5.      1// Anyone is free to copy, modify, publish, use, compile, sell, or
    +
        6.      1// distribute this software, either in source code form or as a compiled
    +
        7.      1// binary, for any purpose, commercial or non-commercial, and by any
    +
        8.      1// means.
    +
        9.      1//
    +
       10.      1// In jurisdictions that recognize copyright laws, the author or authors
    +
       11.      1// of this software dedicate any and all copyright interest in the
    +
       12.      1// software to the public domain. We make this dedication for the benefit
    +
       13.      1// of the public at large and to the detriment of our heirs and
    +
       14.      1// successors. We intend this dedication to be an overt act of
    +
       15.      1// relinquishment in perpetuity of all present and future rights to this
    +
       16.      1// software under copyright law.
    +
       17.      1//
    +
       18.      1// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    +
       19.      1// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    +
       20.      1// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    +
       21.      1// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
    +
       22.      1// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
    +
       23.      1// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    +
       24.      1// OTHER DEALINGS IN THE SOFTWARE.
    +
       25.      1
    +
       26.      1
    +
       27.      1/*jslint beta, node*/
    +
       28.      1/*property
    +
       29.      1    module, readFileSync, replace, runInNewContext
    +
       30.      1*/
    +
       31.      1require("vm").runInNewContext(
    +
       32.      1    (
    +
       33.      1        "\"use strict\";"
    +
       34.      1        + require("fs").readFileSync(
    +
       35.      1            __dirname + "/jslint.mjs",
    +
       36.      1            "utf8"
    +
       37.      1        ).replace(
    +
       38.      1            "\nexport default Object.freeze(jslint_export);",
    +
       39.      1            "\nmodule.exports = jslint_export;"
    +
       40.      1        ).replace(
    +
       41.      1            "\njslint_import_meta_url = import.meta.url;",
    +
       42.      1            "\n// jslint_import_meta_url = import.meta.url;"
    +
       43.      1        )
    +
       44.      1    ),
    +
       45.      1    {
    +
       46.      1        module
    +
       47.      1    }
    +
       48.      1);
    +
       49.      1
    +
    + + + + diff --git a/branch-master/.artifact/coverage/test.mjs.html b/branch-master/.artifact/coverage/test.mjs.html new file mode 100644 index 000000000..455e80af5 --- /dev/null +++ b/branch-master/.artifact/coverage/test.mjs.html @@ -0,0 +1,1744 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test.mjs
    +
    +
    +
    +
    + 100.00 %
    + 1576 / 1576 +
    +
    + 0 / 1576 +
    +
    + + +
    +
        1.      1/*jslint beta, node*/
    +
        2.      1import jslint from "./jslint.mjs";
    +
        3.      1import jslintCjs from "./jslint_wrapper_cjs.cjs";
    +
        4.      1import moduleFs from "fs";
    +
        5.      1import modulePath from "path";
    +
        6.      1
    +
        7.      1let {
    +
        8.      1    assertErrorThrownAsync,
    +
        9.      1    assertJsonEqual,
    +
       10.      1    assertOrThrow,
    +
       11.      1    debugInline,
    +
       12.      1    fsWriteFileWithParents,
    +
       13.      1    globExclude,
    +
       14.      1    jstestDescribe,
    +
       15.      1    jstestIt,
    +
       16.      1    jstestOnExit,
    +
       17.      1    moduleFsInit,
    +
       18.      1    noop,
    +
       19.      1    v8CoverageListMerge,
    +
       20.      1    v8CoverageReportCreate
    +
       21.      1} = jslint;
    +
       22.      1let sourceJslintMjs;
    +
       23.      1let testCoverageMergeData;
    +
       24.      1
    +
       25.      1await (async function init() {
    +
       26.      1
    +
       27.      1// Coverage-hack - Ugly-hack to get test-coverage for all initialization-states.
    +
       28.      1
    +
       29.      1    moduleFsInit();
    +
       30.      1    moduleFsInit();
    +
       31.      1
    +
       32.      1// Cleanup directory .tmp
    +
       33.      1
    +
       34.      1    await moduleFs.promises.rm(".tmp", {
    +
       35.      1        recursive: true
    +
       36.      1    }).catch(noop);
    +
       37.      1
    +
       38.      1// init sourceJslintMjs
    +
       39.      1
    +
       40.      1    sourceJslintMjs = await moduleFs.promises.readFile("jslint.mjs", "utf8");
    +
       41.      1
    +
       42.      1// init testCoverageMergeData
    +
       43.      1
    +
       44.      1    testCoverageMergeData = JSON.parse(
    +
       45.      1        await moduleFs.promises.readFile(
    +
       46.      1            "test_coverage_merge_data.json",
    +
       47.      1            "utf8"
    +
       48.      1        )
    +
       49.      1    );
    +
       50.      1}());
    +
       51.      1
    +
       52.      1jstestDescribe((
    +
       53.      1    "test fsXxx handling-behavior"
    +
       54.      1), function testBehaviorFsXxx() {
    +
       55.      1    jstestIt((
    +
       56.      1        "test fsWriteFileWithParents handling-behavior"
    +
       57.      1    ), async function () {
    +
       58.      1        await Promise.all([
    +
       59.      1            1, 2, 3, 4
    +
       60.      4        ].map(async function () {
    +
       61.      4            await fsWriteFileWithParents(
    +
       62.      4                ".tmp/fsWriteFileWithParents/aa/bb/cc",
    +
       63.      4                "aa"
    +
       64.      4            );
    +
       65.      4        }));
    +
       66.      1        assertJsonEqual(
    +
       67.      1            await moduleFs.promises.readFile(
    +
       68.      1                ".tmp/fsWriteFileWithParents/aa/bb/cc",
    +
       69.      1                "utf8"
    +
       70.      1            ),
    +
       71.      1            "aa"
    +
       72.      1        );
    +
       73.      1    });
    +
       74.      1});
    +
       75.      1
    +
       76.      1jstestDescribe((
    +
       77.      1    "test globXxx handling-behavior"
    +
       78.      1), function testBehaviorGlobXxx() {
    +
       79.      1    jstestIt((
    +
       80.      1        "test globAssertNotWeird-error handling-behavior"
    +
       81.      1    ), async function () {
    +
       82.      1        await Promise.all([
    +
       83.      1            "\n",
    +
       84.      1            "\r",
    +
       85.      1            "\u0000"
    +
       86.      3        ].map(async function (char) {
    +
       87.      3            await assertErrorThrownAsync(function () {
    +
       88.      3                return globExclude({
    +
       89.      3                    pathnameList: [
    +
       90.      3                        "aa",
    +
       91.      3                        `cc/${char}/dd`,
    +
       92.      3                        "bb"
    +
       93.      3                    ]
    +
       94.      3                });
    +
       95.      3            }, (
    +
       96.      3                "Weird character "
    +
       97.      3                + JSON.stringify(char).replace("\\", "\\\\")
    +
       98.      3                + " found in "
    +
       99.      3            ));
    +
      100.      3        }));
    +
      101.      1    });
    +
      102.      1    jstestIt((
    +
      103.      1        "test globExclude handling-behavior"
    +
      104.      1    ), function () {
    +
      105.      1        let pathnameList = [
    +
      106.      1            ".dockerignore",
    +
      107.      1            ".eslintrc.js",
    +
      108.      1            ".gitignore",
    +
      109.      1            ".npmignore",
    +
      110.      1            ".travis.yml",
    +
      111.      1            "/node_modules/aa/bb/cc.js",
    +
      112.      1            "/node_modules/aa/bb/dd.js",
    +
      113.      1            "CHANGELOG.md",
    +
      114.      1            "CONTRIBUTING.md",
    +
      115.      1            "Dockerfile",
    +
      116.      1            "LICENSE",
    +
      117.      1            "Makefile",
    +
      118.      1            "README.md",
    +
      119.      1            "appveyor.yml",
    +
      120.      1            "benchmark/insert-transaction.sql",
    +
      121.      1            "benchmark/insert.js",
    +
      122.      1            "binding.gyp",
    +
      123.      1            "cloudformation/ci.template.js",
    +
      124.      1            "deps/common-sqlite.gypi",
    +
      125.      1            "deps/extract.py",
    +
      126.      1            "deps/sqlite-autoconf-3340000.tar.gz",
    +
      127.      1            "deps/sqlite3.gyp",
    +
      128.      1            "examples/simple-chaining.js",
    +
      129.      1            "lib/index.js",
    +
      130.      1            "lib/sqlite3-binding.js",
    +
      131.      1            "lib/sqlite3.js",
    +
      132.      1            "lib/trace.js",
    +
      133.      1            "node_modules/aa/bb/cc.js",
    +
      134.      1            "node_modules/aa/bb/dd.js",
    +
      135.      1            "package.json",
    +
      136.      1            "scripts/build-appveyor.bat",
    +
      137.      1            "scripts/build-local.bat",
    +
      138.      1            "scripts/build_against_electron.sh",
    +
      139.      1            "scripts/build_against_node.sh",
    +
      140.      1            "scripts/build_against_node_webkit.sh",
    +
      141.      1            "scripts/build_for_node_webkit.cmd",
    +
      142.      1            "scripts/install_node.sh",
    +
      143.      1            "scripts/validate_tag.sh",
    +
      144.      1            "sqlite3.js",
    +
      145.      1            "src/async.h",
    +
      146.      1            "src/backup.cc",
    +
      147.      1            "src/backup.h",
    +
      148.      1            "src/database.cc",
    +
      149.      1            "src/database.h",
    +
      150.      1            "src/gcc-preinclude.h",
    +
      151.      1            "src/macros.h",
    +
      152.      1            "src/node_sqlite3.cc",
    +
      153.      1            "src/statement.cc",
    +
      154.      1            "src/statement.h",
    +
      155.      1            "src/threading.h",
    +
      156.      1            "test/affected.test.js",
    +
      157.      1            "test/backup.test.js",
    +
      158.      1            "test/blob.test.js",
    +
      159.      1            "test/cache.test.js",
    +
      160.      1            "test/constants.test.js",
    +
      161.      1            "test/database_fail.test.js",
    +
      162.      1            "test/each.test.js",
    +
      163.      1            "test/exec.test.js",
    +
      164.      1            "test/extension.test.js",
    +
      165.      1            "test/fts-content.test.js",
    +
      166.      1            "test/interrupt.test.js",
    +
      167.      1            "test/issue-108.test.js",
    +
      168.      1            "test/json.test.js",
    +
      169.      1            "test/map.test.js",
    +
      170.      1            "test/named_columns.test.js",
    +
      171.      1            "test/named_params.test.js",
    +
      172.      1            "test/null_error.test.js",
    +
      173.      1            "test/nw/.gitignore",
    +
      174.      1            "test/nw/Makefile",
    +
      175.      1            "test/nw/index.html",
    +
      176.      1            "test/nw/package.json",
    +
      177.      1            "test/open_close.test.js",
    +
      178.      1            "test/other_objects.test.js",
    +
      179.      1            "test/parallel_insert.test.js",
    +
      180.      1            "test/prepare.test.js",
    +
      181.      1            "test/profile.test.js",
    +
      182.      1            "test/rerun.test.js",
    +
      183.      1            "test/scheduling.test.js",
    +
      184.      1            "test/serialization.test.js",
    +
      185.      1            "test/support/createdb-electron.js",
    +
      186.      1            "test/support/createdb.js",
    +
      187.      1            "test/support/elmo.png",
    +
      188.      1            "test/support/helper.js",
    +
      189.      1            "test/support/prepare.db",
    +
      190.      1            "test/support/script.sql",
    +
      191.      1            "test/trace.test.js",
    +
      192.      1            "test/unicode.test.js",
    +
      193.      1            "test/upsert.test.js",
    +
      194.      1            "test/verbose.test.js",
    +
      195.      1            "tools/docker/architecture/linux-arm/Dockerfile",
    +
      196.      1            "tools/docker/architecture/linux-arm/run.sh",
    +
      197.      1            "tools/docker/architecture/linux-arm64/Dockerfile",
    +
      198.      1            "tools/docker/architecture/linux-arm64/run.sh"
    +
      199.      1        ];
    +
      200.      1        [
    +
      201.      1            "tes?/",
    +
      202.      1            "tes[-t-]/",
    +
      203.      1            "tes[-t]/",
    +
      204.      1            "tes[0-9A-Z_a-z-]/",
    +
      205.      1            "tes[t-]/",
    +
      206.      1            "test/**/*.js"
    +
      207.      6        ].forEach(function (aa) {
    +
      208.      6            [
    +
      209.      6                "li*/*.js",
    +
      210.      6                "li?/*.js",
    +
      211.      6                "lib/",
    +
      212.      6                "lib/*",
    +
      213.      6                "lib/**/*.js",
    +
      214.      6                "lib/*.js"
    +
      215.     36            ].forEach(function (bb) {
    +
      216.     36                [
    +
      217.     36                    "",
    +
      218.     36                    "**/node_modules/",
    +
      219.     36                    "node_modules/"
    +
      220.    108                ].forEach(function (cc) {
    +
      221.    108                    assertJsonEqual(
    +
      222.    108                        globExclude({
    +
      223.    108                            excludeList: [
    +
      224.    108                                "tes[!0-9A-Z_a-z-]/",
    +
      225.    108                                "tes[^0-9A-Z_a-z-]/",
    +
      226.    108                                "test/suppor*/*elper.js",
    +
      227.    108                                "test/suppor?/?elper.js",
    +
      228.    108                                "test/support/helper.js"
    +
      229.    108                            ].concat(aa, cc),
    +
      230.    108                            includeList: [
    +
      231.    108                                "**/*.cjs",
    +
      232.    108                                "**/*.js",
    +
      233.    108                                "**/*.mjs",
    +
      234.    108                                "lib/sqlite3.js"
    +
      235.    108                            ].concat(bb),
    +
      236.    108                            pathnameList
    +
      237.    108                        }).pathnameList,
    +
      238.    108                        [
    +
      239.    108                            ".eslintrc.js",
    +
      240.    108                            "benchmark/insert.js",
    +
      241.    108                            "cloudformation/ci.template.js",
    +
      242.    108                            "examples/simple-chaining.js",
    +
      243.    108                            "lib/index.js",
    +
      244.    108                            "lib/sqlite3-binding.js",
    +
      245.    108                            "lib/sqlite3.js",
    +
      246.    108                            "lib/trace.js",
    +
      247.    108                            "sqlite3.js"
    +
      248.    108                        ].concat(
    +
      249.    108                            cc === "**/node_modules/"
    +
      250.     36                            ? [
    +
      251.     36                                "node_modules/aa/bb/cc.js",
    +
      252.     36                                "node_modules/aa/bb/dd.js"
    +
      253.     36                            ]
    +
      254.     72                            : cc === "node_modules/"
    +
      255.     72                            ? [
    +
      256.     72                                "/node_modules/aa/bb/cc.js",
    +
      257.     72                                "/node_modules/aa/bb/dd.js"
    +
      258.     72                            ]
    +
      259.     72                            : [
    +
      260.     72                                "/node_modules/aa/bb/cc.js",
    +
      261.     72                                "/node_modules/aa/bb/dd.js",
    +
      262.     72                                "node_modules/aa/bb/cc.js",
    +
      263.     72                                "node_modules/aa/bb/dd.js"
    +
      264.     72                            ]
    +
      265.    108                        ).sort()
    +
      266.    108                    );
    +
      267.    108                });
    +
      268.     36            });
    +
      269.      6        });
    +
      270.      1    });
    +
      271.      1    jstestIt((
    +
      272.      1        "test globToRegexp handling-behavior"
    +
      273.      1    ), function () {
    +
      274.      1        Object.entries({
    +
      275.      1            "*": (
    +
      276.      1                /^[^\/]*?$/gm
    +
      277.      1            ),
    +
      278.      1            "**": (
    +
      279.      1                /^.*?$/gm
    +
      280.      1            ),
    +
      281.      1            "***": (
    +
      282.      1                /^.*?$/gm
    +
      283.      1            ),
    +
      284.      1            "****": (
    +
      285.      1                /^.*?$/gm
    +
      286.      1            ),
    +
      287.      1            "****////****": (
    +
      288.      1                /^.*?$/gm
    +
      289.      1            ),
    +
      290.      1            "***///***": (
    +
      291.      1                /^.*?$/gm
    +
      292.      1            ),
    +
      293.      1            "**/*": (
    +
      294.      1                /^.*?$/gm
    +
      295.      1            ),
    +
      296.      1            "**/node_modules/": (
    +
      297.      1                /^.*?\/node_modules\/.*?$/gm
    +
      298.      1            ),
    +
      299.      1            "**/node_modules/**/*": (
    +
      300.      1                /^.*?\/node_modules\/.*?$/gm
    +
      301.      1            ),
    +
      302.      1            "?": (
    +
      303.      1                /^[^\/]$/gm
    +
      304.      1            ),
    +
      305.      1            "[!0-9A-Za-z-]": (
    +
      306.      1                /^[^0-9A-Za-z\-]$/gm
    +
      307.      1            ),
    +
      308.      1            "[0-9A-Za-z-]": (
    +
      309.      1                /^[0-9A-Za-z\-]$/gm
    +
      310.      1            ),
    +
      311.      1            "[[]] ]][[": (
    +
      312.      1                /^[\[]\] \]\][\[]$/gm
    +
      313.      1            ),
    +
      314.      1            "[]": (
    +
      315.      1                /^$/gm
    +
      316.      1            ),
    +
      317.      1            "[^0-9A-Za-z-]": (
    +
      318.      1                /^[^0-9A-Za-z\-]$/gm
    +
      319.      1            ),
    +
      320.      1            "aa/bb/cc": (
    +
      321.      1                /^aa\/bb\/cc$/gm
    +
      322.      1            ),
    +
      323.      1            "aa/bb/cc/": (
    +
      324.      1                /^aa\/bb\/cc\/.*?$/gm
    +
      325.      1            ),
    +
      326.      1            "li*/*": (
    +
      327.      1                /^li[^\/]*?\/[^\/]*?$/gm
    +
      328.      1            ),
    +
      329.      1            "li?/*": (
    +
      330.      1                /^li[^\/]\/[^\/]*?$/gm
    +
      331.      1            ),
    +
      332.      1            "lib/": (
    +
      333.      1                /^lib\/.*?$/gm
    +
      334.      1            ),
    +
      335.      1            "lib/*": (
    +
      336.      1                /^lib\/[^\/]*?$/gm
    +
      337.      1            ),
    +
      338.      1            "lib/**/*.js": (
    +
      339.      1                /^lib\/.*?\.js$/gm
    +
      340.      1            ),
    +
      341.      1            "lib/*.js": (
    +
      342.      1                /^lib\/[^\/]*?\.js$/gm
    +
      343.      1            ),
    +
      344.      1            "node_modules/": (
    +
      345.      1                /^node_modules\/.*?$/gm
    +
      346.      1            ),
    +
      347.      1            "node_modules/**/*": (
    +
      348.      1                /^node_modules\/.*?$/gm
    +
      349.      1            ),
    +
      350.      1            "tes[!0-9A-Z_a-z-]/**/*": (
    +
      351.      1                /^tes[^0-9A-Z_a-z\-]\/.*?$/gm
    +
      352.      1            ),
    +
      353.      1            "tes[0-9A-Z_a-z-]/**/*": (
    +
      354.      1                /^tes[0-9A-Z_a-z\-]\/.*?$/gm
    +
      355.      1            ),
    +
      356.      1            "tes[^0-9A-Z_a-z-]/**/*": (
    +
      357.      1                /^tes[^0-9A-Z_a-z\-]\/.*?$/gm
    +
      358.      1            ),
    +
      359.      1            "test/**/*": (
    +
      360.      1                /^test\/.*?$/gm
    +
      361.      1            ),
    +
      362.      1            "test/**/*.js": (
    +
      363.      1                /^test\/.*?\.js$/gm
    +
      364.      1            ),
    +
      365.      1            "test/suppor*/*elper.js": (
    +
      366.      1                /^test\/suppor[^\/]*?\/[^\/]*?elper\.js$/gm
    +
      367.      1            ),
    +
      368.      1            "test/suppor?/?elper.js": (
    +
      369.      1                /^test\/suppor[^\/]\/[^\/]elper\.js$/gm
    +
      370.      1            ),
    +
      371.      1            "test/support/helper.js": (
    +
      372.      1                /^test\/support\/helper\.js$/gm
    +
      373.      1            )
    +
      374.     33        }).forEach(function ([
    +
      375.     33            pattern, rgx
    +
      376.     33        ]) {
    +
      377.     33            assertJsonEqual(
    +
      378.     33                globExclude({
    +
      379.     33                    excludeList: [
    +
      380.     33                        pattern
    +
      381.     33                    ]
    +
      382.     33                }).excludeList[0].source,
    +
      383.     33                rgx.source
    +
      384.     33            );
    +
      385.     33            assertJsonEqual(
    +
      386.     33                globExclude({
    +
      387.     33                    includeList: [
    +
      388.     33                        pattern
    +
      389.     33                    ]
    +
      390.     33                }).includeList[0].source,
    +
      391.     33                rgx.source
    +
      392.     33            );
    +
      393.     33        });
    +
      394.      1    });
    +
      395.      1});
    +
      396.      1
    +
      397.      1jstestDescribe((
    +
      398.      1    "test jslint's cli handling-behavior"
    +
      399.      1), function testBehaviorJslintCli() {
    +
      400.      5    function processExit0(exitCode) {
    +
      401.      5        assertOrThrow(exitCode === 0, exitCode);
    +
      402.      5    }
    +
      403.      6    function processExit1(exitCode) {
    +
      404.      6        assertOrThrow(exitCode === 1, exitCode);
    +
      405.      6    }
    +
      406.      1    jstestIt((
    +
      407.      1        "test cli-null-case handling-behavior"
    +
      408.      1    ), function () {
    +
      409.      1        jslint.jslint_cli({
    +
      410.      1            mode_noop: true,
    +
      411.      1            process_exit: processExit0
    +
      412.      1        });
    +
      413.      1    });
    +
      414.      1    jstestIt((
    +
      415.      1        "test cli-window-jslint handling-behavior"
    +
      416.      1    ), function () {
    +
      417.      1        [
    +
      418.      1            "&window_jslint=",
    +
      419.      1            "&window_jslint=12",
    +
      420.      1            "&window_jslint=1?",
    +
      421.      1            "&window_jslint=?",
    +
      422.      1            "?window_jslint=",
    +
      423.      1            "?window_jslint=12",
    +
      424.      1            "?window_jslint=1?",
    +
      425.      1            "?window_jslint=?",
    +
      426.      1            "window_jslint=1",
    +
      427.      1            "window_jslint=1&",
    +
      428.      1            "window_jslint=12",
    +
      429.      1            "window_jslint=1?"
    +
      430.     12        ].forEach(function (import_meta_url) {
    +
      431.     12            jslint.jslint_cli({
    +
      432.     12                import_meta_url
    +
      433.     12            });
    +
      434.     12            assertOrThrow(globalThis.jslint === undefined);
    +
      435.     12        });
    +
      436.      1        [
    +
      437.      1            "&window_jslint=1",
    +
      438.      1            "&window_jslint=1&",
    +
      439.      1            "?window_jslint=1",
    +
      440.      1            "?window_jslint=1&"
    +
      441.      4        ].forEach(function (import_meta_url) {
    +
      442.      4            jslint.jslint_cli({
    +
      443.      4                import_meta_url
    +
      444.      4            });
    +
      445.      4            assertOrThrow(globalThis.jslint === jslint);
    +
      446.      4            delete globalThis.jslint;
    +
      447.      4        });
    +
      448.      1    });
    +
      449.      1    jstestIt((
    +
      450.      1        "test cli-cjs-and-invalid-file handling-behavior"
    +
      451.      1    ), async function () {
    +
      452.      1        await fsWriteFileWithParents(".test_dir.cjs/touch.txt", "");
    +
      453.      1        [
    +
      454.      1            ".",            // test dir handling-behavior
    +
      455.      1            "jslint.mjs",   // test file handling-behavior
    +
      456.      1            undefined       // test file-undefined handling-behavior
    +
      457.      3        ].forEach(function (file) {
    +
      458.      3            jslint.jslint_cli({
    +
      459.      3                file,
    +
      460.      3                mode_cli: true,
    +
      461.      3                process_env: {
    +
      462.      3                    JSLINT_BETA: "1"
    +
      463.      3                },
    +
      464.      3                process_exit: processExit0
    +
      465.      3            });
    +
      466.      3        });
    +
      467.      1    });
    +
      468.      1    jstestIt((
    +
      469.      1        "test cli-apidoc handling-behavior"
    +
      470.      1    ), function () {
    +
      471.      1        jslint.jslint_cli({
    +
      472.      1            mode_cli: true,
    +
      473.      1            process_argv: [
    +
      474.      1                "node",
    +
      475.      1                "jslint.mjs",
    +
      476.      1                "jslint_apidoc=.artifact/apidoc.html",
    +
      477.      1                JSON.stringify({
    +
      478.      1                    example_list: [
    +
      479.      1                        "README.md",
    +
      480.      1                        "test.mjs",
    +
      481.      1                        "jslint.mjs"
    +
      482.      1                    ],
    +
      483.      1                    github_repo: "https://github.com/jslint-org/jslint",
    +
      484.      1                    module_list: [
    +
      485.      1                        {
    +
      486.      1                            pathname: "./jslint.mjs"
    +
      487.      1                        }
    +
      488.      1                    ],
    +
      489.      1                    package_name: "JSLint",
    +
      490.      1                    version: jslint.jslint_edition
    +
      491.      1                })
    +
      492.      1            ],
    +
      493.      1            process_exit: processExit0
    +
      494.      1        });
    +
      495.      1    });
    +
      496.      1    jstestIt((
    +
      497.      1        "test cli-file-error handling-behavior"
    +
      498.      1    ), function () {
    +
      499.      1        jslint.jslint_cli({
    +
      500.      1            // suppress error
    +
      501.      1            console_error: noop,
    +
      502.      1            file: "undefined",
    +
      503.      1            mode_cli: true,
    +
      504.      1            process_exit: processExit1
    +
      505.      1        });
    +
      506.      1    });
    +
      507.      1    jstestIt((
    +
      508.      1        "test cli-syntax-error handling-behavior"
    +
      509.      1    ), function () {
    +
      510.      1        jslint.jslint_cli({
    +
      511.      1            // suppress error
    +
      512.      1            console_error: noop,
    +
      513.      1            file: "syntax-error.js",
    +
      514.      1            mode_cli: true,
    +
      515.      1            option: {
    +
      516.      1                trace: true
    +
      517.      1            },
    +
      518.      1            process_exit: processExit1,
    +
      519.      1            source: "syntax error"
    +
      520.      1        });
    +
      521.      1    });
    +
      522.      1    jstestIt((
    +
      523.      1        "test cli-report handling-behavior"
    +
      524.      1    ), function () {
    +
      525.      1        jslint.jslint_cli({
    +
      526.      1            // suppress error
    +
      527.      1            console_error: noop,
    +
      528.      1            mode_cli: true,
    +
      529.      1            process_argv: [
    +
      530.      1                "node",
    +
      531.      1                "jslint.mjs",
    +
      532.      1                "jslint_report=.tmp/jslint_report.html",
    +
      533.      1                "jslint.mjs"
    +
      534.      1            ],
    +
      535.      1            process_exit: processExit0
    +
      536.      1        });
    +
      537.      1    });
    +
      538.      1    jstestIt((
    +
      539.      1        "test cli-report-error handling-behavior"
    +
      540.      1    ), function () {
    +
      541.      1        jslint.jslint_cli({
    +
      542.      1            // suppress error
    +
      543.      1            console_error: noop,
    +
      544.      1            mode_cli: true,
    +
      545.      1            process_argv: [
    +
      546.      1                "node",
    +
      547.      1                "jslint.mjs",
    +
      548.      1                "jslint_report=.tmp/jslint_report.html",
    +
      549.      1                "syntax-error.js"
    +
      550.      1            ],
    +
      551.      1            process_exit: processExit1,
    +
      552.      1            source: "syntax error"
    +
      553.      1        });
    +
      554.      1    });
    +
      555.      1    jstestIt((
    +
      556.      1        "test cli-report-json handling-behavior"
    +
      557.      1    ), function () {
    +
      558.      1        jslint.jslint_cli({
    +
      559.      1            // suppress error
    +
      560.      1            console_error: noop,
    +
      561.      1            mode_cli: true,
    +
      562.      1            process_argv: [
    +
      563.      1                "node",
    +
      564.      1                "jslint.mjs",
    +
      565.      1                "jslint_report=.tmp/jslint_report.html",
    +
      566.      1                "aa.json"
    +
      567.      1            ],
    +
      568.      1            process_exit: processExit0,
    +
      569.      1            source: "[]"
    +
      570.      1        });
    +
      571.      1    });
    +
      572.      1    jstestIt((
    +
      573.      1        "test cli-report-json-error handling-behavior"
    +
      574.      1    ), function () {
    +
      575.      1        jslint.jslint_cli({
    +
      576.      1            // suppress error
    +
      577.      1            console_error: noop,
    +
      578.      1            mode_cli: true,
    +
      579.      1            process_argv: [
    +
      580.      1                "node",
    +
      581.      1                "jslint.mjs",
    +
      582.      1                "jslint_report=.tmp/jslint_report.html",
    +
      583.      1                "aa.json"
    +
      584.      1            ],
    +
      585.      1            process_exit: processExit1,
    +
      586.      1            source: "["
    +
      587.      1        });
    +
      588.      1    });
    +
      589.      1    jstestIt((
    +
      590.      1        "test cli-report-misc handling-behavior"
    +
      591.      1    ), function () {
    +
      592.      1        jslint.jslint_cli({
    +
      593.      1            // suppress error
    +
      594.      1            console_error: noop,
    +
      595.      1            mode_cli: true,
    +
      596.      1            process_argv: [
    +
      597.      1                "node",
    +
      598.      1                "jslint.mjs",
    +
      599.      1                "jslint_report=.tmp/jslint_report.html",
    +
      600.      1                "aa.js"
    +
      601.      1            ],
    +
      602.      1            process_exit: processExit0,
    +
      603.      1            source: "let aa = 0;"
    +
      604.      1        });
    +
      605.      1        jslint.jslint_cli({
    +
      606.      1            // suppress error
    +
      607.      1            console_error: noop,
    +
      608.      1            mode_cli: true,
    +
      609.      1            process_argv: [
    +
      610.      1                "node",
    +
      611.      1                "jslint.mjs",
    +
      612.      1                "jslint_report=.tmp/jslint_report.html",
    +
      613.      1                "aa.js"
    +
      614.      1            ],
    +
      615.      1            process_exit: processExit1,
    +
      616.      1            source: "(aa)=>aa; function aa([aa]){}"
    +
      617.      1        });
    +
      618.      1    });
    +
      619.      1    jstestIt((
    +
      620.      1        "test cli-jslint-wrapper-vim handling-behavior"
    +
      621.      1    ), function () {
    +
      622.      1        jslint.jslint_cli({
    +
      623.      1            // suppress error
    +
      624.      1            console_error: noop,
    +
      625.      1            mode_cli: true,
    +
      626.      1            process_argv: [
    +
      627.      1                "node",
    +
      628.      1                "jslint.mjs",
    +
      629.      1                "jslint_wrapper_vim",
    +
      630.      1                "syntax-error.js"
    +
      631.      1            ],
    +
      632.      1            process_exit: processExit1,
    +
      633.      1            source: "syntax error"
    +
      634.      1        });
    +
      635.      1    });
    +
      636.      1});
    +
      637.      1
    +
      638.      1jstestDescribe((
    +
      639.      1    "test jslint's no-warnings handling-behavior"
    +
      640.      1), function testBehaviorJslintNoWarnings() {
    +
      641.      1    jstestIt((
    +
      642.      1        "test jslint's no-warnings handling-behavior"
    +
      643.      1    ), function () {
    +
      644.      1        Object.values({
    +
      645.      1            array: [
    +
      646.      1                "new Array(0);"
    +
      647.      1            ],
    +
      648.      1            async_await: [
    +
      649.      1                "async function aa() {\n    await aa();\n}",
    +
      650.      1
    +
      651.      1// PR-405 - Bugfix - fix expression after "await" mis-identified as statement.
    +
      652.      1
    +
      653.      1                "async function aa() {\n    await aa;\n}",
    +
      654.      1                (
    +
      655.      1                    "async function aa() {\n"
    +
      656.      1                    + "    try {\n"
    +
      657.      1                    + "        aa();\n"
    +
      658.      1                    + "    } catch (err) {\n"
    +
      659.      1                    + "        await err();\n"
    +
      660.      1                    + "    }\n"
    +
      661.      1                    + "}\n"
    +
      662.      1                ),
    +
      663.      1                (
    +
      664.      1                    "async function aa() {\n"
    +
      665.      1                    + "    try {\n"
    +
      666.      1                    + "        await aa();\n"
    +
      667.      1                    + "    } catch (err) {\n"
    +
      668.      1                    + "        await err();\n"
    +
      669.      1                    + "    }\n"
    +
      670.      1                    + "}\n"
    +
      671.      1                ),
    +
      672.      1
    +
      673.      1// PR-370 - Add top-level-await support.
    +
      674.      1
    +
      675.      1                "await String();\n"
    +
      676.      1            ],
    +
      677.      1
    +
      678.      1// PR-351 - Add BigInt support.
    +
      679.      1
    +
      680.      1            bigint: [
    +
      681.      1                "let aa = 0b0n;\n",
    +
      682.      1                "let aa = 0o0n;\n",
    +
      683.      1                "let aa = 0x0n;\n",
    +
      684.      1                "let aa = BigInt(0n);\n",
    +
      685.      1                "let aa = typeof aa === \"bigint\";\n"
    +
      686.      1            ],
    +
      687.      1            date: [
    +
      688.      1                "Date.getTime();",
    +
      689.      1                "let aa = aa().getTime();",
    +
      690.      1                "let aa = aa.aa().getTime();"
    +
      691.      1            ],
    +
      692.      1            directive: [
    +
      693.      1                "#!\n/*jslint browser:false, node*/\n\"use strict\";",
    +
      694.      1                "/*property aa bb*/"
    +
      695.      1            ],
    +
      696.      1            for: [
    +
      697.      1                (
    +
      698.      1                    "/*jslint for*/\n"
    +
      699.      1                    + "function aa(bb) {\n"
    +
      700.      1                    + "    for (bb = 0; bb < 0; bb += 1) {\n"
    +
      701.      1                    + "        bb();\n"
    +
      702.      1                    + "    }\n"
    +
      703.      1                    + "}\n"
    +
      704.      1                )
    +
      705.      1            ],
    +
      706.      1            jslint_disable: [
    +
      707.      1                "/*jslint-disable*/\n0\n/*jslint-enable*/"
    +
      708.      1            ],
    +
      709.      1            jslint_ignore_line: [
    +
      710.      1                "0 //jslint-ignore-line"
    +
      711.      1            ],
    +
      712.      1            json: [
    +
      713.      1                "{\"aa\":[[],-0,null]}"
    +
      714.      1            ],
    +
      715.      1            label: [
    +
      716.      1                (
    +
      717.      1                    "function aa() {\n"
    +
      718.      1                    + "bb:\n"
    +
      719.      1                    + "    while (true) {\n"
    +
      720.      1                    + "        if (true) {\n"
    +
      721.      1                    + "            break bb;\n"
    +
      722.      1                    + "        }\n"
    +
      723.      1                    + "    }\n"
    +
      724.      1                    + "}\n"
    +
      725.      1                )
    +
      726.      1            ],
    +
      727.      1            loop: [
    +
      728.      1                (
    +
      729.      1                    "function aa() {\n"
    +
      730.      1                    + "    do {\n"
    +
      731.      1                    + "        aa();\n"
    +
      732.      1                    + "    } while (aa());\n"
    +
      733.      1                    + "}\n"
    +
      734.      1                ),
    +
      735.      1
    +
      736.      1// PR-378 - Relax warning "function_in_loop".
    +
      737.      1
    +
      738.      1                (
    +
      739.      1                    "function aa() {\n"
    +
      740.      1                    + "    while (true) {\n"
    +
      741.      1                    + "        (function () {\n"
    +
      742.      1                    + "            return;\n"
    +
      743.      1                    + "        }());\n"
    +
      744.      1                    + "    }\n"
    +
      745.      1                    + "}\n"
    +
      746.      1                )
    +
      747.      1            ],
    +
      748.      1            module: [
    +
      749.      1                "export default Object.freeze();",
    +
      750.      1
    +
      751.      1// PR-439 - Add grammar for "export async function ...".
    +
      752.      1
    +
      753.      1                (
    +
      754.      1                    "export default Object.freeze(async function () {\n"
    +
      755.      1                    + "    return await 0;\n"
    +
      756.      1                    + "});\n"
    +
      757.      1                ),
    +
      758.      1                "import {aa, bb} from \"aa\";\naa(bb);",
    +
      759.      1                "import {} from \"aa\";",
    +
      760.      1                "import(\"aa\").then(function () {\n    return;\n});",
    +
      761.      1                (
    +
      762.      1                    "let aa = 0;\n"
    +
      763.      1                    + "import(aa).then(aa).then(aa)"
    +
      764.      1                    + ".catch(aa).finally(aa);\n"
    +
      765.      1                )
    +
      766.      1            ],
    +
      767.      1            number: [
    +
      768.      1                "let aa = 0.0e0;",
    +
      769.      1                "let aa = 0b0;",
    +
      770.      1                "let aa = 0o0;",
    +
      771.      1                "let aa = 0x0;"
    +
      772.      1            ],
    +
      773.      1
    +
      774.      1// PR-390 - Add numeric-separator support.
    +
      775.      1
    +
      776.      1            numeric_separator: [
    +
      777.      1                "let aa = 0.0_0_0;",
    +
      778.      1                "let aa = 0b0_1111_1111n;\n",
    +
      779.      1                "let aa = 0o0_1234_1234n;\n",
    +
      780.      1                "let aa = 0x0_1234_1234n;\n",
    +
      781.      1                "let aa = 1_234_234.1_234_234E1_234_234;"
    +
      782.      1            ],
    +
      783.      1            optional_chaining: [
    +
      784.      1                "let aa = aa?.bb?.cc;"
    +
      785.      1            ],
    +
      786.      1            param: [
    +
      787.      1                "function aa({aa, bb}) {\n    return {aa, bb};\n}\n",
    +
      788.      1                (
    +
      789.      1                    "function aa({constructor}) {\n"
    +
      790.      1                    + "    return {constructor};\n"
    +
      791.      1                    + "}\n"
    +
      792.      1                )
    +
      793.      1            ],
    +
      794.      1            property: [
    +
      795.      1                "let aa = aa[`!`];"
    +
      796.      1            ],
    +
      797.      1            regexp: [
    +
      798.      1                "function aa() {\n    return /./;\n}",
    +
      799.      1                "let aa = /(?!.)(?:.)(?=.)/;",
    +
      800.      1                "let aa = /./gimuy;",
    +
      801.      1                "let aa = /[\\--\\-]/;"
    +
      802.      1            ],
    +
      803.      1            ternary: [
    +
      804.      1                (
    +
      805.      1                    "let aa = (\n"
    +
      806.      1                    + "    aa()\n"
    +
      807.      1                    + "    ? 0\n"
    +
      808.      1                    + "    : 1\n"
    +
      809.      1                    + ") "
    +
      810.      1                    + "&& (\n"
    +
      811.      1                    + "    aa()\n"
    +
      812.      1                    + "    ? 0\n"
    +
      813.      1                    + "    : 1\n"
    +
      814.      1                    + ");"
    +
      815.      1                ),
    +
      816.      1                (
    +
      817.      1                    "let aa = (\n"
    +
      818.      1                    + "    aa()\n"
    +
      819.      1                    + "    ? `${0}`\n"
    +
      820.      1                    + "    : `${1}`\n"
    +
      821.      1                    + ");"
    +
      822.      1                ),
    +
      823.      1
    +
      824.      1// PR-394 - Bugfix
    +
      825.      1// Fix jslint falsely believing megastring literals `0` and `1` are similar.
    +
      826.      1
    +
      827.      1                (
    +
      828.      1                    "let aa = (\n"
    +
      829.      1                    + "    aa()\n"
    +
      830.      1                    + "    ? `0`\n"
    +
      831.      1                    + "    : `1`\n"
    +
      832.      1                    + ");"
    +
      833.      1                )
    +
      834.      1            ],
    +
      835.      1            try_catch: [
    +
      836.      1                (
    +
      837.      1                    "let aa = 0;\n"
    +
      838.      1                    + "try {\n"
    +
      839.      1                    + "    aa();\n"
    +
      840.      1                    + "} catch (err) {\n"
    +
      841.      1                    + "    aa = err;\n"
    +
      842.      1                    + "}\n"
    +
      843.      1                    + "try {\n"
    +
      844.      1                    + "    aa();\n"
    +
      845.      1                    + "} catch (err) {\n"
    +
      846.      1                    + "    aa = err;\n"
    +
      847.      1                    + "}\n"
    +
      848.      1                    + "aa();\n"
    +
      849.      1                )
    +
      850.      1            ],
    +
      851.      1            try_finally: [
    +
      852.      1                (
    +
      853.      1                    "let aa = 0;\n"
    +
      854.      1                    + "try {\n"
    +
      855.      1                    + "    aa();\n"
    +
      856.      1                    + "} finally {\n"
    +
      857.      1                    + "    aa();\n"
    +
      858.      1                    + "}\n"
    +
      859.      1                )
    +
      860.      1            ],
    +
      861.      1            use_strict: [
    +
      862.      1                (
    +
      863.      1                    "\"use strict\";\n"
    +
      864.      1                    + "let aa = 0;\n"
    +
      865.      1                    + "function bb() {\n"
    +
      866.      1                    + "    \"use strict\";\n"
    +
      867.      1                    + "    return aa;\n"
    +
      868.      1                    + "}\n"
    +
      869.      1                )
    +
      870.      1            ],
    +
      871.      1            var: [
    +
      872.      1
    +
      873.      1// PR-363 - Bugfix
    +
      874.      1// Add test against false-warning <uninitialized 'bb'> in code
    +
      875.      1// '/*jslint node*/\nlet {aa:bb} = {}; bb();'.
    +
      876.      1
    +
      877.      1                "/*jslint node*/\n",
    +
      878.      1                ""
    +
      879.      2            ].map(function (directive) {
    +
      880.      2                return [
    +
      881.      2                    "let [\n    aa, bb = 0\n] = 0;\naa();\nbb();",
    +
      882.      2                    "let aa = 0;\nlet [...bb] = [...aa];\nbb();",
    +
      883.      2
    +
      884.      2// PR-459 - Allow destructuring-assignment after function-definition.
    +
      885.      2
    +
      886.      2                    (
    +
      887.      2                        "let aa;\n"
    +
      888.      2                        + "let bb;\n"
    +
      889.      2                        + "function cc() {\n"
    +
      890.      2                        + "    return;\n"
    +
      891.      2                        + "}\n"
    +
      892.      2                        + "[aa, bb] = cc();\n"
    +
      893.      2                    ),
    +
      894.      2                    "let constructor = 0;\nconstructor();",
    +
      895.      2                    "let {\n    aa: bb\n} = 0;\nbb();",
    +
      896.      2                    "let {\n    aa: bb,\n    bb: cc\n} = 0;\nbb();\ncc();",
    +
      897.      2                    "let {aa, bb} = 0;\naa();\nbb();",
    +
      898.      2                    "let {constructor} = 0;\nconstructor();"
    +
      899.     16                ].map(function (code) {
    +
      900.     16                    return directive + code;
    +
      901.     16                });
    +
      902.      2            }).flat()
    +
      903.     23        }).forEach(function (codeList) {
    +
      904.     23            let elemPrv = "";
    +
      905.     68            codeList.forEach(function (code) {
    +
      906.     68                let warnings;
    +
      907.     68                // Assert codeList is sorted.
    +
      908.     68                assertOrThrow(elemPrv < code, JSON.stringify([
    +
      909.     68                    elemPrv, code
    +
      910.     68                ], undefined, 4));
    +
      911.     68                elemPrv = code;
    +
      912.     68                [
    +
      913.     68                    jslint.jslint,
    +
      914.     68                    jslintCjs.jslint
    +
      915.    136                ].forEach(function (jslint) {
    +
      916.    136                    warnings = jslint(code, {
    +
      917.    136                        beta: true
    +
      918.    136                    }).warnings;
    +
      919.    136                    assertOrThrow(
    +
      920.    136                        warnings.length === 0,
    +
      921.    136                        JSON.stringify([code, warnings])
    +
      922.    136                    );
    +
      923.    136                });
    +
      924.     68            });
    +
      925.     23        });
    +
      926.      1    });
    +
      927.      1});
    +
      928.      1
    +
      929.      1jstestDescribe((
    +
      930.      1    "test jslint's option handling-behavior"
    +
      931.      1), function testBehaviorJslintOption() {
    +
      932.      1    let elemPrv = "";
    +
      933.      1    [
    +
      934.      1        [
    +
      935.      1            "let aa = aa | 0;", {bitwise: true}, []
    +
      936.      1        ], [
    +
      937.      1            ";\naa(new XMLHttpRequest());", {browser: true}, ["aa"]
    +
      938.      1        ], [
    +
      939.      1            "let aa = \"aa\" + 0;", {convert: true}, []
    +
      940.      1        ], [
    +
      941.      1            "registerType();", {couch: true}, []
    +
      942.      1        ], [
    +
      943.      1            "debugger;", {devel: true}, []
    +
      944.      1        ], [
    +
      945.      1
    +
      946.      1// PR-404 - Alias "evil" to jslint-directive "eval" for backwards-compat.
    +
      947.      1
    +
      948.      1            "new Function();\neval();", {eval: true, evil: true}, []
    +
      949.      1        ], [
    +
      950.      1            "let aa = () => 0;", {fart: true}, []
    +
      951.      1        ], [
    +
      952.      1            (
    +
      953.      1                "let aa = async (bb, {cc, dd}, [ee, ff], ...gg) => {\n"
    +
      954.      1                + "    bb += 1;\n"
    +
      955.      1                + "    return await (bb + cc + dd + ee + ff + gg);\n"
    +
      956.      1                + "};\n"
    +
      957.      1            ), {fart: true}, []
    +
      958.      1        ], [
    +
      959.      1            (
    +
      960.      1                "function aa(aa) {\n"
    +
      961.      1                + "    for (aa = 0; aa < 0; aa += 1) {\n"
    +
      962.      1                + "        aa();\n"
    +
      963.      1                + "    }\n"
    +
      964.      1                + "}\n"
    +
      965.      1            ), {for: true}, []
    +
      966.      1        ], [
    +
      967.      1            "let aa = {get aa() {\n    return;\n}};", {getset: true}, []
    +
      968.      1        ], [
    +
      969.      1            "let aa = {set aa(aa) {\n    return aa;\n}};", {getset: true}, []
    +
      970.      1        ], [
    +
      971.      1            sourceJslintMjs.replace((
    +
      972.      1                /    /g
    +
      973.      1            ), "  "), {indent2: true}, []
    +
      974.      1        ], [
    +
      975.      1            "function aa() {\n  return;\n}", {indent2: true}, []
    +
      976.      1        ], [
    +
      977.      1            "/".repeat(100), {long: true}, []
    +
      978.      1        ], [
    +
      979.      1
    +
      980.      1// PR-404 - Alias "nomen" to jslint-directive "name" for backwards-compat.
    +
      981.      1
    +
      982.      1            "let aa = aa._;", {name: true, nomen: true}, []
    +
      983.      1        ], [
    +
      984.      1            "require();", {node: true}, []
    +
      985.      1        ], [
    +
      986.      1            "let aa = 'aa';", {single: true}, []
    +
      987.      1        ], [
    +
      988.      1
    +
      989.      1// PR-404 - Add new directive "subscript" to play nice with Google Closure.
    +
      990.      1
    +
      991.      1            "aa[\"aa\"] = 1;", {subscript: true}, ["aa"]
    +
      992.      1        ], [
    +
      993.      1            "", {test_internal_error: true}, []
    +
      994.      1        ], [
    +
      995.      1            "let aa = this;", {this: true}, []
    +
      996.      1        ], [
    +
      997.      1            "", {trace: true}, []
    +
      998.      1        ], [
    +
      999.      1            (
    +
     1000.      1                "function aa({bb, aa}) {\n"
    +
     1001.      1                + "    switch (aa) {\n"
    +
     1002.      1                + "    case 1:\n"
    +
     1003.      1                + "        break;\n"
    +
     1004.      1                + "    case 0:\n"
    +
     1005.      1                + "        break;\n"
    +
     1006.      1                + "    default:\n"
    +
     1007.      1                + "        return {bb, aa};\n"
    +
     1008.      1                + "    }\n"
    +
     1009.      1                + "}\n"
    +
     1010.      1            ), {unordered: true}, []
    +
     1011.      1        ], [
    +
     1012.      1            "let {bb, aa} = 0;", {unordered: true}, []
    +
     1013.      1        ], [
    +
     1014.      1            (
    +
     1015.      1                "function aa() {\n"
    +
     1016.      1                + "    if (aa) {\n"
    +
     1017.      1                + "        let bb = 0;\n"
    +
     1018.      1                + "        return bb;\n"
    +
     1019.      1                + "    }\n"
    +
     1020.      1                + "}\n"
    +
     1021.      1            ), {variable: true}, []
    +
     1022.      1        ], [
    +
     1023.      1            "let bb = 0;\nlet aa = 0;", {variable: true}, []
    +
     1024.      1        ], [
    +
     1025.      1            "\t", {white: true}, []
    +
     1026.      1        ]
    +
     1027.     26    ].forEach(function ([
    +
     1028.     26        source, option_dict, global_list
    +
     1029.     26    ]) {
    +
     1030.     26        jstestIt((
    +
     1031.     26            `test option=${JSON.stringify(option_dict)} handling-behavior`
    +
     1032.     26        ), function () {
    +
     1033.     26            let elemNow = JSON.stringify([
    +
     1034.     26                option_dict, source, global_list
    +
     1035.     26            ]);
    +
     1036.     26            let warningsLength = (
    +
     1037.     26                option_dict.test_internal_error
    +
     1038.      1                ? 1
    +
     1039.     25                : 0
    +
     1040.     26            );
    +
     1041.     26            // Assert list is sorted.
    +
     1042.     26            assertOrThrow(elemPrv < elemNow, JSON.stringify([
    +
     1043.     26                elemPrv, elemNow
    +
     1044.     26            ], undefined, 4));
    +
     1045.     26            elemPrv = elemNow;
    +
     1046.     26            option_dict.beta = true;
    +
     1047.     26            [
    +
     1048.     26                jslint.jslint,
    +
     1049.     26                jslintCjs.jslint
    +
     1050.     52            ].forEach(function (jslint) {
    +
     1051.     52                // test jslint's option handling-behavior
    +
     1052.     52                assertOrThrow(
    +
     1053.     52                    jslint(
    +
     1054.     52                        source,
    +
     1055.     52                        option_dict,
    +
     1056.     52                        global_list
    +
     1057.     52                    ).warnings.length === warningsLength,
    +
     1058.     52                    "jslint.jslint(" + JSON.stringify([
    +
     1059.     52                        source, option_dict, global_list
    +
     1060.     52                    ]) + ")"
    +
     1061.     52                );
    +
     1062.     52                // test jslint's directive handling-behavior
    +
     1063.     52                source = (
    +
     1064.     52                    "/*jslint " + JSON.stringify(
    +
     1065.     52                        option_dict
    +
     1066.     52                    ).slice(1, -1).replace((
    +
     1067.     52                        /"/g
    +
     1068.     52                    ), "") + "*/\n"
    +
     1069.     52                    + (
    +
     1070.     52                        global_list.length === 0
    +
     1071.     48                        ? ""
    +
     1072.      4                        : "/*global " + global_list.join(",") + "*/\n"
    +
     1073.     52                    )
    +
     1074.     52                    + source.replace((
    +
     1075.     52                        /^#!/
    +
     1076.     52                    ), "//")
    +
     1077.     52                );
    +
     1078.     52                assertOrThrow(
    +
     1079.     52                    jslint(source).warnings.length === warningsLength,
    +
     1080.     52                    source
    +
     1081.     52                );
    +
     1082.     52            });
    +
     1083.     26        });
    +
     1084.     26    });
    +
     1085.      1});
    +
     1086.      1
    +
     1087.      1jstestDescribe((
    +
     1088.      1    "test jslint's warnings handling-behavior"
    +
     1089.      1), function testBehaviorJslintWarnings() {
    +
     1090.      1    jstestIt((
    +
     1091.      1        "test jslint's warning handling-behavior"
    +
     1092.      1    ), function () {
    +
     1093.      1
    +
     1094.      1// this function will validate each jslint <warning> is raised with given
    +
     1095.      1// malformed <code>
    +
     1096.      1
    +
     1097.      1        sourceJslintMjs.replace((
    +
     1098.      1            /(\n\s*?\/\/\s*?test_cause:\s*?)(\S[\S\s]*?\S)(\n\n\s*?) *?\S/g
    +
     1099.    328        ), function (match0, header, causeList, footer) {
    +
     1100.    328            let tmp;
    +
     1101.    328            // console.error(match0);
    +
     1102.    328            // Validate header.
    +
     1103.    328            assertOrThrow(header === "\n\n// test_cause:\n", match0);
    +
     1104.    328            // Validate footer.
    +
     1105.    328            assertOrThrow(footer === "\n\n", match0);
    +
     1106.    328            // Validate causeList.
    +
     1107.    328            causeList = causeList.replace((
    +
     1108.    328                /^\/\/ /gm
    +
     1109.    328            ), "").replace((
    +
     1110.    328                /^\["\n([\S\s]*?)\n"(,.*?)$/gm
    +
     1111.     43            ), function (ignore, source, param) {
    +
     1112.     43                source = "[" + JSON.stringify(source) + param;
    +
     1113.     43                assertOrThrow(source.length > (80 - 3), source);
    +
     1114.     43                return source;
    +
     1115.     43            }).replace((
    +
     1116.    328                / \/\/jslint-ignore-line$/gm
    +
     1117.    328            ), "");
    +
     1118.    505            tmp = causeList.split("\n").map(function (cause) {
    +
     1119.    505                return (
    +
     1120.    505                    "["
    +
     1121.   2525                    + JSON.parse(cause).map(function (elem) {
    +
     1122.   2525                        return JSON.stringify(elem);
    +
     1123.   2525                    }).join(", ")
    +
     1124.    505                    + "]"
    +
     1125.    505                );
    +
     1126.    505            }).sort().join("\n");
    +
     1127.    328            assertOrThrow(
    +
     1128.    328                causeList === tmp,
    +
     1129.    328                "\n" + causeList + "\n\n" + tmp
    +
     1130.    328            );
    +
     1131.    505            causeList.split("\n").forEach(function (cause) {
    +
     1132.    505                cause = JSON.parse(cause);
    +
     1133.    505                tmp = jslint.jslint(cause[0], {
    +
     1134.    505                    beta: true,
    +
     1135.    505                    test_cause: true
    +
     1136.    505                }).causes;
    +
     1137.    505                // Validate cause.
    +
     1138.    505                assertOrThrow(
    +
     1139.    505                    tmp[JSON.stringify(cause.slice(1))],
    +
     1140.    505                    (
    +
     1141.    505                        "\n" + JSON.stringify(cause) + "\n\n"
    +
     1142.    505                        + Object.keys(tmp).sort().join("\n")
    +
     1143.    505                    )
    +
     1144.    505                );
    +
     1145.    505            });
    +
     1146.    328            return "";
    +
     1147.    328        });
    +
     1148.      1    });
    +
     1149.      1});
    +
     1150.      1
    +
     1151.      1jstestDescribe((
    +
     1152.      1    "test jstestXxx handling-behavior"
    +
     1153.      1), function testBehaviorJstestXxx() {
    +
     1154.      1    jstestIt((
    +
     1155.      1        "test jstestDescribe error handling-behavior"
    +
     1156.      1    ), function () {
    +
     1157.      1        throw new Error();
    +
     1158.      1    }, "pass");
    +
     1159.      1    jstestIt((
    +
     1160.      1        "test jstestOnExit tests-failed handling-behavior"
    +
     1161.      1    ), function () {
    +
     1162.      1        jstestOnExit(undefined, "testsFailed");
    +
     1163.      1    });
    +
     1164.      1});
    +
     1165.      1
    +
     1166.      1jstestDescribe((
    +
     1167.      1    "test misc handling-behavior"
    +
     1168.      1), function testBehaviorMisc() {
    +
     1169.      1    jstestIt((
    +
     1170.      1        "test misc handling-behavior"
    +
     1171.      1    ), async function () {
    +
     1172.      1        // test debugInline handling-behavior
    +
     1173.      1        noop(debugInline);
    +
     1174.      1        // test assertErrorThrownAsync error handling-behavior
    +
     1175.      1        await assertErrorThrownAsync(function () {
    +
     1176.      1            return assertErrorThrownAsync(noop);
    +
     1177.      1        });
    +
     1178.      1        // test assertJsonEqual error handling-behavior
    +
     1179.      1        await assertErrorThrownAsync(function () {
    +
     1180.      1            assertJsonEqual(1, 2);
    +
     1181.      1        });
    +
     1182.      1        await assertErrorThrownAsync(function () {
    +
     1183.      1            assertJsonEqual(1, 2, "undefined");
    +
     1184.      1        });
    +
     1185.      1        await assertErrorThrownAsync(function () {
    +
     1186.      1            assertJsonEqual(1, 2, {});
    +
     1187.      1        });
    +
     1188.      1        // test assertOrThrow error handling-behavior
    +
     1189.      1        await assertErrorThrownAsync(function () {
    +
     1190.      1            assertOrThrow(undefined, "undefined");
    +
     1191.      1        });
    +
     1192.      1        await assertErrorThrownAsync(function () {
    +
     1193.      1            assertOrThrow(undefined, new Error());
    +
     1194.      1        });
    +
     1195.      1    });
    +
     1196.      1});
    +
     1197.      1
    +
     1198.      1jstestDescribe((
    +
     1199.      1    "test v8CoverageListMerge handling-behavior"
    +
     1200.      1), function testBehaviorV8CoverageListMerge() {
    +
     1201.      1    let functionsInput = JSON.stringify([
    +
     1202.      1        {
    +
     1203.      1            functionName: "test",
    +
     1204.      1            isBlockCoverage: true,
    +
     1205.      1            ranges: [
    +
     1206.      1                {
    +
     1207.      1                    count: 2,
    +
     1208.      1                    endOffset: 4,
    +
     1209.      1                    startOffset: 0
    +
     1210.      1                },
    +
     1211.      1                {
    +
     1212.      1                    count: 1,
    +
     1213.      1                    endOffset: 2,
    +
     1214.      1                    startOffset: 1
    +
     1215.      1                },
    +
     1216.      1                {
    +
     1217.      1                    count: 1,
    +
     1218.      1                    endOffset: 3,
    +
     1219.      1                    startOffset: 2
    +
     1220.      1                }
    +
     1221.      1            ]
    +
     1222.      1        }
    +
     1223.      1    ]);
    +
     1224.      1    jstestIt((
    +
     1225.      1        "accepts empty arrays for `v8CoverageListMerge`"
    +
     1226.      1    ), function () {
    +
     1227.      1        assertJsonEqual(v8CoverageListMerge([]), {
    +
     1228.      1            result: []
    +
     1229.      1        });
    +
     1230.      1    });
    +
     1231.      1    jstestIt((
    +
     1232.      1        "funcCovs.length === 1"
    +
     1233.      1    ), function () {
    +
     1234.      1        assertJsonEqual(v8CoverageListMerge([
    +
     1235.      1            {
    +
     1236.      1                result: [
    +
     1237.      1                    {
    +
     1238.      1                        functions: [
    +
     1239.      1                            {
    +
     1240.      1                                functionName: "test",
    +
     1241.      1                                isBlockCoverage: true,
    +
     1242.      1                                ranges: [
    +
     1243.      1                                    {
    +
     1244.      1                                        count: 2,
    +
     1245.      1                                        endOffset: 4,
    +
     1246.      1                                        startOffset: 0
    +
     1247.      1                                    }
    +
     1248.      1                                ]
    +
     1249.      1                            }
    +
     1250.      1                        ],
    +
     1251.      1                        moduleUrl: "/lib.js",
    +
     1252.      1                        scriptId: "1"
    +
     1253.      1                    }
    +
     1254.      1                ]
    +
     1255.      1            },
    +
     1256.      1            {
    +
     1257.      1                result: [
    +
     1258.      1                    {
    +
     1259.      1                        functions: [],
    +
     1260.      1                        moduleUrl: "/lib.js",
    +
     1261.      1                        scriptId: "2"
    +
     1262.      1                    }
    +
     1263.      1                ]
    +
     1264.      1            }
    +
     1265.      1        ]), {
    +
     1266.      1            result: [
    +
     1267.      1                {
    +
     1268.      1                    functions: [
    +
     1269.      1                        {
    +
     1270.      1                            functionName: "test",
    +
     1271.      1                            isBlockCoverage: true,
    +
     1272.      1                            ranges: [
    +
     1273.      1                                {
    +
     1274.      1                                    count: 2,
    +
     1275.      1                                    endOffset: 4,
    +
     1276.      1                                    startOffset: 0
    +
     1277.      1                                }
    +
     1278.      1                            ]
    +
     1279.      1                        }
    +
     1280.      1                    ],
    +
     1281.      1                    scriptId: "0"
    +
     1282.      1                }
    +
     1283.      1            ]
    +
     1284.      1        });
    +
     1285.      1    });
    +
     1286.      1    jstestIt((
    +
     1287.      1        "accepts arrays with a single item for `v8CoverageListMerge`"
    +
     1288.      1    ), function () {
    +
     1289.      1        assertJsonEqual(v8CoverageListMerge([
    +
     1290.      1            {
    +
     1291.      1                result: [
    +
     1292.      1                    {
    +
     1293.      1                        functions: JSON.parse(functionsInput),
    +
     1294.      1                        moduleUrl: "/lib.js",
    +
     1295.      1                        scriptId: "123"
    +
     1296.      1                    }
    +
     1297.      1                ]
    +
     1298.      1            }
    +
     1299.      1        ]), {
    +
     1300.      1            result: [
    +
     1301.      1                {
    +
     1302.      1                    functions: [
    +
     1303.      1                        {
    +
     1304.      1                            functionName: "test",
    +
     1305.      1                            isBlockCoverage: true,
    +
     1306.      1                            ranges: [
    +
     1307.      1                                {
    +
     1308.      1                                    count: 2,
    +
     1309.      1                                    endOffset: 4,
    +
     1310.      1                                    startOffset: 0
    +
     1311.      1                                },
    +
     1312.      1                                {
    +
     1313.      1                                    count: 1,
    +
     1314.      1                                    endOffset: 3,
    +
     1315.      1                                    startOffset: 1
    +
     1316.      1                                }
    +
     1317.      1                            ]
    +
     1318.      1                        }
    +
     1319.      1                    ],
    +
     1320.      1                    moduleUrl: "/lib.js",
    +
     1321.      1                    scriptId: "0"
    +
     1322.      1                }
    +
     1323.      1            ]
    +
     1324.      1        });
    +
     1325.      1    });
    +
     1326.      1    jstestIt((
    +
     1327.      1        "accepts arrays with two identical items for"
    +
     1328.      1        + " `v8CoverageListMerge`"
    +
     1329.      1    ), function () {
    +
     1330.      1        assertJsonEqual(v8CoverageListMerge([
    +
     1331.      1            {
    +
     1332.      1                result: [
    +
     1333.      1                    {
    +
     1334.      1                        functions: JSON.parse(functionsInput),
    +
     1335.      1                        scriptId: "123",
    +
     1336.      1                        url: "/lib.js"
    +
     1337.      1                    }, {
    +
     1338.      1                        functions: JSON.parse(functionsInput),
    +
     1339.      1                        scriptId: "123",
    +
     1340.      1                        url: "/lib.js"
    +
     1341.      1                    }
    +
     1342.      1                ]
    +
     1343.      1            }
    +
     1344.      1        ]), {
    +
     1345.      1            result: [
    +
     1346.      1                {
    +
     1347.      1                    functions: [
    +
     1348.      1                        {
    +
     1349.      1                            functionName: "test",
    +
     1350.      1                            isBlockCoverage: true,
    +
     1351.      1                            ranges: [
    +
     1352.      1                                {
    +
     1353.      1                                    count: 4,
    +
     1354.      1                                    endOffset: 4,
    +
     1355.      1                                    startOffset: 0
    +
     1356.      1                                },
    +
     1357.      1                                {
    +
     1358.      1                                    count: 2,
    +
     1359.      1                                    endOffset: 3,
    +
     1360.      1                                    startOffset: 1
    +
     1361.      1                                }
    +
     1362.      1                            ]
    +
     1363.      1                        }
    +
     1364.      1                    ],
    +
     1365.      1                    scriptId: "0",
    +
     1366.      1                    url: "/lib.js"
    +
     1367.      1                }
    +
     1368.      1            ]
    +
     1369.      1        });
    +
     1370.      1    });
    +
     1371.      1    [
    +
     1372.      1        "test_coverage_merge_is_block_coverage_test.json",
    +
     1373.      1        "test_coverage_merge_issue_2_mixed_is_block_coverage_test.json",
    +
     1374.      1        "test_coverage_merge_node_10_internal_errors_one_of_test.json",
    +
     1375.      1        "test_coverage_merge_reduced_test.json",
    +
     1376.      1        "test_coverage_merge_simple_test.json",
    +
     1377.      1        "test_coverage_merge_various_test.json"
    +
     1378.      6    ].forEach(function (file) {
    +
     1379.      6        jstestIt(file, function () {
    +
     1380.      6            file = testCoverageMergeData[file];
    +
     1381.     84            file.forEach(function ({
    +
     1382.     84                expected,
    +
     1383.     84                inputs
    +
     1384.     84            }) {
    +
     1385.     84                assertJsonEqual(v8CoverageListMerge(inputs), expected);
    +
     1386.     84            });
    +
     1387.      6        });
    +
     1388.      6    });
    +
     1389.      1    jstestIt((
    +
     1390.      1        "merge multiple node-sqlite coverage files"
    +
     1391.      1    ), function () {
    +
     1392.      1        let data1 = [
    +
     1393.      1            "test_v8_coverage_node_sqlite_9884_1633662346346_0.json",
    +
     1394.      1            "test_v8_coverage_node_sqlite_13216_1633662333140_0.json"
    +
     1395.      2        ].map(function (file) {
    +
     1396.      2            return testCoverageMergeData[file];
    +
     1397.      2        });
    +
     1398.      1        let data2 = testCoverageMergeData[
    +
     1399.      1            "test_v8_coverage_node_sqlite_merged.json"
    +
     1400.      1        ];
    +
     1401.      1        data1 = v8CoverageListMerge(data1);
    +
     1402.      1        data1 = v8CoverageListMerge([data1]);
    +
     1403.      1
    +
     1404.      1// Debug data1.
    +
     1405.      1// await moduleFs.promises.writeFile(
    +
     1406.      1//     ".test_v8_coverage_node_sqlite_merged.json",
    +
     1407.      1//     JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n"
    +
     1408.      1// );
    +
     1409.      1
    +
     1410.      1        assertJsonEqual(data1, data2);
    +
     1411.      1    });
    +
     1412.      1});
    +
     1413.      1
    +
     1414.      1jstestDescribe((
    +
     1415.      1    "test v8CoverageReportCreate handling-behavior"
    +
     1416.      1), function testBehaviorV8CoverageReportCreate() {
    +
     1417.      1    jstestIt((
    +
     1418.      1        "test null-case handling-behavior"
    +
     1419.      1    ), async function () {
    +
     1420.      1        await assertErrorThrownAsync(function () {
    +
     1421.      1            return v8CoverageReportCreate({});
    +
     1422.      1        }, "invalid coverageDir");
    +
     1423.      1    });
    +
     1424.      1    jstestIt((
    +
     1425.      1        "test coverage-report jslint.mjs handling-behavior"
    +
     1426.      1    ), async function () {
    +
     1427.      1        // test remove-old-coverage handling-behavior
    +
     1428.      1        await fsWriteFileWithParents(
    +
     1429.      1            ".tmp/coverage_jslint/coverage-0-0-0.json",
    +
     1430.      1            ""
    +
     1431.      1        );
    +
     1432.      1        await jslint.jslint_cli({
    +
     1433.      1            console_error: noop, // comment to debug
    +
     1434.      1            mode_cli: true,
    +
     1435.      1            process_argv: [
    +
     1436.      1                "node", "jslint.mjs",
    +
     1437.      1                "v8_coverage_report=.tmp/coverage_jslint",
    +
     1438.      1                "--exclude=aa.js",
    +
     1439.      1                "--include-node-modules=1",
    +
     1440.      1                "--include=jslint.mjs",
    +
     1441.      1                "node", "jslint.mjs"
    +
     1442.      1            ]
    +
     1443.      1        });
    +
     1444.      1    });
    +
     1445.      1    [
    +
     1446.      1        [
    +
     1447.      1            "v8CoverageReportCreate_high.js", (
    +
     1448.      1                "switch(0){\n"
    +
     1449.      1                + "case 0:break;\n"
    +
     1450.      1                + "}\n"
    +
     1451.      1            )
    +
     1452.      1        ], [
    +
     1453.      1            "v8CoverageReportCreate_ignore.js", (
    +
     1454.      1                "/*coverage-ignore-file*/\n"
    +
     1455.      1                + "switch(0){\n"
    +
     1456.      1                + "case 0:break;\n"
    +
     1457.      1                + "}\n"
    +
     1458.      1            )
    +
     1459.      1        ], [
    +
     1460.      1            "v8CoverageReportCreate_low.js", (
    +
     1461.      1                "switch(0){\n"
    +
     1462.      1                + "case 1:break;\n"
    +
     1463.      1                + "case 2:break;\n"
    +
     1464.      1                + "case 3:break;\n"
    +
     1465.      1                + "case 4:break;\n"
    +
     1466.      1                + "}\n"
    +
     1467.      1            )
    +
     1468.      1        ], [
    +
     1469.      1            "v8CoverageReportCreate_medium.js", (
    +
     1470.      1                "switch(0){\n"
    +
     1471.      1                + "case 0:break;\n"
    +
     1472.      1                + "case 1:break;\n"
    +
     1473.      1                + "case 2:break;\n"
    +
     1474.      1                + "}\n"
    +
     1475.      1            )
    +
     1476.      1        ]
    +
     1477.      4    ].forEach(function ([
    +
     1478.      4        file, data
    +
     1479.      4    ], ii) {
    +
     1480.      4        jstestIt(file, async function () {
    +
     1481.      4            let dir = ".tmp/coverage_" + ii + "/";
    +
     1482.      4            file = dir + file;
    +
     1483.      4            await fsWriteFileWithParents(file, data);
    +
     1484.      4            await jslint.jslint_cli({
    +
     1485.      4                console_error: noop, // comment to debug
    +
     1486.      4                mode_cli: true,
    +
     1487.      4                process_argv: [
    +
     1488.      4                    "node", "jslint.mjs",
    +
     1489.      4                    "v8_coverage_report=" + dir,
    +
     1490.      4                    "node",
    +
     1491.      4                    file
    +
     1492.      4                ]
    +
     1493.      4            });
    +
     1494.      4        });
    +
     1495.      4    });
    +
     1496.      1    jstestIt((
    +
     1497.      1        "test npm handling-behavior"
    +
     1498.      1    ), async function () {
    +
     1499.      1        await jslint.jslint_cli({
    +
     1500.      1            console_error: noop, // comment to debug
    +
     1501.      1            mode_cli: true,
    +
     1502.      1            process_argv: [
    +
     1503.      1                "node", "jslint.mjs",
    +
     1504.      1                "v8_coverage_report=.tmp/coverage_npm",
    +
     1505.      1                "npm", "--version"
    +
     1506.      1            ]
    +
     1507.      1        });
    +
     1508.      1    });
    +
     1509.      1    jstestIt((
    +
     1510.      1        "test misc handling-behavior"
    +
     1511.      1    ), async function () {
    +
     1512.      1        await Promise.all([
    +
     1513.      1            [
    +
     1514.      1                ".tmp/coverage_misc/aa.js", "\n".repeat(0x100)
    +
     1515.      1            ], [
    +
     1516.      1                ".tmp/coverage_misc/coverage-0-0-0.json", JSON.stringify({
    +
     1517.      1                    "result": [
    +
     1518.      1                        {
    +
     1519.      1                            "functions": [
    +
     1520.      1                                {
    +
     1521.      1                                    "functionName": "",
    +
     1522.      1                                    "isBlockCoverage": true,
    +
     1523.      1                                    "ranges": [
    +
     1524.      1                                        {
    +
     1525.      1                                            "count": 1,
    +
     1526.      1                                            "endOffset": 0xf0,
    +
     1527.      1                                            "startOffset": 0x10
    +
     1528.      1                                        },
    +
     1529.      1                                        {
    +
     1530.      1                                            "count": 1,
    +
     1531.      1                                            "endOffset": 0x40,
    +
     1532.      1                                            "startOffset": 0x20
    +
     1533.      1                                        },
    +
     1534.      1                                        {
    +
     1535.      1                                            "count": 1,
    +
     1536.      1                                            "endOffset": 0x80,
    +
     1537.      1                                            "startOffset": 0x60
    +
     1538.      1                                        },
    +
     1539.      1                                        {
    +
     1540.      1                                            "count": 0,
    +
     1541.      1                                            "endOffset": 0x45,
    +
     1542.      1                                            "startOffset": 0x25
    +
     1543.      1                                        },
    +
     1544.      1                                        {
    +
     1545.      1                                            "count": 0,
    +
     1546.      1                                            "endOffset": 0x85,
    +
     1547.      1                                            "startOffset": 0x65
    +
     1548.      1                                        }
    +
     1549.      1                                    ]
    +
     1550.      1                                }
    +
     1551.      1                            ],
    +
     1552.      1                            "scriptId": "0",
    +
     1553.      1                            "url": "file:///" + modulePath.resolve(
    +
     1554.      1                                ".tmp/coverage_misc/aa.js"
    +
     1555.      1                            )
    +
     1556.      1                        }
    +
     1557.      1                    ]
    +
     1558.      1                }, undefined, 4)
    +
     1559.      1            ]
    +
     1560.      2        ].map(async function ([
    +
     1561.      2            file, data
    +
     1562.      2        ]) {
    +
     1563.      2            await fsWriteFileWithParents(file, data);
    +
     1564.      2        }));
    +
     1565.      1        await jslint.jslint_cli({
    +
     1566.      1            console_error: noop, // comment to debug
    +
     1567.      1            mode_cli: true,
    +
     1568.      1            process_argv: [
    +
     1569.      1                "node", "jslint.mjs",
    +
     1570.      1                "v8_coverage_report=.tmp/coverage_misc"
    +
     1571.      1                // "node", ".tmp/coverage_misc/aa.js"
    +
     1572.      1            ]
    +
     1573.      1        });
    +
     1574.      1    });
    +
     1575.      1});
    +
     1576.      1
    +
    + + + + diff --git a/branch-master/.artifact/coverage/touch.txt b/branch-master/.artifact/coverage/touch.txt new file mode 100644 index 000000000..e69de29bb diff --git a/branch-master/.artifact/coverage_sqlite3_js/coverage_badge.svg b/branch-master/.artifact/coverage_sqlite3_js/coverage_badge.svg new file mode 100644 index 000000000..8e28d5ff5 --- /dev/null +++ b/branch-master/.artifact/coverage_sqlite3_js/coverage_badge.svg @@ -0,0 +1,14 @@ + + + + +coverage +97.62 % + + diff --git a/branch-master/.artifact/coverage_sqlite3_js/coverage_report.txt b/branch-master/.artifact/coverage_sqlite3_js/coverage_report.txt new file mode 100644 index 000000000..b19e8684a --- /dev/null +++ b/branch-master/.artifact/coverage_sqlite3_js/coverage_report.txt @@ -0,0 +1,16 @@ +V8 Coverage Report ++----------------------------------+-------------------+-------------------+ +| Files covered | Lines | Remaining | ++----------------------------------+-------------------+-------------------+ +| ./ | 97.62 % | | +| *******************************_ | 247 / 253 | 6 / 253 | ++----------------------------------+-------------------+-------------------+ +| ./lib/sqlite3-binding.js | 100.00 % | | +| ******************************** | 6 / 6 | 0 / 6 | ++----------------------------------+-------------------+-------------------+ +| ./lib/sqlite3.js | 97.11 % | | +| *******************************_ | 202 / 208 | 6 / 208 | ++----------------------------------+-------------------+-------------------+ +| ./lib/trace.js | 100.00 % | | +| ******************************** | 39 / 39 | 0 / 39 | ++----------------------------------+-------------------+-------------------+ diff --git a/branch-master/.artifact/coverage_sqlite3_js/index.html b/branch-master/.artifact/coverage_sqlite3_js/index.html new file mode 100644 index 000000000..83d95a7bd --- /dev/null +++ b/branch-master/.artifact/coverage_sqlite3_js/index.html @@ -0,0 +1,212 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . /
    +
    +
    +
    +
    + 97.62 %
    + 247 / 253 +
    +
    + 6 / 253 +
    + . / lib/sqlite3-binding.js
    +
    +
    +
    +
    + 100.00 %
    + 6 / 6 +
    +
    + 0 / 6 +
    + . / lib/sqlite3.js
    +
    +
    +
    +
    + 97.11 %
    + 202 / 208 +
    +
    + 6 / 208 +
    + . / lib/trace.js
    +
    +
    +
    +
    + 100.00 %
    + 39 / 39 +
    +
    + 0 / 39 +
    +
    + + + + diff --git a/branch-master/.artifact/coverage_sqlite3_js/lib/sqlite3-binding.js.html b/branch-master/.artifact/coverage_sqlite3_js/lib/sqlite3-binding.js.html new file mode 100644 index 000000000..d3fec08a6 --- /dev/null +++ b/branch-master/.artifact/coverage_sqlite3_js/lib/sqlite3-binding.js.html @@ -0,0 +1,174 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / lib/sqlite3-binding.js
    +
    +
    +
    +
    + 100.00 %
    + 6 / 6 +
    +
    + 0 / 6 +
    +
    + + +
    +
        1.      2const binary = require('@mapbox/node-pre-gyp');
    +
        2.      2const path = require('path');
    +
        3.      2const binding_path = binary.find(path.resolve(path.join(__dirname,'../package.json')));
    +
        4.      2const binding = require(binding_path);
    +
        5.      2module.exports = exports = binding;
    +
        6.      2
    +
    + + + + diff --git a/branch-master/.artifact/coverage_sqlite3_js/lib/sqlite3.js.html b/branch-master/.artifact/coverage_sqlite3_js/lib/sqlite3.js.html new file mode 100644 index 000000000..9f8ff0b52 --- /dev/null +++ b/branch-master/.artifact/coverage_sqlite3_js/lib/sqlite3.js.html @@ -0,0 +1,376 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / lib/sqlite3.js
    +
    +
    +
    +
    + 97.11 %
    + 202 / 208 +
    +
    + 6 / 208 +
    +
    + + +
    +
        1.      2const path = require('path');
    +
        2.      2const sqlite3 = require('./sqlite3-binding.js');
    +
        3.      2const EventEmitter = require('events').EventEmitter;
    +
        4.      2module.exports = exports = sqlite3;
    +
        5.      2
    +
        6.     12function normalizeMethod (fn) {
    +
        7.   3483    return function (sql) {
    +
        8.   3483        let errBack;
    +
        9.   3483        const args = Array.prototype.slice.call(arguments, 1);
    +
       10.   3483
    +
       11.   1119        if (typeof args[args.length - 1] === 'function') {
    +
       12.   1119            const callback = args[args.length - 1];
    +
       13.   1119            errBack = function(err) {
    +
       14.   1119                if (err) {
    +
       15.   1119                    callback(err);
    +
       16.   1119                }
    +
       17.   1119            };
    +
       18.   1119        }
    +
       19.   3483        const statement = new Statement(this, sql, errBack);
    +
       20.   3483        return fn.call(this, statement, args);
    +
       21.   3483    };
    +
       22.     12}
    +
       23.      2
    +
       24.      6function inherits(target, source) {
    +
       25.      6    for (const k in source.prototype)
    +
       26.    108        target.prototype[k] = source.prototype[k];
    +
       27.      6}
    +
       28.      2
    +
       29.      2sqlite3.cached = {
    +
       30.      4    Database: function(file, a, b) {
    +
       31.     -0        if (file === '' || file === ':memory:') {
    +
       32.     -0            // Don't cache special databases.
    +
       33.     -0            return new Database(file, a, b);
    +
       34.     -0        }
    +
       35.      4
    +
       36.      4        let db;
    +
       37.      4        file = path.resolve(file);
    +
       38.      4
    +
       39.      2        if (!sqlite3.cached.objects[file]) {
    +
       40.      2            db = sqlite3.cached.objects[file] = new Database(file, a, b);
    +
       41.      2        }
    +
       42.      2        else {
    +
       43.      2            // Make sure the callback is called.
    +
       44.      2            db = sqlite3.cached.objects[file];
    +
       45.     -0            const callback = (typeof a === 'number') ? b : a;
    +
       46.      2            if (typeof callback === 'function') {
    +
       47.      2                function cb() { callback.call(db, null); }
    +
       48.      2                if (db.open) process.nextTick(cb);
    +
       49.      2                else db.once('open', cb);
    +
       50.      2            }
    +
       51.      2        }
    +
       52.      4
    +
       53.      4        return db;
    +
       54.      4    },
    +
       55.      2    objects: {}
    +
       56.      2};
    +
       57.      2
    +
       58.      2
    +
       59.      2const Database = sqlite3.Database;
    +
       60.      2const Statement = sqlite3.Statement;
    +
       61.      2const Backup = sqlite3.Backup;
    +
       62.      2
    +
       63.      2inherits(Database, EventEmitter);
    +
       64.      2inherits(Statement, EventEmitter);
    +
       65.      2inherits(Backup, EventEmitter);
    +
       66.      2
    +
       67.      2// Database#prepare(sql, [bind1, bind2, ...], [callback])
    +
       68.   1356Database.prototype.prepare = normalizeMethod(function(statement, params) {
    +
       69.   1356    return params.length
    +
       70.      7        ? statement.bind.apply(statement, params)
    +
       71.   1349        : statement;
    +
       72.   1356});
    +
       73.      2
    +
       74.      2// Database#run(sql, [bind1, bind2, ...], [callback])
    +
       75.   2074Database.prototype.run = normalizeMethod(function(statement, params) {
    +
       76.   2074    statement.run.apply(statement, params).finalize();
    +
       77.   2074    return this;
    +
       78.   2074});
    +
       79.      2
    +
       80.      2// Database#get(sql, [bind1, bind2, ...], [callback])
    +
       81.     35Database.prototype.get = normalizeMethod(function(statement, params) {
    +
       82.     35    statement.get.apply(statement, params).finalize();
    +
       83.     35    return this;
    +
       84.     35});
    +
       85.      2
    +
       86.      2// Database#all(sql, [bind1, bind2, ...], [callback])
    +
       87.      9Database.prototype.all = normalizeMethod(function(statement, params) {
    +
       88.      9    statement.all.apply(statement, params).finalize();
    +
       89.      9    return this;
    +
       90.      9});
    +
       91.      2
    +
       92.      2// Database#each(sql, [bind1, bind2, ...], [callback], [complete])
    +
       93.      7Database.prototype.each = normalizeMethod(function(statement, params) {
    +
       94.      7    statement.each.apply(statement, params).finalize();
    +
       95.      7    return this;
    +
       96.      7});
    +
       97.      2
    +
       98.      2Database.prototype.map = normalizeMethod(function(statement, params) {
    +
       99.      2    statement.map.apply(statement, params).finalize();
    +
      100.      2    return this;
    +
      101.      2});
    +
      102.      2
    +
      103.      2// Database#backup(filename, [callback])
    +
      104.      2// Database#backup(filename, destName, sourceName, filenameIsDest, [callback])
    +
      105.     15Database.prototype.backup = function() {
    +
      106.     15    let backup;
    +
      107.     13    if (arguments.length <= 2) {
    +
      108.     13        // By default, we write the main database out to the main database of the named file.
    +
      109.     13        // This is the most likely use of the backup api.
    +
      110.     13        backup = new Backup(this, arguments[0], 'main', 'main', true, arguments[1]);
    +
      111.     13    } else {
    +
      112.      2        // Otherwise, give the user full control over the sqlite3_backup_init arguments.
    +
      113.      2        backup = new Backup(this, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]);
    +
      114.      2    }
    +
      115.     15    // Per the sqlite docs, exclude the following errors as non-fatal by default.
    +
      116.     15    backup.retryErrors = [sqlite3.BUSY, sqlite3.LOCKED];
    +
      117.     15    return backup;
    +
      118.     15};
    +
      119.      2
    +
      120.      2Statement.prototype.map = function() {
    +
      121.      2    const params = Array.prototype.slice.call(arguments);
    +
      122.      2    const callback = params.pop();
    +
      123.      2    params.push(function(err, rows) {
    +
      124.     -0        if (err) return callback(err);
    +
      125.      2        const result = {};
    +
      126.      2        if (rows.length) {
    +
      127.      2            const keys = Object.keys(rows[0]);
    +
      128.      2            const key = keys[0];
    +
      129.      1            if (keys.length > 2) {
    +
      130.      1                // Value is an object
    +
      131.      5                for (let i = 0; i < rows.length; i++) {
    +
      132.      5                    result[rows[i][key]] = rows[i];
    +
      133.      5                }
    +
      134.      1            } else {
    +
      135.      1                const value = keys[1];
    +
      136.      1                // Value is a plain value
    +
      137.      5                for (let i = 0; i < rows.length; i++) {
    +
      138.      5                    result[rows[i][key]] = rows[i][value];
    +
      139.      5                }
    +
      140.      1            }
    +
      141.      2        }
    +
      142.      2        callback(err, result);
    +
      143.      2    });
    +
      144.      2    return this.all.apply(this, params);
    +
      145.      2};
    +
      146.      2
    +
      147.      2let isVerbose = false;
    +
      148.      2
    +
      149.      2const supportedEvents = [ 'trace', 'profile', 'insert', 'update', 'delete' ];
    +
      150.      2
    +
      151.      7Database.prototype.addListener = Database.prototype.on = function(type) {
    +
      152.      7    const val = EventEmitter.prototype.addListener.apply(this, arguments);
    +
      153.      4    if (supportedEvents.indexOf(type) >= 0) {
    +
      154.      4        this.configure(type, true);
    +
      155.      4    }
    +
      156.      7    return val;
    +
      157.      7};
    +
      158.      2
    +
      159.      2Database.prototype.removeListener = function(type) {
    +
      160.      2    const val = EventEmitter.prototype.removeListener.apply(this, arguments);
    +
      161.      1    if (supportedEvents.indexOf(type) >= 0 && !this._events[type]) {
    +
      162.      1        this.configure(type, false);
    +
      163.      1    }
    +
      164.      2    return val;
    +
      165.      2};
    +
      166.      2
    +
      167.      1Database.prototype.removeAllListeners = function(type) {
    +
      168.      1    const val = EventEmitter.prototype.removeAllListeners.apply(this, arguments);
    +
      169.      1    if (supportedEvents.indexOf(type) >= 0) {
    +
      170.      1        this.configure(type, false);
    +
      171.      1    }
    +
      172.      1    return val;
    +
      173.      1};
    +
      174.      2
    +
      175.      2// Save the stack trace over EIO callbacks.
    +
      176.      1sqlite3.verbose = function() {
    +
      177.      1    if (!isVerbose) {
    +
      178.      1        const trace = require('./trace');
    +
      179.      1        [
    +
      180.      1            'prepare',
    +
      181.      1            'get',
    +
      182.      1            'run',
    +
      183.      1            'all',
    +
      184.      1            'each',
    +
      185.      1            'map',
    +
      186.      1            'close',
    +
      187.      1            'exec'
    +
      188.      8        ].forEach(function (name) {
    +
      189.      8            trace.extendTrace(Database.prototype, name);
    +
      190.      8        });
    +
      191.      1        [
    +
      192.      1            'bind',
    +
      193.      1            'get',
    +
      194.      1            'run',
    +
      195.      1            'all',
    +
      196.      1            'each',
    +
      197.      1            'map',
    +
      198.      1            'reset',
    +
      199.      1            'finalize',
    +
      200.      8        ].forEach(function (name) {
    +
      201.      8            trace.extendTrace(Statement.prototype, name);
    +
      202.      8        });
    +
      203.      1        isVerbose = true;
    +
      204.      1    }
    +
      205.      1
    +
      206.      1    return this;
    +
      207.      1};
    +
      208.      2
    +
    + + + + diff --git a/branch-master/.artifact/coverage_sqlite3_js/lib/trace.js.html b/branch-master/.artifact/coverage_sqlite3_js/lib/trace.js.html new file mode 100644 index 000000000..28608da6d --- /dev/null +++ b/branch-master/.artifact/coverage_sqlite3_js/lib/trace.js.html @@ -0,0 +1,207 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / lib/trace.js
    +
    +
    +
    +
    + 100.00 %
    + 39 / 39 +
    +
    + 0 / 39 +
    +
    + + +
    +
        1.      1// Inspired by https://github.com/tlrobinson/long-stack-traces
    +
        2.      1const util = require('util');
    +
        3.      1
    +
        4.     16function extendTrace(object, property, pos) {
    +
        5.     16    const old = object[property];
    +
        6.      1    object[property] = function() {
    +
        7.      1        const error = new Error();
    +
        8.      1        const name = object.constructor.name + '#' + property + '(' +
    +
        9.      2            Array.prototype.slice.call(arguments).map(function(el) {
    +
       10.      2                return util.inspect(el, false, 0);
    +
       11.      2            }).join(', ') + ')';
    +
       12.      1
    +
       13.      1        if (typeof pos === 'undefined') pos = -1;
    +
       14.      1        if (pos < 0) pos += arguments.length;
    +
       15.      1        const cb = arguments[pos];
    +
       16.      1        if (typeof arguments[pos] === 'function') {
    +
       17.      1            arguments[pos] = function replacement() {
    +
       18.      1                const err = arguments[0];
    +
       19.      1                if (err && err.stack && !err.__augmented) {
    +
       20.      1                    err.stack = filter(err).join('\n');
    +
       21.      1                    err.stack += '\n--> in ' + name;
    +
       22.      1                    err.stack += '\n' + filter(error).slice(1).join('\n');
    +
       23.      1                    err.__augmented = true;
    +
       24.      1                }
    +
       25.      1                return cb.apply(this, arguments);
    +
       26.      1            };
    +
       27.      1        }
    +
       28.      1        return old.apply(this, arguments);
    +
       29.      1    };
    +
       30.     16}
    +
       31.      1exports.extendTrace = extendTrace;
    +
       32.      1
    +
       33.      1
    +
       34.      2function filter(error) {
    +
       35.     13    return error.stack.split('\n').filter(function(line) {
    +
       36.     13        return line.indexOf(__filename) < 0;
    +
       37.     13    });
    +
       38.      2}
    +
       39.      1
    +
    + + + + diff --git a/branch-master/.artifact/coverage_sqlite3_js/touch.txt b/branch-master/.artifact/coverage_sqlite3_js/touch.txt new file mode 100644 index 000000000..e69de29bb diff --git a/branch-master/.artifact/coverage_sqlite3_sh/coverage_badge.svg b/branch-master/.artifact/coverage_sqlite3_sh/coverage_badge.svg new file mode 100644 index 000000000..8e28d5ff5 --- /dev/null +++ b/branch-master/.artifact/coverage_sqlite3_sh/coverage_badge.svg @@ -0,0 +1,14 @@ + + + + +coverage +97.62 % + + diff --git a/branch-master/.artifact/coverage_sqlite3_sh/coverage_report.txt b/branch-master/.artifact/coverage_sqlite3_sh/coverage_report.txt new file mode 100644 index 000000000..b19e8684a --- /dev/null +++ b/branch-master/.artifact/coverage_sqlite3_sh/coverage_report.txt @@ -0,0 +1,16 @@ +V8 Coverage Report ++----------------------------------+-------------------+-------------------+ +| Files covered | Lines | Remaining | ++----------------------------------+-------------------+-------------------+ +| ./ | 97.62 % | | +| *******************************_ | 247 / 253 | 6 / 253 | ++----------------------------------+-------------------+-------------------+ +| ./lib/sqlite3-binding.js | 100.00 % | | +| ******************************** | 6 / 6 | 0 / 6 | ++----------------------------------+-------------------+-------------------+ +| ./lib/sqlite3.js | 97.11 % | | +| *******************************_ | 202 / 208 | 6 / 208 | ++----------------------------------+-------------------+-------------------+ +| ./lib/trace.js | 100.00 % | | +| ******************************** | 39 / 39 | 0 / 39 | ++----------------------------------+-------------------+-------------------+ diff --git a/branch-master/.artifact/coverage_sqlite3_sh/index.html b/branch-master/.artifact/coverage_sqlite3_sh/index.html new file mode 100644 index 000000000..83d95a7bd --- /dev/null +++ b/branch-master/.artifact/coverage_sqlite3_sh/index.html @@ -0,0 +1,212 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . /
    +
    +
    +
    +
    + 97.62 %
    + 247 / 253 +
    +
    + 6 / 253 +
    + . / lib/sqlite3-binding.js
    +
    +
    +
    +
    + 100.00 %
    + 6 / 6 +
    +
    + 0 / 6 +
    + . / lib/sqlite3.js
    +
    +
    +
    +
    + 97.11 %
    + 202 / 208 +
    +
    + 6 / 208 +
    + . / lib/trace.js
    +
    +
    +
    +
    + 100.00 %
    + 39 / 39 +
    +
    + 0 / 39 +
    +
    + + + + diff --git a/branch-master/.artifact/coverage_sqlite3_sh/lib/sqlite3-binding.js.html b/branch-master/.artifact/coverage_sqlite3_sh/lib/sqlite3-binding.js.html new file mode 100644 index 000000000..d3fec08a6 --- /dev/null +++ b/branch-master/.artifact/coverage_sqlite3_sh/lib/sqlite3-binding.js.html @@ -0,0 +1,174 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / lib/sqlite3-binding.js
    +
    +
    +
    +
    + 100.00 %
    + 6 / 6 +
    +
    + 0 / 6 +
    +
    + + +
    +
        1.      2const binary = require('@mapbox/node-pre-gyp');
    +
        2.      2const path = require('path');
    +
        3.      2const binding_path = binary.find(path.resolve(path.join(__dirname,'../package.json')));
    +
        4.      2const binding = require(binding_path);
    +
        5.      2module.exports = exports = binding;
    +
        6.      2
    +
    + + + + diff --git a/branch-master/.artifact/coverage_sqlite3_sh/lib/sqlite3.js.html b/branch-master/.artifact/coverage_sqlite3_sh/lib/sqlite3.js.html new file mode 100644 index 000000000..b0e82f5b0 --- /dev/null +++ b/branch-master/.artifact/coverage_sqlite3_sh/lib/sqlite3.js.html @@ -0,0 +1,376 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / lib/sqlite3.js
    +
    +
    +
    +
    + 97.11 %
    + 202 / 208 +
    +
    + 6 / 208 +
    +
    + + +
    +
        1.      2const path = require('path');
    +
        2.      2const sqlite3 = require('./sqlite3-binding.js');
    +
        3.      2const EventEmitter = require('events').EventEmitter;
    +
        4.      2module.exports = exports = sqlite3;
    +
        5.      2
    +
        6.     12function normalizeMethod (fn) {
    +
        7.   4299    return function (sql) {
    +
        8.   4299        let errBack;
    +
        9.   4299        const args = Array.prototype.slice.call(arguments, 1);
    +
       10.   4299
    +
       11.   1119        if (typeof args[args.length - 1] === 'function') {
    +
       12.   1119            const callback = args[args.length - 1];
    +
       13.   1119            errBack = function(err) {
    +
       14.   1119                if (err) {
    +
       15.   1119                    callback(err);
    +
       16.   1119                }
    +
       17.   1119            };
    +
       18.   1119        }
    +
       19.   4299        const statement = new Statement(this, sql, errBack);
    +
       20.   4299        return fn.call(this, statement, args);
    +
       21.   4299    };
    +
       22.     12}
    +
       23.      2
    +
       24.      6function inherits(target, source) {
    +
       25.      6    for (const k in source.prototype)
    +
       26.    108        target.prototype[k] = source.prototype[k];
    +
       27.      6}
    +
       28.      2
    +
       29.      2sqlite3.cached = {
    +
       30.      4    Database: function(file, a, b) {
    +
       31.     -0        if (file === '' || file === ':memory:') {
    +
       32.     -0            // Don't cache special databases.
    +
       33.     -0            return new Database(file, a, b);
    +
       34.     -0        }
    +
       35.      4
    +
       36.      4        let db;
    +
       37.      4        file = path.resolve(file);
    +
       38.      4
    +
       39.      2        if (!sqlite3.cached.objects[file]) {
    +
       40.      2            db = sqlite3.cached.objects[file] = new Database(file, a, b);
    +
       41.      2        }
    +
       42.      2        else {
    +
       43.      2            // Make sure the callback is called.
    +
       44.      2            db = sqlite3.cached.objects[file];
    +
       45.     -0            const callback = (typeof a === 'number') ? b : a;
    +
       46.      2            if (typeof callback === 'function') {
    +
       47.      2                function cb() { callback.call(db, null); }
    +
       48.      2                if (db.open) process.nextTick(cb);
    +
       49.      2                else db.once('open', cb);
    +
       50.      2            }
    +
       51.      2        }
    +
       52.      4
    +
       53.      4        return db;
    +
       54.      4    },
    +
       55.      2    objects: {}
    +
       56.      2};
    +
       57.      2
    +
       58.      2
    +
       59.      2const Database = sqlite3.Database;
    +
       60.      2const Statement = sqlite3.Statement;
    +
       61.      2const Backup = sqlite3.Backup;
    +
       62.      2
    +
       63.      2inherits(Database, EventEmitter);
    +
       64.      2inherits(Statement, EventEmitter);
    +
       65.      2inherits(Backup, EventEmitter);
    +
       66.      2
    +
       67.      2// Database#prepare(sql, [bind1, bind2, ...], [callback])
    +
       68.   2172Database.prototype.prepare = normalizeMethod(function(statement, params) {
    +
       69.   2172    return params.length
    +
       70.      7        ? statement.bind.apply(statement, params)
    +
       71.   2165        : statement;
    +
       72.   2172});
    +
       73.      2
    +
       74.      2// Database#run(sql, [bind1, bind2, ...], [callback])
    +
       75.   2074Database.prototype.run = normalizeMethod(function(statement, params) {
    +
       76.   2074    statement.run.apply(statement, params).finalize();
    +
       77.   2074    return this;
    +
       78.   2074});
    +
       79.      2
    +
       80.      2// Database#get(sql, [bind1, bind2, ...], [callback])
    +
       81.     35Database.prototype.get = normalizeMethod(function(statement, params) {
    +
       82.     35    statement.get.apply(statement, params).finalize();
    +
       83.     35    return this;
    +
       84.     35});
    +
       85.      2
    +
       86.      2// Database#all(sql, [bind1, bind2, ...], [callback])
    +
       87.      9Database.prototype.all = normalizeMethod(function(statement, params) {
    +
       88.      9    statement.all.apply(statement, params).finalize();
    +
       89.      9    return this;
    +
       90.      9});
    +
       91.      2
    +
       92.      2// Database#each(sql, [bind1, bind2, ...], [callback], [complete])
    +
       93.      7Database.prototype.each = normalizeMethod(function(statement, params) {
    +
       94.      7    statement.each.apply(statement, params).finalize();
    +
       95.      7    return this;
    +
       96.      7});
    +
       97.      2
    +
       98.      2Database.prototype.map = normalizeMethod(function(statement, params) {
    +
       99.      2    statement.map.apply(statement, params).finalize();
    +
      100.      2    return this;
    +
      101.      2});
    +
      102.      2
    +
      103.      2// Database#backup(filename, [callback])
    +
      104.      2// Database#backup(filename, destName, sourceName, filenameIsDest, [callback])
    +
      105.     15Database.prototype.backup = function() {
    +
      106.     15    let backup;
    +
      107.     13    if (arguments.length <= 2) {
    +
      108.     13        // By default, we write the main database out to the main database of the named file.
    +
      109.     13        // This is the most likely use of the backup api.
    +
      110.     13        backup = new Backup(this, arguments[0], 'main', 'main', true, arguments[1]);
    +
      111.     13    } else {
    +
      112.      2        // Otherwise, give the user full control over the sqlite3_backup_init arguments.
    +
      113.      2        backup = new Backup(this, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]);
    +
      114.      2    }
    +
      115.     15    // Per the sqlite docs, exclude the following errors as non-fatal by default.
    +
      116.     15    backup.retryErrors = [sqlite3.BUSY, sqlite3.LOCKED];
    +
      117.     15    return backup;
    +
      118.     15};
    +
      119.      2
    +
      120.      2Statement.prototype.map = function() {
    +
      121.      2    const params = Array.prototype.slice.call(arguments);
    +
      122.      2    const callback = params.pop();
    +
      123.      2    params.push(function(err, rows) {
    +
      124.     -0        if (err) return callback(err);
    +
      125.      2        const result = {};
    +
      126.      2        if (rows.length) {
    +
      127.      2            const keys = Object.keys(rows[0]);
    +
      128.      2            const key = keys[0];
    +
      129.      1            if (keys.length > 2) {
    +
      130.      1                // Value is an object
    +
      131.      5                for (let i = 0; i < rows.length; i++) {
    +
      132.      5                    result[rows[i][key]] = rows[i];
    +
      133.      5                }
    +
      134.      1            } else {
    +
      135.      1                const value = keys[1];
    +
      136.      1                // Value is a plain value
    +
      137.      5                for (let i = 0; i < rows.length; i++) {
    +
      138.      5                    result[rows[i][key]] = rows[i][value];
    +
      139.      5                }
    +
      140.      1            }
    +
      141.      2        }
    +
      142.      2        callback(err, result);
    +
      143.      2    });
    +
      144.      2    return this.all.apply(this, params);
    +
      145.      2};
    +
      146.      2
    +
      147.      2let isVerbose = false;
    +
      148.      2
    +
      149.      2const supportedEvents = [ 'trace', 'profile', 'insert', 'update', 'delete' ];
    +
      150.      2
    +
      151.      7Database.prototype.addListener = Database.prototype.on = function(type) {
    +
      152.      7    const val = EventEmitter.prototype.addListener.apply(this, arguments);
    +
      153.      4    if (supportedEvents.indexOf(type) >= 0) {
    +
      154.      4        this.configure(type, true);
    +
      155.      4    }
    +
      156.      7    return val;
    +
      157.      7};
    +
      158.      2
    +
      159.      2Database.prototype.removeListener = function(type) {
    +
      160.      2    const val = EventEmitter.prototype.removeListener.apply(this, arguments);
    +
      161.      1    if (supportedEvents.indexOf(type) >= 0 && !this._events[type]) {
    +
      162.      1        this.configure(type, false);
    +
      163.      1    }
    +
      164.      2    return val;
    +
      165.      2};
    +
      166.      2
    +
      167.      1Database.prototype.removeAllListeners = function(type) {
    +
      168.      1    const val = EventEmitter.prototype.removeAllListeners.apply(this, arguments);
    +
      169.      1    if (supportedEvents.indexOf(type) >= 0) {
    +
      170.      1        this.configure(type, false);
    +
      171.      1    }
    +
      172.      1    return val;
    +
      173.      1};
    +
      174.      2
    +
      175.      2// Save the stack trace over EIO callbacks.
    +
      176.      1sqlite3.verbose = function() {
    +
      177.      1    if (!isVerbose) {
    +
      178.      1        const trace = require('./trace');
    +
      179.      1        [
    +
      180.      1            'prepare',
    +
      181.      1            'get',
    +
      182.      1            'run',
    +
      183.      1            'all',
    +
      184.      1            'each',
    +
      185.      1            'map',
    +
      186.      1            'close',
    +
      187.      1            'exec'
    +
      188.      8        ].forEach(function (name) {
    +
      189.      8            trace.extendTrace(Database.prototype, name);
    +
      190.      8        });
    +
      191.      1        [
    +
      192.      1            'bind',
    +
      193.      1            'get',
    +
      194.      1            'run',
    +
      195.      1            'all',
    +
      196.      1            'each',
    +
      197.      1            'map',
    +
      198.      1            'reset',
    +
      199.      1            'finalize',
    +
      200.      8        ].forEach(function (name) {
    +
      201.      8            trace.extendTrace(Statement.prototype, name);
    +
      202.      8        });
    +
      203.      1        isVerbose = true;
    +
      204.      1    }
    +
      205.      1
    +
      206.      1    return this;
    +
      207.      1};
    +
      208.      2
    +
    + + + + diff --git a/branch-master/.artifact/coverage_sqlite3_sh/lib/trace.js.html b/branch-master/.artifact/coverage_sqlite3_sh/lib/trace.js.html new file mode 100644 index 000000000..28608da6d --- /dev/null +++ b/branch-master/.artifact/coverage_sqlite3_sh/lib/trace.js.html @@ -0,0 +1,207 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / lib/trace.js
    +
    +
    +
    +
    + 100.00 %
    + 39 / 39 +
    +
    + 0 / 39 +
    +
    + + +
    +
        1.      1// Inspired by https://github.com/tlrobinson/long-stack-traces
    +
        2.      1const util = require('util');
    +
        3.      1
    +
        4.     16function extendTrace(object, property, pos) {
    +
        5.     16    const old = object[property];
    +
        6.      1    object[property] = function() {
    +
        7.      1        const error = new Error();
    +
        8.      1        const name = object.constructor.name + '#' + property + '(' +
    +
        9.      2            Array.prototype.slice.call(arguments).map(function(el) {
    +
       10.      2                return util.inspect(el, false, 0);
    +
       11.      2            }).join(', ') + ')';
    +
       12.      1
    +
       13.      1        if (typeof pos === 'undefined') pos = -1;
    +
       14.      1        if (pos < 0) pos += arguments.length;
    +
       15.      1        const cb = arguments[pos];
    +
       16.      1        if (typeof arguments[pos] === 'function') {
    +
       17.      1            arguments[pos] = function replacement() {
    +
       18.      1                const err = arguments[0];
    +
       19.      1                if (err && err.stack && !err.__augmented) {
    +
       20.      1                    err.stack = filter(err).join('\n');
    +
       21.      1                    err.stack += '\n--> in ' + name;
    +
       22.      1                    err.stack += '\n' + filter(error).slice(1).join('\n');
    +
       23.      1                    err.__augmented = true;
    +
       24.      1                }
    +
       25.      1                return cb.apply(this, arguments);
    +
       26.      1            };
    +
       27.      1        }
    +
       28.      1        return old.apply(this, arguments);
    +
       29.      1    };
    +
       30.     16}
    +
       31.      1exports.extendTrace = extendTrace;
    +
       32.      1
    +
       33.      1
    +
       34.      2function filter(error) {
    +
       35.     13    return error.stack.split('\n').filter(function(line) {
    +
       36.     13        return line.indexOf(__filename) < 0;
    +
       37.     13    });
    +
       38.      2}
    +
       39.      1
    +
    + + + + diff --git a/branch-master/.artifact/coverage_sqlite3_sh/touch.txt b/branch-master/.artifact/coverage_sqlite3_sh/touch.txt new file mode 100644 index 000000000..e69de29bb diff --git a/branch-master/.artifact/jslint_report_hello.html b/branch-master/.artifact/jslint_report_hello.html new file mode 100644 index 000000000..ffa15a7ff --- /dev/null +++ b/branch-master/.artifact/jslint_report_hello.html @@ -0,0 +1,246 @@ + + +
    +JSLint Report +
    +
    +Report: Warnings (2) +
    +
    1: 17
    1. Undeclared 'console'.
    function foo() {console.log('hello world');} + +
    1: 29
    2. Use double quotes, not single quotes.
    function foo() {console.log('hello world');} + +
    +
    +
    +Report: Properties (1) + +
    +
    +Report: Functions (1) +
    +
    +
    global
    foo
    +
    1: 1
    foo()
    +
    +
    + diff --git a/branch-master/.artifact/jslint_wrapper_vscode/.vscode/launch.json b/branch-master/.artifact/jslint_wrapper_vscode/.vscode/launch.json new file mode 100644 index 000000000..140e1f4ec --- /dev/null +++ b/branch-master/.artifact/jslint_wrapper_vscode/.vscode/launch.json @@ -0,0 +1,13 @@ +{ + "configurations": [ + { + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}" + ], + "name": "Run Extension", + "request": "launch", + "type": "extensionHost" + } + ], + "version": "0.0.1" +} diff --git a/branch-master/.artifact/jslint_wrapper_vscode/.vscodeignore b/branch-master/.artifact/jslint_wrapper_vscode/.vscodeignore new file mode 100644 index 000000000..b81046e0b --- /dev/null +++ b/branch-master/.artifact/jslint_wrapper_vscode/.vscodeignore @@ -0,0 +1,12 @@ +* +.* +node_modules +!.npmignore +!CHANGELOG.md +!LICENSE +!README.md + +!asset_* +!jslint.mjs +!jslint_wrapper_cjs.cjs +!jslint_wrapper_vscode.js diff --git a/branch-master/.artifact/jslint_wrapper_vscode/LICENSE b/branch-master/.artifact/jslint_wrapper_vscode/LICENSE new file mode 100644 index 000000000..b3dbff00c --- /dev/null +++ b/branch-master/.artifact/jslint_wrapper_vscode/LICENSE @@ -0,0 +1,22 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/branch-master/.artifact/jslint_wrapper_vscode/README.md b/branch-master/.artifact/jslint_wrapper_vscode/README.md new file mode 100644 index 000000000..af9808967 --- /dev/null +++ b/branch-master/.artifact/jslint_wrapper_vscode/README.md @@ -0,0 +1,9 @@ +# Quickstart JSLint in VSCode +1. In VSCode, search and install extension [`vscode-jslint`](https://marketplace.visualstudio.com/items?itemName=jslint.vscode-jslint) +2. In VSCode, while editing a javascript file: + - right-click context-menu and select `[JSLint - Lint File]` + - or use key-binding `[Ctrl + Shift + J], [L]` + - or use key-binding `[ Cmd + Shift + J], [L]` for Mac +- screenshot + +[![screenshot](https://jslint-org.github.io/jslint/asset_image_jslint_wrapper_vscode.png)](https://marketplace.visualstudio.com/items?itemName=jslint.vscode-jslint) diff --git a/branch-master/.artifact/jslint_wrapper_vscode/asset_image_logo_512.png b/branch-master/.artifact/jslint_wrapper_vscode/asset_image_logo_512.png new file mode 100644 index 000000000..19d154d68 Binary files /dev/null and b/branch-master/.artifact/jslint_wrapper_vscode/asset_image_logo_512.png differ diff --git a/branch-master/.artifact/jslint_wrapper_vscode/jslint.mjs b/branch-master/.artifact/jslint_wrapper_vscode/jslint.mjs new file mode 100644 index 000000000..b486c1ce2 --- /dev/null +++ b/branch-master/.artifact/jslint_wrapper_vscode/jslint.mjs @@ -0,0 +1,11573 @@ +// #!/usr/bin/env node +// JSLint + +// The Unlicense +// +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + + +// jslint(source, option_dict, global_list) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze. +// option_dict An object whose keys correspond to option names. +// global_list An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// PHASE 1. Split by newlines into . +// PHASE 2. Lex into . +// PHASE 3. Parse into using the Pratt-parser. +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// PHASE 5. Check whitespace between tokens in . + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*jslint beta, node*/ +/*property + JSLINT_BETA, NODE_V8_COVERAGE, a, all, argv, arity, artifact, + assertErrorThrownAsync, assertJsonEqual, assertOrThrow, assign, async, b, + beta, bitwise, block, body, browser, c, calls, catch, catch_list, + catch_stack, causes, char, children, clear, closer, closure, code, column, + concat, consoleError, console_error, console_log, constant, context, + convert, count, coverageDir, create, cwd, d, dead, debugInline, default, + delta, devel, directive, directive_ignore_line, directive_list, directives, + dirname, disrupt, dot, edition, elem_list, ellipsis, else, end, endOffset, + endsWith, entries, env, error, eval, every, example_list, excludeList, exec, + execArgv, exit, exitCode, export_dict, exports, expression, extra, fart, + file, fileList, fileURLToPath, filter, finally, flag, floor, for, forEach, + formatted_message, free, freeze, from, froms, fsWriteFileWithParents, + fud_stmt, functionName, function_list, function_stack, functions, get, + getset, github_repo, globExclude, global, global_dict, global_list, + holeList, htmlEscape, id, identifier, import, import_list, import_meta_url, + inc, includeList, indent2, index, indexOf, init, initial, isArray, + isBlockCoverage, isHole, isNaN, is_equal, is_weird, join, jslint, + jslint_apidoc, jslint_assert, jslint_charset_ascii, jslint_cli, + jslint_edition, jslint_phase1_split, jslint_phase2_lex, jslint_phase3_parse, + jslint_phase4_walk, jslint_phase5_whitage, jslint_report, json, + jstestDescribe, jstestIt, jstestOnExit, keys, label, lbp, led_infix, length, + level, line, lineList, line_list, line_offset, line_source, lines, + linesCovered, linesTotal, live, log, long, loop, m, map, margin, match, max, + message, meta, min, mkdir, modeCoverageIgnoreFile, modeIndex, mode_cli, + mode_conditional, mode_json, mode_module, mode_noop, mode_property, + mode_shebang, mode_stop, module, moduleFsInit, moduleName, module_list, + name, names, node, nomen, noop, now, nr, nud_prefix, + objectDeepCopyWithKeysSorted, ok, on, open, opening, option, option_dict, + order, package_name, padEnd, padStart, parameters, parent, parentIi, parse, + pathname, pathnameList, platform, pop, processArgv, process_argv, + process_env, process_exit, promises, property, property_dict, push, quote, + ranges, readFile, readdir, readonly, recursive, reduce, repeat, replace, + resolve, result, reverse, role, round, scriptId, search, set, shebang, + shell, shift, signature, single, slice, some, sort, source, spawn, splice, + split, stack, stack_trace, start, startOffset, startsWith, statement, + statement_prv, stdio, stop, stop_at, stringify, subscript, switch, + syntax_dict, tenure, test, test_cause, test_internal_error, this, thru, + toLocaleString, toString, token, token_global, token_list, token_nxt, + token_tree, tokens, trace, tree, trim, trimEnd, trimRight, try, type, + unlink, unordered, unshift, url, used, v8CoverageListMerge, + v8CoverageReportCreate, value, variable, version, versions, warn, warn_at, + warning, warning_list, warnings, white, wrapped, writeFile +*/ + +// init debugInline +let debugInline = (function () { + let __consoleError = function () { + return; + }; + function debug(...argv) { + +// This function will print to stderr and then return [0]. + + __consoleError("\n\ndebugInline"); + __consoleError(...argv); + __consoleError("\n"); + return argv[0]; + } + debug(); // Coverage-hack. + __consoleError = console.error; //jslint-ignore-line + return debug; +}()); +let jslint_charset_ascii = ( + "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007" + + "\b\t\n\u000b\f\r\u000e\u000f" + + "\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017" + + "\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f" + + " !\"#$%&'()*+,-./0123456789:;<=>?" + + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + + "`abcdefghijklmnopqrstuvwxyz{|}~\u007f" +); +let jslint_edition = "v2024.11.24"; +let jslint_export; // The jslint object to be exported. +let jslint_fudge = 1; // Fudge starting line and starting + // ... column to 1. +let jslint_import_meta_url = ""; // import.meta.url used by cli. +let jslint_rgx_cap = ( + /^[A-Z]/ +); +let jslint_rgx_crlf = ( + /\n|\r\n?/ +); +let jslint_rgx_digits_bits = ( + /^[01_]*/ +); +let jslint_rgx_digits_decimals = ( + /^[0-9_]*/ +); +let jslint_rgx_digits_hexs = ( + /^[0-9A-F_]*/i +); +let jslint_rgx_digits_octals = ( + /^[0-7_]*/ +); +let jslint_rgx_directive = ( + /^(jslint|property|global)\s+(.*)$/ +); +let jslint_rgx_directive_part = ( + /([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*|$/g +); +let jslint_rgx_identifier = ( + /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/ +); +let jslint_rgx_json_number = ( + +// https://datatracker.ietf.org/doc/html/rfc7159#section-6 +// number = [ minus ] int [ frac ] [ exp ] + + /^-?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][\-+]?\d+)?$/ +); +let jslint_rgx_mega = ( + +// Vim-hack - vim-editor has trouble parsing naked '`' in regexp + + /[\u0060\\]|\$\{/ +); +let jslint_rgx_module = ( + /^[a-zA-Z0-9_$:.@\-\/]+$/ +); +let jslint_rgx_numeric_separator_illegal = ( + /__|_$|_n$/m +); +let jslint_rgx_slash_star_or_slash = ( + /\/\*|\/$/ +); +let jslint_rgx_tab = ( + /\t/g +); +let jslint_rgx_todo = ( + /\b(?:todo|TO\s?DO|HACK)\b/ +); +let jslint_rgx_token = new RegExp( + "^(" + + "(\\s+)" + + "|([a-zA-Z_$][a-zA-Z0-9_$]*)" + + "|[(){}\\[\\],:;'\"~\\`]" + + "|\\?[?.]?" + + "|=(?:==?|>)?" + + "|\\.+" + + "|\\*[*\\/=]?" + + "|\\/[*\\/]?" + + "|\\+[=+]?" + + "|-[=\\-]?" + + "|[\\^%]=?" + + "|&[&=]?" + + "|\\" + + "|[|=]?" + + "|>{1,3}=?" + + "|< throws an error. + + let err; + try { + await asyncFunc(); + } catch (errCaught) { + err = errCaught; + } + assertOrThrow(err, "No error thrown."); + assertOrThrow( + !regexp || new RegExp(regexp).test(err.message), + err + ); +} + +function assertJsonEqual(aa, bb, message) { + +// This function will assert JSON.stringify() === JSON.stringify(). + + aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa), undefined, 1); + bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb), undefined, 1); + if (aa !== bb) { + throw new Error( + "\n" + aa + "\n!==\n" + bb + + ( + typeof message === "string" + ? " - " + message + : message + ? " - " + JSON.stringify(message) + : "" + ) + ); + } +} + +function assertOrThrow(condition, message) { + +// This function will throw if is falsy. + + if (!condition) { + throw ( + (!message || typeof message === "string") + ? new Error(String(message).slice(0, 2048)) + : message + ); + } +} + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than '{}' because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +async function fsWriteFileWithParents(pathname, data) { + +// This function will write to and lazy-mkdirp if necessary. + + await moduleFsInit(); + +// Try writing to pathname. + + try { + await moduleFs.promises.writeFile(pathname, data); + } catch (ignore) { + +// Lazy mkdirp. + + await moduleFs.promises.mkdir(modulePath.dirname(pathname), { + recursive: true + }); + +// Retry writing to pathname. + + await moduleFs.promises.writeFile(pathname, data); + } + console.error("wrote file " + pathname); +} + +function globExclude({ + excludeList = [], + includeList = [], + pathnameList = [] +}) { + +// This function will +// 1. Exclude pathnames in that don't match glob-patterns in +// . +// 2. Exclude pathnames in that match glob-patterns in +// . + + function globAssertNotWeird(list, name) { + +// This function will check if of strings contain weird characters. + + [ + [ + "\n", ( + /^.*?([\u0000-\u0007\r]).*/gm + ) + ], + [ + "\r", ( + /^.*?([\n]).*/gm + ) + ] + ].forEach(function ([ + separator, rgx + ]) { + list.join(separator).replace(rgx, function (match0, char) { + throw new Error( + "Weird character " + + JSON.stringify(char) + + " found in " + name + " " + + JSON.stringify(match0) + ); + }); + }); + } + + function globToRegexp(pattern) { + +// This function will translate glob to javascript-regexp, +// which javascript can then use to "glob" pathnames. + + let ii = 0; + let isClass = false; + let strClass = ""; + let strRegex = ""; + pattern = pattern.replace(( + /\/\/+/g + ), "/"); + pattern = pattern.replace(( + /\*\*\*+/g + ), "**"); + pattern.replace(( + /\\\\|\\\[|\\\]|\[|\]|./g + ), function (match0) { + switch (match0) { + case "[": + if (isClass) { + strClass += "["; + return; + } + strClass += "\u0000"; + strRegex += "\u0000"; + isClass = true; + return; + case "]": + if (isClass) { + isClass = false; + return; + } + strRegex += "]"; + return; + default: + if (isClass) { + strClass += match0; + return; + } + strRegex += match0; + } + return ""; + }); + strClass += "\u0000"; + +// An expression "[!...]" matches a single character, namely any character that +// is not matched by the expression obtained by removing the first '!' from it. +// (Thus, "[!a-]" matches any single character except 'a', and '-'.) + + strClass = strClass.replace(( + /\u0000!/g + ), "\u0000^"); + +// One may include '-' in its literal meaning by making it the first or last +// character between the brackets. + + strClass = strClass.replace(( + /\u0000-/g + ), "\u0000\\-"); + strClass = strClass.replace(( + /-\u0000/g + ), "\\-\u0000"); + +// Escape brackets '[', ']' in character class. + + strClass = strClass.replace(( + /[\[\]]/g + ), "\\$&"); + +// https://stackoverflow.com/questions/3561493 +// /is-there-a-regexp-escape-function-in-javascript +// $()*+-./?[\]^{|} + + strRegex = strRegex.replace(( + +// Ignore [-/]. + + /[$()*+.?\[\\\]\^{|}]/g + ), "\\$&"); + +// Expand wildcard '**/*'. + + strRegex = strRegex.replace(( + /\\\*\\\*\/(?:\\\*)+/g + ), ".*?"); + +// Expand wildcard '**'. + + strRegex = strRegex.replace(( + /(^|\/)\\\*\\\*(\/|$)/gm + ), "$1.*?$2"); + +// Expand wildcard '*'. + + strRegex = strRegex.replace(( + /(?:\\\*)+/g + ), "[^\\/]*?"); + +// Expand wildcard '?'. + + strRegex = strRegex.replace(( + /\\\?/g + ), "[^\\/]"); + +// Expand directory-with-trailing-slash '.../'. + + strRegex = strRegex.replace(( + /\/$/gm + ), "\\/.*?"); + +// Merge strClass into strRegex. + + ii = 0; + strClass = strClass.split("\u0000"); + strRegex = strRegex.replace(( + /\u0000/g + ), function () { + ii += 1; + if (strClass[ii] === "") { + return ""; + } + return "[" + strClass[ii] + "]"; + }); + +// Change strRegex from string to regexp. + + strRegex = new RegExp("^" + strRegex + "$", "gm"); + return strRegex; + } + +// Validate excludeList, includeList, pathnameList. + + globAssertNotWeird(excludeList, "pattern"); + globAssertNotWeird(includeList, "pattern"); + globAssertNotWeird(pathnameList, "pathname"); + +// Optimization +// Concat pathnames into a single, newline-separated string, +// whose pathnames can all be filtered with a single, regexp-pass. + + pathnameList = pathnameList.join("\n"); + +// 1. Exclude pathnames in that don't match glob-patterns in +// . + + if (includeList.length > 0) { + includeList = includeList.map(globToRegexp); + includeList.forEach(function (pattern) { + pathnameList = pathnameList.replace(pattern, "\u0000$&"); + }); + pathnameList = pathnameList.replace(( + /^[^\u0000].*/gm + ), ""); + pathnameList = pathnameList.replace(( + /^\u0000+/gm + ), ""); + } + +// 2. Exclude pathnames in that match glob-patterns in +// . + + excludeList = excludeList.map(globToRegexp); + excludeList.forEach(function (pattern) { + pathnameList = pathnameList.replace(pattern, ""); + }); + +// Split newline-separated pathnames back to list. + + pathnameList = pathnameList.split("\n").filter(function (elem) { + return elem; + }); + return { + excludeList, + includeList, + pathnameList + }; +} + +function htmlEscape(str) { + +// This function will make html-safe by escaping & < >. + + return String(str).replace(( + /&/g + ), "&").replace(( + //g + ), ">"); +} + +function jslint( + source = "", // A text to analyze. + option_dict = empty(), // An object whose keys correspond to option + // ... names. + global_list = [] // An array of strings containing global + // ... variables that the file is allowed + // ... readonly access. +) { + +// The jslint function itself. + + let catch_list = []; // The array containing all catch-blocks. + let catch_stack = [ // The stack of catch-blocks. + { + context: empty() + } + ]; + let cause_dict = empty(); // The object of test-causes. + let directive_list = []; // The directive comments. + let export_dict = empty(); // The exported names and values. + let function_list = []; // The array containing all functions. + let function_stack = []; // The stack of functions. + let global_dict = empty(); // The object containing the global + // ... declarations. + let import_list = []; // The array collecting all import-from strings. + let line_list = String( // The array containing source lines. + "\n" + source + ).split(jslint_rgx_crlf).map(function (line_source) { + return { + line_source + }; + }); + let mode_stop = false; // true if JSLint cannot finish. + let property_dict = empty(); // The object containing the tallied + // ... property names. + let state = empty(); // jslint state-object to be passed between + // jslint functions. + let syntax_dict = empty(); // The object containing the parser. + let tenure = empty(); // The predefined property registry. + let token_global = { // The global object; the outermost context. + async: 0, + body: true, + context: empty(), + finally: 0, + from: 0, + id: "(global)", + level: 0, + line: jslint_fudge, + live: [], + loop: 0, + switch: 0, + thru: 0, + try: 0 + }; + let token_list = []; // The array of tokens. + let warning_list = []; // The array collecting all generated warnings. + +// Error reportage functions: + + function artifact(the_token) { + +// Return a string representing an artifact. + + the_token = the_token || state.token_nxt; + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); + } + + function is_equal(aa, bb) { + +// test_cause: +// ["0&&0", "is_equal", "", "", 0] + + test_cause(""); + +// Probably deadcode. +// if (aa === bb) { +// return true; +// } + + jslint_assert(!(aa === bb), `Expected !(aa === bb).`); + if (Array.isArray(aa)) { + return ( + Array.isArray(bb) + && aa.length === bb.length + && aa.every(function (value, index) { + +// test_cause: +// ["`${0}`&&`${0}`", "is_equal", "recurse_isArray", "", 0] +// ["`${0}`&&`${1}`", "is_equal", "recurse_isArray", "", 0] + + test_cause("recurse_isArray"); + return is_equal(value, bb[index]); + }) + ); + } + +// Probably deadcode. +// if (Array.isArray(bb)) { +// return false; +// } + + jslint_assert(!Array.isArray(bb), `Expected !Array.isArray(bb).`); + switch (aa.id === bb.id && aa.id) { + case "(number)": + case "(string)": + return aa.value === bb.value; + +// PR-394 - Bugfix +// Fix jslint falsely believing megastring literals `0` and `1` are similar. + + case "`": + if (!is_equal(aa.value, bb.value)) { + return false; + } + break; + } + if (is_weird(aa) || is_weird(bb)) { + +// test_cause: +// ["aa(/./)||{}", "is_equal", "false", "", 0] + + test_cause("false"); + return false; + } + if (aa.arity === bb.arity && aa.id === bb.id) { + if (aa.id === "." || aa.id === "?.") { + +// test_cause: +// ["aa.bb&&aa.bb", "is_equal", "recurse_arity_id", "", 0] +// ["aa?.bb&&aa?.bb", "is_equal", "recurse_arity_id", "", 0] + + test_cause("recurse_arity_id"); + return ( + is_equal(aa.expression, bb.expression) + && is_equal(aa.name, bb.name) + ); + } + if (aa.arity === "unary") { + +// test_cause: +// ["+0&&+0", "is_equal", "recurse_unary", "", 0] + + test_cause("recurse_unary"); + return is_equal(aa.expression, bb.expression); + } + if (aa.arity === "binary") { + +// test_cause: +// ["aa[0]&&aa[0]", "is_equal", "recurse_binary", "", 0] + + test_cause("recurse_binary"); + return ( + aa.id !== "(" + && is_equal(aa.expression[0], bb.expression[0]) + && is_equal(aa.expression[1], bb.expression[1]) + ); + } + if (aa.arity === "ternary") { + +// test_cause: +// ["aa=(``?``:``)&&(``?``:``)", "is_equal", "recurse_ternary", "", 0] + + test_cause("recurse_ternary"); + return ( + is_equal(aa.expression[0], bb.expression[0]) + && is_equal(aa.expression[1], bb.expression[1]) + && is_equal(aa.expression[2], bb.expression[2]) + ); + } + +// Probably deadcode. +// if (aa.arity === "function" || aa.arity === "regexp") { +// return false; +// } + + jslint_assert( + !(aa.arity === "function" || aa.arity === "regexp"), + `Expected !(aa.arity === "function" || aa.arity === "regexp").` + ); + +// test_cause: +// ["undefined&&undefined", "is_equal", "true", "", 0] + + test_cause("true"); + return true; + } + +// test_cause: +// ["null&&undefined", "is_equal", "false", "", 0] + + test_cause("false"); + return false; + } + + function is_weird(thing) { + switch (thing.id) { + case "(regexp)": + return true; + case "=>": + return true; + case "[": + return thing.arity === "unary"; + case "function": + return true; + case "{": + return true; + default: + return false; + } + } + + function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + the_token = the_token || state.token_nxt; + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); + } + + function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); + } + + function test_cause(code, aa, column) { + +// This function will instrument to for test-purposes. + + if (option_dict.test_cause) { + cause_dict[JSON.stringify([ + String(new Error().stack).replace(( + /^ at (?:file|stop|stop_at|test_cause|warn|warn_at)\b.*?\n/gm + ), "").match( + /\n at ((?:Object\.\w+?_)?\w+?) / + )[1].replace(( + /^Object\./ + ), ""), + code, + String( + (aa === undefined || aa === token_global) + ? "" + : aa + ), + column || 0 + ])] = true; + } + } + + function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + let the_warning; + the_token = the_token || state.token_nxt; + the_warning = warn_at( + code, + the_token.line, + (the_token.from || 0) + jslint_fudge, + a || artifact(the_token), + b, + c, + d + ); + +// Issue #408 +// Warnings that should be ignored sometimes suppress legitimate warnings. + + if (the_warning.directive_ignore_line) { + return the_warning; + } + +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token.warning) { + warning_list.pop(); + return the_warning; + } + the_token.warning = the_warning; + return the_warning; + } + + function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + let mm; + let warning = Object.assign(empty(), { + a, + b, + c, + code, + +// Fudge column numbers in warning message. + + column: column || jslint_fudge, + d, + line, + line_source: "", + name: "JSLintError" + }, line_list[line]); + warning.column = Math.max( + Math.min(warning.column, warning.line_source.length), + jslint_fudge + ); + test_cause(code, b || a, warning.column); + switch (code) { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + case "and": + mm = `The '&&' subexpression should be wrapped in parens.`; + break; + case "bad_assignment_a": + mm = `Bad assignment to '${a}'.`; + break; + case "bad_directive_a": + mm = `Bad directive '${a}'.`; + break; + case "bad_get": + mm = `A get function takes no parameters.`; + break; + case "bad_module_name_a": + mm = `Bad module name '${a}'.`; + break; + case "bad_option_a": + mm = `Bad option '${a}'.`; + break; + case "bad_set": + mm = `A set function takes one parameter.`; + break; + case "duplicate_a": + mm = `Duplicate '${a}'.`; + break; + case "empty_block": + mm = `Empty block.`; + break; + case "expected_a": + mm = `Expected '${a}'.`; + break; + case "expected_a_at_b_c": + mm = `Expected '${a}' at column ${b}, not column ${c}.`; + break; + case "expected_a_b": + mm = `Expected '${a}' and instead saw '${b}'.`; + break; + case "expected_a_b_before_c_d": + mm = `Expected ${a} '${b}' to be ordered before ${c} '${d}'.`; + break; + case "expected_a_b_from_c_d": + mm = ( + `Expected '${a}' to match '${b}' from line ${c}` + + ` and instead saw '${d}'.` + ); + break; + case "expected_a_before_b": + mm = `Expected '${a}' before '${b}'.`; + break; + case "expected_digits_after_a": + mm = `Expected digits after '${a}'.`; + break; + case "expected_four_digits": + mm = `Expected four digits after '\\u'.`; + break; + case "expected_identifier_a": + mm = `Expected an identifier and instead saw '${a}'.`; + break; + case "expected_line_break_a_b": + mm = `Expected a line break between '${a}' and '${b}'.`; + break; + case "expected_regexp_factor_a": + mm = `Expected a regexp factor and instead saw '${a}'.`; + break; + case "expected_space_a_b": + mm = `Expected one space between '${a}' and '${b}'.`; + break; + case "expected_statements_a": + mm = `Expected statements before '${a}'.`; + break; + case "expected_string_a": + mm = `Expected a string and instead saw '${a}'.`; + break; + case "expected_type_string_a": + mm = `Expected a type string and instead saw '${a}'.`; + break; + case "freeze_exports": + mm = ( + `Expected 'Object.freeze('. All export values should be frozen.` + ); + break; + +// PR-378 - Relax warning "function_in_loop". +// +// case "function_in_loop": +// mm = `Don't create functions within a loop.`; +// break; + +// PR-390 - Add numeric-separator check. + + case "illegal_num_separator": + mm = `Illegal numeric separator '_' at column ${column}.`; + break; + case "infix_in": + mm = ( + `Unexpected 'in'. Compare with undefined,` + + ` or use the hasOwnProperty method instead.` + ); + break; + case "label_a": + mm = `'${a}' is a statement label.`; + break; + case "misplaced_a": + mm = `Place '${a}' at the outermost level.`; + break; + case "misplaced_directive_a": + mm = `Place the '/*${a}*/' directive before the first statement.`; + break; + case "missing_await_statement": + mm = `Expected await statement in async function.`; + break; + +// PR-347 - Disable warning "missing_browser". +// +// case "missing_browser": +// mm = `/*global*/ requires the Assume a browser option.`; +// break; + + case "missing_m": + mm = `Expected 'm' flag on a multiline regular expression.`; + break; + case "naked_block": + mm = `Naked block.`; + break; + case "nested_comment": + mm = `Nested comment.`; + break; + case "not_label_a": + mm = `'${a}' is not a label.`; + break; + case "number_isNaN": + mm = `Use Number.isNaN function to compare with NaN.`; + break; + case "out_of_scope_a": + mm = `'${a}' is out of scope.`; + break; + case "redefinition_a_b": + mm = `Redefinition of '${a}' from line ${b}.`; + break; + case "redefinition_global_a_b": + mm = `Redefinition of global ${a} variable '${b}'.`; + break; + case "required_a_optional_b": + mm = `Required parameter '${a}' after optional parameter '${b}'.`; + break; + case "reserved_a": + mm = `Reserved name '${a}'.`; + break; + case "subscript_a": + mm = `['${a}'] is better written in dot notation.`; + break; + case "todo_comment": + mm = `Unexpected TODO comment.`; + break; + case "too_long": + mm = `Line is longer than 80 characters.`; + break; + case "too_many_digits": + mm = `Too many digits.`; + break; + case "unclosed_comment": + mm = `Unclosed comment.`; + break; + case "unclosed_disable": + mm = ( + `Directive '/*jslint-disable*/' was not closed` + + ` with '/*jslint-enable*/'.` + ); + break; + case "unclosed_mega": + mm = `Unclosed mega literal.`; + break; + case "unclosed_string": + mm = `Unclosed string.`; + break; + case "undeclared_a": + mm = `Undeclared '${a}'.`; + break; + case "unexpected_a": + mm = `Unexpected '${a}'.`; + break; + case "unexpected_a_after_b": + mm = `Unexpected '${a}' after '${b}'.`; + break; + case "unexpected_a_before_b": + mm = `Unexpected '${a}' before '${b}'.`; + break; + case "unexpected_at_top_level_a": + mm = `Expected '${a}' to be in a function.`; + break; + case "unexpected_char_a": + mm = `Unexpected character '${a}'.`; + break; + case "unexpected_comment": + mm = `Unexpected comment.`; + break; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// case "unexpected_directive_a": +// mm = `When using modules, don't use directive '/\u002a${a}'.`; +// break; + + case "unexpected_expression_a": + mm = `Unexpected expression '${a}' in statement position.`; + break; + case "unexpected_label_a": + mm = `Unexpected label '${a}'.`; + break; + case "unexpected_parens": + mm = `Don't wrap function literals in parens.`; + break; + case "unexpected_space_a_b": + mm = `Unexpected space between '${a}' and '${b}'.`; + break; + case "unexpected_statement_a": + mm = `Unexpected statement '${a}' in expression position.`; + break; + case "unexpected_trailing_space": + mm = `Unexpected trailing space.`; + break; + case "unexpected_typeof_a": + mm = ( + `Unexpected 'typeof'. Use '===' to compare directly with ${a}.` + ); + break; + case "uninitialized_a": + mm = `Uninitialized '${a}'.`; + break; + case "unopened_enable": + mm = ( + `Directive '/*jslint-enable*/' was not opened` + + ` with '/*jslint-disable*/'.` + ); + break; + case "unreachable_a": + mm = `Unreachable '${a}'.`; + break; + case "unregistered_property_a": + mm = `Unregistered property name '${a}'.`; + break; + case "unused_a": + mm = `Unused '${a}'.`; + break; + case "use_double": + mm = `Use double quotes, not single quotes.`; + break; + +// PR-386 - Fix issue #382 - Make fart-related warnings more readable. + + case "use_function_not_fart": + mm = ( + `Use 'function (...)', not '(...) =>' when arrow functions` + + ` become too complex.` + ); + break; + case "use_open": + mm = ( + `Wrap a ternary expression in parens,` + + ` with a line break after the left paren.` + ); + break; + case "use_spaces": + mm = `Use spaces, not tabs.`; + break; + case "var_on_top": + mm = `Move variable declaration to top of function or script.`; + break; + case "var_switch": + mm = `Don't declare variables in a switch.`; + break; + case "weird_condition_a": + mm = `Weird condition '${a}'.`; + break; + case "weird_expression_a": + mm = `Weird expression '${a}'.`; + break; + case "weird_loop": + mm = `Weird loop.`; + break; + case "weird_property_a": + mm = `Weird property name '${a}'.`; + break; + case "weird_relation_a": + mm = `Weird relation '${a}'.`; + break; + case "wrap_condition": + mm = `Wrap the condition in parens.`; + break; + +// PR-386 - Fix issue #382 - Make fart-related warnings more readable. + + case "wrap_fart_parameter": + mm = `Wrap the parameter before '=>' in parens.`; + break; + case "wrap_immediate": + mm = ( + `Wrap an immediate function invocation in parentheses to assist` + + ` the reader in understanding that the expression is the` + + ` result of a function, and not the function itself.` + ); + break; + case "wrap_regexp": + mm = `Wrap this regexp in parens to avoid confusion.`; + break; + case "wrap_unary": + mm = `Wrap the unary expression in parens.`; + break; + } + +// Validate mm. + + jslint_assert(mm, code); + warning.message = mm; + +// PR-242 - Include stack_trace for jslint to debug itself for errors. + + if (option_dict.trace) { + warning.stack_trace = new Error().stack; + } + if (warning.directive_ignore_line) { + +// test_cause: +// ["0 //jslint-ignore-line", "semicolon", "directive_ignore_line", "", 0] + + test_cause("directive_ignore_line"); + return warning; + } + warning_list.push(warning); + return warning; + } + + try { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + + option_dict = Object.assign(empty(), option_dict); + Object.assign(state, { + artifact, + catch_list, + catch_stack, + directive_list, + export_dict, + function_list, + function_stack, + global_dict, + global_list, + import_list, + is_equal, + is_weird, + line_list, + mode_json: false, // true if parsing JSON. + mode_module: false, // true if import or export was used. + mode_property: false, // true if directive /*property*/ is + // ... used. + mode_shebang: false, // true if #! is seen on the first line. + option_dict, + property_dict, + source, + stop, + stop_at, + syntax_dict, + tenure, + test_cause, + token_global, + token_list, + token_nxt: token_global, + warn, + warn_at, + warning_list + }); + +// PHASE 1. Split by newlines into . + + jslint_phase1_split(state); + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 2. Lex into . + + jslint_phase2_lex(state); + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 3. Parse into using the Pratt-parser. + + jslint_phase3_parse(state); + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). + + if (!state.mode_json) { + jslint_phase4_walk(state); + } + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 5. Check whitespace between tokens in . + + if (!state.mode_json && warning_list.length === 0) { + jslint_phase5_whitage(state); + } + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PR-347 - Disable warning "missing_browser". +// +// if (!option_dict.browser) { +// directive_list.forEach(function (comment) { +// if (comment.directive === "global") { +// +// // test_cause: +// // ["/*global aa*/", "jslint", "missing_browser", "(comment)", 1] +// +// warn("missing_browser", comment); +// } +// }); +// } + + if (option_dict.test_internal_error) { + jslint_assert(undefined, "test_internal_error"); + } + } catch (err) { + mode_stop = true; + err.message = "[JSLint was unable to finish] " + err.message; + err.mode_stop = true; + if (err.name !== "JSLintError") { + Object.assign(err, { + column: jslint_fudge, + line: jslint_fudge, + line_source: "", + stack_trace: err.stack + }); + } + if (warning_list.indexOf(err) === -1) { + warning_list.push(err); + } + } + +// Sort warning_list by mode_stop first, line, column respectively. + + warning_list.sort(function (aa, bb) { + return ( + Boolean(bb.mode_stop) - Boolean(aa.mode_stop) + || aa.line - bb.line + || aa.column - bb.column + ); + +// Update each warning with formatted_message ready-for-use by jslint_cli. + + }).map(function ({ + column, + line, + line_source, + message, + stack_trace = "" + }, ii, list) { + list[ii].formatted_message = String( + String(ii + 1).padStart(2, " ") + + ". \u001b[31m" + message + "\u001b[39m" + + " \u001b[90m\/\/ line " + line + ", column " + column + + "\u001b[39m\n" + + (" " + line_source.trim()).slice(0, 72) + "\n" + + stack_trace + ).trimRight(); + }); + + return { + causes: cause_dict, + directives: directive_list, + edition: jslint_edition, + exports: export_dict, + froms: import_list, + functions: function_list, + global: token_global, + id: "(JSLint)", + json: state.mode_json, + lines: line_list, + module: state.mode_module === true, + ok: warning_list.length === 0 && !mode_stop, + option: option_dict, + property: property_dict, + shebang: ( + state.mode_shebang + ? line_list[jslint_fudge].line_source + : undefined + ), + stop: mode_stop, + tokens: token_list, + tree: state.token_tree, + warnings: warning_list + }; +} + +// PR-362 - Add API Doc. + +async function jslint_apidoc({ + example_list, + github_repo, + module_list, + package_name, + pathname, + version +}) { + +// This function will create API Doc from . + + let elem_ii = 0; + let html; + + function elem_create(moduleObj, key, moduleName) { + +// This function will create a sub API Doc from elem []. + + let example = "N/A"; + let id = encodeURIComponent("apidoc.elem." + moduleName + "." + key); + let name; + let signature; + let source; + name = htmlEscape((typeof moduleObj[key]) + " " + key); + if (typeof moduleObj[key] !== "function") { + return { + name, + signature: (` + +${name} + + `), + source: (` +
  • +

    + + ${name} + +

    +
  • + `) + }; + } + // init source + source = htmlEscape(trim_start(moduleObj[key].toString())); + // init signature + source = source.replace(( + /(\([\S\s]*?\)) \{/ + ), function (match0, match1) { + signature = htmlEscape( + match1.replace(( + / *?\/\*[\S\s]*?\*\/ */g + ), "").replace(( + / *?\/\/.*/g + ), "").replace(( + /\n{2,}/g + ), "\n") + ); + return match0; + }); + // init comment + source = source.replace(( + /\n(?:\/\/.*?\n)+\n/ + ), "$&"); + // init example + example_list.some(function (example2) { + example2.replace( + new RegExp(( + "((?:\\n.*?){8}(function )?)\\b" + + key + + "(\\((?:.*?\\n){8})" + ), "g"), + function (ignore, header, isDeclaration, footer) { + if (!isDeclaration) { + example = "..." + trim_start( + htmlEscape(header) + + "" + + htmlEscape(key) + + "" + + htmlEscape(footer) + ).trimEnd() + "\n..."; + } + return ""; + } + ); + return example !== "N/A"; + }); + if (source.length > 2048) { + source = source.slice(0, 2048) + "...\n}\n"; + } + return { + name, + signature: (` + +${name}${signature} + + `), + source: (` +
  • +

    + + ${name}${signature} + +

    +
  • +
  • Description and source-code:
    ${source}
  • +
  • Example usage:
    ${example}
  • + `) + }; + } + + function trim_start(str) { + +// This function will normalize whitespace before . + + let whitespace = ""; + str.trim().replace(( + /^ */gm + ), function (match0) { + if (whitespace === "" || match0.length < whitespace.length) { + whitespace = match0; + } + return ""; + }); + str = str.replace(new RegExp("^" + whitespace, "gm"), ""); + return str; + } + await moduleFsInit(); + +// Html-escape params. + + github_repo = htmlEscape(github_repo); + package_name = htmlEscape(package_name); + version = htmlEscape(version); + +// Init example_list. + + example_list = await Promise.all(example_list.map(async function (file) { + +// This function will read example from given file. + + let result = await moduleFs.promises.readFile(file, "utf8"); + result = ( + "\n\n\n\n\n\n\n\n" + // bug-workaround - truncate example to manageable size + + result.slice(0, 524288) + + "\n\n\n\n\n\n\n\n" + ); + result = result.replace(( + /\r\n*/g + ), "\n"); + return result; + })); + +// Init module_list. + + module_list = await Promise.all(module_list.map(async function ({ + pathname + }) { + let moduleName = htmlEscape(JSON.stringify(pathname)); + let moduleObj = await import(pathname); + if (moduleObj.default) { + moduleObj = moduleObj.default; + } + return { + elem_list: Object.keys(moduleObj).map(function (key) { + return elem_create(moduleObj, key, moduleName); + }).sort(function (aa, bb) { + return ( + aa.name < bb.name + ? -1 + : 1 + ); + }).map(function (elem) { + elem_ii += 1; + elem.signature = elem.signature.replace( + ">", + ">" + elem_ii + ". " + ); + elem.source = elem.source.replace( + "\">", + "\">" + elem_ii + ". " + ); + return elem; + }), + id: encodeURIComponent("apidoc.module." + moduleName), + moduleName + }; + })); + html = (` + + + + + + +${package_name} apidoc + + + +
    +

    API Doc for ${package_name} (${version})

    +
    + +

    Table of Contents

    +
    +
      + `) + module_list.map(function ({ + elem_list, + id, + moduleName + }) { + return ( + (` +
    • + Module ${moduleName} +
        + `) + + elem_list.map(function ({ + signature + }) { + return "
      • \n" + signature + "\n
      • \n"; + }).join("") + + (` +
      +
    • + `) + ); + }).join("") + (` +
    +
    + `) + module_list.map(function ({ + elem_list, + id, + moduleName + }) { + return ( + (` +
    +

    Module ${moduleName}

    +
      + `) + + elem_list.map(function ({ + source + }) { + return source; + }).join("") + + (` +
    +
    + `) + ); + }).join("") + (` +
    + [ + This document was created with + JSLint + ] +
    +
    + + + `); + html = html.trim().replace(( + / +?$/gm + ), "") + "\n"; + await fsWriteFileWithParents(pathname, html); +} + +function jslint_assert(condition, message) { + +// This function will throw if is falsy. + + if (condition) { + return condition; + } + throw new Error( + `This was caused by a bug in JSLint. +Please open an issue with this stack-trace (and possible example-code) at +https://github.com/jslint-org/jslint/issues. +edition = "${jslint_edition}"; +${String(message).slice(0, 2000)}` + ); +} + +async function jslint_cli({ + console_error, + console_log, + file, + import_meta_url, + mode_cli, + mode_noop, + option, + process_argv, + process_env, + process_exit, + source +}) { + +// This function will run jslint from nodejs-cli. + + let command; + let data; + let exit_code = 0; + let mode_report; + let mode_wrapper_vim; + let result; + + function jslint_from_file({ + code, + file, + line_offset = 0, + mode_conditional, + option = empty() + }) { + let result_from_file; + if ( + mode_conditional + && !( + /^\/\*jslint\b/m + ).test(code.slice(0, 65536)) + ) { + return; + } + option = Object.assign(empty(), option, { + file + }); + switch (( + /\.\w+?$|$/m + ).exec(file)[0]) { + case ".html": + +// Recursively jslint embedded "". + + code.replace(( + /^]*?>\n([\S\s]*?\n)<\/script>$/gm + ), function (ignore, match1, ii) { + jslint_from_file({ + code: match1, + file: file + ". + + import_meta_url = import_meta_url || jslint_import_meta_url; + if ( + jslint_rgx_url_search_window_jslint.test(import_meta_url) + && (typeof globalThis === "object" && globalThis) + ) { + globalThis.jslint = jslint; + } + +// Feature-detect nodejs. + + if (!( + (typeof process === "object" && process) + && process.versions + && typeof process.versions.node === "string" + && !mode_noop + )) { + return exit_code; + } + console_error = console_error || console.error; + console_log = console_log || console.log; + process_argv = process_argv || process.argv; + process_env = process_env || process.env; + process_exit = process_exit || process.exit; + await moduleFsInit(); + if ( + !( + +// Feature-detect nodejs-cli. + + process.execArgv.indexOf("--eval") === -1 + && process.execArgv.indexOf("-e") === -1 + && ( + ( + /[\/|\\]jslint(?:\.[cm]?js)?$/m + ).test(process_argv[1]) + || mode_cli + ) + && ( + moduleUrl.fileURLToPath(import_meta_url) + === + modulePath.resolve(process_argv[1]) + ) + ) + && !mode_cli + ) { + return exit_code; + } + +// init commmand + + command = String(process_argv[2]).split("="); + command[1] = command.slice(1).join("="); + + switch (command[0]) { + +// PR-362 - Add API Doc. + + case "jslint_apidoc": + await jslint_apidoc(Object.assign(JSON.parse(process_argv[3]), { + pathname: command[1] + })); + return; + +// PR-363 - Add command jslint_report. + + case "jslint_report": + mode_report = command[1]; + process_argv = process_argv.slice(1); + break; + +// COMMIT-b26d6df2 - Add command jslint_wrapper_vim. + + case "jslint_wrapper_vim": + mode_wrapper_vim = true; + process_argv = process_argv.slice(1); + break; + +// PR-364 - Add command v8_coverage_report. + + case "v8_coverage_report": + await v8CoverageReportCreate({ + consoleError: console_error, + coverageDir: command[1], + processArgv: process_argv.slice(3) + }); + return; + } + +// Normalize file relative to process.cwd(). + + process_argv.slice(2).some(function (arg) { + if (!arg.startsWith("-")) { + file = file || arg; + return true; + } + }); + if (!file) { + return; + } + file = modulePath.resolve(file) + "/"; + if (file.startsWith(process.cwd() + "/")) { + file = file.replace(process.cwd() + "/", "").slice(0, -1) || "."; + } + file = file.replace(( + /\\/g + ), "/").replace(( + /\/$/g + ), ""); + if (source) { + data = source; + } else { + +// jslint_cli - jslint directory. + + try { + data = await moduleFs.promises.readdir(file, "utf8"); + } catch (ignore) {} + if (data) { + await Promise.all(data.map(async function (file2) { + let code; + let time_start = Date.now(); + file2 = file + "/" + file2; + switch (( + /\.\w+?$|$/m + ).exec(file2)[0]) { + case ".cjs": + case ".html": + case ".js": + case ".json": + case ".md": + case ".mjs": + case ".sh": + break; + default: + return; + } + try { + code = await moduleFs.promises.readFile(file2, "utf8"); + } catch (ignore) { + return; + } + if ( + ( + /(?:\b|_)(?:lock|min|raw|rollup)(?:\b|_)/ + ).test(file2) + || !(code && code.length < 1048576) + ) { + return; + } + jslint_from_file({ + code, + file: file2, + option + }); + console_error( + "jslint - " + (Date.now() - time_start) + "ms - " + file2 + ); + })); + process_exit(exit_code); + return exit_code; + } + +// jslint_cli - jslint file. + + try { + data = await moduleFs.promises.readFile(file, "utf8"); + } catch (err) { + console_error(err); + exit_code = 1; + process_exit(exit_code); + return exit_code; + } + } + result = jslint_from_file({ + code: data, + file, + option + }); + if (mode_report) { + result = jslint.jslint_report(result); + result = `\n${result}\n`; + await fsWriteFileWithParents(mode_report, result); + } + process_exit(exit_code); + return exit_code; +} + +function jslint_phase1_split() { + +// PHASE 1. Split by newlines into . + + return; +} + +function jslint_phase2_lex(state) { + +// PHASE 2. Lex into . + + let { + artifact, + directive_list, + global_dict, + global_list, + line_list, + option_dict, + stop, + stop_at, + tenure, + test_cause, + token_global, + token_list, + warn, + warn_at + } = state; + let char; // The current character being lexed. + let column = 0; // The column number of the next character. + let from; // The starting column number of the token. + let from_mega; // The starting column of megastring. + let line = 0; // The line number of the next character. + let line_disable; // The starting line of "/*jslint-disable*/". + let line_mega; // The starting line of megastring. + let line_source = ""; // The remaining line source string. + let line_whole = ""; // The whole line source string. + let mode_digits_empty_string = 1; + let mode_digits_numeric_separator = 2; + let mode_directive = true; // true if directives are still allowed. + let mode_mega = false; // true if currently parsing a megastring + // ... literal. + let mode_regexp; // true if regular expression literal seen on + // ... this line. + let paren_backtrack_list = []; // List of most recent "(" tokens at any + // ... paren-depth. + let paren_depth = 0; // Keeps track of current paren-depth. + let snippet = ""; // A piece of string. + let token_1; // The first token. + let token_prv = token_global; // The previous token including + // ... comments. + let token_prv_expr = token_global; // The previous token excluding + // ... comments. + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions char_after, char_before, +// read_digits, and char_after_escape help in the parsing of literals. + + function char_after(match) { + +// Get the next character from the source line. Remove it from the line_source, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + +// test_cause: +// ["aa=/[", "char_after", "expected_a", "]", 5] +// ["aa=/aa{/", "char_after", "expected_a_b", "/", 8] + + return ( + char === "" + ? stop_at("expected_a", line, column - 1, match) + : stop_at("expected_a_b", line, column, match, char) + ); + } + char = line_source.slice(0, 1); + line_source = line_source.slice(1); + snippet += char || " "; + column += 1; + return char; + } + + function char_after_escape(extra) { + +// Validate char after escape "\\". + + char_after("\\"); + switch (char) { + case "": + +// test_cause: +// ["\"\\", "char_after_escape", "unclosed_string", "", 2] + + return stop_at("unclosed_string", line, column); + case "/": + return char_after(); + case "\\": + return char_after(); + case "`": + return char_after(); + case "b": + return char_after(); + case "f": + return char_after(); + case "n": + return char_after(); + case "r": + return char_after(); + case "t": + +// test_cause: +// ["\"\\/\\\\\\`\\b\\f\\n\\r\\t\"", "char_after_escape", "char_after", "", 0] + + test_cause("char_after"); + return char_after(); + case "u": + if (char_after("u") === "{") { + if (state.mode_json) { + +// test_cause: +// ["[\"\\u{12345}\"]", "char_after_escape", "unexpected_a", "{", 5] + + warn_at("unexpected_a", line, column, char); + } + if (read_digits("x", undefined) > 5) { + +// test_cause: +// ["\"\\u{123456}\"", "char_after_escape", "too_many_digits", "", 11] + + warn_at("too_many_digits", line, column); + } + if (char !== "}") { + +// test_cause: +// ["\"\\u{12345\"", "char_after_escape", "expected_a_before_b", "\"", 10] + + stop_at("expected_a_before_b", line, column, "}", char); + } + return char_after(); + } + char_before(); + if (read_digits("x", mode_digits_empty_string) < 4) { + +// test_cause: +// ["\"\\u0\"", "char_after_escape", "expected_four_digits", "", 5] + + warn_at("expected_four_digits", line, column); + } + return; + default: + if (extra && extra.indexOf(char) >= 0) { + return char_after(); + } + +// test_cause: +// ["\"\\0\"", "char_after_escape", "unexpected_a_before_b", "0", 3] + + warn_at("unexpected_a_before_b", line, column, "\\", char); + } + } + + function char_before() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the line_source. + + char = snippet.slice(-1); + line_source = char + line_source; + column -= char.length; + +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + return char; + } + + function check_numeric_separator(digits, column) { + +// This function will check for illegal numeric-separator in . + + digits.replace(( + jslint_rgx_numeric_separator_illegal + ), function (ignore, ii) { + +// test_cause: +// ["0x0_0_;", "check_numeric_separator", "illegal_num_separator", "", 6] +// ["0x0_0__0;", "check_numeric_separator", "illegal_num_separator", "", 6] +// ["aa=1_2_;", "check_numeric_separator", "illegal_num_separator", "", 7] +// ["aa=1_2__3;", "check_numeric_separator", "illegal_num_separator", "", 7] +// ["aa=1_2_n;", "check_numeric_separator", "illegal_num_separator", "", 7] + + warn_at("illegal_num_separator", line, column + ii + 1); + return ""; + }); + } + + function lex_comment() { + let body; + let ii = 0; + let jj = 0; + let the_comment; + +// Create a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + +// Create token from comment //.... + + if (snippet === "//") { + snippet = line_source; + line_source = ""; + the_comment = token_create("(comment)", snippet); + if (mode_mega) { + +// test_cause: +// ["`${//}`", "lex_comment", "unexpected_comment", "`", 4] + + warn("unexpected_comment", the_comment, "`"); + } + +// Create token from comment /*...*/. + + } else { + snippet = []; + if (line_source[0] === "/") { + +// test_cause: +// ["/*/", "lex_comment", "unexpected_a", "/", 2] + + warn_at("unexpected_a", line, column + ii, "/"); + } + +// Lex/loop through each line until "*/". + + while (true) { + // jslint_rgx_star_slash + ii = line_source.indexOf("*/"); + if (ii >= 0) { + break; + } + // jslint_rgx_slash_star + ii = line_source.indexOf("/*"); + if (ii >= 0) { + +// test_cause: +// ["/*/*", "lex_comment", "nested_comment", "", 2] + + warn_at("nested_comment", line, column + ii); + } + snippet.push(line_source); + line_source = read_line(); + if (line_source === undefined) { + +// test_cause: +// ["/*", "lex_comment", "unclosed_comment", "", 1] + + return stop_at("unclosed_comment", line, column); + } + } + jj = line_source.slice(0, ii).search( + jslint_rgx_slash_star_or_slash + ); + if (jj >= 0) { + +// test_cause: +// ["/*/**/", "lex_comment", "nested_comment", "", 2] + + warn_at("nested_comment", line, column + jj); + } + snippet.push(line_source.slice(0, ii)); + snippet = snippet.join(" "); + column += ii + 2; + line_source = line_source.slice(ii + 2); + the_comment = token_create("(comment)", snippet); + } + +// Uncompleted work comment. + + if (!option_dict.devel && jslint_rgx_todo.test(snippet)) { + +// test_cause: +// ["//todo", "lex_comment", "todo_comment", "(comment)", 1] //jslint-ignore-line + + warn("todo_comment", the_comment); + } + +// Lex directives in comment. + + [ + the_comment.directive, body + ] = Array.from(snippet.match(jslint_rgx_directive) || []).slice(1); + if (the_comment.directive === undefined) { + return the_comment; + } + directive_list.push(the_comment); + if (!mode_directive) { + +// test_cause: +// ["0\n/*global aa*/", "lex_comment", "misplaced_directive_a", "global", 1] + + warn_at("misplaced_directive_a", line, from, the_comment.directive); + return the_comment; + } + +// lex_directive(); +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + +// Lex/loop through each directive in /*...*/ + + ii = 0; + body.replace(jslint_rgx_directive_part, function ( + match0, + key, + val, + jj + ) { + if (ii !== jj) { + +// test_cause: +// ["/*jslint !*/", "lex_comment", "bad_directive_a", "!", 1] + + return stop("bad_directive_a", the_comment, body.slice(ii)); + } + if (match0 === "") { + return ""; + } + ii += match0.length; + switch (the_comment.directive) { + case "global": + if (val) { + +// test_cause: +// ["/*global aa:false*/", "lex_comment", "bad_option_a", "aa:false", 1] + + warn("bad_option_a", the_comment, key + ":" + val); + } + global_dict[key] = "user-defined"; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// state.mode_module = the_comment; + + break; + case "jslint": + if (!option_set_item(key, val !== "false")) { + +// test_cause: +// ["/*jslint undefined*/", "lex_comment", "bad_option_a", "undefined", 1] + + warn("bad_option_a", the_comment, key); + } + break; + case "property": + state.mode_property = true; + tenure[key] = true; + break; + } + return ""; + }); + return the_comment; + } + + function lex_megastring() { + let id; + let match; + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (mode_mega) { + +// test_cause: +// ["`${`", "lex_megastring", "expected_a_b", "`", 4] + + return stop_at("expected_a_b", line, column, "}", "`"); + } + from_mega = from; + line_mega = line; + mode_mega = true; + snippet = ""; + +// Parsing a mega literal is tricky. First create a ` token. + + token_create("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + while (true) { + match = line_source.match(jslint_rgx_mega) || { + "0": "", + index: 0 + }; + snippet += line_source.slice(0, match.index); + column += match.index; + line_source = line_source.slice(match.index); + match = match[0]; + switch (match) { + case "${": + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + token_create("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then create tokens that will become part of an expression until +// a } token is made. + + column += 2; + token_create("${"); + line_source = line_source.slice(2); + +// Lex/loop through each token inside megastring-expression `${...}`. + + while (true) { + id = lex_token().id; + if (id === "{") { + +// test_cause: +// ["`${{", "lex_megastring", "expected_a_b", "{", 4] + + return stop_at("expected_a_b", line, column, "}", "{"); + } + if (id === "}") { + break; + } + } + break; + case "\\": + snippet += line_source.slice(0, 2); + line_source = line_source.slice(2); + column += 2; + break; + case "`": + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + token_create("(string)", snippet).quote = "`"; + snippet = ""; + +// Terminate megastring with `. + + line_source = line_source.slice(1); + column += 1; + mode_mega = false; + return token_create("`"); + default: + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + snippet += line_source + "\n"; + if (read_line() === undefined) { + +// test_cause: +// ["`", "lex_megastring", "unclosed_mega", "", 1] + + return stop_at("unclosed_mega", line_mega, from_mega); + } + } + } + } + + function lex_number() { + let prefix = snippet; + +// PR-390 - Add numeric-separator check. + + check_numeric_separator(prefix, column - prefix.length); + char_after(); + switch (prefix === "0" && char) { + case "b": + case "o": + case "x": + read_digits(char, mode_digits_numeric_separator); + +// PR-351 - Ignore BigInt suffix 'n'. + + if (char === "n") { + char_after("n"); + } + break; + default: + if (char === ".") { + read_digits("d", mode_digits_numeric_separator); + } + if (char === "E" || char === "e") { + char_after(char); + if (char !== "+" && char !== "-") { + char_before(); + } + read_digits("d", mode_digits_numeric_separator); + } + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + +// test_cause: +// ["0a", "lex_number", "unexpected_a_after_b", "0", 2] + + return stop_at( + "unexpected_a_after_b", + line, + column, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + char_before(); + return token_create("(number)", snippet); + } + + function lex_regexp() { + +// Regexp +// Lex a regular expression literal. + + let flag; + let mode_regexp_multiline; + let result; + let value; + mode_regexp = true; + + function lex_regexp_bracketed() { + let mode_regexp_range; + +// RegExp +// Match a class. + + char_after("["); + if (char === "^") { + char_after("^"); + } + while (true) { + +// RegExp +// Match a character in a character class. + + switch (char) { + case "": + case "]": + +// test_cause: +// ["aa=/[", "lex_regexp_bracketed", "closer", "", 0] +// ["aa=/[]/", "lex_regexp_bracketed", "closer", "", 0] + + test_cause("closer"); + if (mode_regexp_range) { + +// test_cause: +// ["aa=/[0-]/", "lex_regexp_bracketed", "unexpected_a", "-", 7] + + warn_at("unexpected_a", line, column - 1, "-"); + } + return char_after("]"); + +// PR-362 - Relax regexp-warning against using . +// +// case " ": +// +// // test_cause: +// // ["aa=/[ ]/", "lex_regexp_bracketed", "expected_a_b", " ", 6] +// +// warn_at("expected_a_b", line, column, "\\u0020", " "); +// break; + + case "-": + case "/": + case "[": + case "^": + +// test_cause: +// ["aa=/[-]/", "lex_regexp_bracketed", "expected_a_before_b", "-", 6] +// ["aa=/[.^]/", "lex_regexp_bracketed", "expected_a_before_b", "^", 7] +// ["aa=/[/", "lex_regexp_bracketed", "expected_a_before_b", "/", 6] +// ["aa=/[\\\\/]/", "lex_regexp_bracketed", "expected_a_before_b", "/", 8] +// ["aa=/[\\\\[]/", "lex_regexp_bracketed", "expected_a_before_b", "[", 8] + + warn_at("expected_a_before_b", line, column, "\\", char); + break; + case "\\": + char_after_escape("BbDdSsWw-[]^"); + char_before(); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${/[`]/}`", "lex_regexp_bracketed", "unexpected_a", "`", 6] + + warn_at("unexpected_a", line, column, "`"); + } + break; + } + char_after(); + mode_regexp_range = false; + if (char === "-") { + +// RegExp +// Match a range of subclasses. + + mode_regexp_range = true; + char_after("-"); + } + } + } + + function lex_regexp_group() { + +// RegExp +// Lex sequence of characters in regexp. + + switch (char) { + case "": + warn_at("expected_regexp_factor_a", line, column, char); + break; + case ")": + warn_at("expected_regexp_factor_a", line, column, char); + break; + case "]": + +// test_cause: +// ["/ /", "lex_regexp_group", "expected_regexp_factor_a", "", 3] +// ["aa=/)", "lex_regexp_group", "expected_regexp_factor_a", ")", 5] +// ["aa=/]", "lex_regexp_group", "expected_regexp_factor_a", "]", 5] + + warn_at("expected_regexp_factor_a", line, column, char); + break; + } + while (true) { + switch (char) { + case "": + case ")": + case "/": + case "]": + return; + +// PR-362 - Relax regexp-warning against using . +// +// case " ": +// +// // test_cause: +// // ["aa=/ /", "lex_regexp_group", "expected_a_b", " ", 5] +// +// warn_at("expected_a_b", line, column, "\\s", " "); +// char_after(); +// break; + + case "$": + if (line_source[0] !== "/") { + mode_regexp_multiline = true; + } + char_after(); + break; + case "(": + +// RegExp +// Match a group that starts with left paren. + + char_after("("); + switch (char) { + case ":": + +// test_cause: +// ["aa=/(:)/", "lex_regexp_group", "expected_a_before_b", ":", 6] +// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5] + + warn_at("expected_a_before_b", line, column, "?", ":"); + break; + case "?": + char_after("?"); + switch (char) { + case "!": + +// PR-437 - Add grammar for regexp-named-capture-group. + + case "<": + case "=": + char_after(); + break; + default: + char_after(":"); + } + break; + } + +// RegExp +// Recurse lex_regexp_group(). + + lex_regexp_group(); + char_after(")"); + break; + case "*": + case "+": + case "?": + case "{": + case "}": + +// test_cause: +// ["aa=/+/", "lex_regexp_group", "expected_a_before_b", "+", 5] +// ["aa=/.**/", "lex_regexp_group", "expected_a_before_b", "*", 7] +// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5] +// ["aa=/{/", "lex_regexp_group", "expected_a_before_b", "{", 5] +// ["aa=/}/", "lex_regexp_group", "expected_a_before_b", "}", 5] + + warn_at("expected_a_before_b", line, column, "\\", char); + char_after(); + break; + case "[": + lex_regexp_bracketed(); + break; + case "\\": + +// test_cause: +// ["aa=/\\/", "lex_regexp_group", "escape", "", 0] + + test_cause("escape"); + +// PR-437 - Add grammar for regexp-named-backreference. + + char_after_escape("BbDdSsWw^${}[]():=!.|*+?k"); + break; + case "^": + if (snippet !== "^") { + mode_regexp_multiline = true; + } + char_after(); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${/`/}`", "lex_regexp_group", "unexpected_a", "`", 5] + + warn_at("unexpected_a", line, column, "`"); + } + char_after(); + break; + default: + char_after(); + } + +// RegExp +// Match an optional quantifier. + + switch (char) { + case "*": + case "+": + if (char_after(char) === "?") { + +// test_cause: +// ["aa=/.*?/", "lex_regexp_group", "?", "", 0] +// ["aa=/.+?/", "lex_regexp_group", "?", "", 0] + + test_cause("?"); + char_after("?"); + } + break; + case "?": + if (char_after("?") === "?") { + +// test_cause: +// ["aa=/.??/", "lex_regexp_group", "unexpected_a", "?", 7] + + warn_at("unexpected_a", line, column, char); + char_after("?"); + } + break; + case "{": + if (read_digits("d", mode_digits_empty_string) === 0) { + +// test_cause: +// ["aa=/aa{/", "lex_regexp_group", "expected_a_before_b", ",", 8] + + warn_at("expected_a_before_b", line, column, "0", ","); + } + if (char === ",") { + +// test_cause: +// ["aa=/.{,/", "lex_regexp_group", "comma", "", 0] + + test_cause("comma"); + read_digits("d", mode_digits_empty_string); + } + if (char_after("}") === "?") { + +// test_cause: +// ["aa=/.{0}?/", "lex_regexp_group", "unexpected_a", "?", 9] + + warn_at("unexpected_a", line, column, char); + char_after("?"); + } + break; + } + } + } + +// RegExp +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + char_after(); + if (char === "=") { + +// test_cause: +// ["aa=/=/", "lex_regexp", "expected_a_before_b", "=", 5] + + warn_at("expected_a_before_b", line, column, "\\", "="); + } + lex_regexp_group(); + +// RegExp +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + +// RegExp +// Make sure there is a closing slash. + + value = snippet; + char_after("/"); + +// RegExp +// Create flag. + + flag = empty(); + while ( + +// Regexp +// char is a letter. + + (char >= "a" && char <= "z\uffff") + || (char >= "A" && char <= "Z\uffff") + ) { + +// RegExp +// Process dangling flag letters. + + switch (!flag[char] && char) { + case "g": + break; + case "i": + break; + case "m": + break; + case "u": + break; + case "y": + +// test_cause: +// ["aa=/./gimuy", "lex_regexp", "flag", "", 0] + + test_cause("flag"); + break; + default: + +// test_cause: +// ["aa=/./gg", "lex_regexp", "unexpected_a", "g", 8] +// ["aa=/./z", "lex_regexp", "unexpected_a", "z", 7] + + warn_at("unexpected_a", line, column, char); + } + flag[char] = true; + char_after(); + } + char_before(); + if (char === "/" || char === "*") { + +// test_cause: +// ["aa=/.//", "lex_regexp", "unexpected_a", "/", 3] + + return stop_at("unexpected_a", line, from, char); + } + result = token_create("(regexp)", char); + result.flag = flag; + result.value = value; + if (mode_regexp_multiline && !flag.m) { + +// test_cause: +// ["aa=/$^/", "lex_regexp", "missing_m", "", 7] + + warn_at("missing_m", line, column); + } + return result; + } + + function lex_slash_or_regexp() { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + let the_token; + switch ( + token_prv_expr.identifier + && !token_prv_expr.dot + && token_prv_expr.id + ) { + case "case": + case "delete": + case "in": + case "instanceof": + case "new": + case "typeof": + case "void": + case "yield": + the_token = lex_regexp(); + +// test_cause: +// ["case /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6] +// ["delete /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8] +// ["in /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 4] +// ["instanceof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 12] +// ["new /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 5] +// ["typeof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8] +// ["void /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6] +// ["yield /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 7] + + return stop("unexpected_a", the_token); + case "return": + return lex_regexp(); + } + switch (!token_prv_expr.identifier && token_prv_expr.id.slice(-1)) { + case "!": + case "%": + case "&": + case "*": + case "+": + case "-": + case "/": + case ";": + case "<": + case ">": + case "^": + case "{": + case "|": + case "}": + case "~": + the_token = lex_regexp(); + +// test_cause: +// ["!/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["%/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["&/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["+/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["-/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["0 * /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5] +// ["0 / /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5] +// [";/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["^/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["{/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["|/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["}/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["~/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] + + warn("wrap_regexp", the_token); + return the_token; + case "(": + case ",": + case ":": + case "=": + case "?": + case "[": + +// test_cause: +// ["(/./", "lex_slash_or_regexp", "recurse", "", 0] +// [",/./", "lex_slash_or_regexp", "recurse", "", 0] +// [":/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["=/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["?/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["aa[/./", "lex_slash_or_regexp", "recurse", "", 0] + + test_cause("recurse"); + return lex_regexp(); + } + if (line_source[0] === "=") { + column += 1; + line_source = line_source.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + return token_create(snippet); + } + + function lex_string(quote) { + +// Create a string token. + + let the_token; + if (!option_dict.single && quote === "'") { + +// test_cause: +// ["''", "lex_string", "use_double", "", 1] + + warn_at("use_double", line, column); + } + snippet = ""; + char_after(); + +// Lex/loop through each character in "...". + + while (true) { + switch (char) { + case "": + +// test_cause: +// ["\"", "lex_string", "unclosed_string", "", 1] + + return stop_at("unclosed_string", line, column); + case "\\": + char_after_escape(quote); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${\"`\"}`", "lex_string", "unexpected_a", "`", 5] + + warn_at("unexpected_a", line, column, "`"); + } + char_after("`"); + break; + case quote: + +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + the_token = token_create("(string)", snippet); + the_token.quote = quote; + return the_token; + default: + char_after(); + } + } + } + + function lex_token() { + let match; + +// Lex/loop through each whitespace. + + while (true) { + +// Lex/loop through each blank-line. + + while (!line_source) { + line_source = read_line(); + from = 0; + if (line_source === undefined) { + return ( + mode_mega + +// test_cause: +// ["`${//}`", "lex_token", "unclosed_mega", "", 1] + + ? stop_at("unclosed_mega", line_mega, from_mega) + : line_disable !== undefined + +// test_cause: +// ["/*jslint-disable*/", "lex_token", "unclosed_disable", "", 1] + + ? stop_at("unclosed_disable", line_disable) + : token_create("(end)") + ); + } + } + from = column; + match = line_source.match(jslint_rgx_token); + +// match[1] token +// match[2] whitespace +// match[3] identifier +// match[4] number +// match[5] rest + + if (!match) { + +// test_cause: +// ["#", "lex_token", "unexpected_char_a", "#", 1] + + return stop_at( + "unexpected_char_a", + line, + column, + line_source[0] + ); + } + snippet = match[1]; + column += snippet.length; + line_source = match[5]; + if (!match[2]) { + break; + } + } + +// The token is an identifier. + + if (match[3]) { + return token_create(snippet, undefined, true); + } + +// Create token from number. + + if (match[4]) { + return lex_number(); + } + +// Create token from string "..." or '...'. + + if (snippet === "\"" || snippet === "'") { + return lex_string(snippet); + } + +// Create token from megastring `...`. + + if (snippet === "`") { + return lex_megastring(); + } + +// Create token from comment /*...*/ or //.... + + if (snippet === "/*" || snippet === "//") { + return lex_comment(); + } + +// Create token from slash /. + + if (snippet === "/") { + return lex_slash_or_regexp(); + } + return token_create(snippet); + } + + function option_set_item(key, val) { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + switch (key) { + case "beta": // Enable experimental warnings. + case "bitwise": // Allow bitwise operator. + case "browser": // Assume browser environment. + case "convert": // Allow conversion operator. + case "couch": // Assume CouchDb environment. + case "devel": // Allow console.log() and friends. + case "ecma": // Assume ECMAScript environment. + case "eval": // Allow eval(). + case "fart": // Allow complex fat-arrow. + case "for": // Allow for-statement. + case "getset": // Allow get() and set(). + case "indent2": // Use 2-space indent. + case "long": // Allow long lines. + case "node": // Assume Node.js environment. + case "nomen": // Allow weird property name. + case "single": // Allow single-quote strings. + case "subscript": // Allow identifier in subscript-notation. + case "test_cause": // Test jslint's causes. + case "test_internal_error": // Test jslint's internal-error + // ... handling-ability. + case "this": // Allow 'this'. + case "trace": // Include jslint stack-trace in warnings. + case "unordered": // Allow unordered cases, params, properties, + // ... variables, and exports. + case "variable": // Allow unordered const and let declarations + // ... that are not at top of function-scope. + case "white": // Allow messy whitespace. + option_dict[key] = val; + break; + +// PR-404 - Alias "evil" to jslint-directive "eval" for backwards-compat. + + case "evil": + return option_set_item("eval", val); + +// PR-404 - Alias "nomen" to jslint-directive "name" for backwards-compat. + + case "name": + return option_set_item("nomen", val); + default: + return false; + } + +// Initialize global-variables. + + switch (val && key) { + +// Assign global browser variables to global_dict. +/* +// /\*jslint beta, browser, devel*\/ +console.log(JSON.stringify(Object.keys(window).sort(), undefined, 4)); +*/ + + case "browser": + object_assign_from_list(global_dict, [ + +// Shared with Node.js. + + "AbortController", + // "Buffer", + // "Crypto", + // "CryptoKey", + "Event", + "EventTarget", + "MessageChannel", + "MessageEvent", + "MessagePort", + // "Request", + // "Response", + // "SubtleCrypto", + "TextDecoder", + "TextEncoder", + "URL", + "URLSearchParams", + "WebAssembly", + // "__dirname", + // "__filename", + // "atob", + // "btoa", + // "clearImmediate", + "clearInterval", + "clearTimeout", + // "console", + // "crypto", + // "exports", + // "fetch", + // "global", + // "module", + "performance", + // "process", + "queueMicrotask", + // "require", + // "setImmediate", + "setInterval", + "setTimeout", + +// Web worker only. +// https://github.com/mdn/content/blob/main/files/en-us/web/api +// /workerglobalscope/index.md + + "importScripts", + +// Window. + + "Blob", + // "CharacterData", + // "DocumentType", + // "Element", + // "Event", + "FileReader", + // "FontFace", + "FormData", + "IntersectionObserver", + "MutationObserver", + // "Storage", + // "TextDecoder", + // "TextEncoder", + // "URL", + "Worker", + "XMLHttpRequest", + // "caches", + // "clearInterval", + // "clearTimeout", + "document", + // "event", + "fetch", + // "history", + "indexedDb", + "localStorage", + "location", + // "name", + "navigator", + "postMessage", + // "screen", + "sessionStorage", + // "setInterval", + // "setTimeout", + "structuredClone", + "window" + ], "browser"); + break; + +// https://docs.couchdb.org/en/stable/query-server/javascript.html#javascript + + case "couch": + object_assign_from_list(global_dict, [ + "emit", + "getRow", + "isArray", + "log", + "provides", + "registerType", + "require", + "send", + "start", + "sum", + "toJSON" + ], "CouchDb"); + break; + case "devel": + object_assign_from_list(global_dict, [ + "alert", "confirm", "console", "prompt" + ], "development"); + break; + +// These are the globals that are provided by the language standard. +// Assign global ECMAScript variables to global_dict. +/* +node --input-type=module --eval ' +// /\*jslint beta, node*\/ +import https from "https"; +(async function () { + let dict = {import: true}; + let result = ""; + await new Promise(function (resolve) { + https.get(( + "https://raw.githubusercontent.com/mdn/content/main/files" + + "/en-us/web/javascript/reference/global_objects/index.md" + ), function (res) { + res.on("data", function (chunk) { + result += chunk; + }).on("end", resolve).setEncoding("utf8"); + }); + }); + result.replace(( + /\n- \{\{JSxRef\("(?:Global_Objects\/)?([^"\/]+?)"/g + ), function (ignore, key) { + if (globalThis.hasOwnProperty(key)) { + dict[key] = true; + } + return ""; + }); + console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4)); +}()); +' +*/ + + case "ecma": + object_assign_from_list(global_dict, [ + "AggregateError", + "Array", + "ArrayBuffer", + "Atomics", + "BigInt", + "BigInt64Array", + "BigUint64Array", + "Boolean", + "DataView", + "Date", + "Error", + "EvalError", + "Float32Array", + "Float64Array", + "Function", + "Infinity", + "Int16Array", + "Int32Array", + "Int8Array", + "Intl", + "JSON", + "Map", + "Math", + "NaN", + "Number", + "Object", + "Promise", + "Proxy", + "RangeError", + "ReferenceError", + "Reflect", + "RegExp", + "Set", + "SharedArrayBuffer", + "String", + "Symbol", + "SyntaxError", + "TypeError", + "URIError", + "Uint16Array", + "Uint32Array", + "Uint8Array", + "Uint8ClampedArray", + "WeakMap", + "WeakSet", + "WebAssembly", + "decodeURI", + "decodeURIComponent", + "encodeURI", + "encodeURIComponent", + "eval", + "globalThis", + "import", + "isFinite", + "isNaN", + "parseFloat", + "parseInt", + "undefined" + ], "ECMAScript"); + break; + +// Assign global Node.js variables to global_dict. +/* +node --input-type=module --eval ' +// /\*jslint beta, node*\/ +import moduleHttps from "https"; +(async function () { + let dict = Object.create(null); + let result = ""; + await new Promise(function (resolve) { + moduleHttps.get(( + "https://raw.githubusercontent.com/nodejs/node/v16.x/doc/api" + + "/globals.md" + ), function (res) { + res.on("data", function (chunk) { + result += chunk; + }).on("end", resolve).setEncoding("utf8"); + }); + }); + result.replace(( + /\n(?:\* \[`|## |## Class: )`\w+/g + ), function (match0) { + dict[match0.split("`")[1]] = true; + return ""; + }); + console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4)); +}()); +' +*/ + + case "node": + object_assign_from_list(global_dict, [ + "AbortController", + "Buffer", + // "Crypto", + // "CryptoKey", + "Event", + "EventTarget", + "MessageChannel", + "MessageEvent", + "MessagePort", + // "Request", + // "Response", + // "SubtleCrypto", + "TextDecoder", + "TextEncoder", + "URL", + "URLSearchParams", + "WebAssembly", + "__dirname", + "__filename", + // "atob", + // "btoa", + "clearImmediate", + "clearInterval", + "clearTimeout", + "console", + // "crypto", + "exports", + // "fetch", + "global", + "module", + "performance", + "process", + "queueMicrotask", + "require", + "setImmediate", + "setInterval", + "setTimeout" + ], "Node.js"); + break; + } + return true; + } + + function read_digits(base, mode) { + let digits = line_source.match( + base === "b" + ? jslint_rgx_digits_bits + : base === "o" + ? jslint_rgx_digits_octals + : base === "x" + ? jslint_rgx_digits_hexs + : jslint_rgx_digits_decimals + )[0]; + if ( + (mode !== mode_digits_empty_string && digits.length === 0) + || digits[0] === "_" + ) { + +// test_cause: +// ["0x", "read_digits", "expected_digits_after_a", "0x", 2] +// ["0x_", "read_digits", "expected_digits_after_a", "0x", 2] + + warn_at("expected_digits_after_a", line, column, snippet); + } + +// PR-390 - Add numeric-separator check. + + if (mode === mode_digits_numeric_separator) { + check_numeric_separator(digits, column); + } else if (digits.indexOf("_") >= 0) { + +// test_cause: +// ["\"\\u{1_2}\"", "read_digits", "illegal_num_separator", "", 6] + + warn_at( + "illegal_num_separator", + line, + column + digits.indexOf("_") + 1 + ); + } + column += digits.length; + line_source = line_source.slice(digits.length); + snippet += digits; + char_after(); + return digits.length; + } + + function read_line() { + +// Put the next line of source in line_source. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + if ( + !option_dict.long + && line_whole.length > 80 + && line_disable === undefined + && !state.mode_json + && token_1 + && !mode_regexp + ) { + +// test_cause: +// ["/////////////////////////////////////////////////////////////////////////////////", "read_line", "too_long", "", 1] //jslint-ignore-line + + warn_at("too_long", line); + } + column = 0; + line += 1; + mode_regexp = false; + line_source = undefined; + line_whole = ""; + if (line_list[line] === undefined) { + return line_source; + } + line_source = line_list[line].line_source; + line_whole = line_source; + +// Scan each line for following ignore-directives: +// "/*jslint-disable*/" +// "/*jslint-enable*/" +// "//jslint-ignore-line" + + if (line_source === "/*jslint-disable*/") { + +// test_cause: +// ["/*jslint-disable*/", "read_line", "jslint_disable", "", 0] + + test_cause("jslint_disable"); + line_disable = line; + } else if (line_source === "/*jslint-enable*/") { + if (line_disable === undefined) { + +// test_cause: +// ["/*jslint-enable*/", "read_line", "unopened_enable", "", 1] + + stop_at("unopened_enable", line); + } + line_disable = undefined; + } else if ( + line_source.endsWith(" //jslint-ignore-line") + || line_source.endsWith(" //jslint-quiet") + ) { + +// test_cause: +// ["0 //jslint-ignore-line", "read_line", "jslint_ignore_line", "", 0] + + test_cause("jslint_ignore_line"); + line_list[line].directive_ignore_line = true; + } + if (line_disable !== undefined) { + +// test_cause: +// ["/*jslint-disable*/\n0", "read_line", "line_disable", "", 0] + + test_cause("line_disable"); + line_source = ""; + } + // jslint_rgx_tab + if (line_source.indexOf("\t") >= 0) { + if (!option_dict.white) { + +// test_cause: +// ["\t", "read_line", "use_spaces", "", 1] + + warn_at("use_spaces", line, line_source.indexOf("\t") + 1); + } + line_source = line_source.replace(jslint_rgx_tab, " "); + } + if (!option_dict.white && line_source.endsWith(" ")) { + +// test_cause: +// [" ", "read_line", "unexpected_trailing_space", "", 1] + + warn_at("unexpected_trailing_space", line, line_source.length - 1); + } + return line_source; + } + + function token_create(id, value, identifier) { + +// Create the token object and append it to token_list. + + let the_token = { + from, + id, + identifier: Boolean(identifier), + line, + nr: token_list.length, + thru: column, + value + }; + token_list.push(the_token); + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + mode_directive = false; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option_dict.white. + + if ( + token_prv.line === line + && token_prv.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (token_prv.id === "(comment)" || token_prv.id === "(regexp)") + ) { + +// test_cause: +// ["/**//**/", "token_create", "expected_space_a_b", "(comment)", 5] + + warn( + "expected_space_a_b", + the_token, + artifact(token_prv), + artifact(the_token) + ); + } + if (token_prv.id === "." && id === "(number)") { + +// test_cause: +// [".0", "token_create", "expected_a_before_b", ".", 1] + + warn("expected_a_before_b", token_prv, "0", "."); + } + if (token_prv_expr.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart. +// Farts are now detected by keeping a list of most recent "(" tokens at any +// given depth. When a "=>" token is encountered, the most recent "(" token at +// current depth is marked as a fart. + + switch (id) { + case "(": + paren_backtrack_list[paren_depth] = the_token; + paren_depth += 1; + break; + case ")": + paren_depth -= 1; + break; + case "=>": + if ( + token_prv_expr.id === ")" + && paren_backtrack_list[paren_depth] + ) { + paren_backtrack_list[paren_depth].fart = the_token; + } + break; + } + +// The previous token is used to detect adjacency problems. + + token_prv = the_token; + +// The token_prv_expr token is a previous token that was not a comment. +// The token_prv_expr token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (token_prv.id !== "(comment)") { + token_prv_expr = token_prv; + } + return the_token; + } + +// Init global_dict and option_dict. + + option_set_item("ecma", true); + Object.keys(option_dict).sort().forEach(function (key) { + option_set_item(key, option_dict[key] === true); + }); + object_assign_from_list(global_dict, global_list, "user-defined"); + +// Scan first line for "#!" and ignore it. + + if (line_list[jslint_fudge].line_source.startsWith("#!")) { + line += 1; + state.mode_shebang = true; + } + token_1 = lex_token(); + state.mode_json = token_1.id === "{" || token_1.id === "["; + +// Lex/loop through each token until (end). + + while (true) { + if (lex_token().id === "(end)") { + break; + } + } +} + +function jslint_phase3_parse(state) { + +// PHASE 3. Parse into using the Pratt-parser. + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + + let anon = "anonymous"; // The guessed name for anonymous functions. + let { + artifact, + catch_list, + catch_stack, + export_dict, + function_list, + function_stack, + global_dict, + import_list, + is_equal, + option_dict, + property_dict, + stop, + syntax_dict, + tenure, + test_cause, + token_global, + token_list, + warn, + warn_at + } = state; + let catchage = catch_stack[0]; // The current catch-block. + let functionage = token_global; // The current function. + let mode_var; // "var" if using var; "let" if using let. + let token_ii = 0; // The number of the next token. + let token_now = token_global; // The current token being examined in + // ... the parse. + let token_nxt = token_global; // The next token to be examined in + // ... . + + function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if ( + token_now.identifier + && token_now.id !== "function" + && token_now.id !== "async" + ) { + anon = token_now.id; + } else if ( + token_now.id === "(string)" + && jslint_rgx_identifier.test(token_now.value) + ) { + anon = token_now.value; + } + +// Attempt to match token_nxt with an expected id. + + if (id !== undefined && token_nxt.id !== id) { + return ( + match === undefined + +// test_cause: +// ["{0:0}", "advance", "expected_a_b", "0", 2] + + ? stop("expected_a_b", token_nxt, id, artifact()) + +// test_cause: +// ["{\"aa\":0", "advance", "expected_a_b_from_c_d", "{", 1] + + : stop( + "expected_a_b_from_c_d", + token_nxt, + id, + artifact(match), + match.line, + artifact() + ) + ); + } + +// Promote the tokens, skipping comments. + + token_now = token_nxt; + while (true) { + token_nxt = token_list[token_ii]; + state.token_nxt = token_nxt; + token_ii += 1; + if (token_nxt.id !== "(comment)") { + if (token_nxt.id === "(end)") { + token_ii -= 1; + } + break; + } + if (state.mode_json) { + +// test_cause: +// ["[//]", "advance", "unexpected_a", "(comment)", 2] + + warn("unexpected_a"); + } + } + } + + function assignment(id) { + +// Create an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led_infix = function (left) { + const the_token = token_now; + let right; + the_token.arity = "assignment"; + right = parse_expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "preassign" + || right.arity === "postassign" + ) { + warn("unexpected_a", right); + } + check_mutation(left); + return the_token; + }; + return the_symbol; + } + + function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token_now; + if (special !== "body") { + functionage.statement_prv = the_block; + } + the_block.arity = "statement"; + the_block.body = special === "body"; + +// Top level function bodies may include the "use strict" pragma. + + if ( + special === "body" + && function_stack.length === 1 + && token_nxt.value === "use strict" + ) { + token_nxt.statement = true; + advance("(string)"); + advance(";"); + } + stmts = parse_statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option_dict.devel && special !== "ignore") { + +// test_cause: +// ["function aa(){}", "block", "empty_block", "{", 14] + + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; + } + + function check_left(left, right) { + +// Warn if the left is not one of these: +// ?. +// ?: +// e() +// e.b +// e[b] +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !check_left(left.expression[1]) + && !check_left(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "?." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right || token_nxt); + return false; + } + return true; + } + + function check_mutation(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + +// test_cause: +// ["0=0", "check_mutation", "bad_assignment_a", "0", 1] + + warn("bad_assignment_a", the_thing); + return false; + } + return true; + } + + function check_not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === token_global) { + +// test_cause: +// [" +// while(0){} +// ", "check_not_top_level", "unexpected_at_top_level_a", "while", 1] + + warn("unexpected_at_top_level_a", thing); + } + } + + function check_ordered(type, token_list) { + +// This function will warn if is unordered. + + token_list.reduce(function (aa, token) { + const bb = artifact(token); + if (!option_dict.unordered && aa > bb) { + warn("expected_a_b_before_c_d", token, type, bb, type, aa); + } + return bb; + }, ""); + } + + function check_ordered_case(case_list) { + +// This function will warn if is unordered. + + case_list.filter(noop).map(function (token) { + switch (token.identifier || token.id) { + case "(number)": + return { + order: 1, + token, + type: "number", + value: Number(artifact(token)) + }; + case "(string)": + return { + order: 2, + token, + type: "string", + value: artifact(token) + }; + case true: + return { + order: 3, + token, + type: "identifier", + value: artifact(token) + }; + } + }).reduce(function (aa, bb) { + if ( + +// PR-419 - Hide warning about unordered case-statements behind beta-flag. + + option_dict.beta + && !option_dict.unordered + && aa && bb + && ( + aa.order > bb.order + || (aa.order === bb.order && aa.value > bb.value) + ) + ) { + warn( + "expected_a_b_before_c_d", + bb.token, + `case-${bb.type}`, + bb.value, + `case-${aa.type}`, + aa.value + ); + } + return bb; + }, undefined); + } + + function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = token_nxt; + let the_value; + +// test_cause: +// ["do{}while()", "condition", "", "", 0] +// ["if(){}", "condition", "", "", 0] +// ["while(){}", "condition", "", "", 0] + + test_cause(""); + the_paren.free = true; + advance("("); + the_value = parse_expression(0); + advance(")"); + if (the_value.wrapped === true) { + +// test_cause: +// ["while((0)){}", "condition", "unexpected_a", "(", 6] + + warn("unexpected_a", the_paren); + } + +// Check for anticondition. + + switch (the_value.id) { + case "%": + warn("unexpected_a", the_value); + break; + case "&": + warn("unexpected_a", the_value); + break; + case "(number)": + warn("unexpected_a", the_value); + break; + case "(string)": + warn("unexpected_a", the_value); + break; + case "*": + warn("unexpected_a", the_value); + break; + case "+": + warn("unexpected_a", the_value); + break; + case "-": + warn("unexpected_a", the_value); + break; + case "/": + warn("unexpected_a", the_value); + break; + case "<<": + warn("unexpected_a", the_value); + break; + case ">>": + warn("unexpected_a", the_value); + break; + case ">>>": + warn("unexpected_a", the_value); + break; + case "?": + warn("unexpected_a", the_value); + break; + case "^": + warn("unexpected_a", the_value); + break; + case "typeof": + warn("unexpected_a", the_value); + break; + case "|": + warn("unexpected_a", the_value); + break; + case "~": + +// test_cause: +// ["if(0%0){}", "condition", "unexpected_a", "%", 5] +// ["if(0&0){}", "condition", "unexpected_a", "&", 5] +// ["if(0){}", "condition", "unexpected_a", "0", 4] +// ["if(0*0){}", "condition", "unexpected_a", "*", 5] +// ["if(0+0){}", "condition", "unexpected_a", "+", 5] +// ["if(0-0){}", "condition", "unexpected_a", "-", 5] +// ["if(0/0){}", "condition", "unexpected_a", "/", 5] +// ["if(0<<0){}", "condition", "unexpected_a", "<<", 5] +// ["if(0>>0){}", "condition", "unexpected_a", ">>", 5] +// ["if(0>>>0){}", "condition", "unexpected_a", ">>>", 5] +// ["if(0?0:0){}", "condition", "unexpected_a", "?", 5] +// ["if(0^0){}", "condition", "unexpected_a", "^", 5] +// ["if(0|0){}", "condition", "unexpected_a", "|", 5] +// ["if(\"aa\"){}", "condition", "unexpected_a", "aa", 4] +// ["if(typeof 0){}", "condition", "unexpected_a", "typeof", 4] +// ["if(~0){}", "condition", "unexpected_a", "~", 4] + + warn("unexpected_a", the_value); + break; + } + return the_value; + } + + function constant(id, type, value) { + +// Create a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud_prefix = ( + typeof value === "function" + ? value + : function () { + token_now.constant = true; + if (value !== undefined) { + token_now.value = value; + } + return token_now; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; + } + + function constant_Function() { + if (!option_dict.eval) { + +// test_cause: +// ["Function", "constant_Function", "unexpected_a", "Function", 1] + + warn("unexpected_a", token_now); + } else if (token_nxt.id !== "(") { + +// test_cause: +// [" +// /*jslint eval*/ +// Function +// ", "constant_Function", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "(", artifact()); + } + return token_now; + } + + function constant_arguments() { + +// test_cause: +// ["arguments", "constant_arguments", "unexpected_a", "arguments", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function constant_eval() { + if (!option_dict.eval) { + +// test_cause: +// ["eval", "constant_eval", "unexpected_a", "eval", 1] + + warn("unexpected_a", token_now); + } else if (token_nxt.id !== "(") { + +// test_cause: +// ["/*jslint eval*/\neval", "constant_eval", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "(", artifact()); + } + return token_now; + } + + function constant_ignore() { + +// test_cause: +// ["ignore", "constant_ignore", "unexpected_a", "ignore", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function constant_isInfinite() { + +// test_cause: +// ["isFinite", "constant_isInfinite", "expected_a_b", "isFinite", 1] + + warn("expected_a_b", token_now, "Number.isFinite", "isFinite"); + return token_now; + } + + function constant_isNaN() { + +// test_cause: +// ["isNaN(0)", "constant_isNaN", "number_isNaN", "isNaN", 1] + + warn("number_isNaN", token_now); + return token_now; + } + + function constant_this() { + if (!option_dict.this) { + +// test_cause: +// ["this", "constant_this", "unexpected_a", "this", 1] + + warn("unexpected_a", token_now); + } + return token_now; + } + + function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + let earlier; + let id = name.id; + +// Reserved words may not be enrolled. + + if (syntax_dict[id] !== undefined && id !== "ignore") { + +// test_cause: +// ["let undefined", "enroll", "reserved_a", "undefined", 5] + + warn("reserved_a", name); + return; + } + +// Has the name been enrolled in this context? + + earlier = functionage.context[id] || catchage.context[id]; + if (earlier) { + +// test_cause: +// ["let aa;let aa", "enroll", "redefinition_a_b", "1", 12] + + warn("redefinition_a_b", name, id, earlier.line); + return; + } + +// Has the name been enrolled in an outer context? + + function_stack.forEach(function ({ + context + }) { + earlier = context[id] || earlier; + }); + if (earlier && id === "ignore") { + if (earlier.role === "variable") { + +// test_cause: +// ["let ignore;function aa(ignore){}", "enroll", "redefinition_a_b", "1", 24] + + warn("redefinition_a_b", name, id, earlier.line); + } + } else if ( + earlier + && role !== "parameter" && role !== "function" + && (role !== "exception" || earlier.role !== "exception") + ) { + +// test_cause: +// [" +// function aa(){try{aa();}catch(aa){aa();}} +// ", "enroll", "redefinition_a_b", "1", 31] +// ["function aa(){var aa;}", "enroll", "redefinition_a_b", "1", 19] + + warn("redefinition_a_b", name, id, earlier.line); + } else if ( + option_dict.beta + && global_dict[id] + && role !== "parameter" + ) { + +// test_cause: +// ["let Array", "enroll", "redefinition_global_a_b", "Array", 5] + + warn("redefinition_global_a_b", name, global_dict[id], id); + } + +// Enroll it. + + Object.assign(name, { + dead: true, + init: false, + parent: ( + role === "exception" + ? catchage + : functionage + ), + readonly, + role, + used: 0 + }); + name.parent.context[id] = name; + } + + function infix(bp, id, f) { + +// Create an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led_infix = function (left) { + const the_token = token_now; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, parse_expression(bp)]; + return the_token; + }; + return the_symbol; + } + + function infix_dot(left) { + const the_token = token_now; + let name = token_nxt; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "flat" + && name.id !== "flatMap" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + +// test_cause: +// ["\"\".aa", "check_left", "unexpected_a", ".", 3] + + check_left(left, the_token); + } + if (!name.identifier) { + +// test_cause: +// ["aa.0", "infix_dot", "expected_identifier_a", "0", 4] + + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; + } + + function infix_fart_unwrapped() { + +// test_cause: +// ["aa=>0", "infix_fart_unwrapped", "wrap_fart_parameter", "=>", 3] + + return stop("wrap_fart_parameter", token_now); + } + + function infix_grave(left) { + const the_tick = prefix_tick(); + +// test_cause: +// ["0``", "check_left", "unexpected_a", "`", 2] + + check_left(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; + } + + function infix_lbracket(left) { + const the_token = token_now; + let name; + let the_subscript = parse_expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + name = survey(the_subscript); + +// PR-404 - Add new directive "subscript" to play nice with Google Closure. + + if (!option_dict.subscript && jslint_rgx_identifier.test(name)) { + +// test_cause: +// ["aa[`aa`]", "infix_lbracket", "subscript_a", "aa", 4] + + warn("subscript_a", the_subscript, name); + } + } + +// test_cause: +// ["0[0]", "check_left", "unexpected_a", "[", 2] + + check_left(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; + } + + function infix_lparen(left) { + const the_paren = token_now; + let ellipsis; + let the_argument; + if (left.id !== "function") { + +// test_cause: +// ["(0?0:0)()", "check_left", "unexpected_a", "(", 8] +// ["0()", "check_left", "unexpected_a", "(", 2] + + check_left(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (token_nxt.id !== ")") { + +// Parse/loop through each token in expression (...). + + while (true) { + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = parse_expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + +// test_cause: +// ["aa(0)", "infix_lparen", "free", "", 0] + + test_cause("free"); + the_paren.free = true; + if (the_argument.wrapped === true) { + +// test_cause: +// ["aa((0))", "infix_lparen", "unexpected_a", "(", 3] + + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + +// test_cause: +// ["aa()", "infix_lparen", "not_free", "", 0] +// ["aa(0,0)", "infix_lparen", "not_free", "", 0] + + test_cause("not_free"); + the_paren.free = false; + } + return the_paren; + } + + function infix_option_chain(left) { + const the_token = token_now; + let name = token_nxt; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + +// test_cause: +// ["(0+0)?.0", "infix_option_chain", "check_left", "", 0] + + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + test_cause("check_left"); + +// test_cause: +// ["(/./)?.0", "check_left", "unexpected_a", "?.", 6] +// ["\"aa\"?.0", "check_left", "unexpected_a", "?.", 5] +// ["aa=[]?.aa", "check_left", "unexpected_a", "?.", 6] + + check_left(left, the_token); + } + +// Issue #468 - Fix optional dynamic-property/function-call not recognized. + + if (name.id === "[" || name.id === "(") { + test_cause("dyn_prop_or_call"); + +// test_cause: +// ["aa?.(bb)", "infix_option_chain", "dyn_prop_or_call", "", 0] +// ["aa?.[bb]", "infix_option_chain", "dyn_prop_or_call", "", 0] + + return left; + } + if (!name.identifier) { + +// test_cause: +// ["aa?.0", "infix_option_chain", "expected_identifier_a", "0", 5] + + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; + } + + function infixr(bp, id) { + +// Create a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led_infix = function parse_infixr_led(left) { + const the_token = token_now; + +// test_cause: +// ["0**0", "parse_infixr_led", "led_infix", "", 0] + + test_cause("led_infix"); + the_token.arity = "binary"; + the_token.expression = [left, parse_expression(bp - 1)]; + return the_token; + }; + return the_symbol; + } + + function parse_expression(rbp, initial) { + +// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it +// is looking for ad hoc lint patterns. We add .fud_stmt to Pratt's model, which +// is like .nud_prefix except that it is only used on the first token of a +// statement. Having .fud_stmt makes it much easier to define statement-oriented +// languages like JavaScript. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// .nud_prefix Null denotation. The prefix handler. +// .fud_stmt First null denotation. The statement handler. +// .led_infix Left denotation. The infix/postfix handler. +// lbp Left binding power of infix operator. It tells us how strongly +// the operator binds to the argument at its left. +// rbp Right binding power. + +// It processes a nud_prefix (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop (it +// consumes tokens until it meets a token whose lbp <= rbp). Specifically, it +// means that it collects all tokens that bind together before returning to the +// operator that called it. It returns the expression's parse tree. + +// For example, "3 + 1 * 2 * 4 + 5" +// parses into +// { +// "id": "+", +// "expression": [ +// { +// "id": "+", +// "expression": [ +// { +// "id": "(number)", +// "value": "3" +// }, +// { +// "id": "*", +// "expression": [ +// { +// "id": "*", +// "expression": [ +// { +// "id": "(number)", +// "value": "1" +// }, +// { +// "id": "(number)", +// "value": "2" +// } +// ] +// }, +// { +// "id": "(number)", +// "value": "4" +// } +// ] +// } +// ] +// }, +// { +// "id": "(number)", +// "value": "5" +// } +// ] +// } + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement. + + if (!initial) { + advance(); + } + the_symbol = syntax_dict[token_now.id]; + if (the_symbol !== undefined && the_symbol.nud_prefix !== undefined) { + +// test_cause: +// ["0", "parse_expression", "symbol", "", 0] + + test_cause("symbol"); + left = the_symbol.nud_prefix(); + } else if (token_now.identifier) { + +// test_cause: +// ["aa", "parse_expression", "identifier", "", 0] + + test_cause("identifier"); + left = token_now; + left.arity = "variable"; + } else { + +// test_cause: +// ["!", "parse_expression", "unexpected_a", "(end)", 1] +// ["/./", "parse_expression", "unexpected_a", "/", 1] +// ["let aa=`${}`;", "parse_expression", "unexpected_a", "}", 11] + + return stop("unexpected_a", token_now); + } + +// Parse/loop through each symbol in expression. + + while (true) { + the_symbol = syntax_dict[token_nxt.id]; + if ( + the_symbol === undefined + || the_symbol.led_infix === undefined + || the_symbol.lbp <= rbp + ) { + break; + } + advance(); + left = the_symbol.led_infix(left); + } + return left; + } + + function parse_fart(the_fart) { + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + Object.assign(the_fart, { + arity: "binary", + context: empty(), + finally: 0, + level: functionage.level + 1, + loop: 0, + name: anon, + switch: 0, + try: 0 + }); + +// PR-384 - Relax warning "function_in_loop". +// +// if (functionage.loop > 0) { + +// // test_cause: +// // ["while(0){aa.map(()=>0);}", "parse_fart", "function_in_loop", "=>", 19] +// +// warn("function_in_loop", the_fart); +// } + +// Push the current function context and establish a new one. + + function_list.push(the_fart); + function_stack.push(functionage); + functionage = the_fart; + +// Parse the parameter list. + + prefix_function_parameter(the_fart); + advance("=>"); + +// The function's body is a block. + + if (token_nxt.id === "{") { + if (!option_dict.fart) { + +// test_cause: +// ["()=>{}", "parse_fart", "use_function_not_fart", "=>", 3] + + warn("use_function_not_fart", the_fart); + } + the_fart.block = block("body"); + } else if ( + syntax_dict[token_nxt.id] !== undefined + && syntax_dict[token_nxt.id].fud_stmt !== undefined + ) { + +// PR-384 - Bugfix - Fixes issue #379 - warn against naked-statement in fart. + +// test_cause: +// ["()=>delete aa", "parse_fart", "unexpected_a_after_b", "=>", 5] + + stop("unexpected_a_after_b", token_nxt, token_nxt.id, "=>"); + +// The function's body is an expression. + + } else { + the_fart.expression = parse_expression(0); + } + +// Restore the previous context. + + functionage = function_stack.pop(); + return the_fart; + } + + function parse_json() { + let container; + let is_dup; + let name; + let negative; + switch (token_nxt.id) { + case "(number)": + if (!jslint_rgx_json_number.test(token_nxt.value)) { + +// test_cause: +// ["[-.0]", "parse_json", "unexpected_a", ".", 3] +// ["[-0x0]", "parse_json", "unexpected_a", "0x0", 3] +// ["[.0]", "parse_json", "unexpected_a", ".", 2] +// ["[0x0]", "parse_json", "unexpected_a", "0x0", 2] + + warn("unexpected_a"); + } + advance("(number)"); + return token_now; + case "(string)": + if (token_nxt.quote !== "\"") { + +// test_cause: +// ["['']", "parse_json", "unexpected_a", "'", 2] + + warn("unexpected_a", token_nxt, token_nxt.quote); + } + advance("(string)"); + return token_now; + case "-": + negative = token_nxt; + negative.arity = "unary"; + advance("-"); + +// Recurse parse_json(). + + negative.expression = parse_json(); + return negative; + case "[": + +// test_cause: +// ["[]", "parse_json", "bracket", "", 0] + + test_cause("bracket"); + container = token_nxt; + container.expression = []; + advance("["); + if (token_nxt.id !== "]") { + while (true) { + +// Recurse parse_json(). + + container.expression.push(parse_json()); + if (token_nxt.id !== ",") { + +// test_cause: +// ["[0,0]", "parse_json", "comma", "", 0] + + test_cause("comma"); + break; + } + advance(","); + } + } + advance("]", container); + return container; + case "false": + case "null": + case "true": + +// test_cause: +// ["[false]", "parse_json", "constant", "", 0] +// ["[null]", "parse_json", "constant", "", 0] +// ["[true]", "parse_json", "constant", "", 0] + + test_cause("constant"); + advance(); + return token_now; + case "{": + +// test_cause: +// ["{}", "parse_json", "brace", "", 0] + + test_cause("brace"); + container = token_nxt; + +// Explicit empty-object required to detect "__proto__". + + is_dup = empty(); + container.expression = []; + advance("{"); + if (token_nxt.id !== "}") { + +// JSON +// Parse/loop through each property in {...}. + + while (true) { + if (token_nxt.quote !== "\"") { + +// test_cause: +// ["{0:0}", "parse_json", "unexpected_a", "0", 2] + + warn( + "unexpected_a", + token_nxt, + token_nxt.quote + ); + } + name = token_nxt; + advance("(string)"); + if (is_dup[token_now.value] !== undefined) { + +// test_cause: +// ["{\"aa\":0,\"aa\":0}", "parse_json", "duplicate_a", "aa", 9] + + warn("duplicate_a", token_now); + } else if (token_now.value === "__proto__") { + +// test_cause: +// ["{\"__proto__\":0}", "parse_json", "weird_property_a", "__proto__", 2] + + warn("weird_property_a", token_now); + } else { + is_dup[token_now.value] = token_now; + } + advance(":"); + container.expression.push( + +// Recurse parse_json(). + + Object.assign(parse_json(), { + label: name + }) + ); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance("}", container); + return container; + default: + +// test_cause: +// ["[undefined]", "parse_json", "unexpected_a", "undefined", 2] + + stop("unexpected_a"); + } + } + + function parse_statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token_now.identifier && token_nxt.id === ":") { + the_label = token_now; + if (the_label.id === "ignore") { + +// test_cause: +// ["ignore:", "parse_statement", "unexpected_a", "ignore", 1] + + warn("unexpected_a", the_label); + } + advance(":"); + switch (token_nxt.id) { + case "do": + case "for": + case "switch": + case "while": + +// test_cause: +// ["aa:do{}", "parse_statement", "the_statement_label", "do", 0] +// ["aa:for{}", "parse_statement", "the_statement_label", "for", 0] +// ["aa:switch{}", "parse_statement", "the_statement_label", "switch", 0] +// ["aa:while{}", "parse_statement", "the_statement_label", "while", 0] + + test_cause("the_statement_label", token_nxt.id); + enroll(the_label, "label", true); + the_label.dead = false; + the_label.init = true; + the_statement = parse_statement(); + functionage.statement_prv = the_statement; + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + +// test_cause: +// ["aa:", "parse_statement", "unexpected_label_a", "aa", 1] + + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token_now; + first.statement = true; + the_symbol = syntax_dict[first.id]; + if ( + the_symbol !== undefined + && the_symbol.fud_stmt !== undefined + +// PR-318 - Bugfix - Fixes issues #316, #317 - dynamic-import(). + + && !(the_symbol.id === "import" && token_nxt.id === "(") + ) { + the_symbol.disrupt = false; + the_symbol.statement = true; + token_now.arity = "statement"; + the_statement = the_symbol.fud_stmt(); + functionage.statement_prv = the_statement; + } else { + +// It is an expression statement. + + the_statement = parse_expression(0, true); + functionage.statement_prv = the_statement; + if (the_statement.wrapped && the_statement.id !== "(") { + +// test_cause: +// ["(0)", "parse_statement", "unexpected_a", "(", 1] + + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; + } + + function parse_statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const statement_list = []; + let a_statement; + let disrupt = false; + +// Parse/loop each statement until a statement-terminator is reached. + + while (true) { + switch (token_nxt.id) { + case "(end)": + case "case": + case "default": + case "else": + case "}": + +// test_cause: +// [";", "parse_statements", "closer", "", 0] +// ["case", "parse_statements", "closer", "", 0] +// ["default", "parse_statements", "closer", "", 0] +// ["else", "parse_statements", "closer", "", 0] +// ["}", "parse_statements", "closer", "", 0] + + test_cause("closer"); + return statement_list; + } + a_statement = parse_statement(); + statement_list.push(a_statement); + if (disrupt) { + +// test_cause: +// ["while(0){break;0;}", "parse_statements", "unreachable_a", "0", 16] + + warn("unreachable_a", a_statement); + } + disrupt = a_statement.disrupt; + } + } + + function postassign(id) { + +// Create one of the postassign operators. + + const the_symbol = symbol(id, 150); + the_symbol.led_infix = function (left) { + token_now.expression = left; + token_now.arity = "postassign"; + check_mutation(token_now.expression); + return token_now; + }; + return the_symbol; + } + + function preassign(id) { + +// Create one of the preassign operators. + + const the_symbol = symbol(id); + the_symbol.nud_prefix = function () { + const the_token = token_now; + the_token.arity = "preassign"; + the_token.expression = parse_expression(150); + check_mutation(the_token.expression); + return the_token; + }; + return the_symbol; + } + + function prefix(id, f) { + +// Create a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud_prefix = function () { + const the_token = token_now; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = parse_expression(150); + return the_token; + }; + return the_symbol; + } + + function prefix_assign_divide() { + +// test_cause: +// ["/=", "prefix_assign_divide", "expected_a_b", "/=", 1] + + stop("expected_a_b", token_now, "/\\=", "/="); + } + + function prefix_async() { + let the_async = token_now; + let the_function; + token_nxt.arity = the_async.arity; + +// PR-414 - Parse async fart. + + if (token_nxt.fart) { + advance("("); + the_function = Object.assign(token_now.fart, { + async: 1 + }); + if (!option_dict.fart) { + +// test_cause: +// ["async()=>0", "prefix_async", "use_function_not_fart", "=>", 8] + + warn("use_function_not_fart", the_function); + } + prefix_lparen(); + +// Parse async function. + + } else { + advance("function"); + the_function = Object.assign(token_now, { + async: 1 + }); + prefix_function(); + } + if (the_function.async === 1) { + +// test_cause: +// [" +// async function aa(){} +// ", "prefix_async", "missing_await_statement", "function", 7] + + warn("missing_await_statement", the_function); + } + return the_function; + } + + function prefix_await() { + const the_await = token_now; + +// PR-370 - Add top-level-await support. + + if (functionage.async === 0 && functionage !== token_global) { + +// test_cause: +// ["function aa(){aa=await 0;}", "prefix_await", "unexpected_a", "await", 18] +// ["function aa(){await 0;}", "prefix_await", "unexpected_a", "await", 15] + + warn("unexpected_a", the_await); + } else { + functionage.async += 1; + } + if (the_await.arity === "statement") { + +// PR-405 - Bugfix - fix expression after "await" mis-identified as statement. + + the_await.expression = parse_expression(150); + semicolon(); + } else { + the_await.expression = parse_expression(150); + } + return the_await; + } + + function prefix_fart() { + +// test_cause: +// ["=>0", "prefix_fart", "expected_a_before_b", "=>", 1] + + return stop("expected_a_before_b", token_now, "()", "=>"); + } + + function prefix_function(the_function) { + let name = the_function && the_function.name; + if (the_function === undefined) { + the_function = token_now; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!token_nxt.identifier) { + +// test_cause: +// ["function(){}", "prefix_function", "expected_identifier_a", "(", 9] +// ["function*aa(){}", "prefix_function", "expected_identifier_a", "*", 9] + + return stop("expected_identifier_a"); + } + name = token_nxt; + enroll(name, "variable", true); + the_function.name = Object.assign(name, { + calls: empty(), + +// PR-331 - Bugfix - Fixes issue #272 - function hoisting not allowed. + + dead: false, + init: true + }); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + the_function.name = anon; + if (token_nxt.identifier) { + name = token_nxt; + the_function.name = name; + advance(); + } + } + } + +// Probably deadcode. +// if (mode_mega) { +// warn("unexpected_a", the_function); +// } +// jslint_assert(!mode_mega, `Expected !mode_mega.`); + +// PR-378 - Relax warning "function_in_loop". +// +// // Don't create functions in loops. It is inefficient, and it can lead to +// // scoping errors. +// +// if (functionage.loop > 0) { +// +// // test_cause: +// // [" +// // while(0){aa.map(function(){});} +// // ", "prefix_function", "function_in_loop", "function", 17] +// +// warn("function_in_loop", the_function); +// } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + Object.assign(the_function, { + async: the_function.async || 0, + context: empty(), + finally: 0, + level: functionage.level + 1, + loop: 0, + statement_prv: undefined, + switch: 0, + try: 0 + }); + if (the_function.arity !== "statement" && typeof name === "object") { + +// test_cause: +// ["let aa=function bb(){return;};", "prefix_function", "expression", "bb", 0] + + test_cause("expression", name.id); + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// PR-334 - Bugfix - fix function-redefinition not warned inside function-call. +// Push the current function context and establish a new one. + + function_list.push(the_function); + function_stack.push(functionage); + functionage = the_function; + +// Parse the parameter list. + + advance("("); + token_now.arity = "function"; + prefix_function_parameter(the_function); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && token_nxt.line === token_now.line + ) { + +// test_cause: +// ["function aa(){}0", "prefix_function", "unexpected_a", "0", 16] + + return stop("unexpected_a"); + } + if ( + token_nxt.id === "." + || token_nxt.id === "?." + +// PR-459 - Allow destructuring-assignment after function-definition. + + // || token_nxt.id === "[" + ) { + +// test_cause: +// ["function aa(){}\n.aa", "prefix_function", "unexpected_a", ".", 1] +// ["function aa(){}\n?.aa", "prefix_function", "unexpected_a", "?.", 1] + + warn("unexpected_a"); + } + +// Check functions are ordered. + + check_ordered( + "function", + function_list.slice( + function_list.indexOf(the_function) + 1 + ).map(function ({ + level, + name + }) { + return (level === the_function.level + 1) && name; + }).filter(function (name) { + return option_dict.beta && name && name.id; + }) + ); + +// Restore the previous context. + + functionage = function_stack.pop(); + return the_function; + } + + function prefix_function_parameter(the_function) { + +// This function will parse input at beginning of + + let optional; + let parameters = []; + let signature = ["("]; + let subparam; + function param_enroll(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + +// test_cause: +// ["([aa])=>0", "param_enroll", "use_function_not_fart", "=>", 7] +// ["({aa})=>0", "param_enroll", "use_function_not_fart", "=>", 7] + + if (the_function.id === "=>" && !option_dict.fart) { + warn("use_function_not_fart", the_function); + } + +// Recurse param_enroll(). + + name.names.forEach(param_enroll); + } + } + function param_parse() { + let ellipsis = false; + let param; + if (token_nxt.id === "{") { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,{}){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + param = token_nxt; + param.names = []; + advance("{"); + signature.push("{"); + while (true) { + subparam = token_nxt; + if (!subparam.identifier) { + +// test_cause: +// ["function aa(aa=0,{}){}", "param_parse", "expected_identifier_a", "}", 19] +// ["function aa({0}){}", "param_parse", "expected_identifier_a", "0", 14] + + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (token_nxt.id === ":") { + advance(":"); + advance(); + token_now.label = subparam; + subparam = token_now; + if (!subparam.identifier) { + +// test_cause: +// ["function aa({aa:0}){}", "param_parse", "expected_identifier_a", "}", 18] + + return stop( + "expected_identifier_a", + token_nxt + ); + } + } + +// test_cause: +// ["function aa({aa=aa},aa){}", "param_parse", "equal", "", 0] + + test_cause("equal"); + if (token_nxt.id === "=") { + advance("="); + subparam.expression = parse_expression(); + param.open = true; + } + param.names.push(subparam); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + } else { + break; + } + } + parameters.push(param); + +// test_cause: +// [" +// function aa({bb,aa}){} +// ", "check_ordered", "expected_a_b_before_c_d", "aa", 17] + + check_ordered("parameter", param.names); + advance("}"); + signature.push("}"); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } else if (token_nxt.id === "[") { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,[]){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + param = token_nxt; + param.names = []; + advance("["); + signature.push("[]"); + while (true) { + subparam = token_nxt; + if (!subparam.identifier) { + +// test_cause: +// ["function aa(aa=0,[]){}", "param_parse", "expected_identifier_a", "]", 19] + + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + +// test_cause: +// ["function aa([aa=aa],aa){}", "param_parse", "id", "", 0] + + test_cause("id"); + if (token_nxt.id === "=") { + advance("="); + subparam.expression = parse_expression(); + param.open = true; + } + if (token_nxt.id === ",") { + advance(","); + } else { + break; + } + } + parameters.push(param); + advance("]"); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } else { + if (token_nxt.id === "...") { + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,...){}", "param_parse", "required_a_optional_b", "aa", 21] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + } + if (!token_nxt.identifier) { + +// test_cause: +// ["function aa(0){}", "param_parse", "expected_identifier_a", "0", 13] + + return stop("expected_identifier_a"); + } + param = token_nxt; + parameters.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (token_nxt.id === "=") { + optional = param; + advance("="); + param.expression = parse_expression(0); + } else { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,bb){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } + } + } + +// test_cause: +// ["function aa(){}", "prefix_function_parameter", "opener", "(", 0] + + test_cause("opener", token_now.id); + token_now.free = false; + if (token_nxt.id !== ")" && token_nxt.id !== "(end)") { + param_parse(); + } + advance(")"); + signature.push(")"); + parameters.forEach(param_enroll); + the_function.parameters = parameters; + the_function.signature = signature.join(""); + } + + function prefix_lbrace() { + const seen = empty(); + const the_brace = token_now; + let extra; + let full; + let id; + let name; + let the_colon; + let value; + the_brace.expression = []; + if (token_nxt.id !== "}") { + +// Parse/loop through each property in {...}. + + while (true) { + name = token_nxt; + advance(); + if ( + (name.id === "get" || name.id === "set") + && token_nxt.identifier + ) { + if (!option_dict.getset) { + +// test_cause: +// ["aa={get aa(){}}", "prefix_lbrace", "unexpected_a", "get", 5] + + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + token_nxt.id; + name = token_nxt; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + +// test_cause: +// ["aa={get aa(){},get aa(){}}", "prefix_lbrace", "duplicate_a", "aa", 20] + + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else if (name.id === "`") { + +// test_cause: +// ["aa={`aa`:0}", "prefix_lbrace", "unexpected_a", "`", 5] + + stop("unexpected_a", name); + + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + +// test_cause: +// ["aa={aa,aa}", "prefix_lbrace", "duplicate_a", "aa", 8] + + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (token_nxt.id === "}" || token_nxt.id === ",") { + if (typeof extra === "string") { + +// test_cause: +// ["aa={get aa}", "prefix_lbrace", "closer", "", 0] + + test_cause("closer"); + advance("("); + } + value = parse_expression(Infinity, true); + } else if (token_nxt.id === "(") { + +// test_cause: +// ["aa={aa()}", "prefix_lbrace", "paren", "", 0] +// ["aa={get aa(){}}", "prefix_lbrace", "paren", "", 0] + + test_cause("paren"); + value = prefix_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + +// test_cause: +// ["aa={get aa.aa}", "prefix_lbrace", "paren", "", 0] + + test_cause("paren"); + advance("("); + } + the_colon = token_nxt; + advance(":"); + value = parse_expression(0); + if ( + value.id === name.id + && value.id !== "function" + ) { + +// test_cause: +// ["aa={aa:aa}", "prefix_lbrace", "unexpected_a", ": aa", 7] + + warn("unexpected_a", the_colon, ": " + name.id); + } + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + +// test_cause: +// ["aa={\"aa\":0}", "prefix_lbrace", "colon", "", 0] + + test_cause("colon"); + advance(":"); + value = parse_expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (token_nxt.id !== ",") { + break; + } + +// test_cause: +// ["aa={\"aa\":0,\"bb\":0}", "prefix_lbrace", "comma", "", 0] + + test_cause("comma"); + advance(","); + if (token_nxt.id === "}") { + +// test_cause: +// ["let aa={aa:0,}", "prefix_lbrace", "unexpected_a", ",", 13] + + warn("unexpected_a", token_now); + break; + } + } + } + +// test_cause: +// ["aa={bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8] + + check_ordered( + "property", + the_brace.expression.map(function ({ + label + }) { + return label; + }) + ); + advance("}"); + return the_brace; + } + + function prefix_lbracket() { + const the_token = token_now; + let element; + let ellipsis; + the_token.expression = []; + if (token_nxt.id !== "]") { + +// Parse/loop through each element in [...]. + + while (true) { + ellipsis = false; + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + element = parse_expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (token_nxt.id !== ",") { + break; + } + advance(","); + if (token_nxt.id === "]") { + +// test_cause: +// ["let aa=[0,]", "prefix_lbracket", "unexpected_a", ",", 10] + + warn("unexpected_a", token_now); + break; + } + } + } + advance("]"); + return the_token; + } + + function prefix_lparen() { + let the_paren = token_now; + let the_value; + +// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart. + + if (token_now.fart) { + return parse_fart(token_now.fart); + } + +// test_cause: +// ["(0)", "prefix_lparen", "expr", "", 0] + + test_cause("expr"); + the_paren.free = true; + the_value = parse_expression(0); + if (the_value.wrapped === true) { + +// test_cause: +// ["((0))", "prefix_lparen", "unexpected_a", "(", 1] + + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + return the_value; + } + + function prefix_new() { + const the_new = token_now; + let right; + right = parse_expression(160); + if (token_nxt.id !== "(") { + +// test_cause: +// ["new aa", "prefix_new", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "()", artifact()); + } + the_new.expression = right; + return the_new; + } + + function prefix_tick() { + const the_tick = token_now; + the_tick.value = []; + the_tick.expression = []; + if (token_nxt.id !== "`") { + +// Parse/loop through each token in `${...}`. + + while (true) { + advance("(string)"); + the_tick.value.push(token_now); + if (token_nxt.id !== "${") { + break; + } + advance("${"); + +// test_cause: +// ["let aa=`${}`;", "prefix_tick", "${", "", 0] + + test_cause("${"); + the_tick.expression.push(parse_expression(0)); + advance("}"); + } + } + advance("`"); + return the_tick; + } + + function prefix_void() { + const the_void = token_now; + +// test_cause: +// ["void 0", "prefix_void", "unexpected_a", "void", 1] +// ["void", "prefix_void", "unexpected_a", "void", 1] + + warn("unexpected_a", the_void); + the_void.expression = parse_expression(0); + return the_void; + } + + function semicolon() { + +// Try to match a semicolon. + + if (token_nxt.id === ";") { + advance(";"); + } else { + +// test_cause: +// ["0", "semicolon", "expected_a_b", "(end)", 1] + + warn_at( + "expected_a_b", + token_now.line, + token_now.thru + 1, + ";", + artifact() + ); + } + anon = "anonymous"; + } + + function stmt(id, fud_stmt) { + +// Create a statement. + + const the_symbol = symbol(id); + the_symbol.fud_stmt = fud_stmt; + return the_symbol; + } + + function stmt_break() { + const the_break = token_now; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + +// test_cause: +// ["break", "stmt_break", "unexpected_a", "break", 1] + + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (token_nxt.identifier && token_now.line === token_nxt.line) { + the_label = functionage.context[token_nxt.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + if (the_label !== undefined && the_label.dead) { + +// test_cause: +// ["aa:{function aa(aa){break aa;}}", "stmt_break", "out_of_scope_a", "aa", 27] + + warn("out_of_scope_a"); + } else { + +// test_cause: +// ["aa:{break aa;}", "stmt_break", "not_label_a", "aa", 11] + + warn("not_label_a"); + } + } else { + the_label.used += 1; + } + the_break.label = token_nxt; + advance(); + } + advance(";"); + return the_break; + } + + function stmt_continue() { + const the_continue = token_now; + if (functionage.loop < 1 || functionage.finally > 0) { + +// test_cause: +// ["continue", "stmt_continue", "unexpected_a", "continue", 1] +// [" +// function aa(){while(0){try{}finally{continue}}} +// ", "stmt_continue", "unexpected_a", "continue", 37] + + warn("unexpected_a", the_continue); + } + check_not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; + } + + function stmt_debugger() { + const the_debug = token_now; + if (!option_dict.devel) { + +// test_cause: +// ["debugger", "stmt_debugger", "unexpected_a", "debugger", 1] + + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; + } + + function stmt_delete() { + const the_token = token_now; + const the_value = parse_expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + +// test_cause: +// ["delete 0", "stmt_delete", "expected_a_b", "0", 8] + + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; + } + + function stmt_do() { + const the_do = token_now; + check_not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + +// test_cause: +// ["function aa(){do{break;}while(0)}", "stmt_do", "weird_loop", "do", 15] + + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; + } + + function stmt_export() { + let export_list = []; + let the_export = token_now; + let the_id; + let the_name; + let the_thing; + + the_export.expression = []; + if (token_nxt.id === "default") { + if (export_dict.default !== undefined) { + +// test_cause: +// [" +// export default 0;export default 0 +// ", "stmt_export", "duplicate_a", "default", 25] + + warn("duplicate_a"); + } + advance("default"); + the_thing = parse_expression(0); + if ( + the_thing.id !== "(" + || the_thing.expression[0].id !== "." + || the_thing.expression[0].expression.id !== "Object" + || the_thing.expression[0].name.id !== "freeze" + ) { + +// test_cause: +// ["export default {}", "stmt_export", "freeze_exports", "{", 16] + + warn("freeze_exports", the_thing); + +// PR-301 - Bugfix - Fixes issues #282 - optional-semicolon. + + } else { + +// test_cause: +// [" +// export default Object.freeze({}) +// ", "semicolon", "expected_a_b", "(end)", 32] + + semicolon(); + } + export_dict.default = the_thing; + the_export.expression.push(the_thing); + } else { + +// PR-439 - Add grammar for "export async function ...". + + if (token_nxt.id === "function" || token_nxt.id === "async") { + +// test_cause: +// ["export async function aa(){}", "stmt_export", "freeze_exports", "async", 8] +// ["export function aa(){}", "stmt_export", "freeze_exports", "function", 8] + + warn("freeze_exports"); + the_thing = parse_statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (export_dict[the_id] !== undefined) { + +// test_cause: +// [" +// let aa;export{aa};export function aa(){} +// ", "stmt_export", "duplicate_a", "aa", 35] + + warn("duplicate_a", the_name); + } + export_dict[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + token_nxt.id === "var" + || token_nxt.id === "let" + || token_nxt.id === "const" + ) { + +// test_cause: +// ["export const", "stmt_export", "unexpected_a", "const", 8] +// ["export let", "stmt_export", "unexpected_a", "let", 8] +// ["export var", "stmt_export", "unexpected_a", "var", 8] + + warn("unexpected_a"); + parse_statement(); + } else if (token_nxt.id === "{") { + +// test_cause: +// ["export {}", "stmt_export", "advance{", "", 0] + + test_cause("advance{"); + advance("{"); + while (true) { + if (!token_nxt.identifier) { + +// test_cause: +// ["export {}", "stmt_export", "expected_identifier_a", "}", 9] + + stop("expected_identifier_a"); + } + the_id = token_nxt.id; + export_list.push(token_nxt); + the_name = token_global.context[the_id]; + if (the_name === undefined) { + +// test_cause: +// ["export {aa}", "stmt_export", "unexpected_a", "aa", 9] + + warn("unexpected_a"); + } else { + the_name.used += 1; + if (export_dict[the_id] !== undefined) { + +// test_cause: +// ["let aa;export{aa,aa}", "stmt_export", "duplicate_a", "aa", 18] + + warn("duplicate_a"); + } + export_dict[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + if (token_nxt.id === ",") { + advance(","); + } else { + break; + } + } + +// PR-439 - Check exported properties are ordered. + +// test_cause: +// ["export {bb, aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 13] + + check_ordered("export", export_list); + advance("}"); + semicolon(); + } else { + +// test_cause: +// ["export", "stmt_export", "unexpected_a", "(end)", 1] + + stop("unexpected_a"); + } + } + state.mode_module = true; + return the_export; + } + + function stmt_for() { + let first; + let the_for = token_now; + if (!option_dict.for) { + +// test_cause: +// ["for", "stmt_for", "unexpected_a", "for", 1] + + warn("unexpected_a", the_for); + } + check_not_top_level(the_for); + functionage.loop += 1; + advance("("); + token_now.free = true; + if (token_nxt.id === ";") { + +// test_cause: +// ["for(;;){}", "stmt_for", "expected_a_b", "for (;", 1] + + return stop("expected_a_b", the_for, "while (", "for (;"); + } + switch (token_nxt.id) { + case "const": + case "let": + case "var": + +// test_cause: +// ["for(const aa in aa){}", "stmt_for", "unexpected_a", "const", 5] + + return stop("unexpected_a"); + } + first = parse_expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + +// test_cause: +// ["for(0 in aa){}", "stmt_for", "bad_assignment_a", "0", 5] + + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = parse_expression(0); + advance(";"); + the_for.inc = parse_expression(0); + if (the_for.inc.id === "++") { + +// test_cause: +// ["for(aa;aa;aa++){}", "stmt_for", "expected_a_b", "++", 13] + + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + +// test_cause: +// [" +// /*jslint for*/ +// function aa(bb,cc){for(0;0;0){break;}} +// ", "stmt_for", "weird_loop", "for", 20] + + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; + } + + function stmt_if() { + const the_if = token_now; + let the_else; + the_if.expression = condition(); + the_if.block = block(); + if (token_nxt.id === "else") { + advance("else"); + the_else = token_now; + the_if.else = ( + token_nxt.id === "if" + ? parse_statement() + : block() + ); + +// test_cause: +// ["if(0){0}else if(0){0}", "stmt_if", "else", "", 0] +// ["if(0){0}else{0}", "stmt_if", "else", "", 0] + + test_cause("else"); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + +// test_cause: +// ["if(0){break;}else{break;}", "stmt_if", "disrupt", "", 0] + + test_cause("disrupt"); + the_if.disrupt = true; + } else { + +// test_cause: +// ["if(0){break;}else{}", "stmt_if", "unexpected_a", "else", 14] + + warn("unexpected_a", the_else); + } + } + } + return the_if; + } + + function stmt_import() { + const the_import = token_now; + let name; + let names; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// if (typeof state.mode_module === "object") { +// +// // test_cause: +// // [" +// // /*global aa*/ +// // import aa from "aa" +// // ", "stmt_import", "unexpected_directive_a", "global", 1] +// +// warn( +// "unexpected_directive_a", +// state.mode_module, +// state.mode_module.directive +// ); +// } + + state.mode_module = true; + +// PR-436 - Add grammar for side-effect import-statement. + + if (token_nxt.id === "(string)") { + +// test_cause: +// ["import \"./aa.mjs\";", "stmt_import", "import_side_effect", "", 0] + + test_cause("import_side_effect"); + warn("expected_a_b", token_nxt, "{", artifact()); + advance(); + semicolon(); + return the_import; + } + if (token_nxt.identifier) { + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// ["import ignore from \"aa\"", "stmt_import", "unexpected_a", "ignore", 8] + + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + names = []; + advance("{"); + if (token_nxt.id !== "}") { + while (true) { + if (!token_nxt.identifier) { + +// test_cause: +// ["import {", "stmt_import", "expected_identifier_a", "(end)", 1] + + stop("expected_identifier_a"); + } + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// ["import {ignore} from \"aa\"", "stmt_import", "unexpected_a", "ignore", 9] + + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token_now; + if (!jslint_rgx_module.test(token_now.value)) { + +// test_cause: +// ["import aa from \"!aa\"", "stmt_import", "bad_module_name_a", "!aa", 16] + + warn("bad_module_name_a", token_now); + } + import_list.push(token_now.value); + semicolon(); + return the_import; + } + + function stmt_lbrace() { + +// test_cause: +// [";{}", "stmt_lbrace", "naked_block", "{", 2] +// ["class aa{}", "stmt_lbrace", "naked_block", "{", 9] + + warn("naked_block", token_now); + return block("naked"); + } + + function stmt_return() { + const the_return = token_now; + check_not_top_level(the_return); + if (functionage.finally > 0) { + +// test_cause: +// [" +// function aa(){try{}finally{return;}} +// ", "stmt_return", "unexpected_a", "return", 28] + + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (token_nxt.id !== ";" && the_return.line === token_nxt.line) { + the_return.expression = parse_expression(10); + } + advance(";"); + return the_return; + } + + function stmt_semicolon() { + +// test_cause: +// [";", "stmt_semicolon", "unexpected_a", ";", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function stmt_switch() { + const the_cases = []; + const the_switch = token_now; + let dups = []; + let exp; + let last; + let stmts; + let the_case; + let the_default; + let the_disrupt = true; + let the_last; + function is_dup(thing) { + return is_equal(thing, exp); + } + check_not_top_level(the_switch); + if (functionage.finally > 0) { + +// test_cause: +// [" +// function aa(){try{}finally{switch(0){}}} +// ", "stmt_switch", "unexpected_a", "switch", 28] + + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token_now.free = true; + the_switch.expression = parse_expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + while (true) { + +// Loop through cases with breaks. + + the_case = token_nxt; + the_case.arity = "statement"; + the_case.expression = []; + while (true) { + +// Loop through fallthrough cases. + + advance("case"); + token_now.switch = true; + exp = parse_expression(0); + if (dups.some(is_dup)) { + +// test_cause: +// [" +// switch(0){case 0:break;case 0:break} +// ", "stmt_switch", "unexpected_a", "0", 29] + + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (token_nxt.id !== "case") { + break; + } + } + +// test_cause: +// [" +// switch(0){case 1:case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 23] +// [" +// switch(0){case "aa":case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 26] +// [" +// switch(0){case "bb":case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 26] +// [" +// switch(0){case aa:case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24] +// [" +// switch(0){case bb:case aa:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24] + + check_ordered_case(the_case.expression); + stmts = parse_statements(); + if (stmts.length < 1) { + +// test_cause: +// [" +// switch(0){case 0:default:} +// ", "stmt_switch", "expected_statements_a", "default", 18] +// ["switch(0){case 0:}", "stmt_switch", "expected_statements_a", "}", 18] + + warn("expected_statements_a"); + +// PR-359 - Bugfix - Fixes issue #358 - switch-statement crashes jslint. + + break; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn("expected_a_before_b", token_nxt, "break;", artifact()); + } + if (token_nxt.id !== "case") { + break; + } + } + +// test_cause: +// [" +// switch(0){case 1:break;case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 29] +// [" +// switch(0){case "aa":break;case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 32] +// [" +// switch(0){case "bb":break;case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 32] +// [" +// switch(0){case aa:break;case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30] +// [" +// switch(0){case bb:break;case aa:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30] + + check_ordered_case(the_cases.map(function ({ + expression + }) { + return expression[0]; + })); + dups = undefined; + if (token_nxt.id === "default") { + the_default = token_nxt; + advance("default"); + token_now.switch = true; + advance(":"); + the_switch.else = parse_statements(); + if (the_switch.else.length < 1) { + +// test_cause: +// [" +// switch(0){case 0:break;default:} +// ", "stmt_switch", "unexpected_a", "default", 24] + + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + the_last = the_switch.else[ + the_switch.else.length - 1 + ]; + if ( + the_last.id === "break" + && the_last.label === undefined + ) { + +// test_cause: +// [" +// switch(0){case 0:break;default:break;} +// ", "stmt_switch", "unexpected_a", "break", 32] + + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; + } + + function stmt_throw() { + const the_throw = token_now; + the_throw.disrupt = true; + the_throw.expression = parse_expression(10); + semicolon(); + if (functionage.try > 0) { + +// test_cause: +// ["try{throw 0}catch(){}", "stmt_throw", "unexpected_a", "throw", 5] + + warn("unexpected_a", the_throw); + } + return the_throw; + } + + function stmt_try() { + const the_try = token_now; + let ignored; + let the_catch; + let the_disrupt; + if (functionage.try > 0) { + +// test_cause: +// ["try{try{}catch(){}}catch(){}", "stmt_try", "unexpected_a", "try", 5] + + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (token_nxt.id === "catch") { + ignored = "ignore"; + the_catch = token_nxt; + the_try.catch = the_catch; + advance("catch"); + +// Create new catch-scope for catch-parameter. + + catch_stack.push(catchage); + catchage = the_catch; + catch_list.push(catchage); + the_catch.context = empty(); + if (token_nxt.id === "(") { + advance("("); + if (!token_nxt.identifier) { + +// test_cause: +// ["try{}catch(){}", "stmt_try", "expected_identifier_a", ")", 12] + + return stop("expected_identifier_a"); + } + if (token_nxt.id !== "ignore") { + ignored = undefined; + the_catch.name = token_nxt; + enroll(token_nxt, "exception", true); + } + advance(); + advance(")"); + } + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + +// Restore previous catch-scope after catch-block. + + catchage = catch_stack.pop(); + +// PR-404 - Relax warning about missing `catch` in `try...finally` statement. +// +// } else { +// +// // test_cause: +// // ["try{}finally{break;}", "stmt_try", "expected_a_before_b", "finally", 6] +// +// warn("expected_a_before_b", token_nxt, "catch", artifact()); + + } + if (token_nxt.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; + } + + function stmt_var() { + let ellipsis; + let mode_const; + let name; + let the_brace; + let the_bracket; + let the_variable = token_now; + let variable_prv; + mode_const = the_variable.id === "const"; + the_variable.names = []; + +// A program may use var or let, but not both. + + if (!mode_const) { + if (mode_var === undefined) { + mode_var = the_variable.id; + } else if (the_variable.id !== mode_var) { + +// test_cause: +// ["let aa;var aa", "stmt_var", "expected_a_b", "var", 8] + + warn("expected_a_b", the_variable, mode_var, the_variable.id); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + +// test_cause: +// ["switch(0){case 0:var aa}", "stmt_var", "var_switch", "var", 18] + + warn("var_switch", the_variable); + } + switch ( + Boolean(functionage.statement_prv) + && functionage.statement_prv.id + ) { + case "const": + case "let": + case "var": + +// test_cause: +// ["const aa=0;const bb=0;", "stmt_var", "var_prv", "const", 0] +// ["let aa=0;let bb=0;", "stmt_var", "var_prv", "let", 0] +// ["var aa=0;var bb=0;", "stmt_var", "var_prv", "var", 0] + + test_cause("var_prv", functionage.statement_prv.id); + variable_prv = functionage.statement_prv; + break; + case "import": + +// test_cause: +// ["import aa from \"aa\";\nlet bb=0;", "stmt_var", "import_prv", "", 0] + + test_cause("import_prv"); + break; + case false: + break; + default: + if ( + (option_dict.beta && !option_dict.variable) + || the_variable.id === "var" + ) { + +// test_cause: +// ["console.log();let aa=0;", "stmt_var", "var_on_top", "let", 15] +// ["console.log();var aa=0;", "stmt_var", "var_on_top", "var", 15] +// ["try{aa();}catch(aa){var aa=0;}", "stmt_var", "var_on_top", "var", 21] +// ["while(0){var aa;}", "stmt_var", "var_on_top", "var", 10] + + warn("var_on_top", token_now); + } + } + while (true) { + if (token_nxt.id === "{") { + if (the_variable.id === "var") { + +// test_cause: +// ["var{aa}=0", "stmt_var", "unexpected_a", "var", 1] + + warn("unexpected_a", the_variable); + } + the_brace = token_nxt; + advance("{"); + while (true) { + name = token_nxt; + if (!name.identifier) { + +// test_cause: +// ["let {0}", "stmt_var", "expected_identifier_a", "0", 6] + + return stop("expected_identifier_a"); + } + survey(name); + advance(); + if (token_nxt.id === ":") { + advance(":"); + if (!token_nxt.identifier) { + +// test_cause: +// ["let {aa:0}", "stmt_var", "expected_identifier_a", "0", 9] +// ["let {aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 9] + + return stop("expected_identifier_a"); + } + +// PR-363 - Bugfix +// Add test against false-warning in code +// '/*jslint node*/\nlet {aa:bb} = {}; bb();'. +// +// token_nxt.label = name; +// the_variable.names.push(token_nxt); +// enroll(token_nxt, "variable", mode_const); + + name = token_nxt; + the_variable.names.push(name); + survey(name); + enroll(name, "variable", mode_const); + + advance(); + the_brace.open = true; + } else { + the_variable.names.push(name); + enroll(name, "variable", mode_const); + } + name.dead = false; + name.init = true; + if (token_nxt.id === "=") { + +// test_cause: +// ["let {aa=0}", "stmt_var", "assign", "", 0] + + test_cause("assign"); + advance("="); + name.expression = parse_expression(); + the_brace.open = true; + } + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + +// test_cause: +// ["let{bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8] + + check_ordered(the_variable.id, the_variable.names); + advance("}"); + advance("="); + the_variable.expression = parse_expression(0); + } else if (token_nxt.id === "[") { + if (the_variable.id === "var") { + +// test_cause: +// ["var[aa]=0", "stmt_var", "unexpected_a", "var", 1] + + warn("unexpected_a", the_variable); + } + the_bracket = token_nxt; + advance("["); + while (true) { + ellipsis = false; + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + if (!token_nxt.identifier) { + +// test_cause: +// ["let[]", "stmt_var", "expected_identifier_a", "]", 5] + + return stop("expected_identifier_a"); + } + name = token_nxt; + advance(); + the_variable.names.push(name); + enroll(name, "variable", mode_const); + name.dead = false; + name.init = true; + if (ellipsis) { + name.ellipsis = true; + break; + } + if (token_nxt.id === "=") { + advance("="); + name.expression = parse_expression(); + the_bracket.open = true; + } + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + advance("]"); + advance("="); + the_variable.expression = parse_expression(0); + } else if (token_nxt.identifier) { + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// [" +// let ignore;function aa(ignore) {} +// ", "stmt_var", "unexpected_a", "ignore", 5] + + warn("unexpected_a", name); + } + enroll(name, "variable", mode_const); + if (token_nxt.id === "=" || mode_const) { + advance("="); + name.dead = false; + name.init = true; + name.expression = parse_expression(0); + } + the_variable.names.push(name); + } else { + +// test_cause: +// ["let 0", "stmt_var", "expected_identifier_a", "0", 5] +// ["var{aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 8] + + return stop("expected_identifier_a"); + } + if (token_nxt.id !== ",") { + break; + } + +// test_cause: +// ["let aa,bb;", "stmt_var", "expected_a_b", ",", 7] + + warn("expected_a_b", token_nxt, ";", ","); + advance(","); + } + +// Warn if variable declarations are unordered. + + if ( + option_dict.beta + && !option_dict.unordered + && !option_dict.variable + && variable_prv + && ( + variable_prv.id + " " + variable_prv.names[0].id + > the_variable.id + " " + the_variable.names[0].id + ) + ) { + +// test_cause: +// ["const bb=0;const aa=0;", "stmt_var", "expected_a_b_before_c_d", "aa", 12] +// ["let bb;let aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8] +// ["var bb;var aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8] + + warn( + "expected_a_b_before_c_d", + the_variable, + the_variable.id, + the_variable.names[0].id, + variable_prv.id, + variable_prv.names[0].id + ); + } + semicolon(); + return the_variable; + } + + function stmt_while() { + const the_while = token_now; + check_not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + +// test_cause: +// ["function aa(){while(0){break;}}", "stmt_while", "weird_loop", "while", 15] + + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; + } + + function stmt_with() { + +// test_cause: +// ["with", "stmt_with", "unexpected_a", "with", 1] + + stop("unexpected_a", token_now); + } + + function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!jslint_rgx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!jslint_rgx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + +// test_cause: +// ["let aa={0:0}", "survey", "expected_identifier_a", "0", 9] + + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property_dict[id] === "number") { + property_dict[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (state.mode_property) { + if (tenure[id] !== true) { + +// test_cause: +// ["/*property aa*/\naa.bb", "survey", "unregistered_property_a", "bb", 4] + + warn("unregistered_property_a", name); + } + } else if ( + !option_dict.nomen + && name.identifier + && jslint_rgx_weird_property.test(id) + ) { + +// test_cause: +// ["aa.$", "survey", "weird_property_a", "$", 4] +// ["aa._", "survey", "weird_property_a", "_", 4] +// ["aa._aa", "survey", "weird_property_a", "_aa", 4] +// ["aa.aaSync", "survey", "weird_property_a", "aaSync", 4] +// ["aa.aa_", "survey", "weird_property_a", "aa_", 4] + + warn("weird_property_a", name); + } + property_dict[id] = 1; + } + return id; + } + +// These functions are used to specify the grammar of our language: + + function symbol(id, bp) { + +// Create a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax_dict[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax_dict[id] = the_symbol; + } + return the_symbol; + } + + function ternary(id1, id2) { + +// Create a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led_infix = function parse_ternary_led(left) { + const the_token = token_now; + let second; + second = parse_expression(20); + advance(id2); + token_now.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, parse_expression(10)]; + if (token_nxt.id !== ")") { + +// test_cause: +// ["0?0:0", "parse_ternary_led", "use_open", "?", 2] + + warn("use_open", the_token); + } + return the_token; + }; + return the_symbol; + } + +// Now we parse JavaScript. +// Begin defining the language. + + assignment("%="); + assignment("&="); + assignment("*="); + assignment("+="); + assignment("-="); + assignment("/="); + assignment("<<="); + assignment("="); + assignment(">>="); + assignment(">>>="); + assignment("^="); + assignment("|="); + constant("(number)", "number"); + constant("(regexp)", "regexp"); + constant("(string)", "string"); + constant("Function", "function", constant_Function); + constant("Infinity", "number", Infinity); + constant("NaN", "number", NaN); + constant("arguments", "object", constant_arguments); + constant("eval", "function", constant_eval); + constant("false", "boolean", false); + constant("ignore", "undefined", constant_ignore); + constant("isFinite", "function", constant_isInfinite); + constant("isNaN", "function", constant_isNaN); + constant("null", "null", null); + constant("this", "object", constant_this); + constant("true", "boolean", true); + constant("undefined", "undefined"); + infix(100, "!="); + infix(100, "!=="); + infix(100, "=="); + infix(100, "==="); + infix(110, "<"); + infix(110, "<="); + infix(110, ">"); + infix(110, ">="); + infix(110, "in"); + infix(110, "instanceof"); + infix(120, "<<"); + infix(120, ">>"); + infix(120, ">>>"); + infix(130, "+"); + infix(130, "-"); + infix(140, "%"); + infix(140, "*"); + infix(140, "/"); + infix(160, "(", infix_lparen); + infix(160, "`", infix_grave); + infix(170, ".", infix_dot); + infix(170, "=>", infix_fart_unwrapped); + infix(170, "?.", infix_option_chain); + infix(170, "[", infix_lbracket); + infix(35, "??"); + infix(40, "||"); + infix(50, "&&"); + infix(70, "|"); + infix(80, "^"); + infix(90, "&"); + infixr(150, "**"); + postassign("++"); + postassign("--"); + preassign("++"); + preassign("--"); + prefix("!!"); + prefix("!"); + prefix("(", prefix_lparen); + prefix("+"); + prefix("-"); + prefix("/=", prefix_assign_divide); + prefix("=>", prefix_fart); + prefix("[", prefix_lbracket); + prefix("`", prefix_tick); + prefix("async", prefix_async); + prefix("await", prefix_await); + prefix("function", prefix_function); + prefix("new", prefix_new); + prefix("typeof"); + prefix("void", prefix_void); + prefix("{", prefix_lbrace); + prefix("~"); + stmt(";", stmt_semicolon); + stmt("async", prefix_async); + stmt("await", prefix_await); + stmt("break", stmt_break); + stmt("const", stmt_var); + stmt("continue", stmt_continue); + stmt("debugger", stmt_debugger); + stmt("delete", stmt_delete); + stmt("do", stmt_do); + stmt("export", stmt_export); + stmt("for", stmt_for); + stmt("function", prefix_function); + stmt("if", stmt_if); + stmt("import", stmt_import); + stmt("let", stmt_var); + stmt("return", stmt_return); + stmt("switch", stmt_switch); + stmt("throw", stmt_throw); + stmt("try", stmt_try); + stmt("var", stmt_var); + stmt("while", stmt_while); + stmt("with", stmt_with); + stmt("{", stmt_lbrace); + symbol(")"); + symbol("*/"); + symbol(","); + symbol(":"); + symbol(";"); + symbol("]"); + symbol("async"); + symbol("await"); + symbol("case"); + symbol("catch"); + symbol("class"); + symbol("default"); + symbol("else"); + symbol("enum"); + symbol("finally"); + symbol("implements"); + symbol("interface"); + symbol("package"); + symbol("private"); + symbol("protected"); + symbol("public"); + symbol("static"); + symbol("super"); + symbol("void"); + symbol("yield"); + symbol("}"); + ternary("?", ":"); + +// Init token_nxt. + + advance(); + +// Parsing of JSON is simple: + + if (state.mode_json) { + state.token_tree = parse_json(); + advance("(end)"); + return; + } + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option_dict.browser) { + if (token_nxt.id === ";") { + advance(";"); + } + +// If we are not in a browser, then the file form of strict pragma may be used. + + } else if (token_nxt.value === "use strict") { + advance("(string)"); + advance(";"); + } + state.token_tree = parse_statements(); + advance("(end)"); + +// Check global functions are ordered. + + check_ordered( + "function", + function_list.map(function ({ + level, + name + }) { + return (level === 1) && name; + }).filter(function (name) { + return option_dict.beta && name && name.id; + }) + ); +} + +function jslint_phase4_walk(state) { + +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). + + let { + artifact, + catch_stack, + function_stack, + global_dict, + is_equal, + is_weird, + option_dict, + syntax_dict, + test_cause, + token_global, + warn + } = state; + let block_stack = []; // The stack of blocks. + let blockage = token_global; // The current block. + let catchage = catch_stack[0]; // The current catch-block. + let functionage = token_global; // The current function. + let postaction; + let postamble; + let posts = empty(); + let preaction; + let preamble; + let pres = empty(); + +// The relational operators. + + let relationop = object_assign_from_list(empty(), [ + "!=", "!==", "<", "<=", "==", "===", ">", ">=" + ], true); + +// Ambulation of the parse tree. + + function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; + } + + function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + task(the_token); + }); + } + } + }; + } + + function init_variable(name) { + let the_variable = lookup(name); + if (!the_variable || the_variable.readonly) { + warn("bad_assignment_a", name); + return; + } + the_variable.init = true; + } + + function lookup(thing) { + let id = thing.id; + let the_variable; + if (thing.arity !== "variable") { + return; + } + +// Look up the variable in the current context. + + the_variable = functionage.context[id] || catchage.context[id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable && the_variable.role === "label") { + +// test_cause: +// ["aa:while(0){aa;}", "lookup", "label_a", "aa", 13] + + warn("label_a", thing); + return the_variable; + } + if (!the_variable) { + function_stack.forEach(function ({ + context + }) { + if (context[id] && context[id].role !== "label") { + the_variable = context[id]; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (!the_variable && global_dict[id] === undefined) { + +// test_cause: +// ["aa", "lookup", "undeclared_a", "aa", 1] +// ["class aa{}", "lookup", "undeclared_a", "aa", 7] +// [" +// let aa=0;try{aa();}catch(bb){bb();}bb(); +// ", "lookup", "undeclared_a", "bb", 36] +// [" +// let aa=0;try{aa();}catch(ignore){bb();} +// ", "lookup", "undeclared_a", "bb", 34] + + warn("undeclared_a", thing); + return; + } + if (!the_variable) { + the_variable = { + dead: false, + id, + init: true, + parent: token_global, + readonly: true, + role: "variable", + used: 0 + }; + token_global.context[id] = the_variable; + } + the_variable.closure = true; + functionage.context[id] = the_variable; + } + if ( + ( + the_variable.calls === undefined + || functionage.name === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + && the_variable.dead + ) { + +// test_cause: +// ["let aa;if(aa){let bb;}bb;", "lookup", "out_of_scope_a", "bb", 23] + + warn("out_of_scope_a", thing); + } + return the_variable; + } + + function post_a(thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + let right; + if (thing.id === "=") { + if (thing.names !== undefined) { + +// test_cause: +// ["if(0){aa=0}", "post_a", "=", "", 0] + + test_cause("="); + +// Probably deadcode. +// if (Array.isArray(thing.names)) { +// thing.names.forEach(init_variable); +// } else { +// init_variable(thing.names); +// } + + jslint_assert( + !Array.isArray(thing.names), + `Expected !Array.isArray(thing.names).` + ); + init_variable(thing.names); + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + +// test_cause: +// ["aa.aa=undefined", "post_a", "expected_a_b", "undefined", 1] + + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.readonly) { + warn("bad_assignment_a", lvalue); + } + } + right = syntax_dict[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + +// test_cause: +// ["aa+=undefined", "post_a", "unexpected_a", "undefined", 5] + + warn("unexpected_a", thing.expression[1]); + } + } + } + + function post_a_pluseq(thing) { + const right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } + } + + function post_b(thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || is_equal(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + +// test_cause: +// ["if(0===0){0}", "post_b", "weird_relation_a", "===", 5] + + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option_dict.convert) { + if (thing.expression[0].value === "") { + +// test_cause: +// ["\"\"+0", "post_b", "expected_a_b", "\"\" +", 3] + + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + +// test_cause: +// ["0+\"\"", "post_b", "expected_a_b", "+ \"\"", 2] + + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + +// test_cause: +// ["aa=window[0]", "post_b", "weird_expression_a", "window[...]", 10] + + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + +// test_cause: +// ["aa=self[0]", "post_b", "weird_expression_a", "self[...]", 8] + + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === "." || thing.id === "?.") { + if (thing.expression.id === "RegExp") { + +// test_cause: +// ["aa=RegExp.aa", "post_b", "weird_expression_a", ".", 10] + + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + +// test_cause: +// ["0- -0", "post_b", "wrap_unary", "-", 4] + + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } + } + + function post_b_and(thing) { + if ( + is_weird(thing.expression[0]) + || is_equal(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + +// test_cause: +// ["aa=(()=>0)&&(()=>0)", "post_b_and", "weird_condition_a", "&&", 11] +// ["aa=(``?``:``)&&(``?``:``)", "post_b_and", "weird_condition_a", "&&", 14] +// ["aa=/./&&/./", "post_b_and", "weird_condition_a", "&&", 7] +// ["aa=0&&0", "post_b_and", "weird_condition_a", "&&", 5] +// ["aa=[]&&[]", "post_b_and", "weird_condition_a", "&&", 6] +// ["aa=`${0}`&&`${0}`", "post_b_and", "weird_condition_a", "&&", 10] +// [" +// aa=function aa(){}&&function aa(){} +// ", "post_b_and", "weird_condition_a", "&&", 19] +// ["aa={}&&{}", "post_b_and", "weird_condition_a", "&&", 6] + + warn("weird_condition_a", thing); + } + } + + function post_b_lbracket(thing) { + if (thing.expression[0].id === "RegExp") { + +// test_cause: +// ["aa=RegExp[0]", "post_b_lbracket", "weird_expression_a", "[", 10] + + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + +// test_cause: +// ["aa[[0]]", "post_b_lbracket", "weird_expression_a", "[", 4] + + warn("weird_expression_a", thing.expression[1]); + } + } + + function post_b_lparen(thing) { + let arg; + let array; + let cack; + let left = thing.expression[0]; + let new_date; + let paren; + let the_new; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + +// test_cause: +// ["aa=function(){}()", "post_b_lparen", "wrap_immediate", "(", 16] + + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "BigInt" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + +// test_cause: +// ["new BigInt()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Boolean()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Number()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new String()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Symbol()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new aa()", "post_b_lparen", "unexpected_a", "new", 1] + + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option_dict.eval) { + +// test_cause: +// ["new Function()", "post_b_lparen", "unexpected_a", "new Function", 5] + + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + +// test_cause: +// ["new Array()", "post_b_lparen", "expected_a_b", "new Array", 5] + + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + +// test_cause: +// ["new Object()", "post_b_lparen", "expected_a_b", "new Object", 5] + + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "BigInt" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + +// test_cause: +// ["let Aa=Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8] + + warn("expected_a_before_b", left, "new", artifact(left)); + } + } + } else if (left.id === ".") { + cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + +// test_cause: +// ["new Date.UTC()", "post_b_lparen", "cack", "", 0] + + test_cause("cack"); + cack = !cack; + } + if (jslint_rgx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + +// test_cause: +// ["new Date.UTC()", "post_b_lparen", "unexpected_a", "new", 1] + + warn("unexpected_a", the_new); + } else { + +// test_cause: +// ["let Aa=Aa.Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8] + + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + paren = left.expression; + if (paren.id === "(") { + array = paren.expression; + if (array.length === 1) { + new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + +// test_cause: +// [" +// new Date().getTime() +// ", "post_b_lparen", "expected_a_b", "new Date().getTime()", 1] + + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } + } + + function post_b_or(thing) { + if ( + is_weird(thing.expression[0]) + || is_equal(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + +// test_cause: +// ["aa=0||0", "post_b_or", "weird_condition_a", "||", 5] + + warn("weird_condition_a", thing); + } + } + + function post_s_export(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== token_global) { + +// test_cause: +// [" +// if(0){import aa from "aa";} +// ", "post_s_export", "misplaced_a", "import", 7] + + warn("misplaced_a", the_thing); + } + } + + function post_s_for(thing) { + +// Recurse walk_statement(). + + walk_statement(thing.inc); + } + + function post_s_function(thing) { + delete functionage.async; + delete functionage.finally; + delete functionage.loop; + delete functionage.statement_prv; + delete functionage.switch; + delete functionage.try; + functionage = function_stack.pop(); + if (thing.wrapped) { + +// test_cause: +// ["aa=(function(){})", "post_s_function", "unexpected_parens", "function", 5] + + warn("unexpected_parens", thing); + } + return post_s_lbrace(); + } + + function post_s_import(the_thing) { + const name = the_thing.name; + if (name) { + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return post_s_export(the_thing); + } + } + + function post_s_lbrace() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); + } + + function post_s_try(thing) { + if (thing.catch) { + if (thing.catch.name) { + Object.assign(catchage.context[thing.catch.name.id], { + dead: false, + init: true + }); + } + +// Recurse walk_statement(). + + walk_statement(thing.catch.block); + +// Restore previous catch-scope after catch-block. + + catchage = catch_stack.pop(); + } + } + + function post_s_var(thing) { + thing.names.forEach(function (name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + +// Probably deadcode. +// if (name.id === "{" || name.id === "[") { +// name.names.forEach(subactivate); +// } else { +// name.init = true; +// } + + jslint_assert( + !(name.id === "{" || name.id === "["), + `Expected !(name.id === "{" || name.id === "[").` + ); + name.init = true; + } + blockage.live.push(name); + }); + } + + function post_t(thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || is_equal(thing.expression[1], thing.expression[2]) + ) { + +// test_cause: +// ["let aa=(aa?`${0}`:`${0}`);", "post_t", "unexpected_a", "?", 11] +// ["let aa=(aa?`0`:`0`);", "post_t", "unexpected_a", "?", 11] + + warn("unexpected_a", thing); + } else if (is_equal(thing.expression[0], thing.expression[1])) { + +// test_cause: +// ["aa?aa:0", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "||", "?"); + } else if (is_equal(thing.expression[0], thing.expression[2])) { + +// test_cause: +// ["aa?0:aa", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + +// test_cause: +// ["aa?true:false", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + +// test_cause: +// ["aa?false:true", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + +// test_cause: +// ["(aa&&!aa?0:1)", "post_t", "wrap_condition", "&&", 4] + + warn("wrap_condition", thing.expression[0]); + } + } + + function post_u(thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option_dict.convert) { + +// test_cause: +// ["!!0", "post_u", "expected_a_b", "!!", 1] + + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } + } + + function post_u_plus(thing) { + const right = thing.expression; + if (!option_dict.convert) { + +// test_cause: +// ["aa=+0", "post_u_plus", "expected_a_b", "+", 4] + + warn("expected_a_b", thing, "Number(...)", "+"); + } + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } + } + + function pre_a_bitwise(thing) { + +// These are the bitwise operators. + + switch (!option_dict.bitwise && thing.id) { + case "&": + case "&=": + case "<<": + case "<<=": + case ">>": + case ">>=": + case ">>>": + case ">>>=": + case "^": + case "^=": + case "|": + case "|=": + case "~": + +// test_cause: +// ["0&0", "pre_a_bitwise", "unexpected_a", "&", 2] +// ["0&=0", "pre_a_bitwise", "unexpected_a", "&=", 2] +// ["0<<0", "pre_a_bitwise", "unexpected_a", "<<", 2] +// ["0<<=0", "pre_a_bitwise", "unexpected_a", "<<=", 2] +// ["0>>0", "pre_a_bitwise", "unexpected_a", ">>", 2] +// ["0>>=0", "pre_a_bitwise", "unexpected_a", ">>=", 2] +// ["0>>>0", "pre_a_bitwise", "unexpected_a", ">>>", 2] +// ["0>>>=0", "pre_a_bitwise", "unexpected_a", ">>>=", 2] +// ["0^0", "pre_a_bitwise", "unexpected_a", "^", 2] +// ["0^=0", "pre_a_bitwise", "unexpected_a", "^=", 2] +// ["0|0", "pre_a_bitwise", "unexpected_a", "|", 2] +// ["0|=0", "pre_a_bitwise", "unexpected_a", "|=", 2] +// ["~0", "pre_a_bitwise", "unexpected_a", "~", 1] + + warn("unexpected_a", thing); + break; + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + +// test_cause: +// ["0<0<0", "pre_a_bitwise", "unexpected_a", "<", 4] + + warn("unexpected_a", thing); + } + } + + function pre_b(thing) { + let left; + let right; + let value; + if (relationop[thing.id] === true) { + left = thing.expression[0]; + right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + +// test_cause: +// ["NaN===NaN", "pre_b", "number_isNaN", "===", 4] + + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + +// test_cause: +// ["typeof 0===0", "pre_b", "expected_string_a", "0", 12] + + warn("expected_string_a", right); + } + } else { + value = right.value; + if (value === "null" || value === "undefined") { + +// test_cause: +// [" +// typeof aa==="undefined" +// ", "pre_b", "unexpected_typeof_a", "undefined", 13] + + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "bigint" + && value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + +// test_cause: +// ["typeof 0===\"aa\"", "pre_b", "expected_type_string_a", "aa", 12] + + warn("expected_type_string_a", right, value); + } + } + } + } + } + + function pre_b_eqeq(thing) { + +// test_cause: +// ["0==0", "pre_b_eqeq", "expected_a_b", "==", 2] + + warn("expected_a_b", thing, "===", "=="); + } + + function pre_b_in(thing) { + +// test_cause: +// ["aa in aa", "pre_b_in", "infix_in", "in", 4] + + warn("infix_in", thing); + } + + function pre_b_instanceof(thing) { + +// test_cause: +// ["0 instanceof 0", "pre_b_instanceof", "unexpected_a", "instanceof", 3] + + warn("unexpected_a", thing); + } + + function pre_b_lparen(thing) { + const left = thing.expression[0]; + let left_variable; + let parent; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + parent = functionage.name.parent; + if (parent) { + left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + +// Probably deadcode. +// && left_variable.dead + + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } + } + + function pre_b_noteq(thing) { + +// test_cause: +// ["0!=0", "pre_b_noteq", "expected_a_b", "!=", 2] + + warn("expected_a_b", thing, "!==", "!="); + } + + function pre_b_or(thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + +// test_cause: +// ["0&&0||0", "pre_b_or", "and", "&&", 2] + + warn("and", thang); + } + }); + } + + function pre_s_for(thing) { + let the_variable; + if (thing.name !== undefined) { + thing.name.dead = false; + the_variable = lookup(thing.name); + if (the_variable !== undefined) { + if (the_variable.init && the_variable.readonly) { + +// test_cause: +// ["const aa=0;for(aa in aa){}", "pre_s_for", "bad_assignment_a", "aa", 16] + + warn("bad_assignment_a", thing.name); + } + the_variable.init = true; + } + } + +// Recurse walk_statement(). + + walk_statement(thing.initial); + } + + function pre_s_function(thing) { + +// test_cause: +// ["()=>0", "pre_s_function", "", "", 0] +// ["(function (){}())", "pre_s_function", "", "", 0] +// ["function aa(){}", "pre_s_function", "", "", 0] + + test_cause(""); + if (thing.arity === "statement" && blockage.body !== true) { + +// test_cause: +// ["if(0){function aa(){}\n}", "pre_s_function", "unexpected_a", "function", 7] + + warn("unexpected_a", thing); + } + function_stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + +// test_cause: +// [" +// /*jslint getset*/ +// aa={get aa(aa){}} +// ", "pre_s_function", "bad_get", "function", 9] + + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + +// test_cause: +// [" +// /*jslint getset*/ +// aa={set aa(){}} +// ", "pre_s_function", "bad_set", "function", 9] + + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); + } + + function pre_s_lbrace(thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; + } + + function pre_try(thing) { + if (thing.catch !== undefined) { + +// Create new catch-scope for catch-parameter. + + catch_stack.push(catchage); + catchage = thing.catch; + } + } + + function pre_v(thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } + } + + function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); + } + + function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + +// test_cause: +// ["(function(){}())", "walk_expression", "isArray", "", 0] +// ["0&&0", "walk_expression", "isArray", "", 0] + + test_cause("isArray"); + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + +// PR-414 - Bugfix - fix fart-body not being walked. + + if (thing.id === "function" || thing.id === "=>") { + +// test_cause: +// ["aa=()=>0", "walk_expression", "function", "=>", 0] +// ["aa=function(){}", "walk_expression", "function", "function", 0] + + test_cause("function", thing.id); + +// Recurse walk_statement(). + + walk_statement(thing.block); + } + if ( + thing.arity === "preassign" || thing.arity === "postassign" + ) { + +// test_cause: +// ["aa=++aa", "walk_expression", "unexpected_a", "++", 4] +// ["aa=--aa", "walk_expression", "unexpected_a", "--", 4] + + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + +// test_cause: +// ["aa[aa=0]", "walk_expression", "unexpected_statement_a", "=", 6] + + warn("unexpected_statement_a", thing); + } + +// test_cause: +// ["aa=0", "walk_expression", "default", "", 0] + + test_cause("default"); + postamble(thing); + } + } + } + + function walk_statement(thing) { + if (!thing) { + return; + } + if (Array.isArray(thing)) { + +// test_cause: +// ["+[]", "walk_statement", "isArray", "", 0] + + test_cause("isArray"); + +// Recurse walk_statement(). + + thing.forEach(walk_statement); + return; + } + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + +// test_cause: +// ["0&&0", "walk_statement", "unexpected_expression_a", "&&", 2] + + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + && thing.id !== "import" + ) { + +// test_cause: +// ["!0", "walk_statement", "unexpected_expression_a", "!", 1] +// ["+[]", "walk_statement", "unexpected_expression_a", "+", 1] +// ["+new aa()", "walk_statement", "unexpected_expression_a", "+", 1] +// ["0", "walk_statement", "unexpected_expression_a", "0", 1] +// ["typeof 0", "walk_statement", "unexpected_expression_a", "typeof", 1] + + warn("unexpected_expression_a", thing); + } + +// Recurse walk_statement(). + + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + + postaction = action(posts); + postamble = amble(posts); + preaction = action(pres); + preamble = amble(pres); + postaction("assignment", "+=", post_a_pluseq); + postaction("assignment", post_a); + postaction("binary", "&&", post_b_and); + postaction("binary", "(", post_b_lparen); + postaction("binary", "=>", post_s_function); + postaction("binary", "[", post_b_lbracket); + postaction("binary", "||", post_b_or); + postaction("binary", post_b); + postaction("statement", "const", post_s_var); + postaction("statement", "export", post_s_export); + postaction("statement", "for", post_s_for); + postaction("statement", "function", post_s_function); + postaction("statement", "import", post_s_import); + postaction("statement", "let", post_s_var); + postaction("statement", "try", post_s_try); + postaction("statement", "var", post_s_var); + postaction("statement", "{", post_s_lbrace); + postaction("ternary", post_t); + postaction("unary", "+", post_u_plus); + postaction("unary", "function", post_s_function); + postaction("unary", post_u); + preaction("assignment", pre_a_bitwise); + preaction("binary", "!=", pre_b_noteq); + preaction("binary", "(", pre_b_lparen); + preaction("binary", "==", pre_b_eqeq); + preaction("binary", "=>", pre_s_function); + preaction("binary", "in", pre_b_in); + preaction("binary", "instanceof", pre_b_instanceof); + preaction("binary", "||", pre_b_or); + preaction("binary", pre_b); + preaction("binary", pre_a_bitwise); + preaction("statement", "for", pre_s_for); + preaction("statement", "function", pre_s_function); + preaction("statement", "try", pre_try); + preaction("statement", "{", pre_s_lbrace); + preaction("unary", "function", pre_s_function); + preaction("unary", "~", pre_a_bitwise); + preaction("variable", pre_v); + + walk_statement(state.token_tree); +} + +function jslint_phase5_whitage(state) { + +// PHASE 5. Check whitespace between tokens in . + + let { + artifact, + catch_list, + function_list, + function_stack, + option_dict, + test_cause, + token_global, + token_list, + warn + } = state; + let closer = "(end)"; + let free = false; + +// free = false + +// cause: +// "()=>0" +// "aa()" +// "aa(0,0)" +// "function(){}" + +// free = true + +// cause: +// "(0)" +// "(aa)" +// "aa(0)" +// "do{}while()" +// "for(){}" +// "if(){}" +// "switch(){}" +// "while(){}" + + let left = token_global; + let margin = 0; + let mode_indent = ( + +// PR-330 - Allow 2-space indent. + + option_dict.indent2 + ? 2 + : 4 + ); + let nr_comments_skipped = 0; + let open = true; + let opening = true; + let right; + +// This is the set of infix operators that require a space on each side. + + let spaceop = object_assign_from_list(empty(), [ + "!=", "!==", "%", "%=", "&", "&&", "&=", "*", "*=", "+=", "-=", "/", + "/=", "<", "<<", "<<=", "<=", "=", "==", "===", "=>", ">", ">=", ">>", + ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" + ], true); + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + const name = the_function.context[id]; + if (id !== "ignore" && name.parent === the_function) { + +// test_cause: +// ["function aa(aa) {return aa;}", "delve", "id", "", 0] + + test_cause("id"); + if ( + name.used === 0 + +// Probably deadcode. +// && ( +// name.role !== "function" +// || name.parent.arity !== "unary" +// ) + + && jslint_assert( + name.role !== "function", + `Expected name.role !== "function".` + ) + ) { + +// test_cause: +// ["/*jslint node*/\nlet aa;", "delve", "unused_a", "aa", 5] +// ["function aa(aa){return;}", "delve", "unused_a", "aa", 13] +// ["let aa=0;try{aa();}catch(bb){aa();}", "delve", "unused_a", "bb", 26] + + warn("unused_a", name); + } else if (!name.init) { + +// test_cause: +// ["/*jslint node*/\nlet aa;aa();", "delve", "uninitialized_a", "aa", 5] + + warn("uninitialized_a", name); + } + } + }); + } + + function expected_at(at) { + +// Probably deadcode. +// if (right === undefined) { +// right = token_nxt; +// } + + jslint_assert( + !(right === undefined), + `Expected !(right === undefined).` + ); + warn( + "expected_a_at_b_c", + right, + artifact(right), + +// Fudge column numbers in warning message. + + at + jslint_fudge, + right.from + jslint_fudge + ); + } + + function no_space() { + if (left.line === right.line) { + +// from: +// if (left.line === right.line) { +// no_space(); +// } else { + + if (left.thru !== right.from && nr_comments_skipped === 0) { + +// test_cause: +// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14] + + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + +// from: +// } else if ( +// right.arity === "binary" +// && right.id === "(" +// && free +// ) { +// no_space(); +// } else if ( + +// Probably deadcode. +// if (open) { +// const at = ( +// free +// ? margin +// : margin + 8 +// ); +// if (right.from < at) { +// expected_at(at); +// } +// } else { +// if (right.from !== margin + 8) { +// expected_at(margin + 8); +// } +// } + + jslint_assert(open, `Expected open.`); + jslint_assert(free, `Expected free.`); + if (right.from < margin) { + +// test_cause: +// ["let aa = aa(\naa\n()\n);", "expected_at", "expected_a_at_b_c", "5", 1] + + expected_at(margin); + } + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line || !open) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (right.from !== margin) { + expected_at(margin); + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn("expected_space_a_b", right, artifact(left), artifact(right)); + } + } + + function pop() { + const previous = function_stack.pop(); + closer = previous.closer; + free = previous.free; + margin = previous.margin; + open = previous.open; + opening = previous.opening; + } + + function push() { + function_stack.push({ + closer, + free, + margin, + open, + opening + }); + } + +// uninitialized_and_unused(); +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (state.mode_module === true || option_dict.node) { + delve(token_global); + } + catch_list.forEach(delve); + function_list.forEach(delve); + + if (option_dict.white) { + return; + } + +// whitage(); +// Go through the token list, looking at usage of whitespace. + + token_list.forEach(function whitage(the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + +// The open and close pairs. + + switch (left.id) { + case "${": + case "(": + case "[": + case "{": + +// test_cause: +// ["let aa=[];", "whitage", "opener", "", 0] +// ["let aa=`${0}`;", "whitage", "opener", "", 0] +// ["let aa=aa();", "whitage", "opener", "", 0] +// ["let aa={};", "whitage", "opener", "", 0] + + test_cause("opener"); + +// Probably deadcode. +// case "${}": + + jslint_assert( + !(left.id + right.id === "${}"), + "Expected !(left.id + right.id === \"${}\")." + ); + switch (left.id + right.id) { + case "()": + case "[]": + case "{}": + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like '{]') have already been detected. + +// test_cause: +// ["let aa=[];", "whitage", "opener_closer", "", 0] +// ["let aa=aa();", "whitage", "opener_closer", "", 0] +// ["let aa={};", "whitage", "opener_closer", "", 0] + + test_cause("opener_closer"); + if (left.line === right.line) { + +// test_cause: +// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14] + + no_space(); + } else { + +// test_cause: +// ["let aa = aa(\n );", "expected_at", "expected_a_at_b_c", "1", 2] + + at_margin(0); + } + break; + default: + +// test_cause: +// ["let aa=(0);", "whitage", "opener_operand", "", 0] +// ["let aa=[0];", "whitage", "opener_operand", "", 0] +// ["let aa=`${0}`;", "whitage", "opener_operand", "", 0] +// ["let aa=aa(0);", "whitage", "opener_operand", "", 0] +// ["let aa={aa:0};", "whitage", "opener_operand", "", 0] + + test_cause("opener_operand"); + opening = left.open || (left.line !== right.line); + push(); + switch (left.id) { + case "${": + closer = "}"; + break; + case "(": + closer = ")"; + break; + case "[": + closer = "]"; + break; + case "{": + closer = "}"; + break; + } + if (opening) { + +// test_cause: +// ["function aa(){\nreturn;\n}", "whitage", "opening", "", 0] +// ["let aa=(\n0\n);", "whitage", "opening", "", 0] +// ["let aa=[\n0\n];", "whitage", "opening", "", 0] +// ["let aa=`${\n0\n}`;", "whitage", "opening", "", 0] +// ["let aa={\naa:0\n};", "whitage", "opening", "", 0] + + test_cause("opening"); + free = closer === ")" && left.free; + open = true; + margin += mode_indent; + if (right.role === "label") { + if (right.from !== 0) { + +// test_cause: +// [" +// function aa() { +// bb: +// while (aa) { +// if (aa) { +// break bb; +// } +// } +// } +// ", "expected_at", "expected_a_at_b_c", "1", 2] + + expected_at(0); + } + } else if (right.switch) { + at_margin(-mode_indent); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + +// test_cause: +// [" +// function aa() {bb: +// while (aa) { +// aa(); +// } +// } +// ", "whitage", "expected_line_break_a_b", "bb", 16] + + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + +// test_cause: +// ["let aa=(0);", "whitage", "not_free", "", 0] +// ["let aa=[0];", "whitage", "not_free", "", 0] +// ["let aa=`${0}`;", "whitage", "not_free", "", 0] +// ["let aa={aa:0};", "whitage", "not_free", "", 0] + + test_cause("not_free"); + free = false; + open = false; + +// test_cause: +// ["let aa = ( 0 );", "no_space_only", "unexpected_space_a_b", "0", 12] + + no_space_only(); + } + } + break; + default: + if (right.statement === true) { + if (left.id === "else") { + +// test_cause: +// [" +// let aa = 0; +// if (aa) { +// aa(); +// } else if (aa) { +// aa(); +// } +// ", "one_space_only", "expected_space_a_b", "if", 9] + + one_space_only(); + } else { + +// test_cause: +// [" let aa = 0;", "expected_at", "expected_a_at_b_c", "1", 2] + + at_margin(0); + open = false; + } + +// If right is a closer, then pop the previous state. + + } else if (right.id === closer) { + pop(); + if (opening && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-mode_indent); + } else if (right.role === "label") { + if (right.from !== 0) { + +// test_cause: +// [" +// function aa() { +// aa();cc: +// while (aa) { +// if (aa) { +// break cc; +// } +// } +// } +// ", "expected_at", "expected_a_at_b_c", "1", 10] + + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + +// test_cause: +// ["let {aa,bb} = 0;", "one_space", "expected_space_a_b", "bb", 9] + + one_space(); + } else { + +// test_cause: +// [" +// function aa() { +// aa( +// 0,0 +// ); +// } +// ", "expected_at", "expected_a_at_b_c", "9", 11] + + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + +// test_cause: +// [" +// let aa = ( +// aa +// ? 0 +// : 1 +// ); +// ", "expected_at", "expected_a_at_b_c", "5", 1] + + at_margin(0); + } else { + +// test_cause: +// ["let aa = (aa ? 0 : 1);", "whitage", "use_open", "?", 14] + + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + +// test_cause: +// ["let aa = aa(\naa ()\n);", "no_space", "unexpected_space_a_b", "(", 4] + + no_space(); + } else if ( + left.id === "." + || left.id === "?." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + || (right.id === "." || right.id === "?.") + ) { + +// test_cause: +// ["let aa = 0 ;", "no_space_only", "unexpected_space_a_b", ";", 12] +// ["let aa = aa ?.aa;", "no_space_only", "unexpected_space_a_b", "?.", 13] + + no_space_only(); + } else if (left.id === ";") { + +// test_cause: +// [" +// /*jslint for*/ +// function aa() { +// for ( +// aa(); +// aa; +// aa() +// ) { +// aa(); +// } +// } +// ", "expected_at", "expected_a_at_b_c", "9", 1] + + if (open) { + at_margin(0); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || left.id === "await" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + +// test_cause: +// [" +// function aa() { +// do { +// aa(); +// } while(aa()); +// } +// ", "one_space_only", "expected_space_a_b", "(", 12] + + one_space_only(); + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || left.id === "async" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + +// test_cause: +// ["let aa=0;", "one_space", "expected_space_a_b", "0", 8] +// ["let aa={\naa:\n0\n};", "expected_at", "expected_a_at_b_c", "5", 1] + + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +function jslint_report({ + exports, + froms, + functions, + global, + json, + module, + property, + stop, + warnings +}) { + +// This function will create human-readable, html-report +// for warnings, properties, and functions from jslint-result-object. +// +// Example usage: +// let result = jslint("console.log('hello world')"); +// let html = jslint_report(result); + + let html = ""; + let length_80 = 1111; + + function address(line = 1, column = 1) { + +// This function will create HTML address element from and + + return `
    ${Number(line)}: ${Number(column)}
    `; + + } + + function detail(title, list) { + return ( + (Array.isArray(list) && list.length > 0) + ? ( + +// Google Lighthouse Accessibility -
    's do not contain only properly-ordered +//
    and
    groups, + + + + + + + + + + + + +

    CodeMirror: JSLint Demo

    +

    +This demo will auto-lint the code below, and auto-generate a report as you type. +

    + + + + + + + +
    + + + + + +``` +3. Live example at https://www.jslint.com/jslint_wrapper_codemirror.html + +[![screenshot](https://kaizhu256.github.io/jslint/branch-master/.artifact/screenshot_browser__2fjslint_2fbranch-master_2fjslint_wrapper_codemirror.html.png)](https://kaizhu256.github.io/jslint/jslint_wrapper_codemirror.html) + + +

    +# Quickstart JSLint in Vim +1. Download and save [`jslint.mjs`](https://www.jslint.com/jslint.mjs), [`jslint_wrapper_vim.vim`](https://www.jslint.com/jslint_wrapper_vim.vim) to directory `~/.vim/` +2. Add vim-command `:source ~/.vim/jslint_wrapper_vim.vim` to file `~/.vimrc` + - If above files were saved to custom-directory, then use that directory instead, e.g.: + - save [`jslint.mjs`](https://www.jslint.com/jslint.mjs), [`jslint_wrapper_vim.vim`](https://www.jslint.com/jslint_wrapper_vim.vim) to directory `~/vimfiles/` + - vim-command `:source ~/vimfiles/jslint_wrapper_vim.vim` +3. Vim can now jslint files (via nodejs): + - with vim-command `:SaveAndJslint` + - with vim-key-combo ` ` +- screenshot + +[![screenshot](asset_image_jslint_wrapper_vim.png)](https://www.jslint.com/jslint_wrapper_vim.vim) + + +

    +# Quickstart JSLint in VSCode +1. In VSCode, search and install extension [`vscode-jslint`](https://marketplace.visualstudio.com/items?itemName=jslint.vscode-jslint) +2. In VSCode, while editing a javascript file: + - right-click context-menu and select `[JSLint - Lint File]` + - or use key-binding `[Ctrl + Shift + J], [L]` + - or use key-binding `[ Cmd + Shift + J], [L]` for Mac +- screenshot + +[![screenshot](https://kaizhu256.github.io/jslint/asset_image_jslint_wrapper_vscode.png)](https://marketplace.visualstudio.com/items?itemName=jslint.vscode-jslint) + + +

    +# Documentation + + +- [jslint.mjs](jslint.mjs) contains the jslint function. It parses and analyzes a source file, returning an object with information about the file. It can also take an object that sets options. + +- [index.html](index.html) runs the jslint.mjs function in a web page. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[https://www.crockford.com/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. + + +

    +### API Doc +- https://www.jslint.com/apidoc.html + +[![screenshot](https://kaizhu256.github.io/jslint/branch-master/.artifact/screenshot_browser__2f.artifact_2fapidoc.html.png)](https://www.jslint.com/apidoc.html) + + +

    +### Directive `/*jslint*/` + +
    + +##### `/*jslint beta*/` + +```js +/*jslint beta*/ +// Enable experimental warnings. +// Warn if global variables are redefined. +// Warn if const / let statements are not declared at top of function or +// script, similar to var statements. +// Warn if const / let / var statements are not declared in ascii-order. +// Warn if named-functions are not declared in ascii-order. +// Warn if cases in switch-statements are not in ascii-order. +``` + +
    + +##### `/*jslint bitwise*/` + +```js +/*jslint bitwise*/ +// Allow bitwise operator. + +let foo = 0 | 1; +``` + +
    + +##### `/*jslint browser*/` + +```js +/*jslint browser*/ +// Assume browser environment. + +localStorage.getItem("foo"); +``` + +
    + +##### `/*jslint convert*/` + +```js +/*jslint convert*/ +// Allow conversion operator. + +let foo = new Date() + ""; +let bar = !!0; +``` + +
    + +##### `/*jslint couch*/` + +```js +/*jslint couch*/ +// Assume CouchDb environment. + +registerType("text-json", "text/json"); +``` + +
    + +##### `/*jslint devel*/` + +```js +/*jslint devel*/ +// Allow console.log() and friends. + +console.log("hello"); +``` + +
    + +##### `/*jslint eval*/` + +```js +/*jslint eval*/ +// Allow eval(). + +eval("1"); +``` + +
    + +##### `/*jslint fart*/` + +```js +/*jslint fart*/ +// Allow complex fat-arrow. + +let foo = async ({bar, baz}) => { + return await bar(baz); +}; +``` + +
    + +##### `/*jslint for*/` + +```js +/*jslint for*/ +// Allow for-loop. + +function foo() { + let ii; + for (ii = 0; ii < 10; ii += 1) { + foo(); + } +} +``` + +
    + +##### `/*jslint getset*/` + +```js +/*jslint getset, this, devel*/ +// Allow get() and set(). + +let foo = { + bar: 0, + get getBar() { + return this.bar; + }, + set setBar(value) { + this.bar = value; + } +}; +console.log(foo.getBar); // 0 +foo.setBar = 1; +console.log(foo.getBar); // 1 +``` + +
    + +##### `/*jslint indent2*/` + +```js +/*jslint indent2*/ +// Use 2-space indent. + +function foo() { + return; +} +``` + +
    + +##### `/*jslint long*/` + +```js +/*jslint long*/ +// Allow long lines. + +let foo = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; +``` + +
    + +##### `/*jslint node*/` + +```js +/*jslint node*/ +// Assume Node.js environment. + +require("fs"); +``` + +
    + +##### `/*jslint nomen*/` + +```js +/*jslint nomen*/ +// Allow weird property name. + +let foo = {}; +foo._bar = 1; +``` + +
    + +##### `/*jslint single*/` + +```js +/*jslint single*/ +// Allow single-quote strings. + +let foo = ''; +``` + +
    + +##### `/*jslint subscript*/` + +```js +/*jslint subscript*/ +// Allow identifiers in subscript-notation. + +let foo = {}; +foo["bar"] = 1; +``` + +
    + +##### `/*jslint this*/` + +```js +/*jslint this*/ +// Allow 'this'. + +function foo() { + return this; +} +``` + +
    + +##### `/*jslint trace*/` + +```js +/*jslint trace*/ +// Include jslint stack-trace in warnings. + +console.log('hello world'); +/* +1. Undeclared 'console'. +console.log('hello world'); +Error + at warn_at (...) + at warn (...) + at lookup (...) + at pre_v (...) + at jslint.mjs +2. Use double quotes, not single quotes. +console.log(...); +Error + at warn_at (...) + at lex_string (...) + at lex_token (...) + at jslint_phase2_lex (...) + at Function.jslint (...) + at jslint.mjs +*/ +``` + +
    + +##### `/*jslint unordered*/` + +```js +/*jslint unordered*/ +// Allow unordered cases, params, properties, variables, and exports. + +let foo = {bb: 1, aa: 0}; + +function bar({ + bb = 1, + aa = 0 +}) { + return aa + bb; +} + +export { + foo, + bar +}; +``` + +
    + +##### `/*jslint white*/` + +```js +/*jslint white*/ +// Allow messy whitespace. + +let foo = 1; let bar = 2; +``` + + +

    +### Directive `/*global*/` + +```js +/*global foo, bar*/ +// Declare global variables foo, bar. + +foo(); +bar(); +``` + + +

    +### Directive `/*property*/` + +```js +/*property foo, bar*/ +// Restrict property-access to only .foo, .bar. + +let aa = {bar: 1, foo: 2}; +``` + + +

    +### Directive `/*jslint-disable*/.../*jslint-enable*/` + +```js +/*jslint-disable*/ + +JSLint will ignore and treat this region as blank-lines. +Syntax error. + +/*jslint-enable*/ +``` + + +

    +### Directive `//jslint-ignore-line` + +```js +// JSLint will ignore non-fatal warnings at given line. + +eval("1"); //jslint-ignore-line +``` + + +

    +# Package Listing +![screenshot_package_listing.svg](https://kaizhu256.github.io/jslint/branch-master/.artifact/screenshot_package_listing.svg) + + +

    +# Changelog +- [Full CHANGELOG.md](CHANGELOG.md) + +![screenshot_changelog.svg](https://kaizhu256.github.io/jslint/branch-master/.artifact/screenshot_changelog.svg) + + +

    +# License +- JSLint is under [Unlicense License](LICENSE). +- CodeMirror editor is under [MIT License](https://github.com/codemirror/codemirror5/blob/d0e3b2e727c41aa4fd89fbad0adfb3815339174c/LICENSE). +- Function `v8CoverageListMerge` is derived from [MIT Licensed v8-coverage](https://github.com/demurgos/v8-coverage/blob/73446087dc38f61b09832c9867122a23f8577099/ts/LICENSE.md). + + +

    +# Devops Instruction + + +

    +### pull-request merge +- find highest issue-number at https://github.com/kaizhu256/jslint/issues/, https://github.com/kaizhu256/jslint/pulls/, and add +1 to it for PR-xxx +- `shGitPullrequest beta beta` + - verify ci-success for origin-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- `git push upstream alpha -f` + - verify ci-success for upstream-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- goto https://github.com/kaizhu256/jslint/compare/beta...kaizhu256:jslint:branch-p2024.11.24 +- click `Create pull request` +- input `Add your description here...` with: +``` +Fixes #xxx. +- + +This PR will ... + +This PR will additionally: +- +... + + +``` +- verify `commit into jslint-org:beta` +- click `Create pull request` + - verify ci-success for pull-request + - https://github.com/kaizhu256/jslint/actions/workflows/on_pull_request.yml +- wait awhile before continuing ... +- click `Rebase and merge` + - verify ci-success for upstream-branch-beta + - https://github.com/kaizhu256/jslint/actions +- `shGitPullrequestCleanup` + - verify ci-success for origin-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- `git push upstream alpha -f` + - verify ci-success for upstream-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- click `Delete branch` + + +

    +### branch-master commit +- `shGitPullrequest master beta` + - verify ci-success for origin-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- `git push upstream alpha -f` + - verify ci-success for upstream-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- goto https://github.com/kaizhu256/jslint/compare/beta...kaizhu256:jslint:branch-v2024.11.24 +- click `Create pull request` +- input `Add a title` with: `# v20yy.mm.dd` +- input `Add a description` with: +``` +- +- +``` +- verify `commit into jslint-org:beta` +- click `Create pull request` + - verify ci-success for pull-request + - https://github.com/kaizhu256/jslint/actions/workflows/on_pull_request.yml +- wait awhile before continuing ... +- click `Rebase and merge` + - verify ci-success for upstream-branch-beta + - https://github.com/kaizhu256/jslint/actions +- `shGitPullrequestCleanup` + - verify ci-success for origin-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- `git push upstream alpha -f` + - verify ci-success for upstream-branch-alpha + - https://github.com/kaizhu256/jslint/actions +- click `Delete branch` +- `git push origin beta:master` + - verify ci-success for origin-branch-master + - https://github.com/kaizhu256/jslint/actions +- `git push upstream beta:master` + - verify ci-success for upstream-branch-master + - https://github.com/kaizhu256/jslint/actions + + +

    +### branch-master publish +- `git push upstream beta:master` + - verify ci-success for upstream-branch-master + - https://github.com/kaizhu256/jslint/actions +- goto https://github.com/kaizhu256/jslint/releases/new +- input `Choose a tag` with: `v20yy.mm.dd` +- click `Create new tag: v20yy.mm.dd on publish` + - verify correct-year `20yy` +- select `Target: master` +- select `Previous tag:auto` +- input `Release title` with: `v20yy.mm.dd - ` +- input `Describe this release` with: +``` +- +- +``` +- click `Generate release notes` +- click `Set as the latest release` +- click `Preview` and review +- click `Publish release` + - verify ci-success for upstream-branch-publish + - https://github.com/kaizhu256/jslint/actions + - verify email-notification `Successfully published @kaizhu256/jslint@20yy.mm.dd` + + +

    +### vscode-jslint publish +- goto https://github.com/kaizhu256/jslint/tree/gh-pages/branch-master/.artifact/jslint_wrapper_vscode +- click `vscode-jslint-20yy.mm.dd.vsix` +- click `Raw` to download +- goto https://marketplace.visualstudio.com/manage/publishers/jslint +- right-click `Update` +- upload downloaded file `vscode-jslint-20yy.mm.dd.vsix` +- click 'Upload' +- verify email-notification `[Succeeded] Extension publish on Visual Studio Marketplace - vscode-jslint` + + + diff --git a/branch-master/asset_codemirror_rollup.js b/branch-master/asset_codemirror_rollup.js new file mode 100644 index 000000000..9b93a0427 --- /dev/null +++ b/branch-master/asset_codemirror_rollup.js @@ -0,0 +1,11481 @@ +/*jslint-disable*/ +/* +shRollupFetch +{ + "fetchList": [ + { + "comment": true, + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/LICENSE" + }, + { + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/codemirror.js", + "url2": "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.10/codemirror.js" + }, + { + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/addon/edit/matchbrackets.js" + }, + { + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/addon/edit/trailingspace.js" + }, + { + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/addon/lint/lint.js" + }, + { + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/addon/selection/active-line.js" + }, + { + "url": "https://github.com/codemirror/codemirror5/blob/5.65.10/mode/javascript/javascript.js" + } + ], + "replaceList": [] +} +*/ + + +/* +repo https://github.com/codemirror/codemirror5/tree/5.65.10 +committed 2022-11-20T15:35:33Z +*/ + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/LICENSE +*/ +/* +MIT License + +Copyright (C) 2017 by Marijn Haverbeke and others + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/codemirror.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +// This is CodeMirror (https://codemirror.net/5), a code editor +// implemented in JavaScript on top of the browser's DOM. +// +// You can find some technical background for some of the code below +// at http://marijnhaverbeke.nl/blog/#cm-internals . + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, global.CodeMirror = factory()); +}(this, (function () { 'use strict'; + + // Kludges for bugs and behavior differences that can't be feature + // detected are enabled based on userAgent etc sniffing. + var userAgent = navigator.userAgent; + var platform = navigator.platform; + + var gecko = /gecko\/\d/i.test(userAgent); + var ie_upto10 = /MSIE \d/.test(userAgent); + var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent); + var edge = /Edge\/(\d+)/.exec(userAgent); + var ie = ie_upto10 || ie_11up || edge; + var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]); + var webkit = !edge && /WebKit\//.test(userAgent); + var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent); + var chrome = !edge && /Chrome\/(\d+)/.exec(userAgent); + var chrome_version = chrome && +chrome[1]; + var presto = /Opera\//.test(userAgent); + var safari = /Apple Computer/.test(navigator.vendor); + var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent); + var phantom = /PhantomJS/.test(userAgent); + + var ios = safari && (/Mobile\/\w+/.test(userAgent) || navigator.maxTouchPoints > 2); + var android = /Android/.test(userAgent); + // This is woefully incomplete. Suggestions for alternative methods welcome. + var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent); + var mac = ios || /Mac/.test(platform); + var chromeOS = /\bCrOS\b/.test(userAgent); + var windows = /win/i.test(platform); + + var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/); + if (presto_version) { presto_version = Number(presto_version[1]); } + if (presto_version && presto_version >= 15) { presto = false; webkit = true; } + // Some browsers use the wrong event properties to signal cmd/ctrl on OS X + var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11)); + var captureRightClick = gecko || (ie && ie_version >= 9); + + function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") } + + var rmClass = function(node, cls) { + var current = node.className; + var match = classTest(cls).exec(current); + if (match) { + var after = current.slice(match.index + match[0].length); + node.className = current.slice(0, match.index) + (after ? match[1] + after : ""); + } + }; + + function removeChildren(e) { + for (var count = e.childNodes.length; count > 0; --count) + { e.removeChild(e.firstChild); } + return e + } + + function removeChildrenAndAdd(parent, e) { + return removeChildren(parent).appendChild(e) + } + + function elt(tag, content, className, style) { + var e = document.createElement(tag); + if (className) { e.className = className; } + if (style) { e.style.cssText = style; } + if (typeof content == "string") { e.appendChild(document.createTextNode(content)); } + else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } } + return e + } + // wrapper for elt, which removes the elt from the accessibility tree + function eltP(tag, content, className, style) { + var e = elt(tag, content, className, style); + e.setAttribute("role", "presentation"); + return e + } + + var range; + if (document.createRange) { range = function(node, start, end, endNode) { + var r = document.createRange(); + r.setEnd(endNode || node, end); + r.setStart(node, start); + return r + }; } + else { range = function(node, start, end) { + var r = document.body.createTextRange(); + try { r.moveToElementText(node.parentNode); } + catch(e) { return r } + r.collapse(true); + r.moveEnd("character", end); + r.moveStart("character", start); + return r + }; } + + function contains(parent, child) { + if (child.nodeType == 3) // Android browser always returns false when child is a textnode + { child = child.parentNode; } + if (parent.contains) + { return parent.contains(child) } + do { + if (child.nodeType == 11) { child = child.host; } + if (child == parent) { return true } + } while (child = child.parentNode) + } + + function activeElt(doc) { + // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement. + // IE < 10 will throw when accessed while the page is loading or in an iframe. + // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable. + var activeElement; + try { + activeElement = doc.activeElement; + } catch(e) { + activeElement = doc.body || null; + } + while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement) + { activeElement = activeElement.shadowRoot.activeElement; } + return activeElement + } + + function addClass(node, cls) { + var current = node.className; + if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; } + } + function joinClasses(a, b) { + var as = a.split(" "); + for (var i = 0; i < as.length; i++) + { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } } + return b + } + + var selectInput = function(node) { node.select(); }; + if (ios) // Mobile Safari apparently has a bug where select() is broken. + { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; } + else if (ie) // Suppress mysterious IE10 errors + { selectInput = function(node) { try { node.select(); } catch(_e) {} }; } + + function doc(cm) { return cm.display.wrapper.ownerDocument } + + function win(cm) { return doc(cm).defaultView } + + function bind(f) { + var args = Array.prototype.slice.call(arguments, 1); + return function(){return f.apply(null, args)} + } + + function copyObj(obj, target, overwrite) { + if (!target) { target = {}; } + for (var prop in obj) + { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop))) + { target[prop] = obj[prop]; } } + return target + } + + // Counts the column offset in a string, taking tabs into account. + // Used mostly to find indentation. + function countColumn(string, end, tabSize, startIndex, startValue) { + if (end == null) { + end = string.search(/[^\s\u00a0]/); + if (end == -1) { end = string.length; } + } + for (var i = startIndex || 0, n = startValue || 0;;) { + var nextTab = string.indexOf("\t", i); + if (nextTab < 0 || nextTab >= end) + { return n + (end - i) } + n += nextTab - i; + n += tabSize - (n % tabSize); + i = nextTab + 1; + } + } + + var Delayed = function() { + this.id = null; + this.f = null; + this.time = 0; + this.handler = bind(this.onTimeout, this); + }; + Delayed.prototype.onTimeout = function (self) { + self.id = 0; + if (self.time <= +new Date) { + self.f(); + } else { + setTimeout(self.handler, self.time - +new Date); + } + }; + Delayed.prototype.set = function (ms, f) { + this.f = f; + var time = +new Date + ms; + if (!this.id || time < this.time) { + clearTimeout(this.id); + this.id = setTimeout(this.handler, ms); + this.time = time; + } + }; + + function indexOf(array, elt) { + for (var i = 0; i < array.length; ++i) + { if (array[i] == elt) { return i } } + return -1 + } + + // Number of pixels added to scroller and sizer to hide scrollbar + var scrollerGap = 50; + + // Returned or thrown by various protocols to signal 'I'm not + // handling this'. + var Pass = {toString: function(){return "CodeMirror.Pass"}}; + + // Reused option objects for setSelection & friends + var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"}; + + // The inverse of countColumn -- find the offset that corresponds to + // a particular column. + function findColumn(string, goal, tabSize) { + for (var pos = 0, col = 0;;) { + var nextTab = string.indexOf("\t", pos); + if (nextTab == -1) { nextTab = string.length; } + var skipped = nextTab - pos; + if (nextTab == string.length || col + skipped >= goal) + { return pos + Math.min(skipped, goal - col) } + col += nextTab - pos; + col += tabSize - (col % tabSize); + pos = nextTab + 1; + if (col >= goal) { return pos } + } + } + + var spaceStrs = [""]; + function spaceStr(n) { + while (spaceStrs.length <= n) + { spaceStrs.push(lst(spaceStrs) + " "); } + return spaceStrs[n] + } + + function lst(arr) { return arr[arr.length-1] } + + function map(array, f) { + var out = []; + for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); } + return out + } + + function insertSorted(array, value, score) { + var pos = 0, priority = score(value); + while (pos < array.length && score(array[pos]) <= priority) { pos++; } + array.splice(pos, 0, value); + } + + function nothing() {} + + function createObj(base, props) { + var inst; + if (Object.create) { + inst = Object.create(base); + } else { + nothing.prototype = base; + inst = new nothing(); + } + if (props) { copyObj(props, inst); } + return inst + } + + var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; + function isWordCharBasic(ch) { + return /\w/.test(ch) || ch > "\x80" && + (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)) + } + function isWordChar(ch, helper) { + if (!helper) { return isWordCharBasic(ch) } + if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true } + return helper.test(ch) + } + + function isEmpty(obj) { + for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } } + return true + } + + // Extending unicode characters. A series of a non-extending char + + // any number of extending chars is treated as a single unit as far + // as editing and measuring is concerned. This is not fully correct, + // since some scripts/fonts/browsers also treat other configurations + // of code points as a group. + var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/; + function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) } + + // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range. + function skipExtendingChars(str, pos, dir) { + while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; } + return pos + } + + // Returns the value from the range [`from`; `to`] that satisfies + // `pred` and is closest to `from`. Assumes that at least `to` + // satisfies `pred`. Supports `from` being greater than `to`. + function findFirst(pred, from, to) { + // At any point we are certain `to` satisfies `pred`, don't know + // whether `from` does. + var dir = from > to ? -1 : 1; + for (;;) { + if (from == to) { return from } + var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF); + if (mid == from) { return pred(mid) ? from : to } + if (pred(mid)) { to = mid; } + else { from = mid + dir; } + } + } + + // BIDI HELPERS + + function iterateBidiSections(order, from, to, f) { + if (!order) { return f(from, to, "ltr", 0) } + var found = false; + for (var i = 0; i < order.length; ++i) { + var part = order[i]; + if (part.from < to && part.to > from || from == to && part.to == from) { + f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i); + found = true; + } + } + if (!found) { f(from, to, "ltr"); } + } + + var bidiOther = null; + function getBidiPartAt(order, ch, sticky) { + var found; + bidiOther = null; + for (var i = 0; i < order.length; ++i) { + var cur = order[i]; + if (cur.from < ch && cur.to > ch) { return i } + if (cur.to == ch) { + if (cur.from != cur.to && sticky == "before") { found = i; } + else { bidiOther = i; } + } + if (cur.from == ch) { + if (cur.from != cur.to && sticky != "before") { found = i; } + else { bidiOther = i; } + } + } + return found != null ? found : bidiOther + } + + // Bidirectional ordering algorithm + // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm + // that this (partially) implements. + + // One-char codes used for character types: + // L (L): Left-to-Right + // R (R): Right-to-Left + // r (AL): Right-to-Left Arabic + // 1 (EN): European Number + // + (ES): European Number Separator + // % (ET): European Number Terminator + // n (AN): Arabic Number + // , (CS): Common Number Separator + // m (NSM): Non-Spacing Mark + // b (BN): Boundary Neutral + // s (B): Paragraph Separator + // t (S): Segment Separator + // w (WS): Whitespace + // N (ON): Other Neutrals + + // Returns null if characters are ordered as they appear + // (left-to-right), or an array of sections ({from, to, level} + // objects) in the order in which they occur visually. + var bidiOrdering = (function() { + // Character types for codepoints 0 to 0xff + var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"; + // Character types for codepoints 0x600 to 0x6f9 + var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111"; + function charType(code) { + if (code <= 0xf7) { return lowTypes.charAt(code) } + else if (0x590 <= code && code <= 0x5f4) { return "R" } + else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) } + else if (0x6ee <= code && code <= 0x8ac) { return "r" } + else if (0x2000 <= code && code <= 0x200b) { return "w" } + else if (code == 0x200c) { return "b" } + else { return "L" } + } + + var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/; + var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/; + + function BidiSpan(level, from, to) { + this.level = level; + this.from = from; this.to = to; + } + + return function(str, direction) { + var outerType = direction == "ltr" ? "L" : "R"; + + if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false } + var len = str.length, types = []; + for (var i = 0; i < len; ++i) + { types.push(charType(str.charCodeAt(i))); } + + // W1. Examine each non-spacing mark (NSM) in the level run, and + // change the type of the NSM to the type of the previous + // character. If the NSM is at the start of the level run, it will + // get the type of sor. + for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) { + var type = types[i$1]; + if (type == "m") { types[i$1] = prev; } + else { prev = type; } + } + + // W2. Search backwards from each instance of a European number + // until the first strong type (R, L, AL, or sor) is found. If an + // AL is found, change the type of the European number to Arabic + // number. + // W3. Change all ALs to R. + for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) { + var type$1 = types[i$2]; + if (type$1 == "1" && cur == "r") { types[i$2] = "n"; } + else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } } + } + + // W4. A single European separator between two European numbers + // changes to a European number. A single common separator between + // two numbers of the same type changes to that type. + for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) { + var type$2 = types[i$3]; + if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; } + else if (type$2 == "," && prev$1 == types[i$3+1] && + (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; } + prev$1 = type$2; + } + + // W5. A sequence of European terminators adjacent to European + // numbers changes to all European numbers. + // W6. Otherwise, separators and terminators change to Other + // Neutral. + for (var i$4 = 0; i$4 < len; ++i$4) { + var type$3 = types[i$4]; + if (type$3 == ",") { types[i$4] = "N"; } + else if (type$3 == "%") { + var end = (void 0); + for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {} + var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"; + for (var j = i$4; j < end; ++j) { types[j] = replace; } + i$4 = end - 1; + } + } + + // W7. Search backwards from each instance of a European number + // until the first strong type (R, L, or sor) is found. If an L is + // found, then change the type of the European number to L. + for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) { + var type$4 = types[i$5]; + if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; } + else if (isStrong.test(type$4)) { cur$1 = type$4; } + } + + // N1. A sequence of neutrals takes the direction of the + // surrounding strong text if the text on both sides has the same + // direction. European and Arabic numbers act as if they were R in + // terms of their influence on neutrals. Start-of-level-run (sor) + // and end-of-level-run (eor) are used at level run boundaries. + // N2. Any remaining neutrals take the embedding direction. + for (var i$6 = 0; i$6 < len; ++i$6) { + if (isNeutral.test(types[i$6])) { + var end$1 = (void 0); + for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {} + var before = (i$6 ? types[i$6-1] : outerType) == "L"; + var after = (end$1 < len ? types[end$1] : outerType) == "L"; + var replace$1 = before == after ? (before ? "L" : "R") : outerType; + for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; } + i$6 = end$1 - 1; + } + } + + // Here we depart from the documented algorithm, in order to avoid + // building up an actual levels array. Since there are only three + // levels (0, 1, 2) in an implementation that doesn't take + // explicit embedding into account, we can build up the order on + // the fly, without following the level-based algorithm. + var order = [], m; + for (var i$7 = 0; i$7 < len;) { + if (countsAsLeft.test(types[i$7])) { + var start = i$7; + for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {} + order.push(new BidiSpan(0, start, i$7)); + } else { + var pos = i$7, at = order.length, isRTL = direction == "rtl" ? 1 : 0; + for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {} + for (var j$2 = pos; j$2 < i$7;) { + if (countsAsNum.test(types[j$2])) { + if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); at += isRTL; } + var nstart = j$2; + for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {} + order.splice(at, 0, new BidiSpan(2, nstart, j$2)); + at += isRTL; + pos = j$2; + } else { ++j$2; } + } + if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); } + } + } + if (direction == "ltr") { + if (order[0].level == 1 && (m = str.match(/^\s+/))) { + order[0].from = m[0].length; + order.unshift(new BidiSpan(0, 0, m[0].length)); + } + if (lst(order).level == 1 && (m = str.match(/\s+$/))) { + lst(order).to -= m[0].length; + order.push(new BidiSpan(0, len - m[0].length, len)); + } + } + + return direction == "rtl" ? order.reverse() : order + } + })(); + + // Get the bidi ordering for the given line (and cache it). Returns + // false for lines that are fully left-to-right, and an array of + // BidiSpan objects otherwise. + function getOrder(line, direction) { + var order = line.order; + if (order == null) { order = line.order = bidiOrdering(line.text, direction); } + return order + } + + // EVENT HANDLING + + // Lightweight event framework. on/off also work on DOM nodes, + // registering native DOM handlers. + + var noHandlers = []; + + var on = function(emitter, type, f) { + if (emitter.addEventListener) { + emitter.addEventListener(type, f, false); + } else if (emitter.attachEvent) { + emitter.attachEvent("on" + type, f); + } else { + var map = emitter._handlers || (emitter._handlers = {}); + map[type] = (map[type] || noHandlers).concat(f); + } + }; + + function getHandlers(emitter, type) { + return emitter._handlers && emitter._handlers[type] || noHandlers + } + + function off(emitter, type, f) { + if (emitter.removeEventListener) { + emitter.removeEventListener(type, f, false); + } else if (emitter.detachEvent) { + emitter.detachEvent("on" + type, f); + } else { + var map = emitter._handlers, arr = map && map[type]; + if (arr) { + var index = indexOf(arr, f); + if (index > -1) + { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)); } + } + } + } + + function signal(emitter, type /*, values...*/) { + var handlers = getHandlers(emitter, type); + if (!handlers.length) { return } + var args = Array.prototype.slice.call(arguments, 2); + for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); } + } + + // The DOM events that CodeMirror handles can be overridden by + // registering a (non-DOM) handler on the editor for the event name, + // and preventDefault-ing the event in that handler. + function signalDOMEvent(cm, e, override) { + if (typeof e == "string") + { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; } + signal(cm, override || e.type, cm, e); + return e_defaultPrevented(e) || e.codemirrorIgnore + } + + function signalCursorActivity(cm) { + var arr = cm._handlers && cm._handlers.cursorActivity; + if (!arr) { return } + var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []); + for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1) + { set.push(arr[i]); } } + } + + function hasHandler(emitter, type) { + return getHandlers(emitter, type).length > 0 + } + + // Add on and off methods to a constructor's prototype, to make + // registering events on such objects more convenient. + function eventMixin(ctor) { + ctor.prototype.on = function(type, f) {on(this, type, f);}; + ctor.prototype.off = function(type, f) {off(this, type, f);}; + } + + // Due to the fact that we still support jurassic IE versions, some + // compatibility wrappers are needed. + + function e_preventDefault(e) { + if (e.preventDefault) { e.preventDefault(); } + else { e.returnValue = false; } + } + function e_stopPropagation(e) { + if (e.stopPropagation) { e.stopPropagation(); } + else { e.cancelBubble = true; } + } + function e_defaultPrevented(e) { + return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false + } + function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);} + + function e_target(e) {return e.target || e.srcElement} + function e_button(e) { + var b = e.which; + if (b == null) { + if (e.button & 1) { b = 1; } + else if (e.button & 2) { b = 3; } + else if (e.button & 4) { b = 2; } + } + if (mac && e.ctrlKey && b == 1) { b = 3; } + return b + } + + // Detect drag-and-drop + var dragAndDrop = function() { + // There is *some* kind of drag-and-drop support in IE6-8, but I + // couldn't get it to work yet. + if (ie && ie_version < 9) { return false } + var div = elt('div'); + return "draggable" in div || "dragDrop" in div + }(); + + var zwspSupported; + function zeroWidthElement(measure) { + if (zwspSupported == null) { + var test = elt("span", "\u200b"); + removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")])); + if (measure.firstChild.offsetHeight != 0) + { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); } + } + var node = zwspSupported ? elt("span", "\u200b") : + elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px"); + node.setAttribute("cm-text", ""); + return node + } + + // Feature-detect IE's crummy client rect reporting for bidi text + var badBidiRects; + function hasBadBidiRects(measure) { + if (badBidiRects != null) { return badBidiRects } + var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA")); + var r0 = range(txt, 0, 1).getBoundingClientRect(); + var r1 = range(txt, 1, 2).getBoundingClientRect(); + removeChildren(measure); + if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780) + return badBidiRects = (r1.right - r0.right < 3) + } + + // See if "".split is the broken IE version, if so, provide an + // alternative way to split lines. + var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) { + var pos = 0, result = [], l = string.length; + while (pos <= l) { + var nl = string.indexOf("\n", pos); + if (nl == -1) { nl = string.length; } + var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl); + var rt = line.indexOf("\r"); + if (rt != -1) { + result.push(line.slice(0, rt)); + pos += rt + 1; + } else { + result.push(line); + pos = nl + 1; + } + } + return result + } : function (string) { return string.split(/\r\n?|\n/); }; + + var hasSelection = window.getSelection ? function (te) { + try { return te.selectionStart != te.selectionEnd } + catch(e) { return false } + } : function (te) { + var range; + try {range = te.ownerDocument.selection.createRange();} + catch(e) {} + if (!range || range.parentElement() != te) { return false } + return range.compareEndPoints("StartToEnd", range) != 0 + }; + + var hasCopyEvent = (function () { + var e = elt("div"); + if ("oncopy" in e) { return true } + e.setAttribute("oncopy", "return;"); + return typeof e.oncopy == "function" + })(); + + var badZoomedRects = null; + function hasBadZoomedRects(measure) { + if (badZoomedRects != null) { return badZoomedRects } + var node = removeChildrenAndAdd(measure, elt("span", "x")); + var normal = node.getBoundingClientRect(); + var fromRange = range(node, 0, 1).getBoundingClientRect(); + return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1 + } + + // Known modes, by name and by MIME + var modes = {}, mimeModes = {}; + + // Extra arguments are stored as the mode's dependencies, which is + // used by (legacy) mechanisms like loadmode.js to automatically + // load a mode. (Preferred mechanism is the require/define calls.) + function defineMode(name, mode) { + if (arguments.length > 2) + { mode.dependencies = Array.prototype.slice.call(arguments, 2); } + modes[name] = mode; + } + + function defineMIME(mime, spec) { + mimeModes[mime] = spec; + } + + // Given a MIME type, a {name, ...options} config object, or a name + // string, return a mode config object. + function resolveMode(spec) { + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { + spec = mimeModes[spec]; + } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { + var found = mimeModes[spec.name]; + if (typeof found == "string") { found = {name: found}; } + spec = createObj(found, spec); + spec.name = found.name; + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { + return resolveMode("application/xml") + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) { + return resolveMode("application/json") + } + if (typeof spec == "string") { return {name: spec} } + else { return spec || {name: "null"} } + } + + // Given a mode spec (anything that resolveMode accepts), find and + // initialize an actual mode object. + function getMode(options, spec) { + spec = resolveMode(spec); + var mfactory = modes[spec.name]; + if (!mfactory) { return getMode(options, "text/plain") } + var modeObj = mfactory(options, spec); + if (modeExtensions.hasOwnProperty(spec.name)) { + var exts = modeExtensions[spec.name]; + for (var prop in exts) { + if (!exts.hasOwnProperty(prop)) { continue } + if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; } + modeObj[prop] = exts[prop]; + } + } + modeObj.name = spec.name; + if (spec.helperType) { modeObj.helperType = spec.helperType; } + if (spec.modeProps) { for (var prop$1 in spec.modeProps) + { modeObj[prop$1] = spec.modeProps[prop$1]; } } + + return modeObj + } + + // This can be used to attach properties to mode objects from + // outside the actual mode definition. + var modeExtensions = {}; + function extendMode(mode, properties) { + var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); + copyObj(properties, exts); + } + + function copyState(mode, state) { + if (state === true) { return state } + if (mode.copyState) { return mode.copyState(state) } + var nstate = {}; + for (var n in state) { + var val = state[n]; + if (val instanceof Array) { val = val.concat([]); } + nstate[n] = val; + } + return nstate + } + + // Given a mode and a state (for that mode), find the inner mode and + // state at the position that the state refers to. + function innerMode(mode, state) { + var info; + while (mode.innerMode) { + info = mode.innerMode(state); + if (!info || info.mode == mode) { break } + state = info.state; + mode = info.mode; + } + return info || {mode: mode, state: state} + } + + function startState(mode, a1, a2) { + return mode.startState ? mode.startState(a1, a2) : true + } + + // STRING STREAM + + // Fed to the mode parsers, provides helper functions to make + // parsers more succinct. + + var StringStream = function(string, tabSize, lineOracle) { + this.pos = this.start = 0; + this.string = string; + this.tabSize = tabSize || 8; + this.lastColumnPos = this.lastColumnValue = 0; + this.lineStart = 0; + this.lineOracle = lineOracle; + }; + + StringStream.prototype.eol = function () {return this.pos >= this.string.length}; + StringStream.prototype.sol = function () {return this.pos == this.lineStart}; + StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined}; + StringStream.prototype.next = function () { + if (this.pos < this.string.length) + { return this.string.charAt(this.pos++) } + }; + StringStream.prototype.eat = function (match) { + var ch = this.string.charAt(this.pos); + var ok; + if (typeof match == "string") { ok = ch == match; } + else { ok = ch && (match.test ? match.test(ch) : match(ch)); } + if (ok) {++this.pos; return ch} + }; + StringStream.prototype.eatWhile = function (match) { + var start = this.pos; + while (this.eat(match)){} + return this.pos > start + }; + StringStream.prototype.eatSpace = function () { + var start = this.pos; + while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; } + return this.pos > start + }; + StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;}; + StringStream.prototype.skipTo = function (ch) { + var found = this.string.indexOf(ch, this.pos); + if (found > -1) {this.pos = found; return true} + }; + StringStream.prototype.backUp = function (n) {this.pos -= n;}; + StringStream.prototype.column = function () { + if (this.lastColumnPos < this.start) { + this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue); + this.lastColumnPos = this.start; + } + return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) + }; + StringStream.prototype.indentation = function () { + return countColumn(this.string, null, this.tabSize) - + (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) + }; + StringStream.prototype.match = function (pattern, consume, caseInsensitive) { + if (typeof pattern == "string") { + var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; }; + var substr = this.string.substr(this.pos, pattern.length); + if (cased(substr) == cased(pattern)) { + if (consume !== false) { this.pos += pattern.length; } + return true + } + } else { + var match = this.string.slice(this.pos).match(pattern); + if (match && match.index > 0) { return null } + if (match && consume !== false) { this.pos += match[0].length; } + return match + } + }; + StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)}; + StringStream.prototype.hideFirstChars = function (n, inner) { + this.lineStart += n; + try { return inner() } + finally { this.lineStart -= n; } + }; + StringStream.prototype.lookAhead = function (n) { + var oracle = this.lineOracle; + return oracle && oracle.lookAhead(n) + }; + StringStream.prototype.baseToken = function () { + var oracle = this.lineOracle; + return oracle && oracle.baseToken(this.pos) + }; + + // Find the line object corresponding to the given line number. + function getLine(doc, n) { + n -= doc.first; + if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") } + var chunk = doc; + while (!chunk.lines) { + for (var i = 0;; ++i) { + var child = chunk.children[i], sz = child.chunkSize(); + if (n < sz) { chunk = child; break } + n -= sz; + } + } + return chunk.lines[n] + } + + // Get the part of a document between two positions, as an array of + // strings. + function getBetween(doc, start, end) { + var out = [], n = start.line; + doc.iter(start.line, end.line + 1, function (line) { + var text = line.text; + if (n == end.line) { text = text.slice(0, end.ch); } + if (n == start.line) { text = text.slice(start.ch); } + out.push(text); + ++n; + }); + return out + } + // Get the lines between from and to, as array of strings. + function getLines(doc, from, to) { + var out = []; + doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value + return out + } + + // Update the height of a line, propagating the height change + // upwards to parent nodes. + function updateLineHeight(line, height) { + var diff = height - line.height; + if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } } + } + + // Given a line object, find its line number by walking up through + // its parent links. + function lineNo(line) { + if (line.parent == null) { return null } + var cur = line.parent, no = indexOf(cur.lines, line); + for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { + for (var i = 0;; ++i) { + if (chunk.children[i] == cur) { break } + no += chunk.children[i].chunkSize(); + } + } + return no + cur.first + } + + // Find the line at the given vertical position, using the height + // information in the document tree. + function lineAtHeight(chunk, h) { + var n = chunk.first; + outer: do { + for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) { + var child = chunk.children[i$1], ch = child.height; + if (h < ch) { chunk = child; continue outer } + h -= ch; + n += child.chunkSize(); + } + return n + } while (!chunk.lines) + var i = 0; + for (; i < chunk.lines.length; ++i) { + var line = chunk.lines[i], lh = line.height; + if (h < lh) { break } + h -= lh; + } + return n + i + } + + function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size} + + function lineNumberFor(options, i) { + return String(options.lineNumberFormatter(i + options.firstLineNumber)) + } + + // A Pos instance represents a position within the text. + function Pos(line, ch, sticky) { + if ( sticky === void 0 ) sticky = null; + + if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) } + this.line = line; + this.ch = ch; + this.sticky = sticky; + } + + // Compare two positions, return 0 if they are the same, a negative + // number when a is less, and a positive number otherwise. + function cmp(a, b) { return a.line - b.line || a.ch - b.ch } + + function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 } + + function copyPos(x) {return Pos(x.line, x.ch)} + function maxPos(a, b) { return cmp(a, b) < 0 ? b : a } + function minPos(a, b) { return cmp(a, b) < 0 ? a : b } + + // Most of the external API clips given positions to make sure they + // actually exist within the document. + function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))} + function clipPos(doc, pos) { + if (pos.line < doc.first) { return Pos(doc.first, 0) } + var last = doc.first + doc.size - 1; + if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) } + return clipToLen(pos, getLine(doc, pos.line).text.length) + } + function clipToLen(pos, linelen) { + var ch = pos.ch; + if (ch == null || ch > linelen) { return Pos(pos.line, linelen) } + else if (ch < 0) { return Pos(pos.line, 0) } + else { return pos } + } + function clipPosArray(doc, array) { + var out = []; + for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); } + return out + } + + var SavedContext = function(state, lookAhead) { + this.state = state; + this.lookAhead = lookAhead; + }; + + var Context = function(doc, state, line, lookAhead) { + this.state = state; + this.doc = doc; + this.line = line; + this.maxLookAhead = lookAhead || 0; + this.baseTokens = null; + this.baseTokenPos = 1; + }; + + Context.prototype.lookAhead = function (n) { + var line = this.doc.getLine(this.line + n); + if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; } + return line + }; + + Context.prototype.baseToken = function (n) { + if (!this.baseTokens) { return null } + while (this.baseTokens[this.baseTokenPos] <= n) + { this.baseTokenPos += 2; } + var type = this.baseTokens[this.baseTokenPos + 1]; + return {type: type && type.replace(/( |^)overlay .*/, ""), + size: this.baseTokens[this.baseTokenPos] - n} + }; + + Context.prototype.nextLine = function () { + this.line++; + if (this.maxLookAhead > 0) { this.maxLookAhead--; } + }; + + Context.fromSaved = function (doc, saved, line) { + if (saved instanceof SavedContext) + { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) } + else + { return new Context(doc, copyState(doc.mode, saved), line) } + }; + + Context.prototype.save = function (copy) { + var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state; + return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state + }; + + + // Compute a style array (an array starting with a mode generation + // -- for invalidation -- followed by pairs of end positions and + // style strings), which is used to highlight the tokens on the + // line. + function highlightLine(cm, line, context, forceToEnd) { + // A styles array always starts with a number identifying the + // mode/overlays that it is based on (for easy invalidation). + var st = [cm.state.modeGen], lineClasses = {}; + // Compute the base array of styles + runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); }, + lineClasses, forceToEnd); + var state = context.state; + + // Run overlays, adjust style array. + var loop = function ( o ) { + context.baseTokens = st; + var overlay = cm.state.overlays[o], i = 1, at = 0; + context.state = true; + runMode(cm, line.text, overlay.mode, context, function (end, style) { + var start = i; + // Ensure there's a token end at the current position, and that i points at it + while (at < end) { + var i_end = st[i]; + if (i_end > end) + { st.splice(i, 1, end, st[i+1], i_end); } + i += 2; + at = Math.min(end, i_end); + } + if (!style) { return } + if (overlay.opaque) { + st.splice(start, i - start, end, "overlay " + style); + i = start + 2; + } else { + for (; start < i; start += 2) { + var cur = st[start+1]; + st[start+1] = (cur ? cur + " " : "") + "overlay " + style; + } + } + }, lineClasses); + context.state = state; + context.baseTokens = null; + context.baseTokenPos = 1; + }; + + for (var o = 0; o < cm.state.overlays.length; ++o) loop( o ); + + return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null} + } + + function getLineStyles(cm, line, updateFrontier) { + if (!line.styles || line.styles[0] != cm.state.modeGen) { + var context = getContextBefore(cm, lineNo(line)); + var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state); + var result = highlightLine(cm, line, context); + if (resetState) { context.state = resetState; } + line.stateAfter = context.save(!resetState); + line.styles = result.styles; + if (result.classes) { line.styleClasses = result.classes; } + else if (line.styleClasses) { line.styleClasses = null; } + if (updateFrontier === cm.doc.highlightFrontier) + { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); } + } + return line.styles + } + + function getContextBefore(cm, n, precise) { + var doc = cm.doc, display = cm.display; + if (!doc.mode.startState) { return new Context(doc, true, n) } + var start = findStartLine(cm, n, precise); + var saved = start > doc.first && getLine(doc, start - 1).stateAfter; + var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start); + + doc.iter(start, n, function (line) { + processLine(cm, line.text, context); + var pos = context.line; + line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null; + context.nextLine(); + }); + if (precise) { doc.modeFrontier = context.line; } + return context + } + + // Lightweight form of highlight -- proceed over this line and + // update state, but don't save a style array. Used for lines that + // aren't currently visible. + function processLine(cm, text, context, startAt) { + var mode = cm.doc.mode; + var stream = new StringStream(text, cm.options.tabSize, context); + stream.start = stream.pos = startAt || 0; + if (text == "") { callBlankLine(mode, context.state); } + while (!stream.eol()) { + readToken(mode, stream, context.state); + stream.start = stream.pos; + } + } + + function callBlankLine(mode, state) { + if (mode.blankLine) { return mode.blankLine(state) } + if (!mode.innerMode) { return } + var inner = innerMode(mode, state); + if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) } + } + + function readToken(mode, stream, state, inner) { + for (var i = 0; i < 10; i++) { + if (inner) { inner[0] = innerMode(mode, state).mode; } + var style = mode.token(stream, state); + if (stream.pos > stream.start) { return style } + } + throw new Error("Mode " + mode.name + " failed to advance stream.") + } + + var Token = function(stream, type, state) { + this.start = stream.start; this.end = stream.pos; + this.string = stream.current(); + this.type = type || null; + this.state = state; + }; + + // Utility for getTokenAt and getLineTokens + function takeToken(cm, pos, precise, asArray) { + var doc = cm.doc, mode = doc.mode, style; + pos = clipPos(doc, pos); + var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise); + var stream = new StringStream(line.text, cm.options.tabSize, context), tokens; + if (asArray) { tokens = []; } + while ((asArray || stream.pos < pos.ch) && !stream.eol()) { + stream.start = stream.pos; + style = readToken(mode, stream, context.state); + if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); } + } + return asArray ? tokens : new Token(stream, style, context.state) + } + + function extractLineClasses(type, output) { + if (type) { for (;;) { + var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/); + if (!lineClass) { break } + type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length); + var prop = lineClass[1] ? "bgClass" : "textClass"; + if (output[prop] == null) + { output[prop] = lineClass[2]; } + else if (!(new RegExp("(?:^|\\s)" + lineClass[2] + "(?:$|\\s)")).test(output[prop])) + { output[prop] += " " + lineClass[2]; } + } } + return type + } + + // Run the given mode's parser over a line, calling f for each token. + function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) { + var flattenSpans = mode.flattenSpans; + if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; } + var curStart = 0, curStyle = null; + var stream = new StringStream(text, cm.options.tabSize, context), style; + var inner = cm.options.addModeClass && [null]; + if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); } + while (!stream.eol()) { + if (stream.pos > cm.options.maxHighlightLength) { + flattenSpans = false; + if (forceToEnd) { processLine(cm, text, context, stream.pos); } + stream.pos = text.length; + style = null; + } else { + style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses); + } + if (inner) { + var mName = inner[0].name; + if (mName) { style = "m-" + (style ? mName + " " + style : mName); } + } + if (!flattenSpans || curStyle != style) { + while (curStart < stream.start) { + curStart = Math.min(stream.start, curStart + 5000); + f(curStart, curStyle); + } + curStyle = style; + } + stream.start = stream.pos; + } + while (curStart < stream.pos) { + // Webkit seems to refuse to render text nodes longer than 57444 + // characters, and returns inaccurate measurements in nodes + // starting around 5000 chars. + var pos = Math.min(stream.pos, curStart + 5000); + f(pos, curStyle); + curStart = pos; + } + } + + // Finds the line to start with when starting a parse. Tries to + // find a line with a stateAfter, so that it can start with a + // valid state. If that fails, it returns the line with the + // smallest indentation, which tends to need the least context to + // parse correctly. + function findStartLine(cm, n, precise) { + var minindent, minline, doc = cm.doc; + var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100); + for (var search = n; search > lim; --search) { + if (search <= doc.first) { return doc.first } + var line = getLine(doc, search - 1), after = line.stateAfter; + if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier)) + { return search } + var indented = countColumn(line.text, null, cm.options.tabSize); + if (minline == null || minindent > indented) { + minline = search - 1; + minindent = indented; + } + } + return minline + } + + function retreatFrontier(doc, n) { + doc.modeFrontier = Math.min(doc.modeFrontier, n); + if (doc.highlightFrontier < n - 10) { return } + var start = doc.first; + for (var line = n - 1; line > start; line--) { + var saved = getLine(doc, line).stateAfter; + // change is on 3 + // state on line 1 looked ahead 2 -- so saw 3 + // test 1 + 2 < 3 should cover this + if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) { + start = line + 1; + break + } + } + doc.highlightFrontier = Math.min(doc.highlightFrontier, start); + } + + // Optimize some code when these features are not used. + var sawReadOnlySpans = false, sawCollapsedSpans = false; + + function seeReadOnlySpans() { + sawReadOnlySpans = true; + } + + function seeCollapsedSpans() { + sawCollapsedSpans = true; + } + + // TEXTMARKER SPANS + + function MarkedSpan(marker, from, to) { + this.marker = marker; + this.from = from; this.to = to; + } + + // Search an array of spans for a span matching the given marker. + function getMarkedSpanFor(spans, marker) { + if (spans) { for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.marker == marker) { return span } + } } + } + + // Remove a span from an array, returning undefined if no spans are + // left (we don't store arrays for lines without spans). + function removeMarkedSpan(spans, span) { + var r; + for (var i = 0; i < spans.length; ++i) + { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } } + return r + } + + // Add a span to a line. + function addMarkedSpan(line, span, op) { + var inThisOp = op && window.WeakSet && (op.markedSpans || (op.markedSpans = new WeakSet)); + if (inThisOp && line.markedSpans && inThisOp.has(line.markedSpans)) { + line.markedSpans.push(span); + } else { + line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]; + if (inThisOp) { inThisOp.add(line.markedSpans); } + } + span.marker.attachLine(line); + } + + // Used for the algorithm that adjusts markers for a change in the + // document. These functions cut an array of spans at a given + // character position, returning an array of remaining chunks (or + // undefined if nothing remains). + function markedSpansBefore(old, startCh, isInsert) { + var nw; + if (old) { for (var i = 0; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh); + if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) { + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh) + ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to)); + } + } } + return nw + } + function markedSpansAfter(old, endCh, isInsert) { + var nw; + if (old) { for (var i = 0; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh); + if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) { + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh) + ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh, + span.to == null ? null : span.to - endCh)); + } + } } + return nw + } + + // Given a change object, compute the new set of marker spans that + // cover the line in which the change took place. Removes spans + // entirely within the change, reconnects spans belonging to the + // same marker that appear on both sides of the change, and cuts off + // spans partially within the change. Returns an array of span + // arrays with one element for each line in (after) the change. + function stretchSpansOverChange(doc, change) { + if (change.full) { return null } + var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans; + var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans; + if (!oldFirst && !oldLast) { return null } + + var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0; + // Get the spans that 'stick out' on both sides + var first = markedSpansBefore(oldFirst, startCh, isInsert); + var last = markedSpansAfter(oldLast, endCh, isInsert); + + // Next, merge those two ends + var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0); + if (first) { + // Fix up .to properties of first + for (var i = 0; i < first.length; ++i) { + var span = first[i]; + if (span.to == null) { + var found = getMarkedSpanFor(last, span.marker); + if (!found) { span.to = startCh; } + else if (sameLine) { span.to = found.to == null ? null : found.to + offset; } + } + } + } + if (last) { + // Fix up .from in last (or move them into first in case of sameLine) + for (var i$1 = 0; i$1 < last.length; ++i$1) { + var span$1 = last[i$1]; + if (span$1.to != null) { span$1.to += offset; } + if (span$1.from == null) { + var found$1 = getMarkedSpanFor(first, span$1.marker); + if (!found$1) { + span$1.from = offset; + if (sameLine) { (first || (first = [])).push(span$1); } + } + } else { + span$1.from += offset; + if (sameLine) { (first || (first = [])).push(span$1); } + } + } + } + // Make sure we didn't create any zero-length spans + if (first) { first = clearEmptySpans(first); } + if (last && last != first) { last = clearEmptySpans(last); } + + var newMarkers = [first]; + if (!sameLine) { + // Fill gap with whole-line-spans + var gap = change.text.length - 2, gapMarkers; + if (gap > 0 && first) + { for (var i$2 = 0; i$2 < first.length; ++i$2) + { if (first[i$2].to == null) + { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } } + for (var i$3 = 0; i$3 < gap; ++i$3) + { newMarkers.push(gapMarkers); } + newMarkers.push(last); + } + return newMarkers + } + + // Remove spans that are empty and don't have a clearWhenEmpty + // option of false. + function clearEmptySpans(spans) { + for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false) + { spans.splice(i--, 1); } + } + if (!spans.length) { return null } + return spans + } + + // Used to 'clip' out readOnly ranges when making a change. + function removeReadOnlyRanges(doc, from, to) { + var markers = null; + doc.iter(from.line, to.line + 1, function (line) { + if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { + var mark = line.markedSpans[i].marker; + if (mark.readOnly && (!markers || indexOf(markers, mark) == -1)) + { (markers || (markers = [])).push(mark); } + } } + }); + if (!markers) { return null } + var parts = [{from: from, to: to}]; + for (var i = 0; i < markers.length; ++i) { + var mk = markers[i], m = mk.find(0); + for (var j = 0; j < parts.length; ++j) { + var p = parts[j]; + if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue } + var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to); + if (dfrom < 0 || !mk.inclusiveLeft && !dfrom) + { newParts.push({from: p.from, to: m.from}); } + if (dto > 0 || !mk.inclusiveRight && !dto) + { newParts.push({from: m.to, to: p.to}); } + parts.splice.apply(parts, newParts); + j += newParts.length - 3; + } + } + return parts + } + + // Connect or disconnect spans from a line. + function detachMarkedSpans(line) { + var spans = line.markedSpans; + if (!spans) { return } + for (var i = 0; i < spans.length; ++i) + { spans[i].marker.detachLine(line); } + line.markedSpans = null; + } + function attachMarkedSpans(line, spans) { + if (!spans) { return } + for (var i = 0; i < spans.length; ++i) + { spans[i].marker.attachLine(line); } + line.markedSpans = spans; + } + + // Helpers used when computing which overlapping collapsed span + // counts as the larger one. + function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 } + function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 } + + // Returns a number indicating which of two overlapping collapsed + // spans is larger (and thus includes the other). Falls back to + // comparing ids when the spans cover exactly the same range. + function compareCollapsedMarkers(a, b) { + var lenDiff = a.lines.length - b.lines.length; + if (lenDiff != 0) { return lenDiff } + var aPos = a.find(), bPos = b.find(); + var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b); + if (fromCmp) { return -fromCmp } + var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b); + if (toCmp) { return toCmp } + return b.id - a.id + } + + // Find out whether a line ends or starts in a collapsed span. If + // so, return the marker for that span. + function collapsedSpanAtSide(line, start) { + var sps = sawCollapsedSpans && line.markedSpans, found; + if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { + sp = sps[i]; + if (sp.marker.collapsed && (start ? sp.from : sp.to) == null && + (!found || compareCollapsedMarkers(found, sp.marker) < 0)) + { found = sp.marker; } + } } + return found + } + function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) } + function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) } + + function collapsedSpanAround(line, ch) { + var sps = sawCollapsedSpans && line.markedSpans, found; + if (sps) { for (var i = 0; i < sps.length; ++i) { + var sp = sps[i]; + if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) && + (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; } + } } + return found + } + + // Test whether there exists a collapsed span that partially + // overlaps (covers the start or end, but not both) of a new span. + // Such overlap is not allowed. + function conflictingCollapsedRange(doc, lineNo, from, to, marker) { + var line = getLine(doc, lineNo); + var sps = sawCollapsedSpans && line.markedSpans; + if (sps) { for (var i = 0; i < sps.length; ++i) { + var sp = sps[i]; + if (!sp.marker.collapsed) { continue } + var found = sp.marker.find(0); + var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker); + var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker); + if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue } + if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) || + fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0)) + { return true } + } } + } + + // A visual line is a line as drawn on the screen. Folding, for + // example, can cause multiple logical lines to appear on the same + // visual line. This finds the start of the visual line that the + // given line is part of (usually that is the line itself). + function visualLine(line) { + var merged; + while (merged = collapsedSpanAtStart(line)) + { line = merged.find(-1, true).line; } + return line + } + + function visualLineEnd(line) { + var merged; + while (merged = collapsedSpanAtEnd(line)) + { line = merged.find(1, true).line; } + return line + } + + // Returns an array of logical lines that continue the visual line + // started by the argument, or undefined if there are no such lines. + function visualLineContinued(line) { + var merged, lines; + while (merged = collapsedSpanAtEnd(line)) { + line = merged.find(1, true).line + ;(lines || (lines = [])).push(line); + } + return lines + } + + // Get the line number of the start of the visual line that the + // given line number is part of. + function visualLineNo(doc, lineN) { + var line = getLine(doc, lineN), vis = visualLine(line); + if (line == vis) { return lineN } + return lineNo(vis) + } + + // Get the line number of the start of the next visual line after + // the given line. + function visualLineEndNo(doc, lineN) { + if (lineN > doc.lastLine()) { return lineN } + var line = getLine(doc, lineN), merged; + if (!lineIsHidden(doc, line)) { return lineN } + while (merged = collapsedSpanAtEnd(line)) + { line = merged.find(1, true).line; } + return lineNo(line) + 1 + } + + // Compute whether a line is hidden. Lines count as hidden when they + // are part of a visual line that starts with another line, or when + // they are entirely covered by collapsed, non-widget span. + function lineIsHidden(doc, line) { + var sps = sawCollapsedSpans && line.markedSpans; + if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { + sp = sps[i]; + if (!sp.marker.collapsed) { continue } + if (sp.from == null) { return true } + if (sp.marker.widgetNode) { continue } + if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp)) + { return true } + } } + } + function lineIsHiddenInner(doc, line, span) { + if (span.to == null) { + var end = span.marker.find(1, true); + return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker)) + } + if (span.marker.inclusiveRight && span.to == line.text.length) + { return true } + for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) { + sp = line.markedSpans[i]; + if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to && + (sp.to == null || sp.to != span.from) && + (sp.marker.inclusiveLeft || span.marker.inclusiveRight) && + lineIsHiddenInner(doc, line, sp)) { return true } + } + } + + // Find the height above the given line. + function heightAtLine(lineObj) { + lineObj = visualLine(lineObj); + + var h = 0, chunk = lineObj.parent; + for (var i = 0; i < chunk.lines.length; ++i) { + var line = chunk.lines[i]; + if (line == lineObj) { break } + else { h += line.height; } + } + for (var p = chunk.parent; p; chunk = p, p = chunk.parent) { + for (var i$1 = 0; i$1 < p.children.length; ++i$1) { + var cur = p.children[i$1]; + if (cur == chunk) { break } + else { h += cur.height; } + } + } + return h + } + + // Compute the character length of a line, taking into account + // collapsed ranges (see markText) that might hide parts, and join + // other lines onto it. + function lineLength(line) { + if (line.height == 0) { return 0 } + var len = line.text.length, merged, cur = line; + while (merged = collapsedSpanAtStart(cur)) { + var found = merged.find(0, true); + cur = found.from.line; + len += found.from.ch - found.to.ch; + } + cur = line; + while (merged = collapsedSpanAtEnd(cur)) { + var found$1 = merged.find(0, true); + len -= cur.text.length - found$1.from.ch; + cur = found$1.to.line; + len += cur.text.length - found$1.to.ch; + } + return len + } + + // Find the longest line in the document. + function findMaxLine(cm) { + var d = cm.display, doc = cm.doc; + d.maxLine = getLine(doc, doc.first); + d.maxLineLength = lineLength(d.maxLine); + d.maxLineChanged = true; + doc.iter(function (line) { + var len = lineLength(line); + if (len > d.maxLineLength) { + d.maxLineLength = len; + d.maxLine = line; + } + }); + } + + // LINE DATA STRUCTURE + + // Line objects. These hold state related to a line, including + // highlighting info (the styles array). + var Line = function(text, markedSpans, estimateHeight) { + this.text = text; + attachMarkedSpans(this, markedSpans); + this.height = estimateHeight ? estimateHeight(this) : 1; + }; + + Line.prototype.lineNo = function () { return lineNo(this) }; + eventMixin(Line); + + // Change the content (text, markers) of a line. Automatically + // invalidates cached information and tries to re-estimate the + // line's height. + function updateLine(line, text, markedSpans, estimateHeight) { + line.text = text; + if (line.stateAfter) { line.stateAfter = null; } + if (line.styles) { line.styles = null; } + if (line.order != null) { line.order = null; } + detachMarkedSpans(line); + attachMarkedSpans(line, markedSpans); + var estHeight = estimateHeight ? estimateHeight(line) : 1; + if (estHeight != line.height) { updateLineHeight(line, estHeight); } + } + + // Detach a line from the document tree and its markers. + function cleanUpLine(line) { + line.parent = null; + detachMarkedSpans(line); + } + + // Convert a style as returned by a mode (either null, or a string + // containing one or more styles) to a CSS style. This is cached, + // and also looks for line-wide styles. + var styleToClassCache = {}, styleToClassCacheWithMode = {}; + function interpretTokenStyle(style, options) { + if (!style || /^\s*$/.test(style)) { return null } + var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache; + return cache[style] || + (cache[style] = style.replace(/\S+/g, "cm-$&")) + } + + // Render the DOM representation of the text of a line. Also builds + // up a 'line map', which points at the DOM nodes that represent + // specific stretches of text, and is used by the measuring code. + // The returned object contains the DOM node, this map, and + // information about line-wide styles that were set by the mode. + function buildLineContent(cm, lineView) { + // The padding-right forces the element to have a 'border', which + // is needed on Webkit to be able to get line-level bounding + // rectangles for it (in measureChar). + var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null); + var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content, + col: 0, pos: 0, cm: cm, + trailingSpace: false, + splitSpaces: cm.getOption("lineWrapping")}; + lineView.measure = {}; + + // Iterate over the logical lines that make up this visual line. + for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) { + var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0); + builder.pos = 0; + builder.addToken = buildToken; + // Optionally wire in some hacks into the token-rendering + // algorithm, to deal with browser quirks. + if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction))) + { builder.addToken = buildTokenBadBidi(builder.addToken, order); } + builder.map = []; + var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line); + insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate)); + if (line.styleClasses) { + if (line.styleClasses.bgClass) + { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); } + if (line.styleClasses.textClass) + { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); } + } + + // Ensure at least a single node is present, for measuring. + if (builder.map.length == 0) + { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); } + + // Store the map and a cache object for the current logical line + if (i == 0) { + lineView.measure.map = builder.map; + lineView.measure.cache = {}; + } else { + (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map) + ;(lineView.measure.caches || (lineView.measure.caches = [])).push({}); + } + } + + // See issue #2901 + if (webkit) { + var last = builder.content.lastChild; + if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab"))) + { builder.content.className = "cm-tab-wrap-hack"; } + } + + signal(cm, "renderLine", cm, lineView.line, builder.pre); + if (builder.pre.className) + { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); } + + return builder + } + + function defaultSpecialCharPlaceholder(ch) { + var token = elt("span", "\u2022", "cm-invalidchar"); + token.title = "\\u" + ch.charCodeAt(0).toString(16); + token.setAttribute("aria-label", token.title); + return token + } + + // Build up the DOM representation for a single token, and add it to + // the line map. Takes care to render special characters separately. + function buildToken(builder, text, style, startStyle, endStyle, css, attributes) { + if (!text) { return } + var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text; + var special = builder.cm.state.specialChars, mustWrap = false; + var content; + if (!special.test(text)) { + builder.col += text.length; + content = document.createTextNode(displayText); + builder.map.push(builder.pos, builder.pos + text.length, content); + if (ie && ie_version < 9) { mustWrap = true; } + builder.pos += text.length; + } else { + content = document.createDocumentFragment(); + var pos = 0; + while (true) { + special.lastIndex = pos; + var m = special.exec(text); + var skipped = m ? m.index - pos : text.length - pos; + if (skipped) { + var txt = document.createTextNode(displayText.slice(pos, pos + skipped)); + if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); } + else { content.appendChild(txt); } + builder.map.push(builder.pos, builder.pos + skipped, txt); + builder.col += skipped; + builder.pos += skipped; + } + if (!m) { break } + pos += skipped + 1; + var txt$1 = (void 0); + if (m[0] == "\t") { + var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize; + txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")); + txt$1.setAttribute("role", "presentation"); + txt$1.setAttribute("cm-text", "\t"); + builder.col += tabWidth; + } else if (m[0] == "\r" || m[0] == "\n") { + txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar")); + txt$1.setAttribute("cm-text", m[0]); + builder.col += 1; + } else { + txt$1 = builder.cm.options.specialCharPlaceholder(m[0]); + txt$1.setAttribute("cm-text", m[0]); + if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); } + else { content.appendChild(txt$1); } + builder.col += 1; + } + builder.map.push(builder.pos, builder.pos + 1, txt$1); + builder.pos++; + } + } + builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32; + if (style || startStyle || endStyle || mustWrap || css || attributes) { + var fullStyle = style || ""; + if (startStyle) { fullStyle += startStyle; } + if (endStyle) { fullStyle += endStyle; } + var token = elt("span", [content], fullStyle, css); + if (attributes) { + for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class") + { token.setAttribute(attr, attributes[attr]); } } + } + return builder.content.appendChild(token) + } + builder.content.appendChild(content); + } + + // Change some spaces to NBSP to prevent the browser from collapsing + // trailing spaces at the end of a line when rendering text (issue #1362). + function splitSpaces(text, trailingBefore) { + if (text.length > 1 && !/ /.test(text)) { return text } + var spaceBefore = trailingBefore, result = ""; + for (var i = 0; i < text.length; i++) { + var ch = text.charAt(i); + if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32)) + { ch = "\u00a0"; } + result += ch; + spaceBefore = ch == " "; + } + return result + } + + // Work around nonsense dimensions being reported for stretches of + // right-to-left text. + function buildTokenBadBidi(inner, order) { + return function (builder, text, style, startStyle, endStyle, css, attributes) { + style = style ? style + " cm-force-border" : "cm-force-border"; + var start = builder.pos, end = start + text.length; + for (;;) { + // Find the part that overlaps with the start of this text + var part = (void 0); + for (var i = 0; i < order.length; i++) { + part = order[i]; + if (part.to > start && part.from <= start) { break } + } + if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) } + inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes); + startStyle = null; + text = text.slice(part.to - start); + start = part.to; + } + } + } + + function buildCollapsedSpan(builder, size, marker, ignoreWidget) { + var widget = !ignoreWidget && marker.widgetNode; + if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); } + if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) { + if (!widget) + { widget = builder.content.appendChild(document.createElement("span")); } + widget.setAttribute("cm-marker", marker.id); + } + if (widget) { + builder.cm.display.input.setUneditable(widget); + builder.content.appendChild(widget); + } + builder.pos += size; + builder.trailingSpace = false; + } + + // Outputs a number of spans to make up a line, taking highlighting + // and marked text into account. + function insertLineContent(line, builder, styles) { + var spans = line.markedSpans, allText = line.text, at = 0; + if (!spans) { + for (var i$1 = 1; i$1 < styles.length; i$1+=2) + { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); } + return + } + + var len = allText.length, pos = 0, i = 1, text = "", style, css; + var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes; + for (;;) { + if (nextChange == pos) { // Update current marker set + spanStyle = spanEndStyle = spanStartStyle = css = ""; + attributes = null; + collapsed = null; nextChange = Infinity; + var foundBookmarks = [], endStyles = (void 0); + for (var j = 0; j < spans.length; ++j) { + var sp = spans[j], m = sp.marker; + if (m.type == "bookmark" && sp.from == pos && m.widgetNode) { + foundBookmarks.push(m); + } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) { + if (sp.to != null && sp.to != pos && nextChange > sp.to) { + nextChange = sp.to; + spanEndStyle = ""; + } + if (m.className) { spanStyle += " " + m.className; } + if (m.css) { css = (css ? css + ";" : "") + m.css; } + if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; } + if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); } + // support for the old title property + // https://github.com/codemirror/CodeMirror/pull/5673 + if (m.title) { (attributes || (attributes = {})).title = m.title; } + if (m.attributes) { + for (var attr in m.attributes) + { (attributes || (attributes = {}))[attr] = m.attributes[attr]; } + } + if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)) + { collapsed = sp; } + } else if (sp.from > pos && nextChange > sp.from) { + nextChange = sp.from; + } + } + if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2) + { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } } + + if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2) + { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } } + if (collapsed && (collapsed.from || 0) == pos) { + buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos, + collapsed.marker, collapsed.from == null); + if (collapsed.to == null) { return } + if (collapsed.to == pos) { collapsed = false; } + } + } + if (pos >= len) { break } + + var upto = Math.min(len, nextChange); + while (true) { + if (text) { + var end = pos + text.length; + if (!collapsed) { + var tokenText = end > upto ? text.slice(0, upto - pos) : text; + builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle, + spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes); + } + if (end >= upto) {text = text.slice(upto - pos); pos = upto; break} + pos = end; + spanStartStyle = ""; + } + text = allText.slice(at, at = styles[i++]); + style = interpretTokenStyle(styles[i++], builder.cm.options); + } + } + } + + + // These objects are used to represent the visible (currently drawn) + // part of the document. A LineView may correspond to multiple + // logical lines, if those are connected by collapsed ranges. + function LineView(doc, line, lineN) { + // The starting line + this.line = line; + // Continuing lines, if any + this.rest = visualLineContinued(line); + // Number of logical lines in this visual line + this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1; + this.node = this.text = null; + this.hidden = lineIsHidden(doc, line); + } + + // Create a range of LineView objects for the given lines. + function buildViewArray(cm, from, to) { + var array = [], nextPos; + for (var pos = from; pos < to; pos = nextPos) { + var view = new LineView(cm.doc, getLine(cm.doc, pos), pos); + nextPos = pos + view.size; + array.push(view); + } + return array + } + + var operationGroup = null; + + function pushOperation(op) { + if (operationGroup) { + operationGroup.ops.push(op); + } else { + op.ownsGroup = operationGroup = { + ops: [op], + delayedCallbacks: [] + }; + } + } + + function fireCallbacksForOps(group) { + // Calls delayed callbacks and cursorActivity handlers until no + // new ones appear + var callbacks = group.delayedCallbacks, i = 0; + do { + for (; i < callbacks.length; i++) + { callbacks[i].call(null); } + for (var j = 0; j < group.ops.length; j++) { + var op = group.ops[j]; + if (op.cursorActivityHandlers) + { while (op.cursorActivityCalled < op.cursorActivityHandlers.length) + { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } } + } + } while (i < callbacks.length) + } + + function finishOperation(op, endCb) { + var group = op.ownsGroup; + if (!group) { return } + + try { fireCallbacksForOps(group); } + finally { + operationGroup = null; + endCb(group); + } + } + + var orphanDelayedCallbacks = null; + + // Often, we want to signal events at a point where we are in the + // middle of some work, but don't want the handler to start calling + // other methods on the editor, which might be in an inconsistent + // state or simply not expect any other events to happen. + // signalLater looks whether there are any handlers, and schedules + // them to be executed when the last operation ends, or, if no + // operation is active, when a timeout fires. + function signalLater(emitter, type /*, values...*/) { + var arr = getHandlers(emitter, type); + if (!arr.length) { return } + var args = Array.prototype.slice.call(arguments, 2), list; + if (operationGroup) { + list = operationGroup.delayedCallbacks; + } else if (orphanDelayedCallbacks) { + list = orphanDelayedCallbacks; + } else { + list = orphanDelayedCallbacks = []; + setTimeout(fireOrphanDelayed, 0); + } + var loop = function ( i ) { + list.push(function () { return arr[i].apply(null, args); }); + }; + + for (var i = 0; i < arr.length; ++i) + loop( i ); + } + + function fireOrphanDelayed() { + var delayed = orphanDelayedCallbacks; + orphanDelayedCallbacks = null; + for (var i = 0; i < delayed.length; ++i) { delayed[i](); } + } + + // When an aspect of a line changes, a string is added to + // lineView.changes. This updates the relevant part of the line's + // DOM structure. + function updateLineForChanges(cm, lineView, lineN, dims) { + for (var j = 0; j < lineView.changes.length; j++) { + var type = lineView.changes[j]; + if (type == "text") { updateLineText(cm, lineView); } + else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); } + else if (type == "class") { updateLineClasses(cm, lineView); } + else if (type == "widget") { updateLineWidgets(cm, lineView, dims); } + } + lineView.changes = null; + } + + // Lines with gutter elements, widgets or a background class need to + // be wrapped, and have the extra elements added to the wrapper div + function ensureLineWrapped(lineView) { + if (lineView.node == lineView.text) { + lineView.node = elt("div", null, null, "position: relative"); + if (lineView.text.parentNode) + { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); } + lineView.node.appendChild(lineView.text); + if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; } + } + return lineView.node + } + + function updateLineBackground(cm, lineView) { + var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass; + if (cls) { cls += " CodeMirror-linebackground"; } + if (lineView.background) { + if (cls) { lineView.background.className = cls; } + else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; } + } else if (cls) { + var wrap = ensureLineWrapped(lineView); + lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild); + cm.display.input.setUneditable(lineView.background); + } + } + + // Wrapper around buildLineContent which will reuse the structure + // in display.externalMeasured when possible. + function getLineContent(cm, lineView) { + var ext = cm.display.externalMeasured; + if (ext && ext.line == lineView.line) { + cm.display.externalMeasured = null; + lineView.measure = ext.measure; + return ext.built + } + return buildLineContent(cm, lineView) + } + + // Redraw the line's text. Interacts with the background and text + // classes because the mode may output tokens that influence these + // classes. + function updateLineText(cm, lineView) { + var cls = lineView.text.className; + var built = getLineContent(cm, lineView); + if (lineView.text == lineView.node) { lineView.node = built.pre; } + lineView.text.parentNode.replaceChild(built.pre, lineView.text); + lineView.text = built.pre; + if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) { + lineView.bgClass = built.bgClass; + lineView.textClass = built.textClass; + updateLineClasses(cm, lineView); + } else if (cls) { + lineView.text.className = cls; + } + } + + function updateLineClasses(cm, lineView) { + updateLineBackground(cm, lineView); + if (lineView.line.wrapClass) + { ensureLineWrapped(lineView).className = lineView.line.wrapClass; } + else if (lineView.node != lineView.text) + { lineView.node.className = ""; } + var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass; + lineView.text.className = textClass || ""; + } + + function updateLineGutter(cm, lineView, lineN, dims) { + if (lineView.gutter) { + lineView.node.removeChild(lineView.gutter); + lineView.gutter = null; + } + if (lineView.gutterBackground) { + lineView.node.removeChild(lineView.gutterBackground); + lineView.gutterBackground = null; + } + if (lineView.line.gutterClass) { + var wrap = ensureLineWrapped(lineView); + lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass, + ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px")); + cm.display.input.setUneditable(lineView.gutterBackground); + wrap.insertBefore(lineView.gutterBackground, lineView.text); + } + var markers = lineView.line.gutterMarkers; + if (cm.options.lineNumbers || markers) { + var wrap$1 = ensureLineWrapped(lineView); + var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px")); + gutterWrap.setAttribute("aria-hidden", "true"); + cm.display.input.setUneditable(gutterWrap); + wrap$1.insertBefore(gutterWrap, lineView.text); + if (lineView.line.gutterClass) + { gutterWrap.className += " " + lineView.line.gutterClass; } + if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"])) + { lineView.lineNumber = gutterWrap.appendChild( + elt("div", lineNumberFor(cm.options, lineN), + "CodeMirror-linenumber CodeMirror-gutter-elt", + ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); } + if (markers) { for (var k = 0; k < cm.display.gutterSpecs.length; ++k) { + var id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id]; + if (found) + { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", + ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); } + } } + } + } + + function updateLineWidgets(cm, lineView, dims) { + if (lineView.alignable) { lineView.alignable = null; } + var isWidget = classTest("CodeMirror-linewidget"); + for (var node = lineView.node.firstChild, next = (void 0); node; node = next) { + next = node.nextSibling; + if (isWidget.test(node.className)) { lineView.node.removeChild(node); } + } + insertLineWidgets(cm, lineView, dims); + } + + // Build a line's DOM representation from scratch + function buildLineElement(cm, lineView, lineN, dims) { + var built = getLineContent(cm, lineView); + lineView.text = lineView.node = built.pre; + if (built.bgClass) { lineView.bgClass = built.bgClass; } + if (built.textClass) { lineView.textClass = built.textClass; } + + updateLineClasses(cm, lineView); + updateLineGutter(cm, lineView, lineN, dims); + insertLineWidgets(cm, lineView, dims); + return lineView.node + } + + // A lineView may contain multiple logical lines (when merged by + // collapsed spans). The widgets for all of them need to be drawn. + function insertLineWidgets(cm, lineView, dims) { + insertLineWidgetsFor(cm, lineView.line, lineView, dims, true); + if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) + { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } } + } + + function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) { + if (!line.widgets) { return } + var wrap = ensureLineWrapped(lineView); + for (var i = 0, ws = line.widgets; i < ws.length; ++i) { + var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget" + (widget.className ? " " + widget.className : "")); + if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); } + positionLineWidget(widget, node, lineView, dims); + cm.display.input.setUneditable(node); + if (allowAbove && widget.above) + { wrap.insertBefore(node, lineView.gutter || lineView.text); } + else + { wrap.appendChild(node); } + signalLater(widget, "redraw"); + } + } + + function positionLineWidget(widget, node, lineView, dims) { + if (widget.noHScroll) { + (lineView.alignable || (lineView.alignable = [])).push(node); + var width = dims.wrapperWidth; + node.style.left = dims.fixedPos + "px"; + if (!widget.coverGutter) { + width -= dims.gutterTotalWidth; + node.style.paddingLeft = dims.gutterTotalWidth + "px"; + } + node.style.width = width + "px"; + } + if (widget.coverGutter) { + node.style.zIndex = 5; + node.style.position = "relative"; + if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; } + } + } + + function widgetHeight(widget) { + if (widget.height != null) { return widget.height } + var cm = widget.doc.cm; + if (!cm) { return 0 } + if (!contains(document.body, widget.node)) { + var parentStyle = "position: relative;"; + if (widget.coverGutter) + { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; } + if (widget.noHScroll) + { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; } + removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle)); + } + return widget.height = widget.node.parentNode.offsetHeight + } + + // Return true when the given mouse event happened in a widget + function eventInWidget(display, e) { + for (var n = e_target(e); n != display.wrapper; n = n.parentNode) { + if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") || + (n.parentNode == display.sizer && n != display.mover)) + { return true } + } + } + + // POSITION MEASUREMENT + + function paddingTop(display) {return display.lineSpace.offsetTop} + function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight} + function paddingH(display) { + if (display.cachedPaddingH) { return display.cachedPaddingH } + var e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-line-like")); + var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle; + var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}; + if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; } + return data + } + + function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth } + function displayWidth(cm) { + return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth + } + function displayHeight(cm) { + return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight + } + + // Ensure the lineView.wrapping.heights array is populated. This is + // an array of bottom offsets for the lines that make up a drawn + // line. When lineWrapping is on, there might be more than one + // height. + function ensureLineHeights(cm, lineView, rect) { + var wrapping = cm.options.lineWrapping; + var curWidth = wrapping && displayWidth(cm); + if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) { + var heights = lineView.measure.heights = []; + if (wrapping) { + lineView.measure.width = curWidth; + var rects = lineView.text.firstChild.getClientRects(); + for (var i = 0; i < rects.length - 1; i++) { + var cur = rects[i], next = rects[i + 1]; + if (Math.abs(cur.bottom - next.bottom) > 2) + { heights.push((cur.bottom + next.top) / 2 - rect.top); } + } + } + heights.push(rect.bottom - rect.top); + } + } + + // Find a line map (mapping character offsets to text nodes) and a + // measurement cache for the given line number. (A line view might + // contain multiple lines when collapsed ranges are present.) + function mapFromLineView(lineView, line, lineN) { + if (lineView.line == line) + { return {map: lineView.measure.map, cache: lineView.measure.cache} } + if (lineView.rest) { + for (var i = 0; i < lineView.rest.length; i++) + { if (lineView.rest[i] == line) + { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } } + for (var i$1 = 0; i$1 < lineView.rest.length; i$1++) + { if (lineNo(lineView.rest[i$1]) > lineN) + { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } } + } + } + + // Render a line into the hidden node display.externalMeasured. Used + // when measurement is needed for a line that's not in the viewport. + function updateExternalMeasurement(cm, line) { + line = visualLine(line); + var lineN = lineNo(line); + var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN); + view.lineN = lineN; + var built = view.built = buildLineContent(cm, view); + view.text = built.pre; + removeChildrenAndAdd(cm.display.lineMeasure, built.pre); + return view + } + + // Get a {top, bottom, left, right} box (in line-local coordinates) + // for a given character. + function measureChar(cm, line, ch, bias) { + return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias) + } + + // Find a line view that corresponds to the given line number. + function findViewForLine(cm, lineN) { + if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo) + { return cm.display.view[findViewIndex(cm, lineN)] } + var ext = cm.display.externalMeasured; + if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size) + { return ext } + } + + // Measurement can be split in two steps, the set-up work that + // applies to the whole line, and the measurement of the actual + // character. Functions like coordsChar, that need to do a lot of + // measurements in a row, can thus ensure that the set-up work is + // only done once. + function prepareMeasureForLine(cm, line) { + var lineN = lineNo(line); + var view = findViewForLine(cm, lineN); + if (view && !view.text) { + view = null; + } else if (view && view.changes) { + updateLineForChanges(cm, view, lineN, getDimensions(cm)); + cm.curOp.forceUpdate = true; + } + if (!view) + { view = updateExternalMeasurement(cm, line); } + + var info = mapFromLineView(view, line, lineN); + return { + line: line, view: view, rect: null, + map: info.map, cache: info.cache, before: info.before, + hasHeights: false + } + } + + // Given a prepared measurement object, measures the position of an + // actual character (or fetches it from the cache). + function measureCharPrepared(cm, prepared, ch, bias, varHeight) { + if (prepared.before) { ch = -1; } + var key = ch + (bias || ""), found; + if (prepared.cache.hasOwnProperty(key)) { + found = prepared.cache[key]; + } else { + if (!prepared.rect) + { prepared.rect = prepared.view.text.getBoundingClientRect(); } + if (!prepared.hasHeights) { + ensureLineHeights(cm, prepared.view, prepared.rect); + prepared.hasHeights = true; + } + found = measureCharInner(cm, prepared, ch, bias); + if (!found.bogus) { prepared.cache[key] = found; } + } + return {left: found.left, right: found.right, + top: varHeight ? found.rtop : found.top, + bottom: varHeight ? found.rbottom : found.bottom} + } + + var nullRect = {left: 0, right: 0, top: 0, bottom: 0}; + + function nodeAndOffsetInLineMap(map, ch, bias) { + var node, start, end, collapse, mStart, mEnd; + // First, search the line map for the text node corresponding to, + // or closest to, the target character. + for (var i = 0; i < map.length; i += 3) { + mStart = map[i]; + mEnd = map[i + 1]; + if (ch < mStart) { + start = 0; end = 1; + collapse = "left"; + } else if (ch < mEnd) { + start = ch - mStart; + end = start + 1; + } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) { + end = mEnd - mStart; + start = end - 1; + if (ch >= mEnd) { collapse = "right"; } + } + if (start != null) { + node = map[i + 2]; + if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right")) + { collapse = bias; } + if (bias == "left" && start == 0) + { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) { + node = map[(i -= 3) + 2]; + collapse = "left"; + } } + if (bias == "right" && start == mEnd - mStart) + { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) { + node = map[(i += 3) + 2]; + collapse = "right"; + } } + break + } + } + return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd} + } + + function getUsefulRect(rects, bias) { + var rect = nullRect; + if (bias == "left") { for (var i = 0; i < rects.length; i++) { + if ((rect = rects[i]).left != rect.right) { break } + } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) { + if ((rect = rects[i$1]).left != rect.right) { break } + } } + return rect + } + + function measureCharInner(cm, prepared, ch, bias) { + var place = nodeAndOffsetInLineMap(prepared.map, ch, bias); + var node = place.node, start = place.start, end = place.end, collapse = place.collapse; + + var rect; + if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates. + for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned + while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; } + while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; } + if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) + { rect = node.parentNode.getBoundingClientRect(); } + else + { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); } + if (rect.left || rect.right || start == 0) { break } + end = start; + start = start - 1; + collapse = "right"; + } + if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); } + } else { // If it is a widget, simply get the box for the whole widget. + if (start > 0) { collapse = bias = "right"; } + var rects; + if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1) + { rect = rects[bias == "right" ? rects.length - 1 : 0]; } + else + { rect = node.getBoundingClientRect(); } + } + if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) { + var rSpan = node.parentNode.getClientRects()[0]; + if (rSpan) + { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; } + else + { rect = nullRect; } + } + + var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top; + var mid = (rtop + rbot) / 2; + var heights = prepared.view.measure.heights; + var i = 0; + for (; i < heights.length - 1; i++) + { if (mid < heights[i]) { break } } + var top = i ? heights[i - 1] : 0, bot = heights[i]; + var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left, + right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left, + top: top, bottom: bot}; + if (!rect.left && !rect.right) { result.bogus = true; } + if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; } + + return result + } + + // Work around problem with bounding client rects on ranges being + // returned incorrectly when zoomed on IE10 and below. + function maybeUpdateRectForZooming(measure, rect) { + if (!window.screen || screen.logicalXDPI == null || + screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure)) + { return rect } + var scaleX = screen.logicalXDPI / screen.deviceXDPI; + var scaleY = screen.logicalYDPI / screen.deviceYDPI; + return {left: rect.left * scaleX, right: rect.right * scaleX, + top: rect.top * scaleY, bottom: rect.bottom * scaleY} + } + + function clearLineMeasurementCacheFor(lineView) { + if (lineView.measure) { + lineView.measure.cache = {}; + lineView.measure.heights = null; + if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) + { lineView.measure.caches[i] = {}; } } + } + } + + function clearLineMeasurementCache(cm) { + cm.display.externalMeasure = null; + removeChildren(cm.display.lineMeasure); + for (var i = 0; i < cm.display.view.length; i++) + { clearLineMeasurementCacheFor(cm.display.view[i]); } + } + + function clearCaches(cm) { + clearLineMeasurementCache(cm); + cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null; + if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; } + cm.display.lineNumChars = null; + } + + function pageScrollX(doc) { + // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206 + // which causes page_Offset and bounding client rects to use + // different reference viewports and invalidate our calculations. + if (chrome && android) { return -(doc.body.getBoundingClientRect().left - parseInt(getComputedStyle(doc.body).marginLeft)) } + return doc.defaultView.pageXOffset || (doc.documentElement || doc.body).scrollLeft + } + function pageScrollY(doc) { + if (chrome && android) { return -(doc.body.getBoundingClientRect().top - parseInt(getComputedStyle(doc.body).marginTop)) } + return doc.defaultView.pageYOffset || (doc.documentElement || doc.body).scrollTop + } + + function widgetTopHeight(lineObj) { + var ref = visualLine(lineObj); + var widgets = ref.widgets; + var height = 0; + if (widgets) { for (var i = 0; i < widgets.length; ++i) { if (widgets[i].above) + { height += widgetHeight(widgets[i]); } } } + return height + } + + // Converts a {top, bottom, left, right} box from line-local + // coordinates into another coordinate system. Context may be one of + // "line", "div" (display.lineDiv), "local"./null (editor), "window", + // or "page". + function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) { + if (!includeWidgets) { + var height = widgetTopHeight(lineObj); + rect.top += height; rect.bottom += height; + } + if (context == "line") { return rect } + if (!context) { context = "local"; } + var yOff = heightAtLine(lineObj); + if (context == "local") { yOff += paddingTop(cm.display); } + else { yOff -= cm.display.viewOffset; } + if (context == "page" || context == "window") { + var lOff = cm.display.lineSpace.getBoundingClientRect(); + yOff += lOff.top + (context == "window" ? 0 : pageScrollY(doc(cm))); + var xOff = lOff.left + (context == "window" ? 0 : pageScrollX(doc(cm))); + rect.left += xOff; rect.right += xOff; + } + rect.top += yOff; rect.bottom += yOff; + return rect + } + + // Coverts a box from "div" coords to another coordinate system. + // Context may be "window", "page", "div", or "local"./null. + function fromCoordSystem(cm, coords, context) { + if (context == "div") { return coords } + var left = coords.left, top = coords.top; + // First move into "page" coordinate system + if (context == "page") { + left -= pageScrollX(doc(cm)); + top -= pageScrollY(doc(cm)); + } else if (context == "local" || !context) { + var localBox = cm.display.sizer.getBoundingClientRect(); + left += localBox.left; + top += localBox.top; + } + + var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect(); + return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top} + } + + function charCoords(cm, pos, context, lineObj, bias) { + if (!lineObj) { lineObj = getLine(cm.doc, pos.line); } + return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context) + } + + // Returns a box for a given cursor position, which may have an + // 'other' property containing the position of the secondary cursor + // on a bidi boundary. + // A cursor Pos(line, char, "before") is on the same visual line as `char - 1` + // and after `char - 1` in writing order of `char - 1` + // A cursor Pos(line, char, "after") is on the same visual line as `char` + // and before `char` in writing order of `char` + // Examples (upper-case letters are RTL, lower-case are LTR): + // Pos(0, 1, ...) + // before after + // ab a|b a|b + // aB a|B aB| + // Ab |Ab A|b + // AB B|A B|A + // Every position after the last character on a line is considered to stick + // to the last character on the line. + function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) { + lineObj = lineObj || getLine(cm.doc, pos.line); + if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); } + function get(ch, right) { + var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight); + if (right) { m.left = m.right; } else { m.right = m.left; } + return intoCoordSystem(cm, lineObj, m, context) + } + var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky; + if (ch >= lineObj.text.length) { + ch = lineObj.text.length; + sticky = "before"; + } else if (ch <= 0) { + ch = 0; + sticky = "after"; + } + if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") } + + function getBidi(ch, partPos, invert) { + var part = order[partPos], right = part.level == 1; + return get(invert ? ch - 1 : ch, right != invert) + } + var partPos = getBidiPartAt(order, ch, sticky); + var other = bidiOther; + var val = getBidi(ch, partPos, sticky == "before"); + if (other != null) { val.other = getBidi(ch, other, sticky != "before"); } + return val + } + + // Used to cheaply estimate the coordinates for a position. Used for + // intermediate scroll updates. + function estimateCoords(cm, pos) { + var left = 0; + pos = clipPos(cm.doc, pos); + if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; } + var lineObj = getLine(cm.doc, pos.line); + var top = heightAtLine(lineObj) + paddingTop(cm.display); + return {left: left, right: left, top: top, bottom: top + lineObj.height} + } + + // Positions returned by coordsChar contain some extra information. + // xRel is the relative x position of the input coordinates compared + // to the found position (so xRel > 0 means the coordinates are to + // the right of the character position, for example). When outside + // is true, that means the coordinates lie outside the line's + // vertical range. + function PosWithInfo(line, ch, sticky, outside, xRel) { + var pos = Pos(line, ch, sticky); + pos.xRel = xRel; + if (outside) { pos.outside = outside; } + return pos + } + + // Compute the character position closest to the given coordinates. + // Input must be lineSpace-local ("div" coordinate system). + function coordsChar(cm, x, y) { + var doc = cm.doc; + y += cm.display.viewOffset; + if (y < 0) { return PosWithInfo(doc.first, 0, null, -1, -1) } + var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1; + if (lineN > last) + { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1) } + if (x < 0) { x = 0; } + + var lineObj = getLine(doc, lineN); + for (;;) { + var found = coordsCharInner(cm, lineObj, lineN, x, y); + var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0)); + if (!collapsed) { return found } + var rangeEnd = collapsed.find(1); + if (rangeEnd.line == lineN) { return rangeEnd } + lineObj = getLine(doc, lineN = rangeEnd.line); + } + } + + function wrappedLineExtent(cm, lineObj, preparedMeasure, y) { + y -= widgetTopHeight(lineObj); + var end = lineObj.text.length; + var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0); + end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end); + return {begin: begin, end: end} + } + + function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) { + if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); } + var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top; + return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop) + } + + // Returns true if the given side of a box is after the given + // coordinates, in top-to-bottom, left-to-right order. + function boxIsAfter(box, x, y, left) { + return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x + } + + function coordsCharInner(cm, lineObj, lineNo, x, y) { + // Move y into line-local coordinate space + y -= heightAtLine(lineObj); + var preparedMeasure = prepareMeasureForLine(cm, lineObj); + // When directly calling `measureCharPrepared`, we have to adjust + // for the widgets at this line. + var widgetHeight = widgetTopHeight(lineObj); + var begin = 0, end = lineObj.text.length, ltr = true; + + var order = getOrder(lineObj, cm.doc.direction); + // If the line isn't plain left-to-right text, first figure out + // which bidi section the coordinates fall into. + if (order) { + var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart) + (cm, lineObj, lineNo, preparedMeasure, order, x, y); + ltr = part.level != 1; + // The awkward -1 offsets are needed because findFirst (called + // on these below) will treat its first bound as inclusive, + // second as exclusive, but we want to actually address the + // characters in the part's range + begin = ltr ? part.from : part.to - 1; + end = ltr ? part.to : part.from - 1; + } + + // A binary search to find the first character whose bounding box + // starts after the coordinates. If we run across any whose box wrap + // the coordinates, store that. + var chAround = null, boxAround = null; + var ch = findFirst(function (ch) { + var box = measureCharPrepared(cm, preparedMeasure, ch); + box.top += widgetHeight; box.bottom += widgetHeight; + if (!boxIsAfter(box, x, y, false)) { return false } + if (box.top <= y && box.left <= x) { + chAround = ch; + boxAround = box; + } + return true + }, begin, end); + + var baseX, sticky, outside = false; + // If a box around the coordinates was found, use that + if (boxAround) { + // Distinguish coordinates nearer to the left or right side of the box + var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr; + ch = chAround + (atStart ? 0 : 1); + sticky = atStart ? "after" : "before"; + baseX = atLeft ? boxAround.left : boxAround.right; + } else { + // (Adjust for extended bound, if necessary.) + if (!ltr && (ch == end || ch == begin)) { ch++; } + // To determine which side to associate with, get the box to the + // left of the character and compare it's vertical position to the + // coordinates + sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" : + (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ? + "after" : "before"; + // Now get accurate coordinates for this place, in order to get a + // base X position + var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure); + baseX = coords.left; + outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0; + } + + ch = skipExtendingChars(lineObj.text, ch, 1); + return PosWithInfo(lineNo, ch, sticky, outside, x - baseX) + } + + function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) { + // Bidi parts are sorted left-to-right, and in a non-line-wrapping + // situation, we can take this ordering to correspond to the visual + // ordering. This finds the first part whose end is after the given + // coordinates. + var index = findFirst(function (i) { + var part = order[i], ltr = part.level != 1; + return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"), + "line", lineObj, preparedMeasure), x, y, true) + }, 0, order.length - 1); + var part = order[index]; + // If this isn't the first part, the part's start is also after + // the coordinates, and the coordinates aren't on the same line as + // that start, move one part back. + if (index > 0) { + var ltr = part.level != 1; + var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"), + "line", lineObj, preparedMeasure); + if (boxIsAfter(start, x, y, true) && start.top > y) + { part = order[index - 1]; } + } + return part + } + + function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) { + // In a wrapped line, rtl text on wrapping boundaries can do things + // that don't correspond to the ordering in our `order` array at + // all, so a binary search doesn't work, and we want to return a + // part that only spans one line so that the binary search in + // coordsCharInner is safe. As such, we first find the extent of the + // wrapped line, and then do a flat search in which we discard any + // spans that aren't on the line. + var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y); + var begin = ref.begin; + var end = ref.end; + if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; } + var part = null, closestDist = null; + for (var i = 0; i < order.length; i++) { + var p = order[i]; + if (p.from >= end || p.to <= begin) { continue } + var ltr = p.level != 1; + var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right; + // Weigh against spans ending before this, so that they are only + // picked if nothing ends after + var dist = endX < x ? x - endX + 1e9 : endX - x; + if (!part || closestDist > dist) { + part = p; + closestDist = dist; + } + } + if (!part) { part = order[order.length - 1]; } + // Clip the part to the wrapped line. + if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; } + if (part.to > end) { part = {from: part.from, to: end, level: part.level}; } + return part + } + + var measureText; + // Compute the default text height. + function textHeight(display) { + if (display.cachedTextHeight != null) { return display.cachedTextHeight } + if (measureText == null) { + measureText = elt("pre", null, "CodeMirror-line-like"); + // Measure a bunch of lines, for browsers that compute + // fractional heights. + for (var i = 0; i < 49; ++i) { + measureText.appendChild(document.createTextNode("x")); + measureText.appendChild(elt("br")); + } + measureText.appendChild(document.createTextNode("x")); + } + removeChildrenAndAdd(display.measure, measureText); + var height = measureText.offsetHeight / 50; + if (height > 3) { display.cachedTextHeight = height; } + removeChildren(display.measure); + return height || 1 + } + + // Compute the default character width. + function charWidth(display) { + if (display.cachedCharWidth != null) { return display.cachedCharWidth } + var anchor = elt("span", "xxxxxxxxxx"); + var pre = elt("pre", [anchor], "CodeMirror-line-like"); + removeChildrenAndAdd(display.measure, pre); + var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10; + if (width > 2) { display.cachedCharWidth = width; } + return width || 10 + } + + // Do a bulk-read of the DOM positions and sizes needed to draw the + // view, so that we don't interleave reading and writing to the DOM. + function getDimensions(cm) { + var d = cm.display, left = {}, width = {}; + var gutterLeft = d.gutters.clientLeft; + for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) { + var id = cm.display.gutterSpecs[i].className; + left[id] = n.offsetLeft + n.clientLeft + gutterLeft; + width[id] = n.clientWidth; + } + return {fixedPos: compensateForHScroll(d), + gutterTotalWidth: d.gutters.offsetWidth, + gutterLeft: left, + gutterWidth: width, + wrapperWidth: d.wrapper.clientWidth} + } + + // Computes display.scroller.scrollLeft + display.gutters.offsetWidth, + // but using getBoundingClientRect to get a sub-pixel-accurate + // result. + function compensateForHScroll(display) { + return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left + } + + // Returns a function that estimates the height of a line, to use as + // first approximation until the line becomes visible (and is thus + // properly measurable). + function estimateHeight(cm) { + var th = textHeight(cm.display), wrapping = cm.options.lineWrapping; + var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3); + return function (line) { + if (lineIsHidden(cm.doc, line)) { return 0 } + + var widgetsHeight = 0; + if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) { + if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; } + } } + + if (wrapping) + { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th } + else + { return widgetsHeight + th } + } + } + + function estimateLineHeights(cm) { + var doc = cm.doc, est = estimateHeight(cm); + doc.iter(function (line) { + var estHeight = est(line); + if (estHeight != line.height) { updateLineHeight(line, estHeight); } + }); + } + + // Given a mouse event, find the corresponding position. If liberal + // is false, it checks whether a gutter or scrollbar was clicked, + // and returns null if it was. forRect is used by rectangular + // selections, and tries to estimate a character position even for + // coordinates beyond the right of the text. + function posFromMouse(cm, e, liberal, forRect) { + var display = cm.display; + if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null } + + var x, y, space = display.lineSpace.getBoundingClientRect(); + // Fails unpredictably on IE[67] when mouse is dragged around quickly. + try { x = e.clientX - space.left; y = e.clientY - space.top; } + catch (e$1) { return null } + var coords = coordsChar(cm, x, y), line; + if (forRect && coords.xRel > 0 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) { + var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length; + coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff)); + } + return coords + } + + // Find the view element corresponding to a given line. Return null + // when the line isn't visible. + function findViewIndex(cm, n) { + if (n >= cm.display.viewTo) { return null } + n -= cm.display.viewFrom; + if (n < 0) { return null } + var view = cm.display.view; + for (var i = 0; i < view.length; i++) { + n -= view[i].size; + if (n < 0) { return i } + } + } + + // Updates the display.view data structure for a given change to the + // document. From and to are in pre-change coordinates. Lendiff is + // the amount of lines added or subtracted by the change. This is + // used for changes that span multiple lines, or change the way + // lines are divided into visual lines. regLineChange (below) + // registers single-line changes. + function regChange(cm, from, to, lendiff) { + if (from == null) { from = cm.doc.first; } + if (to == null) { to = cm.doc.first + cm.doc.size; } + if (!lendiff) { lendiff = 0; } + + var display = cm.display; + if (lendiff && to < display.viewTo && + (display.updateLineNumbers == null || display.updateLineNumbers > from)) + { display.updateLineNumbers = from; } + + cm.curOp.viewChanged = true; + + if (from >= display.viewTo) { // Change after + if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo) + { resetView(cm); } + } else if (to <= display.viewFrom) { // Change before + if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) { + resetView(cm); + } else { + display.viewFrom += lendiff; + display.viewTo += lendiff; + } + } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap + resetView(cm); + } else if (from <= display.viewFrom) { // Top overlap + var cut = viewCuttingPoint(cm, to, to + lendiff, 1); + if (cut) { + display.view = display.view.slice(cut.index); + display.viewFrom = cut.lineN; + display.viewTo += lendiff; + } else { + resetView(cm); + } + } else if (to >= display.viewTo) { // Bottom overlap + var cut$1 = viewCuttingPoint(cm, from, from, -1); + if (cut$1) { + display.view = display.view.slice(0, cut$1.index); + display.viewTo = cut$1.lineN; + } else { + resetView(cm); + } + } else { // Gap in the middle + var cutTop = viewCuttingPoint(cm, from, from, -1); + var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1); + if (cutTop && cutBot) { + display.view = display.view.slice(0, cutTop.index) + .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN)) + .concat(display.view.slice(cutBot.index)); + display.viewTo += lendiff; + } else { + resetView(cm); + } + } + + var ext = display.externalMeasured; + if (ext) { + if (to < ext.lineN) + { ext.lineN += lendiff; } + else if (from < ext.lineN + ext.size) + { display.externalMeasured = null; } + } + } + + // Register a change to a single line. Type must be one of "text", + // "gutter", "class", "widget" + function regLineChange(cm, line, type) { + cm.curOp.viewChanged = true; + var display = cm.display, ext = cm.display.externalMeasured; + if (ext && line >= ext.lineN && line < ext.lineN + ext.size) + { display.externalMeasured = null; } + + if (line < display.viewFrom || line >= display.viewTo) { return } + var lineView = display.view[findViewIndex(cm, line)]; + if (lineView.node == null) { return } + var arr = lineView.changes || (lineView.changes = []); + if (indexOf(arr, type) == -1) { arr.push(type); } + } + + // Clear the view. + function resetView(cm) { + cm.display.viewFrom = cm.display.viewTo = cm.doc.first; + cm.display.view = []; + cm.display.viewOffset = 0; + } + + function viewCuttingPoint(cm, oldN, newN, dir) { + var index = findViewIndex(cm, oldN), diff, view = cm.display.view; + if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size) + { return {index: index, lineN: newN} } + var n = cm.display.viewFrom; + for (var i = 0; i < index; i++) + { n += view[i].size; } + if (n != oldN) { + if (dir > 0) { + if (index == view.length - 1) { return null } + diff = (n + view[index].size) - oldN; + index++; + } else { + diff = n - oldN; + } + oldN += diff; newN += diff; + } + while (visualLineNo(cm.doc, newN) != newN) { + if (index == (dir < 0 ? 0 : view.length - 1)) { return null } + newN += dir * view[index - (dir < 0 ? 1 : 0)].size; + index += dir; + } + return {index: index, lineN: newN} + } + + // Force the view to cover a given range, adding empty view element + // or clipping off existing ones as needed. + function adjustView(cm, from, to) { + var display = cm.display, view = display.view; + if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) { + display.view = buildViewArray(cm, from, to); + display.viewFrom = from; + } else { + if (display.viewFrom > from) + { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); } + else if (display.viewFrom < from) + { display.view = display.view.slice(findViewIndex(cm, from)); } + display.viewFrom = from; + if (display.viewTo < to) + { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); } + else if (display.viewTo > to) + { display.view = display.view.slice(0, findViewIndex(cm, to)); } + } + display.viewTo = to; + } + + // Count the number of lines in the view whose DOM representation is + // out of date (or nonexistent). + function countDirtyView(cm) { + var view = cm.display.view, dirty = 0; + for (var i = 0; i < view.length; i++) { + var lineView = view[i]; + if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; } + } + return dirty + } + + function updateSelection(cm) { + cm.display.input.showSelection(cm.display.input.prepareSelection()); + } + + function prepareSelection(cm, primary) { + if ( primary === void 0 ) primary = true; + + var doc = cm.doc, result = {}; + var curFragment = result.cursors = document.createDocumentFragment(); + var selFragment = result.selection = document.createDocumentFragment(); + + var customCursor = cm.options.$customCursor; + if (customCursor) { primary = true; } + for (var i = 0; i < doc.sel.ranges.length; i++) { + if (!primary && i == doc.sel.primIndex) { continue } + var range = doc.sel.ranges[i]; + if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue } + var collapsed = range.empty(); + if (customCursor) { + var head = customCursor(cm, range); + if (head) { drawSelectionCursor(cm, head, curFragment); } + } else if (collapsed || cm.options.showCursorWhenSelecting) { + drawSelectionCursor(cm, range.head, curFragment); + } + if (!collapsed) + { drawSelectionRange(cm, range, selFragment); } + } + return result + } + + // Draws a cursor for the given range + function drawSelectionCursor(cm, head, output) { + var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine); + + var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")); + cursor.style.left = pos.left + "px"; + cursor.style.top = pos.top + "px"; + cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"; + + if (/\bcm-fat-cursor\b/.test(cm.getWrapperElement().className)) { + var charPos = charCoords(cm, head, "div", null, null); + var width = charPos.right - charPos.left; + cursor.style.width = (width > 0 ? width : cm.defaultCharWidth()) + "px"; + } + + if (pos.other) { + // Secondary cursor, shown when on a 'jump' in bi-directional text + var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor")); + otherCursor.style.display = ""; + otherCursor.style.left = pos.other.left + "px"; + otherCursor.style.top = pos.other.top + "px"; + otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"; + } + } + + function cmpCoords(a, b) { return a.top - b.top || a.left - b.left } + + // Draws the given range as a highlighted selection + function drawSelectionRange(cm, range, output) { + var display = cm.display, doc = cm.doc; + var fragment = document.createDocumentFragment(); + var padding = paddingH(cm.display), leftSide = padding.left; + var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right; + var docLTR = doc.direction == "ltr"; + + function add(left, top, width, bottom) { + if (top < 0) { top = 0; } + top = Math.round(top); + bottom = Math.round(bottom); + fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: absolute; left: " + left + "px;\n top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px;\n height: " + (bottom - top) + "px"))); + } + + function drawForLine(line, fromArg, toArg) { + var lineObj = getLine(doc, line); + var lineLen = lineObj.text.length; + var start, end; + function coords(ch, bias) { + return charCoords(cm, Pos(line, ch), "div", lineObj, bias) + } + + function wrapX(pos, dir, side) { + var extent = wrappedLineExtentChar(cm, lineObj, null, pos); + var prop = (dir == "ltr") == (side == "after") ? "left" : "right"; + var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1); + return coords(ch, prop)[prop] + } + + var order = getOrder(lineObj, doc.direction); + iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) { + var ltr = dir == "ltr"; + var fromPos = coords(from, ltr ? "left" : "right"); + var toPos = coords(to - 1, ltr ? "right" : "left"); + + var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen; + var first = i == 0, last = !order || i == order.length - 1; + if (toPos.top - fromPos.top <= 3) { // Single line + var openLeft = (docLTR ? openStart : openEnd) && first; + var openRight = (docLTR ? openEnd : openStart) && last; + var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left; + var right = openRight ? rightSide : (ltr ? toPos : fromPos).right; + add(left, fromPos.top, right - left, fromPos.bottom); + } else { // Multiple lines + var topLeft, topRight, botLeft, botRight; + if (ltr) { + topLeft = docLTR && openStart && first ? leftSide : fromPos.left; + topRight = docLTR ? rightSide : wrapX(from, dir, "before"); + botLeft = docLTR ? leftSide : wrapX(to, dir, "after"); + botRight = docLTR && openEnd && last ? rightSide : toPos.right; + } else { + topLeft = !docLTR ? leftSide : wrapX(from, dir, "before"); + topRight = !docLTR && openStart && first ? rightSide : fromPos.right; + botLeft = !docLTR && openEnd && last ? leftSide : toPos.left; + botRight = !docLTR ? rightSide : wrapX(to, dir, "after"); + } + add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom); + if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); } + add(botLeft, toPos.top, botRight - botLeft, toPos.bottom); + } + + if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; } + if (cmpCoords(toPos, start) < 0) { start = toPos; } + if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; } + if (cmpCoords(toPos, end) < 0) { end = toPos; } + }); + return {start: start, end: end} + } + + var sFrom = range.from(), sTo = range.to(); + if (sFrom.line == sTo.line) { + drawForLine(sFrom.line, sFrom.ch, sTo.ch); + } else { + var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line); + var singleVLine = visualLine(fromLine) == visualLine(toLine); + var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end; + var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start; + if (singleVLine) { + if (leftEnd.top < rightStart.top - 2) { + add(leftEnd.right, leftEnd.top, null, leftEnd.bottom); + add(leftSide, rightStart.top, rightStart.left, rightStart.bottom); + } else { + add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom); + } + } + if (leftEnd.bottom < rightStart.top) + { add(leftSide, leftEnd.bottom, null, rightStart.top); } + } + + output.appendChild(fragment); + } + + // Cursor-blinking + function restartBlink(cm) { + if (!cm.state.focused) { return } + var display = cm.display; + clearInterval(display.blinker); + var on = true; + display.cursorDiv.style.visibility = ""; + if (cm.options.cursorBlinkRate > 0) + { display.blinker = setInterval(function () { + if (!cm.hasFocus()) { onBlur(cm); } + display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; + }, cm.options.cursorBlinkRate); } + else if (cm.options.cursorBlinkRate < 0) + { display.cursorDiv.style.visibility = "hidden"; } + } + + function ensureFocus(cm) { + if (!cm.hasFocus()) { + cm.display.input.focus(); + if (!cm.state.focused) { onFocus(cm); } + } + } + + function delayBlurEvent(cm) { + cm.state.delayingBlurEvent = true; + setTimeout(function () { if (cm.state.delayingBlurEvent) { + cm.state.delayingBlurEvent = false; + if (cm.state.focused) { onBlur(cm); } + } }, 100); + } + + function onFocus(cm, e) { + if (cm.state.delayingBlurEvent && !cm.state.draggingText) { cm.state.delayingBlurEvent = false; } + + if (cm.options.readOnly == "nocursor") { return } + if (!cm.state.focused) { + signal(cm, "focus", cm, e); + cm.state.focused = true; + addClass(cm.display.wrapper, "CodeMirror-focused"); + // This test prevents this from firing when a context + // menu is closed (since the input reset would kill the + // select-all detection hack) + if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) { + cm.display.input.reset(); + if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730 + } + cm.display.input.receivedFocus(); + } + restartBlink(cm); + } + function onBlur(cm, e) { + if (cm.state.delayingBlurEvent) { return } + + if (cm.state.focused) { + signal(cm, "blur", cm, e); + cm.state.focused = false; + rmClass(cm.display.wrapper, "CodeMirror-focused"); + } + clearInterval(cm.display.blinker); + setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150); + } + + // Read the actual heights of the rendered lines, and update their + // stored heights to match. + function updateHeightsInViewport(cm) { + var display = cm.display; + var prevBottom = display.lineDiv.offsetTop; + var viewTop = Math.max(0, display.scroller.getBoundingClientRect().top); + var oldHeight = display.lineDiv.getBoundingClientRect().top; + var mustScroll = 0; + for (var i = 0; i < display.view.length; i++) { + var cur = display.view[i], wrapping = cm.options.lineWrapping; + var height = (void 0), width = 0; + if (cur.hidden) { continue } + oldHeight += cur.line.height; + if (ie && ie_version < 8) { + var bot = cur.node.offsetTop + cur.node.offsetHeight; + height = bot - prevBottom; + prevBottom = bot; + } else { + var box = cur.node.getBoundingClientRect(); + height = box.bottom - box.top; + // Check that lines don't extend past the right of the current + // editor width + if (!wrapping && cur.text.firstChild) + { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; } + } + var diff = cur.line.height - height; + if (diff > .005 || diff < -.005) { + if (oldHeight < viewTop) { mustScroll -= diff; } + updateLineHeight(cur.line, height); + updateWidgetHeight(cur.line); + if (cur.rest) { for (var j = 0; j < cur.rest.length; j++) + { updateWidgetHeight(cur.rest[j]); } } + } + if (width > cm.display.sizerWidth) { + var chWidth = Math.ceil(width / charWidth(cm.display)); + if (chWidth > cm.display.maxLineLength) { + cm.display.maxLineLength = chWidth; + cm.display.maxLine = cur.line; + cm.display.maxLineChanged = true; + } + } + } + if (Math.abs(mustScroll) > 2) { display.scroller.scrollTop += mustScroll; } + } + + // Read and store the height of line widgets associated with the + // given line. + function updateWidgetHeight(line) { + if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) { + var w = line.widgets[i], parent = w.node.parentNode; + if (parent) { w.height = parent.offsetHeight; } + } } + } + + // Compute the lines that are visible in a given viewport (defaults + // the the current scroll position). viewport may contain top, + // height, and ensure (see op.scrollToPos) properties. + function visibleLines(display, doc, viewport) { + var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop; + top = Math.floor(top - paddingTop(display)); + var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight; + + var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom); + // Ensure is a {from: {line, ch}, to: {line, ch}} object, and + // forces those lines into the viewport (if possible). + if (viewport && viewport.ensure) { + var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line; + if (ensureFrom < from) { + from = ensureFrom; + to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight); + } else if (Math.min(ensureTo, doc.lastLine()) >= to) { + from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight); + to = ensureTo; + } + } + return {from: from, to: Math.max(to, from + 1)} + } + + // SCROLLING THINGS INTO VIEW + + // If an editor sits on the top or bottom of the window, partially + // scrolled out of view, this ensures that the cursor is visible. + function maybeScrollWindow(cm, rect) { + if (signalDOMEvent(cm, "scrollCursorIntoView")) { return } + + var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null; + var doc = display.wrapper.ownerDocument; + if (rect.top + box.top < 0) { doScroll = true; } + else if (rect.bottom + box.top > (doc.defaultView.innerHeight || doc.documentElement.clientHeight)) { doScroll = false; } + if (doScroll != null && !phantom) { + var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n top: " + (rect.top - display.viewOffset - paddingTop(cm.display)) + "px;\n height: " + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + "px;\n left: " + (rect.left) + "px; width: " + (Math.max(2, rect.right - rect.left)) + "px;")); + cm.display.lineSpace.appendChild(scrollNode); + scrollNode.scrollIntoView(doScroll); + cm.display.lineSpace.removeChild(scrollNode); + } + } + + // Scroll a given position into view (immediately), verifying that + // it actually became visible (as line heights are accurately + // measured, the position of something may 'drift' during drawing). + function scrollPosIntoView(cm, pos, end, margin) { + if (margin == null) { margin = 0; } + var rect; + if (!cm.options.lineWrapping && pos == end) { + // Set pos and end to the cursor positions around the character pos sticks to + // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch + // If pos == Pos(_, 0, "before"), pos and end are unchanged + end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos; + pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos; + } + for (var limit = 0; limit < 5; limit++) { + var changed = false; + var coords = cursorCoords(cm, pos); + var endCoords = !end || end == pos ? coords : cursorCoords(cm, end); + rect = {left: Math.min(coords.left, endCoords.left), + top: Math.min(coords.top, endCoords.top) - margin, + right: Math.max(coords.left, endCoords.left), + bottom: Math.max(coords.bottom, endCoords.bottom) + margin}; + var scrollPos = calculateScrollPos(cm, rect); + var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft; + if (scrollPos.scrollTop != null) { + updateScrollTop(cm, scrollPos.scrollTop); + if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; } + } + if (scrollPos.scrollLeft != null) { + setScrollLeft(cm, scrollPos.scrollLeft); + if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; } + } + if (!changed) { break } + } + return rect + } + + // Scroll a given set of coordinates into view (immediately). + function scrollIntoView(cm, rect) { + var scrollPos = calculateScrollPos(cm, rect); + if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); } + if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); } + } + + // Calculate a new scroll position needed to scroll the given + // rectangle into view. Returns an object with scrollTop and + // scrollLeft properties. When these are undefined, the + // vertical/horizontal position does not need to be adjusted. + function calculateScrollPos(cm, rect) { + var display = cm.display, snapMargin = textHeight(cm.display); + if (rect.top < 0) { rect.top = 0; } + var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop; + var screen = displayHeight(cm), result = {}; + if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; } + var docBottom = cm.doc.height + paddingVert(display); + var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin; + if (rect.top < screentop) { + result.scrollTop = atTop ? 0 : rect.top; + } else if (rect.bottom > screentop + screen) { + var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen); + if (newTop != screentop) { result.scrollTop = newTop; } + } + + var gutterSpace = cm.options.fixedGutter ? 0 : display.gutters.offsetWidth; + var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft - gutterSpace; + var screenw = displayWidth(cm) - display.gutters.offsetWidth; + var tooWide = rect.right - rect.left > screenw; + if (tooWide) { rect.right = rect.left + screenw; } + if (rect.left < 10) + { result.scrollLeft = 0; } + else if (rect.left < screenleft) + { result.scrollLeft = Math.max(0, rect.left + gutterSpace - (tooWide ? 0 : 10)); } + else if (rect.right > screenw + screenleft - 3) + { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; } + return result + } + + // Store a relative adjustment to the scroll position in the current + // operation (to be applied when the operation finishes). + function addToScrollTop(cm, top) { + if (top == null) { return } + resolveScrollToPos(cm); + cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top; + } + + // Make sure that at the end of the operation the current cursor is + // shown. + function ensureCursorVisible(cm) { + resolveScrollToPos(cm); + var cur = cm.getCursor(); + cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin}; + } + + function scrollToCoords(cm, x, y) { + if (x != null || y != null) { resolveScrollToPos(cm); } + if (x != null) { cm.curOp.scrollLeft = x; } + if (y != null) { cm.curOp.scrollTop = y; } + } + + function scrollToRange(cm, range) { + resolveScrollToPos(cm); + cm.curOp.scrollToPos = range; + } + + // When an operation has its scrollToPos property set, and another + // scroll action is applied before the end of the operation, this + // 'simulates' scrolling that position into view in a cheap way, so + // that the effect of intermediate scroll commands is not ignored. + function resolveScrollToPos(cm) { + var range = cm.curOp.scrollToPos; + if (range) { + cm.curOp.scrollToPos = null; + var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to); + scrollToCoordsRange(cm, from, to, range.margin); + } + } + + function scrollToCoordsRange(cm, from, to, margin) { + var sPos = calculateScrollPos(cm, { + left: Math.min(from.left, to.left), + top: Math.min(from.top, to.top) - margin, + right: Math.max(from.right, to.right), + bottom: Math.max(from.bottom, to.bottom) + margin + }); + scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop); + } + + // Sync the scrollable area and scrollbars, ensure the viewport + // covers the visible area. + function updateScrollTop(cm, val) { + if (Math.abs(cm.doc.scrollTop - val) < 2) { return } + if (!gecko) { updateDisplaySimple(cm, {top: val}); } + setScrollTop(cm, val, true); + if (gecko) { updateDisplaySimple(cm); } + startWorker(cm, 100); + } + + function setScrollTop(cm, val, forceScroll) { + val = Math.max(0, Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val)); + if (cm.display.scroller.scrollTop == val && !forceScroll) { return } + cm.doc.scrollTop = val; + cm.display.scrollbars.setScrollTop(val); + if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; } + } + + // Sync scroller and scrollbar, ensure the gutter elements are + // aligned. + function setScrollLeft(cm, val, isScroller, forceScroll) { + val = Math.max(0, Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth)); + if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return } + cm.doc.scrollLeft = val; + alignHorizontally(cm); + if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; } + cm.display.scrollbars.setScrollLeft(val); + } + + // SCROLLBARS + + // Prepare DOM reads needed to update the scrollbars. Done in one + // shot to minimize update/measure roundtrips. + function measureForScrollbars(cm) { + var d = cm.display, gutterW = d.gutters.offsetWidth; + var docH = Math.round(cm.doc.height + paddingVert(cm.display)); + return { + clientHeight: d.scroller.clientHeight, + viewHeight: d.wrapper.clientHeight, + scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth, + viewWidth: d.wrapper.clientWidth, + barLeft: cm.options.fixedGutter ? gutterW : 0, + docHeight: docH, + scrollHeight: docH + scrollGap(cm) + d.barHeight, + nativeBarWidth: d.nativeBarWidth, + gutterWidth: gutterW + } + } + + var NativeScrollbars = function(place, scroll, cm) { + this.cm = cm; + var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar"); + var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar"); + vert.tabIndex = horiz.tabIndex = -1; + place(vert); place(horiz); + + on(vert, "scroll", function () { + if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); } + }); + on(horiz, "scroll", function () { + if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); } + }); + + this.checkedZeroWidth = false; + // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). + if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; } + }; + + NativeScrollbars.prototype.update = function (measure) { + var needsH = measure.scrollWidth > measure.clientWidth + 1; + var needsV = measure.scrollHeight > measure.clientHeight + 1; + var sWidth = measure.nativeBarWidth; + + if (needsV) { + this.vert.style.display = "block"; + this.vert.style.bottom = needsH ? sWidth + "px" : "0"; + var totalHeight = measure.viewHeight - (needsH ? sWidth : 0); + // A bug in IE8 can cause this value to be negative, so guard it. + this.vert.firstChild.style.height = + Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"; + } else { + this.vert.scrollTop = 0; + this.vert.style.display = ""; + this.vert.firstChild.style.height = "0"; + } + + if (needsH) { + this.horiz.style.display = "block"; + this.horiz.style.right = needsV ? sWidth + "px" : "0"; + this.horiz.style.left = measure.barLeft + "px"; + var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0); + this.horiz.firstChild.style.width = + Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px"; + } else { + this.horiz.style.display = ""; + this.horiz.firstChild.style.width = "0"; + } + + if (!this.checkedZeroWidth && measure.clientHeight > 0) { + if (sWidth == 0) { this.zeroWidthHack(); } + this.checkedZeroWidth = true; + } + + return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0} + }; + + NativeScrollbars.prototype.setScrollLeft = function (pos) { + if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; } + if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); } + }; + + NativeScrollbars.prototype.setScrollTop = function (pos) { + if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; } + if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); } + }; + + NativeScrollbars.prototype.zeroWidthHack = function () { + var w = mac && !mac_geMountainLion ? "12px" : "18px"; + this.horiz.style.height = this.vert.style.width = w; + this.horiz.style.visibility = this.vert.style.visibility = "hidden"; + this.disableHoriz = new Delayed; + this.disableVert = new Delayed; + }; + + NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) { + bar.style.visibility = ""; + function maybeDisable() { + // To find out whether the scrollbar is still visible, we + // check whether the element under the pixel in the bottom + // right corner of the scrollbar box is the scrollbar box + // itself (when the bar is still visible) or its filler child + // (when the bar is hidden). If it is still visible, we keep + // it enabled, if it's hidden, we disable pointer events. + var box = bar.getBoundingClientRect(); + var elt = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2) + : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1); + if (elt != bar) { bar.style.visibility = "hidden"; } + else { delay.set(1000, maybeDisable); } + } + delay.set(1000, maybeDisable); + }; + + NativeScrollbars.prototype.clear = function () { + var parent = this.horiz.parentNode; + parent.removeChild(this.horiz); + parent.removeChild(this.vert); + }; + + var NullScrollbars = function () {}; + + NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} }; + NullScrollbars.prototype.setScrollLeft = function () {}; + NullScrollbars.prototype.setScrollTop = function () {}; + NullScrollbars.prototype.clear = function () {}; + + function updateScrollbars(cm, measure) { + if (!measure) { measure = measureForScrollbars(cm); } + var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight; + updateScrollbarsInner(cm, measure); + for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) { + if (startWidth != cm.display.barWidth && cm.options.lineWrapping) + { updateHeightsInViewport(cm); } + updateScrollbarsInner(cm, measureForScrollbars(cm)); + startWidth = cm.display.barWidth; startHeight = cm.display.barHeight; + } + } + + // Re-synchronize the fake scrollbars with the actual size of the + // content. + function updateScrollbarsInner(cm, measure) { + var d = cm.display; + var sizes = d.scrollbars.update(measure); + + d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"; + d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"; + d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"; + + if (sizes.right && sizes.bottom) { + d.scrollbarFiller.style.display = "block"; + d.scrollbarFiller.style.height = sizes.bottom + "px"; + d.scrollbarFiller.style.width = sizes.right + "px"; + } else { d.scrollbarFiller.style.display = ""; } + if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) { + d.gutterFiller.style.display = "block"; + d.gutterFiller.style.height = sizes.bottom + "px"; + d.gutterFiller.style.width = measure.gutterWidth + "px"; + } else { d.gutterFiller.style.display = ""; } + } + + var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}; + + function initScrollbars(cm) { + if (cm.display.scrollbars) { + cm.display.scrollbars.clear(); + if (cm.display.scrollbars.addClass) + { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); } + } + + cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) { + cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller); + // Prevent clicks in the scrollbars from killing focus + on(node, "mousedown", function () { + if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); } + }); + node.setAttribute("cm-not-content", "true"); + }, function (pos, axis) { + if (axis == "horizontal") { setScrollLeft(cm, pos); } + else { updateScrollTop(cm, pos); } + }, cm); + if (cm.display.scrollbars.addClass) + { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); } + } + + // Operations are used to wrap a series of changes to the editor + // state in such a way that each change won't have to update the + // cursor and display (which would be awkward, slow, and + // error-prone). Instead, display updates are batched and then all + // combined and executed at once. + + var nextOpId = 0; + // Start a new operation. + function startOperation(cm) { + cm.curOp = { + cm: cm, + viewChanged: false, // Flag that indicates that lines might need to be redrawn + startHeight: cm.doc.height, // Used to detect need to update scrollbar + forceUpdate: false, // Used to force a redraw + updateInput: 0, // Whether to reset the input textarea + typing: false, // Whether this reset should be careful to leave existing text (for compositing) + changeObjs: null, // Accumulated changes, for firing change events + cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on + cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already + selectionChanged: false, // Whether the selection needs to be redrawn + updateMaxLine: false, // Set when the widest line needs to be determined anew + scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet + scrollToPos: null, // Used to scroll to a specific position + focus: false, + id: ++nextOpId, // Unique ID + markArrays: null // Used by addMarkedSpan + }; + pushOperation(cm.curOp); + } + + // Finish an operation, updating the display and signalling delayed events + function endOperation(cm) { + var op = cm.curOp; + if (op) { finishOperation(op, function (group) { + for (var i = 0; i < group.ops.length; i++) + { group.ops[i].cm.curOp = null; } + endOperations(group); + }); } + } + + // The DOM updates done when an operation finishes are batched so + // that the minimum number of relayouts are required. + function endOperations(group) { + var ops = group.ops; + for (var i = 0; i < ops.length; i++) // Read DOM + { endOperation_R1(ops[i]); } + for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe) + { endOperation_W1(ops[i$1]); } + for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM + { endOperation_R2(ops[i$2]); } + for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe) + { endOperation_W2(ops[i$3]); } + for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM + { endOperation_finish(ops[i$4]); } + } + + function endOperation_R1(op) { + var cm = op.cm, display = cm.display; + maybeClipScrollbars(cm); + if (op.updateMaxLine) { findMaxLine(cm); } + + op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null || + op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom || + op.scrollToPos.to.line >= display.viewTo) || + display.maxLineChanged && cm.options.lineWrapping; + op.update = op.mustUpdate && + new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate); + } + + function endOperation_W1(op) { + op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update); + } + + function endOperation_R2(op) { + var cm = op.cm, display = cm.display; + if (op.updatedDisplay) { updateHeightsInViewport(cm); } + + op.barMeasure = measureForScrollbars(cm); + + // If the max line changed since it was last measured, measure it, + // and ensure the document's width matches it. + // updateDisplay_W2 will use these properties to do the actual resizing + if (display.maxLineChanged && !cm.options.lineWrapping) { + op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3; + cm.display.sizerWidth = op.adjustWidthTo; + op.barMeasure.scrollWidth = + Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth); + op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm)); + } + + if (op.updatedDisplay || op.selectionChanged) + { op.preparedSelection = display.input.prepareSelection(); } + } + + function endOperation_W2(op) { + var cm = op.cm; + + if (op.adjustWidthTo != null) { + cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"; + if (op.maxScrollLeft < cm.doc.scrollLeft) + { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); } + cm.display.maxLineChanged = false; + } + + var takeFocus = op.focus && op.focus == activeElt(doc(cm)); + if (op.preparedSelection) + { cm.display.input.showSelection(op.preparedSelection, takeFocus); } + if (op.updatedDisplay || op.startHeight != cm.doc.height) + { updateScrollbars(cm, op.barMeasure); } + if (op.updatedDisplay) + { setDocumentHeight(cm, op.barMeasure); } + + if (op.selectionChanged) { restartBlink(cm); } + + if (cm.state.focused && op.updateInput) + { cm.display.input.reset(op.typing); } + if (takeFocus) { ensureFocus(op.cm); } + } + + function endOperation_finish(op) { + var cm = op.cm, display = cm.display, doc = cm.doc; + + if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); } + + // Abort mouse wheel delta measurement, when scrolling explicitly + if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos)) + { display.wheelStartX = display.wheelStartY = null; } + + // Propagate the scroll position to the actual DOM scroller + if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); } + + if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); } + // If we need to scroll a specific position into view, do so. + if (op.scrollToPos) { + var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from), + clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin); + maybeScrollWindow(cm, rect); + } + + // Fire events for markers that are hidden/unidden by editing or + // undoing + var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers; + if (hidden) { for (var i = 0; i < hidden.length; ++i) + { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } } + if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1) + { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } } + + if (display.wrapper.offsetHeight) + { doc.scrollTop = cm.display.scroller.scrollTop; } + + // Fire change events, and delayed event handlers + if (op.changeObjs) + { signal(cm, "changes", cm, op.changeObjs); } + if (op.update) + { op.update.finish(); } + } + + // Run the given function in an operation + function runInOp(cm, f) { + if (cm.curOp) { return f() } + startOperation(cm); + try { return f() } + finally { endOperation(cm); } + } + // Wraps a function in an operation. Returns the wrapped function. + function operation(cm, f) { + return function() { + if (cm.curOp) { return f.apply(cm, arguments) } + startOperation(cm); + try { return f.apply(cm, arguments) } + finally { endOperation(cm); } + } + } + // Used to add methods to editor and doc instances, wrapping them in + // operations. + function methodOp(f) { + return function() { + if (this.curOp) { return f.apply(this, arguments) } + startOperation(this); + try { return f.apply(this, arguments) } + finally { endOperation(this); } + } + } + function docMethodOp(f) { + return function() { + var cm = this.cm; + if (!cm || cm.curOp) { return f.apply(this, arguments) } + startOperation(cm); + try { return f.apply(this, arguments) } + finally { endOperation(cm); } + } + } + + // HIGHLIGHT WORKER + + function startWorker(cm, time) { + if (cm.doc.highlightFrontier < cm.display.viewTo) + { cm.state.highlight.set(time, bind(highlightWorker, cm)); } + } + + function highlightWorker(cm) { + var doc = cm.doc; + if (doc.highlightFrontier >= cm.display.viewTo) { return } + var end = +new Date + cm.options.workTime; + var context = getContextBefore(cm, doc.highlightFrontier); + var changedLines = []; + + doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) { + if (context.line >= cm.display.viewFrom) { // Visible + var oldStyles = line.styles; + var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null; + var highlighted = highlightLine(cm, line, context, true); + if (resetState) { context.state = resetState; } + line.styles = highlighted.styles; + var oldCls = line.styleClasses, newCls = highlighted.classes; + if (newCls) { line.styleClasses = newCls; } + else if (oldCls) { line.styleClasses = null; } + var ischange = !oldStyles || oldStyles.length != line.styles.length || + oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass); + for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; } + if (ischange) { changedLines.push(context.line); } + line.stateAfter = context.save(); + context.nextLine(); + } else { + if (line.text.length <= cm.options.maxHighlightLength) + { processLine(cm, line.text, context); } + line.stateAfter = context.line % 5 == 0 ? context.save() : null; + context.nextLine(); + } + if (+new Date > end) { + startWorker(cm, cm.options.workDelay); + return true + } + }); + doc.highlightFrontier = context.line; + doc.modeFrontier = Math.max(doc.modeFrontier, context.line); + if (changedLines.length) { runInOp(cm, function () { + for (var i = 0; i < changedLines.length; i++) + { regLineChange(cm, changedLines[i], "text"); } + }); } + } + + // DISPLAY DRAWING + + var DisplayUpdate = function(cm, viewport, force) { + var display = cm.display; + + this.viewport = viewport; + // Store some values that we'll need later (but don't want to force a relayout for) + this.visible = visibleLines(display, cm.doc, viewport); + this.editorIsHidden = !display.wrapper.offsetWidth; + this.wrapperHeight = display.wrapper.clientHeight; + this.wrapperWidth = display.wrapper.clientWidth; + this.oldDisplayWidth = displayWidth(cm); + this.force = force; + this.dims = getDimensions(cm); + this.events = []; + }; + + DisplayUpdate.prototype.signal = function (emitter, type) { + if (hasHandler(emitter, type)) + { this.events.push(arguments); } + }; + DisplayUpdate.prototype.finish = function () { + for (var i = 0; i < this.events.length; i++) + { signal.apply(null, this.events[i]); } + }; + + function maybeClipScrollbars(cm) { + var display = cm.display; + if (!display.scrollbarsClipped && display.scroller.offsetWidth) { + display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth; + display.heightForcer.style.height = scrollGap(cm) + "px"; + display.sizer.style.marginBottom = -display.nativeBarWidth + "px"; + display.sizer.style.borderRightWidth = scrollGap(cm) + "px"; + display.scrollbarsClipped = true; + } + } + + function selectionSnapshot(cm) { + if (cm.hasFocus()) { return null } + var active = activeElt(doc(cm)); + if (!active || !contains(cm.display.lineDiv, active)) { return null } + var result = {activeElt: active}; + if (window.getSelection) { + var sel = win(cm).getSelection(); + if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) { + result.anchorNode = sel.anchorNode; + result.anchorOffset = sel.anchorOffset; + result.focusNode = sel.focusNode; + result.focusOffset = sel.focusOffset; + } + } + return result + } + + function restoreSelection(snapshot) { + if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt(snapshot.activeElt.ownerDocument)) { return } + snapshot.activeElt.focus(); + if (!/^(INPUT|TEXTAREA)$/.test(snapshot.activeElt.nodeName) && + snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) { + var doc = snapshot.activeElt.ownerDocument; + var sel = doc.defaultView.getSelection(), range = doc.createRange(); + range.setEnd(snapshot.anchorNode, snapshot.anchorOffset); + range.collapse(false); + sel.removeAllRanges(); + sel.addRange(range); + sel.extend(snapshot.focusNode, snapshot.focusOffset); + } + } + + // Does the actual updating of the line display. Bails out + // (returning false) when there is nothing to be done and forced is + // false. + function updateDisplayIfNeeded(cm, update) { + var display = cm.display, doc = cm.doc; + + if (update.editorIsHidden) { + resetView(cm); + return false + } + + // Bail out if the visible area is already rendered and nothing changed. + if (!update.force && + update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo && + (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) && + display.renderedView == display.view && countDirtyView(cm) == 0) + { return false } + + if (maybeUpdateLineNumberWidth(cm)) { + resetView(cm); + update.dims = getDimensions(cm); + } + + // Compute a suitable new viewport (from & to) + var end = doc.first + doc.size; + var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first); + var to = Math.min(end, update.visible.to + cm.options.viewportMargin); + if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); } + if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); } + if (sawCollapsedSpans) { + from = visualLineNo(cm.doc, from); + to = visualLineEndNo(cm.doc, to); + } + + var different = from != display.viewFrom || to != display.viewTo || + display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth; + adjustView(cm, from, to); + + display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom)); + // Position the mover div to align with the current scroll position + cm.display.mover.style.top = display.viewOffset + "px"; + + var toUpdate = countDirtyView(cm); + if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view && + (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo)) + { return false } + + // For big changes, we hide the enclosing element during the + // update, since that speeds up the operations on most browsers. + var selSnapshot = selectionSnapshot(cm); + if (toUpdate > 4) { display.lineDiv.style.display = "none"; } + patchDisplay(cm, display.updateLineNumbers, update.dims); + if (toUpdate > 4) { display.lineDiv.style.display = ""; } + display.renderedView = display.view; + // There might have been a widget with a focused element that got + // hidden or updated, if so re-focus it. + restoreSelection(selSnapshot); + + // Prevent selection and cursors from interfering with the scroll + // width and height. + removeChildren(display.cursorDiv); + removeChildren(display.selectionDiv); + display.gutters.style.height = display.sizer.style.minHeight = 0; + + if (different) { + display.lastWrapHeight = update.wrapperHeight; + display.lastWrapWidth = update.wrapperWidth; + startWorker(cm, 400); + } + + display.updateLineNumbers = null; + + return true + } + + function postUpdateDisplay(cm, update) { + var viewport = update.viewport; + + for (var first = true;; first = false) { + if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) { + // Clip forced viewport to actual scrollable area. + if (viewport && viewport.top != null) + { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; } + // Updated line heights might result in the drawn area not + // actually covering the viewport. Keep looping until it does. + update.visible = visibleLines(cm.display, cm.doc, viewport); + if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo) + { break } + } else if (first) { + update.visible = visibleLines(cm.display, cm.doc, viewport); + } + if (!updateDisplayIfNeeded(cm, update)) { break } + updateHeightsInViewport(cm); + var barMeasure = measureForScrollbars(cm); + updateSelection(cm); + updateScrollbars(cm, barMeasure); + setDocumentHeight(cm, barMeasure); + update.force = false; + } + + update.signal(cm, "update", cm); + if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) { + update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo); + cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo; + } + } + + function updateDisplaySimple(cm, viewport) { + var update = new DisplayUpdate(cm, viewport); + if (updateDisplayIfNeeded(cm, update)) { + updateHeightsInViewport(cm); + postUpdateDisplay(cm, update); + var barMeasure = measureForScrollbars(cm); + updateSelection(cm); + updateScrollbars(cm, barMeasure); + setDocumentHeight(cm, barMeasure); + update.finish(); + } + } + + // Sync the actual display DOM structure with display.view, removing + // nodes for lines that are no longer in view, and creating the ones + // that are not there yet, and updating the ones that are out of + // date. + function patchDisplay(cm, updateNumbersFrom, dims) { + var display = cm.display, lineNumbers = cm.options.lineNumbers; + var container = display.lineDiv, cur = container.firstChild; + + function rm(node) { + var next = node.nextSibling; + // Works around a throw-scroll bug in OS X Webkit + if (webkit && mac && cm.display.currentWheelTarget == node) + { node.style.display = "none"; } + else + { node.parentNode.removeChild(node); } + return next + } + + var view = display.view, lineN = display.viewFrom; + // Loop over the elements in the view, syncing cur (the DOM nodes + // in display.lineDiv) with the view as we go. + for (var i = 0; i < view.length; i++) { + var lineView = view[i]; + if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet + var node = buildLineElement(cm, lineView, lineN, dims); + container.insertBefore(node, cur); + } else { // Already drawn + while (cur != lineView.node) { cur = rm(cur); } + var updateNumber = lineNumbers && updateNumbersFrom != null && + updateNumbersFrom <= lineN && lineView.lineNumber; + if (lineView.changes) { + if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; } + updateLineForChanges(cm, lineView, lineN, dims); + } + if (updateNumber) { + removeChildren(lineView.lineNumber); + lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN))); + } + cur = lineView.node.nextSibling; + } + lineN += lineView.size; + } + while (cur) { cur = rm(cur); } + } + + function updateGutterSpace(display) { + var width = display.gutters.offsetWidth; + display.sizer.style.marginLeft = width + "px"; + // Send an event to consumers responding to changes in gutter width. + signalLater(display, "gutterChanged", display); + } + + function setDocumentHeight(cm, measure) { + cm.display.sizer.style.minHeight = measure.docHeight + "px"; + cm.display.heightForcer.style.top = measure.docHeight + "px"; + cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px"; + } + + // Re-align line numbers and gutter marks to compensate for + // horizontal scrolling. + function alignHorizontally(cm) { + var display = cm.display, view = display.view; + if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return } + var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft; + var gutterW = display.gutters.offsetWidth, left = comp + "px"; + for (var i = 0; i < view.length; i++) { if (!view[i].hidden) { + if (cm.options.fixedGutter) { + if (view[i].gutter) + { view[i].gutter.style.left = left; } + if (view[i].gutterBackground) + { view[i].gutterBackground.style.left = left; } + } + var align = view[i].alignable; + if (align) { for (var j = 0; j < align.length; j++) + { align[j].style.left = left; } } + } } + if (cm.options.fixedGutter) + { display.gutters.style.left = (comp + gutterW) + "px"; } + } + + // Used to ensure that the line number gutter is still the right + // size for the current document size. Returns true when an update + // is needed. + function maybeUpdateLineNumberWidth(cm) { + if (!cm.options.lineNumbers) { return false } + var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display; + if (last.length != display.lineNumChars) { + var test = display.measure.appendChild(elt("div", [elt("div", last)], + "CodeMirror-linenumber CodeMirror-gutter-elt")); + var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW; + display.lineGutter.style.width = ""; + display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1; + display.lineNumWidth = display.lineNumInnerWidth + padding; + display.lineNumChars = display.lineNumInnerWidth ? last.length : -1; + display.lineGutter.style.width = display.lineNumWidth + "px"; + updateGutterSpace(cm.display); + return true + } + return false + } + + function getGutters(gutters, lineNumbers) { + var result = [], sawLineNumbers = false; + for (var i = 0; i < gutters.length; i++) { + var name = gutters[i], style = null; + if (typeof name != "string") { style = name.style; name = name.className; } + if (name == "CodeMirror-linenumbers") { + if (!lineNumbers) { continue } + else { sawLineNumbers = true; } + } + result.push({className: name, style: style}); + } + if (lineNumbers && !sawLineNumbers) { result.push({className: "CodeMirror-linenumbers", style: null}); } + return result + } + + // Rebuild the gutter elements, ensure the margin to the left of the + // code matches their width. + function renderGutters(display) { + var gutters = display.gutters, specs = display.gutterSpecs; + removeChildren(gutters); + display.lineGutter = null; + for (var i = 0; i < specs.length; ++i) { + var ref = specs[i]; + var className = ref.className; + var style = ref.style; + var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + className)); + if (style) { gElt.style.cssText = style; } + if (className == "CodeMirror-linenumbers") { + display.lineGutter = gElt; + gElt.style.width = (display.lineNumWidth || 1) + "px"; + } + } + gutters.style.display = specs.length ? "" : "none"; + updateGutterSpace(display); + } + + function updateGutters(cm) { + renderGutters(cm.display); + regChange(cm); + alignHorizontally(cm); + } + + // The display handles the DOM integration, both for input reading + // and content drawing. It holds references to DOM nodes and + // display-related state. + + function Display(place, doc, input, options) { + var d = this; + this.input = input; + + // Covers bottom-right square when both scrollbars are present. + d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); + d.scrollbarFiller.setAttribute("cm-not-content", "true"); + // Covers bottom of gutter when coverGutterNextToScrollbar is on + // and h scrollbar is present. + d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler"); + d.gutterFiller.setAttribute("cm-not-content", "true"); + // Will contain the actual code, positioned to cover the viewport. + d.lineDiv = eltP("div", null, "CodeMirror-code"); + // Elements are added to these to represent selection and cursors. + d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); + d.cursorDiv = elt("div", null, "CodeMirror-cursors"); + // A visibility: hidden element used to find the size of things. + d.measure = elt("div", null, "CodeMirror-measure"); + // When lines outside of the viewport are measured, they are drawn in this. + d.lineMeasure = elt("div", null, "CodeMirror-measure"); + // Wraps everything that needs to exist inside the vertically-padded coordinate system + d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv], + null, "position: relative; outline: none"); + var lines = eltP("div", [d.lineSpace], "CodeMirror-lines"); + // Moved around its parent to cover visible view. + d.mover = elt("div", [lines], null, "position: relative"); + // Set to the height of the document, allowing scrolling. + d.sizer = elt("div", [d.mover], "CodeMirror-sizer"); + d.sizerWidth = null; + // Behavior of elts with overflow: auto and padding is + // inconsistent across browsers. This is used to ensure the + // scrollable area is big enough. + d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;"); + // Will contain the gutters, if any. + d.gutters = elt("div", null, "CodeMirror-gutters"); + d.lineGutter = null; + // Actual scrollable element. + d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll"); + d.scroller.setAttribute("tabIndex", "-1"); + // The element in which the editor lives. + d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror"); + // See #6982. FIXME remove when this has been fixed for a while in Chrome + if (chrome && chrome_version >= 105) { d.wrapper.style.clipPath = "inset(0px)"; } + + // This attribute is respected by automatic translation systems such as Google Translate, + // and may also be respected by tools used by human translators. + d.wrapper.setAttribute('translate', 'no'); + + // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported) + if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; } + if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; } + + if (place) { + if (place.appendChild) { place.appendChild(d.wrapper); } + else { place(d.wrapper); } + } + + // Current rendered range (may be bigger than the view window). + d.viewFrom = d.viewTo = doc.first; + d.reportedViewFrom = d.reportedViewTo = doc.first; + // Information about the rendered lines. + d.view = []; + d.renderedView = null; + // Holds info about a single rendered line when it was rendered + // for measurement, while not in view. + d.externalMeasured = null; + // Empty space (in pixels) above the view + d.viewOffset = 0; + d.lastWrapHeight = d.lastWrapWidth = 0; + d.updateLineNumbers = null; + + d.nativeBarWidth = d.barHeight = d.barWidth = 0; + d.scrollbarsClipped = false; + + // Used to only resize the line number gutter when necessary (when + // the amount of lines crosses a boundary that makes its width change) + d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null; + // Set to true when a non-horizontal-scrolling line widget is + // added. As an optimization, line widget aligning is skipped when + // this is false. + d.alignWidgets = false; + + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; + + // Tracks the maximum line length so that the horizontal scrollbar + // can be kept static when scrolling. + d.maxLine = null; + d.maxLineLength = 0; + d.maxLineChanged = false; + + // Used for measuring wheel scrolling granularity + d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null; + + // True when shift is held down. + d.shift = false; + + // Used to track whether anything happened since the context menu + // was opened. + d.selForContextMenu = null; + + d.activeTouch = null; + + d.gutterSpecs = getGutters(options.gutters, options.lineNumbers); + renderGutters(d); + + input.init(d); + } + + // Since the delta values reported on mouse wheel events are + // unstandardized between browsers and even browser versions, and + // generally horribly unpredictable, this code starts by measuring + // the scroll effect that the first few mouse wheel events have, + // and, from that, detects the way it can convert deltas to pixel + // offsets afterwards. + // + // The reason we want to know the amount a wheel event will scroll + // is that it gives us a chance to update the display before the + // actual scrolling happens, reducing flickering. + + var wheelSamples = 0, wheelPixelsPerUnit = null; + // Fill in a browser-detected starting value on browsers where we + // know one. These don't have to be accurate -- the result of them + // being wrong would just be a slight flicker on the first wheel + // scroll (if it is large enough). + if (ie) { wheelPixelsPerUnit = -.53; } + else if (gecko) { wheelPixelsPerUnit = 15; } + else if (chrome) { wheelPixelsPerUnit = -.7; } + else if (safari) { wheelPixelsPerUnit = -1/3; } + + function wheelEventDelta(e) { + var dx = e.wheelDeltaX, dy = e.wheelDeltaY; + if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; } + if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; } + else if (dy == null) { dy = e.wheelDelta; } + return {x: dx, y: dy} + } + function wheelEventPixels(e) { + var delta = wheelEventDelta(e); + delta.x *= wheelPixelsPerUnit; + delta.y *= wheelPixelsPerUnit; + return delta + } + + function onScrollWheel(cm, e) { + // On Chrome 102, viewport updates somehow stop wheel-based + // scrolling. Turning off pointer events during the scroll seems + // to avoid the issue. + if (chrome && chrome_version == 102) { + if (cm.display.chromeScrollHack == null) { cm.display.sizer.style.pointerEvents = "none"; } + else { clearTimeout(cm.display.chromeScrollHack); } + cm.display.chromeScrollHack = setTimeout(function () { + cm.display.chromeScrollHack = null; + cm.display.sizer.style.pointerEvents = ""; + }, 100); + } + var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y; + var pixelsPerUnit = wheelPixelsPerUnit; + if (e.deltaMode === 0) { + dx = e.deltaX; + dy = e.deltaY; + pixelsPerUnit = 1; + } + + var display = cm.display, scroll = display.scroller; + // Quit if there's nothing to scroll here + var canScrollX = scroll.scrollWidth > scroll.clientWidth; + var canScrollY = scroll.scrollHeight > scroll.clientHeight; + if (!(dx && canScrollX || dy && canScrollY)) { return } + + // Webkit browsers on OS X abort momentum scrolls when the target + // of the scroll event is removed from the scrollable element. + // This hack (see related code in patchDisplay) makes sure the + // element is kept around. + if (dy && mac && webkit) { + outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) { + for (var i = 0; i < view.length; i++) { + if (view[i].node == cur) { + cm.display.currentWheelTarget = cur; + break outer + } + } + } + } + + // On some browsers, horizontal scrolling will cause redraws to + // happen before the gutter has been realigned, causing it to + // wriggle around in a most unseemly way. When we have an + // estimated pixels/delta value, we just handle horizontal + // scrolling entirely here. It'll be slightly off from native, but + // better than glitching out. + if (dx && !gecko && !presto && pixelsPerUnit != null) { + if (dy && canScrollY) + { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * pixelsPerUnit)); } + setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * pixelsPerUnit)); + // Only prevent default scrolling if vertical scrolling is + // actually possible. Otherwise, it causes vertical scroll + // jitter on OSX trackpads when deltaX is small and deltaY + // is large (issue #3579) + if (!dy || (dy && canScrollY)) + { e_preventDefault(e); } + display.wheelStartX = null; // Abort measurement, if in progress + return + } + + // 'Project' the visible viewport to cover the area that is being + // scrolled into view (if we know enough to estimate it). + if (dy && pixelsPerUnit != null) { + var pixels = dy * pixelsPerUnit; + var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight; + if (pixels < 0) { top = Math.max(0, top + pixels - 50); } + else { bot = Math.min(cm.doc.height, bot + pixels + 50); } + updateDisplaySimple(cm, {top: top, bottom: bot}); + } + + if (wheelSamples < 20 && e.deltaMode !== 0) { + if (display.wheelStartX == null) { + display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop; + display.wheelDX = dx; display.wheelDY = dy; + setTimeout(function () { + if (display.wheelStartX == null) { return } + var movedX = scroll.scrollLeft - display.wheelStartX; + var movedY = scroll.scrollTop - display.wheelStartY; + var sample = (movedY && display.wheelDY && movedY / display.wheelDY) || + (movedX && display.wheelDX && movedX / display.wheelDX); + display.wheelStartX = display.wheelStartY = null; + if (!sample) { return } + wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1); + ++wheelSamples; + }, 200); + } else { + display.wheelDX += dx; display.wheelDY += dy; + } + } + } + + // Selection objects are immutable. A new one is created every time + // the selection changes. A selection is one or more non-overlapping + // (and non-touching) ranges, sorted, and an integer that indicates + // which one is the primary selection (the one that's scrolled into + // view, that getCursor returns, etc). + var Selection = function(ranges, primIndex) { + this.ranges = ranges; + this.primIndex = primIndex; + }; + + Selection.prototype.primary = function () { return this.ranges[this.primIndex] }; + + Selection.prototype.equals = function (other) { + if (other == this) { return true } + if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false } + for (var i = 0; i < this.ranges.length; i++) { + var here = this.ranges[i], there = other.ranges[i]; + if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false } + } + return true + }; + + Selection.prototype.deepCopy = function () { + var out = []; + for (var i = 0; i < this.ranges.length; i++) + { out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)); } + return new Selection(out, this.primIndex) + }; + + Selection.prototype.somethingSelected = function () { + for (var i = 0; i < this.ranges.length; i++) + { if (!this.ranges[i].empty()) { return true } } + return false + }; + + Selection.prototype.contains = function (pos, end) { + if (!end) { end = pos; } + for (var i = 0; i < this.ranges.length; i++) { + var range = this.ranges[i]; + if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) + { return i } + } + return -1 + }; + + var Range = function(anchor, head) { + this.anchor = anchor; this.head = head; + }; + + Range.prototype.from = function () { return minPos(this.anchor, this.head) }; + Range.prototype.to = function () { return maxPos(this.anchor, this.head) }; + Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch }; + + // Take an unsorted, potentially overlapping set of ranges, and + // build a selection out of it. 'Consumes' ranges array (modifying + // it). + function normalizeSelection(cm, ranges, primIndex) { + var mayTouch = cm && cm.options.selectionsMayTouch; + var prim = ranges[primIndex]; + ranges.sort(function (a, b) { return cmp(a.from(), b.from()); }); + primIndex = indexOf(ranges, prim); + for (var i = 1; i < ranges.length; i++) { + var cur = ranges[i], prev = ranges[i - 1]; + var diff = cmp(prev.to(), cur.from()); + if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) { + var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to()); + var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head; + if (i <= primIndex) { --primIndex; } + ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to)); + } + } + return new Selection(ranges, primIndex) + } + + function simpleSelection(anchor, head) { + return new Selection([new Range(anchor, head || anchor)], 0) + } + + // Compute the position of the end of a change (its 'to' property + // refers to the pre-change end). + function changeEnd(change) { + if (!change.text) { return change.to } + return Pos(change.from.line + change.text.length - 1, + lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)) + } + + // Adjust a position to refer to the post-change position of the + // same text, or the end of the change if the change covers it. + function adjustForChange(pos, change) { + if (cmp(pos, change.from) < 0) { return pos } + if (cmp(pos, change.to) <= 0) { return changeEnd(change) } + + var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch; + if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; } + return Pos(line, ch) + } + + function computeSelAfterChange(doc, change) { + var out = []; + for (var i = 0; i < doc.sel.ranges.length; i++) { + var range = doc.sel.ranges[i]; + out.push(new Range(adjustForChange(range.anchor, change), + adjustForChange(range.head, change))); + } + return normalizeSelection(doc.cm, out, doc.sel.primIndex) + } + + function offsetPos(pos, old, nw) { + if (pos.line == old.line) + { return Pos(nw.line, pos.ch - old.ch + nw.ch) } + else + { return Pos(nw.line + (pos.line - old.line), pos.ch) } + } + + // Used by replaceSelections to allow moving the selection to the + // start or around the replaced test. Hint may be "start" or "around". + function computeReplacedSel(doc, changes, hint) { + var out = []; + var oldPrev = Pos(doc.first, 0), newPrev = oldPrev; + for (var i = 0; i < changes.length; i++) { + var change = changes[i]; + var from = offsetPos(change.from, oldPrev, newPrev); + var to = offsetPos(changeEnd(change), oldPrev, newPrev); + oldPrev = change.to; + newPrev = to; + if (hint == "around") { + var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0; + out[i] = new Range(inv ? to : from, inv ? from : to); + } else { + out[i] = new Range(from, from); + } + } + return new Selection(out, doc.sel.primIndex) + } + + // Used to get the editor into a consistent state again when options change. + + function loadMode(cm) { + cm.doc.mode = getMode(cm.options, cm.doc.modeOption); + resetModeState(cm); + } + + function resetModeState(cm) { + cm.doc.iter(function (line) { + if (line.stateAfter) { line.stateAfter = null; } + if (line.styles) { line.styles = null; } + }); + cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first; + startWorker(cm, 100); + cm.state.modeGen++; + if (cm.curOp) { regChange(cm); } + } + + // DOCUMENT DATA STRUCTURE + + // By default, updates that start and end at the beginning of a line + // are treated specially, in order to make the association of line + // widgets and marker elements with the text behave more intuitive. + function isWholeLineUpdate(doc, change) { + return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" && + (!doc.cm || doc.cm.options.wholeLineUpdateBefore) + } + + // Perform a change on the document data structure. + function updateDoc(doc, change, markedSpans, estimateHeight) { + function spansFor(n) {return markedSpans ? markedSpans[n] : null} + function update(line, text, spans) { + updateLine(line, text, spans, estimateHeight); + signalLater(line, "change", line, change); + } + function linesFor(start, end) { + var result = []; + for (var i = start; i < end; ++i) + { result.push(new Line(text[i], spansFor(i), estimateHeight)); } + return result + } + + var from = change.from, to = change.to, text = change.text; + var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line); + var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line; + + // Adjust the line structure + if (change.full) { + doc.insert(0, linesFor(0, text.length)); + doc.remove(text.length, doc.size - text.length); + } else if (isWholeLineUpdate(doc, change)) { + // This is a whole-line replace. Treated specially to make + // sure line objects move the way they are supposed to. + var added = linesFor(0, text.length - 1); + update(lastLine, lastLine.text, lastSpans); + if (nlines) { doc.remove(from.line, nlines); } + if (added.length) { doc.insert(from.line, added); } + } else if (firstLine == lastLine) { + if (text.length == 1) { + update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans); + } else { + var added$1 = linesFor(1, text.length - 1); + added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight)); + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); + doc.insert(from.line + 1, added$1); + } + } else if (text.length == 1) { + update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0)); + doc.remove(from.line + 1, nlines); + } else { + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); + update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans); + var added$2 = linesFor(1, text.length - 1); + if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); } + doc.insert(from.line + 1, added$2); + } + + signalLater(doc, "change", doc, change); + } + + // Call f for all linked documents. + function linkedDocs(doc, f, sharedHistOnly) { + function propagate(doc, skip, sharedHist) { + if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) { + var rel = doc.linked[i]; + if (rel.doc == skip) { continue } + var shared = sharedHist && rel.sharedHist; + if (sharedHistOnly && !shared) { continue } + f(rel.doc, shared); + propagate(rel.doc, doc, shared); + } } + } + propagate(doc, null, true); + } + + // Attach a document to an editor. + function attachDoc(cm, doc) { + if (doc.cm) { throw new Error("This document is already in use.") } + cm.doc = doc; + doc.cm = cm; + estimateLineHeights(cm); + loadMode(cm); + setDirectionClass(cm); + cm.options.direction = doc.direction; + if (!cm.options.lineWrapping) { findMaxLine(cm); } + cm.options.mode = doc.modeOption; + regChange(cm); + } + + function setDirectionClass(cm) { + (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl"); + } + + function directionChanged(cm) { + runInOp(cm, function () { + setDirectionClass(cm); + regChange(cm); + }); + } + + function History(prev) { + // Arrays of change events and selections. Doing something adds an + // event to done and clears undo. Undoing moves events from done + // to undone, redoing moves them in the other direction. + this.done = []; this.undone = []; + this.undoDepth = prev ? prev.undoDepth : Infinity; + // Used to track when changes can be merged into a single undo + // event + this.lastModTime = this.lastSelTime = 0; + this.lastOp = this.lastSelOp = null; + this.lastOrigin = this.lastSelOrigin = null; + // Used by the isClean() method + this.generation = this.maxGeneration = prev ? prev.maxGeneration : 1; + } + + // Create a history change event from an updateDoc-style change + // object. + function historyChangeFromChange(doc, change) { + var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}; + attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); + linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true); + return histChange + } + + // Pop all selection events off the end of a history array. Stop at + // a change event. + function clearSelectionEvents(array) { + while (array.length) { + var last = lst(array); + if (last.ranges) { array.pop(); } + else { break } + } + } + + // Find the top change event in the history. Pop off selection + // events that are in the way. + function lastChangeEvent(hist, force) { + if (force) { + clearSelectionEvents(hist.done); + return lst(hist.done) + } else if (hist.done.length && !lst(hist.done).ranges) { + return lst(hist.done) + } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) { + hist.done.pop(); + return lst(hist.done) + } + } + + // Register a change in the history. Merges changes that are within + // a single operation, or are close together with an origin that + // allows merging (starting with "+") into a single event. + function addChangeToHistory(doc, change, selAfter, opId) { + var hist = doc.history; + hist.undone.length = 0; + var time = +new Date, cur; + var last; + + if ((hist.lastOp == opId || + hist.lastOrigin == change.origin && change.origin && + ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) || + change.origin.charAt(0) == "*")) && + (cur = lastChangeEvent(hist, hist.lastOp == opId))) { + // Merge this change into the last event + last = lst(cur.changes); + if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) { + // Optimized case for simple insertion -- don't want to add + // new changesets for every character typed + last.to = changeEnd(change); + } else { + // Add new sub-event + cur.changes.push(historyChangeFromChange(doc, change)); + } + } else { + // Can not be merged, start a new event. + var before = lst(hist.done); + if (!before || !before.ranges) + { pushSelectionToHistory(doc.sel, hist.done); } + cur = {changes: [historyChangeFromChange(doc, change)], + generation: hist.generation}; + hist.done.push(cur); + while (hist.done.length > hist.undoDepth) { + hist.done.shift(); + if (!hist.done[0].ranges) { hist.done.shift(); } + } + } + hist.done.push(selAfter); + hist.generation = ++hist.maxGeneration; + hist.lastModTime = hist.lastSelTime = time; + hist.lastOp = hist.lastSelOp = opId; + hist.lastOrigin = hist.lastSelOrigin = change.origin; + + if (!last) { signal(doc, "historyAdded"); } + } + + function selectionEventCanBeMerged(doc, origin, prev, sel) { + var ch = origin.charAt(0); + return ch == "*" || + ch == "+" && + prev.ranges.length == sel.ranges.length && + prev.somethingSelected() == sel.somethingSelected() && + new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500) + } + + // Called whenever the selection changes, sets the new selection as + // the pending selection in the history, and pushes the old pending + // selection into the 'done' array when it was significantly + // different (in number of selected ranges, emptiness, or time). + function addSelectionToHistory(doc, sel, opId, options) { + var hist = doc.history, origin = options && options.origin; + + // A new event is started when the previous origin does not match + // the current, or the origins don't allow matching. Origins + // starting with * are always merged, those starting with + are + // merged when similar and close together in time. + if (opId == hist.lastSelOp || + (origin && hist.lastSelOrigin == origin && + (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin || + selectionEventCanBeMerged(doc, origin, lst(hist.done), sel)))) + { hist.done[hist.done.length - 1] = sel; } + else + { pushSelectionToHistory(sel, hist.done); } + + hist.lastSelTime = +new Date; + hist.lastSelOrigin = origin; + hist.lastSelOp = opId; + if (options && options.clearRedo !== false) + { clearSelectionEvents(hist.undone); } + } + + function pushSelectionToHistory(sel, dest) { + var top = lst(dest); + if (!(top && top.ranges && top.equals(sel))) + { dest.push(sel); } + } + + // Used to store marked span information in the history. + function attachLocalSpans(doc, change, from, to) { + var existing = change["spans_" + doc.id], n = 0; + doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) { + if (line.markedSpans) + { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; } + ++n; + }); + } + + // When un/re-doing restores text containing marked spans, those + // that have been explicitly cleared should not be restored. + function removeClearedSpans(spans) { + if (!spans) { return null } + var out; + for (var i = 0; i < spans.length; ++i) { + if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } } + else if (out) { out.push(spans[i]); } + } + return !out ? spans : out.length ? out : null + } + + // Retrieve and filter the old marked spans stored in a change event. + function getOldSpans(doc, change) { + var found = change["spans_" + doc.id]; + if (!found) { return null } + var nw = []; + for (var i = 0; i < change.text.length; ++i) + { nw.push(removeClearedSpans(found[i])); } + return nw + } + + // Used for un/re-doing changes from the history. Combines the + // result of computing the existing spans with the set of spans that + // existed in the history (so that deleting around a span and then + // undoing brings back the span). + function mergeOldSpans(doc, change) { + var old = getOldSpans(doc, change); + var stretched = stretchSpansOverChange(doc, change); + if (!old) { return stretched } + if (!stretched) { return old } + + for (var i = 0; i < old.length; ++i) { + var oldCur = old[i], stretchCur = stretched[i]; + if (oldCur && stretchCur) { + spans: for (var j = 0; j < stretchCur.length; ++j) { + var span = stretchCur[j]; + for (var k = 0; k < oldCur.length; ++k) + { if (oldCur[k].marker == span.marker) { continue spans } } + oldCur.push(span); + } + } else if (stretchCur) { + old[i] = stretchCur; + } + } + return old + } + + // Used both to provide a JSON-safe object in .getHistory, and, when + // detaching a document, to split the history in two + function copyHistoryArray(events, newGroup, instantiateSel) { + var copy = []; + for (var i = 0; i < events.length; ++i) { + var event = events[i]; + if (event.ranges) { + copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event); + continue + } + var changes = event.changes, newChanges = []; + copy.push({changes: newChanges}); + for (var j = 0; j < changes.length; ++j) { + var change = changes[j], m = (void 0); + newChanges.push({from: change.from, to: change.to, text: change.text}); + if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) { + if (indexOf(newGroup, Number(m[1])) > -1) { + lst(newChanges)[prop] = change[prop]; + delete change[prop]; + } + } } } + } + } + return copy + } + + // The 'scroll' parameter given to many of these indicated whether + // the new cursor position should be scrolled into view after + // modifying the selection. + + // If shift is held or the extend flag is set, extends a range to + // include a given position (and optionally a second position). + // Otherwise, simply returns the range between the given positions. + // Used for cursor motion and such. + function extendRange(range, head, other, extend) { + if (extend) { + var anchor = range.anchor; + if (other) { + var posBefore = cmp(head, anchor) < 0; + if (posBefore != (cmp(other, anchor) < 0)) { + anchor = head; + head = other; + } else if (posBefore != (cmp(head, other) < 0)) { + head = other; + } + } + return new Range(anchor, head) + } else { + return new Range(other || head, head) + } + } + + // Extend the primary selection range, discard the rest. + function extendSelection(doc, head, other, options, extend) { + if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); } + setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options); + } + + // Extend all selections (pos is an array of selections with length + // equal the number of selections) + function extendSelections(doc, heads, options) { + var out = []; + var extend = doc.cm && (doc.cm.display.shift || doc.extend); + for (var i = 0; i < doc.sel.ranges.length; i++) + { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); } + var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex); + setSelection(doc, newSel, options); + } + + // Updates a single range in the selection. + function replaceOneSelection(doc, i, range, options) { + var ranges = doc.sel.ranges.slice(0); + ranges[i] = range; + setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options); + } + + // Reset the selection to a single range. + function setSimpleSelection(doc, anchor, head, options) { + setSelection(doc, simpleSelection(anchor, head), options); + } + + // Give beforeSelectionChange handlers a change to influence a + // selection update. + function filterSelectionChange(doc, sel, options) { + var obj = { + ranges: sel.ranges, + update: function(ranges) { + this.ranges = []; + for (var i = 0; i < ranges.length; i++) + { this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), + clipPos(doc, ranges[i].head)); } + }, + origin: options && options.origin + }; + signal(doc, "beforeSelectionChange", doc, obj); + if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); } + if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) } + else { return sel } + } + + function setSelectionReplaceHistory(doc, sel, options) { + var done = doc.history.done, last = lst(done); + if (last && last.ranges) { + done[done.length - 1] = sel; + setSelectionNoUndo(doc, sel, options); + } else { + setSelection(doc, sel, options); + } + } + + // Set a new selection. + function setSelection(doc, sel, options) { + setSelectionNoUndo(doc, sel, options); + addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options); + } + + function setSelectionNoUndo(doc, sel, options) { + if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) + { sel = filterSelectionChange(doc, sel, options); } + + var bias = options && options.bias || + (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1); + setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true)); + + if (!(options && options.scroll === false) && doc.cm && doc.cm.getOption("readOnly") != "nocursor") + { ensureCursorVisible(doc.cm); } + } + + function setSelectionInner(doc, sel) { + if (sel.equals(doc.sel)) { return } + + doc.sel = sel; + + if (doc.cm) { + doc.cm.curOp.updateInput = 1; + doc.cm.curOp.selectionChanged = true; + signalCursorActivity(doc.cm); + } + signalLater(doc, "cursorActivity", doc); + } + + // Verify that the selection does not partially select any atomic + // marked ranges. + function reCheckSelection(doc) { + setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false)); + } + + // Return a selection that does not partially select any atomic + // ranges. + function skipAtomicInSelection(doc, sel, bias, mayClear) { + var out; + for (var i = 0; i < sel.ranges.length; i++) { + var range = sel.ranges[i]; + var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]; + var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear); + var newHead = range.head == range.anchor ? newAnchor : skipAtomic(doc, range.head, old && old.head, bias, mayClear); + if (out || newAnchor != range.anchor || newHead != range.head) { + if (!out) { out = sel.ranges.slice(0, i); } + out[i] = new Range(newAnchor, newHead); + } + } + return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel + } + + function skipAtomicInner(doc, pos, oldPos, dir, mayClear) { + var line = getLine(doc, pos.line); + if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { + var sp = line.markedSpans[i], m = sp.marker; + + // Determine if we should prevent the cursor being placed to the left/right of an atomic marker + // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it + // is with selectLeft/Right + var preventCursorLeft = ("selectLeft" in m) ? !m.selectLeft : m.inclusiveLeft; + var preventCursorRight = ("selectRight" in m) ? !m.selectRight : m.inclusiveRight; + + if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) && + (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) { + if (mayClear) { + signal(m, "beforeCursorEnter"); + if (m.explicitlyCleared) { + if (!line.markedSpans) { break } + else {--i; continue} + } + } + if (!m.atomic) { continue } + + if (oldPos) { + var near = m.find(dir < 0 ? 1 : -1), diff = (void 0); + if (dir < 0 ? preventCursorRight : preventCursorLeft) + { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); } + if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0)) + { return skipAtomicInner(doc, near, pos, dir, mayClear) } + } + + var far = m.find(dir < 0 ? -1 : 1); + if (dir < 0 ? preventCursorLeft : preventCursorRight) + { far = movePos(doc, far, dir, far.line == pos.line ? line : null); } + return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null + } + } } + return pos + } + + // Ensure a given position is not inside an atomic range. + function skipAtomic(doc, pos, oldPos, bias, mayClear) { + var dir = bias || 1; + var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) || + (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) || + skipAtomicInner(doc, pos, oldPos, -dir, mayClear) || + (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true)); + if (!found) { + doc.cantEdit = true; + return Pos(doc.first, 0) + } + return found + } + + function movePos(doc, pos, dir, line) { + if (dir < 0 && pos.ch == 0) { + if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) } + else { return null } + } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) { + if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) } + else { return null } + } else { + return new Pos(pos.line, pos.ch + dir) + } + } + + function selectAll(cm) { + cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll); + } + + // UPDATING + + // Allow "beforeChange" event handlers to influence a change + function filterChange(doc, change, update) { + var obj = { + canceled: false, + from: change.from, + to: change.to, + text: change.text, + origin: change.origin, + cancel: function () { return obj.canceled = true; } + }; + if (update) { obj.update = function (from, to, text, origin) { + if (from) { obj.from = clipPos(doc, from); } + if (to) { obj.to = clipPos(doc, to); } + if (text) { obj.text = text; } + if (origin !== undefined) { obj.origin = origin; } + }; } + signal(doc, "beforeChange", doc, obj); + if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); } + + if (obj.canceled) { + if (doc.cm) { doc.cm.curOp.updateInput = 2; } + return null + } + return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin} + } + + // Apply a change to a document, and add it to the document's + // history, and propagating it to all linked documents. + function makeChange(doc, change, ignoreReadOnly) { + if (doc.cm) { + if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) } + if (doc.cm.state.suppressEdits) { return } + } + + if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) { + change = filterChange(doc, change, true); + if (!change) { return } + } + + // Possibly split or suppress the update based on the presence + // of read-only spans in its range. + var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to); + if (split) { + for (var i = split.length - 1; i >= 0; --i) + { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); } + } else { + makeChangeInner(doc, change); + } + } + + function makeChangeInner(doc, change) { + if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return } + var selAfter = computeSelAfterChange(doc, change); + addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN); + + makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)); + var rebased = []; + + linkedDocs(doc, function (doc, sharedHist) { + if (!sharedHist && indexOf(rebased, doc.history) == -1) { + rebaseHist(doc.history, change); + rebased.push(doc.history); + } + makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change)); + }); + } + + // Revert a change stored in a document's history. + function makeChangeFromHistory(doc, type, allowSelectionOnly) { + var suppress = doc.cm && doc.cm.state.suppressEdits; + if (suppress && !allowSelectionOnly) { return } + + var hist = doc.history, event, selAfter = doc.sel; + var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done; + + // Verify that there is a useable event (so that ctrl-z won't + // needlessly clear selection events) + var i = 0; + for (; i < source.length; i++) { + event = source[i]; + if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges) + { break } + } + if (i == source.length) { return } + hist.lastOrigin = hist.lastSelOrigin = null; + + for (;;) { + event = source.pop(); + if (event.ranges) { + pushSelectionToHistory(event, dest); + if (allowSelectionOnly && !event.equals(doc.sel)) { + setSelection(doc, event, {clearRedo: false}); + return + } + selAfter = event; + } else if (suppress) { + source.push(event); + return + } else { break } + } + + // Build up a reverse change object to add to the opposite history + // stack (redo when undoing, and vice versa). + var antiChanges = []; + pushSelectionToHistory(selAfter, dest); + dest.push({changes: antiChanges, generation: hist.generation}); + hist.generation = event.generation || ++hist.maxGeneration; + + var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange"); + + var loop = function ( i ) { + var change = event.changes[i]; + change.origin = type; + if (filter && !filterChange(doc, change, false)) { + source.length = 0; + return {} + } + + antiChanges.push(historyChangeFromChange(doc, change)); + + var after = i ? computeSelAfterChange(doc, change) : lst(source); + makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); + if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); } + var rebased = []; + + // Propagate to the linked documents + linkedDocs(doc, function (doc, sharedHist) { + if (!sharedHist && indexOf(rebased, doc.history) == -1) { + rebaseHist(doc.history, change); + rebased.push(doc.history); + } + makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change)); + }); + }; + + for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) { + var returned = loop( i$1 ); + + if ( returned ) return returned.v; + } + } + + // Sub-views need their line numbers shifted when text is added + // above or below them in the parent document. + function shiftDoc(doc, distance) { + if (distance == 0) { return } + doc.first += distance; + doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range( + Pos(range.anchor.line + distance, range.anchor.ch), + Pos(range.head.line + distance, range.head.ch) + ); }), doc.sel.primIndex); + if (doc.cm) { + regChange(doc.cm, doc.first, doc.first - distance, distance); + for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++) + { regLineChange(doc.cm, l, "gutter"); } + } + } + + // More lower-level change function, handling only a single document + // (not linked ones). + function makeChangeSingleDoc(doc, change, selAfter, spans) { + if (doc.cm && !doc.cm.curOp) + { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) } + + if (change.to.line < doc.first) { + shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line)); + return + } + if (change.from.line > doc.lastLine()) { return } + + // Clip the change to the size of this doc + if (change.from.line < doc.first) { + var shift = change.text.length - 1 - (doc.first - change.from.line); + shiftDoc(doc, shift); + change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch), + text: [lst(change.text)], origin: change.origin}; + } + var last = doc.lastLine(); + if (change.to.line > last) { + change = {from: change.from, to: Pos(last, getLine(doc, last).text.length), + text: [change.text[0]], origin: change.origin}; + } + + change.removed = getBetween(doc, change.from, change.to); + + if (!selAfter) { selAfter = computeSelAfterChange(doc, change); } + if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); } + else { updateDoc(doc, change, spans); } + setSelectionNoUndo(doc, selAfter, sel_dontScroll); + + if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0))) + { doc.cantEdit = false; } + } + + // Handle the interaction of a change to a document with the editor + // that this document is part of. + function makeChangeSingleDocInEditor(cm, change, spans) { + var doc = cm.doc, display = cm.display, from = change.from, to = change.to; + + var recomputeMaxLength = false, checkWidthStart = from.line; + if (!cm.options.lineWrapping) { + checkWidthStart = lineNo(visualLine(getLine(doc, from.line))); + doc.iter(checkWidthStart, to.line + 1, function (line) { + if (line == display.maxLine) { + recomputeMaxLength = true; + return true + } + }); + } + + if (doc.sel.contains(change.from, change.to) > -1) + { signalCursorActivity(cm); } + + updateDoc(doc, change, spans, estimateHeight(cm)); + + if (!cm.options.lineWrapping) { + doc.iter(checkWidthStart, from.line + change.text.length, function (line) { + var len = lineLength(line); + if (len > display.maxLineLength) { + display.maxLine = line; + display.maxLineLength = len; + display.maxLineChanged = true; + recomputeMaxLength = false; + } + }); + if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; } + } + + retreatFrontier(doc, from.line); + startWorker(cm, 400); + + var lendiff = change.text.length - (to.line - from.line) - 1; + // Remember that these lines changed, for updating the display + if (change.full) + { regChange(cm); } + else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change)) + { regLineChange(cm, from.line, "text"); } + else + { regChange(cm, from.line, to.line + 1, lendiff); } + + var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change"); + if (changeHandler || changesHandler) { + var obj = { + from: from, to: to, + text: change.text, + removed: change.removed, + origin: change.origin + }; + if (changeHandler) { signalLater(cm, "change", cm, obj); } + if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); } + } + cm.display.selForContextMenu = null; + } + + function replaceRange(doc, code, from, to, origin) { + var assign; + + if (!to) { to = from; } + if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); } + if (typeof code == "string") { code = doc.splitLines(code); } + makeChange(doc, {from: from, to: to, text: code, origin: origin}); + } + + // Rebasing/resetting history to deal with externally-sourced changes + + function rebaseHistSelSingle(pos, from, to, diff) { + if (to < pos.line) { + pos.line += diff; + } else if (from < pos.line) { + pos.line = from; + pos.ch = 0; + } + } + + // Tries to rebase an array of history events given a change in the + // document. If the change touches the same lines as the event, the + // event, and everything 'behind' it, is discarded. If the change is + // before the event, the event's positions are updated. Uses a + // copy-on-write scheme for the positions, to avoid having to + // reallocate them all on every rebase, but also avoid problems with + // shared position objects being unsafely updated. + function rebaseHistArray(array, from, to, diff) { + for (var i = 0; i < array.length; ++i) { + var sub = array[i], ok = true; + if (sub.ranges) { + if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; } + for (var j = 0; j < sub.ranges.length; j++) { + rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff); + rebaseHistSelSingle(sub.ranges[j].head, from, to, diff); + } + continue + } + for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) { + var cur = sub.changes[j$1]; + if (to < cur.from.line) { + cur.from = Pos(cur.from.line + diff, cur.from.ch); + cur.to = Pos(cur.to.line + diff, cur.to.ch); + } else if (from <= cur.to.line) { + ok = false; + break + } + } + if (!ok) { + array.splice(0, i + 1); + i = 0; + } + } + } + + function rebaseHist(hist, change) { + var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1; + rebaseHistArray(hist.done, from, to, diff); + rebaseHistArray(hist.undone, from, to, diff); + } + + // Utility for applying a change to a line by handle or number, + // returning the number and optionally registering the line as + // changed. + function changeLine(doc, handle, changeType, op) { + var no = handle, line = handle; + if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); } + else { no = lineNo(handle); } + if (no == null) { return null } + if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); } + return line + } + + // The document is represented as a BTree consisting of leaves, with + // chunk of lines in them, and branches, with up to ten leaves or + // other branch nodes below them. The top node is always a branch + // node, and is the document object itself (meaning it has + // additional methods and properties). + // + // All nodes have parent links. The tree is used both to go from + // line numbers to line objects, and to go from objects to numbers. + // It also indexes by height, and is used to convert between height + // and line object, and to find the total height of the document. + // + // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html + + function LeafChunk(lines) { + this.lines = lines; + this.parent = null; + var height = 0; + for (var i = 0; i < lines.length; ++i) { + lines[i].parent = this; + height += lines[i].height; + } + this.height = height; + } + + LeafChunk.prototype = { + chunkSize: function() { return this.lines.length }, + + // Remove the n lines at offset 'at'. + removeInner: function(at, n) { + for (var i = at, e = at + n; i < e; ++i) { + var line = this.lines[i]; + this.height -= line.height; + cleanUpLine(line); + signalLater(line, "delete"); + } + this.lines.splice(at, n); + }, + + // Helper used to collapse a small branch into a single leaf. + collapse: function(lines) { + lines.push.apply(lines, this.lines); + }, + + // Insert the given array of lines at offset 'at', count them as + // having the given height. + insertInner: function(at, lines, height) { + this.height += height; + this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at)); + for (var i = 0; i < lines.length; ++i) { lines[i].parent = this; } + }, + + // Used to iterate over a part of the tree. + iterN: function(at, n, op) { + for (var e = at + n; at < e; ++at) + { if (op(this.lines[at])) { return true } } + } + }; + + function BranchChunk(children) { + this.children = children; + var size = 0, height = 0; + for (var i = 0; i < children.length; ++i) { + var ch = children[i]; + size += ch.chunkSize(); height += ch.height; + ch.parent = this; + } + this.size = size; + this.height = height; + this.parent = null; + } + + BranchChunk.prototype = { + chunkSize: function() { return this.size }, + + removeInner: function(at, n) { + this.size -= n; + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at < sz) { + var rm = Math.min(n, sz - at), oldHeight = child.height; + child.removeInner(at, rm); + this.height -= oldHeight - child.height; + if (sz == rm) { this.children.splice(i--, 1); child.parent = null; } + if ((n -= rm) == 0) { break } + at = 0; + } else { at -= sz; } + } + // If the result is smaller than 25 lines, ensure that it is a + // single leaf node. + if (this.size - n < 25 && + (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) { + var lines = []; + this.collapse(lines); + this.children = [new LeafChunk(lines)]; + this.children[0].parent = this; + } + }, + + collapse: function(lines) { + for (var i = 0; i < this.children.length; ++i) { this.children[i].collapse(lines); } + }, + + insertInner: function(at, lines, height) { + this.size += lines.length; + this.height += height; + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at <= sz) { + child.insertInner(at, lines, height); + if (child.lines && child.lines.length > 50) { + // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced. + // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest. + var remaining = child.lines.length % 25 + 25; + for (var pos = remaining; pos < child.lines.length;) { + var leaf = new LeafChunk(child.lines.slice(pos, pos += 25)); + child.height -= leaf.height; + this.children.splice(++i, 0, leaf); + leaf.parent = this; + } + child.lines = child.lines.slice(0, remaining); + this.maybeSpill(); + } + break + } + at -= sz; + } + }, + + // When a node has grown, check whether it should be split. + maybeSpill: function() { + if (this.children.length <= 10) { return } + var me = this; + do { + var spilled = me.children.splice(me.children.length - 5, 5); + var sibling = new BranchChunk(spilled); + if (!me.parent) { // Become the parent node + var copy = new BranchChunk(me.children); + copy.parent = me; + me.children = [copy, sibling]; + me = copy; + } else { + me.size -= sibling.size; + me.height -= sibling.height; + var myIndex = indexOf(me.parent.children, me); + me.parent.children.splice(myIndex + 1, 0, sibling); + } + sibling.parent = me.parent; + } while (me.children.length > 10) + me.parent.maybeSpill(); + }, + + iterN: function(at, n, op) { + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at < sz) { + var used = Math.min(n, sz - at); + if (child.iterN(at, used, op)) { return true } + if ((n -= used) == 0) { break } + at = 0; + } else { at -= sz; } + } + } + }; + + // Line widgets are block elements displayed above or below a line. + + var LineWidget = function(doc, node, options) { + if (options) { for (var opt in options) { if (options.hasOwnProperty(opt)) + { this[opt] = options[opt]; } } } + this.doc = doc; + this.node = node; + }; + + LineWidget.prototype.clear = function () { + var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line); + if (no == null || !ws) { return } + for (var i = 0; i < ws.length; ++i) { if (ws[i] == this) { ws.splice(i--, 1); } } + if (!ws.length) { line.widgets = null; } + var height = widgetHeight(this); + updateLineHeight(line, Math.max(0, line.height - height)); + if (cm) { + runInOp(cm, function () { + adjustScrollWhenAboveVisible(cm, line, -height); + regLineChange(cm, no, "widget"); + }); + signalLater(cm, "lineWidgetCleared", cm, this, no); + } + }; + + LineWidget.prototype.changed = function () { + var this$1 = this; + + var oldH = this.height, cm = this.doc.cm, line = this.line; + this.height = null; + var diff = widgetHeight(this) - oldH; + if (!diff) { return } + if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); } + if (cm) { + runInOp(cm, function () { + cm.curOp.forceUpdate = true; + adjustScrollWhenAboveVisible(cm, line, diff); + signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line)); + }); + } + }; + eventMixin(LineWidget); + + function adjustScrollWhenAboveVisible(cm, line, diff) { + if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop)) + { addToScrollTop(cm, diff); } + } + + function addLineWidget(doc, handle, node, options) { + var widget = new LineWidget(doc, node, options); + var cm = doc.cm; + if (cm && widget.noHScroll) { cm.display.alignWidgets = true; } + changeLine(doc, handle, "widget", function (line) { + var widgets = line.widgets || (line.widgets = []); + if (widget.insertAt == null) { widgets.push(widget); } + else { widgets.splice(Math.min(widgets.length, Math.max(0, widget.insertAt)), 0, widget); } + widget.line = line; + if (cm && !lineIsHidden(doc, line)) { + var aboveVisible = heightAtLine(line) < doc.scrollTop; + updateLineHeight(line, line.height + widgetHeight(widget)); + if (aboveVisible) { addToScrollTop(cm, widget.height); } + cm.curOp.forceUpdate = true; + } + return true + }); + if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); } + return widget + } + + // TEXTMARKERS + + // Created with markText and setBookmark methods. A TextMarker is a + // handle that can be used to clear or find a marked position in the + // document. Line objects hold arrays (markedSpans) containing + // {from, to, marker} object pointing to such marker objects, and + // indicating that such a marker is present on that line. Multiple + // lines may point to the same marker when it spans across lines. + // The spans will have null for their from/to properties when the + // marker continues beyond the start/end of the line. Markers have + // links back to the lines they currently touch. + + // Collapsed markers have unique ids, in order to be able to order + // them, which is needed for uniquely determining an outer marker + // when they overlap (they may nest, but not partially overlap). + var nextMarkerId = 0; + + var TextMarker = function(doc, type) { + this.lines = []; + this.type = type; + this.doc = doc; + this.id = ++nextMarkerId; + }; + + // Clear the marker. + TextMarker.prototype.clear = function () { + if (this.explicitlyCleared) { return } + var cm = this.doc.cm, withOp = cm && !cm.curOp; + if (withOp) { startOperation(cm); } + if (hasHandler(this, "clear")) { + var found = this.find(); + if (found) { signalLater(this, "clear", found.from, found.to); } + } + var min = null, max = null; + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); + if (cm && !this.collapsed) { regLineChange(cm, lineNo(line), "text"); } + else if (cm) { + if (span.to != null) { max = lineNo(line); } + if (span.from != null) { min = lineNo(line); } + } + line.markedSpans = removeMarkedSpan(line.markedSpans, span); + if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm) + { updateLineHeight(line, textHeight(cm.display)); } + } + if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) { + var visual = visualLine(this.lines[i$1]), len = lineLength(visual); + if (len > cm.display.maxLineLength) { + cm.display.maxLine = visual; + cm.display.maxLineLength = len; + cm.display.maxLineChanged = true; + } + } } + + if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); } + this.lines.length = 0; + this.explicitlyCleared = true; + if (this.atomic && this.doc.cantEdit) { + this.doc.cantEdit = false; + if (cm) { reCheckSelection(cm.doc); } + } + if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); } + if (withOp) { endOperation(cm); } + if (this.parent) { this.parent.clear(); } + }; + + // Find the position of the marker in the document. Returns a {from, + // to} object by default. Side can be passed to get a specific side + // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the + // Pos objects returned contain a line object, rather than a line + // number (used to prevent looking up the same line twice). + TextMarker.prototype.find = function (side, lineObj) { + if (side == null && this.type == "bookmark") { side = 1; } + var from, to; + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); + if (span.from != null) { + from = Pos(lineObj ? line : lineNo(line), span.from); + if (side == -1) { return from } + } + if (span.to != null) { + to = Pos(lineObj ? line : lineNo(line), span.to); + if (side == 1) { return to } + } + } + return from && {from: from, to: to} + }; + + // Signals that the marker's widget changed, and surrounding layout + // should be recomputed. + TextMarker.prototype.changed = function () { + var this$1 = this; + + var pos = this.find(-1, true), widget = this, cm = this.doc.cm; + if (!pos || !cm) { return } + runInOp(cm, function () { + var line = pos.line, lineN = lineNo(pos.line); + var view = findViewForLine(cm, lineN); + if (view) { + clearLineMeasurementCacheFor(view); + cm.curOp.selectionChanged = cm.curOp.forceUpdate = true; + } + cm.curOp.updateMaxLine = true; + if (!lineIsHidden(widget.doc, line) && widget.height != null) { + var oldHeight = widget.height; + widget.height = null; + var dHeight = widgetHeight(widget) - oldHeight; + if (dHeight) + { updateLineHeight(line, line.height + dHeight); } + } + signalLater(cm, "markerChanged", cm, this$1); + }); + }; + + TextMarker.prototype.attachLine = function (line) { + if (!this.lines.length && this.doc.cm) { + var op = this.doc.cm.curOp; + if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) + { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); } + } + this.lines.push(line); + }; + + TextMarker.prototype.detachLine = function (line) { + this.lines.splice(indexOf(this.lines, line), 1); + if (!this.lines.length && this.doc.cm) { + var op = this.doc.cm.curOp + ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this); + } + }; + eventMixin(TextMarker); + + // Create a marker, wire it up to the right lines, and + function markText(doc, from, to, options, type) { + // Shared markers (across linked documents) are handled separately + // (markTextShared will call out to this again, once per + // document). + if (options && options.shared) { return markTextShared(doc, from, to, options, type) } + // Ensure we are in an operation. + if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) } + + var marker = new TextMarker(doc, type), diff = cmp(from, to); + if (options) { copyObj(options, marker, false); } + // Don't connect empty markers unless clearWhenEmpty is false + if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false) + { return marker } + if (marker.replacedWith) { + // Showing up as a widget implies collapsed (widget replaces text) + marker.collapsed = true; + marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget"); + if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); } + if (options.insertLeft) { marker.widgetNode.insertLeft = true; } + } + if (marker.collapsed) { + if (conflictingCollapsedRange(doc, from.line, from, to, marker) || + from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker)) + { throw new Error("Inserting collapsed marker partially overlapping an existing one") } + seeCollapsedSpans(); + } + + if (marker.addToHistory) + { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); } + + var curLine = from.line, cm = doc.cm, updateMaxLine; + doc.iter(curLine, to.line + 1, function (line) { + if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine) + { updateMaxLine = true; } + if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); } + addMarkedSpan(line, new MarkedSpan(marker, + curLine == from.line ? from.ch : null, + curLine == to.line ? to.ch : null), doc.cm && doc.cm.curOp); + ++curLine; + }); + // lineIsHidden depends on the presence of the spans, so needs a second pass + if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) { + if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); } + }); } + + if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); } + + if (marker.readOnly) { + seeReadOnlySpans(); + if (doc.history.done.length || doc.history.undone.length) + { doc.clearHistory(); } + } + if (marker.collapsed) { + marker.id = ++nextMarkerId; + marker.atomic = true; + } + if (cm) { + // Sync editor state + if (updateMaxLine) { cm.curOp.updateMaxLine = true; } + if (marker.collapsed) + { regChange(cm, from.line, to.line + 1); } + else if (marker.className || marker.startStyle || marker.endStyle || marker.css || + marker.attributes || marker.title) + { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } } + if (marker.atomic) { reCheckSelection(cm.doc); } + signalLater(cm, "markerAdded", cm, marker); + } + return marker + } + + // SHARED TEXTMARKERS + + // A shared marker spans multiple linked documents. It is + // implemented as a meta-marker-object controlling multiple normal + // markers. + var SharedTextMarker = function(markers, primary) { + this.markers = markers; + this.primary = primary; + for (var i = 0; i < markers.length; ++i) + { markers[i].parent = this; } + }; + + SharedTextMarker.prototype.clear = function () { + if (this.explicitlyCleared) { return } + this.explicitlyCleared = true; + for (var i = 0; i < this.markers.length; ++i) + { this.markers[i].clear(); } + signalLater(this, "clear"); + }; + + SharedTextMarker.prototype.find = function (side, lineObj) { + return this.primary.find(side, lineObj) + }; + eventMixin(SharedTextMarker); + + function markTextShared(doc, from, to, options, type) { + options = copyObj(options); + options.shared = false; + var markers = [markText(doc, from, to, options, type)], primary = markers[0]; + var widget = options.widgetNode; + linkedDocs(doc, function (doc) { + if (widget) { options.widgetNode = widget.cloneNode(true); } + markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type)); + for (var i = 0; i < doc.linked.length; ++i) + { if (doc.linked[i].isParent) { return } } + primary = lst(markers); + }); + return new SharedTextMarker(markers, primary) + } + + function findSharedMarkers(doc) { + return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; }) + } + + function copySharedMarkers(doc, markers) { + for (var i = 0; i < markers.length; i++) { + var marker = markers[i], pos = marker.find(); + var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to); + if (cmp(mFrom, mTo)) { + var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type); + marker.markers.push(subMark); + subMark.parent = marker; + } + } + } + + function detachSharedMarkers(markers) { + var loop = function ( i ) { + var marker = markers[i], linked = [marker.primary.doc]; + linkedDocs(marker.primary.doc, function (d) { return linked.push(d); }); + for (var j = 0; j < marker.markers.length; j++) { + var subMarker = marker.markers[j]; + if (indexOf(linked, subMarker.doc) == -1) { + subMarker.parent = null; + marker.markers.splice(j--, 1); + } + } + }; + + for (var i = 0; i < markers.length; i++) loop( i ); + } + + var nextDocId = 0; + var Doc = function(text, mode, firstLine, lineSep, direction) { + if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) } + if (firstLine == null) { firstLine = 0; } + + BranchChunk.call(this, [new LeafChunk([new Line("", null)])]); + this.first = firstLine; + this.scrollTop = this.scrollLeft = 0; + this.cantEdit = false; + this.cleanGeneration = 1; + this.modeFrontier = this.highlightFrontier = firstLine; + var start = Pos(firstLine, 0); + this.sel = simpleSelection(start); + this.history = new History(null); + this.id = ++nextDocId; + this.modeOption = mode; + this.lineSep = lineSep; + this.direction = (direction == "rtl") ? "rtl" : "ltr"; + this.extend = false; + + if (typeof text == "string") { text = this.splitLines(text); } + updateDoc(this, {from: start, to: start, text: text}); + setSelection(this, simpleSelection(start), sel_dontScroll); + }; + + Doc.prototype = createObj(BranchChunk.prototype, { + constructor: Doc, + // Iterate over the document. Supports two forms -- with only one + // argument, it calls that for each line in the document. With + // three, it iterates over the range given by the first two (with + // the second being non-inclusive). + iter: function(from, to, op) { + if (op) { this.iterN(from - this.first, to - from, op); } + else { this.iterN(this.first, this.first + this.size, from); } + }, + + // Non-public interface for adding and removing lines. + insert: function(at, lines) { + var height = 0; + for (var i = 0; i < lines.length; ++i) { height += lines[i].height; } + this.insertInner(at - this.first, lines, height); + }, + remove: function(at, n) { this.removeInner(at - this.first, n); }, + + // From here, the methods are part of the public interface. Most + // are also available from CodeMirror (editor) instances. + + getValue: function(lineSep) { + var lines = getLines(this, this.first, this.first + this.size); + if (lineSep === false) { return lines } + return lines.join(lineSep || this.lineSeparator()) + }, + setValue: docMethodOp(function(code) { + var top = Pos(this.first, 0), last = this.first + this.size - 1; + makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length), + text: this.splitLines(code), origin: "setValue", full: true}, true); + if (this.cm) { scrollToCoords(this.cm, 0, 0); } + setSelection(this, simpleSelection(top), sel_dontScroll); + }), + replaceRange: function(code, from, to, origin) { + from = clipPos(this, from); + to = to ? clipPos(this, to) : from; + replaceRange(this, code, from, to, origin); + }, + getRange: function(from, to, lineSep) { + var lines = getBetween(this, clipPos(this, from), clipPos(this, to)); + if (lineSep === false) { return lines } + if (lineSep === '') { return lines.join('') } + return lines.join(lineSep || this.lineSeparator()) + }, + + getLine: function(line) {var l = this.getLineHandle(line); return l && l.text}, + + getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }}, + getLineNumber: function(line) {return lineNo(line)}, + + getLineHandleVisualStart: function(line) { + if (typeof line == "number") { line = getLine(this, line); } + return visualLine(line) + }, + + lineCount: function() {return this.size}, + firstLine: function() {return this.first}, + lastLine: function() {return this.first + this.size - 1}, + + clipPos: function(pos) {return clipPos(this, pos)}, + + getCursor: function(start) { + var range = this.sel.primary(), pos; + if (start == null || start == "head") { pos = range.head; } + else if (start == "anchor") { pos = range.anchor; } + else if (start == "end" || start == "to" || start === false) { pos = range.to(); } + else { pos = range.from(); } + return pos + }, + listSelections: function() { return this.sel.ranges }, + somethingSelected: function() {return this.sel.somethingSelected()}, + + setCursor: docMethodOp(function(line, ch, options) { + setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options); + }), + setSelection: docMethodOp(function(anchor, head, options) { + setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options); + }), + extendSelection: docMethodOp(function(head, other, options) { + extendSelection(this, clipPos(this, head), other && clipPos(this, other), options); + }), + extendSelections: docMethodOp(function(heads, options) { + extendSelections(this, clipPosArray(this, heads), options); + }), + extendSelectionsBy: docMethodOp(function(f, options) { + var heads = map(this.sel.ranges, f); + extendSelections(this, clipPosArray(this, heads), options); + }), + setSelections: docMethodOp(function(ranges, primary, options) { + if (!ranges.length) { return } + var out = []; + for (var i = 0; i < ranges.length; i++) + { out[i] = new Range(clipPos(this, ranges[i].anchor), + clipPos(this, ranges[i].head || ranges[i].anchor)); } + if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); } + setSelection(this, normalizeSelection(this.cm, out, primary), options); + }), + addSelection: docMethodOp(function(anchor, head, options) { + var ranges = this.sel.ranges.slice(0); + ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor))); + setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options); + }), + + getSelection: function(lineSep) { + var ranges = this.sel.ranges, lines; + for (var i = 0; i < ranges.length; i++) { + var sel = getBetween(this, ranges[i].from(), ranges[i].to()); + lines = lines ? lines.concat(sel) : sel; + } + if (lineSep === false) { return lines } + else { return lines.join(lineSep || this.lineSeparator()) } + }, + getSelections: function(lineSep) { + var parts = [], ranges = this.sel.ranges; + for (var i = 0; i < ranges.length; i++) { + var sel = getBetween(this, ranges[i].from(), ranges[i].to()); + if (lineSep !== false) { sel = sel.join(lineSep || this.lineSeparator()); } + parts[i] = sel; + } + return parts + }, + replaceSelection: function(code, collapse, origin) { + var dup = []; + for (var i = 0; i < this.sel.ranges.length; i++) + { dup[i] = code; } + this.replaceSelections(dup, collapse, origin || "+input"); + }, + replaceSelections: docMethodOp(function(code, collapse, origin) { + var changes = [], sel = this.sel; + for (var i = 0; i < sel.ranges.length; i++) { + var range = sel.ranges[i]; + changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin}; + } + var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse); + for (var i$1 = changes.length - 1; i$1 >= 0; i$1--) + { makeChange(this, changes[i$1]); } + if (newSel) { setSelectionReplaceHistory(this, newSel); } + else if (this.cm) { ensureCursorVisible(this.cm); } + }), + undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}), + redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}), + undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}), + redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}), + + setExtending: function(val) {this.extend = val;}, + getExtending: function() {return this.extend}, + + historySize: function() { + var hist = this.history, done = 0, undone = 0; + for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } } + for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } } + return {undo: done, redo: undone} + }, + clearHistory: function() { + var this$1 = this; + + this.history = new History(this.history); + linkedDocs(this, function (doc) { return doc.history = this$1.history; }, true); + }, + + markClean: function() { + this.cleanGeneration = this.changeGeneration(true); + }, + changeGeneration: function(forceSplit) { + if (forceSplit) + { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; } + return this.history.generation + }, + isClean: function (gen) { + return this.history.generation == (gen || this.cleanGeneration) + }, + + getHistory: function() { + return {done: copyHistoryArray(this.history.done), + undone: copyHistoryArray(this.history.undone)} + }, + setHistory: function(histData) { + var hist = this.history = new History(this.history); + hist.done = copyHistoryArray(histData.done.slice(0), null, true); + hist.undone = copyHistoryArray(histData.undone.slice(0), null, true); + }, + + setGutterMarker: docMethodOp(function(line, gutterID, value) { + return changeLine(this, line, "gutter", function (line) { + var markers = line.gutterMarkers || (line.gutterMarkers = {}); + markers[gutterID] = value; + if (!value && isEmpty(markers)) { line.gutterMarkers = null; } + return true + }) + }), + + clearGutter: docMethodOp(function(gutterID) { + var this$1 = this; + + this.iter(function (line) { + if (line.gutterMarkers && line.gutterMarkers[gutterID]) { + changeLine(this$1, line, "gutter", function () { + line.gutterMarkers[gutterID] = null; + if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; } + return true + }); + } + }); + }), + + lineInfo: function(line) { + var n; + if (typeof line == "number") { + if (!isLine(this, line)) { return null } + n = line; + line = getLine(this, line); + if (!line) { return null } + } else { + n = lineNo(line); + if (n == null) { return null } + } + return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, + textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass, + widgets: line.widgets} + }, + + addLineClass: docMethodOp(function(handle, where, cls) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { + var prop = where == "text" ? "textClass" + : where == "background" ? "bgClass" + : where == "gutter" ? "gutterClass" : "wrapClass"; + if (!line[prop]) { line[prop] = cls; } + else if (classTest(cls).test(line[prop])) { return false } + else { line[prop] += " " + cls; } + return true + }) + }), + removeLineClass: docMethodOp(function(handle, where, cls) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { + var prop = where == "text" ? "textClass" + : where == "background" ? "bgClass" + : where == "gutter" ? "gutterClass" : "wrapClass"; + var cur = line[prop]; + if (!cur) { return false } + else if (cls == null) { line[prop] = null; } + else { + var found = cur.match(classTest(cls)); + if (!found) { return false } + var end = found.index + found[0].length; + line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null; + } + return true + }) + }), + + addLineWidget: docMethodOp(function(handle, node, options) { + return addLineWidget(this, handle, node, options) + }), + removeLineWidget: function(widget) { widget.clear(); }, + + markText: function(from, to, options) { + return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range") + }, + setBookmark: function(pos, options) { + var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options), + insertLeft: options && options.insertLeft, + clearWhenEmpty: false, shared: options && options.shared, + handleMouseEvents: options && options.handleMouseEvents}; + pos = clipPos(this, pos); + return markText(this, pos, pos, realOpts, "bookmark") + }, + findMarksAt: function(pos) { + pos = clipPos(this, pos); + var markers = [], spans = getLine(this, pos.line).markedSpans; + if (spans) { for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if ((span.from == null || span.from <= pos.ch) && + (span.to == null || span.to >= pos.ch)) + { markers.push(span.marker.parent || span.marker); } + } } + return markers + }, + findMarks: function(from, to, filter) { + from = clipPos(this, from); to = clipPos(this, to); + var found = [], lineNo = from.line; + this.iter(from.line, to.line + 1, function (line) { + var spans = line.markedSpans; + if (spans) { for (var i = 0; i < spans.length; i++) { + var span = spans[i]; + if (!(span.to != null && lineNo == from.line && from.ch >= span.to || + span.from == null && lineNo != from.line || + span.from != null && lineNo == to.line && span.from >= to.ch) && + (!filter || filter(span.marker))) + { found.push(span.marker.parent || span.marker); } + } } + ++lineNo; + }); + return found + }, + getAllMarks: function() { + var markers = []; + this.iter(function (line) { + var sps = line.markedSpans; + if (sps) { for (var i = 0; i < sps.length; ++i) + { if (sps[i].from != null) { markers.push(sps[i].marker); } } } + }); + return markers + }, + + posFromIndex: function(off) { + var ch, lineNo = this.first, sepSize = this.lineSeparator().length; + this.iter(function (line) { + var sz = line.text.length + sepSize; + if (sz > off) { ch = off; return true } + off -= sz; + ++lineNo; + }); + return clipPos(this, Pos(lineNo, ch)) + }, + indexFromPos: function (coords) { + coords = clipPos(this, coords); + var index = coords.ch; + if (coords.line < this.first || coords.ch < 0) { return 0 } + var sepSize = this.lineSeparator().length; + this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value + index += line.text.length + sepSize; + }); + return index + }, + + copy: function(copyHistory) { + var doc = new Doc(getLines(this, this.first, this.first + this.size), + this.modeOption, this.first, this.lineSep, this.direction); + doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft; + doc.sel = this.sel; + doc.extend = false; + if (copyHistory) { + doc.history.undoDepth = this.history.undoDepth; + doc.setHistory(this.getHistory()); + } + return doc + }, + + linkedDoc: function(options) { + if (!options) { options = {}; } + var from = this.first, to = this.first + this.size; + if (options.from != null && options.from > from) { from = options.from; } + if (options.to != null && options.to < to) { to = options.to; } + var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction); + if (options.sharedHist) { copy.history = this.history + ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist}); + copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]; + copySharedMarkers(copy, findSharedMarkers(this)); + return copy + }, + unlinkDoc: function(other) { + if (other instanceof CodeMirror) { other = other.doc; } + if (this.linked) { for (var i = 0; i < this.linked.length; ++i) { + var link = this.linked[i]; + if (link.doc != other) { continue } + this.linked.splice(i, 1); + other.unlinkDoc(this); + detachSharedMarkers(findSharedMarkers(this)); + break + } } + // If the histories were shared, split them again + if (other.history == this.history) { + var splitIds = [other.id]; + linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true); + other.history = new History(null); + other.history.done = copyHistoryArray(this.history.done, splitIds); + other.history.undone = copyHistoryArray(this.history.undone, splitIds); + } + }, + iterLinkedDocs: function(f) {linkedDocs(this, f);}, + + getMode: function() {return this.mode}, + getEditor: function() {return this.cm}, + + splitLines: function(str) { + if (this.lineSep) { return str.split(this.lineSep) } + return splitLinesAuto(str) + }, + lineSeparator: function() { return this.lineSep || "\n" }, + + setDirection: docMethodOp(function (dir) { + if (dir != "rtl") { dir = "ltr"; } + if (dir == this.direction) { return } + this.direction = dir; + this.iter(function (line) { return line.order = null; }); + if (this.cm) { directionChanged(this.cm); } + }) + }); + + // Public alias. + Doc.prototype.eachLine = Doc.prototype.iter; + + // Kludge to work around strange IE behavior where it'll sometimes + // re-fire a series of drag-related events right after the drop (#1551) + var lastDrop = 0; + + function onDrop(e) { + var cm = this; + clearDragCursor(cm); + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) + { return } + e_preventDefault(e); + if (ie) { lastDrop = +new Date; } + var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files; + if (!pos || cm.isReadOnly()) { return } + // Might be a file drop, in which case we simply extract the text + // and insert it. + if (files && files.length && window.FileReader && window.File) { + var n = files.length, text = Array(n), read = 0; + var markAsReadAndPasteIfAllFilesAreRead = function () { + if (++read == n) { + operation(cm, function () { + pos = clipPos(cm.doc, pos); + var change = {from: pos, to: pos, + text: cm.doc.splitLines( + text.filter(function (t) { return t != null; }).join(cm.doc.lineSeparator())), + origin: "paste"}; + makeChange(cm.doc, change); + setSelectionReplaceHistory(cm.doc, simpleSelection(clipPos(cm.doc, pos), clipPos(cm.doc, changeEnd(change)))); + })(); + } + }; + var readTextFromFile = function (file, i) { + if (cm.options.allowDropFileTypes && + indexOf(cm.options.allowDropFileTypes, file.type) == -1) { + markAsReadAndPasteIfAllFilesAreRead(); + return + } + var reader = new FileReader; + reader.onerror = function () { return markAsReadAndPasteIfAllFilesAreRead(); }; + reader.onload = function () { + var content = reader.result; + if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { + markAsReadAndPasteIfAllFilesAreRead(); + return + } + text[i] = content; + markAsReadAndPasteIfAllFilesAreRead(); + }; + reader.readAsText(file); + }; + for (var i = 0; i < files.length; i++) { readTextFromFile(files[i], i); } + } else { // Normal drop + // Don't do a replace if the drop happened inside of the selected text. + if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) { + cm.state.draggingText(e); + // Ensure the editor is re-focused + setTimeout(function () { return cm.display.input.focus(); }, 20); + return + } + try { + var text$1 = e.dataTransfer.getData("Text"); + if (text$1) { + var selected; + if (cm.state.draggingText && !cm.state.draggingText.copy) + { selected = cm.listSelections(); } + setSelectionNoUndo(cm.doc, simpleSelection(pos, pos)); + if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1) + { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } } + cm.replaceSelection(text$1, "around", "paste"); + cm.display.input.focus(); + } + } + catch(e$1){} + } + } + + function onDragStart(cm, e) { + if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return } + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return } + + e.dataTransfer.setData("Text", cm.getSelection()); + e.dataTransfer.effectAllowed = "copyMove"; + + // Use dummy image instead of default browsers image. + // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there. + if (e.dataTransfer.setDragImage && !safari) { + var img = elt("img", null, null, "position: fixed; left: 0; top: 0;"); + img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; + if (presto) { + img.width = img.height = 1; + cm.display.wrapper.appendChild(img); + // Force a relayout, or Opera won't use our image for some obscure reason + img._top = img.offsetTop; + } + e.dataTransfer.setDragImage(img, 0, 0); + if (presto) { img.parentNode.removeChild(img); } + } + } + + function onDragOver(cm, e) { + var pos = posFromMouse(cm, e); + if (!pos) { return } + var frag = document.createDocumentFragment(); + drawSelectionCursor(cm, pos, frag); + if (!cm.display.dragCursor) { + cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors"); + cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv); + } + removeChildrenAndAdd(cm.display.dragCursor, frag); + } + + function clearDragCursor(cm) { + if (cm.display.dragCursor) { + cm.display.lineSpace.removeChild(cm.display.dragCursor); + cm.display.dragCursor = null; + } + } + + // These must be handled carefully, because naively registering a + // handler for each editor will cause the editors to never be + // garbage collected. + + function forEachCodeMirror(f) { + if (!document.getElementsByClassName) { return } + var byClass = document.getElementsByClassName("CodeMirror"), editors = []; + for (var i = 0; i < byClass.length; i++) { + var cm = byClass[i].CodeMirror; + if (cm) { editors.push(cm); } + } + if (editors.length) { editors[0].operation(function () { + for (var i = 0; i < editors.length; i++) { f(editors[i]); } + }); } + } + + var globalsRegistered = false; + function ensureGlobalHandlers() { + if (globalsRegistered) { return } + registerGlobalHandlers(); + globalsRegistered = true; + } + function registerGlobalHandlers() { + // When the window resizes, we need to refresh active editors. + var resizeTimer; + on(window, "resize", function () { + if (resizeTimer == null) { resizeTimer = setTimeout(function () { + resizeTimer = null; + forEachCodeMirror(onResize); + }, 100); } + }); + // When the window loses focus, we want to show the editor as blurred + on(window, "blur", function () { return forEachCodeMirror(onBlur); }); + } + // Called when the window resizes + function onResize(cm) { + var d = cm.display; + // Might be a text scaling operation, clear size caches. + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; + d.scrollbarsClipped = false; + cm.setSize(); + } + + var keyNames = { + 3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", + 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", + 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", + 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", + 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 145: "ScrollLock", + 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", + 221: "]", 222: "'", 224: "Mod", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", + 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert" + }; + + // Number keys + for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); } + // Alphabetic keys + for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); } + // Function keys + for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; } + + var keyMap = {}; + + keyMap.basic = { + "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", + "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", + "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore", + "Tab": "defaultTab", "Shift-Tab": "indentAuto", + "Enter": "newlineAndIndent", "Insert": "toggleOverwrite", + "Esc": "singleSelection" + }; + // Note that the save and find-related commands aren't defined by + // default. User code or addons can define them. Unknown commands + // are simply ignored. + keyMap.pcDefault = { + "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", + "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown", + "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", + "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find", + "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", + "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", + "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection", + "fallthrough": "basic" + }; + // Very basic readline/emacs-style bindings, which are standard on Mac. + keyMap.emacsy = { + "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", + "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", + "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", + "Ctrl-T": "transposeChars", "Ctrl-O": "openLine" + }; + keyMap.macDefault = { + "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", + "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft", + "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore", + "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find", + "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", + "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight", + "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd", + "fallthrough": ["basic", "emacsy"] + }; + keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault; + + // KEYMAP DISPATCH + + function normalizeKeyName(name) { + var parts = name.split(/-(?!$)/); + name = parts[parts.length - 1]; + var alt, ctrl, shift, cmd; + for (var i = 0; i < parts.length - 1; i++) { + var mod = parts[i]; + if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; } + else if (/^a(lt)?$/i.test(mod)) { alt = true; } + else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; } + else if (/^s(hift)?$/i.test(mod)) { shift = true; } + else { throw new Error("Unrecognized modifier name: " + mod) } + } + if (alt) { name = "Alt-" + name; } + if (ctrl) { name = "Ctrl-" + name; } + if (cmd) { name = "Cmd-" + name; } + if (shift) { name = "Shift-" + name; } + return name + } + + // This is a kludge to keep keymaps mostly working as raw objects + // (backwards compatibility) while at the same time support features + // like normalization and multi-stroke key bindings. It compiles a + // new normalized keymap, and then updates the old object to reflect + // this. + function normalizeKeyMap(keymap) { + var copy = {}; + for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) { + var value = keymap[keyname]; + if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue } + if (value == "...") { delete keymap[keyname]; continue } + + var keys = map(keyname.split(" "), normalizeKeyName); + for (var i = 0; i < keys.length; i++) { + var val = (void 0), name = (void 0); + if (i == keys.length - 1) { + name = keys.join(" "); + val = value; + } else { + name = keys.slice(0, i + 1).join(" "); + val = "..."; + } + var prev = copy[name]; + if (!prev) { copy[name] = val; } + else if (prev != val) { throw new Error("Inconsistent bindings for " + name) } + } + delete keymap[keyname]; + } } + for (var prop in copy) { keymap[prop] = copy[prop]; } + return keymap + } + + function lookupKey(key, map, handle, context) { + map = getKeyMap(map); + var found = map.call ? map.call(key, context) : map[key]; + if (found === false) { return "nothing" } + if (found === "...") { return "multi" } + if (found != null && handle(found)) { return "handled" } + + if (map.fallthrough) { + if (Object.prototype.toString.call(map.fallthrough) != "[object Array]") + { return lookupKey(key, map.fallthrough, handle, context) } + for (var i = 0; i < map.fallthrough.length; i++) { + var result = lookupKey(key, map.fallthrough[i], handle, context); + if (result) { return result } + } + } + } + + // Modifier key presses don't count as 'real' key presses for the + // purpose of keymap fallthrough. + function isModifierKey(value) { + var name = typeof value == "string" ? value : keyNames[value.keyCode]; + return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod" + } + + function addModifierNames(name, event, noShift) { + var base = name; + if (event.altKey && base != "Alt") { name = "Alt-" + name; } + if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; } + if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Mod") { name = "Cmd-" + name; } + if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; } + return name + } + + // Look up the name of a key as indicated by an event object. + function keyName(event, noShift) { + if (presto && event.keyCode == 34 && event["char"]) { return false } + var name = keyNames[event.keyCode]; + if (name == null || event.altGraphKey) { return false } + // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause, + // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+) + if (event.keyCode == 3 && event.code) { name = event.code; } + return addModifierNames(name, event, noShift) + } + + function getKeyMap(val) { + return typeof val == "string" ? keyMap[val] : val + } + + // Helper for deleting text near the selection(s), used to implement + // backspace, delete, and similar functionality. + function deleteNearSelection(cm, compute) { + var ranges = cm.doc.sel.ranges, kill = []; + // Build up a set of ranges to kill first, merging overlapping + // ranges. + for (var i = 0; i < ranges.length; i++) { + var toKill = compute(ranges[i]); + while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) { + var replaced = kill.pop(); + if (cmp(replaced.from, toKill.from) < 0) { + toKill.from = replaced.from; + break + } + } + kill.push(toKill); + } + // Next, remove those actual ranges. + runInOp(cm, function () { + for (var i = kill.length - 1; i >= 0; i--) + { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); } + ensureCursorVisible(cm); + }); + } + + function moveCharLogically(line, ch, dir) { + var target = skipExtendingChars(line.text, ch + dir, dir); + return target < 0 || target > line.text.length ? null : target + } + + function moveLogically(line, start, dir) { + var ch = moveCharLogically(line, start.ch, dir); + return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before") + } + + function endOfLine(visually, cm, lineObj, lineNo, dir) { + if (visually) { + if (cm.doc.direction == "rtl") { dir = -dir; } + var order = getOrder(lineObj, cm.doc.direction); + if (order) { + var part = dir < 0 ? lst(order) : order[0]; + var moveInStorageOrder = (dir < 0) == (part.level == 1); + var sticky = moveInStorageOrder ? "after" : "before"; + var ch; + // With a wrapped rtl chunk (possibly spanning multiple bidi parts), + // it could be that the last bidi part is not on the last visual line, + // since visual lines contain content order-consecutive chunks. + // Thus, in rtl, we are looking for the first (content-order) character + // in the rtl chunk that is on the last line (that is, the same line + // as the last (content-order) character). + if (part.level > 0 || cm.doc.direction == "rtl") { + var prep = prepareMeasureForLine(cm, lineObj); + ch = dir < 0 ? lineObj.text.length - 1 : 0; + var targetTop = measureCharPrepared(cm, prep, ch).top; + ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch); + if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); } + } else { ch = dir < 0 ? part.to : part.from; } + return new Pos(lineNo, ch, sticky) + } + } + return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after") + } + + function moveVisually(cm, line, start, dir) { + var bidi = getOrder(line, cm.doc.direction); + if (!bidi) { return moveLogically(line, start, dir) } + if (start.ch >= line.text.length) { + start.ch = line.text.length; + start.sticky = "before"; + } else if (start.ch <= 0) { + start.ch = 0; + start.sticky = "after"; + } + var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]; + if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) { + // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines, + // nothing interesting happens. + return moveLogically(line, start, dir) + } + + var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }; + var prep; + var getWrappedLineExtent = function (ch) { + if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} } + prep = prep || prepareMeasureForLine(cm, line); + return wrappedLineExtentChar(cm, line, prep, ch) + }; + var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch); + + if (cm.doc.direction == "rtl" || part.level == 1) { + var moveInStorageOrder = (part.level == 1) == (dir < 0); + var ch = mv(start, moveInStorageOrder ? 1 : -1); + if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) { + // Case 2: We move within an rtl part or in an rtl editor on the same visual line + var sticky = moveInStorageOrder ? "before" : "after"; + return new Pos(start.line, ch, sticky) + } + } + + // Case 3: Could not move within this bidi part in this visual line, so leave + // the current bidi part + + var searchInVisualLine = function (partPos, dir, wrappedLineExtent) { + var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder + ? new Pos(start.line, mv(ch, 1), "before") + : new Pos(start.line, ch, "after"); }; + + for (; partPos >= 0 && partPos < bidi.length; partPos += dir) { + var part = bidi[partPos]; + var moveInStorageOrder = (dir > 0) == (part.level != 1); + var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1); + if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) } + ch = moveInStorageOrder ? part.from : mv(part.to, -1); + if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) } + } + }; + + // Case 3a: Look for other bidi parts on the same visual line + var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent); + if (res) { return res } + + // Case 3b: Look for other bidi parts on the next visual line + var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1); + if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) { + res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh)); + if (res) { return res } + } + + // Case 4: Nowhere to move + return null + } + + // Commands are parameter-less actions that can be performed on an + // editor, mostly used for keybindings. + var commands = { + selectAll: selectAll, + singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); }, + killLine: function (cm) { return deleteNearSelection(cm, function (range) { + if (range.empty()) { + var len = getLine(cm.doc, range.head.line).text.length; + if (range.head.ch == len && range.head.line < cm.lastLine()) + { return {from: range.head, to: Pos(range.head.line + 1, 0)} } + else + { return {from: range.head, to: Pos(range.head.line, len)} } + } else { + return {from: range.from(), to: range.to()} + } + }); }, + deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({ + from: Pos(range.from().line, 0), + to: clipPos(cm.doc, Pos(range.to().line + 1, 0)) + }); }); }, + delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({ + from: Pos(range.from().line, 0), to: range.from() + }); }); }, + delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { + var top = cm.charCoords(range.head, "div").top + 5; + var leftPos = cm.coordsChar({left: 0, top: top}, "div"); + return {from: leftPos, to: range.from()} + }); }, + delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) { + var top = cm.charCoords(range.head, "div").top + 5; + var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"); + return {from: range.from(), to: rightPos } + }); }, + undo: function (cm) { return cm.undo(); }, + redo: function (cm) { return cm.redo(); }, + undoSelection: function (cm) { return cm.undoSelection(); }, + redoSelection: function (cm) { return cm.redoSelection(); }, + goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); }, + goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); }, + goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); }, + {origin: "+move", bias: 1} + ); }, + goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); }, + {origin: "+move", bias: 1} + ); }, + goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); }, + {origin: "+move", bias: -1} + ); }, + goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.cursorCoords(range.head, "div").top + 5; + return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") + }, sel_move); }, + goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.cursorCoords(range.head, "div").top + 5; + return cm.coordsChar({left: 0, top: top}, "div") + }, sel_move); }, + goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.cursorCoords(range.head, "div").top + 5; + var pos = cm.coordsChar({left: 0, top: top}, "div"); + if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) } + return pos + }, sel_move); }, + goLineUp: function (cm) { return cm.moveV(-1, "line"); }, + goLineDown: function (cm) { return cm.moveV(1, "line"); }, + goPageUp: function (cm) { return cm.moveV(-1, "page"); }, + goPageDown: function (cm) { return cm.moveV(1, "page"); }, + goCharLeft: function (cm) { return cm.moveH(-1, "char"); }, + goCharRight: function (cm) { return cm.moveH(1, "char"); }, + goColumnLeft: function (cm) { return cm.moveH(-1, "column"); }, + goColumnRight: function (cm) { return cm.moveH(1, "column"); }, + goWordLeft: function (cm) { return cm.moveH(-1, "word"); }, + goGroupRight: function (cm) { return cm.moveH(1, "group"); }, + goGroupLeft: function (cm) { return cm.moveH(-1, "group"); }, + goWordRight: function (cm) { return cm.moveH(1, "word"); }, + delCharBefore: function (cm) { return cm.deleteH(-1, "codepoint"); }, + delCharAfter: function (cm) { return cm.deleteH(1, "char"); }, + delWordBefore: function (cm) { return cm.deleteH(-1, "word"); }, + delWordAfter: function (cm) { return cm.deleteH(1, "word"); }, + delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); }, + delGroupAfter: function (cm) { return cm.deleteH(1, "group"); }, + indentAuto: function (cm) { return cm.indentSelection("smart"); }, + indentMore: function (cm) { return cm.indentSelection("add"); }, + indentLess: function (cm) { return cm.indentSelection("subtract"); }, + insertTab: function (cm) { return cm.replaceSelection("\t"); }, + insertSoftTab: function (cm) { + var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize; + for (var i = 0; i < ranges.length; i++) { + var pos = ranges[i].from(); + var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize); + spaces.push(spaceStr(tabSize - col % tabSize)); + } + cm.replaceSelections(spaces); + }, + defaultTab: function (cm) { + if (cm.somethingSelected()) { cm.indentSelection("add"); } + else { cm.execCommand("insertTab"); } + }, + // Swap the two chars left and right of each selection's head. + // Move cursor behind the two swapped characters afterwards. + // + // Doesn't consider line feeds a character. + // Doesn't scan more than one line above to find a character. + // Doesn't do anything on an empty line. + // Doesn't do anything with non-empty selections. + transposeChars: function (cm) { return runInOp(cm, function () { + var ranges = cm.listSelections(), newSel = []; + for (var i = 0; i < ranges.length; i++) { + if (!ranges[i].empty()) { continue } + var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text; + if (line) { + if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); } + if (cur.ch > 0) { + cur = new Pos(cur.line, cur.ch + 1); + cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), + Pos(cur.line, cur.ch - 2), cur, "+transpose"); + } else if (cur.line > cm.doc.first) { + var prev = getLine(cm.doc, cur.line - 1).text; + if (prev) { + cur = new Pos(cur.line, 1); + cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() + + prev.charAt(prev.length - 1), + Pos(cur.line - 1, prev.length - 1), cur, "+transpose"); + } + } + } + newSel.push(new Range(cur, cur)); + } + cm.setSelections(newSel); + }); }, + newlineAndIndent: function (cm) { return runInOp(cm, function () { + var sels = cm.listSelections(); + for (var i = sels.length - 1; i >= 0; i--) + { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); } + sels = cm.listSelections(); + for (var i$1 = 0; i$1 < sels.length; i$1++) + { cm.indentLine(sels[i$1].from().line, null, true); } + ensureCursorVisible(cm); + }); }, + openLine: function (cm) { return cm.replaceSelection("\n", "start"); }, + toggleOverwrite: function (cm) { return cm.toggleOverwrite(); } + }; + + + function lineStart(cm, lineN) { + var line = getLine(cm.doc, lineN); + var visual = visualLine(line); + if (visual != line) { lineN = lineNo(visual); } + return endOfLine(true, cm, visual, lineN, 1) + } + function lineEnd(cm, lineN) { + var line = getLine(cm.doc, lineN); + var visual = visualLineEnd(line); + if (visual != line) { lineN = lineNo(visual); } + return endOfLine(true, cm, line, lineN, -1) + } + function lineStartSmart(cm, pos) { + var start = lineStart(cm, pos.line); + var line = getLine(cm.doc, start.line); + var order = getOrder(line, cm.doc.direction); + if (!order || order[0].level == 0) { + var firstNonWS = Math.max(start.ch, line.text.search(/\S/)); + var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch; + return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky) + } + return start + } + + // Run a handler that was bound to a key. + function doHandleBinding(cm, bound, dropShift) { + if (typeof bound == "string") { + bound = commands[bound]; + if (!bound) { return false } + } + // Ensure previous input has been read, so that the handler sees a + // consistent view of the document + cm.display.input.ensurePolled(); + var prevShift = cm.display.shift, done = false; + try { + if (cm.isReadOnly()) { cm.state.suppressEdits = true; } + if (dropShift) { cm.display.shift = false; } + done = bound(cm) != Pass; + } finally { + cm.display.shift = prevShift; + cm.state.suppressEdits = false; + } + return done + } + + function lookupKeyForEditor(cm, name, handle) { + for (var i = 0; i < cm.state.keyMaps.length; i++) { + var result = lookupKey(name, cm.state.keyMaps[i], handle, cm); + if (result) { return result } + } + return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm)) + || lookupKey(name, cm.options.keyMap, handle, cm) + } + + // Note that, despite the name, this function is also used to check + // for bound mouse clicks. + + var stopSeq = new Delayed; + + function dispatchKey(cm, name, e, handle) { + var seq = cm.state.keySeq; + if (seq) { + if (isModifierKey(name)) { return "handled" } + if (/\'$/.test(name)) + { cm.state.keySeq = null; } + else + { stopSeq.set(50, function () { + if (cm.state.keySeq == seq) { + cm.state.keySeq = null; + cm.display.input.reset(); + } + }); } + if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true } + } + return dispatchKeyInner(cm, name, e, handle) + } + + function dispatchKeyInner(cm, name, e, handle) { + var result = lookupKeyForEditor(cm, name, handle); + + if (result == "multi") + { cm.state.keySeq = name; } + if (result == "handled") + { signalLater(cm, "keyHandled", cm, name, e); } + + if (result == "handled" || result == "multi") { + e_preventDefault(e); + restartBlink(cm); + } + + return !!result + } + + // Handle a key from the keydown event. + function handleKeyBinding(cm, e) { + var name = keyName(e, true); + if (!name) { return false } + + if (e.shiftKey && !cm.state.keySeq) { + // First try to resolve full name (including 'Shift-'). Failing + // that, see if there is a cursor-motion command (starting with + // 'go') bound to the keyname without 'Shift-'. + return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); }) + || dispatchKey(cm, name, e, function (b) { + if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion) + { return doHandleBinding(cm, b) } + }) + } else { + return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); }) + } + } + + // Handle a key from the keypress event + function handleCharBinding(cm, e, ch) { + return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); }) + } + + var lastStoppedKey = null; + function onKeyDown(e) { + var cm = this; + if (e.target && e.target != cm.display.input.getField()) { return } + cm.curOp.focus = activeElt(doc(cm)); + if (signalDOMEvent(cm, e)) { return } + // IE does strange things with escape. + if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; } + var code = e.keyCode; + cm.display.shift = code == 16 || e.shiftKey; + var handled = handleKeyBinding(cm, e); + if (presto) { + lastStoppedKey = handled ? code : null; + // Opera has no cut event... we try to at least catch the key combo + if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey)) + { cm.replaceSelection("", null, "cut"); } + } + if (gecko && !mac && !handled && code == 46 && e.shiftKey && !e.ctrlKey && document.execCommand) + { document.execCommand("cut"); } + + // Turn mouse into crosshair when Alt is held on Mac. + if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className)) + { showCrossHair(cm); } + } + + function showCrossHair(cm) { + var lineDiv = cm.display.lineDiv; + addClass(lineDiv, "CodeMirror-crosshair"); + + function up(e) { + if (e.keyCode == 18 || !e.altKey) { + rmClass(lineDiv, "CodeMirror-crosshair"); + off(document, "keyup", up); + off(document, "mouseover", up); + } + } + on(document, "keyup", up); + on(document, "mouseover", up); + } + + function onKeyUp(e) { + if (e.keyCode == 16) { this.doc.sel.shift = false; } + signalDOMEvent(this, e); + } + + function onKeyPress(e) { + var cm = this; + if (e.target && e.target != cm.display.input.getField()) { return } + if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return } + var keyCode = e.keyCode, charCode = e.charCode; + if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return} + if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return } + var ch = String.fromCharCode(charCode == null ? keyCode : charCode); + // Some browsers fire keypress events for backspace + if (ch == "\x08") { return } + if (handleCharBinding(cm, e, ch)) { return } + cm.display.input.onKeyPress(e); + } + + var DOUBLECLICK_DELAY = 400; + + var PastClick = function(time, pos, button) { + this.time = time; + this.pos = pos; + this.button = button; + }; + + PastClick.prototype.compare = function (time, pos, button) { + return this.time + DOUBLECLICK_DELAY > time && + cmp(pos, this.pos) == 0 && button == this.button + }; + + var lastClick, lastDoubleClick; + function clickRepeat(pos, button) { + var now = +new Date; + if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) { + lastClick = lastDoubleClick = null; + return "triple" + } else if (lastClick && lastClick.compare(now, pos, button)) { + lastDoubleClick = new PastClick(now, pos, button); + lastClick = null; + return "double" + } else { + lastClick = new PastClick(now, pos, button); + lastDoubleClick = null; + return "single" + } + } + + // A mouse down can be a single click, double click, triple click, + // start of selection drag, start of text drag, new cursor + // (ctrl-click), rectangle drag (alt-drag), or xwin + // middle-click-paste. Or it might be a click on something we should + // not interfere with, such as a scrollbar or widget. + function onMouseDown(e) { + var cm = this, display = cm.display; + if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return } + display.input.ensurePolled(); + display.shift = e.shiftKey; + + if (eventInWidget(display, e)) { + if (!webkit) { + // Briefly turn off draggability, to allow widgets to do + // normal dragging things. + display.scroller.draggable = false; + setTimeout(function () { return display.scroller.draggable = true; }, 100); + } + return + } + if (clickInGutter(cm, e)) { return } + var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single"; + win(cm).focus(); + + // #3261: make sure, that we're not starting a second selection + if (button == 1 && cm.state.selectingText) + { cm.state.selectingText(e); } + + if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return } + + if (button == 1) { + if (pos) { leftButtonDown(cm, pos, repeat, e); } + else if (e_target(e) == display.scroller) { e_preventDefault(e); } + } else if (button == 2) { + if (pos) { extendSelection(cm.doc, pos); } + setTimeout(function () { return display.input.focus(); }, 20); + } else if (button == 3) { + if (captureRightClick) { cm.display.input.onContextMenu(e); } + else { delayBlurEvent(cm); } + } + } + + function handleMappedButton(cm, button, pos, repeat, event) { + var name = "Click"; + if (repeat == "double") { name = "Double" + name; } + else if (repeat == "triple") { name = "Triple" + name; } + name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name; + + return dispatchKey(cm, addModifierNames(name, event), event, function (bound) { + if (typeof bound == "string") { bound = commands[bound]; } + if (!bound) { return false } + var done = false; + try { + if (cm.isReadOnly()) { cm.state.suppressEdits = true; } + done = bound(cm, pos) != Pass; + } finally { + cm.state.suppressEdits = false; + } + return done + }) + } + + function configureMouse(cm, repeat, event) { + var option = cm.getOption("configureMouse"); + var value = option ? option(cm, repeat, event) : {}; + if (value.unit == null) { + var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey; + value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line"; + } + if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; } + if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; } + if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); } + return value + } + + function leftButtonDown(cm, pos, repeat, event) { + if (ie) { setTimeout(bind(ensureFocus, cm), 0); } + else { cm.curOp.focus = activeElt(doc(cm)); } + + var behavior = configureMouse(cm, repeat, event); + + var sel = cm.doc.sel, contained; + if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() && + repeat == "single" && (contained = sel.contains(pos)) > -1 && + (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) && + (cmp(contained.to(), pos) > 0 || pos.xRel < 0)) + { leftButtonStartDrag(cm, event, pos, behavior); } + else + { leftButtonSelect(cm, event, pos, behavior); } + } + + // Start a text drag. When it ends, see if any dragging actually + // happen, and treat as a click if it didn't. + function leftButtonStartDrag(cm, event, pos, behavior) { + var display = cm.display, moved = false; + var dragEnd = operation(cm, function (e) { + if (webkit) { display.scroller.draggable = false; } + cm.state.draggingText = false; + if (cm.state.delayingBlurEvent) { + if (cm.hasFocus()) { cm.state.delayingBlurEvent = false; } + else { delayBlurEvent(cm); } + } + off(display.wrapper.ownerDocument, "mouseup", dragEnd); + off(display.wrapper.ownerDocument, "mousemove", mouseMove); + off(display.scroller, "dragstart", dragStart); + off(display.scroller, "drop", dragEnd); + if (!moved) { + e_preventDefault(e); + if (!behavior.addNew) + { extendSelection(cm.doc, pos, null, null, behavior.extend); } + // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081) + if ((webkit && !safari) || ie && ie_version == 9) + { setTimeout(function () {display.wrapper.ownerDocument.body.focus({preventScroll: true}); display.input.focus();}, 20); } + else + { display.input.focus(); } + } + }); + var mouseMove = function(e2) { + moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10; + }; + var dragStart = function () { return moved = true; }; + // Let the drag handler handle this. + if (webkit) { display.scroller.draggable = true; } + cm.state.draggingText = dragEnd; + dragEnd.copy = !behavior.moveOnDrag; + on(display.wrapper.ownerDocument, "mouseup", dragEnd); + on(display.wrapper.ownerDocument, "mousemove", mouseMove); + on(display.scroller, "dragstart", dragStart); + on(display.scroller, "drop", dragEnd); + + cm.state.delayingBlurEvent = true; + setTimeout(function () { return display.input.focus(); }, 20); + // IE's approach to draggable + if (display.scroller.dragDrop) { display.scroller.dragDrop(); } + } + + function rangeForUnit(cm, pos, unit) { + if (unit == "char") { return new Range(pos, pos) } + if (unit == "word") { return cm.findWordAt(pos) } + if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) } + var result = unit(cm, pos); + return new Range(result.from, result.to) + } + + // Normal selection, as opposed to text dragging. + function leftButtonSelect(cm, event, start, behavior) { + if (ie) { delayBlurEvent(cm); } + var display = cm.display, doc$1 = cm.doc; + e_preventDefault(event); + + var ourRange, ourIndex, startSel = doc$1.sel, ranges = startSel.ranges; + if (behavior.addNew && !behavior.extend) { + ourIndex = doc$1.sel.contains(start); + if (ourIndex > -1) + { ourRange = ranges[ourIndex]; } + else + { ourRange = new Range(start, start); } + } else { + ourRange = doc$1.sel.primary(); + ourIndex = doc$1.sel.primIndex; + } + + if (behavior.unit == "rectangle") { + if (!behavior.addNew) { ourRange = new Range(start, start); } + start = posFromMouse(cm, event, true, true); + ourIndex = -1; + } else { + var range = rangeForUnit(cm, start, behavior.unit); + if (behavior.extend) + { ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend); } + else + { ourRange = range; } + } + + if (!behavior.addNew) { + ourIndex = 0; + setSelection(doc$1, new Selection([ourRange], 0), sel_mouse); + startSel = doc$1.sel; + } else if (ourIndex == -1) { + ourIndex = ranges.length; + setSelection(doc$1, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex), + {scroll: false, origin: "*mouse"}); + } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) { + setSelection(doc$1, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0), + {scroll: false, origin: "*mouse"}); + startSel = doc$1.sel; + } else { + replaceOneSelection(doc$1, ourIndex, ourRange, sel_mouse); + } + + var lastPos = start; + function extendTo(pos) { + if (cmp(lastPos, pos) == 0) { return } + lastPos = pos; + + if (behavior.unit == "rectangle") { + var ranges = [], tabSize = cm.options.tabSize; + var startCol = countColumn(getLine(doc$1, start.line).text, start.ch, tabSize); + var posCol = countColumn(getLine(doc$1, pos.line).text, pos.ch, tabSize); + var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol); + for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line)); + line <= end; line++) { + var text = getLine(doc$1, line).text, leftPos = findColumn(text, left, tabSize); + if (left == right) + { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); } + else if (text.length > leftPos) + { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); } + } + if (!ranges.length) { ranges.push(new Range(start, start)); } + setSelection(doc$1, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex), + {origin: "*mouse", scroll: false}); + cm.scrollIntoView(pos); + } else { + var oldRange = ourRange; + var range = rangeForUnit(cm, pos, behavior.unit); + var anchor = oldRange.anchor, head; + if (cmp(range.anchor, anchor) > 0) { + head = range.head; + anchor = minPos(oldRange.from(), range.anchor); + } else { + head = range.anchor; + anchor = maxPos(oldRange.to(), range.head); + } + var ranges$1 = startSel.ranges.slice(0); + ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc$1, anchor), head)); + setSelection(doc$1, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse); + } + } + + var editorSize = display.wrapper.getBoundingClientRect(); + // Used to ensure timeout re-tries don't fire when another extend + // happened in the meantime (clearTimeout isn't reliable -- at + // least on Chrome, the timeouts still happen even when cleared, + // if the clear happens after their scheduled firing time). + var counter = 0; + + function extend(e) { + var curCount = ++counter; + var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle"); + if (!cur) { return } + if (cmp(cur, lastPos) != 0) { + cm.curOp.focus = activeElt(doc(cm)); + extendTo(cur); + var visible = visibleLines(display, doc$1); + if (cur.line >= visible.to || cur.line < visible.from) + { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); } + } else { + var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0; + if (outside) { setTimeout(operation(cm, function () { + if (counter != curCount) { return } + display.scroller.scrollTop += outside; + extend(e); + }), 50); } + } + } + + function done(e) { + cm.state.selectingText = false; + counter = Infinity; + // If e is null or undefined we interpret this as someone trying + // to explicitly cancel the selection rather than the user + // letting go of the mouse button. + if (e) { + e_preventDefault(e); + display.input.focus(); + } + off(display.wrapper.ownerDocument, "mousemove", move); + off(display.wrapper.ownerDocument, "mouseup", up); + doc$1.history.lastSelOrigin = null; + } + + var move = operation(cm, function (e) { + if (e.buttons === 0 || !e_button(e)) { done(e); } + else { extend(e); } + }); + var up = operation(cm, done); + cm.state.selectingText = up; + on(display.wrapper.ownerDocument, "mousemove", move); + on(display.wrapper.ownerDocument, "mouseup", up); + } + + // Used when mouse-selecting to adjust the anchor to the proper side + // of a bidi jump depending on the visual position of the head. + function bidiSimplify(cm, range) { + var anchor = range.anchor; + var head = range.head; + var anchorLine = getLine(cm.doc, anchor.line); + if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range } + var order = getOrder(anchorLine); + if (!order) { return range } + var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index]; + if (part.from != anchor.ch && part.to != anchor.ch) { return range } + var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1); + if (boundary == 0 || boundary == order.length) { return range } + + // Compute the relative visual position of the head compared to the + // anchor (<0 is to the left, >0 to the right) + var leftSide; + if (head.line != anchor.line) { + leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0; + } else { + var headIndex = getBidiPartAt(order, head.ch, head.sticky); + var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1); + if (headIndex == boundary - 1 || headIndex == boundary) + { leftSide = dir < 0; } + else + { leftSide = dir > 0; } + } + + var usePart = order[boundary + (leftSide ? -1 : 0)]; + var from = leftSide == (usePart.level == 1); + var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before"; + return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head) + } + + + // Determines whether an event happened in the gutter, and fires the + // handlers for the corresponding event. + function gutterEvent(cm, e, type, prevent) { + var mX, mY; + if (e.touches) { + mX = e.touches[0].clientX; + mY = e.touches[0].clientY; + } else { + try { mX = e.clientX; mY = e.clientY; } + catch(e$1) { return false } + } + if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false } + if (prevent) { e_preventDefault(e); } + + var display = cm.display; + var lineBox = display.lineDiv.getBoundingClientRect(); + + if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) } + mY -= lineBox.top - display.viewOffset; + + for (var i = 0; i < cm.display.gutterSpecs.length; ++i) { + var g = display.gutters.childNodes[i]; + if (g && g.getBoundingClientRect().right >= mX) { + var line = lineAtHeight(cm.doc, mY); + var gutter = cm.display.gutterSpecs[i]; + signal(cm, type, cm, line, gutter.className, e); + return e_defaultPrevented(e) + } + } + } + + function clickInGutter(cm, e) { + return gutterEvent(cm, e, "gutterClick", true) + } + + // CONTEXT MENU HANDLING + + // To make the context menu work, we need to briefly unhide the + // textarea (making it as unobtrusive as possible) to let the + // right-click take effect on it. + function onContextMenu(cm, e) { + if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return } + if (signalDOMEvent(cm, e, "contextmenu")) { return } + if (!captureRightClick) { cm.display.input.onContextMenu(e); } + } + + function contextMenuInGutter(cm, e) { + if (!hasHandler(cm, "gutterContextMenu")) { return false } + return gutterEvent(cm, e, "gutterContextMenu", false) + } + + function themeChanged(cm) { + cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") + + cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-"); + clearCaches(cm); + } + + var Init = {toString: function(){return "CodeMirror.Init"}}; + + var defaults = {}; + var optionHandlers = {}; + + function defineOptions(CodeMirror) { + var optionHandlers = CodeMirror.optionHandlers; + + function option(name, deflt, handle, notOnInit) { + CodeMirror.defaults[name] = deflt; + if (handle) { optionHandlers[name] = + notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; } + } + + CodeMirror.defineOption = option; + + // Passed to option handlers when there is no old value. + CodeMirror.Init = Init; + + // These two are, on init, called from the constructor because they + // have to be initialized before the editor can start at all. + option("value", "", function (cm, val) { return cm.setValue(val); }, true); + option("mode", null, function (cm, val) { + cm.doc.modeOption = val; + loadMode(cm); + }, true); + + option("indentUnit", 2, loadMode, true); + option("indentWithTabs", false); + option("smartIndent", true); + option("tabSize", 4, function (cm) { + resetModeState(cm); + clearCaches(cm); + regChange(cm); + }, true); + + option("lineSeparator", null, function (cm, val) { + cm.doc.lineSep = val; + if (!val) { return } + var newBreaks = [], lineNo = cm.doc.first; + cm.doc.iter(function (line) { + for (var pos = 0;;) { + var found = line.text.indexOf(val, pos); + if (found == -1) { break } + pos = found + val.length; + newBreaks.push(Pos(lineNo, found)); + } + lineNo++; + }); + for (var i = newBreaks.length - 1; i >= 0; i--) + { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); } + }); + option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\u202d\u202e\u2066\u2067\u2069\ufeff\ufff9-\ufffc]/g, function (cm, val, old) { + cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g"); + if (old != Init) { cm.refresh(); } + }); + option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true); + option("electricChars", true); + option("inputStyle", mobile ? "contenteditable" : "textarea", function () { + throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME + }, true); + option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true); + option("autocorrect", false, function (cm, val) { return cm.getInputField().autocorrect = val; }, true); + option("autocapitalize", false, function (cm, val) { return cm.getInputField().autocapitalize = val; }, true); + option("rtlMoveVisually", !windows); + option("wholeLineUpdateBefore", true); + + option("theme", "default", function (cm) { + themeChanged(cm); + updateGutters(cm); + }, true); + option("keyMap", "default", function (cm, val, old) { + var next = getKeyMap(val); + var prev = old != Init && getKeyMap(old); + if (prev && prev.detach) { prev.detach(cm, next); } + if (next.attach) { next.attach(cm, prev || null); } + }); + option("extraKeys", null); + option("configureMouse", null); + + option("lineWrapping", false, wrappingChanged, true); + option("gutters", [], function (cm, val) { + cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers); + updateGutters(cm); + }, true); + option("fixedGutter", true, function (cm, val) { + cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"; + cm.refresh(); + }, true); + option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true); + option("scrollbarStyle", "native", function (cm) { + initScrollbars(cm); + updateScrollbars(cm); + cm.display.scrollbars.setScrollTop(cm.doc.scrollTop); + cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft); + }, true); + option("lineNumbers", false, function (cm, val) { + cm.display.gutterSpecs = getGutters(cm.options.gutters, val); + updateGutters(cm); + }, true); + option("firstLineNumber", 1, updateGutters, true); + option("lineNumberFormatter", function (integer) { return integer; }, updateGutters, true); + option("showCursorWhenSelecting", false, updateSelection, true); + + option("resetSelectionOnContextMenu", true); + option("lineWiseCopyCut", true); + option("pasteLinesPerSelection", true); + option("selectionsMayTouch", false); + + option("readOnly", false, function (cm, val) { + if (val == "nocursor") { + onBlur(cm); + cm.display.input.blur(); + } + cm.display.input.readOnlyChanged(val); + }); + + option("screenReaderLabel", null, function (cm, val) { + val = (val === '') ? null : val; + cm.display.input.screenReaderLabelChanged(val); + }); + + option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true); + option("dragDrop", true, dragDropChanged); + option("allowDropFileTypes", null); + + option("cursorBlinkRate", 530); + option("cursorScrollMargin", 0); + option("cursorHeight", 1, updateSelection, true); + option("singleCursorHeightPerLine", true, updateSelection, true); + option("workTime", 100); + option("workDelay", 100); + option("flattenSpans", true, resetModeState, true); + option("addModeClass", false, resetModeState, true); + option("pollInterval", 100); + option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; }); + option("historyEventDelay", 1250); + option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true); + option("maxHighlightLength", 10000, resetModeState, true); + option("moveInputWithCursor", true, function (cm, val) { + if (!val) { cm.display.input.resetPosition(); } + }); + + option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; }); + option("autofocus", null); + option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true); + option("phrases", null); + } + + function dragDropChanged(cm, value, old) { + var wasOn = old && old != Init; + if (!value != !wasOn) { + var funcs = cm.display.dragFunctions; + var toggle = value ? on : off; + toggle(cm.display.scroller, "dragstart", funcs.start); + toggle(cm.display.scroller, "dragenter", funcs.enter); + toggle(cm.display.scroller, "dragover", funcs.over); + toggle(cm.display.scroller, "dragleave", funcs.leave); + toggle(cm.display.scroller, "drop", funcs.drop); + } + } + + function wrappingChanged(cm) { + if (cm.options.lineWrapping) { + addClass(cm.display.wrapper, "CodeMirror-wrap"); + cm.display.sizer.style.minWidth = ""; + cm.display.sizerWidth = null; + } else { + rmClass(cm.display.wrapper, "CodeMirror-wrap"); + findMaxLine(cm); + } + estimateLineHeights(cm); + regChange(cm); + clearCaches(cm); + setTimeout(function () { return updateScrollbars(cm); }, 100); + } + + // A CodeMirror instance represents an editor. This is the object + // that user code is usually dealing with. + + function CodeMirror(place, options) { + var this$1 = this; + + if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) } + + this.options = options = options ? copyObj(options) : {}; + // Determine effective options based on given values and defaults. + copyObj(defaults, options, false); + + var doc = options.value; + if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); } + else if (options.mode) { doc.modeOption = options.mode; } + this.doc = doc; + + var input = new CodeMirror.inputStyles[options.inputStyle](this); + var display = this.display = new Display(place, doc, input, options); + display.wrapper.CodeMirror = this; + themeChanged(this); + if (options.lineWrapping) + { this.display.wrapper.className += " CodeMirror-wrap"; } + initScrollbars(this); + + this.state = { + keyMaps: [], // stores maps added by addKeyMap + overlays: [], // highlighting overlays, as added by addOverlay + modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info + overwrite: false, + delayingBlurEvent: false, + focused: false, + suppressEdits: false, // used to disable editing during key handlers when in readOnly mode + pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll + selectingText: false, + draggingText: false, + highlight: new Delayed(), // stores highlight worker timeout + keySeq: null, // Unfinished key sequence + specialChars: null + }; + + if (options.autofocus && !mobile) { display.input.focus(); } + + // Override magic textarea content restore that IE sometimes does + // on our hidden textarea on reload + if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); } + + registerEventHandlers(this); + ensureGlobalHandlers(); + + startOperation(this); + this.curOp.forceUpdate = true; + attachDoc(this, doc); + + if ((options.autofocus && !mobile) || this.hasFocus()) + { setTimeout(function () { + if (this$1.hasFocus() && !this$1.state.focused) { onFocus(this$1); } + }, 20); } + else + { onBlur(this); } + + for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt)) + { optionHandlers[opt](this, options[opt], Init); } } + maybeUpdateLineNumberWidth(this); + if (options.finishInit) { options.finishInit(this); } + for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this); } + endOperation(this); + // Suppress optimizelegibility in Webkit, since it breaks text + // measuring on line wrapping boundaries. + if (webkit && options.lineWrapping && + getComputedStyle(display.lineDiv).textRendering == "optimizelegibility") + { display.lineDiv.style.textRendering = "auto"; } + } + + // The default configuration options. + CodeMirror.defaults = defaults; + // Functions to run when options are changed. + CodeMirror.optionHandlers = optionHandlers; + + // Attach the necessary event handlers when initializing the editor + function registerEventHandlers(cm) { + var d = cm.display; + on(d.scroller, "mousedown", operation(cm, onMouseDown)); + // Older IE's will not fire a second mousedown for a double click + if (ie && ie_version < 11) + { on(d.scroller, "dblclick", operation(cm, function (e) { + if (signalDOMEvent(cm, e)) { return } + var pos = posFromMouse(cm, e); + if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return } + e_preventDefault(e); + var word = cm.findWordAt(pos); + extendSelection(cm.doc, word.anchor, word.head); + })); } + else + { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); } + // Some browsers fire contextmenu *after* opening the menu, at + // which point we can't mess with it anymore. Context menu is + // handled in onMouseDown for these browsers. + on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }); + on(d.input.getField(), "contextmenu", function (e) { + if (!d.scroller.contains(e.target)) { onContextMenu(cm, e); } + }); + + // Used to suppress mouse event handling when a touch happens + var touchFinished, prevTouch = {end: 0}; + function finishTouch() { + if (d.activeTouch) { + touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000); + prevTouch = d.activeTouch; + prevTouch.end = +new Date; + } + } + function isMouseLikeTouchEvent(e) { + if (e.touches.length != 1) { return false } + var touch = e.touches[0]; + return touch.radiusX <= 1 && touch.radiusY <= 1 + } + function farAway(touch, other) { + if (other.left == null) { return true } + var dx = other.left - touch.left, dy = other.top - touch.top; + return dx * dx + dy * dy > 20 * 20 + } + on(d.scroller, "touchstart", function (e) { + if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) { + d.input.ensurePolled(); + clearTimeout(touchFinished); + var now = +new Date; + d.activeTouch = {start: now, moved: false, + prev: now - prevTouch.end <= 300 ? prevTouch : null}; + if (e.touches.length == 1) { + d.activeTouch.left = e.touches[0].pageX; + d.activeTouch.top = e.touches[0].pageY; + } + } + }); + on(d.scroller, "touchmove", function () { + if (d.activeTouch) { d.activeTouch.moved = true; } + }); + on(d.scroller, "touchend", function (e) { + var touch = d.activeTouch; + if (touch && !eventInWidget(d, e) && touch.left != null && + !touch.moved && new Date - touch.start < 300) { + var pos = cm.coordsChar(d.activeTouch, "page"), range; + if (!touch.prev || farAway(touch, touch.prev)) // Single tap + { range = new Range(pos, pos); } + else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap + { range = cm.findWordAt(pos); } + else // Triple tap + { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); } + cm.setSelection(range.anchor, range.head); + cm.focus(); + e_preventDefault(e); + } + finishTouch(); + }); + on(d.scroller, "touchcancel", finishTouch); + + // Sync scrolling between fake scrollbars and real scrollable + // area, ensure viewport is updated when scrolling. + on(d.scroller, "scroll", function () { + if (d.scroller.clientHeight) { + updateScrollTop(cm, d.scroller.scrollTop); + setScrollLeft(cm, d.scroller.scrollLeft, true); + signal(cm, "scroll", cm); + } + }); + + // Listen to wheel events in order to try and update the viewport on time. + on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); }); + on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); }); + + // Prevent wrapper from ever scrolling + on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); + + d.dragFunctions = { + enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }}, + over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }}, + start: function (e) { return onDragStart(cm, e); }, + drop: operation(cm, onDrop), + leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }} + }; + + var inp = d.input.getField(); + on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); }); + on(inp, "keydown", operation(cm, onKeyDown)); + on(inp, "keypress", operation(cm, onKeyPress)); + on(inp, "focus", function (e) { return onFocus(cm, e); }); + on(inp, "blur", function (e) { return onBlur(cm, e); }); + } + + var initHooks = []; + CodeMirror.defineInitHook = function (f) { return initHooks.push(f); }; + + // Indent the given line. The how parameter can be "smart", + // "add"/null, "subtract", or "prev". When aggressive is false + // (typically set to true for forced single-line indents), empty + // lines are not indented, and places where the mode returns Pass + // are left alone. + function indentLine(cm, n, how, aggressive) { + var doc = cm.doc, state; + if (how == null) { how = "add"; } + if (how == "smart") { + // Fall back to "prev" when the mode doesn't have an indentation + // method. + if (!doc.mode.indent) { how = "prev"; } + else { state = getContextBefore(cm, n).state; } + } + + var tabSize = cm.options.tabSize; + var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize); + if (line.stateAfter) { line.stateAfter = null; } + var curSpaceString = line.text.match(/^\s*/)[0], indentation; + if (!aggressive && !/\S/.test(line.text)) { + indentation = 0; + how = "not"; + } else if (how == "smart") { + indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text); + if (indentation == Pass || indentation > 150) { + if (!aggressive) { return } + how = "prev"; + } + } + if (how == "prev") { + if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); } + else { indentation = 0; } + } else if (how == "add") { + indentation = curSpace + cm.options.indentUnit; + } else if (how == "subtract") { + indentation = curSpace - cm.options.indentUnit; + } else if (typeof how == "number") { + indentation = curSpace + how; + } + indentation = Math.max(0, indentation); + + var indentString = "", pos = 0; + if (cm.options.indentWithTabs) + { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} } + if (pos < indentation) { indentString += spaceStr(indentation - pos); } + + if (indentString != curSpaceString) { + replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); + line.stateAfter = null; + return true + } else { + // Ensure that, if the cursor was in the whitespace at the start + // of the line, it is moved to the end of that space. + for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) { + var range = doc.sel.ranges[i$1]; + if (range.head.line == n && range.head.ch < curSpaceString.length) { + var pos$1 = Pos(n, curSpaceString.length); + replaceOneSelection(doc, i$1, new Range(pos$1, pos$1)); + break + } + } + } + } + + // This will be set to a {lineWise: bool, text: [string]} object, so + // that, when pasting, we know what kind of selections the copied + // text was made out of. + var lastCopied = null; + + function setLastCopied(newLastCopied) { + lastCopied = newLastCopied; + } + + function applyTextInput(cm, inserted, deleted, sel, origin) { + var doc = cm.doc; + cm.display.shift = false; + if (!sel) { sel = doc.sel; } + + var recent = +new Date - 200; + var paste = origin == "paste" || cm.state.pasteIncoming > recent; + var textLines = splitLinesAuto(inserted), multiPaste = null; + // When pasting N lines into N selections, insert one line per selection + if (paste && sel.ranges.length > 1) { + if (lastCopied && lastCopied.text.join("\n") == inserted) { + if (sel.ranges.length % lastCopied.text.length == 0) { + multiPaste = []; + for (var i = 0; i < lastCopied.text.length; i++) + { multiPaste.push(doc.splitLines(lastCopied.text[i])); } + } + } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) { + multiPaste = map(textLines, function (l) { return [l]; }); + } + } + + var updateInput = cm.curOp.updateInput; + // Normal behavior is to insert the new text into every selection + for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) { + var range = sel.ranges[i$1]; + var from = range.from(), to = range.to(); + if (range.empty()) { + if (deleted && deleted > 0) // Handle deletion + { from = Pos(from.line, from.ch - deleted); } + else if (cm.state.overwrite && !paste) // Handle overwrite + { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); } + else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == textLines.join("\n")) + { from = to = Pos(from.line, 0); } + } + var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines, + origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")}; + makeChange(cm.doc, changeEvent); + signalLater(cm, "inputRead", cm, changeEvent); + } + if (inserted && !paste) + { triggerElectric(cm, inserted); } + + ensureCursorVisible(cm); + if (cm.curOp.updateInput < 2) { cm.curOp.updateInput = updateInput; } + cm.curOp.typing = true; + cm.state.pasteIncoming = cm.state.cutIncoming = -1; + } + + function handlePaste(e, cm) { + var pasted = e.clipboardData && e.clipboardData.getData("Text"); + if (pasted) { + e.preventDefault(); + if (!cm.isReadOnly() && !cm.options.disableInput && cm.hasFocus()) + { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); } + return true + } + } + + function triggerElectric(cm, inserted) { + // When an 'electric' character is inserted, immediately trigger a reindent + if (!cm.options.electricChars || !cm.options.smartIndent) { return } + var sel = cm.doc.sel; + + for (var i = sel.ranges.length - 1; i >= 0; i--) { + var range = sel.ranges[i]; + if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) { continue } + var mode = cm.getModeAt(range.head); + var indented = false; + if (mode.electricChars) { + for (var j = 0; j < mode.electricChars.length; j++) + { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) { + indented = indentLine(cm, range.head.line, "smart"); + break + } } + } else if (mode.electricInput) { + if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch))) + { indented = indentLine(cm, range.head.line, "smart"); } + } + if (indented) { signalLater(cm, "electricInput", cm, range.head.line); } + } + } + + function copyableRanges(cm) { + var text = [], ranges = []; + for (var i = 0; i < cm.doc.sel.ranges.length; i++) { + var line = cm.doc.sel.ranges[i].head.line; + var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}; + ranges.push(lineRange); + text.push(cm.getRange(lineRange.anchor, lineRange.head)); + } + return {text: text, ranges: ranges} + } + + function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) { + field.setAttribute("autocorrect", autocorrect ? "" : "off"); + field.setAttribute("autocapitalize", autocapitalize ? "" : "off"); + field.setAttribute("spellcheck", !!spellcheck); + } + + function hiddenTextarea() { + var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; min-height: 1em; outline: none"); + var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); + // The textarea is kept positioned near the cursor to prevent the + // fact that it'll be scrolled into view on input from scrolling + // our fake cursor out of view. On webkit, when wrap=off, paste is + // very slow. So make the area wide instead. + if (webkit) { te.style.width = "1000px"; } + else { te.setAttribute("wrap", "off"); } + // If border: 0; -- iOS fails to open keyboard (issue #1287) + if (ios) { te.style.border = "1px solid black"; } + disableBrowserMagic(te); + return div + } + + // The publicly visible API. Note that methodOp(f) means + // 'wrap f in an operation, performed on its `this` parameter'. + + // This is not the complete set of editor methods. Most of the + // methods defined on the Doc type are also injected into + // CodeMirror.prototype, for backwards compatibility and + // convenience. + + function addEditorMethods(CodeMirror) { + var optionHandlers = CodeMirror.optionHandlers; + + var helpers = CodeMirror.helpers = {}; + + CodeMirror.prototype = { + constructor: CodeMirror, + focus: function(){win(this).focus(); this.display.input.focus();}, + + setOption: function(option, value) { + var options = this.options, old = options[option]; + if (options[option] == value && option != "mode") { return } + options[option] = value; + if (optionHandlers.hasOwnProperty(option)) + { operation(this, optionHandlers[option])(this, value, old); } + signal(this, "optionChange", this, option); + }, + + getOption: function(option) {return this.options[option]}, + getDoc: function() {return this.doc}, + + addKeyMap: function(map, bottom) { + this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map)); + }, + removeKeyMap: function(map) { + var maps = this.state.keyMaps; + for (var i = 0; i < maps.length; ++i) + { if (maps[i] == map || maps[i].name == map) { + maps.splice(i, 1); + return true + } } + }, + + addOverlay: methodOp(function(spec, options) { + var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec); + if (mode.startState) { throw new Error("Overlays may not be stateful.") } + insertSorted(this.state.overlays, + {mode: mode, modeSpec: spec, opaque: options && options.opaque, + priority: (options && options.priority) || 0}, + function (overlay) { return overlay.priority; }); + this.state.modeGen++; + regChange(this); + }), + removeOverlay: methodOp(function(spec) { + var overlays = this.state.overlays; + for (var i = 0; i < overlays.length; ++i) { + var cur = overlays[i].modeSpec; + if (cur == spec || typeof spec == "string" && cur.name == spec) { + overlays.splice(i, 1); + this.state.modeGen++; + regChange(this); + return + } + } + }), + + indentLine: methodOp(function(n, dir, aggressive) { + if (typeof dir != "string" && typeof dir != "number") { + if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; } + else { dir = dir ? "add" : "subtract"; } + } + if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); } + }), + indentSelection: methodOp(function(how) { + var ranges = this.doc.sel.ranges, end = -1; + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i]; + if (!range.empty()) { + var from = range.from(), to = range.to(); + var start = Math.max(end, from.line); + end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; + for (var j = start; j < end; ++j) + { indentLine(this, j, how); } + var newRanges = this.doc.sel.ranges; + if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0) + { replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); } + } else if (range.head.line > end) { + indentLine(this, range.head.line, how, true); + end = range.head.line; + if (i == this.doc.sel.primIndex) { ensureCursorVisible(this); } + } + } + }), + + // Fetch the parser token for a given character. Useful for hacks + // that want to inspect the mode state (say, for completion). + getTokenAt: function(pos, precise) { + return takeToken(this, pos, precise) + }, + + getLineTokens: function(line, precise) { + return takeToken(this, Pos(line), precise, true) + }, + + getTokenTypeAt: function(pos) { + pos = clipPos(this.doc, pos); + var styles = getLineStyles(this, getLine(this.doc, pos.line)); + var before = 0, after = (styles.length - 1) / 2, ch = pos.ch; + var type; + if (ch == 0) { type = styles[2]; } + else { for (;;) { + var mid = (before + after) >> 1; + if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; } + else if (styles[mid * 2 + 1] < ch) { before = mid + 1; } + else { type = styles[mid * 2 + 2]; break } + } } + var cut = type ? type.indexOf("overlay ") : -1; + return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1) + }, + + getModeAt: function(pos) { + var mode = this.doc.mode; + if (!mode.innerMode) { return mode } + return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode + }, + + getHelper: function(pos, type) { + return this.getHelpers(pos, type)[0] + }, + + getHelpers: function(pos, type) { + var found = []; + if (!helpers.hasOwnProperty(type)) { return found } + var help = helpers[type], mode = this.getModeAt(pos); + if (typeof mode[type] == "string") { + if (help[mode[type]]) { found.push(help[mode[type]]); } + } else if (mode[type]) { + for (var i = 0; i < mode[type].length; i++) { + var val = help[mode[type][i]]; + if (val) { found.push(val); } + } + } else if (mode.helperType && help[mode.helperType]) { + found.push(help[mode.helperType]); + } else if (help[mode.name]) { + found.push(help[mode.name]); + } + for (var i$1 = 0; i$1 < help._global.length; i$1++) { + var cur = help._global[i$1]; + if (cur.pred(mode, this) && indexOf(found, cur.val) == -1) + { found.push(cur.val); } + } + return found + }, + + getStateAfter: function(line, precise) { + var doc = this.doc; + line = clipLine(doc, line == null ? doc.first + doc.size - 1: line); + return getContextBefore(this, line + 1, precise).state + }, + + cursorCoords: function(start, mode) { + var pos, range = this.doc.sel.primary(); + if (start == null) { pos = range.head; } + else if (typeof start == "object") { pos = clipPos(this.doc, start); } + else { pos = start ? range.from() : range.to(); } + return cursorCoords(this, pos, mode || "page") + }, + + charCoords: function(pos, mode) { + return charCoords(this, clipPos(this.doc, pos), mode || "page") + }, + + coordsChar: function(coords, mode) { + coords = fromCoordSystem(this, coords, mode || "page"); + return coordsChar(this, coords.left, coords.top) + }, + + lineAtHeight: function(height, mode) { + height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top; + return lineAtHeight(this.doc, height + this.display.viewOffset) + }, + heightAtLine: function(line, mode, includeWidgets) { + var end = false, lineObj; + if (typeof line == "number") { + var last = this.doc.first + this.doc.size - 1; + if (line < this.doc.first) { line = this.doc.first; } + else if (line > last) { line = last; end = true; } + lineObj = getLine(this.doc, line); + } else { + lineObj = line; + } + return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top + + (end ? this.doc.height - heightAtLine(lineObj) : 0) + }, + + defaultTextHeight: function() { return textHeight(this.display) }, + defaultCharWidth: function() { return charWidth(this.display) }, + + getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}}, + + addWidget: function(pos, node, scroll, vert, horiz) { + var display = this.display; + pos = cursorCoords(this, clipPos(this.doc, pos)); + var top = pos.bottom, left = pos.left; + node.style.position = "absolute"; + node.setAttribute("cm-ignore-events", "true"); + this.display.input.setUneditable(node); + display.sizer.appendChild(node); + if (vert == "over") { + top = pos.top; + } else if (vert == "above" || vert == "near") { + var vspace = Math.max(display.wrapper.clientHeight, this.doc.height), + hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth); + // Default to positioning above (if specified and possible); otherwise default to positioning below + if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight) + { top = pos.top - node.offsetHeight; } + else if (pos.bottom + node.offsetHeight <= vspace) + { top = pos.bottom; } + if (left + node.offsetWidth > hspace) + { left = hspace - node.offsetWidth; } + } + node.style.top = top + "px"; + node.style.left = node.style.right = ""; + if (horiz == "right") { + left = display.sizer.clientWidth - node.offsetWidth; + node.style.right = "0px"; + } else { + if (horiz == "left") { left = 0; } + else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; } + node.style.left = left + "px"; + } + if (scroll) + { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); } + }, + + triggerOnKeyDown: methodOp(onKeyDown), + triggerOnKeyPress: methodOp(onKeyPress), + triggerOnKeyUp: onKeyUp, + triggerOnMouseDown: methodOp(onMouseDown), + + execCommand: function(cmd) { + if (commands.hasOwnProperty(cmd)) + { return commands[cmd].call(null, this) } + }, + + triggerElectric: methodOp(function(text) { triggerElectric(this, text); }), + + findPosH: function(from, amount, unit, visually) { + var dir = 1; + if (amount < 0) { dir = -1; amount = -amount; } + var cur = clipPos(this.doc, from); + for (var i = 0; i < amount; ++i) { + cur = findPosH(this.doc, cur, dir, unit, visually); + if (cur.hitSide) { break } + } + return cur + }, + + moveH: methodOp(function(dir, unit) { + var this$1 = this; + + this.extendSelectionsBy(function (range) { + if (this$1.display.shift || this$1.doc.extend || range.empty()) + { return findPosH(this$1.doc, range.head, dir, unit, this$1.options.rtlMoveVisually) } + else + { return dir < 0 ? range.from() : range.to() } + }, sel_move); + }), + + deleteH: methodOp(function(dir, unit) { + var sel = this.doc.sel, doc = this.doc; + if (sel.somethingSelected()) + { doc.replaceSelection("", null, "+delete"); } + else + { deleteNearSelection(this, function (range) { + var other = findPosH(doc, range.head, dir, unit, false); + return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other} + }); } + }), + + findPosV: function(from, amount, unit, goalColumn) { + var dir = 1, x = goalColumn; + if (amount < 0) { dir = -1; amount = -amount; } + var cur = clipPos(this.doc, from); + for (var i = 0; i < amount; ++i) { + var coords = cursorCoords(this, cur, "div"); + if (x == null) { x = coords.left; } + else { coords.left = x; } + cur = findPosV(this, coords, dir, unit); + if (cur.hitSide) { break } + } + return cur + }, + + moveV: methodOp(function(dir, unit) { + var this$1 = this; + + var doc = this.doc, goals = []; + var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected(); + doc.extendSelectionsBy(function (range) { + if (collapse) + { return dir < 0 ? range.from() : range.to() } + var headPos = cursorCoords(this$1, range.head, "div"); + if (range.goalColumn != null) { headPos.left = range.goalColumn; } + goals.push(headPos.left); + var pos = findPosV(this$1, headPos, dir, unit); + if (unit == "page" && range == doc.sel.primary()) + { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); } + return pos + }, sel_move); + if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++) + { doc.sel.ranges[i].goalColumn = goals[i]; } } + }), + + // Find the word at the given position (as returned by coordsChar). + findWordAt: function(pos) { + var doc = this.doc, line = getLine(doc, pos.line).text; + var start = pos.ch, end = pos.ch; + if (line) { + var helper = this.getHelper(pos, "wordChars"); + if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; } + var startChar = line.charAt(start); + var check = isWordChar(startChar, helper) + ? function (ch) { return isWordChar(ch, helper); } + : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); } + : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); }; + while (start > 0 && check(line.charAt(start - 1))) { --start; } + while (end < line.length && check(line.charAt(end))) { ++end; } + } + return new Range(Pos(pos.line, start), Pos(pos.line, end)) + }, + + toggleOverwrite: function(value) { + if (value != null && value == this.state.overwrite) { return } + if (this.state.overwrite = !this.state.overwrite) + { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); } + else + { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); } + + signal(this, "overwriteToggle", this, this.state.overwrite); + }, + hasFocus: function() { return this.display.input.getField() == activeElt(doc(this)) }, + isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) }, + + scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }), + getScrollInfo: function() { + var scroller = this.display.scroller; + return {left: scroller.scrollLeft, top: scroller.scrollTop, + height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight, + width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth, + clientHeight: displayHeight(this), clientWidth: displayWidth(this)} + }, + + scrollIntoView: methodOp(function(range, margin) { + if (range == null) { + range = {from: this.doc.sel.primary().head, to: null}; + if (margin == null) { margin = this.options.cursorScrollMargin; } + } else if (typeof range == "number") { + range = {from: Pos(range, 0), to: null}; + } else if (range.from == null) { + range = {from: range, to: null}; + } + if (!range.to) { range.to = range.from; } + range.margin = margin || 0; + + if (range.from.line != null) { + scrollToRange(this, range); + } else { + scrollToCoordsRange(this, range.from, range.to, range.margin); + } + }), + + setSize: methodOp(function(width, height) { + var this$1 = this; + + var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; }; + if (width != null) { this.display.wrapper.style.width = interpret(width); } + if (height != null) { this.display.wrapper.style.height = interpret(height); } + if (this.options.lineWrapping) { clearLineMeasurementCache(this); } + var lineNo = this.display.viewFrom; + this.doc.iter(lineNo, this.display.viewTo, function (line) { + if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) + { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo, "widget"); break } } } + ++lineNo; + }); + this.curOp.forceUpdate = true; + signal(this, "refresh", this); + }), + + operation: function(f){return runInOp(this, f)}, + startOperation: function(){return startOperation(this)}, + endOperation: function(){return endOperation(this)}, + + refresh: methodOp(function() { + var oldHeight = this.display.cachedTextHeight; + regChange(this); + this.curOp.forceUpdate = true; + clearCaches(this); + scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop); + updateGutterSpace(this.display); + if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5 || this.options.lineWrapping) + { estimateLineHeights(this); } + signal(this, "refresh", this); + }), + + swapDoc: methodOp(function(doc) { + var old = this.doc; + old.cm = null; + // Cancel the current text selection if any (#5821) + if (this.state.selectingText) { this.state.selectingText(); } + attachDoc(this, doc); + clearCaches(this); + this.display.input.reset(); + scrollToCoords(this, doc.scrollLeft, doc.scrollTop); + this.curOp.forceScroll = true; + signalLater(this, "swapDoc", this, old); + return old + }), + + phrase: function(phraseText) { + var phrases = this.options.phrases; + return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText + }, + + getInputField: function(){return this.display.input.getField()}, + getWrapperElement: function(){return this.display.wrapper}, + getScrollerElement: function(){return this.display.scroller}, + getGutterElement: function(){return this.display.gutters} + }; + eventMixin(CodeMirror); + + CodeMirror.registerHelper = function(type, name, value) { + if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; } + helpers[type][name] = value; + }; + CodeMirror.registerGlobalHelper = function(type, name, predicate, value) { + CodeMirror.registerHelper(type, name, value); + helpers[type]._global.push({pred: predicate, val: value}); + }; + } + + // Used for horizontal relative motion. Dir is -1 or 1 (left or + // right), unit can be "codepoint", "char", "column" (like char, but + // doesn't cross line boundaries), "word" (across next word), or + // "group" (to the start of next group of word or + // non-word-non-whitespace chars). The visually param controls + // whether, in right-to-left text, direction 1 means to move towards + // the next index in the string, or towards the character to the right + // of the current position. The resulting position will have a + // hitSide=true property if it reached the end of the document. + function findPosH(doc, pos, dir, unit, visually) { + var oldPos = pos; + var origDir = dir; + var lineObj = getLine(doc, pos.line); + var lineDir = visually && doc.direction == "rtl" ? -dir : dir; + function findNextLine() { + var l = pos.line + lineDir; + if (l < doc.first || l >= doc.first + doc.size) { return false } + pos = new Pos(l, pos.ch, pos.sticky); + return lineObj = getLine(doc, l) + } + function moveOnce(boundToLine) { + var next; + if (unit == "codepoint") { + var ch = lineObj.text.charCodeAt(pos.ch + (dir > 0 ? 0 : -1)); + if (isNaN(ch)) { + next = null; + } else { + var astral = dir > 0 ? ch >= 0xD800 && ch < 0xDC00 : ch >= 0xDC00 && ch < 0xDFFF; + next = new Pos(pos.line, Math.max(0, Math.min(lineObj.text.length, pos.ch + dir * (astral ? 2 : 1))), -dir); + } + } else if (visually) { + next = moveVisually(doc.cm, lineObj, pos, dir); + } else { + next = moveLogically(lineObj, pos, dir); + } + if (next == null) { + if (!boundToLine && findNextLine()) + { pos = endOfLine(visually, doc.cm, lineObj, pos.line, lineDir); } + else + { return false } + } else { + pos = next; + } + return true + } + + if (unit == "char" || unit == "codepoint") { + moveOnce(); + } else if (unit == "column") { + moveOnce(true); + } else if (unit == "word" || unit == "group") { + var sawType = null, group = unit == "group"; + var helper = doc.cm && doc.cm.getHelper(pos, "wordChars"); + for (var first = true;; first = false) { + if (dir < 0 && !moveOnce(!first)) { break } + var cur = lineObj.text.charAt(pos.ch) || "\n"; + var type = isWordChar(cur, helper) ? "w" + : group && cur == "\n" ? "n" + : !group || /\s/.test(cur) ? null + : "p"; + if (group && !first && !type) { type = "s"; } + if (sawType && sawType != type) { + if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";} + break + } + + if (type) { sawType = type; } + if (dir > 0 && !moveOnce(!first)) { break } + } + } + var result = skipAtomic(doc, pos, oldPos, origDir, true); + if (equalCursorPos(oldPos, result)) { result.hitSide = true; } + return result + } + + // For relative vertical movement. Dir may be -1 or 1. Unit can be + // "page" or "line". The resulting position will have a hitSide=true + // property if it reached the end of the document. + function findPosV(cm, pos, dir, unit) { + var doc = cm.doc, x = pos.left, y; + if (unit == "page") { + var pageSize = Math.min(cm.display.wrapper.clientHeight, win(cm).innerHeight || doc(cm).documentElement.clientHeight); + var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3); + y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount; + } else if (unit == "line") { + y = dir > 0 ? pos.bottom + 3 : pos.top - 3; + } + var target; + for (;;) { + target = coordsChar(cm, x, y); + if (!target.outside) { break } + if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break } + y += dir * 5; + } + return target + } + + // CONTENTEDITABLE INPUT STYLE + + var ContentEditableInput = function(cm) { + this.cm = cm; + this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null; + this.polling = new Delayed(); + this.composing = null; + this.gracePeriod = false; + this.readDOMTimeout = null; + }; + + ContentEditableInput.prototype.init = function (display) { + var this$1 = this; + + var input = this, cm = input.cm; + var div = input.div = display.lineDiv; + div.contentEditable = true; + disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize); + + function belongsToInput(e) { + for (var t = e.target; t; t = t.parentNode) { + if (t == div) { return true } + if (/\bCodeMirror-(?:line)?widget\b/.test(t.className)) { break } + } + return false + } + + on(div, "paste", function (e) { + if (!belongsToInput(e) || signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } + // IE doesn't fire input events, so we schedule a read for the pasted content in this way + if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); } + }); + + on(div, "compositionstart", function (e) { + this$1.composing = {data: e.data, done: false}; + }); + on(div, "compositionupdate", function (e) { + if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; } + }); + on(div, "compositionend", function (e) { + if (this$1.composing) { + if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); } + this$1.composing.done = true; + } + }); + + on(div, "touchstart", function () { return input.forceCompositionEnd(); }); + + on(div, "input", function () { + if (!this$1.composing) { this$1.readFromDOMSoon(); } + }); + + function onCopyCut(e) { + if (!belongsToInput(e) || signalDOMEvent(cm, e)) { return } + if (cm.somethingSelected()) { + setLastCopied({lineWise: false, text: cm.getSelections()}); + if (e.type == "cut") { cm.replaceSelection("", null, "cut"); } + } else if (!cm.options.lineWiseCopyCut) { + return + } else { + var ranges = copyableRanges(cm); + setLastCopied({lineWise: true, text: ranges.text}); + if (e.type == "cut") { + cm.operation(function () { + cm.setSelections(ranges.ranges, 0, sel_dontScroll); + cm.replaceSelection("", null, "cut"); + }); + } + } + if (e.clipboardData) { + e.clipboardData.clearData(); + var content = lastCopied.text.join("\n"); + // iOS exposes the clipboard API, but seems to discard content inserted into it + e.clipboardData.setData("Text", content); + if (e.clipboardData.getData("Text") == content) { + e.preventDefault(); + return + } + } + // Old-fashioned briefly-focus-a-textarea hack + var kludge = hiddenTextarea(), te = kludge.firstChild; + cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild); + te.value = lastCopied.text.join("\n"); + var hadFocus = activeElt(div.ownerDocument); + selectInput(te); + setTimeout(function () { + cm.display.lineSpace.removeChild(kludge); + hadFocus.focus(); + if (hadFocus == div) { input.showPrimarySelection(); } + }, 50); + } + on(div, "copy", onCopyCut); + on(div, "cut", onCopyCut); + }; + + ContentEditableInput.prototype.screenReaderLabelChanged = function (label) { + // Label for screenreaders, accessibility + if(label) { + this.div.setAttribute('aria-label', label); + } else { + this.div.removeAttribute('aria-label'); + } + }; + + ContentEditableInput.prototype.prepareSelection = function () { + var result = prepareSelection(this.cm, false); + result.focus = activeElt(this.div.ownerDocument) == this.div; + return result + }; + + ContentEditableInput.prototype.showSelection = function (info, takeFocus) { + if (!info || !this.cm.display.view.length) { return } + if (info.focus || takeFocus) { this.showPrimarySelection(); } + this.showMultipleSelections(info); + }; + + ContentEditableInput.prototype.getSelection = function () { + return this.cm.display.wrapper.ownerDocument.getSelection() + }; + + ContentEditableInput.prototype.showPrimarySelection = function () { + var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary(); + var from = prim.from(), to = prim.to(); + + if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) { + sel.removeAllRanges(); + return + } + + var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); + var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset); + if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad && + cmp(minPos(curAnchor, curFocus), from) == 0 && + cmp(maxPos(curAnchor, curFocus), to) == 0) + { return } + + var view = cm.display.view; + var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) || + {node: view[0].measure.map[2], offset: 0}; + var end = to.line < cm.display.viewTo && posToDOM(cm, to); + if (!end) { + var measure = view[view.length - 1].measure; + var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map; + end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]}; + } + + if (!start || !end) { + sel.removeAllRanges(); + return + } + + var old = sel.rangeCount && sel.getRangeAt(0), rng; + try { rng = range(start.node, start.offset, end.offset, end.node); } + catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible + if (rng) { + if (!gecko && cm.state.focused) { + sel.collapse(start.node, start.offset); + if (!rng.collapsed) { + sel.removeAllRanges(); + sel.addRange(rng); + } + } else { + sel.removeAllRanges(); + sel.addRange(rng); + } + if (old && sel.anchorNode == null) { sel.addRange(old); } + else if (gecko) { this.startGracePeriod(); } + } + this.rememberSelection(); + }; + + ContentEditableInput.prototype.startGracePeriod = function () { + var this$1 = this; + + clearTimeout(this.gracePeriod); + this.gracePeriod = setTimeout(function () { + this$1.gracePeriod = false; + if (this$1.selectionChanged()) + { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); } + }, 20); + }; + + ContentEditableInput.prototype.showMultipleSelections = function (info) { + removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors); + removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection); + }; + + ContentEditableInput.prototype.rememberSelection = function () { + var sel = this.getSelection(); + this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset; + this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset; + }; + + ContentEditableInput.prototype.selectionInEditor = function () { + var sel = this.getSelection(); + if (!sel.rangeCount) { return false } + var node = sel.getRangeAt(0).commonAncestorContainer; + return contains(this.div, node) + }; + + ContentEditableInput.prototype.focus = function () { + if (this.cm.options.readOnly != "nocursor") { + if (!this.selectionInEditor() || activeElt(this.div.ownerDocument) != this.div) + { this.showSelection(this.prepareSelection(), true); } + this.div.focus(); + } + }; + ContentEditableInput.prototype.blur = function () { this.div.blur(); }; + ContentEditableInput.prototype.getField = function () { return this.div }; + + ContentEditableInput.prototype.supportsTouch = function () { return true }; + + ContentEditableInput.prototype.receivedFocus = function () { + var this$1 = this; + + var input = this; + if (this.selectionInEditor()) + { setTimeout(function () { return this$1.pollSelection(); }, 20); } + else + { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); } + + function poll() { + if (input.cm.state.focused) { + input.pollSelection(); + input.polling.set(input.cm.options.pollInterval, poll); + } + } + this.polling.set(this.cm.options.pollInterval, poll); + }; + + ContentEditableInput.prototype.selectionChanged = function () { + var sel = this.getSelection(); + return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset || + sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset + }; + + ContentEditableInput.prototype.pollSelection = function () { + if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return } + var sel = this.getSelection(), cm = this.cm; + // On Android Chrome (version 56, at least), backspacing into an + // uneditable block element will put the cursor in that element, + // and then, because it's not editable, hide the virtual keyboard. + // Because Android doesn't allow us to actually detect backspace + // presses in a sane way, this code checks for when that happens + // and simulates a backspace press in this case. + if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) { + this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs}); + this.blur(); + this.focus(); + return + } + if (this.composing) { return } + this.rememberSelection(); + var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); + var head = domToPos(cm, sel.focusNode, sel.focusOffset); + if (anchor && head) { runInOp(cm, function () { + setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll); + if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; } + }); } + }; + + ContentEditableInput.prototype.pollContent = function () { + if (this.readDOMTimeout != null) { + clearTimeout(this.readDOMTimeout); + this.readDOMTimeout = null; + } + + var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary(); + var from = sel.from(), to = sel.to(); + if (from.ch == 0 && from.line > cm.firstLine()) + { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); } + if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine()) + { to = Pos(to.line + 1, 0); } + if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false } + + var fromIndex, fromLine, fromNode; + if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) { + fromLine = lineNo(display.view[0].line); + fromNode = display.view[0].node; + } else { + fromLine = lineNo(display.view[fromIndex].line); + fromNode = display.view[fromIndex - 1].node.nextSibling; + } + var toIndex = findViewIndex(cm, to.line); + var toLine, toNode; + if (toIndex == display.view.length - 1) { + toLine = display.viewTo - 1; + toNode = display.lineDiv.lastChild; + } else { + toLine = lineNo(display.view[toIndex + 1].line) - 1; + toNode = display.view[toIndex + 1].node.previousSibling; + } + + if (!fromNode) { return false } + var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine)); + var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length)); + while (newText.length > 1 && oldText.length > 1) { + if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; } + else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; } + else { break } + } + + var cutFront = 0, cutEnd = 0; + var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length); + while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront)) + { ++cutFront; } + var newBot = lst(newText), oldBot = lst(oldText); + var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0), + oldBot.length - (oldText.length == 1 ? cutFront : 0)); + while (cutEnd < maxCutEnd && + newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) + { ++cutEnd; } + // Try to move start of change to start of selection if ambiguous + if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) { + while (cutFront && cutFront > from.ch && + newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) { + cutFront--; + cutEnd++; + } + } + + newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, ""); + newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, ""); + + var chFrom = Pos(fromLine, cutFront); + var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0); + if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) { + replaceRange(cm.doc, newText, chFrom, chTo, "+input"); + return true + } + }; + + ContentEditableInput.prototype.ensurePolled = function () { + this.forceCompositionEnd(); + }; + ContentEditableInput.prototype.reset = function () { + this.forceCompositionEnd(); + }; + ContentEditableInput.prototype.forceCompositionEnd = function () { + if (!this.composing) { return } + clearTimeout(this.readDOMTimeout); + this.composing = null; + this.updateFromDOM(); + this.div.blur(); + this.div.focus(); + }; + ContentEditableInput.prototype.readFromDOMSoon = function () { + var this$1 = this; + + if (this.readDOMTimeout != null) { return } + this.readDOMTimeout = setTimeout(function () { + this$1.readDOMTimeout = null; + if (this$1.composing) { + if (this$1.composing.done) { this$1.composing = null; } + else { return } + } + this$1.updateFromDOM(); + }, 80); + }; + + ContentEditableInput.prototype.updateFromDOM = function () { + var this$1 = this; + + if (this.cm.isReadOnly() || !this.pollContent()) + { runInOp(this.cm, function () { return regChange(this$1.cm); }); } + }; + + ContentEditableInput.prototype.setUneditable = function (node) { + node.contentEditable = "false"; + }; + + ContentEditableInput.prototype.onKeyPress = function (e) { + if (e.charCode == 0 || this.composing) { return } + e.preventDefault(); + if (!this.cm.isReadOnly()) + { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); } + }; + + ContentEditableInput.prototype.readOnlyChanged = function (val) { + this.div.contentEditable = String(val != "nocursor"); + }; + + ContentEditableInput.prototype.onContextMenu = function () {}; + ContentEditableInput.prototype.resetPosition = function () {}; + + ContentEditableInput.prototype.needsContentAttribute = true; + + function posToDOM(cm, pos) { + var view = findViewForLine(cm, pos.line); + if (!view || view.hidden) { return null } + var line = getLine(cm.doc, pos.line); + var info = mapFromLineView(view, line, pos.line); + + var order = getOrder(line, cm.doc.direction), side = "left"; + if (order) { + var partPos = getBidiPartAt(order, pos.ch); + side = partPos % 2 ? "right" : "left"; + } + var result = nodeAndOffsetInLineMap(info.map, pos.ch, side); + result.offset = result.collapse == "right" ? result.end : result.start; + return result + } + + function isInGutter(node) { + for (var scan = node; scan; scan = scan.parentNode) + { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } } + return false + } + + function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos } + + function domTextBetween(cm, from, to, fromLine, toLine) { + var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false; + function recognizeMarker(id) { return function (marker) { return marker.id == id; } } + function close() { + if (closing) { + text += lineSep; + if (extraLinebreak) { text += lineSep; } + closing = extraLinebreak = false; + } + } + function addText(str) { + if (str) { + close(); + text += str; + } + } + function walk(node) { + if (node.nodeType == 1) { + var cmText = node.getAttribute("cm-text"); + if (cmText) { + addText(cmText); + return + } + var markerID = node.getAttribute("cm-marker"), range; + if (markerID) { + var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID)); + if (found.length && (range = found[0].find(0))) + { addText(getBetween(cm.doc, range.from, range.to).join(lineSep)); } + return + } + if (node.getAttribute("contenteditable") == "false") { return } + var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName); + if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return } + + if (isBlock) { close(); } + for (var i = 0; i < node.childNodes.length; i++) + { walk(node.childNodes[i]); } + + if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; } + if (isBlock) { closing = true; } + } else if (node.nodeType == 3) { + addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " ")); + } + } + for (;;) { + walk(from); + if (from == to) { break } + from = from.nextSibling; + extraLinebreak = false; + } + return text + } + + function domToPos(cm, node, offset) { + var lineNode; + if (node == cm.display.lineDiv) { + lineNode = cm.display.lineDiv.childNodes[offset]; + if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) } + node = null; offset = 0; + } else { + for (lineNode = node;; lineNode = lineNode.parentNode) { + if (!lineNode || lineNode == cm.display.lineDiv) { return null } + if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break } + } + } + for (var i = 0; i < cm.display.view.length; i++) { + var lineView = cm.display.view[i]; + if (lineView.node == lineNode) + { return locateNodeInLineView(lineView, node, offset) } + } + } + + function locateNodeInLineView(lineView, node, offset) { + var wrapper = lineView.text.firstChild, bad = false; + if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) } + if (node == wrapper) { + bad = true; + node = wrapper.childNodes[offset]; + offset = 0; + if (!node) { + var line = lineView.rest ? lst(lineView.rest) : lineView.line; + return badPos(Pos(lineNo(line), line.text.length), bad) + } + } + + var textNode = node.nodeType == 3 ? node : null, topNode = node; + if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) { + textNode = node.firstChild; + if (offset) { offset = textNode.nodeValue.length; } + } + while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; } + var measure = lineView.measure, maps = measure.maps; + + function find(textNode, topNode, offset) { + for (var i = -1; i < (maps ? maps.length : 0); i++) { + var map = i < 0 ? measure.map : maps[i]; + for (var j = 0; j < map.length; j += 3) { + var curNode = map[j + 2]; + if (curNode == textNode || curNode == topNode) { + var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]); + var ch = map[j] + offset; + if (offset < 0 || curNode != textNode) { ch = map[j + (offset ? 1 : 0)]; } + return Pos(line, ch) + } + } + } + } + var found = find(textNode, topNode, offset); + if (found) { return badPos(found, bad) } + + // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems + for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) { + found = find(after, after.firstChild, 0); + if (found) + { return badPos(Pos(found.line, found.ch - dist), bad) } + else + { dist += after.textContent.length; } + } + for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) { + found = find(before, before.firstChild, -1); + if (found) + { return badPos(Pos(found.line, found.ch + dist$1), bad) } + else + { dist$1 += before.textContent.length; } + } + } + + // TEXTAREA INPUT STYLE + + var TextareaInput = function(cm) { + this.cm = cm; + // See input.poll and input.reset + this.prevInput = ""; + + // Flag that indicates whether we expect input to appear real soon + // now (after some event like 'keypress' or 'input') and are + // polling intensively. + this.pollingFast = false; + // Self-resetting timeout for the poller + this.polling = new Delayed(); + // Used to work around IE issue with selection being forgotten when focus moves away from textarea + this.hasSelection = false; + this.composing = null; + this.resetting = false; + }; + + TextareaInput.prototype.init = function (display) { + var this$1 = this; + + var input = this, cm = this.cm; + this.createField(display); + var te = this.textarea; + + display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild); + + // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore) + if (ios) { te.style.width = "0px"; } + + on(te, "input", function () { + if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; } + input.poll(); + }); + + on(te, "paste", function (e) { + if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } + + cm.state.pasteIncoming = +new Date; + input.fastPoll(); + }); + + function prepareCopyCut(e) { + if (signalDOMEvent(cm, e)) { return } + if (cm.somethingSelected()) { + setLastCopied({lineWise: false, text: cm.getSelections()}); + } else if (!cm.options.lineWiseCopyCut) { + return + } else { + var ranges = copyableRanges(cm); + setLastCopied({lineWise: true, text: ranges.text}); + if (e.type == "cut") { + cm.setSelections(ranges.ranges, null, sel_dontScroll); + } else { + input.prevInput = ""; + te.value = ranges.text.join("\n"); + selectInput(te); + } + } + if (e.type == "cut") { cm.state.cutIncoming = +new Date; } + } + on(te, "cut", prepareCopyCut); + on(te, "copy", prepareCopyCut); + + on(display.scroller, "paste", function (e) { + if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return } + if (!te.dispatchEvent) { + cm.state.pasteIncoming = +new Date; + input.focus(); + return + } + + // Pass the `paste` event to the textarea so it's handled by its event listener. + var event = new Event("paste"); + event.clipboardData = e.clipboardData; + te.dispatchEvent(event); + }); + + // Prevent normal selection in the editor (we handle our own) + on(display.lineSpace, "selectstart", function (e) { + if (!eventInWidget(display, e)) { e_preventDefault(e); } + }); + + on(te, "compositionstart", function () { + var start = cm.getCursor("from"); + if (input.composing) { input.composing.range.clear(); } + input.composing = { + start: start, + range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"}) + }; + }); + on(te, "compositionend", function () { + if (input.composing) { + input.poll(); + input.composing.range.clear(); + input.composing = null; + } + }); + }; + + TextareaInput.prototype.createField = function (_display) { + // Wraps and hides input textarea + this.wrapper = hiddenTextarea(); + // The semihidden textarea that is focused when the editor is + // focused, and receives input. + this.textarea = this.wrapper.firstChild; + }; + + TextareaInput.prototype.screenReaderLabelChanged = function (label) { + // Label for screenreaders, accessibility + if(label) { + this.textarea.setAttribute('aria-label', label); + } else { + this.textarea.removeAttribute('aria-label'); + } + }; + + TextareaInput.prototype.prepareSelection = function () { + // Redraw the selection and/or cursor + var cm = this.cm, display = cm.display, doc = cm.doc; + var result = prepareSelection(cm); + + // Move the hidden textarea near the cursor to prevent scrolling artifacts + if (cm.options.moveInputWithCursor) { + var headPos = cursorCoords(cm, doc.sel.primary().head, "div"); + var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect(); + result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10, + headPos.top + lineOff.top - wrapOff.top)); + result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10, + headPos.left + lineOff.left - wrapOff.left)); + } + + return result + }; + + TextareaInput.prototype.showSelection = function (drawn) { + var cm = this.cm, display = cm.display; + removeChildrenAndAdd(display.cursorDiv, drawn.cursors); + removeChildrenAndAdd(display.selectionDiv, drawn.selection); + if (drawn.teTop != null) { + this.wrapper.style.top = drawn.teTop + "px"; + this.wrapper.style.left = drawn.teLeft + "px"; + } + }; + + // Reset the input to correspond to the selection (or to be empty, + // when not typing and nothing is selected) + TextareaInput.prototype.reset = function (typing) { + if (this.contextMenuPending || this.composing && typing) { return } + var cm = this.cm; + this.resetting = true; + if (cm.somethingSelected()) { + this.prevInput = ""; + var content = cm.getSelection(); + this.textarea.value = content; + if (cm.state.focused) { selectInput(this.textarea); } + if (ie && ie_version >= 9) { this.hasSelection = content; } + } else if (!typing) { + this.prevInput = this.textarea.value = ""; + if (ie && ie_version >= 9) { this.hasSelection = null; } + } + this.resetting = false; + }; + + TextareaInput.prototype.getField = function () { return this.textarea }; + + TextareaInput.prototype.supportsTouch = function () { return false }; + + TextareaInput.prototype.focus = function () { + if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt(this.textarea.ownerDocument) != this.textarea)) { + try { this.textarea.focus(); } + catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM + } + }; + + TextareaInput.prototype.blur = function () { this.textarea.blur(); }; + + TextareaInput.prototype.resetPosition = function () { + this.wrapper.style.top = this.wrapper.style.left = 0; + }; + + TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); }; + + // Poll for input changes, using the normal rate of polling. This + // runs as long as the editor is focused. + TextareaInput.prototype.slowPoll = function () { + var this$1 = this; + + if (this.pollingFast) { return } + this.polling.set(this.cm.options.pollInterval, function () { + this$1.poll(); + if (this$1.cm.state.focused) { this$1.slowPoll(); } + }); + }; + + // When an event has just come in that is likely to add or change + // something in the input textarea, we poll faster, to ensure that + // the change appears on the screen quickly. + TextareaInput.prototype.fastPoll = function () { + var missed = false, input = this; + input.pollingFast = true; + function p() { + var changed = input.poll(); + if (!changed && !missed) {missed = true; input.polling.set(60, p);} + else {input.pollingFast = false; input.slowPoll();} + } + input.polling.set(20, p); + }; + + // Read input from the textarea, and update the document to match. + // When something is selected, it is present in the textarea, and + // selected (unless it is huge, in which case a placeholder is + // used). When nothing is selected, the cursor sits after previously + // seen text (can be empty), which is stored in prevInput (we must + // not reset the textarea when typing, because that breaks IME). + TextareaInput.prototype.poll = function () { + var this$1 = this; + + var cm = this.cm, input = this.textarea, prevInput = this.prevInput; + // Since this is called a *lot*, try to bail out as cheaply as + // possible when it is clear that nothing happened. hasSelection + // will be the case when there is a lot of text in the textarea, + // in which case reading its value would be expensive. + if (this.contextMenuPending || this.resetting || !cm.state.focused || + (hasSelection(input) && !prevInput && !this.composing) || + cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq) + { return false } + + var text = input.value; + // If nothing changed, bail. + if (text == prevInput && !cm.somethingSelected()) { return false } + // Work around nonsensical selection resetting in IE9/10, and + // inexplicable appearance of private area unicode characters on + // some key combos in Mac (#2689). + if (ie && ie_version >= 9 && this.hasSelection === text || + mac && /[\uf700-\uf7ff]/.test(text)) { + cm.display.input.reset(); + return false + } + + if (cm.doc.sel == cm.display.selForContextMenu) { + var first = text.charCodeAt(0); + if (first == 0x200b && !prevInput) { prevInput = "\u200b"; } + if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") } + } + // Find the part of the input that is actually new + var same = 0, l = Math.min(prevInput.length, text.length); + while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; } + + runInOp(cm, function () { + applyTextInput(cm, text.slice(same), prevInput.length - same, + null, this$1.composing ? "*compose" : null); + + // Don't leave long text in the textarea, since it makes further polling slow + if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; } + else { this$1.prevInput = text; } + + if (this$1.composing) { + this$1.composing.range.clear(); + this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"), + {className: "CodeMirror-composing"}); + } + }); + return true + }; + + TextareaInput.prototype.ensurePolled = function () { + if (this.pollingFast && this.poll()) { this.pollingFast = false; } + }; + + TextareaInput.prototype.onKeyPress = function () { + if (ie && ie_version >= 9) { this.hasSelection = null; } + this.fastPoll(); + }; + + TextareaInput.prototype.onContextMenu = function (e) { + var input = this, cm = input.cm, display = cm.display, te = input.textarea; + if (input.contextMenuPending) { input.contextMenuPending(); } + var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop; + if (!pos || presto) { return } // Opera is difficult. + + // Reset the current text selection only if the click is done outside of the selection + // and 'resetSelectionOnContextMenu' option is true. + var reset = cm.options.resetSelectionOnContextMenu; + if (reset && cm.doc.sel.contains(pos) == -1) + { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); } + + var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText; + var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect(); + input.wrapper.style.cssText = "position: static"; + te.style.cssText = "position: absolute; width: 30px; height: 30px;\n top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; + var oldScrollY; + if (webkit) { oldScrollY = te.ownerDocument.defaultView.scrollY; } // Work around Chrome issue (#2712) + display.input.focus(); + if (webkit) { te.ownerDocument.defaultView.scrollTo(null, oldScrollY); } + display.input.reset(); + // Adds "Select all" to context menu in FF + if (!cm.somethingSelected()) { te.value = input.prevInput = " "; } + input.contextMenuPending = rehide; + display.selForContextMenu = cm.doc.sel; + clearTimeout(display.detectingSelectAll); + + // Select-all will be greyed out if there's nothing to select, so + // this adds a zero-width space so that we can later check whether + // it got selected. + function prepareSelectAllHack() { + if (te.selectionStart != null) { + var selected = cm.somethingSelected(); + var extval = "\u200b" + (selected ? te.value : ""); + te.value = "\u21da"; // Used to catch context-menu undo + te.value = extval; + input.prevInput = selected ? "" : "\u200b"; + te.selectionStart = 1; te.selectionEnd = extval.length; + // Re-set this, in case some other handler touched the + // selection in the meantime. + display.selForContextMenu = cm.doc.sel; + } + } + function rehide() { + if (input.contextMenuPending != rehide) { return } + input.contextMenuPending = false; + input.wrapper.style.cssText = oldWrapperCSS; + te.style.cssText = oldCSS; + if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); } + + // Try to detect the user choosing select-all + if (te.selectionStart != null) { + if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); } + var i = 0, poll = function () { + if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 && + te.selectionEnd > 0 && input.prevInput == "\u200b") { + operation(cm, selectAll)(cm); + } else if (i++ < 10) { + display.detectingSelectAll = setTimeout(poll, 500); + } else { + display.selForContextMenu = null; + display.input.reset(); + } + }; + display.detectingSelectAll = setTimeout(poll, 200); + } + } + + if (ie && ie_version >= 9) { prepareSelectAllHack(); } + if (captureRightClick) { + e_stop(e); + var mouseup = function () { + off(window, "mouseup", mouseup); + setTimeout(rehide, 20); + }; + on(window, "mouseup", mouseup); + } else { + setTimeout(rehide, 50); + } + }; + + TextareaInput.prototype.readOnlyChanged = function (val) { + if (!val) { this.reset(); } + this.textarea.disabled = val == "nocursor"; + this.textarea.readOnly = !!val; + }; + + TextareaInput.prototype.setUneditable = function () {}; + + TextareaInput.prototype.needsContentAttribute = false; + + function fromTextArea(textarea, options) { + options = options ? copyObj(options) : {}; + options.value = textarea.value; + if (!options.tabindex && textarea.tabIndex) + { options.tabindex = textarea.tabIndex; } + if (!options.placeholder && textarea.placeholder) + { options.placeholder = textarea.placeholder; } + // Set autofocus to true if this textarea is focused, or if it has + // autofocus and no other element is focused. + if (options.autofocus == null) { + var hasFocus = activeElt(textarea.ownerDocument); + options.autofocus = hasFocus == textarea || + textarea.getAttribute("autofocus") != null && hasFocus == document.body; + } + + function save() {textarea.value = cm.getValue();} + + var realSubmit; + if (textarea.form) { + on(textarea.form, "submit", save); + // Deplorable hack to make the submit method do the right thing. + if (!options.leaveSubmitMethodAlone) { + var form = textarea.form; + realSubmit = form.submit; + try { + var wrappedSubmit = form.submit = function () { + save(); + form.submit = realSubmit; + form.submit(); + form.submit = wrappedSubmit; + }; + } catch(e) {} + } + } + + options.finishInit = function (cm) { + cm.save = save; + cm.getTextArea = function () { return textarea; }; + cm.toTextArea = function () { + cm.toTextArea = isNaN; // Prevent this from being ran twice + save(); + textarea.parentNode.removeChild(cm.getWrapperElement()); + textarea.style.display = ""; + if (textarea.form) { + off(textarea.form, "submit", save); + if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == "function") + { textarea.form.submit = realSubmit; } + } + }; + }; + + textarea.style.display = "none"; + var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); }, + options); + return cm + } + + function addLegacyProps(CodeMirror) { + CodeMirror.off = off; + CodeMirror.on = on; + CodeMirror.wheelEventPixels = wheelEventPixels; + CodeMirror.Doc = Doc; + CodeMirror.splitLines = splitLinesAuto; + CodeMirror.countColumn = countColumn; + CodeMirror.findColumn = findColumn; + CodeMirror.isWordChar = isWordCharBasic; + CodeMirror.Pass = Pass; + CodeMirror.signal = signal; + CodeMirror.Line = Line; + CodeMirror.changeEnd = changeEnd; + CodeMirror.scrollbarModel = scrollbarModel; + CodeMirror.Pos = Pos; + CodeMirror.cmpPos = cmp; + CodeMirror.modes = modes; + CodeMirror.mimeModes = mimeModes; + CodeMirror.resolveMode = resolveMode; + CodeMirror.getMode = getMode; + CodeMirror.modeExtensions = modeExtensions; + CodeMirror.extendMode = extendMode; + CodeMirror.copyState = copyState; + CodeMirror.startState = startState; + CodeMirror.innerMode = innerMode; + CodeMirror.commands = commands; + CodeMirror.keyMap = keyMap; + CodeMirror.keyName = keyName; + CodeMirror.isModifierKey = isModifierKey; + CodeMirror.lookupKey = lookupKey; + CodeMirror.normalizeKeyMap = normalizeKeyMap; + CodeMirror.StringStream = StringStream; + CodeMirror.SharedTextMarker = SharedTextMarker; + CodeMirror.TextMarker = TextMarker; + CodeMirror.LineWidget = LineWidget; + CodeMirror.e_preventDefault = e_preventDefault; + CodeMirror.e_stopPropagation = e_stopPropagation; + CodeMirror.e_stop = e_stop; + CodeMirror.addClass = addClass; + CodeMirror.contains = contains; + CodeMirror.rmClass = rmClass; + CodeMirror.keyNames = keyNames; + } + + // EDITOR CONSTRUCTOR + + defineOptions(CodeMirror); + + addEditorMethods(CodeMirror); + + // Set up methods on CodeMirror's prototype to redirect to the editor's document. + var dontDelegate = "iter insert remove copy getEditor constructor".split(" "); + for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0) + { CodeMirror.prototype[prop] = (function(method) { + return function() {return method.apply(this.doc, arguments)} + })(Doc.prototype[prop]); } } + + eventMixin(Doc); + CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}; + + // Extra arguments are stored as the mode's dependencies, which is + // used by (legacy) mechanisms like loadmode.js to automatically + // load a mode. (Preferred mechanism is the require/define calls.) + CodeMirror.defineMode = function(name/*, mode, …*/) { + if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name; } + defineMode.apply(this, arguments); + }; + + CodeMirror.defineMIME = defineMIME; + + // Minimal default mode. + CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); }); + CodeMirror.defineMIME("text/plain", "null"); + + // EXTENSIONS + + CodeMirror.defineExtension = function (name, func) { + CodeMirror.prototype[name] = func; + }; + CodeMirror.defineDocExtension = function (name, func) { + Doc.prototype[name] = func; + }; + + CodeMirror.fromTextArea = fromTextArea; + + addLegacyProps(CodeMirror); + + CodeMirror.version = "5.65.10"; + + return CodeMirror; +}))); + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/addon/edit/matchbrackets.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + var ie_lt8 = /MSIE \d/.test(navigator.userAgent) && + (document.documentMode == null || document.documentMode < 8); + + var Pos = CodeMirror.Pos; + + var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<", "<": ">>", ">": "<<"}; + + function bracketRegex(config) { + return config && config.bracketRegex || /[(){}[\]]/ + } + + function findMatchingBracket(cm, where, config) { + var line = cm.getLineHandle(where.line), pos = where.ch - 1; + var afterCursor = config && config.afterCursor + if (afterCursor == null) + afterCursor = /(^| )cm-fat-cursor($| )/.test(cm.getWrapperElement().className) + var re = bracketRegex(config) + + // A cursor is defined as between two characters, but in in vim command mode + // (i.e. not insert mode), the cursor is visually represented as a + // highlighted box on top of the 2nd character. Otherwise, we allow matches + // from before or after the cursor. + var match = (!afterCursor && pos >= 0 && re.test(line.text.charAt(pos)) && matching[line.text.charAt(pos)]) || + re.test(line.text.charAt(pos + 1)) && matching[line.text.charAt(++pos)]; + if (!match) return null; + var dir = match.charAt(1) == ">" ? 1 : -1; + if (config && config.strict && (dir > 0) != (pos == where.ch)) return null; + var style = cm.getTokenTypeAt(Pos(where.line, pos + 1)); + + var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style, config); + if (found == null) return null; + return {from: Pos(where.line, pos), to: found && found.pos, + match: found && found.ch == match.charAt(0), forward: dir > 0}; + } + + // bracketRegex is used to specify which type of bracket to scan + // should be a regexp, e.g. /[[\]]/ + // + // Note: If "where" is on an open bracket, then this bracket is ignored. + // + // Returns false when no bracket was found, null when it reached + // maxScanLines and gave up + function scanForBracket(cm, where, dir, style, config) { + var maxScanLen = (config && config.maxScanLineLength) || 10000; + var maxScanLines = (config && config.maxScanLines) || 1000; + + var stack = []; + var re = bracketRegex(config) + var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1) + : Math.max(cm.firstLine() - 1, where.line - maxScanLines); + for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) { + var line = cm.getLine(lineNo); + if (!line) continue; + var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1; + if (line.length > maxScanLen) continue; + if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0); + for (; pos != end; pos += dir) { + var ch = line.charAt(pos); + if (re.test(ch) && (style === undefined || + (cm.getTokenTypeAt(Pos(lineNo, pos + 1)) || "") == (style || ""))) { + var match = matching[ch]; + if (match && (match.charAt(1) == ">") == (dir > 0)) stack.push(ch); + else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch}; + else stack.pop(); + } + } + } + return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null; + } + + function matchBrackets(cm, autoclear, config) { + // Disable brace matching in long lines, since it'll cause hugely slow updates + var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000, + highlightNonMatching = config && config.highlightNonMatching; + var marks = [], ranges = cm.listSelections(); + for (var i = 0; i < ranges.length; i++) { + var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, config); + if (match && (match.match || highlightNonMatching !== false) && cm.getLine(match.from.line).length <= maxHighlightLen) { + var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; + marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style})); + if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen) + marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style})); + } + } + + if (marks.length) { + // Kludge to work around the IE bug from issue #1193, where text + // input stops going to the textarea whenever this fires. + if (ie_lt8 && cm.state.focused) cm.focus(); + + var clear = function() { + cm.operation(function() { + for (var i = 0; i < marks.length; i++) marks[i].clear(); + }); + }; + if (autoclear) setTimeout(clear, 800); + else return clear; + } + } + + function doMatchBrackets(cm) { + cm.operation(function() { + if (cm.state.matchBrackets.currentlyHighlighted) { + cm.state.matchBrackets.currentlyHighlighted(); + cm.state.matchBrackets.currentlyHighlighted = null; + } + cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets); + }); + } + + function clearHighlighted(cm) { + if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) { + cm.state.matchBrackets.currentlyHighlighted(); + cm.state.matchBrackets.currentlyHighlighted = null; + } + } + + CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) { + cm.off("cursorActivity", doMatchBrackets); + cm.off("focus", doMatchBrackets) + cm.off("blur", clearHighlighted) + clearHighlighted(cm); + } + if (val) { + cm.state.matchBrackets = typeof val == "object" ? val : {}; + cm.on("cursorActivity", doMatchBrackets); + cm.on("focus", doMatchBrackets) + cm.on("blur", clearHighlighted) + } + }); + + CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);}); + CodeMirror.defineExtension("findMatchingBracket", function(pos, config, oldConfig){ + // Backwards-compatibility kludge + if (oldConfig || typeof config == "boolean") { + if (!oldConfig) { + config = config ? {strict: true} : null + } else { + oldConfig.strict = config + config = oldConfig + } + } + return findMatchingBracket(this, pos, config) + }); + CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){ + return scanForBracket(this, pos, dir, style, config); + }); +}); + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/addon/edit/trailingspace.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) { + if (prev == CodeMirror.Init) prev = false; + if (prev && !val) + cm.removeOverlay("trailingspace"); + else if (!prev && val) + cm.addOverlay({ + token: function(stream) { + for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {} + if (i > stream.pos) { stream.pos = i; return null; } + stream.pos = l; + return "trailingspace"; + }, + name: "trailingspace" + }); + }); +}); + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/addon/lint/lint.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + var GUTTER_ID = "CodeMirror-lint-markers"; + var LINT_LINE_ID = "CodeMirror-lint-line-"; + + function showTooltip(cm, e, content) { + var tt = document.createElement("div"); + tt.className = "CodeMirror-lint-tooltip cm-s-" + cm.options.theme; + tt.appendChild(content.cloneNode(true)); + if (cm.state.lint.options.selfContain) + cm.getWrapperElement().appendChild(tt); + else + document.body.appendChild(tt); + + function position(e) { + if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position); + tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + "px"; + tt.style.left = (e.clientX + 5) + "px"; + } + CodeMirror.on(document, "mousemove", position); + position(e); + if (tt.style.opacity != null) tt.style.opacity = 1; + return tt; + } + function rm(elt) { + if (elt.parentNode) elt.parentNode.removeChild(elt); + } + function hideTooltip(tt) { + if (!tt.parentNode) return; + if (tt.style.opacity == null) rm(tt); + tt.style.opacity = 0; + setTimeout(function() { rm(tt); }, 600); + } + + function showTooltipFor(cm, e, content, node) { + var tooltip = showTooltip(cm, e, content); + function hide() { + CodeMirror.off(node, "mouseout", hide); + if (tooltip) { hideTooltip(tooltip); tooltip = null; } + } + var poll = setInterval(function() { + if (tooltip) for (var n = node;; n = n.parentNode) { + if (n && n.nodeType == 11) n = n.host; + if (n == document.body) return; + if (!n) { hide(); break; } + } + if (!tooltip) return clearInterval(poll); + }, 400); + CodeMirror.on(node, "mouseout", hide); + } + + function LintState(cm, conf, hasGutter) { + this.marked = []; + if (conf instanceof Function) conf = {getAnnotations: conf}; + if (!conf || conf === true) conf = {}; + this.options = {}; + this.linterOptions = conf.options || {}; + for (var prop in defaults) this.options[prop] = defaults[prop]; + for (var prop in conf) { + if (defaults.hasOwnProperty(prop)) { + if (conf[prop] != null) this.options[prop] = conf[prop]; + } else if (!conf.options) { + this.linterOptions[prop] = conf[prop]; + } + } + this.timeout = null; + this.hasGutter = hasGutter; + this.onMouseOver = function(e) { onMouseOver(cm, e); }; + this.waitingFor = 0 + } + + var defaults = { + highlightLines: false, + tooltips: true, + delay: 500, + lintOnChange: true, + getAnnotations: null, + async: false, + selfContain: null, + formatAnnotation: null, + onUpdateLinting: null + } + + function clearMarks(cm) { + var state = cm.state.lint; + if (state.hasGutter) cm.clearGutter(GUTTER_ID); + if (state.options.highlightLines) clearErrorLines(cm); + for (var i = 0; i < state.marked.length; ++i) + state.marked[i].clear(); + state.marked.length = 0; + } + + function clearErrorLines(cm) { + cm.eachLine(function(line) { + var has = line.wrapClass && /\bCodeMirror-lint-line-\w+\b/.exec(line.wrapClass); + if (has) cm.removeLineClass(line, "wrap", has[0]); + }) + } + + function makeMarker(cm, labels, severity, multiple, tooltips) { + var marker = document.createElement("div"), inner = marker; + marker.className = "CodeMirror-lint-marker CodeMirror-lint-marker-" + severity; + if (multiple) { + inner = marker.appendChild(document.createElement("div")); + inner.className = "CodeMirror-lint-marker CodeMirror-lint-marker-multiple"; + } + + if (tooltips != false) CodeMirror.on(inner, "mouseover", function(e) { + showTooltipFor(cm, e, labels, inner); + }); + + return marker; + } + + function getMaxSeverity(a, b) { + if (a == "error") return a; + else return b; + } + + function groupByLine(annotations) { + var lines = []; + for (var i = 0; i < annotations.length; ++i) { + var ann = annotations[i], line = ann.from.line; + (lines[line] || (lines[line] = [])).push(ann); + } + return lines; + } + + function annotationTooltip(ann) { + var severity = ann.severity; + if (!severity) severity = "error"; + var tip = document.createElement("div"); + tip.className = "CodeMirror-lint-message CodeMirror-lint-message-" + severity; + if (typeof ann.messageHTML != 'undefined') { + tip.innerHTML = ann.messageHTML; + } else { + tip.appendChild(document.createTextNode(ann.message)); + } + return tip; + } + + function lintAsync(cm, getAnnotations) { + var state = cm.state.lint + var id = ++state.waitingFor + function abort() { + id = -1 + cm.off("change", abort) + } + cm.on("change", abort) + getAnnotations(cm.getValue(), function(annotations, arg2) { + cm.off("change", abort) + if (state.waitingFor != id) return + if (arg2 && annotations instanceof CodeMirror) annotations = arg2 + cm.operation(function() {updateLinting(cm, annotations)}) + }, state.linterOptions, cm); + } + + function startLinting(cm) { + var state = cm.state.lint; + if (!state) return; + var options = state.options; + /* + * Passing rules in `options` property prevents JSHint (and other linters) from complaining + * about unrecognized rules like `onUpdateLinting`, `delay`, `lintOnChange`, etc. + */ + var getAnnotations = options.getAnnotations || cm.getHelper(CodeMirror.Pos(0, 0), "lint"); + if (!getAnnotations) return; + if (options.async || getAnnotations.async) { + lintAsync(cm, getAnnotations) + } else { + var annotations = getAnnotations(cm.getValue(), state.linterOptions, cm); + if (!annotations) return; + if (annotations.then) annotations.then(function(issues) { + cm.operation(function() {updateLinting(cm, issues)}) + }); + else cm.operation(function() {updateLinting(cm, annotations)}) + } + } + + function updateLinting(cm, annotationsNotSorted) { + var state = cm.state.lint; + if (!state) return; + var options = state.options; + clearMarks(cm); + + var annotations = groupByLine(annotationsNotSorted); + + for (var line = 0; line < annotations.length; ++line) { + var anns = annotations[line]; + if (!anns) continue; + + // filter out duplicate messages + var message = []; + anns = anns.filter(function(item) { return message.indexOf(item.message) > -1 ? false : message.push(item.message) }); + + var maxSeverity = null; + var tipLabel = state.hasGutter && document.createDocumentFragment(); + + for (var i = 0; i < anns.length; ++i) { + var ann = anns[i]; + var severity = ann.severity; + if (!severity) severity = "error"; + maxSeverity = getMaxSeverity(maxSeverity, severity); + + if (options.formatAnnotation) ann = options.formatAnnotation(ann); + if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann)); + + if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, { + className: "CodeMirror-lint-mark CodeMirror-lint-mark-" + severity, + __annotation: ann + })); + } + // use original annotations[line] to show multiple messages + if (state.hasGutter) + cm.setGutterMarker(line, GUTTER_ID, makeMarker(cm, tipLabel, maxSeverity, annotations[line].length > 1, + options.tooltips)); + + if (options.highlightLines) + cm.addLineClass(line, "wrap", LINT_LINE_ID + maxSeverity); + } + if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm); + } + + function onChange(cm) { + var state = cm.state.lint; + if (!state) return; + clearTimeout(state.timeout); + state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay); + } + + function popupTooltips(cm, annotations, e) { + var target = e.target || e.srcElement; + var tooltip = document.createDocumentFragment(); + for (var i = 0; i < annotations.length; i++) { + var ann = annotations[i]; + tooltip.appendChild(annotationTooltip(ann)); + } + showTooltipFor(cm, e, tooltip, target); + } + + function onMouseOver(cm, e) { + var target = e.target || e.srcElement; + if (!/\bCodeMirror-lint-mark-/.test(target.className)) return; + var box = target.getBoundingClientRect(), x = (box.left + box.right) / 2, y = (box.top + box.bottom) / 2; + var spans = cm.findMarksAt(cm.coordsChar({left: x, top: y}, "client")); + + var annotations = []; + for (var i = 0; i < spans.length; ++i) { + var ann = spans[i].__annotation; + if (ann) annotations.push(ann); + } + if (annotations.length) popupTooltips(cm, annotations, e); + } + + CodeMirror.defineOption("lint", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) { + clearMarks(cm); + if (cm.state.lint.options.lintOnChange !== false) + cm.off("change", onChange); + CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver); + clearTimeout(cm.state.lint.timeout); + delete cm.state.lint; + } + + if (val) { + var gutters = cm.getOption("gutters"), hasLintGutter = false; + for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true; + var state = cm.state.lint = new LintState(cm, val, hasLintGutter); + if (state.options.lintOnChange) + cm.on("change", onChange); + if (state.options.tooltips != false && state.options.tooltips != "gutter") + CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver); + + startLinting(cm); + } + }); + + CodeMirror.defineExtension("performLint", function() { + startLinting(this); + }); +}); + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/addon/selection/active-line.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + var WRAP_CLASS = "CodeMirror-activeline"; + var BACK_CLASS = "CodeMirror-activeline-background"; + var GUTT_CLASS = "CodeMirror-activeline-gutter"; + + CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) { + var prev = old == CodeMirror.Init ? false : old; + if (val == prev) return + if (prev) { + cm.off("beforeSelectionChange", selectionChange); + clearActiveLines(cm); + delete cm.state.activeLines; + } + if (val) { + cm.state.activeLines = []; + updateActiveLines(cm, cm.listSelections()); + cm.on("beforeSelectionChange", selectionChange); + } + }); + + function clearActiveLines(cm) { + for (var i = 0; i < cm.state.activeLines.length; i++) { + cm.removeLineClass(cm.state.activeLines[i], "wrap", WRAP_CLASS); + cm.removeLineClass(cm.state.activeLines[i], "background", BACK_CLASS); + cm.removeLineClass(cm.state.activeLines[i], "gutter", GUTT_CLASS); + } + } + + function sameArray(a, b) { + if (a.length != b.length) return false; + for (var i = 0; i < a.length; i++) + if (a[i] != b[i]) return false; + return true; + } + + function updateActiveLines(cm, ranges) { + var active = []; + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i]; + var option = cm.getOption("styleActiveLine"); + if (typeof option == "object" && option.nonEmpty ? range.anchor.line != range.head.line : !range.empty()) + continue + var line = cm.getLineHandleVisualStart(range.head.line); + if (active[active.length - 1] != line) active.push(line); + } + if (sameArray(cm.state.activeLines, active)) return; + cm.operation(function() { + clearActiveLines(cm); + for (var i = 0; i < active.length; i++) { + cm.addLineClass(active[i], "wrap", WRAP_CLASS); + cm.addLineClass(active[i], "background", BACK_CLASS); + cm.addLineClass(active[i], "gutter", GUTT_CLASS); + } + cm.state.activeLines = active; + }); + } + + function selectionChange(cm, sel) { + updateActiveLines(cm, sel.ranges); + } +}); + + +/* +file https://github.com/codemirror/codemirror5/blob/5.65.10/mode/javascript/javascript.js +*/ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("javascript", function(config, parserConfig) { + var indentUnit = config.indentUnit; + var statementIndent = parserConfig.statementIndent; + var jsonldMode = parserConfig.jsonld; + var jsonMode = parserConfig.json || jsonldMode; + var trackScope = parserConfig.trackScope !== false + var isTS = parserConfig.typescript; + var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/; + + // Tokenizer + + var keywords = function(){ + function kw(type) {return {type: type, style: "keyword"};} + var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d"); + var operator = kw("operator"), atom = {type: "atom", style: "atom"}; + + return { + "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, + "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C, + "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"), + "function": kw("function"), "catch": kw("catch"), + "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"), + "in": operator, "typeof": operator, "instanceof": operator, + "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom, + "this": kw("this"), "class": kw("class"), "super": kw("atom"), + "yield": C, "export": kw("export"), "import": kw("import"), "extends": C, + "await": C + }; + }(); + + var isOperatorChar = /[+\-*&%=<>!?|~^@]/; + var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/; + + function readRegexp(stream) { + var escaped = false, next, inSet = false; + while ((next = stream.next()) != null) { + if (!escaped) { + if (next == "/" && !inSet) return; + if (next == "[") inSet = true; + else if (inSet && next == "]") inSet = false; + } + escaped = !escaped && next == "\\"; + } + } + + // Used as scratch variables to communicate multiple values without + // consing up tons of objects. + var type, content; + function ret(tp, style, cont) { + type = tp; content = cont; + return style; + } + function tokenBase(stream, state) { + var ch = stream.next(); + if (ch == '"' || ch == "'") { + state.tokenize = tokenString(ch); + return state.tokenize(stream, state); + } else if (ch == "." && stream.match(/^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/)) { + return ret("number", "number"); + } else if (ch == "." && stream.match("..")) { + return ret("spread", "meta"); + } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) { + return ret(ch); + } else if (ch == "=" && stream.eat(">")) { + return ret("=>", "operator"); + } else if (ch == "0" && stream.match(/^(?:x[\dA-Fa-f_]+|o[0-7_]+|b[01_]+)n?/)) { + return ret("number", "number"); + } else if (/\d/.test(ch)) { + stream.match(/^[\d_]*(?:n|(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)?/); + return ret("number", "number"); + } else if (ch == "/") { + if (stream.eat("*")) { + state.tokenize = tokenComment; + return tokenComment(stream, state); + } else if (stream.eat("/")) { + stream.skipToEnd(); + return ret("comment", "comment"); + } else if (expressionAllowed(stream, state, 1)) { + readRegexp(stream); + stream.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/); + return ret("regexp", "string-2"); + } else { + stream.eat("="); + return ret("operator", "operator", stream.current()); + } + } else if (ch == "`") { + state.tokenize = tokenQuasi; + return tokenQuasi(stream, state); + } else if (ch == "#" && stream.peek() == "!") { + stream.skipToEnd(); + return ret("meta", "meta"); + } else if (ch == "#" && stream.eatWhile(wordRE)) { + return ret("variable", "property") + } else if (ch == "<" && stream.match("!--") || + (ch == "-" && stream.match("->") && !/\S/.test(stream.string.slice(0, stream.start)))) { + stream.skipToEnd() + return ret("comment", "comment") + } else if (isOperatorChar.test(ch)) { + if (ch != ">" || !state.lexical || state.lexical.type != ">") { + if (stream.eat("=")) { + if (ch == "!" || ch == "=") stream.eat("=") + } else if (/[<>*+\-|&?]/.test(ch)) { + stream.eat(ch) + if (ch == ">") stream.eat(ch) + } + } + if (ch == "?" && stream.eat(".")) return ret(".") + return ret("operator", "operator", stream.current()); + } else if (wordRE.test(ch)) { + stream.eatWhile(wordRE); + var word = stream.current() + if (state.lastType != ".") { + if (keywords.propertyIsEnumerable(word)) { + var kw = keywords[word] + return ret(kw.type, kw.style, word) + } + if (word == "async" && stream.match(/^(\s|\/\*([^*]|\*(?!\/))*?\*\/)*[\[\(\w]/, false)) + return ret("async", "keyword", word) + } + return ret("variable", "variable", word) + } + } + + function tokenString(quote) { + return function(stream, state) { + var escaped = false, next; + if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){ + state.tokenize = tokenBase; + return ret("jsonld-keyword", "meta"); + } + while ((next = stream.next()) != null) { + if (next == quote && !escaped) break; + escaped = !escaped && next == "\\"; + } + if (!escaped) state.tokenize = tokenBase; + return ret("string", "string"); + }; + } + + function tokenComment(stream, state) { + var maybeEnd = false, ch; + while (ch = stream.next()) { + if (ch == "/" && maybeEnd) { + state.tokenize = tokenBase; + break; + } + maybeEnd = (ch == "*"); + } + return ret("comment", "comment"); + } + + function tokenQuasi(stream, state) { + var escaped = false, next; + while ((next = stream.next()) != null) { + if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) { + state.tokenize = tokenBase; + break; + } + escaped = !escaped && next == "\\"; + } + return ret("quasi", "string-2", stream.current()); + } + + var brackets = "([{}])"; + // This is a crude lookahead trick to try and notice that we're + // parsing the argument patterns for a fat-arrow function before we + // actually hit the arrow token. It only works if the arrow is on + // the same line as the arguments and there's no strange noise + // (comments) in between. Fallback is to only notice when we hit the + // arrow, and not declare the arguments as locals for the arrow + // body. + function findFatArrow(stream, state) { + if (state.fatArrowAt) state.fatArrowAt = null; + var arrow = stream.string.indexOf("=>", stream.start); + if (arrow < 0) return; + + if (isTS) { // Try to skip TypeScript return type declarations after the arguments + var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow)) + if (m) arrow = m.index + } + + var depth = 0, sawSomething = false; + for (var pos = arrow - 1; pos >= 0; --pos) { + var ch = stream.string.charAt(pos); + var bracket = brackets.indexOf(ch); + if (bracket >= 0 && bracket < 3) { + if (!depth) { ++pos; break; } + if (--depth == 0) { if (ch == "(") sawSomething = true; break; } + } else if (bracket >= 3 && bracket < 6) { + ++depth; + } else if (wordRE.test(ch)) { + sawSomething = true; + } else if (/["'\/`]/.test(ch)) { + for (;; --pos) { + if (pos == 0) return + var next = stream.string.charAt(pos - 1) + if (next == ch && stream.string.charAt(pos - 2) != "\\") { pos--; break } + } + } else if (sawSomething && !depth) { + ++pos; + break; + } + } + if (sawSomething && !depth) state.fatArrowAt = pos; + } + + // Parser + + var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, + "regexp": true, "this": true, "import": true, "jsonld-keyword": true}; + + function JSLexical(indented, column, type, align, prev, info) { + this.indented = indented; + this.column = column; + this.type = type; + this.prev = prev; + this.info = info; + if (align != null) this.align = align; + } + + function inScope(state, varname) { + if (!trackScope) return false + for (var v = state.localVars; v; v = v.next) + if (v.name == varname) return true; + for (var cx = state.context; cx; cx = cx.prev) { + for (var v = cx.vars; v; v = v.next) + if (v.name == varname) return true; + } + } + + function parseJS(state, style, type, content, stream) { + var cc = state.cc; + // Communicate our context to the combinators. + // (Less wasteful than consing up a hundred closures on every call.) + cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style; + + if (!state.lexical.hasOwnProperty("align")) + state.lexical.align = true; + + while(true) { + var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement; + if (combinator(type, content)) { + while(cc.length && cc[cc.length - 1].lex) + cc.pop()(); + if (cx.marked) return cx.marked; + if (type == "variable" && inScope(state, content)) return "variable-2"; + return style; + } + } + } + + // Combinator utils + + var cx = {state: null, column: null, marked: null, cc: null}; + function pass() { + for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]); + } + function cont() { + pass.apply(null, arguments); + return true; + } + function inList(name, list) { + for (var v = list; v; v = v.next) if (v.name == name) return true + return false; + } + function register(varname) { + var state = cx.state; + cx.marked = "def"; + if (!trackScope) return + if (state.context) { + if (state.lexical.info == "var" && state.context && state.context.block) { + // FIXME function decls are also not block scoped + var newContext = registerVarScoped(varname, state.context) + if (newContext != null) { + state.context = newContext + return + } + } else if (!inList(varname, state.localVars)) { + state.localVars = new Var(varname, state.localVars) + return + } + } + // Fall through means this is global + if (parserConfig.globalVars && !inList(varname, state.globalVars)) + state.globalVars = new Var(varname, state.globalVars) + } + function registerVarScoped(varname, context) { + if (!context) { + return null + } else if (context.block) { + var inner = registerVarScoped(varname, context.prev) + if (!inner) return null + if (inner == context.prev) return context + return new Context(inner, context.vars, true) + } else if (inList(varname, context.vars)) { + return context + } else { + return new Context(context.prev, new Var(varname, context.vars), false) + } + } + + function isModifier(name) { + return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly" + } + + // Combinators + + function Context(prev, vars, block) { this.prev = prev; this.vars = vars; this.block = block } + function Var(name, next) { this.name = name; this.next = next } + + var defaultVars = new Var("this", new Var("arguments", null)) + function pushcontext() { + cx.state.context = new Context(cx.state.context, cx.state.localVars, false) + cx.state.localVars = defaultVars + } + function pushblockcontext() { + cx.state.context = new Context(cx.state.context, cx.state.localVars, true) + cx.state.localVars = null + } + pushcontext.lex = pushblockcontext.lex = true + function popcontext() { + cx.state.localVars = cx.state.context.vars + cx.state.context = cx.state.context.prev + } + popcontext.lex = true + function pushlex(type, info) { + var result = function() { + var state = cx.state, indent = state.indented; + if (state.lexical.type == "stat") indent = state.lexical.indented; + else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev) + indent = outer.indented; + state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info); + }; + result.lex = true; + return result; + } + function poplex() { + var state = cx.state; + if (state.lexical.prev) { + if (state.lexical.type == ")") + state.indented = state.lexical.indented; + state.lexical = state.lexical.prev; + } + } + poplex.lex = true; + + function expect(wanted) { + function exp(type) { + if (type == wanted) return cont(); + else if (wanted == ";" || type == "}" || type == ")" || type == "]") return pass(); + else return cont(exp); + }; + return exp; + } + + function statement(type, value) { + if (type == "var") return cont(pushlex("vardef", value), vardef, expect(";"), poplex); + if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex); + if (type == "keyword b") return cont(pushlex("form"), statement, poplex); + if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex); + if (type == "debugger") return cont(expect(";")); + if (type == "{") return cont(pushlex("}"), pushblockcontext, block, poplex, popcontext); + if (type == ";") return cont(); + if (type == "if") { + if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex) + cx.state.cc.pop()(); + return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse); + } + if (type == "function") return cont(functiondef); + if (type == "for") return cont(pushlex("form"), pushblockcontext, forspec, statement, popcontext, poplex); + if (type == "class" || (isTS && value == "interface")) { + cx.marked = "keyword" + return cont(pushlex("form", type == "class" ? type : value), className, poplex) + } + if (type == "variable") { + if (isTS && value == "declare") { + cx.marked = "keyword" + return cont(statement) + } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) { + cx.marked = "keyword" + if (value == "enum") return cont(enumdef); + else if (value == "type") return cont(typename, expect("operator"), typeexpr, expect(";")); + else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex) + } else if (isTS && value == "namespace") { + cx.marked = "keyword" + return cont(pushlex("form"), expression, statement, poplex) + } else if (isTS && value == "abstract") { + cx.marked = "keyword" + return cont(statement) + } else { + return cont(pushlex("stat"), maybelabel); + } + } + if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"), pushblockcontext, + block, poplex, poplex, popcontext); + if (type == "case") return cont(expression, expect(":")); + if (type == "default") return cont(expect(":")); + if (type == "catch") return cont(pushlex("form"), pushcontext, maybeCatchBinding, statement, poplex, popcontext); + if (type == "export") return cont(pushlex("stat"), afterExport, poplex); + if (type == "import") return cont(pushlex("stat"), afterImport, poplex); + if (type == "async") return cont(statement) + if (value == "@") return cont(expression, statement) + return pass(pushlex("stat"), expression, expect(";"), poplex); + } + function maybeCatchBinding(type) { + if (type == "(") return cont(funarg, expect(")")) + } + function expression(type, value) { + return expressionInner(type, value, false); + } + function expressionNoComma(type, value) { + return expressionInner(type, value, true); + } + function parenExpr(type) { + if (type != "(") return pass() + return cont(pushlex(")"), maybeexpression, expect(")"), poplex) + } + function expressionInner(type, value, noComma) { + if (cx.state.fatArrowAt == cx.stream.start) { + var body = noComma ? arrowBodyNoComma : arrowBody; + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext); + else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext); + } + + var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma; + if (atomicTypes.hasOwnProperty(type)) return cont(maybeop); + if (type == "function") return cont(functiondef, maybeop); + if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); } + if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression); + if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop); + if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression); + if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop); + if (type == "{") return contCommasep(objprop, "}", null, maybeop); + if (type == "quasi") return pass(quasi, maybeop); + if (type == "new") return cont(maybeTarget(noComma)); + return cont(); + } + function maybeexpression(type) { + if (type.match(/[;\}\)\],]/)) return pass(); + return pass(expression); + } + + function maybeoperatorComma(type, value) { + if (type == ",") return cont(maybeexpression); + return maybeoperatorNoComma(type, value, false); + } + function maybeoperatorNoComma(type, value, noComma) { + var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma; + var expr = noComma == false ? expression : expressionNoComma; + if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext); + if (type == "operator") { + if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me); + if (isTS && value == "<" && cx.stream.match(/^([^<>]|<[^<>]*>)*>\s*\(/, false)) + return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me); + if (value == "?") return cont(expression, expect(":"), expr); + return cont(expr); + } + if (type == "quasi") { return pass(quasi, me); } + if (type == ";") return; + if (type == "(") return contCommasep(expressionNoComma, ")", "call", me); + if (type == ".") return cont(property, me); + if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me); + if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) } + if (type == "regexp") { + cx.state.lastType = cx.marked = "operator" + cx.stream.backUp(cx.stream.pos - cx.stream.start - 1) + return cont(expr) + } + } + function quasi(type, value) { + if (type != "quasi") return pass(); + if (value.slice(value.length - 2) != "${") return cont(quasi); + return cont(maybeexpression, continueQuasi); + } + function continueQuasi(type) { + if (type == "}") { + cx.marked = "string-2"; + cx.state.tokenize = tokenQuasi; + return cont(quasi); + } + } + function arrowBody(type) { + findFatArrow(cx.stream, cx.state); + return pass(type == "{" ? statement : expression); + } + function arrowBodyNoComma(type) { + findFatArrow(cx.stream, cx.state); + return pass(type == "{" ? statement : expressionNoComma); + } + function maybeTarget(noComma) { + return function(type) { + if (type == ".") return cont(noComma ? targetNoComma : target); + else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma) + else return pass(noComma ? expressionNoComma : expression); + }; + } + function target(_, value) { + if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); } + } + function targetNoComma(_, value) { + if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); } + } + function maybelabel(type) { + if (type == ":") return cont(poplex, statement); + return pass(maybeoperatorComma, expect(";"), poplex); + } + function property(type) { + if (type == "variable") {cx.marked = "property"; return cont();} + } + function objprop(type, value) { + if (type == "async") { + cx.marked = "property"; + return cont(objprop); + } else if (type == "variable" || cx.style == "keyword") { + cx.marked = "property"; + if (value == "get" || value == "set") return cont(getterSetter); + var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params + if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false))) + cx.state.fatArrowAt = cx.stream.pos + m[0].length + return cont(afterprop); + } else if (type == "number" || type == "string") { + cx.marked = jsonldMode ? "property" : (cx.style + " property"); + return cont(afterprop); + } else if (type == "jsonld-keyword") { + return cont(afterprop); + } else if (isTS && isModifier(value)) { + cx.marked = "keyword" + return cont(objprop) + } else if (type == "[") { + return cont(expression, maybetype, expect("]"), afterprop); + } else if (type == "spread") { + return cont(expressionNoComma, afterprop); + } else if (value == "*") { + cx.marked = "keyword"; + return cont(objprop); + } else if (type == ":") { + return pass(afterprop) + } + } + function getterSetter(type) { + if (type != "variable") return pass(afterprop); + cx.marked = "property"; + return cont(functiondef); + } + function afterprop(type) { + if (type == ":") return cont(expressionNoComma); + if (type == "(") return pass(functiondef); + } + function commasep(what, end, sep) { + function proceed(type, value) { + if (sep ? sep.indexOf(type) > -1 : type == ",") { + var lex = cx.state.lexical; + if (lex.info == "call") lex.pos = (lex.pos || 0) + 1; + return cont(function(type, value) { + if (type == end || value == end) return pass() + return pass(what) + }, proceed); + } + if (type == end || value == end) return cont(); + if (sep && sep.indexOf(";") > -1) return pass(what) + return cont(expect(end)); + } + return function(type, value) { + if (type == end || value == end) return cont(); + return pass(what, proceed); + }; + } + function contCommasep(what, end, info) { + for (var i = 3; i < arguments.length; i++) + cx.cc.push(arguments[i]); + return cont(pushlex(end, info), commasep(what, end), poplex); + } + function block(type) { + if (type == "}") return cont(); + return pass(statement, block); + } + function maybetype(type, value) { + if (isTS) { + if (type == ":") return cont(typeexpr); + if (value == "?") return cont(maybetype); + } + } + function maybetypeOrIn(type, value) { + if (isTS && (type == ":" || value == "in")) return cont(typeexpr) + } + function mayberettype(type) { + if (isTS && type == ":") { + if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr) + else return cont(typeexpr) + } + } + function isKW(_, value) { + if (value == "is") { + cx.marked = "keyword" + return cont() + } + } + function typeexpr(type, value) { + if (value == "keyof" || value == "typeof" || value == "infer" || value == "readonly") { + cx.marked = "keyword" + return cont(value == "typeof" ? expressionNoComma : typeexpr) + } + if (type == "variable" || value == "void") { + cx.marked = "type" + return cont(afterType) + } + if (value == "|" || value == "&") return cont(typeexpr) + if (type == "string" || type == "number" || type == "atom") return cont(afterType); + if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType) + if (type == "{") return cont(pushlex("}"), typeprops, poplex, afterType) + if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType, afterType) + if (type == "<") return cont(commasep(typeexpr, ">"), typeexpr) + if (type == "quasi") { return pass(quasiType, afterType); } + } + function maybeReturnType(type) { + if (type == "=>") return cont(typeexpr) + } + function typeprops(type) { + if (type.match(/[\}\)\]]/)) return cont() + if (type == "," || type == ";") return cont(typeprops) + return pass(typeprop, typeprops) + } + function typeprop(type, value) { + if (type == "variable" || cx.style == "keyword") { + cx.marked = "property" + return cont(typeprop) + } else if (value == "?" || type == "number" || type == "string") { + return cont(typeprop) + } else if (type == ":") { + return cont(typeexpr) + } else if (type == "[") { + return cont(expect("variable"), maybetypeOrIn, expect("]"), typeprop) + } else if (type == "(") { + return pass(functiondecl, typeprop) + } else if (!type.match(/[;\}\)\],]/)) { + return cont() + } + } + function quasiType(type, value) { + if (type != "quasi") return pass(); + if (value.slice(value.length - 2) != "${") return cont(quasiType); + return cont(typeexpr, continueQuasiType); + } + function continueQuasiType(type) { + if (type == "}") { + cx.marked = "string-2"; + cx.state.tokenize = tokenQuasi; + return cont(quasiType); + } + } + function typearg(type, value) { + if (type == "variable" && cx.stream.match(/^\s*[?:]/, false) || value == "?") return cont(typearg) + if (type == ":") return cont(typeexpr) + if (type == "spread") return cont(typearg) + return pass(typeexpr) + } + function afterType(type, value) { + if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) + if (value == "|" || type == "." || value == "&") return cont(typeexpr) + if (type == "[") return cont(typeexpr, expect("]"), afterType) + if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) } + if (value == "?") return cont(typeexpr, expect(":"), typeexpr) + } + function maybeTypeArgs(_, value) { + if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) + } + function typeparam() { + return pass(typeexpr, maybeTypeDefault) + } + function maybeTypeDefault(_, value) { + if (value == "=") return cont(typeexpr) + } + function vardef(_, value) { + if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)} + return pass(pattern, maybetype, maybeAssign, vardefCont); + } + function pattern(type, value) { + if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) } + if (type == "variable") { register(value); return cont(); } + if (type == "spread") return cont(pattern); + if (type == "[") return contCommasep(eltpattern, "]"); + if (type == "{") return contCommasep(proppattern, "}"); + } + function proppattern(type, value) { + if (type == "variable" && !cx.stream.match(/^\s*:/, false)) { + register(value); + return cont(maybeAssign); + } + if (type == "variable") cx.marked = "property"; + if (type == "spread") return cont(pattern); + if (type == "}") return pass(); + if (type == "[") return cont(expression, expect(']'), expect(':'), proppattern); + return cont(expect(":"), pattern, maybeAssign); + } + function eltpattern() { + return pass(pattern, maybeAssign) + } + function maybeAssign(_type, value) { + if (value == "=") return cont(expressionNoComma); + } + function vardefCont(type) { + if (type == ",") return cont(vardef); + } + function maybeelse(type, value) { + if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex); + } + function forspec(type, value) { + if (value == "await") return cont(forspec); + if (type == "(") return cont(pushlex(")"), forspec1, poplex); + } + function forspec1(type) { + if (type == "var") return cont(vardef, forspec2); + if (type == "variable") return cont(forspec2); + return pass(forspec2) + } + function forspec2(type, value) { + if (type == ")") return cont() + if (type == ";") return cont(forspec2) + if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression, forspec2) } + return pass(expression, forspec2) + } + function functiondef(type, value) { + if (value == "*") {cx.marked = "keyword"; return cont(functiondef);} + if (type == "variable") {register(value); return cont(functiondef);} + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext); + if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef) + } + function functiondecl(type, value) { + if (value == "*") {cx.marked = "keyword"; return cont(functiondecl);} + if (type == "variable") {register(value); return cont(functiondecl);} + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, popcontext); + if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondecl) + } + function typename(type, value) { + if (type == "keyword" || type == "variable") { + cx.marked = "type" + return cont(typename) + } else if (value == "<") { + return cont(pushlex(">"), commasep(typeparam, ">"), poplex) + } + } + function funarg(type, value) { + if (value == "@") cont(expression, funarg) + if (type == "spread") return cont(funarg); + if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); } + if (isTS && type == "this") return cont(maybetype, maybeAssign) + return pass(pattern, maybetype, maybeAssign); + } + function classExpression(type, value) { + // Class expressions may have an optional name. + if (type == "variable") return className(type, value); + return classNameAfter(type, value); + } + function className(type, value) { + if (type == "variable") {register(value); return cont(classNameAfter);} + } + function classNameAfter(type, value) { + if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter) + if (value == "extends" || value == "implements" || (isTS && type == ",")) { + if (value == "implements") cx.marked = "keyword"; + return cont(isTS ? typeexpr : expression, classNameAfter); + } + if (type == "{") return cont(pushlex("}"), classBody, poplex); + } + function classBody(type, value) { + if (type == "async" || + (type == "variable" && + (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) && + cx.stream.match(/^\s+#?[\w$\xa1-\uffff]/, false))) { + cx.marked = "keyword"; + return cont(classBody); + } + if (type == "variable" || cx.style == "keyword") { + cx.marked = "property"; + return cont(classfield, classBody); + } + if (type == "number" || type == "string") return cont(classfield, classBody); + if (type == "[") + return cont(expression, maybetype, expect("]"), classfield, classBody) + if (value == "*") { + cx.marked = "keyword"; + return cont(classBody); + } + if (isTS && type == "(") return pass(functiondecl, classBody) + if (type == ";" || type == ",") return cont(classBody); + if (type == "}") return cont(); + if (value == "@") return cont(expression, classBody) + } + function classfield(type, value) { + if (value == "!") return cont(classfield) + if (value == "?") return cont(classfield) + if (type == ":") return cont(typeexpr, maybeAssign) + if (value == "=") return cont(expressionNoComma) + var context = cx.state.lexical.prev, isInterface = context && context.info == "interface" + return pass(isInterface ? functiondecl : functiondef) + } + function afterExport(type, value) { + if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); } + if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); } + if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";")); + return pass(statement); + } + function exportField(type, value) { + if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); } + if (type == "variable") return pass(expressionNoComma, exportField); + } + function afterImport(type) { + if (type == "string") return cont(); + if (type == "(") return pass(expression); + if (type == ".") return pass(maybeoperatorComma); + return pass(importSpec, maybeMoreImports, maybeFrom); + } + function importSpec(type, value) { + if (type == "{") return contCommasep(importSpec, "}"); + if (type == "variable") register(value); + if (value == "*") cx.marked = "keyword"; + return cont(maybeAs); + } + function maybeMoreImports(type) { + if (type == ",") return cont(importSpec, maybeMoreImports) + } + function maybeAs(_type, value) { + if (value == "as") { cx.marked = "keyword"; return cont(importSpec); } + } + function maybeFrom(_type, value) { + if (value == "from") { cx.marked = "keyword"; return cont(expression); } + } + function arrayLiteral(type) { + if (type == "]") return cont(); + return pass(commasep(expressionNoComma, "]")); + } + function enumdef() { + return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex) + } + function enummember() { + return pass(pattern, maybeAssign); + } + + function isContinuedStatement(state, textAfter) { + return state.lastType == "operator" || state.lastType == "," || + isOperatorChar.test(textAfter.charAt(0)) || + /[,.]/.test(textAfter.charAt(0)); + } + + function expressionAllowed(stream, state, backUp) { + return state.tokenize == tokenBase && + /^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) || + (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0)))) + } + + // Interface + + return { + startState: function(basecolumn) { + var state = { + tokenize: tokenBase, + lastType: "sof", + cc: [], + lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false), + localVars: parserConfig.localVars, + context: parserConfig.localVars && new Context(null, null, false), + indented: basecolumn || 0 + }; + if (parserConfig.globalVars && typeof parserConfig.globalVars == "object") + state.globalVars = parserConfig.globalVars; + return state; + }, + + token: function(stream, state) { + if (stream.sol()) { + if (!state.lexical.hasOwnProperty("align")) + state.lexical.align = false; + state.indented = stream.indentation(); + findFatArrow(stream, state); + } + if (state.tokenize != tokenComment && stream.eatSpace()) return null; + var style = state.tokenize(stream, state); + if (type == "comment") return style; + state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type; + return parseJS(state, style, type, content, stream); + }, + + indent: function(state, textAfter) { + if (state.tokenize == tokenComment || state.tokenize == tokenQuasi) return CodeMirror.Pass; + if (state.tokenize != tokenBase) return 0; + var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top + // Kludge to prevent 'maybelse' from blocking lexical scope pops + if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) { + var c = state.cc[i]; + if (c == poplex) lexical = lexical.prev; + else if (c != maybeelse && c != popcontext) break; + } + while ((lexical.type == "stat" || lexical.type == "form") && + (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) && + (top == maybeoperatorComma || top == maybeoperatorNoComma) && + !/^[,\.=+\-*:?[\(]/.test(textAfter)))) + lexical = lexical.prev; + if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat") + lexical = lexical.prev; + var type = lexical.type, closing = firstChar == type; + + if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info.length + 1 : 0); + else if (type == "form" && firstChar == "{") return lexical.indented; + else if (type == "form") return lexical.indented + indentUnit; + else if (type == "stat") + return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0); + else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false) + return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit); + else if (lexical.align) return lexical.column + (closing ? 0 : 1); + else return lexical.indented + (closing ? 0 : indentUnit); + }, + + electricInput: /^\s*(?:case .*?:|default:|\{|\})$/, + blockCommentStart: jsonMode ? null : "/*", + blockCommentEnd: jsonMode ? null : "*/", + blockCommentContinue: jsonMode ? null : " * ", + lineComment: jsonMode ? null : "//", + fold: "brace", + closeBrackets: "()[]{}''\"\"``", + + helperType: jsonMode ? "json" : "javascript", + jsonldMode: jsonldMode, + jsonMode: jsonMode, + + expressionAllowed: expressionAllowed, + + skipExpression: function(state) { + parseJS(state, "atom", "atom", "true", new CodeMirror.StringStream("", 2, null)) + } + }; +}); + +CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/); + +CodeMirror.defineMIME("text/javascript", "javascript"); +CodeMirror.defineMIME("text/ecmascript", "javascript"); +CodeMirror.defineMIME("application/javascript", "javascript"); +CodeMirror.defineMIME("application/x-javascript", "javascript"); +CodeMirror.defineMIME("application/ecmascript", "javascript"); +CodeMirror.defineMIME("application/json", { name: "javascript", json: true }); +CodeMirror.defineMIME("application/x-json", { name: "javascript", json: true }); +CodeMirror.defineMIME("application/manifest+json", { name: "javascript", json: true }) +CodeMirror.defineMIME("application/ld+json", { name: "javascript", jsonld: true }); +CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true }); +CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true }); +}); + + +/* +file none +*/ +/*jslint-enable*/ diff --git a/branch-master/asset_font_daley_bold.woff2 b/branch-master/asset_font_daley_bold.woff2 new file mode 100644 index 000000000..783bdd697 Binary files /dev/null and b/branch-master/asset_font_daley_bold.woff2 differ diff --git a/branch-master/asset_image_folder_open_solid.svg b/branch-master/asset_image_folder_open_solid.svg new file mode 100644 index 000000000..99d403c81 --- /dev/null +++ b/branch-master/asset_image_folder_open_solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/branch-master/asset_image_github_brands.svg b/branch-master/asset_image_github_brands.svg new file mode 100644 index 000000000..7870c06dc --- /dev/null +++ b/branch-master/asset_image_github_brands.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/branch-master/asset_image_jslint_wrapper_vim.png b/branch-master/asset_image_jslint_wrapper_vim.png new file mode 100644 index 000000000..c987b46b6 Binary files /dev/null and b/branch-master/asset_image_jslint_wrapper_vim.png differ diff --git a/branch-master/asset_image_jslint_wrapper_vscode.png b/branch-master/asset_image_jslint_wrapper_vscode.png new file mode 100644 index 000000000..1f4297171 Binary files /dev/null and b/branch-master/asset_image_jslint_wrapper_vscode.png differ diff --git a/branch-master/asset_image_json_160.svg b/branch-master/asset_image_json_160.svg new file mode 100644 index 000000000..fca9b8749 --- /dev/null +++ b/branch-master/asset_image_json_160.svg @@ -0,0 +1,104 @@ + + + + + JSON logo + + + + + + + + + + + + diff --git a/branch-master/asset_image_logo_512.html b/branch-master/asset_image_logo_512.html new file mode 100644 index 000000000..b538e29dd --- /dev/null +++ b/branch-master/asset_image_logo_512.html @@ -0,0 +1,199 @@ + + + +logo + + + +
    +
    JS
    +
    Lint
    +
    + + diff --git a/branch-master/asset_image_logo_512.png b/branch-master/asset_image_logo_512.png new file mode 100644 index 000000000..19d154d68 Binary files /dev/null and b/branch-master/asset_image_logo_512.png differ diff --git a/branch-master/asset_image_logo_512.svg b/branch-master/asset_image_logo_512.svg new file mode 100644 index 000000000..61872c4ae --- /dev/null +++ b/branch-master/asset_image_logo_512.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + diff --git a/branch-master/index.html b/branch-master/index.html new file mode 100644 index 000000000..fb09af3de --- /dev/null +++ b/branch-master/index.html @@ -0,0 +1,1649 @@ + + + + + + + + + JSLint: The JavaScript Code Quality and Coverage Tool + + + + + + + + + + + + +
    +
    Loading modules
    +   + + . + . + . + +
    +
    + +
    + Source + +
    +
    + + + +
    +
    + Options +
    +
    + Env... +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    + Allow... +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
      +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
      +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
      +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    +
    + + diff --git a/branch-master/jslint.js b/branch-master/jslint.js new file mode 100644 index 000000000..b486c1ce2 --- /dev/null +++ b/branch-master/jslint.js @@ -0,0 +1,11573 @@ +// #!/usr/bin/env node +// JSLint + +// The Unlicense +// +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + + +// jslint(source, option_dict, global_list) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze. +// option_dict An object whose keys correspond to option names. +// global_list An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// PHASE 1. Split by newlines into . +// PHASE 2. Lex into . +// PHASE 3. Parse into using the Pratt-parser. +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// PHASE 5. Check whitespace between tokens in . + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*jslint beta, node*/ +/*property + JSLINT_BETA, NODE_V8_COVERAGE, a, all, argv, arity, artifact, + assertErrorThrownAsync, assertJsonEqual, assertOrThrow, assign, async, b, + beta, bitwise, block, body, browser, c, calls, catch, catch_list, + catch_stack, causes, char, children, clear, closer, closure, code, column, + concat, consoleError, console_error, console_log, constant, context, + convert, count, coverageDir, create, cwd, d, dead, debugInline, default, + delta, devel, directive, directive_ignore_line, directive_list, directives, + dirname, disrupt, dot, edition, elem_list, ellipsis, else, end, endOffset, + endsWith, entries, env, error, eval, every, example_list, excludeList, exec, + execArgv, exit, exitCode, export_dict, exports, expression, extra, fart, + file, fileList, fileURLToPath, filter, finally, flag, floor, for, forEach, + formatted_message, free, freeze, from, froms, fsWriteFileWithParents, + fud_stmt, functionName, function_list, function_stack, functions, get, + getset, github_repo, globExclude, global, global_dict, global_list, + holeList, htmlEscape, id, identifier, import, import_list, import_meta_url, + inc, includeList, indent2, index, indexOf, init, initial, isArray, + isBlockCoverage, isHole, isNaN, is_equal, is_weird, join, jslint, + jslint_apidoc, jslint_assert, jslint_charset_ascii, jslint_cli, + jslint_edition, jslint_phase1_split, jslint_phase2_lex, jslint_phase3_parse, + jslint_phase4_walk, jslint_phase5_whitage, jslint_report, json, + jstestDescribe, jstestIt, jstestOnExit, keys, label, lbp, led_infix, length, + level, line, lineList, line_list, line_offset, line_source, lines, + linesCovered, linesTotal, live, log, long, loop, m, map, margin, match, max, + message, meta, min, mkdir, modeCoverageIgnoreFile, modeIndex, mode_cli, + mode_conditional, mode_json, mode_module, mode_noop, mode_property, + mode_shebang, mode_stop, module, moduleFsInit, moduleName, module_list, + name, names, node, nomen, noop, now, nr, nud_prefix, + objectDeepCopyWithKeysSorted, ok, on, open, opening, option, option_dict, + order, package_name, padEnd, padStart, parameters, parent, parentIi, parse, + pathname, pathnameList, platform, pop, processArgv, process_argv, + process_env, process_exit, promises, property, property_dict, push, quote, + ranges, readFile, readdir, readonly, recursive, reduce, repeat, replace, + resolve, result, reverse, role, round, scriptId, search, set, shebang, + shell, shift, signature, single, slice, some, sort, source, spawn, splice, + split, stack, stack_trace, start, startOffset, startsWith, statement, + statement_prv, stdio, stop, stop_at, stringify, subscript, switch, + syntax_dict, tenure, test, test_cause, test_internal_error, this, thru, + toLocaleString, toString, token, token_global, token_list, token_nxt, + token_tree, tokens, trace, tree, trim, trimEnd, trimRight, try, type, + unlink, unordered, unshift, url, used, v8CoverageListMerge, + v8CoverageReportCreate, value, variable, version, versions, warn, warn_at, + warning, warning_list, warnings, white, wrapped, writeFile +*/ + +// init debugInline +let debugInline = (function () { + let __consoleError = function () { + return; + }; + function debug(...argv) { + +// This function will print to stderr and then return [0]. + + __consoleError("\n\ndebugInline"); + __consoleError(...argv); + __consoleError("\n"); + return argv[0]; + } + debug(); // Coverage-hack. + __consoleError = console.error; //jslint-ignore-line + return debug; +}()); +let jslint_charset_ascii = ( + "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007" + + "\b\t\n\u000b\f\r\u000e\u000f" + + "\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017" + + "\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f" + + " !\"#$%&'()*+,-./0123456789:;<=>?" + + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + + "`abcdefghijklmnopqrstuvwxyz{|}~\u007f" +); +let jslint_edition = "v2024.11.24"; +let jslint_export; // The jslint object to be exported. +let jslint_fudge = 1; // Fudge starting line and starting + // ... column to 1. +let jslint_import_meta_url = ""; // import.meta.url used by cli. +let jslint_rgx_cap = ( + /^[A-Z]/ +); +let jslint_rgx_crlf = ( + /\n|\r\n?/ +); +let jslint_rgx_digits_bits = ( + /^[01_]*/ +); +let jslint_rgx_digits_decimals = ( + /^[0-9_]*/ +); +let jslint_rgx_digits_hexs = ( + /^[0-9A-F_]*/i +); +let jslint_rgx_digits_octals = ( + /^[0-7_]*/ +); +let jslint_rgx_directive = ( + /^(jslint|property|global)\s+(.*)$/ +); +let jslint_rgx_directive_part = ( + /([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*|$/g +); +let jslint_rgx_identifier = ( + /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/ +); +let jslint_rgx_json_number = ( + +// https://datatracker.ietf.org/doc/html/rfc7159#section-6 +// number = [ minus ] int [ frac ] [ exp ] + + /^-?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][\-+]?\d+)?$/ +); +let jslint_rgx_mega = ( + +// Vim-hack - vim-editor has trouble parsing naked '`' in regexp + + /[\u0060\\]|\$\{/ +); +let jslint_rgx_module = ( + /^[a-zA-Z0-9_$:.@\-\/]+$/ +); +let jslint_rgx_numeric_separator_illegal = ( + /__|_$|_n$/m +); +let jslint_rgx_slash_star_or_slash = ( + /\/\*|\/$/ +); +let jslint_rgx_tab = ( + /\t/g +); +let jslint_rgx_todo = ( + /\b(?:todo|TO\s?DO|HACK)\b/ +); +let jslint_rgx_token = new RegExp( + "^(" + + "(\\s+)" + + "|([a-zA-Z_$][a-zA-Z0-9_$]*)" + + "|[(){}\\[\\],:;'\"~\\`]" + + "|\\?[?.]?" + + "|=(?:==?|>)?" + + "|\\.+" + + "|\\*[*\\/=]?" + + "|\\/[*\\/]?" + + "|\\+[=+]?" + + "|-[=\\-]?" + + "|[\\^%]=?" + + "|&[&=]?" + + "|\\" + + "|[|=]?" + + "|>{1,3}=?" + + "|< throws an error. + + let err; + try { + await asyncFunc(); + } catch (errCaught) { + err = errCaught; + } + assertOrThrow(err, "No error thrown."); + assertOrThrow( + !regexp || new RegExp(regexp).test(err.message), + err + ); +} + +function assertJsonEqual(aa, bb, message) { + +// This function will assert JSON.stringify() === JSON.stringify(). + + aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa), undefined, 1); + bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb), undefined, 1); + if (aa !== bb) { + throw new Error( + "\n" + aa + "\n!==\n" + bb + + ( + typeof message === "string" + ? " - " + message + : message + ? " - " + JSON.stringify(message) + : "" + ) + ); + } +} + +function assertOrThrow(condition, message) { + +// This function will throw if is falsy. + + if (!condition) { + throw ( + (!message || typeof message === "string") + ? new Error(String(message).slice(0, 2048)) + : message + ); + } +} + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than '{}' because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +async function fsWriteFileWithParents(pathname, data) { + +// This function will write to and lazy-mkdirp if necessary. + + await moduleFsInit(); + +// Try writing to pathname. + + try { + await moduleFs.promises.writeFile(pathname, data); + } catch (ignore) { + +// Lazy mkdirp. + + await moduleFs.promises.mkdir(modulePath.dirname(pathname), { + recursive: true + }); + +// Retry writing to pathname. + + await moduleFs.promises.writeFile(pathname, data); + } + console.error("wrote file " + pathname); +} + +function globExclude({ + excludeList = [], + includeList = [], + pathnameList = [] +}) { + +// This function will +// 1. Exclude pathnames in that don't match glob-patterns in +// . +// 2. Exclude pathnames in that match glob-patterns in +// . + + function globAssertNotWeird(list, name) { + +// This function will check if of strings contain weird characters. + + [ + [ + "\n", ( + /^.*?([\u0000-\u0007\r]).*/gm + ) + ], + [ + "\r", ( + /^.*?([\n]).*/gm + ) + ] + ].forEach(function ([ + separator, rgx + ]) { + list.join(separator).replace(rgx, function (match0, char) { + throw new Error( + "Weird character " + + JSON.stringify(char) + + " found in " + name + " " + + JSON.stringify(match0) + ); + }); + }); + } + + function globToRegexp(pattern) { + +// This function will translate glob to javascript-regexp, +// which javascript can then use to "glob" pathnames. + + let ii = 0; + let isClass = false; + let strClass = ""; + let strRegex = ""; + pattern = pattern.replace(( + /\/\/+/g + ), "/"); + pattern = pattern.replace(( + /\*\*\*+/g + ), "**"); + pattern.replace(( + /\\\\|\\\[|\\\]|\[|\]|./g + ), function (match0) { + switch (match0) { + case "[": + if (isClass) { + strClass += "["; + return; + } + strClass += "\u0000"; + strRegex += "\u0000"; + isClass = true; + return; + case "]": + if (isClass) { + isClass = false; + return; + } + strRegex += "]"; + return; + default: + if (isClass) { + strClass += match0; + return; + } + strRegex += match0; + } + return ""; + }); + strClass += "\u0000"; + +// An expression "[!...]" matches a single character, namely any character that +// is not matched by the expression obtained by removing the first '!' from it. +// (Thus, "[!a-]" matches any single character except 'a', and '-'.) + + strClass = strClass.replace(( + /\u0000!/g + ), "\u0000^"); + +// One may include '-' in its literal meaning by making it the first or last +// character between the brackets. + + strClass = strClass.replace(( + /\u0000-/g + ), "\u0000\\-"); + strClass = strClass.replace(( + /-\u0000/g + ), "\\-\u0000"); + +// Escape brackets '[', ']' in character class. + + strClass = strClass.replace(( + /[\[\]]/g + ), "\\$&"); + +// https://stackoverflow.com/questions/3561493 +// /is-there-a-regexp-escape-function-in-javascript +// $()*+-./?[\]^{|} + + strRegex = strRegex.replace(( + +// Ignore [-/]. + + /[$()*+.?\[\\\]\^{|}]/g + ), "\\$&"); + +// Expand wildcard '**/*'. + + strRegex = strRegex.replace(( + /\\\*\\\*\/(?:\\\*)+/g + ), ".*?"); + +// Expand wildcard '**'. + + strRegex = strRegex.replace(( + /(^|\/)\\\*\\\*(\/|$)/gm + ), "$1.*?$2"); + +// Expand wildcard '*'. + + strRegex = strRegex.replace(( + /(?:\\\*)+/g + ), "[^\\/]*?"); + +// Expand wildcard '?'. + + strRegex = strRegex.replace(( + /\\\?/g + ), "[^\\/]"); + +// Expand directory-with-trailing-slash '.../'. + + strRegex = strRegex.replace(( + /\/$/gm + ), "\\/.*?"); + +// Merge strClass into strRegex. + + ii = 0; + strClass = strClass.split("\u0000"); + strRegex = strRegex.replace(( + /\u0000/g + ), function () { + ii += 1; + if (strClass[ii] === "") { + return ""; + } + return "[" + strClass[ii] + "]"; + }); + +// Change strRegex from string to regexp. + + strRegex = new RegExp("^" + strRegex + "$", "gm"); + return strRegex; + } + +// Validate excludeList, includeList, pathnameList. + + globAssertNotWeird(excludeList, "pattern"); + globAssertNotWeird(includeList, "pattern"); + globAssertNotWeird(pathnameList, "pathname"); + +// Optimization +// Concat pathnames into a single, newline-separated string, +// whose pathnames can all be filtered with a single, regexp-pass. + + pathnameList = pathnameList.join("\n"); + +// 1. Exclude pathnames in that don't match glob-patterns in +// . + + if (includeList.length > 0) { + includeList = includeList.map(globToRegexp); + includeList.forEach(function (pattern) { + pathnameList = pathnameList.replace(pattern, "\u0000$&"); + }); + pathnameList = pathnameList.replace(( + /^[^\u0000].*/gm + ), ""); + pathnameList = pathnameList.replace(( + /^\u0000+/gm + ), ""); + } + +// 2. Exclude pathnames in that match glob-patterns in +// . + + excludeList = excludeList.map(globToRegexp); + excludeList.forEach(function (pattern) { + pathnameList = pathnameList.replace(pattern, ""); + }); + +// Split newline-separated pathnames back to list. + + pathnameList = pathnameList.split("\n").filter(function (elem) { + return elem; + }); + return { + excludeList, + includeList, + pathnameList + }; +} + +function htmlEscape(str) { + +// This function will make html-safe by escaping & < >. + + return String(str).replace(( + /&/g + ), "&").replace(( + //g + ), ">"); +} + +function jslint( + source = "", // A text to analyze. + option_dict = empty(), // An object whose keys correspond to option + // ... names. + global_list = [] // An array of strings containing global + // ... variables that the file is allowed + // ... readonly access. +) { + +// The jslint function itself. + + let catch_list = []; // The array containing all catch-blocks. + let catch_stack = [ // The stack of catch-blocks. + { + context: empty() + } + ]; + let cause_dict = empty(); // The object of test-causes. + let directive_list = []; // The directive comments. + let export_dict = empty(); // The exported names and values. + let function_list = []; // The array containing all functions. + let function_stack = []; // The stack of functions. + let global_dict = empty(); // The object containing the global + // ... declarations. + let import_list = []; // The array collecting all import-from strings. + let line_list = String( // The array containing source lines. + "\n" + source + ).split(jslint_rgx_crlf).map(function (line_source) { + return { + line_source + }; + }); + let mode_stop = false; // true if JSLint cannot finish. + let property_dict = empty(); // The object containing the tallied + // ... property names. + let state = empty(); // jslint state-object to be passed between + // jslint functions. + let syntax_dict = empty(); // The object containing the parser. + let tenure = empty(); // The predefined property registry. + let token_global = { // The global object; the outermost context. + async: 0, + body: true, + context: empty(), + finally: 0, + from: 0, + id: "(global)", + level: 0, + line: jslint_fudge, + live: [], + loop: 0, + switch: 0, + thru: 0, + try: 0 + }; + let token_list = []; // The array of tokens. + let warning_list = []; // The array collecting all generated warnings. + +// Error reportage functions: + + function artifact(the_token) { + +// Return a string representing an artifact. + + the_token = the_token || state.token_nxt; + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); + } + + function is_equal(aa, bb) { + +// test_cause: +// ["0&&0", "is_equal", "", "", 0] + + test_cause(""); + +// Probably deadcode. +// if (aa === bb) { +// return true; +// } + + jslint_assert(!(aa === bb), `Expected !(aa === bb).`); + if (Array.isArray(aa)) { + return ( + Array.isArray(bb) + && aa.length === bb.length + && aa.every(function (value, index) { + +// test_cause: +// ["`${0}`&&`${0}`", "is_equal", "recurse_isArray", "", 0] +// ["`${0}`&&`${1}`", "is_equal", "recurse_isArray", "", 0] + + test_cause("recurse_isArray"); + return is_equal(value, bb[index]); + }) + ); + } + +// Probably deadcode. +// if (Array.isArray(bb)) { +// return false; +// } + + jslint_assert(!Array.isArray(bb), `Expected !Array.isArray(bb).`); + switch (aa.id === bb.id && aa.id) { + case "(number)": + case "(string)": + return aa.value === bb.value; + +// PR-394 - Bugfix +// Fix jslint falsely believing megastring literals `0` and `1` are similar. + + case "`": + if (!is_equal(aa.value, bb.value)) { + return false; + } + break; + } + if (is_weird(aa) || is_weird(bb)) { + +// test_cause: +// ["aa(/./)||{}", "is_equal", "false", "", 0] + + test_cause("false"); + return false; + } + if (aa.arity === bb.arity && aa.id === bb.id) { + if (aa.id === "." || aa.id === "?.") { + +// test_cause: +// ["aa.bb&&aa.bb", "is_equal", "recurse_arity_id", "", 0] +// ["aa?.bb&&aa?.bb", "is_equal", "recurse_arity_id", "", 0] + + test_cause("recurse_arity_id"); + return ( + is_equal(aa.expression, bb.expression) + && is_equal(aa.name, bb.name) + ); + } + if (aa.arity === "unary") { + +// test_cause: +// ["+0&&+0", "is_equal", "recurse_unary", "", 0] + + test_cause("recurse_unary"); + return is_equal(aa.expression, bb.expression); + } + if (aa.arity === "binary") { + +// test_cause: +// ["aa[0]&&aa[0]", "is_equal", "recurse_binary", "", 0] + + test_cause("recurse_binary"); + return ( + aa.id !== "(" + && is_equal(aa.expression[0], bb.expression[0]) + && is_equal(aa.expression[1], bb.expression[1]) + ); + } + if (aa.arity === "ternary") { + +// test_cause: +// ["aa=(``?``:``)&&(``?``:``)", "is_equal", "recurse_ternary", "", 0] + + test_cause("recurse_ternary"); + return ( + is_equal(aa.expression[0], bb.expression[0]) + && is_equal(aa.expression[1], bb.expression[1]) + && is_equal(aa.expression[2], bb.expression[2]) + ); + } + +// Probably deadcode. +// if (aa.arity === "function" || aa.arity === "regexp") { +// return false; +// } + + jslint_assert( + !(aa.arity === "function" || aa.arity === "regexp"), + `Expected !(aa.arity === "function" || aa.arity === "regexp").` + ); + +// test_cause: +// ["undefined&&undefined", "is_equal", "true", "", 0] + + test_cause("true"); + return true; + } + +// test_cause: +// ["null&&undefined", "is_equal", "false", "", 0] + + test_cause("false"); + return false; + } + + function is_weird(thing) { + switch (thing.id) { + case "(regexp)": + return true; + case "=>": + return true; + case "[": + return thing.arity === "unary"; + case "function": + return true; + case "{": + return true; + default: + return false; + } + } + + function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + the_token = the_token || state.token_nxt; + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); + } + + function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); + } + + function test_cause(code, aa, column) { + +// This function will instrument to for test-purposes. + + if (option_dict.test_cause) { + cause_dict[JSON.stringify([ + String(new Error().stack).replace(( + /^ at (?:file|stop|stop_at|test_cause|warn|warn_at)\b.*?\n/gm + ), "").match( + /\n at ((?:Object\.\w+?_)?\w+?) / + )[1].replace(( + /^Object\./ + ), ""), + code, + String( + (aa === undefined || aa === token_global) + ? "" + : aa + ), + column || 0 + ])] = true; + } + } + + function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + let the_warning; + the_token = the_token || state.token_nxt; + the_warning = warn_at( + code, + the_token.line, + (the_token.from || 0) + jslint_fudge, + a || artifact(the_token), + b, + c, + d + ); + +// Issue #408 +// Warnings that should be ignored sometimes suppress legitimate warnings. + + if (the_warning.directive_ignore_line) { + return the_warning; + } + +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token.warning) { + warning_list.pop(); + return the_warning; + } + the_token.warning = the_warning; + return the_warning; + } + + function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + let mm; + let warning = Object.assign(empty(), { + a, + b, + c, + code, + +// Fudge column numbers in warning message. + + column: column || jslint_fudge, + d, + line, + line_source: "", + name: "JSLintError" + }, line_list[line]); + warning.column = Math.max( + Math.min(warning.column, warning.line_source.length), + jslint_fudge + ); + test_cause(code, b || a, warning.column); + switch (code) { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + case "and": + mm = `The '&&' subexpression should be wrapped in parens.`; + break; + case "bad_assignment_a": + mm = `Bad assignment to '${a}'.`; + break; + case "bad_directive_a": + mm = `Bad directive '${a}'.`; + break; + case "bad_get": + mm = `A get function takes no parameters.`; + break; + case "bad_module_name_a": + mm = `Bad module name '${a}'.`; + break; + case "bad_option_a": + mm = `Bad option '${a}'.`; + break; + case "bad_set": + mm = `A set function takes one parameter.`; + break; + case "duplicate_a": + mm = `Duplicate '${a}'.`; + break; + case "empty_block": + mm = `Empty block.`; + break; + case "expected_a": + mm = `Expected '${a}'.`; + break; + case "expected_a_at_b_c": + mm = `Expected '${a}' at column ${b}, not column ${c}.`; + break; + case "expected_a_b": + mm = `Expected '${a}' and instead saw '${b}'.`; + break; + case "expected_a_b_before_c_d": + mm = `Expected ${a} '${b}' to be ordered before ${c} '${d}'.`; + break; + case "expected_a_b_from_c_d": + mm = ( + `Expected '${a}' to match '${b}' from line ${c}` + + ` and instead saw '${d}'.` + ); + break; + case "expected_a_before_b": + mm = `Expected '${a}' before '${b}'.`; + break; + case "expected_digits_after_a": + mm = `Expected digits after '${a}'.`; + break; + case "expected_four_digits": + mm = `Expected four digits after '\\u'.`; + break; + case "expected_identifier_a": + mm = `Expected an identifier and instead saw '${a}'.`; + break; + case "expected_line_break_a_b": + mm = `Expected a line break between '${a}' and '${b}'.`; + break; + case "expected_regexp_factor_a": + mm = `Expected a regexp factor and instead saw '${a}'.`; + break; + case "expected_space_a_b": + mm = `Expected one space between '${a}' and '${b}'.`; + break; + case "expected_statements_a": + mm = `Expected statements before '${a}'.`; + break; + case "expected_string_a": + mm = `Expected a string and instead saw '${a}'.`; + break; + case "expected_type_string_a": + mm = `Expected a type string and instead saw '${a}'.`; + break; + case "freeze_exports": + mm = ( + `Expected 'Object.freeze('. All export values should be frozen.` + ); + break; + +// PR-378 - Relax warning "function_in_loop". +// +// case "function_in_loop": +// mm = `Don't create functions within a loop.`; +// break; + +// PR-390 - Add numeric-separator check. + + case "illegal_num_separator": + mm = `Illegal numeric separator '_' at column ${column}.`; + break; + case "infix_in": + mm = ( + `Unexpected 'in'. Compare with undefined,` + + ` or use the hasOwnProperty method instead.` + ); + break; + case "label_a": + mm = `'${a}' is a statement label.`; + break; + case "misplaced_a": + mm = `Place '${a}' at the outermost level.`; + break; + case "misplaced_directive_a": + mm = `Place the '/*${a}*/' directive before the first statement.`; + break; + case "missing_await_statement": + mm = `Expected await statement in async function.`; + break; + +// PR-347 - Disable warning "missing_browser". +// +// case "missing_browser": +// mm = `/*global*/ requires the Assume a browser option.`; +// break; + + case "missing_m": + mm = `Expected 'm' flag on a multiline regular expression.`; + break; + case "naked_block": + mm = `Naked block.`; + break; + case "nested_comment": + mm = `Nested comment.`; + break; + case "not_label_a": + mm = `'${a}' is not a label.`; + break; + case "number_isNaN": + mm = `Use Number.isNaN function to compare with NaN.`; + break; + case "out_of_scope_a": + mm = `'${a}' is out of scope.`; + break; + case "redefinition_a_b": + mm = `Redefinition of '${a}' from line ${b}.`; + break; + case "redefinition_global_a_b": + mm = `Redefinition of global ${a} variable '${b}'.`; + break; + case "required_a_optional_b": + mm = `Required parameter '${a}' after optional parameter '${b}'.`; + break; + case "reserved_a": + mm = `Reserved name '${a}'.`; + break; + case "subscript_a": + mm = `['${a}'] is better written in dot notation.`; + break; + case "todo_comment": + mm = `Unexpected TODO comment.`; + break; + case "too_long": + mm = `Line is longer than 80 characters.`; + break; + case "too_many_digits": + mm = `Too many digits.`; + break; + case "unclosed_comment": + mm = `Unclosed comment.`; + break; + case "unclosed_disable": + mm = ( + `Directive '/*jslint-disable*/' was not closed` + + ` with '/*jslint-enable*/'.` + ); + break; + case "unclosed_mega": + mm = `Unclosed mega literal.`; + break; + case "unclosed_string": + mm = `Unclosed string.`; + break; + case "undeclared_a": + mm = `Undeclared '${a}'.`; + break; + case "unexpected_a": + mm = `Unexpected '${a}'.`; + break; + case "unexpected_a_after_b": + mm = `Unexpected '${a}' after '${b}'.`; + break; + case "unexpected_a_before_b": + mm = `Unexpected '${a}' before '${b}'.`; + break; + case "unexpected_at_top_level_a": + mm = `Expected '${a}' to be in a function.`; + break; + case "unexpected_char_a": + mm = `Unexpected character '${a}'.`; + break; + case "unexpected_comment": + mm = `Unexpected comment.`; + break; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// case "unexpected_directive_a": +// mm = `When using modules, don't use directive '/\u002a${a}'.`; +// break; + + case "unexpected_expression_a": + mm = `Unexpected expression '${a}' in statement position.`; + break; + case "unexpected_label_a": + mm = `Unexpected label '${a}'.`; + break; + case "unexpected_parens": + mm = `Don't wrap function literals in parens.`; + break; + case "unexpected_space_a_b": + mm = `Unexpected space between '${a}' and '${b}'.`; + break; + case "unexpected_statement_a": + mm = `Unexpected statement '${a}' in expression position.`; + break; + case "unexpected_trailing_space": + mm = `Unexpected trailing space.`; + break; + case "unexpected_typeof_a": + mm = ( + `Unexpected 'typeof'. Use '===' to compare directly with ${a}.` + ); + break; + case "uninitialized_a": + mm = `Uninitialized '${a}'.`; + break; + case "unopened_enable": + mm = ( + `Directive '/*jslint-enable*/' was not opened` + + ` with '/*jslint-disable*/'.` + ); + break; + case "unreachable_a": + mm = `Unreachable '${a}'.`; + break; + case "unregistered_property_a": + mm = `Unregistered property name '${a}'.`; + break; + case "unused_a": + mm = `Unused '${a}'.`; + break; + case "use_double": + mm = `Use double quotes, not single quotes.`; + break; + +// PR-386 - Fix issue #382 - Make fart-related warnings more readable. + + case "use_function_not_fart": + mm = ( + `Use 'function (...)', not '(...) =>' when arrow functions` + + ` become too complex.` + ); + break; + case "use_open": + mm = ( + `Wrap a ternary expression in parens,` + + ` with a line break after the left paren.` + ); + break; + case "use_spaces": + mm = `Use spaces, not tabs.`; + break; + case "var_on_top": + mm = `Move variable declaration to top of function or script.`; + break; + case "var_switch": + mm = `Don't declare variables in a switch.`; + break; + case "weird_condition_a": + mm = `Weird condition '${a}'.`; + break; + case "weird_expression_a": + mm = `Weird expression '${a}'.`; + break; + case "weird_loop": + mm = `Weird loop.`; + break; + case "weird_property_a": + mm = `Weird property name '${a}'.`; + break; + case "weird_relation_a": + mm = `Weird relation '${a}'.`; + break; + case "wrap_condition": + mm = `Wrap the condition in parens.`; + break; + +// PR-386 - Fix issue #382 - Make fart-related warnings more readable. + + case "wrap_fart_parameter": + mm = `Wrap the parameter before '=>' in parens.`; + break; + case "wrap_immediate": + mm = ( + `Wrap an immediate function invocation in parentheses to assist` + + ` the reader in understanding that the expression is the` + + ` result of a function, and not the function itself.` + ); + break; + case "wrap_regexp": + mm = `Wrap this regexp in parens to avoid confusion.`; + break; + case "wrap_unary": + mm = `Wrap the unary expression in parens.`; + break; + } + +// Validate mm. + + jslint_assert(mm, code); + warning.message = mm; + +// PR-242 - Include stack_trace for jslint to debug itself for errors. + + if (option_dict.trace) { + warning.stack_trace = new Error().stack; + } + if (warning.directive_ignore_line) { + +// test_cause: +// ["0 //jslint-ignore-line", "semicolon", "directive_ignore_line", "", 0] + + test_cause("directive_ignore_line"); + return warning; + } + warning_list.push(warning); + return warning; + } + + try { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + + option_dict = Object.assign(empty(), option_dict); + Object.assign(state, { + artifact, + catch_list, + catch_stack, + directive_list, + export_dict, + function_list, + function_stack, + global_dict, + global_list, + import_list, + is_equal, + is_weird, + line_list, + mode_json: false, // true if parsing JSON. + mode_module: false, // true if import or export was used. + mode_property: false, // true if directive /*property*/ is + // ... used. + mode_shebang: false, // true if #! is seen on the first line. + option_dict, + property_dict, + source, + stop, + stop_at, + syntax_dict, + tenure, + test_cause, + token_global, + token_list, + token_nxt: token_global, + warn, + warn_at, + warning_list + }); + +// PHASE 1. Split by newlines into . + + jslint_phase1_split(state); + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 2. Lex into . + + jslint_phase2_lex(state); + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 3. Parse into using the Pratt-parser. + + jslint_phase3_parse(state); + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). + + if (!state.mode_json) { + jslint_phase4_walk(state); + } + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 5. Check whitespace between tokens in . + + if (!state.mode_json && warning_list.length === 0) { + jslint_phase5_whitage(state); + } + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PR-347 - Disable warning "missing_browser". +// +// if (!option_dict.browser) { +// directive_list.forEach(function (comment) { +// if (comment.directive === "global") { +// +// // test_cause: +// // ["/*global aa*/", "jslint", "missing_browser", "(comment)", 1] +// +// warn("missing_browser", comment); +// } +// }); +// } + + if (option_dict.test_internal_error) { + jslint_assert(undefined, "test_internal_error"); + } + } catch (err) { + mode_stop = true; + err.message = "[JSLint was unable to finish] " + err.message; + err.mode_stop = true; + if (err.name !== "JSLintError") { + Object.assign(err, { + column: jslint_fudge, + line: jslint_fudge, + line_source: "", + stack_trace: err.stack + }); + } + if (warning_list.indexOf(err) === -1) { + warning_list.push(err); + } + } + +// Sort warning_list by mode_stop first, line, column respectively. + + warning_list.sort(function (aa, bb) { + return ( + Boolean(bb.mode_stop) - Boolean(aa.mode_stop) + || aa.line - bb.line + || aa.column - bb.column + ); + +// Update each warning with formatted_message ready-for-use by jslint_cli. + + }).map(function ({ + column, + line, + line_source, + message, + stack_trace = "" + }, ii, list) { + list[ii].formatted_message = String( + String(ii + 1).padStart(2, " ") + + ". \u001b[31m" + message + "\u001b[39m" + + " \u001b[90m\/\/ line " + line + ", column " + column + + "\u001b[39m\n" + + (" " + line_source.trim()).slice(0, 72) + "\n" + + stack_trace + ).trimRight(); + }); + + return { + causes: cause_dict, + directives: directive_list, + edition: jslint_edition, + exports: export_dict, + froms: import_list, + functions: function_list, + global: token_global, + id: "(JSLint)", + json: state.mode_json, + lines: line_list, + module: state.mode_module === true, + ok: warning_list.length === 0 && !mode_stop, + option: option_dict, + property: property_dict, + shebang: ( + state.mode_shebang + ? line_list[jslint_fudge].line_source + : undefined + ), + stop: mode_stop, + tokens: token_list, + tree: state.token_tree, + warnings: warning_list + }; +} + +// PR-362 - Add API Doc. + +async function jslint_apidoc({ + example_list, + github_repo, + module_list, + package_name, + pathname, + version +}) { + +// This function will create API Doc from . + + let elem_ii = 0; + let html; + + function elem_create(moduleObj, key, moduleName) { + +// This function will create a sub API Doc from elem []. + + let example = "N/A"; + let id = encodeURIComponent("apidoc.elem." + moduleName + "." + key); + let name; + let signature; + let source; + name = htmlEscape((typeof moduleObj[key]) + " " + key); + if (typeof moduleObj[key] !== "function") { + return { + name, + signature: (` + +${name} + + `), + source: (` +
  • +

    + + ${name} + +

    +
  • + `) + }; + } + // init source + source = htmlEscape(trim_start(moduleObj[key].toString())); + // init signature + source = source.replace(( + /(\([\S\s]*?\)) \{/ + ), function (match0, match1) { + signature = htmlEscape( + match1.replace(( + / *?\/\*[\S\s]*?\*\/ */g + ), "").replace(( + / *?\/\/.*/g + ), "").replace(( + /\n{2,}/g + ), "\n") + ); + return match0; + }); + // init comment + source = source.replace(( + /\n(?:\/\/.*?\n)+\n/ + ), "$&"); + // init example + example_list.some(function (example2) { + example2.replace( + new RegExp(( + "((?:\\n.*?){8}(function )?)\\b" + + key + + "(\\((?:.*?\\n){8})" + ), "g"), + function (ignore, header, isDeclaration, footer) { + if (!isDeclaration) { + example = "..." + trim_start( + htmlEscape(header) + + "" + + htmlEscape(key) + + "" + + htmlEscape(footer) + ).trimEnd() + "\n..."; + } + return ""; + } + ); + return example !== "N/A"; + }); + if (source.length > 2048) { + source = source.slice(0, 2048) + "...\n}\n"; + } + return { + name, + signature: (` + +${name}${signature} + + `), + source: (` +
  • +

    + + ${name}${signature} + +

    +
  • +
  • Description and source-code:
    ${source}
  • +
  • Example usage:
    ${example}
  • + `) + }; + } + + function trim_start(str) { + +// This function will normalize whitespace before . + + let whitespace = ""; + str.trim().replace(( + /^ */gm + ), function (match0) { + if (whitespace === "" || match0.length < whitespace.length) { + whitespace = match0; + } + return ""; + }); + str = str.replace(new RegExp("^" + whitespace, "gm"), ""); + return str; + } + await moduleFsInit(); + +// Html-escape params. + + github_repo = htmlEscape(github_repo); + package_name = htmlEscape(package_name); + version = htmlEscape(version); + +// Init example_list. + + example_list = await Promise.all(example_list.map(async function (file) { + +// This function will read example from given file. + + let result = await moduleFs.promises.readFile(file, "utf8"); + result = ( + "\n\n\n\n\n\n\n\n" + // bug-workaround - truncate example to manageable size + + result.slice(0, 524288) + + "\n\n\n\n\n\n\n\n" + ); + result = result.replace(( + /\r\n*/g + ), "\n"); + return result; + })); + +// Init module_list. + + module_list = await Promise.all(module_list.map(async function ({ + pathname + }) { + let moduleName = htmlEscape(JSON.stringify(pathname)); + let moduleObj = await import(pathname); + if (moduleObj.default) { + moduleObj = moduleObj.default; + } + return { + elem_list: Object.keys(moduleObj).map(function (key) { + return elem_create(moduleObj, key, moduleName); + }).sort(function (aa, bb) { + return ( + aa.name < bb.name + ? -1 + : 1 + ); + }).map(function (elem) { + elem_ii += 1; + elem.signature = elem.signature.replace( + ">", + ">" + elem_ii + ". " + ); + elem.source = elem.source.replace( + "\">", + "\">" + elem_ii + ". " + ); + return elem; + }), + id: encodeURIComponent("apidoc.module." + moduleName), + moduleName + }; + })); + html = (` + + + + + + +${package_name} apidoc + + + +
    +

    API Doc for ${package_name} (${version})

    +
    + +

    Table of Contents

    +
    +
      + `) + module_list.map(function ({ + elem_list, + id, + moduleName + }) { + return ( + (` +
    • + Module ${moduleName} +
        + `) + + elem_list.map(function ({ + signature + }) { + return "
      • \n" + signature + "\n
      • \n"; + }).join("") + + (` +
      +
    • + `) + ); + }).join("") + (` +
    +
    + `) + module_list.map(function ({ + elem_list, + id, + moduleName + }) { + return ( + (` +
    +

    Module ${moduleName}

    +
      + `) + + elem_list.map(function ({ + source + }) { + return source; + }).join("") + + (` +
    +
    + `) + ); + }).join("") + (` +
    + [ + This document was created with + JSLint + ] +
    +
    + + + `); + html = html.trim().replace(( + / +?$/gm + ), "") + "\n"; + await fsWriteFileWithParents(pathname, html); +} + +function jslint_assert(condition, message) { + +// This function will throw if is falsy. + + if (condition) { + return condition; + } + throw new Error( + `This was caused by a bug in JSLint. +Please open an issue with this stack-trace (and possible example-code) at +https://github.com/jslint-org/jslint/issues. +edition = "${jslint_edition}"; +${String(message).slice(0, 2000)}` + ); +} + +async function jslint_cli({ + console_error, + console_log, + file, + import_meta_url, + mode_cli, + mode_noop, + option, + process_argv, + process_env, + process_exit, + source +}) { + +// This function will run jslint from nodejs-cli. + + let command; + let data; + let exit_code = 0; + let mode_report; + let mode_wrapper_vim; + let result; + + function jslint_from_file({ + code, + file, + line_offset = 0, + mode_conditional, + option = empty() + }) { + let result_from_file; + if ( + mode_conditional + && !( + /^\/\*jslint\b/m + ).test(code.slice(0, 65536)) + ) { + return; + } + option = Object.assign(empty(), option, { + file + }); + switch (( + /\.\w+?$|$/m + ).exec(file)[0]) { + case ".html": + +// Recursively jslint embedded "". + + code.replace(( + /^]*?>\n([\S\s]*?\n)<\/script>$/gm + ), function (ignore, match1, ii) { + jslint_from_file({ + code: match1, + file: file + ". + + import_meta_url = import_meta_url || jslint_import_meta_url; + if ( + jslint_rgx_url_search_window_jslint.test(import_meta_url) + && (typeof globalThis === "object" && globalThis) + ) { + globalThis.jslint = jslint; + } + +// Feature-detect nodejs. + + if (!( + (typeof process === "object" && process) + && process.versions + && typeof process.versions.node === "string" + && !mode_noop + )) { + return exit_code; + } + console_error = console_error || console.error; + console_log = console_log || console.log; + process_argv = process_argv || process.argv; + process_env = process_env || process.env; + process_exit = process_exit || process.exit; + await moduleFsInit(); + if ( + !( + +// Feature-detect nodejs-cli. + + process.execArgv.indexOf("--eval") === -1 + && process.execArgv.indexOf("-e") === -1 + && ( + ( + /[\/|\\]jslint(?:\.[cm]?js)?$/m + ).test(process_argv[1]) + || mode_cli + ) + && ( + moduleUrl.fileURLToPath(import_meta_url) + === + modulePath.resolve(process_argv[1]) + ) + ) + && !mode_cli + ) { + return exit_code; + } + +// init commmand + + command = String(process_argv[2]).split("="); + command[1] = command.slice(1).join("="); + + switch (command[0]) { + +// PR-362 - Add API Doc. + + case "jslint_apidoc": + await jslint_apidoc(Object.assign(JSON.parse(process_argv[3]), { + pathname: command[1] + })); + return; + +// PR-363 - Add command jslint_report. + + case "jslint_report": + mode_report = command[1]; + process_argv = process_argv.slice(1); + break; + +// COMMIT-b26d6df2 - Add command jslint_wrapper_vim. + + case "jslint_wrapper_vim": + mode_wrapper_vim = true; + process_argv = process_argv.slice(1); + break; + +// PR-364 - Add command v8_coverage_report. + + case "v8_coverage_report": + await v8CoverageReportCreate({ + consoleError: console_error, + coverageDir: command[1], + processArgv: process_argv.slice(3) + }); + return; + } + +// Normalize file relative to process.cwd(). + + process_argv.slice(2).some(function (arg) { + if (!arg.startsWith("-")) { + file = file || arg; + return true; + } + }); + if (!file) { + return; + } + file = modulePath.resolve(file) + "/"; + if (file.startsWith(process.cwd() + "/")) { + file = file.replace(process.cwd() + "/", "").slice(0, -1) || "."; + } + file = file.replace(( + /\\/g + ), "/").replace(( + /\/$/g + ), ""); + if (source) { + data = source; + } else { + +// jslint_cli - jslint directory. + + try { + data = await moduleFs.promises.readdir(file, "utf8"); + } catch (ignore) {} + if (data) { + await Promise.all(data.map(async function (file2) { + let code; + let time_start = Date.now(); + file2 = file + "/" + file2; + switch (( + /\.\w+?$|$/m + ).exec(file2)[0]) { + case ".cjs": + case ".html": + case ".js": + case ".json": + case ".md": + case ".mjs": + case ".sh": + break; + default: + return; + } + try { + code = await moduleFs.promises.readFile(file2, "utf8"); + } catch (ignore) { + return; + } + if ( + ( + /(?:\b|_)(?:lock|min|raw|rollup)(?:\b|_)/ + ).test(file2) + || !(code && code.length < 1048576) + ) { + return; + } + jslint_from_file({ + code, + file: file2, + option + }); + console_error( + "jslint - " + (Date.now() - time_start) + "ms - " + file2 + ); + })); + process_exit(exit_code); + return exit_code; + } + +// jslint_cli - jslint file. + + try { + data = await moduleFs.promises.readFile(file, "utf8"); + } catch (err) { + console_error(err); + exit_code = 1; + process_exit(exit_code); + return exit_code; + } + } + result = jslint_from_file({ + code: data, + file, + option + }); + if (mode_report) { + result = jslint.jslint_report(result); + result = `\n${result}\n`; + await fsWriteFileWithParents(mode_report, result); + } + process_exit(exit_code); + return exit_code; +} + +function jslint_phase1_split() { + +// PHASE 1. Split by newlines into . + + return; +} + +function jslint_phase2_lex(state) { + +// PHASE 2. Lex into . + + let { + artifact, + directive_list, + global_dict, + global_list, + line_list, + option_dict, + stop, + stop_at, + tenure, + test_cause, + token_global, + token_list, + warn, + warn_at + } = state; + let char; // The current character being lexed. + let column = 0; // The column number of the next character. + let from; // The starting column number of the token. + let from_mega; // The starting column of megastring. + let line = 0; // The line number of the next character. + let line_disable; // The starting line of "/*jslint-disable*/". + let line_mega; // The starting line of megastring. + let line_source = ""; // The remaining line source string. + let line_whole = ""; // The whole line source string. + let mode_digits_empty_string = 1; + let mode_digits_numeric_separator = 2; + let mode_directive = true; // true if directives are still allowed. + let mode_mega = false; // true if currently parsing a megastring + // ... literal. + let mode_regexp; // true if regular expression literal seen on + // ... this line. + let paren_backtrack_list = []; // List of most recent "(" tokens at any + // ... paren-depth. + let paren_depth = 0; // Keeps track of current paren-depth. + let snippet = ""; // A piece of string. + let token_1; // The first token. + let token_prv = token_global; // The previous token including + // ... comments. + let token_prv_expr = token_global; // The previous token excluding + // ... comments. + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions char_after, char_before, +// read_digits, and char_after_escape help in the parsing of literals. + + function char_after(match) { + +// Get the next character from the source line. Remove it from the line_source, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + +// test_cause: +// ["aa=/[", "char_after", "expected_a", "]", 5] +// ["aa=/aa{/", "char_after", "expected_a_b", "/", 8] + + return ( + char === "" + ? stop_at("expected_a", line, column - 1, match) + : stop_at("expected_a_b", line, column, match, char) + ); + } + char = line_source.slice(0, 1); + line_source = line_source.slice(1); + snippet += char || " "; + column += 1; + return char; + } + + function char_after_escape(extra) { + +// Validate char after escape "\\". + + char_after("\\"); + switch (char) { + case "": + +// test_cause: +// ["\"\\", "char_after_escape", "unclosed_string", "", 2] + + return stop_at("unclosed_string", line, column); + case "/": + return char_after(); + case "\\": + return char_after(); + case "`": + return char_after(); + case "b": + return char_after(); + case "f": + return char_after(); + case "n": + return char_after(); + case "r": + return char_after(); + case "t": + +// test_cause: +// ["\"\\/\\\\\\`\\b\\f\\n\\r\\t\"", "char_after_escape", "char_after", "", 0] + + test_cause("char_after"); + return char_after(); + case "u": + if (char_after("u") === "{") { + if (state.mode_json) { + +// test_cause: +// ["[\"\\u{12345}\"]", "char_after_escape", "unexpected_a", "{", 5] + + warn_at("unexpected_a", line, column, char); + } + if (read_digits("x", undefined) > 5) { + +// test_cause: +// ["\"\\u{123456}\"", "char_after_escape", "too_many_digits", "", 11] + + warn_at("too_many_digits", line, column); + } + if (char !== "}") { + +// test_cause: +// ["\"\\u{12345\"", "char_after_escape", "expected_a_before_b", "\"", 10] + + stop_at("expected_a_before_b", line, column, "}", char); + } + return char_after(); + } + char_before(); + if (read_digits("x", mode_digits_empty_string) < 4) { + +// test_cause: +// ["\"\\u0\"", "char_after_escape", "expected_four_digits", "", 5] + + warn_at("expected_four_digits", line, column); + } + return; + default: + if (extra && extra.indexOf(char) >= 0) { + return char_after(); + } + +// test_cause: +// ["\"\\0\"", "char_after_escape", "unexpected_a_before_b", "0", 3] + + warn_at("unexpected_a_before_b", line, column, "\\", char); + } + } + + function char_before() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the line_source. + + char = snippet.slice(-1); + line_source = char + line_source; + column -= char.length; + +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + return char; + } + + function check_numeric_separator(digits, column) { + +// This function will check for illegal numeric-separator in . + + digits.replace(( + jslint_rgx_numeric_separator_illegal + ), function (ignore, ii) { + +// test_cause: +// ["0x0_0_;", "check_numeric_separator", "illegal_num_separator", "", 6] +// ["0x0_0__0;", "check_numeric_separator", "illegal_num_separator", "", 6] +// ["aa=1_2_;", "check_numeric_separator", "illegal_num_separator", "", 7] +// ["aa=1_2__3;", "check_numeric_separator", "illegal_num_separator", "", 7] +// ["aa=1_2_n;", "check_numeric_separator", "illegal_num_separator", "", 7] + + warn_at("illegal_num_separator", line, column + ii + 1); + return ""; + }); + } + + function lex_comment() { + let body; + let ii = 0; + let jj = 0; + let the_comment; + +// Create a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + +// Create token from comment //.... + + if (snippet === "//") { + snippet = line_source; + line_source = ""; + the_comment = token_create("(comment)", snippet); + if (mode_mega) { + +// test_cause: +// ["`${//}`", "lex_comment", "unexpected_comment", "`", 4] + + warn("unexpected_comment", the_comment, "`"); + } + +// Create token from comment /*...*/. + + } else { + snippet = []; + if (line_source[0] === "/") { + +// test_cause: +// ["/*/", "lex_comment", "unexpected_a", "/", 2] + + warn_at("unexpected_a", line, column + ii, "/"); + } + +// Lex/loop through each line until "*/". + + while (true) { + // jslint_rgx_star_slash + ii = line_source.indexOf("*/"); + if (ii >= 0) { + break; + } + // jslint_rgx_slash_star + ii = line_source.indexOf("/*"); + if (ii >= 0) { + +// test_cause: +// ["/*/*", "lex_comment", "nested_comment", "", 2] + + warn_at("nested_comment", line, column + ii); + } + snippet.push(line_source); + line_source = read_line(); + if (line_source === undefined) { + +// test_cause: +// ["/*", "lex_comment", "unclosed_comment", "", 1] + + return stop_at("unclosed_comment", line, column); + } + } + jj = line_source.slice(0, ii).search( + jslint_rgx_slash_star_or_slash + ); + if (jj >= 0) { + +// test_cause: +// ["/*/**/", "lex_comment", "nested_comment", "", 2] + + warn_at("nested_comment", line, column + jj); + } + snippet.push(line_source.slice(0, ii)); + snippet = snippet.join(" "); + column += ii + 2; + line_source = line_source.slice(ii + 2); + the_comment = token_create("(comment)", snippet); + } + +// Uncompleted work comment. + + if (!option_dict.devel && jslint_rgx_todo.test(snippet)) { + +// test_cause: +// ["//todo", "lex_comment", "todo_comment", "(comment)", 1] //jslint-ignore-line + + warn("todo_comment", the_comment); + } + +// Lex directives in comment. + + [ + the_comment.directive, body + ] = Array.from(snippet.match(jslint_rgx_directive) || []).slice(1); + if (the_comment.directive === undefined) { + return the_comment; + } + directive_list.push(the_comment); + if (!mode_directive) { + +// test_cause: +// ["0\n/*global aa*/", "lex_comment", "misplaced_directive_a", "global", 1] + + warn_at("misplaced_directive_a", line, from, the_comment.directive); + return the_comment; + } + +// lex_directive(); +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + +// Lex/loop through each directive in /*...*/ + + ii = 0; + body.replace(jslint_rgx_directive_part, function ( + match0, + key, + val, + jj + ) { + if (ii !== jj) { + +// test_cause: +// ["/*jslint !*/", "lex_comment", "bad_directive_a", "!", 1] + + return stop("bad_directive_a", the_comment, body.slice(ii)); + } + if (match0 === "") { + return ""; + } + ii += match0.length; + switch (the_comment.directive) { + case "global": + if (val) { + +// test_cause: +// ["/*global aa:false*/", "lex_comment", "bad_option_a", "aa:false", 1] + + warn("bad_option_a", the_comment, key + ":" + val); + } + global_dict[key] = "user-defined"; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// state.mode_module = the_comment; + + break; + case "jslint": + if (!option_set_item(key, val !== "false")) { + +// test_cause: +// ["/*jslint undefined*/", "lex_comment", "bad_option_a", "undefined", 1] + + warn("bad_option_a", the_comment, key); + } + break; + case "property": + state.mode_property = true; + tenure[key] = true; + break; + } + return ""; + }); + return the_comment; + } + + function lex_megastring() { + let id; + let match; + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (mode_mega) { + +// test_cause: +// ["`${`", "lex_megastring", "expected_a_b", "`", 4] + + return stop_at("expected_a_b", line, column, "}", "`"); + } + from_mega = from; + line_mega = line; + mode_mega = true; + snippet = ""; + +// Parsing a mega literal is tricky. First create a ` token. + + token_create("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + while (true) { + match = line_source.match(jslint_rgx_mega) || { + "0": "", + index: 0 + }; + snippet += line_source.slice(0, match.index); + column += match.index; + line_source = line_source.slice(match.index); + match = match[0]; + switch (match) { + case "${": + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + token_create("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then create tokens that will become part of an expression until +// a } token is made. + + column += 2; + token_create("${"); + line_source = line_source.slice(2); + +// Lex/loop through each token inside megastring-expression `${...}`. + + while (true) { + id = lex_token().id; + if (id === "{") { + +// test_cause: +// ["`${{", "lex_megastring", "expected_a_b", "{", 4] + + return stop_at("expected_a_b", line, column, "}", "{"); + } + if (id === "}") { + break; + } + } + break; + case "\\": + snippet += line_source.slice(0, 2); + line_source = line_source.slice(2); + column += 2; + break; + case "`": + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + token_create("(string)", snippet).quote = "`"; + snippet = ""; + +// Terminate megastring with `. + + line_source = line_source.slice(1); + column += 1; + mode_mega = false; + return token_create("`"); + default: + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + snippet += line_source + "\n"; + if (read_line() === undefined) { + +// test_cause: +// ["`", "lex_megastring", "unclosed_mega", "", 1] + + return stop_at("unclosed_mega", line_mega, from_mega); + } + } + } + } + + function lex_number() { + let prefix = snippet; + +// PR-390 - Add numeric-separator check. + + check_numeric_separator(prefix, column - prefix.length); + char_after(); + switch (prefix === "0" && char) { + case "b": + case "o": + case "x": + read_digits(char, mode_digits_numeric_separator); + +// PR-351 - Ignore BigInt suffix 'n'. + + if (char === "n") { + char_after("n"); + } + break; + default: + if (char === ".") { + read_digits("d", mode_digits_numeric_separator); + } + if (char === "E" || char === "e") { + char_after(char); + if (char !== "+" && char !== "-") { + char_before(); + } + read_digits("d", mode_digits_numeric_separator); + } + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + +// test_cause: +// ["0a", "lex_number", "unexpected_a_after_b", "0", 2] + + return stop_at( + "unexpected_a_after_b", + line, + column, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + char_before(); + return token_create("(number)", snippet); + } + + function lex_regexp() { + +// Regexp +// Lex a regular expression literal. + + let flag; + let mode_regexp_multiline; + let result; + let value; + mode_regexp = true; + + function lex_regexp_bracketed() { + let mode_regexp_range; + +// RegExp +// Match a class. + + char_after("["); + if (char === "^") { + char_after("^"); + } + while (true) { + +// RegExp +// Match a character in a character class. + + switch (char) { + case "": + case "]": + +// test_cause: +// ["aa=/[", "lex_regexp_bracketed", "closer", "", 0] +// ["aa=/[]/", "lex_regexp_bracketed", "closer", "", 0] + + test_cause("closer"); + if (mode_regexp_range) { + +// test_cause: +// ["aa=/[0-]/", "lex_regexp_bracketed", "unexpected_a", "-", 7] + + warn_at("unexpected_a", line, column - 1, "-"); + } + return char_after("]"); + +// PR-362 - Relax regexp-warning against using . +// +// case " ": +// +// // test_cause: +// // ["aa=/[ ]/", "lex_regexp_bracketed", "expected_a_b", " ", 6] +// +// warn_at("expected_a_b", line, column, "\\u0020", " "); +// break; + + case "-": + case "/": + case "[": + case "^": + +// test_cause: +// ["aa=/[-]/", "lex_regexp_bracketed", "expected_a_before_b", "-", 6] +// ["aa=/[.^]/", "lex_regexp_bracketed", "expected_a_before_b", "^", 7] +// ["aa=/[/", "lex_regexp_bracketed", "expected_a_before_b", "/", 6] +// ["aa=/[\\\\/]/", "lex_regexp_bracketed", "expected_a_before_b", "/", 8] +// ["aa=/[\\\\[]/", "lex_regexp_bracketed", "expected_a_before_b", "[", 8] + + warn_at("expected_a_before_b", line, column, "\\", char); + break; + case "\\": + char_after_escape("BbDdSsWw-[]^"); + char_before(); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${/[`]/}`", "lex_regexp_bracketed", "unexpected_a", "`", 6] + + warn_at("unexpected_a", line, column, "`"); + } + break; + } + char_after(); + mode_regexp_range = false; + if (char === "-") { + +// RegExp +// Match a range of subclasses. + + mode_regexp_range = true; + char_after("-"); + } + } + } + + function lex_regexp_group() { + +// RegExp +// Lex sequence of characters in regexp. + + switch (char) { + case "": + warn_at("expected_regexp_factor_a", line, column, char); + break; + case ")": + warn_at("expected_regexp_factor_a", line, column, char); + break; + case "]": + +// test_cause: +// ["/ /", "lex_regexp_group", "expected_regexp_factor_a", "", 3] +// ["aa=/)", "lex_regexp_group", "expected_regexp_factor_a", ")", 5] +// ["aa=/]", "lex_regexp_group", "expected_regexp_factor_a", "]", 5] + + warn_at("expected_regexp_factor_a", line, column, char); + break; + } + while (true) { + switch (char) { + case "": + case ")": + case "/": + case "]": + return; + +// PR-362 - Relax regexp-warning against using . +// +// case " ": +// +// // test_cause: +// // ["aa=/ /", "lex_regexp_group", "expected_a_b", " ", 5] +// +// warn_at("expected_a_b", line, column, "\\s", " "); +// char_after(); +// break; + + case "$": + if (line_source[0] !== "/") { + mode_regexp_multiline = true; + } + char_after(); + break; + case "(": + +// RegExp +// Match a group that starts with left paren. + + char_after("("); + switch (char) { + case ":": + +// test_cause: +// ["aa=/(:)/", "lex_regexp_group", "expected_a_before_b", ":", 6] +// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5] + + warn_at("expected_a_before_b", line, column, "?", ":"); + break; + case "?": + char_after("?"); + switch (char) { + case "!": + +// PR-437 - Add grammar for regexp-named-capture-group. + + case "<": + case "=": + char_after(); + break; + default: + char_after(":"); + } + break; + } + +// RegExp +// Recurse lex_regexp_group(). + + lex_regexp_group(); + char_after(")"); + break; + case "*": + case "+": + case "?": + case "{": + case "}": + +// test_cause: +// ["aa=/+/", "lex_regexp_group", "expected_a_before_b", "+", 5] +// ["aa=/.**/", "lex_regexp_group", "expected_a_before_b", "*", 7] +// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5] +// ["aa=/{/", "lex_regexp_group", "expected_a_before_b", "{", 5] +// ["aa=/}/", "lex_regexp_group", "expected_a_before_b", "}", 5] + + warn_at("expected_a_before_b", line, column, "\\", char); + char_after(); + break; + case "[": + lex_regexp_bracketed(); + break; + case "\\": + +// test_cause: +// ["aa=/\\/", "lex_regexp_group", "escape", "", 0] + + test_cause("escape"); + +// PR-437 - Add grammar for regexp-named-backreference. + + char_after_escape("BbDdSsWw^${}[]():=!.|*+?k"); + break; + case "^": + if (snippet !== "^") { + mode_regexp_multiline = true; + } + char_after(); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${/`/}`", "lex_regexp_group", "unexpected_a", "`", 5] + + warn_at("unexpected_a", line, column, "`"); + } + char_after(); + break; + default: + char_after(); + } + +// RegExp +// Match an optional quantifier. + + switch (char) { + case "*": + case "+": + if (char_after(char) === "?") { + +// test_cause: +// ["aa=/.*?/", "lex_regexp_group", "?", "", 0] +// ["aa=/.+?/", "lex_regexp_group", "?", "", 0] + + test_cause("?"); + char_after("?"); + } + break; + case "?": + if (char_after("?") === "?") { + +// test_cause: +// ["aa=/.??/", "lex_regexp_group", "unexpected_a", "?", 7] + + warn_at("unexpected_a", line, column, char); + char_after("?"); + } + break; + case "{": + if (read_digits("d", mode_digits_empty_string) === 0) { + +// test_cause: +// ["aa=/aa{/", "lex_regexp_group", "expected_a_before_b", ",", 8] + + warn_at("expected_a_before_b", line, column, "0", ","); + } + if (char === ",") { + +// test_cause: +// ["aa=/.{,/", "lex_regexp_group", "comma", "", 0] + + test_cause("comma"); + read_digits("d", mode_digits_empty_string); + } + if (char_after("}") === "?") { + +// test_cause: +// ["aa=/.{0}?/", "lex_regexp_group", "unexpected_a", "?", 9] + + warn_at("unexpected_a", line, column, char); + char_after("?"); + } + break; + } + } + } + +// RegExp +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + char_after(); + if (char === "=") { + +// test_cause: +// ["aa=/=/", "lex_regexp", "expected_a_before_b", "=", 5] + + warn_at("expected_a_before_b", line, column, "\\", "="); + } + lex_regexp_group(); + +// RegExp +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + +// RegExp +// Make sure there is a closing slash. + + value = snippet; + char_after("/"); + +// RegExp +// Create flag. + + flag = empty(); + while ( + +// Regexp +// char is a letter. + + (char >= "a" && char <= "z\uffff") + || (char >= "A" && char <= "Z\uffff") + ) { + +// RegExp +// Process dangling flag letters. + + switch (!flag[char] && char) { + case "g": + break; + case "i": + break; + case "m": + break; + case "u": + break; + case "y": + +// test_cause: +// ["aa=/./gimuy", "lex_regexp", "flag", "", 0] + + test_cause("flag"); + break; + default: + +// test_cause: +// ["aa=/./gg", "lex_regexp", "unexpected_a", "g", 8] +// ["aa=/./z", "lex_regexp", "unexpected_a", "z", 7] + + warn_at("unexpected_a", line, column, char); + } + flag[char] = true; + char_after(); + } + char_before(); + if (char === "/" || char === "*") { + +// test_cause: +// ["aa=/.//", "lex_regexp", "unexpected_a", "/", 3] + + return stop_at("unexpected_a", line, from, char); + } + result = token_create("(regexp)", char); + result.flag = flag; + result.value = value; + if (mode_regexp_multiline && !flag.m) { + +// test_cause: +// ["aa=/$^/", "lex_regexp", "missing_m", "", 7] + + warn_at("missing_m", line, column); + } + return result; + } + + function lex_slash_or_regexp() { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + let the_token; + switch ( + token_prv_expr.identifier + && !token_prv_expr.dot + && token_prv_expr.id + ) { + case "case": + case "delete": + case "in": + case "instanceof": + case "new": + case "typeof": + case "void": + case "yield": + the_token = lex_regexp(); + +// test_cause: +// ["case /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6] +// ["delete /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8] +// ["in /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 4] +// ["instanceof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 12] +// ["new /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 5] +// ["typeof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8] +// ["void /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6] +// ["yield /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 7] + + return stop("unexpected_a", the_token); + case "return": + return lex_regexp(); + } + switch (!token_prv_expr.identifier && token_prv_expr.id.slice(-1)) { + case "!": + case "%": + case "&": + case "*": + case "+": + case "-": + case "/": + case ";": + case "<": + case ">": + case "^": + case "{": + case "|": + case "}": + case "~": + the_token = lex_regexp(); + +// test_cause: +// ["!/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["%/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["&/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["+/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["-/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["0 * /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5] +// ["0 / /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5] +// [";/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["^/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["{/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["|/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["}/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["~/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] + + warn("wrap_regexp", the_token); + return the_token; + case "(": + case ",": + case ":": + case "=": + case "?": + case "[": + +// test_cause: +// ["(/./", "lex_slash_or_regexp", "recurse", "", 0] +// [",/./", "lex_slash_or_regexp", "recurse", "", 0] +// [":/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["=/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["?/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["aa[/./", "lex_slash_or_regexp", "recurse", "", 0] + + test_cause("recurse"); + return lex_regexp(); + } + if (line_source[0] === "=") { + column += 1; + line_source = line_source.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + return token_create(snippet); + } + + function lex_string(quote) { + +// Create a string token. + + let the_token; + if (!option_dict.single && quote === "'") { + +// test_cause: +// ["''", "lex_string", "use_double", "", 1] + + warn_at("use_double", line, column); + } + snippet = ""; + char_after(); + +// Lex/loop through each character in "...". + + while (true) { + switch (char) { + case "": + +// test_cause: +// ["\"", "lex_string", "unclosed_string", "", 1] + + return stop_at("unclosed_string", line, column); + case "\\": + char_after_escape(quote); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${\"`\"}`", "lex_string", "unexpected_a", "`", 5] + + warn_at("unexpected_a", line, column, "`"); + } + char_after("`"); + break; + case quote: + +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + the_token = token_create("(string)", snippet); + the_token.quote = quote; + return the_token; + default: + char_after(); + } + } + } + + function lex_token() { + let match; + +// Lex/loop through each whitespace. + + while (true) { + +// Lex/loop through each blank-line. + + while (!line_source) { + line_source = read_line(); + from = 0; + if (line_source === undefined) { + return ( + mode_mega + +// test_cause: +// ["`${//}`", "lex_token", "unclosed_mega", "", 1] + + ? stop_at("unclosed_mega", line_mega, from_mega) + : line_disable !== undefined + +// test_cause: +// ["/*jslint-disable*/", "lex_token", "unclosed_disable", "", 1] + + ? stop_at("unclosed_disable", line_disable) + : token_create("(end)") + ); + } + } + from = column; + match = line_source.match(jslint_rgx_token); + +// match[1] token +// match[2] whitespace +// match[3] identifier +// match[4] number +// match[5] rest + + if (!match) { + +// test_cause: +// ["#", "lex_token", "unexpected_char_a", "#", 1] + + return stop_at( + "unexpected_char_a", + line, + column, + line_source[0] + ); + } + snippet = match[1]; + column += snippet.length; + line_source = match[5]; + if (!match[2]) { + break; + } + } + +// The token is an identifier. + + if (match[3]) { + return token_create(snippet, undefined, true); + } + +// Create token from number. + + if (match[4]) { + return lex_number(); + } + +// Create token from string "..." or '...'. + + if (snippet === "\"" || snippet === "'") { + return lex_string(snippet); + } + +// Create token from megastring `...`. + + if (snippet === "`") { + return lex_megastring(); + } + +// Create token from comment /*...*/ or //.... + + if (snippet === "/*" || snippet === "//") { + return lex_comment(); + } + +// Create token from slash /. + + if (snippet === "/") { + return lex_slash_or_regexp(); + } + return token_create(snippet); + } + + function option_set_item(key, val) { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + switch (key) { + case "beta": // Enable experimental warnings. + case "bitwise": // Allow bitwise operator. + case "browser": // Assume browser environment. + case "convert": // Allow conversion operator. + case "couch": // Assume CouchDb environment. + case "devel": // Allow console.log() and friends. + case "ecma": // Assume ECMAScript environment. + case "eval": // Allow eval(). + case "fart": // Allow complex fat-arrow. + case "for": // Allow for-statement. + case "getset": // Allow get() and set(). + case "indent2": // Use 2-space indent. + case "long": // Allow long lines. + case "node": // Assume Node.js environment. + case "nomen": // Allow weird property name. + case "single": // Allow single-quote strings. + case "subscript": // Allow identifier in subscript-notation. + case "test_cause": // Test jslint's causes. + case "test_internal_error": // Test jslint's internal-error + // ... handling-ability. + case "this": // Allow 'this'. + case "trace": // Include jslint stack-trace in warnings. + case "unordered": // Allow unordered cases, params, properties, + // ... variables, and exports. + case "variable": // Allow unordered const and let declarations + // ... that are not at top of function-scope. + case "white": // Allow messy whitespace. + option_dict[key] = val; + break; + +// PR-404 - Alias "evil" to jslint-directive "eval" for backwards-compat. + + case "evil": + return option_set_item("eval", val); + +// PR-404 - Alias "nomen" to jslint-directive "name" for backwards-compat. + + case "name": + return option_set_item("nomen", val); + default: + return false; + } + +// Initialize global-variables. + + switch (val && key) { + +// Assign global browser variables to global_dict. +/* +// /\*jslint beta, browser, devel*\/ +console.log(JSON.stringify(Object.keys(window).sort(), undefined, 4)); +*/ + + case "browser": + object_assign_from_list(global_dict, [ + +// Shared with Node.js. + + "AbortController", + // "Buffer", + // "Crypto", + // "CryptoKey", + "Event", + "EventTarget", + "MessageChannel", + "MessageEvent", + "MessagePort", + // "Request", + // "Response", + // "SubtleCrypto", + "TextDecoder", + "TextEncoder", + "URL", + "URLSearchParams", + "WebAssembly", + // "__dirname", + // "__filename", + // "atob", + // "btoa", + // "clearImmediate", + "clearInterval", + "clearTimeout", + // "console", + // "crypto", + // "exports", + // "fetch", + // "global", + // "module", + "performance", + // "process", + "queueMicrotask", + // "require", + // "setImmediate", + "setInterval", + "setTimeout", + +// Web worker only. +// https://github.com/mdn/content/blob/main/files/en-us/web/api +// /workerglobalscope/index.md + + "importScripts", + +// Window. + + "Blob", + // "CharacterData", + // "DocumentType", + // "Element", + // "Event", + "FileReader", + // "FontFace", + "FormData", + "IntersectionObserver", + "MutationObserver", + // "Storage", + // "TextDecoder", + // "TextEncoder", + // "URL", + "Worker", + "XMLHttpRequest", + // "caches", + // "clearInterval", + // "clearTimeout", + "document", + // "event", + "fetch", + // "history", + "indexedDb", + "localStorage", + "location", + // "name", + "navigator", + "postMessage", + // "screen", + "sessionStorage", + // "setInterval", + // "setTimeout", + "structuredClone", + "window" + ], "browser"); + break; + +// https://docs.couchdb.org/en/stable/query-server/javascript.html#javascript + + case "couch": + object_assign_from_list(global_dict, [ + "emit", + "getRow", + "isArray", + "log", + "provides", + "registerType", + "require", + "send", + "start", + "sum", + "toJSON" + ], "CouchDb"); + break; + case "devel": + object_assign_from_list(global_dict, [ + "alert", "confirm", "console", "prompt" + ], "development"); + break; + +// These are the globals that are provided by the language standard. +// Assign global ECMAScript variables to global_dict. +/* +node --input-type=module --eval ' +// /\*jslint beta, node*\/ +import https from "https"; +(async function () { + let dict = {import: true}; + let result = ""; + await new Promise(function (resolve) { + https.get(( + "https://raw.githubusercontent.com/mdn/content/main/files" + + "/en-us/web/javascript/reference/global_objects/index.md" + ), function (res) { + res.on("data", function (chunk) { + result += chunk; + }).on("end", resolve).setEncoding("utf8"); + }); + }); + result.replace(( + /\n- \{\{JSxRef\("(?:Global_Objects\/)?([^"\/]+?)"/g + ), function (ignore, key) { + if (globalThis.hasOwnProperty(key)) { + dict[key] = true; + } + return ""; + }); + console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4)); +}()); +' +*/ + + case "ecma": + object_assign_from_list(global_dict, [ + "AggregateError", + "Array", + "ArrayBuffer", + "Atomics", + "BigInt", + "BigInt64Array", + "BigUint64Array", + "Boolean", + "DataView", + "Date", + "Error", + "EvalError", + "Float32Array", + "Float64Array", + "Function", + "Infinity", + "Int16Array", + "Int32Array", + "Int8Array", + "Intl", + "JSON", + "Map", + "Math", + "NaN", + "Number", + "Object", + "Promise", + "Proxy", + "RangeError", + "ReferenceError", + "Reflect", + "RegExp", + "Set", + "SharedArrayBuffer", + "String", + "Symbol", + "SyntaxError", + "TypeError", + "URIError", + "Uint16Array", + "Uint32Array", + "Uint8Array", + "Uint8ClampedArray", + "WeakMap", + "WeakSet", + "WebAssembly", + "decodeURI", + "decodeURIComponent", + "encodeURI", + "encodeURIComponent", + "eval", + "globalThis", + "import", + "isFinite", + "isNaN", + "parseFloat", + "parseInt", + "undefined" + ], "ECMAScript"); + break; + +// Assign global Node.js variables to global_dict. +/* +node --input-type=module --eval ' +// /\*jslint beta, node*\/ +import moduleHttps from "https"; +(async function () { + let dict = Object.create(null); + let result = ""; + await new Promise(function (resolve) { + moduleHttps.get(( + "https://raw.githubusercontent.com/nodejs/node/v16.x/doc/api" + + "/globals.md" + ), function (res) { + res.on("data", function (chunk) { + result += chunk; + }).on("end", resolve).setEncoding("utf8"); + }); + }); + result.replace(( + /\n(?:\* \[`|## |## Class: )`\w+/g + ), function (match0) { + dict[match0.split("`")[1]] = true; + return ""; + }); + console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4)); +}()); +' +*/ + + case "node": + object_assign_from_list(global_dict, [ + "AbortController", + "Buffer", + // "Crypto", + // "CryptoKey", + "Event", + "EventTarget", + "MessageChannel", + "MessageEvent", + "MessagePort", + // "Request", + // "Response", + // "SubtleCrypto", + "TextDecoder", + "TextEncoder", + "URL", + "URLSearchParams", + "WebAssembly", + "__dirname", + "__filename", + // "atob", + // "btoa", + "clearImmediate", + "clearInterval", + "clearTimeout", + "console", + // "crypto", + "exports", + // "fetch", + "global", + "module", + "performance", + "process", + "queueMicrotask", + "require", + "setImmediate", + "setInterval", + "setTimeout" + ], "Node.js"); + break; + } + return true; + } + + function read_digits(base, mode) { + let digits = line_source.match( + base === "b" + ? jslint_rgx_digits_bits + : base === "o" + ? jslint_rgx_digits_octals + : base === "x" + ? jslint_rgx_digits_hexs + : jslint_rgx_digits_decimals + )[0]; + if ( + (mode !== mode_digits_empty_string && digits.length === 0) + || digits[0] === "_" + ) { + +// test_cause: +// ["0x", "read_digits", "expected_digits_after_a", "0x", 2] +// ["0x_", "read_digits", "expected_digits_after_a", "0x", 2] + + warn_at("expected_digits_after_a", line, column, snippet); + } + +// PR-390 - Add numeric-separator check. + + if (mode === mode_digits_numeric_separator) { + check_numeric_separator(digits, column); + } else if (digits.indexOf("_") >= 0) { + +// test_cause: +// ["\"\\u{1_2}\"", "read_digits", "illegal_num_separator", "", 6] + + warn_at( + "illegal_num_separator", + line, + column + digits.indexOf("_") + 1 + ); + } + column += digits.length; + line_source = line_source.slice(digits.length); + snippet += digits; + char_after(); + return digits.length; + } + + function read_line() { + +// Put the next line of source in line_source. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + if ( + !option_dict.long + && line_whole.length > 80 + && line_disable === undefined + && !state.mode_json + && token_1 + && !mode_regexp + ) { + +// test_cause: +// ["/////////////////////////////////////////////////////////////////////////////////", "read_line", "too_long", "", 1] //jslint-ignore-line + + warn_at("too_long", line); + } + column = 0; + line += 1; + mode_regexp = false; + line_source = undefined; + line_whole = ""; + if (line_list[line] === undefined) { + return line_source; + } + line_source = line_list[line].line_source; + line_whole = line_source; + +// Scan each line for following ignore-directives: +// "/*jslint-disable*/" +// "/*jslint-enable*/" +// "//jslint-ignore-line" + + if (line_source === "/*jslint-disable*/") { + +// test_cause: +// ["/*jslint-disable*/", "read_line", "jslint_disable", "", 0] + + test_cause("jslint_disable"); + line_disable = line; + } else if (line_source === "/*jslint-enable*/") { + if (line_disable === undefined) { + +// test_cause: +// ["/*jslint-enable*/", "read_line", "unopened_enable", "", 1] + + stop_at("unopened_enable", line); + } + line_disable = undefined; + } else if ( + line_source.endsWith(" //jslint-ignore-line") + || line_source.endsWith(" //jslint-quiet") + ) { + +// test_cause: +// ["0 //jslint-ignore-line", "read_line", "jslint_ignore_line", "", 0] + + test_cause("jslint_ignore_line"); + line_list[line].directive_ignore_line = true; + } + if (line_disable !== undefined) { + +// test_cause: +// ["/*jslint-disable*/\n0", "read_line", "line_disable", "", 0] + + test_cause("line_disable"); + line_source = ""; + } + // jslint_rgx_tab + if (line_source.indexOf("\t") >= 0) { + if (!option_dict.white) { + +// test_cause: +// ["\t", "read_line", "use_spaces", "", 1] + + warn_at("use_spaces", line, line_source.indexOf("\t") + 1); + } + line_source = line_source.replace(jslint_rgx_tab, " "); + } + if (!option_dict.white && line_source.endsWith(" ")) { + +// test_cause: +// [" ", "read_line", "unexpected_trailing_space", "", 1] + + warn_at("unexpected_trailing_space", line, line_source.length - 1); + } + return line_source; + } + + function token_create(id, value, identifier) { + +// Create the token object and append it to token_list. + + let the_token = { + from, + id, + identifier: Boolean(identifier), + line, + nr: token_list.length, + thru: column, + value + }; + token_list.push(the_token); + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + mode_directive = false; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option_dict.white. + + if ( + token_prv.line === line + && token_prv.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (token_prv.id === "(comment)" || token_prv.id === "(regexp)") + ) { + +// test_cause: +// ["/**//**/", "token_create", "expected_space_a_b", "(comment)", 5] + + warn( + "expected_space_a_b", + the_token, + artifact(token_prv), + artifact(the_token) + ); + } + if (token_prv.id === "." && id === "(number)") { + +// test_cause: +// [".0", "token_create", "expected_a_before_b", ".", 1] + + warn("expected_a_before_b", token_prv, "0", "."); + } + if (token_prv_expr.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart. +// Farts are now detected by keeping a list of most recent "(" tokens at any +// given depth. When a "=>" token is encountered, the most recent "(" token at +// current depth is marked as a fart. + + switch (id) { + case "(": + paren_backtrack_list[paren_depth] = the_token; + paren_depth += 1; + break; + case ")": + paren_depth -= 1; + break; + case "=>": + if ( + token_prv_expr.id === ")" + && paren_backtrack_list[paren_depth] + ) { + paren_backtrack_list[paren_depth].fart = the_token; + } + break; + } + +// The previous token is used to detect adjacency problems. + + token_prv = the_token; + +// The token_prv_expr token is a previous token that was not a comment. +// The token_prv_expr token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (token_prv.id !== "(comment)") { + token_prv_expr = token_prv; + } + return the_token; + } + +// Init global_dict and option_dict. + + option_set_item("ecma", true); + Object.keys(option_dict).sort().forEach(function (key) { + option_set_item(key, option_dict[key] === true); + }); + object_assign_from_list(global_dict, global_list, "user-defined"); + +// Scan first line for "#!" and ignore it. + + if (line_list[jslint_fudge].line_source.startsWith("#!")) { + line += 1; + state.mode_shebang = true; + } + token_1 = lex_token(); + state.mode_json = token_1.id === "{" || token_1.id === "["; + +// Lex/loop through each token until (end). + + while (true) { + if (lex_token().id === "(end)") { + break; + } + } +} + +function jslint_phase3_parse(state) { + +// PHASE 3. Parse into using the Pratt-parser. + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + + let anon = "anonymous"; // The guessed name for anonymous functions. + let { + artifact, + catch_list, + catch_stack, + export_dict, + function_list, + function_stack, + global_dict, + import_list, + is_equal, + option_dict, + property_dict, + stop, + syntax_dict, + tenure, + test_cause, + token_global, + token_list, + warn, + warn_at + } = state; + let catchage = catch_stack[0]; // The current catch-block. + let functionage = token_global; // The current function. + let mode_var; // "var" if using var; "let" if using let. + let token_ii = 0; // The number of the next token. + let token_now = token_global; // The current token being examined in + // ... the parse. + let token_nxt = token_global; // The next token to be examined in + // ... . + + function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if ( + token_now.identifier + && token_now.id !== "function" + && token_now.id !== "async" + ) { + anon = token_now.id; + } else if ( + token_now.id === "(string)" + && jslint_rgx_identifier.test(token_now.value) + ) { + anon = token_now.value; + } + +// Attempt to match token_nxt with an expected id. + + if (id !== undefined && token_nxt.id !== id) { + return ( + match === undefined + +// test_cause: +// ["{0:0}", "advance", "expected_a_b", "0", 2] + + ? stop("expected_a_b", token_nxt, id, artifact()) + +// test_cause: +// ["{\"aa\":0", "advance", "expected_a_b_from_c_d", "{", 1] + + : stop( + "expected_a_b_from_c_d", + token_nxt, + id, + artifact(match), + match.line, + artifact() + ) + ); + } + +// Promote the tokens, skipping comments. + + token_now = token_nxt; + while (true) { + token_nxt = token_list[token_ii]; + state.token_nxt = token_nxt; + token_ii += 1; + if (token_nxt.id !== "(comment)") { + if (token_nxt.id === "(end)") { + token_ii -= 1; + } + break; + } + if (state.mode_json) { + +// test_cause: +// ["[//]", "advance", "unexpected_a", "(comment)", 2] + + warn("unexpected_a"); + } + } + } + + function assignment(id) { + +// Create an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led_infix = function (left) { + const the_token = token_now; + let right; + the_token.arity = "assignment"; + right = parse_expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "preassign" + || right.arity === "postassign" + ) { + warn("unexpected_a", right); + } + check_mutation(left); + return the_token; + }; + return the_symbol; + } + + function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token_now; + if (special !== "body") { + functionage.statement_prv = the_block; + } + the_block.arity = "statement"; + the_block.body = special === "body"; + +// Top level function bodies may include the "use strict" pragma. + + if ( + special === "body" + && function_stack.length === 1 + && token_nxt.value === "use strict" + ) { + token_nxt.statement = true; + advance("(string)"); + advance(";"); + } + stmts = parse_statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option_dict.devel && special !== "ignore") { + +// test_cause: +// ["function aa(){}", "block", "empty_block", "{", 14] + + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; + } + + function check_left(left, right) { + +// Warn if the left is not one of these: +// ?. +// ?: +// e() +// e.b +// e[b] +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !check_left(left.expression[1]) + && !check_left(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "?." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right || token_nxt); + return false; + } + return true; + } + + function check_mutation(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + +// test_cause: +// ["0=0", "check_mutation", "bad_assignment_a", "0", 1] + + warn("bad_assignment_a", the_thing); + return false; + } + return true; + } + + function check_not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === token_global) { + +// test_cause: +// [" +// while(0){} +// ", "check_not_top_level", "unexpected_at_top_level_a", "while", 1] + + warn("unexpected_at_top_level_a", thing); + } + } + + function check_ordered(type, token_list) { + +// This function will warn if is unordered. + + token_list.reduce(function (aa, token) { + const bb = artifact(token); + if (!option_dict.unordered && aa > bb) { + warn("expected_a_b_before_c_d", token, type, bb, type, aa); + } + return bb; + }, ""); + } + + function check_ordered_case(case_list) { + +// This function will warn if is unordered. + + case_list.filter(noop).map(function (token) { + switch (token.identifier || token.id) { + case "(number)": + return { + order: 1, + token, + type: "number", + value: Number(artifact(token)) + }; + case "(string)": + return { + order: 2, + token, + type: "string", + value: artifact(token) + }; + case true: + return { + order: 3, + token, + type: "identifier", + value: artifact(token) + }; + } + }).reduce(function (aa, bb) { + if ( + +// PR-419 - Hide warning about unordered case-statements behind beta-flag. + + option_dict.beta + && !option_dict.unordered + && aa && bb + && ( + aa.order > bb.order + || (aa.order === bb.order && aa.value > bb.value) + ) + ) { + warn( + "expected_a_b_before_c_d", + bb.token, + `case-${bb.type}`, + bb.value, + `case-${aa.type}`, + aa.value + ); + } + return bb; + }, undefined); + } + + function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = token_nxt; + let the_value; + +// test_cause: +// ["do{}while()", "condition", "", "", 0] +// ["if(){}", "condition", "", "", 0] +// ["while(){}", "condition", "", "", 0] + + test_cause(""); + the_paren.free = true; + advance("("); + the_value = parse_expression(0); + advance(")"); + if (the_value.wrapped === true) { + +// test_cause: +// ["while((0)){}", "condition", "unexpected_a", "(", 6] + + warn("unexpected_a", the_paren); + } + +// Check for anticondition. + + switch (the_value.id) { + case "%": + warn("unexpected_a", the_value); + break; + case "&": + warn("unexpected_a", the_value); + break; + case "(number)": + warn("unexpected_a", the_value); + break; + case "(string)": + warn("unexpected_a", the_value); + break; + case "*": + warn("unexpected_a", the_value); + break; + case "+": + warn("unexpected_a", the_value); + break; + case "-": + warn("unexpected_a", the_value); + break; + case "/": + warn("unexpected_a", the_value); + break; + case "<<": + warn("unexpected_a", the_value); + break; + case ">>": + warn("unexpected_a", the_value); + break; + case ">>>": + warn("unexpected_a", the_value); + break; + case "?": + warn("unexpected_a", the_value); + break; + case "^": + warn("unexpected_a", the_value); + break; + case "typeof": + warn("unexpected_a", the_value); + break; + case "|": + warn("unexpected_a", the_value); + break; + case "~": + +// test_cause: +// ["if(0%0){}", "condition", "unexpected_a", "%", 5] +// ["if(0&0){}", "condition", "unexpected_a", "&", 5] +// ["if(0){}", "condition", "unexpected_a", "0", 4] +// ["if(0*0){}", "condition", "unexpected_a", "*", 5] +// ["if(0+0){}", "condition", "unexpected_a", "+", 5] +// ["if(0-0){}", "condition", "unexpected_a", "-", 5] +// ["if(0/0){}", "condition", "unexpected_a", "/", 5] +// ["if(0<<0){}", "condition", "unexpected_a", "<<", 5] +// ["if(0>>0){}", "condition", "unexpected_a", ">>", 5] +// ["if(0>>>0){}", "condition", "unexpected_a", ">>>", 5] +// ["if(0?0:0){}", "condition", "unexpected_a", "?", 5] +// ["if(0^0){}", "condition", "unexpected_a", "^", 5] +// ["if(0|0){}", "condition", "unexpected_a", "|", 5] +// ["if(\"aa\"){}", "condition", "unexpected_a", "aa", 4] +// ["if(typeof 0){}", "condition", "unexpected_a", "typeof", 4] +// ["if(~0){}", "condition", "unexpected_a", "~", 4] + + warn("unexpected_a", the_value); + break; + } + return the_value; + } + + function constant(id, type, value) { + +// Create a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud_prefix = ( + typeof value === "function" + ? value + : function () { + token_now.constant = true; + if (value !== undefined) { + token_now.value = value; + } + return token_now; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; + } + + function constant_Function() { + if (!option_dict.eval) { + +// test_cause: +// ["Function", "constant_Function", "unexpected_a", "Function", 1] + + warn("unexpected_a", token_now); + } else if (token_nxt.id !== "(") { + +// test_cause: +// [" +// /*jslint eval*/ +// Function +// ", "constant_Function", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "(", artifact()); + } + return token_now; + } + + function constant_arguments() { + +// test_cause: +// ["arguments", "constant_arguments", "unexpected_a", "arguments", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function constant_eval() { + if (!option_dict.eval) { + +// test_cause: +// ["eval", "constant_eval", "unexpected_a", "eval", 1] + + warn("unexpected_a", token_now); + } else if (token_nxt.id !== "(") { + +// test_cause: +// ["/*jslint eval*/\neval", "constant_eval", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "(", artifact()); + } + return token_now; + } + + function constant_ignore() { + +// test_cause: +// ["ignore", "constant_ignore", "unexpected_a", "ignore", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function constant_isInfinite() { + +// test_cause: +// ["isFinite", "constant_isInfinite", "expected_a_b", "isFinite", 1] + + warn("expected_a_b", token_now, "Number.isFinite", "isFinite"); + return token_now; + } + + function constant_isNaN() { + +// test_cause: +// ["isNaN(0)", "constant_isNaN", "number_isNaN", "isNaN", 1] + + warn("number_isNaN", token_now); + return token_now; + } + + function constant_this() { + if (!option_dict.this) { + +// test_cause: +// ["this", "constant_this", "unexpected_a", "this", 1] + + warn("unexpected_a", token_now); + } + return token_now; + } + + function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + let earlier; + let id = name.id; + +// Reserved words may not be enrolled. + + if (syntax_dict[id] !== undefined && id !== "ignore") { + +// test_cause: +// ["let undefined", "enroll", "reserved_a", "undefined", 5] + + warn("reserved_a", name); + return; + } + +// Has the name been enrolled in this context? + + earlier = functionage.context[id] || catchage.context[id]; + if (earlier) { + +// test_cause: +// ["let aa;let aa", "enroll", "redefinition_a_b", "1", 12] + + warn("redefinition_a_b", name, id, earlier.line); + return; + } + +// Has the name been enrolled in an outer context? + + function_stack.forEach(function ({ + context + }) { + earlier = context[id] || earlier; + }); + if (earlier && id === "ignore") { + if (earlier.role === "variable") { + +// test_cause: +// ["let ignore;function aa(ignore){}", "enroll", "redefinition_a_b", "1", 24] + + warn("redefinition_a_b", name, id, earlier.line); + } + } else if ( + earlier + && role !== "parameter" && role !== "function" + && (role !== "exception" || earlier.role !== "exception") + ) { + +// test_cause: +// [" +// function aa(){try{aa();}catch(aa){aa();}} +// ", "enroll", "redefinition_a_b", "1", 31] +// ["function aa(){var aa;}", "enroll", "redefinition_a_b", "1", 19] + + warn("redefinition_a_b", name, id, earlier.line); + } else if ( + option_dict.beta + && global_dict[id] + && role !== "parameter" + ) { + +// test_cause: +// ["let Array", "enroll", "redefinition_global_a_b", "Array", 5] + + warn("redefinition_global_a_b", name, global_dict[id], id); + } + +// Enroll it. + + Object.assign(name, { + dead: true, + init: false, + parent: ( + role === "exception" + ? catchage + : functionage + ), + readonly, + role, + used: 0 + }); + name.parent.context[id] = name; + } + + function infix(bp, id, f) { + +// Create an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led_infix = function (left) { + const the_token = token_now; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, parse_expression(bp)]; + return the_token; + }; + return the_symbol; + } + + function infix_dot(left) { + const the_token = token_now; + let name = token_nxt; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "flat" + && name.id !== "flatMap" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + +// test_cause: +// ["\"\".aa", "check_left", "unexpected_a", ".", 3] + + check_left(left, the_token); + } + if (!name.identifier) { + +// test_cause: +// ["aa.0", "infix_dot", "expected_identifier_a", "0", 4] + + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; + } + + function infix_fart_unwrapped() { + +// test_cause: +// ["aa=>0", "infix_fart_unwrapped", "wrap_fart_parameter", "=>", 3] + + return stop("wrap_fart_parameter", token_now); + } + + function infix_grave(left) { + const the_tick = prefix_tick(); + +// test_cause: +// ["0``", "check_left", "unexpected_a", "`", 2] + + check_left(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; + } + + function infix_lbracket(left) { + const the_token = token_now; + let name; + let the_subscript = parse_expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + name = survey(the_subscript); + +// PR-404 - Add new directive "subscript" to play nice with Google Closure. + + if (!option_dict.subscript && jslint_rgx_identifier.test(name)) { + +// test_cause: +// ["aa[`aa`]", "infix_lbracket", "subscript_a", "aa", 4] + + warn("subscript_a", the_subscript, name); + } + } + +// test_cause: +// ["0[0]", "check_left", "unexpected_a", "[", 2] + + check_left(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; + } + + function infix_lparen(left) { + const the_paren = token_now; + let ellipsis; + let the_argument; + if (left.id !== "function") { + +// test_cause: +// ["(0?0:0)()", "check_left", "unexpected_a", "(", 8] +// ["0()", "check_left", "unexpected_a", "(", 2] + + check_left(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (token_nxt.id !== ")") { + +// Parse/loop through each token in expression (...). + + while (true) { + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = parse_expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + +// test_cause: +// ["aa(0)", "infix_lparen", "free", "", 0] + + test_cause("free"); + the_paren.free = true; + if (the_argument.wrapped === true) { + +// test_cause: +// ["aa((0))", "infix_lparen", "unexpected_a", "(", 3] + + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + +// test_cause: +// ["aa()", "infix_lparen", "not_free", "", 0] +// ["aa(0,0)", "infix_lparen", "not_free", "", 0] + + test_cause("not_free"); + the_paren.free = false; + } + return the_paren; + } + + function infix_option_chain(left) { + const the_token = token_now; + let name = token_nxt; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + +// test_cause: +// ["(0+0)?.0", "infix_option_chain", "check_left", "", 0] + + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + test_cause("check_left"); + +// test_cause: +// ["(/./)?.0", "check_left", "unexpected_a", "?.", 6] +// ["\"aa\"?.0", "check_left", "unexpected_a", "?.", 5] +// ["aa=[]?.aa", "check_left", "unexpected_a", "?.", 6] + + check_left(left, the_token); + } + +// Issue #468 - Fix optional dynamic-property/function-call not recognized. + + if (name.id === "[" || name.id === "(") { + test_cause("dyn_prop_or_call"); + +// test_cause: +// ["aa?.(bb)", "infix_option_chain", "dyn_prop_or_call", "", 0] +// ["aa?.[bb]", "infix_option_chain", "dyn_prop_or_call", "", 0] + + return left; + } + if (!name.identifier) { + +// test_cause: +// ["aa?.0", "infix_option_chain", "expected_identifier_a", "0", 5] + + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; + } + + function infixr(bp, id) { + +// Create a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led_infix = function parse_infixr_led(left) { + const the_token = token_now; + +// test_cause: +// ["0**0", "parse_infixr_led", "led_infix", "", 0] + + test_cause("led_infix"); + the_token.arity = "binary"; + the_token.expression = [left, parse_expression(bp - 1)]; + return the_token; + }; + return the_symbol; + } + + function parse_expression(rbp, initial) { + +// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it +// is looking for ad hoc lint patterns. We add .fud_stmt to Pratt's model, which +// is like .nud_prefix except that it is only used on the first token of a +// statement. Having .fud_stmt makes it much easier to define statement-oriented +// languages like JavaScript. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// .nud_prefix Null denotation. The prefix handler. +// .fud_stmt First null denotation. The statement handler. +// .led_infix Left denotation. The infix/postfix handler. +// lbp Left binding power of infix operator. It tells us how strongly +// the operator binds to the argument at its left. +// rbp Right binding power. + +// It processes a nud_prefix (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop (it +// consumes tokens until it meets a token whose lbp <= rbp). Specifically, it +// means that it collects all tokens that bind together before returning to the +// operator that called it. It returns the expression's parse tree. + +// For example, "3 + 1 * 2 * 4 + 5" +// parses into +// { +// "id": "+", +// "expression": [ +// { +// "id": "+", +// "expression": [ +// { +// "id": "(number)", +// "value": "3" +// }, +// { +// "id": "*", +// "expression": [ +// { +// "id": "*", +// "expression": [ +// { +// "id": "(number)", +// "value": "1" +// }, +// { +// "id": "(number)", +// "value": "2" +// } +// ] +// }, +// { +// "id": "(number)", +// "value": "4" +// } +// ] +// } +// ] +// }, +// { +// "id": "(number)", +// "value": "5" +// } +// ] +// } + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement. + + if (!initial) { + advance(); + } + the_symbol = syntax_dict[token_now.id]; + if (the_symbol !== undefined && the_symbol.nud_prefix !== undefined) { + +// test_cause: +// ["0", "parse_expression", "symbol", "", 0] + + test_cause("symbol"); + left = the_symbol.nud_prefix(); + } else if (token_now.identifier) { + +// test_cause: +// ["aa", "parse_expression", "identifier", "", 0] + + test_cause("identifier"); + left = token_now; + left.arity = "variable"; + } else { + +// test_cause: +// ["!", "parse_expression", "unexpected_a", "(end)", 1] +// ["/./", "parse_expression", "unexpected_a", "/", 1] +// ["let aa=`${}`;", "parse_expression", "unexpected_a", "}", 11] + + return stop("unexpected_a", token_now); + } + +// Parse/loop through each symbol in expression. + + while (true) { + the_symbol = syntax_dict[token_nxt.id]; + if ( + the_symbol === undefined + || the_symbol.led_infix === undefined + || the_symbol.lbp <= rbp + ) { + break; + } + advance(); + left = the_symbol.led_infix(left); + } + return left; + } + + function parse_fart(the_fart) { + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + Object.assign(the_fart, { + arity: "binary", + context: empty(), + finally: 0, + level: functionage.level + 1, + loop: 0, + name: anon, + switch: 0, + try: 0 + }); + +// PR-384 - Relax warning "function_in_loop". +// +// if (functionage.loop > 0) { + +// // test_cause: +// // ["while(0){aa.map(()=>0);}", "parse_fart", "function_in_loop", "=>", 19] +// +// warn("function_in_loop", the_fart); +// } + +// Push the current function context and establish a new one. + + function_list.push(the_fart); + function_stack.push(functionage); + functionage = the_fart; + +// Parse the parameter list. + + prefix_function_parameter(the_fart); + advance("=>"); + +// The function's body is a block. + + if (token_nxt.id === "{") { + if (!option_dict.fart) { + +// test_cause: +// ["()=>{}", "parse_fart", "use_function_not_fart", "=>", 3] + + warn("use_function_not_fart", the_fart); + } + the_fart.block = block("body"); + } else if ( + syntax_dict[token_nxt.id] !== undefined + && syntax_dict[token_nxt.id].fud_stmt !== undefined + ) { + +// PR-384 - Bugfix - Fixes issue #379 - warn against naked-statement in fart. + +// test_cause: +// ["()=>delete aa", "parse_fart", "unexpected_a_after_b", "=>", 5] + + stop("unexpected_a_after_b", token_nxt, token_nxt.id, "=>"); + +// The function's body is an expression. + + } else { + the_fart.expression = parse_expression(0); + } + +// Restore the previous context. + + functionage = function_stack.pop(); + return the_fart; + } + + function parse_json() { + let container; + let is_dup; + let name; + let negative; + switch (token_nxt.id) { + case "(number)": + if (!jslint_rgx_json_number.test(token_nxt.value)) { + +// test_cause: +// ["[-.0]", "parse_json", "unexpected_a", ".", 3] +// ["[-0x0]", "parse_json", "unexpected_a", "0x0", 3] +// ["[.0]", "parse_json", "unexpected_a", ".", 2] +// ["[0x0]", "parse_json", "unexpected_a", "0x0", 2] + + warn("unexpected_a"); + } + advance("(number)"); + return token_now; + case "(string)": + if (token_nxt.quote !== "\"") { + +// test_cause: +// ["['']", "parse_json", "unexpected_a", "'", 2] + + warn("unexpected_a", token_nxt, token_nxt.quote); + } + advance("(string)"); + return token_now; + case "-": + negative = token_nxt; + negative.arity = "unary"; + advance("-"); + +// Recurse parse_json(). + + negative.expression = parse_json(); + return negative; + case "[": + +// test_cause: +// ["[]", "parse_json", "bracket", "", 0] + + test_cause("bracket"); + container = token_nxt; + container.expression = []; + advance("["); + if (token_nxt.id !== "]") { + while (true) { + +// Recurse parse_json(). + + container.expression.push(parse_json()); + if (token_nxt.id !== ",") { + +// test_cause: +// ["[0,0]", "parse_json", "comma", "", 0] + + test_cause("comma"); + break; + } + advance(","); + } + } + advance("]", container); + return container; + case "false": + case "null": + case "true": + +// test_cause: +// ["[false]", "parse_json", "constant", "", 0] +// ["[null]", "parse_json", "constant", "", 0] +// ["[true]", "parse_json", "constant", "", 0] + + test_cause("constant"); + advance(); + return token_now; + case "{": + +// test_cause: +// ["{}", "parse_json", "brace", "", 0] + + test_cause("brace"); + container = token_nxt; + +// Explicit empty-object required to detect "__proto__". + + is_dup = empty(); + container.expression = []; + advance("{"); + if (token_nxt.id !== "}") { + +// JSON +// Parse/loop through each property in {...}. + + while (true) { + if (token_nxt.quote !== "\"") { + +// test_cause: +// ["{0:0}", "parse_json", "unexpected_a", "0", 2] + + warn( + "unexpected_a", + token_nxt, + token_nxt.quote + ); + } + name = token_nxt; + advance("(string)"); + if (is_dup[token_now.value] !== undefined) { + +// test_cause: +// ["{\"aa\":0,\"aa\":0}", "parse_json", "duplicate_a", "aa", 9] + + warn("duplicate_a", token_now); + } else if (token_now.value === "__proto__") { + +// test_cause: +// ["{\"__proto__\":0}", "parse_json", "weird_property_a", "__proto__", 2] + + warn("weird_property_a", token_now); + } else { + is_dup[token_now.value] = token_now; + } + advance(":"); + container.expression.push( + +// Recurse parse_json(). + + Object.assign(parse_json(), { + label: name + }) + ); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance("}", container); + return container; + default: + +// test_cause: +// ["[undefined]", "parse_json", "unexpected_a", "undefined", 2] + + stop("unexpected_a"); + } + } + + function parse_statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token_now.identifier && token_nxt.id === ":") { + the_label = token_now; + if (the_label.id === "ignore") { + +// test_cause: +// ["ignore:", "parse_statement", "unexpected_a", "ignore", 1] + + warn("unexpected_a", the_label); + } + advance(":"); + switch (token_nxt.id) { + case "do": + case "for": + case "switch": + case "while": + +// test_cause: +// ["aa:do{}", "parse_statement", "the_statement_label", "do", 0] +// ["aa:for{}", "parse_statement", "the_statement_label", "for", 0] +// ["aa:switch{}", "parse_statement", "the_statement_label", "switch", 0] +// ["aa:while{}", "parse_statement", "the_statement_label", "while", 0] + + test_cause("the_statement_label", token_nxt.id); + enroll(the_label, "label", true); + the_label.dead = false; + the_label.init = true; + the_statement = parse_statement(); + functionage.statement_prv = the_statement; + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + +// test_cause: +// ["aa:", "parse_statement", "unexpected_label_a", "aa", 1] + + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token_now; + first.statement = true; + the_symbol = syntax_dict[first.id]; + if ( + the_symbol !== undefined + && the_symbol.fud_stmt !== undefined + +// PR-318 - Bugfix - Fixes issues #316, #317 - dynamic-import(). + + && !(the_symbol.id === "import" && token_nxt.id === "(") + ) { + the_symbol.disrupt = false; + the_symbol.statement = true; + token_now.arity = "statement"; + the_statement = the_symbol.fud_stmt(); + functionage.statement_prv = the_statement; + } else { + +// It is an expression statement. + + the_statement = parse_expression(0, true); + functionage.statement_prv = the_statement; + if (the_statement.wrapped && the_statement.id !== "(") { + +// test_cause: +// ["(0)", "parse_statement", "unexpected_a", "(", 1] + + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; + } + + function parse_statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const statement_list = []; + let a_statement; + let disrupt = false; + +// Parse/loop each statement until a statement-terminator is reached. + + while (true) { + switch (token_nxt.id) { + case "(end)": + case "case": + case "default": + case "else": + case "}": + +// test_cause: +// [";", "parse_statements", "closer", "", 0] +// ["case", "parse_statements", "closer", "", 0] +// ["default", "parse_statements", "closer", "", 0] +// ["else", "parse_statements", "closer", "", 0] +// ["}", "parse_statements", "closer", "", 0] + + test_cause("closer"); + return statement_list; + } + a_statement = parse_statement(); + statement_list.push(a_statement); + if (disrupt) { + +// test_cause: +// ["while(0){break;0;}", "parse_statements", "unreachable_a", "0", 16] + + warn("unreachable_a", a_statement); + } + disrupt = a_statement.disrupt; + } + } + + function postassign(id) { + +// Create one of the postassign operators. + + const the_symbol = symbol(id, 150); + the_symbol.led_infix = function (left) { + token_now.expression = left; + token_now.arity = "postassign"; + check_mutation(token_now.expression); + return token_now; + }; + return the_symbol; + } + + function preassign(id) { + +// Create one of the preassign operators. + + const the_symbol = symbol(id); + the_symbol.nud_prefix = function () { + const the_token = token_now; + the_token.arity = "preassign"; + the_token.expression = parse_expression(150); + check_mutation(the_token.expression); + return the_token; + }; + return the_symbol; + } + + function prefix(id, f) { + +// Create a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud_prefix = function () { + const the_token = token_now; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = parse_expression(150); + return the_token; + }; + return the_symbol; + } + + function prefix_assign_divide() { + +// test_cause: +// ["/=", "prefix_assign_divide", "expected_a_b", "/=", 1] + + stop("expected_a_b", token_now, "/\\=", "/="); + } + + function prefix_async() { + let the_async = token_now; + let the_function; + token_nxt.arity = the_async.arity; + +// PR-414 - Parse async fart. + + if (token_nxt.fart) { + advance("("); + the_function = Object.assign(token_now.fart, { + async: 1 + }); + if (!option_dict.fart) { + +// test_cause: +// ["async()=>0", "prefix_async", "use_function_not_fart", "=>", 8] + + warn("use_function_not_fart", the_function); + } + prefix_lparen(); + +// Parse async function. + + } else { + advance("function"); + the_function = Object.assign(token_now, { + async: 1 + }); + prefix_function(); + } + if (the_function.async === 1) { + +// test_cause: +// [" +// async function aa(){} +// ", "prefix_async", "missing_await_statement", "function", 7] + + warn("missing_await_statement", the_function); + } + return the_function; + } + + function prefix_await() { + const the_await = token_now; + +// PR-370 - Add top-level-await support. + + if (functionage.async === 0 && functionage !== token_global) { + +// test_cause: +// ["function aa(){aa=await 0;}", "prefix_await", "unexpected_a", "await", 18] +// ["function aa(){await 0;}", "prefix_await", "unexpected_a", "await", 15] + + warn("unexpected_a", the_await); + } else { + functionage.async += 1; + } + if (the_await.arity === "statement") { + +// PR-405 - Bugfix - fix expression after "await" mis-identified as statement. + + the_await.expression = parse_expression(150); + semicolon(); + } else { + the_await.expression = parse_expression(150); + } + return the_await; + } + + function prefix_fart() { + +// test_cause: +// ["=>0", "prefix_fart", "expected_a_before_b", "=>", 1] + + return stop("expected_a_before_b", token_now, "()", "=>"); + } + + function prefix_function(the_function) { + let name = the_function && the_function.name; + if (the_function === undefined) { + the_function = token_now; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!token_nxt.identifier) { + +// test_cause: +// ["function(){}", "prefix_function", "expected_identifier_a", "(", 9] +// ["function*aa(){}", "prefix_function", "expected_identifier_a", "*", 9] + + return stop("expected_identifier_a"); + } + name = token_nxt; + enroll(name, "variable", true); + the_function.name = Object.assign(name, { + calls: empty(), + +// PR-331 - Bugfix - Fixes issue #272 - function hoisting not allowed. + + dead: false, + init: true + }); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + the_function.name = anon; + if (token_nxt.identifier) { + name = token_nxt; + the_function.name = name; + advance(); + } + } + } + +// Probably deadcode. +// if (mode_mega) { +// warn("unexpected_a", the_function); +// } +// jslint_assert(!mode_mega, `Expected !mode_mega.`); + +// PR-378 - Relax warning "function_in_loop". +// +// // Don't create functions in loops. It is inefficient, and it can lead to +// // scoping errors. +// +// if (functionage.loop > 0) { +// +// // test_cause: +// // [" +// // while(0){aa.map(function(){});} +// // ", "prefix_function", "function_in_loop", "function", 17] +// +// warn("function_in_loop", the_function); +// } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + Object.assign(the_function, { + async: the_function.async || 0, + context: empty(), + finally: 0, + level: functionage.level + 1, + loop: 0, + statement_prv: undefined, + switch: 0, + try: 0 + }); + if (the_function.arity !== "statement" && typeof name === "object") { + +// test_cause: +// ["let aa=function bb(){return;};", "prefix_function", "expression", "bb", 0] + + test_cause("expression", name.id); + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// PR-334 - Bugfix - fix function-redefinition not warned inside function-call. +// Push the current function context and establish a new one. + + function_list.push(the_function); + function_stack.push(functionage); + functionage = the_function; + +// Parse the parameter list. + + advance("("); + token_now.arity = "function"; + prefix_function_parameter(the_function); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && token_nxt.line === token_now.line + ) { + +// test_cause: +// ["function aa(){}0", "prefix_function", "unexpected_a", "0", 16] + + return stop("unexpected_a"); + } + if ( + token_nxt.id === "." + || token_nxt.id === "?." + +// PR-459 - Allow destructuring-assignment after function-definition. + + // || token_nxt.id === "[" + ) { + +// test_cause: +// ["function aa(){}\n.aa", "prefix_function", "unexpected_a", ".", 1] +// ["function aa(){}\n?.aa", "prefix_function", "unexpected_a", "?.", 1] + + warn("unexpected_a"); + } + +// Check functions are ordered. + + check_ordered( + "function", + function_list.slice( + function_list.indexOf(the_function) + 1 + ).map(function ({ + level, + name + }) { + return (level === the_function.level + 1) && name; + }).filter(function (name) { + return option_dict.beta && name && name.id; + }) + ); + +// Restore the previous context. + + functionage = function_stack.pop(); + return the_function; + } + + function prefix_function_parameter(the_function) { + +// This function will parse input at beginning of + + let optional; + let parameters = []; + let signature = ["("]; + let subparam; + function param_enroll(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + +// test_cause: +// ["([aa])=>0", "param_enroll", "use_function_not_fart", "=>", 7] +// ["({aa})=>0", "param_enroll", "use_function_not_fart", "=>", 7] + + if (the_function.id === "=>" && !option_dict.fart) { + warn("use_function_not_fart", the_function); + } + +// Recurse param_enroll(). + + name.names.forEach(param_enroll); + } + } + function param_parse() { + let ellipsis = false; + let param; + if (token_nxt.id === "{") { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,{}){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + param = token_nxt; + param.names = []; + advance("{"); + signature.push("{"); + while (true) { + subparam = token_nxt; + if (!subparam.identifier) { + +// test_cause: +// ["function aa(aa=0,{}){}", "param_parse", "expected_identifier_a", "}", 19] +// ["function aa({0}){}", "param_parse", "expected_identifier_a", "0", 14] + + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (token_nxt.id === ":") { + advance(":"); + advance(); + token_now.label = subparam; + subparam = token_now; + if (!subparam.identifier) { + +// test_cause: +// ["function aa({aa:0}){}", "param_parse", "expected_identifier_a", "}", 18] + + return stop( + "expected_identifier_a", + token_nxt + ); + } + } + +// test_cause: +// ["function aa({aa=aa},aa){}", "param_parse", "equal", "", 0] + + test_cause("equal"); + if (token_nxt.id === "=") { + advance("="); + subparam.expression = parse_expression(); + param.open = true; + } + param.names.push(subparam); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + } else { + break; + } + } + parameters.push(param); + +// test_cause: +// [" +// function aa({bb,aa}){} +// ", "check_ordered", "expected_a_b_before_c_d", "aa", 17] + + check_ordered("parameter", param.names); + advance("}"); + signature.push("}"); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } else if (token_nxt.id === "[") { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,[]){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + param = token_nxt; + param.names = []; + advance("["); + signature.push("[]"); + while (true) { + subparam = token_nxt; + if (!subparam.identifier) { + +// test_cause: +// ["function aa(aa=0,[]){}", "param_parse", "expected_identifier_a", "]", 19] + + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + +// test_cause: +// ["function aa([aa=aa],aa){}", "param_parse", "id", "", 0] + + test_cause("id"); + if (token_nxt.id === "=") { + advance("="); + subparam.expression = parse_expression(); + param.open = true; + } + if (token_nxt.id === ",") { + advance(","); + } else { + break; + } + } + parameters.push(param); + advance("]"); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } else { + if (token_nxt.id === "...") { + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,...){}", "param_parse", "required_a_optional_b", "aa", 21] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + } + if (!token_nxt.identifier) { + +// test_cause: +// ["function aa(0){}", "param_parse", "expected_identifier_a", "0", 13] + + return stop("expected_identifier_a"); + } + param = token_nxt; + parameters.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (token_nxt.id === "=") { + optional = param; + advance("="); + param.expression = parse_expression(0); + } else { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,bb){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } + } + } + +// test_cause: +// ["function aa(){}", "prefix_function_parameter", "opener", "(", 0] + + test_cause("opener", token_now.id); + token_now.free = false; + if (token_nxt.id !== ")" && token_nxt.id !== "(end)") { + param_parse(); + } + advance(")"); + signature.push(")"); + parameters.forEach(param_enroll); + the_function.parameters = parameters; + the_function.signature = signature.join(""); + } + + function prefix_lbrace() { + const seen = empty(); + const the_brace = token_now; + let extra; + let full; + let id; + let name; + let the_colon; + let value; + the_brace.expression = []; + if (token_nxt.id !== "}") { + +// Parse/loop through each property in {...}. + + while (true) { + name = token_nxt; + advance(); + if ( + (name.id === "get" || name.id === "set") + && token_nxt.identifier + ) { + if (!option_dict.getset) { + +// test_cause: +// ["aa={get aa(){}}", "prefix_lbrace", "unexpected_a", "get", 5] + + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + token_nxt.id; + name = token_nxt; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + +// test_cause: +// ["aa={get aa(){},get aa(){}}", "prefix_lbrace", "duplicate_a", "aa", 20] + + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else if (name.id === "`") { + +// test_cause: +// ["aa={`aa`:0}", "prefix_lbrace", "unexpected_a", "`", 5] + + stop("unexpected_a", name); + + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + +// test_cause: +// ["aa={aa,aa}", "prefix_lbrace", "duplicate_a", "aa", 8] + + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (token_nxt.id === "}" || token_nxt.id === ",") { + if (typeof extra === "string") { + +// test_cause: +// ["aa={get aa}", "prefix_lbrace", "closer", "", 0] + + test_cause("closer"); + advance("("); + } + value = parse_expression(Infinity, true); + } else if (token_nxt.id === "(") { + +// test_cause: +// ["aa={aa()}", "prefix_lbrace", "paren", "", 0] +// ["aa={get aa(){}}", "prefix_lbrace", "paren", "", 0] + + test_cause("paren"); + value = prefix_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + +// test_cause: +// ["aa={get aa.aa}", "prefix_lbrace", "paren", "", 0] + + test_cause("paren"); + advance("("); + } + the_colon = token_nxt; + advance(":"); + value = parse_expression(0); + if ( + value.id === name.id + && value.id !== "function" + ) { + +// test_cause: +// ["aa={aa:aa}", "prefix_lbrace", "unexpected_a", ": aa", 7] + + warn("unexpected_a", the_colon, ": " + name.id); + } + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + +// test_cause: +// ["aa={\"aa\":0}", "prefix_lbrace", "colon", "", 0] + + test_cause("colon"); + advance(":"); + value = parse_expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (token_nxt.id !== ",") { + break; + } + +// test_cause: +// ["aa={\"aa\":0,\"bb\":0}", "prefix_lbrace", "comma", "", 0] + + test_cause("comma"); + advance(","); + if (token_nxt.id === "}") { + +// test_cause: +// ["let aa={aa:0,}", "prefix_lbrace", "unexpected_a", ",", 13] + + warn("unexpected_a", token_now); + break; + } + } + } + +// test_cause: +// ["aa={bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8] + + check_ordered( + "property", + the_brace.expression.map(function ({ + label + }) { + return label; + }) + ); + advance("}"); + return the_brace; + } + + function prefix_lbracket() { + const the_token = token_now; + let element; + let ellipsis; + the_token.expression = []; + if (token_nxt.id !== "]") { + +// Parse/loop through each element in [...]. + + while (true) { + ellipsis = false; + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + element = parse_expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (token_nxt.id !== ",") { + break; + } + advance(","); + if (token_nxt.id === "]") { + +// test_cause: +// ["let aa=[0,]", "prefix_lbracket", "unexpected_a", ",", 10] + + warn("unexpected_a", token_now); + break; + } + } + } + advance("]"); + return the_token; + } + + function prefix_lparen() { + let the_paren = token_now; + let the_value; + +// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart. + + if (token_now.fart) { + return parse_fart(token_now.fart); + } + +// test_cause: +// ["(0)", "prefix_lparen", "expr", "", 0] + + test_cause("expr"); + the_paren.free = true; + the_value = parse_expression(0); + if (the_value.wrapped === true) { + +// test_cause: +// ["((0))", "prefix_lparen", "unexpected_a", "(", 1] + + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + return the_value; + } + + function prefix_new() { + const the_new = token_now; + let right; + right = parse_expression(160); + if (token_nxt.id !== "(") { + +// test_cause: +// ["new aa", "prefix_new", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "()", artifact()); + } + the_new.expression = right; + return the_new; + } + + function prefix_tick() { + const the_tick = token_now; + the_tick.value = []; + the_tick.expression = []; + if (token_nxt.id !== "`") { + +// Parse/loop through each token in `${...}`. + + while (true) { + advance("(string)"); + the_tick.value.push(token_now); + if (token_nxt.id !== "${") { + break; + } + advance("${"); + +// test_cause: +// ["let aa=`${}`;", "prefix_tick", "${", "", 0] + + test_cause("${"); + the_tick.expression.push(parse_expression(0)); + advance("}"); + } + } + advance("`"); + return the_tick; + } + + function prefix_void() { + const the_void = token_now; + +// test_cause: +// ["void 0", "prefix_void", "unexpected_a", "void", 1] +// ["void", "prefix_void", "unexpected_a", "void", 1] + + warn("unexpected_a", the_void); + the_void.expression = parse_expression(0); + return the_void; + } + + function semicolon() { + +// Try to match a semicolon. + + if (token_nxt.id === ";") { + advance(";"); + } else { + +// test_cause: +// ["0", "semicolon", "expected_a_b", "(end)", 1] + + warn_at( + "expected_a_b", + token_now.line, + token_now.thru + 1, + ";", + artifact() + ); + } + anon = "anonymous"; + } + + function stmt(id, fud_stmt) { + +// Create a statement. + + const the_symbol = symbol(id); + the_symbol.fud_stmt = fud_stmt; + return the_symbol; + } + + function stmt_break() { + const the_break = token_now; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + +// test_cause: +// ["break", "stmt_break", "unexpected_a", "break", 1] + + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (token_nxt.identifier && token_now.line === token_nxt.line) { + the_label = functionage.context[token_nxt.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + if (the_label !== undefined && the_label.dead) { + +// test_cause: +// ["aa:{function aa(aa){break aa;}}", "stmt_break", "out_of_scope_a", "aa", 27] + + warn("out_of_scope_a"); + } else { + +// test_cause: +// ["aa:{break aa;}", "stmt_break", "not_label_a", "aa", 11] + + warn("not_label_a"); + } + } else { + the_label.used += 1; + } + the_break.label = token_nxt; + advance(); + } + advance(";"); + return the_break; + } + + function stmt_continue() { + const the_continue = token_now; + if (functionage.loop < 1 || functionage.finally > 0) { + +// test_cause: +// ["continue", "stmt_continue", "unexpected_a", "continue", 1] +// [" +// function aa(){while(0){try{}finally{continue}}} +// ", "stmt_continue", "unexpected_a", "continue", 37] + + warn("unexpected_a", the_continue); + } + check_not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; + } + + function stmt_debugger() { + const the_debug = token_now; + if (!option_dict.devel) { + +// test_cause: +// ["debugger", "stmt_debugger", "unexpected_a", "debugger", 1] + + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; + } + + function stmt_delete() { + const the_token = token_now; + const the_value = parse_expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + +// test_cause: +// ["delete 0", "stmt_delete", "expected_a_b", "0", 8] + + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; + } + + function stmt_do() { + const the_do = token_now; + check_not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + +// test_cause: +// ["function aa(){do{break;}while(0)}", "stmt_do", "weird_loop", "do", 15] + + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; + } + + function stmt_export() { + let export_list = []; + let the_export = token_now; + let the_id; + let the_name; + let the_thing; + + the_export.expression = []; + if (token_nxt.id === "default") { + if (export_dict.default !== undefined) { + +// test_cause: +// [" +// export default 0;export default 0 +// ", "stmt_export", "duplicate_a", "default", 25] + + warn("duplicate_a"); + } + advance("default"); + the_thing = parse_expression(0); + if ( + the_thing.id !== "(" + || the_thing.expression[0].id !== "." + || the_thing.expression[0].expression.id !== "Object" + || the_thing.expression[0].name.id !== "freeze" + ) { + +// test_cause: +// ["export default {}", "stmt_export", "freeze_exports", "{", 16] + + warn("freeze_exports", the_thing); + +// PR-301 - Bugfix - Fixes issues #282 - optional-semicolon. + + } else { + +// test_cause: +// [" +// export default Object.freeze({}) +// ", "semicolon", "expected_a_b", "(end)", 32] + + semicolon(); + } + export_dict.default = the_thing; + the_export.expression.push(the_thing); + } else { + +// PR-439 - Add grammar for "export async function ...". + + if (token_nxt.id === "function" || token_nxt.id === "async") { + +// test_cause: +// ["export async function aa(){}", "stmt_export", "freeze_exports", "async", 8] +// ["export function aa(){}", "stmt_export", "freeze_exports", "function", 8] + + warn("freeze_exports"); + the_thing = parse_statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (export_dict[the_id] !== undefined) { + +// test_cause: +// [" +// let aa;export{aa};export function aa(){} +// ", "stmt_export", "duplicate_a", "aa", 35] + + warn("duplicate_a", the_name); + } + export_dict[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + token_nxt.id === "var" + || token_nxt.id === "let" + || token_nxt.id === "const" + ) { + +// test_cause: +// ["export const", "stmt_export", "unexpected_a", "const", 8] +// ["export let", "stmt_export", "unexpected_a", "let", 8] +// ["export var", "stmt_export", "unexpected_a", "var", 8] + + warn("unexpected_a"); + parse_statement(); + } else if (token_nxt.id === "{") { + +// test_cause: +// ["export {}", "stmt_export", "advance{", "", 0] + + test_cause("advance{"); + advance("{"); + while (true) { + if (!token_nxt.identifier) { + +// test_cause: +// ["export {}", "stmt_export", "expected_identifier_a", "}", 9] + + stop("expected_identifier_a"); + } + the_id = token_nxt.id; + export_list.push(token_nxt); + the_name = token_global.context[the_id]; + if (the_name === undefined) { + +// test_cause: +// ["export {aa}", "stmt_export", "unexpected_a", "aa", 9] + + warn("unexpected_a"); + } else { + the_name.used += 1; + if (export_dict[the_id] !== undefined) { + +// test_cause: +// ["let aa;export{aa,aa}", "stmt_export", "duplicate_a", "aa", 18] + + warn("duplicate_a"); + } + export_dict[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + if (token_nxt.id === ",") { + advance(","); + } else { + break; + } + } + +// PR-439 - Check exported properties are ordered. + +// test_cause: +// ["export {bb, aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 13] + + check_ordered("export", export_list); + advance("}"); + semicolon(); + } else { + +// test_cause: +// ["export", "stmt_export", "unexpected_a", "(end)", 1] + + stop("unexpected_a"); + } + } + state.mode_module = true; + return the_export; + } + + function stmt_for() { + let first; + let the_for = token_now; + if (!option_dict.for) { + +// test_cause: +// ["for", "stmt_for", "unexpected_a", "for", 1] + + warn("unexpected_a", the_for); + } + check_not_top_level(the_for); + functionage.loop += 1; + advance("("); + token_now.free = true; + if (token_nxt.id === ";") { + +// test_cause: +// ["for(;;){}", "stmt_for", "expected_a_b", "for (;", 1] + + return stop("expected_a_b", the_for, "while (", "for (;"); + } + switch (token_nxt.id) { + case "const": + case "let": + case "var": + +// test_cause: +// ["for(const aa in aa){}", "stmt_for", "unexpected_a", "const", 5] + + return stop("unexpected_a"); + } + first = parse_expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + +// test_cause: +// ["for(0 in aa){}", "stmt_for", "bad_assignment_a", "0", 5] + + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = parse_expression(0); + advance(";"); + the_for.inc = parse_expression(0); + if (the_for.inc.id === "++") { + +// test_cause: +// ["for(aa;aa;aa++){}", "stmt_for", "expected_a_b", "++", 13] + + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + +// test_cause: +// [" +// /*jslint for*/ +// function aa(bb,cc){for(0;0;0){break;}} +// ", "stmt_for", "weird_loop", "for", 20] + + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; + } + + function stmt_if() { + const the_if = token_now; + let the_else; + the_if.expression = condition(); + the_if.block = block(); + if (token_nxt.id === "else") { + advance("else"); + the_else = token_now; + the_if.else = ( + token_nxt.id === "if" + ? parse_statement() + : block() + ); + +// test_cause: +// ["if(0){0}else if(0){0}", "stmt_if", "else", "", 0] +// ["if(0){0}else{0}", "stmt_if", "else", "", 0] + + test_cause("else"); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + +// test_cause: +// ["if(0){break;}else{break;}", "stmt_if", "disrupt", "", 0] + + test_cause("disrupt"); + the_if.disrupt = true; + } else { + +// test_cause: +// ["if(0){break;}else{}", "stmt_if", "unexpected_a", "else", 14] + + warn("unexpected_a", the_else); + } + } + } + return the_if; + } + + function stmt_import() { + const the_import = token_now; + let name; + let names; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// if (typeof state.mode_module === "object") { +// +// // test_cause: +// // [" +// // /*global aa*/ +// // import aa from "aa" +// // ", "stmt_import", "unexpected_directive_a", "global", 1] +// +// warn( +// "unexpected_directive_a", +// state.mode_module, +// state.mode_module.directive +// ); +// } + + state.mode_module = true; + +// PR-436 - Add grammar for side-effect import-statement. + + if (token_nxt.id === "(string)") { + +// test_cause: +// ["import \"./aa.mjs\";", "stmt_import", "import_side_effect", "", 0] + + test_cause("import_side_effect"); + warn("expected_a_b", token_nxt, "{", artifact()); + advance(); + semicolon(); + return the_import; + } + if (token_nxt.identifier) { + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// ["import ignore from \"aa\"", "stmt_import", "unexpected_a", "ignore", 8] + + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + names = []; + advance("{"); + if (token_nxt.id !== "}") { + while (true) { + if (!token_nxt.identifier) { + +// test_cause: +// ["import {", "stmt_import", "expected_identifier_a", "(end)", 1] + + stop("expected_identifier_a"); + } + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// ["import {ignore} from \"aa\"", "stmt_import", "unexpected_a", "ignore", 9] + + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token_now; + if (!jslint_rgx_module.test(token_now.value)) { + +// test_cause: +// ["import aa from \"!aa\"", "stmt_import", "bad_module_name_a", "!aa", 16] + + warn("bad_module_name_a", token_now); + } + import_list.push(token_now.value); + semicolon(); + return the_import; + } + + function stmt_lbrace() { + +// test_cause: +// [";{}", "stmt_lbrace", "naked_block", "{", 2] +// ["class aa{}", "stmt_lbrace", "naked_block", "{", 9] + + warn("naked_block", token_now); + return block("naked"); + } + + function stmt_return() { + const the_return = token_now; + check_not_top_level(the_return); + if (functionage.finally > 0) { + +// test_cause: +// [" +// function aa(){try{}finally{return;}} +// ", "stmt_return", "unexpected_a", "return", 28] + + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (token_nxt.id !== ";" && the_return.line === token_nxt.line) { + the_return.expression = parse_expression(10); + } + advance(";"); + return the_return; + } + + function stmt_semicolon() { + +// test_cause: +// [";", "stmt_semicolon", "unexpected_a", ";", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function stmt_switch() { + const the_cases = []; + const the_switch = token_now; + let dups = []; + let exp; + let last; + let stmts; + let the_case; + let the_default; + let the_disrupt = true; + let the_last; + function is_dup(thing) { + return is_equal(thing, exp); + } + check_not_top_level(the_switch); + if (functionage.finally > 0) { + +// test_cause: +// [" +// function aa(){try{}finally{switch(0){}}} +// ", "stmt_switch", "unexpected_a", "switch", 28] + + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token_now.free = true; + the_switch.expression = parse_expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + while (true) { + +// Loop through cases with breaks. + + the_case = token_nxt; + the_case.arity = "statement"; + the_case.expression = []; + while (true) { + +// Loop through fallthrough cases. + + advance("case"); + token_now.switch = true; + exp = parse_expression(0); + if (dups.some(is_dup)) { + +// test_cause: +// [" +// switch(0){case 0:break;case 0:break} +// ", "stmt_switch", "unexpected_a", "0", 29] + + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (token_nxt.id !== "case") { + break; + } + } + +// test_cause: +// [" +// switch(0){case 1:case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 23] +// [" +// switch(0){case "aa":case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 26] +// [" +// switch(0){case "bb":case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 26] +// [" +// switch(0){case aa:case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24] +// [" +// switch(0){case bb:case aa:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24] + + check_ordered_case(the_case.expression); + stmts = parse_statements(); + if (stmts.length < 1) { + +// test_cause: +// [" +// switch(0){case 0:default:} +// ", "stmt_switch", "expected_statements_a", "default", 18] +// ["switch(0){case 0:}", "stmt_switch", "expected_statements_a", "}", 18] + + warn("expected_statements_a"); + +// PR-359 - Bugfix - Fixes issue #358 - switch-statement crashes jslint. + + break; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn("expected_a_before_b", token_nxt, "break;", artifact()); + } + if (token_nxt.id !== "case") { + break; + } + } + +// test_cause: +// [" +// switch(0){case 1:break;case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 29] +// [" +// switch(0){case "aa":break;case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 32] +// [" +// switch(0){case "bb":break;case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 32] +// [" +// switch(0){case aa:break;case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30] +// [" +// switch(0){case bb:break;case aa:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30] + + check_ordered_case(the_cases.map(function ({ + expression + }) { + return expression[0]; + })); + dups = undefined; + if (token_nxt.id === "default") { + the_default = token_nxt; + advance("default"); + token_now.switch = true; + advance(":"); + the_switch.else = parse_statements(); + if (the_switch.else.length < 1) { + +// test_cause: +// [" +// switch(0){case 0:break;default:} +// ", "stmt_switch", "unexpected_a", "default", 24] + + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + the_last = the_switch.else[ + the_switch.else.length - 1 + ]; + if ( + the_last.id === "break" + && the_last.label === undefined + ) { + +// test_cause: +// [" +// switch(0){case 0:break;default:break;} +// ", "stmt_switch", "unexpected_a", "break", 32] + + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; + } + + function stmt_throw() { + const the_throw = token_now; + the_throw.disrupt = true; + the_throw.expression = parse_expression(10); + semicolon(); + if (functionage.try > 0) { + +// test_cause: +// ["try{throw 0}catch(){}", "stmt_throw", "unexpected_a", "throw", 5] + + warn("unexpected_a", the_throw); + } + return the_throw; + } + + function stmt_try() { + const the_try = token_now; + let ignored; + let the_catch; + let the_disrupt; + if (functionage.try > 0) { + +// test_cause: +// ["try{try{}catch(){}}catch(){}", "stmt_try", "unexpected_a", "try", 5] + + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (token_nxt.id === "catch") { + ignored = "ignore"; + the_catch = token_nxt; + the_try.catch = the_catch; + advance("catch"); + +// Create new catch-scope for catch-parameter. + + catch_stack.push(catchage); + catchage = the_catch; + catch_list.push(catchage); + the_catch.context = empty(); + if (token_nxt.id === "(") { + advance("("); + if (!token_nxt.identifier) { + +// test_cause: +// ["try{}catch(){}", "stmt_try", "expected_identifier_a", ")", 12] + + return stop("expected_identifier_a"); + } + if (token_nxt.id !== "ignore") { + ignored = undefined; + the_catch.name = token_nxt; + enroll(token_nxt, "exception", true); + } + advance(); + advance(")"); + } + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + +// Restore previous catch-scope after catch-block. + + catchage = catch_stack.pop(); + +// PR-404 - Relax warning about missing `catch` in `try...finally` statement. +// +// } else { +// +// // test_cause: +// // ["try{}finally{break;}", "stmt_try", "expected_a_before_b", "finally", 6] +// +// warn("expected_a_before_b", token_nxt, "catch", artifact()); + + } + if (token_nxt.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; + } + + function stmt_var() { + let ellipsis; + let mode_const; + let name; + let the_brace; + let the_bracket; + let the_variable = token_now; + let variable_prv; + mode_const = the_variable.id === "const"; + the_variable.names = []; + +// A program may use var or let, but not both. + + if (!mode_const) { + if (mode_var === undefined) { + mode_var = the_variable.id; + } else if (the_variable.id !== mode_var) { + +// test_cause: +// ["let aa;var aa", "stmt_var", "expected_a_b", "var", 8] + + warn("expected_a_b", the_variable, mode_var, the_variable.id); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + +// test_cause: +// ["switch(0){case 0:var aa}", "stmt_var", "var_switch", "var", 18] + + warn("var_switch", the_variable); + } + switch ( + Boolean(functionage.statement_prv) + && functionage.statement_prv.id + ) { + case "const": + case "let": + case "var": + +// test_cause: +// ["const aa=0;const bb=0;", "stmt_var", "var_prv", "const", 0] +// ["let aa=0;let bb=0;", "stmt_var", "var_prv", "let", 0] +// ["var aa=0;var bb=0;", "stmt_var", "var_prv", "var", 0] + + test_cause("var_prv", functionage.statement_prv.id); + variable_prv = functionage.statement_prv; + break; + case "import": + +// test_cause: +// ["import aa from \"aa\";\nlet bb=0;", "stmt_var", "import_prv", "", 0] + + test_cause("import_prv"); + break; + case false: + break; + default: + if ( + (option_dict.beta && !option_dict.variable) + || the_variable.id === "var" + ) { + +// test_cause: +// ["console.log();let aa=0;", "stmt_var", "var_on_top", "let", 15] +// ["console.log();var aa=0;", "stmt_var", "var_on_top", "var", 15] +// ["try{aa();}catch(aa){var aa=0;}", "stmt_var", "var_on_top", "var", 21] +// ["while(0){var aa;}", "stmt_var", "var_on_top", "var", 10] + + warn("var_on_top", token_now); + } + } + while (true) { + if (token_nxt.id === "{") { + if (the_variable.id === "var") { + +// test_cause: +// ["var{aa}=0", "stmt_var", "unexpected_a", "var", 1] + + warn("unexpected_a", the_variable); + } + the_brace = token_nxt; + advance("{"); + while (true) { + name = token_nxt; + if (!name.identifier) { + +// test_cause: +// ["let {0}", "stmt_var", "expected_identifier_a", "0", 6] + + return stop("expected_identifier_a"); + } + survey(name); + advance(); + if (token_nxt.id === ":") { + advance(":"); + if (!token_nxt.identifier) { + +// test_cause: +// ["let {aa:0}", "stmt_var", "expected_identifier_a", "0", 9] +// ["let {aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 9] + + return stop("expected_identifier_a"); + } + +// PR-363 - Bugfix +// Add test against false-warning in code +// '/*jslint node*/\nlet {aa:bb} = {}; bb();'. +// +// token_nxt.label = name; +// the_variable.names.push(token_nxt); +// enroll(token_nxt, "variable", mode_const); + + name = token_nxt; + the_variable.names.push(name); + survey(name); + enroll(name, "variable", mode_const); + + advance(); + the_brace.open = true; + } else { + the_variable.names.push(name); + enroll(name, "variable", mode_const); + } + name.dead = false; + name.init = true; + if (token_nxt.id === "=") { + +// test_cause: +// ["let {aa=0}", "stmt_var", "assign", "", 0] + + test_cause("assign"); + advance("="); + name.expression = parse_expression(); + the_brace.open = true; + } + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + +// test_cause: +// ["let{bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8] + + check_ordered(the_variable.id, the_variable.names); + advance("}"); + advance("="); + the_variable.expression = parse_expression(0); + } else if (token_nxt.id === "[") { + if (the_variable.id === "var") { + +// test_cause: +// ["var[aa]=0", "stmt_var", "unexpected_a", "var", 1] + + warn("unexpected_a", the_variable); + } + the_bracket = token_nxt; + advance("["); + while (true) { + ellipsis = false; + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + if (!token_nxt.identifier) { + +// test_cause: +// ["let[]", "stmt_var", "expected_identifier_a", "]", 5] + + return stop("expected_identifier_a"); + } + name = token_nxt; + advance(); + the_variable.names.push(name); + enroll(name, "variable", mode_const); + name.dead = false; + name.init = true; + if (ellipsis) { + name.ellipsis = true; + break; + } + if (token_nxt.id === "=") { + advance("="); + name.expression = parse_expression(); + the_bracket.open = true; + } + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + advance("]"); + advance("="); + the_variable.expression = parse_expression(0); + } else if (token_nxt.identifier) { + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// [" +// let ignore;function aa(ignore) {} +// ", "stmt_var", "unexpected_a", "ignore", 5] + + warn("unexpected_a", name); + } + enroll(name, "variable", mode_const); + if (token_nxt.id === "=" || mode_const) { + advance("="); + name.dead = false; + name.init = true; + name.expression = parse_expression(0); + } + the_variable.names.push(name); + } else { + +// test_cause: +// ["let 0", "stmt_var", "expected_identifier_a", "0", 5] +// ["var{aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 8] + + return stop("expected_identifier_a"); + } + if (token_nxt.id !== ",") { + break; + } + +// test_cause: +// ["let aa,bb;", "stmt_var", "expected_a_b", ",", 7] + + warn("expected_a_b", token_nxt, ";", ","); + advance(","); + } + +// Warn if variable declarations are unordered. + + if ( + option_dict.beta + && !option_dict.unordered + && !option_dict.variable + && variable_prv + && ( + variable_prv.id + " " + variable_prv.names[0].id + > the_variable.id + " " + the_variable.names[0].id + ) + ) { + +// test_cause: +// ["const bb=0;const aa=0;", "stmt_var", "expected_a_b_before_c_d", "aa", 12] +// ["let bb;let aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8] +// ["var bb;var aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8] + + warn( + "expected_a_b_before_c_d", + the_variable, + the_variable.id, + the_variable.names[0].id, + variable_prv.id, + variable_prv.names[0].id + ); + } + semicolon(); + return the_variable; + } + + function stmt_while() { + const the_while = token_now; + check_not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + +// test_cause: +// ["function aa(){while(0){break;}}", "stmt_while", "weird_loop", "while", 15] + + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; + } + + function stmt_with() { + +// test_cause: +// ["with", "stmt_with", "unexpected_a", "with", 1] + + stop("unexpected_a", token_now); + } + + function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!jslint_rgx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!jslint_rgx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + +// test_cause: +// ["let aa={0:0}", "survey", "expected_identifier_a", "0", 9] + + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property_dict[id] === "number") { + property_dict[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (state.mode_property) { + if (tenure[id] !== true) { + +// test_cause: +// ["/*property aa*/\naa.bb", "survey", "unregistered_property_a", "bb", 4] + + warn("unregistered_property_a", name); + } + } else if ( + !option_dict.nomen + && name.identifier + && jslint_rgx_weird_property.test(id) + ) { + +// test_cause: +// ["aa.$", "survey", "weird_property_a", "$", 4] +// ["aa._", "survey", "weird_property_a", "_", 4] +// ["aa._aa", "survey", "weird_property_a", "_aa", 4] +// ["aa.aaSync", "survey", "weird_property_a", "aaSync", 4] +// ["aa.aa_", "survey", "weird_property_a", "aa_", 4] + + warn("weird_property_a", name); + } + property_dict[id] = 1; + } + return id; + } + +// These functions are used to specify the grammar of our language: + + function symbol(id, bp) { + +// Create a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax_dict[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax_dict[id] = the_symbol; + } + return the_symbol; + } + + function ternary(id1, id2) { + +// Create a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led_infix = function parse_ternary_led(left) { + const the_token = token_now; + let second; + second = parse_expression(20); + advance(id2); + token_now.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, parse_expression(10)]; + if (token_nxt.id !== ")") { + +// test_cause: +// ["0?0:0", "parse_ternary_led", "use_open", "?", 2] + + warn("use_open", the_token); + } + return the_token; + }; + return the_symbol; + } + +// Now we parse JavaScript. +// Begin defining the language. + + assignment("%="); + assignment("&="); + assignment("*="); + assignment("+="); + assignment("-="); + assignment("/="); + assignment("<<="); + assignment("="); + assignment(">>="); + assignment(">>>="); + assignment("^="); + assignment("|="); + constant("(number)", "number"); + constant("(regexp)", "regexp"); + constant("(string)", "string"); + constant("Function", "function", constant_Function); + constant("Infinity", "number", Infinity); + constant("NaN", "number", NaN); + constant("arguments", "object", constant_arguments); + constant("eval", "function", constant_eval); + constant("false", "boolean", false); + constant("ignore", "undefined", constant_ignore); + constant("isFinite", "function", constant_isInfinite); + constant("isNaN", "function", constant_isNaN); + constant("null", "null", null); + constant("this", "object", constant_this); + constant("true", "boolean", true); + constant("undefined", "undefined"); + infix(100, "!="); + infix(100, "!=="); + infix(100, "=="); + infix(100, "==="); + infix(110, "<"); + infix(110, "<="); + infix(110, ">"); + infix(110, ">="); + infix(110, "in"); + infix(110, "instanceof"); + infix(120, "<<"); + infix(120, ">>"); + infix(120, ">>>"); + infix(130, "+"); + infix(130, "-"); + infix(140, "%"); + infix(140, "*"); + infix(140, "/"); + infix(160, "(", infix_lparen); + infix(160, "`", infix_grave); + infix(170, ".", infix_dot); + infix(170, "=>", infix_fart_unwrapped); + infix(170, "?.", infix_option_chain); + infix(170, "[", infix_lbracket); + infix(35, "??"); + infix(40, "||"); + infix(50, "&&"); + infix(70, "|"); + infix(80, "^"); + infix(90, "&"); + infixr(150, "**"); + postassign("++"); + postassign("--"); + preassign("++"); + preassign("--"); + prefix("!!"); + prefix("!"); + prefix("(", prefix_lparen); + prefix("+"); + prefix("-"); + prefix("/=", prefix_assign_divide); + prefix("=>", prefix_fart); + prefix("[", prefix_lbracket); + prefix("`", prefix_tick); + prefix("async", prefix_async); + prefix("await", prefix_await); + prefix("function", prefix_function); + prefix("new", prefix_new); + prefix("typeof"); + prefix("void", prefix_void); + prefix("{", prefix_lbrace); + prefix("~"); + stmt(";", stmt_semicolon); + stmt("async", prefix_async); + stmt("await", prefix_await); + stmt("break", stmt_break); + stmt("const", stmt_var); + stmt("continue", stmt_continue); + stmt("debugger", stmt_debugger); + stmt("delete", stmt_delete); + stmt("do", stmt_do); + stmt("export", stmt_export); + stmt("for", stmt_for); + stmt("function", prefix_function); + stmt("if", stmt_if); + stmt("import", stmt_import); + stmt("let", stmt_var); + stmt("return", stmt_return); + stmt("switch", stmt_switch); + stmt("throw", stmt_throw); + stmt("try", stmt_try); + stmt("var", stmt_var); + stmt("while", stmt_while); + stmt("with", stmt_with); + stmt("{", stmt_lbrace); + symbol(")"); + symbol("*/"); + symbol(","); + symbol(":"); + symbol(";"); + symbol("]"); + symbol("async"); + symbol("await"); + symbol("case"); + symbol("catch"); + symbol("class"); + symbol("default"); + symbol("else"); + symbol("enum"); + symbol("finally"); + symbol("implements"); + symbol("interface"); + symbol("package"); + symbol("private"); + symbol("protected"); + symbol("public"); + symbol("static"); + symbol("super"); + symbol("void"); + symbol("yield"); + symbol("}"); + ternary("?", ":"); + +// Init token_nxt. + + advance(); + +// Parsing of JSON is simple: + + if (state.mode_json) { + state.token_tree = parse_json(); + advance("(end)"); + return; + } + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option_dict.browser) { + if (token_nxt.id === ";") { + advance(";"); + } + +// If we are not in a browser, then the file form of strict pragma may be used. + + } else if (token_nxt.value === "use strict") { + advance("(string)"); + advance(";"); + } + state.token_tree = parse_statements(); + advance("(end)"); + +// Check global functions are ordered. + + check_ordered( + "function", + function_list.map(function ({ + level, + name + }) { + return (level === 1) && name; + }).filter(function (name) { + return option_dict.beta && name && name.id; + }) + ); +} + +function jslint_phase4_walk(state) { + +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). + + let { + artifact, + catch_stack, + function_stack, + global_dict, + is_equal, + is_weird, + option_dict, + syntax_dict, + test_cause, + token_global, + warn + } = state; + let block_stack = []; // The stack of blocks. + let blockage = token_global; // The current block. + let catchage = catch_stack[0]; // The current catch-block. + let functionage = token_global; // The current function. + let postaction; + let postamble; + let posts = empty(); + let preaction; + let preamble; + let pres = empty(); + +// The relational operators. + + let relationop = object_assign_from_list(empty(), [ + "!=", "!==", "<", "<=", "==", "===", ">", ">=" + ], true); + +// Ambulation of the parse tree. + + function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; + } + + function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + task(the_token); + }); + } + } + }; + } + + function init_variable(name) { + let the_variable = lookup(name); + if (!the_variable || the_variable.readonly) { + warn("bad_assignment_a", name); + return; + } + the_variable.init = true; + } + + function lookup(thing) { + let id = thing.id; + let the_variable; + if (thing.arity !== "variable") { + return; + } + +// Look up the variable in the current context. + + the_variable = functionage.context[id] || catchage.context[id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable && the_variable.role === "label") { + +// test_cause: +// ["aa:while(0){aa;}", "lookup", "label_a", "aa", 13] + + warn("label_a", thing); + return the_variable; + } + if (!the_variable) { + function_stack.forEach(function ({ + context + }) { + if (context[id] && context[id].role !== "label") { + the_variable = context[id]; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (!the_variable && global_dict[id] === undefined) { + +// test_cause: +// ["aa", "lookup", "undeclared_a", "aa", 1] +// ["class aa{}", "lookup", "undeclared_a", "aa", 7] +// [" +// let aa=0;try{aa();}catch(bb){bb();}bb(); +// ", "lookup", "undeclared_a", "bb", 36] +// [" +// let aa=0;try{aa();}catch(ignore){bb();} +// ", "lookup", "undeclared_a", "bb", 34] + + warn("undeclared_a", thing); + return; + } + if (!the_variable) { + the_variable = { + dead: false, + id, + init: true, + parent: token_global, + readonly: true, + role: "variable", + used: 0 + }; + token_global.context[id] = the_variable; + } + the_variable.closure = true; + functionage.context[id] = the_variable; + } + if ( + ( + the_variable.calls === undefined + || functionage.name === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + && the_variable.dead + ) { + +// test_cause: +// ["let aa;if(aa){let bb;}bb;", "lookup", "out_of_scope_a", "bb", 23] + + warn("out_of_scope_a", thing); + } + return the_variable; + } + + function post_a(thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + let right; + if (thing.id === "=") { + if (thing.names !== undefined) { + +// test_cause: +// ["if(0){aa=0}", "post_a", "=", "", 0] + + test_cause("="); + +// Probably deadcode. +// if (Array.isArray(thing.names)) { +// thing.names.forEach(init_variable); +// } else { +// init_variable(thing.names); +// } + + jslint_assert( + !Array.isArray(thing.names), + `Expected !Array.isArray(thing.names).` + ); + init_variable(thing.names); + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + +// test_cause: +// ["aa.aa=undefined", "post_a", "expected_a_b", "undefined", 1] + + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.readonly) { + warn("bad_assignment_a", lvalue); + } + } + right = syntax_dict[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + +// test_cause: +// ["aa+=undefined", "post_a", "unexpected_a", "undefined", 5] + + warn("unexpected_a", thing.expression[1]); + } + } + } + + function post_a_pluseq(thing) { + const right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } + } + + function post_b(thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || is_equal(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + +// test_cause: +// ["if(0===0){0}", "post_b", "weird_relation_a", "===", 5] + + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option_dict.convert) { + if (thing.expression[0].value === "") { + +// test_cause: +// ["\"\"+0", "post_b", "expected_a_b", "\"\" +", 3] + + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + +// test_cause: +// ["0+\"\"", "post_b", "expected_a_b", "+ \"\"", 2] + + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + +// test_cause: +// ["aa=window[0]", "post_b", "weird_expression_a", "window[...]", 10] + + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + +// test_cause: +// ["aa=self[0]", "post_b", "weird_expression_a", "self[...]", 8] + + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === "." || thing.id === "?.") { + if (thing.expression.id === "RegExp") { + +// test_cause: +// ["aa=RegExp.aa", "post_b", "weird_expression_a", ".", 10] + + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + +// test_cause: +// ["0- -0", "post_b", "wrap_unary", "-", 4] + + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } + } + + function post_b_and(thing) { + if ( + is_weird(thing.expression[0]) + || is_equal(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + +// test_cause: +// ["aa=(()=>0)&&(()=>0)", "post_b_and", "weird_condition_a", "&&", 11] +// ["aa=(``?``:``)&&(``?``:``)", "post_b_and", "weird_condition_a", "&&", 14] +// ["aa=/./&&/./", "post_b_and", "weird_condition_a", "&&", 7] +// ["aa=0&&0", "post_b_and", "weird_condition_a", "&&", 5] +// ["aa=[]&&[]", "post_b_and", "weird_condition_a", "&&", 6] +// ["aa=`${0}`&&`${0}`", "post_b_and", "weird_condition_a", "&&", 10] +// [" +// aa=function aa(){}&&function aa(){} +// ", "post_b_and", "weird_condition_a", "&&", 19] +// ["aa={}&&{}", "post_b_and", "weird_condition_a", "&&", 6] + + warn("weird_condition_a", thing); + } + } + + function post_b_lbracket(thing) { + if (thing.expression[0].id === "RegExp") { + +// test_cause: +// ["aa=RegExp[0]", "post_b_lbracket", "weird_expression_a", "[", 10] + + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + +// test_cause: +// ["aa[[0]]", "post_b_lbracket", "weird_expression_a", "[", 4] + + warn("weird_expression_a", thing.expression[1]); + } + } + + function post_b_lparen(thing) { + let arg; + let array; + let cack; + let left = thing.expression[0]; + let new_date; + let paren; + let the_new; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + +// test_cause: +// ["aa=function(){}()", "post_b_lparen", "wrap_immediate", "(", 16] + + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "BigInt" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + +// test_cause: +// ["new BigInt()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Boolean()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Number()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new String()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Symbol()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new aa()", "post_b_lparen", "unexpected_a", "new", 1] + + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option_dict.eval) { + +// test_cause: +// ["new Function()", "post_b_lparen", "unexpected_a", "new Function", 5] + + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + +// test_cause: +// ["new Array()", "post_b_lparen", "expected_a_b", "new Array", 5] + + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + +// test_cause: +// ["new Object()", "post_b_lparen", "expected_a_b", "new Object", 5] + + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "BigInt" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + +// test_cause: +// ["let Aa=Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8] + + warn("expected_a_before_b", left, "new", artifact(left)); + } + } + } else if (left.id === ".") { + cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + +// test_cause: +// ["new Date.UTC()", "post_b_lparen", "cack", "", 0] + + test_cause("cack"); + cack = !cack; + } + if (jslint_rgx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + +// test_cause: +// ["new Date.UTC()", "post_b_lparen", "unexpected_a", "new", 1] + + warn("unexpected_a", the_new); + } else { + +// test_cause: +// ["let Aa=Aa.Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8] + + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + paren = left.expression; + if (paren.id === "(") { + array = paren.expression; + if (array.length === 1) { + new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + +// test_cause: +// [" +// new Date().getTime() +// ", "post_b_lparen", "expected_a_b", "new Date().getTime()", 1] + + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } + } + + function post_b_or(thing) { + if ( + is_weird(thing.expression[0]) + || is_equal(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + +// test_cause: +// ["aa=0||0", "post_b_or", "weird_condition_a", "||", 5] + + warn("weird_condition_a", thing); + } + } + + function post_s_export(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== token_global) { + +// test_cause: +// [" +// if(0){import aa from "aa";} +// ", "post_s_export", "misplaced_a", "import", 7] + + warn("misplaced_a", the_thing); + } + } + + function post_s_for(thing) { + +// Recurse walk_statement(). + + walk_statement(thing.inc); + } + + function post_s_function(thing) { + delete functionage.async; + delete functionage.finally; + delete functionage.loop; + delete functionage.statement_prv; + delete functionage.switch; + delete functionage.try; + functionage = function_stack.pop(); + if (thing.wrapped) { + +// test_cause: +// ["aa=(function(){})", "post_s_function", "unexpected_parens", "function", 5] + + warn("unexpected_parens", thing); + } + return post_s_lbrace(); + } + + function post_s_import(the_thing) { + const name = the_thing.name; + if (name) { + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return post_s_export(the_thing); + } + } + + function post_s_lbrace() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); + } + + function post_s_try(thing) { + if (thing.catch) { + if (thing.catch.name) { + Object.assign(catchage.context[thing.catch.name.id], { + dead: false, + init: true + }); + } + +// Recurse walk_statement(). + + walk_statement(thing.catch.block); + +// Restore previous catch-scope after catch-block. + + catchage = catch_stack.pop(); + } + } + + function post_s_var(thing) { + thing.names.forEach(function (name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + +// Probably deadcode. +// if (name.id === "{" || name.id === "[") { +// name.names.forEach(subactivate); +// } else { +// name.init = true; +// } + + jslint_assert( + !(name.id === "{" || name.id === "["), + `Expected !(name.id === "{" || name.id === "[").` + ); + name.init = true; + } + blockage.live.push(name); + }); + } + + function post_t(thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || is_equal(thing.expression[1], thing.expression[2]) + ) { + +// test_cause: +// ["let aa=(aa?`${0}`:`${0}`);", "post_t", "unexpected_a", "?", 11] +// ["let aa=(aa?`0`:`0`);", "post_t", "unexpected_a", "?", 11] + + warn("unexpected_a", thing); + } else if (is_equal(thing.expression[0], thing.expression[1])) { + +// test_cause: +// ["aa?aa:0", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "||", "?"); + } else if (is_equal(thing.expression[0], thing.expression[2])) { + +// test_cause: +// ["aa?0:aa", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + +// test_cause: +// ["aa?true:false", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + +// test_cause: +// ["aa?false:true", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + +// test_cause: +// ["(aa&&!aa?0:1)", "post_t", "wrap_condition", "&&", 4] + + warn("wrap_condition", thing.expression[0]); + } + } + + function post_u(thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option_dict.convert) { + +// test_cause: +// ["!!0", "post_u", "expected_a_b", "!!", 1] + + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } + } + + function post_u_plus(thing) { + const right = thing.expression; + if (!option_dict.convert) { + +// test_cause: +// ["aa=+0", "post_u_plus", "expected_a_b", "+", 4] + + warn("expected_a_b", thing, "Number(...)", "+"); + } + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } + } + + function pre_a_bitwise(thing) { + +// These are the bitwise operators. + + switch (!option_dict.bitwise && thing.id) { + case "&": + case "&=": + case "<<": + case "<<=": + case ">>": + case ">>=": + case ">>>": + case ">>>=": + case "^": + case "^=": + case "|": + case "|=": + case "~": + +// test_cause: +// ["0&0", "pre_a_bitwise", "unexpected_a", "&", 2] +// ["0&=0", "pre_a_bitwise", "unexpected_a", "&=", 2] +// ["0<<0", "pre_a_bitwise", "unexpected_a", "<<", 2] +// ["0<<=0", "pre_a_bitwise", "unexpected_a", "<<=", 2] +// ["0>>0", "pre_a_bitwise", "unexpected_a", ">>", 2] +// ["0>>=0", "pre_a_bitwise", "unexpected_a", ">>=", 2] +// ["0>>>0", "pre_a_bitwise", "unexpected_a", ">>>", 2] +// ["0>>>=0", "pre_a_bitwise", "unexpected_a", ">>>=", 2] +// ["0^0", "pre_a_bitwise", "unexpected_a", "^", 2] +// ["0^=0", "pre_a_bitwise", "unexpected_a", "^=", 2] +// ["0|0", "pre_a_bitwise", "unexpected_a", "|", 2] +// ["0|=0", "pre_a_bitwise", "unexpected_a", "|=", 2] +// ["~0", "pre_a_bitwise", "unexpected_a", "~", 1] + + warn("unexpected_a", thing); + break; + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + +// test_cause: +// ["0<0<0", "pre_a_bitwise", "unexpected_a", "<", 4] + + warn("unexpected_a", thing); + } + } + + function pre_b(thing) { + let left; + let right; + let value; + if (relationop[thing.id] === true) { + left = thing.expression[0]; + right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + +// test_cause: +// ["NaN===NaN", "pre_b", "number_isNaN", "===", 4] + + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + +// test_cause: +// ["typeof 0===0", "pre_b", "expected_string_a", "0", 12] + + warn("expected_string_a", right); + } + } else { + value = right.value; + if (value === "null" || value === "undefined") { + +// test_cause: +// [" +// typeof aa==="undefined" +// ", "pre_b", "unexpected_typeof_a", "undefined", 13] + + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "bigint" + && value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + +// test_cause: +// ["typeof 0===\"aa\"", "pre_b", "expected_type_string_a", "aa", 12] + + warn("expected_type_string_a", right, value); + } + } + } + } + } + + function pre_b_eqeq(thing) { + +// test_cause: +// ["0==0", "pre_b_eqeq", "expected_a_b", "==", 2] + + warn("expected_a_b", thing, "===", "=="); + } + + function pre_b_in(thing) { + +// test_cause: +// ["aa in aa", "pre_b_in", "infix_in", "in", 4] + + warn("infix_in", thing); + } + + function pre_b_instanceof(thing) { + +// test_cause: +// ["0 instanceof 0", "pre_b_instanceof", "unexpected_a", "instanceof", 3] + + warn("unexpected_a", thing); + } + + function pre_b_lparen(thing) { + const left = thing.expression[0]; + let left_variable; + let parent; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + parent = functionage.name.parent; + if (parent) { + left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + +// Probably deadcode. +// && left_variable.dead + + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } + } + + function pre_b_noteq(thing) { + +// test_cause: +// ["0!=0", "pre_b_noteq", "expected_a_b", "!=", 2] + + warn("expected_a_b", thing, "!==", "!="); + } + + function pre_b_or(thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + +// test_cause: +// ["0&&0||0", "pre_b_or", "and", "&&", 2] + + warn("and", thang); + } + }); + } + + function pre_s_for(thing) { + let the_variable; + if (thing.name !== undefined) { + thing.name.dead = false; + the_variable = lookup(thing.name); + if (the_variable !== undefined) { + if (the_variable.init && the_variable.readonly) { + +// test_cause: +// ["const aa=0;for(aa in aa){}", "pre_s_for", "bad_assignment_a", "aa", 16] + + warn("bad_assignment_a", thing.name); + } + the_variable.init = true; + } + } + +// Recurse walk_statement(). + + walk_statement(thing.initial); + } + + function pre_s_function(thing) { + +// test_cause: +// ["()=>0", "pre_s_function", "", "", 0] +// ["(function (){}())", "pre_s_function", "", "", 0] +// ["function aa(){}", "pre_s_function", "", "", 0] + + test_cause(""); + if (thing.arity === "statement" && blockage.body !== true) { + +// test_cause: +// ["if(0){function aa(){}\n}", "pre_s_function", "unexpected_a", "function", 7] + + warn("unexpected_a", thing); + } + function_stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + +// test_cause: +// [" +// /*jslint getset*/ +// aa={get aa(aa){}} +// ", "pre_s_function", "bad_get", "function", 9] + + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + +// test_cause: +// [" +// /*jslint getset*/ +// aa={set aa(){}} +// ", "pre_s_function", "bad_set", "function", 9] + + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); + } + + function pre_s_lbrace(thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; + } + + function pre_try(thing) { + if (thing.catch !== undefined) { + +// Create new catch-scope for catch-parameter. + + catch_stack.push(catchage); + catchage = thing.catch; + } + } + + function pre_v(thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } + } + + function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); + } + + function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + +// test_cause: +// ["(function(){}())", "walk_expression", "isArray", "", 0] +// ["0&&0", "walk_expression", "isArray", "", 0] + + test_cause("isArray"); + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + +// PR-414 - Bugfix - fix fart-body not being walked. + + if (thing.id === "function" || thing.id === "=>") { + +// test_cause: +// ["aa=()=>0", "walk_expression", "function", "=>", 0] +// ["aa=function(){}", "walk_expression", "function", "function", 0] + + test_cause("function", thing.id); + +// Recurse walk_statement(). + + walk_statement(thing.block); + } + if ( + thing.arity === "preassign" || thing.arity === "postassign" + ) { + +// test_cause: +// ["aa=++aa", "walk_expression", "unexpected_a", "++", 4] +// ["aa=--aa", "walk_expression", "unexpected_a", "--", 4] + + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + +// test_cause: +// ["aa[aa=0]", "walk_expression", "unexpected_statement_a", "=", 6] + + warn("unexpected_statement_a", thing); + } + +// test_cause: +// ["aa=0", "walk_expression", "default", "", 0] + + test_cause("default"); + postamble(thing); + } + } + } + + function walk_statement(thing) { + if (!thing) { + return; + } + if (Array.isArray(thing)) { + +// test_cause: +// ["+[]", "walk_statement", "isArray", "", 0] + + test_cause("isArray"); + +// Recurse walk_statement(). + + thing.forEach(walk_statement); + return; + } + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + +// test_cause: +// ["0&&0", "walk_statement", "unexpected_expression_a", "&&", 2] + + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + && thing.id !== "import" + ) { + +// test_cause: +// ["!0", "walk_statement", "unexpected_expression_a", "!", 1] +// ["+[]", "walk_statement", "unexpected_expression_a", "+", 1] +// ["+new aa()", "walk_statement", "unexpected_expression_a", "+", 1] +// ["0", "walk_statement", "unexpected_expression_a", "0", 1] +// ["typeof 0", "walk_statement", "unexpected_expression_a", "typeof", 1] + + warn("unexpected_expression_a", thing); + } + +// Recurse walk_statement(). + + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + + postaction = action(posts); + postamble = amble(posts); + preaction = action(pres); + preamble = amble(pres); + postaction("assignment", "+=", post_a_pluseq); + postaction("assignment", post_a); + postaction("binary", "&&", post_b_and); + postaction("binary", "(", post_b_lparen); + postaction("binary", "=>", post_s_function); + postaction("binary", "[", post_b_lbracket); + postaction("binary", "||", post_b_or); + postaction("binary", post_b); + postaction("statement", "const", post_s_var); + postaction("statement", "export", post_s_export); + postaction("statement", "for", post_s_for); + postaction("statement", "function", post_s_function); + postaction("statement", "import", post_s_import); + postaction("statement", "let", post_s_var); + postaction("statement", "try", post_s_try); + postaction("statement", "var", post_s_var); + postaction("statement", "{", post_s_lbrace); + postaction("ternary", post_t); + postaction("unary", "+", post_u_plus); + postaction("unary", "function", post_s_function); + postaction("unary", post_u); + preaction("assignment", pre_a_bitwise); + preaction("binary", "!=", pre_b_noteq); + preaction("binary", "(", pre_b_lparen); + preaction("binary", "==", pre_b_eqeq); + preaction("binary", "=>", pre_s_function); + preaction("binary", "in", pre_b_in); + preaction("binary", "instanceof", pre_b_instanceof); + preaction("binary", "||", pre_b_or); + preaction("binary", pre_b); + preaction("binary", pre_a_bitwise); + preaction("statement", "for", pre_s_for); + preaction("statement", "function", pre_s_function); + preaction("statement", "try", pre_try); + preaction("statement", "{", pre_s_lbrace); + preaction("unary", "function", pre_s_function); + preaction("unary", "~", pre_a_bitwise); + preaction("variable", pre_v); + + walk_statement(state.token_tree); +} + +function jslint_phase5_whitage(state) { + +// PHASE 5. Check whitespace between tokens in . + + let { + artifact, + catch_list, + function_list, + function_stack, + option_dict, + test_cause, + token_global, + token_list, + warn + } = state; + let closer = "(end)"; + let free = false; + +// free = false + +// cause: +// "()=>0" +// "aa()" +// "aa(0,0)" +// "function(){}" + +// free = true + +// cause: +// "(0)" +// "(aa)" +// "aa(0)" +// "do{}while()" +// "for(){}" +// "if(){}" +// "switch(){}" +// "while(){}" + + let left = token_global; + let margin = 0; + let mode_indent = ( + +// PR-330 - Allow 2-space indent. + + option_dict.indent2 + ? 2 + : 4 + ); + let nr_comments_skipped = 0; + let open = true; + let opening = true; + let right; + +// This is the set of infix operators that require a space on each side. + + let spaceop = object_assign_from_list(empty(), [ + "!=", "!==", "%", "%=", "&", "&&", "&=", "*", "*=", "+=", "-=", "/", + "/=", "<", "<<", "<<=", "<=", "=", "==", "===", "=>", ">", ">=", ">>", + ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" + ], true); + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + const name = the_function.context[id]; + if (id !== "ignore" && name.parent === the_function) { + +// test_cause: +// ["function aa(aa) {return aa;}", "delve", "id", "", 0] + + test_cause("id"); + if ( + name.used === 0 + +// Probably deadcode. +// && ( +// name.role !== "function" +// || name.parent.arity !== "unary" +// ) + + && jslint_assert( + name.role !== "function", + `Expected name.role !== "function".` + ) + ) { + +// test_cause: +// ["/*jslint node*/\nlet aa;", "delve", "unused_a", "aa", 5] +// ["function aa(aa){return;}", "delve", "unused_a", "aa", 13] +// ["let aa=0;try{aa();}catch(bb){aa();}", "delve", "unused_a", "bb", 26] + + warn("unused_a", name); + } else if (!name.init) { + +// test_cause: +// ["/*jslint node*/\nlet aa;aa();", "delve", "uninitialized_a", "aa", 5] + + warn("uninitialized_a", name); + } + } + }); + } + + function expected_at(at) { + +// Probably deadcode. +// if (right === undefined) { +// right = token_nxt; +// } + + jslint_assert( + !(right === undefined), + `Expected !(right === undefined).` + ); + warn( + "expected_a_at_b_c", + right, + artifact(right), + +// Fudge column numbers in warning message. + + at + jslint_fudge, + right.from + jslint_fudge + ); + } + + function no_space() { + if (left.line === right.line) { + +// from: +// if (left.line === right.line) { +// no_space(); +// } else { + + if (left.thru !== right.from && nr_comments_skipped === 0) { + +// test_cause: +// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14] + + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + +// from: +// } else if ( +// right.arity === "binary" +// && right.id === "(" +// && free +// ) { +// no_space(); +// } else if ( + +// Probably deadcode. +// if (open) { +// const at = ( +// free +// ? margin +// : margin + 8 +// ); +// if (right.from < at) { +// expected_at(at); +// } +// } else { +// if (right.from !== margin + 8) { +// expected_at(margin + 8); +// } +// } + + jslint_assert(open, `Expected open.`); + jslint_assert(free, `Expected free.`); + if (right.from < margin) { + +// test_cause: +// ["let aa = aa(\naa\n()\n);", "expected_at", "expected_a_at_b_c", "5", 1] + + expected_at(margin); + } + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line || !open) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (right.from !== margin) { + expected_at(margin); + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn("expected_space_a_b", right, artifact(left), artifact(right)); + } + } + + function pop() { + const previous = function_stack.pop(); + closer = previous.closer; + free = previous.free; + margin = previous.margin; + open = previous.open; + opening = previous.opening; + } + + function push() { + function_stack.push({ + closer, + free, + margin, + open, + opening + }); + } + +// uninitialized_and_unused(); +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (state.mode_module === true || option_dict.node) { + delve(token_global); + } + catch_list.forEach(delve); + function_list.forEach(delve); + + if (option_dict.white) { + return; + } + +// whitage(); +// Go through the token list, looking at usage of whitespace. + + token_list.forEach(function whitage(the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + +// The open and close pairs. + + switch (left.id) { + case "${": + case "(": + case "[": + case "{": + +// test_cause: +// ["let aa=[];", "whitage", "opener", "", 0] +// ["let aa=`${0}`;", "whitage", "opener", "", 0] +// ["let aa=aa();", "whitage", "opener", "", 0] +// ["let aa={};", "whitage", "opener", "", 0] + + test_cause("opener"); + +// Probably deadcode. +// case "${}": + + jslint_assert( + !(left.id + right.id === "${}"), + "Expected !(left.id + right.id === \"${}\")." + ); + switch (left.id + right.id) { + case "()": + case "[]": + case "{}": + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like '{]') have already been detected. + +// test_cause: +// ["let aa=[];", "whitage", "opener_closer", "", 0] +// ["let aa=aa();", "whitage", "opener_closer", "", 0] +// ["let aa={};", "whitage", "opener_closer", "", 0] + + test_cause("opener_closer"); + if (left.line === right.line) { + +// test_cause: +// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14] + + no_space(); + } else { + +// test_cause: +// ["let aa = aa(\n );", "expected_at", "expected_a_at_b_c", "1", 2] + + at_margin(0); + } + break; + default: + +// test_cause: +// ["let aa=(0);", "whitage", "opener_operand", "", 0] +// ["let aa=[0];", "whitage", "opener_operand", "", 0] +// ["let aa=`${0}`;", "whitage", "opener_operand", "", 0] +// ["let aa=aa(0);", "whitage", "opener_operand", "", 0] +// ["let aa={aa:0};", "whitage", "opener_operand", "", 0] + + test_cause("opener_operand"); + opening = left.open || (left.line !== right.line); + push(); + switch (left.id) { + case "${": + closer = "}"; + break; + case "(": + closer = ")"; + break; + case "[": + closer = "]"; + break; + case "{": + closer = "}"; + break; + } + if (opening) { + +// test_cause: +// ["function aa(){\nreturn;\n}", "whitage", "opening", "", 0] +// ["let aa=(\n0\n);", "whitage", "opening", "", 0] +// ["let aa=[\n0\n];", "whitage", "opening", "", 0] +// ["let aa=`${\n0\n}`;", "whitage", "opening", "", 0] +// ["let aa={\naa:0\n};", "whitage", "opening", "", 0] + + test_cause("opening"); + free = closer === ")" && left.free; + open = true; + margin += mode_indent; + if (right.role === "label") { + if (right.from !== 0) { + +// test_cause: +// [" +// function aa() { +// bb: +// while (aa) { +// if (aa) { +// break bb; +// } +// } +// } +// ", "expected_at", "expected_a_at_b_c", "1", 2] + + expected_at(0); + } + } else if (right.switch) { + at_margin(-mode_indent); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + +// test_cause: +// [" +// function aa() {bb: +// while (aa) { +// aa(); +// } +// } +// ", "whitage", "expected_line_break_a_b", "bb", 16] + + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + +// test_cause: +// ["let aa=(0);", "whitage", "not_free", "", 0] +// ["let aa=[0];", "whitage", "not_free", "", 0] +// ["let aa=`${0}`;", "whitage", "not_free", "", 0] +// ["let aa={aa:0};", "whitage", "not_free", "", 0] + + test_cause("not_free"); + free = false; + open = false; + +// test_cause: +// ["let aa = ( 0 );", "no_space_only", "unexpected_space_a_b", "0", 12] + + no_space_only(); + } + } + break; + default: + if (right.statement === true) { + if (left.id === "else") { + +// test_cause: +// [" +// let aa = 0; +// if (aa) { +// aa(); +// } else if (aa) { +// aa(); +// } +// ", "one_space_only", "expected_space_a_b", "if", 9] + + one_space_only(); + } else { + +// test_cause: +// [" let aa = 0;", "expected_at", "expected_a_at_b_c", "1", 2] + + at_margin(0); + open = false; + } + +// If right is a closer, then pop the previous state. + + } else if (right.id === closer) { + pop(); + if (opening && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-mode_indent); + } else if (right.role === "label") { + if (right.from !== 0) { + +// test_cause: +// [" +// function aa() { +// aa();cc: +// while (aa) { +// if (aa) { +// break cc; +// } +// } +// } +// ", "expected_at", "expected_a_at_b_c", "1", 10] + + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + +// test_cause: +// ["let {aa,bb} = 0;", "one_space", "expected_space_a_b", "bb", 9] + + one_space(); + } else { + +// test_cause: +// [" +// function aa() { +// aa( +// 0,0 +// ); +// } +// ", "expected_at", "expected_a_at_b_c", "9", 11] + + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + +// test_cause: +// [" +// let aa = ( +// aa +// ? 0 +// : 1 +// ); +// ", "expected_at", "expected_a_at_b_c", "5", 1] + + at_margin(0); + } else { + +// test_cause: +// ["let aa = (aa ? 0 : 1);", "whitage", "use_open", "?", 14] + + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + +// test_cause: +// ["let aa = aa(\naa ()\n);", "no_space", "unexpected_space_a_b", "(", 4] + + no_space(); + } else if ( + left.id === "." + || left.id === "?." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + || (right.id === "." || right.id === "?.") + ) { + +// test_cause: +// ["let aa = 0 ;", "no_space_only", "unexpected_space_a_b", ";", 12] +// ["let aa = aa ?.aa;", "no_space_only", "unexpected_space_a_b", "?.", 13] + + no_space_only(); + } else if (left.id === ";") { + +// test_cause: +// [" +// /*jslint for*/ +// function aa() { +// for ( +// aa(); +// aa; +// aa() +// ) { +// aa(); +// } +// } +// ", "expected_at", "expected_a_at_b_c", "9", 1] + + if (open) { + at_margin(0); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || left.id === "await" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + +// test_cause: +// [" +// function aa() { +// do { +// aa(); +// } while(aa()); +// } +// ", "one_space_only", "expected_space_a_b", "(", 12] + + one_space_only(); + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || left.id === "async" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + +// test_cause: +// ["let aa=0;", "one_space", "expected_space_a_b", "0", 8] +// ["let aa={\naa:\n0\n};", "expected_at", "expected_a_at_b_c", "5", 1] + + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +function jslint_report({ + exports, + froms, + functions, + global, + json, + module, + property, + stop, + warnings +}) { + +// This function will create human-readable, html-report +// for warnings, properties, and functions from jslint-result-object. +// +// Example usage: +// let result = jslint("console.log('hello world')"); +// let html = jslint_report(result); + + let html = ""; + let length_80 = 1111; + + function address(line = 1, column = 1) { + +// This function will create HTML address element from and + + return `
    ${Number(line)}: ${Number(column)}
    `; + + } + + function detail(title, list) { + return ( + (Array.isArray(list) && list.length > 0) + ? ( + +// Google Lighthouse Accessibility -
    's do not contain only properly-ordered +//
    and
    groups, ". + + code.replace(( + /^]*?>\n([\S\s]*?\n)<\/script>$/gm + ), function (ignore, match1, ii) { + jslint_from_file({ + code: match1, + file: file + ". + + import_meta_url = import_meta_url || jslint_import_meta_url; + if ( + jslint_rgx_url_search_window_jslint.test(import_meta_url) + && (typeof globalThis === "object" && globalThis) + ) { + globalThis.jslint = jslint; + } + +// Feature-detect nodejs. + + if (!( + (typeof process === "object" && process) + && process.versions + && typeof process.versions.node === "string" + && !mode_noop + )) { + return exit_code; + } + console_error = console_error || console.error; + console_log = console_log || console.log; + process_argv = process_argv || process.argv; + process_env = process_env || process.env; + process_exit = process_exit || process.exit; + await moduleFsInit(); + if ( + !( + +// Feature-detect nodejs-cli. + + process.execArgv.indexOf("--eval") === -1 + && process.execArgv.indexOf("-e") === -1 + && ( + ( + /[\/|\\]jslint(?:\.[cm]?js)?$/m + ).test(process_argv[1]) + || mode_cli + ) + && ( + moduleUrl.fileURLToPath(import_meta_url) + === + modulePath.resolve(process_argv[1]) + ) + ) + && !mode_cli + ) { + return exit_code; + } + +// init commmand + + command = String(process_argv[2]).split("="); + command[1] = command.slice(1).join("="); + + switch (command[0]) { + +// PR-362 - Add API Doc. + + case "jslint_apidoc": + await jslint_apidoc(Object.assign(JSON.parse(process_argv[3]), { + pathname: command[1] + })); + return; + +// PR-363 - Add command jslint_report. + + case "jslint_report": + mode_report = command[1]; + process_argv = process_argv.slice(1); + break; + +// COMMIT-b26d6df2 - Add command jslint_wrapper_vim. + + case "jslint_wrapper_vim": + mode_wrapper_vim = true; + process_argv = process_argv.slice(1); + break; + +// PR-364 - Add command v8_coverage_report. + + case "v8_coverage_report": + await v8CoverageReportCreate({ + consoleError: console_error, + coverageDir: command[1], + processArgv: process_argv.slice(3) + }); + return; + } + +// Normalize file relative to process.cwd(). + + process_argv.slice(2).some(function (arg) { + if (!arg.startsWith("-")) { + file = file || arg; + return true; + } + }); + if (!file) { + return; + } + file = modulePath.resolve(file) + "/"; + if (file.startsWith(process.cwd() + "/")) { + file = file.replace(process.cwd() + "/", "").slice(0, -1) || "."; + } + file = file.replace(( + /\\/g + ), "/").replace(( + /\/$/g + ), ""); + if (source) { + data = source; + } else { + +// jslint_cli - jslint directory. + + try { + data = await moduleFs.promises.readdir(file, "utf8"); + } catch (ignore) {} + if (data) { + await Promise.all(data.map(async function (file2) { + let code; + let time_start = Date.now(); + file2 = file + "/" + file2; + switch (( + /\.\w+?$|$/m + ).exec(file2)[0]) { + case ".cjs": + case ".html": + case ".js": + case ".json": + case ".md": + case ".mjs": + case ".sh": + break; + default: + return; + } + try { + code = await moduleFs.promises.readFile(file2, "utf8"); + } catch (ignore) { + return; + } + if ( + ( + /(?:\b|_)(?:lock|min|raw|rollup)(?:\b|_)/ + ).test(file2) + || !(code && code.length < 1048576) + ) { + return; + } + jslint_from_file({ + code, + file: file2, + option + }); + console_error( + "jslint - " + (Date.now() - time_start) + "ms - " + file2 + ); + })); + process_exit(exit_code); + return exit_code; + } + +// jslint_cli - jslint file. + + try { + data = await moduleFs.promises.readFile(file, "utf8"); + } catch (err) { + console_error(err); + exit_code = 1; + process_exit(exit_code); + return exit_code; + } + } + result = jslint_from_file({ + code: data, + file, + option + }); + if (mode_report) { + result = jslint.jslint_report(result); + result = `\n${result}\n`; + await fsWriteFileWithParents(mode_report, result); + } + process_exit(exit_code); + return exit_code; +} + +function jslint_phase1_split() { + +// PHASE 1. Split by newlines into . + + return; +} + +function jslint_phase2_lex(state) { + +// PHASE 2. Lex into . + + let { + artifact, + directive_list, + global_dict, + global_list, + line_list, + option_dict, + stop, + stop_at, + tenure, + test_cause, + token_global, + token_list, + warn, + warn_at + } = state; + let char; // The current character being lexed. + let column = 0; // The column number of the next character. + let from; // The starting column number of the token. + let from_mega; // The starting column of megastring. + let line = 0; // The line number of the next character. + let line_disable; // The starting line of "/*jslint-disable*/". + let line_mega; // The starting line of megastring. + let line_source = ""; // The remaining line source string. + let line_whole = ""; // The whole line source string. + let mode_digits_empty_string = 1; + let mode_digits_numeric_separator = 2; + let mode_directive = true; // true if directives are still allowed. + let mode_mega = false; // true if currently parsing a megastring + // ... literal. + let mode_regexp; // true if regular expression literal seen on + // ... this line. + let paren_backtrack_list = []; // List of most recent "(" tokens at any + // ... paren-depth. + let paren_depth = 0; // Keeps track of current paren-depth. + let snippet = ""; // A piece of string. + let token_1; // The first token. + let token_prv = token_global; // The previous token including + // ... comments. + let token_prv_expr = token_global; // The previous token excluding + // ... comments. + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions char_after, char_before, +// read_digits, and char_after_escape help in the parsing of literals. + + function char_after(match) { + +// Get the next character from the source line. Remove it from the line_source, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + +// test_cause: +// ["aa=/[", "char_after", "expected_a", "]", 5] +// ["aa=/aa{/", "char_after", "expected_a_b", "/", 8] + + return ( + char === "" + ? stop_at("expected_a", line, column - 1, match) + : stop_at("expected_a_b", line, column, match, char) + ); + } + char = line_source.slice(0, 1); + line_source = line_source.slice(1); + snippet += char || " "; + column += 1; + return char; + } + + function char_after_escape(extra) { + +// Validate char after escape "\\". + + char_after("\\"); + switch (char) { + case "": + +// test_cause: +// ["\"\\", "char_after_escape", "unclosed_string", "", 2] + + return stop_at("unclosed_string", line, column); + case "/": + return char_after(); + case "\\": + return char_after(); + case "`": + return char_after(); + case "b": + return char_after(); + case "f": + return char_after(); + case "n": + return char_after(); + case "r": + return char_after(); + case "t": + +// test_cause: +// ["\"\\/\\\\\\`\\b\\f\\n\\r\\t\"", "char_after_escape", "char_after", "", 0] + + test_cause("char_after"); + return char_after(); + case "u": + if (char_after("u") === "{") { + if (state.mode_json) { + +// test_cause: +// ["[\"\\u{12345}\"]", "char_after_escape", "unexpected_a", "{", 5] + + warn_at("unexpected_a", line, column, char); + } + if (read_digits("x", undefined) > 5) { + +// test_cause: +// ["\"\\u{123456}\"", "char_after_escape", "too_many_digits", "", 11] + + warn_at("too_many_digits", line, column); + } + if (char !== "}") { + +// test_cause: +// ["\"\\u{12345\"", "char_after_escape", "expected_a_before_b", "\"", 10] + + stop_at("expected_a_before_b", line, column, "}", char); + } + return char_after(); + } + char_before(); + if (read_digits("x", mode_digits_empty_string) < 4) { + +// test_cause: +// ["\"\\u0\"", "char_after_escape", "expected_four_digits", "", 5] + + warn_at("expected_four_digits", line, column); + } + return; + default: + if (extra && extra.indexOf(char) >= 0) { + return char_after(); + } + +// test_cause: +// ["\"\\0\"", "char_after_escape", "unexpected_a_before_b", "0", 3] + + warn_at("unexpected_a_before_b", line, column, "\\", char); + } + } + + function char_before() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the line_source. + + char = snippet.slice(-1); + line_source = char + line_source; + column -= char.length; + +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + return char; + } + + function check_numeric_separator(digits, column) { + +// This function will check for illegal numeric-separator in . + + digits.replace(( + jslint_rgx_numeric_separator_illegal + ), function (ignore, ii) { + +// test_cause: +// ["0x0_0_;", "check_numeric_separator", "illegal_num_separator", "", 6] +// ["0x0_0__0;", "check_numeric_separator", "illegal_num_separator", "", 6] +// ["aa=1_2_;", "check_numeric_separator", "illegal_num_separator", "", 7] +// ["aa=1_2__3;", "check_numeric_separator", "illegal_num_separator", "", 7] +// ["aa=1_2_n;", "check_numeric_separator", "illegal_num_separator", "", 7] + + warn_at("illegal_num_separator", line, column + ii + 1); + return ""; + }); + } + + function lex_comment() { + let body; + let ii = 0; + let jj = 0; + let the_comment; + +// Create a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + +// Create token from comment //.... + + if (snippet === "//") { + snippet = line_source; + line_source = ""; + the_comment = token_create("(comment)", snippet); + if (mode_mega) { + +// test_cause: +// ["`${//}`", "lex_comment", "unexpected_comment", "`", 4] + + warn("unexpected_comment", the_comment, "`"); + } + +// Create token from comment /*...*/. + + } else { + snippet = []; + if (line_source[0] === "/") { + +// test_cause: +// ["/*/", "lex_comment", "unexpected_a", "/", 2] + + warn_at("unexpected_a", line, column + ii, "/"); + } + +// Lex/loop through each line until "*/". + + while (true) { + // jslint_rgx_star_slash + ii = line_source.indexOf("*/"); + if (ii >= 0) { + break; + } + // jslint_rgx_slash_star + ii = line_source.indexOf("/*"); + if (ii >= 0) { + +// test_cause: +// ["/*/*", "lex_comment", "nested_comment", "", 2] + + warn_at("nested_comment", line, column + ii); + } + snippet.push(line_source); + line_source = read_line(); + if (line_source === undefined) { + +// test_cause: +// ["/*", "lex_comment", "unclosed_comment", "", 1] + + return stop_at("unclosed_comment", line, column); + } + } + jj = line_source.slice(0, ii).search( + jslint_rgx_slash_star_or_slash + ); + if (jj >= 0) { + +// test_cause: +// ["/*/**/", "lex_comment", "nested_comment", "", 2] + + warn_at("nested_comment", line, column + jj); + } + snippet.push(line_source.slice(0, ii)); + snippet = snippet.join(" "); + column += ii + 2; + line_source = line_source.slice(ii + 2); + the_comment = token_create("(comment)", snippet); + } + +// Uncompleted work comment. + + if (!option_dict.devel && jslint_rgx_todo.test(snippet)) { + +// test_cause: +// ["//todo", "lex_comment", "todo_comment", "(comment)", 1] //jslint-ignore-line + + warn("todo_comment", the_comment); + } + +// Lex directives in comment. + + [ + the_comment.directive, body + ] = Array.from(snippet.match(jslint_rgx_directive) || []).slice(1); + if (the_comment.directive === undefined) { + return the_comment; + } + directive_list.push(the_comment); + if (!mode_directive) { + +// test_cause: +// ["0\n/*global aa*/", "lex_comment", "misplaced_directive_a", "global", 1] + + warn_at("misplaced_directive_a", line, from, the_comment.directive); + return the_comment; + } + +// lex_directive(); +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + +// Lex/loop through each directive in /*...*/ + + ii = 0; + body.replace(jslint_rgx_directive_part, function ( + match0, + key, + val, + jj + ) { + if (ii !== jj) { + +// test_cause: +// ["/*jslint !*/", "lex_comment", "bad_directive_a", "!", 1] + + return stop("bad_directive_a", the_comment, body.slice(ii)); + } + if (match0 === "") { + return ""; + } + ii += match0.length; + switch (the_comment.directive) { + case "global": + if (val) { + +// test_cause: +// ["/*global aa:false*/", "lex_comment", "bad_option_a", "aa:false", 1] + + warn("bad_option_a", the_comment, key + ":" + val); + } + global_dict[key] = "user-defined"; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// state.mode_module = the_comment; + + break; + case "jslint": + if (!option_set_item(key, val !== "false")) { + +// test_cause: +// ["/*jslint undefined*/", "lex_comment", "bad_option_a", "undefined", 1] + + warn("bad_option_a", the_comment, key); + } + break; + case "property": + state.mode_property = true; + tenure[key] = true; + break; + } + return ""; + }); + return the_comment; + } + + function lex_megastring() { + let id; + let match; + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (mode_mega) { + +// test_cause: +// ["`${`", "lex_megastring", "expected_a_b", "`", 4] + + return stop_at("expected_a_b", line, column, "}", "`"); + } + from_mega = from; + line_mega = line; + mode_mega = true; + snippet = ""; + +// Parsing a mega literal is tricky. First create a ` token. + + token_create("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + while (true) { + match = line_source.match(jslint_rgx_mega) || { + "0": "", + index: 0 + }; + snippet += line_source.slice(0, match.index); + column += match.index; + line_source = line_source.slice(match.index); + match = match[0]; + switch (match) { + case "${": + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + token_create("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then create tokens that will become part of an expression until +// a } token is made. + + column += 2; + token_create("${"); + line_source = line_source.slice(2); + +// Lex/loop through each token inside megastring-expression `${...}`. + + while (true) { + id = lex_token().id; + if (id === "{") { + +// test_cause: +// ["`${{", "lex_megastring", "expected_a_b", "{", 4] + + return stop_at("expected_a_b", line, column, "}", "{"); + } + if (id === "}") { + break; + } + } + break; + case "\\": + snippet += line_source.slice(0, 2); + line_source = line_source.slice(2); + column += 2; + break; + case "`": + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + token_create("(string)", snippet).quote = "`"; + snippet = ""; + +// Terminate megastring with `. + + line_source = line_source.slice(1); + column += 1; + mode_mega = false; + return token_create("`"); + default: + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + snippet += line_source + "\n"; + if (read_line() === undefined) { + +// test_cause: +// ["`", "lex_megastring", "unclosed_mega", "", 1] + + return stop_at("unclosed_mega", line_mega, from_mega); + } + } + } + } + + function lex_number() { + let prefix = snippet; + +// PR-390 - Add numeric-separator check. + + check_numeric_separator(prefix, column - prefix.length); + char_after(); + switch (prefix === "0" && char) { + case "b": + case "o": + case "x": + read_digits(char, mode_digits_numeric_separator); + +// PR-351 - Ignore BigInt suffix 'n'. + + if (char === "n") { + char_after("n"); + } + break; + default: + if (char === ".") { + read_digits("d", mode_digits_numeric_separator); + } + if (char === "E" || char === "e") { + char_after(char); + if (char !== "+" && char !== "-") { + char_before(); + } + read_digits("d", mode_digits_numeric_separator); + } + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + +// test_cause: +// ["0a", "lex_number", "unexpected_a_after_b", "0", 2] + + return stop_at( + "unexpected_a_after_b", + line, + column, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + char_before(); + return token_create("(number)", snippet); + } + + function lex_regexp() { + +// Regexp +// Lex a regular expression literal. + + let flag; + let mode_regexp_multiline; + let result; + let value; + mode_regexp = true; + + function lex_regexp_bracketed() { + let mode_regexp_range; + +// RegExp +// Match a class. + + char_after("["); + if (char === "^") { + char_after("^"); + } + while (true) { + +// RegExp +// Match a character in a character class. + + switch (char) { + case "": + case "]": + +// test_cause: +// ["aa=/[", "lex_regexp_bracketed", "closer", "", 0] +// ["aa=/[]/", "lex_regexp_bracketed", "closer", "", 0] + + test_cause("closer"); + if (mode_regexp_range) { + +// test_cause: +// ["aa=/[0-]/", "lex_regexp_bracketed", "unexpected_a", "-", 7] + + warn_at("unexpected_a", line, column - 1, "-"); + } + return char_after("]"); + +// PR-362 - Relax regexp-warning against using . +// +// case " ": +// +// // test_cause: +// // ["aa=/[ ]/", "lex_regexp_bracketed", "expected_a_b", " ", 6] +// +// warn_at("expected_a_b", line, column, "\\u0020", " "); +// break; + + case "-": + case "/": + case "[": + case "^": + +// test_cause: +// ["aa=/[-]/", "lex_regexp_bracketed", "expected_a_before_b", "-", 6] +// ["aa=/[.^]/", "lex_regexp_bracketed", "expected_a_before_b", "^", 7] +// ["aa=/[/", "lex_regexp_bracketed", "expected_a_before_b", "/", 6] +// ["aa=/[\\\\/]/", "lex_regexp_bracketed", "expected_a_before_b", "/", 8] +// ["aa=/[\\\\[]/", "lex_regexp_bracketed", "expected_a_before_b", "[", 8] + + warn_at("expected_a_before_b", line, column, "\\", char); + break; + case "\\": + char_after_escape("BbDdSsWw-[]^"); + char_before(); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${/[`]/}`", "lex_regexp_bracketed", "unexpected_a", "`", 6] + + warn_at("unexpected_a", line, column, "`"); + } + break; + } + char_after(); + mode_regexp_range = false; + if (char === "-") { + +// RegExp +// Match a range of subclasses. + + mode_regexp_range = true; + char_after("-"); + } + } + } + + function lex_regexp_group() { + +// RegExp +// Lex sequence of characters in regexp. + + switch (char) { + case "": + warn_at("expected_regexp_factor_a", line, column, char); + break; + case ")": + warn_at("expected_regexp_factor_a", line, column, char); + break; + case "]": + +// test_cause: +// ["/ /", "lex_regexp_group", "expected_regexp_factor_a", "", 3] +// ["aa=/)", "lex_regexp_group", "expected_regexp_factor_a", ")", 5] +// ["aa=/]", "lex_regexp_group", "expected_regexp_factor_a", "]", 5] + + warn_at("expected_regexp_factor_a", line, column, char); + break; + } + while (true) { + switch (char) { + case "": + case ")": + case "/": + case "]": + return; + +// PR-362 - Relax regexp-warning against using . +// +// case " ": +// +// // test_cause: +// // ["aa=/ /", "lex_regexp_group", "expected_a_b", " ", 5] +// +// warn_at("expected_a_b", line, column, "\\s", " "); +// char_after(); +// break; + + case "$": + if (line_source[0] !== "/") { + mode_regexp_multiline = true; + } + char_after(); + break; + case "(": + +// RegExp +// Match a group that starts with left paren. + + char_after("("); + switch (char) { + case ":": + +// test_cause: +// ["aa=/(:)/", "lex_regexp_group", "expected_a_before_b", ":", 6] +// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5] + + warn_at("expected_a_before_b", line, column, "?", ":"); + break; + case "?": + char_after("?"); + switch (char) { + case "!": + +// PR-437 - Add grammar for regexp-named-capture-group. + + case "<": + case "=": + char_after(); + break; + default: + char_after(":"); + } + break; + } + +// RegExp +// Recurse lex_regexp_group(). + + lex_regexp_group(); + char_after(")"); + break; + case "*": + case "+": + case "?": + case "{": + case "}": + +// test_cause: +// ["aa=/+/", "lex_regexp_group", "expected_a_before_b", "+", 5] +// ["aa=/.**/", "lex_regexp_group", "expected_a_before_b", "*", 7] +// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5] +// ["aa=/{/", "lex_regexp_group", "expected_a_before_b", "{", 5] +// ["aa=/}/", "lex_regexp_group", "expected_a_before_b", "}", 5] + + warn_at("expected_a_before_b", line, column, "\\", char); + char_after(); + break; + case "[": + lex_regexp_bracketed(); + break; + case "\\": + +// test_cause: +// ["aa=/\\/", "lex_regexp_group", "escape", "", 0] + + test_cause("escape"); + +// PR-437 - Add grammar for regexp-named-backreference. + + char_after_escape("BbDdSsWw^${}[]():=!.|*+?k"); + break; + case "^": + if (snippet !== "^") { + mode_regexp_multiline = true; + } + char_after(); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${/`/}`", "lex_regexp_group", "unexpected_a", "`", 5] + + warn_at("unexpected_a", line, column, "`"); + } + char_after(); + break; + default: + char_after(); + } + +// RegExp +// Match an optional quantifier. + + switch (char) { + case "*": + case "+": + if (char_after(char) === "?") { + +// test_cause: +// ["aa=/.*?/", "lex_regexp_group", "?", "", 0] +// ["aa=/.+?/", "lex_regexp_group", "?", "", 0] + + test_cause("?"); + char_after("?"); + } + break; + case "?": + if (char_after("?") === "?") { + +// test_cause: +// ["aa=/.??/", "lex_regexp_group", "unexpected_a", "?", 7] + + warn_at("unexpected_a", line, column, char); + char_after("?"); + } + break; + case "{": + if (read_digits("d", mode_digits_empty_string) === 0) { + +// test_cause: +// ["aa=/aa{/", "lex_regexp_group", "expected_a_before_b", ",", 8] + + warn_at("expected_a_before_b", line, column, "0", ","); + } + if (char === ",") { + +// test_cause: +// ["aa=/.{,/", "lex_regexp_group", "comma", "", 0] + + test_cause("comma"); + read_digits("d", mode_digits_empty_string); + } + if (char_after("}") === "?") { + +// test_cause: +// ["aa=/.{0}?/", "lex_regexp_group", "unexpected_a", "?", 9] + + warn_at("unexpected_a", line, column, char); + char_after("?"); + } + break; + } + } + } + +// RegExp +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + char_after(); + if (char === "=") { + +// test_cause: +// ["aa=/=/", "lex_regexp", "expected_a_before_b", "=", 5] + + warn_at("expected_a_before_b", line, column, "\\", "="); + } + lex_regexp_group(); + +// RegExp +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + +// RegExp +// Make sure there is a closing slash. + + value = snippet; + char_after("/"); + +// RegExp +// Create flag. + + flag = empty(); + while ( + +// Regexp +// char is a letter. + + (char >= "a" && char <= "z\uffff") + || (char >= "A" && char <= "Z\uffff") + ) { + +// RegExp +// Process dangling flag letters. + + switch (!flag[char] && char) { + case "g": + break; + case "i": + break; + case "m": + break; + case "u": + break; + case "y": + +// test_cause: +// ["aa=/./gimuy", "lex_regexp", "flag", "", 0] + + test_cause("flag"); + break; + default: + +// test_cause: +// ["aa=/./gg", "lex_regexp", "unexpected_a", "g", 8] +// ["aa=/./z", "lex_regexp", "unexpected_a", "z", 7] + + warn_at("unexpected_a", line, column, char); + } + flag[char] = true; + char_after(); + } + char_before(); + if (char === "/" || char === "*") { + +// test_cause: +// ["aa=/.//", "lex_regexp", "unexpected_a", "/", 3] + + return stop_at("unexpected_a", line, from, char); + } + result = token_create("(regexp)", char); + result.flag = flag; + result.value = value; + if (mode_regexp_multiline && !flag.m) { + +// test_cause: +// ["aa=/$^/", "lex_regexp", "missing_m", "", 7] + + warn_at("missing_m", line, column); + } + return result; + } + + function lex_slash_or_regexp() { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + let the_token; + switch ( + token_prv_expr.identifier + && !token_prv_expr.dot + && token_prv_expr.id + ) { + case "case": + case "delete": + case "in": + case "instanceof": + case "new": + case "typeof": + case "void": + case "yield": + the_token = lex_regexp(); + +// test_cause: +// ["case /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6] +// ["delete /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8] +// ["in /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 4] +// ["instanceof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 12] +// ["new /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 5] +// ["typeof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8] +// ["void /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6] +// ["yield /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 7] + + return stop("unexpected_a", the_token); + case "return": + return lex_regexp(); + } + switch (!token_prv_expr.identifier && token_prv_expr.id.slice(-1)) { + case "!": + case "%": + case "&": + case "*": + case "+": + case "-": + case "/": + case ";": + case "<": + case ">": + case "^": + case "{": + case "|": + case "}": + case "~": + the_token = lex_regexp(); + +// test_cause: +// ["!/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["%/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["&/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["+/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["-/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["0 * /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5] +// ["0 / /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5] +// [";/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["^/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["{/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["|/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["}/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["~/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] + + warn("wrap_regexp", the_token); + return the_token; + case "(": + case ",": + case ":": + case "=": + case "?": + case "[": + +// test_cause: +// ["(/./", "lex_slash_or_regexp", "recurse", "", 0] +// [",/./", "lex_slash_or_regexp", "recurse", "", 0] +// [":/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["=/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["?/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["aa[/./", "lex_slash_or_regexp", "recurse", "", 0] + + test_cause("recurse"); + return lex_regexp(); + } + if (line_source[0] === "=") { + column += 1; + line_source = line_source.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + return token_create(snippet); + } + + function lex_string(quote) { + +// Create a string token. + + let the_token; + if (!option_dict.single && quote === "'") { + +// test_cause: +// ["''", "lex_string", "use_double", "", 1] + + warn_at("use_double", line, column); + } + snippet = ""; + char_after(); + +// Lex/loop through each character in "...". + + while (true) { + switch (char) { + case "": + +// test_cause: +// ["\"", "lex_string", "unclosed_string", "", 1] + + return stop_at("unclosed_string", line, column); + case "\\": + char_after_escape(quote); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${\"`\"}`", "lex_string", "unexpected_a", "`", 5] + + warn_at("unexpected_a", line, column, "`"); + } + char_after("`"); + break; + case quote: + +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + the_token = token_create("(string)", snippet); + the_token.quote = quote; + return the_token; + default: + char_after(); + } + } + } + + function lex_token() { + let match; + +// Lex/loop through each whitespace. + + while (true) { + +// Lex/loop through each blank-line. + + while (!line_source) { + line_source = read_line(); + from = 0; + if (line_source === undefined) { + return ( + mode_mega + +// test_cause: +// ["`${//}`", "lex_token", "unclosed_mega", "", 1] + + ? stop_at("unclosed_mega", line_mega, from_mega) + : line_disable !== undefined + +// test_cause: +// ["/*jslint-disable*/", "lex_token", "unclosed_disable", "", 1] + + ? stop_at("unclosed_disable", line_disable) + : token_create("(end)") + ); + } + } + from = column; + match = line_source.match(jslint_rgx_token); + +// match[1] token +// match[2] whitespace +// match[3] identifier +// match[4] number +// match[5] rest + + if (!match) { + +// test_cause: +// ["#", "lex_token", "unexpected_char_a", "#", 1] + + return stop_at( + "unexpected_char_a", + line, + column, + line_source[0] + ); + } + snippet = match[1]; + column += snippet.length; + line_source = match[5]; + if (!match[2]) { + break; + } + } + +// The token is an identifier. + + if (match[3]) { + return token_create(snippet, undefined, true); + } + +// Create token from number. + + if (match[4]) { + return lex_number(); + } + +// Create token from string "..." or '...'. + + if (snippet === "\"" || snippet === "'") { + return lex_string(snippet); + } + +// Create token from megastring `...`. + + if (snippet === "`") { + return lex_megastring(); + } + +// Create token from comment /*...*/ or //.... + + if (snippet === "/*" || snippet === "//") { + return lex_comment(); + } + +// Create token from slash /. + + if (snippet === "/") { + return lex_slash_or_regexp(); + } + return token_create(snippet); + } + + function option_set_item(key, val) { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + switch (key) { + case "beta": // Enable experimental warnings. + case "bitwise": // Allow bitwise operator. + case "browser": // Assume browser environment. + case "convert": // Allow conversion operator. + case "couch": // Assume CouchDb environment. + case "devel": // Allow console.log() and friends. + case "ecma": // Assume ECMAScript environment. + case "eval": // Allow eval(). + case "fart": // Allow complex fat-arrow. + case "for": // Allow for-statement. + case "getset": // Allow get() and set(). + case "indent2": // Use 2-space indent. + case "long": // Allow long lines. + case "node": // Assume Node.js environment. + case "nomen": // Allow weird property name. + case "single": // Allow single-quote strings. + case "subscript": // Allow identifier in subscript-notation. + case "test_cause": // Test jslint's causes. + case "test_internal_error": // Test jslint's internal-error + // ... handling-ability. + case "this": // Allow 'this'. + case "trace": // Include jslint stack-trace in warnings. + case "unordered": // Allow unordered cases, params, properties, + // ... variables, and exports. + case "variable": // Allow unordered const and let declarations + // ... that are not at top of function-scope. + case "white": // Allow messy whitespace. + option_dict[key] = val; + break; + +// PR-404 - Alias "evil" to jslint-directive "eval" for backwards-compat. + + case "evil": + return option_set_item("eval", val); + +// PR-404 - Alias "nomen" to jslint-directive "name" for backwards-compat. + + case "name": + return option_set_item("nomen", val); + default: + return false; + } + +// Initialize global-variables. + + switch (val && key) { + +// Assign global browser variables to global_dict. +/* +// /\*jslint beta, browser, devel*\/ +console.log(JSON.stringify(Object.keys(window).sort(), undefined, 4)); +*/ + + case "browser": + object_assign_from_list(global_dict, [ + +// Shared with Node.js. + + "AbortController", + // "Buffer", + // "Crypto", + // "CryptoKey", + "Event", + "EventTarget", + "MessageChannel", + "MessageEvent", + "MessagePort", + // "Request", + // "Response", + // "SubtleCrypto", + "TextDecoder", + "TextEncoder", + "URL", + "URLSearchParams", + "WebAssembly", + // "__dirname", + // "__filename", + // "atob", + // "btoa", + // "clearImmediate", + "clearInterval", + "clearTimeout", + // "console", + // "crypto", + // "exports", + // "fetch", + // "global", + // "module", + "performance", + // "process", + "queueMicrotask", + // "require", + // "setImmediate", + "setInterval", + "setTimeout", + +// Web worker only. +// https://github.com/mdn/content/blob/main/files/en-us/web/api +// /workerglobalscope/index.md + + "importScripts", + +// Window. + + "Blob", + // "CharacterData", + // "DocumentType", + // "Element", + // "Event", + "FileReader", + // "FontFace", + "FormData", + "IntersectionObserver", + "MutationObserver", + // "Storage", + // "TextDecoder", + // "TextEncoder", + // "URL", + "Worker", + "XMLHttpRequest", + // "caches", + // "clearInterval", + // "clearTimeout", + "document", + // "event", + "fetch", + // "history", + "indexedDb", + "localStorage", + "location", + // "name", + "navigator", + "postMessage", + // "screen", + "sessionStorage", + // "setInterval", + // "setTimeout", + "structuredClone", + "window" + ], "browser"); + break; + +// https://docs.couchdb.org/en/stable/query-server/javascript.html#javascript + + case "couch": + object_assign_from_list(global_dict, [ + "emit", + "getRow", + "isArray", + "log", + "provides", + "registerType", + "require", + "send", + "start", + "sum", + "toJSON" + ], "CouchDb"); + break; + case "devel": + object_assign_from_list(global_dict, [ + "alert", "confirm", "console", "prompt" + ], "development"); + break; + +// These are the globals that are provided by the language standard. +// Assign global ECMAScript variables to global_dict. +/* +node --input-type=module --eval ' +// /\*jslint beta, node*\/ +import https from "https"; +(async function () { + let dict = {import: true}; + let result = ""; + await new Promise(function (resolve) { + https.get(( + "https://raw.githubusercontent.com/mdn/content/main/files" + + "/en-us/web/javascript/reference/global_objects/index.md" + ), function (res) { + res.on("data", function (chunk) { + result += chunk; + }).on("end", resolve).setEncoding("utf8"); + }); + }); + result.replace(( + /\n- \{\{JSxRef\("(?:Global_Objects\/)?([^"\/]+?)"/g + ), function (ignore, key) { + if (globalThis.hasOwnProperty(key)) { + dict[key] = true; + } + return ""; + }); + console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4)); +}()); +' +*/ + + case "ecma": + object_assign_from_list(global_dict, [ + "AggregateError", + "Array", + "ArrayBuffer", + "Atomics", + "BigInt", + "BigInt64Array", + "BigUint64Array", + "Boolean", + "DataView", + "Date", + "Error", + "EvalError", + "Float32Array", + "Float64Array", + "Function", + "Infinity", + "Int16Array", + "Int32Array", + "Int8Array", + "Intl", + "JSON", + "Map", + "Math", + "NaN", + "Number", + "Object", + "Promise", + "Proxy", + "RangeError", + "ReferenceError", + "Reflect", + "RegExp", + "Set", + "SharedArrayBuffer", + "String", + "Symbol", + "SyntaxError", + "TypeError", + "URIError", + "Uint16Array", + "Uint32Array", + "Uint8Array", + "Uint8ClampedArray", + "WeakMap", + "WeakSet", + "WebAssembly", + "decodeURI", + "decodeURIComponent", + "encodeURI", + "encodeURIComponent", + "eval", + "globalThis", + "import", + "isFinite", + "isNaN", + "parseFloat", + "parseInt", + "undefined" + ], "ECMAScript"); + break; + +// Assign global Node.js variables to global_dict. +/* +node --input-type=module --eval ' +// /\*jslint beta, node*\/ +import moduleHttps from "https"; +(async function () { + let dict = Object.create(null); + let result = ""; + await new Promise(function (resolve) { + moduleHttps.get(( + "https://raw.githubusercontent.com/nodejs/node/v16.x/doc/api" + + "/globals.md" + ), function (res) { + res.on("data", function (chunk) { + result += chunk; + }).on("end", resolve).setEncoding("utf8"); + }); + }); + result.replace(( + /\n(?:\* \[`|## |## Class: )`\w+/g + ), function (match0) { + dict[match0.split("`")[1]] = true; + return ""; + }); + console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4)); +}()); +' +*/ + + case "node": + object_assign_from_list(global_dict, [ + "AbortController", + "Buffer", + // "Crypto", + // "CryptoKey", + "Event", + "EventTarget", + "MessageChannel", + "MessageEvent", + "MessagePort", + // "Request", + // "Response", + // "SubtleCrypto", + "TextDecoder", + "TextEncoder", + "URL", + "URLSearchParams", + "WebAssembly", + "__dirname", + "__filename", + // "atob", + // "btoa", + "clearImmediate", + "clearInterval", + "clearTimeout", + "console", + // "crypto", + "exports", + // "fetch", + "global", + "module", + "performance", + "process", + "queueMicrotask", + "require", + "setImmediate", + "setInterval", + "setTimeout" + ], "Node.js"); + break; + } + return true; + } + + function read_digits(base, mode) { + let digits = line_source.match( + base === "b" + ? jslint_rgx_digits_bits + : base === "o" + ? jslint_rgx_digits_octals + : base === "x" + ? jslint_rgx_digits_hexs + : jslint_rgx_digits_decimals + )[0]; + if ( + (mode !== mode_digits_empty_string && digits.length === 0) + || digits[0] === "_" + ) { + +// test_cause: +// ["0x", "read_digits", "expected_digits_after_a", "0x", 2] +// ["0x_", "read_digits", "expected_digits_after_a", "0x", 2] + + warn_at("expected_digits_after_a", line, column, snippet); + } + +// PR-390 - Add numeric-separator check. + + if (mode === mode_digits_numeric_separator) { + check_numeric_separator(digits, column); + } else if (digits.indexOf("_") >= 0) { + +// test_cause: +// ["\"\\u{1_2}\"", "read_digits", "illegal_num_separator", "", 6] + + warn_at( + "illegal_num_separator", + line, + column + digits.indexOf("_") + 1 + ); + } + column += digits.length; + line_source = line_source.slice(digits.length); + snippet += digits; + char_after(); + return digits.length; + } + + function read_line() { + +// Put the next line of source in line_source. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + if ( + !option_dict.long + && line_whole.length > 80 + && line_disable === undefined + && !state.mode_json + && token_1 + && !mode_regexp + ) { + +// test_cause: +// ["/////////////////////////////////////////////////////////////////////////////////", "read_line", "too_long", "", 1] //jslint-ignore-line + + warn_at("too_long", line); + } + column = 0; + line += 1; + mode_regexp = false; + line_source = undefined; + line_whole = ""; + if (line_list[line] === undefined) { + return line_source; + } + line_source = line_list[line].line_source; + line_whole = line_source; + +// Scan each line for following ignore-directives: +// "/*jslint-disable*/" +// "/*jslint-enable*/" +// "//jslint-ignore-line" + + if (line_source === "/*jslint-disable*/") { + +// test_cause: +// ["/*jslint-disable*/", "read_line", "jslint_disable", "", 0] + + test_cause("jslint_disable"); + line_disable = line; + } else if (line_source === "/*jslint-enable*/") { + if (line_disable === undefined) { + +// test_cause: +// ["/*jslint-enable*/", "read_line", "unopened_enable", "", 1] + + stop_at("unopened_enable", line); + } + line_disable = undefined; + } else if ( + line_source.endsWith(" //jslint-ignore-line") + || line_source.endsWith(" //jslint-quiet") + ) { + +// test_cause: +// ["0 //jslint-ignore-line", "read_line", "jslint_ignore_line", "", 0] + + test_cause("jslint_ignore_line"); + line_list[line].directive_ignore_line = true; + } + if (line_disable !== undefined) { + +// test_cause: +// ["/*jslint-disable*/\n0", "read_line", "line_disable", "", 0] + + test_cause("line_disable"); + line_source = ""; + } + // jslint_rgx_tab + if (line_source.indexOf("\t") >= 0) { + if (!option_dict.white) { + +// test_cause: +// ["\t", "read_line", "use_spaces", "", 1] + + warn_at("use_spaces", line, line_source.indexOf("\t") + 1); + } + line_source = line_source.replace(jslint_rgx_tab, " "); + } + if (!option_dict.white && line_source.endsWith(" ")) { + +// test_cause: +// [" ", "read_line", "unexpected_trailing_space", "", 1] + + warn_at("unexpected_trailing_space", line, line_source.length - 1); + } + return line_source; + } + + function token_create(id, value, identifier) { + +// Create the token object and append it to token_list. + + let the_token = { + from, + id, + identifier: Boolean(identifier), + line, + nr: token_list.length, + thru: column, + value + }; + token_list.push(the_token); + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + mode_directive = false; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option_dict.white. + + if ( + token_prv.line === line + && token_prv.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (token_prv.id === "(comment)" || token_prv.id === "(regexp)") + ) { + +// test_cause: +// ["/**//**/", "token_create", "expected_space_a_b", "(comment)", 5] + + warn( + "expected_space_a_b", + the_token, + artifact(token_prv), + artifact(the_token) + ); + } + if (token_prv.id === "." && id === "(number)") { + +// test_cause: +// [".0", "token_create", "expected_a_before_b", ".", 1] + + warn("expected_a_before_b", token_prv, "0", "."); + } + if (token_prv_expr.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart. +// Farts are now detected by keeping a list of most recent "(" tokens at any +// given depth. When a "=>" token is encountered, the most recent "(" token at +// current depth is marked as a fart. + + switch (id) { + case "(": + paren_backtrack_list[paren_depth] = the_token; + paren_depth += 1; + break; + case ")": + paren_depth -= 1; + break; + case "=>": + if ( + token_prv_expr.id === ")" + && paren_backtrack_list[paren_depth] + ) { + paren_backtrack_list[paren_depth].fart = the_token; + } + break; + } + +// The previous token is used to detect adjacency problems. + + token_prv = the_token; + +// The token_prv_expr token is a previous token that was not a comment. +// The token_prv_expr token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (token_prv.id !== "(comment)") { + token_prv_expr = token_prv; + } + return the_token; + } + +// Init global_dict and option_dict. + + option_set_item("ecma", true); + Object.keys(option_dict).sort().forEach(function (key) { + option_set_item(key, option_dict[key] === true); + }); + object_assign_from_list(global_dict, global_list, "user-defined"); + +// Scan first line for "#!" and ignore it. + + if (line_list[jslint_fudge].line_source.startsWith("#!")) { + line += 1; + state.mode_shebang = true; + } + token_1 = lex_token(); + state.mode_json = token_1.id === "{" || token_1.id === "["; + +// Lex/loop through each token until (end). + + while (true) { + if (lex_token().id === "(end)") { + break; + } + } +} + +function jslint_phase3_parse(state) { + +// PHASE 3. Parse into using the Pratt-parser. + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + + let anon = "anonymous"; // The guessed name for anonymous functions. + let { + artifact, + catch_list, + catch_stack, + export_dict, + function_list, + function_stack, + global_dict, + import_list, + is_equal, + option_dict, + property_dict, + stop, + syntax_dict, + tenure, + test_cause, + token_global, + token_list, + warn, + warn_at + } = state; + let catchage = catch_stack[0]; // The current catch-block. + let functionage = token_global; // The current function. + let mode_var; // "var" if using var; "let" if using let. + let token_ii = 0; // The number of the next token. + let token_now = token_global; // The current token being examined in + // ... the parse. + let token_nxt = token_global; // The next token to be examined in + // ... . + + function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if ( + token_now.identifier + && token_now.id !== "function" + && token_now.id !== "async" + ) { + anon = token_now.id; + } else if ( + token_now.id === "(string)" + && jslint_rgx_identifier.test(token_now.value) + ) { + anon = token_now.value; + } + +// Attempt to match token_nxt with an expected id. + + if (id !== undefined && token_nxt.id !== id) { + return ( + match === undefined + +// test_cause: +// ["{0:0}", "advance", "expected_a_b", "0", 2] + + ? stop("expected_a_b", token_nxt, id, artifact()) + +// test_cause: +// ["{\"aa\":0", "advance", "expected_a_b_from_c_d", "{", 1] + + : stop( + "expected_a_b_from_c_d", + token_nxt, + id, + artifact(match), + match.line, + artifact() + ) + ); + } + +// Promote the tokens, skipping comments. + + token_now = token_nxt; + while (true) { + token_nxt = token_list[token_ii]; + state.token_nxt = token_nxt; + token_ii += 1; + if (token_nxt.id !== "(comment)") { + if (token_nxt.id === "(end)") { + token_ii -= 1; + } + break; + } + if (state.mode_json) { + +// test_cause: +// ["[//]", "advance", "unexpected_a", "(comment)", 2] + + warn("unexpected_a"); + } + } + } + + function assignment(id) { + +// Create an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led_infix = function (left) { + const the_token = token_now; + let right; + the_token.arity = "assignment"; + right = parse_expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "preassign" + || right.arity === "postassign" + ) { + warn("unexpected_a", right); + } + check_mutation(left); + return the_token; + }; + return the_symbol; + } + + function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token_now; + if (special !== "body") { + functionage.statement_prv = the_block; + } + the_block.arity = "statement"; + the_block.body = special === "body"; + +// Top level function bodies may include the "use strict" pragma. + + if ( + special === "body" + && function_stack.length === 1 + && token_nxt.value === "use strict" + ) { + token_nxt.statement = true; + advance("(string)"); + advance(";"); + } + stmts = parse_statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option_dict.devel && special !== "ignore") { + +// test_cause: +// ["function aa(){}", "block", "empty_block", "{", 14] + + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; + } + + function check_left(left, right) { + +// Warn if the left is not one of these: +// ?. +// ?: +// e() +// e.b +// e[b] +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !check_left(left.expression[1]) + && !check_left(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "?." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right || token_nxt); + return false; + } + return true; + } + + function check_mutation(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + +// test_cause: +// ["0=0", "check_mutation", "bad_assignment_a", "0", 1] + + warn("bad_assignment_a", the_thing); + return false; + } + return true; + } + + function check_not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === token_global) { + +// test_cause: +// [" +// while(0){} +// ", "check_not_top_level", "unexpected_at_top_level_a", "while", 1] + + warn("unexpected_at_top_level_a", thing); + } + } + + function check_ordered(type, token_list) { + +// This function will warn if is unordered. + + token_list.reduce(function (aa, token) { + const bb = artifact(token); + if (!option_dict.unordered && aa > bb) { + warn("expected_a_b_before_c_d", token, type, bb, type, aa); + } + return bb; + }, ""); + } + + function check_ordered_case(case_list) { + +// This function will warn if is unordered. + + case_list.filter(noop).map(function (token) { + switch (token.identifier || token.id) { + case "(number)": + return { + order: 1, + token, + type: "number", + value: Number(artifact(token)) + }; + case "(string)": + return { + order: 2, + token, + type: "string", + value: artifact(token) + }; + case true: + return { + order: 3, + token, + type: "identifier", + value: artifact(token) + }; + } + }).reduce(function (aa, bb) { + if ( + +// PR-419 - Hide warning about unordered case-statements behind beta-flag. + + option_dict.beta + && !option_dict.unordered + && aa && bb + && ( + aa.order > bb.order + || (aa.order === bb.order && aa.value > bb.value) + ) + ) { + warn( + "expected_a_b_before_c_d", + bb.token, + `case-${bb.type}`, + bb.value, + `case-${aa.type}`, + aa.value + ); + } + return bb; + }, undefined); + } + + function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = token_nxt; + let the_value; + +// test_cause: +// ["do{}while()", "condition", "", "", 0] +// ["if(){}", "condition", "", "", 0] +// ["while(){}", "condition", "", "", 0] + + test_cause(""); + the_paren.free = true; + advance("("); + the_value = parse_expression(0); + advance(")"); + if (the_value.wrapped === true) { + +// test_cause: +// ["while((0)){}", "condition", "unexpected_a", "(", 6] + + warn("unexpected_a", the_paren); + } + +// Check for anticondition. + + switch (the_value.id) { + case "%": + warn("unexpected_a", the_value); + break; + case "&": + warn("unexpected_a", the_value); + break; + case "(number)": + warn("unexpected_a", the_value); + break; + case "(string)": + warn("unexpected_a", the_value); + break; + case "*": + warn("unexpected_a", the_value); + break; + case "+": + warn("unexpected_a", the_value); + break; + case "-": + warn("unexpected_a", the_value); + break; + case "/": + warn("unexpected_a", the_value); + break; + case "<<": + warn("unexpected_a", the_value); + break; + case ">>": + warn("unexpected_a", the_value); + break; + case ">>>": + warn("unexpected_a", the_value); + break; + case "?": + warn("unexpected_a", the_value); + break; + case "^": + warn("unexpected_a", the_value); + break; + case "typeof": + warn("unexpected_a", the_value); + break; + case "|": + warn("unexpected_a", the_value); + break; + case "~": + +// test_cause: +// ["if(0%0){}", "condition", "unexpected_a", "%", 5] +// ["if(0&0){}", "condition", "unexpected_a", "&", 5] +// ["if(0){}", "condition", "unexpected_a", "0", 4] +// ["if(0*0){}", "condition", "unexpected_a", "*", 5] +// ["if(0+0){}", "condition", "unexpected_a", "+", 5] +// ["if(0-0){}", "condition", "unexpected_a", "-", 5] +// ["if(0/0){}", "condition", "unexpected_a", "/", 5] +// ["if(0<<0){}", "condition", "unexpected_a", "<<", 5] +// ["if(0>>0){}", "condition", "unexpected_a", ">>", 5] +// ["if(0>>>0){}", "condition", "unexpected_a", ">>>", 5] +// ["if(0?0:0){}", "condition", "unexpected_a", "?", 5] +// ["if(0^0){}", "condition", "unexpected_a", "^", 5] +// ["if(0|0){}", "condition", "unexpected_a", "|", 5] +// ["if(\"aa\"){}", "condition", "unexpected_a", "aa", 4] +// ["if(typeof 0){}", "condition", "unexpected_a", "typeof", 4] +// ["if(~0){}", "condition", "unexpected_a", "~", 4] + + warn("unexpected_a", the_value); + break; + } + return the_value; + } + + function constant(id, type, value) { + +// Create a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud_prefix = ( + typeof value === "function" + ? value + : function () { + token_now.constant = true; + if (value !== undefined) { + token_now.value = value; + } + return token_now; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; + } + + function constant_Function() { + if (!option_dict.eval) { + +// test_cause: +// ["Function", "constant_Function", "unexpected_a", "Function", 1] + + warn("unexpected_a", token_now); + } else if (token_nxt.id !== "(") { + +// test_cause: +// [" +// /*jslint eval*/ +// Function +// ", "constant_Function", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "(", artifact()); + } + return token_now; + } + + function constant_arguments() { + +// test_cause: +// ["arguments", "constant_arguments", "unexpected_a", "arguments", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function constant_eval() { + if (!option_dict.eval) { + +// test_cause: +// ["eval", "constant_eval", "unexpected_a", "eval", 1] + + warn("unexpected_a", token_now); + } else if (token_nxt.id !== "(") { + +// test_cause: +// ["/*jslint eval*/\neval", "constant_eval", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "(", artifact()); + } + return token_now; + } + + function constant_ignore() { + +// test_cause: +// ["ignore", "constant_ignore", "unexpected_a", "ignore", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function constant_isInfinite() { + +// test_cause: +// ["isFinite", "constant_isInfinite", "expected_a_b", "isFinite", 1] + + warn("expected_a_b", token_now, "Number.isFinite", "isFinite"); + return token_now; + } + + function constant_isNaN() { + +// test_cause: +// ["isNaN(0)", "constant_isNaN", "number_isNaN", "isNaN", 1] + + warn("number_isNaN", token_now); + return token_now; + } + + function constant_this() { + if (!option_dict.this) { + +// test_cause: +// ["this", "constant_this", "unexpected_a", "this", 1] + + warn("unexpected_a", token_now); + } + return token_now; + } + + function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + let earlier; + let id = name.id; + +// Reserved words may not be enrolled. + + if (syntax_dict[id] !== undefined && id !== "ignore") { + +// test_cause: +// ["let undefined", "enroll", "reserved_a", "undefined", 5] + + warn("reserved_a", name); + return; + } + +// Has the name been enrolled in this context? + + earlier = functionage.context[id] || catchage.context[id]; + if (earlier) { + +// test_cause: +// ["let aa;let aa", "enroll", "redefinition_a_b", "1", 12] + + warn("redefinition_a_b", name, id, earlier.line); + return; + } + +// Has the name been enrolled in an outer context? + + function_stack.forEach(function ({ + context + }) { + earlier = context[id] || earlier; + }); + if (earlier && id === "ignore") { + if (earlier.role === "variable") { + +// test_cause: +// ["let ignore;function aa(ignore){}", "enroll", "redefinition_a_b", "1", 24] + + warn("redefinition_a_b", name, id, earlier.line); + } + } else if ( + earlier + && role !== "parameter" && role !== "function" + && (role !== "exception" || earlier.role !== "exception") + ) { + +// test_cause: +// [" +// function aa(){try{aa();}catch(aa){aa();}} +// ", "enroll", "redefinition_a_b", "1", 31] +// ["function aa(){var aa;}", "enroll", "redefinition_a_b", "1", 19] + + warn("redefinition_a_b", name, id, earlier.line); + } else if ( + option_dict.beta + && global_dict[id] + && role !== "parameter" + ) { + +// test_cause: +// ["let Array", "enroll", "redefinition_global_a_b", "Array", 5] + + warn("redefinition_global_a_b", name, global_dict[id], id); + } + +// Enroll it. + + Object.assign(name, { + dead: true, + init: false, + parent: ( + role === "exception" + ? catchage + : functionage + ), + readonly, + role, + used: 0 + }); + name.parent.context[id] = name; + } + + function infix(bp, id, f) { + +// Create an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led_infix = function (left) { + const the_token = token_now; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, parse_expression(bp)]; + return the_token; + }; + return the_symbol; + } + + function infix_dot(left) { + const the_token = token_now; + let name = token_nxt; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "flat" + && name.id !== "flatMap" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + +// test_cause: +// ["\"\".aa", "check_left", "unexpected_a", ".", 3] + + check_left(left, the_token); + } + if (!name.identifier) { + +// test_cause: +// ["aa.0", "infix_dot", "expected_identifier_a", "0", 4] + + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; + } + + function infix_fart_unwrapped() { + +// test_cause: +// ["aa=>0", "infix_fart_unwrapped", "wrap_fart_parameter", "=>", 3] + + return stop("wrap_fart_parameter", token_now); + } + + function infix_grave(left) { + const the_tick = prefix_tick(); + +// test_cause: +// ["0``", "check_left", "unexpected_a", "`", 2] + + check_left(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; + } + + function infix_lbracket(left) { + const the_token = token_now; + let name; + let the_subscript = parse_expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + name = survey(the_subscript); + +// PR-404 - Add new directive "subscript" to play nice with Google Closure. + + if (!option_dict.subscript && jslint_rgx_identifier.test(name)) { + +// test_cause: +// ["aa[`aa`]", "infix_lbracket", "subscript_a", "aa", 4] + + warn("subscript_a", the_subscript, name); + } + } + +// test_cause: +// ["0[0]", "check_left", "unexpected_a", "[", 2] + + check_left(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; + } + + function infix_lparen(left) { + const the_paren = token_now; + let ellipsis; + let the_argument; + if (left.id !== "function") { + +// test_cause: +// ["(0?0:0)()", "check_left", "unexpected_a", "(", 8] +// ["0()", "check_left", "unexpected_a", "(", 2] + + check_left(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (token_nxt.id !== ")") { + +// Parse/loop through each token in expression (...). + + while (true) { + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = parse_expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + +// test_cause: +// ["aa(0)", "infix_lparen", "free", "", 0] + + test_cause("free"); + the_paren.free = true; + if (the_argument.wrapped === true) { + +// test_cause: +// ["aa((0))", "infix_lparen", "unexpected_a", "(", 3] + + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + +// test_cause: +// ["aa()", "infix_lparen", "not_free", "", 0] +// ["aa(0,0)", "infix_lparen", "not_free", "", 0] + + test_cause("not_free"); + the_paren.free = false; + } + return the_paren; + } + + function infix_option_chain(left) { + const the_token = token_now; + let name = token_nxt; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + +// test_cause: +// ["(0+0)?.0", "infix_option_chain", "check_left", "", 0] + + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + test_cause("check_left"); + +// test_cause: +// ["(/./)?.0", "check_left", "unexpected_a", "?.", 6] +// ["\"aa\"?.0", "check_left", "unexpected_a", "?.", 5] +// ["aa=[]?.aa", "check_left", "unexpected_a", "?.", 6] + + check_left(left, the_token); + } + +// Issue #468 - Fix optional dynamic-property/function-call not recognized. + + if (name.id === "[" || name.id === "(") { + test_cause("dyn_prop_or_call"); + +// test_cause: +// ["aa?.(bb)", "infix_option_chain", "dyn_prop_or_call", "", 0] +// ["aa?.[bb]", "infix_option_chain", "dyn_prop_or_call", "", 0] + + return left; + } + if (!name.identifier) { + +// test_cause: +// ["aa?.0", "infix_option_chain", "expected_identifier_a", "0", 5] + + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; + } + + function infixr(bp, id) { + +// Create a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led_infix = function parse_infixr_led(left) { + const the_token = token_now; + +// test_cause: +// ["0**0", "parse_infixr_led", "led_infix", "", 0] + + test_cause("led_infix"); + the_token.arity = "binary"; + the_token.expression = [left, parse_expression(bp - 1)]; + return the_token; + }; + return the_symbol; + } + + function parse_expression(rbp, initial) { + +// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it +// is looking for ad hoc lint patterns. We add .fud_stmt to Pratt's model, which +// is like .nud_prefix except that it is only used on the first token of a +// statement. Having .fud_stmt makes it much easier to define statement-oriented +// languages like JavaScript. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// .nud_prefix Null denotation. The prefix handler. +// .fud_stmt First null denotation. The statement handler. +// .led_infix Left denotation. The infix/postfix handler. +// lbp Left binding power of infix operator. It tells us how strongly +// the operator binds to the argument at its left. +// rbp Right binding power. + +// It processes a nud_prefix (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop (it +// consumes tokens until it meets a token whose lbp <= rbp). Specifically, it +// means that it collects all tokens that bind together before returning to the +// operator that called it. It returns the expression's parse tree. + +// For example, "3 + 1 * 2 * 4 + 5" +// parses into +// { +// "id": "+", +// "expression": [ +// { +// "id": "+", +// "expression": [ +// { +// "id": "(number)", +// "value": "3" +// }, +// { +// "id": "*", +// "expression": [ +// { +// "id": "*", +// "expression": [ +// { +// "id": "(number)", +// "value": "1" +// }, +// { +// "id": "(number)", +// "value": "2" +// } +// ] +// }, +// { +// "id": "(number)", +// "value": "4" +// } +// ] +// } +// ] +// }, +// { +// "id": "(number)", +// "value": "5" +// } +// ] +// } + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement. + + if (!initial) { + advance(); + } + the_symbol = syntax_dict[token_now.id]; + if (the_symbol !== undefined && the_symbol.nud_prefix !== undefined) { + +// test_cause: +// ["0", "parse_expression", "symbol", "", 0] + + test_cause("symbol"); + left = the_symbol.nud_prefix(); + } else if (token_now.identifier) { + +// test_cause: +// ["aa", "parse_expression", "identifier", "", 0] + + test_cause("identifier"); + left = token_now; + left.arity = "variable"; + } else { + +// test_cause: +// ["!", "parse_expression", "unexpected_a", "(end)", 1] +// ["/./", "parse_expression", "unexpected_a", "/", 1] +// ["let aa=`${}`;", "parse_expression", "unexpected_a", "}", 11] + + return stop("unexpected_a", token_now); + } + +// Parse/loop through each symbol in expression. + + while (true) { + the_symbol = syntax_dict[token_nxt.id]; + if ( + the_symbol === undefined + || the_symbol.led_infix === undefined + || the_symbol.lbp <= rbp + ) { + break; + } + advance(); + left = the_symbol.led_infix(left); + } + return left; + } + + function parse_fart(the_fart) { + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + Object.assign(the_fart, { + arity: "binary", + context: empty(), + finally: 0, + level: functionage.level + 1, + loop: 0, + name: anon, + switch: 0, + try: 0 + }); + +// PR-384 - Relax warning "function_in_loop". +// +// if (functionage.loop > 0) { + +// // test_cause: +// // ["while(0){aa.map(()=>0);}", "parse_fart", "function_in_loop", "=>", 19] +// +// warn("function_in_loop", the_fart); +// } + +// Push the current function context and establish a new one. + + function_list.push(the_fart); + function_stack.push(functionage); + functionage = the_fart; + +// Parse the parameter list. + + prefix_function_parameter(the_fart); + advance("=>"); + +// The function's body is a block. + + if (token_nxt.id === "{") { + if (!option_dict.fart) { + +// test_cause: +// ["()=>{}", "parse_fart", "use_function_not_fart", "=>", 3] + + warn("use_function_not_fart", the_fart); + } + the_fart.block = block("body"); + } else if ( + syntax_dict[token_nxt.id] !== undefined + && syntax_dict[token_nxt.id].fud_stmt !== undefined + ) { + +// PR-384 - Bugfix - Fixes issue #379 - warn against naked-statement in fart. + +// test_cause: +// ["()=>delete aa", "parse_fart", "unexpected_a_after_b", "=>", 5] + + stop("unexpected_a_after_b", token_nxt, token_nxt.id, "=>"); + +// The function's body is an expression. + + } else { + the_fart.expression = parse_expression(0); + } + +// Restore the previous context. + + functionage = function_stack.pop(); + return the_fart; + } + + function parse_json() { + let container; + let is_dup; + let name; + let negative; + switch (token_nxt.id) { + case "(number)": + if (!jslint_rgx_json_number.test(token_nxt.value)) { + +// test_cause: +// ["[-.0]", "parse_json", "unexpected_a", ".", 3] +// ["[-0x0]", "parse_json", "unexpected_a", "0x0", 3] +// ["[.0]", "parse_json", "unexpected_a", ".", 2] +// ["[0x0]", "parse_json", "unexpected_a", "0x0", 2] + + warn("unexpected_a"); + } + advance("(number)"); + return token_now; + case "(string)": + if (token_nxt.quote !== "\"") { + +// test_cause: +// ["['']", "parse_json", "unexpected_a", "'", 2] + + warn("unexpected_a", token_nxt, token_nxt.quote); + } + advance("(string)"); + return token_now; + case "-": + negative = token_nxt; + negative.arity = "unary"; + advance("-"); + +// Recurse parse_json(). + + negative.expression = parse_json(); + return negative; + case "[": + +// test_cause: +// ["[]", "parse_json", "bracket", "", 0] + + test_cause("bracket"); + container = token_nxt; + container.expression = []; + advance("["); + if (token_nxt.id !== "]") { + while (true) { + +// Recurse parse_json(). + + container.expression.push(parse_json()); + if (token_nxt.id !== ",") { + +// test_cause: +// ["[0,0]", "parse_json", "comma", "", 0] + + test_cause("comma"); + break; + } + advance(","); + } + } + advance("]", container); + return container; + case "false": + case "null": + case "true": + +// test_cause: +// ["[false]", "parse_json", "constant", "", 0] +// ["[null]", "parse_json", "constant", "", 0] +// ["[true]", "parse_json", "constant", "", 0] + + test_cause("constant"); + advance(); + return token_now; + case "{": + +// test_cause: +// ["{}", "parse_json", "brace", "", 0] + + test_cause("brace"); + container = token_nxt; + +// Explicit empty-object required to detect "__proto__". + + is_dup = empty(); + container.expression = []; + advance("{"); + if (token_nxt.id !== "}") { + +// JSON +// Parse/loop through each property in {...}. + + while (true) { + if (token_nxt.quote !== "\"") { + +// test_cause: +// ["{0:0}", "parse_json", "unexpected_a", "0", 2] + + warn( + "unexpected_a", + token_nxt, + token_nxt.quote + ); + } + name = token_nxt; + advance("(string)"); + if (is_dup[token_now.value] !== undefined) { + +// test_cause: +// ["{\"aa\":0,\"aa\":0}", "parse_json", "duplicate_a", "aa", 9] + + warn("duplicate_a", token_now); + } else if (token_now.value === "__proto__") { + +// test_cause: +// ["{\"__proto__\":0}", "parse_json", "weird_property_a", "__proto__", 2] + + warn("weird_property_a", token_now); + } else { + is_dup[token_now.value] = token_now; + } + advance(":"); + container.expression.push( + +// Recurse parse_json(). + + Object.assign(parse_json(), { + label: name + }) + ); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance("}", container); + return container; + default: + +// test_cause: +// ["[undefined]", "parse_json", "unexpected_a", "undefined", 2] + + stop("unexpected_a"); + } + } + + function parse_statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token_now.identifier && token_nxt.id === ":") { + the_label = token_now; + if (the_label.id === "ignore") { + +// test_cause: +// ["ignore:", "parse_statement", "unexpected_a", "ignore", 1] + + warn("unexpected_a", the_label); + } + advance(":"); + switch (token_nxt.id) { + case "do": + case "for": + case "switch": + case "while": + +// test_cause: +// ["aa:do{}", "parse_statement", "the_statement_label", "do", 0] +// ["aa:for{}", "parse_statement", "the_statement_label", "for", 0] +// ["aa:switch{}", "parse_statement", "the_statement_label", "switch", 0] +// ["aa:while{}", "parse_statement", "the_statement_label", "while", 0] + + test_cause("the_statement_label", token_nxt.id); + enroll(the_label, "label", true); + the_label.dead = false; + the_label.init = true; + the_statement = parse_statement(); + functionage.statement_prv = the_statement; + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + +// test_cause: +// ["aa:", "parse_statement", "unexpected_label_a", "aa", 1] + + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token_now; + first.statement = true; + the_symbol = syntax_dict[first.id]; + if ( + the_symbol !== undefined + && the_symbol.fud_stmt !== undefined + +// PR-318 - Bugfix - Fixes issues #316, #317 - dynamic-import(). + + && !(the_symbol.id === "import" && token_nxt.id === "(") + ) { + the_symbol.disrupt = false; + the_symbol.statement = true; + token_now.arity = "statement"; + the_statement = the_symbol.fud_stmt(); + functionage.statement_prv = the_statement; + } else { + +// It is an expression statement. + + the_statement = parse_expression(0, true); + functionage.statement_prv = the_statement; + if (the_statement.wrapped && the_statement.id !== "(") { + +// test_cause: +// ["(0)", "parse_statement", "unexpected_a", "(", 1] + + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; + } + + function parse_statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const statement_list = []; + let a_statement; + let disrupt = false; + +// Parse/loop each statement until a statement-terminator is reached. + + while (true) { + switch (token_nxt.id) { + case "(end)": + case "case": + case "default": + case "else": + case "}": + +// test_cause: +// [";", "parse_statements", "closer", "", 0] +// ["case", "parse_statements", "closer", "", 0] +// ["default", "parse_statements", "closer", "", 0] +// ["else", "parse_statements", "closer", "", 0] +// ["}", "parse_statements", "closer", "", 0] + + test_cause("closer"); + return statement_list; + } + a_statement = parse_statement(); + statement_list.push(a_statement); + if (disrupt) { + +// test_cause: +// ["while(0){break;0;}", "parse_statements", "unreachable_a", "0", 16] + + warn("unreachable_a", a_statement); + } + disrupt = a_statement.disrupt; + } + } + + function postassign(id) { + +// Create one of the postassign operators. + + const the_symbol = symbol(id, 150); + the_symbol.led_infix = function (left) { + token_now.expression = left; + token_now.arity = "postassign"; + check_mutation(token_now.expression); + return token_now; + }; + return the_symbol; + } + + function preassign(id) { + +// Create one of the preassign operators. + + const the_symbol = symbol(id); + the_symbol.nud_prefix = function () { + const the_token = token_now; + the_token.arity = "preassign"; + the_token.expression = parse_expression(150); + check_mutation(the_token.expression); + return the_token; + }; + return the_symbol; + } + + function prefix(id, f) { + +// Create a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud_prefix = function () { + const the_token = token_now; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = parse_expression(150); + return the_token; + }; + return the_symbol; + } + + function prefix_assign_divide() { + +// test_cause: +// ["/=", "prefix_assign_divide", "expected_a_b", "/=", 1] + + stop("expected_a_b", token_now, "/\\=", "/="); + } + + function prefix_async() { + let the_async = token_now; + let the_function; + token_nxt.arity = the_async.arity; + +// PR-414 - Parse async fart. + + if (token_nxt.fart) { + advance("("); + the_function = Object.assign(token_now.fart, { + async: 1 + }); + if (!option_dict.fart) { + +// test_cause: +// ["async()=>0", "prefix_async", "use_function_not_fart", "=>", 8] + + warn("use_function_not_fart", the_function); + } + prefix_lparen(); + +// Parse async function. + + } else { + advance("function"); + the_function = Object.assign(token_now, { + async: 1 + }); + prefix_function(); + } + if (the_function.async === 1) { + +// test_cause: +// [" +// async function aa(){} +// ", "prefix_async", "missing_await_statement", "function", 7] + + warn("missing_await_statement", the_function); + } + return the_function; + } + + function prefix_await() { + const the_await = token_now; + +// PR-370 - Add top-level-await support. + + if (functionage.async === 0 && functionage !== token_global) { + +// test_cause: +// ["function aa(){aa=await 0;}", "prefix_await", "unexpected_a", "await", 18] +// ["function aa(){await 0;}", "prefix_await", "unexpected_a", "await", 15] + + warn("unexpected_a", the_await); + } else { + functionage.async += 1; + } + if (the_await.arity === "statement") { + +// PR-405 - Bugfix - fix expression after "await" mis-identified as statement. + + the_await.expression = parse_expression(150); + semicolon(); + } else { + the_await.expression = parse_expression(150); + } + return the_await; + } + + function prefix_fart() { + +// test_cause: +// ["=>0", "prefix_fart", "expected_a_before_b", "=>", 1] + + return stop("expected_a_before_b", token_now, "()", "=>"); + } + + function prefix_function(the_function) { + let name = the_function && the_function.name; + if (the_function === undefined) { + the_function = token_now; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!token_nxt.identifier) { + +// test_cause: +// ["function(){}", "prefix_function", "expected_identifier_a", "(", 9] +// ["function*aa(){}", "prefix_function", "expected_identifier_a", "*", 9] + + return stop("expected_identifier_a"); + } + name = token_nxt; + enroll(name, "variable", true); + the_function.name = Object.assign(name, { + calls: empty(), + +// PR-331 - Bugfix - Fixes issue #272 - function hoisting not allowed. + + dead: false, + init: true + }); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + the_function.name = anon; + if (token_nxt.identifier) { + name = token_nxt; + the_function.name = name; + advance(); + } + } + } + +// Probably deadcode. +// if (mode_mega) { +// warn("unexpected_a", the_function); +// } +// jslint_assert(!mode_mega, `Expected !mode_mega.`); + +// PR-378 - Relax warning "function_in_loop". +// +// // Don't create functions in loops. It is inefficient, and it can lead to +// // scoping errors. +// +// if (functionage.loop > 0) { +// +// // test_cause: +// // [" +// // while(0){aa.map(function(){});} +// // ", "prefix_function", "function_in_loop", "function", 17] +// +// warn("function_in_loop", the_function); +// } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + Object.assign(the_function, { + async: the_function.async || 0, + context: empty(), + finally: 0, + level: functionage.level + 1, + loop: 0, + statement_prv: undefined, + switch: 0, + try: 0 + }); + if (the_function.arity !== "statement" && typeof name === "object") { + +// test_cause: +// ["let aa=function bb(){return;};", "prefix_function", "expression", "bb", 0] + + test_cause("expression", name.id); + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// PR-334 - Bugfix - fix function-redefinition not warned inside function-call. +// Push the current function context and establish a new one. + + function_list.push(the_function); + function_stack.push(functionage); + functionage = the_function; + +// Parse the parameter list. + + advance("("); + token_now.arity = "function"; + prefix_function_parameter(the_function); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && token_nxt.line === token_now.line + ) { + +// test_cause: +// ["function aa(){}0", "prefix_function", "unexpected_a", "0", 16] + + return stop("unexpected_a"); + } + if ( + token_nxt.id === "." + || token_nxt.id === "?." + +// PR-459 - Allow destructuring-assignment after function-definition. + + // || token_nxt.id === "[" + ) { + +// test_cause: +// ["function aa(){}\n.aa", "prefix_function", "unexpected_a", ".", 1] +// ["function aa(){}\n?.aa", "prefix_function", "unexpected_a", "?.", 1] + + warn("unexpected_a"); + } + +// Check functions are ordered. + + check_ordered( + "function", + function_list.slice( + function_list.indexOf(the_function) + 1 + ).map(function ({ + level, + name + }) { + return (level === the_function.level + 1) && name; + }).filter(function (name) { + return option_dict.beta && name && name.id; + }) + ); + +// Restore the previous context. + + functionage = function_stack.pop(); + return the_function; + } + + function prefix_function_parameter(the_function) { + +// This function will parse input at beginning of + + let optional; + let parameters = []; + let signature = ["("]; + let subparam; + function param_enroll(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + +// test_cause: +// ["([aa])=>0", "param_enroll", "use_function_not_fart", "=>", 7] +// ["({aa})=>0", "param_enroll", "use_function_not_fart", "=>", 7] + + if (the_function.id === "=>" && !option_dict.fart) { + warn("use_function_not_fart", the_function); + } + +// Recurse param_enroll(). + + name.names.forEach(param_enroll); + } + } + function param_parse() { + let ellipsis = false; + let param; + if (token_nxt.id === "{") { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,{}){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + param = token_nxt; + param.names = []; + advance("{"); + signature.push("{"); + while (true) { + subparam = token_nxt; + if (!subparam.identifier) { + +// test_cause: +// ["function aa(aa=0,{}){}", "param_parse", "expected_identifier_a", "}", 19] +// ["function aa({0}){}", "param_parse", "expected_identifier_a", "0", 14] + + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (token_nxt.id === ":") { + advance(":"); + advance(); + token_now.label = subparam; + subparam = token_now; + if (!subparam.identifier) { + +// test_cause: +// ["function aa({aa:0}){}", "param_parse", "expected_identifier_a", "}", 18] + + return stop( + "expected_identifier_a", + token_nxt + ); + } + } + +// test_cause: +// ["function aa({aa=aa},aa){}", "param_parse", "equal", "", 0] + + test_cause("equal"); + if (token_nxt.id === "=") { + advance("="); + subparam.expression = parse_expression(); + param.open = true; + } + param.names.push(subparam); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + } else { + break; + } + } + parameters.push(param); + +// test_cause: +// [" +// function aa({bb,aa}){} +// ", "check_ordered", "expected_a_b_before_c_d", "aa", 17] + + check_ordered("parameter", param.names); + advance("}"); + signature.push("}"); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } else if (token_nxt.id === "[") { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,[]){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + param = token_nxt; + param.names = []; + advance("["); + signature.push("[]"); + while (true) { + subparam = token_nxt; + if (!subparam.identifier) { + +// test_cause: +// ["function aa(aa=0,[]){}", "param_parse", "expected_identifier_a", "]", 19] + + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + +// test_cause: +// ["function aa([aa=aa],aa){}", "param_parse", "id", "", 0] + + test_cause("id"); + if (token_nxt.id === "=") { + advance("="); + subparam.expression = parse_expression(); + param.open = true; + } + if (token_nxt.id === ",") { + advance(","); + } else { + break; + } + } + parameters.push(param); + advance("]"); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } else { + if (token_nxt.id === "...") { + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,...){}", "param_parse", "required_a_optional_b", "aa", 21] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + } + if (!token_nxt.identifier) { + +// test_cause: +// ["function aa(0){}", "param_parse", "expected_identifier_a", "0", 13] + + return stop("expected_identifier_a"); + } + param = token_nxt; + parameters.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (token_nxt.id === "=") { + optional = param; + advance("="); + param.expression = parse_expression(0); + } else { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,bb){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } + } + } + +// test_cause: +// ["function aa(){}", "prefix_function_parameter", "opener", "(", 0] + + test_cause("opener", token_now.id); + token_now.free = false; + if (token_nxt.id !== ")" && token_nxt.id !== "(end)") { + param_parse(); + } + advance(")"); + signature.push(")"); + parameters.forEach(param_enroll); + the_function.parameters = parameters; + the_function.signature = signature.join(""); + } + + function prefix_lbrace() { + const seen = empty(); + const the_brace = token_now; + let extra; + let full; + let id; + let name; + let the_colon; + let value; + the_brace.expression = []; + if (token_nxt.id !== "}") { + +// Parse/loop through each property in {...}. + + while (true) { + name = token_nxt; + advance(); + if ( + (name.id === "get" || name.id === "set") + && token_nxt.identifier + ) { + if (!option_dict.getset) { + +// test_cause: +// ["aa={get aa(){}}", "prefix_lbrace", "unexpected_a", "get", 5] + + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + token_nxt.id; + name = token_nxt; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + +// test_cause: +// ["aa={get aa(){},get aa(){}}", "prefix_lbrace", "duplicate_a", "aa", 20] + + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else if (name.id === "`") { + +// test_cause: +// ["aa={`aa`:0}", "prefix_lbrace", "unexpected_a", "`", 5] + + stop("unexpected_a", name); + + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + +// test_cause: +// ["aa={aa,aa}", "prefix_lbrace", "duplicate_a", "aa", 8] + + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (token_nxt.id === "}" || token_nxt.id === ",") { + if (typeof extra === "string") { + +// test_cause: +// ["aa={get aa}", "prefix_lbrace", "closer", "", 0] + + test_cause("closer"); + advance("("); + } + value = parse_expression(Infinity, true); + } else if (token_nxt.id === "(") { + +// test_cause: +// ["aa={aa()}", "prefix_lbrace", "paren", "", 0] +// ["aa={get aa(){}}", "prefix_lbrace", "paren", "", 0] + + test_cause("paren"); + value = prefix_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + +// test_cause: +// ["aa={get aa.aa}", "prefix_lbrace", "paren", "", 0] + + test_cause("paren"); + advance("("); + } + the_colon = token_nxt; + advance(":"); + value = parse_expression(0); + if ( + value.id === name.id + && value.id !== "function" + ) { + +// test_cause: +// ["aa={aa:aa}", "prefix_lbrace", "unexpected_a", ": aa", 7] + + warn("unexpected_a", the_colon, ": " + name.id); + } + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + +// test_cause: +// ["aa={\"aa\":0}", "prefix_lbrace", "colon", "", 0] + + test_cause("colon"); + advance(":"); + value = parse_expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (token_nxt.id !== ",") { + break; + } + +// test_cause: +// ["aa={\"aa\":0,\"bb\":0}", "prefix_lbrace", "comma", "", 0] + + test_cause("comma"); + advance(","); + if (token_nxt.id === "}") { + +// test_cause: +// ["let aa={aa:0,}", "prefix_lbrace", "unexpected_a", ",", 13] + + warn("unexpected_a", token_now); + break; + } + } + } + +// test_cause: +// ["aa={bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8] + + check_ordered( + "property", + the_brace.expression.map(function ({ + label + }) { + return label; + }) + ); + advance("}"); + return the_brace; + } + + function prefix_lbracket() { + const the_token = token_now; + let element; + let ellipsis; + the_token.expression = []; + if (token_nxt.id !== "]") { + +// Parse/loop through each element in [...]. + + while (true) { + ellipsis = false; + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + element = parse_expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (token_nxt.id !== ",") { + break; + } + advance(","); + if (token_nxt.id === "]") { + +// test_cause: +// ["let aa=[0,]", "prefix_lbracket", "unexpected_a", ",", 10] + + warn("unexpected_a", token_now); + break; + } + } + } + advance("]"); + return the_token; + } + + function prefix_lparen() { + let the_paren = token_now; + let the_value; + +// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart. + + if (token_now.fart) { + return parse_fart(token_now.fart); + } + +// test_cause: +// ["(0)", "prefix_lparen", "expr", "", 0] + + test_cause("expr"); + the_paren.free = true; + the_value = parse_expression(0); + if (the_value.wrapped === true) { + +// test_cause: +// ["((0))", "prefix_lparen", "unexpected_a", "(", 1] + + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + return the_value; + } + + function prefix_new() { + const the_new = token_now; + let right; + right = parse_expression(160); + if (token_nxt.id !== "(") { + +// test_cause: +// ["new aa", "prefix_new", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "()", artifact()); + } + the_new.expression = right; + return the_new; + } + + function prefix_tick() { + const the_tick = token_now; + the_tick.value = []; + the_tick.expression = []; + if (token_nxt.id !== "`") { + +// Parse/loop through each token in `${...}`. + + while (true) { + advance("(string)"); + the_tick.value.push(token_now); + if (token_nxt.id !== "${") { + break; + } + advance("${"); + +// test_cause: +// ["let aa=`${}`;", "prefix_tick", "${", "", 0] + + test_cause("${"); + the_tick.expression.push(parse_expression(0)); + advance("}"); + } + } + advance("`"); + return the_tick; + } + + function prefix_void() { + const the_void = token_now; + +// test_cause: +// ["void 0", "prefix_void", "unexpected_a", "void", 1] +// ["void", "prefix_void", "unexpected_a", "void", 1] + + warn("unexpected_a", the_void); + the_void.expression = parse_expression(0); + return the_void; + } + + function semicolon() { + +// Try to match a semicolon. + + if (token_nxt.id === ";") { + advance(";"); + } else { + +// test_cause: +// ["0", "semicolon", "expected_a_b", "(end)", 1] + + warn_at( + "expected_a_b", + token_now.line, + token_now.thru + 1, + ";", + artifact() + ); + } + anon = "anonymous"; + } + + function stmt(id, fud_stmt) { + +// Create a statement. + + const the_symbol = symbol(id); + the_symbol.fud_stmt = fud_stmt; + return the_symbol; + } + + function stmt_break() { + const the_break = token_now; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + +// test_cause: +// ["break", "stmt_break", "unexpected_a", "break", 1] + + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (token_nxt.identifier && token_now.line === token_nxt.line) { + the_label = functionage.context[token_nxt.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + if (the_label !== undefined && the_label.dead) { + +// test_cause: +// ["aa:{function aa(aa){break aa;}}", "stmt_break", "out_of_scope_a", "aa", 27] + + warn("out_of_scope_a"); + } else { + +// test_cause: +// ["aa:{break aa;}", "stmt_break", "not_label_a", "aa", 11] + + warn("not_label_a"); + } + } else { + the_label.used += 1; + } + the_break.label = token_nxt; + advance(); + } + advance(";"); + return the_break; + } + + function stmt_continue() { + const the_continue = token_now; + if (functionage.loop < 1 || functionage.finally > 0) { + +// test_cause: +// ["continue", "stmt_continue", "unexpected_a", "continue", 1] +// [" +// function aa(){while(0){try{}finally{continue}}} +// ", "stmt_continue", "unexpected_a", "continue", 37] + + warn("unexpected_a", the_continue); + } + check_not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; + } + + function stmt_debugger() { + const the_debug = token_now; + if (!option_dict.devel) { + +// test_cause: +// ["debugger", "stmt_debugger", "unexpected_a", "debugger", 1] + + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; + } + + function stmt_delete() { + const the_token = token_now; + const the_value = parse_expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + +// test_cause: +// ["delete 0", "stmt_delete", "expected_a_b", "0", 8] + + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; + } + + function stmt_do() { + const the_do = token_now; + check_not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + +// test_cause: +// ["function aa(){do{break;}while(0)}", "stmt_do", "weird_loop", "do", 15] + + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; + } + + function stmt_export() { + let export_list = []; + let the_export = token_now; + let the_id; + let the_name; + let the_thing; + + the_export.expression = []; + if (token_nxt.id === "default") { + if (export_dict.default !== undefined) { + +// test_cause: +// [" +// export default 0;export default 0 +// ", "stmt_export", "duplicate_a", "default", 25] + + warn("duplicate_a"); + } + advance("default"); + the_thing = parse_expression(0); + if ( + the_thing.id !== "(" + || the_thing.expression[0].id !== "." + || the_thing.expression[0].expression.id !== "Object" + || the_thing.expression[0].name.id !== "freeze" + ) { + +// test_cause: +// ["export default {}", "stmt_export", "freeze_exports", "{", 16] + + warn("freeze_exports", the_thing); + +// PR-301 - Bugfix - Fixes issues #282 - optional-semicolon. + + } else { + +// test_cause: +// [" +// export default Object.freeze({}) +// ", "semicolon", "expected_a_b", "(end)", 32] + + semicolon(); + } + export_dict.default = the_thing; + the_export.expression.push(the_thing); + } else { + +// PR-439 - Add grammar for "export async function ...". + + if (token_nxt.id === "function" || token_nxt.id === "async") { + +// test_cause: +// ["export async function aa(){}", "stmt_export", "freeze_exports", "async", 8] +// ["export function aa(){}", "stmt_export", "freeze_exports", "function", 8] + + warn("freeze_exports"); + the_thing = parse_statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (export_dict[the_id] !== undefined) { + +// test_cause: +// [" +// let aa;export{aa};export function aa(){} +// ", "stmt_export", "duplicate_a", "aa", 35] + + warn("duplicate_a", the_name); + } + export_dict[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + token_nxt.id === "var" + || token_nxt.id === "let" + || token_nxt.id === "const" + ) { + +// test_cause: +// ["export const", "stmt_export", "unexpected_a", "const", 8] +// ["export let", "stmt_export", "unexpected_a", "let", 8] +// ["export var", "stmt_export", "unexpected_a", "var", 8] + + warn("unexpected_a"); + parse_statement(); + } else if (token_nxt.id === "{") { + +// test_cause: +// ["export {}", "stmt_export", "advance{", "", 0] + + test_cause("advance{"); + advance("{"); + while (true) { + if (!token_nxt.identifier) { + +// test_cause: +// ["export {}", "stmt_export", "expected_identifier_a", "}", 9] + + stop("expected_identifier_a"); + } + the_id = token_nxt.id; + export_list.push(token_nxt); + the_name = token_global.context[the_id]; + if (the_name === undefined) { + +// test_cause: +// ["export {aa}", "stmt_export", "unexpected_a", "aa", 9] + + warn("unexpected_a"); + } else { + the_name.used += 1; + if (export_dict[the_id] !== undefined) { + +// test_cause: +// ["let aa;export{aa,aa}", "stmt_export", "duplicate_a", "aa", 18] + + warn("duplicate_a"); + } + export_dict[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + if (token_nxt.id === ",") { + advance(","); + } else { + break; + } + } + +// PR-439 - Check exported properties are ordered. + +// test_cause: +// ["export {bb, aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 13] + + check_ordered("export", export_list); + advance("}"); + semicolon(); + } else { + +// test_cause: +// ["export", "stmt_export", "unexpected_a", "(end)", 1] + + stop("unexpected_a"); + } + } + state.mode_module = true; + return the_export; + } + + function stmt_for() { + let first; + let the_for = token_now; + if (!option_dict.for) { + +// test_cause: +// ["for", "stmt_for", "unexpected_a", "for", 1] + + warn("unexpected_a", the_for); + } + check_not_top_level(the_for); + functionage.loop += 1; + advance("("); + token_now.free = true; + if (token_nxt.id === ";") { + +// test_cause: +// ["for(;;){}", "stmt_for", "expected_a_b", "for (;", 1] + + return stop("expected_a_b", the_for, "while (", "for (;"); + } + switch (token_nxt.id) { + case "const": + case "let": + case "var": + +// test_cause: +// ["for(const aa in aa){}", "stmt_for", "unexpected_a", "const", 5] + + return stop("unexpected_a"); + } + first = parse_expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + +// test_cause: +// ["for(0 in aa){}", "stmt_for", "bad_assignment_a", "0", 5] + + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = parse_expression(0); + advance(";"); + the_for.inc = parse_expression(0); + if (the_for.inc.id === "++") { + +// test_cause: +// ["for(aa;aa;aa++){}", "stmt_for", "expected_a_b", "++", 13] + + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + +// test_cause: +// [" +// /*jslint for*/ +// function aa(bb,cc){for(0;0;0){break;}} +// ", "stmt_for", "weird_loop", "for", 20] + + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; + } + + function stmt_if() { + const the_if = token_now; + let the_else; + the_if.expression = condition(); + the_if.block = block(); + if (token_nxt.id === "else") { + advance("else"); + the_else = token_now; + the_if.else = ( + token_nxt.id === "if" + ? parse_statement() + : block() + ); + +// test_cause: +// ["if(0){0}else if(0){0}", "stmt_if", "else", "", 0] +// ["if(0){0}else{0}", "stmt_if", "else", "", 0] + + test_cause("else"); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + +// test_cause: +// ["if(0){break;}else{break;}", "stmt_if", "disrupt", "", 0] + + test_cause("disrupt"); + the_if.disrupt = true; + } else { + +// test_cause: +// ["if(0){break;}else{}", "stmt_if", "unexpected_a", "else", 14] + + warn("unexpected_a", the_else); + } + } + } + return the_if; + } + + function stmt_import() { + const the_import = token_now; + let name; + let names; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// if (typeof state.mode_module === "object") { +// +// // test_cause: +// // [" +// // /*global aa*/ +// // import aa from "aa" +// // ", "stmt_import", "unexpected_directive_a", "global", 1] +// +// warn( +// "unexpected_directive_a", +// state.mode_module, +// state.mode_module.directive +// ); +// } + + state.mode_module = true; + +// PR-436 - Add grammar for side-effect import-statement. + + if (token_nxt.id === "(string)") { + +// test_cause: +// ["import \"./aa.mjs\";", "stmt_import", "import_side_effect", "", 0] + + test_cause("import_side_effect"); + warn("expected_a_b", token_nxt, "{", artifact()); + advance(); + semicolon(); + return the_import; + } + if (token_nxt.identifier) { + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// ["import ignore from \"aa\"", "stmt_import", "unexpected_a", "ignore", 8] + + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + names = []; + advance("{"); + if (token_nxt.id !== "}") { + while (true) { + if (!token_nxt.identifier) { + +// test_cause: +// ["import {", "stmt_import", "expected_identifier_a", "(end)", 1] + + stop("expected_identifier_a"); + } + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// ["import {ignore} from \"aa\"", "stmt_import", "unexpected_a", "ignore", 9] + + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token_now; + if (!jslint_rgx_module.test(token_now.value)) { + +// test_cause: +// ["import aa from \"!aa\"", "stmt_import", "bad_module_name_a", "!aa", 16] + + warn("bad_module_name_a", token_now); + } + import_list.push(token_now.value); + semicolon(); + return the_import; + } + + function stmt_lbrace() { + +// test_cause: +// [";{}", "stmt_lbrace", "naked_block", "{", 2] +// ["class aa{}", "stmt_lbrace", "naked_block", "{", 9] + + warn("naked_block", token_now); + return block("naked"); + } + + function stmt_return() { + const the_return = token_now; + check_not_top_level(the_return); + if (functionage.finally > 0) { + +// test_cause: +// [" +// function aa(){try{}finally{return;}} +// ", "stmt_return", "unexpected_a", "return", 28] + + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (token_nxt.id !== ";" && the_return.line === token_nxt.line) { + the_return.expression = parse_expression(10); + } + advance(";"); + return the_return; + } + + function stmt_semicolon() { + +// test_cause: +// [";", "stmt_semicolon", "unexpected_a", ";", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function stmt_switch() { + const the_cases = []; + const the_switch = token_now; + let dups = []; + let exp; + let last; + let stmts; + let the_case; + let the_default; + let the_disrupt = true; + let the_last; + function is_dup(thing) { + return is_equal(thing, exp); + } + check_not_top_level(the_switch); + if (functionage.finally > 0) { + +// test_cause: +// [" +// function aa(){try{}finally{switch(0){}}} +// ", "stmt_switch", "unexpected_a", "switch", 28] + + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token_now.free = true; + the_switch.expression = parse_expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + while (true) { + +// Loop through cases with breaks. + + the_case = token_nxt; + the_case.arity = "statement"; + the_case.expression = []; + while (true) { + +// Loop through fallthrough cases. + + advance("case"); + token_now.switch = true; + exp = parse_expression(0); + if (dups.some(is_dup)) { + +// test_cause: +// [" +// switch(0){case 0:break;case 0:break} +// ", "stmt_switch", "unexpected_a", "0", 29] + + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (token_nxt.id !== "case") { + break; + } + } + +// test_cause: +// [" +// switch(0){case 1:case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 23] +// [" +// switch(0){case "aa":case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 26] +// [" +// switch(0){case "bb":case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 26] +// [" +// switch(0){case aa:case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24] +// [" +// switch(0){case bb:case aa:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24] + + check_ordered_case(the_case.expression); + stmts = parse_statements(); + if (stmts.length < 1) { + +// test_cause: +// [" +// switch(0){case 0:default:} +// ", "stmt_switch", "expected_statements_a", "default", 18] +// ["switch(0){case 0:}", "stmt_switch", "expected_statements_a", "}", 18] + + warn("expected_statements_a"); + +// PR-359 - Bugfix - Fixes issue #358 - switch-statement crashes jslint. + + break; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn("expected_a_before_b", token_nxt, "break;", artifact()); + } + if (token_nxt.id !== "case") { + break; + } + } + +// test_cause: +// [" +// switch(0){case 1:break;case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 29] +// [" +// switch(0){case "aa":break;case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 32] +// [" +// switch(0){case "bb":break;case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 32] +// [" +// switch(0){case aa:break;case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30] +// [" +// switch(0){case bb:break;case aa:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30] + + check_ordered_case(the_cases.map(function ({ + expression + }) { + return expression[0]; + })); + dups = undefined; + if (token_nxt.id === "default") { + the_default = token_nxt; + advance("default"); + token_now.switch = true; + advance(":"); + the_switch.else = parse_statements(); + if (the_switch.else.length < 1) { + +// test_cause: +// [" +// switch(0){case 0:break;default:} +// ", "stmt_switch", "unexpected_a", "default", 24] + + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + the_last = the_switch.else[ + the_switch.else.length - 1 + ]; + if ( + the_last.id === "break" + && the_last.label === undefined + ) { + +// test_cause: +// [" +// switch(0){case 0:break;default:break;} +// ", "stmt_switch", "unexpected_a", "break", 32] + + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; + } + + function stmt_throw() { + const the_throw = token_now; + the_throw.disrupt = true; + the_throw.expression = parse_expression(10); + semicolon(); + if (functionage.try > 0) { + +// test_cause: +// ["try{throw 0}catch(){}", "stmt_throw", "unexpected_a", "throw", 5] + + warn("unexpected_a", the_throw); + } + return the_throw; + } + + function stmt_try() { + const the_try = token_now; + let ignored; + let the_catch; + let the_disrupt; + if (functionage.try > 0) { + +// test_cause: +// ["try{try{}catch(){}}catch(){}", "stmt_try", "unexpected_a", "try", 5] + + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (token_nxt.id === "catch") { + ignored = "ignore"; + the_catch = token_nxt; + the_try.catch = the_catch; + advance("catch"); + +// Create new catch-scope for catch-parameter. + + catch_stack.push(catchage); + catchage = the_catch; + catch_list.push(catchage); + the_catch.context = empty(); + if (token_nxt.id === "(") { + advance("("); + if (!token_nxt.identifier) { + +// test_cause: +// ["try{}catch(){}", "stmt_try", "expected_identifier_a", ")", 12] + + return stop("expected_identifier_a"); + } + if (token_nxt.id !== "ignore") { + ignored = undefined; + the_catch.name = token_nxt; + enroll(token_nxt, "exception", true); + } + advance(); + advance(")"); + } + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + +// Restore previous catch-scope after catch-block. + + catchage = catch_stack.pop(); + +// PR-404 - Relax warning about missing `catch` in `try...finally` statement. +// +// } else { +// +// // test_cause: +// // ["try{}finally{break;}", "stmt_try", "expected_a_before_b", "finally", 6] +// +// warn("expected_a_before_b", token_nxt, "catch", artifact()); + + } + if (token_nxt.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; + } + + function stmt_var() { + let ellipsis; + let mode_const; + let name; + let the_brace; + let the_bracket; + let the_variable = token_now; + let variable_prv; + mode_const = the_variable.id === "const"; + the_variable.names = []; + +// A program may use var or let, but not both. + + if (!mode_const) { + if (mode_var === undefined) { + mode_var = the_variable.id; + } else if (the_variable.id !== mode_var) { + +// test_cause: +// ["let aa;var aa", "stmt_var", "expected_a_b", "var", 8] + + warn("expected_a_b", the_variable, mode_var, the_variable.id); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + +// test_cause: +// ["switch(0){case 0:var aa}", "stmt_var", "var_switch", "var", 18] + + warn("var_switch", the_variable); + } + switch ( + Boolean(functionage.statement_prv) + && functionage.statement_prv.id + ) { + case "const": + case "let": + case "var": + +// test_cause: +// ["const aa=0;const bb=0;", "stmt_var", "var_prv", "const", 0] +// ["let aa=0;let bb=0;", "stmt_var", "var_prv", "let", 0] +// ["var aa=0;var bb=0;", "stmt_var", "var_prv", "var", 0] + + test_cause("var_prv", functionage.statement_prv.id); + variable_prv = functionage.statement_prv; + break; + case "import": + +// test_cause: +// ["import aa from \"aa\";\nlet bb=0;", "stmt_var", "import_prv", "", 0] + + test_cause("import_prv"); + break; + case false: + break; + default: + if ( + (option_dict.beta && !option_dict.variable) + || the_variable.id === "var" + ) { + +// test_cause: +// ["console.log();let aa=0;", "stmt_var", "var_on_top", "let", 15] +// ["console.log();var aa=0;", "stmt_var", "var_on_top", "var", 15] +// ["try{aa();}catch(aa){var aa=0;}", "stmt_var", "var_on_top", "var", 21] +// ["while(0){var aa;}", "stmt_var", "var_on_top", "var", 10] + + warn("var_on_top", token_now); + } + } + while (true) { + if (token_nxt.id === "{") { + if (the_variable.id === "var") { + +// test_cause: +// ["var{aa}=0", "stmt_var", "unexpected_a", "var", 1] + + warn("unexpected_a", the_variable); + } + the_brace = token_nxt; + advance("{"); + while (true) { + name = token_nxt; + if (!name.identifier) { + +// test_cause: +// ["let {0}", "stmt_var", "expected_identifier_a", "0", 6] + + return stop("expected_identifier_a"); + } + survey(name); + advance(); + if (token_nxt.id === ":") { + advance(":"); + if (!token_nxt.identifier) { + +// test_cause: +// ["let {aa:0}", "stmt_var", "expected_identifier_a", "0", 9] +// ["let {aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 9] + + return stop("expected_identifier_a"); + } + +// PR-363 - Bugfix +// Add test against false-warning in code +// '/*jslint node*/\nlet {aa:bb} = {}; bb();'. +// +// token_nxt.label = name; +// the_variable.names.push(token_nxt); +// enroll(token_nxt, "variable", mode_const); + + name = token_nxt; + the_variable.names.push(name); + survey(name); + enroll(name, "variable", mode_const); + + advance(); + the_brace.open = true; + } else { + the_variable.names.push(name); + enroll(name, "variable", mode_const); + } + name.dead = false; + name.init = true; + if (token_nxt.id === "=") { + +// test_cause: +// ["let {aa=0}", "stmt_var", "assign", "", 0] + + test_cause("assign"); + advance("="); + name.expression = parse_expression(); + the_brace.open = true; + } + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + +// test_cause: +// ["let{bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8] + + check_ordered(the_variable.id, the_variable.names); + advance("}"); + advance("="); + the_variable.expression = parse_expression(0); + } else if (token_nxt.id === "[") { + if (the_variable.id === "var") { + +// test_cause: +// ["var[aa]=0", "stmt_var", "unexpected_a", "var", 1] + + warn("unexpected_a", the_variable); + } + the_bracket = token_nxt; + advance("["); + while (true) { + ellipsis = false; + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + if (!token_nxt.identifier) { + +// test_cause: +// ["let[]", "stmt_var", "expected_identifier_a", "]", 5] + + return stop("expected_identifier_a"); + } + name = token_nxt; + advance(); + the_variable.names.push(name); + enroll(name, "variable", mode_const); + name.dead = false; + name.init = true; + if (ellipsis) { + name.ellipsis = true; + break; + } + if (token_nxt.id === "=") { + advance("="); + name.expression = parse_expression(); + the_bracket.open = true; + } + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + advance("]"); + advance("="); + the_variable.expression = parse_expression(0); + } else if (token_nxt.identifier) { + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// [" +// let ignore;function aa(ignore) {} +// ", "stmt_var", "unexpected_a", "ignore", 5] + + warn("unexpected_a", name); + } + enroll(name, "variable", mode_const); + if (token_nxt.id === "=" || mode_const) { + advance("="); + name.dead = false; + name.init = true; + name.expression = parse_expression(0); + } + the_variable.names.push(name); + } else { + +// test_cause: +// ["let 0", "stmt_var", "expected_identifier_a", "0", 5] +// ["var{aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 8] + + return stop("expected_identifier_a"); + } + if (token_nxt.id !== ",") { + break; + } + +// test_cause: +// ["let aa,bb;", "stmt_var", "expected_a_b", ",", 7] + + warn("expected_a_b", token_nxt, ";", ","); + advance(","); + } + +// Warn if variable declarations are unordered. + + if ( + option_dict.beta + && !option_dict.unordered + && !option_dict.variable + && variable_prv + && ( + variable_prv.id + " " + variable_prv.names[0].id + > the_variable.id + " " + the_variable.names[0].id + ) + ) { + +// test_cause: +// ["const bb=0;const aa=0;", "stmt_var", "expected_a_b_before_c_d", "aa", 12] +// ["let bb;let aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8] +// ["var bb;var aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8] + + warn( + "expected_a_b_before_c_d", + the_variable, + the_variable.id, + the_variable.names[0].id, + variable_prv.id, + variable_prv.names[0].id + ); + } + semicolon(); + return the_variable; + } + + function stmt_while() { + const the_while = token_now; + check_not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + +// test_cause: +// ["function aa(){while(0){break;}}", "stmt_while", "weird_loop", "while", 15] + + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; + } + + function stmt_with() { + +// test_cause: +// ["with", "stmt_with", "unexpected_a", "with", 1] + + stop("unexpected_a", token_now); + } + + function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!jslint_rgx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!jslint_rgx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + +// test_cause: +// ["let aa={0:0}", "survey", "expected_identifier_a", "0", 9] + + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property_dict[id] === "number") { + property_dict[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (state.mode_property) { + if (tenure[id] !== true) { + +// test_cause: +// ["/*property aa*/\naa.bb", "survey", "unregistered_property_a", "bb", 4] + + warn("unregistered_property_a", name); + } + } else if ( + !option_dict.nomen + && name.identifier + && jslint_rgx_weird_property.test(id) + ) { + +// test_cause: +// ["aa.$", "survey", "weird_property_a", "$", 4] +// ["aa._", "survey", "weird_property_a", "_", 4] +// ["aa._aa", "survey", "weird_property_a", "_aa", 4] +// ["aa.aaSync", "survey", "weird_property_a", "aaSync", 4] +// ["aa.aa_", "survey", "weird_property_a", "aa_", 4] + + warn("weird_property_a", name); + } + property_dict[id] = 1; + } + return id; + } + +// These functions are used to specify the grammar of our language: + + function symbol(id, bp) { + +// Create a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax_dict[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax_dict[id] = the_symbol; + } + return the_symbol; + } + + function ternary(id1, id2) { + +// Create a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led_infix = function parse_ternary_led(left) { + const the_token = token_now; + let second; + second = parse_expression(20); + advance(id2); + token_now.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, parse_expression(10)]; + if (token_nxt.id !== ")") { + +// test_cause: +// ["0?0:0", "parse_ternary_led", "use_open", "?", 2] + + warn("use_open", the_token); + } + return the_token; + }; + return the_symbol; + } + +// Now we parse JavaScript. +// Begin defining the language. + + assignment("%="); + assignment("&="); + assignment("*="); + assignment("+="); + assignment("-="); + assignment("/="); + assignment("<<="); + assignment("="); + assignment(">>="); + assignment(">>>="); + assignment("^="); + assignment("|="); + constant("(number)", "number"); + constant("(regexp)", "regexp"); + constant("(string)", "string"); + constant("Function", "function", constant_Function); + constant("Infinity", "number", Infinity); + constant("NaN", "number", NaN); + constant("arguments", "object", constant_arguments); + constant("eval", "function", constant_eval); + constant("false", "boolean", false); + constant("ignore", "undefined", constant_ignore); + constant("isFinite", "function", constant_isInfinite); + constant("isNaN", "function", constant_isNaN); + constant("null", "null", null); + constant("this", "object", constant_this); + constant("true", "boolean", true); + constant("undefined", "undefined"); + infix(100, "!="); + infix(100, "!=="); + infix(100, "=="); + infix(100, "==="); + infix(110, "<"); + infix(110, "<="); + infix(110, ">"); + infix(110, ">="); + infix(110, "in"); + infix(110, "instanceof"); + infix(120, "<<"); + infix(120, ">>"); + infix(120, ">>>"); + infix(130, "+"); + infix(130, "-"); + infix(140, "%"); + infix(140, "*"); + infix(140, "/"); + infix(160, "(", infix_lparen); + infix(160, "`", infix_grave); + infix(170, ".", infix_dot); + infix(170, "=>", infix_fart_unwrapped); + infix(170, "?.", infix_option_chain); + infix(170, "[", infix_lbracket); + infix(35, "??"); + infix(40, "||"); + infix(50, "&&"); + infix(70, "|"); + infix(80, "^"); + infix(90, "&"); + infixr(150, "**"); + postassign("++"); + postassign("--"); + preassign("++"); + preassign("--"); + prefix("!!"); + prefix("!"); + prefix("(", prefix_lparen); + prefix("+"); + prefix("-"); + prefix("/=", prefix_assign_divide); + prefix("=>", prefix_fart); + prefix("[", prefix_lbracket); + prefix("`", prefix_tick); + prefix("async", prefix_async); + prefix("await", prefix_await); + prefix("function", prefix_function); + prefix("new", prefix_new); + prefix("typeof"); + prefix("void", prefix_void); + prefix("{", prefix_lbrace); + prefix("~"); + stmt(";", stmt_semicolon); + stmt("async", prefix_async); + stmt("await", prefix_await); + stmt("break", stmt_break); + stmt("const", stmt_var); + stmt("continue", stmt_continue); + stmt("debugger", stmt_debugger); + stmt("delete", stmt_delete); + stmt("do", stmt_do); + stmt("export", stmt_export); + stmt("for", stmt_for); + stmt("function", prefix_function); + stmt("if", stmt_if); + stmt("import", stmt_import); + stmt("let", stmt_var); + stmt("return", stmt_return); + stmt("switch", stmt_switch); + stmt("throw", stmt_throw); + stmt("try", stmt_try); + stmt("var", stmt_var); + stmt("while", stmt_while); + stmt("with", stmt_with); + stmt("{", stmt_lbrace); + symbol(")"); + symbol("*/"); + symbol(","); + symbol(":"); + symbol(";"); + symbol("]"); + symbol("async"); + symbol("await"); + symbol("case"); + symbol("catch"); + symbol("class"); + symbol("default"); + symbol("else"); + symbol("enum"); + symbol("finally"); + symbol("implements"); + symbol("interface"); + symbol("package"); + symbol("private"); + symbol("protected"); + symbol("public"); + symbol("static"); + symbol("super"); + symbol("void"); + symbol("yield"); + symbol("}"); + ternary("?", ":"); + +// Init token_nxt. + + advance(); + +// Parsing of JSON is simple: + + if (state.mode_json) { + state.token_tree = parse_json(); + advance("(end)"); + return; + } + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option_dict.browser) { + if (token_nxt.id === ";") { + advance(";"); + } + +// If we are not in a browser, then the file form of strict pragma may be used. + + } else if (token_nxt.value === "use strict") { + advance("(string)"); + advance(";"); + } + state.token_tree = parse_statements(); + advance("(end)"); + +// Check global functions are ordered. + + check_ordered( + "function", + function_list.map(function ({ + level, + name + }) { + return (level === 1) && name; + }).filter(function (name) { + return option_dict.beta && name && name.id; + }) + ); +} + +function jslint_phase4_walk(state) { + +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). + + let { + artifact, + catch_stack, + function_stack, + global_dict, + is_equal, + is_weird, + option_dict, + syntax_dict, + test_cause, + token_global, + warn + } = state; + let block_stack = []; // The stack of blocks. + let blockage = token_global; // The current block. + let catchage = catch_stack[0]; // The current catch-block. + let functionage = token_global; // The current function. + let postaction; + let postamble; + let posts = empty(); + let preaction; + let preamble; + let pres = empty(); + +// The relational operators. + + let relationop = object_assign_from_list(empty(), [ + "!=", "!==", "<", "<=", "==", "===", ">", ">=" + ], true); + +// Ambulation of the parse tree. + + function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; + } + + function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + task(the_token); + }); + } + } + }; + } + + function init_variable(name) { + let the_variable = lookup(name); + if (!the_variable || the_variable.readonly) { + warn("bad_assignment_a", name); + return; + } + the_variable.init = true; + } + + function lookup(thing) { + let id = thing.id; + let the_variable; + if (thing.arity !== "variable") { + return; + } + +// Look up the variable in the current context. + + the_variable = functionage.context[id] || catchage.context[id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable && the_variable.role === "label") { + +// test_cause: +// ["aa:while(0){aa;}", "lookup", "label_a", "aa", 13] + + warn("label_a", thing); + return the_variable; + } + if (!the_variable) { + function_stack.forEach(function ({ + context + }) { + if (context[id] && context[id].role !== "label") { + the_variable = context[id]; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (!the_variable && global_dict[id] === undefined) { + +// test_cause: +// ["aa", "lookup", "undeclared_a", "aa", 1] +// ["class aa{}", "lookup", "undeclared_a", "aa", 7] +// [" +// let aa=0;try{aa();}catch(bb){bb();}bb(); +// ", "lookup", "undeclared_a", "bb", 36] +// [" +// let aa=0;try{aa();}catch(ignore){bb();} +// ", "lookup", "undeclared_a", "bb", 34] + + warn("undeclared_a", thing); + return; + } + if (!the_variable) { + the_variable = { + dead: false, + id, + init: true, + parent: token_global, + readonly: true, + role: "variable", + used: 0 + }; + token_global.context[id] = the_variable; + } + the_variable.closure = true; + functionage.context[id] = the_variable; + } + if ( + ( + the_variable.calls === undefined + || functionage.name === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + && the_variable.dead + ) { + +// test_cause: +// ["let aa;if(aa){let bb;}bb;", "lookup", "out_of_scope_a", "bb", 23] + + warn("out_of_scope_a", thing); + } + return the_variable; + } + + function post_a(thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + let right; + if (thing.id === "=") { + if (thing.names !== undefined) { + +// test_cause: +// ["if(0){aa=0}", "post_a", "=", "", 0] + + test_cause("="); + +// Probably deadcode. +// if (Array.isArray(thing.names)) { +// thing.names.forEach(init_variable); +// } else { +// init_variable(thing.names); +// } + + jslint_assert( + !Array.isArray(thing.names), + `Expected !Array.isArray(thing.names).` + ); + init_variable(thing.names); + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + +// test_cause: +// ["aa.aa=undefined", "post_a", "expected_a_b", "undefined", 1] + + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.readonly) { + warn("bad_assignment_a", lvalue); + } + } + right = syntax_dict[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + +// test_cause: +// ["aa+=undefined", "post_a", "unexpected_a", "undefined", 5] + + warn("unexpected_a", thing.expression[1]); + } + } + } + + function post_a_pluseq(thing) { + const right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } + } + + function post_b(thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || is_equal(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + +// test_cause: +// ["if(0===0){0}", "post_b", "weird_relation_a", "===", 5] + + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option_dict.convert) { + if (thing.expression[0].value === "") { + +// test_cause: +// ["\"\"+0", "post_b", "expected_a_b", "\"\" +", 3] + + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + +// test_cause: +// ["0+\"\"", "post_b", "expected_a_b", "+ \"\"", 2] + + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + +// test_cause: +// ["aa=window[0]", "post_b", "weird_expression_a", "window[...]", 10] + + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + +// test_cause: +// ["aa=self[0]", "post_b", "weird_expression_a", "self[...]", 8] + + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === "." || thing.id === "?.") { + if (thing.expression.id === "RegExp") { + +// test_cause: +// ["aa=RegExp.aa", "post_b", "weird_expression_a", ".", 10] + + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + +// test_cause: +// ["0- -0", "post_b", "wrap_unary", "-", 4] + + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } + } + + function post_b_and(thing) { + if ( + is_weird(thing.expression[0]) + || is_equal(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + +// test_cause: +// ["aa=(()=>0)&&(()=>0)", "post_b_and", "weird_condition_a", "&&", 11] +// ["aa=(``?``:``)&&(``?``:``)", "post_b_and", "weird_condition_a", "&&", 14] +// ["aa=/./&&/./", "post_b_and", "weird_condition_a", "&&", 7] +// ["aa=0&&0", "post_b_and", "weird_condition_a", "&&", 5] +// ["aa=[]&&[]", "post_b_and", "weird_condition_a", "&&", 6] +// ["aa=`${0}`&&`${0}`", "post_b_and", "weird_condition_a", "&&", 10] +// [" +// aa=function aa(){}&&function aa(){} +// ", "post_b_and", "weird_condition_a", "&&", 19] +// ["aa={}&&{}", "post_b_and", "weird_condition_a", "&&", 6] + + warn("weird_condition_a", thing); + } + } + + function post_b_lbracket(thing) { + if (thing.expression[0].id === "RegExp") { + +// test_cause: +// ["aa=RegExp[0]", "post_b_lbracket", "weird_expression_a", "[", 10] + + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + +// test_cause: +// ["aa[[0]]", "post_b_lbracket", "weird_expression_a", "[", 4] + + warn("weird_expression_a", thing.expression[1]); + } + } + + function post_b_lparen(thing) { + let arg; + let array; + let cack; + let left = thing.expression[0]; + let new_date; + let paren; + let the_new; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + +// test_cause: +// ["aa=function(){}()", "post_b_lparen", "wrap_immediate", "(", 16] + + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "BigInt" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + +// test_cause: +// ["new BigInt()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Boolean()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Number()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new String()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Symbol()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new aa()", "post_b_lparen", "unexpected_a", "new", 1] + + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option_dict.eval) { + +// test_cause: +// ["new Function()", "post_b_lparen", "unexpected_a", "new Function", 5] + + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + +// test_cause: +// ["new Array()", "post_b_lparen", "expected_a_b", "new Array", 5] + + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + +// test_cause: +// ["new Object()", "post_b_lparen", "expected_a_b", "new Object", 5] + + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "BigInt" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + +// test_cause: +// ["let Aa=Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8] + + warn("expected_a_before_b", left, "new", artifact(left)); + } + } + } else if (left.id === ".") { + cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + +// test_cause: +// ["new Date.UTC()", "post_b_lparen", "cack", "", 0] + + test_cause("cack"); + cack = !cack; + } + if (jslint_rgx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + +// test_cause: +// ["new Date.UTC()", "post_b_lparen", "unexpected_a", "new", 1] + + warn("unexpected_a", the_new); + } else { + +// test_cause: +// ["let Aa=Aa.Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8] + + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + paren = left.expression; + if (paren.id === "(") { + array = paren.expression; + if (array.length === 1) { + new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + +// test_cause: +// [" +// new Date().getTime() +// ", "post_b_lparen", "expected_a_b", "new Date().getTime()", 1] + + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } + } + + function post_b_or(thing) { + if ( + is_weird(thing.expression[0]) + || is_equal(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + +// test_cause: +// ["aa=0||0", "post_b_or", "weird_condition_a", "||", 5] + + warn("weird_condition_a", thing); + } + } + + function post_s_export(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== token_global) { + +// test_cause: +// [" +// if(0){import aa from "aa";} +// ", "post_s_export", "misplaced_a", "import", 7] + + warn("misplaced_a", the_thing); + } + } + + function post_s_for(thing) { + +// Recurse walk_statement(). + + walk_statement(thing.inc); + } + + function post_s_function(thing) { + delete functionage.async; + delete functionage.finally; + delete functionage.loop; + delete functionage.statement_prv; + delete functionage.switch; + delete functionage.try; + functionage = function_stack.pop(); + if (thing.wrapped) { + +// test_cause: +// ["aa=(function(){})", "post_s_function", "unexpected_parens", "function", 5] + + warn("unexpected_parens", thing); + } + return post_s_lbrace(); + } + + function post_s_import(the_thing) { + const name = the_thing.name; + if (name) { + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return post_s_export(the_thing); + } + } + + function post_s_lbrace() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); + } + + function post_s_try(thing) { + if (thing.catch) { + if (thing.catch.name) { + Object.assign(catchage.context[thing.catch.name.id], { + dead: false, + init: true + }); + } + +// Recurse walk_statement(). + + walk_statement(thing.catch.block); + +// Restore previous catch-scope after catch-block. + + catchage = catch_stack.pop(); + } + } + + function post_s_var(thing) { + thing.names.forEach(function (name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + +// Probably deadcode. +// if (name.id === "{" || name.id === "[") { +// name.names.forEach(subactivate); +// } else { +// name.init = true; +// } + + jslint_assert( + !(name.id === "{" || name.id === "["), + `Expected !(name.id === "{" || name.id === "[").` + ); + name.init = true; + } + blockage.live.push(name); + }); + } + + function post_t(thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || is_equal(thing.expression[1], thing.expression[2]) + ) { + +// test_cause: +// ["let aa=(aa?`${0}`:`${0}`);", "post_t", "unexpected_a", "?", 11] +// ["let aa=(aa?`0`:`0`);", "post_t", "unexpected_a", "?", 11] + + warn("unexpected_a", thing); + } else if (is_equal(thing.expression[0], thing.expression[1])) { + +// test_cause: +// ["aa?aa:0", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "||", "?"); + } else if (is_equal(thing.expression[0], thing.expression[2])) { + +// test_cause: +// ["aa?0:aa", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + +// test_cause: +// ["aa?true:false", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + +// test_cause: +// ["aa?false:true", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + +// test_cause: +// ["(aa&&!aa?0:1)", "post_t", "wrap_condition", "&&", 4] + + warn("wrap_condition", thing.expression[0]); + } + } + + function post_u(thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option_dict.convert) { + +// test_cause: +// ["!!0", "post_u", "expected_a_b", "!!", 1] + + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } + } + + function post_u_plus(thing) { + const right = thing.expression; + if (!option_dict.convert) { + +// test_cause: +// ["aa=+0", "post_u_plus", "expected_a_b", "+", 4] + + warn("expected_a_b", thing, "Number(...)", "+"); + } + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } + } + + function pre_a_bitwise(thing) { + +// These are the bitwise operators. + + switch (!option_dict.bitwise && thing.id) { + case "&": + case "&=": + case "<<": + case "<<=": + case ">>": + case ">>=": + case ">>>": + case ">>>=": + case "^": + case "^=": + case "|": + case "|=": + case "~": + +// test_cause: +// ["0&0", "pre_a_bitwise", "unexpected_a", "&", 2] +// ["0&=0", "pre_a_bitwise", "unexpected_a", "&=", 2] +// ["0<<0", "pre_a_bitwise", "unexpected_a", "<<", 2] +// ["0<<=0", "pre_a_bitwise", "unexpected_a", "<<=", 2] +// ["0>>0", "pre_a_bitwise", "unexpected_a", ">>", 2] +// ["0>>=0", "pre_a_bitwise", "unexpected_a", ">>=", 2] +// ["0>>>0", "pre_a_bitwise", "unexpected_a", ">>>", 2] +// ["0>>>=0", "pre_a_bitwise", "unexpected_a", ">>>=", 2] +// ["0^0", "pre_a_bitwise", "unexpected_a", "^", 2] +// ["0^=0", "pre_a_bitwise", "unexpected_a", "^=", 2] +// ["0|0", "pre_a_bitwise", "unexpected_a", "|", 2] +// ["0|=0", "pre_a_bitwise", "unexpected_a", "|=", 2] +// ["~0", "pre_a_bitwise", "unexpected_a", "~", 1] + + warn("unexpected_a", thing); + break; + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + +// test_cause: +// ["0<0<0", "pre_a_bitwise", "unexpected_a", "<", 4] + + warn("unexpected_a", thing); + } + } + + function pre_b(thing) { + let left; + let right; + let value; + if (relationop[thing.id] === true) { + left = thing.expression[0]; + right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + +// test_cause: +// ["NaN===NaN", "pre_b", "number_isNaN", "===", 4] + + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + +// test_cause: +// ["typeof 0===0", "pre_b", "expected_string_a", "0", 12] + + warn("expected_string_a", right); + } + } else { + value = right.value; + if (value === "null" || value === "undefined") { + +// test_cause: +// [" +// typeof aa==="undefined" +// ", "pre_b", "unexpected_typeof_a", "undefined", 13] + + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "bigint" + && value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + +// test_cause: +// ["typeof 0===\"aa\"", "pre_b", "expected_type_string_a", "aa", 12] + + warn("expected_type_string_a", right, value); + } + } + } + } + } + + function pre_b_eqeq(thing) { + +// test_cause: +// ["0==0", "pre_b_eqeq", "expected_a_b", "==", 2] + + warn("expected_a_b", thing, "===", "=="); + } + + function pre_b_in(thing) { + +// test_cause: +// ["aa in aa", "pre_b_in", "infix_in", "in", 4] + + warn("infix_in", thing); + } + + function pre_b_instanceof(thing) { + +// test_cause: +// ["0 instanceof 0", "pre_b_instanceof", "unexpected_a", "instanceof", 3] + + warn("unexpected_a", thing); + } + + function pre_b_lparen(thing) { + const left = thing.expression[0]; + let left_variable; + let parent; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + parent = functionage.name.parent; + if (parent) { + left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + +// Probably deadcode. +// && left_variable.dead + + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } + } + + function pre_b_noteq(thing) { + +// test_cause: +// ["0!=0", "pre_b_noteq", "expected_a_b", "!=", 2] + + warn("expected_a_b", thing, "!==", "!="); + } + + function pre_b_or(thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + +// test_cause: +// ["0&&0||0", "pre_b_or", "and", "&&", 2] + + warn("and", thang); + } + }); + } + + function pre_s_for(thing) { + let the_variable; + if (thing.name !== undefined) { + thing.name.dead = false; + the_variable = lookup(thing.name); + if (the_variable !== undefined) { + if (the_variable.init && the_variable.readonly) { + +// test_cause: +// ["const aa=0;for(aa in aa){}", "pre_s_for", "bad_assignment_a", "aa", 16] + + warn("bad_assignment_a", thing.name); + } + the_variable.init = true; + } + } + +// Recurse walk_statement(). + + walk_statement(thing.initial); + } + + function pre_s_function(thing) { + +// test_cause: +// ["()=>0", "pre_s_function", "", "", 0] +// ["(function (){}())", "pre_s_function", "", "", 0] +// ["function aa(){}", "pre_s_function", "", "", 0] + + test_cause(""); + if (thing.arity === "statement" && blockage.body !== true) { + +// test_cause: +// ["if(0){function aa(){}\n}", "pre_s_function", "unexpected_a", "function", 7] + + warn("unexpected_a", thing); + } + function_stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + +// test_cause: +// [" +// /*jslint getset*/ +// aa={get aa(aa){}} +// ", "pre_s_function", "bad_get", "function", 9] + + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + +// test_cause: +// [" +// /*jslint getset*/ +// aa={set aa(){}} +// ", "pre_s_function", "bad_set", "function", 9] + + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); + } + + function pre_s_lbrace(thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; + } + + function pre_try(thing) { + if (thing.catch !== undefined) { + +// Create new catch-scope for catch-parameter. + + catch_stack.push(catchage); + catchage = thing.catch; + } + } + + function pre_v(thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } + } + + function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); + } + + function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + +// test_cause: +// ["(function(){}())", "walk_expression", "isArray", "", 0] +// ["0&&0", "walk_expression", "isArray", "", 0] + + test_cause("isArray"); + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + +// PR-414 - Bugfix - fix fart-body not being walked. + + if (thing.id === "function" || thing.id === "=>") { + +// test_cause: +// ["aa=()=>0", "walk_expression", "function", "=>", 0] +// ["aa=function(){}", "walk_expression", "function", "function", 0] + + test_cause("function", thing.id); + +// Recurse walk_statement(). + + walk_statement(thing.block); + } + if ( + thing.arity === "preassign" || thing.arity === "postassign" + ) { + +// test_cause: +// ["aa=++aa", "walk_expression", "unexpected_a", "++", 4] +// ["aa=--aa", "walk_expression", "unexpected_a", "--", 4] + + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + +// test_cause: +// ["aa[aa=0]", "walk_expression", "unexpected_statement_a", "=", 6] + + warn("unexpected_statement_a", thing); + } + +// test_cause: +// ["aa=0", "walk_expression", "default", "", 0] + + test_cause("default"); + postamble(thing); + } + } + } + + function walk_statement(thing) { + if (!thing) { + return; + } + if (Array.isArray(thing)) { + +// test_cause: +// ["+[]", "walk_statement", "isArray", "", 0] + + test_cause("isArray"); + +// Recurse walk_statement(). + + thing.forEach(walk_statement); + return; + } + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + +// test_cause: +// ["0&&0", "walk_statement", "unexpected_expression_a", "&&", 2] + + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + && thing.id !== "import" + ) { + +// test_cause: +// ["!0", "walk_statement", "unexpected_expression_a", "!", 1] +// ["+[]", "walk_statement", "unexpected_expression_a", "+", 1] +// ["+new aa()", "walk_statement", "unexpected_expression_a", "+", 1] +// ["0", "walk_statement", "unexpected_expression_a", "0", 1] +// ["typeof 0", "walk_statement", "unexpected_expression_a", "typeof", 1] + + warn("unexpected_expression_a", thing); + } + +// Recurse walk_statement(). + + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + + postaction = action(posts); + postamble = amble(posts); + preaction = action(pres); + preamble = amble(pres); + postaction("assignment", "+=", post_a_pluseq); + postaction("assignment", post_a); + postaction("binary", "&&", post_b_and); + postaction("binary", "(", post_b_lparen); + postaction("binary", "=>", post_s_function); + postaction("binary", "[", post_b_lbracket); + postaction("binary", "||", post_b_or); + postaction("binary", post_b); + postaction("statement", "const", post_s_var); + postaction("statement", "export", post_s_export); + postaction("statement", "for", post_s_for); + postaction("statement", "function", post_s_function); + postaction("statement", "import", post_s_import); + postaction("statement", "let", post_s_var); + postaction("statement", "try", post_s_try); + postaction("statement", "var", post_s_var); + postaction("statement", "{", post_s_lbrace); + postaction("ternary", post_t); + postaction("unary", "+", post_u_plus); + postaction("unary", "function", post_s_function); + postaction("unary", post_u); + preaction("assignment", pre_a_bitwise); + preaction("binary", "!=", pre_b_noteq); + preaction("binary", "(", pre_b_lparen); + preaction("binary", "==", pre_b_eqeq); + preaction("binary", "=>", pre_s_function); + preaction("binary", "in", pre_b_in); + preaction("binary", "instanceof", pre_b_instanceof); + preaction("binary", "||", pre_b_or); + preaction("binary", pre_b); + preaction("binary", pre_a_bitwise); + preaction("statement", "for", pre_s_for); + preaction("statement", "function", pre_s_function); + preaction("statement", "try", pre_try); + preaction("statement", "{", pre_s_lbrace); + preaction("unary", "function", pre_s_function); + preaction("unary", "~", pre_a_bitwise); + preaction("variable", pre_v); + + walk_statement(state.token_tree); +} + +function jslint_phase5_whitage(state) { + +// PHASE 5. Check whitespace between tokens in . + + let { + artifact, + catch_list, + function_list, + function_stack, + option_dict, + test_cause, + token_global, + token_list, + warn + } = state; + let closer = "(end)"; + let free = false; + +// free = false + +// cause: +// "()=>0" +// "aa()" +// "aa(0,0)" +// "function(){}" + +// free = true + +// cause: +// "(0)" +// "(aa)" +// "aa(0)" +// "do{}while()" +// "for(){}" +// "if(){}" +// "switch(){}" +// "while(){}" + + let left = token_global; + let margin = 0; + let mode_indent = ( + +// PR-330 - Allow 2-space indent. + + option_dict.indent2 + ? 2 + : 4 + ); + let nr_comments_skipped = 0; + let open = true; + let opening = true; + let right; + +// This is the set of infix operators that require a space on each side. + + let spaceop = object_assign_from_list(empty(), [ + "!=", "!==", "%", "%=", "&", "&&", "&=", "*", "*=", "+=", "-=", "/", + "/=", "<", "<<", "<<=", "<=", "=", "==", "===", "=>", ">", ">=", ">>", + ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" + ], true); + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + const name = the_function.context[id]; + if (id !== "ignore" && name.parent === the_function) { + +// test_cause: +// ["function aa(aa) {return aa;}", "delve", "id", "", 0] + + test_cause("id"); + if ( + name.used === 0 + +// Probably deadcode. +// && ( +// name.role !== "function" +// || name.parent.arity !== "unary" +// ) + + && jslint_assert( + name.role !== "function", + `Expected name.role !== "function".` + ) + ) { + +// test_cause: +// ["/*jslint node*/\nlet aa;", "delve", "unused_a", "aa", 5] +// ["function aa(aa){return;}", "delve", "unused_a", "aa", 13] +// ["let aa=0;try{aa();}catch(bb){aa();}", "delve", "unused_a", "bb", 26] + + warn("unused_a", name); + } else if (!name.init) { + +// test_cause: +// ["/*jslint node*/\nlet aa;aa();", "delve", "uninitialized_a", "aa", 5] + + warn("uninitialized_a", name); + } + } + }); + } + + function expected_at(at) { + +// Probably deadcode. +// if (right === undefined) { +// right = token_nxt; +// } + + jslint_assert( + !(right === undefined), + `Expected !(right === undefined).` + ); + warn( + "expected_a_at_b_c", + right, + artifact(right), + +// Fudge column numbers in warning message. + + at + jslint_fudge, + right.from + jslint_fudge + ); + } + + function no_space() { + if (left.line === right.line) { + +// from: +// if (left.line === right.line) { +// no_space(); +// } else { + + if (left.thru !== right.from && nr_comments_skipped === 0) { + +// test_cause: +// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14] + + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + +// from: +// } else if ( +// right.arity === "binary" +// && right.id === "(" +// && free +// ) { +// no_space(); +// } else if ( + +// Probably deadcode. +// if (open) { +// const at = ( +// free +// ? margin +// : margin + 8 +// ); +// if (right.from < at) { +// expected_at(at); +// } +// } else { +// if (right.from !== margin + 8) { +// expected_at(margin + 8); +// } +// } + + jslint_assert(open, `Expected open.`); + jslint_assert(free, `Expected free.`); + if (right.from < margin) { + +// test_cause: +// ["let aa = aa(\naa\n()\n);", "expected_at", "expected_a_at_b_c", "5", 1] + + expected_at(margin); + } + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line || !open) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (right.from !== margin) { + expected_at(margin); + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn("expected_space_a_b", right, artifact(left), artifact(right)); + } + } + + function pop() { + const previous = function_stack.pop(); + closer = previous.closer; + free = previous.free; + margin = previous.margin; + open = previous.open; + opening = previous.opening; + } + + function push() { + function_stack.push({ + closer, + free, + margin, + open, + opening + }); + } + +// uninitialized_and_unused(); +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (state.mode_module === true || option_dict.node) { + delve(token_global); + } + catch_list.forEach(delve); + function_list.forEach(delve); + + if (option_dict.white) { + return; + } + +// whitage(); +// Go through the token list, looking at usage of whitespace. + + token_list.forEach(function whitage(the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + +// The open and close pairs. + + switch (left.id) { + case "${": + case "(": + case "[": + case "{": + +// test_cause: +// ["let aa=[];", "whitage", "opener", "", 0] +// ["let aa=`${0}`;", "whitage", "opener", "", 0] +// ["let aa=aa();", "whitage", "opener", "", 0] +// ["let aa={};", "whitage", "opener", "", 0] + + test_cause("opener"); + +// Probably deadcode. +// case "${}": + + jslint_assert( + !(left.id + right.id === "${}"), + "Expected !(left.id + right.id === \"${}\")." + ); + switch (left.id + right.id) { + case "()": + case "[]": + case "{}": + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like '{]') have already been detected. + +// test_cause: +// ["let aa=[];", "whitage", "opener_closer", "", 0] +// ["let aa=aa();", "whitage", "opener_closer", "", 0] +// ["let aa={};", "whitage", "opener_closer", "", 0] + + test_cause("opener_closer"); + if (left.line === right.line) { + +// test_cause: +// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14] + + no_space(); + } else { + +// test_cause: +// ["let aa = aa(\n );", "expected_at", "expected_a_at_b_c", "1", 2] + + at_margin(0); + } + break; + default: + +// test_cause: +// ["let aa=(0);", "whitage", "opener_operand", "", 0] +// ["let aa=[0];", "whitage", "opener_operand", "", 0] +// ["let aa=`${0}`;", "whitage", "opener_operand", "", 0] +// ["let aa=aa(0);", "whitage", "opener_operand", "", 0] +// ["let aa={aa:0};", "whitage", "opener_operand", "", 0] + + test_cause("opener_operand"); + opening = left.open || (left.line !== right.line); + push(); + switch (left.id) { + case "${": + closer = "}"; + break; + case "(": + closer = ")"; + break; + case "[": + closer = "]"; + break; + case "{": + closer = "}"; + break; + } + if (opening) { + +// test_cause: +// ["function aa(){\nreturn;\n}", "whitage", "opening", "", 0] +// ["let aa=(\n0\n);", "whitage", "opening", "", 0] +// ["let aa=[\n0\n];", "whitage", "opening", "", 0] +// ["let aa=`${\n0\n}`;", "whitage", "opening", "", 0] +// ["let aa={\naa:0\n};", "whitage", "opening", "", 0] + + test_cause("opening"); + free = closer === ")" && left.free; + open = true; + margin += mode_indent; + if (right.role === "label") { + if (right.from !== 0) { + +// test_cause: +// [" +// function aa() { +// bb: +// while (aa) { +// if (aa) { +// break bb; +// } +// } +// } +// ", "expected_at", "expected_a_at_b_c", "1", 2] + + expected_at(0); + } + } else if (right.switch) { + at_margin(-mode_indent); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + +// test_cause: +// [" +// function aa() {bb: +// while (aa) { +// aa(); +// } +// } +// ", "whitage", "expected_line_break_a_b", "bb", 16] + + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + +// test_cause: +// ["let aa=(0);", "whitage", "not_free", "", 0] +// ["let aa=[0];", "whitage", "not_free", "", 0] +// ["let aa=`${0}`;", "whitage", "not_free", "", 0] +// ["let aa={aa:0};", "whitage", "not_free", "", 0] + + test_cause("not_free"); + free = false; + open = false; + +// test_cause: +// ["let aa = ( 0 );", "no_space_only", "unexpected_space_a_b", "0", 12] + + no_space_only(); + } + } + break; + default: + if (right.statement === true) { + if (left.id === "else") { + +// test_cause: +// [" +// let aa = 0; +// if (aa) { +// aa(); +// } else if (aa) { +// aa(); +// } +// ", "one_space_only", "expected_space_a_b", "if", 9] + + one_space_only(); + } else { + +// test_cause: +// [" let aa = 0;", "expected_at", "expected_a_at_b_c", "1", 2] + + at_margin(0); + open = false; + } + +// If right is a closer, then pop the previous state. + + } else if (right.id === closer) { + pop(); + if (opening && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-mode_indent); + } else if (right.role === "label") { + if (right.from !== 0) { + +// test_cause: +// [" +// function aa() { +// aa();cc: +// while (aa) { +// if (aa) { +// break cc; +// } +// } +// } +// ", "expected_at", "expected_a_at_b_c", "1", 10] + + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + +// test_cause: +// ["let {aa,bb} = 0;", "one_space", "expected_space_a_b", "bb", 9] + + one_space(); + } else { + +// test_cause: +// [" +// function aa() { +// aa( +// 0,0 +// ); +// } +// ", "expected_at", "expected_a_at_b_c", "9", 11] + + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + +// test_cause: +// [" +// let aa = ( +// aa +// ? 0 +// : 1 +// ); +// ", "expected_at", "expected_a_at_b_c", "5", 1] + + at_margin(0); + } else { + +// test_cause: +// ["let aa = (aa ? 0 : 1);", "whitage", "use_open", "?", 14] + + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + +// test_cause: +// ["let aa = aa(\naa ()\n);", "no_space", "unexpected_space_a_b", "(", 4] + + no_space(); + } else if ( + left.id === "." + || left.id === "?." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + || (right.id === "." || right.id === "?.") + ) { + +// test_cause: +// ["let aa = 0 ;", "no_space_only", "unexpected_space_a_b", ";", 12] +// ["let aa = aa ?.aa;", "no_space_only", "unexpected_space_a_b", "?.", 13] + + no_space_only(); + } else if (left.id === ";") { + +// test_cause: +// [" +// /*jslint for*/ +// function aa() { +// for ( +// aa(); +// aa; +// aa() +// ) { +// aa(); +// } +// } +// ", "expected_at", "expected_a_at_b_c", "9", 1] + + if (open) { + at_margin(0); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || left.id === "await" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + +// test_cause: +// [" +// function aa() { +// do { +// aa(); +// } while(aa()); +// } +// ", "one_space_only", "expected_space_a_b", "(", 12] + + one_space_only(); + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || left.id === "async" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + +// test_cause: +// ["let aa=0;", "one_space", "expected_space_a_b", "0", 8] +// ["let aa={\naa:\n0\n};", "expected_at", "expected_a_at_b_c", "5", 1] + + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +function jslint_report({ + exports, + froms, + functions, + global, + json, + module, + property, + stop, + warnings +}) { + +// This function will create human-readable, html-report +// for warnings, properties, and functions from jslint-result-object. +// +// Example usage: +// let result = jslint("console.log('hello world')"); +// let html = jslint_report(result); + + let html = ""; + let length_80 = 1111; + + function address(line = 1, column = 1) { + +// This function will create HTML address element from and + + return `
    ${Number(line)}: ${Number(column)}
    `; + + } + + function detail(title, list) { + return ( + (Array.isArray(list) && list.length > 0) + ? ( + +// Google Lighthouse Accessibility -
    's do not contain only properly-ordered +//
    and
    groups, + + + + + + + + + + + + +

    CodeMirror: JSLint Demo

    +

    +This demo will auto-lint the code below, and auto-generate a report as you type. +

    + + + + + + + +
    + + + + + diff --git a/branch-master/jslint_wrapper_codemirror.js b/branch-master/jslint_wrapper_codemirror.js new file mode 100644 index 000000000..0cdc0950c --- /dev/null +++ b/branch-master/jslint_wrapper_codemirror.js @@ -0,0 +1,146 @@ +// The Unlicense +// +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + + +// This wrapper will integrate JSLint with CodeMirror's lint.js addon. +// Requires CodeMirror and JSLint. +// +// Example usage: +/* + + + + + + + + + + + +*/ + +/*jslint browser, devel*/ +/*global CodeMirror define exports jslint module require*/ +/*property + Pos, amd, column, error, from, globals, jslint, line, map, message, + mode_stop, registerHelper, result, severity, signal, source, to, warnings +*/ + +(function (mod) { + "use strict"; + +// CommonJS + + if (typeof exports === "object" && typeof module === "object") { + mod(require("../../lib/codemirror")); + +// AMD + + } else if (typeof define === "function" && define.amd) { + define(["../../lib/codemirror"], mod); + +// Plain browser env + + } else { + mod(CodeMirror); + } +}(function (CodeMirror) { + "use strict"; + if (!window.jslint) { + console.error( + "Error: window.jslint not defined," + + " CodeMirror JavaScript linting cannot run." + ); + return []; + } + CodeMirror.registerHelper("lint", "javascript", function ( + source, + options, + editor + ) { + let result; + +// Emit before linter is run, so it can be modified +// before passing to jslint. +// +// Example usage: +// editor.on("lintJslintBefore", function (options) { +// options.browser = true; +// options.node = true; +// options.globals = ["caches", "indexedDb"]; +// }); + + options.source = source; + CodeMirror.signal(editor, "lintJslintBefore", options); + +// Run jslint. + + result = jslint.jslint(source, options, options.globals); + +// Emit after linter is run, so it can be used to generate reports. +// +// Example usage: +// editor.on("lintJslintAfter", function (options) { +// divReport.innerHTML = jslint.jslint_report(options.result); +// }); + + options.result = result; + CodeMirror.signal(editor, "lintJslintAfter", options); + +// Return warnings. + + return result.warnings.map(function ({ + column, + line, + message, + mode_stop + }) { + return { + from: CodeMirror.Pos(line - 1, column - 1), //jslint-ignore-line + message, + severity: ( + mode_stop + ? "error" + : "warning" + ), + to: CodeMirror.Pos(line - 1, column) //jslint-ignore-line + }; + }); + }); +})); diff --git a/branch-master/jslint_wrapper_vim.vim b/branch-master/jslint_wrapper_vim.vim new file mode 100644 index 000000000..ce6700c1c --- /dev/null +++ b/branch-master/jslint_wrapper_vim.vim @@ -0,0 +1,59 @@ +"" The Unlicense +"" +"" This is free and unencumbered software released into the public domain. +"" +"" Anyone is free to copy, modify, publish, use, compile, sell, or +"" distribute this software, either in source code form or as a compiled +"" binary, for any purpose, commercial or non-commercial, and by any +"" means. +"" +"" In jurisdictions that recognize copyright laws, the author or authors +"" of this software dedicate any and all copyright interest in the +"" software to the public domain. We make this dedication for the benefit +"" of the public at large and to the detriment of our heirs and +"" successors. We intend this dedication to be an overt act of +"" relinquishment in perpetuity of all present and future rights to this +"" software under copyright law. +"" +"" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +"" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +"" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +"" IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +"" OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +"" ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +"" OTHER DEALINGS IN THE SOFTWARE. + + +"" jslint_wrapper_vim.vim +"" +"" jslint wrapper for vim +"" +"" 1. Save this file and "jslint.mjs" to directory "~/.vim/" +"" 2. Add vim-command ":source ~/.vim/jslint_wrapper_vim.vim" to file "~/.vimrc" +"" 3. Vim can now jslint files (via nodejs): +"" - with vim-command ":SaveAndJslint" +"" - with vim-key-combo " " + +let s:dir = expand(":p:h") + +"" this function will save current file and jslint it (via nodejs) +function! SaveAndJslint(bang) + "" save file + if a:bang == "!" | write! | else | write | endif + "" jslint file (via nodejs) + let &l:errorformat = + \ "%f..js:%n:%l:%c:%m," . + \ "%f:%n:%l:%c:%m" + let &l:makeprg = "node" + \ . " \"" . s:dir . "/jslint.mjs\"" + \ . " jslint_wrapper_vim" + \ . " \"" . fnamemodify(bufname("%"), ":p") . "\"" + silent make! | cwindow | redraw! +endfunction + +"" create vim-command ":SaveAndJslint" +command! -nargs=* -bang SaveAndJslint call SaveAndJslint("") + +"" map vim-key-combo " " to ":SaveAndJslint" +inoremap :SaveAndJslint +nnoremap :SaveAndJslint diff --git a/branch-master/jslint_wrapper_vscode.js b/branch-master/jslint_wrapper_vscode.js new file mode 100644 index 000000000..3ecf2a54e --- /dev/null +++ b/branch-master/jslint_wrapper_vscode.js @@ -0,0 +1,243 @@ +// The Unlicense +// +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + + +/*jslint beta, node*/ +/*property + Diagnostic, DiagnosticSeverity, ProgressLocation, Warning, Window, activate, + cancellable, character, clear, column, commands, createDiagnosticCollection, + document, end, endsWith, exports, fsPath, getText, increment, insert, + isEmpty, jslint, languages, line, lineAt, location, map, message, module, + promises, push, range, rangeIncludingLineBreak, readFileSync, + registerTextEditorCommand, replace, report, runInNewContext, selection, set, + slice, start, subscriptions, title, uri, warnings, window, withProgress, + writeFile +*/ + +"use strict"; + +function activate({ + subscriptions +}) { +/** + * @param {vscode.ExtensionContext} context + */ +// This method is called when your extension is activated. +// Your extension is activated the very first time the command is executed. + +// Directly print diagnostic without language-server. +// https://stackoverflow.com/questions/35581332 +// /create-diagnostics-entries-without-language-server-in-visual-studio-code + + let diagnosticCollection; + let jslint; + let vscode; + + function jslintClear() { + return vscode.window.withProgress({ + cancellable: false, + location: vscode.ProgressLocation.Window, + title: "JSLint clear warnings ..." + }, async function (progress) { + progress.report({ + increment: 0 + }); + +// Clear "Problems" tab. + + diagnosticCollection.clear(); + +// Wait awhile for flicker to give user visual confirmation "Problems" tab +// is refreshed. + + await new Promise(function (resolve) { + setTimeout(resolve, 500); + }); + + progress.report({ + increment: 100 + }); + }); + } + + function jslintDisableRegion({ + document, + selection + }, edit) { + let range; + let text; + edit.insert({ + character: 0, + line: selection.start.line + }, "/*jslint-disable*/\n"); + range = document.lineAt(selection.end).rangeIncludingLineBreak; + text = document.getText(range); + +// If selection-end is EOL without preceding line-break, +// then prepend line-break before directive. + + if (!text.endsWith("\n")) { + text += "\n/*jslint-enable*/"; + +// If selection-end is start of a new line, then prepend directive before it. + + } else if (!selection.isEmpty && selection.end.character === 0) { + text = "/*jslint-enable*/\n" + text; + +// Append directive to selection-end. + + } else { + text += "/*jslint-enable*/\n"; + } + edit.replace(range, text); + } + + function jslintIgnoreLine({ + document, + selection + }, edit) { + edit.insert({ + character: document.lineAt(selection.end).range.end.character, + line: selection.end.line + }, " //jslint-ignore-line"); + } + + function jslintLint({ + document + }) { + return vscode.window.withProgress({ + cancellable: false, + location: vscode.ProgressLocation.Window, + title: "JSLint lint file ..." + }, async function (progress) { + let result; + progress.report({ + increment: 0 + }); + +// Clear "Problems" tab. + + diagnosticCollection.clear(); + result = document.getText(); + result = jslint.jslint(result); + result = result.warnings.slice(0, 100).map(function ({ + column, + line, + // line_source, + message + }) { + return new vscode.Diagnostic( + // code: line_source, + { + end: { + character: column - 1, + line: line - 1 + }, + start: { + character: column - 1, + line: line - 1 + } + }, + `JSLint - ${message}`, + vscode.DiagnosticSeverity.Warning + ); + }); + +// Wait awhile for flicker to give user visual confirmation "Problems" tab +// is refreshed. + + await new Promise(function (resolve) { + setTimeout(resolve, 100); + }); + +// Update "Problems" tab. + + diagnosticCollection.set(document.uri, result); + progress.report({ + increment: 100 + }); + }); + } + +// PR-429 - Add manual lint-on-save command. + + async function jslintLintAndSave({ + document + }) { + jslintLint({ + document + }); + await require("fs").promises.writeFile( + document.uri.fsPath, + document.getText() + ); + } + +// Initialize vscode and jslint. + + vscode = require("vscode"); + diagnosticCollection = vscode.languages.createDiagnosticCollection( + "jslint" + ); + require("vm").runInNewContext( + ( + "\"use strict\";" + + require("fs").readFileSync( //jslint-ignore-line + __dirname + "/jslint.mjs", + "utf8" + ).replace( + "\nexport default Object.freeze(jslint_export);", + "\nmodule.exports = jslint_export;" + ).replace( + "\njslint_import_meta_url = import.meta.url;", + "\n// jslint_import_meta_url = import.meta.url;" + ) + ), + { + module + } + ); + jslint = module.exports; + +// Register extension commands. + + subscriptions.push(vscode.commands.registerTextEditorCommand(( + "jslint.clear" + ), jslintClear)); + subscriptions.push(vscode.commands.registerTextEditorCommand(( + "jslint.disableRegion" + ), jslintDisableRegion)); + subscriptions.push(vscode.commands.registerTextEditorCommand(( + "jslint.ignoreLine" + ), jslintIgnoreLine)); + subscriptions.push(vscode.commands.registerTextEditorCommand(( + "jslint.lint" + ), jslintLint)); + subscriptions.push(vscode.commands.registerTextEditorCommand(( + "jslint.lintAndSave" + ), jslintLintAndSave)); +} + +exports.activate = activate; diff --git a/branch-master/package.json b/branch-master/package.json new file mode 100644 index 000000000..d1f5d2207 --- /dev/null +++ b/branch-master/package.json @@ -0,0 +1,39 @@ +{ + "bin": { + "jslint": "jslint.mjs" + }, + "bugs": { + "url": "https://github.com/jslint-org/jslint/issues" + }, + "counter": 0, + "description": "JSLint, The JavaScript Code Quality and Coverage Tool", + "exports": { + "default": "./jslint_wrapper_cjs.cjs", + "import": "./jslint.mjs" + }, + "fileCount": 34, + "keywords": [ + "coverage-report", + "javascript", + "jslint", + "linter", + "zero-config", + "zero-dependency" + ], + "license": "UNLICENSE", + "main": "./jslint_wrapper_cjs.cjs", + "module": "./jslint.mjs", + "name": "@jslint-org/jslint", + "repository": { + "type": "git", + "url": "git+https://github.com/jslint-org/jslint.git" + }, + "scripts": { + "test": "node jslint.mjs v8_coverage_report=.artifact/coverage node test.mjs", + "test2": "sh jslint_ci.sh shCiBase" + }, + "shCiArtifactUpload": 1, + "shCiPublishNpm": 1, + "type": "module", + "version": "2024.11.24" +} diff --git a/branch-master/test.mjs b/branch-master/test.mjs new file mode 100644 index 000000000..24967f887 --- /dev/null +++ b/branch-master/test.mjs @@ -0,0 +1,1575 @@ +/*jslint beta, node*/ +import jslint from "./jslint.mjs"; +import jslintCjs from "./jslint_wrapper_cjs.cjs"; +import moduleFs from "fs"; +import modulePath from "path"; + +let { + assertErrorThrownAsync, + assertJsonEqual, + assertOrThrow, + debugInline, + fsWriteFileWithParents, + globExclude, + jstestDescribe, + jstestIt, + jstestOnExit, + moduleFsInit, + noop, + v8CoverageListMerge, + v8CoverageReportCreate +} = jslint; +let sourceJslintMjs; +let testCoverageMergeData; + +await (async function init() { + +// Coverage-hack - Ugly-hack to get test-coverage for all initialization-states. + + moduleFsInit(); + moduleFsInit(); + +// Cleanup directory .tmp + + await moduleFs.promises.rm(".tmp", { + recursive: true + }).catch(noop); + +// init sourceJslintMjs + + sourceJslintMjs = await moduleFs.promises.readFile("jslint.mjs", "utf8"); + +// init testCoverageMergeData + + testCoverageMergeData = JSON.parse( + await moduleFs.promises.readFile( + "test_coverage_merge_data.json", + "utf8" + ) + ); +}()); + +jstestDescribe(( + "test fsXxx handling-behavior" +), function testBehaviorFsXxx() { + jstestIt(( + "test fsWriteFileWithParents handling-behavior" + ), async function () { + await Promise.all([ + 1, 2, 3, 4 + ].map(async function () { + await fsWriteFileWithParents( + ".tmp/fsWriteFileWithParents/aa/bb/cc", + "aa" + ); + })); + assertJsonEqual( + await moduleFs.promises.readFile( + ".tmp/fsWriteFileWithParents/aa/bb/cc", + "utf8" + ), + "aa" + ); + }); +}); + +jstestDescribe(( + "test globXxx handling-behavior" +), function testBehaviorGlobXxx() { + jstestIt(( + "test globAssertNotWeird-error handling-behavior" + ), async function () { + await Promise.all([ + "\n", + "\r", + "\u0000" + ].map(async function (char) { + await assertErrorThrownAsync(function () { + return globExclude({ + pathnameList: [ + "aa", + `cc/${char}/dd`, + "bb" + ] + }); + }, ( + "Weird character " + + JSON.stringify(char).replace("\\", "\\\\") + + " found in " + )); + })); + }); + jstestIt(( + "test globExclude handling-behavior" + ), function () { + let pathnameList = [ + ".dockerignore", + ".eslintrc.js", + ".gitignore", + ".npmignore", + ".travis.yml", + "/node_modules/aa/bb/cc.js", + "/node_modules/aa/bb/dd.js", + "CHANGELOG.md", + "CONTRIBUTING.md", + "Dockerfile", + "LICENSE", + "Makefile", + "README.md", + "appveyor.yml", + "benchmark/insert-transaction.sql", + "benchmark/insert.js", + "binding.gyp", + "cloudformation/ci.template.js", + "deps/common-sqlite.gypi", + "deps/extract.py", + "deps/sqlite-autoconf-3340000.tar.gz", + "deps/sqlite3.gyp", + "examples/simple-chaining.js", + "lib/index.js", + "lib/sqlite3-binding.js", + "lib/sqlite3.js", + "lib/trace.js", + "node_modules/aa/bb/cc.js", + "node_modules/aa/bb/dd.js", + "package.json", + "scripts/build-appveyor.bat", + "scripts/build-local.bat", + "scripts/build_against_electron.sh", + "scripts/build_against_node.sh", + "scripts/build_against_node_webkit.sh", + "scripts/build_for_node_webkit.cmd", + "scripts/install_node.sh", + "scripts/validate_tag.sh", + "sqlite3.js", + "src/async.h", + "src/backup.cc", + "src/backup.h", + "src/database.cc", + "src/database.h", + "src/gcc-preinclude.h", + "src/macros.h", + "src/node_sqlite3.cc", + "src/statement.cc", + "src/statement.h", + "src/threading.h", + "test/affected.test.js", + "test/backup.test.js", + "test/blob.test.js", + "test/cache.test.js", + "test/constants.test.js", + "test/database_fail.test.js", + "test/each.test.js", + "test/exec.test.js", + "test/extension.test.js", + "test/fts-content.test.js", + "test/interrupt.test.js", + "test/issue-108.test.js", + "test/json.test.js", + "test/map.test.js", + "test/named_columns.test.js", + "test/named_params.test.js", + "test/null_error.test.js", + "test/nw/.gitignore", + "test/nw/Makefile", + "test/nw/index.html", + "test/nw/package.json", + "test/open_close.test.js", + "test/other_objects.test.js", + "test/parallel_insert.test.js", + "test/prepare.test.js", + "test/profile.test.js", + "test/rerun.test.js", + "test/scheduling.test.js", + "test/serialization.test.js", + "test/support/createdb-electron.js", + "test/support/createdb.js", + "test/support/elmo.png", + "test/support/helper.js", + "test/support/prepare.db", + "test/support/script.sql", + "test/trace.test.js", + "test/unicode.test.js", + "test/upsert.test.js", + "test/verbose.test.js", + "tools/docker/architecture/linux-arm/Dockerfile", + "tools/docker/architecture/linux-arm/run.sh", + "tools/docker/architecture/linux-arm64/Dockerfile", + "tools/docker/architecture/linux-arm64/run.sh" + ]; + [ + "tes?/", + "tes[-t-]/", + "tes[-t]/", + "tes[0-9A-Z_a-z-]/", + "tes[t-]/", + "test/**/*.js" + ].forEach(function (aa) { + [ + "li*/*.js", + "li?/*.js", + "lib/", + "lib/*", + "lib/**/*.js", + "lib/*.js" + ].forEach(function (bb) { + [ + "", + "**/node_modules/", + "node_modules/" + ].forEach(function (cc) { + assertJsonEqual( + globExclude({ + excludeList: [ + "tes[!0-9A-Z_a-z-]/", + "tes[^0-9A-Z_a-z-]/", + "test/suppor*/*elper.js", + "test/suppor?/?elper.js", + "test/support/helper.js" + ].concat(aa, cc), + includeList: [ + "**/*.cjs", + "**/*.js", + "**/*.mjs", + "lib/sqlite3.js" + ].concat(bb), + pathnameList + }).pathnameList, + [ + ".eslintrc.js", + "benchmark/insert.js", + "cloudformation/ci.template.js", + "examples/simple-chaining.js", + "lib/index.js", + "lib/sqlite3-binding.js", + "lib/sqlite3.js", + "lib/trace.js", + "sqlite3.js" + ].concat( + cc === "**/node_modules/" + ? [ + "node_modules/aa/bb/cc.js", + "node_modules/aa/bb/dd.js" + ] + : cc === "node_modules/" + ? [ + "/node_modules/aa/bb/cc.js", + "/node_modules/aa/bb/dd.js" + ] + : [ + "/node_modules/aa/bb/cc.js", + "/node_modules/aa/bb/dd.js", + "node_modules/aa/bb/cc.js", + "node_modules/aa/bb/dd.js" + ] + ).sort() + ); + }); + }); + }); + }); + jstestIt(( + "test globToRegexp handling-behavior" + ), function () { + Object.entries({ + "*": ( + /^[^\/]*?$/gm + ), + "**": ( + /^.*?$/gm + ), + "***": ( + /^.*?$/gm + ), + "****": ( + /^.*?$/gm + ), + "****////****": ( + /^.*?$/gm + ), + "***///***": ( + /^.*?$/gm + ), + "**/*": ( + /^.*?$/gm + ), + "**/node_modules/": ( + /^.*?\/node_modules\/.*?$/gm + ), + "**/node_modules/**/*": ( + /^.*?\/node_modules\/.*?$/gm + ), + "?": ( + /^[^\/]$/gm + ), + "[!0-9A-Za-z-]": ( + /^[^0-9A-Za-z\-]$/gm + ), + "[0-9A-Za-z-]": ( + /^[0-9A-Za-z\-]$/gm + ), + "[[]] ]][[": ( + /^[\[]\] \]\][\[]$/gm + ), + "[]": ( + /^$/gm + ), + "[^0-9A-Za-z-]": ( + /^[^0-9A-Za-z\-]$/gm + ), + "aa/bb/cc": ( + /^aa\/bb\/cc$/gm + ), + "aa/bb/cc/": ( + /^aa\/bb\/cc\/.*?$/gm + ), + "li*/*": ( + /^li[^\/]*?\/[^\/]*?$/gm + ), + "li?/*": ( + /^li[^\/]\/[^\/]*?$/gm + ), + "lib/": ( + /^lib\/.*?$/gm + ), + "lib/*": ( + /^lib\/[^\/]*?$/gm + ), + "lib/**/*.js": ( + /^lib\/.*?\.js$/gm + ), + "lib/*.js": ( + /^lib\/[^\/]*?\.js$/gm + ), + "node_modules/": ( + /^node_modules\/.*?$/gm + ), + "node_modules/**/*": ( + /^node_modules\/.*?$/gm + ), + "tes[!0-9A-Z_a-z-]/**/*": ( + /^tes[^0-9A-Z_a-z\-]\/.*?$/gm + ), + "tes[0-9A-Z_a-z-]/**/*": ( + /^tes[0-9A-Z_a-z\-]\/.*?$/gm + ), + "tes[^0-9A-Z_a-z-]/**/*": ( + /^tes[^0-9A-Z_a-z\-]\/.*?$/gm + ), + "test/**/*": ( + /^test\/.*?$/gm + ), + "test/**/*.js": ( + /^test\/.*?\.js$/gm + ), + "test/suppor*/*elper.js": ( + /^test\/suppor[^\/]*?\/[^\/]*?elper\.js$/gm + ), + "test/suppor?/?elper.js": ( + /^test\/suppor[^\/]\/[^\/]elper\.js$/gm + ), + "test/support/helper.js": ( + /^test\/support\/helper\.js$/gm + ) + }).forEach(function ([ + pattern, rgx + ]) { + assertJsonEqual( + globExclude({ + excludeList: [ + pattern + ] + }).excludeList[0].source, + rgx.source + ); + assertJsonEqual( + globExclude({ + includeList: [ + pattern + ] + }).includeList[0].source, + rgx.source + ); + }); + }); +}); + +jstestDescribe(( + "test jslint's cli handling-behavior" +), function testBehaviorJslintCli() { + function processExit0(exitCode) { + assertOrThrow(exitCode === 0, exitCode); + } + function processExit1(exitCode) { + assertOrThrow(exitCode === 1, exitCode); + } + jstestIt(( + "test cli-null-case handling-behavior" + ), function () { + jslint.jslint_cli({ + mode_noop: true, + process_exit: processExit0 + }); + }); + jstestIt(( + "test cli-window-jslint handling-behavior" + ), function () { + [ + "&window_jslint=", + "&window_jslint=12", + "&window_jslint=1?", + "&window_jslint=?", + "?window_jslint=", + "?window_jslint=12", + "?window_jslint=1?", + "?window_jslint=?", + "window_jslint=1", + "window_jslint=1&", + "window_jslint=12", + "window_jslint=1?" + ].forEach(function (import_meta_url) { + jslint.jslint_cli({ + import_meta_url + }); + assertOrThrow(globalThis.jslint === undefined); + }); + [ + "&window_jslint=1", + "&window_jslint=1&", + "?window_jslint=1", + "?window_jslint=1&" + ].forEach(function (import_meta_url) { + jslint.jslint_cli({ + import_meta_url + }); + assertOrThrow(globalThis.jslint === jslint); + delete globalThis.jslint; + }); + }); + jstestIt(( + "test cli-cjs-and-invalid-file handling-behavior" + ), async function () { + await fsWriteFileWithParents(".test_dir.cjs/touch.txt", ""); + [ + ".", // test dir handling-behavior + "jslint.mjs", // test file handling-behavior + undefined // test file-undefined handling-behavior + ].forEach(function (file) { + jslint.jslint_cli({ + file, + mode_cli: true, + process_env: { + JSLINT_BETA: "1" + }, + process_exit: processExit0 + }); + }); + }); + jstestIt(( + "test cli-apidoc handling-behavior" + ), function () { + jslint.jslint_cli({ + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_apidoc=.artifact/apidoc.html", + JSON.stringify({ + example_list: [ + "README.md", + "test.mjs", + "jslint.mjs" + ], + github_repo: "https://github.com/jslint-org/jslint", + module_list: [ + { + pathname: "./jslint.mjs" + } + ], + package_name: "JSLint", + version: jslint.jslint_edition + }) + ], + process_exit: processExit0 + }); + }); + jstestIt(( + "test cli-file-error handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + file: "undefined", + mode_cli: true, + process_exit: processExit1 + }); + }); + jstestIt(( + "test cli-syntax-error handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + file: "syntax-error.js", + mode_cli: true, + option: { + trace: true + }, + process_exit: processExit1, + source: "syntax error" + }); + }); + jstestIt(( + "test cli-report handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_report=.tmp/jslint_report.html", + "jslint.mjs" + ], + process_exit: processExit0 + }); + }); + jstestIt(( + "test cli-report-error handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_report=.tmp/jslint_report.html", + "syntax-error.js" + ], + process_exit: processExit1, + source: "syntax error" + }); + }); + jstestIt(( + "test cli-report-json handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_report=.tmp/jslint_report.html", + "aa.json" + ], + process_exit: processExit0, + source: "[]" + }); + }); + jstestIt(( + "test cli-report-json-error handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_report=.tmp/jslint_report.html", + "aa.json" + ], + process_exit: processExit1, + source: "[" + }); + }); + jstestIt(( + "test cli-report-misc handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_report=.tmp/jslint_report.html", + "aa.js" + ], + process_exit: processExit0, + source: "let aa = 0;" + }); + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_report=.tmp/jslint_report.html", + "aa.js" + ], + process_exit: processExit1, + source: "(aa)=>aa; function aa([aa]){}" + }); + }); + jstestIt(( + "test cli-jslint-wrapper-vim handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_wrapper_vim", + "syntax-error.js" + ], + process_exit: processExit1, + source: "syntax error" + }); + }); +}); + +jstestDescribe(( + "test jslint's no-warnings handling-behavior" +), function testBehaviorJslintNoWarnings() { + jstestIt(( + "test jslint's no-warnings handling-behavior" + ), function () { + Object.values({ + array: [ + "new Array(0);" + ], + async_await: [ + "async function aa() {\n await aa();\n}", + +// PR-405 - Bugfix - fix expression after "await" mis-identified as statement. + + "async function aa() {\n await aa;\n}", + ( + "async function aa() {\n" + + " try {\n" + + " aa();\n" + + " } catch (err) {\n" + + " await err();\n" + + " }\n" + + "}\n" + ), + ( + "async function aa() {\n" + + " try {\n" + + " await aa();\n" + + " } catch (err) {\n" + + " await err();\n" + + " }\n" + + "}\n" + ), + +// PR-370 - Add top-level-await support. + + "await String();\n" + ], + +// PR-351 - Add BigInt support. + + bigint: [ + "let aa = 0b0n;\n", + "let aa = 0o0n;\n", + "let aa = 0x0n;\n", + "let aa = BigInt(0n);\n", + "let aa = typeof aa === \"bigint\";\n" + ], + date: [ + "Date.getTime();", + "let aa = aa().getTime();", + "let aa = aa.aa().getTime();" + ], + directive: [ + "#!\n/*jslint browser:false, node*/\n\"use strict\";", + "/*property aa bb*/" + ], + for: [ + ( + "/*jslint for*/\n" + + "function aa(bb) {\n" + + " for (bb = 0; bb < 0; bb += 1) {\n" + + " bb();\n" + + " }\n" + + "}\n" + ) + ], + jslint_disable: [ + "/*jslint-disable*/\n0\n/*jslint-enable*/" + ], + jslint_ignore_line: [ + "0 //jslint-ignore-line" + ], + json: [ + "{\"aa\":[[],-0,null]}" + ], + label: [ + ( + "function aa() {\n" + + "bb:\n" + + " while (true) {\n" + + " if (true) {\n" + + " break bb;\n" + + " }\n" + + " }\n" + + "}\n" + ) + ], + loop: [ + ( + "function aa() {\n" + + " do {\n" + + " aa();\n" + + " } while (aa());\n" + + "}\n" + ), + +// PR-378 - Relax warning "function_in_loop". + + ( + "function aa() {\n" + + " while (true) {\n" + + " (function () {\n" + + " return;\n" + + " }());\n" + + " }\n" + + "}\n" + ) + ], + module: [ + "export default Object.freeze();", + +// PR-439 - Add grammar for "export async function ...". + + ( + "export default Object.freeze(async function () {\n" + + " return await 0;\n" + + "});\n" + ), + "import {aa, bb} from \"aa\";\naa(bb);", + "import {} from \"aa\";", + "import(\"aa\").then(function () {\n return;\n});", + ( + "let aa = 0;\n" + + "import(aa).then(aa).then(aa)" + + ".catch(aa).finally(aa);\n" + ) + ], + number: [ + "let aa = 0.0e0;", + "let aa = 0b0;", + "let aa = 0o0;", + "let aa = 0x0;" + ], + +// PR-390 - Add numeric-separator support. + + numeric_separator: [ + "let aa = 0.0_0_0;", + "let aa = 0b0_1111_1111n;\n", + "let aa = 0o0_1234_1234n;\n", + "let aa = 0x0_1234_1234n;\n", + "let aa = 1_234_234.1_234_234E1_234_234;" + ], + optional_chaining: [ + "let aa = aa?.bb?.cc;" + ], + param: [ + "function aa({aa, bb}) {\n return {aa, bb};\n}\n", + ( + "function aa({constructor}) {\n" + + " return {constructor};\n" + + "}\n" + ) + ], + property: [ + "let aa = aa[`!`];" + ], + regexp: [ + "function aa() {\n return /./;\n}", + "let aa = /(?!.)(?:.)(?=.)/;", + "let aa = /./gimuy;", + "let aa = /[\\--\\-]/;" + ], + ternary: [ + ( + "let aa = (\n" + + " aa()\n" + + " ? 0\n" + + " : 1\n" + + ") " + + "&& (\n" + + " aa()\n" + + " ? 0\n" + + " : 1\n" + + ");" + ), + ( + "let aa = (\n" + + " aa()\n" + + " ? `${0}`\n" + + " : `${1}`\n" + + ");" + ), + +// PR-394 - Bugfix +// Fix jslint falsely believing megastring literals `0` and `1` are similar. + + ( + "let aa = (\n" + + " aa()\n" + + " ? `0`\n" + + " : `1`\n" + + ");" + ) + ], + try_catch: [ + ( + "let aa = 0;\n" + + "try {\n" + + " aa();\n" + + "} catch (err) {\n" + + " aa = err;\n" + + "}\n" + + "try {\n" + + " aa();\n" + + "} catch (err) {\n" + + " aa = err;\n" + + "}\n" + + "aa();\n" + ) + ], + try_finally: [ + ( + "let aa = 0;\n" + + "try {\n" + + " aa();\n" + + "} finally {\n" + + " aa();\n" + + "}\n" + ) + ], + use_strict: [ + ( + "\"use strict\";\n" + + "let aa = 0;\n" + + "function bb() {\n" + + " \"use strict\";\n" + + " return aa;\n" + + "}\n" + ) + ], + var: [ + +// PR-363 - Bugfix +// Add test against false-warning in code +// '/*jslint node*/\nlet {aa:bb} = {}; bb();'. + + "/*jslint node*/\n", + "" + ].map(function (directive) { + return [ + "let [\n aa, bb = 0\n] = 0;\naa();\nbb();", + "let aa = 0;\nlet [...bb] = [...aa];\nbb();", + +// PR-459 - Allow destructuring-assignment after function-definition. + + ( + "let aa;\n" + + "let bb;\n" + + "function cc() {\n" + + " return;\n" + + "}\n" + + "[aa, bb] = cc();\n" + ), + "let constructor = 0;\nconstructor();", + "let {\n aa: bb\n} = 0;\nbb();", + "let {\n aa: bb,\n bb: cc\n} = 0;\nbb();\ncc();", + "let {aa, bb} = 0;\naa();\nbb();", + "let {constructor} = 0;\nconstructor();" + ].map(function (code) { + return directive + code; + }); + }).flat() + }).forEach(function (codeList) { + let elemPrv = ""; + codeList.forEach(function (code) { + let warnings; + // Assert codeList is sorted. + assertOrThrow(elemPrv < code, JSON.stringify([ + elemPrv, code + ], undefined, 4)); + elemPrv = code; + [ + jslint.jslint, + jslintCjs.jslint + ].forEach(function (jslint) { + warnings = jslint(code, { + beta: true + }).warnings; + assertOrThrow( + warnings.length === 0, + JSON.stringify([code, warnings]) + ); + }); + }); + }); + }); +}); + +jstestDescribe(( + "test jslint's option handling-behavior" +), function testBehaviorJslintOption() { + let elemPrv = ""; + [ + [ + "let aa = aa | 0;", {bitwise: true}, [] + ], [ + ";\naa(new XMLHttpRequest());", {browser: true}, ["aa"] + ], [ + "let aa = \"aa\" + 0;", {convert: true}, [] + ], [ + "registerType();", {couch: true}, [] + ], [ + "debugger;", {devel: true}, [] + ], [ + +// PR-404 - Alias "evil" to jslint-directive "eval" for backwards-compat. + + "new Function();\neval();", {eval: true, evil: true}, [] + ], [ + "let aa = () => 0;", {fart: true}, [] + ], [ + ( + "let aa = async (bb, {cc, dd}, [ee, ff], ...gg) => {\n" + + " bb += 1;\n" + + " return await (bb + cc + dd + ee + ff + gg);\n" + + "};\n" + ), {fart: true}, [] + ], [ + ( + "function aa(aa) {\n" + + " for (aa = 0; aa < 0; aa += 1) {\n" + + " aa();\n" + + " }\n" + + "}\n" + ), {for: true}, [] + ], [ + "let aa = {get aa() {\n return;\n}};", {getset: true}, [] + ], [ + "let aa = {set aa(aa) {\n return aa;\n}};", {getset: true}, [] + ], [ + sourceJslintMjs.replace(( + / /g + ), " "), {indent2: true}, [] + ], [ + "function aa() {\n return;\n}", {indent2: true}, [] + ], [ + "/".repeat(100), {long: true}, [] + ], [ + +// PR-404 - Alias "nomen" to jslint-directive "name" for backwards-compat. + + "let aa = aa._;", {name: true, nomen: true}, [] + ], [ + "require();", {node: true}, [] + ], [ + "let aa = 'aa';", {single: true}, [] + ], [ + +// PR-404 - Add new directive "subscript" to play nice with Google Closure. + + "aa[\"aa\"] = 1;", {subscript: true}, ["aa"] + ], [ + "", {test_internal_error: true}, [] + ], [ + "let aa = this;", {this: true}, [] + ], [ + "", {trace: true}, [] + ], [ + ( + "function aa({bb, aa}) {\n" + + " switch (aa) {\n" + + " case 1:\n" + + " break;\n" + + " case 0:\n" + + " break;\n" + + " default:\n" + + " return {bb, aa};\n" + + " }\n" + + "}\n" + ), {unordered: true}, [] + ], [ + "let {bb, aa} = 0;", {unordered: true}, [] + ], [ + ( + "function aa() {\n" + + " if (aa) {\n" + + " let bb = 0;\n" + + " return bb;\n" + + " }\n" + + "}\n" + ), {variable: true}, [] + ], [ + "let bb = 0;\nlet aa = 0;", {variable: true}, [] + ], [ + "\t", {white: true}, [] + ] + ].forEach(function ([ + source, option_dict, global_list + ]) { + jstestIt(( + `test option=${JSON.stringify(option_dict)} handling-behavior` + ), function () { + let elemNow = JSON.stringify([ + option_dict, source, global_list + ]); + let warningsLength = ( + option_dict.test_internal_error + ? 1 + : 0 + ); + // Assert list is sorted. + assertOrThrow(elemPrv < elemNow, JSON.stringify([ + elemPrv, elemNow + ], undefined, 4)); + elemPrv = elemNow; + option_dict.beta = true; + [ + jslint.jslint, + jslintCjs.jslint + ].forEach(function (jslint) { + // test jslint's option handling-behavior + assertOrThrow( + jslint( + source, + option_dict, + global_list + ).warnings.length === warningsLength, + "jslint.jslint(" + JSON.stringify([ + source, option_dict, global_list + ]) + ")" + ); + // test jslint's directive handling-behavior + source = ( + "/*jslint " + JSON.stringify( + option_dict + ).slice(1, -1).replace(( + /"/g + ), "") + "*/\n" + + ( + global_list.length === 0 + ? "" + : "/*global " + global_list.join(",") + "*/\n" + ) + + source.replace(( + /^#!/ + ), "//") + ); + assertOrThrow( + jslint(source).warnings.length === warningsLength, + source + ); + }); + }); + }); +}); + +jstestDescribe(( + "test jslint's warnings handling-behavior" +), function testBehaviorJslintWarnings() { + jstestIt(( + "test jslint's warning handling-behavior" + ), function () { + +// this function will validate each jslint is raised with given +// malformed + + sourceJslintMjs.replace(( + /(\n\s*?\/\/\s*?test_cause:\s*?)(\S[\S\s]*?\S)(\n\n\s*?) *?\S/g + ), function (match0, header, causeList, footer) { + let tmp; + // console.error(match0); + // Validate header. + assertOrThrow(header === "\n\n// test_cause:\n", match0); + // Validate footer. + assertOrThrow(footer === "\n\n", match0); + // Validate causeList. + causeList = causeList.replace(( + /^\/\/ /gm + ), "").replace(( + /^\["\n([\S\s]*?)\n"(,.*?)$/gm + ), function (ignore, source, param) { + source = "[" + JSON.stringify(source) + param; + assertOrThrow(source.length > (80 - 3), source); + return source; + }).replace(( + / \/\/jslint-ignore-line$/gm + ), ""); + tmp = causeList.split("\n").map(function (cause) { + return ( + "[" + + JSON.parse(cause).map(function (elem) { + return JSON.stringify(elem); + }).join(", ") + + "]" + ); + }).sort().join("\n"); + assertOrThrow( + causeList === tmp, + "\n" + causeList + "\n\n" + tmp + ); + causeList.split("\n").forEach(function (cause) { + cause = JSON.parse(cause); + tmp = jslint.jslint(cause[0], { + beta: true, + test_cause: true + }).causes; + // Validate cause. + assertOrThrow( + tmp[JSON.stringify(cause.slice(1))], + ( + "\n" + JSON.stringify(cause) + "\n\n" + + Object.keys(tmp).sort().join("\n") + ) + ); + }); + return ""; + }); + }); +}); + +jstestDescribe(( + "test jstestXxx handling-behavior" +), function testBehaviorJstestXxx() { + jstestIt(( + "test jstestDescribe error handling-behavior" + ), function () { + throw new Error(); + }, "pass"); + jstestIt(( + "test jstestOnExit tests-failed handling-behavior" + ), function () { + jstestOnExit(undefined, "testsFailed"); + }); +}); + +jstestDescribe(( + "test misc handling-behavior" +), function testBehaviorMisc() { + jstestIt(( + "test misc handling-behavior" + ), async function () { + // test debugInline handling-behavior + noop(debugInline); + // test assertErrorThrownAsync error handling-behavior + await assertErrorThrownAsync(function () { + return assertErrorThrownAsync(noop); + }); + // test assertJsonEqual error handling-behavior + await assertErrorThrownAsync(function () { + assertJsonEqual(1, 2); + }); + await assertErrorThrownAsync(function () { + assertJsonEqual(1, 2, "undefined"); + }); + await assertErrorThrownAsync(function () { + assertJsonEqual(1, 2, {}); + }); + // test assertOrThrow error handling-behavior + await assertErrorThrownAsync(function () { + assertOrThrow(undefined, "undefined"); + }); + await assertErrorThrownAsync(function () { + assertOrThrow(undefined, new Error()); + }); + }); +}); + +jstestDescribe(( + "test v8CoverageListMerge handling-behavior" +), function testBehaviorV8CoverageListMerge() { + let functionsInput = JSON.stringify([ + { + functionName: "test", + isBlockCoverage: true, + ranges: [ + { + count: 2, + endOffset: 4, + startOffset: 0 + }, + { + count: 1, + endOffset: 2, + startOffset: 1 + }, + { + count: 1, + endOffset: 3, + startOffset: 2 + } + ] + } + ]); + jstestIt(( + "accepts empty arrays for `v8CoverageListMerge`" + ), function () { + assertJsonEqual(v8CoverageListMerge([]), { + result: [] + }); + }); + jstestIt(( + "funcCovs.length === 1" + ), function () { + assertJsonEqual(v8CoverageListMerge([ + { + result: [ + { + functions: [ + { + functionName: "test", + isBlockCoverage: true, + ranges: [ + { + count: 2, + endOffset: 4, + startOffset: 0 + } + ] + } + ], + moduleUrl: "/lib.js", + scriptId: "1" + } + ] + }, + { + result: [ + { + functions: [], + moduleUrl: "/lib.js", + scriptId: "2" + } + ] + } + ]), { + result: [ + { + functions: [ + { + functionName: "test", + isBlockCoverage: true, + ranges: [ + { + count: 2, + endOffset: 4, + startOffset: 0 + } + ] + } + ], + scriptId: "0" + } + ] + }); + }); + jstestIt(( + "accepts arrays with a single item for `v8CoverageListMerge`" + ), function () { + assertJsonEqual(v8CoverageListMerge([ + { + result: [ + { + functions: JSON.parse(functionsInput), + moduleUrl: "/lib.js", + scriptId: "123" + } + ] + } + ]), { + result: [ + { + functions: [ + { + functionName: "test", + isBlockCoverage: true, + ranges: [ + { + count: 2, + endOffset: 4, + startOffset: 0 + }, + { + count: 1, + endOffset: 3, + startOffset: 1 + } + ] + } + ], + moduleUrl: "/lib.js", + scriptId: "0" + } + ] + }); + }); + jstestIt(( + "accepts arrays with two identical items for" + + " `v8CoverageListMerge`" + ), function () { + assertJsonEqual(v8CoverageListMerge([ + { + result: [ + { + functions: JSON.parse(functionsInput), + scriptId: "123", + url: "/lib.js" + }, { + functions: JSON.parse(functionsInput), + scriptId: "123", + url: "/lib.js" + } + ] + } + ]), { + result: [ + { + functions: [ + { + functionName: "test", + isBlockCoverage: true, + ranges: [ + { + count: 4, + endOffset: 4, + startOffset: 0 + }, + { + count: 2, + endOffset: 3, + startOffset: 1 + } + ] + } + ], + scriptId: "0", + url: "/lib.js" + } + ] + }); + }); + [ + "test_coverage_merge_is_block_coverage_test.json", + "test_coverage_merge_issue_2_mixed_is_block_coverage_test.json", + "test_coverage_merge_node_10_internal_errors_one_of_test.json", + "test_coverage_merge_reduced_test.json", + "test_coverage_merge_simple_test.json", + "test_coverage_merge_various_test.json" + ].forEach(function (file) { + jstestIt(file, function () { + file = testCoverageMergeData[file]; + file.forEach(function ({ + expected, + inputs + }) { + assertJsonEqual(v8CoverageListMerge(inputs), expected); + }); + }); + }); + jstestIt(( + "merge multiple node-sqlite coverage files" + ), function () { + let data1 = [ + "test_v8_coverage_node_sqlite_9884_1633662346346_0.json", + "test_v8_coverage_node_sqlite_13216_1633662333140_0.json" + ].map(function (file) { + return testCoverageMergeData[file]; + }); + let data2 = testCoverageMergeData[ + "test_v8_coverage_node_sqlite_merged.json" + ]; + data1 = v8CoverageListMerge(data1); + data1 = v8CoverageListMerge([data1]); + +// Debug data1. +// await moduleFs.promises.writeFile( +// ".test_v8_coverage_node_sqlite_merged.json", +// JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n" +// ); + + assertJsonEqual(data1, data2); + }); +}); + +jstestDescribe(( + "test v8CoverageReportCreate handling-behavior" +), function testBehaviorV8CoverageReportCreate() { + jstestIt(( + "test null-case handling-behavior" + ), async function () { + await assertErrorThrownAsync(function () { + return v8CoverageReportCreate({}); + }, "invalid coverageDir"); + }); + jstestIt(( + "test coverage-report jslint.mjs handling-behavior" + ), async function () { + // test remove-old-coverage handling-behavior + await fsWriteFileWithParents( + ".tmp/coverage_jslint/coverage-0-0-0.json", + "" + ); + await jslint.jslint_cli({ + console_error: noop, // comment to debug + mode_cli: true, + process_argv: [ + "node", "jslint.mjs", + "v8_coverage_report=.tmp/coverage_jslint", + "--exclude=aa.js", + "--include-node-modules=1", + "--include=jslint.mjs", + "node", "jslint.mjs" + ] + }); + }); + [ + [ + "v8CoverageReportCreate_high.js", ( + "switch(0){\n" + + "case 0:break;\n" + + "}\n" + ) + ], [ + "v8CoverageReportCreate_ignore.js", ( + "/*coverage-ignore-file*/\n" + + "switch(0){\n" + + "case 0:break;\n" + + "}\n" + ) + ], [ + "v8CoverageReportCreate_low.js", ( + "switch(0){\n" + + "case 1:break;\n" + + "case 2:break;\n" + + "case 3:break;\n" + + "case 4:break;\n" + + "}\n" + ) + ], [ + "v8CoverageReportCreate_medium.js", ( + "switch(0){\n" + + "case 0:break;\n" + + "case 1:break;\n" + + "case 2:break;\n" + + "}\n" + ) + ] + ].forEach(function ([ + file, data + ], ii) { + jstestIt(file, async function () { + let dir = ".tmp/coverage_" + ii + "/"; + file = dir + file; + await fsWriteFileWithParents(file, data); + await jslint.jslint_cli({ + console_error: noop, // comment to debug + mode_cli: true, + process_argv: [ + "node", "jslint.mjs", + "v8_coverage_report=" + dir, + "node", + file + ] + }); + }); + }); + jstestIt(( + "test npm handling-behavior" + ), async function () { + await jslint.jslint_cli({ + console_error: noop, // comment to debug + mode_cli: true, + process_argv: [ + "node", "jslint.mjs", + "v8_coverage_report=.tmp/coverage_npm", + "npm", "--version" + ] + }); + }); + jstestIt(( + "test misc handling-behavior" + ), async function () { + await Promise.all([ + [ + ".tmp/coverage_misc/aa.js", "\n".repeat(0x100) + ], [ + ".tmp/coverage_misc/coverage-0-0-0.json", JSON.stringify({ + "result": [ + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 0xf0, + "startOffset": 0x10 + }, + { + "count": 1, + "endOffset": 0x40, + "startOffset": 0x20 + }, + { + "count": 1, + "endOffset": 0x80, + "startOffset": 0x60 + }, + { + "count": 0, + "endOffset": 0x45, + "startOffset": 0x25 + }, + { + "count": 0, + "endOffset": 0x85, + "startOffset": 0x65 + } + ] + } + ], + "scriptId": "0", + "url": "file:///" + modulePath.resolve( + ".tmp/coverage_misc/aa.js" + ) + } + ] + }, undefined, 4) + ] + ].map(async function ([ + file, data + ]) { + await fsWriteFileWithParents(file, data); + })); + await jslint.jslint_cli({ + console_error: noop, // comment to debug + mode_cli: true, + process_argv: [ + "node", "jslint.mjs", + "v8_coverage_report=.tmp/coverage_misc" + // "node", ".tmp/coverage_misc/aa.js" + ] + }); + }); +}); diff --git a/branch-master/test_coverage_merge_data.json b/branch-master/test_coverage_merge_data.json new file mode 100644 index 000000000..bc0bb0e48 --- /dev/null +++ b/branch-master/test_coverage_merge_data.json @@ -0,0 +1,17877 @@ +{ + "test_coverage_merge_is_block_coverage_test.json": [ + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Identity preserves 'isBlockCoverage: true'", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Identity preserves 'isBlockCoverage: false'", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a flat block-coverage (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a flat block-coverage (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a flat block-coverage (permutation 2)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a flat block-coverage (permutation 3)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a flat block-coverage (permutation 4)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a flat block-coverage (permutation 5)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a block-coverage with a child (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a block-coverage with a child (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a block-coverage with a child (permutation 2)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a block-coverage with a child (permutation 3)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a block-coverage with a child (permutation 4)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a block-coverage with a child (permutation 5)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage and two flat block-coverages (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage and two flat block-coverages (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage and two flat block-coverages (permutation 2)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage and two flat block-coverages (permutation 3)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage and two flat block-coverages (permutation 4)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage and two flat block-coverages (permutation 5)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 24, + "endOffset": 6, + "startOffset": 2 + }, + { + "count": 6, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 42, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverages and two block-coverages with children (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 24, + "endOffset": 6, + "startOffset": 2 + }, + { + "count": 6, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 42, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverages and two block-coverages with children (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 24, + "endOffset": 6, + "startOffset": 2 + }, + { + "count": 6, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 42, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverages and two block-coverages with children (permutation 2)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 24, + "endOffset": 6, + "startOffset": 2 + }, + { + "count": 6, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 42, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverages and two block-coverages with children (permutation 3)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 24, + "endOffset": 6, + "startOffset": 2 + }, + { + "count": 6, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 42, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverages and two block-coverages with children (permutation 4)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 24, + "endOffset": 6, + "startOffset": 2 + }, + { + "count": 6, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 42, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverages and two block-coverages with children (permutation 5)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 50, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "count": 40, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage with a flat block-coverage with count (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 50, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "count": 40, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage with a flat block-coverage with count (permutation 1)", + "status": "run" + } + ], + "test_coverage_merge_issue_2_mixed_is_block_coverage_test.json": [ + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 23, + "functionName": "realpathSync", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 42951, + "startOffset": 39013 + }, + { + "count": 15, + "endOffset": 39088, + "startOffset": 39069 + }, + { + "count": 5, + "endOffset": 39140, + "startOffset": 39088 + }, + { + "count": 0, + "endOffset": 39214, + "startOffset": 39196 + }, + { + "count": 5, + "endOffset": 39356, + "startOffset": 39341 + }, + { + "count": 1, + "endOffset": 39418, + "startOffset": 39383 + }, + { + "count": 19, + "endOffset": 39991, + "startOffset": 39418 + }, + { + "count": 0, + "endOffset": 40010, + "startOffset": 39991 + }, + { + "count": 0, + "endOffset": 40187, + "startOffset": 40012 + }, + { + "count": 19, + "endOffset": 40324, + "startOffset": 40187 + }, + { + "count": 427, + "endOffset": 42868, + "startOffset": 40324 + }, + { + "count": 34, + "endOffset": 40551, + "startOffset": 40436 + }, + { + "count": 393, + "endOffset": 40677, + "startOffset": 40551 + }, + { + "count": 187, + "endOffset": 40798, + "startOffset": 40760 + }, + { + "count": 28, + "endOffset": 40797, + "startOffset": 40770 + }, + { + "count": 257, + "endOffset": 40937, + "startOffset": 40800 + }, + { + "count": 0, + "endOffset": 40915, + "startOffset": 40891 + }, + { + "count": 170, + "endOffset": 40999, + "startOffset": 40937 + }, + { + "count": 11, + "endOffset": 41017, + "startOffset": 40999 + }, + { + "count": 0, + "endOffset": 41097, + "startOffset": 41048 + }, + { + "count": 170, + "endOffset": 42382, + "startOffset": 41097 + }, + { + "count": 135, + "endOffset": 41551, + "startOffset": 41450 + }, + { + "count": 11, + "endOffset": 41525, + "startOffset": 41503 + }, + { + "count": 35, + "endOffset": 41964, + "startOffset": 41551 + }, + { + "count": 13, + "endOffset": 41924, + "startOffset": 41875 + }, + { + "count": 22, + "endOffset": 42214, + "startOffset": 41964 + }, + { + "count": 34, + "endOffset": 42296, + "startOffset": 42214 + }, + { + "count": 0, + "endOffset": 42326, + "startOffset": 42296 + }, + { + "count": 34, + "endOffset": 42376, + "startOffset": 42326 + }, + { + "count": 34, + "endOffset": 42658, + "startOffset": 42382 + }, + { + "count": 0, + "endOffset": 42677, + "startOffset": 42658 + }, + { + "count": 0, + "endOffset": 42864, + "startOffset": 42679 + }, + { + "count": 18, + "endOffset": 42883, + "startOffset": 42868 + }, + { + "count": 4, + "endOffset": 42906, + "startOffset": 42883 + }, + { + "count": 18, + "endOffset": 42950, + "startOffset": 42906 + } + ] + } + ], + "scriptId": "0", + "url": "fs.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "realpathSync", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 42951, + "startOffset": 39013 + }, + { + "count": 15, + "endOffset": 39088, + "startOffset": 39069 + }, + { + "count": 5, + "endOffset": 39140, + "startOffset": 39088 + }, + { + "count": 0, + "endOffset": 39214, + "startOffset": 39196 + }, + { + "count": 5, + "endOffset": 39356, + "startOffset": 39341 + }, + { + "count": 1, + "endOffset": 39418, + "startOffset": 39383 + }, + { + "count": 19, + "endOffset": 39991, + "startOffset": 39418 + }, + { + "count": 0, + "endOffset": 40010, + "startOffset": 39991 + }, + { + "count": 0, + "endOffset": 40187, + "startOffset": 40012 + }, + { + "count": 19, + "endOffset": 40324, + "startOffset": 40187 + }, + { + "count": 427, + "endOffset": 42868, + "startOffset": 40324 + }, + { + "count": 34, + "endOffset": 40551, + "startOffset": 40436 + }, + { + "count": 393, + "endOffset": 40677, + "startOffset": 40551 + }, + { + "count": 187, + "endOffset": 40798, + "startOffset": 40760 + }, + { + "count": 28, + "endOffset": 40797, + "startOffset": 40770 + }, + { + "count": 257, + "endOffset": 40937, + "startOffset": 40800 + }, + { + "count": 0, + "endOffset": 40915, + "startOffset": 40891 + }, + { + "count": 170, + "endOffset": 40999, + "startOffset": 40937 + }, + { + "count": 11, + "endOffset": 41017, + "startOffset": 40999 + }, + { + "count": 0, + "endOffset": 41097, + "startOffset": 41048 + }, + { + "count": 170, + "endOffset": 42382, + "startOffset": 41097 + }, + { + "count": 135, + "endOffset": 41551, + "startOffset": 41450 + }, + { + "count": 11, + "endOffset": 41525, + "startOffset": 41503 + }, + { + "count": 35, + "endOffset": 41932, + "startOffset": 41551 + }, + { + "count": 13, + "endOffset": 41924, + "startOffset": 41875 + }, + { + "count": 35, + "endOffset": 41964, + "startOffset": 41932 + }, + { + "count": 22, + "endOffset": 42214, + "startOffset": 41964 + }, + { + "count": 34, + "endOffset": 42296, + "startOffset": 42214 + }, + { + "count": 0, + "endOffset": 42326, + "startOffset": 42296 + }, + { + "count": 34, + "endOffset": 42376, + "startOffset": 42326 + }, + { + "count": 34, + "endOffset": 42658, + "startOffset": 42382 + }, + { + "count": 0, + "endOffset": 42677, + "startOffset": 42658 + }, + { + "count": 0, + "endOffset": 42864, + "startOffset": 42679 + }, + { + "count": 18, + "endOffset": 42883, + "startOffset": 42868 + }, + { + "count": 4, + "endOffset": 42906, + "startOffset": 42883 + }, + { + "count": 18, + "endOffset": 42950, + "startOffset": 42906 + } + ] + } + ], + "scriptId": "48", + "url": "fs.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "realpathSync", + "isBlockCoverage": false, + "ranges": [ + { + "count": 3, + "endOffset": 42951, + "startOffset": 39013 + } + ] + } + ], + "scriptId": "48", + "url": "fs.js" + } + ] + } + ], + "name": "Issue 2: Mixed isBlockCoverage (https://github.com/demurgos/v8-coverage/issues/2)", + "status": "run" + } + ], + "test_coverage_merge_node_10_internal_errors_one_of_test.json": [ + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2516, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 719, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 718, + "endOffset": 148299, + "startOffset": 147892 + }, + { + "count": 537, + "endOffset": 148155, + "startOffset": 147892 + }, + { + "count": 530, + "endOffset": 148061, + "startOffset": 147892 + }, + { + "count": 2, + "endOffset": 148061, + "startOffset": 148055 + }, + { + "count": 2, + "endOffset": 148155, + "startOffset": 148061 + }, + { + "count": 188, + "endOffset": 148299, + "startOffset": 148155 + }, + { + "count": 2, + "endOffset": 148299, + "startOffset": 148291 + }, + { + "count": 38, + "endOffset": 148437, + "startOffset": 148299 + }, + { + "count": 5, + "endOffset": 148433, + "startOffset": 148299 + }, + { + "count": 4, + "endOffset": 148427, + "startOffset": 148299 + }, + { + "count": 2, + "endOffset": 148433, + "startOffset": 148427 + }, + { + "count": 2, + "endOffset": 148437, + "startOffset": 148433 + }, + { + "count": 2019, + "endOffset": 148555, + "startOffset": 148437 + }, + { + "count": 1799, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 2, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 148556, + "startOffset": 147273 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1453, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 4, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 1, + "endOffset": 148061, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148061, + "startOffset": 148055 + }, + { + "count": 2, + "endOffset": 148433, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148155, + "startOffset": 148061 + }, + { + "count": 1, + "endOffset": 148299, + "startOffset": 148155 + }, + { + "count": 0, + "endOffset": 148299, + "startOffset": 148291 + }, + { + "count": 1, + "endOffset": 148427, + "startOffset": 148299 + }, + { + "count": 0, + "endOffset": 148433, + "startOffset": 148427 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 148433 + }, + { + "count": 1, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 28, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 2, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 1, + "endOffset": 148061, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148061, + "startOffset": 148055 + }, + { + "count": 1, + "endOffset": 148433, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148155, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148433, + "startOffset": 148291 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 148433 + }, + { + "count": 26, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 1, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 0, + "endOffset": 148299, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 148427 + }, + { + "count": 1, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 173, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 33, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 0, + "endOffset": 148155, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 148291 + }, + { + "count": 140, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 313, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 154, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 148055 + }, + { + "count": 159, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 43, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 26, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 24, + "endOffset": 148061, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148061, + "startOffset": 148055 + }, + { + "count": 2, + "endOffset": 148433, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148155, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148433, + "startOffset": 148291 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 148433 + }, + { + "count": 17, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 148, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 0, + "endOffset": 148155, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148291 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 2, + "endOffset": 148061, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148061, + "startOffset": 148055 + }, + { + "count": 1, + "endOffset": 148433, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148155, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148433, + "startOffset": 148291 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148433 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 346, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148055 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "node@10.11.0: internal/errors.js#oneOf", + "status": "run" + } + ], + "test_coverage_merge_reduced_test.json": [ + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1962, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 332, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 331, + "endOffset": 4, + "startOffset": 2 + }, + { + "count": 329, + "endOffset": 4, + "startOffset": 3 + }, + { + "count": 30, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 4 + }, + { + "count": 29, + "endOffset": 6, + "startOffset": 5 + }, + { + "count": 1814, + "endOffset": 7, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1811, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 181, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 180, + "endOffset": 4, + "startOffset": 2 + }, + { + "count": 27, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 4 + }, + { + "count": 26, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 148, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 0, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "node@10.11.0: internal/errors.js#oneOf: reduced", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1962, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 332, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 331, + "endOffset": 4, + "startOffset": 2 + }, + { + "count": 329, + "endOffset": 4, + "startOffset": 3 + }, + { + "count": 30, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 4 + }, + { + "count": 29, + "endOffset": 6, + "startOffset": 5 + }, + { + "count": 1814, + "endOffset": 7, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1811, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 181, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 180, + "endOffset": 4, + "startOffset": 2 + }, + { + "count": 27, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 4 + }, + { + "count": 26, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 148, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 0, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "node@10.11.0: internal/errors.js#oneOf: reduced with hint", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 66, + "endOffset": 4, + "startOffset": 2 + }, + { + "count": 30, + "endOffset": 4, + "startOffset": 3 + }, + { + "count": 43, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 4 + }, + { + "count": 52, + "endOffset": 7, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 6, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "node@10.11.0: internal/errors.js#oneOf: minimal", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 66, + "endOffset": 4, + "startOffset": 2 + }, + { + "count": 30, + "endOffset": 4, + "startOffset": 3 + }, + { + "count": 43, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 4 + }, + { + "count": 52, + "endOffset": 7, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 6, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 40, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "node@10.11.0: internal/errors.js#oneOf: minimal with hint", + "status": "run" + } + ], + "test_coverage_merge_simple_test.json": [ + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "No children", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "One child", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Equal", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 3, + "startOffset": 1 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 3, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (BeforeAdjacent)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 2, + "startOffset": 1 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 2, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (BeforeStrict)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 12, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (AfterAdjacent)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 12, + "endOffset": 8, + "startOffset": 7 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 7 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (AfterStrict)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Nested (OverlapStrict)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 6, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Nested (OverlapEqualEnd)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 8, + "startOffset": 3 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Nested (OverlapEqualStart)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Nested (ContainedEqualStart)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 6, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Nested (ContainedEqualEnd)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Nested (ContainedStrict)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Partial overlap (PartialOverlapStart)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 12, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Partial overlap (PartialOverlapEnd)", + "status": "run" + } + ], + "test_coverage_merge_various_test.json": [ + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three empty trees", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 3, + "endOffset": 3, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 3, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 3, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Equal children, same startOffset as parents", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 3, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Equal children, same endOffset as parents", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 3, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 3, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (strict) children, offsets matching parents (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 3, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 3, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (strict) children, offsets matching parents (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 4, + "startOffset": 1 + }, + { + "count": 12, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (adjacent) children (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 4, + "startOffset": 1 + }, + { + "count": 12, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (adjacent) children (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 2 left & 1 right (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 2 left & 1 right (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 2 left & 1 right (permutation 2)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 2 left & 1 right (permutation 3)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 2 left & 1 right (permutation 4)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 2 left & 1 right (permutation 5)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 7, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 16, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 1 left & 2 right (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 7, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 16, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 1 left & 2 right (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 7, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 16, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 1 left & 2 right (permutation 2)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 7, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 16, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 1 left & 2 right (permutation 3)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 7, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 16, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 1 left & 2 right (permutation 4)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 7, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 16, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 1 left & 2 right (permutation 5)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 13, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 10, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 9, + "endOffset": 8, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 6, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 7, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 8, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Normalization: two trees with complementary children (not summing to the same count)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 14, + "endOffset": 8, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 6, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 5, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 8, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 8, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 9, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 8, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Normalization: two trees with complementary children (summing to the same count, same as parent)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 10, + "endOffset": 8, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 6, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 7, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Normalization: two trees with complementary children (summing to the same count, different from parent)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 5, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 4, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: a+b", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 5, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 4, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: b+a", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 6, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: a+c", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: b+c", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 5, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 4, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: (a+b)+c", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 6, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: (a+c)+b", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: a+(b+c)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: a+b+c", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 40, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 31, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 22, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 13, + "endOffset": 5, + "startOffset": 2 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 7, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: a+b+c+d", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 4, + "startOffset": 0 + }, + { + "count": 3, + "endOffset": 4, + "startOffset": 1 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a tagged sliding chain: a+b", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 4, + "startOffset": 0 + }, + { + "count": 3, + "endOffset": 4, + "startOffset": 1 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a tagged sliding chain: a+b", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 15, + "endOffset": 16, + "startOffset": 0 + }, + { + "count": 20, + "endOffset": 4, + "startOffset": 0 + }, + { + "count": 18, + "endOffset": 1, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 2, + "startOffset": 1 + }, + { + "count": 17, + "endOffset": 4, + "startOffset": 3 + }, + { + "count": 12, + "endOffset": 11, + "startOffset": 4 + }, + { + "count": 13, + "endOffset": 8, + "startOffset": 5 + }, + { + "count": 16, + "endOffset": 7, + "startOffset": 6 + }, + { + "count": 9, + "endOffset": 8, + "startOffset": 7 + }, + { + "count": 11, + "endOffset": 11, + "startOffset": 9 + }, + { + "count": 19, + "endOffset": 11, + "startOffset": 10 + }, + { + "count": 23, + "endOffset": 15, + "startOffset": 11 + }, + { + "count": 22, + "endOffset": 12, + "startOffset": 11 + }, + { + "count": 16, + "endOffset": 14, + "startOffset": 13 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 16, + "startOffset": 0 + }, + { + "count": 6, + "endOffset": 4, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 2, + "startOffset": 1 + }, + { + "count": 4, + "endOffset": 7, + "startOffset": 6 + }, + { + "count": 9, + "endOffset": 15, + "startOffset": 10 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 16, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 1, + "startOffset": 0 + }, + { + "count": 5, + "endOffset": 8, + "startOffset": 5 + }, + { + "count": 3, + "endOffset": 12, + "startOffset": 9 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 16, + "startOffset": 0 + }, + { + "count": 7, + "endOffset": 11, + "startOffset": 3 + }, + { + "count": 3, + "endOffset": 8, + "startOffset": 7 + }, + { + "count": 3, + "endOffset": 14, + "startOffset": 13 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges three trees with a complex relation (chains, nesting)", + "status": "run" + } + ], + "test_v8_coverage_node_sqlite_13216_1633662333140_0.json": { + "result": [ + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 20604, + "startOffset": 0 + } + ] + }, + { + "functionName": "Console", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4925, + "startOffset": 2686 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5200, + "startOffset": 5144 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5669, + "startOffset": 5458 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 6489, + "startOffset": 5878 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 6165, + "startOffset": 6067 + }, + { + "count": 1, + "endOffset": 6124, + "startOffset": 6101 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6208, + "startOffset": 6178 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6417, + "startOffset": 6315 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6460, + "startOffset": 6430 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 7776, + "startOffset": 6563 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9334, + "startOffset": 7850 + }, + { + "count": 0, + "endOffset": 8095, + "startOffset": 8081 + }, + { + "count": 0, + "endOffset": 8197, + "startOffset": 8171 + }, + { + "count": 0, + "endOffset": 8432, + "startOffset": 8238 + }, + { + "count": 0, + "endOffset": 8521, + "startOffset": 8493 + }, + { + "count": 0, + "endOffset": 9261, + "startOffset": 8971 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9918, + "startOffset": 9411 + }, + { + "count": 0, + "endOffset": 9635, + "startOffset": 9629 + }, + { + "count": 0, + "endOffset": 9840, + "startOffset": 9714 + }, + { + "count": 0, + "endOffset": 9910, + "startOffset": 9886 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 10124, + "startOffset": 9993 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10330, + "startOffset": 10199 + } + ] + }, + { + "functionName": "createWriteErrorHandler", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 11331, + "startOffset": 10424 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 11327, + "startOffset": 10493 + }, + { + "count": 0, + "endOffset": 10786, + "startOffset": 10768 + } + ] + }, + { + "functionName": "log", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 11452, + "startOffset": 11363 + } + ] + }, + { + "functionName": "warn", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11551, + "startOffset": 11461 + } + ] + }, + { + "functionName": "dir", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11751, + "startOffset": 11560 + } + ] + }, + { + "functionName": "time", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12125, + "startOffset": 11758 + } + ] + }, + { + "functionName": "timeEnd", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12429, + "startOffset": 12132 + } + ] + }, + { + "functionName": "timeLog", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12679, + "startOffset": 12436 + } + ] + }, + { + "functionName": "trace", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12886, + "startOffset": 12693 + } + ] + }, + { + "functionName": "assert", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 13116, + "startOffset": 12893 + } + ] + }, + { + "functionName": "clear", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 13620, + "startOffset": 13180 + } + ] + }, + { + "functionName": "count", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14153, + "startOffset": 13684 + } + ] + }, + { + "functionName": "countReset", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14518, + "startOffset": 14222 + } + ] + }, + { + "functionName": "group", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14699, + "startOffset": 14525 + } + ] + }, + { + "functionName": "groupEnd", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14880, + "startOffset": 14706 + } + ] + }, + { + "functionName": "table", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18454, + "startOffset": 14932 + } + ] + }, + { + "functionName": "timeLogImpl", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19012, + "startOffset": 18499 + } + ] + }, + { + "functionName": "pad", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19095, + "startOffset": 19016 + } + ] + }, + { + "functionName": "formatTime", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19892, + "startOffset": 19099 + } + ] + }, + { + "functionName": "isArray", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20089, + "startOffset": 20033 + } + ] + }, + { + "functionName": "noop", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20112, + "startOffset": 20094 + } + ] + } + ], + "scriptId": "28", + "url": "internal/console/constructor.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1638, + "startOffset": 0 + } + ] + } + ], + "scriptId": "29", + "url": "internal/constants.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 2283, + "startOffset": 0 + } + ] + }, + { + "functionName": "sendInspectorCommand", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 453, + "startOffset": 100 + } + ] + }, + { + "functionName": "installConsoleExtensions", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1094, + "startOffset": 530 + } + ] + }, + { + "functionName": "wrapConsole", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 1984, + "startOffset": 1176 + }, + { + "count": 23, + "endOffset": 1981, + "startOffset": 1336 + }, + { + "count": 19, + "endOffset": 1855, + "startOffset": 1555 + }, + { + "count": 4, + "endOffset": 1976, + "startOffset": 1855 + } + ] + }, + { + "functionName": "get consoleFromVM", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2217, + "startOffset": 2164 + } + ] + }, + { + "functionName": "set consoleFromVM", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 2277, + "startOffset": 2222 + } + ] + } + ], + "scriptId": "30", + "url": "internal/util/inspector.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 42795, + "startOffset": 0 + } + ] + }, + { + "functionName": "toUSVString", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2627, + "startOffset": 2323 + } + ] + }, + { + "functionName": "serializeTupleOrigin", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2965, + "startOffset": 2845 + } + ] + }, + { + "functionName": "URLContext", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 3611, + "startOffset": 3378 + } + ] + }, + { + "functionName": "URLSearchParams", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 6333, + "startOffset": 3909 + }, + { + "count": 0, + "endOffset": 6267, + "startOffset": 4027 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7439, + "startOffset": 6339 + } + ] + }, + { + "functionName": "onParseComplete", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 8156, + "startOffset": 7446 + }, + { + "count": 0, + "endOffset": 7716, + "startOffset": 7706 + }, + { + "count": 0, + "endOffset": 7790, + "startOffset": 7780 + }, + { + "count": 0, + "endOffset": 7877, + "startOffset": 7873 + } + ] + }, + { + "functionName": "onParseError", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8237, + "startOffset": 8160 + } + ] + }, + { + "functionName": "onParseProtocolComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8596, + "startOffset": 8241 + } + ] + }, + { + "functionName": "onParseHostnameComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8956, + "startOffset": 8600 + } + ] + }, + { + "functionName": "onParsePortComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9125, + "startOffset": 8960 + } + ] + }, + { + "functionName": "onParseHostComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9440, + "startOffset": 9129 + } + ] + }, + { + "functionName": "onParsePathComplete", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 9954, + "startOffset": 9444 + }, + { + "count": 0, + "endOffset": 9787, + "startOffset": 9716 + } + ] + }, + { + "functionName": "onParseSearchComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10129, + "startOffset": 9958 + } + ] + }, + { + "functionName": "onParseHashComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10306, + "startOffset": 10133 + } + ] + }, + { + "functionName": "URL", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 10663, + "startOffset": 10325 + }, + { + "count": 0, + "endOffset": 10518, + "startOffset": 10464 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10752, + "startOffset": 10669 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 327, + "endOffset": 10853, + "startOffset": 10758 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11136, + "startOffset": 10931 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12107, + "startOffset": 11142 + } + ] + }, + { + "functionName": "format", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 13502, + "startOffset": 12284 + }, + { + "count": 0, + "endOffset": 12432, + "startOffset": 12371 + }, + { + "count": 0, + "endOffset": 13023, + "startOffset": 12856 + }, + { + "count": 0, + "endOffset": 13094, + "startOffset": 13056 + }, + { + "count": 0, + "endOffset": 13172, + "startOffset": 13150 + }, + { + "count": 0, + "endOffset": 13247, + "startOffset": 13181 + }, + { + "count": 0, + "endOffset": 13386, + "startOffset": 13363 + }, + { + "count": 0, + "endOffset": 13476, + "startOffset": 13450 + } + ] + }, + { + "functionName": "toString", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 13849, + "startOffset": 13788 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 13967, + "startOffset": 13920 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14159, + "startOffset": 13974 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14895, + "startOffset": 14245 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15020, + "startOffset": 14970 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15408, + "startOffset": 15027 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15535, + "startOffset": 15483 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15953, + "startOffset": 15542 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16080, + "startOffset": 16028 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16498, + "startOffset": 16087 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16731, + "startOffset": 16569 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17038, + "startOffset": 16738 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17167, + "startOffset": 17113 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17482, + "startOffset": 17174 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17656, + "startOffset": 17553 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17991, + "startOffset": 17663 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": true, + "ranges": [ + { + "count": 218, + "endOffset": 18268, + "startOffset": 18066 + }, + { + "count": 0, + "endOffset": 18167, + "startOffset": 18148 + }, + { + "count": 0, + "endOffset": 18221, + "startOffset": 18211 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 18501, + "startOffset": 18275 + }, + { + "count": 0, + "endOffset": 18396, + "startOffset": 18389 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18718, + "startOffset": 18574 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19232, + "startOffset": 18725 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19372, + "startOffset": 19324 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19599, + "startOffset": 19443 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20037, + "startOffset": 19606 + } + ] + }, + { + "functionName": "toJSON", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20249, + "startOffset": 20190 + } + ] + }, + { + "functionName": "update", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20581, + "startOffset": 20263 + } + ] + }, + { + "functionName": "initSearchParams", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 20731, + "startOffset": 20585 + }, + { + "count": 0, + "endOffset": 20730, + "startOffset": 20686 + } + ] + }, + { + "functionName": "parseParams", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 23241, + "startOffset": 20844 + } + ] + }, + { + "functionName": "serializeParams", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 24820, + "startOffset": 24244 + } + ] + }, + { + "functionName": "defineIDLClass", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 25592, + "startOffset": 24878 + }, + { + "count": 13, + "endOffset": 25379, + "startOffset": 25226 + }, + { + "count": 1, + "endOffset": 25589, + "startOffset": 25435 + } + ] + }, + { + "functionName": "merge", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 26271, + "startOffset": 25615 + } + ] + }, + { + "functionName": "append", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 26740, + "startOffset": 26341 + } + ] + }, + { + "functionName": "delete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27265, + "startOffset": 26747 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27722, + "startOffset": 27272 + } + ] + }, + { + "functionName": "getAll", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 28214, + "startOffset": 27729 + } + ] + }, + { + "functionName": "has", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 28665, + "startOffset": 28221 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 29736, + "startOffset": 28672 + } + ] + }, + { + "functionName": "sort", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 30962, + "startOffset": 29743 + } + ] + }, + { + "functionName": "entries", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31345, + "startOffset": 31130 + } + ] + }, + { + "functionName": "forEach", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31943, + "startOffset": 31352 + } + ] + }, + { + "functionName": "keys", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 32207, + "startOffset": 32001 + } + ] + }, + { + "functionName": "values", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 32424, + "startOffset": 32214 + } + ] + }, + { + "functionName": "toString", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 32767, + "startOffset": 32561 + } + ] + }, + { + "functionName": "createSearchParamsIterator", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33269, + "startOffset": 33058 + } + ] + }, + { + "functionName": "next", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 34289, + "startOffset": 33499 + } + ] + }, + { + "functionName": "defineIDLClass", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 35544, + "startOffset": 34294 + } + ] + }, + { + "functionName": "domainToASCII", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 35734, + "startOffset": 35553 + } + ] + }, + { + "functionName": "domainToUnicode", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 35923, + "startOffset": 35738 + } + ] + }, + { + "functionName": "urlToOptions", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36633, + "startOffset": 36071 + } + ] + }, + { + "functionName": "getPathFromURLWin32", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 38099, + "startOffset": 36673 + } + ] + }, + { + "functionName": "getPathFromURLPosix", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 38623, + "startOffset": 38103 + } + ] + }, + { + "functionName": "fileURLToPath", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 38982, + "startOffset": 38627 + } + ] + }, + { + "functionName": "encodePathChars", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 40330, + "startOffset": 39761 + }, + { + "count": 0, + "endOffset": 39883, + "startOffset": 39834 + }, + { + "count": 0, + "endOffset": 39985, + "startOffset": 39959 + }, + { + "count": 0, + "endOffset": 40043, + "startOffset": 39992 + }, + { + "count": 0, + "endOffset": 40130, + "startOffset": 40081 + }, + { + "count": 0, + "endOffset": 40224, + "startOffset": 40168 + }, + { + "count": 0, + "endOffset": 40307, + "startOffset": 40262 + } + ] + }, + { + "functionName": "pathToFileURL", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 41482, + "startOffset": 40334 + }, + { + "count": 0, + "endOffset": 41025, + "startOffset": 40456 + }, + { + "count": 0, + "endOffset": 41381, + "startOffset": 41327 + }, + { + "count": 0, + "endOffset": 41406, + "startOffset": 41390 + } + ] + }, + { + "functionName": "isURLInstance", + "isBlockCoverage": true, + "ranges": [ + { + "count": 277, + "endOffset": 41607, + "startOffset": 41486 + }, + { + "count": 0, + "endOffset": 41603, + "startOffset": 41580 + } + ] + }, + { + "functionName": "toPathIfFileURL", + "isBlockCoverage": true, + "ranges": [ + { + "count": 277, + "endOffset": 41760, + "startOffset": 41611 + }, + { + "count": 0, + "endOffset": 41759, + "startOffset": 41717 + } + ] + }, + { + "functionName": "constructUrl", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 42484, + "startOffset": 41764 + } + ] + } + ], + "scriptId": "31", + "url": "internal/url.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 3134, + "startOffset": 0 + } + ] + }, + { + "functionName": "encodeStr", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3065, + "startOffset": 1374 + } + ] + } + ], + "scriptId": "32", + "url": "internal/querystring.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 44873, + "startOffset": 0 + } + ] + }, + { + "functionName": "isPathSeparator", + "isBlockCoverage": true, + "ranges": [ + { + "count": 144876, + "endOffset": 1635, + "startOffset": 1529 + }, + { + "count": 144646, + "endOffset": 1631, + "startOffset": 1600 + } + ] + }, + { + "functionName": "isPosixPathSeparator", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1718, + "startOffset": 1639 + } + ] + }, + { + "functionName": "isWindowsDeviceRoot", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5087, + "endOffset": 1895, + "startOffset": 1722 + }, + { + "count": 4801, + "endOffset": 1822, + "startOffset": 1795 + }, + { + "count": 1027, + "endOffset": 1891, + "startOffset": 1824 + }, + { + "count": 741, + "endOffset": 1890, + "startOffset": 1863 + } + ] + }, + { + "functionName": "normalizeString", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2284, + "endOffset": 3826, + "startOffset": 1961 + }, + { + "count": 131113, + "endOffset": 3808, + "startOffset": 2184 + }, + { + "count": 128829, + "endOffset": 2245, + "startOffset": 2219 + }, + { + "count": 2284, + "endOffset": 2340, + "startOffset": 2245 + }, + { + "count": 2279, + "endOffset": 2296, + "startOffset": 2290 + }, + { + "count": 5, + "endOffset": 2340, + "startOffset": 2296 + }, + { + "count": 128834, + "endOffset": 2375, + "startOffset": 2340 + }, + { + "count": 12268, + "endOffset": 3704, + "startOffset": 2375 + }, + { + "count": 11644, + "endOffset": 2421, + "startOffset": 2408 + }, + { + "count": 724, + "endOffset": 2450, + "startOffset": 2423 + }, + { + "count": 11544, + "endOffset": 3658, + "startOffset": 2450 + }, + { + "count": 31, + "endOffset": 3439, + "startOffset": 2472 + }, + { + "count": 0, + "endOffset": 2588, + "startOffset": 2529 + }, + { + "count": 0, + "endOffset": 2648, + "startOffset": 2589 + }, + { + "count": 0, + "endOffset": 2868, + "startOffset": 2789 + }, + { + "count": 0, + "endOffset": 3283, + "startOffset": 3108 + }, + { + "count": 0, + "endOffset": 3430, + "startOffset": 3294 + }, + { + "count": 11513, + "endOffset": 3658, + "startOffset": 3439 + }, + { + "count": 9341, + "endOffset": 3540, + "startOffset": 3487 + }, + { + "count": 2172, + "endOffset": 3601, + "startOffset": 3540 + }, + { + "count": 12237, + "endOffset": 3704, + "startOffset": 3658 + }, + { + "count": 116566, + "endOffset": 3803, + "startOffset": 3704 + }, + { + "count": 1642, + "endOffset": 3746, + "startOffset": 3732 + }, + { + "count": 165, + "endOffset": 3771, + "startOffset": 3748 + }, + { + "count": 116401, + "endOffset": 3803, + "startOffset": 3771 + } + ] + }, + { + "functionName": "_format", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4453, + "startOffset": 4017 + } + ] + }, + { + "functionName": "resolve", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2279, + "endOffset": 9115, + "startOffset": 4579 + }, + { + "count": 3106, + "endOffset": 8648, + "startOffset": 4742 + }, + { + "count": 3105, + "endOffset": 4953, + "startOffset": 4780 + }, + { + "count": 0, + "endOffset": 4944, + "startOffset": 4911 + }, + { + "count": 1, + "endOffset": 5845, + "startOffset": 4953 + }, + { + "count": 0, + "endOffset": 5845, + "startOffset": 5033 + }, + { + "count": 0, + "endOffset": 6218, + "startOffset": 6053 + }, + { + "count": 0, + "endOffset": 7598, + "startOffset": 6251 + }, + { + "count": 2975, + "endOffset": 7689, + "startOffset": 7634 + }, + { + "count": 2279, + "endOffset": 8015, + "startOffset": 7691 + }, + { + "count": 2279, + "endOffset": 8333, + "startOffset": 8048 + }, + { + "count": 0, + "endOffset": 8270, + "startOffset": 8090 + }, + { + "count": 0, + "endOffset": 8433, + "startOffset": 8365 + }, + { + "count": 2279, + "endOffset": 8600, + "startOffset": 8572 + }, + { + "count": 2279, + "endOffset": 8632, + "startOffset": 8602 + }, + { + "count": 0, + "endOffset": 9109, + "startOffset": 9059 + } + ] + }, + { + "functionName": "normalize", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 12023, + "startOffset": 9187 + }, + { + "count": 0, + "endOffset": 9308, + "startOffset": 9297 + }, + { + "count": 0, + "endOffset": 9622, + "startOffset": 9465 + }, + { + "count": 0, + "endOffset": 11204, + "startOffset": 9655 + }, + { + "count": 4, + "endOffset": 11582, + "startOffset": 11278 + }, + { + "count": 0, + "endOffset": 11708, + "startOffset": 11697 + }, + { + "count": 0, + "endOffset": 11751, + "startOffset": 11737 + }, + { + "count": 0, + "endOffset": 11771, + "startOffset": 11760 + }, + { + "count": 0, + "endOffset": 11863, + "startOffset": 11850 + }, + { + "count": 1, + "endOffset": 11950, + "startOffset": 11895 + }, + { + "count": 0, + "endOffset": 11935, + "startOffset": 11922 + }, + { + "count": 4, + "endOffset": 11996, + "startOffset": 11950 + }, + { + "count": 0, + "endOffset": 12017, + "startOffset": 11997 + } + ] + }, + { + "functionName": "isAbsolute", + "isBlockCoverage": true, + "ranges": [ + { + "count": 312, + "endOffset": 12477, + "startOffset": 12096 + }, + { + "count": 0, + "endOffset": 12220, + "startOffset": 12207 + }, + { + "count": 311, + "endOffset": 12380, + "startOffset": 12345 + }, + { + "count": 156, + "endOffset": 12424, + "startOffset": 12381 + }, + { + "count": 112, + "endOffset": 12470, + "startOffset": 12425 + } + ] + }, + { + "functionName": "join", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 14671, + "startOffset": 12552 + }, + { + "count": 0, + "endOffset": 12614, + "startOffset": 12603 + }, + { + "count": 11, + "endOffset": 12926, + "startOffset": 12697 + }, + { + "count": 5, + "endOffset": 12863, + "startOffset": 12838 + }, + { + "count": 6, + "endOffset": 12910, + "startOffset": 12863 + }, + { + "count": 0, + "endOffset": 12978, + "startOffset": 12967 + }, + { + "count": 0, + "endOffset": 14271, + "startOffset": 13851 + }, + { + "count": 0, + "endOffset": 14499, + "startOffset": 14466 + }, + { + "count": 0, + "endOffset": 14620, + "startOffset": 14579 + } + ] + }, + { + "functionName": "relative", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18564, + "startOffset": 14979 + } + ] + }, + { + "functionName": "toNamespacedPath", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1064, + "endOffset": 19630, + "startOffset": 18571 + }, + { + "count": 0, + "endOffset": 18702, + "startOffset": 18690 + }, + { + "count": 0, + "endOffset": 18759, + "startOffset": 18733 + }, + { + "count": 0, + "endOffset": 18865, + "startOffset": 18853 + }, + { + "count": 0, + "endOffset": 19300, + "startOffset": 18929 + }, + { + "count": 0, + "endOffset": 19629, + "startOffset": 19605 + } + ] + }, + { + "functionName": "dirname", + "isBlockCoverage": true, + "ranges": [ + { + "count": 489, + "endOffset": 22114, + "startOffset": 19702 + }, + { + "count": 0, + "endOffset": 19821, + "startOffset": 19810 + }, + { + "count": 0, + "endOffset": 20090, + "startOffset": 19926 + }, + { + "count": 0, + "endOffset": 21455, + "startOffset": 20153 + }, + { + "count": 0, + "endOffset": 21602, + "startOffset": 21599 + }, + { + "count": 5638, + "endOffset": 21973, + "startOffset": 21732 + }, + { + "count": 489, + "endOffset": 21871, + "startOffset": 21782 + }, + { + "count": 5149, + "endOffset": 21966, + "startOffset": 21871 + }, + { + "count": 0, + "endOffset": 22077, + "startOffset": 21997 + } + ] + }, + { + "functionName": "basename", + "isBlockCoverage": true, + "ranges": [ + { + "count": 112, + "endOffset": 24930, + "startOffset": 22214 + }, + { + "count": 0, + "endOffset": 22298, + "startOffset": 22271 + }, + { + "count": 0, + "endOffset": 22778, + "startOffset": 22761 + }, + { + "count": 0, + "endOffset": 22807, + "startOffset": 22779 + }, + { + "count": 0, + "endOffset": 24318, + "startOffset": 22809 + }, + { + "count": 1309, + "endOffset": 24848, + "startOffset": 24371 + }, + { + "count": 112, + "endOffset": 24652, + "startOffset": 24421 + }, + { + "count": 1197, + "endOffset": 24841, + "startOffset": 24652 + }, + { + "count": 112, + "endOffset": 24841, + "startOffset": 24674 + }, + { + "count": 0, + "endOffset": 24889, + "startOffset": 24879 + } + ] + }, + { + "functionName": "extname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27073, + "startOffset": 25002 + } + ] + }, + { + "functionName": "parse", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31903, + "startOffset": 27291 + } + ] + }, + { + "functionName": "resolve", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33025, + "startOffset": 32100 + } + ] + }, + { + "functionName": "normalize", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33694, + "startOffset": 33097 + } + ] + }, + { + "functionName": "isAbsolute", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33899, + "startOffset": 33767 + } + ] + }, + { + "functionName": "join", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 34405, + "startOffset": 33974 + } + ] + }, + { + "functionName": "relative", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36780, + "startOffset": 34502 + } + ] + }, + { + "functionName": "toNamespacedPath", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36866, + "startOffset": 36787 + } + ] + }, + { + "functionName": "dirname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 37590, + "startOffset": 36938 + } + ] + }, + { + "functionName": "basename", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 40083, + "startOffset": 37690 + } + ] + }, + { + "functionName": "extname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 41863, + "startOffset": 40155 + } + ] + }, + { + "functionName": "parse", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 44518, + "startOffset": 42086 + } + ] + } + ], + "scriptId": "33", + "url": "path.js" + } + ], + "timestamp": 967695.999409 + }, + "test_v8_coverage_node_sqlite_9884_1633662346346_0.json": { + "result": [ + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 3134, + "startOffset": 0 + } + ] + }, + { + "functionName": "encodeStr", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3065, + "startOffset": 1374 + } + ] + } + ], + "scriptId": "32", + "url": "internal/querystring.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 44873, + "startOffset": 0 + } + ] + }, + { + "functionName": "isPathSeparator", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2111, + "endOffset": 1635, + "startOffset": 1529 + }, + { + "count": 2108, + "endOffset": 1631, + "startOffset": 1600 + } + ] + }, + { + "functionName": "isPosixPathSeparator", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1718, + "startOffset": 1639 + } + ] + }, + { + "functionName": "isWindowsDeviceRoot", + "isBlockCoverage": true, + "ranges": [ + { + "count": 86, + "endOffset": 1895, + "startOffset": 1722 + }, + { + "count": 81, + "endOffset": 1822, + "startOffset": 1795 + }, + { + "count": 14, + "endOffset": 1891, + "startOffset": 1824 + }, + { + "count": 9, + "endOffset": 1890, + "startOffset": 1863 + } + ] + }, + { + "functionName": "normalizeString", + "isBlockCoverage": true, + "ranges": [ + { + "count": 41, + "endOffset": 3826, + "startOffset": 1961 + }, + { + "count": 1915, + "endOffset": 3808, + "startOffset": 2184 + }, + { + "count": 1874, + "endOffset": 2245, + "startOffset": 2219 + }, + { + "count": 41, + "endOffset": 2340, + "startOffset": 2245 + }, + { + "count": 40, + "endOffset": 2296, + "startOffset": 2290 + }, + { + "count": 1, + "endOffset": 2340, + "startOffset": 2296 + }, + { + "count": 1875, + "endOffset": 2375, + "startOffset": 2340 + }, + { + "count": 209, + "endOffset": 3704, + "startOffset": 2375 + }, + { + "count": 200, + "endOffset": 2421, + "startOffset": 2408 + }, + { + "count": 10, + "endOffset": 2450, + "startOffset": 2423 + }, + { + "count": 199, + "endOffset": 3658, + "startOffset": 2450 + }, + { + "count": 2, + "endOffset": 3439, + "startOffset": 2472 + }, + { + "count": 0, + "endOffset": 2588, + "startOffset": 2529 + }, + { + "count": 0, + "endOffset": 2648, + "startOffset": 2589 + }, + { + "count": 0, + "endOffset": 2868, + "startOffset": 2789 + }, + { + "count": 0, + "endOffset": 3283, + "startOffset": 3108 + }, + { + "count": 0, + "endOffset": 3430, + "startOffset": 3294 + }, + { + "count": 197, + "endOffset": 3658, + "startOffset": 3439 + }, + { + "count": 158, + "endOffset": 3540, + "startOffset": 3487 + }, + { + "count": 39, + "endOffset": 3601, + "startOffset": 3540 + }, + { + "count": 207, + "endOffset": 3704, + "startOffset": 3658 + }, + { + "count": 1666, + "endOffset": 3803, + "startOffset": 3704 + }, + { + "count": 24, + "endOffset": 3746, + "startOffset": 3732 + }, + { + "count": 9, + "endOffset": 3771, + "startOffset": 3748 + }, + { + "count": 1657, + "endOffset": 3803, + "startOffset": 3771 + } + ] + }, + { + "functionName": "_format", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4453, + "startOffset": 4017 + } + ] + }, + { + "functionName": "resolve", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9115, + "startOffset": 4579 + }, + { + "count": 53, + "endOffset": 8648, + "startOffset": 4742 + }, + { + "count": 52, + "endOffset": 4953, + "startOffset": 4780 + }, + { + "count": 0, + "endOffset": 4944, + "startOffset": 4911 + }, + { + "count": 1, + "endOffset": 5845, + "startOffset": 4953 + }, + { + "count": 0, + "endOffset": 5845, + "startOffset": 5033 + }, + { + "count": 0, + "endOffset": 6218, + "startOffset": 6053 + }, + { + "count": 0, + "endOffset": 7598, + "startOffset": 6251 + }, + { + "count": 49, + "endOffset": 7689, + "startOffset": 7634 + }, + { + "count": 40, + "endOffset": 8015, + "startOffset": 7691 + }, + { + "count": 40, + "endOffset": 8333, + "startOffset": 8048 + }, + { + "count": 0, + "endOffset": 8270, + "startOffset": 8090 + }, + { + "count": 0, + "endOffset": 8433, + "startOffset": 8365 + }, + { + "count": 40, + "endOffset": 8600, + "startOffset": 8572 + }, + { + "count": 40, + "endOffset": 8632, + "startOffset": 8602 + }, + { + "count": 0, + "endOffset": 9109, + "startOffset": 9059 + } + ] + }, + { + "functionName": "normalize", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 12023, + "startOffset": 9187 + }, + { + "count": 0, + "endOffset": 9308, + "startOffset": 9297 + }, + { + "count": 0, + "endOffset": 9622, + "startOffset": 9465 + }, + { + "count": 0, + "endOffset": 11204, + "startOffset": 9655 + }, + { + "count": 0, + "endOffset": 11708, + "startOffset": 11697 + }, + { + "count": 0, + "endOffset": 11751, + "startOffset": 11737 + }, + { + "count": 0, + "endOffset": 11771, + "startOffset": 11760 + }, + { + "count": 0, + "endOffset": 11863, + "startOffset": 11850 + }, + { + "count": 0, + "endOffset": 11950, + "startOffset": 11895 + }, + { + "count": 0, + "endOffset": 12017, + "startOffset": 11997 + } + ] + }, + { + "functionName": "isAbsolute", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 12477, + "startOffset": 12096 + }, + { + "count": 0, + "endOffset": 12220, + "startOffset": 12207 + }, + { + "count": 4, + "endOffset": 12424, + "startOffset": 12381 + }, + { + "count": 4, + "endOffset": 12470, + "startOffset": 12425 + } + ] + }, + { + "functionName": "join", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 14671, + "startOffset": 12552 + }, + { + "count": 0, + "endOffset": 12614, + "startOffset": 12603 + }, + { + "count": 2, + "endOffset": 12926, + "startOffset": 12697 + }, + { + "count": 1, + "endOffset": 12910, + "startOffset": 12838 + }, + { + "count": 0, + "endOffset": 12978, + "startOffset": 12967 + }, + { + "count": 0, + "endOffset": 14271, + "startOffset": 13851 + }, + { + "count": 0, + "endOffset": 14499, + "startOffset": 14466 + }, + { + "count": 0, + "endOffset": 14620, + "startOffset": 14579 + } + ] + }, + { + "functionName": "relative", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18564, + "startOffset": 14979 + } + ] + }, + { + "functionName": "toNamespacedPath", + "isBlockCoverage": true, + "ranges": [ + { + "count": 18, + "endOffset": 19630, + "startOffset": 18571 + }, + { + "count": 0, + "endOffset": 18702, + "startOffset": 18690 + }, + { + "count": 0, + "endOffset": 18759, + "startOffset": 18733 + }, + { + "count": 0, + "endOffset": 18865, + "startOffset": 18853 + }, + { + "count": 0, + "endOffset": 19300, + "startOffset": 18929 + }, + { + "count": 0, + "endOffset": 19629, + "startOffset": 19605 + } + ] + }, + { + "functionName": "dirname", + "isBlockCoverage": true, + "ranges": [ + { + "count": 7, + "endOffset": 22114, + "startOffset": 19702 + }, + { + "count": 0, + "endOffset": 19821, + "startOffset": 19810 + }, + { + "count": 0, + "endOffset": 20090, + "startOffset": 19926 + }, + { + "count": 0, + "endOffset": 21455, + "startOffset": 20153 + }, + { + "count": 0, + "endOffset": 21602, + "startOffset": 21599 + }, + { + "count": 57, + "endOffset": 21973, + "startOffset": 21732 + }, + { + "count": 7, + "endOffset": 21871, + "startOffset": 21782 + }, + { + "count": 50, + "endOffset": 21966, + "startOffset": 21871 + }, + { + "count": 0, + "endOffset": 22077, + "startOffset": 21997 + } + ] + }, + { + "functionName": "basename", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 24930, + "startOffset": 22214 + }, + { + "count": 0, + "endOffset": 22298, + "startOffset": 22271 + }, + { + "count": 0, + "endOffset": 22778, + "startOffset": 22761 + }, + { + "count": 0, + "endOffset": 22807, + "startOffset": 22779 + }, + { + "count": 0, + "endOffset": 24318, + "startOffset": 22809 + }, + { + "count": 17, + "endOffset": 24848, + "startOffset": 24371 + }, + { + "count": 2, + "endOffset": 24652, + "startOffset": 24421 + }, + { + "count": 15, + "endOffset": 24841, + "startOffset": 24652 + }, + { + "count": 2, + "endOffset": 24841, + "startOffset": 24674 + }, + { + "count": 0, + "endOffset": 24889, + "startOffset": 24879 + } + ] + }, + { + "functionName": "extname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27073, + "startOffset": 25002 + } + ] + }, + { + "functionName": "parse", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31903, + "startOffset": 27291 + } + ] + }, + { + "functionName": "resolve", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33025, + "startOffset": 32100 + } + ] + }, + { + "functionName": "normalize", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33694, + "startOffset": 33097 + } + ] + }, + { + "functionName": "isAbsolute", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33899, + "startOffset": 33767 + } + ] + }, + { + "functionName": "join", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 34405, + "startOffset": 33974 + } + ] + }, + { + "functionName": "relative", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36780, + "startOffset": 34502 + } + ] + }, + { + "functionName": "toNamespacedPath", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36866, + "startOffset": 36787 + } + ] + }, + { + "functionName": "dirname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 37590, + "startOffset": 36938 + } + ] + }, + { + "functionName": "basename", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 40083, + "startOffset": 37690 + } + ] + }, + { + "functionName": "extname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 41863, + "startOffset": 40155 + } + ] + }, + { + "functionName": "parse", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 44518, + "startOffset": 42086 + } + ] + } + ], + "scriptId": "33", + "url": "path.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 16488, + "startOffset": 0 + } + ] + }, + { + "functionName": "lazyBuffer", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1119, + "startOffset": 1006 + } + ] + }, + { + "functionName": "validateEncoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1251, + "startOffset": 1123 + } + ] + }, + { + "functionName": "validateDecoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1383, + "startOffset": 1255 + } + ] + }, + { + "functionName": "validateArgument", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1601, + "startOffset": 1387 + } + ] + }, + { + "functionName": "trimAsciiWhitespace", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9068, + "startOffset": 8566 + } + ] + }, + { + "functionName": "getEncodingFromLabel", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9255, + "startOffset": 9072 + } + ] + }, + { + "functionName": "TextEncoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9379, + "startOffset": 9331 + } + ] + }, + { + "functionName": "get encoding", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9455, + "startOffset": 9385 + } + ] + }, + { + "functionName": "encode", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9556, + "startOffset": 9461 + } + ] + }, + { + "functionName": "encodeInto", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9879, + "startOffset": 9562 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10291, + "startOffset": 9885 + } + ] + }, + { + "functionName": "makeTextDecoderICU", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 12397, + "startOffset": 10680 + } + ] + }, + { + "functionName": "TextDecoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11574, + "startOffset": 10825 + } + ] + }, + { + "functionName": "decode", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12364, + "startOffset": 11584 + } + ] + }, + { + "functionName": "makeTextDecoderJS", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15025, + "startOffset": 12401 + } + ] + }, + { + "functionName": "get encoding", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15240, + "startOffset": 15156 + } + ] + }, + { + "functionName": "get fatal", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15379, + "startOffset": 15249 + } + ] + }, + { + "functionName": "get ignoreBOM", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15547, + "startOffset": 15388 + } + ] + }, + { + "functionName": "ObjectGetOwnPropertyDescriptors", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16186, + "startOffset": 15556 + } + ] + } + ], + "scriptId": "34", + "url": "internal/encoding.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 8643, + "startOffset": 0 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1882, + "startOffset": 1855 + } + ] + }, + { + "functionName": "unenroll", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3615, + "startOffset": 2396 + } + ] + }, + { + "functionName": "enroll", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4089, + "startOffset": 3827 + } + ] + }, + { + "functionName": "setTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4800, + "startOffset": 4127 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5019, + "startOffset": 4881 + } + ] + }, + { + "functionName": "clearTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5407, + "startOffset": 5028 + } + ] + }, + { + "functionName": "setInterval", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6086, + "startOffset": 5411 + } + ] + }, + { + "functionName": "clearInterval", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6393, + "startOffset": 6090 + } + ] + }, + { + "functionName": "Timeout.close", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6477, + "startOffset": 6423 + } + ] + }, + { + "functionName": "Timeout.", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6689, + "startOffset": 6521 + } + ] + }, + { + "functionName": "setImmediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7259, + "startOffset": 6694 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7482, + "startOffset": 7342 + } + ] + }, + { + "functionName": "clearImmediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7977, + "startOffset": 7493 + } + ] + } + ], + "scriptId": "35", + "url": "timers.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1166, + "startOffset": 0 + } + ] + }, + { + "functionName": "init", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 93, + "startOffset": 17 + } + ] + }, + { + "functionName": "peek", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 221, + "startOffset": 126 + } + ] + }, + { + "functionName": "remove", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 497, + "startOffset": 259 + } + ] + }, + { + "functionName": "append", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1021, + "startOffset": 556 + } + ] + }, + { + "functionName": "isEmpty", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1087, + "startOffset": 1025 + } + ] + } + ], + "scriptId": "36", + "url": "internal/linkedlist.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 19158, + "startOffset": 0 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4543, + "startOffset": 4516 + } + ] + }, + { + "functionName": "initAsyncResource", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5802, + "startOffset": 5518 + } + ] + }, + { + "functionName": "Timeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6918, + "startOffset": 5891 + } + ] + }, + { + "functionName": "Timeout.", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7213, + "startOffset": 7034 + } + ] + }, + { + "functionName": "Timeout.refresh", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7351, + "startOffset": 7246 + } + ] + }, + { + "functionName": "Timeout.unref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7517, + "startOffset": 7382 + } + ] + }, + { + "functionName": "Timeout.ref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7681, + "startOffset": 7546 + } + ] + }, + { + "functionName": "Timeout.hasRef", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7752, + "startOffset": 7713 + } + ] + }, + { + "functionName": "TimersList", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8064, + "startOffset": 7757 + } + ] + }, + { + "functionName": "TimersList.", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8362, + "startOffset": 8183 + } + ] + }, + { + "functionName": "ImmediateList", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 8494, + "startOffset": 8423 + } + ] + }, + { + "functionName": "ImmediateList.append", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8852, + "startOffset": 8677 + } + ] + }, + { + "functionName": "ImmediateList.remove", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9387, + "startOffset": 9034 + } + ] + }, + { + "functionName": "incRefCount", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9471, + "startOffset": 9392 + } + ] + }, + { + "functionName": "decRefCount", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9555, + "startOffset": 9475 + } + ] + }, + { + "functionName": "active", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9698, + "startOffset": 9642 + } + ] + }, + { + "functionName": "unrefActive", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9911, + "startOffset": 9849 + } + ] + }, + { + "functionName": "insertGuarded", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10677, + "startOffset": 10138 + } + ] + }, + { + "functionName": "insert", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11352, + "startOffset": 10681 + } + ] + }, + { + "functionName": "setUnrefTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11672, + "startOffset": 11356 + } + ] + }, + { + "functionName": "getTimerDuration", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12279, + "startOffset": 11742 + } + ] + }, + { + "functionName": "compareTimersLists", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12497, + "startOffset": 12283 + } + ] + }, + { + "functionName": "setPosition", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12574, + "startOffset": 12501 + } + ] + }, + { + "functionName": "getTimerCallbacks", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 17857, + "startOffset": 12578 + } + ] + }, + { + "functionName": "processImmediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14758, + "startOffset": 12896 + } + ] + }, + { + "functionName": "processTimers", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15258, + "startOffset": 14766 + } + ] + }, + { + "functionName": "listOnTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17792, + "startOffset": 15264 + } + ] + }, + { + "functionName": "Immediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18218, + "startOffset": 17882 + } + ] + }, + { + "functionName": "ref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18404, + "startOffset": 18224 + } + ] + }, + { + "functionName": "unref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18593, + "startOffset": 18410 + } + ] + }, + { + "functionName": "hasRef", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18642, + "startOffset": 18599 + } + ] + } + ], + "scriptId": "37", + "url": "internal/timers.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 3052, + "startOffset": 0 + } + ] + }, + { + "functionName": "PriorityQueue", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 838, + "startOffset": 589 + } + ] + }, + { + "functionName": "module.exports", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 886, + "startOffset": 844 + } + ] + }, + { + "functionName": "insert", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1086, + "startOffset": 892 + } + ] + }, + { + "functionName": "peek", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1133, + "startOffset": 1092 + } + ] + }, + { + "functionName": "percolateDown", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1829, + "startOffset": 1139 + } + ] + }, + { + "functionName": "percolateUp", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2344, + "startOffset": 1835 + } + ] + }, + { + "functionName": "removeAt", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2695, + "startOffset": 2350 + } + ] + }, + { + "functionName": "remove", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2876, + "startOffset": 2701 + } + ] + }, + { + "functionName": "shift", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3046, + "startOffset": 2882 + } + ] + } + ], + "scriptId": "38", + "url": "internal/priority_queue.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 2918, + "startOffset": 0 + } + ] + }, + { + "functionName": "initializeDebugEnv", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 904, + "startOffset": 521 + }, + { + "count": 0, + "endOffset": 819, + "startOffset": 614 + } + ] + }, + { + "functionName": "emitWarningIfNeeded", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1314, + "startOffset": 982 + } + ] + }, + { + "functionName": "noop", + "isBlockCoverage": true, + "ranges": [ + { + "count": 13, + "endOffset": 1336, + "startOffset": 1318 + } + ] + }, + { + "functionName": "debuglogImpl", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 1921, + "startOffset": 1340 + }, + { + "count": 2, + "endOffset": 1891, + "startOffset": 1416 + }, + { + "count": 0, + "endOffset": 1841, + "startOffset": 1436 + } + ] + }, + { + "functionName": "debug", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1833, + "startOffset": 1528 + } + ] + }, + { + "functionName": "debuglog", + "isBlockCoverage": true, + "ranges": [ + { + "count": 12, + "endOffset": 2855, + "startOffset": 2147 + } + ] + }, + { + "functionName": "init", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 2278, + "startOffset": 2179 + } + ] + }, + { + "functionName": "debug", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 2539, + "startOffset": 2294 + } + ] + }, + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2644, + "startOffset": 2571 + } + ] + }, + { + "functionName": "logger", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 2691, + "startOffset": 2664 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2779, + "startOffset": 2743 + } + ] + } + ], + "scriptId": "39", + "url": "internal/util/debuglog.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 7161, + "startOffset": 0 + } + ] + }, + { + "functionName": "tryGetCwd", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 890, + "startOffset": 551 + }, + { + "count": 0, + "endOffset": 887, + "startOffset": 615 + } + ] + }, + { + "functionName": "evalModule", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1347, + "startOffset": 894 + } + ] + }, + { + "functionName": "evalScript", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2774, + "startOffset": 1351 + } + ] + }, + { + "functionName": "setUncaughtExceptionCaptureCallback", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3570, + "startOffset": 2858 + } + ] + }, + { + "functionName": "hasUncaughtExceptionCaptureCallback", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3677, + "startOffset": 3574 + } + ] + }, + { + "functionName": "noop", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3699, + "startOffset": 3681 + } + ] + }, + { + "functionName": "createOnGlobalUncaughtException", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 6711, + "startOffset": 4252 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6707, + "startOffset": 4525 + } + ] + }, + { + "functionName": "readStdin", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6931, + "startOffset": 6715 + } + ] + } + ], + "scriptId": "40", + "url": "internal/process/execution.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 5002, + "startOffset": 0 + } + ] + }, + { + "functionName": "lazyOption", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 864, + "startOffset": 295 + } + ] + }, + { + "functionName": "writeOut", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1097, + "startOffset": 970 + } + ] + }, + { + "functionName": "writeToFile", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1504, + "startOffset": 1101 + } + ] + }, + { + "functionName": "doEmitWarning", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1581, + "startOffset": 1508 + } + ] + }, + { + "functionName": "onWarning", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2833, + "startOffset": 1623 + } + ] + }, + { + "functionName": "emitWarning", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4139, + "startOffset": 2961 + } + ] + }, + { + "functionName": "emitWarningSync", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4239, + "startOffset": 4143 + } + ] + }, + { + "functionName": "createWarningObject", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4924, + "startOffset": 4243 + } + ] + } + ], + "scriptId": "41", + "url": "internal/process/warning.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 6632, + "startOffset": 0 + } + ] + }, + { + "functionName": "process._startProfilerIdleNotifier", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 533, + "startOffset": 525 + } + ] + }, + { + "functionName": "process._stopProfilerIdleNotifier", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 580, + "startOffset": 572 + } + ] + }, + { + "functionName": "defineStream", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 738, + "startOffset": 585 + } + ] + }, + { + "functionName": "createWritableStdioStream", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2943, + "startOffset": 1318 + } + ] + }, + { + "functionName": "dummyDestroy", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3343, + "startOffset": 2947 + } + ] + }, + { + "functionName": "getStdout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3728, + "startOffset": 3387 + } + ] + }, + { + "functionName": "getStderr", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4073, + "startOffset": 3732 + } + ] + }, + { + "functionName": "getStdin", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6480, + "startOffset": 4077 + } + ] + }, + { + "functionName": "rawMethods.resetStdioForTesting", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6629, + "startOffset": 6546 + } + ] + } + ], + "scriptId": "42", + "url": "internal/bootstrap/switches/is_main_thread.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1177, + "startOffset": 0 + } + ] + }, + { + "functionName": "isSignal", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 318, + "startOffset": 220 + } + ] + }, + { + "functionName": "startListeningIfSignal", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 892, + "startOffset": 385 + }, + { + "count": 1, + "endOffset": 472, + "startOffset": 447 + }, + { + "count": 1, + "endOffset": 889, + "startOffset": 474 + }, + { + "count": 0, + "endOffset": 848, + "startOffset": 766 + } + ] + }, + { + "functionName": "stopListeningIfSignal", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1097, + "startOffset": 896 + } + ] + } + ], + "scriptId": "43", + "url": "internal/process/signal.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 3655, + "startOffset": 0 + }, + { + "count": 0, + "endOffset": 598, + "startOffset": 298 + } + ] + }, + { + "functionName": "wrapPosixCredentialSetters", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3064, + "startOffset": 846 + } + ] + }, + { + "functionName": "wrappedChdir", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3397, + "startOffset": 3221 + } + ] + }, + { + "functionName": "wrappedUmask", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3542, + "startOffset": 3401 + } + ] + }, + { + "functionName": "wrappedCwd", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 3653, + "startOffset": 3546 + }, + { + "count": 1, + "endOffset": 3629, + "startOffset": 3600 + } + ] + } + ], + "scriptId": "44", + "url": "internal/bootstrap/switches/does_own_process_state.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 649, + "startOffset": 0 + } + ] + } + ], + "scriptId": "45", + "url": "internal/main/run_main_module.js" + } + ], + "timestamp": 967709.209879 + }, + "test_v8_coverage_node_sqlite_merged.json": { + "result": [ + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 3655, + "startOffset": 0 + }, + { + "count": 0, + "endOffset": 598, + "startOffset": 298 + } + ] + }, + { + "functionName": "wrapPosixCredentialSetters", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3064, + "startOffset": 846 + } + ] + }, + { + "functionName": "wrappedChdir", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3397, + "startOffset": 3221 + } + ] + }, + { + "functionName": "wrappedUmask", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3542, + "startOffset": 3401 + } + ] + }, + { + "functionName": "wrappedCwd", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 3653, + "startOffset": 3546 + }, + { + "count": 1, + "endOffset": 3629, + "startOffset": 3600 + } + ] + } + ], + "scriptId": "0", + "url": "internal/bootstrap/switches/does_own_process_state.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 6632, + "startOffset": 0 + } + ] + }, + { + "functionName": "process._startProfilerIdleNotifier", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 533, + "startOffset": 525 + } + ] + }, + { + "functionName": "process._stopProfilerIdleNotifier", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 580, + "startOffset": 572 + } + ] + }, + { + "functionName": "defineStream", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 738, + "startOffset": 585 + } + ] + }, + { + "functionName": "createWritableStdioStream", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2943, + "startOffset": 1318 + } + ] + }, + { + "functionName": "dummyDestroy", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3343, + "startOffset": 2947 + } + ] + }, + { + "functionName": "getStdout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3728, + "startOffset": 3387 + } + ] + }, + { + "functionName": "getStderr", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4073, + "startOffset": 3732 + } + ] + }, + { + "functionName": "getStdin", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6480, + "startOffset": 4077 + } + ] + }, + { + "functionName": "rawMethods.resetStdioForTesting", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6629, + "startOffset": 6546 + } + ] + } + ], + "scriptId": "1", + "url": "internal/bootstrap/switches/is_main_thread.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 20604, + "startOffset": 0 + } + ] + }, + { + "functionName": "Console", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4925, + "startOffset": 2686 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5200, + "startOffset": 5144 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5669, + "startOffset": 5458 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 6489, + "startOffset": 5878 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 6165, + "startOffset": 6067 + }, + { + "count": 1, + "endOffset": 6124, + "startOffset": 6101 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6208, + "startOffset": 6178 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6417, + "startOffset": 6315 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6460, + "startOffset": 6430 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 7776, + "startOffset": 6563 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9334, + "startOffset": 7850 + }, + { + "count": 0, + "endOffset": 8095, + "startOffset": 8081 + }, + { + "count": 0, + "endOffset": 8197, + "startOffset": 8171 + }, + { + "count": 0, + "endOffset": 8432, + "startOffset": 8238 + }, + { + "count": 0, + "endOffset": 8521, + "startOffset": 8493 + }, + { + "count": 0, + "endOffset": 9261, + "startOffset": 8971 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9918, + "startOffset": 9411 + }, + { + "count": 0, + "endOffset": 9635, + "startOffset": 9629 + }, + { + "count": 0, + "endOffset": 9840, + "startOffset": 9714 + }, + { + "count": 0, + "endOffset": 9910, + "startOffset": 9886 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 10124, + "startOffset": 9993 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10330, + "startOffset": 10199 + } + ] + }, + { + "functionName": "createWriteErrorHandler", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 11331, + "startOffset": 10424 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 11327, + "startOffset": 10493 + }, + { + "count": 0, + "endOffset": 10786, + "startOffset": 10768 + } + ] + }, + { + "functionName": "log", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 11452, + "startOffset": 11363 + } + ] + }, + { + "functionName": "warn", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11551, + "startOffset": 11461 + } + ] + }, + { + "functionName": "dir", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11751, + "startOffset": 11560 + } + ] + }, + { + "functionName": "time", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12125, + "startOffset": 11758 + } + ] + }, + { + "functionName": "timeEnd", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12429, + "startOffset": 12132 + } + ] + }, + { + "functionName": "timeLog", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12679, + "startOffset": 12436 + } + ] + }, + { + "functionName": "trace", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12886, + "startOffset": 12693 + } + ] + }, + { + "functionName": "assert", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 13116, + "startOffset": 12893 + } + ] + }, + { + "functionName": "clear", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 13620, + "startOffset": 13180 + } + ] + }, + { + "functionName": "count", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14153, + "startOffset": 13684 + } + ] + }, + { + "functionName": "countReset", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14518, + "startOffset": 14222 + } + ] + }, + { + "functionName": "group", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14699, + "startOffset": 14525 + } + ] + }, + { + "functionName": "groupEnd", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14880, + "startOffset": 14706 + } + ] + }, + { + "functionName": "table", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18454, + "startOffset": 14932 + } + ] + }, + { + "functionName": "timeLogImpl", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19012, + "startOffset": 18499 + } + ] + }, + { + "functionName": "pad", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19095, + "startOffset": 19016 + } + ] + }, + { + "functionName": "formatTime", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19892, + "startOffset": 19099 + } + ] + }, + { + "functionName": "isArray", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20089, + "startOffset": 20033 + } + ] + }, + { + "functionName": "noop", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20112, + "startOffset": 20094 + } + ] + } + ], + "scriptId": "2", + "url": "internal/console/constructor.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1638, + "startOffset": 0 + } + ] + } + ], + "scriptId": "3", + "url": "internal/constants.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 16488, + "startOffset": 0 + } + ] + }, + { + "functionName": "lazyBuffer", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1119, + "startOffset": 1006 + } + ] + }, + { + "functionName": "validateEncoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1251, + "startOffset": 1123 + } + ] + }, + { + "functionName": "validateDecoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1383, + "startOffset": 1255 + } + ] + }, + { + "functionName": "validateArgument", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1601, + "startOffset": 1387 + } + ] + }, + { + "functionName": "trimAsciiWhitespace", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9068, + "startOffset": 8566 + } + ] + }, + { + "functionName": "getEncodingFromLabel", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9255, + "startOffset": 9072 + } + ] + }, + { + "functionName": "TextEncoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9379, + "startOffset": 9331 + } + ] + }, + { + "functionName": "get encoding", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9455, + "startOffset": 9385 + } + ] + }, + { + "functionName": "encode", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9556, + "startOffset": 9461 + } + ] + }, + { + "functionName": "encodeInto", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9879, + "startOffset": 9562 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10291, + "startOffset": 9885 + } + ] + }, + { + "functionName": "makeTextDecoderICU", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 12397, + "startOffset": 10680 + } + ] + }, + { + "functionName": "TextDecoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11574, + "startOffset": 10825 + } + ] + }, + { + "functionName": "decode", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12364, + "startOffset": 11584 + } + ] + }, + { + "functionName": "makeTextDecoderJS", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15025, + "startOffset": 12401 + } + ] + }, + { + "functionName": "get encoding", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15240, + "startOffset": 15156 + } + ] + }, + { + "functionName": "get fatal", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15379, + "startOffset": 15249 + } + ] + }, + { + "functionName": "get ignoreBOM", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15547, + "startOffset": 15388 + } + ] + }, + { + "functionName": "ObjectGetOwnPropertyDescriptors", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16186, + "startOffset": 15556 + } + ] + } + ], + "scriptId": "4", + "url": "internal/encoding.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1166, + "startOffset": 0 + } + ] + }, + { + "functionName": "init", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 93, + "startOffset": 17 + } + ] + }, + { + "functionName": "peek", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 221, + "startOffset": 126 + } + ] + }, + { + "functionName": "remove", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 497, + "startOffset": 259 + } + ] + }, + { + "functionName": "append", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1021, + "startOffset": 556 + } + ] + }, + { + "functionName": "isEmpty", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1087, + "startOffset": 1025 + } + ] + } + ], + "scriptId": "5", + "url": "internal/linkedlist.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 649, + "startOffset": 0 + } + ] + } + ], + "scriptId": "6", + "url": "internal/main/run_main_module.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 3052, + "startOffset": 0 + } + ] + }, + { + "functionName": "PriorityQueue", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 838, + "startOffset": 589 + } + ] + }, + { + "functionName": "module.exports", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 886, + "startOffset": 844 + } + ] + }, + { + "functionName": "insert", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1086, + "startOffset": 892 + } + ] + }, + { + "functionName": "peek", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1133, + "startOffset": 1092 + } + ] + }, + { + "functionName": "percolateDown", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1829, + "startOffset": 1139 + } + ] + }, + { + "functionName": "percolateUp", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2344, + "startOffset": 1835 + } + ] + }, + { + "functionName": "removeAt", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2695, + "startOffset": 2350 + } + ] + }, + { + "functionName": "remove", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2876, + "startOffset": 2701 + } + ] + }, + { + "functionName": "shift", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3046, + "startOffset": 2882 + } + ] + } + ], + "scriptId": "7", + "url": "internal/priority_queue.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 7161, + "startOffset": 0 + } + ] + }, + { + "functionName": "tryGetCwd", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 890, + "startOffset": 551 + }, + { + "count": 0, + "endOffset": 887, + "startOffset": 615 + } + ] + }, + { + "functionName": "evalModule", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1347, + "startOffset": 894 + } + ] + }, + { + "functionName": "evalScript", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2774, + "startOffset": 1351 + } + ] + }, + { + "functionName": "setUncaughtExceptionCaptureCallback", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3570, + "startOffset": 2858 + } + ] + }, + { + "functionName": "hasUncaughtExceptionCaptureCallback", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3677, + "startOffset": 3574 + } + ] + }, + { + "functionName": "noop", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3699, + "startOffset": 3681 + } + ] + }, + { + "functionName": "createOnGlobalUncaughtException", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 6711, + "startOffset": 4252 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6707, + "startOffset": 4525 + } + ] + }, + { + "functionName": "readStdin", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6931, + "startOffset": 6715 + } + ] + } + ], + "scriptId": "8", + "url": "internal/process/execution.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1177, + "startOffset": 0 + } + ] + }, + { + "functionName": "isSignal", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 318, + "startOffset": 220 + } + ] + }, + { + "functionName": "startListeningIfSignal", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 892, + "startOffset": 385 + }, + { + "count": 1, + "endOffset": 472, + "startOffset": 447 + }, + { + "count": 1, + "endOffset": 889, + "startOffset": 474 + }, + { + "count": 0, + "endOffset": 848, + "startOffset": 766 + } + ] + }, + { + "functionName": "stopListeningIfSignal", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1097, + "startOffset": 896 + } + ] + } + ], + "scriptId": "9", + "url": "internal/process/signal.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 5002, + "startOffset": 0 + } + ] + }, + { + "functionName": "lazyOption", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 864, + "startOffset": 295 + } + ] + }, + { + "functionName": "writeOut", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1097, + "startOffset": 970 + } + ] + }, + { + "functionName": "writeToFile", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1504, + "startOffset": 1101 + } + ] + }, + { + "functionName": "doEmitWarning", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1581, + "startOffset": 1508 + } + ] + }, + { + "functionName": "onWarning", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2833, + "startOffset": 1623 + } + ] + }, + { + "functionName": "emitWarning", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4139, + "startOffset": 2961 + } + ] + }, + { + "functionName": "emitWarningSync", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4239, + "startOffset": 4143 + } + ] + }, + { + "functionName": "createWarningObject", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4924, + "startOffset": 4243 + } + ] + } + ], + "scriptId": "10", + "url": "internal/process/warning.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 2, + "endOffset": 3134, + "startOffset": 0 + } + ] + }, + { + "functionName": "encodeStr", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3065, + "startOffset": 1374 + } + ] + } + ], + "scriptId": "11", + "url": "internal/querystring.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 19158, + "startOffset": 0 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4543, + "startOffset": 4516 + } + ] + }, + { + "functionName": "initAsyncResource", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5802, + "startOffset": 5518 + } + ] + }, + { + "functionName": "Timeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6918, + "startOffset": 5891 + } + ] + }, + { + "functionName": "Timeout.", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7213, + "startOffset": 7034 + } + ] + }, + { + "functionName": "Timeout.refresh", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7351, + "startOffset": 7246 + } + ] + }, + { + "functionName": "Timeout.unref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7517, + "startOffset": 7382 + } + ] + }, + { + "functionName": "Timeout.ref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7681, + "startOffset": 7546 + } + ] + }, + { + "functionName": "Timeout.hasRef", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7752, + "startOffset": 7713 + } + ] + }, + { + "functionName": "TimersList", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8064, + "startOffset": 7757 + } + ] + }, + { + "functionName": "TimersList.", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8362, + "startOffset": 8183 + } + ] + }, + { + "functionName": "ImmediateList", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 8494, + "startOffset": 8423 + } + ] + }, + { + "functionName": "ImmediateList.append", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8852, + "startOffset": 8677 + } + ] + }, + { + "functionName": "ImmediateList.remove", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9387, + "startOffset": 9034 + } + ] + }, + { + "functionName": "incRefCount", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9471, + "startOffset": 9392 + } + ] + }, + { + "functionName": "decRefCount", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9555, + "startOffset": 9475 + } + ] + }, + { + "functionName": "active", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9698, + "startOffset": 9642 + } + ] + }, + { + "functionName": "unrefActive", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9911, + "startOffset": 9849 + } + ] + }, + { + "functionName": "insertGuarded", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10677, + "startOffset": 10138 + } + ] + }, + { + "functionName": "insert", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11352, + "startOffset": 10681 + } + ] + }, + { + "functionName": "setUnrefTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11672, + "startOffset": 11356 + } + ] + }, + { + "functionName": "getTimerDuration", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12279, + "startOffset": 11742 + } + ] + }, + { + "functionName": "compareTimersLists", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12497, + "startOffset": 12283 + } + ] + }, + { + "functionName": "setPosition", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12574, + "startOffset": 12501 + } + ] + }, + { + "functionName": "getTimerCallbacks", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 17857, + "startOffset": 12578 + } + ] + }, + { + "functionName": "processImmediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14758, + "startOffset": 12896 + } + ] + }, + { + "functionName": "processTimers", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15258, + "startOffset": 14766 + } + ] + }, + { + "functionName": "listOnTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17792, + "startOffset": 15264 + } + ] + }, + { + "functionName": "Immediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18218, + "startOffset": 17882 + } + ] + }, + { + "functionName": "ref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18404, + "startOffset": 18224 + } + ] + }, + { + "functionName": "unref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18593, + "startOffset": 18410 + } + ] + }, + { + "functionName": "hasRef", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18642, + "startOffset": 18599 + } + ] + } + ], + "scriptId": "12", + "url": "internal/timers.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 42795, + "startOffset": 0 + } + ] + }, + { + "functionName": "toUSVString", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2627, + "startOffset": 2323 + } + ] + }, + { + "functionName": "serializeTupleOrigin", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2965, + "startOffset": 2845 + } + ] + }, + { + "functionName": "URLContext", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 3611, + "startOffset": 3378 + } + ] + }, + { + "functionName": "URLSearchParams", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 6333, + "startOffset": 3909 + }, + { + "count": 0, + "endOffset": 6267, + "startOffset": 4027 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7439, + "startOffset": 6339 + } + ] + }, + { + "functionName": "onParseComplete", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 8156, + "startOffset": 7446 + }, + { + "count": 0, + "endOffset": 7716, + "startOffset": 7706 + }, + { + "count": 0, + "endOffset": 7790, + "startOffset": 7780 + }, + { + "count": 0, + "endOffset": 7877, + "startOffset": 7873 + } + ] + }, + { + "functionName": "onParseError", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8237, + "startOffset": 8160 + } + ] + }, + { + "functionName": "onParseProtocolComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8596, + "startOffset": 8241 + } + ] + }, + { + "functionName": "onParseHostnameComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8956, + "startOffset": 8600 + } + ] + }, + { + "functionName": "onParsePortComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9125, + "startOffset": 8960 + } + ] + }, + { + "functionName": "onParseHostComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9440, + "startOffset": 9129 + } + ] + }, + { + "functionName": "onParsePathComplete", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 9954, + "startOffset": 9444 + }, + { + "count": 0, + "endOffset": 9787, + "startOffset": 9716 + } + ] + }, + { + "functionName": "onParseSearchComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10129, + "startOffset": 9958 + } + ] + }, + { + "functionName": "onParseHashComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10306, + "startOffset": 10133 + } + ] + }, + { + "functionName": "URL", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 10663, + "startOffset": 10325 + }, + { + "count": 0, + "endOffset": 10518, + "startOffset": 10464 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10752, + "startOffset": 10669 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 327, + "endOffset": 10853, + "startOffset": 10758 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11136, + "startOffset": 10931 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12107, + "startOffset": 11142 + } + ] + }, + { + "functionName": "format", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 13502, + "startOffset": 12284 + }, + { + "count": 0, + "endOffset": 12432, + "startOffset": 12371 + }, + { + "count": 0, + "endOffset": 13023, + "startOffset": 12856 + }, + { + "count": 0, + "endOffset": 13094, + "startOffset": 13056 + }, + { + "count": 0, + "endOffset": 13172, + "startOffset": 13150 + }, + { + "count": 0, + "endOffset": 13247, + "startOffset": 13181 + }, + { + "count": 0, + "endOffset": 13386, + "startOffset": 13363 + }, + { + "count": 0, + "endOffset": 13476, + "startOffset": 13450 + } + ] + }, + { + "functionName": "toString", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 13849, + "startOffset": 13788 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 13967, + "startOffset": 13920 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14159, + "startOffset": 13974 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14895, + "startOffset": 14245 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15020, + "startOffset": 14970 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15408, + "startOffset": 15027 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15535, + "startOffset": 15483 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15953, + "startOffset": 15542 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16080, + "startOffset": 16028 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16498, + "startOffset": 16087 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16731, + "startOffset": 16569 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17038, + "startOffset": 16738 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17167, + "startOffset": 17113 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17482, + "startOffset": 17174 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17656, + "startOffset": 17553 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17991, + "startOffset": 17663 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": true, + "ranges": [ + { + "count": 218, + "endOffset": 18268, + "startOffset": 18066 + }, + { + "count": 0, + "endOffset": 18167, + "startOffset": 18148 + }, + { + "count": 0, + "endOffset": 18221, + "startOffset": 18211 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 18501, + "startOffset": 18275 + }, + { + "count": 0, + "endOffset": 18396, + "startOffset": 18389 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18718, + "startOffset": 18574 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19232, + "startOffset": 18725 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19372, + "startOffset": 19324 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19599, + "startOffset": 19443 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20037, + "startOffset": 19606 + } + ] + }, + { + "functionName": "toJSON", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20249, + "startOffset": 20190 + } + ] + }, + { + "functionName": "update", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20581, + "startOffset": 20263 + } + ] + }, + { + "functionName": "initSearchParams", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 20731, + "startOffset": 20585 + }, + { + "count": 0, + "endOffset": 20730, + "startOffset": 20686 + } + ] + }, + { + "functionName": "parseParams", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 23241, + "startOffset": 20844 + } + ] + }, + { + "functionName": "serializeParams", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 24820, + "startOffset": 24244 + } + ] + }, + { + "functionName": "defineIDLClass", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 25592, + "startOffset": 24878 + }, + { + "count": 13, + "endOffset": 25379, + "startOffset": 25226 + }, + { + "count": 1, + "endOffset": 25589, + "startOffset": 25435 + } + ] + }, + { + "functionName": "merge", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 26271, + "startOffset": 25615 + } + ] + }, + { + "functionName": "append", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 26740, + "startOffset": 26341 + } + ] + }, + { + "functionName": "delete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27265, + "startOffset": 26747 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27722, + "startOffset": 27272 + } + ] + }, + { + "functionName": "getAll", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 28214, + "startOffset": 27729 + } + ] + }, + { + "functionName": "has", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 28665, + "startOffset": 28221 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 29736, + "startOffset": 28672 + } + ] + }, + { + "functionName": "sort", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 30962, + "startOffset": 29743 + } + ] + }, + { + "functionName": "entries", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31345, + "startOffset": 31130 + } + ] + }, + { + "functionName": "forEach", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31943, + "startOffset": 31352 + } + ] + }, + { + "functionName": "keys", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 32207, + "startOffset": 32001 + } + ] + }, + { + "functionName": "values", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 32424, + "startOffset": 32214 + } + ] + }, + { + "functionName": "toString", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 32767, + "startOffset": 32561 + } + ] + }, + { + "functionName": "createSearchParamsIterator", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33269, + "startOffset": 33058 + } + ] + }, + { + "functionName": "next", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 34289, + "startOffset": 33499 + } + ] + }, + { + "functionName": "defineIDLClass", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 35544, + "startOffset": 34294 + } + ] + }, + { + "functionName": "domainToASCII", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 35734, + "startOffset": 35553 + } + ] + }, + { + "functionName": "domainToUnicode", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 35923, + "startOffset": 35738 + } + ] + }, + { + "functionName": "urlToOptions", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36633, + "startOffset": 36071 + } + ] + }, + { + "functionName": "getPathFromURLWin32", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 38099, + "startOffset": 36673 + } + ] + }, + { + "functionName": "getPathFromURLPosix", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 38623, + "startOffset": 38103 + } + ] + }, + { + "functionName": "fileURLToPath", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 38982, + "startOffset": 38627 + } + ] + }, + { + "functionName": "encodePathChars", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 40330, + "startOffset": 39761 + }, + { + "count": 0, + "endOffset": 39883, + "startOffset": 39834 + }, + { + "count": 0, + "endOffset": 39985, + "startOffset": 39959 + }, + { + "count": 0, + "endOffset": 40043, + "startOffset": 39992 + }, + { + "count": 0, + "endOffset": 40130, + "startOffset": 40081 + }, + { + "count": 0, + "endOffset": 40224, + "startOffset": 40168 + }, + { + "count": 0, + "endOffset": 40307, + "startOffset": 40262 + } + ] + }, + { + "functionName": "pathToFileURL", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 41482, + "startOffset": 40334 + }, + { + "count": 0, + "endOffset": 41025, + "startOffset": 40456 + }, + { + "count": 0, + "endOffset": 41381, + "startOffset": 41327 + }, + { + "count": 0, + "endOffset": 41406, + "startOffset": 41390 + } + ] + }, + { + "functionName": "isURLInstance", + "isBlockCoverage": true, + "ranges": [ + { + "count": 277, + "endOffset": 41607, + "startOffset": 41486 + }, + { + "count": 0, + "endOffset": 41603, + "startOffset": 41580 + } + ] + }, + { + "functionName": "toPathIfFileURL", + "isBlockCoverage": true, + "ranges": [ + { + "count": 277, + "endOffset": 41760, + "startOffset": 41611 + }, + { + "count": 0, + "endOffset": 41759, + "startOffset": 41717 + } + ] + }, + { + "functionName": "constructUrl", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 42484, + "startOffset": 41764 + } + ] + } + ], + "scriptId": "13", + "url": "internal/url.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 2918, + "startOffset": 0 + } + ] + }, + { + "functionName": "initializeDebugEnv", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 904, + "startOffset": 521 + }, + { + "count": 0, + "endOffset": 819, + "startOffset": 614 + } + ] + }, + { + "functionName": "emitWarningIfNeeded", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1314, + "startOffset": 982 + } + ] + }, + { + "functionName": "noop", + "isBlockCoverage": true, + "ranges": [ + { + "count": 13, + "endOffset": 1336, + "startOffset": 1318 + } + ] + }, + { + "functionName": "debuglogImpl", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 1921, + "startOffset": 1340 + }, + { + "count": 2, + "endOffset": 1891, + "startOffset": 1416 + }, + { + "count": 0, + "endOffset": 1841, + "startOffset": 1436 + } + ] + }, + { + "functionName": "debug", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1833, + "startOffset": 1528 + } + ] + }, + { + "functionName": "debuglog", + "isBlockCoverage": true, + "ranges": [ + { + "count": 12, + "endOffset": 2855, + "startOffset": 2147 + } + ] + }, + { + "functionName": "init", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 2278, + "startOffset": 2179 + } + ] + }, + { + "functionName": "debug", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 2539, + "startOffset": 2294 + } + ] + }, + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2644, + "startOffset": 2571 + } + ] + }, + { + "functionName": "logger", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 2691, + "startOffset": 2664 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2779, + "startOffset": 2743 + } + ] + } + ], + "scriptId": "14", + "url": "internal/util/debuglog.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 2283, + "startOffset": 0 + } + ] + }, + { + "functionName": "sendInspectorCommand", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 453, + "startOffset": 100 + } + ] + }, + { + "functionName": "installConsoleExtensions", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1094, + "startOffset": 530 + } + ] + }, + { + "functionName": "wrapConsole", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 1984, + "startOffset": 1176 + }, + { + "count": 23, + "endOffset": 1981, + "startOffset": 1336 + }, + { + "count": 19, + "endOffset": 1855, + "startOffset": 1555 + }, + { + "count": 4, + "endOffset": 1976, + "startOffset": 1855 + } + ] + }, + { + "functionName": "get consoleFromVM", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2217, + "startOffset": 2164 + } + ] + }, + { + "functionName": "set consoleFromVM", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 2277, + "startOffset": 2222 + } + ] + } + ], + "scriptId": "15", + "url": "internal/util/inspector.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 2, + "endOffset": 44873, + "startOffset": 0 + } + ] + }, + { + "functionName": "isPathSeparator", + "isBlockCoverage": true, + "ranges": [ + { + "count": 146987, + "endOffset": 1635, + "startOffset": 1529 + }, + { + "count": 146754, + "endOffset": 1631, + "startOffset": 1600 + } + ] + }, + { + "functionName": "isPosixPathSeparator", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1718, + "startOffset": 1639 + } + ] + }, + { + "functionName": "isWindowsDeviceRoot", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5173, + "endOffset": 1895, + "startOffset": 1722 + }, + { + "count": 4882, + "endOffset": 1822, + "startOffset": 1795 + }, + { + "count": 1041, + "endOffset": 1891, + "startOffset": 1824 + }, + { + "count": 750, + "endOffset": 1890, + "startOffset": 1863 + } + ] + }, + { + "functionName": "normalizeString", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2325, + "endOffset": 3826, + "startOffset": 1961 + }, + { + "count": 133028, + "endOffset": 3808, + "startOffset": 2184 + }, + { + "count": 130703, + "endOffset": 2245, + "startOffset": 2219 + }, + { + "count": 2325, + "endOffset": 2340, + "startOffset": 2245 + }, + { + "count": 2319, + "endOffset": 2296, + "startOffset": 2290 + }, + { + "count": 6, + "endOffset": 2340, + "startOffset": 2296 + }, + { + "count": 130709, + "endOffset": 2375, + "startOffset": 2340 + }, + { + "count": 12477, + "endOffset": 3704, + "startOffset": 2375 + }, + { + "count": 11844, + "endOffset": 2421, + "startOffset": 2408 + }, + { + "count": 734, + "endOffset": 2450, + "startOffset": 2423 + }, + { + "count": 11743, + "endOffset": 3658, + "startOffset": 2450 + }, + { + "count": 33, + "endOffset": 3439, + "startOffset": 2472 + }, + { + "count": 0, + "endOffset": 2588, + "startOffset": 2529 + }, + { + "count": 0, + "endOffset": 2648, + "startOffset": 2589 + }, + { + "count": 0, + "endOffset": 2868, + "startOffset": 2789 + }, + { + "count": 0, + "endOffset": 3283, + "startOffset": 3108 + }, + { + "count": 0, + "endOffset": 3430, + "startOffset": 3294 + }, + { + "count": 11710, + "endOffset": 3658, + "startOffset": 3439 + }, + { + "count": 9499, + "endOffset": 3540, + "startOffset": 3487 + }, + { + "count": 2211, + "endOffset": 3601, + "startOffset": 3540 + }, + { + "count": 12444, + "endOffset": 3704, + "startOffset": 3658 + }, + { + "count": 118232, + "endOffset": 3803, + "startOffset": 3704 + }, + { + "count": 1666, + "endOffset": 3746, + "startOffset": 3732 + }, + { + "count": 174, + "endOffset": 3771, + "startOffset": 3748 + }, + { + "count": 118058, + "endOffset": 3803, + "startOffset": 3771 + } + ] + }, + { + "functionName": "_format", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4453, + "startOffset": 4017 + } + ] + }, + { + "functionName": "resolve", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2319, + "endOffset": 9115, + "startOffset": 4579 + }, + { + "count": 3159, + "endOffset": 8648, + "startOffset": 4742 + }, + { + "count": 3157, + "endOffset": 4953, + "startOffset": 4780 + }, + { + "count": 0, + "endOffset": 4944, + "startOffset": 4911 + }, + { + "count": 2, + "endOffset": 5845, + "startOffset": 4953 + }, + { + "count": 0, + "endOffset": 5845, + "startOffset": 5033 + }, + { + "count": 0, + "endOffset": 6218, + "startOffset": 6053 + }, + { + "count": 0, + "endOffset": 7598, + "startOffset": 6251 + }, + { + "count": 3024, + "endOffset": 7689, + "startOffset": 7634 + }, + { + "count": 2319, + "endOffset": 8015, + "startOffset": 7691 + }, + { + "count": 2319, + "endOffset": 8333, + "startOffset": 8048 + }, + { + "count": 0, + "endOffset": 8270, + "startOffset": 8090 + }, + { + "count": 0, + "endOffset": 8433, + "startOffset": 8365 + }, + { + "count": 2319, + "endOffset": 8600, + "startOffset": 8572 + }, + { + "count": 2319, + "endOffset": 8632, + "startOffset": 8602 + }, + { + "count": 0, + "endOffset": 9109, + "startOffset": 9059 + } + ] + }, + { + "functionName": "normalize", + "isBlockCoverage": true, + "ranges": [ + { + "count": 6, + "endOffset": 12023, + "startOffset": 9187 + }, + { + "count": 0, + "endOffset": 9308, + "startOffset": 9297 + }, + { + "count": 0, + "endOffset": 9622, + "startOffset": 9465 + }, + { + "count": 0, + "endOffset": 11204, + "startOffset": 9655 + }, + { + "count": 5, + "endOffset": 11582, + "startOffset": 11278 + }, + { + "count": 0, + "endOffset": 11708, + "startOffset": 11697 + }, + { + "count": 0, + "endOffset": 11751, + "startOffset": 11737 + }, + { + "count": 0, + "endOffset": 11771, + "startOffset": 11760 + }, + { + "count": 0, + "endOffset": 11863, + "startOffset": 11850 + }, + { + "count": 1, + "endOffset": 11950, + "startOffset": 11895 + }, + { + "count": 0, + "endOffset": 11935, + "startOffset": 11922 + }, + { + "count": 5, + "endOffset": 11996, + "startOffset": 11950 + }, + { + "count": 0, + "endOffset": 12017, + "startOffset": 11997 + } + ] + }, + { + "functionName": "isAbsolute", + "isBlockCoverage": true, + "ranges": [ + { + "count": 317, + "endOffset": 12477, + "startOffset": 12096 + }, + { + "count": 0, + "endOffset": 12220, + "startOffset": 12207 + }, + { + "count": 316, + "endOffset": 12380, + "startOffset": 12345 + }, + { + "count": 160, + "endOffset": 12424, + "startOffset": 12381 + }, + { + "count": 116, + "endOffset": 12470, + "startOffset": 12425 + } + ] + }, + { + "functionName": "join", + "isBlockCoverage": true, + "ranges": [ + { + "count": 6, + "endOffset": 14671, + "startOffset": 12552 + }, + { + "count": 0, + "endOffset": 12614, + "startOffset": 12603 + }, + { + "count": 13, + "endOffset": 12926, + "startOffset": 12697 + }, + { + "count": 12, + "endOffset": 12910, + "startOffset": 12838 + }, + { + "count": 6, + "endOffset": 12863, + "startOffset": 12838 + }, + { + "count": 7, + "endOffset": 12910, + "startOffset": 12863 + }, + { + "count": 0, + "endOffset": 12978, + "startOffset": 12967 + }, + { + "count": 0, + "endOffset": 14271, + "startOffset": 13851 + }, + { + "count": 0, + "endOffset": 14499, + "startOffset": 14466 + }, + { + "count": 0, + "endOffset": 14620, + "startOffset": 14579 + } + ] + }, + { + "functionName": "relative", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18564, + "startOffset": 14979 + } + ] + }, + { + "functionName": "toNamespacedPath", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1082, + "endOffset": 19630, + "startOffset": 18571 + }, + { + "count": 0, + "endOffset": 18702, + "startOffset": 18690 + }, + { + "count": 0, + "endOffset": 18759, + "startOffset": 18733 + }, + { + "count": 0, + "endOffset": 18865, + "startOffset": 18853 + }, + { + "count": 0, + "endOffset": 19300, + "startOffset": 18929 + }, + { + "count": 0, + "endOffset": 19629, + "startOffset": 19605 + } + ] + }, + { + "functionName": "dirname", + "isBlockCoverage": true, + "ranges": [ + { + "count": 496, + "endOffset": 22114, + "startOffset": 19702 + }, + { + "count": 0, + "endOffset": 19821, + "startOffset": 19810 + }, + { + "count": 0, + "endOffset": 20090, + "startOffset": 19926 + }, + { + "count": 0, + "endOffset": 21455, + "startOffset": 20153 + }, + { + "count": 0, + "endOffset": 21602, + "startOffset": 21599 + }, + { + "count": 5695, + "endOffset": 21973, + "startOffset": 21732 + }, + { + "count": 496, + "endOffset": 21871, + "startOffset": 21782 + }, + { + "count": 5199, + "endOffset": 21966, + "startOffset": 21871 + }, + { + "count": 0, + "endOffset": 22077, + "startOffset": 21997 + } + ] + }, + { + "functionName": "basename", + "isBlockCoverage": true, + "ranges": [ + { + "count": 114, + "endOffset": 24930, + "startOffset": 22214 + }, + { + "count": 0, + "endOffset": 22298, + "startOffset": 22271 + }, + { + "count": 0, + "endOffset": 22778, + "startOffset": 22761 + }, + { + "count": 0, + "endOffset": 22807, + "startOffset": 22779 + }, + { + "count": 0, + "endOffset": 24318, + "startOffset": 22809 + }, + { + "count": 1326, + "endOffset": 24848, + "startOffset": 24371 + }, + { + "count": 114, + "endOffset": 24652, + "startOffset": 24421 + }, + { + "count": 1212, + "endOffset": 24841, + "startOffset": 24652 + }, + { + "count": 114, + "endOffset": 24841, + "startOffset": 24674 + }, + { + "count": 0, + "endOffset": 24889, + "startOffset": 24879 + } + ] + }, + { + "functionName": "extname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27073, + "startOffset": 25002 + } + ] + }, + { + "functionName": "parse", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31903, + "startOffset": 27291 + } + ] + }, + { + "functionName": "resolve", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33025, + "startOffset": 32100 + } + ] + }, + { + "functionName": "normalize", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33694, + "startOffset": 33097 + } + ] + }, + { + "functionName": "isAbsolute", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33899, + "startOffset": 33767 + } + ] + }, + { + "functionName": "join", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 34405, + "startOffset": 33974 + } + ] + }, + { + "functionName": "relative", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36780, + "startOffset": 34502 + } + ] + }, + { + "functionName": "toNamespacedPath", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36866, + "startOffset": 36787 + } + ] + }, + { + "functionName": "dirname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 37590, + "startOffset": 36938 + } + ] + }, + { + "functionName": "basename", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 40083, + "startOffset": 37690 + } + ] + }, + { + "functionName": "extname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 41863, + "startOffset": 40155 + } + ] + }, + { + "functionName": "parse", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 44518, + "startOffset": 42086 + } + ] + } + ], + "scriptId": "16", + "url": "path.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 8643, + "startOffset": 0 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1882, + "startOffset": 1855 + } + ] + }, + { + "functionName": "unenroll", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3615, + "startOffset": 2396 + } + ] + }, + { + "functionName": "enroll", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4089, + "startOffset": 3827 + } + ] + }, + { + "functionName": "setTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4800, + "startOffset": 4127 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5019, + "startOffset": 4881 + } + ] + }, + { + "functionName": "clearTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5407, + "startOffset": 5028 + } + ] + }, + { + "functionName": "setInterval", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6086, + "startOffset": 5411 + } + ] + }, + { + "functionName": "clearInterval", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6393, + "startOffset": 6090 + } + ] + }, + { + "functionName": "Timeout.close", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6477, + "startOffset": 6423 + } + ] + }, + { + "functionName": "Timeout.", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6689, + "startOffset": 6521 + } + ] + }, + { + "functionName": "setImmediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7259, + "startOffset": 6694 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7482, + "startOffset": 7342 + } + ] + }, + { + "functionName": "clearImmediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7977, + "startOffset": 7493 + } + ] + } + ], + "scriptId": "17", + "url": "timers.js" + } + ] + } +} diff --git a/branch-sandbox/.artifact/apidoc.html b/branch-sandbox/.artifact/apidoc.html new file mode 100644 index 000000000..718a7a36b --- /dev/null +++ b/branch-sandbox/.artifact/apidoc.html @@ -0,0 +1,1966 @@ + + + + + + +JSLint apidoc + + + +
    +

    API Doc for JSLint (v2021.12.20)

    +
    + +

    Table of Contents

    +
    + +
    + +
    +

    Module "./jslint.mjs"

    +
      + +
    • +

      + 1. + function assertErrorThrownAsync(asyncFunc, regexp) + +

      +
    • +
    • Description and source-code:
      async function assertErrorThrownAsync(asyncFunc, regexp) {
      +
      +// This function will assert calling <asyncFunc> throws an error.
      +
      +    let err;
      +    try {
      +        await asyncFunc();
      +    } catch (errCaught) {
      +        err = errCaught;
      +    }
      +    assertOrThrow(err, "No error thrown.");
      +    assertOrThrow(
      +        regexp === undefined || new RegExp(regexp).test(err.message),
      +        err
      +    );
      +}
    • +
    • Example usage:
      ...
      +
      +jstestDescribe((
      +    "test v8CoverageReportCreate's handling-behavior"
      +), function () {
      +    jstestIt((
      +        "test null-case handling-behavior"
      +    ), async function () {
      +        await assertErrorThrownAsync(function () {
      +            return v8CoverageReportCreate({});
      +        }, "invalid coverageDir");
      +    });
      +    jstestIt((
      +        "test coverage-report jslint.mjs handling-behavior"
      +    ), async function () {
      +        // test remove-old-coverage handling-behavior
      +...
    • + +
    • +

      + 2. + function assertJsonEqual(aa, bb, message) + +

      +
    • +
    • Description and source-code:
      function assertJsonEqual(aa, bb, message) {
      +
      +// This function will assert JSON.stringify(<aa>) === JSON.stringify(<bb>).
      +
      +    aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa), undefined, 1);
      +    bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb), undefined, 1);
      +    if (aa !== bb) {
      +        throw new Error(
      +            "\n" + aa + "\n!==\n" + bb
      +            + (
      +                typeof message === "string"
      +                ? " - " + message
      +                : message
      +                ? " - " + JSON.stringify(message)
      +                : ""
      +            )
      +        );
      +    }
      +}
    • +
    • Example usage:
      ...
      +
      +// Debug data1.
      +// await moduleFs.promises.writeFile(
      +//     ".test_v8_coverage_node_sqlite_merged.json",
      +//     JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n"
      +// );
      +
      +        assertJsonEqual(data1, data2);
      +    });
      +});
      +
      +jstestDescribe((
      +    "test v8CoverageReportCreate's handling-behavior"
      +), function () {
      +    jstestIt((
      +...
    • + +
    • +

      + 3. + function assertOrThrow(condition, message) + +

      +
    • +
    • Description and source-code:
      function assertOrThrow(condition, message) {
      +
      +// This function will throw <message> if <condition> is falsy.
      +
      +    if (!condition) {
      +        throw (
      +            (!message || typeof message === "string")
      +            ? new Error(String(message).slice(0, 2048))
      +            : message
      +        );
      +    }
      +}
    • +
    • Example usage:
      ...
      +        assertJsonEqual(1, 2, "undefined");
      +    });
      +    await assertErrorThrownAsync(function () {
      +        assertJsonEqual(1, 2, {});
      +    });
      +    // test assertOrThrow's error handling-behavior
      +    await assertErrorThrownAsync(function () {
      +        assertOrThrow(undefined, "undefined");
      +    });
      +    await assertErrorThrownAsync(function () {
      +        assertOrThrow(undefined, new Error());
      +    });
      +}());
      +
      +(async function testcaseV8CoverageXxx() {
      +...
    • + +
    • +

      + 4. + function debugInline(...argv) + +

      +
    • +
    • Description and source-code:
      function debug(...argv) {
      +
      +// This function will print <argv> to stderr and then return <argv>[0].
      +
      +    consoleError("\n\ndebugInline");
      +    consoleError(...argv);
      +    consoleError("\n");
      +    return argv[0];
      +}
    • +
    • Example usage:
      N/A
    • + +
    • +

      + 5. + function fsWriteFileWithParents(pathname, data) + +

      +
    • +
    • Description and source-code:
      async function fsWriteFileWithParents(pathname, data) {
      +
      +// This function will write <data> to <pathname> and lazy-mkdirp if necessary.
      +
      +    await moduleFsInit();
      +
      +// Try writing to pathname.
      +
      +    try {
      +        await moduleFs.promises.writeFile(pathname, data);
      +    } catch (ignore) {
      +
      +// Lazy mkdirp.
      +
      +        await moduleFs.promises.mkdir(modulePath.dirname(pathname), {
      +            recursive: true
      +        });
      +
      +// Retry writing to pathname.
      +
      +        await moduleFs.promises.writeFile(pathname, data);
      +    }
      +    console.error("wrote file " + pathname);
      +}
    • +
    • Example usage:
      ...
      +                }
      +            ]
      +        }, undefined, 4)
      +    ]
      +].map(async function ([
      +    file, data
      +]) {
      +    await fsWriteFileWithParents(file, data);
      +}));
      +await jslint.jslint_cli({
      +    mode_cli: true,
      +    process_argv: [
      +        "node", "jslint.mjs",
      +        "v8_coverage_report=.tmp/coverage_misc"
      +        // "node", ".tmp/coverage_misc/aa.js"
      +...
    • + +
    • +

      + 6. + function htmlEscape(str) + +

      +
    • +
    • Description and source-code:
      function htmlEscape(str) {
      +
      +// This function will make <str> html-safe by escaping & < >.
      +
      +    return String(str).replace((
      +        /&/g
      +    ), "&amp;").replace((
      +        /</g
      +    ), "&lt;").replace((
      +        />/g
      +    ), "&gt;");
      +}
    • +
    • Example usage:
      ...
      +                            inHole = isHole;
      +                        }
      +                        chunk += char;
      +                    });
      +                    lineHtml += htmlEscape(chunk);
      +                    break;
      +                default:
      +                    lineHtml += htmlEscape(line);
      +                }
      +                html += String(`
      +<pre>
      +<span class="lineno">
      +<a href="#${lineId}" id="${lineId}">${String(ii + 1).padStart(5, " ")}.</a>
      +</span>
      +<span class="count
      +...
    • + +
    • +

      + 7. + function jslint( + source = "", + option_dict = empty(), + global_list = [] +) + +

      +
    • +
    • Description and source-code:
      function jslint(
      +    source = "",                // A text to analyze.
      +    option_dict = empty(),      // An object whose keys correspond to option
      +                                // ... names.
      +    global_list = []            // An array of strings containing global
      +                                // ... variables that the file is allowed
      +                                // ... readonly access.
      +) {
      +
      +// The jslint function itself.
      +
      +    let catch_list = [];        // The array containing all catch-blocks.
      +    let catch_stack = [         // The stack of catch-blocks.
      +        {
      +            context: empty()
      +        }
      +    ];
      +    let cause_dict = empty();   // The object of test-causes.
      +    let directive_list = [];    // The directive comments.
      +    let export_dict = empty();  // The exported names and values.
      +    let function_list = [];     // The array containing all functions.
      +    let function_stack = [];    // The stack of functions.
      +    let global_dict = empty();  // The object containing the global
      +                                // ... declarations.
      +    let import_list = [];       // The array collecting all import-from strings.
      +    let line_list = String(     // The array containing source lines.
      +        "\n" + source
      +    ).split(
      +        // rx_crlf
      +        /\n|\r\n?/
      +    ).map(function (line_source) {
      +        return {
      +            line_source
      +        };
      +    });
      +    let mode_stop = false;      // true if JSLint cannot finish.
      +    let property_dict = empty();        // The object containing the tallied
      +                                        // ... property names.
      +    let state = empty();        // jslint state-object to be passed between
      +                                // jslint functions.
      +    let syntax_dict = empty();  // The object containing the parser.
      +    let tenure = empty();       // The predefined property registry.
      +    let token_global = {        // The global object; the outermost context.
      +        async: 0,
      +        body: true,
      +        context: empty(),
      +        f...
      +}
      +
    • +
    • Example usage:
      ...
      +(async function () {
      +    let report;
      +    let result;
      +    let source = "function foo() {console.log(\u0027hello world\u0027);}\n";
      +
      +// Create JSLint report from <source> in javascript.
      +
      +    result = jslint.jslint(source);
      +    report = jslint.jslint_report(result);
      +
      +    await fs.promises.mkdir(".artifact/", {recursive: true});
      +    await fs.promises.writeFile(".artifact/jslint_report_hello.html", report);
      +    console.error("wrote file .artifact/jslint_report_hello.html");
      +}());
      +...
    • + +
    • +

      + 8. + function jslint_apidoc({ + example_list, + github_repo, + module_list, + package_name, + pathname, + version +}) + +

      +
    • +
    • Description and source-code:
      async function jslint_apidoc({
      +    example_list,
      +    github_repo,
      +    module_list,
      +    package_name,
      +    pathname,
      +    version
      +}) {
      +
      +// This function will create API Doc from <module_list>.
      +
      +    let elem_ii = 0;
      +    let html;
      +
      +    function elem_create(moduleObj, key, moduleName) {
      +
      +// This function will create a sub API Doc from elem <moduleObj>[<key>].
      +
      +        let example = "N/A";
      +        let id = encodeURIComponent("apidoc.elem." + moduleName + "." + key);
      +        let name;
      +        let signature;
      +        let source;
      +        name = htmlEscape((typeof moduleObj[key]) + " " + key);
      +        if (typeof moduleObj[key] !== "function") {
      +            return {
      +                name,
      +                signature: (`
      +<a class="apidocElementLiA" href="#${id}">
      +${name}
      +</a>
      +                `),
      +                source: (`
      +<li>
      +    <h2>
      +    <a href="#${id}" id="${id}">
      +    ${name}
      +    </a>
      +    </h2>
      +</li>
      +                `)
      +            };
      +        }
      +        // init source
      +        source = htmlEscape(trim_start(moduleObj[key].toString()));
      +        // init signature
      +        source = source.replace((
      +            /(\([\S\s]*?\)) \{/
      +        ), function (match0, match1) {
      +            signature = htmlEscape(
      +                match1.replace((
      +                    / *?\/\*[\S\s]*?\*\/ */g
      +                ), "").replace((
      +                    / *?\/\/.*/g
      +                ), "").replace((
      +                    /\n{2,}/g
      +                ), "\n")
      +            );
      +            return match0;
      +        });
      +        // init comment
      +        source = source.replace((
      +            /\n(?:\/\/.*?\n)+\n/
      +        ), "<span class=\"apidocCodeCommentSpan\">$&</span>");
      +        // init example
      +        example_list.some(function (example2) {
      +            example2.replace(
      +                new RegExp((
      +                    "((?:\\n.*?){8}(function )?)\\b"
      +                    + key
      +                    + "(\\((?:.*?\\n){8})"
      +                ), "g"),
      +  ...
      +}
      +
    • +
    • Example usage:
      ...
      +command[1] = command.slice(1).join("=");
      +
      +switch (command[0]) {
      +
      +// PR-362 - Add API Doc.
      +
      +case "jslint_apidoc":
      +    await jslint_apidoc(Object.assign(JSON.parse(process_argv[3]), {
      +        pathname: command[1]
      +    }));
      +    return;
      +
      +// COMMIT-b26d6df2 - Add command jslint_plugin_vim.
      +
      +case "jslint_plugin_vim":
      +...
    • + +
    • +

      + 9. + function jslint_assert(condition, message) + +

      +
    • +
    • Description and source-code:
      function jslint_assert(condition, message) {
      +
      +// This function will throw <message> if <condition> is falsy.
      +
      +    if (condition) {
      +        return condition;
      +    }
      +    throw new Error(
      +        `This was caused by a bug in JSLint.
      +Please open an issue with this stack-trace (and possible example-code) at
      +https://github.com/jslint-org/jslint/issues.
      +edition = "${jslint_edition}";
      +${String(message).slice(0, 2000)}`
      +    );
      +}
    • +
    • Example usage:
      ...
      +// ["let aa={};", "whitage", "opener", "", 0]
      +
      +test_cause("opener");
      +
      +// Probably deadcode.
      +// case "${}":
      +
      +jslint_assert(
      +    !(left.id + right.id === "${}"),
      +    "Expected !(left.id + right.id === \"${}\")."
      +);
      +switch (left.id + right.id) {
      +case "()":
      +case "[]":
      +case "{}":
      +...
    • + +
    • +

      + 10. + function jslint_cli({ + console_error, + console_log, + file, + mode_cli, + mode_noop, + option, + process_argv, + process_env, + process_exit, + source +}) + +

      +
    • +
    • Description and source-code:
      async function jslint_cli({
      +    console_error,
      +    console_log,
      +    file,
      +    mode_cli,
      +    mode_noop,
      +    option,
      +    process_argv,
      +    process_env,
      +    process_exit,
      +    source
      +}) {
      +
      +// This function will run jslint from nodejs-cli.
      +
      +    let command;
      +    let data;
      +    let exit_code = 0;
      +    let mode_plugin_vim;
      +    let mode_report;
      +    let result;
      +
      +    function jslint_from_file({
      +        code,
      +        file,
      +        line_offset = 0,
      +        mode_conditional,
      +        option = empty()
      +    }) {
      +        let result_from_file;
      +        if (
      +            mode_conditional
      +            && !(
      +                /^\/\*jslint\b/m
      +            ).test(code.slice(0, 65536))
      +        ) {
      +            return;
      +        }
      +        option = Object.assign(empty(), option, {
      +            file
      +        });
      +        switch ((
      +            /\.\w+?$|$/m
      +        ).exec(file)[0]) {
      +        case ".html":
      +
      +// Recursively jslint embedded "<script>\n...\n</script>".
      +
      +            code.replace((
      +                /^<script\b[^>]*?>\n([\S\s]*?\n)<\/script>$/gm
      +            ), function (ignore, match1, ii) {
      +                jslint_from_file({
      +                    code: match1,
      +                    file: file + ".<script>.js",
      +                    line_offset: string_line_count(code.slice(0, ii)) + 1,
      +                    option: Object.assign(empty(), {
      +                        browser: true
      +                    }, option)
      +                });
      +                return "";
      +            });
      +            return;
      +        case ".md":
      +
      +// Recursively jslint embedded "node --eval '\n...\n'".
      +
      +            jslint_node_eval({
      +                code,
      +                file,
      +                mode_conditional: true,
      +                option
      +            });
      +            return;
      +        case ".sh":
      +
      +// Recursively jslint embedded "node --eval '\n...\n'".
      +
      +            jslint_node_eval({
      +                code,
      +                file,
      +                option
      +            });
      +            return;
      +        default:
      +            ...
      +}
      +
    • +
    • Example usage:
      ...
      +        }, undefined, 4)
      +    ]
      +].map(async function ([
      +    file, data
      +]) {
      +    await fsWriteFileWithParents(file, data);
      +}));
      +await jslint.jslint_cli({
      +    mode_cli: true,
      +    process_argv: [
      +        "node", "jslint.mjs",
      +        "v8_coverage_report=.tmp/coverage_misc"
      +        // "node", ".tmp/coverage_misc/aa.js"
      +    ]
      +});
      +...
    • + +
    • +

      + 11. + function jslint_phase1_split() + +

      +
    • +
    • Description and source-code:
      function jslint_phase1_split() {
      +
      +// PHASE 1. Split <source> by newlines into <line_list>.
      +
      +    return;
      +}
    • +
    • Example usage:
      ...
      +            warn,
      +            warn_at,
      +            warning_list
      +        });
      +
      +// PHASE 1. Split <source> by newlines into <line_list>.
      +
      +        jslint_phase1_split(state);
      +        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
      +        jslint_assert(
      +            function_stack.length === 0,
      +            `function_stack.length === 0.`
      +        );
      +
      +// PHASE 2. Lex <line_list> into <token_list>.
      +...
    • + +
    • +

      + 12. + function jslint_phase2_lex(state) + +

      +
    • +
    • Description and source-code:
      function jslint_phase2_lex(state) {
      +
      +// PHASE 2. Lex <line_list> into <token_list>.
      +
      +    let {
      +        artifact,
      +        directive_list,
      +        global_dict,
      +        global_list,
      +        line_list,
      +        option_dict,
      +        stop,
      +        stop_at,
      +        tenure,
      +        test_cause,
      +        token_global,
      +        token_list,
      +        warn,
      +        warn_at
      +    } = state;
      +    let char;                   // The current character being lexed.
      +    let column = 0;             // The column number of the next character.
      +    let from;                   // The starting column number of the token.
      +    let from_mega;              // The starting column of megastring.
      +    let line = 0;               // The line number of the next character.
      +    let line_disable;           // The starting line of "/*jslint-disable*/".
      +    let line_mega;              // The starting line of megastring.
      +    let line_source = "";       // The remaining line source string.
      +    let line_whole = "";        // The whole line source string.
      +    let mode_directive = true;  // true if directives are still allowed.
      +    let mode_mega = false;      // true if currently parsing a megastring
      +                                // ... literal.
      +    let mode_regexp;            // true if regular expression literal seen on
      +                                // ... this line.
      +    let rx_token = new RegExp(
      +        "^("
      +        + "(\\s+)"
      +        + "|([a-zA-Z_$][a-zA-Z0-9_$]*)"
      +        + "|[(){}\\[\\],:;'\"~\\`]"
      +        + "|\\?[?.]?"
      +        + "|=(?:==?|>)?"
      +        + "|\\.+"
      +        + "|\\*[*\\/=]?"
      +        + "|\\/[*\\/]?"
      +        + "|\\+[=+]?"
      +        + "|-[=\\-]?"
      +        + "|[\\^%]=?"
      +        + "|&[&=]?"
      +        + "|\\"
      +        + "|[|=]?"
      +        + "|>{1,3}=?"
      +        + "|<<?=?"
      +        + "|!(?:!|==?)?"
      +
      +// PR-351 - Add BigInt support.
      +
      +        + "|(0n?|[1-9][0-9]*n?)"
      +        + ")"
      +        + "(.*)$"
      +    );
      +    let snippet = "";           // A piece of string.
      +    let token_1;               ...
      +}
      +
    • +
    • Example usage:
      ...
      +        jslint_assert(
      +            function_stack.length === 0,
      +            `function_stack.length === 0.`
      +        );
      +
      +// PHASE 2. Lex <line_list> into <token_list>.
      +
      +        jslint_phase2_lex(state);
      +        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
      +        jslint_assert(
      +            function_stack.length === 0,
      +            `function_stack.length === 0.`
      +        );
      +
      +// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
      +...
    • + +
    • +

      + 13. + function jslint_phase3_parse(state) + +

      +
    • +
    • Description and source-code:
      function jslint_phase3_parse(state) {
      +
      +// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
      +
      +// Parsing:
      +
      +// Parsing weaves the tokens into an abstract syntax tree. During that process,
      +// a token may be given any of these properties:
      +
      +//      arity       string
      +//      label       identifier
      +//      name        identifier
      +//      expression  expressions
      +//      block       statements
      +//      else        statements (else, default, catch)
      +
      +// Specialized tokens may have additional properties.
      +
      +    let anon = "anonymous";     // The guessed name for anonymous functions.
      +    let {
      +        artifact,
      +        catch_list,
      +        catch_stack,
      +        export_dict,
      +        function_list,
      +        function_stack,
      +        global_dict,
      +        import_list,
      +        is_equal,
      +        option_dict,
      +        property_dict,
      +        stop,
      +        syntax_dict,
      +        tenure,
      +        test_cause,
      +        token_global,
      +        token_list,
      +        warn,
      +        warn_at
      +    } = state;
      +    let catchage = catch_stack[0];      // The current catch-block.
      +    let functionage = token_global;     // The current function.
      +    let mode_var;               // "var" if using var; "let" if using let.
      +    let rx_identifier = (
      +        /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/
      +    );
      +    let token_ii = 0;           // The number of the next token.
      +    let token_now = token_global;       // The current token being examined in
      +                                        // ... the parse.
      +    let token_nxt = token_global;       // The next token to be examined in
      +                                        // ... <token_list>.
      +
      +    function advance(id, match) {
      +
      +// Produce the next token.
      +
      +// Attempt to give helpful names to anonymous functions.
      +
      +        if (token_now.identifier && token_now.id !== "function") {
      +            anon = token_now.id;
      +        } else if (
      +            token_now.id === "(string)"
      +            && rx_identifier.test(token_now.value)
      +        ) {
      +            a...
      +}
      +
    • +
    • Example usage:
      ...
      +        jslint_assert(
      +            function_stack.length === 0,
      +            `function_stack.length === 0.`
      +        );
      +
      +// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
      +
      +        jslint_phase3_parse(state);
      +        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
      +        jslint_assert(
      +            function_stack.length === 0,
      +            `function_stack.length === 0.`
      +        );
      +
      +// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
      +...
    • + +
    • +

      + 14. + function jslint_phase4_walk(state) + +

      +
    • +
    • Description and source-code:
      function jslint_phase4_walk(state) {
      +
      +// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
      +//          recursive traversal. Each node may be processed on the way down
      +//          (preaction) and on the way up (postaction).
      +
      +    let {
      +        artifact,
      +        catch_stack,
      +        function_stack,
      +        global_dict,
      +        is_equal,
      +        is_weird,
      +        option_dict,
      +        syntax_dict,
      +        test_cause,
      +        token_global,
      +        warn
      +    } = state;
      +    let block_stack = [];               // The stack of blocks.
      +    let blockage = token_global;        // The current block.
      +    let catchage = catch_stack[0];      // The current catch-block.
      +    let functionage = token_global;     // The current function.
      +    let postaction;
      +    let postamble;
      +    let posts = empty();
      +    let preaction;
      +    let preamble;
      +    let pres = empty();
      +
      +// The relational operators.
      +
      +    let relationop = object_assign_from_list(empty(), [
      +        "!=", "!==", "<", "<=", "==", "===", ">", ">="
      +    ], true);
      +
      +// Ambulation of the parse tree.
      +
      +    function action(when) {
      +
      +// Produce a function that will register task functions that will be called as
      +// the tree is traversed.
      +
      +        return function (arity, id, task) {
      +            let a_set = when[arity];
      +            let i_set;
      +
      +// The id parameter is optional. If excluded, the task will be applied to all
      +// ids.
      +
      +            if (typeof id !== "string") {
      +                task = id;
      +                id = "(all)";
      +            }
      +
      +// If this arity has no registrations yet, then create a set object to hold
      +// them.
      +
      +            if (a_set === undefined) {
      +                a_set = empty();
      +                when[arity] = a_set;
      +            }
      +
      +// If this id has no registrations yet, then create a set array to hold them.
      +
      +            i_set = a_set[id];
      +            if (i_set === undefined) {
      +                i_set = [];
      +                a_set[id] = i_set;
      +            }
      +
      +// Register the task with the arity and the id.
      +...
      +}
      +
    • +
    • Example usage:
      ...
      +);
      +
      +// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
      +//          recursive traversal. Each node may be processed on the way down
      +//          (preaction) and on the way up (postaction).
      +
      +if (!state.mode_json) {
      +    jslint_phase4_walk(state);
      +}
      +jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
      +jslint_assert(
      +    function_stack.length === 0,
      +    `function_stack.length === 0.`
      +);
      +...
    • + +
    • +

      + 15. + function jslint_phase5_whitage(state) + +

      +
    • +
    • Description and source-code:
      function jslint_phase5_whitage(state) {
      +
      +// PHASE 5. Check whitespace between tokens in <token_list>.
      +
      +    let {
      +        artifact,
      +        catch_list,
      +        function_list,
      +        function_stack,
      +        option_dict,
      +        test_cause,
      +        token_global,
      +        token_list,
      +        warn
      +    } = state;
      +    let closer = "(end)";
      +    let free = false;
      +
      +// free = false
      +
      +// cause:
      +// "()=>0"
      +// "aa()"
      +// "aa(0,0)"
      +// "function(){}"
      +
      +// free = true
      +
      +// cause:
      +// "(0)"
      +// "(aa)"
      +// "aa(0)"
      +// "do{}while()"
      +// "for(){}"
      +// "if(){}"
      +// "switch(){}"
      +// "while(){}"
      +
      +    let left = token_global;
      +    let margin = 0;
      +    let mode_indent = (
      +
      +// PR-330 - Allow 2-space indent.
      +
      +        option_dict.indent2
      +        ? 2
      +        : 4
      +    );
      +    let nr_comments_skipped = 0;
      +    let open = true;
      +    let opening = true;
      +    let right;
      +
      +// This is the set of infix operators that require a space on each side.
      +
      +    let spaceop = object_assign_from_list(empty(), [
      +        "!=", "!==", "%", "%=", "&", "&&", "&=", "*", "*=", "+=", "-=", "/",
      +        "/=", "<", "<<", "<<=", "<=", "=", "==", "===", "=>", ">", ">=", ">>",
      +        ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||"
      +    ], true);
      +
      +    function at_margin(fit) {
      +        const at = margin + fit;
      +        if (right.from !== at) {
      +            return expected_at(at);
      +        }
      +    }
      +
      +    function delve(the_function) {
      +        Object.keys(the_function.context).forEach(function (id) {
      +            const name = the_function.context[id];
      +            if (id !== "ignore" && name.parent === the_function) {
      +
      +// test_cause:
      +// ["function aa(aa) {return aa;}", "delve", "id", "", 0]
      +
      +                test_cause("id");
      +                if (
      +                    name.used === 0
      +
      +// Probably deadcode.
      +// && (
      +//     name.role !== "function"
      +//     || name.parent.arity !== "unary"
      +// )
      +
      +                    && jslint_assert(
      +                        name.role...
      +}
      +
    • +
    • Example usage:
      ...
      +    function_stack.length === 0,
      +    `function_stack.length === 0.`
      +);
      +
      +// PHASE 5. Check whitespace between tokens in <token_list>.
      +
      +if (!state.mode_json && warning_list.length === 0) {
      +    jslint_phase5_whitage(state);
      +}
      +jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
      +jslint_assert(
      +    function_stack.length === 0,
      +    `function_stack.length === 0.`
      +);
      +...
    • + +
    • +

      + 16. + function jslint_report({ + exports, + froms, + functions, + global, + json, + module, + property, + stop, + warnings +}) + +

      +
    • +
    • Description and source-code:
      function jslint_report({
      +    exports,
      +    froms,
      +    functions,
      +    global,
      +    json,
      +    module,
      +    property,
      +    stop,
      +    warnings
      +}) {
      +
      +// This function will create human-readable, html-report
      +// for warnings, properties, and functions from jslint-result-object.
      +// Example usage:
      +//  let result = jslint("console.log('hello world')");
      +//  let html = jslint_report(result);
      +
      +    let html = "";
      +    let length_80 = 1111;
      +
      +    function address(line = 1, column = 1) {
      +
      +// This function will create HTML address element from <line> and <column>
      +
      +        return `<address>${Number(line)}: ${Number(column)}</address>`;
      +
      +    }
      +
      +    function detail(title, list) {
      +        return (
      +            (Array.isArray(list) && list.length > 0)
      +            ? (
      +
      +// Google Lighthouse Accessibility - <dl>'s do not contain only properly-ordered
      +// <dt> and <dd> groups, <script>, <template> or <div> elements.
      +
      +                "<dl>"
      +                + "<dt>" + htmlEscape(title) + "</dt>"
      +                + "<dd>" + list.join(", ") + "</dd>"
      +                + "</dl>"
      +            )
      +            : ""
      +        );
      +    }
      +
      +    html += "<div class=\"JSLINT_\" id=\"JSLINT_REPORT_HTML\">\n";
      +    html += String(`
      +<style class="JSLINT_REPORT_STYLE">
      +/* jslint utility2:true */
      +/*csslint box-model: false, ids:false */
      +/*csslint ignore:start*/
      +@font-face {
      +    font-display: swap;
      +    font-family: "Daley";
      +    src: url(
      +"data:font/woff2;base64,d09GMgABAAAAABy4AA4AAAAAThwAABxiAAEAAAAAAAAAAAAA\
      +AAAAAAAAAAAAAAAABmAAgiQINAmcDBEICuc41DEBNgIkA4R2C4I+AAQgBYkuByAMgScfYUIF\
      +7NgjsHGAbcDVFkXZ5Jwd+P96IGPc9rl9ETBEaCzCJkvY2UpziRZ7zftZWk8052U9+NqX6vXL\
      +KDflSQnlJ0bP+QnPQAy744n9mup6H9PaCDFwM5zjf8exB89bZ1cdrYOP0NgnuRDRWlk9u/fE\
      +llkxqmfH8lmRQ/DAmER9opk9wR6suc1LvTiXNEe1vbhUCH2USgnEwH3vUm05JQqejGvZvOtz\
      +7sIKEGgLdDNl/IrfqWVZG/wr42ekomEm91VA1p4LhHBuFzHF8//u7vvbREHMQqGtNLmiOOD/\
      +X7WWiwqyCE98qt0jk5JJmgR5WJJElBmzRb1F7a66MmSLT...
      +}
      +
    • +
    • Example usage:
      ...
      +    let report;
      +    let result;
      +    let source = "function foo() {console.log(\u0027hello world\u0027);}\n";
      +
      +// Create JSLint report from <source> in javascript.
      +
      +    result = jslint.jslint(source);
      +    report = jslint.jslint_report(result);
      +
      +    await fs.promises.mkdir(".artifact/", {recursive: true});
      +    await fs.promises.writeFile(".artifact/jslint_report_hello.html", report);
      +    console.error("wrote file .artifact/jslint_report_hello.html");
      +}());
      +
      +'
      +...
    • + +
    • +

      + 17. + function jstestDescribe(description, testFunction) + +

      +
    • +
    • Description and source-code:
      async function jstestDescribe(description, testFunction) {
      +
      +// This function will create-and-run test-group <testFunction>
      +// with given <description>.
      +
      +    let message;
      +    let result;
      +
      +// Init jstestTimeStart.
      +
      +    if (jstestTimeStart === undefined) {
      +        jstestTimeStart = jstestTimeStart || Date.now();
      +        process.on("exit", jstestOnExit);
      +    }
      +
      +// Init jstestItList.
      +
      +    jstestItList = [];
      +    testFunction();
      +
      +// Wait for jstestItList to resolve.
      +
      +    result = await Promise.all(jstestItList);
      +
      +// Print test results.
      +
      +    message = (
      +        "\n  " + (Date.now() - jstestTimeStart) + "ms"
      +        + " - test describe - " + description + "\n"
      +        + result.map(function ([
      +            err, description, mode
      +        ]) {
      +            jstestItCount += 1;
      +            if (err) {
      +                jstestCountFailed += 1;
      +                err = (
      +                    "    \u001b[31m\u2718 " + jstestItCount + ". test it - "
      +                    + description + "\n" + err.stack + "\u001b[39m"
      +                );
      +                if (mode === "pass") {
      +                    jstestCountFailed -= 1;
      +                    err = "";
      +                }
      +            }
      +            return err || (
      +                "    \u001b[32m\u2714 " + jstestItCount + ". test it - "
      +                + description + "\u001b[39m"
      +            );
      +        }).join("\n")
      +    );
      +    console.error(message);
      +}
    • +
    • Example usage:
      ...
      +//     JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n"
      +// );
      +
      +        assertJsonEqual(data1, data2);
      +    });
      +});
      +
      +jstestDescribe((
      +    "test v8CoverageReportCreate's handling-behavior"
      +), function () {
      +    jstestIt((
      +        "test null-case handling-behavior"
      +    ), async function () {
      +        await assertErrorThrownAsync(function () {
      +            return v8CoverageReportCreate({});
      +...
    • + +
    • +

      + 18. + function jstestIt(description, testFunction, mode) + +

      +
    • +
    • Description and source-code:
      function jstestIt(description, testFunction, mode) {
      +
      +// This function will create-and-run test-case <testFunction>
      +// inside current test-group with given <description>.
      +
      +    jstestCountTotal += 1;
      +    jstestItList.push(new Promise(async function (resolve) {
      +        let err;
      +        try {
      +            await testFunction();
      +        } catch (errCaught) {
      +            err = errCaught;
      +        }
      +        resolve([
      +            err, description, mode
      +        ]);
      +    }));
      +}
    • +
    • Example usage:
      ...
      +        process_argv: [
      +            "node", "jslint.mjs",
      +            "v8_coverage_report=.tmp/coverage_npm",
      +            "npm", "--version"
      +        ]
      +    });
      +});
      +jstestIt((
      +    "test misc handling-behavior"
      +), async function () {
      +    await Promise.all([
      +        [
      +            ".tmp/coverage_misc/aa.js", "\n".repeat(0x100)
      +        ], [
      +            ".tmp/coverage_misc/coverage-0-0-0.json", JSON.stringify({
      +...
    • + +
    • +

      + 19. + function jstestOnExit(exitCode, processExit, countFailed) + +

      +
    • +
    • Description and source-code:
      function jstestOnExit(exitCode, processExit, countFailed) {
      +
      +// This function will on process-exit, print test-report
      +// and exit with non-zero exit-code if any test failed.
      +
      +    let message = (
      +        (
      +            (jstestCountFailed || countFailed)
      +            ? "\n\u001b[31m"
      +            : "\n\u001b[32m"
      +        )
      +        + "  tests total  - " + jstestCountTotal + "\n"
      +        + "  tests failed - " + jstestCountFailed + "\n"
      +        + "\u001b[39m"
      +    );
      +    if (!processExit) {
      +        console.error(message);
      +        processExit = process.exit;
      +    }
      +    processExit(exitCode || jstestCountFailed);
      +    return message;
      +}
    • +
    • Example usage:
      ...
      +        "test jstestDescribe's error handling-behavior"
      +    ), function () {
      +        throw new Error();
      +    }, "pass");
      +    jstestIt((
      +        "test jstestOnExit's error handling-behavior"
      +    ), function () {
      +        jstestOnExit(undefined, noop, 1);
      +    });
      +});
      +
      +jstestDescribe((
      +    "test v8CoverageListMerge's handling-behavior"
      +), function () {
      +    let functionsInput = JSON.stringify([
      +...
    • + +
    • +

      + 20. + function moduleFsInit() + +

      +
    • +
    • Description and source-code:
      async function moduleFsInit() {
      +
      +// This function will import nodejs builtin-modules if they have not yet been
      +// imported.
      +
      +// State 3 - Modules already imported.
      +
      +    if (moduleFs !== undefined) {
      +        return;
      +    }
      +
      +// State 2 - Wait while modules are importing.
      +
      +    if (moduleFsInitResolveList !== undefined) {
      +        return new Promise(function (resolve) {
      +            moduleFsInitResolveList.push(resolve);
      +        });
      +    }
      +
      +// State 1 - Start importing modules.
      +
      +    moduleFsInitResolveList = [];
      +    [
      +        moduleChildProcess,
      +        moduleFs,
      +        modulePath,
      +        moduleUrl
      +    ] = await Promise.all([
      +        import("child_process"),
      +        import("fs"),
      +        import("path"),
      +        import("url")
      +    ]);
      +    while (moduleFsInitResolveList.length > 0) {
      +        moduleFsInitResolveList.shift()();
      +    }
      +}
    • +
    • Example usage:
      ...
      +}
      +function processExit1(exitCode) {
      +    assertOrThrow(exitCode === 1, exitCode);
      +}
      +
      +// Coverage-hack - Ugly-hack to get test-coverage for all initialization-states.
      +
      +moduleFsInit();
      +moduleFsInit();
      +
      +// Cleanup directory .tmp
      +// await moduleFs.promises.rm(".tmp", {
      +//     recursive: true
      +// }).catch(noop);
      +//
      +...
    • + +
    • +

      + 21. + function noop(val) + +

      +
    • +
    • Description and source-code:
      function noop(val) {
      +
      +// This function will do nothing except return <val>.
      +
      +    return val;
      +}
    • +
    • Example usage:
      ...
      +}());
      +
      +(async function testcaseMisc() {
      +
      +// This function will test misc handling-behavior.
      +
      +// test debugInline's handling-behavior
      +noop(debugInline);
      +// test assertErrorThrownAsync's error handling-behavior
      +await assertErrorThrownAsync(function () {
      +    return assertErrorThrownAsync(noop);
      +});
      +// test assertJsonEqual's error handling-behavior
      +await assertErrorThrownAsync(function () {
      +    assertJsonEqual(1, 2);
      +...
    • + +
    • +

      + 22. + function objectDeepCopyWithKeysSorted(obj) + +

      +
    • +
    • Description and source-code:
      function objectDeepCopyWithKeysSorted(obj) {
      +
      +// This function will recursively deep-copy <obj> with keys sorted.
      +
      +    let sorted;
      +    if (typeof obj !== "object" || !obj) {
      +        return obj;
      +    }
      +
      +// Recursively deep-copy list with child-keys sorted.
      +
      +    if (Array.isArray(obj)) {
      +        return obj.map(objectDeepCopyWithKeysSorted);
      +    }
      +
      +// Recursively deep-copy obj with keys sorted.
      +
      +    sorted = {};
      +    Object.keys(obj).sort().forEach(function (key) {
      +        sorted[key] = objectDeepCopyWithKeysSorted(obj[key]);
      +    });
      +    return sorted;
      +}
    • +
    • Example usage:
      ...
      +        ];
      +        data1 = v8CoverageListMerge(data1);
      +        data1 = v8CoverageListMerge([data1]);
      +
      +// Debug data1.
      +// await moduleFs.promises.writeFile(
      +//     ".test_v8_coverage_node_sqlite_merged.json",
      +//     JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n"
      +// );
      +
      +        assertJsonEqual(data1, data2);
      +    });
      +});
      +
      +jstestDescribe((
      +...
    • + +
    • +

      + 23. + function v8CoverageListMerge(processCovs) + +

      +
    • +
    • Description and source-code:
      function v8CoverageListMerge(processCovs) {
      +
      +// This function is derived from MIT Licensed v8-coverage at
      +// https://github.com/demurgos/v8-coverage/tree/master/ts
      +// https://github.com/demurgos/v8-coverage/blob/master/ts/LICENSE.md
      +//
      +// Merges a list of v8 process coverages.
      +// The result is normalized.
      +// The input values may be mutated, it is not safe to use them after passing
      +// them to this function.
      +// The computation is synchronous.
      +// @param processCovs Process coverages to merge.
      +// @return Merged process coverage.
      +
      +    let resultMerged = [];      // List of merged scripts from processCovs.
      +    let urlToScriptDict = new Map();    // Map scriptCov.url to scriptCovs.
      +
      +    function compareRangeList(aa, bb) {
      +
      +// Compares two range coverages.
      +// The ranges are first ordered by ascending `startOffset` and then by
      +// descending `endOffset`.
      +// This corresponds to a pre-order tree traversal.
      +
      +        if (aa.startOffset !== bb.startOffset) {
      +            return aa.startOffset - bb.startOffset;
      +        }
      +        return bb.endOffset - aa.endOffset;
      +    }
      +
      +    function dictKeyValueAppend(dict, key, val) {
      +
      +// This function will append <val> to list <dict>[<key>].
      +
      +        let list = dict.get(key);
      +        if (list === undefined) {
      +            list = [];
      +            dict.set(key, list);
      +        }
      +        list.push(val);
      +    }
      +
      +    function mergeTreeList(parentTrees) {
      +
      +// This function will return RangeTree object with <parentTrees> merged into
      +// property-children.
      +// @precondition Same `start` and `end` for all the parentTrees
      +
      +        if (parentTrees.length <= 1) {
      +            return parentTrees[0];
      +        }
      +
      +// new RangeTree().
      +
      +        return {
      +
      +// Merge parentTrees into property-children.
      +
      +            children: mergeTreeListToChildren(parentTrees),
      +            delta: parentTrees.reduce(function (aa, bb) {
      +                return aa + bb.delta;
      +            }, 0),
      +            end: parentTrees[0].end,
      +            start: parentTrees[0].start
      +   ...
      +}
      +
    • +
    • Example usage:
      ...
      +                "test_v8_coverage_node_sqlite_13216_1633662333140_0.json"
      +            ].map(function (file) {
      +                return testCoverageMergeData[file];
      +            });
      +            let data2 = testCoverageMergeData[
      +                "test_v8_coverage_node_sqlite_merged.json"
      +            ];
      +            data1 = v8CoverageListMerge(data1);
      +            data1 = v8CoverageListMerge([data1]);
      +
      +// Debug data1.
      +// await moduleFs.promises.writeFile(
      +//     ".test_v8_coverage_node_sqlite_merged.json",
      +//     JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n"
      +// );
      +...
    • + +
    • +

      + 24. + function v8CoverageReportCreate({ + consoleError, + coverageDir, + processArgv = [] +}) + +

      +
    • +
    • Description and source-code:
      async function v8CoverageReportCreate({
      +    consoleError,
      +    coverageDir,
      +    processArgv = []
      +}) {
      +
      +// This function will create html-coverage-reports directly from
      +// v8-coverage-files in <coverageDir>.
      +// 1. Spawn node.js program <processArgv> with NODE_V8_COVERAGE.
      +// 2. Merge JSON v8-coverage-files in <coverageDir>.
      +// 3. Create html-coverage-reports in <coverageDir>.
      +
      +    let cwd;
      +    let exitCode = 0;
      +    let fileDict;
      +    let fileExcludeList = [];
      +    let fileIncludeList = [];
      +    let fileIncludeNodeModules;
      +    let processArgElem;
      +    let promiseList = [];
      +    let v8CoverageObj;
      +
      +    function htmlRender({
      +        fileList,
      +        lineList,
      +        modeIndex,
      +        pathname
      +    }) {
      +        let html;
      +        let padLines;
      +        let padPathname;
      +        let txt;
      +        let txtBorder;
      +        html = "";
      +        html += String(`
      +<!DOCTYPE html>
      +<html lang="en">
      +<head>
      +<title>V8 Coverage Report</title>
      +<style>
      +/* jslint utility2:true */
      +/*csslint ignore:start*/
      +* {
      +box-sizing: border-box;
      +    font-family: consolas, menlo, monospace;
      +}
      +/*csslint ignore:end*/
      +
      +/* css - coverage_report - general */
      +body {
      +    margin: 0;
      +}
      +.coverage pre {
      +    margin: 5px 0;
      +}
      +.coverage table {
      +    border-collapse: collapse;
      +}
      +.coverage td,
      +.coverage th {
      +    border: 1px solid #777;
      +    line-height: 20px;
      +    margin: 0;
      +    padding: 5px 10px;
      +}
      +.coverage td span {
      +    display: inline-block;
      +    width: 100%;
      +}
      +.coverage .content {
      +    padding: 0 5px;
      +}
      +.coverage .content a {
      +    text-decoration: none;
      +}
      +.coverage .count {
      +    margin: 0 5px;
      +    padding: 0 5px;
      +}
      +.coverage .footer,
      +.coverage .header {
      +    padding: 20px;
      +}
      +.coverage .footer {
      +    text-align: center;
      +}
      +.coverage .percentbar {
      +    height: 12px;
      +    margin: 2px 0;
      +    min-width: 200px;
      +    position: relative;
      +    width: 100%;
      +}
      +.coverage .percentbar div {
      +    height: 100%;
      +    position: absolute;
      +}
      +.coverage .title {
      +    font-size: large;
      +    font-weight: bold;
      +...
      +}
      +
    • +
    • Example usage:
      ...
      +
      +/*jslint node*/
      +import jslint from "../jslint.mjs";
      +(async function () {
      +
      +// Create V8 coverage report from program `npm run test` in javascript.
      +
      +await jslint.v8CoverageReportCreate({
      +    coverageDir: "../.artifact/coverage_sqlite3_js/",
      +    processArgv: [
      +        "--include=lib/sqlite3-binding.js,lib/sqlite3.js",
      +        "--include=lib/trace.js",
      +        "npm", "run", "test"
      +    ]
      +});
      +...
    • + +
    • +

      + 25. + string jslint_charset_ascii + +

      +
    • + +
    • +

      + 26. + string jslint_edition + +

      +
    • + +
    +
    + +
    + [ + This document was created with + JSLint + ] +
    +
    + + diff --git a/branch-sandbox/.artifact/asset_font_daley_bold.woff2 b/branch-sandbox/.artifact/asset_font_daley_bold.woff2 new file mode 100644 index 000000000..783bdd697 Binary files /dev/null and b/branch-sandbox/.artifact/asset_font_daley_bold.woff2 differ diff --git a/branch-sandbox/.artifact/asset_image_logo_128.png b/branch-sandbox/.artifact/asset_image_logo_128.png new file mode 100644 index 000000000..2f18dd491 Binary files /dev/null and b/branch-sandbox/.artifact/asset_image_logo_128.png differ diff --git a/branch-sandbox/.artifact/asset_image_logo_256.png b/branch-sandbox/.artifact/asset_image_logo_256.png new file mode 100644 index 000000000..dce8b640a Binary files /dev/null and b/branch-sandbox/.artifact/asset_image_logo_256.png differ diff --git a/branch-sandbox/.artifact/asset_image_logo_32.png b/branch-sandbox/.artifact/asset_image_logo_32.png new file mode 100644 index 000000000..03e2a386f Binary files /dev/null and b/branch-sandbox/.artifact/asset_image_logo_32.png differ diff --git a/branch-sandbox/.artifact/asset_image_logo_512.html b/branch-sandbox/.artifact/asset_image_logo_512.html new file mode 100644 index 000000000..5d3a54040 --- /dev/null +++ b/branch-sandbox/.artifact/asset_image_logo_512.html @@ -0,0 +1,63 @@ + + + + +logo + + + +
    +
    JS
    +
    Lint
    +
    + + + diff --git a/branch-sandbox/.artifact/asset_image_logo_512.png b/branch-sandbox/.artifact/asset_image_logo_512.png new file mode 100644 index 000000000..832b8cbf5 Binary files /dev/null and b/branch-sandbox/.artifact/asset_image_logo_512.png differ diff --git a/branch-sandbox/.artifact/asset_image_logo_64.png b/branch-sandbox/.artifact/asset_image_logo_64.png new file mode 100644 index 000000000..237fde8e0 Binary files /dev/null and b/branch-sandbox/.artifact/asset_image_logo_64.png differ diff --git a/branch-sandbox/.artifact/coverage/coverage_badge.svg b/branch-sandbox/.artifact/coverage/coverage_badge.svg new file mode 100644 index 000000000..5e137a1ce --- /dev/null +++ b/branch-sandbox/.artifact/coverage/coverage_badge.svg @@ -0,0 +1,14 @@ + + + + +coverage +100.00 % + + diff --git a/branch-sandbox/.artifact/coverage/coverage_report.txt b/branch-sandbox/.artifact/coverage/coverage_report.txt new file mode 100644 index 000000000..f0ee189cd --- /dev/null +++ b/branch-sandbox/.artifact/coverage/coverage_report.txt @@ -0,0 +1,16 @@ +V8 Coverage Report ++----------------------------------+-------------------+ +| Files covered | Lines | ++----------------------------------+-------------------+ +| ./ | 100.00 % | +| ******************************** | 12088 / 12088 | ++----------------------------------+-------------------+ +| ./jslint.cjs | 100.00 % | +| ******************************** | 15 / 15 | ++----------------------------------+-------------------+ +| ./jslint.mjs | 100.00 % | +| ******************************** | 11005 / 11005 | ++----------------------------------+-------------------+ +| ./test.mjs | 100.00 % | +| ******************************** | 1068 / 1068 | ++----------------------------------+-------------------+ diff --git a/branch-sandbox/.artifact/coverage/index.html b/branch-sandbox/.artifact/coverage/index.html new file mode 100644 index 000000000..71889b791 --- /dev/null +++ b/branch-sandbox/.artifact/coverage/index.html @@ -0,0 +1,202 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . /
    +
    +
    +
    +
    + 100.00 %
    + 12088 / 12088 +
    +
    + 0 / 12088 +
    + . / jslint.cjs
    +
    +
    +
    +
    + 100.00 %
    + 15 / 15 +
    +
    + 0 / 15 +
    + . / jslint.mjs
    +
    +
    +
    +
    + 100.00 %
    + 11005 / 11005 +
    +
    + 0 / 11005 +
    + . / test.mjs
    +
    +
    +
    +
    + 100.00 %
    + 1068 / 1068 +
    +
    + 0 / 1068 +
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage/jslint.cjs.html b/branch-sandbox/.artifact/coverage/jslint.cjs.html new file mode 100644 index 000000000..b34b950f7 --- /dev/null +++ b/branch-sandbox/.artifact/coverage/jslint.cjs.html @@ -0,0 +1,173 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / jslint.cjs
    +
    +
    +
    +
    + 100.00 %
    + 15 / 15 +
    +
    + 0 / 15 +
    +
    + + +
    +
        1.      1/*jslint beta, node*/
    +
        2.      1/*property
    +
        3.      1    readFileSync, replace, runInNewContext
    +
        4.      1*/
    +
        5.      1require("vm").runInNewContext(
    +
        6.      1    require("fs").readFileSync(__dirname + "/jslint.mjs", "utf8").replace(
    +
        7.      1        "\nexport default Object.freeze(jslint_export);",
    +
        8.      1        "\nexports = jslint_export;"
    +
        9.      1    ).replace(
    +
       10.      1        "\njslint_import_meta_url = import.meta.url;",
    +
       11.      1        "\n// jslint_import_meta_url = import.meta.url;"
    +
       12.      1    ),
    +
       13.      1    module
    +
       14.      1);
    +
       15.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage/jslint.mjs.html b/branch-sandbox/.artifact/coverage/jslint.mjs.html new file mode 100644 index 000000000..88b66d68b --- /dev/null +++ b/branch-sandbox/.artifact/coverage/jslint.mjs.html @@ -0,0 +1,11163 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / jslint.mjs
    +
    +
    +
    +
    + 100.00 %
    + 11005 / 11005 +
    +
    + 0 / 11005 +
    +
    + + +
    +
        1.      1// #!/usr/bin/env node
    +
        2.      1// JSLint
    +
        3.      1// Original Author: Douglas Crockford (https://www.jslint.com).
    +
        4.      1
    +
        5.      1// This is free and unencumbered software released into the public domain.
    +
        6.      1
    +
        7.      1// Anyone is free to copy, modify, publish, use, compile, sell, or
    +
        8.      1// distribute this software, either in source code form or as a compiled
    +
        9.      1// binary, for any purpose, commercial or non-commercial, and by any
    +
       10.      1// means.
    +
       11.      1
    +
       12.      1// In jurisdictions that recognize copyright laws, the author or authors
    +
       13.      1// of this software dedicate any and all copyright interest in the
    +
       14.      1// software to the public domain. We make this dedication for the benefit
    +
       15.      1// of the public at large and to the detriment of our heirs and
    +
       16.      1// successors. We intend this dedication to be an overt act of
    +
       17.      1// relinquishment in perpetuity of all present and future rights to this
    +
       18.      1// software under copyright law.
    +
       19.      1
    +
       20.      1// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    +
       21.      1// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    +
       22.      1// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    +
       23.      1// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
    +
       24.      1// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
    +
       25.      1// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    +
       26.      1// OTHER DEALINGS IN THE SOFTWARE.
    +
       27.      1
    +
       28.      1// For more information, please refer to <https://unlicense.org/>
    +
       29.      1
    +
       30.      1
    +
       31.      1// jslint(source, option_dict, global_list) is a function that takes 3
    +
       32.      1// arguments. The second two arguments are optional.
    +
       33.      1
    +
       34.      1//      source          A text to analyze.
    +
       35.      1//      option_dict     An object whose keys correspond to option names.
    +
       36.      1//      global_list     An array of strings containing global variables that
    +
       37.      1//                      the file is allowed readonly access.
    +
       38.      1
    +
       39.      1// jslint returns an object containing its results. The object contains a lot
    +
       40.      1// of valuable information. It can be used to generate reports. The object
    +
       41.      1// contains:
    +
       42.      1
    +
       43.      1//      directives: an array of directive comment tokens.
    +
       44.      1//      edition: the version of JSLint that did the analysis.
    +
       45.      1//      exports: the names exported from the module.
    +
       46.      1//      froms: an array of strings representing each of the imports.
    +
       47.      1//      functions: an array of objects that represent all functions
    +
       48.      1//              declared in the file.
    +
       49.      1//      global: an object representing the global object. Its .context property
    +
       50.      1//              is an object containing a property for each global variable.
    +
       51.      1//      id: "(JSLint)"
    +
       52.      1//      json: true if the file is a JSON text.
    +
       53.      1//      lines: an array of strings, the source.
    +
       54.      1//      module: true if an import or export statement was used.
    +
       55.      1//      ok: true if no warnings were generated. This is what you want.
    +
       56.      1//      option: the option argument.
    +
       57.      1//      property: a property object.
    +
       58.      1//      stop: true if JSLint was unable to finish. You don't want this.
    +
       59.      1//      tokens: an array of objects representing the tokens in the file.
    +
       60.      1//      tree: the token objects arranged in a tree.
    +
       61.      1//      warnings: an array of warning objects. A warning object can contain:
    +
       62.      1//          name: "JSLintError"
    +
       63.      1//          column: A column number in the file.
    +
       64.      1//          line: A line number in the file.
    +
       65.      1//          code: A warning code string.
    +
       66.      1//          message: The warning message string.
    +
       67.      1//          a: Exhibit A.
    +
       68.      1//          b: Exhibit B.
    +
       69.      1//          c: Exhibit C.
    +
       70.      1//          d: Exhibit D.
    +
       71.      1
    +
       72.      1// jslint works in several phases. In any of these phases, errors might be
    +
       73.      1// found. Sometimes JSLint is able to recover from an error and continue
    +
       74.      1// parsing. In some cases, it cannot and will stop early. If that should happen,
    +
       75.      1// repair your code and try again.
    +
       76.      1
    +
       77.      1// Phases:
    +
       78.      1
    +
       79.      1// PHASE 1. Split <source> by newlines into <line_list>.
    +
       80.      1// PHASE 2. Lex <line_list> into <token_list>.
    +
       81.      1// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
    +
       82.      1// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
    +
       83.      1//          recursive traversal. Each node may be processed on the way down
    +
       84.      1//          (preaction) and on the way up (postaction).
    +
       85.      1// PHASE 5. Check whitespace between tokens in <token_list>.
    +
       86.      1
    +
       87.      1// jslint can also examine JSON text. It decides that a file is JSON text if
    +
       88.      1// the first token is "[" or "{". Processing of JSON text is much simpler than
    +
       89.      1// the processing of JavaScript programs. Only the first three phases are
    +
       90.      1// required.
    +
       91.      1
    +
       92.      1// WARNING: JSLint will hurt your feelings.
    +
       93.      1
    +
       94.      1/*jslint beta, node*/
    +
       95.      1
    +
       96.      1/*property
    +
       97.      1    mode_conditional,
    +
       98.      1    JSLINT_BETA, NODE_V8_COVERAGE, a, all, argv, arity, artifact,
    +
       99.      1    assertErrorThrownAsync, assertJsonEqual, assertOrThrow, assign, async, b,
    +
      100.      1    beta, bitwise, block, body, browser, c, calls, catch, catch_list,
    +
      101.      1    catch_stack, causes, char, children, clear, closer,
    +
      102.      1    closure, code, column, concat, consoleError, console_error, console_log,
    +
      103.      1    constant, context, convert, count, coverageDir, create, cwd, d, dead,
    +
      104.      1    debugInline, default, delta, devel, directive, directive_list,
    +
      105.      1    directive_quiet, directives, dirname, disrupt, dot, edition, elem_list,
    +
      106.      1    ellipsis, else, end, endOffset, endsWith, entries, env, error, eval, every,
    +
      107.      1    example_list, exec, execArgv, exit, export_dict, exports, expression, extra,
    +
      108.      1    file, fileList, fileURLToPath, filter, finally, flag, floor, for, forEach,
    +
      109.      1    formatted_message, free, freeze, from, froms,
    +
      110.      1    fsWriteFileWithParents, fud, functionName, function_list, function_stack,
    +
      111.      1    functions, get, getset, github_repo, global, global_dict, global_list,
    +
      112.      1    holeList, htmlEscape, id, identifier, import, import_list, inc, indent2,
    +
      113.      1    index, indexOf, init, initial, isArray, isBlockCoverage, isHole, isNaN,
    +
      114.      1    is_equal, is_weird, join, jslint, jslint_apidoc, jslint_assert,
    +
      115.      1    jslint_charset_ascii, jslint_cli, jslint_edition, jslint_phase1_split,
    +
      116.      1    jslint_phase2_lex, jslint_phase3_parse, jslint_phase4_walk,
    +
      117.      1    jslint_phase5_whitage, jslint_report, json, jstestDescribe, jstestIt,
    +
      118.      1    jstestOnExit, keys, label, lbp, led, length, level, line, lineList,
    +
      119.      1    line_list, line_offset, line_source, lines, linesCovered, linesTotal, live,
    +
      120.      1    log, long, loop, m, main, map, margin, match, max, message, meta, min,
    +
      121.      1    mkdir, modeCoverageIgnoreFile, modeIndex, mode_cli, mode_json, mode_module,
    +
      122.      1    mode_noop, mode_property, mode_shebang, mode_stop, module, moduleFsInit,
    +
      123.      1    moduleName, module_list, name, names, node, noop, now,
    +
      124.      1    nr, nud, objectDeepCopyWithKeysSorted, ok, on, open, opening, option,
    +
      125.      1    option_dict, order, package_name, padEnd, padStart, parameters, parent,
    +
      126.      1    parentIi, parse, pathname, platform, pop, processArgv, process_argv,
    +
      127.      1    process_env, process_exit, process_version, promises, property,
    +
      128.      1    property_dict, push, quote, ranges, readFile, readdir, readonly, recursive,
    +
      129.      1    reduce, repeat, replace, resolve, result, reverse, rm, rmdir, role, round,
    +
      130.      1    scriptId, search, set, shebang, shift, signature, single, slice, some, sort,
    +
      131.      1    source, spawn, splice, split, stack, stack_trace, start, startOffset,
    +
      132.      1    startsWith, statement, statement_prv, stdio, stop, stop_at, stringify,
    +
      133.      1    switch, syntax_dict, tenure, test, test_cause, test_internal_error, this,
    +
      134.      1    thru, toString, token, token_global, token_list, token_nxt, token_tree,
    +
      135.      1    tokens, trace, tree, trim, trimEnd, trimRight, try, type, unlink, unordered,
    +
      136.      1    unshift, url, used, v8CoverageListMerge, v8CoverageReportCreate, value,
    +
      137.      1    variable, version, versions, warn, warn_at, warning, warning_list, warnings,
    +
      138.      1    white, wrapped, writeFile
    +
      139.      1*/
    +
      140.      1
    +
      141.      1// init debugInline
    +
      142.      1let debugInline = (function () {
    +
      143.      3    let consoleError = function () {
    +
      144.      3        return;
    +
      145.      3    };
    +
      146.      1    function debug(...argv) {
    +
      147.      1
    +
      148.      1// This function will print <argv> to stderr and then return <argv>[0].
    +
      149.      1
    +
      150.      1        consoleError("\n\ndebugInline");
    +
      151.      1        consoleError(...argv);
    +
      152.      1        consoleError("\n");
    +
      153.      1        return argv[0];
    +
      154.      1    }
    +
      155.      1    debug(); // Coverage-hack.
    +
      156.      1    consoleError = console.error;
    +
      157.      1    return debug;
    +
      158.      1}());
    +
      159.      1let jslint_charset_ascii = (
    +
      160.      1    "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007"
    +
      161.      1    + "\b\t\n\u000b\f\r\u000e\u000f"
    +
      162.      1    + "\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017"
    +
      163.      1    + "\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f"
    +
      164.      1    + " !\"#$%&'()*+,-./0123456789:;<=>?"
    +
      165.      1    + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
    +
      166.      1    + "`abcdefghijklmnopqrstuvwxyz{|}~\u007f"
    +
      167.      1);
    +
      168.      1let jslint_edition = "v2021.12.20";
    +
      169.      1let jslint_export;                      // The jslint object to be exported.
    +
      170.      1let jslint_fudge = 1;                   // Fudge starting line and starting
    +
      171.      1                                        // ... column to 1.
    +
      172.      1let jslint_import_meta_url = "";        // import.meta.url used by cli.
    +
      173.      1let jstestCountFailed = 0;
    +
      174.      1let jstestCountTotal = 0;
    +
      175.      1let jstestItCount = 0;
    +
      176.      1let jstestItList = [];
    +
      177.      1let jstestTimeStart;
    +
      178.      1let moduleChildProcess;
    +
      179.      1let moduleFs;
    +
      180.      1let moduleFsInitResolveList;
    +
      181.      1let modulePath;
    +
      182.      1let moduleUrl;
    +
      183.      1
    +
      184.      8async function assertErrorThrownAsync(asyncFunc, regexp) {
    +
      185.      8
    +
      186.      8// This function will assert calling <asyncFunc> throws an error.
    +
      187.      8
    +
      188.      8    let err;
    +
      189.      8    try {
    +
      190.      1        await asyncFunc();
    +
      191.      7    } catch (errCaught) {
    +
      192.      7        err = errCaught;
    +
      193.      7    }
    +
      194.      8    assertOrThrow(err, "No error thrown.");
    +
      195.      8    assertOrThrow(
    +
      196.      1        regexp === undefined || new RegExp(regexp).test(err.message),
    +
      197.      8        err
    +
      198.      8    );
    +
      199.      8}
    +
      200.      1
    +
      201.     93function assertJsonEqual(aa, bb, message) {
    +
      202.     93
    +
      203.     93// This function will assert JSON.stringify(<aa>) === JSON.stringify(<bb>).
    +
      204.     93
    +
      205.     93    aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa), undefined, 1);
    +
      206.     93    bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb), undefined, 1);
    +
      207.      3    if (aa !== bb) {
    +
      208.      3        throw new Error(
    +
      209.      3            "\n" + aa + "\n!==\n" + bb
    +
      210.      3            + (
    +
      211.      3                typeof message === "string"
    +
      212.      3                ? " - " + message
    +
      213.      3                : message
    +
      214.      3                ? " - " + JSON.stringify(message)
    +
      215.      3                : ""
    +
      216.      3            )
    +
      217.      3        );
    +
      218.      3    }
    +
      219.     93}
    +
      220.      1
    +
      221.   1801function assertOrThrow(condition, message) {
    +
      222.   1801
    +
      223.   1801// This function will throw <message> if <condition> is falsy.
    +
      224.   1801
    +
      225.      4    if (!condition) {
    +
      226.      4        throw (
    +
      227.      4            (!message || typeof message === "string")
    +
      228.      4            ? new Error(String(message).slice(0, 2048))
    +
      229.      4            : message
    +
      230.      4        );
    +
      231.      4    }
    +
      232.   1801}
    +
      233.      1
    +
      234.  88957function empty() {
    +
      235.  88957
    +
      236.  88957// The empty function produces a new empty object that inherits nothing. This is
    +
      237.  88957// much better than '{}' because confusions around accidental method names like
    +
      238.  88957// 'constructor' are completely avoided.
    +
      239.  88957
    +
      240.  88957    return Object.create(null);
    +
      241.  88957}
    +
      242.      1
    +
      243.     58async function fsWriteFileWithParents(pathname, data) {
    +
      244.     58
    +
      245.     58// This function will write <data> to <pathname> and lazy-mkdirp if necessary.
    +
      246.     58
    +
      247.     58    await moduleFsInit();
    +
      248.     58
    +
      249.     58// Try writing to pathname.
    +
      250.     58
    +
      251.     58    try {
    +
      252.     45        await moduleFs.promises.writeFile(pathname, data);
    +
      253.     45    } catch (ignore) {
    +
      254.     13
    +
      255.     13// Lazy mkdirp.
    +
      256.     13
    +
      257.     13        await moduleFs.promises.mkdir(modulePath.dirname(pathname), {
    +
      258.     13            recursive: true
    +
      259.     13        });
    +
      260.     13
    +
      261.     13// Retry writing to pathname.
    +
      262.     13
    +
      263.     13        await moduleFs.promises.writeFile(pathname, data);
    +
      264.     13    }
    +
      265.     58    console.error("wrote file " + pathname);
    +
      266.     58}
    +
      267.      1
    +
      268.  13469function htmlEscape(str) {
    +
      269.  13469
    +
      270.  13469// This function will make <str> html-safe by escaping & < >.
    +
      271.  13469
    +
      272.  13469    return String(str).replace((
    +
      273.  13469        /&/g
    +
      274.  13469    ), "&amp;").replace((
    +
      275.  13469        /</g
    +
      276.  13469    ), "&lt;").replace((
    +
      277.  13469        />/g
    +
      278.  13469    ), "&gt;");
    +
      279.  13469}
    +
      280.      1
    +
      281.    634function jslint(
    +
      282.    634    source = "",                // A text to analyze.
    +
      283.    634    option_dict = empty(),      // An object whose keys correspond to option
    +
      284.    634                                // ... names.
    +
      285.    634    global_list = []            // An array of strings containing global
    +
      286.    634                                // ... variables that the file is allowed
    +
      287.    634                                // ... readonly access.
    +
      288.    634) {
    +
      289.    634
    +
      290.    634// The jslint function itself.
    +
      291.    634
    +
      292.    634    let catch_list = [];        // The array containing all catch-blocks.
    +
      293.    634    let catch_stack = [         // The stack of catch-blocks.
    +
      294.    634        {
    +
      295.    634            context: empty()
    +
      296.    634        }
    +
      297.    634    ];
    +
      298.    634    let cause_dict = empty();   // The object of test-causes.
    +
      299.    634    let directive_list = [];    // The directive comments.
    +
      300.    634    let export_dict = empty();  // The exported names and values.
    +
      301.    634    let function_list = [];     // The array containing all functions.
    +
      302.    634    let function_stack = [];    // The stack of functions.
    +
      303.    634    let global_dict = empty();  // The object containing the global
    +
      304.    634                                // ... declarations.
    +
      305.    634    let import_list = [];       // The array collecting all import-from strings.
    +
      306.    634    let line_list = String(     // The array containing source lines.
    +
      307.    634        "\n" + source
    +
      308.    634    ).split(
    +
      309.    634        // rx_crlf
    +
      310.    634        /\n|\r\n?/
    +
      311.  93065    ).map(function (line_source) {
    +
      312.  93065        return {
    +
      313.  93065            line_source
    +
      314.  93065        };
    +
      315.  93065    });
    +
      316.    634    let mode_stop = false;      // true if JSLint cannot finish.
    +
      317.    634    let property_dict = empty();        // The object containing the tallied
    +
      318.    634                                        // ... property names.
    +
      319.    634    let state = empty();        // jslint state-object to be passed between
    +
      320.    634                                // jslint functions.
    +
      321.    634    let syntax_dict = empty();  // The object containing the parser.
    +
      322.    634    let tenure = empty();       // The predefined property registry.
    +
      323.    634    let token_global = {        // The global object; the outermost context.
    +
      324.    634        async: 0,
    +
      325.    634        body: true,
    +
      326.    634        context: empty(),
    +
      327.    634        finally: 0,
    +
      328.    634        from: 0,
    +
      329.    634        id: "(global)",
    +
      330.    634        level: 0,
    +
      331.    634        line: jslint_fudge,
    +
      332.    634        live: [],
    +
      333.    634        loop: 0,
    +
      334.    634        switch: 0,
    +
      335.    634        thru: 0,
    +
      336.    634        try: 0
    +
      337.    634    };
    +
      338.    634    let token_list = [];        // The array of tokens.
    +
      339.    634    let warning_list = [];      // The array collecting all generated warnings.
    +
      340.    634
    +
      341.    634// Error reportage functions:
    +
      342.    634
    +
      343.   7458    function artifact(the_token) {
    +
      344.   7458
    +
      345.   7458// Return a string representing an artifact.
    +
      346.   7458
    +
      347.    252        the_token = the_token || state.token_nxt;
    +
      348.   7458        return (
    +
      349.   4965            (the_token.id === "(string)" || the_token.id === "(number)")
    +
      350.   2617            ? String(the_token.value)
    +
      351.   4841            : the_token.id
    +
      352.   7458        );
    +
      353.   7458    }
    +
      354.    634
    +
      355.  30481    function is_equal(aa, bb) {
    +
      356.  30481        let aa_value;
    +
      357.  30481        let bb_value;
    +
      358.  30481
    +
      359.  30481// test_cause:
    +
      360.  30481// ["0&&0", "is_equal", "", "", 0]
    +
      361.  30481
    +
      362.  30481        test_cause("");
    +
      363.  30481
    +
      364.  30481// Probably deadcode.
    +
      365.  30481// if (aa === bb) {
    +
      366.  30481//     return true;
    +
      367.  30481// }
    +
      368.  30481
    +
      369.  30481        jslint_assert(!(aa === bb), `Expected !(aa === bb).`);
    +
      370.      9        if (Array.isArray(aa)) {
    +
      371.      9            return (
    +
      372.      9                Array.isArray(bb)
    +
      373.      9                && aa.length === bb.length
    +
      374.      9                && aa.every(function (value, index) {
    +
      375.      9
    +
      376.      9// test_cause:
    +
      377.      9// ["`${0}`&&`${0}`", "is_equal", "recurse_isArray", "", 0]
    +
      378.      9
    +
      379.      9                    test_cause("recurse_isArray");
    +
      380.      9                    return is_equal(value, bb[index]);
    +
      381.      9                })
    +
      382.      9            );
    +
      383.  30472        }
    +
      384.  30472
    +
      385.  30472// Probably deadcode.
    +
      386.  30472// if (Array.isArray(bb)) {
    +
      387.  30472//     return false;
    +
      388.  30472// }
    +
      389.  30472
    +
      390.  30472        jslint_assert(!Array.isArray(bb), `Expected !Array.isArray(bb).`);
    +
      391.  30472        if (aa.id === "(number)" && bb.id === "(number)") {
    +
      392.     61            return aa.value === bb.value;
    +
      393.  30411        }
    +
      394.  30411        if (aa.id === "(string)") {
    +
      395.  22056            aa_value = aa.value;
    +
      396.  22056        } else if (aa.id === "`" && aa.constant) {
    +
      397.   8355            aa_value = aa.value[0];
    +
      398.  30411        }
    +
      399.  30411        if (bb.id === "(string)") {
    +
      400.  24532            bb_value = bb.value;
    +
      401.  24532        } else if (bb.id === "`" && bb.constant) {
    +
      402.   5879            bb_value = bb.value[0];
    +
      403.  30411        }
    +
      404.  30411        if (typeof aa_value === "string") {
    +
      405.  22056            return aa_value === bb_value;
    +
      406.  22056        }
    +
      407.   8355        if (is_weird(aa) || is_weird(bb)) {
    +
      408.     66
    +
      409.     66// test_cause:
    +
      410.     66// ["aa(/./)||{}", "is_equal", "false", "", 0]
    +
      411.     66
    +
      412.     66            test_cause("false");
    +
      413.     66            return false;
    +
      414.   8289        }
    +
      415.   8289        if (aa.arity === bb.arity && aa.id === bb.id) {
    +
      416.   2093            if (aa.id === ".") {
    +
      417.   2093
    +
      418.   2093// test_cause:
    +
      419.   2093// ["aa.bb&&aa.bb", "is_equal", "recurse_arity_id", "", 0]
    +
      420.   2093
    +
      421.   2093                test_cause("recurse_arity_id");
    +
      422.   2093                return (
    +
      423.   2093                    is_equal(aa.expression, bb.expression)
    +
      424.   2093                    && is_equal(aa.name, bb.name)
    +
      425.   2093                );
    +
      426.   2093            }
    +
      427.   2093            if (aa.arity === "unary") {
    +
      428.   2093
    +
      429.   2093// test_cause:
    +
      430.   2093// ["+0&&+0", "is_equal", "recurse_unary", "", 0]
    +
      431.   2093
    +
      432.   2093                test_cause("recurse_unary");
    +
      433.   2093                return is_equal(aa.expression, bb.expression);
    +
      434.   2093            }
    +
      435.   2093            if (aa.arity === "binary") {
    +
      436.   2093
    +
      437.   2093// test_cause:
    +
      438.   2093// ["aa[0]&&aa[0]", "is_equal", "recurse_binary", "", 0]
    +
      439.   2093
    +
      440.   2093                test_cause("recurse_binary");
    +
      441.   2093                return (
    +
      442.   2093                    aa.id !== "("
    +
      443.   2093                    && is_equal(aa.expression[0], bb.expression[0])
    +
      444.   2093                    && is_equal(aa.expression[1], bb.expression[1])
    +
      445.   2093                );
    +
      446.   2093            }
    +
      447.   2093            if (aa.arity === "ternary") {
    +
      448.   2093
    +
      449.   2093// test_cause:
    +
      450.   2093// ["aa=(``?``:``)&&(``?``:``)", "is_equal", "recurse_ternary", "", 0]
    +
      451.   2093
    +
      452.   2093                test_cause("recurse_ternary");
    +
      453.   2093                return (
    +
      454.   2093                    is_equal(aa.expression[0], bb.expression[0])
    +
      455.   2093                    && is_equal(aa.expression[1], bb.expression[1])
    +
      456.   2093                    && is_equal(aa.expression[2], bb.expression[2])
    +
      457.   2093                );
    +
      458.   2093            }
    +
      459.   2093
    +
      460.   2093// Probably deadcode.
    +
      461.   2093// if (aa.arity === "function" || aa.arity === "regexp") {
    +
      462.   2093//     return false;
    +
      463.   2093// }
    +
      464.   2093
    +
      465.   2093            jslint_assert(
    +
      466.   2093                !(aa.arity === "function" || aa.arity === "regexp"),
    +
      467.   2093                `Expected !(aa.arity === "function" || aa.arity === "regexp").`
    +
      468.   2093            );
    +
      469.   2093
    +
      470.   2093// test_cause:
    +
      471.   2093// ["undefined&&undefined", "is_equal", "true", "", 0]
    +
      472.   2093
    +
      473.   2093            test_cause("true");
    +
      474.   2093            return true;
    +
      475.   6196        }
    +
      476.   6196
    +
      477.   6196// test_cause:
    +
      478.   6196// ["null&&undefined", "is_equal", "false", "", 0]
    +
      479.   6196
    +
      480.   6196        test_cause("false");
    +
      481.   6196        return false;
    +
      482.   6196    }
    +
      483.    634
    +
      484.  28366    function is_weird(thing) {
    +
      485.  28366        switch (thing.id) {
    +
      486.     36        case "(regexp)":
    +
      487.     36            return true;
    +
      488.      1        case "=>":
    +
      489.      1            return true;
    +
      490.    560        case "[":
    +
      491.    560            return thing.arity === "unary";
    +
      492.     14        case "function":
    +
      493.     14            return true;
    +
      494.      7        case "{":
    +
      495.      7            return true;
    +
      496.  27748        default:
    +
      497.  27748            return false;
    +
      498.  28366        }
    +
      499.  28366    }
    +
      500.    634
    +
      501.    110    function stop(code, the_token, a, b, c, d) {
    +
      502.    110
    +
      503.    110// Similar to warn and stop_at. If the token already had a warning, that
    +
      504.    110// warning will be replaced with this new one. It is likely that the stopping
    +
      505.    110// warning will be the more meaningful.
    +
      506.    110
    +
      507.     38        the_token = the_token || state.token_nxt;
    +
      508.    110        delete the_token.warning;
    +
      509.    110        throw warn(code, the_token, a, b, c, d);
    +
      510.    110    }
    +
      511.    634
    +
      512.     28    function stop_at(code, line, column, a, b, c, d) {
    +
      513.     28
    +
      514.     28// Same as warn_at, except that it stops the analysis.
    +
      515.     28
    +
      516.     28        throw warn_at(code, line, column, a, b, c, d);
    +
      517.     28    }
    +
      518.    634
    +
      519. 320554    function test_cause(code, aa, column) {
    +
      520. 320554
    +
      521. 320554// This function will instrument <cause> to <cause_dict> for test-purposes.
    +
      522. 320554
    +
      523.   4686        if (option_dict.test_cause) {
    +
      524.   4686            cause_dict[JSON.stringify([
    +
      525.   4686                String(new Error().stack).replace((
    +
      526.   4686                    /^    at (?:file|stop|stop_at|test_cause|warn|warn_at)\b.*?\n/gm
    +
      527.   4686                ), "").match(
    +
      528.   4686                    /\n    at ((?:Object\.\w+?_)?\w+?) /
    +
      529.   4686                )[1].replace((
    +
      530.   4686                    /^Object\./
    +
      531.   4686                ), ""),
    +
      532.   4686                code,
    +
      533.   4686                String(
    +
      534.   4686                    (aa === undefined || aa === token_global)
    +
      535.   4686                    ? ""
    +
      536.   4686                    : aa
    +
      537.   4686                ),
    +
      538.   4686                column || 0
    +
      539.   4686            ])] = true;
    +
      540.   4686        }
    +
      541. 320554    }
    +
      542.    634
    +
      543.   1062    function warn(code, the_token, a, b, c, d) {
    +
      544.   1062
    +
      545.   1062// Same as warn_at, except the warning will be associated with a specific token.
    +
      546.   1062// If there is already a warning on this token, suppress the new one. It is
    +
      547.   1062// likely that the first warning will be the most meaningful.
    +
      548.   1062
    +
      549.   1062        let the_warning;
    +
      550.     18        the_token = the_token || state.token_nxt;
    +
      551.   1062        the_warning = warn_at(
    +
      552.   1062            code,
    +
      553.   1062            the_token.line,
    +
      554.    371            (the_token.from || 0) + jslint_fudge,
    +
      555.    811            a || artifact(the_token),
    +
      556.   1062            b,
    +
      557.   1062            c,
    +
      558.   1062            d
    +
      559.   1062        );
    +
      560.    878        if (the_token.warning === undefined) {
    +
      561.    878            the_token.warning = the_warning;
    +
      562.    878        } else {
    +
      563.    183            warning_list.pop();
    +
      564.   1061        }
    +
      565.   1061        return the_warning;
    +
      566.   1061    }
    +
      567.    634
    +
      568.   1361    function warn_at(code, line, column, a, b, c, d) {
    +
      569.   1361
    +
      570.   1361// Report an error at some line and column of the program. The warning object
    +
      571.   1361// resembles an exception.
    +
      572.   1361
    +
      573.   1361        let mm;
    +
      574.   1361        let warning = Object.assign(empty(), {
    +
      575.   1361            a,
    +
      576.   1361            b,
    +
      577.   1361            c,
    +
      578.   1361            code,
    +
      579.   1361
    +
      580.   1361// Fudge column numbers in warning message.
    +
      581.   1361
    +
      582.     22            column: column || jslint_fudge,
    +
      583.   1361            d,
    +
      584.   1361            line,
    +
      585.   1361            line_source: "",
    +
      586.   1361            name: "JSLintError"
    +
      587.   1361        }, line_list[line]);
    +
      588.   1361        warning.column = Math.max(
    +
      589.   1361            Math.min(warning.column, warning.line_source.length),
    +
      590.   1361            jslint_fudge
    +
      591.   1361        );
    +
      592.    889        test_cause(code, b || a, warning.column);
    +
      593.   1361        switch (code) {
    +
      594.   1361
    +
      595.   1361// The bundle contains the raw text messages that are generated by jslint. It
    +
      596.   1361// seems that they are all error messages and warnings. There are no "Atta
    +
      597.   1361// boy!" or "You are so awesome!" messages. There is no positive reinforcement
    +
      598.   1361// or encouragement. This relentless negativity can undermine self-esteem and
    +
      599.   1361// wound the inner child. But if you accept it as sound advice rather than as
    +
      600.   1361// personal criticism, it can make your programs better.
    +
      601.   1361
    +
      602.      1        case "and":
    +
      603.      1            mm = `The '&&' subexpression should be wrapped in parens.`;
    +
      604.      1            break;
    +
      605.     67        case "bad_assignment_a":
    +
      606.     67            mm = `Bad assignment to '${a}'.`;
    +
      607.     67            break;
    +
      608.      1        case "bad_directive_a":
    +
      609.      1            mm = `Bad directive '${a}'.`;
    +
      610.      1            break;
    +
      611.      1        case "bad_get":
    +
      612.      1            mm = `A get function takes no parameters.`;
    +
      613.      1            break;
    +
      614.      1        case "bad_module_name_a":
    +
      615.      1            mm = `Bad module name '${a}'.`;
    +
      616.      1            break;
    +
      617.      2        case "bad_option_a":
    +
      618.      2            mm = `Bad option '${a}'.`;
    +
      619.      2            break;
    +
      620.      1        case "bad_set":
    +
      621.      1            mm = `A set function takes one parameter.`;
    +
      622.      1            break;
    +
      623.      6        case "duplicate_a":
    +
      624.      6            mm = `Duplicate '${a}'.`;
    +
      625.      6            break;
    +
      626.     66        case "empty_block":
    +
      627.     66            mm = `Empty block.`;
    +
      628.     66            break;
    +
      629.      5        case "expected_a":
    +
      630.      5            mm = `Expected '${a}'.`;
    +
      631.      5            break;
    +
      632.     25        case "expected_a_at_b_c":
    +
      633.     25            mm = `Expected '${a}' at column ${b}, not column ${c}.`;
    +
      634.     25            break;
    +
      635.    284        case "expected_a_b":
    +
      636.    284            mm = `Expected '${a}' and instead saw '${b}'.`;
    +
      637.    284            break;
    +
      638.     16        case "expected_a_b_before_c_d":
    +
      639.     16            mm = `Expected ${a} '${b}' to be ordered before ${c} '${d}'.`;
    +
      640.     16            break;
    +
      641.      2        case "expected_a_b_from_c_d":
    +
      642.      2            mm = (
    +
      643.      2                `Expected '${a}' to match '${b}' from line ${c}`
    +
      644.      2                + ` and instead saw '${d}'.`
    +
      645.      2            );
    +
      646.      2            break;
    +
      647.     38        case "expected_a_before_b":
    +
      648.     38            mm = `Expected '${a}' before '${b}'.`;
    +
      649.     38            break;
    +
      650.      1        case "expected_digits_after_a":
    +
      651.      1            mm = `Expected digits after '${a}'.`;
    +
      652.      1            break;
    +
      653.      1        case "expected_four_digits":
    +
      654.      1            mm = `Expected four digits after '\\u'.`;
    +
      655.      1            break;
    +
      656.     32        case "expected_identifier_a":
    +
      657.     32            mm = `Expected an identifier and instead saw '${a}'.`;
    +
      658.     32            break;
    +
      659.      6        case "expected_line_break_a_b":
    +
      660.      6            mm = `Expected a line break between '${a}' and '${b}'.`;
    +
      661.      6            break;
    +
      662.      3        case "expected_regexp_factor_a":
    +
      663.      3            mm = `Expected a regexp factor and instead saw '${a}'.`;
    +
      664.      3            break;
    +
      665.     76        case "expected_space_a_b":
    +
      666.     76            mm = `Expected one space between '${a}' and '${b}'.`;
    +
      667.     76            break;
    +
      668.      2        case "expected_statements_a":
    +
      669.      2            mm = `Expected statements before '${a}'.`;
    +
      670.      2            break;
    +
      671.      1        case "expected_string_a":
    +
      672.      1            mm = `Expected a string and instead saw '${a}'.`;
    +
      673.      1            break;
    +
      674.      1        case "expected_type_string_a":
    +
      675.      1            mm = `Expected a type string and instead saw '${a}'.`;
    +
      676.      1            break;
    +
      677.      5        case "freeze_exports":
    +
      678.      5            mm = (
    +
      679.      5                `Expected 'Object.freeze('. All export values should be frozen.`
    +
      680.      5            );
    +
      681.      5            break;
    +
      682.   1361
    +
      683.   1361// PR-378 - Relax warning "function_in_loop".
    +
      684.   1361//
    +
      685.   1361//         case "function_in_loop":
    +
      686.   1361//             mm = `Don't create functions within a loop.`;
    +
      687.   1361//             break;
    +
      688.      1        case "infix_in":
    +
      689.      1            mm = (
    +
      690.      1                `Unexpected 'in'. Compare with undefined,`
    +
      691.      1                + ` or use the hasOwnProperty method instead.`
    +
      692.      1            );
    +
      693.      1            break;
    +
      694.      1        case "label_a":
    +
      695.      1            mm = `'${a}' is a statement label.`;
    +
      696.      1            break;
    +
      697.      1        case "misplaced_a":
    +
      698.      1            mm = `Place '${a}' at the outermost level.`;
    +
      699.      1            break;
    +
      700.      1        case "misplaced_directive_a":
    +
      701.      1            mm = `Place the '/*${a}*/' directive before the first statement.`;
    +
      702.      1            break;
    +
      703.      1        case "missing_await_statement":
    +
      704.      1            mm = `Expected await statement in async function.`;
    +
      705.      1            break;
    +
      706.   1361
    +
      707.   1361// PR-347 - Disable warning "missing_browser".
    +
      708.   1361//         case "missing_browser":
    +
      709.   1361//             mm = `/*global*/ requires the Assume a browser option.`;
    +
      710.   1361//             break;
    +
      711.   1361
    +
      712.      1        case "missing_m":
    +
      713.      1            mm = `Expected 'm' flag on a multiline regular expression.`;
    +
      714.      1            break;
    +
      715.      5        case "naked_block":
    +
      716.      5            mm = `Naked block.`;
    +
      717.      5            break;
    +
      718.      2        case "nested_comment":
    +
      719.      2            mm = `Nested comment.`;
    +
      720.      2            break;
    +
      721.      1        case "not_label_a":
    +
      722.      1            mm = `'${a}' is not a label.`;
    +
      723.      1            break;
    +
      724.      2        case "number_isNaN":
    +
      725.      2            mm = `Use Number.isNaN function to compare with NaN.`;
    +
      726.      2            break;
    +
      727.      4        case "out_of_scope_a":
    +
      728.      4            mm = `'${a}' is out of scope.`;
    +
      729.      4            break;
    +
      730.     11        case "redefinition_a_b":
    +
      731.     11            mm = `Redefinition of '${a}' from line ${b}.`;
    +
      732.     11            break;
    +
      733.      1        case "redefinition_global_a_b":
    +
      734.      1            mm = `Redefinition of global ${a} variable '${b}'.`;
    +
      735.      1            break;
    +
      736.      6        case "required_a_optional_b":
    +
      737.      6            mm = `Required parameter '${a}' after optional parameter '${b}'.`;
    +
      738.      6            break;
    +
      739.      1        case "reserved_a":
    +
      740.      1            mm = `Reserved name '${a}'.`;
    +
      741.      1            break;
    +
      742.      1        case "subscript_a":
    +
      743.      1            mm = `['${a}'] is better written in dot notation.`;
    +
      744.      1            break;
    +
      745.     17        case "todo_comment":
    +
      746.     17            mm = `Unexpected TODO comment.`;
    +
      747.     17            break;
    +
      748.      8        case "too_long":
    +
      749.      8            mm = `Line is longer than 80 characters.`;
    +
      750.      8            break;
    +
      751.      1        case "too_many_digits":
    +
      752.      1            mm = `Too many digits.`;
    +
      753.      1            break;
    +
      754.      3        case "unclosed_comment":
    +
      755.      3            mm = `Unclosed comment.`;
    +
      756.      3            break;
    +
      757.      3        case "unclosed_disable":
    +
      758.      3            mm = (
    +
      759.      3                `Directive '/*jslint-disable*/' was not closed`
    +
      760.      3                + ` with '/*jslint-enable*/'.`
    +
      761.      3            );
    +
      762.      3            break;
    +
      763.      3        case "unclosed_mega":
    +
      764.      3            mm = `Unclosed mega literal.`;
    +
      765.      3            break;
    +
      766.      2        case "unclosed_string":
    +
      767.      2            mm = `Unclosed string.`;
    +
      768.      2            break;
    +
      769.    127        case "undeclared_a":
    +
      770.    127            mm = `Undeclared '${a}'.`;
    +
      771.    127            break;
    +
      772.    235        case "unexpected_a":
    +
      773.    235            mm = `Unexpected '${a}'.`;
    +
      774.    235            break;
    +
      775.      1        case "unexpected_a_after_b":
    +
      776.      1            mm = `Unexpected '${a}' after '${b}'.`;
    +
      777.      1            break;
    +
      778.      2        case "unexpected_a_before_b":
    +
      779.      2            mm = `Unexpected '${a}' before '${b}'.`;
    +
      780.      2            break;
    +
      781.     35        case "unexpected_at_top_level_a":
    +
      782.     35            mm = `Expected '${a}' to be in a function.`;
    +
      783.     35            break;
    +
      784.      1        case "unexpected_char_a":
    +
      785.      1            mm = `Unexpected character '${a}'.`;
    +
      786.      1            break;
    +
      787.      2        case "unexpected_comment":
    +
      788.      2            mm = `Unexpected comment.`;
    +
      789.      2            break;
    +
      790.   1361
    +
      791.   1361// PR-347 - Disable warning "unexpected_directive_a".
    +
      792.   1361//         case "unexpected_directive_a":
    +
      793.   1361//             mm = `When using modules, don't use directive '/\u002a${a}'.`;
    +
      794.   1361//             break;
    +
      795.   1361
    +
      796.    123        case "unexpected_expression_a":
    +
      797.    123            mm = `Unexpected expression '${a}' in statement position.`;
    +
      798.    123            break;
    +
      799.      4        case "unexpected_label_a":
    +
      800.      4            mm = `Unexpected label '${a}'.`;
    +
      801.      4            break;
    +
      802.      3        case "unexpected_parens":
    +
      803.      3            mm = `Don't wrap function literals in parens.`;
    +
      804.      3            break;
    +
      805.      8        case "unexpected_space_a_b":
    +
      806.      8            mm = `Unexpected space between '${a}' and '${b}'.`;
    +
      807.      8            break;
    +
      808.      1        case "unexpected_statement_a":
    +
      809.      1            mm = `Unexpected statement '${a}' in expression position.`;
    +
      810.      1            break;
    +
      811.      2        case "unexpected_trailing_space":
    +
      812.      2            mm = `Unexpected trailing space.`;
    +
      813.      2            break;
    +
      814.      1        case "unexpected_typeof_a":
    +
      815.      1            mm = (
    +
      816.      1                `Unexpected 'typeof'. Use '===' to compare directly with ${a}.`
    +
      817.      1            );
    +
      818.      1            break;
    +
      819.      1        case "uninitialized_a":
    +
      820.      1            mm = `Uninitialized '${a}'.`;
    +
      821.      1            break;
    +
      822.      1        case "unopened_enable":
    +
      823.      1            mm = (
    +
      824.      1                `Directive '/*jslint-enable*/' was not opened`
    +
      825.      1                + ` with '/*jslint-disable*/'.`
    +
      826.      1            );
    +
      827.      1            break;
    +
      828.      1        case "unreachable_a":
    +
      829.      1            mm = `Unreachable '${a}'.`;
    +
      830.      1            break;
    +
      831.      1        case "unregistered_property_a":
    +
      832.      1            mm = `Unregistered property name '${a}'.`;
    +
      833.      1            break;
    +
      834.      6        case "unused_a":
    +
      835.      6            mm = `Unused '${a}'.`;
    +
      836.      6            break;
    +
      837.      2        case "use_double":
    +
      838.      2            mm = `Use double quotes, not single quotes.`;
    +
      839.      2            break;
    +
      840.      7        case "use_open":
    +
      841.      7            mm = (
    +
      842.      7                `Wrap a ternary expression in parens,`
    +
      843.      7                + ` with a line break after the left paren.`
    +
      844.      7            );
    +
      845.      7            break;
    +
      846.      1        case "use_spaces":
    +
      847.      1            mm = `Use spaces, not tabs.`;
    +
      848.      1            break;
    +
      849.      5        case "var_on_top":
    +
      850.      5            mm = `Move variable declaration to top of function or script.`;
    +
      851.      5            break;
    +
      852.      1        case "var_switch":
    +
      853.      1            mm = `Don't declare variables in a switch.`;
    +
      854.      1            break;
    +
      855.     21        case "weird_condition_a":
    +
      856.     21            mm = `Weird condition '${a}'.`;
    +
      857.     21            break;
    +
      858.      5        case "weird_expression_a":
    +
      859.      5            mm = `Weird expression '${a}'.`;
    +
      860.      5            break;
    +
      861.      3        case "weird_loop":
    +
      862.      3            mm = `Weird loop.`;
    +
      863.      3            break;
    +
      864.      9        case "weird_property_a":
    +
      865.      9            mm = `Weird property name '${a}'.`;
    +
      866.      9            break;
    +
      867.      8        case "weird_relation_a":
    +
      868.      8            mm = `Weird relation '${a}'.`;
    +
      869.      8            break;
    +
      870.      1        case "wrap_condition":
    +
      871.      1            mm = `Wrap the condition in parens.`;
    +
      872.      1            break;
    +
      873.      1        case "wrap_immediate":
    +
      874.      1            mm = (
    +
      875.      1                `Wrap an immediate function invocation in parentheses to assist`
    +
      876.      1                + ` the reader in understanding that the expression is the`
    +
      877.      1                + ` result of a function, and not the function itself.`
    +
      878.      1            );
    +
      879.      1            break;
    +
      880.      1        case "wrap_parameter":
    +
      881.      1            mm = `Wrap the parameter in parens.`;
    +
      882.      1            break;
    +
      883.     18        case "wrap_regexp":
    +
      884.     18            mm = `Wrap this regexp in parens to avoid confusion.`;
    +
      885.     18            break;
    +
      886.      1        case "wrap_unary":
    +
      887.      1            mm = `Wrap the unary expression in parens.`;
    +
      888.      1            break;
    +
      889.   1361        }
    +
      890.   1361
    +
      891.   1361// Validate mm.
    +
      892.   1361
    +
      893.   1361        jslint_assert(mm, code);
    +
      894.   1361        warning.message = mm;
    +
      895.   1361
    +
      896.   1361// PR-242 - Include stack_trace for jslint to debug itself for errors.
    +
      897.   1361
    +
      898.      6        if (option_dict.trace) {
    +
      899.      6            warning.stack_trace = new Error().stack;
    +
      900.   1360        }
    +
      901.   1360        if (warning.directive_quiet) {
    +
      902.     37
    +
      903.     37// test_cause:
    +
      904.     37// ["0 //jslint-quiet", "semicolon", "directive_quiet", "", 0]
    +
      905.     37
    +
      906.     37            test_cause("directive_quiet");
    +
      907.     37            return warning;
    +
      908.   1323        }
    +
      909.   1323        warning_list.push(warning);
    +
      910.   1323        return warning;
    +
      911.   1323    }
    +
      912.    634
    +
      913.    634    try {
    +
      914.    634
    +
      915.    634// tokenize takes a source and produces from it an array of token objects.
    +
      916.    634// JavaScript is notoriously difficult to tokenize because of the horrible
    +
      917.    634// interactions between automatic semicolon insertion, regular expression
    +
      918.    634// literals, and now megastring literals. JSLint benefits from eliminating
    +
      919.    634// automatic semicolon insertion and nested megastring literals, which allows
    +
      920.    634// full tokenization to precede parsing.
    +
      921.    634
    +
      922.    634        option_dict = Object.assign(empty(), option_dict);
    +
      923.    634        Object.assign(state, {
    +
      924.    634            artifact,
    +
      925.    634            catch_list,
    +
      926.    634            catch_stack,
    +
      927.    634            directive_list,
    +
      928.    634            export_dict,
    +
      929.    634            function_list,
    +
      930.    634            function_stack,
    +
      931.    634            global_dict,
    +
      932.    634            global_list,
    +
      933.    634            import_list,
    +
      934.    634            is_equal,
    +
      935.    634            is_weird,
    +
      936.    634            line_list,
    +
      937.    634            mode_json: false,           // true if parsing JSON.
    +
      938.    634            mode_module: false,         // true if import or export was used.
    +
      939.    634            mode_property: false,       // true if directive /*property*/ is
    +
      940.    634                                        // used.
    +
      941.    634            mode_shebang: false,        // true if #! is seen on the first line.
    +
      942.    634            option_dict,
    +
      943.    634            property_dict,
    +
      944.    634            source,
    +
      945.    634            stop,
    +
      946.    634            stop_at,
    +
      947.    634            syntax_dict,
    +
      948.    634            tenure,
    +
      949.    634            test_cause,
    +
      950.    634            token_global,
    +
      951.    634            token_list,
    +
      952.    634            token_nxt: token_global,
    +
      953.    634            warn,
    +
      954.    634            warn_at,
    +
      955.    634            warning_list
    +
      956.    634        });
    +
      957.    634
    +
      958.    634// PHASE 1. Split <source> by newlines into <line_list>.
    +
      959.    634
    +
      960.    634        jslint_phase1_split(state);
    +
      961.    634        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
    +
      962.    634        jslint_assert(
    +
      963.    634            function_stack.length === 0,
    +
      964.    634            `function_stack.length === 0.`
    +
      965.    634        );
    +
      966.    634
    +
      967.    634// PHASE 2. Lex <line_list> into <token_list>.
    +
      968.    634
    +
      969.    634        jslint_phase2_lex(state);
    +
      970.    634        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
    +
      971.    634        jslint_assert(
    +
      972.    634            function_stack.length === 0,
    +
      973.    634            `function_stack.length === 0.`
    +
      974.    634        );
    +
      975.    634
    +
      976.    634// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
    +
      977.    634
    +
      978.    634        jslint_phase3_parse(state);
    +
      979.    634        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
    +
      980.    634        jslint_assert(
    +
      981.    634            function_stack.length === 0,
    +
      982.    634            `function_stack.length === 0.`
    +
      983.    634        );
    +
      984.    634
    +
      985.    634// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
    +
      986.    634//          recursive traversal. Each node may be processed on the way down
    +
      987.    634//          (preaction) and on the way up (postaction).
    +
      988.    634
    +
      989.    479        if (!state.mode_json) {
    +
      990.    479            jslint_phase4_walk(state);
    +
      991.    494        }
    +
      992.    494        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
    +
      993.    494        jslint_assert(
    +
      994.    494            function_stack.length === 0,
    +
      995.    494            `function_stack.length === 0.`
    +
      996.    494        );
    +
      997.    494
    +
      998.    494// PHASE 5. Check whitespace between tokens in <token_list>.
    +
      999.    494
    +
     1000.    494        if (!state.mode_json && warning_list.length === 0) {
    +
     1001.    184            jslint_phase5_whitage(state);
    +
     1002.    494        }
    +
     1003.    494        jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`);
    +
     1004.    494        jslint_assert(
    +
     1005.    494            function_stack.length === 0,
    +
     1006.    494            `function_stack.length === 0.`
    +
     1007.    494        );
    +
     1008.    494
    +
     1009.    494// PR-347 - Disable warning "missing_browser".
    +
     1010.    494//         if (!option_dict.browser) {
    +
     1011.    494//             directive_list.forEach(function (comment) {
    +
     1012.    494//                 if (comment.directive === "global") {
    +
     1013.    494//
    +
     1014.    494// // test_cause:
    +
     1015.    494// // ["/*global aa*/", "jslint", "missing_browser", "(comment)", 1]
    +
     1016.    494//
    +
     1017.    494//                     warn("missing_browser", comment);
    +
     1018.    494//                 }
    +
     1019.    494//             });
    +
     1020.    494//         }
    +
     1021.    494
    +
     1022.    494        if (option_dict.test_internal_error) {
    +
     1023.      1            jslint_assert(undefined, "test_internal_error");
    +
     1024.      1        }
    +
     1025.    141    } catch (err) {
    +
     1026.    141        mode_stop = true;
    +
     1027.    141        err.message = "[JSLint was unable to finish] " + err.message;
    +
     1028.    141        err.mode_stop = true;
    +
     1029.    141        if (err.name !== "JSLintError") {
    +
     1030.    141            Object.assign(err, {
    +
     1031.    141                column: jslint_fudge,
    +
     1032.    141                line: jslint_fudge,
    +
     1033.    141                line_source: "",
    +
     1034.    141                stack_trace: err.stack
    +
     1035.    141            });
    +
     1036.    141        }
    +
     1037.    141        if (warning_list.indexOf(err) === -1) {
    +
     1038.    141            warning_list.push(err);
    +
     1039.    141        }
    +
     1040.    141    }
    +
     1041.    634
    +
     1042.    634// Sort warning_list by mode_stop first, line, column respectively.
    +
     1043.    634
    +
     1044.    965    warning_list.sort(function (aa, bb) {
    +
     1045.    965        return (
    +
     1046.    965            Boolean(bb.mode_stop) - Boolean(aa.mode_stop)
    +
     1047.    868            || aa.line - bb.line
    +
     1048.    847            || aa.column - bb.column
    +
     1049.    965        );
    +
     1050.    965
    +
     1051.    965// Update each warning with formatted_message ready-for-use by jslint_cli.
    +
     1052.    965
    +
     1053.   1143    }).map(function ({
    +
     1054.   1143        column,
    +
     1055.   1143        line,
    +
     1056.   1143        line_source,
    +
     1057.   1143        message,
    +
     1058.   1143        stack_trace = ""
    +
     1059.   1143    }, ii, list) {
    +
     1060.   1143        list[ii].formatted_message = String(
    +
     1061.   1143            String(ii + 1).padStart(2, " ")
    +
     1062.   1143            + ". \u001b[31m" + message + "\u001b[39m"
    +
     1063.   1143            + " \u001b[90m\/\/ line " + line + ", column " + column
    +
     1064.   1143            + "\u001b[39m\n"
    +
     1065.   1143            + ("    " + line_source.trim()).slice(0, 72) + "\n"
    +
     1066.   1143            + stack_trace
    +
     1067.   1143        ).trimRight();
    +
     1068.   1143    });
    +
     1069.    634
    +
     1070.    634    return {
    +
     1071.    634        causes: cause_dict,
    +
     1072.    634        directives: directive_list,
    +
     1073.    634        edition: jslint_edition,
    +
     1074.    634        exports: export_dict,
    +
     1075.    634        froms: import_list,
    +
     1076.    634        functions: function_list,
    +
     1077.    634        global: token_global,
    +
     1078.    634        id: "(JSLint)",
    +
     1079.    634        json: state.mode_json,
    +
     1080.    634        lines: line_list,
    +
     1081.    634        module: state.mode_module === true,
    +
     1082.    141        ok: warning_list.length === 0 && !mode_stop,
    +
     1083.    634        option: option_dict,
    +
     1084.    634        property: property_dict,
    +
     1085.    634        shebang: (
    +
     1086.    634            state.mode_shebang
    +
     1087.      1            ? line_list[jslint_fudge].line_source
    +
     1088.    633            : undefined
    +
     1089.    634        ),
    +
     1090.    634        stop: mode_stop,
    +
     1091.    634        tokens: token_list,
    +
     1092.    634        tree: state.token_tree,
    +
     1093.    634        warnings: warning_list
    +
     1094.    634    };
    +
     1095.    634}
    +
     1096.      1
    +
     1097.      1// PR-362 - Add API Doc.
    +
     1098.      1
    +
     1099.      1async function jslint_apidoc({
    +
     1100.      1    example_list,
    +
     1101.      1    github_repo,
    +
     1102.      1    module_list,
    +
     1103.      1    package_name,
    +
     1104.      1    pathname,
    +
     1105.      1    version
    +
     1106.      1}) {
    +
     1107.      1
    +
     1108.      1// This function will create API Doc from <module_list>.
    +
     1109.      1
    +
     1110.      1    let elem_ii = 0;
    +
     1111.      1    let html;
    +
     1112.      1
    +
     1113.     26    function elem_create(moduleObj, key, moduleName) {
    +
     1114.     26
    +
     1115.     26// This function will create a sub API Doc from elem <moduleObj>[<key>].
    +
     1116.     26
    +
     1117.     26        let example = "N/A";
    +
     1118.     26        let id = encodeURIComponent("apidoc.elem." + moduleName + "." + key);
    +
     1119.     26        let name;
    +
     1120.     26        let signature;
    +
     1121.     26        let source;
    +
     1122.     26        name = htmlEscape((typeof moduleObj[key]) + " " + key);
    +
     1123.      2        if (typeof moduleObj[key] !== "function") {
    +
     1124.      2            return {
    +
     1125.      2                name,
    +
     1126.      2                signature: (`
    +
     1127.      2<a class="apidocElementLiA" href="#${id}">
    +
     1128.      2${name}
    +
     1129.      2</a>
    +
     1130.      2                `),
    +
     1131.      2                source: (`
    +
     1132.      2<li>
    +
     1133.      2    <h2>
    +
     1134.      2    <a href="#${id}" id="${id}">
    +
     1135.      2    ${name}
    +
     1136.      2    </a>
    +
     1137.      2    </h2>
    +
     1138.      2</li>
    +
     1139.      2                `)
    +
     1140.      2            };
    +
     1141.     24        }
    +
     1142.     24        // init source
    +
     1143.     24        source = htmlEscape(trim_start(moduleObj[key].toString()));
    +
     1144.     24        // init signature
    +
     1145.     24        source = source.replace((
    +
     1146.     24            /(\([\S\s]*?\)) \{/
    +
     1147.     24        ), function (match0, match1) {
    +
     1148.     24            signature = htmlEscape(
    +
     1149.     24                match1.replace((
    +
     1150.     24                    / *?\/\*[\S\s]*?\*\/ */g
    +
     1151.     24                ), "").replace((
    +
     1152.     24                    / *?\/\/.*/g
    +
     1153.     24                ), "").replace((
    +
     1154.     24                    /\n{2,}/g
    +
     1155.     24                ), "\n")
    +
     1156.     24            );
    +
     1157.     24            return match0;
    +
     1158.     24        });
    +
     1159.     24        // init comment
    +
     1160.     24        source = source.replace((
    +
     1161.     24            /\n(?:\/\/.*?\n)+\n/
    +
     1162.     24        ), "<span class=\"apidocCodeCommentSpan\">$&</span>");
    +
     1163.     24        // init example
    +
     1164.     54        example_list.some(function (example2) {
    +
     1165.     54            example2.replace(
    +
     1166.     54                new RegExp((
    +
     1167.     54                    "((?:\\n.*?){8}(function )?)\\b"
    +
     1168.     54                    + key
    +
     1169.     54                    + "(\\((?:.*?\\n){8})"
    +
     1170.     54                ), "g"),
    +
     1171.     99                function (ignore, header, isDeclaration, footer) {
    +
     1172.     91                    if (!isDeclaration) {
    +
     1173.     91                        example = "..." + trim_start(
    +
     1174.     91                            htmlEscape(header)
    +
     1175.     91                            + "<span class=\"apidocCodeKeywordSpan\">"
    +
     1176.     91                            + htmlEscape(key)
    +
     1177.     91                            + "</span>"
    +
     1178.     91                            + htmlEscape(footer)
    +
     1179.     91                        ).trimEnd() + "\n...";
    +
     1180.     91                    }
    +
     1181.     99                    return "";
    +
     1182.     99                }
    +
     1183.     54            );
    +
     1184.     54            return example !== "N/A";
    +
     1185.     54        });
    +
     1186.     24        if (source.length > 2048) {
    +
     1187.     10            source = source.slice(0, 2048) + "...\n}\n";
    +
     1188.     24        }
    +
     1189.     24        return {
    +
     1190.     24            name,
    +
     1191.     24            signature: (`
    +
     1192.     24<a class="apidocElementLiA" href="#${id}">
    +
     1193.     24${name}<span class="apidocSignatureSpan">${signature}</span>
    +
     1194.     24</a>
    +
     1195.     24            `),
    +
     1196.     24            source: (`
    +
     1197.     24<li>
    +
     1198.     24    <h2>
    +
     1199.     24    <a href="#${id}" id="${id}">
    +
     1200.     24    ${name}<span class="apidocSignatureSpan">${signature}</span>
    +
     1201.     24    </a>
    +
     1202.     24    </h2>
    +
     1203.     24</li>
    +
     1204.     24<li>Description and source-code:<pre class="apidocCodePre">${source}</pre></li>
    +
     1205.     24<li>Example usage:<pre class="apidocCodePre">${example}</pre></li>
    +
     1206.     24            `)
    +
     1207.     24        };
    +
     1208.     24    }
    +
     1209.      1
    +
     1210.    115    function trim_start(str) {
    +
     1211.    115
    +
     1212.    115// This function will normalize whitespace before <str>.
    +
     1213.    115
    +
     1214.    115        let whitespace = "";
    +
     1215.    115        str.trim().replace((
    +
     1216.    115            /^ */gm
    +
     1217.  12096        ), function (match0) {
    +
     1218.   7840            if (whitespace === "" || match0.length < whitespace.length) {
    +
     1219.   6058                whitespace = match0;
    +
     1220.   6058            }
    +
     1221.  12096            return "";
    +
     1222.  12096        });
    +
     1223.    115        str = str.replace(new RegExp("^" + whitespace, "gm"), "");
    +
     1224.    115        return str;
    +
     1225.    115    }
    +
     1226.      1    await moduleFsInit();
    +
     1227.      1
    +
     1228.      1// Html-escape params.
    +
     1229.      1
    +
     1230.      1    github_repo = htmlEscape(github_repo);
    +
     1231.      1    package_name = htmlEscape(package_name);
    +
     1232.      1    version = htmlEscape(version);
    +
     1233.      1
    +
     1234.      1// Init example_list.
    +
     1235.      1
    +
     1236.      3    example_list = await Promise.all(example_list.map(async function (file) {
    +
     1237.      3
    +
     1238.      3// This function will read example from given file.
    +
     1239.      3
    +
     1240.      3        let result = await moduleFs.promises.readFile(file, "utf8");
    +
     1241.      3        result = (
    +
     1242.      3            "\n\n\n\n\n\n\n\n"
    +
     1243.      3            // bug-workaround - truncate example to manageable size
    +
     1244.      3            + result.slice(0, 524288)
    +
     1245.      3            + "\n\n\n\n\n\n\n\n"
    +
     1246.      3        );
    +
     1247.      3        result = result.replace((
    +
     1248.      3            /\r\n*/g
    +
     1249.      3        ), "\n");
    +
     1250.      3        return result;
    +
     1251.      3    }));
    +
     1252.      1    // init module_list
    +
     1253.      1    module_list = await Promise.all(module_list.map(async function ({
    +
     1254.      1        pathname
    +
     1255.      1    }) {
    +
     1256.      1        let moduleName = htmlEscape(JSON.stringify(pathname));
    +
     1257.      1        let moduleObj = await import(pathname);
    +
     1258.      1        if (moduleObj.default) {
    +
     1259.      1            moduleObj = moduleObj.default;
    +
     1260.      1        }
    +
     1261.      1        return {
    +
     1262.     26            elem_list: Object.keys(moduleObj).map(function (key) {
    +
     1263.     26                return elem_create(moduleObj, key, moduleName);
    +
     1264.     75            }).sort(function (aa, bb) {
    +
     1265.     75                return (
    +
     1266.     75                    aa.name < bb.name
    +
     1267.     22                    ? -1
    +
     1268.     53                    : 1
    +
     1269.     75                );
    +
     1270.     26            }).map(function (elem) {
    +
     1271.     26                elem_ii += 1;
    +
     1272.     26                elem.signature = elem.signature.replace(
    +
     1273.     26                    ">",
    +
     1274.     26                    ">" + elem_ii + ". "
    +
     1275.     26                );
    +
     1276.     26                elem.source = elem.source.replace(
    +
     1277.     26                    "\">",
    +
     1278.     26                    "\">" + elem_ii + ". "
    +
     1279.     26                );
    +
     1280.     26                return elem;
    +
     1281.     26            }),
    +
     1282.      1            id: encodeURIComponent("apidoc.module." + moduleName),
    +
     1283.      1            moduleName
    +
     1284.      1        };
    +
     1285.      1    }));
    +
     1286.      1    html = (`
    +
     1287.      1<!DOCTYPE html>
    +
     1288.      1<html lang="en">
    +
     1289.      1<head>
    +
     1290.      1<meta charset="utf-8">
    +
     1291.      1<meta name="viewport" content="width=device-width, initial-scale=1">
    +
     1292.      1<meta name="description" content="${package_name} API Doc">
    +
     1293.      1<title>${package_name} apidoc</title>
    +
     1294.      1<style>
    +
     1295.      1/* jslint utility2:true */
    +
     1296.      1/*csslint*/
    +
     1297.      1body {
    +
     1298.      1    margin: 0;
    +
     1299.      1    padding: 20px;
    +
     1300.      1}
    +
     1301.      1.apidocCodeCommentSpan,
    +
     1302.      1.apidocCodeKeywordSpan {
    +
     1303.      1    background: royalblue;
    +
     1304.      1    color: white;
    +
     1305.      1}
    +
     1306.      1.apidocCodeCommentSpan {
    +
     1307.      1    display: block;
    +
     1308.      1}
    +
     1309.      1.apidocCodePre {
    +
     1310.      1    background: #eef;
    +
     1311.      1    border: 1px solid;
    +
     1312.      1    font-size: 14px;
    +
     1313.      1    overflow-wrap: break-word;
    +
     1314.      1    padding: 5px;
    +
     1315.      1    white-space: pre-wrap;
    +
     1316.      1}
    +
     1317.      1.apidocDiv {
    +
     1318.      1    color: #555;
    +
     1319.      1    font-family: sans-serif;
    +
     1320.      1}
    +
     1321.      1.apidocDiv a[href] {
    +
     1322.      1    color: royalblue;
    +
     1323.      1    text-decoration: none;
    +
     1324.      1}
    +
     1325.      1.apidocDiv a[href]:hover {
    +
     1326.      1    text-decoration: underline;
    +
     1327.      1}
    +
     1328.      1.apidocDiv li a {
    +
     1329.      1    display: inline-block;
    +
     1330.      1    padding: 8px 0;
    +
     1331.      1}
    +
     1332.      1.apidocDiv ul {
    +
     1333.      1    list-style: none;
    +
     1334.      1    padding-left: 20px;
    +
     1335.      1}
    +
     1336.      1.apidocFooterDiv {
    +
     1337.      1    margin-top: 20px;
    +
     1338.      1    text-align: center;
    +
     1339.      1}
    +
     1340.      1.apidocModuleA {
    +
     1341.      1    font-size: 24px;
    +
     1342.      1    font-weight: bold;
    +
     1343.      1}
    +
     1344.      1.apidocSectionDiv {
    +
     1345.      1    border-top: 1px solid;
    +
     1346.      1    margin-top: 20px;
    +
     1347.      1}
    +
     1348.      1.apidocSignatureSpan {
    +
     1349.      1    color: #666;
    +
     1350.      1    white-space: pre-wrap;
    +
     1351.      1}
    +
     1352.      1</style>
    +
     1353.      1</head>
    +
     1354.      1<body>
    +
     1355.      1<div class="apidocDiv">
    +
     1356.      1<h1>API Doc for <a href="${github_repo}">${package_name} (${version})</a></h1>
    +
     1357.      1<div class="apidocSectionDiv">
    +
     1358.      1    <a href="#apidocTableOfContents1" id="apidocTableOfContents1">
    +
     1359.      1        <h1>Table of Contents</h1>
    +
     1360.      1    </a>
    +
     1361.      1    <ul>
    +
     1362.      1    `) + module_list.map(function ({
    +
     1363.      1        elem_list,
    +
     1364.      1        id,
    +
     1365.      1        moduleName
    +
     1366.      1    }) {
    +
     1367.      1        return (
    +
     1368.      1            (`
    +
     1369.      1        <li>
    +
     1370.      1            <a class="apidocModuleA" href="#${id}">Module ${moduleName}</a>
    +
     1371.      1            <ul>
    +
     1372.      1            `)
    +
     1373.     26            + elem_list.map(function ({
    +
     1374.     26                signature
    +
     1375.     26            }) {
    +
     1376.     26                return "<li>\n" + signature + "\n</li>\n";
    +
     1377.     26            }).join("")
    +
     1378.      1            + (`
    +
     1379.      1            </ul>
    +
     1380.      1        </li>
    +
     1381.      1            `)
    +
     1382.      1        );
    +
     1383.      1    }).join("") + (`
    +
     1384.      1    </ul>
    +
     1385.      1</div>
    +
     1386.      1    `) + module_list.map(function ({
    +
     1387.      1        elem_list,
    +
     1388.      1        id,
    +
     1389.      1        moduleName
    +
     1390.      1    }) {
    +
     1391.      1        return (
    +
     1392.      1            (`
    +
     1393.      1<div class="apidocSectionDiv">
    +
     1394.      1    <h1><a href="#${id}" id="${id}">Module ${moduleName}</a></h1>
    +
     1395.      1    <ul>
    +
     1396.      1            `)
    +
     1397.     26            + elem_list.map(function ({
    +
     1398.     26                source
    +
     1399.     26            }) {
    +
     1400.     26                return source;
    +
     1401.     26            }).join("")
    +
     1402.      1            + (`
    +
     1403.      1    </ul>
    +
     1404.      1</div>
    +
     1405.      1            `)
    +
     1406.      1        );
    +
     1407.      1    }).join("") + (`
    +
     1408.      1<div class="apidocFooterDiv">
    +
     1409.      1    [
    +
     1410.      1    This document was created with
    +
     1411.      1    <a href="https://github.com/jslint-org/jslint">JSLint</a>
    +
     1412.      1    ]
    +
     1413.      1</div>
    +
     1414.      1</div>
    +
     1415.      1</body>
    +
     1416.      1</html>
    +
     1417.      1    `);
    +
     1418.      1    html = html.trim().replace((
    +
     1419.      1        / +?$/gm
    +
     1420.      1    ), "") + "\n";
    +
     1421.      1    await fsWriteFileWithParents(pathname, html);
    +
     1422.      1}
    +
     1423.      1
    +
     1424.  97545function jslint_assert(condition, message) {
    +
     1425.  97545
    +
     1426.  97545// This function will throw <message> if <condition> is falsy.
    +
     1427.  97545
    +
     1428.  97543    if (condition) {
    +
     1429.  97543        return condition;
    +
     1430.  97543    }
    +
     1431.      2    throw new Error(
    +
     1432.      2        `This was caused by a bug in JSLint.
    +
     1433.      2Please open an issue with this stack-trace (and possible example-code) at
    +
     1434.      2https://github.com/jslint-org/jslint/issues.
    +
     1435.      2edition = "${jslint_edition}";
    +
     1436.      2${String(message).slice(0, 2000)}`
    +
     1437.      2    );
    +
     1438.      2}
    +
     1439.      1
    +
     1440.     21async function jslint_cli({
    +
     1441.     21    console_error,
    +
     1442.     21    console_log,
    +
     1443.     21    file,
    +
     1444.     21    mode_cli,
    +
     1445.     21    mode_noop,
    +
     1446.     21    option,
    +
     1447.     21    process_argv,
    +
     1448.     21    process_env,
    +
     1449.     21    process_exit,
    +
     1450.     21    source
    +
     1451.     21}) {
    +
     1452.     21
    +
     1453.     21// This function will run jslint from nodejs-cli.
    +
     1454.     21
    +
     1455.     21    let command;
    +
     1456.     21    let data;
    +
     1457.     21    let exit_code = 0;
    +
     1458.     21    let mode_plugin_vim;
    +
     1459.     21    let mode_report;
    +
     1460.     21    let result;
    +
     1461.     21
    +
     1462.     43    function jslint_from_file({
    +
     1463.     43        code,
    +
     1464.     43        file,
    +
     1465.     43        line_offset = 0,
    +
     1466.     43        mode_conditional,
    +
     1467.     43        option = empty()
    +
     1468.     43    }) {
    +
     1469.     43        let result_from_file;
    +
     1470.     43        if (
    +
     1471.     43            mode_conditional
    +
     1472.      5            && !(
    +
     1473.      5                /^\/\*jslint\b/m
    +
     1474.      5            ).test(code.slice(0, 65536))
    +
     1475.      1        ) {
    +
     1476.      1            return;
    +
     1477.     42        }
    +
     1478.     42        option = Object.assign(empty(), option, {
    +
     1479.     42            file
    +
     1480.     42        });
    +
     1481.     42        switch ((
    +
     1482.     42            /\.\w+?$|$/m
    +
     1483.     42        ).exec(file)[0]) {
    +
     1484.     42        case ".html":
    +
     1485.      2
    +
     1486.      2// Recursively jslint embedded "<script>\n...\n</script>".
    +
     1487.      2
    +
     1488.      2            code.replace((
    +
     1489.      2                /^<script\b[^>]*?>\n([\S\s]*?\n)<\/script>$/gm
    +
     1490.      2            ), function (ignore, match1, ii) {
    +
     1491.      2                jslint_from_file({
    +
     1492.      2                    code: match1,
    +
     1493.      2                    file: file + ".<script>.js",
    +
     1494.      2                    line_offset: string_line_count(code.slice(0, ii)) + 1,
    +
     1495.      2                    option: Object.assign(empty(), {
    +
     1496.      2                        browser: true
    +
     1497.      2                    }, option)
    +
     1498.      2                });
    +
     1499.      2                return "";
    +
     1500.      2            });
    +
     1501.      2            return;
    +
     1502.      2        case ".md":
    +
     1503.      2
    +
     1504.      2// Recursively jslint embedded "node --eval '\n...\n'".
    +
     1505.      2
    +
     1506.      2            jslint_node_eval({
    +
     1507.      2                code,
    +
     1508.      2                file,
    +
     1509.      2                mode_conditional: true,
    +
     1510.      2                option
    +
     1511.      2            });
    +
     1512.      2            return;
    +
     1513.      2        case ".sh":
    +
     1514.      2
    +
     1515.      2// Recursively jslint embedded "node --eval '\n...\n'".
    +
     1516.      2
    +
     1517.      2            jslint_node_eval({
    +
     1518.      2                code,
    +
     1519.      2                file,
    +
     1520.      2                option
    +
     1521.      2            });
    +
     1522.      2            return;
    +
     1523.     36        default:
    +
     1524.     36            result_from_file = jslint("\n".repeat(line_offset) + code, option);
    +
     1525.     36        }
    +
     1526.     36
    +
     1527.     36// Print only first 10 warnings to stderr.
    +
     1528.     36
    +
     1529.     36        if (result_from_file.warnings.length > 0) {
    +
     1530.      5            exit_code = 1;
    +
     1531.      5            console_error(
    +
     1532.      5                mode_plugin_vim
    +
     1533.      5
    +
     1534.      5// PR-349 - Print warnings in format readable by vim.
    +
     1535.      5
    +
     1536.      5                ? result_from_file.warnings.slice(0, 10).map(function ({
    +
     1537.      5                    column,
    +
     1538.      5                    line,
    +
     1539.      5                    message
    +
     1540.      5                }, ii) {
    +
     1541.      5                    return (
    +
     1542.      5                        file
    +
     1543.      5                        + ":" + ii
    +
     1544.      5                        + ":" + line
    +
     1545.      5                        + ":" + column
    +
     1546.      5                        + ":" + message
    +
     1547.      5                    );
    +
     1548.      5                }).join("\n")
    +
     1549.      5
    +
     1550.      5// Print warnings in format readable by human.
    +
     1551.      5
    +
     1552.      5                : "\u001b[1mjslint " + file + "\u001b[22m\n"
    +
     1553.     11                + result_from_file.warnings.slice(0, 10).map(function ({
    +
     1554.     11                    formatted_message
    +
     1555.     11                }) {
    +
     1556.     11                    return formatted_message;
    +
     1557.     11                }).join("\n")
    +
     1558.      5            );
    +
     1559.     36        }
    +
     1560.     36        return result_from_file;
    +
     1561.     36    }
    +
     1562.     21
    +
     1563.      4    function jslint_node_eval({
    +
     1564.      4        code,
    +
     1565.      4        file,
    +
     1566.      4        mode_conditional,
    +
     1567.      4        option = empty()
    +
     1568.      4    }) {
    +
     1569.      4        code.replace((
    +
     1570.      4            /\bnode\b.*? (?:--eval|-e) '\n([\S\s]*?\n)'/gm
    +
     1571.     23        ), function (ignore, match1, ii) {
    +
     1572.     23            jslint_from_file({
    +
     1573.     23                code: match1,
    +
     1574.     23                file: file + ".<node -e>.js",
    +
     1575.     23                line_offset: string_line_count(code.slice(0, ii)) + 1,
    +
     1576.     23                mode_conditional,
    +
     1577.     23                option: Object.assign(empty(), {
    +
     1578.     23                    beta: Boolean(
    +
     1579.     23                        process_env.JSLINT_BETA
    +
     1580.     23                        && !(
    +
     1581.     23                            /0|false|null|undefined/
    +
     1582.     23                        ).test(process_env.JSLINT_BETA)
    +
     1583.     23                    ),
    +
     1584.     23                    node: true
    +
     1585.     23                }, option)
    +
     1586.     23            });
    +
     1587.     23            return "";
    +
     1588.     23        });
    +
     1589.      4    }
    +
     1590.     21
    +
     1591.     24    function string_line_count(code) {
    +
     1592.     24
    +
     1593.     24// This function will count number of newlines in <code>.
    +
     1594.     24
    +
     1595.     24        let count;
    +
     1596.     24        let ii;
    +
     1597.     24
    +
     1598.     24// https://jsperf.com/regexp-counting-2/8
    +
     1599.     24
    +
     1600.     24        count = 0;
    +
     1601.     24        ii = 0;
    +
     1602.  14997        while (true) {
    +
     1603.  14997            ii = code.indexOf("\n", ii) + 1;
    +
     1604.  14997            if (ii === 0) {
    +
     1605.  14997                break;
    +
     1606.  14997            }
    +
     1607.  14997            count += 1;
    +
     1608.  14997        }
    +
     1609.     24        return count;
    +
     1610.     24    }
    +
     1611.     21
    +
     1612.     21// Feature-detect nodejs.
    +
     1613.     21
    +
     1614.     21    if (!(
    +
     1615.     21        typeof process === "object"
    +
     1616.     21        && process
    +
     1617.     21        && process.versions
    +
     1618.     21        && typeof process.versions.node === "string"
    +
     1619.     21        && !mode_noop
    +
     1620.      1    )) {
    +
     1621.      1        return exit_code;
    +
     1622.     20    }
    +
     1623.     20    console_error = console_error || console.error;
    +
     1624.     20    console_log = console_log || console.log;
    +
     1625.      6    process_argv = process_argv || process.argv;
    +
     1626.     17    process_env = process_env || process.env;
    +
     1627.      8    process_exit = process_exit || process.exit;
    +
     1628.     20    await moduleFsInit();
    +
     1629.     20    if (
    +
     1630.     20        !(
    +
     1631.     20
    +
     1632.     20// Feature-detect nodejs-cli.
    +
     1633.     20
    +
     1634.     20            process.execArgv.indexOf("--eval") === -1
    +
     1635.     20            && process.execArgv.indexOf("-e") === -1
    +
     1636.     20            && (
    +
     1637.     20                (
    +
     1638.     20                    /[\/|\\]jslint(?:\.[cm]?js)?$/m
    +
     1639.     20                ).test(process_argv[1])
    +
     1640.     20                || mode_cli
    +
     1641.     20            )
    +
     1642.     19            && moduleUrl.fileURLToPath(jslint_import_meta_url)
    +
     1643.     19            === modulePath.resolve(process_argv[1])
    +
     1644.     21        )
    +
     1645.      6        && !mode_cli
    +
     1646.      1    ) {
    +
     1647.      1        return exit_code;
    +
     1648.     19    }
    +
     1649.     19
    +
     1650.     19// init commmand
    +
     1651.     19
    +
     1652.     19    command = String(process_argv[2]).split("=");
    +
     1653.     19    command[1] = command.slice(1).join("=");
    +
     1654.     19
    +
     1655.     19    switch (command[0]) {
    +
     1656.     19
    +
     1657.     19// PR-362 - Add API Doc.
    +
     1658.     19
    +
     1659.     19    case "jslint_apidoc":
    +
     1660.      1        await jslint_apidoc(Object.assign(JSON.parse(process_argv[3]), {
    +
     1661.      1            pathname: command[1]
    +
     1662.      1        }));
    +
     1663.      1        return;
    +
     1664.     21
    +
     1665.     21// COMMIT-b26d6df2 - Add command jslint_plugin_vim.
    +
     1666.     21
    +
     1667.      1    case "jslint_plugin_vim":
    +
     1668.      1        mode_plugin_vim = true;
    +
     1669.      1        process_argv = process_argv.slice(1);
    +
     1670.      1        break;
    +
     1671.     21
    +
     1672.     21// PR-363 - Add command jslint_report.
    +
     1673.     21
    +
     1674.      5    case "jslint_report":
    +
     1675.      5        mode_report = command[1];
    +
     1676.      5        process_argv = process_argv.slice(1);
    +
     1677.      5        break;
    +
     1678.     21
    +
     1679.     21// PR-364 - Add command v8_coverage_report.
    +
     1680.     21
    +
     1681.      7    case "v8_coverage_report":
    +
     1682.      7        await v8CoverageReportCreate({
    +
     1683.      7            coverageDir: command[1],
    +
     1684.      7            processArgv: process_argv.slice(3)
    +
     1685.      7        });
    +
     1686.      7        return;
    +
     1687.     11    }
    +
     1688.     11
    +
     1689.     11// PR-349 - Detect cli-option --mode-vim-plugin.
    +
     1690.     11
    +
     1691.     11    mode_plugin_vim = (
    +
     1692.     11        process_argv.slice(2).indexOf("--mode-vim-plugin") >= 0
    +
     1693.     11        || mode_plugin_vim
    +
     1694.     21    );
    +
     1695.     21
    +
     1696.     21// Normalize file relative to process.cwd().
    +
     1697.     21
    +
     1698.      6    process_argv.slice(2).some(function (arg) {
    +
     1699.      6        if (!arg.startsWith("-")) {
    +
     1700.      6            file = file || arg;
    +
     1701.      6            return true;
    +
     1702.      6        }
    +
     1703.      6    });
    +
     1704.      1    if (!file) {
    +
     1705.      1        return;
    +
     1706.     10    }
    +
     1707.     10    file = modulePath.resolve(file) + "/";
    +
     1708.     10    if (file.startsWith(process.cwd() + "/")) {
    +
     1709.     10        file = file.replace(process.cwd() + "/", "").slice(0, -1) || ".";
    +
     1710.     10    }
    +
     1711.     10    file = file.replace((
    +
     1712.     10        /\\/g
    +
     1713.     10    ), "/").replace((
    +
     1714.     10        /\/$/g
    +
     1715.     10    ), "");
    +
     1716.     10    if (source) {
    +
     1717.      6        data = source;
    +
     1718.      6    } else {
    +
     1719.      4
    +
     1720.      4// jslint_cli - jslint directory.
    +
     1721.      4
    +
     1722.      4        try {
    +
     1723.      4            data = await moduleFs.promises.readdir(file, "utf8");
    +
     1724.      4        } catch (ignore) {}
    +
     1725.      4        if (data) {
    +
     1726.     28            await Promise.all(data.map(async function (file2) {
    +
     1727.     28                let code;
    +
     1728.     28                let time_start = Date.now();
    +
     1729.     28                file2 = file + "/" + file2;
    +
     1730.     28                switch ((
    +
     1731.     28                    /\.\w+?$|$/m
    +
     1732.     28                ).exec(file2)[0]) {
    +
     1733.      4                case ".cjs":
    +
     1734.      4                case ".html":
    +
     1735.      5                case ".js":
    +
     1736.      7                case ".json":
    +
     1737.      9                case ".md":
    +
     1738.     11                case ".mjs":
    +
     1739.     13                case ".sh":
    +
     1740.     13                    break;
    +
     1741.     15                default:
    +
     1742.     15                    return;
    +
     1743.     13                }
    +
     1744.     13                try {
    +
     1745.     13                    code = await moduleFs.promises.readFile(file2, "utf8");
    +
     1746.     12                } catch (ignore) {
    +
     1747.      4                    return;
    +
     1748.     12                }
    +
     1749.     12                if (
    +
     1750.     12                    (
    +
     1751.     12                        /(?:\b|_)(?:lock|min|raw|rollup)(?:\b|_)/
    +
     1752.     12                    ).test(file2)
    +
     1753.     12                    || !(code && code.length < 1048576)
    +
     1754.      4                ) {
    +
     1755.      4                    return;
    +
     1756.     11                }
    +
     1757.     11                jslint_from_file({
    +
     1758.     11                    code,
    +
     1759.     11                    file: file2,
    +
     1760.     11                    option
    +
     1761.     11                });
    +
     1762.     11                console_error(
    +
     1763.     11                    "jslint - " + (Date.now() - time_start) + "ms - " + file2
    +
     1764.     11                );
    +
     1765.     11            }));
    +
     1766.      4            process_exit(exit_code);
    +
     1767.      4            return exit_code;
    +
     1768.      4        }
    +
     1769.      4
    +
     1770.      4// jslint_cli - jslint file.
    +
     1771.      4
    +
     1772.      4        try {
    +
     1773.      4            data = await moduleFs.promises.readFile(file, "utf8");
    +
     1774.      4        } catch (err) {
    +
     1775.      4            console_error(err);
    +
     1776.      4            exit_code = 1;
    +
     1777.      4            process_exit(exit_code);
    +
     1778.      4            return exit_code;
    +
     1779.      4        }
    +
     1780.      8    }
    +
     1781.      8    result = jslint_from_file({
    +
     1782.      8        code: data,
    +
     1783.      8        file,
    +
     1784.      8        option
    +
     1785.      8    });
    +
     1786.      8    if (mode_report) {
    +
     1787.      5        await fsWriteFileWithParents(mode_report, jslint_report(result));
    +
     1788.      8    }
    +
     1789.      8    process_exit(exit_code);
    +
     1790.      8    return exit_code;
    +
     1791.      8}
    +
     1792.      1
    +
     1793.    634function jslint_phase1_split() {
    +
     1794.    634
    +
     1795.    634// PHASE 1. Split <source> by newlines into <line_list>.
    +
     1796.    634
    +
     1797.    634    return;
    +
     1798.    634}
    +
     1799.      1
    +
     1800.    634function jslint_phase2_lex(state) {
    +
     1801.    634
    +
     1802.    634// PHASE 2. Lex <line_list> into <token_list>.
    +
     1803.    634
    +
     1804.    634    let {
    +
     1805.    634        artifact,
    +
     1806.    634        directive_list,
    +
     1807.    634        global_dict,
    +
     1808.    634        global_list,
    +
     1809.    634        line_list,
    +
     1810.    634        option_dict,
    +
     1811.    634        stop,
    +
     1812.    634        stop_at,
    +
     1813.    634        tenure,
    +
     1814.    634        test_cause,
    +
     1815.    634        token_global,
    +
     1816.    634        token_list,
    +
     1817.    634        warn,
    +
     1818.    634        warn_at
    +
     1819.    634    } = state;
    +
     1820.    634    let char;                   // The current character being lexed.
    +
     1821.    634    let column = 0;             // The column number of the next character.
    +
     1822.    634    let from;                   // The starting column number of the token.
    +
     1823.    634    let from_mega;              // The starting column of megastring.
    +
     1824.    634    let line = 0;               // The line number of the next character.
    +
     1825.    634    let line_disable;           // The starting line of "/*jslint-disable*/".
    +
     1826.    634    let line_mega;              // The starting line of megastring.
    +
     1827.    634    let line_source = "";       // The remaining line source string.
    +
     1828.    634    let line_whole = "";        // The whole line source string.
    +
     1829.    634    let mode_directive = true;  // true if directives are still allowed.
    +
     1830.    634    let mode_mega = false;      // true if currently parsing a megastring
    +
     1831.    634                                // ... literal.
    +
     1832.    634    let mode_regexp;            // true if regular expression literal seen on
    +
     1833.    634                                // ... this line.
    +
     1834.    634    let rx_token = new RegExp(
    +
     1835.    634        "^("
    +
     1836.    634        + "(\\s+)"
    +
     1837.    634        + "|([a-zA-Z_$][a-zA-Z0-9_$]*)"
    +
     1838.    634        + "|[(){}\\[\\],:;'\"~\\`]"
    +
     1839.    634        + "|\\?[?.]?"
    +
     1840.    634        + "|=(?:==?|>)?"
    +
     1841.    634        + "|\\.+"
    +
     1842.    634        + "|\\*[*\\/=]?"
    +
     1843.    634        + "|\\/[*\\/]?"
    +
     1844.    634        + "|\\+[=+]?"
    +
     1845.    634        + "|-[=\\-]?"
    +
     1846.    634        + "|[\\^%]=?"
    +
     1847.    634        + "|&[&=]?"
    +
     1848.    634        + "|\\"
    +
     1849.    634        + "|[|=]?"
    +
     1850.    634        + "|>{1,3}=?"
    +
     1851.    634        + "|<<?=?"
    +
     1852.    634        + "|!(?:!|==?)?"
    +
     1853.    634
    +
     1854.    634// PR-351 - Add BigInt support.
    +
     1855.    634
    +
     1856.    634        + "|(0n?|[1-9][0-9]*n?)"
    +
     1857.    634        + ")"
    +
     1858.    634        + "(.*)$"
    +
     1859.    634    );
    +
     1860.    634    let snippet = "";           // A piece of string.
    +
     1861.    634    let token_1;                // The first token.
    +
     1862.    634    let token_prv = token_global;       // The previous token including
    +
     1863.    634                                        // ... comments.
    +
     1864.    634    let token_prv_expr = token_global;  // The previous token excluding
    +
     1865.    634                                        // ... comments.
    +
     1866.    634
    +
     1867.    634// Most tokens, including the identifiers, operators, and punctuators, can be
    +
     1868.    634// found with a regular expression. Regular expressions cannot correctly match
    +
     1869.    634// regular expression literals, so we will match those the hard way. String
    +
     1870.    634// literals and number literals can be matched by regular expressions, but they
    +
     1871.    634// don't provide good warnings. The functions char_after, char_before,
    +
     1872.    634// read_digits, and char_after_escape help in the parsing of literals.
    +
     1873.    634
    +
     1874. 223403    function char_after(match) {
    +
     1875. 223403
    +
     1876. 223403// Get the next character from the source line. Remove it from the line_source,
    +
     1877. 223403// and append it to the snippet. Optionally check that the previous character
    +
     1878. 223403// matched an expected value.
    +
     1879. 223403
    +
     1880.   4642        if (match !== undefined && char !== match) {
    +
     1881.     10
    +
     1882.     10// test_cause:
    +
     1883.     10// ["aa=/[", "char_after", "expected_a", "]", 5]
    +
     1884.     10// ["aa=/aa{/", "char_after", "expected_a_b", "/", 8]
    +
     1885.     10
    +
     1886.     10            return (
    +
     1887.     10                char === ""
    +
     1888.     10                ? stop_at("expected_a", line, column - 1, match)
    +
     1889.     10                : stop_at("expected_a_b", line, column, match, char)
    +
     1890.     10            );
    +
     1891. 223393        }
    +
     1892. 223393        char = line_source.slice(0, 1);
    +
     1893. 223393        line_source = line_source.slice(1);
    +
     1894. 223393        snippet += char || " ";
    +
     1895. 223403        column += 1;
    +
     1896. 223403        return char;
    +
     1897. 223403    }
    +
     1898.    634
    +
     1899.   2349    function char_after_escape(extra) {
    +
     1900.   2349
    +
     1901.   2349// Validate char after escape "\\".
    +
     1902.   2349
    +
     1903.   2349        char_after("\\");
    +
     1904.   2349        switch (char) {
    +
     1905.      1        case "":
    +
     1906.      1
    +
     1907.      1// test_cause:
    +
     1908.      1// ["\"\\", "char_after_escape", "unclosed_string", "", 2]
    +
     1909.      1
    +
     1910.      1            return stop_at("unclosed_string", line, column);
    +
     1911.    143        case "/":
    +
     1912.    143            return char_after();
    +
     1913.    212        case "\\":
    +
     1914.    212            return char_after();
    +
     1915.      1        case "`":
    +
     1916.      1            return char_after();
    +
     1917.     67        case "b":
    +
     1918.     67            return char_after();
    +
     1919.      6        case "f":
    +
     1920.      6            return char_after();
    +
     1921.    928        case "n":
    +
     1922.    928            return char_after();
    +
     1923.     19        case "r":
    +
     1924.     19            return char_after();
    +
     1925.     24        case "t":
    +
     1926.     24
    +
     1927.     24// test_cause:
    +
     1928.     24// ["\"\\/\\\\\\`\\b\\f\\n\\r\\t\"", "char_after_escape", "char_after", "", 0]
    +
     1929.     24
    +
     1930.     24            test_cause("char_after");
    +
     1931.     24            return char_after();
    +
     1932.    268        case "u":
    +
     1933.    268            if (char_after("u") === "{") {
    +
     1934.    268                if (state.mode_json) {
    +
     1935.    268
    +
     1936.    268// test_cause:
    +
     1937.    268// ["[\"\\u{12345}\"]", "char_after_escape", "unexpected_a", "{", 5]
    +
     1938.    268
    +
     1939.    268                    warn_at("unexpected_a", line, column, char);
    +
     1940.    268                }
    +
     1941.    268                if (read_digits("x") > 5) {
    +
     1942.    268
    +
     1943.    268// test_cause:
    +
     1944.    268// ["\"\\u{123456}\"", "char_after_escape", "too_many_digits", "", 11]
    +
     1945.    268
    +
     1946.    268                    warn_at("too_many_digits", line, column);
    +
     1947.    268                }
    +
     1948.    268                if (char !== "}") {
    +
     1949.    268
    +
     1950.    268// test_cause:
    +
     1951.    268// ["\"\\u{12345\"", "char_after_escape", "expected_a_before_b", "\"", 10]
    +
     1952.    268
    +
     1953.    268                    stop_at("expected_a_before_b", line, column, "}", char);
    +
     1954.    268                }
    +
     1955.    268                return char_after();
    +
     1956.    268            }
    +
     1957.    268            char_before();
    +
     1958.    268            if (read_digits("x", true) < 4) {
    +
     1959.    268
    +
     1960.    268// test_cause:
    +
     1961.    268// ["\"\\u0\"", "char_after_escape", "expected_four_digits", "", 5]
    +
     1962.    268
    +
     1963.    268                warn_at("expected_four_digits", line, column);
    +
     1964.    268            }
    +
     1965.    268            return;
    +
     1966.    680        default:
    +
     1967.    680            if (extra && extra.indexOf(char) >= 0) {
    +
     1968.    680                return char_after();
    +
     1969.    680            }
    +
     1970.    680
    +
     1971.    680// test_cause:
    +
     1972.    680// ["\"\\0\"", "char_after_escape", "unexpected_a_before_b", "0", 3]
    +
     1973.    680
    +
     1974.    680            warn_at("unexpected_a_before_b", line, column, "\\", char);
    +
     1975.   2349        }
    +
     1976.   2349    }
    +
     1977.    634
    +
     1978.   9529    function char_before() {
    +
     1979.   9529
    +
     1980.   9529// Back up one character by moving a character from the end of the snippet to
    +
     1981.   9529// the front of the line_source.
    +
     1982.   9529
    +
     1983.   9529        char = snippet.slice(-1);
    +
     1984.   9529        line_source = char + line_source;
    +
     1985.   9529        column -= char.length;
    +
     1986.   9529
    +
     1987.   9529// Remove last character from snippet.
    +
     1988.   9529
    +
     1989.   9529        snippet = snippet.slice(0, -1);
    +
     1990.   9529        return char;
    +
     1991.   9529    }
    +
     1992.    634
    +
     1993.  10185    function lex_comment() {
    +
     1994.  10185        let body;
    +
     1995.  10185        let ii = 0;
    +
     1996.  10185        let jj = 0;
    +
     1997.  10185        let the_comment;
    +
     1998.  10185
    +
     1999.  10185// Create a comment object. Comments are not allowed in JSON text. Comments can
    +
     2000.  10185// include directives and notices of incompletion.
    +
     2001.  10185
    +
     2002.  10185// Create token from comment //....
    +
     2003.  10185
    +
     2004.  10084        if (snippet === "//") {
    +
     2005.  10084            snippet = line_source;
    +
     2006.  10084            line_source = "";
    +
     2007.  10084            the_comment = token_create("(comment)", snippet);
    +
     2008.  10084            if (mode_mega) {
    +
     2009.  10084
    +
     2010.  10084// test_cause:
    +
     2011.  10084// ["`${//}`", "lex_comment", "unexpected_comment", "`", 4]
    +
     2012.  10084
    +
     2013.  10084                warn("unexpected_comment", the_comment, "`");
    +
     2014.  10084            }
    +
     2015.  10084
    +
     2016.  10084// Create token from comment /*...*/.
    +
     2017.  10084
    +
     2018.  10084        } else {
    +
     2019.    101            snippet = [];
    +
     2020.    101            if (line_source[0] === "/") {
    +
     2021.    101
    +
     2022.    101// test_cause:
    +
     2023.    101// ["/*/", "lex_comment", "unexpected_a", "/", 2]
    +
     2024.    101
    +
     2025.    101                warn_at("unexpected_a", line, column + ii, "/");
    +
     2026.    101            }
    +
     2027.    101
    +
     2028.    101// Lex/loop through each line until "*/".
    +
     2029.    101
    +
     2030.    651            while (true) {
    +
     2031.    651                // rx_star_slash
    +
     2032.    651                ii = line_source.indexOf("*/");
    +
     2033.    651                if (ii >= 0) {
    +
     2034.    651                    break;
    +
     2035.    651                }
    +
     2036.    651                // rx_slash_star
    +
     2037.    651                ii = line_source.indexOf("/*");
    +
     2038.    651                if (ii >= 0) {
    +
     2039.    651
    +
     2040.    651// test_cause:
    +
     2041.    651// ["/*/*", "lex_comment", "nested_comment", "", 2]
    +
     2042.    651
    +
     2043.    651                    warn_at("nested_comment", line, column + ii);
    +
     2044.    651                }
    +
     2045.    651                snippet.push(line_source);
    +
     2046.    651                line_source = read_line();
    +
     2047.    651                if (line_source === undefined) {
    +
     2048.    651
    +
     2049.    651// test_cause:
    +
     2050.    651// ["/*", "lex_comment", "unclosed_comment", "", 1]
    +
     2051.    651
    +
     2052.    651                    return stop_at("unclosed_comment", line, column);
    +
     2053.    651                }
    +
     2054.    651            }
    +
     2055.    101            jj = line_source.slice(0, ii).search(
    +
     2056.    101                // rx_slash_star_or_slash
    +
     2057.    101                /\/\*|\/$/
    +
     2058.    101            );
    +
     2059.    101            if (jj >= 0) {
    +
     2060.    101
    +
     2061.    101// test_cause:
    +
     2062.    101// ["/*/**/", "lex_comment", "nested_comment", "", 2]
    +
     2063.    101
    +
     2064.    101                warn_at("nested_comment", line, column + jj);
    +
     2065.    101            }
    +
     2066.    101            snippet.push(line_source.slice(0, ii));
    +
     2067.    101            snippet = snippet.join(" ");
    +
     2068.    101            column += ii + 2;
    +
     2069.    101            line_source = line_source.slice(ii + 2);
    +
     2070.    101            the_comment = token_create("(comment)", snippet);
    +
     2071.  10182        }
    +
     2072.  10182
    +
     2073.  10182// Uncompleted work comment.
    +
     2074.  10182
    +
     2075.  10182        if (
    +
     2076.  10182            !option_dict.devel
    +
     2077.  10182            && (
    +
     2078.  10177                // rx_todo
    +
     2079.  10177                /\b(?:todo|TO\s?DO|HACK)\b/
    +
     2080.  10177            ).test(snippet)
    +
     2081.     17        ) {
    +
     2082.     17
    +
     2083.     17// test_cause:
    +
     2084.     17// ["//todo", "lex_comment", "todo_comment", "(comment)", 1] //jslint-quiet
    +
     2085.     17
    +
     2086.     17            warn("todo_comment", the_comment);
    +
     2087.  10182        }
    +
     2088.  10182
    +
     2089.  10182// Lex directives in comment.
    +
     2090.  10182
    +
     2091.  10182        [
    +
     2092.  10182            the_comment.directive, body
    +
     2093.  10182        ] = Array.from(snippet.match(
    +
     2094.  10182            // rx_directive
    +
     2095.  10182            /^(jslint|property|global)\s+(.*)$/
    +
     2096.  10182        ) || []).slice(1);
    +
     2097.  10116        if (the_comment.directive === undefined) {
    +
     2098.  10116            return the_comment;
    +
     2099.  10116        }
    +
     2100.     66        directive_list.push(the_comment);
    +
     2101.     66        if (!mode_directive) {
    +
     2102.      1
    +
     2103.      1// test_cause:
    +
     2104.      1// ["0\n/*global aa*/", "lex_comment", "misplaced_directive_a", "global", 1]
    +
     2105.      1
    +
     2106.      1            warn_at("misplaced_directive_a", line, from, the_comment.directive);
    +
     2107.      1            return the_comment;
    +
     2108.     65        }
    +
     2109.     65
    +
     2110.     65// lex_directive();
    +
     2111.     65// JSLint recognizes three directives that can be encoded in comments. This
    +
     2112.     65// function processes one item, and calls itself recursively to process the
    +
     2113.     65// next one.
    +
     2114.     65
    +
     2115.     65// Lex/loop through each directive in /*...*/
    +
     2116.     65
    +
     2117.     65        ii = 0;
    +
     2118.     65        body.replace((
    +
     2119.     65            // rx_directive_part
    +
     2120.     65            /([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*|$/g
    +
     2121.   1706        ), function (match0, key, val, jj) {
    +
     2122.     65            if (ii !== jj) {
    +
     2123.     65
    +
     2124.     65// test_cause:
    +
     2125.     65// ["/*jslint !*/", "lex_comment", "bad_directive_a", "!", 1]
    +
     2126.     65
    +
     2127.     65                return stop("bad_directive_a", the_comment, body.slice(ii));
    +
     2128.   1705            }
    +
     2129.   1705            if (match0 === "") {
    +
     2130.     65                return "";
    +
     2131.   1641            }
    +
     2132.   1641            ii += match0.length;
    +
     2133.   1641            switch (the_comment.directive) {
    +
     2134.   1641            case "global":
    +
     2135.     65                if (val) {
    +
     2136.     65
    +
     2137.     65// test_cause:
    +
     2138.     65// ["/*global aa:false*/", "lex_comment", "bad_option_a", "aa:false", 1]
    +
     2139.     65
    +
     2140.     65                    warn("bad_option_a", the_comment, key + ":" + val);
    +
     2141.     65                }
    +
     2142.     65                global_dict[key] = "user-defined";
    +
     2143.     65
    +
     2144.     65// PR-347 - Disable warning "unexpected_directive_a".
    +
     2145.     65//                 state.mode_module = the_comment;
    +
     2146.     65
    +
     2147.     65                break;
    +
     2148.     84            case "jslint":
    +
     2149.     84                if (!option_set_item(key, val !== "false")) {
    +
     2150.     84
    +
     2151.     84// test_cause:
    +
     2152.     84// ["/*jslint undefined*/", "lex_comment", "bad_option_a", "undefined", 1]
    +
     2153.     84
    +
     2154.     84                    warn("bad_option_a", the_comment, key);
    +
     2155.     84                }
    +
     2156.     84                break;
    +
     2157.   1555            case "property":
    +
     2158.   1555                state.mode_property = true;
    +
     2159.   1555                tenure[key] = true;
    +
     2160.   1555                break;
    +
     2161.   1641            }
    +
     2162.   1641            return "";
    +
     2163.   1641        });
    +
     2164.     65        return the_comment;
    +
     2165.     65    }
    +
     2166.    634
    +
     2167.    714    function lex_megastring() {
    +
     2168.    714        let id;
    +
     2169.    714        let match;
    +
     2170.    714
    +
     2171.    714// The token is a megastring. We don't allow any kind of mega nesting.
    +
     2172.    714
    +
     2173.      1        if (mode_mega) {
    +
     2174.      1
    +
     2175.      1// test_cause:
    +
     2176.      1// ["`${`", "lex_megastring", "expected_a_b", "`", 4]
    +
     2177.      1
    +
     2178.      1            return stop_at("expected_a_b", line, column, "}", "`");
    +
     2179.    713        }
    +
     2180.    713        from_mega = from;
    +
     2181.    713        line_mega = line;
    +
     2182.    713        mode_mega = true;
    +
     2183.    713        snippet = "";
    +
     2184.    713
    +
     2185.    713// Parsing a mega literal is tricky. First create a ` token.
    +
     2186.    713
    +
     2187.    713        token_create("`");
    +
     2188.    713        from += 1;
    +
     2189.    713
    +
     2190.    713// Then loop, building up a string, possibly from many lines, until seeing
    +
     2191.    713// the end of file, a closing `, or a ${ indicting an expression within the
    +
     2192.    713// string.
    +
     2193.    713
    +
     2194.   5613        while (true) {
    +
     2195.   5613            match = line_source.match(
    +
     2196.   5613
    +
     2197.   5613// Vim-hack - vim-editor has trouble parsing '`' in regexp
    +
     2198.   5613
    +
     2199.   5613                // rx_mega
    +
     2200.   5613                /[\u0060\\]|\$\{/
    +
     2201.   5613            ) || {
    +
     2202.   5613                "0": "",
    +
     2203.   5613                index: 0
    +
     2204.   5613            };
    +
     2205.   5613            snippet += line_source.slice(0, match.index);
    +
     2206.   5613            column += match.index;
    +
     2207.   5613            line_source = line_source.slice(match.index);
    +
     2208.   5613            match = match[0];
    +
     2209.   5613            switch (match) {
    +
     2210.   5613            case "${":
    +
     2211.   5613
    +
     2212.   5613// if either ` or ${ was found, then the preceding joins the snippet to become
    +
     2213.   5613// a string token.
    +
     2214.   5613
    +
     2215.   5613                token_create("(string)", snippet).quote = "`";
    +
     2216.   5613                snippet = "";
    +
     2217.   5613
    +
     2218.   5613// If ${, then create tokens that will become part of an expression until
    +
     2219.   5613// a } token is made.
    +
     2220.   5613
    +
     2221.   5613                column += 2;
    +
     2222.   5613                token_create("${");
    +
     2223.   5613                line_source = line_source.slice(2);
    +
     2224.   5613
    +
     2225.   5613// Lex/loop through each token inside megastring-expression `${...}`.
    +
     2226.   5613
    +
     2227.   5613                while (true) {
    +
     2228.   5613                    id = lex_token().id;
    +
     2229.   5613                    if (id === "{") {
    +
     2230.   5613
    +
     2231.   5613// test_cause:
    +
     2232.   5613// ["`${{", "lex_megastring", "expected_a_b", "{", 4]
    +
     2233.   5613
    +
     2234.   5613                        return stop_at("expected_a_b", line, column, "}", "{");
    +
     2235.   5613                    }
    +
     2236.   5613                    if (id === "}") {
    +
     2237.   5613                        break;
    +
     2238.   5613                    }
    +
     2239.   5613                }
    +
     2240.   5613                break;
    +
     2241.   5613            case "\\":
    +
     2242.   5613                snippet += line_source.slice(0, 2);
    +
     2243.   5613                line_source = line_source.slice(2);
    +
     2244.   5613                column += 2;
    +
     2245.   5613                break;
    +
     2246.   5613            case "`":
    +
     2247.   5613
    +
     2248.   5613// if either ` or ${ was found, then the preceding joins the snippet to become
    +
     2249.   5613// a string token.
    +
     2250.   5613
    +
     2251.   5613                token_create("(string)", snippet).quote = "`";
    +
     2252.   5613                snippet = "";
    +
     2253.   5613
    +
     2254.   5613// Terminate megastring with `.
    +
     2255.   5613
    +
     2256.   5613                line_source = line_source.slice(1);
    +
     2257.   5613                column += 1;
    +
     2258.   5613                mode_mega = false;
    +
     2259.   5613                return token_create("`");
    +
     2260.   5613            default:
    +
     2261.   5613
    +
     2262.   5613// If neither ` nor ${ is seen, then the whole line joins the snippet.
    +
     2263.   5613
    +
     2264.   5613                snippet += line_source + "\n";
    +
     2265.   5613                if (read_line() === undefined) {
    +
     2266.   5613
    +
     2267.   5613// test_cause:
    +
     2268.   5613// ["`", "lex_megastring", "unclosed_mega", "", 1]
    +
     2269.   5613
    +
     2270.   5613                    return stop_at("unclosed_mega", line_mega, from_mega);
    +
     2271.   5613                }
    +
     2272.   5613            }
    +
     2273.   5613        }
    +
     2274.    714    }
    +
     2275.    634
    +
     2276.   8735    function lex_number() {
    +
     2277.   8735        let prefix = snippet;
    +
     2278.   8735        char_after();
    +
     2279.   2846        switch (prefix === "0" && char) {
    +
     2280.      2        case "b":
    +
     2281.      4        case "o":
    +
     2282.     20        case "x":
    +
     2283.     20            read_digits(char);
    +
     2284.     20
    +
     2285.     20// PR-351 - Ignore BigInt suffix 'n'.
    +
     2286.     20
    +
     2287.     20            if (char === "n") {
    +
     2288.     20                char_after("n");
    +
     2289.     20            }
    +
     2290.     20            break;
    +
     2291.   8715        default:
    +
     2292.   8715            if (char === ".") {
    +
     2293.   8715                read_digits("d");
    +
     2294.   8715            }
    +
     2295.   8715            if (char === "E" || char === "e") {
    +
     2296.   8715                char_after(char);
    +
     2297.   8715                if (char !== "+" && char !== "-") {
    +
     2298.   8715                    char_before();
    +
     2299.   8715                }
    +
     2300.   8715                read_digits("d");
    +
     2301.   8715            }
    +
     2302.   8735        }
    +
     2303.   8735
    +
     2304.   8735// If the next character after a number is a digit or letter, then something
    +
     2305.   8735// unexpected is going on.
    +
     2306.   8735
    +
     2307.   8735        if (
    +
     2308.   1520            (char >= "0" && char <= "9")
    +
     2309.     39            || (char >= "a" && char <= "z")
    +
     2310.   8734            || (char >= "A" && char <= "Z")
    +
     2311.      1        ) {
    +
     2312.      1
    +
     2313.      1// test_cause:
    +
     2314.      1// ["0a", "lex_number", "unexpected_a_after_b", "0", 2]
    +
     2315.      1
    +
     2316.      1            return stop_at(
    +
     2317.      1                "unexpected_a_after_b",
    +
     2318.      1                line,
    +
     2319.      1                column,
    +
     2320.      1                snippet.slice(-1),
    +
     2321.      1                snippet.slice(0, -1)
    +
     2322.      1            );
    +
     2323.   8734        }
    +
     2324.   8734        char_before();
    +
     2325.   8734        return token_create("(number)", snippet);
    +
     2326.   8734    }
    +
     2327.    634
    +
     2328.    424    function lex_regexp() {
    +
     2329.    424
    +
     2330.    424// Regexp
    +
     2331.    424// Lex a regular expression literal.
    +
     2332.    424
    +
     2333.    424        let flag;
    +
     2334.    424        let mode_regexp_multiline;
    +
     2335.    424        let result;
    +
     2336.    424        let value;
    +
     2337.    424        mode_regexp = true;
    +
     2338.    424
    +
     2339.    143        function lex_regexp_bracketed() {
    +
     2340.    143            let mode_regexp_range;
    +
     2341.    143
    +
     2342.    143// RegExp
    +
     2343.    143// Match a class.
    +
     2344.    143
    +
     2345.    143            char_after("[");
    +
     2346.     11            if (char === "^") {
    +
     2347.     11                char_after("^");
    +
     2348.     11            }
    +
     2349.    594            while (true) {
    +
     2350.    594
    +
     2351.    594// RegExp
    +
     2352.    594// Match a character in a character class.
    +
     2353.    594
    +
     2354.    594                switch (char) {
    +
     2355.    594                case "":
    +
     2356.    594                case "]":
    +
     2357.    594
    +
     2358.    594// test_cause:
    +
     2359.    594// ["aa=/[", "lex_regexp_bracketed", "closer", "", 0]
    +
     2360.    594// ["aa=/[]/", "lex_regexp_bracketed", "closer", "", 0]
    +
     2361.    594
    +
     2362.    594                    test_cause("closer");
    +
     2363.    594                    if (mode_regexp_range) {
    +
     2364.    594
    +
     2365.    594// test_cause:
    +
     2366.    594// ["aa=/[0-]/", "lex_regexp_bracketed", "unexpected_a", "-", 7]
    +
     2367.    594
    +
     2368.    594                        warn_at("unexpected_a", line, column - 1, "-");
    +
     2369.    594                    }
    +
     2370.    594                    return char_after("]");
    +
     2371.    594
    +
     2372.    594// PR-362 - Relax regexp-warning against using <space>.
    +
     2373.    594//                 case " ":
    +
     2374.    594//
    +
     2375.    594// // test_cause:
    +
     2376.    594// // ["aa=/[ ]/", "lex_regexp_bracketed", "expected_a_b", " ", 6]
    +
     2377.    594//
    +
     2378.    594//                     warn_at("expected_a_b", line, column, "\\u0020", " ");
    +
     2379.    594//                     break;
    +
     2380.    594
    +
     2381.    594                case "-":
    +
     2382.    594                case "/":
    +
     2383.    594                case "[":
    +
     2384.    594                case "^":
    +
     2385.    594
    +
     2386.    594// test_cause:
    +
     2387.    594// ["aa=/[-]/", "lex_regexp_bracketed", "expected_a_before_b", "-", 6]
    +
     2388.    594// ["aa=/[.^]/", "lex_regexp_bracketed", "expected_a_before_b", "^", 7]
    +
     2389.    594// ["aa=/[/", "lex_regexp_bracketed", "expected_a_before_b", "/", 6]
    +
     2390.    594// ["aa=/[\\\\/]/", "lex_regexp_bracketed", "expected_a_before_b", "/", 8]
    +
     2391.    594// ["aa=/[\\\\[]/", "lex_regexp_bracketed", "expected_a_before_b", "[", 8]
    +
     2392.    594
    +
     2393.    594                    warn_at("expected_a_before_b", line, column, "\\", char);
    +
     2394.    594                    break;
    +
     2395.    594                case "\\":
    +
     2396.    594                    char_after_escape("BbDdSsWw-[]^");
    +
     2397.    594                    char_before();
    +
     2398.    594                    break;
    +
     2399.    594                case "`":
    +
     2400.    594                    if (mode_mega) {
    +
     2401.    594
    +
     2402.    594// test_cause:
    +
     2403.    594// ["`${/[`]/}`", "lex_regexp_bracketed", "unexpected_a", "`", 6]
    +
     2404.    594
    +
     2405.    594                        warn_at("unexpected_a", line, column, "`");
    +
     2406.    594                    }
    +
     2407.    594                    break;
    +
     2408.    594                }
    +
     2409.    594                char_after();
    +
     2410.    594                mode_regexp_range = false;
    +
     2411.    594                if (char === "-") {
    +
     2412.    594
    +
     2413.    594// RegExp
    +
     2414.    594// Match a range of subclasses.
    +
     2415.    594
    +
     2416.    594                    mode_regexp_range = true;
    +
     2417.    594                    char_after("-");
    +
     2418.    594                }
    +
     2419.    594            }
    +
     2420.    143        }
    +
     2421.    424
    +
     2422.    590        function lex_regexp_group() {
    +
     2423.    590
    +
     2424.    590// RegExp
    +
     2425.    590// Lex sequence of characters in regexp.
    +
     2426.    590
    +
     2427.    590            switch (char) {
    +
     2428.      1            case "":
    +
     2429.      1                warn_at("expected_regexp_factor_a", line, column, char);
    +
     2430.      1                break;
    +
     2431.      1            case ")":
    +
     2432.      1                warn_at("expected_regexp_factor_a", line, column, char);
    +
     2433.      1                break;
    +
     2434.      1            case "]":
    +
     2435.      1
    +
     2436.      1// test_cause:
    +
     2437.      1// ["/ /", "lex_regexp_group", "expected_regexp_factor_a", "", 3]
    +
     2438.      1// ["aa=/)", "lex_regexp_group", "expected_regexp_factor_a", ")", 5]
    +
     2439.      1// ["aa=/]", "lex_regexp_group", "expected_regexp_factor_a", "]", 5]
    +
     2440.      1
    +
     2441.      1                warn_at("expected_regexp_factor_a", line, column, char);
    +
     2442.      1                break;
    +
     2443.    590            }
    +
     2444.   4284            while (true) {
    +
     2445.   4284                switch (char) {
    +
     2446.   4284                case "":
    +
     2447.   4284                case ")":
    +
     2448.   4284                case "/":
    +
     2449.   4284                case "]":
    +
     2450.   4284                    return;
    +
     2451.   4284
    +
     2452.   4284// PR-362 - Relax regexp-warning against using <space>.
    +
     2453.   4284//                 case " ":
    +
     2454.   4284//
    +
     2455.   4284// // test_cause:
    +
     2456.   4284// // ["aa=/ /", "lex_regexp_group", "expected_a_b", " ", 5]
    +
     2457.   4284//
    +
     2458.   4284//                     warn_at("expected_a_b", line, column, "\\s", " ");
    +
     2459.   4284//                     char_after();
    +
     2460.   4284//                     break;
    +
     2461.   4284
    +
     2462.   4284                case "$":
    +
     2463.   4284                    if (line_source[0] !== "/") {
    +
     2464.   4284                        mode_regexp_multiline = true;
    +
     2465.   4284                    }
    +
     2466.   4284                    char_after();
    +
     2467.   4284                    break;
    +
     2468.   4284                case "(":
    +
     2469.   4284
    +
     2470.   4284// RegExp
    +
     2471.   4284// Match a group that starts with left paren.
    +
     2472.   4284
    +
     2473.   4284                    char_after("(");
    +
     2474.   4284                    if (char === "?") {
    +
     2475.   4284                        char_after("?");
    +
     2476.   4284                        if (char === "=" || char === "!") {
    +
     2477.   4284                            char_after();
    +
     2478.   4284                        } else {
    +
     2479.   4284                            char_after(":");
    +
     2480.   4284                        }
    +
     2481.   4284                    } else if (char === ":") {
    +
     2482.   4284
    +
     2483.   4284// test_cause:
    +
     2484.   4284// ["aa=/(:)/", "lex_regexp_group", "expected_a_before_b", ":", 6]
    +
     2485.   4284// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5]
    +
     2486.   4284
    +
     2487.   4284                        warn_at("expected_a_before_b", line, column, "?", ":");
    +
     2488.   4284                    }
    +
     2489.   4284
    +
     2490.   4284// RegExp
    +
     2491.   4284// Recurse lex_regexp_group().
    +
     2492.   4284
    +
     2493.   4284                    lex_regexp_group();
    +
     2494.   4284                    char_after(")");
    +
     2495.   4284                    break;
    +
     2496.   4284                case "*":
    +
     2497.   4284                case "+":
    +
     2498.   4284                case "?":
    +
     2499.   4284                case "{":
    +
     2500.   4284                case "}":
    +
     2501.   4284
    +
     2502.   4284// test_cause:
    +
     2503.   4284// ["aa=/+/", "lex_regexp_group", "expected_a_before_b", "+", 5]
    +
     2504.   4284// ["aa=/.**/", "lex_regexp_group", "expected_a_before_b", "*", 7]
    +
     2505.   4284// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5]
    +
     2506.   4284// ["aa=/{/", "lex_regexp_group", "expected_a_before_b", "{", 5]
    +
     2507.   4284// ["aa=/}/", "lex_regexp_group", "expected_a_before_b", "}", 5]
    +
     2508.   4284
    +
     2509.   4284                    warn_at("expected_a_before_b", line, column, "\\", char);
    +
     2510.   4284                    char_after();
    +
     2511.   4284                    break;
    +
     2512.   4284                case "[":
    +
     2513.   4284                    lex_regexp_bracketed();
    +
     2514.   4284                    break;
    +
     2515.   4284                case "\\":
    +
     2516.   4284
    +
     2517.   4284// test_cause:
    +
     2518.   4284// ["aa=/\\/", "lex_regexp_group", "escape", "", 0]
    +
     2519.   4284
    +
     2520.   4284                    test_cause("escape");
    +
     2521.   4284                    char_after_escape("BbDdSsWw^${}[]():=!.|*+?");
    +
     2522.   4284                    break;
    +
     2523.   4284                case "^":
    +
     2524.   4284                    if (snippet !== "^") {
    +
     2525.   4284                        mode_regexp_multiline = true;
    +
     2526.   4284                    }
    +
     2527.   4284                    char_after();
    +
     2528.   4284                    break;
    +
     2529.   4284                case "`":
    +
     2530.   4284                    if (mode_mega) {
    +
     2531.   4284
    +
     2532.   4284// test_cause:
    +
     2533.   4284// ["`${/`/}`", "lex_regexp_group", "unexpected_a", "`", 5]
    +
     2534.   4284
    +
     2535.   4284                        warn_at("unexpected_a", line, column, "`");
    +
     2536.   4284                    }
    +
     2537.   4284                    char_after();
    +
     2538.   4284                    break;
    +
     2539.   4284                default:
    +
     2540.   4284                    char_after();
    +
     2541.   4284                }
    +
     2542.   4284
    +
     2543.   4284// RegExp
    +
     2544.   4284// Match an optional quantifier.
    +
     2545.   4284
    +
     2546.   4284                switch (char) {
    +
     2547.   4284                case "*":
    +
     2548.   4284                case "+":
    +
     2549.   4284                    if (char_after(char) === "?") {
    +
     2550.   4284
    +
     2551.   4284// test_cause:
    +
     2552.   4284// ["aa=/.*?/", "lex_regexp_group", "?", "", 0]
    +
     2553.   4284// ["aa=/.+?/", "lex_regexp_group", "?", "", 0]
    +
     2554.   4284
    +
     2555.   4284                        test_cause("?");
    +
     2556.   4284                        char_after("?");
    +
     2557.   4284                    }
    +
     2558.   4284                    break;
    +
     2559.   4284                case "?":
    +
     2560.   4284                    if (char_after("?") === "?") {
    +
     2561.   4284
    +
     2562.   4284// test_cause:
    +
     2563.   4284// ["aa=/.??/", "lex_regexp_group", "unexpected_a", "?", 7]
    +
     2564.   4284
    +
     2565.   4284                        warn_at("unexpected_a", line, column, char);
    +
     2566.   4284                        char_after("?");
    +
     2567.   4284                    }
    +
     2568.   4284                    break;
    +
     2569.   4284                case "{":
    +
     2570.   4284                    if (read_digits("d", true) === 0) {
    +
     2571.   4284
    +
     2572.   4284// test_cause:
    +
     2573.   4284// ["aa=/aa{/", "lex_regexp_group", "expected_a_before_b", ",", 8]
    +
     2574.   4284
    +
     2575.   4284                        warn_at("expected_a_before_b", line, column, "0", ",");
    +
     2576.   4284                    }
    +
     2577.   4284                    if (char === ",") {
    +
     2578.   4284
    +
     2579.   4284// test_cause:
    +
     2580.   4284// ["aa=/.{,/", "lex_regexp_group", "comma", "", 0]
    +
     2581.   4284
    +
     2582.   4284                        test_cause("comma");
    +
     2583.   4284                        read_digits("d", true);
    +
     2584.   4284                    }
    +
     2585.   4284                    if (char_after("}") === "?") {
    +
     2586.   4284
    +
     2587.   4284// test_cause:
    +
     2588.   4284// ["aa=/.{0}?/", "lex_regexp_group", "unexpected_a", "?", 9]
    +
     2589.   4284
    +
     2590.   4284                        warn_at("unexpected_a", line, column, char);
    +
     2591.   4284                        char_after("?");
    +
     2592.   4284                    }
    +
     2593.   4284                    break;
    +
     2594.   4284                }
    +
     2595.   4284            }
    +
     2596.    590        }
    +
     2597.    424
    +
     2598.    424// RegExp
    +
     2599.    424// Scan the regexp literal. Give a warning if the first character is = because
    +
     2600.    424// /= looks like a division assignment operator.
    +
     2601.    424
    +
     2602.    424        snippet = "";
    +
     2603.    424        char_after();
    +
     2604.      1        if (char === "=") {
    +
     2605.      1
    +
     2606.      1// test_cause:
    +
     2607.      1// ["aa=/=/", "lex_regexp", "expected_a_before_b", "=", 5]
    +
     2608.      1
    +
     2609.      1            warn_at("expected_a_before_b", line, column, "\\", "=");
    +
     2610.      1        }
    +
     2611.    424        lex_regexp_group();
    +
     2612.    424
    +
     2613.    424// RegExp
    +
     2614.    424// Remove last character from snippet.
    +
     2615.    424
    +
     2616.    424        snippet = snippet.slice(0, -1);
    +
     2617.    424
    +
     2618.    424// RegExp
    +
     2619.    424// Make sure there is a closing slash.
    +
     2620.    424
    +
     2621.    424        value = snippet;
    +
     2622.    424        char_after("/");
    +
     2623.    424
    +
     2624.    424// RegExp
    +
     2625.    424// Create flag.
    +
     2626.    424
    +
     2627.    424        flag = empty();
    +
     2628.    424        while (
    +
     2629.    424
    +
     2630.    424// Regexp
    +
     2631.    424// char is a letter.
    +
     2632.    424
    +
     2633.    288            (char >= "a" && char <= "z\uffff")
    +
     2634.    414            || (char >= "A" && char <= "Z\uffff")
    +
     2635.    286        ) {
    +
     2636.    286
    +
     2637.    286// RegExp
    +
     2638.    286// Process dangling flag letters.
    +
     2639.    286
    +
     2640.    286            switch (!flag[char] && char) {
    +
     2641.    286            case "g":
    +
     2642.    286                break;
    +
     2643.    286            case "i":
    +
     2644.    286                break;
    +
     2645.    286            case "m":
    +
     2646.    286                break;
    +
     2647.    286            case "u":
    +
     2648.    286                break;
    +
     2649.    286            case "y":
    +
     2650.    286
    +
     2651.    286// test_cause:
    +
     2652.    286// ["aa=/./gimuy", "lex_regexp", "flag", "", 0]
    +
     2653.    286
    +
     2654.    286                test_cause("flag");
    +
     2655.    286                break;
    +
     2656.    286            default:
    +
     2657.    286
    +
     2658.    286// test_cause:
    +
     2659.    286// ["aa=/./gg", "lex_regexp", "unexpected_a", "g", 8]
    +
     2660.    286// ["aa=/./z", "lex_regexp", "unexpected_a", "z", 7]
    +
     2661.    286
    +
     2662.    286                warn_at("unexpected_a", line, column, char);
    +
     2663.    286            }
    +
     2664.    286            flag[char] = true;
    +
     2665.    286            char_after();
    +
     2666.    414        }
    +
     2667.    414        char_before();
    +
     2668.    414        if (char === "/" || char === "*") {
    +
     2669.      1
    +
     2670.      1// test_cause:
    +
     2671.      1// ["aa=/.//", "lex_regexp", "unexpected_a", "/", 3]
    +
     2672.      1
    +
     2673.      1            return stop_at("unexpected_a", line, from, char);
    +
     2674.    413        }
    +
     2675.    413        result = token_create("(regexp)", char);
    +
     2676.    413        result.flag = flag;
    +
     2677.    413        result.value = value;
    +
     2678.    413        if (mode_regexp_multiline && !flag.m) {
    +
     2679.      1
    +
     2680.      1// test_cause:
    +
     2681.      1// ["aa=/$^/", "lex_regexp", "missing_m", "", 7]
    +
     2682.      1
    +
     2683.      1            warn_at("missing_m", line, column);
    +
     2684.    413        }
    +
     2685.    413        return result;
    +
     2686.    413    }
    +
     2687.    634
    +
     2688.    439    function lex_slash_or_regexp() {
    +
     2689.    439
    +
     2690.    439// The / can be a division operator or the beginning of a regular expression
    +
     2691.    439// literal. It is not possible to know which without doing a complete parse.
    +
     2692.    439// We want to complete the tokenization before we begin to parse, so we will
    +
     2693.    439// estimate. This estimator can fail in some cases. For example, it cannot
    +
     2694.    439// know if "}" is ending a block or ending an object literal, so it can
    +
     2695.    439// behave incorrectly in that case; it is not meaningful to divide an
    +
     2696.    439// object, so it is likely that we can get away with it. We avoided the worst
    +
     2697.    439// cases by eliminating automatic semicolon insertion.
    +
     2698.    439
    +
     2699.    439        let the_token;
    +
     2700.    439        switch (
    +
     2701.    439            token_prv_expr.identifier
    +
     2702.     18            && !token_prv_expr.dot
    +
     2703.     15            && token_prv_expr.id
    +
     2704.    439        ) {
    +
     2705.      1        case "case":
    +
     2706.      2        case "delete":
    +
     2707.      3        case "in":
    +
     2708.      4        case "instanceof":
    +
     2709.      5        case "new":
    +
     2710.      6        case "typeof":
    +
     2711.      7        case "void":
    +
     2712.      8        case "yield":
    +
     2713.      8            the_token = lex_regexp();
    +
     2714.      8
    +
     2715.      8// test_cause:
    +
     2716.      8// ["case /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6]
    +
     2717.      8// ["delete /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8]
    +
     2718.      8// ["in /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 4]
    +
     2719.      8// ["instanceof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 12]
    +
     2720.      8// ["new /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 5]
    +
     2721.      8// ["typeof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8]
    +
     2722.      8// ["void /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6]
    +
     2723.      8// ["yield /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 7]
    +
     2724.      8
    +
     2725.      8            return stop("unexpected_a", the_token);
    +
     2726.      1        case "return":
    +
     2727.      1            return lex_regexp();
    +
     2728.    430        }
    +
     2729.    430        switch (!token_prv_expr.identifier && token_prv_expr.id.slice(-1)) {
    +
     2730.      1        case "!":
    +
     2731.      2        case "%":
    +
     2732.      4        case "&":
    +
     2733.      5        case "*":
    +
     2734.      6        case "+":
    +
     2735.      7        case "-":
    +
     2736.      9        case "/":
    +
     2737.     10        case ";":
    +
     2738.     11        case "<":
    +
     2739.     12        case ">":
    +
     2740.     13        case "^":
    +
     2741.     16        case "{":
    +
     2742.     17        case "|":
    +
     2743.     18        case "}":
    +
     2744.     19        case "~":
    +
     2745.     19            the_token = lex_regexp();
    +
     2746.     19
    +
     2747.     19// test_cause:
    +
     2748.     19// ["!/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     2749.     19// ["%/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     2750.     19// ["&/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     2751.     19// ["+/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     2752.     19// ["-/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     2753.     19// ["0 * /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5]
    +
     2754.     19// ["0 / /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5]
    +
     2755.     19// [";/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     2756.     19// ["</./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     2757.     19// [">/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     2758.     19// ["^/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     2759.     19// ["{/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     2760.     19// ["|/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     2761.     19// ["}/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     2762.     19// ["~/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2]
    +
     2763.     19
    +
     2764.     19            warn("wrap_regexp", the_token);
    +
     2765.     19            return the_token;
    +
     2766.    355        case "(":
    +
     2767.    356        case ",":
    +
     2768.    357        case ":":
    +
     2769.    394        case "=":
    +
     2770.    395        case "?":
    +
     2771.    396        case "[":
    +
     2772.    396
    +
     2773.    396// test_cause:
    +
     2774.    396// ["(/./", "lex_slash_or_regexp", "recurse", "", 0]
    +
     2775.    396// [",/./", "lex_slash_or_regexp", "recurse", "", 0]
    +
     2776.    396// [":/./", "lex_slash_or_regexp", "recurse", "", 0]
    +
     2777.    396// ["=/./", "lex_slash_or_regexp", "recurse", "", 0]
    +
     2778.    396// ["?/./", "lex_slash_or_regexp", "recurse", "", 0]
    +
     2779.    396// ["aa[/./", "lex_slash_or_regexp", "recurse", "", 0]
    +
     2780.    396
    +
     2781.    396            test_cause("recurse");
    +
     2782.    396            return lex_regexp();
    +
     2783.     15        }
    +
     2784.     15        if (line_source[0] === "=") {
    +
     2785.      1            column += 1;
    +
     2786.      1            line_source = line_source.slice(1);
    +
     2787.      1            snippet = "/=";
    +
     2788.      1            warn_at("unexpected_a", line, column, "/=");
    +
     2789.     15        }
    +
     2790.     15        return token_create(snippet);
    +
     2791.     15    }
    +
     2792.    634
    +
     2793.  23349    function lex_string(quote) {
    +
     2794.  23349
    +
     2795.  23349// Create a string token.
    +
     2796.  23349
    +
     2797.  23349        let the_token;
    +
     2798.  23347        if (!option_dict.single && quote === "'") {
    +
     2799.      2
    +
     2800.      2// test_cause:
    +
     2801.      2// ["''", "lex_string", "use_double", "", 1]
    +
     2802.      2
    +
     2803.      2            warn_at("use_double", line, column);
    +
     2804.      2        }
    +
     2805.  23349        snippet = "";
    +
     2806.  23349        char_after();
    +
     2807.  23349
    +
     2808.  23349// Lex/loop through each character in "...".
    +
     2809.  23349
    +
     2810. 205439        while (true) {
    +
     2811. 205439            switch (char) {
    +
     2812. 205439            case "":
    +
     2813. 205439
    +
     2814. 205439// test_cause:
    +
     2815. 205439// ["\"", "lex_string", "unclosed_string", "", 1]
    +
     2816. 205439
    +
     2817. 205439                return stop_at("unclosed_string", line, column);
    +
     2818. 205439            case "\\":
    +
     2819. 205439                char_after_escape(quote);
    +
     2820. 205439                break;
    +
     2821. 205439            case "`":
    +
     2822. 205439                if (mode_mega) {
    +
     2823. 205439
    +
     2824. 205439// test_cause:
    +
     2825. 205439// ["`${\"`\"}`", "lex_string", "unexpected_a", "`", 5]
    +
     2826. 205439
    +
     2827. 205439                    warn_at("unexpected_a", line, column, "`");
    +
     2828. 205439                }
    +
     2829. 205439                char_after("`");
    +
     2830. 205439                break;
    +
     2831. 205439            case quote:
    +
     2832. 205439
    +
     2833. 205439// Remove last character from snippet.
    +
     2834. 205439
    +
     2835. 205439                snippet = snippet.slice(0, -1);
    +
     2836. 205439                the_token = token_create("(string)", snippet);
    +
     2837. 205439                the_token.quote = quote;
    +
     2838. 205439                return the_token;
    +
     2839. 205439            default:
    +
     2840. 205439                char_after();
    +
     2841. 205439            }
    +
     2842. 205439        }
    +
     2843.  23349    }
    +
     2844.    634
    +
     2845. 240077    function lex_token() {
    +
     2846. 240077        let match;
    +
     2847. 240077
    +
     2848. 240077// Lex/loop through each whitespace.
    +
     2849. 240077
    +
     2850. 358587        while (true) {
    +
     2851. 358587
    +
     2852. 358587// Lex/loop through each blank-line.
    +
     2853. 358587
    +
     2854. 358587            while (!line_source) {
    +
     2855. 358587                line_source = read_line();
    +
     2856. 358587                from = 0;
    +
     2857. 358587                if (line_source === undefined) {
    +
     2858. 358587                    return (
    +
     2859. 358587                        mode_mega
    +
     2860. 358587
    +
     2861. 358587// test_cause:
    +
     2862. 358587// ["`${//}`", "lex_token", "unclosed_mega", "", 1]
    +
     2863. 358587
    +
     2864. 358587                        ? stop_at("unclosed_mega", line_mega, from_mega)
    +
     2865. 358587                        : line_disable !== undefined
    +
     2866. 358587
    +
     2867. 358587// test_cause:
    +
     2868. 358587// ["/*jslint-disable*/", "lex_token", "unclosed_disable", "", 1]
    +
     2869. 358587
    +
     2870. 358587                        ? stop_at("unclosed_disable", line_disable)
    +
     2871. 358587                        : token_create("(end)")
    +
     2872. 358587                    );
    +
     2873. 358587                }
    +
     2874. 358587            }
    +
     2875. 358587            from = column;
    +
     2876. 358587            match = line_source.match(rx_token);
    +
     2877. 358587
    +
     2878. 358587// match[1] token
    +
     2879. 358587// match[2] whitespace
    +
     2880. 358587// match[3] identifier
    +
     2881. 358587// match[4] number
    +
     2882. 358587// match[5] rest
    +
     2883. 358587
    +
     2884. 358587            if (!match) {
    +
     2885. 358587
    +
     2886. 358587// test_cause:
    +
     2887. 358587// ["#", "lex_token", "unexpected_char_a", "#", 1]
    +
     2888. 358587
    +
     2889. 358587                return stop_at(
    +
     2890. 358587                    "unexpected_char_a",
    +
     2891. 358587                    line,
    +
     2892. 358587                    column,
    +
     2893. 358587                    line_source[0]
    +
     2894. 358587                );
    +
     2895. 358587            }
    +
     2896. 358587            snippet = match[1];
    +
     2897. 358587            column += snippet.length;
    +
     2898. 358587            line_source = match[5];
    +
     2899. 358587            if (!match[2]) {
    +
     2900. 358587                break;
    +
     2901. 358587            }
    +
     2902. 358587        }
    +
     2903. 239468
    +
     2904. 239468// The token is an identifier.
    +
     2905. 239468
    +
     2906. 239468        if (match[3]) {
    +
     2907.  64347            return token_create(snippet, undefined, true);
    +
     2908. 175121        }
    +
     2909. 175121
    +
     2910. 175121// Create token from number.
    +
     2911. 175121
    +
     2912. 175121        if (match[4]) {
    +
     2913.   8735            return lex_number();
    +
     2914. 166386        }
    +
     2915. 166386
    +
     2916. 166386// Create token from string "..." or '...'.
    +
     2917. 166386
    +
     2918. 166386        if (snippet === "\"" || snippet === "'") {
    +
     2919.  23349            return lex_string(snippet);
    +
     2920. 143037        }
    +
     2921. 143037
    +
     2922. 143037// Create token from megastring `...`.
    +
     2923. 143037
    +
     2924. 143037        if (snippet === "`") {
    +
     2925.    714            return lex_megastring();
    +
     2926. 142323        }
    +
     2927. 142323
    +
     2928. 142323// Create token from comment /*...*/ or //....
    +
     2929. 142323
    +
     2930. 142323        if (snippet === "/*" || snippet === "//") {
    +
     2931.  10185            return lex_comment();
    +
     2932. 132138        }
    +
     2933. 132138
    +
     2934. 132138// Create token from slash /.
    +
     2935. 132138
    +
     2936. 132138        if (snippet === "/") {
    +
     2937.    439            return lex_slash_or_regexp();
    +
     2938. 131699        }
    +
     2939. 131699        return token_create(snippet);
    +
     2940. 131699    }
    +
     2941.    634
    +
     2942.   2527    function option_set_item(key, val) {
    +
     2943.   2527
    +
     2944.   2527// These are the options that are recognized in the option object or that may
    +
     2945.   2527// appear in a /*jslint*/ directive. Most options will have a boolean value,
    +
     2946.   2527// usually true. Some options will also predefine some number of global
    +
     2947.   2527// variables.
    +
     2948.   2527
    +
     2949.   2527        switch (key) {
    +
     2950.    627        case "beta":            // Enable experimental warnings.
    +
     2951.    629        case "bitwise":         // Allow bitwise operators.
    +
     2952.    634        case "browser":         // Assume browser environment.
    +
     2953.    636        case "convert":         // Allow conversion operators.
    +
     2954.    638        case "couch":           // Assume CouchDb environment.
    +
     2955.    643        case "devel":           // Allow console.log() and friends.
    +
     2956.   1911        case "ecma":            // Assume ECMAScript environment.
    +
     2957.   1915        case "eval":            // Allow eval().
    +
     2958.   1920        case "for":             // Allow for-statement.
    +
     2959.   1926        case "getset":          // Allow get() and set().
    +
     2960.   1931        case "indent2":         // Use 2-space indent.
    +
     2961.   1933        case "long":            // Allow long lines.
    +
     2962.   1935        case "name":            // Allow weird property names.
    +
     2963.   1977        case "node":            // Assume Node.js environment.
    +
     2964.   1979        case "single":          // Allow single-quote strings.
    +
     2965.   2474        case "test_cause":      // Test jslint's causes.
    +
     2966.   2475        case "test_internal_error":     // Test jslint's internal-error
    +
     2967.   2527                                        // ... handling-ability.
    +
     2968.   2477        case "this":            // Allow 'this'.
    +
     2969.   2480        case "trace":           // Include jslint stack-trace in warnings.
    +
     2970.   2484        case "unordered":       // Allow unordered cases, params, properties,
    +
     2971.   2527                                // ... and variables.
    +
     2972.   2488        case "variable":        // Allow unordered const and let declarations
    +
     2973.   2527                                // ... that are not at top of function-scope.
    +
     2974.   2490        case "white":           // Allow messy whitespace.
    +
     2975.   2490            option_dict[key] = val;
    +
     2976.   2490            break;
    +
     2977.     37        default:
    +
     2978.     37            return false;
    +
     2979.   2490        }
    +
     2980.   2490
    +
     2981.   2490// Initialize global-variables.
    +
     2982.   2490
    +
     2983.   2490        switch (val && key) {
    +
     2984.   2527
    +
     2985.   2527// Assign global browser variables to global_dict.
    +
     2986.   2527/*
    +
     2987.   2527// /\*jslint beta, browser, devel*\/
    +
     2988.   2527console.log(JSON.stringify(Object.keys(window).sort(), undefined, 4));
    +
     2989.   2527*/
    +
     2990.   2527
    +
     2991.      4        case "browser":
    +
     2992.      4            object_assign_from_list(global_dict, [
    +
     2993.      4
    +
     2994.      4// Shared with Node.js.
    +
     2995.      4
    +
     2996.      4                "AbortController",
    +
     2997.      4                // "Buffer",
    +
     2998.      4                "DOMException",
    +
     2999.      4                "Event",
    +
     3000.      4                "EventTarget",
    +
     3001.      4                "MessageChannel",
    +
     3002.      4                "MessageEvent",
    +
     3003.      4                "MessagePort",
    +
     3004.      4                "TextDecoder",
    +
     3005.      4                "TextEncoder",
    +
     3006.      4                "URL",
    +
     3007.      4                "URLSearchParams",
    +
     3008.      4                "WebAssembly",
    +
     3009.      4                // "__dirname",
    +
     3010.      4                // "__filename",
    +
     3011.      4                // "atob",
    +
     3012.      4                // "btoa",
    +
     3013.      4                // "clearImmediate",
    +
     3014.      4                "clearInterval",
    +
     3015.      4                "clearTimeout",
    +
     3016.      4                // "console",
    +
     3017.      4                // "exports",
    +
     3018.      4                // "global",
    +
     3019.      4                // "module",
    +
     3020.      4                "performance",
    +
     3021.      4                // "process",
    +
     3022.      4                "queueMicrotask",
    +
     3023.      4                // "require",
    +
     3024.      4                // "setImmediate",
    +
     3025.      4                "setInterval",
    +
     3026.      4                "setTimeout",
    +
     3027.      4                "structuredClone",
    +
     3028.      4
    +
     3029.      4// Web worker only.
    +
     3030.      4// https://github.com/mdn/content/blob/main/files/en-us/web/api
    +
     3031.      4// /workerglobalscope/index.md
    +
     3032.      4
    +
     3033.      4                "importScripts",
    +
     3034.      4
    +
     3035.      4// Window.
    +
     3036.      4
    +
     3037.      4                "Blob",
    +
     3038.      4                // "CharacterData",
    +
     3039.      4                // "DocumentType",
    +
     3040.      4                // "Element",
    +
     3041.      4                // "Event",
    +
     3042.      4                "FileReader",
    +
     3043.      4                // "FontFace",
    +
     3044.      4                "FormData",
    +
     3045.      4                "IntersectionObserver",
    +
     3046.      4                "MutationObserver",
    +
     3047.      4                // "Storage",
    +
     3048.      4                // "TextDecoder",
    +
     3049.      4                // "TextEncoder",
    +
     3050.      4                // "URL",
    +
     3051.      4                "Worker",
    +
     3052.      4                "XMLHttpRequest",
    +
     3053.      4                // "caches",
    +
     3054.      4                // "clearInterval",
    +
     3055.      4                // "clearTimeout",
    +
     3056.      4                "document",
    +
     3057.      4                // "event",
    +
     3058.      4                "fetch",
    +
     3059.      4                // "history",
    +
     3060.      4                "indexedDb",
    +
     3061.      4                "localStorage",
    +
     3062.      4                "location",
    +
     3063.      4                // "name",
    +
     3064.      4                "navigator",
    +
     3065.      4                // "screen",
    +
     3066.      4                "sessionStorage",
    +
     3067.      4                // "setInterval",
    +
     3068.      4                // "setTimeout",
    +
     3069.      4                "window"
    +
     3070.      4            ], "browser");
    +
     3071.      4            break;
    +
     3072.   2527
    +
     3073.   2527// https://docs.couchdb.org/en/stable/query-server/javascript.html#javascript
    +
     3074.   2527
    +
     3075.      2        case "couch":
    +
     3076.      2            object_assign_from_list(global_dict, [
    +
     3077.      2                "emit",
    +
     3078.      2                "getRow",
    +
     3079.      2                "isArray",
    +
     3080.      2                "log",
    +
     3081.      2                "provides",
    +
     3082.      2                "registerType",
    +
     3083.      2                "require",
    +
     3084.      2                "send",
    +
     3085.      2                "start",
    +
     3086.      2                "sum",
    +
     3087.      2                "toJSON"
    +
     3088.      2            ], "CouchDb");
    +
     3089.      2            break;
    +
     3090.      5        case "devel":
    +
     3091.      5            object_assign_from_list(global_dict, [
    +
     3092.      5                "alert", "confirm", "console", "prompt"
    +
     3093.      5            ], "development");
    +
     3094.      5            break;
    +
     3095.   2527
    +
     3096.   2527// These are the globals that are provided by the language standard.
    +
     3097.   2527// Assign global ECMAScript variables to global_dict.
    +
     3098.   2527/*
    +
     3099.   2527node --input-type=module --eval '
    +
     3100.   2527// /\*jslint beta, node*\/
    +
     3101.   2527import https from "https";
    +
     3102.   2527(async function () {
    +
     3103.   2527    let dict = {import: true};
    +
     3104.   2527    let result = "";
    +
     3105.   2527    await new Promise(function (resolve) {
    +
     3106.   2527        https.get((
    +
     3107.   2527            "https://raw.githubusercontent.com/mdn/content/main/files"
    +
     3108.   2527            + "/en-us/web/javascript/reference/global_objects/index.md"
    +
     3109.   2527        ), function (res) {
    +
     3110.   2527            res.on("data", function (chunk) {
    +
     3111.   2527                result += chunk;
    +
     3112.   2527            }).on("end", resolve).setEncoding("utf8");
    +
     3113.   2527        });
    +
     3114.   2527    });
    +
     3115.   2527    result.replace((
    +
     3116.   2527        /\n- \{\{JSxRef\("(?:Global_Objects\/)?([^"\/]+?)"/g
    +
     3117.   2527    ), function (ignore, key) {
    +
     3118.   2527        if (globalThis.hasOwnProperty(key)) {
    +
     3119.   2527            dict[key] = true;
    +
     3120.   2527        }
    +
     3121.   2527        return "";
    +
     3122.   2527    });
    +
     3123.   2527    console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4));
    +
     3124.   2527}());
    +
     3125.   2527'
    +
     3126.   2527*/
    +
     3127.   2527
    +
     3128.   1268        case "ecma":
    +
     3129.   1268            object_assign_from_list(global_dict, [
    +
     3130.   1268                "Array",
    +
     3131.   1268                "ArrayBuffer",
    +
     3132.   1268                "Atomics",
    +
     3133.   1268                "BigInt",
    +
     3134.   1268                "BigInt64Array",
    +
     3135.   1268                "BigUint64Array",
    +
     3136.   1268                "Boolean",
    +
     3137.   1268                "DataView",
    +
     3138.   1268                "Date",
    +
     3139.   1268                "Error",
    +
     3140.   1268                "EvalError",
    +
     3141.   1268                "Float32Array",
    +
     3142.   1268                "Float64Array",
    +
     3143.   1268                "Function",
    +
     3144.   1268                "Infinity",
    +
     3145.   1268                "Int16Array",
    +
     3146.   1268                "Int32Array",
    +
     3147.   1268                "Int8Array",
    +
     3148.   1268                "Intl",
    +
     3149.   1268                "JSON",
    +
     3150.   1268                "Map",
    +
     3151.   1268                "Math",
    +
     3152.   1268                "NaN",
    +
     3153.   1268                "Number",
    +
     3154.   1268                "Object",
    +
     3155.   1268                "Promise",
    +
     3156.   1268                "Proxy",
    +
     3157.   1268                "RangeError",
    +
     3158.   1268                "ReferenceError",
    +
     3159.   1268                "Reflect",
    +
     3160.   1268                "RegExp",
    +
     3161.   1268                "Set",
    +
     3162.   1268                "SharedArrayBuffer",
    +
     3163.   1268                "String",
    +
     3164.   1268                "Symbol",
    +
     3165.   1268                "SyntaxError",
    +
     3166.   1268                "TypeError",
    +
     3167.   1268                "URIError",
    +
     3168.   1268                "Uint16Array",
    +
     3169.   1268                "Uint32Array",
    +
     3170.   1268                "Uint8Array",
    +
     3171.   1268                "Uint8ClampedArray",
    +
     3172.   1268                "WeakMap",
    +
     3173.   1268                "WeakSet",
    +
     3174.   1268                "WebAssembly",
    +
     3175.   1268                "decodeURI",
    +
     3176.   1268                "decodeURIComponent",
    +
     3177.   1268                "encodeURI",
    +
     3178.   1268                "encodeURIComponent",
    +
     3179.   1268                "eval",
    +
     3180.   1268                "globalThis",
    +
     3181.   1268                "import",
    +
     3182.   1268                "isFinite",
    +
     3183.   1268                "isNaN",
    +
     3184.   1268                "parseFloat",
    +
     3185.   1268                "parseInt",
    +
     3186.   1268                "undefined"
    +
     3187.   1268            ], "ECMAScript");
    +
     3188.   1268            break;
    +
     3189.   2527
    +
     3190.   2527// Assign global Node.js variables to global_dict.
    +
     3191.   2527/*
    +
     3192.   2527node --input-type=module --eval '
    +
     3193.   2527// /\*jslint beta, node*\/
    +
     3194.   2527import moduleHttps from "https";
    +
     3195.   2527(async function () {
    +
     3196.   2527    let dict = {};
    +
     3197.   2527    let result = "";
    +
     3198.   2527    await new Promise(function (resolve) {
    +
     3199.   2527        moduleHttps.get((
    +
     3200.   2527            "https://raw.githubusercontent.com/nodejs/node/master/doc/api"
    +
     3201.   2527            + "/globals.md"
    +
     3202.   2527        ), function (res) {
    +
     3203.   2527            res.on("data", function (chunk) {
    +
     3204.   2527                result += chunk;
    +
     3205.   2527            }).on("end", resolve).setEncoding("utf8");
    +
     3206.   2527        });
    +
     3207.   2527    });
    +
     3208.   2527    result.replace((
    +
     3209.   2527        /\n(?:\* \[`|## |## Class: )`\w+/g
    +
     3210.   2527    ), function (match0) {
    +
     3211.   2527        dict[match0.split("`")[1]] = true;
    +
     3212.   2527        return "";
    +
     3213.   2527    });
    +
     3214.   2527    console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4));
    +
     3215.   2527}());
    +
     3216.   2527'
    +
     3217.   2527*/
    +
     3218.   2527
    +
     3219.     42        case "node":
    +
     3220.     42            object_assign_from_list(global_dict, [
    +
     3221.     42                "AbortController",
    +
     3222.     42                "Buffer",
    +
     3223.     42                "DOMException",
    +
     3224.     42                "Event",
    +
     3225.     42                "EventTarget",
    +
     3226.     42                "MessageChannel",
    +
     3227.     42                "MessageEvent",
    +
     3228.     42                "MessagePort",
    +
     3229.     42                "TextDecoder",
    +
     3230.     42                "TextEncoder",
    +
     3231.     42                "URL",
    +
     3232.     42                "URLSearchParams",
    +
     3233.     42                "WebAssembly",
    +
     3234.     42                "__dirname",
    +
     3235.     42                "__filename",
    +
     3236.     42                // "atob",
    +
     3237.     42                // "btoa",
    +
     3238.     42                "clearImmediate",
    +
     3239.     42                "clearInterval",
    +
     3240.     42                "clearTimeout",
    +
     3241.     42                "console",
    +
     3242.     42                "exports",
    +
     3243.     42                "global",
    +
     3244.     42                "module",
    +
     3245.     42                "performance",
    +
     3246.     42                "process",
    +
     3247.     42                "queueMicrotask",
    +
     3248.     42                "require",
    +
     3249.     42                "setImmediate",
    +
     3250.     42                "setInterval",
    +
     3251.     42                "setTimeout",
    +
     3252.     42                "structuredClone"
    +
     3253.     42            ], "Node.js");
    +
     3254.     42            break;
    +
     3255.   2490        }
    +
     3256.   2490        return true;
    +
     3257.   2490    }
    +
     3258.    634
    +
     3259.    342    function read_digits(base, quiet) {
    +
     3260.    342        let digits = line_source.match(
    +
     3261.    342            base === "b"
    +
     3262.      2            ? (
    +
     3263.      2                // rx_bits
    +
     3264.      2                /^[01]*/
    +
     3265.      2            )
    +
     3266.    340            : base === "o"
    +
     3267.    340            ? (
    +
     3268.    340                // rx_octals
    +
     3269.    340                /^[0-7]*/
    +
     3270.    340            )
    +
     3271.    340            : base === "x"
    +
     3272.    340            ? (
    +
     3273.    340                // rx_hexs
    +
     3274.    340                /^[0-9A-F]*/i
    +
     3275.    340            )
    +
     3276.    340            : (
    +
     3277.    340                // rx_digits
    +
     3278.    340                /^[0-9]*/
    +
     3279.    340            )
    +
     3280.    342        )[0];
    +
     3281.    342        let length = digits.length;
    +
     3282.     57        if (!quiet && length === 0) {
    +
     3283.      1
    +
     3284.      1// test_cause:
    +
     3285.      1// ["0x", "read_digits", "expected_digits_after_a", "0x", 2]
    +
     3286.      1
    +
     3287.      1            warn_at("expected_digits_after_a", line, column, snippet);
    +
     3288.      1        }
    +
     3289.    342        column += length;
    +
     3290.    342        line_source = line_source.slice(length);
    +
     3291.    342        snippet += digits;
    +
     3292.    342        char_after();
    +
     3293.    342        return length;
    +
     3294.    342    }
    +
     3295.    634
    +
     3296.  93041    function read_line() {
    +
     3297.  93041
    +
     3298.  93041// Put the next line of source in line_source. If the line contains tabs,
    +
     3299.  93041// replace them with spaces and give a warning. Also warn if the line contains
    +
     3300.  93041// unsafe characters or is too damn long.
    +
     3301.  93041
    +
     3302.  93041        if (
    +
     3303.  93041            !option_dict.long
    +
     3304.  93037            && line_whole.length > 80
    +
     3305.     48            && line_disable === undefined
    +
     3306.     48            && !state.mode_json
    +
     3307.     15            && token_1
    +
     3308.     15            && !mode_regexp
    +
     3309.      8        ) {
    +
     3310.      8
    +
     3311.      8// test_cause:
    +
     3312.      8// ["/////////////////////////////////////////////////////////////////////////////////", "read_line", "too_long", "", 1] //jslint-quiet
    +
     3313.      8
    +
     3314.      8            warn_at("too_long", line);
    +
     3315.      8        }
    +
     3316.  93041        column = 0;
    +
     3317.  93041        line += 1;
    +
     3318.  93041        mode_regexp = false;
    +
     3319.  93041        line_source = undefined;
    +
     3320.  93041        line_whole = "";
    +
     3321.    611        if (line_list[line] === undefined) {
    +
     3322.    611            return line_source;
    +
     3323.  92430        }
    +
     3324.  92430        line_source = line_list[line].line_source;
    +
     3325.  92430        line_whole = line_source;
    +
     3326.  92430
    +
     3327.  92430// Scan each line for following ignore-directives:
    +
     3328.  92430// "/*jslint-disable*/"
    +
     3329.  92430// "/*jslint-enable*/"
    +
     3330.  92430// "//jslint-quiet"
    +
     3331.  92430
    +
     3332.  92430        if (line_source === "/*jslint-disable*/") {
    +
     3333.      5
    +
     3334.      5// test_cause:
    +
     3335.      5// ["/*jslint-disable*/", "read_line", "jslint_disable", "", 0]
    +
     3336.      5
    +
     3337.      5            test_cause("jslint_disable");
    +
     3338.      5            line_disable = line;
    +
     3339.  92425        } else if (line_source === "/*jslint-enable*/") {
    +
     3340.  92425            if (line_disable === undefined) {
    +
     3341.  92425
    +
     3342.  92425// test_cause:
    +
     3343.  92425// ["/*jslint-enable*/", "read_line", "unopened_enable", "", 1]
    +
     3344.  92425
    +
     3345.  92425                stop_at("unopened_enable", line);
    +
     3346.  92425            }
    +
     3347.  92425            line_disable = undefined;
    +
     3348.  92425        } else if (line_source.endsWith(" //jslint-quiet")) {
    +
     3349.  92425
    +
     3350.  92425// test_cause:
    +
     3351.  92425// ["0 //jslint-quiet", "read_line", "jslint_quiet", "", 0]
    +
     3352.  92425
    +
     3353.  92425            test_cause("jslint_quiet");
    +
     3354.  92425            line_list[line].directive_quiet = true;
    +
     3355.  92429        }
    +
     3356.  92429        if (line_disable !== undefined) {
    +
     3357.      8
    +
     3358.      8// test_cause:
    +
     3359.      8// ["/*jslint-disable*/\n0", "read_line", "line_disable", "", 0]
    +
     3360.      8
    +
     3361.      8            test_cause("line_disable");
    +
     3362.      8            line_source = "";
    +
     3363.  92429        }
    +
     3364.  92429        if (line_source.indexOf("\t") >= 0) {
    +
     3365.      3            if (!option_dict.white) {
    +
     3366.      3
    +
     3367.      3// test_cause:
    +
     3368.      3// ["\t", "read_line", "use_spaces", "", 1]
    +
     3369.      3
    +
     3370.      3                warn_at("use_spaces", line, line_source.indexOf("\t") + 1);
    +
     3371.      3            }
    +
     3372.      3            line_source = line_source.replace((
    +
     3373.      3                // rx_tab
    +
     3374.      3                /\t/g
    +
     3375.      3            ), " ");
    +
     3376.  92429        }
    +
     3377.  92429        if (!option_dict.white && line_source.endsWith(" ")) {
    +
     3378.      2
    +
     3379.      2// test_cause:
    +
     3380.      2// [" ", "read_line", "unexpected_trailing_space", "", 1]
    +
     3381.      2
    +
     3382.      2            warn_at("unexpected_trailing_space", line, line_source.length - 1);
    +
     3383.  92429        }
    +
     3384.  92429        return line_source;
    +
     3385.  92429    }
    +
     3386.    634
    +
     3387. 242781    function token_create(id, value, identifier) {
    +
     3388. 242781
    +
     3389. 242781// Create the token object and append it to token_list.
    +
     3390. 242781
    +
     3391. 242781        let the_token = {
    +
     3392. 242781            from,
    +
     3393. 242781            id,
    +
     3394. 242781            identifier: Boolean(identifier),
    +
     3395. 242781            line,
    +
     3396. 242781            nr: token_list.length,
    +
     3397. 242781            thru: column
    +
     3398. 242781        };
    +
     3399. 242781        token_list.push(the_token);
    +
     3400. 242781
    +
     3401. 242781// Directives must appear before the first statement.
    +
     3402. 242781
    +
     3403. 232599        if (id !== "(comment)" && id !== ";") {
    +
     3404. 217430            mode_directive = false;
    +
     3405. 217430        }
    +
     3406. 242781
    +
     3407. 242781// If the token is to have a value, give it one.
    +
     3408. 242781
    +
     3409.  44040        if (value !== undefined) {
    +
     3410.  44040            the_token.value = value;
    +
     3411.  44040        }
    +
     3412. 242781
    +
     3413. 242781// If this token is an identifier that touches a preceding number, or
    +
     3414. 242781// a "/", comment, or regular expression literal that touches a preceding
    +
     3415. 242781// comment or regular expression literal, then give a missing space warning.
    +
     3416. 242781// This warning is not suppressed by option_dict.white.
    +
     3417. 242781
    +
     3418. 242781        if (
    +
     3419. 242781            token_prv.line === line
    +
     3420. 177185            && token_prv.thru === from
    +
     3421. 114116            && (id === "(comment)" || id === "(regexp)" || id === "/")
    +
     3422.    109            && (token_prv.id === "(comment)" || token_prv.id === "(regexp)")
    +
     3423.      1        ) {
    +
     3424.      1
    +
     3425.      1// test_cause:
    +
     3426.      1// ["/**//**/", "token_create", "expected_space_a_b", "(comment)", 5]
    +
     3427.      1
    +
     3428.      1            warn(
    +
     3429.      1                "expected_space_a_b",
    +
     3430.      1                the_token,
    +
     3431.      1                artifact(token_prv),
    +
     3432.      1                artifact(the_token)
    +
     3433.      1            );
    +
     3434.      1        }
    +
     3435.  11087        if (token_prv.id === "." && id === "(number)") {
    +
     3436.      4
    +
     3437.      4// test_cause:
    +
     3438.      4// [".0", "token_create", "expected_a_before_b", ".", 1]
    +
     3439.      4
    +
     3440.      4            warn("expected_a_before_b", token_prv, "0", ".");
    +
     3441.      4        }
    +
     3442.  11087        if (token_prv_expr.id === "." && the_token.identifier) {
    +
     3443.  11082            the_token.dot = true;
    +
     3444.  11082        }
    +
     3445. 242781
    +
     3446. 242781// The previous token is used to detect adjacency problems.
    +
     3447. 242781
    +
     3448. 242781        token_prv = the_token;
    +
     3449. 242781
    +
     3450. 242781// The token_prv_expr token is a previous token that was not a comment.
    +
     3451. 242781// The token_prv_expr token
    +
     3452. 242781// is used to disambiguate "/", which can mean division or regular expression
    +
     3453. 242781// literal.
    +
     3454. 242781
    +
     3455. 232599        if (token_prv.id !== "(comment)") {
    +
     3456. 232599            token_prv_expr = token_prv;
    +
     3457. 232599        }
    +
     3458. 242781        return the_token;
    +
     3459. 242781    }
    +
     3460.    634
    +
     3461.    634// Init global_dict and option_dict.
    +
     3462.    634
    +
     3463.    634    option_set_item("ecma", true);
    +
     3464.   1809    Object.keys(option_dict).sort().forEach(function (key) {
    +
     3465.   1809        option_set_item(key, option_dict[key] === true);
    +
     3466.   1809    });
    +
     3467.    634    object_assign_from_list(global_dict, global_list, "user-defined");
    +
     3468.    634
    +
     3469.    634// Scan first line for "#!" and ignore it.
    +
     3470.    634
    +
     3471.      1    if (line_list[jslint_fudge].line_source.startsWith("#!")) {
    +
     3472.      1        line += 1;
    +
     3473.      1        state.mode_shebang = true;
    +
     3474.      1    }
    +
     3475.    634    token_1 = lex_token();
    +
     3476.    607    state.mode_json = token_1.id === "{" || token_1.id === "[";
    +
     3477.    634
    +
     3478.    634// Lex/loop through each token until (end).
    +
     3479.    634
    +
     3480. 237485    while (true) {
    +
     3481. 237485        if (lex_token().id === "(end)") {
    +
     3482. 237485            break;
    +
     3483. 237485        }
    +
     3484. 237485    }
    +
     3485.    634}
    +
     3486.      1
    +
     3487.    597function jslint_phase3_parse(state) {
    +
     3488.    597
    +
     3489.    597// PHASE 3. Parse <token_list> into <token_tree> using the Pratt-parser.
    +
     3490.    597
    +
     3491.    597// Parsing:
    +
     3492.    597
    +
     3493.    597// Parsing weaves the tokens into an abstract syntax tree. During that process,
    +
     3494.    597// a token may be given any of these properties:
    +
     3495.    597
    +
     3496.    597//      arity       string
    +
     3497.    597//      label       identifier
    +
     3498.    597//      name        identifier
    +
     3499.    597//      expression  expressions
    +
     3500.    597//      block       statements
    +
     3501.    597//      else        statements (else, default, catch)
    +
     3502.    597
    +
     3503.    597// Specialized tokens may have additional properties.
    +
     3504.    597
    +
     3505.    597    let anon = "anonymous";     // The guessed name for anonymous functions.
    +
     3506.    597    let {
    +
     3507.    597        artifact,
    +
     3508.    597        catch_list,
    +
     3509.    597        catch_stack,
    +
     3510.    597        export_dict,
    +
     3511.    597        function_list,
    +
     3512.    597        function_stack,
    +
     3513.    597        global_dict,
    +
     3514.    597        import_list,
    +
     3515.    597        is_equal,
    +
     3516.    597        option_dict,
    +
     3517.    597        property_dict,
    +
     3518.    597        stop,
    +
     3519.    597        syntax_dict,
    +
     3520.    597        tenure,
    +
     3521.    597        test_cause,
    +
     3522.    597        token_global,
    +
     3523.    597        token_list,
    +
     3524.    597        warn,
    +
     3525.    597        warn_at
    +
     3526.    597    } = state;
    +
     3527.    597    let catchage = catch_stack[0];      // The current catch-block.
    +
     3528.    597    let functionage = token_global;     // The current function.
    +
     3529.    597    let mode_var;               // "var" if using var; "let" if using let.
    +
     3530.    597    let rx_identifier = (
    +
     3531.    597        /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/
    +
     3532.    597    );
    +
     3533.    597    let token_ii = 0;           // The number of the next token.
    +
     3534.    597    let token_now = token_global;       // The current token being examined in
    +
     3535.    597                                        // ... the parse.
    +
     3536.    597    let token_nxt = token_global;       // The next token to be examined in
    +
     3537.    597                                        // ... <token_list>.
    +
     3538.    597
    +
     3539. 232890    function advance(id, match) {
    +
     3540. 232890
    +
     3541. 232890// Produce the next token.
    +
     3542. 232890
    +
     3543. 232890// Attempt to give helpful names to anonymous functions.
    +
     3544. 232890
    +
     3545.  64306        if (token_now.identifier && token_now.id !== "function") {
    +
     3546.  62445            anon = token_now.id;
    +
     3547. 170445        } else if (
    +
     3548. 170445            token_now.id === "(string)"
    +
     3549. 170445            && rx_identifier.test(token_now.value)
    +
     3550. 170445        ) {
    +
     3551. 170445            anon = token_now.value;
    +
     3552. 170445        }
    +
     3553. 232890
    +
     3554. 232890// Attempt to match token_nxt with an expected id.
    +
     3555. 232890
    +
     3556. 115306        if (id !== undefined && token_nxt.id !== id) {
    +
     3557.     26            return (
    +
     3558.     26                match === undefined
    +
     3559.     26
    +
     3560.     26// test_cause:
    +
     3561.     26// ["()", "advance", "expected_a_b", "(end)", 1]
    +
     3562.     26
    +
     3563.     26                ? stop("expected_a_b", token_nxt, id, artifact())
    +
     3564.     26
    +
     3565.     26// test_cause:
    +
     3566.     26// ["{\"aa\":0", "advance", "expected_a_b_from_c_d", "{", 1]
    +
     3567.     26
    +
     3568.     26                : stop(
    +
     3569.     26                    "expected_a_b_from_c_d",
    +
     3570.     26                    token_nxt,
    +
     3571.     26                    id,
    +
     3572.     26                    artifact(match),
    +
     3573.     26                    match.line,
    +
     3574.     26                    artifact()
    +
     3575.     26                )
    +
     3576.     26            );
    +
     3577. 232864        }
    +
     3578. 232864
    +
     3579. 232864// Promote the tokens, skipping comments.
    +
     3580. 232864
    +
     3581. 232864        token_now = token_nxt;
    +
     3582. 243043        while (true) {
    +
     3583. 243043            token_nxt = token_list[token_ii];
    +
     3584. 243043            state.token_nxt = token_nxt;
    +
     3585. 243043            token_ii += 1;
    +
     3586. 243043            if (token_nxt.id !== "(comment)") {
    +
     3587. 243043                if (token_nxt.id === "(end)") {
    +
     3588. 243043                    token_ii -= 1;
    +
     3589. 243043                }
    +
     3590. 243043                break;
    +
     3591. 243043            }
    +
     3592. 243043            if (state.mode_json) {
    +
     3593. 243043
    +
     3594. 243043// test_cause:
    +
     3595. 243043// ["[//]", "advance", "unexpected_a", "(comment)", 2]
    +
     3596. 243043
    +
     3597. 243043                warn("unexpected_a");
    +
     3598. 243043            }
    +
     3599. 243043        }
    +
     3600. 232890    }
    +
     3601.    597
    +
     3602.   7164    function assignment(id) {
    +
     3603.   7164
    +
     3604.   7164// Create an assignment operator. The one true assignment is different because
    +
     3605.   7164// its left side, when it is a variable, is not treated as an expression.
    +
     3606.   7164// That case is special because that is when a variable gets initialized. The
    +
     3607.   7164// other assignment operators can modify, but they cannot initialize.
    +
     3608.   7164
    +
     3609.   7164        const the_symbol = symbol(id, 20);
    +
     3610.   4788        the_symbol.led = function (left) {
    +
     3611.   4788            const the_token = token_now;
    +
     3612.   4788            let right;
    +
     3613.   4788            the_token.arity = "assignment";
    +
     3614.   4788            right = parse_expression(20 - 1);
    +
     3615.   4064            if (id === "=" && left.arity === "variable") {
    +
     3616.   2624                the_token.names = left;
    +
     3617.   2624                the_token.expression = right;
    +
     3618.   2624            } else {
    +
     3619.   2160                the_token.expression = [left, right];
    +
     3620.   4784            }
    +
     3621.   4784            if (
    +
     3622.   4784                right.arity === "assignment"
    +
     3623.   4784                || right.arity === "preassign"
    +
     3624.   4782                || right.arity === "postassign"
    +
     3625.      2            ) {
    +
     3626.      2                warn("unexpected_a", right);
    +
     3627.   4784            }
    +
     3628.   4784            check_mutation(left);
    +
     3629.   4784            return the_token;
    +
     3630.   4784        };
    +
     3631.   7164        return the_symbol;
    +
     3632.   7164    }
    +
     3633.    597
    +
     3634.   5540    function block(special) {
    +
     3635.   5540
    +
     3636.   5540// Parse a block, a sequence of statements wrapped in braces.
    +
     3637.   5540//  special "body"      The block is a function body.
    +
     3638.   5540//          "ignore"    No warning on an empty block.
    +
     3639.   5540//          "naked"     No advance.
    +
     3640.   5540//          undefined   An ordinary block.
    +
     3641.   5540
    +
     3642.   5540        let stmts;
    +
     3643.   5540        let the_block;
    +
     3644.   5535        if (special !== "naked") {
    +
     3645.   5535            advance("{");
    +
     3646.   5539        }
    +
     3647.   5539        the_block = token_now;
    +
     3648.   5539        if (special !== "body") {
    +
     3649.   3674            functionage.statement_prv = the_block;
    +
     3650.   5539        }
    +
     3651.   5539        the_block.arity = "statement";
    +
     3652.   5539        the_block.body = special === "body";
    +
     3653.   5539
    +
     3654.   5539// Top level function bodies may include the "use strict" pragma.
    +
     3655.   5539
    +
     3656.   5539        if (
    +
     3657.   5539            special === "body"
    +
     3658.   5539            && function_stack.length === 1
    +
     3659.    258            && token_nxt.value === "use strict"
    +
     3660.      2        ) {
    +
     3661.      2            token_nxt.statement = true;
    +
     3662.      2            advance("(string)");
    +
     3663.      2            advance(";");
    +
     3664.   5539        }
    +
     3665.   5539        stmts = parse_statements();
    +
     3666.   5539        the_block.block = stmts;
    +
     3667.   5539        if (stmts.length === 0) {
    +
     3668.     72            if (!option_dict.devel && special !== "ignore") {
    +
     3669.     72
    +
     3670.     72// test_cause:
    +
     3671.     72// ["function aa(){}", "block", "empty_block", "{", 14]
    +
     3672.     72
    +
     3673.     72                warn("empty_block", the_block);
    +
     3674.     72            }
    +
     3675.     72            the_block.disrupt = false;
    +
     3676.   5459        } else {
    +
     3677.   5459            the_block.disrupt = stmts[stmts.length - 1].disrupt;
    +
     3678.   5531        }
    +
     3679.   5531        advance("}");
    +
     3680.   5531        return the_block;
    +
     3681.   5531    }
    +
     3682.    597
    +
     3683.  22096    function check_left(left, right) {
    +
     3684.  22096
    +
     3685.  22096// Warn if the left is not one of these:
    +
     3686.  22096//      ?.
    +
     3687.  22096//      ?:
    +
     3688.  22096//      e()
    +
     3689.  22096//      e.b
    +
     3690.  22096//      e[b]
    +
     3691.  22096//      identifier
    +
     3692.  22096
    +
     3693.  22096        const id = left.id;
    +
     3694.  22096        if (
    +
     3695.  22096            !left.identifier
    +
     3696.   5784            && (
    +
     3697.   5784                left.arity !== "ternary"
    +
     3698.   5784                || (
    +
     3699.   5784                    !check_left(left.expression[1])
    +
     3700.   5784                    && !check_left(left.expression[2])
    +
     3701.   5784                )
    +
     3702.   5784            )
    +
     3703.   5784            && (
    +
     3704.   5784                left.arity !== "binary"
    +
     3705.   5784                || (id !== "." && id !== "?." && id !== "(" && id !== "[")
    +
     3706.   5784            )
    +
     3707.     11        ) {
    +
     3708.     11            warn("unexpected_a", right || token_nxt);
    +
     3709.     11            return false;
    +
     3710.  22085        }
    +
     3711.  22085        return true;
    +
     3712.  22085    }
    +
     3713.    597
    +
     3714.   4787    function check_mutation(the_thing) {
    +
     3715.   4787
    +
     3716.   4787// The only expressions that may be assigned to are
    +
     3717.   4787//      e.b
    +
     3718.   4787//      e[b]
    +
     3719.   4787//      v
    +
     3720.   4787//      [destructure]
    +
     3721.   4787//      {destructure}
    +
     3722.   4787
    +
     3723.   4787        if (
    +
     3724.   4787            the_thing.arity !== "variable"
    +
     3725.   1567            && the_thing.id !== "."
    +
     3726.    174            && the_thing.id !== "["
    +
     3727.      7            && the_thing.id !== "{"
    +
     3728.      7        ) {
    +
     3729.      7
    +
     3730.      7// test_cause:
    +
     3731.      7// ["0=0", "check_mutation", "bad_assignment_a", "0", 1]
    +
     3732.      7
    +
     3733.      7            warn("bad_assignment_a", the_thing);
    +
     3734.      7            return false;
    +
     3735.   4780        }
    +
     3736.   4780        return true;
    +
     3737.   4780    }
    +
     3738.    597
    +
     3739.   2104    function check_not_top_level(thing) {
    +
     3740.   2104
    +
     3741.   2104// Some features should not be at the outermost level.
    +
     3742.   2104
    +
     3743.     35        if (functionage === token_global) {
    +
     3744.     35
    +
     3745.     35// test_cause:
    +
     3746.     35// ["
    +
     3747.     35// while(0){}
    +
     3748.     35// ", "check_not_top_level", "unexpected_at_top_level_a", "while", 1]
    +
     3749.     35
    +
     3750.     35            warn("unexpected_at_top_level_a", thing);
    +
     3751.     35        }
    +
     3752.   2104    }
    +
     3753.    597
    +
     3754.   3087    function check_ordered(type, token_list) {
    +
     3755.   3087
    +
     3756.   3087// This function will warn if <token_list> is unordered.
    +
     3757.   3087
    +
     3758.   3680        token_list.reduce(function (aa, token) {
    +
     3759.   3680            const bb = artifact(token);
    +
     3760.   3666            if (!option_dict.unordered && aa > bb) {
    +
     3761.      3                warn("expected_a_b_before_c_d", token, type, bb, type, aa);
    +
     3762.      3            }
    +
     3763.   3680            return bb;
    +
     3764.   3680        }, "");
    +
     3765.   3087    }
    +
     3766.    597
    +
     3767.   1200    function check_ordered_case(case_list) {
    +
     3768.   1200
    +
     3769.   1200// This function will warn if <case_list> is unordered.
    +
     3770.   1200
    +
     3771.   2518        case_list.filter(noop).map(function (token) {
    +
     3772.   2477            switch (token.identifier || token.id) {
    +
     3773.     34            case "(number)":
    +
     3774.     34                return {
    +
     3775.     34                    order: 1,
    +
     3776.     34                    token,
    +
     3777.     34                    type: "number",
    +
     3778.     34                    value: Number(artifact(token))
    +
     3779.     34                };
    +
     3780.   2431            case "(string)":
    +
     3781.   2431                return {
    +
     3782.   2431                    order: 2,
    +
     3783.   2431                    token,
    +
     3784.   2431                    type: "string",
    +
     3785.   2431                    value: artifact(token)
    +
     3786.   2431                };
    +
     3787.     41            case true:
    +
     3788.     41                return {
    +
     3789.     41                    order: 3,
    +
     3790.     41                    token,
    +
     3791.     41                    type: "identifier",
    +
     3792.     41                    value: artifact(token)
    +
     3793.     41                };
    +
     3794.   2518            }
    +
     3795.   2518        }).reduce(function (aa, bb) {
    +
     3796.   2518            if (
    +
     3797.   2518                !option_dict.unordered
    +
     3798.   2510                && aa && bb
    +
     3799.   1312                && (
    +
     3800.   1312                    aa.order > bb.order
    +
     3801.   1312                    || (aa.order === bb.order && aa.value > bb.value)
    +
     3802.   1312                )
    +
     3803.     10            ) {
    +
     3804.     10                warn(
    +
     3805.     10                    "expected_a_b_before_c_d",
    +
     3806.     10                    bb.token,
    +
     3807.     10                    `case-${bb.type}`,
    +
     3808.     10                    bb.value,
    +
     3809.     10                    `case-${aa.type}`,
    +
     3810.     10                    aa.value
    +
     3811.     10                );
    +
     3812.     10            }
    +
     3813.   2518            return bb;
    +
     3814.   2518        }, undefined);
    +
     3815.   1200    }
    +
     3816.    597
    +
     3817.   3219    function condition() {
    +
     3818.   3219
    +
     3819.   3219// Parse the condition part of a do, if, while.
    +
     3820.   3219
    +
     3821.   3219        const the_paren = token_nxt;
    +
     3822.   3219        let the_value;
    +
     3823.   3219
    +
     3824.   3219// test_cause:
    +
     3825.   3219// ["do{}while()", "condition", "", "", 0]
    +
     3826.   3219// ["if(){}", "condition", "", "", 0]
    +
     3827.   3219// ["while(){}", "condition", "", "", 0]
    +
     3828.   3219
    +
     3829.   3219        test_cause("");
    +
     3830.   3219        the_paren.free = true;
    +
     3831.   3219        advance("(");
    +
     3832.   3219        the_value = parse_expression(0);
    +
     3833.   3219        advance(")");
    +
     3834.      1        if (the_value.wrapped === true) {
    +
     3835.      1
    +
     3836.      1// test_cause:
    +
     3837.      1// ["while((0)){}", "condition", "unexpected_a", "(", 6]
    +
     3838.      1
    +
     3839.      1            warn("unexpected_a", the_paren);
    +
     3840.   3215        }
    +
     3841.   3215
    +
     3842.   3215// Check for anticondition.
    +
     3843.   3215
    +
     3844.   3215        switch (the_value.id) {
    +
     3845.   3215        case "%":
    +
     3846.      1            warn("unexpected_a", the_value);
    +
     3847.      1            break;
    +
     3848.      1        case "&":
    +
     3849.      1            warn("unexpected_a", the_value);
    +
     3850.      1            break;
    +
     3851.     18        case "(number)":
    +
     3852.     18            warn("unexpected_a", the_value);
    +
     3853.     18            break;
    +
     3854.      1        case "(string)":
    +
     3855.      1            warn("unexpected_a", the_value);
    +
     3856.      1            break;
    +
     3857.      1        case "*":
    +
     3858.      1            warn("unexpected_a", the_value);
    +
     3859.      1            break;
    +
     3860.      1        case "+":
    +
     3861.      1            warn("unexpected_a", the_value);
    +
     3862.      1            break;
    +
     3863.      1        case "-":
    +
     3864.      1            warn("unexpected_a", the_value);
    +
     3865.      1            break;
    +
     3866.      1        case "/":
    +
     3867.      1            warn("unexpected_a", the_value);
    +
     3868.      1            break;
    +
     3869.      1        case "<<":
    +
     3870.      1            warn("unexpected_a", the_value);
    +
     3871.      1            break;
    +
     3872.      1        case ">>":
    +
     3873.      1            warn("unexpected_a", the_value);
    +
     3874.      1            break;
    +
     3875.      1        case ">>>":
    +
     3876.      1            warn("unexpected_a", the_value);
    +
     3877.      1            break;
    +
     3878.      1        case "?":
    +
     3879.      1            warn("unexpected_a", the_value);
    +
     3880.      1            break;
    +
     3881.      1        case "^":
    +
     3882.      1            warn("unexpected_a", the_value);
    +
     3883.      1            break;
    +
     3884.      1        case "typeof":
    +
     3885.      1            warn("unexpected_a", the_value);
    +
     3886.      1            break;
    +
     3887.      1        case "|":
    +
     3888.      1            warn("unexpected_a", the_value);
    +
     3889.      1            break;
    +
     3890.      1        case "~":
    +
     3891.      1
    +
     3892.      1// test_cause:
    +
     3893.      1// ["if(0%0){}", "condition", "unexpected_a", "%", 5]
    +
     3894.      1// ["if(0&0){}", "condition", "unexpected_a", "&", 5]
    +
     3895.      1// ["if(0){}", "condition", "unexpected_a", "0", 4]
    +
     3896.      1// ["if(0*0){}", "condition", "unexpected_a", "*", 5]
    +
     3897.      1// ["if(0+0){}", "condition", "unexpected_a", "+", 5]
    +
     3898.      1// ["if(0-0){}", "condition", "unexpected_a", "-", 5]
    +
     3899.      1// ["if(0/0){}", "condition", "unexpected_a", "/", 5]
    +
     3900.      1// ["if(0<<0){}", "condition", "unexpected_a", "<<", 5]
    +
     3901.      1// ["if(0>>0){}", "condition", "unexpected_a", ">>", 5]
    +
     3902.      1// ["if(0>>>0){}", "condition", "unexpected_a", ">>>", 5]
    +
     3903.      1// ["if(0?0:0){}", "condition", "unexpected_a", "?", 5]
    +
     3904.      1// ["if(0^0){}", "condition", "unexpected_a", "^", 5]
    +
     3905.      1// ["if(0|0){}", "condition", "unexpected_a", "|", 5]
    +
     3906.      1// ["if(\"aa\"){}", "condition", "unexpected_a", "aa", 4]
    +
     3907.      1// ["if(typeof 0){}", "condition", "unexpected_a", "typeof", 4]
    +
     3908.      1// ["if(~0){}", "condition", "unexpected_a", "~", 4]
    +
     3909.      1
    +
     3910.      1            warn("unexpected_a", the_value);
    +
     3911.      1            break;
    +
     3912.   3215        }
    +
     3913.   3215        return the_value;
    +
     3914.   3215    }
    +
     3915.    597
    +
     3916.   9552    function constant(id, type, value) {
    +
     3917.   9552
    +
     3918.   9552// Create a constant symbol.
    +
     3919.   9552
    +
     3920.   9552        const the_symbol = symbol(id);
    +
     3921.   9552        the_symbol.constant = true;
    +
     3922.   9552        the_symbol.nud = (
    +
     3923.   9552            typeof value === "function"
    +
     3924.   4179            ? value
    +
     3925.  17537            : function () {
    +
     3926.  17537                token_now.constant = true;
    +
     3927.   5373                if (value !== undefined) {
    +
     3928.   5373                    token_now.value = value;
    +
     3929.   5373                }
    +
     3930.  17537                return token_now;
    +
     3931.  17537            }
    +
     3932.   9552        );
    +
     3933.   9552        the_symbol.type = type;
    +
     3934.   9552        the_symbol.value = value;
    +
     3935.   9552        return the_symbol;
    +
     3936.   9552    }
    +
     3937.    597
    +
     3938.      5    function constant_Function() {
    +
     3939.      2        if (!option_dict.eval) {
    +
     3940.      2
    +
     3941.      2// test_cause:
    +
     3942.      2// ["Function", "constant_Function", "unexpected_a", "Function", 1]
    +
     3943.      2
    +
     3944.      2            warn("unexpected_a", token_now);
    +
     3945.      3        } else if (token_nxt.id !== "(") {
    +
     3946.      3
    +
     3947.      3// test_cause:
    +
     3948.      3// ["
    +
     3949.      3// /*jslint eval*/
    +
     3950.      3// Function
    +
     3951.      3// ", "constant_Function", "expected_a_before_b", "(end)", 1]
    +
     3952.      3
    +
     3953.      3            warn("expected_a_before_b", token_nxt, "(", artifact());
    +
     3954.      3        }
    +
     3955.      5        return token_now;
    +
     3956.      5    }
    +
     3957.    597
    +
     3958.      1    function constant_arguments() {
    +
     3959.      1
    +
     3960.      1// test_cause:
    +
     3961.      1// ["arguments", "constant_arguments", "unexpected_a", "arguments", 1]
    +
     3962.      1
    +
     3963.      1        warn("unexpected_a", token_now);
    +
     3964.      1        return token_now;
    +
     3965.      1    }
    +
     3966.    597
    +
     3967.      4    function constant_eval() {
    +
     3968.      1        if (!option_dict.eval) {
    +
     3969.      1
    +
     3970.      1// test_cause:
    +
     3971.      1// ["eval", "constant_eval", "unexpected_a", "eval", 1]
    +
     3972.      1
    +
     3973.      1            warn("unexpected_a", token_now);
    +
     3974.      3        } else if (token_nxt.id !== "(") {
    +
     3975.      3
    +
     3976.      3// test_cause:
    +
     3977.      3// ["/*jslint eval*/\neval", "constant_eval", "expected_a_before_b", "(end)", 1]
    +
     3978.      3
    +
     3979.      3            warn("expected_a_before_b", token_nxt, "(", artifact());
    +
     3980.      3        }
    +
     3981.      4        return token_now;
    +
     3982.      4    }
    +
     3983.    597
    +
     3984.      1    function constant_ignore() {
    +
     3985.      1
    +
     3986.      1// test_cause:
    +
     3987.      1// ["ignore", "constant_ignore", "unexpected_a", "ignore", 1]
    +
     3988.      1
    +
     3989.      1        warn("unexpected_a", token_now);
    +
     3990.      1        return token_now;
    +
     3991.      1    }
    +
     3992.    597
    +
     3993.      1    function constant_isInfinite() {
    +
     3994.      1
    +
     3995.      1// test_cause:
    +
     3996.      1// ["isFinite", "constant_isInfinite", "expected_a_b", "isFinite", 1]
    +
     3997.      1
    +
     3998.      1        warn("expected_a_b", token_now, "Number.isFinite", "isFinite");
    +
     3999.      1        return token_now;
    +
     4000.      1    }
    +
     4001.    597
    +
     4002.      1    function constant_isNaN() {
    +
     4003.      1
    +
     4004.      1// test_cause:
    +
     4005.      1// ["isNaN(0)", "constant_isNaN", "number_isNaN", "isNaN", 1]
    +
     4006.      1
    +
     4007.      1        warn("number_isNaN", token_now);
    +
     4008.      1        return token_now;
    +
     4009.      1    }
    +
     4010.    597
    +
     4011.      3    function constant_this() {
    +
     4012.      1        if (!option_dict.this) {
    +
     4013.      1
    +
     4014.      1// test_cause:
    +
     4015.      1// ["this", "constant_this", "unexpected_a", "this", 1]
    +
     4016.      1
    +
     4017.      1            warn("unexpected_a", token_now);
    +
     4018.      1        }
    +
     4019.      3        return token_now;
    +
     4020.      3    }
    +
     4021.    597
    +
     4022.   5972    function enroll(name, role, readonly) {
    +
     4023.   5972
    +
     4024.   5972// Enroll a name into the current function context. The role can be exception,
    +
     4025.   5972// function, label, parameter, or variable. We look for variable redefinition
    +
     4026.   5972// because it causes confusion.
    +
     4027.   5972
    +
     4028.   5972        let earlier;
    +
     4029.   5972        let id = name.id;
    +
     4030.   5972
    +
     4031.   5972// Reserved words may not be enrolled.
    +
     4032.   5972
    +
     4033.     37        if (syntax_dict[id] !== undefined && id !== "ignore") {
    +
     4034.      1
    +
     4035.      1// test_cause:
    +
     4036.      1// ["let undefined", "enroll", "reserved_a", "undefined", 5]
    +
     4037.      1
    +
     4038.      1            warn("reserved_a", name);
    +
     4039.      1            return;
    +
     4040.   5971        }
    +
     4041.   5971
    +
     4042.   5971// Has the name been enrolled in this context?
    +
     4043.   5971
    +
     4044.   5971        earlier = functionage.context[id] || catchage.context[id];
    +
     4045.      7        if (earlier) {
    +
     4046.      7
    +
     4047.      7// test_cause:
    +
     4048.      7// ["let aa;let aa", "enroll", "redefinition_a_b", "1", 12]
    +
     4049.      7
    +
     4050.      7            warn("redefinition_a_b", name, id, earlier.line);
    +
     4051.      7            return;
    +
     4052.   5964        }
    +
     4053.   5964
    +
     4054.   5964// Has the name been enrolled in an outer context?
    +
     4055.   5964
    +
     4056.  10251        function_stack.forEach(function ({
    +
     4057.  10251            context
    +
     4058.  10251        }) {
    +
     4059.  10133            earlier = context[id] || earlier;
    +
     4060.  10251        });
    +
     4061.   5964        if (earlier && id === "ignore") {
    +
     4062.      4            if (earlier.role === "variable") {
    +
     4063.      4
    +
     4064.      4// test_cause:
    +
     4065.      4// ["let ignore;function aa(ignore){}", "enroll", "redefinition_a_b", "1", 24]
    +
     4066.      4
    +
     4067.      4                warn("redefinition_a_b", name, id, earlier.line);
    +
     4068.      4            }
    +
     4069.   5960        } else if (
    +
     4070.   5960            earlier
    +
     4071.   5960            && role !== "parameter" && role !== "function"
    +
     4072.   5960            && (role !== "exception" || earlier.role !== "exception")
    +
     4073.   5960        ) {
    +
     4074.   5960
    +
     4075.   5960// test_cause:
    +
     4076.   5960// ["
    +
     4077.   5960// function aa(){try{aa();}catch(aa){aa();}}
    +
     4078.   5960// ", "enroll", "redefinition_a_b", "1", 31]
    +
     4079.   5960// ["function aa(){var aa;}", "enroll", "redefinition_a_b", "1", 19]
    +
     4080.   5960
    +
     4081.   5960            warn("redefinition_a_b", name, id, earlier.line);
    +
     4082.   5960        } else if (
    +
     4083.   5960            option_dict.beta
    +
     4084.   5960            && global_dict[id]
    +
     4085.   5960            && role !== "parameter"
    +
     4086.   5960        ) {
    +
     4087.   5960
    +
     4088.   5960// test_cause:
    +
     4089.   5960// ["let Array", "enroll", "redefinition_global_a_b", "Array", 5]
    +
     4090.   5960
    +
     4091.   5960            warn("redefinition_global_a_b", name, global_dict[id], id);
    +
     4092.   5964        }
    +
     4093.   5964
    +
     4094.   5964// Enroll it.
    +
     4095.   5964
    +
     4096.   5964        Object.assign(name, {
    +
     4097.   5964            dead: true,
    +
     4098.   5964            init: false,
    +
     4099.   5964            parent: (
    +
     4100.   5964                role === "exception"
    +
     4101.   5964                ? catchage
    +
     4102.   5934                : functionage
    +
     4103.   5972            ),
    +
     4104.   5972            readonly,
    +
     4105.   5972            role,
    +
     4106.   5972            used: 0
    +
     4107.   5972        });
    +
     4108.   5972        name.parent.context[id] = name;
    +
     4109.   5972    }
    +
     4110.    597
    +
     4111.  17910    function infix(bp, id, f) {
    +
     4112.  17910
    +
     4113.  17910// Create an infix operator.
    +
     4114.  17910
    +
     4115.  17910        const the_symbol = symbol(id, bp);
    +
     4116.  30464        the_symbol.led = function (left) {
    +
     4117.  30464            const the_token = token_now;
    +
     4118.  30464            the_token.arity = "binary";
    +
     4119.  22291            if (f !== undefined) {
    +
     4120.  22291                return f(left);
    +
     4121.  22291            }
    +
     4122.   8173            the_token.expression = [left, parse_expression(bp)];
    +
     4123.   8173            return the_token;
    +
     4124.   8173        };
    +
     4125.  17910        return the_symbol;
    +
     4126.  17910    }
    +
     4127.    597
    +
     4128.  11081    function infix_dot(left) {
    +
     4129.  11081        const the_token = token_now;
    +
     4130.  11081        let name = token_nxt;
    +
     4131.  11081        if (
    +
     4132.  11081            (
    +
     4133.  11081                left.id !== "(string)"
    +
     4134.     32                || (name.id !== "indexOf" && name.id !== "repeat")
    +
     4135.  11081            )
    +
     4136.  11050            && (
    +
     4137.  11050                left.id !== "["
    +
     4138.  11050                || (
    +
     4139.  11050                    name.id !== "concat"
    +
     4140.  11050                    && name.id !== "forEach"
    +
     4141.  11050                    && name.id !== "join"
    +
     4142.  11050                    && name.id !== "map"
    +
     4143.  11050                )
    +
     4144.  11050            )
    +
     4145.  11026            && (left.id !== "+" || name.id !== "slice")
    +
     4146.  11021            && (
    +
     4147.  11021                left.id !== "(regexp)"
    +
     4148.  11021                || (name.id !== "exec" && name.id !== "test")
    +
     4149.  11021            )
    +
     4150.  10927        ) {
    +
     4151.  10927
    +
     4152.  10927// test_cause:
    +
     4153.  10927// ["\"\".aa", "check_left", "unexpected_a", ".", 3]
    +
     4154.  10927
    +
     4155.  10927            check_left(left, the_token);
    +
     4156.  10927        }
    +
     4157.      1        if (!name.identifier) {
    +
     4158.      1
    +
     4159.      1// test_cause:
    +
     4160.      1// ["aa.0", "infix_dot", "expected_identifier_a", "0", 4]
    +
     4161.      1
    +
     4162.      1            stop("expected_identifier_a");
    +
     4163.  11080        }
    +
     4164.  11080        advance();
    +
     4165.  11080        survey(name);
    +
     4166.  11080
    +
     4167.  11080// The property name is not an expression.
    +
     4168.  11080
    +
     4169.  11080        the_token.name = name;
    +
     4170.  11080        the_token.expression = left;
    +
     4171.  11080        return the_token;
    +
     4172.  11080    }
    +
     4173.    597
    +
     4174.      1    function infix_fart_unwrapped(left) {
    +
     4175.      1
    +
     4176.      1// test_cause:
    +
     4177.      1// ["aa=>0", "infix_fart_unwrapped", "wrap_parameter", "aa", 1]
    +
     4178.      1
    +
     4179.      1        return stop("wrap_parameter", left);
    +
     4180.      1    }
    +
     4181.    597
    +
     4182.      1    function infix_grave(left) {
    +
     4183.      1        const the_tick = prefix_tick();
    +
     4184.      1
    +
     4185.      1// test_cause:
    +
     4186.      1// ["0``", "check_left", "unexpected_a", "`", 2]
    +
     4187.      1
    +
     4188.      1        check_left(left, the_tick);
    +
     4189.      1        the_tick.expression = [left].concat(the_tick.expression);
    +
     4190.      1        return the_tick;
    +
     4191.      1    }
    +
     4192.    597
    +
     4193.   1404    function infix_lbracket(left) {
    +
     4194.   1404        const the_token = token_now;
    +
     4195.   1404        let name;
    +
     4196.   1404        let the_subscript = parse_expression(0);
    +
     4197.   1388        if (the_subscript.id === "(string)" || the_subscript.id === "`") {
    +
     4198.     18            name = survey(the_subscript);
    +
     4199.     18            if (rx_identifier.test(name)) {
    +
     4200.     18
    +
     4201.     18// test_cause:
    +
     4202.     18// ["aa[`aa`]", "infix_lbracket", "subscript_a", "aa", 4]
    +
     4203.     18
    +
     4204.     18                warn("subscript_a", the_subscript, name);
    +
     4205.     18            }
    +
     4206.     18        }
    +
     4207.   1404
    +
     4208.   1404// test_cause:
    +
     4209.   1404// ["0[0]", "check_left", "unexpected_a", "[", 2]
    +
     4210.   1404
    +
     4211.   1404        check_left(left, the_token);
    +
     4212.   1404        the_token.expression = [left, the_subscript];
    +
     4213.   1404        advance("]");
    +
     4214.   1404        return the_token;
    +
     4215.   1404    }
    +
     4216.    597
    +
     4217.   9796    function infix_lparen(left) {
    +
     4218.   9796        const the_paren = token_now;
    +
     4219.   9796        let ellipsis;
    +
     4220.   9796        let the_argument;
    +
     4221.   9754        if (left.id !== "function") {
    +
     4222.   9754
    +
     4223.   9754// test_cause:
    +
     4224.   9754// ["(0?0:0)()", "check_left", "unexpected_a", "(", 8]
    +
     4225.   9754// ["0()", "check_left", "unexpected_a", "(", 2]
    +
     4226.   9754
    +
     4227.   9754            check_left(left, the_paren);
    +
     4228.   9754        }
    +
     4229.   7356        if (functionage.arity === "statement" && left.identifier) {
    +
     4230.   5393            functionage.name.calls[left.id] = left;
    +
     4231.   5393        }
    +
     4232.   9796        the_paren.expression = [left];
    +
     4233.   8359        if (token_nxt.id !== ")") {
    +
     4234.   8359
    +
     4235.   8359// Parse/loop through each token in expression (...).
    +
     4236.   8359
    +
     4237.  13336            while (true) {
    +
     4238.  13336                if (token_nxt.id === "...") {
    +
     4239.  13336                    ellipsis = true;
    +
     4240.  13336                    advance("...");
    +
     4241.  13336                }
    +
     4242.  13336                the_argument = parse_expression(10);
    +
     4243.  13336                if (ellipsis) {
    +
     4244.  13336                    the_argument.ellipsis = true;
    +
     4245.  13336                }
    +
     4246.  13336                the_paren.expression.push(the_argument);
    +
     4247.  13336                if (token_nxt.id !== ",") {
    +
     4248.  13336                    break;
    +
     4249.  13336                }
    +
     4250.  13336                advance(",");
    +
     4251.  13336            }
    +
     4252.   9795        }
    +
     4253.   9795        advance(")", the_paren);
    +
     4254.   9795        if (the_paren.expression.length === 2) {
    +
     4255.   5007
    +
     4256.   5007// test_cause:
    +
     4257.   5007// ["aa(0)", "infix_lparen", "free", "", 0]
    +
     4258.   5007
    +
     4259.   5007            test_cause("free");
    +
     4260.   5007            the_paren.free = true;
    +
     4261.   5007            if (the_argument.wrapped === true) {
    +
     4262.   5007
    +
     4263.   5007// test_cause:
    +
     4264.   5007// ["aa((0))", "infix_lparen", "unexpected_a", "(", 3]
    +
     4265.   5007
    +
     4266.   5007                warn("unexpected_a", the_paren);
    +
     4267.   5007            }
    +
     4268.   5007            if (the_argument.id === "(") {
    +
     4269.   5007                the_argument.wrapped = true;
    +
     4270.   5007            }
    +
     4271.   5007        } else {
    +
     4272.   4788
    +
     4273.   4788// test_cause:
    +
     4274.   4788// ["aa()", "infix_lparen", "not_free", "", 0]
    +
     4275.   4788// ["aa(0,0)", "infix_lparen", "not_free", "", 0]
    +
     4276.   4788
    +
     4277.   4788            test_cause("not_free");
    +
     4278.   4788            the_paren.free = false;
    +
     4279.   9795        }
    +
     4280.   9795        return the_paren;
    +
     4281.   9795    }
    +
     4282.    597
    +
     4283.      8    function infix_option_chain(left) {
    +
     4284.      8        const the_token = token_now;
    +
     4285.      8        let name;
    +
     4286.      8        name = token_nxt;
    +
     4287.      8        if (
    +
     4288.      8            (
    +
     4289.      8                left.id !== "(string)"
    +
     4290.      1                || (name.id !== "indexOf" && name.id !== "repeat")
    +
     4291.      8            )
    +
     4292.      8            && (
    +
     4293.      8                left.id !== "["
    +
     4294.      1                || (
    +
     4295.      1                    name.id !== "concat"
    +
     4296.      1                    && name.id !== "forEach"
    +
     4297.      1                    && name.id !== "join"
    +
     4298.      1                    && name.id !== "map"
    +
     4299.      1                )
    +
     4300.      8            )
    +
     4301.      8
    +
     4302.      8// test_cause:
    +
     4303.      8// ["(0+0)?.0", "infix_option_chain", "check_left", "", 0]
    +
     4304.      8
    +
     4305.      1            && (left.id !== "+" || name.id !== "slice")
    +
     4306.      8            && (
    +
     4307.      8                left.id !== "(regexp)"
    +
     4308.      1                || (name.id !== "exec" && name.id !== "test")
    +
     4309.      8            )
    +
     4310.      8        ) {
    +
     4311.      8            test_cause("check_left");
    +
     4312.      8
    +
     4313.      8// test_cause:
    +
     4314.      8// ["(/./)?.0", "check_left", "unexpected_a", "?.", 6]
    +
     4315.      8// ["\"aa\"?.0", "check_left", "unexpected_a", "?.", 5]
    +
     4316.      8// ["aa=[]?.aa", "check_left", "unexpected_a", "?.", 6]
    +
     4317.      8
    +
     4318.      8            check_left(left, the_token);
    +
     4319.      8        }
    +
     4320.      4        if (!name.identifier) {
    +
     4321.      4
    +
     4322.      4// test_cause:
    +
     4323.      4// ["aa?.0", "infix_option_chain", "expected_identifier_a", "0", 5]
    +
     4324.      4
    +
     4325.      4            stop("expected_identifier_a");
    +
     4326.      4        }
    +
     4327.      4        advance();
    +
     4328.      4        survey(name);
    +
     4329.      4
    +
     4330.      4// The property name is not an expression.
    +
     4331.      4
    +
     4332.      4        the_token.name = name;
    +
     4333.      4        the_token.expression = left;
    +
     4334.      4        return the_token;
    +
     4335.      4    }
    +
     4336.    597
    +
     4337.    597    function infixr(bp, id) {
    +
     4338.    597
    +
     4339.    597// Create a right associative infix operator.
    +
     4340.    597
    +
     4341.    597        const the_symbol = symbol(id, bp);
    +
     4342.      1        the_symbol.led = function parse_infixr_led(left) {
    +
     4343.      1            const the_token = token_now;
    +
     4344.      1
    +
     4345.      1// test_cause:
    +
     4346.      1// ["0**0", "parse_infixr_led", "led", "", 0]
    +
     4347.      1
    +
     4348.      1            test_cause("led");
    +
     4349.      1            the_token.arity = "binary";
    +
     4350.      1            the_token.expression = [left, parse_expression(bp - 1)];
    +
     4351.      1            return the_token;
    +
     4352.      1        };
    +
     4353.    597        return the_symbol;
    +
     4354.    597    }
    +
     4355.    597
    +
     4356.   1371    function lookahead() {
    +
     4357.   1371
    +
     4358.   1371// Look ahead one token without advancing, skipping comments.
    +
     4359.   1371
    +
     4360.   1371        let cadet;
    +
     4361.   1371        let ii = token_ii;
    +
     4362.   1388        while (true) {
    +
     4363.   1388            cadet = token_list[ii];
    +
     4364.   1388            if (cadet.id !== "(comment)") {
    +
     4365.   1388                return cadet;
    +
     4366.   1388            }
    +
     4367.   1388            ii += 1;
    +
     4368.   1388        }
    +
     4369.   1371    }
    +
     4370.    597
    +
     4371.  52636    function parse_expression(rbp, initial) {
    +
     4372.  52636
    +
     4373.  52636// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it
    +
     4374.  52636// is looking for ad hoc lint patterns. We add .fud to Pratt's model, which is
    +
     4375.  52636// like .nud except that it is only used on the first token of a statement.
    +
     4376.  52636// Having .fud makes it much easier to define statement-oriented languages like
    +
     4377.  52636// JavaScript. I retained Pratt's nomenclature.
    +
     4378.  52636// They are elements of the parsing method called Top Down Operator Precedence.
    +
     4379.  52636
    +
     4380.  52636// .nud     Null denotation
    +
     4381.  52636// .fud     First null denotation
    +
     4382.  52636// .led     Left denotation
    +
     4383.  52636//  lbp     Left binding power
    +
     4384.  52636//  rbp     Right binding power
    +
     4385.  52636
    +
     4386.  52636// It processes a nud (variable, constant, prefix operator). It will then
    +
     4387.  52636// process leds (infix operators) until the bind powers cause it to stop. It
    +
     4388.  52636// returns the expression's parse tree.
    +
     4389.  52636
    +
     4390.  52636        let left;
    +
     4391.  52636        let the_symbol;
    +
     4392.  52636
    +
     4393.  52636// Statements will have already advanced, so advance now only if the token is
    +
     4394.  52636// not the first of a statement.
    +
     4395.  52636
    +
     4396.  41832        if (!initial) {
    +
     4397.  41832            advance();
    +
     4398.  41832        }
    +
     4399.  52636        the_symbol = syntax_dict[token_now.id];
    +
     4400.  22712        if (the_symbol !== undefined && the_symbol.nud !== undefined) {
    +
     4401.  22653
    +
     4402.  22653// test_cause:
    +
     4403.  22653// ["0", "parse_expression", "symbol", "", 0]
    +
     4404.  22653
    +
     4405.  22653            test_cause("symbol");
    +
     4406.  22653            left = the_symbol.nud();
    +
     4407.  29983        } else if (token_now.identifier) {
    +
     4408.  29983
    +
     4409.  29983// test_cause:
    +
     4410.  29983// ["aa", "parse_expression", "identifier", "", 0]
    +
     4411.  29983
    +
     4412.  29983            test_cause("identifier");
    +
     4413.  29983            left = token_now;
    +
     4414.  29983            left.arity = "variable";
    +
     4415.  29983        } else {
    +
     4416.  29983
    +
     4417.  29983// test_cause:
    +
     4418.  29983// ["!", "parse_expression", "unexpected_a", "(end)", 1]
    +
     4419.  29983// ["/./", "parse_expression", "unexpected_a", "/", 1]
    +
     4420.  29983// ["let aa=`${}`;", "parse_expression", "unexpected_a", "}", 11]
    +
     4421.  29983
    +
     4422.  29983            return stop("unexpected_a", token_now);
    +
     4423.  52594        }
    +
     4424.  52594
    +
     4425.  52594// Parse/loop through each symbol in expression.
    +
     4426.  52594
    +
     4427.  88043        while (true) {
    +
     4428.  88043            the_symbol = syntax_dict[token_nxt.id];
    +
     4429.  88043            if (
    +
     4430.  88043                the_symbol === undefined
    +
     4431.  88043                || the_symbol.led === undefined
    +
     4432.  88043                || the_symbol.lbp <= rbp
    +
     4433.  88043            ) {
    +
     4434.  88043                break;
    +
     4435.  88043            }
    +
     4436.  88043            advance();
    +
     4437.  88043            left = the_symbol.led(left);
    +
     4438.  88043        }
    +
     4439.  52582        return left;
    +
     4440.  52582    }
    +
     4441.    597
    +
     4442.     10    function parse_fart(pl) {
    +
     4443.     10        let the_fart;
    +
     4444.     10        advance("=>");
    +
     4445.     10        the_fart = token_now;
    +
     4446.     10        the_fart.arity = "binary";
    +
     4447.     10        the_fart.name = "=>";
    +
     4448.     10        the_fart.level = functionage.level + 1;
    +
     4449.     10        function_list.push(the_fart);
    +
     4450.      1        if (functionage.loop > 0) {
    +
     4451.      1
    +
     4452.      1// test_cause:
    +
     4453.      1// ["while(0){aa.map(()=>0);}", "parse_fart", "function_in_loop", "=>", 19]
    +
     4454.      1
    +
     4455.      1            warn("function_in_loop", the_fart);
    +
     4456.      8        }
    +
     4457.      8
    +
     4458.      8// Give the function properties storing its names and for observing the depth
    +
     4459.      8// of loops and switches.
    +
     4460.      8
    +
     4461.      8        the_fart.context = empty();
    +
     4462.      8        the_fart.finally = 0;
    +
     4463.      8        the_fart.loop = 0;
    +
     4464.      8        the_fart.switch = 0;
    +
     4465.      8        the_fart.try = 0;
    +
     4466.      8
    +
     4467.      8// Push the current function context and establish a new one.
    +
     4468.      8
    +
     4469.      8        function_stack.push(functionage);
    +
     4470.      8        functionage = the_fart;
    +
     4471.      8        the_fart.parameters = pl[0];
    +
     4472.      8        the_fart.signature = pl[1];
    +
     4473.      8        the_fart.parameters.forEach(function (name) {
    +
     4474.      8
    +
     4475.      8// test_cause:
    +
     4476.      8// ["(aa)=>{}", "parse_fart", "parameter", "", 0]
    +
     4477.      8
    +
     4478.      8            test_cause("parameter");
    +
     4479.      8            enroll(name, "parameter", true);
    +
     4480.      8        });
    +
     4481.      8        if (token_nxt.id === "{") {
    +
     4482.      2
    +
     4483.      2// test_cause:
    +
     4484.      2// ["()=>{}", "parse_fart", "expected_a_b", "=>", 3]
    +
     4485.      2
    +
     4486.      2            warn("expected_a_b", the_fart, "function", "=>");
    +
     4487.      2            the_fart.block = block("body");
    +
     4488.      6        } else {
    +
     4489.      6            the_fart.expression = parse_expression(0);
    +
     4490.      8        }
    +
     4491.      8        functionage = function_stack.pop();
    +
     4492.      8        return the_fart;
    +
     4493.      8    }
    +
     4494.    597
    +
     4495.  12955    function parse_json() {
    +
     4496.  12955        let container;
    +
     4497.  12955        let is_dup;
    +
     4498.  12955        let name;
    +
     4499.  12955        let negative;
    +
     4500.  12955        switch (token_nxt.id) {
    +
     4501.   5260        case "(number)":
    +
     4502.   5260            if (!(
    +
     4503.   5260
    +
     4504.   5260// https://datatracker.ietf.org/doc/html/rfc7159#section-6
    +
     4505.   5260// number = [ minus ] int [ frac ] [ exp ]
    +
     4506.   5260
    +
     4507.   5260                /^-?(?:0|[1-9]\d*?)(?:\.\d*?)?(?:[eE][+\-]?\d+?)?$/
    +
     4508.   5260            ).test(token_nxt.value)) {
    +
     4509.   5260
    +
     4510.   5260// test_cause:
    +
     4511.   5260// ["[-.0]", "parse_json", "unexpected_a", ".", 3]
    +
     4512.   5260// ["[-0x0]", "parse_json", "unexpected_a", "0x0", 3]
    +
     4513.   5260// ["[.0]", "parse_json", "unexpected_a", ".", 2]
    +
     4514.   5260// ["[0x0]", "parse_json", "unexpected_a", "0x0", 2]
    +
     4515.   5260
    +
     4516.   5260                warn("unexpected_a");
    +
     4517.   5260            }
    +
     4518.   5260            advance("(number)");
    +
     4519.   5260            return token_now;
    +
     4520.   1765        case "(string)":
    +
     4521.   1765            if (token_nxt.quote !== "\"") {
    +
     4522.   1765
    +
     4523.   1765// test_cause:
    +
     4524.   1765// ["['']", "parse_json", "unexpected_a", "'", 2]
    +
     4525.   1765
    +
     4526.   1765                warn("unexpected_a", token_nxt, token_nxt.quote);
    +
     4527.   1765            }
    +
     4528.   1765            advance("(string)");
    +
     4529.   1765            return token_now;
    +
     4530.      3        case "-":
    +
     4531.      3            negative = token_nxt;
    +
     4532.      3            negative.arity = "unary";
    +
     4533.      3            advance("-");
    +
     4534.      3
    +
     4535.      3// Recurse parse_json().
    +
     4536.      3
    +
     4537.      3            negative.expression = parse_json();
    +
     4538.      3            return negative;
    +
     4539.   1649        case "[":
    +
     4540.   1649
    +
     4541.   1649// test_cause:
    +
     4542.   1649// ["[]", "parse_json", "bracket", "", 0]
    +
     4543.   1649
    +
     4544.   1649            test_cause("bracket");
    +
     4545.   1649            container = token_nxt;
    +
     4546.   1649            container.expression = [];
    +
     4547.   1649            advance("[");
    +
     4548.   1649            if (token_nxt.id !== "]") {
    +
     4549.   3299                while (true) {
    +
     4550.   3299
    +
     4551.   3299// Recurse parse_json().
    +
     4552.   3299
    +
     4553.   3299                    container.expression.push(parse_json());
    +
     4554.   3299                    if (token_nxt.id !== ",") {
    +
     4555.   3299
    +
     4556.   3299// test_cause:
    +
     4557.   3299// ["[0,0]", "parse_json", "comma", "", 0]
    +
     4558.   3299
    +
     4559.   3299                        test_cause("comma");
    +
     4560.   3299                        break;
    +
     4561.   3299                    }
    +
     4562.   3299                    advance(",");
    +
     4563.   3299                }
    +
     4564.   1649            }
    +
     4565.   1649            advance("]", container);
    +
     4566.   1649            return container;
    +
     4567.    509        case "false":
    +
     4568.    511        case "null":
    +
     4569.    896        case "true":
    +
     4570.    896
    +
     4571.    896// test_cause:
    +
     4572.    896// ["[false]", "parse_json", "constant", "", 0]
    +
     4573.    896// ["[null]", "parse_json", "constant", "", 0]
    +
     4574.    896// ["[true]", "parse_json", "constant", "", 0]
    +
     4575.    896
    +
     4576.    896            test_cause("constant");
    +
     4577.    896            advance();
    +
     4578.    896            return token_now;
    +
     4579.   3377        case "{":
    +
     4580.   3377
    +
     4581.   3377// test_cause:
    +
     4582.   3377// ["{}", "parse_json", "brace", "", 0]
    +
     4583.   3377
    +
     4584.   3377            test_cause("brace");
    +
     4585.   3377            container = token_nxt;
    +
     4586.   3377
    +
     4587.   3377// Explicit empty-object required to detect "__proto__".
    +
     4588.   3377
    +
     4589.   3377            is_dup = empty();
    +
     4590.   3377            container.expression = [];
    +
     4591.   3377            advance("{");
    +
     4592.   3377            if (token_nxt.id !== "}") {
    +
     4593.   3377
    +
     4594.   3377// JSON
    +
     4595.   3377// Parse/loop through each property in {...}.
    +
     4596.   3377
    +
     4597.   9631                while (true) {
    +
     4598.   9631                    if (token_nxt.quote !== "\"") {
    +
     4599.   9631
    +
     4600.   9631// test_cause:
    +
     4601.   9631// ["{0:0}", "parse_json", "unexpected_a", "0", 2]
    +
     4602.   9631
    +
     4603.   9631                        warn(
    +
     4604.   9631                            "unexpected_a",
    +
     4605.   9631                            token_nxt,
    +
     4606.   9631                            token_nxt.quote
    +
     4607.   9631                        );
    +
     4608.   9631                    }
    +
     4609.   9631                    name = token_nxt;
    +
     4610.   9631                    advance("(string)");
    +
     4611.   9631                    if (is_dup[token_now.value] !== undefined) {
    +
     4612.   9631
    +
     4613.   9631// test_cause:
    +
     4614.   9631// ["{\"aa\":0,\"aa\":0}", "parse_json", "duplicate_a", "aa", 9]
    +
     4615.   9631
    +
     4616.   9631                        warn("duplicate_a", token_now);
    +
     4617.   9631                    } else if (token_now.value === "__proto__") {
    +
     4618.   9631
    +
     4619.   9631// test_cause:
    +
     4620.   9631// ["{\"__proto__\":0}", "parse_json", "weird_property_a", "__proto__", 2]
    +
     4621.   9631
    +
     4622.   9631                        warn("weird_property_a", token_now);
    +
     4623.   9631                    } else {
    +
     4624.   9631                        is_dup[token_now.value] = token_now;
    +
     4625.   9631                    }
    +
     4626.   9631                    advance(":");
    +
     4627.   9631                    container.expression.push(
    +
     4628.   9631
    +
     4629.   9631// Recurse parse_json().
    +
     4630.   9631
    +
     4631.   9631                        Object.assign(parse_json(), {
    +
     4632.   9631                            label: name
    +
     4633.   9631                        })
    +
     4634.   9631                    );
    +
     4635.   9631                    if (token_nxt.id !== ",") {
    +
     4636.   9631                        break;
    +
     4637.   9631                    }
    +
     4638.   9631                    advance(",");
    +
     4639.   9631                }
    +
     4640.   3377            }
    +
     4641.   3377            advance("}", container);
    +
     4642.   3377            return container;
    +
     4643.      5        default:
    +
     4644.      5
    +
     4645.      5// test_cause:
    +
     4646.      5// ["[undefined]", "parse_json", "unexpected_a", "undefined", 2]
    +
     4647.      5
    +
     4648.      5            stop("unexpected_a");
    +
     4649.  12955        }
    +
     4650.  12955    }
    +
     4651.    597
    +
     4652.  19977    function parse_statement() {
    +
     4653.  19977
    +
     4654.  19977// Parse a statement. Any statement may have a label, but only four statements
    +
     4655.  19977// have use for one. A statement can be one of the standard statements, or
    +
     4656.  19977// an assignment expression, or an invocation expression.
    +
     4657.  19977
    +
     4658.  19977        let first;
    +
     4659.  19977        let the_label;
    +
     4660.  19977        let the_statement;
    +
     4661.  19977        let the_symbol;
    +
     4662.  19977        advance();
    +
     4663.  19794        if (token_now.identifier && token_nxt.id === ":") {
    +
     4664.     13            the_label = token_now;
    +
     4665.     13            if (the_label.id === "ignore") {
    +
     4666.     13
    +
     4667.     13// test_cause:
    +
     4668.     13// ["ignore:", "parse_statement", "unexpected_a", "ignore", 1]
    +
     4669.     13
    +
     4670.     13                warn("unexpected_a", the_label);
    +
     4671.     13            }
    +
     4672.     13            advance(":");
    +
     4673.     13            switch (token_nxt.id) {
    +
     4674.     13            case "do":
    +
     4675.     13            case "for":
    +
     4676.     13            case "switch":
    +
     4677.     13            case "while":
    +
     4678.     13
    +
     4679.     13// test_cause:
    +
     4680.     13// ["aa:do{}", "parse_statement", "the_statement_label", "do", 0]
    +
     4681.     13// ["aa:for{}", "parse_statement", "the_statement_label", "for", 0]
    +
     4682.     13// ["aa:switch{}", "parse_statement", "the_statement_label", "switch", 0]
    +
     4683.     13// ["aa:while{}", "parse_statement", "the_statement_label", "while", 0]
    +
     4684.     13
    +
     4685.     13                test_cause("the_statement_label", token_nxt.id);
    +
     4686.     13                enroll(the_label, "label", true);
    +
     4687.     13                the_label.dead = false;
    +
     4688.     13                the_label.init = true;
    +
     4689.     13                the_statement = parse_statement();
    +
     4690.     13                functionage.statement_prv = the_statement;
    +
     4691.     13                the_statement.label = the_label;
    +
     4692.     13                the_statement.statement = true;
    +
     4693.     13                return the_statement;
    +
     4694.     13            }
    +
     4695.     13            advance();
    +
     4696.     13
    +
     4697.     13// test_cause:
    +
     4698.     13// ["aa:", "parse_statement", "unexpected_label_a", "aa", 1]
    +
     4699.     13
    +
     4700.     13            warn("unexpected_label_a", the_label);
    +
     4701.  19968        }
    +
     4702.  19968
    +
     4703.  19968// Parse the statement.
    +
     4704.  19968
    +
     4705.  19968        first = token_now;
    +
     4706.  19968        first.statement = true;
    +
     4707.  19968        the_symbol = syntax_dict[first.id];
    +
     4708.  19968        if (
    +
     4709.  19968            the_symbol !== undefined
    +
     4710.  19968            && the_symbol.fud !== undefined
    +
     4711.  19977
    +
     4712.  19977// PR-318 - Bugfix - Fixes issues #316, #317 - dynamic-import().
    +
     4713.  19977
    +
     4714.   9710            && !(the_symbol.id === "import" && token_nxt.id === "(")
    +
     4715.   9708        ) {
    +
     4716.   9708            the_symbol.disrupt = false;
    +
     4717.   9708            the_symbol.statement = true;
    +
     4718.   9708            token_now.arity = "statement";
    +
     4719.   9708            the_statement = the_symbol.fud();
    +
     4720.   9708            functionage.statement_prv = the_statement;
    +
     4721.  10260        } else {
    +
     4722.  10260
    +
     4723.  10260// It is an expression statement.
    +
     4724.  10260
    +
     4725.  10260            the_statement = parse_expression(0, true);
    +
     4726.  10260            functionage.statement_prv = the_statement;
    +
     4727.  10260            if (the_statement.wrapped && the_statement.id !== "(") {
    +
     4728.  10260
    +
     4729.  10260// test_cause:
    +
     4730.  10260// ["(0)", "parse_statement", "unexpected_a", "(", 1]
    +
     4731.  10260
    +
     4732.  10260                warn("unexpected_a", first);
    +
     4733.  10260            }
    +
     4734.  10260            semicolon();
    +
     4735.  19867        }
    +
     4736.  19867        if (the_label !== undefined) {
    +
     4737.      1            the_label.dead = true;
    +
     4738.  19867        }
    +
     4739.  19867        return the_statement;
    +
     4740.  19867    }
    +
     4741.    597
    +
     4742.   7209    function parse_statements() {
    +
     4743.   7209
    +
     4744.   7209// Parse a list of statements. Give a warning if an unreachable statement
    +
     4745.   7209// follows a disruptive statement.
    +
     4746.   7209
    +
     4747.   7209        const statement_list = [];
    +
     4748.   7209        let a_statement;
    +
     4749.   7209        let disrupt = false;
    +
     4750.   7209
    +
     4751.   7209// Parse/loop each statement until a statement-terminator is reached.
    +
     4752.   7209
    +
     4753.  26750        while (true) {
    +
     4754.  26750            switch (token_nxt.id) {
    +
     4755.  26750            case "(end)":
    +
     4756.  26750            case "case":
    +
     4757.  26750            case "default":
    +
     4758.  26750            case "else":
    +
     4759.  26750            case "}":
    +
     4760.  26750
    +
     4761.  26750// test_cause:
    +
     4762.  26750// [";", "parse_statements", "closer", "", 0]
    +
     4763.  26750// ["case", "parse_statements", "closer", "", 0]
    +
     4764.  26750// ["default", "parse_statements", "closer", "", 0]
    +
     4765.  26750// ["else", "parse_statements", "closer", "", 0]
    +
     4766.  26750// ["}", "parse_statements", "closer", "", 0]
    +
     4767.  26750
    +
     4768.  26750                test_cause("closer");
    +
     4769.  26750                return statement_list;
    +
     4770.  26750            }
    +
     4771.  26750            a_statement = parse_statement();
    +
     4772.  26750            statement_list.push(a_statement);
    +
     4773.  26750            if (disrupt) {
    +
     4774.  26750
    +
     4775.  26750// test_cause:
    +
     4776.  26750// ["while(0){break;0;}", "parse_statements", "unreachable_a", "0", 16]
    +
     4777.  26750
    +
     4778.  26750                warn("unreachable_a", a_statement);
    +
     4779.  26750            }
    +
     4780.  26750            disrupt = a_statement.disrupt;
    +
     4781.  26750        }
    +
     4782.   7209    }
    +
     4783.    597
    +
     4784.   1194    function postassign(id) {
    +
     4785.   1194
    +
     4786.   1194// Create one of the postassign operators.
    +
     4787.   1194
    +
     4788.   1194        const the_symbol = symbol(id, 150);
    +
     4789.      1        the_symbol.led = function (left) {
    +
     4790.      1            token_now.expression = left;
    +
     4791.      1            token_now.arity = "postassign";
    +
     4792.      1            check_mutation(token_now.expression);
    +
     4793.      1            return token_now;
    +
     4794.      1        };
    +
     4795.   1194        return the_symbol;
    +
     4796.   1194    }
    +
     4797.    597
    +
     4798.   1194    function preassign(id) {
    +
     4799.   1194
    +
     4800.   1194// Create one of the preassign operators.
    +
     4801.   1194
    +
     4802.   1194        const the_symbol = symbol(id);
    +
     4803.      2        the_symbol.nud = function () {
    +
     4804.      2            const the_token = token_now;
    +
     4805.      2            the_token.arity = "preassign";
    +
     4806.      2            the_token.expression = parse_expression(150);
    +
     4807.      2            check_mutation(the_token.expression);
    +
     4808.      2            return the_token;
    +
     4809.      2        };
    +
     4810.   1194        return the_symbol;
    +
     4811.   1194    }
    +
     4812.    597
    +
     4813.  10149    function prefix(id, f) {
    +
     4814.  10149
    +
     4815.  10149// Create a prefix operator.
    +
     4816.  10149
    +
     4817.  10149        const the_symbol = symbol(id);
    +
     4818.   5098        the_symbol.nud = function () {
    +
     4819.   5098            const the_token = token_now;
    +
     4820.   5098            the_token.arity = "unary";
    +
     4821.   4313            if (typeof f === "function") {
    +
     4822.   4313                return f();
    +
     4823.   4313            }
    +
     4824.    785            the_token.expression = parse_expression(150);
    +
     4825.    785            return the_token;
    +
     4826.    785        };
    +
     4827.  10149        return the_symbol;
    +
     4828.  10149    }
    +
     4829.    597
    +
     4830.      1    function prefix_assign_divide() {
    +
     4831.      1
    +
     4832.      1// test_cause:
    +
     4833.      1// ["/=", "prefix_assign_divide", "expected_a_b", "/=", 1]
    +
     4834.      1
    +
     4835.      1        stop("expected_a_b", token_now, "/\\=", "/=");
    +
     4836.      1    }
    +
     4837.    597
    +
     4838.    121    function prefix_async() {
    +
     4839.    121        let the_async;
    +
     4840.    121        let the_function;
    +
     4841.    121        the_async = token_now;
    +
     4842.    121        advance("function");
    +
     4843.    121        the_function = Object.assign(token_now, {
    +
     4844.    121            arity: the_async.arity,
    +
     4845.    121            async: 1
    +
     4846.    121        });
    +
     4847.    121        prefix_function();
    +
     4848.      1        if (the_function.async === 1) {
    +
     4849.      1
    +
     4850.      1// test_cause:
    +
     4851.      1// ["
    +
     4852.      1// async function aa(){}
    +
     4853.      1// ", "prefix_async", "missing_await_statement", "function", 7]
    +
     4854.      1
    +
     4855.      1            warn("missing_await_statement", the_function);
    +
     4856.      1        }
    +
     4857.    121        return the_function;
    +
     4858.    121    }
    +
     4859.    597
    +
     4860.    265    function prefix_await() {
    +
     4861.    265        const the_await = token_now;
    +
     4862.    265
    +
     4863.    265// PR-370 - Add top-level-await support.
    +
     4864.    265
    +
     4865.      3        if (functionage.async === 0 && functionage !== token_global) {
    +
     4866.      2
    +
     4867.      2// test_cause:
    +
     4868.      2// ["function aa(){aa=await 0;}", "prefix_await", "unexpected_a", "await", 18]
    +
     4869.      2// ["function aa(){await 0;}", "prefix_await", "unexpected_a", "await", 15]
    +
     4870.      2
    +
     4871.      2            warn("unexpected_a", the_await);
    +
     4872.    263        } else {
    +
     4873.    263            functionage.async += 1;
    +
     4874.    263        }
    +
     4875.    161        if (the_await.arity === "statement") {
    +
     4876.    161            the_await.block = parse_expression();
    +
     4877.    161            semicolon();
    +
     4878.    161        } else {
    +
     4879.    104            the_await.expression = parse_expression();
    +
     4880.    104        }
    +
     4881.    265        return the_await;
    +
     4882.    265    }
    +
     4883.    597
    +
     4884.      1    function prefix_fart() {
    +
     4885.      1
    +
     4886.      1// test_cause:
    +
     4887.      1// ["=>0", "prefix_fart", "expected_a_before_b", "=>", 1]
    +
     4888.      1
    +
     4889.      1        return stop("expected_a_before_b", token_now, "()", "=>");
    +
     4890.      1    }
    +
     4891.    597
    +
     4892.   1874    function prefix_function(the_function) {
    +
     4893.     11        let name = the_function && the_function.name;
    +
     4894.   1863        if (the_function === undefined) {
    +
     4895.   1863            the_function = token_now;
    +
     4896.   1863
    +
     4897.   1863// A function statement must have a name that will be in the parent's scope.
    +
     4898.   1863
    +
     4899.   1863            if (the_function.arity === "statement") {
    +
     4900.   1863                if (!token_nxt.identifier) {
    +
     4901.   1863
    +
     4902.   1863// test_cause:
    +
     4903.   1863// ["function(){}", "prefix_function", "expected_identifier_a", "(", 9]
    +
     4904.   1863// ["function*aa(){}", "prefix_function", "expected_identifier_a", "*", 9]
    +
     4905.   1863
    +
     4906.   1863                    return stop("expected_identifier_a");
    +
     4907.   1863                }
    +
     4908.   1863                name = token_nxt;
    +
     4909.   1863                enroll(name, "variable", true);
    +
     4910.   1863                the_function.name = Object.assign(name, {
    +
     4911.   1863                    calls: empty(),
    +
     4912.   1863
    +
     4913.   1863// PR-331 - Bugfix - Fixes issue #272 - function hoisting not allowed.
    +
     4914.   1863
    +
     4915.   1863                    dead: false,
    +
     4916.   1863                    init: true
    +
     4917.   1863                });
    +
     4918.   1863                advance();
    +
     4919.   1863            } else if (name === undefined) {
    +
     4920.   1863
    +
     4921.   1863// A function expression may have an optional name.
    +
     4922.   1863
    +
     4923.   1863                the_function.name = anon;
    +
     4924.   1863                if (token_nxt.identifier) {
    +
     4925.   1863                    name = token_nxt;
    +
     4926.   1863                    the_function.name = name;
    +
     4927.   1863                    advance();
    +
     4928.   1863                }
    +
     4929.   1863            }
    +
     4930.   1872        }
    +
     4931.   1872        the_function.level = functionage.level + 1;
    +
     4932.   1872
    +
     4933.   1872//  Probably deadcode.
    +
     4934.   1872//  if (mode_mega) {
    +
     4935.   1872//      warn("unexpected_a", the_function);
    +
     4936.   1872//  }
    +
     4937.   1872//  jslint_assert(!mode_mega, `Expected !mode_mega.`);
    +
     4938.   1872
    +
     4939.   1872// PR-378 - Relax warning "function_in_loop".
    +
     4940.   1872//
    +
     4941.   1872// // Don't create functions in loops. It is inefficient, and it can lead to
    +
     4942.   1872// // scoping errors.
    +
     4943.   1872//
    +
     4944.   1872//         if (functionage.loop > 0) {
    +
     4945.   1872//
    +
     4946.   1872// // test_cause:
    +
     4947.   1872// // ["
    +
     4948.   1872// // while(0){aa.map(function(){});}
    +
     4949.   1872// // ", "prefix_function", "function_in_loop", "function", 17]
    +
     4950.   1872//
    +
     4951.   1872//             warn("function_in_loop", the_function);
    +
     4952.   1872//         }
    +
     4953.   1872
    +
     4954.   1872// Give the function properties for storing its names and for observing the
    +
     4955.   1872// depth of loops and switches.
    +
     4956.   1872
    +
     4957.   1872        Object.assign(the_function, {
    +
     4958.   1872            async: the_function.async || 0,
    +
     4959.   1874            context: empty(),
    +
     4960.   1874            finally: 0,
    +
     4961.   1874            loop: 0,
    +
     4962.   1874            statement_prv: undefined,
    +
     4963.   1874            switch: 0,
    +
     4964.   1874            try: 0
    +
     4965.   1874        });
    +
     4966.    825        if (the_function.arity !== "statement" && typeof name === "object") {
    +
     4967.     39
    +
     4968.     39// test_cause:
    +
     4969.     39// ["let aa=function bb(){return;};", "prefix_function", "expression", "", 0]
    +
     4970.     39
    +
     4971.     39            test_cause("expression");
    +
     4972.     39            enroll(name, "function", true);
    +
     4973.     39            name.dead = false;
    +
     4974.     39            name.init = true;
    +
     4975.     39            name.used = 1;
    +
     4976.   1872        }
    +
     4977.   1872
    +
     4978.   1872// Bugfix - fix function-redefinitions not warned inside function-calls.
    +
     4979.   1872// Push the current function context and establish a new one.
    +
     4980.   1872
    +
     4981.   1872        function_stack.push(functionage);
    +
     4982.   1872        function_list.push(the_function);
    +
     4983.   1872        functionage = the_function;
    +
     4984.   1872
    +
     4985.   1872// Parse the parameter list.
    +
     4986.   1872
    +
     4987.   1872        advance("(");
    +
     4988.   1872
    +
     4989.   1872// test_cause:
    +
     4990.   1872// ["function aa(){}", "prefix_function", "opener", "", 0]
    +
     4991.   1872
    +
     4992.   1872        test_cause("opener");
    +
     4993.   1872        token_now.free = false;
    +
     4994.   1872        token_now.arity = "function";
    +
     4995.   1872        [functionage.parameters, functionage.signature] = prefix_function_arg();
    +
     4996.   2576        functionage.parameters.forEach(function enroll_parameter(name) {
    +
     4997.   2336            if (name.identifier) {
    +
     4998.   2336                enroll(name, "parameter", false);
    +
     4999.   2336            } else {
    +
     5000.   1872                name.names.forEach(enroll_parameter);
    +
     5001.   1872            }
    +
     5002.   2576        });
    +
     5003.   1872
    +
     5004.   1872// The function's body is a block.
    +
     5005.   1872
    +
     5006.   1872        the_function.block = block("body");
    +
     5007.   1872        if (
    +
     5008.   1872            the_function.arity === "statement"
    +
     5009.   1872            && token_nxt.line === token_now.line
    +
     5010.      2        ) {
    +
     5011.      2
    +
     5012.      2// test_cause:
    +
     5013.      2// ["function aa(){}0", "prefix_function", "unexpected_a", "0", 16]
    +
     5014.      2
    +
     5015.      2            return stop("unexpected_a");
    +
     5016.   1859        }
    +
     5017.   1859        if (
    +
     5018.   1859            token_nxt.id === "."
    +
     5019.   1859            || token_nxt.id === "?."
    +
     5020.   1857            || token_nxt.id === "["
    +
     5021.      3        ) {
    +
     5022.      3
    +
     5023.      3// test_cause:
    +
     5024.      3// ["function aa(){}\n.aa", "prefix_function", "unexpected_a", ".", 1]
    +
     5025.      3// ["function aa(){}\n?.aa", "prefix_function", "unexpected_a", "?.", 1]
    +
     5026.      3// ["function aa(){}\n[]", "prefix_function", "unexpected_a", "[", 1]
    +
     5027.      3
    +
     5028.      3            warn("unexpected_a");
    +
     5029.   1859        }
    +
     5030.   1859
    +
     5031.   1859// Check functions are ordered.
    +
     5032.   1859
    +
     5033.   1859        check_ordered(
    +
     5034.   1859            "function",
    +
     5035.   1859            function_list.slice(
    +
     5036.   1859                function_list.indexOf(the_function) + 1
    +
     5037.   2340            ).map(function ({
    +
     5038.   2340                level,
    +
     5039.   2340                name
    +
     5040.   2340            }) {
    +
     5041.   1859                return (level === the_function.level + 1) && name;
    +
     5042.   2340            }).filter(function (name) {
    +
     5043.   1859                return option_dict.beta && name && name.id;
    +
     5044.   2340            })
    +
     5045.   1859        );
    +
     5046.   1859
    +
     5047.   1859// Restore the previous context.
    +
     5048.   1859
    +
     5049.   1859        functionage = function_stack.pop();
    +
     5050.   1859        return the_function;
    +
     5051.   1859    }
    +
     5052.    597
    +
     5053.   1880    function prefix_function_arg() {
    +
     5054.   1880        const list = [];
    +
     5055.   1880        const signature = ["("];
    +
     5056.   1880        let optional;
    +
     5057.   1880        let subparam;
    +
     5058.   1942        function parameter() {
    +
     5059.   1942            let ellipsis = false;
    +
     5060.   1942            let param;
    +
     5061.    210            if (token_nxt.id === "{") {
    +
     5062.    210                if (optional !== undefined) {
    +
     5063.    210
    +
     5064.    210// test_cause:
    +
     5065.    210// ["function aa(aa=0,{}){}", "parameter", "required_a_optional_b", "aa", 18]
    +
     5066.    210
    +
     5067.    210                    warn(
    +
     5068.    210                        "required_a_optional_b",
    +
     5069.    210                        token_nxt,
    +
     5070.    210                        token_nxt.id,
    +
     5071.    210                        optional.id
    +
     5072.    210                    );
    +
     5073.    210                }
    +
     5074.    210                param = token_nxt;
    +
     5075.    210                param.names = [];
    +
     5076.    210                advance("{");
    +
     5077.    210                signature.push("{");
    +
     5078.    574                while (true) {
    +
     5079.    574                    subparam = token_nxt;
    +
     5080.    574                    if (!subparam.identifier) {
    +
     5081.    574
    +
     5082.    574// test_cause:
    +
     5083.    574// ["function aa(aa=0,{}){}", "parameter", "expected_identifier_a", "}", 19]
    +
     5084.    574// ["function aa({0}){}", "parameter", "expected_identifier_a", "0", 14]
    +
     5085.    574
    +
     5086.    574                        return stop("expected_identifier_a");
    +
     5087.    574                    }
    +
     5088.    574                    survey(subparam);
    +
     5089.    574                    advance();
    +
     5090.    574                    signature.push(subparam.id);
    +
     5091.    574                    if (token_nxt.id === ":") {
    +
     5092.    574                        advance(":");
    +
     5093.    574                        advance();
    +
     5094.    574                        token_now.label = subparam;
    +
     5095.    574                        subparam = token_now;
    +
     5096.    574                        if (!subparam.identifier) {
    +
     5097.    574
    +
     5098.    574// test_cause:
    +
     5099.    574// ["function aa({aa:0}){}", "parameter", "expected_identifier_a", "}", 18]
    +
     5100.    574
    +
     5101.    574                            return stop(
    +
     5102.    574                                "expected_identifier_a",
    +
     5103.    574                                token_nxt
    +
     5104.    574                            );
    +
     5105.    574                        }
    +
     5106.    574                    }
    +
     5107.    574
    +
     5108.    574// test_cause:
    +
     5109.    574// ["function aa({aa=aa},aa){}", "parameter", "equal", "", 0]
    +
     5110.    574
    +
     5111.    574                    test_cause("equal");
    +
     5112.    574                    if (token_nxt.id === "=") {
    +
     5113.    574                        advance("=");
    +
     5114.    574                        subparam.expression = parse_expression();
    +
     5115.    574                        param.open = true;
    +
     5116.    574                    }
    +
     5117.    574                    param.names.push(subparam);
    +
     5118.    574                    if (token_nxt.id === ",") {
    +
     5119.    574                        advance(",");
    +
     5120.    574                        signature.push(", ");
    +
     5121.    574                    } else {
    +
     5122.    574                        break;
    +
     5123.    574                    }
    +
     5124.    574                }
    +
     5125.    210                list.push(param);
    +
     5126.    210
    +
     5127.    210// test_cause:
    +
     5128.    210// ["
    +
     5129.    210// function aa({bb,aa}){}
    +
     5130.    210// ", "check_ordered", "expected_a_b_before_c_d", "aa", 17]
    +
     5131.    210
    +
     5132.    210                check_ordered("parameter", param.names);
    +
     5133.    210                advance("}");
    +
     5134.    210                signature.push("}");
    +
     5135.    210                if (token_nxt.id === ",") {
    +
     5136.    210                    advance(",");
    +
     5137.    210                    signature.push(", ");
    +
     5138.    210                    return parameter();
    +
     5139.    210                }
    +
     5140.   1732            } else if (token_nxt.id === "[") {
    +
     5141.   1732                if (optional !== undefined) {
    +
     5142.   1732
    +
     5143.   1732// test_cause:
    +
     5144.   1732// ["function aa(aa=0,[]){}", "parameter", "required_a_optional_b", "aa", 18]
    +
     5145.   1732
    +
     5146.   1732                    warn(
    +
     5147.   1732                        "required_a_optional_b",
    +
     5148.   1732                        token_nxt,
    +
     5149.   1732                        token_nxt.id,
    +
     5150.   1732                        optional.id
    +
     5151.   1732                    );
    +
     5152.   1732                }
    +
     5153.   1732                param = token_nxt;
    +
     5154.   1732                param.names = [];
    +
     5155.   1732                advance("[");
    +
     5156.   1732                signature.push("[]");
    +
     5157.   1732                while (true) {
    +
     5158.   1732                    subparam = token_nxt;
    +
     5159.   1732                    if (!subparam.identifier) {
    +
     5160.   1732
    +
     5161.   1732// test_cause:
    +
     5162.   1732// ["function aa(aa=0,[]){}", "parameter", "expected_identifier_a", "]", 19]
    +
     5163.   1732
    +
     5164.   1732                        return stop("expected_identifier_a");
    +
     5165.   1732                    }
    +
     5166.   1732                    advance();
    +
     5167.   1732                    param.names.push(subparam);
    +
     5168.   1732
    +
     5169.   1732// test_cause:
    +
     5170.   1732// ["function aa([aa=aa],aa){}", "parameter", "id", "", 0]
    +
     5171.   1732
    +
     5172.   1732                    test_cause("id");
    +
     5173.   1732                    if (token_nxt.id === "=") {
    +
     5174.   1732                        advance("=");
    +
     5175.   1732                        subparam.expression = parse_expression();
    +
     5176.   1732                        param.open = true;
    +
     5177.   1732                    }
    +
     5178.   1732                    if (token_nxt.id === ",") {
    +
     5179.   1732                        advance(",");
    +
     5180.   1732                    } else {
    +
     5181.   1732                        break;
    +
     5182.   1732                    }
    +
     5183.   1732                }
    +
     5184.   1732                list.push(param);
    +
     5185.   1732                advance("]");
    +
     5186.   1732                if (token_nxt.id === ",") {
    +
     5187.   1732                    advance(",");
    +
     5188.   1732                    signature.push(", ");
    +
     5189.   1732                    return parameter();
    +
     5190.   1732                }
    +
     5191.   1732            } else {
    +
     5192.   1732                if (token_nxt.id === "...") {
    +
     5193.   1732                    ellipsis = true;
    +
     5194.   1732                    signature.push("...");
    +
     5195.   1732                    advance("...");
    +
     5196.   1732                    if (optional !== undefined) {
    +
     5197.   1732
    +
     5198.   1732// test_cause:
    +
     5199.   1732// ["function aa(aa=0,...){}", "parameter", "required_a_optional_b", "aa", 21]
    +
     5200.   1732
    +
     5201.   1732                        warn(
    +
     5202.   1732                            "required_a_optional_b",
    +
     5203.   1732                            token_nxt,
    +
     5204.   1732                            token_nxt.id,
    +
     5205.   1732                            optional.id
    +
     5206.   1732                        );
    +
     5207.   1732                    }
    +
     5208.   1732                }
    +
     5209.   1732                if (!token_nxt.identifier) {
    +
     5210.   1732
    +
     5211.   1732// test_cause:
    +
     5212.   1732// ["function aa(0){}", "parameter", "expected_identifier_a", "0", 13]
    +
     5213.   1732
    +
     5214.   1732                    return stop("expected_identifier_a");
    +
     5215.   1732                }
    +
     5216.   1732                param = token_nxt;
    +
     5217.   1732                list.push(param);
    +
     5218.   1732                advance();
    +
     5219.   1732                signature.push(param.id);
    +
     5220.   1732                if (ellipsis) {
    +
     5221.   1732                    param.ellipsis = true;
    +
     5222.   1732                } else {
    +
     5223.   1732                    if (token_nxt.id === "=") {
    +
     5224.   1732                        optional = param;
    +
     5225.   1732                        advance("=");
    +
     5226.   1732                        param.expression = parse_expression(0);
    +
     5227.   1732                    } else {
    +
     5228.   1732                        if (optional !== undefined) {
    +
     5229.   1732
    +
     5230.   1732// test_cause:
    +
     5231.   1732// ["function aa(aa=0,bb){}", "parameter", "required_a_optional_b", "aa", 18]
    +
     5232.   1732
    +
     5233.   1732                            warn(
    +
     5234.   1732                                "required_a_optional_b",
    +
     5235.   1732                                param,
    +
     5236.   1732                                param.id,
    +
     5237.   1732                                optional.id
    +
     5238.   1732                            );
    +
     5239.   1732                        }
    +
     5240.   1732                    }
    +
     5241.   1732                    if (token_nxt.id === ",") {
    +
     5242.   1732                        advance(",");
    +
     5243.   1732                        signature.push(", ");
    +
     5244.   1732                        return parameter();
    +
     5245.   1732                    }
    +
     5246.   1732                }
    +
     5247.   1732            }
    +
     5248.   1942        }
    +
     5249.   1379        if (token_nxt.id !== ")" && token_nxt.id !== "(end)") {
    +
     5250.   1379            parameter();
    +
     5251.   1872        }
    +
     5252.   1872        advance(")");
    +
     5253.   1872        signature.push(")");
    +
     5254.   1872        return [list, signature.join("")];
    +
     5255.   1872    }
    +
     5256.    597
    +
     5257.    508    function prefix_lbrace() {
    +
     5258.    508        const seen = empty();
    +
     5259.    508        const the_brace = token_now;
    +
     5260.    508        let extra;
    +
     5261.    508        let full;
    +
     5262.    508        let id;
    +
     5263.    508        let name;
    +
     5264.    508        let the_colon;
    +
     5265.    508        let value;
    +
     5266.    508        the_brace.expression = [];
    +
     5267.    462        if (token_nxt.id !== "}") {
    +
     5268.    462
    +
     5269.    462// Parse/loop through each property in {...}.
    +
     5270.    462
    +
     5271.   1710            while (true) {
    +
     5272.   1710                name = token_nxt;
    +
     5273.   1710                advance();
    +
     5274.   1710                if (
    +
     5275.   1710                    (name.id === "get" || name.id === "set")
    +
     5276.   1710                    && token_nxt.identifier
    +
     5277.   1710                ) {
    +
     5278.   1710                    if (!option_dict.getset) {
    +
     5279.   1710
    +
     5280.   1710// test_cause:
    +
     5281.   1710// ["aa={get aa(){}}", "prefix_lbrace", "unexpected_a", "get", 5]
    +
     5282.   1710
    +
     5283.   1710                        warn("unexpected_a", name);
    +
     5284.   1710                    }
    +
     5285.   1710                    extra = name.id;
    +
     5286.   1710                    full = extra + " " + token_nxt.id;
    +
     5287.   1710                    name = token_nxt;
    +
     5288.   1710                    advance();
    +
     5289.   1710                    id = survey(name);
    +
     5290.   1710                    if (seen[full] === true || seen[id] === true) {
    +
     5291.   1710
    +
     5292.   1710// test_cause:
    +
     5293.   1710// ["aa={get aa(){},get aa(){}}", "prefix_lbrace", "duplicate_a", "aa", 20]
    +
     5294.   1710
    +
     5295.   1710                        warn("duplicate_a", name);
    +
     5296.   1710                    }
    +
     5297.   1710                    seen[id] = false;
    +
     5298.   1710                    seen[full] = true;
    +
     5299.   1710                } else if (name.id === "`") {
    +
     5300.   1710
    +
     5301.   1710// test_cause:
    +
     5302.   1710// ["aa={`aa`:0}", "prefix_lbrace", "unexpected_a", "`", 5]
    +
     5303.   1710
    +
     5304.   1710                    stop("unexpected_a", name);
    +
     5305.   1710
    +
     5306.   1710                } else {
    +
     5307.   1710                    id = survey(name);
    +
     5308.   1710                    if (typeof seen[id] === "boolean") {
    +
     5309.   1710
    +
     5310.   1710// test_cause:
    +
     5311.   1710// ["aa={aa,aa}", "prefix_lbrace", "duplicate_a", "aa", 8]
    +
     5312.   1710
    +
     5313.   1710                        warn("duplicate_a", name);
    +
     5314.   1710                    }
    +
     5315.   1710                    seen[id] = true;
    +
     5316.   1710                }
    +
     5317.   1710                if (name.identifier) {
    +
     5318.   1710                    if (token_nxt.id === "}" || token_nxt.id === ",") {
    +
     5319.   1710                        if (typeof extra === "string") {
    +
     5320.   1710
    +
     5321.   1710// test_cause:
    +
     5322.   1710// ["aa={get aa}", "prefix_lbrace", "closer", "", 0]
    +
     5323.   1710
    +
     5324.   1710                            test_cause("closer");
    +
     5325.   1710                            advance("(");
    +
     5326.   1710                        }
    +
     5327.   1710                        value = parse_expression(Infinity, true);
    +
     5328.   1710                    } else if (token_nxt.id === "(") {
    +
     5329.   1710
    +
     5330.   1710// test_cause:
    +
     5331.   1710// ["aa={aa()}", "prefix_lbrace", "paren", "", 0]
    +
     5332.   1710// ["aa={get aa(){}}", "prefix_lbrace", "paren", "", 0]
    +
     5333.   1710
    +
     5334.   1710                        test_cause("paren");
    +
     5335.   1710                        value = prefix_function({
    +
     5336.   1710                            arity: "unary",
    +
     5337.   1710                            from: name.from,
    +
     5338.   1710                            id: "function",
    +
     5339.   1710                            line: name.line,
    +
     5340.   1710                            name: (
    +
     5341.   1710                                typeof extra === "string"
    +
     5342.   1710                                ? extra
    +
     5343.   1710                                : id
    +
     5344.   1710                            ),
    +
     5345.   1710                            thru: name.from
    +
     5346.   1710                        });
    +
     5347.   1710                    } else {
    +
     5348.   1710                        if (typeof extra === "string") {
    +
     5349.   1710
    +
     5350.   1710// test_cause:
    +
     5351.   1710// ["aa={get aa.aa}", "prefix_lbrace", "paren", "", 0]
    +
     5352.   1710
    +
     5353.   1710                            test_cause("paren");
    +
     5354.   1710                            advance("(");
    +
     5355.   1710                        }
    +
     5356.   1710                        the_colon = token_nxt;
    +
     5357.   1710                        advance(":");
    +
     5358.   1710                        value = parse_expression(0);
    +
     5359.   1710                        if (
    +
     5360.   1710                            value.id === name.id
    +
     5361.   1710                            && value.id !== "function"
    +
     5362.   1710                        ) {
    +
     5363.   1710
    +
     5364.   1710// test_cause:
    +
     5365.   1710// ["aa={aa:aa}", "prefix_lbrace", "unexpected_a", ": aa", 7]
    +
     5366.   1710
    +
     5367.   1710                            warn("unexpected_a", the_colon, ": " + name.id);
    +
     5368.   1710                        }
    +
     5369.   1710                    }
    +
     5370.   1710                    value.label = name;
    +
     5371.   1710                    if (typeof extra === "string") {
    +
     5372.   1710                        value.extra = extra;
    +
     5373.   1710                    }
    +
     5374.   1710                    the_brace.expression.push(value);
    +
     5375.   1710                } else {
    +
     5376.   1710
    +
     5377.   1710// test_cause:
    +
     5378.   1710// ["aa={\"aa\":0}", "prefix_lbrace", "colon", "", 0]
    +
     5379.   1710
    +
     5380.   1710                    test_cause("colon");
    +
     5381.   1710                    advance(":");
    +
     5382.   1710                    value = parse_expression(0);
    +
     5383.   1710                    value.label = name;
    +
     5384.   1710                    the_brace.expression.push(value);
    +
     5385.   1710                }
    +
     5386.   1710                if (token_nxt.id !== ",") {
    +
     5387.   1710                    break;
    +
     5388.   1710                }
    +
     5389.   1710
    +
     5390.   1710// test_cause:
    +
     5391.   1710// ["aa={\"aa\":0,\"bb\":0}", "prefix_lbrace", "comma", "", 0]
    +
     5392.   1710
    +
     5393.   1710                test_cause("comma");
    +
     5394.   1710                advance(",");
    +
     5395.   1710                if (token_nxt.id === "}") {
    +
     5396.   1710
    +
     5397.   1710// test_cause:
    +
     5398.   1710// ["let aa={aa:0,}", "prefix_lbrace", "unexpected_a", ",", 13]
    +
     5399.   1710
    +
     5400.   1710                    warn("unexpected_a", token_now);
    +
     5401.   1710                    break;
    +
     5402.   1710                }
    +
     5403.   1710            }
    +
     5404.    503        }
    +
     5405.    503
    +
     5406.    503// test_cause:
    +
     5407.    503// ["aa={bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8]
    +
     5408.    503
    +
     5409.    503        check_ordered(
    +
     5410.    503            "property",
    +
     5411.   1705            the_brace.expression.map(function ({
    +
     5412.   1705                label
    +
     5413.   1705            }) {
    +
     5414.   1705                return label;
    +
     5415.   1705            })
    +
     5416.    503        );
    +
     5417.    503        advance("}");
    +
     5418.    503        return the_brace;
    +
     5419.    503    }
    +
     5420.    597
    +
     5421.    678    function prefix_lbracket() {
    +
     5422.    678        const the_token = token_now;
    +
     5423.    678        let element;
    +
     5424.    678        let ellipsis;
    +
     5425.    678        the_token.expression = [];
    +
     5426.    345        if (token_nxt.id !== "]") {
    +
     5427.    345
    +
     5428.    345// Parse/loop through each element in [...].
    +
     5429.    345
    +
     5430.   1616            while (true) {
    +
     5431.   1616                ellipsis = false;
    +
     5432.   1616                if (token_nxt.id === "...") {
    +
     5433.   1616                    ellipsis = true;
    +
     5434.   1616                    advance("...");
    +
     5435.   1616                }
    +
     5436.   1616                element = parse_expression(10);
    +
     5437.   1616                if (ellipsis) {
    +
     5438.   1616                    element.ellipsis = true;
    +
     5439.   1616                }
    +
     5440.   1616                the_token.expression.push(element);
    +
     5441.   1616                if (token_nxt.id !== ",") {
    +
     5442.   1616                    break;
    +
     5443.   1616                }
    +
     5444.   1616                advance(",");
    +
     5445.   1616                if (token_nxt.id === "]") {
    +
     5446.   1616
    +
     5447.   1616// test_cause:
    +
     5448.   1616// ["let aa=[0,]", "prefix_lbracket", "unexpected_a", ",", 10]
    +
     5449.   1616
    +
     5450.   1616                    warn("unexpected_a", token_now);
    +
     5451.   1616                    break;
    +
     5452.   1616                }
    +
     5453.   1616            }
    +
     5454.    345        }
    +
     5455.    678        advance("]");
    +
     5456.    678        return the_token;
    +
     5457.    678    }
    +
     5458.    597
    +
     5459.   1371    function prefix_lparen() {
    +
     5460.   1371        const cadet = lookahead().id;
    +
     5461.   1371        const the_paren = token_now;
    +
     5462.   1371        let the_value;
    +
     5463.   1371
    +
     5464.   1371// We can distinguish between a parameter list for => and a wrapped expression
    +
     5465.   1371// with one token of lookahead.
    +
     5466.   1371
    +
     5467.   1371        if (
    +
     5468.   1371            token_nxt.id === ")"
    +
     5469.   1363            || token_nxt.id === "..."
    +
     5470.   1363            || (token_nxt.identifier && (cadet === "," || cadet === "="))
    +
     5471.      8        ) {
    +
     5472.      8
    +
     5473.      8// test_cause:
    +
     5474.      8// ["()=>0", "prefix_lparen", "fart", "", 0]
    +
     5475.      8
    +
     5476.      8            test_cause("fart");
    +
     5477.      8            the_paren.free = false;
    +
     5478.      8            return parse_fart(prefix_function_arg());
    +
     5479.   1363        }
    +
     5480.   1363
    +
     5481.   1363// test_cause:
    +
     5482.   1363// ["(0)", "prefix_lparen", "expr", "", 0]
    +
     5483.   1363
    +
     5484.   1363        test_cause("expr");
    +
     5485.   1363        the_paren.free = true;
    +
     5486.   1363        the_value = parse_expression(0);
    +
     5487.   1363        if (the_value.wrapped === true) {
    +
     5488.      1
    +
     5489.      1// test_cause:
    +
     5490.      1// ["((0))", "prefix_lparen", "unexpected_a", "(", 1]
    +
     5491.      1
    +
     5492.      1            warn("unexpected_a", the_paren);
    +
     5493.   1363        }
    +
     5494.   1363        the_value.wrapped = true;
    +
     5495.   1363        advance(")", the_paren);
    +
     5496.   1363        if (token_nxt.id === "=>") {
    +
     5497.      7            if (the_value.arity !== "variable") {
    +
     5498.      7                if (the_value.id === "{" || the_value.id === "[") {
    +
     5499.      7
    +
     5500.      7// test_cause:
    +
     5501.      7// ["([])=>0", "prefix_lparen", "expected_a_before_b", "(", 1]
    +
     5502.      7// ["({})=>0", "prefix_lparen", "expected_a_before_b", "(", 1]
    +
     5503.      7
    +
     5504.      7                    warn("expected_a_before_b", the_paren, "function", "(");
    +
     5505.      7
    +
     5506.      7// test_cause:
    +
     5507.      7// ["([])=>0", "prefix_lparen", "expected_a_b", "=>", 5]
    +
     5508.      7// ["({})=>0", "prefix_lparen", "expected_a_b", "=>", 5]
    +
     5509.      7
    +
     5510.      7                    return stop("expected_a_b", token_nxt, "{", "=>");
    +
     5511.      7                }
    +
     5512.      7
    +
     5513.      7// test_cause:
    +
     5514.      7// ["(0)=>0", "prefix_lparen", "expected_identifier_a", "0", 2]
    +
     5515.      7
    +
     5516.      7                return stop("expected_identifier_a", the_value);
    +
     5517.      7            }
    +
     5518.      7            the_paren.expression = [the_value];
    +
     5519.      7            return parse_fart([the_paren.expression, "(" + the_value.id + ")"]);
    +
     5520.   1355        }
    +
     5521.   1355        return the_value;
    +
     5522.   1355    }
    +
     5523.    597
    +
     5524.    128    function prefix_new() {
    +
     5525.    128        const the_new = token_now;
    +
     5526.    128        let right;
    +
     5527.    128        right = parse_expression(160);
    +
     5528.      1        if (token_nxt.id !== "(") {
    +
     5529.      1
    +
     5530.      1// test_cause:
    +
     5531.      1// ["new aa", "prefix_new", "expected_a_before_b", "(end)", 1]
    +
     5532.      1
    +
     5533.      1            warn("expected_a_before_b", token_nxt, "()", artifact());
    +
     5534.      1        }
    +
     5535.    128        the_new.expression = right;
    +
     5536.    128        return the_new;
    +
     5537.    128    }
    +
     5538.    597
    +
     5539.    707    function prefix_tick() {
    +
     5540.    707        const the_tick = token_now;
    +
     5541.    707        the_tick.value = [];
    +
     5542.    707        the_tick.expression = [];
    +
     5543.    707        if (token_nxt.id !== "`") {
    +
     5544.    707
    +
     5545.    707// Parse/loop through each token in `${...}`.
    +
     5546.    707
    +
     5547.   1358            while (true) {
    +
     5548.   1358                advance("(string)");
    +
     5549.   1358                the_tick.value.push(token_now);
    +
     5550.   1358                if (token_nxt.id !== "${") {
    +
     5551.   1358                    break;
    +
     5552.   1358                }
    +
     5553.   1358                advance("${");
    +
     5554.   1358
    +
     5555.   1358// test_cause:
    +
     5556.   1358// ["let aa=`${}`;", "prefix_tick", "${", "", 0]
    +
     5557.   1358
    +
     5558.   1358                test_cause("${");
    +
     5559.   1358                the_tick.expression.push(parse_expression(0));
    +
     5560.   1358                advance("}");
    +
     5561.   1358            }
    +
     5562.    705        }
    +
     5563.    705        advance("`");
    +
     5564.    705        return the_tick;
    +
     5565.    705    }
    +
     5566.    597
    +
     5567.      2    function prefix_void() {
    +
     5568.      2        const the_void = token_now;
    +
     5569.      2
    +
     5570.      2// test_cause:
    +
     5571.      2// ["void 0", "prefix_void", "unexpected_a", "void", 1]
    +
     5572.      2// ["void", "prefix_void", "unexpected_a", "void", 1]
    +
     5573.      2
    +
     5574.      2        warn("unexpected_a", the_void);
    +
     5575.      2        the_void.expression = parse_expression(0);
    +
     5576.      2        return the_void;
    +
     5577.      2    }
    +
     5578.    597
    +
     5579.  12706    function semicolon() {
    +
     5580.  12706
    +
     5581.  12706// Try to match a semicolon.
    +
     5582.  12706
    +
     5583.  12488        if (token_nxt.id === ";") {
    +
     5584.  12488            advance(";");
    +
     5585.  12488        } else {
    +
     5586.    218
    +
     5587.    218// test_cause:
    +
     5588.    218// ["0", "semicolon", "expected_a_b", "(end)", 1]
    +
     5589.    218
    +
     5590.    218            warn_at(
    +
     5591.    218                "expected_a_b",
    +
     5592.    218                token_now.line,
    +
     5593.    218                token_now.thru + 1,
    +
     5594.    218                ";",
    +
     5595.    218                artifact()
    +
     5596.    218            );
    +
     5597.    218        }
    +
     5598.  12706        anon = "anonymous";
    +
     5599.  12706    }
    +
     5600.    597
    +
     5601.  13731    function stmt(id, fud) {
    +
     5602.  13731
    +
     5603.  13731// Create a statement.
    +
     5604.  13731
    +
     5605.  13731        const the_symbol = symbol(id);
    +
     5606.  13731        the_symbol.fud = fud;
    +
     5607.  13731        return the_symbol;
    +
     5608.  13731    }
    +
     5609.    597
    +
     5610.    977    function stmt_break() {
    +
     5611.    977        const the_break = token_now;
    +
     5612.    977        let the_label;
    +
     5613.    977        if (
    +
     5614.    688            (functionage.loop < 1 && functionage.switch < 1)
    +
     5615.    970            || functionage.finally > 0
    +
     5616.      7        ) {
    +
     5617.      7
    +
     5618.      7// test_cause:
    +
     5619.      7// ["break", "stmt_break", "unexpected_a", "break", 1]
    +
     5620.      7
    +
     5621.      7            warn("unexpected_a", the_break);
    +
     5622.      7        }
    +
     5623.    977        the_break.disrupt = true;
    +
     5624.      5        if (token_nxt.identifier && token_now.line === token_nxt.line) {
    +
     5625.      5            the_label = functionage.context[token_nxt.id];
    +
     5626.      5            if (
    +
     5627.      5                the_label === undefined
    +
     5628.      5                || the_label.role !== "label"
    +
     5629.      5                || the_label.dead
    +
     5630.      5            ) {
    +
     5631.      5                if (the_label !== undefined && the_label.dead) {
    +
     5632.      5
    +
     5633.      5// test_cause:
    +
     5634.      5// ["aa:{function aa(aa){break aa;}}", "stmt_break", "out_of_scope_a", "aa", 27]
    +
     5635.      5
    +
     5636.      5                    warn("out_of_scope_a");
    +
     5637.      5                } else {
    +
     5638.      5
    +
     5639.      5// test_cause:
    +
     5640.      5// ["aa:{break aa;}", "stmt_break", "not_label_a", "aa", 11]
    +
     5641.      5
    +
     5642.      5                    warn("not_label_a");
    +
     5643.      5                }
    +
     5644.      5            } else {
    +
     5645.      5                the_label.used += 1;
    +
     5646.      5            }
    +
     5647.      5            the_break.label = token_nxt;
    +
     5648.      5            advance();
    +
     5649.      5        }
    +
     5650.    977        advance(";");
    +
     5651.    977        return the_break;
    +
     5652.    977    }
    +
     5653.    597
    +
     5654.      2    function stmt_continue() {
    +
     5655.      2        const the_continue = token_now;
    +
     5656.      1        if (functionage.loop < 1 || functionage.finally > 0) {
    +
     5657.      2
    +
     5658.      2// test_cause:
    +
     5659.      2// ["continue", "stmt_continue", "unexpected_a", "continue", 1]
    +
     5660.      2// ["
    +
     5661.      2// function aa(){while(0){try{}finally{continue}}}
    +
     5662.      2// ", "stmt_continue", "unexpected_a", "continue", 37]
    +
     5663.      2
    +
     5664.      2            warn("unexpected_a", the_continue);
    +
     5665.      2        }
    +
     5666.      2        check_not_top_level(the_continue);
    +
     5667.      2        the_continue.disrupt = true;
    +
     5668.      2        warn("unexpected_a", the_continue);
    +
     5669.      2        advance(";");
    +
     5670.      2        return the_continue;
    +
     5671.      2    }
    +
     5672.    597
    +
     5673.      3    function stmt_debugger() {
    +
     5674.      3        const the_debug = token_now;
    +
     5675.      1        if (!option_dict.devel) {
    +
     5676.      1
    +
     5677.      1// test_cause:
    +
     5678.      1// ["debugger", "stmt_debugger", "unexpected_a", "debugger", 1]
    +
     5679.      1
    +
     5680.      1            warn("unexpected_a", the_debug);
    +
     5681.      1        }
    +
     5682.      3        semicolon();
    +
     5683.      3        return the_debug;
    +
     5684.      3    }
    +
     5685.    597
    +
     5686.     71    function stmt_delete() {
    +
     5687.     71        const the_token = token_now;
    +
     5688.     71        const the_value = parse_expression(0);
    +
     5689.     71        if (
    +
     5690.      1            (the_value.id !== "." && the_value.id !== "[")
    +
     5691.     70            || the_value.arity !== "binary"
    +
     5692.      1        ) {
    +
     5693.      1
    +
     5694.      1// test_cause:
    +
     5695.      1// ["delete 0", "stmt_delete", "expected_a_b", "0", 8]
    +
     5696.      1
    +
     5697.      1            stop("expected_a_b", the_value, ".", artifact(the_value));
    +
     5698.     70        }
    +
     5699.     70        the_token.expression = the_value;
    +
     5700.     70        semicolon();
    +
     5701.     70        return the_token;
    +
     5702.     70    }
    +
     5703.    597
    +
     5704.      5    function stmt_do() {
    +
     5705.      5        const the_do = token_now;
    +
     5706.      5        check_not_top_level(the_do);
    +
     5707.      5        functionage.loop += 1;
    +
     5708.      5        the_do.block = block();
    +
     5709.      5        advance("while");
    +
     5710.      5        the_do.expression = condition();
    +
     5711.      5        semicolon();
    +
     5712.      1        if (the_do.block.disrupt === true) {
    +
     5713.      1
    +
     5714.      1// test_cause:
    +
     5715.      1// ["function aa(){do{break;}while(0)}", "stmt_do", "weird_loop", "do", 15]
    +
     5716.      1
    +
     5717.      1            warn("weird_loop", the_do);
    +
     5718.      3        }
    +
     5719.      3        functionage.loop -= 1;
    +
     5720.      3        return the_do;
    +
     5721.      3    }
    +
     5722.    597
    +
     5723.     21    function stmt_export() {
    +
     5724.     21        const the_export = token_now;
    +
     5725.     21        let the_id;
    +
     5726.     21        let the_name;
    +
     5727.     21        let the_thing;
    +
     5728.     21
    +
     5729.     21        the_export.expression = [];
    +
     5730.     10        if (token_nxt.id === "default") {
    +
     5731.     10            if (export_dict.default !== undefined) {
    +
     5732.     10
    +
     5733.     10// test_cause:
    +
     5734.     10// ["
    +
     5735.     10// export default 0;export default 0
    +
     5736.     10// ", "stmt_export", "duplicate_a", "default", 25]
    +
     5737.     10
    +
     5738.     10                warn("duplicate_a");
    +
     5739.     10            }
    +
     5740.     10            advance("default");
    +
     5741.     10            the_thing = parse_expression(0);
    +
     5742.     10            if (
    +
     5743.     10                the_thing.id !== "("
    +
     5744.     10                || the_thing.expression[0].id !== "."
    +
     5745.     10                || the_thing.expression[0].expression.id !== "Object"
    +
     5746.     10                || the_thing.expression[0].name.id !== "freeze"
    +
     5747.     10            ) {
    +
     5748.     10
    +
     5749.     10// test_cause:
    +
     5750.     10// ["export default {}", "stmt_export", "freeze_exports", "{", 16]
    +
     5751.     10
    +
     5752.     10                warn("freeze_exports", the_thing);
    +
     5753.     10
    +
     5754.     10// PR-301 - Bugfix - Fixes issues #282 - optional-semicolon.
    +
     5755.     10
    +
     5756.     10            } else {
    +
     5757.     10
    +
     5758.     10// test_cause:
    +
     5759.     10// ["
    +
     5760.     10// export default Object.freeze({})
    +
     5761.     10// ", "semicolon", "expected_a_b", "(end)", 32]
    +
     5762.     10
    +
     5763.     10                semicolon();
    +
     5764.     10            }
    +
     5765.     10            export_dict.default = the_thing;
    +
     5766.     10            the_export.expression.push(the_thing);
    +
     5767.     11        } else {
    +
     5768.     11            if (token_nxt.id === "function") {
    +
     5769.     11
    +
     5770.     11// test_cause:
    +
     5771.     11// ["export function aa(){}", "stmt_export", "freeze_exports", "function", 8]
    +
     5772.     11
    +
     5773.     11                warn("freeze_exports");
    +
     5774.     11                the_thing = parse_statement();
    +
     5775.     11                the_name = the_thing.name;
    +
     5776.     11                the_id = the_name.id;
    +
     5777.     11                the_name.used += 1;
    +
     5778.     11                if (export_dict[the_id] !== undefined) {
    +
     5779.     11
    +
     5780.     11// test_cause:
    +
     5781.     11// ["
    +
     5782.     11// let aa;export{aa};export function aa(){}
    +
     5783.     11// ", "stmt_export", "duplicate_a", "aa", 35]
    +
     5784.     11
    +
     5785.     11                    warn("duplicate_a", the_name);
    +
     5786.     11                }
    +
     5787.     11                export_dict[the_id] = the_thing;
    +
     5788.     11                the_export.expression.push(the_thing);
    +
     5789.     11                the_thing.statement = false;
    +
     5790.     11                the_thing.arity = "unary";
    +
     5791.     11            } else if (
    +
     5792.     11                token_nxt.id === "var"
    +
     5793.     11                || token_nxt.id === "let"
    +
     5794.     11                || token_nxt.id === "const"
    +
     5795.     11            ) {
    +
     5796.     11
    +
     5797.     11// test_cause:
    +
     5798.     11// ["export const", "stmt_export", "unexpected_a", "const", 8]
    +
     5799.     11// ["export let", "stmt_export", "unexpected_a", "let", 8]
    +
     5800.     11// ["export var", "stmt_export", "unexpected_a", "var", 8]
    +
     5801.     11
    +
     5802.     11                warn("unexpected_a");
    +
     5803.     11                parse_statement();
    +
     5804.     11            } else if (token_nxt.id === "{") {
    +
     5805.     11
    +
     5806.     11// test_cause:
    +
     5807.     11// ["export {}", "stmt_export", "advance{", "", 0]
    +
     5808.     11
    +
     5809.     11                test_cause("advance{");
    +
     5810.     11                advance("{");
    +
     5811.     11                while (true) {
    +
     5812.     11                    if (!token_nxt.identifier) {
    +
     5813.     11
    +
     5814.     11// test_cause:
    +
     5815.     11// ["export {}", "stmt_export", "expected_identifier_a", "}", 9]
    +
     5816.     11
    +
     5817.     11                        stop("expected_identifier_a");
    +
     5818.     11                    }
    +
     5819.     11                    the_id = token_nxt.id;
    +
     5820.     11                    the_name = token_global.context[the_id];
    +
     5821.     11                    if (the_name === undefined) {
    +
     5822.     11
    +
     5823.     11// test_cause:
    +
     5824.     11// ["export {aa}", "stmt_export", "unexpected_a", "aa", 9]
    +
     5825.     11
    +
     5826.     11                        warn("unexpected_a");
    +
     5827.     11                    } else {
    +
     5828.     11                        the_name.used += 1;
    +
     5829.     11                        if (export_dict[the_id] !== undefined) {
    +
     5830.     11
    +
     5831.     11// test_cause:
    +
     5832.     11// ["let aa;export{aa,aa}", "stmt_export", "duplicate_a", "aa", 18]
    +
     5833.     11
    +
     5834.     11                            warn("duplicate_a");
    +
     5835.     11                        }
    +
     5836.     11                        export_dict[the_id] = the_name;
    +
     5837.     11                    }
    +
     5838.     11                    advance();
    +
     5839.     11                    the_export.expression.push(the_thing);
    +
     5840.     11                    if (token_nxt.id === ",") {
    +
     5841.     11                        advance(",");
    +
     5842.     11                    } else {
    +
     5843.     11                        break;
    +
     5844.     11                    }
    +
     5845.     11                }
    +
     5846.     11                advance("}");
    +
     5847.     11                semicolon();
    +
     5848.     11            } else {
    +
     5849.     11
    +
     5850.     11// test_cause:
    +
     5851.     11// ["export", "stmt_export", "unexpected_a", "(end)", 1]
    +
     5852.     11
    +
     5853.     11                stop("unexpected_a");
    +
     5854.     11            }
    +
     5855.     15        }
    +
     5856.     15        state.mode_module = true;
    +
     5857.     15        return the_export;
    +
     5858.     15    }
    +
     5859.    597
    +
     5860.     12    function stmt_for() {
    +
     5861.     12        let first;
    +
     5862.     12        let the_for = token_now;
    +
     5863.      7        if (!option_dict.for) {
    +
     5864.      7
    +
     5865.      7// test_cause:
    +
     5866.      7// ["for", "stmt_for", "unexpected_a", "for", 1]
    +
     5867.      7
    +
     5868.      7            warn("unexpected_a", the_for);
    +
     5869.      7        }
    +
     5870.     12        check_not_top_level(the_for);
    +
     5871.     12        functionage.loop += 1;
    +
     5872.     12        advance("(");
    +
     5873.     12        token_now.free = true;
    +
     5874.      1        if (token_nxt.id === ";") {
    +
     5875.      1
    +
     5876.      1// test_cause:
    +
     5877.      1// ["for(;;){}", "stmt_for", "expected_a_b", "for (;", 1]
    +
     5878.      1
    +
     5879.      1            return stop("expected_a_b", the_for, "while (", "for (;");
    +
     5880.      9        }
    +
     5881.      9        switch (token_nxt.id) {
    +
     5882.      9        case "const":
    +
     5883.      1        case "let":
    +
     5884.      1        case "var":
    +
     5885.      1
    +
     5886.      1// test_cause:
    +
     5887.      1// ["for(const aa in aa){}", "stmt_for", "unexpected_a", "const", 5]
    +
     5888.      1
    +
     5889.      1            return stop("unexpected_a");
    +
     5890.      8        }
    +
     5891.      8        first = parse_expression(0);
    +
     5892.      8        if (first.id === "in") {
    +
     5893.      2            if (first.expression[0].arity !== "variable") {
    +
     5894.      2
    +
     5895.      2// test_cause:
    +
     5896.      2// ["for(0 in aa){}", "stmt_for", "bad_assignment_a", "0", 5]
    +
     5897.      2
    +
     5898.      2                warn("bad_assignment_a", first.expression[0]);
    +
     5899.      2            }
    +
     5900.      2            the_for.name = first.expression[0];
    +
     5901.      2            the_for.expression = first.expression[1];
    +
     5902.      2            warn("expected_a_b", the_for, "Object.keys", "for in");
    +
     5903.      6        } else {
    +
     5904.      6            the_for.initial = first;
    +
     5905.      6            advance(";");
    +
     5906.      6            the_for.expression = parse_expression(0);
    +
     5907.      6            advance(";");
    +
     5908.      6            the_for.inc = parse_expression(0);
    +
     5909.      6            if (the_for.inc.id === "++") {
    +
     5910.      6
    +
     5911.      6// test_cause:
    +
     5912.      6// ["for(aa;aa;aa++){}", "stmt_for", "expected_a_b", "++", 13]
    +
     5913.      6
    +
     5914.      6                warn("expected_a_b", the_for.inc, "+= 1", "++");
    +
     5915.      6            }
    +
     5916.      8        }
    +
     5917.      8        advance(")");
    +
     5918.      8        the_for.block = block();
    +
     5919.      8        if (the_for.block.disrupt === true) {
    +
     5920.      1
    +
     5921.      1// test_cause:
    +
     5922.      1// ["
    +
     5923.      1// /*jslint for*/
    +
     5924.      1// function aa(bb,cc){for(0;0;0){break;}}
    +
     5925.      1// ", "stmt_for", "weird_loop", "for", 20]
    +
     5926.      1
    +
     5927.      1            warn("weird_loop", the_for);
    +
     5928.      8        }
    +
     5929.      8        functionage.loop -= 1;
    +
     5930.      8        return the_for;
    +
     5931.      8    }
    +
     5932.    597
    +
     5933.   3001    function stmt_if() {
    +
     5934.   3001        const the_if = token_now;
    +
     5935.   3001        let the_else;
    +
     5936.   3001        the_if.expression = condition();
    +
     5937.   3001        the_if.block = block();
    +
     5938.    658        if (token_nxt.id === "else") {
    +
     5939.    658            advance("else");
    +
     5940.    658            the_else = token_now;
    +
     5941.    658            the_if.else = (
    +
     5942.    658                token_nxt.id === "if"
    +
     5943.    658                ? parse_statement()
    +
     5944.    658                : block()
    +
     5945.    658            );
    +
     5946.    658
    +
     5947.    658// test_cause:
    +
     5948.    658// ["if(0){0}else if(0){0}", "stmt_if", "else", "", 0]
    +
     5949.    658// ["if(0){0}else{0}", "stmt_if", "else", "", 0]
    +
     5950.    658
    +
     5951.    658            test_cause("else");
    +
     5952.    658            if (the_if.block.disrupt === true) {
    +
     5953.    658                if (the_if.else.disrupt === true) {
    +
     5954.    658
    +
     5955.    658// test_cause:
    +
     5956.    658// ["if(0){break;}else{break;}", "stmt_if", "disrupt", "", 0]
    +
     5957.    658
    +
     5958.    658                    test_cause("disrupt");
    +
     5959.    658                    the_if.disrupt = true;
    +
     5960.    658                } else {
    +
     5961.    658
    +
     5962.    658// test_cause:
    +
     5963.    658// ["if(0){break;}else{}", "stmt_if", "unexpected_a", "else", 14]
    +
     5964.    658
    +
     5965.    658                    warn("unexpected_a", the_else);
    +
     5966.    658                }
    +
     5967.    658            }
    +
     5968.   3000        }
    +
     5969.   3000        return the_if;
    +
     5970.   3000    }
    +
     5971.    597
    +
     5972.     50    function stmt_import() {
    +
     5973.     50        const the_import = token_now;
    +
     5974.     50        let name;
    +
     5975.     50        let names;
    +
     5976.     50
    +
     5977.     50// PR-347 - Disable warning "unexpected_directive_a".
    +
     5978.     50//         if (typeof state.mode_module === "object") {
    +
     5979.     50//
    +
     5980.     50// // test_cause:
    +
     5981.     50// // ["
    +
     5982.     50// // /*global aa*/
    +
     5983.     50// // import aa from "aa"
    +
     5984.     50// // ", "stmt_import", "unexpected_directive_a", "global", 1]
    +
     5985.     50//
    +
     5986.     50//             warn(
    +
     5987.     50//                 "unexpected_directive_a",
    +
     5988.     50//                 state.mode_module,
    +
     5989.     50//                 state.mode_module.directive
    +
     5990.     50//             );
    +
     5991.     50//         }
    +
     5992.     50
    +
     5993.     50        state.mode_module = true;
    +
     5994.     46        if (token_nxt.identifier) {
    +
     5995.     46            name = token_nxt;
    +
     5996.     46            advance();
    +
     5997.     46            if (name.id === "ignore") {
    +
     5998.     46
    +
     5999.     46// test_cause:
    +
     6000.     46// ["import ignore from \"aa\"", "stmt_import", "unexpected_a", "ignore", 8]
    +
     6001.     46
    +
     6002.     46                warn("unexpected_a", name);
    +
     6003.     46            }
    +
     6004.     46            enroll(name, "variable", true);
    +
     6005.     46            the_import.name = name;
    +
     6006.     46        } else {
    +
     6007.      4            names = [];
    +
     6008.      4            advance("{");
    +
     6009.      4            if (token_nxt.id !== "}") {
    +
     6010.      4                while (true) {
    +
     6011.      4                    if (!token_nxt.identifier) {
    +
     6012.      4
    +
     6013.      4// test_cause:
    +
     6014.      4// ["import {", "stmt_import", "expected_identifier_a", "(end)", 1]
    +
     6015.      4
    +
     6016.      4                        stop("expected_identifier_a");
    +
     6017.      4                    }
    +
     6018.      4                    name = token_nxt;
    +
     6019.      4                    advance();
    +
     6020.      4                    if (name.id === "ignore") {
    +
     6021.      4
    +
     6022.      4// test_cause:
    +
     6023.      4// ["import {ignore} from \"aa\"", "stmt_import", "unexpected_a", "ignore", 9]
    +
     6024.      4
    +
     6025.      4                        warn("unexpected_a", name);
    +
     6026.      4                    }
    +
     6027.      4                    enroll(name, "variable", true);
    +
     6028.      4                    names.push(name);
    +
     6029.      4                    if (token_nxt.id !== ",") {
    +
     6030.      4                        break;
    +
     6031.      4                    }
    +
     6032.      4                    advance(",");
    +
     6033.      4                }
    +
     6034.      4            }
    +
     6035.      4            advance("}");
    +
     6036.      4            the_import.name = names;
    +
     6037.     49        }
    +
     6038.     49        advance("from");
    +
     6039.     49        advance("(string)");
    +
     6040.     49        the_import.import = token_now;
    +
     6041.     49        if (!(
    +
     6042.     49            // rx_module
    +
     6043.     49            /^[a-zA-Z0-9_$:.@\-\/]+$/
    +
     6044.     49        ).test(token_now.value)) {
    +
     6045.      1
    +
     6046.      1// test_cause:
    +
     6047.      1// ["import aa from \"!aa\"", "stmt_import", "bad_module_name_a", "!aa", 16]
    +
     6048.      1
    +
     6049.      1            warn("bad_module_name_a", token_now);
    +
     6050.     49        }
    +
     6051.     49        import_list.push(token_now.value);
    +
     6052.     49        semicolon();
    +
     6053.     49        return the_import;
    +
     6054.     49    }
    +
     6055.    597
    +
     6056.      5    function stmt_lbrace() {
    +
     6057.      5
    +
     6058.      5// test_cause:
    +
     6059.      5// [";{}", "stmt_lbrace", "naked_block", "{", 2]
    +
     6060.      5// ["class aa{}", "stmt_lbrace", "naked_block", "{", 9]
    +
     6061.      5
    +
     6062.      5        warn("naked_block", token_now);
    +
     6063.      5        return block("naked");
    +
     6064.      5    }
    +
     6065.    597
    +
     6066.   1678    function stmt_return() {
    +
     6067.   1678        const the_return = token_now;
    +
     6068.   1678        check_not_top_level(the_return);
    +
     6069.      1        if (functionage.finally > 0) {
    +
     6070.      1
    +
     6071.      1// test_cause:
    +
     6072.      1// ["
    +
     6073.      1// function aa(){try{}finally{return;}}
    +
     6074.      1// ", "stmt_return", "unexpected_a", "return", 28]
    +
     6075.      1
    +
     6076.      1            warn("unexpected_a", the_return);
    +
     6077.      1        }
    +
     6078.   1678        the_return.disrupt = true;
    +
     6079.   1479        if (token_nxt.id !== ";" && the_return.line === token_nxt.line) {
    +
     6080.   1479            the_return.expression = parse_expression(10);
    +
     6081.   1479        }
    +
     6082.   1678        advance(";");
    +
     6083.   1678        return the_return;
    +
     6084.   1678    }
    +
     6085.    597
    +
     6086.      5    function stmt_semicolon() {
    +
     6087.      5
    +
     6088.      5// test_cause:
    +
     6089.      5// [";", "stmt_semicolon", "unexpected_a", ";", 1]
    +
     6090.      5
    +
     6091.      5        warn("unexpected_a", token_now);
    +
     6092.      5        return token_now;
    +
     6093.      5    }
    +
     6094.    597
    +
     6095.    193    function stmt_switch() {
    +
     6096.    193        const the_cases = [];
    +
     6097.    193        const the_switch = token_now;
    +
     6098.    193        let dups = [];
    +
     6099.    193        let exp;
    +
     6100.    193        let last;
    +
     6101.    193        let stmts;
    +
     6102.    193        let the_case;
    +
     6103.    193        let the_default;
    +
     6104.    193        let the_disrupt = true;
    +
     6105.    193        let the_last;
    +
     6106.  21735        function is_dup(thing) {
    +
     6107.  21735            return is_equal(thing, exp);
    +
     6108.  21735        }
    +
     6109.    193        check_not_top_level(the_switch);
    +
     6110.      1        if (functionage.finally > 0) {
    +
     6111.      1
    +
     6112.      1// test_cause:
    +
     6113.      1// ["
    +
     6114.      1// function aa(){try{}finally{switch(0){}}}
    +
     6115.      1// ", "stmt_switch", "unexpected_a", "switch", 28]
    +
     6116.      1
    +
     6117.      1            warn("unexpected_a", the_switch);
    +
     6118.      1        }
    +
     6119.    193        functionage.switch += 1;
    +
     6120.    193        advance("(");
    +
     6121.    193        token_now.free = true;
    +
     6122.    193        the_switch.expression = parse_expression(0);
    +
     6123.    193        the_switch.block = the_cases;
    +
     6124.    193        advance(")");
    +
     6125.    193        advance("{");
    +
     6126.   1011        while (true) {
    +
     6127.   1011
    +
     6128.   1011// Loop through cases with breaks.
    +
     6129.   1011
    +
     6130.   1011            the_case = token_nxt;
    +
     6131.   1011            the_case.arity = "statement";
    +
     6132.   1011            the_case.expression = [];
    +
     6133.   1513            while (true) {
    +
     6134.   1513
    +
     6135.   1513// Loop through fallthrough cases.
    +
     6136.   1513
    +
     6137.   1513                advance("case");
    +
     6138.   1513                token_now.switch = true;
    +
     6139.   1513                exp = parse_expression(0);
    +
     6140.   1513                if (dups.some(is_dup)) {
    +
     6141.   1513
    +
     6142.   1513// test_cause:
    +
     6143.   1513// ["
    +
     6144.   1513// switch(0){case 0:break;case 0:break}
    +
     6145.   1513// ", "stmt_switch", "unexpected_a", "0", 29]
    +
     6146.   1513
    +
     6147.   1513                    warn("unexpected_a", exp);
    +
     6148.   1513                }
    +
     6149.   1513                dups.push(exp);
    +
     6150.   1513                the_case.expression.push(exp);
    +
     6151.   1513                advance(":");
    +
     6152.   1513                if (token_nxt.id !== "case") {
    +
     6153.   1513                    break;
    +
     6154.   1513                }
    +
     6155.   1513            }
    +
     6156.   1011
    +
     6157.   1011// test_cause:
    +
     6158.   1011// ["
    +
     6159.   1011// switch(0){case 1:case 0:break;}
    +
     6160.   1011// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 23]
    +
     6161.   1011// ["
    +
     6162.   1011// switch(0){case "aa":case 0:break;}
    +
     6163.   1011// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 26]
    +
     6164.   1011// ["
    +
     6165.   1011// switch(0){case "bb":case "aa":break;}
    +
     6166.   1011// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 26]
    +
     6167.   1011// ["
    +
     6168.   1011// switch(0){case aa:case "aa":break;}
    +
     6169.   1011// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24]
    +
     6170.   1011// ["
    +
     6171.   1011// switch(0){case bb:case aa:break;}
    +
     6172.   1011// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24]
    +
     6173.   1011
    +
     6174.   1011            check_ordered_case(the_case.expression);
    +
     6175.   1011            stmts = parse_statements();
    +
     6176.   1011            if (stmts.length < 1) {
    +
     6177.   1011
    +
     6178.   1011// test_cause:
    +
     6179.   1011// ["
    +
     6180.   1011// switch(0){case 0:default:}
    +
     6181.   1011// ", "stmt_switch", "expected_statements_a", "default", 18]
    +
     6182.   1011// ["switch(0){case 0:}", "stmt_switch", "expected_statements_a", "}", 18]
    +
     6183.   1011
    +
     6184.   1011                warn("expected_statements_a");
    +
     6185.   1011
    +
     6186.   1011// PR-359 - Bugfix - Fixes issue #358 - switch-statement crashes jslint.
    +
     6187.   1011
    +
     6188.   1011                break;
    +
     6189.   1011            }
    +
     6190.   1011            the_case.block = stmts;
    +
     6191.   1011            the_cases.push(the_case);
    +
     6192.   1011            last = stmts[stmts.length - 1];
    +
     6193.   1011            if (last.disrupt) {
    +
     6194.   1011                if (last.id === "break" && last.label === undefined) {
    +
     6195.   1011                    the_disrupt = false;
    +
     6196.   1011                }
    +
     6197.   1011            } else {
    +
     6198.   1011                warn("expected_a_before_b", token_nxt, "break;", artifact());
    +
     6199.   1011            }
    +
     6200.   1011            if (token_nxt.id !== "case") {
    +
     6201.   1011                break;
    +
     6202.   1011            }
    +
     6203.   1011        }
    +
     6204.    190
    +
     6205.    190// test_cause:
    +
     6206.    190// ["
    +
     6207.    190// switch(0){case 1:break;case 0:break;}
    +
     6208.    190// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 29]
    +
     6209.    190// ["
    +
     6210.    190// switch(0){case "aa":break;case 0:break;}
    +
     6211.    190// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 32]
    +
     6212.    190// ["
    +
     6213.    190// switch(0){case "bb":break;case "aa":break;}
    +
     6214.    190// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 32]
    +
     6215.    190// ["
    +
     6216.    190// switch(0){case aa:break;case "aa":break;}
    +
     6217.    190// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30]
    +
     6218.    190// ["
    +
     6219.    190// switch(0){case bb:break;case aa:break;}
    +
     6220.    190// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30]
    +
     6221.    190
    +
     6222.   1006        check_ordered_case(the_cases.map(function ({
    +
     6223.   1006            expression
    +
     6224.   1006        }) {
    +
     6225.   1006            return expression[0];
    +
     6226.   1006        }));
    +
     6227.    190        dups = undefined;
    +
     6228.    190        if (token_nxt.id === "default") {
    +
     6229.     87            the_default = token_nxt;
    +
     6230.     87            advance("default");
    +
     6231.     87            token_now.switch = true;
    +
     6232.     87            advance(":");
    +
     6233.     87            the_switch.else = parse_statements();
    +
     6234.     87            if (the_switch.else.length < 1) {
    +
     6235.     87
    +
     6236.     87// test_cause:
    +
     6237.     87// ["
    +
     6238.     87// switch(0){case 0:break;default:}
    +
     6239.     87// ", "stmt_switch", "unexpected_a", "default", 24]
    +
     6240.     87
    +
     6241.     87                warn("unexpected_a", the_default);
    +
     6242.     87                the_disrupt = false;
    +
     6243.     87            } else {
    +
     6244.     87                the_last = the_switch.else[
    +
     6245.     87                    the_switch.else.length - 1
    +
     6246.     87                ];
    +
     6247.     87                if (
    +
     6248.     87                    the_last.id === "break"
    +
     6249.     87                    && the_last.label === undefined
    +
     6250.     87                ) {
    +
     6251.     87
    +
     6252.     87// test_cause:
    +
     6253.     87// ["
    +
     6254.     87// switch(0){case 0:break;default:break;}
    +
     6255.     87// ", "stmt_switch", "unexpected_a", "break", 32]
    +
     6256.     87
    +
     6257.     87                    warn("unexpected_a", the_last);
    +
     6258.     87                    the_last.disrupt = false;
    +
     6259.     87                }
    +
     6260.     87                the_disrupt = the_disrupt && the_last.disrupt;
    +
     6261.     87            }
    +
     6262.    103        } else {
    +
     6263.    103            the_disrupt = false;
    +
     6264.    190        }
    +
     6265.    190        advance("}", the_switch);
    +
     6266.    190        functionage.switch -= 1;
    +
     6267.    190        the_switch.disrupt = the_disrupt;
    +
     6268.    190        return the_switch;
    +
     6269.    190    }
    +
     6270.    597
    +
     6271.     37    function stmt_throw() {
    +
     6272.     37        const the_throw = token_now;
    +
     6273.     37        the_throw.disrupt = true;
    +
     6274.     37        the_throw.expression = parse_expression(10);
    +
     6275.     37        semicolon();
    +
     6276.      1        if (functionage.try > 0) {
    +
     6277.      1
    +
     6278.      1// test_cause:
    +
     6279.      1// ["try{throw 0}catch(){}", "stmt_throw", "unexpected_a", "throw", 5]
    +
     6280.      1
    +
     6281.      1            warn("unexpected_a", the_throw);
    +
     6282.      1        }
    +
     6283.     37        return the_throw;
    +
     6284.     37    }
    +
     6285.    597
    +
     6286.     57    function stmt_try() {
    +
     6287.     57        const the_try = token_now;
    +
     6288.     57        let ignored;
    +
     6289.     57        let the_catch;
    +
     6290.     57        let the_disrupt;
    +
     6291.      1        if (functionage.try > 0) {
    +
     6292.      1
    +
     6293.      1// test_cause:
    +
     6294.      1// ["try{try{}catch(){}}catch(){}", "stmt_try", "unexpected_a", "try", 5]
    +
     6295.      1
    +
     6296.      1            warn("unexpected_a", the_try);
    +
     6297.      1        }
    +
     6298.     57        functionage.try += 1;
    +
     6299.     57        the_try.block = block();
    +
     6300.     57        the_disrupt = the_try.block.disrupt;
    +
     6301.     52        if (token_nxt.id === "catch") {
    +
     6302.     52            ignored = "ignore";
    +
     6303.     52            the_catch = token_nxt;
    +
     6304.     52            the_try.catch = the_catch;
    +
     6305.     52            advance("catch");
    +
     6306.     52
    +
     6307.     52// Create new catch-scope for catch-parameter.
    +
     6308.     52
    +
     6309.     52            catch_stack.push(catchage);
    +
     6310.     52            catchage = the_catch;
    +
     6311.     52            catch_list.push(catchage);
    +
     6312.     52            the_catch.context = empty();
    +
     6313.     52            if (token_nxt.id === "(") {
    +
     6314.     52                advance("(");
    +
     6315.     52                if (!token_nxt.identifier) {
    +
     6316.     52
    +
     6317.     52// test_cause:
    +
     6318.     52// ["try{}catch(){}", "stmt_try", "expected_identifier_a", ")", 12]
    +
     6319.     52
    +
     6320.     52                    return stop("expected_identifier_a");
    +
     6321.     52                }
    +
     6322.     52                if (token_nxt.id !== "ignore") {
    +
     6323.     52                    ignored = undefined;
    +
     6324.     52                    the_catch.name = token_nxt;
    +
     6325.     52                    enroll(token_nxt, "exception", true);
    +
     6326.     52                }
    +
     6327.     52                advance();
    +
     6328.     52                advance(")");
    +
     6329.     52            }
    +
     6330.     52            the_catch.block = block(ignored);
    +
     6331.     52            if (the_catch.block.disrupt !== true) {
    +
     6332.     52                the_disrupt = false;
    +
     6333.     52            }
    +
     6334.     52
    +
     6335.     52// Restore previous catch-scope after catch-block.
    +
     6336.     52
    +
     6337.     52            catchage = catch_stack.pop();
    +
     6338.     52        } else {
    +
     6339.      4
    +
     6340.      4// test_cause:
    +
     6341.      4// ["try{}finally{break;}", "stmt_try", "expected_a_before_b", "finally", 6]
    +
     6342.      4
    +
     6343.      4            warn("expected_a_before_b", token_nxt, "catch", artifact());
    +
     6344.     53        }
    +
     6345.     53        if (token_nxt.id === "finally") {
    +
     6346.      4            functionage.finally += 1;
    +
     6347.      4            advance("finally");
    +
     6348.      4            the_try.else = block();
    +
     6349.      4            the_disrupt = the_try.else.disrupt;
    +
     6350.      4            functionage.finally -= 1;
    +
     6351.     51        }
    +
     6352.     51        the_try.disrupt = the_disrupt;
    +
     6353.     51        functionage.try -= 1;
    +
     6354.     51        return the_try;
    +
     6355.     51    }
    +
     6356.    597
    +
     6357.   2166    function stmt_var() {
    +
     6358.   2166        let ellipsis;
    +
     6359.   2166        let mode_const;
    +
     6360.   2166        let name;
    +
     6361.   2166        let the_brace;
    +
     6362.   2166        let the_bracket;
    +
     6363.   2166        let the_variable = token_now;
    +
     6364.   2166        let variable_prv;
    +
     6365.   2166        mode_const = the_variable.id === "const";
    +
     6366.   2166        the_variable.names = [];
    +
     6367.   2166
    +
     6368.   2166// A program may use var or let, but not both.
    +
     6369.   2166
    +
     6370.   1865        if (!mode_const) {
    +
     6371.   1865            if (mode_var === undefined) {
    +
     6372.   1865                mode_var = the_variable.id;
    +
     6373.   1865            } else if (the_variable.id !== mode_var) {
    +
     6374.   1865
    +
     6375.   1865// test_cause:
    +
     6376.   1865// ["let aa;var aa", "stmt_var", "expected_a_b", "var", 8]
    +
     6377.   1865
    +
     6378.   1865                warn("expected_a_b", the_variable, mode_var, the_variable.id);
    +
     6379.   1865            }
    +
     6380.   1865        }
    +
     6381.   2166
    +
     6382.   2166// We don't expect to see variables created in switch statements.
    +
     6383.   2166
    +
     6384.      1        if (functionage.switch > 0) {
    +
     6385.      1
    +
     6386.      1// test_cause:
    +
     6387.      1// ["switch(0){case 0:var aa}", "stmt_var", "var_switch", "var", 18]
    +
     6388.      1
    +
     6389.      1            warn("var_switch", the_variable);
    +
     6390.      1        }
    +
     6391.   2166        switch (
    +
     6392.   2166            Boolean(functionage.statement_prv)
    +
     6393.   1304            && functionage.statement_prv.id
    +
     6394.   2166        ) {
    +
     6395.    132        case "const":
    +
     6396.   1291        case "let":
    +
     6397.   1293        case "var":
    +
     6398.   1293
    +
     6399.   1293// test_cause:
    +
     6400.   1293// ["const aa=0;const bb=0;", "stmt_var", "var_prv", "const", 0]
    +
     6401.   1293// ["let aa=0;let bb=0;", "stmt_var", "var_prv", "let", 0]
    +
     6402.   1293// ["var aa=0;var bb=0;", "stmt_var", "var_prv", "var", 0]
    +
     6403.   1293
    +
     6404.   1293            test_cause("var_prv", functionage.statement_prv.id);
    +
     6405.   1293            variable_prv = functionage.statement_prv;
    +
     6406.   1293            break;
    +
     6407.      4        case "import":
    +
     6408.      4
    +
     6409.      4// test_cause:
    +
     6410.      4// ["import aa from \"aa\";\nlet bb=0;", "stmt_var", "import_prv", "", 0]
    +
     6411.      4
    +
     6412.      4            test_cause("import_prv");
    +
     6413.      4            break;
    +
     6414.    862        case false:
    +
     6415.    862            break;
    +
     6416.      7        default:
    +
     6417.      7            if (
    +
     6418.      7                (option_dict.beta && !option_dict.variable)
    +
     6419.      7                || the_variable.id === "var"
    +
     6420.      7            ) {
    +
     6421.      7
    +
     6422.      7// test_cause:
    +
     6423.      7// ["console.log();let aa=0;", "stmt_var", "var_on_top", "let", 15]
    +
     6424.      7// ["console.log();var aa=0;", "stmt_var", "var_on_top", "var", 15]
    +
     6425.      7// ["try{aa();}catch(aa){var aa=0;}", "stmt_var", "var_on_top", "var", 21]
    +
     6426.      7// ["while(0){var aa;}", "stmt_var", "var_on_top", "var", 10]
    +
     6427.      7
    +
     6428.      7                warn("var_on_top", token_now);
    +
     6429.      7            }
    +
     6430.   2166        }
    +
     6431.   2167        while (true) {
    +
     6432.   2167            if (token_nxt.id === "{") {
    +
     6433.   2167                if (the_variable.id === "var") {
    +
     6434.   2167
    +
     6435.   2167// test_cause:
    +
     6436.   2167// ["var{aa}=0", "stmt_var", "unexpected_a", "var", 1]
    +
     6437.   2167
    +
     6438.   2167                    warn("unexpected_a", the_variable);
    +
     6439.   2167                }
    +
     6440.   2167                the_brace = token_nxt;
    +
     6441.   2167                advance("{");
    +
     6442.   2167                while (true) {
    +
     6443.   2167                    name = token_nxt;
    +
     6444.   2167                    if (!name.identifier) {
    +
     6445.   2167
    +
     6446.   2167// test_cause:
    +
     6447.   2167// ["let {0}", "stmt_var", "expected_identifier_a", "0", 6]
    +
     6448.   2167
    +
     6449.   2167                        return stop("expected_identifier_a");
    +
     6450.   2167                    }
    +
     6451.   2167                    survey(name);
    +
     6452.   2167                    advance();
    +
     6453.   2167                    if (token_nxt.id === ":") {
    +
     6454.   2167                        advance(":");
    +
     6455.   2167                        if (!token_nxt.identifier) {
    +
     6456.   2167
    +
     6457.   2167// test_cause:
    +
     6458.   2167// ["let {aa:0}", "stmt_var", "expected_identifier_a", "0", 9]
    +
     6459.   2167// ["let {aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 9]
    +
     6460.   2167
    +
     6461.   2167                            return stop("expected_identifier_a");
    +
     6462.   2167                        }
    +
     6463.   2167
    +
     6464.   2167// PR-363 - Bugfix - fix false-warning
    +
     6465.   2167// <uninitialized 'bb'> in code '/*jslint node*/\nlet {aa:bb} = {}; bb();'
    +
     6466.   2167
    +
     6467.   2167                        // token_nxt.label = name;
    +
     6468.   2167                        // the_variable.names.push(token_nxt);
    +
     6469.   2167                        // enroll(token_nxt, "variable", mode_const);
    +
     6470.   2167                        name = token_nxt;
    +
     6471.   2167                        the_variable.names.push(name);
    +
     6472.   2167                        survey(name);
    +
     6473.   2167                        enroll(name, "variable", mode_const);
    +
     6474.   2167
    +
     6475.   2167                        advance();
    +
     6476.   2167                        the_brace.open = true;
    +
     6477.   2167                    } else {
    +
     6478.   2167                        the_variable.names.push(name);
    +
     6479.   2167                        enroll(name, "variable", mode_const);
    +
     6480.   2167                    }
    +
     6481.   2167                    name.dead = false;
    +
     6482.   2167                    name.init = true;
    +
     6483.   2167                    if (token_nxt.id === "=") {
    +
     6484.   2167
    +
     6485.   2167// test_cause:
    +
     6486.   2167// ["let {aa=0}", "stmt_var", "assign", "", 0]
    +
     6487.   2167
    +
     6488.   2167                        test_cause("assign");
    +
     6489.   2167                        advance("=");
    +
     6490.   2167                        name.expression = parse_expression();
    +
     6491.   2167                        the_brace.open = true;
    +
     6492.   2167                    }
    +
     6493.   2167                    if (token_nxt.id !== ",") {
    +
     6494.   2167                        break;
    +
     6495.   2167                    }
    +
     6496.   2167                    advance(",");
    +
     6497.   2167                }
    +
     6498.   2167
    +
     6499.   2167// test_cause:
    +
     6500.   2167// ["let{bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8]
    +
     6501.   2167
    +
     6502.   2167                check_ordered(the_variable.id, the_variable.names);
    +
     6503.   2167                advance("}");
    +
     6504.   2167                advance("=");
    +
     6505.   2167                the_variable.expression = parse_expression(0);
    +
     6506.   2167            } else if (token_nxt.id === "[") {
    +
     6507.   2167                if (the_variable.id === "var") {
    +
     6508.   2167
    +
     6509.   2167// test_cause:
    +
     6510.   2167// ["var[aa]=0", "stmt_var", "unexpected_a", "var", 1]
    +
     6511.   2167
    +
     6512.   2167                    warn("unexpected_a", the_variable);
    +
     6513.   2167                }
    +
     6514.   2167                the_bracket = token_nxt;
    +
     6515.   2167                advance("[");
    +
     6516.   2167                while (true) {
    +
     6517.   2167                    ellipsis = false;
    +
     6518.   2167                    if (token_nxt.id === "...") {
    +
     6519.   2167                        ellipsis = true;
    +
     6520.   2167                        advance("...");
    +
     6521.   2167                    }
    +
     6522.   2167                    if (!token_nxt.identifier) {
    +
     6523.   2167
    +
     6524.   2167// test_cause:
    +
     6525.   2167// ["let[]", "stmt_var", "expected_identifier_a", "]", 5]
    +
     6526.   2167
    +
     6527.   2167                        return stop("expected_identifier_a");
    +
     6528.   2167                    }
    +
     6529.   2167                    name = token_nxt;
    +
     6530.   2167                    advance();
    +
     6531.   2167                    the_variable.names.push(name);
    +
     6532.   2167                    enroll(name, "variable", mode_const);
    +
     6533.   2167                    name.dead = false;
    +
     6534.   2167                    name.init = true;
    +
     6535.   2167                    if (ellipsis) {
    +
     6536.   2167                        name.ellipsis = true;
    +
     6537.   2167                        break;
    +
     6538.   2167                    }
    +
     6539.   2167                    if (token_nxt.id === "=") {
    +
     6540.   2167                        advance("=");
    +
     6541.   2167                        name.expression = parse_expression();
    +
     6542.   2167                        the_bracket.open = true;
    +
     6543.   2167                    }
    +
     6544.   2167                    if (token_nxt.id !== ",") {
    +
     6545.   2167                        break;
    +
     6546.   2167                    }
    +
     6547.   2167                    advance(",");
    +
     6548.   2167                }
    +
     6549.   2167                advance("]");
    +
     6550.   2167                advance("=");
    +
     6551.   2167                the_variable.expression = parse_expression(0);
    +
     6552.   2167            } else if (token_nxt.identifier) {
    +
     6553.   2167                name = token_nxt;
    +
     6554.   2167                advance();
    +
     6555.   2167                if (name.id === "ignore") {
    +
     6556.   2167
    +
     6557.   2167// test_cause:
    +
     6558.   2167// ["
    +
     6559.   2167// let ignore;function aa(ignore) {}
    +
     6560.   2167// ", "stmt_var", "unexpected_a", "ignore", 5]
    +
     6561.   2167
    +
     6562.   2167                    warn("unexpected_a", name);
    +
     6563.   2167                }
    +
     6564.   2167                enroll(name, "variable", mode_const);
    +
     6565.   2167                if (token_nxt.id === "=" || mode_const) {
    +
     6566.   2167                    advance("=");
    +
     6567.   2167                    name.dead = false;
    +
     6568.   2167                    name.init = true;
    +
     6569.   2167                    name.expression = parse_expression(0);
    +
     6570.   2167                }
    +
     6571.   2167                the_variable.names.push(name);
    +
     6572.   2167            } else {
    +
     6573.   2167
    +
     6574.   2167// test_cause:
    +
     6575.   2167// ["let 0", "stmt_var", "expected_identifier_a", "0", 5]
    +
     6576.   2167// ["var{aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 8]
    +
     6577.   2167
    +
     6578.   2167                return stop("expected_identifier_a");
    +
     6579.   2167            }
    +
     6580.   2167            if (token_nxt.id !== ",") {
    +
     6581.   2167                break;
    +
     6582.   2167            }
    +
     6583.   2167
    +
     6584.   2167// test_cause:
    +
     6585.   2167// ["let aa,bb;", "stmt_var", "expected_a_b", ",", 7]
    +
     6586.   2167
    +
     6587.   2167            warn("expected_a_b", token_nxt, ";", ",");
    +
     6588.   2167            advance(",");
    +
     6589.   2167        }
    +
     6590.   2152
    +
     6591.   2152// Warn if variable declarations are unordered.
    +
     6592.   2152
    +
     6593.   2152        if (
    +
     6594.   2152            option_dict.beta
    +
     6595.   2152            && !option_dict.unordered
    +
     6596.   2150            && !option_dict.variable
    +
     6597.   2144            && variable_prv
    +
     6598.   1291            && (
    +
     6599.   1291                variable_prv.id + " " + variable_prv.names[0].id
    +
     6600.   1291                > the_variable.id + " " + the_variable.names[0].id
    +
     6601.   1291            )
    +
     6602.      3        ) {
    +
     6603.      3
    +
     6604.      3// test_cause:
    +
     6605.      3// ["const bb=0;const aa=0;", "stmt_var", "expected_a_b_before_c_d", "aa", 12]
    +
     6606.      3// ["let bb;let aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8]
    +
     6607.      3// ["var bb;var aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8]
    +
     6608.      3
    +
     6609.      3            warn(
    +
     6610.      3                "expected_a_b_before_c_d",
    +
     6611.      3                the_variable,
    +
     6612.      3                the_variable.id,
    +
     6613.      3                the_variable.names[0].id,
    +
     6614.      3                variable_prv.id,
    +
     6615.      3                variable_prv.names[0].id
    +
     6616.      3            );
    +
     6617.   2152        }
    +
     6618.   2152        semicolon();
    +
     6619.   2152        return the_variable;
    +
     6620.   2152    }
    +
     6621.    597
    +
     6622.    214    function stmt_while() {
    +
     6623.    214        const the_while = token_now;
    +
     6624.    214        check_not_top_level(the_while);
    +
     6625.    214        functionage.loop += 1;
    +
     6626.    214        the_while.expression = condition();
    +
     6627.    214        the_while.block = block();
    +
     6628.      1        if (the_while.block.disrupt === true) {
    +
     6629.      1
    +
     6630.      1// test_cause:
    +
     6631.      1// ["function aa(){while(0){break;}}", "stmt_while", "weird_loop", "while", 15]
    +
     6632.      1
    +
     6633.      1            warn("weird_loop", the_while);
    +
     6634.    210        }
    +
     6635.    210        functionage.loop -= 1;
    +
     6636.    210        return the_while;
    +
     6637.    210    }
    +
     6638.    597
    +
     6639.      1    function stmt_with() {
    +
     6640.      1
    +
     6641.      1// test_cause:
    +
     6642.      1// ["with", "stmt_with", "unexpected_a", "with", 1]
    +
     6643.      1
    +
     6644.      1        stop("unexpected_a", token_now);
    +
     6645.      1    }
    +
     6646.    597
    +
     6647.  13725    function survey(name) {
    +
     6648.  13725        let id = name.id;
    +
     6649.  13725
    +
     6650.  13725// Tally the property name. If it is a string, only tally strings that conform
    +
     6651.  13725// to the identifier rules.
    +
     6652.  13725
    +
     6653.     69        if (id === "(string)") {
    +
     6654.     69            id = name.value;
    +
     6655.     69            if (!rx_identifier.test(id)) {
    +
     6656.     69                return id;
    +
     6657.     69            }
    +
     6658.  13656        } else if (id === "`") {
    +
     6659.  13656            if (name.value.length === 1) {
    +
     6660.  13656                id = name.value[0].value;
    +
     6661.  13656                if (!rx_identifier.test(id)) {
    +
     6662.  13656                    return id;
    +
     6663.  13656                }
    +
     6664.  13656            }
    +
     6665.  13656        } else if (!name.identifier) {
    +
     6666.  13656
    +
     6667.  13656// test_cause:
    +
     6668.  13656// ["let aa={0:0}", "survey", "expected_identifier_a", "0", 9]
    +
     6669.  13656
    +
     6670.  13656            return stop("expected_identifier_a", name);
    +
     6671.  13679        }
    +
     6672.  13679
    +
     6673.  13679// If we have seen this name before, increment its count.
    +
     6674.  13679
    +
     6675.  13679        if (typeof property_dict[id] === "number") {
    +
     6676.  11516            property_dict[id] += 1;
    +
     6677.  11516
    +
     6678.  11516// If this is the first time seeing this property name, and if there is a
    +
     6679.  11516// tenure list, then it must be on the list. Otherwise, it must conform to
    +
     6680.  11516// the rules for good property names.
    +
     6681.  11516
    +
     6682.  11516        } else {
    +
     6683.   2163            if (state.mode_property) {
    +
     6684.   2163                if (tenure[id] !== true) {
    +
     6685.   2163
    +
     6686.   2163// test_cause:
    +
     6687.   2163// ["/*property aa*/\naa.bb", "survey", "unregistered_property_a", "bb", 4]
    +
     6688.   2163
    +
     6689.   2163                    warn("unregistered_property_a", name);
    +
     6690.   2163                }
    +
     6691.   2163            } else if (
    +
     6692.   2163                !option_dict.name
    +
     6693.   2163                && name.identifier
    +
     6694.   2163                && (
    +
     6695.   2163                    // rx_weird_property
    +
     6696.   2163                    /^_|\$|Sync$|_$/m
    +
     6697.   2163                ).test(id)
    +
     6698.   2163            ) {
    +
     6699.   2163
    +
     6700.   2163// test_cause:
    +
     6701.   2163// ["aa.$", "survey", "weird_property_a", "$", 4]
    +
     6702.   2163// ["aa._", "survey", "weird_property_a", "_", 4]
    +
     6703.   2163// ["aa._aa", "survey", "weird_property_a", "_aa", 4]
    +
     6704.   2163// ["aa.aaSync", "survey", "weird_property_a", "aaSync", 4]
    +
     6705.   2163// ["aa.aa_", "survey", "weird_property_a", "aa_", 4]
    +
     6706.   2163
    +
     6707.   2163                warn("weird_property_a", name);
    +
     6708.   2163            }
    +
     6709.   2163            property_dict[id] = 1;
    +
     6710.  13679        }
    +
     6711.  13679        return id;
    +
     6712.  13679    }
    +
     6713.    597
    +
     6714.    597// These functions are used to specify the grammar of our language:
    +
     6715.    597
    +
     6716.  77610    function symbol(id, bp) {
    +
     6717.  77610
    +
     6718.  77610// Create a symbol if it does not already exist in the language's syntax.
    +
     6719.  77610
    +
     6720.  77610        let the_symbol = syntax_dict[id];
    +
     6721.  67461        if (the_symbol === undefined) {
    +
     6722.  67461            the_symbol = empty();
    +
     6723.  67461            the_symbol.id = id;
    +
     6724.  67461            the_symbol.lbp = bp || 0;
    +
     6725.  67461            syntax_dict[id] = the_symbol;
    +
     6726.  67461        }
    +
     6727.  77610        return the_symbol;
    +
     6728.  77610    }
    +
     6729.    597
    +
     6730.    597    function ternary(id1, id2) {
    +
     6731.    597
    +
     6732.    597// Create a ternary operator.
    +
     6733.    597
    +
     6734.    597        const the_symbol = symbol(id1, 30);
    +
     6735.    207        the_symbol.led = function parse_ternary_led(left) {
    +
     6736.    207            const the_token = token_now;
    +
     6737.    207            let second;
    +
     6738.    207            second = parse_expression(20);
    +
     6739.    207            advance(id2);
    +
     6740.    207            token_now.arity = "ternary";
    +
     6741.    207            the_token.arity = "ternary";
    +
     6742.    207            the_token.expression = [left, second, parse_expression(10)];
    +
     6743.      5            if (token_nxt.id !== ")") {
    +
     6744.      5
    +
     6745.      5// test_cause:
    +
     6746.      5// ["0?0:0", "parse_ternary_led", "use_open", "?", 2]
    +
     6747.      5
    +
     6748.      5                warn("use_open", the_token);
    +
     6749.      5            }
    +
     6750.    207            return the_token;
    +
     6751.    207        };
    +
     6752.    597        return the_symbol;
    +
     6753.    597    }
    +
     6754.    597
    +
     6755.    597// Now we parse JavaScript.
    +
     6756.    597// Begin defining the language.
    +
     6757.    597
    +
     6758.    597    assignment("%=");
    +
     6759.    597    assignment("&=");
    +
     6760.    597    assignment("*=");
    +
     6761.    597    assignment("+=");
    +
     6762.    597    assignment("-=");
    +
     6763.    597    assignment("/=");
    +
     6764.    597    assignment("<<=");
    +
     6765.    597    assignment("=");
    +
     6766.    597    assignment(">>=");
    +
     6767.    597    assignment(">>>=");
    +
     6768.    597    assignment("^=");
    +
     6769.    597    assignment("|=");
    +
     6770.    597    constant("(number)", "number");
    +
     6771.    597    constant("(regexp)", "regexp");
    +
     6772.    597    constant("(string)", "string");
    +
     6773.    597    constant("Function", "function", constant_Function);
    +
     6774.    597    constant("Infinity", "number", Infinity);
    +
     6775.    597    constant("NaN", "number", NaN);
    +
     6776.    597    constant("arguments", "object", constant_arguments);
    +
     6777.    597    constant("eval", "function", constant_eval);
    +
     6778.    597    constant("false", "boolean", false);
    +
     6779.    597    constant("ignore", "undefined", constant_ignore);
    +
     6780.    597    constant("isFinite", "function", constant_isInfinite);
    +
     6781.    597    constant("isNaN", "function", constant_isNaN);
    +
     6782.    597    constant("null", "null", null);
    +
     6783.    597    constant("this", "object", constant_this);
    +
     6784.    597    constant("true", "boolean", true);
    +
     6785.    597    constant("undefined", "undefined");
    +
     6786.    597    infix(100, "!=");
    +
     6787.    597    infix(100, "!==");
    +
     6788.    597    infix(100, "==");
    +
     6789.    597    infix(100, "===");
    +
     6790.    597    infix(110, "<");
    +
     6791.    597    infix(110, "<=");
    +
     6792.    597    infix(110, ">");
    +
     6793.    597    infix(110, ">=");
    +
     6794.    597    infix(110, "in");
    +
     6795.    597    infix(110, "instanceof");
    +
     6796.    597    infix(120, "<<");
    +
     6797.    597    infix(120, ">>");
    +
     6798.    597    infix(120, ">>>");
    +
     6799.    597    infix(130, "+");
    +
     6800.    597    infix(130, "-");
    +
     6801.    597    infix(140, "%");
    +
     6802.    597    infix(140, "*");
    +
     6803.    597    infix(140, "/");
    +
     6804.    597    infix(160, "(", infix_lparen);
    +
     6805.    597    infix(160, "`", infix_grave);
    +
     6806.    597    infix(170, ".", infix_dot);
    +
     6807.    597    infix(170, "=>", infix_fart_unwrapped);
    +
     6808.    597    infix(170, "?.", infix_option_chain);
    +
     6809.    597    infix(170, "[", infix_lbracket);
    +
     6810.    597    infix(35, "??");
    +
     6811.    597    infix(40, "||");
    +
     6812.    597    infix(50, "&&");
    +
     6813.    597    infix(70, "|");
    +
     6814.    597    infix(80, "^");
    +
     6815.    597    infix(90, "&");
    +
     6816.    597    infixr(150, "**");
    +
     6817.    597    postassign("++");
    +
     6818.    597    postassign("--");
    +
     6819.    597    preassign("++");
    +
     6820.    597    preassign("--");
    +
     6821.    597    prefix("!!");
    +
     6822.    597    prefix("!");
    +
     6823.    597    prefix("(", prefix_lparen);
    +
     6824.    597    prefix("+");
    +
     6825.    597    prefix("-");
    +
     6826.    597    prefix("/=", prefix_assign_divide);
    +
     6827.    597    prefix("=>", prefix_fart);
    +
     6828.    597    prefix("[", prefix_lbracket);
    +
     6829.    597    prefix("`", prefix_tick);
    +
     6830.    597    prefix("async", prefix_async);
    +
     6831.    597    prefix("await", prefix_await);
    +
     6832.    597    prefix("function", prefix_function);
    +
     6833.    597    prefix("new", prefix_new);
    +
     6834.    597    prefix("typeof");
    +
     6835.    597    prefix("void", prefix_void);
    +
     6836.    597    prefix("{", prefix_lbrace);
    +
     6837.    597    prefix("~");
    +
     6838.    597    stmt(";", stmt_semicolon);
    +
     6839.    597    stmt("async", prefix_async);
    +
     6840.    597    stmt("await", prefix_await);
    +
     6841.    597    stmt("break", stmt_break);
    +
     6842.    597    stmt("const", stmt_var);
    +
     6843.    597    stmt("continue", stmt_continue);
    +
     6844.    597    stmt("debugger", stmt_debugger);
    +
     6845.    597    stmt("delete", stmt_delete);
    +
     6846.    597    stmt("do", stmt_do);
    +
     6847.    597    stmt("export", stmt_export);
    +
     6848.    597    stmt("for", stmt_for);
    +
     6849.    597    stmt("function", prefix_function);
    +
     6850.    597    stmt("if", stmt_if);
    +
     6851.    597    stmt("import", stmt_import);
    +
     6852.    597    stmt("let", stmt_var);
    +
     6853.    597    stmt("return", stmt_return);
    +
     6854.    597    stmt("switch", stmt_switch);
    +
     6855.    597    stmt("throw", stmt_throw);
    +
     6856.    597    stmt("try", stmt_try);
    +
     6857.    597    stmt("var", stmt_var);
    +
     6858.    597    stmt("while", stmt_while);
    +
     6859.    597    stmt("with", stmt_with);
    +
     6860.    597    stmt("{", stmt_lbrace);
    +
     6861.    597    symbol(")");
    +
     6862.    597    symbol("*/");
    +
     6863.    597    symbol(",");
    +
     6864.    597    symbol(":");
    +
     6865.    597    symbol(";");
    +
     6866.    597    symbol("]");
    +
     6867.    597    symbol("async");
    +
     6868.    597    symbol("await");
    +
     6869.    597    symbol("case");
    +
     6870.    597    symbol("catch");
    +
     6871.    597    symbol("class");
    +
     6872.    597    symbol("default");
    +
     6873.    597    symbol("else");
    +
     6874.    597    symbol("enum");
    +
     6875.    597    symbol("finally");
    +
     6876.    597    symbol("implements");
    +
     6877.    597    symbol("interface");
    +
     6878.    597    symbol("package");
    +
     6879.    597    symbol("private");
    +
     6880.    597    symbol("protected");
    +
     6881.    597    symbol("public");
    +
     6882.    597    symbol("static");
    +
     6883.    597    symbol("super");
    +
     6884.    597    symbol("void");
    +
     6885.    597    symbol("yield");
    +
     6886.    597    symbol("}");
    +
     6887.    597    ternary("?", ":");
    +
     6888.    597
    +
     6889.    597// Init token_nxt.
    +
     6890.    597
    +
     6891.    597    advance();
    +
     6892.    597
    +
     6893.    597// Parsing of JSON is simple:
    +
     6894.    597
    +
     6895.     24    if (state.mode_json) {
    +
     6896.     24        state.token_tree = parse_json();
    +
     6897.     24        advance("(end)");
    +
     6898.     24        return;
    +
     6899.    573    }
    +
     6900.    573
    +
     6901.    573// Because browsers encourage combining of script files, the first token might
    +
     6902.    573// be a semicolon to defend against a missing semicolon in the preceding file.
    +
     6903.    573
    +
     6904.    573    if (option_dict.browser) {
    +
     6905.      3        if (token_nxt.id === ";") {
    +
     6906.      3            advance(";");
    +
     6907.      3        }
    +
     6908.      3
    +
     6909.      3// If we are not in a browser, then the file form of strict pragma may be used.
    +
     6910.      3
    +
     6911.    570    } else if (token_nxt.value === "use strict") {
    +
     6912.    570        advance("(string)");
    +
     6913.    570        advance(";");
    +
     6914.    573    }
    +
     6915.    573    state.token_tree = parse_statements();
    +
     6916.    573    advance("(end)");
    +
     6917.    573
    +
     6918.    573// Check global functions are ordered.
    +
     6919.    573
    +
     6920.    573    check_ordered(
    +
     6921.    573        "function",
    +
     6922.   1865        function_list.map(function ({
    +
     6923.   1865            level,
    +
     6924.   1865            name
    +
     6925.   1865        }) {
    +
     6926.    573            return (level === 1) && name;
    +
     6927.   1865        }).filter(function (name) {
    +
     6928.   1863            return option_dict.beta && name && name.id;
    +
     6929.   1865        })
    +
     6930.    573    );
    +
     6931.    573}
    +
     6932.      1
    +
     6933.    479function jslint_phase4_walk(state) {
    +
     6934.    479
    +
     6935.    479// PHASE 4. Walk <token_tree>, traversing all nodes of the tree. It is a
    +
     6936.    479//          recursive traversal. Each node may be processed on the way down
    +
     6937.    479//          (preaction) and on the way up (postaction).
    +
     6938.    479
    +
     6939.    479    let {
    +
     6940.    479        artifact,
    +
     6941.    479        catch_stack,
    +
     6942.    479        function_stack,
    +
     6943.    479        global_dict,
    +
     6944.    479        is_equal,
    +
     6945.    479        is_weird,
    +
     6946.    479        option_dict,
    +
     6947.    479        syntax_dict,
    +
     6948.    479        test_cause,
    +
     6949.    479        token_global,
    +
     6950.    479        warn
    +
     6951.    479    } = state;
    +
     6952.    479    let block_stack = [];               // The stack of blocks.
    +
     6953.    479    let blockage = token_global;        // The current block.
    +
     6954.    479    let catchage = catch_stack[0];      // The current catch-block.
    +
     6955.    479    let functionage = token_global;     // The current function.
    +
     6956.    479    let postaction;
    +
     6957.    479    let postamble;
    +
     6958.    479    let posts = empty();
    +
     6959.    479    let preaction;
    +
     6960.    479    let preamble;
    +
     6961.    479    let pres = empty();
    +
     6962.    479
    +
     6963.    479// The relational operators.
    +
     6964.    479
    +
     6965.    479    let relationop = object_assign_from_list(empty(), [
    +
     6966.    479        "!=", "!==", "<", "<=", "==", "===", ">", ">="
    +
     6967.    479    ], true);
    +
     6968.    479
    +
     6969.    479// Ambulation of the parse tree.
    +
     6970.    479
    +
     6971.    958    function action(when) {
    +
     6972.    958
    +
     6973.    958// Produce a function that will register task functions that will be called as
    +
     6974.    958// the tree is traversed.
    +
     6975.    958
    +
     6976.  18202        return function (arity, id, task) {
    +
     6977.  18202            let a_set = when[arity];
    +
     6978.  18202            let i_set;
    +
     6979.  18202
    +
     6980.  18202// The id parameter is optional. If excluded, the task will be applied to all
    +
     6981.  18202// ids.
    +
     6982.  18202
    +
     6983.   3832            if (typeof id !== "string") {
    +
     6984.   3832                task = id;
    +
     6985.   3832                id = "(all)";
    +
     6986.   3832            }
    +
     6987.  18202
    +
     6988.  18202// If this arity has no registrations yet, then create a set object to hold
    +
     6989.  18202// them.
    +
     6990.  18202
    +
     6991.   4790            if (a_set === undefined) {
    +
     6992.   4790                a_set = empty();
    +
     6993.   4790                when[arity] = a_set;
    +
     6994.   4790            }
    +
     6995.  18202
    +
     6996.  18202// If this id has no registrations yet, then create a set array to hold them.
    +
     6997.  18202
    +
     6998.  18202            i_set = a_set[id];
    +
     6999.  17723            if (i_set === undefined) {
    +
     7000.  17723                i_set = [];
    +
     7001.  17723                a_set[id] = i_set;
    +
     7002.  17723            }
    +
     7003.  18202
    +
     7004.  18202// Register the task with the arity and the id.
    +
     7005.  18202
    +
     7006.  18202            i_set.push(task);
    +
     7007.  18202        };
    +
     7008.    958    }
    +
     7009.    479
    +
     7010.    958    function amble(when) {
    +
     7011.    958
    +
     7012.    958// Produce a function that will act on the tasks registered by an action
    +
     7013.    958// function while walking the tree.
    +
     7014.    958
    +
     7015. 200300        return function (the_token) {
    +
     7016. 200300
    +
     7017. 200300// Given a task set that was built by an action function, run all
    +
     7018. 200300// relevant tasks on the token.
    +
     7019. 200300
    +
     7020. 200300            let a_set = when[the_token.arity];
    +
     7021. 200300            let i_set;
    +
     7022. 200300
    +
     7023. 200300// If there are tasks associated with the token's arity...
    +
     7024. 200300
    +
     7025. 137747            if (a_set !== undefined) {
    +
     7026. 137747
    +
     7027. 137747// If there are tasks associated with the token's id...
    +
     7028. 137747
    +
     7029. 137747                i_set = a_set[the_token.id];
    +
     7030. 137747                if (i_set !== undefined) {
    +
     7031. 137747                    i_set.forEach(function (task) {
    +
     7032. 137747                        return task(the_token);
    +
     7033. 137747                    });
    +
     7034. 137747                }
    +
     7035. 137747
    +
     7036. 137747// If there are tasks for all ids.
    +
     7037. 137747
    +
     7038. 137747                i_set = a_set["(all)"];
    +
     7039. 137747                if (i_set !== undefined) {
    +
     7040. 137747                    i_set.forEach(function (task) {
    +
     7041. 137747                        return task(the_token);
    +
     7042. 137747                    });
    +
     7043. 137747                }
    +
     7044. 137747            }
    +
     7045. 200300        };
    +
     7046.    958    }
    +
     7047.    479
    +
     7048.   2624    function init_variable(name) {
    +
     7049.   2624        let the_variable = lookup(name);
    +
     7050.   2569        if (!the_variable || the_variable.readonly) {
    +
     7051.     57            warn("bad_assignment_a", name);
    +
     7052.     57            return;
    +
     7053.   2567        }
    +
     7054.   2567        the_variable.init = true;
    +
     7055.   2567    }
    +
     7056.    479
    +
     7057.  29938    function lookup(thing) {
    +
     7058.  29938        let id = thing.id;
    +
     7059.  29938        let the_variable;
    +
     7060.      1        if (thing.arity !== "variable") {
    +
     7061.      1            return;
    +
     7062.  29937        }
    +
     7063.  29937
    +
     7064.  29937// Look up the variable in the current context.
    +
     7065.  29937
    +
     7066.  29937        the_variable = functionage.context[id] || catchage.context[id];
    +
     7067.  29938
    +
     7068.  29938// If it isn't local, search all the other contexts. If there are name
    +
     7069.  29938// collisions, take the most recent.
    +
     7070.  29938
    +
     7071.  23570        if (the_variable && the_variable.role === "label") {
    +
     7072.      1
    +
     7073.      1// test_cause:
    +
     7074.      1// ["aa:while(0){aa;}", "lookup", "label_a", "aa", 13]
    +
     7075.      1
    +
     7076.      1            warn("label_a", thing);
    +
     7077.      1            return the_variable;
    +
     7078.  29936        }
    +
     7079.  29936        if (!the_variable) {
    +
     7080.  13682            function_stack.forEach(function ({
    +
     7081.  13682                context
    +
     7082.  13682            }) {
    +
     7083.   6367                if (context[id] && context[id].role !== "label") {
    +
     7084.   6367                    the_variable = context[id];
    +
     7085.   6367                }
    +
     7086.  13682            });
    +
     7087.   6367
    +
     7088.   6367// If it isn't in any of those either, perhaps it is a predefined global.
    +
     7089.   6367// If so, add it to the global context.
    +
     7090.   6367
    +
     7091.   6367            if (!the_variable && global_dict[id] === undefined) {
    +
     7092.   6367
    +
     7093.   6367// test_cause:
    +
     7094.   6367// ["aa", "lookup", "undeclared_a", "aa", 1]
    +
     7095.   6367// ["class aa{}", "lookup", "undeclared_a", "aa", 7]
    +
     7096.   6367// ["
    +
     7097.   6367// let aa=0;try{aa();}catch(bb){bb();}bb();
    +
     7098.   6367// ", "lookup", "undeclared_a", "bb", 36]
    +
     7099.   6367// ["
    +
     7100.   6367// let aa=0;try{aa();}catch(ignore){bb();}
    +
     7101.   6367// ", "lookup", "undeclared_a", "bb", 34]
    +
     7102.   6367
    +
     7103.   6367                warn("undeclared_a", thing);
    +
     7104.   6367                return;
    +
     7105.   6367            }
    +
     7106.   6367            if (!the_variable) {
    +
     7107.   6367                the_variable = {
    +
     7108.   6367                    dead: false,
    +
     7109.   6367                    id,
    +
     7110.   6367                    init: true,
    +
     7111.   6367                    parent: token_global,
    +
     7112.   6367                    readonly: true,
    +
     7113.   6367                    role: "variable",
    +
     7114.   6367                    used: 0
    +
     7115.   6367                };
    +
     7116.   6367                token_global.context[id] = the_variable;
    +
     7117.   6367            }
    +
     7118.   6367            the_variable.closure = true;
    +
     7119.   6367            functionage.context[id] = the_variable;
    +
     7120.  29809        }
    +
     7121.  29809        if (
    +
     7122.  29809            (
    +
     7123.  29809                the_variable.calls === undefined
    +
     7124.  29809                || functionage.name === undefined
    +
     7125.   4259                || the_variable.calls[functionage.name.id] === undefined
    +
     7126.  29938            )
    +
     7127.  29639            && the_variable.dead
    +
     7128.      3        ) {
    +
     7129.      3
    +
     7130.      3// test_cause:
    +
     7131.      3// ["let aa;if(aa){let bb;}bb;", "lookup", "out_of_scope_a", "bb", 23]
    +
     7132.      3
    +
     7133.      3            warn("out_of_scope_a", thing);
    +
     7134.  29809        }
    +
     7135.  29809        return the_variable;
    +
     7136.  29809    }
    +
     7137.    479
    +
     7138.   4784    function post_a(thing) {
    +
     7139.   4784
    +
     7140.   4784// Assignment using = sets the init property of a variable. No other assignment
    +
     7141.   4784// operator can do this. A = token keeps that variable (or array of variables
    +
     7142.   4784// in case of destructuring) in its name property.
    +
     7143.   4784
    +
     7144.   4784        const lvalue = thing.expression[0];
    +
     7145.   4784        let right;
    +
     7146.   4064        if (thing.id === "=") {
    +
     7147.   4064            if (thing.names !== undefined) {
    +
     7148.   4064
    +
     7149.   4064// test_cause:
    +
     7150.   4064// ["if(0){aa=0}", "post_a", "=", "", 0]
    +
     7151.   4064
    +
     7152.   4064                test_cause("=");
    +
     7153.   4064
    +
     7154.   4064// Probably deadcode.
    +
     7155.   4064// if (Array.isArray(thing.names)) {
    +
     7156.   4064//     thing.names.forEach(init_variable);
    +
     7157.   4064// } else {
    +
     7158.   4064//     init_variable(thing.names);
    +
     7159.   4064// }
    +
     7160.   4064
    +
     7161.   4064                jslint_assert(
    +
     7162.   4064                    !Array.isArray(thing.names),
    +
     7163.   4064                    `Expected !Array.isArray(thing.names).`
    +
     7164.   4064                );
    +
     7165.   4064                init_variable(thing.names);
    +
     7166.   4064            } else {
    +
     7167.   4064                if (lvalue.id === "[" || lvalue.id === "{") {
    +
     7168.   4064                    lvalue.expression.forEach(function (thing) {
    +
     7169.   4064                        if (thing.variable) {
    +
     7170.   4064                            thing.variable.init = true;
    +
     7171.   4064                        }
    +
     7172.   4064                    });
    +
     7173.   4064                } else if (
    +
     7174.   4064                    lvalue.id === "."
    +
     7175.   4064                    && thing.expression[1].id === "undefined"
    +
     7176.   4064                ) {
    +
     7177.   4064
    +
     7178.   4064// test_cause:
    +
     7179.   4064// ["aa.aa=undefined", "post_a", "expected_a_b", "undefined", 1]
    +
     7180.   4064
    +
     7181.   4064                    warn(
    +
     7182.   4064                        "expected_a_b",
    +
     7183.   4064                        lvalue.expression,
    +
     7184.   4064                        "delete",
    +
     7185.   4064                        "undefined"
    +
     7186.   4064                    );
    +
     7187.   4064                }
    +
     7188.   4064            }
    +
     7189.   4064        } else {
    +
     7190.    720            if (lvalue.arity === "variable") {
    +
     7191.    720                if (!lvalue.variable || lvalue.variable.readonly) {
    +
     7192.    720                    warn("bad_assignment_a", lvalue);
    +
     7193.    720                }
    +
     7194.    720            }
    +
     7195.    720            right = syntax_dict[thing.expression[1].id];
    +
     7196.    720            if (
    +
     7197.    720                right !== undefined
    +
     7198.    720                && (
    +
     7199.    720                    right.id === "function"
    +
     7200.    720                    || right.id === "=>"
    +
     7201.    720                    || (
    +
     7202.    720                        right.constant
    +
     7203.    720                        && right.id !== "(number)"
    +
     7204.    720                        && (right.id !== "(string)" || thing.id !== "+=")
    +
     7205.    720                    )
    +
     7206.    720                )
    +
     7207.    720            ) {
    +
     7208.    720
    +
     7209.    720// test_cause:
    +
     7210.    720// ["aa+=undefined", "post_a", "unexpected_a", "undefined", 5]
    +
     7211.    720
    +
     7212.    720                warn("unexpected_a", thing.expression[1]);
    +
     7213.    720            }
    +
     7214.    720        }
    +
     7215.   4784    }
    +
     7216.    479
    +
     7217.    657    function post_a_pluseq(thing) {
    +
     7218.    657        const right = thing.expression[1];
    +
     7219.    360        if (right.constant) {
    +
     7220.    360            if (
    +
     7221.    360                right.value === ""
    +
     7222.    360                || (right.id === "(number)" && right.value === "0")
    +
     7223.    360                || right.id === "(boolean)"
    +
     7224.    360                || right.id === "null"
    +
     7225.    360                || right.id === "undefined"
    +
     7226.    360                || Number.isNaN(right.value)
    +
     7227.    360            ) {
    +
     7228.    360                warn("unexpected_a", right);
    +
     7229.    360            }
    +
     7230.    360        }
    +
     7231.    657    }
    +
     7232.    479
    +
     7233.  30451    function post_b(thing) {
    +
     7234.  30451        let right;
    +
     7235.   3955        if (relationop[thing.id]) {
    +
     7236.   3955            if (
    +
     7237.   3955                is_weird(thing.expression[0])
    +
     7238.   3955                || is_weird(thing.expression[1])
    +
     7239.   3955                || is_equal(thing.expression[0], thing.expression[1])
    +
     7240.   3955                || (
    +
     7241.   3955                    thing.expression[0].constant === true
    +
     7242.   3955                    && thing.expression[1].constant === true
    +
     7243.   3955                )
    +
     7244.   3955            ) {
    +
     7245.   3955
    +
     7246.   3955// test_cause:
    +
     7247.   3955// ["if(0===0){0}", "post_b", "weird_relation_a", "===", 5]
    +
     7248.   3955
    +
     7249.   3955                warn("weird_relation_a", thing);
    +
     7250.   3955            }
    +
     7251.   3955        }
    +
     7252.   1802        if (thing.id === "+") {
    +
     7253.   1802            if (!option_dict.convert) {
    +
     7254.   1802                if (thing.expression[0].value === "") {
    +
     7255.   1802
    +
     7256.   1802// test_cause:
    +
     7257.   1802// ["\"\"+0", "post_b", "expected_a_b", "\"\" +", 3]
    +
     7258.   1802
    +
     7259.   1802                    warn("expected_a_b", thing, "String(...)", "\"\" +");
    +
     7260.   1802                } else if (thing.expression[1].value === "") {
    +
     7261.   1802
    +
     7262.   1802// test_cause:
    +
     7263.   1802// ["0+\"\"", "post_b", "expected_a_b", "+ \"\"", 2]
    +
     7264.   1802
    +
     7265.   1802                    warn("expected_a_b", thing, "String(...)", "+ \"\"");
    +
     7266.   1802                }
    +
     7267.   1802            }
    +
     7268.  28649        } else if (thing.id === "[") {
    +
     7269.  28649            if (thing.expression[0].id === "window") {
    +
     7270.  28649
    +
     7271.  28649// test_cause:
    +
     7272.  28649// ["aa=window[0]", "post_b", "weird_expression_a", "window[...]", 10]
    +
     7273.  28649
    +
     7274.  28649                warn("weird_expression_a", thing, "window[...]");
    +
     7275.  28649            }
    +
     7276.  28649            if (thing.expression[0].id === "self") {
    +
     7277.  28649
    +
     7278.  28649// test_cause:
    +
     7279.  28649// ["aa=self[0]", "post_b", "weird_expression_a", "self[...]", 8]
    +
     7280.  28649
    +
     7281.  28649                warn("weird_expression_a", thing, "self[...]");
    +
     7282.  28649            }
    +
     7283.  28649        } else if (thing.id === "." || thing.id === "?.") {
    +
     7284.  28649            if (thing.expression.id === "RegExp") {
    +
     7285.  28649
    +
     7286.  28649// test_cause:
    +
     7287.  28649// ["aa=RegExp.aa", "post_b", "weird_expression_a", ".", 10]
    +
     7288.  28649
    +
     7289.  28649                warn("weird_expression_a", thing);
    +
     7290.  28649            }
    +
     7291.  28649        } else if (thing.id !== "=>" && thing.id !== "(") {
    +
     7292.  28649            right = thing.expression[1];
    +
     7293.  28649            if (
    +
     7294.  28649                (thing.id === "+" || thing.id === "-")
    +
     7295.  28649                && right.id === thing.id
    +
     7296.  28649                && right.arity === "unary"
    +
     7297.  28649                && !right.wrapped
    +
     7298.  28649            ) {
    +
     7299.  28649
    +
     7300.  28649// test_cause:
    +
     7301.  28649// ["0- -0", "post_b", "wrap_unary", "-", 4]
    +
     7302.  28649
    +
     7303.  28649                warn("wrap_unary", right);
    +
     7304.  28649            }
    +
     7305.  28649            if (
    +
     7306.  28649                thing.expression[0].constant === true
    +
     7307.  28649                && right.constant === true
    +
     7308.  28649            ) {
    +
     7309.  28649                thing.constant = true;
    +
     7310.  28649            }
    +
     7311.  28649        }
    +
     7312.  30451    }
    +
     7313.    479
    +
     7314.   1172    function post_b_and(thing) {
    +
     7315.   1172        if (
    +
     7316.   1172            is_weird(thing.expression[0])
    +
     7317.   1167            || is_equal(thing.expression[0], thing.expression[1])
    +
     7318.   1154            || thing.expression[0].constant === true
    +
     7319.   1153            || thing.expression[1].constant === true
    +
     7320.     19        ) {
    +
     7321.     19
    +
     7322.     19// test_cause:
    +
     7323.     19// ["aa=(()=>0)&&(()=>0)", "post_b_and", "weird_condition_a", "&&", 11]
    +
     7324.     19// ["aa=(``?``:``)&&(``?``:``)", "post_b_and", "weird_condition_a", "&&", 14]
    +
     7325.     19// ["aa=/./&&/./", "post_b_and", "weird_condition_a", "&&", 7]
    +
     7326.     19// ["aa=0&&0", "post_b_and", "weird_condition_a", "&&", 5]
    +
     7327.     19// ["aa=[]&&[]", "post_b_and", "weird_condition_a", "&&", 6]
    +
     7328.     19// ["aa=`${0}`&&`${0}`", "post_b_and", "weird_condition_a", "&&", 10]
    +
     7329.     19// ["
    +
     7330.     19// aa=function aa(){}&&function aa(){}
    +
     7331.     19// ", "post_b_and", "weird_condition_a", "&&", 19]
    +
     7332.     19// ["aa={}&&{}", "post_b_and", "weird_condition_a", "&&", 6]
    +
     7333.     19
    +
     7334.     19            warn("weird_condition_a", thing);
    +
     7335.     19        }
    +
     7336.   1172    }
    +
     7337.    479
    +
     7338.   1403    function post_b_lbracket(thing) {
    +
     7339.      1        if (thing.expression[0].id === "RegExp") {
    +
     7340.      1
    +
     7341.      1// test_cause:
    +
     7342.      1// ["aa=RegExp[0]", "post_b_lbracket", "weird_expression_a", "[", 10]
    +
     7343.      1
    +
     7344.      1            warn("weird_expression_a", thing);
    +
     7345.      1        }
    +
     7346.      1        if (is_weird(thing.expression[1])) {
    +
     7347.      1
    +
     7348.      1// test_cause:
    +
     7349.      1// ["aa[[0]]", "post_b_lbracket", "weird_expression_a", "[", 4]
    +
     7350.      1
    +
     7351.      1            warn("weird_expression_a", thing.expression[1]);
    +
     7352.      1        }
    +
     7353.   1403    }
    +
     7354.    479
    +
     7355.   9785    function post_b_lparen(thing) {
    +
     7356.   9785        let arg;
    +
     7357.   9785        let array;
    +
     7358.   9785        let cack;
    +
     7359.   9785        let left = thing.expression[0];
    +
     7360.   9785        let new_date;
    +
     7361.   9785        let paren;
    +
     7362.   9785        let the_new;
    +
     7363.    127        if (left.id === "new") {
    +
     7364.    127            the_new = left;
    +
     7365.    127            left = left.expression;
    +
     7366.    127        }
    +
     7367.     42        if (left.id === "function") {
    +
     7368.     42            if (!thing.wrapped) {
    +
     7369.     42
    +
     7370.     42// test_cause:
    +
     7371.     42// ["aa=function(){}()", "post_b_lparen", "wrap_immediate", "(", 16]
    +
     7372.     42
    +
     7373.     42                warn("wrap_immediate", thing);
    +
     7374.     42            }
    +
     7375.   9743        } else if (left.identifier) {
    +
     7376.   9743            if (the_new !== undefined) {
    +
     7377.   9743                if (
    +
     7378.   9743                    left.id[0] > "Z"
    +
     7379.   9743                    || left.id === "BigInt"
    +
     7380.   9743                    || left.id === "Boolean"
    +
     7381.   9743                    || left.id === "Number"
    +
     7382.   9743                    || left.id === "String"
    +
     7383.   9743                    || left.id === "Symbol"
    +
     7384.   9743                ) {
    +
     7385.   9743
    +
     7386.   9743// test_cause:
    +
     7387.   9743// ["new BigInt()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7388.   9743// ["new Boolean()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7389.   9743// ["new Number()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7390.   9743// ["new String()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7391.   9743// ["new Symbol()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7392.   9743// ["new aa()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7393.   9743
    +
     7394.   9743                    warn("unexpected_a", the_new);
    +
     7395.   9743                } else if (left.id === "Function") {
    +
     7396.   9743                    if (!option_dict.eval) {
    +
     7397.   9743
    +
     7398.   9743// test_cause:
    +
     7399.   9743// ["new Function()", "post_b_lparen", "unexpected_a", "new Function", 5]
    +
     7400.   9743
    +
     7401.   9743                        warn("unexpected_a", left, "new Function");
    +
     7402.   9743                    }
    +
     7403.   9743                } else if (left.id === "Array") {
    +
     7404.   9743                    arg = thing.expression;
    +
     7405.   9743                    if (arg.length !== 2 || arg[1].id === "(string)") {
    +
     7406.   9743
    +
     7407.   9743// test_cause:
    +
     7408.   9743// ["new Array()", "post_b_lparen", "expected_a_b", "new Array", 5]
    +
     7409.   9743
    +
     7410.   9743                        warn("expected_a_b", left, "[]", "new Array");
    +
     7411.   9743                    }
    +
     7412.   9743                } else if (left.id === "Object") {
    +
     7413.   9743
    +
     7414.   9743// test_cause:
    +
     7415.   9743// ["new Object()", "post_b_lparen", "expected_a_b", "new Object", 5]
    +
     7416.   9743
    +
     7417.   9743                    warn(
    +
     7418.   9743                        "expected_a_b",
    +
     7419.   9743                        left,
    +
     7420.   9743                        "Object.create(null)",
    +
     7421.   9743                        "new Object"
    +
     7422.   9743                    );
    +
     7423.   9743                }
    +
     7424.   9743            } else {
    +
     7425.   9743                if (
    +
     7426.   9743                    left.id[0] >= "A"
    +
     7427.   9743                    && left.id[0] <= "Z"
    +
     7428.   9743                    && left.id !== "BigInt"
    +
     7429.   9743                    && left.id !== "Boolean"
    +
     7430.   9743                    && left.id !== "Number"
    +
     7431.   9743                    && left.id !== "String"
    +
     7432.   9743                    && left.id !== "Symbol"
    +
     7433.   9743                ) {
    +
     7434.   9743
    +
     7435.   9743// test_cause:
    +
     7436.   9743// ["let Aa=Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8]
    +
     7437.   9743
    +
     7438.   9743                    warn("expected_a_before_b", left, "new", artifact(left));
    +
     7439.   9743                }
    +
     7440.   9743            }
    +
     7441.   9743        } else if (left.id === ".") {
    +
     7442.   9743            cack = the_new !== undefined;
    +
     7443.   9743            if (left.expression.id === "Date" && left.name.id === "UTC") {
    +
     7444.   9743
    +
     7445.   9743// test_cause:
    +
     7446.   9743// ["new Date.UTC()", "post_b_lparen", "cack", "", 0]
    +
     7447.   9743
    +
     7448.   9743                test_cause("cack");
    +
     7449.   9743                cack = !cack;
    +
     7450.   9743            }
    +
     7451.   9743            if ((
    +
     7452.   9743                // rx_cap
    +
     7453.   9743                /^[A-Z]/
    +
     7454.   9743            ).test(left.name.id) !== cack) {
    +
     7455.   9743                if (the_new !== undefined) {
    +
     7456.   9743
    +
     7457.   9743// test_cause:
    +
     7458.   9743// ["new Date.UTC()", "post_b_lparen", "unexpected_a", "new", 1]
    +
     7459.   9743
    +
     7460.   9743                    warn("unexpected_a", the_new);
    +
     7461.   9743                } else {
    +
     7462.   9743
    +
     7463.   9743// test_cause:
    +
     7464.   9743// ["let Aa=Aa.Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8]
    +
     7465.   9743
    +
     7466.   9743                    warn(
    +
     7467.   9743                        "expected_a_before_b",
    +
     7468.   9743                        left.expression,
    +
     7469.   9743                        "new",
    +
     7470.   9743                        left.name.id
    +
     7471.   9743                    );
    +
     7472.   9743                }
    +
     7473.   9743            }
    +
     7474.   9743            if (left.name.id === "getTime") {
    +
     7475.   9743                paren = left.expression;
    +
     7476.   9743                if (paren.id === "(") {
    +
     7477.   9743                    array = paren.expression;
    +
     7478.   9743                    if (array.length === 1) {
    +
     7479.   9743                        new_date = array[0];
    +
     7480.   9743                        if (
    +
     7481.   9743                            new_date.id === "new"
    +
     7482.   9743                            && new_date.expression.id === "Date"
    +
     7483.   9743                        ) {
    +
     7484.   9743
    +
     7485.   9743// test_cause:
    +
     7486.   9743// ["
    +
     7487.   9743// new Date().getTime()
    +
     7488.   9743// ", "post_b_lparen", "expected_a_b", "new Date().getTime()", 1]
    +
     7489.   9743
    +
     7490.   9743                            warn(
    +
     7491.   9743                                "expected_a_b",
    +
     7492.   9743                                new_date,
    +
     7493.   9743                                "Date.now()",
    +
     7494.   9743                                "new Date().getTime()"
    +
     7495.   9743                            );
    +
     7496.   9743                        }
    +
     7497.   9743                    }
    +
     7498.   9743                }
    +
     7499.   9743            }
    +
     7500.   9743        }
    +
     7501.   9785    }
    +
     7502.    479
    +
     7503.    979    function post_b_or(thing) {
    +
     7504.    979        if (
    +
     7505.    979            is_weird(thing.expression[0])
    +
     7506.    979            || is_equal(thing.expression[0], thing.expression[1])
    +
     7507.    978            || thing.expression[0].constant === true
    +
     7508.      2        ) {
    +
     7509.      2
    +
     7510.      2// test_cause:
    +
     7511.      2// ["aa=0||0", "post_b_or", "weird_condition_a", "||", 5]
    +
     7512.      2
    +
     7513.      2            warn("weird_condition_a", thing);
    +
     7514.      2        }
    +
     7515.    979    }
    +
     7516.    479
    +
     7517.     64    function post_s_export(the_thing) {
    +
     7518.     64
    +
     7519.     64// Some features must be at the most outermost level.
    +
     7520.     64
    +
     7521.      1        if (blockage !== token_global) {
    +
     7522.      1
    +
     7523.      1// test_cause:
    +
     7524.      1// ["
    +
     7525.      1// if(0){import aa from "aa";}
    +
     7526.      1// ", "post_s_export", "misplaced_a", "import", 7]
    +
     7527.      1
    +
     7528.      1            warn("misplaced_a", the_thing);
    +
     7529.      1        }
    +
     7530.     64    }
    +
     7531.    479
    +
     7532.      8    function post_s_for(thing) {
    +
     7533.      8
    +
     7534.      8// Recurse walk_statement().
    +
     7535.      8
    +
     7536.      8        walk_statement(thing.inc);
    +
     7537.      8    }
    +
     7538.    479
    +
     7539.   1865    function post_s_function(thing) {
    +
     7540.   1865        delete functionage.async;
    +
     7541.   1865        delete functionage.finally;
    +
     7542.   1865        delete functionage.loop;
    +
     7543.   1865        delete functionage.statement_prv;
    +
     7544.   1865        delete functionage.switch;
    +
     7545.   1865        delete functionage.try;
    +
     7546.   1865        functionage = function_stack.pop();
    +
     7547.      3        if (thing.wrapped) {
    +
     7548.      3
    +
     7549.      3// test_cause:
    +
     7550.      3// ["aa=(function(){})", "post_s_function", "unexpected_parens", "function", 5]
    +
     7551.      3
    +
     7552.      3            warn("unexpected_parens", thing);
    +
     7553.      3        }
    +
     7554.   1865        return post_s_lbrace();
    +
     7555.   1865    }
    +
     7556.    479
    +
     7557.     49    function post_s_import(the_thing) {
    +
     7558.     49        const name = the_thing.name;
    +
     7559.     49        if (name) {
    +
     7560.      3            if (Array.isArray(name)) {
    +
     7561.      3                name.forEach(function (name) {
    +
     7562.      3                    name.dead = false;
    +
     7563.      3                    name.init = true;
    +
     7564.      3                    blockage.live.push(name);
    +
     7565.      3                });
    +
     7566.     46            } else {
    +
     7567.     46                name.dead = false;
    +
     7568.     46                name.init = true;
    +
     7569.     46                blockage.live.push(name);
    +
     7570.     46            }
    +
     7571.     49            return post_s_export(the_thing);
    +
     7572.     49        }
    +
     7573.     49    }
    +
     7574.    479
    +
     7575.   7385    function post_s_lbrace() {
    +
     7576.   2857        blockage.live.forEach(function (name) {
    +
     7577.   2857            name.dead = true;
    +
     7578.   2857        });
    +
     7579.   7385        delete blockage.live;
    +
     7580.   7385        blockage = block_stack.pop();
    +
     7581.   7385    }
    +
     7582.    479
    +
     7583.     51    function post_s_try(thing) {
    +
     7584.     49        if (thing.catch) {
    +
     7585.     49            if (thing.catch.name) {
    +
     7586.     49                Object.assign(catchage.context[thing.catch.name.id], {
    +
     7587.     49                    dead: false,
    +
     7588.     49                    init: true
    +
     7589.     49                });
    +
     7590.     49            }
    +
     7591.     49
    +
     7592.     49// Recurse walk_statement().
    +
     7593.     49
    +
     7594.     49            walk_statement(thing.catch.block);
    +
     7595.     49
    +
     7596.     49// Restore previous catch-scope after catch-block.
    +
     7597.     49
    +
     7598.     49            catchage = catch_stack.pop();
    +
     7599.     49        }
    +
     7600.     51    }
    +
     7601.    479
    +
     7602.   2152    function post_s_var(thing) {
    +
     7603.   2454        thing.names.forEach(function (name) {
    +
     7604.   2454            name.dead = false;
    +
     7605.   1089            if (name.expression !== undefined) {
    +
     7606.   1089                walk_expression(name.expression);
    +
     7607.   1089
    +
     7608.   1089// Probably deadcode.
    +
     7609.   1089// if (name.id === "{" || name.id === "[") {
    +
     7610.   1089//     name.names.forEach(subactivate);
    +
     7611.   1089// } else {
    +
     7612.   1089//     name.init = true;
    +
     7613.   1089// }
    +
     7614.   1089
    +
     7615.   1089                jslint_assert(
    +
     7616.   1089                    !(name.id === "{" || name.id === "["),
    +
     7617.   1089                    `Expected !(name.id === "{" || name.id === "[").`
    +
     7618.   1089                );
    +
     7619.   1089                name.init = true;
    +
     7620.   1089            }
    +
     7621.   2454            blockage.live.push(name);
    +
     7622.   2454        });
    +
     7623.   2152    }
    +
     7624.    479
    +
     7625.    207    function post_t(thing) {
    +
     7626.    207        if (
    +
     7627.    207            is_weird(thing.expression[0])
    +
     7628.    207            || thing.expression[0].constant === true
    +
     7629.    200            || is_equal(thing.expression[1], thing.expression[2])
    +
     7630.      7        ) {
    +
     7631.      7            warn("unexpected_a", thing);
    +
     7632.    200        } else if (is_equal(thing.expression[0], thing.expression[1])) {
    +
     7633.    200
    +
     7634.    200// test_cause:
    +
     7635.    200// ["aa?aa:0", "post_t", "expected_a_b", "?", 3]
    +
     7636.    200
    +
     7637.    200            warn("expected_a_b", thing, "||", "?");
    +
     7638.    200        } else if (is_equal(thing.expression[0], thing.expression[2])) {
    +
     7639.    200
    +
     7640.    200// test_cause:
    +
     7641.    200// ["aa?0:aa", "post_t", "expected_a_b", "?", 3]
    +
     7642.    200
    +
     7643.    200            warn("expected_a_b", thing, "&&", "?");
    +
     7644.    200        } else if (
    +
     7645.    200            thing.expression[1].id === "true"
    +
     7646.    200            && thing.expression[2].id === "false"
    +
     7647.    200        ) {
    +
     7648.    200
    +
     7649.    200// test_cause:
    +
     7650.    200// ["aa?true:false", "post_t", "expected_a_b", "?", 3]
    +
     7651.    200
    +
     7652.    200            warn("expected_a_b", thing, "!!", "?");
    +
     7653.    200        } else if (
    +
     7654.    200            thing.expression[1].id === "false"
    +
     7655.    200            && thing.expression[2].id === "true"
    +
     7656.    200        ) {
    +
     7657.    200
    +
     7658.    200// test_cause:
    +
     7659.    200// ["aa?false:true", "post_t", "expected_a_b", "?", 3]
    +
     7660.    200
    +
     7661.    200            warn("expected_a_b", thing, "!", "?");
    +
     7662.    200        } else if (
    +
     7663.    200            thing.expression[0].wrapped !== true
    +
     7664.    200            && (
    +
     7665.    200                thing.expression[0].id === "||"
    +
     7666.    200                || thing.expression[0].id === "&&"
    +
     7667.    200            )
    +
     7668.    200        ) {
    +
     7669.    200
    +
     7670.    200// test_cause:
    +
     7671.    200// ["(aa&&!aa?0:1)", "post_t", "wrap_condition", "&&", 4]
    +
     7672.    200
    +
     7673.    200            warn("wrap_condition", thing.expression[0]);
    +
     7674.    200        }
    +
     7675.    207    }
    +
     7676.    479
    +
     7677.   3718    function post_u(thing) {
    +
     7678.    704        if (thing.id === "`") {
    +
     7679.    704            if (thing.expression.every(function (thing) {
    +
     7680.    704                return thing.constant;
    +
     7681.    704            })) {
    +
     7682.    704                thing.constant = true;
    +
     7683.    704            }
    +
     7684.   3014        } else if (thing.id === "!") {
    +
     7685.   3014            if (thing.expression.constant === true) {
    +
     7686.   3014                warn("unexpected_a", thing);
    +
     7687.   3014            }
    +
     7688.   3014        } else if (thing.id === "!!") {
    +
     7689.   3014            if (!option_dict.convert) {
    +
     7690.   3014
    +
     7691.   3014// test_cause:
    +
     7692.   3014// ["!!0", "post_u", "expected_a_b", "!!", 1]
    +
     7693.   3014
    +
     7694.   3014                warn("expected_a_b", thing, "Boolean(...)", "!!");
    +
     7695.   3014            }
    +
     7696.   3014        } else if (
    +
     7697.   3014            thing.id !== "["
    +
     7698.   3014            && thing.id !== "{"
    +
     7699.   3014            && thing.id !== "function"
    +
     7700.   3014            && thing.id !== "new"
    +
     7701.   3014        ) {
    +
     7702.   3014            if (thing.expression.constant === true) {
    +
     7703.   3014                thing.constant = true;
    +
     7704.   3014            }
    +
     7705.   3014        }
    +
     7706.   3718    }
    +
     7707.    479
    +
     7708.      7    function post_u_plus(thing) {
    +
     7709.      7        const right = thing.expression;
    +
     7710.      7        if (!option_dict.convert) {
    +
     7711.      7
    +
     7712.      7// test_cause:
    +
     7713.      7// ["aa=+0", "post_u_plus", "expected_a_b", "+", 4]
    +
     7714.      7
    +
     7715.      7            warn("expected_a_b", thing, "Number(...)", "+");
    +
     7716.      7        }
    +
     7717.      1        if (right.id === "(" && right.expression[0].id === "new") {
    +
     7718.      1            warn("unexpected_a_before_b", thing, "+", "new");
    +
     7719.      6        } else if (
    +
     7720.      6            right.constant
    +
     7721.      6            || right.id === "{"
    +
     7722.      6            || (right.id === "[" && right.arity !== "binary")
    +
     7723.      6        ) {
    +
     7724.      6            warn("unexpected_a", thing, "+");
    +
     7725.      6        }
    +
     7726.      7    }
    +
     7727.    479
    +
     7728.  35238    function pre_a_bitwise(thing) {
    +
     7729.  35238
    +
     7730.  35238// These are the bitwise operators.
    +
     7731.  35238
    +
     7732.  35236        switch (!option_dict.bitwise && thing.id) {
    +
     7733.      2        case "&":
    +
     7734.      3        case "&=":
    +
     7735.      5        case "<<":
    +
     7736.      6        case "<<=":
    +
     7737.      8        case ">>":
    +
     7738.      9        case ">>=":
    +
     7739.     11        case ">>>":
    +
     7740.     12        case ">>>=":
    +
     7741.     14        case "^":
    +
     7742.     15        case "^=":
    +
     7743.     17        case "|":
    +
     7744.     18        case "|=":
    +
     7745.     21        case "~":
    +
     7746.     21
    +
     7747.     21// test_cause:
    +
     7748.     21// ["0&0", "pre_a_bitwise", "unexpected_a", "&", 2]
    +
     7749.     21// ["0&=0", "pre_a_bitwise", "unexpected_a", "&=", 2]
    +
     7750.     21// ["0<<0", "pre_a_bitwise", "unexpected_a", "<<", 2]
    +
     7751.     21// ["0<<=0", "pre_a_bitwise", "unexpected_a", "<<=", 2]
    +
     7752.     21// ["0>>0", "pre_a_bitwise", "unexpected_a", ">>", 2]
    +
     7753.     21// ["0>>=0", "pre_a_bitwise", "unexpected_a", ">>=", 2]
    +
     7754.     21// ["0>>>0", "pre_a_bitwise", "unexpected_a", ">>>", 2]
    +
     7755.     21// ["0>>>=0", "pre_a_bitwise", "unexpected_a", ">>>=", 2]
    +
     7756.     21// ["0^0", "pre_a_bitwise", "unexpected_a", "^", 2]
    +
     7757.     21// ["0^=0", "pre_a_bitwise", "unexpected_a", "^=", 2]
    +
     7758.     21// ["0|0", "pre_a_bitwise", "unexpected_a", "|", 2]
    +
     7759.     21// ["0|=0", "pre_a_bitwise", "unexpected_a", "|=", 2]
    +
     7760.     21// ["~0", "pre_a_bitwise", "unexpected_a", "~", 1]
    +
     7761.     21
    +
     7762.     21            warn("unexpected_a", thing);
    +
     7763.     21            break;
    +
     7764.  35238        }
    +
     7765.  35238        if (
    +
     7766.  35238            thing.id !== "("
    +
     7767.  25453            && thing.id !== "&&"
    +
     7768.  24281            && thing.id !== "||"
    +
     7769.  23302            && thing.id !== "="
    +
     7770.  19238            && Array.isArray(thing.expression)
    +
     7771.   8144            && thing.expression.length === 2
    +
     7772.   8143            && (
    +
     7773.   8143                relationop[thing.expression[0].id] === true
    +
     7774.   8143                || relationop[thing.expression[1].id] === true
    +
     7775.   8143            )
    +
     7776.      1        ) {
    +
     7777.      1
    +
     7778.      1// test_cause:
    +
     7779.      1// ["0<0<0", "pre_a_bitwise", "unexpected_a", "<", 4]
    +
     7780.      1
    +
     7781.      1            warn("unexpected_a", thing);
    +
     7782.      1        }
    +
     7783.  35238    }
    +
     7784.    479
    +
     7785.  30451    function pre_b(thing) {
    +
     7786.  30451        let left;
    +
     7787.  30451        let right;
    +
     7788.  30451        let value;
    +
     7789.   3955        if (relationop[thing.id] === true) {
    +
     7790.   3955            left = thing.expression[0];
    +
     7791.   3955            right = thing.expression[1];
    +
     7792.   3955            if (left.id === "NaN" || right.id === "NaN") {
    +
     7793.   3955
    +
     7794.   3955// test_cause:
    +
     7795.   3955// ["NaN===NaN", "pre_b", "number_isNaN", "===", 4]
    +
     7796.   3955
    +
     7797.   3955                warn("number_isNaN", thing);
    +
     7798.   3955            } else if (left.id === "typeof") {
    +
     7799.   3955                if (right.id !== "(string)") {
    +
     7800.   3955                    if (right.id !== "typeof") {
    +
     7801.   3955
    +
     7802.   3955// test_cause:
    +
     7803.   3955// ["typeof 0===0", "pre_b", "expected_string_a", "0", 12]
    +
     7804.   3955
    +
     7805.   3955                        warn("expected_string_a", right);
    +
     7806.   3955                    }
    +
     7807.   3955                } else {
    +
     7808.   3955                    value = right.value;
    +
     7809.   3955                    if (value === "null" || value === "undefined") {
    +
     7810.   3955
    +
     7811.   3955// test_cause:
    +
     7812.   3955// ["
    +
     7813.   3955// typeof aa==="undefined"
    +
     7814.   3955// ", "pre_b", "unexpected_typeof_a", "undefined", 13]
    +
     7815.   3955
    +
     7816.   3955                        warn("unexpected_typeof_a", right, value);
    +
     7817.   3955                    } else if (
    +
     7818.   3955                        value !== "bigint"
    +
     7819.   3955                        && value !== "boolean"
    +
     7820.   3955                        && value !== "function"
    +
     7821.   3955                        && value !== "number"
    +
     7822.   3955                        && value !== "object"
    +
     7823.   3955                        && value !== "string"
    +
     7824.   3955                        && value !== "symbol"
    +
     7825.   3955                    ) {
    +
     7826.   3955
    +
     7827.   3955// test_cause:
    +
     7828.   3955// ["typeof 0===\"aa\"", "pre_b", "expected_type_string_a", "aa", 12]
    +
     7829.   3955
    +
     7830.   3955                        warn("expected_type_string_a", right, value);
    +
     7831.   3955                    }
    +
     7832.   3955                }
    +
     7833.   3955            }
    +
     7834.   3955        }
    +
     7835.  30451    }
    +
     7836.    479
    +
     7837.      1    function pre_b_eqeq(thing) {
    +
     7838.      1
    +
     7839.      1// test_cause:
    +
     7840.      1// ["0==0", "pre_b_eqeq", "expected_a_b", "==", 2]
    +
     7841.      1
    +
     7842.      1        warn("expected_a_b", thing, "===", "==");
    +
     7843.      1    }
    +
     7844.    479
    +
     7845.      1    function pre_b_in(thing) {
    +
     7846.      1
    +
     7847.      1// test_cause:
    +
     7848.      1// ["aa in aa", "pre_b_in", "infix_in", "in", 4]
    +
     7849.      1
    +
     7850.      1        warn("infix_in", thing);
    +
     7851.      1    }
    +
     7852.    479
    +
     7853.      1    function pre_b_instanceof(thing) {
    +
     7854.      1
    +
     7855.      1// test_cause:
    +
     7856.      1// ["0 instanceof 0", "pre_b_instanceof", "unexpected_a", "instanceof", 3]
    +
     7857.      1
    +
     7858.      1        warn("unexpected_a", thing);
    +
     7859.      1    }
    +
     7860.    479
    +
     7861.   9785    function pre_b_lparen(thing) {
    +
     7862.   9785        const left = thing.expression[0];
    +
     7863.   9785        let left_variable;
    +
     7864.   9785        let parent;
    +
     7865.   9785        if (
    +
     7866.   9785            left.identifier
    +
     7867.   6470            && functionage.context[left.id] === undefined
    +
     7868.   2935            && typeof functionage.name === "object"
    +
     7869.   2336        ) {
    +
     7870.   2336            parent = functionage.name.parent;
    +
     7871.   2336            if (parent) {
    +
     7872.   2336                left_variable = parent.context[left.id];
    +
     7873.   2336                if (
    +
     7874.   2336                    left_variable !== undefined
    +
     7875.   2336
    +
     7876.   2336// Probably deadcode.
    +
     7877.   2336// && left_variable.dead
    +
     7878.   2336
    +
     7879.   2336                    && left_variable.parent === parent
    +
     7880.   2336                    && left_variable.calls !== undefined
    +
     7881.   2336                    && left_variable.calls[functionage.name.id] !== undefined
    +
     7882.   2336                ) {
    +
     7883.   2336                    left_variable.dead = false;
    +
     7884.   2336                }
    +
     7885.   2336            }
    +
     7886.   2336        }
    +
     7887.   9785    }
    +
     7888.    479
    +
     7889.      1    function pre_b_noteq(thing) {
    +
     7890.      1
    +
     7891.      1// test_cause:
    +
     7892.      1// ["0!=0", "pre_b_noteq", "expected_a_b", "!=", 2]
    +
     7893.      1
    +
     7894.      1        warn("expected_a_b", thing, "!==", "!=");
    +
     7895.      1    }
    +
     7896.    479
    +
     7897.    979    function pre_b_or(thing) {
    +
     7898.   1958        thing.expression.forEach(function (thang) {
    +
     7899.    188            if (thang.id === "&&" && !thang.wrapped) {
    +
     7900.      1
    +
     7901.      1// test_cause:
    +
     7902.      1// ["0&&0||0", "pre_b_or", "and", "&&", 2]
    +
     7903.      1
    +
     7904.      1                warn("and", thang);
    +
     7905.      1            }
    +
     7906.   1958        });
    +
     7907.    979    }
    +
     7908.    479
    +
     7909.      8    function pre_s_for(thing) {
    +
     7910.      8        let the_variable;
    +
     7911.      2        if (thing.name !== undefined) {
    +
     7912.      2            thing.name.dead = false;
    +
     7913.      2            the_variable = lookup(thing.name);
    +
     7914.      2            if (the_variable !== undefined) {
    +
     7915.      2                if (the_variable.init && the_variable.readonly) {
    +
     7916.      2
    +
     7917.      2// test_cause:
    +
     7918.      2// ["const aa=0;for(aa in aa){}", "pre_s_for", "bad_assignment_a", "aa", 16]
    +
     7919.      2
    +
     7920.      2                    warn("bad_assignment_a", thing.name);
    +
     7921.      2                }
    +
     7922.      2                the_variable.init = true;
    +
     7923.      2            }
    +
     7924.      2        }
    +
     7925.      8
    +
     7926.      8// Recurse walk_statement().
    +
     7927.      8
    +
     7928.      8        walk_statement(thing.initial);
    +
     7929.      8    }
    +
     7930.    479
    +
     7931.   1865    function pre_s_function(thing) {
    +
     7932.   1865
    +
     7933.   1865// test_cause:
    +
     7934.   1865// ["()=>0", "pre_s_function", "", "", 0]
    +
     7935.   1865// ["(function (){}())", "pre_s_function", "", "", 0]
    +
     7936.   1865// ["function aa(){}", "pre_s_function", "", "", 0]
    +
     7937.   1865
    +
     7938.   1865        test_cause("");
    +
     7939.   1031        if (thing.arity === "statement" && blockage.body !== true) {
    +
     7940.      1
    +
     7941.      1// test_cause:
    +
     7942.      1// ["if(0){function aa(){}\n}", "pre_s_function", "unexpected_a", "function", 7]
    +
     7943.      1
    +
     7944.      1            warn("unexpected_a", thing);
    +
     7945.      1        }
    +
     7946.   1865        function_stack.push(functionage);
    +
     7947.   1865        block_stack.push(blockage);
    +
     7948.   1865        functionage = thing;
    +
     7949.   1865        blockage = thing;
    +
     7950.   1865        thing.live = [];
    +
     7951.   1072        if (typeof thing.name === "object") {
    +
     7952.   1072            thing.name.dead = false;
    +
     7953.   1072            thing.name.init = true;
    +
     7954.   1072        }
    +
     7955.      7        if (thing.extra === "get") {
    +
     7956.      7            if (thing.parameters.length !== 0) {
    +
     7957.      7
    +
     7958.      7// test_cause:
    +
     7959.      7// ["
    +
     7960.      7// /*jslint getset*/
    +
     7961.      7// aa={get aa(aa){}}
    +
     7962.      7// ", "pre_s_function", "bad_get", "function", 9]
    +
     7963.      7
    +
     7964.      7                warn("bad_get", thing);
    +
     7965.      7            }
    +
     7966.   1858        } else if (thing.extra === "set") {
    +
     7967.   1858            if (thing.parameters.length !== 1) {
    +
     7968.   1858
    +
     7969.   1858// test_cause:
    +
     7970.   1858// ["
    +
     7971.   1858// /*jslint getset*/
    +
     7972.   1858// aa={set aa(){}}
    +
     7973.   1858// ", "pre_s_function", "bad_set", "function", 9]
    +
     7974.   1858
    +
     7975.   1858                warn("bad_set", thing);
    +
     7976.   1858            }
    +
     7977.   1858        }
    +
     7978.   1930        thing.parameters.forEach(function (name) {
    +
     7979.   1930            walk_expression(name.expression);
    +
     7980.   1724            if (name.id === "{" || name.id === "[") {
    +
     7981.    240                name.names.forEach(subactivate);
    +
     7982.   1690            } else {
    +
     7983.   1690                name.dead = false;
    +
     7984.   1690                name.init = true;
    +
     7985.   1690            }
    +
     7986.   1930        });
    +
     7987.   1865    }
    +
     7988.    479
    +
     7989.   5520    function pre_s_lbrace(thing) {
    +
     7990.   5520        block_stack.push(blockage);
    +
     7991.   5520        blockage = thing;
    +
     7992.   5520        thing.live = [];
    +
     7993.   5520    }
    +
     7994.    479
    +
     7995.     51    function pre_try(thing) {
    +
     7996.     49        if (thing.catch !== undefined) {
    +
     7997.     49
    +
     7998.     49// Create new catch-scope for catch-parameter.
    +
     7999.     49
    +
     8000.     49            catch_stack.push(catchage);
    +
     8001.     49            catchage = thing.catch;
    +
     8002.     49        }
    +
     8003.     51    }
    +
     8004.    479
    +
     8005.  27312    function pre_v(thing) {
    +
     8006.  27312        const the_variable = lookup(thing);
    +
     8007.  27240        if (the_variable !== undefined) {
    +
     8008.  27240            thing.variable = the_variable;
    +
     8009.  27240            the_variable.used += 1;
    +
     8010.  27240        }
    +
     8011.  27312    }
    +
     8012.    479
    +
     8013.    647    function subactivate(name) {
    +
     8014.    647        name.init = true;
    +
     8015.    647        name.dead = false;
    +
     8016.    647        blockage.live.push(name);
    +
     8017.    647    }
    +
     8018.    479
    +
     8019. 155875    function walk_expression(thing) {
    +
     8020.  98218        if (thing) {
    +
     8021.  98218            if (Array.isArray(thing)) {
    +
     8022.  98218
    +
     8023.  98218// test_cause:
    +
     8024.  98218// ["(function(){}())", "walk_expression", "isArray", "", 0]
    +
     8025.  98218// ["0&&0", "walk_expression", "isArray", "", 0]
    +
     8026.  98218
    +
     8027.  98218                test_cause("isArray");
    +
     8028.  98218                thing.forEach(walk_expression);
    +
     8029.  98218            } else {
    +
     8030.  98218                preamble(thing);
    +
     8031.  98218                walk_expression(thing.expression);
    +
     8032.  98218                if (thing.id === "function") {
    +
     8033.  98218
    +
     8034.  98218// test_cause:
    +
     8035.  98218// ["aa=function(){}", "walk_expression", "function", "", 0]
    +
     8036.  98218
    +
     8037.  98218                    test_cause("function");
    +
     8038.  98218
    +
     8039.  98218// Recurse walk_statement().
    +
     8040.  98218
    +
     8041.  98218                    walk_statement(thing.block);
    +
     8042.  98218                }
    +
     8043.  98218                if (
    +
     8044.  98218                    thing.arity === "preassign" || thing.arity === "postassign"
    +
     8045.  98218                ) {
    +
     8046.  98218
    +
     8047.  98218// test_cause:
    +
     8048.  98218// ["aa=++aa", "walk_expression", "unexpected_a", "++", 4]
    +
     8049.  98218// ["aa=--aa", "walk_expression", "unexpected_a", "--", 4]
    +
     8050.  98218
    +
     8051.  98218                    warn("unexpected_a", thing);
    +
     8052.  98218                } else if (
    +
     8053.  98218                    thing.arity === "statement"
    +
     8054.  98218                    || thing.arity === "assignment"
    +
     8055.  98218                ) {
    +
     8056.  98218
    +
     8057.  98218// test_cause:
    +
     8058.  98218// ["aa[aa=0]", "walk_expression", "unexpected_statement_a", "=", 6]
    +
     8059.  98218
    +
     8060.  98218                    warn("unexpected_statement_a", thing);
    +
     8061.  98218                }
    +
     8062.  98218
    +
     8063.  98218// test_cause:
    +
     8064.  98218// ["aa=0", "walk_expression", "default", "", 0]
    +
     8065.  98218
    +
     8066.  98218                test_cause("default");
    +
     8067.  98218                postamble(thing);
    +
     8068.  98218            }
    +
     8069.  98218        }
    +
     8070. 155875    }
    +
     8071.    479
    +
     8072.  75022    function walk_statement(thing) {
    +
     8073.  33837        if (thing) {
    +
     8074.  33837            if (Array.isArray(thing)) {
    +
     8075.  33837
    +
     8076.  33837// test_cause:
    +
     8077.  33837// ["+[]", "walk_statement", "isArray", "", 0]
    +
     8078.  33837
    +
     8079.  33837                test_cause("isArray");
    +
     8080.  33837
    +
     8081.  33837// Recurse walk_statement().
    +
     8082.  33837
    +
     8083.  33837                thing.forEach(walk_statement);
    +
     8084.  33837            } else {
    +
     8085.  33837                preamble(thing);
    +
     8086.  33837                walk_expression(thing.expression);
    +
     8087.  33837                if (thing.arity === "binary") {
    +
     8088.  33837                    if (thing.id !== "(") {
    +
     8089.  33837
    +
     8090.  33837// test_cause:
    +
     8091.  33837// ["0&&0", "walk_statement", "unexpected_expression_a", "&&", 2]
    +
     8092.  33837
    +
     8093.  33837                        warn("unexpected_expression_a", thing);
    +
     8094.  33837                    }
    +
     8095.  33837                } else if (
    +
     8096.  33837                    thing.arity !== "statement"
    +
     8097.  33837                    && thing.arity !== "assignment"
    +
     8098.  33837                    && thing.id !== "import"
    +
     8099.  33837                ) {
    +
     8100.  33837
    +
     8101.  33837// test_cause:
    +
     8102.  33837// ["!0", "walk_statement", "unexpected_expression_a", "!", 1]
    +
     8103.  33837// ["+[]", "walk_statement", "unexpected_expression_a", "+", 1]
    +
     8104.  33837// ["+new aa()", "walk_statement", "unexpected_expression_a", "+", 1]
    +
     8105.  33837// ["0", "walk_statement", "unexpected_expression_a", "0", 1]
    +
     8106.  33837// ["
    +
     8107.  33837// async function aa(){await 0;}
    +
     8108.  33837// ", "walk_statement", "unexpected_expression_a", "0", 27]
    +
     8109.  33837// ["typeof 0", "walk_statement", "unexpected_expression_a", "typeof", 1]
    +
     8110.  33837
    +
     8111.  33837                    warn("unexpected_expression_a", thing);
    +
     8112.  33837                }
    +
     8113.  33837
    +
     8114.  33837// Recurse walk_statement().
    +
     8115.  33837
    +
     8116.  33837                walk_statement(thing.block);
    +
     8117.  33837                walk_statement(thing.else);
    +
     8118.  33837                postamble(thing);
    +
     8119.  33837            }
    +
     8120.  33837        }
    +
     8121.  75022    }
    +
     8122.    479
    +
     8123.    479    postaction = action(posts);
    +
     8124.    479    postamble = amble(posts);
    +
     8125.    479    preaction = action(pres);
    +
     8126.    479    preamble = amble(pres);
    +
     8127.    479    postaction("assignment", "+=", post_a_pluseq);
    +
     8128.    479    postaction("assignment", post_a);
    +
     8129.    479    postaction("binary", "&&", post_b_and);
    +
     8130.    479    postaction("binary", "(", post_b_lparen);
    +
     8131.    479    postaction("binary", "=>", post_s_function);
    +
     8132.    479    postaction("binary", "[", post_b_lbracket);
    +
     8133.    479    postaction("binary", "||", post_b_or);
    +
     8134.    479    postaction("binary", post_b);
    +
     8135.    479    postaction("statement", "const", post_s_var);
    +
     8136.    479    postaction("statement", "export", post_s_export);
    +
     8137.    479    postaction("statement", "for", post_s_for);
    +
     8138.    479    postaction("statement", "function", post_s_function);
    +
     8139.    479    postaction("statement", "import", post_s_import);
    +
     8140.    479    postaction("statement", "let", post_s_var);
    +
     8141.    479    postaction("statement", "try", post_s_try);
    +
     8142.    479    postaction("statement", "var", post_s_var);
    +
     8143.    479    postaction("statement", "{", post_s_lbrace);
    +
     8144.    479    postaction("ternary", post_t);
    +
     8145.    479    postaction("unary", "+", post_u_plus);
    +
     8146.    479    postaction("unary", "function", post_s_function);
    +
     8147.    479    postaction("unary", post_u);
    +
     8148.    479    preaction("assignment", pre_a_bitwise);
    +
     8149.    479    preaction("binary", "!=", pre_b_noteq);
    +
     8150.    479    preaction("binary", "(", pre_b_lparen);
    +
     8151.    479    preaction("binary", "==", pre_b_eqeq);
    +
     8152.    479    preaction("binary", "=>", pre_s_function);
    +
     8153.    479    preaction("binary", "in", pre_b_in);
    +
     8154.    479    preaction("binary", "instanceof", pre_b_instanceof);
    +
     8155.    479    preaction("binary", "||", pre_b_or);
    +
     8156.    479    preaction("binary", pre_b);
    +
     8157.    479    preaction("binary", pre_a_bitwise);
    +
     8158.    479    preaction("statement", "for", pre_s_for);
    +
     8159.    479    preaction("statement", "function", pre_s_function);
    +
     8160.    479    preaction("statement", "try", pre_try);
    +
     8161.    479    preaction("statement", "{", pre_s_lbrace);
    +
     8162.    479    preaction("unary", "function", pre_s_function);
    +
     8163.    479    preaction("unary", "~", pre_a_bitwise);
    +
     8164.    479    preaction("variable", pre_v);
    +
     8165.    479
    +
     8166.    479    walk_statement(state.token_tree);
    +
     8167.    479}
    +
     8168.      1
    +
     8169.    184function jslint_phase5_whitage(state) {
    +
     8170.    184
    +
     8171.    184// PHASE 5. Check whitespace between tokens in <token_list>.
    +
     8172.    184
    +
     8173.    184    let {
    +
     8174.    184        artifact,
    +
     8175.    184        catch_list,
    +
     8176.    184        function_list,
    +
     8177.    184        function_stack,
    +
     8178.    184        option_dict,
    +
     8179.    184        test_cause,
    +
     8180.    184        token_global,
    +
     8181.    184        token_list,
    +
     8182.    184        warn
    +
     8183.    184    } = state;
    +
     8184.    184    let closer = "(end)";
    +
     8185.    184    let free = false;
    +
     8186.    184
    +
     8187.    184// free = false
    +
     8188.    184
    +
     8189.    184// cause:
    +
     8190.    184// "()=>0"
    +
     8191.    184// "aa()"
    +
     8192.    184// "aa(0,0)"
    +
     8193.    184// "function(){}"
    +
     8194.    184
    +
     8195.    184// free = true
    +
     8196.    184
    +
     8197.    184// cause:
    +
     8198.    184// "(0)"
    +
     8199.    184// "(aa)"
    +
     8200.    184// "aa(0)"
    +
     8201.    184// "do{}while()"
    +
     8202.    184// "for(){}"
    +
     8203.    184// "if(){}"
    +
     8204.    184// "switch(){}"
    +
     8205.    184// "while(){}"
    +
     8206.    184
    +
     8207.    184    let left = token_global;
    +
     8208.    184    let margin = 0;
    +
     8209.    184    let mode_indent = (
    +
     8210.    184
    +
     8211.    184// PR-330 - Allow 2-space indent.
    +
     8212.    184
    +
     8213.    184        option_dict.indent2
    +
     8214.      5        ? 2
    +
     8215.    179        : 4
    +
     8216.    184    );
    +
     8217.    184    let nr_comments_skipped = 0;
    +
     8218.    184    let open = true;
    +
     8219.    184    let opening = true;
    +
     8220.    184    let right;
    +
     8221.    184
    +
     8222.    184// This is the set of infix operators that require a space on each side.
    +
     8223.    184
    +
     8224.    184    let spaceop = object_assign_from_list(empty(), [
    +
     8225.    184        "!=", "!==", "%", "%=", "&", "&&", "&=", "*", "*=", "+=", "-=", "/",
    +
     8226.    184        "/=", "<", "<<", "<<=", "<=", "=", "==", "===", "=>", ">", ">=", ">>",
    +
     8227.    184        ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||"
    +
     8228.    184    ], true);
    +
     8229.    184
    +
     8230.  35434    function at_margin(fit) {
    +
     8231.  35434        const at = margin + fit;
    +
     8232.     21        if (right.from !== at) {
    +
     8233.     21            return expected_at(at);
    +
     8234.     21        }
    +
     8235.  35434    }
    +
     8236.    184
    +
     8237.   1912    function delve(the_function) {
    +
     8238.  12131        Object.keys(the_function.context).forEach(function (id) {
    +
     8239.  12131            const name = the_function.context[id];
    +
     8240.  12101            if (id !== "ignore" && name.parent === the_function) {
    +
     8241.   5895
    +
     8242.   5895// test_cause:
    +
     8243.   5895// ["function aa(aa) {return aa;}", "delve", "id", "", 0]
    +
     8244.   5895
    +
     8245.   5895                test_cause("id");
    +
     8246.   5895                if (
    +
     8247.   5895                    name.used === 0
    +
     8248.   5895
    +
     8249.   5895// Probably deadcode.
    +
     8250.   5895// && (
    +
     8251.   5895//     name.role !== "function"
    +
     8252.   5895//     || name.parent.arity !== "unary"
    +
     8253.   5895// )
    +
     8254.   5895
    +
     8255.   5895                    && jslint_assert(
    +
     8256.   5895                        name.role !== "function",
    +
     8257.   5895                        `Expected name.role !== "function".`
    +
     8258.   5895                    )
    +
     8259.   5895                ) {
    +
     8260.   5895
    +
     8261.   5895// test_cause:
    +
     8262.   5895// ["/*jslint node*/\nlet aa;", "delve", "unused_a", "aa", 5]
    +
     8263.   5895// ["function aa(aa){return;}", "delve", "unused_a", "aa", 13]
    +
     8264.   5895// ["let aa=0;try{aa();}catch(bb){aa();}", "delve", "unused_a", "bb", 26]
    +
     8265.   5895
    +
     8266.   5895                    warn("unused_a", name);
    +
     8267.   5895                } else if (!name.init) {
    +
     8268.   5895
    +
     8269.   5895// test_cause:
    +
     8270.   5895// ["/*jslint node*/\nlet aa;aa();", "delve", "uninitialized_a", "aa", 5]
    +
     8271.   5895
    +
     8272.   5895                    warn("uninitialized_a", name);
    +
     8273.   5895                }
    +
     8274.   5895            }
    +
     8275.  12131        });
    +
     8276.   1912    }
    +
     8277.    184
    +
     8278.     25    function expected_at(at) {
    +
     8279.     25
    +
     8280.     25// Probably deadcode.
    +
     8281.     25// if (right === undefined) {
    +
     8282.     25//     right = token_nxt;
    +
     8283.     25// }
    +
     8284.     25
    +
     8285.     25        jslint_assert(
    +
     8286.     25            !(right === undefined),
    +
     8287.     25            `Expected !(right === undefined).`
    +
     8288.     25        );
    +
     8289.     25        warn(
    +
     8290.     25            "expected_a_at_b_c",
    +
     8291.     25            right,
    +
     8292.     25            artifact(right),
    +
     8293.     25
    +
     8294.     25// Fudge column numbers in warning message.
    +
     8295.     25
    +
     8296.     25            at + jslint_fudge,
    +
     8297.     25            right.from + jslint_fudge
    +
     8298.     25        );
    +
     8299.     25    }
    +
     8300.    184
    +
     8301.   2953    function no_space() {
    +
     8302.   2952        if (left.line === right.line) {
    +
     8303.   2952
    +
     8304.   2952// from:
    +
     8305.   2952// if (left.line === right.line) {
    +
     8306.   2952//     no_space();
    +
     8307.   2952// } else {
    +
     8308.   2952
    +
     8309.   2952            if (left.thru !== right.from && nr_comments_skipped === 0) {
    +
     8310.   2952
    +
     8311.   2952// test_cause:
    +
     8312.   2952// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14]
    +
     8313.   2952
    +
     8314.   2952                warn(
    +
     8315.   2952                    "unexpected_space_a_b",
    +
     8316.   2952                    right,
    +
     8317.   2952                    artifact(left),
    +
     8318.   2952                    artifact(right)
    +
     8319.   2952                );
    +
     8320.   2952            }
    +
     8321.   2952        } else {
    +
     8322.      1
    +
     8323.      1// from:
    +
     8324.      1// } else if (
    +
     8325.      1//     right.arity === "binary"
    +
     8326.      1//     && right.id === "("
    +
     8327.      1//     && free
    +
     8328.      1// ) {
    +
     8329.      1//     no_space();
    +
     8330.      1// } else if (
    +
     8331.      1
    +
     8332.      1// Probably deadcode.
    +
     8333.      1// if (open) {
    +
     8334.      1//     const at = (
    +
     8335.      1//         free
    +
     8336.      1//         ? margin
    +
     8337.      1//         : margin + 8
    +
     8338.      1//     );
    +
     8339.      1//     if (right.from < at) {
    +
     8340.      1//         expected_at(at);
    +
     8341.      1//     }
    +
     8342.      1// } else {
    +
     8343.      1//     if (right.from !== margin + 8) {
    +
     8344.      1//         expected_at(margin + 8);
    +
     8345.      1//     }
    +
     8346.      1// }
    +
     8347.      1
    +
     8348.      1            jslint_assert(open, `Expected open.`);
    +
     8349.      1            jslint_assert(free, `Expected free.`);
    +
     8350.      1            if (right.from < margin) {
    +
     8351.      1
    +
     8352.      1// test_cause:
    +
     8353.      1// ["let aa = aa(\naa\n()\n);", "expected_at", "expected_a_at_b_c", "5", 1]
    +
     8354.      1
    +
     8355.      1                expected_at(margin);
    +
     8356.      1            }
    +
     8357.      1        }
    +
     8358.   2953    }
    +
     8359.    184
    +
     8360.  90547    function no_space_only() {
    +
     8361.  90547        if (
    +
     8362.  90547            left.id !== "(global)"
    +
     8363.  90545            && left.nr + 1 === right.nr
    +
     8364.  90545            && (
    +
     8365.  90545                left.line !== right.line
    +
     8366.  90545                || left.thru !== right.from
    +
     8367.  90545            )
    +
     8368.      5        ) {
    +
     8369.      5            warn(
    +
     8370.      5                "unexpected_space_a_b",
    +
     8371.      5                right,
    +
     8372.      5                artifact(left),
    +
     8373.      5                artifact(right)
    +
     8374.      5            );
    +
     8375.      5        }
    +
     8376.  90547    }
    +
     8377.    184
    +
     8378.  43806    function one_space() {
    +
     8379.  42022        if (left.line === right.line || !open) {
    +
     8380.  42022            if (left.thru + 1 !== right.from && nr_comments_skipped === 0) {
    +
     8381.  42022                warn(
    +
     8382.  42022                    "expected_space_a_b",
    +
     8383.  42022                    right,
    +
     8384.  42022                    artifact(left),
    +
     8385.  42022                    artifact(right)
    +
     8386.  42022                );
    +
     8387.  42022            }
    +
     8388.  42022        } else {
    +
     8389.   1784            if (right.from !== margin) {
    +
     8390.   1784                expected_at(margin);
    +
     8391.   1784            }
    +
     8392.   1784        }
    +
     8393.  43806    }
    +
     8394.    184
    +
     8395.   9025    function one_space_only() {
    +
     8396.      8        if (left.line !== right.line || left.thru + 1 !== right.from) {
    +
     8397.      8            warn("expected_space_a_b", right, artifact(left), artifact(right));
    +
     8398.      8        }
    +
     8399.   9025    }
    +
     8400.    184
    +
     8401.  23158    function pop() {
    +
     8402.  23158        const previous = function_stack.pop();
    +
     8403.  23158        closer = previous.closer;
    +
     8404.  23158        free = previous.free;
    +
     8405.  23158        margin = previous.margin;
    +
     8406.  23158        open = previous.open;
    +
     8407.  23158        opening = previous.opening;
    +
     8408.  23158    }
    +
     8409.    184
    +
     8410.  23158    function push() {
    +
     8411.  23158        function_stack.push({
    +
     8412.  23158            closer,
    +
     8413.  23158            free,
    +
     8414.  23158            margin,
    +
     8415.  23158            open,
    +
     8416.  23158            opening
    +
     8417.  23158        });
    +
     8418.  23158    }
    +
     8419.    184
    +
     8420.    184// uninitialized_and_unused();
    +
     8421.    184// Delve into the functions looking for variables that were not initialized
    +
     8422.    184// or used. If the file imports or exports, then its global object is also
    +
     8423.    184// delved.
    +
     8424.    184
    +
     8425.    154    if (state.mode_module === true || option_dict.node) {
    +
     8426.     46        delve(token_global);
    +
     8427.     46    }
    +
     8428.    184    catch_list.forEach(delve);
    +
     8429.    184    function_list.forEach(delve);
    +
     8430.    184
    +
     8431.      2    if (option_dict.white) {
    +
     8432.      2        return;
    +
     8433.    182    }
    +
     8434.    182
    +
     8435.    182// whitage();
    +
     8436.    182// Go through the token list, looking at usage of whitespace.
    +
     8437.    182
    +
     8438. 194783    token_list.forEach(function whitage(the_token) {
    +
     8439. 194783        right = the_token;
    +
     8440. 184620        if (right.id === "(comment)" || right.id === "(end)") {
    +
     8441.  10347            nr_comments_skipped += 1;
    +
     8442. 184436        } else {
    +
     8443. 184436
    +
     8444. 184436// If left is an opener and right is not the closer, then push the previous
    +
     8445. 184436// state. If the token following the opener is on the next line, then this is
    +
     8446. 184436// an open form. If the tokens are on the same line, then it is a closed form.
    +
     8447. 184436// Open form is more readable, with each item (statement, argument, parameter,
    +
     8448. 184436// etc) starting on its own line. Closed form is more compact. Statement blocks
    +
     8449. 184436// are always in open form.
    +
     8450. 184436
    +
     8451. 184436// The open and close pairs.
    +
     8452. 184436
    +
     8453. 184436            switch (left.id) {
    +
     8454. 184436            case "${":
    +
     8455. 184436            case "(":
    +
     8456. 184436            case "[":
    +
     8457. 184436            case "{":
    +
     8458. 184436
    +
     8459. 184436// test_cause:
    +
     8460. 184436// ["let aa=[];", "whitage", "opener", "", 0]
    +
     8461. 184436// ["let aa=`${0}`;", "whitage", "opener", "", 0]
    +
     8462. 184436// ["let aa=aa();", "whitage", "opener", "", 0]
    +
     8463. 184436// ["let aa={};", "whitage", "opener", "", 0]
    +
     8464. 184436
    +
     8465. 184436                test_cause("opener");
    +
     8466. 184436
    +
     8467. 184436// Probably deadcode.
    +
     8468. 184436// case "${}":
    +
     8469. 184436
    +
     8470. 184436                jslint_assert(
    +
     8471. 184436                    !(left.id + right.id === "${}"),
    +
     8472. 184436                    "Expected !(left.id + right.id === \"${}\")."
    +
     8473. 184436                );
    +
     8474. 184436                switch (left.id + right.id) {
    +
     8475. 184436                case "()":
    +
     8476. 184436                case "[]":
    +
     8477. 184436                case "{}":
    +
     8478. 184436
    +
     8479. 184436// If left and right are opener and closer, then the placement of right depends
    +
     8480. 184436// on the openness. Illegal pairs (like '{]') have already been detected.
    +
     8481. 184436
    +
     8482. 184436// test_cause:
    +
     8483. 184436// ["let aa=[];", "whitage", "opener_closer", "", 0]
    +
     8484. 184436// ["let aa=aa();", "whitage", "opener_closer", "", 0]
    +
     8485. 184436// ["let aa={};", "whitage", "opener_closer", "", 0]
    +
     8486. 184436
    +
     8487. 184436                    test_cause("opener_closer");
    +
     8488. 184436                    if (left.line === right.line) {
    +
     8489. 184436
    +
     8490. 184436// test_cause:
    +
     8491. 184436// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14]
    +
     8492. 184436
    +
     8493. 184436                        no_space();
    +
     8494. 184436                    } else {
    +
     8495. 184436
    +
     8496. 184436// test_cause:
    +
     8497. 184436// ["let aa = aa(\n );", "expected_at", "expected_a_at_b_c", "1", 2]
    +
     8498. 184436
    +
     8499. 184436                        at_margin(0);
    +
     8500. 184436                    }
    +
     8501. 184436                    break;
    +
     8502. 184436                default:
    +
     8503. 184436
    +
     8504. 184436// test_cause:
    +
     8505. 184436// ["let aa=(0);", "whitage", "opener_operand", "", 0]
    +
     8506. 184436// ["let aa=[0];", "whitage", "opener_operand", "", 0]
    +
     8507. 184436// ["let aa=`${0}`;", "whitage", "opener_operand", "", 0]
    +
     8508. 184436// ["let aa=aa(0);", "whitage", "opener_operand", "", 0]
    +
     8509. 184436// ["let aa={aa:0};", "whitage", "opener_operand", "", 0]
    +
     8510. 184436
    +
     8511. 184436                    test_cause("opener_operand");
    +
     8512. 184436                    opening = left.open || (left.line !== right.line);
    +
     8513. 184436                    push();
    +
     8514. 184436                    switch (left.id) {
    +
     8515. 184436                    case "${":
    +
     8516. 184436                        closer = "}";
    +
     8517. 184436                        break;
    +
     8518. 184436                    case "(":
    +
     8519. 184436                        closer = ")";
    +
     8520. 184436                        break;
    +
     8521. 184436                    case "[":
    +
     8522. 184436                        closer = "]";
    +
     8523. 184436                        break;
    +
     8524. 184436                    case "{":
    +
     8525. 184436                        closer = "}";
    +
     8526. 184436                        break;
    +
     8527. 184436                    }
    +
     8528. 184436                    if (opening) {
    +
     8529. 184436
    +
     8530. 184436// test_cause:
    +
     8531. 184436// ["function aa(){\nreturn;\n}", "whitage", "opening", "", 0]
    +
     8532. 184436// ["let aa=(\n0\n);", "whitage", "opening", "", 0]
    +
     8533. 184436// ["let aa=[\n0\n];", "whitage", "opening", "", 0]
    +
     8534. 184436// ["let aa=`${\n0\n}`;", "whitage", "opening", "", 0]
    +
     8535. 184436// ["let aa={\naa:0\n};", "whitage", "opening", "", 0]
    +
     8536. 184436
    +
     8537. 184436                        test_cause("opening");
    +
     8538. 184436                        free = closer === ")" && left.free;
    +
     8539. 184436                        open = true;
    +
     8540. 184436                        margin += mode_indent;
    +
     8541. 184436                        if (right.role === "label") {
    +
     8542. 184436                            if (right.from !== 0) {
    +
     8543. 184436
    +
     8544. 184436// test_cause:
    +
     8545. 184436// ["
    +
     8546. 184436// function aa() {
    +
     8547. 184436//  bb:
    +
     8548. 184436//     while (aa) {
    +
     8549. 184436//         if (aa) {
    +
     8550. 184436//             break bb;
    +
     8551. 184436//         }
    +
     8552. 184436//     }
    +
     8553. 184436// }
    +
     8554. 184436// ", "expected_at", "expected_a_at_b_c", "1", 2]
    +
     8555. 184436
    +
     8556. 184436                                expected_at(0);
    +
     8557. 184436                            }
    +
     8558. 184436                        } else if (right.switch) {
    +
     8559. 184436                            at_margin(-mode_indent);
    +
     8560. 184436                        } else {
    +
     8561. 184436                            at_margin(0);
    +
     8562. 184436                        }
    +
     8563. 184436                    } else {
    +
     8564. 184436                        if (right.statement || right.role === "label") {
    +
     8565. 184436
    +
     8566. 184436// test_cause:
    +
     8567. 184436// ["
    +
     8568. 184436// function aa() {bb:
    +
     8569. 184436//     while (aa) {
    +
     8570. 184436//         aa();
    +
     8571. 184436//     }
    +
     8572. 184436// }
    +
     8573. 184436// ", "whitage", "expected_line_break_a_b", "bb", 16]
    +
     8574. 184436
    +
     8575. 184436                            warn(
    +
     8576. 184436                                "expected_line_break_a_b",
    +
     8577. 184436                                right,
    +
     8578. 184436                                artifact(left),
    +
     8579. 184436                                artifact(right)
    +
     8580. 184436                            );
    +
     8581. 184436                        }
    +
     8582. 184436
    +
     8583. 184436// test_cause:
    +
     8584. 184436// ["let aa=(0);", "whitage", "not_free", "", 0]
    +
     8585. 184436// ["let aa=[0];", "whitage", "not_free", "", 0]
    +
     8586. 184436// ["let aa=`${0}`;", "whitage", "not_free", "", 0]
    +
     8587. 184436// ["let aa={aa:0};", "whitage", "not_free", "", 0]
    +
     8588. 184436
    +
     8589. 184436                        test_cause("not_free");
    +
     8590. 184436                        free = false;
    +
     8591. 184436                        open = false;
    +
     8592. 184436
    +
     8593. 184436// test_cause:
    +
     8594. 184436// ["let aa = ( 0 );", "no_space_only", "unexpected_space_a_b", "0", 12]
    +
     8595. 184436
    +
     8596. 184436                        no_space_only();
    +
     8597. 184436                    }
    +
     8598. 184436                }
    +
     8599. 184436                break;
    +
     8600. 184436            default:
    +
     8601. 184436                if (right.statement === true) {
    +
     8602. 184436                    if (left.id === "else") {
    +
     8603. 184436
    +
     8604. 184436// test_cause:
    +
     8605. 184436// ["
    +
     8606. 184436// let aa = 0;
    +
     8607. 184436// if (aa) {
    +
     8608. 184436//     aa();
    +
     8609. 184436// } else  if (aa) {
    +
     8610. 184436//     aa();
    +
     8611. 184436// }
    +
     8612. 184436// ", "one_space_only", "expected_space_a_b", "if", 9]
    +
     8613. 184436
    +
     8614. 184436                        one_space_only();
    +
     8615. 184436                    } else {
    +
     8616. 184436
    +
     8617. 184436// test_cause:
    +
     8618. 184436// [" let aa = 0;", "expected_at", "expected_a_at_b_c", "1", 2]
    +
     8619. 184436
    +
     8620. 184436                        at_margin(0);
    +
     8621. 184436                        open = false;
    +
     8622. 184436                    }
    +
     8623. 184436
    +
     8624. 184436// If right is a closer, then pop the previous state.
    +
     8625. 184436
    +
     8626. 184436                } else if (right.id === closer) {
    +
     8627. 184436                    pop();
    +
     8628. 184436                    if (opening && right.id !== ";") {
    +
     8629. 184436                        at_margin(0);
    +
     8630. 184436                    } else {
    +
     8631. 184436                        no_space_only();
    +
     8632. 184436                    }
    +
     8633. 184436                } else {
    +
     8634. 184436
    +
     8635. 184436// Left is not an opener, and right is not a closer.
    +
     8636. 184436// The nature of left and right will determine the space between them.
    +
     8637. 184436
    +
     8638. 184436// If left is ',' or ';' or right is a statement then if open,
    +
     8639. 184436// right must go at the margin, or if closed, a space between.
    +
     8640. 184436
    +
     8641. 184436                    if (right.switch) {
    +
     8642. 184436                        at_margin(-mode_indent);
    +
     8643. 184436                    } else if (right.role === "label") {
    +
     8644. 184436                        if (right.from !== 0) {
    +
     8645. 184436
    +
     8646. 184436// test_cause:
    +
     8647. 184436// ["
    +
     8648. 184436// function aa() {
    +
     8649. 184436//     aa();cc:
    +
     8650. 184436//     while (aa) {
    +
     8651. 184436//         if (aa) {
    +
     8652. 184436//             break cc;
    +
     8653. 184436//         }
    +
     8654. 184436//     }
    +
     8655. 184436// }
    +
     8656. 184436// ", "expected_at", "expected_a_at_b_c", "1", 10]
    +
     8657. 184436
    +
     8658. 184436                            expected_at(0);
    +
     8659. 184436                        }
    +
     8660. 184436                    } else if (left.id === ",") {
    +
     8661. 184436                        if (!open || (
    +
     8662. 184436                            (free || closer === "]")
    +
     8663. 184436                            && left.line === right.line
    +
     8664. 184436                        )) {
    +
     8665. 184436
    +
     8666. 184436// test_cause:
    +
     8667. 184436// ["let {aa,bb} = 0;", "one_space", "expected_space_a_b", "bb", 9]
    +
     8668. 184436
    +
     8669. 184436                            one_space();
    +
     8670. 184436                        } else {
    +
     8671. 184436
    +
     8672. 184436// test_cause:
    +
     8673. 184436// ["
    +
     8674. 184436// function aa() {
    +
     8675. 184436//     aa(
    +
     8676. 184436//         0,0
    +
     8677. 184436//     );
    +
     8678. 184436// }
    +
     8679. 184436// ", "expected_at", "expected_a_at_b_c", "9", 11]
    +
     8680. 184436
    +
     8681. 184436                            at_margin(0);
    +
     8682. 184436                        }
    +
     8683. 184436
    +
     8684. 184436// If right is a ternary operator, line it up on the margin.
    +
     8685. 184436
    +
     8686. 184436                    } else if (right.arity === "ternary") {
    +
     8687. 184436                        if (open) {
    +
     8688. 184436
    +
     8689. 184436// test_cause:
    +
     8690. 184436// ["
    +
     8691. 184436// let aa = (
    +
     8692. 184436//     aa
    +
     8693. 184436//     ? 0
    +
     8694. 184436// : 1
    +
     8695. 184436// );
    +
     8696. 184436// ", "expected_at", "expected_a_at_b_c", "5", 1]
    +
     8697. 184436
    +
     8698. 184436                            at_margin(0);
    +
     8699. 184436                        } else {
    +
     8700. 184436
    +
     8701. 184436// test_cause:
    +
     8702. 184436// ["let aa = (aa ? 0 : 1);", "whitage", "use_open", "?", 14]
    +
     8703. 184436
    +
     8704. 184436                            warn("use_open", right);
    +
     8705. 184436                        }
    +
     8706. 184436                    } else if (
    +
     8707. 184436                        right.arity === "binary"
    +
     8708. 184436                        && right.id === "("
    +
     8709. 184436                        && free
    +
     8710. 184436                    ) {
    +
     8711. 184436
    +
     8712. 184436// test_cause:
    +
     8713. 184436// ["let aa = aa(\naa ()\n);", "no_space", "unexpected_space_a_b", "(", 4]
    +
     8714. 184436
    +
     8715. 184436                        no_space();
    +
     8716. 184436                    } else if (
    +
     8717. 184436                        left.id === "."
    +
     8718. 184436                        || left.id === "?."
    +
     8719. 184436                        || left.id === "..."
    +
     8720. 184436                        || right.id === ","
    +
     8721. 184436                        || right.id === ";"
    +
     8722. 184436                        || right.id === ":"
    +
     8723. 184436                        || (
    +
     8724. 184436                            right.arity === "binary"
    +
     8725. 184436                            && (right.id === "(" || right.id === "[")
    +
     8726. 184436                        )
    +
     8727. 184436                        || (
    +
     8728. 184436                            right.arity === "function"
    +
     8729. 184436                            && left.id !== "function"
    +
     8730. 184436                        )
    +
     8731. 184436                        || (right.id === "." || right.id === "?.")
    +
     8732. 184436                    ) {
    +
     8733. 184436
    +
     8734. 184436// test_cause:
    +
     8735. 184436// ["let aa = 0 ;", "no_space_only", "unexpected_space_a_b", ";", 12]
    +
     8736. 184436// ["let aa = aa ?.aa;", "no_space_only", "unexpected_space_a_b", "?.", 13]
    +
     8737. 184436
    +
     8738. 184436                        no_space_only();
    +
     8739. 184436                    } else if (left.id === ";") {
    +
     8740. 184436
    +
     8741. 184436// test_cause:
    +
     8742. 184436// ["
    +
     8743. 184436// /*jslint for*/
    +
     8744. 184436// function aa() {
    +
     8745. 184436//     for (
    +
     8746. 184436//         aa();
    +
     8747. 184436// aa;
    +
     8748. 184436//         aa()
    +
     8749. 184436//     ) {
    +
     8750. 184436//         aa();
    +
     8751. 184436//     }
    +
     8752. 184436// }
    +
     8753. 184436// ", "expected_at", "expected_a_at_b_c", "9", 1]
    +
     8754. 184436
    +
     8755. 184436                        if (open) {
    +
     8756. 184436                            at_margin(0);
    +
     8757. 184436                        }
    +
     8758. 184436                    } else if (
    +
     8759. 184436                        left.arity === "ternary"
    +
     8760. 184436                        || left.id === "case"
    +
     8761. 184436                        || left.id === "catch"
    +
     8762. 184436                        || left.id === "else"
    +
     8763. 184436                        || left.id === "finally"
    +
     8764. 184436                        || left.id === "while"
    +
     8765. 184436                        || left.id === "await"
    +
     8766. 184436                        || right.id === "catch"
    +
     8767. 184436                        || right.id === "else"
    +
     8768. 184436                        || right.id === "finally"
    +
     8769. 184436                        || (right.id === "while" && !right.statement)
    +
     8770. 184436                        || (left.id === ")" && right.id === "{")
    +
     8771. 184436                    ) {
    +
     8772. 184436
    +
     8773. 184436// test_cause:
    +
     8774. 184436// ["
    +
     8775. 184436// function aa() {
    +
     8776. 184436//     do {
    +
     8777. 184436//         aa();
    +
     8778. 184436//     } while(aa());
    +
     8779. 184436// }
    +
     8780. 184436// ", "one_space_only", "expected_space_a_b", "(", 12]
    +
     8781. 184436
    +
     8782. 184436                        one_space_only();
    +
     8783. 184436                    } else if (
    +
     8784. 184436
    +
     8785. 184436// There is a space between left and right.
    +
     8786. 184436
    +
     8787. 184436                        spaceop[left.id] === true
    +
     8788. 184436                        || spaceop[right.id] === true
    +
     8789. 184436                        || (
    +
     8790. 184436                            left.arity === "binary"
    +
     8791. 184436                            && (left.id === "+" || left.id === "-")
    +
     8792. 184436                        )
    +
     8793. 184436                        || (
    +
     8794. 184436                            right.arity === "binary"
    +
     8795. 184436                            && (right.id === "+" || right.id === "-")
    +
     8796. 184436                        )
    +
     8797. 184436                        || left.id === "function"
    +
     8798. 184436                        || left.id === ":"
    +
     8799. 184436                        || (
    +
     8800. 184436                            (
    +
     8801. 184436                                left.identifier
    +
     8802. 184436                                || left.id === "(string)"
    +
     8803. 184436                                || left.id === "(number)"
    +
     8804. 184436                            )
    +
     8805. 184436                            && (
    +
     8806. 184436                                right.identifier
    +
     8807. 184436                                || right.id === "(string)"
    +
     8808. 184436                                || right.id === "(number)"
    +
     8809. 184436                            )
    +
     8810. 184436                        )
    +
     8811. 184436                        || (left.arity === "statement" && right.id !== ";")
    +
     8812. 184436                    ) {
    +
     8813. 184436
    +
     8814. 184436// test_cause:
    +
     8815. 184436// ["let aa=0;", "one_space", "expected_space_a_b", "0", 8]
    +
     8816. 184436// ["let aa={\naa:\n0\n};", "expected_at", "expected_a_at_b_c", "5", 1]
    +
     8817. 184436
    +
     8818. 184436                        one_space();
    +
     8819. 184436                    } else if (left.arity === "unary" && left.id !== "`") {
    +
     8820. 184436                        no_space_only();
    +
     8821. 184436                    }
    +
     8822. 184436                }
    +
     8823. 184436            }
    +
     8824. 184436            nr_comments_skipped = 0;
    +
     8825. 184436            delete left.calls;
    +
     8826. 184436            delete left.dead;
    +
     8827. 184436            delete left.free;
    +
     8828. 184436            delete left.init;
    +
     8829. 184436            delete left.open;
    +
     8830. 184436            delete left.used;
    +
     8831. 184436            left = right;
    +
     8832. 184436        }
    +
     8833. 194783    });
    +
     8834.    182}
    +
     8835.      1
    +
     8836.      5function jslint_report({
    +
     8837.      5    exports,
    +
     8838.      5    froms,
    +
     8839.      5    functions,
    +
     8840.      5    global,
    +
     8841.      5    json,
    +
     8842.      5    module,
    +
     8843.      5    property,
    +
     8844.      5    stop,
    +
     8845.      5    warnings
    +
     8846.      5}) {
    +
     8847.      5
    +
     8848.      5// This function will create human-readable, html-report
    +
     8849.      5// for warnings, properties, and functions from jslint-result-object.
    +
     8850.      5// Example usage:
    +
     8851.      5//  let result = jslint("console.log('hello world')");
    +
     8852.      5//  let html = jslint_report(result);
    +
     8853.      5
    +
     8854.      5    let html = "";
    +
     8855.      5    let length_80 = 1111;
    +
     8856.      5
    +
     8857.    317    function address(line = 1, column = 1) {
    +
     8858.    317
    +
     8859.    317// This function will create HTML address element from <line> and <column>
    +
     8860.    317
    +
     8861.    317        return `<address>${Number(line)}: ${Number(column)}</address>`;
    +
     8862.    317
    +
     8863.    317    }
    +
     8864.      5
    +
     8865.   2176    function detail(title, list) {
    +
     8866.   2176        return (
    +
     8867.   2176            (Array.isArray(list) && list.length > 0)
    +
     8868.    747            ? (
    +
     8869.    747
    +
     8870.    747// Google Lighthouse Accessibility - <dl>'s do not contain only properly-ordered
    +
     8871.    747// <dt> and <dd> groups, <script>, <template> or <div> elements.
    +
     8872.    747
    +
     8873.    747                "<dl>"
    +
     8874.    747                + "<dt>" + htmlEscape(title) + "</dt>"
    +
     8875.    747                + "<dd>" + list.join(", ") + "</dd>"
    +
     8876.    747                + "</dl>"
    +
     8877.    747            )
    +
     8878.   1429            : ""
    +
     8879.   2176        );
    +
     8880.   2176    }
    +
     8881.      5
    +
     8882.      5    html += "<div class=\"JSLINT_\" id=\"JSLINT_REPORT_HTML\">\n";
    +
     8883.      5    html += String(`
    +
     8884.      5<style class="JSLINT_REPORT_STYLE">
    +
     8885.      5/* jslint utility2:true */
    +
     8886.      5/*csslint box-model: false, ids:false */
    +
     8887.      5/*csslint ignore:start*/
    +
     8888.      5@font-face {
    +
     8889.      5    font-display: swap;
    +
     8890.      5    font-family: "Daley";
    +
     8891.      5    src: url(
    +
     8892.      5"data:font/woff2;base64,d09GMgABAAAAABy4AA4AAAAAThwAABxiAAEAAAAAAAAAAAAA\
    +
     8893.      5AAAAAAAAAAAAAAAABmAAgiQINAmcDBEICuc41DEBNgIkA4R2C4I+AAQgBYkuByAMgScfYUIF\
    +
     8894.      57NgjsHGAbcDVFkXZ5Jwd+P96IGPc9rl9ETBEaCzCJkvY2UpziRZ7zftZWk8052U9+NqX6vXL\
    +
     8895.      5KDflSQnlJ0bP+QnPQAy744n9mup6H9PaCDFwM5zjf8exB89bZ1cdrYOP0NgnuRDRWlk9u/fE\
    +
     8896.      5llkxqmfH8lmRQ/DAmER9opk9wR6suc1LvTiXNEe1vbhUCH2USgnEwH3vUm05JQqejGvZvOtz\
    +
     8897.      57sIKEGgLdDNl/IrfqWVZG/wr42ekomEm91VA1p4LhHBuFzHF8//u7vvbREHMQqGtNLmiOOD/\
    +
     8898.      5X7WWiwqyCE98qt0jk5JJmgR5WJJElBmzRb1F7a66MmSLTNWZ2XSHfKBSKHoVteSEJ6EOdvVw\
    +
     8899.      5fNZOtXKDe39jXdRlkmMnOWIOFBgeEK/b0mFsgffnPyyAitNyutKky7J8a8MSEkAKGLgfptnS\
    +
     8900.      5/gDRSo7vwdNUmQDB7oP6pK7QF5d9SrY8M/tkrXcurSIQAmX7tz7pd33LIB7GQkBQ/k81s/0D\
    +
     8901.      5gpt4gbw7x0Cn/PocitK5KIGPGQIzQzAMuCeC2ERAidx9TySVqX06goT0SFFOOV9Kuxdi5Rg7\
    +
     8902.      5l6n3c+nKRemidOm2dtFV1jXMk4rP2m6RJ8xEdPYONLTbeMgaJ1nwS2W4su3MHwqkkvJ2PdDU\
    +
     8903.      5r7pgAnVRt4Kh789FXlD0r3p6jUtNO19O1s74U9pnIxqFpw+mBgF+8y30PAyw1dzlknLLVcSB\
    +
     8904.      5J2OuCr9eV5Efew6cOGd47ZEfhrW7HXI+FBNFvWgWnugUU4UvlrV63niv2ZPeKu8M76y/HQaG\
    +
     8905.      5weU+4Gzp+Y+cfb9R9djDWcd1Svr1xG7l+j/yf3eM996548qlC+dOzOqQ8//Lo0uaSEQCFuLD\
    +
     8906.      5/bXyWhJ6aPmyaRonVPxGABFL4/0slcKI6f+PmT0M+QRsplmWnv4F49VT+JsPifoa6aeyr2Hz\
    +
     8907.      5EeLdP1FEOV/ZN+c9sAuoNh0BRS0xgCCc9wME5s0HOKj/wc0fWYsTbFQpsZL5SayJPkL45kDo\
    +
     8908.      5DcJJ10MvD0ZSq7FEIr1TfqZ7NC6s75zSp8viaNO5/PczYCV9z6NTa0KBdnGBg6kbdeBkRLfU\
    +
     8909.      5qRd3D9Pqw5jWCc5WM/i95OE8731MBd1u2EmsXIa5dCvavY32U1Ytza4nfbERg6OVRZka7jq0\
    +
     8910.      5r2FcXNDyEhXheaHtaU1o1kvO9MuBOHqugLUEzN+4jznu0oK9wZPur1lWVFfxl8lZzn2XwcjZ\
    +
     8911.      5Csg/RJy0mAMMmgnqXS8ELhOCRUSLzvsM5gAPudEh2lVoRxGgyUVnArZMruE0YS1PqFMD3upb\
    +
     8912.      5jVoecGj1KpWl6/ZysuyzkG4SGA4bps6FBQSg4e4IxNUgdmosmoDn0TpIex/s1BFau6GBNO4z\
    +
     8913.      5cvWXypm4hEg5k3llelySFqNmUtRZ3PHBA7p4MBX1nK4awwAV6kWzIVbUA67A55QKYbMsgVaH\
    +
     8914.      5c1ZxKuZ0DCyqxCsJjLyCEY36gf0wjAu3t0zemc87PmBCJbU9Lso0YAaYJUx8wsR02hYz5hGy\
    +
     8915.      5Js0+A4uHGZgfuf5SOR9iBQuLhpOExaIFrHj6JlXanebzGHp2ELDh6av09PVE1fmdsj2oHRWs\
    +
     8916.      5fOtYrV6wRCyx7XogHqvpnZiPBBdNcL6kIoS9UI/DOIlumlveSgv9oqMBYp7WZ2fGxAXmZmaG\
    +
     8917.      5OCyJG6+wAszZFCQw/EXVjx+YA2uVyN6bhNWiZhgtYjAwR5U/7uV1scghiTGiAPZbA5ZqHw5u\
    +
     8918.      5Yu1cDjhRwREBFyq2wa0R8GgceDUKPo2BX+MhoAkQ1EQIaZqVHMwH3xM+P32TTA34tmOMNZ4n\
    +
     8919.      5mHXqn49fmE3qX1+wMNYoYetOsPx6wxKzkURImERJIjGSSJwkkiCJJEkiKZJImiSSIYlkSYqK\
    +
     8920.      5UBu0UOopuLMmasiJW0PMFOO2UgbDif2NaQUqkBbyaGjdTUvuyamEQwCq9DWsxsG9qPt+VFqV\
    +
     8921.      56cIsXcyWujWIEtNFdeia9ssNrJUpe3IDMPQZOReC8x+qvt17drPWdcHeL0gTarWwoQ6o828o\
    +
     8922.      50EJzrA20yZsgVyVHdlCJOF3NaACxHbP38TA+MGx3St9c5t2CxbGtunB4J9AF4Px2rSr1wyK9\
    +
     8923.      59KoXBR13vw9Fk9qhTX0ivZoanrvhLa5oiJO8cqR0lX7QtJ2c1a62V3PMtutaaoit+hxtXuC5\
    +
     8924.      5ZUXJePSR6btQlt5g7PqPQ822g7F8D123pc4kaGXz7qYztJxDXCxJr7foKqxwy4rikI/NvINx\
    +
     8925.      5bkArRTTnnMWy6YA8J39LfTweThKsqlt7Mz078NDSOPOGgtGTpeG8ZRBF+xKBjdSoNe8gE6uC\
    +
     8926.      5ucOH98jE4+cv1JEjI555TFjYj4+0KdFlojzJGWp2wc1tCaYGSeO8dBfT0u3lpDY3tazzu4wn\
    +
     8927.      5lF9wzy2nK+sTr/qEVdANoZ0ToBdD+MY4ewOHNnkXPBvKVXLSbEGfGVD0Nzr0Fs3HID3Y1Kqx\
    +
     8928.      5mzJ6p1C1/R6Xneyw/q9YRDLahbnsI1u76XzMLPqsK0yvQDeQ4TMR41709sIssmEgs0XH1lcj\
    +
     8929.      57HLnUG6u2Xpy5vbOowIGqrR6cwF0TLGI5PF7pkbzIVYQU0sIaoNgul3LGAH2B1nREFYXUMia\
    +
     8930.      5prCeAzggGxrC5gIK2dK0exs/AIRKdlIIuxkUspdSsU+rqXagqXaooXakqTiWS/a0E7zA6QIK\
    +
     8931.      5OdMUznMAh+RCQ7hcQCFXmspr3ciuds/6gPsZFPIgpfJhwUIepRAeZ1DIk5Tue4oKfSfKZyNV\
    +
     8932.      5pKU/J7J4Abx1EMV5mXSRDl6lMfU6jfBmBww4k7f6gLzTB+J9od/kA/uGj2mET2nkn7+zQ/JF\
    +
     8933.      5H5Kv+pB804fkOyvwI43wM438V5sdkd/6iPzRR+SvPiL/WIH/aYRxGqMb/Oqe3d54+LWR1vr2\
    +
     8934.      5knnnc467iD247eXBA3YYBAiFfierClXz/8jyL3Qh/zP8y+Y/1eN8jq+SKZAML/lIidjwZ8N4\
    +
     8935.      5aLthvhxGUkGPo+p0eHKZ0sT5FsqJcQCy9UhHIvcJFIlIvANTPFWUTUhSiVdsNRnvwEQxm5uc\
    +
     8936.      5ksjdv5evJfpOgI6c7juH8pnG2RKwlXaDYe9g8rMwYfML3A2SMWeBDopJJsmS5dUE2KttnmQa\
    +
     8937.      5JZlMspvEpJioiEDFNpPUTbwqG3Zjhx2VCeJrIf60s2mI6blZMZVyAyYzI+1a2Y0AIqcbLUgR\
    +
     8938.      56iRbNtnp82GrImXW0YbcbczDgqQDWNdTenvtTAlT9iPHenluV+d3eed1/5MjMBrX2LgrK2ml\
    +
     8939.      5FuoDOz036n/kaHbAeszR3jHoI4NWB3lusTfuVgkMUkLQaH0F6+pSCS11fXRwT421vs9s7axd\
    +
     8940.      5nvtF7/eeIeq9s1aCLsLWdh+w7sXz3IYdEsSQ0LVsebmES/vXDU9k653W4MiNq8bMj5nLioCY\
    +
     8941.      5edGgOT6tmYwqiOW1ugiEmew6iwjvvYb3SaeZJb7XNufOo9oH8FTneWGL+BLiclptpnhPwcui\
    +
     8942.      5T+rzcF34+ycsL7p3AveuML9i9h13beylyg8CzEz5HppadqmmDxKrAquG9L3ztedRoWxEsAYt\
    +
     8943.      5OM1Eu0G0gyTHkxf7cSkHJQRbA4xmlqHWkv1C0KhFhBq1z81Wq1CZoWic8TJ570WfSj5qsM+Q\
    +
     8944.      5nl4k3H5+P+P3zlv9ltQrzv41qyiSwV/gOadyQBchsmwDGu/JI8tXflE8jqUVA0Zw0SKbdDC9\
    +
     8945.      5c4FR+fak95SdF7uqpoRe9z6YRv+85YUzF4qJy6Q8GOVNwUn/ymyjNNbmcuVfXYeH2osLdCte\
    +
     8946.      5ebmZRyUfQQZA1BSCLK4PWA/z1kBvDZm0t+i3or1LkMD6en95pGG0UOa8ZJXgS9TdEA1I2mZw\
    +
     8947.      51JOWWxDu0NEh4rM19H55rvueMBUZV1RjkmB3oxkXhAckpa5gzzxUDA2VLOrWFAXx+4gmfU17\
    +
     8948.      55o3v9H7EYdvGFuM+tDB3TA4ITjVUKduO/R4bXRAcPXZusWkN+t59sFz7Hyi0FkSdzrHXQVFq\
    +
     8949.      5b8c9k9eLRjVlBbNvt4172CanYg/F3Rket1zCTc77UZ61Gq/Be9J8hrKrxbDZMEotf5o8zHDc\
    +
     8950.      5/UJaEtdhgwHEcBEQKM+6NBWIewLmI1sHuWYAedZCw8U1hJfSWcld+2tv3jpCFc5FnosLWC0+\
    +
     8951.      5DnAlnOXUXLoMXrmCVerNQkZHvRm8YtE12vG8+N/vOnPcu3vM1uOnzE3u3VP2ppmLZawm2NuO\
    +
     8952.      5tPa7xwHFCgVKpox5PVrOmaDHrThk1tX864a2+/qhJd3nCFRQ+bfUKI4O+Wgk5byB3saMcUfV\
    +
     8953.      5C8G137yMd16zRm3ZSq+UrDlk5ha3TiAj0b74prWO/vYG+RC+ronP1/McDtefBtY1XhZE0PIB\
    +
     8954.      5wTe7CBTte2U6KPbYd5GffApQlDGssdfmxYGSlnHrQt7++KEwUg3ikkoQyKPixgUDB6Lozjv5\
    +
     8955.      5vM5PBnllt+UzMnP6DStFsOfossbXOefWhQApACCNpkTYGAONIowDfndqDKRFuzn685nthZPe\
    +
     8956.      5vEL7TIWkXAG2yxKBH90+yMzuRzWn3KMmyKGwZWnIErlJ9Vwt8OtR6+4TKad5y9+ViBtTzVG+\
    +
     8957.      5tpv/xiLrcGKJRtYvCUlGeL4Dwy1jo1CSQe0X71EXK1YG44ztxTONjIslL8SwY0Cki0k0vsX/\
    +
     8958.      5/xz7CxkAc9dEdJZhMy/JCGzD2FAGtUcag0tc2e2miJkp477V2qTKB+nFnDl/noxpXJ+yqVdO\
    +
     8959.      5wNjbplmeiuburg9ii1Z1zwtG8QjcJAiVPSOV2mHzq1Qt7p2+YCcIKPmFusE5O+m8s+Wd8o3t\
    +
     8960.      5qO1b1IZF8N0tx6RQnZ9Ux3gXijHlolixst6vhJV6ao0ZFzSprfAc3x0MLvxU0OsmXEVddMVK\
    +
     8961.      529CC6mPgPtXTUW7tVnZxwm0DTJwNOeVRV4axMSPlpgyv1Va1MQhQqWwUOb0s+gVLOecos4Nf\
    +
     8962.      5eqlFW3fLQrlP86R4XRxrDHF0VIx6ArM5/sTWtObY6U2aosgxbN6FUa1iNTUpMThk1sUfJOC6\
    +
     8963.      5s1SKo9D0g1NfiVmavyful/K7nZdDgutV1A26i7FR3r16bv3zz1cGw+ta17IX/+ripyutix3C\
    +
     8964.      5xNmCxs7uiqKu9/Zjjn06tblXpJxlaLF5Od0d5W9QhQrs2u6UN0trQlCyEK2j9VYgCEIDrhQN\
    +
     8965.      5c00rxg/FOfZ1N+nLV7RXDsYP+p0EzqKcuPujzuzEQsu2mFf4nYvf3Yp32rq/RYLetDLuOOTc\
    +
     8966.      50WXBtgoech7AHUxAxPBg81qWCsYlzTofRU5/MpuyNoegR6mCJO5ckrLOhWbG7xo/VGwGgpRb\
    +
     8967.      5+Ch+TmlcuY6Qct/2x3gxzeDUU9u+ltexrjelJ0VRR9KXH/AqrbYxHa0vmQ/kBnE5EORBK1ZH\
    +
     8968.      5mTSy7A8DJMgzzqDsu9ML5J3ufkuUNDCfN5UKAjBgw2I/QlS8MQ6o/ll9dTAdoM7HYtV4cNWE\
    +
     8969.      5U4pOl5Y4SIzdMbNSjXFmsBV1uXXf7GaBZZslpFGFiIpokSzxWj4hjlGl4VKJDACo7ScxQf29\
    +
     8970.      5kM8gHD3nUJkwkN2aW2TGttqwOrygJ7r9nYX2tYqy7Z3TQV5ocWzUI8l871y3LsQLoTgEO76B\
    +
     8971.      5Upp69hy6VKRpZvpvgfQ2T06qgXjxh38eatREitX6bzKggIYmN4sAkA3a5oeJZDK3ahQrVJwa\
    +
     8972.      5AD65cEGBkS/tKH9TtybiREEWCMcKD0HH0gELtjB+KNSk7bspmpr6eb0CscIiFyZpmXu8+gxw\
    +
     8973.      5O7pJNbAK2h9q2c5dMHBaoi5DylbNGdweVVdN3Jm9u6YXXlmx4nYY2vIPfSkrE/vyv9gn/Z+j\
    +
     8974.      5R3HKExaUhdV0Az77YWbQPhNfjw+F0vTteSMin+wIfxyPe0DEoI4uz6o2IXwsZC7sg8MicQ3o\
    +
     8975.      5wys+NJYKVW72YiVQ5LKDVwrEg2jNVM6XdNjbsHlRDcAkD08o5iWtFB2dVoydRmmDRLalE+4t\
    +
     8976.      53gBbAPa7n7qXXXbTZTJXZKy5+1W0K7dgYEcIlu90ovC0C+5gxXiKtZisT14qDJ7f2ksyK59U\
    +
     8977.      5r3QeHtBb24mPz7YDB3rgMTyUZ/fxM8h2i1Z21B8/VA5+9l7BKaOJZ15lWsyPv/z6CjU32ZKq\
    +
     8978.      5+QFeyUywxYnUxUmcQfGc1Sp69oE2n6zFL8BXf5rc3cJMM6S97gagTT1bi7cmAV4MibkC4rz/\
    +
     8979.      5icmmFtMlo5aN1Wp3uxsBfd4+9T42xmxvd79FV/hfuviBcrIaX092PrY5rle9FR4wTnDzrwj4\
    +
     8980.      57frD2d0KsMcdcADQ1Yu1LECg9Wj3yOS8OhrJdQBqXqsam17vmt2wjjjouHE/EO9sGPdqt23v\
    +
     8981.      5j8rL6wid6ulagtNK5p1hjRkFtUxTIaZnIXk63Zb3P0t5MQ+3vxHIFrmgAdWwiDuA67tbVIF6\
    +
     8982.      5wJ53z0uhyhsfH9bgF0kPT9v2hrT3HKIBgUXIYoxsVU+uryemiUiQEwh+BfxP//qLShlumR26\
    +
     8983.      5I8OqjD+x3hHDj/IrEWmvyL6ioG/atfxe+5GzIqRgfaoayWOiTk+YixO15KDO6Os3XACDjboe\
    +
     8984.      5ryXXOuEmTpDsc7czk+H04Kw1PNJazW32CAURHwBldqK0/nqYHtcrtLyyTYmoD8hbcnJUfa3U\
    +
     8985.      53FxWNus7uic3Qm1BzEecJW0MAz+W2CyN9FLIy+EpSy6CjkXsllZw1uBs1SxrQWM97/vnHu7m\
    +
     8986.      5OtrkRl8AtBN3RDxI/fg7dZLLtDFYuCYYPMwXiO6ZIpwJ1GGydI9oUYYgnQQKDKoMTcwsjrfe\
    +
     8987.      5Tcht6y18bLcpNfX41WE27447vLNzHuF+j15co5N7Py8vKUpTCoghHMEYKkM6y02lvX+9XiFg\
    +
     8988.      5xBKMRNiwX69+LJb2Xa5WGqo7Rlk0cxsLVd0l2UXAW5jORg31sFMKYWXsDcRUKRDP8Q87OjiM\
    +
     8989.      5dI1hNEt43netf8rOyfp+L58fq3holY9gxXwRJLY6gahgLQi4hS8w9LS+rFcJtdSCBrQLWsMs\
    +
     8990.      5aDg/n8/P8/N+fcyoLepYr3W/CIUT7HsRQTtkduddbVfbo6Twt6fyJVPRrUGqRkWp3rdry65v\
    +
     8991.      5sPYInyq1mPHrQDrqGJYI/LzA/QAzAXLnx+lu9uxHTEka9xgWgRvqEioskh+UWgD4nDvTAxaz\
    +
     8992.      53v9BqqmFuQwy1wSXye1Df1NXVF7G8bUFxUE4F9axG5fm+vFQJvP8iuYjrFveB6++AqmJTQJ0\
    +
     8993.      59GHjbPhzdSzkZGxokQzONVs0R0FCPJz1hJKbvDKsaj9hT0vp/gH5oiT8pAbWsBChwAbxHgDd\
    +
     8994.      559iJVZE3bAzPRN1RuG+MT7th+J3i6KFwVJvPvsGRDIZW4P2rVfiKjDVBM2Va+w6PgI0c5u3K\
    +
     8995.      5O7MrWryPhXFFdBoAwi2JCaW9sZ3fTagQ4Tld6u4djwcWzeCdiYoeNbfalsRYo740afYQ1Rid\
    +
     8996.      5Bp/E9mbcTemEjoWWXIU7I5nK5H/BEqmZnPMyhDV234BTLQKCe6nhU+frwQo1gNFWf+eQGN62\
    +
     8997.      5aeF7BuzaN/94W2xlKd8t8KMA/3uoxymFt19OlCjYZeaMWbTKM9Yog9zDhptYMOzIQAoO7kn6\
    +
     8998.      5nTao8CxjrRRgjKe7mKa+tzuufhAAZtgjA92THkulWvIzEi0++j1DvXMnupDUS8aVusWain+c\
    +
     8999.      5CcvmR5orC+RcJs3wVahLYyEcqbvAS2e0QJ6BlU36R/IEd9Aol9q+M+UGvlo8EyRzISvqusNS\
    +
     9000.      57ePQ6cQzG1s725db4jNYNHAfF3KFG8wHqDwZDpWDsJ5qRLXR1ulFx85GhkypPubYaCiOQ5DR\
    +
     9001.      5PQUiNpgk4fLJHenSMLMiswXsqW4Cpln1rFoHzpOoBbuZIixmVyhKajeqlFmp8zNAEsbEJz0g\
    +
     9002.      5X0qlQuykZhf82pkhq2hWtCtYUdBODn6iPTBJT5Zk8IqFxqfBeFKjXk/sMeumhT8muOtq2Bgn\
    +
     9003.      5dR4fj6RoOi0zI25kajAXlDZhUhS39jipk39h/69AfDPBLmOxhDj7Lg/WUTbOwJiJ3p7WtOpm\
    +
     9004.      5ypARmhorQifINNm1CNS99GfDcLbD8sn8Fvlmvn7CmW65Pdmu6bKtuE0tn7NglIX1e/JAJP+G\
    +
     9005.      5gB3At7cSOp92rl0lp0pp0xVb5YaQedwGgcJA1pT4cy24lS+jvzDw86YTfb2igJm5MysHmejW\
    +
     9006.      5ZTGXpoAoLBLucUGEz/DwbjqOdzGAl5jy5VoCQws5zNYl4SVt030aZulYMgpDBPZd+kL0wV+w\
    +
     9007.      5nob2LPRDQGEbdUoeFm4fEKio9c/ferVlpSO8Bx7OFZyHip1PIizvoqFe02kpmS17TvIOty42\
    +
     9008.      5+Q0QaCnOpeLsPwwo+vixIeIeUjucUsKejVlez35qyuC0mm5pJJJLEVP2JAe/LTOwUUfKJkNy\
    +
     9009.      5lEe3Kdth241ZNQmkVcAIh6DZJBzvQov5fn3JZA0phBWdNq5iTsm5N2D8gyve3V3X2o3zF3VY\
    +
     9010.      5OqEBgTIADAbC69z7vOKJjGOzHRmUUwLU66iabtIbqR6SPOHCL+fCTfvpKcB/oG2p3wRKErEJ\
    +
     9011.      5v1YOfu9iaKEMLXS3ptdH8fwN2Rdww9bZ7rFa2bwrzcyux3o+hPV6bJZpb71j7lLAdzge3VX7\
    +
     9012.      59uSCdz6f/FDb7+wzWnbbDSPj9R20+PybDUm/lVAsTuF0aycFQwJfPCUwcBvCGWEq6xoTIEOy\
    +
     9013.      5b0bLta20+LYRYdyEceX7ypfezQKIy5OvJTAHCJy/WyOYaDVyPucMsHnZ0GCH75Cd//te1Bv2\
    +
     9014.      5RkMykqYurBiNbuH3Xfuprirr4Dd453O6abAYGb5tw1d6wrBL8p1J1Sx9Lgw7yxqYn0FTrc0y\
    +
     9015.      559yLlV+4zIkLfZlPFRVnanHpTyrIlpn4lGkt269+JXnIWhEQWNvuVsrt531jr+8AHkVZfQU8\
    +
     9016.      58U/4AUZMuOj5iliigFrof/usmloYEI1f8erhJku75snYW7YmFmUcoC7UtG/KfJRuz6j0tWPa\
    +
     9017.      556J5QA0rJHwSIhNT4GWvez19HT2lia+Pz7/+MVEWlvjY6+9P85a0y9qWkTzQ7nF0wDXpQpw3\
    +
     9018.      5K4xnfK2L08b5MrxdeI+DDfVDeV2JY0Fp6KH602tj2MbxxKM8oG+wTkE/dr8jyo4Sfs/IV6uf\
    +
     9019.      5+IIXpH2Nca1+WCJV5qEv193bcUELLR4iFu83xUedKy9353tn+3o01dF2bNEQ3fK9Q5tCXrCi\
    +
     9020.      5La+woCuvEeYrr+PiN2+i2V/eDJck580pyra8BV5ZIZbpe3kr5vJD3pqoGsnbcl/d+ndvR23b\
    +
     9021.      5K41M4dKwaAwDaMA1gZGBoQWAcYE9mYkmQOnAjkaG41FkGkIP2BAIgKvUvzhpE5JbA6lze2iL\
    +
     9022.      55Nr+AwiDo2W4BStvK30dKy0JGNbzAY5akexsrV0xo5K8rY50LOTLvDyukIZNbRLKOCk18mD3\
    +
     9023.      5WxmZGlsCMxNdGFYGNJnetUWyCPgo4BONEL4I9b8UeEBGYXuCdCd+DkctrqVLYXGSfE46kvAu\
    +
     9024.      5+ltK5SRxQPnjUqyJXsSYs6VY6WPKfns9+IXjHhd5wQvGipgFdMwVEQ+A7a2AAS0clQwH7KHW\
    +
     9025.      5SEGjhnklSVRghMtPy6gEtJRIKJeYkpQyQiequQunFOOU4BLdK1yp55olZS6npyPhMnvK7xIa\
    +
     9026.      5pyNj+JctcQLXenBOCms46aMkenIx45WpXqxxVJQLz/vgpmAVa0fmDv6Pue9xVTBPfVxCUGfj\
    +
     9027.      51R8uVi8Zu9nRFqk/t0gR6wmWOlzuKRqk33HpO8qQ+nbGoEZLL/0Va156SJ+u+t86/os7ic49\
    +
     9028.      5/7xoEqvL+2E8VOyCTuT/7j269Zy4jUtN+g4="
    +
     9029.      5    ) format("woff2");
    +
     9030.      5}
    +
     9031.      5*,
    +
     9032.      5*:after,
    +
     9033.      5*:before {
    +
     9034.      5    border: 0;
    +
     9035.      5    box-sizing: border-box;
    +
     9036.      5    margin: 0;
    +
     9037.      5    padding: 0;
    +
     9038.      5}
    +
     9039.      5.JSLINT_ {
    +
     9040.      5    -ms-text-size-adjust: none;
    +
     9041.      5    -webkit-text-size-adjust: none;
    +
     9042.      5    text-size-adjust: none;
    +
     9043.      5}
    +
     9044.      5/*csslint ignore:end*/
    +
     9045.      5
    +
     9046.      5/* css - jslint_report - font */
    +
     9047.      5.JSLINT_,
    +
     9048.      5.JSLINT_ fieldset legend,
    +
     9049.      5.JSLINT_ .center {
    +
     9050.      5    font-family: daley, sans-serif;
    +
     9051.      5    font-size: 14px;
    +
     9052.      5}
    +
     9053.      5.JSLINT_ fieldset textarea,
    +
     9054.      5.JSLINT_ #JSLINT_REPORT_FUNCTIONS dt,
    +
     9055.      5.JSLINT_ #JSLINT_REPORT_WARNINGS samp {
    +
     9056.      5    font-size: 12px;
    +
     9057.      5}
    +
     9058.      5.JSLINT_ fieldset textarea,
    +
     9059.      5.JSLINT_ #JSLINT_REPORT_FUNCTIONS > div {
    +
     9060.      5    font-family: monospace;
    +
     9061.      5}
    +
     9062.      5.JSLINT_ fieldset > div {
    +
     9063.      5    font-family: sans-serif;
    +
     9064.      5}
    +
     9065.      5.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dfn {
    +
     9066.      5    font-style: normal;
    +
     9067.      5    font-weight: bold;
    +
     9068.      5}
    +
     9069.      5.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dt {
    +
     9070.      5    font-style: italic;
    +
     9071.      5}
    +
     9072.      5.JSLINT_ #JSLINT_REPORT_TITLE {
    +
     9073.      5    font-size: 32px;
    +
     9074.      5}
    +
     9075.      5
    +
     9076.      5/* css - jslint_report - general */
    +
     9077.      5body {
    +
     9078.      5    background: antiquewhite;
    +
     9079.      5}
    +
     9080.      5.JSLINT_ fieldset {
    +
     9081.      5    background: gainsboro;
    +
     9082.      5    clear: both;
    +
     9083.      5    margin: 16px 40px;
    +
     9084.      5    width: auto;
    +
     9085.      5}
    +
     9086.      5.JSLINT_ fieldset address {
    +
     9087.      5    float: right;
    +
     9088.      5}
    +
     9089.      5.JSLINT_ fieldset legend,
    +
     9090.      5.JSLINT_ .center {
    +
     9091.      5    text-align: center;
    +
     9092.      5}
    +
     9093.      5.JSLINT_ fieldset legend {
    +
     9094.      5    background: darkslategray;
    +
     9095.      5    color: white;
    +
     9096.      5    padding: 4px 0;
    +
     9097.      5    width: 100%;
    +
     9098.      5}
    +
     9099.      5.JSLINT_ fieldset textarea {
    +
     9100.      5    padding: 4px;
    +
     9101.      5    resize: none;
    +
     9102.      5    white-space: pre;
    +
     9103.      5    width: 100%;
    +
     9104.      5}
    +
     9105.      5.JSLINT_ fieldset textarea::selection {
    +
     9106.      5    background: wheat;
    +
     9107.      5}
    +
     9108.      5.JSLINT_ fieldset > div {
    +
     9109.      5    padding: 16px;
    +
     9110.      5    width: 100%;
    +
     9111.      5    word-wrap: break-word;
    +
     9112.      5}
    +
     9113.      5.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level {
    +
     9114.      5    background: cornsilk;
    +
     9115.      5    padding: 8px 16px;
    +
     9116.      5}
    +
     9117.      5.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dd {
    +
     9118.      5    line-height: 20px;
    +
     9119.      5    padding-left: 120px;
    +
     9120.      5}
    +
     9121.      5.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dfn {
    +
     9122.      5    display: block;
    +
     9123.      5    line-height: 20px;
    +
     9124.      5}
    +
     9125.      5.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dl {
    +
     9126.      5    position: relative
    +
     9127.      5}
    +
     9128.      5.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level dt {
    +
     9129.      5    line-height: 20px;
    +
     9130.      5    position: absolute;
    +
     9131.      5    text-align: right;
    +
     9132.      5    width: 100px;
    +
     9133.      5}
    +
     9134.      5.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level0 {
    +
     9135.      5    background: white;
    +
     9136.      5}
    +
     9137.      5.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level1 {
    +
     9138.      5    /* yellow */
    +
     9139.      5    background: #ffffe0;
    +
     9140.      5    margin-left: 16px;
    +
     9141.      5}
    +
     9142.      5.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level2 {
    +
     9143.      5    /* green */
    +
     9144.      5    background: #e0ffe0;
    +
     9145.      5    margin-left: 32px;
    +
     9146.      5}
    +
     9147.      5.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level3 {
    +
     9148.      5    /* blue */
    +
     9149.      5    background: #D0D0ff;
    +
     9150.      5    margin-left: 48px;
    +
     9151.      5}
    +
     9152.      5.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level4 {
    +
     9153.      5    /* purple */
    +
     9154.      5    background: #ffe0ff;
    +
     9155.      5    margin-left: 64px;
    +
     9156.      5}
    +
     9157.      5.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level5 {
    +
     9158.      5    /* red */
    +
     9159.      5    background: #ffe0e0;
    +
     9160.      5    margin-left: 80px;
    +
     9161.      5}
    +
     9162.      5.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level6 {
    +
     9163.      5    /* orange */
    +
     9164.      5    background: #ffe390;
    +
     9165.      5    margin-left: 96px;
    +
     9166.      5}
    +
     9167.      5.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level7 {
    +
     9168.      5    /* gray */
    +
     9169.      5    background: #e0e0e0;
    +
     9170.      5    margin-left: 112px;
    +
     9171.      5}
    +
     9172.      5.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level8 {
    +
     9173.      5    margin-left: 128px;
    +
     9174.      5}
    +
     9175.      5.JSLINT_ #JSLINT_REPORT_FUNCTIONS .level9 {
    +
     9176.      5    margin-left: 144px;
    +
     9177.      5}
    +
     9178.      5.JSLINT_ #JSLINT_REPORT_PROPERTIES {
    +
     9179.      5    background: transparent;
    +
     9180.      5}
    +
     9181.      5.JSLINT_ #JSLINT_REPORT_PROPERTIES textarea {
    +
     9182.      5    background: honeydew;
    +
     9183.      5    height: 100px;
    +
     9184.      5}
    +
     9185.      5.JSLINT_ #JSLINT_REPORT_TITLE {
    +
     9186.      5    color: darkslategray;
    +
     9187.      5    padding-top: 16px;
    +
     9188.      5}
    +
     9189.      5.JSLINT_ #JSLINT_REPORT_WARNINGS cite {
    +
     9190.      5    display: block;
    +
     9191.      5    margin: 16px 0 4px 0;
    +
     9192.      5    overflow-x: hidden;
    +
     9193.      5    white-space: pre-line;
    +
     9194.      5}
    +
     9195.      5.JSLINT_ #JSLINT_REPORT_WARNINGS cite:nth-child(1) {
    +
     9196.      5    margin-top: 0;
    +
     9197.      5}
    +
     9198.      5.JSLINT_ #JSLINT_REPORT_WARNINGS samp {
    +
     9199.      5    background: lavenderblush;
    +
     9200.      5    display: block;
    +
     9201.      5    padding: 4px;
    +
     9202.      5    white-space: pre-wrap;
    +
     9203.      5}
    +
     9204.      5.JSLINT_ #JSLINT_REPORT_WARNINGS > div {
    +
     9205.      5    background: pink;
    +
     9206.      5    max-height: 400px;
    +
     9207.      5    overflow-y: auto;
    +
     9208.      5}
    +
     9209.      5.JSLINT_ #JSLINT_REPORT_WARNINGS > legend {
    +
     9210.      5/* Google Lighthouse Accessibility - Background and foreground colors do not */
    +
     9211.      5/* have a sufficient contrast ratio. */
    +
     9212.      5    /* background: indianred; */
    +
     9213.      5    background: #b44;
    +
     9214.      5}
    +
     9215.      5</style>
    +
     9216.      5            `).trim() + "\n";
    +
     9217.      5
    +
     9218.      5// Produce the Title.
    +
     9219.      5
    +
     9220.      5    html += "<div class=\"center\" id=\"JSLINT_REPORT_TITLE\">\n";
    +
     9221.      5    html += "JSLint Report\n";
    +
     9222.      5    html += "</div>\n";
    +
     9223.      5
    +
     9224.      5// Produce the HTML Error Report.
    +
     9225.      5// <cite>
    +
     9226.      5//     <address>LINE_NUMBER</address>
    +
     9227.      5//     MESSAGE
    +
     9228.      5// </cite>
    +
     9229.      5// <samp>EVIDENCE</samp>
    +
     9230.      5
    +
     9231.      5    html += "<fieldset id=\"JSLINT_REPORT_WARNINGS\">\n";
    +
     9232.      5    html += "<legend>Report: Warnings (" + warnings.length + ")</legend>\n";
    +
     9233.      5    html += "<div>\n";
    +
     9234.      1    if (stop) {
    +
     9235.      1        html += "<div class=\"center\">JSLint was unable to finish.</div>\n";
    +
     9236.      1    }
    +
     9237.      7    warnings.forEach(function ({
    +
     9238.      7        column,
    +
     9239.      7        line,
    +
     9240.      7        line_source,
    +
     9241.      7        message,
    +
     9242.      7        stack_trace = ""
    +
     9243.      7    }, ii) {
    +
     9244.      7        html += (
    +
     9245.      7            "<cite>"
    +
     9246.      7            + address(line, column)
    +
     9247.      7            + htmlEscape((ii + 1) + ". " + message)
    +
     9248.      7            + "</cite>"
    +
     9249.      7            + "<samp>"
    +
     9250.      7            + htmlEscape(line_source.slice(0, 400) + "\n" + stack_trace)
    +
     9251.      7            + "</samp>\n"
    +
     9252.      7        );
    +
     9253.      7    });
    +
     9254.      2    if (warnings.length === 0) {
    +
     9255.      2        html += "<div class=\"center\">There are no warnings.</div>\n";
    +
     9256.      2    }
    +
     9257.      5    html += "</div>\n";
    +
     9258.      5    html += "</fieldset>\n";
    +
     9259.      5
    +
     9260.      5// Produce the /*property*/ directive.
    +
     9261.      5
    +
     9262.      5    html += "<fieldset id=\"JSLINT_REPORT_PROPERTIES\">\n";
    +
     9263.      5    html += (
    +
     9264.      5        "<legend>Report: Properties ("
    +
     9265.      5        + Object.keys(property).length
    +
     9266.      5        + ")</legend>\n"
    +
     9267.      5    );
    +
     9268.      5    html += "<label>\n";
    +
     9269.      5    html += "<textarea readonly>";
    +
     9270.      5    html += "/*property";
    +
     9271.    290    Object.keys(property).sort().forEach(function (key, ii) {
    +
     9272.    289        if (ii !== 0) {
    +
     9273.    289            html += ",";
    +
     9274.    289            length_80 += 2;
    +
     9275.    289        }
    +
     9276.     40        if (length_80 + key.length >= 80) {
    +
     9277.     40            length_80 = 4;
    +
     9278.     40            html += "\n   ";
    +
     9279.     40        }
    +
     9280.    290        html += " " + key;
    +
     9281.    290        length_80 += key.length;
    +
     9282.    290    });
    +
     9283.      5    html += "\n*/\n";
    +
     9284.      5    html += "</textarea>\n";
    +
     9285.      5    html += "</label>\n";
    +
     9286.      5    html += "</fieldset>\n";
    +
     9287.      5
    +
     9288.      5// Produce the HTML Function Report.
    +
     9289.      5// <div class=LEVEL>
    +
     9290.      5//     <address>LINE_NUMBER</address>
    +
     9291.      5//     <dfn>FUNCTION_NAME_AND_SIGNATURE</dfn>
    +
     9292.      5//     <dl>
    +
     9293.      5//         <dt>DETAIL</dt>
    +
     9294.      5//         <dd>NAMES</dd>
    +
     9295.      5//     </dl>
    +
     9296.      5// </div>
    +
     9297.      5
    +
     9298.      5    html += "<fieldset id=\"JSLINT_REPORT_FUNCTIONS\">\n";
    +
     9299.      5    html += "<legend>Report: Functions (" + functions.length + ")</legend>\n";
    +
     9300.      5    html += "<div>\n";
    +
     9301.      2    if (json) {
    +
     9302.      2
    +
     9303.      2// Bugfix - fix website crashing when linting pure json-object.
    +
     9304.      2// return (
    +
     9305.      2
    +
     9306.      2        html += (
    +
     9307.      2            warnings.length === 0
    +
     9308.      2            ? "<div class=\"center\">JSON: good.</div>\n"
    +
     9309.      2            : "<div class=\"center\">JSON: bad.</div>\n"
    +
     9310.      2        );
    +
     9311.      3    } else if (functions.length === 0) {
    +
     9312.      3        html += "<div class=\"center\">There are no functions.</div>\n";
    +
     9313.      3    }
    +
     9314.      5    exports = Object.keys(exports).sort();
    +
     9315.      5    froms.sort();
    +
     9316.      5    global = Object.keys(global.context).sort();
    +
     9317.      5    module = (
    +
     9318.      5        module
    +
     9319.      1        ? "module"
    +
     9320.      4        : "global"
    +
     9321.      5    );
    +
     9322.      2    if (global.length + froms.length + exports.length > 0) {
    +
     9323.      2        html += "<div class=\"level level0\">\n";
    +
     9324.      2        html += detail(module, global);
    +
     9325.      2        html += detail("import from", froms);
    +
     9326.      2        html += detail("export", exports);
    +
     9327.      2        html += "</div>\n";
    +
     9328.      2    }
    +
     9329.    310    functions.forEach(function (the_function) {
    +
     9330.    310        let {
    +
     9331.    310            context,
    +
     9332.    310            from,
    +
     9333.    310            level,
    +
     9334.    310            line,
    +
     9335.    310            name,
    +
     9336.    310
    +
     9337.    310// Bugfix - fix html-report from crashing if parameters is undefined.
    +
     9338.    310
    +
     9339.    310            parameters = [],
    +
     9340.    310            signature
    +
     9341.    310        } = the_function;
    +
     9342.    310        let list = Object.keys(context);
    +
     9343.    310        let params;
    +
     9344.    310        html += (
    +
     9345.    310            "<div class=\"level level" + htmlEscape(level) + "\">"
    +
     9346.    310            + address(line, from + 1)
    +
     9347.    310            + "<dfn>"
    +
     9348.    310            + (
    +
     9349.    310                name === "=>"
    +
     9350.      1                ? htmlEscape(signature) + " =>"
    +
     9351.    309                : (
    +
     9352.    309                    typeof name === "string"
    +
     9353.    309                    ? "\u00ab" + htmlEscape(name) + "\u00bb"
    +
     9354.    309                    : htmlEscape(name.id)
    +
     9355.    309                )
    +
     9356.    310            ) + htmlEscape(signature)
    +
     9357.    310            + "</dfn>"
    +
     9358.    310        );
    +
     9359.    310        params = [];
    +
     9360.    450        parameters.forEach(function extract({
    +
     9361.    450            id,
    +
     9362.    450            names
    +
     9363.    450        }) {
    +
     9364.    450            switch (id) {
    +
     9365.      5            case "[":
    +
     9366.     40            case "{":
    +
     9367.     40
    +
     9368.     40// Recurse extract().
    +
     9369.     40
    +
     9370.     40                names.forEach(extract);
    +
     9371.     40                break;
    +
     9372.      3            case "ignore":
    +
     9373.      3                break;
    +
     9374.    407            default:
    +
     9375.    407                params.push(id);
    +
     9376.    450            }
    +
     9377.    450        });
    +
     9378.    310        html += detail("parameter", params.sort());
    +
     9379.    310        list.sort();
    +
     9380.   2119        html += detail("variable", list.filter(function (id) {
    +
     9381.   2119            return (
    +
     9382.   2119                context[id].role === "variable"
    +
     9383.   1632                && context[id].parent === the_function
    +
     9384.   2119            );
    +
     9385.   2119        }));
    +
     9386.   2119        html += detail("exception", list.filter(function (id) {
    +
     9387.   2119            return context[id].role === "exception";
    +
     9388.   2119        }));
    +
     9389.   2119        html += detail("closure", list.filter(function (id) {
    +
     9390.   2119            return (
    +
     9391.   2119                context[id].closure === true
    +
     9392.   1421                && context[id].parent === the_function
    +
     9393.   2119            );
    +
     9394.   2119        }));
    +
     9395.   2119        html += detail("outer", list.filter(function (id) {
    +
     9396.   2119            return (
    +
     9397.   2119                context[id].parent !== the_function
    +
     9398.   1129                && context[id].parent.id !== "(global)"
    +
     9399.   2119            );
    +
     9400.   2119        }));
    +
     9401.   2119        html += detail(module, list.filter(function (id) {
    +
     9402.   2119            return context[id].parent.id === "(global)";
    +
     9403.   2119        }));
    +
     9404.   2119        html += detail("label", list.filter(function (id) {
    +
     9405.   2119            return context[id].role === "label";
    +
     9406.   2119        }));
    +
     9407.    310        html += "</div>\n";
    +
     9408.    310    });
    +
     9409.      5    html += "</div>\n";
    +
     9410.      5    html += "</fieldset>\n";
    +
     9411.      5    html += "</div>\n";
    +
     9412.      5    return html;
    +
     9413.      5}
    +
     9414.      1
    +
     9415.      4async function jstestDescribe(description, testFunction) {
    +
     9416.      4
    +
     9417.      4// This function will create-and-run test-group <testFunction>
    +
     9418.      4// with given <description>.
    +
     9419.      4
    +
     9420.      4    let message;
    +
     9421.      4    let result;
    +
     9422.      4
    +
     9423.      4// Init jstestTimeStart.
    +
     9424.      4
    +
     9425.      1    if (jstestTimeStart === undefined) {
    +
     9426.      1        jstestTimeStart = jstestTimeStart || Date.now();
    +
     9427.      1        process.on("exit", jstestOnExit);
    +
     9428.      1    }
    +
     9429.      4
    +
     9430.      4// Init jstestItList.
    +
     9431.      4
    +
     9432.      4    jstestItList = [];
    +
     9433.      4    testFunction();
    +
     9434.      4
    +
     9435.      4// Wait for jstestItList to resolve.
    +
     9436.      4
    +
     9437.      4    result = await Promise.all(jstestItList);
    +
     9438.      4
    +
     9439.      4// Print test results.
    +
     9440.      4
    +
     9441.      4    message = (
    +
     9442.      4        "\n  " + (Date.now() - jstestTimeStart) + "ms"
    +
     9443.      4        + " - test describe - " + description + "\n"
    +
     9444.     22        + result.map(function ([
    +
     9445.     22            err, description, mode
    +
     9446.     22        ]) {
    +
     9447.     22            jstestItCount += 1;
    +
     9448.      1            if (err) {
    +
     9449.      1                jstestCountFailed += 1;
    +
     9450.      1                err = (
    +
     9451.      1                    "    \u001b[31m\u2718 " + jstestItCount + ". test it - "
    +
     9452.      1                    + description + "\n" + err.stack + "\u001b[39m"
    +
     9453.      1                );
    +
     9454.      1                if (mode === "pass") {
    +
     9455.      1                    jstestCountFailed -= 1;
    +
     9456.      1                    err = "";
    +
     9457.      1                }
    +
     9458.      1            }
    +
     9459.     22            return err || (
    +
     9460.     22                "    \u001b[32m\u2714 " + jstestItCount + ". test it - "
    +
     9461.     22                + description + "\u001b[39m"
    +
     9462.     22            );
    +
     9463.     22        }).join("\n")
    +
     9464.      4    );
    +
     9465.      4    console.error(message);
    +
     9466.      4}
    +
     9467.      1
    +
     9468.     22function jstestIt(description, testFunction, mode) {
    +
     9469.     22
    +
     9470.     22// This function will create-and-run test-case <testFunction>
    +
     9471.     22// inside current test-group with given <description>.
    +
     9472.     22
    +
     9473.     22    jstestCountTotal += 1;
    +
     9474.     22    jstestItList.push(new Promise(async function (resolve) {
    +
     9475.     22        let err;
    +
     9476.     22        try {
    +
     9477.     21            await testFunction();
    +
     9478.     21        } catch (errCaught) {
    +
     9479.      1            err = errCaught;
    +
     9480.      1        }
    +
     9481.     22        resolve([
    +
     9482.     22            err, description, mode
    +
     9483.     22        ]);
    +
     9484.     22    }));
    +
     9485.     22}
    +
     9486.      1
    +
     9487.      2function jstestOnExit(exitCode, processExit, countFailed) {
    +
     9488.      2
    +
     9489.      2// This function will on process-exit, print test-report
    +
     9490.      2// and exit with non-zero exit-code if any test failed.
    +
     9491.      2
    +
     9492.      2    let message = (
    +
     9493.      2        (
    +
     9494.      2            (jstestCountFailed || countFailed)
    +
     9495.      1            ? "\n\u001b[31m"
    +
     9496.      1            : "\n\u001b[32m"
    +
     9497.      2        )
    +
     9498.      2        + "  tests total  - " + jstestCountTotal + "\n"
    +
     9499.      2        + "  tests failed - " + jstestCountFailed + "\n"
    +
     9500.      2        + "\u001b[39m"
    +
     9501.      2    );
    +
     9502.      1    if (!processExit) {
    +
     9503.      1        console.error(message);
    +
     9504.      1        processExit = process.exit;
    +
     9505.      1    }
    +
     9506.      2    processExit(exitCode || jstestCountFailed);
    +
     9507.      2    return message;
    +
     9508.      2}
    +
     9509.      1
    +
     9510.     89async function moduleFsInit() {
    +
     9511.     89
    +
     9512.     89// This function will import nodejs builtin-modules if they have not yet been
    +
     9513.     89// imported.
    +
     9514.     89
    +
     9515.     89// State 3 - Modules already imported.
    +
     9516.     89
    +
     9517.     81    if (moduleFs !== undefined) {
    +
     9518.     81        return;
    +
     9519.     81    }
    +
     9520.      8
    +
     9521.      8// State 2 - Wait while modules are importing.
    +
     9522.      8
    +
     9523.      8    if (moduleFsInitResolveList !== undefined) {
    +
     9524.      7        return new Promise(function (resolve) {
    +
     9525.      7            moduleFsInitResolveList.push(resolve);
    +
     9526.      7        });
    +
     9527.      7    }
    +
     9528.      1
    +
     9529.      1// State 1 - Start importing modules.
    +
     9530.      1
    +
     9531.      1    moduleFsInitResolveList = [];
    +
     9532.      1    [
    +
     9533.      1        moduleChildProcess,
    +
     9534.      1        moduleFs,
    +
     9535.      1        modulePath,
    +
     9536.      1        moduleUrl
    +
     9537.      1    ] = await Promise.all([
    +
     9538.      1        import("child_process"),
    +
     9539.      1        import("fs"),
    +
     9540.      1        import("path"),
    +
     9541.      1        import("url")
    +
     9542.      1    ]);
    +
     9543.      7    while (moduleFsInitResolveList.length > 0) {
    +
     9544.      7        moduleFsInitResolveList.shift()();
    +
     9545.      7    }
    +
     9546.     89}
    +
     9547.      1
    +
     9548.   2527function noop(val) {
    +
     9549.   2527
    +
     9550.   2527// This function will do nothing except return <val>.
    +
     9551.   2527
    +
     9552.   2527    return val;
    +
     9553.   2527}
    +
     9554.      1
    +
     9555.  10051function objectDeepCopyWithKeysSorted(obj) {
    +
     9556.  10051
    +
     9557.  10051// This function will recursively deep-copy <obj> with keys sorted.
    +
     9558.  10051
    +
     9559.  10051    let sorted;
    +
     9560.   6343    if (typeof obj !== "object" || !obj) {
    +
     9561.   6343        return obj;
    +
     9562.   6343    }
    +
     9563.   3708
    +
     9564.   3708// Recursively deep-copy list with child-keys sorted.
    +
     9565.   3708
    +
     9566.   3708    if (Array.isArray(obj)) {
    +
     9567.   1122        return obj.map(objectDeepCopyWithKeysSorted);
    +
     9568.   2586    }
    +
     9569.   2586
    +
     9570.   2586// Recursively deep-copy obj with keys sorted.
    +
     9571.   2586
    +
     9572.   2586    sorted = {};
    +
     9573.   7457    Object.keys(obj).sort().forEach(function (key) {
    +
     9574.   7457        sorted[key] = objectDeepCopyWithKeysSorted(obj[key]);
    +
     9575.   7457    });
    +
     9576.   2586    return sorted;
    +
     9577.   2586}
    +
     9578.      1
    +
     9579.   2618function object_assign_from_list(dict, list, val) {
    +
     9580.   2618
    +
     9581.   2618// Assign each property-name from <list> to <dict>.
    +
     9582.   2618
    +
     9583.  83439    list.forEach(function (key) {
    +
     9584.  83439        dict[key] = val;
    +
     9585.  83439    });
    +
     9586.   2618    return dict;
    +
     9587.   2618}
    +
     9588.      1
    +
     9589.     97function v8CoverageListMerge(processCovs) {
    +
     9590.     97
    +
     9591.     97// This function is derived from MIT Licensed v8-coverage at
    +
     9592.     97// https://github.com/demurgos/v8-coverage/tree/master/ts
    +
     9593.     97// https://github.com/demurgos/v8-coverage/blob/master/ts/LICENSE.md
    +
     9594.     97//
    +
     9595.     97// Merges a list of v8 process coverages.
    +
     9596.     97// The result is normalized.
    +
     9597.     97// The input values may be mutated, it is not safe to use them after passing
    +
     9598.     97// them to this function.
    +
     9599.     97// The computation is synchronous.
    +
     9600.     97// @param processCovs Process coverages to merge.
    +
     9601.     97// @return Merged process coverage.
    +
     9602.     97
    +
     9603.     97    let resultMerged = [];      // List of merged scripts from processCovs.
    +
     9604.     97    let urlToScriptDict = new Map();    // Map scriptCov.url to scriptCovs.
    +
     9605.     97
    +
     9606.   1091    function compareRangeList(aa, bb) {
    +
     9607.   1091
    +
     9608.   1091// Compares two range coverages.
    +
     9609.   1091// The ranges are first ordered by ascending `startOffset` and then by
    +
     9610.   1091// descending `endOffset`.
    +
     9611.   1091// This corresponds to a pre-order tree traversal.
    +
     9612.   1091
    +
     9613.   1062        if (aa.startOffset !== bb.startOffset) {
    +
     9614.   1062            return aa.startOffset - bb.startOffset;
    +
     9615.   1062        }
    +
     9616.     29        return bb.endOffset - aa.endOffset;
    +
     9617.     29    }
    +
     9618.     97
    +
     9619.   1707    function dictKeyValueAppend(dict, key, val) {
    +
     9620.   1707
    +
     9621.   1707// This function will append <val> to list <dict>[<key>].
    +
     9622.   1707
    +
     9623.   1707        let list = dict.get(key);
    +
     9624.   1165        if (list === undefined) {
    +
     9625.   1165            list = [];
    +
     9626.   1165            dict.set(key, list);
    +
     9627.   1165        }
    +
     9628.   1707        list.push(val);
    +
     9629.   1707    }
    +
     9630.     97
    +
     9631.    384    function mergeTreeList(parentTrees) {
    +
     9632.    384
    +
     9633.    384// This function will return RangeTree object with <parentTrees> merged into
    +
     9634.    384// property-children.
    +
     9635.    384// @precondition Same `start` and `end` for all the parentTrees
    +
     9636.    384
    +
     9637.    119        if (parentTrees.length <= 1) {
    +
     9638.    119            return parentTrees[0];
    +
     9639.    265        }
    +
     9640.    265
    +
     9641.    265// new RangeTree().
    +
     9642.    265
    +
     9643.    265        return {
    +
     9644.    265
    +
     9645.    265// Merge parentTrees into property-children.
    +
     9646.    265
    +
     9647.    265            children: mergeTreeListToChildren(parentTrees),
    +
     9648.    669            delta: parentTrees.reduce(function (aa, bb) {
    +
     9649.    669                return aa + bb.delta;
    +
     9650.    669            }, 0),
    +
     9651.    265            end: parentTrees[0].end,
    +
     9652.    265            start: parentTrees[0].start
    +
     9653.    265        };
    +
     9654.    265    }
    +
     9655.     97
    +
     9656.    265    function mergeTreeListToChildren(parentTrees) {
    +
     9657.    265
    +
     9658.    265// This function will return <resultChildren> with <parentTrees> merged.
    +
     9659.    265
    +
     9660.    265        let openRange;
    +
     9661.    265        let parentToChildDict = new Map();      // Map parent to child.
    +
     9662.    265        let queueList;
    +
     9663.    265        let queueListIi = 0;
    +
     9664.    265        let queueOffset;
    +
     9665.    265        let queueTrees;
    +
     9666.    265        let resultChildren = [];
    +
     9667.    265        let startToTreeDict = new Map();        // Map tree.start to tree.
    +
     9668.    639        function nextXxx() {
    +
     9669.    639
    +
     9670.    639// Increment nextOffset, nextTrees.
    +
     9671.    639
    +
     9672.    639            let [
    +
     9673.    639                nextOffset, nextTrees
    +
     9674.    300            ] = queueList[queueListIi] || [];
    +
     9675.    639            let openRangeEnd;
    +
     9676.    583            if (queueTrees === undefined) {
    +
     9677.    583                queueListIi += 1;
    +
     9678.    583
    +
     9679.    583// Increment nextOffset, nextTrees.
    +
     9680.    583
    +
     9681.    583            } else if (nextOffset === undefined || nextOffset > queueOffset) {
    +
     9682.     56                nextOffset = queueOffset;
    +
     9683.     56                nextTrees = queueTrees;
    +
     9684.     56                queueTrees = undefined;
    +
     9685.     56
    +
     9686.     56// Concat queueTrees to nextTrees.
    +
     9687.     56
    +
     9688.     56            } else {
    +
     9689.     56                if (nextOffset === queueOffset) {
    +
     9690.     56                    queueTrees.forEach(function (tree) {
    +
     9691.     56                        nextTrees.push(tree);
    +
     9692.     56                    });
    +
     9693.     56                    queueTrees = undefined;
    +
     9694.     56                }
    +
     9695.     56                queueListIi += 1;
    +
     9696.     56            }
    +
     9697.    639
    +
     9698.    639// Reached end of queueList.
    +
     9699.    639
    +
     9700.    265            if (nextOffset === undefined) {
    +
     9701.    265                if (openRange !== undefined) {
    +
     9702.    265
    +
     9703.    265// Append nested-children from parentToChildDict (within openRange) to
    +
     9704.    265// resultChildren.
    +
     9705.    265
    +
     9706.    265                    resultAppendNextChild();
    +
     9707.    265                }
    +
     9708.    265                return true;
    +
     9709.    374            }
    +
     9710.    374            if (openRange !== undefined && openRange.end <= nextOffset) {
    +
     9711.    129
    +
     9712.    129// Append nested-children from parentToChildDict (within openRange) to
    +
     9713.    129// resultChildren.
    +
     9714.    129
    +
     9715.    129                resultAppendNextChild();
    +
     9716.    129                openRange = undefined;
    +
     9717.    374            }
    +
     9718.    374            if (openRange === undefined) {
    +
     9719.    292                openRangeEnd = nextOffset + 1;
    +
     9720.    502                nextTrees.forEach(function ({
    +
     9721.    502                    parentIi,
    +
     9722.    502                    tree
    +
     9723.    502                }) {
    +
     9724.    502                    openRangeEnd = Math.max(openRangeEnd, tree.end);
    +
     9725.    502
    +
     9726.    502// Append children from nextTrees to parentToChildDict.
    +
     9727.    502
    +
     9728.    502                    dictKeyValueAppend(parentToChildDict, parentIi, tree);
    +
     9729.    502                });
    +
     9730.    292                queueOffset = openRangeEnd;
    +
     9731.    292                openRange = {
    +
     9732.    292                    end: openRangeEnd,
    +
     9733.    292                    start: nextOffset
    +
     9734.    292                };
    +
     9735.    292            } else {
    +
     9736.    114                nextTrees.forEach(function ({
    +
     9737.    114                    parentIi,
    +
     9738.    114                    tree
    +
     9739.    114                }) {
    +
     9740.    114                    let right;
    +
     9741.     82                    if (tree.end > openRange.end) {
    +
     9742.     82                        right = treeSplit(tree, openRange.end);
    +
     9743.     82                        if (queueTrees === undefined) {
    +
     9744.     82                            queueTrees = [];
    +
     9745.     82                        }
    +
     9746.     82
    +
     9747.     82// new RangeTreeWithParent().
    +
     9748.     82
    +
     9749.     82                        queueTrees.push({
    +
     9750.     82                            parentIi,
    +
     9751.     82                            tree: right
    +
     9752.     82                        });
    +
     9753.     82                    }
    +
     9754.    114
    +
     9755.    114// Append children from nextTrees to parentToChildDict.
    +
     9756.    114
    +
     9757.    114                    dictKeyValueAppend(parentToChildDict, parentIi, tree);
    +
     9758.    114                });
    +
     9759.     82            }
    +
     9760.    639        }
    +
     9761.    292        function resultAppendNextChild() {
    +
     9762.    292
    +
     9763.    292// This function will append next child to <resultChildren>.
    +
     9764.    292
    +
     9765.    292            let treesMatching = [];
    +
     9766.    589            parentToChildDict.forEach(function (nested) {
    +
     9767.    589                if (
    +
     9768.    589                    nested.length === 1
    +
     9769.    563                    && nested[0].start === openRange.start
    +
     9770.    480                    && nested[0].end === openRange.end
    +
     9771.    468                ) {
    +
     9772.    468                    treesMatching.push(nested[0]);
    +
     9773.    468                } else {
    +
     9774.    121
    +
     9775.    121// new rangeTreeCreate().
    +
     9776.    121
    +
     9777.    121                    treesMatching.push({
    +
     9778.    121                        children: nested,
    +
     9779.    121                        delta: 0,
    +
     9780.    121                        end: openRange.end,
    +
     9781.    121                        start: openRange.start
    +
     9782.    121                    });
    +
     9783.    121                }
    +
     9784.    589            });
    +
     9785.    292            parentToChildDict.clear();
    +
     9786.    292
    +
     9787.    292// Recurse mergeTreeList().
    +
     9788.    292
    +
     9789.    292            resultChildren.push(mergeTreeList(treesMatching));
    +
     9790.    292        }
    +
     9791.     75        function treeSplit(tree, offset) {
    +
     9792.     75
    +
     9793.     75// This function will split <tree> along <offset> and return the right-side.
    +
     9794.     75// @precondition `tree.start < offset && offset < tree.end`
    +
     9795.     75// @return RangeTree Right part
    +
     9796.     75
    +
     9797.     75            let child;
    +
     9798.     75            let ii = 0;
    +
     9799.     75            let leftChildLen = tree.children.length;
    +
     9800.     75            let mid;
    +
     9801.     75            let resultTree;
    +
     9802.     75            let rightChildren;
    +
     9803.     75
    +
     9804.     75// TODO(perf): Binary search (check overhead) //jslint-quiet
    +
     9805.     75
    +
     9806.     19            while (ii < tree.children.length) {
    +
     9807.     19                child = tree.children[ii];
    +
     9808.     19                if (child.start < offset && offset < child.end) {
    +
     9809.     19
    +
     9810.     19// Recurse treeSplit().
    +
     9811.     19
    +
     9812.     19                    mid = treeSplit(child, offset);
    +
     9813.     19                    leftChildLen = ii + 1;
    +
     9814.     19                    break;
    +
     9815.     19                }
    +
     9816.     19                if (child.start >= offset) {
    +
     9817.     19                    leftChildLen = ii;
    +
     9818.     19                    break;
    +
     9819.     19                }
    +
     9820.     19                ii += 1;
    +
     9821.     19            }
    +
     9822.     75            rightChildren = tree.children.splice(
    +
     9823.     75                leftChildLen,
    +
     9824.     75                tree.children.length - leftChildLen
    +
     9825.     75            );
    +
     9826.      4            if (mid !== undefined) {
    +
     9827.      4                rightChildren.unshift(mid);
    +
     9828.      4            }
    +
     9829.     75
    +
     9830.     75// new rangeTreeCreate().
    +
     9831.     75
    +
     9832.     75            resultTree = {
    +
     9833.     75                children: rightChildren,
    +
     9834.     75                delta: tree.delta,
    +
     9835.     75                end: tree.end,
    +
     9836.     75                start: offset
    +
     9837.     75            };
    +
     9838.     75            tree.end = offset;
    +
     9839.     75            return resultTree;
    +
     9840.     75        }
    +
     9841.    265
    +
     9842.    265// Init startToTreeDict.
    +
     9843.    265
    +
     9844.    669        parentTrees.forEach(function (parentTree, parentIi) {
    +
     9845.    545            parentTree.children.forEach(function (child) {
    +
     9846.    545
    +
     9847.    545// Append child with child.start to startToTreeDict.
    +
     9848.    545
    +
     9849.    545                dictKeyValueAppend(startToTreeDict, child.start, {
    +
     9850.    545                    parentIi,
    +
     9851.    545                    tree: child
    +
     9852.    545                });
    +
     9853.    545            });
    +
     9854.    669        });
    +
     9855.    265
    +
     9856.    265// init queueList.
    +
     9857.    265
    +
     9858.    335        queueList = Array.from(startToTreeDict).map(function ([
    +
     9859.    335            startOffset, trees
    +
     9860.    335        ]) {
    +
     9861.    335
    +
     9862.    335// new StartEvent().
    +
     9863.    335
    +
     9864.    335            return [
    +
     9865.    335                startOffset, trees
    +
     9866.    335            ];
    +
     9867.    217        }).sort(function (aa, bb) {
    +
     9868.    217            return aa[0] - bb[0];
    +
     9869.    217        });
    +
     9870.    639        while (true) {
    +
     9871.    639            if (nextXxx()) {
    +
     9872.    639                break;
    +
     9873.    639            }
    +
     9874.    639        }
    +
     9875.    265        return resultChildren;
    +
     9876.    265    }
    +
     9877.     97
    +
     9878.    688    function sortFunc(funcCov) {
    +
     9879.    688
    +
     9880.    688// This function will normalize-and-sort <funcCov>.ranges.
    +
     9881.    688// Sorts the ranges (pre-order sort).
    +
     9882.    688// TODO: Tree-based normalization of the ranges. //jslint-quiet
    +
     9883.    688// @param funcCov Function coverage to normalize.
    +
     9884.    688
    +
     9885.    688        funcCov.ranges = treeToRanges(treeFromSortedRanges(
    +
     9886.    688            funcCov.ranges.sort(compareRangeList)
    +
     9887.    688        ));
    +
     9888.    688        return funcCov;
    +
     9889.    688    }
    +
     9890.     97
    +
     9891.    129    function sortScript(scriptCov) {
    +
     9892.    129
    +
     9893.    129// This function will normalize-and-sort <scriptCov>.functions.
    +
     9894.    129
    +
     9895.    129// Normalize-and-sort functions[xxx].ranges.
    +
     9896.    129
    +
     9897.    687        scriptCov.functions.forEach(function (funcCov) {
    +
     9898.    687            sortFunc(funcCov);
    +
     9899.    687        });
    +
     9900.    129
    +
     9901.    129// Sort functions by root range (pre-order sort).
    +
     9902.    129
    +
     9903.    558        scriptCov.functions.sort(function (aa, bb) {
    +
     9904.    558            return compareRangeList(aa.ranges[0], bb.ranges[0]);
    +
     9905.    558        });
    +
     9906.    129        return scriptCov;
    +
     9907.    129    }
    +
     9908.     97
    +
     9909.    887    function treeFromSortedRanges(ranges) {
    +
     9910.    887
    +
     9911.    887// @precondition `ranges` are well-formed and pre-order sorted
    +
     9912.    887
    +
     9913.    887        let root;
    +
     9914.    887        let stack = [];   // Stack of parent trees and parent counts.
    +
     9915.   1853        ranges.forEach(function (range) {
    +
     9916.   1853
    +
     9917.   1853// new rangeTreeCreate().
    +
     9918.   1853
    +
     9919.   1853            let node = {
    +
     9920.   1853                children: [],
    +
     9921.   1853                delta: range.count,
    +
     9922.   1853                end: range.endOffset,
    +
     9923.   1853                start: range.startOffset
    +
     9924.   1853            };
    +
     9925.   1853            let parent;
    +
     9926.   1853            let parentCount;
    +
     9927.    887            if (root === undefined) {
    +
     9928.    887                root = node;
    +
     9929.    887                stack.push([
    +
     9930.    887                    node, range.count
    +
     9931.    887                ]);
    +
     9932.    887                return;
    +
     9933.    966            }
    +
     9934.   1561            while (true) {
    +
     9935.   1561                [
    +
     9936.   1561                    parent, parentCount
    +
     9937.   1561                ] = stack[stack.length - 1];
    +
     9938.   1561
    +
     9939.   1561// assert: `top !== undefined` (the ranges are sorted)
    +
     9940.   1561
    +
     9941.   1561                if (range.startOffset < parent.end) {
    +
     9942.   1561                    break;
    +
     9943.   1561                }
    +
     9944.   1561                stack.pop();
    +
     9945.   1561            }
    +
     9946.    966            node.delta -= parentCount;
    +
     9947.    966            parent.children.push(node);
    +
     9948.    966            stack.push([
    +
     9949.    966                node, range.count
    +
     9950.    966            ]);
    +
     9951.    966        });
    +
     9952.    887        return root;
    +
     9953.    887    }
    +
     9954.     97
    +
     9955.    780    function treeToRanges(tree) {
    +
     9956.    780
    +
     9957.    780// Get the range coverages corresponding to the tree.
    +
     9958.    780// The ranges are pre-order sorted.
    +
     9959.    780
    +
     9960.    780        let count;
    +
     9961.    780        let cur;
    +
     9962.    780        let ii;
    +
     9963.    780        let parentCount;
    +
     9964.    780        let ranges = [];
    +
     9965.    780        let stack = [           // Stack of parent trees and counts.
    +
     9966.    780            [
    +
     9967.    780                tree, 0
    +
     9968.    780            ]
    +
     9969.    780        ];
    +
     9970.   1627        function normalizeRange(tree) {
    +
     9971.   1627
    +
     9972.   1627// @internal
    +
     9973.   1627
    +
     9974.   1627            let children = [];
    +
     9975.   1627            let curEnd;
    +
     9976.   1627            let head;
    +
     9977.   1627            let tail = [];
    +
     9978.    847            function endChain() {
    +
     9979.     18                if (tail.length !== 0) {
    +
     9980.     18                    head.end = tail[tail.length - 1].end;
    +
     9981.     18                    tail.forEach(function (tailTree) {
    +
     9982.     18                        tailTree.children.forEach(function (subChild) {
    +
     9983.     18                            subChild.delta += tailTree.delta - head.delta;
    +
     9984.     18                            head.children.push(subChild);
    +
     9985.     18                        });
    +
     9986.     18                    });
    +
     9987.     18                    tail.length = 0;
    +
     9988.     18                }
    +
     9989.    847
    +
     9990.    847// Recurse normalizeRange().
    +
     9991.    847
    +
     9992.    847                normalizeRange(head);
    +
     9993.    847                children.push(head);
    +
     9994.    847            }
    +
     9995.    865            tree.children.forEach(function (child) {
    +
     9996.    432                if (head === undefined) {
    +
     9997.    432                    head = child;
    +
     9998.    433                } else if (
    +
     9999.    433                    child.delta === head.delta && child.start === curEnd
    +
    10000.    433                ) {
    +
    10001.    433                    tail.push(child);
    +
    10002.    433                } else {
    +
    10003.    433                    endChain();
    +
    10004.    433                    head = child;
    +
    10005.    433                }
    +
    10006.    865                curEnd = child.end;
    +
    10007.    865            });
    +
    10008.    432            if (head !== undefined) {
    +
    10009.    432                endChain();
    +
    10010.    432            }
    +
    10011.    238            if (children.length === 1) {
    +
    10012.    238                if (
    +
    10013.    238                    children[0].start === tree.start
    +
    10014.    238                    && children[0].end === tree.end
    +
    10015.    238                ) {
    +
    10016.    238                    tree.delta += children[0].delta;
    +
    10017.    238                    tree.children = children[0].children;
    +
    10018.    238
    +
    10019.    238// `.lazyCount` is zero for both (both are after normalization)
    +
    10020.    238
    +
    10021.    238                    return;
    +
    10022.    238                }
    +
    10023.   1621            }
    +
    10024.   1621            tree.children = children;
    +
    10025.   1621        }
    +
    10026.    780        normalizeRange(tree);
    +
    10027.   1621        while (stack.length > 0) {
    +
    10028.   1621            [
    +
    10029.   1621                cur, parentCount
    +
    10030.   1621            ] = stack.pop();
    +
    10031.   1621            count = parentCount + cur.delta;
    +
    10032.   1621            ranges.push({
    +
    10033.   1621                count,
    +
    10034.   1621                endOffset: cur.end,
    +
    10035.   1621                startOffset: cur.start
    +
    10036.   1621            });
    +
    10037.   1621            ii = cur.children.length - 1;
    +
    10038.   1621            while (ii >= 0) {
    +
    10039.   1621                stack.push([
    +
    10040.   1621                    cur.children[ii], count
    +
    10041.   1621                ]);
    +
    10042.   1621                ii -= 1;
    +
    10043.   1621            }
    +
    10044.   1621        }
    +
    10045.    780        return ranges;
    +
    10046.    780    }
    +
    10047.     97
    +
    10048.      1    if (processCovs.length === 0) {
    +
    10049.      1        return {
    +
    10050.      1            result: []
    +
    10051.      1        };
    +
    10052.     96    }
    +
    10053.     96
    +
    10054.     96// Init urlToScriptDict.
    +
    10055.     96
    +
    10056.    234    processCovs.forEach(function ({
    +
    10057.    234        result
    +
    10058.    234    }) {
    +
    10059.    269        result.forEach(function (scriptCov) {
    +
    10060.    269            dictKeyValueAppend(urlToScriptDict, scriptCov.url, scriptCov);
    +
    10061.    269        });
    +
    10062.    234    });
    +
    10063.    129    urlToScriptDict.forEach(function (scriptCovs) {
    +
    10064.    129
    +
    10065.    129// assert: `scriptCovs.length > 0`
    +
    10066.    129
    +
    10067.    129// function mergeScriptList(scriptCovs) {
    +
    10068.    129// Merges a list of matching script coverages.
    +
    10069.    129// Scripts are matching if they have the same `url`.
    +
    10070.    129// The result is normalized.
    +
    10071.    129// The input values may be mutated, it is not safe to use them after passing
    +
    10072.    129// them to this function.
    +
    10073.    129// The computation is synchronous.
    +
    10074.    129// @param scriptCovs Process coverages to merge.
    +
    10075.    129// @return Merged script coverage, or `undefined` if the input list was empty.
    +
    10076.    129
    +
    10077.    129        let functions = [];
    +
    10078.    129
    +
    10079.    129// Map funcCovRoot.startOffset:funcCovRoot.endOffset to funcCov.
    +
    10080.    129
    +
    10081.    129        let rangeToFuncDict = new Map();
    +
    10082.    129
    +
    10083.    129// Probably deadcode.
    +
    10084.    129// if (scriptCovs.length === 0) {
    +
    10085.    129//     return undefined;
    +
    10086.    129// }
    +
    10087.    129
    +
    10088.     96        if (scriptCovs.length === 1) {
    +
    10089.     96            resultMerged.push(sortScript(scriptCovs[0]));
    +
    10090.     96            return;
    +
    10091.     96        }
    +
    10092.     96
    +
    10093.     96// Init rangeToFuncDict.
    +
    10094.     96// Map funcCovRoot.startOffset:funcCovRoot.endOffset to funcCov.
    +
    10095.     96
    +
    10096.    226        scriptCovs.forEach(function ({
    +
    10097.    226            functions
    +
    10098.    226        }) {
    +
    10099.    277            functions.forEach(function (funcCov) {
    +
    10100.    277                dictKeyValueAppend(
    +
    10101.    277                    rangeToFuncDict,
    +
    10102.    277
    +
    10103.    277// This string can be used to match function with same root range.
    +
    10104.    277// The string is derived from the start and end offsets of the root range of
    +
    10105.    277// the function.
    +
    10106.    277// This assumes that `ranges` is non-empty (true for valid function coverages).
    +
    10107.    277
    +
    10108.    277                    (
    +
    10109.    277                        funcCov.ranges[0].startOffset
    +
    10110.    277                        + ";" + funcCov.ranges[0].endOffset
    +
    10111.    277                    ),
    +
    10112.    277                    funcCov
    +
    10113.    277                );
    +
    10114.    277            });
    +
    10115.    226        });
    +
    10116.    112        rangeToFuncDict.forEach(function (funcCovs) {
    +
    10117.    112
    +
    10118.    112// assert: `funcCovs.length > 0`
    +
    10119.    112
    +
    10120.    112// function mergeFuncList(funcCovs) {
    +
    10121.    112// Merges a list of matching function coverages.
    +
    10122.    112// Functions are matching if their root ranges have the same span.
    +
    10123.    112// The result is normalized.
    +
    10124.    112// The input values may be mutated, it is not safe to use them after passing
    +
    10125.    112// them to this function.
    +
    10126.    112// The computation is synchronous.
    +
    10127.    112// @param funcCovs Function coverages to merge.
    +
    10128.    112// @return Merged function coverage, or `undefined` if the input list was empty.
    +
    10129.    112
    +
    10130.    112            let count = 0;
    +
    10131.    112            let isBlockCoverage;
    +
    10132.    112            let merged;
    +
    10133.    112            let ranges;
    +
    10134.    112            let trees = [];
    +
    10135.    112
    +
    10136.    112// Probably deadcode.
    +
    10137.    112// if (funcCovs.length === 0) {
    +
    10138.    112//     return undefined;
    +
    10139.    112// }
    +
    10140.    112
    +
    10141.     96            if (funcCovs.length === 1) {
    +
    10142.     96                functions.push(sortFunc(funcCovs[0]));
    +
    10143.     96                return;
    +
    10144.    111            }
    +
    10145.    111
    +
    10146.    111// assert: `funcCovs[0].ranges.length > 0`
    +
    10147.    111
    +
    10148.    276            funcCovs.forEach(function (funcCov) {
    +
    10149.    276
    +
    10150.    276// assert: `funcCov.ranges.length > 0`
    +
    10151.    276// assert: `funcCov.ranges` is sorted
    +
    10152.    276
    +
    10153.    276                count += (
    +
    10154.    276                    funcCov.count !== undefined
    +
    10155.    111                    ? funcCov.count
    +
    10156.    274                    : funcCov.ranges[0].count
    +
    10157.    276                );
    +
    10158.    199                if (funcCov.isBlockCoverage) {
    +
    10159.    199                    trees.push(treeFromSortedRanges(funcCov.ranges));
    +
    10160.    199                }
    +
    10161.    276            });
    +
    10162.    111            if (trees.length > 0) {
    +
    10163.     96                isBlockCoverage = true;
    +
    10164.     96                ranges = treeToRanges(mergeTreeList(trees));
    +
    10165.     96            } else {
    +
    10166.     96                isBlockCoverage = false;
    +
    10167.     96                ranges = [
    +
    10168.     96                    {
    +
    10169.     96                        count,
    +
    10170.     96                        endOffset: funcCovs[0].ranges[0].endOffset,
    +
    10171.     96                        startOffset: funcCovs[0].ranges[0].startOffset
    +
    10172.     96                    }
    +
    10173.     96                ];
    +
    10174.    111            }
    +
    10175.    111            merged = {
    +
    10176.    111                functionName: funcCovs[0].functionName,
    +
    10177.    111                isBlockCoverage,
    +
    10178.    111                ranges
    +
    10179.    111            };
    +
    10180.    111            if (count !== ranges[0].count) {
    +
    10181.     96                merged.count = count;
    +
    10182.    111            }
    +
    10183.    111
    +
    10184.    111// assert: `merged` is normalized
    +
    10185.    111
    +
    10186.    111            functions.push(merged);
    +
    10187.    111        });
    +
    10188.     96        resultMerged.push(sortScript({
    +
    10189.     96            functions,
    +
    10190.     96            scriptId: scriptCovs[0].scriptId,
    +
    10191.     96            url: scriptCovs[0].url
    +
    10192.     96        }));
    +
    10193.     96    });
    +
    10194.     96
    +
    10195.     96// Sorts the scripts alphabetically by `url`.
    +
    10196.     96// Reassigns script ids: the script at index `0` receives `"0"`, the script at
    +
    10197.     96// index `1` receives `"1"` etc.
    +
    10198.     96
    +
    10199.     96    Object.entries(resultMerged.sort(function (aa, bb) {
    +
    10200.     96        return (
    +
    10201.     96            aa.url > bb.url
    +
    10202.     96            ? 1
    +
    10203.     96            : -1
    +
    10204.     96        );
    +
    10205.    129    })).forEach(function ([
    +
    10206.    129        scriptId, scriptCov
    +
    10207.    129    ]) {
    +
    10208.    129        scriptCov.scriptId = scriptId.toString(10);
    +
    10209.    129    });
    +
    10210.     96    return {
    +
    10211.     96        result: resultMerged
    +
    10212.     96    };
    +
    10213.     96}
    +
    10214.      1
    +
    10215.      8async function v8CoverageReportCreate({
    +
    10216.      8    consoleError,
    +
    10217.      8    coverageDir,
    +
    10218.      8    processArgv = []
    +
    10219.      8}) {
    +
    10220.      8
    +
    10221.      8// This function will create html-coverage-reports directly from
    +
    10222.      8// v8-coverage-files in <coverageDir>.
    +
    10223.      8// 1. Spawn node.js program <processArgv> with NODE_V8_COVERAGE.
    +
    10224.      8// 2. Merge JSON v8-coverage-files in <coverageDir>.
    +
    10225.      8// 3. Create html-coverage-reports in <coverageDir>.
    +
    10226.      8
    +
    10227.      8    let cwd;
    +
    10228.      8    let exitCode = 0;
    +
    10229.      8    let fileDict;
    +
    10230.      8    let fileExcludeList = [];
    +
    10231.      8    let fileIncludeList = [];
    +
    10232.      8    let fileIncludeNodeModules;
    +
    10233.      8    let processArgElem;
    +
    10234.      8    let promiseList = [];
    +
    10235.      8    let v8CoverageObj;
    +
    10236.      8
    +
    10237.     13    function htmlRender({
    +
    10238.     13        fileList,
    +
    10239.     13        lineList,
    +
    10240.     13        modeIndex,
    +
    10241.     13        pathname
    +
    10242.     13    }) {
    +
    10243.     13        let html;
    +
    10244.     13        let padLines;
    +
    10245.     13        let padPathname;
    +
    10246.     13        let txt;
    +
    10247.     13        let txtBorder;
    +
    10248.     13        html = "";
    +
    10249.     13        html += String(`
    +
    10250.     13<!DOCTYPE html>
    +
    10251.     13<html lang="en">
    +
    10252.     13<head>
    +
    10253.     13<title>V8 Coverage Report</title>
    +
    10254.     13<style>
    +
    10255.     13/* jslint utility2:true */
    +
    10256.     13/*csslint ignore:start*/
    +
    10257.     13* {
    +
    10258.     13box-sizing: border-box;
    +
    10259.     13    font-family: consolas, menlo, monospace;
    +
    10260.     13}
    +
    10261.     13/*csslint ignore:end*/
    +
    10262.     13
    +
    10263.     13/* css - coverage_report - general */
    +
    10264.     13body {
    +
    10265.     13    margin: 0;
    +
    10266.     13}
    +
    10267.     13.coverage pre {
    +
    10268.     13    margin: 5px 0;
    +
    10269.     13}
    +
    10270.     13.coverage table {
    +
    10271.     13    border-collapse: collapse;
    +
    10272.     13}
    +
    10273.     13.coverage td,
    +
    10274.     13.coverage th {
    +
    10275.     13    border: 1px solid #777;
    +
    10276.     13    line-height: 20px;
    +
    10277.     13    margin: 0;
    +
    10278.     13    padding: 5px 10px;
    +
    10279.     13}
    +
    10280.     13.coverage td span {
    +
    10281.     13    display: inline-block;
    +
    10282.     13    width: 100%;
    +
    10283.     13}
    +
    10284.     13.coverage .content {
    +
    10285.     13    padding: 0 5px;
    +
    10286.     13}
    +
    10287.     13.coverage .content a {
    +
    10288.     13    text-decoration: none;
    +
    10289.     13}
    +
    10290.     13.coverage .count {
    +
    10291.     13    margin: 0 5px;
    +
    10292.     13    padding: 0 5px;
    +
    10293.     13}
    +
    10294.     13.coverage .footer,
    +
    10295.     13.coverage .header {
    +
    10296.     13    padding: 20px;
    +
    10297.     13}
    +
    10298.     13.coverage .footer {
    +
    10299.     13    text-align: center;
    +
    10300.     13}
    +
    10301.     13.coverage .percentbar {
    +
    10302.     13    height: 12px;
    +
    10303.     13    margin: 2px 0;
    +
    10304.     13    min-width: 200px;
    +
    10305.     13    position: relative;
    +
    10306.     13    width: 100%;
    +
    10307.     13}
    +
    10308.     13.coverage .percentbar div {
    +
    10309.     13    height: 100%;
    +
    10310.     13    position: absolute;
    +
    10311.     13}
    +
    10312.     13.coverage .title {
    +
    10313.     13    font-size: large;
    +
    10314.     13    font-weight: bold;
    +
    10315.     13    margin-bottom: 10px;
    +
    10316.     13}
    +
    10317.     13
    +
    10318.     13/* css - coverage_report - color */
    +
    10319.     13.coverage td,
    +
    10320.     13.coverage th {
    +
    10321.     13    background: #fff;
    +
    10322.     13}
    +
    10323.     13.coverage .count,
    +
    10324.     13.coverage .coverageHigh {
    +
    10325.     13    background: #9d9;
    +
    10326.     13}
    +
    10327.     13.coverage .count {
    +
    10328.     13    color: #666;
    +
    10329.     13}
    +
    10330.     13.coverage .coverageIgnore {
    +
    10331.     13    background: #ccc;
    +
    10332.     13}
    +
    10333.     13.coverage .coverageLow,
    +
    10334.     13.coverage .uncovered {
    +
    10335.     13    background: #ebb;
    +
    10336.     13}
    +
    10337.     13.coverage .coverageMedium {
    +
    10338.     13    background: #fd7;
    +
    10339.     13}
    +
    10340.     13.coverage .footer,
    +
    10341.     13.coverage .header,
    +
    10342.     13.coverage .lineno {
    +
    10343.     13    background: #ddd;
    +
    10344.     13}
    +
    10345.     13.coverage .percentbar {
    +
    10346.     13    background: #999;
    +
    10347.     13}
    +
    10348.     13.coverage .percentbar div {
    +
    10349.     13    background: #666;
    +
    10350.     13}
    +
    10351.     13
    +
    10352.     13/* css - coverage_report - important */
    +
    10353.     13.coverage pre:hover span,
    +
    10354.     13.coverage tr:hover td {
    +
    10355.     13    background: #7d7;
    +
    10356.     13}
    +
    10357.     13.coverage pre:hover span.uncovered,
    +
    10358.     13.coverage tr:hover td.coverageLow {
    +
    10359.     13    background: #f99;
    +
    10360.     13}
    +
    10361.     13</style>
    +
    10362.     13</head>
    +
    10363.     13<body class="coverage">
    +
    10364.     13<!-- header start -->
    +
    10365.     13<div class="header">
    +
    10366.     13<div class="title">V8 Coverage Report</div>
    +
    10367.     13<table>
    +
    10368.     13<thead>
    +
    10369.     13    <tr>
    +
    10370.     13    <th>Files covered</th>
    +
    10371.     13    <th>Lines</th>
    +
    10372.     13    <th>Remaining</th>
    +
    10373.     13    </tr>
    +
    10374.     13</thead>
    +
    10375.     13<tbody>
    +
    10376.     13        `).trim() + "\n";
    +
    10377.      7        if (modeIndex) {
    +
    10378.      7            padLines = String("(ignore) 100.00 %").length;
    +
    10379.      7            padPathname = 32;
    +
    10380.      7            fileList.unshift({
    +
    10381.      7                linesCovered: 0,
    +
    10382.      7                linesTotal: 0,
    +
    10383.      7                modeCoverageIgnoreFile: "",
    +
    10384.      7                pathname: "./"
    +
    10385.      7            });
    +
    10386.      7            fileList.slice(1).forEach(function ({
    +
    10387.      7                linesCovered,
    +
    10388.      7                linesTotal,
    +
    10389.      7                modeCoverageIgnoreFile,
    +
    10390.      7                pathname
    +
    10391.      7            }) {
    +
    10392.      7                if (!modeCoverageIgnoreFile) {
    +
    10393.      7                    fileList[0].linesCovered += linesCovered;
    +
    10394.      7                    fileList[0].linesTotal += linesTotal;
    +
    10395.      7                }
    +
    10396.      7                padPathname = Math.max(padPathname, pathname.length + 2);
    +
    10397.      7                padLines = Math.max(
    +
    10398.      7                    padLines,
    +
    10399.      7                    String(linesCovered + " / " + linesTotal).length
    +
    10400.      7                );
    +
    10401.      7            });
    +
    10402.      7        }
    +
    10403.     13        txtBorder = (
    +
    10404.     13            "+" + "-".repeat(padPathname + 2) + "+"
    +
    10405.     13            + "-".repeat(padLines + 2) + "+\n"
    +
    10406.     13        );
    +
    10407.     13        txt = "";
    +
    10408.     13        txt += "V8 Coverage Report\n";
    +
    10409.     13        txt += txtBorder;
    +
    10410.     13        txt += (
    +
    10411.     13            "| " + String("Files covered").padEnd(padPathname, " ") + " | "
    +
    10412.     13            + String("Lines").padStart(padLines, " ") + " |\n"
    +
    10413.     13        );
    +
    10414.     13        txt += txtBorder;
    +
    10415.     19        fileList.forEach(function ({
    +
    10416.     19            linesCovered,
    +
    10417.     19            linesTotal,
    +
    10418.     19            modeCoverageIgnoreFile,
    +
    10419.     19            pathname
    +
    10420.     19        }, ii) {
    +
    10421.     19            let coverageLevel;
    +
    10422.     19            let coveragePct;
    +
    10423.     19            let fill;
    +
    10424.     19            let str1;
    +
    10425.     19            let str2;
    +
    10426.     19            let xx1;
    +
    10427.     19            let xx2;
    +
    10428.      2            coveragePct = Math.floor(10000 * linesCovered / linesTotal || 0);
    +
    10429.     19            coverageLevel = (
    +
    10430.     19                modeCoverageIgnoreFile
    +
    10431.      2                ? "coverageIgnore"
    +
    10432.     17                : coveragePct >= 8000
    +
    10433.     17                ? "coverageHigh"
    +
    10434.     17                : coveragePct >= 5000
    +
    10435.     17                ? "coverageMedium"
    +
    10436.     17                : "coverageLow"
    +
    10437.     19            );
    +
    10438.     19            coveragePct = String(coveragePct).replace((
    +
    10439.     19                /..$/m
    +
    10440.     19            ), ".$&");
    +
    10441.     13            if (modeIndex && ii === 0) {
    +
    10442.      7                fill = (
    +
    10443.      7
    +
    10444.      7// Badge-color rgb-red.
    +
    10445.      7
    +
    10446.      7                    "#" + Math.round(
    +
    10447.      7                        (100 - Number(coveragePct)) * 2.21
    +
    10448.      7                    ).toString(16).padStart(2, "0")
    +
    10449.      7
    +
    10450.      7// Badge-color rgb-green.
    +
    10451.      7
    +
    10452.      7                    + Math.round(
    +
    10453.      7                        Number(coveragePct) * 2.21
    +
    10454.      7                    ).toString(16).padStart(2, "0")
    +
    10455.      7
    +
    10456.      7// Badge-color rgb-blue.
    +
    10457.      7
    +
    10458.      7                    + "00"
    +
    10459.      7                );
    +
    10460.      7                str1 = "coverage";
    +
    10461.      7                str2 = coveragePct + " %";
    +
    10462.      7                xx1 = 6 * str1.length + 20;
    +
    10463.      7                xx2 = 6 * str2.length + 20;
    +
    10464.      7
    +
    10465.      7// Fs - write coverage_badge.svg.
    +
    10466.      7
    +
    10467.      7                promiseList.push(fsWriteFileWithParents((
    +
    10468.      7                    coverageDir + "coverage_badge.svg"
    +
    10469.      7                ), String(`
    +
    10470.      7<svg height="20" width="${xx1 + xx2}" xmlns="http://www.w3.org/2000/svg">
    +
    10471.      7<rect fill="#555" height="20" width="${xx1 + xx2}"/>
    +
    10472.      7<rect fill="${fill}" height="20" width="${xx2}" x="${xx1}"/>
    +
    10473.      7<g
    +
    10474.      7    fill="#fff"
    +
    10475.      7    font-family="verdana, geneva, dejavu sans, sans-serif"
    +
    10476.      7    font-size="11"
    +
    10477.      7    font-weight="bold"
    +
    10478.      7    text-anchor="middle"
    +
    10479.      7>
    +
    10480.      7<text x="${0.5 * xx1}" y="14">${str1}</text>
    +
    10481.      7<text x="${xx1 + 0.5 * xx2}" y="14">${str2}</text>
    +
    10482.      7</g>
    +
    10483.      7</svg>
    +
    10484.      7                `).trim() + "\n"));
    +
    10485.      7                pathname = "";
    +
    10486.      7            }
    +
    10487.     19            txt += (
    +
    10488.     19                "| "
    +
    10489.     19                + String("./" + pathname).padEnd(padPathname, " ") + " | "
    +
    10490.     19                + String(
    +
    10491.     19                    modeCoverageIgnoreFile + " " + coveragePct + " %"
    +
    10492.     19                ).padStart(padLines, " ") + " |\n"
    +
    10493.     19            );
    +
    10494.     19            txt += (
    +
    10495.     19                "| " + "*".repeat(
    +
    10496.     19                    Math.round(0.01 * coveragePct * padPathname)
    +
    10497.     19                ).padEnd(padPathname, "_") + " | "
    +
    10498.     19                + String(
    +
    10499.     19                    linesCovered + " / " + linesTotal
    +
    10500.     19                ).padStart(padLines, " ") + " |\n"
    +
    10501.     19            );
    +
    10502.     19            txt += txtBorder;
    +
    10503.     19            pathname = htmlEscape(pathname);
    +
    10504.     19
    +
    10505.     19// CL-37251d17 - Bugfix - Fix incorrect http-link to index.html.
    +
    10506.     19
    +
    10507.     19            html += String(`
    +
    10508.     19    <tr>
    +
    10509.     19    <td class="${coverageLevel}">
    +
    10510.     19            ${(
    +
    10511.     19                modeIndex
    +
    10512.     13                ? (
    +
    10513.     13                    "<a href=\"" + (pathname || "index") + ".html\">. / "
    +
    10514.     13                    + pathname + "</a><br>"
    +
    10515.     13                )
    +
    10516.      6                : (
    +
    10517.      6                    "<a href=\""
    +
    10518.      6                    + "../".repeat(pathname.split("/").length - 1)
    +
    10519.      6                    + "index.html\">. / </a>"
    +
    10520.      6                    + pathname + "<br>"
    +
    10521.      6                )
    +
    10522.     19            )}
    +
    10523.     19        <div class="percentbar">
    +
    10524.     19            <div style="width: ${coveragePct}%;"></div>
    +
    10525.     19        </div>
    +
    10526.     19    </td>
    +
    10527.     19    <td style="text-align: right;">
    +
    10528.     19        ${modeCoverageIgnoreFile} ${coveragePct} %<br>
    +
    10529.     19        ${linesCovered} / ${linesTotal}
    +
    10530.     19    </td>
    +
    10531.     19    <td style="text-align: right;">
    +
    10532.     19        <br>
    +
    10533.     19        ${linesTotal - linesCovered} / ${linesTotal}
    +
    10534.     19    </td>
    +
    10535.     19    </tr>
    +
    10536.     19        `).trim() + "\n";
    +
    10537.     19        });
    +
    10538.     13        html += String(`
    +
    10539.     13</tbody>
    +
    10540.     13</table>
    +
    10541.     13</div>
    +
    10542.     13<!-- header end -->
    +
    10543.     13        `).trim() + "\n";
    +
    10544.      6        if (!modeIndex) {
    +
    10545.      6            html += String(`
    +
    10546.      6<!-- content start -->
    +
    10547.      6<div class="content">
    +
    10548.      6            `).trim() + "\n";
    +
    10549.  11284            lineList.forEach(function ({
    +
    10550.  11284                count,
    +
    10551.  11284                holeList,
    +
    10552.  11284                line,
    +
    10553.  11284                startOffset
    +
    10554.  11284            }, ii) {
    +
    10555.  11284                let chunk;
    +
    10556.  11284                let inHole;
    +
    10557.  11284                let lineHtml;
    +
    10558.  11284                let lineId;
    +
    10559.  11284                lineHtml = "";
    +
    10560.  11284                lineId = "line_" + (ii + 1);
    +
    10561.  11284                switch (count) {
    +
    10562.     32                case -1:
    +
    10563.  10746                case 0:
    +
    10564.  10746                    if (holeList.length === 0) {
    +
    10565.  10746                        lineHtml += "</span>";
    +
    10566.  10746                        lineHtml += "<span class=\"uncovered\">";
    +
    10567.  10746                        lineHtml += htmlEscape(line);
    +
    10568.  10746                        break;
    +
    10569.  10746                    }
    +
    10570.  10746                    line = line.split("").map(function (char) {
    +
    10571.  10746                        return {
    +
    10572.  10746                            char,
    +
    10573.  10746                            isHole: undefined
    +
    10574.  10746                        };
    +
    10575.  10746                    });
    +
    10576.  10746                    holeList.forEach(function ([
    +
    10577.  10746                        aa, bb
    +
    10578.  10746                    ]) {
    +
    10579.  10746                        aa = Math.max(aa - startOffset, 0);
    +
    10580.  10746                        bb = Math.min(bb - startOffset, line.length);
    +
    10581.  10746                        while (aa < bb) {
    +
    10582.  10746                            line[aa].isHole = true;
    +
    10583.  10746                            aa += 1;
    +
    10584.  10746                        }
    +
    10585.  10746                    });
    +
    10586.  10746                    chunk = "";
    +
    10587.  10746                    line.forEach(function ({
    +
    10588.  10746                        char,
    +
    10589.  10746                        isHole
    +
    10590.  10746                    }) {
    +
    10591.  10746                        if (inHole !== isHole) {
    +
    10592.  10746                            lineHtml += htmlEscape(chunk);
    +
    10593.  10746                            lineHtml += "</span><span";
    +
    10594.  10746
    +
    10595.  10746// Coverage-hack - Ugly-hack around possible deadcode where isHole is always
    +
    10596.  10746// true.
    +
    10597.  10746
    +
    10598.  10746                            if (isHole) {
    +
    10599.  10746                                lineHtml += " class=\"uncovered\"";
    +
    10600.  10746                            }
    +
    10601.  10746                            lineHtml += ">";
    +
    10602.  10746                            chunk = "";
    +
    10603.  10746                            inHole = isHole;
    +
    10604.  10746                        }
    +
    10605.  10746                        chunk += char;
    +
    10606.  10746                    });
    +
    10607.  10746                    lineHtml += htmlEscape(chunk);
    +
    10608.  10746                    break;
    +
    10609.    538                default:
    +
    10610.    538                    lineHtml += htmlEscape(line);
    +
    10611.  11284                }
    +
    10612.  11284                html += String(`
    +
    10613.  11284<pre>
    +
    10614.  11284<span class="lineno">
    +
    10615.  11284<a href="#${lineId}" id="${lineId}">${String(ii + 1).padStart(5, " ")}.</a>
    +
    10616.  11284</span>
    +
    10617.  11284<span class="count
    +
    10618.  11284                ${(
    +
    10619.  11284                    count <= 0
    +
    10620.  10746                    ? "uncovered"
    +
    10621.    538                    : ""
    +
    10622.  11284                )}"
    +
    10623.  11284>
    +
    10624.  10714${String(count || "-0").padStart(7, " ")}
    +
    10625.  11284</span>
    +
    10626.  11284<span>${lineHtml}</span>
    +
    10627.  11284</pre>
    +
    10628.  11284                `).replace((
    +
    10629.  11284                    /\n/g
    +
    10630.  11284                ), "").trim() + "\n";
    +
    10631.  11284            });
    +
    10632.      6            html += String(`
    +
    10633.      6</div>
    +
    10634.      6<!-- content end -->
    +
    10635.      6            `).trim() + "\n";
    +
    10636.      6        }
    +
    10637.     13        html += String(`
    +
    10638.     13<div class="footer">
    +
    10639.     13    [
    +
    10640.     13    This document was created with
    +
    10641.     13    <a href="https://github.com/jslint-org/jslint">JSLint</a>
    +
    10642.     13    ]
    +
    10643.     13</div>
    +
    10644.     13</body>
    +
    10645.     13</html>
    +
    10646.     13        `).trim() + "\n";
    +
    10647.     13
    +
    10648.     13// Fs - write <file>.html.
    +
    10649.     13
    +
    10650.     13        promiseList.push(fsWriteFileWithParents(pathname + ".html", html));
    +
    10651.      6        if (!modeIndex) {
    +
    10652.      6            return;
    +
    10653.      7        }
    +
    10654.      7
    +
    10655.      7// Fs - write coverage_report.txt.
    +
    10656.      7
    +
    10657.      7        consoleError("\n" + txt);
    +
    10658.      7        promiseList.push(fsWriteFileWithParents((
    +
    10659.      7            coverageDir + "coverage_report.txt"
    +
    10660.      7        ), txt));
    +
    10661.      7    }
    +
    10662.      8
    +
    10663.    386    function pathnameRelativeCwd(pathname) {
    +
    10664.    386
    +
    10665.    386// This function will if <pathname> is inside <cwd>,
    +
    10666.    386// return it relative to <cwd>, else empty-string.
    +
    10667.    386
    +
    10668.    386        pathname = modulePath.resolve(pathname).replace((
    +
    10669.    386            /\\/g
    +
    10670.    386        ), "/");
    +
    10671.    380        if (!pathname.startsWith(cwd)) {
    +
    10672.    380            return;
    +
    10673.    380        }
    +
    10674.      6        pathname = pathname.slice(cwd.length);
    +
    10675.      6        return pathname;
    +
    10676.      6    }
    +
    10677.      8
    +
    10678.      8/*
    +
    10679.      8function sentinel() {}
    +
    10680.      8*/
    +
    10681.      8
    +
    10682.      8    await moduleFsInit();
    +
    10683.      8    consoleError = consoleError || console.error;
    +
    10684.      8    cwd = process.cwd().replace((
    +
    10685.      8        /\\/g
    +
    10686.      8    ), "/") + "/";
    +
    10687.      8
    +
    10688.      8// Init coverageDir.
    +
    10689.      8// Assert coverageDir is subdirectory of cwd.
    +
    10690.      8
    +
    10691.      8    assertOrThrow(coverageDir, "invalid coverageDir " + coverageDir);
    +
    10692.      8
    +
    10693.      8// CL-61b11012 - coverage - Relax requirement for coverageDir to be in cwd.
    +
    10694.      8//     assertOrThrow(
    +
    10695.      8//         pathnameRelativeCwd(coverageDir),
    +
    10696.      8//         "coverageDir " + coverageDir + " is not subdirectory of cwd " + cwd
    +
    10697.      8//     );
    +
    10698.      8
    +
    10699.      8    coverageDir = modulePath.resolve(coverageDir).replace((
    +
    10700.      8        /\\/g
    +
    10701.      8    ), "/") + "/";
    +
    10702.      8
    +
    10703.      8    processArgv = processArgv.slice();
    +
    10704.      9    while (processArgv[0] && processArgv[0][0] === "-") {
    +
    10705.      3        processArgElem = processArgv.shift().split("=");
    +
    10706.      3        processArgElem[1] = processArgElem.slice(1).join("=");
    +
    10707.      3        switch (processArgElem[0]) {
    +
    10708.      3
    +
    10709.      3// PR-371 - add cli-option `--exclude=aa,bb`
    +
    10710.      3
    +
    10711.      3        case "--exclude":
    +
    10712.      3            fileExcludeList = fileExcludeList.concat(
    +
    10713.      3                processArgElem[1].split(",")
    +
    10714.      3            );
    +
    10715.      3            break;
    +
    10716.      3
    +
    10717.      3// PR-371 - add cli-option `--exclude-node-modules=false`
    +
    10718.      3
    +
    10719.      3        case "--exclude-node-modules":
    +
    10720.      3            fileIncludeNodeModules = (
    +
    10721.      3                /0|false|null|undefined/
    +
    10722.      3            ).test(processArgElem[1]);
    +
    10723.      3            break;
    +
    10724.      3
    +
    10725.      3// PR-371 - add cli-option `--include=aa,bb`
    +
    10726.      3
    +
    10727.      3        case "--include":
    +
    10728.      3            fileIncludeList = fileIncludeList.concat(
    +
    10729.      3                processArgElem[1].split(",")
    +
    10730.      3            );
    +
    10731.      3            break;
    +
    10732.      3        }
    +
    10733.      7    }
    +
    10734.      7
    +
    10735.      7// 1. Spawn node.js program <processArgv> with coverage
    +
    10736.      7
    +
    10737.      7    if (processArgv.length > 0) {
    +
    10738.      6
    +
    10739.      6// Remove old coverage-files.
    +
    10740.      6
    +
    10741.      6        await fsWriteFileWithParents(coverageDir + "/touch.txt", "");
    +
    10742.      6        await Promise.all(Array.from(
    +
    10743.      6            await moduleFs.promises.readdir(coverageDir)
    +
    10744.     11        ).map(async function (file) {
    +
    10745.     11            if ((
    +
    10746.     11                /^coverage-\d+?-\d+?-\d+?\.json$/
    +
    10747.      6            ).test(file)) {
    +
    10748.      6                console.error("rm file " + coverageDir + file);
    +
    10749.      6                await moduleFs.promises.unlink(coverageDir + file);
    +
    10750.      6            }
    +
    10751.     11        }));
    +
    10752.      6        exitCode = await new Promise(function (resolve) {
    +
    10753.      6            moduleChildProcess.spawn((
    +
    10754.      6                processArgv[0] === "npm"
    +
    10755.      6
    +
    10756.      6// If win32 environment, then replace program npm with npm.cmd.
    +
    10757.      6// Coverage-hack - Ugly-hack to get test-coverage under both win32 and linux.
    +
    10758.      6
    +
    10759.      6                ? process.platform.replace("win32", "npm.cmd").replace(
    +
    10760.      6                    process.platform,
    +
    10761.      6                    "npm"
    +
    10762.      6                )
    +
    10763.      6                : processArgv[0]
    +
    10764.      6            ), processArgv.slice(1), {
    +
    10765.      6                env: Object.assign({}, process.env, {
    +
    10766.      6                    NODE_V8_COVERAGE: coverageDir
    +
    10767.      6                }),
    +
    10768.      6                stdio: [
    +
    10769.      6                    "ignore", 1, 2
    +
    10770.      6                ]
    +
    10771.      6            }).on("exit", resolve);
    +
    10772.      6        });
    +
    10773.      7    }
    +
    10774.      7
    +
    10775.      7// 2. Merge JSON v8-coverage-files in <coverageDir>.
    +
    10776.      7
    +
    10777.      7    v8CoverageObj = await moduleFs.promises.readdir(coverageDir);
    +
    10778.     18    v8CoverageObj = v8CoverageObj.filter(function (file) {
    +
    10779.     18        return (
    +
    10780.     18            /^coverage-\d+?-\d+?-\d+?\.json$/
    +
    10781.     18        ).test(file);
    +
    10782.     18    });
    +
    10783.      7    v8CoverageObj = await Promise.all(v8CoverageObj.map(async function (file) {
    +
    10784.      7        let data = await moduleFs.promises.readFile(coverageDir + file, "utf8");
    +
    10785.      7        data = JSON.parse(data);
    +
    10786.    752        data.result = data.result.filter(function (scriptCov) {
    +
    10787.    752            let pathname = scriptCov.url;
    +
    10788.    752
    +
    10789.    752// Filter out internal coverages.
    +
    10790.    752
    +
    10791.    366            if (!pathname.startsWith("file:///")) {
    +
    10792.    366                return;
    +
    10793.    386            }
    +
    10794.    386
    +
    10795.    386// Normalize pathname.
    +
    10796.    386
    +
    10797.    386            pathname = pathnameRelativeCwd(moduleUrl.fileURLToPath(pathname));
    +
    10798.    386            if (
    +
    10799.    386
    +
    10800.    386// Filter files outside of cwd.
    +
    10801.    386
    +
    10802.    386                !pathname
    +
    10803.    386                || pathname.startsWith("[")
    +
    10804.    752
    +
    10805.    752// PR-371 - Filter directory node_modules.
    +
    10806.    752
    +
    10807.      7                || (
    +
    10808.      7                    !fileIncludeNodeModules
    +
    10809.      7                    && (
    +
    10810.      7                        /(?:^|\/)node_modules\//m
    +
    10811.      7                    ).test(pathname)
    +
    10812.      7                )
    +
    10813.    752
    +
    10814.    752// PR-371 - Filter fileExcludeList.
    +
    10815.    752
    +
    10816.      7                || fileExcludeList.indexOf(pathname) >= 0
    +
    10817.    752
    +
    10818.    752// PR-371 - Filter fileIncludeList.
    +
    10819.    752
    +
    10820.      7                || (
    +
    10821.      7                    fileIncludeList.length > 0
    +
    10822.      7                    && fileIncludeList.indexOf(pathname) === -1
    +
    10823.      7                )
    +
    10824.    380            ) {
    +
    10825.    380                return;
    +
    10826.    380            }
    +
    10827.      7            scriptCov.url = pathname;
    +
    10828.      7            return true;
    +
    10829.      7        });
    +
    10830.      7        return data;
    +
    10831.      7    }));
    +
    10832.      7
    +
    10833.      7// Merge v8CoverageObj.
    +
    10834.      7
    +
    10835.      7    v8CoverageObj = v8CoverageListMerge(v8CoverageObj);
    +
    10836.      7
    +
    10837.      7// debug v8CoverageObj.
    +
    10838.      7
    +
    10839.      7    await fsWriteFileWithParents(
    +
    10840.      7        coverageDir + "v8_coverage_merged.json",
    +
    10841.      7        JSON.stringify(v8CoverageObj, undefined, 1)
    +
    10842.      7    );
    +
    10843.      7
    +
    10844.      7// 3. Create html-coverage-reports in <coverageDir>.
    +
    10845.      7
    +
    10846.      7    fileDict = {};
    +
    10847.      7    await Promise.all(v8CoverageObj.result.map(async function ({
    +
    10848.      7        functions,
    +
    10849.      7        url: pathname
    +
    10850.      7    }) {
    +
    10851.      7        let lineList;
    +
    10852.      7        let linesCovered;
    +
    10853.      7        let linesTotal;
    +
    10854.      7        let source;
    +
    10855.      7        source = await moduleFs.promises.readFile(pathname, "utf8");
    +
    10856.      7        lineList = [{}];
    +
    10857.      7        source.replace((
    +
    10858.      7            /^.*$/gm
    +
    10859.  11284        ), function (line, startOffset) {
    +
    10860.  11284            lineList[lineList.length - 1].endOffset = startOffset - 1;
    +
    10861.  11284            lineList.push({
    +
    10862.  11284                count: -1,
    +
    10863.  11284                endOffset: 0,
    +
    10864.  11284                holeList: [],
    +
    10865.  11284                line,
    +
    10866.  11284                startOffset
    +
    10867.  11284            });
    +
    10868.  11284            return "";
    +
    10869.  11284        });
    +
    10870.      7        lineList.shift();
    +
    10871.      7        lineList[lineList.length - 1].endOffset = source.length;
    +
    10872.     40        functions.reverse().forEach(function ({
    +
    10873.     40            ranges
    +
    10874.     40        }) {
    +
    10875.     62            ranges.reverse().forEach(function ({
    +
    10876.     62                count,
    +
    10877.     62                endOffset,
    +
    10878.     62                startOffset
    +
    10879.     62            }, ii, list) {
    +
    10880. 518582                lineList.forEach(function (elem) {
    +
    10881. 518582                    if (!(
    +
    10882. 518582                        (
    +
    10883. 518582                            elem.startOffset <= startOffset
    +
    10884. 184808                            && startOffset <= elem.endOffset
    +
    10885. 518520                        ) || (
    +
    10886. 518520                            elem.startOffset <= endOffset
    +
    10887. 518520                            && endOffset <= elem.endOffset
    +
    10888. 518520                        ) || (
    +
    10889. 518466                            startOffset <= elem.startOffset
    +
    10890. 518466                            && elem.endOffset <= endOffset
    +
    10891. 518466                        )
    +
    10892. 496089                    )) {
    +
    10893. 496089                        return;
    +
    10894. 496089                    }
    +
    10895.  22493
    +
    10896.  22493// Handle tree-root.
    +
    10897.  22493
    +
    10898.  22493                    if (ii + 1 === list.length) {
    +
    10899.  22231                        if (elem.count === -1) {
    +
    10900.  22231                            elem.count = count;
    +
    10901.  22231                        }
    +
    10902.  22231                        return;
    +
    10903.  22231                    }
    +
    10904.    262
    +
    10905.    262// Handle tree-children.
    +
    10906.    262
    +
    10907.    262                    if (elem.count !== 0) {
    +
    10908.    163                        elem.count = Math.max(count, elem.count);
    +
    10909.    262                    }
    +
    10910.    262                    if (count === 0) {
    +
    10911.    196                        elem.count = 0;
    +
    10912.    196                        elem.holeList.push([
    +
    10913.    196                            startOffset, endOffset
    +
    10914.    196                        ]);
    +
    10915.    196                    }
    +
    10916. 518582                });
    +
    10917.     62            });
    +
    10918.     40        });
    +
    10919.      7        linesTotal = lineList.length;
    +
    10920.  11284        linesCovered = lineList.filter(function ({
    +
    10921.  11284            count
    +
    10922.  11284        }) {
    +
    10923.  11284            return count > 0;
    +
    10924.  11284        }).length;
    +
    10925.      7        await moduleFs.promises.mkdir((
    +
    10926.      7            modulePath.dirname(coverageDir + pathname)
    +
    10927.      7        ), {
    +
    10928.      7            recursive: true
    +
    10929.      7        });
    +
    10930.      7        fileDict[pathname] = {
    +
    10931.      7            lineList,
    +
    10932.      7            linesCovered,
    +
    10933.      7            linesTotal,
    +
    10934.      7            modeCoverageIgnoreFile: (
    +
    10935.      7                (
    +
    10936.      7                    /^\/\*coverage-ignore-file\*\/$/m
    +
    10937.      7                ).test(source.slice(0, 65536))
    +
    10938.      7                ? "(ignore)"
    +
    10939.      7                : ""
    +
    10940.      7            ),
    +
    10941.      7            pathname
    +
    10942.      7        };
    +
    10943.      7        htmlRender({
    +
    10944.      7            fileList: [
    +
    10945.      7                fileDict[pathname]
    +
    10946.      7            ],
    +
    10947.      7            lineList,
    +
    10948.      7            pathname: coverageDir + pathname
    +
    10949.      7        });
    +
    10950.      7    }));
    +
    10951.      7    htmlRender({
    +
    10952.      7        fileList: Object.keys(fileDict).sort().map(function (pathname) {
    +
    10953.      7            return fileDict[pathname];
    +
    10954.      7        }),
    +
    10955.      7        modeIndex: true,
    +
    10956.      7        pathname: coverageDir + "index"
    +
    10957.      7    });
    +
    10958.      7    await Promise.all(promiseList);
    +
    10959.      7    assertOrThrow(
    +
    10960.      7        exitCode === 0,
    +
    10961.      7        "v8CoverageReportCreate - nonzero exitCode " + exitCode
    +
    10962.      7    );
    +
    10963.      7}
    +
    10964.      1
    +
    10965.      1/*
    +
    10966.      1function sentinel() {}
    +
    10967.      1*/
    +
    10968.      1
    +
    10969.      1// Export jslint as cjs/esm.
    +
    10970.      1
    +
    10971.      1jslint_export = Object.freeze(Object.assign(jslint, {
    +
    10972.      1    assertErrorThrownAsync,
    +
    10973.      1    assertJsonEqual,
    +
    10974.      1    assertOrThrow,
    +
    10975.      1    debugInline,
    +
    10976.      1    fsWriteFileWithParents,
    +
    10977.      1    htmlEscape,
    +
    10978.      1    jslint,
    +
    10979.      1    jslint_apidoc,
    +
    10980.      1    jslint_assert,
    +
    10981.      1    jslint_charset_ascii,
    +
    10982.      1    jslint_cli,
    +
    10983.      1    jslint_edition,
    +
    10984.      1    jslint_phase1_split,
    +
    10985.      1    jslint_phase2_lex,
    +
    10986.      1    jslint_phase3_parse,
    +
    10987.      1    jslint_phase4_walk,
    +
    10988.      1    jslint_phase5_whitage,
    +
    10989.      1    jslint_report,
    +
    10990.      1    jstestDescribe,
    +
    10991.      1    jstestIt,
    +
    10992.      1    jstestOnExit,
    +
    10993.      1    moduleFsInit,
    +
    10994.      1    noop,
    +
    10995.      1    objectDeepCopyWithKeysSorted,
    +
    10996.      1    v8CoverageListMerge,
    +
    10997.      1    v8CoverageReportCreate
    +
    10998.      1}));
    +
    10999.      1// module.exports = jslint_export;              // Export jslint as cjs.
    +
    11000.      1export default Object.freeze(jslint_export);    // Export jslint as esm.
    +
    11001.      1jslint_import_meta_url = import.meta.url;
    +
    11002.      1
    +
    11003.      1// Run jslint_cli.
    +
    11004.      1jslint_cli({});
    +
    11005.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage/test.mjs.html b/branch-sandbox/.artifact/coverage/test.mjs.html new file mode 100644 index 000000000..8ae746708 --- /dev/null +++ b/branch-sandbox/.artifact/coverage/test.mjs.html @@ -0,0 +1,1226 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test.mjs
    +
    +
    +
    +
    + 100.00 %
    + 1068 / 1068 +
    +
    + 0 / 1068 +
    +
    + + +
    +
        1.      1/*jslint beta, node*/
    +
        2.      1import jslint from "./jslint.mjs";
    +
        3.      1import jslintCjs from "./jslint.cjs";
    +
        4.      1import moduleFs from "fs";
    +
        5.      1import modulePath from "path";
    +
        6.      1
    +
        7.      1let {
    +
        8.      1    assertErrorThrownAsync,
    +
        9.      1    assertJsonEqual,
    +
       10.      1    assertOrThrow,
    +
       11.      1    debugInline,
    +
       12.      1    fsWriteFileWithParents,
    +
       13.      1    jstestDescribe,
    +
       14.      1    jstestIt,
    +
       15.      1    jstestOnExit,
    +
       16.      1    moduleFsInit,
    +
       17.      1    noop,
    +
       18.      1    v8CoverageListMerge,
    +
       19.      1    v8CoverageReportCreate
    +
       20.      1} = jslint;
    +
       21.      1
    +
       22.      4function processExit0(exitCode) {
    +
       23.      4    assertOrThrow(exitCode === 0, exitCode);
    +
       24.      4}
    +
       25.      6function processExit1(exitCode) {
    +
       26.      6    assertOrThrow(exitCode === 1, exitCode);
    +
       27.      6}
    +
       28.      1
    +
       29.      1// Coverage-hack - Ugly-hack to get test-coverage for all initialization-states.
    +
       30.      1
    +
       31.      1moduleFsInit();
    +
       32.      1moduleFsInit();
    +
       33.      1
    +
       34.      1// Cleanup directory .tmp
    +
       35.      1// await moduleFs.promises.rm(".tmp", {
    +
       36.      1//     recursive: true
    +
       37.      1// }).catch(noop);
    +
       38.      1//
    +
       39.      1// TODO //jslint-quiet
    +
       40.      1// Replace code below with code above, after nodejs-v12 is deprecated
    +
       41.      1
    +
       42.      1try {
    +
       43.      1    moduleFs.rmSync(".tmp", { //jslint-quiet
    +
       44.      1        recursive: true
    +
       45.      1    });
    +
       46.      1    assertOrThrow(undefined);
    +
       47.      1} catch (ignore) {}
    +
       48.      1
    +
       49.      1(function testcaseFsXxx() {
    +
       50.      1
    +
       51.      1// This function will test fsXxx's handling-behavior.
    +
       52.      1
    +
       53.      1    jstestDescribe((
    +
       54.      1        "test fsXxx's handling-behavior"
    +
       55.      1    ), function () {
    +
       56.      1        jstestIt((
    +
       57.      1            "test fsWriteFileWithParents's handling-behavior"
    +
       58.      1        ), async function () {
    +
       59.      1            await Promise.all([
    +
       60.      1                1, 2, 3, 4
    +
       61.      4            ].map(async function () {
    +
       62.      4                await fsWriteFileWithParents(
    +
       63.      4                    ".tmp/fsWriteFileWithParents/aa/bb/cc",
    +
       64.      4                    "aa"
    +
       65.      4                );
    +
       66.      4            }));
    +
       67.      1            assertJsonEqual(
    +
       68.      1                await moduleFs.promises.readFile(
    +
       69.      1                    ".tmp/fsWriteFileWithParents/aa/bb/cc",
    +
       70.      1                    "utf8"
    +
       71.      1                ),
    +
       72.      1                "aa"
    +
       73.      1            );
    +
       74.      1        });
    +
       75.      1    });
    +
       76.      1}());
    +
       77.      1
    +
       78.      1(async function testcaseJslintCli() {
    +
       79.      1
    +
       80.      1// This function will test jslint's cli handling-behavior.
    +
       81.      1
    +
       82.      1    // test null-case handling-behavior
    +
       83.      1    jslint.jslint_cli({
    +
       84.      1        mode_noop: true,
    +
       85.      1        process_exit: processExit0
    +
       86.      1    });
    +
       87.      1    // test cjs-and-invalid-file handling-behavior
    +
       88.      1    await fsWriteFileWithParents(".test_dir.cjs/touch.txt", "");
    +
       89.      1    [
    +
       90.      1        ".",            // test dir handling-behavior
    +
       91.      1        "jslint.mjs",   // test file handling-behavior
    +
       92.      1        undefined       // test file-undefined handling-behavior
    +
       93.      3    ].forEach(function (file) {
    +
       94.      3        jslint.jslint_cli({
    +
       95.      3            file,
    +
       96.      3            mode_cli: true,
    +
       97.      3            process_env: {
    +
       98.      3                JSLINT_BETA: "1"
    +
       99.      3            },
    +
      100.      3            process_exit: processExit0
    +
      101.      3        });
    +
      102.      3    });
    +
      103.      1    // test apidoc handling-behavior
    +
      104.      1    jslint.jslint_cli({
    +
      105.      1        mode_cli: true,
    +
      106.      1        process_argv: [
    +
      107.      1            "node",
    +
      108.      1            "jslint.mjs",
    +
      109.      1            "jslint_apidoc=.artifact/apidoc.html",
    +
      110.      1            JSON.stringify({
    +
      111.      1                example_list: [
    +
      112.      1                    "README.md",
    +
      113.      1                    "test.mjs",
    +
      114.      1                    "jslint.mjs"
    +
      115.      1                ],
    +
      116.      1                github_repo: "https://github.com/jslint-org/jslint",
    +
      117.      1                module_list: [
    +
      118.      1                    {
    +
      119.      1                        pathname: "./jslint.mjs"
    +
      120.      1                    }
    +
      121.      1                ],
    +
      122.      1                package_name: "JSLint",
    +
      123.      1                version: jslint.jslint_edition
    +
      124.      1            })
    +
      125.      1        ],
    +
      126.      1        process_exit: processExit0
    +
      127.      1    });
    +
      128.      1    // test file-error handling-behavior
    +
      129.      1    jslint.jslint_cli({
    +
      130.      1        // suppress error
    +
      131.      1        console_error: noop,
    +
      132.      1        file: "undefined",
    +
      133.      1        mode_cli: true,
    +
      134.      1        process_exit: processExit1
    +
      135.      1    });
    +
      136.      1    // test syntax-error handling-behavior
    +
      137.      1    jslint.jslint_cli({
    +
      138.      1        // suppress error
    +
      139.      1        console_error: noop,
    +
      140.      1        file: "syntax-error.js",
    +
      141.      1        mode_cli: true,
    +
      142.      1        option: {
    +
      143.      1            trace: true
    +
      144.      1        },
    +
      145.      1        process_exit: processExit1,
    +
      146.      1        source: "syntax error"
    +
      147.      1    });
    +
      148.      1    // test report handling-behavior
    +
      149.      1    jslint.jslint_cli({
    +
      150.      1        // suppress error
    +
      151.      1        console_error: noop,
    +
      152.      1        mode_cli: true,
    +
      153.      1        process_argv: [
    +
      154.      1            "node",
    +
      155.      1            "jslint.mjs",
    +
      156.      1            "jslint_report=.tmp/jslint_report.html",
    +
      157.      1            "jslint.mjs"
    +
      158.      1        ],
    +
      159.      1        process_exit: processExit0
    +
      160.      1    });
    +
      161.      1    // test report-error handling-behavior
    +
      162.      1    jslint.jslint_cli({
    +
      163.      1        // suppress error
    +
      164.      1        console_error: noop,
    +
      165.      1        mode_cli: true,
    +
      166.      1        process_argv: [
    +
      167.      1            "node",
    +
      168.      1            "jslint.mjs",
    +
      169.      1            "jslint_report=.tmp/jslint_report.html",
    +
      170.      1            "syntax-error.js"
    +
      171.      1        ],
    +
      172.      1        process_exit: processExit1,
    +
      173.      1        source: "syntax error"
    +
      174.      1    });
    +
      175.      1    // test report-json handling-behavior
    +
      176.      1    jslint.jslint_cli({
    +
      177.      1        // suppress error
    +
      178.      1        console_error: noop,
    +
      179.      1        mode_cli: true,
    +
      180.      1        process_argv: [
    +
      181.      1            "node",
    +
      182.      1            "jslint.mjs",
    +
      183.      1            "jslint_report=.tmp/jslint_report.html",
    +
      184.      1            "aa.json"
    +
      185.      1        ],
    +
      186.      1        process_exit: processExit0,
    +
      187.      1        source: "[]"
    +
      188.      1    });
    +
      189.      1    // test report-misc handling-behavior
    +
      190.      1    jslint.jslint_cli({
    +
      191.      1        // suppress error
    +
      192.      1        console_error: noop,
    +
      193.      1        mode_cli: true,
    +
      194.      1        process_argv: [
    +
      195.      1            "node",
    +
      196.      1            "jslint.mjs",
    +
      197.      1            "jslint_report=.tmp/jslint_report.html",
    +
      198.      1            "aa.js"
    +
      199.      1        ],
    +
      200.      1        process_exit: processExit1,
    +
      201.      1        source: "(aa)=>aa; function aa([aa]){}"
    +
      202.      1    });
    +
      203.      1    // test report-json-error handling-behavior
    +
      204.      1    jslint.jslint_cli({
    +
      205.      1        // suppress error
    +
      206.      1        console_error: noop,
    +
      207.      1        mode_cli: true,
    +
      208.      1        process_argv: [
    +
      209.      1            "node",
    +
      210.      1            "jslint.mjs",
    +
      211.      1            "jslint_report=.tmp/jslint_report.html",
    +
      212.      1            "aa.json"
    +
      213.      1        ],
    +
      214.      1        process_exit: processExit1,
    +
      215.      1        source: "["
    +
      216.      1    });
    +
      217.      1    // test plugin-vim handling-behavior
    +
      218.      1    jslint.jslint_cli({
    +
      219.      1        // suppress error
    +
      220.      1        console_error: noop,
    +
      221.      1        mode_cli: true,
    +
      222.      1        process_argv: [
    +
      223.      1            "node",
    +
      224.      1            "jslint.mjs",
    +
      225.      1            "jslint_plugin_vim",
    +
      226.      1            "syntax-error.js"
    +
      227.      1        ],
    +
      228.      1        process_exit: processExit1,
    +
      229.      1        source: "syntax error"
    +
      230.      1    });
    +
      231.      1}());
    +
      232.      1
    +
      233.      1(function testcaseJslintCodeValidate() {
    +
      234.      1
    +
      235.      1// This function will validate each code is valid in jslint.
    +
      236.      1
    +
      237.      1    Object.values({
    +
      238.      1        array: [
    +
      239.      1            "new Array(0);"
    +
      240.      1        ],
    +
      241.      1        async_await: [
    +
      242.      1            "async function aa() {\n    await aa();\n}",
    +
      243.      1            (
    +
      244.      1                "async function aa() {\n"
    +
      245.      1                + "    try {\n"
    +
      246.      1                + "        aa();\n"
    +
      247.      1                + "    } catch (err) {\n"
    +
      248.      1                + "        await err();\n"
    +
      249.      1                + "    }\n"
    +
      250.      1                + "}\n"
    +
      251.      1            ), (
    +
      252.      1                "async function aa() {\n"
    +
      253.      1                + "    try {\n"
    +
      254.      1                + "        await aa();\n"
    +
      255.      1                + "    } catch (err) {\n"
    +
      256.      1                + "        await err();\n"
    +
      257.      1                + "    }\n"
    +
      258.      1                + "}\n"
    +
      259.      1            ),
    +
      260.      1
    +
      261.      1// PR-370 - Add top-level-await support.
    +
      262.      1
    +
      263.      1            "await String();\n"
    +
      264.      1        ],
    +
      265.      1
    +
      266.      1// PR-351 - Add BigInt support.
    +
      267.      1
    +
      268.      1        bigint: [
    +
      269.      1            "let aa = 0b0n;\n",
    +
      270.      1            "let aa = 0o0n;\n",
    +
      271.      1            "let aa = 0x0n;\n",
    +
      272.      1            "let aa = BigInt(0n);\n",
    +
      273.      1            "let aa = typeof aa === \"bigint\";\n"
    +
      274.      1        ],
    +
      275.      1        date: [
    +
      276.      1            "Date.getTime();",
    +
      277.      1            "let aa = aa().getTime();",
    +
      278.      1            "let aa = aa.aa().getTime();"
    +
      279.      1        ],
    +
      280.      1        directive: [
    +
      281.      1            "#!\n/*jslint browser:false, node*/\n\"use strict\";",
    +
      282.      1            "/*property aa bb*/"
    +
      283.      1        ],
    +
      284.      1        fart: [
    +
      285.      1            "function aa() {\n    return () => 0;\n}"
    +
      286.      1        ],
    +
      287.      1        for: [
    +
      288.      1            (
    +
      289.      1                "/*jslint for*/\n"
    +
      290.      1                + "function aa(bb) {\n"
    +
      291.      1                + "    for (bb = 0; bb < 0; bb += 1) {\n"
    +
      292.      1                + "        bb();\n"
    +
      293.      1                + "    }\n"
    +
      294.      1                + "}\n"
    +
      295.      1            )
    +
      296.      1        ],
    +
      297.      1        jslint_disable: [
    +
      298.      1            "/*jslint-disable*/\n0\n/*jslint-enable*/"
    +
      299.      1        ],
    +
      300.      1        jslint_quiet: [
    +
      301.      1            "0 //jslint-quiet"
    +
      302.      1        ],
    +
      303.      1        json: [
    +
      304.      1            "{\"aa\":[[],-0,null]}"
    +
      305.      1        ],
    +
      306.      1        label: [
    +
      307.      1            (
    +
      308.      1                "function aa() {\n"
    +
      309.      1                + "bb:\n"
    +
      310.      1                + "    while (true) {\n"
    +
      311.      1                + "        if (true) {\n"
    +
      312.      1                + "            break bb;\n"
    +
      313.      1                + "        }\n"
    +
      314.      1                + "    }\n"
    +
      315.      1                + "}\n"
    +
      316.      1            )
    +
      317.      1        ],
    +
      318.      1        loop: [
    +
      319.      1            (
    +
      320.      1                "function aa() {\n"
    +
      321.      1                + "    do {\n"
    +
      322.      1                + "        aa();\n"
    +
      323.      1                + "    } while (aa());\n"
    +
      324.      1                + "}\n"
    +
      325.      1            ),
    +
      326.      1
    +
      327.      1// PR-378 - Relax warning "function_in_loop".
    +
      328.      1
    +
      329.      1            (
    +
      330.      1                "function aa() {\n"
    +
      331.      1                + "    while (true) {\n"
    +
      332.      1                + "        (function () {\n"
    +
      333.      1                + "            return;\n"
    +
      334.      1                + "        }());\n"
    +
      335.      1                + "    }\n"
    +
      336.      1                + "}\n"
    +
      337.      1            )
    +
      338.      1        ],
    +
      339.      1        module: [
    +
      340.      1            "export default Object.freeze();",
    +
      341.      1            "import {aa, bb} from \"aa\";\naa(bb);",
    +
      342.      1            "import {} from \"aa\";",
    +
      343.      1            "import(\"aa\").then(function () {\n    return;\n});",
    +
      344.      1            "let aa = 0;\nimport(aa).then(aa).then(aa).catch(aa).finally(aa);"
    +
      345.      1        ],
    +
      346.      1        number: [
    +
      347.      1            "let aa = 0.0e0;",
    +
      348.      1            "let aa = 0b0;",
    +
      349.      1            "let aa = 0o0;",
    +
      350.      1            "let aa = 0x0;"
    +
      351.      1        ],
    +
      352.      1        optional_chaining: [
    +
      353.      1            "let aa = aa?.bb?.cc;"
    +
      354.      1        ],
    +
      355.      1        param: [
    +
      356.      1            (
    +
      357.      1                "function aa({aa, bb}) {\n"
    +
      358.      1                + "    return {aa, bb};\n"
    +
      359.      1                + "}\n"
    +
      360.      1            ), (
    +
      361.      1                "function aa({constructor}) {\n"
    +
      362.      1                + "    return {constructor};\n"
    +
      363.      1                + "}\n"
    +
      364.      1            )
    +
      365.      1        ],
    +
      366.      1        property: [
    +
      367.      1            "let aa = aa[`!`];"
    +
      368.      1        ],
    +
      369.      1        regexp: [
    +
      370.      1            "function aa() {\n    return /./;\n}",
    +
      371.      1            "let aa = /(?!.)(?:.)(?=.)/;",
    +
      372.      1            "let aa = /./gimuy;",
    +
      373.      1            "let aa = /[\\--\\-]/;"
    +
      374.      1        ],
    +
      375.      1        ternary: [
    +
      376.      1            (
    +
      377.      1                "let aa = (\n    aa()\n    ? 0\n    : 1\n) "
    +
      378.      1                + "&& (\n    aa()\n    ? 0\n    : 1\n);"
    +
      379.      1            ),
    +
      380.      1            "let aa = (\n    aa()\n    ? `${0}`\n    : `${1}`\n);"
    +
      381.      1        ],
    +
      382.      1        try_catch: [
    +
      383.      1            (
    +
      384.      1                "let aa = 0;\n"
    +
      385.      1                + "try {\n"
    +
      386.      1                + "    aa();\n"
    +
      387.      1                + "} catch (err) {\n"
    +
      388.      1                + "    aa = err;\n"
    +
      389.      1                + "}\n"
    +
      390.      1                + "try {\n"
    +
      391.      1                + "    aa();\n"
    +
      392.      1                + "} catch (err) {\n"
    +
      393.      1                + "    aa = err;\n"
    +
      394.      1                + "}\n"
    +
      395.      1                + "aa();\n"
    +
      396.      1            )
    +
      397.      1        ],
    +
      398.      1        use_strict: [
    +
      399.      1            (
    +
      400.      1                "\"use strict\";\n"
    +
      401.      1                + "let aa = 0;\n"
    +
      402.      1                + "function bb() {\n"
    +
      403.      1                + "    \"use strict\";\n"
    +
      404.      1                + "    return aa;\n"
    +
      405.      1                + "}\n"
    +
      406.      1            )
    +
      407.      1        ],
    +
      408.      1        var: [
    +
      409.      1
    +
      410.      1// PR-363 - Bugfix - add test against false-warning
    +
      411.      1// <uninitialized 'bb'> in code '/*jslint node*/\nlet {aa:bb} = {}; bb();'
    +
      412.      1
    +
      413.      1            "/*jslint node*/\n",
    +
      414.      1            ""
    +
      415.      2        ].map(function (directive) {
    +
      416.      2            return [
    +
      417.      2                "let [\n    aa, bb = 0\n] = 0;\naa();\nbb();",
    +
      418.      2                "let [...aa] = [...aa];\naa();",
    +
      419.      2                "let constructor = 0;\nconstructor();",
    +
      420.      2                "let {\n    aa: bb\n} = 0;\nbb();",
    +
      421.      2                "let {\n    aa: bb,\n    bb: cc\n} = 0;\nbb();\ncc();",
    +
      422.      2                "let {aa, bb} = 0;\naa();\nbb();",
    +
      423.      2                "let {constructor} = 0;\nconstructor();"
    +
      424.     14            ].map(function (code) {
    +
      425.     14                return directive + code;
    +
      426.     14            });
    +
      427.      2        }).flat()
    +
      428.     22    }).forEach(function (codeList) {
    +
      429.     22        let elemPrv = "";
    +
      430.     58        codeList.forEach(function (code) {
    +
      431.     58            let warnings;
    +
      432.     58            // Assert codeList is sorted.
    +
      433.     58            assertOrThrow(elemPrv < code, JSON.stringify([
    +
      434.     58                elemPrv, code
    +
      435.     58            ], undefined, 4));
    +
      436.     58            elemPrv = code;
    +
      437.     58            [
    +
      438.     58                jslint.jslint,
    +
      439.     58                jslintCjs.jslint
    +
      440.    116            ].forEach(function (jslint) {
    +
      441.    116                warnings = jslint.jslint(code, {
    +
      442.    116                    beta: true
    +
      443.    116                }).warnings;
    +
      444.    116                assertOrThrow(
    +
      445.    116                    warnings.length === 0,
    +
      446.    116                    JSON.stringify([code, warnings])
    +
      447.    116                );
    +
      448.    116            });
    +
      449.     58        });
    +
      450.     22    });
    +
      451.      1}());
    +
      452.      1
    +
      453.      1(async function testcaseJslintOption() {
    +
      454.      1
    +
      455.      1// This function will test jslint's option handling-behavior.
    +
      456.      1
    +
      457.      1    let elemPrv = "";
    +
      458.      1    [
    +
      459.      1        [
    +
      460.      1            "let aa = aa | 0;", {bitwise: true}, []
    +
      461.      1        ], [
    +
      462.      1            ";\naa(new XMLHttpRequest());", {browser: true}, ["aa"]
    +
      463.      1        ], [
    +
      464.      1            "let aa = \"aa\" + 0;", {convert: true}, []
    +
      465.      1        ], [
    +
      466.      1            "registerType();", {couch: true}, []
    +
      467.      1        ], [
    +
      468.      1            "debugger;", {devel: true}, []
    +
      469.      1        ], [
    +
      470.      1            "new Function();\neval();", {eval: true}, []
    +
      471.      1        ], [
    +
      472.      1            (
    +
      473.      1                "function aa(aa) {\n"
    +
      474.      1                + "    for (aa = 0; aa < 0; aa += 1) {\n"
    +
      475.      1                + "        aa();\n"
    +
      476.      1                + "    }\n"
    +
      477.      1                + "}\n"
    +
      478.      1            ), {for: true}, []
    +
      479.      1        ], [
    +
      480.      1            "let aa = {get aa() {\n    return;\n}};", {getset: true}, []
    +
      481.      1        ], [
    +
      482.      1            "let aa = {set aa(aa) {\n    return aa;\n}};", {getset: true}, []
    +
      483.      1        ], [
    +
      484.      1            String(
    +
      485.      1                await moduleFs.promises.readFile("jslint.mjs", "utf8")
    +
      486.      1            ).replace((
    +
      487.      1                /    /g
    +
      488.      1            ), "  "),
    +
      489.      1            {indent2: true},
    +
      490.      1            []
    +
      491.      1        ], [
    +
      492.      1            "function aa() {\n  return;\n}", {indent2: true}, []
    +
      493.      1        ], [
    +
      494.      1            "/".repeat(100), {long: true}, []
    +
      495.      1        ], [
    +
      496.      1            "let aa = aa._;", {name: true}, []
    +
      497.      1        ], [
    +
      498.      1            "require();", {node: true}, []
    +
      499.      1        ], [
    +
      500.      1            "let aa = 'aa';", {single: true}, []
    +
      501.      1        ], [
    +
      502.      1            "let aa = this;", {this: true}, []
    +
      503.      1        ], [
    +
      504.      1            "", {trace: true}, []
    +
      505.      1        ], [
    +
      506.      1            (
    +
      507.      1                "function aa({bb, aa}) {\n"
    +
      508.      1                + "    switch (aa) {\n"
    +
      509.      1                + "    case 1:\n"
    +
      510.      1                + "        break;\n"
    +
      511.      1                + "    case 0:\n"
    +
      512.      1                + "        break;\n"
    +
      513.      1                + "    default:\n"
    +
      514.      1                + "        return {bb, aa};\n"
    +
      515.      1                + "    }\n"
    +
      516.      1                + "}\n"
    +
      517.      1            ), {unordered: true}, []
    +
      518.      1        ], [
    +
      519.      1            "let {bb, aa} = 0;", {unordered: true}, []
    +
      520.      1        ], [
    +
      521.      1            (
    +
      522.      1                "function aa() {\n"
    +
      523.      1                + "    if (aa) {\n"
    +
      524.      1                + "        let bb = 0;\n"
    +
      525.      1                + "        return bb;\n"
    +
      526.      1                + "    }\n"
    +
      527.      1                + "}\n"
    +
      528.      1            ), {variable: true}, []
    +
      529.      1        ], [
    +
      530.      1            "let bb = 0;\nlet aa = 0;", {variable: true}, []
    +
      531.      1        ], [
    +
      532.      1            "\t", {white: true}, []
    +
      533.      1        ]
    +
      534.     22    ].forEach(function ([
    +
      535.     22        source, option_dict, global_list
    +
      536.     22    ]) {
    +
      537.     22        let elemNow = JSON.stringify([
    +
      538.     22            option_dict, source, global_list
    +
      539.     22        ]);
    +
      540.     22        // Assert list is sorted.
    +
      541.     22        assertOrThrow(elemPrv < elemNow, JSON.stringify([
    +
      542.     22            elemPrv, elemNow
    +
      543.     22        ], undefined, 4));
    +
      544.     22        elemPrv = elemNow;
    +
      545.     22        option_dict.beta = true;
    +
      546.     22        // test jslint's option handling-behavior
    +
      547.     22        assertOrThrow(
    +
      548.     22            jslint.jslint(
    +
      549.     22                source,
    +
      550.     22                option_dict,
    +
      551.     22                global_list
    +
      552.     22            ).warnings.length === 0,
    +
      553.     22            "jslint.jslint(" + JSON.stringify([
    +
      554.     22                source, option_dict, global_list
    +
      555.     22            ]) + ")"
    +
      556.     22        );
    +
      557.     22        // test jslint's directive handling-behavior
    +
      558.     22        source = (
    +
      559.     22            "/*jslint " + JSON.stringify(option_dict).slice(1, -1).replace((
    +
      560.     22                /"/g
    +
      561.     22            ), "") + "*/\n"
    +
      562.     22            + (
    +
      563.     22                global_list.length === 0
    +
      564.     21                ? ""
    +
      565.      1                : "/*global " + global_list.join(",") + "*/\n"
    +
      566.     22            )
    +
      567.     22            + source.replace((
    +
      568.     22                /^#!/
    +
      569.     22            ), "//")
    +
      570.     22        );
    +
      571.     22        assertOrThrow(jslint.jslint(source).warnings.length === 0, source);
    +
      572.     22    });
    +
      573.      1    assertOrThrow(jslint.jslint("", {
    +
      574.      1        test_internal_error: true
    +
      575.      1    }).warnings.length === 1);
    +
      576.      1    assertOrThrow(jslintCjs.jslint("", {
    +
      577.      1        test_internal_error: true
    +
      578.      1    }).warnings.length === 1);
    +
      579.      1}());
    +
      580.      1
    +
      581.      1(async function testcaseJslintWarningsValidate() {
    +
      582.      1/*
    +
      583.      1 * this function will validate each jslint <warning> is raised with given
    +
      584.      1 * malformed <code>
    +
      585.      1 */
    +
      586.      1    String(
    +
      587.      1        await moduleFs.promises.readFile("jslint.mjs", "utf8")
    +
      588.      1    ).replace((
    +
      589.      1        /(\n\s*?\/\/\s*?test_cause:\s*?)(\S[\S\s]*?\S)(\n\n\s*?) *?\S/g
    +
      590.    326    ), function (match0, header, causeList, footer) {
    +
      591.    326        let tmp;
    +
      592.    326        // console.error(match0);
    +
      593.    326        // Validate header.
    +
      594.    326        assertOrThrow(header === "\n\n// test_cause:\n", match0);
    +
      595.    326        // Validate footer.
    +
      596.    326        assertOrThrow(footer === "\n\n", match0);
    +
      597.    326        // Validate causeList.
    +
      598.    326        causeList = causeList.replace((
    +
      599.    326            /^\/\/ /gm
    +
      600.    326        ), "").replace((
    +
      601.    326            /^\["\n([\S\s]*?)\n"(,.*?)$/gm
    +
      602.     44        ), function (ignore, source, param) {
    +
      603.     44            source = "[" + JSON.stringify(source) + param;
    +
      604.     44            assertOrThrow(source.length > (80 - 3), source);
    +
      605.     44            return source;
    +
      606.     44        }).replace((
    +
      607.    326            / \/\/jslint-quiet$/gm
    +
      608.    326        ), "");
    +
      609.    495        tmp = causeList.split("\n").map(function (cause) {
    +
      610.    495            return (
    +
      611.    495                "["
    +
      612.   2475                + JSON.parse(cause).map(function (elem) {
    +
      613.   2475                    return JSON.stringify(elem);
    +
      614.   2475                }).join(", ")
    +
      615.    495                + "]"
    +
      616.    495            );
    +
      617.    495        }).sort().join("\n");
    +
      618.    326        assertOrThrow(causeList === tmp, "\n" + causeList + "\n\n" + tmp);
    +
      619.    495        causeList.split("\n").forEach(function (cause) {
    +
      620.    495            cause = JSON.parse(cause);
    +
      621.    495            tmp = jslint.jslint(cause[0], {
    +
      622.    495                beta: true,
    +
      623.    495                test_cause: true
    +
      624.    495            }).causes;
    +
      625.    495            // Validate cause.
    +
      626.    495            assertOrThrow(
    +
      627.    495                tmp[JSON.stringify(cause.slice(1))],
    +
      628.    495                (
    +
      629.    495                    "\n" + JSON.stringify(cause) + "\n\n"
    +
      630.    495                    + Object.keys(tmp).sort().join("\n")
    +
      631.    495                )
    +
      632.    495            );
    +
      633.    495        });
    +
      634.    326        return "";
    +
      635.    326    });
    +
      636.      1}());
    +
      637.      1
    +
      638.      1(async function testcaseMisc() {
    +
      639.      1
    +
      640.      1// This function will test misc handling-behavior.
    +
      641.      1
    +
      642.      1    // test debugInline's handling-behavior
    +
      643.      1    noop(debugInline);
    +
      644.      1    // test assertErrorThrownAsync's error handling-behavior
    +
      645.      1    await assertErrorThrownAsync(function () {
    +
      646.      1        return assertErrorThrownAsync(noop);
    +
      647.      1    });
    +
      648.      1    // test assertJsonEqual's error handling-behavior
    +
      649.      1    await assertErrorThrownAsync(function () {
    +
      650.      1        assertJsonEqual(1, 2);
    +
      651.      1    });
    +
      652.      1    await assertErrorThrownAsync(function () {
    +
      653.      1        assertJsonEqual(1, 2, "undefined");
    +
      654.      1    });
    +
      655.      1    await assertErrorThrownAsync(function () {
    +
      656.      1        assertJsonEqual(1, 2, {});
    +
      657.      1    });
    +
      658.      1    // test assertOrThrow's error handling-behavior
    +
      659.      1    await assertErrorThrownAsync(function () {
    +
      660.      1        assertOrThrow(undefined, "undefined");
    +
      661.      1    });
    +
      662.      1    await assertErrorThrownAsync(function () {
    +
      663.      1        assertOrThrow(undefined, new Error());
    +
      664.      1    });
    +
      665.      1}());
    +
      666.      1
    +
      667.      1(async function testcaseV8CoverageXxx() {
    +
      668.      1
    +
      669.      1// This function will test V8CoverageXxx's handling-behavior
    +
      670.      1
    +
      671.      1    let testCoverageMergeData = JSON.parse(
    +
      672.      1        await moduleFs.promises.readFile(
    +
      673.      1            "test_coverage_merge_data.json",
    +
      674.      1            "utf8"
    +
      675.      1        )
    +
      676.      1    );
    +
      677.      1
    +
      678.      1    jstestDescribe((
    +
      679.      1        "test jstestXxx's handling-behavior"
    +
      680.      1    ), function () {
    +
      681.      1        jstestIt((
    +
      682.      1            "test jstestDescribe's error handling-behavior"
    +
      683.      1        ), function () {
    +
      684.      1            throw new Error();
    +
      685.      1        }, "pass");
    +
      686.      1        jstestIt((
    +
      687.      1            "test jstestOnExit's error handling-behavior"
    +
      688.      1        ), function () {
    +
      689.      1            jstestOnExit(undefined, noop, 1);
    +
      690.      1        });
    +
      691.      1    });
    +
      692.      1
    +
      693.      1    jstestDescribe((
    +
      694.      1        "test v8CoverageListMerge's handling-behavior"
    +
      695.      1    ), function () {
    +
      696.      1        let functionsInput = JSON.stringify([
    +
      697.      1            {
    +
      698.      1                functionName: "test",
    +
      699.      1                isBlockCoverage: true,
    +
      700.      1                ranges: [
    +
      701.      1                    {
    +
      702.      1                        count: 2,
    +
      703.      1                        endOffset: 4,
    +
      704.      1                        startOffset: 0
    +
      705.      1                    },
    +
      706.      1                    {
    +
      707.      1                        count: 1,
    +
      708.      1                        endOffset: 2,
    +
      709.      1                        startOffset: 1
    +
      710.      1                    },
    +
      711.      1                    {
    +
      712.      1                        count: 1,
    +
      713.      1                        endOffset: 3,
    +
      714.      1                        startOffset: 2
    +
      715.      1                    }
    +
      716.      1                ]
    +
      717.      1            }
    +
      718.      1        ]);
    +
      719.      1        jstestIt((
    +
      720.      1            "accepts empty arrays for `v8CoverageListMerge`"
    +
      721.      1        ), function () {
    +
      722.      1            assertJsonEqual(v8CoverageListMerge([]), {
    +
      723.      1                result: []
    +
      724.      1            });
    +
      725.      1        });
    +
      726.      1        jstestIt((
    +
      727.      1            "funcCovs.length === 1"
    +
      728.      1        ), function () {
    +
      729.      1            assertJsonEqual(v8CoverageListMerge([
    +
      730.      1                {
    +
      731.      1                    result: [
    +
      732.      1                        {
    +
      733.      1                            functions: [
    +
      734.      1                                {
    +
      735.      1                                    functionName: "test",
    +
      736.      1                                    isBlockCoverage: true,
    +
      737.      1                                    ranges: [
    +
      738.      1                                        {
    +
      739.      1                                            count: 2,
    +
      740.      1                                            endOffset: 4,
    +
      741.      1                                            startOffset: 0
    +
      742.      1                                        }
    +
      743.      1                                    ]
    +
      744.      1                                }
    +
      745.      1                            ],
    +
      746.      1                            moduleUrl: "/lib.js",
    +
      747.      1                            scriptId: "1"
    +
      748.      1                        }
    +
      749.      1                    ]
    +
      750.      1                },
    +
      751.      1                {
    +
      752.      1                    result: [
    +
      753.      1                        {
    +
      754.      1                            functions: [],
    +
      755.      1                            moduleUrl: "/lib.js",
    +
      756.      1                            scriptId: "2"
    +
      757.      1                        }
    +
      758.      1                    ]
    +
      759.      1                }
    +
      760.      1            ]), {
    +
      761.      1                result: [
    +
      762.      1                    {
    +
      763.      1                        functions: [
    +
      764.      1                            {
    +
      765.      1                                functionName: "test",
    +
      766.      1                                isBlockCoverage: true,
    +
      767.      1                                ranges: [
    +
      768.      1                                    {
    +
      769.      1                                        count: 2,
    +
      770.      1                                        endOffset: 4,
    +
      771.      1                                        startOffset: 0
    +
      772.      1                                    }
    +
      773.      1                                ]
    +
      774.      1                            }
    +
      775.      1                        ],
    +
      776.      1                        scriptId: "0"
    +
      777.      1                    }
    +
      778.      1                ]
    +
      779.      1            });
    +
      780.      1        });
    +
      781.      1        jstestIt((
    +
      782.      1            "accepts arrays with a single item for `v8CoverageListMerge`"
    +
      783.      1        ), function () {
    +
      784.      1            assertJsonEqual(v8CoverageListMerge([
    +
      785.      1                {
    +
      786.      1                    result: [
    +
      787.      1                        {
    +
      788.      1                            functions: JSON.parse(functionsInput),
    +
      789.      1                            moduleUrl: "/lib.js",
    +
      790.      1                            scriptId: "123"
    +
      791.      1                        }
    +
      792.      1                    ]
    +
      793.      1                }
    +
      794.      1            ]), {
    +
      795.      1                result: [
    +
      796.      1                    {
    +
      797.      1                        functions: [
    +
      798.      1                            {
    +
      799.      1                                functionName: "test",
    +
      800.      1                                isBlockCoverage: true,
    +
      801.      1                                ranges: [
    +
      802.      1                                    {
    +
      803.      1                                        count: 2,
    +
      804.      1                                        endOffset: 4,
    +
      805.      1                                        startOffset: 0
    +
      806.      1                                    },
    +
      807.      1                                    {
    +
      808.      1                                        count: 1,
    +
      809.      1                                        endOffset: 3,
    +
      810.      1                                        startOffset: 1
    +
      811.      1                                    }
    +
      812.      1                                ]
    +
      813.      1                            }
    +
      814.      1                        ],
    +
      815.      1                        moduleUrl: "/lib.js",
    +
      816.      1                        scriptId: "0"
    +
      817.      1                    }
    +
      818.      1                ]
    +
      819.      1            });
    +
      820.      1        });
    +
      821.      1        jstestIt((
    +
      822.      1            "accepts arrays with two identical items for"
    +
      823.      1            + " `v8CoverageListMerge`"
    +
      824.      1        ), function () {
    +
      825.      1            assertJsonEqual(v8CoverageListMerge([
    +
      826.      1                {
    +
      827.      1                    result: [
    +
      828.      1                        {
    +
      829.      1                            functions: JSON.parse(functionsInput),
    +
      830.      1                            scriptId: "123",
    +
      831.      1                            url: "/lib.js"
    +
      832.      1                        }, {
    +
      833.      1                            functions: JSON.parse(functionsInput),
    +
      834.      1                            scriptId: "123",
    +
      835.      1                            url: "/lib.js"
    +
      836.      1                        }
    +
      837.      1                    ]
    +
      838.      1                }
    +
      839.      1            ]), {
    +
      840.      1                result: [
    +
      841.      1                    {
    +
      842.      1                        functions: [
    +
      843.      1                            {
    +
      844.      1                                functionName: "test",
    +
      845.      1                                isBlockCoverage: true,
    +
      846.      1                                ranges: [
    +
      847.      1                                    {
    +
      848.      1                                        count: 4,
    +
      849.      1                                        endOffset: 4,
    +
      850.      1                                        startOffset: 0
    +
      851.      1                                    },
    +
      852.      1                                    {
    +
      853.      1                                        count: 2,
    +
      854.      1                                        endOffset: 3,
    +
      855.      1                                        startOffset: 1
    +
      856.      1                                    }
    +
      857.      1                                ]
    +
      858.      1                            }
    +
      859.      1                        ],
    +
      860.      1                        scriptId: "0",
    +
      861.      1                        url: "/lib.js"
    +
      862.      1                    }
    +
      863.      1                ]
    +
      864.      1            });
    +
      865.      1        });
    +
      866.      1        [
    +
      867.      1            "test_coverage_merge_is_block_coverage_test.json",
    +
      868.      1            "test_coverage_merge_issue_2_mixed_is_block_coverage_test.json",
    +
      869.      1            "test_coverage_merge_node_10_internal_errors_one_of_test.json",
    +
      870.      1            "test_coverage_merge_reduced_test.json",
    +
      871.      1            "test_coverage_merge_simple_test.json",
    +
      872.      1            "test_coverage_merge_various_test.json"
    +
      873.      6        ].forEach(function (file) {
    +
      874.      6            jstestIt(file, function () {
    +
      875.      6                file = testCoverageMergeData[file];
    +
      876.     84                file.forEach(function ({
    +
      877.     84                    expected,
    +
      878.     84                    inputs
    +
      879.     84                }) {
    +
      880.     84                    assertJsonEqual(v8CoverageListMerge(inputs), expected);
    +
      881.     84                });
    +
      882.      6            });
    +
      883.      6        });
    +
      884.      1        jstestIt((
    +
      885.      1            "merge multiple node-sqlite coverage files"
    +
      886.      1        ), function () {
    +
      887.      1            let data1 = [
    +
      888.      1                "test_v8_coverage_node_sqlite_9884_1633662346346_0.json",
    +
      889.      1                "test_v8_coverage_node_sqlite_13216_1633662333140_0.json"
    +
      890.      2            ].map(function (file) {
    +
      891.      2                return testCoverageMergeData[file];
    +
      892.      2            });
    +
      893.      1            let data2 = testCoverageMergeData[
    +
      894.      1                "test_v8_coverage_node_sqlite_merged.json"
    +
      895.      1            ];
    +
      896.      1            data1 = v8CoverageListMerge(data1);
    +
      897.      1            data1 = v8CoverageListMerge([data1]);
    +
      898.      1
    +
      899.      1// Debug data1.
    +
      900.      1// await moduleFs.promises.writeFile(
    +
      901.      1//     ".test_v8_coverage_node_sqlite_merged.json",
    +
      902.      1//     JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n"
    +
      903.      1// );
    +
      904.      1
    +
      905.      1            assertJsonEqual(data1, data2);
    +
      906.      1        });
    +
      907.      1    });
    +
      908.      1
    +
      909.      1    jstestDescribe((
    +
      910.      1        "test v8CoverageReportCreate's handling-behavior"
    +
      911.      1    ), function () {
    +
      912.      1        jstestIt((
    +
      913.      1            "test null-case handling-behavior"
    +
      914.      1        ), async function () {
    +
      915.      1            await assertErrorThrownAsync(function () {
    +
      916.      1                return v8CoverageReportCreate({});
    +
      917.      1            }, "invalid coverageDir");
    +
      918.      1        });
    +
      919.      1        jstestIt((
    +
      920.      1            "test coverage-report jslint.mjs handling-behavior"
    +
      921.      1        ), async function () {
    +
      922.      1            // test remove-old-coverage handling-behavior
    +
      923.      1            await fsWriteFileWithParents(
    +
      924.      1                ".tmp/coverage_jslint/coverage-0-0-0.json",
    +
      925.      1                ""
    +
      926.      1            );
    +
      927.      1            await jslint.jslint_cli({
    +
      928.      1                mode_cli: true,
    +
      929.      1                process_argv: [
    +
      930.      1                    "node", "jslint.mjs",
    +
      931.      1                    "v8_coverage_report=.tmp/coverage_jslint",
    +
      932.      1                    "--exclude-node-modules=0",
    +
      933.      1                    "--exclude=aa.js",
    +
      934.      1                    "--include=jslint.mjs",
    +
      935.      1                    "node", "jslint.mjs"
    +
      936.      1                ]
    +
      937.      1            });
    +
      938.      1        });
    +
      939.      1        [
    +
      940.      1            [
    +
      941.      1                "v8CoverageReportCreate_high.js", (
    +
      942.      1                    "switch(0){\n"
    +
      943.      1                    + "case 0:break;\n"
    +
      944.      1                    + "}\n"
    +
      945.      1                )
    +
      946.      1            ], [
    +
      947.      1                "v8CoverageReportCreate_ignore.js", (
    +
      948.      1                    "/*coverage-ignore-file*/\n"
    +
      949.      1                    + "switch(0){\n"
    +
      950.      1                    + "case 0:break;\n"
    +
      951.      1                    + "}\n"
    +
      952.      1                )
    +
      953.      1            ], [
    +
      954.      1                "v8CoverageReportCreate_low.js", (
    +
      955.      1                    "switch(0){\n"
    +
      956.      1                    + "case 1:break;\n"
    +
      957.      1                    + "case 2:break;\n"
    +
      958.      1                    + "case 3:break;\n"
    +
      959.      1                    + "case 4:break;\n"
    +
      960.      1                    + "}\n"
    +
      961.      1                )
    +
      962.      1            ], [
    +
      963.      1                "v8CoverageReportCreate_medium.js", (
    +
      964.      1                    "switch(0){\n"
    +
      965.      1                    + "case 0:break;\n"
    +
      966.      1                    + "case 1:break;\n"
    +
      967.      1                    + "case 2:break;\n"
    +
      968.      1                    + "}\n"
    +
      969.      1                )
    +
      970.      1            ]
    +
      971.      4        ].forEach(function ([
    +
      972.      4            file, data
    +
      973.      4        ], ii) {
    +
      974.      4            jstestIt(file, async function () {
    +
      975.      4                let dir = ".tmp/coverage_" + ii + "/";
    +
      976.      4                file = dir + file;
    +
      977.      4                await fsWriteFileWithParents(file, data);
    +
      978.      4                await jslint.jslint_cli({
    +
      979.      4                    mode_cli: true,
    +
      980.      4                    process_argv: [
    +
      981.      4                        "node", "jslint.mjs",
    +
      982.      4                        "v8_coverage_report=" + dir,
    +
      983.      4                        "node",
    +
      984.      4                        file
    +
      985.      4                    ]
    +
      986.      4                });
    +
      987.      4            });
    +
      988.      4        });
    +
      989.      1        jstestIt((
    +
      990.      1            "test npm handling-behavior"
    +
      991.      1        ), async function () {
    +
      992.      1            await jslint.jslint_cli({
    +
      993.      1                mode_cli: true,
    +
      994.      1                process_argv: [
    +
      995.      1                    "node", "jslint.mjs",
    +
      996.      1                    "v8_coverage_report=.tmp/coverage_npm",
    +
      997.      1                    "npm", "--version"
    +
      998.      1                ]
    +
      999.      1            });
    +
     1000.      1        });
    +
     1001.      1        jstestIt((
    +
     1002.      1            "test misc handling-behavior"
    +
     1003.      1        ), async function () {
    +
     1004.      1            await Promise.all([
    +
     1005.      1                [
    +
     1006.      1                    ".tmp/coverage_misc/aa.js", "\n".repeat(0x100)
    +
     1007.      1                ], [
    +
     1008.      1                    ".tmp/coverage_misc/coverage-0-0-0.json", JSON.stringify({
    +
     1009.      1                        "result": [
    +
     1010.      1                            {
    +
     1011.      1                                "functions": [
    +
     1012.      1                                    {
    +
     1013.      1                                        "functionName": "",
    +
     1014.      1                                        "isBlockCoverage": true,
    +
     1015.      1                                        "ranges": [
    +
     1016.      1                                            {
    +
     1017.      1                                                "count": 1,
    +
     1018.      1                                                "endOffset": 0xf0,
    +
     1019.      1                                                "startOffset": 0x10
    +
     1020.      1                                            },
    +
     1021.      1                                            {
    +
     1022.      1                                                "count": 1,
    +
     1023.      1                                                "endOffset": 0x40,
    +
     1024.      1                                                "startOffset": 0x20
    +
     1025.      1                                            },
    +
     1026.      1                                            {
    +
     1027.      1                                                "count": 1,
    +
     1028.      1                                                "endOffset": 0x80,
    +
     1029.      1                                                "startOffset": 0x60
    +
     1030.      1                                            },
    +
     1031.      1                                            {
    +
     1032.      1                                                "count": 0,
    +
     1033.      1                                                "endOffset": 0x45,
    +
     1034.      1                                                "startOffset": 0x25
    +
     1035.      1                                            },
    +
     1036.      1                                            {
    +
     1037.      1                                                "count": 0,
    +
     1038.      1                                                "endOffset": 0x85,
    +
     1039.      1                                                "startOffset": 0x65
    +
     1040.      1                                            }
    +
     1041.      1                                        ]
    +
     1042.      1                                    }
    +
     1043.      1                                ],
    +
     1044.      1                                "scriptId": "0",
    +
     1045.      1                                "url": "file:///" + modulePath.resolve(
    +
     1046.      1                                    ".tmp/coverage_misc/aa.js"
    +
     1047.      1                                )
    +
     1048.      1                            }
    +
     1049.      1                        ]
    +
     1050.      1                    }, undefined, 4)
    +
     1051.      1                ]
    +
     1052.      2            ].map(async function ([
    +
     1053.      2                file, data
    +
     1054.      2            ]) {
    +
     1055.      2                await fsWriteFileWithParents(file, data);
    +
     1056.      2            }));
    +
     1057.      1            await jslint.jslint_cli({
    +
     1058.      1                mode_cli: true,
    +
     1059.      1                process_argv: [
    +
     1060.      1                    "node", "jslint.mjs",
    +
     1061.      1                    "v8_coverage_report=.tmp/coverage_misc"
    +
     1062.      1                    // "node", ".tmp/coverage_misc/aa.js"
    +
     1063.      1                ]
    +
     1064.      1            });
    +
     1065.      1        });
    +
     1066.      1    });
    +
     1067.      1}());
    +
     1068.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage/touch.txt b/branch-sandbox/.artifact/coverage/touch.txt new file mode 100644 index 000000000..e69de29bb diff --git a/branch-sandbox/.artifact/coverage_sqlite3_js/coverage_badge.svg b/branch-sandbox/.artifact/coverage_sqlite3_js/coverage_badge.svg new file mode 100644 index 000000000..e5d678a1b --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_js/coverage_badge.svg @@ -0,0 +1,14 @@ + + + + +coverage +97.60 % + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_js/coverage_report.txt b/branch-sandbox/.artifact/coverage_sqlite3_js/coverage_report.txt new file mode 100644 index 000000000..6df93809c --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_js/coverage_report.txt @@ -0,0 +1,16 @@ +V8 Coverage Report ++----------------------------------+-------------------+ +| Files covered | Lines | ++----------------------------------+-------------------+ +| ./ | 97.60 % | +| *******************************_ | 245 / 251 | ++----------------------------------+-------------------+ +| ./lib/sqlite3-binding.js | 100.00 % | +| ******************************** | 6 / 6 | ++----------------------------------+-------------------+ +| ./lib/sqlite3.js | 97.08 % | +| *******************************_ | 200 / 206 | ++----------------------------------+-------------------+ +| ./lib/trace.js | 100.00 % | +| ******************************** | 39 / 39 | ++----------------------------------+-------------------+ diff --git a/branch-sandbox/.artifact/coverage_sqlite3_js/index.html b/branch-sandbox/.artifact/coverage_sqlite3_js/index.html new file mode 100644 index 000000000..f014766d1 --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_js/index.html @@ -0,0 +1,202 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . /
    +
    +
    +
    +
    + 97.60 %
    + 245 / 251 +
    +
    + 6 / 251 +
    + . / lib/sqlite3-binding.js
    +
    +
    +
    +
    + 100.00 %
    + 6 / 6 +
    +
    + 0 / 6 +
    + . / lib/sqlite3.js
    +
    +
    +
    +
    + 97.08 %
    + 200 / 206 +
    +
    + 6 / 206 +
    + . / lib/trace.js
    +
    +
    +
    +
    + 100.00 %
    + 39 / 39 +
    +
    + 0 / 39 +
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_js/lib/sqlite3-binding.js.html b/branch-sandbox/.artifact/coverage_sqlite3_js/lib/sqlite3-binding.js.html new file mode 100644 index 000000000..e9c7ad22a --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_js/lib/sqlite3-binding.js.html @@ -0,0 +1,164 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / lib/sqlite3-binding.js
    +
    +
    +
    +
    + 100.00 %
    + 6 / 6 +
    +
    + 0 / 6 +
    +
    + + +
    +
        1.      2var binary = require('node-pre-gyp');
    +
        2.      2var path = require('path');
    +
        3.      2var binding_path = binary.find(path.resolve(path.join(__dirname,'../package.json')));
    +
        4.      2var binding = require(binding_path);
    +
        5.      2module.exports = exports = binding;
    +
        6.      2
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_js/lib/sqlite3.js.html b/branch-sandbox/.artifact/coverage_sqlite3_js/lib/sqlite3.js.html new file mode 100644 index 000000000..07cc610ab --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_js/lib/sqlite3.js.html @@ -0,0 +1,364 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / lib/sqlite3.js
    +
    +
    +
    +
    + 97.08 %
    + 200 / 206 +
    +
    + 6 / 206 +
    +
    + + +
    +
        1.      2var path = require('path');
    +
        2.      2var sqlite3 = require('./sqlite3-binding.js');
    +
        3.      2var EventEmitter = require('events').EventEmitter;
    +
        4.      2module.exports = exports = sqlite3;
    +
        5.      2
    +
        6.     12function normalizeMethod (fn) {
    +
        7.   4238    return function (sql) {
    +
        8.   4238        var errBack;
    +
        9.   4238        var args = Array.prototype.slice.call(arguments, 1);
    +
       10.   1118        if (typeof args[args.length - 1] === 'function') {
    +
       11.   1118            var callback = args[args.length - 1];
    +
       12.   1118            errBack = function(err) {
    +
       13.   1118                if (err) {
    +
       14.   1118                    callback(err);
    +
       15.   1118                }
    +
       16.   1118            };
    +
       17.   1118        }
    +
       18.   4238        var statement = new Statement(this, sql, errBack);
    +
       19.   4238        return fn.call(this, statement, args);
    +
       20.   4238    };
    +
       21.     12}
    +
       22.      2
    +
       23.      6function inherits(target, source) {
    +
       24.      6    for (var k in source.prototype)
    +
       25.    108        target.prototype[k] = source.prototype[k];
    +
       26.      6}
    +
       27.      2
    +
       28.      2sqlite3.cached = {
    +
       29.      4    Database: function(file, a, b) {
    +
       30.     -0        if (file === '' || file === ':memory:') {
    +
       31.     -0            // Don't cache special databases.
    +
       32.     -0            return new Database(file, a, b);
    +
       33.     -0        }
    +
       34.      4
    +
       35.      4        var db;
    +
       36.      4        file = path.resolve(file);
    +
       37.      2        function cb() { callback.call(db, null); }
    +
       38.      4
    +
       39.      2        if (!sqlite3.cached.objects[file]) {
    +
       40.      2            db = sqlite3.cached.objects[file] = new Database(file, a, b);
    +
       41.      2        }
    +
       42.      2        else {
    +
       43.      2            // Make sure the callback is called.
    +
       44.      2            db = sqlite3.cached.objects[file];
    +
       45.     -0            var callback = (typeof a === 'number') ? b : a;
    +
       46.      2            if (typeof callback === 'function') {
    +
       47.      2                if (db.open) process.nextTick(cb);
    +
       48.      2                else db.once('open', cb);
    +
       49.      2            }
    +
       50.      2        }
    +
       51.      4
    +
       52.      4        return db;
    +
       53.      4    },
    +
       54.      2    objects: {}
    +
       55.      2};
    +
       56.      2
    +
       57.      2
    +
       58.      2var Database = sqlite3.Database;
    +
       59.      2var Statement = sqlite3.Statement;
    +
       60.      2var Backup = sqlite3.Backup;
    +
       61.      2
    +
       62.      2inherits(Database, EventEmitter);
    +
       63.      2inherits(Statement, EventEmitter);
    +
       64.      2inherits(Backup, EventEmitter);
    +
       65.      2
    +
       66.      2// Database#prepare(sql, [bind1, bind2, ...], [callback])
    +
       67.   2110Database.prototype.prepare = normalizeMethod(function(statement, params) {
    +
       68.   2110    return params.length
    +
       69.      7        ? statement.bind.apply(statement, params)
    +
       70.   2103        : statement;
    +
       71.   2110});
    +
       72.      2
    +
       73.      2// Database#run(sql, [bind1, bind2, ...], [callback])
    +
       74.   2075Database.prototype.run = normalizeMethod(function(statement, params) {
    +
       75.   2075    statement.run.apply(statement, params).finalize();
    +
       76.   2075    return this;
    +
       77.   2075});
    +
       78.      2
    +
       79.      2// Database#get(sql, [bind1, bind2, ...], [callback])
    +
       80.     35Database.prototype.get = normalizeMethod(function(statement, params) {
    +
       81.     35    statement.get.apply(statement, params).finalize();
    +
       82.     35    return this;
    +
       83.     35});
    +
       84.      2
    +
       85.      2// Database#all(sql, [bind1, bind2, ...], [callback])
    +
       86.      9Database.prototype.all = normalizeMethod(function(statement, params) {
    +
       87.      9    statement.all.apply(statement, params).finalize();
    +
       88.      9    return this;
    +
       89.      9});
    +
       90.      2
    +
       91.      2// Database#each(sql, [bind1, bind2, ...], [callback], [complete])
    +
       92.      7Database.prototype.each = normalizeMethod(function(statement, params) {
    +
       93.      7    statement.each.apply(statement, params).finalize();
    +
       94.      7    return this;
    +
       95.      7});
    +
       96.      2
    +
       97.      2Database.prototype.map = normalizeMethod(function(statement, params) {
    +
       98.      2    statement.map.apply(statement, params).finalize();
    +
       99.      2    return this;
    +
      100.      2});
    +
      101.      2
    +
      102.      2// Database#backup(filename, [callback])
    +
      103.      2// Database#backup(filename, destName, sourceName, filenameIsDest, [callback])
    +
      104.     15Database.prototype.backup = function() {
    +
      105.     15    var backup;
    +
      106.     13    if (arguments.length <= 2) {
    +
      107.     13        // By default, we write the main database out to the main database of the named file.
    +
      108.     13        // This is the most likely use of the backup api.
    +
      109.     13        backup = new Backup(this, arguments[0], 'main', 'main', true, arguments[1]);
    +
      110.     13    } else {
    +
      111.      2        // Otherwise, give the user full control over the sqlite3_backup_init arguments.
    +
      112.      2        backup = new Backup(this, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]);
    +
      113.      2    }
    +
      114.     15    // Per the sqlite docs, exclude the following errors as non-fatal by default.
    +
      115.     15    backup.retryErrors = [sqlite3.BUSY, sqlite3.LOCKED];
    +
      116.     15    return backup;
    +
      117.     15};
    +
      118.      2
    +
      119.      2Statement.prototype.map = function() {
    +
      120.      2    var params = Array.prototype.slice.call(arguments);
    +
      121.      2    var callback = params.pop();
    +
      122.      2    params.push(function(err, rows) {
    +
      123.     -0        if (err) return callback(err);
    +
      124.      2        var result = {};
    +
      125.      2        if (rows.length) {
    +
      126.      2            var keys = Object.keys(rows[0]), key = keys[0];
    +
      127.      1            if (keys.length > 2) {
    +
      128.      1                // Value is an object
    +
      129.      5                for (var i = 0; i < rows.length; i++) {
    +
      130.      5                    result[rows[i][key]] = rows[i];
    +
      131.      5                }
    +
      132.      1            } else {
    +
      133.      1                var value = keys[1];
    +
      134.      1                // Value is a plain value
    +
      135.      5                for (i = 0; i < rows.length; i++) {
    +
      136.      5                    result[rows[i][key]] = rows[i][value];
    +
      137.      5                }
    +
      138.      1            }
    +
      139.      2        }
    +
      140.      2        callback(err, result);
    +
      141.      2    });
    +
      142.      2    return this.all.apply(this, params);
    +
      143.      2};
    +
      144.      2
    +
      145.      2var isVerbose = false;
    +
      146.      2
    +
      147.      2var supportedEvents = [ 'trace', 'profile', 'insert', 'update', 'delete' ];
    +
      148.      2
    +
      149.      7Database.prototype.addListener = Database.prototype.on = function(type) {
    +
      150.      7    var val = EventEmitter.prototype.addListener.apply(this, arguments);
    +
      151.      4    if (supportedEvents.indexOf(type) >= 0) {
    +
      152.      4        this.configure(type, true);
    +
      153.      4    }
    +
      154.      7    return val;
    +
      155.      7};
    +
      156.      2
    +
      157.      2Database.prototype.removeListener = function(type) {
    +
      158.      2    var val = EventEmitter.prototype.removeListener.apply(this, arguments);
    +
      159.      1    if (supportedEvents.indexOf(type) >= 0 && !this._events[type]) {
    +
      160.      1        this.configure(type, false);
    +
      161.      1    }
    +
      162.      2    return val;
    +
      163.      2};
    +
      164.      2
    +
      165.      1Database.prototype.removeAllListeners = function(type) {
    +
      166.      1    var val = EventEmitter.prototype.removeAllListeners.apply(this, arguments);
    +
      167.      1    if (supportedEvents.indexOf(type) >= 0) {
    +
      168.      1        this.configure(type, false);
    +
      169.      1    }
    +
      170.      1    return val;
    +
      171.      1};
    +
      172.      2
    +
      173.      2// Save the stack trace over EIO callbacks.
    +
      174.      1sqlite3.verbose = function() {
    +
      175.      1    if (!isVerbose) {
    +
      176.      1        var trace = require('./trace');
    +
      177.      1        [
    +
      178.      1            'prepare',
    +
      179.      1            'get',
    +
      180.      1            'run',
    +
      181.      1            'all',
    +
      182.      1            'each',
    +
      183.      1            'map',
    +
      184.      1            'close',
    +
      185.      1            'exec'
    +
      186.      8        ].forEach(function (name) {
    +
      187.      8            trace.extendTrace(Database.prototype, name);
    +
      188.      8        });
    +
      189.      1        [
    +
      190.      1            'bind',
    +
      191.      1            'get',
    +
      192.      1            'run',
    +
      193.      1            'all',
    +
      194.      1            'each',
    +
      195.      1            'map',
    +
      196.      1            'reset',
    +
      197.      1            'finalize',
    +
      198.      8        ].forEach(function (name) {
    +
      199.      8            trace.extendTrace(Statement.prototype, name);
    +
      200.      8        });
    +
      201.      1        isVerbose = true;
    +
      202.      1    }
    +
      203.      1
    +
      204.      1    return this;
    +
      205.      1};
    +
      206.      2
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_js/lib/trace.js.html b/branch-sandbox/.artifact/coverage_sqlite3_js/lib/trace.js.html new file mode 100644 index 000000000..da268560d --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_js/lib/trace.js.html @@ -0,0 +1,197 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / lib/trace.js
    +
    +
    +
    +
    + 100.00 %
    + 39 / 39 +
    +
    + 0 / 39 +
    +
    + + +
    +
        1.      1// Inspired by https://github.com/tlrobinson/long-stack-traces
    +
        2.      1var util = require('util');
    +
        3.      1
    +
        4.     16function extendTrace(object, property, pos) {
    +
        5.     16    var old = object[property];
    +
        6.      1    object[property] = function() {
    +
        7.      1        var error = new Error();
    +
        8.      1        var name = object.constructor.name + '#' + property + '(' + 
    +
        9.      2            Array.prototype.slice.call(arguments).map(function(el) {
    +
       10.      2                return util.inspect(el, false, 0);
    +
       11.      2            }).join(', ') + ')';
    +
       12.      1
    +
       13.      1        if (typeof pos === 'undefined') pos = -1;
    +
       14.      1        if (pos < 0) pos += arguments.length;
    +
       15.      1        var cb = arguments[pos];
    +
       16.      1        if (typeof arguments[pos] === 'function') {
    +
       17.      1            arguments[pos] = function replacement() {
    +
       18.      1                var err = arguments[0];
    +
       19.      1                if (err && err.stack && !err.__augmented) {
    +
       20.      1                    err.stack = filter(err).join('\n');
    +
       21.      1                    err.stack += '\n--> in ' + name;
    +
       22.      1                    err.stack += '\n' + filter(error).slice(1).join('\n');
    +
       23.      1                    err.__augmented = true;
    +
       24.      1                }
    +
       25.      1                return cb.apply(this, arguments);
    +
       26.      1            };
    +
       27.      1        }
    +
       28.      1        return old.apply(this, arguments);
    +
       29.      1    };
    +
       30.     16}
    +
       31.      1exports.extendTrace = extendTrace;
    +
       32.      1
    +
       33.      1
    +
       34.      2function filter(error) {
    +
       35.     13    return error.stack.split('\n').filter(function(line) {
    +
       36.     13        return line.indexOf(__filename) < 0;
    +
       37.     13    });
    +
       38.      2}
    +
       39.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_js/touch.txt b/branch-sandbox/.artifact/coverage_sqlite3_js/touch.txt new file mode 100644 index 000000000..e69de29bb diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/coverage_badge.svg b/branch-sandbox/.artifact/coverage_sqlite3_sh/coverage_badge.svg new file mode 100644 index 000000000..da896e8c6 --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/coverage_badge.svg @@ -0,0 +1,14 @@ + + + + +coverage +91.76 % + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/coverage_report.txt b/branch-sandbox/.artifact/coverage_sqlite3_sh/coverage_report.txt new file mode 100644 index 000000000..8b56bdaa8 --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/coverage_report.txt @@ -0,0 +1,109 @@ +V8 Coverage Report ++----------------------------------+-------------------+ +| Files covered | Lines | ++----------------------------------+-------------------+ +| ./ | 91.76 % | +| *****************************___ | 2474 / 2696 | ++----------------------------------+-------------------+ +| ./lib/sqlite3-binding.js | 100.00 % | +| ******************************** | 6 / 6 | ++----------------------------------+-------------------+ +| ./lib/sqlite3.js | 97.08 % | +| *******************************_ | 200 / 206 | ++----------------------------------+-------------------+ +| ./lib/trace.js | 100.00 % | +| ******************************** | 39 / 39 | ++----------------------------------+-------------------+ +| ./test/affected.test.js | 93.93 % | +| ******************************__ | 31 / 33 | ++----------------------------------+-------------------+ +| ./test/backup.test.js | 83.21 % | +| ***************************_____ | 233 / 280 | ++----------------------------------+-------------------+ +| ./test/blob.test.js | 90.90 % | +| *****************************___ | 50 / 55 | ++----------------------------------+-------------------+ +| ./test/cache.test.js | 90.69 % | +| *****************************___ | 39 / 43 | ++----------------------------------+-------------------+ +| ./test/constants.test.js | 100.00 % | +| ******************************** | 45 / 45 | ++----------------------------------+-------------------+ +| ./test/database_fail.test.js | 77.92 % | +| *************************_______ | 120 / 154 | ++----------------------------------+-------------------+ +| ./test/each.test.js | 95.00 % | +| ******************************__ | 38 / 40 | ++----------------------------------+-------------------+ +| ./test/exec.test.js | 97.50 % | +| *******************************_ | 39 / 40 | ++----------------------------------+-------------------+ +| ./test/extension.test.js | 96.15 % | +| *******************************_ | 25 / 26 | ++----------------------------------+-------------------+ +| ./test/fts-content.test.js | 100.00 % | +| ******************************** | 14 / 14 | ++----------------------------------+-------------------+ +| ./test/interrupt.test.js | 92.59 % | +| ******************************__ | 75 / 81 | ++----------------------------------+-------------------+ +| ./test/issue-108.test.js | 89.65 % | +| *****************************___ | 26 / 29 | ++----------------------------------+-------------------+ +| ./test/json.test.js | 65.21 % | +| *********************___________ | 15 / 23 | ++----------------------------------+-------------------+ +| ./test/map.test.js | 93.75 % | +| ******************************__ | 60 / 64 | ++----------------------------------+-------------------+ +| ./test/named_columns.test.js | 94.87 % | +| ******************************__ | 37 / 39 | ++----------------------------------+-------------------+ +| ./test/named_params.test.js | 98.57 % | +| ******************************** | 69 / 70 | ++----------------------------------+-------------------+ +| ./test/null_error.test.js | 95.23 % | +| ******************************__ | 40 / 42 | ++----------------------------------+-------------------+ +| ./test/open_close.test.js | 93.61 % | +| ******************************__ | 176 / 188 | ++----------------------------------+-------------------+ +| ./test/other_objects.test.js | 91.11 % | +| *****************************___ | 82 / 90 | ++----------------------------------+-------------------+ +| ./test/parallel_insert.test.js | 100.00 % | +| ******************************** | 45 / 45 | ++----------------------------------+-------------------+ +| ./test/prepare.test.js | 94.85 % | +| ******************************__ | 406 / 428 | ++----------------------------------+-------------------+ +| ./test/profile.test.js | 89.65 % | +| *****************************___ | 52 / 58 | ++----------------------------------+-------------------+ +| ./test/rerun.test.js | 94.11 % | +| ******************************__ | 48 / 51 | ++----------------------------------+-------------------+ +| ./test/scheduling.test.js | 91.11 % | +| *****************************___ | 41 / 45 | ++----------------------------------+-------------------+ +| ./test/serialization.test.js | 95.23 % | +| ******************************__ | 100 / 105 | ++----------------------------------+-------------------+ +| ./test/support/createdb.js | 89.58 % | +| *****************************___ | 43 / 48 | ++----------------------------------+-------------------+ +| ./test/support/helper.js | 72.97 % | +| ***********************_________ | 27 / 37 | ++----------------------------------+-------------------+ +| ./test/trace.test.js | 80.88 % | +| **************************______ | 55 / 68 | ++----------------------------------+-------------------+ +| ./test/unicode.test.js | 98.26 % | +| *******************************_ | 113 / 115 | ++----------------------------------+-------------------+ +| ./test/upsert.test.js | 85.71 % | +| ***************************_____ | 24 / 28 | ++----------------------------------+-------------------+ +| ./test/verbose.test.js | 100.00 % | +| ******************************** | 61 / 61 | ++----------------------------------+-------------------+ diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/index.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/index.html new file mode 100644 index 000000000..428407fae --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/index.html @@ -0,0 +1,698 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . /
    +
    +
    +
    +
    + 91.76 %
    + 2474 / 2696 +
    +
    + 222 / 2696 +
    + . / lib/sqlite3-binding.js
    +
    +
    +
    +
    + 100.00 %
    + 6 / 6 +
    +
    + 0 / 6 +
    + . / lib/sqlite3.js
    +
    +
    +
    +
    + 97.08 %
    + 200 / 206 +
    +
    + 6 / 206 +
    + . / lib/trace.js
    +
    +
    +
    +
    + 100.00 %
    + 39 / 39 +
    +
    + 0 / 39 +
    + . / test/affected.test.js
    +
    +
    +
    +
    + 93.93 %
    + 31 / 33 +
    +
    + 2 / 33 +
    + . / test/backup.test.js
    +
    +
    +
    +
    + 83.21 %
    + 233 / 280 +
    +
    + 47 / 280 +
    + . / test/blob.test.js
    +
    +
    +
    +
    + 90.90 %
    + 50 / 55 +
    +
    + 5 / 55 +
    + . / test/cache.test.js
    +
    +
    +
    +
    + 90.69 %
    + 39 / 43 +
    +
    + 4 / 43 +
    + . / test/constants.test.js
    +
    +
    +
    +
    + 100.00 %
    + 45 / 45 +
    +
    + 0 / 45 +
    + . / test/database_fail.test.js
    +
    +
    +
    +
    + 77.92 %
    + 120 / 154 +
    +
    + 34 / 154 +
    + . / test/each.test.js
    +
    +
    +
    +
    + 95.00 %
    + 38 / 40 +
    +
    + 2 / 40 +
    + . / test/exec.test.js
    +
    +
    +
    +
    + 97.50 %
    + 39 / 40 +
    +
    + 1 / 40 +
    + . / test/extension.test.js
    +
    +
    +
    +
    + 96.15 %
    + 25 / 26 +
    +
    + 1 / 26 +
    + . / test/fts-content.test.js
    +
    +
    +
    +
    + 100.00 %
    + 14 / 14 +
    +
    + 0 / 14 +
    + . / test/interrupt.test.js
    +
    +
    +
    +
    + 92.59 %
    + 75 / 81 +
    +
    + 6 / 81 +
    + . / test/issue-108.test.js
    +
    +
    +
    +
    + 89.65 %
    + 26 / 29 +
    +
    + 3 / 29 +
    + . / test/json.test.js
    +
    +
    +
    +
    + 65.21 %
    + 15 / 23 +
    +
    + 8 / 23 +
    + . / test/map.test.js
    +
    +
    +
    +
    + 93.75 %
    + 60 / 64 +
    +
    + 4 / 64 +
    + . / test/named_columns.test.js
    +
    +
    +
    +
    + 94.87 %
    + 37 / 39 +
    +
    + 2 / 39 +
    + . / test/named_params.test.js
    +
    +
    +
    +
    + 98.57 %
    + 69 / 70 +
    +
    + 1 / 70 +
    + . / test/null_error.test.js
    +
    +
    +
    +
    + 95.23 %
    + 40 / 42 +
    +
    + 2 / 42 +
    + . / test/open_close.test.js
    +
    +
    +
    +
    + 93.61 %
    + 176 / 188 +
    +
    + 12 / 188 +
    + . / test/other_objects.test.js
    +
    +
    +
    +
    + 91.11 %
    + 82 / 90 +
    +
    + 8 / 90 +
    + . / test/parallel_insert.test.js
    +
    +
    +
    +
    + 100.00 %
    + 45 / 45 +
    +
    + 0 / 45 +
    + . / test/prepare.test.js
    +
    +
    +
    +
    + 94.85 %
    + 406 / 428 +
    +
    + 22 / 428 +
    + . / test/profile.test.js
    +
    +
    +
    +
    + 89.65 %
    + 52 / 58 +
    +
    + 6 / 58 +
    + . / test/rerun.test.js
    +
    +
    +
    +
    + 94.11 %
    + 48 / 51 +
    +
    + 3 / 51 +
    + . / test/scheduling.test.js
    +
    +
    +
    +
    + 91.11 %
    + 41 / 45 +
    +
    + 4 / 45 +
    + . / test/serialization.test.js
    +
    +
    +
    +
    + 95.23 %
    + 100 / 105 +
    +
    + 5 / 105 +
    + . / test/support/createdb.js
    +
    +
    +
    +
    + 89.58 %
    + 43 / 48 +
    +
    + 5 / 48 +
    + . / test/support/helper.js
    +
    +
    +
    +
    + 72.97 %
    + 27 / 37 +
    +
    + 10 / 37 +
    + . / test/trace.test.js
    +
    +
    +
    +
    + 80.88 %
    + 55 / 68 +
    +
    + 13 / 68 +
    + . / test/unicode.test.js
    +
    +
    +
    +
    + 98.26 %
    + 113 / 115 +
    +
    + 2 / 115 +
    + . / test/upsert.test.js
    +
    +
    +
    +
    + 85.71 %
    + 24 / 28 +
    +
    + 4 / 28 +
    + . / test/verbose.test.js
    +
    +
    +
    +
    + 100.00 %
    + 61 / 61 +
    +
    + 0 / 61 +
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/lib/sqlite3-binding.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/lib/sqlite3-binding.js.html new file mode 100644 index 000000000..e9c7ad22a --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/lib/sqlite3-binding.js.html @@ -0,0 +1,164 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / lib/sqlite3-binding.js
    +
    +
    +
    +
    + 100.00 %
    + 6 / 6 +
    +
    + 0 / 6 +
    +
    + + +
    +
        1.      2var binary = require('node-pre-gyp');
    +
        2.      2var path = require('path');
    +
        3.      2var binding_path = binary.find(path.resolve(path.join(__dirname,'../package.json')));
    +
        4.      2var binding = require(binding_path);
    +
        5.      2module.exports = exports = binding;
    +
        6.      2
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/lib/sqlite3.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/lib/sqlite3.js.html new file mode 100644 index 000000000..3d027015d --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/lib/sqlite3.js.html @@ -0,0 +1,364 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / lib/sqlite3.js
    +
    +
    +
    +
    + 97.08 %
    + 200 / 206 +
    +
    + 6 / 206 +
    +
    + + +
    +
        1.      2var path = require('path');
    +
        2.      2var sqlite3 = require('./sqlite3-binding.js');
    +
        3.      2var EventEmitter = require('events').EventEmitter;
    +
        4.      2module.exports = exports = sqlite3;
    +
        5.      2
    +
        6.     12function normalizeMethod (fn) {
    +
        7.   4083    return function (sql) {
    +
        8.   4083        var errBack;
    +
        9.   4083        var args = Array.prototype.slice.call(arguments, 1);
    +
       10.   1118        if (typeof args[args.length - 1] === 'function') {
    +
       11.   1118            var callback = args[args.length - 1];
    +
       12.   1118            errBack = function(err) {
    +
       13.   1118                if (err) {
    +
       14.   1118                    callback(err);
    +
       15.   1118                }
    +
       16.   1118            };
    +
       17.   1118        }
    +
       18.   4083        var statement = new Statement(this, sql, errBack);
    +
       19.   4083        return fn.call(this, statement, args);
    +
       20.   4083    };
    +
       21.     12}
    +
       22.      2
    +
       23.      6function inherits(target, source) {
    +
       24.      6    for (var k in source.prototype)
    +
       25.    108        target.prototype[k] = source.prototype[k];
    +
       26.      6}
    +
       27.      2
    +
       28.      2sqlite3.cached = {
    +
       29.      4    Database: function(file, a, b) {
    +
       30.     -0        if (file === '' || file === ':memory:') {
    +
       31.     -0            // Don't cache special databases.
    +
       32.     -0            return new Database(file, a, b);
    +
       33.     -0        }
    +
       34.      4
    +
       35.      4        var db;
    +
       36.      4        file = path.resolve(file);
    +
       37.      2        function cb() { callback.call(db, null); }
    +
       38.      4
    +
       39.      2        if (!sqlite3.cached.objects[file]) {
    +
       40.      2            db = sqlite3.cached.objects[file] = new Database(file, a, b);
    +
       41.      2        }
    +
       42.      2        else {
    +
       43.      2            // Make sure the callback is called.
    +
       44.      2            db = sqlite3.cached.objects[file];
    +
       45.     -0            var callback = (typeof a === 'number') ? b : a;
    +
       46.      2            if (typeof callback === 'function') {
    +
       47.      2                if (db.open) process.nextTick(cb);
    +
       48.      2                else db.once('open', cb);
    +
       49.      2            }
    +
       50.      2        }
    +
       51.      4
    +
       52.      4        return db;
    +
       53.      4    },
    +
       54.      2    objects: {}
    +
       55.      2};
    +
       56.      2
    +
       57.      2
    +
       58.      2var Database = sqlite3.Database;
    +
       59.      2var Statement = sqlite3.Statement;
    +
       60.      2var Backup = sqlite3.Backup;
    +
       61.      2
    +
       62.      2inherits(Database, EventEmitter);
    +
       63.      2inherits(Statement, EventEmitter);
    +
       64.      2inherits(Backup, EventEmitter);
    +
       65.      2
    +
       66.      2// Database#prepare(sql, [bind1, bind2, ...], [callback])
    +
       67.   1955Database.prototype.prepare = normalizeMethod(function(statement, params) {
    +
       68.   1955    return params.length
    +
       69.      7        ? statement.bind.apply(statement, params)
    +
       70.   1948        : statement;
    +
       71.   1955});
    +
       72.      2
    +
       73.      2// Database#run(sql, [bind1, bind2, ...], [callback])
    +
       74.   2075Database.prototype.run = normalizeMethod(function(statement, params) {
    +
       75.   2075    statement.run.apply(statement, params).finalize();
    +
       76.   2075    return this;
    +
       77.   2075});
    +
       78.      2
    +
       79.      2// Database#get(sql, [bind1, bind2, ...], [callback])
    +
       80.     35Database.prototype.get = normalizeMethod(function(statement, params) {
    +
       81.     35    statement.get.apply(statement, params).finalize();
    +
       82.     35    return this;
    +
       83.     35});
    +
       84.      2
    +
       85.      2// Database#all(sql, [bind1, bind2, ...], [callback])
    +
       86.      9Database.prototype.all = normalizeMethod(function(statement, params) {
    +
       87.      9    statement.all.apply(statement, params).finalize();
    +
       88.      9    return this;
    +
       89.      9});
    +
       90.      2
    +
       91.      2// Database#each(sql, [bind1, bind2, ...], [callback], [complete])
    +
       92.      7Database.prototype.each = normalizeMethod(function(statement, params) {
    +
       93.      7    statement.each.apply(statement, params).finalize();
    +
       94.      7    return this;
    +
       95.      7});
    +
       96.      2
    +
       97.      2Database.prototype.map = normalizeMethod(function(statement, params) {
    +
       98.      2    statement.map.apply(statement, params).finalize();
    +
       99.      2    return this;
    +
      100.      2});
    +
      101.      2
    +
      102.      2// Database#backup(filename, [callback])
    +
      103.      2// Database#backup(filename, destName, sourceName, filenameIsDest, [callback])
    +
      104.     15Database.prototype.backup = function() {
    +
      105.     15    var backup;
    +
      106.     13    if (arguments.length <= 2) {
    +
      107.     13        // By default, we write the main database out to the main database of the named file.
    +
      108.     13        // This is the most likely use of the backup api.
    +
      109.     13        backup = new Backup(this, arguments[0], 'main', 'main', true, arguments[1]);
    +
      110.     13    } else {
    +
      111.      2        // Otherwise, give the user full control over the sqlite3_backup_init arguments.
    +
      112.      2        backup = new Backup(this, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]);
    +
      113.      2    }
    +
      114.     15    // Per the sqlite docs, exclude the following errors as non-fatal by default.
    +
      115.     15    backup.retryErrors = [sqlite3.BUSY, sqlite3.LOCKED];
    +
      116.     15    return backup;
    +
      117.     15};
    +
      118.      2
    +
      119.      2Statement.prototype.map = function() {
    +
      120.      2    var params = Array.prototype.slice.call(arguments);
    +
      121.      2    var callback = params.pop();
    +
      122.      2    params.push(function(err, rows) {
    +
      123.     -0        if (err) return callback(err);
    +
      124.      2        var result = {};
    +
      125.      2        if (rows.length) {
    +
      126.      2            var keys = Object.keys(rows[0]), key = keys[0];
    +
      127.      1            if (keys.length > 2) {
    +
      128.      1                // Value is an object
    +
      129.      5                for (var i = 0; i < rows.length; i++) {
    +
      130.      5                    result[rows[i][key]] = rows[i];
    +
      131.      5                }
    +
      132.      1            } else {
    +
      133.      1                var value = keys[1];
    +
      134.      1                // Value is a plain value
    +
      135.      5                for (i = 0; i < rows.length; i++) {
    +
      136.      5                    result[rows[i][key]] = rows[i][value];
    +
      137.      5                }
    +
      138.      1            }
    +
      139.      2        }
    +
      140.      2        callback(err, result);
    +
      141.      2    });
    +
      142.      2    return this.all.apply(this, params);
    +
      143.      2};
    +
      144.      2
    +
      145.      2var isVerbose = false;
    +
      146.      2
    +
      147.      2var supportedEvents = [ 'trace', 'profile', 'insert', 'update', 'delete' ];
    +
      148.      2
    +
      149.      7Database.prototype.addListener = Database.prototype.on = function(type) {
    +
      150.      7    var val = EventEmitter.prototype.addListener.apply(this, arguments);
    +
      151.      4    if (supportedEvents.indexOf(type) >= 0) {
    +
      152.      4        this.configure(type, true);
    +
      153.      4    }
    +
      154.      7    return val;
    +
      155.      7};
    +
      156.      2
    +
      157.      2Database.prototype.removeListener = function(type) {
    +
      158.      2    var val = EventEmitter.prototype.removeListener.apply(this, arguments);
    +
      159.      1    if (supportedEvents.indexOf(type) >= 0 && !this._events[type]) {
    +
      160.      1        this.configure(type, false);
    +
      161.      1    }
    +
      162.      2    return val;
    +
      163.      2};
    +
      164.      2
    +
      165.      1Database.prototype.removeAllListeners = function(type) {
    +
      166.      1    var val = EventEmitter.prototype.removeAllListeners.apply(this, arguments);
    +
      167.      1    if (supportedEvents.indexOf(type) >= 0) {
    +
      168.      1        this.configure(type, false);
    +
      169.      1    }
    +
      170.      1    return val;
    +
      171.      1};
    +
      172.      2
    +
      173.      2// Save the stack trace over EIO callbacks.
    +
      174.      1sqlite3.verbose = function() {
    +
      175.      1    if (!isVerbose) {
    +
      176.      1        var trace = require('./trace');
    +
      177.      1        [
    +
      178.      1            'prepare',
    +
      179.      1            'get',
    +
      180.      1            'run',
    +
      181.      1            'all',
    +
      182.      1            'each',
    +
      183.      1            'map',
    +
      184.      1            'close',
    +
      185.      1            'exec'
    +
      186.      8        ].forEach(function (name) {
    +
      187.      8            trace.extendTrace(Database.prototype, name);
    +
      188.      8        });
    +
      189.      1        [
    +
      190.      1            'bind',
    +
      191.      1            'get',
    +
      192.      1            'run',
    +
      193.      1            'all',
    +
      194.      1            'each',
    +
      195.      1            'map',
    +
      196.      1            'reset',
    +
      197.      1            'finalize',
    +
      198.      8        ].forEach(function (name) {
    +
      199.      8            trace.extendTrace(Statement.prototype, name);
    +
      200.      8        });
    +
      201.      1        isVerbose = true;
    +
      202.      1    }
    +
      203.      1
    +
      204.      1    return this;
    +
      205.      1};
    +
      206.      2
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/lib/trace.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/lib/trace.js.html new file mode 100644 index 000000000..da268560d --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/lib/trace.js.html @@ -0,0 +1,197 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / lib/trace.js
    +
    +
    +
    +
    + 100.00 %
    + 39 / 39 +
    +
    + 0 / 39 +
    +
    + + +
    +
        1.      1// Inspired by https://github.com/tlrobinson/long-stack-traces
    +
        2.      1var util = require('util');
    +
        3.      1
    +
        4.     16function extendTrace(object, property, pos) {
    +
        5.     16    var old = object[property];
    +
        6.      1    object[property] = function() {
    +
        7.      1        var error = new Error();
    +
        8.      1        var name = object.constructor.name + '#' + property + '(' + 
    +
        9.      2            Array.prototype.slice.call(arguments).map(function(el) {
    +
       10.      2                return util.inspect(el, false, 0);
    +
       11.      2            }).join(', ') + ')';
    +
       12.      1
    +
       13.      1        if (typeof pos === 'undefined') pos = -1;
    +
       14.      1        if (pos < 0) pos += arguments.length;
    +
       15.      1        var cb = arguments[pos];
    +
       16.      1        if (typeof arguments[pos] === 'function') {
    +
       17.      1            arguments[pos] = function replacement() {
    +
       18.      1                var err = arguments[0];
    +
       19.      1                if (err && err.stack && !err.__augmented) {
    +
       20.      1                    err.stack = filter(err).join('\n');
    +
       21.      1                    err.stack += '\n--> in ' + name;
    +
       22.      1                    err.stack += '\n' + filter(error).slice(1).join('\n');
    +
       23.      1                    err.__augmented = true;
    +
       24.      1                }
    +
       25.      1                return cb.apply(this, arguments);
    +
       26.      1            };
    +
       27.      1        }
    +
       28.      1        return old.apply(this, arguments);
    +
       29.      1    };
    +
       30.     16}
    +
       31.      1exports.extendTrace = extendTrace;
    +
       32.      1
    +
       33.      1
    +
       34.      2function filter(error) {
    +
       35.     13    return error.stack.split('\n').filter(function(line) {
    +
       36.     13        return line.indexOf(__filename) < 0;
    +
       37.     13    });
    +
       38.      2}
    +
       39.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/affected.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/affected.test.js.html new file mode 100644 index 000000000..d15e174e9 --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/affected.test.js.html @@ -0,0 +1,191 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/affected.test.js
    +
    +
    +
    +
    + 93.93 %
    + 31 / 33 +
    +
    + 2 / 33 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..');
    +
        2.      1var assert = require('assert');
    +
        3.      1
    +
        4.      1describe('query properties', function() {
    +
        5.      1    var db;
    +
        6.      1    before(function(done) {
    +
        7.      1        db = new sqlite3.Database(':memory:');
    +
        8.      1        db.run("CREATE TABLE foo (id INT, txt TEXT)", done);
    +
        9.      1    });
    +
       10.      1
    +
       11.      1    it('should return the correct lastID', function(done) {
    +
       12.      1        var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)");
    +
       13.      1        var j = 1;
    +
       14.   5000        for (var i = 0; i < 5000; i++) {
    +
       15.   5000            stmt.run(i, "demo", function(err) {
    +
       16.     -0                if (err) throw err;
    +
       17.   5000                // Relies on SQLite's row numbering to be gapless and starting
    +
       18.   5000                // from 1.
    +
       19.   5000                assert.equal(j++, this.lastID);
    +
       20.   5000            });
    +
       21.   5000        }
    +
       22.      1        db.wait(done);
    +
       23.      1    });
    +
       24.      1
    +
       25.      1    it('should return the correct changes count', function(done) {
    +
       26.      1        db.run("UPDATE foo SET id = id + 1 WHERE id % 2 = 0", function(err) {
    +
       27.     -0            if (err) throw err;
    +
       28.      1            assert.equal(2500, this.changes);
    +
       29.      1            done();
    +
       30.      1        });
    +
       31.      1    });
    +
       32.      1});
    +
       33.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/backup.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/backup.test.js.html new file mode 100644 index 000000000..38ff08288 --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/backup.test.js.html @@ -0,0 +1,438 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/backup.test.js
    +
    +
    +
    +
    + 83.21 %
    + 233 / 280 +
    +
    + 47 / 280 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..');
    +
        2.      1var assert = require('assert');
    +
        3.      1var fs = require('fs');
    +
        4.      1var helper = require('./support/helper');
    +
        5.      1
    +
        6.      1// Check that the number of rows in two tables matches.
    +
        7.      3function assertRowsMatchDb(db1, table1, db2, table2, done) {
    +
        8.      3    db1.get("SELECT COUNT(*) as count FROM " + table1, function(err, row) {
    +
        9.     -0        if (err) throw err;
    +
       10.      3        db2.get("SELECT COUNT(*) as count FROM " + table2, function(err, row2) {
    +
       11.     -0            if (err) throw err;
    +
       12.      3            assert.equal(row.count, row2.count);
    +
       13.      3            done();
    +
       14.      3        });
    +
       15.      3    });
    +
       16.      3}
    +
       17.      1
    +
       18.      1// Check that the number of rows in the table "foo" is preserved in a backup.
    +
       19.      2function assertRowsMatchFile(db, backupName, done) {
    +
       20.      2    var db2 = new sqlite3.Database(backupName, sqlite3.OPEN_READONLY, function(err) {
    +
       21.     -0        if (err) throw err;
    +
       22.      2        assertRowsMatchDb(db, 'foo', db2, 'foo', function() {
    +
       23.      2            db2.close(done);
    +
       24.      2        });
    +
       25.      2    });
    +
       26.      2}
    +
       27.      1
    +
       28.      1describe('backup', function() {
    +
       29.      1    before(function() {
    +
       30.      1        helper.ensureExists('test/tmp');
    +
       31.      1    });
    +
       32.      1
    +
       33.      1    var db;
    +
       34.     14    beforeEach(function(done) {
    +
       35.     14        helper.deleteFile('test/tmp/backup.db');
    +
       36.     14        helper.deleteFile('test/tmp/backup2.db');
    +
       37.     14        db = new sqlite3.Database('test/support/prepare.db', sqlite3.OPEN_READONLY, done);
    +
       38.     14    });
    +
       39.      1
    +
       40.     14    afterEach(function(done) {
    +
       41.      3        if (!db) { done(); }
    +
       42.     14        db.close(done);
    +
       43.     14    });
    +
       44.      1
    +
       45.      1    it ('output db created once step is called', function(done) {
    +
       46.      1        var backup = db.backup('test/tmp/backup.db', function(err) {
    +
       47.     -0            if (err) throw err;
    +
       48.      1            backup.step(1, function(err) {
    +
       49.     -0                if (err) throw err;
    +
       50.      1                assert.fileExists('test/tmp/backup.db');
    +
       51.      1                backup.finish(done);
    +
       52.      1            });
    +
       53.      1        });
    +
       54.      1    });
    +
       55.      1
    +
       56.      1    it ('copies source fully with step(-1)', function(done) {
    +
       57.      1        var backup = db.backup('test/tmp/backup.db');
    +
       58.      1        backup.step(-1, function(err) {
    +
       59.     -0            if (err) throw err;
    +
       60.      1            assert.fileExists('test/tmp/backup.db');
    +
       61.      1            backup.finish(function(err) {
    +
       62.     -0                if (err) throw err;
    +
       63.      1                assertRowsMatchFile(db, 'test/tmp/backup.db', done);
    +
       64.      1            });
    +
       65.      1        });
    +
       66.      1    });
    +
       67.      1
    +
       68.      1    it ('backup db not created if finished immediately', function(done) {
    +
       69.      1        var backup = db.backup('test/tmp/backup.db');
    +
       70.      1        backup.finish(function(err) {
    +
       71.     -0            if (err) throw err;
    +
       72.      1            assert.fileDoesNotExist('test/tmp/backup.db');
    +
       73.      1            done();
    +
       74.      1        });
    +
       75.      1    });
    +
       76.      1
    +
       77.      1    it ('error closing db if backup not finished', function(done) {
    +
       78.      1        var backup = db.backup('test/tmp/backup.db');
    +
       79.      1        db.close(function(err) {
    +
       80.      1            db = null;
    +
       81.     -0            if (!err) throw new Error('should have an error');
    +
       82.      1            if (err.errno == sqlite3.BUSY) {
    +
       83.      1                done();
    +
       84.     -0            }
    +
       85.     -0            else throw err;
    +
       86.      1        });
    +
       87.      1    });
    +
       88.      1
    +
       89.      1    it ('using the backup after finished is an error', function(done) {
    +
       90.      1        var backup = db.backup('test/tmp/backup.db');
    +
       91.      1        backup.finish(function(err) {
    +
       92.     -0            if (err) throw err;
    +
       93.      1            backup.step(1, function(err) {
    +
       94.     -0                if (!err) throw new Error('should have an error');
    +
       95.      1                if (err.errno == sqlite3.MISUSE &&
    +
       96.      1                    err.message === 'SQLITE_MISUSE: Backup is already finished') {
    +
       97.      1                    done();
    +
       98.     -0                }
    +
       99.     -0                else throw err;
    +
      100.      1            });
    +
      101.      1        });
    +
      102.      1    });
    +
      103.      1
    +
      104.      1    it ('remaining/pageCount are available after call to step', function(done) {
    +
      105.      1        var backup = db.backup('test/tmp/backup.db');
    +
      106.      1        backup.step(0, function(err) {
    +
      107.     -0            if (err) throw err;
    +
      108.      1            assert.equal(typeof this.pageCount, 'number');
    +
      109.      1            assert.equal(typeof this.remaining, 'number');
    +
      110.      1            assert.equal(this.remaining, this.pageCount);
    +
      111.      1            var prevRemaining = this.remaining;
    +
      112.      1            var prevPageCount = this.pageCount;
    +
      113.      1            backup.step(1, function(err) {
    +
      114.     -0                if (err) throw err;
    +
      115.      1                assert.notEqual(this.remaining, prevRemaining);
    +
      116.      1                assert.equal(this.pageCount, prevPageCount);
    +
      117.      1                backup.finish(done);
    +
      118.      1            });
    +
      119.      1        });
    +
      120.      1    });
    +
      121.      1
    +
      122.      1    it ('backup works if database is modified half-way through', function(done) {
    +
      123.      1        var backup = db.backup('test/tmp/backup.db');
    +
      124.      1        backup.step(-1, function(err) {
    +
      125.     -0            if (err) throw err;
    +
      126.      1            backup.finish(function(err) {
    +
      127.     -0                if (err) throw err;
    +
      128.      1                var db2 = new sqlite3.Database('test/tmp/backup.db', function(err) {
    +
      129.     -0                    if (err) throw err;
    +
      130.      1                    var backup2 = db2.backup('test/tmp/backup2.db');
    +
      131.      1                    backup2.step(1, function(err, completed) {
    +
      132.     -0                        if (err) throw err;
    +
      133.      1                        assert.equal(completed, false);  // Page size for the test db
    +
      134.      1                        // should not be raised to high.
    +
      135.      1                        db2.exec("insert into foo(txt) values('hello')", function(err) {
    +
      136.     -0                            if (err) throw err;
    +
      137.      1                            backup2.step(-1, function(err, completed) {
    +
      138.     -0                                if (err) throw err;
    +
      139.      1                                assert.equal(completed, true);
    +
      140.      1                                assertRowsMatchFile(db2, 'test/tmp/backup2.db', function() {
    +
      141.      1                                    backup2.finish(function(err) {
    +
      142.     -0                                        if (err) throw err;
    +
      143.      1                                        db2.close(done);
    +
      144.      1                                    });
    +
      145.      1                                });
    +
      146.      1                            });
    +
      147.      1                        });
    +
      148.      1                    });
    +
      149.      1                });
    +
      150.      1            });
    +
      151.      1        });
    +
      152.      1    });
    +
      153.      1
    +
      154.     -0    (sqlite3.VERSION_NUMBER < 3026000 ? it.skip : it) ('can backup from temp to main', function(done) {
    +
      155.      1        db.exec("CREATE TEMP TABLE space (txt TEXT)", function(err) {
    +
      156.     -0            if (err) throw err;
    +
      157.      1            db.exec("INSERT INTO space(txt) VALUES('monkey')", function(err) {
    +
      158.     -0                if (err) throw err;
    +
      159.      1                var backup = db.backup('test/tmp/backup.db', 'temp', 'main', true, function(err) {
    +
      160.     -0                    if (err) throw err;
    +
      161.      1                    backup.step(-1, function(err) {
    +
      162.     -0                        if (err) throw err;
    +
      163.      1                        backup.finish(function(err) {
    +
      164.     -0                            if (err) throw err;
    +
      165.      1                            var db2 = new sqlite3.Database('test/tmp/backup.db', function(err) {
    +
      166.     -0                                if (err) throw err;
    +
      167.      1                                db2.get("SELECT * FROM space", function(err, row) {
    +
      168.     -0                                    if (err) throw err;
    +
      169.      1                                    assert.equal(row.txt, 'monkey');
    +
      170.      1                                    db2.close(done);
    +
      171.      1                                });
    +
      172.      1                            });
    +
      173.      1                        });
    +
      174.      1                    });
    +
      175.      1                });
    +
      176.      1            });
    +
      177.      1        });
    +
      178.      1    });
    +
      179.      1
    +
      180.     -0    (sqlite3.VERSION_NUMBER < 3026000 ? it.skip : it) ('can backup from main to temp', function(done) {
    +
      181.      1        var backup = db.backup('test/support/prepare.db', 'main', 'temp', false, function(err) {
    +
      182.     -0            if (err) throw err;
    +
      183.      1            backup.step(-1, function(err) {
    +
      184.     -0                if (err) throw err;
    +
      185.      1                backup.finish(function(err) {
    +
      186.     -0                    if (err) throw err;
    +
      187.      1                    assertRowsMatchDb(db, 'temp.foo', db, 'main.foo', done);
    +
      188.      1                });
    +
      189.      1            });
    +
      190.      1        });
    +
      191.      1    });
    +
      192.      1
    +
      193.      1    it ('cannot backup to a locked db', function(done) {
    +
      194.      1        var db2 = new sqlite3.Database('test/tmp/backup.db', function(err) {
    +
      195.      1            db2.exec("PRAGMA locking_mode = EXCLUSIVE");
    +
      196.      1            db2.exec("BEGIN EXCLUSIVE", function(err) {
    +
      197.     -0                if (err) throw err;
    +
      198.      1                var backup = db.backup('test/tmp/backup.db');
    +
      199.      1                backup.step(-1, function(stepErr) {
    +
      200.      1                    db2.close(function(err) {
    +
      201.     -0                        if (err) throw err;
    +
      202.      1                        if (stepErr.errno == sqlite3.BUSY) {
    +
      203.      1                            backup.finish(done);
    +
      204.     -0                        }
    +
      205.     -0                        else throw stepErr;
    +
      206.      1                    });
    +
      207.      1                });
    +
      208.      1            });
    +
      209.      1        });
    +
      210.      1    });
    +
      211.      1
    +
      212.      1    it ('fuss-free incremental backups work', function(done) {
    +
      213.      1        var backup = db.backup('test/tmp/backup.db');
    +
      214.      1        var timer;
    +
      215.     81        function makeProgress() {
    +
      216.     80            if (backup.idle) {
    +
      217.     80                backup.step(1);
    +
      218.     80            }
    +
      219.     80            if (backup.completed || backup.failed) {
    +
      220.      1                clearInterval(timer);
    +
      221.      1                assert.equal(backup.completed, true);
    +
      222.      1                assert.equal(backup.failed, false);
    +
      223.      1                done();
    +
      224.      1            }
    +
      225.     81        }
    +
      226.      1        timer = setInterval(makeProgress, 2);
    +
      227.      1    });
    +
      228.      1
    +
      229.      1    it ('setting retryErrors to empty disables automatic finishing', function(done) {
    +
      230.      1        var backup = db.backup('test/tmp/backup.db');
    +
      231.      1        backup.retryErrors = [];
    +
      232.      1        backup.step(-1, function(err) {
    +
      233.     -0            if (err) throw err;
    +
      234.      1            db.close(function(err) {
    +
      235.      1                db = null;
    +
      236.     -0                if (!err) throw new Error('should have an error');
    +
      237.      1                assert.equal(err.errno, sqlite3.BUSY);
    +
      238.      1                done();
    +
      239.      1            });
    +
      240.      1        });
    +
      241.      1    });
    +
      242.      1
    +
      243.      1    it ('setting retryErrors enables automatic finishing', function(done) {
    +
      244.      1        var backup = db.backup('test/tmp/backup.db');
    +
      245.      1        backup.retryErrors = [sqlite3.OK];
    +
      246.      1        backup.step(-1, function(err) {
    +
      247.     -0            if (err) throw err;
    +
      248.      1            db.close(function(err) {
    +
      249.     -0                if (err) throw err;
    +
      250.      1                db = null;
    +
      251.      1                done();
    +
      252.      1            });
    +
      253.      1        });
    +
      254.      1    });
    +
      255.      1
    +
      256.      1    it ('default retryErrors will retry on a locked/busy db', function(done) {
    +
      257.      1        var db2 = new sqlite3.Database('test/tmp/backup.db', function(err) {
    +
      258.      1            db2.exec("PRAGMA locking_mode = EXCLUSIVE");
    +
      259.      1            db2.exec("BEGIN EXCLUSIVE", function(err) {
    +
      260.     -0                if (err) throw err;
    +
      261.      1                var backup = db.backup('test/tmp/backup.db');
    +
      262.      1                backup.step(-1, function(stepErr) {
    +
      263.      1                    db2.close(function(err) {
    +
      264.     -0                        if (err) throw err;
    +
      265.      1                        assert.equal(stepErr.errno, sqlite3.BUSY);
    +
      266.      1                        assert.equal(backup.completed, false);
    +
      267.      1                        assert.equal(backup.failed, false);
    +
      268.      1                        backup.step(-1, function(err) {
    +
      269.     -0                            if (err) throw err;
    +
      270.      1                            assert.equal(backup.completed, true);
    +
      271.      1                            assert.equal(backup.failed, false);
    +
      272.      1                            done();
    +
      273.      1                        });
    +
      274.      1                    });
    +
      275.      1                });
    +
      276.      1            });
    +
      277.      1        });
    +
      278.      1    });
    +
      279.      1});
    +
      280.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/blob.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/blob.test.js.html new file mode 100644 index 000000000..889cf132a --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/blob.test.js.html @@ -0,0 +1,213 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/blob.test.js
    +
    +
    +
    +
    + 90.90 %
    + 50 / 55 +
    +
    + 5 / 55 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..'),
    +
        2.      1    fs = require('fs'),
    +
        3.      1    assert = require('assert'),
    +
        4.      1    Buffer = require('buffer').Buffer;
    +
        5.      1
    +
        6.      1// lots of elmo
    +
        7.      1var elmo = fs.readFileSync(__dirname + '/support/elmo.png');
    +
        8.      1
    +
        9.      1describe('blob', function() {
    +
       10.      1    var db;
    +
       11.      1    before(function(done) {
    +
       12.      1        db = new sqlite3.Database(':memory:');
    +
       13.      1        db.run("CREATE TABLE elmos (id INT, image BLOB)", done);
    +
       14.      1    });
    +
       15.      1
    +
       16.      1    var total = 10;
    +
       17.      1    var inserted = 0;
    +
       18.      1    var retrieved = 0;
    +
       19.      1
    +
       20.      1
    +
       21.      1    it('should insert blobs', function(done) {
    +
       22.     10        for (var i = 0; i < total; i++) {
    +
       23.     10            db.run('INSERT INTO elmos (id, image) VALUES (?, ?)', i, elmo, function(err) {
    +
       24.     -0                if (err) throw err;
    +
       25.     10                inserted++;
    +
       26.     10            });
    +
       27.     10        }
    +
       28.      1        db.wait(function() {
    +
       29.      1            assert.equal(inserted, total);
    +
       30.      1            done();
    +
       31.      1        });
    +
       32.      1    });
    +
       33.      1
    +
       34.      1    it('should retrieve the blobs', function(done) {
    +
       35.      1        db.all('SELECT id, image FROM elmos ORDER BY id', function(err, rows) {
    +
       36.     -0            if (err) throw err;
    +
       37.     10            for (var i = 0; i < rows.length; i++) {
    +
       38.     10                assert.ok(Buffer.isBuffer(rows[i].image));
    +
       39.     10                assert.ok(elmo.length, rows[i].image);
    +
       40.     10
    +
       41.1620740                for (var j = 0; j < elmo.length; j++) {
    +
       42.     -0                    if (elmo[j] !== rows[i].image[j]) {
    +
       43.     -0                        assert.ok(false, "Wrong byte");
    +
       44.     -0                    }
    +
       45.1620740                }
    +
       46.     10
    +
       47.     10                retrieved++;
    +
       48.     10            }
    +
       49.      1
    +
       50.      1            assert.equal(retrieved, total);
    +
       51.      1            done();
    +
       52.      1        });
    +
       53.      1    });
    +
       54.      1});
    +
       55.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/cache.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/cache.test.js.html new file mode 100644 index 000000000..877f3b673 --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/cache.test.js.html @@ -0,0 +1,201 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/cache.test.js
    +
    +
    +
    +
    + 90.69 %
    + 39 / 43 +
    +
    + 4 / 43 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..');
    +
        2.      1var assert = require('assert');
    +
        3.      1var helper = require('./support/helper');
    +
        4.      1
    +
        5.      1describe('cache', function() {
    +
        6.      1    before(function() {
    +
        7.      1        helper.ensureExists('test/tmp');
    +
        8.      1    });
    +
        9.      1
    +
       10.      1    it('should cache Database objects while opening', function(done) {
    +
       11.      1        var filename = 'test/tmp/test_cache.db';
    +
       12.      1        helper.deleteFile(filename);
    +
       13.      1        var opened1 = false, opened2 = false;
    +
       14.      1        var db1 = new sqlite3.cached.Database(filename, function(err) {
    +
       15.     -0            if (err) throw err;
    +
       16.      1            opened1 = true;
    +
       17.     -0            if (opened1 && opened2) done();
    +
       18.      1        });
    +
       19.      1        var db2 = new sqlite3.cached.Database(filename, function(err) {
    +
       20.     -0            if (err) throw err;
    +
       21.      1            opened2 = true;
    +
       22.      1            if (opened1 && opened2) done();
    +
       23.      1        });
    +
       24.      1        assert.equal(db1, db2);
    +
       25.      1    });
    +
       26.      1
    +
       27.      1    it('should cache Database objects after they are open', function(done) {
    +
       28.      1        var filename = 'test/tmp/test_cache2.db';
    +
       29.      1        helper.deleteFile(filename);
    +
       30.      1        var db1, db2;
    +
       31.      1        db1 = new sqlite3.cached.Database(filename, function(err) {
    +
       32.     -0            if (err) throw err;
    +
       33.      1            process.nextTick(function() {
    +
       34.      1                db2 = new sqlite3.cached.Database(filename, function(err) {
    +
       35.      1                    done();
    +
       36.      1
    +
       37.      1                });
    +
       38.      1                assert.equal(db1, db2);
    +
       39.      1            });
    +
       40.      1        });
    +
       41.      1    });
    +
       42.      1});
    +
       43.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/constants.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/constants.test.js.html new file mode 100644 index 000000000..408c2a5a4 --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/constants.test.js.html @@ -0,0 +1,203 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/constants.test.js
    +
    +
    +
    +
    + 100.00 %
    + 45 / 45 +
    +
    + 0 / 45 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..');
    +
        2.      1var assert = require('assert');
    +
        3.      1
    +
        4.      1describe('constants', function() {
    +
        5.      1    it('should have the right OPEN_* flags', function() {
    +
        6.      1        assert.ok(sqlite3.OPEN_READONLY === 1);
    +
        7.      1        assert.ok(sqlite3.OPEN_READWRITE === 2);
    +
        8.      1        assert.ok(sqlite3.OPEN_CREATE === 4);
    +
        9.      1        assert.ok(sqlite3.OPEN_URI === 0x00000040);
    +
       10.      1        assert.ok(sqlite3.OPEN_FULLMUTEX === 0x00010000);
    +
       11.      1        assert.ok(sqlite3.OPEN_SHAREDCACHE === 0x00020000);
    +
       12.      1        assert.ok(sqlite3.OPEN_PRIVATECACHE === 0x00040000);
    +
       13.      1    });
    +
       14.      1
    +
       15.      1    it('should have the right error flags', function() {
    +
       16.      1        assert.ok(sqlite3.OK === 0);
    +
       17.      1        assert.ok(sqlite3.ERROR === 1);
    +
       18.      1        assert.ok(sqlite3.INTERNAL === 2);
    +
       19.      1        assert.ok(sqlite3.PERM === 3);
    +
       20.      1        assert.ok(sqlite3.ABORT === 4);
    +
       21.      1        assert.ok(sqlite3.BUSY === 5);
    +
       22.      1        assert.ok(sqlite3.LOCKED === 6);
    +
       23.      1        assert.ok(sqlite3.NOMEM === 7);
    +
       24.      1        assert.ok(sqlite3.READONLY === 8);
    +
       25.      1        assert.ok(sqlite3.INTERRUPT === 9);
    +
       26.      1        assert.ok(sqlite3.IOERR === 10);
    +
       27.      1        assert.ok(sqlite3.CORRUPT === 11);
    +
       28.      1        assert.ok(sqlite3.NOTFOUND === 12);
    +
       29.      1        assert.ok(sqlite3.FULL === 13);
    +
       30.      1        assert.ok(sqlite3.CANTOPEN === 14);
    +
       31.      1        assert.ok(sqlite3.PROTOCOL === 15);
    +
       32.      1        assert.ok(sqlite3.EMPTY === 16);
    +
       33.      1        assert.ok(sqlite3.SCHEMA === 17);
    +
       34.      1        assert.ok(sqlite3.TOOBIG === 18);
    +
       35.      1        assert.ok(sqlite3.CONSTRAINT === 19);
    +
       36.      1        assert.ok(sqlite3.MISMATCH === 20);
    +
       37.      1        assert.ok(sqlite3.MISUSE === 21);
    +
       38.      1        assert.ok(sqlite3.NOLFS === 22);
    +
       39.      1        assert.ok(sqlite3.AUTH === 23);
    +
       40.      1        assert.ok(sqlite3.FORMAT === 24);
    +
       41.      1        assert.ok(sqlite3.RANGE === 25);
    +
       42.      1        assert.ok(sqlite3.NOTADB === 26);
    +
       43.      1    });
    +
       44.      1});
    +
       45.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/database_fail.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/database_fail.test.js.html new file mode 100644 index 000000000..1c14c8a12 --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/database_fail.test.js.html @@ -0,0 +1,312 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/database_fail.test.js
    +
    +
    +
    +
    + 77.92 %
    + 120 / 154 +
    +
    + 34 / 154 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..');
    +
        2.      1var assert = require('assert');
    +
        3.      1
    +
        4.      1describe('error handling', function() {
    +
        5.      1    var db;
    +
        6.      1    before(function(done) {
    +
        7.      1        db = new sqlite3.Database(':memory:', done);
    +
        8.      1    });
    +
        9.      1
    +
       10.      1    it('throw when calling Database() without new', function() {
    +
       11.      1        assert.throws(function() {
    +
       12.      1            sqlite3.Database(':memory:');
    +
       13.      1        }, (/Class constructors cannot be invoked without 'new'/));
    +
       14.      1
    +
       15.      1        assert.throws(function() {
    +
       16.      1            sqlite3.Statement();
    +
       17.      1        }, (/Class constructors cannot be invoked without 'new'/));
    +
       18.      1    });
    +
       19.      1
    +
       20.      1    it('should error when calling Database#get on a missing table', function(done) {
    +
       21.      1        db.get('SELECT id, txt FROM foo', function(err, row) {
    +
       22.      1            if (err) {
    +
       23.      1                assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
    +
       24.      1                assert.equal(err.errno, sqlite3.ERROR);
    +
       25.      1                assert.equal(err.code, 'SQLITE_ERROR');
    +
       26.      1                done();
    +
       27.     -0            } else {
    +
       28.     -0                done(new Error('Completed query without error, but expected error'));
    +
       29.     -0            }
    +
       30.      1        });
    +
       31.      1    });
    +
       32.      1
    +
       33.      1    it('Database#all prepare fail', function(done) {
    +
       34.      1        db.all('SELECT id, txt FROM foo', function(err, row) {
    +
       35.      1            if (err) {
    +
       36.      1                assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
    +
       37.      1                assert.equal(err.errno, sqlite3.ERROR);
    +
       38.      1                assert.equal(err.code, 'SQLITE_ERROR');
    +
       39.      1                done();
    +
       40.     -0            } else {
    +
       41.     -0                done(new Error('Completed query without error, but expected error'));
    +
       42.     -0            }
    +
       43.      1        });
    +
       44.      1    });
    +
       45.      1
    +
       46.      1    it('Database#run prepare fail', function(done) {
    +
       47.      1        db.run('SELECT id, txt FROM foo', function(err, row) {
    +
       48.      1            if (err) {
    +
       49.      1                assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
    +
       50.      1                assert.equal(err.errno, sqlite3.ERROR);
    +
       51.      1                assert.equal(err.code, 'SQLITE_ERROR');
    +
       52.      1                done();
    +
       53.     -0            } else {
    +
       54.     -0                done(new Error('Completed query without error, but expected error'));
    +
       55.     -0            }
    +
       56.      1        });
    +
       57.      1    });
    +
       58.      1
    +
       59.      1    it('Database#each prepare fail', function(done) {
    +
       60.     -0        db.each('SELECT id, txt FROM foo', function(err, row) {
    +
       61.     -0            assert.ok(false, "this should not be called");
    +
       62.      1        }, function(err, num) {
    +
       63.      1            if (err) {
    +
       64.      1                assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
    +
       65.      1                assert.equal(err.errno, sqlite3.ERROR);
    +
       66.      1                assert.equal(err.code, 'SQLITE_ERROR');
    +
       67.      1                done();
    +
       68.     -0            } else {
    +
       69.     -0                done(new Error('Completed query without error, but expected error'));
    +
       70.     -0            }
    +
       71.      1        });
    +
       72.      1    });
    +
       73.      1
    +
       74.      1    it('Database#each prepare fail without completion handler', function(done) {
    +
       75.      1        db.each('SELECT id, txt FROM foo', function(err, row) {
    +
       76.      1            if (err) {
    +
       77.      1                assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
    +
       78.      1                assert.equal(err.errno, sqlite3.ERROR);
    +
       79.      1                assert.equal(err.code, 'SQLITE_ERROR');
    +
       80.      1                done();
    +
       81.     -0            } else {
    +
       82.     -0                done(new Error('Completed query without error, but expected error'));
    +
       83.     -0            }
    +
       84.      1        });
    +
       85.      1    });
    +
       86.      1
    +
       87.      1    it('Database#get prepare fail with param binding', function(done) {
    +
       88.      1        db.get('SELECT id, txt FROM foo WHERE id = ?', 1, function(err, row) {
    +
       89.      1            if (err) {
    +
       90.      1                assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
    +
       91.      1                assert.equal(err.errno, sqlite3.ERROR);
    +
       92.      1                assert.equal(err.code, 'SQLITE_ERROR');
    +
       93.      1                done();
    +
       94.     -0            } else {
    +
       95.     -0                done(new Error('Completed query without error, but expected error'));
    +
       96.     -0            }
    +
       97.      1        });
    +
       98.      1    });
    +
       99.      1
    +
      100.      1    it('Database#all prepare fail with param binding', function(done) {
    +
      101.      1        db.all('SELECT id, txt FROM foo WHERE id = ?', 1, function(err, row) {
    +
      102.      1            if (err) {
    +
      103.      1                assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
    +
      104.      1                assert.equal(err.errno, sqlite3.ERROR);
    +
      105.      1                assert.equal(err.code, 'SQLITE_ERROR');
    +
      106.      1                done();
    +
      107.     -0            } else {
    +
      108.     -0                done(new Error('Completed query without error, but expected error'));
    +
      109.     -0            }
    +
      110.      1        });
    +
      111.      1    });
    +
      112.      1
    +
      113.      1    it('Database#run prepare fail with param binding', function(done) {
    +
      114.      1        db.run('SELECT id, txt FROM foo WHERE id = ?', 1, function(err, row) {
    +
      115.      1            if (err) {
    +
      116.      1                assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
    +
      117.      1                assert.equal(err.errno, sqlite3.ERROR);
    +
      118.      1                assert.equal(err.code, 'SQLITE_ERROR');
    +
      119.      1                done();
    +
      120.     -0            } else {
    +
      121.     -0                done(new Error('Completed query without error, but expected error'));
    +
      122.     -0            }
    +
      123.      1        });
    +
      124.      1    });
    +
      125.      1
    +
      126.      1    it('Database#each prepare fail with param binding', function(done) {
    +
      127.     -0        db.each('SELECT id, txt FROM foo WHERE id = ?', 1, function(err, row) {
    +
      128.     -0            assert.ok(false, "this should not be called");
    +
      129.      1        }, function(err, num) {
    +
      130.      1            if (err) {
    +
      131.      1                assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
    +
      132.      1                assert.equal(err.errno, sqlite3.ERROR);
    +
      133.      1                assert.equal(err.code, 'SQLITE_ERROR');
    +
      134.      1                done();
    +
      135.     -0            } else {
    +
      136.     -0                done(new Error('Completed query without error, but expected error'));
    +
      137.     -0            }
    +
      138.      1        });
    +
      139.      1    });
    +
      140.      1
    +
      141.      1    it('Database#each prepare fail with param binding without completion handler', function(done) {
    +
      142.      1        db.each('SELECT id, txt FROM foo WHERE id = ?', 1, function(err, row) {
    +
      143.      1            if (err) {
    +
      144.      1                assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
    +
      145.      1                assert.equal(err.errno, sqlite3.ERROR);
    +
      146.      1                assert.equal(err.code, 'SQLITE_ERROR');
    +
      147.      1                done();
    +
      148.     -0            } else {
    +
      149.     -0                done(new Error('Completed query without error, but expected error'));
    +
      150.     -0            }
    +
      151.      1        });
    +
      152.      1    });
    +
      153.      1});
    +
      154.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/each.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/each.test.js.html new file mode 100644 index 000000000..fb67a6df6 --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/each.test.js.html @@ -0,0 +1,198 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/each.test.js
    +
    +
    +
    +
    + 95.00 %
    + 38 / 40 +
    +
    + 2 / 40 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..');
    +
        2.      1var assert = require('assert');
    +
        3.      1
    +
        4.      1describe('each', function() {
    +
        5.      1    var db;
    +
        6.      1    before(function(done) {
    +
        7.      1        db = new sqlite3.Database('test/support/big.db', sqlite3.OPEN_READONLY, done);
    +
        8.      1    });
    +
        9.      1
    +
       10.      1    it('retrieve 100,000 rows with Statement#each', function(done) {
    +
       11.      1        var total = 100000;
    +
       12.      1        var retrieved = 0;
    +
       13.      1        
    +
       14.      1
    +
       15. 100000        db.each('SELECT id, txt FROM foo LIMIT 0, ?', total, function(err, row) {
    +
       16.     -0            if (err) throw err;
    +
       17. 100000            retrieved++;
    +
       18. 100000            
    +
       19.      1            if(retrieved === total) {
    +
       20.      1                assert.equal(retrieved, total, "Only retrieved " + retrieved + " out of " + total + " rows.");
    +
       21.      1                done();
    +
       22.      1            }
    +
       23. 100000        });
    +
       24.      1    });
    +
       25.      1
    +
       26.      1    it('Statement#each with complete callback', function(done) {
    +
       27.      1        var total = 10000;
    +
       28.      1        var retrieved = 0;
    +
       29.      1
    +
       30.  10000        db.each('SELECT id, txt FROM foo LIMIT 0, ?', total, function(err, row) {
    +
       31.     -0            if (err) throw err;
    +
       32.  10000            retrieved++;
    +
       33.      1        }, function(err, num) {
    +
       34.      1            assert.equal(retrieved, num);
    +
       35.      1            assert.equal(retrieved, total, "Only retrieved " + retrieved + " out of " + total + " rows.");
    +
       36.      1            done();
    +
       37.      1        });
    +
       38.      1    });
    +
       39.      1});
    +
       40.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/exec.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/exec.test.js.html new file mode 100644 index 000000000..506e3accb --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/exec.test.js.html @@ -0,0 +1,198 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/exec.test.js
    +
    +
    +
    +
    + 97.50 %
    + 39 / 40 +
    +
    + 1 / 40 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..');
    +
        2.      1var assert = require('assert');
    +
        3.      1var fs = require('fs');
    +
        4.      1
    +
        5.      1describe('exec', function() {
    +
        6.      1    var db;
    +
        7.      1    before(function(done) {
    +
        8.      1        db = new sqlite3.Database(':memory:', done);
    +
        9.      1    });
    +
       10.      1
    +
       11.      1    it('Database#exec', function(done) {
    +
       12.      1        var sql = fs.readFileSync('test/support/script.sql', 'utf8');
    +
       13.      1        db.exec(sql, done);
    +
       14.      1    });
    +
       15.      1
    +
       16.      1    it('retrieve database structure', function(done) {
    +
       17.      1        db.all("SELECT type, name FROM sqlite_master ORDER BY type, name", function(err, rows) {
    +
       18.     -0            if (err) throw err;
    +
       19.      1            assert.deepEqual(rows, [
    +
       20.      1                { type: 'index', name: 'grid_key_lookup' },
    +
       21.      1                { type: 'index', name: 'grid_utfgrid_lookup' },
    +
       22.      1                { type: 'index', name: 'images_id' },
    +
       23.      1                { type: 'index', name: 'keymap_lookup' },
    +
       24.      1                { type: 'index', name: 'map_index' },
    +
       25.      1                { type: 'index', name: 'name' },
    +
       26.      1                { type: 'table', name: 'grid_key' },
    +
       27.      1                { type: 'table', name: 'grid_utfgrid' },
    +
       28.      1                { type: 'table', name: 'images' },
    +
       29.      1                { type: 'table', name: 'keymap' },
    +
       30.      1                { type: 'table', name: 'map' },
    +
       31.      1                { type: 'table', name: 'metadata' },
    +
       32.      1                { type: 'view', name: 'grid_data' },
    +
       33.      1                { type: 'view', name: 'grids' },
    +
       34.      1                { type: 'view', name: 'tiles' }
    +
       35.      1            ]);
    +
       36.      1            done();
    +
       37.      1        });
    +
       38.      1    });
    +
       39.      1});
    +
       40.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/extension.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/extension.test.js.html new file mode 100644 index 000000000..3d3d96cb4 --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/extension.test.js.html @@ -0,0 +1,184 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/extension.test.js
    +
    +
    +
    +
    + 96.15 %
    + 25 / 26 +
    +
    + 1 / 26 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..');
    +
        2.      1var assert = require('assert');
    +
        3.     -0var exists = require('fs').existsSync || require('path').existsSync;
    +
        4.      1
    +
        5.      1/*
    +
        6.      1
    +
        7.      1// disabled because this is not a generically safe test to run on all systems
    +
        8.      1
    +
        9.      1var spatialite_ext = '/usr/local/lib/libspatialite.dylib';
    +
       10.      1
    +
       11.      1describe('loadExtension', function(done) {
    +
       12.      1    var db;
    +
       13.      1    before(function(done) {
    +
       14.      1        db = new sqlite3.Database(':memory:', done);
    +
       15.      1    });
    +
       16.      1
    +
       17.      1    if (exists(spatialite_ext)) {
    +
       18.      1        it('libspatialite', function(done) {
    +
       19.      1            db.loadExtension(spatialite_ext, done);
    +
       20.      1        });
    +
       21.      1    } else {
    +
       22.      1        it('libspatialite');
    +
       23.      1    }
    +
       24.      1});
    +
       25.      1
    +
       26.      1*/
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/fts-content.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/fts-content.test.js.html new file mode 100644 index 000000000..934c42882 --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/fts-content.test.js.html @@ -0,0 +1,172 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/fts-content.test.js
    +
    +
    +
    +
    + 100.00 %
    + 14 / 14 +
    +
    + 0 / 14 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..');
    +
        2.      1var assert = require('assert');
    +
        3.      1
    +
        4.      1describe('fts', function() {
    +
        5.      1    var db;
    +
        6.      1    before(function(done) {
    +
        7.      1        db = new sqlite3.Database(':memory:', done);
    +
        8.      1    });
    +
        9.      1
    +
       10.      1    it('should create a new fts4 table', function(done) {
    +
       11.      1        db.exec('CREATE VIRTUAL TABLE t1 USING fts4(content="", a, b, c);', done);
    +
       12.      1    });
    +
       13.      1});
    +
       14.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/interrupt.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/interrupt.test.js.html new file mode 100644 index 000000000..6dc81aebd --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/interrupt.test.js.html @@ -0,0 +1,239 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/interrupt.test.js
    +
    +
    +
    +
    + 92.59 %
    + 75 / 81 +
    +
    + 6 / 81 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..');
    +
        2.      1var assert = require('assert');
    +
        3.      1
    +
        4.      1describe('interrupt', function() {
    +
        5.      1    it('should interrupt queries', function(done) {
    +
        6.      1        var interrupted = false;
    +
        7.      1        var saved = null;
    +
        8.      1
    +
        9.      1        var db = new sqlite3.Database(':memory:', function() {
    +
       10.      1            db.serialize();
    +
       11.      1
    +
       12.      1            var setup = 'create table t (n int);';
    +
       13.      8            for (var i = 0; i < 8; i += 1) {
    +
       14.      8                setup += 'insert into t values (' + i + ');';
    +
       15.      8            }
    +
       16.      1
    +
       17.      1            db.exec(setup, function(err) {
    +
       18.     -0                if (err) {
    +
       19.     -0                    return done(err);
    +
       20.     -0                }
    +
       21.      1
    +
       22.      1                var query = 'select last.n ' +
    +
       23.      1                    'from t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t as last';
    +
       24.      1
    +
       25.    132                db.each(query, function(err) {
    +
       26.      1                    if (err) {
    +
       27.      1                        saved = err;
    +
       28.    131                    } else if (!interrupted) {
    +
       29.    131                        interrupted = true;
    +
       30.    131                        db.interrupt();
    +
       31.    131                    }
    +
       32.    132                });
    +
       33.      1
    +
       34.      1                db.close(function() {
    +
       35.      1                    if (saved) {
    +
       36.      1                        assert.equal(saved.message, 'SQLITE_INTERRUPT: interrupted');
    +
       37.      1                        assert.equal(saved.errno, sqlite3.INTERRUPT);
    +
       38.      1                        assert.equal(saved.code, 'SQLITE_INTERRUPT');
    +
       39.      1                        done();
    +
       40.     -0                    } else {
    +
       41.     -0                        done(new Error('Completed query without error, but expected error'));
    +
       42.     -0                    }
    +
       43.      1                });
    +
       44.      1            });
    +
       45.      1        });
    +
       46.      1    });
    +
       47.      1
    +
       48.      1    it('should throw if interrupt is called before open', function(done) {
    +
       49.      1        var db = new sqlite3.Database(':memory:');
    +
       50.      1
    +
       51.      1        assert.throws(function() {
    +
       52.      1            db.interrupt();
    +
       53.      1        }, (/Database is not open/));
    +
       54.      1
    +
       55.      1        db.close();
    +
       56.      1        done();
    +
       57.      1    });
    +
       58.      1
    +
       59.      1    it('should throw if interrupt is called after close', function(done) {
    +
       60.      1        var db = new sqlite3.Database(':memory:');
    +
       61.      1
    +
       62.      1        db.close(function() {
    +
       63.      1            assert.throws(function() {
    +
       64.      1                db.interrupt();
    +
       65.      1            }, (/Database is not open/));
    +
       66.      1
    +
       67.      1            done();
    +
       68.      1        });
    +
       69.      1    });
    +
       70.      1
    +
       71.      1    it('should throw if interrupt is called during close', function(done) {
    +
       72.      1        var db = new sqlite3.Database(':memory:', function() {
    +
       73.      1            db.close();
    +
       74.      1            assert.throws(function() {
    +
       75.      1                db.interrupt();
    +
       76.      1            }, (/Database is closing/));
    +
       77.      1            done();
    +
       78.      1        });
    +
       79.      1    });
    +
       80.      1});
    +
       81.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/issue-108.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/issue-108.test.js.html new file mode 100644 index 000000000..375434589 --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/issue-108.test.js.html @@ -0,0 +1,187 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/issue-108.test.js
    +
    +
    +
    +
    + 89.65 %
    + 26 / 29 +
    +
    + 3 / 29 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..'),
    +
        2.      1    assert = require('assert');
    +
        3.      1
    +
        4.      1describe('buffer', function() {
    +
        5.      1    var db;
    +
        6.      1    // before(function() {
    +
        7.      1    // });
    +
        8.      1
    +
        9.      1    it('should insert blobs', function(done) {
    +
       10.      1        db = new sqlite3.Database(':memory:');
    +
       11.      1        db.serialize(function () {
    +
       12.      1
    +
       13.      1            db.run("CREATE TABLE lorem (info BLOB)");
    +
       14.      1            var stmt = db.prepare("INSERT INTO lorem VALUES (?)");
    +
       15.      1
    +
       16.     -0            stmt.on('error', function (err) {
    +
       17.     -0                throw err;
    +
       18.     -0            });
    +
       19.      1
    +
       20.      1            var buff = new Buffer(2);
    +
       21.      1            stmt.run(buff);
    +
       22.      1            stmt.finalize();
    +
       23.      1        });
    +
       24.      1
    +
       25.      1        db.close(done);
    +
       26.      1
    +
       27.      1    });
    +
       28.      1});
    +
       29.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/json.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/json.test.js.html new file mode 100644 index 000000000..cbc506119 --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/json.test.js.html @@ -0,0 +1,181 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/json.test.js
    +
    +
    +
    +
    + 65.21 %
    + 15 / 23 +
    +
    + 8 / 23 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..');
    +
        2.      1
    +
        3.     -0if( process.env.NODE_SQLITE3_JSON1 === 'no' ){
    +
        4.     -0    describe('json', function() {
    +
        5.     -0        it(
    +
        6.     -0            'skips JSON tests when --sqlite=/usr (or similar) is tested',
    +
        7.     -0            function(){}
    +
        8.     -0        );
    +
        9.     -0    });
    +
       10.     -0} else {
    +
       11.      1    describe('json', function() {
    +
       12.      1        var db;
    +
       13.      1
    +
       14.      1        before(function(done) {
    +
       15.      1            db = new sqlite3.Database(':memory:', done);
    +
       16.      1        });
    +
       17.      1
    +
       18.      1        it('should select JSON', function(done) {
    +
       19.      1            db.run('SELECT json(?)', JSON.stringify({ok:true}), done);
    +
       20.      1        });
    +
       21.      1    });
    +
       22.      1}
    +
       23.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/map.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/map.test.js.html new file mode 100644 index 000000000..56a09a7b3 --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/map.test.js.html @@ -0,0 +1,222 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/map.test.js
    +
    +
    +
    +
    + 93.75 %
    + 60 / 64 +
    +
    + 4 / 64 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..');
    +
        2.      1var assert = require('assert');
    +
        3.      1
    +
        4.      1describe('map', function() {
    +
        5.      1    it('test Database#map() with two columns', function(done) {
    +
        6.      1        var count = 10;
    +
        7.      1        var inserted = 0;
    +
        8.      1
    +
        9.      1        var db = new sqlite3.Database(':memory:');
    +
       10.      1        db.serialize(function() {
    +
       11.      1            db.run("CREATE TABLE foo (id INT, value TEXT)");
    +
       12.      1
    +
       13.      1            var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)");
    +
       14.      5            for (var i = 5; i < count; i++) {
    +
       15.      5                stmt.run(i, 'Value for ' + i, function(err) {
    +
       16.     -0                    if (err) throw err;
    +
       17.      5                    inserted++;
    +
       18.      5                });
    +
       19.      5            }
    +
       20.      1            stmt.finalize();
    +
       21.      1
    +
       22.      1            db.map("SELECT * FROM foo", function(err, map) {
    +
       23.     -0                if (err) throw err;
    +
       24.      1                assert.deepEqual(map, { 5: 'Value for 5', 6: 'Value for 6', 7: 'Value for 7', 8: 'Value for 8', 9: 'Value for 9' });
    +
       25.      1                assert.equal(inserted, 5);
    +
       26.      1                done();
    +
       27.      1            });
    +
       28.      1        });
    +
       29.      1    });
    +
       30.      1
    +
       31.      1    it('test Database#map() with three columns', function(done) {
    +
       32.      1        var db = new sqlite3.Database(':memory:');
    +
       33.      1
    +
       34.      1        var count = 10;
    +
       35.      1        var inserted = 0;
    +
       36.      1
    +
       37.      1        db.serialize(function() {
    +
       38.      1            db.run("CREATE TABLE foo (id INT, value TEXT, other TEXT)");
    +
       39.      1
    +
       40.      1            var stmt = db.prepare("INSERT INTO foo VALUES(?, ?, ?)");
    +
       41.      5            for (var i = 5; i < count; i++) {
    +
       42.      5                stmt.run(i, 'Value for ' + i, null, function(err) {
    +
       43.     -0                    if (err) throw err;
    +
       44.      5                    inserted++;
    +
       45.      5                });
    +
       46.      5            }
    +
       47.      1            stmt.finalize();
    +
       48.      1
    +
       49.      1            db.map("SELECT * FROM foo", function(err, map) {
    +
       50.     -0                if (err) throw err;
    +
       51.      1                assert.deepEqual(map, {
    +
       52.      1                    5: { id: 5, value: 'Value for 5', other: null },
    +
       53.      1                    6: { id: 6, value: 'Value for 6', other: null },
    +
       54.      1                    7: { id: 7, value: 'Value for 7', other: null },
    +
       55.      1                    8: { id: 8, value: 'Value for 8', other: null },
    +
       56.      1                    9: { id: 9, value: 'Value for 9', other: null }
    +
       57.      1                });
    +
       58.      1                assert.equal(inserted, 5);
    +
       59.      1                done();
    +
       60.      1            });
    +
       61.      1        });
    +
       62.      1    });
    +
       63.      1});
    +
       64.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/named_columns.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/named_columns.test.js.html new file mode 100644 index 000000000..9222a7620 --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/named_columns.test.js.html @@ -0,0 +1,197 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/named_columns.test.js
    +
    +
    +
    +
    + 94.87 %
    + 37 / 39 +
    +
    + 2 / 39 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..');
    +
        2.      1var assert = require('assert');
    +
        3.      1
    +
        4.      1describe('named columns', function() {
    +
        5.      1    var db;
    +
        6.      1    before(function(done) {
    +
        7.      1        db = new sqlite3.Database(':memory:', done);
    +
        8.      1    });
    +
        9.      1
    +
       10.      1    it('should create the table', function(done) {
    +
       11.      1        db.run("CREATE TABLE foo (txt TEXT, num INT)", done);
    +
       12.      1    });
    +
       13.      1
    +
       14.      1    it('should insert a value', function(done) {
    +
       15.      1        db.run("INSERT INTO foo VALUES($text, $id)", {
    +
       16.      1            $id: 1,
    +
       17.      1            $text: "Lorem Ipsum"
    +
       18.      1        }, done);
    +
       19.      1    });
    +
       20.      1
    +
       21.      1    it('should retrieve the values', function(done) {
    +
       22.      1        db.get("SELECT txt, num FROM foo ORDER BY num", function(err, row) {
    +
       23.     -0            if (err) throw err;
    +
       24.      1            assert.equal(row.txt, "Lorem Ipsum");
    +
       25.      1            assert.equal(row.num, 1);
    +
       26.      1            done();
    +
       27.      1        });
    +
       28.      1    });
    +
       29.      1
    +
       30.      1    it('should be able to retrieve rowid of last inserted value', function(done) {
    +
       31.      1        db.get("SELECT last_insert_rowid() as last_id FROM foo", function(err, row) {
    +
       32.     -0            if (err) throw err;
    +
       33.      1            assert.equal(row.last_id, 1);
    +
       34.      1            done();
    +
       35.      1        });
    +
       36.      1    });
    +
       37.      1
    +
       38.      1});
    +
       39.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/named_params.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/named_params.test.js.html new file mode 100644 index 000000000..bd07abd12 --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/named_params.test.js.html @@ -0,0 +1,228 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/named_params.test.js
    +
    +
    +
    +
    + 98.57 %
    + 69 / 70 +
    +
    + 1 / 70 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..');
    +
        2.      1var assert = require('assert');
    +
        3.      1
    +
        4.      1describe('named parameters', function() {
    +
        5.      1    var db;
    +
        6.      1    before(function(done) {
    +
        7.      1        db = new sqlite3.Database(':memory:', done);
    +
        8.      1    });
    +
        9.      1
    +
       10.      1    it('should create the table', function(done) {
    +
       11.      1        db.run("CREATE TABLE foo (txt TEXT, num INT)", done);
    +
       12.      1    });
    +
       13.      1
    +
       14.      1    it('should insert a value with $ placeholders', function(done) {
    +
       15.      1        db.run("INSERT INTO foo VALUES($text, $id)", {
    +
       16.      1            $id: 1,
    +
       17.      1            $text: "Lorem Ipsum"
    +
       18.      1        }, done);
    +
       19.      1    });
    +
       20.      1
    +
       21.      1    it('should insert a value with : placeholders', function(done) {
    +
       22.      1        db.run("INSERT INTO foo VALUES(:text, :id)", {
    +
       23.      1            ':id': 2,
    +
       24.      1            ':text': "Dolor Sit Amet"
    +
       25.      1        }, done);
    +
       26.      1    });
    +
       27.      1
    +
       28.      1    it('should insert a value with @ placeholders', function(done) {
    +
       29.      1        db.run("INSERT INTO foo VALUES(@txt, @id)", {
    +
       30.      1            "@id": 3,
    +
       31.      1            "@txt": "Consectetur Adipiscing Elit"
    +
       32.      1        }, done);
    +
       33.      1    });
    +
       34.      1
    +
       35.      1    it('should insert a value with @ placeholders using an array', function(done) {
    +
       36.      1        db.run("INSERT INTO foo VALUES(@txt, @id)", [ 'Sed Do Eiusmod', 4 ], done);
    +
       37.      1    });
    +
       38.      1
    +
       39.      1    it('should insert a value with indexed placeholders', function(done) {
    +
       40.      1        db.run("INSERT INTO foo VALUES(?2, ?4)",
    +
       41.      1            [ null, 'Tempor Incididunt', null, 5 ], done);
    +
       42.      1    });
    +
       43.      1
    +
       44.      1    it('should insert a value with autoindexed placeholders', function(done) {
    +
       45.      1        db.run("INSERT INTO foo VALUES(?, ?)", {
    +
       46.      1            2: 6,
    +
       47.      1            1: "Ut Labore Et Dolore"
    +
       48.      1        }, done);
    +
       49.      1    });
    +
       50.      1
    +
       51.      1    it('should retrieve all inserted values', function(done) {
    +
       52.      1        db.all("SELECT txt, num FROM foo ORDER BY num", function(err, rows) {
    +
       53.     -0            if (err) throw err;
    +
       54.      1            assert.equal(rows[0].txt, "Lorem Ipsum");
    +
       55.      1            assert.equal(rows[0].num, 1);
    +
       56.      1            assert.equal(rows[1].txt, "Dolor Sit Amet");
    +
       57.      1            assert.equal(rows[1].num, 2);
    +
       58.      1            assert.equal(rows[2].txt, "Consectetur Adipiscing Elit");
    +
       59.      1            assert.equal(rows[2].num, 3);
    +
       60.      1            assert.equal(rows[3].txt, "Sed Do Eiusmod");
    +
       61.      1            assert.equal(rows[3].num, 4);
    +
       62.      1            assert.equal(rows[4].txt, "Tempor Incididunt");
    +
       63.      1            assert.equal(rows[4].num, 5);
    +
       64.      1            assert.equal(rows[5].txt, "Ut Labore Et Dolore");
    +
       65.      1            assert.equal(rows[5].num, 6);
    +
       66.      1            done();
    +
       67.      1        });
    +
       68.      1    });
    +
       69.      1});
    +
       70.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/null_error.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/null_error.test.js.html new file mode 100644 index 000000000..fe9865c53 --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/null_error.test.js.html @@ -0,0 +1,200 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/null_error.test.js
    +
    +
    +
    +
    + 95.23 %
    + 40 / 42 +
    +
    + 2 / 42 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..');
    +
        2.      1var assert = require('assert');
    +
        3.      1var helper = require('./support/helper');
    +
        4.      1
    +
        5.      1describe('null error', function() {
    +
        6.      1    var filename = 'test/tmp/test_sqlite_ok_error.db';
    +
        7.      1    var db;
    +
        8.      1
    +
        9.      1    before(function(done) {
    +
       10.      1        helper.ensureExists('test/tmp');
    +
       11.      1        helper.deleteFile(filename);
    +
       12.      1        db = new sqlite3.Database(filename, done);
    +
       13.      1    });
    +
       14.      1
    +
       15.      1    it('should create a table', function(done) {
    +
       16.      1        db.run("CREATE TABLE febp_data (leacode TEXT, leaname TEXT, state TEXT, postcode TEXT, fips TEXT, titleistim TEXT, ideastim TEXT, ideapool TEXT, ideapoolname TEXT, localebasis TEXT, localetype2 TEXT, version TEXT, leacount_2006 TEXT, ppexpend_2005 TEXT, ppexpend_2006 TEXT, ppexpend_2007 TEXT, ppexpend_2008 TEXT, ppexpendrank_2006 TEXT, ppexpendrank_2007 TEXT, ppexpendrank_2008 TEXT, rankppexpend_2005 TEXT, opbud_2004 TEXT, opbud_2006 TEXT, opbud_2007 TEXT, opbud_2008 TEXT, titlei_2004 TEXT, titlei_2006 TEXT, titlei_2007 TEXT, titlei_2008 TEXT, titlei_2009 TEXT, titlei_2010 TEXT, idea_2004 TEXT, idea_2005 TEXT, idea_2006 TEXT, idea_2007 TEXT, idea_2008 TEXT, idea_2009 TEXT, ideaest_2010 TEXT, impact_2007 TEXT, impact_2008 TEXT, impact_2009 TEXT, impact_2010 TEXT, fedrev_2006 TEXT, fedrev_2007 TEXT, fedrev_2008 TEXT, schonut_2006 TEXT, schonut_2007 TEXT, schomeal_2006 TEXT, schomeal_2007 TEXT, schoco_2006 TEXT, schocom_2007 TEXT, medicaid_2006 TEXT, medicaid_2007 TEXT, medicaid_2008 TEXT, cenpov_2004 TEXT, cenpov_2007 TEXT, cenpov_2008 TEXT, rankcenpov_2004 TEXT, rankcenpov_2007 TEXT, rankcenpov_2008 TEXT, enroll_2006 TEXT, enroll_2007 TEXT, enroll_2008 TEXT, white_2006 TEXT, white_2007 TEXT, white_2008 TEXT, afam_2006 TEXT, afam_2007 TEXT, afam_2008 TEXT, amin_2006 TEXT, amin_2007 TEXT, amin_2008 TEXT, asian_2006 TEXT, asian_2007 TEXT, asian_2008 TEXT, hisp_2006 TEXT, hisp_2007 TEXT, hisp_2008 TEXT, frpl_2006 TEXT, frpl_2007 TEXT, frpl_2008 TEXT, ell_2006 TEXT, ell_2007 TEXT, ell_2008 TEXT, sped_2006 TEXT, sped_2007 TEXT, sped_2008 TEXT, state4read_2005 TEXT, state4read_2006 TEXT, state4read_2007 TEXT, state4read_2008 TEXT, state4read_2009 TEXT, state4math_2005 TEXT, state4math_2006 TEXT, state4math_2007 TEXT, state4math_2008 TEXT, state4math_2009 TEXT, minor_2007 TEXT, minor_2008 TEXT, state8math_2006 TEXT, state8math_2007 TEXT, state8math_2008 TEXT, state8math_2009 TEXT, state8read_2006 TEXT, state8read_2007 TEXT, state8read_2008 TEXT, state8read_2009 TEXT, statehsmath_2006 TEXT, statehsmath_2007 TEXT, statehsmath_2008 TEXT, statehsmath_2009 TEXT, statehsread_2006 TEXT, statehsread_2007 TEXT, statehsread_2008 TEXT, statehsread_2009 TEXT)", done);
    +
       17.      1    });
    +
       18.      1
    +
       19.      1    it('should insert rows with lots of null values', function(done) {
    +
       20.      1        var stmt = db.prepare('INSERT INTO febp_data VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', function(err) {
    +
       21.     -0            if (err) throw err;
    +
       22.      1
    +
       23.    100            for (var i = 0; i < 100; i++) {
    +
       24.    100                stmt.run([ '100005', 'Albertville City School District', 'ALABAMA', 'AL', '1', '856031', '753000', 'NULL', 'NULL', '6-Small Town', 'Town', 21, '130', '6624', '7140', '8731', '8520', '102', '88', '100', '94', '23352000', '27280000', '30106000', '33028000', '768478', '845886', '782696', '1096819', '1279663', '1168521', '561522', '657649', '684366', '687531', '710543', '727276', '726647', 'N/A', 'N/A', 'N/A', 'N/A', '986', '977', '1006', '1080250', '1202325', '1009962', '1109310', '70287', '93015', '14693.56', '13634.58', 'N/A', '0.230', '0.301', '0.268882175', '73', '26', '29', '3718', '3747', '3790', '2663', '2615', '2575', '75', '82', '89', '3', '2', '6', '11', '9', '8', '955', '1028', '1102', '1991', '2061', '2146', '649', '729', '770', '443', '278', '267', '0.860', '0.86', '0.8474', '0.84', '0.8235', '0.810', '0.84', '0.7729', '0.75', '0.7843', '1121', '1205', '0.74', '0.6862', '0.72', '0.7317', '0.78', '0.7766', '0.79', '0.7387', '0.84', '0.9255', '0.86', '0.9302', '0.88', '0.9308', '0.84', '0.8605' ]);
    +
       25.    100            }
    +
       26.      1
    +
       27.      1            stmt.finalize(function(err) {
    +
       28.     -0                if (err) throw err;
    +
       29.      1                done();
    +
       30.      1            });
    +
       31.      1        });
    +
       32.      1    });
    +
       33.      1
    +
       34.      1    it('should have created the database', function() {
    +
       35.      1        assert.fileExists(filename);
    +
       36.      1    });
    +
       37.      1
    +
       38.      1    after(function() {
    +
       39.      1        helper.deleteFile(filename);
    +
       40.      1    });
    +
       41.      1});
    +
       42.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/open_close.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/open_close.test.js.html new file mode 100644 index 000000000..0a20b3134 --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/open_close.test.js.html @@ -0,0 +1,346 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/open_close.test.js
    +
    +
    +
    +
    + 93.61 %
    + 176 / 188 +
    +
    + 12 / 188 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..');
    +
        2.      1var assert = require('assert');
    +
        3.      1var fs = require('fs');
    +
        4.      1var helper = require('./support/helper');
    +
        5.      1
    +
        6.      1describe('open/close', function() {
    +
        7.      1    before(function() {
    +
        8.      1        helper.ensureExists('test/tmp');
    +
        9.      1    });
    +
       10.      1
    +
       11.      1    describe('open and close non-existant database', function() {
    +
       12.      1        before(function() {
    +
       13.      1            helper.deleteFile('test/tmp/test_create.db');
    +
       14.      1        });
    +
       15.      1
    +
       16.      1        var db;
    +
       17.      1        it('should open the database', function(done) {
    +
       18.      1            db = new sqlite3.Database('test/tmp/test_create.db', done);
    +
       19.      1        });
    +
       20.      1
    +
       21.      1        it('should close the database', function(done) {
    +
       22.      1            db.close(done);
    +
       23.      1        });
    +
       24.      1
    +
       25.      1        it('should have created the file', function() {
    +
       26.      1            assert.fileExists('test/tmp/test_create.db');
    +
       27.      1        });
    +
       28.      1
    +
       29.      1        after(function() {
    +
       30.      1            helper.deleteFile('test/tmp/test_create.db');
    +
       31.      1        });
    +
       32.      1    });
    +
       33.      1    
    +
       34.      1    describe('open and close non-existant shared database', function() {
    +
       35.      1        before(function() {
    +
       36.      1            helper.deleteFile('test/tmp/test_create_shared.db');
    +
       37.      1        });
    +
       38.      1
    +
       39.      1        var db;
    +
       40.      1        it('should open the database', function(done) {
    +
       41.      1            db = new sqlite3.Database('file:./test/tmp/test_create_shared.db', sqlite3.OPEN_URI | sqlite3.OPEN_SHAREDCACHE | sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, done);
    +
       42.      1        });
    +
       43.      1
    +
       44.      1        it('should close the database', function(done) {
    +
       45.      1            db.close(done);
    +
       46.      1        });
    +
       47.      1
    +
       48.      1        it('should have created the file', function() {
    +
       49.      1            assert.fileExists('test/tmp/test_create_shared.db');
    +
       50.      1        });
    +
       51.      1
    +
       52.      1        after(function() {
    +
       53.      1            helper.deleteFile('test/tmp/test_create_shared.db');
    +
       54.      1        });
    +
       55.      1    });
    +
       56.      1
    +
       57.      1
    +
       58.     -0    (sqlite3.VERSION_NUMBER < 3008000 ? describe.skip : describe)('open and close shared memory database', function() {
    +
       59.      1
    +
       60.      1        var db1;
    +
       61.      1        var db2;
    +
       62.      1
    +
       63.      1        it('should open the first database', function(done) {
    +
       64.      1            db1 = new sqlite3.Database('file:./test/tmp/test_memory.db?mode=memory', sqlite3.OPEN_URI | sqlite3.OPEN_SHAREDCACHE | sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, done);
    +
       65.      1        });
    +
       66.      1
    +
       67.      1        it('should open the second database', function(done) {
    +
       68.      1            db2 = new sqlite3.Database('file:./test/tmp/test_memory.db?mode=memory', sqlite3.OPEN_URI | sqlite3.OPEN_SHAREDCACHE | sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, done);
    +
       69.      1        });
    +
       70.      1
    +
       71.      1        it('first database should set the user_version', function(done) {
    +
       72.      1            db1.exec('PRAGMA user_version=42', done);
    +
       73.      1        });
    +
       74.      1
    +
       75.      1        it('second database should get the user_version', function(done) {
    +
       76.      1            db2.get('PRAGMA user_version', function(err, row) {
    +
       77.     -0                if (err) throw err;
    +
       78.      1                assert.equal(row.user_version, 42);
    +
       79.      1                done();
    +
       80.      1            });
    +
       81.      1        });
    +
       82.      1
    +
       83.      1        it('should close the first database', function(done) {
    +
       84.      1            db1.close(done);
    +
       85.      1        });
    +
       86.      1
    +
       87.      1        it('should close the second database', function(done) {
    +
       88.      1            db2.close(done);
    +
       89.      1        });
    +
       90.      1    });
    +
       91.      1
    +
       92.      1    it('should not be unable to open an inaccessible database', function(done) {
    +
       93.      1        // NOTE: test assumes that the user is not allowed to create new files
    +
       94.      1        // in /usr/bin.
    +
       95.      1        var db = new sqlite3.Database('/test/tmp/directory-does-not-exist/test.db', function(err) {
    +
       96.      1            if (err && err.errno === sqlite3.CANTOPEN) {
    +
       97.      1                done();
    +
       98.     -0            } else if (err) {
    +
       99.     -0                done(err);
    +
      100.     -0            } else {
    +
      101.     -0                done('Opened database that should be inaccessible');
    +
      102.     -0            }
    +
      103.      1        });
    +
      104.      1    });
    +
      105.      1
    +
      106.      1
    +
      107.      1    describe('creating database without create flag', function() {
    +
      108.      1        before(function() {
    +
      109.      1            helper.deleteFile('test/tmp/test_readonly.db');
    +
      110.      1        });
    +
      111.      1
    +
      112.      1        it('should fail to open the database', function(done) {
    +
      113.      1            new sqlite3.Database('tmp/test_readonly.db', sqlite3.OPEN_READONLY, function(err) {
    +
      114.      1                if (err && err.errno === sqlite3.CANTOPEN) {
    +
      115.      1                    done();
    +
      116.     -0                } else if (err) {
    +
      117.     -0                    done(err);
    +
      118.     -0                } else {
    +
      119.     -0                    done('Created database without create flag');
    +
      120.     -0                }
    +
      121.      1            });
    +
      122.      1        });
    +
      123.      1
    +
      124.      1        it('should not have created the file', function() {
    +
      125.      1            assert.fileDoesNotExist('test/tmp/test_readonly.db');
    +
      126.      1        });
    +
      127.      1
    +
      128.      1        after(function() {
    +
      129.      1            helper.deleteFile('test/tmp/test_readonly.db');
    +
      130.      1        });
    +
      131.      1    });
    +
      132.      1
    +
      133.      1    describe('open and close memory database queuing', function() {
    +
      134.      1        var db;
    +
      135.      1        it('should open the database', function(done) {
    +
      136.      1            db = new sqlite3.Database(':memory:', done);
    +
      137.      1        });
    +
      138.      1
    +
      139.      1        it('should close the database', function(done) {
    +
      140.      1            db.close(done);
    +
      141.      1        });
    +
      142.      1
    +
      143.      1        it('shouldn\'t close the database again', function(done) {
    +
      144.      1            db.close(function(err) {
    +
      145.      1                assert.ok(err, 'No error object received on second close');
    +
      146.      1                assert.ok(err.errno === sqlite3.MISUSE);
    +
      147.      1                done();
    +
      148.      1            });
    +
      149.      1        });
    +
      150.      1    });
    +
      151.      1
    +
      152.      1    describe('closing with unfinalized statements', function(done) {
    +
      153.      1        var completed = false;
    +
      154.      1        var completedSecond = false;
    +
      155.      1        var closed = false;
    +
      156.      1
    +
      157.      1        var db;
    +
      158.      1        before(function() {
    +
      159.      1            db = new sqlite3.Database(':memory:', done);
    +
      160.      1        });
    +
      161.      1
    +
      162.      1        it('should create a table', function(done) {
    +
      163.      1            db.run("CREATE TABLE foo (id INT, num INT)", done);
    +
      164.      1        });
    +
      165.      1
    +
      166.      1        var stmt;
    +
      167.      1        it('should prepare/run a statement', function(done) {
    +
      168.      1            stmt = db.prepare('INSERT INTO foo VALUES (?, ?)');
    +
      169.      1            stmt.run(1, 2, done);
    +
      170.      1        });
    +
      171.      1
    +
      172.      1        it('should fail to close the database', function(done) {
    +
      173.      1            db.close(function(err) {
    +
      174.      1                assert.ok(err.message,
    +
      175.      1                    "SQLITE_BUSY: unable to close due to unfinalised statements");
    +
      176.      1                done();
    +
      177.      1            });
    +
      178.      1        });
    +
      179.      1
    +
      180.      1        it('should succeed to close the database after finalizing', function(done) {
    +
      181.      1            stmt.run(3, 4, function() {
    +
      182.      1                stmt.finalize();
    +
      183.      1                db.close(done);
    +
      184.      1            });
    +
      185.      1        });
    +
      186.      1    });
    +
      187.      1});
    +
      188.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/other_objects.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/other_objects.test.js.html new file mode 100644 index 000000000..31011da13 --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/other_objects.test.js.html @@ -0,0 +1,248 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/other_objects.test.js
    +
    +
    +
    +
    + 91.11 %
    + 82 / 90 +
    +
    + 8 / 90 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..');
    +
        2.      1var assert = require('assert');
    +
        3.      1
    +
        4.      1describe('data types', function() {
    +
        5.      1    var db;
    +
        6.      1    before(function(done) {
    +
        7.      1        db = new sqlite3.Database(':memory:');
    +
        8.      1        db.run("CREATE TABLE txt_table (txt TEXT)");
    +
        9.      1        db.run("CREATE TABLE int_table (int INTEGER)");
    +
       10.      1        db.run("CREATE TABLE flt_table (flt FLOAT)");
    +
       11.      1        db.wait(done);
    +
       12.      1    });
    +
       13.      1
    +
       14.     22    beforeEach(function(done) {
    +
       15.     22        db.exec('DELETE FROM txt_table; DELETE FROM int_table; DELETE FROM flt_table;', done);
    +
       16.     22    });
    +
       17.      1
    +
       18.      1    it('should serialize Date()', function(done) {
    +
       19.      1        var date = new Date();
    +
       20.      1        db.run("INSERT INTO int_table VALUES(?)", date, function (err) {
    +
       21.     -0            if (err) throw err;
    +
       22.      1            db.get("SELECT int FROM int_table", function(err, row) {
    +
       23.     -0                if (err) throw err;
    +
       24.      1                assert.equal(row.int, +date);
    +
       25.      1                done();
    +
       26.      1            });
    +
       27.      1        });
    +
       28.      1    });
    +
       29.      1
    +
       30.      1    it('should serialize RegExp()', function(done) {
    +
       31.      1        var regexp = /^f\noo/;
    +
       32.      1        db.run("INSERT INTO txt_table VALUES(?)", regexp, function (err) {
    +
       33.     -0            if (err) throw err;
    +
       34.      1            db.get("SELECT txt FROM txt_table", function(err, row) {
    +
       35.     -0                if (err) throw err;
    +
       36.      1                assert.equal(row.txt, String(regexp));
    +
       37.      1                done();
    +
       38.      1            });
    +
       39.      1        });
    +
       40.      1    });
    +
       41.      1
    +
       42.      1    [
    +
       43.      1        4294967296.249,
    +
       44.      1        Math.PI,
    +
       45.      1        3924729304762836.5,
    +
       46.      1        new Date().valueOf(),
    +
       47.      1        912667.394828365,
    +
       48.      1        2.3948728634826374e+83,
    +
       49.      1        9.293476892934982e+300,
    +
       50.      1        Infinity,
    +
       51.      1        -9.293476892934982e+300,
    +
       52.      1        -2.3948728634826374e+83,
    +
       53.      1        -Infinity
    +
       54.     11    ].forEach(function(flt) {
    +
       55.     11        it('should serialize float ' + flt, function(done) {
    +
       56.     11            db.run("INSERT INTO flt_table VALUES(?)", flt, function (err) {
    +
       57.     -0                if (err) throw err;
    +
       58.     11                db.get("SELECT flt FROM flt_table", function(err, row) {
    +
       59.     -0                    if (err) throw err;
    +
       60.     11                    assert.equal(row.flt, flt);
    +
       61.     11                    done();
    +
       62.     11                });
    +
       63.     11            });
    +
       64.     11        });
    +
       65.     11    });
    +
       66.      1
    +
       67.      1    [
    +
       68.      1        4294967299,
    +
       69.      1        3924729304762836,
    +
       70.      1        new Date().valueOf(),
    +
       71.      1        2.3948728634826374e+83,
    +
       72.      1        9.293476892934982e+300,
    +
       73.      1        Infinity,
    +
       74.      1        -9.293476892934982e+300,
    +
       75.      1        -2.3948728634826374e+83,
    +
       76.      1        -Infinity
    +
       77.      9    ].forEach(function(integer) {
    +
       78.      9        it('should serialize integer ' + integer, function(done) {
    +
       79.      9            db.run("INSERT INTO int_table VALUES(?)", integer, function (err) {
    +
       80.     -0                if (err) throw err;
    +
       81.      9                db.get("SELECT int AS integer FROM int_table", function(err, row) {
    +
       82.     -0                    if (err) throw err;
    +
       83.      9                    assert.equal(row.integer, integer);
    +
       84.      9                    done();
    +
       85.      9                });
    +
       86.      9            });
    +
       87.      9        });
    +
       88.      9    });
    +
       89.      1});
    +
       90.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/parallel_insert.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/parallel_insert.test.js.html new file mode 100644 index 000000000..85a8bfb24 --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/parallel_insert.test.js.html @@ -0,0 +1,203 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/parallel_insert.test.js
    +
    +
    +
    +
    + 100.00 %
    + 45 / 45 +
    +
    + 0 / 45 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..');
    +
        2.      1var assert = require('assert');
    +
        3.      1var helper = require('./support/helper');
    +
        4.      1
    +
        5.      1describe('parallel', function() {
    +
        6.      1    var db;
    +
        7.      1    before(function(done) {
    +
        8.      1        helper.deleteFile('test/tmp/test_parallel_inserts.db');
    +
        9.      1        helper.ensureExists('test/tmp');
    +
       10.      1        db = new sqlite3.Database('test/tmp/test_parallel_inserts.db', done);
    +
       11.      1    });
    +
       12.      1
    +
       13.      1    var columns = [];
    +
       14.    128    for (var i = 0; i < 128; i++) {
    +
       15.    128        columns.push('id' + i);
    +
       16.    128    }
    +
       17.      1
    +
       18.      1    it('should create the table', function(done) {
    +
       19.      1        db.run("CREATE TABLE foo (" + columns + ")", done);
    +
       20.      1    });
    +
       21.      1
    +
       22.      1    it('should insert in parallel', function(done) {
    +
       23.   1000        for (var i = 0; i < 1000; i++) {
    +
       24. 128000            for (var values = [], j = 0; j < columns.length; j++) {
    +
       25. 128000                values.push(i * j);
    +
       26. 128000            }
    +
       27.   1000            db.run("INSERT INTO foo VALUES (" + values + ")");
    +
       28.   1000        }
    +
       29.      1
    +
       30.      1        db.wait(done);
    +
       31.      1    });
    +
       32.      1
    +
       33.      1    it('should close the database', function(done) {
    +
       34.      1        db.close(done);
    +
       35.      1    });
    +
       36.      1
    +
       37.      1    it('should verify that the database exists', function() {
    +
       38.      1        assert.fileExists('test/tmp/test_parallel_inserts.db');
    +
       39.      1    });
    +
       40.      1
    +
       41.      1    after(function() {
    +
       42.      1        helper.deleteFile('test/tmp/test_parallel_inserts.db');
    +
       43.      1    });
    +
       44.      1});
    +
       45.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/prepare.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/prepare.test.js.html new file mode 100644 index 000000000..86fdbd6bd --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/prepare.test.js.html @@ -0,0 +1,586 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/prepare.test.js
    +
    +
    +
    +
    + 94.85 %
    + 406 / 428 +
    +
    + 22 / 428 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..');
    +
        2.      1var assert = require('assert');
    +
        3.      1
    +
        4.      1describe('prepare', function() {
    +
        5.      1    describe('invalid SQL', function() {
    +
        6.      1        var db;
    +
        7.      1        before(function(done) { db = new sqlite3.Database(':memory:', done); });
    +
        8.      1
    +
        9.      1        var stmt;
    +
       10.      1        it('should fail preparing a statement with invalid SQL', function(done) {
    +
       11.      1            stmt = db.prepare('CRATE TALE foo text bar)', function(err, statement) {
    +
       12.      1                if (err && err.errno == sqlite3.ERROR &&
    +
       13.      1                    err.message === 'SQLITE_ERROR: near "CRATE": syntax error') {
    +
       14.      1                    done();
    +
       15.     -0                }
    +
       16.     -0                else throw err;
    +
       17.      1            });
    +
       18.      1        });
    +
       19.      1
    +
       20.      1        after(function(done) { db.close(done); });
    +
       21.      1    });
    +
       22.      1
    +
       23.      1    describe('simple prepared statement', function() {
    +
       24.      1        var db;
    +
       25.      1        before(function(done) { db = new sqlite3.Database(':memory:', done); });
    +
       26.      1
    +
       27.      1        it('should prepare, run and finalize the statement', function(done) {
    +
       28.      1            db.prepare("CREATE TABLE foo (text bar)")
    +
       29.      1                .run()
    +
       30.      1                .finalize(done);
    +
       31.      1        });
    +
       32.      1
    +
       33.      1        after(function(done) { db.close(done); });
    +
       34.      1    });
    +
       35.      1
    +
       36.      1    describe('inserting and retrieving rows', function() {
    +
       37.      1        var db;
    +
       38.      1        before(function(done) { db = new sqlite3.Database(':memory:', done); });
    +
       39.      1
    +
       40.      1        var inserted = 0;
    +
       41.      1        var retrieved = 0;
    +
       42.      1
    +
       43.      1        // We insert and retrieve that many rows.
    +
       44.      1        var count = 1000;
    +
       45.      1
    +
       46.      1        it('should create the table', function(done) {
    +
       47.      1            db.prepare("CREATE TABLE foo (txt text, num int, flt float, blb blob)").run().finalize(done);
    +
       48.      1        });
    +
       49.      1
    +
       50.      1        it('should insert ' + count + ' rows', function(done) {
    +
       51.   1000            for (var i = 0; i < count; i++) {
    +
       52.   1000                db.prepare("INSERT INTO foo VALUES(?, ?, ?, ?)").run(
    +
       53.   1000                    'String ' + i,
    +
       54.   1000                    i,
    +
       55.   1000                    i * Math.PI,
    +
       56.   1000                    // null (SQLite sets this implicitly)
    +
       57.   1000                    function(err) {
    +
       58.     -0                        if (err) throw err;
    +
       59.   1000                        inserted++;
    +
       60.   1000                    }
    +
       61.   1000                ).finalize(function(err) {
    +
       62.     -0                    if (err) throw err;
    +
       63.   1000                    if (inserted == count) done();
    +
       64.   1000                });
    +
       65.   1000            }
    +
       66.      1        });
    +
       67.      1
    +
       68.      1        it('should prepare a statement and run it ' + (count + 5) + ' times', function(done) {
    +
       69.      1            var stmt = db.prepare("SELECT txt, num, flt, blb FROM foo ORDER BY num", function(err) {
    +
       70.     -0                if (err) throw err;
    +
       71.      1                assert.equal(stmt.sql, 'SELECT txt, num, flt, blb FROM foo ORDER BY num');
    +
       72.      1            });
    +
       73.      1
    +
       74.   1005            for (var i = 0; i < count + 5; i++) (function(i) {
    +
       75.   1005                stmt.get(function(err, row) {
    +
       76.     -0                    if (err) throw err;
    +
       77.   1005
    +
       78.   1005                    if (retrieved >= 1000) {
    +
       79.   1005                        assert.equal(row, undefined);
    +
       80.   1005                    } else {
    +
       81.   1005                        assert.equal(row.txt, 'String ' + i);
    +
       82.   1005                        assert.equal(row.num, i);
    +
       83.   1005                        assert.equal(row.flt, i * Math.PI);
    +
       84.   1005                        assert.equal(row.blb, null);
    +
       85.   1005                    }
    +
       86.   1005
    +
       87.   1005                    retrieved++;
    +
       88.   1005                });
    +
       89.   1005            })(i);
    +
       90.      1
    +
       91.      1            stmt.finalize(done);
    +
       92.      1        });
    +
       93.      1
    +
       94.      1        it('should have retrieved ' + (count + 5) + ' rows', function() {
    +
       95.      1            assert.equal(count + 5, retrieved, "Didn't retrieve all rows");
    +
       96.      1        });
    +
       97.      1
    +
       98.      1
    +
       99.      1        after(function(done) { db.close(done); });
    +
      100.      1    });
    +
      101.      1
    +
      102.      1    describe('inserting with accidental undefined', function() {
    +
      103.      1        var db;
    +
      104.      1        before(function(done) { db = new sqlite3.Database(':memory:', done); });
    +
      105.      1
    +
      106.      1        var inserted = 0;
    +
      107.      1        var retrieved = 0;
    +
      108.      1
    +
      109.      1        it('should create the table', function(done) {
    +
      110.      1            db.prepare("CREATE TABLE foo (num int)").run().finalize(done);
    +
      111.      1        });
    +
      112.      1
    +
      113.      1        it('should insert two rows', function(done) {
    +
      114.      1            db.prepare('INSERT INTO foo VALUES(4)').run(function(err) {
    +
      115.     -0                if (err) throw err;
    +
      116.      1                inserted++;
    +
      117.      1            }).run(undefined, function (err) {
    +
      118.      1                // The second time we pass undefined as a parameter. This is
    +
      119.      1                // a mistake, but it should either throw an error or be ignored,
    +
      120.      1                // not silently fail to run the statement.
    +
      121.     -0                if (err) throw err;
    +
      122.      1                inserted++;
    +
      123.      1            }).finalize(function(err) {
    +
      124.     -0                if (err) throw err;
    +
      125.      1                if (inserted == 2) done();
    +
      126.      1            });
    +
      127.      1        });
    +
      128.      1
    +
      129.      1        it('should retrieve the data', function(done) {
    +
      130.      1            var stmt = db.prepare("SELECT num FROM foo", function(err) {
    +
      131.     -0                if (err) throw err;
    +
      132.      1            });
    +
      133.      1
    +
      134.      2            for (var i = 0; i < 2; i++) (function(i) {
    +
      135.      2                stmt.get(function(err, row) {
    +
      136.     -0                    if (err) throw err;
    +
      137.      2                    assert(row);
    +
      138.      2                    assert.equal(row.num, 4);
    +
      139.      2                    retrieved++;
    +
      140.      2                });
    +
      141.      2            })(i);
    +
      142.      1
    +
      143.      1            stmt.finalize(done);
    +
      144.      1        });
    +
      145.      1
    +
      146.      1        it('should have retrieved two rows', function() {
    +
      147.      1            assert.equal(2, retrieved, "Didn't retrieve all rows");
    +
      148.      1        });
    +
      149.      1
    +
      150.      1        after(function(done) { db.close(done); });
    +
      151.      1    });
    +
      152.      1
    +
      153.      1    describe('retrieving reset() function', function() {
    +
      154.      1        var db;
    +
      155.      1        before(function(done) { db = new sqlite3.Database('test/support/prepare.db', sqlite3.OPEN_READONLY, done); });
    +
      156.      1
    +
      157.      1        var retrieved = 0;
    +
      158.      1
    +
      159.      1        it('should retrieve the same row over and over again', function(done) {
    +
      160.      1            var stmt = db.prepare("SELECT txt, num, flt, blb FROM foo ORDER BY num");
    +
      161.     10            for (var i = 0; i < 10; i++) {
    +
      162.     10                stmt.reset();
    +
      163.     10                stmt.get(function(err, row) {
    +
      164.     -0                    if (err) throw err;
    +
      165.     10                    assert.equal(row.txt, 'String 0');
    +
      166.     10                    assert.equal(row.num, 0);
    +
      167.     10                    assert.equal(row.flt, 0.0);
    +
      168.     10                    assert.equal(row.blb, null);
    +
      169.     10                    retrieved++;
    +
      170.     10                });
    +
      171.     10            }
    +
      172.      1            stmt.finalize(done);
    +
      173.      1        });
    +
      174.      1
    +
      175.      1        it('should have retrieved 10 rows', function() {
    +
      176.      1            assert.equal(10, retrieved, "Didn't retrieve all rows");
    +
      177.      1        });
    +
      178.      1
    +
      179.      1        after(function(done) { db.close(done); });
    +
      180.      1    });
    +
      181.      1
    +
      182.      1    describe('multiple get() parameter binding', function() {
    +
      183.      1        var db;
    +
      184.      1        before(function(done) { db = new sqlite3.Database('test/support/prepare.db', sqlite3.OPEN_READONLY, done); });
    +
      185.      1
    +
      186.      1        var retrieved = 0;
    +
      187.      1
    +
      188.      1        it('should retrieve particular rows', function(done) {
    +
      189.      1            var stmt = db.prepare("SELECT txt, num, flt, blb FROM foo WHERE num = ?");
    +
      190.      1
    +
      191.     10            for (var i = 0; i < 10; i++) (function(i) {
    +
      192.     10                stmt.get(i * 10 + 1, function(err, row) {
    +
      193.     -0                    if (err) throw err;
    +
      194.     10                    var val = i * 10 + 1;
    +
      195.     10                    assert.equal(row.txt, 'String ' + val);
    +
      196.     10                    assert.equal(row.num, val);
    +
      197.     10                    assert.equal(row.flt, val * Math.PI);
    +
      198.     10                    assert.equal(row.blb, null);
    +
      199.     10                    retrieved++;
    +
      200.     10                });
    +
      201.     10            })(i);
    +
      202.      1
    +
      203.      1            stmt.finalize(done);
    +
      204.      1        });
    +
      205.      1
    +
      206.      1        it('should have retrieved 10 rows', function() {
    +
      207.      1            assert.equal(10, retrieved, "Didn't retrieve all rows");
    +
      208.      1        });
    +
      209.      1
    +
      210.      1        after(function(done) { db.close(done); });
    +
      211.      1    });
    +
      212.      1
    +
      213.      1    describe('prepare() parameter binding', function() {
    +
      214.      1        var db;
    +
      215.      1        before(function(done) { db = new sqlite3.Database('test/support/prepare.db', sqlite3.OPEN_READONLY, done); });
    +
      216.      1
    +
      217.      1        var retrieved = 0;
    +
      218.      1
    +
      219.      1        it('should retrieve particular rows', function(done) {
    +
      220.      1            db.prepare("SELECT txt, num, flt, blb FROM foo WHERE num = ? AND txt = ?", 10, 'String 10')
    +
      221.      1                .get(function(err, row) {
    +
      222.     -0                    if (err) throw err;
    +
      223.      1                    assert.equal(row.txt, 'String 10');
    +
      224.      1                    assert.equal(row.num, 10);
    +
      225.      1                    assert.equal(row.flt, 10 * Math.PI);
    +
      226.      1                    assert.equal(row.blb, null);
    +
      227.      1                    retrieved++;
    +
      228.      1                })
    +
      229.      1                .finalize(done);
    +
      230.      1        });
    +
      231.      1
    +
      232.      1        it('should have retrieved 1 row', function() {
    +
      233.      1            assert.equal(1, retrieved, "Didn't retrieve all rows");
    +
      234.      1        });
    +
      235.      1
    +
      236.      1        after(function(done) { db.close(done); });
    +
      237.      1    });
    +
      238.      1
    +
      239.      1    describe('all()', function() {
    +
      240.      1        var db;
    +
      241.      1        before(function(done) { db = new sqlite3.Database('test/support/prepare.db', sqlite3.OPEN_READONLY, done); });
    +
      242.      1
    +
      243.      1        var retrieved = 0;
    +
      244.      1        var count = 1000;
    +
      245.      1
    +
      246.      1        it('should retrieve particular rows', function(done) {
    +
      247.      1            db.prepare("SELECT txt, num, flt, blb FROM foo WHERE num < ? ORDER BY num", count)
    +
      248.      1                .all(function(err, rows) {
    +
      249.     -0                    if (err) throw err;
    +
      250.   1000                    for (var i = 0; i < rows.length; i++) {
    +
      251.   1000                        assert.equal(rows[i].txt, 'String ' + i);
    +
      252.   1000                        assert.equal(rows[i].num, i);
    +
      253.   1000                        assert.equal(rows[i].flt, i * Math.PI);
    +
      254.   1000                        assert.equal(rows[i].blb, null);
    +
      255.   1000                        retrieved++;
    +
      256.   1000                    }
    +
      257.      1                })
    +
      258.      1                .finalize(done);
    +
      259.      1        });
    +
      260.      1
    +
      261.      1        it('should have retrieved all rows', function() {
    +
      262.      1            assert.equal(count, retrieved, "Didn't retrieve all rows");
    +
      263.      1        });
    +
      264.      1
    +
      265.      1        after(function(done) { db.close(done); });
    +
      266.      1    });
    +
      267.      1
    +
      268.      1    describe('all()', function() {
    +
      269.      1        var db;
    +
      270.      1        before(function(done) { db = new sqlite3.Database('test/support/prepare.db', sqlite3.OPEN_READONLY, done); });
    +
      271.      1
    +
      272.      1        it('should retrieve particular rows', function(done) {
    +
      273.      1           db.prepare("SELECT txt, num, flt, blb FROM foo WHERE num > 5000")
    +
      274.      1                .all(function(err, rows) {
    +
      275.     -0                    if (err) throw err;
    +
      276.      1                    assert.ok(rows.length === 0);
    +
      277.      1                })
    +
      278.      1                .finalize(done);
    +
      279.      1        });
    +
      280.      1
    +
      281.      1        after(function(done) { db.close(done); });
    +
      282.      1    });
    +
      283.      1
    +
      284.      1    describe('high concurrency', function() {
    +
      285.      1        var db;
    +
      286.      1        before(function(done) { db = new sqlite3.Database(':memory:', done); });
    +
      287.      1
    +
      288.    924        function randomString() {
    +
      289.    924            var str = '';
    +
      290. 141949            for (var i = Math.random() * 300; i > 0; i--) {
    +
      291. 141949                str += String.fromCharCode(Math.floor(Math.random() * 256));
    +
      292. 141949            }
    +
      293.    924            return str;
    +
      294.    924        }
    +
      295.      1
    +
      296.      1        // Generate random data.
    +
      297.      1        var data = [];
    +
      298.      1        var length = Math.floor(Math.random() * 1000) + 200;
    +
      299.    924        for (var i = 0; i < length; i++) {
    +
      300.    924            data.push([ randomString(), i, i * Math.random(), null ]);
    +
      301.    924        }
    +
      302.      1
    +
      303.      1        var inserted = 0;
    +
      304.      1        var retrieved = 0;
    +
      305.      1
    +
      306.      1        it('should create the table', function(done) {
    +
      307.      1            db.prepare("CREATE TABLE foo (txt text, num int, flt float, blb blob)").run().finalize(done);
    +
      308.      1        });
    +
      309.      1
    +
      310.      1        it('should insert all values', function(done) {
    +
      311.    924            for (var i = 0; i < data.length; i++) {
    +
      312.    924                var stmt = db.prepare("INSERT INTO foo VALUES(?, ?, ?, ?)");
    +
      313.    924                stmt.run(data[i][0], data[i][1], data[i][2], data[i][3], function(err) {
    +
      314.     -0                    if (err) throw err;
    +
      315.    924                    inserted++;
    +
      316.    924                }).finalize(function(err) {
    +
      317.     -0                    if (err) throw err;
    +
      318.    924                    if (inserted == data.length) done();
    +
      319.    924                });
    +
      320.    924            }
    +
      321.      1        });
    +
      322.      1
    +
      323.      1        it('should retrieve all values', function(done) {
    +
      324.      1            db.prepare("SELECT txt, num, flt, blb FROM foo")
    +
      325.      1                .all(function(err, rows) {
    +
      326.     -0                    if (err) throw err;
    +
      327.      1
    +
      328.    924                    for (var i = 0; i < rows.length; i++) {
    +
      329.    924                        assert.ok(data[rows[i].num] !== true);
    +
      330.    924
    +
      331.    924                        assert.equal(rows[i].txt, data[rows[i].num][0]);
    +
      332.    924                        assert.equal(rows[i].num, data[rows[i].num][1]);
    +
      333.    924                        assert.equal(rows[i].flt, data[rows[i].num][2]);
    +
      334.    924                        assert.equal(rows[i].blb, data[rows[i].num][3]);
    +
      335.    924
    +
      336.    924                        // Mark the data row as already retrieved.
    +
      337.    924                        data[rows[i].num] = true;
    +
      338.    924                        retrieved++;
    +
      339.    924
    +
      340.    924                    }
    +
      341.      1
    +
      342.      1                    assert.equal(retrieved, data.length);
    +
      343.      1                    assert.equal(retrieved, inserted);
    +
      344.      1                })
    +
      345.      1                .finalize(done);
    +
      346.      1        });
    +
      347.      1
    +
      348.      1        after(function(done) { db.close(done); });
    +
      349.      1    });
    +
      350.      1
    +
      351.      1
    +
      352.      1    describe('test Database#get()', function() {
    +
      353.      1        var db;
    +
      354.      1        before(function(done) { db = new sqlite3.Database('test/support/prepare.db', sqlite3.OPEN_READONLY, done); });
    +
      355.      1
    +
      356.      1        var retrieved = 0;
    +
      357.      1
    +
      358.      1        it('should get a row', function(done) {
    +
      359.      1            db.get("SELECT txt, num, flt, blb FROM foo WHERE num = ? AND txt = ?", 10, 'String 10', function(err, row) {
    +
      360.     -0                if (err) throw err;
    +
      361.      1                assert.equal(row.txt, 'String 10');
    +
      362.      1                assert.equal(row.num, 10);
    +
      363.      1                assert.equal(row.flt, 10 * Math.PI);
    +
      364.      1                assert.equal(row.blb, null);
    +
      365.      1                retrieved++;
    +
      366.      1                done();
    +
      367.      1            });
    +
      368.      1        });
    +
      369.      1
    +
      370.      1        it('should have retrieved all rows', function() {
    +
      371.      1            assert.equal(1, retrieved, "Didn't retrieve all rows");
    +
      372.      1        });
    +
      373.      1
    +
      374.      1        after(function(done) { db.close(done); });
    +
      375.      1    });
    +
      376.      1
    +
      377.      1    describe('Database#run() and Database#all()', function() {
    +
      378.      1        var db;
    +
      379.      1        before(function(done) { db = new sqlite3.Database(':memory:', done); });
    +
      380.      1
    +
      381.      1        var inserted = 0;
    +
      382.      1        var retrieved = 0;
    +
      383.      1
    +
      384.      1        // We insert and retrieve that many rows.
    +
      385.      1        var count = 1000;
    +
      386.      1
    +
      387.      1        it('should create the table', function(done) {
    +
      388.      1            db.run("CREATE TABLE foo (txt text, num int, flt float, blb blob)", done);
    +
      389.      1        });
    +
      390.      1
    +
      391.      1        it('should insert ' + count + ' rows', function(done) {
    +
      392.   1000            for (var i = 0; i < count; i++) {
    +
      393.   1000                db.run("INSERT INTO foo VALUES(?, ?, ?, ?)",
    +
      394.   1000                    'String ' + i,
    +
      395.   1000                    i,
    +
      396.   1000                    i * Math.PI,
    +
      397.   1000                    // null (SQLite sets this implicitly)
    +
      398.   1000                    function(err) {
    +
      399.     -0                        if (err) throw err;
    +
      400.   1000                        inserted++;
    +
      401.   1000                        if (inserted == count) done();
    +
      402.   1000                    }
    +
      403.   1000                );
    +
      404.   1000            }
    +
      405.      1        });
    +
      406.      1
    +
      407.      1        it('should retrieve all rows', function(done) {
    +
      408.      1            db.all("SELECT txt, num, flt, blb FROM foo ORDER BY num", function(err, rows) {
    +
      409.     -0                if (err) throw err;
    +
      410.   1000                for (var i = 0; i < rows.length; i++) {
    +
      411.   1000                    assert.equal(rows[i].txt, 'String ' + i);
    +
      412.   1000                    assert.equal(rows[i].num, i);
    +
      413.   1000                    assert.equal(rows[i].flt, i * Math.PI);
    +
      414.   1000                    assert.equal(rows[i].blb, null);
    +
      415.   1000                    retrieved++;
    +
      416.   1000                }
    +
      417.      1
    +
      418.      1                assert.equal(retrieved, count);
    +
      419.      1                assert.equal(retrieved, inserted);
    +
      420.      1
    +
      421.      1                done();
    +
      422.      1            });
    +
      423.      1        });
    +
      424.      1
    +
      425.      1        after(function(done) { db.close(done); });
    +
      426.      1    });
    +
      427.      1});
    +
      428.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/profile.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/profile.test.js.html new file mode 100644 index 000000000..b13a42c6e --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/profile.test.js.html @@ -0,0 +1,216 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/profile.test.js
    +
    +
    +
    +
    + 89.65 %
    + 52 / 58 +
    +
    + 6 / 58 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..');
    +
        2.      1var assert = require('assert');
    +
        3.      1
    +
        4.      1describe('profiling', function() {
    +
        5.      1    var create = false;
    +
        6.      1    var select = false;
    +
        7.      1
    +
        8.      1    var db;
    +
        9.      1    before(function(done) {
    +
       10.      1        db = new sqlite3.Database(':memory:', done);
    +
       11.      1
    +
       12.      2        db.on('profile', function(sql, nsecs) {
    +
       13.      2            assert.ok(typeof nsecs === "number");
    +
       14.      1            if (sql.match(/^SELECT/)) {
    +
       15.      1                assert.ok(!select);
    +
       16.      1                assert.equal(sql, "SELECT * FROM foo");
    +
       17.      1                console.log('profile select');
    +
       18.      1                select = true;
    +
       19.      1            }
    +
       20.      1            else if (sql.match(/^CREATE/)) {
    +
       21.      1                assert.ok(!create);
    +
       22.      1                assert.equal(sql, "CREATE TABLE foo (id int)");
    +
       23.      1                create = true;
    +
       24.     -0            }
    +
       25.     -0            else {
    +
       26.     -0                assert.ok(false);
    +
       27.     -0            }
    +
       28.      2        });
    +
       29.      1    });
    +
       30.      1
    +
       31.      1    it('should profile a create table', function(done) {
    +
       32.      1        assert.ok(!create);
    +
       33.      1        db.run("CREATE TABLE foo (id int)", function(err) {
    +
       34.     -0            if (err) throw err;
    +
       35.      1            setImmediate(function() {
    +
       36.      1                assert.ok(create);
    +
       37.      1                done();
    +
       38.      1            });
    +
       39.      1        });
    +
       40.      1    });
    +
       41.      1
    +
       42.      1
    +
       43.      1    it('should profile a select', function(done) {
    +
       44.      1        assert.ok(!select);
    +
       45.      1        db.run("SELECT * FROM foo", function(err) {
    +
       46.     -0            if (err) throw err;
    +
       47.      1            setImmediate(function() {
    +
       48.      1                assert.ok(select);
    +
       49.      1                done();
    +
       50.      1            }, 0);
    +
       51.      1        });
    +
       52.      1    });
    +
       53.      1
    +
       54.      1    after(function(done) {
    +
       55.      1        db.close(done);
    +
       56.      1    });
    +
       57.      1});
    +
       58.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/rerun.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/rerun.test.js.html new file mode 100644 index 000000000..a2ea92c4a --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/rerun.test.js.html @@ -0,0 +1,209 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/rerun.test.js
    +
    +
    +
    +
    + 94.11 %
    + 48 / 51 +
    +
    + 3 / 51 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..');
    +
        2.      1var assert = require('assert');
    +
        3.      1
    +
        4.      1describe('rerunning statements', function() {
    +
        5.      1    var db;
    +
        6.      1    before(function(done) { db = new sqlite3.Database(':memory:', done); });
    +
        7.      1
    +
        8.      1    var count = 10;
    +
        9.      1    var inserted = 0;
    +
       10.      1    var retrieved = 0;
    +
       11.      1
    +
       12.      1    it('should create the table', function(done) {
    +
       13.      1        db.run("CREATE TABLE foo (id int)", done);
    +
       14.      1    });
    +
       15.      1
    +
       16.      1    it('should insert repeatedly, reusing the same statement', function(done) {
    +
       17.      1        var stmt = db.prepare("INSERT INTO foo VALUES(?)");
    +
       18.      5        for (var i = 5; i < count; i++) {
    +
       19.      5            stmt.run(i, function(err) {
    +
       20.     -0                if (err) throw err;
    +
       21.      5                inserted++;
    +
       22.      5            });
    +
       23.      5        }
    +
       24.      1        stmt.finalize(done);
    +
       25.      1    });
    +
       26.      1
    +
       27.      1    it('should retrieve repeatedly, resuing the same statement', function(done) {
    +
       28.      1        var collected = [];
    +
       29.      1        var stmt = db.prepare("SELECT id FROM foo WHERE id = ?");
    +
       30.     10        for (var i = 0; i < count; i++) {
    +
       31.     10            stmt.get(i, function(err, row) {
    +
       32.     -0                if (err) throw err;
    +
       33.     10                if (row) collected.push(row);
    +
       34.     10            });
    +
       35.     10        }
    +
       36.      1        stmt.finalize(function(err) {
    +
       37.     -0            if (err) throw err;
    +
       38.      1            retrieved += collected.length;
    +
       39.      1            assert.deepEqual(collected, [ { id: 5 }, { id: 6 }, { id: 7 }, { id: 8 }, { id: 9 } ]);
    +
       40.      1            done();
    +
       41.      1        });
    +
       42.      1    });
    +
       43.      1
    +
       44.      1    it('should have inserted and retrieved the right amount', function() {
    +
       45.      1        assert.equal(inserted, 5);
    +
       46.      1        assert.equal(retrieved, 5);
    +
       47.      1    });
    +
       48.      1
    +
       49.      1    after(function(done) { db.close(done); });
    +
       50.      1});
    +
       51.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/scheduling.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/scheduling.test.js.html new file mode 100644 index 000000000..e4e6274f6 --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/scheduling.test.js.html @@ -0,0 +1,203 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/scheduling.test.js
    +
    +
    +
    +
    + 91.11 %
    + 41 / 45 +
    +
    + 4 / 45 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..');
    +
        2.      1var assert = require('assert');
    +
        3.      1
    +
        4.      1describe('scheduling', function() {
    +
        5.      1    it('scheduling after the database was closed', function(done) {
    +
        6.      1        var db = new sqlite3.Database(':memory:');
    +
        7.      1        db.on('error', function(err) {
    +
        8.      1            assert.ok(err.message && err.message.indexOf("SQLITE_MISUSE: Database handle is closed") > -1);
    +
        9.      1            done();
    +
       10.      1        });
    +
       11.      1
    +
       12.      1        db.close();
    +
       13.      1        db.run("CREATE TABLE foo (id int)");
    +
       14.      1    });
    +
       15.      1
    +
       16.      1
    +
       17.      1    it('scheduling a query with callback after the database was closed', function(done) {
    +
       18.      1        var db = new sqlite3.Database(':memory:');
    +
       19.     -0        db.on('error', function(err) {
    +
       20.     -0            assert.ok(false, 'Event was accidentally triggered');
    +
       21.     -0        });
    +
       22.      1
    +
       23.      1        db.close();
    +
       24.      1        db.run("CREATE TABLE foo (id int)", function(err) {
    +
       25.      1            assert.ok(err.message && err.message.indexOf("SQLITE_MISUSE: Database handle is closed") > -1);
    +
       26.      1            done();
    +
       27.      1        });
    +
       28.      1    });
    +
       29.      1
    +
       30.      1    it('running a query after the database was closed', function(done) {
    +
       31.      1        var db = new sqlite3.Database(':memory:');
    +
       32.      1
    +
       33.      1        var stmt = db.prepare("SELECT * FROM sqlite_master", function(err) {
    +
       34.     -0            if (err) throw err;
    +
       35.      1            db.close(function(err) {
    +
       36.      1                assert.ok(err);
    +
       37.      1                assert.ok(err.message && err.message.indexOf("SQLITE_BUSY: unable to close due to") > -1);
    +
       38.      1
    +
       39.      1                // Running a statement now should not fail.
    +
       40.      1                stmt.run(done);
    +
       41.      1            });
    +
       42.      1        });
    +
       43.      1    });
    +
       44.      1});
    +
       45.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/serialization.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/serialization.test.js.html new file mode 100644 index 000000000..f001f892d --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/serialization.test.js.html @@ -0,0 +1,263 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/serialization.test.js
    +
    +
    +
    +
    + 95.23 %
    + 100 / 105 +
    +
    + 5 / 105 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..');
    +
        2.      1var assert = require('assert');
    +
        3.      1
    +
        4.      1
    +
        5.      1describe('serialize() and parallelize()', function() {
    +
        6.      1    var db;
    +
        7.      1    before(function(done) { db = new sqlite3.Database(':memory:', done); });
    +
        8.      1
    +
        9.      1    var inserted1 = 0;
    +
       10.      1    var inserted2 = 0;
    +
       11.      1    var retrieved = 0;
    +
       12.      1
    +
       13.      1    var count = 1000;
    +
       14.      1
    +
       15.      1    it('should toggle', function(done) {
    +
       16.      1        db.serialize();
    +
       17.      1        db.run("CREATE TABLE foo (txt text, num int, flt float, blb blob)");
    +
       18.      1        db.parallelize(done);
    +
       19.      1    });
    +
       20.      1
    +
       21.      1    it('should insert rows', function() {
    +
       22.      1        var stmt1 = db.prepare("INSERT INTO foo VALUES(?, ?, ?, ?)");
    +
       23.      1        var stmt2 = db.prepare("INSERT INTO foo VALUES(?, ?, ?, ?)");
    +
       24.    500        for (var i = 0; i < count; i++) {
    +
       25.    500            // Interleaved inserts with two statements.
    +
       26.    500            stmt1.run('String ' + i, i, i * Math.PI, function(err) {
    +
       27.     -0                if (err) throw err;
    +
       28.    500                inserted1++;
    +
       29.    500            });
    +
       30.    500            i++;
    +
       31.    500            stmt2.run('String ' + i, i, i * Math.PI, function(err) {
    +
       32.     -0                if (err) throw err;
    +
       33.    500                inserted2++;
    +
       34.    500            });
    +
       35.    500        }
    +
       36.      1        stmt1.finalize();
    +
       37.      1        stmt2.finalize();
    +
       38.      1    });
    +
       39.      1
    +
       40.      1    it('should have inserted all the rows after synchronizing with serialize()', function(done) {
    +
       41.      1        db.serialize();
    +
       42.      1        db.all("SELECT txt, num, flt, blb FROM foo ORDER BY num", function(err, rows) {
    +
       43.     -0            if (err) throw err;
    +
       44.   1000            for (var i = 0; i < rows.length; i++) {
    +
       45.   1000                assert.equal(rows[i].txt, 'String ' + i);
    +
       46.   1000                assert.equal(rows[i].num, i);
    +
       47.   1000                assert.equal(rows[i].flt, i * Math.PI);
    +
       48.   1000                assert.equal(rows[i].blb, null);
    +
       49.   1000                retrieved++;
    +
       50.   1000            }
    +
       51.      1
    +
       52.      1            assert.equal(count, inserted1 + inserted2, "Didn't insert all rows");
    +
       53.      1            assert.equal(count, retrieved, "Didn't retrieve all rows");
    +
       54.      1            done();
    +
       55.      1        });
    +
       56.      1    });
    +
       57.      1
    +
       58.      1    after(function(done) { db.close(done); });
    +
       59.      1});
    +
       60.      1
    +
       61.      1describe('serialize(fn)', function() {
    +
       62.      1    var db;
    +
       63.      1    before(function(done) { db = new sqlite3.Database(':memory:', done); });
    +
       64.      1
    +
       65.      1    var inserted = 0;
    +
       66.      1    var retrieved = 0;
    +
       67.      1
    +
       68.      1    var count = 1000;
    +
       69.      1
    +
       70.      1    it('should call the callback', function(done) {
    +
       71.      1        db.serialize(function() {
    +
       72.      1            db.run("CREATE TABLE foo (txt text, num int, flt float, blb blob)");
    +
       73.      1
    +
       74.      1            var stmt = db.prepare("INSERT INTO foo VALUES(?, ?, ?, ?)");
    +
       75.   1000            for (var i = 0; i < count; i++) {
    +
       76.   1000                stmt.run('String ' + i, i, i * Math.PI, function(err) {
    +
       77.     -0                    if (err) throw err;
    +
       78.   1000                    inserted++;
    +
       79.   1000                });
    +
       80.   1000            }
    +
       81.      1            stmt.finalize();
    +
       82.      1
    +
       83.      1            db.all("SELECT txt, num, flt, blb FROM foo ORDER BY num", function(err, rows) {
    +
       84.     -0                if (err) throw err;
    +
       85.   1000                for (var i = 0; i < rows.length; i++) {
    +
       86.   1000                    assert.equal(rows[i].txt, 'String ' + i);
    +
       87.   1000                    assert.equal(rows[i].num, i);
    +
       88.   1000                    assert.equal(rows[i].flt, i * Math.PI);
    +
       89.   1000                    assert.equal(rows[i].blb, null);
    +
       90.   1000                    retrieved++;
    +
       91.   1000                }
    +
       92.      1                done();
    +
       93.      1            });
    +
       94.      1        });
    +
       95.      1    });
    +
       96.      1
    +
       97.      1
    +
       98.      1    it('should have inserted and retrieved all rows', function() {
    +
       99.      1        assert.equal(count, inserted, "Didn't insert all rows");
    +
      100.      1        assert.equal(count, retrieved, "Didn't retrieve all rows");
    +
      101.      1    });
    +
      102.      1
    +
      103.      1    after(function(done) { db.close(done); });
    +
      104.      1});
    +
      105.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/support/createdb.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/support/createdb.js.html new file mode 100644 index 000000000..7ffaf4cfa --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/support/createdb.js.html @@ -0,0 +1,206 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/support/createdb.js
    +
    +
    +
    +
    + 89.58 %
    + 43 / 48 +
    +
    + 5 / 48 +
    +
    + + +
    +
        1.      1#!/usr/bin/env node
    +
        2.      1
    +
        3.      1function createdb(callback) {
    +
        4.     -0    var existsSync = require('fs').existsSync || require('path').existsSync;
    +
        5.      1    var path = require('path');
    +
        6.      1
    +
        7.      1    var sqlite3 = require('../../lib/sqlite3');
    +
        8.      1
    +
        9.      1    var count = 1000000;
    +
       10.      1    var db_path = path.join(__dirname,'big.db');
    +
       11.      1
    +
       12.1000000    function randomString() {
    +
       13.1000000        var str = '';
    +
       14.1000000        var chars = 'abcdefghijklmnopqrstuvwxzyABCDEFGHIJKLMNOPQRSTUVWXZY0123456789  ';
    +
       15.50468194        for (var i = Math.random() * 100; i > 0; i--) {
    +
       16.50468194            str += chars[Math.floor(Math.random() * chars.length)];
    +
       17.50468194        }
    +
       18.1000000        return str;
    +
       19.1000000    };
    +
       20.      1
    +
       21.      1
    +
       22.     -0    if (existsSync(db_path)) {
    +
       23.     -0        console.log('okay: database already created (' + db_path + ')');
    +
       24.     -0        if (callback) callback();
    +
       25.     -0    } else {
    +
       26.      1        console.log("Creating test database... This may take several minutes.");
    +
       27.      1        var db = new sqlite3.Database(db_path);
    +
       28.      1        db.serialize(function() {
    +
       29.      1            db.run("CREATE TABLE foo (id INT, txt TEXT)");
    +
       30.      1            db.run("BEGIN TRANSACTION");
    +
       31.      1            var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)");
    +
       32.1000000            for (var i = 0; i < count; i++) {
    +
       33.1000000                stmt.run(i, randomString());
    +
       34.1000000            }
    +
       35.      1            stmt.finalize();
    +
       36.      1            db.run("COMMIT TRANSACTION", [], function () {
    +
       37.      1                db.close(callback);
    +
       38.      1            });
    +
       39.      1        });
    +
       40.      1    }
    +
       41.      1};
    +
       42.      1
    +
       43.      1if (require.main === module) {
    +
       44.      1    createdb();
    +
       45.      1}
    +
       46.      1
    +
       47.      1module.exports = createdb;
    +
       48.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/support/helper.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/support/helper.js.html new file mode 100644 index 000000000..6cb96266c --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/support/helper.js.html @@ -0,0 +1,195 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/support/helper.js
    +
    +
    +
    +
    + 72.97 %
    + 27 / 37 +
    +
    + 10 / 37 +
    +
    + + +
    +
        1.      1var assert = require('assert');
    +
        2.      1var fs = require('fs');
    +
        3.     -0var pathExists = require('fs').existsSync || require('path').existsSync;
    +
        4.      1
    +
        5.     40exports.deleteFile = function(name) {
    +
        6.     40    try {
    +
        7.     40        fs.unlinkSync(name);
    +
        8.     23    } catch(err) {
    +
        9.     -0        if (err.errno !== process.ENOENT && err.code !== 'ENOENT' && err.syscall !== 'unlink') {
    +
       10.     -0            throw err;
    +
       11.     -0        }
    +
       12.     23    }
    +
       13.     40};
    +
       14.      1
    +
       15.      5exports.ensureExists = function(name,cb) {
    +
       16.      1    if (!pathExists(name)) {
    +
       17.      1        fs.mkdirSync(name);
    +
       18.      1    };
    +
       19.      5}
    +
       20.      1
    +
       21.      2assert.fileDoesNotExist = function(name) {
    +
       22.      2    try {
    +
       23.      2        fs.statSync(name);
    +
       24.      1    } catch(err) {
    +
       25.     -0        if (err.errno !== process.ENOENT && err.code !== 'ENOENT' && err.syscall !== 'unlink') {
    +
       26.     -0            throw err;
    +
       27.     -0        }
    +
       28.      1    }
    +
       29.      2};
    +
       30.      1
    +
       31.      6assert.fileExists = function(name) {
    +
       32.      6    try {
    +
       33.      6        fs.statSync(name);
    +
       34.     -0    } catch(err) {
    +
       35.     -0        throw err;
    +
       36.     -0    }
    +
       37.      6};
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/trace.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/trace.test.js.html new file mode 100644 index 000000000..c44278f16 --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/trace.test.js.html @@ -0,0 +1,226 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/trace.test.js
    +
    +
    +
    +
    + 80.88 %
    + 55 / 68 +
    +
    + 13 / 68 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..');
    +
        2.      1var assert = require('assert');
    +
        3.      1
    +
        4.      1describe('tracing', function() {
    +
        5.      1    it('Database tracing', function(done) {
    +
        6.      1        var db = new sqlite3.Database(':memory:');
    +
        7.      1        var create = false;
    +
        8.      1        var select = false;
    +
        9.      1
    +
       10.      2        db.on('trace', function(sql) {
    +
       11.      1            if (sql.match(/^SELECT/)) {
    +
       12.      1                assert.ok(!select);
    +
       13.      1                assert.equal(sql, "SELECT * FROM foo");
    +
       14.      1                select = true;
    +
       15.      1            }
    +
       16.      1            else if (sql.match(/^CREATE/)) {
    +
       17.      1                assert.ok(!create);
    +
       18.      1                assert.equal(sql, "CREATE TABLE foo (id int)");
    +
       19.      1                create = true;
    +
       20.     -0            }
    +
       21.     -0            else {
    +
       22.     -0                assert.ok(false);
    +
       23.     -0            }
    +
       24.      2        });
    +
       25.      1
    +
       26.      1        db.serialize(function() {
    +
       27.      1            db.run("CREATE TABLE foo (id int)");
    +
       28.      1            db.run("SELECT * FROM foo");
    +
       29.      1        });
    +
       30.      1
    +
       31.      1        db.close(function(err) {
    +
       32.     -0            if (err) throw err;
    +
       33.      1            assert.ok(create);
    +
       34.      1            assert.ok(select);
    +
       35.      1            done();
    +
       36.      1        });
    +
       37.      1    });
    +
       38.      1
    +
       39.      1
    +
       40.      1    it('test disabling tracing #1', function(done) {
    +
       41.      1        var db = new sqlite3.Database(':memory:');
    +
       42.      1
    +
       43.     -0        db.on('trace', function(sql) {});
    +
       44.      1        db.removeAllListeners('trace');
    +
       45.     -0        db._events['trace'] = function(sql) {
    +
       46.     -0            assert.ok(false);
    +
       47.     -0        };
    +
       48.      1
    +
       49.      1        db.run("CREATE TABLE foo (id int)");
    +
       50.      1        db.close(done);
    +
       51.      1    });
    +
       52.      1
    +
       53.      1
    +
       54.      1    it('test disabling tracing #2', function(done) {
    +
       55.      1        var db = new sqlite3.Database(':memory:');
    +
       56.      1
    +
       57.     -0        var trace = function(sql) {};
    +
       58.      1        db.on('trace', trace);
    +
       59.      1        db.removeListener('trace', trace);
    +
       60.     -0        db._events['trace'] = function(sql) {
    +
       61.     -0            assert.ok(false);
    +
       62.     -0        };
    +
       63.      1
    +
       64.      1        db.run("CREATE TABLE foo (id int)");
    +
       65.      1        db.close(done);
    +
       66.      1    });
    +
       67.      1});
    +
       68.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/unicode.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/unicode.test.js.html new file mode 100644 index 000000000..428248d51 --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/unicode.test.js.html @@ -0,0 +1,273 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/unicode.test.js
    +
    +
    +
    +
    + 98.26 %
    + 113 / 115 +
    +
    + 2 / 115 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..');
    +
        2.      1var assert = require('assert');
    +
        3.      1
    +
        4.      1describe('unicode', function() {
    +
        5.      1    var first_values = [],
    +
        6.      1        trailing_values = [],
    +
        7.      1        chars = [],
    +
        8.      1        subranges = new Array(2),
    +
        9.      1        len = subranges.length,
    +
       10.      1        db,
    +
       11.      1        i;
    +
       12.      1
    +
       13.      1    before(function(done) { db = new sqlite3.Database(':memory:', done); });
    +
       14.      1
    +
       15.     96    for (i = 0x20; i < 0x80; i++) {
    +
       16.     96        first_values.push(i);
    +
       17.     96    }
    +
       18.      1
    +
       19.     46    for (i = 0xc2; i < 0xf0; i++) {
    +
       20.     46        first_values.push(i);
    +
       21.     46    }
    +
       22.      1
    +
       23.     64    for (i = 0x80; i < 0xc0; i++) {
    +
       24.     64        trailing_values.push(i);
    +
       25.     64    }
    +
       26.      1
    +
       27.      2    for (i = 0; i < len; i++) {
    +
       28.      2        subranges[i] = [];
    +
       29.      2    }
    +
       30.      1
    +
       31.     32    for (i = 0xa0; i < 0xc0; i++) {
    +
       32.     32        subranges[0].push(i);
    +
       33.     32    }
    +
       34.      1
    +
       35.     32    for (i = 0x80; i < 0xa0; i++) {
    +
       36.     32        subranges[1].push(i);
    +
       37.     32    }
    +
       38.      1
    +
       39.  65908    function random_choice(arr) {
    +
       40.  65908        return arr[Math.random() * arr.length | 0];
    +
       41.  65908    }
    +
       42.      1
    +
       43.  45931    function random_utf8() {
    +
       44.  45931        var first = random_choice(first_values);
    +
       45.  45931
    +
       46.  31079        if (first < 0x80) {
    +
       47.  31079            return String.fromCharCode(first);
    +
       48.  31079        } else if (first < 0xe0) {
    +
       49.  14852            return String.fromCharCode((first & 0x1f) << 0x6 | random_choice(trailing_values) & 0x3f);
    +
       50.  14852        } else if (first == 0xe0) {
    +
       51.  14852             return String.fromCharCode(((first & 0xf) << 0xc) | ((random_choice(subranges[0]) & 0x3f) << 6) | random_choice(trailing_values) & 0x3f);
    +
       52.  14852        } else if (first == 0xed) {
    +
       53.  14852            return String.fromCharCode(((first & 0xf) << 0xc) | ((random_choice(subranges[1]) & 0x3f) << 6) | random_choice(trailing_values) & 0x3f);
    +
       54.  14852        } else if (first < 0xf0) {
    +
       55.  14852            return String.fromCharCode(((first & 0xf) << 0xc) | ((random_choice(trailing_values) & 0x3f) << 6) | random_choice(trailing_values) & 0x3f);
    +
       56.  14852        }
    +
       57.  45931    }
    +
       58.      1
    +
       59.    293    function randomString() {
    +
       60.    293        var str = '',
    +
       61.    293        i;
    +
       62.    293
    +
       63.  45931        for (i = Math.random() * 300; i > 0; i--) {
    +
       64.  45931            str += random_utf8();
    +
       65.  45931        }
    +
       66.    293
    +
       67.    293        return str;
    +
       68.    293    }
    +
       69.      1
    +
       70.      1
    +
       71.      1        // Generate random data.
    +
       72.      1    var data = [];
    +
       73.      1    var length = Math.floor(Math.random() * 1000) + 200;
    +
       74.    293    for (var i = 0; i < length; i++) {
    +
       75.    293        data.push(randomString());
    +
       76.    293    }
    +
       77.      1
    +
       78.      1    var inserted = 0;
    +
       79.      1    var retrieved = 0;
    +
       80.      1
    +
       81.      1    it('should create the table', function(done) {
    +
       82.      1        db.run("CREATE TABLE foo (id int, txt text)", done);
    +
       83.      1    });
    +
       84.      1
    +
       85.      1    it('should insert all values', function(done) {
    +
       86.      1        var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)");
    +
       87.    293        for (var i = 0; i < data.length; i++) {
    +
       88.    293            stmt.run(i, data[i], function(err) {
    +
       89.     -0                if (err) throw err;
    +
       90.    293                inserted++;
    +
       91.    293            });
    +
       92.    293        }
    +
       93.      1        stmt.finalize(done);
    +
       94.      1    });
    +
       95.      1
    +
       96.      1    it('should retrieve all values', function(done) {
    +
       97.      1        db.all("SELECT txt FROM foo ORDER BY id", function(err, rows) {
    +
       98.     -0            if (err) throw err;
    +
       99.      1
    +
      100.    293            for (var i = 0; i < rows.length; i++) {
    +
      101.    293                assert.equal(rows[i].txt, data[i]);
    +
      102.    293                retrieved++;
    +
      103.    293            }
    +
      104.      1            done();
    +
      105.      1        });
    +
      106.      1    });
    +
      107.      1
    +
      108.      1    it('should have inserted and retrieved the correct amount', function() {
    +
      109.      1        assert.equal(inserted, length);
    +
      110.      1        assert.equal(retrieved, length);
    +
      111.      1    });
    +
      112.      1
    +
      113.      1    after(function(done) { db.close(done); });
    +
      114.      1});
    +
      115.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/upsert.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/upsert.test.js.html new file mode 100644 index 000000000..18754a165 --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/upsert.test.js.html @@ -0,0 +1,186 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/upsert.test.js
    +
    +
    +
    +
    + 85.71 %
    + 24 / 28 +
    +
    + 4 / 28 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..');
    +
        2.      1var assert = require('assert');
    +
        3.      1
    +
        4.      1describe('query properties', function() {
    +
        5.      1    var db;
    +
        6.      1    before(function(done) {
    +
        7.      1        db = new sqlite3.Database(':memory:');
    +
        8.      1        db.run("CREATE TABLE foo (id INT PRIMARY KEY, count INT)", done);
    +
        9.      1    });
    +
       10.      1
    +
       11.     -0    (sqlite3.VERSION_NUMBER < 3024000 ? it.skip : it)('should upsert', function(done) {
    +
       12.      1        var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)");
    +
       13.      1        stmt.run(1, 1, function(err) { // insert 1
    +
       14.     -0            if (err) throw err;
    +
       15.      1            var upsert_stmt = db.prepare("INSERT INTO foo VALUES(?, ?) ON CONFLICT (id) DO UPDATE SET count = count + excluded.count");
    +
       16.      1            upsert_stmt.run(1, 2, function(err) { // add 2
    +
       17.     -0                if (err) throw err;
    +
       18.      1                var select_stmt = db.prepare("SELECT count FROM foo WHERE id = ?");
    +
       19.      1                select_stmt.get(1, function(err, row) {
    +
       20.     -0                    if (err) throw err;
    +
       21.      1                    assert.equal(row.count, 3); // equals 3
    +
       22.      1                });
    +
       23.      1            })
    +
       24.      1        });
    +
       25.      1        db.wait(done);
    +
       26.      1    });
    +
       27.      1});
    +
       28.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/test/verbose.test.js.html b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/verbose.test.js.html new file mode 100644 index 000000000..05ebe1b80 --- /dev/null +++ b/branch-sandbox/.artifact/coverage_sqlite3_sh/test/verbose.test.js.html @@ -0,0 +1,219 @@ + + + +V8 Coverage Report + + + + +
    +
    V8 Coverage Report
    + + + + + + + + + + + + + + + +
    Files coveredLinesRemaining
    + . / test/verbose.test.js
    +
    +
    +
    +
    + 100.00 %
    + 61 / 61 +
    +
    + 0 / 61 +
    +
    + + +
    +
        1.      1var sqlite3 = require('..');
    +
        2.      1var assert = require('assert');
    +
        3.      1
    +
        4.      1var invalid_sql = 'update non_existent_table set id=1';
    +
        5.      1
    +
        6.      1var originalMethods = {
    +
        7.      1    Database: {},
    +
        8.      1    Statement: {},
    +
        9.      1};
    +
       10.      1
    +
       11.      1function backupOriginalMethods() {
    +
       12.      2    for (var obj in originalMethods) {
    +
       13.     44        for (var attr in sqlite3[obj].prototype) {
    +
       14.     44            originalMethods[obj][attr] = sqlite3[obj].prototype[attr];
    +
       15.     44        }
    +
       16.      2    }
    +
       17.      1}
    +
       18.      1
    +
       19.      1function resetVerbose() {
    +
       20.      2    for (var obj in originalMethods) {
    +
       21.     44        for (var attr in originalMethods[obj]) {
    +
       22.     44            sqlite3[obj].prototype[attr] = originalMethods[obj][attr];
    +
       23.     44        }
    +
       24.      2    }
    +
       25.      1}
    +
       26.      1
    +
       27.      1describe('verbose', function() {
    +
       28.      1    it('Shoud add trace info to error when verbose is called', function(done) {
    +
       29.      1        var db = new sqlite3.Database(':memory:');
    +
       30.      1        backupOriginalMethods();
    +
       31.      1        sqlite3.verbose();
    +
       32.      1
    +
       33.      1        db.run(invalid_sql, function(err) {
    +
       34.      1            assert(err instanceof Error);
    +
       35.      1
    +
       36.      1            assert(
    +
       37.      1                err.stack.indexOf(`Database#run('${invalid_sql}'`) > -1,
    +
       38.      1                `Stack shoud contain trace info, stack = ${err.stack}`
    +
       39.      1            );
    +
       40.      1
    +
       41.      1            done();
    +
       42.      1            resetVerbose();
    +
       43.      1        });
    +
       44.      1    });
    +
       45.      1
    +
       46.      1    it('Shoud not add trace info to error when verbose is not called', function(done) {
    +
       47.      1        var db = new sqlite3.Database(':memory:');
    +
       48.      1
    +
       49.      1        db.run(invalid_sql, function(err) {
    +
       50.      1            assert(err instanceof Error);
    +
       51.      1
    +
       52.      1            assert(
    +
       53.      1                err.stack.indexOf(invalid_sql) === -1,
    +
       54.      1                `Stack shoud not contain trace info, stack = ${err.stack}`
    +
       55.      1            );
    +
       56.      1
    +
       57.      1            done();
    +
       58.      1        });
    +
       59.      1    });
    +
       60.      1});
    +
       61.      1
    +
    + + + + diff --git a/branch-sandbox/.artifact/coverage_sqlite3_sh/touch.txt b/branch-sandbox/.artifact/coverage_sqlite3_sh/touch.txt new file mode 100644 index 000000000..e69de29bb diff --git a/branch-sandbox/.artifact/jslint_report_hello.html b/branch-sandbox/.artifact/jslint_report_hello.html new file mode 100644 index 000000000..b7267032c --- /dev/null +++ b/branch-sandbox/.artifact/jslint_report_hello.html @@ -0,0 +1,227 @@ +
    + +
    +JSLint Report +
    +
    +Report: Warnings (2) +
    +
    1: 17
    1. Undeclared 'console'.
    function foo() {console.log('hello world');} + +
    1: 29
    2. Use double quotes, not single quotes.
    function foo() {console.log('hello world');} + +
    +
    +
    +Report: Properties (1) + +
    +
    +Report: Functions (1) +
    +
    +
    global
    foo
    +
    1: 1
    foo()
    +
    +
    +
    diff --git a/branch-sandbox/.artifact/screenshot_browser__2f.artifact_2fapidoc.html.png b/branch-sandbox/.artifact/screenshot_browser__2f.artifact_2fapidoc.html.png new file mode 100644 index 000000000..ad66f7afc Binary files /dev/null and b/branch-sandbox/.artifact/screenshot_browser__2f.artifact_2fapidoc.html.png differ diff --git a/branch-sandbox/.artifact/screenshot_browser__2f.artifact_2fcoverage_sqlite3_js_2findex.html.png b/branch-sandbox/.artifact/screenshot_browser__2f.artifact_2fcoverage_sqlite3_js_2findex.html.png new file mode 100644 index 000000000..64f0963c2 Binary files /dev/null and b/branch-sandbox/.artifact/screenshot_browser__2f.artifact_2fcoverage_sqlite3_js_2findex.html.png differ diff --git a/branch-sandbox/.artifact/screenshot_browser__2f.artifact_2fcoverage_sqlite3_js_2flib_2fsqlite3.js.html.png b/branch-sandbox/.artifact/screenshot_browser__2f.artifact_2fcoverage_sqlite3_js_2flib_2fsqlite3.js.html.png new file mode 100644 index 000000000..318d489ea Binary files /dev/null and b/branch-sandbox/.artifact/screenshot_browser__2f.artifact_2fcoverage_sqlite3_js_2flib_2fsqlite3.js.html.png differ diff --git a/branch-sandbox/.artifact/screenshot_browser__2f.artifact_2fcoverage_sqlite3_sh_2findex.html.png b/branch-sandbox/.artifact/screenshot_browser__2f.artifact_2fcoverage_sqlite3_sh_2findex.html.png new file mode 100644 index 000000000..2f0656b70 Binary files /dev/null and b/branch-sandbox/.artifact/screenshot_browser__2f.artifact_2fcoverage_sqlite3_sh_2findex.html.png differ diff --git a/branch-sandbox/.artifact/screenshot_browser__2f.artifact_2fcoverage_sqlite3_sh_2flib_2fsqlite3.js.html.png b/branch-sandbox/.artifact/screenshot_browser__2f.artifact_2fcoverage_sqlite3_sh_2flib_2fsqlite3.js.html.png new file mode 100644 index 000000000..c2ffe16a4 Binary files /dev/null and b/branch-sandbox/.artifact/screenshot_browser__2f.artifact_2fcoverage_sqlite3_sh_2flib_2fsqlite3.js.html.png differ diff --git a/branch-sandbox/.artifact/screenshot_browser__2f.artifact_2fjslint_report_hello.html.png b/branch-sandbox/.artifact/screenshot_browser__2f.artifact_2fjslint_report_hello.html.png new file mode 100644 index 000000000..7f73fa4c3 Binary files /dev/null and b/branch-sandbox/.artifact/screenshot_browser__2f.artifact_2fjslint_report_hello.html.png differ diff --git a/branch-sandbox/.artifact/screenshot_browser__2fjslint_2fbranch-beta_2findex.html.png b/branch-sandbox/.artifact/screenshot_browser__2fjslint_2fbranch-beta_2findex.html.png new file mode 100644 index 000000000..2e9ce4c09 Binary files /dev/null and b/branch-sandbox/.artifact/screenshot_browser__2fjslint_2fbranch-beta_2findex.html.png differ diff --git a/branch-sandbox/.artifact/screenshot_changelog.svg b/branch-sandbox/.artifact/screenshot_changelog.svg new file mode 100644 index 000000000..61e08fddf --- /dev/null +++ b/branch-sandbox/.artifact/screenshot_changelog.svg @@ -0,0 +1,335 @@ + + + +# Changelog + +# Todo +- cli - remove cli-option `--mode-vim-plugin` +- coverage - add macros `/*coverage-disable*/` and `/*coverage-enable*/`. +- coverage - support globbing `*` in cli-options `--exclude` and `--include` +- jslint - add html and css linting back into jslint. +- jslint - add new warning requiring paren around plus-separated concatenations. +- jslint - add numeric-separators support. +- jslint - relax warning against console.log and friends and deprecate directive `devel` +- jslint - require regexp to use open-form. +- jslint - try to improve parser to be able to parse jquery.js without stopping. +- jslint - unify analysis of variable-assignment/function-parameters into one function +- node - after node-v14 is deprecated, remove shell-code `export "NODE_OPTIONS=--unhandled-rejec\ + tions=strict"`. +- perf - improve performance by hoisting inlined regexps out of loops and subfunctions + +# v2021.12.20 +- jslint - relax warning "function_in_loop" +- update function assertJsonEqual to JSON.stringify 3rd param if its an object + +# v2021.11.20 +- jslint - add top-level-await support +- ci - deprecate/remove jslint.cjs from ci +- coverage - add cli-options `--exclude=aa,bb`, `--exclude-node-modules=false`, `--include=aa,bb\ + ` +- coverage - dedupe coverage-logic now applied when only one script passed +- npm - add file .npmignore +- website - add clickable-links to editor-code in report-warnings and report-functions + +# v2021.10.20 +- ci - add release-trigger to publish to `@jslint-org/jslint` +- bugfix - fix coverage-report having incorrect http-link to index.html +- bugfix - fix false warning `uninitialized 'bb'` in code `/*jslint node*/\nlet {aa:bb} = {}; bb\ + ();` +- bugfix - fix issue #358 - switch-statement crashes jslint +- ci - cache coverage-example node-sqlite3 to speed up ci +- ci - rename dir .build/ to .artifact/ +- ci - update shell-function shRunWithCoverage() to reduce size of string/argument passed to nod\ + ejs by using 2-space-indent +- cli - add cli-command jslint_plugin_vim +- cli - add cli-command v8_coverage_report +- cli - change cli-option `--mode-report` to cli-command `jslint_report=<filename>` +- coverage - relax requirement for coverageDir to be in cwd +- deprecated - cli - add cli-option `--mode-report` +- doc - add api-documentation +- fs - merge file asset_codemirror_rollup.css into index.html +- fs - merge file browser.mjs into index.html +- fs - merge file function.html into help.html +- fs - remove little-used font asset_font_programma_bold.woff2 +- fs - rename files with dashes to files with underscore +- jslint - disable linting of embedded javascript in markdown-files +- jslint - relax regexp-warning against using 'space' +- npm - add file package.json and command `npm test` + + + diff --git a/branch-sandbox/.artifact/screenshot_js_coverage_report_spawn.svg b/branch-sandbox/.artifact/screenshot_js_coverage_report_spawn.svg new file mode 100644 index 000000000..a8437a769 --- /dev/null +++ b/branch-sandbox/.artifact/screenshot_js_coverage_report_spawn.svg @@ -0,0 +1,473 @@ + + + +> #!/bin/sh +> +> git clone https://github.com/mapbox/node-sqlite3 node-sqlite3-js \ +> --branch=v5.0.2 \ +> --depth=1 \ +> --single-branch +> +> cd node-sqlite3-js +> npm install +> +> node --input-type=module --eval ' +> +> /*jslint node*/ +> import jslint from "../jslint.mjs"; +> (async function () { +> +> // Create V8 coverage report from program `npm run test` in javascript. +> +> await jslint.v8CoverageReportCreate({ +> coverageDir: "../.artifact/coverage_sqlite3_js/", +> processArgv: [ +> "--include=lib/sqlite3-binding.js,lib/sqlite3.js", +> "--include=lib/trace.js", +> "npm", "run", "test" +> ] +> }); +> }()); +> +> ' + + +HEAD is now at 60a022c napi: temporarily disable v6 (see issue #1444) +npm WARN deprecated har-validator@5.1.5: this library is no longer supported +npm WARN deprecated querystring@0.2.0: The querystring API is considered Legacy. new code should\ + use the URLSearchParams API instead. +npm WARN deprecated circular-json@0.3.3: CircularJSON is in maintenance only, flatted is its suc\ + cessor. +npm WARN deprecated uuid@3.3.2: Please upgrade to version 7 or higher. Older versions may use \ + Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/\ + blog/math-random for details. +npm WARN deprecated mkdirp@0.5.1: Legacy versions of mkdirp are no longer supported. Please upda\ + te to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.) +npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/\ + request/issues/3142 +npm WARN deprecated node-pre-gyp@0.11.0: Please upgrade to @mapbox/node-pre-gyp: the non-scoped \ + node-pre-gyp package is deprecated and only the @mapbox scoped package will recieve updates in\ + the future +npm WARN deprecated tar@2.2.2: This version of tar is no longer supported, and will not receive \ + security updates. Please upgrade asap. + +> sqlite3@5.0.2 install +> node-pre-gyp install --fallback-to-build + +node-pre-gyp info it worked if it ends with ok +node-pre-gyp info using node-pre-gyp@0.11.0 +node-pre-gyp info using node@16.13.1 | linux | x64 +node-pre-gyp WARN Using request for node-pre-gyp https download +node-pre-gyp info check checked for "/home/runner/work/jslint/jslint/node-sqlite3-js/lib/binding\ + /napi-v3-linux-x64/node_sqlite3.node" (not found) +node-pre-gyp http GET https://mapbox-node-binary.s3.amazonaws.com/sqlite3/v5.0.2/napi-v3-linux-x\ + 64.tar.gz +node-pre-gyp http 200 https://mapbox-node-binary.s3.amazonaws.com/sqlite3/v5.0.2/napi-v3-linux-x\ + 64.tar.gz +node-pre-gyp info install unpacking napi-v3-linux-x64/node_sqlite3.node +node-pre-gyp info tarball done parsing tarball +[sqlite3] Success: "/home/runner/work/jslint/jslint/node-sqlite3-js/lib/binding/napi-v3-linux-x6\ + 4/node_sqlite3.node" is installed via remote +node-pre-gyp info ok + +added 244 packages, and audited 245 packages in 11s + +4 packages are looking for funding + run `npm fund` for details + +8 vulnerabilities (6 moderate, 2 high) + +To address issues that do not require attention, run: + + + diff --git a/branch-sandbox/.artifact/screenshot_js_coverage_report_spawn.svg.sh b/branch-sandbox/.artifact/screenshot_js_coverage_report_spawn.svg.sh new file mode 100644 index 000000000..0704cb428 --- /dev/null +++ b/branch-sandbox/.artifact/screenshot_js_coverage_report_spawn.svg.sh @@ -0,0 +1,66 @@ +(set -e +printf '> #!/bin/sh +> +> git clone https://github.com/mapbox/node-sqlite3 node-sqlite3-js \\ +> --branch=v5.0.2 \\ +> --depth=1 \\ +> --single-branch +> +> cd node-sqlite3-js +> npm install +> +> node --input-type=module --eval '"'"' +> +> /*jslint node*/ +> import jslint from "../jslint.mjs"; +> (async function () { +> +> // Create V8 coverage report from program `npm run test` in javascript. +> +> await jslint.v8CoverageReportCreate({ +> coverageDir: "../.artifact/coverage_sqlite3_js/", +> processArgv: [ +> "--include=lib/sqlite3-binding.js,lib/sqlite3.js", +> "--include=lib/trace.js", +> "npm", "run", "test" +> ] +> }); +> }()); +> +> '"'"' + + +' +#!/bin/sh + +git clone https://github.com/mapbox/node-sqlite3 node-sqlite3-js \ + --branch=v5.0.2 \ + --depth=1 \ + --single-branch 2>/dev/null || true + + +cd node-sqlite3-js + +git checkout 60a022c511a37788e652c271af23174566a80c30 +npm install + +node --input-type=module --eval ' + +/*jslint node*/ +import jslint from "../jslint.mjs"; +(async function () { + +// Create V8 coverage report from program `npm run test` in javascript. + + await jslint.v8CoverageReportCreate({ + coverageDir: "../.artifact/coverage_sqlite3_js/", + processArgv: [ + "--include=lib/sqlite3-binding.js,lib/sqlite3.js", + "--include=lib/trace.js", + "npm", "run", "test" + ] + }); +}()); + +' +) diff --git a/branch-sandbox/.artifact/screenshot_js_import_cjs.svg b/branch-sandbox/.artifact/screenshot_js_import_cjs.svg new file mode 100644 index 000000000..d0936e215 --- /dev/null +++ b/branch-sandbox/.artifact/screenshot_js_import_cjs.svg @@ -0,0 +1,215 @@ + + + +> #!/bin/sh +> +> node --eval ' +> +> /*jslint devel*/ +> (async function () { +> let globals = ["caches", "indexedDb"]; +> let jslint; +> let options = {browser: true}; +> let result; +> let source = "console.log(\u0027hello world\u0027);\n"; +> +> // Import JSLint in CommonJS environment. +> +> jslint = await import("./jslint.mjs"); +> jslint = jslint.default; +> +> // JSLint <source> and print <formatted_message>. +> +> result = jslint.jslint(source, options, globals); +> result.warnings.forEach(function ({ +> formatted_message +> }) { +> console.error(formatted_message); +> }); +> }()); +> +> ' + + + 1. Undeclared 'console'. // line 1, column 1 + console.log('hello world'); + 2. Use double quotes, not single quotes. // line 1, column 13 + console.log('hello world'); + + + diff --git a/branch-sandbox/.artifact/screenshot_js_import_cjs.svg.sh b/branch-sandbox/.artifact/screenshot_js_import_cjs.svg.sh new file mode 100644 index 000000000..6d4dbc57f --- /dev/null +++ b/branch-sandbox/.artifact/screenshot_js_import_cjs.svg.sh @@ -0,0 +1,61 @@ +(set -e +printf '> #!/bin/sh +> +> node --eval '"'"' +> +> /*jslint devel*/ +> (async function () { +> let globals = ["caches", "indexedDb"]; +> let jslint; +> let options = {browser: true}; +> let result; +> let source = "console.log(\\u0027hello world\\u0027);\\n"; +> +> // Import JSLint in CommonJS environment. +> +> jslint = await import("./jslint.mjs"); +> jslint = jslint.default; +> +> // JSLint and print . +> +> result = jslint.jslint(source, options, globals); +> result.warnings.forEach(function ({ +> formatted_message +> }) { +> console.error(formatted_message); +> }); +> }()); +> +> '"'"' + + +' +#!/bin/sh + +node --eval ' + +/*jslint devel*/ +(async function () { + let globals = ["caches", "indexedDb"]; + let jslint; + let options = {browser: true}; + let result; + let source = "console.log(\u0027hello world\u0027);\n"; + +// Import JSLint in CommonJS environment. + + jslint = await import("./jslint.mjs"); + jslint = jslint.default; + +// JSLint and print . + + result = jslint.jslint(source, options, globals); + result.warnings.forEach(function ({ + formatted_message + }) { + console.error(formatted_message); + }); +}()); + +' +) diff --git a/branch-sandbox/.artifact/screenshot_js_import_esm.svg b/branch-sandbox/.artifact/screenshot_js_import_esm.svg new file mode 100644 index 000000000..304fcc3b0 --- /dev/null +++ b/branch-sandbox/.artifact/screenshot_js_import_esm.svg @@ -0,0 +1,197 @@ + + + +> #!/bin/sh +> +> node --input-type=module --eval ' +> +> /*jslint devel*/ +> +> // Import JSLint in ES Module environment. +> +> import jslint from "./jslint.mjs"; +> +> let globals = ["caches", "indexedDb"]; +> let options = {browser: true}; +> let result; +> let source = "console.log(\u0027hello world\u0027);\n"; +> +> // JSLint <source> and print <formatted_message>. +> +> result = jslint.jslint(source, options, globals); +> result.warnings.forEach(function ({ +> formatted_message +> }) { +> console.error(formatted_message); +> }); +> +> ' + + + 1. Undeclared 'console'. // line 1, column 1 + console.log('hello world'); + 2. Use double quotes, not single quotes. // line 1, column 13 + console.log('hello world'); + + + diff --git a/branch-sandbox/.artifact/screenshot_js_import_esm.svg.sh b/branch-sandbox/.artifact/screenshot_js_import_esm.svg.sh new file mode 100644 index 000000000..eaf7b7ab0 --- /dev/null +++ b/branch-sandbox/.artifact/screenshot_js_import_esm.svg.sh @@ -0,0 +1,55 @@ +(set -e +printf '> #!/bin/sh +> +> node --input-type=module --eval '"'"' +> +> /*jslint devel*/ +> +> // Import JSLint in ES Module environment. +> +> import jslint from "./jslint.mjs"; +> +> let globals = ["caches", "indexedDb"]; +> let options = {browser: true}; +> let result; +> let source = "console.log(\\u0027hello world\\u0027);\\n"; +> +> // JSLint and print . +> +> result = jslint.jslint(source, options, globals); +> result.warnings.forEach(function ({ +> formatted_message +> }) { +> console.error(formatted_message); +> }); +> +> '"'"' + + +' +#!/bin/sh + +node --input-type=module --eval ' + +/*jslint devel*/ + +// Import JSLint in ES Module environment. + +import jslint from "./jslint.mjs"; + +let globals = ["caches", "indexedDb"]; +let options = {browser: true}; +let result; +let source = "console.log(\u0027hello world\u0027);\n"; + +// JSLint and print . + +result = jslint.jslint(source, options, globals); +result.warnings.forEach(function ({ + formatted_message +}) { + console.error(formatted_message); +}); + +' +) diff --git a/branch-sandbox/.artifact/screenshot_js_jslint_report_file.svg b/branch-sandbox/.artifact/screenshot_js_jslint_report_file.svg new file mode 100644 index 000000000..e4212b715 --- /dev/null +++ b/branch-sandbox/.artifact/screenshot_js_jslint_report_file.svg @@ -0,0 +1,167 @@ + + + +> #!/bin/sh +> +> node --input-type=module --eval ' +> +> /*jslint devel*/ +> import jslint from "./jslint.mjs"; +> import fs from "fs"; +> (async function () { +> let report; +> let result; +> let source = "function foo() {console.log(\u0027hello world\u0027);}\n"; +> +> // Create JSLint report from <source> in javascript. +> +> result = jslint.jslint(source); +> report = jslint.jslint_report(result); +> +> await fs.promises.mkdir(".artifact/", {recursive: true}); +> await fs.promises.writeFile(".artifact/jslint_report_hello.html", report); +> console.error("wrote file .artifact/jslint_report_hello.html"); +> }()); +> +> ' + + +wrote file .artifact/jslint_report_hello.html + + + diff --git a/branch-sandbox/.artifact/screenshot_js_jslint_report_file.svg.sh b/branch-sandbox/.artifact/screenshot_js_jslint_report_file.svg.sh new file mode 100644 index 000000000..08e6b330e --- /dev/null +++ b/branch-sandbox/.artifact/screenshot_js_jslint_report_file.svg.sh @@ -0,0 +1,51 @@ +(set -e +printf '> #!/bin/sh +> +> node --input-type=module --eval '"'"' +> +> /*jslint devel*/ +> import jslint from "./jslint.mjs"; +> import fs from "fs"; +> (async function () { +> let report; +> let result; +> let source = "function foo() {console.log(\\u0027hello world\\u0027);}\\n"; +> +> // Create JSLint report from in javascript. +> +> result = jslint.jslint(source); +> report = jslint.jslint_report(result); +> +> await fs.promises.mkdir(".artifact/", {recursive: true}); +> await fs.promises.writeFile(".artifact/jslint_report_hello.html", report); +> console.error("wrote file .artifact/jslint_report_hello.html"); +> }()); +> +> '"'"' + + +' +#!/bin/sh + +node --input-type=module --eval ' + +/*jslint devel*/ +import jslint from "./jslint.mjs"; +import fs from "fs"; +(async function () { + let report; + let result; + let source = "function foo() {console.log(\u0027hello world\u0027);}\n"; + +// Create JSLint report from in javascript. + + result = jslint.jslint(source); + report = jslint.jslint_report(result); + + await fs.promises.mkdir(".artifact/", {recursive: true}); + await fs.promises.writeFile(".artifact/jslint_report_hello.html", report); + console.error("wrote file .artifact/jslint_report_hello.html"); +}()); + +' +) diff --git a/branch-sandbox/.artifact/screenshot_package_listing.svg b/branch-sandbox/.artifact/screenshot_package_listing.svg new file mode 100644 index 000000000..73d5e5167 --- /dev/null +++ b/branch-sandbox/.artifact/screenshot_package_listing.svg @@ -0,0 +1,191 @@ + + + + 0. 755 2021-12-20T23:41:41Z 1371 KB . + 1. 644 2021-12-19T01:10:46Z 9 KB .ci.sh + 2. 644 2021-05-26T14:36:14Z 1 KB .gitconfig + 3. 644 2021-10-02T19:55:12Z 1 KB .github/ISSUE_TEMPLATE/bug_report.md + 4. 644 2021-10-02T19:55:12Z 1 KB .github/ISSUE_TEMPLATE/feature_request.md + 5. 644 2021-10-02T19:55:12Z 1 KB .github/ISSUE_TEMPLATE/website_issue.md + 6. 644 2021-12-19T01:10:46Z 2 KB .github/workflows/ci.yml + 7. 644 2021-10-02T19:55:12Z 2 KB .github/workflows/on_pull_request.yml + 8. 644 2021-12-19T01:10:46Z 2 KB .github/workflows/publish.yml + 9. 644 2021-11-03T16:46:41Z 1 KB .gitignore +10. 644 2021-12-19T01:10:46Z 1 KB .npmignore +11. 644 2021-12-20T22:04:17Z 14 KB CHANGELOG.md +12. 644 2021-10-02T18:57:15Z 2 KB LICENSE +13. 644 2021-12-20T22:04:17Z 15 KB README.md +14. 644 2021-10-26T00:21:09Z 450 KB asset_codemirror_rollup.js +15. 644 2021-10-02T19:55:12Z 8 KB asset_font_daley_bold.woff2 +16. 644 2021-10-02T19:55:12Z 1 KB asset_image_folder_open_solid.svg +17. 644 2021-10-02T19:55:12Z 2 KB asset_image_github_brands.svg +18. 644 2021-10-02T19:55:12Z 13 KB asset_image_jslint_vim_plugin.png +19. 644 2021-10-03T03:04:13Z 5 KB asset_image_json_160.svg +20. 644 2021-10-02T19:55:12Z 3 KB asset_image_logo_512.svg +21. 644 2021-10-03T03:04:13Z 54 KB help.html +22. 644 2021-12-20T22:04:17Z 49 KB index.html +23. 644 2021-12-20T23:41:41Z 1 KB jslint.cjs +24. 644 2021-12-20T22:04:17Z 315 KB jslint.mjs +25. 644 2021-10-07T22:14:10Z 1 KB jslint.vim +26. 644 2021-12-19T01:10:46Z 78 KB jslint_ci.sh +27. 644 2021-12-20T23:41:41Z 1 KB package.json +28. 644 2021-12-20T23:28:08Z 33 KB test.mjs +29. 644 2021-10-12T00:21:08Z 319 KB test_coverage_merge_data.json + + + diff --git a/branch-sandbox/.artifact/screenshot_sh_coverage_report_spawn.svg b/branch-sandbox/.artifact/screenshot_sh_coverage_report_spawn.svg new file mode 100644 index 000000000..a14f5a8a9 --- /dev/null +++ b/branch-sandbox/.artifact/screenshot_sh_coverage_report_spawn.svg @@ -0,0 +1,473 @@ + + + +> #!/bin/sh +> +> git clone https://github.com/mapbox/node-sqlite3 node-sqlite3-sh \ +> --branch=v5.0.2 \ +> --depth=1 \ +> --single-branch +> +> cd node-sqlite3-sh +> npm install +> +> # Create V8 coverage report from program `npm run test` in shell. +> +> node ../jslint.mjs \ +> v8_coverage_report=../.artifact/coverage_sqlite3_sh/ \ +> --exclude-node-modules=true \ +> --exclude=test/foo.js,test/bar.js \ +> --exclude=test/baz.js \ +> npm run test + + +HEAD is now at 60a022c napi: temporarily disable v6 (see issue #1444) +npm WARN deprecated har-validator@5.1.5: this library is no longer supported +npm WARN deprecated circular-json@0.3.3: CircularJSON is in maintenance only, flatted is its suc\ + cessor. +npm WARN deprecated querystring@0.2.0: The querystring API is considered Legacy. new code should\ + use the URLSearchParams API instead. +npm WARN deprecated uuid@3.3.2: Please upgrade to version 7 or higher. Older versions may use \ + Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/\ + blog/math-random for details. +npm WARN deprecated mkdirp@0.5.1: Legacy versions of mkdirp are no longer supported. Please upda\ + te to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.) +npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/\ + request/issues/3142 +npm WARN deprecated node-pre-gyp@0.11.0: Please upgrade to @mapbox/node-pre-gyp: the non-scoped \ + node-pre-gyp package is deprecated and only the @mapbox scoped package will recieve updates in\ + the future +npm WARN deprecated tar@2.2.2: This version of tar is no longer supported, and will not receive \ + security updates. Please upgrade asap. + +> sqlite3@5.0.2 install +> node-pre-gyp install --fallback-to-build + +node-pre-gyp info it worked if it ends with ok +node-pre-gyp info using node-pre-gyp@0.11.0 +node-pre-gyp info using node@16.13.1 | linux | x64 +node-pre-gyp WARN Using request for node-pre-gyp https download +node-pre-gyp info check checked for "/home/runner/work/jslint/jslint/node-sqlite3-sh/lib/binding\ + /napi-v3-linux-x64/node_sqlite3.node" (not found) +node-pre-gyp http GET https://mapbox-node-binary.s3.amazonaws.com/sqlite3/v5.0.2/napi-v3-linux-x\ + 64.tar.gz +node-pre-gyp http 200 https://mapbox-node-binary.s3.amazonaws.com/sqlite3/v5.0.2/napi-v3-linux-x\ + 64.tar.gz +node-pre-gyp info install unpacking napi-v3-linux-x64/node_sqlite3.node +node-pre-gyp info tarball done parsing tarball +[sqlite3] Success: "/home/runner/work/jslint/jslint/node-sqlite3-sh/lib/binding/napi-v3-linux-x6\ + 4/node_sqlite3.node" is installed via remote +node-pre-gyp info ok + +added 244 packages, and audited 245 packages in 12s + +4 packages are looking for funding + run `npm fund` for details + +8 vulnerabilities (6 moderate, 2 high) + +To address issues that do not require attention, run: + npm audit fix + +To address all issues (including breaking changes), run: + npm audit fix --force + +Run `npm audit` for details. +wrote file /home/runner/work/jslint/jslint/.artifact/coverage_sqlite3_sh//touch.txt + +> sqlite3@5.0.2 pretest +> node test/support/createdb.js + + + + diff --git a/branch-sandbox/.artifact/screenshot_sh_coverage_report_spawn.svg.sh b/branch-sandbox/.artifact/screenshot_sh_coverage_report_spawn.svg.sh new file mode 100644 index 000000000..188dccdd3 --- /dev/null +++ b/branch-sandbox/.artifact/screenshot_sh_coverage_report_spawn.svg.sh @@ -0,0 +1,44 @@ +(set -e +printf '> #!/bin/sh +> +> git clone https://github.com/mapbox/node-sqlite3 node-sqlite3-sh \\ +> --branch=v5.0.2 \\ +> --depth=1 \\ +> --single-branch +> +> cd node-sqlite3-sh +> npm install +> +> # Create V8 coverage report from program `npm run test` in shell. +> +> node ../jslint.mjs \\ +> v8_coverage_report=../.artifact/coverage_sqlite3_sh/ \\ +> --exclude-node-modules=true \\ +> --exclude=test/foo.js,test/bar.js \\ +> --exclude=test/baz.js \\ +> npm run test + + +' +#!/bin/sh + +git clone https://github.com/mapbox/node-sqlite3 node-sqlite3-sh \ + --branch=v5.0.2 \ + --depth=1 \ + --single-branch 2>/dev/null || true + + +cd node-sqlite3-sh + +git checkout 60a022c511a37788e652c271af23174566a80c30 +npm install + +# Create V8 coverage report from program `npm run test` in shell. + +node ../jslint.mjs \ + v8_coverage_report=../.artifact/coverage_sqlite3_sh/ \ + --exclude-node-modules=true \ + --exclude=test/foo.js,test/bar.js \ + --exclude=test/baz.js \ + npm run test +) diff --git a/branch-sandbox/.artifact/screenshot_sh_install_download.svg b/branch-sandbox/.artifact/screenshot_sh_install_download.svg new file mode 100644 index 000000000..3476a9bbe --- /dev/null +++ b/branch-sandbox/.artifact/screenshot_sh_install_download.svg @@ -0,0 +1,59 @@ + + + +> #!/bin/sh +> +> curl -L https://www.jslint.com/jslint.mjs > jslint.mjs + + +% Total % Received % Xferd Average Speed Time Time Time Current + Dload Upload Total Spent Left Speed +100 314k 100 314k 0 0 314k 0 0:00:01 --:--:-- 0:00:01 314k + + + diff --git a/branch-sandbox/.artifact/screenshot_sh_install_download.svg.sh b/branch-sandbox/.artifact/screenshot_sh_install_download.svg.sh new file mode 100644 index 000000000..882f6fed7 --- /dev/null +++ b/branch-sandbox/.artifact/screenshot_sh_install_download.svg.sh @@ -0,0 +1,13 @@ +(set -e +printf '> #!/bin/sh +> +> curl -L https://www.jslint.com/jslint.mjs > jslint.mjs + + +' +#!/bin/sh + +echo "% Total % Received % Xferd Average Speed Time Time Time Current + Dload Upload Total Spent Left Speed +100 314k 100 314k 0 0 314k 0 0:00:01 --:--:-- 0:00:01 314k" +) diff --git a/branch-sandbox/.artifact/screenshot_sh_jslint_dir.svg b/branch-sandbox/.artifact/screenshot_sh_jslint_dir.svg new file mode 100644 index 000000000..78603c927 --- /dev/null +++ b/branch-sandbox/.artifact/screenshot_sh_jslint_dir.svg @@ -0,0 +1,161 @@ + + + +> #!/bin/sh +> +> # JSLint directory '.' +> +> node jslint.mjs . + + +jslint - 79ms - ./.ci.sh +jslint - 87ms - ./CHANGELOG.md +jslint - 96ms - ./README.md +jslint ./hello.js + 1. Undeclared 'console'. // line 1, column 17 + function foo() {console.log('hello world');} + 2. Use double quotes, not single quotes. // line 1, column 29 + function foo() {console.log('hello world');} +jslint - 97ms - ./hello.js +jslint - 97ms - ./help.html +jslint - 134ms - ./index.html +jslint - 135ms - ./jslint.cjs +jslint - 383ms - ./jslint.js +jslint - 479ms - ./jslint_ci.sh +jslint - 480ms - ./package.json +jslint - 510ms - ./test.mjs +jslint - 757ms - ./jslint.mjs +jslint - 846ms - ./test_coverage_merge_data.json + + + diff --git a/branch-sandbox/.artifact/screenshot_sh_jslint_dir.svg.sh b/branch-sandbox/.artifact/screenshot_sh_jslint_dir.svg.sh new file mode 100644 index 000000000..ebae355e1 --- /dev/null +++ b/branch-sandbox/.artifact/screenshot_sh_jslint_dir.svg.sh @@ -0,0 +1,15 @@ +(set -e +printf '> #!/bin/sh +> +> # JSLint directory '"'"'.'"'"' +> +> node jslint.mjs . + + +' +#!/bin/sh + +# JSLint directory '.' + +node jslint.mjs . +) diff --git a/branch-sandbox/.artifact/screenshot_sh_jslint_file.svg b/branch-sandbox/.artifact/screenshot_sh_jslint_file.svg new file mode 100644 index 000000000..ff4d678da --- /dev/null +++ b/branch-sandbox/.artifact/screenshot_sh_jslint_file.svg @@ -0,0 +1,83 @@ + + + +> #!/bin/sh +> +> printf "console.log('hello world');\n" > hello.js +> +> node jslint.mjs hello.js + + +jslint hello.js + 1. Undeclared 'console'. // line 1, column 17 + function foo() {console.log('hello world');} + 2. Use double quotes, not single quotes. // line 1, column 29 + function foo() {console.log('hello world');} + + + diff --git a/branch-sandbox/.artifact/screenshot_sh_jslint_file.svg.sh b/branch-sandbox/.artifact/screenshot_sh_jslint_file.svg.sh new file mode 100644 index 000000000..e34977436 --- /dev/null +++ b/branch-sandbox/.artifact/screenshot_sh_jslint_file.svg.sh @@ -0,0 +1,15 @@ +(set -e +printf '> #!/bin/sh +> +> printf "console.log('"'"'hello world'"'"');\\n" > hello.js +> +> node jslint.mjs hello.js + + +' +#!/bin/sh + +printf "console.log('hello world');\n" > hello.js + +node jslint.mjs hello.js +) diff --git a/branch-sandbox/.artifact/screenshot_sh_jslint_report_file.svg b/branch-sandbox/.artifact/screenshot_sh_jslint_report_file.svg new file mode 100644 index 000000000..38dd10c37 --- /dev/null +++ b/branch-sandbox/.artifact/screenshot_sh_jslint_report_file.svg @@ -0,0 +1,113 @@ + + + +> #!/bin/sh +> +> printf "function foo() {console.log('hello world');}\n" > hello.js +> +> # Create JSLint report from file 'hello.js' in shell. +> +> node jslint.mjs \ +> jslint_report=.artifact/jslint_report_hello.html \ +> hello.js + + +jslint hello.js + 1. Undeclared 'console'. // line 1, column 17 + function foo() {console.log('hello world');} + 2. Use double quotes, not single quotes. // line 1, column 29 + function foo() {console.log('hello world');} +wrote file .artifact/jslint_report_hello.html + + + diff --git a/branch-sandbox/.artifact/screenshot_sh_jslint_report_file.svg.sh b/branch-sandbox/.artifact/screenshot_sh_jslint_report_file.svg.sh new file mode 100644 index 000000000..32200c7cd --- /dev/null +++ b/branch-sandbox/.artifact/screenshot_sh_jslint_report_file.svg.sh @@ -0,0 +1,23 @@ +(set -e +printf '> #!/bin/sh +> +> printf "function foo() {console.log('"'"'hello world'"'"');}\\n" > hello.js +> +> # Create JSLint report from file '"'"'hello.js'"'"' in shell. +> +> node jslint.mjs \\ +> jslint_report=.artifact/jslint_report_hello.html \\ +> hello.js + + +' +#!/bin/sh + +printf "function foo() {console.log('hello world');}\n" > hello.js + +# Create JSLint report from file 'hello.js' in shell. + +node jslint.mjs \ + jslint_report=.artifact/jslint_report_hello.html \ + hello.js +) diff --git a/branch-sandbox/.ci.sh b/branch-sandbox/.ci.sh new file mode 100644 index 000000000..c0063f920 --- /dev/null +++ b/branch-sandbox/.ci.sh @@ -0,0 +1,262 @@ +shCiArtifactUploadCustom() {(set -e +# this function will custom-upload build-artifacts to branch-gh-pages + # .cache - restore + if [ -d .cache ] + then + cp -a .cache/* . # js-hack - */ + fi + # add jslint.js + cp jslint.mjs jslint.js + git add -f jslint.js + # seo - inline css-assets and invalidate cached-assets + node --input-type=module --eval ' +import moduleFs from "fs"; +(async function () { + let cacheKey = Math.random().toString(36).slice(-4); + let fileDict = {}; + await Promise.all([ + "index.html" + ].map(async function (file) { + try { + fileDict[file] = await moduleFs.promises.readFile(file, "utf8"); + } catch (ignore) { + process.exit(); + } + })); + // invalidate cached-assets + fileDict["index.html"] = fileDict["index.html"].replace(( + /\b(?:href|src)=".+?\.(?:css|js|mjs)\b/g + ), function (match0) { + return `${match0}?cc=${cacheKey}`; + }); + // write file + Object.entries(fileDict).map(function ([ + file, data + ]) { + moduleFs.promises.writeFile(file, data); + }); +}()); +' "$@" # ' + # screenshot quickstart + node --input-type=module --eval ' +import moduleFs from "fs"; +import moduleChildProcess from "child_process"; +(async function () { + let screenshotCurl; + screenshotCurl = await moduleFs.promises.stat("jslint.mjs"); + screenshotCurl = String(` +echo "\ +% Total % Received % Xferd Average Speed Time Time Time Current + Dload Upload Total Spent Left Speed +100 250k 100 250k 0 0 250k 0 0:00:01 --:--:-- 0:00:01 250k\ +" + `).trim().replace(( + /250/g + ), Math.floor(screenshotCurl.size / 1024)); + // parallel-task - run-and-screenshot example-shell-commands in README.md + await Promise.all(Array.from(String( + await moduleFs.promises.readFile("README.md", "utf8") + ).matchAll( + /\n```shell \n([\S\s]*?\n)```\n/g + )).map(async function ([ + ignore, file, script0 + ]) { + let script = script0; + // modify script - jslint install + script = script.replace( + "curl -L https://www.jslint.com/jslint.mjs > jslint.mjs", + screenshotCurl + ); + // modify script - cd node-sqlite3 + script = script.replace(( + /\n\ncd node-sqlite3-\w*?\n/g + ), ( + " 2>/dev/null || true\n" + + "$&\n" + + "git checkout 60a022c511a37788e652c271af23174566a80c30\n" + )); + // printf script + script = ( + "(set -e\n" + + "printf \u0027" + + script0.trim().replace(( + /[%\\]/gm + ), "$&$&").replace(( + /\u0027/g + ), "\u0027\"\u0027\"\u0027").replace(( + /^/gm + ), "> ") + + "\n\n\n\u0027\n" + + script + + ")\n" + ); + await moduleFs.promises.writeFile(file + ".sh", script); + await new Promise(function (resolve) { + moduleChildProcess.spawn( + "sh", + [ + "jslint_ci.sh", "shRunWithScreenshotTxt", file, + "sh", file + ".sh" + ], + { + env: Object.assign({ + // limit stdout to xxx lines + SH_RUN_WITH_SCREENSHOT_TXT_MAX_LINES: 64 + }, process.env), + stdio: [ + "ignore", 1, 2 + ] + } + ).on("exit", resolve); + }); + })); +}()); +' "$@" # ' + # screenshot asset_image_logo + shImageLogoCreate & + # screenshot html + node --input-type=module --eval ' +import moduleChildProcess from "child_process"; +(async function () { + await Promise.all([ + ( + "https://" + + process.env.UPSTREAM_OWNER + + ".github.io/" + + process.env.UPSTREAM_REPO + + "/branch-beta/index.html" + ), + ".artifact/apidoc.html", + ".artifact/coverage_sqlite3_js/index.html", + ".artifact/coverage_sqlite3_js/lib/sqlite3.js.html", + ".artifact/coverage_sqlite3_sh/index.html", + ".artifact/coverage_sqlite3_sh/lib/sqlite3.js.html", + ".artifact/jslint_report_hello.html" + ].map(async function (url) { + await new Promise(function (resolve) { + moduleChildProcess.spawn( + "sh", + [ + "jslint_ci.sh", "shBrowserScreenshot", url + ], + { + stdio: [ + "ignore", 1, 2 + ] + } + ).on("exit", resolve); + }); + })); +}()); +' "$@" # ' + # remove bloated json-coverage-files + rm .artifact/coverage/*.json # js-hack - */ + rm .artifact/coverage_sqlite3_*/*.json # js-hack - */ + # .cache - save + if [ ! -d .cache ] + then + mkdir .cache + cp -a node-sqlite3-* .cache + fi +)} + +shCiBaseCustom() {(set -e +# this function will run base-ci + # update files + if [ "$(git branch --show-current)" = alpha ] + then + node --input-type=module --eval ' +import jslint from "./jslint.mjs"; +import moduleFs from "fs"; +(async function () { + let fileDict = {}; + let fileModified; + let versionBeta; + let versionMaster; + await Promise.all([ + "CHANGELOG.md", + "index.html", + "jslint.mjs", + "jslint_ci.sh" + ].map(async function (file) { + fileDict[file] = await moduleFs.promises.readFile(file, "utf8"); + })); + Array.from(fileDict["CHANGELOG.md"].matchAll( + /\n\n# (v\d\d\d\d\.\d\d?\.\d\d?(.*?)?)\n/g + )).slice(0, 2).forEach(function ([ + ignore, version, isBeta + ]) { + versionBeta = versionBeta || version; + versionMaster = versionMaster || (!isBeta && version); + }); + await Promise.all([ + { + file: "index.html", + src: fileDict["index.html"].replace(( + /\n + + +
    JSLint
    + +
    Help
    +
    +
    Il semble que la perfection soit atteinte non quand il n’y a + plus rien à ajouter, mais quand il n’y a plus rien à + retrancher.
    +
    + Antoine de Saint-Exupéry +
    +
    + Terre des Hommes (1939) +
    +

    Good Parts

    +

    The Principle of the Good Parts is

    +
    If a feature is sometimes useful and sometimes dangerous and if + there is a better option then always use the better option.
    +

    JSLint is a JavaScript program that looks for problems in + JavaScript programs. It is a code quality tool.

    +

    When C was + a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    +

    As the language matured, the definition of the language was + strengthened to eliminate some insecurities, and compilers got better + at issuing warnings. lint is no longer needed.

    +

    JavaScript is a + young-for-its-age language. It was originally intended to do small tasks in + webpages, tasks for which Java was too heavy and clumsy. But JavaScript is + a surprisingly capable language, and it is now being used in larger + projects. Many of the features that were intended to make the language easy + to use are troublesome when projects become complicated. A lint + for JavaScript is needed: JSLint, a JavaScript syntax checker + and validator.

    +

    JSLint takes a JavaScript source and scans it. If it finds a + problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by the ECMAScript Programming Language + Standard (the strangely named document that governs JavaScript). + JSLint will reject most legal programs. It is a higher + standard.

    +

    JavaScript is a sloppy language, but hidden deep inside there is an elegant, + better language. JSLint helps you to program in that better + language and to avoid most of the slop. JSLint will reject + programs that browsers will accept because JSLint is concerned + with the quality of your code and browsers are not. You should gladly accept + all of JSLint's advice.

    +

    JSLint can operate on JavaScript source + or JSON text.

    +

    ECMAScript Sixth Edition

    +

    Some of + ES6’s features are good, so JSLint will recognize the good + parts of ES6.

    +

    Currently, these features are recognized:

    +
      +
    • The ... ellipsis marker in parameter + lists and argument lists, replacing the arguments object + for variadic functions.
    • +
    • The let statement, which is like the var + statement except that it respects block scope. + You may use let or var but not both.
    • +
    • The const statement is like the let statement + except that it disallows the use of assignment on the variable, although + if the value of the variable is mutable, it can still be mutated. + const is preferred to let. +
    • Destructuring of arrays and objects is allowed in parameter lists and on + the left side of let, and const, but not + var or assignment statements, and not deep destructuring + or eliding.
    • +
    • Enhanced object literals, providing shorter forms for function + declaration and properties that are initialized by variables with the + same name.
    • +
    • The fat arrow => fart functions.
    • +
    • The simplest forms of import and export.
    • +
    • `Megastring` literals, but not nested + `megastring` literals.
    • +
    • New global functions, such as Map, Set, + WeakMap, and WeakSet.
    • +
    • 0b- and 0o- number literals.
    • +
    +

    The most important new feature of ES6 is proper tail calls. This has no + new syntax, so JSLint doesn’t see it. But it makes recursion + much more attractive, which makes loops, particularly for + loops, much less attractive.

    +

    import export

    +

    The ES6 module feature will be an important improvement over JavaScript’s + global variables as a means of linking separate files together. + JSLint recognizes a small but essential subset of the module + syntax.

    +
    +

    import name from stringliteral;

    +

    import {name} from stringliteral;

    +

    import(string).then(function);

    +

    export default function () {};

    +

    export default function name() {};

    +

    export default expression;

    +

    export function name() {}

    +

    export name; // where name is const

    +

    export {name};

    +
    +

    Directives

    +

    JSLint provides three directives that may be placed in a + file to manage JSLint’s behavior. Use of these directives is + optional. If they are used, they should be placed in a source file before + the first statement. They are written in the form of a comment, where the + directive name is placed immediately after the opening of the comment before + any whitespace. The three directives are global, + jslint, and property. Directives in a file are + stronger than options selected from the UI or passed with the option object.

    +

    /*global*/

    +

    The /*global*/ directive is used to specify a set of globals + (usually functions and objects containing functions) that are available to + this file. This was commonly used in browsers to link source files together + before ES6 modules appeared. Use of global variables is strongly + discouraged, but unfortunately web browsers require their use. The + /*global*/ directive can only be used when the + Assume a browser option is selected. +

    Each of the names listed will indicate a read-only global variable. The names + are separated by , comma. This directive + should not be used if import or export is used. + This directive inhibits warnings, but it does not declare the names in the + execution environment. +

    For example: +

    /*global +
    ADSAFE, report, jslint
    +*/
    +

    instructs JSLint to not give warnings about the global + variables ADsafe, report, and jslint. + However, if any of those names are expected to be supplied by other files + and those other files fail to do so, then execution errors will result. It + is usually better to use top-level variable declarations instead: +

        var ADSAFE;
    +    var report;
    +    var jslint; 
    +

    Using var in this way allows comparing a global variable to the + undefined value to determine whether it is has been used in the global context.

    +

    /*jslint*/

    +

    The /*jslint*/ directive allows for the control of several + options. These options can also be set from the + jslint.com user interface.

    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Tolerate bitwise operatorsbitwisetrue if bitwise operators should be allowed. The + bitwise operators are rarely used in JavaScript programs, so it + is usually more likely that & is a mistyping of + && than that it indicates a bitwise and. + JSLint will give warnings on the bitwise operators + unless this option is selected.
    Assume a browser browsertrue if the standard browser globals should be + predefined. This option will reject the use of + import and export. This option also + disallows the file form of the "use + strict" pragma. It does not supply + self; you will have to request that unnecessary + alias of the dreaded global object yourself. It adds the same + globals as this directive: +
    /*global +
    + clearInterval, clearTimeout, document, event, FileReader, + FormData, history, localStorage, location, name, navigator, + screen, sessionStorage, setInterval, setTimeout, Storage, + URL, window, XMLHttpRequest +
    + */
    +
    Tolerate conversion operatorsconverttrue if !!, + prefix, + and concatenation with "" are allowed. + The Boolean, Number, and + String functions are preferred
    Assume CouchDBcouchtrue if + Couch DB + globals should be predefined. It adds the same globals as this + directive: +
    /*global +
    emit, getRow, isArray, log, provides, registerType, require, + send, start, sum, toJSON
    + */
    Assume in developmentdeveltrue if browser globals that are useful in + development should be predefined, and if debugger + statements and TODO comments + should be allowed. It adds the + same globals as this directive: +
    /*global +
    alert, confirm, console, prompt
    + */
    Be sure to turn this option off before going + into production.
    Tolerate evalevaltrue if eval should be allowed. In the + past, the eval function was the most misused + feature of the language.
    Tolerate for statementfortrue if the for statement should be + allowed. It is almost always better to use the array methods + instead.
    Tolerate get and setgetsettrue if accessor properties are allowed in object literals.
    Tolerate long source lineslongtrue if a line can contain more than 80 characters.
    Tolerate bad property namesname + true if bad property names like + $, _foo, fooSync, foo_ + are allowed. +
    Assume Node.jsnodetrue if Node.js globals should be predefined. It + will predefine globals that are used in the Node.js environment. + It adds the same globals as this directive: +
    /*global +
    + Buffer, clearImmediate, clearInterval, clearTimeout, + console, exports, module, process, require, + setImmediate, setInterval, setTimeout, TextDecoder, + TextEncoder, URL, URLSearchParams, __dirname, __filename +
    + */
    Tolerate single quote stringssingletrue if 'single quote + should be allowed to enclose string literals.
    Tolerate this
    thistrue if this should be allowed.
    Tolerate unordered case-statements, params, properties.unorderedtrue if objects and functions are allowed to declare properties and params in non-alphabetical order.
    Tolerate whitespace messwhitetrue if the whitespace rules should be ignored.
    +
    + +

    For example:

    +
    /*jslint
    +    bitwise, node
    +*/
    + +

    /*property*/

    +

    The /*property*/ directive is used to declare a list of + property identifiers that are used in the file. Each property name in the + program is looked up in this list. If a name is not found, that indicates an + error, most likely a typing error.

    +

    The list can also be used to evade some of JSLint’s naming + rules.

    +

    JSLint can build the /*property*/ list for you. At + the bottom of its report, JSLint displays a + /*property*/ directive. It contains all of the names that were + used with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. You + can copy the /*property*/ directive to the top of your + script file. JSLint will check the spelling of all property + names against the list. That way, you can have JSLint look for + misspellings for you.

    +

    For example,

    +
    /*property +
    charAt, slice, _$_
    + */
    +

    ignore

    +

    JSLint introduces a new reserved word: ignore. It + is used in parameter lists and in catch clauses to indicate a + parameter that will be ignored. Unused warnings will not be produced for + ignore. +

    function handler(ignore, value) {
    +    return do_something_useful(value);
    +}
    +

    var let const

    +

    JavaScript provides three statements for declaring variables: + var, let, and const. The + var statement suffers from a bad practice called hoisting. The + let statement does not do hoisting and respects block scope. + The const statement is like let except that it + marks the variable (but not its contents) as read only, making it an error + to attempt to assign to the variable. When given a choice, + const is the best, var is the worst.

    +

    JSLint uses the intersection of the var rules and the + let rules, and by doing so avoids the errors related to either. + A name should be declared only once in a function. It should be declared + before it is used. It should not be used outside of the block in which it is + declared. A variable should not have the same name as a variable or + parameter in an outer function. Do not mix var and + let. Declare one name per statement.

    +

    = == ===

    +

    Fortran made a terrible + mistake in using the equality operator as its assignment operator. That + mistake has been replicated in most languages since then. C compounded that + mistake by making == its equality operator. The visual + similarity is a source of errors. JavaScript compounded this further by + making == a type coercing comparison operator that produces + false positive results. This was mitigated by adding the === + operator, leaving the broken == operator in place.

    +

    JSLint attempts to minimize errors by the following rules:

    +

    == is not allowed. This avoids the false positives, and + increases the visual distance between = and ===. + Assignments are not allowed in expression position, and comparisons are not + allowed in statement position. This also reduces confusion. 

    +

    Semicolon

    +

    JavaScript uses a C-like syntax that requires the use of semicolons to + delimit certain statements. JavaScript attempts to make those semicolons + optional with an automatic semicolon insertion mechanism, but it does not + work very well. Automatic semicolon insertion was added to make + things easier for beginners. Unfortunately, it sometimes fails. Do not rely + on it unless you are a beginner.

    +

    JSLint expects that every statement will be followed by + ; except for for, function, + if, switch, try, and + while. JSLint does not expect to see unnecessary + semicolons, the empty statement, or empty blocks.

    +

    function =>

    +

    JavaScript has four syntactic forms for making function objects: function + statements, function expressions, enhanced object literals, and the + => fart operator.

    +

    The function statement creates a variable and assigns the function object to + it. It should be used in a file or function body, but not inside of a block.

    +
    function name(parameters) { +
    statements
    + }
    +

    The function expression unfortunately looks like the function statement. It + may appear anywhere that an expression may appear, but not in statement + position and not in a loop. It produces a function object but does not + create a variable in which to store it.

    +
    function (parameters) { +
    statements
    + }
    +

    The enhanced object literal provides an ES6 shorthand for creating a property + whose value is a function, saving you from having to type + : colon and function.

    +
    { +
    name(parameters) { +
    statements
    + }
    + }
    +

    Finally, ES6 provides an even shorter form of function expression that leaves + out the words function and return:

    +
    (parameters) => + expression
    +

    JSLint requires the parens around the parameters, and forbids a + { left brace after the + => fart to avoid syntactic ambiguity.

    +

    Comma

    +

    The , comma operator is unnecessary and can + mask programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as + an operator. It does not expect to see elided elements in array literals. A + comma should not appear after the last element of an array literal or object + literal.

    +

    Blocks

    +

    JSLint expects blocks with function, + if, switch, while, for, + do, and try statements and nowhere else.

    +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    +

    JavaScript allows an if to be written like this:

    +
    if (condition)
    +    statement;
    +

    That form is known to contribute to mistakes in projects where many + programmers are working on the same code. That is why JSLint + expects the use of a block:

    +
    if (condition) {
    +    statements;
    +}
    +

    Experience shows that this form is more resilient.

    +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call. All other expression statements are considered + to be errors.

    +

    for

    +

    JSLint does not recommend use of the for statement. + Use array methods like forEach instead. The for + option will suppress some warnings. The forms of for that + JSLint accepts are restricted, excluding the new ES6 forms.

    +

    for in

    +

    JSLint does not recommend use of the for + in statement. Use Object.keys instead.

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, it also + loops through all of the properties that were inherited through the + prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written + without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    for (name in object) {
    +    if (object.hasOwnProperty(name)) {
    +        ....
    +    }
    +}
    +

    Note that the above code will fail if the object contains a property + named hasOwnProperty. Use Object.keys instead.

    +

    switch

    +

    A common error in switch statements is to forget to place a + break statement after each case, resulting in unintended + fall-through. JSLint expects that the statement before the next + case or default is one of these: + break, return, or throw.

    +

    with

    +

    The with statement was intended to provide a shorthand in + accessing properties in deeply nested objects. Unfortunately, it behaves + very badly when setting new properties. Never use the with + statement.

    +

    JSLint does not expect to see a with statement.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that + labels will be distinct from vars and parameters.

    +

    Unreachable Code

    +

    JSLint expects that a return, break, + or throw statement will be followed by a + } right brace or case or + default.

    +

    Confusing Pluses and Minuses

    +

    JSLint expects that + will not be followed by + + or ++, and that - will not be + followed by - or --. A misplaced space can turn + + + into ++, an error that is difficult to see. + Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ increment and + -- decrement operators have been known to + contribute to bad code by encouraging excessive trickiness. They are second + only to faulty architecture in enabling to viruses and other security + menaces. Also, preincrement/postincrement confusion can produce off-by-one + errors that are extremely difficult to diagnose. Fortunately, they are also + complete unnecessary. There are better ways to add 1 to a variable.

    +

    It is best to avoid these operators entirely and rely on += and + -= instead.

    +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. + JSLint looks for problems that may cause portability problems. + It also attempts to resolve visual ambiguities by recommending explicit + escapement.

    +

    JavaScript’s syntax for regular expression literals overloads the + / slash character. To avoid ambiguity, + JSLint expects that the character preceding a regular + expression literal is a ( left paren or + = equal or + : colon or + , comma character.

    +

    this

    +
    Having this in the language makes it harder to talk + about the language. It is like pair programming with Abbott and Costello. +
    +

    Avoid using this. Warnings about this can be + suppressed with option.this.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the + new prefix. The new prefix creates a new object + based on the function's prototype, and binds that object to the + function's implied this parameter. If you neglect to use the + new prefix, no new object will be made and this + will be bound to the global object.

    +

    JSLint enforces the convention that constructor functions be + given names with initial uppercase. JSLint does not expect to + see a function invocation with an initial uppercase name unless it has the + new prefix. JSLint does not expect to see the + new prefix used with functions whose names do not start with + initial uppercase.

    +

    JSLint does not expect to see the wrapper forms + new Number, new String, new Boolean.

    +

    JSLint does not expect to see new Object. + Use Object.create(null) instead.

    +

    Whitespace

    +

    JSLint has a specific set of rules about the use of whitespace. + Where possible, these rules are consistent with centuries of good practice + with literary style.

    +

    The indentation increases by 4 spaces when the last token on a line is + { left brace, + [ left bracket, + ( left paren. The matching closing + token will be the first token on a line, restoring the previous + indentation.

    +

    The ternary operator can be visually confusing, so + ? question mark and + : colon always begin a line and increase + the indentation by 4 spaces.

    +
    return (
    +    (the_token.id === "(string)" || the_token.id === "(number)")
    +    ? String(the_token.value)
    +    : the_token.id
    +);
    +

    The word function is always followed with one space.

    +

    Clauses (case, catch, default, + else, finally) are not statements and so should + not be indented like statements.

    +

    Spaces are used to make things that are not invocations look less like + invocations.

    +

    Tabs and spaces should not be mixed. We should pick just one in order to + avoid the problems that come from having both. Personal preference is an + extremely unreliable criterion. Neither offers a powerful advantage over the + other. Fifty years ago, tab had the advantage of consuming less memory, but + Moore's Law has eliminated that advantage. Space has one clear advantage + over tab: there is no reliable standard for how many spaces a tab + represents, but it is universally accepted that a space occupies a space. So + use spaces. You can edit with tabs if you must, but make sure it is spaces + again before you commit. Maybe someday we will finally get a universal + standard for tabs, but until that day comes, the better choice is spaces.

    +

    Report

    +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    +
      +
    • The line number on which it starts.
    • +
    • The function’s name. In the case of anonymous functions, + JSLint will attempt to + «guess» the name.
    • +
    • The parameters.
    • +
    • Variables: The variables that are declared in the function.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Exceptions: The variables that are declared by catch + clauses of try statements.
    • +
    • Outer: Variables used by this function that are declared in + other functions.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements.

    +

    Try it

    +

    Try it. Paste your script + into the window and click the + + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    JSLint is written entirely in JavaScript, so it can run + anywhere that JavaScript (or even Java) can run.

    +

    Perfectly fine

    +

    JSLint was designed to reject code that some would consider to + be perfectly fine. The reason for this is that JSLint's + purpose is to help produce programs that are free of error. That is + difficult in any language and is especially hard in JavaScript. + JSLint attempts to help you increase the visual distance + between correct programs and incorrect programs, making the remaining errors + more obvious. JSLint will give warnings about things that are + not necessarily wrong in the current situation, but which have been observed + to mask or obscure errors. Avoid those things when there are better options + available.

    +

    It is dangerous out there. JSLint is here to help.

    +

    Warning

    +

    JSLint will hurt your feelings. Side effects may include + headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, + dry mouth, cleaner code, and a reduced error rate.

    + +

    JSLint

    +

    JSLint is delivered as a set of files:

    +
    + + + + + + + +
    FilenameContent
    help.htmlUsing jslint as a single page JSLint application.
    index.htmlSingle page JSLint application that uses the + js files.
    jslint.mjsThe jslint function.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    +
    + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring"(JSLint)"
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    parenttokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    parenttokenThe function in which the variable was declared.variable
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    shebangstringThe first line of text if it started with #!line
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable may be assigned to.variable
    +

    Reports

    +

    The report object contains three functions that all take a + result from the jslint function as input. + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    +
    + +

    Further Reading

    +
    +

    + Code Conventions for the JavaScript Programming Language.

    + + + +
    + + diff --git a/branch-sandbox/index.html b/branch-sandbox/index.html new file mode 100644 index 000000000..13c68bc7c --- /dev/null +++ b/branch-sandbox/index.html @@ -0,0 +1,1563 @@ + + + + + + + + +JSLint: The JavaScript Code Quality and Coverage Tool + + + + + + + + + + + + +
    +
    Loading modules
    +   + + . + . + . + +
    +
    + +
    + Source + +
    +
    + + + +
    +
    + Options +
    +
    + Env... +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    + Allow... +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
      +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
      +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
      +
    + +
    +
    + +
    +
    +
    +
    + + + diff --git a/branch-sandbox/jslint.cjs b/branch-sandbox/jslint.cjs new file mode 100644 index 000000000..b0cf5a68a --- /dev/null +++ b/branch-sandbox/jslint.cjs @@ -0,0 +1,14 @@ +/*jslint beta, node*/ +/*property + readFileSync, replace, runInNewContext +*/ +require("vm").runInNewContext( + require("fs").readFileSync(__dirname + "/jslint.mjs", "utf8").replace( + "\nexport default Object.freeze(jslint_export);", + "\nexports = jslint_export;" + ).replace( + "\njslint_import_meta_url = import.meta.url;", + "\n// jslint_import_meta_url = import.meta.url;" + ), + module +); diff --git a/branch-sandbox/jslint.js b/branch-sandbox/jslint.js new file mode 100644 index 000000000..944889fe4 --- /dev/null +++ b/branch-sandbox/jslint.js @@ -0,0 +1,11004 @@ +// #!/usr/bin/env node +// JSLint +// Original Author: Douglas Crockford (https://www.jslint.com). + +// This is free and unencumbered software released into the public domain. + +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. + +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +// For more information, please refer to + + +// jslint(source, option_dict, global_list) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze. +// option_dict An object whose keys correspond to option names. +// global_list An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// PHASE 1. Split by newlines into . +// PHASE 2. Lex into . +// PHASE 3. Parse into using the Pratt-parser. +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// PHASE 5. Check whitespace between tokens in . + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*jslint beta, node*/ + +/*property + mode_conditional, + JSLINT_BETA, NODE_V8_COVERAGE, a, all, argv, arity, artifact, + assertErrorThrownAsync, assertJsonEqual, assertOrThrow, assign, async, b, + beta, bitwise, block, body, browser, c, calls, catch, catch_list, + catch_stack, causes, char, children, clear, closer, + closure, code, column, concat, consoleError, console_error, console_log, + constant, context, convert, count, coverageDir, create, cwd, d, dead, + debugInline, default, delta, devel, directive, directive_list, + directive_quiet, directives, dirname, disrupt, dot, edition, elem_list, + ellipsis, else, end, endOffset, endsWith, entries, env, error, eval, every, + example_list, exec, execArgv, exit, export_dict, exports, expression, extra, + file, fileList, fileURLToPath, filter, finally, flag, floor, for, forEach, + formatted_message, free, freeze, from, froms, + fsWriteFileWithParents, fud, functionName, function_list, function_stack, + functions, get, getset, github_repo, global, global_dict, global_list, + holeList, htmlEscape, id, identifier, import, import_list, inc, indent2, + index, indexOf, init, initial, isArray, isBlockCoverage, isHole, isNaN, + is_equal, is_weird, join, jslint, jslint_apidoc, jslint_assert, + jslint_charset_ascii, jslint_cli, jslint_edition, jslint_phase1_split, + jslint_phase2_lex, jslint_phase3_parse, jslint_phase4_walk, + jslint_phase5_whitage, jslint_report, json, jstestDescribe, jstestIt, + jstestOnExit, keys, label, lbp, led, length, level, line, lineList, + line_list, line_offset, line_source, lines, linesCovered, linesTotal, live, + log, long, loop, m, main, map, margin, match, max, message, meta, min, + mkdir, modeCoverageIgnoreFile, modeIndex, mode_cli, mode_json, mode_module, + mode_noop, mode_property, mode_shebang, mode_stop, module, moduleFsInit, + moduleName, module_list, name, names, node, noop, now, + nr, nud, objectDeepCopyWithKeysSorted, ok, on, open, opening, option, + option_dict, order, package_name, padEnd, padStart, parameters, parent, + parentIi, parse, pathname, platform, pop, processArgv, process_argv, + process_env, process_exit, process_version, promises, property, + property_dict, push, quote, ranges, readFile, readdir, readonly, recursive, + reduce, repeat, replace, resolve, result, reverse, rm, rmdir, role, round, + scriptId, search, set, shebang, shift, signature, single, slice, some, sort, + source, spawn, splice, split, stack, stack_trace, start, startOffset, + startsWith, statement, statement_prv, stdio, stop, stop_at, stringify, + switch, syntax_dict, tenure, test, test_cause, test_internal_error, this, + thru, toString, token, token_global, token_list, token_nxt, token_tree, + tokens, trace, tree, trim, trimEnd, trimRight, try, type, unlink, unordered, + unshift, url, used, v8CoverageListMerge, v8CoverageReportCreate, value, + variable, version, versions, warn, warn_at, warning, warning_list, warnings, + white, wrapped, writeFile +*/ + +// init debugInline +let debugInline = (function () { + let consoleError = function () { + return; + }; + function debug(...argv) { + +// This function will print to stderr and then return [0]. + + consoleError("\n\ndebugInline"); + consoleError(...argv); + consoleError("\n"); + return argv[0]; + } + debug(); // Coverage-hack. + consoleError = console.error; + return debug; +}()); +let jslint_charset_ascii = ( + "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007" + + "\b\t\n\u000b\f\r\u000e\u000f" + + "\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017" + + "\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f" + + " !\"#$%&'()*+,-./0123456789:;<=>?" + + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + + "`abcdefghijklmnopqrstuvwxyz{|}~\u007f" +); +let jslint_edition = "v2021.12.20"; +let jslint_export; // The jslint object to be exported. +let jslint_fudge = 1; // Fudge starting line and starting + // ... column to 1. +let jslint_import_meta_url = ""; // import.meta.url used by cli. +let jstestCountFailed = 0; +let jstestCountTotal = 0; +let jstestItCount = 0; +let jstestItList = []; +let jstestTimeStart; +let moduleChildProcess; +let moduleFs; +let moduleFsInitResolveList; +let modulePath; +let moduleUrl; + +async function assertErrorThrownAsync(asyncFunc, regexp) { + +// This function will assert calling throws an error. + + let err; + try { + await asyncFunc(); + } catch (errCaught) { + err = errCaught; + } + assertOrThrow(err, "No error thrown."); + assertOrThrow( + regexp === undefined || new RegExp(regexp).test(err.message), + err + ); +} + +function assertJsonEqual(aa, bb, message) { + +// This function will assert JSON.stringify() === JSON.stringify(). + + aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa), undefined, 1); + bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb), undefined, 1); + if (aa !== bb) { + throw new Error( + "\n" + aa + "\n!==\n" + bb + + ( + typeof message === "string" + ? " - " + message + : message + ? " - " + JSON.stringify(message) + : "" + ) + ); + } +} + +function assertOrThrow(condition, message) { + +// This function will throw if is falsy. + + if (!condition) { + throw ( + (!message || typeof message === "string") + ? new Error(String(message).slice(0, 2048)) + : message + ); + } +} + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than '{}' because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +async function fsWriteFileWithParents(pathname, data) { + +// This function will write to and lazy-mkdirp if necessary. + + await moduleFsInit(); + +// Try writing to pathname. + + try { + await moduleFs.promises.writeFile(pathname, data); + } catch (ignore) { + +// Lazy mkdirp. + + await moduleFs.promises.mkdir(modulePath.dirname(pathname), { + recursive: true + }); + +// Retry writing to pathname. + + await moduleFs.promises.writeFile(pathname, data); + } + console.error("wrote file " + pathname); +} + +function htmlEscape(str) { + +// This function will make html-safe by escaping & < >. + + return String(str).replace(( + /&/g + ), "&").replace(( + //g + ), ">"); +} + +function jslint( + source = "", // A text to analyze. + option_dict = empty(), // An object whose keys correspond to option + // ... names. + global_list = [] // An array of strings containing global + // ... variables that the file is allowed + // ... readonly access. +) { + +// The jslint function itself. + + let catch_list = []; // The array containing all catch-blocks. + let catch_stack = [ // The stack of catch-blocks. + { + context: empty() + } + ]; + let cause_dict = empty(); // The object of test-causes. + let directive_list = []; // The directive comments. + let export_dict = empty(); // The exported names and values. + let function_list = []; // The array containing all functions. + let function_stack = []; // The stack of functions. + let global_dict = empty(); // The object containing the global + // ... declarations. + let import_list = []; // The array collecting all import-from strings. + let line_list = String( // The array containing source lines. + "\n" + source + ).split( + // rx_crlf + /\n|\r\n?/ + ).map(function (line_source) { + return { + line_source + }; + }); + let mode_stop = false; // true if JSLint cannot finish. + let property_dict = empty(); // The object containing the tallied + // ... property names. + let state = empty(); // jslint state-object to be passed between + // jslint functions. + let syntax_dict = empty(); // The object containing the parser. + let tenure = empty(); // The predefined property registry. + let token_global = { // The global object; the outermost context. + async: 0, + body: true, + context: empty(), + finally: 0, + from: 0, + id: "(global)", + level: 0, + line: jslint_fudge, + live: [], + loop: 0, + switch: 0, + thru: 0, + try: 0 + }; + let token_list = []; // The array of tokens. + let warning_list = []; // The array collecting all generated warnings. + +// Error reportage functions: + + function artifact(the_token) { + +// Return a string representing an artifact. + + the_token = the_token || state.token_nxt; + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); + } + + function is_equal(aa, bb) { + let aa_value; + let bb_value; + +// test_cause: +// ["0&&0", "is_equal", "", "", 0] + + test_cause(""); + +// Probably deadcode. +// if (aa === bb) { +// return true; +// } + + jslint_assert(!(aa === bb), `Expected !(aa === bb).`); + if (Array.isArray(aa)) { + return ( + Array.isArray(bb) + && aa.length === bb.length + && aa.every(function (value, index) { + +// test_cause: +// ["`${0}`&&`${0}`", "is_equal", "recurse_isArray", "", 0] + + test_cause("recurse_isArray"); + return is_equal(value, bb[index]); + }) + ); + } + +// Probably deadcode. +// if (Array.isArray(bb)) { +// return false; +// } + + jslint_assert(!Array.isArray(bb), `Expected !Array.isArray(bb).`); + if (aa.id === "(number)" && bb.id === "(number)") { + return aa.value === bb.value; + } + if (aa.id === "(string)") { + aa_value = aa.value; + } else if (aa.id === "`" && aa.constant) { + aa_value = aa.value[0]; + } + if (bb.id === "(string)") { + bb_value = bb.value; + } else if (bb.id === "`" && bb.constant) { + bb_value = bb.value[0]; + } + if (typeof aa_value === "string") { + return aa_value === bb_value; + } + if (is_weird(aa) || is_weird(bb)) { + +// test_cause: +// ["aa(/./)||{}", "is_equal", "false", "", 0] + + test_cause("false"); + return false; + } + if (aa.arity === bb.arity && aa.id === bb.id) { + if (aa.id === ".") { + +// test_cause: +// ["aa.bb&&aa.bb", "is_equal", "recurse_arity_id", "", 0] + + test_cause("recurse_arity_id"); + return ( + is_equal(aa.expression, bb.expression) + && is_equal(aa.name, bb.name) + ); + } + if (aa.arity === "unary") { + +// test_cause: +// ["+0&&+0", "is_equal", "recurse_unary", "", 0] + + test_cause("recurse_unary"); + return is_equal(aa.expression, bb.expression); + } + if (aa.arity === "binary") { + +// test_cause: +// ["aa[0]&&aa[0]", "is_equal", "recurse_binary", "", 0] + + test_cause("recurse_binary"); + return ( + aa.id !== "(" + && is_equal(aa.expression[0], bb.expression[0]) + && is_equal(aa.expression[1], bb.expression[1]) + ); + } + if (aa.arity === "ternary") { + +// test_cause: +// ["aa=(``?``:``)&&(``?``:``)", "is_equal", "recurse_ternary", "", 0] + + test_cause("recurse_ternary"); + return ( + is_equal(aa.expression[0], bb.expression[0]) + && is_equal(aa.expression[1], bb.expression[1]) + && is_equal(aa.expression[2], bb.expression[2]) + ); + } + +// Probably deadcode. +// if (aa.arity === "function" || aa.arity === "regexp") { +// return false; +// } + + jslint_assert( + !(aa.arity === "function" || aa.arity === "regexp"), + `Expected !(aa.arity === "function" || aa.arity === "regexp").` + ); + +// test_cause: +// ["undefined&&undefined", "is_equal", "true", "", 0] + + test_cause("true"); + return true; + } + +// test_cause: +// ["null&&undefined", "is_equal", "false", "", 0] + + test_cause("false"); + return false; + } + + function is_weird(thing) { + switch (thing.id) { + case "(regexp)": + return true; + case "=>": + return true; + case "[": + return thing.arity === "unary"; + case "function": + return true; + case "{": + return true; + default: + return false; + } + } + + function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + the_token = the_token || state.token_nxt; + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); + } + + function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); + } + + function test_cause(code, aa, column) { + +// This function will instrument to for test-purposes. + + if (option_dict.test_cause) { + cause_dict[JSON.stringify([ + String(new Error().stack).replace(( + /^ at (?:file|stop|stop_at|test_cause|warn|warn_at)\b.*?\n/gm + ), "").match( + /\n at ((?:Object\.\w+?_)?\w+?) / + )[1].replace(( + /^Object\./ + ), ""), + code, + String( + (aa === undefined || aa === token_global) + ? "" + : aa + ), + column || 0 + ])] = true; + } + } + + function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + let the_warning; + the_token = the_token || state.token_nxt; + the_warning = warn_at( + code, + the_token.line, + (the_token.from || 0) + jslint_fudge, + a || artifact(the_token), + b, + c, + d + ); + if (the_token.warning === undefined) { + the_token.warning = the_warning; + } else { + warning_list.pop(); + } + return the_warning; + } + + function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + let mm; + let warning = Object.assign(empty(), { + a, + b, + c, + code, + +// Fudge column numbers in warning message. + + column: column || jslint_fudge, + d, + line, + line_source: "", + name: "JSLintError" + }, line_list[line]); + warning.column = Math.max( + Math.min(warning.column, warning.line_source.length), + jslint_fudge + ); + test_cause(code, b || a, warning.column); + switch (code) { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + case "and": + mm = `The '&&' subexpression should be wrapped in parens.`; + break; + case "bad_assignment_a": + mm = `Bad assignment to '${a}'.`; + break; + case "bad_directive_a": + mm = `Bad directive '${a}'.`; + break; + case "bad_get": + mm = `A get function takes no parameters.`; + break; + case "bad_module_name_a": + mm = `Bad module name '${a}'.`; + break; + case "bad_option_a": + mm = `Bad option '${a}'.`; + break; + case "bad_set": + mm = `A set function takes one parameter.`; + break; + case "duplicate_a": + mm = `Duplicate '${a}'.`; + break; + case "empty_block": + mm = `Empty block.`; + break; + case "expected_a": + mm = `Expected '${a}'.`; + break; + case "expected_a_at_b_c": + mm = `Expected '${a}' at column ${b}, not column ${c}.`; + break; + case "expected_a_b": + mm = `Expected '${a}' and instead saw '${b}'.`; + break; + case "expected_a_b_before_c_d": + mm = `Expected ${a} '${b}' to be ordered before ${c} '${d}'.`; + break; + case "expected_a_b_from_c_d": + mm = ( + `Expected '${a}' to match '${b}' from line ${c}` + + ` and instead saw '${d}'.` + ); + break; + case "expected_a_before_b": + mm = `Expected '${a}' before '${b}'.`; + break; + case "expected_digits_after_a": + mm = `Expected digits after '${a}'.`; + break; + case "expected_four_digits": + mm = `Expected four digits after '\\u'.`; + break; + case "expected_identifier_a": + mm = `Expected an identifier and instead saw '${a}'.`; + break; + case "expected_line_break_a_b": + mm = `Expected a line break between '${a}' and '${b}'.`; + break; + case "expected_regexp_factor_a": + mm = `Expected a regexp factor and instead saw '${a}'.`; + break; + case "expected_space_a_b": + mm = `Expected one space between '${a}' and '${b}'.`; + break; + case "expected_statements_a": + mm = `Expected statements before '${a}'.`; + break; + case "expected_string_a": + mm = `Expected a string and instead saw '${a}'.`; + break; + case "expected_type_string_a": + mm = `Expected a type string and instead saw '${a}'.`; + break; + case "freeze_exports": + mm = ( + `Expected 'Object.freeze('. All export values should be frozen.` + ); + break; + +// PR-378 - Relax warning "function_in_loop". +// +// case "function_in_loop": +// mm = `Don't create functions within a loop.`; +// break; + case "infix_in": + mm = ( + `Unexpected 'in'. Compare with undefined,` + + ` or use the hasOwnProperty method instead.` + ); + break; + case "label_a": + mm = `'${a}' is a statement label.`; + break; + case "misplaced_a": + mm = `Place '${a}' at the outermost level.`; + break; + case "misplaced_directive_a": + mm = `Place the '/*${a}*/' directive before the first statement.`; + break; + case "missing_await_statement": + mm = `Expected await statement in async function.`; + break; + +// PR-347 - Disable warning "missing_browser". +// case "missing_browser": +// mm = `/*global*/ requires the Assume a browser option.`; +// break; + + case "missing_m": + mm = `Expected 'm' flag on a multiline regular expression.`; + break; + case "naked_block": + mm = `Naked block.`; + break; + case "nested_comment": + mm = `Nested comment.`; + break; + case "not_label_a": + mm = `'${a}' is not a label.`; + break; + case "number_isNaN": + mm = `Use Number.isNaN function to compare with NaN.`; + break; + case "out_of_scope_a": + mm = `'${a}' is out of scope.`; + break; + case "redefinition_a_b": + mm = `Redefinition of '${a}' from line ${b}.`; + break; + case "redefinition_global_a_b": + mm = `Redefinition of global ${a} variable '${b}'.`; + break; + case "required_a_optional_b": + mm = `Required parameter '${a}' after optional parameter '${b}'.`; + break; + case "reserved_a": + mm = `Reserved name '${a}'.`; + break; + case "subscript_a": + mm = `['${a}'] is better written in dot notation.`; + break; + case "todo_comment": + mm = `Unexpected TODO comment.`; + break; + case "too_long": + mm = `Line is longer than 80 characters.`; + break; + case "too_many_digits": + mm = `Too many digits.`; + break; + case "unclosed_comment": + mm = `Unclosed comment.`; + break; + case "unclosed_disable": + mm = ( + `Directive '/*jslint-disable*/' was not closed` + + ` with '/*jslint-enable*/'.` + ); + break; + case "unclosed_mega": + mm = `Unclosed mega literal.`; + break; + case "unclosed_string": + mm = `Unclosed string.`; + break; + case "undeclared_a": + mm = `Undeclared '${a}'.`; + break; + case "unexpected_a": + mm = `Unexpected '${a}'.`; + break; + case "unexpected_a_after_b": + mm = `Unexpected '${a}' after '${b}'.`; + break; + case "unexpected_a_before_b": + mm = `Unexpected '${a}' before '${b}'.`; + break; + case "unexpected_at_top_level_a": + mm = `Expected '${a}' to be in a function.`; + break; + case "unexpected_char_a": + mm = `Unexpected character '${a}'.`; + break; + case "unexpected_comment": + mm = `Unexpected comment.`; + break; + +// PR-347 - Disable warning "unexpected_directive_a". +// case "unexpected_directive_a": +// mm = `When using modules, don't use directive '/\u002a${a}'.`; +// break; + + case "unexpected_expression_a": + mm = `Unexpected expression '${a}' in statement position.`; + break; + case "unexpected_label_a": + mm = `Unexpected label '${a}'.`; + break; + case "unexpected_parens": + mm = `Don't wrap function literals in parens.`; + break; + case "unexpected_space_a_b": + mm = `Unexpected space between '${a}' and '${b}'.`; + break; + case "unexpected_statement_a": + mm = `Unexpected statement '${a}' in expression position.`; + break; + case "unexpected_trailing_space": + mm = `Unexpected trailing space.`; + break; + case "unexpected_typeof_a": + mm = ( + `Unexpected 'typeof'. Use '===' to compare directly with ${a}.` + ); + break; + case "uninitialized_a": + mm = `Uninitialized '${a}'.`; + break; + case "unopened_enable": + mm = ( + `Directive '/*jslint-enable*/' was not opened` + + ` with '/*jslint-disable*/'.` + ); + break; + case "unreachable_a": + mm = `Unreachable '${a}'.`; + break; + case "unregistered_property_a": + mm = `Unregistered property name '${a}'.`; + break; + case "unused_a": + mm = `Unused '${a}'.`; + break; + case "use_double": + mm = `Use double quotes, not single quotes.`; + break; + case "use_open": + mm = ( + `Wrap a ternary expression in parens,` + + ` with a line break after the left paren.` + ); + break; + case "use_spaces": + mm = `Use spaces, not tabs.`; + break; + case "var_on_top": + mm = `Move variable declaration to top of function or script.`; + break; + case "var_switch": + mm = `Don't declare variables in a switch.`; + break; + case "weird_condition_a": + mm = `Weird condition '${a}'.`; + break; + case "weird_expression_a": + mm = `Weird expression '${a}'.`; + break; + case "weird_loop": + mm = `Weird loop.`; + break; + case "weird_property_a": + mm = `Weird property name '${a}'.`; + break; + case "weird_relation_a": + mm = `Weird relation '${a}'.`; + break; + case "wrap_condition": + mm = `Wrap the condition in parens.`; + break; + case "wrap_immediate": + mm = ( + `Wrap an immediate function invocation in parentheses to assist` + + ` the reader in understanding that the expression is the` + + ` result of a function, and not the function itself.` + ); + break; + case "wrap_parameter": + mm = `Wrap the parameter in parens.`; + break; + case "wrap_regexp": + mm = `Wrap this regexp in parens to avoid confusion.`; + break; + case "wrap_unary": + mm = `Wrap the unary expression in parens.`; + break; + } + +// Validate mm. + + jslint_assert(mm, code); + warning.message = mm; + +// PR-242 - Include stack_trace for jslint to debug itself for errors. + + if (option_dict.trace) { + warning.stack_trace = new Error().stack; + } + if (warning.directive_quiet) { + +// test_cause: +// ["0 //jslint-quiet", "semicolon", "directive_quiet", "", 0] + + test_cause("directive_quiet"); + return warning; + } + warning_list.push(warning); + return warning; + } + + try { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + + option_dict = Object.assign(empty(), option_dict); + Object.assign(state, { + artifact, + catch_list, + catch_stack, + directive_list, + export_dict, + function_list, + function_stack, + global_dict, + global_list, + import_list, + is_equal, + is_weird, + line_list, + mode_json: false, // true if parsing JSON. + mode_module: false, // true if import or export was used. + mode_property: false, // true if directive /*property*/ is + // used. + mode_shebang: false, // true if #! is seen on the first line. + option_dict, + property_dict, + source, + stop, + stop_at, + syntax_dict, + tenure, + test_cause, + token_global, + token_list, + token_nxt: token_global, + warn, + warn_at, + warning_list + }); + +// PHASE 1. Split by newlines into . + + jslint_phase1_split(state); + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 2. Lex into . + + jslint_phase2_lex(state); + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 3. Parse into using the Pratt-parser. + + jslint_phase3_parse(state); + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). + + if (!state.mode_json) { + jslint_phase4_walk(state); + } + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 5. Check whitespace between tokens in . + + if (!state.mode_json && warning_list.length === 0) { + jslint_phase5_whitage(state); + } + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PR-347 - Disable warning "missing_browser". +// if (!option_dict.browser) { +// directive_list.forEach(function (comment) { +// if (comment.directive === "global") { +// +// // test_cause: +// // ["/*global aa*/", "jslint", "missing_browser", "(comment)", 1] +// +// warn("missing_browser", comment); +// } +// }); +// } + + if (option_dict.test_internal_error) { + jslint_assert(undefined, "test_internal_error"); + } + } catch (err) { + mode_stop = true; + err.message = "[JSLint was unable to finish] " + err.message; + err.mode_stop = true; + if (err.name !== "JSLintError") { + Object.assign(err, { + column: jslint_fudge, + line: jslint_fudge, + line_source: "", + stack_trace: err.stack + }); + } + if (warning_list.indexOf(err) === -1) { + warning_list.push(err); + } + } + +// Sort warning_list by mode_stop first, line, column respectively. + + warning_list.sort(function (aa, bb) { + return ( + Boolean(bb.mode_stop) - Boolean(aa.mode_stop) + || aa.line - bb.line + || aa.column - bb.column + ); + +// Update each warning with formatted_message ready-for-use by jslint_cli. + + }).map(function ({ + column, + line, + line_source, + message, + stack_trace = "" + }, ii, list) { + list[ii].formatted_message = String( + String(ii + 1).padStart(2, " ") + + ". \u001b[31m" + message + "\u001b[39m" + + " \u001b[90m\/\/ line " + line + ", column " + column + + "\u001b[39m\n" + + (" " + line_source.trim()).slice(0, 72) + "\n" + + stack_trace + ).trimRight(); + }); + + return { + causes: cause_dict, + directives: directive_list, + edition: jslint_edition, + exports: export_dict, + froms: import_list, + functions: function_list, + global: token_global, + id: "(JSLint)", + json: state.mode_json, + lines: line_list, + module: state.mode_module === true, + ok: warning_list.length === 0 && !mode_stop, + option: option_dict, + property: property_dict, + shebang: ( + state.mode_shebang + ? line_list[jslint_fudge].line_source + : undefined + ), + stop: mode_stop, + tokens: token_list, + tree: state.token_tree, + warnings: warning_list + }; +} + +// PR-362 - Add API Doc. + +async function jslint_apidoc({ + example_list, + github_repo, + module_list, + package_name, + pathname, + version +}) { + +// This function will create API Doc from . + + let elem_ii = 0; + let html; + + function elem_create(moduleObj, key, moduleName) { + +// This function will create a sub API Doc from elem []. + + let example = "N/A"; + let id = encodeURIComponent("apidoc.elem." + moduleName + "." + key); + let name; + let signature; + let source; + name = htmlEscape((typeof moduleObj[key]) + " " + key); + if (typeof moduleObj[key] !== "function") { + return { + name, + signature: (` + +${name} + + `), + source: (` +
  • +

    + + ${name} + +

    +
  • + `) + }; + } + // init source + source = htmlEscape(trim_start(moduleObj[key].toString())); + // init signature + source = source.replace(( + /(\([\S\s]*?\)) \{/ + ), function (match0, match1) { + signature = htmlEscape( + match1.replace(( + / *?\/\*[\S\s]*?\*\/ */g + ), "").replace(( + / *?\/\/.*/g + ), "").replace(( + /\n{2,}/g + ), "\n") + ); + return match0; + }); + // init comment + source = source.replace(( + /\n(?:\/\/.*?\n)+\n/ + ), "$&"); + // init example + example_list.some(function (example2) { + example2.replace( + new RegExp(( + "((?:\\n.*?){8}(function )?)\\b" + + key + + "(\\((?:.*?\\n){8})" + ), "g"), + function (ignore, header, isDeclaration, footer) { + if (!isDeclaration) { + example = "..." + trim_start( + htmlEscape(header) + + "" + + htmlEscape(key) + + "" + + htmlEscape(footer) + ).trimEnd() + "\n..."; + } + return ""; + } + ); + return example !== "N/A"; + }); + if (source.length > 2048) { + source = source.slice(0, 2048) + "...\n}\n"; + } + return { + name, + signature: (` + +${name}${signature} + + `), + source: (` +
  • +

    + + ${name}${signature} + +

    +
  • +
  • Description and source-code:
    ${source}
  • +
  • Example usage:
    ${example}
  • + `) + }; + } + + function trim_start(str) { + +// This function will normalize whitespace before . + + let whitespace = ""; + str.trim().replace(( + /^ */gm + ), function (match0) { + if (whitespace === "" || match0.length < whitespace.length) { + whitespace = match0; + } + return ""; + }); + str = str.replace(new RegExp("^" + whitespace, "gm"), ""); + return str; + } + await moduleFsInit(); + +// Html-escape params. + + github_repo = htmlEscape(github_repo); + package_name = htmlEscape(package_name); + version = htmlEscape(version); + +// Init example_list. + + example_list = await Promise.all(example_list.map(async function (file) { + +// This function will read example from given file. + + let result = await moduleFs.promises.readFile(file, "utf8"); + result = ( + "\n\n\n\n\n\n\n\n" + // bug-workaround - truncate example to manageable size + + result.slice(0, 524288) + + "\n\n\n\n\n\n\n\n" + ); + result = result.replace(( + /\r\n*/g + ), "\n"); + return result; + })); + // init module_list + module_list = await Promise.all(module_list.map(async function ({ + pathname + }) { + let moduleName = htmlEscape(JSON.stringify(pathname)); + let moduleObj = await import(pathname); + if (moduleObj.default) { + moduleObj = moduleObj.default; + } + return { + elem_list: Object.keys(moduleObj).map(function (key) { + return elem_create(moduleObj, key, moduleName); + }).sort(function (aa, bb) { + return ( + aa.name < bb.name + ? -1 + : 1 + ); + }).map(function (elem) { + elem_ii += 1; + elem.signature = elem.signature.replace( + ">", + ">" + elem_ii + ". " + ); + elem.source = elem.source.replace( + "\">", + "\">" + elem_ii + ". " + ); + return elem; + }), + id: encodeURIComponent("apidoc.module." + moduleName), + moduleName + }; + })); + html = (` + + + + + + +${package_name} apidoc + + + +
    +

    API Doc for ${package_name} (${version})

    +
    + +

    Table of Contents

    +
    +
      + `) + module_list.map(function ({ + elem_list, + id, + moduleName + }) { + return ( + (` +
    • + Module ${moduleName} +
        + `) + + elem_list.map(function ({ + signature + }) { + return "
      • \n" + signature + "\n
      • \n"; + }).join("") + + (` +
      +
    • + `) + ); + }).join("") + (` +
    +
    + `) + module_list.map(function ({ + elem_list, + id, + moduleName + }) { + return ( + (` +
    +

    Module ${moduleName}

    +
      + `) + + elem_list.map(function ({ + source + }) { + return source; + }).join("") + + (` +
    +
    + `) + ); + }).join("") + (` +
    + [ + This document was created with + JSLint + ] +
    +
    + + + `); + html = html.trim().replace(( + / +?$/gm + ), "") + "\n"; + await fsWriteFileWithParents(pathname, html); +} + +function jslint_assert(condition, message) { + +// This function will throw if is falsy. + + if (condition) { + return condition; + } + throw new Error( + `This was caused by a bug in JSLint. +Please open an issue with this stack-trace (and possible example-code) at +https://github.com/jslint-org/jslint/issues. +edition = "${jslint_edition}"; +${String(message).slice(0, 2000)}` + ); +} + +async function jslint_cli({ + console_error, + console_log, + file, + mode_cli, + mode_noop, + option, + process_argv, + process_env, + process_exit, + source +}) { + +// This function will run jslint from nodejs-cli. + + let command; + let data; + let exit_code = 0; + let mode_plugin_vim; + let mode_report; + let result; + + function jslint_from_file({ + code, + file, + line_offset = 0, + mode_conditional, + option = empty() + }) { + let result_from_file; + if ( + mode_conditional + && !( + /^\/\*jslint\b/m + ).test(code.slice(0, 65536)) + ) { + return; + } + option = Object.assign(empty(), option, { + file + }); + switch (( + /\.\w+?$|$/m + ).exec(file)[0]) { + case ".html": + +// Recursively jslint embedded "". + + code.replace(( + /^]*?>\n([\S\s]*?\n)<\/script>$/gm + ), function (ignore, match1, ii) { + jslint_from_file({ + code: match1, + file: file + ".". + + code.replace(( + /^]*?>\n([\S\s]*?\n)<\/script>$/gm + ), function (ignore, match1, ii) { + jslint_from_file({ + code: match1, + file: file + ". + diff --git a/branch.a9f822d2/jslint.css b/branch.a9f822d2/jslint.css new file mode 100644 index 000000000..9c1b1a2f9 --- /dev/null +++ b/branch.a9f822d2/jslint.css @@ -0,0 +1,340 @@ +@font-face { + font-family: 'Programma'; + src: url('Programma.woff') format('woff'); +} +@font-face { + font-family: 'Daley'; + font-weight: bold; + src: url('Daley-Bold.woff') format('woff'); +} + +body { + background-color: antiquewhite; + color: black; + font-family: 'Daley', sans-serif; + margin: 0; + padding: 0; +} + +#JSLINT_TITLEBOX { + font-family: 'Daley', sans-serif; + margin: 0; + margin-left: 3%; + margin-right: 3%; + padding: 0; +} + +#JSLINT_TITLEBOX ul { + float: right; + font-size: 12pt; +} + +#JSLINT_TITLEBOX div { + color: darkslategray; + float: left; + font-size: 48pt; +} + +#JSLINT_ fieldset { + background-color: gainsboro; + border: 0; + clear: both; + margin-bottom: 1em; + margin-left: 3%; + margin-right: 3%; + margin-top: 1em; + padding: 0; + width: auto; +} +#JSLINT_ legend { + background-color: darkslategray; + border: 0; + color: white; + font-size: 100%; + font-style: normal; + font-weight: normal; + margin: 0; + padding-bottom: 0.25em; + padding-left: 0; + padding-right: 0; + padding-top: 0.25em; + text-align: center; + width: 100%; +} +#JSLINT_ button { + background-color: darkslategray; + border: 0; + color: white; + cursor: pointer; + font-family: 'Daley', sans-serif; + font-size: 100%; + font-style: normal; + margin-left: 1em; + margin-right: 1em; + padding-left: 1em; + padding-right: 1em; + padding-bottom: 0.25em; + padding-top: 0.25em; + text-align: center; +} +#JSLINT_ button:hover { + background-color: slategray; + color: white; + border: 0; +} + +#JSLINT_ button:active { + border: 0; + color: black; +} + +#JSLINT_ button:disabled { + background-color: gray; + border: 0; + color: gainsboro; +} + +a:visited { + color: #69565c; +} + +a:link { + color: black; +} + +#JSLINT_ input[type="text"] { + background-color: white; + border: 0; + color: black; + margin-bottom: 0.2em; + margin-right: 0.5em; + padding-left: 0.5em; + padding-right: 0.5em; + text-align: right; + width: 3em; +} + +#JSLINT_ textarea { + border: 0; + background-color: white; + border: 0; + font-family: Programma, monospace; + font-size: 100%; + font-weight: bold; + resize: none; +} + +#JSLINT_ textarea::selection { + background-color: yellow; + color: black; +} + +#JSLINT_NUMBER { + color: darkgray; + display: inline-block; + height: 4in; + margin: 2%; + margin-right: 0; + overflow: hidden; + padding-right: 0.5%; + text-align: right; + white-space: pre; + width: 4%; +} + +#JSLINT_SOURCE { + color: black; + display: inline-block; + font-size: 100%; + height: 4in; + margin: 2%; + margin-left: 0; + margin-right: 0; + overflow: auto; + padding-left: 0.5%; + white-space: pre; + width: 91%; +} + +#JSLINT_PROPERTY { + background-color: honeydew; + border: 0; + margin: 2%; + padding: 0.5%; + white-space: pre; + width: 95%; +} + +#JSLINT_GLOBAL { + white-space: normal; + width: 95%; +} +#JSLINT_ label { + font-family: sans-serif; + font-size: 90%; + padding-left: 0.25em; +} +#JSLINT_OPTIONS>div { + float: left; + margin: 0.5em; +} + +#JSLINT_ address { + color: black; + display: block; + float: right; + font-family: serif; + font-size: 90%; + margin-left: 1em; +} +#JSLINT_ dl { + background-color: cornsilk; + color: white; + margin: 0; + padding-bottom: 2pt; + padding-left: 1em; + padding-right: 1em; + padding-top: 2pt; +} +#JSLINT_ dfn { + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + margin-bottom: 2pt; +} +#JSLINT_ dt { + color: black; + display: block; + float: left; + font-family: serif; + font-size: 75%; + font-style: italic; + margin: 0; + width: 8em; + text-align: right; +} +#JSLINT_ dd { + color: black; + display: block; + font-family: Programma, monospace; + font-weight: bold; + margin-left: 8em; + padding-bottom: 2pt; +} +#JSLINT_WARNINGS>legend { + background-color: indianred; +} +#JSLINT_WARNINGS>div { + background-color: pink; + padding: 1em; +} +#JSLINT_WARNINGS cite { + color: black; + display: block; + font-family: serif; + font-size: 100%; + font-style: normal; + margin-bottom: 4pt; + margin-left: 20pt; + margin-right: 20pt; + margin-top: 4pt; + overflow-x: hidden; +} +#JSLINT_WARNINGS samp { + background-color: lavenderblush; + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + padding: 4pt; + margin-bottom: 0; + margin-left: 16pt; + margin-right: 16pt; + margin-top: 0; + white-space: pre-wrap; +} + +#JSLINT_REPORT center { + margin-top: 1em; +} + +#JSLINT_WARNINGS dl address { + color: black; + display: inline; + float: none; + font-size: 80%; + margin: 0; +} + +#JSLINT_REPORT>div { + padding: 1em; +} + +#JSLINT_ dl.level0 { + color: black; + background-color: white; +} + +#JSLINT_ dl.level1 { + color: black; + background-color: #ffffe0; /* yellow */ + margin-left: 1em; +} + +#JSLINT_ dl.level2 { + color: black; + background-color: #e0ffe0; /* green */ + margin-left: 2em; +} + +#JSLINT_ dl.level3 { + color: black; + background-color: #e0e0ff; /* blue */ + margin-left: 3em; +} + +#JSLINT_ dl.level4 { + background-color: #ffe0ff; /* purple */ + color: black; + margin-left: 4em; +} + +#JSLINT_ dl.level5 { + background-color: #ffe0e0; /* red */ + color: black; + margin-left: 5em; +} + +#JSLINT_ dl.level6 { + background-color: #ffe390; /* orange */ + color: black; + margin-left: 6em; +} + +#JSLINT_ dl.level7 { + background-color: #e0e0e0; /* gray */ + color: black; + margin-left: 7em; +} + +#JSLINT_ dl.level8 { + color: black; + margin-left: 8em; +} + +#JSLINT_ dl.level9 { + color: black; + margin-left: 9em; +} + +.none { + display: none; +} +.center { + text-align: center; +} diff --git a/branch.a9f822d2/jslint.js b/branch.a9f822d2/jslint.js new file mode 100644 index 000000000..d0c506531 --- /dev/null +++ b/branch.a9f822d2/jslint.js @@ -0,0 +1,4924 @@ +// jslint.js +// 2018-09-26 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// jslint(source, option_object, global_array) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze, a string or an array of strings. +// option_object An object whose keys correspond to option names. +// global_array An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all of the functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// 1. If the source is a single string, split it into an array of strings. +// 2. Turn the source into an array of tokens. +// 3. Furcate the tokens into a parse tree. +// 4. Walk the tree, traversing all of the nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// 5. Check the whitespace between the tokens. + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*property + a, and, arity, assign, b, bad_assignment_a, bad_directive_a, bad_get, + bad_module_name_a, bad_option_a, bad_property_a, bad_set, bitwise, block, + body, browser, c, calls, catch, charCodeAt, closer, closure, code, column, + complex, concat, constant, context, convert, couch, create, d, dead, + default, devel, directive, directives, disrupt, dot, duplicate_a, edition, + ellipsis, else, empty_block, escape_mega, eval, every, expected_a, + expected_a_at_b_c, expected_a_b, expected_a_b_from_c_d, expected_a_before_b, + expected_a_next_at_b, expected_digits_after_a, expected_four_digits, + expected_identifier_a, expected_line_break_a_b, expected_regexp_factor_a, + expected_space_a_b, expected_statements_a, expected_string_a, + expected_type_string_a, exports, expression, extra, finally, flag, for, + forEach, free, from, froms, fud, fudge, function_in_loop, functions, g, + getset, global, i, id, identifier, import, inc, indexOf, infix_in, init, + initial, isArray, isNaN, join, json, keys, label, label_a, lbp, led, length, + level, line, lines, live, long, loop, m, margin, match, message, + misplaced_a, misplaced_directive_a, missing_browser, missing_m, module, + multivar, naked_block, name, names, nested_comment, new, node, not_label_a, + nr, nud, number_isNaN, ok, open, option, out_of_scope_a, parameters, parent, + pop, property, push, quote, redefinition_a_b, replace, + required_a_optional_b, reserved_a, right, role, search, shebang, signature, + single, slice, some, sort, split, startsWith, statement, stop, strict, + subscript_a, switch, test, this, thru, toString, todo_comment, tokens, + too_long, too_many_digits, tree, try, type, u, unclosed_comment, + unclosed_mega, unclosed_string, undeclared_a, unexpected_a, + unexpected_a_after_b, unexpected_a_before_b, unexpected_at_top_level_a, + unexpected_char_a, unexpected_comment, unexpected_directive_a, + unexpected_expression_a, unexpected_label_a, unexpected_parens, + unexpected_space_a_b, unexpected_statement_a, unexpected_trailing_space, + unexpected_typeof_a, uninitialized_a, unreachable_a, + unregistered_property_a, unsafe, unused_a, use_double, use_open, use_spaces, + use_strict, used, value, var_loop, var_switch, variable, warning, warnings, + weird_condition_a, weird_expression_a, weird_loop, weird_relation_a, white, + wrap_assignment, wrap_condition, wrap_immediate, wrap_parameter, + wrap_regexp, wrap_unary, wrapped, writable, y +*/ + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than {} because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +function populate(array, object = empty(), value = true) { + +// Augment an object by taking property names from an array of strings. + + array.forEach(function (name) { + object[name] = value; + }); + return object; +} + +const allowed_option = { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + bitwise: true, + browser: [ + "caches", "clearInterval", "clearTimeout", "document", "DOMException", + "Element", "Event", "event", "FileReader", "FormData", "history", + "localStorage", "location", "MutationObserver", "name", "navigator", + "screen", "sessionStorage", "setInterval", "setTimeout", "Storage", + "URL", "window", "Worker", "XMLHttpRequest" + ], + couch: [ + "emit", "getRow", "isArray", "log", "provides", "registerType", + "require", "send", "start", "sum", "toJSON" + ], + convert: true, + devel: [ + "alert", "confirm", "console", "prompt" + ], + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + multivar: true, + node: [ + "Buffer", "clearImmediate", "clearInterval", "clearTimeout", + "console", "exports", "module", "process", "require", + "setImmediate", "setInterval", "setTimeout", "URL", + "URLSearchParams", "__dirname", "__filename" + ], + single: true, + this: true, + white: true +}; + +const anticondition = populate([ + "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%", + "typeof", "(number)", "(string)" +]); + +// These are the bitwise operators. + +const bitwiseop = populate([ + "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=", + ">>>", ">>>=" +]); + +const escapeable = populate([ + "\\", "/", "`", "b", "f", "n", "r", "t" +]); + +const opener = { + +// The open and close pairs. + + "(": ")", // paren + "[": "]", // bracket + "{": "}", // brace + "${": "}" // mega +}; + +// The relational operators. + +const relationop = populate([ + "!=", "!==", "==", "===", "<", "<=", ">", ">=" +]); + +// This is the set of infix operators that require a space on each side. + +const spaceop = populate([ + "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/", + "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=", + ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" +]); + +const standard = [ + +// These are the globals that are provided by the language standard. + + "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI", + "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error", + "EvalError", "Float32Array", "Float64Array", "Generator", + "GeneratorFunction", "Int8Array", "Int16Array", "Int32Array", "Intl", + "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat", + "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", + "Set", "String", "Symbol", "SyntaxError", "System", "TypeError", + "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", + "URIError", "WeakMap", "WeakSet" +]; + +const bundle = { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + and: "The '&&' subexpression should be wrapped in parens.", + bad_assignment_a: "Bad assignment to '{a}'.", + bad_directive_a: "Bad directive '{a}'.", + bad_get: "A get function takes no parameters.", + bad_module_name_a: "Bad module name '{a}'.", + bad_option_a: "Bad option '{a}'.", + bad_property_a: "Bad property name '{a}'.", + bad_set: "A set function takes one parameter.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + escape_mega: "Unexpected escapement in mega literal.", + expected_a: "Expected '{a}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: ( + "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'." + ), + expected_a_before_b: "Expected '{a}' before '{b}'.", + expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.", + expected_digits_after_a: "Expected digits after '{a}'.", + expected_four_digits: "Expected four digits after '\\u'.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.", + expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.", + expected_space_a_b: "Expected one space between '{a}' and '{b}'.", + expected_statements_a: "Expected statements before '{a}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + expected_type_string_a: "Expected a type string and instead saw '{a}'.", + function_in_loop: "Don't make functions within a loop.", + infix_in: ( + "Unexpected 'in'. Compare with undefined, " + + "or use the hasOwnProperty method instead." + ), + label_a: "'{a}' is a statement label.", + misplaced_a: "Place '{a}' at the outermost level.", + misplaced_directive_a: ( + "Place the '/*{a}*/' directive before the first statement." + ), + missing_browser: "/*global*/ requires the Assume a browser option.", + missing_m: "Expected 'm' flag on a multiline regular expression.", + naked_block: "Naked block.", + nested_comment: "Nested comment.", + not_label_a: "'{a}' is not a label.", + number_isNaN: "Use Number.isNaN function to compare with NaN.", + out_of_scope_a: "'{a}' is out of scope.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + required_a_optional_b: ( + "Required parameter '{a}' after optional parameter '{b}'." + ), + reserved_a: "Reserved name '{a}'.", + subscript_a: "['{a}'] is better written in dot notation.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line is longer than 80 characters.", + too_many_digits: "Too many digits.", + unclosed_comment: "Unclosed comment.", + unclosed_mega: "Unclosed mega literal.", + unclosed_string: "Unclosed string.", + undeclared_a: "Undeclared '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_a_after_b: "Unexpected '{a}' after '{b}'.", + unexpected_a_before_b: "Unexpected '{a}' before '{b}'.", + unexpected_at_top_level_a: "Expected '{a}' to be in a function.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_directive_a: "When using modules, don't use directive '/*{a}'.", + unexpected_expression_a: ( + "Unexpected expression '{a}' in statement position." + ), + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_parens: "Don't wrap function literals in parens.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_statement_a: ( + "Unexpected statement '{a}' in expression position." + ), + unexpected_trailing_space: "Unexpected trailing space.", + unexpected_typeof_a: ( + "Unexpected 'typeof'. Use '===' to compare directly with {a}." + ), + uninitialized_a: "Uninitialized '{a}'.", + unreachable_a: "Unreachable '{a}'.", + unregistered_property_a: "Unregistered property name '{a}'.", + unsafe: "Unsafe character '{a}'.", + unused_a: "Unused '{a}'.", + use_double: "Use double quotes, not single quotes.", + use_open: ( + "Wrap a ternary expression in parens, " + + "with a line break after the left paren." + ), + use_spaces: "Use spaces, not tabs.", + use_strict: "This function needs a \"use strict\" pragma.", + var_loop: "Don't declare variables in a loop.", + var_switch: "Don't declare variables in a switch.", + weird_condition_a: "Weird condition '{a}'.", + weird_expression_a: "Weird expression '{a}'.", + weird_loop: "Weird loop.", + weird_relation_a: "Weird relation '{a}'.", + wrap_assignment: "Don't wrap assignment statements in parens.", + wrap_condition: "Wrap the condition in parens.", + wrap_immediate: ( + "Wrap an immediate function invocation in parentheses to assist " + + "the reader in understanding that the expression is the result " + + "of a function, and not the function itself." + ), + wrap_parameter: "Wrap the parameter in parens.", + wrap_regexp: "Wrap this regexp in parens to avoid confusion.", + wrap_unary: "Wrap the unary expression in parens." +}; + +// Regular expression literals: + +// supplant {variables} +const rx_supplant = /\{([^{}]*)\}/g; +// carriage return, carriage return linefeed, or linefeed +const rx_crlf = /\n|\r\n?/; +// unsafe characters that are silently deleted by one or more browsers +const rx_unsafe = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; +// identifier +const rx_identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; +const rx_module = /^[a-zA-Z0-9_$:.@\-\/]+$/; +const rx_bad_property = /^_|\$|Sync\$|_$/; +// star slash +const rx_star_slash = /\*\//; +// slash star +const rx_slash_star = /\/\*/; +// slash star or ending slash +const rx_slash_star_or_slash = /\/\*|\/$/; +// uncompleted work comment +const rx_todo = /\b(?:todo|TO\s?DO|HACK)\b/; +// tab +const rx_tab = /\t/g; +// directive +const rx_directive = /^(jslint|property|global)\s+(.*)$/; +const rx_directive_part = /^([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*(.*)$/; +// token (sorry it is so long) +const rx_token = /^((\s+)|([a-zA-Z_$][a-zA-Z0-9_$]*)|[(){}\[\]?,:;'"~`]|=(?:==?|>)?|\.+|[*\/][*\/=]?|\+[=+]?|-[=\-]?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<= "a" && string <= "z\uffff") + || (string >= "A" && string <= "Z\uffff") + ); +} + +function supplant(string, object) { + return string.replace(rx_supplant, function (found, filling) { + const replacement = object[filling]; + return ( + replacement !== undefined + ? replacement + : found + ); + }); +} + +let anon; // The guessed name for anonymous functions. +let blockage; // The current block. +let block_stack; // The stack of blocks. +let declared_globals; // The object containing the global declarations. +let directives; // The directive comments. +let directive_mode; // true if directives are still allowed. +let early_stop; // true if JSLint cannot finish. +let exports; // The exported names and values. +let froms; // The array collecting all import-from strings. +let fudge; // true if the natural numbers start with 1. +let functionage; // The current function. +let functions; // The array containing all of the functions. +let global; // The global object; the outermost context. +let json_mode; // true if parsing JSON. +let lines; // The array containing source lines. +let module_mode; // true if import or export was used. +let next_token; // The next token to be examined in the parse. +let option; // The options parameter. +let property; // The object containing the tallied property names. +let mega_mode; // true if currently parsing a megastring literal. +let shebang; // true if a #! was seen on the first line. +let stack; // The stack of functions. +let syntax; // The object containing the parser. +let token; // The current token being examined in the parse. +let token_nr; // The number of the next token. +let tokens; // The array of tokens. +let tenure; // The predefined property registry. +let tree; // The abstract parse tree. +let var_mode; // "var" if using var; "let" if using let. +let warnings; // The array collecting all generated warnings. + +// Error reportage functions: + +function artifact(the_token) { + +// Return a string representing an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); +} + +function artifact_line(the_token) { + +// Return the fudged line number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.line + fudge; +} + +function artifact_column(the_token) { + +// Return the fudged column number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.from + fudge; +} + +function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + const warning = { // ~~ + name: "JSLintError", + column: column, + line: line, + code: code + }; + if (a !== undefined) { + warning.a = a; + } + if (b !== undefined) { + warning.b = b; + } + if (c !== undefined) { + warning.c = c; + } + if (d !== undefined) { + warning.d = d; + } + warning.message = supplant(bundle[code] || code, warning); + warnings.push(warning); + return warning; +} + +function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); +} + +function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + if (the_token.warning === undefined) { + the_token.warning = warn_at( + code, + the_token.line, + the_token.from, + a || artifact(the_token), + b, + c, + d + ); + return the_token.warning; + } +} + +function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); +} + +// Tokenize: + +function tokenize(source) { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + +// If the source is not an array, then it is split into lines at the +// carriage return/linefeed. + + lines = ( + Array.isArray(source) + ? source + : source.split(rx_crlf) + ); + tokens = []; + + let char; // a popular character + let column = 0; // the column number of the next character + let first; // the first token + let from; // the starting column number of the token + let line = -1; // the line number of the next character + let nr = 0; // the next token number + let previous = global; // the previous token including comments + let prior = global; // the previous token excluding comments + let mega_from; // the starting column of megastring + let mega_line; // the starting line of megastring + let regexp_seen; // regular expression literal seen on this line + let snippet; // a piece of string + let source_line = ""; // the remaining line source string + let whole_line = ""; // the whole line source string + + if (lines[0].startsWith("#!")) { + line = 0; + shebang = true; + } + + function next_line() { + +// Put the next line of source in source_line. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + let at; + if ( + !option.long + && whole_line.length > 80 + && !json_mode + && first + && !regexp_seen + ) { + warn_at("too_long", line, 80); + } + column = 0; + line += 1; + regexp_seen = false; + whole_line = lines[line]; + source_line = whole_line; + if (source_line !== undefined) { + at = source_line.search(rx_tab); + if (at >= 0) { + if (!option.white) { + warn_at("use_spaces", line, at + 1); + } + source_line = source_line.replace(rx_tab, " "); + } + at = source_line.search(rx_unsafe); + if (at >= 0) { + warn_at( + "unsafe", + line, + column + at, + "U+" + source_line.charCodeAt(at).toString(16) + ); + } + if (!option.white && source_line.slice(-1) === " ") { + warn_at( + "unexpected_trailing_space", + line, + source_line.length - 1 + ); + } + } + return source_line; + } + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions snip, next_char, prev_char, +// some_digits, and escape help in the parsing of literals. + + function snip() { + +// Remove the last character from snippet. + + snippet = snippet.slice(0, -1); + } + + function next_char(match) { + +// Get the next character from the source line. Remove it from the source_line, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + return stop_at( + ( + char === "" + ? "expected_a" + : "expected_a_b" + ), + line, + column - 1, + match, + char + ); + } + if (source_line) { + char = source_line[0]; + source_line = source_line.slice(1); + snippet += char; + } else { + char = ""; + snippet += " "; + } + column += 1; + return char; + } + + function back_char() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the source_line. + + if (snippet) { + char = snippet.slice(-1); + source_line = char + source_line; + column -= 1; + snip(); + } else { + char = ""; + } + return char; + } + + function some_digits(rx, quiet) { + const result = source_line.match(rx); + if (result) { + char = result[1]; + column += char.length; + source_line = result[2]; + snippet += char; + } else { + char = ""; + if (!quiet) { + warn_at( + "expected_digits_after_a", + line, + column, + snippet + ); + } + } + return char.length; + } + + function escape(extra) { + next_char("\\"); + if (escapeable[char] === true) { + return next_char(); + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "u") { + if (next_char("u") === "{") { + if (json_mode) { + warn_at("unexpected_a", line, column - 1, char); + } + if (some_digits(rx_hexs) > 5) { + warn_at("too_many_digits", line, column - 1); + } + if (next_char() !== "}") { + stop_at("expected_a_before_b", line, column, "}", char); + } + return next_char(); + } + back_char(); + if (some_digits(rx_hexs, true) < 4) { + warn_at("expected_four_digits", line, column - 1); + } + return; + } + if (extra && extra.indexOf(char) >= 0) { + return next_char(); + } + warn_at("unexpected_a_before_b", line, column - 2, "\\", char); + } + + function make(id, value, identifier) { + +// Make the token object and append it to the tokens list. + + const the_token = { + from: from, + id: id, + identifier: Boolean(identifier), + line: line, + nr: nr, + thru: column + }; + tokens[nr] = the_token; + nr += 1; + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + directive_mode = false; + } + +// If the token is to have a value, give it one. + + if (value !== undefined) { + the_token.value = value; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option.white. + + if ( + previous.line === line + && previous.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (previous.id === "(comment)" || previous.id === "(regexp)") + ) { + warn( + "expected_space_a_b", + the_token, + artifact(previous), + artifact(the_token) + ); + } + if (previous.id === "." && id === "(number)") { + warn("expected_a_before_b", previous, "0", "."); + } + if (prior.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// The previous token is used to detect adjacency problems. + + previous = the_token; + +// The prior token is a previous token that was not a comment. The prior token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (previous.id !== "(comment)") { + prior = previous; + } + return the_token; + } + + function parse_directive(the_comment, body) { + +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + + const result = body.match(rx_directive_part); + if (result) { + let allowed; + const name = result[1]; + const value = result[2]; + if (the_comment.directive === "jslint") { + allowed = allowed_option[name]; + if ( + typeof allowed === "boolean" + || typeof allowed === "object" + ) { + if ( + value === "" + || value === "true" + || value === undefined + ) { + option[name] = true; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } else if (value === "false") { + option[name] = false; + } else { + warn("bad_option_a", the_comment, name + ":" + value); + } + } else { + warn("bad_option_a", the_comment, name); + } + } else if (the_comment.directive === "property") { + if (tenure === undefined) { + tenure = empty(); + } + tenure[name] = true; + } else if (the_comment.directive === "global") { + if (value) { + warn("bad_option_a", the_comment, name + ":" + value); + } + declared_globals[name] = false; + module_mode = the_comment; + } + return parse_directive(the_comment, result[3]); + } + if (body) { + return stop("bad_directive_a", the_comment, body); + } + } + + function comment(snippet) { + +// Make a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + + const the_comment = make("(comment)", snippet); + if (Array.isArray(snippet)) { + snippet = snippet.join(" "); + } + if (!option.devel && rx_todo.test(snippet)) { + warn("todo_comment", the_comment); + } + const result = snippet.match(rx_directive); + if (result) { + if (!directive_mode) { + warn_at("misplaced_directive_a", line, from, result[1]); + } else { + the_comment.directive = result[1]; + parse_directive(the_comment, result[2]); + } + directives.push(the_comment); + } + return the_comment; + } + + function regexp() { + +// Parse a regular expression literal. + + let multi_mode = false; + let result; + let value; + regexp_seen = true; + + function quantifier() { + +// Match an optional quantifier. + + if (char === "?" || char === "*" || char === "+") { + next_char(); + } else if (char === "{") { + if (some_digits(rx_digits, true) === 0) { + warn_at("expected_a", line, column, "0"); + } + if (next_char() === ",") { + some_digits(rx_digits, true); + next_char(); + } + next_char("}"); + } else { + return; + } + if (char === "?") { + next_char("?"); + } + } + + function subklass() { + +// Match a character in a character class. + + if (char === "\\") { + escape("BbDdSsWw-[]^"); + return true; + } + if ( + char === "" + || char === "[" + || char === "]" + || char === "/" + || char === "^" + || char === "-" + ) { + return false; + } + if (char === " ") { + warn_at("expected_a_b", line, column, "\\u0020", " "); + } else if (char === "`" && mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char(); + return true; + } + + function ranges() { + +// Match a range of subclasses. + + if (subklass()) { + if (char === "-") { + next_char("-"); + if (!subklass()) { + return stop_at( + "unexpected_a", + line, + column - 1, + "-" + ); + } + } + return ranges(); + } + } + + function klass() { + +// Match a class. + + next_char("["); + if (char === "^") { + next_char("^"); + } + (function classy() { + ranges(); + if (char !== "]" && char !== "") { + warn_at( + "expected_a_before_b", + line, + column, + "\\", + char + ); + next_char(); + return classy(); + } + }()); + next_char("]"); + } + + function choice() { + + function group() { + +// Match a group that starts with left paren. + + next_char("("); + if (char === "?") { + next_char("?"); + if (char === "=" || char === "!") { + next_char(); + } else { + next_char(":"); + } + } else if (char === ":") { + warn_at("expected_a_before_b", line, column, "?", ":"); + } + choice(); + next_char(")"); + } + + function factor() { + if ( + char === "" + || char === "/" + || char === "]" + || char === ")" + ) { + return false; + } + if (char === "(") { + group(); + return true; + } + if (char === "[") { + klass(); + return true; + } + if (char === "\\") { + escape("BbDdSsWw^${}[]():=!.-|*+?"); + return true; + } + if ( + char === "?" + || char === "+" + || char === "*" + || char === "}" + || char === "{" + ) { + warn_at( + "expected_a_before_b", + line, + column - 1, + "\\", + char + ); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column - 1, "`"); + } + } else if (char === " ") { + warn_at( + "expected_a_b", + line, + column - 1, + "\\s", + " " + ); + } else if (char === "$") { + if (source_line[0] !== "/") { + multi_mode = true; + } + } else if (char === "^") { + if (snippet !== "^") { + multi_mode = true; + } + } + next_char(); + return true; + } + + function sequence(follow) { + if (factor()) { + quantifier(); + return sequence(true); + } + if (!follow) { + warn_at("expected_regexp_factor_a", line, column, char); + } + } + +// Match a choice (a sequence that can be followed by | and another choice). + + sequence(); + if (char === "|") { + next_char("|"); + return choice(); + } + } + +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + next_char(); + if (char === "=") { + warn_at("expected_a_before_b", line, column, "\\", "="); + } + choice(); + +// Make sure there is a closing slash. + + snip(); + value = snippet; + next_char("/"); + +// Process dangling flag letters. + + const allowed = { + g: true, + i: true, + m: true, + u: true, + y: true + }; + const flag = empty(); + (function make_flag() { + if (is_letter(char)) { + if (allowed[char] !== true) { + warn_at("unexpected_a", line, column, char); + } + allowed[char] = false; + flag[char] = true; + next_char(); + return make_flag(); + } + }()); + back_char(); + if (char === "/" || char === "*") { + return stop_at("unexpected_a", line, from, char); + } + result = make("(regexp)", char); + result.flag = flag; + result.value = value; + if (multi_mode && !flag.m) { + warn_at("missing_m", line, column); + } + return result; + } + + function string(quote) { + +// Make a string token. + + let the_token; + snippet = ""; + next_char(); + + return (function next() { + if (char === quote) { + snip(); + the_token = make("(string)", snippet); + the_token.quote = quote; + return the_token; + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "\\") { + escape(quote); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char("`"); + } else { + next_char(); + } + return next(); + }()); + } + + function frack() { + if (char === ".") { + some_digits(rx_digits); + next_char(); + } + if (char === "E" || char === "e") { + next_char(); + if (char !== "+" && char !== "-") { + back_char(); + } + some_digits(rx_digits); + next_char(); + } + } + + function number() { + if (snippet === "0") { + next_char(); + if (char === ".") { + frack(); + } else if (char === "b") { + some_digits(rx_bits); + next_char(); + } else if (char === "o") { + some_digits(rx_octals); + next_char(); + } else if (char === "x") { + some_digits(rx_hexs); + next_char(); + } + } else { + next_char(); + frack(); + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + return stop_at( + "unexpected_a_after_b", + line, + column - 1, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + back_char(); + return make("(number)", snippet); + } + + function lex() { + let array; + let i = 0; + let j = 0; + let last; + let result; + let the_token; + if (!source_line) { + source_line = next_line(); + from = 0; + return ( + source_line === undefined + ? ( + mega_mode + ? stop_at("unclosed_mega", mega_line, mega_from) + : make("(end)") + ) + : lex() + ); + } + from = column; + result = source_line.match(rx_token); + +// result[1] token +// result[2] whitespace +// result[3] identifier +// result[4] number +// result[5] rest + + if (!result) { + return stop_at( + "unexpected_char_a", + line, + column, + source_line[0] + ); + } + + snippet = result[1]; + column += snippet.length; + source_line = result[5]; + +// Whitespace was matched. Call lex again to get more. + + if (result[2]) { + return lex(); + } + +// The token is an identifier. + + if (result[3]) { + return make(snippet, undefined, true); + } + +// The token is a number. + + if (result[4]) { + return number(snippet); + } + +// The token is a string. + + if (snippet === "\"") { + return string(snippet); + } + if (snippet === "'") { + if (!option.single) { + warn_at("use_double", line, column); + } + return string(snippet); + } + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (snippet === "`") { + if (mega_mode) { + return stop_at("expected_a_b", line, column, "}", "`"); + } + snippet = ""; + mega_from = from; + mega_line = line; + mega_mode = true; + +// Parsing a mega literal is tricky. First make a ` token. + + make("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + (function part() { + const at = source_line.search(rx_mega); + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + if (at < 0) { + snippet += source_line + "\n"; + return ( + next_line() === undefined + ? stop_at("unclosed_mega", mega_line, mega_from) + : part() + ); + } + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + snippet += source_line.slice(0, at); + column += at; + source_line = source_line.slice(at); + if (source_line[0] === "\\") { + stop_at("escape_mega", line, at); + } + make("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then make tokens that will become part of an expression until +// a } token is made. + + if (source_line[0] === "$") { + column += 2; + make("${"); + source_line = source_line.slice(2); + (function expr() { + const id = lex().id; + if (id === "{") { + return stop_at( + "expected_a_b", + line, + column, + "}", + "{" + ); + } + if (id !== "}") { + return expr(); + } + }()); + return part(); + } + }()); + source_line = source_line.slice(1); + column += 1; + mega_mode = false; + return make("`"); + } + +// The token is a // comment. + + if (snippet === "//") { + snippet = source_line; + source_line = ""; + the_token = comment(snippet); + if (mega_mode) { + warn("unexpected_comment", the_token, "`"); + } + return the_token; + } + +// The token is a /* comment. + + if (snippet === "/*") { + array = []; + if (source_line[0] === "/") { + warn_at("unexpected_a", line, column + i, "/"); + } + (function next() { + if (source_line > "") { + i = source_line.search(rx_star_slash); + if (i >= 0) { + return; + } + j = source_line.search(rx_slash_star); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + } + array.push(source_line); + source_line = next_line(); + if (source_line === undefined) { + return stop_at("unclosed_comment", line, column); + } + return next(); + }()); + snippet = source_line.slice(0, i); + j = snippet.search(rx_slash_star_or_slash); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + array.push(snippet); + column += i + 2; + source_line = source_line.slice(i + 2); + return comment(array); + } + +// The token is a slash. + + if (snippet === "/") { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + if (prior.identifier) { + if (!prior.dot) { + if (prior.id === "return") { + return regexp(); + } + if ( + prior.id === "(begin)" + || prior.id === "case" + || prior.id === "delete" + || prior.id === "in" + || prior.id === "instanceof" + || prior.id === "new" + || prior.id === "typeof" + || prior.id === "void" + || prior.id === "yield" + ) { + the_token = regexp(); + return stop("unexpected_a", the_token); + } + } + } else { + last = prior.id[prior.id.length - 1]; + if ("(,=:?[".indexOf(last) >= 0) { + return regexp(); + } + if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) { + the_token = regexp(); + warn("wrap_regexp", the_token); + return the_token; + } + } + if (source_line[0] === "/") { + column += 1; + source_line = source_line.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + } + return make(snippet); + } + + first = lex(); + json_mode = first.id === "{" || first.id === "["; + +// This is the only loop in JSLint. It will turn into a recursive call to lex +// when ES6 has been finished and widely deployed and adopted. + + while (true) { + if (lex().id === "(end)") { + break; + } + } +} + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + +function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!rx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!rx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property[id] === "number") { + property[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (tenure !== undefined) { + if (tenure[id] !== true) { + warn("unregistered_property_a", name); + } + } else { + if (name.identifier && rx_bad_property.test(id)) { + warn("bad_property_a", name); + } + } + property[id] = 1; + } + return id; +} + +function dispense() { + +// Deliver the next token, skipping the comments. + + const cadet = tokens[token_nr]; + token_nr += 1; + if (cadet.id === "(comment)") { + if (json_mode) { + warn("unexpected_a", cadet); + } + return dispense(); + } else { + return cadet; + } +} + +function lookahead() { + +// Look ahead one token without advancing. + + const old_token_nr = token_nr; + const cadet = dispense(true); + token_nr = old_token_nr; + return cadet; +} + +function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if (token.identifier && token.id !== "function") { + anon = token.id; + } else if (token.id === "(string)" && rx_identifier.test(token.value)) { + anon = token.value; + } + +// Attempt to match next_token with an expected id. + + if (id !== undefined && next_token.id !== id) { + return ( + match === undefined + ? stop("expected_a_b", next_token, id, artifact()) + : stop( + "expected_a_b_from_c_d", + next_token, + id, + artifact(match), + artifact_line(match), + artifact(next_token) + ) + ); + } + +// Promote the tokens, skipping comments. + + token = next_token; + next_token = dispense(); + if (next_token.id === "(end)") { + token_nr -= 1; + } +} + +// Parsing of JSON is simple: + +function json_value() { + let negative; + if (next_token.id === "{") { + return (function json_object() { + const brace = next_token; + const object = empty(); + const properties = []; + brace.expression = properties; + advance("{"); + if (next_token.id !== "}") { + (function next() { + let name; + let value; + if (next_token.quote !== "\"") { + warn( + "unexpected_a", + next_token, + next_token.quote + ); + } + name = next_token; + advance("(string)"); + if (object[token.value] !== undefined) { + warn("duplicate_a", token); + } else if (token.value === "__proto__") { + warn("bad_property_a", token); + } else { + object[token.value] = token; + } + advance(":"); + value = json_value(); + value.label = name; + properties.push(value); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("}", brace); + return brace; + }()); + } + if (next_token.id === "[") { + return (function json_array() { + const bracket = next_token; + const elements = []; + bracket.expression = elements; + advance("["); + if (next_token.id !== "]") { + (function next() { + elements.push(json_value()); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]", bracket); + return bracket; + }()); + } + if ( + next_token.id === "true" + || next_token.id === "false" + || next_token.id === "null" + ) { + advance(); + return token; + } + if (next_token.id === "(number)") { + if (!rx_JSON_number.test(next_token.value)) { + warn("unexpected_a"); + } + advance(); + return token; + } + if (next_token.id === "(string)") { + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + advance(); + return token; + } + if (next_token.id === "-") { + negative = next_token; + negative.arity = "unary"; + advance("-"); + advance("(number)"); + negative.expression = token; + return negative; + } + stop("unexpected_a"); +} + +// Now we parse JavaScript. + +function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + const id = name.id; + +// Reserved words may not be enrolled. + + if (syntax[id] !== undefined && id !== "ignore") { + warn("reserved_a", name); + } else { + +// Has the name been enrolled in this context? + + let earlier = functionage.context[id]; + if (earlier) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + +// Has the name been enrolled in an outer context? + + } else { + stack.forEach(function (value) { + const item = value.context[id]; + if (item !== undefined) { + earlier = item; + } + }); + if (earlier) { + if (id === "ignore") { + if (earlier.role === "variable") { + warn("unexpected_a", name); + } + } else { + if ( + ( + role !== "exception" + || earlier.role !== "exception" + ) + && role !== "parameter" + && role !== "function" + ) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + } + } + } + +// Enroll it. + + functionage.context[id] = name; + name.dead = true; + name.parent = functionage; + name.init = false; + name.role = role; + name.used = 0; + name.writable = !readonly; + } + } +} + +function expression(rbp, initial) { + +// This is the heart of the Pratt parser. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// nud Null denotation +// led Left denotation +// lbp Left binding power +// rbp Right binding power + +// It processes a nud (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop. It +// returns the expression's parse tree. + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement, + + if (!initial) { + advance(); + } + the_symbol = syntax[token.id]; + if (the_symbol !== undefined && the_symbol.nud !== undefined) { + left = the_symbol.nud(); + } else if (token.identifier) { + left = token; + left.arity = "variable"; + } else { + return stop("unexpected_a", token); + } + (function right() { + the_symbol = syntax[next_token.id]; + if ( + the_symbol !== undefined + && the_symbol.led !== undefined + && rbp < the_symbol.lbp + ) { + advance(); + left = the_symbol.led(left); + return right(); + } + }()); + return left; +} + +function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = next_token; + let the_value; + the_paren.free = true; + advance("("); + the_value = expression(0); + advance(")"); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (anticondition[the_value.id] === true) { + warn("unexpected_a", the_value); + } + return the_value; +} + +function is_weird(thing) { + return ( + thing.id === "(regexp)" + || thing.id === "{" + || thing.id === "=>" + || thing.id === "function" + || (thing.id === "[" && thing.arity === "unary") + ); +} + +function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + return ( + Array.isArray(b) + && a.length === b.length + && a.every(function (value, index) { + return are_similar(value, b[index]); + }) + ); + } + if (Array.isArray(b)) { + return false; + } + if (a.id === "(number)" && b.id === "(number)") { + return a.value === b.value; + } + let a_string; + let b_string; + if (a.id === "(string)") { + a_string = a.value; + } else if (a.id === "`" && a.constant) { + a_string = a.value[0]; + } + if (b.id === "(string)") { + b_string = b.value; + } else if (b.id === "`" && b.constant) { + b_string = b.value[0]; + } + if (typeof a_string === "string") { + return a_string === b_string; + } + if (is_weird(a) || is_weird(b)) { + return false; + } + if (a.arity === b.arity && a.id === b.id) { + if (a.id === ".") { + return ( + are_similar(a.expression, b.expression) + && are_similar(a.name, b.name) + ); + } + if (a.arity === "unary") { + return are_similar(a.expression, b.expression); + } + if (a.arity === "binary") { + return ( + a.id !== "(" + && are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + ); + } + if (a.arity === "ternary") { + return ( + are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + && are_similar(a.expression[2], b.expression[2]) + ); + } + if (a.arity === "function" && a.arity === "regexp") { + return false; + } + return true; + } + return false; +} + +function semicolon() { + +// Try to match a semicolon. + + if (next_token.id === ";") { + advance(";"); + } else { + warn_at( + "expected_a_b", + token.line, + token.thru, + ";", + artifact(next_token) + ); + } + anon = "anonymous"; +} + +function statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token.identifier && next_token.id === ":") { + the_label = token; + if (the_label.id === "ignore") { + warn("unexpected_a", the_label); + } + advance(":"); + if ( + next_token.id === "do" + || next_token.id === "for" + || next_token.id === "switch" + || next_token.id === "while" + ) { + enroll(the_label, "label", true); + the_label.init = true; + the_label.dead = false; + the_statement = statement(); + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token; + first.statement = true; + the_symbol = syntax[first.id]; + if (the_symbol !== undefined && the_symbol.fud !== undefined) { + the_symbol.disrupt = false; + the_symbol.statement = true; + the_statement = the_symbol.fud(); + } else { + +// It is an expression statement. + + the_statement = expression(0, true); + if (the_statement.wrapped && the_statement.id !== "(") { + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; +} + +function statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const array = []; + (function next(disrupt) { + if ( + next_token.id !== "}" + && next_token.id !== "case" + && next_token.id !== "default" + && next_token.id !== "else" + && next_token.id !== "(end)" + ) { + let a_statement = statement(); + array.push(a_statement); + if (disrupt) { + warn("unreachable_a", a_statement); + } + return next(a_statement.disrupt); + } + }(false)); + return array; +} + +function not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === global) { + warn("unexpected_at_top_level_a", thing); + } +} + +function top_level_only(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== global) { + warn("misplaced_a", the_thing); + } +} + +function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token; + the_block.arity = "statement"; + the_block.body = special === "body"; + +// All top level function bodies should include the "use strict" pragma unless +// the whole file is strict or the file is a module or the function parameters +// use es6 syntax. + + if ( + special === "body" + && stack.length === 1 + && next_token.value === "use strict" + ) { + the_block.strict = next_token; + next_token.statement = true; + advance("(string)"); + advance(";"); + } + stmts = statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option.devel && special !== "ignore") { + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; +} + +function mutation_check(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + warn("bad_assignment_a", the_thing); + return false; + } + return true; +} + +function left_check(left, right) { + +// Warn if the left is not one of these: +// e.b +// e[b] +// e() +// ?: +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !left_check(left.expression[1]) + && !left_check(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right); + return false; + } + return true; +} + +// These functions are used to specify the grammar of our language: + +function symbol(id, bp) { + +// Make a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax[id] = the_symbol; + } + return the_symbol; +} + +function assignment(id) { + +// Make an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led = function (left) { + const the_token = token; + let right; + the_token.arity = "assignment"; + right = expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "pre" + || right.arity === "post" + ) { + warn("unexpected_a", right); + } + mutation_check(left); + return the_token; + }; + return the_symbol; +} + +function constant(id, type, value) { + +// Make a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud = ( + typeof value === "function" + ? value + : function () { + token.constant = true; + if (value !== undefined) { + token.value = value; + } + return token; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; +} + +function infix(id, bp, f) { + +// Make an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, expression(bp)]; + return the_token; + }; + return the_symbol; +} + +function infixr(id, bp) { + +// Make a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + the_token.expression = [left, expression(bp - 1)]; + return the_token; + }; + return the_symbol; +} + +function post(id) { + +// Make one of the post operators. + + const the_symbol = symbol(id, 150); + the_symbol.led = function (left) { + token.expression = left; + token.arity = "post"; + mutation_check(token.expression); + return token; + }; + return the_symbol; +} + +function pre(id) { + +// Make one of the pre operators. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "pre"; + the_token.expression = expression(150); + mutation_check(the_token.expression); + return the_token; + }; + return the_symbol; +} + +function prefix(id, f) { + +// Make a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = expression(150); + return the_token; + }; + return the_symbol; +} + +function stmt(id, f) { + +// Make a statement. + + const the_symbol = symbol(id); + the_symbol.fud = function () { + token.arity = "statement"; + return f(); + }; + return the_symbol; +} + +function ternary(id1, id2) { + +// Make a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led = function (left) { + const the_token = token; + const second = expression(20); + advance(id2); + token.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, expression(10)]; + return the_token; + }; + return the_symbol; +} + +// Begin defining the language. + +syntax = empty(); + +symbol("}"); +symbol(")"); +symbol("]"); +symbol(","); +symbol(";"); +symbol(":"); +symbol("*/"); +symbol("await"); +symbol("case"); +symbol("catch"); +symbol("class"); +symbol("default"); +symbol("else"); +symbol("enum"); +symbol("finally"); +symbol("implements"); +symbol("interface"); +symbol("package"); +symbol("private"); +symbol("protected"); +symbol("public"); +symbol("static"); +symbol("super"); +symbol("void"); +symbol("yield"); + +constant("(number)", "number"); +constant("(regexp)", "regexp"); +constant("(string)", "string"); +constant("arguments", "object", function () { + warn("unexpected_a", token); + return token; +}); +constant("eval", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("false", "boolean", false); +constant("Function", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("ignore", "undefined", function () { + warn("unexpected_a", token); + return token; +}); +constant("Infinity", "number", Infinity); +constant("isFinite", "function", function () { + warn("expected_a_b", token, "Number.isFinite", "isFinite"); + return token; +}); +constant("isNaN", "function", function () { + warn("number_isNaN", token); + return token; +}); +constant("NaN", "number", NaN); +constant("null", "null", null); +constant("this", "object", function () { + if (!option.this) { + warn("unexpected_a", token); + } + return token; +}); +constant("true", "boolean", true); +constant("undefined", "undefined"); + +assignment("="); +assignment("+="); +assignment("-="); +assignment("*="); +assignment("/="); +assignment("%="); +assignment("&="); +assignment("|="); +assignment("^="); +assignment("<<="); +assignment(">>="); +assignment(">>>="); + +infix("||", 40); +infix("&&", 50); +infix("|", 70); +infix("^", 80); +infix("&", 90); +infix("==", 100); +infix("===", 100); +infix("!=", 100); +infix("!==", 100); +infix("<", 110); +infix(">", 110); +infix("<=", 110); +infix(">=", 110); +infix("in", 110); +infix("instanceof", 110); +infix("<<", 120); +infix(">>", 120); +infix(">>>", 120); +infix("+", 130); +infix("-", 130); +infix("*", 140); +infix("/", 140); +infix("%", 140); +infixr("**", 150); +infix("(", 160, function (left) { + const the_paren = token; + let the_argument; + if (left.id !== "function") { + left_check(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (next_token.id !== ")") { + (function next() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + the_paren.free = true; + if (the_argument.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + the_paren.free = false; + } + return the_paren; +}); +infix(".", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("[", 170, function (left) { + const the_token = token; + const the_subscript = expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + const name = survey(the_subscript); + if (rx_identifier.test(name)) { + warn("subscript_a", the_subscript, name); + } + } + left_check(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; +}); +infix("=>", 170, function (left) { + return stop("wrap_parameter", left); +}); + +function do_tick() { + const the_tick = token; + the_tick.value = []; + the_tick.expression = []; + if (next_token.id !== "`") { + (function part() { + advance("(string)"); + the_tick.value.push(token); + if (next_token.id === "${") { + advance("${"); + the_tick.expression.push(expression(0)); + advance("}"); + return part(); + } + }()); + } + advance("`"); + return the_tick; +} + +infix("`", 160, function (left) { + const the_tick = do_tick(); + left_check(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; +}); + +post("++"); +post("--"); +pre("++"); +pre("--"); + +prefix("+"); +prefix("-"); +prefix("~"); +prefix("!"); +prefix("!!"); +prefix("[", function () { + const the_token = token; + the_token.expression = []; + if (next_token.id !== "]") { + (function next() { + let element; + let ellipsis = false; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + element = expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]"); + return the_token; +}); +prefix("/=", function () { + stop("expected_a_b", token, "/\\=", "/="); +}); +prefix("=>", function () { + return stop("expected_a_before_b", token, "()", "=>"); +}); +prefix("new", function () { + const the_new = token; + const right = expression(160); + if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "()", artifact(next_token)); + } + the_new.expression = right; + return the_new; +}); +prefix("typeof"); +prefix("void", function () { + const the_void = token; + warn("unexpected_a", the_void); + the_void.expression = expression(0); + return the_void; +}); + +function parameter_list() { + let complex = false; + const list = []; + let optional; + const signature = ["("]; + if (next_token.id !== ")" && next_token.id !== "(end)") { + (function parameter() { + let ellipsis = false; + let param; + if (next_token.id === "{") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("{"); + signature.push("{"); + (function subparameter() { + let subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (next_token.id === ":") { + advance(":"); + advance(); + token.label = subparam; + subparam = token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + } + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return subparameter(); + } + }()); + list.push(param); + advance("}"); + signature.push("}"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else if (next_token.id === "[") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("["); + signature.push("[]"); + (function subparameter() { + const subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + return subparameter(); + } + }()); + list.push(param); + advance("]"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else { + if (next_token.id === "...") { + complex = true; + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + } + if (!next_token.identifier) { + return stop("expected_identifier_a"); + } + param = next_token; + list.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (next_token.id === "=") { + complex = true; + optional = param; + advance("="); + param.expression = expression(0); + } else { + if (optional !== undefined) { + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } + } + }()); + } + advance(")"); + signature.push(")"); + return [list, signature.join(""), complex]; +} + +function do_function(the_function) { + let name; + if (the_function === undefined) { + the_function = token; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + name = next_token; + enroll(name, "variable", true); + the_function.name = name; + name.init = true; + name.calls = empty(); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + if (next_token.identifier) { + name = next_token; + the_function.name = name; + advance(); + } else { + the_function.name = anon; + } + } + } else { + name = the_function.name; + } + the_function.level = functionage.level + 1; + if (mega_mode) { + warn("unexpected_a", the_function); + } + +// Don't make functions in loops. It is inefficient, and it can lead to scoping +// errors. + + if (functionage.loop > 0) { + warn("function_in_loop", the_function); + } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + the_function.context = empty(); + the_function.finally = 0; + the_function.loop = 0; + the_function.switch = 0; + the_function.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functions.push(the_function); + functionage = the_function; + if (the_function.arity !== "statement" && typeof name === "object") { + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// Parse the parameter list. + + advance("("); + token.free = false; + token.arity = "function"; + const pl = parameter_list(); + functionage.parameters = pl[0]; + functionage.signature = pl[1]; + functionage.complex = pl[2]; + functionage.parameters.forEach(function enroll_parameter(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + name.names.forEach(enroll_parameter); + } + }); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && next_token.line === token.line + ) { + return stop("unexpected_a", next_token); + } + if (next_token.id === "." || next_token.id === "[") { + warn("unexpected_a"); + } + +// Restore the previous context. + + functionage = stack.pop(); + return the_function; +} + +prefix("function", do_function); + +function fart(pl) { + if (next_token.id === ";") { + stop("wrap_assignment", token); + } + advance("=>"); + const the_fart = token; + the_fart.arity = "binary"; + the_fart.name = "=>"; + the_fart.level = functionage.level + 1; + functions.push(the_fart); + if (functionage.loop > 0) { + warn("function_in_loop", the_fart); + } + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + the_fart.context = empty(); + the_fart.finally = 0; + the_fart.loop = 0; + the_fart.switch = 0; + the_fart.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functionage = the_fart; + the_fart.parameters = pl[0]; + the_fart.signature = pl[1]; + the_fart.complex = true; + the_fart.parameters.forEach(function (name) { + enroll(name, "parameter", true); + }); + if (next_token.id === "{") { + warn("expected_a_b", the_fart, "function", "=>"); + the_fart.block = block("body"); + } else { + the_fart.expression = expression(0); + } + functionage = stack.pop(); + return the_fart; +} + +prefix("(", function () { + const the_paren = token; + let the_value; + const cadet = lookahead().id; + +// We can distinguish between a parameter list for => and a wrapped expression +// with one token of lookahead. + + if ( + next_token.id === ")" + || next_token.id === "..." + || (next_token.identifier && (cadet === "," || cadet === "=")) + ) { + the_paren.free = false; + return fart(parameter_list()); + } + the_paren.free = true; + the_value = expression(0); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + if (next_token.id === "=>") { + if (the_value.arity !== "variable") { + if (the_value.id === "{" || the_value.id === "[") { + warn("expected_a_before_b", the_paren, "function", "("); + return stop("expected_a_b", next_token, "{", "=>"); + } + return stop("expected_identifier_a", the_value); + } + the_paren.expression = [the_value]; + return fart([the_paren.expression, "(" + the_value.id + ")"]); + } + return the_value; +}); +prefix("`", do_tick); +prefix("{", function () { + const the_brace = token; + const seen = empty(); + the_brace.expression = []; + if (next_token.id !== "}") { + (function member() { + let extra; + let full; + let id; + let name = next_token; + let value; + advance(); + if ( + (name.id === "get" || name.id === "set") + && next_token.identifier + ) { + if (!option.getset) { + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + next_token.id; + name = next_token; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (next_token.id === "}" || next_token.id === ",") { + if (typeof extra === "string") { + advance("("); + } + value = expression(Infinity, true); + } else if (next_token.id === "(") { + value = do_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + advance("("); + } + advance(":"); + value = expression(0); + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + advance(":"); + value = expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (next_token.id === ",") { + advance(","); + return member(); + } + }()); + } + advance("}"); + return the_brace; +}); + +stmt(";", function () { + warn("unexpected_a", token); + return token; +}); +stmt("{", function () { + warn("naked_block", token); + return block("naked"); +}); +stmt("break", function () { + const the_break = token; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (next_token.identifier && token.line === next_token.line) { + the_label = functionage.context[next_token.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + warn( + (the_label !== undefined && the_label.dead) + ? "out_of_scope_a" + : "not_label_a" + ); + } else { + the_label.used += 1; + } + the_break.label = next_token; + advance(); + } + advance(";"); + return the_break; +}); + +function do_var() { + const the_statement = token; + const is_const = the_statement.id === "const"; + the_statement.names = []; + +// A program may use var or let, but not both. + + if (!is_const) { + if (var_mode === undefined) { + var_mode = the_statement.id; + } else if (the_statement.id !== var_mode) { + warn( + "expected_a_b", + the_statement, + var_mode, + the_statement.id + ); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + warn("var_switch", the_statement); + } + if (functionage.loop > 0 && the_statement.id === "var") { + warn("var_loop", the_statement); + } + (function next() { + if (next_token.id === "{" && the_statement.id !== "var") { + const the_brace = next_token; + the_brace.names = []; + advance("{"); + (function pair() { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + survey(name); + advance(); + if (next_token.id === ":") { + advance(":"); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + next_token.label = name; + the_brace.names.push(next_token); + enroll(next_token, "variable", is_const); + advance(); + } else { + the_brace.names.push(name); + enroll(name, "variable", is_const); + } + if (next_token.id === ",") { + advance(","); + return pair(); + } + }()); + advance("}"); + advance("="); + the_brace.expression = expression(0); + the_statement.names.push(the_brace); + } else if (next_token.id === "[" && the_statement.id !== "var") { + const the_bracket = next_token; + the_bracket.names = []; + advance("["); + (function element() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + advance(); + the_bracket.names.push(name); + enroll(name, "variable", the_statement.id === "const"); + if (ellipsis) { + name.ellipsis = true; + } else if (next_token.id === ",") { + advance(","); + return element(); + } + }()); + advance("]"); + advance("="); + the_bracket.expression = expression(0); + the_statement.names.push(the_bracket); + } else if (next_token.identifier) { + const name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", is_const); + if (next_token.id === "=" || is_const) { + advance("="); + name.init = true; + name.expression = expression(0); + } + the_statement.names.push(name); + } else { + return stop("expected_identifier_a", next_token); + } + if (next_token.id === ",") { + if (!option.multivar) { + warn("expected_a_b", next_token, ";", ","); + } + advance(","); + return next(); + } + }()); + the_statement.open = ( + the_statement.names.length > 1 + && the_statement.line !== the_statement.names[1].line + ); + semicolon(); + return the_statement; +} + +stmt("const", do_var); +stmt("continue", function () { + const the_continue = token; + if (functionage.loop < 1 || functionage.finally > 0) { + warn("unexpected_a", the_continue); + } + not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; +}); +stmt("debugger", function () { + const the_debug = token; + if (!option.devel) { + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; +}); +stmt("delete", function () { + const the_token = token; + const the_value = expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; +}); +stmt("do", function () { + const the_do = token; + not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; +}); +stmt("export", function () { + const the_export = token; + let the_id; + let the_name; + let the_thing; + + function export_id() { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + the_id = next_token.id; + the_name = global.context[the_id]; + if (the_name === undefined) { + warn("unexpected_a"); + } else { + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a"); + } + exports[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + } + + the_export.expression = []; + if (next_token.id === "default") { + if (exports.default !== undefined) { + warn("duplicate_a"); + } + advance("default"); + the_thing = expression(0); + semicolon(); + exports.default = the_thing; + the_export.expression.push(the_thing); + } else { + if (next_token.id === "function") { + the_thing = statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a", the_name); + } + exports[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + warn("unexpected_a"); + } else if (next_token.id === "{") { + advance("{"); + (function loop() { + export_id(); + if (next_token.id === ",") { + advance(","); + return loop(); + } + }()); + advance("}"); + semicolon(); + } else { + export_id(); + if (the_name.writable !== true) { + warn("unexpected_a", token); + } + semicolon(); + } + } + module_mode = true; + return the_export; +}); +stmt("for", function () { + let first; + const the_for = token; + if (!option.for) { + warn("unexpected_a", the_for); + } + not_top_level(the_for); + functionage.loop += 1; + advance("("); + token.free = true; + if (next_token.id === ";") { + return stop("expected_a_b", the_for, "while (", "for (;"); + } + if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + return stop("unexpected_a"); + } + first = expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = expression(0); + advance(";"); + the_for.inc = expression(0); + if (the_for.inc.id === "++") { + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; +}); +stmt("function", do_function); +stmt("if", function () { + let the_else; + const the_if = token; + the_if.expression = condition(); + the_if.block = block(); + if (next_token.id === "else") { + advance("else"); + the_else = token; + the_if.else = ( + next_token.id === "if" + ? statement() + : block() + ); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + the_if.disrupt = true; + } else { + warn("unexpected_a", the_else); + } + } + } + return the_if; +}); +stmt("import", function () { + const the_import = token; + let name; + if (typeof module_mode === "object") { + warn("unexpected_directive_a", module_mode, module_mode.directive); + } + module_mode = true; + if (next_token.identifier) { + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + const names = []; + advance("{"); + if (next_token.id !== "}") { + while (true) { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (next_token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token; + if (!rx_module.test(token.value)) { + warn("bad_module_name_a", token); + } + froms.push(token.value); + semicolon(); + return the_import; +}); +stmt("let", do_var); +stmt("return", function () { + const the_return = token; + not_top_level(the_return); + if (functionage.finally > 0) { + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (next_token.id !== ";" && the_return.line === next_token.line) { + the_return.expression = expression(10); + } + advance(";"); + return the_return; +}); +stmt("switch", function () { + let dups = []; + let last; + let stmts; + const the_cases = []; + let the_disrupt = true; + const the_switch = token; + not_top_level(the_switch); + if (functionage.finally > 0) { + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token.free = true; + the_switch.expression = expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + (function major() { + const the_case = next_token; + the_case.arity = "statement"; + the_case.expression = []; + (function minor() { + advance("case"); + token.switch = true; + const exp = expression(0); + if (dups.some(function (thing) { + return are_similar(thing, exp); + })) { + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (next_token.id === "case") { + return minor(); + } + }()); + stmts = statements(); + if (stmts.length < 1) { + warn("expected_statements_a"); + return; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "break;", + artifact(next_token) + ); + } + if (next_token.id === "case") { + return major(); + } + }()); + dups = undefined; + if (next_token.id === "default") { + const the_default = next_token; + advance("default"); + token.switch = true; + advance(":"); + the_switch.else = statements(); + if (the_switch.else.length < 1) { + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + const the_last = the_switch.else[the_switch.else.length - 1]; + if (the_last.id === "break" && the_last.label === undefined) { + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; +}); +stmt("throw", function () { + const the_throw = token; + the_throw.disrupt = true; + the_throw.expression = expression(10); + semicolon(); + if (functionage.try > 0) { + warn("unexpected_a", the_throw); + } + return the_throw; +}); +stmt("try", function () { + let the_catch; + let the_disrupt; + const the_try = token; + if (functionage.try > 0) { + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (next_token.id === "catch") { + let ignored = "ignore"; + the_catch = next_token; + the_try.catch = the_catch; + advance("catch"); + advance("("); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + if (next_token.id !== "ignore") { + ignored = undefined; + the_catch.name = next_token; + enroll(next_token, "exception", true); + } + advance(); + advance(")"); + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "catch", + artifact(next_token) + ); + + } + if (next_token.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; +}); +stmt("var", do_var); +stmt("while", function () { + const the_while = token; + not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; +}); +stmt("with", function () { + stop("unexpected_a", token); +}); + +ternary("?", ":"); + +// Ambulation of the parse tree. + +function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; +} + +function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all of the +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + } + }; +} + +const posts = empty(); +const pres = empty(); +const preaction = action(pres); +const postaction = action(posts); +const preamble = amble(pres); +const postamble = amble(posts); + +function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.id === "function") { + walk_statement(thing.block); + } + if (thing.arity === "pre" || thing.arity === "post") { + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + warn("unexpected_statement_a", thing); + } + postamble(thing); + } + } +} + +function walk_statement(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_statement); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + ) { + warn( + (thing.id === "(string)" && thing.value === "use strict") + ? "unexpected_a" + : "unexpected_expression_a", + thing + ); + } + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + } +} + +function lookup(thing) { + if (thing.arity === "variable") { + +// Look up the variable in the current context. + + let the_variable = functionage.context[thing.id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable === undefined) { + stack.forEach(function (outer) { + const a_variable = outer.context[thing.id]; + if ( + a_variable !== undefined + && a_variable.role !== "label" + ) { + the_variable = a_variable; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (the_variable === undefined) { + if (declared_globals[thing.id] === undefined) { + warn("undeclared_a", thing); + return; + } + the_variable = { + dead: false, + parent: global, + id: thing.id, + init: true, + role: "variable", + used: 0, + writable: false + }; + global.context[thing.id] = the_variable; + } + the_variable.closure = true; + functionage.context[thing.id] = the_variable; + } else if (the_variable.role === "label") { + warn("label_a", thing); + } + if ( + the_variable.dead + && ( + the_variable.calls === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + ) { + warn("out_of_scope_a", thing); + } + return the_variable; + } +} + +function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); +} + +function preaction_function(thing) { + if (thing.arity === "statement" && blockage.body !== true) { + warn("unexpected_a", thing); + } + if (thing.level === 1) { + if ( + module_mode === true + || global.strict !== undefined + || thing.complex + ) { + if (thing.id !== "=>" && thing.block.strict !== undefined) { + warn("unexpected_a", thing.block.strict); + } + } else { + if (thing.block.strict === undefined) { + warn("use_strict", thing); + } + } + } + stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); +} + +function bitwise_check(thing) { + if (!option.bitwise && bitwiseop[thing.id] === true) { + warn("unexpected_a", thing); + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + warn("unexpected_a", thing); + } +} + +function pop_block() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); +} + +function activate(name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.init = true; + } + } + blockage.live.push(name); +} + +function action_var(thing) { + thing.names.forEach(activate); +} + +preaction("assignment", bitwise_check); +preaction("binary", bitwise_check); +preaction("binary", function (thing) { + if (relationop[thing.id] === true) { + const left = thing.expression[0]; + const right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + warn("expected_string_a", right); + } + } else { + const value = right.value; + if (value === "null" || value === "undefined") { + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + warn("expected_type_string_a", right, value); + } + } + } + } +}); +preaction("binary", "==", function (thing) { + warn("expected_a_b", thing, "===", "=="); +}); +preaction("binary", "!=", function (thing) { + warn("expected_a_b", thing, "!==", "!="); +}); +preaction("binary", "=>", preaction_function); +preaction("binary", "||", function (thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + warn("and", thang); + } + }); +}); +preaction("binary", "(", function (thing) { + const left = thing.expression[0]; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + const parent = functionage.name.parent; + if (parent) { + const left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + && left_variable.dead + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } +}); +preaction("binary", "in", function (thing) { + warn("infix_in", thing); +}); +preaction("binary", "instanceof", function (thing) { + warn("unexpected_a", thing); +}); +preaction("binary", ".", function (thing) { + if (thing.expression.new) { + thing.new = true; + } +}); +preaction("statement", "{", function (thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; +}); +preaction("statement", "for", function (thing) { + if (thing.name !== undefined) { + const the_variable = lookup(thing.name); + if (the_variable !== undefined) { + the_variable.init = true; + if (!the_variable.writable) { + warn("bad_assignment_a", thing.name); + } + } + } + walk_statement(thing.initial); +}); +preaction("statement", "function", preaction_function); +preaction("unary", "~", bitwise_check); +preaction("unary", "function", preaction_function); +preaction("variable", function (thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } +}); + +function init_variable(name) { + const the_variable = lookup(name); + if (the_variable !== undefined) { + if (the_variable.writable) { + the_variable.init = true; + return; + } + } + warn("bad_assignment_a", name); +} + +postaction("assignment", "+=", function (thing) { + let right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } +}); +postaction("assignment", function (thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + if (thing.id === "=") { + if (thing.names !== undefined) { + if (Array.isArray(thing.names)) { + thing.names.forEach(init_variable); + } else { + init_variable(thing.names); + } + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.writable !== true) { + warn("bad_assignment_a", lvalue); + } + } + const right = syntax[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + warn("unexpected_a", thing.expression[1]); + } + } +}); + +function postaction_function(thing) { + delete functionage.finally; + delete functionage.loop; + delete functionage.switch; + delete functionage.try; + functionage = stack.pop(); + if (thing.wrapped) { + warn("unexpected_parens", thing); + } + return pop_block(); +} + +postaction("binary", function (thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || are_similar(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option.convert) { + if (thing.expression[0].value === "") { + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === ".") { + if (thing.expression.id === "RegExp") { + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } +}); +postaction("binary", "&&", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "||", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "=>", postaction_function); +postaction("binary", "(", function (thing) { + let left = thing.expression[0]; + let the_new; + let arg; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option.eval) { + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + warn( + "expected_a_before_b", + left, + "new", + artifact(left) + ); + } + } + } else if (left.id === ".") { + let cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + cack = !cack; + } + if (rx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + warn("unexpected_a", the_new); + } else { + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + const paren = left.expression; + if (paren.id === "(") { + const array = paren.expression; + if (array.length === 1) { + const new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } +}); +postaction("binary", "[", function (thing) { + if (thing.expression[0].id === "RegExp") { + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + warn("weird_expression_a", thing.expression[1]); + } +}); +postaction("statement", "{", pop_block); +postaction("statement", "const", action_var); +postaction("statement", "export", top_level_only); +postaction("statement", "for", function (thing) { + walk_statement(thing.inc); +}); +postaction("statement", "function", postaction_function); +postaction("statement", "import", function (the_thing) { + const name = the_thing.name; + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return top_level_only(the_thing); +}); +postaction("statement", "let", action_var); +postaction("statement", "try", function (thing) { + if (thing.catch !== undefined) { + const the_name = thing.catch.name; + if (the_name !== undefined) { + const the_variable = functionage.context[the_name.id]; + the_variable.dead = false; + the_variable.init = true; + } + walk_statement(thing.catch.block); + } +}); +postaction("statement", "var", action_var); +postaction("ternary", function (thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || are_similar(thing.expression[1], thing.expression[2]) + ) { + warn("unexpected_a", thing); + } else if (are_similar(thing.expression[0], thing.expression[1])) { + warn("expected_a_b", thing, "||", "?"); + } else if (are_similar(thing.expression[0], thing.expression[2])) { + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + warn("wrap_condition", thing.expression[0]); + } +}); +postaction("unary", function (thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option.convert) { + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } +}); +postaction("unary", "function", postaction_function); +postaction("unary", "+", function (thing) { + if (!option.convert) { + warn("expected_a_b", thing, "Number(...)", "+"); + } + const right = thing.expression; + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } +}); + +function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + if (id !== "ignore") { + const name = the_function.context[id]; + if (name.parent === the_function) { + if ( + name.used === 0 + && ( + name.role !== "function" + || name.parent.arity !== "unary" + ) + ) { + warn("unused_a", name); + } else if (!name.init) { + warn("uninitialized_a", name); + } + } + } + }); +} + +function uninitialized_and_unused() { + +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (module_mode === true || option.node) { + delve(global); + } + functions.forEach(delve); +} + +// Go through the token list, looking at usage of whitespace. + +function whitage() { + let closer = "(end)"; + let free = false; + let left = global; + let margin = 0; + let nr_comments_skipped = 0; + let open = true; + let right; + + function expected_at(at) { + warn( + "expected_a_at_b_c", + right, + artifact(right), + fudge + at, + artifact_column(right) + ); + } + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function no_space() { + if (left.line === right.line) { + if (left.thru !== right.from && nr_comments_skipped === 0) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (open) { + const at = ( + free + ? margin + : margin + 8 + ); + if (right.from < at) { + expected_at(at); + } + } else { + if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (free) { + if (right.from < margin) { + expected_at(margin); + } + } else { + const mislaid = ( + stack.length > 0 + ? stack[stack.length - 1].right + : undefined + ); + if (!open && mislaid !== undefined) { + warn( + "expected_a_next_at_b", + mislaid, + artifact(mislaid.id), + margin + 4 + fudge + ); + } else if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + stack = []; + tokens.forEach(function (the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + + const new_closer = opener[left.id]; + if (typeof new_closer === "string") { + if (new_closer !== right.id) { + stack.push({ + closer: closer, + free: free, + margin: margin, + open: open, + right: right + }); + closer = new_closer; + if (left.line !== right.line) { + free = closer === ")" && left.free; + open = true; + margin += 4; + if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (right.switch) { + at_margin(-4); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + free = false; + open = false; + no_space_only(); + } + } else { + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like {]) have already been detected. + + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } + } else { + +// If right is a closer, then pop the previous state. + + if (right.id === closer) { + const previous = stack.pop(); + margin = previous.margin; + if (open && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + closer = previous.closer; + free = previous.free; + open = previous.open; + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-4); + } else if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + one_space(); + } else { + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + at_margin(0); + } else { + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + no_space(); + } else if ( + left.id === "." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + ) { + no_space_only(); + } else if (right.id === ".") { + no_space_only(); + } else if (left.id === ";") { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + one_space_only(); + } else if (right.statement === true) { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.id === "var" + || left.id === "const" + || left.id === "let" + ) { + stack.push({ + closer: closer, + free: free, + margin: margin, + open: open + }); + closer = ";"; + free = false; + open = left.open; + if (open) { + margin = margin + 4; + at_margin(0); + } else { + one_space_only(); + } + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +// The jslint function itself. + +export default function jslint( + source = "", + option_object = empty(), + global_array = [] +) { + try { + warnings = []; + option = Object.assign(empty(), option_object); + anon = "anonymous"; + block_stack = []; + declared_globals = empty(); + directive_mode = true; + directives = []; + early_stop = true; + exports = empty(); + froms = []; + fudge = ( + option.fudge + ? 1 + : 0 + ); + functions = []; + global = { + id: "(global)", + body: true, + context: empty(), + from: 0, + level: 0, + line: 0, + live: [], + loop: 0, + switch: 0, + thru: 0 + }; + blockage = global; + functionage = global; + json_mode = false; + mega_mode = false; + module_mode = false; + next_token = global; + property = empty(); + shebang = false; + stack = []; + tenure = undefined; + token = global; + token_nr = 0; + var_mode = undefined; + populate(standard, declared_globals, false); + populate(global_array, declared_globals, false); + Object.keys(option).forEach(function (name) { + if (option[name] === true) { + const allowed = allowed_option[name]; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } + }); + tokenize(source); + advance(); + if (json_mode) { + tree = json_value(); + advance("(end)"); + } else { + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option.browser) { + if (next_token.id === ";") { + advance(";"); + } + } else { + +// If we are not in a browser, then the file form of strict pragma may be used. + + if ( + next_token.value === "use strict" + ) { + global.strict = next_token; + advance("(string)"); + advance(";"); + } + } + tree = statements(); + advance("(end)"); + functionage = global; + walk_statement(tree); + if (module_mode && global.strict !== undefined) { + warn("unexpected_a", global.strict); + } + if (warnings.length === 0) { + uninitialized_and_unused(); + if (!option.white) { + whitage(); + } + } + } + if (!option.browser) { + directives.forEach(function (comment) { + if (comment.directive === "global") { + warn("missing_browser", comment); + } + }); + } + early_stop = false; + } catch (e) { + if (e.name !== "JSLintError") { + warnings.push(e); + } + } + return { + directives, + edition: "2018-09-26", + exports, + froms, + functions, + global, + id: "(JSLint)", + json: json_mode, + lines, + module: module_mode === true, + ok: warnings.length === 0 && !early_stop, + option, + property, + shebang: ( + shebang + ? lines[0] + : undefined + ), + stop: early_stop, + tokens, + tree, + warnings: warnings.sort(function (a, b) { + return a.line - b.line || a.column - b.column; + }) + }; +}; diff --git a/branch.a9f822d2/report.js b/branch.a9f822d2/report.js new file mode 100644 index 000000000..f123e9055 --- /dev/null +++ b/branch.a9f822d2/report.js @@ -0,0 +1,221 @@ +// report.js +// 2018-07-29 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Generate JSLint HTML reports. + +/*property + closure, column, context, edition, error, exports, filter, forEach, froms, + fudge, function, functions, global, id, isArray, join, json, keys, length, + level, line, lines, message, module, name, names, option, parameters, + parent, property, push, replace, role, signature, sort, stop, warnings +*/ + +const rx_amp = /&/g; +const rx_gt = />/g; +const rx_lt = / with less destructive entities. + + return String( + string + ).replace( + rx_amp, + "&" + ).replace( + rx_lt, + "<" + ).replace( + rx_gt, + ">" + ); +} + +export default { + error: function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + let fudge = Number(Boolean(data.option.fudge)); + let output = []; + if (data.stop) { + output.push("
    JSLint was unable to finish.
    "); + } + data.warnings.forEach(function (warning) { + output.push( + "
    ", + entityify(warning.line + fudge), + ".", + entityify(warning.column + fudge), + "
    ", + entityify(warning.message), + "
    ", + entityify(data.lines[warning.line] || ""), + "" + ); + }); + return output.join(""); + }, + + function: function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + let fudge = Number(Boolean(data.option.fudge)); + let mode = ( + data.module + ? "module" + : "global" + ); + let output = []; + + if (data.json) { + return ( + data.warnings.length === 0 + ? "
    JSON: good.
    " + : "
    JSON: bad.
    " + ); + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + let global = Object.keys(data.global.context).sort(); + let froms = data.froms.sort(); + let exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + let context = the_function.context; + let list = Object.keys(context); + output.push( + "
    ", + entityify(the_function.line + fudge), + "
    ", + ( + the_function.name === "=>" + ? entityify(the_function.signature) + " =>" + : ( + typeof the_function.name === "string" + ? "«" + entityify(the_function.name) + "»" + : "" + entityify(the_function.name.id) + "" + ) + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + let params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.parent === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.parent === the_function + ); + })); + detail("outer", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.parent !== the_function + && the_variable.parent.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].parent.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + output.push( + "
    JSLint edition ", + entityify(data.edition), + "
    " + ); + return output.join(""); + }, + + property: function property_directive(data) { + +// Produce the /*property*/ directive. + + let not_first = false; + let output = ["/*property"]; + let length = 1111; + let properties = Object.keys(data.property); + + if (properties.length > 0) { + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length >= 80) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); + } + } +}; diff --git a/branch.alpha/.build/coverage/coverage-badge.svg b/branch.alpha/.build/coverage/coverage-badge.svg new file mode 100644 index 000000000..b4e592536 --- /dev/null +++ b/branch.alpha/.build/coverage/coverage-badge.svg @@ -0,0 +1,14 @@ + + + + +coverage +99.65 % + + diff --git a/branch.alpha/.build/coverage/coverage-report.txt b/branch.alpha/.build/coverage/coverage-report.txt new file mode 100644 index 000000000..c69de4ebf --- /dev/null +++ b/branch.alpha/.build/coverage/coverage-report.txt @@ -0,0 +1,13 @@ +coverage-report ++----------------------------------+-------------+ +| files covered | lines | ++----------------------------------+-------------+ +| ./ | 99.65 % | +| ******************************** | 6400 / 6422 | ++----------------------------------+-------------+ +| ./jslint.js | 99.63 % | +| ******************************** | 6022 / 6044 | ++----------------------------------+-------------+ +| ./test.js | 100.00 % | +| ******************************** | 378 / 378 | ++----------------------------------+-------------+ diff --git a/branch.alpha/.build/coverage/coverage-v8.json b/branch.alpha/.build/coverage/coverage-v8.json new file mode 100644 index 000000000..4228938c9 --- /dev/null +++ b/branch.alpha/.build/coverage/coverage-v8.json @@ -0,0 +1 @@ +{"result":[{"scriptId":"6","url":"internal/per_context/primordials.js","functions":[{"functionName":"uncurryThis","ranges":[{"startOffset":1000,"endOffset":1096,"count":5}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":1038,"endOffset":1093,"count":806}],"isBlockCoverage":true}]},{"scriptId":"9","url":"internal/bootstrap/loaders.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":10311,"count":1}],"isBlockCoverage":true},{"functionName":"binding","ranges":[{"startOffset":3652,"endOffset":4049,"count":0}],"isBlockCoverage":false},{"functionName":"_linkedBinding","ranges":[{"startOffset":4079,"endOffset":4287,"count":0}],"isBlockCoverage":false},{"functionName":"internalBinding","ranges":[{"startOffset":4467,"endOffset":4729,"count":96},{"startOffset":4569,"endOffset":4709,"count":30}],"isBlockCoverage":true},{"functionName":"getOwn","ranges":[{"startOffset":4874,"endOffset":5028,"count":198},{"startOffset":5010,"endOffset":5025,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":5395,"endOffset":5493,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":5454,"endOffset":5488,"count":232}],"isBlockCoverage":true},{"functionName":"NativeModule","ranges":[{"startOffset":5498,"endOffset":6250,"count":232}],"isBlockCoverage":true},{"functionName":"exposeInternals","ranges":[{"startOffset":6400,"endOffset":6626,"count":0}],"isBlockCoverage":false},{"functionName":"exists","ranges":[{"startOffset":6637,"endOffset":6690,"count":0}],"isBlockCoverage":false},{"functionName":"canBeRequiredByUsers","ranges":[{"startOffset":6701,"endOffset":6817,"count":7},{"startOffset":6785,"endOffset":6812,"count":5}],"isBlockCoverage":true},{"functionName":"compileForPublicLoader","ranges":[{"startOffset":6889,"endOffset":7583,"count":1},{"startOffset":6952,"endOffset":7144,"count":0},{"startOffset":7467,"endOffset":7471,"count":0}],"isBlockCoverage":true},{"functionName":"getESMFacade","ranges":[{"startOffset":7587,"endOffset":8138,"count":2},{"startOffset":7625,"endOffset":8137,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":7865,"endOffset":7978,"count":1}],"isBlockCoverage":true},{"functionName":"syncExports","ranges":[{"startOffset":8434,"endOffset":8778,"count":2},{"startOffset":8553,"endOffset":8768,"count":198},{"startOffset":8630,"endOffset":8639,"count":0}],"isBlockCoverage":true},{"functionName":"compileForInternalLoader","ranges":[{"startOffset":8782,"endOffset":9367,"count":346},{"startOffset":8831,"endOffset":8846,"count":81},{"startOffset":8848,"endOffset":8882,"count":269},{"startOffset":8882,"endOffset":9021,"count":77},{"startOffset":9021,"endOffset":9056,"count":0},{"startOffset":9057,"endOffset":9078,"count":77},{"startOffset":9232,"endOffset":9366,"count":77}],"isBlockCoverage":true},{"functionName":"nativeModuleRequire","ranges":[{"startOffset":9565,"endOffset":9936,"count":350},{"startOffset":9623,"endOffset":9654,"count":5},{"startOffset":9654,"endOffset":9838,"count":345},{"startOffset":9838,"endOffset":9893,"count":0},{"startOffset":9893,"endOffset":9935,"count":345}],"isBlockCoverage":true},{"functionName":"requireWithFallbackInDeps","ranges":[{"startOffset":10052,"endOffset":10224,"count":0}],"isBlockCoverage":false}]},{"scriptId":"10","url":"internal/bootstrap/node.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":12616,"count":1}],"isBlockCoverage":true},{"functionName":"process.openStdin","ranges":[{"startOffset":3399,"endOffset":3469,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":6160,"endOffset":6322,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":6424,"endOffset":6596,"count":0}],"isBlockCoverage":false},{"functionName":"setupPrepareStackTrace","ranges":[{"startOffset":9383,"endOffset":9969,"count":1}],"isBlockCoverage":true},{"functionName":"setupProcessObject","ranges":[{"startOffset":9971,"endOffset":10576,"count":1}],"isBlockCoverage":true},{"functionName":"setupGlobalProxy","ranges":[{"startOffset":10578,"endOffset":10755,"count":1}],"isBlockCoverage":true},{"functionName":"setupBuffer","ranges":[{"startOffset":10757,"endOffset":11193,"count":1}],"isBlockCoverage":true},{"functionName":"createGlobalConsole","ranges":[{"startOffset":11195,"endOffset":11876,"count":1}],"isBlockCoverage":true},{"functionName":"exposeNamespace","ranges":[{"startOffset":11928,"endOffset":12126,"count":1}],"isBlockCoverage":true},{"functionName":"exposeInterface","ranges":[{"startOffset":12178,"endOffset":12376,"count":4}],"isBlockCoverage":true},{"functionName":"defineOperation","ranges":[{"startOffset":12436,"endOffset":12615,"count":7}],"isBlockCoverage":true}]},{"scriptId":"11","url":"internal/errors.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":53549,"count":1}],"isBlockCoverage":false},{"functionName":"prepareStackTrace","ranges":[{"startOffset":1404,"endOffset":2120,"count":11},{"startOffset":1581,"endOffset":1697,"count":0},{"startOffset":1824,"endOffset":1846,"count":0},{"startOffset":2027,"endOffset":2056,"count":0}],"isBlockCoverage":true},{"functionName":"maybeOverridePrepareStackTrace","ranges":[{"startOffset":2162,"endOffset":2869,"count":11},{"startOffset":2431,"endOffset":2497,"count":0},{"startOffset":2778,"endOffset":2844,"count":0}],"isBlockCoverage":true},{"functionName":"lazyInternalUtil","ranges":[{"startOffset":2959,"endOffset":3085,"count":0}],"isBlockCoverage":false},{"functionName":"lazyInternalUtilInspect","ranges":[{"startOffset":3119,"endOffset":3281,"count":0}],"isBlockCoverage":false},{"functionName":"lazyBuffer","ranges":[{"startOffset":3295,"endOffset":3404,"count":0}],"isBlockCoverage":false},{"functionName":"SystemError","ranges":[{"startOffset":3906,"endOffset":6444,"count":0}],"isBlockCoverage":false},{"functionName":"toString","ranges":[{"startOffset":6448,"endOffset":6523,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":6527,"endOffset":6716,"count":0}],"isBlockCoverage":false},{"functionName":"makeSystemErrorWithCode","ranges":[{"startOffset":6720,"endOffset":6865,"count":4}],"isBlockCoverage":true},{"functionName":"NodeError","ranges":[{"startOffset":6811,"endOffset":6858,"count":0}],"isBlockCoverage":false},{"functionName":"makeNodeErrorWithCode","ranges":[{"startOffset":6867,"endOffset":7622,"count":233}],"isBlockCoverage":true},{"functionName":"NodeError","ranges":[{"startOffset":6955,"endOffset":7536,"count":6},{"startOffset":7045,"endOffset":7254,"count":0}],"isBlockCoverage":true},{"functionName":"toString","ranges":[{"startOffset":7542,"endOffset":7615,"count":0}],"isBlockCoverage":false},{"functionName":"hideStackFrames","ranges":[{"startOffset":7694,"endOffset":8105,"count":26}],"isBlockCoverage":true},{"functionName":"hidden","ranges":[{"startOffset":7734,"endOffset":8102,"count":304},{"startOffset":7898,"endOffset":7962,"count":260},{"startOffset":8046,"endOffset":8092,"count":260}],"isBlockCoverage":true},{"functionName":"addCodeToName","ranges":[{"startOffset":8107,"endOffset":8723,"count":6},{"startOffset":8205,"endOffset":8260,"count":0},{"startOffset":8545,"endOffset":8689,"count":0}],"isBlockCoverage":true},{"functionName":"E","ranges":[{"startOffset":8835,"endOffset":9343,"count":234},{"startOffset":9077,"endOffset":9122,"count":4},{"startOffset":9122,"endOffset":9176,"count":230},{"startOffset":9211,"endOffset":9321,"count":3}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":9238,"endOffset":9315,"count":3}],"isBlockCoverage":true},{"functionName":"getMessage","ranges":[{"startOffset":9345,"endOffset":10149,"count":6},{"startOffset":9446,"endOffset":9482,"count":1},{"startOffset":9773,"endOffset":9831,"count":0},{"startOffset":10053,"endOffset":10148,"count":0}],"isBlockCoverage":true},{"functionName":"lazyUv","ranges":[{"startOffset":10167,"endOffset":10271,"count":0}],"isBlockCoverage":false},{"functionName":"uvErrmapGet","ranges":[{"startOffset":10328,"endOffset":10498,"count":0}],"isBlockCoverage":false},{"functionName":"uvException","ranges":[{"startOffset":10791,"endOffset":11987,"count":0}],"isBlockCoverage":false},{"functionName":"uvExceptionWithHostPort","ranges":[{"startOffset":12300,"endOffset":13205,"count":0}],"isBlockCoverage":false},{"functionName":"errnoException","ranges":[{"startOffset":13384,"endOffset":14090,"count":0}],"isBlockCoverage":false},{"functionName":"exceptionWithHostPort","ranges":[{"startOffset":14443,"endOffset":15659,"count":0}],"isBlockCoverage":false},{"functionName":"dnsException","ranges":[{"startOffset":15823,"endOffset":17338,"count":0}],"isBlockCoverage":false},{"functionName":"connResetException","ranges":[{"startOffset":17340,"endOffset":17495,"count":0}],"isBlockCoverage":false},{"functionName":"isStackOverflowError","ranges":[{"startOffset":17785,"endOffset":18163,"count":0}],"isBlockCoverage":false},{"functionName":"addNumericalSeparator","ranges":[{"startOffset":18244,"endOffset":18480,"count":0}],"isBlockCoverage":false},{"functionName":"beforeInspector","ranges":[{"startOffset":18759,"endOffset":19150,"count":0}],"isBlockCoverage":false},{"functionName":"afterInspector","ranges":[{"startOffset":19154,"endOffset":20492,"count":0}],"isBlockCoverage":false},{"functionName":"AbortError","ranges":[{"startOffset":20728,"endOffset":20846,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":22625,"endOffset":22789,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":25628,"endOffset":25743,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":26032,"endOffset":26126,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":28407,"endOffset":28635,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":30368,"endOffset":30586,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":32336,"endOffset":32636,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":32678,"endOffset":32822,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":32865,"endOffset":36070,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":36111,"endOffset":36367,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":36759,"endOffset":36922,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":37584,"endOffset":37719,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":37760,"endOffset":38084,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":38243,"endOffset":38391,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":38435,"endOffset":39209,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":39587,"endOffset":39751,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":39804,"endOffset":40135,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":40179,"endOffset":40486,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":40856,"endOffset":40931,"count":6}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":40975,"endOffset":41263,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":41591,"endOffset":42022,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":42698,"endOffset":43221,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":43315,"endOffset":43416,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":44176,"endOffset":44874,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":44925,"endOffset":45117,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":45166,"endOffset":45482,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":45515,"endOffset":46392,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":46844,"endOffset":47103,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":48453,"endOffset":48622,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":50520,"endOffset":50651,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":51244,"endOffset":51527,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":52507,"endOffset":52605,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":52799,"endOffset":53154,"count":0}],"isBlockCoverage":false}]},{"scriptId":"12","url":"internal/util.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":12498,"count":1}],"isBlockCoverage":false},{"functionName":"lazyUv","ranges":[{"startOffset":991,"endOffset":1082,"count":0}],"isBlockCoverage":false},{"functionName":"removeColors","ranges":[{"startOffset":1084,"endOffset":1153,"count":0}],"isBlockCoverage":false},{"functionName":"isError","ranges":[{"startOffset":1155,"endOffset":1405,"count":0}],"isBlockCoverage":false},{"functionName":"deprecate","ranges":[{"startOffset":1690,"endOffset":2787,"count":11},{"startOffset":1764,"endOffset":1784,"count":0},{"startOffset":1844,"endOffset":1899,"count":0},{"startOffset":2541,"endOffset":2763,"count":10}],"isBlockCoverage":true},{"functionName":"deprecated","ranges":[{"startOffset":1925,"endOffset":2399,"count":0}],"isBlockCoverage":false},{"functionName":"decorateErrorStack","ranges":[{"startOffset":2789,"endOffset":3128,"count":0}],"isBlockCoverage":false},{"functionName":"assertCrypto","ranges":[{"startOffset":3130,"endOffset":3204,"count":0}],"isBlockCoverage":false},{"functionName":"normalizeEncoding","ranges":[{"startOffset":3383,"endOffset":3514,"count":15},{"startOffset":3453,"endOffset":3471,"count":0},{"startOffset":3487,"endOffset":3513,"count":0}],"isBlockCoverage":true},{"functionName":"slowCases","ranges":[{"startOffset":3516,"endOffset":5096,"count":0}],"isBlockCoverage":false},{"functionName":"emitExperimentalWarning","ranges":[{"startOffset":5098,"endOffset":5386,"count":0}],"isBlockCoverage":false},{"functionName":"filterDuplicateStrings","ranges":[{"startOffset":5388,"endOffset":5696,"count":0}],"isBlockCoverage":false},{"functionName":"cachedResult","ranges":[{"startOffset":5698,"endOffset":5841,"count":0}],"isBlockCoverage":false},{"functionName":"createClassWrapper","ranges":[{"startOffset":6106,"endOffset":6471,"count":0}],"isBlockCoverage":false},{"functionName":"getSignalsToNamesMapping","ranges":[{"startOffset":6500,"endOffset":6778,"count":0}],"isBlockCoverage":false},{"functionName":"convertToValidSignal","ranges":[{"startOffset":6780,"endOffset":7087,"count":0}],"isBlockCoverage":false},{"functionName":"getConstructorOf","ranges":[{"startOffset":7089,"endOffset":7435,"count":0}],"isBlockCoverage":false},{"functionName":"getSystemErrorName","ranges":[{"startOffset":7437,"endOffset":7566,"count":0}],"isBlockCoverage":false},{"functionName":"getSystemErrorMap","ranges":[{"startOffset":7568,"endOffset":7633,"count":0}],"isBlockCoverage":false},{"functionName":"promisify","ranges":[{"startOffset":7778,"endOffset":9249,"count":3},{"startOffset":7851,"endOffset":7916,"count":0},{"startOffset":7960,"endOffset":8281,"count":0}],"isBlockCoverage":true},{"functionName":"fn","ranges":[{"startOffset":8481,"endOffset":8962,"count":0}],"isBlockCoverage":false},{"functionName":"join","ranges":[{"startOffset":9344,"endOffset":9666,"count":0}],"isBlockCoverage":false},{"functionName":"spliceOne","ranges":[{"startOffset":9807,"endOffset":9934,"count":0}],"isBlockCoverage":false},{"functionName":"isInsideNodeModules","ranges":[{"startOffset":10016,"endOffset":11188,"count":0}],"isBlockCoverage":false},{"functionName":"once","ranges":[{"startOffset":11190,"endOffset":11348,"count":0}],"isBlockCoverage":false},{"functionName":"sleep","ranges":[{"startOffset":11371,"endOffset":11586,"count":0}],"isBlockCoverage":false}]},{"scriptId":"13","url":"events.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":26873,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":2207,"endOffset":2367,"count":0}],"isBlockCoverage":false},{"functionName":"EventEmitter","ranges":[{"startOffset":2372,"endOffset":2441,"count":3}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":2805,"endOffset":2861,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":2865,"endOffset":3099,"count":0}],"isBlockCoverage":false},{"functionName":"checkListener","ranges":[{"startOffset":3671,"endOffset":3821,"count":635},{"startOffset":3744,"endOffset":3819,"count":0}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":3910,"endOffset":3958,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":3967,"endOffset":4242,"count":0}],"isBlockCoverage":false},{"functionName":"EventEmitter.setMaxListeners","ranges":[{"startOffset":4618,"endOffset":5532,"count":0}],"isBlockCoverage":false},{"functionName":"EventEmitter.init","ranges":[{"startOffset":5555,"endOffset":6285,"count":3},{"startOffset":5606,"endOffset":5666,"count":1},{"startOffset":5668,"endOffset":5739,"count":2},{"startOffset":5810,"endOffset":5835,"count":2},{"startOffset":5837,"endOffset":6096,"count":0}],"isBlockCoverage":true},{"functionName":"addCatch","ranges":[{"startOffset":6288,"endOffset":6847,"count":0}],"isBlockCoverage":false},{"functionName":"emitUnhandledRejectionOrErr","ranges":[{"startOffset":6849,"endOffset":7507,"count":0}],"isBlockCoverage":false},{"functionName":"setMaxListeners","ranges":[{"startOffset":7678,"endOffset":7877,"count":0}],"isBlockCoverage":false},{"functionName":"_getMaxListeners","ranges":[{"startOffset":7880,"endOffset":8029,"count":0}],"isBlockCoverage":false},{"functionName":"getMaxListeners","ranges":[{"startOffset":8072,"endOffset":8135,"count":0}],"isBlockCoverage":false},{"functionName":"identicalSequenceRange","ranges":[{"startOffset":8263,"endOffset":8839,"count":0}],"isBlockCoverage":false},{"functionName":"enhanceStackTrace","ranges":[{"startOffset":8841,"endOffset":9447,"count":0}],"isBlockCoverage":false},{"functionName":"emit","ranges":[{"startOffset":9479,"endOffset":11762,"count":6},{"startOffset":9624,"endOffset":9662,"count":0},{"startOffset":9670,"endOffset":9704,"count":0},{"startOffset":9728,"endOffset":9757,"count":0},{"startOffset":9763,"endOffset":9800,"count":0},{"startOffset":9872,"endOffset":10804,"count":0},{"startOffset":10872,"endOffset":10885,"count":2},{"startOffset":10885,"endOffset":11213,"count":4},{"startOffset":11140,"endOffset":11158,"count":0},{"startOffset":11160,"endOffset":11209,"count":0},{"startOffset":11213,"endOffset":11744,"count":0},{"startOffset":11744,"endOffset":11761,"count":4}],"isBlockCoverage":true},{"functionName":"_addListener","ranges":[{"startOffset":11765,"endOffset":13820,"count":215},{"startOffset":11945,"endOffset":12029,"count":0},{"startOffset":12214,"endOffset":12494,"count":3},{"startOffset":12291,"endOffset":12310,"count":0},{"startOffset":12697,"endOffset":13800,"count":0}],"isBlockCoverage":true},{"functionName":"addListener","ranges":[{"startOffset":13859,"endOffset":13951,"count":215}],"isBlockCoverage":true},{"functionName":"prependListener","ranges":[{"startOffset":14064,"endOffset":14167,"count":0}],"isBlockCoverage":false},{"functionName":"onceWrapper","ranges":[{"startOffset":14170,"endOffset":14434,"count":0}],"isBlockCoverage":false},{"functionName":"_onceWrap","ranges":[{"startOffset":14436,"endOffset":14677,"count":210}],"isBlockCoverage":true},{"functionName":"once","ranges":[{"startOffset":14709,"endOffset":14835,"count":210}],"isBlockCoverage":true},{"functionName":"prependOnceListener","ranges":[{"startOffset":14887,"endOffset":15057,"count":0}],"isBlockCoverage":false},{"functionName":"removeListener","ranges":[{"startOffset":15179,"endOffset":16473,"count":210},{"startOffset":15328,"endOffset":15340,"count":0},{"startOffset":15413,"endOffset":15425,"count":0},{"startOffset":15455,"endOffset":15484,"count":209},{"startOffset":15537,"endOffset":15571,"count":0},{"startOffset":15667,"endOffset":15728,"count":1},{"startOffset":15746,"endOffset":16447,"count":0}],"isBlockCoverage":true},{"functionName":"removeAllListeners","ranges":[{"startOffset":16593,"endOffset":17919,"count":0}],"isBlockCoverage":false},{"functionName":"_listeners","ranges":[{"startOffset":17922,"endOffset":18317,"count":0}],"isBlockCoverage":false},{"functionName":"listeners","ranges":[{"startOffset":18354,"endOffset":18421,"count":0}],"isBlockCoverage":false},{"functionName":"rawListeners","ranges":[{"startOffset":18462,"endOffset":18533,"count":0}],"isBlockCoverage":false},{"functionName":"EventEmitter.listenerCount","ranges":[{"startOffset":18565,"endOffset":18733,"count":0}],"isBlockCoverage":false},{"functionName":"listenerCount","ranges":[{"startOffset":18790,"endOffset":19080,"count":418},{"startOffset":18963,"endOffset":18986,"count":208},{"startOffset":18986,"endOffset":19061,"count":210},{"startOffset":19022,"endOffset":19061,"count":0},{"startOffset":19065,"endOffset":19079,"count":210}],"isBlockCoverage":true},{"functionName":"eventNames","ranges":[{"startOffset":19118,"endOffset":19211,"count":0}],"isBlockCoverage":false},{"functionName":"arrayClone","ranges":[{"startOffset":19214,"endOffset":19674,"count":0}],"isBlockCoverage":false},{"functionName":"unwrapListeners","ranges":[{"startOffset":19676,"endOffset":19890,"count":0}],"isBlockCoverage":false},{"functionName":"getEventListeners","ranges":[{"startOffset":19892,"endOffset":20687,"count":0}],"isBlockCoverage":false},{"functionName":"once","ranges":[{"startOffset":20689,"endOffset":22475,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":22553,"endOffset":22574,"count":0}],"isBlockCoverage":false},{"functionName":"createIterResult","ranges":[{"startOffset":22589,"endOffset":22657,"count":0}],"isBlockCoverage":false},{"functionName":"addErrorHandlerIfEventEmitter","ranges":[{"startOffset":22659,"endOffset":22842,"count":0}],"isBlockCoverage":false},{"functionName":"eventTargetAgnosticRemoveListener","ranges":[{"startOffset":22844,"endOffset":23229,"count":0}],"isBlockCoverage":false},{"functionName":"eventTargetAgnosticAddListener","ranges":[{"startOffset":23231,"endOffset":23820,"count":0}],"isBlockCoverage":false},{"functionName":"on","ranges":[{"startOffset":23822,"endOffset":26872,"count":0}],"isBlockCoverage":false}]},{"scriptId":"14","url":"internal/util/inspect.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":71637,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":2893,"endOffset":2929,"count":62}],"isBlockCoverage":true},{"functionName":"isUndetectableObject","ranges":[{"startOffset":3020,"endOffset":3070,"count":0}],"isBlockCoverage":false},{"functionName":"getUserOptions","ranges":[{"startOffset":6215,"endOffset":7666,"count":0}],"isBlockCoverage":false},{"functionName":"inspect","ranges":[{"startOffset":7961,"endOffset":9878,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":9970,"endOffset":10015,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":10019,"endOffset":10227,"count":0}],"isBlockCoverage":false},{"functionName":"defineColorAlias","ranges":[{"startOffset":11964,"endOffset":12206,"count":12}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":12059,"endOffset":12099,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":12105,"endOffset":12151,"count":0}],"isBlockCoverage":false},{"functionName":"addQuotes","ranges":[{"startOffset":13216,"endOffset":13374,"count":0}],"isBlockCoverage":false},{"functionName":"escapeFn","ranges":[{"startOffset":13393,"endOffset":13425,"count":0}],"isBlockCoverage":false},{"functionName":"strEscape","ranges":[{"startOffset":13538,"endOffset":15164,"count":0}],"isBlockCoverage":false},{"functionName":"stylizeWithColor","ranges":[{"startOffset":15166,"endOffset":15432,"count":0}],"isBlockCoverage":false},{"functionName":"stylizeNoColor","ranges":[{"startOffset":15434,"endOffset":15480,"count":0}],"isBlockCoverage":false},{"functionName":"getEmptyFormatArray","ranges":[{"startOffset":15559,"endOffset":15606,"count":0}],"isBlockCoverage":false},{"functionName":"isInstanceof","ranges":[{"startOffset":15608,"endOffset":15726,"count":0}],"isBlockCoverage":false},{"functionName":"getConstructorName","ranges":[{"startOffset":15728,"endOffset":16988,"count":0}],"isBlockCoverage":false},{"functionName":"addPrototypeProperties","ranges":[{"startOffset":17175,"endOffset":19018,"count":0}],"isBlockCoverage":false},{"functionName":"getPrefix","ranges":[{"startOffset":19020,"endOffset":19407,"count":0}],"isBlockCoverage":false},{"functionName":"getKeys","ranges":[{"startOffset":19444,"endOffset":20386,"count":0}],"isBlockCoverage":false},{"functionName":"getCtxStyle","ranges":[{"startOffset":20388,"endOffset":20651,"count":0}],"isBlockCoverage":false},{"functionName":"formatProxy","ranges":[{"startOffset":20653,"endOffset":21102,"count":0}],"isBlockCoverage":false},{"functionName":"findTypedConstructor","ranges":[{"startOffset":21104,"endOffset":21627,"count":0}],"isBlockCoverage":false},{"functionName":"formatValue","ranges":[{"startOffset":21809,"endOffset":24348,"count":0}],"isBlockCoverage":false},{"functionName":"formatRaw","ranges":[{"startOffset":24350,"endOffset":34825,"count":0}],"isBlockCoverage":false},{"functionName":"getIteratorBraces","ranges":[{"startOffset":34827,"endOffset":35009,"count":0}],"isBlockCoverage":false},{"functionName":"getBoxedBase","ranges":[{"startOffset":35011,"endOffset":36185,"count":0}],"isBlockCoverage":false},{"functionName":"getClassBase","ranges":[{"startOffset":36187,"endOffset":36787,"count":0}],"isBlockCoverage":false},{"functionName":"getFunctionBase","ranges":[{"startOffset":36789,"endOffset":37882,"count":0}],"isBlockCoverage":false},{"functionName":"formatError","ranges":[{"startOffset":37884,"endOffset":41005,"count":0}],"isBlockCoverage":false},{"functionName":"groupArrayElements","ranges":[{"startOffset":41007,"endOffset":45258,"count":0}],"isBlockCoverage":false},{"functionName":"handleMaxCallStackSize","ranges":[{"startOffset":45260,"endOffset":45648,"count":0}],"isBlockCoverage":false},{"functionName":"formatNumber","ranges":[{"startOffset":45650,"endOffset":45827,"count":0}],"isBlockCoverage":false},{"functionName":"formatBigInt","ranges":[{"startOffset":45829,"endOffset":45901,"count":0}],"isBlockCoverage":false},{"functionName":"formatPrimitive","ranges":[{"startOffset":45903,"endOffset":47086,"count":0}],"isBlockCoverage":false},{"functionName":"formatNamespaceObject","ranges":[{"startOffset":47088,"endOffset":48208,"count":0}],"isBlockCoverage":false},{"functionName":"formatSpecialArray","ranges":[{"startOffset":48255,"endOffset":49462,"count":0}],"isBlockCoverage":false},{"functionName":"formatArrayBuffer","ranges":[{"startOffset":49464,"endOffset":50064,"count":0}],"isBlockCoverage":false},{"functionName":"formatArray","ranges":[{"startOffset":50066,"endOffset":50660,"count":0}],"isBlockCoverage":false},{"functionName":"formatTypedArray","ranges":[{"startOffset":50662,"endOffset":51678,"count":0}],"isBlockCoverage":false},{"functionName":"formatSet","ranges":[{"startOffset":51680,"endOffset":51912,"count":0}],"isBlockCoverage":false},{"functionName":"formatMap","ranges":[{"startOffset":51914,"endOffset":52212,"count":0}],"isBlockCoverage":false},{"functionName":"formatSetIterInner","ranges":[{"startOffset":52214,"endOffset":53027,"count":0}],"isBlockCoverage":false},{"functionName":"formatMapIterInner","ranges":[{"startOffset":53029,"endOffset":54351,"count":0}],"isBlockCoverage":false},{"functionName":"formatWeakCollection","ranges":[{"startOffset":54353,"endOffset":54445,"count":0}],"isBlockCoverage":false},{"functionName":"formatWeakSet","ranges":[{"startOffset":54447,"endOffset":54604,"count":0}],"isBlockCoverage":false},{"functionName":"formatWeakMap","ranges":[{"startOffset":54606,"endOffset":54763,"count":0}],"isBlockCoverage":false},{"functionName":"formatIterator","ranges":[{"startOffset":54765,"endOffset":55156,"count":0}],"isBlockCoverage":false},{"functionName":"formatPromise","ranges":[{"startOffset":55158,"endOffset":55623,"count":0}],"isBlockCoverage":false},{"functionName":"formatProperty","ranges":[{"startOffset":55625,"endOffset":58023,"count":0}],"isBlockCoverage":false},{"functionName":"isBelowBreakLength","ranges":[{"startOffset":58025,"endOffset":58967,"count":0}],"isBlockCoverage":false},{"functionName":"reduceToSingleString","ranges":[{"startOffset":58969,"endOffset":61715,"count":0}],"isBlockCoverage":false},{"functionName":"hasBuiltInToString","ranges":[{"startOffset":61717,"endOffset":62736,"count":0}],"isBlockCoverage":false},{"functionName":"firstErrorLine","ranges":[{"startOffset":62761,"endOffset":62800,"count":0}],"isBlockCoverage":false},{"functionName":"tryStringify","ranges":[{"startOffset":62830,"endOffset":63299,"count":0}],"isBlockCoverage":false},{"functionName":"format","ranges":[{"startOffset":63301,"endOffset":63385,"count":0}],"isBlockCoverage":false},{"functionName":"formatWithOptions","ranges":[{"startOffset":63387,"endOffset":63665,"count":209},{"startOffset":63510,"endOffset":63602,"count":0}],"isBlockCoverage":true},{"functionName":"formatWithOptionsInternal","ranges":[{"startOffset":63667,"endOffset":67451,"count":209},{"startOffset":63890,"endOffset":67250,"count":0},{"startOffset":67254,"endOffset":67450,"count":0}],"isBlockCoverage":true},{"functionName":"getStringWidth","ranges":[{"startOffset":67880,"endOffset":68431,"count":0}],"isBlockCoverage":false},{"functionName":"getStringWidth","ranges":[{"startOffset":68546,"endOffset":68958,"count":0}],"isBlockCoverage":false},{"functionName":"isFullWidthCodePoint","ranges":[{"startOffset":69126,"endOffset":70735,"count":0}],"isBlockCoverage":false},{"functionName":"isZeroWidthCodePoint","ranges":[{"startOffset":70769,"endOffset":71337,"count":0}],"isBlockCoverage":false},{"functionName":"stripVTControlCharacters","ranges":[{"startOffset":71427,"endOffset":71501,"count":0}],"isBlockCoverage":false}]},{"scriptId":"15","url":"internal/util/types.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1915,"count":1}],"isBlockCoverage":false},{"functionName":"isTypedArray","ranges":[{"startOffset":425,"endOffset":516,"count":0}],"isBlockCoverage":false},{"functionName":"isUint8Array","ranges":[{"startOffset":518,"endOffset":612,"count":75}],"isBlockCoverage":true},{"functionName":"isUint8ClampedArray","ranges":[{"startOffset":614,"endOffset":722,"count":0}],"isBlockCoverage":false},{"functionName":"isUint16Array","ranges":[{"startOffset":724,"endOffset":820,"count":0}],"isBlockCoverage":false},{"functionName":"isUint32Array","ranges":[{"startOffset":822,"endOffset":918,"count":0}],"isBlockCoverage":false},{"functionName":"isInt8Array","ranges":[{"startOffset":920,"endOffset":1012,"count":0}],"isBlockCoverage":false},{"functionName":"isInt16Array","ranges":[{"startOffset":1014,"endOffset":1108,"count":0}],"isBlockCoverage":false},{"functionName":"isInt32Array","ranges":[{"startOffset":1110,"endOffset":1204,"count":0}],"isBlockCoverage":false},{"functionName":"isFloat32Array","ranges":[{"startOffset":1206,"endOffset":1304,"count":0}],"isBlockCoverage":false},{"functionName":"isFloat64Array","ranges":[{"startOffset":1306,"endOffset":1404,"count":0}],"isBlockCoverage":false},{"functionName":"isBigInt64Array","ranges":[{"startOffset":1406,"endOffset":1506,"count":0}],"isBlockCoverage":false},{"functionName":"isBigUint64Array","ranges":[{"startOffset":1508,"endOffset":1610,"count":2}],"isBlockCoverage":true}]},{"scriptId":"16","url":"internal/assert.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":474,"count":1}],"isBlockCoverage":false},{"functionName":"lazyError","ranges":[{"startOffset":26,"endOffset":155,"count":0}],"isBlockCoverage":false},{"functionName":"assert","ranges":[{"startOffset":157,"endOffset":307,"count":10},{"startOffset":205,"endOffset":305,"count":0}],"isBlockCoverage":true},{"functionName":"fail","ranges":[{"startOffset":309,"endOffset":426,"count":0}],"isBlockCoverage":false}]},{"scriptId":"17","url":"internal/validators.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":7218,"count":1}],"isBlockCoverage":false},{"functionName":"isInt32","ranges":[{"startOffset":581,"endOffset":640,"count":1}],"isBlockCoverage":true},{"functionName":"isUint32","ranges":[{"startOffset":642,"endOffset":704,"count":17}],"isBlockCoverage":true},{"functionName":"parseFileMode","ranges":[{"startOffset":1326,"endOffset":1807,"count":17},{"startOffset":1389,"endOffset":1409,"count":0},{"startOffset":1411,"endOffset":1432,"count":0},{"startOffset":1480,"endOffset":1806,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":1852,"endOffset":2233,"count":76},{"startOffset":1972,"endOffset":2026,"count":0},{"startOffset":2066,"endOffset":2120,"count":0},{"startOffset":2163,"endOffset":2229,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":2279,"endOffset":2860,"count":1},{"startOffset":2441,"endOffset":2739,"count":0},{"startOffset":2776,"endOffset":2856,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":2904,"endOffset":3414,"count":0}],"isBlockCoverage":false},{"functionName":"validateString","ranges":[{"startOffset":3418,"endOffset":3550,"count":48},{"startOffset":3494,"endOffset":3548,"count":0}],"isBlockCoverage":true},{"functionName":"validateNumber","ranges":[{"startOffset":3552,"endOffset":3684,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":3724,"endOffset":4174,"count":0}],"isBlockCoverage":false},{"functionName":"validateBoolean","ranges":[{"startOffset":4178,"endOffset":4313,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":4357,"endOffset":4582,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":4624,"endOffset":4911,"count":0}],"isBlockCoverage":false},{"functionName":"validateSignalName","ranges":[{"startOffset":4915,"endOffset":5336,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":5377,"endOffset":5607,"count":0}],"isBlockCoverage":false},{"functionName":"validateEncoding","ranges":[{"startOffset":5611,"endOffset":5945,"count":0}],"isBlockCoverage":false},{"functionName":"validatePort","ranges":[{"startOffset":6089,"endOffset":6463,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":6506,"endOffset":6607,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":6655,"endOffset":6872,"count":0}],"isBlockCoverage":false}]},{"scriptId":"18","url":"buffer.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":36751,"count":1}],"isBlockCoverage":false},{"functionName":"validateOffset","ranges":[{"startOffset":2784,"endOffset":2868,"count":0}],"isBlockCoverage":false},{"functionName":"createUnsafeBuffer","ranges":[{"startOffset":4082,"endOffset":4218,"count":8}],"isBlockCoverage":true},{"functionName":"createPool","ranges":[{"startOffset":4220,"endOffset":4379,"count":1}],"isBlockCoverage":true},{"functionName":"alignPool","ranges":[{"startOffset":4395,"endOffset":4517,"count":2}],"isBlockCoverage":true},{"functionName":"showFlaggedDeprecation","ranges":[{"startOffset":4821,"endOffset":5501,"count":0}],"isBlockCoverage":false},{"functionName":"toInteger","ranges":[{"startOffset":5503,"endOffset":5721,"count":0}],"isBlockCoverage":false},{"functionName":"_copy","ranges":[{"startOffset":5723,"endOffset":6988,"count":0}],"isBlockCoverage":false},{"functionName":"_copyActual","ranges":[{"startOffset":6990,"endOffset":7592,"count":51},{"startOffset":7131,"endOffset":7185,"count":0},{"startOffset":7347,"endOffset":7362,"count":0},{"startOffset":7389,"endOffset":7404,"count":0},{"startOffset":7464,"endOffset":7540,"count":0}],"isBlockCoverage":true},{"functionName":"Buffer","ranges":[{"startOffset":8168,"endOffset":8501,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":8594,"endOffset":8622,"count":0}],"isBlockCoverage":false},{"functionName":"from","ranges":[{"startOffset":8879,"endOffset":9843,"count":2},{"startOffset":9008,"endOffset":9059,"count":0},{"startOffset":9061,"endOffset":9842,"count":0}],"isBlockCoverage":true},{"functionName":"of","ranges":[{"startOffset":10214,"endOffset":10366,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":10655,"endOffset":10876,"count":83},{"startOffset":10699,"endOffset":10764,"count":0},{"startOffset":10807,"endOffset":10874,"count":0}],"isBlockCoverage":true},{"functionName":"alloc","ranges":[{"startOffset":10979,"endOffset":11224,"count":76},{"startOffset":11063,"endOffset":11076,"count":0},{"startOffset":11077,"endOffset":11088,"count":0},{"startOffset":11090,"endOffset":11191,"count":0}],"isBlockCoverage":true},{"functionName":"allocUnsafe","ranges":[{"startOffset":11403,"endOffset":11478,"count":7}],"isBlockCoverage":true},{"functionName":"allocUnsafeSlow","ranges":[{"startOffset":11719,"endOffset":11808,"count":0}],"isBlockCoverage":false},{"functionName":"SlowBuffer","ranges":[{"startOffset":11904,"endOffset":11994,"count":0}],"isBlockCoverage":false},{"functionName":"allocate","ranges":[{"startOffset":12108,"endOffset":12440,"count":7},{"startOffset":12151,"endOffset":12185,"count":0},{"startOffset":12224,"endOffset":12403,"count":0}],"isBlockCoverage":true},{"functionName":"fromStringFast","ranges":[{"startOffset":12442,"endOffset":12988,"count":2},{"startOffset":12568,"endOffset":12617,"count":0},{"startOffset":12663,"endOffset":12676,"count":0},{"startOffset":12809,"endOffset":12935,"count":0}],"isBlockCoverage":true},{"functionName":"fromString","ranges":[{"startOffset":12990,"endOffset":13443,"count":2},{"startOffset":13076,"endOffset":13100,"count":0},{"startOffset":13139,"endOffset":13163,"count":0},{"startOffset":13221,"endOffset":13403,"count":0}],"isBlockCoverage":true},{"functionName":"fromArrayBuffer","ranges":[{"startOffset":13445,"endOffset":14142,"count":0}],"isBlockCoverage":false},{"functionName":"fromArrayLike","ranges":[{"startOffset":14144,"endOffset":14518,"count":0}],"isBlockCoverage":false},{"functionName":"fromObject","ranges":[{"startOffset":14520,"endOffset":14826,"count":0}],"isBlockCoverage":false},{"functionName":"isBuffer","ranges":[{"startOffset":14865,"endOffset":14919,"count":0}],"isBlockCoverage":false},{"functionName":"compare","ranges":[{"startOffset":14939,"endOffset":15264,"count":0}],"isBlockCoverage":false},{"functionName":"isEncoding","ranges":[{"startOffset":15287,"endOffset":15438,"count":15}],"isBlockCoverage":true},{"functionName":"concat","ranges":[{"startOffset":15504,"endOffset":16708,"count":8},{"startOffset":15563,"endOffset":15627,"count":0},{"startOffset":15658,"endOffset":15682,"count":1},{"startOffset":15682,"endOffset":15853,"count":7},{"startOffset":15772,"endOffset":15849,"count":51},{"startOffset":15853,"endOffset":15902,"count":0},{"startOffset":15902,"endOffset":16004,"count":7},{"startOffset":16004,"endOffset":16352,"count":51},{"startOffset":16059,"endOffset":16291,"count":0},{"startOffset":16352,"endOffset":16443,"count":7},{"startOffset":16443,"endOffset":16688,"count":0},{"startOffset":16688,"endOffset":16707,"count":7}],"isBlockCoverage":true},{"functionName":"base64ByteLength","ranges":[{"startOffset":16711,"endOffset":16947,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":17082,"endOffset":17146,"count":2}],"isBlockCoverage":true},{"functionName":"slice","ranges":[{"startOffset":17159,"endOffset":17205,"count":13}],"isBlockCoverage":true},{"functionName":"indexOf","ranges":[{"startOffset":17220,"endOffset":17316,"count":0}],"isBlockCoverage":false},{"functionName":"byteLength","ranges":[{"startOffset":17409,"endOffset":17438,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":17451,"endOffset":17515,"count":0}],"isBlockCoverage":false},{"functionName":"slice","ranges":[{"startOffset":17528,"endOffset":17574,"count":2}],"isBlockCoverage":true},{"functionName":"indexOf","ranges":[{"startOffset":17589,"endOffset":17688,"count":0}],"isBlockCoverage":false},{"functionName":"byteLength","ranges":[{"startOffset":17787,"endOffset":17816,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":17829,"endOffset":17893,"count":0}],"isBlockCoverage":false},{"functionName":"slice","ranges":[{"startOffset":17906,"endOffset":17952,"count":0}],"isBlockCoverage":false},{"functionName":"indexOf","ranges":[{"startOffset":17967,"endOffset":18066,"count":0}],"isBlockCoverage":false},{"functionName":"byteLength","ranges":[{"startOffset":18162,"endOffset":18187,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":18200,"endOffset":18266,"count":0}],"isBlockCoverage":false},{"functionName":"slice","ranges":[{"startOffset":18279,"endOffset":18327,"count":0}],"isBlockCoverage":false},{"functionName":"indexOf","ranges":[{"startOffset":18342,"endOffset":18440,"count":0}],"isBlockCoverage":false},{"functionName":"byteLength","ranges":[{"startOffset":18533,"endOffset":18558,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":18571,"endOffset":18636,"count":0}],"isBlockCoverage":false},{"functionName":"slice","ranges":[{"startOffset":18649,"endOffset":18696,"count":0}],"isBlockCoverage":false},{"functionName":"indexOf","ranges":[{"startOffset":18711,"endOffset":18923,"count":0}],"isBlockCoverage":false},{"functionName":"byteLength","ranges":[{"startOffset":19019,"endOffset":19070,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":19083,"endOffset":19149,"count":0}],"isBlockCoverage":false},{"functionName":"slice","ranges":[{"startOffset":19162,"endOffset":19210,"count":0}],"isBlockCoverage":false},{"functionName":"indexOf","ranges":[{"startOffset":19225,"endOffset":19439,"count":0}],"isBlockCoverage":false},{"functionName":"byteLength","ranges":[{"startOffset":19526,"endOffset":19557,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":19570,"endOffset":19633,"count":0}],"isBlockCoverage":false},{"functionName":"slice","ranges":[{"startOffset":19646,"endOffset":19691,"count":0}],"isBlockCoverage":false},{"functionName":"indexOf","ranges":[{"startOffset":19706,"endOffset":19914,"count":0}],"isBlockCoverage":false},{"functionName":"getEncodingOps","ranges":[{"startOffset":19922,"endOffset":21477,"count":15},{"startOffset":20048,"endOffset":20072,"count":13},{"startOffset":20072,"endOffset":20128,"count":2},{"startOffset":20128,"endOffset":20294,"count":0},{"startOffset":20299,"endOffset":20704,"count":0},{"startOffset":20709,"endOffset":20839,"count":0},{"startOffset":20844,"endOffset":20976,"count":0},{"startOffset":20981,"endOffset":21348,"count":0},{"startOffset":21353,"endOffset":21471,"count":0}],"isBlockCoverage":true},{"functionName":"byteLength","ranges":[{"startOffset":21479,"endOffset":22136,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":22276,"endOffset":22370,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":22448,"endOffset":22546,"count":0}],"isBlockCoverage":false},{"functionName":"copy","ranges":[{"startOffset":22578,"endOffset":22711,"count":0}],"isBlockCoverage":false},{"functionName":"toString","ranges":[{"startOffset":22992,"endOffset":23571,"count":16},{"startOffset":23064,"endOffset":23112,"count":0},{"startOffset":23164,"endOffset":23174,"count":0},{"startOffset":23204,"endOffset":23214,"count":0},{"startOffset":23263,"endOffset":23275,"count":0},{"startOffset":23291,"endOffset":23312,"count":0},{"startOffset":23338,"endOffset":23348,"count":1},{"startOffset":23348,"endOffset":23384,"count":15},{"startOffset":23384,"endOffset":23418,"count":0},{"startOffset":23418,"endOffset":23489,"count":15},{"startOffset":23489,"endOffset":23530,"count":0},{"startOffset":23530,"endOffset":23570,"count":15}],"isBlockCoverage":true},{"functionName":"equals","ranges":[{"startOffset":23600,"endOffset":23954,"count":0}],"isBlockCoverage":false},{"functionName":"inspect","ranges":[{"startOffset":24082,"endOffset":25077,"count":0}],"isBlockCoverage":false},{"functionName":"compare","ranges":[{"startOffset":25173,"endOffset":26322,"count":0}],"isBlockCoverage":false},{"functionName":"bidirectionalIndexOf","ranges":[{"startOffset":26750,"endOffset":28057,"count":0}],"isBlockCoverage":false},{"functionName":"indexOf","ranges":[{"startOffset":28086,"endOffset":28203,"count":0}],"isBlockCoverage":false},{"functionName":"lastIndexOf","ranges":[{"startOffset":28237,"endOffset":28359,"count":0}],"isBlockCoverage":false},{"functionName":"includes","ranges":[{"startOffset":28390,"endOffset":28495,"count":0}],"isBlockCoverage":false},{"functionName":"fill","ranges":[{"startOffset":28673,"endOffset":28772,"count":0}],"isBlockCoverage":false},{"functionName":"_fill","ranges":[{"startOffset":28775,"endOffset":30684,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":30711,"endOffset":31726,"count":0}],"isBlockCoverage":false},{"functionName":"toJSON","ranges":[{"startOffset":31755,"endOffset":31989,"count":0}],"isBlockCoverage":false},{"functionName":"adjustOffset","ranges":[{"startOffset":31992,"endOffset":32426,"count":118},{"startOffset":32232,"endOffset":32270,"count":59},{"startOffset":32270,"endOffset":32333,"count":0},{"startOffset":32333,"endOffset":32357,"count":59},{"startOffset":32357,"endOffset":32381,"count":7},{"startOffset":32381,"endOffset":32411,"count":52},{"startOffset":32411,"endOffset":32414,"count":0},{"startOffset":32415,"endOffset":32423,"count":52}],"isBlockCoverage":true},{"functionName":"slice","ranges":[{"startOffset":32453,"endOffset":32753,"count":59},{"startOffset":32614,"endOffset":32625,"count":0},{"startOffset":32673,"endOffset":32676,"count":0}],"isBlockCoverage":true},{"functionName":"swap","ranges":[{"startOffset":32756,"endOffset":32827,"count":0}],"isBlockCoverage":false},{"functionName":"swap16","ranges":[{"startOffset":32855,"endOffset":33259,"count":0}],"isBlockCoverage":false},{"functionName":"swap32","ranges":[{"startOffset":33288,"endOffset":33732,"count":0}],"isBlockCoverage":false},{"functionName":"swap64","ranges":[{"startOffset":33761,"endOffset":34269,"count":0}],"isBlockCoverage":false},{"functionName":"transcode","ranges":[{"startOffset":34582,"endOffset":35322,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":35391,"endOffset":35581,"count":0}],"isBlockCoverage":false},{"functionName":"btoa","ranges":[{"startOffset":35585,"endOffset":35921,"count":0}],"isBlockCoverage":false},{"functionName":"atob","ranges":[{"startOffset":36017,"endOffset":36338,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":36664,"endOffset":36699,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":36705,"endOffset":36742,"count":0}],"isBlockCoverage":false}]},{"scriptId":"19","url":"internal/buffer.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":29666,"count":1}],"isBlockCoverage":false},{"functionName":"checkBounds","ranges":[{"startOffset":1107,"endOffset":1323,"count":0}],"isBlockCoverage":false},{"functionName":"checkInt","ranges":[{"startOffset":1325,"endOffset":1947,"count":0}],"isBlockCoverage":false},{"functionName":"boundsError","ranges":[{"startOffset":1949,"endOffset":2352,"count":0}],"isBlockCoverage":false},{"functionName":"readBigUInt64LE","ranges":[{"startOffset":2372,"endOffset":2871,"count":0}],"isBlockCoverage":false},{"functionName":"readBigUInt64BE","ranges":[{"startOffset":2873,"endOffset":3372,"count":0}],"isBlockCoverage":false},{"functionName":"readBigInt64LE","ranges":[{"startOffset":3374,"endOffset":3875,"count":0}],"isBlockCoverage":false},{"functionName":"readBigInt64BE","ranges":[{"startOffset":3877,"endOffset":4372,"count":0}],"isBlockCoverage":false},{"functionName":"readUIntLE","ranges":[{"startOffset":4374,"endOffset":4926,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt48LE","ranges":[{"startOffset":4928,"endOffset":5311,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt40LE","ranges":[{"startOffset":5313,"endOffset":5669,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt32LE","ranges":[{"startOffset":5671,"endOffset":5997,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt24LE","ranges":[{"startOffset":5999,"endOffset":6287,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt16LE","ranges":[{"startOffset":6289,"endOffset":6549,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt8","ranges":[{"startOffset":6551,"endOffset":6731,"count":0}],"isBlockCoverage":false},{"functionName":"readUIntBE","ranges":[{"startOffset":6733,"endOffset":7285,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt48BE","ranges":[{"startOffset":7287,"endOffset":7670,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt40BE","ranges":[{"startOffset":7672,"endOffset":8028,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt32BE","ranges":[{"startOffset":8030,"endOffset":8356,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt24BE","ranges":[{"startOffset":8358,"endOffset":8646,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt16BE","ranges":[{"startOffset":8648,"endOffset":8908,"count":0}],"isBlockCoverage":false},{"functionName":"readIntLE","ranges":[{"startOffset":8910,"endOffset":9455,"count":0}],"isBlockCoverage":false},{"functionName":"readInt48LE","ranges":[{"startOffset":9457,"endOffset":9888,"count":0}],"isBlockCoverage":false},{"functionName":"readInt40LE","ranges":[{"startOffset":9890,"endOffset":10277,"count":0}],"isBlockCoverage":false},{"functionName":"readInt32LE","ranges":[{"startOffset":10279,"endOffset":10614,"count":0}],"isBlockCoverage":false},{"functionName":"readInt24LE","ranges":[{"startOffset":10616,"endOffset":10948,"count":0}],"isBlockCoverage":false},{"functionName":"readInt16LE","ranges":[{"startOffset":10950,"endOffset":11256,"count":0}],"isBlockCoverage":false},{"functionName":"readInt8","ranges":[{"startOffset":11258,"endOffset":11466,"count":0}],"isBlockCoverage":false},{"functionName":"readIntBE","ranges":[{"startOffset":11468,"endOffset":12013,"count":0}],"isBlockCoverage":false},{"functionName":"readInt48BE","ranges":[{"startOffset":12015,"endOffset":12444,"count":0}],"isBlockCoverage":false},{"functionName":"readInt40BE","ranges":[{"startOffset":12446,"endOffset":12834,"count":0}],"isBlockCoverage":false},{"functionName":"readInt32BE","ranges":[{"startOffset":12836,"endOffset":13171,"count":0}],"isBlockCoverage":false},{"functionName":"readInt24BE","ranges":[{"startOffset":13173,"endOffset":13505,"count":0}],"isBlockCoverage":false},{"functionName":"readInt16BE","ranges":[{"startOffset":13507,"endOffset":13813,"count":0}],"isBlockCoverage":false},{"functionName":"readFloatBackwards","ranges":[{"startOffset":13830,"endOffset":14235,"count":0}],"isBlockCoverage":false},{"functionName":"readFloatForwards","ranges":[{"startOffset":14237,"endOffset":14641,"count":0}],"isBlockCoverage":false},{"functionName":"readDoubleBackwards","ranges":[{"startOffset":14643,"endOffset":15213,"count":0}],"isBlockCoverage":false},{"functionName":"readDoubleForwards","ranges":[{"startOffset":15215,"endOffset":15784,"count":0}],"isBlockCoverage":false},{"functionName":"writeBigU_Int64LE","ranges":[{"startOffset":15805,"endOffset":16287,"count":0}],"isBlockCoverage":false},{"functionName":"writeBigUInt64LE","ranges":[{"startOffset":16289,"endOffset":16411,"count":0}],"isBlockCoverage":false},{"functionName":"writeBigU_Int64BE","ranges":[{"startOffset":16413,"endOffset":16911,"count":0}],"isBlockCoverage":false},{"functionName":"writeBigUInt64BE","ranges":[{"startOffset":16913,"endOffset":17035,"count":0}],"isBlockCoverage":false},{"functionName":"writeBigInt64LE","ranges":[{"startOffset":17037,"endOffset":17181,"count":0}],"isBlockCoverage":false},{"functionName":"writeBigInt64BE","ranges":[{"startOffset":17183,"endOffset":17327,"count":0}],"isBlockCoverage":false},{"functionName":"writeUIntLE","ranges":[{"startOffset":17329,"endOffset":17938,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int48LE","ranges":[{"startOffset":17940,"endOffset":18353,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int40LE","ranges":[{"startOffset":18355,"endOffset":18734,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int32LE","ranges":[{"startOffset":18736,"endOffset":19043,"count":0}],"isBlockCoverage":false},{"functionName":"writeUInt32LE","ranges":[{"startOffset":19045,"endOffset":19151,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int24LE","ranges":[{"startOffset":19153,"endOffset":19412,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int16LE","ranges":[{"startOffset":19414,"endOffset":19610,"count":0}],"isBlockCoverage":false},{"functionName":"writeUInt16LE","ranges":[{"startOffset":19612,"endOffset":19714,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int8","ranges":[{"startOffset":19716,"endOffset":20128,"count":0}],"isBlockCoverage":false},{"functionName":"writeUInt8","ranges":[{"startOffset":20130,"endOffset":20224,"count":0}],"isBlockCoverage":false},{"functionName":"writeUIntBE","ranges":[{"startOffset":20226,"endOffset":20835,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int48BE","ranges":[{"startOffset":20837,"endOffset":21258,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int40BE","ranges":[{"startOffset":21260,"endOffset":21622,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int32BE","ranges":[{"startOffset":21624,"endOffset":21939,"count":0}],"isBlockCoverage":false},{"functionName":"writeUInt32BE","ranges":[{"startOffset":21941,"endOffset":22047,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int24BE","ranges":[{"startOffset":22049,"endOffset":22314,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int16BE","ranges":[{"startOffset":22316,"endOffset":22512,"count":0}],"isBlockCoverage":false},{"functionName":"writeUInt16BE","ranges":[{"startOffset":22514,"endOffset":22616,"count":0}],"isBlockCoverage":false},{"functionName":"writeIntLE","ranges":[{"startOffset":22618,"endOffset":23280,"count":0}],"isBlockCoverage":false},{"functionName":"writeInt32LE","ranges":[{"startOffset":23282,"endOffset":23397,"count":0}],"isBlockCoverage":false},{"functionName":"writeInt16LE","ranges":[{"startOffset":23399,"endOffset":23506,"count":0}],"isBlockCoverage":false},{"functionName":"writeInt8","ranges":[{"startOffset":23508,"endOffset":23605,"count":0}],"isBlockCoverage":false},{"functionName":"writeIntBE","ranges":[{"startOffset":23607,"endOffset":24269,"count":0}],"isBlockCoverage":false},{"functionName":"writeInt32BE","ranges":[{"startOffset":24271,"endOffset":24386,"count":0}],"isBlockCoverage":false},{"functionName":"writeInt16BE","ranges":[{"startOffset":24388,"endOffset":24495,"count":0}],"isBlockCoverage":false},{"functionName":"writeDoubleForwards","ranges":[{"startOffset":24514,"endOffset":24980,"count":0}],"isBlockCoverage":false},{"functionName":"writeDoubleBackwards","ranges":[{"startOffset":24982,"endOffset":25449,"count":0}],"isBlockCoverage":false},{"functionName":"writeFloatForwards","ranges":[{"startOffset":25451,"endOffset":25752,"count":0}],"isBlockCoverage":false},{"functionName":"writeFloatBackwards","ranges":[{"startOffset":25754,"endOffset":26056,"count":0}],"isBlockCoverage":false},{"functionName":"addBufferPrototypeMethods","ranges":[{"startOffset":26098,"endOffset":29155,"count":1},{"startOffset":28181,"endOffset":28201,"count":0},{"startOffset":28255,"endOffset":28274,"count":0},{"startOffset":28330,"endOffset":28351,"count":0},{"startOffset":28407,"endOffset":28427,"count":0},{"startOffset":28484,"endOffset":28505,"count":0},{"startOffset":28561,"endOffset":28581,"count":0},{"startOffset":28639,"endOffset":28661,"count":0},{"startOffset":28719,"endOffset":28740,"count":0}],"isBlockCoverage":true},{"functionName":"markAsUntransferable","ranges":[{"startOffset":29311,"endOffset":29575,"count":1},{"startOffset":29379,"endOffset":29407,"count":0},{"startOffset":29430,"endOffset":29437,"count":0}],"isBlockCoverage":true}]},{"scriptId":"20","url":"internal/worker/js_transferable.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1310,"count":1}],"isBlockCoverage":false},{"functionName":"setup","ranges":[{"startOffset":304,"endOffset":1091,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":585,"endOffset":1087,"count":0}],"isBlockCoverage":false}]},{"scriptId":"21","url":"internal/process/per_thread.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":10598,"count":1}],"isBlockCoverage":false},{"functionName":"assert","ranges":[{"startOffset":796,"endOffset":884,"count":0}],"isBlockCoverage":false},{"functionName":"wrapProcessMethods","ranges":[{"startOffset":962,"endOffset":6703,"count":1}],"isBlockCoverage":true},{"functionName":"_rawDebug","ranges":[{"startOffset":1173,"endOffset":1255,"count":0}],"isBlockCoverage":false},{"functionName":"cpuUsage","ranges":[{"startOffset":1466,"endOffset":3025,"count":0}],"isBlockCoverage":false},{"functionName":"previousValueIsValid","ranges":[{"startOffset":3178,"endOffset":3315,"count":0}],"isBlockCoverage":false},{"functionName":"hrtime","ranges":[{"startOffset":3539,"endOffset":4142,"count":0}],"isBlockCoverage":false},{"functionName":"hrtimeBigInt","ranges":[{"startOffset":4329,"endOffset":4423,"count":0}],"isBlockCoverage":false},{"functionName":"memoryUsage","ranges":[{"startOffset":4468,"endOffset":4694,"count":0}],"isBlockCoverage":false},{"functionName":"exit","ranges":[{"startOffset":4698,"endOffset":5136,"count":0}],"isBlockCoverage":false},{"functionName":"kill","ranges":[{"startOffset":5140,"endOffset":5785,"count":0}],"isBlockCoverage":false},{"functionName":"resourceUsage","ranges":[{"startOffset":5836,"endOffset":6569,"count":0}],"isBlockCoverage":false},{"functionName":"buildAllowedFlags","ranges":[{"startOffset":6914,"endOffset":9974,"count":0}],"isBlockCoverage":false},{"functionName":"toggleTraceCategoryState","ranges":[{"startOffset":10164,"endOffset":10494,"count":1},{"startOffset":10244,"endOffset":10419,"count":0},{"startOffset":10451,"endOffset":10492,"count":0}],"isBlockCoverage":true}]},{"scriptId":"22","url":"internal/async_hooks.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":19081,"count":1}],"isBlockCoverage":false},{"functionName":"useDomainTrampoline","ranges":[{"startOffset":5274,"endOffset":5328,"count":0}],"isBlockCoverage":false},{"functionName":"callbackTrampoline","ranges":[{"startOffset":5330,"endOffset":5923,"count":0}],"isBlockCoverage":false},{"functionName":"executionAsyncResource","ranges":[{"startOffset":5999,"endOffset":6497,"count":0}],"isBlockCoverage":false},{"functionName":"inspectExceptionValue","ranges":[{"startOffset":6499,"endOffset":6635,"count":0}],"isBlockCoverage":false},{"functionName":"fatalError","ranges":[{"startOffset":6696,"endOffset":7082,"count":0}],"isBlockCoverage":false},{"functionName":"lookupPublicResource","ranges":[{"startOffset":7084,"endOffset":7433,"count":0}],"isBlockCoverage":false},{"functionName":"emitInitNative","ranges":[{"startOffset":7624,"endOffset":8717,"count":0}],"isBlockCoverage":false},{"functionName":"emitHook","ranges":[{"startOffset":8818,"endOffset":9768,"count":0}],"isBlockCoverage":false},{"functionName":"emitHookFactory","ranges":[{"startOffset":9770,"endOffset":10030,"count":4}],"isBlockCoverage":true},{"functionName":"getHookArrays","ranges":[{"startOffset":10059,"endOffset":10549,"count":0}],"isBlockCoverage":false},{"functionName":"storeActiveHooks","ranges":[{"startOffset":10552,"endOffset":10852,"count":0}],"isBlockCoverage":false},{"functionName":"copyHooks","ranges":[{"startOffset":10854,"endOffset":11119,"count":0}],"isBlockCoverage":false},{"functionName":"restoreActiveHooks","ranges":[{"startOffset":11234,"endOffset":11439,"count":0}],"isBlockCoverage":false},{"functionName":"trackPromise","ranges":[{"startOffset":11441,"endOffset":11798,"count":0}],"isBlockCoverage":false},{"functionName":"fastPromiseHook","ranges":[{"startOffset":11800,"endOffset":12936,"count":0}],"isBlockCoverage":false},{"functionName":"enableHooks","ranges":[{"startOffset":12967,"endOffset":13027,"count":0}],"isBlockCoverage":false},{"functionName":"updatePromiseHookMode","ranges":[{"startOffset":13055,"endOffset":13346,"count":0}],"isBlockCoverage":false},{"functionName":"disableHooks","ranges":[{"startOffset":13348,"endOffset":13623,"count":0}],"isBlockCoverage":false},{"functionName":"disablePromiseHookIfNecessary","ranges":[{"startOffset":13625,"endOffset":13751,"count":0}],"isBlockCoverage":false},{"functionName":"newAsyncId","ranges":[{"startOffset":13952,"endOffset":14022,"count":12}],"isBlockCoverage":true},{"functionName":"getOrSetAsyncId","ranges":[{"startOffset":14024,"endOffset":14214,"count":0}],"isBlockCoverage":false},{"functionName":"getDefaultTriggerAsyncId","ranges":[{"startOffset":14397,"endOffset":14687,"count":12},{"startOffset":14653,"endOffset":14686,"count":0}],"isBlockCoverage":true},{"functionName":"clearDefaultTriggerAsyncId","ranges":[{"startOffset":14690,"endOffset":14779,"count":0}],"isBlockCoverage":false},{"functionName":"defaultTriggerAsyncIdScope","ranges":[{"startOffset":14782,"endOffset":15257,"count":0}],"isBlockCoverage":false},{"functionName":"hasHooks","ranges":[{"startOffset":15259,"endOffset":15322,"count":60}],"isBlockCoverage":true},{"functionName":"enabledHooksExist","ranges":[{"startOffset":15324,"endOffset":15383,"count":12}],"isBlockCoverage":true},{"functionName":"initHooksExist","ranges":[{"startOffset":15385,"endOffset":15440,"count":12}],"isBlockCoverage":true},{"functionName":"afterHooksExist","ranges":[{"startOffset":15442,"endOffset":15499,"count":0}],"isBlockCoverage":false},{"functionName":"destroyHooksExist","ranges":[{"startOffset":15501,"endOffset":15562,"count":12}],"isBlockCoverage":true},{"functionName":"emitInitScript","ranges":[{"startOffset":15565,"endOffset":15973,"count":0}],"isBlockCoverage":false},{"functionName":"emitBeforeScript","ranges":[{"startOffset":15976,"endOffset":16152,"count":12},{"startOffset":16124,"endOffset":16150,"count":0}],"isBlockCoverage":true},{"functionName":"emitAfterScript","ranges":[{"startOffset":16155,"endOffset":16275,"count":12},{"startOffset":16219,"endOffset":16244,"count":0}],"isBlockCoverage":true},{"functionName":"emitDestroyScript","ranges":[{"startOffset":16278,"endOffset":16488,"count":0}],"isBlockCoverage":false},{"functionName":"hasAsyncIdStack","ranges":[{"startOffset":16491,"endOffset":16554,"count":0}],"isBlockCoverage":false},{"functionName":"pushAsyncContext","ranges":[{"startOffset":16620,"endOffset":17190,"count":12},{"startOffset":16840,"endOffset":16890,"count":0}],"isBlockCoverage":true},{"functionName":"popAsyncContext","ranges":[{"startOffset":17255,"endOffset":17879,"count":12},{"startOffset":17371,"endOffset":17384,"count":0},{"startOffset":17463,"endOffset":17569,"count":0}],"isBlockCoverage":true},{"functionName":"executionAsyncId","ranges":[{"startOffset":17882,"endOffset":17958,"count":0}],"isBlockCoverage":false},{"functionName":"triggerAsyncId","ranges":[{"startOffset":17960,"endOffset":18032,"count":0}],"isBlockCoverage":false}]},{"scriptId":"23","url":"internal/process/task_queues.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":4409,"count":1}],"isBlockCoverage":false},{"functionName":"hasTickScheduled","ranges":[{"startOffset":1007,"endOffset":1082,"count":0}],"isBlockCoverage":false},{"functionName":"setHasTickScheduled","ranges":[{"startOffset":1084,"endOffset":1170,"count":24},{"startOffset":1160,"endOffset":1163,"count":12},{"startOffset":1164,"endOffset":1167,"count":12}],"isBlockCoverage":true},{"functionName":"runNextTicks","ranges":[{"startOffset":1272,"endOffset":1468,"count":0}],"isBlockCoverage":false},{"functionName":"processTicksAndRejections","ranges":[{"startOffset":1470,"endOffset":2438,"count":12},{"startOffset":1762,"endOffset":1795,"count":0},{"startOffset":1928,"endOffset":1970,"count":0},{"startOffset":1983,"endOffset":2034,"count":0},{"startOffset":2047,"endOffset":2107,"count":0},{"startOffset":2120,"endOffset":2147,"count":0},{"startOffset":2231,"endOffset":2252,"count":0}],"isBlockCoverage":true},{"functionName":"nextTick","ranges":[{"startOffset":2582,"endOffset":3497,"count":12},{"startOffset":2654,"endOffset":2695,"count":0},{"startOffset":2725,"endOffset":2732,"count":0},{"startOffset":2780,"endOffset":2794,"count":0},{"startOffset":2841,"endOffset":2892,"count":0},{"startOffset":2897,"endOffset":2962,"count":0},{"startOffset":2967,"endOffset":3110,"count":0},{"startOffset":3409,"endOffset":3469,"count":0}],"isBlockCoverage":true},{"functionName":"runMicrotask","ranges":[{"startOffset":3499,"endOffset":3675,"count":0}],"isBlockCoverage":false},{"functionName":"queueMicrotask","ranges":[{"startOffset":3747,"endOffset":4107,"count":0}],"isBlockCoverage":false},{"functionName":"setupTaskQueue","ranges":[{"startOffset":4130,"endOffset":4387,"count":1}],"isBlockCoverage":true}]},{"scriptId":"24","url":"internal/process/promises.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":9605,"count":1}],"isBlockCoverage":false},{"functionName":"setHasRejectionToWarn","ranges":[{"startOffset":1918,"endOffset":2008,"count":12},{"startOffset":1998,"endOffset":2001,"count":0}],"isBlockCoverage":true},{"functionName":"hasRejectionToWarn","ranges":[{"startOffset":2010,"endOffset":2089,"count":0}],"isBlockCoverage":false},{"functionName":"getUnhandledRejectionsMode","ranges":[{"startOffset":2091,"endOffset":2626,"count":0}],"isBlockCoverage":false},{"functionName":"promiseRejectHandler","ranges":[{"startOffset":2628,"endOffset":3197,"count":0}],"isBlockCoverage":false},{"functionName":"resolveError","ranges":[{"startOffset":3199,"endOffset":3449,"count":0}],"isBlockCoverage":false},{"functionName":"unhandledRejection","ranges":[{"startOffset":3451,"endOffset":3745,"count":0}],"isBlockCoverage":false},{"functionName":"handledRejection","ranges":[{"startOffset":3747,"endOffset":4563,"count":0}],"isBlockCoverage":false},{"functionName":"emitUnhandledRejectionWarning","ranges":[{"startOffset":4635,"endOffset":5531,"count":0}],"isBlockCoverage":false},{"functionName":"emitDeprecationWarning","ranges":[{"startOffset":5564,"endOffset":5849,"count":0}],"isBlockCoverage":false},{"functionName":"processPromiseRejections","ranges":[{"startOffset":6022,"endOffset":8506,"count":12},{"startOffset":6180,"endOffset":6346,"count":0},{"startOffset":6411,"endOffset":8410,"count":0}],"isBlockCoverage":true},{"functionName":"getErrorWithoutStack","ranges":[{"startOffset":8508,"endOffset":8926,"count":0}],"isBlockCoverage":false},{"functionName":"generateUnhandledRejectionError","ranges":[{"startOffset":8928,"endOffset":9398,"count":0}],"isBlockCoverage":false},{"functionName":"listenForRejections","ranges":[{"startOffset":9400,"endOffset":9484,"count":1}],"isBlockCoverage":true}]},{"scriptId":"25","url":"internal/fixed_queue.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":4184,"count":1}],"isBlockCoverage":false},{"functionName":"FixedCircularBuffer","ranges":[{"startOffset":2959,"endOffset":3073,"count":1}],"isBlockCoverage":true},{"functionName":"isEmpty","ranges":[{"startOffset":3077,"endOffset":3129,"count":48}],"isBlockCoverage":true},{"functionName":"isFull","ranges":[{"startOffset":3133,"endOffset":3200,"count":12}],"isBlockCoverage":true},{"functionName":"push","ranges":[{"startOffset":3204,"endOffset":3291,"count":12}],"isBlockCoverage":true},{"functionName":"shift","ranges":[{"startOffset":3295,"endOffset":3510,"count":24},{"startOffset":3388,"endOffset":3509,"count":12}],"isBlockCoverage":true},{"functionName":"FixedQueue","ranges":[{"startOffset":3552,"endOffset":3626,"count":1}],"isBlockCoverage":true},{"functionName":"isEmpty","ranges":[{"startOffset":3630,"endOffset":3677,"count":24}],"isBlockCoverage":true},{"functionName":"push","ranges":[{"startOffset":3681,"endOffset":3945,"count":12},{"startOffset":3722,"endOffset":3915,"count":0}],"isBlockCoverage":true},{"functionName":"shift","ranges":[{"startOffset":3949,"endOffset":4180,"count":24},{"startOffset":4064,"endOffset":4159,"count":0}],"isBlockCoverage":true}]},{"scriptId":"26","url":"async_hooks.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":9502,"count":1}],"isBlockCoverage":false},{"functionName":"AsyncHook","ranges":[{"startOffset":1416,"endOffset":2250,"count":1},{"startOffset":1544,"endOffset":1586,"count":0},{"startOffset":1616,"endOffset":1647,"count":0},{"startOffset":1655,"endOffset":1699,"count":0},{"startOffset":1728,"endOffset":1758,"count":0},{"startOffset":1766,"endOffset":1809,"count":0},{"startOffset":1840,"endOffset":1872,"count":0},{"startOffset":1880,"endOffset":1925,"count":0},{"startOffset":1963,"endOffset":2002,"count":0},{"startOffset":2010,"endOffset":2062,"count":0}],"isBlockCoverage":true},{"functionName":"enable","ranges":[{"startOffset":2254,"endOffset":3491,"count":0}],"isBlockCoverage":false},{"functionName":"disable","ranges":[{"startOffset":3495,"endOffset":4271,"count":0}],"isBlockCoverage":false},{"functionName":"createHook","ranges":[{"startOffset":4276,"endOffset":4333,"count":1}],"isBlockCoverage":true},{"functionName":"AsyncResource","ranges":[{"startOffset":4426,"endOffset":5613,"count":0}],"isBlockCoverage":false},{"functionName":"runInAsyncScope","ranges":[{"startOffset":5617,"endOffset":5979,"count":0}],"isBlockCoverage":false},{"functionName":"emitDestroy","ranges":[{"startOffset":5983,"endOffset":6158,"count":0}],"isBlockCoverage":false},{"functionName":"asyncId","ranges":[{"startOffset":6162,"endOffset":6211,"count":0}],"isBlockCoverage":false},{"functionName":"triggerAsyncId","ranges":[{"startOffset":6215,"endOffset":6279,"count":0}],"isBlockCoverage":false},{"functionName":"bind","ranges":[{"startOffset":6283,"endOffset":6785,"count":0}],"isBlockCoverage":false},{"functionName":"bind","ranges":[{"startOffset":6796,"endOffset":6915,"count":0}],"isBlockCoverage":false},{"functionName":"init","ranges":[{"startOffset":6978,"endOffset":7260,"count":0}],"isBlockCoverage":false},{"functionName":"AsyncLocalStorage","ranges":[{"startOffset":7357,"endOffset":7454,"count":0}],"isBlockCoverage":false},{"functionName":"disable","ranges":[{"startOffset":7458,"endOffset":7783,"count":0}],"isBlockCoverage":false},{"functionName":"_enable","ranges":[{"startOffset":7787,"endOffset":7933,"count":0}],"isBlockCoverage":false},{"functionName":"_propagate","ranges":[{"startOffset":8002,"endOffset":8176,"count":0}],"isBlockCoverage":false},{"functionName":"enterWith","ranges":[{"startOffset":8180,"endOffset":8312,"count":0}],"isBlockCoverage":false},{"functionName":"run","ranges":[{"startOffset":8316,"endOffset":8892,"count":0}],"isBlockCoverage":false},{"functionName":"exit","ranges":[{"startOffset":8896,"endOffset":9094,"count":0}],"isBlockCoverage":false},{"functionName":"getStore","ranges":[{"startOffset":9098,"endOffset":9237,"count":0}],"isBlockCoverage":false}]},{"scriptId":"27","url":"internal/console/global.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1939,"count":1}],"isBlockCoverage":false}]},{"scriptId":"28","url":"internal/console/constructor.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":19933,"count":1}],"isBlockCoverage":false},{"functionName":"Console","ranges":[{"startOffset":2592,"endOffset":4763,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":4972,"endOffset":5026,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":5274,"endOffset":5480,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":5683,"endOffset":6271,"count":1}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":5865,"endOffset":5960,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":5972,"endOffset":6002,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":6104,"endOffset":6203,"count":627},{"startOffset":6137,"endOffset":6164,"count":1}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":6215,"endOffset":6245,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":6341,"endOffset":7523,"count":1}],"isBlockCoverage":true},{"functionName":"value","ranges":[{"startOffset":7593,"endOffset":9039,"count":209},{"startOffset":7804,"endOffset":7818,"count":0},{"startOffset":7872,"endOffset":7906,"count":0},{"startOffset":7972,"endOffset":8161,"count":0},{"startOffset":8219,"endOffset":8247,"count":0},{"startOffset":8604,"endOffset":8631,"count":198},{"startOffset":8685,"endOffset":8969,"count":0}],"isBlockCoverage":true},{"functionName":"value","ranges":[{"startOffset":9112,"endOffset":9602,"count":209},{"startOffset":9226,"endOffset":9332,"count":0},{"startOffset":9406,"endOffset":9527,"count":0},{"startOffset":9548,"endOffset":9570,"count":0}],"isBlockCoverage":true},{"functionName":"value","ranges":[{"startOffset":9673,"endOffset":9801,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":9872,"endOffset":10000,"count":209}],"isBlockCoverage":true},{"functionName":"createWriteErrorHandler","ranges":[{"startOffset":10089,"endOffset":10978,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":10157,"endOffset":10975,"count":209},{"startOffset":10402,"endOffset":10426,"count":0},{"startOffset":10920,"endOffset":10965,"count":12}],"isBlockCoverage":true},{"functionName":"log","ranges":[{"startOffset":11007,"endOffset":11094,"count":0}],"isBlockCoverage":false},{"functionName":"warn","ranges":[{"startOffset":11100,"endOffset":11188,"count":209}],"isBlockCoverage":true},{"functionName":"dir","ranges":[{"startOffset":11194,"endOffset":11379,"count":0}],"isBlockCoverage":false},{"functionName":"time","ranges":[{"startOffset":11384,"endOffset":11742,"count":0}],"isBlockCoverage":false},{"functionName":"timeEnd","ranges":[{"startOffset":11747,"endOffset":12036,"count":0}],"isBlockCoverage":false},{"functionName":"timeLog","ranges":[{"startOffset":12041,"endOffset":12279,"count":0}],"isBlockCoverage":false},{"functionName":"trace","ranges":[{"startOffset":12291,"endOffset":12477,"count":0}],"isBlockCoverage":false},{"functionName":"assert","ranges":[{"startOffset":12482,"endOffset":12700,"count":0}],"isBlockCoverage":false},{"functionName":"clear","ranges":[{"startOffset":12761,"endOffset":13191,"count":0}],"isBlockCoverage":false},{"functionName":"count","ranges":[{"startOffset":13252,"endOffset":13708,"count":0}],"isBlockCoverage":false},{"functionName":"countReset","ranges":[{"startOffset":13774,"endOffset":14062,"count":0}],"isBlockCoverage":false},{"functionName":"group","ranges":[{"startOffset":14067,"endOffset":14235,"count":0}],"isBlockCoverage":false},{"functionName":"groupEnd","ranges":[{"startOffset":14240,"endOffset":14408,"count":0}],"isBlockCoverage":false},{"functionName":"table","ranges":[{"startOffset":14457,"endOffset":17867,"count":0}],"isBlockCoverage":false},{"functionName":"timeLogImpl","ranges":[{"startOffset":17908,"endOffset":18404,"count":0}],"isBlockCoverage":false},{"functionName":"pad","ranges":[{"startOffset":18406,"endOffset":18483,"count":0}],"isBlockCoverage":false},{"functionName":"formatTime","ranges":[{"startOffset":18485,"endOffset":19247,"count":0}],"isBlockCoverage":false},{"functionName":"isArray","ranges":[{"startOffset":19381,"endOffset":19437,"count":0}],"isBlockCoverage":false},{"functionName":"noop","ranges":[{"startOffset":19440,"endOffset":19458,"count":0}],"isBlockCoverage":false}]},{"scriptId":"29","url":"internal/constants.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1582,"count":1}],"isBlockCoverage":false}]},{"scriptId":"30","url":"internal/util/inspector.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2215,"count":1}],"isBlockCoverage":false},{"functionName":"sendInspectorCommand","ranges":[{"startOffset":92,"endOffset":434,"count":0}],"isBlockCoverage":false},{"functionName":"installConsoleExtensions","ranges":[{"startOffset":508,"endOffset":1062,"count":0}],"isBlockCoverage":false},{"functionName":"wrapConsole","ranges":[{"startOffset":1141,"endOffset":1931,"count":1},{"startOffset":1299,"endOffset":1929,"count":23},{"startOffset":1514,"endOffset":1807,"count":19},{"startOffset":1807,"endOffset":1925,"count":4}],"isBlockCoverage":true},{"functionName":"get consoleFromVM","ranges":[{"startOffset":2103,"endOffset":2154,"count":0}],"isBlockCoverage":false},{"functionName":"set consoleFromVM","ranges":[{"startOffset":2158,"endOffset":2211,"count":1}],"isBlockCoverage":true}]},{"scriptId":"31","url":"internal/url.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":41325,"count":1}],"isBlockCoverage":false},{"functionName":"toUSVString","ranges":[{"startOffset":2224,"endOffset":2520,"count":2},{"startOffset":2477,"endOffset":2519,"count":0}],"isBlockCoverage":true},{"functionName":"serializeTupleOrigin","ranges":[{"startOffset":2732,"endOffset":2850,"count":0}],"isBlockCoverage":false},{"functionName":"URLContext","ranges":[{"startOffset":3254,"endOffset":3477,"count":24}],"isBlockCoverage":true},{"functionName":"URLSearchParams","ranges":[{"startOffset":3767,"endOffset":6130,"count":18},{"startOffset":3882,"endOffset":6068,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":6134,"endOffset":7204,"count":0}],"isBlockCoverage":false},{"functionName":"onParseComplete","ranges":[{"startOffset":7208,"endOffset":7901,"count":18},{"startOffset":7463,"endOffset":7473,"count":0},{"startOffset":7536,"endOffset":7546,"count":0},{"startOffset":7627,"endOffset":7631,"count":0}],"isBlockCoverage":true},{"functionName":"onParseError","ranges":[{"startOffset":7903,"endOffset":7978,"count":6}],"isBlockCoverage":true},{"functionName":"onParseProtocolComplete","ranges":[{"startOffset":7980,"endOffset":8325,"count":0}],"isBlockCoverage":false},{"functionName":"onParseHostnameComplete","ranges":[{"startOffset":8327,"endOffset":8673,"count":0}],"isBlockCoverage":false},{"functionName":"onParsePortComplete","ranges":[{"startOffset":8675,"endOffset":8837,"count":0}],"isBlockCoverage":false},{"functionName":"onParseHostComplete","ranges":[{"startOffset":8839,"endOffset":9145,"count":0}],"isBlockCoverage":false},{"functionName":"onParsePathComplete","ranges":[{"startOffset":9147,"endOffset":9641,"count":4},{"startOffset":9413,"endOffset":9481,"count":0}],"isBlockCoverage":true},{"functionName":"onParseSearchComplete","ranges":[{"startOffset":9643,"endOffset":9811,"count":0}],"isBlockCoverage":false},{"functionName":"onParseHashComplete","ranges":[{"startOffset":9813,"endOffset":9983,"count":0}],"isBlockCoverage":false},{"functionName":"URL","ranges":[{"startOffset":9999,"endOffset":10327,"count":24},{"startOffset":10134,"endOffset":10186,"count":3}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":10331,"endOffset":10412,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":10416,"endOffset":10509,"count":54}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":10584,"endOffset":10784,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":10788,"endOffset":11721,"count":0}],"isBlockCoverage":false},{"functionName":"format","ranges":[{"startOffset":11890,"endOffset":13071,"count":18},{"startOffset":11975,"endOffset":12036,"count":0},{"startOffset":12446,"endOffset":12607,"count":0},{"startOffset":12639,"endOffset":12676,"count":0},{"startOffset":12730,"endOffset":12752,"count":0},{"startOffset":12760,"endOffset":12824,"count":0},{"startOffset":12936,"endOffset":12959,"count":0},{"startOffset":13021,"endOffset":13047,"count":0}],"isBlockCoverage":true},{"functionName":"toString","ranges":[{"startOffset":13345,"endOffset":13404,"count":2}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":13470,"endOffset":13515,"count":16}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":13521,"endOffset":13701,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":13782,"endOffset":14410,"count":10},{"startOffset":13922,"endOffset":14166,"count":0},{"startOffset":14175,"endOffset":14187,"count":0},{"startOffset":14196,"endOffset":14211,"count":0},{"startOffset":14220,"endOffset":14233,"count":0},{"startOffset":14242,"endOffset":14256,"count":0},{"startOffset":14265,"endOffset":14276,"count":0},{"startOffset":14285,"endOffset":14368,"count":0}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":14480,"endOffset":14528,"count":17}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":14534,"endOffset":14903,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":14973,"endOffset":15023,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":15029,"endOffset":15427,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":15497,"endOffset":15547,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":15553,"endOffset":15951,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":16017,"endOffset":16173,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":16179,"endOffset":16470,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":16540,"endOffset":16592,"count":8}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":16598,"endOffset":16897,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":16963,"endOffset":17063,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":17069,"endOffset":17386,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":17456,"endOffset":17651,"count":50},{"startOffset":17535,"endOffset":17554,"count":0},{"startOffset":17596,"endOffset":17606,"count":0}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":17657,"endOffset":17876,"count":4},{"startOffset":17767,"endOffset":17774,"count":0}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":17944,"endOffset":18083,"count":2},{"startOffset":18016,"endOffset":18031,"count":0},{"startOffset":18051,"endOffset":18082,"count":0}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":18089,"endOffset":18581,"count":2},{"startOffset":18275,"endOffset":18523,"count":0}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":18668,"endOffset":18714,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":18780,"endOffset":18931,"count":2},{"startOffset":18858,"endOffset":18876,"count":0},{"startOffset":18896,"endOffset":18930,"count":0}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":18937,"endOffset":19355,"count":2},{"startOffset":19159,"endOffset":19354,"count":0}],"isBlockCoverage":true},{"functionName":"toJSON","ranges":[{"startOffset":19501,"endOffset":19558,"count":0}],"isBlockCoverage":false},{"functionName":"update","ranges":[{"startOffset":19568,"endOffset":19873,"count":0}],"isBlockCoverage":false},{"functionName":"initSearchParams","ranges":[{"startOffset":19875,"endOffset":20015,"count":20},{"startOffset":19972,"endOffset":20014,"count":0}],"isBlockCoverage":true},{"functionName":"parseParams","ranges":[{"startOffset":20124,"endOffset":22425,"count":0}],"isBlockCoverage":false},{"functionName":"serializeParams","ranges":[{"startOffset":23404,"endOffset":23964,"count":0}],"isBlockCoverage":false},{"functionName":"defineIDLClass","ranges":[{"startOffset":24019,"endOffset":24707,"count":2},{"startOffset":24357,"endOffset":24503,"count":13},{"startOffset":24558,"endOffset":24705,"count":1}],"isBlockCoverage":true},{"functionName":"merge","ranges":[{"startOffset":24727,"endOffset":25357,"count":0}],"isBlockCoverage":false},{"functionName":"append","ranges":[{"startOffset":25424,"endOffset":25811,"count":0}],"isBlockCoverage":false},{"functionName":"delete","ranges":[{"startOffset":25816,"endOffset":26315,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":26320,"endOffset":26754,"count":0}],"isBlockCoverage":false},{"functionName":"getAll","ranges":[{"startOffset":26759,"endOffset":27227,"count":0}],"isBlockCoverage":false},{"functionName":"has","ranges":[{"startOffset":27232,"endOffset":27660,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":27665,"endOffset":28691,"count":0}],"isBlockCoverage":false},{"functionName":"sort","ranges":[{"startOffset":28696,"endOffset":29873,"count":0}],"isBlockCoverage":false},{"functionName":"entries","ranges":[{"startOffset":30036,"endOffset":30245,"count":0}],"isBlockCoverage":false},{"functionName":"forEach","ranges":[{"startOffset":30250,"endOffset":30822,"count":0}],"isBlockCoverage":false},{"functionName":"keys","ranges":[{"startOffset":30877,"endOffset":31077,"count":0}],"isBlockCoverage":false},{"functionName":"values","ranges":[{"startOffset":31082,"endOffset":31286,"count":0}],"isBlockCoverage":false},{"functionName":"toString","ranges":[{"startOffset":31419,"endOffset":31619,"count":0}],"isBlockCoverage":false},{"functionName":"createSearchParamsIterator","ranges":[{"startOffset":31899,"endOffset":32102,"count":0}],"isBlockCoverage":false},{"functionName":"next","ranges":[{"startOffset":32326,"endOffset":33079,"count":0}],"isBlockCoverage":false},{"functionName":"defineIDLClass","ranges":[{"startOffset":33083,"endOffset":34297,"count":0}],"isBlockCoverage":false},{"functionName":"domainToASCII","ranges":[{"startOffset":34303,"endOffset":34478,"count":0}],"isBlockCoverage":false},{"functionName":"domainToUnicode","ranges":[{"startOffset":34480,"endOffset":34659,"count":0}],"isBlockCoverage":false},{"functionName":"urlToOptions","ranges":[{"startOffset":34802,"endOffset":35345,"count":0}],"isBlockCoverage":false},{"functionName":"getPathFromURLWin32","ranges":[{"startOffset":35381,"endOffset":36774,"count":0}],"isBlockCoverage":false},{"functionName":"getPathFromURLPosix","ranges":[{"startOffset":36776,"endOffset":37280,"count":8},{"startOffset":36839,"endOffset":36895,"count":0},{"startOffset":36973,"endOffset":37239,"count":328},{"startOffset":37004,"endOffset":37235,"count":0}],"isBlockCoverage":true},{"functionName":"fileURLToPath","ranges":[{"startOffset":37282,"endOffset":37629,"count":8},{"startOffset":37349,"endOffset":37370,"count":0},{"startOffset":37408,"endOffset":37472,"count":0},{"startOffset":37510,"endOffset":37551,"count":0},{"startOffset":37571,"endOffset":37598,"count":0}],"isBlockCoverage":true},{"functionName":"encodePathChars","ranges":[{"startOffset":38389,"endOffset":38945,"count":4},{"startOffset":38460,"endOffset":38509,"count":0},{"startOffset":38615,"endOffset":38666,"count":0},{"startOffset":38702,"endOffset":38751,"count":0},{"startOffset":38787,"endOffset":38843,"count":0},{"startOffset":38879,"endOffset":38924,"count":0}],"isBlockCoverage":true},{"functionName":"pathToFileURL","ranges":[{"startOffset":38947,"endOffset":40062,"count":4},{"startOffset":39035,"endOffset":39065,"count":0},{"startOffset":39067,"endOffset":39616,"count":0},{"startOffset":39848,"endOffset":39911,"count":3},{"startOffset":39871,"endOffset":39910,"count":0},{"startOffset":39913,"endOffset":39966,"count":1},{"startOffset":39974,"endOffset":39990,"count":1}],"isBlockCoverage":true},{"functionName":"isURLInstance","ranges":[{"startOffset":40064,"endOffset":40183,"count":32},{"startOffset":40157,"endOffset":40180,"count":10}],"isBlockCoverage":true},{"functionName":"toPathIfFileURL","ranges":[{"startOffset":40185,"endOffset":40330,"count":24},{"startOffset":40268,"endOffset":40289,"count":22},{"startOffset":40289,"endOffset":40329,"count":2}],"isBlockCoverage":true},{"functionName":"constructUrl","ranges":[{"startOffset":40332,"endOffset":41032,"count":0}],"isBlockCoverage":false}]},{"scriptId":"32","url":"internal/querystring.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":3021,"count":1}],"isBlockCoverage":false},{"functionName":"encodeStr","ranges":[{"startOffset":1336,"endOffset":2959,"count":0}],"isBlockCoverage":false}]},{"scriptId":"33","url":"path.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":43399,"count":1}],"isBlockCoverage":false},{"functionName":"isPathSeparator","ranges":[{"startOffset":1492,"endOffset":1596,"count":0}],"isBlockCoverage":false},{"functionName":"isPosixPathSeparator","ranges":[{"startOffset":1598,"endOffset":1675,"count":705}],"isBlockCoverage":true},{"functionName":"isWindowsDeviceRoot","ranges":[{"startOffset":1677,"endOffset":1847,"count":0}],"isBlockCoverage":false},{"functionName":"normalizeString","ranges":[{"startOffset":1910,"endOffset":3714,"count":17},{"startOffset":2127,"endOffset":3698,"count":705},{"startOffset":2160,"endOffset":2186,"count":688},{"startOffset":2186,"endOffset":2277,"count":17},{"startOffset":2235,"endOffset":2277,"count":0},{"startOffset":2277,"endOffset":2310,"count":688},{"startOffset":2310,"endOffset":3599,"count":120},{"startOffset":2342,"endOffset":2355,"count":100},{"startOffset":2357,"endOffset":2382,"count":20},{"startOffset":2382,"endOffset":3556,"count":100},{"startOffset":2404,"endOffset":3343,"count":2},{"startOffset":2460,"endOffset":2518,"count":0},{"startOffset":2519,"endOffset":2577,"count":0},{"startOffset":2715,"endOffset":2791,"count":0},{"startOffset":3024,"endOffset":3193,"count":0},{"startOffset":3203,"endOffset":3335,"count":0},{"startOffset":3343,"endOffset":3556,"count":98},{"startOffset":3389,"endOffset":3442,"count":81},{"startOffset":3442,"endOffset":3501,"count":17},{"startOffset":3556,"endOffset":3599,"count":118},{"startOffset":3599,"endOffset":3694,"count":568},{"startOffset":3627,"endOffset":3641,"count":22},{"startOffset":3643,"endOffset":3664,"count":7},{"startOffset":3664,"endOffset":3694,"count":561}],"isBlockCoverage":true},{"functionName":"_format","ranges":[{"startOffset":3892,"endOffset":4317,"count":0}],"isBlockCoverage":false},{"functionName":"resolve","ranges":[{"startOffset":4435,"endOffset":8838,"count":0}],"isBlockCoverage":false},{"functionName":"normalize","ranges":[{"startOffset":8904,"endOffset":11655,"count":0}],"isBlockCoverage":false},{"functionName":"isAbsolute","ranges":[{"startOffset":11722,"endOffset":12090,"count":0}],"isBlockCoverage":false},{"functionName":"join","ranges":[{"startOffset":12159,"endOffset":14215,"count":0}],"isBlockCoverage":false},{"functionName":"relative","ranges":[{"startOffset":14512,"endOffset":17986,"count":0}],"isBlockCoverage":false},{"functionName":"toNamespacedPath","ranges":[{"startOffset":17991,"endOffset":19019,"count":0}],"isBlockCoverage":false},{"functionName":"dirname","ranges":[{"startOffset":19085,"endOffset":21413,"count":0}],"isBlockCoverage":false},{"functionName":"basename","ranges":[{"startOffset":21506,"endOffset":24141,"count":0}],"isBlockCoverage":false},{"functionName":"extname","ranges":[{"startOffset":24207,"endOffset":26216,"count":0}],"isBlockCoverage":false},{"functionName":"parse","ranges":[{"startOffset":26420,"endOffset":30886,"count":0}],"isBlockCoverage":false},{"functionName":"resolve","ranges":[{"startOffset":31069,"endOffset":31965,"count":17},{"startOffset":31192,"endOffset":31212,"count":42},{"startOffset":31219,"endOffset":31519,"count":26},{"startOffset":31247,"endOffset":31256,"count":25},{"startOffset":31257,"endOffset":31272,"count":1},{"startOffset":31369,"endOffset":31396,"count":0},{"startOffset":31904,"endOffset":31954,"count":0},{"startOffset":31955,"endOffset":31960,"count":0}],"isBlockCoverage":true},{"functionName":"normalize","ranges":[{"startOffset":32031,"endOffset":32606,"count":0}],"isBlockCoverage":false},{"functionName":"isAbsolute","ranges":[{"startOffset":32673,"endOffset":32802,"count":4}],"isBlockCoverage":true},{"functionName":"join","ranges":[{"startOffset":32871,"endOffset":33285,"count":0}],"isBlockCoverage":false},{"functionName":"relative","ranges":[{"startOffset":33375,"endOffset":35585,"count":0}],"isBlockCoverage":false},{"functionName":"toNamespacedPath","ranges":[{"startOffset":35590,"endOffset":35666,"count":35}],"isBlockCoverage":true},{"functionName":"dirname","ranges":[{"startOffset":35732,"endOffset":36360,"count":0}],"isBlockCoverage":false},{"functionName":"basename","ranges":[{"startOffset":36453,"endOffset":38773,"count":0}],"isBlockCoverage":false},{"functionName":"extname","ranges":[{"startOffset":38839,"endOffset":40497,"count":2},{"startOffset":39172,"endOffset":40118,"count":18},{"startOffset":39252,"endOffset":39498,"count":2},{"startOffset":39472,"endOffset":39498,"count":0},{"startOffset":39498,"endOffset":39521,"count":16},{"startOffset":39521,"endOffset":39678,"count":2},{"startOffset":39678,"endOffset":39708,"count":16},{"startOffset":39708,"endOffset":39908,"count":2},{"startOffset":39837,"endOffset":39900,"count":0},{"startOffset":39908,"endOffset":40112,"count":14},{"startOffset":39935,"endOffset":40112,"count":10},{"startOffset":40357,"endOffset":40389,"count":0},{"startOffset":40390,"endOffset":40428,"count":0},{"startOffset":40431,"endOffset":40455,"count":0}],"isBlockCoverage":true},{"functionName":"parse","ranges":[{"startOffset":40706,"endOffset":43060,"count":0}],"isBlockCoverage":false}]},{"scriptId":"34","url":"internal/encoding.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":15916,"count":1}],"isBlockCoverage":false},{"functionName":"lazyBuffer","ranges":[{"startOffset":958,"endOffset":1067,"count":0}],"isBlockCoverage":false},{"functionName":"validateEncoder","ranges":[{"startOffset":1069,"endOffset":1194,"count":0}],"isBlockCoverage":false},{"functionName":"validateDecoder","ranges":[{"startOffset":1196,"endOffset":1321,"count":2},{"startOffset":1277,"endOffset":1319,"count":0}],"isBlockCoverage":true},{"functionName":"validateArgument","ranges":[{"startOffset":1323,"endOffset":1533,"count":3},{"startOffset":1470,"endOffset":1531,"count":0}],"isBlockCoverage":true},{"functionName":"trimAsciiWhitespace","ranges":[{"startOffset":8274,"endOffset":8756,"count":0}],"isBlockCoverage":false},{"functionName":"getEncodingFromLabel","ranges":[{"startOffset":8758,"endOffset":8937,"count":1},{"startOffset":8869,"endOffset":8936,"count":0}],"isBlockCoverage":true},{"functionName":"TextEncoder","ranges":[{"startOffset":9008,"endOffset":9054,"count":0}],"isBlockCoverage":false},{"functionName":"get encoding","ranges":[{"startOffset":9058,"endOffset":9125,"count":0}],"isBlockCoverage":false},{"functionName":"encode","ranges":[{"startOffset":9129,"endOffset":9221,"count":0}],"isBlockCoverage":false},{"functionName":"encodeInto","ranges":[{"startOffset":9225,"endOffset":9535,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":9539,"endOffset":9934,"count":0}],"isBlockCoverage":false},{"functionName":"makeTextDecoderICU","ranges":[{"startOffset":10305,"endOffset":11966,"count":1}],"isBlockCoverage":true},{"functionName":"TextDecoder","ranges":[{"startOffset":10443,"endOffset":11170,"count":1},{"startOffset":10675,"endOffset":10722,"count":0},{"startOffset":10806,"endOffset":10829,"count":0},{"startOffset":10870,"endOffset":10898,"count":0},{"startOffset":11000,"endOffset":11047,"count":0}],"isBlockCoverage":true},{"functionName":"decode","ranges":[{"startOffset":11177,"endOffset":11937,"count":2},{"startOffset":11279,"endOffset":11330,"count":0},{"startOffset":11367,"endOffset":11546,"count":0},{"startOffset":11693,"endOffset":11696,"count":0},{"startOffset":11814,"endOffset":11896,"count":0}],"isBlockCoverage":true},{"functionName":"makeTextDecoderJS","ranges":[{"startOffset":11968,"endOffset":14509,"count":0}],"isBlockCoverage":false},{"functionName":"get encoding","ranges":[{"startOffset":14634,"endOffset":14715,"count":0}],"isBlockCoverage":false},{"functionName":"get fatal","ranges":[{"startOffset":14722,"endOffset":14849,"count":0}],"isBlockCoverage":false},{"functionName":"get ignoreBOM","ranges":[{"startOffset":14856,"endOffset":15011,"count":0}],"isBlockCoverage":false},{"functionName":"ObjectGetOwnPropertyDescriptors","ranges":[{"startOffset":15018,"endOffset":15632,"count":0}],"isBlockCoverage":false}]},{"scriptId":"35","url":"timers.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":8324,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":1797,"endOffset":1822,"count":0}],"isBlockCoverage":false},{"functionName":"unenroll","ranges":[{"startOffset":2319,"endOffset":3503,"count":0}],"isBlockCoverage":false},{"functionName":"enroll","ranges":[{"startOffset":3710,"endOffset":3963,"count":0}],"isBlockCoverage":false},{"functionName":"setTimeout","ranges":[{"startOffset":3994,"endOffset":4639,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":4716,"endOffset":4850,"count":0}],"isBlockCoverage":false},{"functionName":"clearTimeout","ranges":[{"startOffset":4856,"endOffset":5222,"count":0}],"isBlockCoverage":false},{"functionName":"setInterval","ranges":[{"startOffset":5224,"endOffset":5871,"count":0}],"isBlockCoverage":false},{"functionName":"clearInterval","ranges":[{"startOffset":5873,"endOffset":6171,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.close","ranges":[{"startOffset":6199,"endOffset":6250,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.","ranges":[{"startOffset":6292,"endOffset":6453,"count":0}],"isBlockCoverage":false},{"functionName":"setImmediate","ranges":[{"startOffset":6456,"endOffset":6997,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":7076,"endOffset":7212,"count":0}],"isBlockCoverage":false},{"functionName":"clearImmediate","ranges":[{"startOffset":7219,"endOffset":7685,"count":0}],"isBlockCoverage":false}]},{"scriptId":"36","url":"internal/linkedlist.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1112,"count":1}],"isBlockCoverage":false},{"functionName":"init","ranges":[{"startOffset":15,"endOffset":88,"count":0}],"isBlockCoverage":false},{"functionName":"peek","ranges":[{"startOffset":118,"endOffset":210,"count":0}],"isBlockCoverage":false},{"functionName":"remove","ranges":[{"startOffset":245,"endOffset":472,"count":0}],"isBlockCoverage":false},{"functionName":"append","ranges":[{"startOffset":528,"endOffset":980,"count":0}],"isBlockCoverage":false},{"functionName":"isEmpty","ranges":[{"startOffset":982,"endOffset":1042,"count":0}],"isBlockCoverage":false}]},{"scriptId":"37","url":"internal/timers.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":18495,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":4398,"endOffset":4423,"count":0}],"isBlockCoverage":false},{"functionName":"initAsyncResource","ranges":[{"startOffset":5366,"endOffset":5644,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout","ranges":[{"startOffset":5729,"endOffset":6726,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.","ranges":[{"startOffset":6839,"endOffset":7010,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.refresh","ranges":[{"startOffset":7041,"endOffset":7139,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.unref","ranges":[{"startOffset":7168,"endOffset":7296,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.ref","ranges":[{"startOffset":7323,"endOffset":7451,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.hasRef","ranges":[{"startOffset":7481,"endOffset":7518,"count":0}],"isBlockCoverage":false},{"functionName":"TimersList","ranges":[{"startOffset":7521,"endOffset":7821,"count":0}],"isBlockCoverage":false},{"functionName":"TimersList.","ranges":[{"startOffset":7937,"endOffset":8108,"count":0}],"isBlockCoverage":false},{"functionName":"ImmediateList","ranges":[{"startOffset":8166,"endOffset":8234,"count":2}],"isBlockCoverage":true},{"functionName":"ImmediateList.append","ranges":[{"startOffset":8413,"endOffset":8580,"count":0}],"isBlockCoverage":false},{"functionName":"ImmediateList.remove","ranges":[{"startOffset":8758,"endOffset":9095,"count":0}],"isBlockCoverage":false},{"functionName":"incRefCount","ranges":[{"startOffset":9098,"endOffset":9174,"count":0}],"isBlockCoverage":false},{"functionName":"decRefCount","ranges":[{"startOffset":9176,"endOffset":9253,"count":0}],"isBlockCoverage":false},{"functionName":"active","ranges":[{"startOffset":9336,"endOffset":9390,"count":0}],"isBlockCoverage":false},{"functionName":"unrefActive","ranges":[{"startOffset":9537,"endOffset":9597,"count":0}],"isBlockCoverage":false},{"functionName":"insertGuarded","ranges":[{"startOffset":9818,"endOffset":10334,"count":0}],"isBlockCoverage":false},{"functionName":"insert","ranges":[{"startOffset":10336,"endOffset":10987,"count":0}],"isBlockCoverage":false},{"functionName":"setUnrefTimeout","ranges":[{"startOffset":10989,"endOffset":11295,"count":0}],"isBlockCoverage":false},{"functionName":"getTimerDuration","ranges":[{"startOffset":11362,"endOffset":11884,"count":0}],"isBlockCoverage":false},{"functionName":"compareTimersLists","ranges":[{"startOffset":11886,"endOffset":12091,"count":0}],"isBlockCoverage":false},{"functionName":"setPosition","ranges":[{"startOffset":12093,"endOffset":12164,"count":0}],"isBlockCoverage":false},{"functionName":"getTimerCallbacks","ranges":[{"startOffset":12166,"endOffset":17263,"count":1}],"isBlockCoverage":true},{"functionName":"processImmediate","ranges":[{"startOffset":12478,"endOffset":14279,"count":0}],"isBlockCoverage":false},{"functionName":"processTimers","ranges":[{"startOffset":14284,"endOffset":14758,"count":0}],"isBlockCoverage":false},{"functionName":"listOnTimeout","ranges":[{"startOffset":14762,"endOffset":17204,"count":0}],"isBlockCoverage":false},{"functionName":"Immediate","ranges":[{"startOffset":17285,"endOffset":17607,"count":0}],"isBlockCoverage":false},{"functionName":"ref","ranges":[{"startOffset":17611,"endOffset":17784,"count":0}],"isBlockCoverage":false},{"functionName":"unref","ranges":[{"startOffset":17788,"endOffset":17964,"count":0}],"isBlockCoverage":false},{"functionName":"hasRef","ranges":[{"startOffset":17968,"endOffset":18009,"count":0}],"isBlockCoverage":false}]},{"scriptId":"38","url":"internal/priority_queue.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2924,"count":1}],"isBlockCoverage":false},{"functionName":"PriorityQueue","ranges":[{"startOffset":570,"endOffset":811,"count":1}],"isBlockCoverage":true},{"functionName":"module.exports","ranges":[{"startOffset":815,"endOffset":855,"count":0}],"isBlockCoverage":false},{"functionName":"insert","ranges":[{"startOffset":859,"endOffset":1044,"count":0}],"isBlockCoverage":false},{"functionName":"peek","ranges":[{"startOffset":1048,"endOffset":1087,"count":0}],"isBlockCoverage":false},{"functionName":"percolateDown","ranges":[{"startOffset":1091,"endOffset":1759,"count":0}],"isBlockCoverage":false},{"functionName":"percolateUp","ranges":[{"startOffset":1763,"endOffset":2254,"count":0}],"isBlockCoverage":false},{"functionName":"removeAt","ranges":[{"startOffset":2258,"endOffset":2591,"count":0}],"isBlockCoverage":false},{"functionName":"remove","ranges":[{"startOffset":2595,"endOffset":2761,"count":0}],"isBlockCoverage":false},{"functionName":"shift","ranges":[{"startOffset":2765,"endOffset":2920,"count":0}],"isBlockCoverage":false}]},{"scriptId":"39","url":"internal/util/debuglog.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2815,"count":1}],"isBlockCoverage":false},{"functionName":"initializeDebugEnv","ranges":[{"startOffset":500,"endOffset":873,"count":1},{"startOffset":591,"endOffset":790,"count":0}],"isBlockCoverage":true},{"functionName":"emitWarningIfNeeded","ranges":[{"startOffset":947,"endOffset":1272,"count":0}],"isBlockCoverage":false},{"functionName":"noop","ranges":[{"startOffset":1274,"endOffset":1292,"count":8}],"isBlockCoverage":true},{"functionName":"debuglogImpl","ranges":[{"startOffset":1294,"endOffset":1859,"count":3},{"startOffset":1369,"endOffset":1831,"count":2},{"startOffset":1388,"endOffset":1784,"count":0}],"isBlockCoverage":true},{"functionName":"debug","ranges":[{"startOffset":1477,"endOffset":1777,"count":0}],"isBlockCoverage":false},{"functionName":"debuglog","ranges":[{"startOffset":2079,"endOffset":2758,"count":11}],"isBlockCoverage":true},{"functionName":"init","ranges":[{"startOffset":2110,"endOffset":2206,"count":3}],"isBlockCoverage":true},{"functionName":"debug","ranges":[{"startOffset":2221,"endOffset":2458,"count":3}],"isBlockCoverage":true},{"functionName":"test","ranges":[{"startOffset":2488,"endOffset":2557,"count":0}],"isBlockCoverage":false},{"functionName":"logger","ranges":[{"startOffset":2576,"endOffset":2603,"count":3}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":2653,"endOffset":2687,"count":0}],"isBlockCoverage":false}]},{"scriptId":"40","url":"internal/process/execution.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":6944,"count":1}],"isBlockCoverage":false},{"functionName":"tryGetCwd","ranges":[{"startOffset":526,"endOffset":856,"count":1},{"startOffset":587,"endOffset":854,"count":0}],"isBlockCoverage":true},{"functionName":"evalModule","ranges":[{"startOffset":858,"endOffset":1298,"count":0}],"isBlockCoverage":false},{"functionName":"evalScript","ranges":[{"startOffset":1300,"endOffset":2682,"count":0}],"isBlockCoverage":false},{"functionName":"setUncaughtExceptionCaptureCallback","ranges":[{"startOffset":2759,"endOffset":3453,"count":0}],"isBlockCoverage":false},{"functionName":"hasUncaughtExceptionCaptureCallback","ranges":[{"startOffset":3455,"endOffset":3556,"count":0}],"isBlockCoverage":false},{"functionName":"noop","ranges":[{"startOffset":3558,"endOffset":3576,"count":0}],"isBlockCoverage":false},{"functionName":"createOnGlobalUncaughtException","ranges":[{"startOffset":4119,"endOffset":6518,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":4388,"endOffset":6515,"count":0}],"isBlockCoverage":false},{"functionName":"readStdin","ranges":[{"startOffset":6520,"endOffset":6725,"count":0}],"isBlockCoverage":false}]},{"scriptId":"41","url":"internal/process/warning.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":4833,"count":1}],"isBlockCoverage":false},{"functionName":"lazyOption","ranges":[{"startOffset":277,"endOffset":831,"count":0}],"isBlockCoverage":false},{"functionName":"writeOut","ranges":[{"startOffset":932,"endOffset":1054,"count":0}],"isBlockCoverage":false},{"functionName":"writeToFile","ranges":[{"startOffset":1056,"endOffset":1440,"count":0}],"isBlockCoverage":false},{"functionName":"doEmitWarning","ranges":[{"startOffset":1442,"endOffset":1513,"count":0}],"isBlockCoverage":false},{"functionName":"onWarning","ranges":[{"startOffset":1552,"endOffset":2730,"count":0}],"isBlockCoverage":false},{"functionName":"emitWarning","ranges":[{"startOffset":2853,"endOffset":3997,"count":0}],"isBlockCoverage":false},{"functionName":"emitWarningSync","ranges":[{"startOffset":3999,"endOffset":4093,"count":0}],"isBlockCoverage":false},{"functionName":"createWarningObject","ranges":[{"startOffset":4095,"endOffset":4762,"count":0}],"isBlockCoverage":false}]},{"scriptId":"42","url":"internal/bootstrap/switches/is_main_thread.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":6397,"count":1}],"isBlockCoverage":true},{"functionName":"process._startProfilerIdleNotifier","ranges":[{"startOffset":513,"endOffset":521,"count":0}],"isBlockCoverage":false},{"functionName":"process._stopProfilerIdleNotifier","ranges":[{"startOffset":559,"endOffset":567,"count":0}],"isBlockCoverage":false},{"functionName":"defineStream","ranges":[{"startOffset":570,"endOffset":717,"count":3}],"isBlockCoverage":true},{"functionName":"createWritableStdioStream","ranges":[{"startOffset":1278,"endOffset":2845,"count":1},{"startOffset":1430,"endOffset":1556,"count":0},{"startOffset":1562,"endOffset":1748,"count":0},{"startOffset":2053,"endOffset":2081,"count":0},{"startOffset":2083,"endOffset":2303,"count":0},{"startOffset":2479,"endOffset":2724,"count":0}],"isBlockCoverage":true},{"functionName":"write","ranges":[{"startOffset":2667,"endOffset":2714,"count":0}],"isBlockCoverage":false},{"functionName":"dummyDestroy","ranges":[{"startOffset":2847,"endOffset":3230,"count":0}],"isBlockCoverage":false},{"functionName":"getStdout","ranges":[{"startOffset":3268,"endOffset":3599,"count":0}],"isBlockCoverage":false},{"functionName":"getStderr","ranges":[{"startOffset":3601,"endOffset":3932,"count":1},{"startOffset":3638,"endOffset":3652,"count":0},{"startOffset":3851,"endOffset":3913,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":3880,"endOffset":3907,"count":0}],"isBlockCoverage":false},{"functionName":"getStdin","ranges":[{"startOffset":3934,"endOffset":6253,"count":0}],"isBlockCoverage":false},{"functionName":"rawMethods.resetStdioForTesting","ranges":[{"startOffset":6316,"endOffset":6395,"count":0}],"isBlockCoverage":false}]},{"scriptId":"43","url":"internal/process/signal.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1124,"count":1}],"isBlockCoverage":false},{"functionName":"isSignal","ranges":[{"startOffset":205,"endOffset":301,"count":3}],"isBlockCoverage":true},{"functionName":"startListeningIfSignal","ranges":[{"startOffset":365,"endOffset":853,"count":3},{"startOffset":426,"endOffset":451,"count":0},{"startOffset":453,"endOffset":851,"count":0}],"isBlockCoverage":true},{"functionName":"stopListeningIfSignal","ranges":[{"startOffset":855,"endOffset":1050,"count":1},{"startOffset":957,"endOffset":993,"count":0},{"startOffset":995,"endOffset":1048,"count":0}],"isBlockCoverage":true}]},{"scriptId":"44","url":"internal/bootstrap/switches/does_own_process_state.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":3523,"count":1}],"isBlockCoverage":true},{"functionName":"wrapPosixCredentialSetters","ranges":[{"startOffset":817,"endOffset":2957,"count":1}],"isBlockCoverage":true},{"functionName":"initgroups","ranges":[{"startOffset":1278,"endOffset":1695,"count":0}],"isBlockCoverage":false},{"functionName":"setgroups","ranges":[{"startOffset":1699,"endOffset":2179,"count":0}],"isBlockCoverage":false},{"functionName":"wrapIdSetter","ranges":[{"startOffset":2183,"endOffset":2508,"count":4}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":2232,"endOffset":2503,"count":0}],"isBlockCoverage":false},{"functionName":"validateId","ranges":[{"startOffset":2512,"endOffset":2730,"count":0}],"isBlockCoverage":false},{"functionName":"wrappedChdir","ranges":[{"startOffset":3108,"endOffset":3279,"count":0}],"isBlockCoverage":false},{"functionName":"wrappedUmask","ranges":[{"startOffset":3281,"endOffset":3417,"count":0}],"isBlockCoverage":false},{"functionName":"wrappedCwd","ranges":[{"startOffset":3419,"endOffset":3522,"count":3},{"startOffset":3471,"endOffset":3500,"count":1}],"isBlockCoverage":true}]},{"scriptId":"45","url":"internal/main/run_main_module.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":632,"count":1}],"isBlockCoverage":true}]},{"scriptId":"46","url":"internal/bootstrap/pre_execution.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":15185,"count":1}],"isBlockCoverage":true},{"functionName":"prepareMainThreadExecution","ranges":[{"startOffset":410,"endOffset":2164,"count":1}],"isBlockCoverage":true},{"functionName":"patchProcessObject","ranges":[{"startOffset":2166,"endOffset":3743,"count":1},{"startOffset":2762,"endOffset":2770,"count":0}],"isBlockCoverage":true},{"functionName":"addReadOnlyProcessAlias","ranges":[{"startOffset":3745,"endOffset":4002,"count":13},{"startOffset":3866,"endOffset":4000,"count":1}],"isBlockCoverage":true},{"functionName":"setupWarningHandler","ranges":[{"startOffset":4004,"endOffset":4233,"count":1}],"isBlockCoverage":true},{"functionName":"setupCoverageHooks","ranges":[{"startOffset":4345,"endOffset":5022,"count":1},{"startOffset":4815,"endOffset":4992,"count":0}],"isBlockCoverage":true},{"functionName":"setupStacktracePrinterOnSigint","ranges":[{"startOffset":5024,"endOffset":5249,"count":1},{"startOffset":5126,"endOffset":5248,"count":0}],"isBlockCoverage":true},{"functionName":"initializeReport","ranges":[{"startOffset":5251,"endOffset":5475,"count":1}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":5433,"endOffset":5467,"count":0}],"isBlockCoverage":false},{"functionName":"setupDebugEnv","ranges":[{"startOffset":5477,"endOffset":5709,"count":1},{"startOffset":5628,"endOffset":5707,"count":0}],"isBlockCoverage":true},{"functionName":"initializeReportSignalHandlers","ranges":[{"startOffset":5771,"endOffset":5906,"count":1}],"isBlockCoverage":true},{"functionName":"initializeHeapSnapshotSignalHandlers","ranges":[{"startOffset":5908,"endOffset":6215,"count":1},{"startOffset":6043,"endOffset":6214,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":6175,"endOffset":6211,"count":0}],"isBlockCoverage":false},{"functionName":"setupTraceCategoryState","ranges":[{"startOffset":6217,"endOffset":6476,"count":1}],"isBlockCoverage":true},{"functionName":"setupInspectorHooks","ranges":[{"startOffset":6478,"endOffset":7059,"count":1}],"isBlockCoverage":true},{"functionName":"initializeDeprecations","ranges":[{"startOffset":7254,"endOffset":9775,"count":1},{"startOffset":7965,"endOffset":8267,"count":16},{"startOffset":8010,"endOffset":8242,"count":0},{"startOffset":8584,"endOffset":8757,"count":0},{"startOffset":8785,"endOffset":9179,"count":0}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":9428,"endOffset":9464,"count":9}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":9470,"endOffset":9512,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":9638,"endOffset":9673,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":9679,"endOffset":9720,"count":0}],"isBlockCoverage":false},{"functionName":"initializeAbortController","ranges":[{"startOffset":9777,"endOffset":10345,"count":1},{"startOffset":9915,"endOffset":10343,"count":0}],"isBlockCoverage":true},{"functionName":"setupChildProcessIpcChannel","ranges":[{"startOffset":10347,"endOffset":10912,"count":1},{"startOffset":10423,"endOffset":10910,"count":0}],"isBlockCoverage":true},{"functionName":"initializeClusterIPC","ranges":[{"startOffset":10914,"endOffset":11184,"count":1},{"startOffset":11001,"endOffset":11182,"count":0}],"isBlockCoverage":true},{"functionName":"initializePolicy","ranges":[{"startOffset":11186,"endOffset":13010,"count":1},{"startOffset":11312,"endOffset":13008,"count":0}],"isBlockCoverage":true},{"functionName":"initializeWASI","ranges":[{"startOffset":13012,"endOffset":13241,"count":1}],"isBlockCoverage":true},{"functionName":"initializeCJSLoader","ranges":[{"startOffset":13243,"endOffset":13529,"count":1}],"isBlockCoverage":true},{"functionName":"initializeESMLoader","ranges":[{"startOffset":13531,"endOffset":14199,"count":1},{"startOffset":13740,"endOffset":13747,"count":0}],"isBlockCoverage":true},{"functionName":"initializeFrozenIntrinsics","ranges":[{"startOffset":14201,"endOffset":14458,"count":1},{"startOffset":14286,"endOffset":14456,"count":0}],"isBlockCoverage":true},{"functionName":"loadPreloadModules","ranges":[{"startOffset":14460,"endOffset":14807,"count":1},{"startOffset":14654,"endOffset":14805,"count":0}],"isBlockCoverage":true}]},{"scriptId":"47","url":"internal/options.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":898,"count":1}],"isBlockCoverage":false},{"functionName":"getOptionValue","ranges":[{"startOffset":175,"endOffset":314,"count":48},{"startOffset":262,"endOffset":289,"count":0}],"isBlockCoverage":true},{"functionName":"getAllowUnauthorized","ranges":[{"startOffset":316,"endOffset":781,"count":0}],"isBlockCoverage":false}]},{"scriptId":"48","url":"internal/inspector_async_hook.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1972,"count":1}],"isBlockCoverage":false},{"functionName":"lazyHookCreation","ranges":[{"startOffset":75,"endOffset":1257,"count":0}],"isBlockCoverage":false},{"functionName":"enable","ranges":[{"startOffset":1259,"endOffset":1840,"count":0}],"isBlockCoverage":false},{"functionName":"disable","ranges":[{"startOffset":1842,"endOffset":1928,"count":0}],"isBlockCoverage":false}]},{"scriptId":"49","url":"internal/source_map/source_map_cache.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":8419,"count":1}],"isBlockCoverage":false},{"functionName":"ObjectGetValueSafe","ranges":[{"startOffset":305,"endOffset":483,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":665,"endOffset":690,"count":0}],"isBlockCoverage":false},{"functionName":"getSourceMapsEnabled","ranges":[{"startOffset":1236,"endOffset":1740,"count":2},{"startOffset":1309,"endOffset":1710,"count":1},{"startOffset":1402,"endOffset":1706,"count":0}],"isBlockCoverage":true},{"functionName":"maybeCacheSourceMap","ranges":[{"startOffset":1742,"endOffset":2852,"count":2},{"startOffset":1900,"endOffset":1920,"count":0},{"startOffset":1923,"endOffset":1930,"count":0},{"startOffset":1990,"endOffset":2128,"count":0},{"startOffset":2232,"endOffset":2850,"count":0}],"isBlockCoverage":true},{"functionName":"dataFromUrl","ranges":[{"startOffset":2854,"endOffset":3380,"count":0}],"isBlockCoverage":false},{"functionName":"lineLengths","ranges":[{"startOffset":3570,"endOffset":3878,"count":0}],"isBlockCoverage":false},{"functionName":"sourceMapFromFile","ranges":[{"startOffset":3880,"endOffset":4136,"count":0}],"isBlockCoverage":false},{"functionName":"sourceMapFromDataUrl","ranges":[{"startOffset":4230,"endOffset":4867,"count":0}],"isBlockCoverage":false},{"functionName":"sourcesToAbsolute","ranges":[{"startOffset":5052,"endOffset":5379,"count":0}],"isBlockCoverage":false},{"functionName":"rekeySourceMap","ranges":[{"startOffset":5448,"endOffset":5643,"count":0}],"isBlockCoverage":false},{"functionName":"sourceMapCacheToObject","ranges":[{"startOffset":6081,"endOffset":6450,"count":0}],"isBlockCoverage":false},{"functionName":"appendCJSCache","ranges":[{"startOffset":6689,"endOffset":7344,"count":0}],"isBlockCoverage":false},{"functionName":"findSourceMap","ranges":[{"startOffset":7562,"endOffset":8287,"count":0}],"isBlockCoverage":false}]},{"scriptId":"50","url":"fs.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":59762,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":3694,"endOffset":3854,"count":0}],"isBlockCoverage":false},{"functionName":"showTruncateDeprecation","ranges":[{"startOffset":4089,"endOffset":4376,"count":0}],"isBlockCoverage":false},{"functionName":"maybeCallback","ranges":[{"startOffset":4378,"endOffset":4494,"count":0}],"isBlockCoverage":false},{"functionName":"makeCallback","ranges":[{"startOffset":4697,"endOffset":4840,"count":0}],"isBlockCoverage":false},{"functionName":"makeStatsCallback","ranges":[{"startOffset":5021,"endOffset":5236,"count":0}],"isBlockCoverage":false},{"functionName":"isFileType","ranges":[{"startOffset":5262,"endOffset":5522,"count":23},{"startOffset":5461,"endOffset":5481,"count":13}],"isBlockCoverage":true},{"functionName":"access","ranges":[{"startOffset":5524,"endOffset":5882,"count":0}],"isBlockCoverage":false},{"functionName":"accessSync","ranges":[{"startOffset":5884,"endOffset":6122,"count":0}],"isBlockCoverage":false},{"functionName":"exists","ranges":[{"startOffset":6124,"endOffset":6362,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":6435,"endOffset":6513,"count":0}],"isBlockCoverage":false},{"functionName":"existsSync","ranges":[{"startOffset":6930,"endOffset":7460,"count":0}],"isBlockCoverage":false},{"functionName":"readFileAfterOpen","ranges":[{"startOffset":7462,"endOffset":7742,"count":0}],"isBlockCoverage":false},{"functionName":"readFileAfterStat","ranges":[{"startOffset":7744,"endOffset":8245,"count":0}],"isBlockCoverage":false},{"functionName":"readFile","ranges":[{"startOffset":8247,"endOffset":9263,"count":0}],"isBlockCoverage":false},{"functionName":"tryStatSync","ranges":[{"startOffset":9265,"endOffset":9495,"count":0}],"isBlockCoverage":false},{"functionName":"tryCreateBuffer","ranges":[{"startOffset":9497,"endOffset":9808,"count":0}],"isBlockCoverage":false},{"functionName":"tryReadSync","ranges":[{"startOffset":9810,"endOffset":10065,"count":0}],"isBlockCoverage":false},{"functionName":"readFileSync","ranges":[{"startOffset":10067,"endOffset":11450,"count":0}],"isBlockCoverage":false},{"functionName":"defaultCloseCallback","ranges":[{"startOffset":11452,"endOffset":11520,"count":0}],"isBlockCoverage":false},{"functionName":"close","ranges":[{"startOffset":11522,"endOffset":11778,"count":0}],"isBlockCoverage":false},{"functionName":"closeSync","ranges":[{"startOffset":11780,"endOffset":11923,"count":0}],"isBlockCoverage":false},{"functionName":"open","ranges":[{"startOffset":11925,"endOffset":12502,"count":0}],"isBlockCoverage":false},{"functionName":"openSync","ranges":[{"startOffset":12505,"endOffset":12901,"count":0}],"isBlockCoverage":false},{"functionName":"read","ranges":[{"startOffset":13008,"endOffset":14506,"count":0}],"isBlockCoverage":false},{"functionName":"readSync","ranges":[{"startOffset":14775,"endOffset":15690,"count":0}],"isBlockCoverage":false},{"functionName":"readv","ranges":[{"startOffset":15692,"endOffset":16122,"count":0}],"isBlockCoverage":false},{"functionName":"readvSync","ranges":[{"startOffset":16265,"endOffset":16575,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":16721,"endOffset":17989,"count":0}],"isBlockCoverage":false},{"functionName":"writeSync","ranges":[{"startOffset":18266,"endOffset":19132,"count":0}],"isBlockCoverage":false},{"functionName":"writev","ranges":[{"startOffset":19193,"endOffset":19621,"count":0}],"isBlockCoverage":false},{"functionName":"writevSync","ranges":[{"startOffset":19750,"endOffset":20063,"count":0}],"isBlockCoverage":false},{"functionName":"rename","ranges":[{"startOffset":20065,"endOffset":20446,"count":0}],"isBlockCoverage":false},{"functionName":"renameSync","ranges":[{"startOffset":20448,"endOffset":20795,"count":0}],"isBlockCoverage":false},{"functionName":"truncate","ranges":[{"startOffset":20797,"endOffset":21415,"count":0}],"isBlockCoverage":false},{"functionName":"truncateSync","ranges":[{"startOffset":21417,"endOffset":21820,"count":0}],"isBlockCoverage":false},{"functionName":"ftruncate","ranges":[{"startOffset":21822,"endOffset":22162,"count":0}],"isBlockCoverage":false},{"functionName":"ftruncateSync","ranges":[{"startOffset":22164,"endOffset":22384,"count":0}],"isBlockCoverage":false},{"functionName":"lazyLoadRimraf","ranges":[{"startOffset":22387,"endOffset":22506,"count":0}],"isBlockCoverage":false},{"functionName":"rmdir","ranges":[{"startOffset":22508,"endOffset":23148,"count":0}],"isBlockCoverage":false},{"functionName":"rmdirSync","ranges":[{"startOffset":23150,"endOffset":23588,"count":0}],"isBlockCoverage":false},{"functionName":"rm","ranges":[{"startOffset":23590,"endOffset":23928,"count":0}],"isBlockCoverage":false},{"functionName":"rmSync","ranges":[{"startOffset":23930,"endOffset":24100,"count":0}],"isBlockCoverage":false},{"functionName":"fdatasync","ranges":[{"startOffset":24102,"endOffset":24276,"count":0}],"isBlockCoverage":false},{"functionName":"fdatasyncSync","ranges":[{"startOffset":24278,"endOffset":24428,"count":0}],"isBlockCoverage":false},{"functionName":"fsync","ranges":[{"startOffset":24430,"endOffset":24596,"count":0}],"isBlockCoverage":false},{"functionName":"fsyncSync","ranges":[{"startOffset":24598,"endOffset":24740,"count":0}],"isBlockCoverage":false},{"functionName":"mkdir","ranges":[{"startOffset":24742,"endOffset":25523,"count":0}],"isBlockCoverage":false},{"functionName":"mkdirSync","ranges":[{"startOffset":25525,"endOffset":26297,"count":0}],"isBlockCoverage":false},{"functionName":"readdir","ranges":[{"startOffset":26299,"endOffset":26880,"count":0}],"isBlockCoverage":false},{"functionName":"readdirSync","ranges":[{"startOffset":26882,"endOffset":27308,"count":0}],"isBlockCoverage":false},{"functionName":"fstat","ranges":[{"startOffset":27310,"endOffset":27649,"count":0}],"isBlockCoverage":false},{"functionName":"lstat","ranges":[{"startOffset":27651,"endOffset":28026,"count":0}],"isBlockCoverage":false},{"functionName":"stat","ranges":[{"startOffset":28028,"endOffset":28401,"count":0}],"isBlockCoverage":false},{"functionName":"hasNoEntryError","ranges":[{"startOffset":28403,"endOffset":28625,"count":0}],"isBlockCoverage":false},{"functionName":"fstatSync","ranges":[{"startOffset":28627,"endOffset":28891,"count":0}],"isBlockCoverage":false},{"functionName":"lstatSync","ranges":[{"startOffset":28893,"endOffset":29317,"count":0}],"isBlockCoverage":false},{"functionName":"statSync","ranges":[{"startOffset":29319,"endOffset":29740,"count":2},{"startOffset":29618,"endOffset":29641,"count":0},{"startOffset":29643,"endOffset":29670,"count":0}],"isBlockCoverage":true},{"functionName":"readlink","ranges":[{"startOffset":29742,"endOffset":30090,"count":0}],"isBlockCoverage":false},{"functionName":"readlinkSync","ranges":[{"startOffset":30092,"endOffset":30423,"count":0}],"isBlockCoverage":false},{"functionName":"symlink","ranges":[{"startOffset":30425,"endOffset":32035,"count":0}],"isBlockCoverage":false},{"functionName":"symlinkSync","ranges":[{"startOffset":32037,"endOffset":32694,"count":0}],"isBlockCoverage":false},{"functionName":"link","ranges":[{"startOffset":32696,"endOffset":33097,"count":0}],"isBlockCoverage":false},{"functionName":"linkSync","ranges":[{"startOffset":33099,"endOffset":33548,"count":0}],"isBlockCoverage":false},{"functionName":"unlink","ranges":[{"startOffset":33550,"endOffset":33777,"count":0}],"isBlockCoverage":false},{"functionName":"unlinkSync","ranges":[{"startOffset":33779,"endOffset":33965,"count":0}],"isBlockCoverage":false},{"functionName":"fchmod","ranges":[{"startOffset":33967,"endOffset":34209,"count":0}],"isBlockCoverage":false},{"functionName":"fchmodSync","ranges":[{"startOffset":34211,"endOffset":34405,"count":0}],"isBlockCoverage":false},{"functionName":"lchmod","ranges":[{"startOffset":34407,"endOffset":34844,"count":0}],"isBlockCoverage":false},{"functionName":"lchmodSync","ranges":[{"startOffset":34846,"endOffset":35168,"count":0}],"isBlockCoverage":false},{"functionName":"chmod","ranges":[{"startOffset":35171,"endOffset":35447,"count":0}],"isBlockCoverage":false},{"functionName":"chmodSync","ranges":[{"startOffset":35449,"endOffset":35684,"count":0}],"isBlockCoverage":false},{"functionName":"lchown","ranges":[{"startOffset":35686,"endOffset":36027,"count":0}],"isBlockCoverage":false},{"functionName":"lchownSync","ranges":[{"startOffset":36029,"endOffset":36329,"count":0}],"isBlockCoverage":false},{"functionName":"fchown","ranges":[{"startOffset":36331,"endOffset":36637,"count":0}],"isBlockCoverage":false},{"functionName":"fchownSync","ranges":[{"startOffset":36639,"endOffset":36898,"count":0}],"isBlockCoverage":false},{"functionName":"chown","ranges":[{"startOffset":36900,"endOffset":37240,"count":0}],"isBlockCoverage":false},{"functionName":"chownSync","ranges":[{"startOffset":37242,"endOffset":37540,"count":0}],"isBlockCoverage":false},{"functionName":"utimes","ranges":[{"startOffset":37542,"endOffset":37883,"count":0}],"isBlockCoverage":false},{"functionName":"utimesSync","ranges":[{"startOffset":37885,"endOffset":38167,"count":0}],"isBlockCoverage":false},{"functionName":"futimes","ranges":[{"startOffset":38169,"endOffset":38477,"count":0}],"isBlockCoverage":false},{"functionName":"futimesSync","ranges":[{"startOffset":38479,"endOffset":38739,"count":0}],"isBlockCoverage":false},{"functionName":"lutimes","ranges":[{"startOffset":38741,"endOffset":39087,"count":0}],"isBlockCoverage":false},{"functionName":"lutimesSync","ranges":[{"startOffset":39089,"endOffset":39393,"count":0}],"isBlockCoverage":false},{"functionName":"writeAll","ranges":[{"startOffset":39395,"endOffset":40339,"count":0}],"isBlockCoverage":false},{"functionName":"writeFile","ranges":[{"startOffset":40341,"endOffset":41280,"count":0}],"isBlockCoverage":false},{"functionName":"writeFileSync","ranges":[{"startOffset":41282,"endOffset":41992,"count":0}],"isBlockCoverage":false},{"functionName":"appendFile","ranges":[{"startOffset":41994,"endOffset":42434,"count":0}],"isBlockCoverage":false},{"functionName":"appendFileSync","ranges":[{"startOffset":42436,"endOffset":42815,"count":0}],"isBlockCoverage":false},{"functionName":"watch","ranges":[{"startOffset":42817,"endOffset":44050,"count":0}],"isBlockCoverage":false},{"functionName":"watchFile","ranges":[{"startOffset":44086,"endOffset":45199,"count":0}],"isBlockCoverage":false},{"functionName":"unwatchFile","ranges":[{"startOffset":45201,"endOffset":45889,"count":0}],"isBlockCoverage":false},{"functionName":"splitRoot","ranges":[{"startOffset":46105,"endOffset":46171,"count":0}],"isBlockCoverage":false},{"functionName":"splitRoot","ranges":[{"startOffset":46196,"endOffset":46374,"count":3},{"startOffset":46263,"endOffset":46354,"count":6},{"startOffset":46325,"endOffset":46348,"count":3},{"startOffset":46354,"endOffset":46373,"count":0}],"isBlockCoverage":true},{"functionName":"encodeRealpathResult","ranges":[{"startOffset":46379,"endOffset":46666,"count":3},{"startOffset":46464,"endOffset":46494,"count":0},{"startOffset":46514,"endOffset":46665,"count":0}],"isBlockCoverage":true},{"functionName":"nextPart","ranges":[{"startOffset":46789,"endOffset":47032,"count":0}],"isBlockCoverage":false},{"functionName":"nextPart","ranges":[{"startOffset":47056,"endOffset":47109,"count":18}],"isBlockCoverage":true},{"functionName":"realpathSync","ranges":[{"startOffset":47151,"endOffset":51017,"count":4},{"startOffset":47285,"endOffset":47303,"count":0},{"startOffset":47472,"endOffset":47507,"count":1},{"startOffset":47507,"endOffset":48079,"count":3},{"startOffset":48079,"endOffset":48254,"count":0},{"startOffset":48254,"endOffset":48391,"count":3},{"startOffset":48391,"endOffset":50934,"count":18},{"startOffset":48503,"endOffset":48618,"count":3},{"startOffset":48618,"endOffset":48744,"count":15},{"startOffset":48867,"endOffset":49004,"count":5},{"startOffset":48958,"endOffset":48982,"count":0},{"startOffset":49004,"endOffset":49084,"count":13},{"startOffset":49115,"endOffset":49164,"count":0},{"startOffset":49164,"endOffset":50448,"count":13},{"startOffset":49617,"endOffset":50442,"count":0},{"startOffset":50448,"endOffset":50743,"count":0},{"startOffset":50745,"endOffset":50930,"count":0},{"startOffset":50934,"endOffset":51016,"count":3}],"isBlockCoverage":true},{"functionName":"realpathSync.native","ranges":[{"startOffset":51042,"endOffset":51281,"count":0}],"isBlockCoverage":false},{"functionName":"realpath","ranges":[{"startOffset":51285,"endOffset":54720,"count":0}],"isBlockCoverage":false},{"functionName":"realpath.native","ranges":[{"startOffset":54741,"endOffset":55011,"count":0}],"isBlockCoverage":false},{"functionName":"mkdtemp","ranges":[{"startOffset":55014,"endOffset":55485,"count":0}],"isBlockCoverage":false},{"functionName":"mkdtempSync","ranges":[{"startOffset":55488,"endOffset":55962,"count":0}],"isBlockCoverage":false},{"functionName":"copyFile","ranges":[{"startOffset":55965,"endOffset":56519,"count":0}],"isBlockCoverage":false},{"functionName":"copyFileSync","ranges":[{"startOffset":56522,"endOffset":56894,"count":0}],"isBlockCoverage":false},{"functionName":"lazyLoadStreams","ranges":[{"startOffset":56896,"endOffset":57090,"count":8},{"startOffset":56944,"endOffset":57088,"count":1}],"isBlockCoverage":true},{"functionName":"createReadStream","ranges":[{"startOffset":57092,"endOffset":57197,"count":0}],"isBlockCoverage":false},{"functionName":"createWriteStream","ranges":[{"startOffset":57199,"endOffset":57306,"count":0}],"isBlockCoverage":false},{"functionName":"get ReadStream","ranges":[{"startOffset":58529,"endOffset":58597,"count":2}],"isBlockCoverage":true},{"functionName":"set ReadStream","ranges":[{"startOffset":58602,"endOffset":58649,"count":0}],"isBlockCoverage":false},{"functionName":"get WriteStream","ranges":[{"startOffset":58654,"endOffset":58724,"count":2}],"isBlockCoverage":true},{"functionName":"set WriteStream","ranges":[{"startOffset":58729,"endOffset":58778,"count":0}],"isBlockCoverage":false},{"functionName":"get FileReadStream","ranges":[{"startOffset":58916,"endOffset":58992,"count":2}],"isBlockCoverage":true},{"functionName":"set FileReadStream","ranges":[{"startOffset":58997,"endOffset":59052,"count":0}],"isBlockCoverage":false},{"functionName":"get FileWriteStream","ranges":[{"startOffset":59057,"endOffset":59135,"count":2}],"isBlockCoverage":true},{"functionName":"set FileWriteStream","ranges":[{"startOffset":59140,"endOffset":59197,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":59628,"endOffset":59753,"count":3},{"startOffset":59673,"endOffset":59724,"count":1}],"isBlockCoverage":true}]},{"scriptId":"51","url":"internal/fs/utils.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":21731,"count":1}],"isBlockCoverage":false},{"functionName":"lazyLoadFs","ranges":[{"startOffset":2496,"endOffset":2575,"count":0}],"isBlockCoverage":false},{"functionName":"assertEncoding","ranges":[{"startOffset":2577,"endOffset":2724,"count":19},{"startOffset":2628,"endOffset":2659,"count":15},{"startOffset":2661,"endOffset":2722,"count":0}],"isBlockCoverage":true},{"functionName":"Dirent","ranges":[{"startOffset":2743,"endOffset":2818,"count":0}],"isBlockCoverage":false},{"functionName":"isDirectory","ranges":[{"startOffset":2822,"endOffset":2883,"count":0}],"isBlockCoverage":false},{"functionName":"isFile","ranges":[{"startOffset":2887,"endOffset":2944,"count":0}],"isBlockCoverage":false},{"functionName":"isBlockDevice","ranges":[{"startOffset":2948,"endOffset":3013,"count":0}],"isBlockCoverage":false},{"functionName":"isCharacterDevice","ranges":[{"startOffset":3017,"endOffset":3085,"count":0}],"isBlockCoverage":false},{"functionName":"isSymbolicLink","ranges":[{"startOffset":3089,"endOffset":3154,"count":0}],"isBlockCoverage":false},{"functionName":"isFIFO","ranges":[{"startOffset":3158,"endOffset":3215,"count":0}],"isBlockCoverage":false},{"functionName":"isSocket","ranges":[{"startOffset":3219,"endOffset":3280,"count":0}],"isBlockCoverage":false},{"functionName":"DirentFromStats","ranges":[{"startOffset":3325,"endOffset":3404,"count":0}],"isBlockCoverage":false},{"functionName":"DirentFromStats.","ranges":[{"startOffset":3549,"endOffset":3598,"count":0}],"isBlockCoverage":false},{"functionName":"copyObject","ranges":[{"startOffset":3603,"endOffset":3731,"count":0}],"isBlockCoverage":false},{"functionName":"join","ranges":[{"startOffset":3781,"endOffset":4388,"count":0}],"isBlockCoverage":false},{"functionName":"getDirents","ranges":[{"startOffset":4390,"endOffset":5485,"count":0}],"isBlockCoverage":false},{"functionName":"getDirent","ranges":[{"startOffset":5487,"endOffset":6209,"count":0}],"isBlockCoverage":false},{"functionName":"getOptions","ranges":[{"startOffset":6211,"endOffset":6853,"count":22},{"startOffset":6306,"endOffset":6344,"count":19},{"startOffset":6346,"endOffset":6378,"count":3},{"startOffset":6378,"endOffset":6415,"count":19},{"startOffset":6415,"endOffset":6533,"count":15},{"startOffset":6533,"endOffset":6655,"count":4},{"startOffset":6572,"endOffset":6655,"count":0},{"startOffset":6655,"endOffset":6769,"count":19},{"startOffset":6769,"endOffset":6833,"count":0},{"startOffset":6833,"endOffset":6852,"count":19}],"isBlockCoverage":true},{"functionName":"handleErrorFromBinding","ranges":[{"startOffset":6855,"endOffset":7384,"count":15},{"startOffset":6925,"endOffset":7060,"count":0},{"startOffset":7092,"endOffset":7382,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":7525,"endOffset":8078,"count":24},{"startOffset":7755,"endOffset":7775,"count":0},{"startOffset":7829,"endOffset":7877,"count":0},{"startOffset":7896,"endOffset":8077,"count":0}],"isBlockCoverage":true},{"functionName":"preprocessSymlinkDestination","ranges":[{"startOffset":8082,"endOffset":8766,"count":0}],"isBlockCoverage":false},{"functionName":"StatsBase","ranges":[{"startOffset":8799,"endOffset":9106,"count":2}],"isBlockCoverage":true},{"functionName":"StatsBase.isDirectory","ranges":[{"startOffset":9142,"endOffset":9199,"count":2}],"isBlockCoverage":true},{"functionName":"StatsBase.isFile","ranges":[{"startOffset":9231,"endOffset":9288,"count":2}],"isBlockCoverage":true},{"functionName":"StatsBase.isBlockDevice","ranges":[{"startOffset":9327,"endOffset":9384,"count":0}],"isBlockCoverage":false},{"functionName":"StatsBase.isCharacterDevice","ranges":[{"startOffset":9427,"endOffset":9484,"count":0}],"isBlockCoverage":false},{"functionName":"StatsBase.isSymbolicLink","ranges":[{"startOffset":9524,"endOffset":9581,"count":0}],"isBlockCoverage":false},{"functionName":"StatsBase.isFIFO","ranges":[{"startOffset":9613,"endOffset":9670,"count":0}],"isBlockCoverage":false},{"functionName":"StatsBase.isSocket","ranges":[{"startOffset":9704,"endOffset":9762,"count":0}],"isBlockCoverage":false},{"functionName":"msFromTimeSpec","ranges":[{"startOffset":9887,"endOffset":9969,"count":8}],"isBlockCoverage":true},{"functionName":"nsFromTimeSpecBigInt","ranges":[{"startOffset":9971,"endOffset":10054,"count":0}],"isBlockCoverage":false},{"functionName":"dateFromMs","ranges":[{"startOffset":10403,"endOffset":10467,"count":8}],"isBlockCoverage":true},{"functionName":"BigIntStats","ranges":[{"startOffset":10469,"endOffset":11205,"count":0}],"isBlockCoverage":false},{"functionName":"BigIntStats._checkModeProperty","ranges":[{"startOffset":11363,"endOffset":11609,"count":0}],"isBlockCoverage":false},{"functionName":"Stats","ranges":[{"startOffset":11612,"endOffset":12129,"count":2}],"isBlockCoverage":true},{"functionName":"Stats._checkModeProperty","ranges":[{"startOffset":12470,"endOffset":12700,"count":4},{"startOffset":12507,"endOffset":12585,"count":0},{"startOffset":12587,"endOffset":12654,"count":0}],"isBlockCoverage":true},{"functionName":"getStatsFromBinding","ranges":[{"startOffset":12703,"endOffset":13781,"count":2},{"startOffset":12784,"endOffset":13309,"count":0}],"isBlockCoverage":true},{"functionName":"stringToFlags","ranges":[{"startOffset":13783,"endOffset":14991,"count":17},{"startOffset":13848,"endOffset":13871,"count":0},{"startOffset":13894,"endOffset":13920,"count":0},{"startOffset":13977,"endOffset":13988,"count":0},{"startOffset":14010,"endOffset":14047,"count":0},{"startOffset":14052,"endOffset":14078,"count":0},{"startOffset":14083,"endOffset":14095,"count":0},{"startOffset":14117,"endOffset":14153,"count":0},{"startOffset":14159,"endOffset":14206,"count":0},{"startOffset":14211,"endOffset":14222,"count":0},{"startOffset":14244,"endOffset":14301,"count":0},{"startOffset":14307,"endOffset":14353,"count":0},{"startOffset":14358,"endOffset":14369,"count":0},{"startOffset":14391,"endOffset":14446,"count":0},{"startOffset":14452,"endOffset":14500,"count":0},{"startOffset":14505,"endOffset":14516,"count":0},{"startOffset":14538,"endOffset":14596,"count":0},{"startOffset":14601,"endOffset":14612,"count":0},{"startOffset":14634,"endOffset":14692,"count":0},{"startOffset":14698,"endOffset":14745,"count":0},{"startOffset":14750,"endOffset":14761,"count":0},{"startOffset":14783,"endOffset":14839,"count":0},{"startOffset":14844,"endOffset":14855,"count":0},{"startOffset":14877,"endOffset":14933,"count":0},{"startOffset":14937,"endOffset":14990,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":15037,"endOffset":15397,"count":0}],"isBlockCoverage":false},{"functionName":"toUnixTimestamp","ranges":[{"startOffset":15459,"endOffset":15902,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":15956,"endOffset":16335,"count":76},{"startOffset":16012,"endOffset":16079,"count":0},{"startOffset":16100,"endOffset":16167,"count":0},{"startOffset":16208,"endOffset":16331,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":16393,"endOffset":16667,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":16709,"endOffset":16979,"count":24},{"startOffset":16771,"endOffset":16793,"count":0},{"startOffset":16795,"endOffset":16881,"count":0},{"startOffset":16957,"endOffset":16977,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":17024,"endOffset":17159,"count":20}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":17207,"endOffset":17538,"count":0}],"isBlockCoverage":false},{"functionName":"warnOnNonPortableTemplate","ranges":[{"startOffset":17579,"endOffset":18037,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":18273,"endOffset":18893,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":18943,"endOffset":19525,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":19577,"endOffset":20059,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":20100,"endOffset":20678,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":20741,"endOffset":21113,"count":0}],"isBlockCoverage":false}]},{"scriptId":"52","url":"internal/fs/dir.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":6714,"count":1}],"isBlockCoverage":false},{"functionName":"Dir","ranges":[{"startOffset":1109,"endOffset":1881,"count":0}],"isBlockCoverage":false},{"functionName":"get path","ranges":[{"startOffset":1885,"endOffset":1928,"count":0}],"isBlockCoverage":false},{"functionName":"read","ranges":[{"startOffset":1932,"endOffset":1999,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":2003,"endOffset":3375,"count":0}],"isBlockCoverage":false},{"functionName":"readSync","ranges":[{"startOffset":3379,"endOffset":4135,"count":0}],"isBlockCoverage":false},{"functionName":"close","ranges":[{"startOffset":4139,"endOffset":4865,"count":0}],"isBlockCoverage":false},{"functionName":"closeSync","ranges":[{"startOffset":4869,"endOffset":5246,"count":0}],"isBlockCoverage":false},{"functionName":"entries","ranges":[{"startOffset":5250,"endOffset":5513,"count":0}],"isBlockCoverage":false},{"functionName":"opendir","ranges":[{"startOffset":5674,"endOffset":6299,"count":0}],"isBlockCoverage":false},{"functionName":"opendirSync","ranges":[{"startOffset":6301,"endOffset":6658,"count":0}],"isBlockCoverage":false}]},{"scriptId":"53","url":"internal/modules/cjs/helpers.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":5427,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":681,"endOffset":706,"count":1}],"isBlockCoverage":true},{"functionName":"loadNativeModule","ranges":[{"startOffset":860,"endOffset":1066,"count":1}],"isBlockCoverage":true},{"functionName":"makeRequireFunction","ranges":[{"startOffset":1315,"endOffset":3313,"count":0}],"isBlockCoverage":false},{"functionName":"stripBOM","ranges":[{"startOffset":3498,"endOffset":3624,"count":0}],"isBlockCoverage":false},{"functionName":"addBuiltinLibsToObject","ranges":[{"startOffset":3626,"endOffset":5091,"count":0}],"isBlockCoverage":false},{"functionName":"normalizeReferrerURL","ranges":[{"startOffset":5093,"endOffset":5281,"count":2},{"startOffset":5200,"endOffset":5246,"count":0}],"isBlockCoverage":true}]},{"scriptId":"54","url":"url.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":30124,"count":1}],"isBlockCoverage":false},{"functionName":"Url","ranges":[{"startOffset":1879,"endOffset":2155,"count":0}],"isBlockCoverage":false},{"functionName":"urlParse","ranges":[{"startOffset":3833,"endOffset":4047,"count":0}],"isBlockCoverage":false},{"functionName":"isIpv6Hostname","ranges":[{"startOffset":4049,"endOffset":4272,"count":0}],"isBlockCoverage":false},{"functionName":"parse","ranges":[{"startOffset":4296,"endOffset":13617,"count":0}],"isBlockCoverage":false},{"functionName":"getHostname","ranges":[{"startOffset":13620,"endOffset":14345,"count":0}],"isBlockCoverage":false},{"functionName":"autoEscapeStr","ranges":[{"startOffset":15401,"endOffset":16107,"count":0}],"isBlockCoverage":false},{"functionName":"urlFormat","ranges":[{"startOffset":16153,"endOffset":16863,"count":0}],"isBlockCoverage":false},{"functionName":"format","ranges":[{"startOffset":17570,"endOffset":20020,"count":0}],"isBlockCoverage":false},{"functionName":"urlResolve","ranges":[{"startOffset":20023,"endOffset":20122,"count":0}],"isBlockCoverage":false},{"functionName":"resolve","ranges":[{"startOffset":20148,"endOffset":20249,"count":0}],"isBlockCoverage":false},{"functionName":"urlResolveObject","ranges":[{"startOffset":20252,"endOffset":20395,"count":0}],"isBlockCoverage":false},{"functionName":"resolveObject","ranges":[{"startOffset":20427,"endOffset":29546,"count":0}],"isBlockCoverage":false},{"functionName":"parseHost","ranges":[{"startOffset":29575,"endOffset":29848,"count":0}],"isBlockCoverage":false}]},{"scriptId":"55","url":"internal/idna.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":264,"count":1}],"isBlockCoverage":false}]},{"scriptId":"56","url":"internal/process/report.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2945,"count":1}],"isBlockCoverage":false},{"functionName":"writeReport","ranges":[{"startOffset":298,"endOffset":805,"count":0}],"isBlockCoverage":false},{"functionName":"getReport","ranges":[{"startOffset":809,"endOffset":1045,"count":0}],"isBlockCoverage":false},{"functionName":"get directory","ranges":[{"startOffset":1049,"endOffset":1100,"count":0}],"isBlockCoverage":false},{"functionName":"set directory","ranges":[{"startOffset":1104,"endOffset":1192,"count":0}],"isBlockCoverage":false},{"functionName":"get filename","ranges":[{"startOffset":1196,"endOffset":1245,"count":0}],"isBlockCoverage":false},{"functionName":"set filename","ranges":[{"startOffset":1249,"endOffset":1337,"count":0}],"isBlockCoverage":false},{"functionName":"get compact","ranges":[{"startOffset":1341,"endOffset":1388,"count":0}],"isBlockCoverage":false},{"functionName":"set compact","ranges":[{"startOffset":1392,"endOffset":1469,"count":0}],"isBlockCoverage":false},{"functionName":"get signal","ranges":[{"startOffset":1473,"endOffset":1518,"count":0}],"isBlockCoverage":false},{"functionName":"set signal","ranges":[{"startOffset":1522,"endOffset":1659,"count":0}],"isBlockCoverage":false},{"functionName":"get reportOnFatalError","ranges":[{"startOffset":1663,"endOffset":1735,"count":0}],"isBlockCoverage":false},{"functionName":"set reportOnFatalError","ranges":[{"startOffset":1739,"endOffset":1923,"count":0}],"isBlockCoverage":false},{"functionName":"get reportOnSignal","ranges":[{"startOffset":1927,"endOffset":1991,"count":0}],"isBlockCoverage":false},{"functionName":"set reportOnSignal","ranges":[{"startOffset":1995,"endOffset":2222,"count":0}],"isBlockCoverage":false},{"functionName":"get reportOnUncaughtException","ranges":[{"startOffset":2226,"endOffset":2312,"count":0}],"isBlockCoverage":false},{"functionName":"set reportOnUncaughtException","ranges":[{"startOffset":2316,"endOffset":2514,"count":0}],"isBlockCoverage":false},{"functionName":"addSignalHandler","ranges":[{"startOffset":2519,"endOffset":2690,"count":1},{"startOffset":2585,"endOffset":2688,"count":0}],"isBlockCoverage":true},{"functionName":"removeSignalHandler","ranges":[{"startOffset":2692,"endOffset":2816,"count":0}],"isBlockCoverage":false},{"functionName":"signalHandler","ranges":[{"startOffset":2818,"endOffset":2892,"count":0}],"isBlockCoverage":false}]},{"scriptId":"57","url":"internal/modules/cjs/loader.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":38134,"count":1}],"isBlockCoverage":false},{"functionName":"get hasLoadedAnyUserCJSModule","ranges":[{"startOffset":1880,"endOffset":1949,"count":1}],"isBlockCoverage":true},{"functionName":"stat","ranges":[{"startOffset":4257,"endOffset":4574,"count":1},{"startOffset":4355,"endOffset":4449,"count":0},{"startOffset":4523,"endOffset":4555,"count":0}],"isBlockCoverage":true},{"functionName":"updateChildren","ranges":[{"startOffset":4576,"endOffset":4751,"count":0}],"isBlockCoverage":false},{"functionName":"Module","ranges":[{"startOffset":4753,"endOffset":4990,"count":0}],"isBlockCoverage":false},{"functionName":"wrap","ranges":[{"startOffset":5441,"endOffset":5518,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":5663,"endOffset":5785,"count":0}],"isBlockCoverage":false},{"functionName":"defineProperty","ranges":[{"startOffset":5790,"endOffset":5923,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":5970,"endOffset":5998,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":6003,"endOffset":6057,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":6107,"endOffset":6143,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":6148,"endOffset":6210,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":6275,"endOffset":6305,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":6376,"endOffset":6401,"count":0}],"isBlockCoverage":false},{"functionName":"readPackage","ranges":[{"startOffset":6749,"endOffset":7559,"count":1},{"startOffset":6929,"endOffset":6945,"count":0},{"startOffset":7043,"endOffset":7049,"count":0},{"startOffset":7093,"endOffset":7159,"count":0},{"startOffset":7442,"endOffset":7557,"count":0}],"isBlockCoverage":true},{"functionName":"readPackageScope","ranges":[{"startOffset":7561,"endOffset":8104,"count":1},{"startOffset":7903,"endOffset":7916,"count":0},{"startOffset":8041,"endOffset":8103,"count":0}],"isBlockCoverage":true},{"functionName":"tryPackage","ranges":[{"startOffset":8106,"endOffset":9407,"count":0}],"isBlockCoverage":false},{"functionName":"tryFile","ranges":[{"startOffset":9748,"endOffset":9958,"count":0}],"isBlockCoverage":false},{"functionName":"toRealPath","ranges":[{"startOffset":9960,"endOffset":10091,"count":2}],"isBlockCoverage":true},{"functionName":"tryExtensions","ranges":[{"startOffset":10166,"endOffset":10372,"count":0}],"isBlockCoverage":false},{"functionName":"findLongestRegisteredExtension","ranges":[{"startOffset":10461,"endOffset":10897,"count":0}],"isBlockCoverage":false},{"functionName":"trySelfParentPath","ranges":[{"startOffset":10899,"endOffset":11188,"count":0}],"isBlockCoverage":false},{"functionName":"trySelf","ranges":[{"startOffset":11190,"endOffset":12039,"count":0}],"isBlockCoverage":false},{"functionName":"resolveExports","ranges":[{"startOffset":12200,"endOffset":12938,"count":0}],"isBlockCoverage":false},{"functionName":"Module._findPath","ranges":[{"startOffset":13004,"endOffset":15631,"count":1},{"startOffset":13137,"endOffset":13200,"count":0},{"startOffset":13287,"endOffset":13307,"count":0},{"startOffset":13372,"endOffset":13385,"count":0},{"startOffset":13767,"endOffset":13787,"count":0},{"startOffset":13789,"endOffset":13798,"count":0},{"startOffset":13826,"endOffset":13956,"count":0},{"startOffset":14140,"endOffset":14308,"count":0},{"startOffset":14340,"endOffset":14972,"count":0},{"startOffset":15063,"endOffset":15257,"count":0},{"startOffset":15283,"endOffset":15294,"count":0},{"startOffset":15296,"endOffset":15512,"count":0},{"startOffset":15612,"endOffset":15630,"count":0}],"isBlockCoverage":true},{"functionName":"Module._nodeModulePaths","ranges":[{"startOffset":15875,"endOffset":17266,"count":0}],"isBlockCoverage":false},{"functionName":"Module._nodeModulePaths","ranges":[{"startOffset":17358,"endOffset":18399,"count":0}],"isBlockCoverage":false},{"functionName":"Module._resolveLookupPaths","ranges":[{"startOffset":18433,"endOffset":19571,"count":0}],"isBlockCoverage":false},{"functionName":"emitCircularRequireWarning","ranges":[{"startOffset":19574,"endOffset":19757,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":19950,"endOffset":20322,"count":0}],"isBlockCoverage":false},{"functionName":"getOwnPropertyDescriptor","ranges":[{"startOffset":20327,"endOffset":20568,"count":0}],"isBlockCoverage":false},{"functionName":"getExportsForCircularRequire","ranges":[{"startOffset":20769,"endOffset":21419,"count":0}],"isBlockCoverage":false},{"functionName":"Module._load","ranges":[{"startOffset":21831,"endOffset":24853,"count":0}],"isBlockCoverage":false},{"functionName":"Module._resolveFilename","ranges":[{"startOffset":24882,"endOffset":27745,"count":0}],"isBlockCoverage":false},{"functionName":"finalizeEsmResolution","ranges":[{"startOffset":27748,"endOffset":28462,"count":0}],"isBlockCoverage":false},{"functionName":"createEsmNotFoundErr","ranges":[{"startOffset":28464,"endOffset":28754,"count":0}],"isBlockCoverage":false},{"functionName":"Module.load","ranges":[{"startOffset":28843,"endOffset":29647,"count":0}],"isBlockCoverage":false},{"functionName":"Module.require","ranges":[{"startOffset":29765,"endOffset":30064,"count":0}],"isBlockCoverage":false},{"functionName":"wrapSafe","ranges":[{"startOffset":30244,"endOffset":31360,"count":0}],"isBlockCoverage":false},{"functionName":"Module._compile","ranges":[{"startOffset":31560,"endOffset":33402,"count":0}],"isBlockCoverage":false},{"functionName":"Module._extensions..js","ranges":[{"startOffset":33461,"endOffset":34235,"count":0}],"isBlockCoverage":false},{"functionName":"Module._extensions..json","ranges":[{"startOffset":34299,"endOffset":34663,"count":0}],"isBlockCoverage":false},{"functionName":"Module._extensions..node","ranges":[{"startOffset":34727,"endOffset":35045,"count":0}],"isBlockCoverage":false},{"functionName":"createRequireFromPath","ranges":[{"startOffset":35048,"endOffset":35473,"count":0}],"isBlockCoverage":false},{"functionName":"createRequire","ranges":[{"startOffset":35758,"endOffset":36311,"count":0}],"isBlockCoverage":false},{"functionName":"Module._initPaths","ranges":[{"startOffset":36372,"endOffset":37280,"count":1},{"startOffset":36413,"endOffset":36438,"count":0},{"startOffset":36490,"endOffset":36513,"count":0},{"startOffset":36721,"endOffset":36763,"count":0},{"startOffset":37030,"endOffset":37159,"count":0}],"isBlockCoverage":true},{"functionName":"pathsFilterCB","ranges":[{"startOffset":37082,"endOffset":37139,"count":0}],"isBlockCoverage":false},{"functionName":"Module._preloadModules","ranges":[{"startOffset":37308,"endOffset":37890,"count":0}],"isBlockCoverage":false},{"functionName":"syncBuiltinESMExports","ranges":[{"startOffset":37924,"endOffset":38080,"count":0}],"isBlockCoverage":false}]},{"scriptId":"58","url":"vm.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":12941,"count":1}],"isBlockCoverage":false},{"functionName":"Script","ranges":[{"startOffset":1992,"endOffset":4152,"count":0}],"isBlockCoverage":false},{"functionName":"runInThisContext","ranges":[{"startOffset":4156,"endOffset":4436,"count":0}],"isBlockCoverage":false},{"functionName":"runInContext","ranges":[{"startOffset":4440,"endOffset":4846,"count":0}],"isBlockCoverage":false},{"functionName":"runInNewContext","ranges":[{"startOffset":4850,"endOffset":5021,"count":0}],"isBlockCoverage":false},{"functionName":"validateContext","ranges":[{"startOffset":5025,"endOffset":5244,"count":0}],"isBlockCoverage":false},{"functionName":"getRunInContextArgs","ranges":[{"startOffset":5246,"endOffset":5837,"count":0}],"isBlockCoverage":false},{"functionName":"getContextOptions","ranges":[{"startOffset":5839,"endOffset":6907,"count":0}],"isBlockCoverage":false},{"functionName":"isContext","ranges":[{"startOffset":6909,"endOffset":7091,"count":0}],"isBlockCoverage":false},{"functionName":"createContext","ranges":[{"startOffset":7126,"endOffset":8261,"count":0}],"isBlockCoverage":false},{"functionName":"createScript","ranges":[{"startOffset":8263,"endOffset":8339,"count":0}],"isBlockCoverage":false},{"functionName":"sigintHandlersWrap","ranges":[{"startOffset":8493,"endOffset":8939,"count":0}],"isBlockCoverage":false},{"functionName":"runInContext","ranges":[{"startOffset":8941,"endOffset":9338,"count":0}],"isBlockCoverage":false},{"functionName":"runInNewContext","ranges":[{"startOffset":9340,"endOffset":9692,"count":0}],"isBlockCoverage":false},{"functionName":"runInThisContext","ranges":[{"startOffset":9694,"endOffset":9880,"count":0}],"isBlockCoverage":false},{"functionName":"compileFunction","ranges":[{"startOffset":9882,"endOffset":11615,"count":0}],"isBlockCoverage":false},{"functionName":"measureMemory","ranges":[{"startOffset":11892,"endOffset":12454,"count":0}],"isBlockCoverage":false}]},{"scriptId":"59","url":"internal/modules/package_json_reader.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":975,"count":1}],"isBlockCoverage":false},{"functionName":"read","ranges":[{"startOffset":279,"endOffset":946,"count":2},{"startOffset":332,"endOffset":896,"count":1},{"startOffset":686,"endOffset":739,"count":0},{"startOffset":789,"endOffset":892,"count":0},{"startOffset":896,"endOffset":945,"count":1}],"isBlockCoverage":true}]},{"scriptId":"60","url":"internal/process/esm_loader.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2326,"count":1}],"isBlockCoverage":false},{"functionName":"exports.initializeImportMetaObject","ranges":[{"startOffset":405,"endOffset":701,"count":0}],"isBlockCoverage":false},{"functionName":"exports.importModuleDynamicallyCallback","ranges":[{"startOffset":746,"endOffset":1137,"count":4},{"startOffset":1081,"endOffset":1136,"count":0}],"isBlockCoverage":true},{"functionName":"initializeLoader","ranges":[{"startOffset":1202,"endOffset":1969,"count":1},{"startOffset":1388,"endOffset":1968,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":1722,"endOffset":1963,"count":0}],"isBlockCoverage":true},{"functionName":"loadESM","ranges":[{"startOffset":1989,"endOffset":2324,"count":1},{"startOffset":2097,"endOffset":2322,"count":0}],"isBlockCoverage":true}]},{"scriptId":"61","url":"internal/modules/esm/loader.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":8625,"count":1}],"isBlockCoverage":false},{"functionName":"Loader","ranges":[{"startOffset":1416,"endOffset":3189,"count":1}],"isBlockCoverage":true},{"functionName":"resolve","ranges":[{"startOffset":3193,"endOffset":3816,"count":7},{"startOffset":3298,"endOffset":3337,"count":6},{"startOffset":3514,"endOffset":3617,"count":0},{"startOffset":3689,"endOffset":3796,"count":0}],"isBlockCoverage":true},{"functionName":"getFormat","ranges":[{"startOffset":3820,"endOffset":5128,"count":7},{"startOffset":3977,"endOffset":4084,"count":0},{"startOffset":4153,"endOffset":4410,"count":0},{"startOffset":4447,"endOffset":4562,"count":0},{"startOffset":4594,"endOffset":4622,"count":5},{"startOffset":4622,"endOffset":4666,"count":2},{"startOffset":4666,"endOffset":4835,"count":0},{"startOffset":4835,"endOffset":4927,"count":2},{"startOffset":4928,"endOffset":4977,"count":0},{"startOffset":4984,"endOffset":5104,"count":0},{"startOffset":5104,"endOffset":5127,"count":2}],"isBlockCoverage":true},{"functionName":"eval","ranges":[{"startOffset":5132,"endOffset":5807,"count":0}],"isBlockCoverage":false},{"functionName":"import","ranges":[{"startOffset":5811,"endOffset":5982,"count":5}],"isBlockCoverage":true},{"functionName":"hook","ranges":[{"startOffset":5986,"endOffset":6947,"count":0}],"isBlockCoverage":false},{"functionName":"runGlobalPreloadCode","ranges":[{"startOffset":6951,"endOffset":7755,"count":0}],"isBlockCoverage":false},{"functionName":"getModuleJob","ranges":[{"startOffset":7759,"endOffset":8549,"count":7},{"startOffset":8046,"endOffset":8083,"count":0},{"startOffset":8117,"endOffset":8128,"count":4},{"startOffset":8128,"endOffset":8170,"count":3},{"startOffset":8170,"endOffset":8214,"count":0},{"startOffset":8214,"endOffset":8316,"count":3},{"startOffset":8316,"endOffset":8346,"count":1},{"startOffset":8347,"endOffset":8381,"count":1}],"isBlockCoverage":true}]},{"scriptId":"62","url":"internal/modules/esm/module_map.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":878,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":175,"endOffset":200,"count":1}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":419,"endOffset":492,"count":7}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":495,"endOffset":771,"count":3},{"startOffset":585,"endOffset":621,"count":0},{"startOffset":623,"endOffset":693,"count":0}],"isBlockCoverage":true},{"functionName":"has","ranges":[{"startOffset":774,"endOffset":847,"count":0}],"isBlockCoverage":false}]},{"scriptId":"63","url":"internal/modules/esm/module_job.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":5778,"count":1}],"isBlockCoverage":false},{"functionName":"ModuleJob","ranges":[{"startOffset":832,"endOffset":2478,"count":3}],"isBlockCoverage":true},{"functionName":"link","ranges":[{"startOffset":1301,"endOffset":2105,"count":3}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":1757,"endOffset":1984,"count":2}],"isBlockCoverage":true},{"functionName":"instantiate","ranges":[{"startOffset":2482,"endOffset":2627,"count":5},{"startOffset":2539,"endOffset":2593,"count":1}],"isBlockCoverage":true},{"functionName":"_instantiate","ranges":[{"startOffset":2631,"endOffset":5498,"count":1},{"startOffset":3105,"endOffset":3282,"count":0},{"startOffset":3339,"endOffset":5251,"count":0},{"startOffset":5298,"endOffset":5494,"count":3}],"isBlockCoverage":true},{"functionName":"addJobsToDependencyGraph","ranges":[{"startOffset":2730,"endOffset":3004,"count":3},{"startOffset":2791,"endOffset":2816,"count":0}],"isBlockCoverage":true},{"functionName":"run","ranges":[{"startOffset":5502,"endOffset":5698,"count":5}],"isBlockCoverage":true}]},{"scriptId":"64","url":"internal/modules/esm/resolve.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":28247,"count":1}],"isBlockCoverage":false},{"functionName":"getConditionsSet","ranges":[{"startOffset":1873,"endOffset":2225,"count":2},{"startOffset":1982,"endOffset":2190,"count":0}],"isBlockCoverage":true},{"functionName":"tryStatSync","ranges":[{"startOffset":2336,"endOffset":2441,"count":2},{"startOffset":2404,"endOffset":2439,"count":0}],"isBlockCoverage":true},{"functionName":"getPackageConfig","ranges":[{"startOffset":2443,"endOffset":3781,"count":2},{"startOffset":2571,"endOffset":2680,"count":1},{"startOffset":2680,"endOffset":2955,"count":0},{"startOffset":2955,"endOffset":3025,"count":1},{"startOffset":3025,"endOffset":3204,"count":0},{"startOffset":3204,"endOffset":3326,"count":1},{"startOffset":3326,"endOffset":3345,"count":0},{"startOffset":3347,"endOffset":3450,"count":1},{"startOffset":3450,"endOffset":3467,"count":0},{"startOffset":3467,"endOffset":3545,"count":1},{"startOffset":3545,"endOffset":3567,"count":0},{"startOffset":3569,"endOffset":3583,"count":0},{"startOffset":3583,"endOffset":3780,"count":1}],"isBlockCoverage":true},{"functionName":"getPackageScopeConfig","ranges":[{"startOffset":3783,"endOffset":4883,"count":2},{"startOffset":4041,"endOffset":4047,"count":0},{"startOffset":4227,"endOffset":4550,"count":0},{"startOffset":4554,"endOffset":4882,"count":0}],"isBlockCoverage":true},{"functionName":"fileExists","ranges":[{"startOffset":5139,"endOffset":5218,"count":0}],"isBlockCoverage":false},{"functionName":"legacyMainResolve","ranges":[{"startOffset":5220,"endOffset":6891,"count":0}],"isBlockCoverage":false},{"functionName":"resolveExtensionsWithTryExactName","ranges":[{"startOffset":6893,"endOffset":7024,"count":0}],"isBlockCoverage":false},{"functionName":"resolveExtensions","ranges":[{"startOffset":7080,"endOffset":7337,"count":0}],"isBlockCoverage":false},{"functionName":"resolveIndex","ranges":[{"startOffset":7339,"endOffset":7426,"count":0}],"isBlockCoverage":false},{"functionName":"finalizeResolution","ranges":[{"startOffset":7464,"endOffset":8671,"count":2},{"startOffset":7577,"endOffset":7720,"count":0},{"startOffset":7834,"endOffset":8243,"count":0},{"startOffset":8308,"endOffset":8344,"count":0},{"startOffset":8381,"endOffset":8508,"count":0},{"startOffset":8535,"endOffset":8649,"count":0}],"isBlockCoverage":true},{"functionName":"throwImportNotDefined","ranges":[{"startOffset":8673,"endOffset":8888,"count":0}],"isBlockCoverage":false},{"functionName":"throwExportsNotFound","ranges":[{"startOffset":8890,"endOffset":9089,"count":0}],"isBlockCoverage":false},{"functionName":"throwInvalidSubpath","ranges":[{"startOffset":9091,"endOffset":9441,"count":0}],"isBlockCoverage":false},{"functionName":"throwInvalidPackageTarget","ranges":[{"startOffset":9443,"endOffset":9825,"count":0}],"isBlockCoverage":false},{"functionName":"resolvePackageTargetString","ranges":[{"startOffset":9926,"endOffset":11589,"count":0}],"isBlockCoverage":false},{"functionName":"isArrayIndex","ranges":[{"startOffset":11644,"endOffset":11784,"count":0}],"isBlockCoverage":false},{"functionName":"resolvePackageTarget","ranges":[{"startOffset":11786,"endOffset":13918,"count":0}],"isBlockCoverage":false},{"functionName":"isConditionalExportsMainSugar","ranges":[{"startOffset":13920,"endOffset":14855,"count":0}],"isBlockCoverage":false},{"functionName":"packageExportsResolve","ranges":[{"startOffset":15040,"endOffset":16923,"count":0}],"isBlockCoverage":false},{"functionName":"packageImportsResolve","ranges":[{"startOffset":16925,"endOffset":18921,"count":0}],"isBlockCoverage":false},{"functionName":"getPackageType","ranges":[{"startOffset":18923,"endOffset":19036,"count":2}],"isBlockCoverage":true},{"functionName":"packageResolve","ranges":[{"startOffset":19149,"endOffset":21981,"count":0}],"isBlockCoverage":false},{"functionName":"isBareSpecifier","ranges":[{"startOffset":21983,"endOffset":22093,"count":0}],"isBlockCoverage":false},{"functionName":"isRelativeSpecifier","ranges":[{"startOffset":22095,"endOffset":22366,"count":2},{"startOffset":22165,"endOffset":22348,"count":1},{"startOffset":22235,"endOffset":22344,"count":0},{"startOffset":22348,"endOffset":22365,"count":1}],"isBlockCoverage":true},{"functionName":"shouldBeTreatedAsRelativeOrAbsolutePath","ranges":[{"startOffset":22368,"endOffset":22551,"count":2},{"startOffset":22454,"endOffset":22467,"count":0},{"startOffset":22496,"endOffset":22508,"count":0}],"isBlockCoverage":true},{"functionName":"moduleResolve","ranges":[{"startOffset":22664,"endOffset":23235,"count":2},{"startOffset":22892,"endOffset":23188,"count":1},{"startOffset":22970,"endOffset":23048,"count":0},{"startOffset":23109,"endOffset":23184,"count":0}],"isBlockCoverage":true},{"functionName":"resolveAsCommonJS","ranges":[{"startOffset":23381,"endOffset":24789,"count":0}],"isBlockCoverage":false},{"functionName":"defaultResolve","ranges":[{"startOffset":24791,"endOffset":28097,"count":7},{"startOffset":24923,"endOffset":24942,"count":6},{"startOffset":24944,"endOffset":25547,"count":0},{"startOffset":25640,"endOffset":25694,"count":0},{"startOffset":25699,"endOffset":25707,"count":6},{"startOffset":25721,"endOffset":25751,"count":1},{"startOffset":25757,"endOffset":25783,"count":0},{"startOffset":25797,"endOffset":25827,"count":1},{"startOffset":25828,"endOffset":25858,"count":0},{"startOffset":25864,"endOffset":25913,"count":0},{"startOffset":25966,"endOffset":26022,"count":5},{"startOffset":26022,"endOffset":26039,"count":2},{"startOffset":26039,"endOffset":26087,"count":1},{"startOffset":26089,"endOffset":26177,"count":0},{"startOffset":26177,"endOffset":26235,"count":2},{"startOffset":26235,"endOffset":26762,"count":1},{"startOffset":26719,"endOffset":26758,"count":0},{"startOffset":26762,"endOffset":26891,"count":2},{"startOffset":26891,"endOffset":27695,"count":0},{"startOffset":27695,"endOffset":27710,"count":2},{"startOffset":27710,"endOffset":27733,"count":1},{"startOffset":27734,"endOffset":27753,"count":1},{"startOffset":27755,"endOffset":28066,"count":2},{"startOffset":27995,"endOffset":28000,"count":0},{"startOffset":28066,"endOffset":28096,"count":2}],"isBlockCoverage":true}]},{"scriptId":"65","url":"internal/modules/esm/get_format.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2496,"count":1}],"isBlockCoverage":false},{"functionName":"defaultGetFormat","ranges":[{"startOffset":1131,"endOffset":2450,"count":7},{"startOffset":1244,"endOffset":1283,"count":5},{"startOffset":1283,"endOffset":1350,"count":2},{"startOffset":1350,"endOffset":1760,"count":0},{"startOffset":1760,"endOffset":2421,"count":2},{"startOffset":1951,"endOffset":1963,"count":0},{"startOffset":1970,"endOffset":2023,"count":0},{"startOffset":2041,"endOffset":2378,"count":0},{"startOffset":2407,"endOffset":2414,"count":0},{"startOffset":2421,"endOffset":2449,"count":0}],"isBlockCoverage":true}]},{"scriptId":"66","url":"internal/modules/esm/get_source.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1307,"count":1}],"isBlockCoverage":false},{"functionName":"defaultGetSource","ranges":[{"startOffset":609,"endOffset":1261,"count":2},{"startOffset":810,"endOffset":1155,"count":0},{"startOffset":1180,"endOffset":1238,"count":0}],"isBlockCoverage":true}]},{"scriptId":"67","url":"internal/fs/promises.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":20020,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":2272,"endOffset":2432,"count":0}],"isBlockCoverage":false},{"functionName":"FileHandle","ranges":[{"startOffset":2480,"endOffset":2657,"count":17},{"startOffset":2594,"endOffset":2598,"count":0}],"isBlockCoverage":true},{"functionName":"getAsyncId","ranges":[{"startOffset":2661,"endOffset":2718,"count":0}],"isBlockCoverage":false},{"functionName":"get fd","ranges":[{"startOffset":2722,"endOffset":2758,"count":93}],"isBlockCoverage":true},{"functionName":"appendFile","ranges":[{"startOffset":2762,"endOffset":2844,"count":0}],"isBlockCoverage":false},{"functionName":"chmod","ranges":[{"startOffset":2848,"endOffset":2904,"count":0}],"isBlockCoverage":false},{"functionName":"chown","ranges":[{"startOffset":2908,"endOffset":2972,"count":0}],"isBlockCoverage":false},{"functionName":"datasync","ranges":[{"startOffset":2976,"endOffset":3028,"count":0}],"isBlockCoverage":false},{"functionName":"sync","ranges":[{"startOffset":3032,"endOffset":3076,"count":0}],"isBlockCoverage":false},{"functionName":"read","ranges":[{"startOffset":3080,"endOffset":3189,"count":0}],"isBlockCoverage":false},{"functionName":"readv","ranges":[{"startOffset":3193,"endOffset":3274,"count":0}],"isBlockCoverage":false},{"functionName":"readFile","ranges":[{"startOffset":3278,"endOffset":3345,"count":0}],"isBlockCoverage":false},{"functionName":"stat","ranges":[{"startOffset":3349,"endOffset":3409,"count":0}],"isBlockCoverage":false},{"functionName":"truncate","ranges":[{"startOffset":3413,"endOffset":3477,"count":0}],"isBlockCoverage":false},{"functionName":"utimes","ranges":[{"startOffset":3481,"endOffset":3555,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":3559,"endOffset":3670,"count":0}],"isBlockCoverage":false},{"functionName":"writev","ranges":[{"startOffset":3674,"endOffset":3757,"count":0}],"isBlockCoverage":false},{"functionName":"writeFile","ranges":[{"startOffset":3761,"endOffset":3842,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":3846,"endOffset":4538,"count":17}],"isBlockCoverage":false},{"functionName":"close","ranges":[{"startOffset":3854,"endOffset":4538,"count":17},{"startOffset":3888,"endOffset":3926,"count":0},{"startOffset":3957,"endOffset":3998,"count":0},{"startOffset":4192,"endOffset":4501,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":4128,"endOffset":4184,"count":17}],"isBlockCoverage":true},{"functionName":".Promise.finally.","ranges":[{"startOffset":4240,"endOffset":4346,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":4356,"endOffset":4493,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":4542,"endOffset":5011,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":5015,"endOffset":5068,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":5072,"endOffset":5159,"count":0}],"isBlockCoverage":false},{"functionName":"fsCall","ranges":[{"startOffset":5163,"endOffset":5781,"count":0}],"isBlockCoverage":false},{"functionName":"writeFileHandle","ranges":[{"startOffset":5783,"endOffset":6459,"count":0}],"isBlockCoverage":false},{"functionName":"readFileHandle","ranges":[{"startOffset":6461,"endOffset":7737,"count":17},{"startOffset":6572,"endOffset":6589,"count":0},{"startOffset":6591,"endOffset":6667,"count":0},{"startOffset":6760,"endOffset":6777,"count":0},{"startOffset":6779,"endOffset":6855,"count":0},{"startOffset":6923,"endOffset":6964,"count":16},{"startOffset":6964,"endOffset":6989,"count":1},{"startOffset":7022,"endOffset":7060,"count":0},{"startOffset":7114,"endOffset":7141,"count":2},{"startOffset":7142,"endOffset":7184,"count":15},{"startOffset":7216,"endOffset":7567,"count":76},{"startOffset":7233,"endOffset":7250,"count":0},{"startOffset":7252,"endOffset":7332,"count":0},{"startOffset":7460,"endOffset":7523,"count":75},{"startOffset":7523,"endOffset":7563,"count":59},{"startOffset":7567,"endOffset":7626,"count":16},{"startOffset":7626,"endOffset":7637,"count":8},{"startOffset":7638,"endOffset":7661,"count":8},{"startOffset":7690,"endOffset":7725,"count":14},{"startOffset":7726,"endOffset":7734,"count":2}],"isBlockCoverage":true},{"functionName":"access","ranges":[{"startOffset":7890,"endOffset":8111,"count":0}],"isBlockCoverage":false},{"functionName":"copyFile","ranges":[{"startOffset":8113,"endOffset":8471,"count":0}],"isBlockCoverage":false},{"functionName":"open","ranges":[{"startOffset":8591,"endOffset":8916,"count":17}],"isBlockCoverage":true},{"functionName":"read","ranges":[{"startOffset":8918,"endOffset":9972,"count":76},{"startOffset":9057,"endOffset":9339,"count":0},{"startOffset":9363,"endOffset":9384,"count":0},{"startOffset":9476,"endOffset":9513,"count":0},{"startOffset":9542,"endOffset":9672,"count":0},{"startOffset":9776,"endOffset":9790,"count":0},{"startOffset":9930,"endOffset":9932,"count":75},{"startOffset":9932,"endOffset":9936,"count":16}],"isBlockCoverage":true},{"functionName":"readv","ranges":[{"startOffset":9974,"endOffset":10294,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":10296,"endOffset":11217,"count":0}],"isBlockCoverage":false},{"functionName":"writev","ranges":[{"startOffset":11219,"endOffset":11551,"count":0}],"isBlockCoverage":false},{"functionName":"rename","ranges":[{"startOffset":11553,"endOffset":11859,"count":0}],"isBlockCoverage":false},{"functionName":"truncate","ranges":[{"startOffset":11861,"endOffset":12004,"count":0}],"isBlockCoverage":false},{"functionName":"ftruncate","ranges":[{"startOffset":12006,"endOffset":12165,"count":0}],"isBlockCoverage":false},{"functionName":"rm","ranges":[{"startOffset":12167,"endOffset":12364,"count":0}],"isBlockCoverage":false},{"functionName":"rmdir","ranges":[{"startOffset":12366,"endOffset":12629,"count":0}],"isBlockCoverage":false},{"functionName":"fdatasync","ranges":[{"startOffset":12631,"endOffset":12720,"count":0}],"isBlockCoverage":false},{"functionName":"fsync","ranges":[{"startOffset":12722,"endOffset":12803,"count":0}],"isBlockCoverage":false},{"functionName":"mkdir","ranges":[{"startOffset":12805,"endOffset":13337,"count":0}],"isBlockCoverage":false},{"functionName":"readdir","ranges":[{"startOffset":13339,"endOffset":13791,"count":1},{"startOffset":13729,"endOffset":13775,"count":0}],"isBlockCoverage":true},{"functionName":"readlink","ranges":[{"startOffset":13793,"endOffset":14036,"count":0}],"isBlockCoverage":false},{"functionName":"symlink","ranges":[{"startOffset":14038,"endOffset":14451,"count":0}],"isBlockCoverage":false},{"functionName":"fstat","ranges":[{"startOffset":14453,"endOffset":14631,"count":0}],"isBlockCoverage":false},{"functionName":"lstat","ranges":[{"startOffset":14633,"endOffset":14903,"count":0}],"isBlockCoverage":false},{"functionName":"stat","ranges":[{"startOffset":14905,"endOffset":15172,"count":0}],"isBlockCoverage":false},{"functionName":"link","ranges":[{"startOffset":15174,"endOffset":15497,"count":0}],"isBlockCoverage":false},{"functionName":"unlink","ranges":[{"startOffset":15499,"endOffset":15637,"count":0}],"isBlockCoverage":false},{"functionName":"fchmod","ranges":[{"startOffset":15639,"endOffset":15772,"count":0}],"isBlockCoverage":false},{"functionName":"chmod","ranges":[{"startOffset":15774,"endOffset":15960,"count":0}],"isBlockCoverage":false},{"functionName":"lchmod","ranges":[{"startOffset":15962,"endOffset":16200,"count":0}],"isBlockCoverage":false},{"functionName":"lchown","ranges":[{"startOffset":16202,"endOffset":16478,"count":0}],"isBlockCoverage":false},{"functionName":"fchown","ranges":[{"startOffset":16480,"endOffset":16677,"count":0}],"isBlockCoverage":false},{"functionName":"chown","ranges":[{"startOffset":16679,"endOffset":16952,"count":0}],"isBlockCoverage":false},{"functionName":"utimes","ranges":[{"startOffset":16954,"endOffset":17226,"count":0}],"isBlockCoverage":false},{"functionName":"futimes","ranges":[{"startOffset":17228,"endOffset":17427,"count":0}],"isBlockCoverage":false},{"functionName":"lutimes","ranges":[{"startOffset":17429,"endOffset":17706,"count":0}],"isBlockCoverage":false},{"functionName":"realpath","ranges":[{"startOffset":17708,"endOffset":17885,"count":0}],"isBlockCoverage":false},{"functionName":"mkdtemp","ranges":[{"startOffset":17887,"endOffset":18217,"count":0}],"isBlockCoverage":false},{"functionName":"writeFile","ranges":[{"startOffset":18219,"endOffset":18938,"count":0}],"isBlockCoverage":false},{"functionName":"appendFile","ranges":[{"startOffset":18940,"endOffset":19181,"count":0}],"isBlockCoverage":false},{"functionName":"readFile","ranges":[{"startOffset":19183,"endOffset":19613,"count":17},{"startOffset":19300,"endOffset":19306,"count":0},{"startOffset":19347,"endOffset":19384,"count":0},{"startOffset":19417,"endOffset":19493,"count":0}],"isBlockCoverage":true}]},{"scriptId":"68","url":"internal/fs/rimraf.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":7039,"count":1}],"isBlockCoverage":false},{"functionName":"rimraf","ranges":[{"startOffset":1137,"endOffset":1597,"count":0}],"isBlockCoverage":false},{"functionName":"_rimraf","ranges":[{"startOffset":1600,"endOffset":2458,"count":0}],"isBlockCoverage":false},{"functionName":"fixWinEPERM","ranges":[{"startOffset":2461,"endOffset":2896,"count":0}],"isBlockCoverage":false},{"functionName":"_rmdir","ranges":[{"startOffset":2899,"endOffset":3197,"count":0}],"isBlockCoverage":false},{"functionName":"_rmchildren","ranges":[{"startOffset":3200,"endOffset":3872,"count":0}],"isBlockCoverage":false},{"functionName":"rimrafPromises","ranges":[{"startOffset":3875,"endOffset":4073,"count":0}],"isBlockCoverage":false},{"functionName":"rimrafSync","ranges":[{"startOffset":4076,"endOffset":4781,"count":0}],"isBlockCoverage":false},{"functionName":"_unlinkSync","ranges":[{"startOffset":4784,"endOffset":5267,"count":0}],"isBlockCoverage":false},{"functionName":"_rmdirSync","ranges":[{"startOffset":5270,"endOffset":6540,"count":0}],"isBlockCoverage":false},{"functionName":"fixWinEPERMSync","ranges":[{"startOffset":6543,"endOffset":6979,"count":0}],"isBlockCoverage":false}]},{"scriptId":"69","url":"internal/modules/esm/transform_source.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":215,"count":1}],"isBlockCoverage":false},{"functionName":"defaultTransformSource","ranges":[{"startOffset":15,"endOffset":157,"count":2}],"isBlockCoverage":true}]},{"scriptId":"70","url":"internal/modules/esm/translators.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":12048,"count":1}],"isBlockCoverage":false},{"functionName":"lazyTypes","ranges":[{"startOffset":416,"endOffset":528,"count":4},{"startOffset":462,"endOffset":476,"count":3},{"startOffset":476,"endOffset":527,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":1202,"endOffset":1227,"count":1}],"isBlockCoverage":true},{"functionName":"initCJSParse","ranges":[{"startOffset":1860,"endOffset":2152,"count":0}],"isBlockCoverage":false},{"functionName":"assertBufferSource","ranges":[{"startOffset":2286,"endOffset":2706,"count":4},{"startOffset":2363,"endOffset":2390,"count":2},{"startOffset":2392,"endOffset":2409,"count":0},{"startOffset":2503,"endOffset":2528,"count":0},{"startOffset":2547,"endOffset":2626,"count":0},{"startOffset":2627,"endOffset":2631,"count":0}],"isBlockCoverage":true},{"functionName":"stringify","ranges":[{"startOffset":2708,"endOffset":2926,"count":2},{"startOffset":2767,"endOffset":2779,"count":0},{"startOffset":2863,"endOffset":2882,"count":1},{"startOffset":2883,"endOffset":2892,"count":1}],"isBlockCoverage":true},{"functionName":"errPath","ranges":[{"startOffset":2928,"endOffset":3073,"count":0}],"isBlockCoverage":false},{"functionName":"importModuleDynamically","ranges":[{"startOffset":3075,"endOffset":3189,"count":4}],"isBlockCoverage":true},{"functionName":"createImportMetaResolve","ranges":[{"startOffset":3191,"endOffset":3539,"count":0}],"isBlockCoverage":false},{"functionName":"initializeImportMeta","ranges":[{"startOffset":3541,"endOffset":3711,"count":0}],"isBlockCoverage":false},{"functionName":"moduleStrategy","ranges":[{"startOffset":3793,"endOffset":4374,"count":2}],"isBlockCoverage":true},{"functionName":"enrichCJSError","ranges":[{"startOffset":4378,"endOffset":5277,"count":0}],"isBlockCoverage":false},{"functionName":"commonjsStrategy","ranges":[{"startOffset":5435,"endOffset":6741,"count":0}],"isBlockCoverage":false},{"functionName":"cjsPreparseModuleExports","ranges":[{"startOffset":6745,"endOffset":8189,"count":0}],"isBlockCoverage":false},{"functionName":"builtinStrategy","ranges":[{"startOffset":8313,"endOffset":8701,"count":1},{"startOffset":8574,"endOffset":8626,"count":0}],"isBlockCoverage":true},{"functionName":"jsonStrategy","ranges":[{"startOffset":8765,"endOffset":10884,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":10950,"endOffset":12045,"count":0}],"isBlockCoverage":false}]},{"scriptId":"71","url":"internal/modules/esm/create_dynamic_module.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1756,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":190,"endOffset":215,"count":0}],"isBlockCoverage":false},{"functionName":"createImport","ranges":[{"startOffset":219,"endOffset":409,"count":0}],"isBlockCoverage":false},{"functionName":"createExport","ranges":[{"startOffset":411,"endOffset":612,"count":0}],"isBlockCoverage":false},{"functionName":"createDynamicModule","ranges":[{"startOffset":642,"endOffset":1715,"count":0}],"isBlockCoverage":false}]},{"scriptId":"72","url":"internal/vm/module.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":12877,"count":1}],"isBlockCoverage":false},{"functionName":"Module","ranges":[{"startOffset":1618,"endOffset":3804,"count":0}],"isBlockCoverage":false},{"functionName":"get identifier","ranges":[{"startOffset":3808,"endOffset":3945,"count":0}],"isBlockCoverage":false},{"functionName":"get context","ranges":[{"startOffset":3949,"endOffset":4082,"count":0}],"isBlockCoverage":false},{"functionName":"get namespace","ranges":[{"startOffset":4086,"endOffset":4363,"count":0}],"isBlockCoverage":false},{"functionName":"get status","ranges":[{"startOffset":4367,"endOffset":4520,"count":0}],"isBlockCoverage":false},{"functionName":"get error","ranges":[{"startOffset":4524,"endOffset":4774,"count":0}],"isBlockCoverage":false},{"functionName":"link","ranges":[{"startOffset":4778,"endOffset":5257,"count":0}],"isBlockCoverage":false},{"functionName":"evaluate","ranges":[{"startOffset":5261,"endOffset":6213,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":6217,"endOffset":6945,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":7092,"endOffset":7128,"count":0}],"isBlockCoverage":false},{"functionName":"SourceTextModule","ranges":[{"startOffset":7133,"endOffset":9559,"count":0}],"isBlockCoverage":false},{"functionName":"get dependencySpecifiers","ranges":[{"startOffset":9563,"endOffset":9862,"count":0}],"isBlockCoverage":false},{"functionName":"get status","ranges":[{"startOffset":9866,"endOffset":10135,"count":0}],"isBlockCoverage":false},{"functionName":"get error","ranges":[{"startOffset":10139,"endOffset":10335,"count":0}],"isBlockCoverage":false},{"functionName":"createCachedData","ranges":[{"startOffset":10339,"endOffset":10601,"count":0}],"isBlockCoverage":false},{"functionName":"SyntheticModule","ranges":[{"startOffset":10646,"endOffset":11943,"count":0}],"isBlockCoverage":false},{"functionName":"setExport","ranges":[{"startOffset":11947,"endOffset":12249,"count":0}],"isBlockCoverage":false},{"functionName":"importModuleDynamicallyWrap","ranges":[{"startOffset":12253,"endOffset":12715,"count":0}],"isBlockCoverage":false},{"functionName":"getModuleFromWrap","ranges":[{"startOffset":12837,"endOffset":12872,"count":4}],"isBlockCoverage":true}]},{"scriptId":"73","url":"internal/modules/run_main.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2582,"count":1}],"isBlockCoverage":false},{"functionName":"resolveMainPath","ranges":[{"startOffset":220,"endOffset":658,"count":1},{"startOffset":487,"endOffset":494,"count":0}],"isBlockCoverage":true},{"functionName":"shouldUseESMLoader","ranges":[{"startOffset":660,"endOffset":1215,"count":1},{"startOffset":784,"endOffset":796,"count":0},{"startOffset":944,"endOffset":956,"count":0},{"startOffset":1051,"endOffset":1063,"count":0},{"startOffset":1114,"endOffset":1127,"count":0}],"isBlockCoverage":true},{"functionName":"runMainESM","ranges":[{"startOffset":1217,"endOffset":1552,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":1400,"endOffset":1547,"count":1},{"startOffset":1497,"endOffset":1507,"count":0}],"isBlockCoverage":true},{"functionName":"handleMainPromise","ranges":[{"startOffset":1554,"endOffset":1991,"count":1}],"isBlockCoverage":true},{"functionName":"handler","ranges":[{"startOffset":1803,"endOffset":1896,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":1953,"endOffset":1987,"count":1}],"isBlockCoverage":true},{"functionName":"executeUserEntryPoint","ranges":[{"startOffset":2177,"endOffset":2512,"count":1},{"startOffset":2387,"endOffset":2394,"count":0},{"startOffset":2400,"endOffset":2510,"count":0}],"isBlockCoverage":true}]},{"scriptId":"74","url":"file:///home/runner/work/jslint/jslint/test.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":10735,"count":1}],"isBlockCoverage":true},{"functionName":"assertOrThrow","ranges":[{"startOffset":72,"endOffset":228,"count":846},{"startOffset":189,"endOffset":226,"count":1}],"isBlockCoverage":true},{"functionName":"noop","ranges":[{"startOffset":230,"endOffset":301,"count":1}],"isBlockCoverage":true},{"functionName":"testCaseJslintCli","ranges":[{"startOffset":304,"endOffset":916,"count":1}],"isBlockCoverage":true},{"functionName":"process.exit","ranges":[{"startOffset":419,"endOffset":490,"count":1}],"isBlockCoverage":true},{"functionName":"testCaseJslintMisc","ranges":[{"startOffset":923,"endOffset":1155,"count":1}],"isBlockCoverage":true},{"functionName":"testCaseJslintOption","ranges":[{"startOffset":1162,"endOffset":1741,"count":1}],"isBlockCoverage":true},{"functionName":"testCaseJslintCodeValidate","ranges":[{"startOffset":1748,"endOffset":4484,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":4196,"endOffset":4480,"count":15}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":4243,"endOffset":4472,"count":38}],"isBlockCoverage":true},{"functionName":"testCaseJslintWarningsValidate","ranges":[{"startOffset":4491,"endOffset":10730,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":7915,"endOffset":8455,"count":7}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":8013,"endOffset":8447,"count":96}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":8119,"endOffset":8247,"count":151}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":8709,"endOffset":10726,"count":198},{"startOffset":9494,"endOffset":10033,"count":158},{"startOffset":9611,"endOffset":9628,"count":1},{"startOffset":9641,"endOffset":9742,"count":4},{"startOffset":9755,"endOffset":9861,"count":1},{"startOffset":9874,"endOffset":9891,"count":2},{"startOffset":9904,"endOffset":10009,"count":3}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":10128,"endOffset":10718,"count":255},{"startOffset":10300,"endOffset":10319,"count":247},{"startOffset":10336,"endOffset":10412,"count":8},{"startOffset":10633,"endOffset":10656,"count":71}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":10499,"endOffset":10631,"count":463}],"isBlockCoverage":true}]},{"scriptId":"75","url":"internal/fs/streams.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":11147,"count":1}],"isBlockCoverage":false},{"functionName":"ReadStream","ranges":[{"startOffset":702,"endOffset":2789,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":2928,"endOffset":2963,"count":0}],"isBlockCoverage":false},{"functionName":"_openReadFs","ranges":[{"startOffset":3063,"endOffset":3544,"count":0}],"isBlockCoverage":false},{"functionName":"ReadStream._read","ranges":[{"startOffset":3575,"endOffset":4888,"count":0}],"isBlockCoverage":false},{"functionName":"ReadStream._destroy","ranges":[{"startOffset":4923,"endOffset":5210,"count":0}],"isBlockCoverage":false},{"functionName":"closeFsStream","ranges":[{"startOffset":5213,"endOffset":5369,"count":0}],"isBlockCoverage":false},{"functionName":"ReadStream.close","ranges":[{"startOffset":5400,"endOffset":5486,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":5547,"endOffset":5581,"count":0}],"isBlockCoverage":false},{"functionName":"WriteStream","ranges":[{"startOffset":5609,"endOffset":7859,"count":0}],"isBlockCoverage":false},{"functionName":"WriteStream._final","ranges":[{"startOffset":8002,"endOffset":8159,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":8192,"endOffset":8228,"count":0}],"isBlockCoverage":false},{"functionName":"_openWriteFs","ranges":[{"startOffset":8331,"endOffset":8764,"count":0}],"isBlockCoverage":false},{"functionName":"WriteStream._write","ranges":[{"startOffset":8798,"endOffset":9466,"count":0}],"isBlockCoverage":false},{"functionName":"WriteStream._writev","ranges":[{"startOffset":9502,"endOffset":10392,"count":0}],"isBlockCoverage":false},{"functionName":"WriteStream.close","ranges":[{"startOffset":10490,"endOffset":10872,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":11035,"endOffset":11069,"count":0}],"isBlockCoverage":false}]},{"scriptId":"76","url":"stream.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2187,"count":1}],"isBlockCoverage":false},{"functionName":"_uint8ArrayToBuffer","ranges":[{"startOffset":1978,"endOffset":2185,"count":0}],"isBlockCoverage":false}]},{"scriptId":"77","url":"internal/streams/pipeline.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":7631,"count":1}],"isBlockCoverage":false},{"functionName":"destroyer","ranges":[{"startOffset":543,"endOffset":1935,"count":0}],"isBlockCoverage":false},{"functionName":"popCallback","ranges":[{"startOffset":1937,"endOffset":2308,"count":0}],"isBlockCoverage":false},{"functionName":"isReadable","ranges":[{"startOffset":2310,"endOffset":2390,"count":0}],"isBlockCoverage":false},{"functionName":"isWritable","ranges":[{"startOffset":2392,"endOffset":2473,"count":0}],"isBlockCoverage":false},{"functionName":"isStream","ranges":[{"startOffset":2475,"endOffset":2546,"count":0}],"isBlockCoverage":false},{"functionName":"isIterable","ranges":[{"startOffset":2548,"endOffset":2871,"count":0}],"isBlockCoverage":false},{"functionName":"makeAsyncIterable","ranges":[{"startOffset":2873,"endOffset":3149,"count":0}],"isBlockCoverage":false},{"functionName":"fromReadable","ranges":[{"startOffset":3151,"endOffset":3315,"count":0}],"isBlockCoverage":false},{"functionName":"pump","ranges":[{"startOffset":3317,"endOffset":3794,"count":0}],"isBlockCoverage":false},{"functionName":"pipeline","ranges":[{"startOffset":3796,"endOffset":7602,"count":0}],"isBlockCoverage":false}]},{"scriptId":"78","url":"internal/streams/destroy.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":3954,"count":1}],"isBlockCoverage":false},{"functionName":"destroy","ranges":[{"startOffset":123,"endOffset":1394,"count":0}],"isBlockCoverage":false},{"functionName":"emitErrorCloseNT","ranges":[{"startOffset":1396,"endOffset":1483,"count":0}],"isBlockCoverage":false},{"functionName":"emitCloseNT","ranges":[{"startOffset":1485,"endOffset":1703,"count":0}],"isBlockCoverage":false},{"functionName":"emitErrorNT","ranges":[{"startOffset":1705,"endOffset":1992,"count":0}],"isBlockCoverage":false},{"functionName":"undestroy","ranges":[{"startOffset":1994,"endOffset":2557,"count":1}],"isBlockCoverage":true},{"functionName":"errorOrDestroy","ranges":[{"startOffset":2559,"endOffset":3458,"count":0}],"isBlockCoverage":false},{"functionName":"isRequest","ranges":[{"startOffset":3460,"endOffset":3565,"count":0}],"isBlockCoverage":false},{"functionName":"destroyer","ranges":[{"startOffset":3600,"endOffset":3876,"count":0}],"isBlockCoverage":false}]},{"scriptId":"79","url":"internal/streams/end-of-stream.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":5791,"count":1}],"isBlockCoverage":false},{"functionName":"isRequest","ranges":[{"startOffset":280,"endOffset":375,"count":0}],"isBlockCoverage":false},{"functionName":"isReadable","ranges":[{"startOffset":377,"endOffset":535,"count":0}],"isBlockCoverage":false},{"functionName":"isWritable","ranges":[{"startOffset":537,"endOffset":695,"count":0}],"isBlockCoverage":false},{"functionName":"isWritableFinished","ranges":[{"startOffset":697,"endOffset":934,"count":0}],"isBlockCoverage":false},{"functionName":"nop","ranges":[{"startOffset":936,"endOffset":953,"count":0}],"isBlockCoverage":false},{"functionName":"isReadableEnded","ranges":[{"startOffset":955,"endOffset":1188,"count":0}],"isBlockCoverage":false},{"functionName":"eos","ranges":[{"startOffset":1190,"endOffset":5767,"count":0}],"isBlockCoverage":false}]},{"scriptId":"80","url":"internal/streams/legacy.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2081,"count":1}],"isBlockCoverage":false},{"functionName":"Stream","ranges":[{"startOffset":96,"endOffset":144,"count":2}],"isBlockCoverage":true},{"functionName":"Stream.pipe","ranges":[{"startOffset":258,"endOffset":2053,"count":0}],"isBlockCoverage":false}]},{"scriptId":"81","url":"internal/streams/readable.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":40444,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":1596,"endOffset":1621,"count":0}],"isBlockCoverage":false},{"functionName":"nop","ranges":[{"startOffset":2218,"endOffset":2235,"count":0}],"isBlockCoverage":false},{"functionName":"prependListener","ranges":[{"startOffset":2278,"endOffset":3085,"count":0}],"isBlockCoverage":false},{"functionName":"ReadableState","ranges":[{"startOffset":3087,"endOffset":6664,"count":1},{"startOffset":3486,"endOffset":3529,"count":0},{"startOffset":4062,"endOffset":4098,"count":0},{"startOffset":6476,"endOffset":6662,"count":0}],"isBlockCoverage":true},{"functionName":"Readable","ranges":[{"startOffset":6667,"endOffset":7237,"count":1},{"startOffset":6735,"endOffset":6764,"count":0},{"startOffset":7087,"endOffset":7113,"count":0},{"startOffset":7168,"endOffset":7200,"count":0}],"isBlockCoverage":true},{"functionName":"Readable._destroy","ranges":[{"startOffset":7374,"endOffset":7406,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.","ranges":[{"startOffset":7457,"endOffset":7495,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.push","ranges":[{"startOffset":7724,"endOffset":7810,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.unshift","ranges":[{"startOffset":7906,"endOffset":7991,"count":0}],"isBlockCoverage":false},{"functionName":"readableAddChunk","ranges":[{"startOffset":7994,"endOffset":10247,"count":0}],"isBlockCoverage":false},{"functionName":"addChunk","ranges":[{"startOffset":10249,"endOffset":10949,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.isPaused","ranges":[{"startOffset":10981,"endOffset":11093,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.setEncoding","ranges":[{"startOffset":11157,"endOffset":11801,"count":0}],"isBlockCoverage":false},{"functionName":"computeNewHighWaterMark","ranges":[{"startOffset":11862,"endOffset":12227,"count":0}],"isBlockCoverage":false},{"functionName":"howMuchToRead","ranges":[{"startOffset":12340,"endOffset":12734,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.read","ranges":[{"startOffset":12831,"endOffset":17061,"count":0}],"isBlockCoverage":false},{"functionName":"onEofChunk","ranges":[{"startOffset":17064,"endOffset":17884,"count":0}],"isBlockCoverage":false},{"functionName":"emitReadable","ranges":[{"startOffset":18085,"endOffset":18412,"count":0}],"isBlockCoverage":false},{"functionName":"emitReadable_","ranges":[{"startOffset":18414,"endOffset":19050,"count":0}],"isBlockCoverage":false},{"functionName":"maybeReadMore","ranges":[{"startOffset":19400,"endOffset":19556,"count":0}],"isBlockCoverage":false},{"functionName":"maybeReadMore_","ranges":[{"startOffset":19558,"endOffset":21350,"count":0}],"isBlockCoverage":false},{"functionName":"Readable._read","ranges":[{"startOffset":21621,"endOffset":21687,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.pipe","ranges":[{"startOffset":21716,"endOffset":26193,"count":0}],"isBlockCoverage":false},{"functionName":"pipeOnDrain","ranges":[{"startOffset":26196,"endOffset":26870,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.unpipe","ranges":[{"startOffset":26901,"endOffset":27570,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.on","ranges":[{"startOffset":27696,"endOffset":28603,"count":211},{"startOffset":27828,"endOffset":28160,"count":0},{"startOffset":28189,"endOffset":28586,"count":0}],"isBlockCoverage":true},{"functionName":"Readable.removeListener","ranges":[{"startOffset":28698,"endOffset":29212,"count":209},{"startOffset":28809,"endOffset":29195,"count":0}],"isBlockCoverage":true},{"functionName":"Readable.removeAllListeners","ranges":[{"startOffset":29315,"endOffset":29853,"count":0}],"isBlockCoverage":false},{"functionName":"updateReadableListening","ranges":[{"startOffset":29856,"endOffset":30366,"count":0}],"isBlockCoverage":false},{"functionName":"nReadingNextTick","ranges":[{"startOffset":30368,"endOffset":30456,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.resume","ranges":[{"startOffset":30610,"endOffset":30935,"count":0}],"isBlockCoverage":false},{"functionName":"resume","ranges":[{"startOffset":30938,"endOffset":31088,"count":0}],"isBlockCoverage":false},{"functionName":"resume_","ranges":[{"startOffset":31090,"endOffset":31341,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.pause","ranges":[{"startOffset":31370,"endOffset":31637,"count":0}],"isBlockCoverage":false},{"functionName":"flow","ranges":[{"startOffset":31640,"endOffset":31787,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.wrap","ranges":[{"startOffset":31971,"endOffset":33786,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.","ranges":[{"startOffset":33831,"endOffset":34212,"count":0}],"isBlockCoverage":false},{"functionName":"createAsyncIterator","ranges":[{"startOffset":34215,"endOffset":35508,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":35706,"endOffset":36095,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":36101,"endOffset":36231,"count":1}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":36297,"endOffset":36363,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":36422,"endOffset":36504,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":36564,"endOffset":36624,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":36635,"endOffset":36744,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":36798,"endOffset":36852,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":36910,"endOffset":36998,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":37054,"endOffset":37139,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":37188,"endOffset":37322,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":37328,"endOffset":37617,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":37670,"endOffset":37758,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":37877,"endOffset":37922,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":37980,"endOffset":38031,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":38037,"endOffset":38086,"count":0}],"isBlockCoverage":false},{"functionName":"fromList","ranges":[{"startOffset":38390,"endOffset":38952,"count":0}],"isBlockCoverage":false},{"functionName":"endReadable","ranges":[{"startOffset":38954,"endOffset":39175,"count":0}],"isBlockCoverage":false},{"functionName":"endReadableNT","ranges":[{"startOffset":39177,"endOffset":40109,"count":0}],"isBlockCoverage":false},{"functionName":"endWritableNT","ranges":[{"startOffset":40111,"endOffset":40278,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.from","ranges":[{"startOffset":40296,"endOffset":40442,"count":0}],"isBlockCoverage":false}]},{"scriptId":"82","url":"internal/streams/buffer_list.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":3798,"count":1}],"isBlockCoverage":false},{"functionName":"BufferList","ranges":[{"startOffset":204,"endOffset":288,"count":1}],"isBlockCoverage":true},{"functionName":"push","ranges":[{"startOffset":292,"endOffset":479,"count":0}],"isBlockCoverage":false},{"functionName":"unshift","ranges":[{"startOffset":483,"endOffset":641,"count":0}],"isBlockCoverage":false},{"functionName":"shift","ranges":[{"startOffset":645,"endOffset":872,"count":0}],"isBlockCoverage":false},{"functionName":"clear","ranges":[{"startOffset":876,"endOffset":944,"count":0}],"isBlockCoverage":false},{"functionName":"join","ranges":[{"startOffset":948,"endOffset":1119,"count":0}],"isBlockCoverage":false},{"functionName":"concat","ranges":[{"startOffset":1123,"endOffset":1386,"count":0}],"isBlockCoverage":false},{"functionName":"consume","ranges":[{"startOffset":1470,"endOffset":1924,"count":0}],"isBlockCoverage":false},{"functionName":"first","ranges":[{"startOffset":1928,"endOffset":1968,"count":0}],"isBlockCoverage":false},{"functionName":"module.exports","ranges":[{"startOffset":1972,"endOffset":2068,"count":0}],"isBlockCoverage":false},{"functionName":"_getString","ranges":[{"startOffset":2143,"endOffset":2738,"count":0}],"isBlockCoverage":false},{"functionName":"_getBuffer","ranges":[{"startOffset":2808,"endOffset":3518,"count":0}],"isBlockCoverage":false},{"functionName":"module.exports","ranges":[{"startOffset":3599,"endOffset":3794,"count":0}],"isBlockCoverage":false}]},{"scriptId":"83","url":"internal/streams/state.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":881,"count":1}],"isBlockCoverage":false},{"functionName":"highWaterMarkFrom","ranges":[{"startOffset":142,"endOffset":309,"count":2},{"startOffset":240,"endOffset":263,"count":0},{"startOffset":300,"endOffset":306,"count":0}],"isBlockCoverage":true},{"functionName":"getDefaultHighWaterMark","ranges":[{"startOffset":311,"endOffset":397,"count":2},{"startOffset":378,"endOffset":382,"count":0}],"isBlockCoverage":true},{"functionName":"getHighWaterMark","ranges":[{"startOffset":399,"endOffset":811,"count":2},{"startOffset":546,"endOffset":737,"count":0}],"isBlockCoverage":true}]},{"scriptId":"84","url":"internal/streams/writable.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":22800,"count":1}],"isBlockCoverage":false},{"functionName":"nop","ranges":[{"startOffset":2223,"endOffset":2240,"count":0}],"isBlockCoverage":false},{"functionName":"WritableState","ranges":[{"startOffset":2242,"endOffset":6406,"count":1},{"startOffset":2637,"endOffset":2680,"count":0},{"startOffset":3231,"endOffset":3267,"count":0}],"isBlockCoverage":true},{"functionName":"resetBuffer","ranges":[{"startOffset":6408,"endOffset":6540,"count":1}],"isBlockCoverage":true},{"functionName":"getBuffer","ranges":[{"startOffset":6578,"endOffset":6652,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":6729,"endOffset":6794,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":7121,"endOffset":7335,"count":0}],"isBlockCoverage":false},{"functionName":"realHasInstance","ranges":[{"startOffset":7371,"endOffset":7428,"count":0}],"isBlockCoverage":false},{"functionName":"Writable","ranges":[{"startOffset":7433,"endOffset":8605,"count":1},{"startOffset":8074,"endOffset":8114,"count":0},{"startOffset":8120,"endOffset":8149,"count":0},{"startOffset":8288,"endOffset":8316,"count":0},{"startOffset":8370,"endOffset":8400,"count":0},{"startOffset":8455,"endOffset":8487,"count":0},{"startOffset":8540,"endOffset":8568,"count":0}],"isBlockCoverage":true},{"functionName":"Writable.pipe","ranges":[{"startOffset":8701,"endOffset":8769,"count":0}],"isBlockCoverage":false},{"functionName":"Writable.write","ranges":[{"startOffset":8799,"endOffset":10008,"count":209},{"startOffset":8969,"endOffset":9089,"count":0},{"startOffset":9113,"endOffset":9158,"count":0},{"startOffset":9267,"endOffset":9351,"count":0},{"startOffset":9357,"endOffset":9660,"count":0},{"startOffset":9697,"endOffset":9746,"count":0},{"startOffset":9773,"endOffset":9823,"count":0},{"startOffset":9836,"endOffset":9927,"count":0}],"isBlockCoverage":true},{"functionName":"Writable.cork","ranges":[{"startOffset":10037,"endOffset":10083,"count":0}],"isBlockCoverage":false},{"functionName":"Writable.uncork","ranges":[{"startOffset":10114,"endOffset":10269,"count":0}],"isBlockCoverage":false},{"functionName":"setDefaultEncoding","ranges":[{"startOffset":10312,"endOffset":10623,"count":0}],"isBlockCoverage":false},{"functionName":"writeOrBuffer","ranges":[{"startOffset":10813,"endOffset":11804,"count":209},{"startOffset":10911,"endOffset":10914,"count":0},{"startOffset":11133,"endOffset":11156,"count":0},{"startOffset":11212,"endOffset":11444,"count":0}],"isBlockCoverage":true},{"functionName":"doWrite","ranges":[{"startOffset":11806,"endOffset":12184,"count":0}],"isBlockCoverage":false},{"functionName":"onwriteError","ranges":[{"startOffset":12186,"endOffset":12606,"count":0}],"isBlockCoverage":false},{"functionName":"onwrite","ranges":[{"startOffset":12608,"endOffset":14202,"count":209},{"startOffset":12766,"endOffset":12840,"count":0},{"startOffset":12958,"endOffset":13469,"count":0},{"startOffset":13530,"endOffset":13571,"count":0},{"startOffset":13886,"endOffset":13933,"count":197},{"startOffset":13935,"endOffset":13986,"count":197},{"startOffset":13986,"endOffset":14137,"count":12},{"startOffset":14143,"endOffset":14196,"count":0}],"isBlockCoverage":true},{"functionName":"afterWriteTick","ranges":[{"startOffset":14204,"endOffset":14343,"count":12}],"isBlockCoverage":true},{"functionName":"afterWrite","ranges":[{"startOffset":14345,"endOffset":14755,"count":12},{"startOffset":14511,"endOffset":14571,"count":0},{"startOffset":14595,"endOffset":14633,"count":209},{"startOffset":14658,"endOffset":14722,"count":0}],"isBlockCoverage":true},{"functionName":"errorBuffer","ranges":[{"startOffset":14827,"endOffset":15148,"count":0}],"isBlockCoverage":false},{"functionName":"clearBuffer","ranges":[{"startOffset":15214,"endOffset":16647,"count":0}],"isBlockCoverage":false},{"functionName":"Writable._write","ranges":[{"startOffset":16677,"endOffset":16846,"count":0}],"isBlockCoverage":false},{"functionName":"Writable.end","ranges":[{"startOffset":16910,"endOffset":18094,"count":0}],"isBlockCoverage":false},{"functionName":"needFinish","ranges":[{"startOffset":18097,"endOffset":18310,"count":12},{"startOffset":18149,"endOffset":18180,"count":0},{"startOffset":18181,"endOffset":18208,"count":0},{"startOffset":18209,"endOffset":18249,"count":0},{"startOffset":18250,"endOffset":18278,"count":0},{"startOffset":18279,"endOffset":18306,"count":0}],"isBlockCoverage":true},{"functionName":"callFinal","ranges":[{"startOffset":18312,"endOffset":18572,"count":0}],"isBlockCoverage":false},{"functionName":"prefinish","ranges":[{"startOffset":18574,"endOffset":18922,"count":0}],"isBlockCoverage":false},{"functionName":"finishMaybe","ranges":[{"startOffset":18924,"endOffset":19251,"count":12},{"startOffset":19014,"endOffset":19234,"count":0}],"isBlockCoverage":true},{"functionName":"finish","ranges":[{"startOffset":19253,"endOffset":19871,"count":0}],"isBlockCoverage":false},{"functionName":"onFinished","ranges":[{"startOffset":19937,"endOffset":20401,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":20468,"endOffset":20555,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":20561,"endOffset":20743,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":20768,"endOffset":21160,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":21166,"endOffset":21300,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":21333,"endOffset":21419,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":21454,"endOffset":21542,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":21573,"endOffset":21655,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":21685,"endOffset":21769,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":21803,"endOffset":21961,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":21999,"endOffset":22083,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":22114,"endOffset":22194,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":22225,"endOffset":22302,"count":0}],"isBlockCoverage":false},{"functionName":"Writable.destroy","ranges":[{"startOffset":22378,"endOffset":22589,"count":0}],"isBlockCoverage":false},{"functionName":"Writable._destroy","ranges":[{"startOffset":22677,"endOffset":22709,"count":0}],"isBlockCoverage":false},{"functionName":"Writable.","ranges":[{"startOffset":22760,"endOffset":22798,"count":0}],"isBlockCoverage":false}]},{"scriptId":"85","url":"internal/streams/duplex.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":3759,"count":1}],"isBlockCoverage":false},{"functionName":"Duplex","ranges":[{"startOffset":1936,"endOffset":2360,"count":1},{"startOffset":2000,"endOffset":2027,"count":0},{"startOffset":2248,"endOffset":2270,"count":0},{"startOffset":2313,"endOffset":2354,"count":0}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":3271,"endOffset":3483,"count":12},{"startOffset":3369,"endOffset":3400,"count":0},{"startOffset":3444,"endOffset":3476,"count":0}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":3489,"endOffset":3750,"count":0}],"isBlockCoverage":false}]},{"scriptId":"86","url":"internal/streams/transform.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":8217,"count":1}],"isBlockCoverage":false},{"functionName":"afterTransform","ranges":[{"startOffset":4032,"endOffset":4550,"count":0}],"isBlockCoverage":false},{"functionName":"Transform","ranges":[{"startOffset":4553,"endOffset":5382,"count":0}],"isBlockCoverage":false},{"functionName":"prefinish","ranges":[{"startOffset":5384,"endOffset":5596,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":5691,"endOffset":5741,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":5838,"endOffset":5890,"count":0}],"isBlockCoverage":false},{"functionName":"Transform.push","ranges":[{"startOffset":5988,"endOffset":6124,"count":0}],"isBlockCoverage":false},{"functionName":"Transform._transform","ranges":[{"startOffset":6607,"endOffset":6696,"count":0}],"isBlockCoverage":false},{"functionName":"Transform._write","ranges":[{"startOffset":6728,"endOffset":7067,"count":0}],"isBlockCoverage":false},{"functionName":"Transform._read","ranges":[{"startOffset":7239,"endOffset":7613,"count":0}],"isBlockCoverage":false},{"functionName":"Transform._destroy","ranges":[{"startOffset":7648,"endOffset":7745,"count":0}],"isBlockCoverage":false},{"functionName":"done","ranges":[{"startOffset":7749,"endOffset":8216,"count":0}],"isBlockCoverage":false}]},{"scriptId":"87","url":"internal/streams/passthrough.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1762,"count":1}],"isBlockCoverage":false},{"functionName":"PassThrough","ranges":[{"startOffset":1529,"endOffset":1671,"count":0}],"isBlockCoverage":false},{"functionName":"PassThrough._transform","ranges":[{"startOffset":1708,"endOffset":1760,"count":0}],"isBlockCoverage":false}]},{"scriptId":"88","url":"file:///home/runner/work/jslint/jslint/jslint.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":167419,"count":1}],"isBlockCoverage":true},{"functionName":"assert_or_throw","ranges":[{"startOffset":7101,"endOffset":7428,"count":8634},{"startOffset":7223,"endOffset":7426,"count":1}],"isBlockCoverage":true},{"functionName":"empty","ranges":[{"startOffset":7430,"endOffset":7686,"count":3597}],"isBlockCoverage":true},{"functionName":"populate","ranges":[{"startOffset":7688,"endOffset":7915,"count":836}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":7839,"endOffset":7892,"count":19497}],"isBlockCoverage":true},{"functionName":"tag_regexp","ranges":[{"startOffset":16902,"endOffset":16992,"count":12}],"isBlockCoverage":true},{"functionName":"is_letter","ranges":[{"startOffset":18796,"endOffset":18943,"count":167},{"startOffset":18861,"endOffset":18883,"count":77},{"startOffset":18893,"endOffset":18934,"count":93},{"startOffset":18911,"endOffset":18933,"count":3}],"isBlockCoverage":true},{"functionName":"artifact","ranges":[{"startOffset":20832,"endOffset":21130,"count":826},{"startOffset":20943,"endOffset":20982,"count":20},{"startOffset":21033,"endOffset":21063,"count":819},{"startOffset":21073,"endOffset":21098,"count":87},{"startOffset":21107,"endOffset":21121,"count":739}],"isBlockCoverage":true},{"functionName":"warn_at","ranges":[{"startOffset":21132,"endOffset":22104,"count":879},{"startOffset":21430,"endOffset":21460,"count":862},{"startOffset":21486,"endOffset":21516,"count":315},{"startOffset":21542,"endOffset":21572,"count":27},{"startOffset":21598,"endOffset":21628,"count":3},{"startOffset":21998,"endOffset":22054,"count":4}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":21685,"endOffset":21909,"count":1115}],"isBlockCoverage":true},{"functionName":"stop_at","ranges":[{"startOffset":22106,"endOffset":22266,"count":17}],"isBlockCoverage":true},{"functionName":"warn","ranges":[{"startOffset":22268,"endOffset":22895,"count":809},{"startOffset":22567,"endOffset":22606,"count":12},{"startOffset":22648,"endOffset":22893,"count":656},{"startOffset":22775,"endOffset":22797,"count":517}],"isBlockCoverage":true},{"functionName":"stop","ranges":[{"startOffset":22897,"endOffset":23284,"count":91},{"startOffset":23168,"endOffset":23207,"count":16}],"isBlockCoverage":true},{"functionName":"tokenize","ranges":[{"startOffset":23300,"endOffset":52835,"count":405},{"startOffset":23899,"endOffset":23907,"count":1},{"startOffset":23916,"endOffset":23939,"count":404},{"startOffset":24934,"endOffset":24983,"count":3},{"startOffset":52599,"endOffset":52618,"count":377},{"startOffset":52761,"endOffset":52833,"count":56570},{"startOffset":52797,"endOffset":52827,"count":379}],"isBlockCoverage":true},{"functionName":"next_line","ranges":[{"startOffset":24989,"endOffset":26189,"count":16909},{"startOffset":25275,"endOffset":25300,"count":16906},{"startOffset":25313,"endOffset":25326,"count":1},{"startOffset":25339,"endOffset":25347,"count":1},{"startOffset":25360,"endOffset":25375,"count":1},{"startOffset":25386,"endOffset":25440,"count":1},{"startOffset":25577,"endOffset":25582,"count":4363},{"startOffset":25623,"endOffset":26155,"count":16522},{"startOffset":25695,"endOffset":25903,"count":2},{"startOffset":25732,"endOffset":25825,"count":1},{"startOffset":25934,"endOffset":25966,"count":16520},{"startOffset":25968,"endOffset":26145,"count":1}],"isBlockCoverage":true},{"functionName":"snip","ranges":[{"startOffset":26643,"endOffset":26751,"count":5112}],"isBlockCoverage":true},{"functionName":"next_char","ranges":[{"startOffset":26757,"endOffset":27580,"count":36155},{"startOffset":27007,"endOffset":27024,"count":773},{"startOffset":27026,"endOffset":27315,"count":3},{"startOffset":27126,"endOffset":27140,"count":2},{"startOffset":27161,"endOffset":27177,"count":1},{"startOffset":27315,"endOffset":27341,"count":36152},{"startOffset":27341,"endOffset":27464,"count":35995},{"startOffset":27464,"endOffset":27532,"count":157},{"startOffset":27532,"endOffset":27579,"count":36152}],"isBlockCoverage":true},{"functionName":"back_char","ranges":[{"startOffset":27586,"endOffset":27871,"count":1189}],"isBlockCoverage":true},{"functionName":"some_digits","ranges":[{"startOffset":27877,"endOffset":28285,"count":53},{"startOffset":28017,"endOffset":28032,"count":17},{"startOffset":28034,"endOffset":28133,"count":1}],"isBlockCoverage":true},{"functionName":"escape","ranges":[{"startOffset":28291,"endOffset":29532,"count":403},{"startOffset":28380,"endOffset":28423,"count":226},{"startOffset":28423,"endOffset":28449,"count":177},{"startOffset":28449,"endOffset":28540,"count":1},{"startOffset":28540,"endOffset":28567,"count":176},{"startOffset":28567,"endOffset":29341,"count":36},{"startOffset":28609,"endOffset":29132,"count":4},{"startOffset":28642,"endOffset":28761,"count":2},{"startOffset":28808,"endOffset":28923,"count":1},{"startOffset":28958,"endOffset":29082,"count":1},{"startOffset":29082,"endOffset":29132,"count":3},{"startOffset":29132,"endOffset":29206,"count":32},{"startOffset":29206,"endOffset":29311,"count":1},{"startOffset":29311,"endOffset":29341,"count":32},{"startOffset":29341,"endOffset":29387,"count":140},{"startOffset":29389,"endOffset":29432,"count":139},{"startOffset":29432,"endOffset":29531,"count":1}],"isBlockCoverage":true},{"functionName":"make","ranges":[{"startOffset":29538,"endOffset":31489,"count":57310},{"startOffset":29951,"endOffset":29964,"count":55648},{"startOffset":29966,"endOffset":30013,"count":51329},{"startOffset":30098,"endOffset":30146,"count":6743},{"startOffset":30481,"endOffset":30506,"count":45169},{"startOffset":30519,"endOffset":30577,"count":28817},{"startOffset":30542,"endOffset":30562,"count":28785},{"startOffset":30563,"endOffset":30576,"count":28769},{"startOffset":30590,"endOffset":30652,"count":53},{"startOffset":30622,"endOffset":30651,"count":52},{"startOffset":30663,"endOffset":30867,"count":1},{"startOffset":30900,"endOffset":30920,"count":3334},{"startOffset":30922,"endOffset":31011,"count":2},{"startOffset":31041,"endOffset":31064,"count":3334},{"startOffset":31066,"endOffset":31111,"count":3330},{"startOffset":31416,"endOffset":31457,"count":55648}],"isBlockCoverage":true},{"functionName":"parse_directive","ranges":[{"startOffset":31495,"endOffset":33365,"count":603},{"startOffset":31782,"endOffset":33239,"count":570},{"startOffset":31934,"endOffset":32661,"count":25},{"startOffset":32074,"endOffset":32104,"count":12},{"startOffset":32123,"endOffset":32526,"count":24},{"startOffset":32190,"endOffset":32410,"count":23},{"startOffset":32289,"endOffset":32388,"count":10},{"startOffset":32410,"endOffset":32508,"count":1},{"startOffset":32526,"endOffset":32647,"count":1},{"startOffset":32661,"endOffset":33169,"count":545},{"startOffset":32709,"endOffset":32861,"count":542},{"startOffset":32753,"endOffset":32810,"count":5},{"startOffset":32861,"endOffset":33169,"count":3},{"startOffset":32936,"endOffset":33064,"count":1},{"startOffset":33239,"endOffset":33258,"count":33},{"startOffset":33258,"endOffset":33359,"count":1}],"isBlockCoverage":true},{"functionName":"comment","ranges":[{"startOffset":33371,"endOffset":34253,"count":1662},{"startOffset":33622,"endOffset":33674,"count":51},{"startOffset":33701,"endOffset":33725,"count":1657},{"startOffset":33727,"endOffset":33811,"count":1},{"startOffset":33884,"endOffset":34219,"count":34},{"startOffset":33919,"endOffset":34038,"count":1},{"startOffset":34038,"endOffset":34219,"count":33},{"startOffset":34219,"endOffset":34252,"count":1661}],"isBlockCoverage":true},{"functionName":"regexp","ranges":[{"startOffset":34259,"endOffset":41414,"count":97},{"startOffset":40258,"endOffset":40338,"count":1},{"startOffset":41079,"endOffset":41094,"count":92},{"startOffset":41096,"endOffset":41169,"count":1},{"startOffset":41169,"endOffset":41292,"count":92},{"startOffset":41292,"endOffset":41302,"count":9},{"startOffset":41304,"endOffset":41385,"count":1},{"startOffset":41385,"endOffset":41413,"count":92}],"isBlockCoverage":true},{"functionName":"quantifier","ranges":[{"startOffset":34428,"endOffset":35053,"count":546},{"startOffset":34516,"endOffset":34531,"count":540},{"startOffset":34532,"endOffset":34547,"count":515},{"startOffset":34549,"endOffset":34593,"count":46},{"startOffset":34593,"endOffset":34965,"count":500},{"startOffset":34617,"endOffset":34920,"count":3},{"startOffset":34675,"endOffset":34770,"count":1},{"startOffset":34805,"endOffset":34874,"count":1},{"startOffset":34920,"endOffset":34965,"count":497},{"startOffset":34965,"endOffset":34996,"count":48},{"startOffset":34996,"endOffset":35043,"count":28}],"isBlockCoverage":true},{"functionName":"subklass","ranges":[{"startOffset":35063,"endOffset":35799,"count":103},{"startOffset":35161,"endOffset":35245,"count":23},{"startOffset":35245,"endOffset":35307,"count":80},{"startOffset":35307,"endOffset":35322,"count":79},{"startOffset":35339,"endOffset":35354,"count":79},{"startOffset":35371,"endOffset":35386,"count":48},{"startOffset":35403,"endOffset":35418,"count":47},{"startOffset":35435,"endOffset":35450,"count":47},{"startOffset":35465,"endOffset":35510,"count":33},{"startOffset":35510,"endOffset":35541,"count":47},{"startOffset":35541,"endOffset":35627,"count":1},{"startOffset":35627,"endOffset":35739,"count":46},{"startOffset":35650,"endOffset":35662,"count":3},{"startOffset":35664,"endOffset":35739,"count":1},{"startOffset":35739,"endOffset":35798,"count":47}],"isBlockCoverage":true},{"functionName":"ranges","ranges":[{"startOffset":35809,"endOffset":36317,"count":90},{"startOffset":35891,"endOffset":36307,"count":58},{"startOffset":35927,"endOffset":36260,"count":13},{"startOffset":36002,"endOffset":36242,"count":1},{"startOffset":36260,"endOffset":36307,"count":57}],"isBlockCoverage":true},{"functionName":"klass","ranges":[{"startOffset":36327,"endOffset":36945,"count":32},{"startOffset":36424,"endOffset":36471,"count":3}],"isBlockCoverage":true},{"functionName":"classy","ranges":[{"startOffset":36485,"endOffset":36903,"count":33},{"startOffset":36564,"endOffset":36578,"count":2},{"startOffset":36580,"endOffset":36889,"count":1}],"isBlockCoverage":true},{"functionName":"choice","ranges":[{"startOffset":36955,"endOffset":40058,"count":120},{"startOffset":39968,"endOffset":40048,"count":0}],"isBlockCoverage":true},{"functionName":"group","ranges":[{"startOffset":36988,"endOffset":37551,"count":23},{"startOffset":37121,"endOffset":37360,"count":7},{"startOffset":37196,"endOffset":37211,"count":6},{"startOffset":37213,"endOffset":37273,"count":2},{"startOffset":37273,"endOffset":37342,"count":5},{"startOffset":37360,"endOffset":37479,"count":16},{"startOffset":37384,"endOffset":37479,"count":1}],"isBlockCoverage":true},{"functionName":"factor","ranges":[{"startOffset":37565,"endOffset":39508,"count":665},{"startOffset":37698,"endOffset":37713,"count":664},{"startOffset":37734,"endOffset":37749,"count":571},{"startOffset":37770,"endOffset":37785,"count":571},{"startOffset":37804,"endOffset":37857,"count":117},{"startOffset":37857,"endOffset":37892,"count":548},{"startOffset":37892,"endOffset":37973,"count":23},{"startOffset":37973,"endOffset":38008,"count":525},{"startOffset":38008,"endOffset":38089,"count":32},{"startOffset":38089,"endOffset":38125,"count":493},{"startOffset":38125,"endOffset":38233,"count":100},{"startOffset":38233,"endOffset":38308,"count":393},{"startOffset":38308,"endOffset":38323,"count":392},{"startOffset":38344,"endOffset":38359,"count":392},{"startOffset":38380,"endOffset":38395,"count":392},{"startOffset":38416,"endOffset":38431,"count":392},{"startOffset":38450,"endOffset":38693,"count":1},{"startOffset":38693,"endOffset":39436,"count":392},{"startOffset":38717,"endOffset":38867,"count":13},{"startOffset":38754,"endOffset":38849,"count":1},{"startOffset":38867,"endOffset":39436,"count":379},{"startOffset":38891,"endOffset":39127,"count":1},{"startOffset":39127,"endOffset":39436,"count":378},{"startOffset":39151,"endOffset":39285,"count":24},{"startOffset":39201,"endOffset":39267,"count":8},{"startOffset":39285,"endOffset":39436,"count":354},{"startOffset":39309,"endOffset":39436,"count":24},{"startOffset":39352,"endOffset":39418,"count":3},{"startOffset":39436,"endOffset":39507,"count":393}],"isBlockCoverage":true},{"functionName":"sequence","ranges":[{"startOffset":39522,"endOffset":39834,"count":665},{"startOffset":39580,"endOffset":39676,"count":546},{"startOffset":39676,"endOffset":39706,"count":117},{"startOffset":39706,"endOffset":39820,"count":1}],"isBlockCoverage":true},{"functionName":"make_flag","ranges":[{"startOffset":40679,"endOffset":41028,"count":167},{"startOffset":40735,"endOffset":41018,"count":74},{"startOffset":40781,"endOffset":40865,"count":1}],"isBlockCoverage":true},{"functionName":"string","ranges":[{"startOffset":41420,"endOffset":42261,"count":3832}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":41554,"endOffset":42251,"count":33586},{"startOffset":41604,"endOffset":41773,"count":3829},{"startOffset":41773,"endOffset":41803,"count":29757},{"startOffset":41803,"endOffset":41900,"count":1},{"startOffset":41900,"endOffset":41932,"count":29756},{"startOffset":41932,"endOffset":41978,"count":280},{"startOffset":41978,"endOffset":42214,"count":29476},{"startOffset":42002,"endOffset":42164,"count":72},{"startOffset":42035,"endOffset":42118,"count":1},{"startOffset":42164,"endOffset":42214,"count":29404},{"startOffset":42214,"endOffset":42250,"count":29754}],"isBlockCoverage":true},{"functionName":"frack","ranges":[{"startOffset":42267,"endOffset":42571,"count":469},{"startOffset":42312,"endOffset":42359,"count":6},{"startOffset":42402,"endOffset":42565,"count":1}],"isBlockCoverage":true},{"functionName":"number","ranges":[{"startOffset":42577,"endOffset":43590,"count":1064},{"startOffset":42626,"endOffset":42966,"count":599},{"startOffset":42683,"endOffset":42723,"count":4},{"startOffset":42723,"endOffset":42956,"count":595},{"startOffset":42747,"endOffset":42800,"count":1},{"startOffset":42800,"endOffset":42956,"count":594},{"startOffset":42824,"endOffset":42879,"count":1},{"startOffset":42879,"endOffset":42956,"count":593},{"startOffset":42903,"endOffset":42956,"count":4},{"startOffset":42966,"endOffset":43029,"count":465},{"startOffset":43174,"endOffset":43188,"count":478},{"startOffset":43218,"endOffset":43232,"count":24},{"startOffset":43246,"endOffset":43277,"count":1063},{"startOffset":43262,"endOffset":43276,"count":279},{"startOffset":43288,"endOffset":43521,"count":1},{"startOffset":43521,"endOffset":43589,"count":1063}],"isBlockCoverage":true},{"functionName":"lex","ranges":[{"startOffset":43596,"endOffset":52545,"count":82733},{"startOffset":44421,"endOffset":44731,"count":16470},{"startOffset":44527,"endOffset":44721,"count":383},{"startOffset":44604,"endOffset":44652,"count":1},{"startOffset":44673,"endOffset":44688,"count":382},{"startOffset":44731,"endOffset":44930,"count":82350},{"startOffset":44930,"endOffset":45114,"count":1},{"startOffset":45114,"endOffset":45292,"count":82349},{"startOffset":45292,"endOffset":45329,"count":25602},{"startOffset":45329,"endOffset":45386,"count":56747},{"startOffset":45386,"endOffset":45448,"count":18519},{"startOffset":45448,"endOffset":45500,"count":38228},{"startOffset":45500,"endOffset":45547,"count":1064},{"startOffset":45547,"endOffset":45606,"count":37164},{"startOffset":45606,"endOffset":45653,"count":3830},{"startOffset":45653,"endOffset":45683,"count":33334},{"startOffset":45683,"endOffset":45848,"count":2},{"startOffset":45848,"endOffset":45951,"count":33332},{"startOffset":45951,"endOffset":48544,"count":63},{"startOffset":45980,"endOffset":46067,"count":1},{"startOffset":46067,"endOffset":48544,"count":62},{"startOffset":48544,"endOffset":48607,"count":33269},{"startOffset":48607,"endOffset":48880,"count":1611},{"startOffset":48743,"endOffset":48840,"count":1},{"startOffset":48880,"endOffset":48943,"count":31658},{"startOffset":48943,"endOffset":50198,"count":54},{"startOffset":49009,"endOffset":49088,"count":3},{"startOffset":49942,"endOffset":50039,"count":1},{"startOffset":50039,"endOffset":50198,"count":51},{"startOffset":50198,"endOffset":50255,"count":31604},{"startOffset":50255,"endOffset":52509,"count":107},{"startOffset":50874,"endOffset":51859,"count":13},{"startOffset":50908,"endOffset":51845,"count":11},{"startOffset":50957,"endOffset":51021,"count":1},{"startOffset":51021,"endOffset":51140,"count":10},{"startOffset":51165,"endOffset":51189,"count":9},{"startOffset":51214,"endOffset":51234,"count":8},{"startOffset":51259,"endOffset":51287,"count":7},{"startOffset":51312,"endOffset":51333,"count":6},{"startOffset":51358,"endOffset":51382,"count":5},{"startOffset":51407,"endOffset":51429,"count":4},{"startOffset":51454,"endOffset":51477,"count":3},{"startOffset":51500,"endOffset":51827,"count":8},{"startOffset":51859,"endOffset":52269,"count":94},{"startOffset":51970,"endOffset":52026,"count":83},{"startOffset":52026,"endOffset":52085,"count":11},{"startOffset":52085,"endOffset":52255,"count":5},{"startOffset":52269,"endOffset":52310,"count":10},{"startOffset":52310,"endOffset":52499,"count":2},{"startOffset":52509,"endOffset":52544,"count":31507}],"isBlockCoverage":true},{"functionName":"part","ranges":[{"startOffset":46468,"endOffset":48396,"count":485},{"startOffset":46643,"endOffset":46937,"count":312},{"startOffset":46815,"endOffset":46863,"count":1},{"startOffset":46888,"endOffset":46896,"count":311},{"startOffset":46937,"endOffset":47119,"count":173},{"startOffset":47119,"endOffset":47318,"count":76},{"startOffset":47318,"endOffset":47644,"count":97},{"startOffset":47644,"endOffset":48382,"count":39}],"isBlockCoverage":true},{"functionName":"expr","ranges":[{"startOffset":47788,"endOffset":48325,"count":156},{"startOffset":47891,"endOffset":48192,"count":1},{"startOffset":48192,"endOffset":48233,"count":153},{"startOffset":48233,"endOffset":48303,"count":117}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":49102,"endOffset":49810,"count":178},{"startOffset":49158,"endOffset":49527,"count":163},{"startOffset":49251,"endOffset":49306,"count":51},{"startOffset":49306,"endOffset":49398,"count":112},{"startOffset":49398,"endOffset":49509,"count":1},{"startOffset":49527,"endOffset":49659,"count":127},{"startOffset":49659,"endOffset":49765,"count":3},{"startOffset":49765,"endOffset":49809,"count":124}],"isBlockCoverage":true},{"functionName":"survey","ranges":[{"startOffset":53242,"endOffset":54574,"count":3901},{"startOffset":53424,"endOffset":53528,"count":10},{"startOffset":53528,"endOffset":53842,"count":3891},{"startOffset":53550,"endOffset":53728,"count":2},{"startOffset":53670,"endOffset":53712,"count":1},{"startOffset":53728,"endOffset":53842,"count":3889},{"startOffset":53756,"endOffset":53842,"count":1},{"startOffset":53842,"endOffset":53945,"count":3889},{"startOffset":53945,"endOffset":54168,"count":3083},{"startOffset":54168,"endOffset":54557,"count":806},{"startOffset":54210,"endOffset":54366,"count":538},{"startOffset":54249,"endOffset":54356,"count":1},{"startOffset":54366,"endOffset":54525,"count":268},{"startOffset":54406,"endOffset":54433,"count":267},{"startOffset":54435,"endOffset":54515,"count":1},{"startOffset":54557,"endOffset":54573,"count":3889}],"isBlockCoverage":true},{"functionName":"dispense","ranges":[{"startOffset":54576,"endOffset":54886,"count":57828},{"startOffset":54739,"endOffset":54849,"count":1660},{"startOffset":54764,"endOffset":54816,"count":1},{"startOffset":54849,"endOffset":54884,"count":56168}],"isBlockCoverage":true},{"functionName":"lookahead","ranges":[{"startOffset":54888,"endOffset":55073,"count":395}],"isBlockCoverage":true},{"functionName":"advance","ranges":[{"startOffset":55075,"endOffset":56103,"count":55794},{"startOffset":55217,"endOffset":55243,"count":18487},{"startOffset":55245,"endOffset":55277,"count":17901},{"startOffset":55277,"endOffset":55382,"count":37893},{"startOffset":55311,"endOffset":55345,"count":3923},{"startOffset":55347,"endOffset":55382,"count":1745},{"startOffset":55462,"endOffset":55485,"count":20808},{"startOffset":55487,"endOffset":55938,"count":21},{"startOffset":55567,"endOffset":55617,"count":18},{"startOffset":55654,"endOffset":55921,"count":3},{"startOffset":55938,"endOffset":56071,"count":55773},{"startOffset":56071,"endOffset":56101,"count":630}],"isBlockCoverage":true},{"functionName":"json_value","ranges":[{"startOffset":56136,"endOffset":59239,"count":43},{"startOffset":56209,"endOffset":57730,"count":15},{"startOffset":57730,"endOffset":57762,"count":28},{"startOffset":57762,"endOffset":58337,"count":10},{"startOffset":58337,"endOffset":58416,"count":18},{"startOffset":58425,"endOffset":58452,"count":18},{"startOffset":58459,"endOffset":58507,"count":1},{"startOffset":58507,"endOffset":58546,"count":17},{"startOffset":58546,"endOffset":58692,"count":9},{"startOffset":58600,"endOffset":58645,"count":1},{"startOffset":58692,"endOffset":58731,"count":8},{"startOffset":58731,"endOffset":58894,"count":4},{"startOffset":58772,"endOffset":58847,"count":1},{"startOffset":58894,"endOffset":58926,"count":4},{"startOffset":58926,"endOffset":59211,"count":2},{"startOffset":59091,"endOffset":59143,"count":1},{"startOffset":59211,"endOffset":59238,"count":2}],"isBlockCoverage":true},{"functionName":"json_object","ranges":[{"startOffset":56227,"endOffset":57720,"count":15},{"startOffset":56469,"endOffset":57651,"count":13},{"startOffset":57651,"endOffset":57719,"count":9}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":56488,"endOffset":57633,"count":15},{"startOffset":56618,"endOffset":56827,"count":5},{"startOffset":56967,"endOffset":57076,"count":1},{"startOffset":57076,"endOffset":57307,"count":9},{"startOffset":57115,"endOffset":57225,"count":1},{"startOffset":57225,"endOffset":57307,"count":8},{"startOffset":57307,"endOffset":57515,"count":10},{"startOffset":57515,"endOffset":57615,"count":2}],"isBlockCoverage":true},{"functionName":"json_array","ranges":[{"startOffset":57780,"endOffset":58327,"count":10},{"startOffset":57985,"endOffset":58254,"count":8},{"startOffset":58254,"endOffset":58326,"count":9}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":58004,"endOffset":58236,"count":10},{"startOffset":58118,"endOffset":58218,"count":2}],"isBlockCoverage":true},{"functionName":"enroll","ranges":[{"startOffset":59270,"endOffset":61487,"count":1576},{"startOffset":59599,"endOffset":59617,"count":22},{"startOffset":59619,"endOffset":59688,"count":1},{"startOffset":59688,"endOffset":61485,"count":1575},{"startOffset":59813,"endOffset":60058,"count":5},{"startOffset":60058,"endOffset":61479,"count":1570},{"startOffset":60296,"endOffset":61213,"count":36},{"startOffset":60335,"endOffset":60531,"count":4},{"startOffset":60390,"endOffset":60513,"count":3},{"startOffset":60531,"endOffset":61199,"count":32},{"startOffset":60667,"endOffset":60698,"count":1},{"startOffset":60797,"endOffset":60819,"count":2},{"startOffset":60842,"endOffset":61181,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":60092,"endOffset":60268,"count":1857},{"startOffset":60199,"endOffset":60254,"count":38}],"isBlockCoverage":true},{"functionName":"expression","ranges":[{"startOffset":61489,"endOffset":62827,"count":15679},{"startOffset":62155,"endOffset":62181,"count":12444},{"startOffset":62250,"endOffset":62281,"count":6940},{"startOffset":62283,"endOffset":62323,"count":6923},{"startOffset":62323,"endOffset":62486,"count":8756},{"startOffset":62351,"endOffset":62413,"count":8738},{"startOffset":62413,"endOffset":62486,"count":18},{"startOffset":62486,"endOffset":62826,"count":15645}],"isBlockCoverage":true},{"functionName":"right","ranges":[{"startOffset":62492,"endOffset":62804,"count":26298},{"startOffset":62617,"endOffset":62648,"count":26062},{"startOffset":62661,"endOffset":62684,"count":11731},{"startOffset":62695,"endOffset":62798,"count":10660}],"isBlockCoverage":true},{"functionName":"condition","ranges":[{"startOffset":62829,"endOffset":63319,"count":1226},{"startOffset":63152,"endOffset":63200,"count":1},{"startOffset":63200,"endOffset":63247,"count":1220},{"startOffset":63247,"endOffset":63295,"count":18},{"startOffset":63295,"endOffset":63318,"count":1220}],"isBlockCoverage":true},{"functionName":"is_weird","ranges":[{"startOffset":63321,"endOffset":63550,"count":10475},{"startOffset":63493,"endOffset":63541,"count":10471},{"startOffset":63514,"endOffset":63540,"count":180}],"isBlockCoverage":true},{"functionName":"are_similar","ranges":[{"startOffset":63552,"endOffset":66076,"count":3305},{"startOffset":63746,"endOffset":63993,"count":8},{"startOffset":63993,"endOffset":64172,"count":3297},{"startOffset":64172,"endOffset":64194,"count":34},{"startOffset":64196,"endOffset":64239,"count":32},{"startOffset":64239,"endOffset":64305,"count":3265},{"startOffset":64305,"endOffset":64340,"count":196},{"startOffset":64340,"endOffset":64416,"count":3069},{"startOffset":64363,"endOffset":64376,"count":9},{"startOffset":64378,"endOffset":64416,"count":8},{"startOffset":64416,"endOffset":64446,"count":3265},{"startOffset":64446,"endOffset":64481,"count":1316},{"startOffset":64481,"endOffset":64557,"count":1949},{"startOffset":64504,"endOffset":64517,"count":8},{"startOffset":64519,"endOffset":64557,"count":8},{"startOffset":64557,"endOffset":64596,"count":3265},{"startOffset":64596,"endOffset":64641,"count":196},{"startOffset":64641,"endOffset":64676,"count":3069},{"startOffset":64678,"endOffset":64707,"count":4},{"startOffset":64707,"endOffset":64736,"count":3065},{"startOffset":64736,"endOffset":64752,"count":1386},{"startOffset":64754,"endOffset":66026,"count":818},{"startOffset":64809,"endOffset":64959,"count":219},{"startOffset":64904,"endOffset":64934,"count":139},{"startOffset":64959,"endOffset":65014,"count":599},{"startOffset":65014,"endOffset":65085,"count":13},{"startOffset":65085,"endOffset":65120,"count":586},{"startOffset":65120,"endOffset":65353,"count":290},{"startOffset":65215,"endOffset":65263,"count":264},{"startOffset":65280,"endOffset":65328,"count":151},{"startOffset":65353,"endOffset":65389,"count":296},{"startOffset":65389,"endOffset":65668,"count":3},{"startOffset":65530,"endOffset":65578,"count":2},{"startOffset":65595,"endOffset":65643,"count":2},{"startOffset":65668,"endOffset":65752,"count":293},{"startOffset":66026,"endOffset":66075,"count":2247}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":63854,"endOffset":63975,"count":2}],"isBlockCoverage":true},{"functionName":"semicolon","ranges":[{"startOffset":66078,"endOffset":66418,"count":3911},{"startOffset":66163,"endOffset":66192,"count":3737},{"startOffset":66192,"endOffset":66392,"count":174}],"isBlockCoverage":true},{"functionName":"statement","ranges":[{"startOffset":66420,"endOffset":68155,"count":6050},{"startOffset":66774,"endOffset":66798,"count":5899},{"startOffset":66800,"endOffset":67509,"count":9},{"startOffset":66868,"endOffset":66924,"count":1},{"startOffset":67127,"endOffset":67419,"count":5},{"startOffset":67419,"endOffset":67509,"count":4},{"startOffset":67509,"endOffset":67651,"count":6045},{"startOffset":67651,"endOffset":67682,"count":3083},{"startOffset":67684,"endOffset":67806,"count":2895},{"startOffset":67806,"endOffset":68055,"count":3150},{"startOffset":67929,"endOffset":67956,"count":71},{"startOffset":67958,"endOffset":68028,"count":13},{"startOffset":68028,"endOffset":68055,"count":3126},{"startOffset":68055,"endOffset":68089,"count":5965},{"startOffset":68089,"endOffset":68127,"count":1},{"startOffset":68127,"endOffset":68154,"count":5965}],"isBlockCoverage":true},{"functionName":"statements","ranges":[{"startOffset":68157,"endOffset":68863,"count":2390}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":68319,"endOffset":68834,"count":8214},{"startOffset":68403,"endOffset":68430,"count":6201},{"startOffset":68443,"endOffset":68473,"count":6194},{"startOffset":68486,"endOffset":68513,"count":6187},{"startOffset":68526,"endOffset":68554,"count":6187},{"startOffset":68565,"endOffset":68828,"count":5901},{"startOffset":68672,"endOffset":68772,"count":1},{"startOffset":68772,"endOffset":68828,"count":5824}],"isBlockCoverage":true},{"functionName":"not_top_level","ranges":[{"startOffset":68865,"endOffset":69045,"count":584},{"startOffset":68986,"endOffset":69043,"count":28}],"isBlockCoverage":true},{"functionName":"top_level_only","ranges":[{"startOffset":69047,"endOffset":69262,"count":22},{"startOffset":69169,"endOffset":69260,"count":1}],"isBlockCoverage":true},{"functionName":"block","ranges":[{"startOffset":69264,"endOffset":70392,"count":2010},{"startOffset":69604,"endOffset":69633,"count":2000},{"startOffset":69633,"endOffset":69845,"count":2009},{"startOffset":69845,"endOffset":69866,"count":590},{"startOffset":69875,"endOffset":69911,"count":337},{"startOffset":69918,"endOffset":70013,"count":5},{"startOffset":70013,"endOffset":70097,"count":2009},{"startOffset":70097,"endOffset":70276,"count":54},{"startOffset":70150,"endOffset":70235,"count":49},{"startOffset":70276,"endOffset":70350,"count":1952},{"startOffset":70350,"endOffset":70391,"count":2006}],"isBlockCoverage":true},{"functionName":"mutation_check","ranges":[{"startOffset":70394,"endOffset":70825,"count":1281},{"startOffset":70620,"endOffset":70643,"count":616},{"startOffset":70652,"endOffset":70675,"count":53},{"startOffset":70684,"endOffset":70707,"count":1},{"startOffset":70714,"endOffset":70806,"count":1},{"startOffset":70806,"endOffset":70824,"count":1280}],"isBlockCoverage":true},{"functionName":"left_check","ranges":[{"startOffset":70827,"endOffset":71441,"count":6565},{"startOffset":71038,"endOffset":71219,"count":1321},{"startOffset":71092,"endOffset":71209,"count":1},{"startOffset":71228,"endOffset":71349,"count":1321},{"startOffset":71281,"endOffset":71339,"count":1314},{"startOffset":71296,"endOffset":71310,"count":237},{"startOffset":71311,"endOffset":71324,"count":236},{"startOffset":71325,"endOffset":71338,"count":82},{"startOffset":71356,"endOffset":71422,"count":8},{"startOffset":71422,"endOffset":71440,"count":6557}],"isBlockCoverage":true},{"functionName":"symbol","ranges":[{"startOffset":71512,"endOffset":71836,"count":129},{"startOffset":71679,"endOffset":71811,"count":113},{"startOffset":71767,"endOffset":71771,"count":67}],"isBlockCoverage":true},{"functionName":"assignment","ranges":[{"startOffset":71838,"endOffset":72860,"count":12}],"isBlockCoverage":true},{"functionName":"the_symbol.led","ranges":[{"startOffset":72225,"endOffset":72834,"count":1280},{"startOffset":72394,"endOffset":72422,"count":1159},{"startOffset":72424,"endOffset":72513,"count":580},{"startOffset":72513,"endOffset":72580,"count":697},{"startOffset":72580,"endOffset":72671,"count":1277},{"startOffset":72684,"endOffset":72709,"count":1275},{"startOffset":72720,"endOffset":72772,"count":2},{"startOffset":72772,"endOffset":72833,"count":1277}],"isBlockCoverage":true},{"functionName":"constant","ranges":[{"startOffset":72862,"endOffset":73344,"count":16},{"startOffset":73062,"endOffset":73069,"count":7},{"startOffset":73078,"endOffset":73254,"count":9}],"isBlockCoverage":true},{"functionName":"the_symbol.nud","ranges":[{"startOffset":73080,"endOffset":73254,"count":5588},{"startOffset":73166,"endOffset":73218,"count":510}],"isBlockCoverage":true},{"functionName":"infix","ranges":[{"startOffset":73346,"endOffset":73731,"count":30}],"isBlockCoverage":true},{"functionName":"the_symbol.led","ranges":[{"startOffset":73463,"endOffset":73705,"count":9318},{"startOffset":73579,"endOffset":73618,"count":6656},{"startOffset":73618,"endOffset":73704,"count":2662}],"isBlockCoverage":true},{"functionName":"infixr","ranges":[{"startOffset":73733,"endOffset":74087,"count":1}],"isBlockCoverage":true},{"functionName":"the_symbol.led","ranges":[{"startOffset":73865,"endOffset":74061,"count":1}],"isBlockCoverage":true},{"functionName":"post","ranges":[{"startOffset":74089,"endOffset":74383,"count":2}],"isBlockCoverage":true},{"functionName":"the_symbol.led","ranges":[{"startOffset":74207,"endOffset":74357,"count":2}],"isBlockCoverage":true},{"functionName":"pre","ranges":[{"startOffset":74385,"endOffset":74727,"count":2}],"isBlockCoverage":true},{"functionName":"the_symbol.nud","ranges":[{"startOffset":74496,"endOffset":74701,"count":2}],"isBlockCoverage":true},{"functionName":"prefix","ranges":[{"startOffset":74729,"endOffset":75099,"count":17}],"isBlockCoverage":true},{"functionName":"the_symbol.nud","ranges":[{"startOffset":74839,"endOffset":75073,"count":1319},{"startOffset":74958,"endOffset":74993,"count":1074},{"startOffset":74993,"endOffset":75072,"count":245}],"isBlockCoverage":true},{"functionName":"stmt","ranges":[{"startOffset":75101,"endOffset":75303,"count":22}],"isBlockCoverage":true},{"functionName":"the_symbol.fud","ranges":[{"startOffset":75203,"endOffset":75277,"count":2895}],"isBlockCoverage":true},{"functionName":"ternary","ranges":[{"startOffset":75305,"endOffset":75835,"count":1}],"isBlockCoverage":true},{"functionName":"the_symbol.led","ranges":[{"startOffset":75425,"endOffset":75809,"count":59},{"startOffset":75705,"endOffset":75777,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":76452,"endOffset":76518,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":76550,"endOffset":76813,"count":4},{"startOffset":76586,"endOffset":76793,"count":2},{"startOffset":76684,"endOffset":76793,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":76886,"endOffset":77157,"count":4},{"startOffset":76922,"endOffset":77137,"count":2},{"startOffset":77024,"endOffset":77137,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":77192,"endOffset":77258,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":77336,"endOffset":77433,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":77466,"endOffset":77555,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":77649,"endOffset":77749,"count":2},{"startOffset":77685,"endOffset":77729,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":78506,"endOffset":79761,"count":2878},{"startOffset":78607,"endOffset":78651,"count":2813},{"startOffset":78694,"endOffset":78712,"count":952},{"startOffset":78714,"endOffset":78769,"count":700},{"startOffset":78836,"endOffset":79332,"count":2333},{"startOffset":79405,"endOffset":79653,"count":1336},{"startOffset":79501,"endOffset":79557,"count":1},{"startOffset":79595,"endOffset":79647,"count":31},{"startOffset":79653,"endOffset":79737,"count":1542}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":78847,"endOffset":79322,"count":3905},{"startOffset":78932,"endOffset":79012,"count":2},{"startOffset":79082,"endOffset":79143,"count":2},{"startOffset":79236,"endOffset":79312,"count":1572}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":79780,"endOffset":80690,"count":3329},{"startOffset":79922,"endOffset":79972,"count":10},{"startOffset":79948,"endOffset":79971,"count":6},{"startOffset":79991,"endOffset":80217,"count":3319},{"startOffset":80036,"endOffset":80207,"count":83},{"startOffset":80094,"endOffset":80118,"count":81},{"startOffset":80135,"endOffset":80156,"count":80},{"startOffset":80173,"endOffset":80193,"count":80},{"startOffset":80226,"endOffset":80269,"count":3316},{"startOffset":80246,"endOffset":80268,"count":2},{"startOffset":80278,"endOffset":80385,"count":3314},{"startOffset":80330,"endOffset":80375,"count":12},{"startOffset":80353,"endOffset":80374,"count":8},{"startOffset":80392,"endOffset":80436,"count":3302},{"startOffset":80463,"endOffset":80528,"count":1},{"startOffset":80528,"endOffset":80689,"count":3328}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":80710,"endOffset":81644,"count":7},{"startOffset":80852,"endOffset":80902,"count":1},{"startOffset":80966,"endOffset":81137,"count":1},{"startOffset":81199,"endOffset":81221,"count":1},{"startOffset":81283,"endOffset":81328,"count":1},{"startOffset":81416,"endOffset":81482,"count":2},{"startOffset":81482,"endOffset":81643,"count":5}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":81663,"endOffset":82123,"count":417},{"startOffset":81791,"endOffset":81818,"count":415},{"startOffset":81820,"endOffset":81998,"count":4},{"startOffset":81904,"endOffset":81992,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":82143,"endOffset":82223,"count":1}],"isBlockCoverage":true},{"functionName":"do_tick","ranges":[{"startOffset":82227,"endOffset":82729,"count":58}],"isBlockCoverage":true},{"functionName":"part","ranges":[{"startOffset":82373,"endOffset":82678,"count":94},{"startOffset":82504,"endOffset":82668,"count":36}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":82747,"endOffset":82913,"count":24}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":83042,"endOffset":83703,"count":156},{"startOffset":83147,"endOffset":83661,"count":83}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":83158,"endOffset":83651,"count":596},{"startOffset":83276,"endOffset":83356,"count":1},{"startOffset":83421,"endOffset":83477,"count":1},{"startOffset":83565,"endOffset":83641,"count":513}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":83719,"endOffset":83798,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":83814,"endOffset":83906,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":83923,"endOffset":84189,"count":30},{"startOffset":84030,"endOffset":84135,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":84225,"endOffset":84385,"count":2}],"isBlockCoverage":true},{"functionName":"parameter_list","ranges":[{"startOffset":84389,"endOffset":90104,"count":603},{"startOffset":84515,"endOffset":84543,"count":338},{"startOffset":84545,"endOffset":90020,"count":338},{"startOffset":90020,"endOffset":90103,"count":597}],"isBlockCoverage":true},{"functionName":"parameter","ranges":[{"startOffset":84556,"endOffset":90010,"count":485},{"startOffset":84675,"endOffset":86955,"count":27},{"startOffset":84772,"endOffset":85000,"count":1},{"startOffset":86806,"endOffset":86941,"count":8},{"startOffset":86955,"endOffset":90000,"count":458},{"startOffset":86988,"endOffset":88350,"count":10},{"startOffset":87034,"endOffset":87262,"count":1},{"startOffset":88201,"endOffset":88336,"count":3},{"startOffset":88350,"endOffset":90000,"count":448},{"startOffset":88403,"endOffset":88843,"count":4},{"startOffset":88569,"endOffset":88825,"count":1},{"startOffset":88888,"endOffset":88965,"count":2},{"startOffset":88965,"endOffset":89135,"count":446},{"startOffset":89135,"endOffset":89197,"count":3},{"startOffset":89197,"endOffset":89986,"count":443},{"startOffset":89252,"endOffset":89413,"count":20},{"startOffset":89413,"endOffset":89769,"count":423},{"startOffset":89473,"endOffset":89747,"count":1},{"startOffset":89817,"endOffset":89968,"count":136}],"isBlockCoverage":true},{"functionName":"subparameter","ranges":[{"startOffset":85155,"endOffset":86657,"count":63},{"startOffset":85274,"endOffset":85359,"count":1},{"startOffset":85359,"endOffset":85485,"count":62},{"startOffset":85519,"endOffset":85719,"count":1},{"startOffset":85719,"endOffset":85847,"count":62},{"startOffset":85847,"endOffset":86177,"count":2},{"startOffset":86062,"endOffset":86155,"count":1},{"startOffset":86177,"endOffset":86225,"count":61},{"startOffset":86225,"endOffset":86389,"count":16},{"startOffset":86389,"endOffset":86485,"count":61},{"startOffset":86485,"endOffset":86639,"count":36}],"isBlockCoverage":true},{"functionName":"subparameter","ranges":[{"startOffset":87418,"endOffset":88089,"count":19},{"startOffset":87539,"endOffset":87624,"count":2},{"startOffset":87624,"endOffset":87751,"count":17},{"startOffset":87751,"endOffset":87915,"count":1},{"startOffset":87915,"endOffset":87963,"count":17},{"startOffset":87963,"endOffset":88071,"count":9}],"isBlockCoverage":true},{"functionName":"do_function","ranges":[{"startOffset":90106,"endOffset":93032,"count":600},{"startOffset":90193,"endOffset":91038,"count":590},{"startOffset":90352,"endOffset":90715,"count":252},{"startOffset":90394,"endOffset":90505,"count":4},{"startOffset":90505,"endOffset":90715,"count":248},{"startOffset":90715,"endOffset":91032,"count":338},{"startOffset":90840,"endOffset":90959,"count":59},{"startOffset":90959,"endOffset":91022,"count":279},{"startOffset":91038,"endOffset":91085,"count":10},{"startOffset":91085,"endOffset":91408,"count":596},{"startOffset":91408,"endOffset":91509,"count":1},{"startOffset":91509,"endOffset":91970,"count":596},{"startOffset":91970,"endOffset":91997,"count":348},{"startOffset":91999,"endOffset":92122,"count":59},{"startOffset":92122,"endOffset":92693,"count":596},{"startOffset":92693,"endOffset":92726,"count":241},{"startOffset":92733,"endOffset":92789,"count":2},{"startOffset":92789,"endOffset":92862,"count":586},{"startOffset":92871,"endOffset":92895,"count":586},{"startOffset":92902,"endOffset":92939,"count":1},{"startOffset":92939,"endOffset":93031,"count":586}],"isBlockCoverage":true},{"functionName":"enroll_parameter","ranges":[{"startOffset":92360,"endOffset":92553,"count":544},{"startOffset":92423,"endOffset":92480,"count":515},{"startOffset":92480,"endOffset":92547,"count":29}],"isBlockCoverage":true},{"functionName":"do_async","ranges":[{"startOffset":93034,"endOffset":93424,"count":16},{"startOffset":93299,"endOffset":93397,"count":1},{"startOffset":93397,"endOffset":93423,"count":15}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":93502,"endOffset":93784,"count":28},{"startOffset":93589,"endOffset":93682,"count":2},{"startOffset":93682,"endOffset":93783,"count":26}],"isBlockCoverage":true},{"functionName":"fart","ranges":[{"startOffset":93788,"endOffset":94874,"count":8},{"startOffset":94016,"endOffset":94106,"count":1},{"startOffset":94106,"endOffset":94657,"count":6},{"startOffset":94657,"endOffset":94762,"count":1},{"startOffset":94762,"endOffset":94820,"count":5},{"startOffset":94820,"endOffset":94873,"count":6}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":94559,"endOffset":94623,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":94888,"endOffset":96122,"count":395},{"startOffset":95144,"endOffset":95170,"count":388},{"startOffset":95179,"endOffset":95241,"count":388},{"startOffset":95205,"endOffset":95240,"count":265},{"startOffset":95248,"endOffset":95346,"count":7},{"startOffset":95346,"endOffset":95459,"count":388},{"startOffset":95459,"endOffset":95527,"count":2},{"startOffset":95527,"endOffset":95619,"count":387},{"startOffset":95619,"endOffset":96098,"count":3},{"startOffset":95665,"endOffset":95977,"count":2},{"startOffset":95729,"endOffset":95977,"count":1},{"startOffset":95977,"endOffset":96098,"count":1},{"startOffset":96098,"endOffset":96121,"count":383}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":96159,"endOffset":99603,"count":89},{"startOffset":96290,"endOffset":99561,"count":83},{"startOffset":99561,"endOffset":99602,"count":85}],"isBlockCoverage":true},{"functionName":"member","ranges":[{"startOffset":96336,"endOffset":99551,"count":491},{"startOffset":96555,"endOffset":96565,"count":482},{"startOffset":96591,"endOffset":96746,"count":1},{"startOffset":96799,"endOffset":96819,"count":483},{"startOffset":96837,"endOffset":96861,"count":11},{"startOffset":96876,"endOffset":97446,"count":11},{"startOffset":96914,"endOffset":97011,"count":7},{"startOffset":97234,"endOffset":97254,"count":10},{"startOffset":97256,"endOffset":97363,"count":1},{"startOffset":97446,"endOffset":97678,"count":480},{"startOffset":97540,"endOffset":97631,"count":1},{"startOffset":97631,"endOffset":97678,"count":479},{"startOffset":97678,"endOffset":97712,"count":490},{"startOffset":97712,"endOffset":99247,"count":482},{"startOffset":97756,"endOffset":97780,"count":461},{"startOffset":97782,"endOffset":97996,"count":86},{"startOffset":97835,"endOffset":97922,"count":1},{"startOffset":97922,"endOffset":97996,"count":85},{"startOffset":97996,"endOffset":99039,"count":396},{"startOffset":98029,"endOffset":98552,"count":10},{"startOffset":98379,"endOffset":98386,"count":9},{"startOffset":98439,"endOffset":98443,"count":1},{"startOffset":98552,"endOffset":99039,"count":386},{"startOffset":98611,"endOffset":98701,"count":1},{"startOffset":98701,"endOffset":98872,"count":385},{"startOffset":98872,"endOffset":98898,"count":2},{"startOffset":98900,"endOffset":99021,"count":2},{"startOffset":99039,"endOffset":99123,"count":479},{"startOffset":99123,"endOffset":99183,"count":9},{"startOffset":99183,"endOffset":99247,"count":479},{"startOffset":99247,"endOffset":99423,"count":8},{"startOffset":99423,"endOffset":99463,"count":487},{"startOffset":99463,"endOffset":99541,"count":408}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":99617,"endOffset":99683,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":99696,"endOffset":99795,"count":10}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":99837,"endOffset":100753,"count":30},{"startOffset":99938,"endOffset":99963,"count":17},{"startOffset":99973,"endOffset":99999,"count":25},{"startOffset":100006,"endOffset":100054,"count":5},{"startOffset":100115,"endOffset":100148,"count":5},{"startOffset":100150,"endOffset":100711,"count":5},{"startOffset":100269,"endOffset":100298,"count":4},{"startOffset":100311,"endOffset":100328,"count":3},{"startOffset":100339,"endOffset":100598,"count":2},{"startOffset":100381,"endOffset":100398,"count":1},{"startOffset":100400,"endOffset":100588,"count":1},{"startOffset":100598,"endOffset":100648,"count":3}],"isBlockCoverage":true},{"functionName":"do_var","ranges":[{"startOffset":100757,"endOffset":105620,"count":737},{"startOffset":100959,"endOffset":101262,"count":409},{"startOffset":100997,"endOffset":101049,"count":65},{"startOffset":101049,"endOffset":101256,"count":344},{"startOffset":101090,"endOffset":101256,"count":1},{"startOffset":101363,"endOffset":101452,"count":1},{"startOffset":101482,"endOffset":101511,"count":1},{"startOffset":101513,"endOffset":101593,"count":1}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":101599,"endOffset":105571,"count":737},{"startOffset":101651,"endOffset":101680,"count":9},{"startOffset":101682,"endOffset":103541,"count":8},{"startOffset":103541,"endOffset":105565,"count":729},{"startOffset":103573,"endOffset":103602,"count":3},{"startOffset":103604,"endOffset":104884,"count":3},{"startOffset":104884,"endOffset":105565,"count":726},{"startOffset":104917,"endOffset":105441,"count":721},{"startOffset":105017,"endOffset":105124,"count":3},{"startOffset":105211,"endOffset":105222,"count":247},{"startOffset":105224,"endOffset":105387,"count":474},{"startOffset":105387,"endOffset":105441,"count":720},{"startOffset":105441,"endOffset":105565,"count":5}],"isBlockCoverage":true},{"functionName":"pair","ranges":[{"startOffset":101808,"endOffset":103421,"count":12},{"startOffset":101870,"endOffset":101981,"count":1},{"startOffset":101981,"endOffset":102124,"count":11},{"startOffset":102154,"endOffset":102322,"count":2},{"startOffset":102199,"endOffset":102304,"count":1},{"startOffset":102322,"endOffset":102393,"count":11},{"startOffset":102393,"endOffset":102856,"count":2},{"startOffset":102477,"endOffset":102856,"count":1},{"startOffset":102856,"endOffset":102989,"count":9},{"startOffset":102989,"endOffset":103102,"count":10},{"startOffset":103102,"endOffset":103275,"count":1},{"startOffset":103275,"endOffset":103319,"count":10},{"startOffset":103319,"endOffset":103407,"count":4}],"isBlockCoverage":true},{"functionName":"element","ranges":[{"startOffset":103689,"endOffset":104764,"count":4},{"startOffset":103785,"endOffset":103877,"count":1},{"startOffset":103922,"endOffset":104031,"count":1},{"startOffset":104031,"endOffset":104299,"count":3},{"startOffset":104299,"endOffset":104360,"count":1},{"startOffset":104360,"endOffset":104750,"count":2},{"startOffset":104415,"endOffset":104581,"count":1},{"startOffset":104629,"endOffset":104732,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":105662,"endOffset":105967,"count":2},{"startOffset":105765,"endOffset":105816,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":105987,"endOffset":106142,"count":2},{"startOffset":106053,"endOffset":106101,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":106160,"endOffset":106503,"count":25},{"startOffset":106279,"endOffset":106302,"count":1},{"startOffset":106350,"endOffset":106424,"count":1},{"startOffset":106424,"endOffset":106502,"count":24}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":106517,"endOffset":106893,"count":5},{"startOffset":106754,"endOffset":106845,"count":1},{"startOffset":106845,"endOffset":106892,"count":3}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":106911,"endOffset":109772,"count":17},{"startOffset":107700,"endOffset":108385,"count":6},{"startOffset":107745,"endOffset":107837,"count":1},{"startOffset":107959,"endOffset":107996,"count":3},{"startOffset":108009,"endOffset":108062,"count":3},{"startOffset":108075,"endOffset":108122,"count":3},{"startOffset":108133,"endOffset":108223,"count":3},{"startOffset":108259,"endOffset":108295,"count":4},{"startOffset":108385,"endOffset":109723,"count":11},{"startOffset":108435,"endOffset":108998,"count":2},{"startOffset":108754,"endOffset":108816,"count":1},{"startOffset":108998,"endOffset":109717,"count":9},{"startOffset":109057,"endOffset":109083,"count":8},{"startOffset":109096,"endOffset":109124,"count":7},{"startOffset":109135,"endOffset":109290,"count":3},{"startOffset":109290,"endOffset":109717,"count":6},{"startOffset":109323,"endOffset":109645,"count":5},{"startOffset":109645,"endOffset":109717,"count":1},{"startOffset":109723,"endOffset":109771,"count":11}],"isBlockCoverage":true},{"functionName":"export_id","ranges":[{"startOffset":107013,"endOffset":107629,"count":6},{"startOffset":107072,"endOffset":107150,"count":2},{"startOffset":107150,"endOffset":107262,"count":4},{"startOffset":107262,"endOffset":107333,"count":1},{"startOffset":107333,"endOffset":107557,"count":3},{"startOffset":107420,"endOffset":107507,"count":1},{"startOffset":107557,"endOffset":107628,"count":4}],"isBlockCoverage":true},{"functionName":"loop","ranges":[{"startOffset":109388,"endOffset":109580,"count":6},{"startOffset":109478,"endOffset":109566,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":109787,"endOffset":111190,"count":12},{"startOffset":110060,"endOffset":110158,"count":2},{"startOffset":110158,"endOffset":110234,"count":10},{"startOffset":110243,"endOffset":110271,"count":10},{"startOffset":110278,"endOffset":110322,"count":1},{"startOffset":110322,"endOffset":110377,"count":9},{"startOffset":110377,"endOffset":110696,"count":3},{"startOffset":110433,"endOffset":110532,"count":1},{"startOffset":110696,"endOffset":111009,"count":4},{"startOffset":110899,"endOffset":111003,"count":2},{"startOffset":111009,"endOffset":111097,"count":7},{"startOffset":111097,"endOffset":111141,"count":1},{"startOffset":111141,"endOffset":111189,"count":7}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":111235,"endOffset":111948,"count":1196},{"startOffset":111392,"endOffset":111927,"count":292},{"startOffset":111552,"endOffset":111565,"count":139},{"startOffset":111608,"endOffset":111617,"count":153},{"startOffset":111672,"endOffset":111921,"count":4},{"startOffset":111720,"endOffset":111911,"count":2},{"startOffset":111927,"endOffset":111947,"count":1194}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":111966,"endOffset":114027,"count":14},{"startOffset":112041,"endOffset":112581,"count":2},{"startOffset":112251,"endOffset":112334,"count":1},{"startOffset":112581,"endOffset":112637,"count":12},{"startOffset":112637,"endOffset":112771,"count":1},{"startOffset":112771,"endOffset":112827,"count":12},{"startOffset":112827,"endOffset":113038,"count":8},{"startOffset":112909,"endOffset":112960,"count":1},{"startOffset":113038,"endOffset":113754,"count":4},{"startOffset":113129,"endOffset":113693,"count":3},{"startOffset":113156,"endOffset":113683,"count":4},{"startOffset":113202,"endOffset":113295,"count":1},{"startOffset":113295,"endOffset":113400,"count":3},{"startOffset":113400,"endOffset":113467,"count":1},{"startOffset":113467,"endOffset":113593,"count":3},{"startOffset":113593,"endOffset":113639,"count":2},{"startOffset":113639,"endOffset":113683,"count":1},{"startOffset":113693,"endOffset":113754,"count":3},{"startOffset":113754,"endOffset":113870,"count":11},{"startOffset":113870,"endOffset":113956,"count":1},{"startOffset":113956,"endOffset":114026,"count":11}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":114066,"endOffset":114423,"count":527},{"startOffset":114174,"endOffset":114223,"count":1},{"startOffset":114285,"endOffset":114323,"count":487},{"startOffset":114325,"endOffset":114380,"count":487}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":114441,"endOffset":117055,"count":13},{"startOffset":114651,"endOffset":114700,"count":1},{"startOffset":116264,"endOffset":116891,"count":7},{"startOffset":116465,"endOffset":116556,"count":1},{"startOffset":116556,"endOffset":116885,"count":6},{"startOffset":116678,"endOffset":116709,"count":1},{"startOffset":116711,"endOffset":116816,"count":1},{"startOffset":116855,"endOffset":116874,"count":2},{"startOffset":116891,"endOffset":116933,"count":3},{"startOffset":116933,"endOffset":117054,"count":10}],"isBlockCoverage":true},{"functionName":"major","ranges":[{"startOffset":114914,"endOffset":116200,"count":18},{"startOffset":115600,"endOffset":115674,"count":1},{"startOffset":115674,"endOffset":115807,"count":16},{"startOffset":115807,"endOffset":115936,"count":15},{"startOffset":115845,"endOffset":115872,"count":9},{"startOffset":115874,"endOffset":115926,"count":9},{"startOffset":115936,"endOffset":116116,"count":1},{"startOffset":116116,"endOffset":116155,"count":16},{"startOffset":116155,"endOffset":116194,"count":7}],"isBlockCoverage":true},{"functionName":"minor","ranges":[{"startOffset":115051,"endOffset":115535,"count":31},{"startOffset":115280,"endOffset":115338,"count":1},{"startOffset":115478,"endOffset":115525,"count":13}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":115197,"endOffset":115277,"count":48}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":117072,"endOffset":117306,"count":11},{"startOffset":117234,"endOffset":117282,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":117321,"endOffset":118767,"count":13},{"startOffset":117431,"endOffset":117477,"count":1},{"startOffset":117609,"endOffset":118315,"count":11},{"startOffset":117839,"endOffset":117920,"count":1},{"startOffset":117920,"endOffset":117965,"count":10},{"startOffset":117965,"endOffset":118117,"count":3},{"startOffset":118117,"endOffset":118265,"count":10},{"startOffset":118265,"endOffset":118309,"count":8},{"startOffset":118315,"endOffset":118467,"count":1},{"startOffset":118467,"endOffset":118505,"count":11},{"startOffset":118505,"endOffset":118684,"count":4},{"startOffset":118684,"endOffset":118766,"count":11}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":118805,"endOffset":119161,"count":25},{"startOffset":119018,"endOffset":119110,"count":3},{"startOffset":119110,"endOffset":119160,"count":23}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":119177,"endOffset":119244,"count":1}],"isBlockCoverage":true},{"functionName":"action","ranges":[{"startOffset":119302,"endOffset":120195,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":119444,"endOffset":120192,"count":37},{"startOffset":119649,"endOffset":119709,"count":8},{"startOffset":119830,"endOffset":119903,"count":10},{"startOffset":120044,"endOffset":120110,"count":36}],"isBlockCoverage":true},{"functionName":"amble","ranges":[{"startOffset":120197,"endOffset":121150,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":120342,"endOffset":121147,"count":60260},{"startOffset":120628,"endOffset":121141,"count":40901},{"startOffset":120766,"endOffset":120893,"count":13340},{"startOffset":121004,"endOffset":121131,"count":30311}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":120798,"endOffset":120877,"count":13340}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":121036,"endOffset":121115,"count":39628}],"isBlockCoverage":true},{"functionName":"walk_expression","ranges":[{"startOffset":121326,"endOffset":122187,"count":46762},{"startOffset":121375,"endOffset":122185,"count":29192},{"startOffset":121411,"endOffset":121514,"count":7029},{"startOffset":121514,"endOffset":122179,"count":22163},{"startOffset":121639,"endOffset":121729,"count":349},{"startOffset":121768,"endOffset":121793,"count":22161},{"startOffset":121795,"endOffset":121897,"count":2},{"startOffset":121897,"endOffset":122139,"count":22161},{"startOffset":122033,"endOffset":122139,"count":1}],"isBlockCoverage":true},{"functionName":"walk_statement","ranges":[{"startOffset":122189,"endOffset":123090,"count":22420},{"startOffset":122237,"endOffset":123088,"count":10278},{"startOffset":122273,"endOffset":122345,"count":2311},{"startOffset":122345,"endOffset":123082,"count":7967},{"startOffset":122471,"endOffset":122623,"count":1777},{"startOffset":122511,"endOffset":122609,"count":34},{"startOffset":122623,"endOffset":122961,"count":6190},{"startOffset":122694,"endOffset":122725,"count":1355},{"startOffset":122742,"endOffset":122766,"count":79},{"startOffset":122783,"endOffset":122806,"count":78},{"startOffset":122821,"endOffset":122961,"count":68}],"isBlockCoverage":true},{"functionName":"lookup","ranges":[{"startOffset":123092,"endOffset":125087,"count":8727},{"startOffset":123153,"endOffset":125085,"count":8726},{"startOffset":123413,"endOffset":124568,"count":2386},{"startOffset":123898,"endOffset":124459,"count":194},{"startOffset":123962,"endOffset":124098,"count":104},{"startOffset":124098,"endOffset":124459,"count":90},{"startOffset":124459,"endOffset":124568,"count":2282},{"startOffset":124568,"endOffset":124687,"count":6340},{"startOffset":124609,"endOffset":124687,"count":1},{"startOffset":124687,"endOffset":124743,"count":8622},{"startOffset":124743,"endOffset":124933,"count":2},{"startOffset":124813,"endOffset":124846,"count":1},{"startOffset":124863,"endOffset":124919,"count":1},{"startOffset":124944,"endOffset":125050,"count":2},{"startOffset":125050,"endOffset":125085,"count":8622}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":123441,"endOffset":123735,"count":4010},{"startOffset":123606,"endOffset":123636,"count":2392},{"startOffset":123655,"endOffset":123721,"count":2392}],"isBlockCoverage":true},{"functionName":"subactivate","ranges":[{"startOffset":125089,"endOffset":125194,"count":70}],"isBlockCoverage":true},{"functionName":"preaction_function","ranges":[{"startOffset":125196,"endOffset":126342,"count":590},{"startOffset":125347,"endOffset":125372,"count":235},{"startOffset":125374,"endOffset":125457,"count":1},{"startOffset":125627,"endOffset":125699,"count":296},{"startOffset":125731,"endOffset":125880,"count":6},{"startOffset":125776,"endOffset":125874,"count":1},{"startOffset":125880,"endOffset":126060,"count":584},{"startOffset":125913,"endOffset":126060,"count":3},{"startOffset":125958,"endOffset":126054,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":126090,"endOffset":126338,"count":470},{"startOffset":126178,"endOffset":126196,"count":448},{"startOffset":126198,"endOffset":126254,"count":27},{"startOffset":126254,"endOffset":126332,"count":443}],"isBlockCoverage":true},{"functionName":"bitwise_check","ranges":[{"startOffset":126344,"endOffset":126872,"count":10594},{"startOffset":126400,"endOffset":126431,"count":10162},{"startOffset":126433,"endOffset":126477,"count":1},{"startOffset":126520,"endOffset":126540,"count":7716},{"startOffset":126549,"endOffset":126569,"count":7329},{"startOffset":126578,"endOffset":126597,"count":6969},{"startOffset":126606,"endOffset":126640,"count":5810},{"startOffset":126649,"endOffset":126681,"count":2471},{"startOffset":126690,"endOffset":126819,"count":2447},{"startOffset":126826,"endOffset":126870,"count":1}],"isBlockCoverage":true},{"functionName":"pop_block","ranges":[{"startOffset":126874,"endOffset":127036,"count":2586}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":126923,"endOffset":126972,"count":600}],"isBlockCoverage":true},{"functionName":"action_var","ranges":[{"startOffset":127038,"endOffset":127452,"count":726}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":127091,"endOffset":127448,"count":730},{"startOffset":127179,"endOffset":127408,"count":474},{"startOffset":127279,"endOffset":127343,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":127550,"endOffset":128724,"count":9317},{"startOffset":127608,"endOffset":128722,"count":1557},{"startOffset":127725,"endOffset":127746,"count":1556},{"startOffset":127748,"endOffset":127824,"count":1},{"startOffset":127824,"endOffset":128716,"count":1556},{"startOffset":127856,"endOffset":128716,"count":43},{"startOffset":127899,"endOffset":128058,"count":1},{"startOffset":128058,"endOffset":128706,"count":42},{"startOffset":128172,"endOffset":128294,"count":1},{"startOffset":128294,"endOffset":128692,"count":41},{"startOffset":128365,"endOffset":128388,"count":37},{"startOffset":128409,"endOffset":128430,"count":33},{"startOffset":128451,"endOffset":128472,"count":31},{"startOffset":128493,"endOffset":128514,"count":18},{"startOffset":128535,"endOffset":128556,"count":1},{"startOffset":128575,"endOffset":128692,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":128753,"endOffset":128838,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":128867,"endOffset":128952,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":129028,"endOffset":129219,"count":360}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":129076,"endOffset":129215,"count":720},{"startOffset":129125,"endOffset":129142,"count":65},{"startOffset":129144,"endOffset":129209,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":129247,"endOffset":129944,"count":2878},{"startOffset":129345,"endOffset":129390,"count":2208},{"startOffset":129399,"endOffset":129438,"count":1111},{"startOffset":129445,"endOffset":129942,"count":624},{"startOffset":129653,"endOffset":129674,"count":286},{"startOffset":129691,"endOffset":129725,"count":3},{"startOffset":129742,"endOffset":129778,"count":3},{"startOffset":129795,"endOffset":129852,"count":3},{"startOffset":129867,"endOffset":129926,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":129973,"endOffset":130045,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":130082,"endOffset":130164,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":130195,"endOffset":130290,"count":1996}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":130323,"endOffset":130710,"count":7},{"startOffset":130376,"endOffset":130673,"count":3},{"startOffset":130467,"endOffset":130667,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":130883,"endOffset":131058,"count":8144},{"startOffset":130978,"endOffset":131056,"count":8078}],"isBlockCoverage":true},{"functionName":"init_variable","ranges":[{"startOffset":131062,"endOffset":131318,"count":580},{"startOffset":131168,"endOffset":131280,"count":543},{"startOffset":131280,"endOffset":131317,"count":37}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":131351,"endOffset":131767,"count":101},{"startOffset":131431,"endOffset":131765,"count":64},{"startOffset":131517,"endOffset":131539,"count":54},{"startOffset":131553,"endOffset":131580,"count":63},{"startOffset":131593,"endOffset":131615,"count":63},{"startOffset":131628,"endOffset":131655,"count":63},{"startOffset":131668,"endOffset":131696,"count":63},{"startOffset":131707,"endOffset":131759,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":131795,"endOffset":133602,"count":1277},{"startOffset":132091,"endOffset":132922,"count":1159},{"startOffset":132132,"endOffset":132320,"count":580},{"startOffset":132178,"endOffset":132245,"count":0},{"startOffset":132320,"endOffset":132916,"count":579},{"startOffset":132362,"endOffset":132382,"count":529},{"startOffset":132384,"endOffset":132596,"count":50},{"startOffset":132596,"endOffset":132906,"count":529},{"startOffset":132657,"endOffset":132698,"count":528},{"startOffset":132713,"endOffset":132906,"count":1},{"startOffset":132922,"endOffset":133600,"count":118},{"startOffset":132971,"endOffset":133119,"count":81},{"startOffset":133006,"endOffset":133042,"count":78},{"startOffset":133044,"endOffset":133109,"count":3},{"startOffset":133231,"endOffset":133517,"count":103},{"startOffset":133389,"endOffset":133415,"count":76},{"startOffset":133436,"endOffset":133485,"count":7},{"startOffset":133464,"endOffset":133484,"count":6},{"startOffset":133528,"endOffset":133594,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":132428,"endOffset":132580,"count":100},{"startOffset":132487,"endOffset":132562,"count":79}],"isBlockCoverage":true},{"functionName":"postaction_function","ranges":[{"startOffset":133606,"endOffset":133925,"count":590},{"startOffset":133818,"endOffset":133899,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":133948,"endOffset":135786,"count":9317},{"startOffset":134012,"endOffset":134424,"count":1557},{"startOffset":134195,"endOffset":134324,"count":1552},{"startOffset":134270,"endOffset":134310,"count":3},{"startOffset":134335,"endOffset":134418,"count":8},{"startOffset":134451,"endOffset":134764,"count":284},{"startOffset":134534,"endOffset":134619,"count":1},{"startOffset":134619,"endOffset":134748,"count":283},{"startOffset":134663,"endOffset":134748,"count":1},{"startOffset":134764,"endOffset":135784,"count":9033},{"startOffset":134792,"endOffset":135093,"count":417},{"startOffset":134843,"endOffset":134943,"count":1},{"startOffset":134991,"endOffset":135087,"count":1},{"startOffset":135093,"endOffset":135784,"count":8616},{"startOffset":135120,"endOffset":135140,"count":5288},{"startOffset":135142,"endOffset":135281,"count":3333},{"startOffset":135190,"endOffset":135275,"count":1},{"startOffset":135281,"endOffset":135784,"count":5283},{"startOffset":135309,"endOffset":135328,"count":5277},{"startOffset":135330,"endOffset":135784,"count":2399},{"startOffset":135445,"endOffset":135469,"count":52},{"startOffset":135482,"endOffset":135508,"count":1},{"startOffset":135521,"endOffset":135538,"count":1},{"startOffset":135549,"endOffset":135619,"count":1},{"startOffset":135695,"endOffset":135721,"count":33},{"startOffset":135732,"endOffset":135778,"count":26}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":135816,"endOffset":136192,"count":387},{"startOffset":135955,"endOffset":135995,"count":374},{"startOffset":136004,"endOffset":136044,"count":373},{"startOffset":136051,"endOffset":136190,"count":14}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":136222,"endOffset":136481,"count":360},{"startOffset":136361,"endOffset":136401,"count":359},{"startOffset":136408,"endOffset":136479,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":136559,"endOffset":139884,"count":2878},{"startOffset":136671,"endOffset":136734,"count":29},{"startOffset":136767,"endOffset":136889,"count":65},{"startOffset":136797,"endOffset":136883,"count":1},{"startOffset":136889,"endOffset":139882,"count":2813},{"startOffset":136916,"endOffset":138561,"count":2142},{"startOffset":136953,"endOffset":138061,"count":28},{"startOffset":137021,"endOffset":137045,"count":26},{"startOffset":137062,"endOffset":137085,"count":25},{"startOffset":137102,"endOffset":137125,"count":24},{"startOffset":137142,"endOffset":137165,"count":23},{"startOffset":137180,"endOffset":137366,"count":7},{"startOffset":137366,"endOffset":138051,"count":21},{"startOffset":137400,"endOffset":137533,"count":2},{"startOffset":137436,"endOffset":137519,"count":1},{"startOffset":137533,"endOffset":138051,"count":19},{"startOffset":137564,"endOffset":137798,"count":2},{"startOffset":137643,"endOffset":137670,"count":1},{"startOffset":137672,"endOffset":137784,"count":1},{"startOffset":137798,"endOffset":138051,"count":17},{"startOffset":137830,"endOffset":138051,"count":1},{"startOffset":138061,"endOffset":138555,"count":2114},{"startOffset":138173,"endOffset":138197,"count":53},{"startOffset":138214,"endOffset":138237,"count":44},{"startOffset":138254,"endOffset":138277,"count":37},{"startOffset":138294,"endOffset":138317,"count":2},{"startOffset":138332,"endOffset":138545,"count":2},{"startOffset":138561,"endOffset":139882,"count":671},{"startOffset":138588,"endOffset":139882,"count":669},{"startOffset":138674,"endOffset":138699,"count":8},{"startOffset":138701,"endOffset":138738,"count":1},{"startOffset":138787,"endOffset":139131,"count":2},{"startOffset":138828,"endOffset":139121,"count":1},{"startOffset":139172,"endOffset":139876,"count":4},{"startOffset":139251,"endOffset":139866,"count":3},{"startOffset":139485,"endOffset":139521,"count":1},{"startOffset":139544,"endOffset":139834,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":139913,"endOffset":140182,"count":417},{"startOffset":139977,"endOffset":140054,"count":1},{"startOffset":140094,"endOffset":140180,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":140354,"endOffset":140405,"count":7}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":140500,"endOffset":140952,"count":11},{"startOffset":140605,"endOffset":140786,"count":3},{"startOffset":140786,"endOffset":140902,"count":8}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":140632,"endOffset":140774,"count":3}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":141030,"endOffset":141371,"count":11},{"startOffset":141084,"endOffset":141369,"count":10},{"startOffset":141165,"endOffset":141320,"count":3}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":141440,"endOffset":142495,"count":59},{"startOffset":141563,"endOffset":141619,"count":53},{"startOffset":141626,"endOffset":141670,"count":6},{"startOffset":141670,"endOffset":142493,"count":53},{"startOffset":141735,"endOffset":141790,"count":1},{"startOffset":141790,"endOffset":142493,"count":52},{"startOffset":141855,"endOffset":141910,"count":1},{"startOffset":141910,"endOffset":142493,"count":51},{"startOffset":141971,"endOffset":142008,"count":1},{"startOffset":142015,"endOffset":142070,"count":1},{"startOffset":142070,"endOffset":142493,"count":50},{"startOffset":142132,"endOffset":142168,"count":1},{"startOffset":142175,"endOffset":142229,"count":1},{"startOffset":142229,"endOffset":142493,"count":49},{"startOffset":142293,"endOffset":142398,"count":46},{"startOffset":142405,"endOffset":142493,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":142518,"endOffset":143251,"count":920},{"startOffset":142563,"endOffset":142718,"count":34},{"startOffset":142666,"endOffset":142712,"count":26},{"startOffset":142718,"endOffset":143249,"count":886},{"startOffset":142746,"endOffset":142854,"count":167},{"startOffset":142796,"endOffset":142848,"count":2},{"startOffset":142854,"endOffset":143249,"count":719},{"startOffset":142883,"endOffset":143012,"count":1},{"startOffset":143012,"endOffset":143249,"count":718},{"startOffset":143056,"endOffset":143075,"count":566},{"startOffset":143084,"endOffset":143110,"count":483},{"startOffset":143119,"endOffset":143140,"count":134},{"startOffset":143147,"endOffset":143249,"count":104},{"startOffset":143197,"endOffset":143243,"count":32}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":142600,"endOffset":142663,"count":16}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":143333,"endOffset":143782,"count":9},{"startOffset":143503,"endOffset":143538,"count":1},{"startOffset":143540,"endOffset":143605,"count":1},{"startOffset":143605,"endOffset":143780,"count":8},{"startOffset":143647,"endOffset":143666,"count":3},{"startOffset":143675,"endOffset":143724,"count":3},{"startOffset":143696,"endOffset":143723,"count":2},{"startOffset":143731,"endOffset":143780,"count":7}],"isBlockCoverage":true},{"functionName":"delve","ranges":[{"startOffset":143786,"endOffset":144546,"count":567}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":143863,"endOffset":144542,"count":3797},{"startOffset":143908,"endOffset":144536,"count":3784},{"startOffset":144007,"endOffset":144526,"count":1525},{"startOffset":144086,"endOffset":144218,"count":9},{"startOffset":144164,"endOffset":144196,"count":0},{"startOffset":144237,"endOffset":144376,"count":9},{"startOffset":144376,"endOffset":144512,"count":1516},{"startOffset":144398,"endOffset":144512,"count":1}],"isBlockCoverage":true},{"functionName":"uninitialized_and_unused","ranges":[{"startOffset":144548,"endOffset":144857,"count":69},{"startOffset":144779,"endOffset":144793,"count":61},{"startOffset":144795,"endOffset":144825,"count":18}],"isBlockCoverage":true},{"functionName":"whitage","ranges":[{"startOffset":144922,"endOffset":157401,"count":67}],"isBlockCoverage":true},{"functionName":"pop","ranges":[{"startOffset":145417,"endOffset":145641,"count":6751}],"isBlockCoverage":true},{"functionName":"push","ranges":[{"startOffset":145647,"endOffset":145799,"count":6751}],"isBlockCoverage":true},{"functionName":"expected_at","ranges":[{"startOffset":145805,"endOffset":146234,"count":27}],"isBlockCoverage":true},{"functionName":"at_margin","ranges":[{"startOffset":146240,"endOffset":146383,"count":9683},{"startOffset":146330,"endOffset":146377,"count":22}],"isBlockCoverage":true},{"functionName":"no_space_only","ranges":[{"startOffset":146389,"endOffset":146985,"count":26043},{"startOffset":146476,"endOffset":146503,"count":26042},{"startOffset":146516,"endOffset":146619,"count":26042},{"startOffset":146578,"endOffset":146605,"count":26035},{"startOffset":146630,"endOffset":146979,"count":13}],"isBlockCoverage":true},{"functionName":"no_space","ranges":[{"startOffset":146991,"endOffset":147864,"count":958},{"startOffset":147051,"endOffset":147365,"count":956},{"startOffset":147094,"endOffset":147122,"count":1},{"startOffset":147124,"endOffset":147355,"count":1},{"startOffset":147365,"endOffset":147858,"count":2},{"startOffset":147500,"endOffset":147512,"count":0},{"startOffset":147702,"endOffset":147848,"count":0}],"isBlockCoverage":true},{"functionName":"one_space_only","ranges":[{"startOffset":147870,"endOffset":148143,"count":2537},{"startOffset":147935,"endOffset":147966,"count":2536},{"startOffset":147968,"endOffset":148137,"count":6}],"isBlockCoverage":true},{"functionName":"one_space","ranges":[{"startOffset":148149,"endOffset":148618,"count":13964},{"startOffset":148209,"endOffset":148217,"count":518},{"startOffset":148219,"endOffset":148503,"count":13446},{"startOffset":148266,"endOffset":148294,"count":13},{"startOffset":148296,"endOffset":148493,"count":13},{"startOffset":148503,"endOffset":148612,"count":518},{"startOffset":148550,"endOffset":148602,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":148655,"endOffset":157397,"count":55061},{"startOffset":148742,"endOffset":148765,"count":53417},{"startOffset":148767,"endOffset":148816,"count":1712},{"startOffset":148816,"endOffset":157391,"count":53349},{"startOffset":149342,"endOffset":151563,"count":7571},{"startOffset":149448,"endOffset":151129,"count":6751},{"startOffset":149553,"endOffset":149582,"count":6744},{"startOffset":149686,"endOffset":150437,"count":2521},{"startOffset":149817,"endOffset":149829,"count":463},{"startOffset":149957,"endOffset":150246,"count":2},{"startOffset":150009,"endOffset":150220,"count":1},{"startOffset":150246,"endOffset":150415,"count":2519},{"startOffset":150270,"endOffset":150340,"count":6},{"startOffset":150340,"endOffset":150415,"count":2513},{"startOffset":150437,"endOffset":151111,"count":4230},{"startOffset":150489,"endOffset":150514,"count":4227},{"startOffset":150516,"endOffset":150875,"count":4},{"startOffset":151129,"endOffset":151549,"count":820},{"startOffset":151405,"endOffset":151464,"count":819},{"startOffset":151464,"endOffset":151531,"count":1},{"startOffset":151563,"endOffset":157137,"count":45778},{"startOffset":151617,"endOffset":151907,"count":3778},{"startOffset":151663,"endOffset":151728,"count":138},{"startOffset":151728,"endOffset":151833,"count":3640},{"startOffset":151907,"endOffset":157123,"count":42000},{"startOffset":151938,"endOffset":152169,"count":6751},{"startOffset":151999,"endOffset":152018,"count":2521},{"startOffset":152020,"endOffset":152081,"count":2521},{"startOffset":152081,"endOffset":152151,"count":4230},{"startOffset":152169,"endOffset":157123,"count":35249},{"startOffset":152468,"endOffset":152530,"count":24},{"startOffset":152530,"endOffset":157105,"count":35225},{"startOffset":152564,"endOffset":152851,"count":1},{"startOffset":152851,"endOffset":157105,"count":35224},{"startOffset":152878,"endOffset":153312,"count":2674},{"startOffset":152914,"endOffset":153053,"count":1221},{"startOffset":153000,"endOffset":153027,"count":503},{"startOffset":153055,"endOffset":153152,"count":1790},{"startOffset":153152,"endOffset":153227,"count":884},{"startOffset":153312,"endOffset":157105,"count":32550},{"startOffset":153349,"endOffset":153653,"count":96},{"startOffset":153385,"endOffset":153514,"count":94},{"startOffset":153514,"endOffset":153631,"count":2},{"startOffset":153653,"endOffset":157105,"count":32454},{"startOffset":153737,"endOffset":153756,"count":9218},{"startOffset":153781,"endOffset":153788,"count":2843},{"startOffset":153811,"endOffset":153870,"count":139},{"startOffset":153870,"endOffset":157105,"count":32315},{"startOffset":153945,"endOffset":153964,"count":28997},{"startOffset":153989,"endOffset":154009,"count":28995},{"startOffset":154034,"endOffset":154053,"count":28988},{"startOffset":154078,"endOffset":154097,"count":26314},{"startOffset":154122,"endOffset":154141,"count":22052},{"startOffset":154166,"endOffset":154319,"count":21626},{"startOffset":154252,"endOffset":154293,"count":9079},{"startOffset":154273,"endOffset":154292,"count":6375},{"startOffset":154344,"endOffset":154483,"count":18515},{"startOffset":154432,"endOffset":154457,"count":548},{"startOffset":154506,"endOffset":154570,"count":14075},{"startOffset":154570,"endOffset":157105,"count":18240},{"startOffset":154597,"endOffset":154617,"count":14922},{"startOffset":154619,"endOffset":154683,"count":3320},{"startOffset":154683,"endOffset":157105,"count":14920},{"startOffset":154710,"endOffset":154850,"count":0},{"startOffset":154934,"endOffset":154955,"count":14824},{"startOffset":154980,"endOffset":155002,"count":14799},{"startOffset":155027,"endOffset":155048,"count":14789},{"startOffset":155073,"endOffset":155097,"count":14633},{"startOffset":155122,"endOffset":155144,"count":14617},{"startOffset":155169,"endOffset":155191,"count":14602},{"startOffset":155216,"endOffset":155239,"count":14576},{"startOffset":155264,"endOffset":155286,"count":14570},{"startOffset":155311,"endOffset":155336,"count":14282},{"startOffset":155361,"endOffset":155406,"count":14282},{"startOffset":155386,"endOffset":155405,"count":2},{"startOffset":155431,"endOffset":155471,"count":14280},{"startOffset":155451,"endOffset":155470,"count":1933},{"startOffset":155494,"endOffset":155646,"count":2399},{"startOffset":155646,"endOffset":157105,"count":12521},{"startOffset":155777,"endOffset":155806,"count":8487},{"startOffset":155831,"endOffset":155981,"count":4479},{"startOffset":155916,"endOffset":155955,"count":357},{"startOffset":155936,"endOffset":155954,"count":75},{"startOffset":156006,"endOffset":156159,"count":4146},{"startOffset":156092,"endOffset":156133,"count":357},{"startOffset":156113,"endOffset":156132,"count":75},{"startOffset":156184,"endOffset":156209,"count":3813},{"startOffset":156234,"endOffset":156252,"count":3267},{"startOffset":156277,"endOffset":156761,"count":2875},{"startOffset":156392,"endOffset":156417,"count":321},{"startOffset":156450,"endOffset":156475,"count":256},{"startOffset":156534,"endOffset":156735,"count":2619},{"startOffset":156620,"endOffset":156646,"count":1360},{"startOffset":156679,"endOffset":156705,"count":1341},{"startOffset":156786,"endOffset":156837,"count":1597},{"startOffset":156817,"endOffset":156836,"count":1250},{"startOffset":156860,"endOffset":156988,"count":12174},{"startOffset":156988,"endOffset":157105,"count":347},{"startOffset":157021,"endOffset":157039,"count":201},{"startOffset":157041,"endOffset":157105,"count":188}],"isBlockCoverage":true},{"functionName":"jslint","ranges":[{"startOffset":157435,"endOffset":162110,"count":405},{"startOffset":157888,"endOffset":157891,"count":1},{"startOffset":157904,"endOffset":157907,"count":404},{"startOffset":159076,"endOffset":159150,"count":23},{"startOffset":159150,"endOffset":160079,"count":356},{"startOffset":159350,"endOffset":159462,"count":4},{"startOffset":159395,"endOffset":159448,"count":1},{"startOffset":159462,"endOffset":159753,"count":352},{"startOffset":159645,"endOffset":159739,"count":1},{"startOffset":159924,"endOffset":160069,"count":69},{"startOffset":160005,"endOffset":160055,"count":67},{"startOffset":160079,"endOffset":160109,"count":297},{"startOffset":160109,"endOffset":160342,"count":293},{"startOffset":160342,"endOffset":160383,"count":297},{"startOffset":160383,"endOffset":160457,"count":1},{"startOffset":160457,"endOffset":160492,"count":296},{"startOffset":160492,"endOffset":160772,"count":109},{"startOffset":160638,"endOffset":160766,"count":1},{"startOffset":161882,"endOffset":161896,"count":56},{"startOffset":161983,"endOffset":161993,"count":3},{"startOffset":162006,"endOffset":162017,"count":402}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":158741,"endOffset":159005,"count":24},{"startOffset":158898,"endOffset":158981,"count":11}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":160142,"endOffset":160330,"count":30},{"startOffset":160215,"endOffset":160316,"count":3}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":160857,"endOffset":161091,"count":880},{"startOffset":160962,"endOffset":160980,"count":808},{"startOffset":160981,"endOffset":161003,"count":686}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":161097,"endOffset":161626,"count":880}],"isBlockCoverage":true},{"functionName":"cli","ranges":[{"startOffset":162112,"endOffset":166621,"count":4},{"startOffset":165181,"endOffset":165197,"count":3},{"startOffset":165215,"endOffset":165363,"count":2},{"startOffset":165363,"endOffset":166598,"count":1},{"startOffset":166598,"endOffset":166620,"count":2}],"isBlockCoverage":true},{"functionName":"string_line_count","ranges":[{"startOffset":162303,"endOffset":162720,"count":8},{"startOffset":162551,"endOffset":162694,"count":2218},{"startOffset":162624,"endOffset":162662,"count":8},{"startOffset":162662,"endOffset":162694,"count":2210}],"isBlockCoverage":true},{"functionName":"jslint_from_file","ranges":[{"startOffset":162725,"endOffset":165146,"count":22},{"startOffset":162936,"endOffset":163490,"count":5},{"startOffset":163499,"endOffset":163970,"count":2},{"startOffset":163979,"endOffset":164534,"count":1},{"startOffset":164543,"endOffset":164678,"count":14},{"startOffset":164688,"endOffset":164762,"count":14},{"startOffset":164762,"endOffset":165140,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":163070,"endOffset":163468,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":163629,"endOffset":163948,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":164116,"endOffset":164512,"count":6}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":164969,"endOffset":165103,"count":4}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":165447,"endOffset":166440,"count":28},{"startOffset":165627,"endOffset":165640,"count":4},{"startOffset":165653,"endOffset":165664,"count":9},{"startOffset":165677,"endOffset":165690,"count":10},{"startOffset":165703,"endOffset":165714,"count":12},{"startOffset":165727,"endOffset":165739,"count":12},{"startOffset":165752,"endOffset":165786,"count":13},{"startOffset":165799,"endOffset":165831,"count":15},{"startOffset":165845,"endOffset":165928,"count":13},{"startOffset":165928,"endOffset":165943,"count":12},{"startOffset":165943,"endOffset":165997,"count":1},{"startOffset":165997,"endOffset":166115,"count":12},{"startOffset":166115,"endOffset":166122,"count":11},{"startOffset":166123,"endOffset":166147,"count":11},{"startOffset":166163,"endOffset":166202,"count":1},{"startOffset":166202,"endOffset":166439,"count":11}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":166651,"endOffset":166913,"count":394},{"startOffset":166766,"endOffset":166855,"count":3},{"startOffset":166855,"endOffset":166912,"count":391}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":167355,"endOffset":167414,"count":1}],"isBlockCoverage":true}]},{"scriptId":"89","url":"net.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":47106,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":1448,"endOffset":1473,"count":0}],"isBlockCoverage":false},{"functionName":"noop","ranges":[{"startOffset":3388,"endOffset":3406,"count":0}],"isBlockCoverage":false},{"functionName":"getFlags","ranges":[{"startOffset":3408,"endOffset":3502,"count":0}],"isBlockCoverage":false},{"functionName":"createHandle","ranges":[{"startOffset":3504,"endOffset":3885,"count":1},{"startOffset":3671,"endOffset":3693,"count":0},{"startOffset":3727,"endOffset":3884,"count":0}],"isBlockCoverage":true},{"functionName":"getNewAsyncId","ranges":[{"startOffset":3888,"endOffset":4026,"count":1},{"startOffset":3983,"endOffset":4001,"count":0}],"isBlockCoverage":true},{"functionName":"isPipeName","ranges":[{"startOffset":4029,"endOffset":4112,"count":0}],"isBlockCoverage":false},{"functionName":"createServer","ranges":[{"startOffset":4114,"endOffset":4218,"count":0}],"isBlockCoverage":false},{"functionName":"connect","ranges":[{"startOffset":4441,"endOffset":4732,"count":0}],"isBlockCoverage":false},{"functionName":"normalizeArgs","ranges":[{"startOffset":5227,"endOffset":5953,"count":0}],"isBlockCoverage":false},{"functionName":"initSocketHandle","ranges":[{"startOffset":6025,"endOffset":6628,"count":1},{"startOffset":6381,"endOffset":6622,"count":0}],"isBlockCoverage":true},{"functionName":"Socket","ranges":[{"startOffset":6763,"endOffset":10585,"count":1},{"startOffset":6823,"endOffset":6850,"count":0},{"startOffset":7400,"endOffset":7426,"count":0},{"startOffset":7970,"endOffset":8078,"count":0},{"startOffset":8175,"endOffset":8254,"count":0},{"startOffset":8255,"endOffset":8303,"count":0},{"startOffset":8305,"endOffset":8536,"count":0},{"startOffset":9087,"endOffset":9121,"count":0},{"startOffset":9272,"endOffset":9801,"count":0},{"startOffset":10167,"endOffset":10432,"count":0}],"isBlockCoverage":true},{"functionName":"_unrefTimer","ranges":[{"startOffset":10758,"endOffset":10888,"count":209},{"startOffset":10860,"endOffset":10882,"count":0}],"isBlockCoverage":true},{"functionName":"Socket._final","ranges":[{"startOffset":11008,"endOffset":11656,"count":0}],"isBlockCoverage":false},{"functionName":"afterShutdown","ranges":[{"startOffset":11660,"endOffset":12039,"count":0}],"isBlockCoverage":false},{"functionName":"writeAfterFIN","ranges":[{"startOffset":12246,"endOffset":12702,"count":0}],"isBlockCoverage":false},{"functionName":"Socket._onTimeout","ranges":[{"startOffset":12784,"endOffset":13288,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.setNoDelay","ranges":[{"startOffset":13322,"endOffset":13771,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.setKeepAlive","ranges":[{"startOffset":13807,"endOffset":14054,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.address","ranges":[{"startOffset":14085,"endOffset":14129,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":14196,"endOffset":14240,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":14300,"endOffset":14356,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":14447,"endOffset":14762,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":14831,"endOffset":14911,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":14979,"endOffset":15024,"count":0}],"isBlockCoverage":false},{"functionName":"tryReadStart","ranges":[{"startOffset":15031,"endOffset":15277,"count":0}],"isBlockCoverage":false},{"functionName":"Socket._read","ranges":[{"startOffset":15369,"endOffset":15598,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.end","ranges":[{"startOffset":15625,"endOffset":15777,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.pause","ranges":[{"startOffset":15806,"endOffset":16140,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.resume","ranges":[{"startOffset":16170,"endOffset":16354,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.read","ranges":[{"startOffset":16382,"endOffset":16568,"count":0}],"isBlockCoverage":false},{"functionName":"onReadableStreamEnd","ranges":[{"startOffset":16615,"endOffset":16900,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.destroySoon","ranges":[{"startOffset":16934,"endOffset":17081,"count":0}],"isBlockCoverage":false},{"functionName":"Socket._destroy","ranges":[{"startOffset":17113,"endOffset":18067,"count":0}],"isBlockCoverage":false},{"functionName":"Socket._getpeername","ranges":[{"startOffset":18102,"endOffset":18393,"count":0}],"isBlockCoverage":false},{"functionName":"protoGetter","ranges":[{"startOffset":18396,"endOffset":18556,"count":8}],"isBlockCoverage":true},{"functionName":"bytesRead","ranges":[{"startOffset":18583,"endOffset":18674,"count":0}],"isBlockCoverage":false},{"functionName":"remoteAddress","ranges":[{"startOffset":18707,"endOffset":18773,"count":0}],"isBlockCoverage":false},{"functionName":"remoteFamily","ranges":[{"startOffset":18805,"endOffset":18869,"count":0}],"isBlockCoverage":false},{"functionName":"remotePort","ranges":[{"startOffset":18899,"endOffset":18959,"count":0}],"isBlockCoverage":false},{"functionName":"Socket._getsockname","ranges":[{"startOffset":18996,"endOffset":19281,"count":0}],"isBlockCoverage":false},{"functionName":"localAddress","ranges":[{"startOffset":19313,"endOffset":19378,"count":0}],"isBlockCoverage":false},{"functionName":"localPort","ranges":[{"startOffset":19408,"endOffset":19467,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.","ranges":[{"startOffset":19509,"endOffset":19556,"count":0}],"isBlockCoverage":false},{"functionName":"Socket._writeGeneric","ranges":[{"startOffset":19592,"endOffset":20353,"count":209},{"startOffset":19814,"endOffset":20007,"count":0},{"startOffset":20088,"endOffset":20144,"count":0},{"startOffset":20198,"endOffset":20234,"count":0},{"startOffset":20313,"endOffset":20351,"count":0}],"isBlockCoverage":true},{"functionName":"connect","ranges":[{"startOffset":19909,"endOffset":19989,"count":0}],"isBlockCoverage":false},{"functionName":"Socket._writev","ranges":[{"startOffset":20384,"endOffset":20452,"count":0}],"isBlockCoverage":false},{"functionName":"Socket._write","ranges":[{"startOffset":20482,"endOffset":20563,"count":209}],"isBlockCoverage":true},{"functionName":"_bytesDispatched","ranges":[{"startOffset":20756,"endOffset":20860,"count":0}],"isBlockCoverage":false},{"functionName":"bytesWritten","ranges":[{"startOffset":20892,"endOffset":21821,"count":0}],"isBlockCoverage":false},{"functionName":"checkBindError","ranges":[{"startOffset":21826,"endOffset":22625,"count":0}],"isBlockCoverage":false},{"functionName":"internalConnect","ranges":[{"startOffset":22628,"endOffset":24333,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.connect","ranges":[{"startOffset":24363,"endOffset":25667,"count":0}],"isBlockCoverage":false},{"functionName":"lookupAndConnect","ranges":[{"startOffset":25671,"endOffset":28698,"count":0}],"isBlockCoverage":false},{"functionName":"connectErrorNT","ranges":[{"startOffset":28701,"endOffset":28760,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.ref","ranges":[{"startOffset":28786,"endOffset":28973,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.unref","ranges":[{"startOffset":29002,"endOffset":29195,"count":0}],"isBlockCoverage":false},{"functionName":"afterConnect","ranges":[{"startOffset":29199,"endOffset":30447,"count":0}],"isBlockCoverage":false},{"functionName":"Server","ranges":[{"startOffset":30450,"endOffset":31779,"count":0}],"isBlockCoverage":false},{"functionName":"toNumber","ranges":[{"startOffset":31890,"endOffset":31955,"count":0}],"isBlockCoverage":false},{"functionName":"createServerHandle","ranges":[{"startOffset":32023,"endOffset":33475,"count":0}],"isBlockCoverage":false},{"functionName":"setupListenHandle","ranges":[{"startOffset":33477,"endOffset":35828,"count":0}],"isBlockCoverage":false},{"functionName":"emitErrorNT","ranges":[{"startOffset":35895,"endOffset":35957,"count":0}],"isBlockCoverage":false},{"functionName":"emitListeningNT","ranges":[{"startOffset":35960,"endOffset":36075,"count":0}],"isBlockCoverage":false},{"functionName":"listenInCluster","ranges":[{"startOffset":36078,"endOffset":37296,"count":0}],"isBlockCoverage":false},{"functionName":"Server.listen","ranges":[{"startOffset":37325,"endOffset":40823,"count":0}],"isBlockCoverage":false},{"functionName":"lookupAndListen","ranges":[{"startOffset":40826,"endOffset":41238,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":41301,"endOffset":41344,"count":0}],"isBlockCoverage":false},{"functionName":"Server.address","ranges":[{"startOffset":41419,"endOffset":41708,"count":0}],"isBlockCoverage":false},{"functionName":"onconnection","ranges":[{"startOffset":41711,"endOffset":42372,"count":0}],"isBlockCoverage":false},{"functionName":"Server.getConnections","ranges":[{"startOffset":42409,"endOffset":43162,"count":0}],"isBlockCoverage":false},{"functionName":"Server.close","ranges":[{"startOffset":43191,"endOffset":44058,"count":0}],"isBlockCoverage":false},{"functionName":"Server._emitCloseIfDrained","ranges":[{"startOffset":44100,"endOffset":44486,"count":0}],"isBlockCoverage":false},{"functionName":"emitCloseNT","ranges":[{"startOffset":44490,"endOffset":44573,"count":0}],"isBlockCoverage":false},{"functionName":"Server.","ranges":[{"startOffset":44632,"endOffset":44794,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":44997,"endOffset":45033,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":45037,"endOffset":45078,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":45138,"endOffset":45169,"count":217}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":45173,"endOffset":45209,"count":1}],"isBlockCoverage":true},{"functionName":"Server._setupWorker","ranges":[{"startOffset":45247,"endOffset":45473,"count":0}],"isBlockCoverage":false},{"functionName":"Server.ref","ranges":[{"startOffset":45499,"endOffset":45597,"count":0}],"isBlockCoverage":false},{"functionName":"Server.unref","ranges":[{"startOffset":45625,"endOffset":45724,"count":0}],"isBlockCoverage":false},{"functionName":"_setSimultaneousAccepts","ranges":[{"startOffset":45866,"endOffset":46535,"count":0}],"isBlockCoverage":false},{"functionName":"_setSimultaneousAccepts","ranges":[{"startOffset":46574,"endOffset":46815,"count":0}],"isBlockCoverage":false}]},{"scriptId":"90","url":"internal/net.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1694,"count":1}],"isBlockCoverage":false},{"functionName":"isIPv4","ranges":[{"startOffset":974,"endOffset":1022,"count":0}],"isBlockCoverage":false},{"functionName":"isIPv6","ranges":[{"startOffset":1024,"endOffset":1072,"count":0}],"isBlockCoverage":false},{"functionName":"isIP","ranges":[{"startOffset":1074,"endOffset":1160,"count":0}],"isBlockCoverage":false},{"functionName":"makeSyncWrite","ranges":[{"startOffset":1162,"endOffset":1576,"count":0}],"isBlockCoverage":false}]},{"scriptId":"91","url":"internal/stream_base_commons.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":7120,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":987,"endOffset":1012,"count":0}],"isBlockCoverage":false},{"functionName":"handleWriteReq","ranges":[{"startOffset":1131,"endOffset":1986,"count":209},{"startOffset":1231,"endOffset":1398,"count":0},{"startOffset":1403,"endOffset":1417,"count":0},{"startOffset":1422,"endOffset":1486,"count":0},{"startOffset":1574,"endOffset":1636,"count":0},{"startOffset":1641,"endOffset":1653,"count":0},{"startOffset":1658,"endOffset":1671,"count":0},{"startOffset":1676,"endOffset":1691,"count":0},{"startOffset":1696,"endOffset":1760,"count":0},{"startOffset":1765,"endOffset":1980,"count":0}],"isBlockCoverage":true},{"functionName":"onWriteComplete","ranges":[{"startOffset":1988,"endOffset":2498,"count":0}],"isBlockCoverage":false},{"functionName":"createWriteWrap","ranges":[{"startOffset":2500,"endOffset":2701,"count":209}],"isBlockCoverage":true},{"functionName":"writevGeneric","ranges":[{"startOffset":2703,"endOffset":3344,"count":0}],"isBlockCoverage":false},{"functionName":"writeGeneric","ranges":[{"startOffset":3346,"endOffset":3553,"count":209}],"isBlockCoverage":true},{"functionName":"afterWriteDispatched","ranges":[{"startOffset":3555,"endOffset":3864,"count":209},{"startOffset":3728,"endOffset":3793,"count":0},{"startOffset":3828,"endOffset":3862,"count":0}],"isBlockCoverage":true},{"functionName":"onStreamRead","ranges":[{"startOffset":3866,"endOffset":5991,"count":0}],"isBlockCoverage":false},{"functionName":"setStreamTimeout","ranges":[{"startOffset":5993,"endOffset":6895,"count":0}],"isBlockCoverage":false}]},{"scriptId":"92","url":"internal/dtrace.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":568,"count":1}],"isBlockCoverage":false},{"functionName":"DTRACE_HTTP_CLIENT_REQUEST","ranges":[{"startOffset":97,"endOffset":105,"count":0}],"isBlockCoverage":false},{"functionName":"DTRACE_HTTP_CLIENT_RESPONSE","ranges":[{"startOffset":139,"endOffset":147,"count":0}],"isBlockCoverage":false},{"functionName":"DTRACE_HTTP_SERVER_REQUEST","ranges":[{"startOffset":180,"endOffset":188,"count":0}],"isBlockCoverage":false},{"functionName":"DTRACE_HTTP_SERVER_RESPONSE","ranges":[{"startOffset":222,"endOffset":230,"count":0}],"isBlockCoverage":false},{"functionName":"DTRACE_NET_SERVER_CONNECTION","ranges":[{"startOffset":265,"endOffset":273,"count":0}],"isBlockCoverage":false},{"functionName":"DTRACE_NET_STREAM_END","ranges":[{"startOffset":301,"endOffset":309,"count":0}],"isBlockCoverage":false}]}],"timestamp":46.109886} \ No newline at end of file diff --git a/branch.alpha/.build/coverage/index.html b/branch.alpha/.build/coverage/index.html new file mode 100644 index 000000000..7eea1471d --- /dev/null +++ b/branch.alpha/.build/coverage/index.html @@ -0,0 +1,153 @@ + + + +coverage-report + + + +
    +
    coverage-report
    + + + + + + + + + + + + + + + + + + +
    +
    + + diff --git a/branch.alpha/.build/coverage/jslint.js.html b/branch.alpha/.build/coverage/jslint.js.html new file mode 100644 index 000000000..1401c8e88 --- /dev/null +++ b/branch.alpha/.build/coverage/jslint.js.html @@ -0,0 +1,6179 @@ + + + +coverage-report + + + +
    +
    coverage-report
    +
    files coveredlines
    + ./
    +
    +
    +
    +
    + 99.65 %
    + 6400 / 6422 +
    + ./ jslint.js
    +
    +
    +
    +
    + 99.63 %
    + 6022 / 6044 +
    + ./ test.js
    +
    +
    +
    +
    + 100.00 %
    + 378 / 378 +
    + + + + + + + + + + +
    files coveredlines
    + ./ jslint.js
    +
    +
    +
    +
    + 99.63 %
    + 6022 / 6044 +
    +
    +
    +
        1.      1#!/usr/bin/env node
    +
        2.      1// jslint.js
    +
        3.      1// Copyright (c) 2015 Douglas Crockford  (www.JSLint.com)
    +
        4.      1
    +
        5.      1// Permission is hereby granted, free of charge, to any person obtaining a copy
    +
        6.      1// of this software and associated documentation files (the "Software"), to deal
    +
        7.      1// in the Software without restriction, including without limitation the rights
    +
        8.      1// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +
        9.      1// copies of the Software, and to permit persons to whom the Software is
    +
       10.      1// furnished to do so, subject to the following conditions:
    +
       11.      1
    +
       12.      1// The above copyright notice and this permission notice shall be included in
    +
       13.      1// all copies or substantial portions of the Software.
    +
       14.      1
    +
       15.      1// The Software shall be used for Good, not Evil.
    +
       16.      1
    +
       17.      1// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +
       18.      1// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +
       19.      1// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    +
       20.      1// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +
       21.      1// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +
       22.      1// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    +
       23.      1// SOFTWARE.
    +
       24.      1
    +
       25.      1// jslint(source, option_object, global_array) is a function that takes 3
    +
       26.      1// arguments. The second two arguments are optional.
    +
       27.      1
    +
       28.      1//      source          A text to analyze, a string or an array of strings.
    +
       29.      1//      option_object   An object whose keys correspond to option names.
    +
       30.      1//      global_array    An array of strings containing global variables that
    +
       31.      1//                      the file is allowed readonly access.
    +
       32.      1
    +
       33.      1// jslint returns an object containing its results. The object contains a lot
    +
       34.      1// of valuable information. It can be used to generate reports. The object
    +
       35.      1// contains:
    +
       36.      1
    +
       37.      1//      directives: an array of directive comment tokens.
    +
       38.      1//      edition: the version of JSLint that did the analysis.
    +
       39.      1//      exports: the names exported from the module.
    +
       40.      1//      froms: an array of strings representing each of the imports.
    +
       41.      1//      functions: an array of objects that represent all of the functions
    +
       42.      1//              declared in the file.
    +
       43.      1//      global: an object representing the global object. Its .context property
    +
       44.      1//              is an object containing a property for each global variable.
    +
       45.      1//      id: "(JSLint)"
    +
       46.      1//      json: true if the file is a JSON text.
    +
       47.      1//      lines: an array of strings, the source.
    +
       48.      1//      module: true if an import or export statement was used.
    +
       49.      1//      ok: true if no warnings were generated. This is what you want.
    +
       50.      1//      option: the option argument.
    +
       51.      1//      property: a property object.
    +
       52.      1//      stop: true if JSLint was unable to finish. You don't want this.
    +
       53.      1//      tokens: an array of objects representing the tokens in the file.
    +
       54.      1//      tree: the token objects arranged in a tree.
    +
       55.      1//      warnings: an array of warning objects. A warning object can contain:
    +
       56.      1//          name: "JSLintError"
    +
       57.      1//          column: A column number in the file.
    +
       58.      1//          line: A line number in the file.
    +
       59.      1//          code: A warning code string.
    +
       60.      1//          message: The warning message string.
    +
       61.      1//          a: Exhibit A.
    +
       62.      1//          b: Exhibit B.
    +
       63.      1//          c: Exhibit C.
    +
       64.      1//          d: Exhibit D.
    +
       65.      1
    +
       66.      1// jslint works in several phases. In any of these phases, errors might be
    +
       67.      1// found. Sometimes JSLint is able to recover from an error and continue
    +
       68.      1// parsing. In some cases, it cannot and will stop early. If that should happen,
    +
       69.      1// repair your code and try again.
    +
       70.      1
    +
       71.      1// Phases:
    +
       72.      1
    +
       73.      1//      1. If the source is a single string, split it into an array of strings.
    +
       74.      1//      2. Turn the source into an array of tokens.
    +
       75.      1//      3. Furcate the tokens into a parse tree.
    +
       76.      1//      4. Walk the tree, traversing all of the nodes of the tree. It is a
    +
       77.      1//          recursive traversal. Each node may be processed on the way down
    +
       78.      1//          (preaction) and on the way up (postaction).
    +
       79.      1//      5. Check the whitespace between the tokens.
    +
       80.      1
    +
       81.      1// jslint can also examine JSON text. It decides that a file is JSON text if
    +
       82.      1// the first token is "[" or "{". Processing of JSON text is much simpler than
    +
       83.      1// the processing of JavaScript programs. Only the first three phases are
    +
       84.      1// required.
    +
       85.      1
    +
       86.      1// WARNING: JSLint will hurt your feelings.
    +
       87.      1
    +
       88.      1/*jslint node*/
    +
       89.      1
    +
       90.      1/*property
    +
       91.      1    unordered,
    +
       92.      1    JSLINT_CLI, a, all, and, argv, arity, assign, b, bad_assignment_a,
    +
       93.      1    bad_directive_a, bad_get, bad_module_name_a, bad_option_a, bad_property_a,
    +
       94.      1    bad_set, bitwise, block, body, browser, c, calls, catch, cli_mode, closer,
    +
       95.      1    closure, code, column, concat, console_error, constant, context, convert,
    +
       96.      1    couch, create, d, dead, debug, default, devel, directive, directives,
    +
       97.      1    disrupt, dot, duplicate_a, early_stop, edition, ellipsis, else, empty_block,
    +
       98.      1    env, error, eval, every, exec, exit, expected_a, expected_a_at_b_c,
    +
       99.      1    expected_a_b, expected_a_b_from_c_d, expected_a_before_b,
    +
      100.      1    expected_a_next_at_b, expected_digits_after_a, expected_four_digits,
    +
      101.      1    expected_identifier_a, expected_line_break_a_b, expected_regexp_factor_a,
    +
      102.      1    expected_space_a_b, expected_statements_a, expected_string_a,
    +
      103.      1    expected_type_string_a, exports, expression, extra, file, finally, flag,
    +
      104.      1    for, forEach, formatted_message, free, freeze, freeze_exports, from, froms,
    +
      105.      1    fud, fudge, function_in_loop, functions, g, getset, global, has_await, i,
    +
      106.      1    id, identifier, import, inc, indexOf, infix_in, init, initial, isArray,
    +
      107.      1    isNaN, is_async, join, json, keys, label, label_a, lbp, led, length, level,
    +
      108.      1    line, line_offset, lines, live, long, loop, m, map, margin, match, message,
    +
      109.      1    misplaced_a, misplaced_directive_a, missing_await_statement,
    +
      110.      1    missing_browser, missing_m, module, naked_block, name, names,
    +
      111.      1    nested_comment, node, not_label_a, now, nr, nud, number_isNaN, ok, open,
    +
      112.      1    opening, option, out_of_scope_a, padStart, parameters, parent, pop,
    +
      113.      1    promises, property, push, quote, raw, readFile, readdir, redefinition_a_b,
    +
      114.      1    repeat, replace, required_a_optional_b, reserved_a, role, search, shebang,
    +
      115.      1    signature, single, slice, some, sort, source, split, stack, stack_trace,
    +
      116.      1    startsWith, statement, stop, subscript_a, switch, test, test_internal_error,
    +
      117.      1    then, this, thru, todo_comment, tokens, too_long, too_many_digits, tree,
    +
      118.      1    trim, try, type, u, unclosed_comment, unclosed_mega, unclosed_string,
    +
      119.      1    undeclared_a, unexpected_a, unexpected_a_after_b, unexpected_a_before_b,
    +
      120.      1    unexpected_at_top_level_a, unexpected_char_a, unexpected_comment,
    +
      121.      1    unexpected_directive_a, unexpected_expression_a, unexpected_label_a,
    +
      122.      1    unexpected_parens, unexpected_space_a_b, unexpected_statement_a,
    +
      123.      1    unexpected_trailing_space, unexpected_typeof_a, uninitialized_a,
    +
      124.      1    unordered_param_a, unordered_property_a, unreachable_a,
    +
      125.      1    unregistered_property_a, unused_a, use_double, use_open, use_spaces, used,
    +
      126.      1    value, var_loop, var_switch, variable, versions, warning, warnings,
    +
      127.      1    weird_condition_a, weird_expression_a, weird_loop, weird_relation_a, white,
    +
      128.      1    wrap_condition, wrap_immediate, wrap_parameter, wrap_regexp, wrap_unary,
    +
      129.      1    wrapped, writable, y
    +
      130.      1*/
    +
      131.      1
    +
      132.      1const edition = "v2021.5.28-beta";
    +
      133.      1
    +
      134.   8634function assert_or_throw(passed, message) {
    +
      135.   8634
    +
      136.   8634// this function will throw <message> if <passed> is falsy
    +
      137.   8634
    +
      138.      1    if (!passed) {
    +
      139.      1        throw new Error(`This was caused by a bug in JSLint.
    +
      140.      1Please open an issue with this stack-trace at
    +
      141.      1https://github.com/jslint-org/jslint/issues.
    +
      142.      1edition = "${edition}";` + "\n" + message);
    +
      143.      1    }
    +
      144.   8634}
    +
      145.      1
    +
      146.   3597function empty() {
    +
      147.   3597
    +
      148.   3597// The empty function produces a new empty object that inherits nothing. This is
    +
      149.   3597// much better than '{}' because confusions around accidental method names like
    +
      150.   3597// 'constructor' are completely avoided.
    +
      151.   3597
    +
      152.   3597    return Object.create(null);
    +
      153.   3597}
    +
      154.      1
    +
      155.    836function populate(array, object = empty(), value = true) {
    +
      156.    836
    +
      157.    836// Augment an object by taking property names from an array of strings.
    +
      158.    836
    +
      159.  19497    array.forEach(function (name) {
    +
      160.  19497        object[name] = value;
    +
      161.  19497    });
    +
      162.    836    return object;
    +
      163.    836}
    +
      164.      1
    +
      165.      1const allowed_option = {
    +
      166.      1
    +
      167.      1// These are the options that are recognized in the option object or that may
    +
      168.      1// appear in a /*jslint*/ directive. Most options will have a boolean value,
    +
      169.      1// usually true. Some options will also predefine some number of global
    +
      170.      1// variables.
    +
      171.      1
    +
      172.      1    bitwise: true,
    +
      173.      1    browser: [
    +
      174.      1        "caches", "CharacterData", "clearInterval", "clearTimeout", "document",
    +
      175.      1        "DocumentType", "DOMException", "Element", "Event", "event", "fetch",
    +
      176.      1        "FileReader", "FontFace", "FormData", "history", "IntersectionObserver",
    +
      177.      1        "localStorage", "location", "MutationObserver", "name", "navigator",
    +
      178.      1        "screen", "sessionStorage", "setInterval", "setTimeout", "Storage",
    +
      179.      1        "TextDecoder", "TextEncoder", "URL", "window", "Worker",
    +
      180.      1        "XMLHttpRequest"
    +
      181.      1    ],
    +
      182.      1    convert: true,
    +
      183.      1    couch: [
    +
      184.      1        "emit", "getRow", "isArray", "log", "provides", "registerType",
    +
      185.      1        "require", "send", "start", "sum", "toJSON"
    +
      186.      1    ],
    +
      187.      1    debug: true,
    +
      188.      1    devel: [
    +
      189.      1        "alert", "confirm", "console", "prompt"
    +
      190.      1    ],
    +
      191.      1    eval: true,
    +
      192.      1    for: true,
    +
      193.      1    fudge: true,
    +
      194.      1    getset: true,
    +
      195.      1    long: true,
    +
      196.      1    node: [
    +
      197.      1        "Buffer", "clearImmediate", "clearInterval", "clearTimeout",
    +
      198.      1        "console", "exports", "module", "process", "require",
    +
      199.      1        "setImmediate", "setInterval", "setTimeout", "TextDecoder",
    +
      200.      1        "TextEncoder", "URL", "URLSearchParams", "__dirname", "__filename"
    +
      201.      1    ],
    +
      202.      1    single: true,
    +
      203.      1    test_internal_error: true,
    +
      204.      1    this: true,
    +
      205.      1    unordered: true,
    +
      206.      1    white: true
    +
      207.      1};
    +
      208.      1
    +
      209.      1const anticondition = populate([
    +
      210.      1    "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%",
    +
      211.      1    "typeof", "(number)", "(string)"
    +
      212.      1]);
    +
      213.      1
    +
      214.      1// These are the bitwise operators.
    +
      215.      1
    +
      216.      1const bitwiseop = populate([
    +
      217.      1    "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=",
    +
      218.      1    ">>>", ">>>="
    +
      219.      1]);
    +
      220.      1
    +
      221.      1const escapeable = populate([
    +
      222.      1    "\\", "/", "`", "b", "f", "n", "r", "t"
    +
      223.      1]);
    +
      224.      1
    +
      225.      1const opener = {
    +
      226.      1
    +
      227.      1// The open and close pairs.
    +
      228.      1
    +
      229.      1    "${": "}",      // mega
    +
      230.      1    "(": ")",       // paren
    +
      231.      1    "[": "]",       // bracket
    +
      232.      1    "{": "}"        // brace
    +
      233.      1};
    +
      234.      1
    +
      235.      1// The relational operators.
    +
      236.      1
    +
      237.      1const relationop = populate([
    +
      238.      1    "!=", "!==", "==", "===", "<", "<=", ">", ">="
    +
      239.      1]);
    +
      240.      1
    +
      241.      1// This is the set of infix operators that require a space on each side.
    +
      242.      1
    +
      243.      1const spaceop = populate([
    +
      244.      1    "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/",
    +
      245.      1    "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=",
    +
      246.      1    ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||"
    +
      247.      1]);
    +
      248.      1
    +
      249.      1const standard = [
    +
      250.      1
    +
      251.      1// These are the globals that are provided by the language standard.
    +
      252.      1
    +
      253.      1    "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "Error", "EvalError",
    +
      254.      1    "Float32Array", "Float64Array", "Generator", "GeneratorFunction",
    +
      255.      1    "Int16Array", "Int32Array", "Int8Array", "Intl", "JSON", "Map", "Math",
    +
      256.      1    "Number", "Object", "Promise", "Proxy", "RangeError", "ReferenceError",
    +
      257.      1    "Reflect", "RegExp", "Set", "String", "Symbol", "SyntaxError", "System",
    +
      258.      1    "TypeError", "URIError", "Uint16Array", "Uint32Array", "Uint8Array",
    +
      259.      1    "Uint8ClampedArray", "WeakMap", "WeakSet", "decodeURI",
    +
      260.      1    "decodeURIComponent", "encodeURI", "encodeURIComponent", "globalThis",
    +
      261.      1    "import", "parseFloat", "parseInt"
    +
      262.      1];
    +
      263.      1
    +
      264.      1const bundle = {
    +
      265.      1
    +
      266.      1// The bundle contains the raw text messages that are generated by jslint. It
    +
      267.      1// seems that they are all error messages and warnings. There are no "Atta
    +
      268.      1// boy!" or "You are so awesome!" messages. There is no positive reinforcement
    +
      269.      1// or encouragement. This relentless negativity can undermine self-esteem and
    +
      270.      1// wound the inner child. But if you accept it as sound advice rather than as
    +
      271.      1// personal criticism, it can make your programs better.
    +
      272.      1
    +
      273.      1    and: "The '&&' subexpression should be wrapped in parens.",
    +
      274.      1    bad_assignment_a: "Bad assignment to '{a}'.",
    +
      275.      1    bad_directive_a: "Bad directive '{a}'.",
    +
      276.      1    bad_get: "A get function takes no parameters.",
    +
      277.      1    bad_module_name_a: "Bad module name '{a}'.",
    +
      278.      1    bad_option_a: "Bad option '{a}'.",
    +
      279.      1    bad_property_a: "Bad property name '{a}'.",
    +
      280.      1    bad_set: "A set function takes one parameter.",
    +
      281.      1    duplicate_a: "Duplicate '{a}'.",
    +
      282.      1    empty_block: "Empty block.",
    +
      283.      1    expected_a: "Expected '{a}'.",
    +
      284.      1    expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.",
    +
      285.      1    expected_a_b: "Expected '{a}' and instead saw '{b}'.",
    +
      286.      1    expected_a_b_from_c_d: (
    +
      287.      1        "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'."
    +
      288.      1    ),
    +
      289.      1    expected_a_before_b: "Expected '{a}' before '{b}'.",
    +
      290.      1    expected_digits_after_a: "Expected digits after '{a}'.",
    +
      291.      1    expected_four_digits: "Expected four digits after '\\u'.",
    +
      292.      1    expected_identifier_a: "Expected an identifier and instead saw '{a}'.",
    +
      293.      1    expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.",
    +
      294.      1    expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.",
    +
      295.      1    expected_space_a_b: "Expected one space between '{a}' and '{b}'.",
    +
      296.      1    expected_statements_a: "Expected statements before '{a}'.",
    +
      297.      1    expected_string_a: "Expected a string and instead saw '{a}'.",
    +
      298.      1    expected_type_string_a: "Expected a type string and instead saw '{a}'.",
    +
      299.      1    freeze_exports: (
    +
      300.      1        "Expected 'Object.freeze('. All export values should be frozen."
    +
      301.      1    ),
    +
      302.      1    function_in_loop: "Don't make functions within a loop.",
    +
      303.      1    infix_in: (
    +
      304.      1        "Unexpected 'in'. Compare with undefined, "
    +
      305.      1        + "or use the hasOwnProperty method instead."
    +
      306.      1    ),
    +
      307.      1    label_a: "'{a}' is a statement label.",
    +
      308.      1    misplaced_a: "Place '{a}' at the outermost level.",
    +
      309.      1    misplaced_directive_a: (
    +
      310.      1        "Place the '/*{a}*/' directive before the first statement."
    +
      311.      1    ),
    +
      312.      1    missing_await_statement: "Expected await statement in async function.",
    +
      313.      1    missing_browser: "/*global*/ requires the Assume a browser option.",
    +
      314.      1    missing_m: "Expected 'm' flag on a multiline regular expression.",
    +
      315.      1    naked_block: "Naked block.",
    +
      316.      1    nested_comment: "Nested comment.",
    +
      317.      1    not_label_a: "'{a}' is not a label.",
    +
      318.      1    number_isNaN: "Use Number.isNaN function to compare with NaN.",
    +
      319.      1    out_of_scope_a: "'{a}' is out of scope.",
    +
      320.      1    redefinition_a_b: "Redefinition of '{a}' from line {b}.",
    +
      321.      1    required_a_optional_b: (
    +
      322.      1        "Required parameter '{a}' after optional parameter '{b}'."
    +
      323.      1    ),
    +
      324.      1    reserved_a: "Reserved name '{a}'.",
    +
      325.      1    subscript_a: "['{a}'] is better written in dot notation.",
    +
      326.      1    todo_comment: "Unexpected TODO comment.",
    +
      327.      1    too_long: "Line is longer than 80 characters.",
    +
      328.      1    too_many_digits: "Too many digits.",
    +
      329.      1    unclosed_comment: "Unclosed comment.",
    +
      330.      1    unclosed_mega: "Unclosed mega literal.",
    +
      331.      1    unclosed_string: "Unclosed string.",
    +
      332.      1    undeclared_a: "Undeclared '{a}'.",
    +
      333.      1    unexpected_a: "Unexpected '{a}'.",
    +
      334.      1    unexpected_a_after_b: "Unexpected '{a}' after '{b}'.",
    +
      335.      1    unexpected_a_before_b: "Unexpected '{a}' before '{b}'.",
    +
      336.      1    unexpected_at_top_level_a: "Expected '{a}' to be in a function.",
    +
      337.      1    unexpected_char_a: "Unexpected character '{a}'.",
    +
      338.      1    unexpected_comment: "Unexpected comment.",
    +
      339.      1    unexpected_directive_a: "When using modules, don't use directive '/*{a}'.",
    +
      340.      1    unexpected_expression_a: (
    +
      341.      1        "Unexpected expression '{a}' in statement position."
    +
      342.      1    ),
    +
      343.      1    unexpected_label_a: "Unexpected label '{a}'.",
    +
      344.      1    unexpected_parens: "Don't wrap function literals in parens.",
    +
      345.      1    unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.",
    +
      346.      1    unexpected_statement_a: (
    +
      347.      1        "Unexpected statement '{a}' in expression position."
    +
      348.      1    ),
    +
      349.      1    unexpected_trailing_space: "Unexpected trailing space.",
    +
      350.      1    unexpected_typeof_a: (
    +
      351.      1        "Unexpected 'typeof'. Use '===' to compare directly with {a}."
    +
      352.      1    ),
    +
      353.      1    uninitialized_a: "Uninitialized '{a}'.",
    +
      354.      1    unordered_param_a: (
    +
      355.      1        "Parameter '{a}' not listed in alphabetical order."
    +
      356.      1    ),
    +
      357.      1    unordered_property_a: (
    +
      358.      1        "Property name '{a}' not listed in alphabetical order."
    +
      359.      1    ),
    +
      360.      1    unreachable_a: "Unreachable '{a}'.",
    +
      361.      1    unregistered_property_a: "Unregistered property name '{a}'.",
    +
      362.      1    unused_a: "Unused '{a}'.",
    +
      363.      1    use_double: "Use double quotes, not single quotes.",
    +
      364.      1    use_open: (
    +
      365.      1        "Wrap a ternary expression in parens, "
    +
      366.      1        + "with a line break after the left paren."
    +
      367.      1    ),
    +
      368.      1    use_spaces: "Use spaces, not tabs.",
    +
      369.      1    var_loop: "Don't declare variables in a loop.",
    +
      370.      1    var_switch: "Don't declare variables in a switch.",
    +
      371.      1    weird_condition_a: "Weird condition '{a}'.",
    +
      372.      1    weird_expression_a: "Weird expression '{a}'.",
    +
      373.      1    weird_loop: "Weird loop.",
    +
      374.      1    weird_relation_a: "Weird relation '{a}'.",
    +
      375.      1    wrap_condition: "Wrap the condition in parens.",
    +
      376.      1    wrap_immediate: (
    +
      377.      1        "Wrap an immediate function invocation in parentheses to assist "
    +
      378.      1        + "the reader in understanding that the expression is the result "
    +
      379.      1        + "of a function, and not the function itself."
    +
      380.      1    ),
    +
      381.      1    wrap_parameter: "Wrap the parameter in parens.",
    +
      382.      1    wrap_regexp: "Wrap this regexp in parens to avoid confusion.",
    +
      383.      1    wrap_unary: "Wrap the unary expression in parens."
    +
      384.      1};
    +
      385.      1
    +
      386.      1// Regular expression literals:
    +
      387.      1
    +
      388.     12function tag_regexp(strings) {
    +
      389.     12    return new RegExp(strings.raw[0].replace(/\s/g, ""));
    +
      390.     12}
    +
      391.      1
    +
      392.      1// supplant {variables}
    +
      393.      1const rx_supplant = /\{([^{}]*)\}/g;
    +
      394.      1// carriage return, carriage return linefeed, or linefeed
    +
      395.      1const rx_crlf = tag_regexp `
    +
      396.      1      \n
    +
      397.      1    | \r \n?
    +
      398.      1`;
    +
      399.      1// identifier
    +
      400.      1const rx_identifier = tag_regexp ` ^(
    +
      401.      1    [ a-z A-Z _ $ ]
    +
      402.      1    [ a-z A-Z 0-9 _ $ ]*
    +
      403.      1)$`;
    +
      404.      1const rx_module = tag_regexp ` ^ [ a-z A-Z 0-9 _ $ : . @ \- \/ ]+ $ `;
    +
      405.      1const rx_bad_property = tag_regexp `
    +
      406.      1    ^_
    +
      407.      1  | \$
    +
      408.      1  | Sync $
    +
      409.      1  | _ $
    +
      410.      1`;
    +
      411.      1// star slash
    +
      412.      1const rx_star_slash = tag_regexp ` \* \/ `;
    +
      413.      1// slash star
    +
      414.      1const rx_slash_star = tag_regexp ` \/ \* `;
    +
      415.      1// slash star or ending slash
    +
      416.      1const rx_slash_star_or_slash = tag_regexp ` \/ \* | \/ $ `;
    +
      417.      1// uncompleted work comment
    +
      418.      1const rx_todo = tag_regexp ` \b (?:
    +
      419.      1    todo
    +
      420.      1  | TO \s? DO
    +
      421.      1  | HACK
    +
      422.      1) \b `;
    +
      423.      1// tab
    +
      424.      1const rx_tab = /\t/g;
    +
      425.      1// directive
    +
      426.      1const rx_directive = tag_regexp ` ^ (
    +
      427.      1    jslint
    +
      428.      1  | property
    +
      429.      1  | global
    +
      430.      1) \s+ ( .* ) $ `;
    +
      431.      1const rx_directive_part = tag_regexp ` ^ (
    +
      432.      1    [ a-z A-Z $ _ ] [ a-z A-Z 0-9 $ _ ]*
    +
      433.      1) (?:
    +
      434.      1    : \s* ( true | false )
    +
      435.      1)? ,? \s* ( .* ) $ `;
    +
      436.      1// token
    +
      437.      1const rx_token = tag_regexp ` ^ (
    +
      438.      1    (\s+)
    +
      439.      1  | (
    +
      440.      1      [ a-z A-Z _ $ ]
    +
      441.      1      [ a-z A-Z 0-9 _ $ ]*
    +
      442.      1    )
    +
      443.      1  | [
    +
      444.      1      ( ) { } \[ \] , : ; ' " ~ \`
    +
      445.      1  ]
    +
      446.      1  | \? [ ? . ]?
    +
      447.      1  | = (?:
    +
      448.      1        = =?
    +
      449.      1      | >
    +
      450.      1    )?
    +
      451.      1  | \.+
    +
      452.      1  | \* [ * \/ = ]?
    +
      453.      1  | \/ [ * \/ ]?
    +
      454.      1  | \+ [ = + ]?
    +
      455.      1  | - [ = \- ]?
    +
      456.      1  | [ \^ % ] =?
    +
      457.      1  | & [ & = ]?
    +
      458.      1  | \| [ | = ]?
    +
      459.      1  | >{1,3} =?
    +
      460.      1  | < <? =?
    +
      461.      1  | ! (?:
    +
      462.      1        !
    +
      463.      1      | = =?
    +
      464.      1    )?
    +
      465.      1  | (
    +
      466.      1        0
    +
      467.      1      | [ 1-9 ] [ 0-9 ]*
    +
      468.      1    )
    +
      469.      1) ( .* ) $ `;
    +
      470.      1const rx_digits = /^[0-9]*/;
    +
      471.      1const rx_hexs = /^[0-9A-F]*/i;
    +
      472.      1const rx_octals = /^[0-7]*/;
    +
      473.      1const rx_bits = /^[01]*/;
    +
      474.      1// mega
    +
      475.      1const rx_mega = /[`\\]|\$\{/;
    +
      476.      1// JSON number
    +
      477.      1const rx_JSON_number = tag_regexp ` ^
    +
      478.      1    -?
    +
      479.      1    (?: 0 | [ 1-9 ] \d* )
    +
      480.      1    (?: \. \d* )?
    +
      481.      1    (?: [ e E ] [ \- + ]? \d+ )?
    +
      482.      1$ `;
    +
      483.      1// initial cap
    +
      484.      1const rx_cap = /^[A-Z]/;
    +
      485.      1
    +
      486.    167function is_letter(string) {
    +
      487.    167    return (
    +
      488.     77        (string >= "a" && string <= "z\uffff")
    +
      489.     93        || (string >= "A" && string <= "Z\uffff")
    +
      490.    167    );
    +
      491.    167}
    +
      492.      1
    +
      493.      1let anon;               // The guessed name for anonymous functions.
    +
      494.      1let blockage;           // The current block.
    +
      495.      1let block_stack;        // The stack of blocks.
    +
      496.      1let declared_globals;   // The object containing the global declarations.
    +
      497.      1let directives;         // The directive comments.
    +
      498.      1let directive_mode;     // true if directives are still allowed.
    +
      499.      1let early_stop;         // true if JSLint cannot finish.
    +
      500.      1let exports;            // The exported names and values.
    +
      501.      1let froms;              // The array collecting all import-from strings.
    +
      502.      1let fudge;              // true if the natural numbers start with 1.
    +
      503.      1let functionage;        // The current function.
    +
      504.      1let functions;          // The array containing all of the functions.
    +
      505.      1let global;             // The global object; the outermost context.
    +
      506.      1let json_mode;          // true if parsing JSON.
    +
      507.      1let lines;              // The array containing source lines.
    +
      508.      1let mega_mode;          // true if currently parsing a megastring literal.
    +
      509.      1let module_mode;        // true if import or export was used.
    +
      510.      1let next_token;         // The next token to be examined in the parse.
    +
      511.      1let option;             // The options parameter.
    +
      512.      1let property;           // The object containing the tallied property names.
    +
      513.      1let shebang;            // true if a #! was seen on the first line.
    +
      514.      1let stack;              // The stack of functions.
    +
      515.      1let syntax;             // The object containing the parser.
    +
      516.      1let token;              // The current token being examined in the parse.
    +
      517.      1let token_nr;           // The number of the next token.
    +
      518.      1let tokens;             // The array of tokens.
    +
      519.      1let tenure;             // The predefined property registry.
    +
      520.      1let tree;               // The abstract parse tree.
    +
      521.      1let var_mode;           // "var" if using var; "let" if using let.
    +
      522.      1let warnings;           // The array collecting all generated warnings.
    +
      523.      1
    +
      524.      1// Error reportage functions:
    +
      525.      1
    +
      526.    826function artifact(the_token) {
    +
      527.    826
    +
      528.    826// Return a string representing an artifact.
    +
      529.    826
    +
      530.     20    if (the_token === undefined) {
    +
      531.     20        the_token = next_token;
    +
      532.     20    }
    +
      533.    826    return (
    +
      534.    819        (the_token.id === "(string)" || the_token.id === "(number)")
    +
      535.     87        ? String(the_token.value)
    +
      536.    739        : the_token.id
    +
      537.    826    );
    +
      538.    826}
    +
      539.      1
    +
      540.    879function warn_at(code, line, column, a, b, c, d) {
    +
      541.    879
    +
      542.    879// Report an error at some line and column of the program. The warning object
    +
      543.    879// resembles an exception.
    +
      544.    879
    +
      545.    879    const warning = {         // ~~
    +
      546.    879        code,
    +
      547.    879        column,
    +
      548.    879        line,
    +
      549.    879        name: "JSLintError"
    +
      550.    879    };
    +
      551.    862    if (a !== undefined) {
    +
      552.    862        warning.a = a;
    +
      553.    862    }
    +
      554.    315    if (b !== undefined) {
    +
      555.    315        warning.b = b;
    +
      556.    315    }
    +
      557.     27    if (c !== undefined) {
    +
      558.     27        warning.c = c;
    +
      559.     27    }
    +
      560.      3    if (d !== undefined) {
    +
      561.      3        warning.d = d;
    +
      562.      3    }
    +
      563.   1115    warning.message = bundle[code].replace(rx_supplant, function (
    +
      564.   1115        ignore,
    +
      565.   1115        filling
    +
      566.   1115    ) {
    +
      567.   1115        assert_or_throw(
    +
      568.   1115            warning[filling] !== undefined,
    +
      569.   1115            "Expected warning[filling] !== undefined."
    +
      570.   1115        );
    +
      571.   1115        return warning[filling];
    +
      572.   1115    });
    +
      573.    879
    +
      574.    879// Include stack_trace for jslint to debug itself for errors.
    +
      575.    879
    +
      576.      4    if (option.debug) {
    +
      577.      4        warning.stack_trace = new Error().stack;
    +
      578.      4    }
    +
      579.    879    warnings.push(warning);
    +
      580.    879    return warning;
    +
      581.    879}
    +
      582.      1
    +
      583.     17function stop_at(code, line, column, a, b, c, d) {
    +
      584.     17
    +
      585.     17// Same as warn_at, except that it stops the analysis.
    +
      586.     17
    +
      587.     17    throw warn_at(code, line, column, a, b, c, d);
    +
      588.     17}
    +
      589.      1
    +
      590.    809function warn(code, the_token, a, b, c, d) {
    +
      591.    809
    +
      592.    809// Same as warn_at, except the warning will be associated with a specific token.
    +
      593.    809// If there is already a warning on this token, suppress the new one. It is
    +
      594.    809// likely that the first warning will be the most meaningful.
    +
      595.    809
    +
      596.     12    if (the_token === undefined) {
    +
      597.     12        the_token = next_token;
    +
      598.     12    }
    +
      599.    656    if (the_token.warning === undefined) {
    +
      600.    656        the_token.warning = warn_at(
    +
      601.    656            code,
    +
      602.    656            the_token.line,
    +
      603.    656            the_token.from,
    +
      604.    656            a || artifact(the_token),
    +
      605.    656            b,
    +
      606.    656            c,
    +
      607.    656            d
    +
      608.    656        );
    +
      609.    656        return the_token.warning;
    +
      610.    656    }
    +
      611.    809}
    +
      612.      1
    +
      613.     91function stop(code, the_token, a, b, c, d) {
    +
      614.     91
    +
      615.     91// Similar to warn and stop_at. If the token already had a warning, that
    +
      616.     91// warning will be replaced with this new one. It is likely that the stopping
    +
      617.     91// warning will be the more meaningful.
    +
      618.     91
    +
      619.     16    if (the_token === undefined) {
    +
      620.     16        the_token = next_token;
    +
      621.     16    }
    +
      622.     91    delete the_token.warning;
    +
      623.     91    throw warn(code, the_token, a, b, c, d);
    +
      624.     91}
    +
      625.      1
    +
      626.      1// Tokenize:
    +
      627.      1
    +
      628.    405function tokenize(source) {
    +
      629.    405
    +
      630.    405// tokenize takes a source and produces from it an array of token objects.
    +
      631.    405// JavaScript is notoriously difficult to tokenize because of the horrible
    +
      632.    405// interactions between automatic semicolon insertion, regular expression
    +
      633.    405// literals, and now megastring literals. JSLint benefits from eliminating
    +
      634.    405// automatic semicolon insertion and nested megastring literals, which allows
    +
      635.    405// full tokenization to precede parsing.
    +
      636.    405
    +
      637.    405// If the source is not an array, then it is split into lines at the
    +
      638.    405// carriage return/linefeed.
    +
      639.    405
    +
      640.    405    lines = (
    +
      641.    405        Array.isArray(source)
    +
      642.      1        ? source
    +
      643.    404        : source.split(rx_crlf)
    +
      644.    405    );
    +
      645.    405    tokens = [];
    +
      646.    405
    +
      647.    405    let char;                   // a popular character
    +
      648.    405    let column = 0;             // the column number of the next character
    +
      649.    405    let first;                  // the first token
    +
      650.    405    let from;                   // the starting column number of the token
    +
      651.    405    let line = -1;              // the line number of the next character
    +
      652.    405    let nr = 0;                 // the next token number
    +
      653.    405    let previous = global;      // the previous token including comments
    +
      654.    405    let prior = global;         // the previous token excluding comments
    +
      655.    405    let mega_from;              // the starting column of megastring
    +
      656.    405    let mega_line;              // the starting line of megastring
    +
      657.    405    let regexp_seen;            // regular expression literal seen on this line
    +
      658.    405    let snippet = "";           // a piece of string
    +
      659.    405    let source_line = "";       // the remaining line source string
    +
      660.    405    let whole_line = "";        // the whole line source string
    +
      661.    405
    +
      662.      3    if (lines[0].startsWith("#!")) {
    +
      663.      3        line = 0;
    +
      664.      3        shebang = true;
    +
      665.      3    }
    +
      666.    405
    +
      667.  16909    function next_line() {
    +
      668.  16909
    +
      669.  16909// Put the next line of source in source_line. If the line contains tabs,
    +
      670.  16909// replace them with spaces and give a warning. Also warn if the line contains
    +
      671.  16909// unsafe characters or is too damn long.
    +
      672.  16909
    +
      673.  16909        let at;
    +
      674.  16909        if (
    +
      675.  16909            !option.long
    +
      676.  16906            && whole_line.length > 80
    +
      677.      1            && !json_mode
    +
      678.      1            && first
    +
      679.      1            && !regexp_seen
    +
      680.      1        ) {
    +
      681.      1            warn_at("too_long", line, 80);
    +
      682.      1        }
    +
      683.  16909        column = 0;
    +
      684.  16909        line += 1;
    +
      685.  16909        regexp_seen = false;
    +
      686.  16909        source_line = lines[line];
    +
      687.   4363        whole_line = source_line || "";
    +
      688.  16522        if (source_line !== undefined) {
    +
      689.  16522            at = source_line.search(rx_tab);
    +
      690.  16522            if (at >= 0) {
    +
      691.  16522                if (!option.white) {
    +
      692.  16522
    +
      693.  16522// cause: "\t"
    +
      694.  16522
    +
      695.  16522                    warn_at("use_spaces", line, at + 1);
    +
      696.  16522                }
    +
      697.  16522                source_line = source_line.replace(rx_tab, " ");
    +
      698.  16522            }
    +
      699.  16522            if (!option.white && source_line.slice(-1) === " ") {
    +
      700.  16522                warn_at(
    +
      701.  16522                    "unexpected_trailing_space",
    +
      702.  16522                    line,
    +
      703.  16522                    source_line.length - 1
    +
      704.  16522                );
    +
      705.  16522            }
    +
      706.  16522        }
    +
      707.  16909        return source_line;
    +
      708.  16909    }
    +
      709.    405
    +
      710.    405// Most tokens, including the identifiers, operators, and punctuators, can be
    +
      711.    405// found with a regular expression. Regular expressions cannot correctly match
    +
      712.    405// regular expression literals, so we will match those the hard way. String
    +
      713.    405// literals and number literals can be matched by regular expressions, but they
    +
      714.    405// don't provide good warnings. The functions snip, next_char, back_char,
    +
      715.    405// some_digits, and escape help in the parsing of literals.
    +
      716.    405
    +
      717.   5112    function snip() {
    +
      718.   5112
    +
      719.   5112// Remove the last character from snippet.
    +
      720.   5112
    +
      721.   5112        snippet = snippet.slice(0, -1);
    +
      722.   5112    }
    +
      723.    405
    +
      724.  36155    function next_char(match) {
    +
      725.  36155
    +
      726.  36155// Get the next character from the source line. Remove it from the source_line,
    +
      727.  36155// and append it to the snippet. Optionally check that the previous character
    +
      728.  36155// matched an expected value.
    +
      729.  36155
    +
      730.    773        if (match !== undefined && char !== match) {
    +
      731.      3            return stop_at(
    +
      732.      3                (
    +
      733.      3                    char === ""
    +
      734.      3                    ? "expected_a"
    +
      735.      3                    : "expected_a_b"
    +
      736.      3                ),
    +
      737.      3                line,
    +
      738.      3                column - 1,
    +
      739.      3                match,
    +
      740.      3                char
    +
      741.      3            );
    +
      742.  36152        }
    +
      743.  36152        if (source_line) {
    +
      744.  35995            char = source_line[0];
    +
      745.  35995            source_line = source_line.slice(1);
    +
      746.  35995            snippet += char;
    +
      747.  35995        } else {
    +
      748.    157            char = "";
    +
      749.    157            snippet += " ";
    +
      750.  36152        }
    +
      751.  36152        column += 1;
    +
      752.  36152        return char;
    +
      753.  36152    }
    +
      754.    405
    +
      755.   1189    function back_char() {
    +
      756.   1189
    +
      757.   1189// Back up one character by moving a character from the end of the snippet to
    +
      758.   1189// the front of the source_line.
    +
      759.   1189
    +
      760.   1189        char = snippet.slice(-1);
    +
      761.   1189        source_line = char + source_line;
    +
      762.   1189        column -= char.length;
    +
      763.   1189        snip();
    +
      764.   1189        return char;
    +
      765.   1189    }
    +
      766.    405
    +
      767.     53    function some_digits(rx, quiet) {
    +
      768.     53        const digits = source_line.match(rx)[0];
    +
      769.     53        const length = digits.length;
    +
      770.     17        if (!quiet && length === 0) {
    +
      771.      1
    +
      772.      1// cause: "0x"
    +
      773.      1
    +
      774.      1            warn_at("expected_digits_after_a", line, column, snippet);
    +
      775.      1        }
    +
      776.     53        column += length;
    +
      777.     53        source_line = source_line.slice(length);
    +
      778.     53        snippet += digits;
    +
      779.     53        next_char();
    +
      780.     53        return length;
    +
      781.     53    }
    +
      782.    405
    +
      783.    403    function escape(extra) {
    +
      784.    403        next_char("\\");
    +
      785.    226        if (escapeable[char] === true) {
    +
      786.    226            return next_char();
    +
      787.    226        }
    +
      788.    177        if (char === "") {
    +
      789.      1
    +
      790.      1// cause: "\"\\"
    +
      791.      1
    +
      792.      1            return stop_at("unclosed_string", line, column);
    +
      793.    176        }
    +
      794.    176        if (char === "u") {
    +
      795.     36            if (next_char("u") === "{") {
    +
      796.     36                if (json_mode) {
    +
      797.     36
    +
      798.     36// cause: "[\"\\u{12345}\"]"
    +
      799.     36
    +
      800.     36                    warn_at("unexpected_a", line, column - 1, char);
    +
      801.     36                }
    +
      802.     36                if (some_digits(rx_hexs) > 5) {
    +
      803.     36
    +
      804.     36// cause: "\"\\u{123456}\""
    +
      805.     36
    +
      806.     36                    warn_at("too_many_digits", line, column - 1);
    +
      807.     36                }
    +
      808.     36                if (char !== "}") {
    +
      809.     36
    +
      810.     36// cause: "\"\\u{12345\""
    +
      811.     36
    +
      812.     36                    stop_at("expected_a_before_b", line, column, "}", char);
    +
      813.     36                }
    +
      814.     36                return next_char();
    +
      815.     36            }
    +
      816.     36            back_char();
    +
      817.     36            if (some_digits(rx_hexs, true) < 4) {
    +
      818.     36
    +
      819.     36// cause: "\"\\u0\""
    +
      820.     36
    +
      821.     36                warn_at("expected_four_digits", line, column - 1);
    +
      822.     36            }
    +
      823.     36            return;
    +
      824.    140        }
    +
      825.    140        if (extra && extra.indexOf(char) >= 0) {
    +
      826.    139            return next_char();
    +
      827.    139        }
    +
      828.      1
    +
      829.      1// cause: "\"\\a\""
    +
      830.      1
    +
      831.      1        warn_at("unexpected_a_before_b", line, column - 2, "\\", char);
    +
      832.      1    }
    +
      833.    405
    +
      834.  57310    function make(id, value, identifier) {
    +
      835.  57310
    +
      836.  57310// Make the token object and append it to the tokens list.
    +
      837.  57310
    +
      838.  57310        const the_token = {
    +
      839.  57310            from,
    +
      840.  57310            id,
    +
      841.  57310            identifier: Boolean(identifier),
    +
      842.  57310            line,
    +
      843.  57310            nr,
    +
      844.  57310            thru: column
    +
      845.  57310        };
    +
      846.  57310        tokens[nr] = the_token;
    +
      847.  57310        nr += 1;
    +
      848.  57310
    +
      849.  57310// Directives must appear before the first statement.
    +
      850.  57310
    +
      851.  55648        if (id !== "(comment)" && id !== ";") {
    +
      852.  51329            directive_mode = false;
    +
      853.  51329        }
    +
      854.  57310
    +
      855.  57310// If the token is to have a value, give it one.
    +
      856.  57310
    +
      857.   6743        if (value !== undefined) {
    +
      858.   6743            the_token.value = value;
    +
      859.   6743        }
    +
      860.  57310
    +
      861.  57310// If this token is an identifier that touches a preceding number, or
    +
      862.  57310// a "/", comment, or regular expression literal that touches a preceding
    +
      863.  57310// comment or regular expression literal, then give a missing space warning.
    +
      864.  57310// This warning is not suppressed by option.white.
    +
      865.  57310
    +
      866.  57310        if (
    +
      867.  57310            previous.line === line
    +
      868.  45169            && previous.thru === from
    +
      869.  28817            && (id === "(comment)" || id === "(regexp)" || id === "/")
    +
      870.     53            && (previous.id === "(comment)" || previous.id === "(regexp)")
    +
      871.      1        ) {
    +
      872.      1
    +
      873.      1// cause: "/**//**/"
    +
      874.      1
    +
      875.      1            warn(
    +
      876.      1                "expected_space_a_b",
    +
      877.      1                the_token,
    +
      878.      1                artifact(previous),
    +
      879.      1                artifact(the_token)
    +
      880.      1            );
    +
      881.      1        }
    +
      882.   3334        if (previous.id === "." && id === "(number)") {
    +
      883.      2
    +
      884.      2// cause: ".0"
    +
      885.      2
    +
      886.      2            warn("expected_a_before_b", previous, "0", ".");
    +
      887.      2        }
    +
      888.   3334        if (prior.id === "." && the_token.identifier) {
    +
      889.   3330            the_token.dot = true;
    +
      890.   3330        }
    +
      891.  57310
    +
      892.  57310// The previous token is used to detect adjacency problems.
    +
      893.  57310
    +
      894.  57310        previous = the_token;
    +
      895.  57310
    +
      896.  57310// The prior token is a previous token that was not a comment. The prior token
    +
      897.  57310// is used to disambiguate "/", which can mean division or regular expression
    +
      898.  57310// literal.
    +
      899.  57310
    +
      900.  55648        if (previous.id !== "(comment)") {
    +
      901.  55648            prior = previous;
    +
      902.  55648        }
    +
      903.  57310        return the_token;
    +
      904.  57310    }
    +
      905.    405
    +
      906.    603    function parse_directive(the_comment, body) {
    +
      907.    603
    +
      908.    603// JSLint recognizes three directives that can be encoded in comments. This
    +
      909.    603// function processes one item, and calls itself recursively to process the
    +
      910.    603// next one.
    +
      911.    603
    +
      912.    603        const result = body.match(rx_directive_part);
    +
      913.    570        if (result) {
    +
      914.    570            let allowed;
    +
      915.    570            const name = result[1];
    +
      916.    570            const value = result[2];
    +
      917.    570            if (the_comment.directive === "jslint") {
    +
      918.    570                allowed = allowed_option[name];
    +
      919.    570                if (
    +
      920.    570                    typeof allowed === "boolean"
    +
      921.    570                    || typeof allowed === "object"
    +
      922.    570                ) {
    +
      923.    570                    if (value === "true" || value === undefined) {
    +
      924.    570                        option[name] = true;
    +
      925.    570                        if (Array.isArray(allowed)) {
    +
      926.    570                            populate(allowed, declared_globals, false);
    +
      927.    570                        }
    +
      928.    570                    } else if (value === "false") {
    +
      929.    570                        option[name] = false;
    +
      930.    570                    }
    +
      931.    570                } else {
    +
      932.    570
    +
      933.    570// cause: "/*jslint undefined*/"
    +
      934.    570
    +
      935.    570                    warn("bad_option_a", the_comment, name);
    +
      936.    570                }
    +
      937.    570            } else if (the_comment.directive === "property") {
    +
      938.    570                if (tenure === undefined) {
    +
      939.    570                    tenure = empty();
    +
      940.    570                }
    +
      941.    570                tenure[name] = true;
    +
      942.    570            } else if (the_comment.directive === "global") {
    +
      943.    570                if (value) {
    +
      944.    570
    +
      945.    570// cause: "/*global aa:false*/"
    +
      946.    570
    +
      947.    570                    warn("bad_option_a", the_comment, name + ":" + value);
    +
      948.    570                }
    +
      949.    570                declared_globals[name] = false;
    +
      950.    570                module_mode = the_comment;
    +
      951.    570            }
    +
      952.    570            return parse_directive(the_comment, result[3]);
    +
      953.    570        }
    +
      954.     33        if (body) {
    +
      955.      1
    +
      956.      1// cause: "/*jslint !*/"
    +
      957.      1
    +
      958.      1            return stop("bad_directive_a", the_comment, body);
    +
      959.      1        }
    +
      960.    603    }
    +
      961.    405
    +
      962.   1662    function comment(snippet) {
    +
      963.   1662
    +
      964.   1662// Make a comment object. Comments are not allowed in JSON text. Comments can
    +
      965.   1662// include directives and notices of incompletion.
    +
      966.   1662
    +
      967.   1662        const the_comment = make("(comment)", snippet);
    +
      968.     51        if (Array.isArray(snippet)) {
    +
      969.     51            snippet = snippet.join(" ");
    +
      970.     51        }
    +
      971.   1657        if (!option.devel && rx_todo.test(snippet)) {
    +
      972.      1
    +
      973.      1// cause: "//\u0074odo"
    +
      974.      1
    +
      975.      1            warn("todo_comment", the_comment);
    +
      976.      1        }
    +
      977.   1662        const result = snippet.match(rx_directive);
    +
      978.     34        if (result) {
    +
      979.     34            if (!directive_mode) {
    +
      980.     34
    +
      981.     34// cause: "0\n/*global aa*/"
    +
      982.     34
    +
      983.     34                warn_at("misplaced_directive_a", line, from, result[1]);
    +
      984.     34            } else {
    +
      985.     34                the_comment.directive = result[1];
    +
      986.     34                parse_directive(the_comment, result[2]);
    +
      987.     34            }
    +
      988.     34            directives.push(the_comment);
    +
      989.   1661        }
    +
      990.   1661        return the_comment;
    +
      991.   1661    }
    +
      992.    405
    +
      993.     97    function regexp() {
    +
      994.     97
    +
      995.     97// Parse a regular expression literal.
    +
      996.     97
    +
      997.     97        let multi_mode = false;
    +
      998.     97        let result;
    +
      999.     97        let value;
    +
     1000.     97        regexp_seen = true;
    +
     1001.     97
    +
     1002.    546        function quantifier() {
    +
     1003.    546
    +
     1004.    546// Match an optional quantifier.
    +
     1005.    546
    +
     1006.    540            if (char === "?" || char === "*" || char === "+") {
    +
     1007.     46                next_char();
    +
     1008.    500            } else if (char === "{") {
    +
     1009.    500                if (some_digits(rx_digits, true) === 0) {
    +
     1010.    500                    warn_at("expected_a_before_b", line, column, "0", ",");
    +
     1011.    500                }
    +
     1012.    500                if (char === ",") {
    +
     1013.    500                    some_digits(rx_digits, true);
    +
     1014.    500                }
    +
     1015.    500                next_char("}");
    +
     1016.    500            } else {
    +
     1017.    500                return;
    +
     1018.    500            }
    +
     1019.     48            if (char === "?") {
    +
     1020.     28                next_char("?");
    +
     1021.     28            }
    +
     1022.    546        }
    +
     1023.     97
    +
     1024.    103        function subklass() {
    +
     1025.    103
    +
     1026.    103// Match a character in a character class.
    +
     1027.    103
    +
     1028.     23            if (char === "\\") {
    +
     1029.     23                escape("BbDdSsWw-[]^");
    +
     1030.     23                return true;
    +
     1031.     80            }
    +
     1032.     80            if (
    +
     1033.     80                char === ""
    +
     1034.     80                || char === "["
    +
     1035.     79                || char === "]"
    +
     1036.     48                || char === "/"
    +
     1037.     47                || char === "^"
    +
     1038.     47                || char === "-"
    +
     1039.     33            ) {
    +
     1040.     33                return false;
    +
     1041.     47            }
    +
     1042.     47            if (char === " ") {
    +
     1043.      1                warn_at("expected_a_b", line, column, "\\u0020", " ");
    +
     1044.     46            } else if (char === "`" && mega_mode) {
    +
     1045.     46                warn_at("unexpected_a", line, column, "`");
    +
     1046.     47            }
    +
     1047.     47            next_char();
    +
     1048.     47            return true;
    +
     1049.     47        }
    +
     1050.     97
    +
     1051.     90        function ranges() {
    +
     1052.     90
    +
     1053.     90// Match a range of subclasses.
    +
     1054.     90
    +
     1055.     58            if (subklass()) {
    +
     1056.     58                if (char === "-") {
    +
     1057.     58                    next_char("-");
    +
     1058.     58                    if (!subklass()) {
    +
     1059.     58                        return stop_at(
    +
     1060.     58                            "unexpected_a",
    +
     1061.     58                            line,
    +
     1062.     58                            column - 1,
    +
     1063.     58                            "-"
    +
     1064.     58                        );
    +
     1065.     58                    }
    +
     1066.     58                }
    +
     1067.     58                return ranges();
    +
     1068.     58            }
    +
     1069.     90        }
    +
     1070.     97
    +
     1071.     32        function klass() {
    +
     1072.     32
    +
     1073.     32// Match a class.
    +
     1074.     32
    +
     1075.     32            next_char("[");
    +
     1076.      3            if (char === "^") {
    +
     1077.      3                next_char("^");
    +
     1078.      3            }
    +
     1079.     33            (function classy() {
    +
     1080.     33                ranges();
    +
     1081.      2                if (char !== "]" && char !== "") {
    +
     1082.      1                    warn_at(
    +
     1083.      1                        "expected_a_before_b",
    +
     1084.      1                        line,
    +
     1085.      1                        column,
    +
     1086.      1                        "\\",
    +
     1087.      1                        char
    +
     1088.      1                    );
    +
     1089.      1                    next_char();
    +
     1090.      1                    return classy();
    +
     1091.      1                }
    +
     1092.     33            }());
    +
     1093.     32            next_char("]");
    +
     1094.     32        }
    +
     1095.     97
    +
     1096.    120        function choice() {
    +
     1097.    120
    +
     1098.     23            function group() {
    +
     1099.     23
    +
     1100.     23// Match a group that starts with left paren.
    +
     1101.     23
    +
     1102.     23                next_char("(");
    +
     1103.      7                if (char === "?") {
    +
     1104.      7                    next_char("?");
    +
     1105.      7                    if (char === "=" || char === "!") {
    +
     1106.      7                        next_char();
    +
     1107.      7                    } else {
    +
     1108.      7                        next_char(":");
    +
     1109.      7                    }
    +
     1110.     16                } else if (char === ":") {
    +
     1111.     16                    warn_at("expected_a_before_b", line, column, "?", ":");
    +
     1112.     16                }
    +
     1113.     23                choice();
    +
     1114.     23                next_char(")");
    +
     1115.     23            }
    +
     1116.    120
    +
     1117.    665            function factor() {
    +
     1118.    665
    +
     1119.    665// Parse current character in regexp.
    +
     1120.    665
    +
     1121.    665                if (
    +
     1122.    665                    char === ""
    +
     1123.    664                    || char === "/"
    +
     1124.    571                    || char === "]"
    +
     1125.    571                    || char === ")"
    +
     1126.    117                ) {
    +
     1127.    117                    return false;
    +
     1128.    548                }
    +
     1129.    548                if (char === "(") {
    +
     1130.     23                    group();
    +
     1131.     23                    return true;
    +
     1132.    525                }
    +
     1133.    525                if (char === "[") {
    +
     1134.     32                    klass();
    +
     1135.     32                    return true;
    +
     1136.    493                }
    +
     1137.    493                if (char === "\\") {
    +
     1138.    100                    escape("BbDdSsWw^${}[]():=!.|*+?");
    +
     1139.    100                    return true;
    +
     1140.    393                }
    +
     1141.    393                if (
    +
     1142.    393                    char === "?"
    +
     1143.    393                    || char === "+"
    +
     1144.    392                    || char === "*"
    +
     1145.    392                    || char === "}"
    +
     1146.    392                    || char === "{"
    +
     1147.      1                ) {
    +
     1148.      1                    warn_at(
    +
     1149.      1                        "expected_a_before_b",
    +
     1150.      1                        line,
    +
     1151.      1                        column - 1,
    +
     1152.      1                        "\\",
    +
     1153.      1                        char
    +
     1154.      1                    );
    +
     1155.    392                } else if (char === "`") {
    +
     1156.    392                    if (mega_mode) {
    +
     1157.    392                        warn_at("unexpected_a", line, column - 1, "`");
    +
     1158.    392                    }
    +
     1159.    392                } else if (char === " ") {
    +
     1160.    392                    warn_at(
    +
     1161.    392                        "expected_a_b",
    +
     1162.    392                        line,
    +
     1163.    392                        column - 1,
    +
     1164.    392                        "\\s",
    +
     1165.    392                        " "
    +
     1166.    392                    );
    +
     1167.    392                } else if (char === "$") {
    +
     1168.    392                    if (source_line[0] !== "/") {
    +
     1169.    392                        multi_mode = true;
    +
     1170.    392                    }
    +
     1171.    392                } else if (char === "^") {
    +
     1172.    392                    if (snippet !== "^") {
    +
     1173.    392                        multi_mode = true;
    +
     1174.    392                    }
    +
     1175.    393                }
    +
     1176.    393                next_char();
    +
     1177.    393                return true;
    +
     1178.    393            }
    +
     1179.    120
    +
     1180.    665            function sequence(follow) {
    +
     1181.    546                if (factor()) {
    +
     1182.    546                    quantifier();
    +
     1183.    546                    return sequence(true);
    +
     1184.    546                }
    +
     1185.    117                if (!follow) {
    +
     1186.      1
    +
     1187.      1// cause: "/ /"
    +
     1188.      1
    +
     1189.      1                    warn_at("expected_regexp_factor_a", line, column, char);
    +
     1190.      1                }
    +
     1191.    665            }
    +
     1192.    120
    +
     1193.    120// Match a choice (a sequence that can be followed by | and another choice).
    +
     1194.    120
    +
     1195.    120            sequence();
    +
     1196.      0            if (char === "|") {
    +
     1197.      0                next_char("|");
    +
     1198.      0                return choice();
    +
     1199.      0            }
    +
     1200.    120        }
    +
     1201.     97
    +
     1202.     97// Scan the regexp literal. Give a warning if the first character is = because
    +
     1203.     97// /= looks like a division assignment operator.
    +
     1204.     97
    +
     1205.     97        snippet = "";
    +
     1206.     97        next_char();
    +
     1207.      1        if (char === "=") {
    +
     1208.      1            warn_at("expected_a_before_b", line, column, "\\", "=");
    +
     1209.      1        }
    +
     1210.     97        choice();
    +
     1211.     97
    +
     1212.     97// Make sure there is a closing slash.
    +
     1213.     97
    +
     1214.     97        snip();
    +
     1215.     97        value = snippet;
    +
     1216.     97        next_char("/");
    +
     1217.     97
    +
     1218.     97// Process dangling flag letters.
    +
     1219.     97
    +
     1220.     97        const allowed = {
    +
     1221.     97            g: true,
    +
     1222.     97            i: true,
    +
     1223.     97            m: true,
    +
     1224.     97            u: true,
    +
     1225.     97            y: true
    +
     1226.     97        };
    +
     1227.     97        const flag = empty();
    +
     1228.    167        (function make_flag() {
    +
     1229.     74            if (is_letter(char)) {
    +
     1230.     74                if (allowed[char] !== true) {
    +
     1231.     74                    warn_at("unexpected_a", line, column, char);
    +
     1232.     74                }
    +
     1233.     74                allowed[char] = false;
    +
     1234.     74                flag[char] = true;
    +
     1235.     74                next_char();
    +
     1236.     74                return make_flag();
    +
     1237.     74            }
    +
     1238.    167        }());
    +
     1239.     97        back_char();
    +
     1240.     92        if (char === "/" || char === "*") {
    +
     1241.      1            return stop_at("unexpected_a", line, from, char);
    +
     1242.     92        }
    +
     1243.     92        result = make("(regexp)", char);
    +
     1244.     92        result.flag = flag;
    +
     1245.     92        result.value = value;
    +
     1246.     92        if (multi_mode && !flag.m) {
    +
     1247.      1
    +
     1248.      1// cause: "aa=/$^/"
    +
     1249.      1
    +
     1250.      1            warn_at("missing_m", line, column);
    +
     1251.     92        }
    +
     1252.     92        return result;
    +
     1253.     92    }
    +
     1254.    405
    +
     1255.   3832    function string(quote) {
    +
     1256.   3832
    +
     1257.   3832// Make a string token.
    +
     1258.   3832
    +
     1259.   3832        let the_token;
    +
     1260.   3832        snippet = "";
    +
     1261.   3832        next_char();
    +
     1262.   3832
    +
     1263.  33586        return (function next() {
    +
     1264.   3829            if (char === quote) {
    +
     1265.   3829                snip();
    +
     1266.   3829                the_token = make("(string)", snippet);
    +
     1267.   3829                the_token.quote = quote;
    +
     1268.   3829                return the_token;
    +
     1269.  29757            }
    +
     1270.  29757            if (char === "") {
    +
     1271.      1
    +
     1272.      1// cause: "\""
    +
     1273.      1
    +
     1274.      1                return stop_at("unclosed_string", line, column);
    +
     1275.  29756            }
    +
     1276.  29756            if (char === "\\") {
    +
     1277.    280                escape(quote);
    +
     1278.  29476            } else if (char === "`") {
    +
     1279.  29476                if (mega_mode) {
    +
     1280.  29476                    warn_at("unexpected_a", line, column, "`");
    +
     1281.  29476                }
    +
     1282.  29476                next_char("`");
    +
     1283.  29476            } else {
    +
     1284.  29476                next_char();
    +
     1285.  29754            }
    +
     1286.  29754            return next();
    +
     1287.  29754        }());
    +
     1288.   3832    }
    +
     1289.    405
    +
     1290.    469    function frack() {
    +
     1291.      6        if (char === ".") {
    +
     1292.      6            some_digits(rx_digits);
    +
     1293.      6        }
    +
     1294.      1        if (char === "E" || char === "e") {
    +
     1295.      1            next_char();
    +
     1296.      1            if (char !== "+" && char !== "-") {
    +
     1297.      1                back_char();
    +
     1298.      1            }
    +
     1299.      1            some_digits(rx_digits);
    +
     1300.      1        }
    +
     1301.    469    }
    +
     1302.    405
    +
     1303.   1064    function number() {
    +
     1304.    599        if (snippet === "0") {
    +
     1305.    599            next_char();
    +
     1306.    599            if (char === ".") {
    +
     1307.    599                frack();
    +
     1308.    599            } else if (char === "b") {
    +
     1309.    599                some_digits(rx_bits);
    +
     1310.    599            } else if (char === "o") {
    +
     1311.    599                some_digits(rx_octals);
    +
     1312.    599            } else if (char === "x") {
    +
     1313.    599                some_digits(rx_hexs);
    +
     1314.    599            }
    +
     1315.    599        } else {
    +
     1316.    465            next_char();
    +
     1317.    465            frack();
    +
     1318.    465        }
    +
     1319.   1064
    +
     1320.   1064// If the next character after a number is a digit or letter, then something
    +
     1321.   1064// unexpected is going on.
    +
     1322.   1064
    +
     1323.   1064        if (
    +
     1324.    478            (char >= "0" && char <= "9")
    +
     1325.     24            || (char >= "a" && char <= "z")
    +
     1326.   1063            || (char >= "A" && char <= "Z")
    +
     1327.      1        ) {
    +
     1328.      1
    +
     1329.      1// cause: "0a"
    +
     1330.      1
    +
     1331.      1            return stop_at(
    +
     1332.      1                "unexpected_a_after_b",
    +
     1333.      1                line,
    +
     1334.      1                column - 1,
    +
     1335.      1                snippet.slice(-1),
    +
     1336.      1                snippet.slice(0, -1)
    +
     1337.      1            );
    +
     1338.   1063        }
    +
     1339.   1063        back_char();
    +
     1340.   1063        return make("(number)", snippet);
    +
     1341.   1063    }
    +
     1342.    405
    +
     1343.  82733    function lex() {
    +
     1344.  82733        let array;
    +
     1345.  82733        let i = 0;
    +
     1346.  82733        let j = 0;
    +
     1347.  82733        let last;
    +
     1348.  82733        let result;
    +
     1349.  82733        let the_token;
    +
     1350.  82733
    +
     1351.  82733// This should properly be a tail recursive function, but sadly, conformant
    +
     1352.  82733// implementations of ES6 are still rare. This is the ideal code:
    +
     1353.  82733
    +
     1354.  82733//      if (!source_line) {
    +
     1355.  82733//          source_line = next_line();
    +
     1356.  82733//          from = 0;
    +
     1357.  82733//          return (
    +
     1358.  82733//              source_line === undefined
    +
     1359.  82733//              ? (
    +
     1360.  82733//                  mega_mode
    +
     1361.  82733//                  ? stop_at("unclosed_mega", mega_line, mega_from)
    +
     1362.  82733//                  : make("(end)")
    +
     1363.  82733//              )
    +
     1364.  82733//              : lex()
    +
     1365.  82733//          );
    +
     1366.  82733//      }
    +
     1367.  82733
    +
     1368.  82733// Unfortunately, incompetent JavaScript engines will sometimes fail to execute
    +
     1369.  82733// it correctly. So for now, we do it the old fashioned way.
    +
     1370.  82733
    +
     1371.  16470        while (!source_line) {
    +
     1372.  16470            source_line = next_line();
    +
     1373.  16470            from = 0;
    +
     1374.  16470            if (source_line === undefined) {
    +
     1375.  16470                return (
    +
     1376.  16470                    mega_mode
    +
     1377.  16470                    ? stop_at("unclosed_mega", mega_line, mega_from)
    +
     1378.  16470                    : make("(end)")
    +
     1379.  16470                );
    +
     1380.  16470            }
    +
     1381.  82350        }
    +
     1382.  82350
    +
     1383.  82350        from = column;
    +
     1384.  82350        result = source_line.match(rx_token);
    +
     1385.  82350
    +
     1386.  82350// result[1] token
    +
     1387.  82350// result[2] whitespace
    +
     1388.  82350// result[3] identifier
    +
     1389.  82350// result[4] number
    +
     1390.  82350// result[5] rest
    +
     1391.  82350
    +
     1392.  82350        if (!result) {
    +
     1393.      1
    +
     1394.      1// cause: "#"
    +
     1395.      1
    +
     1396.      1            return stop_at(
    +
     1397.      1                "unexpected_char_a",
    +
     1398.      1                line,
    +
     1399.      1                column,
    +
     1400.      1                source_line[0]
    +
     1401.      1            );
    +
     1402.  82349        }
    +
     1403.  82349
    +
     1404.  82349        snippet = result[1];
    +
     1405.  82349        column += snippet.length;
    +
     1406.  82349        source_line = result[5];
    +
     1407.  82349
    +
     1408.  82349// Whitespace was matched. Call lex again to get more.
    +
     1409.  82349
    +
     1410.  82349        if (result[2]) {
    +
     1411.  25602            return lex();
    +
     1412.  56747        }
    +
     1413.  56747
    +
     1414.  56747// The token is an identifier.
    +
     1415.  56747
    +
     1416.  56747        if (result[3]) {
    +
     1417.  18519            return make(snippet, undefined, true);
    +
     1418.  38228        }
    +
     1419.  38228
    +
     1420.  38228// The token is a number.
    +
     1421.  38228
    +
     1422.  38228        if (result[4]) {
    +
     1423.   1064            return number(snippet);
    +
     1424.  37164        }
    +
     1425.  37164
    +
     1426.  37164// The token is a string.
    +
     1427.  37164
    +
     1428.  37164        if (snippet === "\"") {
    +
     1429.   3830            return string(snippet);
    +
     1430.  33334        }
    +
     1431.  33334        if (snippet === "'") {
    +
     1432.      2            if (!option.single) {
    +
     1433.      2
    +
     1434.      2// cause: "''"
    +
     1435.      2
    +
     1436.      2                warn_at("use_double", line, column);
    +
     1437.      2            }
    +
     1438.      2            return string(snippet);
    +
     1439.  33332        }
    +
     1440.  33332
    +
     1441.  33332// The token is a megastring. We don't allow any kind of mega nesting.
    +
     1442.  33332
    +
     1443.  33332        if (snippet === "`") {
    +
     1444.     63            if (mega_mode) {
    +
     1445.     63                return stop_at("expected_a_b", line, column, "}", "`");
    +
     1446.     63            }
    +
     1447.     63            snippet = "";
    +
     1448.     63            mega_from = from;
    +
     1449.     63            mega_line = line;
    +
     1450.     63            mega_mode = true;
    +
     1451.     63
    +
     1452.     63// Parsing a mega literal is tricky. First make a ` token.
    +
     1453.     63
    +
     1454.     63            make("`");
    +
     1455.     63            from += 1;
    +
     1456.     63
    +
     1457.     63// Then loop, building up a string, possibly from many lines, until seeing
    +
     1458.     63// the end of file, a closing `, or a ${ indicting an expression within the
    +
     1459.     63// string.
    +
     1460.     63
    +
     1461.    485            (function part() {
    +
     1462.    485                const at = source_line.search(rx_mega);
    +
     1463.    485
    +
     1464.    485// If neither ` nor ${ is seen, then the whole line joins the snippet.
    +
     1465.    485
    +
     1466.    312                if (at < 0) {
    +
     1467.    312                    snippet += source_line + "\n";
    +
     1468.    312                    return (
    +
     1469.    312                        next_line() === undefined
    +
     1470.    312
    +
     1471.    312// cause: "`"
    +
     1472.    312
    +
     1473.    312                        ? stop_at("unclosed_mega", mega_line, mega_from)
    +
     1474.    312                        : part()
    +
     1475.    312                    );
    +
     1476.    312                }
    +
     1477.    173                snippet += source_line.slice(0, at);
    +
     1478.    173                column += at;
    +
     1479.    173                source_line = source_line.slice(at);
    +
     1480.    173                if (source_line[0] === "\\") {
    +
     1481.     76                    snippet += source_line.slice(0, 2);
    +
     1482.     76                    source_line = source_line.slice(2);
    +
     1483.     76                    column += 2;
    +
     1484.     76                    return part();
    +
     1485.     97                }
    +
     1486.     97
    +
     1487.     97// if either ` or ${ was found, then the preceding joins the snippet to become
    +
     1488.     97// a string token.
    +
     1489.     97
    +
     1490.     97                make("(string)", snippet).quote = "`";
    +
     1491.     97                snippet = "";
    +
     1492.     97
    +
     1493.     97// If ${, then make tokens that will become part of an expression until
    +
     1494.     97// a } token is made.
    +
     1495.     97
    +
     1496.     97                if (source_line[0] === "$") {
    +
     1497.     63                    column += 2;
    +
     1498.     63                    make("${");
    +
     1499.     63                    source_line = source_line.slice(2);
    +
     1500.    156                    (function expr() {
    +
     1501.    156                        const id = lex().id;
    +
     1502.     63                        if (id === "{") {
    +
     1503.     63                            return stop_at(
    +
     1504.     63                                "expected_a_b",
    +
     1505.     63                                line,
    +
     1506.     63                                column,
    +
     1507.     63                                "}",
    +
     1508.     63                                "{"
    +
     1509.     63                            );
    +
     1510.    153                        }
    +
     1511.    153                        if (id !== "}") {
    +
     1512.    117                            return expr();
    +
     1513.    117                        }
    +
     1514.    156                    }());
    +
     1515.     63                    return part();
    +
     1516.     63                }
    +
     1517.    485            }());
    +
     1518.     63            source_line = source_line.slice(1);
    +
     1519.     63            column += 1;
    +
     1520.     63            mega_mode = false;
    +
     1521.     63            return make("`");
    +
     1522.  33269        }
    +
     1523.  33269
    +
     1524.  33269// The token is a // comment.
    +
     1525.  33269
    +
     1526.  33269        if (snippet === "//") {
    +
     1527.   1611            snippet = source_line;
    +
     1528.   1611            source_line = "";
    +
     1529.   1611            the_token = comment(snippet);
    +
     1530.   1611            if (mega_mode) {
    +
     1531.   1611
    +
     1532.   1611// cause: "`${//}`"
    +
     1533.   1611
    +
     1534.   1611                warn("unexpected_comment", the_token, "`");
    +
     1535.   1611            }
    +
     1536.   1611            return the_token;
    +
     1537.  31658        }
    +
     1538.  31658
    +
     1539.  31658// The token is a /* comment.
    +
     1540.  31658
    +
     1541.  31658        if (snippet === "/*") {
    +
     1542.     54            array = [];
    +
     1543.     54            if (source_line[0] === "/") {
    +
     1544.     54                warn_at("unexpected_a", line, column + i, "/");
    +
     1545.     54            }
    +
     1546.    178            (function next() {
    +
     1547.    163                if (source_line > "") {
    +
     1548.    163                    i = source_line.search(rx_star_slash);
    +
     1549.    163                    if (i >= 0) {
    +
     1550.    163                        return;
    +
     1551.    163                    }
    +
     1552.    163                    j = source_line.search(rx_slash_star);
    +
     1553.    163                    if (j >= 0) {
    +
     1554.    163
    +
     1555.    163// cause: "/*/*"
    +
     1556.    163
    +
     1557.    163                        warn_at("nested_comment", line, column + j);
    +
     1558.    163                    }
    +
     1559.    163                }
    +
     1560.    127                array.push(source_line);
    +
     1561.    127                source_line = next_line();
    +
     1562.    127                if (source_line === undefined) {
    +
     1563.     54
    +
     1564.     54// cause: "/*"
    +
     1565.     54
    +
     1566.     54                    return stop_at("unclosed_comment", line, column);
    +
     1567.    124                }
    +
     1568.    124                return next();
    +
     1569.    124            }());
    +
     1570.     54            snippet = source_line.slice(0, i);
    +
     1571.     54            j = snippet.search(rx_slash_star_or_slash);
    +
     1572.     54            if (j >= 0) {
    +
     1573.     54
    +
     1574.     54// cause: "/*/**/"
    +
     1575.     54
    +
     1576.     54                warn_at("nested_comment", line, column + j);
    +
     1577.     54            }
    +
     1578.     54            array.push(snippet);
    +
     1579.     54            column += i + 2;
    +
     1580.     54            source_line = source_line.slice(i + 2);
    +
     1581.     54            return comment(array);
    +
     1582.  31604        }
    +
     1583.  31604
    +
     1584.  31604// The token is a slash.
    +
     1585.  31604
    +
     1586.  31604        if (snippet === "/") {
    +
     1587.    107
    +
     1588.    107// The / can be a division operator or the beginning of a regular expression
    +
     1589.    107// literal. It is not possible to know which without doing a complete parse.
    +
     1590.    107// We want to complete the tokenization before we begin to parse, so we will
    +
     1591.    107// estimate. This estimator can fail in some cases. For example, it cannot
    +
     1592.    107// know if "}" is ending a block or ending an object literal, so it can
    +
     1593.    107// behave incorrectly in that case; it is not meaningful to divide an
    +
     1594.    107// object, so it is likely that we can get away with it. We avoided the worst
    +
     1595.    107// cases by eliminating automatic semicolon insertion.
    +
     1596.    107
    +
     1597.    107            if (prior.identifier) {
    +
     1598.    107                if (!prior.dot) {
    +
     1599.    107                    if (prior.id === "return") {
    +
     1600.    107                        return regexp();
    +
     1601.    107                    }
    +
     1602.    107                    if (
    +
     1603.    107                        prior.id === "(begin)"
    +
     1604.    107                        || prior.id === "case"
    +
     1605.    107                        || prior.id === "delete"
    +
     1606.    107                        || prior.id === "in"
    +
     1607.    107                        || prior.id === "instanceof"
    +
     1608.    107                        || prior.id === "new"
    +
     1609.    107                        || prior.id === "typeof"
    +
     1610.    107                        || prior.id === "void"
    +
     1611.    107                        || prior.id === "yield"
    +
     1612.    107                    ) {
    +
     1613.    107                        the_token = regexp();
    +
     1614.    107
    +
     1615.    107// cause: "/./"
    +
     1616.    107// cause: "case /./"
    +
     1617.    107// cause: "delete /./"
    +
     1618.    107// cause: "in /./"
    +
     1619.    107// cause: "instanceof /./"
    +
     1620.    107// cause: "new /./"
    +
     1621.    107// cause: "typeof /./"
    +
     1622.    107// cause: "void /./"
    +
     1623.    107// cause: "yield /./"
    +
     1624.    107
    +
     1625.    107                        return stop("unexpected_a", the_token);
    +
     1626.    107                    }
    +
     1627.    107                }
    +
     1628.    107            } else {
    +
     1629.    107                last = prior.id[prior.id.length - 1];
    +
     1630.    107                if ("(,=:?[".indexOf(last) >= 0) {
    +
     1631.    107                    return regexp();
    +
     1632.    107                }
    +
     1633.    107                if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) {
    +
     1634.    107                    the_token = regexp();
    +
     1635.    107
    +
     1636.    107// cause: "!/./"
    +
     1637.    107
    +
     1638.    107                    warn("wrap_regexp", the_token);
    +
     1639.    107                    return the_token;
    +
     1640.    107                }
    +
     1641.    107            }
    +
     1642.    107            if (source_line[0] === "=") {
    +
     1643.    107                column += 1;
    +
     1644.    107                source_line = source_line.slice(1);
    +
     1645.    107                snippet = "/=";
    +
     1646.    107                warn_at("unexpected_a", line, column, "/=");
    +
     1647.    107            }
    +
     1648.  31507        }
    +
     1649.  31507        return make(snippet);
    +
     1650.  31507    }
    +
     1651.    405
    +
     1652.    405    first = lex();
    +
     1653.    377    json_mode = first.id === "{" || first.id === "[";
    +
     1654.    405
    +
     1655.    405// This loop will be replaced with a recursive call to lex when ES6 has been
    +
     1656.    405// finished and widely deployed and adopted.
    +
     1657.    405
    +
     1658.  56570    while (true) {
    +
     1659.  56570        if (lex().id === "(end)") {
    +
     1660.  56570            break;
    +
     1661.  56570        }
    +
     1662.  56570    }
    +
     1663.    405}
    +
     1664.      1
    +
     1665.      1// Parsing:
    +
     1666.      1
    +
     1667.      1// Parsing weaves the tokens into an abstract syntax tree. During that process,
    +
     1668.      1// a token may be given any of these properties:
    +
     1669.      1
    +
     1670.      1//      arity       string
    +
     1671.      1//      label       identifier
    +
     1672.      1//      name        identifier
    +
     1673.      1//      expression  expressions
    +
     1674.      1//      block       statements
    +
     1675.      1//      else        statements (else, default, catch)
    +
     1676.      1
    +
     1677.      1// Specialized tokens may have additional properties.
    +
     1678.      1
    +
     1679.   3901function survey(name) {
    +
     1680.   3901    let id = name.id;
    +
     1681.   3901
    +
     1682.   3901// Tally the property name. If it is a string, only tally strings that conform
    +
     1683.   3901// to the identifier rules.
    +
     1684.   3901
    +
     1685.     10    if (id === "(string)") {
    +
     1686.     10        id = name.value;
    +
     1687.     10        if (!rx_identifier.test(id)) {
    +
     1688.     10            return id;
    +
     1689.     10        }
    +
     1690.   3891    } else if (id === "`") {
    +
     1691.   3891        if (name.value.length === 1) {
    +
     1692.   3891            id = name.value[0].value;
    +
     1693.   3891            if (!rx_identifier.test(id)) {
    +
     1694.   3891                return id;
    +
     1695.   3891            }
    +
     1696.   3891        }
    +
     1697.   3891    } else if (!name.identifier) {
    +
     1698.   3891
    +
     1699.   3891// cause: "let aa={0:0}"
    +
     1700.   3891
    +
     1701.   3891        return stop("expected_identifier_a", name);
    +
     1702.   3891    }
    +
     1703.   3889
    +
     1704.   3889// If we have seen this name before, increment its count.
    +
     1705.   3889
    +
     1706.   3889    if (typeof property[id] === "number") {
    +
     1707.   3083        property[id] += 1;
    +
     1708.   3083
    +
     1709.   3083// If this is the first time seeing this property name, and if there is a
    +
     1710.   3083// tenure list, then it must be on the list. Otherwise, it must conform to
    +
     1711.   3083// the rules for good property names.
    +
     1712.   3083
    +
     1713.   3083    } else {
    +
     1714.    806        if (tenure !== undefined) {
    +
     1715.    806            if (tenure[id] !== true) {
    +
     1716.    806
    +
     1717.    806// cause: "/*property aa*/\naa.bb"
    +
     1718.    806
    +
     1719.    806                warn("unregistered_property_a", name);
    +
     1720.    806            }
    +
     1721.    806        } else {
    +
     1722.    806            if (name.identifier && rx_bad_property.test(id)) {
    +
     1723.    806
    +
     1724.    806// cause: "aa._"
    +
     1725.    806
    +
     1726.    806                warn("bad_property_a", name);
    +
     1727.    806            }
    +
     1728.    806        }
    +
     1729.    806        property[id] = 1;
    +
     1730.   3889    }
    +
     1731.   3889    return id;
    +
     1732.   3889}
    +
     1733.      1
    +
     1734.  57828function dispense() {
    +
     1735.  57828
    +
     1736.  57828// Deliver the next token, skipping the comments.
    +
     1737.  57828
    +
     1738.  57828    const cadet = tokens[token_nr];
    +
     1739.  57828    token_nr += 1;
    +
     1740.   1660    if (cadet.id === "(comment)") {
    +
     1741.   1660        if (json_mode) {
    +
     1742.   1660            warn("unexpected_a", cadet);
    +
     1743.   1660        }
    +
     1744.   1660        return dispense();
    +
     1745.  56168    } else {
    +
     1746.  56168        return cadet;
    +
     1747.  56168    }
    +
     1748.  57828}
    +
     1749.      1
    +
     1750.    395function lookahead() {
    +
     1751.    395
    +
     1752.    395// Look ahead one token without advancing.
    +
     1753.    395
    +
     1754.    395    const old_token_nr = token_nr;
    +
     1755.    395    const cadet = dispense(true);
    +
     1756.    395    token_nr = old_token_nr;
    +
     1757.    395    return cadet;
    +
     1758.    395}
    +
     1759.      1
    +
     1760.  55794function advance(id, match) {
    +
     1761.  55794
    +
     1762.  55794// Produce the next token.
    +
     1763.  55794
    +
     1764.  55794// Attempt to give helpful names to anonymous functions.
    +
     1765.  55794
    +
     1766.  18487    if (token.identifier && token.id !== "function") {
    +
     1767.  17901        anon = token.id;
    +
     1768.  37893    } else if (token.id === "(string)" && rx_identifier.test(token.value)) {
    +
     1769.  37893        anon = token.value;
    +
     1770.  37893    }
    +
     1771.  55794
    +
     1772.  55794// Attempt to match next_token with an expected id.
    +
     1773.  55794
    +
     1774.  20808    if (id !== undefined && next_token.id !== id) {
    +
     1775.     21        return (
    +
     1776.     21            match === undefined
    +
     1777.     21
    +
     1778.     21// cause: "()"
    +
     1779.     21
    +
     1780.     21            ? stop("expected_a_b", next_token, id, artifact())
    +
     1781.     21
    +
     1782.     21// cause: "{\"aa\":0"
    +
     1783.     21
    +
     1784.     21            : stop(
    +
     1785.     21                "expected_a_b_from_c_d",
    +
     1786.     21                next_token,
    +
     1787.     21                id,
    +
     1788.     21                artifact(match),
    +
     1789.     21
    +
     1790.     21// Return the fudged line number of an artifact.
    +
     1791.     21
    +
     1792.     21                match.line + fudge,
    +
     1793.     21                artifact(next_token)
    +
     1794.     21            )
    +
     1795.     21        );
    +
     1796.  55773    }
    +
     1797.  55773
    +
     1798.  55773// Promote the tokens, skipping comments.
    +
     1799.  55773
    +
     1800.  55773    token = next_token;
    +
     1801.  55773    next_token = dispense();
    +
     1802.  55773    if (next_token.id === "(end)") {
    +
     1803.    630        token_nr -= 1;
    +
     1804.    630    }
    +
     1805.  55794}
    +
     1806.      1
    +
     1807.      1// Parsing of JSON is simple:
    +
     1808.      1
    +
     1809.     43function json_value() {
    +
     1810.     43    let negative;
    +
     1811.     15    if (next_token.id === "{") {
    +
     1812.     15        return (function json_object() {
    +
     1813.     15            const brace = next_token;
    +
     1814.     15            const object = empty();
    +
     1815.     15            const properties = [];
    +
     1816.     15            brace.expression = properties;
    +
     1817.     15            advance("{");
    +
     1818.     15            if (next_token.id !== "}") {
    +
     1819.     15                (function next() {
    +
     1820.     15                    let name;
    +
     1821.     15                    let value;
    +
     1822.     15                    if (next_token.quote !== "\"") {
    +
     1823.     15                        warn(
    +
     1824.     15                            "unexpected_a",
    +
     1825.     15                            next_token,
    +
     1826.     15                            next_token.quote
    +
     1827.     15                        );
    +
     1828.     15                    }
    +
     1829.     15                    name = next_token;
    +
     1830.     15                    advance("(string)");
    +
     1831.     15                    if (object[token.value] !== undefined) {
    +
     1832.     15
    +
     1833.     15// cause: "{\"aa\":0,\"aa\":0}"
    +
     1834.     15
    +
     1835.     15                        warn("duplicate_a", token);
    +
     1836.     15                    } else if (token.value === "__proto__") {
    +
     1837.     15
    +
     1838.     15// cause: "{\"__proto__\":0}"
    +
     1839.     15
    +
     1840.     15                        warn("bad_property_a", token);
    +
     1841.     15                    } else {
    +
     1842.     15                        object[token.value] = token;
    +
     1843.     15                    }
    +
     1844.     15                    advance(":");
    +
     1845.     15                    value = json_value();
    +
     1846.     15                    value.label = name;
    +
     1847.     15                    properties.push(value);
    +
     1848.     15                    if (next_token.id === ",") {
    +
     1849.     15                        advance(",");
    +
     1850.     15                        return next();
    +
     1851.     15                    }
    +
     1852.     15                }());
    +
     1853.     15            }
    +
     1854.     15            advance("}", brace);
    +
     1855.     15            return brace;
    +
     1856.     15        }());
    +
     1857.     28    }
    +
     1858.     28    if (next_token.id === "[") {
    +
     1859.     10        return (function json_array() {
    +
     1860.     10            const bracket = next_token;
    +
     1861.     10            const elements = [];
    +
     1862.     10            bracket.expression = elements;
    +
     1863.     10            advance("[");
    +
     1864.     10            if (next_token.id !== "]") {
    +
     1865.     10                (function next() {
    +
     1866.     10                    elements.push(json_value());
    +
     1867.     10                    if (next_token.id === ",") {
    +
     1868.     10                        advance(",");
    +
     1869.     10                        return next();
    +
     1870.     10                    }
    +
     1871.     10                }());
    +
     1872.     10            }
    +
     1873.     10            advance("]", bracket);
    +
     1874.     10            return bracket;
    +
     1875.     10        }());
    +
     1876.     18    }
    +
     1877.     18    if (
    +
     1878.     18        next_token.id === "true"
    +
     1879.     18        || next_token.id === "false"
    +
     1880.     18        || next_token.id === "null"
    +
     1881.      1    ) {
    +
     1882.      1        advance();
    +
     1883.      1        return token;
    +
     1884.     17    }
    +
     1885.     17    if (next_token.id === "(number)") {
    +
     1886.      9        if (!rx_JSON_number.test(next_token.value)) {
    +
     1887.      9            warn("unexpected_a");
    +
     1888.      9        }
    +
     1889.      9        advance();
    +
     1890.      9        return token;
    +
     1891.      9    }
    +
     1892.      8    if (next_token.id === "(string)") {
    +
     1893.      4        if (next_token.quote !== "\"") {
    +
     1894.      4            warn("unexpected_a", next_token, next_token.quote);
    +
     1895.      4        }
    +
     1896.      4        advance();
    +
     1897.      4        return token;
    +
     1898.      4    }
    +
     1899.      4    if (next_token.id === "-") {
    +
     1900.      2        negative = next_token;
    +
     1901.      2        negative.arity = "unary";
    +
     1902.      2        advance("-");
    +
     1903.      2        advance("(number)");
    +
     1904.      2        if (!rx_JSON_number.test(token.value)) {
    +
     1905.      2            warn("unexpected_a", token);
    +
     1906.      2        }
    +
     1907.      2        negative.expression = token;
    +
     1908.      2        return negative;
    +
     1909.      2    }
    +
     1910.      2    stop("unexpected_a");
    +
     1911.      2}
    +
     1912.      1
    +
     1913.      1// Now we parse JavaScript.
    +
     1914.      1
    +
     1915.   1576function enroll(name, role, readonly) {
    +
     1916.   1576
    +
     1917.   1576// Enroll a name into the current function context. The role can be exception,
    +
     1918.   1576// function, label, parameter, or variable. We look for variable redefinition
    +
     1919.   1576// because it causes confusion.
    +
     1920.   1576
    +
     1921.   1576    const id = name.id;
    +
     1922.   1576
    +
     1923.   1576// Reserved words may not be enrolled.
    +
     1924.   1576
    +
     1925.     22    if (syntax[id] !== undefined && id !== "ignore") {
    +
     1926.      1
    +
     1927.      1// cause: "let undefined"
    +
     1928.      1
    +
     1929.      1        warn("reserved_a", name);
    +
     1930.   1575    } else {
    +
     1931.   1575
    +
     1932.   1575// Has the name been enrolled in this context?
    +
     1933.   1575
    +
     1934.   1575        let earlier = functionage.context[id];
    +
     1935.   1575        if (earlier) {
    +
     1936.   1575
    +
     1937.   1575// cause: "let aa;let aa"
    +
     1938.   1575
    +
     1939.   1575            warn(
    +
     1940.   1575                "redefinition_a_b",
    +
     1941.   1575                name,
    +
     1942.   1575                name.id,
    +
     1943.   1575                earlier.line + fudge
    +
     1944.   1575            );
    +
     1945.   1575
    +
     1946.   1575// Has the name been enrolled in an outer context?
    +
     1947.   1575
    +
     1948.   1575        } else {
    +
     1949.   1857            stack.forEach(function (value) {
    +
     1950.   1857                const item = value.context[id];
    +
     1951.   1575                if (item !== undefined) {
    +
     1952.   1575                    earlier = item;
    +
     1953.   1575                }
    +
     1954.   1857            });
    +
     1955.   1575            if (earlier) {
    +
     1956.   1575                if (id === "ignore") {
    +
     1957.   1575                    if (earlier.role === "variable") {
    +
     1958.   1575
    +
     1959.   1575// cause: "let ignore;function aa(ignore) {}"
    +
     1960.   1575
    +
     1961.   1575                        warn("unexpected_a", name);
    +
     1962.   1575                    }
    +
     1963.   1575                } else {
    +
     1964.   1575                    if (
    +
     1965.   1575                        (
    +
     1966.   1575                            role !== "exception"
    +
     1967.   1575                            || earlier.role !== "exception"
    +
     1968.   1575                        )
    +
     1969.   1575                        && role !== "parameter"
    +
     1970.   1575                        && role !== "function"
    +
     1971.   1575                    ) {
    +
     1972.   1575
    +
     1973.   1575// cause: "function aa(){var aa;}"
    +
     1974.   1575// cause: "function aa(){try{aa();}catch(aa){aa();}}"
    +
     1975.   1575
    +
     1976.   1575                        warn(
    +
     1977.   1575                            "redefinition_a_b",
    +
     1978.   1575                            name,
    +
     1979.   1575                            name.id,
    +
     1980.   1575                            earlier.line + fudge
    +
     1981.   1575                        );
    +
     1982.   1575                    }
    +
     1983.   1575                }
    +
     1984.   1575            }
    +
     1985.   1575
    +
     1986.   1575// Enroll it.
    +
     1987.   1575
    +
     1988.   1575            functionage.context[id] = name;
    +
     1989.   1575            name.dead = true;
    +
     1990.   1575            name.parent = functionage;
    +
     1991.   1575            name.init = false;
    +
     1992.   1575            name.role = role;
    +
     1993.   1575            name.used = 0;
    +
     1994.   1575            name.writable = !readonly;
    +
     1995.   1575        }
    +
     1996.   1575    }
    +
     1997.   1576}
    +
     1998.      1
    +
     1999.  15679function expression(rbp, initial) {
    +
     2000.  15679
    +
     2001.  15679// This is the heart of the Pratt parser. I retained Pratt's nomenclature.
    +
     2002.  15679// They are elements of the parsing method called Top Down Operator Precedence.
    +
     2003.  15679
    +
     2004.  15679// nud     Null denotation
    +
     2005.  15679// led     Left denotation
    +
     2006.  15679// lbp     Left binding power
    +
     2007.  15679// rbp     Right binding power
    +
     2008.  15679
    +
     2009.  15679// It processes a nud (variable, constant, prefix operator). It will then
    +
     2010.  15679// process leds (infix operators) until the bind powers cause it to stop. It
    +
     2011.  15679// returns the expression's parse tree.
    +
     2012.  15679
    +
     2013.  15679    let left;
    +
     2014.  15679    let the_symbol;
    +
     2015.  15679
    +
     2016.  15679// Statements will have already advanced, so advance now only if the token is
    +
     2017.  15679// not the first of a statement,
    +
     2018.  15679
    +
     2019.  12444    if (!initial) {
    +
     2020.  12444        advance();
    +
     2021.  12444    }
    +
     2022.  15679    the_symbol = syntax[token.id];
    +
     2023.   6940    if (the_symbol !== undefined && the_symbol.nud !== undefined) {
    +
     2024.   6923        left = the_symbol.nud();
    +
     2025.   8756    } else if (token.identifier) {
    +
     2026.   8756        left = token;
    +
     2027.   8756        left.arity = "variable";
    +
     2028.   8756    } else {
    +
     2029.   8756
    +
     2030.   8756// cause: "!"
    +
     2031.   8756
    +
     2032.   8756        return stop("unexpected_a", token);
    +
     2033.  15645    }
    +
     2034.  26298    (function right() {
    +
     2035.  26298        the_symbol = syntax[next_token.id];
    +
     2036.  26298        if (
    +
     2037.  26298            the_symbol !== undefined
    +
     2038.  26062            && the_symbol.led !== undefined
    +
     2039.  15645            && rbp < the_symbol.lbp
    +
     2040.  15645        ) {
    +
     2041.  15645            advance();
    +
     2042.  15645            left = the_symbol.led(left);
    +
     2043.  15645            return right();
    +
     2044.  15645        }
    +
     2045.  26298    }());
    +
     2046.  15645    return left;
    +
     2047.  15645}
    +
     2048.      1
    +
     2049.   1226function condition() {
    +
     2050.   1226
    +
     2051.   1226// Parse the condition part of a do, if, while.
    +
     2052.   1226
    +
     2053.   1226    const the_paren = next_token;
    +
     2054.   1226    let the_value;
    +
     2055.   1226
    +
     2056.   1226// cause: "do{}while()"
    +
     2057.   1226// cause: "if(){}"
    +
     2058.   1226// cause: "while(){}"
    +
     2059.   1226
    +
     2060.   1226    the_paren.free = true;
    +
     2061.   1226    advance("(");
    +
     2062.   1226    the_value = expression(0);
    +
     2063.   1226    advance(")");
    +
     2064.      1    if (the_value.wrapped === true) {
    +
     2065.      1        warn("unexpected_a", the_paren);
    +
     2066.   1220    }
    +
     2067.   1220    if (anticondition[the_value.id] === true) {
    +
     2068.     18        warn("unexpected_a", the_value);
    +
     2069.   1220    }
    +
     2070.   1220    return the_value;
    +
     2071.   1220}
    +
     2072.      1
    +
     2073.  10475function is_weird(thing) {
    +
     2074.  10475    return (
    +
     2075.  10475        thing.id === "(regexp)"
    +
     2076.  10475        || thing.id === "{"
    +
     2077.  10475        || thing.id === "=>"
    +
     2078.  10475        || thing.id === "function"
    +
     2079.  10471        || (thing.id === "[" && thing.arity === "unary")
    +
     2080.  10475    );
    +
     2081.  10475}
    +
     2082.      1
    +
     2083.   3305function are_similar(a, b) {
    +
     2084.   3305
    +
     2085.   3305// cause: "0&&0"
    +
     2086.   3305
    +
     2087.   3305    assert_or_throw(a !== b, "Expected a !== b.");
    +
     2088.   3305//  Probably deadcode.
    +
     2089.   3305//  if (a === b) {
    +
     2090.   3305//      return true;
    +
     2091.   3305//  }
    +
     2092.      8    if (Array.isArray(a)) {
    +
     2093.      8        return (
    +
     2094.      8            Array.isArray(b)
    +
     2095.      8            && a.length === b.length
    +
     2096.      8            && a.every(function (value, index) {
    +
     2097.      8
    +
     2098.      8// cause: "`${0}`&&`${0}`"
    +
     2099.      8
    +
     2100.      8                return are_similar(value, b[index]);
    +
     2101.      8            })
    +
     2102.      8        );
    +
     2103.   3297    }
    +
     2104.   3297    assert_or_throw(!Array.isArray(b), "Expected !Array.isArray(b).");
    +
     2105.   3297//  Probably deadcode.
    +
     2106.   3297//  if (Array.isArray(b)) {
    +
     2107.   3297//      return false;
    +
     2108.   3297//  }
    +
     2109.   3297    if (a.id === "(number)" && b.id === "(number)") {
    +
     2110.     32        return a.value === b.value;
    +
     2111.   3265    }
    +
     2112.   3265    let a_string;
    +
     2113.   3265    let b_string;
    +
     2114.   3265    if (a.id === "(string)") {
    +
     2115.    196        a_string = a.value;
    +
     2116.   3069    } else if (a.id === "`" && a.constant) {
    +
     2117.   3069        a_string = a.value[0];
    +
     2118.   3265    }
    +
     2119.   3265    if (b.id === "(string)") {
    +
     2120.   1316        b_string = b.value;
    +
     2121.   1949    } else if (b.id === "`" && b.constant) {
    +
     2122.   1949        b_string = b.value[0];
    +
     2123.   3265    }
    +
     2124.   3265    if (typeof a_string === "string") {
    +
     2125.    196        return a_string === b_string;
    +
     2126.   3069    }
    +
     2127.   3069    if (is_weird(a) || is_weird(b)) {
    +
     2128.      4        return false;
    +
     2129.   3065    }
    +
     2130.   3065    if (a.arity === b.arity && a.id === b.id) {
    +
     2131.    818
    +
     2132.    818// cause: "aa.bb&&aa.bb"
    +
     2133.    818
    +
     2134.    818        if (a.id === ".") {
    +
     2135.    818            return (
    +
     2136.    818                are_similar(a.expression, b.expression)
    +
     2137.    818                && are_similar(a.name, b.name)
    +
     2138.    818            );
    +
     2139.    818        }
    +
     2140.    818
    +
     2141.    818// cause: "+0&&+0"
    +
     2142.    818
    +
     2143.    818        if (a.arity === "unary") {
    +
     2144.    818            return are_similar(a.expression, b.expression);
    +
     2145.    818        }
    +
     2146.    818        if (a.arity === "binary") {
    +
     2147.    818
    +
     2148.    818// cause: "aa[0]&&aa[0]"
    +
     2149.    818
    +
     2150.    818            return (
    +
     2151.    818                a.id !== "("
    +
     2152.    818                && are_similar(a.expression[0], b.expression[0])
    +
     2153.    818                && are_similar(a.expression[1], b.expression[1])
    +
     2154.    818            );
    +
     2155.    818        }
    +
     2156.    818        if (a.arity === "ternary") {
    +
     2157.    818
    +
     2158.    818// cause: "aa=(``?``:``)&&(``?``:``)"
    +
     2159.    818
    +
     2160.    818            return (
    +
     2161.    818                are_similar(a.expression[0], b.expression[0])
    +
     2162.    818                && are_similar(a.expression[1], b.expression[1])
    +
     2163.    818                && are_similar(a.expression[2], b.expression[2])
    +
     2164.    818            );
    +
     2165.    818        }
    +
     2166.    818        assert_or_throw(
    +
     2167.    818            a.arity !== "function" && a.arity !== "regexp",
    +
     2168.    818            "Expected a.arity !== \"function\" && a.arity !== \"regexp\"."
    +
     2169.    818        );
    +
     2170.    818//      Probably deadcode.
    +
     2171.    818//      if (a.arity === "function" && a.arity === "regexp") {
    +
     2172.    818//          return false;
    +
     2173.    818//      }
    +
     2174.    818
    +
     2175.    818// cause: "undefined&&undefined"
    +
     2176.    818
    +
     2177.    818        return true;
    +
     2178.   2247    }
    +
     2179.   2247
    +
     2180.   2247// cause: "null&&undefined"
    +
     2181.   2247
    +
     2182.   2247    return false;
    +
     2183.   2247}
    +
     2184.      1
    +
     2185.   3911function semicolon() {
    +
     2186.   3911
    +
     2187.   3911// Try to match a semicolon.
    +
     2188.   3911
    +
     2189.   3737    if (next_token.id === ";") {
    +
     2190.   3737        advance(";");
    +
     2191.   3737    } else {
    +
     2192.    174
    +
     2193.    174// cause: "0"
    +
     2194.    174// cause: "0\n0"
    +
     2195.    174
    +
     2196.    174        warn_at(
    +
     2197.    174            "expected_a_b",
    +
     2198.    174            token.line,
    +
     2199.    174            token.thru,
    +
     2200.    174            ";",
    +
     2201.    174            artifact(next_token)
    +
     2202.    174        );
    +
     2203.    174    }
    +
     2204.   3911    anon = "anonymous";
    +
     2205.   3911}
    +
     2206.      1
    +
     2207.   6050function statement() {
    +
     2208.   6050
    +
     2209.   6050// Parse a statement. Any statement may have a label, but only four statements
    +
     2210.   6050// have use for one. A statement can be one of the standard statements, or
    +
     2211.   6050// an assignment expression, or an invocation expression.
    +
     2212.   6050
    +
     2213.   6050    let first;
    +
     2214.   6050    let the_label;
    +
     2215.   6050    let the_statement;
    +
     2216.   6050    let the_symbol;
    +
     2217.   6050    advance();
    +
     2218.   5899    if (token.identifier && next_token.id === ":") {
    +
     2219.      9        the_label = token;
    +
     2220.      9        if (the_label.id === "ignore") {
    +
     2221.      9            warn("unexpected_a", the_label);
    +
     2222.      9        }
    +
     2223.      9        advance(":");
    +
     2224.      9        if (
    +
     2225.      9            next_token.id === "do"
    +
     2226.      9            || next_token.id === "for"
    +
     2227.      9            || next_token.id === "switch"
    +
     2228.      9            || next_token.id === "while"
    +
     2229.      9        ) {
    +
     2230.      9            enroll(the_label, "label", true);
    +
     2231.      9            the_label.init = true;
    +
     2232.      9            the_label.dead = false;
    +
     2233.      9            the_statement = statement();
    +
     2234.      9            the_statement.label = the_label;
    +
     2235.      9            the_statement.statement = true;
    +
     2236.      9            return the_statement;
    +
     2237.      9        }
    +
     2238.      9        advance();
    +
     2239.      9
    +
     2240.      9// cause: "aa:"
    +
     2241.      9
    +
     2242.      9        warn("unexpected_label_a", the_label);
    +
     2243.   6045    }
    +
     2244.   6045
    +
     2245.   6045// Parse the statement.
    +
     2246.   6045
    +
     2247.   6045    first = token;
    +
     2248.   6045    first.statement = true;
    +
     2249.   6045    the_symbol = syntax[first.id];
    +
     2250.   6045    if (the_symbol !== undefined && the_symbol.fud !== undefined) {
    +
     2251.   2895        the_symbol.disrupt = false;
    +
     2252.   2895        the_symbol.statement = true;
    +
     2253.   2895        the_statement = the_symbol.fud();
    +
     2254.   3150    } else {
    +
     2255.   3150
    +
     2256.   3150// It is an expression statement.
    +
     2257.   3150
    +
     2258.   3150        the_statement = expression(0, true);
    +
     2259.   3150        if (the_statement.wrapped && the_statement.id !== "(") {
    +
     2260.   3150
    +
     2261.   3150// cause: "(0)"
    +
     2262.   3150
    +
     2263.   3150            warn("unexpected_a", first);
    +
     2264.   3150        }
    +
     2265.   3150        semicolon();
    +
     2266.   5965    }
    +
     2267.   5965    if (the_label !== undefined) {
    +
     2268.      1        the_label.dead = true;
    +
     2269.   5965    }
    +
     2270.   5965    return the_statement;
    +
     2271.   5965}
    +
     2272.      1
    +
     2273.   2390function statements() {
    +
     2274.   2390
    +
     2275.   2390// Parse a list of statements. Give a warning if an unreachable statement
    +
     2276.   2390// follows a disruptive statement.
    +
     2277.   2390
    +
     2278.   2390    const array = [];
    +
     2279.   8214    (function next(disrupt) {
    +
     2280.   8214        if (
    +
     2281.   8214            next_token.id !== "}"
    +
     2282.   6201            && next_token.id !== "case"
    +
     2283.   6194            && next_token.id !== "default"
    +
     2284.   6187            && next_token.id !== "else"
    +
     2285.   6187            && next_token.id !== "(end)"
    +
     2286.   5901        ) {
    +
     2287.   5901            let a_statement = statement();
    +
     2288.   5901            array.push(a_statement);
    +
     2289.   5901            if (disrupt) {
    +
     2290.   5901
    +
     2291.   5901// cause: "while(0){break;0;}"
    +
     2292.   5901
    +
     2293.   5901                warn("unreachable_a", a_statement);
    +
     2294.   5901            }
    +
     2295.   5901            return next(a_statement.disrupt);
    +
     2296.   5901        }
    +
     2297.   8214    }(false));
    +
     2298.   2390    return array;
    +
     2299.   2390}
    +
     2300.      1
    +
     2301.    584function not_top_level(thing) {
    +
     2302.    584
    +
     2303.    584// Some features should not be at the outermost level.
    +
     2304.    584
    +
     2305.     28    if (functionage === global) {
    +
     2306.     28        warn("unexpected_at_top_level_a", thing);
    +
     2307.     28    }
    +
     2308.    584}
    +
     2309.      1
    +
     2310.     22function top_level_only(the_thing) {
    +
     2311.     22
    +
     2312.     22// Some features must be at the most outermost level.
    +
     2313.     22
    +
     2314.      1    if (blockage !== global) {
    +
     2315.      1
    +
     2316.      1// cause: "if(0){import aa from \"aa\";}"
    +
     2317.      1
    +
     2318.      1        warn("misplaced_a", the_thing);
    +
     2319.      1    }
    +
     2320.     22}
    +
     2321.      1
    +
     2322.   2010function block(special) {
    +
     2323.   2010
    +
     2324.   2010// Parse a block, a sequence of statements wrapped in braces.
    +
     2325.   2010//  special "body"      The block is a function body.
    +
     2326.   2010//          "ignore"    No warning on an empty block.
    +
     2327.   2010//          "naked"     No advance.
    +
     2328.   2010//          undefined   An ordinary block.
    +
     2329.   2010
    +
     2330.   2010    let stmts;
    +
     2331.   2010    let the_block;
    +
     2332.   2000    if (special !== "naked") {
    +
     2333.   2000        advance("{");
    +
     2334.   2009    }
    +
     2335.   2009    the_block = token;
    +
     2336.   2009    the_block.arity = "statement";
    +
     2337.   2009    the_block.body = special === "body";
    +
     2338.   2009
    +
     2339.   2009// Top level function bodies may include the "use strict" pragma.
    +
     2340.   2009
    +
     2341.   2009    if (
    +
     2342.   2009        special === "body"
    +
     2343.   2009        && stack.length === 1
    +
     2344.    337        && next_token.value === "use strict"
    +
     2345.      5    ) {
    +
     2346.      5        next_token.statement = true;
    +
     2347.      5        advance("(string)");
    +
     2348.      5        advance(";");
    +
     2349.   2009    }
    +
     2350.   2009    stmts = statements();
    +
     2351.   2009    the_block.block = stmts;
    +
     2352.   2009    if (stmts.length === 0) {
    +
     2353.     54        if (!option.devel && special !== "ignore") {
    +
     2354.     54
    +
     2355.     54// cause: "function aa(){}"
    +
     2356.     54
    +
     2357.     54            warn("empty_block", the_block);
    +
     2358.     54        }
    +
     2359.     54        the_block.disrupt = false;
    +
     2360.   1952    } else {
    +
     2361.   1952        the_block.disrupt = stmts[stmts.length - 1].disrupt;
    +
     2362.   2006    }
    +
     2363.   2006    advance("}");
    +
     2364.   2006    return the_block;
    +
     2365.   2006}
    +
     2366.      1
    +
     2367.   1281function mutation_check(the_thing) {
    +
     2368.   1281
    +
     2369.   1281// The only expressions that may be assigned to are
    +
     2370.   1281//      e.b
    +
     2371.   1281//      e[b]
    +
     2372.   1281//      v
    +
     2373.   1281//      [destructure]
    +
     2374.   1281//      {destructure}
    +
     2375.   1281
    +
     2376.   1281    if (
    +
     2377.   1281        the_thing.arity !== "variable"
    +
     2378.    616        && the_thing.id !== "."
    +
     2379.     53        && the_thing.id !== "["
    +
     2380.      1        && the_thing.id !== "{"
    +
     2381.      1    ) {
    +
     2382.      1
    +
     2383.      1// cause: "0=0"
    +
     2384.      1
    +
     2385.      1        warn("bad_assignment_a", the_thing);
    +
     2386.      1        return false;
    +
     2387.   1280    }
    +
     2388.   1280    return true;
    +
     2389.   1280}
    +
     2390.      1
    +
     2391.   6565function left_check(left, right) {
    +
     2392.   6565
    +
     2393.   6565// Warn if the left is not one of these:
    +
     2394.   6565//      e.b
    +
     2395.   6565//      e[b]
    +
     2396.   6565//      e()
    +
     2397.   6565//      ?:
    +
     2398.   6565//      identifier
    +
     2399.   6565
    +
     2400.   6565    const id = left.id;
    +
     2401.   6565    if (
    +
     2402.   6565        !left.identifier
    +
     2403.   1321        && (
    +
     2404.   1321            left.arity !== "ternary"
    +
     2405.   1321            || (
    +
     2406.   1321                !left_check(left.expression[1])
    +
     2407.   1321                && !left_check(left.expression[2])
    +
     2408.   1321            )
    +
     2409.   1321        )
    +
     2410.   1321        && (
    +
     2411.   1321            left.arity !== "binary"
    +
     2412.   1321            || (id !== "." && id !== "?." && id !== "(" && id !== "[")
    +
     2413.   1321        )
    +
     2414.      8    ) {
    +
     2415.      8        warn("unexpected_a", right);
    +
     2416.      8        return false;
    +
     2417.   6557    }
    +
     2418.   6557    return true;
    +
     2419.   6557}
    +
     2420.      1
    +
     2421.      1// These functions are used to specify the grammar of our language:
    +
     2422.      1
    +
     2423.    129function symbol(id, bp) {
    +
     2424.    129
    +
     2425.    129// Make a symbol if it does not already exist in the language's syntax.
    +
     2426.    129
    +
     2427.    129    let the_symbol = syntax[id];
    +
     2428.    113    if (the_symbol === undefined) {
    +
     2429.    113        the_symbol = empty();
    +
     2430.    113        the_symbol.id = id;
    +
     2431.    113        the_symbol.lbp = bp || 0;
    +
     2432.    113        syntax[id] = the_symbol;
    +
     2433.    113    }
    +
     2434.    129    return the_symbol;
    +
     2435.    129}
    +
     2436.      1
    +
     2437.     12function assignment(id) {
    +
     2438.     12
    +
     2439.     12// Make an assignment operator. The one true assignment is different because
    +
     2440.     12// its left side, when it is a variable, is not treated as an expression.
    +
     2441.     12// That case is special because that is when a variable gets initialized. The
    +
     2442.     12// other assignment operators can modify, but they cannot initialize.
    +
     2443.     12
    +
     2444.     12    const the_symbol = symbol(id, 20);
    +
     2445.   1280    the_symbol.led = function (left) {
    +
     2446.   1280        const the_token = token;
    +
     2447.   1280        let right;
    +
     2448.   1280        the_token.arity = "assignment";
    +
     2449.   1280        right = expression(20 - 1);
    +
     2450.   1159        if (id === "=" && left.arity === "variable") {
    +
     2451.    580            the_token.names = left;
    +
     2452.    580            the_token.expression = right;
    +
     2453.    697        } else {
    +
     2454.    697            the_token.expression = [left, right];
    +
     2455.   1277        }
    +
     2456.   1277        if (
    +
     2457.   1277            right.arity === "assignment"
    +
     2458.   1277            || right.arity === "pre"
    +
     2459.   1275            || right.arity === "post"
    +
     2460.      2        ) {
    +
     2461.      2            warn("unexpected_a", right);
    +
     2462.   1277        }
    +
     2463.   1277        mutation_check(left);
    +
     2464.   1277        return the_token;
    +
     2465.   1277    };
    +
     2466.     12    return the_symbol;
    +
     2467.     12}
    +
     2468.      1
    +
     2469.     16function constant(id, type, value) {
    +
     2470.     16
    +
     2471.     16// Make a constant symbol.
    +
     2472.     16
    +
     2473.     16    const the_symbol = symbol(id);
    +
     2474.     16    the_symbol.constant = true;
    +
     2475.     16    the_symbol.nud = (
    +
     2476.     16        typeof value === "function"
    +
     2477.      7        ? value
    +
     2478.   5588        : function () {
    +
     2479.   5588            token.constant = true;
    +
     2480.    510            if (value !== undefined) {
    +
     2481.    510                token.value = value;
    +
     2482.    510            }
    +
     2483.   5588            return token;
    +
     2484.   5588        }
    +
     2485.     16    );
    +
     2486.     16    the_symbol.type = type;
    +
     2487.     16    the_symbol.value = value;
    +
     2488.     16    return the_symbol;
    +
     2489.     16}
    +
     2490.      1
    +
     2491.     30function infix(id, bp, f) {
    +
     2492.     30
    +
     2493.     30// Make an infix operator.
    +
     2494.     30
    +
     2495.     30    const the_symbol = symbol(id, bp);
    +
     2496.   9318    the_symbol.led = function (left) {
    +
     2497.   9318        const the_token = token;
    +
     2498.   9318        the_token.arity = "binary";
    +
     2499.   6656        if (f !== undefined) {
    +
     2500.   6656            return f(left);
    +
     2501.   6656        }
    +
     2502.   2662        the_token.expression = [left, expression(bp)];
    +
     2503.   2662        return the_token;
    +
     2504.   2662    };
    +
     2505.     30    return the_symbol;
    +
     2506.     30}
    +
     2507.      1
    +
     2508.      1function infixr(id, bp) {
    +
     2509.      1
    +
     2510.      1// Make a right associative infix operator.
    +
     2511.      1
    +
     2512.      1    const the_symbol = symbol(id, bp);
    +
     2513.      1    the_symbol.led = function (left) {
    +
     2514.      1
    +
     2515.      1// cause: "0**0"
    +
     2516.      1
    +
     2517.      1        const the_token = token;
    +
     2518.      1        the_token.arity = "binary";
    +
     2519.      1        the_token.expression = [left, expression(bp - 1)];
    +
     2520.      1        return the_token;
    +
     2521.      1    };
    +
     2522.      1    return the_symbol;
    +
     2523.      1}
    +
     2524.      1
    +
     2525.      2function post(id) {
    +
     2526.      2
    +
     2527.      2// Make one of the post operators.
    +
     2528.      2
    +
     2529.      2    const the_symbol = symbol(id, 150);
    +
     2530.      2    the_symbol.led = function (left) {
    +
     2531.      2        token.expression = left;
    +
     2532.      2        token.arity = "post";
    +
     2533.      2        mutation_check(token.expression);
    +
     2534.      2        return token;
    +
     2535.      2    };
    +
     2536.      2    return the_symbol;
    +
     2537.      2}
    +
     2538.      1
    +
     2539.      2function pre(id) {
    +
     2540.      2
    +
     2541.      2// Make one of the pre operators.
    +
     2542.      2
    +
     2543.      2    const the_symbol = symbol(id);
    +
     2544.      2    the_symbol.nud = function () {
    +
     2545.      2        const the_token = token;
    +
     2546.      2        the_token.arity = "pre";
    +
     2547.      2        the_token.expression = expression(150);
    +
     2548.      2        mutation_check(the_token.expression);
    +
     2549.      2        return the_token;
    +
     2550.      2    };
    +
     2551.      2    return the_symbol;
    +
     2552.      2}
    +
     2553.      1
    +
     2554.     17function prefix(id, f) {
    +
     2555.     17
    +
     2556.     17// Make a prefix operator.
    +
     2557.     17
    +
     2558.     17    const the_symbol = symbol(id);
    +
     2559.   1319    the_symbol.nud = function () {
    +
     2560.   1319        const the_token = token;
    +
     2561.   1319        the_token.arity = "unary";
    +
     2562.   1074        if (typeof f === "function") {
    +
     2563.   1074            return f();
    +
     2564.   1074        }
    +
     2565.    245        the_token.expression = expression(150);
    +
     2566.    245        return the_token;
    +
     2567.    245    };
    +
     2568.     17    return the_symbol;
    +
     2569.     17}
    +
     2570.      1
    +
     2571.     22function stmt(id, f) {
    +
     2572.     22
    +
     2573.     22// Make a statement.
    +
     2574.     22
    +
     2575.     22    const the_symbol = symbol(id);
    +
     2576.   2895    the_symbol.fud = function () {
    +
     2577.   2895        token.arity = "statement";
    +
     2578.   2895        return f();
    +
     2579.   2895    };
    +
     2580.     22    return the_symbol;
    +
     2581.     22}
    +
     2582.      1
    +
     2583.      1function ternary(id1, id2) {
    +
     2584.      1
    +
     2585.      1// Make a ternary operator.
    +
     2586.      1
    +
     2587.      1    const the_symbol = symbol(id1, 30);
    +
     2588.     59    the_symbol.led = function (left) {
    +
     2589.     59        const the_token = token;
    +
     2590.     59        const second = expression(20);
    +
     2591.     59        advance(id2);
    +
     2592.     59        token.arity = "ternary";
    +
     2593.     59        the_token.arity = "ternary";
    +
     2594.     59        the_token.expression = [left, second, expression(10)];
    +
     2595.      1        if (next_token.id !== ")") {
    +
     2596.      1
    +
     2597.      1// cause: "0?0:0"
    +
     2598.      1
    +
     2599.      1            warn("use_open", the_token);
    +
     2600.      1        }
    +
     2601.     59        return the_token;
    +
     2602.     59    };
    +
     2603.      1    return the_symbol;
    +
     2604.      1}
    +
     2605.      1
    +
     2606.      1// Begin defining the language.
    +
     2607.      1
    +
     2608.      1syntax = empty();
    +
     2609.      1
    +
     2610.      1symbol("}");
    +
     2611.      1symbol(")");
    +
     2612.      1symbol("]");
    +
     2613.      1symbol(",");
    +
     2614.      1symbol(";");
    +
     2615.      1symbol(":");
    +
     2616.      1symbol("*/");
    +
     2617.      1symbol("async");
    +
     2618.      1symbol("await");
    +
     2619.      1symbol("case");
    +
     2620.      1symbol("catch");
    +
     2621.      1symbol("class");
    +
     2622.      1symbol("default");
    +
     2623.      1symbol("else");
    +
     2624.      1symbol("enum");
    +
     2625.      1symbol("finally");
    +
     2626.      1symbol("implements");
    +
     2627.      1symbol("interface");
    +
     2628.      1symbol("package");
    +
     2629.      1symbol("private");
    +
     2630.      1symbol("protected");
    +
     2631.      1symbol("public");
    +
     2632.      1symbol("static");
    +
     2633.      1symbol("super");
    +
     2634.      1symbol("void");
    +
     2635.      1symbol("yield");
    +
     2636.      1
    +
     2637.      1constant("(number)", "number");
    +
     2638.      1constant("(regexp)", "regexp");
    +
     2639.      1constant("(string)", "string");
    +
     2640.      1constant("arguments", "object", function () {
    +
     2641.      1    warn("unexpected_a", token);
    +
     2642.      1    return token;
    +
     2643.      1});
    +
     2644.      4constant("eval", "function", function () {
    +
     2645.      2    if (!option.eval) {
    +
     2646.      2
    +
     2647.      2// cause: "eval()"
    +
     2648.      2
    +
     2649.      2        warn("unexpected_a", token);
    +
     2650.      2    } else if (next_token.id !== "(") {
    +
     2651.      2
    +
     2652.      2// cause: "/*jslint eval*/\neval"
    +
     2653.      2
    +
     2654.      2        warn("expected_a_before_b", next_token, "(", artifact());
    +
     2655.      2    }
    +
     2656.      4    return token;
    +
     2657.      4});
    +
     2658.      1constant("false", "boolean", false);
    +
     2659.      4constant("Function", "function", function () {
    +
     2660.      2    if (!option.eval) {
    +
     2661.      2
    +
     2662.      2// cause: "Function()"
    +
     2663.      2
    +
     2664.      2        warn("unexpected_a", token);
    +
     2665.      2    } else if (next_token.id !== "(") {
    +
     2666.      2
    +
     2667.      2// cause: "/*jslint eval*/\nFunction"
    +
     2668.      2
    +
     2669.      2        warn("expected_a_before_b", next_token, "(", artifact());
    +
     2670.      2    }
    +
     2671.      4    return token;
    +
     2672.      4});
    +
     2673.      1constant("ignore", "undefined", function () {
    +
     2674.      1    warn("unexpected_a", token);
    +
     2675.      1    return token;
    +
     2676.      1});
    +
     2677.      1constant("Infinity", "number", Infinity);
    +
     2678.      1constant("isFinite", "function", function () {
    +
     2679.      1    warn("expected_a_b", token, "Number.isFinite", "isFinite");
    +
     2680.      1    return token;
    +
     2681.      1});
    +
     2682.      1constant("isNaN", "function", function () {
    +
     2683.      1
    +
     2684.      1// cause: "isNaN(0)"
    +
     2685.      1
    +
     2686.      1    warn("number_isNaN", token);
    +
     2687.      1    return token;
    +
     2688.      1});
    +
     2689.      1constant("NaN", "number", NaN);
    +
     2690.      1constant("null", "null", null);
    +
     2691.      2constant("this", "object", function () {
    +
     2692.      1    if (!option.this) {
    +
     2693.      1        warn("unexpected_a", token);
    +
     2694.      1    }
    +
     2695.      2    return token;
    +
     2696.      2});
    +
     2697.      1constant("true", "boolean", true);
    +
     2698.      1constant("undefined", "undefined");
    +
     2699.      1
    +
     2700.      1assignment("=");
    +
     2701.      1assignment("+=");
    +
     2702.      1assignment("-=");
    +
     2703.      1assignment("*=");
    +
     2704.      1assignment("/=");
    +
     2705.      1assignment("%=");
    +
     2706.      1assignment("&=");
    +
     2707.      1assignment("|=");
    +
     2708.      1assignment("^=");
    +
     2709.      1assignment("<<=");
    +
     2710.      1assignment(">>=");
    +
     2711.      1assignment(">>>=");
    +
     2712.      1
    +
     2713.      1infix("??", 35);
    +
     2714.      1infix("||", 40);
    +
     2715.      1infix("&&", 50);
    +
     2716.      1infix("|", 70);
    +
     2717.      1infix("^", 80);
    +
     2718.      1infix("&", 90);
    +
     2719.      1infix("==", 100);
    +
     2720.      1infix("===", 100);
    +
     2721.      1infix("!=", 100);
    +
     2722.      1infix("!==", 100);
    +
     2723.      1infix("<", 110);
    +
     2724.      1infix(">", 110);
    +
     2725.      1infix("<=", 110);
    +
     2726.      1infix(">=", 110);
    +
     2727.      1infix("in", 110);
    +
     2728.      1infix("instanceof", 110);
    +
     2729.      1infix("<<", 120);
    +
     2730.      1infix(">>", 120);
    +
     2731.      1infix(">>>", 120);
    +
     2732.      1infix("+", 130);
    +
     2733.      1infix("-", 130);
    +
     2734.      1infix("*", 140);
    +
     2735.      1infix("/", 140);
    +
     2736.      1infix("%", 140);
    +
     2737.      1infixr("**", 150);
    +
     2738.   2878infix("(", 160, function (left) {
    +
     2739.   2878    const the_paren = token;
    +
     2740.   2878    let the_argument;
    +
     2741.   2813    if (left.id !== "function") {
    +
     2742.   2813        left_check(left, the_paren);
    +
     2743.   2813    }
    +
     2744.    952    if (functionage.arity === "statement" && left.identifier) {
    +
     2745.    700        functionage.name.calls[left.id] = left;
    +
     2746.    700    }
    +
     2747.   2878    the_paren.expression = [left];
    +
     2748.   2333    if (next_token.id !== ")") {
    +
     2749.   3905        (function next() {
    +
     2750.   3905            let ellipsis;
    +
     2751.   2333            if (next_token.id === "...") {
    +
     2752.   2333                ellipsis = true;
    +
     2753.   2333                advance("...");
    +
     2754.   2333            }
    +
     2755.   3905            the_argument = expression(10);
    +
     2756.   2333            if (ellipsis) {
    +
     2757.   2333                the_argument.ellipsis = true;
    +
     2758.   2333            }
    +
     2759.   3905            the_paren.expression.push(the_argument);
    +
     2760.   2333            if (next_token.id === ",") {
    +
     2761.   2333                advance(",");
    +
     2762.   2333                return next();
    +
     2763.   2333            }
    +
     2764.   3905        }());
    +
     2765.   2333    }
    +
     2766.   2878    advance(")", the_paren);
    +
     2767.   1336    if (the_paren.expression.length === 2) {
    +
     2768.   1336
    +
     2769.   1336// cause: "aa(0)"
    +
     2770.   1336
    +
     2771.   1336        the_paren.free = true;
    +
     2772.   1336        if (the_argument.wrapped === true) {
    +
     2773.   1336            warn("unexpected_a", the_paren);
    +
     2774.   1336        }
    +
     2775.   1336        if (the_argument.id === "(") {
    +
     2776.   1336            the_argument.wrapped = true;
    +
     2777.   1336        }
    +
     2778.   1542    } else {
    +
     2779.   1542
    +
     2780.   1542// cause: "aa()"
    +
     2781.   1542// cause: "aa(0,0)"
    +
     2782.   1542
    +
     2783.   1542        the_paren.free = false;
    +
     2784.   1542    }
    +
     2785.   2878    return the_paren;
    +
     2786.   2878});
    +
     2787.   3329infix(".", 170, function (left) {
    +
     2788.   3329    const the_token = token;
    +
     2789.   3329    const name = next_token;
    +
     2790.   3329    if (
    +
     2791.   3329        (
    +
     2792.   3329            left.id !== "(string)"
    +
     2793.     10            || (name.id !== "indexOf" && name.id !== "repeat")
    +
     2794.   3329        )
    +
     2795.   3319        && (
    +
     2796.   3319            left.id !== "["
    +
     2797.   3319            || (
    +
     2798.   3319                name.id !== "concat"
    +
     2799.   3319                && name.id !== "forEach"
    +
     2800.   3319                && name.id !== "join"
    +
     2801.   3319                && name.id !== "map"
    +
     2802.   3319            )
    +
     2803.   3319        )
    +
     2804.   3316        && (left.id !== "+" || name.id !== "slice")
    +
     2805.   3314        && (
    +
     2806.   3314            left.id !== "(regexp)"
    +
     2807.   3314            || (name.id !== "exec" && name.id !== "test")
    +
     2808.   3314        )
    +
     2809.   3302    ) {
    +
     2810.   3302        left_check(left, the_token);
    +
     2811.   3302    }
    +
     2812.      1    if (!name.identifier) {
    +
     2813.      1
    +
     2814.      1// cause: "aa.0"
    +
     2815.      1
    +
     2816.      1        stop("expected_identifier_a");
    +
     2817.   3328    }
    +
     2818.   3328    advance();
    +
     2819.   3328    survey(name);
    +
     2820.   3328
    +
     2821.   3328// The property name is not an expression.
    +
     2822.   3328
    +
     2823.   3328    the_token.name = name;
    +
     2824.   3328    the_token.expression = left;
    +
     2825.   3328    return the_token;
    +
     2826.   3328});
    +
     2827.      7infix("?.", 170, function (left) {
    +
     2828.      7    const the_token = token;
    +
     2829.      7    const name = next_token;
    +
     2830.      7    if (
    +
     2831.      7        (
    +
     2832.      7            left.id !== "(string)"
    +
     2833.      1            || (name.id !== "indexOf" && name.id !== "repeat")
    +
     2834.      7        )
    +
     2835.      7        && (
    +
     2836.      7            left.id !== "["
    +
     2837.      1            || (
    +
     2838.      1                name.id !== "concat"
    +
     2839.      1                && name.id !== "forEach"
    +
     2840.      1                && name.id !== "join"
    +
     2841.      1                && name.id !== "map"
    +
     2842.      1            )
    +
     2843.      7        )
    +
     2844.      7
    +
     2845.      7// cause: "(0+0)?.0"
    +
     2846.      7
    +
     2847.      1        && (left.id !== "+" || name.id !== "slice")
    +
     2848.      7        && (
    +
     2849.      7            left.id !== "(regexp)"
    +
     2850.      1            || (name.id !== "exec" && name.id !== "test")
    +
     2851.      7        )
    +
     2852.      7    ) {
    +
     2853.      7        left_check(left, the_token);
    +
     2854.      7    }
    +
     2855.      2    if (!name.identifier) {
    +
     2856.      2
    +
     2857.      2// cause: "aa?.0"
    +
     2858.      2
    +
     2859.      2        stop("expected_identifier_a");
    +
     2860.      5    }
    +
     2861.      5    advance();
    +
     2862.      5    survey(name);
    +
     2863.      5
    +
     2864.      5// The property name is not an expression.
    +
     2865.      5
    +
     2866.      5    the_token.name = name;
    +
     2867.      5    the_token.expression = left;
    +
     2868.      5    return the_token;
    +
     2869.      5});
    +
     2870.    417infix("[", 170, function (left) {
    +
     2871.    417    const the_token = token;
    +
     2872.    417    const the_subscript = expression(0);
    +
     2873.    415    if (the_subscript.id === "(string)" || the_subscript.id === "`") {
    +
     2874.      4        const name = survey(the_subscript);
    +
     2875.      4        if (rx_identifier.test(name)) {
    +
     2876.      4
    +
     2877.      4// cause: "aa[`aa`]"
    +
     2878.      4
    +
     2879.      4            warn("subscript_a", the_subscript, name);
    +
     2880.      4        }
    +
     2881.      4    }
    +
     2882.    417    left_check(left, the_token);
    +
     2883.    417    the_token.expression = [left, the_subscript];
    +
     2884.    417    advance("]");
    +
     2885.    417    return the_token;
    +
     2886.    417});
    +
     2887.      1infix("=>", 170, function (left) {
    +
     2888.      1
    +
     2889.      1// cause: "aa=>0"
    +
     2890.      1
    +
     2891.      1    return stop("wrap_parameter", left);
    +
     2892.      1});
    +
     2893.      1
    +
     2894.     58function do_tick() {
    +
     2895.     58    const the_tick = token;
    +
     2896.     58    the_tick.value = [];
    +
     2897.     58    the_tick.expression = [];
    +
     2898.     58    if (next_token.id !== "`") {
    +
     2899.     94        (function part() {
    +
     2900.     94            advance("(string)");
    +
     2901.     94            the_tick.value.push(token);
    +
     2902.     36            if (next_token.id === "${") {
    +
     2903.     36                advance("${");
    +
     2904.     36                the_tick.expression.push(expression(0));
    +
     2905.     36                advance("}");
    +
     2906.     36                return part();
    +
     2907.     36            }
    +
     2908.     94        }());
    +
     2909.     58    }
    +
     2910.     58    advance("`");
    +
     2911.     58    return the_tick;
    +
     2912.     58}
    +
     2913.      1
    +
     2914.     24infix("`", 160, function (left) {
    +
     2915.     24    const the_tick = do_tick();
    +
     2916.     24    left_check(left, the_tick);
    +
     2917.     24    the_tick.expression = [left].concat(the_tick.expression);
    +
     2918.     24    return the_tick;
    +
     2919.     24});
    +
     2920.      1
    +
     2921.      1post("++");
    +
     2922.      1post("--");
    +
     2923.      1pre("++");
    +
     2924.      1pre("--");
    +
     2925.      1
    +
     2926.      1prefix("+");
    +
     2927.      1prefix("-");
    +
     2928.      1prefix("~");
    +
     2929.      1prefix("!");
    +
     2930.      1prefix("!!");
    +
     2931.    156prefix("[", function () {
    +
     2932.    156    const the_token = token;
    +
     2933.    156    the_token.expression = [];
    +
     2934.     83    if (next_token.id !== "]") {
    +
     2935.    596        (function next() {
    +
     2936.    596            let element;
    +
     2937.    596            let ellipsis = false;
    +
     2938.     83            if (next_token.id === "...") {
    +
     2939.     83                ellipsis = true;
    +
     2940.     83                advance("...");
    +
     2941.     83            }
    +
     2942.    596            element = expression(10);
    +
     2943.     83            if (ellipsis) {
    +
     2944.     83                element.ellipsis = true;
    +
     2945.     83            }
    +
     2946.    596            the_token.expression.push(element);
    +
     2947.    513            if (next_token.id === ",") {
    +
     2948.    513                advance(",");
    +
     2949.    513                return next();
    +
     2950.    513            }
    +
     2951.    596        }());
    +
     2952.     83    }
    +
     2953.    156    advance("]");
    +
     2954.    156    return the_token;
    +
     2955.    156});
    +
     2956.      1prefix("/=", function () {
    +
     2957.      1
    +
     2958.      1// cause: "/="
    +
     2959.      1
    +
     2960.      1    stop("expected_a_b", token, "/\\=", "/=");
    +
     2961.      1});
    +
     2962.      1prefix("=>", function () {
    +
     2963.      1
    +
     2964.      1// cause: "=>0"
    +
     2965.      1
    +
     2966.      1    return stop("expected_a_before_b", token, "()", "=>");
    +
     2967.      1});
    +
     2968.     30prefix("new", function () {
    +
     2969.     30    const the_new = token;
    +
     2970.     30    const right = expression(160);
    +
     2971.      1    if (next_token.id !== "(") {
    +
     2972.      1
    +
     2973.      1// cause: "new aa"
    +
     2974.      1
    +
     2975.      1        warn("expected_a_before_b", next_token, "()", artifact(next_token));
    +
     2976.      1    }
    +
     2977.     30    the_new.expression = right;
    +
     2978.     30    return the_new;
    +
     2979.     30});
    +
     2980.      1prefix("typeof");
    +
     2981.      2prefix("void", function () {
    +
     2982.      2    const the_void = token;
    +
     2983.      2
    +
     2984.      2// cause: "void"
    +
     2985.      2
    +
     2986.      2    warn("unexpected_a", the_void);
    +
     2987.      2    the_void.expression = expression(0);
    +
     2988.      2    return the_void;
    +
     2989.      2});
    +
     2990.      1
    +
     2991.    603function parameter_list() {
    +
     2992.    603    const list = [];
    +
     2993.    603    let optional;
    +
     2994.    603    const signature = ["("];
    +
     2995.    338    if (next_token.id !== ")" && next_token.id !== "(end)") {
    +
     2996.    485        (function parameter() {
    +
     2997.    485            let ellipsis = false;
    +
     2998.    485            let param;
    +
     2999.    338            if (next_token.id === "{") {
    +
     3000.    338                let a;
    +
     3001.    338                let b = "";
    +
     3002.    338                if (optional !== undefined) {
    +
     3003.    338                    warn(
    +
     3004.    338                        "required_a_optional_b",
    +
     3005.    338                        next_token,
    +
     3006.    338                        next_token.id,
    +
     3007.    338                        optional.id
    +
     3008.    338                    );
    +
     3009.    338                }
    +
     3010.    338                param = next_token;
    +
     3011.    338                param.names = [];
    +
     3012.    338                advance("{");
    +
     3013.    338                signature.push("{");
    +
     3014.    338                (function subparameter() {
    +
     3015.    338                    let subparam = next_token;
    +
     3016.    338                    if (!subparam.identifier) {
    +
     3017.    338                        return stop("expected_identifier_a");
    +
     3018.    338                    }
    +
     3019.    338                    survey(subparam);
    +
     3020.    338                    a = b;
    +
     3021.    338                    b = String(subparam.value || subparam.id);
    +
     3022.    338                    if (a > b) {
    +
     3023.    338                        if (!option.unordered) {
    +
     3024.    338
    +
     3025.    338// cause: "function aa({bb,aa}){}"
    +
     3026.    338
    +
     3027.    338                            warn("unordered_param_a", subparam);
    +
     3028.    338                        }
    +
     3029.    338                    }
    +
     3030.    338                    advance();
    +
     3031.    338                    signature.push(subparam.id);
    +
     3032.    338                    if (next_token.id === ":") {
    +
     3033.    338                        advance(":");
    +
     3034.    338                        advance();
    +
     3035.    338                        token.label = subparam;
    +
     3036.    338                        subparam = token;
    +
     3037.    338                        if (!subparam.identifier) {
    +
     3038.    338                            return stop("expected_identifier_a");
    +
     3039.    338                        }
    +
     3040.    338                    }
    +
     3041.    338                    if (next_token.id === "=") {
    +
     3042.    338                        advance("=");
    +
     3043.    338                        subparam.expression = expression();
    +
     3044.    338                        param.open = true;
    +
     3045.    338                    }
    +
     3046.    338                    param.names.push(subparam);
    +
     3047.    338                    if (next_token.id === ",") {
    +
     3048.    338                        advance(",");
    +
     3049.    338                        signature.push(", ");
    +
     3050.    338                        return subparameter();
    +
     3051.    338                    }
    +
     3052.    338                }());
    +
     3053.    338                list.push(param);
    +
     3054.    338                advance("}");
    +
     3055.    338                signature.push("}");
    +
     3056.    338                if (next_token.id === ",") {
    +
     3057.    338                    advance(",");
    +
     3058.    338                    signature.push(", ");
    +
     3059.    338                    return parameter();
    +
     3060.    338                }
    +
     3061.    458            } else if (next_token.id === "[") {
    +
     3062.    458                if (optional !== undefined) {
    +
     3063.    458                    warn(
    +
     3064.    458                        "required_a_optional_b",
    +
     3065.    458                        next_token,
    +
     3066.    458                        next_token.id,
    +
     3067.    458                        optional.id
    +
     3068.    458                    );
    +
     3069.    458                }
    +
     3070.    458                param = next_token;
    +
     3071.    458                param.names = [];
    +
     3072.    458                advance("[");
    +
     3073.    458                signature.push("[]");
    +
     3074.    458                (function subparameter() {
    +
     3075.    458                    const subparam = next_token;
    +
     3076.    458                    if (!subparam.identifier) {
    +
     3077.    458                        return stop("expected_identifier_a");
    +
     3078.    458                    }
    +
     3079.    458                    advance();
    +
     3080.    458                    param.names.push(subparam);
    +
     3081.    458                    if (next_token.id === "=") {
    +
     3082.    458                        advance("=");
    +
     3083.    458                        subparam.expression = expression();
    +
     3084.    458                        param.open = true;
    +
     3085.    458                    }
    +
     3086.    458                    if (next_token.id === ",") {
    +
     3087.    458                        advance(",");
    +
     3088.    458                        return subparameter();
    +
     3089.    458                    }
    +
     3090.    458                }());
    +
     3091.    458                list.push(param);
    +
     3092.    458                advance("]");
    +
     3093.    458                if (next_token.id === ",") {
    +
     3094.    458                    advance(",");
    +
     3095.    458                    signature.push(", ");
    +
     3096.    458                    return parameter();
    +
     3097.    458                }
    +
     3098.    458            } else {
    +
     3099.    458                if (next_token.id === "...") {
    +
     3100.    458                    ellipsis = true;
    +
     3101.    458                    signature.push("...");
    +
     3102.    458                    advance("...");
    +
     3103.    458                    if (optional !== undefined) {
    +
     3104.    458                        warn(
    +
     3105.    458                            "required_a_optional_b",
    +
     3106.    458                            next_token,
    +
     3107.    458                            next_token.id,
    +
     3108.    458                            optional.id
    +
     3109.    458                        );
    +
     3110.    458                    }
    +
     3111.    458                }
    +
     3112.    458                if (!next_token.identifier) {
    +
     3113.    458                    return stop("expected_identifier_a");
    +
     3114.    458                }
    +
     3115.    458                param = next_token;
    +
     3116.    458                list.push(param);
    +
     3117.    458                advance();
    +
     3118.    458                signature.push(param.id);
    +
     3119.    458                if (ellipsis) {
    +
     3120.    458                    param.ellipsis = true;
    +
     3121.    458                } else {
    +
     3122.    458                    if (next_token.id === "=") {
    +
     3123.    458                        optional = param;
    +
     3124.    458                        advance("=");
    +
     3125.    458                        param.expression = expression(0);
    +
     3126.    458                    } else {
    +
     3127.    458                        if (optional !== undefined) {
    +
     3128.    458                            warn(
    +
     3129.    458                                "required_a_optional_b",
    +
     3130.    458                                param,
    +
     3131.    458                                param.id,
    +
     3132.    458                                optional.id
    +
     3133.    458                            );
    +
     3134.    458                        }
    +
     3135.    458                    }
    +
     3136.    458                    if (next_token.id === ",") {
    +
     3137.    458                        advance(",");
    +
     3138.    458                        signature.push(", ");
    +
     3139.    458                        return parameter();
    +
     3140.    458                    }
    +
     3141.    458                }
    +
     3142.    458            }
    +
     3143.    485        }());
    +
     3144.    597    }
    +
     3145.    597    advance(")");
    +
     3146.    597    signature.push(")");
    +
     3147.    597    return [list, signature.join("")];
    +
     3148.    597}
    +
     3149.      1
    +
     3150.    600function do_function(the_function) {
    +
     3151.    600    let name;
    +
     3152.    590    if (the_function === undefined) {
    +
     3153.    590        the_function = token;
    +
     3154.    590
    +
     3155.    590// A function statement must have a name that will be in the parent's scope.
    +
     3156.    590
    +
     3157.    590        if (the_function.arity === "statement") {
    +
     3158.    590            if (!next_token.identifier) {
    +
     3159.    590
    +
     3160.    590// cause: "function*aa(){}"
    +
     3161.    590
    +
     3162.    590                return stop("expected_identifier_a", next_token);
    +
     3163.    590            }
    +
     3164.    590            name = next_token;
    +
     3165.    590            enroll(name, "variable", true);
    +
     3166.    590            the_function.name = name;
    +
     3167.    590            name.init = true;
    +
     3168.    590            name.calls = empty();
    +
     3169.    590            advance();
    +
     3170.    590        } else if (name === undefined) {
    +
     3171.    590
    +
     3172.    590// A function expression may have an optional name.
    +
     3173.    590
    +
     3174.    590            if (next_token.identifier) {
    +
     3175.    590                name = next_token;
    +
     3176.    590                the_function.name = name;
    +
     3177.    590                advance();
    +
     3178.    590            } else {
    +
     3179.    590                the_function.name = anon;
    +
     3180.    590            }
    +
     3181.    590        }
    +
     3182.    590    } else {
    +
     3183.     10        name = the_function.name;
    +
     3184.    596    }
    +
     3185.    596    the_function.level = functionage.level + 1;
    +
     3186.    596    assert_or_throw(!mega_mode, "Expected !mega_mode.");
    +
     3187.    596//  Probably deadcode.
    +
     3188.    596//  if (mega_mode) {
    +
     3189.    596//      warn("unexpected_a", the_function);
    +
     3190.    596//  }
    +
     3191.    596
    +
     3192.    596// Don't make functions in loops. It is inefficient, and it can lead to scoping
    +
     3193.    596// errors.
    +
     3194.    596
    +
     3195.    596    if (functionage.loop > 0) {
    +
     3196.      1
    +
     3197.      1// cause: "while(0){aa.map(function(){});}"
    +
     3198.      1
    +
     3199.      1        warn("function_in_loop", the_function);
    +
     3200.    596    }
    +
     3201.    596
    +
     3202.    596// Give the function properties for storing its names and for observing the
    +
     3203.    596// depth of loops and switches.
    +
     3204.    596
    +
     3205.    596    the_function.context = empty();
    +
     3206.    596    the_function.finally = 0;
    +
     3207.    596    the_function.loop = 0;
    +
     3208.    596    the_function.switch = 0;
    +
     3209.    596    the_function.try = 0;
    +
     3210.    596
    +
     3211.    596// Push the current function context and establish a new one.
    +
     3212.    596
    +
     3213.    596    stack.push(functionage);
    +
     3214.    596    functions.push(the_function);
    +
     3215.    596    functionage = the_function;
    +
     3216.    596    if (the_function.arity !== "statement" && typeof name === "object") {
    +
     3217.     59        enroll(name, "function", true);
    +
     3218.     59        name.dead = false;
    +
     3219.     59        name.init = true;
    +
     3220.     59        name.used = 1;
    +
     3221.    596    }
    +
     3222.    596
    +
     3223.    596// Parse the parameter list.
    +
     3224.    596
    +
     3225.    596    advance("(");
    +
     3226.    596
    +
     3227.    596// cause: "function(){}"
    +
     3228.    596
    +
     3229.    596    token.free = false;
    +
     3230.    596    token.arity = "function";
    +
     3231.    596    [functionage.parameters, functionage.signature] = parameter_list();
    +
     3232.    596    functionage.parameters.forEach(function enroll_parameter(name) {
    +
     3233.    596        if (name.identifier) {
    +
     3234.    596            enroll(name, "parameter", false);
    +
     3235.    596        } else {
    +
     3236.    596            name.names.forEach(enroll_parameter);
    +
     3237.    596        }
    +
     3238.    596    });
    +
     3239.    596
    +
     3240.    596// The function's body is a block.
    +
     3241.    596
    +
     3242.    596    the_function.block = block("body");
    +
     3243.    596    if (
    +
     3244.    596        the_function.arity === "statement"
    +
     3245.    596        && next_token.line === token.line
    +
     3246.      2    ) {
    +
     3247.      2        return stop("unexpected_a", next_token);
    +
     3248.    586    }
    +
     3249.    586    if (
    +
     3250.    586        next_token.id === "."
    +
     3251.    586        || next_token.id === "?."
    +
     3252.    586        || next_token.id === "["
    +
     3253.      1    ) {
    +
     3254.      1        warn("unexpected_a");
    +
     3255.    586    }
    +
     3256.    586
    +
     3257.    586// Restore the previous context.
    +
     3258.    586
    +
     3259.    586    functionage = stack.pop();
    +
     3260.    586    return the_function;
    +
     3261.    586}
    +
     3262.      1
    +
     3263.     16function do_async() {
    +
     3264.     16    let the_async;
    +
     3265.     16    let the_function;
    +
     3266.     16    the_async = token;
    +
     3267.     16    advance("function");
    +
     3268.     16    the_function = token;
    +
     3269.     16    the_function.is_async = true;
    +
     3270.     16    the_function.arity = the_async.arity;
    +
     3271.     16    do_function();
    +
     3272.      1    if (!the_function.has_await) {
    +
     3273.      1
    +
     3274.      1// cause: "async function aa(){}"
    +
     3275.      1
    +
     3276.      1        warn("missing_await_statement", the_function);
    +
     3277.     15    }
    +
     3278.     15    return the_function;
    +
     3279.     15}
    +
     3280.      1
    +
     3281.      1prefix("async", do_async);
    +
     3282.      1prefix("function", do_function);
    +
     3283.     28prefix("await", function () {
    +
     3284.     28    let the_await;
    +
     3285.     28    the_await = token;
    +
     3286.      2    if (!functionage.is_async) {
    +
     3287.      2
    +
     3288.      2// cause: "function aa(){await 0;}"
    +
     3289.      2
    +
     3290.      2        return stop("unexpected_a", the_await);
    +
     3291.     26    }
    +
     3292.     26    functionage.has_await = true;
    +
     3293.     26    the_await.expression = expression(150);
    +
     3294.     26    return the_await;
    +
     3295.     26});
    +
     3296.      1
    +
     3297.      8function fart(pl) {
    +
     3298.      8    advance("=>");
    +
     3299.      8    const the_fart = token;
    +
     3300.      8    the_fart.arity = "binary";
    +
     3301.      8    the_fart.name = "=>";
    +
     3302.      8    the_fart.level = functionage.level + 1;
    +
     3303.      8    functions.push(the_fart);
    +
     3304.      1    if (functionage.loop > 0) {
    +
     3305.      1
    +
     3306.      1// cause: "while(0){aa.map(()=>0);}"
    +
     3307.      1
    +
     3308.      1        warn("function_in_loop", the_fart);
    +
     3309.      6    }
    +
     3310.      6
    +
     3311.      6// Give the function properties storing its names and for observing the depth
    +
     3312.      6// of loops and switches.
    +
     3313.      6
    +
     3314.      6    the_fart.context = empty();
    +
     3315.      6    the_fart.finally = 0;
    +
     3316.      6    the_fart.loop = 0;
    +
     3317.      6    the_fart.switch = 0;
    +
     3318.      6    the_fart.try = 0;
    +
     3319.      6
    +
     3320.      6// Push the current function context and establish a new one.
    +
     3321.      6
    +
     3322.      6    stack.push(functionage);
    +
     3323.      6    functionage = the_fart;
    +
     3324.      6    the_fart.parameters = pl[0];
    +
     3325.      6    the_fart.signature = pl[1];
    +
     3326.      6    the_fart.parameters.forEach(function (name) {
    +
     3327.      6        enroll(name, "parameter", true);
    +
     3328.      6    });
    +
     3329.      6    if (next_token.id === "{") {
    +
     3330.      1        warn("expected_a_b", the_fart, "function", "=>");
    +
     3331.      1        the_fart.block = block("body");
    +
     3332.      5    } else {
    +
     3333.      5        the_fart.expression = expression(0);
    +
     3334.      6    }
    +
     3335.      6    functionage = stack.pop();
    +
     3336.      6    return the_fart;
    +
     3337.      6}
    +
     3338.      1
    +
     3339.    395prefix("(", function () {
    +
     3340.    395    const the_paren = token;
    +
     3341.    395    let the_value;
    +
     3342.    395    const cadet = lookahead().id;
    +
     3343.    395
    +
     3344.    395// We can distinguish between a parameter list for => and a wrapped expression
    +
     3345.    395// with one token of lookahead.
    +
     3346.    395
    +
     3347.    395    if (
    +
     3348.    395        next_token.id === ")"
    +
     3349.    388        || next_token.id === "..."
    +
     3350.    388        || (next_token.identifier && (cadet === "," || cadet === "="))
    +
     3351.      7    ) {
    +
     3352.      7
    +
     3353.      7// cause: "()=>0"
    +
     3354.      7
    +
     3355.      7        the_paren.free = false;
    +
     3356.      7        return fart(parameter_list());
    +
     3357.    388    }
    +
     3358.    388
    +
     3359.    388// cause: "(0)"
    +
     3360.    388
    +
     3361.    388    the_paren.free = true;
    +
     3362.    388    the_value = expression(0);
    +
     3363.    388    if (the_value.wrapped === true) {
    +
     3364.      2
    +
     3365.      2// cause: "((0))"
    +
     3366.      2
    +
     3367.      2        warn("unexpected_a", the_paren);
    +
     3368.    387    }
    +
     3369.    387    the_value.wrapped = true;
    +
     3370.    387    advance(")", the_paren);
    +
     3371.    387    if (next_token.id === "=>") {
    +
     3372.      3        if (the_value.arity !== "variable") {
    +
     3373.      3            if (the_value.id === "{" || the_value.id === "[") {
    +
     3374.      3                warn("expected_a_before_b", the_paren, "function", "(");
    +
     3375.      3                return stop("expected_a_b", next_token, "{", "=>");
    +
     3376.      3            }
    +
     3377.      3
    +
     3378.      3// cause: "(0)=>0"
    +
     3379.      3
    +
     3380.      3            return stop("expected_identifier_a", the_value);
    +
     3381.      3        }
    +
     3382.      3        the_paren.expression = [the_value];
    +
     3383.      3        return fart([the_paren.expression, "(" + the_value.id + ")"]);
    +
     3384.    383    }
    +
     3385.    383    return the_value;
    +
     3386.    383});
    +
     3387.      1prefix("`", do_tick);
    +
     3388.     89prefix("{", function () {
    +
     3389.     89    const the_brace = token;
    +
     3390.     89    const seen = empty();
    +
     3391.     89    the_brace.expression = [];
    +
     3392.     83    if (next_token.id !== "}") {
    +
     3393.     83        let a;
    +
     3394.     83        let b = "";
    +
     3395.    491        (function member() {
    +
     3396.    491            let extra;
    +
     3397.    491            let full;
    +
     3398.    491            let id;
    +
     3399.    491            let name = next_token;
    +
     3400.    491            let value;
    +
     3401.    491            advance();
    +
     3402.    491            a = b;
    +
     3403.    482            b = String(name.value || name.id);
    +
     3404.     83            if (a > b) {
    +
     3405.     83                if (!option.unordered) {
    +
     3406.     83
    +
     3407.     83// cause: "aa={bb,aa}"
    +
     3408.     83
    +
     3409.     83                    warn("unordered_property_a", name);
    +
     3410.     83                }
    +
     3411.     83            }
    +
     3412.    491            if (
    +
     3413.    483                (name.id === "get" || name.id === "set")
    +
     3414.     83                && next_token.identifier
    +
     3415.     83            ) {
    +
     3416.     83                if (!option.getset) {
    +
     3417.     83
    +
     3418.     83// cause: "aa={get aa(){}}"
    +
     3419.     83
    +
     3420.     83                    warn("unexpected_a", name);
    +
     3421.     83                }
    +
     3422.     83                extra = name.id;
    +
     3423.     83                full = extra + " " + next_token.id;
    +
     3424.     83                name = next_token;
    +
     3425.     83                advance();
    +
     3426.     83                id = survey(name);
    +
     3427.     83                if (seen[full] === true || seen[id] === true) {
    +
     3428.     83
    +
     3429.     83// cause: "aa={get aa(){},get aa(){}}"
    +
     3430.     83
    +
     3431.     83                    warn("duplicate_a", name);
    +
     3432.     83                }
    +
     3433.     83                seen[id] = false;
    +
     3434.     83                seen[full] = true;
    +
     3435.    480            } else {
    +
     3436.    480                id = survey(name);
    +
     3437.    480                if (typeof seen[id] === "boolean") {
    +
     3438.    480
    +
     3439.    480// cause: "aa={aa,aa}"
    +
     3440.    480
    +
     3441.    480                    warn("duplicate_a", name);
    +
     3442.    480                }
    +
     3443.    480                seen[id] = true;
    +
     3444.    490            }
    +
     3445.    490            if (name.identifier) {
    +
     3446.    482                if (next_token.id === "}" || next_token.id === ",") {
    +
     3447.    482                    if (typeof extra === "string") {
    +
     3448.    482
    +
     3449.    482// cause: "aa={get aa}"
    +
     3450.    482
    +
     3451.    482                        advance("(");
    +
     3452.    482                    }
    +
     3453.    482                    value = expression(Infinity, true);
    +
     3454.    482                } else if (next_token.id === "(") {
    +
     3455.    482                    value = do_function({
    +
     3456.    482                        arity: "unary",
    +
     3457.    482                        from: name.from,
    +
     3458.    482                        id: "function",
    +
     3459.    482                        line: name.line,
    +
     3460.    482                        name: (
    +
     3461.    482                            typeof extra === "string"
    +
     3462.    482
    +
     3463.    482// cause: "aa={get aa(){}}"
    +
     3464.    482
    +
     3465.    482                            ? extra
    +
     3466.    482
    +
     3467.    482// cause: "aa={aa()}"
    +
     3468.    482
    +
     3469.    482                            : id
    +
     3470.    482                        ),
    +
     3471.    482                        thru: name.from
    +
     3472.    482                    });
    +
     3473.    482                } else {
    +
     3474.    482                    if (typeof extra === "string") {
    +
     3475.    482
    +
     3476.    482// cause: "aa={get aa.aa}"
    +
     3477.    482
    +
     3478.    482                        advance("(");
    +
     3479.    482                    }
    +
     3480.    482                    let the_colon = next_token;
    +
     3481.    482                    advance(":");
    +
     3482.    482                    value = expression(0);
    +
     3483.    482                    if (value.id === name.id && value.id !== "function") {
    +
     3484.    482
    +
     3485.    482// cause: "aa={aa:aa}"
    +
     3486.    482
    +
     3487.    482                        warn("unexpected_a", the_colon, ": " + name.id);
    +
     3488.    482                    }
    +
     3489.    482                }
    +
     3490.    482                value.label = name;
    +
     3491.    482                if (typeof extra === "string") {
    +
     3492.    482                    value.extra = extra;
    +
     3493.    482                }
    +
     3494.    482                the_brace.expression.push(value);
    +
     3495.    482            } else {
    +
     3496.     83                advance(":");
    +
     3497.     83                value = expression(0);
    +
     3498.     83                value.label = name;
    +
     3499.     83                the_brace.expression.push(value);
    +
     3500.    487            }
    +
     3501.    487            if (next_token.id === ",") {
    +
     3502.    408                advance(",");
    +
     3503.    408                return member();
    +
     3504.    408            }
    +
     3505.    491        }());
    +
     3506.     85    }
    +
     3507.     85    advance("}");
    +
     3508.     85    return the_brace;
    +
     3509.     85});
    +
     3510.      1
    +
     3511.      2stmt(";", function () {
    +
     3512.      2    warn("unexpected_a", token);
    +
     3513.      2    return token;
    +
     3514.      2});
    +
     3515.     10stmt("{", function () {
    +
     3516.     10
    +
     3517.     10// cause: "class aa{}"
    +
     3518.     10
    +
     3519.     10    warn("naked_block", token);
    +
     3520.     10    return block("naked");
    +
     3521.     10});
    +
     3522.      1stmt("async", do_async);
    +
     3523.     30stmt("break", function () {
    +
     3524.     30    const the_break = token;
    +
     3525.     30    let the_label;
    +
     3526.     30    if (
    +
     3527.     17        (functionage.loop < 1 && functionage.switch < 1)
    +
     3528.     25        || functionage.finally > 0
    +
     3529.      5    ) {
    +
     3530.      5        warn("unexpected_a", the_break);
    +
     3531.      5    }
    +
     3532.     30    the_break.disrupt = true;
    +
     3533.      5    if (next_token.identifier && token.line === next_token.line) {
    +
     3534.      5        the_label = functionage.context[next_token.id];
    +
     3535.      5        if (
    +
     3536.      5            the_label === undefined
    +
     3537.      5            || the_label.role !== "label"
    +
     3538.      5            || the_label.dead
    +
     3539.      5        ) {
    +
     3540.      5            if (the_label !== undefined && the_label.dead) {
    +
     3541.      5
    +
     3542.      5// cause: "aa:{function aa(aa){break aa;}}"
    +
     3543.      5
    +
     3544.      5                warn("out_of_scope_a");
    +
     3545.      5            } else {
    +
     3546.      5
    +
     3547.      5// cause: "aa:{break aa;}"
    +
     3548.      5
    +
     3549.      5                warn("not_label_a");
    +
     3550.      5            }
    +
     3551.      5        } else {
    +
     3552.      5            the_label.used += 1;
    +
     3553.      5        }
    +
     3554.      5        the_break.label = next_token;
    +
     3555.      5        advance();
    +
     3556.      5    }
    +
     3557.     30    advance(";");
    +
     3558.     30    return the_break;
    +
     3559.     30});
    +
     3560.      1
    +
     3561.    737function do_var() {
    +
     3562.    737    const the_statement = token;
    +
     3563.    737    const is_const = the_statement.id === "const";
    +
     3564.    737    the_statement.names = [];
    +
     3565.    737
    +
     3566.    737// A program may use var or let, but not both.
    +
     3567.    737
    +
     3568.    409    if (!is_const) {
    +
     3569.    409        if (var_mode === undefined) {
    +
     3570.    409            var_mode = the_statement.id;
    +
     3571.    409        } else if (the_statement.id !== var_mode) {
    +
     3572.    409            warn(
    +
     3573.    409                "expected_a_b",
    +
     3574.    409                the_statement,
    +
     3575.    409                var_mode,
    +
     3576.    409                the_statement.id
    +
     3577.    409            );
    +
     3578.    409        }
    +
     3579.    409    }
    +
     3580.    737
    +
     3581.    737// We don't expect to see variables created in switch statements.
    +
     3582.    737
    +
     3583.      1    if (functionage.switch > 0) {
    +
     3584.      1
    +
     3585.      1// cause: "switch(0){case 0:var aa}"
    +
     3586.      1
    +
     3587.      1        warn("var_switch", the_statement);
    +
     3588.      1    }
    +
     3589.      1    if (functionage.loop > 0 && the_statement.id === "var") {
    +
     3590.      1
    +
     3591.      1// cause: "while(0){var aa;}"
    +
     3592.      1
    +
     3593.      1        warn("var_loop", the_statement);
    +
     3594.      1    }
    +
     3595.    737    (function next() {
    +
     3596.      9        if (next_token.id === "{" && the_statement.id !== "var") {
    +
     3597.      8            let a;
    +
     3598.      8            let b = "";
    +
     3599.      8            const the_brace = next_token;
    +
     3600.      8            advance("{");
    +
     3601.     12            (function pair() {
    +
     3602.      8                if (!next_token.identifier) {
    +
     3603.      8
    +
     3604.      8// cause: "let {0}"
    +
     3605.      8
    +
     3606.      8                    return stop("expected_identifier_a", next_token);
    +
     3607.     11                }
    +
     3608.     11                const name = next_token;
    +
     3609.     11                survey(name);
    +
     3610.     11                a = b;
    +
     3611.     11                b = String(name.value || name.id);
    +
     3612.      8                if (a > b) {
    +
     3613.      8                    if (!option.unordered) {
    +
     3614.      8
    +
     3615.      8// cause: "let{bb,aa}"
    +
     3616.      8
    +
     3617.      8                        warn("unordered_param_a", name);
    +
     3618.      8                    }
    +
     3619.     11                }
    +
     3620.     11                advance();
    +
     3621.     11                if (next_token.id === ":") {
    +
     3622.      8                    advance(":");
    +
     3623.      8                    if (!next_token.identifier) {
    +
     3624.      8
    +
     3625.      8// cause: "let {aa:0}"
    +
     3626.      8
    +
     3627.      8                        return stop("expected_identifier_a", next_token);
    +
     3628.      8                    }
    +
     3629.      8                    next_token.label = name;
    +
     3630.      8                    the_statement.names.push(next_token);
    +
     3631.      8                    enroll(next_token, "variable", is_const);
    +
     3632.      8                    advance();
    +
     3633.      8                    the_brace.open = true;
    +
     3634.      9                } else {
    +
     3635.      9                    the_statement.names.push(name);
    +
     3636.      9                    enroll(name, "variable", is_const);
    +
     3637.     10                }
    +
     3638.     10                name.dead = false;
    +
     3639.     10                name.init = true;
    +
     3640.     10                if (next_token.id === "=") {
    +
     3641.      8
    +
     3642.      8// cause: "let {aa=0}"
    +
     3643.      8
    +
     3644.      8                    advance("=");
    +
     3645.      8                    name.expression = expression();
    +
     3646.      8                    the_brace.open = true;
    +
     3647.     10                }
    +
     3648.     10                if (next_token.id === ",") {
    +
     3649.      8                    advance(",");
    +
     3650.      8                    return pair();
    +
     3651.      8                }
    +
     3652.     12            }());
    +
     3653.      8            advance("}");
    +
     3654.      8            advance("=");
    +
     3655.      8            the_statement.expression = expression(0);
    +
     3656.    729        } else if (next_token.id === "[" && the_statement.id !== "var") {
    +
     3657.    729            const the_bracket = next_token;
    +
     3658.    729            advance("[");
    +
     3659.    729            (function element() {
    +
     3660.    729                let ellipsis;
    +
     3661.    729                if (next_token.id === "...") {
    +
     3662.    729                    ellipsis = true;
    +
     3663.    729                    advance("...");
    +
     3664.    729                }
    +
     3665.    729                if (!next_token.identifier) {
    +
     3666.    729
    +
     3667.    729// cause: "let[]"
    +
     3668.    729
    +
     3669.    729                    return stop("expected_identifier_a", next_token);
    +
     3670.    729                }
    +
     3671.    729                const name = next_token;
    +
     3672.    729                advance();
    +
     3673.    729                the_statement.names.push(name);
    +
     3674.    729                enroll(name, "variable", is_const);
    +
     3675.    729                name.dead = false;
    +
     3676.    729                name.init = true;
    +
     3677.    729                if (ellipsis) {
    +
     3678.    729                    name.ellipsis = true;
    +
     3679.    729                } else {
    +
     3680.    729                    if (next_token.id === "=") {
    +
     3681.    729                        advance("=");
    +
     3682.    729                        name.expression = expression();
    +
     3683.    729                        the_bracket.open = true;
    +
     3684.    729                    }
    +
     3685.    729                    if (next_token.id === ",") {
    +
     3686.    729                        advance(",");
    +
     3687.    729                        return element();
    +
     3688.    729                    }
    +
     3689.    729                }
    +
     3690.    729            }());
    +
     3691.    729            advance("]");
    +
     3692.    729            advance("=");
    +
     3693.    729            the_statement.expression = expression(0);
    +
     3694.    729        } else if (next_token.identifier) {
    +
     3695.    729            const name = next_token;
    +
     3696.    729            advance();
    +
     3697.    729            if (name.id === "ignore") {
    +
     3698.    729
    +
     3699.    729// cause: "let ignore;function aa(ignore) {}"
    +
     3700.    729
    +
     3701.    729                warn("unexpected_a", name);
    +
     3702.    729            }
    +
     3703.    729            enroll(name, "variable", is_const);
    +
     3704.    729            if (next_token.id === "=" || is_const) {
    +
     3705.    729                advance("=");
    +
     3706.    729                name.dead = false;
    +
     3707.    729                name.init = true;
    +
     3708.    729                name.expression = expression(0);
    +
     3709.    729            }
    +
     3710.    729            the_statement.names.push(name);
    +
     3711.    729        } else {
    +
     3712.    729
    +
     3713.    729// cause: "let 0"
    +
     3714.    729// cause: "var{aa:{aa}}"
    +
     3715.    729
    +
     3716.    729            return stop("expected_identifier_a", next_token);
    +
     3717.    729        }
    +
     3718.    737    }());
    +
     3719.    737    semicolon();
    +
     3720.    737    return the_statement;
    +
     3721.    737}
    +
     3722.      1
    +
     3723.      1stmt("const", do_var);
    +
     3724.      2stmt("continue", function () {
    +
     3725.      2    const the_continue = token;
    +
     3726.      1    if (functionage.loop < 1 || functionage.finally > 0) {
    +
     3727.      1        warn("unexpected_a", the_continue);
    +
     3728.      1    }
    +
     3729.      2    not_top_level(the_continue);
    +
     3730.      2    the_continue.disrupt = true;
    +
     3731.      2    warn("unexpected_a", the_continue);
    +
     3732.      2    advance(";");
    +
     3733.      2    return the_continue;
    +
     3734.      2});
    +
     3735.      2stmt("debugger", function () {
    +
     3736.      2    const the_debug = token;
    +
     3737.      1    if (!option.devel) {
    +
     3738.      1        warn("unexpected_a", the_debug);
    +
     3739.      1    }
    +
     3740.      2    semicolon();
    +
     3741.      2    return the_debug;
    +
     3742.      2});
    +
     3743.     25stmt("delete", function () {
    +
     3744.     25    const the_token = token;
    +
     3745.     25    const the_value = expression(0);
    +
     3746.     25    if (
    +
     3747.      1        (the_value.id !== "." && the_value.id !== "[")
    +
     3748.     25        || the_value.arity !== "binary"
    +
     3749.      1    ) {
    +
     3750.      1        stop("expected_a_b", the_value, ".", artifact(the_value));
    +
     3751.     24    }
    +
     3752.     24    the_token.expression = the_value;
    +
     3753.     24    semicolon();
    +
     3754.     24    return the_token;
    +
     3755.     24});
    +
     3756.      5stmt("do", function () {
    +
     3757.      5    const the_do = token;
    +
     3758.      5    not_top_level(the_do);
    +
     3759.      5    functionage.loop += 1;
    +
     3760.      5    the_do.block = block();
    +
     3761.      5    advance("while");
    +
     3762.      5    the_do.expression = condition();
    +
     3763.      5    semicolon();
    +
     3764.      1    if (the_do.block.disrupt === true) {
    +
     3765.      1
    +
     3766.      1// cause: "function aa(){do{break;}while(0)}"
    +
     3767.      1
    +
     3768.      1        warn("weird_loop", the_do);
    +
     3769.      3    }
    +
     3770.      3    functionage.loop -= 1;
    +
     3771.      3    return the_do;
    +
     3772.      3});
    +
     3773.     17stmt("export", function () {
    +
     3774.     17    const the_export = token;
    +
     3775.     17    let the_id;
    +
     3776.     17    let the_name;
    +
     3777.     17    let the_thing;
    +
     3778.     17
    +
     3779.      6    function export_id() {
    +
     3780.      2        if (!next_token.identifier) {
    +
     3781.      2
    +
     3782.      2// cause: "export {}"
    +
     3783.      2
    +
     3784.      2            stop("expected_identifier_a");
    +
     3785.      4        }
    +
     3786.      4        the_id = next_token.id;
    +
     3787.      4        the_name = global.context[the_id];
    +
     3788.      4        if (the_name === undefined) {
    +
     3789.      1
    +
     3790.      1// cause: "export {aa}"
    +
     3791.      1
    +
     3792.      1            warn("unexpected_a");
    +
     3793.      3        } else {
    +
     3794.      3            the_name.used += 1;
    +
     3795.      3            if (exports[the_id] !== undefined) {
    +
     3796.      3
    +
     3797.      3// cause: "let aa;export{aa,aa}"
    +
     3798.      3
    +
     3799.      3                warn("duplicate_a");
    +
     3800.      3            }
    +
     3801.      3            exports[the_id] = the_name;
    +
     3802.      4        }
    +
     3803.      4        advance();
    +
     3804.      4        the_export.expression.push(the_thing);
    +
     3805.      4    }
    +
     3806.     17
    +
     3807.     17    the_export.expression = [];
    +
     3808.      6    if (next_token.id === "default") {
    +
     3809.      6        if (exports.default !== undefined) {
    +
     3810.      6
    +
     3811.      6// cause: "export default 0;export default 0"
    +
     3812.      6
    +
     3813.      6            warn("duplicate_a");
    +
     3814.      6        }
    +
     3815.      6        advance("default");
    +
     3816.      6        the_thing = expression(0);
    +
     3817.      6        if (
    +
     3818.      6            the_thing.id !== "("
    +
     3819.      6            || the_thing.expression[0].id !== "."
    +
     3820.      6            || the_thing.expression[0].expression.id !== "Object"
    +
     3821.      6            || the_thing.expression[0].name.id !== "freeze"
    +
     3822.      6        ) {
    +
     3823.      6
    +
     3824.      6// cause: "export default {}"
    +
     3825.      6
    +
     3826.      6            warn("freeze_exports", the_thing);
    +
     3827.      6        }
    +
     3828.      6        if (next_token.id === ";") {
    +
     3829.      6            semicolon();
    +
     3830.      6        }
    +
     3831.      6        exports.default = the_thing;
    +
     3832.      6        the_export.expression.push(the_thing);
    +
     3833.     11    } else {
    +
     3834.     11        if (next_token.id === "function") {
    +
     3835.     11
    +
     3836.     11// cause: "export function aa(){}"
    +
     3837.     11
    +
     3838.     11            warn("freeze_exports");
    +
     3839.     11            the_thing = statement();
    +
     3840.     11            the_name = the_thing.name;
    +
     3841.     11            the_id = the_name.id;
    +
     3842.     11            the_name.used += 1;
    +
     3843.     11
    +
     3844.     11// cause: "let aa;export{aa};export function aa(){}"
    +
     3845.     11
    +
     3846.     11            if (exports[the_id] !== undefined) {
    +
     3847.     11                warn("duplicate_a", the_name);
    +
     3848.     11            }
    +
     3849.     11            exports[the_id] = the_thing;
    +
     3850.     11            the_export.expression.push(the_thing);
    +
     3851.     11            the_thing.statement = false;
    +
     3852.     11            the_thing.arity = "unary";
    +
     3853.     11        } else if (
    +
     3854.     11            next_token.id === "var"
    +
     3855.     11            || next_token.id === "let"
    +
     3856.     11            || next_token.id === "const"
    +
     3857.     11        ) {
    +
     3858.     11
    +
     3859.     11// cause: "export const"
    +
     3860.     11// cause: "export let"
    +
     3861.     11// cause: "export var"
    +
     3862.     11
    +
     3863.     11            warn("unexpected_a", next_token);
    +
     3864.     11            statement();
    +
     3865.     11        } else if (next_token.id === "{") {
    +
     3866.     11
    +
     3867.     11// cause: "export {}"
    +
     3868.     11
    +
     3869.     11            advance("{");
    +
     3870.     11            (function loop() {
    +
     3871.     11                export_id();
    +
     3872.     11                if (next_token.id === ",") {
    +
     3873.     11                    advance(",");
    +
     3874.     11                    return loop();
    +
     3875.     11                }
    +
     3876.     11            }());
    +
     3877.     11            advance("}");
    +
     3878.     11            semicolon();
    +
     3879.     11        } else {
    +
     3880.     11
    +
     3881.     11// cause: "export"
    +
     3882.     11
    +
     3883.     11            stop("unexpected_a");
    +
     3884.     11        }
    +
     3885.     11    }
    +
     3886.     11    module_mode = true;
    +
     3887.     11    return the_export;
    +
     3888.     11});
    +
     3889.     12stmt("for", function () {
    +
     3890.     12    let first;
    +
     3891.     12    const the_for = token;
    +
     3892.     12    if (!option.for) {
    +
     3893.     12        warn("unexpected_a", the_for);
    +
     3894.     12    }
    +
     3895.     12    not_top_level(the_for);
    +
     3896.     12    functionage.loop += 1;
    +
     3897.     12    advance("(");
    +
     3898.     12
    +
     3899.     12// cause: "for(){}"
    +
     3900.     12
    +
     3901.     12    token.free = true;
    +
     3902.      2    if (next_token.id === ";") {
    +
     3903.      2
    +
     3904.      2// cause: "for(;;){}"
    +
     3905.      2
    +
     3906.      2        return stop("expected_a_b", the_for, "while (", "for (;");
    +
     3907.     10    }
    +
     3908.     10    if (
    +
     3909.     10        next_token.id === "var"
    +
     3910.     10        || next_token.id === "let"
    +
     3911.     10        || next_token.id === "const"
    +
     3912.      1    ) {
    +
     3913.      1        return stop("unexpected_a");
    +
     3914.      9    }
    +
     3915.      9    first = expression(0);
    +
     3916.      9    if (first.id === "in") {
    +
     3917.      3        if (first.expression[0].arity !== "variable") {
    +
     3918.      3
    +
     3919.      3// cause: "for(0 in aa){}"
    +
     3920.      3
    +
     3921.      3            warn("bad_assignment_a", first.expression[0]);
    +
     3922.      3        }
    +
     3923.      3        the_for.name = first.expression[0];
    +
     3924.      3        the_for.expression = first.expression[1];
    +
     3925.      3        warn("expected_a_b", the_for, "Object.keys", "for in");
    +
     3926.      4    } else {
    +
     3927.      4        the_for.initial = first;
    +
     3928.      4        advance(";");
    +
     3929.      4        the_for.expression = expression(0);
    +
     3930.      4        advance(";");
    +
     3931.      4        the_for.inc = expression(0);
    +
     3932.      4        if (the_for.inc.id === "++") {
    +
     3933.      4
    +
     3934.      4// cause: "for(aa;aa;aa++){}"
    +
     3935.      4
    +
     3936.      4            warn("expected_a_b", the_for.inc, "+= 1", "++");
    +
     3937.      4        }
    +
     3938.      7    }
    +
     3939.      7    advance(")");
    +
     3940.      7    the_for.block = block();
    +
     3941.      7    if (the_for.block.disrupt === true) {
    +
     3942.      1        warn("weird_loop", the_for);
    +
     3943.      7    }
    +
     3944.      7    functionage.loop -= 1;
    +
     3945.      7    return the_for;
    +
     3946.      7});
    +
     3947.      1stmt("function", do_function);
    +
     3948.   1196stmt("if", function () {
    +
     3949.   1196    let the_else;
    +
     3950.   1196    const the_if = token;
    +
     3951.   1196    the_if.expression = condition();
    +
     3952.   1196    the_if.block = block();
    +
     3953.    292    if (next_token.id === "else") {
    +
     3954.    292        advance("else");
    +
     3955.    292        the_else = token;
    +
     3956.    292        the_if.else = (
    +
     3957.    292            next_token.id === "if"
    +
     3958.    292
    +
     3959.    292// cause: "if(0){0}else if(0){0}"
    +
     3960.    292
    +
     3961.    292            ? statement()
    +
     3962.    292
    +
     3963.    292// cause: "if(0){0}else{0}"
    +
     3964.    292
    +
     3965.    292            : block()
    +
     3966.    292        );
    +
     3967.    292        if (the_if.block.disrupt === true) {
    +
     3968.    292            if (the_if.else.disrupt === true) {
    +
     3969.    292
    +
     3970.    292// cause: "if(0){break;}else{}"
    +
     3971.    292
    +
     3972.    292                the_if.disrupt = true;
    +
     3973.    292            } else {
    +
     3974.    292
    +
     3975.    292// cause: "if(0){break;}else{}"
    +
     3976.    292
    +
     3977.    292                warn("unexpected_a", the_else);
    +
     3978.    292            }
    +
     3979.    292        }
    +
     3980.   1194    }
    +
     3981.   1194    return the_if;
    +
     3982.   1194});
    +
     3983.     14stmt("import", function () {
    +
     3984.     14    const the_import = token;
    +
     3985.      2    if (next_token.id === "(") {
    +
     3986.      2        the_import.arity = "unary";
    +
     3987.      2        the_import.constant = true;
    +
     3988.      2        the_import.statement = false;
    +
     3989.      2        advance("(");
    +
     3990.      2        const string = expression(0);
    +
     3991.      2        if (string.id !== "(string)") {
    +
     3992.      2
    +
     3993.      2// cause: "import(aa)"
    +
     3994.      2
    +
     3995.      2            warn("expected_string_a", string);
    +
     3996.      2        }
    +
     3997.      2        froms.push(token.value);
    +
     3998.      2        advance(")");
    +
     3999.      2        advance(".");
    +
     4000.      2        advance("then");
    +
     4001.      2        advance("(");
    +
     4002.      2        the_import.expression = expression(0);
    +
     4003.      2        advance(")");
    +
     4004.      2        semicolon();
    +
     4005.      2        return the_import;
    +
     4006.     12    }
    +
     4007.     12    let name;
    +
     4008.     12    if (typeof module_mode === "object") {
    +
     4009.      1
    +
     4010.      1// cause: "/*global aa*/\nimport aa from \"aa\""
    +
     4011.      1
    +
     4012.      1        warn("unexpected_directive_a", module_mode, module_mode.directive);
    +
     4013.     12    }
    +
     4014.     12    module_mode = true;
    +
     4015.     12    if (next_token.identifier) {
    +
     4016.      8        name = next_token;
    +
     4017.      8        advance();
    +
     4018.      8        if (name.id === "ignore") {
    +
     4019.      8            warn("unexpected_a", name);
    +
     4020.      8        }
    +
     4021.      8        enroll(name, "variable", true);
    +
     4022.      8        the_import.name = name;
    +
     4023.      8    } else {
    +
     4024.      4        const names = [];
    +
     4025.      4        advance("{");
    +
     4026.      4        if (next_token.id !== "}") {
    +
     4027.      4            while (true) {
    +
     4028.      4                if (!next_token.identifier) {
    +
     4029.      4
    +
     4030.      4// cause: "import {"
    +
     4031.      4
    +
     4032.      4                    stop("expected_identifier_a");
    +
     4033.      4                }
    +
     4034.      4                name = next_token;
    +
     4035.      4                advance();
    +
     4036.      4                if (name.id === "ignore") {
    +
     4037.      4                    warn("unexpected_a", name);
    +
     4038.      4                }
    +
     4039.      4                enroll(name, "variable", true);
    +
     4040.      4                names.push(name);
    +
     4041.      4                if (next_token.id !== ",") {
    +
     4042.      4                    break;
    +
     4043.      4                }
    +
     4044.      4                advance(",");
    +
     4045.      4            }
    +
     4046.      4        }
    +
     4047.      4        advance("}");
    +
     4048.      4        the_import.name = names;
    +
     4049.     11    }
    +
     4050.     11    advance("from");
    +
     4051.     11    advance("(string)");
    +
     4052.     11    the_import.import = token;
    +
     4053.     11    if (!rx_module.test(token.value)) {
    +
     4054.      1
    +
     4055.      1// cause: "import aa from \"!aa\""
    +
     4056.      1
    +
     4057.      1        warn("bad_module_name_a", token);
    +
     4058.     11    }
    +
     4059.     11    froms.push(token.value);
    +
     4060.     11    semicolon();
    +
     4061.     11    return the_import;
    +
     4062.     11});
    +
     4063.      1stmt("let", do_var);
    +
     4064.    527stmt("return", function () {
    +
     4065.    527    const the_return = token;
    +
     4066.    527    not_top_level(the_return);
    +
     4067.      1    if (functionage.finally > 0) {
    +
     4068.      1        warn("unexpected_a", the_return);
    +
     4069.      1    }
    +
     4070.    527    the_return.disrupt = true;
    +
     4071.    487    if (next_token.id !== ";" && the_return.line === next_token.line) {
    +
     4072.    487        the_return.expression = expression(10);
    +
     4073.    487    }
    +
     4074.    527    advance(";");
    +
     4075.    527    return the_return;
    +
     4076.    527});
    +
     4077.     13stmt("switch", function () {
    +
     4078.     13    let dups = [];
    +
     4079.     13    let last;
    +
     4080.     13    let stmts;
    +
     4081.     13    const the_cases = [];
    +
     4082.     13    let the_disrupt = true;
    +
     4083.     13    const the_switch = token;
    +
     4084.     13    not_top_level(the_switch);
    +
     4085.      1    if (functionage.finally > 0) {
    +
     4086.      1        warn("unexpected_a", the_switch);
    +
     4087.      1    }
    +
     4088.     13    functionage.switch += 1;
    +
     4089.     13    advance("(");
    +
     4090.     13
    +
     4091.     13// cause: "switch(){}"
    +
     4092.     13
    +
     4093.     13    token.free = true;
    +
     4094.     13    the_switch.expression = expression(0);
    +
     4095.     13    the_switch.block = the_cases;
    +
     4096.     13    advance(")");
    +
     4097.     13    advance("{");
    +
     4098.     18    (function major() {
    +
     4099.     18        const the_case = next_token;
    +
     4100.     18        the_case.arity = "statement";
    +
     4101.     18        the_case.expression = [];
    +
     4102.     31        (function minor() {
    +
     4103.     31            advance("case");
    +
     4104.     31            token.switch = true;
    +
     4105.     31            const exp = expression(0);
    +
     4106.     48            if (dups.some(function (thing) {
    +
     4107.     48                return are_similar(thing, exp);
    +
     4108.     48            })) {
    +
     4109.      1                warn("unexpected_a", exp);
    +
     4110.      1            }
    +
     4111.     31            dups.push(exp);
    +
     4112.     31            the_case.expression.push(exp);
    +
     4113.     31            advance(":");
    +
     4114.     13            if (next_token.id === "case") {
    +
     4115.     13                return minor();
    +
     4116.     13            }
    +
     4117.     31        }());
    +
     4118.     18        stmts = statements();
    +
     4119.      1        if (stmts.length < 1) {
    +
     4120.      1            warn("expected_statements_a");
    +
     4121.      1            return;
    +
     4122.     16        }
    +
     4123.     16        the_case.block = stmts;
    +
     4124.     16        the_cases.push(the_case);
    +
     4125.     16        last = stmts[stmts.length - 1];
    +
     4126.     16        if (last.disrupt) {
    +
     4127.     15            if (last.id === "break" && last.label === undefined) {
    +
     4128.     15                the_disrupt = false;
    +
     4129.     15            }
    +
     4130.     15        } else {
    +
     4131.      1            warn(
    +
     4132.      1                "expected_a_before_b",
    +
     4133.      1                next_token,
    +
     4134.      1                "break;",
    +
     4135.      1                artifact(next_token)
    +
     4136.      1            );
    +
     4137.     16        }
    +
     4138.     16        if (next_token.id === "case") {
    +
     4139.      7            return major();
    +
     4140.      7        }
    +
     4141.     18    }());
    +
     4142.     13    dups = undefined;
    +
     4143.      7    if (next_token.id === "default") {
    +
     4144.      7        const the_default = next_token;
    +
     4145.      7        advance("default");
    +
     4146.      7        token.switch = true;
    +
     4147.      7        advance(":");
    +
     4148.      7        the_switch.else = statements();
    +
     4149.      7        if (the_switch.else.length < 1) {
    +
     4150.      7            warn("unexpected_a", the_default);
    +
     4151.      7            the_disrupt = false;
    +
     4152.      7        } else {
    +
     4153.      7            const the_last = the_switch.else[the_switch.else.length - 1];
    +
     4154.      7            if (the_last.id === "break" && the_last.label === undefined) {
    +
     4155.      7                warn("unexpected_a", the_last);
    +
     4156.      7                the_last.disrupt = false;
    +
     4157.      7            }
    +
     4158.      7            the_disrupt = the_disrupt && the_last.disrupt;
    +
     4159.      7        }
    +
     4160.      7    } else {
    +
     4161.      3        the_disrupt = false;
    +
     4162.     10    }
    +
     4163.     10    advance("}", the_switch);
    +
     4164.     10    functionage.switch -= 1;
    +
     4165.     10    the_switch.disrupt = the_disrupt;
    +
     4166.     10    return the_switch;
    +
     4167.     10});
    +
     4168.     11stmt("throw", function () {
    +
     4169.     11    const the_throw = token;
    +
     4170.     11    the_throw.disrupt = true;
    +
     4171.     11    the_throw.expression = expression(10);
    +
     4172.     11    semicolon();
    +
     4173.      1    if (functionage.try > 0) {
    +
     4174.      1        warn("unexpected_a", the_throw);
    +
     4175.      1    }
    +
     4176.     11    return the_throw;
    +
     4177.     11});
    +
     4178.     13stmt("try", function () {
    +
     4179.     13    let the_catch;
    +
     4180.     13    let the_disrupt;
    +
     4181.     13    const the_try = token;
    +
     4182.      1    if (functionage.try > 0) {
    +
     4183.      1        warn("unexpected_a", the_try);
    +
     4184.      1    }
    +
     4185.     13    functionage.try += 1;
    +
     4186.     13    the_try.block = block();
    +
     4187.     13    the_disrupt = the_try.block.disrupt;
    +
     4188.     11    if (next_token.id === "catch") {
    +
     4189.     11        let ignored = "ignore";
    +
     4190.     11        the_catch = next_token;
    +
     4191.     11        the_try.catch = the_catch;
    +
     4192.     11        advance("catch");
    +
     4193.     11        if (next_token.id === "(") {
    +
     4194.     11            advance("(");
    +
     4195.     11            if (!next_token.identifier) {
    +
     4196.     11                return stop("expected_identifier_a", next_token);
    +
     4197.     11            }
    +
     4198.     11            if (next_token.id !== "ignore") {
    +
     4199.     11                ignored = undefined;
    +
     4200.     11                the_catch.name = next_token;
    +
     4201.     11                enroll(next_token, "exception", true);
    +
     4202.     11            }
    +
     4203.     11            advance();
    +
     4204.     11            advance(")");
    +
     4205.     11        }
    +
     4206.     11        the_catch.block = block(ignored);
    +
     4207.     11        if (the_catch.block.disrupt !== true) {
    +
     4208.     11            the_disrupt = false;
    +
     4209.     11        }
    +
     4210.     11    } else {
    +
     4211.      1        warn(
    +
     4212.      1            "expected_a_before_b",
    +
     4213.      1            next_token,
    +
     4214.      1            "catch",
    +
     4215.      1            artifact(next_token)
    +
     4216.      1        );
    +
     4217.      1
    +
     4218.     11    }
    +
     4219.     11    if (next_token.id === "finally") {
    +
     4220.      4        functionage.finally += 1;
    +
     4221.      4        advance("finally");
    +
     4222.      4        the_try.else = block();
    +
     4223.      4        the_disrupt = the_try.else.disrupt;
    +
     4224.      4        functionage.finally -= 1;
    +
     4225.     11    }
    +
     4226.     11    the_try.disrupt = the_disrupt;
    +
     4227.     11    functionage.try -= 1;
    +
     4228.     11    return the_try;
    +
     4229.     11});
    +
     4230.      1stmt("var", do_var);
    +
     4231.     25stmt("while", function () {
    +
     4232.     25    const the_while = token;
    +
     4233.     25    not_top_level(the_while);
    +
     4234.     25    functionage.loop += 1;
    +
     4235.     25    the_while.expression = condition();
    +
     4236.     25    the_while.block = block();
    +
     4237.      3    if (the_while.block.disrupt === true) {
    +
     4238.      3
    +
     4239.      3// cause: "function aa(){while(0){break;}}"
    +
     4240.      3
    +
     4241.      3        warn("weird_loop", the_while);
    +
     4242.     23    }
    +
     4243.     23    functionage.loop -= 1;
    +
     4244.     23    return the_while;
    +
     4245.     23});
    +
     4246.      1stmt("with", function () {
    +
     4247.      1
    +
     4248.      1// cause: "with"
    +
     4249.      1
    +
     4250.      1    stop("unexpected_a", token);
    +
     4251.      1});
    +
     4252.      1
    +
     4253.      1ternary("?", ":");
    +
     4254.      1
    +
     4255.      1// Ambulation of the parse tree.
    +
     4256.      1
    +
     4257.      2function action(when) {
    +
     4258.      2
    +
     4259.      2// Produce a function that will register task functions that will be called as
    +
     4260.      2// the tree is traversed.
    +
     4261.      2
    +
     4262.     37    return function (arity, id, task) {
    +
     4263.     37        let a_set = when[arity];
    +
     4264.     37        let i_set;
    +
     4265.     37
    +
     4266.     37// The id parameter is optional. If excluded, the task will be applied to all
    +
     4267.     37// ids.
    +
     4268.     37
    +
     4269.      8        if (typeof id !== "string") {
    +
     4270.      8            task = id;
    +
     4271.      8            id = "(all)";
    +
     4272.      8        }
    +
     4273.     37
    +
     4274.     37// If this arity has no registrations yet, then create a set object to hold
    +
     4275.     37// them.
    +
     4276.     37
    +
     4277.     10        if (a_set === undefined) {
    +
     4278.     10            a_set = empty();
    +
     4279.     10            when[arity] = a_set;
    +
     4280.     10        }
    +
     4281.     37
    +
     4282.     37// If this id has no registrations yet, then create a set array to hold them.
    +
     4283.     37
    +
     4284.     37        i_set = a_set[id];
    +
     4285.     36        if (i_set === undefined) {
    +
     4286.     36            i_set = [];
    +
     4287.     36            a_set[id] = i_set;
    +
     4288.     36        }
    +
     4289.     37
    +
     4290.     37// Register the task with the arity and the id.
    +
     4291.     37
    +
     4292.     37        i_set.push(task);
    +
     4293.     37    };
    +
     4294.      2}
    +
     4295.      1
    +
     4296.      2function amble(when) {
    +
     4297.      2
    +
     4298.      2// Produce a function that will act on the tasks registered by an action
    +
     4299.      2// function while walking the tree.
    +
     4300.      2
    +
     4301.  60260    return function (the_token) {
    +
     4302.  60260
    +
     4303.  60260// Given a task set that was built by an action function, run all of the
    +
     4304.  60260// relevant tasks on the token.
    +
     4305.  60260
    +
     4306.  60260        let a_set = when[the_token.arity];
    +
     4307.  60260        let i_set;
    +
     4308.  60260
    +
     4309.  60260// If there are tasks associated with the token's arity...
    +
     4310.  60260
    +
     4311.  40901        if (a_set !== undefined) {
    +
     4312.  40901
    +
     4313.  40901// If there are tasks associated with the token's id...
    +
     4314.  40901
    +
     4315.  40901            i_set = a_set[the_token.id];
    +
     4316.  40901            if (i_set !== undefined) {
    +
     4317.  40901                i_set.forEach(function (task) {
    +
     4318.  40901                    return task(the_token);
    +
     4319.  40901                });
    +
     4320.  40901            }
    +
     4321.  40901
    +
     4322.  40901// If there are tasks for all ids.
    +
     4323.  40901
    +
     4324.  40901            i_set = a_set["(all)"];
    +
     4325.  40901            if (i_set !== undefined) {
    +
     4326.  40901                i_set.forEach(function (task) {
    +
     4327.  40901                    return task(the_token);
    +
     4328.  40901                });
    +
     4329.  40901            }
    +
     4330.  40901        }
    +
     4331.  60260    };
    +
     4332.      2}
    +
     4333.      1
    +
     4334.      1const posts = empty();
    +
     4335.      1const pres = empty();
    +
     4336.      1const preaction = action(pres);
    +
     4337.      1const postaction = action(posts);
    +
     4338.      1const preamble = amble(pres);
    +
     4339.      1const postamble = amble(posts);
    +
     4340.      1
    +
     4341.  46762function walk_expression(thing) {
    +
     4342.  29192    if (thing) {
    +
     4343.  29192        if (Array.isArray(thing)) {
    +
     4344.  29192
    +
     4345.  29192// cause: "0&&0"
    +
     4346.  29192// cause: "(function(){}())"
    +
     4347.  29192
    +
     4348.  29192            thing.forEach(walk_expression);
    +
     4349.  29192        } else {
    +
     4350.  29192            preamble(thing);
    +
     4351.  29192            walk_expression(thing.expression);
    +
     4352.  29192            if (thing.id === "function") {
    +
     4353.  29192
    +
     4354.  29192// cause: "aa=function(){}"
    +
     4355.  29192
    +
     4356.  29192                walk_statement(thing.block);
    +
     4357.  29192            }
    +
     4358.  29192            if (thing.arity === "pre" || thing.arity === "post") {
    +
     4359.  29192
    +
     4360.  29192// cause: "aa=++aa"
    +
     4361.  29192// cause: "aa=--aa"
    +
     4362.  29192
    +
     4363.  29192                warn("unexpected_a", thing);
    +
     4364.  29192            } else if (
    +
     4365.  29192
    +
     4366.  29192// cause: "aa=0"
    +
     4367.  29192
    +
     4368.  29192                thing.arity === "statement"
    +
     4369.  29192                || thing.arity === "assignment"
    +
     4370.  29192            ) {
    +
     4371.  29192
    +
     4372.  29192// cause: "aa[aa=0]"
    +
     4373.  29192
    +
     4374.  29192                warn("unexpected_statement_a", thing); // deadcode?
    +
     4375.  29192            }
    +
     4376.  29192            postamble(thing);
    +
     4377.  29192        }
    +
     4378.  29192    }
    +
     4379.  46762}
    +
     4380.      1
    +
     4381.  22420function walk_statement(thing) {
    +
     4382.  10278    if (thing) {
    +
     4383.  10278        if (Array.isArray(thing)) {
    +
     4384.  10278
    +
     4385.  10278// cause: "+[]"
    +
     4386.  10278
    +
     4387.  10278            thing.forEach(walk_statement);
    +
     4388.  10278        } else {
    +
     4389.  10278            preamble(thing);
    +
     4390.  10278            walk_expression(thing.expression);
    +
     4391.  10278            if (thing.arity === "binary") {
    +
     4392.  10278                if (thing.id !== "(") {
    +
     4393.  10278
    +
     4394.  10278// cause: "0&&0"
    +
     4395.  10278
    +
     4396.  10278                    warn("unexpected_expression_a", thing);
    +
     4397.  10278                }
    +
     4398.  10278            } else if (
    +
     4399.  10278                thing.arity !== "statement"
    +
     4400.  10278                && thing.arity !== "assignment"
    +
     4401.  10278                && thing.id !== "import"
    +
     4402.  10278                && thing.id !== "await"
    +
     4403.  10278            ) {
    +
     4404.  10278
    +
     4405.  10278// cause: "!0"
    +
     4406.  10278// cause: "+[]"
    +
     4407.  10278// cause: "+new aa()"
    +
     4408.  10278// cause: "0"
    +
     4409.  10278
    +
     4410.  10278                warn("unexpected_expression_a", thing);
    +
     4411.  10278            }
    +
     4412.  10278            walk_statement(thing.block);
    +
     4413.  10278            walk_statement(thing.else);
    +
     4414.  10278            postamble(thing);
    +
     4415.  10278        }
    +
     4416.  10278    }
    +
     4417.  22420}
    +
     4418.      1
    +
     4419.   8727function lookup(thing) {
    +
     4420.   8726    if (thing.arity === "variable") {
    +
     4421.   8726
    +
     4422.   8726// Look up the variable in the current context.
    +
     4423.   8726
    +
     4424.   8726        let the_variable = functionage.context[thing.id];
    +
     4425.   8726
    +
     4426.   8726// If it isn't local, search all the other contexts. If there are name
    +
     4427.   8726// collisions, take the most recent.
    +
     4428.   8726
    +
     4429.   8726        if (the_variable === undefined) {
    +
     4430.   8726            stack.forEach(function (outer) {
    +
     4431.   8726                const a_variable = outer.context[thing.id];
    +
     4432.   8726                if (
    +
     4433.   8726                    a_variable !== undefined
    +
     4434.   8726                    && a_variable.role !== "label"
    +
     4435.   8726                ) {
    +
     4436.   8726                    the_variable = a_variable;
    +
     4437.   8726                }
    +
     4438.   8726            });
    +
     4439.   8726
    +
     4440.   8726// If it isn't in any of those either, perhaps it is a predefined global.
    +
     4441.   8726// If so, add it to the global context.
    +
     4442.   8726
    +
     4443.   8726            if (the_variable === undefined) {
    +
     4444.   8726                if (declared_globals[thing.id] === undefined) {
    +
     4445.   8726
    +
     4446.   8726// cause: "aa"
    +
     4447.   8726// cause: "class aa{}"
    +
     4448.   8726
    +
     4449.   8726                    warn("undeclared_a", thing);
    +
     4450.   8726                    return;
    +
     4451.   8726                }
    +
     4452.   8726                the_variable = {
    +
     4453.   8726                    dead: false,
    +
     4454.   8726                    id: thing.id,
    +
     4455.   8726                    init: true,
    +
     4456.   8726                    parent: global,
    +
     4457.   8726                    role: "variable",
    +
     4458.   8726                    used: 0,
    +
     4459.   8726                    writable: false
    +
     4460.   8726                };
    +
     4461.   8726                global.context[thing.id] = the_variable;
    +
     4462.   8726            }
    +
     4463.   8726            the_variable.closure = true;
    +
     4464.   8726            functionage.context[thing.id] = the_variable;
    +
     4465.   8726        } else if (the_variable.role === "label") {
    +
     4466.   8726
    +
     4467.   8726// cause: "aa:while(0){aa;}"
    +
     4468.   8726
    +
     4469.   8726            warn("label_a", thing);
    +
     4470.   8726        }
    +
     4471.   8726        if (
    +
     4472.   8726            the_variable.dead
    +
     4473.   8726            && (
    +
     4474.   8726                the_variable.calls === undefined
    +
     4475.   8726                || functionage.name === undefined
    +
     4476.   8726                || the_variable.calls[functionage.name.id] === undefined
    +
     4477.   8726            )
    +
     4478.   8726        ) {
    +
     4479.   8726
    +
     4480.   8726// cause: "function aa(){bb();}\nfunction bb(){}"
    +
     4481.   8726
    +
     4482.   8726            warn("out_of_scope_a", thing);
    +
     4483.   8726        }
    +
     4484.   8726        return the_variable;
    +
     4485.   8726    }
    +
     4486.   8727}
    +
     4487.      1
    +
     4488.     70function subactivate(name) {
    +
     4489.     70    name.init = true;
    +
     4490.     70    name.dead = false;
    +
     4491.     70    blockage.live.push(name);
    +
     4492.     70}
    +
     4493.      1
    +
     4494.    590function preaction_function(thing) {
    +
     4495.    590
    +
     4496.    590// cause: "()=>0"
    +
     4497.    590// cause: "(function (){}())"
    +
     4498.    590// cause: "function aa(){}"
    +
     4499.    590
    +
     4500.    235    if (thing.arity === "statement" && blockage.body !== true) {
    +
     4501.      1
    +
     4502.      1// cause: "if(0){function aa(){}\n}"
    +
     4503.      1
    +
     4504.      1        warn("unexpected_a", thing);
    +
     4505.      1    }
    +
     4506.    590    stack.push(functionage);
    +
     4507.    590    block_stack.push(blockage);
    +
     4508.    590    functionage = thing;
    +
     4509.    590    blockage = thing;
    +
     4510.    590    thing.live = [];
    +
     4511.    296    if (typeof thing.name === "object") {
    +
     4512.    296        thing.name.dead = false;
    +
     4513.    296        thing.name.init = true;
    +
     4514.    296    }
    +
     4515.      6    if (thing.extra === "get") {
    +
     4516.      6        if (thing.parameters.length !== 0) {
    +
     4517.      6
    +
     4518.      6// cause: "/*jslint getset*/\naa={get aa(aa){}}"
    +
     4519.      6
    +
     4520.      6            warn("bad_get", thing);
    +
     4521.      6        }
    +
     4522.    584    } else if (thing.extra === "set") {
    +
     4523.    584        if (thing.parameters.length !== 1) {
    +
     4524.    584
    +
     4525.    584// cause: "/*jslint getset*/\naa={set aa(){}}"
    +
     4526.    584
    +
     4527.    584            warn("bad_set", thing);
    +
     4528.    584        }
    +
     4529.    584    }
    +
     4530.    470    thing.parameters.forEach(function (name) {
    +
     4531.    470        walk_expression(name.expression);
    +
     4532.    448        if (name.id === "{" || name.id === "[") {
    +
     4533.     27            name.names.forEach(subactivate);
    +
     4534.    443        } else {
    +
     4535.    443            name.dead = false;
    +
     4536.    443            name.init = true;
    +
     4537.    443        }
    +
     4538.    470    });
    +
     4539.    590}
    +
     4540.      1
    +
     4541.  10594function bitwise_check(thing) {
    +
     4542.  10162    if (!option.bitwise && bitwiseop[thing.id] === true) {
    +
     4543.      1        warn("unexpected_a", thing);
    +
     4544.      1    }
    +
     4545.  10594    if (
    +
     4546.  10594        thing.id !== "("
    +
     4547.   7716        && thing.id !== "&&"
    +
     4548.   7329        && thing.id !== "||"
    +
     4549.   6969        && thing.id !== "="
    +
     4550.   5810        && Array.isArray(thing.expression)
    +
     4551.   2471        && thing.expression.length === 2
    +
     4552.   2447        && (
    +
     4553.   2447            relationop[thing.expression[0].id] === true
    +
     4554.   2447            || relationop[thing.expression[1].id] === true
    +
     4555.   2447        )
    +
     4556.      1    ) {
    +
     4557.      1        warn("unexpected_a", thing);
    +
     4558.      1    }
    +
     4559.  10594}
    +
     4560.      1
    +
     4561.   2586function pop_block() {
    +
     4562.    600    blockage.live.forEach(function (name) {
    +
     4563.    600        name.dead = true;
    +
     4564.    600    });
    +
     4565.   2586    delete blockage.live;
    +
     4566.   2586    blockage = block_stack.pop();
    +
     4567.   2586}
    +
     4568.      1
    +
     4569.    726function action_var(thing) {
    +
     4570.    730    thing.names.forEach(function (name) {
    +
     4571.    730        name.dead = false;
    +
     4572.    474        if (name.expression !== undefined) {
    +
     4573.    474            walk_expression(name.expression);
    +
     4574.      0            if (name.id === "{" || name.id === "[") {
    +
     4575.      0                name.names.forEach(subactivate);
    +
     4576.      0            } else {
    +
     4577.    474                name.init = true;
    +
     4578.    474            }
    +
     4579.    474        }
    +
     4580.    730        blockage.live.push(name);
    +
     4581.    730    });
    +
     4582.    726}
    +
     4583.      1
    +
     4584.      1preaction("assignment", bitwise_check);
    +
     4585.      1preaction("binary", bitwise_check);
    +
     4586.   9317preaction("binary", function (thing) {
    +
     4587.   1557    if (relationop[thing.id] === true) {
    +
     4588.   1557        const left = thing.expression[0];
    +
     4589.   1557        const right = thing.expression[1];
    +
     4590.   1557        if (left.id === "NaN" || right.id === "NaN") {
    +
     4591.   1557
    +
     4592.   1557// cause: "NaN===NaN"
    +
     4593.   1557
    +
     4594.   1557            warn("number_isNaN", thing);
    +
     4595.   1557        } else if (left.id === "typeof") {
    +
     4596.   1557            if (right.id !== "(string)") {
    +
     4597.   1557                if (right.id !== "typeof") {
    +
     4598.   1557
    +
     4599.   1557// cause: "typeof 0===0"
    +
     4600.   1557
    +
     4601.   1557                    warn("expected_string_a", right);
    +
     4602.   1557                }
    +
     4603.   1557            } else {
    +
     4604.   1557                const value = right.value;
    +
     4605.   1557                if (value === "null" || value === "undefined") {
    +
     4606.   1557
    +
     4607.   1557// cause: "typeof aa===\"undefined\""
    +
     4608.   1557
    +
     4609.   1557                    warn("unexpected_typeof_a", right, value);
    +
     4610.   1557                } else if (
    +
     4611.   1557                    value !== "boolean"
    +
     4612.   1557                    && value !== "function"
    +
     4613.   1557                    && value !== "number"
    +
     4614.   1557                    && value !== "object"
    +
     4615.   1557                    && value !== "string"
    +
     4616.   1557                    && value !== "symbol"
    +
     4617.   1557                ) {
    +
     4618.   1557
    +
     4619.   1557// cause: "typeof 0===\"aa\""
    +
     4620.   1557
    +
     4621.   1557                    warn("expected_type_string_a", right, value);
    +
     4622.   1557                }
    +
     4623.   1557            }
    +
     4624.   1557        }
    +
     4625.   1557    }
    +
     4626.   9317});
    +
     4627.      2preaction("binary", "==", function (thing) {
    +
     4628.      2
    +
     4629.      2// cause: "0==0"
    +
     4630.      2
    +
     4631.      2    warn("expected_a_b", thing, "===", "==");
    +
     4632.      2});
    +
     4633.      1preaction("binary", "!=", function (thing) {
    +
     4634.      1
    +
     4635.      1// cause: "0!=0"
    +
     4636.      1
    +
     4637.      1    warn("expected_a_b", thing, "!==", "!=");
    +
     4638.      1});
    +
     4639.      1preaction("binary", "=>", preaction_function);
    +
     4640.    360preaction("binary", "||", function (thing) {
    +
     4641.    720    thing.expression.forEach(function (thang) {
    +
     4642.     65        if (thang.id === "&&" && !thang.wrapped) {
    +
     4643.      1
    +
     4644.      1// cause: "0&&0||0"
    +
     4645.      1
    +
     4646.      1            warn("and", thang);
    +
     4647.      1        }
    +
     4648.    720    });
    +
     4649.    360});
    +
     4650.   2878preaction("binary", "(", function (thing) {
    +
     4651.   2878    const left = thing.expression[0];
    +
     4652.   2878    if (
    +
     4653.   2878        left.identifier
    +
     4654.   2208        && functionage.context[left.id] === undefined
    +
     4655.   1111        && typeof functionage.name === "object"
    +
     4656.    624    ) {
    +
     4657.    624        const parent = functionage.name.parent;
    +
     4658.    624        if (parent) {
    +
     4659.    624            const left_variable = parent.context[left.id];
    +
     4660.    624            if (
    +
     4661.    624                left_variable !== undefined
    +
     4662.    624                && left_variable.dead
    +
     4663.    624                && left_variable.parent === parent
    +
     4664.    624                && left_variable.calls !== undefined
    +
     4665.    624                && left_variable.calls[functionage.name.id] !== undefined
    +
     4666.    624            ) {
    +
     4667.    624                left_variable.dead = false;
    +
     4668.    624            }
    +
     4669.    624        }
    +
     4670.    624    }
    +
     4671.   2878});
    +
     4672.      1preaction("binary", "in", function (thing) {
    +
     4673.      1
    +
     4674.      1// cause: "aa in aa"
    +
     4675.      1
    +
     4676.      1    warn("infix_in", thing);
    +
     4677.      1});
    +
     4678.      1preaction("binary", "instanceof", function (thing) {
    +
     4679.      1
    +
     4680.      1// cause: "0 instanceof 0"
    +
     4681.      1
    +
     4682.      1    warn("unexpected_a", thing);
    +
     4683.      1});
    +
     4684.   1996preaction("statement", "{", function (thing) {
    +
     4685.   1996    block_stack.push(blockage);
    +
     4686.   1996    blockage = thing;
    +
     4687.   1996    thing.live = [];
    +
     4688.   1996});
    +
     4689.      7preaction("statement", "for", function (thing) {
    +
     4690.      3    if (thing.name !== undefined) {
    +
     4691.      3        const the_variable = lookup(thing.name);
    +
     4692.      3        if (the_variable !== undefined) {
    +
     4693.      3            the_variable.init = true;
    +
     4694.      3            if (!the_variable.writable) {
    +
     4695.      3
    +
     4696.      3// cause: "const aa=0;for(aa in aa){}"
    +
     4697.      3
    +
     4698.      3                warn("bad_assignment_a", thing.name);
    +
     4699.      3            }
    +
     4700.      3        }
    +
     4701.      3    }
    +
     4702.      7    walk_statement(thing.initial);
    +
     4703.      7});
    +
     4704.      1preaction("statement", "function", preaction_function);
    +
     4705.      1preaction("unary", "~", bitwise_check);
    +
     4706.      1preaction("unary", "function", preaction_function);
    +
     4707.   8144preaction("variable", function (thing) {
    +
     4708.   8144    const the_variable = lookup(thing);
    +
     4709.   8078    if (the_variable !== undefined) {
    +
     4710.   8078        thing.variable = the_variable;
    +
     4711.   8078        the_variable.used += 1;
    +
     4712.   8078    }
    +
     4713.   8144});
    +
     4714.      1
    +
     4715.    580function init_variable(name) {
    +
     4716.    580    const the_variable = lookup(name);
    +
     4717.    543    if (the_variable !== undefined) {
    +
     4718.    543        if (the_variable.writable) {
    +
     4719.    543            the_variable.init = true;
    +
     4720.    543            return;
    +
     4721.    543        }
    +
     4722.    543    }
    +
     4723.     37    warn("bad_assignment_a", name);
    +
     4724.     37}
    +
     4725.      1
    +
     4726.    101postaction("assignment", "+=", function (thing) {
    +
     4727.    101    let right = thing.expression[1];
    +
     4728.     64    if (right.constant) {
    +
     4729.     64        if (
    +
     4730.     64            right.value === ""
    +
     4731.     64            || (right.id === "(number)" && right.value === "0")
    +
     4732.     64            || right.id === "(boolean)"
    +
     4733.     64            || right.id === "null"
    +
     4734.     64            || right.id === "undefined"
    +
     4735.     64            || Number.isNaN(right.value)
    +
     4736.     64        ) {
    +
     4737.     64            warn("unexpected_a", right);
    +
     4738.     64        }
    +
     4739.     64    }
    +
     4740.    101});
    +
     4741.   1277postaction("assignment", function (thing) {
    +
     4742.   1277
    +
     4743.   1277// Assignment using = sets the init property of a variable. No other assignment
    +
     4744.   1277// operator can do this. A = token keeps that variable (or array of variables
    +
     4745.   1277// in case of destructuring) in its name property.
    +
     4746.   1277
    +
     4747.   1277    const lvalue = thing.expression[0];
    +
     4748.   1159    if (thing.id === "=") {
    +
     4749.   1159        if (thing.names !== undefined) {
    +
     4750.      0            if (Array.isArray(thing.names)) {
    +
     4751.      0                thing.names.forEach(init_variable);
    +
     4752.      0            } else {
    +
     4753.   1159                init_variable(thing.names);
    +
     4754.   1159            }
    +
     4755.   1159        } else {
    +
     4756.   1159            if (lvalue.id === "[" || lvalue.id === "{") {
    +
     4757.   1159                lvalue.expression.forEach(function (thing) {
    +
     4758.   1159                    if (thing.variable) {
    +
     4759.   1159                        thing.variable.init = true;
    +
     4760.   1159                    }
    +
     4761.   1159                });
    +
     4762.   1159            } else if (
    +
     4763.   1159                lvalue.id === "."
    +
     4764.   1159                && thing.expression[1].id === "undefined"
    +
     4765.   1159            ) {
    +
     4766.   1159                warn(
    +
     4767.   1159                    "expected_a_b",
    +
     4768.   1159                    lvalue.expression,
    +
     4769.   1159                    "delete",
    +
     4770.   1159                    "undefined"
    +
     4771.   1159                );
    +
     4772.   1159            }
    +
     4773.   1159        }
    +
     4774.   1159    } else {
    +
     4775.    118        if (lvalue.arity === "variable") {
    +
     4776.    118            if (!lvalue.variable || lvalue.variable.writable !== true) {
    +
     4777.    118                warn("bad_assignment_a", lvalue);
    +
     4778.    118            }
    +
     4779.    118        }
    +
     4780.    118        const right = syntax[thing.expression[1].id];
    +
     4781.    118        if (
    +
     4782.    118            right !== undefined
    +
     4783.    118            && (
    +
     4784.    118                right.id === "function"
    +
     4785.    118                || right.id === "=>"
    +
     4786.    118                || (
    +
     4787.    118                    right.constant
    +
     4788.    118                    && right.id !== "(number)"
    +
     4789.    118                    && (right.id !== "(string)" || thing.id !== "+=")
    +
     4790.    118                )
    +
     4791.    118            )
    +
     4792.    118        ) {
    +
     4793.    118            warn("unexpected_a", thing.expression[1]);
    +
     4794.    118        }
    +
     4795.    118    }
    +
     4796.   1277});
    +
     4797.      1
    +
     4798.    590function postaction_function(thing) {
    +
     4799.    590    delete functionage.finally;
    +
     4800.    590    delete functionage.loop;
    +
     4801.    590    delete functionage.switch;
    +
     4802.    590    delete functionage.try;
    +
     4803.    590    functionage = stack.pop();
    +
     4804.      1    if (thing.wrapped) {
    +
     4805.      1
    +
     4806.      1// cause: "aa=(function(){})"
    +
     4807.      1
    +
     4808.      1        warn("unexpected_parens", thing);
    +
     4809.      1    }
    +
     4810.    590    return pop_block();
    +
     4811.    590}
    +
     4812.      1
    +
     4813.   9317postaction("binary", function (thing) {
    +
     4814.   9317    let right;
    +
     4815.   1557    if (relationop[thing.id]) {
    +
     4816.   1557        if (
    +
     4817.   1557            is_weird(thing.expression[0])
    +
     4818.   1557            || is_weird(thing.expression[1])
    +
     4819.   1557            || are_similar(thing.expression[0], thing.expression[1])
    +
     4820.   1557            || (
    +
     4821.   1557                thing.expression[0].constant === true
    +
     4822.   1557                && thing.expression[1].constant === true
    +
     4823.   1557            )
    +
     4824.   1557        ) {
    +
     4825.   1557
    +
     4826.   1557// cause: "if(0===0){0}"
    +
     4827.   1557
    +
     4828.   1557            warn("weird_relation_a", thing);
    +
     4829.   1557        }
    +
     4830.   1557    }
    +
     4831.    284    if (thing.id === "+") {
    +
     4832.    284        if (!option.convert) {
    +
     4833.    284            if (thing.expression[0].value === "") {
    +
     4834.    284                warn("expected_a_b", thing, "String(...)", "\"\" +");
    +
     4835.    284            } else if (thing.expression[1].value === "") {
    +
     4836.    284                warn("expected_a_b", thing, "String(...)", "+ \"\"");
    +
     4837.    284            }
    +
     4838.    284        }
    +
     4839.   9033    } else if (thing.id === "[") {
    +
     4840.   9033        if (thing.expression[0].id === "window") {
    +
     4841.   9033
    +
     4842.   9033// cause: "aa=window[0]"
    +
     4843.   9033
    +
     4844.   9033            warn("weird_expression_a", thing, "window[...]");
    +
     4845.   9033        }
    +
     4846.   9033        if (thing.expression[0].id === "self") {
    +
     4847.   9033
    +
     4848.   9033// cause: "aa=self[0]"
    +
     4849.   9033
    +
     4850.   9033            warn("weird_expression_a", thing, "self[...]");
    +
     4851.   9033        }
    +
     4852.   9033    } else if (thing.id === "." || thing.id === "?.") {
    +
     4853.   9033        if (thing.expression.id === "RegExp") {
    +
     4854.   9033
    +
     4855.   9033// cause: "aa=RegExp.aa"
    +
     4856.   9033
    +
     4857.   9033            warn("weird_expression_a", thing);
    +
     4858.   9033        }
    +
     4859.   9033    } else if (thing.id !== "=>" && thing.id !== "(") {
    +
     4860.   9033        right = thing.expression[1];
    +
     4861.   9033        if (
    +
     4862.   9033            (thing.id === "+" || thing.id === "-")
    +
     4863.   9033            && right.id === thing.id
    +
     4864.   9033            && right.arity === "unary"
    +
     4865.   9033            && !right.wrapped
    +
     4866.   9033        ) {
    +
     4867.   9033
    +
     4868.   9033// cause: "0- -0"
    +
     4869.   9033
    +
     4870.   9033            warn("wrap_unary", right);
    +
     4871.   9033        }
    +
     4872.   9033        if (
    +
     4873.   9033            thing.expression[0].constant === true
    +
     4874.   9033            && right.constant === true
    +
     4875.   9033        ) {
    +
     4876.   9033            thing.constant = true;
    +
     4877.   9033        }
    +
     4878.   9033    }
    +
     4879.   9317});
    +
     4880.    387postaction("binary", "&&", function (thing) {
    +
     4881.    387    if (
    +
     4882.    387        is_weird(thing.expression[0])
    +
     4883.    387        || are_similar(thing.expression[0], thing.expression[1])
    +
     4884.    374        || thing.expression[0].constant === true
    +
     4885.    373        || thing.expression[1].constant === true
    +
     4886.     14    ) {
    +
     4887.     14
    +
     4888.     14// cause: "aa=0&&0"
    +
     4889.     14// cause: "aa=`${0}`&&`${0}`"
    +
     4890.     14// cause: "aa=(``?``:``)&&(``?``:``)"
    +
     4891.     14
    +
     4892.     14        warn("weird_condition_a", thing);
    +
     4893.     14    }
    +
     4894.    387});
    +
     4895.    360postaction("binary", "||", function (thing) {
    +
     4896.    360    if (
    +
     4897.    360        is_weird(thing.expression[0])
    +
     4898.    360        || are_similar(thing.expression[0], thing.expression[1])
    +
     4899.    359        || thing.expression[0].constant === true
    +
     4900.      2    ) {
    +
     4901.      2
    +
     4902.      2// cause: "aa=0||0"
    +
     4903.      2
    +
     4904.      2        warn("weird_condition_a", thing);
    +
     4905.      2    }
    +
     4906.    360});
    +
     4907.      1postaction("binary", "=>", postaction_function);
    +
     4908.   2878postaction("binary", "(", function (thing) {
    +
     4909.   2878    let left = thing.expression[0];
    +
     4910.   2878    let the_new;
    +
     4911.   2878    let arg;
    +
     4912.     29    if (left.id === "new") {
    +
     4913.     29        the_new = left;
    +
     4914.     29        left = left.expression;
    +
     4915.     29    }
    +
     4916.     65    if (left.id === "function") {
    +
     4917.     65        if (!thing.wrapped) {
    +
     4918.     65
    +
     4919.     65// cause: "aa=function(){}()"
    +
     4920.     65
    +
     4921.     65            warn("wrap_immediate", thing);
    +
     4922.     65        }
    +
     4923.   2813    } else if (left.identifier) {
    +
     4924.   2813        if (the_new !== undefined) {
    +
     4925.   2813            if (
    +
     4926.   2813                left.id[0] > "Z"
    +
     4927.   2813                || left.id === "Boolean"
    +
     4928.   2813                || left.id === "Number"
    +
     4929.   2813                || left.id === "String"
    +
     4930.   2813                || left.id === "Symbol"
    +
     4931.   2813            ) {
    +
     4932.   2813
    +
     4933.   2813// cause: "new Boolean()"
    +
     4934.   2813// cause: "new Number()"
    +
     4935.   2813// cause: "new String()"
    +
     4936.   2813// cause: "new Symbol()"
    +
     4937.   2813// cause: "new aa()"
    +
     4938.   2813
    +
     4939.   2813                warn("unexpected_a", the_new);
    +
     4940.   2813            } else if (left.id === "Function") {
    +
     4941.   2813                if (!option.eval) {
    +
     4942.   2813                    warn("unexpected_a", left, "new Function");
    +
     4943.   2813                }
    +
     4944.   2813            } else if (left.id === "Array") {
    +
     4945.   2813                arg = thing.expression;
    +
     4946.   2813                if (arg.length !== 2 || arg[1].id === "(string)") {
    +
     4947.   2813
    +
     4948.   2813// cause: "new Array()"
    +
     4949.   2813
    +
     4950.   2813                    warn("expected_a_b", left, "[]", "new Array");
    +
     4951.   2813                }
    +
     4952.   2813            } else if (left.id === "Object") {
    +
     4953.   2813
    +
     4954.   2813// cause: "new Object()"
    +
     4955.   2813
    +
     4956.   2813                warn(
    +
     4957.   2813                    "expected_a_b",
    +
     4958.   2813                    left,
    +
     4959.   2813                    "Object.create(null)",
    +
     4960.   2813                    "new Object"
    +
     4961.   2813                );
    +
     4962.   2813            }
    +
     4963.   2813        } else {
    +
     4964.   2813            if (
    +
     4965.   2813                left.id[0] >= "A"
    +
     4966.   2813                && left.id[0] <= "Z"
    +
     4967.   2813                && left.id !== "Boolean"
    +
     4968.   2813                && left.id !== "Number"
    +
     4969.   2813                && left.id !== "String"
    +
     4970.   2813                && left.id !== "Symbol"
    +
     4971.   2813            ) {
    +
     4972.   2813
    +
     4973.   2813// cause: "let Aa=Aa()"
    +
     4974.   2813
    +
     4975.   2813                warn(
    +
     4976.   2813                    "expected_a_before_b",
    +
     4977.   2813                    left,
    +
     4978.   2813                    "new",
    +
     4979.   2813                    artifact(left)
    +
     4980.   2813                );
    +
     4981.   2813            }
    +
     4982.   2813        }
    +
     4983.   2813    } else if (left.id === ".") {
    +
     4984.   2813        let cack = the_new !== undefined;
    +
     4985.   2813        if (left.expression.id === "Date" && left.name.id === "UTC") {
    +
     4986.   2813            cack = !cack;
    +
     4987.   2813        }
    +
     4988.   2813        if (rx_cap.test(left.name.id) !== cack) {
    +
     4989.   2813            if (the_new !== undefined) {
    +
     4990.   2813                warn("unexpected_a", the_new);
    +
     4991.   2813            } else {
    +
     4992.   2813
    +
     4993.   2813// cause: "let Aa=Aa.Aa()"
    +
     4994.   2813
    +
     4995.   2813                warn(
    +
     4996.   2813                    "expected_a_before_b",
    +
     4997.   2813                    left.expression,
    +
     4998.   2813                    "new",
    +
     4999.   2813                    left.name.id
    +
     5000.   2813                );
    +
     5001.   2813            }
    +
     5002.   2813        }
    +
     5003.   2813        if (left.name.id === "getTime") {
    +
     5004.   2813            const paren = left.expression;
    +
     5005.   2813            if (paren.id === "(") {
    +
     5006.   2813                const array = paren.expression;
    +
     5007.   2813                if (array.length === 1) {
    +
     5008.   2813                    const new_date = array[0];
    +
     5009.   2813                    if (
    +
     5010.   2813                        new_date.id === "new"
    +
     5011.   2813                        && new_date.expression.id === "Date"
    +
     5012.   2813                    ) {
    +
     5013.   2813
    +
     5014.   2813// cause: "new Date().getTime()"
    +
     5015.   2813
    +
     5016.   2813                        warn(
    +
     5017.   2813                            "expected_a_b",
    +
     5018.   2813                            new_date,
    +
     5019.   2813                            "Date.now()",
    +
     5020.   2813                            "new Date().getTime()"
    +
     5021.   2813                        );
    +
     5022.   2813                    }
    +
     5023.   2813                }
    +
     5024.   2813            }
    +
     5025.   2813        }
    +
     5026.   2813    }
    +
     5027.   2878});
    +
     5028.    417postaction("binary", "[", function (thing) {
    +
     5029.      1    if (thing.expression[0].id === "RegExp") {
    +
     5030.      1
    +
     5031.      1// cause: "aa=RegExp[0]"
    +
     5032.      1
    +
     5033.      1        warn("weird_expression_a", thing);
    +
     5034.      1    }
    +
     5035.      1    if (is_weird(thing.expression[1])) {
    +
     5036.      1
    +
     5037.      1// cause: "aa[[0]]"
    +
     5038.      1
    +
     5039.      1        warn("weird_expression_a", thing.expression[1]);
    +
     5040.      1    }
    +
     5041.    417});
    +
     5042.      1postaction("statement", "{", pop_block);
    +
     5043.      1postaction("statement", "const", action_var);
    +
     5044.      1postaction("statement", "export", top_level_only);
    +
     5045.      7postaction("statement", "for", function (thing) {
    +
     5046.      7    walk_statement(thing.inc);
    +
     5047.      7});
    +
     5048.      1postaction("statement", "function", postaction_function);
    +
     5049.     11postaction("statement", "import", function (the_thing) {
    +
     5050.     11    const name = the_thing.name;
    +
     5051.     11    if (name) {
    +
     5052.      3        if (Array.isArray(name)) {
    +
     5053.      3            name.forEach(function (name) {
    +
     5054.      3                name.dead = false;
    +
     5055.      3                name.init = true;
    +
     5056.      3                blockage.live.push(name);
    +
     5057.      3            });
    +
     5058.      8        } else {
    +
     5059.      8            name.dead = false;
    +
     5060.      8            name.init = true;
    +
     5061.      8            blockage.live.push(name);
    +
     5062.      8        }
    +
     5063.     11        return top_level_only(the_thing);
    +
     5064.     11    }
    +
     5065.     11});
    +
     5066.      1postaction("statement", "let", action_var);
    +
     5067.     11postaction("statement", "try", function (thing) {
    +
     5068.     10    if (thing.catch !== undefined) {
    +
     5069.     10        const the_name = thing.catch.name;
    +
     5070.     10        if (the_name !== undefined) {
    +
     5071.     10            const the_variable = functionage.context[the_name.id];
    +
     5072.     10            the_variable.dead = false;
    +
     5073.     10            the_variable.init = true;
    +
     5074.     10        }
    +
     5075.     10        walk_statement(thing.catch.block);
    +
     5076.     10    }
    +
     5077.     11});
    +
     5078.      1postaction("statement", "var", action_var);
    +
     5079.     59postaction("ternary", function (thing) {
    +
     5080.     59    if (
    +
     5081.     59        is_weird(thing.expression[0])
    +
     5082.     59        || thing.expression[0].constant === true
    +
     5083.     53        || are_similar(thing.expression[1], thing.expression[2])
    +
     5084.      6    ) {
    +
     5085.      6        warn("unexpected_a", thing);
    +
     5086.     53    } else if (are_similar(thing.expression[0], thing.expression[1])) {
    +
     5087.     53        warn("expected_a_b", thing, "||", "?");
    +
     5088.     53    } else if (are_similar(thing.expression[0], thing.expression[2])) {
    +
     5089.     53        warn("expected_a_b", thing, "&&", "?");
    +
     5090.     53    } else if (
    +
     5091.     53        thing.expression[1].id === "true"
    +
     5092.     53        && thing.expression[2].id === "false"
    +
     5093.     53    ) {
    +
     5094.     53        warn("expected_a_b", thing, "!!", "?");
    +
     5095.     53    } else if (
    +
     5096.     53        thing.expression[1].id === "false"
    +
     5097.     53        && thing.expression[2].id === "true"
    +
     5098.     53    ) {
    +
     5099.     53        warn("expected_a_b", thing, "!", "?");
    +
     5100.     53    } else if (
    +
     5101.     53        thing.expression[0].wrapped !== true
    +
     5102.     53        && (
    +
     5103.     53            thing.expression[0].id === "||"
    +
     5104.     53            || thing.expression[0].id === "&&"
    +
     5105.     53        )
    +
     5106.     53    ) {
    +
     5107.     53
    +
     5108.     53// cause: "(aa&&!aa?0:1)"
    +
     5109.     53
    +
     5110.     53        warn("wrap_condition", thing.expression[0]);
    +
     5111.     53    }
    +
     5112.     59});
    +
     5113.    920postaction("unary", function (thing) {
    +
     5114.     34    if (thing.id === "`") {
    +
     5115.     34        if (thing.expression.every(function (thing) {
    +
     5116.     34            return thing.constant;
    +
     5117.     34        })) {
    +
     5118.     34            thing.constant = true;
    +
     5119.     34        }
    +
     5120.    886    } else if (thing.id === "!") {
    +
     5121.    886        if (thing.expression.constant === true) {
    +
     5122.    886            warn("unexpected_a", thing);
    +
     5123.    886        }
    +
     5124.    886    } else if (thing.id === "!!") {
    +
     5125.    886        if (!option.convert) {
    +
     5126.    886
    +
     5127.    886// cause: "!!0"
    +
     5128.    886
    +
     5129.    886            warn("expected_a_b", thing, "Boolean(...)", "!!");
    +
     5130.    886        }
    +
     5131.    886    } else if (
    +
     5132.    886        thing.id !== "["
    +
     5133.    886        && thing.id !== "{"
    +
     5134.    886        && thing.id !== "function"
    +
     5135.    886        && thing.id !== "new"
    +
     5136.    886    ) {
    +
     5137.    886        if (thing.expression.constant === true) {
    +
     5138.    886            thing.constant = true;
    +
     5139.    886        }
    +
     5140.    886    }
    +
     5141.    920});
    +
     5142.      1postaction("unary", "function", postaction_function);
    +
     5143.      9postaction("unary", "+", function (thing) {
    +
     5144.      9    if (!option.convert) {
    +
     5145.      9        warn("expected_a_b", thing, "Number(...)", "+");
    +
     5146.      9    }
    +
     5147.      9    const right = thing.expression;
    +
     5148.      1    if (right.id === "(" && right.expression[0].id === "new") {
    +
     5149.      1        warn("unexpected_a_before_b", thing, "+", "new");
    +
     5150.      8    } else if (
    +
     5151.      8        right.constant
    +
     5152.      8        || right.id === "{"
    +
     5153.      8        || (right.id === "[" && right.arity !== "binary")
    +
     5154.      8    ) {
    +
     5155.      8        warn("unexpected_a", thing, "+");
    +
     5156.      8    }
    +
     5157.      9});
    +
     5158.      1
    +
     5159.    567function delve(the_function) {
    +
     5160.   3797    Object.keys(the_function.context).forEach(function (id) {
    +
     5161.   3784        if (id !== "ignore") {
    +
     5162.   3784            const name = the_function.context[id];
    +
     5163.   3784            if (name.parent === the_function) {
    +
     5164.   3784                if (
    +
     5165.   3784                    name.used === 0
    +
     5166.   3784                    && (
    +
     5167.   3784                        name.role !== "function"
    +
     5168.      0                        || name.parent.arity !== "unary"
    +
     5169.   3784                    )
    +
     5170.   3784                ) {
    +
     5171.   3784
    +
     5172.   3784// cause: "/*jslint node*/\nlet aa;"
    +
     5173.   3784// cause: "function aa(aa){return;}"
    +
     5174.   3784
    +
     5175.   3784                    warn("unused_a", name);
    +
     5176.   3784                } else if (!name.init) {
    +
     5177.   3784
    +
     5178.   3784// cause: "/*jslint node*/\nlet aa;aa();"
    +
     5179.   3784
    +
     5180.   3784                    warn("uninitialized_a", name);
    +
     5181.   3784                }
    +
     5182.   3784            }
    +
     5183.   3784        }
    +
     5184.   3797    });
    +
     5185.    567}
    +
     5186.      1
    +
     5187.     69function uninitialized_and_unused() {
    +
     5188.     69
    +
     5189.     69// Delve into the functions looking for variables that were not initialized
    +
     5190.     69// or used. If the file imports or exports, then its global object is also
    +
     5191.     69// delved.
    +
     5192.     69
    +
     5193.     61    if (module_mode === true || option.node) {
    +
     5194.     18        delve(global);
    +
     5195.     18    }
    +
     5196.     69    functions.forEach(delve);
    +
     5197.     69}
    +
     5198.      1
    +
     5199.      1// Go through the token list, looking at usage of whitespace.
    +
     5200.      1
    +
     5201.     67function whitage() {
    +
     5202.     67    let closer = "(end)";
    +
     5203.     67
    +
     5204.     67// free = false
    +
     5205.     67
    +
     5206.     67// cause: "()=>0"
    +
     5207.     67// cause: "aa()"
    +
     5208.     67// cause: "aa(0,0)"
    +
     5209.     67// cause: "function(){}"
    +
     5210.     67
    +
     5211.     67    let free = false;
    +
     5212.     67
    +
     5213.     67// cause: "(0)"
    +
     5214.     67// cause: "(aa)"
    +
     5215.     67// cause: "aa(0)"
    +
     5216.     67// cause: "do{}while()"
    +
     5217.     67// cause: "for(){}"
    +
     5218.     67// cause: "if(){}"
    +
     5219.     67// cause: "switch(){}"
    +
     5220.     67// cause: "while(){}"
    +
     5221.     67
    +
     5222.     67    // let free = true;
    +
     5223.     67
    +
     5224.     67    let left = global;
    +
     5225.     67    let margin = 0;
    +
     5226.     67    let nr_comments_skipped = 0;
    +
     5227.     67    let open = true;
    +
     5228.     67    let opening = true;
    +
     5229.     67    let right;
    +
     5230.     67
    +
     5231.   6751    function pop() {
    +
     5232.   6751        const previous = stack.pop();
    +
     5233.   6751        closer = previous.closer;
    +
     5234.   6751        free = previous.free;
    +
     5235.   6751        margin = previous.margin;
    +
     5236.   6751        open = previous.open;
    +
     5237.   6751        opening = previous.opening;
    +
     5238.   6751    }
    +
     5239.     67
    +
     5240.   6751    function push() {
    +
     5241.   6751        stack.push({
    +
     5242.   6751            closer,
    +
     5243.   6751            free,
    +
     5244.   6751            margin,
    +
     5245.   6751            open,
    +
     5246.   6751            opening
    +
     5247.   6751        });
    +
     5248.   6751    }
    +
     5249.     67
    +
     5250.     27    function expected_at(at) {
    +
     5251.     27        assert_or_throw(right !== undefined, "Expected right !== undefined.");
    +
     5252.     27//      Probably deadcode.
    +
     5253.     27//      if (right === undefined) {
    +
     5254.     27//          right = next_token;
    +
     5255.     27//      }
    +
     5256.     27        warn(
    +
     5257.     27            "expected_a_at_b_c",
    +
     5258.     27            right,
    +
     5259.     27            artifact(right),
    +
     5260.     27            fudge + at,
    +
     5261.     27
    +
     5262.     27// Return the fudged column number of an artifact.
    +
     5263.     27
    +
     5264.     27            right.from + fudge
    +
     5265.     27        );
    +
     5266.     27    }
    +
     5267.     67
    +
     5268.   9683    function at_margin(fit) {
    +
     5269.   9683        const at = margin + fit;
    +
     5270.     22        if (right.from !== at) {
    +
     5271.     22            return expected_at(at);
    +
     5272.     22        }
    +
     5273.   9683    }
    +
     5274.     67
    +
     5275.  26043    function no_space_only() {
    +
     5276.  26043        if (
    +
     5277.  26043            left.id !== "(global)"
    +
     5278.  26042            && left.nr + 1 === right.nr
    +
     5279.  26042            && (
    +
     5280.  26042                left.line !== right.line
    +
     5281.  26042                || left.thru !== right.from
    +
     5282.  26042            )
    +
     5283.     13        ) {
    +
     5284.     13
    +
     5285.     13// cause:
    +
     5286.     13//  (
    +
     5287.     13// function aa (
    +
     5288.     13// bb
    +
     5289.     13// ,
    +
     5290.     13// [
    +
     5291.     13// cc,dd
    +
     5292.     13// ]
    +
     5293.     13// ,
    +
     5294.     13// {
    +
     5295.     13// ee,ff=( 0 )
    +
     5296.     13// }
    +
     5297.     13// ,
    +
     5298.     13// ... zz
    +
     5299.     13// )
    +
     5300.     13// {
    +
     5301.     13// return {
    +
     5302.     13// aa,bb
    +
     5303.     13// }
    +
     5304.     13// ;
    +
     5305.     13// }
    +
     5306.     13// (
    +
     5307.     13// )
    +
     5308.     13//  )
    +
     5309.     13//  ;
    +
     5310.     13
    +
     5311.     13            warn(
    +
     5312.     13                "unexpected_space_a_b",
    +
     5313.     13                right,
    +
     5314.     13                artifact(left),
    +
     5315.     13                artifact(right)
    +
     5316.     13            );
    +
     5317.     13        }
    +
     5318.  26043    }
    +
     5319.     67
    +
     5320.    958    function no_space() {
    +
     5321.    956        if (left.line === right.line) {
    +
     5322.    956            if (left.thru !== right.from && nr_comments_skipped === 0) {
    +
     5323.    956
    +
     5324.    956// cause: "let aa = aa()( );"
    +
     5325.    956
    +
     5326.    956                warn(
    +
     5327.    956                    "unexpected_space_a_b",
    +
     5328.    956                    right,
    +
     5329.    956                    artifact(left),
    +
     5330.    956                    artifact(right)
    +
     5331.    956                );
    +
     5332.    956            }
    +
     5333.    956        } else {
    +
     5334.      2            if (open) {
    +
     5335.      2                const at = (
    +
     5336.      2                    free
    +
     5337.      2                    ? margin
    +
     5338.      0                    : margin + 8 // deadcode?
    +
     5339.      2                );
    +
     5340.      2                if (right.from < at) {
    +
     5341.      2
    +
     5342.      2// cause:
    +
     5343.      2// let aa = aa(
    +
     5344.      2//     aa
    +
     5345.      2// ()
    +
     5346.      2// );
    +
     5347.      2
    +
     5348.      2                    expected_at(at);
    +
     5349.      2                }
    +
     5350.      0            } else { // deadcode?
    +
     5351.      0                if (right.from !== margin + 8) {
    +
     5352.      0                    expected_at(margin + 8);
    +
     5353.      0                }
    +
     5354.      0            }
    +
     5355.      2        }
    +
     5356.    958    }
    +
     5357.     67
    +
     5358.   2537    function one_space_only() {
    +
     5359.   2536        if (left.line !== right.line || left.thru + 1 !== right.from) {
    +
     5360.      6            warn(
    +
     5361.      6                "expected_space_a_b",
    +
     5362.      6                right,
    +
     5363.      6                artifact(left),
    +
     5364.      6                artifact(right)
    +
     5365.      6            );
    +
     5366.      6        }
    +
     5367.   2537    }
    +
     5368.     67
    +
     5369.  13964    function one_space() {
    +
     5370.  13446        if (left.line === right.line || !open) {
    +
     5371.  13446            if (left.thru + 1 !== right.from && nr_comments_skipped === 0) {
    +
     5372.  13446                warn(
    +
     5373.  13446                    "expected_space_a_b",
    +
     5374.  13446                    right,
    +
     5375.  13446                    artifact(left),
    +
     5376.  13446                    artifact(right)
    +
     5377.  13446                );
    +
     5378.  13446            }
    +
     5379.  13446        } else {
    +
     5380.    518            if (right.from !== margin) {
    +
     5381.    518                expected_at(margin);
    +
     5382.    518            }
    +
     5383.    518        }
    +
     5384.  13964    }
    +
     5385.     67
    +
     5386.     67    stack = [];
    +
     5387.  55061    tokens.forEach(function (the_token) {
    +
     5388.  55061        right = the_token;
    +
     5389.  53417        if (right.id === "(comment)" || right.id === "(end)") {
    +
     5390.   1712            nr_comments_skipped += 1;
    +
     5391.  53349        } else {
    +
     5392.  53349
    +
     5393.  53349// If left is an opener and right is not the closer, then push the previous
    +
     5394.  53349// state. If the token following the opener is on the next line, then this is
    +
     5395.  53349// an open form. If the tokens are on the same line, then it is a closed form.
    +
     5396.  53349// Open form is more readable, with each item (statement, argument, parameter,
    +
     5397.  53349// etc) starting on its own line. Closed form is more compact. Statement blocks
    +
     5398.  53349// are always in open form.
    +
     5399.  53349
    +
     5400.  53349            const new_closer = opener[left.id];
    +
     5401.  53349            if (typeof new_closer === "string") {
    +
     5402.  53349
    +
     5403.  53349// cause: "${"
    +
     5404.  53349// cause: "("
    +
     5405.  53349// cause: "["
    +
     5406.  53349// cause: "{"
    +
     5407.  53349
    +
     5408.  53349                if (new_closer !== right.id) {
    +
     5409.  53349
    +
     5410.  53349// cause: "${0"
    +
     5411.  53349// cause: "(0"
    +
     5412.  53349// cause: "[0"
    +
     5413.  53349// cause: "{0"
    +
     5414.  53349
    +
     5415.  53349                    opening = left.open || (left.line !== right.line);
    +
     5416.  53349                    push();
    +
     5417.  53349                    closer = new_closer;
    +
     5418.  53349                    if (opening) {
    +
     5419.  53349
    +
     5420.  53349// cause: "${\n0\n}"
    +
     5421.  53349// cause: "(\n0\n)"
    +
     5422.  53349// cause: "[\n0\n]"
    +
     5423.  53349// cause: "{\n0\n}"
    +
     5424.  53349
    +
     5425.  53349                        free = closer === ")" && left.free;
    +
     5426.  53349                        open = true;
    +
     5427.  53349                        margin += 4;
    +
     5428.  53349                        if (right.role === "label") {
    +
     5429.  53349                            if (right.from !== 0) {
    +
     5430.  53349
    +
     5431.  53349// cause:
    +
     5432.  53349// function aa() {
    +
     5433.  53349//  bb:
    +
     5434.  53349//     while (aa) {
    +
     5435.  53349//         if (aa) {
    +
     5436.  53349//             break bb;
    +
     5437.  53349//         }
    +
     5438.  53349//     }
    +
     5439.  53349// }
    +
     5440.  53349
    +
     5441.  53349                                expected_at(0);
    +
     5442.  53349                            }
    +
     5443.  53349                        } else if (right.switch) {
    +
     5444.  53349                            at_margin(-4);
    +
     5445.  53349                        } else {
    +
     5446.  53349                            at_margin(0);
    +
     5447.  53349                        }
    +
     5448.  53349                    } else {
    +
     5449.  53349                        if (right.statement || right.role === "label") {
    +
     5450.  53349
    +
     5451.  53349// cause:
    +
     5452.  53349// function aa() {bb:
    +
     5453.  53349//     while (aa) {aa();
    +
     5454.  53349//     }
    +
     5455.  53349// }
    +
     5456.  53349
    +
     5457.  53349                            warn(
    +
     5458.  53349                                "expected_line_break_a_b",
    +
     5459.  53349                                right,
    +
     5460.  53349                                artifact(left),
    +
     5461.  53349                                artifact(right)
    +
     5462.  53349                            );
    +
     5463.  53349                        }
    +
     5464.  53349
    +
     5465.  53349// cause: "${0}"
    +
     5466.  53349// cause: "(0)"
    +
     5467.  53349// cause: "[0]"
    +
     5468.  53349// cause: "{0}"
    +
     5469.  53349
    +
     5470.  53349                        free = false;
    +
     5471.  53349                        open = false;
    +
     5472.  53349
    +
     5473.  53349// cause: "let aa = ( 0 );"
    +
     5474.  53349
    +
     5475.  53349                        no_space_only();
    +
     5476.  53349                    }
    +
     5477.  53349                } else {
    +
     5478.  53349
    +
     5479.  53349// If left and right are opener and closer, then the placement of right depends
    +
     5480.  53349// on the openness. Illegal pairs (like '{]') have already been detected.
    +
     5481.  53349
    +
     5482.  53349// cause: "${}"
    +
     5483.  53349// cause: "()"
    +
     5484.  53349// cause: "[]"
    +
     5485.  53349// cause: "{}"
    +
     5486.  53349
    +
     5487.  53349                    if (left.line === right.line) {
    +
     5488.  53349                        no_space();
    +
     5489.  53349                    } else {
    +
     5490.  53349                        at_margin(0);
    +
     5491.  53349                    }
    +
     5492.  53349                }
    +
     5493.  53349            } else {
    +
     5494.  53349                if (right.statement === true) {
    +
     5495.  53349                    if (left.id === "else") {
    +
     5496.  53349                        one_space_only();
    +
     5497.  53349                    } else {
    +
     5498.  53349                        at_margin(0);
    +
     5499.  53349                        open = false;
    +
     5500.  53349                    }
    +
     5501.  53349
    +
     5502.  53349// If right is a closer, then pop the previous state.
    +
     5503.  53349
    +
     5504.  53349                } else if (right.id === closer) {
    +
     5505.  53349                    pop();
    +
     5506.  53349                    if (opening && right.id !== ";") {
    +
     5507.  53349                        at_margin(0);
    +
     5508.  53349                    } else {
    +
     5509.  53349                        no_space_only();
    +
     5510.  53349                    }
    +
     5511.  53349                } else {
    +
     5512.  53349
    +
     5513.  53349// Left is not an opener, and right is not a closer.
    +
     5514.  53349// The nature of left and right will determine the space between them.
    +
     5515.  53349
    +
     5516.  53349// If left is ',' or ';' or right is a statement then if open,
    +
     5517.  53349// right must go at the margin, or if closed, a space between.
    +
     5518.  53349
    +
     5519.  53349                    if (right.switch) {
    +
     5520.  53349                        at_margin(-4);
    +
     5521.  53349                    } else if (right.role === "label") {
    +
     5522.  53349                        if (right.from !== 0) {
    +
     5523.  53349
    +
     5524.  53349// cause:
    +
     5525.  53349// function aa() {
    +
     5526.  53349//     let bb = 0;cc:
    +
     5527.  53349//     while (aa) {
    +
     5528.  53349//         if (aa) {
    +
     5529.  53349//             break cc;
    +
     5530.  53349//         }
    +
     5531.  53349//     }
    +
     5532.  53349// }
    +
     5533.  53349
    +
     5534.  53349                            expected_at(0);
    +
     5535.  53349                        }
    +
     5536.  53349                    } else if (left.id === ",") {
    +
     5537.  53349                        if (!open || (
    +
     5538.  53349                            (free || closer === "]")
    +
     5539.  53349                            && left.line === right.line
    +
     5540.  53349                        )) {
    +
     5541.  53349
    +
     5542.  53349// cause: "let {aa,bb}=0;"
    +
     5543.  53349
    +
     5544.  53349                            one_space();
    +
     5545.  53349                        } else {
    +
     5546.  53349                            at_margin(0);
    +
     5547.  53349                        }
    +
     5548.  53349
    +
     5549.  53349// If right is a ternary operator, line it up on the margin.
    +
     5550.  53349
    +
     5551.  53349                    } else if (right.arity === "ternary") {
    +
     5552.  53349                        if (open) {
    +
     5553.  53349
    +
     5554.  53349// cause:
    +
     5555.  53349// let aa = (
    +
     5556.  53349//     aa
    +
     5557.  53349//     ? 0
    +
     5558.  53349// : 1
    +
     5559.  53349// );
    +
     5560.  53349
    +
     5561.  53349                            at_margin(0);
    +
     5562.  53349                        } else {
    +
     5563.  53349
    +
     5564.  53349// cause: "let aa=(aa?0:1);"
    +
     5565.  53349
    +
     5566.  53349                            warn("use_open", right);
    +
     5567.  53349                        }
    +
     5568.  53349                    } else if (
    +
     5569.  53349                        right.arity === "binary"
    +
     5570.  53349                        && right.id === "("
    +
     5571.  53349                        && free
    +
     5572.  53349                    ) {
    +
     5573.  53349                        no_space();
    +
     5574.  53349                    } else if (
    +
     5575.  53349                        left.id === "."
    +
     5576.  53349                        || left.id === "?."
    +
     5577.  53349                        || left.id === "..."
    +
     5578.  53349                        || right.id === ","
    +
     5579.  53349                        || right.id === ";"
    +
     5580.  53349                        || right.id === ":"
    +
     5581.  53349                        || (
    +
     5582.  53349                            right.arity === "binary"
    +
     5583.  53349                            && (right.id === "(" || right.id === "[")
    +
     5584.  53349                        )
    +
     5585.  53349                        || (
    +
     5586.  53349                            right.arity === "function"
    +
     5587.  53349                            && left.id !== "function"
    +
     5588.  53349                        )
    +
     5589.  53349                    ) {
    +
     5590.  53349                        no_space_only();
    +
     5591.  53349                    } else if (right.id === "." || right.id === "?.") {
    +
     5592.  53349                        no_space_only();
    +
     5593.      0                    } else if (left.id === ";") { // deadcode?
    +
     5594.      0                        if (open) {
    +
     5595.      0                            at_margin(0);
    +
     5596.      0                        }
    +
     5597.      0                    } else if (
    +
     5598.  53349                        left.arity === "ternary"
    +
     5599.  53349                        || left.id === "case"
    +
     5600.  53349                        || left.id === "catch"
    +
     5601.  53349                        || left.id === "else"
    +
     5602.  53349                        || left.id === "finally"
    +
     5603.  53349                        || left.id === "while"
    +
     5604.  53349                        || left.id === "await"
    +
     5605.  53349                        || right.id === "catch"
    +
     5606.  53349                        || right.id === "else"
    +
     5607.  53349                        || right.id === "finally"
    +
     5608.  53349                        || (right.id === "while" && !right.statement)
    +
     5609.  53349                        || (left.id === ")" && right.id === "{")
    +
     5610.  53349                    ) {
    +
     5611.  53349
    +
     5612.  53349// cause:
    +
     5613.  53349// function aa() {
    +
     5614.  53349//     do {
    +
     5615.  53349//         aa();
    +
     5616.  53349//     } while(aa());
    +
     5617.  53349// }
    +
     5618.  53349
    +
     5619.  53349                        one_space_only();
    +
     5620.  53349                    } else if (
    +
     5621.  53349
    +
     5622.  53349// There is a space between left and right.
    +
     5623.  53349
    +
     5624.  53349                        spaceop[left.id] === true
    +
     5625.  53349                        || spaceop[right.id] === true
    +
     5626.  53349                        || (
    +
     5627.  53349                            left.arity === "binary"
    +
     5628.  53349                            && (left.id === "+" || left.id === "-")
    +
     5629.  53349                        )
    +
     5630.  53349                        || (
    +
     5631.  53349                            right.arity === "binary"
    +
     5632.  53349                            && (right.id === "+" || right.id === "-")
    +
     5633.  53349                        )
    +
     5634.  53349                        || left.id === "function"
    +
     5635.  53349                        || left.id === ":"
    +
     5636.  53349                        || (
    +
     5637.  53349                            (
    +
     5638.  53349                                left.identifier
    +
     5639.  53349                                || left.id === "(string)"
    +
     5640.  53349                                || left.id === "(number)"
    +
     5641.  53349                            )
    +
     5642.  53349                            && (
    +
     5643.  53349                                right.identifier
    +
     5644.  53349                                || right.id === "(string)"
    +
     5645.  53349                                || right.id === "(number)"
    +
     5646.  53349                            )
    +
     5647.  53349                        )
    +
     5648.  53349                        || (left.arity === "statement" && right.id !== ";")
    +
     5649.  53349                    ) {
    +
     5650.  53349
    +
     5651.  53349// cause: "let aa=0;"
    +
     5652.  53349// cause:
    +
     5653.  53349// let aa={
    +
     5654.  53349//     aa:
    +
     5655.  53349// 0
    +
     5656.  53349// };
    +
     5657.  53349
    +
     5658.  53349                        one_space();
    +
     5659.  53349                    } else if (left.arity === "unary" && left.id !== "`") {
    +
     5660.  53349                        no_space_only();
    +
     5661.  53349                    }
    +
     5662.  53349                }
    +
     5663.  53349            }
    +
     5664.  53349            nr_comments_skipped = 0;
    +
     5665.  53349            delete left.calls;
    +
     5666.  53349            delete left.dead;
    +
     5667.  53349            delete left.free;
    +
     5668.  53349            delete left.init;
    +
     5669.  53349            delete left.open;
    +
     5670.  53349            delete left.used;
    +
     5671.  53349            left = right;
    +
     5672.  53349        }
    +
     5673.  55061    });
    +
     5674.     67}
    +
     5675.      1
    +
     5676.      1// The jslint function itself.
    +
     5677.      1
    +
     5678.    405function jslint(
    +
     5679.    405    source = "",
    +
     5680.    405    option_object = empty(),
    +
     5681.    405    global_array = []
    +
     5682.    405) {
    +
     5683.    405    try {
    +
     5684.    405        warnings = [];
    +
     5685.    405        option = Object.assign(empty(), option_object);
    +
     5686.    405        anon = "anonymous";
    +
     5687.    405        block_stack = [];
    +
     5688.    405        declared_globals = empty();
    +
     5689.    405        directive_mode = true;
    +
     5690.    405        directives = [];
    +
     5691.    405        early_stop = true;
    +
     5692.    405        exports = empty();
    +
     5693.    405        froms = [];
    +
     5694.    405        fudge = (
    +
     5695.    405            option.fudge
    +
     5696.      1            ? 1
    +
     5697.    404            : 0
    +
     5698.    405        );
    +
     5699.    405        functions = [];
    +
     5700.    405        global = {
    +
     5701.    405            body: true,
    +
     5702.    405            context: empty(),
    +
     5703.    405            finally: 0,
    +
     5704.    405            from: 0,
    +
     5705.    405            id: "(global)",
    +
     5706.    405            level: 0,
    +
     5707.    405            line: 0,
    +
     5708.    405            live: [],
    +
     5709.    405            loop: 0,
    +
     5710.    405            switch: 0,
    +
     5711.    405            thru: 0,
    +
     5712.    405            try: 0
    +
     5713.    405        };
    +
     5714.    405        blockage = global;
    +
     5715.    405        functionage = global;
    +
     5716.    405        json_mode = false;
    +
     5717.    405        mega_mode = false;
    +
     5718.    405        module_mode = false;
    +
     5719.    405        next_token = global;
    +
     5720.    405        property = empty();
    +
     5721.    405        shebang = false;
    +
     5722.    405        stack = [];
    +
     5723.    405        tenure = undefined;
    +
     5724.    405        token = global;
    +
     5725.    405        token_nr = 0;
    +
     5726.    405        var_mode = undefined;
    +
     5727.    405        populate(standard, declared_globals, false);
    +
     5728.    405        populate(global_array, declared_globals, false);
    +
     5729.     24        Object.keys(option).forEach(function (name) {
    +
     5730.     24            if (option[name] === true) {
    +
     5731.     24                const allowed = allowed_option[name];
    +
     5732.     11                if (Array.isArray(allowed)) {
    +
     5733.     11                    populate(allowed, declared_globals, false);
    +
     5734.     11                }
    +
     5735.     24            }
    +
     5736.     24        });
    +
     5737.    405        tokenize(source);
    +
     5738.    405        advance();
    +
     5739.     23        if (json_mode) {
    +
     5740.     23            tree = json_value();
    +
     5741.     23            advance("(end)");
    +
     5742.    356        } else {
    +
     5743.    356
    +
     5744.    356// Because browsers encourage combining of script files, the first token might
    +
     5745.    356// be a semicolon to defend against a missing semicolon in the preceding file.
    +
     5746.    356
    +
     5747.    356            if (option.browser) {
    +
     5748.    356                if (next_token.id === ";") {
    +
     5749.    356                    advance(";");
    +
     5750.    356                }
    +
     5751.    356            } else {
    +
     5752.    356
    +
     5753.    356// If we are not in a browser, then the file form of strict pragma may be used.
    +
     5754.    356
    +
     5755.    356                if (
    +
     5756.    356                    next_token.value === "use strict"
    +
     5757.    356                ) {
    +
     5758.    356                    advance("(string)");
    +
     5759.    356                    advance(";");
    +
     5760.    356                }
    +
     5761.    356            }
    +
     5762.    356            tree = statements();
    +
     5763.    356            advance("(end)");
    +
     5764.    356            functionage = global;
    +
     5765.    356            walk_statement(tree);
    +
     5766.    356            if (warnings.length === 0) {
    +
     5767.    356                uninitialized_and_unused();
    +
     5768.    356                if (!option.white) {
    +
     5769.    356                    whitage();
    +
     5770.    356                }
    +
     5771.    356            }
    +
     5772.    356        }
    +
     5773.    297        if (!option.browser) {
    +
     5774.    293            directives.forEach(function (comment) {
    +
     5775.    293                if (comment.directive === "global") {
    +
     5776.    293
    +
     5777.    293// cause: "/*global aa*/"
    +
     5778.    293
    +
     5779.    293                    warn("missing_browser", comment);
    +
     5780.    293                }
    +
     5781.    293            });
    +
     5782.    297        }
    +
     5783.    297        if (option.test_internal_error) {
    +
     5784.      1            assert_or_throw(undefined, "test_internal_error");
    +
     5785.    296        }
    +
     5786.    296        early_stop = false;
    +
     5787.    296    } catch (e) {
    +
     5788.    109        e.early_stop = true;
    +
     5789.    109        e.message = "[JSLint was unable to finish]\n" + e.message;
    +
     5790.    109        if (e.name !== "JSLintError") {
    +
     5791.    109            e.column = 0;
    +
     5792.    109            e.line = 0;
    +
     5793.    109            e.stack_trace = e.stack;
    +
     5794.    109            warnings.push(e);
    +
     5795.    109        }
    +
     5796.    109    }
    +
     5797.    405
    +
     5798.    405// sort warnings by early_stop first, line, column respectively
    +
     5799.    405
    +
     5800.    880    warnings.sort(function (a, b) {
    +
     5801.    880        return (
    +
     5802.    880            Boolean(b.early_stop) - Boolean(a.early_stop)
    +
     5803.    808            || a.line - b.line || a.column - b.column
    +
     5804.    880        );
    +
     5805.    880
    +
     5806.    880// update each warning with a formatted_message ready for use by cli
    +
     5807.    880
    +
     5808.    880    }).map(function ({
    +
     5809.    880        column = 0,
    +
     5810.    880        line = 0,
    +
     5811.    880        message = "",
    +
     5812.    880        stack_trace = ""
    +
     5813.    880    }, ii, list) {
    +
     5814.    880        column += 1;
    +
     5815.    880        line += 1;
    +
     5816.    880        list[ii].formatted_message = String(
    +
     5817.    880            String(ii + 1).padStart(3, " ")
    +
     5818.    880            + " \u001b[31m" + message + "\u001b[39m"
    +
     5819.    880            + " \u001b[90m\/\/ line " + line + ", column " + column
    +
     5820.    880            + "\u001b[39m\n"
    +
     5821.    880            + ("    " + String(lines && lines[line - 1]).trim()).slice(0, 72)
    +
     5822.    880            + "\n" + stack_trace
    +
     5823.    880        ).trim();
    +
     5824.    880    });
    +
     5825.    405    return {
    +
     5826.    405        directives,
    +
     5827.    405        edition,
    +
     5828.    405        exports,
    +
     5829.    405        froms,
    +
     5830.    405        functions,
    +
     5831.    405        global,
    +
     5832.    405        id: "(JSLint)",
    +
     5833.    405        json: json_mode,
    +
     5834.    405        lines,
    +
     5835.    405        module: module_mode === true,
    +
     5836.     56        ok: warnings.length === 0 && !early_stop,
    +
     5837.    405        option,
    +
     5838.    405        property,
    +
     5839.    405        shebang: (
    +
     5840.    405            shebang
    +
     5841.      3            ? lines[0]
    +
     5842.    402            : undefined
    +
     5843.    405        ),
    +
     5844.    405        stop: early_stop,
    +
     5845.    405        tokens,
    +
     5846.    405        tree,
    +
     5847.    405        warnings
    +
     5848.    405    };
    +
     5849.    405}
    +
     5850.      1
    +
     5851.      4async function cli({
    +
     5852.      4    console_error,
    +
     5853.      4    file,
    +
     5854.      4    option,
    +
     5855.      4    source
    +
     5856.      4}) {
    +
     5857.      4/*
    +
     5858.      4 * this function will run jslint from nodejs-cli
    +
     5859.      4 */
    +
     5860.      4    const fs = await import("fs");
    +
     5861.      4    let exitCode;
    +
     5862.      8    function string_line_count(code) {
    +
     5863.      8    /*
    +
     5864.      8     * this function will count number of newlines in <code>
    +
     5865.      8     */
    +
     5866.      8        let cnt;
    +
     5867.      8        let ii;
    +
     5868.      8        // https://jsperf.com/regexp-counting-2/8
    +
     5869.      8        cnt = 0;
    +
     5870.      8        ii = 0;
    +
     5871.   2218        while (true) {
    +
     5872.   2218            ii = code.indexOf("\n", ii) + 1;
    +
     5873.   2218            if (ii === 0) {
    +
     5874.   2218                break;
    +
     5875.   2218            }
    +
     5876.   2218            cnt += 1;
    +
     5877.   2218        }
    +
     5878.      8        return cnt;
    +
     5879.      8    }
    +
     5880.     22    function jslint_from_file({
    +
     5881.     22        code,
    +
     5882.     22        file,
    +
     5883.     22        line_offset = 0,
    +
     5884.     22        option = {},
    +
     5885.     22        warnings = []
    +
     5886.     22    }) {
    +
     5887.     22        switch ((
    +
     5888.     22            /\.\w+?$|$/m
    +
     5889.     22        ).exec(file)[0]) {
    +
     5890.      5        case ".html":
    +
     5891.      5            // recurse
    +
     5892.      5            code.replace((
    +
     5893.      5                /^<script>\n([\S\s]*?\n)<\/script>$/gm
    +
     5894.      5            ), function (ignore, match1, ii) {
    +
     5895.      5                jslint_from_file({
    +
     5896.      5                    code: match1,
    +
     5897.      5                    file: file + ".<script>.js",
    +
     5898.      5                    line_offset: string_line_count(code.slice(0, ii)) + 1,
    +
     5899.      5                    option: Object.assign({
    +
     5900.      5                        browser: true
    +
     5901.      5                    }, option)
    +
     5902.      5                });
    +
     5903.      5                return "";
    +
     5904.      5            });
    +
     5905.      5            return;
    +
     5906.      2        case ".md":
    +
     5907.      2            // recurse
    +
     5908.      2            code.replace((
    +
     5909.      2                /^```javascript\n([\S\s]*?\n)```$/gm
    +
     5910.      2            ), function (ignore, match1, ii) {
    +
     5911.      2                jslint_from_file({
    +
     5912.      2                    code: match1,
    +
     5913.      2                    file: file + ".<```javascript>.js",
    +
     5914.      2                    line_offset: string_line_count(code.slice(0, ii)) + 1,
    +
     5915.      2                    option
    +
     5916.      2                });
    +
     5917.      2                return "";
    +
     5918.      2            });
    +
     5919.      2            return;
    +
     5920.      1        case ".sh":
    +
     5921.      1            // recurse
    +
     5922.      1            code.replace((
    +
     5923.      1                /\bnode\u0020.*?-e\u0020'\n([\S\s]*?\n)'/gm
    +
     5924.      6            ), function (ignore, match1, ii) {
    +
     5925.      6                jslint_from_file({
    +
     5926.      6                    code: match1,
    +
     5927.      6                    file: file + ".<node -e>.js",
    +
     5928.      6                    line_offset: string_line_count(code.slice(0, ii)) + 1,
    +
     5929.      6                    option: Object.assign({
    +
     5930.      6                        node: true
    +
     5931.      6                    }, option)
    +
     5932.      6                });
    +
     5933.      6                return "";
    +
     5934.      6            });
    +
     5935.      1            return;
    +
     5936.     14        default:
    +
     5937.     14            warnings = jslint(
    +
     5938.     14                "\n".repeat(line_offset) + code,
    +
     5939.     14                option
    +
     5940.     14            ).warnings;
    +
     5941.     14        }
    +
     5942.     14        // print only first 10 warnings
    +
     5943.     14        if (warnings.length > 0) {
    +
     5944.      1            exitCode = 1;
    +
     5945.      1            // print first 10 warnings to stderr
    +
     5946.      1            console_error(
    +
     5947.      1                "\u001b[1mjslint " + file + "\u001b[22m\n"
    +
     5948.      4                + warnings.slice(0, 10).map(function ({
    +
     5949.      4                    formatted_message
    +
     5950.      4                }) {
    +
     5951.      4                    return formatted_message;
    +
     5952.      4                }).join("\n")
    +
     5953.      1            );
    +
     5954.      1        }
    +
     5955.     22    }
    +
     5956.      3    console_error = console_error || console.error;
    +
     5957.      2    if (source) {
    +
     5958.      2        jslint_from_file({
    +
     5959.      2            code: source,
    +
     5960.      2            file,
    +
     5961.      2            option
    +
     5962.      2        });
    +
     5963.      2        return;
    +
     5964.      2    }
    +
     5965.      2    if (file === ".") {
    +
     5966.      1        file = await fs.promises.readdir(".");
    +
     5967.     28        await Promise.all(file.map(async function (file) {
    +
     5968.     28            let code;
    +
     5969.     28            let timeStart = Date.now();
    +
     5970.     28            switch ((
    +
     5971.     28                /\.\w+?$|$/m
    +
     5972.     28            ).exec(file)[0]) {
    +
     5973.      4            case ".html":
    +
     5974.      9            case ".js":
    +
     5975.     10            case ".json":
    +
     5976.     12            case ".md":
    +
     5977.     12            case ".mjs":
    +
     5978.     13            case ".sh":
    +
     5979.     13                break;
    +
     5980.     15            default:
    +
     5981.     15                return;
    +
     5982.     13            }
    +
     5983.     13            try {
    +
     5984.     13                code = await fs.promises.readFile(file, "utf8");
    +
     5985.     12            } catch (ignore) {
    +
     5986.      1                return;
    +
     5987.     12            }
    +
     5988.     12            if (!(
    +
     5989.     12                !(
    +
     5990.     12                    /\b(?:lock|min|raw|rollup)\b/
    +
     5991.     12                ).test(file) && code && code.length < 1048576
    +
     5992.      1            )) {
    +
     5993.      1                return;
    +
     5994.     11            }
    +
     5995.     11            jslint_from_file({
    +
     5996.     11                code,
    +
     5997.     11                file,
    +
     5998.     11                option
    +
     5999.     11            });
    +
     6000.     11            console_error(
    +
     6001.     11                "jslint - " + (Date.now() - timeStart) + "ms - " + file
    +
     6002.     11            );
    +
     6003.     11        }));
    +
     6004.      1    } else {
    +
     6005.      1        jslint_from_file({
    +
     6006.      1            code: await fs.promises.readFile(file, "utf8"),
    +
     6007.      1            file,
    +
     6008.      1            option
    +
     6009.      1        });
    +
     6010.      2    }
    +
     6011.      2    return exitCode;
    +
     6012.      2}
    +
     6013.    394export default Object.freeze(function (
    +
     6014.    394    source = "",
    +
     6015.    394    option_object = empty(),
    +
     6016.    394    global_array = []
    +
     6017.    394) {
    +
     6018.      3    if (option_object.cli_mode) {
    +
     6019.      3        return cli(Object.assign({
    +
     6020.      3            source
    +
     6021.      3        }, option_object));
    +
     6022.    391    }
    +
     6023.    391    return jslint(source, option_object, global_array);
    +
     6024.    391});
    +
     6025.      1// feature-detect nodejs-cli
    +
     6026.      1if (
    +
     6027.      1    typeof process === "object"
    +
     6028.      1    // uncomment when nodejs v12 is no longer used in ci
    +
     6029.      1    // && typeof process?.versions?.node === "string"
    +
     6030.      1    && process && process.versions
    +
     6031.      1    && typeof process.versions.node === "string"
    +
     6032.      1    && (
    +
     6033.      1        (/\bjslint.m?js$/m).test(process.argv[1])
    +
     6034.      1        || process.env.JSLINT_CLI === "1"
    +
     6035.      1    )
    +
     6036.      1) {
    +
     6037.      1    // run cli
    +
     6038.      1    cli({
    +
     6039.      1        file: process.argv[2]
    +
     6040.      1    }).then(function (exitCode) {
    +
     6041.      1        process.exit(exitCode);
    +
     6042.      1    });
    +
     6043.      1}
    +
     6044.      1
    + +
    +
    +
    + + diff --git a/branch.alpha/.build/coverage/test.js.html b/branch.alpha/.build/coverage/test.js.html new file mode 100644 index 000000000..423cc3cf2 --- /dev/null +++ b/branch.alpha/.build/coverage/test.js.html @@ -0,0 +1,513 @@ + + + +coverage-report + + + +
    +
    coverage-report
    + + + + + + + + + + + +
    files coveredlines
    + ./ test.js
    +
    +
    +
    +
    + 100.00 %
    + 378 / 378 +
    +
    +
    +
        1.      1/*jslint node*/
    +
        2.      1import fs from "fs";
    +
        3.      1import jslint from "./jslint.js";
    +
        4.      1
    +
        5.    846function assertOrThrow(passed, msg) {
    +
        6.    846/*
    +
        7.    846 * this function will throw <msg> if <passed> is falsy
    +
        8.    846 */
    +
        9.      1    if (!passed) {
    +
       10.      1        throw new Error(msg);
    +
       11.      1    }
    +
       12.    846}
    +
       13.      1
    +
       14.      1function noop() {
    +
       15.      1/*
    +
       16.      1 * this function will do nothing
    +
       17.      1 */
    +
       18.      1    return;
    +
       19.      1}
    +
       20.      1
    +
       21.      1(function testCaseJslintCli() {
    +
       22.      1/*
    +
       23.      1 * this function will test jslint's cli handling-behavior
    +
       24.      1 */
    +
       25.      1    process.exit = function (exitCode) {
    +
       26.      1        assertOrThrow(!exitCode, exitCode);
    +
       27.      1    };
    +
       28.      1    jslint("", {
    +
       29.      1        cli_mode: true,
    +
       30.      1        file: "jslint.js"
    +
       31.      1    });
    +
       32.      1    jslint("", {
    +
       33.      1        cli_mode: true,
    +
       34.      1        // suppress error
    +
       35.      1        console_error: noop,
    +
       36.      1        file: "syntax_error.js",
    +
       37.      1        option: {
    +
       38.      1            debug: true
    +
       39.      1        },
    +
       40.      1        source: "syntax error"
    +
       41.      1    });
    +
       42.      1    jslint("", {
    +
       43.      1        cli_mode: true,
    +
       44.      1        file: "aa.html",
    +
       45.      1        source: "<script>\nlet aa = 0;\n</script>\n"
    +
       46.      1    });
    +
       47.      1}());
    +
       48.      1
    +
       49.      1(function testCaseJslintMisc() {
    +
       50.      1/*
    +
       51.      1 * this function will test jslint's misc handling-behavior
    +
       52.      1 */
    +
       53.      1    // test assertOrThrow's throw handling-behavior
    +
       54.      1    try {
    +
       55.      1        assertOrThrow(undefined, new Error());
    +
       56.      1    } catch (ignore) {}
    +
       57.      1}());
    +
       58.      1
    +
       59.      1(function testCaseJslintOption() {
    +
       60.      1/*
    +
       61.      1 * this function will test jslint's option handling-behavior
    +
       62.      1 */
    +
       63.      1    assertOrThrow(jslint([""], {
    +
       64.      1        bitwise: true,
    +
       65.      1        browser: true,
    +
       66.      1        convert: true,
    +
       67.      1        couch: true,
    +
       68.      1        debug: true,
    +
       69.      1        devel: true,
    +
       70.      1        eval: true,
    +
       71.      1        for: true,
    +
       72.      1        fudge: true,
    +
       73.      1        getset: true,
    +
       74.      1        long: true,
    +
       75.      1        node: true,
    +
       76.      1        single: true,
    +
       77.      1        this: true,
    +
       78.      1        white: true
    +
       79.      1    }).warnings.length === 0);
    +
       80.      1    assertOrThrow(jslint("", {
    +
       81.      1        test_internal_error: true
    +
       82.      1    }).warnings.length === 1);
    +
       83.      1}());
    +
       84.      1
    +
       85.      1(function testCaseJslintCodeValidate() {
    +
       86.      1/*
    +
       87.      1 * this function will validate each code is valid in jslint
    +
       88.      1 */
    +
       89.      1    Object.values({
    +
       90.      1        array: [
    +
       91.      1            "new Array(0);"
    +
       92.      1        ],
    +
       93.      1        async_await: [
    +
       94.      1            "async function aa() {\n    await aa();\n}"
    +
       95.      1        ],
    +
       96.      1        date: [
    +
       97.      1            "Date.getTime();",
    +
       98.      1            "let aa = aa().getTime();",
    +
       99.      1            "let aa = aa.aa().getTime();"
    +
      100.      1        ],
    +
      101.      1        directives: [
    +
      102.      1            "#!\n/*jslint browser:false, node*/\n\"use strict\";",
    +
      103.      1            "/*jslint bitwise*/\nlet aa = aa | 0;",
    +
      104.      1            "/*jslint browser*/\n;",
    +
      105.      1            "/*jslint debug*/\n",
    +
      106.      1            "/*jslint devel*/\ndebugger;",
    +
      107.      1            "/*jslint eval*/\nnew Function();\neval();",
    +
      108.      1            "/*jslint getset*/\nlet aa = {get aa() {\n    return;\n}};",
    +
      109.      1            "/*jslint getset*/\nlet aa = {set aa(aa) {\n    return aa;\n}};",
    +
      110.      1            "/*jslint this*/\nlet aa = this;",
    +
      111.      1            "/*jslint unordered*/\nlet {bb, aa} = 0;",
    +
      112.      1            "/*jslint white*/\n\t",
    +
      113.      1            "/*property aa bb*/"
    +
      114.      1        ],
    +
      115.      1        fart: [
    +
      116.      1            "function aa() {\n    return () => 0;\n}"
    +
      117.      1        ],
    +
      118.      1        json: [
    +
      119.      1            "{\"aa\":[[],-0,null]}"
    +
      120.      1        ],
    +
      121.      1        label: [
    +
      122.      1            "function aa() {\nbb:\n    while (true) {\n        if (true) {\n"
    +
      123.      1            + "            break bb;\n        }\n    }\n}"
    +
      124.      1        ],
    +
      125.      1        loop: [
    +
      126.      1            "function aa() {\n    do {\n        aa();\n    } while (aa());\n}"
    +
      127.      1        ],
    +
      128.      1        module: [
    +
      129.      1            "export default Object.freeze();",
    +
      130.      1            "import {aa, bb} from \"aa\";\naa(bb);",
    +
      131.      1            "import {} from \"aa\";",
    +
      132.      1            "import(\"aa\").then(function () {\n    return;\n});"
    +
      133.      1        ],
    +
      134.      1        number: [
    +
      135.      1            "let aa = 0.0e0;",
    +
      136.      1            "let aa = 0b0;",
    +
      137.      1            "let aa = 0o0;",
    +
      138.      1            "let aa = 0x0;"
    +
      139.      1        ],
    +
      140.      1        optional_chaining: [
    +
      141.      1            "let aa = aa?.bb?.cc;"
    +
      142.      1        ],
    +
      143.      1        property: [
    +
      144.      1            "let aa = aa[`!`];"
    +
      145.      1        ],
    +
      146.      1        regexp: [
    +
      147.      1            "function aa() {\n    return /./;\n}",
    +
      148.      1            "let aa = /(?!.)(?:.)(?=.)/;"
    +
      149.      1        ],
    +
      150.      1        ternary: [
    +
      151.      1            "let aa = (\n    aa()\n    ? 0\n    : 1\n) "
    +
      152.      1            + "&& (\n    aa()\n    ? 0\n    : 1\n);"
    +
      153.      1        ],
    +
      154.      1        var: [
    +
      155.      1            "let [...aa] = [...aa];",
    +
      156.      1            "let [\n    aa, bb = 0\n] = 0;",
    +
      157.      1            "let {aa, bb} = 0;",
    +
      158.      1            "let {\n    aa: bb\n} = 0;"
    +
      159.      1        ]
    +
      160.     15    }).forEach(function (codeList) {
    +
      161.     38        codeList.forEach(function (code) {
    +
      162.     38            let warnings;
    +
      163.     38            warnings = jslint(code).warnings;
    +
      164.     38            assertOrThrow(
    +
      165.     38                warnings.length === 0,
    +
      166.     38                JSON.stringify([code, warnings])
    +
      167.     38            );
    +
      168.     38        });
    +
      169.     15    });
    +
      170.      1}());
    +
      171.      1
    +
      172.      1(async function testCaseJslintWarningsValidate() {
    +
      173.      1/*
    +
      174.      1 * this function will validate each jslint <warning> is raised with given
    +
      175.      1 * malformed <code>
    +
      176.      1 */
    +
      177.      1    Object.entries({
    +
      178.      1        expected_a_b: [
    +
      179.      1            "([])=>0",
    +
      180.      1            "(aa)=>{}",
    +
      181.      1            "(aa?0:aa)",
    +
      182.      1            "(aa?aa:0)",
    +
      183.      1            "(aa?false:true)",
    +
      184.      1            "(aa?true:false)",
    +
      185.      1            ";{",
    +
      186.      1            "`${/ /}`",
    +
      187.      1            "`${`",
    +
      188.      1            "`${{`",
    +
      189.      1            "aa.aa=undefined",
    +
      190.      1            "aa=+aa",
    +
      191.      1            "aa=/[ ]/",
    +
      192.      1            "aa=/aa{/",
    +
      193.      1            "aa=0+\"\"",
    +
      194.      1            "aa=\"\"+\"\"",
    +
      195.      1            "async",
    +
      196.      1            "delete [0]",
    +
      197.      1            "for(;;){}",
    +
      198.      1            "isFinite(0)",
    +
      199.      1            "let aa;var aa;"
    +
      200.      1        ],
    +
      201.      1        expected_a_before_b: [
    +
      202.      1            "aa=/(:)/",
    +
      203.      1            "aa=/=/",
    +
      204.      1            "aa=/?/",
    +
      205.      1            "aa=/[/"
    +
      206.      1        ],
    +
      207.      1        expected_identifier_a: [
    +
      208.      1            "function aa(0){}",
    +
      209.      1            "function aa([aa]){}\nfunction aa([aa],[aa,aa=aa],[0]){}",
    +
      210.      1            "function aa({aa}){}\nfunction aa({aa},{aa:aa,aa=aa},{aa:0}){}",
    +
      211.      1            "function(){}"
    +
      212.      1        ],
    +
      213.      1        expected_space_a_b: [
    +
      214.      1            "(function(){return;}());"
    +
      215.      1        ],
    +
      216.      1        required_a_optional_b: [
    +
      217.      1            "function aa(aa=0,...){}",
    +
      218.      1            "function aa(aa=0,[]){}",
    +
      219.      1            "function aa(aa=0,{}){}",
    +
      220.      1            "function aa(aa=0,bb){}"
    +
      221.      1        ],
    +
      222.      1        too_long: [
    +
      223.      1            "//".repeat(100)
    +
      224.      1        ],
    +
      225.      1        unexpected_a: [
    +
      226.      1            "((0))",
    +
      227.      1            "(+0?+0:+0)()",
    +
      228.      1            "(/./)?.foo",
    +
      229.      1            "/*/",
    +
      230.      1            "/./",
    +
      231.      1            "0===(0==0)",
    +
      232.      1            "0[0][0]",
    +
      233.      1            "0|0",
    +
      234.      1            ";",
    +
      235.      1            "[-0x0]",
    +
      236.      1            "[0x0]",
    +
      237.      1            "\"aa\"?.bb",
    +
      238.      1            "`${/[`]/}`",
    +
      239.      1            "`${/`/}`",
    +
      240.      1            "`${\"`\"}`",
    +
      241.      1            "aa((0))",
    +
      242.      1            "aa+=NaN",
    +
      243.      1            "aa/=0",
    +
      244.      1            "aa=/[0-]/",
    +
      245.      1            "aa=/.//",
    +
      246.      1            "aa=/./z",
    +
      247.      1            "aa={aa:aa}",
    +
      248.      1            "aa={set aa(){}}",
    +
      249.      1            "arguments",
    +
      250.      1            "await",
    +
      251.      1            "debugger",
    +
      252.      1            "eval",
    +
      253.      1            "for(aa in aa){}",
    +
      254.      1            "for(const ii=0;;){}",
    +
      255.      1            "for(ii=0;ii<0;ii++){}",
    +
      256.      1            "for(ii=0;ii<0;ii+=0){}",
    +
      257.      1            "function aa(){for(0;0;0){break;}}",
    +
      258.      1            "function aa(){try{return;}catch(ignore){}finally{return;}}",
    +
      259.      1            "function aa(){try{}catch(ignore){}finally{switch(0){case 0:}}}",
    +
      260.      1            "function aa(){while(0){continue;}}",
    +
      261.      1            "function aa(){while(0){try{0;}catch(ignore){}finally{continue;}}}",
    +
      262.      1            "function aa(){}0",
    +
      263.      1            "function aa(){}\n[]",
    +
      264.      1            "function ignore(){let ignore;}",
    +
      265.      1            "ignore",
    +
      266.      1            "ignore:",
    +
      267.      1            "import ignore from \"aa\"",
    +
      268.      1            "import {ignore} from \"aa\"",
    +
      269.      1            "let aa=[]?.bb",
    +
      270.      1            "new Date.UTC()",
    +
      271.      1            "new Function()",
    +
      272.      1            "new Symbol()",
    +
      273.      1            "switch(0){case 0:break;case 0:break}",
    +
      274.      1            "switch(0){case 0:break;default:break;}",
    +
      275.      1            "switch(0){case 0:break;default:}",
    +
      276.      1            "this",
    +
      277.      1            "try{throw 0;try{}catch(){}}catch(){}",
    +
      278.      1            "try{}finally{break;}",
    +
      279.      1            "void 0",
    +
      280.      1            "while((0)){}",
    +
      281.      1            "while(0){}",
    +
      282.      1            "{//\n}",
    +
      283.      1            "{0:0}",
    +
      284.      1            "{\"\\u{1234}\":0}",
    +
      285.      1            "{\"aa\":",
    +
      286.      1            "{\"aa\":'aa'}"
    +
      287.      1        ]
    +
      288.      7    }).forEach(function ([
    +
      289.      7        expectedWarning, malformedCodeList
    +
      290.      7    ]) {
    +
      291.     96        malformedCodeList.forEach(function (malformedCode) {
    +
      292.     96            assertOrThrow(
    +
      293.    151                jslint(malformedCode).warnings.some(function ({
    +
      294.    151                    code
    +
      295.    151                }) {
    +
      296.    151                    return code === expectedWarning;
    +
      297.    151                }),
    +
      298.     96                new Error(
    +
      299.     96                    `jslint failed to warn "${expectedWarning}" with `
    +
      300.     96                    + `malfomed code "${malformedCode}"`
    +
      301.     96                )
    +
      302.     96            );
    +
      303.     96        });
    +
      304.      7    });
    +
      305.      1    Array.from(String(
    +
      306.      1        await fs.promises.readFile("jslint.js", "utf8")
    +
      307.      1    ).matchAll(new RegExp((
    +
      308.      1        "\\s*?"
    +
      309.      1        + "(\\/\\/\\s*?cause:.*?\\n(?:\\/\\/.*?\\n)*?)"
    +
      310.      1        + "(\\s*?^[^\\/].*?(?:\\n\\s*?\".*?)?$)"
    +
      311.    198    ), "gm"))).forEach(function ([
    +
      312.    198        match0, causeList, warning
    +
      313.    198    ]) {
    +
      314.    198        let expectedWarningCode;
    +
      315.    198        let fnc;
    +
      316.    198        // debug match0
    +
      317.    198        console.error(match0.trim().replace((/\n\n/g), "\n"));
    +
      318.    198        assertOrThrow(
    +
      319.    198            match0.indexOf("\n\n" + causeList + "\n    ") === 0,
    +
      320.    198            JSON.stringify([
    +
      321.    198                match0, causeList
    +
      322.    198            ], undefined, 4)
    +
      323.    198        );
    +
      324.    198        warning = warning.match(
    +
      325.    198            "("
    +
      326.    198            + "at_margin"
    +
      327.    198            + "|expected_at"
    +
      328.    198            + "|no_space_only"
    +
      329.    198            + "|one_space_only"
    +
      330.    198            + "|one_space"
    +
      331.    198            + "|stop"
    +
      332.    198            + "|stop_at"
    +
      333.    198            + "|warn"
    +
      334.    198            + "|warn_at"
    +
      335.    198            + ")"
    +
      336.    198            + "\\\u0028\\s*?\"?"
    +
      337.    198            + "(\\S[^\n\"]+)"
    +
      338.    198        );
    +
      339.    158        if (warning) {
    +
      340.    158            expectedWarningCode = warning[2];
    +
      341.    158            fnc = warning[1];
    +
      342.    158            switch (fnc) {
    +
      343.    158            case "at_margin":
    +
      344.    158            case "expected_at":
    +
      345.    158                expectedWarningCode = "expected_a_at_b_c";
    +
      346.    158                break;
    +
      347.    158            case "no_space_only":
    +
      348.    158                expectedWarningCode = "unexpected_space_a_b";
    +
      349.    158                break;
    +
      350.    158            case "one_space":
    +
      351.    158            case "one_space_only":
    +
      352.    158                expectedWarningCode = "expected_space_a_b";
    +
      353.    158                break;
    +
      354.    158            }
    +
      355.    158        }
    +
      356.    198        causeList.split(
    +
      357.    198            /\/\/\u0020cause:[\n|\u0020]/
    +
      358.    255        ).slice(1).forEach(function (cause) {
    +
      359.    255            assertOrThrow(cause === cause.trim() + "\n", JSON.stringify(cause));
    +
      360.    255            cause = (
    +
      361.    255                cause[0] === "\""
    +
      362.    247                ? JSON.parse(cause)
    +
      363.      8                : cause.replace((
    +
      364.      8                    /^\/\/\u0020/gm
    +
      365.      8                ), "")
    +
      366.    255            );
    +
      367.    255            assertOrThrow(
    +
      368.    463                jslint(cause).warnings.some(function ({
    +
      369.    463                    code
    +
      370.    463                }) {
    +
      371.    463                    return code === expectedWarningCode;
    +
      372.    463                }) || !expectedWarningCode,
    +
      373.    255                "\n" + cause.trim()
    +
      374.    255            );
    +
      375.    255        });
    +
      376.    198    });
    +
      377.      1}());
    +
      378.      1
    + +
    +
    +
    + + diff --git a/branch.alpha/.build/screenshot.browser._2fjslint_2fbranch.beta_2findex.html.html b/branch.alpha/.build/screenshot.browser._2fjslint_2fbranch.beta_2findex.html.html new file mode 100644 index 000000000..3af9f72aa --- /dev/null +++ b/branch.alpha/.build/screenshot.browser._2fjslint_2fbranch.beta_2findex.html.html @@ -0,0 +1,446 @@ + + + + + + + +JSLint: The JavaScript Code Quality Tool + + + + + +
    +
    Source
    +
    + + +
    + +
    + Function Report +
    module
    Promise, console, https, jslint
    import from
    ./jslint.js, https
    4
    «async»()
    variable
    result
    closure
    result
    module
    Promise, jslint
    6
    «Promise»(resolve)
    parameter
    resolve
    closure
    resolve
    module
    https
    7
    «request»(res)
    parameter
    res
    outer
    result
    9
    «data»(chunk)
    parameter
    chunk
    outer
    result
    11
    «end»()
    outer
    resolve, result
    17
    «forEach»({formatted_message})
    parameter
    formatted_message
    module
    console
    JSLint edition v2021.5.27
    +
    +
    + Property Directive + +
    +
    + + + + +
    +
    Options +
    Assume... +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Global variables... + +
    +
    +
    + + + diff --git a/branch.alpha/.build/screenshot.browser._2fjslint_2fbranch.beta_2findex.html.png b/branch.alpha/.build/screenshot.browser._2fjslint_2fbranch.beta_2findex.html.png new file mode 100644 index 000000000..ac4e732a2 Binary files /dev/null and b/branch.alpha/.build/screenshot.browser._2fjslint_2fbranch.beta_2findex.html.png differ diff --git a/branch.alpha/.gitconfig b/branch.alpha/.gitconfig new file mode 100644 index 000000000..a7cd56f37 --- /dev/null +++ b/branch.alpha/.gitconfig @@ -0,0 +1,25 @@ +[branch "alpha"] + merge = refs/heads/alpha + remote = origin +[branch "base"] + merge = refs/heads/base + remote = origin +[core] + # autocrlf = false + autocrlf = input + bare = false + # filemode = false + logallrefupdates = true + repositoryformatversion = 0 +[diff] + algorithm = histogram +[pull] + ff = only +[receive] + denyCurrentBranch = warn +[remote "origin"] + fetch = +refs/heads/*:refs/remotes/origin/* + url = https://github.com/user/jslint +[remote "upstream"] + fetch = +refs/heads/*:refs/remotes/upstream/* + url = https://github.com/jslint-org/jslint diff --git a/branch.alpha/.github/workflows/ci.yml b/branch.alpha/.github/workflows/ci.yml new file mode 100644 index 000000000..5d4c0ca5e --- /dev/null +++ b/branch.alpha/.github/workflows/ci.yml @@ -0,0 +1,51 @@ +# this workflow will run nodejs coverages and tests and +# upload build-artifacts to branch-gh-pages +name: CI +on: + push: + branches: + - alpha + - beta + - master + - sandbox +# shCiBase - start +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + architecture: + # - arm64 + - x64 + # - x86 + node_version: + - 12 + - 14 + - 16 + os: + - macos-latest + - ubuntu-latest + - windows-latest + name: node . v${{ matrix.node_version }} . ${{ matrix.architecture }} . ${{ matrix.os }} + steps: + # disable autocrlf in windows + - run: git config --global core.autocrlf false + # https://github.com/actions/checkout + - uses: actions/checkout@v2 + # https://github.com/actions/setup-node + - uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node_version }} + architecture: ${{ matrix.architecture }} + # fetch ci.sh from trusted source + - run: git fetch origin alpha && git checkout origin/alpha ci.sh + # run nodejs coverages and tests + - run: sh ci.sh shCiBase +# shCiBase - end + # fetch ci.sh from trusted source + - run: git fetch origin alpha && git checkout origin/alpha ci.sh + # upload build-artifacts to branch-gh-pages + - run: sh ci.sh shCiArtifactUpload + env: + CI_NODE_VERSION_ARCH_PLATFORM: v14.x64.linux + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/branch.alpha/.github/workflows/on_pull_request.yml b/branch.alpha/.github/workflows/on_pull_request.yml new file mode 100644 index 000000000..9274873fc --- /dev/null +++ b/branch.alpha/.github/workflows/on_pull_request.yml @@ -0,0 +1,36 @@ +# this workflow will run nodejs coverages and tests +name: on_pull_request +on: + - pull_request +# shCiBase - start +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + architecture: + # - arm64 + - x64 + # - x86 + node_version: + - 12 + - 14 + - 16 + os: + - macos-latest + - ubuntu-latest + - windows-latest + name: node . v${{ matrix.node_version }} . ${{ matrix.architecture }} . ${{ matrix.os }} + steps: + # https://github.com/actions/checkout + - uses: actions/checkout@v2 + # https://github.com/actions/setup-node + - uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node_version }} + architecture: ${{ matrix.architecture }} + # fetch ci.sh from trusted source + - run: git fetch origin alpha && git checkout origin/alpha ci.sh + # run nodejs coverages and tests + - run: sh ci.sh shCiBase +# shCiBase - end diff --git a/branch.alpha/.gitignore b/branch.alpha/.gitignore new file mode 100644 index 000000000..da69da6f4 --- /dev/null +++ b/branch.alpha/.gitignore @@ -0,0 +1,18 @@ +"* +'* +*.[0123456789][0123456789] +*.lock +*.log +*.pyc +*~ +.* +_* +node_modules +package-lock.json +temp* +tmp +undefined + +!.gitconfig +!.github +!.gitignore diff --git a/branch.alpha/CHANGELOG.md b/branch.alpha/CHANGELOG.md new file mode 100644 index 000000000..6c5ebeb16 --- /dev/null +++ b/branch.alpha/CHANGELOG.md @@ -0,0 +1,85 @@ +# Changelog + +## Todo +- app - deploy jslint as chrome-extension. +- ci - auto-update version numbers in README.md and jslint.js FROM CHANGELOG.md +- ci - continue addng regression tests and improve code-coverage. +- doc - add svg changelog. +- doc - add svg package-listing. +- jslint - add html and css linting back into jslint. +- jslint - cleanup regexp code using switch-statements. +- node - after node-v12 is deprecated, change require("fs").promises to require("fs/promises"). +- node - after node-v14 is deprecated, remove shell-code export "NODE_OPTIONS=--unhandled-rejections=strict". +- none + +## v2021.5.28-beta +- deadcode - replace with assertion-check in function do_function() - "if (mega_mode) { warn... }". +- jslint - inline function `activate` into function `action_var`. +- jslint - inline-document each deadcode-removal/assertion-check. +- jslint - inline-document each warning with cause that can reproduce it - part 2. + +## v2021.5.27 +- ci - fix expectedWarningCode not being validated. +- ci - in windows, disable git-autocrlf. +- deadcode - replace with assertion-check in function are_similar() - "if (a === b) { return true }". +- deadcode - replace with assertion-check in function are_similar() superseded by id-check - "if (Array.isArray(b)) { return false; }". +- deadcode - replace with assertion-check in function are_similar() superseded by is_weird() check - "if (a.arity === "function" && a.arity ===...c". +- jslint - add directive `test_internal_error`. +- jslint - add directive `unordered` to tolerate unordered properties and params. +- jslint - inline-document each warning with cause that can reproduce it - part 1. +- style - refactor code moving infix-operators from post-position to pre-position in multiline statements. +- website - add hotkey ctrl-enter to run jslint. + +## v2021.5.26 +- ci - fix ci silently failing in node-v12 and node-v14. +- cli - add env var JSLINT_CLI to force-trigger cli in jslint.js (used for code-coverage of cli). +- jslint - add "globalThis" to default globals. +- jslint - add new rules unordered_param_a, unordered_property_a, that warn if parameters and properties are listed in nonascii-order. +- jslint - fix bug where (global) functionage missing properties finally and try. +- jslint - fix bug failing to parse unicode "\\u{12345}". +- jslint - fix bug falsely warning against conditional-chaining-operator "?.". +- jslint - remove deadcode for preaction-binary-".". +- jslint - remove deadcode warning bad_option_a. +- website - add fork-me ribbon. +- website - load index.html with example code. +- website - merge file report.js into browser.js. + +## v2021.5.23 +- doc - add section Changelog. +- doc - update README.md with installation instructions. +- cli - merge shell-function shJslintCli into jslint.js. +- jslint - update default globals with support for "import". +- jslint - sort warnings with higher priority for early_stop. +- jslint - add async/await support. +- ci - make branch-beta the default branch. +- ci - validate non-http/file links in *.md files. +- ci - add shell-functions shCiBranchPromote. + +## v2021.5.21 +- this ci-release does not change any core-functionality of file jslint.js. +- doc - add file CHANGELOG.md. +- ci - begin addng regression tests and improve code-coverage. +- ci - allow pull-requests to run restricted-ci (cannot upload artifacts). +- gh-pages - fix missing assets and insecure http-links. +- gh-pages - merge file jslint.css into index.html. +- gh-pages - add files image-jslint-xxx.png. +- gh-pages - cleanup asset naming-convention. +- fix missing fonts in function.html and help.html. +- add files .gitconfig, Daley-Bold.woff2, Programma-Bold.woff2, icon-folder-open-solid.svg, icon-window-maximize-regular.svg. +- ci - fix http-links after moving to jslint-org. +- doc - migrate file README to README.md with embedded ci links and screenshots. +- ci - add macos and windows to ci-matrix. +- ci - ci now fails if jslint-check fails for any of the files in branches. +- ci - add github-workflows to generate code-coverage for jslint.js. + +## v2020.11.6 +- last jslint version before jslint-org migration. + +## v2018.4.25 +- last jslint version written in commonjs. + +## v2017.11.6 +- last jslint version written in es5. + +## v2013.3.13 +- last jslint version that can lint .html and .css files. diff --git a/branch.alpha/README.md b/branch.alpha/README.md new file mode 100644 index 000000000..673adb95f --- /dev/null +++ b/branch.alpha/README.md @@ -0,0 +1,84 @@ +# JSLint, The JavaScript Code Quality Tool + +Douglas Crockford +douglas@crockford.com + +## Status +| Branch | [master
    (v2021.5.27)](https://github.com/kaizhu256/jslint/tree/master) | [beta
    (testing)](https://github.com/kaizhu256/jslint/tree/beta) | [alpha
    (development)](https://github.com/kaizhu256/jslint/tree/alpha) | +|--:|:--:|:--:|:--:| +| CI | [![ci](https://github.com/kaizhu256/jslint/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/kaizhu256/jslint/actions?query=branch%3Amaster) | [![ci](https://github.com/kaizhu256/jslint/actions/workflows/ci.yml/badge.svg?branch=beta)](https://github.com/kaizhu256/jslint/actions?query=branch%3Abeta) | [![ci](https://github.com/kaizhu256/jslint/actions/workflows/ci.yml/badge.svg?branch=alpha)](https://github.com/kaizhu256/jslint/actions?query=branch%3Aalpha) | +| Coverage | [![coverage](https://kaizhu256.github.io/jslint/branch.master/.build/coverage/coverage-badge.svg)](https://kaizhu256.github.io/jslint/branch.master/.build/coverage/index.html) | [![coverage](https://kaizhu256.github.io/jslint/branch.beta/.build/coverage/coverage-badge.svg)](https://kaizhu256.github.io/jslint/branch.beta/.build/coverage/index.html) | [![coverage](https://kaizhu256.github.io/jslint/branch.alpha/.build/coverage/coverage-badge.svg)](https://kaizhu256.github.io/jslint/branch.alpha/.build/coverage/index.html) | +| Demo | [](https://kaizhu256.github.io/jslint/branch.master/index.html) | [](https://kaizhu256.github.io/jslint/branch.beta/index.html) | [](https://kaizhu256.github.io/jslint/branch.alpha/index.html) | +| Artifacts | [](https://github.com/kaizhu256/jslint/tree/gh-pages/branch.master/.build) | [](https://github.com/kaizhu256/jslint/tree/gh-pages/branch.beta/.build) | [](https://github.com/kaizhu256/jslint/tree/gh-pages/branch.alpha/.build) | + +## Live Web Demo +- [https://kaizhu256.github.io/jslint/index.html](https://kaizhu256.github.io/jslint/index.html) + +[![screenshot](https://kaizhu256.github.io/jslint/branch.beta/.build/screenshot.browser._2fjslint_2fbranch.beta_2findex.html.png)](https://kaizhu256.github.io/jslint/index.html) + +## Installation +1. Download [https://www.jslint.com/jslint.js](https://www.jslint.com/jslint.js) and rename to `jslint.mjs` +```shell +#!/bin/sh +curl -L https://www.jslint.com/jslint.js > jslint.mjs +``` + +2. To run `jslint.mjs` from command-line: +```shell +#!/bin/sh +node jslint.mjs hello.js + +# stderr: +# jslint hello.js +# 1 Use double quotes, not single quotes. // line 1, column 14 +# console.log('hello world'); +``` + +3. To load `jslint.mjs` as es-module: +```javascript +/*jslint devel*/ +import jslint from "./jslint.mjs"; +let code = "console.log('hello world');\n"; +let result = jslint(code); +result.warnings.forEach(function ({ + formatted_message +}) { + console.error(formatted_message); +}); + +// stderr: +// 1 Undeclared 'console'. // line 1, column 1 +// console.log('hello world'); +// 2 Use double quotes, not single quotes. // line 1, column 14 +// console.log('hello world'); +``` + +## Description +- [jslint.js](jslint.js) contains the jslint function. It parses and analyzes a source file, returning an object with information about the file. It can also take an object that sets options. + +- [index.html](index.html) runs the jslint.js function in a web page. The page also depends on `browser.js`. + +- [browser.js](browser.js) runs the web user interface and generates the results reports in HTML. + +- [help.html](help.html) describes JSLint's usage. Please [read it](https://kaizhu256.github.io/jslint/help.html). + +- [function.html](function.html) describes the jslint function and the results it produces. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[https://www.crockford.com/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. + +## Changelog +- [CHANGELOG.md](CHANGELOG.md) diff --git a/branch.alpha/browser.js b/branch.alpha/browser.js new file mode 100644 index 000000000..14bceb0e8 --- /dev/null +++ b/branch.alpha/browser.js @@ -0,0 +1,406 @@ +// browser.js +// 2018-06-16 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +/*jslint + browser +*/ + +/*property + addEventListener, ctrlKey, key, + checked, closure, column, context, create, disable, display, edition, + exports, filter, focus, forEach, froms, fudge, functions, getElementById, + global, id, innerHTML, isArray, join, json, keys, length, level, line, + lines, map, message, module, name, names, onchange, onclick, onscroll, + option, parameters, parent, property, push, querySelectorAll, replace, role, + scrollTop, select, signature, sort, split, stop, style, title, trim, value, + warnings +*/ + +import jslint from "./jslint.js"; + +// This is the web script companion file for JSLint. It includes code for +// interacting with the browser and displaying the reports. + +let rx_crlf = /\n|\r\n?/; +let rx_separator = /[\s,;'"]+/; + +let fudge_unit = 0; + +const elem_aux = document.getElementById("JSLINT_AUX"); +const elem_boxes = document.querySelectorAll("[type=checkbox]"); +const elem_fudge = document.getElementById("JSLINT_FUDGE"); +const elem_global = document.getElementById("JSLINT_GLOBAL"); +const elem_number = document.getElementById("JSLINT_NUMBER"); +const elem_property = document.getElementById("JSLINT_PROPERTY"); +const elem_property_fieldset = document.getElementById( + "JSLINT_PROPERTYFIELDSET" +); +const elem_report_field = document.getElementById("JSLINT_REPORT"); +const elem_report_list = document.getElementById("JSLINT_REPORT_LIST"); +const rx_amp = /&/g; +const rx_gt = />/g; +const rx_lt = / with less destructive entities. + + return String( + string + ).replace( + rx_amp, + "&" + ).replace( + rx_lt, + "<" + ).replace( + rx_gt, + ">" + ); +} + +function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + let fudge = Number(Boolean(data.option.fudge)); + let output = []; + if (data.stop) { + output.push("
    JSLint was unable to finish.
    "); + } + data.warnings.forEach(function (warning) { + output.push( + "
    ", + entityify(warning.line + fudge), + ".", + entityify(warning.column + fudge), + "
    ", + entityify(warning.message), + "
    ", + entityify(data.lines[warning.line] || ""), + "" + ); + }); + return output.join(""); +} + +function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + let fudge = Number(Boolean(data.option.fudge)); + let mode = ( + data.module + ? "module" + : "global" + ); + let output = []; + + if (data.json) { + return ( + data.warnings.length === 0 + ? "
    JSON: good.
    " + : "
    JSON: bad.
    " + ); + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + let global = Object.keys(data.global.context).sort(); + let froms = data.froms.sort(); + let exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + let context = the_function.context; + let list = Object.keys(context); + output.push( + "
    ", + entityify(the_function.line + fudge), + "
    ", + ( + the_function.name === "=>" + ? entityify(the_function.signature) + " =>" + : ( + typeof the_function.name === "string" + ? "«" + entityify(the_function.name) + "»" + : "" + entityify(the_function.name.id) + "" + ) + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + let params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.parent === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.parent === the_function + ); + })); + detail("outer", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.parent !== the_function + && the_variable.parent.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].parent.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + output.push( + "
    JSLint edition ", + entityify(data.edition), + "
    " + ); + return output.join(""); +} + +function property_directive(data) { + +// Produce the /*property*/ directive. + + let not_first = false; + let output = ["/*property"]; + let length = 1111; + let properties = Object.keys(data.property); + + if (properties.length > 0) { + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length >= 80) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); + } +} + +function show_numbers() { + elem_number.value = elem_source.value.split(rx_crlf).map(function ( + ignore, + index + ) { + return index + fudge_unit; + }).join("\n"); +} + +function clear() { + elem_aux.style.display = "none"; + elem_number.value = ""; + elem_property.value = ""; + elem_property_fieldset.style.display = "none"; + elem_report_field.style.display = "none"; + elem_report_list.innerHTML = ""; + elem_source.focus(); + elem_source.value = ""; + elem_warnings.style.display = "none"; + elem_warnings_list.innerHTML = ""; +} + +function fudge_change() { + fudge_unit = Number(elem_fudge.checked); + show_numbers(); +} + +function clear_options() { + elem_boxes.forEach(function (node) { + node.checked = false; + }); + fudge_change(); + elem_global.innerHTML = ""; +} + +function call_jslint() { + +// First build the option object. + + let option = Object.create(null); + elem_boxes.forEach(function (node) { + if (node.checked) { + option[node.title] = true; + } + }); + +// Call JSLint with the source text, the options, and the predefined globals. + + let global_string = elem_global.value; + let result = jslint( + elem_source.value, + option, + ( + global_string === "" + ? undefined + : global_string.split(rx_separator) + ) + ); + +// Generate the reports. + + let error_html = error_report(result); + let function_html = function_report(result); + let property_text = property_directive(result); + +// Display the reports. + + elem_warnings_list.innerHTML = error_html; + elem_warnings.style.display = ( + error_html.length === 0 + ? "none" + : "block" + ); + + elem_report_list.innerHTML = function_html; + elem_report_field.style.display = "block"; + if (property_text) { + elem_property.value = property_text; + elem_property_fieldset.style.display = "block"; + elem_property.scrollTop = 0; + elem_select.disable = false; + } else { + elem_property_fieldset.style.display = "none"; + elem_select.disable = true; + } + elem_aux.style.display = "block"; + elem_source.select(); +} + +elem_fudge.onchange = fudge_change; + +elem_source.onchange = function (ignore) { + show_numbers(); +}; + +elem_source.onscroll = function () { + let ss = elem_source.scrollTop; + elem_number.scrollTop = ss; + let sn = elem_number.scrollTop; + if (ss > sn) { + show_numbers(); + elem_number.scrollTop = ss; + } +}; + +document.addEventListener("keydown", function (evt) { + if (evt.ctrlKey && evt.key === "Enter") { + call_jslint(); + } +}); + +document.querySelectorAll("[name='JSLint']").forEach(function (node) { + node.onclick = call_jslint; +}); + +document.querySelectorAll("[name='clear']").forEach(function (node) { + node.onclick = clear; +}); + +document.getElementById("JSLINT_SELECT").onclick = function () { + elem_property.focus(); + elem_property.select(); +}; +document.getElementById("JSLINT_CLEAR_OPTIONS").onclick = clear_options; + +fudge_change(); +elem_source.select(); +elem_source.focus(); +elem_source.value = String(` +#!/usr/bin/env node +/*jslint devel*/ +import jslint from "./jslint.js"; +import https from "https"; +(async function () { + let result; + result = await new Promise(function (resolve) { + https.request("https://www.jslint.com/jslint.js", function (res) { + result = ""; + res.on("data", function (chunk) { + result += chunk; + }).on("end", function () { + resolve(result); + }).setEncoding("utf8"); + }).end(); + }); + result = jslint(result); + result.warnings.forEach(function ({ + formatted_message + }) { + console.error(formatted_message); + }); +}()); +`).trim(); +elem_source.onchange(); +call_jslint(); diff --git a/branch.alpha/ci.sh b/branch.alpha/ci.sh new file mode 100755 index 000000000..dcac7cdc0 --- /dev/null +++ b/branch.alpha/ci.sh @@ -0,0 +1,1085 @@ +#!/bin/sh + +# sh one-liner +# head CHANGELOG.md -n20 +# git fetch origin alpha beta master && git fetch upstream alpha beta master +# sh ci.sh shCiBranchPromote origin alpha beta + +shBrowserScreenshot() {(set -e +# this function will run headless-chrome to screenshot url $1 with +# window-size $2 + node -e ' +// init debugInline +if (!globalThis.debugInline) { + let consoleError; + consoleError = console.error; + globalThis.debugInline = function (...argList) { + /* + * this function will both print to stderr and + * return [0] + */ + consoleError("\n\ndebugInline"); + consoleError(...argList); + consoleError("\n"); + return argList[0]; + }; +} +(function () { + "use strict"; + let file; + let timeStart; + let url; + if (process.platform !== "linux") { + return; + } + timeStart = Date.now(); + url = process.argv[1]; + if (!( + /^\w+?:/ + ).test(url)) { + url = require("path").resolve(url); + } + file = require("url").parse(url).pathname; + // remove prefix $PWD from file + if (String(file + "/").indexOf(process.cwd() + "/") === 0) { + file = file.replace(process.cwd(), ""); + } + file = ".build/screenshot.browser." + encodeURIComponent(file).replace(( + /%/g + ), "_").toLowerCase(); + process.on("exit", function (exitCode) { + if (typeof exitCode === "object" && exitCode) { + console.error(exitCode); + exitCode = 1; + } + console.error( + "shBrowserScreenshot" + + "\n - url - " + url + + "\n - wrote - " + file + ".html" + + "\n - wrote - " + file + ".png" + + "\n - timeElapsed - " + (Date.now() - timeStart) + " ms" + + "\n - EXIT_CODE=" + exitCode + ); + }); + [ + ".html", ".png" + ].forEach(function (extname) { + let argList; + let child; + argList = Array.from([ + "--headless", + "--ignore-certificate-errors", + "--incognito", + "--timeout=30000", + "--user-data-dir=/dev/null", + "--window-size=" + (process.argv[2] || "800x600"), + ( + extname === ".html" + ? "--dump-dom" + : "" + ), + ( + extname === ".png" + ? "--screenshot" + : "" + ), + ( + extname === ".png" + ? "-screenshot=" + file + ".png" + : "" + ), + ( + (process.getuid && process.getuid() === 0) + ? "--no-sandbox" + : "" + ), + url + ]).filter(function (elem) { + return elem; + }); + // debug argList + // console.error(argList); + child = require("child_process").spawn(( + process.platform === "darwin" + ? "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" + : process.platform === "win32" + ? "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe" + : "/usr/bin/google-chrome-stable" + ), argList, { + stdio: [ + "ignore", "pipe", 2 + ] + }); + child.stdout.pipe( + extname === ".html" + ? require("fs").createWriteStream(file + ".html") + : process.stdout + ); + }); +}()); +' "$@" # "' +)} + +shCiArtifactUpload() {(set -e +# this function will upload build-artifacts to branch-gh-pages + export NODE_OPTIONS="--unhandled-rejections=strict" + node -e ' +process.exit( + `${process.version.split(".")[0]}.${process.arch}.${process.platform}` !== + process.env.CI_NODE_VERSION_ARCH_PLATFORM +); +' || return 0 + local BRANCH + # init $BRANCH + BRANCH="$(git rev-parse --abbrev-ref HEAD)" + # init .git/config + git config --local user.email "github-actions@users.noreply.github.com" + git config --local user.name "github-actions" + # update README.md with $GITHUB_REPOSITORY + sed -i \ + -e "s|\bjslint-org/jslint\b|$GITHUB_REPOSITORY|g" \ + -e "s|\bjslint-org\.github\.io/jslint\b|$( + printf "$GITHUB_REPOSITORY" | sed -e "s|/|.github.io/|" + )|g" \ + README.md + # add dir .build + git add -f .build + git commit -am "add dir .build" + # checkout branch-gh-pages + git checkout -b gh-pages + git fetch origin gh-pages + git reset --hard origin/gh-pages + # update dir branch.$BRANCH + rm -rf "branch.$BRANCH" + mkdir "branch.$BRANCH" + (set -e + cd "branch.$BRANCH" + git init -b branch1 + git pull --depth=1 .. "$BRANCH" + rm -rf .git + git add -f . + ) + # update root-dir with branch-beta + if [ "$BRANCH" = beta ] + then + git rm -rf .build + git checkout beta . + fi + git status + git commit -am "update dir branch.$BRANCH" || true + # if branch-gh-pages has more than 100 commits, + # then backup and squash commits + if [ "$(git rev-list --count gh-pages)" -gt 100 ] + then + # backup + shGitCmdWithGithubToken push origin -f gh-pages:gh-pages.backup + # squash commits + git checkout --orphan squash1 + git commit --quiet -am squash || true + # reset branch-gh-pages to squashed-commit + git push . -f squash1:gh-pages + git checkout gh-pages + # force-push squashed-commit + shGitCmdWithGithubToken push origin -f gh-pages + fi + # list files + shGitLsTree + # push branch-gh-pages + shGitCmdWithGithubToken push origin gh-pages + # validate http-links + (set -e + cd "branch.$BRANCH" + sleep 15 + shDirHttplinkValidate + ) +)} + +shCiBase() {(set -e +# this function will run base-ci + export NODE_OPTIONS="--unhandled-rejections=strict" + # run test with coverage-report + # coverage-hack - test jslint's invalid-file handling-behavior + mkdir -p .test-dir.js + # coverage-hack - test jslint's ignore-file handling-behavior + touch .test-min.js + # test jslint's cli handling-behavior + ./jslint.js . + (set -e + # coverage-hack - test jslint's cli handling-behavior + export JSLINT_CLI=1 + shRunWithCoverage node test.js . + ) + # screenshot live-web-demo + shBrowserScreenshot \ + https://jslint-org.github.io/jslint/branch.beta/index.html +)} + +shCiBranchPromote() {(set -e +# this function will promote branch $REMOTE/$BRANCH1 to branch $REMOTE/$BRANCH2 + local BRANCH1 + local BRANCH2 + local REMOTE + REMOTE="$1" + shift + BRANCH1="$1" + shift + BRANCH2="$1" + shift + git fetch "$REMOTE" "$BRANCH1" + git push "$REMOTE" "$REMOTE/$BRANCH1:$BRANCH2" "$@" +)} + +shDirHttplinkValidate() {(set -e +# this function will validate http-links embedded in .html and .md files + node -e ' +(async function () { + "use strict"; + let dict = {}; + Array.from( + await require("fs").promises.readdir(".") + ).forEach(async function (file) { + if (!( + /.\.html$|.\.md$/m + ).test(file)) { + return; + } + let data = await require("fs").promises.readFile(file, "utf8"); + data.replace(( + /\bhttps?:\/\/.*?(?:[\s")\]]|$)/gm + ), function (url) { + url = url.slice(0, -1).replace(( + /[\u0022\u0027]/g + ), "").replace(( + /\/branch\.\w+?\//g + ), "/branch.alpha/").replace(( + /\bjslint-org\/jslint\b/g + ), process.env.GITHUB_REPOSITORY || "jslint-org/jslint").replace(( + /\bjslint-org\.github\.io\/jslint\b/g + ), String( + process.env.GITHUB_REPOSITORY || "jslint-org/jslint" + ).replace("/", ".github.io/")); + if (url.indexOf("http://") === 0) { + throw new Error( + "shDirHttplinkValidate - insecure link " + url + ); + } + // ignore duplicate-link + if (dict.hasOwnProperty(url)) { + return ""; + } + dict[url] = true; + let req = require("https").request(require("url").parse( + url + ), function (res) { + console.error( + "shDirHttplinkValidate " + res.statusCode + " " + url + ); + if (!(res.statusCode < 400)) { + throw new Error( + "shDirHttplinkValidate - " + file + + " - unreachable link " + url + ); + } + req.abort(); + res.destroy(); + }); + req.setTimeout(30000); + req.end(); + return ""; + }); + data.replace(( + /(\bhref=|\bsrc=|\burl\(|\[[^]*?\]\()("?.*?)(?:[")\]]|$)/gm + ), function (ignore, linkType, url) { + if (linkType[0] !== "[") { + url = url.slice(1); + } + // ignore duplicate-link + if (dict.hasOwnProperty(url)) { + return ""; + } + dict[url] = true; + if (!( + /^https?|^mailto:|^[#\/]/m + ).test(url)) { + require("fs").stat(url, function (ignore, exists) { + console.error( + "shDirHttplinkValidate " + Boolean(exists) + " " + url + ); + if (!exists) { + throw new Error( + "shDirHttplinkValidate - " + file + + " - unreachable link " + url + ); + } + }); + } + return ""; + }); + }); +}()); +' # "' +)} + +shGitCmdWithGithubToken() {(set -e +# this function will run git $CMD with $GITHUB_TOKEN + local CMD + local EXIT_CODE + local REMOTE + local URL + printf "shGitCmdWithGithubToken $*\n" + CMD="$1" + shift + REMOTE="$1" + shift + URL="$( + git config "remote.$REMOTE.url" | + sed -e "s|https://|https://x-access-token:$GITHUB_TOKEN@|" + )" + EXIT_CODE=0 + # hide $GITHUB_TOKEN in case of err + git "$CMD" "$URL" "$@" 2>/dev/null || EXIT_CODE="$?" + printf "shGitCmdWithGithubToken - EXIT_CODE=$EXIT_CODE\n" 1>&2 + return "$EXIT_CODE" +)} + +shGitLsTree() {(set -e +# this function will "git ls-tree" all files committed in HEAD +# example use: +# shGitLsTree | sort -rk3 # sort by date +# shGitLsTree | sort -rk4 # sort by size + node -e ' +(async function () { + "use strict"; + let result; + // get file, mode, size + result = await new Promise(function (resolve) { + result = ""; + require("child_process").spawn("git", [ + "ls-tree", "-lr", "HEAD" + ], { + encoding: "utf8", + stdio: [ + "ignore", "pipe", 2 + ] + }).on("exit", function () { + resolve(result); + }).stdout.on("data", function (chunk) { + result += chunk; + }).setEncoding("utf8"); + }); + result = Array.from(result.matchAll( + /^(\S+?)\u0020+?\S+?\u0020+?\S+?\u0020+?(\S+?)\t(\S+?)$/gm + )).map(function ([ + ignore, mode, size, file + ]) { + return { + file, + mode: mode.slice(-3), + size: Number(size) + }; + }); + result = result.sort(function (aa, bb) { + return aa.file > bb.file || -1; + }); + result = result.slice(0, 1000); + result.unshift({ + file: ".", + mode: "755", + size: 0 + }); + // get date + result.forEach(function (elem) { + result[0].size += elem.size; + require("child_process").spawn("git", [ + "log", "--max-count=1", "--format=%at", elem.file + ], { + stdio: [ + "ignore", "pipe", 2 + ] + }).stdout.on("data", function (chunk) { + elem.date = new Date( + Number(chunk) * 1000 + ).toISOString().slice(0, 19) + "Z"; + }); + }); + process.on("exit", function () { + let iiPad; + let sizePad; + iiPad = String(result.length).length + 1; + sizePad = String(Math.ceil(result[0].size / 1024)).length; + process.stdout.write(result.map(function (elem, ii) { + return ( + String(ii + ".").padStart(iiPad, " ") + + " " + elem.mode + + " " + elem.date + + " " + String( + Math.ceil(elem.size / 1024) + ).padStart(sizePad, " ") + " KB" + + " " + elem.file + + "\n" + ); + }).join("")); + }); +}()); +' # "' +)} + +shRunWithCoverage() {(set -e +# this function will run nodejs command $@ with v8-coverage and +# create coverage-report .build/coverage/index.html + local EXIT_CODE + EXIT_CODE=0 + export DIR_COVERAGE=.build/coverage/ + rm -rf "$DIR_COVERAGE" + (set -e + export NODE_V8_COVERAGE="$DIR_COVERAGE" + "$@" + ) || EXIT_CODE="$?" + if [ "$EXIT_CODE" = 0 ] + then + node -e ' +/*jslint bitwise*/ +// init debugInline +if (!globalThis.debugInline) { + let consoleError; + consoleError = console.error; + globalThis.debugInline = function (...argList) { + /* + * this function will both print to stderr and + * return [0] + */ + consoleError("\n\ndebugInline"); + consoleError(...argList); + consoleError("\n"); + return argList[0]; + }; +} +(async function () { + "use strict"; + let DIR_COVERAGE = process.env.DIR_COVERAGE; + let cwd; + let data; + let fileDict; + async function htmlRender({ + fileList, + lineList, + pathname + }) { + let html; + let padLines; + let padPathname; + let txt; + let txtBorder; + function stringHtmlSafe(str) { + /* + * this function will make html-safe + * https://stackoverflow.com/questions/7381974/ + * which-characters-need-to-be-escaped-on-html + */ + return str.replace(( + /&/gu + ), "&").replace(( + /"/gu + ), """).replace(( + /\u0027/gu + ), "'").replace(( + //gu + ), ">").replace(( + /&(amp;|apos;|gt;|lt;|quot;)/igu + ), "&$1"); + } + html = ""; + html += ` + + +coverage-report + + + +
    +
    coverage-report
    + + + + + + + +`; + if (!lineList) { + padLines = String("100.00 %").length; + padPathname = 32; + fileList.unshift({ + linesCovered: 0, + linesTotal: 0, + pathname: "./" + }); + fileList.slice(1).forEach(function ({ + linesCovered, + linesTotal, + pathname + }) { + fileList[0].linesCovered += linesCovered; + fileList[0].linesTotal += linesTotal; + padPathname = Math.max(padPathname, pathname.length + 2); + padLines = Math.max( + padLines, + String(linesCovered + " / " + linesTotal).length + ); + }); + } + txtBorder = ( + "+" + "-".repeat(padPathname + 2) + "+" + + "-".repeat(padLines + 2) + "+\n" + ); + txt = ""; + txt += "coverage-report\n"; + txt += txtBorder; + txt += ( + "| " + String("files covered").padEnd(padPathname, " ") + " | " + + String("lines").padStart(padLines, " ") + " |\n" + ); + txt += txtBorder; + fileList.forEach(function ({ + linesCovered, + linesTotal, + pathname + }, ii) { + let coverageLevel; + let coveragePct; + coveragePct = Math.floor(10000 * linesCovered / linesTotal | 0); + coverageLevel = ( + coveragePct >= 8000 + ? "coverageHigh" + : coveragePct >= 5000 + ? "coverageMedium" + : "coverageLow" + ); + coveragePct = String(coveragePct).replace(( + /..$/m + ), ".$&"); + if (!lineList && ii === 0) { + let fill = ( + // red + "#" + Math.round( + (100 - Number(coveragePct)) * 2.21 + ).toString(16).padStart(2, "0") + // green + + Math.round( + Number(coveragePct) * 2.21 + ).toString(16).padStart(2, "0") + + // blue + "00" + ); + let str1 = "coverage"; + let str2 = coveragePct + " %"; + let xx1 = 6 * str1.length + 20; + let xx2 = 6 * str2.length + 20; + // fs - write coverage-badge.svg + require("fs").promises.writeFile(( + DIR_COVERAGE + "/coverage-badge.svg" + ), String(` + + + + +${str1} +${str2} + + + `).trim() + "\n"); + pathname = ""; + } + txt += ( + "| " + + String("./" + pathname).padEnd(padPathname, " ") + " | " + + String(coveragePct + " %").padStart(padLines, " ") + " |\n" + ); + txt += ( + "| " + "*".repeat( + Math.round(0.01 * coveragePct * padPathname) + ).padEnd(padPathname, "_") + " | " + + String( + linesCovered + " / " + linesTotal + ).padStart(padLines, " ") + " |\n" + ); + txt += txtBorder; + pathname = stringHtmlSafe(pathname); + html += ` + + +`; + }); + if (lineList) { + html += ` +
    files coveredlines
    + ${( + lineList + ? ( + "./ " + + pathname + "
    " + ) + : ( + "./ " + + pathname + "
    " + ) + )} +
    +
    +
    +
    + ${coveragePct} %
    + ${linesCovered} / ${linesTotal} +
    +
    +
    +`; + lineList.forEach(function ({ + count, + holeList, + line, + startOffset + }, ii) { + let chunk; + let inHole; + let lineId; + let lineHtml; + lineHtml = ""; + lineId = "line_" + (ii + 1); + switch (count) { + case -1: + case 0: + if (holeList.length === 0) { + lineHtml += ""; + lineHtml += ""; + lineHtml += stringHtmlSafe(line); + break; + } + line = line.split("").map(function (chr) { + return { + chr, + isHole: undefined + }; + }); + holeList.forEach(function ([ + aa, bb + ]) { + aa = Math.max(aa - startOffset, 0); + bb = Math.min(bb - startOffset, line.length); + while (aa < bb) { + line[aa].isHole = true; + aa += 1; + } + }); + chunk = ""; + line.forEach(function ({ + chr, + isHole + }) { + if (inHole !== isHole) { + lineHtml += stringHtmlSafe(chunk); + lineHtml += ( + isHole + ? "" + : "" + ); + chunk = ""; + inHole = isHole; + } + chunk += chr; + }); + lineHtml += stringHtmlSafe(chunk); + break; + default: + lineHtml += stringHtmlSafe(line); + } + html += String(` +
    +
    +${String(ii + 1).padStart(5, " ")}.
    +
    +
    +${String(count).padStart(7, " ")}
    +
    +${lineHtml}
    +
    + `).replace(( + /\n/g + ), "").trim() + "\n"; + }); + } + html += ` +
    +
    +
    + +`; + html += "\n"; + await require("fs").promises.mkdir(require("path").dirname(pathname), { + recursive: true + }); + // fs - write *.html + require("fs").promises.writeFile(pathname + ".html", html); + if (lineList) { + return; + } + // fs - write coverage.txt + console.error("\n" + txt); + require("fs").promises.writeFile(( + DIR_COVERAGE + "/coverage-report.txt" + ), txt); + } + data = await require("fs").promises.readdir(DIR_COVERAGE); + await Promise.all(data.map(async function (file) { + if (( + /^coverage-.*?\.json$/ + ).test(file)) { + data = await require("fs").promises.readFile(( + DIR_COVERAGE + file + ), "utf8"); + // fs - rename to coverage-v8.json + require("fs").promises.rename( + DIR_COVERAGE + file, + DIR_COVERAGE + "coverage-v8.json" + ); + } + })); + fileDict = {}; + cwd = process.cwd().replace(( + /\\/g + ), "/") + "/"; + await Promise.all(JSON.parse(data).result.map(async function ({ + functions, + url + }) { + let lineList; + let linesCovered; + let linesTotal; + let pathname; + let src; + if (url.indexOf("file:///") !== 0) { + return; + } + pathname = url.replace(( + process.platform === "win32" + ? "file:///" + : "file://" + ), "").replace(( + /\\\\/g + ), "/"); + if ( + pathname.indexOf(cwd) !== 0 + || pathname.indexOf(cwd + "[") === 0 + || ( + process.env.npm_config_mode_coverage !== "all" + && pathname.indexOf("/node_modules/") >= 0 + ) + ) { + return; + } + pathname = pathname.replace(cwd, ""); + src = await require("fs").promises.readFile(pathname, "utf8"); + lineList = [{}]; + src.replace(( + /^.*$/gm + ), function (line, startOffset) { + lineList[lineList.length - 1].endOffset = startOffset - 1; + lineList.push({ + count: -1, + endOffset: 0, + holeList: [], + line, + startOffset + }); + return ""; + }); + lineList.shift(); + lineList[lineList.length - 1].endOffset = src.length; + functions.reverse().forEach(function ({ + ranges + }) { + ranges.reverse().forEach(function ({ + count, + endOffset, + startOffset + }, ii, list) { + lineList.forEach(function (elem) { + if (!( + ( + elem.startOffset <= startOffset + && startOffset <= elem.endOffset + ) || ( + elem.startOffset <= endOffset + && endOffset <= elem.endOffset + ) || ( + startOffset <= elem.startOffset + && elem.endOffset <= endOffset + ) + )) { + return; + } + // handle root-range + if (ii + 1 === list.length) { + if (elem.count === -1) { + elem.count = count; + } + return; + } + // handle non-root-range + if (elem.count !== 0) { + elem.count = Math.max(count, elem.count); + } + if (count === 0) { + elem.count = 0; + elem.holeList.push([ + startOffset, endOffset + ]); + } + }); + }); + }); + linesTotal = lineList.length; + linesCovered = lineList.filter(function ({ + count + }) { + return count > 0; + }).length; + await require("fs").promises.mkdir(( + require("path").dirname(DIR_COVERAGE + pathname) + ), { + recursive: true + }); + await htmlRender({ + fileList: [ + { + linesCovered, + linesTotal, + pathname + } + ], + lineList, + pathname: DIR_COVERAGE + pathname + }); + fileDict[pathname] = { + lineList, + linesCovered, + linesTotal, + pathname, + src + }; + })); + await htmlRender({ + fileList: Object.keys(fileDict).sort().map(function (pathname) { + return fileDict[pathname]; + }), + pathname: DIR_COVERAGE + "index" + }); +}()); +' # "' + find "$DIR_COVERAGE" + fi + printf "shRunWithCoverage - EXIT_CODE=$EXIT_CODE\n" 1>&2 + return "$EXIT_CODE" +)} + +shRunWithScreenshotTxt() {(set -e +# this function will run cmd $@ and screenshot text-output +# https://www.cnx-software.com/2011/09/22/how-to-convert-a-command-line-result-into-an-image-in-linux/ + local EXIT_CODE + EXIT_CODE=0 + export SCREENSHOT_SVG=.build/screenshot.svg + rm -f "$SCREENSHOT_SVG" + printf "0\n" > "$SCREENSHOT_SVG.exit_code" + shCiPrint "shRunWithScreenshotTxt - (shRun $* 2>&1)" + (set -e + (shRun "$@" 2>&1) || printf "$?\n" > "$SCREENSHOT_SVG.exit_code" + ) | tee /tmp/shRunWithScreenshotTxt.txt + EXIT_CODE="$(cat "$SCREENSHOT_SVG.exit_code")" + shCiPrint "shRunWithScreenshotTxt - EXIT_CODE=$EXIT_CODE" + # run shRunWithScreenshotTxtAfter + if (type shRunWithScreenshotTxtAfter > /dev/null 2>&1) + then + eval shRunWithScreenshotTxtAfter + unset shRunWithScreenshotTxtAfter + fi + # format text-output + node -e ' +(async function () { + "use strict"; + let result; + let yy; + yy = 10; + result = await require("fs").promises.readFile( + require("os").tmpdir() + "/shRunWithScreenshotTxt.txt", + "utf8" + ); + // remove ansi escape-code + result = result.replace(( + /\u001b.*?m/g + ), ""); + // format unicode + result = result.replace(( + /\\u[0-9a-f]{4}/g + ), function (match0) { + return String.fromCharCode("0x" + match0.slice(-4)); + }).trimEnd(); + // 96 column wordwrap + result = result.replace(( + /^.*?$/gm + ), function (line) { + return line.replace(( + /.{0,96}/g + ), function (line, ii) { + if (ii && !line) { + return ""; + } + yy += 16; + return "" + line.replace(( + /&/g + ), "&").replace(( + //g + ), ">") + ""; + }).replace(( + /(<\/tspan>\n" + + "\n" + + "\n" + + result + "\n\n" + ); + try { + await require("fs").promises.mkdir(( + require("path").dirname(process.argv[1]) + ), { + recursive: true + }); + } catch (ignore) {} + require("fs").promises.writeFile(process.argv[1], result); +}()); +' "$SCREENSHOT_SVG" # "' + shCiPrint "shRunWithScreenshotTxt - wrote - $SCREENSHOT_SVG" + printf "shRunWithScreenshotTxt - EXIT_CODE=$EXIT_CODE\n" 1>&2 + return "$EXIT_CODE" +)} + +# run $@ +"$@" diff --git a/branch.alpha/font-daley-bold.woff2 b/branch.alpha/font-daley-bold.woff2 new file mode 100644 index 000000000..783bdd697 Binary files /dev/null and b/branch.alpha/font-daley-bold.woff2 differ diff --git a/branch.alpha/font-programma-bold.woff2 b/branch.alpha/font-programma-bold.woff2 new file mode 100644 index 000000000..dfb5a9753 Binary files /dev/null and b/branch.alpha/font-programma-bold.woff2 differ diff --git a/branch.alpha/function.html b/branch.alpha/function.html new file mode 100644 index 000000000..feb0c13dc --- /dev/null +++ b/branch.alpha/function.html @@ -0,0 +1,613 @@ + + + + + + + + +JSLint: jslint function + + + +
    JSLint
    + +
    The jslint Function
    +
    +

    JSLint

    +

    JSLint is delivered as a set of files:

    +
    + + + + + + + + + + + + +
    FilenameContent
    browser.jsBrowser based UI and report object, containing report + generator functions for HTML.
    function.htmlThis page that you are looking at right now.
    help.htmlUsing jslint as a single page JSLint application.
    index.htmlSingle page JSLint application that uses the + js files.
    jslint.jsThe jslint function.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    +
    + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring"(JSLint)"
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    parenttokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    parenttokenThe function in which the variable was declared.variable
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    shebangstringThe first line of text if it started with #!line
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable may be assigned to.variable
    +

    Reports

    +

    The report object contains three functions that all take a + result from the jslint function as input. + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    +
    + +
    + + + +
    + + diff --git a/branch.alpha/help.html b/branch.alpha/help.html new file mode 100644 index 000000000..63c34f0c6 --- /dev/null +++ b/branch.alpha/help.html @@ -0,0 +1,818 @@ + + + + + + + + + +JSLint: Help + + + +
    JSLint
    + +
    Help
    +
    +
    Il semble que la perfection soit atteinte non quand il n’y a + plus rien à ajouter, mais quand il n’y a plus rien à + retrancher.
    +
    + Antoine de Saint-Exupéry +
    +
    + Terre des Hommes (1939) +
    +

    Good Parts

    +

    The Principle of the Good Parts is

    +
    If a feature is sometimes useful and sometimes dangerous and if + there is a better option then always use the better option.
    +

    JSLint is a JavaScript program that looks for problems in + JavaScript programs. It is a code quality tool.

    +

    When C was + a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    +

    As the language matured, the definition of the language was + strengthened to eliminate some insecurities, and compilers got better + at issuing warnings. lint is no longer needed.

    +

    JavaScript is a + young-for-its-age language. It was originally intended to do small tasks in + webpages, tasks for which Java was too heavy and clumsy. But JavaScript is + a surprisingly capable language, and it is now being used in larger + projects. Many of the features that were intended to make the language easy + to use are troublesome when projects become complicated. A lint + for JavaScript is needed: JSLint, a JavaScript syntax checker + and validator.

    +

    JSLint takes a JavaScript source and scans it. If it finds a + problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by the ECMAScript Programming Language + Standard (the strangely named document that governs JavaScript). + JSLint will reject most legal programs. It is a higher + standard.

    +

    JavaScript is a sloppy language, but hidden deep inside there is an elegant, + better language. JSLint helps you to program in that better + language and to avoid most of the slop. JSLint will reject + programs that browsers will accept because JSLint is concerned + with the quality of your code and browsers are not. You should gladly accept + all of JSLint's advice.

    +

    JSLint can operate on JavaScript source + or JSON text.

    +

    ECMAScript Sixth Edition

    +

    Some of + ES6’s features are good, so JSLint will recognize the good + parts of ES6.

    +

    Currently, these features are recognized:

    +
      +
    • The ... ellipsis marker in parameter + lists and argument lists, replacing the arguments object + for variadic functions.
    • +
    • The let statement, which is like the var + statement except that it respects block scope. + You may use let or var but not both.
    • +
    • The const statement is like the let statement + except that it disallows the use of assignment on the variable, although + if the value of the variable is mutable, it can still be mutated. + const is preferred to let. +
    • Destructuring of arrays and objects is allowed in parameter lists and on + the left side of let, and const, but not + var or assignment statements, and not deep destructuring + or eliding.
    • +
    • Enhanced object literals, providing shorter forms for function + declaration and properties that are initialized by variables with the + same name.
    • +
    • The fat arrow => fart functions.
    • +
    • The simplest forms of import and export.
    • +
    • `Megastring` literals, but not nested + `megastring` literals.
    • +
    • New global functions, such as Map, Set, + WeakMap, and WeakSet.
    • +
    • 0b- and 0o- number literals.
    • +
    +

    The most important new feature of ES6 is proper tail calls. This has no + new syntax, so JSLint doesn’t see it. But it makes recursion + much more attractive, which makes loops, particularly for + loops, much less attractive.

    +

    import export

    +

    The ES6 module feature will be an important improvement over JavaScript’s + global variables as a means of linking separate files together. + JSLint recognizes a small but essential subset of the module + syntax.

    +
    +

    import name from stringliteral;

    +

    import {name} from stringliteral;

    +

    import(string).then(function);

    +

    export default function () {};

    +

    export default function name() {};

    +

    export default expression;

    +

    export function name() {}

    +

    export name; // where name is const

    +

    export {name};

    +
    +

    Directives

    +

    JSLint provides three directives that may be placed in a + file to manage JSLint’s behavior. Use of these directives is + optional. If they are used, they should be placed in a source file before + the first statement. They are written in the form of a comment, where the + directive name is placed immediately after the opening of the comment before + any whitespace. The three directives are global, + jslint, and property. Directives in a file are + stronger than options selected from the UI or passed with the option object.

    +

    /*global*/

    +

    The /*global*/ directive is used to specify a set of globals + (usually functions and objects containing functions) that are available to + this file. This was commonly used in browsers to link source files together + before ES6 modules appeared. Use of global variables is strongly + discouraged, but unfortunately web browsers require their use. The + /*global*/ directive can only be used when the + Assume a browser option is selected. +

    Each of the names listed will indicate a read-only global variable. The names + are separated by , comma. This directive + should not be used if import or export is used. + This directive inhibits warnings, but it does not declare the names in the + execution environment. +

    For example: +

    /*global +
    ADSAFE, report, jslint
    +*/
    +

    instructs JSLint to not give warnings about the global + variables ADsafe, report, and jslint. + However, if any of those names are expected to be supplied by other files + and those other files fail to do so, then execution errors will result. It + is usually better to use top-level variable declarations instead: +

        var ADSAFE;
    +    var report;
    +    var jslint; 
    +

    Using var in this way allows comparing a global variable to the + undefined value to determine whether it is has been used in the global context.

    +

    /*jslint*/

    +

    The /*jslint*/ directive allows for the control of several + options. These options can also be set from the + JSLint.com user interface.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Tolerate bitwise operatorsbitwisetrue if bitwise operators should be allowed. The + bitwise operators are rarely used in JavaScript programs, so it + is usually more likely that & is a mistyping of + && than that it indicates a bitwise and. + JSLint will give warnings on the bitwise operators + unless this option is selected.
    Assume a browser browsertrue if the standard browser globals should be + predefined. This option will reject the use of + import and export. This option also + disallows the file form of the "use + strict" pragma. It does not supply + self; you will have to request that unnecessary + alias of the dreaded global object yourself. It adds the same + globals as this directive: +
    /*global +
    + clearInterval, clearTimeout, document, event, FileReader, + FormData, history, localStorage, location, name, navigator, + screen, sessionStorage, setInterval, setTimeout, Storage, + URL, window, XMLHttpRequest +
    + */
    +
    Tolerate conversion operatorsconverttrue if !!, + prefix, + and concatenation with "" are allowed. + The Boolean, Number, and + String functions are preferred
    Assume CouchDBcouchtrue if + Couch DB + globals should be predefined. It adds the same globals as this + directive: +
    /*global +
    emit, getRow, isArray, log, provides, registerType, require, + send, start, sum, toJSON
    + */
    Assume in developmentdeveltrue if browser globals that are useful in + development should be predefined, and if debugger + statements and TODO comments + should be allowed. It adds the + same globals as this directive: +
    /*global +
    alert, confirm, console, prompt
    + */
    Be sure to turn this option off before going + into production.
    Tolerate evalevaltrue if eval should be allowed. In the + past, the eval function was the most misused + feature of the language.
    Tolerate for statementfortrue if the for statement should be + allowed. It is almost always better to use the array methods + instead.
    Fudge the line and column numbersfudgetrue if the origin of a text file is line 1 column + 1. Programmers know that number systems start at zero. + Unfortunately, most text editors start numbering lines and + columns at one. JSLint will by default start + numbering at zero. Use this option to start the numbering at one + in its reports.
    Tolerate get and setgetsettrue if accessor properties are allowed in object literals.
    Tolerate long source lineslongtrue if a line can contain more than 80 characters.
    Assume Node.jsnodetrue if Node.js globals should be predefined. It + will predefine globals that are used in the Node.js environment. + It adds the same globals as this directive: +
    /*global +
    + Buffer, clearImmediate, clearInterval, clearTimeout, console, exports, + module, process, require, setImmediate, setInterval, setTimeout, URL, + URLSearchParams, __dirname, __filename +
    + */
    Tolerate single quote stringssingletrue if 'single quote + should be allowed to enclose string literals.
    Tolerate this
    thistrue if this should be allowed.
    Tolerate unordered properties and params.unorderedtrue if objects and functions are allowed to declare properties and params in non-alphabetical order.
    Tolerate whitespace messwhitetrue if the whitespace rules should be ignored.
    +

    For example:

    +
    /*jslint
    +    bitwise, node
    +*/
    + +

    /*property*/

    +

    The /*property*/ directive is used to declare a list of + property identifiers that are used in the file. Each property name in the + program is looked up in this list. If a name is not found, that indicates an + error, most likely a typing error.

    +

    The list can also be used to evade some of JSLint’s naming + rules.

    +

    JSLint can build the /*property*/ list for you. At + the bottom of its report, JSLint displays a + /*property*/ directive. It contains all of the names that were + used with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. You + can copy the /*property*/ directive to the top of your + script file. JSLint will check the spelling of all property + names against the list. That way, you can have JSLint look for + misspellings for you.

    +

    For example,

    +
    /*property +
    charAt, slice, _$_
    + */
    +

    ignore

    +

    JSLint introduces a new reserved word: ignore. It + is used in parameter lists and in catch clauses to indicate a + parameter that will be ignored. Unused warnings will not be produced for + ignore. +

    function handler(ignore, value) {
    +    return do_something_useful(value);
    +}
    +

    var let const

    +

    JavaScript provides three statements for declaring variables: + var, let, and const. The + var statement suffers from a bad practice called hoisting. The + let statement does not do hoisting and respects block scope. + The const statement is like let except that it + marks the variable (but not its contents) as read only, making it an error + to attempt to assign to the variable. When given a choice, + const is the best, var is the worst.

    +

    JSLint uses the intersection of the var rules and the + let rules, and by doing so avoids the errors related to either. + A name should be declared only once in a function. It should be declared + before it is used. It should not be used outside of the block in which it is + declared. A variable should not have the same name as a variable or + parameter in an outer function. Do not mix var and + let. Declare one name per statement.

    +

    = == ===

    +

    Fortran made a terrible + mistake in using the equality operator as its assignment operator. That + mistake has been replicated in most languages since then. C compounded that + mistake by making == its equality operator. The visual + similarity is a source of errors. JavaScript compounded this further by + making == a type coercing comparison operator that produces + false positive results. This was mitigated by adding the === + operator, leaving the broken == operator in place.

    +

    JSLint attempts to minimize errors by the following rules:

    +

    == is not allowed. This avoids the false positives, and + increases the visual distance between = and ===. + Assignments are not allowed in expression position, and comparisons are not + allowed in statement position. This also reduces confusion. 

    +

    Semicolon

    +

    JavaScript uses a C-like syntax that requires the use of semicolons to + delimit certain statements. JavaScript attempts to make those semicolons + optional with an automatic semicolon insertion mechanism, but it does not + work very well. Automatic semicolon insertion was added to make + things easier for beginners. Unfortunately, it sometimes fails. Do not rely + on it unless you are a beginner.

    +

    JSLint expects that every statement will be followed by + ; except for for, function, + if, switch, try, and + while. JSLint does not expect to see unnecessary + semicolons, the empty statement, or empty blocks.

    +

    function =>

    +

    JavaScript has four syntactic forms for making function objects: function + statements, function expressions, enhanced object literals, and the + => fart operator.

    +

    The function statement creates a variable and assigns the function object to + it. It should be used in a file or function body, but not inside of a block.

    +
    function name(parameters) { +
    statements
    + }
    +

    The function expression unfortunately looks like the function statement. It + may appear anywhere that an expression may appear, but not in statement + position and not in a loop. It produces a function object but does not + create a variable in which to store it.

    +
    function (parameters) { +
    statements
    + }
    +

    The enhanced object literal provides an ES6 shorthand for creating a property + whose value is a function, saving you from having to type + : colon and function.

    +
    { +
    name(parameters) { +
    statements
    + }
    + }
    +

    Finally, ES6 provides an even shorter form of function expression that leaves + out the words function and return:

    +
    (parameters) => + expression
    +

    JSLint requires the parens around the parameters, and forbids a + { left brace after the + => fart to avoid syntactic ambiguity.

    +

    Comma

    +

    The , comma operator is unnecessary and can + mask programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as + an operator. It does not expect to see elided elements in array literals. A + comma should not appear after the last element of an array literal or object + literal.

    +

    Blocks

    +

    JSLint expects blocks with function, + if, switch, while, for, + do, and try statements and nowhere else.

    +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    +

    JavaScript allows an if to be written like this:

    +
    if (condition)
    +    statement;
    +

    That form is known to contribute to mistakes in projects where many + programmers are working on the same code. That is why JSLint + expects the use of a block:

    +
    if (condition) {
    +    statements;
    +}
    +

    Experience shows that this form is more resilient.

    +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call. All other expression statements are considered + to be errors.

    +

    for

    +

    JSLint does not recommend use of the for statement. + Use array methods like forEach instead. The for + option will suppress some warnings. The forms of for that + JSLint accepts are restricted, excluding the new ES6 forms.

    +

    for in

    +

    JSLint does not recommend use of the for + in statement. Use Object.keys instead.

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, it also + loops through all of the properties that were inherited through the + prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written + without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    for (name in object) {
    +    if (object.hasOwnProperty(name)) {
    +        ....
    +    }
    +}
    +

    Note that the above code will fail if the object contains a property + named hasOwnProperty. Use Object.keys instead.

    +

    switch

    +

    A common error in switch statements is to forget to place a + break statement after each case, resulting in unintended + fall-through. JSLint expects that the statement before the next + case or default is one of these: + break, return, or throw.

    +

    with

    +

    The with statement was intended to provide a shorthand in + accessing properties in deeply nested objects. Unfortunately, it behaves + very badly when setting new properties. Never use the with + statement.

    +

    JSLint does not expect to see a with statement.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that + labels will be distinct from vars and parameters.

    +

    Unreachable Code

    +

    JSLint expects that a return, break, + or throw statement will be followed by a + } right brace or case or + default.

    +

    Confusing Pluses and Minuses

    +

    JSLint expects that + will not be followed by + + or ++, and that - will not be + followed by - or --. A misplaced space can turn + + + into ++, an error that is difficult to see. + Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ increment and + -- decrement operators have been known to + contribute to bad code by encouraging excessive trickiness. They are second + only to faulty architecture in enabling to viruses and other security + menaces. Also, preincrement/postincrement confusion can produce off-by-one + errors that are extremely difficult to diagnose. Fortunately, they are also + complete unnecessary. There are better ways to add 1 to a variable.

    +

    It is best to avoid these operators entirely and rely on += and + -= instead.

    +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. + JSLint looks for problems that may cause portability problems. + It also attempts to resolve visual ambiguities by recommending explicit + escapement.

    +

    JavaScript’s syntax for regular expression literals overloads the + / slash character. To avoid ambiguity, + JSLint expects that the character preceding a regular + expression literal is a ( left paren or + = equal or + : colon or + , comma character.

    +

    this

    +
    Having this in the language makes it harder to talk + about the language. It is like pair programming with Abbott and Costello. +
    +

    Avoid using this. Warnings about this can be + suppressed with option.this.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the + new prefix. The new prefix creates a new object + based on the function's prototype, and binds that object to the + function's implied this parameter. If you neglect to use the + new prefix, no new object will be made and this + will be bound to the global object.

    +

    JSLint enforces the convention that constructor functions be + given names with initial uppercase. JSLint does not expect to + see a function invocation with an initial uppercase name unless it has the + new prefix. JSLint does not expect to see the + new prefix used with functions whose names do not start with + initial uppercase.

    +

    JSLint does not expect to see the wrapper forms + new Number, new String, new Boolean.

    +

    JSLint does not expect to see new Object. + Use Object.create(null) instead.

    +

    Whitespace

    +

    JSLint has a specific set of rules about the use of whitespace. + Where possible, these rules are consistent with centuries of good practice + with literary style.

    +

    The indentation increases by 4 spaces when the last token on a line is + { left brace, + [ left bracket, + ( left paren. The matching closing + token will be the first token on a line, restoring the previous + indentation.

    +

    The ternary operator can be visually confusing, so + ? question mark and + : colon always begin a line and increase + the indentation by 4 spaces.

    +
    return (
    +    (the_token.id === "(string)" || the_token.id === "(number)")
    +    ? String(the_token.value)
    +    : the_token.id
    +);
    +

    The word function is always followed with one space.

    +

    Clauses (case, catch, default, + else, finally) are not statements and so should + not be indented like statements.

    +

    Spaces are used to make things that are not invocations look less like + invocations.

    +

    Tabs and spaces should not be mixed. We should pick just one in order to + avoid the problems that come from having both. Personal preference is an + extremely unreliable criterion. Neither offers a powerful advantage over the + other. Fifty years ago, tab had the advantage of consuming less memory, but + Moore's Law has eliminated that advantage. Space has one clear advantage + over tab: there is no reliable standard for how many spaces a tab + represents, but it is universally accepted that a space occupies a space. So + use spaces. You can edit with tabs if you must, but make sure it is spaces + again before you commit. Maybe someday we will finally get a universal + standard for tabs, but until that day comes, the better choice is spaces.

    +

    Report

    +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    +
      +
    • The line number on which it starts.
    • +
    • The function’s name. In the case of anonymous functions, + JSLint will attempt to + «guess» the name.
    • +
    • The parameters.
    • +
    • Variables: The variables that are declared in the function.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Exceptions: The variables that are declared by catch + clauses of try statements.
    • +
    • Outer: Variables used by this function that are declared in + other functions.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements.

    +

    Try it

    +

    Try it. Paste your script + into the window and click the + + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    JSLint is written entirely in JavaScript, so it can run + anywhere that JavaScript (or even Java) can run.

    +

    Perfectly fine

    +

    JSLint was designed to reject code that some would consider to + be perfectly fine. The reason for this is that JSLint's + purpose is to help produce programs that are free of error. That is + difficult in any language and is especially hard in JavaScript. + JSLint attempts to help you increase the visual distance + between correct programs and incorrect programs, making the remaining errors + more obvious. JSLint will give warnings about things that are + not necessarily wrong in the current situation, but which have been observed + to mask or obscure errors. Avoid those things when there are better options + available.

    +

    It is dangerous out there. JSLint is here to help.

    +

    Warning

    +

    JSLint will hurt your feelings. Side effects may include + headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, + dry mouth, cleaner code, and a reduced error rate.

    +

    Further Reading

    + +
    +

    + Code Conventions for the JavaScript Programming Language.

    + + + +
    + + diff --git a/branch.alpha/image-folder-open-solid.svg b/branch.alpha/image-folder-open-solid.svg new file mode 100644 index 000000000..99d403c81 --- /dev/null +++ b/branch.alpha/image-folder-open-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/branch.alpha/image-github-brands.svg b/branch.alpha/image-github-brands.svg new file mode 100644 index 000000000..7870c06dc --- /dev/null +++ b/branch.alpha/image-github-brands.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/branch.alpha/image-jslint-128x128.png b/branch.alpha/image-jslint-128x128.png new file mode 100644 index 000000000..f7340e0b9 Binary files /dev/null and b/branch.alpha/image-jslint-128x128.png differ diff --git a/branch.alpha/image-jslint-256x256.png b/branch.alpha/image-jslint-256x256.png new file mode 100644 index 000000000..1b2c70aba Binary files /dev/null and b/branch.alpha/image-jslint-256x256.png differ diff --git a/branch.alpha/image-jslint-32x32.png b/branch.alpha/image-jslint-32x32.png new file mode 100644 index 000000000..0d32ee109 Binary files /dev/null and b/branch.alpha/image-jslint-32x32.png differ diff --git a/branch.alpha/image-jslint-512x512.png b/branch.alpha/image-jslint-512x512.png new file mode 100644 index 000000000..f1b440efe Binary files /dev/null and b/branch.alpha/image-jslint-512x512.png differ diff --git a/branch.alpha/image-jslint-64x64.png b/branch.alpha/image-jslint-64x64.png new file mode 100644 index 000000000..7545fffbf Binary files /dev/null and b/branch.alpha/image-jslint-64x64.png differ diff --git a/branch.alpha/image-jslint.html b/branch.alpha/image-jslint.html new file mode 100644 index 000000000..ba774fcb6 --- /dev/null +++ b/branch.alpha/image-jslint.html @@ -0,0 +1,52 @@ + + + +logo + + + +
    +
    JS
    +
    Lint
    +
    + + diff --git a/branch.alpha/image-json160.gif b/branch.alpha/image-json160.gif new file mode 100644 index 000000000..3bb55c8dd Binary files /dev/null and b/branch.alpha/image-json160.gif differ diff --git a/branch.alpha/image-window-maximize-regular.svg b/branch.alpha/image-window-maximize-regular.svg new file mode 100644 index 000000000..7b7e5e166 --- /dev/null +++ b/branch.alpha/image-window-maximize-regular.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/branch.alpha/index.html b/branch.alpha/index.html new file mode 100644 index 000000000..3e55d6191 --- /dev/null +++ b/branch.alpha/index.html @@ -0,0 +1,483 @@ + + + + + + + + +JSLint: The JavaScript Code Quality Tool + + + + + +
    +
    Source
    +
    + + +
    +
    + Warnings +
    +
    +
    + Function Report +
    +
    +
    + Property Directive + +
    +
    + + + + +
    +
    Options +
    Assume... +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Global variables... + +
    +
    +
    + + diff --git a/branch.alpha/jslint.js b/branch.alpha/jslint.js new file mode 100755 index 000000000..3ab46e7d1 --- /dev/null +++ b/branch.alpha/jslint.js @@ -0,0 +1,6043 @@ +#!/usr/bin/env node +// jslint.js +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// jslint(source, option_object, global_array) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze, a string or an array of strings. +// option_object An object whose keys correspond to option names. +// global_array An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all of the functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// 1. If the source is a single string, split it into an array of strings. +// 2. Turn the source into an array of tokens. +// 3. Furcate the tokens into a parse tree. +// 4. Walk the tree, traversing all of the nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// 5. Check the whitespace between the tokens. + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*jslint node*/ + +/*property + unordered, + JSLINT_CLI, a, all, and, argv, arity, assign, b, bad_assignment_a, + bad_directive_a, bad_get, bad_module_name_a, bad_option_a, bad_property_a, + bad_set, bitwise, block, body, browser, c, calls, catch, cli_mode, closer, + closure, code, column, concat, console_error, constant, context, convert, + couch, create, d, dead, debug, default, devel, directive, directives, + disrupt, dot, duplicate_a, early_stop, edition, ellipsis, else, empty_block, + env, error, eval, every, exec, exit, expected_a, expected_a_at_b_c, + expected_a_b, expected_a_b_from_c_d, expected_a_before_b, + expected_a_next_at_b, expected_digits_after_a, expected_four_digits, + expected_identifier_a, expected_line_break_a_b, expected_regexp_factor_a, + expected_space_a_b, expected_statements_a, expected_string_a, + expected_type_string_a, exports, expression, extra, file, finally, flag, + for, forEach, formatted_message, free, freeze, freeze_exports, from, froms, + fud, fudge, function_in_loop, functions, g, getset, global, has_await, i, + id, identifier, import, inc, indexOf, infix_in, init, initial, isArray, + isNaN, is_async, join, json, keys, label, label_a, lbp, led, length, level, + line, line_offset, lines, live, long, loop, m, map, margin, match, message, + misplaced_a, misplaced_directive_a, missing_await_statement, + missing_browser, missing_m, module, naked_block, name, names, + nested_comment, node, not_label_a, now, nr, nud, number_isNaN, ok, open, + opening, option, out_of_scope_a, padStart, parameters, parent, pop, + promises, property, push, quote, raw, readFile, readdir, redefinition_a_b, + repeat, replace, required_a_optional_b, reserved_a, role, search, shebang, + signature, single, slice, some, sort, source, split, stack, stack_trace, + startsWith, statement, stop, subscript_a, switch, test, test_internal_error, + then, this, thru, todo_comment, tokens, too_long, too_many_digits, tree, + trim, try, type, u, unclosed_comment, unclosed_mega, unclosed_string, + undeclared_a, unexpected_a, unexpected_a_after_b, unexpected_a_before_b, + unexpected_at_top_level_a, unexpected_char_a, unexpected_comment, + unexpected_directive_a, unexpected_expression_a, unexpected_label_a, + unexpected_parens, unexpected_space_a_b, unexpected_statement_a, + unexpected_trailing_space, unexpected_typeof_a, uninitialized_a, + unordered_param_a, unordered_property_a, unreachable_a, + unregistered_property_a, unused_a, use_double, use_open, use_spaces, used, + value, var_loop, var_switch, variable, versions, warning, warnings, + weird_condition_a, weird_expression_a, weird_loop, weird_relation_a, white, + wrap_condition, wrap_immediate, wrap_parameter, wrap_regexp, wrap_unary, + wrapped, writable, y +*/ + +const edition = "v2021.5.28-beta"; + +function assert_or_throw(passed, message) { + +// this function will throw if is falsy + + if (!passed) { + throw new Error(`This was caused by a bug in JSLint. +Please open an issue with this stack-trace at +https://github.com/jslint-org/jslint/issues. +edition = "${edition}";` + "\n" + message); + } +} + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than '{}' because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +function populate(array, object = empty(), value = true) { + +// Augment an object by taking property names from an array of strings. + + array.forEach(function (name) { + object[name] = value; + }); + return object; +} + +const allowed_option = { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + bitwise: true, + browser: [ + "caches", "CharacterData", "clearInterval", "clearTimeout", "document", + "DocumentType", "DOMException", "Element", "Event", "event", "fetch", + "FileReader", "FontFace", "FormData", "history", "IntersectionObserver", + "localStorage", "location", "MutationObserver", "name", "navigator", + "screen", "sessionStorage", "setInterval", "setTimeout", "Storage", + "TextDecoder", "TextEncoder", "URL", "window", "Worker", + "XMLHttpRequest" + ], + convert: true, + couch: [ + "emit", "getRow", "isArray", "log", "provides", "registerType", + "require", "send", "start", "sum", "toJSON" + ], + debug: true, + devel: [ + "alert", "confirm", "console", "prompt" + ], + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + node: [ + "Buffer", "clearImmediate", "clearInterval", "clearTimeout", + "console", "exports", "module", "process", "require", + "setImmediate", "setInterval", "setTimeout", "TextDecoder", + "TextEncoder", "URL", "URLSearchParams", "__dirname", "__filename" + ], + single: true, + test_internal_error: true, + this: true, + unordered: true, + white: true +}; + +const anticondition = populate([ + "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%", + "typeof", "(number)", "(string)" +]); + +// These are the bitwise operators. + +const bitwiseop = populate([ + "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=", + ">>>", ">>>=" +]); + +const escapeable = populate([ + "\\", "/", "`", "b", "f", "n", "r", "t" +]); + +const opener = { + +// The open and close pairs. + + "${": "}", // mega + "(": ")", // paren + "[": "]", // bracket + "{": "}" // brace +}; + +// The relational operators. + +const relationop = populate([ + "!=", "!==", "==", "===", "<", "<=", ">", ">=" +]); + +// This is the set of infix operators that require a space on each side. + +const spaceop = populate([ + "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/", + "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=", + ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" +]); + +const standard = [ + +// These are the globals that are provided by the language standard. + + "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "Error", "EvalError", + "Float32Array", "Float64Array", "Generator", "GeneratorFunction", + "Int16Array", "Int32Array", "Int8Array", "Intl", "JSON", "Map", "Math", + "Number", "Object", "Promise", "Proxy", "RangeError", "ReferenceError", + "Reflect", "RegExp", "Set", "String", "Symbol", "SyntaxError", "System", + "TypeError", "URIError", "Uint16Array", "Uint32Array", "Uint8Array", + "Uint8ClampedArray", "WeakMap", "WeakSet", "decodeURI", + "decodeURIComponent", "encodeURI", "encodeURIComponent", "globalThis", + "import", "parseFloat", "parseInt" +]; + +const bundle = { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + and: "The '&&' subexpression should be wrapped in parens.", + bad_assignment_a: "Bad assignment to '{a}'.", + bad_directive_a: "Bad directive '{a}'.", + bad_get: "A get function takes no parameters.", + bad_module_name_a: "Bad module name '{a}'.", + bad_option_a: "Bad option '{a}'.", + bad_property_a: "Bad property name '{a}'.", + bad_set: "A set function takes one parameter.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + expected_a: "Expected '{a}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: ( + "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'." + ), + expected_a_before_b: "Expected '{a}' before '{b}'.", + expected_digits_after_a: "Expected digits after '{a}'.", + expected_four_digits: "Expected four digits after '\\u'.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.", + expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.", + expected_space_a_b: "Expected one space between '{a}' and '{b}'.", + expected_statements_a: "Expected statements before '{a}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + expected_type_string_a: "Expected a type string and instead saw '{a}'.", + freeze_exports: ( + "Expected 'Object.freeze('. All export values should be frozen." + ), + function_in_loop: "Don't make functions within a loop.", + infix_in: ( + "Unexpected 'in'. Compare with undefined, " + + "or use the hasOwnProperty method instead." + ), + label_a: "'{a}' is a statement label.", + misplaced_a: "Place '{a}' at the outermost level.", + misplaced_directive_a: ( + "Place the '/*{a}*/' directive before the first statement." + ), + missing_await_statement: "Expected await statement in async function.", + missing_browser: "/*global*/ requires the Assume a browser option.", + missing_m: "Expected 'm' flag on a multiline regular expression.", + naked_block: "Naked block.", + nested_comment: "Nested comment.", + not_label_a: "'{a}' is not a label.", + number_isNaN: "Use Number.isNaN function to compare with NaN.", + out_of_scope_a: "'{a}' is out of scope.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + required_a_optional_b: ( + "Required parameter '{a}' after optional parameter '{b}'." + ), + reserved_a: "Reserved name '{a}'.", + subscript_a: "['{a}'] is better written in dot notation.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line is longer than 80 characters.", + too_many_digits: "Too many digits.", + unclosed_comment: "Unclosed comment.", + unclosed_mega: "Unclosed mega literal.", + unclosed_string: "Unclosed string.", + undeclared_a: "Undeclared '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_a_after_b: "Unexpected '{a}' after '{b}'.", + unexpected_a_before_b: "Unexpected '{a}' before '{b}'.", + unexpected_at_top_level_a: "Expected '{a}' to be in a function.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_directive_a: "When using modules, don't use directive '/*{a}'.", + unexpected_expression_a: ( + "Unexpected expression '{a}' in statement position." + ), + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_parens: "Don't wrap function literals in parens.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_statement_a: ( + "Unexpected statement '{a}' in expression position." + ), + unexpected_trailing_space: "Unexpected trailing space.", + unexpected_typeof_a: ( + "Unexpected 'typeof'. Use '===' to compare directly with {a}." + ), + uninitialized_a: "Uninitialized '{a}'.", + unordered_param_a: ( + "Parameter '{a}' not listed in alphabetical order." + ), + unordered_property_a: ( + "Property name '{a}' not listed in alphabetical order." + ), + unreachable_a: "Unreachable '{a}'.", + unregistered_property_a: "Unregistered property name '{a}'.", + unused_a: "Unused '{a}'.", + use_double: "Use double quotes, not single quotes.", + use_open: ( + "Wrap a ternary expression in parens, " + + "with a line break after the left paren." + ), + use_spaces: "Use spaces, not tabs.", + var_loop: "Don't declare variables in a loop.", + var_switch: "Don't declare variables in a switch.", + weird_condition_a: "Weird condition '{a}'.", + weird_expression_a: "Weird expression '{a}'.", + weird_loop: "Weird loop.", + weird_relation_a: "Weird relation '{a}'.", + wrap_condition: "Wrap the condition in parens.", + wrap_immediate: ( + "Wrap an immediate function invocation in parentheses to assist " + + "the reader in understanding that the expression is the result " + + "of a function, and not the function itself." + ), + wrap_parameter: "Wrap the parameter in parens.", + wrap_regexp: "Wrap this regexp in parens to avoid confusion.", + wrap_unary: "Wrap the unary expression in parens." +}; + +// Regular expression literals: + +function tag_regexp(strings) { + return new RegExp(strings.raw[0].replace(/\s/g, "")); +} + +// supplant {variables} +const rx_supplant = /\{([^{}]*)\}/g; +// carriage return, carriage return linefeed, or linefeed +const rx_crlf = tag_regexp ` + \n + | \r \n? +`; +// identifier +const rx_identifier = tag_regexp ` ^( + [ a-z A-Z _ $ ] + [ a-z A-Z 0-9 _ $ ]* +)$`; +const rx_module = tag_regexp ` ^ [ a-z A-Z 0-9 _ $ : . @ \- \/ ]+ $ `; +const rx_bad_property = tag_regexp ` + ^_ + | \$ + | Sync $ + | _ $ +`; +// star slash +const rx_star_slash = tag_regexp ` \* \/ `; +// slash star +const rx_slash_star = tag_regexp ` \/ \* `; +// slash star or ending slash +const rx_slash_star_or_slash = tag_regexp ` \/ \* | \/ $ `; +// uncompleted work comment +const rx_todo = tag_regexp ` \b (?: + todo + | TO \s? DO + | HACK +) \b `; +// tab +const rx_tab = /\t/g; +// directive +const rx_directive = tag_regexp ` ^ ( + jslint + | property + | global +) \s+ ( .* ) $ `; +const rx_directive_part = tag_regexp ` ^ ( + [ a-z A-Z $ _ ] [ a-z A-Z 0-9 $ _ ]* +) (?: + : \s* ( true | false ) +)? ,? \s* ( .* ) $ `; +// token +const rx_token = tag_regexp ` ^ ( + (\s+) + | ( + [ a-z A-Z _ $ ] + [ a-z A-Z 0-9 _ $ ]* + ) + | [ + ( ) { } \[ \] , : ; ' " ~ \` + ] + | \? [ ? . ]? + | = (?: + = =? + | > + )? + | \.+ + | \* [ * \/ = ]? + | \/ [ * \/ ]? + | \+ [ = + ]? + | - [ = \- ]? + | [ \^ % ] =? + | & [ & = ]? + | \| [ | = ]? + | >{1,3} =? + | < = "a" && string <= "z\uffff") + || (string >= "A" && string <= "Z\uffff") + ); +} + +let anon; // The guessed name for anonymous functions. +let blockage; // The current block. +let block_stack; // The stack of blocks. +let declared_globals; // The object containing the global declarations. +let directives; // The directive comments. +let directive_mode; // true if directives are still allowed. +let early_stop; // true if JSLint cannot finish. +let exports; // The exported names and values. +let froms; // The array collecting all import-from strings. +let fudge; // true if the natural numbers start with 1. +let functionage; // The current function. +let functions; // The array containing all of the functions. +let global; // The global object; the outermost context. +let json_mode; // true if parsing JSON. +let lines; // The array containing source lines. +let mega_mode; // true if currently parsing a megastring literal. +let module_mode; // true if import or export was used. +let next_token; // The next token to be examined in the parse. +let option; // The options parameter. +let property; // The object containing the tallied property names. +let shebang; // true if a #! was seen on the first line. +let stack; // The stack of functions. +let syntax; // The object containing the parser. +let token; // The current token being examined in the parse. +let token_nr; // The number of the next token. +let tokens; // The array of tokens. +let tenure; // The predefined property registry. +let tree; // The abstract parse tree. +let var_mode; // "var" if using var; "let" if using let. +let warnings; // The array collecting all generated warnings. + +// Error reportage functions: + +function artifact(the_token) { + +// Return a string representing an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); +} + +function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + const warning = { // ~~ + code, + column, + line, + name: "JSLintError" + }; + if (a !== undefined) { + warning.a = a; + } + if (b !== undefined) { + warning.b = b; + } + if (c !== undefined) { + warning.c = c; + } + if (d !== undefined) { + warning.d = d; + } + warning.message = bundle[code].replace(rx_supplant, function ( + ignore, + filling + ) { + assert_or_throw( + warning[filling] !== undefined, + "Expected warning[filling] !== undefined." + ); + return warning[filling]; + }); + +// Include stack_trace for jslint to debug itself for errors. + + if (option.debug) { + warning.stack_trace = new Error().stack; + } + warnings.push(warning); + return warning; +} + +function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); +} + +function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + if (the_token.warning === undefined) { + the_token.warning = warn_at( + code, + the_token.line, + the_token.from, + a || artifact(the_token), + b, + c, + d + ); + return the_token.warning; + } +} + +function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); +} + +// Tokenize: + +function tokenize(source) { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + +// If the source is not an array, then it is split into lines at the +// carriage return/linefeed. + + lines = ( + Array.isArray(source) + ? source + : source.split(rx_crlf) + ); + tokens = []; + + let char; // a popular character + let column = 0; // the column number of the next character + let first; // the first token + let from; // the starting column number of the token + let line = -1; // the line number of the next character + let nr = 0; // the next token number + let previous = global; // the previous token including comments + let prior = global; // the previous token excluding comments + let mega_from; // the starting column of megastring + let mega_line; // the starting line of megastring + let regexp_seen; // regular expression literal seen on this line + let snippet = ""; // a piece of string + let source_line = ""; // the remaining line source string + let whole_line = ""; // the whole line source string + + if (lines[0].startsWith("#!")) { + line = 0; + shebang = true; + } + + function next_line() { + +// Put the next line of source in source_line. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + let at; + if ( + !option.long + && whole_line.length > 80 + && !json_mode + && first + && !regexp_seen + ) { + warn_at("too_long", line, 80); + } + column = 0; + line += 1; + regexp_seen = false; + source_line = lines[line]; + whole_line = source_line || ""; + if (source_line !== undefined) { + at = source_line.search(rx_tab); + if (at >= 0) { + if (!option.white) { + +// cause: "\t" + + warn_at("use_spaces", line, at + 1); + } + source_line = source_line.replace(rx_tab, " "); + } + if (!option.white && source_line.slice(-1) === " ") { + warn_at( + "unexpected_trailing_space", + line, + source_line.length - 1 + ); + } + } + return source_line; + } + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions snip, next_char, back_char, +// some_digits, and escape help in the parsing of literals. + + function snip() { + +// Remove the last character from snippet. + + snippet = snippet.slice(0, -1); + } + + function next_char(match) { + +// Get the next character from the source line. Remove it from the source_line, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + return stop_at( + ( + char === "" + ? "expected_a" + : "expected_a_b" + ), + line, + column - 1, + match, + char + ); + } + if (source_line) { + char = source_line[0]; + source_line = source_line.slice(1); + snippet += char; + } else { + char = ""; + snippet += " "; + } + column += 1; + return char; + } + + function back_char() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the source_line. + + char = snippet.slice(-1); + source_line = char + source_line; + column -= char.length; + snip(); + return char; + } + + function some_digits(rx, quiet) { + const digits = source_line.match(rx)[0]; + const length = digits.length; + if (!quiet && length === 0) { + +// cause: "0x" + + warn_at("expected_digits_after_a", line, column, snippet); + } + column += length; + source_line = source_line.slice(length); + snippet += digits; + next_char(); + return length; + } + + function escape(extra) { + next_char("\\"); + if (escapeable[char] === true) { + return next_char(); + } + if (char === "") { + +// cause: "\"\\" + + return stop_at("unclosed_string", line, column); + } + if (char === "u") { + if (next_char("u") === "{") { + if (json_mode) { + +// cause: "[\"\\u{12345}\"]" + + warn_at("unexpected_a", line, column - 1, char); + } + if (some_digits(rx_hexs) > 5) { + +// cause: "\"\\u{123456}\"" + + warn_at("too_many_digits", line, column - 1); + } + if (char !== "}") { + +// cause: "\"\\u{12345\"" + + stop_at("expected_a_before_b", line, column, "}", char); + } + return next_char(); + } + back_char(); + if (some_digits(rx_hexs, true) < 4) { + +// cause: "\"\\u0\"" + + warn_at("expected_four_digits", line, column - 1); + } + return; + } + if (extra && extra.indexOf(char) >= 0) { + return next_char(); + } + +// cause: "\"\\a\"" + + warn_at("unexpected_a_before_b", line, column - 2, "\\", char); + } + + function make(id, value, identifier) { + +// Make the token object and append it to the tokens list. + + const the_token = { + from, + id, + identifier: Boolean(identifier), + line, + nr, + thru: column + }; + tokens[nr] = the_token; + nr += 1; + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + directive_mode = false; + } + +// If the token is to have a value, give it one. + + if (value !== undefined) { + the_token.value = value; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option.white. + + if ( + previous.line === line + && previous.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (previous.id === "(comment)" || previous.id === "(regexp)") + ) { + +// cause: "/**//**/" + + warn( + "expected_space_a_b", + the_token, + artifact(previous), + artifact(the_token) + ); + } + if (previous.id === "." && id === "(number)") { + +// cause: ".0" + + warn("expected_a_before_b", previous, "0", "."); + } + if (prior.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// The previous token is used to detect adjacency problems. + + previous = the_token; + +// The prior token is a previous token that was not a comment. The prior token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (previous.id !== "(comment)") { + prior = previous; + } + return the_token; + } + + function parse_directive(the_comment, body) { + +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + + const result = body.match(rx_directive_part); + if (result) { + let allowed; + const name = result[1]; + const value = result[2]; + if (the_comment.directive === "jslint") { + allowed = allowed_option[name]; + if ( + typeof allowed === "boolean" + || typeof allowed === "object" + ) { + if (value === "true" || value === undefined) { + option[name] = true; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } else if (value === "false") { + option[name] = false; + } + } else { + +// cause: "/*jslint undefined*/" + + warn("bad_option_a", the_comment, name); + } + } else if (the_comment.directive === "property") { + if (tenure === undefined) { + tenure = empty(); + } + tenure[name] = true; + } else if (the_comment.directive === "global") { + if (value) { + +// cause: "/*global aa:false*/" + + warn("bad_option_a", the_comment, name + ":" + value); + } + declared_globals[name] = false; + module_mode = the_comment; + } + return parse_directive(the_comment, result[3]); + } + if (body) { + +// cause: "/*jslint !*/" + + return stop("bad_directive_a", the_comment, body); + } + } + + function comment(snippet) { + +// Make a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + + const the_comment = make("(comment)", snippet); + if (Array.isArray(snippet)) { + snippet = snippet.join(" "); + } + if (!option.devel && rx_todo.test(snippet)) { + +// cause: "//\u0074odo" + + warn("todo_comment", the_comment); + } + const result = snippet.match(rx_directive); + if (result) { + if (!directive_mode) { + +// cause: "0\n/*global aa*/" + + warn_at("misplaced_directive_a", line, from, result[1]); + } else { + the_comment.directive = result[1]; + parse_directive(the_comment, result[2]); + } + directives.push(the_comment); + } + return the_comment; + } + + function regexp() { + +// Parse a regular expression literal. + + let multi_mode = false; + let result; + let value; + regexp_seen = true; + + function quantifier() { + +// Match an optional quantifier. + + if (char === "?" || char === "*" || char === "+") { + next_char(); + } else if (char === "{") { + if (some_digits(rx_digits, true) === 0) { + warn_at("expected_a_before_b", line, column, "0", ","); + } + if (char === ",") { + some_digits(rx_digits, true); + } + next_char("}"); + } else { + return; + } + if (char === "?") { + next_char("?"); + } + } + + function subklass() { + +// Match a character in a character class. + + if (char === "\\") { + escape("BbDdSsWw-[]^"); + return true; + } + if ( + char === "" + || char === "[" + || char === "]" + || char === "/" + || char === "^" + || char === "-" + ) { + return false; + } + if (char === " ") { + warn_at("expected_a_b", line, column, "\\u0020", " "); + } else if (char === "`" && mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char(); + return true; + } + + function ranges() { + +// Match a range of subclasses. + + if (subklass()) { + if (char === "-") { + next_char("-"); + if (!subklass()) { + return stop_at( + "unexpected_a", + line, + column - 1, + "-" + ); + } + } + return ranges(); + } + } + + function klass() { + +// Match a class. + + next_char("["); + if (char === "^") { + next_char("^"); + } + (function classy() { + ranges(); + if (char !== "]" && char !== "") { + warn_at( + "expected_a_before_b", + line, + column, + "\\", + char + ); + next_char(); + return classy(); + } + }()); + next_char("]"); + } + + function choice() { + + function group() { + +// Match a group that starts with left paren. + + next_char("("); + if (char === "?") { + next_char("?"); + if (char === "=" || char === "!") { + next_char(); + } else { + next_char(":"); + } + } else if (char === ":") { + warn_at("expected_a_before_b", line, column, "?", ":"); + } + choice(); + next_char(")"); + } + + function factor() { + +// Parse current character in regexp. + + if ( + char === "" + || char === "/" + || char === "]" + || char === ")" + ) { + return false; + } + if (char === "(") { + group(); + return true; + } + if (char === "[") { + klass(); + return true; + } + if (char === "\\") { + escape("BbDdSsWw^${}[]():=!.|*+?"); + return true; + } + if ( + char === "?" + || char === "+" + || char === "*" + || char === "}" + || char === "{" + ) { + warn_at( + "expected_a_before_b", + line, + column - 1, + "\\", + char + ); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column - 1, "`"); + } + } else if (char === " ") { + warn_at( + "expected_a_b", + line, + column - 1, + "\\s", + " " + ); + } else if (char === "$") { + if (source_line[0] !== "/") { + multi_mode = true; + } + } else if (char === "^") { + if (snippet !== "^") { + multi_mode = true; + } + } + next_char(); + return true; + } + + function sequence(follow) { + if (factor()) { + quantifier(); + return sequence(true); + } + if (!follow) { + +// cause: "/ /" + + warn_at("expected_regexp_factor_a", line, column, char); + } + } + +// Match a choice (a sequence that can be followed by | and another choice). + + sequence(); + if (char === "|") { + next_char("|"); + return choice(); + } + } + +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + next_char(); + if (char === "=") { + warn_at("expected_a_before_b", line, column, "\\", "="); + } + choice(); + +// Make sure there is a closing slash. + + snip(); + value = snippet; + next_char("/"); + +// Process dangling flag letters. + + const allowed = { + g: true, + i: true, + m: true, + u: true, + y: true + }; + const flag = empty(); + (function make_flag() { + if (is_letter(char)) { + if (allowed[char] !== true) { + warn_at("unexpected_a", line, column, char); + } + allowed[char] = false; + flag[char] = true; + next_char(); + return make_flag(); + } + }()); + back_char(); + if (char === "/" || char === "*") { + return stop_at("unexpected_a", line, from, char); + } + result = make("(regexp)", char); + result.flag = flag; + result.value = value; + if (multi_mode && !flag.m) { + +// cause: "aa=/$^/" + + warn_at("missing_m", line, column); + } + return result; + } + + function string(quote) { + +// Make a string token. + + let the_token; + snippet = ""; + next_char(); + + return (function next() { + if (char === quote) { + snip(); + the_token = make("(string)", snippet); + the_token.quote = quote; + return the_token; + } + if (char === "") { + +// cause: "\"" + + return stop_at("unclosed_string", line, column); + } + if (char === "\\") { + escape(quote); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char("`"); + } else { + next_char(); + } + return next(); + }()); + } + + function frack() { + if (char === ".") { + some_digits(rx_digits); + } + if (char === "E" || char === "e") { + next_char(); + if (char !== "+" && char !== "-") { + back_char(); + } + some_digits(rx_digits); + } + } + + function number() { + if (snippet === "0") { + next_char(); + if (char === ".") { + frack(); + } else if (char === "b") { + some_digits(rx_bits); + } else if (char === "o") { + some_digits(rx_octals); + } else if (char === "x") { + some_digits(rx_hexs); + } + } else { + next_char(); + frack(); + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + +// cause: "0a" + + return stop_at( + "unexpected_a_after_b", + line, + column - 1, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + back_char(); + return make("(number)", snippet); + } + + function lex() { + let array; + let i = 0; + let j = 0; + let last; + let result; + let the_token; + +// This should properly be a tail recursive function, but sadly, conformant +// implementations of ES6 are still rare. This is the ideal code: + +// if (!source_line) { +// source_line = next_line(); +// from = 0; +// return ( +// source_line === undefined +// ? ( +// mega_mode +// ? stop_at("unclosed_mega", mega_line, mega_from) +// : make("(end)") +// ) +// : lex() +// ); +// } + +// Unfortunately, incompetent JavaScript engines will sometimes fail to execute +// it correctly. So for now, we do it the old fashioned way. + + while (!source_line) { + source_line = next_line(); + from = 0; + if (source_line === undefined) { + return ( + mega_mode + ? stop_at("unclosed_mega", mega_line, mega_from) + : make("(end)") + ); + } + } + + from = column; + result = source_line.match(rx_token); + +// result[1] token +// result[2] whitespace +// result[3] identifier +// result[4] number +// result[5] rest + + if (!result) { + +// cause: "#" + + return stop_at( + "unexpected_char_a", + line, + column, + source_line[0] + ); + } + + snippet = result[1]; + column += snippet.length; + source_line = result[5]; + +// Whitespace was matched. Call lex again to get more. + + if (result[2]) { + return lex(); + } + +// The token is an identifier. + + if (result[3]) { + return make(snippet, undefined, true); + } + +// The token is a number. + + if (result[4]) { + return number(snippet); + } + +// The token is a string. + + if (snippet === "\"") { + return string(snippet); + } + if (snippet === "'") { + if (!option.single) { + +// cause: "''" + + warn_at("use_double", line, column); + } + return string(snippet); + } + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (snippet === "`") { + if (mega_mode) { + return stop_at("expected_a_b", line, column, "}", "`"); + } + snippet = ""; + mega_from = from; + mega_line = line; + mega_mode = true; + +// Parsing a mega literal is tricky. First make a ` token. + + make("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + (function part() { + const at = source_line.search(rx_mega); + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + if (at < 0) { + snippet += source_line + "\n"; + return ( + next_line() === undefined + +// cause: "`" + + ? stop_at("unclosed_mega", mega_line, mega_from) + : part() + ); + } + snippet += source_line.slice(0, at); + column += at; + source_line = source_line.slice(at); + if (source_line[0] === "\\") { + snippet += source_line.slice(0, 2); + source_line = source_line.slice(2); + column += 2; + return part(); + } + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + make("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then make tokens that will become part of an expression until +// a } token is made. + + if (source_line[0] === "$") { + column += 2; + make("${"); + source_line = source_line.slice(2); + (function expr() { + const id = lex().id; + if (id === "{") { + return stop_at( + "expected_a_b", + line, + column, + "}", + "{" + ); + } + if (id !== "}") { + return expr(); + } + }()); + return part(); + } + }()); + source_line = source_line.slice(1); + column += 1; + mega_mode = false; + return make("`"); + } + +// The token is a // comment. + + if (snippet === "//") { + snippet = source_line; + source_line = ""; + the_token = comment(snippet); + if (mega_mode) { + +// cause: "`${//}`" + + warn("unexpected_comment", the_token, "`"); + } + return the_token; + } + +// The token is a /* comment. + + if (snippet === "/*") { + array = []; + if (source_line[0] === "/") { + warn_at("unexpected_a", line, column + i, "/"); + } + (function next() { + if (source_line > "") { + i = source_line.search(rx_star_slash); + if (i >= 0) { + return; + } + j = source_line.search(rx_slash_star); + if (j >= 0) { + +// cause: "/*/*" + + warn_at("nested_comment", line, column + j); + } + } + array.push(source_line); + source_line = next_line(); + if (source_line === undefined) { + +// cause: "/*" + + return stop_at("unclosed_comment", line, column); + } + return next(); + }()); + snippet = source_line.slice(0, i); + j = snippet.search(rx_slash_star_or_slash); + if (j >= 0) { + +// cause: "/*/**/" + + warn_at("nested_comment", line, column + j); + } + array.push(snippet); + column += i + 2; + source_line = source_line.slice(i + 2); + return comment(array); + } + +// The token is a slash. + + if (snippet === "/") { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + if (prior.identifier) { + if (!prior.dot) { + if (prior.id === "return") { + return regexp(); + } + if ( + prior.id === "(begin)" + || prior.id === "case" + || prior.id === "delete" + || prior.id === "in" + || prior.id === "instanceof" + || prior.id === "new" + || prior.id === "typeof" + || prior.id === "void" + || prior.id === "yield" + ) { + the_token = regexp(); + +// cause: "/./" +// cause: "case /./" +// cause: "delete /./" +// cause: "in /./" +// cause: "instanceof /./" +// cause: "new /./" +// cause: "typeof /./" +// cause: "void /./" +// cause: "yield /./" + + return stop("unexpected_a", the_token); + } + } + } else { + last = prior.id[prior.id.length - 1]; + if ("(,=:?[".indexOf(last) >= 0) { + return regexp(); + } + if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) { + the_token = regexp(); + +// cause: "!/./" + + warn("wrap_regexp", the_token); + return the_token; + } + } + if (source_line[0] === "=") { + column += 1; + source_line = source_line.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + } + return make(snippet); + } + + first = lex(); + json_mode = first.id === "{" || first.id === "["; + +// This loop will be replaced with a recursive call to lex when ES6 has been +// finished and widely deployed and adopted. + + while (true) { + if (lex().id === "(end)") { + break; + } + } +} + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + +function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!rx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!rx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + +// cause: "let aa={0:0}" + + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property[id] === "number") { + property[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (tenure !== undefined) { + if (tenure[id] !== true) { + +// cause: "/*property aa*/\naa.bb" + + warn("unregistered_property_a", name); + } + } else { + if (name.identifier && rx_bad_property.test(id)) { + +// cause: "aa._" + + warn("bad_property_a", name); + } + } + property[id] = 1; + } + return id; +} + +function dispense() { + +// Deliver the next token, skipping the comments. + + const cadet = tokens[token_nr]; + token_nr += 1; + if (cadet.id === "(comment)") { + if (json_mode) { + warn("unexpected_a", cadet); + } + return dispense(); + } else { + return cadet; + } +} + +function lookahead() { + +// Look ahead one token without advancing. + + const old_token_nr = token_nr; + const cadet = dispense(true); + token_nr = old_token_nr; + return cadet; +} + +function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if (token.identifier && token.id !== "function") { + anon = token.id; + } else if (token.id === "(string)" && rx_identifier.test(token.value)) { + anon = token.value; + } + +// Attempt to match next_token with an expected id. + + if (id !== undefined && next_token.id !== id) { + return ( + match === undefined + +// cause: "()" + + ? stop("expected_a_b", next_token, id, artifact()) + +// cause: "{\"aa\":0" + + : stop( + "expected_a_b_from_c_d", + next_token, + id, + artifact(match), + +// Return the fudged line number of an artifact. + + match.line + fudge, + artifact(next_token) + ) + ); + } + +// Promote the tokens, skipping comments. + + token = next_token; + next_token = dispense(); + if (next_token.id === "(end)") { + token_nr -= 1; + } +} + +// Parsing of JSON is simple: + +function json_value() { + let negative; + if (next_token.id === "{") { + return (function json_object() { + const brace = next_token; + const object = empty(); + const properties = []; + brace.expression = properties; + advance("{"); + if (next_token.id !== "}") { + (function next() { + let name; + let value; + if (next_token.quote !== "\"") { + warn( + "unexpected_a", + next_token, + next_token.quote + ); + } + name = next_token; + advance("(string)"); + if (object[token.value] !== undefined) { + +// cause: "{\"aa\":0,\"aa\":0}" + + warn("duplicate_a", token); + } else if (token.value === "__proto__") { + +// cause: "{\"__proto__\":0}" + + warn("bad_property_a", token); + } else { + object[token.value] = token; + } + advance(":"); + value = json_value(); + value.label = name; + properties.push(value); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("}", brace); + return brace; + }()); + } + if (next_token.id === "[") { + return (function json_array() { + const bracket = next_token; + const elements = []; + bracket.expression = elements; + advance("["); + if (next_token.id !== "]") { + (function next() { + elements.push(json_value()); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]", bracket); + return bracket; + }()); + } + if ( + next_token.id === "true" + || next_token.id === "false" + || next_token.id === "null" + ) { + advance(); + return token; + } + if (next_token.id === "(number)") { + if (!rx_JSON_number.test(next_token.value)) { + warn("unexpected_a"); + } + advance(); + return token; + } + if (next_token.id === "(string)") { + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + advance(); + return token; + } + if (next_token.id === "-") { + negative = next_token; + negative.arity = "unary"; + advance("-"); + advance("(number)"); + if (!rx_JSON_number.test(token.value)) { + warn("unexpected_a", token); + } + negative.expression = token; + return negative; + } + stop("unexpected_a"); +} + +// Now we parse JavaScript. + +function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + const id = name.id; + +// Reserved words may not be enrolled. + + if (syntax[id] !== undefined && id !== "ignore") { + +// cause: "let undefined" + + warn("reserved_a", name); + } else { + +// Has the name been enrolled in this context? + + let earlier = functionage.context[id]; + if (earlier) { + +// cause: "let aa;let aa" + + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + +// Has the name been enrolled in an outer context? + + } else { + stack.forEach(function (value) { + const item = value.context[id]; + if (item !== undefined) { + earlier = item; + } + }); + if (earlier) { + if (id === "ignore") { + if (earlier.role === "variable") { + +// cause: "let ignore;function aa(ignore) {}" + + warn("unexpected_a", name); + } + } else { + if ( + ( + role !== "exception" + || earlier.role !== "exception" + ) + && role !== "parameter" + && role !== "function" + ) { + +// cause: "function aa(){var aa;}" +// cause: "function aa(){try{aa();}catch(aa){aa();}}" + + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + } + } + } + +// Enroll it. + + functionage.context[id] = name; + name.dead = true; + name.parent = functionage; + name.init = false; + name.role = role; + name.used = 0; + name.writable = !readonly; + } + } +} + +function expression(rbp, initial) { + +// This is the heart of the Pratt parser. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// nud Null denotation +// led Left denotation +// lbp Left binding power +// rbp Right binding power + +// It processes a nud (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop. It +// returns the expression's parse tree. + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement, + + if (!initial) { + advance(); + } + the_symbol = syntax[token.id]; + if (the_symbol !== undefined && the_symbol.nud !== undefined) { + left = the_symbol.nud(); + } else if (token.identifier) { + left = token; + left.arity = "variable"; + } else { + +// cause: "!" + + return stop("unexpected_a", token); + } + (function right() { + the_symbol = syntax[next_token.id]; + if ( + the_symbol !== undefined + && the_symbol.led !== undefined + && rbp < the_symbol.lbp + ) { + advance(); + left = the_symbol.led(left); + return right(); + } + }()); + return left; +} + +function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = next_token; + let the_value; + +// cause: "do{}while()" +// cause: "if(){}" +// cause: "while(){}" + + the_paren.free = true; + advance("("); + the_value = expression(0); + advance(")"); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (anticondition[the_value.id] === true) { + warn("unexpected_a", the_value); + } + return the_value; +} + +function is_weird(thing) { + return ( + thing.id === "(regexp)" + || thing.id === "{" + || thing.id === "=>" + || thing.id === "function" + || (thing.id === "[" && thing.arity === "unary") + ); +} + +function are_similar(a, b) { + +// cause: "0&&0" + + assert_or_throw(a !== b, "Expected a !== b."); +// Probably deadcode. +// if (a === b) { +// return true; +// } + if (Array.isArray(a)) { + return ( + Array.isArray(b) + && a.length === b.length + && a.every(function (value, index) { + +// cause: "`${0}`&&`${0}`" + + return are_similar(value, b[index]); + }) + ); + } + assert_or_throw(!Array.isArray(b), "Expected !Array.isArray(b)."); +// Probably deadcode. +// if (Array.isArray(b)) { +// return false; +// } + if (a.id === "(number)" && b.id === "(number)") { + return a.value === b.value; + } + let a_string; + let b_string; + if (a.id === "(string)") { + a_string = a.value; + } else if (a.id === "`" && a.constant) { + a_string = a.value[0]; + } + if (b.id === "(string)") { + b_string = b.value; + } else if (b.id === "`" && b.constant) { + b_string = b.value[0]; + } + if (typeof a_string === "string") { + return a_string === b_string; + } + if (is_weird(a) || is_weird(b)) { + return false; + } + if (a.arity === b.arity && a.id === b.id) { + +// cause: "aa.bb&&aa.bb" + + if (a.id === ".") { + return ( + are_similar(a.expression, b.expression) + && are_similar(a.name, b.name) + ); + } + +// cause: "+0&&+0" + + if (a.arity === "unary") { + return are_similar(a.expression, b.expression); + } + if (a.arity === "binary") { + +// cause: "aa[0]&&aa[0]" + + return ( + a.id !== "(" + && are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + ); + } + if (a.arity === "ternary") { + +// cause: "aa=(``?``:``)&&(``?``:``)" + + return ( + are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + && are_similar(a.expression[2], b.expression[2]) + ); + } + assert_or_throw( + a.arity !== "function" && a.arity !== "regexp", + "Expected a.arity !== \"function\" && a.arity !== \"regexp\"." + ); +// Probably deadcode. +// if (a.arity === "function" && a.arity === "regexp") { +// return false; +// } + +// cause: "undefined&&undefined" + + return true; + } + +// cause: "null&&undefined" + + return false; +} + +function semicolon() { + +// Try to match a semicolon. + + if (next_token.id === ";") { + advance(";"); + } else { + +// cause: "0" +// cause: "0\n0" + + warn_at( + "expected_a_b", + token.line, + token.thru, + ";", + artifact(next_token) + ); + } + anon = "anonymous"; +} + +function statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token.identifier && next_token.id === ":") { + the_label = token; + if (the_label.id === "ignore") { + warn("unexpected_a", the_label); + } + advance(":"); + if ( + next_token.id === "do" + || next_token.id === "for" + || next_token.id === "switch" + || next_token.id === "while" + ) { + enroll(the_label, "label", true); + the_label.init = true; + the_label.dead = false; + the_statement = statement(); + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + +// cause: "aa:" + + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token; + first.statement = true; + the_symbol = syntax[first.id]; + if (the_symbol !== undefined && the_symbol.fud !== undefined) { + the_symbol.disrupt = false; + the_symbol.statement = true; + the_statement = the_symbol.fud(); + } else { + +// It is an expression statement. + + the_statement = expression(0, true); + if (the_statement.wrapped && the_statement.id !== "(") { + +// cause: "(0)" + + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; +} + +function statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const array = []; + (function next(disrupt) { + if ( + next_token.id !== "}" + && next_token.id !== "case" + && next_token.id !== "default" + && next_token.id !== "else" + && next_token.id !== "(end)" + ) { + let a_statement = statement(); + array.push(a_statement); + if (disrupt) { + +// cause: "while(0){break;0;}" + + warn("unreachable_a", a_statement); + } + return next(a_statement.disrupt); + } + }(false)); + return array; +} + +function not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === global) { + warn("unexpected_at_top_level_a", thing); + } +} + +function top_level_only(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== global) { + +// cause: "if(0){import aa from \"aa\";}" + + warn("misplaced_a", the_thing); + } +} + +function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token; + the_block.arity = "statement"; + the_block.body = special === "body"; + +// Top level function bodies may include the "use strict" pragma. + + if ( + special === "body" + && stack.length === 1 + && next_token.value === "use strict" + ) { + next_token.statement = true; + advance("(string)"); + advance(";"); + } + stmts = statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option.devel && special !== "ignore") { + +// cause: "function aa(){}" + + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; +} + +function mutation_check(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + +// cause: "0=0" + + warn("bad_assignment_a", the_thing); + return false; + } + return true; +} + +function left_check(left, right) { + +// Warn if the left is not one of these: +// e.b +// e[b] +// e() +// ?: +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !left_check(left.expression[1]) + && !left_check(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "?." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right); + return false; + } + return true; +} + +// These functions are used to specify the grammar of our language: + +function symbol(id, bp) { + +// Make a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax[id] = the_symbol; + } + return the_symbol; +} + +function assignment(id) { + +// Make an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led = function (left) { + const the_token = token; + let right; + the_token.arity = "assignment"; + right = expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "pre" + || right.arity === "post" + ) { + warn("unexpected_a", right); + } + mutation_check(left); + return the_token; + }; + return the_symbol; +} + +function constant(id, type, value) { + +// Make a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud = ( + typeof value === "function" + ? value + : function () { + token.constant = true; + if (value !== undefined) { + token.value = value; + } + return token; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; +} + +function infix(id, bp, f) { + +// Make an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, expression(bp)]; + return the_token; + }; + return the_symbol; +} + +function infixr(id, bp) { + +// Make a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + +// cause: "0**0" + + const the_token = token; + the_token.arity = "binary"; + the_token.expression = [left, expression(bp - 1)]; + return the_token; + }; + return the_symbol; +} + +function post(id) { + +// Make one of the post operators. + + const the_symbol = symbol(id, 150); + the_symbol.led = function (left) { + token.expression = left; + token.arity = "post"; + mutation_check(token.expression); + return token; + }; + return the_symbol; +} + +function pre(id) { + +// Make one of the pre operators. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "pre"; + the_token.expression = expression(150); + mutation_check(the_token.expression); + return the_token; + }; + return the_symbol; +} + +function prefix(id, f) { + +// Make a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = expression(150); + return the_token; + }; + return the_symbol; +} + +function stmt(id, f) { + +// Make a statement. + + const the_symbol = symbol(id); + the_symbol.fud = function () { + token.arity = "statement"; + return f(); + }; + return the_symbol; +} + +function ternary(id1, id2) { + +// Make a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led = function (left) { + const the_token = token; + const second = expression(20); + advance(id2); + token.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, expression(10)]; + if (next_token.id !== ")") { + +// cause: "0?0:0" + + warn("use_open", the_token); + } + return the_token; + }; + return the_symbol; +} + +// Begin defining the language. + +syntax = empty(); + +symbol("}"); +symbol(")"); +symbol("]"); +symbol(","); +symbol(";"); +symbol(":"); +symbol("*/"); +symbol("async"); +symbol("await"); +symbol("case"); +symbol("catch"); +symbol("class"); +symbol("default"); +symbol("else"); +symbol("enum"); +symbol("finally"); +symbol("implements"); +symbol("interface"); +symbol("package"); +symbol("private"); +symbol("protected"); +symbol("public"); +symbol("static"); +symbol("super"); +symbol("void"); +symbol("yield"); + +constant("(number)", "number"); +constant("(regexp)", "regexp"); +constant("(string)", "string"); +constant("arguments", "object", function () { + warn("unexpected_a", token); + return token; +}); +constant("eval", "function", function () { + if (!option.eval) { + +// cause: "eval()" + + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + +// cause: "/*jslint eval*/\neval" + + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("false", "boolean", false); +constant("Function", "function", function () { + if (!option.eval) { + +// cause: "Function()" + + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + +// cause: "/*jslint eval*/\nFunction" + + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("ignore", "undefined", function () { + warn("unexpected_a", token); + return token; +}); +constant("Infinity", "number", Infinity); +constant("isFinite", "function", function () { + warn("expected_a_b", token, "Number.isFinite", "isFinite"); + return token; +}); +constant("isNaN", "function", function () { + +// cause: "isNaN(0)" + + warn("number_isNaN", token); + return token; +}); +constant("NaN", "number", NaN); +constant("null", "null", null); +constant("this", "object", function () { + if (!option.this) { + warn("unexpected_a", token); + } + return token; +}); +constant("true", "boolean", true); +constant("undefined", "undefined"); + +assignment("="); +assignment("+="); +assignment("-="); +assignment("*="); +assignment("/="); +assignment("%="); +assignment("&="); +assignment("|="); +assignment("^="); +assignment("<<="); +assignment(">>="); +assignment(">>>="); + +infix("??", 35); +infix("||", 40); +infix("&&", 50); +infix("|", 70); +infix("^", 80); +infix("&", 90); +infix("==", 100); +infix("===", 100); +infix("!=", 100); +infix("!==", 100); +infix("<", 110); +infix(">", 110); +infix("<=", 110); +infix(">=", 110); +infix("in", 110); +infix("instanceof", 110); +infix("<<", 120); +infix(">>", 120); +infix(">>>", 120); +infix("+", 130); +infix("-", 130); +infix("*", 140); +infix("/", 140); +infix("%", 140); +infixr("**", 150); +infix("(", 160, function (left) { + const the_paren = token; + let the_argument; + if (left.id !== "function") { + left_check(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (next_token.id !== ")") { + (function next() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + +// cause: "aa(0)" + + the_paren.free = true; + if (the_argument.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + +// cause: "aa()" +// cause: "aa(0,0)" + + the_paren.free = false; + } + return the_paren; +}); +infix(".", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + +// cause: "aa.0" + + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("?.", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + +// cause: "(0+0)?.0" + + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + +// cause: "aa?.0" + + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("[", 170, function (left) { + const the_token = token; + const the_subscript = expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + const name = survey(the_subscript); + if (rx_identifier.test(name)) { + +// cause: "aa[`aa`]" + + warn("subscript_a", the_subscript, name); + } + } + left_check(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; +}); +infix("=>", 170, function (left) { + +// cause: "aa=>0" + + return stop("wrap_parameter", left); +}); + +function do_tick() { + const the_tick = token; + the_tick.value = []; + the_tick.expression = []; + if (next_token.id !== "`") { + (function part() { + advance("(string)"); + the_tick.value.push(token); + if (next_token.id === "${") { + advance("${"); + the_tick.expression.push(expression(0)); + advance("}"); + return part(); + } + }()); + } + advance("`"); + return the_tick; +} + +infix("`", 160, function (left) { + const the_tick = do_tick(); + left_check(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; +}); + +post("++"); +post("--"); +pre("++"); +pre("--"); + +prefix("+"); +prefix("-"); +prefix("~"); +prefix("!"); +prefix("!!"); +prefix("[", function () { + const the_token = token; + the_token.expression = []; + if (next_token.id !== "]") { + (function next() { + let element; + let ellipsis = false; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + element = expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]"); + return the_token; +}); +prefix("/=", function () { + +// cause: "/=" + + stop("expected_a_b", token, "/\\=", "/="); +}); +prefix("=>", function () { + +// cause: "=>0" + + return stop("expected_a_before_b", token, "()", "=>"); +}); +prefix("new", function () { + const the_new = token; + const right = expression(160); + if (next_token.id !== "(") { + +// cause: "new aa" + + warn("expected_a_before_b", next_token, "()", artifact(next_token)); + } + the_new.expression = right; + return the_new; +}); +prefix("typeof"); +prefix("void", function () { + const the_void = token; + +// cause: "void" + + warn("unexpected_a", the_void); + the_void.expression = expression(0); + return the_void; +}); + +function parameter_list() { + const list = []; + let optional; + const signature = ["("]; + if (next_token.id !== ")" && next_token.id !== "(end)") { + (function parameter() { + let ellipsis = false; + let param; + if (next_token.id === "{") { + let a; + let b = ""; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("{"); + signature.push("{"); + (function subparameter() { + let subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + survey(subparam); + a = b; + b = String(subparam.value || subparam.id); + if (a > b) { + if (!option.unordered) { + +// cause: "function aa({bb,aa}){}" + + warn("unordered_param_a", subparam); + } + } + advance(); + signature.push(subparam.id); + if (next_token.id === ":") { + advance(":"); + advance(); + token.label = subparam; + subparam = token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + } + if (next_token.id === "=") { + advance("="); + subparam.expression = expression(); + param.open = true; + } + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return subparameter(); + } + }()); + list.push(param); + advance("}"); + signature.push("}"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else if (next_token.id === "[") { + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("["); + signature.push("[]"); + (function subparameter() { + const subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + if (next_token.id === "=") { + advance("="); + subparam.expression = expression(); + param.open = true; + } + if (next_token.id === ",") { + advance(","); + return subparameter(); + } + }()); + list.push(param); + advance("]"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else { + if (next_token.id === "...") { + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + } + if (!next_token.identifier) { + return stop("expected_identifier_a"); + } + param = next_token; + list.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (next_token.id === "=") { + optional = param; + advance("="); + param.expression = expression(0); + } else { + if (optional !== undefined) { + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } + } + }()); + } + advance(")"); + signature.push(")"); + return [list, signature.join("")]; +} + +function do_function(the_function) { + let name; + if (the_function === undefined) { + the_function = token; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!next_token.identifier) { + +// cause: "function*aa(){}" + + return stop("expected_identifier_a", next_token); + } + name = next_token; + enroll(name, "variable", true); + the_function.name = name; + name.init = true; + name.calls = empty(); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + if (next_token.identifier) { + name = next_token; + the_function.name = name; + advance(); + } else { + the_function.name = anon; + } + } + } else { + name = the_function.name; + } + the_function.level = functionage.level + 1; + assert_or_throw(!mega_mode, "Expected !mega_mode."); +// Probably deadcode. +// if (mega_mode) { +// warn("unexpected_a", the_function); +// } + +// Don't make functions in loops. It is inefficient, and it can lead to scoping +// errors. + + if (functionage.loop > 0) { + +// cause: "while(0){aa.map(function(){});}" + + warn("function_in_loop", the_function); + } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + the_function.context = empty(); + the_function.finally = 0; + the_function.loop = 0; + the_function.switch = 0; + the_function.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functions.push(the_function); + functionage = the_function; + if (the_function.arity !== "statement" && typeof name === "object") { + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// Parse the parameter list. + + advance("("); + +// cause: "function(){}" + + token.free = false; + token.arity = "function"; + [functionage.parameters, functionage.signature] = parameter_list(); + functionage.parameters.forEach(function enroll_parameter(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + name.names.forEach(enroll_parameter); + } + }); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && next_token.line === token.line + ) { + return stop("unexpected_a", next_token); + } + if ( + next_token.id === "." + || next_token.id === "?." + || next_token.id === "[" + ) { + warn("unexpected_a"); + } + +// Restore the previous context. + + functionage = stack.pop(); + return the_function; +} + +function do_async() { + let the_async; + let the_function; + the_async = token; + advance("function"); + the_function = token; + the_function.is_async = true; + the_function.arity = the_async.arity; + do_function(); + if (!the_function.has_await) { + +// cause: "async function aa(){}" + + warn("missing_await_statement", the_function); + } + return the_function; +} + +prefix("async", do_async); +prefix("function", do_function); +prefix("await", function () { + let the_await; + the_await = token; + if (!functionage.is_async) { + +// cause: "function aa(){await 0;}" + + return stop("unexpected_a", the_await); + } + functionage.has_await = true; + the_await.expression = expression(150); + return the_await; +}); + +function fart(pl) { + advance("=>"); + const the_fart = token; + the_fart.arity = "binary"; + the_fart.name = "=>"; + the_fart.level = functionage.level + 1; + functions.push(the_fart); + if (functionage.loop > 0) { + +// cause: "while(0){aa.map(()=>0);}" + + warn("function_in_loop", the_fart); + } + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + the_fart.context = empty(); + the_fart.finally = 0; + the_fart.loop = 0; + the_fart.switch = 0; + the_fart.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functionage = the_fart; + the_fart.parameters = pl[0]; + the_fart.signature = pl[1]; + the_fart.parameters.forEach(function (name) { + enroll(name, "parameter", true); + }); + if (next_token.id === "{") { + warn("expected_a_b", the_fart, "function", "=>"); + the_fart.block = block("body"); + } else { + the_fart.expression = expression(0); + } + functionage = stack.pop(); + return the_fart; +} + +prefix("(", function () { + const the_paren = token; + let the_value; + const cadet = lookahead().id; + +// We can distinguish between a parameter list for => and a wrapped expression +// with one token of lookahead. + + if ( + next_token.id === ")" + || next_token.id === "..." + || (next_token.identifier && (cadet === "," || cadet === "=")) + ) { + +// cause: "()=>0" + + the_paren.free = false; + return fart(parameter_list()); + } + +// cause: "(0)" + + the_paren.free = true; + the_value = expression(0); + if (the_value.wrapped === true) { + +// cause: "((0))" + + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + if (next_token.id === "=>") { + if (the_value.arity !== "variable") { + if (the_value.id === "{" || the_value.id === "[") { + warn("expected_a_before_b", the_paren, "function", "("); + return stop("expected_a_b", next_token, "{", "=>"); + } + +// cause: "(0)=>0" + + return stop("expected_identifier_a", the_value); + } + the_paren.expression = [the_value]; + return fart([the_paren.expression, "(" + the_value.id + ")"]); + } + return the_value; +}); +prefix("`", do_tick); +prefix("{", function () { + const the_brace = token; + const seen = empty(); + the_brace.expression = []; + if (next_token.id !== "}") { + let a; + let b = ""; + (function member() { + let extra; + let full; + let id; + let name = next_token; + let value; + advance(); + a = b; + b = String(name.value || name.id); + if (a > b) { + if (!option.unordered) { + +// cause: "aa={bb,aa}" + + warn("unordered_property_a", name); + } + } + if ( + (name.id === "get" || name.id === "set") + && next_token.identifier + ) { + if (!option.getset) { + +// cause: "aa={get aa(){}}" + + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + next_token.id; + name = next_token; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + +// cause: "aa={get aa(){},get aa(){}}" + + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + +// cause: "aa={aa,aa}" + + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (next_token.id === "}" || next_token.id === ",") { + if (typeof extra === "string") { + +// cause: "aa={get aa}" + + advance("("); + } + value = expression(Infinity, true); + } else if (next_token.id === "(") { + value = do_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + +// cause: "aa={get aa(){}}" + + ? extra + +// cause: "aa={aa()}" + + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + +// cause: "aa={get aa.aa}" + + advance("("); + } + let the_colon = next_token; + advance(":"); + value = expression(0); + if (value.id === name.id && value.id !== "function") { + +// cause: "aa={aa:aa}" + + warn("unexpected_a", the_colon, ": " + name.id); + } + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + advance(":"); + value = expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (next_token.id === ",") { + advance(","); + return member(); + } + }()); + } + advance("}"); + return the_brace; +}); + +stmt(";", function () { + warn("unexpected_a", token); + return token; +}); +stmt("{", function () { + +// cause: "class aa{}" + + warn("naked_block", token); + return block("naked"); +}); +stmt("async", do_async); +stmt("break", function () { + const the_break = token; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (next_token.identifier && token.line === next_token.line) { + the_label = functionage.context[next_token.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + if (the_label !== undefined && the_label.dead) { + +// cause: "aa:{function aa(aa){break aa;}}" + + warn("out_of_scope_a"); + } else { + +// cause: "aa:{break aa;}" + + warn("not_label_a"); + } + } else { + the_label.used += 1; + } + the_break.label = next_token; + advance(); + } + advance(";"); + return the_break; +}); + +function do_var() { + const the_statement = token; + const is_const = the_statement.id === "const"; + the_statement.names = []; + +// A program may use var or let, but not both. + + if (!is_const) { + if (var_mode === undefined) { + var_mode = the_statement.id; + } else if (the_statement.id !== var_mode) { + warn( + "expected_a_b", + the_statement, + var_mode, + the_statement.id + ); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + +// cause: "switch(0){case 0:var aa}" + + warn("var_switch", the_statement); + } + if (functionage.loop > 0 && the_statement.id === "var") { + +// cause: "while(0){var aa;}" + + warn("var_loop", the_statement); + } + (function next() { + if (next_token.id === "{" && the_statement.id !== "var") { + let a; + let b = ""; + const the_brace = next_token; + advance("{"); + (function pair() { + if (!next_token.identifier) { + +// cause: "let {0}" + + return stop("expected_identifier_a", next_token); + } + const name = next_token; + survey(name); + a = b; + b = String(name.value || name.id); + if (a > b) { + if (!option.unordered) { + +// cause: "let{bb,aa}" + + warn("unordered_param_a", name); + } + } + advance(); + if (next_token.id === ":") { + advance(":"); + if (!next_token.identifier) { + +// cause: "let {aa:0}" + + return stop("expected_identifier_a", next_token); + } + next_token.label = name; + the_statement.names.push(next_token); + enroll(next_token, "variable", is_const); + advance(); + the_brace.open = true; + } else { + the_statement.names.push(name); + enroll(name, "variable", is_const); + } + name.dead = false; + name.init = true; + if (next_token.id === "=") { + +// cause: "let {aa=0}" + + advance("="); + name.expression = expression(); + the_brace.open = true; + } + if (next_token.id === ",") { + advance(","); + return pair(); + } + }()); + advance("}"); + advance("="); + the_statement.expression = expression(0); + } else if (next_token.id === "[" && the_statement.id !== "var") { + const the_bracket = next_token; + advance("["); + (function element() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + if (!next_token.identifier) { + +// cause: "let[]" + + return stop("expected_identifier_a", next_token); + } + const name = next_token; + advance(); + the_statement.names.push(name); + enroll(name, "variable", is_const); + name.dead = false; + name.init = true; + if (ellipsis) { + name.ellipsis = true; + } else { + if (next_token.id === "=") { + advance("="); + name.expression = expression(); + the_bracket.open = true; + } + if (next_token.id === ",") { + advance(","); + return element(); + } + } + }()); + advance("]"); + advance("="); + the_statement.expression = expression(0); + } else if (next_token.identifier) { + const name = next_token; + advance(); + if (name.id === "ignore") { + +// cause: "let ignore;function aa(ignore) {}" + + warn("unexpected_a", name); + } + enroll(name, "variable", is_const); + if (next_token.id === "=" || is_const) { + advance("="); + name.dead = false; + name.init = true; + name.expression = expression(0); + } + the_statement.names.push(name); + } else { + +// cause: "let 0" +// cause: "var{aa:{aa}}" + + return stop("expected_identifier_a", next_token); + } + }()); + semicolon(); + return the_statement; +} + +stmt("const", do_var); +stmt("continue", function () { + const the_continue = token; + if (functionage.loop < 1 || functionage.finally > 0) { + warn("unexpected_a", the_continue); + } + not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; +}); +stmt("debugger", function () { + const the_debug = token; + if (!option.devel) { + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; +}); +stmt("delete", function () { + const the_token = token; + const the_value = expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; +}); +stmt("do", function () { + const the_do = token; + not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + +// cause: "function aa(){do{break;}while(0)}" + + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; +}); +stmt("export", function () { + const the_export = token; + let the_id; + let the_name; + let the_thing; + + function export_id() { + if (!next_token.identifier) { + +// cause: "export {}" + + stop("expected_identifier_a"); + } + the_id = next_token.id; + the_name = global.context[the_id]; + if (the_name === undefined) { + +// cause: "export {aa}" + + warn("unexpected_a"); + } else { + the_name.used += 1; + if (exports[the_id] !== undefined) { + +// cause: "let aa;export{aa,aa}" + + warn("duplicate_a"); + } + exports[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + } + + the_export.expression = []; + if (next_token.id === "default") { + if (exports.default !== undefined) { + +// cause: "export default 0;export default 0" + + warn("duplicate_a"); + } + advance("default"); + the_thing = expression(0); + if ( + the_thing.id !== "(" + || the_thing.expression[0].id !== "." + || the_thing.expression[0].expression.id !== "Object" + || the_thing.expression[0].name.id !== "freeze" + ) { + +// cause: "export default {}" + + warn("freeze_exports", the_thing); + } + if (next_token.id === ";") { + semicolon(); + } + exports.default = the_thing; + the_export.expression.push(the_thing); + } else { + if (next_token.id === "function") { + +// cause: "export function aa(){}" + + warn("freeze_exports"); + the_thing = statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + +// cause: "let aa;export{aa};export function aa(){}" + + if (exports[the_id] !== undefined) { + warn("duplicate_a", the_name); + } + exports[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + +// cause: "export const" +// cause: "export let" +// cause: "export var" + + warn("unexpected_a", next_token); + statement(); + } else if (next_token.id === "{") { + +// cause: "export {}" + + advance("{"); + (function loop() { + export_id(); + if (next_token.id === ",") { + advance(","); + return loop(); + } + }()); + advance("}"); + semicolon(); + } else { + +// cause: "export" + + stop("unexpected_a"); + } + } + module_mode = true; + return the_export; +}); +stmt("for", function () { + let first; + const the_for = token; + if (!option.for) { + warn("unexpected_a", the_for); + } + not_top_level(the_for); + functionage.loop += 1; + advance("("); + +// cause: "for(){}" + + token.free = true; + if (next_token.id === ";") { + +// cause: "for(;;){}" + + return stop("expected_a_b", the_for, "while (", "for (;"); + } + if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + return stop("unexpected_a"); + } + first = expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + +// cause: "for(0 in aa){}" + + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = expression(0); + advance(";"); + the_for.inc = expression(0); + if (the_for.inc.id === "++") { + +// cause: "for(aa;aa;aa++){}" + + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; +}); +stmt("function", do_function); +stmt("if", function () { + let the_else; + const the_if = token; + the_if.expression = condition(); + the_if.block = block(); + if (next_token.id === "else") { + advance("else"); + the_else = token; + the_if.else = ( + next_token.id === "if" + +// cause: "if(0){0}else if(0){0}" + + ? statement() + +// cause: "if(0){0}else{0}" + + : block() + ); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + +// cause: "if(0){break;}else{}" + + the_if.disrupt = true; + } else { + +// cause: "if(0){break;}else{}" + + warn("unexpected_a", the_else); + } + } + } + return the_if; +}); +stmt("import", function () { + const the_import = token; + if (next_token.id === "(") { + the_import.arity = "unary"; + the_import.constant = true; + the_import.statement = false; + advance("("); + const string = expression(0); + if (string.id !== "(string)") { + +// cause: "import(aa)" + + warn("expected_string_a", string); + } + froms.push(token.value); + advance(")"); + advance("."); + advance("then"); + advance("("); + the_import.expression = expression(0); + advance(")"); + semicolon(); + return the_import; + } + let name; + if (typeof module_mode === "object") { + +// cause: "/*global aa*/\nimport aa from \"aa\"" + + warn("unexpected_directive_a", module_mode, module_mode.directive); + } + module_mode = true; + if (next_token.identifier) { + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + const names = []; + advance("{"); + if (next_token.id !== "}") { + while (true) { + if (!next_token.identifier) { + +// cause: "import {" + + stop("expected_identifier_a"); + } + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (next_token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token; + if (!rx_module.test(token.value)) { + +// cause: "import aa from \"!aa\"" + + warn("bad_module_name_a", token); + } + froms.push(token.value); + semicolon(); + return the_import; +}); +stmt("let", do_var); +stmt("return", function () { + const the_return = token; + not_top_level(the_return); + if (functionage.finally > 0) { + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (next_token.id !== ";" && the_return.line === next_token.line) { + the_return.expression = expression(10); + } + advance(";"); + return the_return; +}); +stmt("switch", function () { + let dups = []; + let last; + let stmts; + const the_cases = []; + let the_disrupt = true; + const the_switch = token; + not_top_level(the_switch); + if (functionage.finally > 0) { + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + +// cause: "switch(){}" + + token.free = true; + the_switch.expression = expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + (function major() { + const the_case = next_token; + the_case.arity = "statement"; + the_case.expression = []; + (function minor() { + advance("case"); + token.switch = true; + const exp = expression(0); + if (dups.some(function (thing) { + return are_similar(thing, exp); + })) { + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (next_token.id === "case") { + return minor(); + } + }()); + stmts = statements(); + if (stmts.length < 1) { + warn("expected_statements_a"); + return; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "break;", + artifact(next_token) + ); + } + if (next_token.id === "case") { + return major(); + } + }()); + dups = undefined; + if (next_token.id === "default") { + const the_default = next_token; + advance("default"); + token.switch = true; + advance(":"); + the_switch.else = statements(); + if (the_switch.else.length < 1) { + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + const the_last = the_switch.else[the_switch.else.length - 1]; + if (the_last.id === "break" && the_last.label === undefined) { + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; +}); +stmt("throw", function () { + const the_throw = token; + the_throw.disrupt = true; + the_throw.expression = expression(10); + semicolon(); + if (functionage.try > 0) { + warn("unexpected_a", the_throw); + } + return the_throw; +}); +stmt("try", function () { + let the_catch; + let the_disrupt; + const the_try = token; + if (functionage.try > 0) { + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (next_token.id === "catch") { + let ignored = "ignore"; + the_catch = next_token; + the_try.catch = the_catch; + advance("catch"); + if (next_token.id === "(") { + advance("("); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + if (next_token.id !== "ignore") { + ignored = undefined; + the_catch.name = next_token; + enroll(next_token, "exception", true); + } + advance(); + advance(")"); + } + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "catch", + artifact(next_token) + ); + + } + if (next_token.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; +}); +stmt("var", do_var); +stmt("while", function () { + const the_while = token; + not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + +// cause: "function aa(){while(0){break;}}" + + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; +}); +stmt("with", function () { + +// cause: "with" + + stop("unexpected_a", token); +}); + +ternary("?", ":"); + +// Ambulation of the parse tree. + +function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; +} + +function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all of the +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + } + }; +} + +const posts = empty(); +const pres = empty(); +const preaction = action(pres); +const postaction = action(posts); +const preamble = amble(pres); +const postamble = amble(posts); + +function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + +// cause: "0&&0" +// cause: "(function(){}())" + + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.id === "function") { + +// cause: "aa=function(){}" + + walk_statement(thing.block); + } + if (thing.arity === "pre" || thing.arity === "post") { + +// cause: "aa=++aa" +// cause: "aa=--aa" + + warn("unexpected_a", thing); + } else if ( + +// cause: "aa=0" + + thing.arity === "statement" + || thing.arity === "assignment" + ) { + +// cause: "aa[aa=0]" + + warn("unexpected_statement_a", thing); // deadcode? + } + postamble(thing); + } + } +} + +function walk_statement(thing) { + if (thing) { + if (Array.isArray(thing)) { + +// cause: "+[]" + + thing.forEach(walk_statement); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + +// cause: "0&&0" + + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + && thing.id !== "import" + && thing.id !== "await" + ) { + +// cause: "!0" +// cause: "+[]" +// cause: "+new aa()" +// cause: "0" + + warn("unexpected_expression_a", thing); + } + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + } +} + +function lookup(thing) { + if (thing.arity === "variable") { + +// Look up the variable in the current context. + + let the_variable = functionage.context[thing.id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable === undefined) { + stack.forEach(function (outer) { + const a_variable = outer.context[thing.id]; + if ( + a_variable !== undefined + && a_variable.role !== "label" + ) { + the_variable = a_variable; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (the_variable === undefined) { + if (declared_globals[thing.id] === undefined) { + +// cause: "aa" +// cause: "class aa{}" + + warn("undeclared_a", thing); + return; + } + the_variable = { + dead: false, + id: thing.id, + init: true, + parent: global, + role: "variable", + used: 0, + writable: false + }; + global.context[thing.id] = the_variable; + } + the_variable.closure = true; + functionage.context[thing.id] = the_variable; + } else if (the_variable.role === "label") { + +// cause: "aa:while(0){aa;}" + + warn("label_a", thing); + } + if ( + the_variable.dead + && ( + the_variable.calls === undefined + || functionage.name === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + ) { + +// cause: "function aa(){bb();}\nfunction bb(){}" + + warn("out_of_scope_a", thing); + } + return the_variable; + } +} + +function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); +} + +function preaction_function(thing) { + +// cause: "()=>0" +// cause: "(function (){}())" +// cause: "function aa(){}" + + if (thing.arity === "statement" && blockage.body !== true) { + +// cause: "if(0){function aa(){}\n}" + + warn("unexpected_a", thing); + } + stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + +// cause: "/*jslint getset*/\naa={get aa(aa){}}" + + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + +// cause: "/*jslint getset*/\naa={set aa(){}}" + + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); +} + +function bitwise_check(thing) { + if (!option.bitwise && bitwiseop[thing.id] === true) { + warn("unexpected_a", thing); + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + warn("unexpected_a", thing); + } +} + +function pop_block() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); +} + +function action_var(thing) { + thing.names.forEach(function (name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.init = true; + } + } + blockage.live.push(name); + }); +} + +preaction("assignment", bitwise_check); +preaction("binary", bitwise_check); +preaction("binary", function (thing) { + if (relationop[thing.id] === true) { + const left = thing.expression[0]; + const right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + +// cause: "NaN===NaN" + + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + +// cause: "typeof 0===0" + + warn("expected_string_a", right); + } + } else { + const value = right.value; + if (value === "null" || value === "undefined") { + +// cause: "typeof aa===\"undefined\"" + + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + +// cause: "typeof 0===\"aa\"" + + warn("expected_type_string_a", right, value); + } + } + } + } +}); +preaction("binary", "==", function (thing) { + +// cause: "0==0" + + warn("expected_a_b", thing, "===", "=="); +}); +preaction("binary", "!=", function (thing) { + +// cause: "0!=0" + + warn("expected_a_b", thing, "!==", "!="); +}); +preaction("binary", "=>", preaction_function); +preaction("binary", "||", function (thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + +// cause: "0&&0||0" + + warn("and", thang); + } + }); +}); +preaction("binary", "(", function (thing) { + const left = thing.expression[0]; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + const parent = functionage.name.parent; + if (parent) { + const left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + && left_variable.dead + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } +}); +preaction("binary", "in", function (thing) { + +// cause: "aa in aa" + + warn("infix_in", thing); +}); +preaction("binary", "instanceof", function (thing) { + +// cause: "0 instanceof 0" + + warn("unexpected_a", thing); +}); +preaction("statement", "{", function (thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; +}); +preaction("statement", "for", function (thing) { + if (thing.name !== undefined) { + const the_variable = lookup(thing.name); + if (the_variable !== undefined) { + the_variable.init = true; + if (!the_variable.writable) { + +// cause: "const aa=0;for(aa in aa){}" + + warn("bad_assignment_a", thing.name); + } + } + } + walk_statement(thing.initial); +}); +preaction("statement", "function", preaction_function); +preaction("unary", "~", bitwise_check); +preaction("unary", "function", preaction_function); +preaction("variable", function (thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } +}); + +function init_variable(name) { + const the_variable = lookup(name); + if (the_variable !== undefined) { + if (the_variable.writable) { + the_variable.init = true; + return; + } + } + warn("bad_assignment_a", name); +} + +postaction("assignment", "+=", function (thing) { + let right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } +}); +postaction("assignment", function (thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + if (thing.id === "=") { + if (thing.names !== undefined) { + if (Array.isArray(thing.names)) { + thing.names.forEach(init_variable); + } else { + init_variable(thing.names); + } + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.writable !== true) { + warn("bad_assignment_a", lvalue); + } + } + const right = syntax[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + warn("unexpected_a", thing.expression[1]); + } + } +}); + +function postaction_function(thing) { + delete functionage.finally; + delete functionage.loop; + delete functionage.switch; + delete functionage.try; + functionage = stack.pop(); + if (thing.wrapped) { + +// cause: "aa=(function(){})" + + warn("unexpected_parens", thing); + } + return pop_block(); +} + +postaction("binary", function (thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || are_similar(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + +// cause: "if(0===0){0}" + + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option.convert) { + if (thing.expression[0].value === "") { + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + +// cause: "aa=window[0]" + + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + +// cause: "aa=self[0]" + + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === "." || thing.id === "?.") { + if (thing.expression.id === "RegExp") { + +// cause: "aa=RegExp.aa" + + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + +// cause: "0- -0" + + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } +}); +postaction("binary", "&&", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + +// cause: "aa=0&&0" +// cause: "aa=`${0}`&&`${0}`" +// cause: "aa=(``?``:``)&&(``?``:``)" + + warn("weird_condition_a", thing); + } +}); +postaction("binary", "||", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + +// cause: "aa=0||0" + + warn("weird_condition_a", thing); + } +}); +postaction("binary", "=>", postaction_function); +postaction("binary", "(", function (thing) { + let left = thing.expression[0]; + let the_new; + let arg; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + +// cause: "aa=function(){}()" + + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + +// cause: "new Boolean()" +// cause: "new Number()" +// cause: "new String()" +// cause: "new Symbol()" +// cause: "new aa()" + + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option.eval) { + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + +// cause: "new Array()" + + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + +// cause: "new Object()" + + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + +// cause: "let Aa=Aa()" + + warn( + "expected_a_before_b", + left, + "new", + artifact(left) + ); + } + } + } else if (left.id === ".") { + let cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + cack = !cack; + } + if (rx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + warn("unexpected_a", the_new); + } else { + +// cause: "let Aa=Aa.Aa()" + + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + const paren = left.expression; + if (paren.id === "(") { + const array = paren.expression; + if (array.length === 1) { + const new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + +// cause: "new Date().getTime()" + + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } +}); +postaction("binary", "[", function (thing) { + if (thing.expression[0].id === "RegExp") { + +// cause: "aa=RegExp[0]" + + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + +// cause: "aa[[0]]" + + warn("weird_expression_a", thing.expression[1]); + } +}); +postaction("statement", "{", pop_block); +postaction("statement", "const", action_var); +postaction("statement", "export", top_level_only); +postaction("statement", "for", function (thing) { + walk_statement(thing.inc); +}); +postaction("statement", "function", postaction_function); +postaction("statement", "import", function (the_thing) { + const name = the_thing.name; + if (name) { + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return top_level_only(the_thing); + } +}); +postaction("statement", "let", action_var); +postaction("statement", "try", function (thing) { + if (thing.catch !== undefined) { + const the_name = thing.catch.name; + if (the_name !== undefined) { + const the_variable = functionage.context[the_name.id]; + the_variable.dead = false; + the_variable.init = true; + } + walk_statement(thing.catch.block); + } +}); +postaction("statement", "var", action_var); +postaction("ternary", function (thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || are_similar(thing.expression[1], thing.expression[2]) + ) { + warn("unexpected_a", thing); + } else if (are_similar(thing.expression[0], thing.expression[1])) { + warn("expected_a_b", thing, "||", "?"); + } else if (are_similar(thing.expression[0], thing.expression[2])) { + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + +// cause: "(aa&&!aa?0:1)" + + warn("wrap_condition", thing.expression[0]); + } +}); +postaction("unary", function (thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option.convert) { + +// cause: "!!0" + + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } +}); +postaction("unary", "function", postaction_function); +postaction("unary", "+", function (thing) { + if (!option.convert) { + warn("expected_a_b", thing, "Number(...)", "+"); + } + const right = thing.expression; + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } +}); + +function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + if (id !== "ignore") { + const name = the_function.context[id]; + if (name.parent === the_function) { + if ( + name.used === 0 + && ( + name.role !== "function" + || name.parent.arity !== "unary" + ) + ) { + +// cause: "/*jslint node*/\nlet aa;" +// cause: "function aa(aa){return;}" + + warn("unused_a", name); + } else if (!name.init) { + +// cause: "/*jslint node*/\nlet aa;aa();" + + warn("uninitialized_a", name); + } + } + } + }); +} + +function uninitialized_and_unused() { + +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (module_mode === true || option.node) { + delve(global); + } + functions.forEach(delve); +} + +// Go through the token list, looking at usage of whitespace. + +function whitage() { + let closer = "(end)"; + +// free = false + +// cause: "()=>0" +// cause: "aa()" +// cause: "aa(0,0)" +// cause: "function(){}" + + let free = false; + +// cause: "(0)" +// cause: "(aa)" +// cause: "aa(0)" +// cause: "do{}while()" +// cause: "for(){}" +// cause: "if(){}" +// cause: "switch(){}" +// cause: "while(){}" + + // let free = true; + + let left = global; + let margin = 0; + let nr_comments_skipped = 0; + let open = true; + let opening = true; + let right; + + function pop() { + const previous = stack.pop(); + closer = previous.closer; + free = previous.free; + margin = previous.margin; + open = previous.open; + opening = previous.opening; + } + + function push() { + stack.push({ + closer, + free, + margin, + open, + opening + }); + } + + function expected_at(at) { + assert_or_throw(right !== undefined, "Expected right !== undefined."); +// Probably deadcode. +// if (right === undefined) { +// right = next_token; +// } + warn( + "expected_a_at_b_c", + right, + artifact(right), + fudge + at, + +// Return the fudged column number of an artifact. + + right.from + fudge + ); + } + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + +// cause: +// ( +// function aa ( +// bb +// , +// [ +// cc,dd +// ] +// , +// { +// ee,ff=( 0 ) +// } +// , +// ... zz +// ) +// { +// return { +// aa,bb +// } +// ; +// } +// ( +// ) +// ) +// ; + + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function no_space() { + if (left.line === right.line) { + if (left.thru !== right.from && nr_comments_skipped === 0) { + +// cause: "let aa = aa()( );" + + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (open) { + const at = ( + free + ? margin + : margin + 8 // deadcode? + ); + if (right.from < at) { + +// cause: +// let aa = aa( +// aa +// () +// ); + + expected_at(at); + } + } else { // deadcode? + if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line || !open) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (right.from !== margin) { + expected_at(margin); + } + } + } + + stack = []; + tokens.forEach(function (the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + + const new_closer = opener[left.id]; + if (typeof new_closer === "string") { + +// cause: "${" +// cause: "(" +// cause: "[" +// cause: "{" + + if (new_closer !== right.id) { + +// cause: "${0" +// cause: "(0" +// cause: "[0" +// cause: "{0" + + opening = left.open || (left.line !== right.line); + push(); + closer = new_closer; + if (opening) { + +// cause: "${\n0\n}" +// cause: "(\n0\n)" +// cause: "[\n0\n]" +// cause: "{\n0\n}" + + free = closer === ")" && left.free; + open = true; + margin += 4; + if (right.role === "label") { + if (right.from !== 0) { + +// cause: +// function aa() { +// bb: +// while (aa) { +// if (aa) { +// break bb; +// } +// } +// } + + expected_at(0); + } + } else if (right.switch) { + at_margin(-4); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + +// cause: +// function aa() {bb: +// while (aa) {aa(); +// } +// } + + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + +// cause: "${0}" +// cause: "(0)" +// cause: "[0]" +// cause: "{0}" + + free = false; + open = false; + +// cause: "let aa = ( 0 );" + + no_space_only(); + } + } else { + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like '{]') have already been detected. + +// cause: "${}" +// cause: "()" +// cause: "[]" +// cause: "{}" + + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } + } else { + if (right.statement === true) { + if (left.id === "else") { + one_space_only(); + } else { + at_margin(0); + open = false; + } + +// If right is a closer, then pop the previous state. + + } else if (right.id === closer) { + pop(); + if (opening && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-4); + } else if (right.role === "label") { + if (right.from !== 0) { + +// cause: +// function aa() { +// let bb = 0;cc: +// while (aa) { +// if (aa) { +// break cc; +// } +// } +// } + + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + +// cause: "let {aa,bb}=0;" + + one_space(); + } else { + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + +// cause: +// let aa = ( +// aa +// ? 0 +// : 1 +// ); + + at_margin(0); + } else { + +// cause: "let aa=(aa?0:1);" + + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + no_space(); + } else if ( + left.id === "." + || left.id === "?." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + ) { + no_space_only(); + } else if (right.id === "." || right.id === "?.") { + no_space_only(); + } else if (left.id === ";") { // deadcode? + if (open) { + at_margin(0); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || left.id === "await" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + +// cause: +// function aa() { +// do { +// aa(); +// } while(aa()); +// } + + one_space_only(); + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + +// cause: "let aa=0;" +// cause: +// let aa={ +// aa: +// 0 +// }; + + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +// The jslint function itself. + +function jslint( + source = "", + option_object = empty(), + global_array = [] +) { + try { + warnings = []; + option = Object.assign(empty(), option_object); + anon = "anonymous"; + block_stack = []; + declared_globals = empty(); + directive_mode = true; + directives = []; + early_stop = true; + exports = empty(); + froms = []; + fudge = ( + option.fudge + ? 1 + : 0 + ); + functions = []; + global = { + body: true, + context: empty(), + finally: 0, + from: 0, + id: "(global)", + level: 0, + line: 0, + live: [], + loop: 0, + switch: 0, + thru: 0, + try: 0 + }; + blockage = global; + functionage = global; + json_mode = false; + mega_mode = false; + module_mode = false; + next_token = global; + property = empty(); + shebang = false; + stack = []; + tenure = undefined; + token = global; + token_nr = 0; + var_mode = undefined; + populate(standard, declared_globals, false); + populate(global_array, declared_globals, false); + Object.keys(option).forEach(function (name) { + if (option[name] === true) { + const allowed = allowed_option[name]; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } + }); + tokenize(source); + advance(); + if (json_mode) { + tree = json_value(); + advance("(end)"); + } else { + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option.browser) { + if (next_token.id === ";") { + advance(";"); + } + } else { + +// If we are not in a browser, then the file form of strict pragma may be used. + + if ( + next_token.value === "use strict" + ) { + advance("(string)"); + advance(";"); + } + } + tree = statements(); + advance("(end)"); + functionage = global; + walk_statement(tree); + if (warnings.length === 0) { + uninitialized_and_unused(); + if (!option.white) { + whitage(); + } + } + } + if (!option.browser) { + directives.forEach(function (comment) { + if (comment.directive === "global") { + +// cause: "/*global aa*/" + + warn("missing_browser", comment); + } + }); + } + if (option.test_internal_error) { + assert_or_throw(undefined, "test_internal_error"); + } + early_stop = false; + } catch (e) { + e.early_stop = true; + e.message = "[JSLint was unable to finish]\n" + e.message; + if (e.name !== "JSLintError") { + e.column = 0; + e.line = 0; + e.stack_trace = e.stack; + warnings.push(e); + } + } + +// sort warnings by early_stop first, line, column respectively + + warnings.sort(function (a, b) { + return ( + Boolean(b.early_stop) - Boolean(a.early_stop) + || a.line - b.line || a.column - b.column + ); + +// update each warning with a formatted_message ready for use by cli + + }).map(function ({ + column = 0, + line = 0, + message = "", + stack_trace = "" + }, ii, list) { + column += 1; + line += 1; + list[ii].formatted_message = String( + String(ii + 1).padStart(3, " ") + + " \u001b[31m" + message + "\u001b[39m" + + " \u001b[90m\/\/ line " + line + ", column " + column + + "\u001b[39m\n" + + (" " + String(lines && lines[line - 1]).trim()).slice(0, 72) + + "\n" + stack_trace + ).trim(); + }); + return { + directives, + edition, + exports, + froms, + functions, + global, + id: "(JSLint)", + json: json_mode, + lines, + module: module_mode === true, + ok: warnings.length === 0 && !early_stop, + option, + property, + shebang: ( + shebang + ? lines[0] + : undefined + ), + stop: early_stop, + tokens, + tree, + warnings + }; +} + +async function cli({ + console_error, + file, + option, + source +}) { +/* + * this function will run jslint from nodejs-cli + */ + const fs = await import("fs"); + let exitCode; + function string_line_count(code) { + /* + * this function will count number of newlines in + */ + let cnt; + let ii; + // https://jsperf.com/regexp-counting-2/8 + cnt = 0; + ii = 0; + while (true) { + ii = code.indexOf("\n", ii) + 1; + if (ii === 0) { + break; + } + cnt += 1; + } + return cnt; + } + function jslint_from_file({ + code, + file, + line_offset = 0, + option = {}, + warnings = [] + }) { + switch (( + /\.\w+?$|$/m + ).exec(file)[0]) { + case ".html": + // recurse + code.replace(( + /^\n" + }); +}()); + +(function testCaseJslintMisc() { +/* + * this function will test jslint's misc handling-behavior + */ + // test assertOrThrow's throw handling-behavior + try { + assertOrThrow(undefined, new Error()); + } catch (ignore) {} +}()); + +(function testCaseJslintOption() { +/* + * this function will test jslint's option handling-behavior + */ + assertOrThrow(jslint([""], { + bitwise: true, + browser: true, + convert: true, + couch: true, + debug: true, + devel: true, + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + node: true, + single: true, + this: true, + white: true + }).warnings.length === 0); + assertOrThrow(jslint("", { + test_internal_error: true + }).warnings.length === 1); +}()); + +(function testCaseJslintCodeValidate() { +/* + * this function will validate each code is valid in jslint + */ + Object.values({ + array: [ + "new Array(0);" + ], + async_await: [ + "async function aa() {\n await aa();\n}" + ], + date: [ + "Date.getTime();", + "let aa = aa().getTime();", + "let aa = aa.aa().getTime();" + ], + directives: [ + "#!\n/*jslint browser:false, node*/\n\"use strict\";", + "/*jslint bitwise*/\nlet aa = aa | 0;", + "/*jslint browser*/\n;", + "/*jslint debug*/\n", + "/*jslint devel*/\ndebugger;", + "/*jslint eval*/\nnew Function();\neval();", + "/*jslint getset*/\nlet aa = {get aa() {\n return;\n}};", + "/*jslint getset*/\nlet aa = {set aa(aa) {\n return aa;\n}};", + "/*jslint this*/\nlet aa = this;", + "/*jslint unordered*/\nlet {bb, aa} = 0;", + "/*jslint white*/\n\t", + "/*property aa bb*/" + ], + fart: [ + "function aa() {\n return () => 0;\n}" + ], + json: [ + "{\"aa\":[[],-0,null]}" + ], + label: [ + "function aa() {\nbb:\n while (true) {\n if (true) {\n" + + " break bb;\n }\n }\n}" + ], + loop: [ + "function aa() {\n do {\n aa();\n } while (aa());\n}" + ], + module: [ + "export default Object.freeze();", + "import {aa, bb} from \"aa\";\naa(bb);", + "import {} from \"aa\";", + "import(\"aa\").then(function () {\n return;\n});" + ], + number: [ + "let aa = 0.0e0;", + "let aa = 0b0;", + "let aa = 0o0;", + "let aa = 0x0;" + ], + optional_chaining: [ + "let aa = aa?.bb?.cc;" + ], + property: [ + "let aa = aa[`!`];" + ], + regexp: [ + "function aa() {\n return /./;\n}", + "let aa = /(?!.)(?:.)(?=.)/;" + ], + ternary: [ + "let aa = (\n aa()\n ? 0\n : 1\n) " + + "&& (\n aa()\n ? 0\n : 1\n);" + ], + var: [ + "let [...aa] = [...aa];", + "let [\n aa, bb = 0\n] = 0;", + "let {aa, bb} = 0;", + "let {\n aa: bb\n} = 0;" + ] + }).forEach(function (codeList) { + codeList.forEach(function (code) { + let warnings; + warnings = jslint(code).warnings; + assertOrThrow( + warnings.length === 0, + JSON.stringify([code, warnings]) + ); + }); + }); +}()); + +(async function testCaseJslintWarningsValidate() { +/* + * this function will validate each jslint is raised with given + * malformed + */ + Object.entries({ + expected_a_b: [ + "([])=>0", + "(aa)=>{}", + "(aa?0:aa)", + "(aa?aa:0)", + "(aa?false:true)", + "(aa?true:false)", + ";{", + "`${/ /}`", + "`${`", + "`${{`", + "aa.aa=undefined", + "aa=+aa", + "aa=/[ ]/", + "aa=/aa{/", + "aa=0+\"\"", + "aa=\"\"+\"\"", + "async", + "delete [0]", + "for(;;){}", + "isFinite(0)", + "let aa;var aa;" + ], + expected_a_before_b: [ + "aa=/(:)/", + "aa=/=/", + "aa=/?/", + "aa=/[/" + ], + expected_identifier_a: [ + "function aa(0){}", + "function aa([aa]){}\nfunction aa([aa],[aa,aa=aa],[0]){}", + "function aa({aa}){}\nfunction aa({aa},{aa:aa,aa=aa},{aa:0}){}", + "function(){}" + ], + expected_space_a_b: [ + "(function(){return;}());" + ], + required_a_optional_b: [ + "function aa(aa=0,...){}", + "function aa(aa=0,[]){}", + "function aa(aa=0,{}){}", + "function aa(aa=0,bb){}" + ], + too_long: [ + "//".repeat(100) + ], + unexpected_a: [ + "((0))", + "(+0?+0:+0)()", + "(/./)?.foo", + "/*/", + "/./", + "0===(0==0)", + "0[0][0]", + "0|0", + ";", + "[-0x0]", + "[0x0]", + "\"aa\"?.bb", + "`${/[`]/}`", + "`${/`/}`", + "`${\"`\"}`", + "aa((0))", + "aa+=NaN", + "aa/=0", + "aa=/[0-]/", + "aa=/.//", + "aa=/./z", + "aa={aa:aa}", + "aa={set aa(){}}", + "arguments", + "await", + "debugger", + "eval", + "for(aa in aa){}", + "for(const ii=0;;){}", + "for(ii=0;ii<0;ii++){}", + "for(ii=0;ii<0;ii+=0){}", + "function aa(){for(0;0;0){break;}}", + "function aa(){try{return;}catch(ignore){}finally{return;}}", + "function aa(){try{}catch(ignore){}finally{switch(0){case 0:}}}", + "function aa(){while(0){continue;}}", + "function aa(){while(0){try{0;}catch(ignore){}finally{continue;}}}", + "function aa(){}0", + "function aa(){}\n[]", + "function ignore(){let ignore;}", + "ignore", + "ignore:", + "import ignore from \"aa\"", + "import {ignore} from \"aa\"", + "let aa=[]?.bb", + "new Date.UTC()", + "new Function()", + "new Symbol()", + "switch(0){case 0:break;case 0:break}", + "switch(0){case 0:break;default:break;}", + "switch(0){case 0:break;default:}", + "this", + "try{throw 0;try{}catch(){}}catch(){}", + "try{}finally{break;}", + "void 0", + "while((0)){}", + "while(0){}", + "{//\n}", + "{0:0}", + "{\"\\u{1234}\":0}", + "{\"aa\":", + "{\"aa\":'aa'}" + ] + }).forEach(function ([ + expectedWarning, malformedCodeList + ]) { + malformedCodeList.forEach(function (malformedCode) { + assertOrThrow( + jslint(malformedCode).warnings.some(function ({ + code + }) { + return code === expectedWarning; + }), + new Error( + `jslint failed to warn "${expectedWarning}" with ` + + `malfomed code "${malformedCode}"` + ) + ); + }); + }); + Array.from(String( + await fs.promises.readFile("jslint.js", "utf8") + ).matchAll(new RegExp(( + "\\s*?" + + "(\\/\\/\\s*?cause:.*?\\n(?:\\/\\/.*?\\n)*?)" + + "(\\s*?^[^\\/].*?(?:\\n\\s*?\".*?)?$)" + ), "gm"))).forEach(function ([ + match0, causeList, warning + ]) { + let expectedWarningCode; + let fnc; + // debug match0 + console.error(match0.trim().replace((/\n\n/g), "\n")); + assertOrThrow( + match0.indexOf("\n\n" + causeList + "\n ") === 0, + JSON.stringify([ + match0, causeList + ], undefined, 4) + ); + warning = warning.match( + "(" + + "at_margin" + + "|expected_at" + + "|no_space_only" + + "|one_space_only" + + "|one_space" + + "|stop" + + "|stop_at" + + "|warn" + + "|warn_at" + + ")" + + "\\\u0028\\s*?\"?" + + "(\\S[^\n\"]+)" + ); + if (warning) { + expectedWarningCode = warning[2]; + fnc = warning[1]; + switch (fnc) { + case "at_margin": + case "expected_at": + expectedWarningCode = "expected_a_at_b_c"; + break; + case "no_space_only": + expectedWarningCode = "unexpected_space_a_b"; + break; + case "one_space": + case "one_space_only": + expectedWarningCode = "expected_space_a_b"; + break; + } + } + causeList.split( + /\/\/\u0020cause:[\n|\u0020]/ + ).slice(1).forEach(function (cause) { + assertOrThrow(cause === cause.trim() + "\n", JSON.stringify(cause)); + cause = ( + cause[0] === "\"" + ? JSON.parse(cause) + : cause.replace(( + /^\/\/\u0020/gm + ), "") + ); + assertOrThrow( + jslint(cause).warnings.some(function ({ + code + }) { + return code === expectedWarningCode; + }) || !expectedWarningCode, + "\n" + cause.trim() + ); + }); + }); +}()); diff --git a/branch.async_await/README b/branch.async_await/README new file mode 100644 index 000000000..cd6b06c8b --- /dev/null +++ b/branch.async_await/README @@ -0,0 +1,39 @@ +JSLint, The JavaScript Code Quality Tool + +Douglas Crockford +douglas@crockford.com + +2019-03-15 + +jslint.js contains the jslint function. It parses and analyzes a source file, +returning an object with information about the file. It can also take an object +that sets options. + +index.html runs the jslint.js function in a web page. The page also depends +browser.js and report.js and jslint.css. + +jslint.css provides styling for index.html. + +browser.js runs the web user interface. + +report.js generates the results reports in HTML. + +help.html describes JSLint's usage. Please read it. + +function.html describes the jslint function and the results it produces. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[http://www.crockford.com/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. diff --git a/branch.async_await/_config.yml b/branch.async_await/_config.yml new file mode 100644 index 000000000..c74188174 --- /dev/null +++ b/branch.async_await/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-slate \ No newline at end of file diff --git a/branch.async_await/browser.js b/branch.async_await/browser.js new file mode 100644 index 000000000..0787bbb83 --- /dev/null +++ b/branch.async_await/browser.js @@ -0,0 +1,162 @@ +// browser.js +// 2018-06-16 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +/*jslint + browser +*/ + +/*property + checked, create, disable, display, error, focus, forEach, function, + getElementById, innerHTML, join, length, map, onchange, onclick, onscroll, + property, querySelectorAll, scrollTop, select, split, style, title, value +*/ + +import jslint from "./jslint.js"; +import report from "./report.js"; + +// This is the web script companion file for JSLint. It includes code for +// interacting with the browser and displaying the reports. + +let rx_crlf = ( + /\n|\r\n?/ +); +let rx_separator = ( + /[\s,;'"]+/ +); + +let fudge_unit = 0; + +const aux = document.getElementById("JSLINT_AUX"); +const boxes = document.querySelectorAll("[type=checkbox]"); +const fudge = document.getElementById("JSLINT_FUDGE"); +const global = document.getElementById("JSLINT_GLOBAL"); +const number = document.getElementById("JSLINT_NUMBER"); +const property = document.getElementById("JSLINT_PROPERTY"); +const property_fieldset = document.getElementById("JSLINT_PROPERTYFIELDSET"); +const report_field = document.getElementById("JSLINT_REPORT"); +const report_list = document.getElementById("JSLINT_REPORT_LIST"); +const source = document.getElementById("JSLINT_SOURCE"); +const select = document.getElementById("JSLINT_SELECT"); +const warnings = document.getElementById("JSLINT_WARNINGS"); +const warnings_list = document.getElementById("JSLINT_WARNINGS_LIST"); + +function show_numbers() { + number.value = source.value.split(rx_crlf).map(function (ignore, index) { + return index + fudge_unit; + }).join("\n"); +} + +function clear() { + aux.style.display = "none"; + number.value = ""; + property.value = ""; + property_fieldset.style.display = "none"; + report_field.style.display = "none"; + report_list.innerHTML = ""; + source.focus(); + source.value = ""; + warnings.style.display = "none"; + warnings_list.innerHTML = ""; +} + +function fudge_change() { + fudge_unit = Number(fudge.checked); + show_numbers(); +} + +function clear_options() { + boxes.forEach(function (node) { + node.checked = false; + }); + fudge_change(); + global.innerHTML = ""; +} + +function call_jslint() { + +// First build the option object. + + let option = Object.create(null); + boxes.forEach(function (node) { + if (node.checked) { + option[node.title] = true; + } + }); + +// Call JSLint with the source text, the options, and the predefined globals. + + let global_string = global.value; + let result = jslint( + source.value, + option, + ( + global_string === "" + ? undefined + : global_string.split(rx_separator) + ) + ); + +// Generate the reports. + + let error_html = report.error(result); + let function_html = report.function(result); + let property_text = report.property(result); + +// Display the reports. + + warnings_list.innerHTML = error_html; + warnings.style.display = ( + error_html.length === 0 + ? "none" + : "block" + ); + + report_list.innerHTML = function_html; + report_field.style.display = "block"; + if (property_text) { + property.value = property_text; + property_fieldset.style.display = "block"; + property.scrollTop = 0; + select.disable = false; + } else { + property_fieldset.style.display = "none"; + select.disable = true; + } + aux.style.display = "block"; + source.select(); +} + +fudge.onchange = fudge_change; + +source.onchange = function (ignore) { + show_numbers(); +}; + +source.onscroll = function () { + let ss = source.scrollTop; + number.scrollTop = ss; + let sn = number.scrollTop; + if (ss > sn) { + show_numbers(); + number.scrollTop = ss; + } +}; + +document.querySelectorAll("[name='JSLint']").forEach(function (node) { + node.onclick = call_jslint; +}); + +document.querySelectorAll("[name='clear']").forEach(function (node) { + node.onclick = clear; +}); + +document.getElementById("JSLINT_SELECT").onclick = function () { + property.focus(); + property.select(); +}; +document.getElementById("JSLINT_CLEAR_OPTIONS").onclick = clear_options; + +fudge_change(); +source.select(); +source.focus(); diff --git a/branch.async_await/function.html b/branch.async_await/function.html new file mode 100644 index 000000000..7097c62e9 --- /dev/null +++ b/branch.async_await/function.html @@ -0,0 +1,615 @@ + + + + + + + + +JSLint: jslint function + + + +
    JSLint
    + +
    The jslint Function
    +
    +

    JSLint

    +

    JSLint is delivered as a set of files:

    +
    + + + + + + + + + + + + + + +
    FilenameContent
    browser.jsBrowser based UI.
    function.htmlThis page that you are looking at right now.
    help.htmlUsing jslint as a single page JSLint application.
    index.htmlSingle page JSLint application that uses the + js files.
    jslint.jsThe jslint function.
    report.jsThe report object, containing report generator functions for + HTML.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    +
    + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring"(JSLint)"
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    parenttokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    parenttokenThe function in which the variable was declared.variable
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    shebangstringThe first line of text if it started with #!line
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable may be assigned to.variable
    +

    Reports

    +

    The report object contains three functions that all take a + result from the jslint function as input. + report.js has no other dependence on other files.

    + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    +
    + +
    + + + +
    + + diff --git a/branch.async_await/help.html b/branch.async_await/help.html new file mode 100644 index 000000000..364dfe255 --- /dev/null +++ b/branch.async_await/help.html @@ -0,0 +1,826 @@ + + + + + + + + + +JSLint: Help + + + +
    JSLint
    + +
    Help
    +
    +
    Il semble que la perfection soit atteinte non quand il n’y a + plus rien à ajouter, mais quand il n’y a plus rien à + retrancher.
    +
    + Antoine de Saint-Exupéry +
    +
    + Terre des Hommes (1939) +
    +

    Good Parts

    +

    The Principle of the Good Parts is

    +
    If a feature is sometimes useful and sometimes dangerous and if + there is a better option then always use the better option.
    +

    JSLint is a JavaScript program that looks for problems in + JavaScript programs. It is a code quality tool.

    +

    When C was + a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    +

    As the language matured, the definition of the language was + strengthened to eliminate some insecurities, and compilers got better + at issuing warnings. lint is no longer needed.

    +

    JavaScript is a + young-for-its-age language. It was originally intended to do small tasks in + webpages, tasks for which Java was too heavy and clumsy. But JavaScript is + a surprisingly capable language, and it is now being used in larger + projects. Many of the features that were intended to make the language easy + to use are troublesome when projects become complicated. A lint + for JavaScript is needed: JSLint, a JavaScript syntax checker + and validator.

    +

    JSLint takes a JavaScript source and scans it. If it finds a + problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by the ECMAScript Programming Language + Standard (the strangely named document that governs JavaScript). + JSLint will reject most legal programs. It is a higher + standard.

    +

    JavaScript is a sloppy language, but hidden deep inside there is an elegant, + better language. JSLint helps you to program in that better + language and to avoid most of the slop. JSLint will reject + programs that browsers will accept because JSLint is concerned + with the quality of your code and browsers are not. You should gladly accept + all of JSLint's advice.

    +

    JSLint can operate on JavaScript source + or JSON text.

    +

    ECMAScript Sixth Edition

    +

    Some of + ES6’s features are good, so JSLint will recognize the good + parts of ES6.

    +

    Currently, these features are recognized:

    +
      +
    • The ... ellipsis marker in parameter + lists and argument lists, replacing the arguments object + for variadic functions.
    • +
    • The let statement, which is like the var + statement except that it respects block scope. + You may use let or var but not both.
    • +
    • The const statement is like the let statement + except that it disallows the use of assignment on the variable, although + if the value of the variable is mutable, it can still be mutated. + const is preferred to let. +
    • Destructuring of arrays and objects is allowed in parameter lists and on + the left side of let, and const, but not + var or assignment statements, and not deep destructuring + or eliding.
    • +
    • Enhanced object literals, providing shorter forms for function + declaration and properties that are initialized by variables with the + same name.
    • +
    • The fat arrow => fart functions.
    • +
    • The simplest forms of import and export.
    • +
    • `Megastring` literals, but not nested + `megastring` literals.
    • +
    • New global functions, such as Map, Set, + WeakMap, and WeakSet.
    • +
    • 0b- and 0o- number literals.
    • +
    +

    The most important new feature of ES6 is proper tail calls. This has no + new syntax, so JSLint doesn’t see it. But it makes recursion + much more attractive, which makes loops, particularly for + loops, much less attractive.

    +

    import export

    +

    The ES6 module feature will be an important improvement over JavaScript’s + global variables as a means of linking separate files together. + JSLint recognizes a small but essential subset of the module + syntax.

    +
    +

    import name from stringliteral;

    +

    import {name} from stringliteral;

    +

    export default function () {};

    +

    export default function name() {};

    +

    export default expression;

    +

    export function name() {}

    +

    export name; // where name is const

    +

    export {name};

    +
    +

    Directives

    +

    JSLint provides three directives that may be placed in a + file to manage JSLint’s behavior. Use of these directives is + optional. If they are used, they should be placed in a source file before + the first statement. They are written in the form of a comment, where the + directive name is placed immediately after the opening of the comment before + any whitespace. The three directives are global, + jslint, and property. Directives in a file are + stronger than options selected from the UI or passed with the option object.

    +

    /*global*/

    +

    The /*global*/ directive is used to specify a set of globals + (usually functions and objects containing functions) that are available to + this file. This was commonly used in browsers to link source files together + before ES6 modules appeared. Use of global variables is strongly + discouraged, but unfortunately web browsers require their use. The + /*global*/ directive can only be used when the + Assume a browser option is selected. +

    Each of the names listed will indicate a read-only global variable. The names + are separated by , comma. This directive + should not be used if import or export is used. + This directive inhibits warnings, but it does not declare the names in the + execution environment. +

    For example: +

    /*global +
    ADSAFE, report, jslint
    +*/
    +

    instructs JSLint to not give warnings about the global + variables ADsafe, report, and jslint. + However, if any of those names are expected to be supplied by other files + and those other files fail to do so, then execution errors will result. It + is usually better to use top-level variable declarations instead: +

        var ADSAFE;
    +    var report;
    +    var jslint; 
    +

    Using var in this way allows comparing a global variable to the + undefined value to determine whether it is has been used in the global context.

    +

    /*jslint*/

    +

    The /*jslint*/ directive allows for the control of several + options. These options can also be set from the + JSLint.com user interface.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Tolerate bitwise operatorsbitwisetrue if bitwise operators should be allowed. The + bitwise operators are rarely used in JavaScript programs, so it + is usually more likely that & is a mistyping of + && than that it indicates a bitwise and. + JSLint will give warnings on the bitwise operators + unless this option is selected.
    Assume a browser browsertrue if the standard browser globals should be + predefined. This option will reject the use of + import and export. This option also + disallows the file form of the "use + strict" pragma. It does not supply + self; you will have to request that unnecessary + alias of the dreaded global object yourself. It adds the same + globals as this directive: +
    /*global +
    + clearInterval, clearTimeout, document, event, FileReader, + FormData, history, localStorage, location, name, navigator, + screen, sessionStorage, setInterval, setTimeout, Storage, + URL, window, XMLHttpRequest +
    + */
    +
    Tolerate conversion operatorsconverttrue if !!, + prefix, + and concatenation with "" are allowed. + The Boolean, Number, and + String functions are preferred
    Assume CouchDBcouchtrue if + Couch DB + globals should be predefined. It adds the same globals as this + directive: +
    /*global +
    emit, getRow, isArray, log, provides, registerType, require, + send, start, sum, toJSON
    + */
    Assume in developmentdeveltrue if browser globals that are useful in + development should be predefined, and if debugger + statements and TODO comments + should be allowed. It adds the + same globals as this directive: +
    /*global +
    alert, confirm, console, prompt
    + */
    Be sure to turn this option off before going + into production.
    Tolerate evalevaltrue if eval should be allowed. In the + past, the eval function was the most misused + feature of the language.
    Tolerate for statementfortrue if the for statement should be + allowed. It is almost always better to use the array methods + instead.
    Fudge the line and column numbersfudgetrue if the origin of a text file is line 1 column + 1. Programmers know that number systems start at zero. + Unfortunately, most text editors start numbering lines and + columns at one. JSLint will by default start + numbering at zero. Use this option to start the numbering at one + in its reports.
    Tolerate get and setgetsettrue if accessor properties are allowed in object literals.
    Tolerate long source lineslongtrue if a line can contain more than 80 characters.
    Assume Node.jsnodetrue if Node.js globals should be predefined. It + will predefine globals that are used in the Node.js environment. + It adds the same globals as this directive: +
    /*global +
    + Buffer, clearImmediate, clearInterval, clearTimeout, console, exports, + module, process, require, setImmediate, setInterval, setTimeout, URL, + URLSearchParams, __dirname, __filename +
    + */
    Tolerate single quote stringssingletrue if 'single quote + should be allowed to enclose string literals.
    Tolerate this
    thistrue if this should be allowed.
    Tolerate whitespace messwhitetrue if the whitespace rules should be ignored.
    +

    For example:

    +
    /*jslint
    +    bitwise, node
    +*/
    + +

    /*property*/

    +

    The /*property*/ directive is used to declare a list of + property identifiers that are used in the file. Each property name in the + program is looked up in this list. If a name is not found, that indicates an + error, most likely a typing error.

    +

    The list can also be used to evade some of JSLint’s naming + rules.

    +

    JSLint can build the /*property*/ list for you. At + the bottom of its report, JSLint displays a + /*property*/ directive. It contains all of the names that were + used with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. You + can copy the /*property*/ directive to the top of your + script file. JSLint will check the spelling of all property + names against the list. That way, you can have JSLint look for + misspellings for you.

    +

    For example,

    +
    /*property +
    charAt, slice, _$_
    + */
    +

    ignore

    +

    JSLint introduces a new reserved word: ignore. It + is used in parameter lists and in catch clauses to indicate a + parameter that will be ignored. Unused warnings will not be produced for + ignore. +

    function handler(ignore, value) {
    +    return do_something_useful(value);
    +}
    +

    var let const

    +

    JavaScript provides three statements for declaring variables: + var, let, and const. The + var statement suffers from a bad practice called hoisting. The + let statement does not do hoisting and respects block scope. + The const statement is like let except that it + marks the variable (but not its contents) as read only, making it an error + to attempt to assign to the variable. When given a choice, + const is the best, var is the worst.

    +

    JSLint uses the intersection of the var rules and the + let rules, and by doing so avoids the errors related to either. + A name should be declared only once in a function. It should be declared + before it is used. It should not be used outside of the block in which it is + declared. A variable should not have the same name as a variable or + parameter in an outer function. Do not mix var and + let. Declare one name per statement.

    +

    = == ===

    +

    Fortran made a terrible + mistake in using the equality operator as its assignment operator. That + mistake has been replicated in most languages since then. C compounded that + mistake by making == its equality operator. The visual + similarity is a source of errors. JavaScript compounded this further by + making == a type coercing comparison operator that produces + false positive results. This was mitigated by adding the === + operator, leaving the broken == operator in place.

    +

    JSLint attempts to minimize errors by the following rules:

    +

    == is not allowed. This avoids the false positives, and + increases the visual distance between = and ===. + Assignments are not allowed in expression position, and comparisons are not + allowed in statement position. This also reduces confusion. 

    +

    Semicolon

    +

    JavaScript uses a C-like syntax that requires the use of semicolons to + delimit certain statements. JavaScript attempts to make those semicolons + optional with an automatic semicolon insertion mechanism, but it does not + work very well. Automatic semicolon insertion was added to make + things easier for beginners. Unfortunately, it sometimes fails. Do not rely + on it unless you are a beginner.

    +

    JSLint expects that every statement will be followed by + ; except for for, function, + if, switch, try, and + while. JSLint does not expect to see unnecessary + semicolons, the empty statement, or empty blocks.

    +

    function =>

    +

    JavaScript has four syntactic forms for making function objects: function + statements, function expressions, enhanced object literals, and the + => fart operator.

    +

    The function statement creates a variable and assigns the function object to + it. It should be used in a file or function body, but not inside of a block.

    +
    function name(parameters) { +
    statements
    + }
    +

    The function expression unfortunately looks like the function statement. It + may appear anywhere that an expression may appear, but not in statement + position and not in a loop. It produces a function object but does not + create a variable in which to store it.

    +
    function (parameters) { +
    statements
    + }
    +

    The enhanced object literal provides an ES6 shorthand for creating a property + whose value is a function, saving you from having to type + : colon and function.

    +
    { +
    name(parameters) { +
    statements
    + }
    + }
    +

    Finally, ES6 provides an even shorter form of function expression that leaves + out the words function and return:

    +
    (parameters) => + expression
    +

    JSLint requires the parens around the parameters, and forbids a + { left brace after the + => fart to avoid syntactic ambiguity.

    +

    Comma

    +

    The , comma operator is unnecessary and can + mask programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as + an operator. It does not expect to see elided elements in array literals. A + comma should not appear after the last element of an array literal or object + literal.

    +

    Blocks

    +

    JSLint expects blocks with function, + if, switch, while, for, + do, and try statements and nowhere else.

    +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    +

    JavaScript allows an if to be written like this:

    +
    if (condition)
    +    statement;
    +

    That form is known to contribute to mistakes in projects where many + programmers are working on the same code. That is why JSLint + expects the use of a block:

    +
    if (condition) {
    +    statements;
    +}
    +

    Experience shows that this form is more resilient.

    +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call. All other expression statements are considered + to be errors.

    +

    for

    +

    JSLint does not recommend use of the for statement. + Use array methods like forEach instead. The for + option will suppress some warnings. The forms of for that + JSLint accepts are restricted, excluding the new ES6 forms.

    +

    for in

    +

    JSLint does not recommend use of the for + in statement. Use Object.keys instead.

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, it also + loops through all of the properties that were inherited through the + prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written + without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    for (name in object) {
    +    if (object.hasOwnProperty(name)) {
    +        ....
    +    }
    +}
    +

    Note that the above code will fail if the object contains a property + named hasOwnProperty. Use Object.keys instead.

    +

    switch

    +

    A common error in switch statements is to forget to place a + break statement after each case, resulting in unintended + fall-through. JSLint expects that the statement before the next + case or default is one of these: + break, return, or throw.

    +

    with

    +

    The with statement was intended to provide a shorthand in + accessing properties in deeply nested objects. Unfortunately, it behaves + very badly when setting new properties. Never use the with + statement.

    +

    JSLint does not expect to see a with statement.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that + labels will be distinct from vars and parameters.

    +

    Unreachable Code

    +

    JSLint expects that a return, break, + or throw statement will be followed by a + } right brace or case or + default.

    +

    Confusing Pluses and Minuses

    +

    JSLint expects that + will not be followed by + + or ++, and that - will not be + followed by - or --. A misplaced space can turn + + + into ++, an error that is difficult to see. + Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ increment and + -- decrement operators have been known to + contribute to bad code by encouraging excessive trickiness. They are second + only to faulty architecture in enabling to viruses and other security + menaces. Also, preincrement/postincrement confusion can produce off-by-one + errors that are extremely difficult to diagnose. Fortunately, they are also + complete unnecessary. There are better ways to add 1 to a variable.

    +

    It is best to avoid these operators entirely and rely on += and + -= instead.

    +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. + JSLint looks for problems that may cause portability problems. + It also attempts to resolve visual ambiguities by recommending explicit + escapement.

    +

    JavaScript’s syntax for regular expression literals overloads the + / slash character. To avoid ambiguity, + JSLint expects that the character preceding a regular + expression literal is a ( left paren or + = equal or + : colon or + , comma character.

    +

    this

    +
    Having this in the language makes it harder to talk + about the language. It is like pair programming with Abbott and Costello. +
    +

    Avoid using this. Warnings about this can be + suppressed with option.this.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the + new prefix. The new prefix creates a new object + based on the function's prototype, and binds that object to the + function's implied this parameter. If you neglect to use the + new prefix, no new object will be made and this + will be bound to the global object.

    +

    JSLint enforces the convention that constructor functions be + given names with initial uppercase. JSLint does not expect to + see a function invocation with an initial uppercase name unless it has the + new prefix. JSLint does not expect to see the + new prefix used with functions whose names do not start with + initial uppercase.

    +

    JSLint does not expect to see the wrapper forms + new Number, new String, new Boolean.

    +

    JSLint does not expect to see new Object. + Use Object.create(null) instead.

    +

    Whitespace

    +

    JSLint has a specific set of rules about the use of whitespace. + Where possible, these rules are consistent with centuries of good practice + with literary style.

    +

    The indentation increases by 4 spaces when the last token on a line is + { left brace, + [ left bracket, + ( left paren. The matching closing + token will be the first token on a line, restoring the previous + indentation.

    +

    The ternary operator can be visually confusing, so + ? question mark and + : colon always begin a line and increase + the indentation by 4 spaces.

    +
    return (
    +    (the_token.id === "(string)" || the_token.id === "(number)")
    +    ? String(the_token.value)
    +    : the_token.id
    +);
    +

    The word function is always followed with one space.

    +

    Clauses (case, catch, default, + else, finally) are not statements and so should + not be indented like statements.

    +

    Spaces are used to make things that are not invocations look less like + invocations.

    +

    Tabs and spaces should not be mixed. We should pick just one in order to + avoid the problems that come from having both. Personal preference is an + extremely unreliable criterion. Neither offers a powerful advantage over the + other. Fifty years ago, tab had the advantage of consuming less memory, but + Moore's Law has eliminated that advantage. Space has one clear advantage + over tab: there is no reliable standard for how many spaces a tab + represents, but it is universally accepted that a space occupies a space. So + use spaces. You can edit with tabs if you must, but make sure it is spaces + again before you commit. Maybe someday we will finally get a universal + standard for tabs, but until that day comes, the better choice is spaces.

    +

    Unsafe Characters

    +

    There are characters that are handled inconsistently in some browsers, and + so must be escaped when placed in strings.

    +
    \u0000-\u001f
    +\u007f-\u009f
    +\u00ad
    +\u0600-\u0604
    +\u070f
    +\u17b4
    +\u17b5
    +\u200c-\u200f
    +\u2028-\u202f
    +\u2060-\u206f
    +\ufeff
    +\ufff0-\uffff
    +

    Report

    +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    +
      +
    • The line number on which it starts.
    • +
    • The function’s name. In the case of anonymous functions, + JSLint will attempt to + «guess» the name.
    • +
    • The parameters.
    • +
    • Variables: The variables that are declared in the function.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Exceptions: The variables that are declared by catch + clauses of try statements.
    • +
    • Outer: Variables used by this function that are declared in + other functions.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements.

    +

    Try it

    +

    Try it. Paste your script + into the window and click the + + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    JSLint is written entirely in JavaScript, so it can run + anywhere that JavaScript (or even Java) can run.

    +

    Perfectly fine

    +

    JSLint was designed to reject code that some would consider to + be perfectly fine. The reason for this is that JSLint's + purpose is to help produce programs that are free of error. That is + difficult in any language and is especially hard in JavaScript. + JSLint attempts to help you increase the visual distance + between correct programs and incorrect programs, making the remaining errors + more obvious. JSLint will give warnings about things that are + not necessarily wrong in the current situation, but which have been observed + to mask or obscure errors. Avoid those things when there are better options + available.

    +

    It is dangerous out there. JSLint is here to help.

    +

    Warning

    +

    JSLint will hurt your feelings. Side effects may include + headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, + dry mouth, cleaner code, and a reduced error rate.

    +

    Further Reading

    + +
    +

    + Code Conventions for the JavaScript Programming Language.

    + + + +
    + + diff --git a/branch.async_await/index.html b/branch.async_await/index.html new file mode 100644 index 000000000..2cfb6b93d --- /dev/null +++ b/branch.async_await/index.html @@ -0,0 +1,146 @@ + + + + + + + + + + + +JSLint: The JavaScript Code Quality Tool + + +
    +
    Source
    +
    + + +
    +
    + Warnings +
    +
    +
    + Function Report +
    +
    +
    + Property Directive + +
    +
    + + + + +
    +
    Options +
    Assume... +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Global variables... + +
    +
    +
    +
    + + + + +
    + + diff --git a/branch.async_await/jslint.css b/branch.async_await/jslint.css new file mode 100644 index 000000000..cc3bf1696 --- /dev/null +++ b/branch.async_await/jslint.css @@ -0,0 +1,341 @@ +@font-face { + font-family: 'Programma'; + font-weight: bold; + src: url('Programma-Bold.woff2') format('woff2'); +} +@font-face { + font-family: 'Daley'; + font-weight: bold; + src: url('Daley-Bold.woff2') format('woff2'); +} + +body { + background-color: antiquewhite; + color: black; + font-family: 'Daley', sans-serif; + margin: 0; + padding: 0; +} + +#JSLINT_TITLEBOX { + font-family: 'Daley', sans-serif; + margin: 0; + margin-left: 3%; + margin-right: 3%; + padding: 0; +} + +#JSLINT_TITLEBOX ul { + float: right; + font-size: 12pt; +} + +#JSLINT_TITLEBOX div { + color: darkslategray; + float: left; + font-size: 48pt; +} + +#JSLINT_ fieldset { + background-color: gainsboro; + border: 0; + clear: both; + margin-bottom: 1em; + margin-left: 3%; + margin-right: 3%; + margin-top: 1em; + padding: 0; + width: auto; +} +#JSLINT_ legend { + background-color: darkslategray; + border: 0; + color: white; + font-size: 100%; + font-style: normal; + font-weight: normal; + margin: 0; + padding-bottom: 0.25em; + padding-left: 0; + padding-right: 0; + padding-top: 0.25em; + text-align: center; + width: 100%; +} +#JSLINT_ button { + background-color: darkslategray; + border: 0; + color: white; + cursor: pointer; + font-family: 'Daley', sans-serif; + font-size: 100%; + font-style: normal; + margin-left: 1em; + margin-right: 1em; + padding-left: 1em; + padding-right: 1em; + padding-bottom: 0.25em; + padding-top: 0.25em; + text-align: center; +} +#JSLINT_ button:hover { + background-color: slategray; + color: white; + border: 0; +} + +#JSLINT_ button:active { + border: 0; + color: black; +} + +#JSLINT_ button:disabled { + background-color: gray; + border: 0; + color: gainsboro; +} + +a:visited { + color: #69565c; +} + +a:link { + color: black; +} + +#JSLINT_ input[type="text"] { + background-color: white; + border: 0; + color: black; + margin-bottom: 0.2em; + margin-right: 0.5em; + padding-left: 0.5em; + padding-right: 0.5em; + text-align: right; + width: 3em; +} + +#JSLINT_ textarea { + border: 0; + background-color: white; + border: 0; + font-family: Programma, monospace; + font-size: 100%; + font-weight: bold; + resize: none; +} + +#JSLINT_ textarea::selection { + background-color: yellow; + color: black; +} + +#JSLINT_NUMBER { + color: darkgray; + display: inline-block; + height: 4in; + margin: 2%; + margin-right: 0; + overflow: hidden; + padding-right: 0.5%; + text-align: right; + white-space: pre; + width: 4%; +} + +#JSLINT_SOURCE { + color: black; + display: inline-block; + font-size: 100%; + height: 4in; + margin: 2%; + margin-left: 0; + margin-right: 0; + overflow: auto; + padding-left: 0.5%; + white-space: pre; + width: 91%; +} + +#JSLINT_PROPERTY { + background-color: honeydew; + border: 0; + margin: 2%; + padding: 0.5%; + white-space: pre; + width: 95%; +} + +#JSLINT_GLOBAL { + white-space: normal; + width: 95%; +} +#JSLINT_ label { + font-family: sans-serif; + font-size: 90%; + padding-left: 0.25em; +} +#JSLINT_OPTIONS>div { + float: left; + margin: 0.5em; +} + +#JSLINT_ address { + color: black; + display: block; + float: right; + font-family: serif; + font-size: 90%; + margin-left: 1em; +} +#JSLINT_ dl { + background-color: cornsilk; + color: white; + margin: 0; + padding-bottom: 2pt; + padding-left: 1em; + padding-right: 1em; + padding-top: 2pt; +} +#JSLINT_ dfn { + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + margin-bottom: 2pt; +} +#JSLINT_ dt { + color: black; + display: block; + float: left; + font-family: serif; + font-size: 75%; + font-style: italic; + margin: 0; + width: 8em; + text-align: right; +} +#JSLINT_ dd { + color: black; + display: block; + font-family: Programma, monospace; + font-weight: bold; + margin-left: 8em; + padding-bottom: 2pt; +} +#JSLINT_WARNINGS>legend { + background-color: indianred; +} +#JSLINT_WARNINGS>div { + background-color: pink; + padding: 1em; +} +#JSLINT_WARNINGS cite { + color: black; + display: block; + font-family: serif; + font-size: 100%; + font-style: normal; + margin-bottom: 4pt; + margin-left: 20pt; + margin-right: 20pt; + margin-top: 4pt; + overflow-x: hidden; +} +#JSLINT_WARNINGS samp { + background-color: lavenderblush; + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + padding: 4pt; + margin-bottom: 0; + margin-left: 16pt; + margin-right: 16pt; + margin-top: 0; + white-space: pre-wrap; +} + +#JSLINT_REPORT center { + margin-top: 1em; +} + +#JSLINT_WARNINGS dl address { + color: black; + display: inline; + float: none; + font-size: 80%; + margin: 0; +} + +#JSLINT_REPORT>div { + padding: 1em; +} + +#JSLINT_ dl.level0 { + color: black; + background-color: white; +} + +#JSLINT_ dl.level1 { + color: black; + background-color: #ffffe0; /* yellow */ + margin-left: 1em; +} + +#JSLINT_ dl.level2 { + color: black; + background-color: #e0ffe0; /* green */ + margin-left: 2em; +} + +#JSLINT_ dl.level3 { + color: black; + background-color: #D0D0ff; /* blue */ + margin-left: 3em; +} + +#JSLINT_ dl.level4 { + background-color: #ffe0ff; /* purple */ + color: black; + margin-left: 4em; +} + +#JSLINT_ dl.level5 { + background-color: #ffe0e0; /* red */ + color: black; + margin-left: 5em; +} + +#JSLINT_ dl.level6 { + background-color: #ffe390; /* orange */ + color: black; + margin-left: 6em; +} + +#JSLINT_ dl.level7 { + background-color: #e0e0e0; /* gray */ + color: black; + margin-left: 7em; +} + +#JSLINT_ dl.level8 { + color: black; + margin-left: 8em; +} + +#JSLINT_ dl.level9 { + color: black; + margin-left: 9em; +} + +.none { + display: none; +} +.center { + text-align: center; +} diff --git a/branch.async_await/jslint.js b/branch.async_await/jslint.js new file mode 100644 index 000000000..8903f5eee --- /dev/null +++ b/branch.async_await/jslint.js @@ -0,0 +1,4976 @@ +// jslint.js +// 2019-08-03 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// jslint(source, option_object, global_array) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze, a string or an array of strings. +// option_object An object whose keys correspond to option names. +// global_array An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all of the functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// 1. If the source is a single string, split it into an array of strings. +// 2. Turn the source into an array of tokens. +// 3. Furcate the tokens into a parse tree. +// 4. Walk the tree, traversing all of the nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// 5. Check the whitespace between the tokens. + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*property + a, and, arity, assign, b, bad_assignment_a, bad_directive_a, bad_get, + bad_module_name_a, bad_option_a, bad_property_a, bad_set, bitwise, block, + body, browser, c, calls, catch, charCodeAt, closer, closure, code, column, + concat, constant, context, convert, couch, create, d, dead, default, devel, + directive, directives, disrupt, dot, duplicate_a, edition, ellipsis, else, + empty_block, escape_mega, eval, every, expected_a, expected_a_at_b_c, + expected_a_b, expected_a_b_from_c_d, expected_a_before_b, + expected_a_next_at_b, expected_digits_after_a, expected_four_digits, + expected_identifier_a, expected_line_break_a_b, expected_regexp_factor_a, + expected_space_a_b, expected_statements_a, expected_string_a, + expected_type_string_a, exports, expression, extra, finally, flag, for, + forEach, free, freeze, freeze_exports, from, froms, fud, fudge, + function_in_loop, functions, g, getset, global, i, id, identifier, import, + inc, indexOf, infix_in, init, initial, isArray, isNaN, join, json, keys, + label, label_a, lbp, led, length, level, line, lines, live, long, loop, m, + margin, match, message, misplaced_a, misplaced_directive_a, missing_browser, + missing_m, module, naked_block, name, names, nested_comment, new, node, + not_label_a, nr, nud, number_isNaN, ok, open, opening, option, + out_of_scope_a, parameters, parent, pop, property, push, quote, + redefinition_a_b, replace, required_a_optional_b, reserved_a, role, search, + shebang, signature, single, slice, some, sort, split, startsWith, statement, + stop, subscript_a, switch, test, this, thru, toString, todo_comment, + tokens, too_long, too_many_digits, tree, try, type, u, unclosed_comment, + unclosed_mega, unclosed_string, undeclared_a, unexpected_a, + unexpected_a_after_b, unexpected_a_before_b, unexpected_at_top_level_a, + unexpected_char_a, unexpected_comment, unexpected_directive_a, + unexpected_expression_a, unexpected_label_a, unexpected_parens, + unexpected_space_a_b, unexpected_statement_a, unexpected_trailing_space, + unexpected_typeof_a, uninitialized_a, unreachable_a, + unregistered_property_a, unsafe, unused_a, use_double, use_open, use_spaces, + used, value, var_loop, var_switch, variable, warning, warnings, + weird_condition_a, weird_expression_a, weird_loop, weird_relation_a, white, + wrap_condition, wrap_immediate, wrap_parameter, wrap_regexp, wrap_unary, + wrapped, writable, y +*/ + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than '{}' because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +function populate(array, object = empty(), value = true) { + +// Augment an object by taking property names from an array of strings. + + array.forEach(function (name) { + object[name] = value; + }); + return object; +} + +const allowed_option = { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + bitwise: true, + browser: [ + "caches", "clearInterval", "clearTimeout", "document", "DOMException", + "Element", "Event", "event", "FileReader", "FormData", "history", + "localStorage", "location", "MutationObserver", "name", "navigator", + "screen", "sessionStorage", "setInterval", "setTimeout", "Storage", + "TextDecoder", "TextEncoder", "URL", "window", "Worker", + "XMLHttpRequest" + ], + couch: [ + "emit", "getRow", "isArray", "log", "provides", "registerType", + "require", "send", "start", "sum", "toJSON" + ], + convert: true, + devel: [ + "alert", "confirm", "console", "prompt" + ], + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + node: [ + "Buffer", "clearImmediate", "clearInterval", "clearTimeout", + "console", "exports", "module", "process", "require", + "setImmediate", "setInterval", "setTimeout", "TextDecoder", + "TextEncoder", "URL", "URLSearchParams", "__dirname", "__filename" + ], + single: true, + this: true, + white: true +}; + +const anticondition = populate([ + "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%", + "typeof", "(number)", "(string)" +]); + +// These are the bitwise operators. + +const bitwiseop = populate([ + "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=", + ">>>", ">>>=" +]); + +const escapeable = populate([ + "\\", "/", "`", "b", "f", "n", "r", "t" +]); + +const opener = { + +// The open and close pairs. + + "(": ")", // paren + "[": "]", // bracket + "{": "}", // brace + "${": "}" // mega +}; + +// The relational operators. + +const relationop = populate([ + "!=", "!==", "==", "===", "<", "<=", ">", ">=" +]); + +// This is the set of infix operators that require a space on each side. + +const spaceop = populate([ + "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/", + "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=", + ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" +]); + +const standard = [ + +// These are the globals that are provided by the language standard. + + "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI", + "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error", + "EvalError", "Float32Array", "Float64Array", "Generator", + "GeneratorFunction", "Int8Array", "Int16Array", "Int32Array", "Intl", + "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat", + "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", + "Set", "String", "Symbol", "SyntaxError", "System", "TypeError", + "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", + "URIError", "WeakMap", "WeakSet" +]; + +const bundle = { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + and: "The '&&' subexpression should be wrapped in parens.", + bad_assignment_a: "Bad assignment to '{a}'.", + bad_directive_a: "Bad directive '{a}'.", + bad_get: "A get function takes no parameters.", + bad_module_name_a: "Bad module name '{a}'.", + bad_option_a: "Bad option '{a}'.", + bad_property_a: "Bad property name '{a}'.", + bad_set: "A set function takes one parameter.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + escape_mega: "Unexpected escapement in mega literal.", + expected_a: "Expected '{a}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: ( + "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'." + ), + expected_a_before_b: "Expected '{a}' before '{b}'.", + expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.", + expected_digits_after_a: "Expected digits after '{a}'.", + expected_four_digits: "Expected four digits after '\\u'.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.", + expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.", + expected_space_a_b: "Expected one space between '{a}' and '{b}'.", + expected_statements_a: "Expected statements before '{a}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + expected_type_string_a: "Expected a type string and instead saw '{a}'.", + freeze_exports: ( + "Expected 'Object.freeze('. All export values should be frozen." + ), + function_in_loop: "Don't make functions within a loop.", + infix_in: ( + "Unexpected 'in'. Compare with undefined, " + + "or use the hasOwnProperty method instead." + ), + label_a: "'{a}' is a statement label.", + misplaced_a: "Place '{a}' at the outermost level.", + misplaced_directive_a: ( + "Place the '/*{a}*/' directive before the first statement." + ), + missing_browser: "/*global*/ requires the Assume a browser option.", + missing_m: "Expected 'm' flag on a multiline regular expression.", + naked_block: "Naked block.", + nested_comment: "Nested comment.", + not_label_a: "'{a}' is not a label.", + number_isNaN: "Use Number.isNaN function to compare with NaN.", + out_of_scope_a: "'{a}' is out of scope.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + required_a_optional_b: ( + "Required parameter '{a}' after optional parameter '{b}'." + ), + reserved_a: "Reserved name '{a}'.", + subscript_a: "['{a}'] is better written in dot notation.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line is longer than 80 characters.", + too_many_digits: "Too many digits.", + unclosed_comment: "Unclosed comment.", + unclosed_mega: "Unclosed mega literal.", + unclosed_string: "Unclosed string.", + undeclared_a: "Undeclared '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_a_after_b: "Unexpected '{a}' after '{b}'.", + unexpected_a_before_b: "Unexpected '{a}' before '{b}'.", + unexpected_at_top_level_a: "Expected '{a}' to be in a function.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_directive_a: "When using modules, don't use directive '/*{a}'.", + unexpected_expression_a: ( + "Unexpected expression '{a}' in statement position." + ), + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_parens: "Don't wrap function literals in parens.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_statement_a: ( + "Unexpected statement '{a}' in expression position." + ), + unexpected_trailing_space: "Unexpected trailing space.", + unexpected_typeof_a: ( + "Unexpected 'typeof'. Use '===' to compare directly with {a}." + ), + uninitialized_a: "Uninitialized '{a}'.", + unreachable_a: "Unreachable '{a}'.", + unregistered_property_a: "Unregistered property name '{a}'.", + unsafe: "Unsafe character '{a}'.", + unused_a: "Unused '{a}'.", + use_double: "Use double quotes, not single quotes.", + use_open: ( + "Wrap a ternary expression in parens, " + + "with a line break after the left paren." + ), + use_spaces: "Use spaces, not tabs.", + var_loop: "Don't declare variables in a loop.", + var_switch: "Don't declare variables in a switch.", + weird_condition_a: "Weird condition '{a}'.", + weird_expression_a: "Weird expression '{a}'.", + weird_loop: "Weird loop.", + weird_relation_a: "Weird relation '{a}'.", + wrap_condition: "Wrap the condition in parens.", + wrap_immediate: ( + "Wrap an immediate function invocation in parentheses to assist " + + "the reader in understanding that the expression is the result " + + "of a function, and not the function itself." + ), + wrap_parameter: "Wrap the parameter in parens.", + wrap_regexp: "Wrap this regexp in parens to avoid confusion.", + wrap_unary: "Wrap the unary expression in parens." +}; + +// Regular expression literals: + +// supplant {variables} +const rx_supplant = /\{([^{}]*)\}/g; +// carriage return, carriage return linefeed, or linefeed +const rx_crlf = /\n|\r\n?/; +// unsafe characters that are silently deleted by one or more browsers +const rx_unsafe = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; +// identifier +const rx_identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; +const rx_module = /^[a-zA-Z0-9_$:.@\-\/]+$/; +const rx_bad_property = /^_|\$|Sync\$|_$/; +// star slash +const rx_star_slash = /\*\//; +// slash star +const rx_slash_star = /\/\*/; +// slash star or ending slash +const rx_slash_star_or_slash = /\/\*|\/$/; +// uncompleted work comment +const rx_todo = /\b(?:todo|TO\s?DO|HACK)\b/; +// tab +const rx_tab = /\t/g; +// directive +const rx_directive = /^(jslint|property|global)\s+(.*)$/; +const rx_directive_part = /^([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*(.*)$/; +// token (sorry it is so long) +const rx_token = /^((\s+)|([a-zA-Z_$][a-zA-Z0-9_$]*)|[(){}\[\],:;'"~`]|\?\.?|=(?:==?|>)?|\.+|[*\/][*\/=]?|\+[=+]?|-[=\-]?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<= "a" && string <= "z\uffff") + || (string >= "A" && string <= "Z\uffff") + ); +} + +function supplant(string, object) { + return string.replace(rx_supplant, function (found, filling) { + const replacement = object[filling]; + return ( + replacement !== undefined + ? replacement + : found + ); + }); +} + +let anon; // The guessed name for anonymous functions. +let blockage; // The current block. +let block_stack; // The stack of blocks. +let declared_globals; // The object containing the global declarations. +let directives; // The directive comments. +let directive_mode; // true if directives are still allowed. +let early_stop; // true if JSLint cannot finish. +let exports; // The exported names and values. +let froms; // The array collecting all import-from strings. +let fudge; // true if the natural numbers start with 1. +let functionage; // The current function. +let functions; // The array containing all of the functions. +let global; // The global object; the outermost context. +let json_mode; // true if parsing JSON. +let lines; // The array containing source lines. +let mega_mode; // true if currently parsing a megastring literal. +let module_mode; // true if import or export was used. +let next_token; // The next token to be examined in the parse. +let option; // The options parameter. +let property; // The object containing the tallied property names. +let shebang; // true if a #! was seen on the first line. +let stack; // The stack of functions. +let syntax; // The object containing the parser. +let token; // The current token being examined in the parse. +let token_nr; // The number of the next token. +let tokens; // The array of tokens. +let tenure; // The predefined property registry. +let tree; // The abstract parse tree. +let var_mode; // "var" if using var; "let" if using let. +let warnings; // The array collecting all generated warnings. + +// Error reportage functions: + +function artifact(the_token) { + +// Return a string representing an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); +} + +function artifact_line(the_token) { + +// Return the fudged line number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.line + fudge; +} + +function artifact_column(the_token) { + +// Return the fudged column number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.from + fudge; +} + +function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + const warning = { // ~~ + name: "JSLintError", + column, + line, + code + }; + if (a !== undefined) { + warning.a = a; + } + if (b !== undefined) { + warning.b = b; + } + if (c !== undefined) { + warning.c = c; + } + if (d !== undefined) { + warning.d = d; + } + warning.message = supplant(bundle[code] || code, warning); + warnings.push(warning); + return warning; +} + +function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); +} + +function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + if (the_token.warning === undefined) { + the_token.warning = warn_at( + code, + the_token.line, + the_token.from, + a || artifact(the_token), + b, + c, + d + ); + return the_token.warning; + } +} + +function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); +} + +// Tokenize: + +function tokenize(source) { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + +// If the source is not an array, then it is split into lines at the +// carriage return/linefeed. + + lines = ( + Array.isArray(source) + ? source + : source.split(rx_crlf) + ); + tokens = []; + + let char; // a popular character + let column = 0; // the column number of the next character + let first; // the first token + let from; // the starting column number of the token + let line = -1; // the line number of the next character + let nr = 0; // the next token number + let previous = global; // the previous token including comments + let prior = global; // the previous token excluding comments + let mega_from; // the starting column of megastring + let mega_line; // the starting line of megastring + let regexp_seen; // regular expression literal seen on this line + let snippet; // a piece of string + let source_line = ""; // the remaining line source string + let whole_line = ""; // the whole line source string + + if (lines[0].startsWith("#!")) { + line = 0; + shebang = true; + } + + function next_line() { + +// Put the next line of source in source_line. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + let at; + if ( + !option.long + && whole_line.length > 80 + && !json_mode + && first + && !regexp_seen + ) { + warn_at("too_long", line, 80); + } + column = 0; + line += 1; + regexp_seen = false; + source_line = lines[line]; + whole_line = source_line || ""; + if (source_line !== undefined) { + at = source_line.search(rx_tab); + if (at >= 0) { + if (!option.white) { + warn_at("use_spaces", line, at + 1); + } + source_line = source_line.replace(rx_tab, " "); + } + at = source_line.search(rx_unsafe); + if (at >= 0) { + warn_at( + "unsafe", + line, + column + at, + "U+" + source_line.charCodeAt(at).toString(16) + ); + } + if (!option.white && source_line.slice(-1) === " ") { + warn_at( + "unexpected_trailing_space", + line, + source_line.length - 1 + ); + } + } + return source_line; + } + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions snip, next_char, prev_char, +// some_digits, and escape help in the parsing of literals. + + function snip() { + +// Remove the last character from snippet. + + snippet = snippet.slice(0, -1); + } + + function next_char(match) { + +// Get the next character from the source line. Remove it from the source_line, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + return stop_at( + ( + char === "" + ? "expected_a" + : "expected_a_b" + ), + line, + column - 1, + match, + char + ); + } + if (source_line) { + char = source_line[0]; + source_line = source_line.slice(1); + snippet += char; + } else { + char = ""; + snippet += " "; + } + column += 1; + return char; + } + + function back_char() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the source_line. + + if (snippet) { + char = snippet.slice(-1); + source_line = char + source_line; + column -= 1; + snip(); + } else { + char = ""; + } + return char; + } + + function some_digits(rx, quiet) { + const result = source_line.match(rx); + if (result) { + char = result[1]; + column += char.length; + source_line = result[2]; + snippet += char; + } else { + char = ""; + if (!quiet) { + warn_at( + "expected_digits_after_a", + line, + column, + snippet + ); + } + } + return char.length; + } + + function escape(extra) { + next_char("\\"); + if (escapeable[char] === true) { + return next_char(); + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "u") { + if (next_char("u") === "{") { + if (json_mode) { + warn_at("unexpected_a", line, column - 1, char); + } + if (some_digits(rx_hexs) > 5) { + warn_at("too_many_digits", line, column - 1); + } + if (next_char() !== "}") { + stop_at("expected_a_before_b", line, column, "}", char); + } + return next_char(); + } + back_char(); + if (some_digits(rx_hexs, true) < 4) { + warn_at("expected_four_digits", line, column - 1); + } + return; + } + if (extra && extra.indexOf(char) >= 0) { + return next_char(); + } + warn_at("unexpected_a_before_b", line, column - 2, "\\", char); + } + + function make(id, value, identifier) { + +// Make the token object and append it to the tokens list. + + const the_token = { + from, + id, + identifier: Boolean(identifier), + line, + nr, + thru: column + }; + tokens[nr] = the_token; + nr += 1; + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + directive_mode = false; + } + +// If the token is to have a value, give it one. + + if (value !== undefined) { + the_token.value = value; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option.white. + + if ( + previous.line === line + && previous.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (previous.id === "(comment)" || previous.id === "(regexp)") + ) { + warn( + "expected_space_a_b", + the_token, + artifact(previous), + artifact(the_token) + ); + } + if (previous.id === "." && id === "(number)") { + warn("expected_a_before_b", previous, "0", "."); + } + if (prior.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// The previous token is used to detect adjacency problems. + + previous = the_token; + +// The prior token is a previous token that was not a comment. The prior token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (previous.id !== "(comment)") { + prior = previous; + } + return the_token; + } + + function parse_directive(the_comment, body) { + +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + + const result = body.match(rx_directive_part); + if (result) { + let allowed; + const name = result[1]; + const value = result[2]; + if (the_comment.directive === "jslint") { + allowed = allowed_option[name]; + if ( + typeof allowed === "boolean" + || typeof allowed === "object" + ) { + if ( + value === "" + || value === "true" + || value === undefined + ) { + option[name] = true; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } else if (value === "false") { + option[name] = false; + } else { + warn("bad_option_a", the_comment, name + ":" + value); + } + } else { + warn("bad_option_a", the_comment, name); + } + } else if (the_comment.directive === "property") { + if (tenure === undefined) { + tenure = empty(); + } + tenure[name] = true; + } else if (the_comment.directive === "global") { + if (value) { + warn("bad_option_a", the_comment, name + ":" + value); + } + declared_globals[name] = false; + module_mode = the_comment; + } + return parse_directive(the_comment, result[3]); + } + if (body) { + return stop("bad_directive_a", the_comment, body); + } + } + + function comment(snippet) { + +// Make a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + + const the_comment = make("(comment)", snippet); + if (Array.isArray(snippet)) { + snippet = snippet.join(" "); + } + if (!option.devel && rx_todo.test(snippet)) { + warn("todo_comment", the_comment); + } + const result = snippet.match(rx_directive); + if (result) { + if (!directive_mode) { + warn_at("misplaced_directive_a", line, from, result[1]); + } else { + the_comment.directive = result[1]; + parse_directive(the_comment, result[2]); + } + directives.push(the_comment); + } + return the_comment; + } + + function regexp() { + +// Parse a regular expression literal. + + let multi_mode = false; + let result; + let value; + regexp_seen = true; + + function quantifier() { + +// Match an optional quantifier. + + if (char === "?" || char === "*" || char === "+") { + next_char(); + } else if (char === "{") { + if (some_digits(rx_digits, true) === 0) { + warn_at("expected_a", line, column, "0"); + } + if (next_char() === ",") { + some_digits(rx_digits, true); + next_char(); + } + next_char("}"); + } else { + return; + } + if (char === "?") { + next_char("?"); + } + } + + function subklass() { + +// Match a character in a character class. + + if (char === "\\") { + escape("BbDdSsWw-[]^"); + return true; + } + if ( + char === "" + || char === "[" + || char === "]" + || char === "/" + || char === "^" + || char === "-" + ) { + return false; + } + if (char === " ") { + warn_at("expected_a_b", line, column, "\\u0020", " "); + } else if (char === "`" && mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char(); + return true; + } + + function ranges() { + +// Match a range of subclasses. + + if (subklass()) { + if (char === "-") { + next_char("-"); + if (!subklass()) { + return stop_at( + "unexpected_a", + line, + column - 1, + "-" + ); + } + } + return ranges(); + } + } + + function klass() { + +// Match a class. + + next_char("["); + if (char === "^") { + next_char("^"); + } + (function classy() { + ranges(); + if (char !== "]" && char !== "") { + warn_at( + "expected_a_before_b", + line, + column, + "\\", + char + ); + next_char(); + return classy(); + } + }()); + next_char("]"); + } + + function choice() { + + function group() { + +// Match a group that starts with left paren. + + next_char("("); + if (char === "?") { + next_char("?"); + if (char === "=" || char === "!") { + next_char(); + } else { + next_char(":"); + } + } else if (char === ":") { + warn_at("expected_a_before_b", line, column, "?", ":"); + } + choice(); + next_char(")"); + } + + function factor() { + if ( + char === "" + || char === "/" + || char === "]" + || char === ")" + ) { + return false; + } + if (char === "(") { + group(); + return true; + } + if (char === "[") { + klass(); + return true; + } + if (char === "\\") { + escape("BbDdSsWw^${}[]():=!.-|*+?"); + return true; + } + if ( + char === "?" + || char === "+" + || char === "*" + || char === "}" + || char === "{" + ) { + warn_at( + "expected_a_before_b", + line, + column - 1, + "\\", + char + ); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column - 1, "`"); + } + } else if (char === " ") { + warn_at( + "expected_a_b", + line, + column - 1, + "\\s", + " " + ); + } else if (char === "$") { + if (source_line[0] !== "/") { + multi_mode = true; + } + } else if (char === "^") { + if (snippet !== "^") { + multi_mode = true; + } + } + next_char(); + return true; + } + + function sequence(follow) { + if (factor()) { + quantifier(); + return sequence(true); + } + if (!follow) { + warn_at("expected_regexp_factor_a", line, column, char); + } + } + +// Match a choice (a sequence that can be followed by | and another choice). + + sequence(); + if (char === "|") { + next_char("|"); + return choice(); + } + } + +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + next_char(); + if (char === "=") { + warn_at("expected_a_before_b", line, column, "\\", "="); + } + choice(); + +// Make sure there is a closing slash. + + snip(); + value = snippet; + next_char("/"); + +// Process dangling flag letters. + + const allowed = { + g: true, + i: true, + m: true, + u: true, + y: true + }; + const flag = empty(); + (function make_flag() { + if (is_letter(char)) { + if (allowed[char] !== true) { + warn_at("unexpected_a", line, column, char); + } + allowed[char] = false; + flag[char] = true; + next_char(); + return make_flag(); + } + }()); + back_char(); + if (char === "/" || char === "*") { + return stop_at("unexpected_a", line, from, char); + } + result = make("(regexp)", char); + result.flag = flag; + result.value = value; + if (multi_mode && !flag.m) { + warn_at("missing_m", line, column); + } + return result; + } + + function string(quote) { + +// Make a string token. + + let the_token; + snippet = ""; + next_char(); + + return (function next() { + if (char === quote) { + snip(); + the_token = make("(string)", snippet); + the_token.quote = quote; + return the_token; + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "\\") { + escape(quote); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char("`"); + } else { + next_char(); + } + return next(); + }()); + } + + function frack() { + if (char === ".") { + some_digits(rx_digits); + next_char(); + } + if (char === "E" || char === "e") { + next_char(); + if (char !== "+" && char !== "-") { + back_char(); + } + some_digits(rx_digits); + next_char(); + } + } + + function number() { + if (snippet === "0") { + next_char(); + if (char === ".") { + frack(); + } else if (char === "b") { + some_digits(rx_bits); + next_char(); + } else if (char === "o") { + some_digits(rx_octals); + next_char(); + } else if (char === "x") { + some_digits(rx_hexs); + next_char(); + } + } else { + next_char(); + frack(); + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + return stop_at( + "unexpected_a_after_b", + line, + column - 1, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + back_char(); + return make("(number)", snippet); + } + + function lex() { + let array; + let i = 0; + let j = 0; + let last; + let result; + let the_token; + +// This should properly be a tail recursive function, but sadly, conformant +// implementations of ES6 are still rare. This is the ideal code: + +// if (!source_line) { +// source_line = next_line(); +// from = 0; +// return ( +// source_line === undefined +// ? ( +// mega_mode +// ? stop_at("unclosed_mega", mega_line, mega_from) +// : make("(end)") +// ) +// : lex() +// ); +// } + +// Unfortunately, incompetent JavaScript engines will sometimes fail to execute +// it correctly. So for now, we do it the old fashioned way. + + while (!source_line) { + source_line = next_line(); + from = 0; + if (source_line === undefined) { + return ( + mega_mode + ? stop_at("unclosed_mega", mega_line, mega_from) + : make("(end)") + ); + } + } + + from = column; + result = source_line.match(rx_token); + +// result[1] token +// result[2] whitespace +// result[3] identifier +// result[4] number +// result[5] rest + + if (!result) { + return stop_at( + "unexpected_char_a", + line, + column, + source_line[0] + ); + } + + snippet = result[1]; + column += snippet.length; + source_line = result[5]; + +// Whitespace was matched. Call lex again to get more. + + if (result[2]) { + return lex(); + } + +// The token is an identifier. + + if (result[3]) { + return make(snippet, undefined, true); + } + +// The token is a number. + + if (result[4]) { + return number(snippet); + } + +// The token is a string. + + if (snippet === "\"") { + return string(snippet); + } + if (snippet === "'") { + if (!option.single) { + warn_at("use_double", line, column); + } + return string(snippet); + } + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (snippet === "`") { + if (mega_mode) { + return stop_at("expected_a_b", line, column, "}", "`"); + } + snippet = ""; + mega_from = from; + mega_line = line; + mega_mode = true; + +// Parsing a mega literal is tricky. First make a ` token. + + make("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + (function part() { + const at = source_line.search(rx_mega); + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + if (at < 0) { + snippet += source_line + "\n"; + return ( + next_line() === undefined + ? stop_at("unclosed_mega", mega_line, mega_from) + : part() + ); + } + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + snippet += source_line.slice(0, at); + column += at; + source_line = source_line.slice(at); + if (source_line[0] === "\\") { + stop_at("escape_mega", line, at); + } + make("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then make tokens that will become part of an expression until +// a } token is made. + + if (source_line[0] === "$") { + column += 2; + make("${"); + source_line = source_line.slice(2); + (function expr() { + const id = lex().id; + if (id === "{") { + return stop_at( + "expected_a_b", + line, + column, + "}", + "{" + ); + } + if (id !== "}") { + return expr(); + } + }()); + return part(); + } + }()); + source_line = source_line.slice(1); + column += 1; + mega_mode = false; + return make("`"); + } + +// The token is a // comment. + + if (snippet === "//") { + snippet = source_line; + source_line = ""; + the_token = comment(snippet); + if (mega_mode) { + warn("unexpected_comment", the_token, "`"); + } + return the_token; + } + +// The token is a /* comment. + + if (snippet === "/*") { + array = []; + if (source_line[0] === "/") { + warn_at("unexpected_a", line, column + i, "/"); + } + (function next() { + if (source_line > "") { + i = source_line.search(rx_star_slash); + if (i >= 0) { + return; + } + j = source_line.search(rx_slash_star); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + } + array.push(source_line); + source_line = next_line(); + if (source_line === undefined) { + return stop_at("unclosed_comment", line, column); + } + return next(); + }()); + snippet = source_line.slice(0, i); + j = snippet.search(rx_slash_star_or_slash); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + array.push(snippet); + column += i + 2; + source_line = source_line.slice(i + 2); + return comment(array); + } + +// The token is a slash. + + if (snippet === "/") { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + if (prior.identifier) { + if (!prior.dot) { + if (prior.id === "return") { + return regexp(); + } + if ( + prior.id === "(begin)" + || prior.id === "case" + || prior.id === "delete" + || prior.id === "in" + || prior.id === "instanceof" + || prior.id === "new" + || prior.id === "typeof" + || prior.id === "void" + || prior.id === "yield" + ) { + the_token = regexp(); + return stop("unexpected_a", the_token); + } + } + } else { + last = prior.id[prior.id.length - 1]; + if ("(,=:?[".indexOf(last) >= 0) { + return regexp(); + } + if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) { + the_token = regexp(); + warn("wrap_regexp", the_token); + return the_token; + } + } + if (source_line[0] === "/") { + column += 1; + source_line = source_line.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + } + return make(snippet); + } + + first = lex(); + json_mode = first.id === "{" || first.id === "["; + +// This loop will be replaced with a recursive call to lex when ES6 has been +// finished and widely deployed and adopted. + + while (true) { + if (lex().id === "(end)") { + break; + } + } +} + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + +function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!rx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!rx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property[id] === "number") { + property[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (tenure !== undefined) { + if (tenure[id] !== true) { + warn("unregistered_property_a", name); + } + } else { + if (name.identifier && rx_bad_property.test(id)) { + warn("bad_property_a", name); + } + } + property[id] = 1; + } + return id; +} + +function dispense() { + +// Deliver the next token, skipping the comments. + + const cadet = tokens[token_nr]; + // hack-jslint - advance token async/await to next_token based on context + const next_cadet_id = (tokens[token_nr + 1] || { id: "" }).id; + if ( + (cadet.id === "async" && next_cadet_id === "function") + || (cadet.id === "await" && next_cadet_id[0].match(/[a-zA-Z_$]/)) + ) { + cadet.id = next_cadet_id; + token_nr += 1; + } + token_nr += 1; + if (cadet.id === "(comment)") { + if (json_mode) { + warn("unexpected_a", cadet); + } + return dispense(); + } else { + return cadet; + } +} + +function lookahead() { + +// Look ahead one token without advancing. + + const old_token_nr = token_nr; + const cadet = dispense(true); + token_nr = old_token_nr; + return cadet; +} + +function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if (token.identifier && token.id !== "function") { + anon = token.id; + } else if (token.id === "(string)" && rx_identifier.test(token.value)) { + anon = token.value; + } + +// Attempt to match next_token with an expected id. + + if (id !== undefined && next_token.id !== id) { + return ( + match === undefined + ? stop("expected_a_b", next_token, id, artifact()) + : stop( + "expected_a_b_from_c_d", + next_token, + id, + artifact(match), + artifact_line(match), + artifact(next_token) + ) + ); + } + +// Promote the tokens, skipping comments. + + token = next_token; + next_token = dispense(); + if (next_token.id === "(end)") { + token_nr -= 1; + } +} + +// Parsing of JSON is simple: + +function json_value() { + let negative; + if (next_token.id === "{") { + return (function json_object() { + const brace = next_token; + const object = empty(); + const properties = []; + brace.expression = properties; + advance("{"); + if (next_token.id !== "}") { + (function next() { + let name; + let value; + if (next_token.quote !== "\"") { + warn( + "unexpected_a", + next_token, + next_token.quote + ); + } + name = next_token; + advance("(string)"); + if (object[token.value] !== undefined) { + warn("duplicate_a", token); + } else if (token.value === "__proto__") { + warn("bad_property_a", token); + } else { + object[token.value] = token; + } + advance(":"); + value = json_value(); + value.label = name; + properties.push(value); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("}", brace); + return brace; + }()); + } + if (next_token.id === "[") { + return (function json_array() { + const bracket = next_token; + const elements = []; + bracket.expression = elements; + advance("["); + if (next_token.id !== "]") { + (function next() { + elements.push(json_value()); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]", bracket); + return bracket; + }()); + } + if ( + next_token.id === "true" + || next_token.id === "false" + || next_token.id === "null" + ) { + advance(); + return token; + } + if (next_token.id === "(number)") { + if (!rx_JSON_number.test(next_token.value)) { + warn("unexpected_a"); + } + advance(); + return token; + } + if (next_token.id === "(string)") { + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + advance(); + return token; + } + if (next_token.id === "-") { + negative = next_token; + negative.arity = "unary"; + advance("-"); + advance("(number)"); + negative.expression = token; + return negative; + } + stop("unexpected_a"); +} + +// Now we parse JavaScript. + +function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + const id = name.id; + +// Reserved words may not be enrolled. + + if (syntax[id] !== undefined && id !== "ignore") { + warn("reserved_a", name); + } else { + +// Has the name been enrolled in this context? + + let earlier = functionage.context[id]; + if (earlier) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + +// Has the name been enrolled in an outer context? + + } else { + stack.forEach(function (value) { + const item = value.context[id]; + if (item !== undefined) { + earlier = item; + } + }); + if (earlier) { + if (id === "ignore") { + if (earlier.role === "variable") { + warn("unexpected_a", name); + } + } else { + if ( + ( + role !== "exception" + || earlier.role !== "exception" + ) + && role !== "parameter" + && role !== "function" + ) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + } + } + } + +// Enroll it. + + functionage.context[id] = name; + name.dead = true; + name.parent = functionage; + name.init = false; + name.role = role; + name.used = 0; + name.writable = !readonly; + } + } +} + +function expression(rbp, initial) { + +// This is the heart of the Pratt parser. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// nud Null denotation +// led Left denotation +// lbp Left binding power +// rbp Right binding power + +// It processes a nud (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop. It +// returns the expression's parse tree. + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement, + + if (!initial) { + advance(); + } + the_symbol = syntax[token.id]; + if (the_symbol !== undefined && the_symbol.nud !== undefined) { + left = the_symbol.nud(); + } else if (token.identifier) { + left = token; + left.arity = "variable"; + } else { + return stop("unexpected_a", token); + } + (function right() { + the_symbol = syntax[next_token.id]; + if ( + the_symbol !== undefined + && the_symbol.led !== undefined + && rbp < the_symbol.lbp + ) { + advance(); + left = the_symbol.led(left); + return right(); + } + }()); + return left; +} + +function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = next_token; + let the_value; + the_paren.free = true; + advance("("); + the_value = expression(0); + advance(")"); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (anticondition[the_value.id] === true) { + warn("unexpected_a", the_value); + } + return the_value; +} + +function is_weird(thing) { + return ( + thing.id === "(regexp)" + || thing.id === "{" + || thing.id === "=>" + || thing.id === "function" + || (thing.id === "[" && thing.arity === "unary") + ); +} + +function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + return ( + Array.isArray(b) + && a.length === b.length + && a.every(function (value, index) { + return are_similar(value, b[index]); + }) + ); + } + if (Array.isArray(b)) { + return false; + } + if (a.id === "(number)" && b.id === "(number)") { + return a.value === b.value; + } + let a_string; + let b_string; + if (a.id === "(string)") { + a_string = a.value; + } else if (a.id === "`" && a.constant) { + a_string = a.value[0]; + } + if (b.id === "(string)") { + b_string = b.value; + } else if (b.id === "`" && b.constant) { + b_string = b.value[0]; + } + if (typeof a_string === "string") { + return a_string === b_string; + } + if (is_weird(a) || is_weird(b)) { + return false; + } + if (a.arity === b.arity && a.id === b.id) { + if (a.id === ".") { + return ( + are_similar(a.expression, b.expression) + && are_similar(a.name, b.name) + ); + } + if (a.arity === "unary") { + return are_similar(a.expression, b.expression); + } + if (a.arity === "binary") { + return ( + a.id !== "(" + && are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + ); + } + if (a.arity === "ternary") { + return ( + are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + && are_similar(a.expression[2], b.expression[2]) + ); + } + if (a.arity === "function" && a.arity === "regexp") { + return false; + } + return true; + } + return false; +} + +function semicolon() { + +// Try to match a semicolon. + + if (next_token.id === ";") { + advance(";"); + } else { + warn_at( + "expected_a_b", + token.line, + token.thru, + ";", + artifact(next_token) + ); + } + anon = "anonymous"; +} + +function statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token.identifier && next_token.id === ":") { + the_label = token; + if (the_label.id === "ignore") { + warn("unexpected_a", the_label); + } + advance(":"); + if ( + next_token.id === "do" + || next_token.id === "for" + || next_token.id === "switch" + || next_token.id === "while" + ) { + enroll(the_label, "label", true); + the_label.init = true; + the_label.dead = false; + the_statement = statement(); + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token; + first.statement = true; + the_symbol = syntax[first.id]; + if (the_symbol !== undefined && the_symbol.fud !== undefined) { + the_symbol.disrupt = false; + the_symbol.statement = true; + the_statement = the_symbol.fud(); + } else { + +// It is an expression statement. + + the_statement = expression(0, true); + if (the_statement.wrapped && the_statement.id !== "(") { + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; +} + +function statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const array = []; + (function next(disrupt) { + if ( + next_token.id !== "}" + && next_token.id !== "case" + && next_token.id !== "default" + && next_token.id !== "else" + && next_token.id !== "(end)" + ) { + let a_statement = statement(); + array.push(a_statement); + if (disrupt) { + warn("unreachable_a", a_statement); + } + return next(a_statement.disrupt); + } + }(false)); + return array; +} + +function not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === global) { + warn("unexpected_at_top_level_a", thing); + } +} + +function top_level_only(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== global) { + warn("misplaced_a", the_thing); + } +} + +function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token; + the_block.arity = "statement"; + the_block.body = special === "body"; + +// Top level function bodies may include the "use strict" pragma. + + if ( + special === "body" + && stack.length === 1 + && next_token.value === "use strict" + ) { + next_token.statement = true; + advance("(string)"); + advance(";"); + } + stmts = statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option.devel && special !== "ignore") { + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; +} + +function mutation_check(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + warn("bad_assignment_a", the_thing); + return false; + } + return true; +} + +function left_check(left, right) { + +// Warn if the left is not one of these: +// e.b +// e[b] +// e() +// ?: +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !left_check(left.expression[1]) + && !left_check(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right); + return false; + } + return true; +} + +// These functions are used to specify the grammar of our language: + +function symbol(id, bp) { + +// Make a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax[id] = the_symbol; + } + return the_symbol; +} + +function assignment(id) { + +// Make an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led = function (left) { + const the_token = token; + let right; + the_token.arity = "assignment"; + right = expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "pre" + || right.arity === "post" + ) { + warn("unexpected_a", right); + } + mutation_check(left); + return the_token; + }; + return the_symbol; +} + +function constant(id, type, value) { + +// Make a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud = ( + typeof value === "function" + ? value + : function () { + token.constant = true; + if (value !== undefined) { + token.value = value; + } + return token; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; +} + +function infix(id, bp, f) { + +// Make an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, expression(bp)]; + return the_token; + }; + return the_symbol; +} + +function infixr(id, bp) { + +// Make a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + the_token.expression = [left, expression(bp - 1)]; + return the_token; + }; + return the_symbol; +} + +function post(id) { + +// Make one of the post operators. + + const the_symbol = symbol(id, 150); + the_symbol.led = function (left) { + token.expression = left; + token.arity = "post"; + mutation_check(token.expression); + return token; + }; + return the_symbol; +} + +function pre(id) { + +// Make one of the pre operators. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "pre"; + the_token.expression = expression(150); + mutation_check(the_token.expression); + return the_token; + }; + return the_symbol; +} + +function prefix(id, f) { + +// Make a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = expression(150); + return the_token; + }; + return the_symbol; +} + +function stmt(id, f) { + +// Make a statement. + + const the_symbol = symbol(id); + the_symbol.fud = function () { + token.arity = "statement"; + return f(); + }; + return the_symbol; +} + +function ternary(id1, id2) { + +// Make a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led = function (left) { + const the_token = token; + const second = expression(20); + advance(id2); + token.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, expression(10)]; + if (next_token.id !== ")") { + warn("use_open", the_token); + } + return the_token; + }; + return the_symbol; +} + +// Begin defining the language. + +syntax = empty(); + +symbol("}"); +symbol(")"); +symbol("]"); +symbol(","); +symbol(";"); +symbol(":"); +symbol("*/"); +symbol("await"); +symbol("case"); +symbol("catch"); +symbol("class"); +symbol("default"); +symbol("else"); +symbol("enum"); +symbol("finally"); +symbol("implements"); +symbol("interface"); +symbol("package"); +symbol("private"); +symbol("protected"); +symbol("public"); +symbol("static"); +symbol("super"); +symbol("void"); +symbol("yield"); + +constant("(number)", "number"); +constant("(regexp)", "regexp"); +constant("(string)", "string"); +constant("arguments", "object", function () { + warn("unexpected_a", token); + return token; +}); +constant("eval", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("false", "boolean", false); +constant("Function", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("ignore", "undefined", function () { + warn("unexpected_a", token); + return token; +}); +constant("Infinity", "number", Infinity); +constant("isFinite", "function", function () { + warn("expected_a_b", token, "Number.isFinite", "isFinite"); + return token; +}); +constant("isNaN", "function", function () { + warn("number_isNaN", token); + return token; +}); +constant("NaN", "number", NaN); +constant("null", "null", null); +constant("this", "object", function () { + if (!option.this) { + warn("unexpected_a", token); + } + return token; +}); +constant("true", "boolean", true); +constant("undefined", "undefined"); + +assignment("="); +assignment("+="); +assignment("-="); +assignment("*="); +assignment("/="); +assignment("%="); +assignment("&="); +assignment("|="); +assignment("^="); +assignment("<<="); +assignment(">>="); +assignment(">>>="); + +infix("||", 40); +infix("&&", 50); +infix("|", 70); +infix("^", 80); +infix("&", 90); +infix("==", 100); +infix("===", 100); +infix("!=", 100); +infix("!==", 100); +infix("<", 110); +infix(">", 110); +infix("<=", 110); +infix(">=", 110); +infix("in", 110); +infix("instanceof", 110); +infix("<<", 120); +infix(">>", 120); +infix(">>>", 120); +infix("+", 130); +infix("-", 130); +infix("*", 140); +infix("/", 140); +infix("%", 140); +infixr("**", 150); +infix("(", 160, function (left) { + const the_paren = token; + let the_argument; + if (left.id !== "function") { + left_check(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (next_token.id !== ")") { + (function next() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + the_paren.free = true; + if (the_argument.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + the_paren.free = false; + } + return the_paren; +}); +infix(".", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("?.", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("[", 170, function (left) { + const the_token = token; + const the_subscript = expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + const name = survey(the_subscript); + if (rx_identifier.test(name)) { + warn("subscript_a", the_subscript, name); + } + } + left_check(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; +}); +infix("=>", 170, function (left) { + return stop("wrap_parameter", left); +}); + +function do_tick() { + const the_tick = token; + the_tick.value = []; + the_tick.expression = []; + if (next_token.id !== "`") { + (function part() { + advance("(string)"); + the_tick.value.push(token); + if (next_token.id === "${") { + advance("${"); + the_tick.expression.push(expression(0)); + advance("}"); + return part(); + } + }()); + } + advance("`"); + return the_tick; +} + +infix("`", 160, function (left) { + const the_tick = do_tick(); + left_check(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; +}); + +post("++"); +post("--"); +pre("++"); +pre("--"); + +prefix("+"); +prefix("-"); +prefix("~"); +prefix("!"); +prefix("!!"); +prefix("[", function () { + const the_token = token; + the_token.expression = []; + if (next_token.id !== "]") { + (function next() { + let element; + let ellipsis = false; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + element = expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]"); + return the_token; +}); +prefix("/=", function () { + stop("expected_a_b", token, "/\\=", "/="); +}); +prefix("=>", function () { + return stop("expected_a_before_b", token, "()", "=>"); +}); +prefix("new", function () { + const the_new = token; + const right = expression(160); + if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "()", artifact(next_token)); + } + the_new.expression = right; + return the_new; +}); +prefix("typeof"); +prefix("void", function () { + const the_void = token; + warn("unexpected_a", the_void); + the_void.expression = expression(0); + return the_void; +}); + +function parameter_list() { + const list = []; + let optional; + const signature = ["("]; + if (next_token.id !== ")" && next_token.id !== "(end)") { + (function parameter() { + let ellipsis = false; + let param; + if (next_token.id === "{") { + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("{"); + signature.push("{"); + (function subparameter() { + let subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (next_token.id === ":") { + advance(":"); + advance(); + token.label = subparam; + subparam = token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + } + if (next_token.id === "=") { + advance("="); + subparam.expression = expression(); + param.open = true; + } + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return subparameter(); + } + }()); + list.push(param); + advance("}"); + signature.push("}"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else if (next_token.id === "[") { + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("["); + signature.push("[]"); + (function subparameter() { + const subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + if (next_token.id === "=") { + advance("="); + subparam.expression = expression(); + param.open = true; + } + if (next_token.id === ",") { + advance(","); + return subparameter(); + } + }()); + list.push(param); + advance("]"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else { + if (next_token.id === "...") { + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + } + if (!next_token.identifier) { + return stop("expected_identifier_a"); + } + param = next_token; + list.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (next_token.id === "=") { + optional = param; + advance("="); + param.expression = expression(0); + } else { + if (optional !== undefined) { + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } + } + }()); + } + advance(")"); + signature.push(")"); + return [list, signature.join("")]; +} + +function do_function(the_function) { + let name; + if (the_function === undefined) { + the_function = token; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + name = next_token; + enroll(name, "variable", true); + the_function.name = name; + name.init = true; + name.calls = empty(); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + if (next_token.identifier) { + name = next_token; + the_function.name = name; + advance(); + } else { + the_function.name = anon; + } + } + } else { + name = the_function.name; + } + the_function.level = functionage.level + 1; + if (mega_mode) { + warn("unexpected_a", the_function); + } + +// Don't make functions in loops. It is inefficient, and it can lead to scoping +// errors. + + if (functionage.loop > 0) { + warn("function_in_loop", the_function); + } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + the_function.context = empty(); + the_function.finally = 0; + the_function.loop = 0; + the_function.switch = 0; + the_function.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functions.push(the_function); + functionage = the_function; + if (the_function.arity !== "statement" && typeof name === "object") { + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// Parse the parameter list. + + advance("("); + token.free = false; + token.arity = "function"; + [functionage.parameters, functionage.signature] = parameter_list(); + functionage.parameters.forEach(function enroll_parameter(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + name.names.forEach(enroll_parameter); + } + }); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && next_token.line === token.line + ) { + return stop("unexpected_a", next_token); + } + if ( + next_token.id === "." + || next_token.id === "?." + || next_token.id === "[" + ) { + warn("unexpected_a"); + } + +// Restore the previous context. + + functionage = stack.pop(); + return the_function; +} + +prefix("function", do_function); + +function fart(pl) { + advance("=>"); + const the_fart = token; + the_fart.arity = "binary"; + the_fart.name = "=>"; + the_fart.level = functionage.level + 1; + functions.push(the_fart); + if (functionage.loop > 0) { + warn("function_in_loop", the_fart); + } + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + the_fart.context = empty(); + the_fart.finally = 0; + the_fart.loop = 0; + the_fart.switch = 0; + the_fart.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functionage = the_fart; + the_fart.parameters = pl[0]; + the_fart.signature = pl[1]; + the_fart.parameters.forEach(function (name) { + enroll(name, "parameter", true); + }); + if (next_token.id === "{") { + warn("expected_a_b", the_fart, "function", "=>"); + the_fart.block = block("body"); + } else { + the_fart.expression = expression(0); + } + functionage = stack.pop(); + return the_fart; +} + +prefix("(", function () { + const the_paren = token; + let the_value; + const cadet = lookahead().id; + +// We can distinguish between a parameter list for => and a wrapped expression +// with one token of lookahead. + + if ( + next_token.id === ")" + || next_token.id === "..." + || (next_token.identifier && (cadet === "," || cadet === "=")) + ) { + the_paren.free = false; + return fart(parameter_list()); + } + the_paren.free = true; + the_value = expression(0); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + if (next_token.id === "=>") { + if (the_value.arity !== "variable") { + if (the_value.id === "{" || the_value.id === "[") { + warn("expected_a_before_b", the_paren, "function", "("); + return stop("expected_a_b", next_token, "{", "=>"); + } + return stop("expected_identifier_a", the_value); + } + the_paren.expression = [the_value]; + return fart([the_paren.expression, "(" + the_value.id + ")"]); + } + return the_value; +}); +prefix("`", do_tick); +prefix("{", function () { + const the_brace = token; + const seen = empty(); + the_brace.expression = []; + if (next_token.id !== "}") { + (function member() { + let extra; + let full; + let id; + let name = next_token; + let value; + advance(); + if ( + (name.id === "get" || name.id === "set") + && next_token.identifier + ) { + if (!option.getset) { + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + next_token.id; + name = next_token; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (next_token.id === "}" || next_token.id === ",") { + if (typeof extra === "string") { + advance("("); + } + value = expression(Infinity, true); + } else if (next_token.id === "(") { + value = do_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + advance("("); + } + let the_colon = next_token; + advance(":"); + value = expression(0); + if (value.id === name.id) { + warn("unexpected_a", the_colon, ": " + name.id); + } + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + advance(":"); + value = expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (next_token.id === ",") { + advance(","); + return member(); + } + }()); + } + advance("}"); + return the_brace; +}); + +stmt(";", function () { + warn("unexpected_a", token); + return token; +}); +stmt("{", function () { + warn("naked_block", token); + return block("naked"); +}); +stmt("break", function () { + const the_break = token; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (next_token.identifier && token.line === next_token.line) { + the_label = functionage.context[next_token.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + warn( + (the_label !== undefined && the_label.dead) + ? "out_of_scope_a" + : "not_label_a" + ); + } else { + the_label.used += 1; + } + the_break.label = next_token; + advance(); + } + advance(";"); + return the_break; +}); + +function do_var() { + const the_statement = token; + const is_const = the_statement.id === "const"; + the_statement.names = []; + +// A program may use var or let, but not both. + + if (!is_const) { + if (var_mode === undefined) { + var_mode = the_statement.id; + } else if (the_statement.id !== var_mode) { + warn( + "expected_a_b", + the_statement, + var_mode, + the_statement.id + ); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + warn("var_switch", the_statement); + } + if (functionage.loop > 0 && the_statement.id === "var") { + warn("var_loop", the_statement); + } + (function next() { + if (next_token.id === "{" && the_statement.id !== "var") { + const the_brace = next_token; + advance("{"); + (function pair() { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + survey(name); + advance(); + if (next_token.id === ":") { + advance(":"); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + next_token.label = name; + the_statement.names.push(next_token); + enroll(next_token, "variable", is_const); + advance(); + the_brace.open = true; + } else { + the_statement.names.push(name); + enroll(name, "variable", is_const); + } + name.dead = false; + name.init = true; + if (next_token.id === "=") { + advance("="); + name.expression = expression(); + the_brace.open = true; + } + if (next_token.id === ",") { + advance(","); + return pair(); + } + }()); + advance("}"); + advance("="); + the_statement.expression = expression(0); + } else if (next_token.id === "[" && the_statement.id !== "var") { + const the_bracket = next_token; + advance("["); + (function element() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + advance(); + the_statement.names.push(name); + enroll(name, "variable", is_const); + name.dead = false; + name.init = true; + if (ellipsis) { + name.ellipsis = true; + } else { + if (next_token.id === "=") { + advance("="); + name.expression = expression(); + the_bracket.open = true; + } + if (next_token.id === ",") { + advance(","); + return element(); + } + } + }()); + advance("]"); + advance("="); + the_statement.expression = expression(0); + } else if (next_token.identifier) { + const name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", is_const); + if (next_token.id === "=" || is_const) { + advance("="); + name.dead = false; + name.init = true; + name.expression = expression(0); + } + the_statement.names.push(name); + } else { + return stop("expected_identifier_a", next_token); + } + }()); + semicolon(); + return the_statement; +} + +stmt("const", do_var); +stmt("continue", function () { + const the_continue = token; + if (functionage.loop < 1 || functionage.finally > 0) { + warn("unexpected_a", the_continue); + } + not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; +}); +stmt("debugger", function () { + const the_debug = token; + if (!option.devel) { + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; +}); +stmt("delete", function () { + const the_token = token; + const the_value = expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; +}); +stmt("do", function () { + const the_do = token; + not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; +}); +stmt("export", function () { + const the_export = token; + let the_id; + let the_name; + let the_thing; + + function export_id() { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + the_id = next_token.id; + the_name = global.context[the_id]; + if (the_name === undefined) { + warn("unexpected_a"); + } else { + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a"); + } + exports[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + } + + the_export.expression = []; + if (next_token.id === "default") { + if (exports.default !== undefined) { + warn("duplicate_a"); + } + advance("default"); + the_thing = expression(0); + if ( + the_thing.id !== "(" + || the_thing.expression[0].id !== "." + || the_thing.expression[0].expression.id !== "Object" + || the_thing.expression[0].name.id !== "freeze" + ) { + warn("freeze_exports", the_thing); + } + if (next_token.id === ";") { + semicolon(); + } + exports.default = the_thing; + the_export.expression.push(the_thing); + } else { + if (next_token.id === "function") { + warn("freeze_exports"); + the_thing = statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a", the_name); + } + exports[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + warn("unexpected_a", next_token); + statement(); + } else if (next_token.id === "{") { + advance("{"); + (function loop() { + export_id(); + if (next_token.id === ",") { + advance(","); + return loop(); + } + }()); + advance("}"); + semicolon(); + } else { + stop("unexpected_a"); + } + } + module_mode = true; + return the_export; +}); +stmt("for", function () { + let first; + const the_for = token; + if (!option.for) { + warn("unexpected_a", the_for); + } + not_top_level(the_for); + functionage.loop += 1; + advance("("); + token.free = true; + if (next_token.id === ";") { + return stop("expected_a_b", the_for, "while (", "for (;"); + } + if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + return stop("unexpected_a"); + } + first = expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = expression(0); + advance(";"); + the_for.inc = expression(0); + if (the_for.inc.id === "++") { + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; +}); +stmt("function", do_function); +stmt("if", function () { + let the_else; + const the_if = token; + the_if.expression = condition(); + the_if.block = block(); + if (next_token.id === "else") { + advance("else"); + the_else = token; + the_if.else = ( + next_token.id === "if" + ? statement() + : block() + ); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + the_if.disrupt = true; + } else { + warn("unexpected_a", the_else); + } + } + } + return the_if; +}); +stmt("import", function () { + const the_import = token; + let name; + if (typeof module_mode === "object") { + warn("unexpected_directive_a", module_mode, module_mode.directive); + } + module_mode = true; + if (next_token.identifier) { + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + const names = []; + advance("{"); + if (next_token.id !== "}") { + while (true) { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (next_token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token; + if (!rx_module.test(token.value)) { + warn("bad_module_name_a", token); + } + froms.push(token.value); + semicolon(); + return the_import; +}); +stmt("let", do_var); +stmt("return", function () { + const the_return = token; + not_top_level(the_return); + if (functionage.finally > 0) { + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (next_token.id !== ";" && the_return.line === next_token.line) { + the_return.expression = expression(10); + } + advance(";"); + return the_return; +}); +stmt("switch", function () { + let dups = []; + let last; + let stmts; + const the_cases = []; + let the_disrupt = true; + const the_switch = token; + not_top_level(the_switch); + if (functionage.finally > 0) { + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token.free = true; + the_switch.expression = expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + (function major() { + const the_case = next_token; + the_case.arity = "statement"; + the_case.expression = []; + (function minor() { + advance("case"); + token.switch = true; + const exp = expression(0); + if (dups.some(function (thing) { + return are_similar(thing, exp); + })) { + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (next_token.id === "case") { + return minor(); + } + }()); + stmts = statements(); + if (stmts.length < 1) { + warn("expected_statements_a"); + return; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "break;", + artifact(next_token) + ); + } + if (next_token.id === "case") { + return major(); + } + }()); + dups = undefined; + if (next_token.id === "default") { + const the_default = next_token; + advance("default"); + token.switch = true; + advance(":"); + the_switch.else = statements(); + if (the_switch.else.length < 1) { + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + const the_last = the_switch.else[the_switch.else.length - 1]; + if (the_last.id === "break" && the_last.label === undefined) { + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; +}); +stmt("throw", function () { + const the_throw = token; + the_throw.disrupt = true; + the_throw.expression = expression(10); + semicolon(); + if (functionage.try > 0) { + warn("unexpected_a", the_throw); + } + return the_throw; +}); +stmt("try", function () { + let the_catch; + let the_disrupt; + const the_try = token; + if (functionage.try > 0) { + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (next_token.id === "catch") { + let ignored = "ignore"; + the_catch = next_token; + the_try.catch = the_catch; + advance("catch"); + if (next_token.id === "(") { + advance("("); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + if (next_token.id !== "ignore") { + ignored = undefined; + the_catch.name = next_token; + enroll(next_token, "exception", true); + } + advance(); + advance(")"); + } + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "catch", + artifact(next_token) + ); + + } + if (next_token.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; +}); +stmt("var", do_var); +stmt("while", function () { + const the_while = token; + not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; +}); +stmt("with", function () { + stop("unexpected_a", token); +}); + +ternary("?", ":"); + +// Ambulation of the parse tree. + +function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; +} + +function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all of the +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + } + }; +} + +const posts = empty(); +const pres = empty(); +const preaction = action(pres); +const postaction = action(posts); +const preamble = amble(pres); +const postamble = amble(posts); + +function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.id === "function") { + walk_statement(thing.block); + } + if (thing.arity === "pre" || thing.arity === "post") { + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + warn("unexpected_statement_a", thing); + } + postamble(thing); + } + } +} + +function walk_statement(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_statement); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + ) { + warn("unexpected_expression_a", thing); + } + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + } +} + +function lookup(thing) { + if (thing.arity === "variable") { + +// Look up the variable in the current context. + + let the_variable = functionage.context[thing.id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable === undefined) { + stack.forEach(function (outer) { + const a_variable = outer.context[thing.id]; + if ( + a_variable !== undefined + && a_variable.role !== "label" + ) { + the_variable = a_variable; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (the_variable === undefined) { + if (declared_globals[thing.id] === undefined) { + warn("undeclared_a", thing); + return; + } + the_variable = { + dead: false, + parent: global, + id: thing.id, + init: true, + role: "variable", + used: 0, + writable: false + }; + global.context[thing.id] = the_variable; + } + the_variable.closure = true; + functionage.context[thing.id] = the_variable; + } else if (the_variable.role === "label") { + warn("label_a", thing); + } + if ( + the_variable.dead + && ( + the_variable.calls === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + ) { + warn("out_of_scope_a", thing); + } + return the_variable; + } +} + +function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); +} + +function preaction_function(thing) { + if (thing.arity === "statement" && blockage.body !== true) { + warn("unexpected_a", thing); + } + stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); +} + +function bitwise_check(thing) { + if (!option.bitwise && bitwiseop[thing.id] === true) { + warn("unexpected_a", thing); + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + warn("unexpected_a", thing); + } +} + +function pop_block() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); +} + +function activate(name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.init = true; + } + } + blockage.live.push(name); +} + +function action_var(thing) { + thing.names.forEach(activate); +} + +preaction("assignment", bitwise_check); +preaction("binary", bitwise_check); +preaction("binary", function (thing) { + if (relationop[thing.id] === true) { + const left = thing.expression[0]; + const right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + warn("expected_string_a", right); + } + } else { + const value = right.value; + if (value === "null" || value === "undefined") { + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + warn("expected_type_string_a", right, value); + } + } + } + } +}); +preaction("binary", "==", function (thing) { + warn("expected_a_b", thing, "===", "=="); +}); +preaction("binary", "!=", function (thing) { + warn("expected_a_b", thing, "!==", "!="); +}); +preaction("binary", "=>", preaction_function); +preaction("binary", "||", function (thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + warn("and", thang); + } + }); +}); +preaction("binary", "(", function (thing) { + const left = thing.expression[0]; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + const parent = functionage.name.parent; + if (parent) { + const left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + && left_variable.dead + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } +}); +preaction("binary", "in", function (thing) { + warn("infix_in", thing); +}); +preaction("binary", "instanceof", function (thing) { + warn("unexpected_a", thing); +}); +preaction("binary", ".", function (thing) { + if (thing.expression.new) { + thing.new = true; + } +}); +preaction("statement", "{", function (thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; +}); +preaction("statement", "for", function (thing) { + if (thing.name !== undefined) { + const the_variable = lookup(thing.name); + if (the_variable !== undefined) { + the_variable.init = true; + if (!the_variable.writable) { + warn("bad_assignment_a", thing.name); + } + } + } + walk_statement(thing.initial); +}); +preaction("statement", "function", preaction_function); +preaction("unary", "~", bitwise_check); +preaction("unary", "function", preaction_function); +preaction("variable", function (thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } +}); + +function init_variable(name) { + const the_variable = lookup(name); + if (the_variable !== undefined) { + if (the_variable.writable) { + the_variable.init = true; + return; + } + } + warn("bad_assignment_a", name); +} + +postaction("assignment", "+=", function (thing) { + let right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } +}); +postaction("assignment", function (thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + if (thing.id === "=") { + if (thing.names !== undefined) { + if (Array.isArray(thing.names)) { + thing.names.forEach(init_variable); + } else { + init_variable(thing.names); + } + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.writable !== true) { + warn("bad_assignment_a", lvalue); + } + } + const right = syntax[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + warn("unexpected_a", thing.expression[1]); + } + } +}); + +function postaction_function(thing) { + delete functionage.finally; + delete functionage.loop; + delete functionage.switch; + delete functionage.try; + functionage = stack.pop(); + if (thing.wrapped) { + warn("unexpected_parens", thing); + } + return pop_block(); +} + +postaction("binary", function (thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || are_similar(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option.convert) { + if (thing.expression[0].value === "") { + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === "." || thing.id === "?.") { + if (thing.expression.id === "RegExp") { + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } +}); +postaction("binary", "&&", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "||", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "=>", postaction_function); +postaction("binary", "(", function (thing) { + let left = thing.expression[0]; + let the_new; + let arg; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option.eval) { + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + warn( + "expected_a_before_b", + left, + "new", + artifact(left) + ); + } + } + } else if (left.id === ".") { + let cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + cack = !cack; + } + if (rx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + warn("unexpected_a", the_new); + } else { + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + const paren = left.expression; + if (paren.id === "(") { + const array = paren.expression; + if (array.length === 1) { + const new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } +}); +postaction("binary", "[", function (thing) { + if (thing.expression[0].id === "RegExp") { + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + warn("weird_expression_a", thing.expression[1]); + } +}); +postaction("statement", "{", pop_block); +postaction("statement", "const", action_var); +postaction("statement", "export", top_level_only); +postaction("statement", "for", function (thing) { + walk_statement(thing.inc); +}); +postaction("statement", "function", postaction_function); +postaction("statement", "import", function (the_thing) { + const name = the_thing.name; + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return top_level_only(the_thing); +}); +postaction("statement", "let", action_var); +postaction("statement", "try", function (thing) { + if (thing.catch !== undefined) { + const the_name = thing.catch.name; + if (the_name !== undefined) { + const the_variable = functionage.context[the_name.id]; + the_variable.dead = false; + the_variable.init = true; + } + walk_statement(thing.catch.block); + } +}); +postaction("statement", "var", action_var); +postaction("ternary", function (thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || are_similar(thing.expression[1], thing.expression[2]) + ) { + warn("unexpected_a", thing); + } else if (are_similar(thing.expression[0], thing.expression[1])) { + warn("expected_a_b", thing, "||", "?"); + } else if (are_similar(thing.expression[0], thing.expression[2])) { + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + warn("wrap_condition", thing.expression[0]); + } +}); +postaction("unary", function (thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option.convert) { + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } +}); +postaction("unary", "function", postaction_function); +postaction("unary", "+", function (thing) { + if (!option.convert) { + warn("expected_a_b", thing, "Number(...)", "+"); + } + const right = thing.expression; + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } +}); + +function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + if (id !== "ignore") { + const name = the_function.context[id]; + if (name.parent === the_function) { + if ( + name.used === 0 + && ( + name.role !== "function" + || name.parent.arity !== "unary" + ) + ) { + warn("unused_a", name); + } else if (!name.init) { + warn("uninitialized_a", name); + } + } + } + }); +} + +function uninitialized_and_unused() { + +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (module_mode === true || option.node) { + delve(global); + } + functions.forEach(delve); +} + +// Go through the token list, looking at usage of whitespace. + +function whitage() { + let closer = "(end)"; + let free = false; + let left = global; + let margin = 0; + let nr_comments_skipped = 0; + let open = true; + let opening = true; + let right; + + function pop() { + const previous = stack.pop(); + closer = previous.closer; + free = previous.free; + margin = previous.margin; + open = previous.open; + opening = previous.opening; + } + + function push() { + stack.push({ + closer, + free, + margin, + open, + opening + }); + } + + function expected_at(at) { + warn( + "expected_a_at_b_c", + right, + artifact(right), + fudge + at, + artifact_column(right) + ); + } + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function no_space() { + if (left.line === right.line) { + if (left.thru !== right.from && nr_comments_skipped === 0) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (open) { + const at = ( + free + ? margin + : margin + 8 + ); + if (right.from < at) { + expected_at(at); + } + } else { + if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line || !open) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (right.from !== margin) { + expected_at(margin); + } + } + } + + stack = []; + tokens.forEach(function (the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + + const new_closer = opener[left.id]; + if (typeof new_closer === "string") { + if (new_closer !== right.id) { + opening = left.open || (left.line !== right.line); + push(); + closer = new_closer; + if (opening) { + free = closer === ")" && left.free; + open = true; + margin += 4; + if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (right.switch) { + at_margin(-4); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + free = false; + open = false; + no_space_only(); + } + } else { + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like '{]') have already been detected. + + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } + } else { + if (right.statement === true) { + if (left.id === "else") { + one_space_only(); + } else { + at_margin(0); + open = false; + } + +// If right is a closer, then pop the previous state. + + } else if (right.id === closer) { + pop(); + if (opening && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-4); + } else if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + one_space(); + } else { + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + at_margin(0); + } else { + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + no_space(); + } else if ( + left.id === "." + || left.id === "?." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + ) { + no_space_only(); + } else if (right.id === "." || right.id === "?.") { + no_space_only(); + } else if (left.id === ";") { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + one_space_only(); + } else if ( + left.id === "var" + || left.id === "const" + || left.id === "let" + ) { + push(); + closer = ";"; + free = false; + open = left.open; + if (open) { + margin = margin + 4; + at_margin(0); + } else { + one_space_only(); + } + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +// The jslint function itself. + +export default Object.freeze(function jslint( + source = "", + option_object = empty(), + global_array = [] +) { + try { + warnings = []; + option = Object.assign(empty(), option_object); + anon = "anonymous"; + block_stack = []; + declared_globals = empty(); + directive_mode = true; + directives = []; + early_stop = true; + exports = empty(); + froms = []; + fudge = ( + option.fudge + ? 1 + : 0 + ); + functions = []; + global = { + id: "(global)", + body: true, + context: empty(), + from: 0, + level: 0, + line: 0, + live: [], + loop: 0, + switch: 0, + thru: 0 + }; + blockage = global; + functionage = global; + json_mode = false; + mega_mode = false; + module_mode = false; + next_token = global; + property = empty(); + shebang = false; + stack = []; + tenure = undefined; + token = global; + token_nr = 0; + var_mode = undefined; + populate(standard, declared_globals, false); + populate(global_array, declared_globals, false); + Object.keys(option).forEach(function (name) { + if (option[name] === true) { + const allowed = allowed_option[name]; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } + }); + tokenize(source); + advance(); + if (json_mode) { + tree = json_value(); + advance("(end)"); + } else { + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option.browser) { + if (next_token.id === ";") { + advance(";"); + } + } else { + +// If we are not in a browser, then the file form of strict pragma may be used. + + if ( + next_token.value === "use strict" + ) { + advance("(string)"); + advance(";"); + } + } + tree = statements(); + advance("(end)"); + functionage = global; + walk_statement(tree); + if (warnings.length === 0) { + uninitialized_and_unused(); + if (!option.white) { + whitage(); + } + } + } + if (!option.browser) { + directives.forEach(function (comment) { + if (comment.directive === "global") { + warn("missing_browser", comment); + } + }); + } + early_stop = false; + } catch (e) { + if (e.name !== "JSLintError") { + warnings.push(e); + } + } + return { + directives, + edition: "2019-08-03", + exports, + froms, + functions, + global, + id: "(JSLint)", + json: json_mode, + lines, + module: module_mode === true, + ok: warnings.length === 0 && !early_stop, + option, + property, + shebang: ( + shebang + ? lines[0] + : undefined + ), + stop: early_stop, + tokens, + tree, + warnings: warnings.sort(function (a, b) { + return a.line - b.line || a.column - b.column; + }) + }; +}); diff --git a/branch.async_await/report.js b/branch.async_await/report.js new file mode 100644 index 000000000..5e487d143 --- /dev/null +++ b/branch.async_await/report.js @@ -0,0 +1,222 @@ +// report.js +// 2018-10-22 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Generate JSLint HTML reports. + +/*property + closure, column, context, edition, error, exports, filter, forEach, freeze, + froms, fudge, function, functions, global, id, isArray, join, json, keys, + length, level, line, lines, message, module, name, names, option, + parameters, parent, property, push, replace, role, signature, sort, stop, + warnings +*/ + +const rx_amp = /&/g; +const rx_gt = />/g; +const rx_lt = / with less destructive entities. + + return String( + string + ).replace( + rx_amp, + "&" + ).replace( + rx_lt, + "<" + ).replace( + rx_gt, + ">" + ); +} + +export default Object.freeze({ + error: function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + let fudge = Number(Boolean(data.option.fudge)); + let output = []; + if (data.stop) { + output.push("
    JSLint was unable to finish.
    "); + } + data.warnings.forEach(function (warning) { + output.push( + "
    ", + entityify(warning.line + fudge), + ".", + entityify(warning.column + fudge), + "
    ", + entityify(warning.message), + "
    ", + entityify(data.lines[warning.line] || ""), + "" + ); + }); + return output.join(""); + }, + + function: function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + let fudge = Number(Boolean(data.option.fudge)); + let mode = ( + data.module + ? "module" + : "global" + ); + let output = []; + + if (data.json) { + return ( + data.warnings.length === 0 + ? "
    JSON: good.
    " + : "
    JSON: bad.
    " + ); + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + let global = Object.keys(data.global.context).sort(); + let froms = data.froms.sort(); + let exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + let context = the_function.context; + let list = Object.keys(context); + output.push( + "
    ", + entityify(the_function.line + fudge), + "
    ", + ( + the_function.name === "=>" + ? entityify(the_function.signature) + " =>" + : ( + typeof the_function.name === "string" + ? "«" + entityify(the_function.name) + "»" + : "" + entityify(the_function.name.id) + "" + ) + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + let params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.parent === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.parent === the_function + ); + })); + detail("outer", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.parent !== the_function + && the_variable.parent.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].parent.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + output.push( + "
    JSLint edition ", + entityify(data.edition), + "
    " + ); + return output.join(""); + }, + + property: function property_directive(data) { + +// Produce the /*property*/ directive. + + let not_first = false; + let output = ["/*property"]; + let length = 1111; + let properties = Object.keys(data.property); + + if (properties.length > 0) { + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length >= 80) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); + } + } +}); diff --git a/branch.autofix/README b/branch.autofix/README new file mode 100644 index 000000000..f46e91069 --- /dev/null +++ b/branch.autofix/README @@ -0,0 +1,42 @@ +JSLint, The JavaScript Code Quality Tool + +Douglas Crockford +douglas@crockford.com + +2018-05-28 + +jslint.js contains the jslint function. It parses and analyzes a source file, +returning an object with information about the file. It can also take an object +that sets options. + +index.html runs the jslint.js function in a web page. The page also depends +browser.js and report.js and jslint.css. + +jslint.css provides styling for index.html. + +browser.js runs the web user interface. + +report.js generates the results reports in HTML. + +help.html describes JSLint's usage. Please read it. + +function.html describes the jslint function and the results it produces. + +Please direct questions and comments to +https://plus.google.com/communities/104441363299760713736. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[http://www.crockford.com/wrrrld/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. diff --git a/branch.autofix/autofix.js b/branch.autofix/autofix.js new file mode 100755 index 000000000..975c51603 --- /dev/null +++ b/branch.autofix/autofix.js @@ -0,0 +1,97 @@ +#!/usr/bin/env node +/*jslint node */ +/*property + argv, + assert, + autofix, + basename, + error, + exit, + fs, + join, + jslint, + main, + message, + option, + readFile, + replace, + runInNewContext, + source_autofixed, + writeFile +*/ +"use strict"; +let fs; +let local; +let modeNext; +let onNext; +let path; +let vm; +onNext = function (error, data) { + if (error) { + console.error(error.message); + return; + } + modeNext += 1; + switch (modeNext) { + case 1: + // run this program only in cli-mode + if (module !== require.main) { + console.error( + "jslint-autofix can only be run from the command-line" + ); + return; + } + // require builtins + fs = require("fs"); + path = require("path"); + vm = require("vm"); + // init local namespace + local = {}; + if (!process.argv[2]) { + console.error( + "error: no specified\n" + + "usage: " + path.basename(__filename) + " \n" + + "example: " + path.basename(__filename) + " example.js" + ); + process.exit(1); + } + fs.readFile(path.join(__dirname, "jslint.js"), "utf8", onNext); + break; + case 2: + // bug-workaround - nodejs does not widely support es-modules + vm.runInNewContext( + data.replace("export default function", "function"), + local + ); + // read file-data + fs.readFile(process.argv[2], "utf8", onNext); + break; + case 3: + // autofix file-data + console.error( + "jslint-autofix - autofixing file " + process.argv[2] + " ..." + ); + data = local.jslint(data, {autofix: true}); + if (typeof data.source_autofixed !== "string") { + onNext(new Error( + "jslint-autofix - could not autofix file " + process.argv[2] + )); + return; + } + // save autofixed file-data + fs.writeFile( + process.argv[2] + ".autofixed.js", + data.source_autofixed, + onNext + ); + break; + case 4: + console.error( + "jslint-autofix - saved autofixed file - " + process.argv[2] + + ".autofixed.js" + ); + break; + } +}; +modeNext = 0; +onNext(); diff --git a/branch.autofix/browser.js b/branch.autofix/browser.js new file mode 100644 index 000000000..c47590b41 --- /dev/null +++ b/branch.autofix/browser.js @@ -0,0 +1,176 @@ +// browser.js +// 2018-06-16 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +/*jslint + browser +*/ + +/*property + autofix, currentTarget, name, source_autofixed, + checked, create, disable, display, error, focus, forEach, function, + getElementById, innerHTML, join, length, map, onchange, onclick, onscroll, + property, querySelectorAll, scrollTop, select, split, style, title, value +*/ + +import jslint from "./jslint.js"; +import report from "./report.js"; + +// This is the web script companion file for JSLint. It includes code for +// interacting with the browser and displaying the reports. + +let rx_crlf = /\n|\r\n?/; +let rx_separator = /[\s,;'"]+/; + +let fudge_unit = 0; + +const aux = document.getElementById("JSLINT_AUX"); +const boxes = document.querySelectorAll("[type=checkbox]"); +const fudge = document.getElementById("JSLINT_FUDGE"); +const global = document.getElementById("JSLINT_GLOBAL"); +const number = document.getElementById("JSLINT_NUMBER"); +const property = document.getElementById("JSLINT_PROPERTY"); +const property_fieldset = document.getElementById("JSLINT_PROPERTYFIELDSET"); +const report_field = document.getElementById("JSLINT_REPORT"); +const report_list = document.getElementById("JSLINT_REPORT_LIST"); +const source = document.getElementById("JSLINT_SOURCE"); +const select = document.getElementById("JSLINT_SELECT"); +const warnings = document.getElementById("JSLINT_WARNINGS"); +const warnings_list = document.getElementById("JSLINT_WARNINGS_LIST"); + +function show_numbers() { + number.value = source.value.split(rx_crlf).map(function (ignore, index) { + return index + fudge_unit; + }).join("\n"); +} + +function clear() { + aux.style.display = "none"; + number.value = ""; + property.value = ""; + property_fieldset.style.display = "none"; + report_field.style.display = "none"; + report_list.innerHTML = ""; + source.focus(); + source.value = ""; + warnings.style.display = "none"; + warnings_list.innerHTML = ""; +} + +function fudge_change() { + fudge_unit = Number(fudge.checked); + show_numbers(); +} + +function clear_options() { + boxes.forEach(function (node) { + node.checked = false; + }); + fudge_change(); + global.innerHTML = ""; +} + +function call_jslint(event) { + +// First build the option object. + + let option = Object.create(null); + boxes.forEach(function (node) { + if (node.checked) { + option[node.title] = true; + } + }); + +// Init autofix option + if (event.currentTarget.name === "autofix") { + option.autofix = true; + } + +// Call JSLint with the source text, the options, and the predefined globals. + + let global_string = global.value; + let result = jslint( + source.value, + option, + ( + global_string === "" + ? undefined + : global_string.split(rx_separator) + ) + ); + +// Generate the reports. + + let error_html = report.error(result); + let function_html = report.function(result); + let property_text = report.property(result); + +// Display the reports. + + warnings_list.innerHTML = error_html; + warnings.style.display = ( + error_html.length === 0 + ? "none" + : "block" + ); + + report_list.innerHTML = function_html; + report_field.style.display = "block"; + if (property_text) { + property.value = property_text; + property_fieldset.style.display = "block"; + property.scrollTop = 0; + select.disable = false; + } else { + property_fieldset.style.display = "none"; + select.disable = true; + } + aux.style.display = "block"; + source.select(); + +// Replace input source-code with autofixed result + if (option.autofix) { + document.getElementById("JSLINT_SOURCE").value = + result.source_autofixed; + } + + return result; +} + +fudge.onchange = fudge_change; + +source.onchange = function (ignore) { + show_numbers(); +}; + +source.onscroll = function () { + let ss = source.scrollTop; + number.scrollTop = ss; + let sn = number.scrollTop; + if (ss > sn) { + show_numbers(); + number.scrollTop = ss; + } +}; + +document.querySelectorAll("[name='JSLint']").forEach(function (node) { + node.onclick = call_jslint; +}); + +document.querySelectorAll("[name='clear']").forEach(function (node) { + node.onclick = clear; +}); + +document.querySelectorAll("[name='autofix']").forEach(function (node) { + node.onclick = call_jslint; +}); + +document.getElementById("JSLINT_SELECT").onclick = function () { + property.focus(); + property.select(); +}; +document.getElementById("JSLINT_CLEAR_OPTIONS").onclick = clear_options; + +fudge_change(); +source.select(); +source.focus(); diff --git a/branch.autofix/function.html b/branch.autofix/function.html new file mode 100644 index 000000000..8d7287cbc --- /dev/null +++ b/branch.autofix/function.html @@ -0,0 +1,612 @@ + + + + + + + + +JSLint: jslint function + + + +
    JSLint
    + +
    The jslint Function
    +
    +

    JSLint

    +

    JSLint is delivered as a set of files:

    +
    + + + + + + + + + + + + + + +
    FilenameContent
    browser.jsBrowser based UI.
    function.htmlThis page that you are looking at right now.
    help.htmlUsing jslint as a single page JSLint application.
    index.htmlSingle page JSLint application that uses the + js files.
    jslint.jsThe jslint function.
    report.jsThe report object, containing report generator functions for + HTML.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    +
    + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring"(JSLint)"
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    parenttokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    parenttokenThe function in which the variable was declared.variable
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable can be assigned to.variable
    +

    Reports

    +

    The report object contains three functions that all take a + result from the jslint function as input. + report.js has no other dependence on other files.

    + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    +
    + +
    + + + + + +
    + + diff --git a/branch.autofix/help.html b/branch.autofix/help.html new file mode 100644 index 000000000..ee236ff95 --- /dev/null +++ b/branch.autofix/help.html @@ -0,0 +1,841 @@ + + + + + + + + + +JSLint: Help + + + +
    JSLint
    + +
    Help
    +
    +
    Il semble que la perfection soit atteinte non quand il n’y a + plus rien à ajouter, mais quand il n’y a plus rien à + retrancher.
    +
    + Antoine de Saint-Exupéry +
    +
    + Terre des Hommes (1939) +
    +

    Good Parts

    +

    The Principle of the Good Parts is

    +
    If a feature is sometimes useful and sometimes dangerous and if + there is a better option then always use the better option.
    +

    JSLint is a JavaScript program that looks for problems in + JavaScript programs. It is a code quality tool.

    +

    When C was + a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    +

    As the language matured, the definition of the language was + strengthened to eliminate some insecurities, and compilers got better + at issuing warnings. lint is no longer needed.

    +

    JavaScript is a + young-for-its-age language. It was originally intended to do small tasks in + webpages, tasks for which Java was too heavy and clumsy. But JavaScript is + a surprisingly capable language, and it is now being used in larger + projects. Many of the features that were intended to make the language easy + to use are troublesome when projects become complicated. A lint + for JavaScript is needed: JSLint, a JavaScript syntax checker + and validator.

    +

    JSLint takes a JavaScript source and scans it. If it finds a + problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by the ECMAScript Programming Language + Standard (the strangely named document that governs JavaScript). + JSLint will reject most legal programs. It is a higher + standard.

    +

    JavaScript is a sloppy language, but hidden deep inside there is an elegant, + better language. JSLint helps you to program in that better + language and to avoid most of the slop. JSLint will reject + programs that browsers will accept because JSLint is concerned + with the quality of your code and browsers are not. You should gladly accept + all of JSLint's advice.

    +

    JSLint can operate on JavaScript source + or JSON text.

    +

    ECMAScript Sixth Edition

    +

    Some of + ES6’s features are good, so JSLint will recognize the good + parts of ES6.

    +

    Currently, these features are recognized:

    +
      +
    • The ... ellipsis marker in parameter + lists and argument lists, replacing the arguments object + for variadic functions.
    • +
    • The let statement, which is like the var + statement except that it respects block scope. + You may use let or var but not both.
    • +
    • The const statement is like the let statement + except that it disallows the use of assignment on the variable, although + if the value of the variable is mutable, it can still be mutated. + const is preferred to let. +
    • Destructuring of arrays and objects is allowed in parameter lists and on + the left side of let, and const, but not + var or assignment statements, and not deep destructuring + or eliding.
    • +
    • Enhanced object literals, providing shorter forms for function + declaration and properties that are initialized by variables with the + same name.
    • +
    • The fat arrow => fart functions.
    • +
    • The simplest forms of import and export.
    • +
    • `Megastring` literals, but not nested + `megastring` literals.
    • +
    • New global functions, such as Map, Set, + WeakMap, and WeakSet.
    • +
    • 0b- and 0o- number literals.
    • +
    +

    The most important new feature of ES6 is proper tail calls. This has no + new syntax, so JSLint doesn’t see it. But it makes recursion + much more attractive, which makes loops, particularly for + loops, much less attractive.

    +

    import export

    +

    The ES6 module feature will be an important improvement over JavaScript’s + global variables as a means of linking separate files together. + JSLint recognizes a small but essential subset of the module + syntax.

    +
    +

    import name from stringliteral;

    +

    import {name} from stringliteral;

    +

    export default function () {};

    +

    export default function name() {};

    +

    export default expression;

    +

    export function name() {}

    +

    export name; // where name is const

    +

    export {name};

    +
    +

    Directives

    +

    JSLint provides three directives that may be placed in a + file to manage JSLint’s behavior. Use of these directives is + optional. If they are used, they should be placed in a source file before + the first statement. They are written in the form of a comment, where the + directive name is placed immediately after the opening of the comment before + any whitespace. The three directives are global, + jslint, and property. Directives in a file are + stronger than options selected from the UI or passed with the option object.

    +

    /*global*/

    +

    The /*global*/ directive is used to specify a set of globals + (usually functions and objects containing functions) that are available to + this file. This was commonly used in browsers to link source files together + before ES6 modules appeared. Use of global variables is strongly + discouraged, but unfortunately web browsers require their use. The + /*global*/ directive can only be used when the + Assume a browser option is selected. +

    Each of the names listed will indicate a read-only global variable. The names + are separated by , comma. This directive + should not be used if import or export is used. + This directive inhibits warnings, but it does not declare the names in the + execution environment. +

    For example: +

    /*global +
    ADSAFE, report, jslint
    +*/
    +

    instructs JSLint to not give warnings about the global + variables ADsafe, report, and jslint. + However, if any of those names are expected to be supplied by other files + and those other files fail to do so, then execution errors will result. It + is usually better to use top-level variable declarations instead: +

        var ADSAFE;
    +    var report;
    +    var jslint; 
    +

    Using var in this way allows comparing a global variable to the + undefined value to determine whether it is has been used in the global context.

    +

    /*jslint*/

    +

    The /*jslint*/ directive allows for the control of several + options. These options can also be set from the + JSLint.com user interface.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Tolerate bitwise operatorsbitwisetrue if bitwise operators should be allowed. The + bitwise operators are rarely used in JavaScript programs, so it + is usually more likely that & is a mistyping of + && than that it indicates a bitwise and. + JSLint will give warnings on the bitwise operators + unless this option is selected.
    Assume a browser browsertrue if the standard browser globals should be + predefined. This option will reject the use of + import and export. This option also + disallows the file form of the "use + strict" pragma. It does not supply + self; you will have to request that unnecessary + alias of the dreaded global object yourself. It adds the same + globals as this directive: +
    /*global +
    + clearInterval, clearTimeout, document, event, FileReader, + FormData, history, localStorage, location, name, navigator, + screen, sessionStorage, setInterval, setTimeout, Storage, + URL, window, XMLHttpRequest +
    + */
    +
    Tolerate conversion operatorsconverttrue if !!, + prefix, + and concatenation with "" are allowed. + The Boolean, Number, and + String functions are preferred
    Assume CouchDBcouchtrue if + Couch DB + globals should be predefined. It adds the same globals as this + directive: +
    /*global +
    emit, getRow, isArray, log, provides, registerType, require, + send, start, sum, toJSON
    + */
    Assume in developmentdeveltrue if browser globals that are useful in + development should be predefined, and if debugger + statements and TODO comments + should be allowed. It adds the + same globals as this directive: +
    /*global +
    alert, confirm, console, prompt
    + */
    Be sure to turn this option off before going + into production.
    Tolerate evalevaltrue if eval should be allowed. In the + past, the eval function was the most misused + feature of the language.
    Tolerate for statementfortrue if the for statement should be + allowed. It is almost always better to use the array methods + instead.
    Fudge the line and column numbersfudgetrue if the origin of a text file is line 1 column + 1. Programmers know that number systems start at zero. + Unfortunately, most text editors start numbering lines and + columns at one. JSLint will by default start + numbering at zero. Use this option to start the numbering at one + in its reports.
    Tolerate get and setgetsettrue if accessor properties are allowed in object literals.
    Tolerate long source lineslongtrue if a line can contain more than 80 characters.
    Tolerate multiple variables declarations per statementmultivartrue if a var, let, or + const statement can declare two or more variables + in a single statement.
    Assume Node.jsnodetrue if Node.js globals should be predefined. It + will predefine globals that are used in the Node.js environment. + It adds the same globals as this directive: +
    /*global +
    + Buffer, clearImmediate, clearInterval, clearTimeout, console, exports, + module, process, require, setImmediate, setInterval, setTimeout, URL, + URLSearchParams, __dirname, __filename +
    + */
    Tolerate single quote stringssingletrue if 'single quote + should be allowed to enclose string literals.
    Tolerate this
    thistrue if this should be allowed.
    Tolerate whitespace messwhitetrue if the whitespace rules should be ignored.
    +

    For example:

    +
    /*jslint
    +    bitwise, node
    +*/
    + +

    /*property*/

    +

    The /*property*/ directive is used to declare a list of + property identifiers that are used in the file. Each property name in the + program is looked up in this list. If a name is not found, that indicates an + error, most likely a typing error.

    +

    The list can also be used to evade some of JSLint’s naming + rules.

    +

    JSLint can build the /*property*/ list for you. At + the bottom of its report, JSLint displays a + /*property*/ directive. It contains all of the names that were + used with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. You + can copy the /*property*/ directive to the top of your + script file. JSLint will check the spelling of all property + names against the list. That way, you can have JSLint look for + misspellings for you.

    +

    For example,

    +
    /*property +
    charAt, slice, _$_
    + */
    +

    ignore

    +

    JSLint introduces a new reserved word: ignore. It + is used in parameter lists and in catch clauses to indicate a + parameter that will be ignored. Unused warnings will not be produced for + ignore. +

    function handler(ignore, value) {
    +    return do_something_useful(value);
    +}
    +

    var let const

    +

    JavaScript provides three statements for declaring variables: + var, let, and const. The + var statement suffers from a bad practice called hoisting. The + let statement does not do hoisting and respects block scope. + The const statement is like let except that it + marks the variable (but not its contents) as read only, making it an error + to attempt to assign to the variable. When given a choice, + const is the best, var is the worst.

    +

    JSLint uses the intersection of the var rules and the + let rules, and by doing so avoids the errors related to either. + A name should be declared only once in a function. It should be declared + before it is used. It should not be used outside of the block in which it is + declared. A variable should not have the same name as a variable or + parameter in an outer function. Do not mix var and + let. Declare one name per statement.

    +

    = == ===

    +

    Fortran made a terrible + mistake in using the equality operator as its assignment operator. That + mistake has been replicated in most languages since then. C compounded that + mistake by making == its equality operator. The visual + similarity is a source of errors. JavaScript compounded this further by + making == a type coercing comparison operator that produces + false positive results. This was mitigated by adding the === + operator, leaving the broken == operator in place.

    +

    JSLint attempts to minimize errors by the following rules:

    +

    == is not allowed. This avoids the false positives, and + increases the visual distance between = and ===. + Assignments are not allowed in expression position, and comparisons are not + allowed in statement position. This also reduces confusion. 

    +

    Semicolon

    +

    JavaScript uses a C-like syntax that requires the use of semicolons to + delimit certain statements. JavaScript attempts to make those semicolons + optional with an automatic semicolon insertion mechanism, but it does not + work very well. Automatic semicolon insertion was added to make + things easier for beginners. Unfortunately, it sometimes fails. Do not rely + on it unless you are a beginner.

    +

    JSLint expects that every statement will be followed by + ; except for for, function, + if, switch, try, and + while. JSLint does not expect to see unnecessary + semicolons, the empty statement, or empty blocks.

    +

    function =>

    +

    JavaScript has four syntactic forms for making function objects: function + statements, function expressions, enhanced object literals, and the + => fart operator.

    +

    The function statement creates a variable and assigns the function object to + it. It should be used in a file or function body, but not inside of a block.

    +
    function name(parameters) { +
    statements
    + }
    +

    The function expression unfortunately looks like the function statement. It + may appear anywhere that an expression may appear, but not in statement + position and not in a loop. It produces a function object but does not + create a variable in which to store it.

    +
    function (parameters) { +
    statements
    + }
    +

    The enhanced object literal provides an ES6 shorthand for creating a property + whose value is a function, saving you from having to type + : colon and function.

    +
    { +
    name(parameters) { +
    statements
    + }
    + }
    +

    Finally, ES6 provides an even shorter form of function expression that leaves + out the words function and return:

    +
    (parameters) => + expression
    +

    JSLint requires the parens around the parameters, and forbids a + { left brace after the + => fart to avoid syntactic ambiguity.

    +

    Comma

    +

    The , comma operator is unnecessary and can + mask programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as + an operator. It does not expect to see elided elements in array literals. A + comma should not appear after the last element of an array literal or object + literal.

    +

    Blocks

    +

    JSLint expects blocks with function, + if, switch, while, for, + do, and try statements and nowhere else.

    +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    +

    JavaScript allows an if to be written like this:

    +
    if (condition)
    +    statement;
    +

    That form is known to contribute to mistakes in projects where many + programmers are working on the same code. That is why JSLint + expects the use of a block:

    +
    if (condition) {
    +    statements;
    +}
    +

    Experience shows that this form is more resilient.

    +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call. All other expression statements are considered + to be errors.

    +

    for

    +

    JSLint does not recommend use of the for statement. + Use array methods like forEach instead. The for + option will suppress some warnings. The forms of for that + JSLint accepts are restricted, excluding the new ES6 forms.

    +

    for in

    +

    JSLint does not recommend use of the for + in statement. Use Object.keys instead.

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, it also + loops through all of the properties that were inherited through the + prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written + without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    for (name in object) {
    +    if (object.hasOwnProperty(name)) {
    +        ....
    +    }
    +}
    +

    Note that the above code will fail if the object contains a property + named hasOwnProperty. Use Object.keys instead.

    +

    switch

    +

    A common error in switch statements is to forget to place a + break statement after each case, resulting in unintended + fall-through. JSLint expects that the statement before the next + case or default is one of these: + break, return, or throw.

    +

    with

    +

    The with statement was intended to provide a shorthand in + accessing properties in deeply nested objects. Unfortunately, it behaves + very badly when setting new properties. Never use the with + statement.

    +

    JSLint does not expect to see a with statement.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that + labels will be distinct from vars and parameters.

    +

    Unreachable Code

    +

    JSLint expects that a return, break, + or throw statement will be followed by a + } right brace or case or + default.

    +

    Confusing Pluses and Minuses

    +

    JSLint expects that + will not be followed by + + or ++, and that - will not be + followed by - or --. A misplaced space can turn + + + into ++, an error that is difficult to see. + Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ increment and + -- decrement operators have been known to + contribute to bad code by encouraging excessive trickiness. They are second + only to faulty architecture in enabling to viruses and other security + menaces. Also, preincrement/postincrement confusion can produce off-by-one + errors that are extremely difficult to diagnose. Fortunately, they are also + complete unnecessary. There are better ways to add 1 to a variable.

    +

    It is best to avoid these operators entirely and rely on += and + -= instead.

    +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. + JSLint looks for problems that may cause portability problems. + It also attempts to resolve visual ambiguities by recommending explicit + escapement.

    +

    JavaScript’s syntax for regular expression literals overloads the + / slash character. To avoid ambiguity, + JSLint expects that the character preceding a regular + expression literal is a ( left paren or + = equal or + : colon or + , comma character.

    +

    this

    +
    Having this in the language makes it harder to talk + about the language. It is like pair programming with Abbott and Costello. +
    +

    Avoid using this. Warnings about this can be + suppressed with option.this.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the + new prefix. The new prefix creates a new object + based on the function's prototype, and binds that object to the + function's implied this parameter. If you neglect to use the + new prefix, no new object will be made and this + will be bound to the global object.

    +

    JSLint enforces the convention that constructor functions be + given names with initial uppercase. JSLint does not expect to + see a function invocation with an initial uppercase name unless it has the + new prefix. JSLint does not expect to see the + new prefix used with functions whose names do not start with + initial uppercase.

    +

    JSLint does not expect to see the wrapper forms + new Number, new String, new Boolean.

    +

    JSLint does not expect to see new Object. + Use Object.create(null) instead.

    +

    Whitespace

    +

    JSLint has a specific set of rules about the use of whitespace. + Where possible, these rules are consistent with centuries of good practice + with literary style.

    +

    The indentation increases by 4 spaces when the last token on a line is + { left brace, + [ left bracket, + ( left paren. The matching closing + token will be the first token on a line, restoring the previous + indentation.

    +

    The ternary operator can be visually confusing, so + ? question mark and + : colon always begin a line and increase + the indentation by 4 spaces.

    +
    return (the_token.id === "(string)" || the_token.id === "(number)")
    +    ? String(the_token.value)
    +    : the_token.id;
    +

    If . period is the first character on a line, the + indentation is increased by 4 spaces.

    +

    A var, let, or const statement will + also cause an indentation if it declares two or more variables, and if the + second variable is not on the same line as the start of the statement.

    +

    Long lines can be continued on the next line with 8 spaces added to the + current indentation. Those 8 spaces do not change the current indentation. + It is 8 to avoid confusion with ordinary indentation.

    +

    The word function is always followed with one space.

    +

    Clauses (case, catch, default, + else, finally) are not statements and so should + not be indented like statements.

    +

    Spaces are used to make things that are not invocations look less like + invocations.

    +

    Tabs and spaces should not be mixed. We should pick just one in order to + avoid the problems that come from having both. Personal preference is an + extremely unreliable criterion. Neither offers a powerful advantage over the + other. Fifty years ago, tab had the advantage of consuming less memory, but + Moore's Law has eliminated that advantage. Space has one clear advantage + over tab: there is no reliable standard for how many spaces a tab + represents, but it is universally accepted that a space occupies a space. So + use spaces. You can edit with tabs if you must, but make sure it is spaces + again before you commit. Maybe someday we will finally get a universal + standard for tabs, but until that day comes, the better choice is spaces.

    +

    Unsafe Characters

    +

    There are characters that are handled inconsistently in some browsers, and + so must be escaped when placed in strings.

    +
    \u0000-\u001f
    +\u007f-\u009f
    +\u00ad
    +\u0600-\u0604
    +\u070f
    +\u17b4
    +\u17b5
    +\u200c-\u200f
    +\u2028-\u202f
    +\u2060-\u206f
    +\ufeff
    +\ufff0-\uffff
    +

    Report

    +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    +
      +
    • The line number on which it starts.
    • +
    • The function’s name. In the case of anonymous functions, + JSLint will attempt to + «guess» the name.
    • +
    • The parameters.
    • +
    • Variables: The variables that are declared in the function.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Exceptions: The variables that are declared by catch + clauses of try statements.
    • +
    • Outer: Variables used by this function that are declared in + other functions.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements. Updates are announced at + https://plus.google.com/communities/104441363299760713736.

    +

    Try it

    +

    Try it. Paste your script + into the window and click the + + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    JSLint is written entirely in JavaScript, so it can run + anywhere that JavaScript (or even Java) can run.

    +

    Perfectly fine

    +

    JSLint was designed to reject code that some would consider to + be perfectly fine. The reason for this is that JSLint's + purpose is to help produce programs that are free of error. That is + difficult in any language and is especially hard in JavaScript. + JSLint attempts to help you increase the visual distance + between correct programs and incorrect programs, making the remaining errors + more obvious. JSLint will give warnings about things that are + not necessarily wrong in the current situation, but which have been observed + to mask or obscure errors. Avoid those things when there are better options + available.

    +

    It is dangerous out there. JSLint is here to help.

    +

    Warning

    +

    JSLint will hurt your feelings. Side effects may include + headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, + dry mouth, cleaner code, and a reduced error rate.

    +

    Further Reading

    + +
    +

    + Code Conventions for the JavaScript Programming Language.

    + + + + + +
    + + diff --git a/branch.autofix/index.html b/branch.autofix/index.html new file mode 100644 index 000000000..bc5217989 --- /dev/null +++ b/branch.autofix/index.html @@ -0,0 +1,126 @@ + + + + + + + + + + + +JSLint: The JavaScript Code Quality Tool + + +
    +
    Source
    +
    + + + +
    +
    + Warnings +
    +
    +
    + Function Report +
    +
    +
    + Property Directive + +
    +
    + + + + +
    +
    Options +
    Assume... +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Global variables... + +
    +
    +
    +
    + + + + + +
    + + diff --git a/branch.autofix/jslint.css b/branch.autofix/jslint.css new file mode 100644 index 000000000..9c1b1a2f9 --- /dev/null +++ b/branch.autofix/jslint.css @@ -0,0 +1,340 @@ +@font-face { + font-family: 'Programma'; + src: url('Programma.woff') format('woff'); +} +@font-face { + font-family: 'Daley'; + font-weight: bold; + src: url('Daley-Bold.woff') format('woff'); +} + +body { + background-color: antiquewhite; + color: black; + font-family: 'Daley', sans-serif; + margin: 0; + padding: 0; +} + +#JSLINT_TITLEBOX { + font-family: 'Daley', sans-serif; + margin: 0; + margin-left: 3%; + margin-right: 3%; + padding: 0; +} + +#JSLINT_TITLEBOX ul { + float: right; + font-size: 12pt; +} + +#JSLINT_TITLEBOX div { + color: darkslategray; + float: left; + font-size: 48pt; +} + +#JSLINT_ fieldset { + background-color: gainsboro; + border: 0; + clear: both; + margin-bottom: 1em; + margin-left: 3%; + margin-right: 3%; + margin-top: 1em; + padding: 0; + width: auto; +} +#JSLINT_ legend { + background-color: darkslategray; + border: 0; + color: white; + font-size: 100%; + font-style: normal; + font-weight: normal; + margin: 0; + padding-bottom: 0.25em; + padding-left: 0; + padding-right: 0; + padding-top: 0.25em; + text-align: center; + width: 100%; +} +#JSLINT_ button { + background-color: darkslategray; + border: 0; + color: white; + cursor: pointer; + font-family: 'Daley', sans-serif; + font-size: 100%; + font-style: normal; + margin-left: 1em; + margin-right: 1em; + padding-left: 1em; + padding-right: 1em; + padding-bottom: 0.25em; + padding-top: 0.25em; + text-align: center; +} +#JSLINT_ button:hover { + background-color: slategray; + color: white; + border: 0; +} + +#JSLINT_ button:active { + border: 0; + color: black; +} + +#JSLINT_ button:disabled { + background-color: gray; + border: 0; + color: gainsboro; +} + +a:visited { + color: #69565c; +} + +a:link { + color: black; +} + +#JSLINT_ input[type="text"] { + background-color: white; + border: 0; + color: black; + margin-bottom: 0.2em; + margin-right: 0.5em; + padding-left: 0.5em; + padding-right: 0.5em; + text-align: right; + width: 3em; +} + +#JSLINT_ textarea { + border: 0; + background-color: white; + border: 0; + font-family: Programma, monospace; + font-size: 100%; + font-weight: bold; + resize: none; +} + +#JSLINT_ textarea::selection { + background-color: yellow; + color: black; +} + +#JSLINT_NUMBER { + color: darkgray; + display: inline-block; + height: 4in; + margin: 2%; + margin-right: 0; + overflow: hidden; + padding-right: 0.5%; + text-align: right; + white-space: pre; + width: 4%; +} + +#JSLINT_SOURCE { + color: black; + display: inline-block; + font-size: 100%; + height: 4in; + margin: 2%; + margin-left: 0; + margin-right: 0; + overflow: auto; + padding-left: 0.5%; + white-space: pre; + width: 91%; +} + +#JSLINT_PROPERTY { + background-color: honeydew; + border: 0; + margin: 2%; + padding: 0.5%; + white-space: pre; + width: 95%; +} + +#JSLINT_GLOBAL { + white-space: normal; + width: 95%; +} +#JSLINT_ label { + font-family: sans-serif; + font-size: 90%; + padding-left: 0.25em; +} +#JSLINT_OPTIONS>div { + float: left; + margin: 0.5em; +} + +#JSLINT_ address { + color: black; + display: block; + float: right; + font-family: serif; + font-size: 90%; + margin-left: 1em; +} +#JSLINT_ dl { + background-color: cornsilk; + color: white; + margin: 0; + padding-bottom: 2pt; + padding-left: 1em; + padding-right: 1em; + padding-top: 2pt; +} +#JSLINT_ dfn { + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + margin-bottom: 2pt; +} +#JSLINT_ dt { + color: black; + display: block; + float: left; + font-family: serif; + font-size: 75%; + font-style: italic; + margin: 0; + width: 8em; + text-align: right; +} +#JSLINT_ dd { + color: black; + display: block; + font-family: Programma, monospace; + font-weight: bold; + margin-left: 8em; + padding-bottom: 2pt; +} +#JSLINT_WARNINGS>legend { + background-color: indianred; +} +#JSLINT_WARNINGS>div { + background-color: pink; + padding: 1em; +} +#JSLINT_WARNINGS cite { + color: black; + display: block; + font-family: serif; + font-size: 100%; + font-style: normal; + margin-bottom: 4pt; + margin-left: 20pt; + margin-right: 20pt; + margin-top: 4pt; + overflow-x: hidden; +} +#JSLINT_WARNINGS samp { + background-color: lavenderblush; + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + padding: 4pt; + margin-bottom: 0; + margin-left: 16pt; + margin-right: 16pt; + margin-top: 0; + white-space: pre-wrap; +} + +#JSLINT_REPORT center { + margin-top: 1em; +} + +#JSLINT_WARNINGS dl address { + color: black; + display: inline; + float: none; + font-size: 80%; + margin: 0; +} + +#JSLINT_REPORT>div { + padding: 1em; +} + +#JSLINT_ dl.level0 { + color: black; + background-color: white; +} + +#JSLINT_ dl.level1 { + color: black; + background-color: #ffffe0; /* yellow */ + margin-left: 1em; +} + +#JSLINT_ dl.level2 { + color: black; + background-color: #e0ffe0; /* green */ + margin-left: 2em; +} + +#JSLINT_ dl.level3 { + color: black; + background-color: #e0e0ff; /* blue */ + margin-left: 3em; +} + +#JSLINT_ dl.level4 { + background-color: #ffe0ff; /* purple */ + color: black; + margin-left: 4em; +} + +#JSLINT_ dl.level5 { + background-color: #ffe0e0; /* red */ + color: black; + margin-left: 5em; +} + +#JSLINT_ dl.level6 { + background-color: #ffe390; /* orange */ + color: black; + margin-left: 6em; +} + +#JSLINT_ dl.level7 { + background-color: #e0e0e0; /* gray */ + color: black; + margin-left: 7em; +} + +#JSLINT_ dl.level8 { + color: black; + margin-left: 8em; +} + +#JSLINT_ dl.level9 { + color: black; + margin-left: 9em; +} + +.none { + display: none; +} +.center { + text-align: center; +} diff --git a/branch.autofix/jslint.js b/branch.autofix/jslint.js new file mode 100644 index 000000000..49a4e2f9a --- /dev/null +++ b/branch.autofix/jslint.js @@ -0,0 +1,5089 @@ +// jslint.js +// 2018-09-13 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// jslint(source, option_object, global_array) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze, a string or an array of strings. +// option_object An object whose keys correspond to option names. +// global_array An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all of the functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// 1. If the source is a single string, split it into an array of strings. +// 2. Turn the source into an array of tokens. +// 3. Furcate the tokens into a parse tree. +// 4. Walk the tree, traversing all of the nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// 5. Check the whitespace between the tokens. + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*jslint long */ + +/*property + autofix, autofix_subroutine, map, repeat, source_autofixed, + a, and, arity, assign, b, bad_assignment_a, bad_directive_a, + bad_get, bad_module_name_a, bad_option_a, bad_property_a, bad_set, + bitwise, block, body, browser, c, calls, catch, charCodeAt, closer, + closure, code, column, complex, concat, constant, context, convert, + couch, create, d, dead, default, devel, directive, directives, + disrupt, dot, duplicate_a, edition, ellipsis, else, empty_block, + escape_mega, eval, every, expected_a, expected_a_at_b_c, + expected_a_b, expected_a_b_from_c_d, expected_a_before_b, + expected_a_next_at_b, expected_digits_after_a, expected_four_digits, + expected_identifier_a, expected_line_break_a_b, + expected_regexp_factor_a, expected_space_a_b, expected_statements_a, + expected_string_a, expected_type_string_a, exports, expression, + extra, finally, flag, for, forEach, free, from, froms, fud, fudge, + function_in_loop, functions, g, getset, global, i, id, identifier, + import, inc, indexOf, infix_in, init, initial, isArray, isNaN, join, + json, keys, label, label_a, lbp, led, length, level, line, lines, + live, long, loop, m, margin, match, message, misplaced_a, + misplaced_directive_a, missing_browser, missing_m, module, multivar, + naked_block, name, names, nested_comment, new, node, not_label_a, + nr, nud, number_isNaN, ok, open, option, out_of_scope_a, parameters, + parent, pop, property, push, quote, redefinition_a_b, replace, + required_a_optional_b, reserved_a, right, role, search, signature, + single, slice, some, sort, split, statement, stop, strict, + subscript_a, switch, test, this, thru, toString, todo_comment, + tokens, too_long, too_many_digits, tree, try, type, u, + unclosed_comment, unclosed_mega, unclosed_string, undeclared_a, + unexpected_a, unexpected_a_after_b, unexpected_a_before_b, + unexpected_at_top_level_a, unexpected_char_a, unexpected_comment, + unexpected_directive_a, unexpected_expression_a, unexpected_label_a, + unexpected_parens, unexpected_space_a_b, unexpected_statement_a, + unexpected_trailing_space, unexpected_typeof_a, uninitialized_a, + unreachable_a, unregistered_property_a, unsafe, unused_a, + use_double, use_open, use_spaces, use_strict, used, value, var_loop, + var_switch, variable, warning, warnings, weird_condition_a, + weird_expression_a, weird_loop, weird_relation_a, white, + wrap_assignment, wrap_condition, wrap_immediate, wrap_parameter, + wrap_regexp, wrap_unary, wrapped, writable, y +*/ + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than {} because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +function populate(array, object = empty(), value = true) { + +// Augment an object by taking property names from an array of strings. + + array.forEach(function (name) { + object[name] = value; + }); + return object; +} + +const allowed_option = { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + bitwise: true, + browser: [ + "caches", "clearInterval", "clearTimeout", "document", "DOMException", + "Element", "Event", "event", "FileReader", "FormData", "history", + "localStorage", "location", "MutationObserver", "name", "navigator", + "screen", "sessionStorage", "setInterval", "setTimeout", "Storage", + "URL", "window", "Worker", "XMLHttpRequest" + ], + couch: [ + "emit", "getRow", "isArray", "log", "provides", "registerType", + "require", "send", "start", "sum", "toJSON" + ], + convert: true, + devel: [ + "alert", "confirm", "console", "prompt" + ], + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + multivar: true, + node: [ + "Buffer", "clearImmediate", "clearInterval", "clearTimeout", + "console", "exports", "module", "process", "require", + "setImmediate", "setInterval", "setTimeout", "URL", + "URLSearchParams", "__dirname", "__filename" + ], + single: true, + this: true, + white: true +}; + +const anticondition = populate([ + "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%", + "typeof", "(number)", "(string)" +]); + +// These are the bitwise operators. + +const bitwiseop = populate([ + "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=", + ">>>", ">>>=" +]); + +const escapeable = populate([ + "\\", "/", "`", "b", "f", "n", "r", "t" +]); + +const opener = { + +// The open and close pairs. + + "(": ")", // paren + "[": "]", // bracket + "{": "}", // brace + "${": "}" // mega +}; + +// The relational operators. + +const relationop = populate([ + "!=", "!==", "==", "===", "<", "<=", ">", ">=" +]); + +// This is the set of infix operators that require a space on each side. + +const spaceop = populate([ + "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/", + "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=", + ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" +]); + +const standard = [ + +// These are the globals that are provided by the language standard. + + "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI", + "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error", + "EvalError", "Float32Array", "Float64Array", "Generator", + "GeneratorFunction", "Int8Array", "Int16Array", "Int32Array", "Intl", + "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat", + "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", + "Set", "String", "Symbol", "SyntaxError", "System", "TypeError", + "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", + "URIError", "WeakMap", "WeakSet" +]; + +const bundle = { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + and: "The '&&' subexpression should be wrapped in parens.", + bad_assignment_a: "Bad assignment to '{a}'.", + bad_directive_a: "Bad directive '{a}'.", + bad_get: "A get function takes no parameters.", + bad_module_name_a: "Bad module name '{a}'.", + bad_option_a: "Bad option '{a}'.", + bad_property_a: "Bad property name '{a}'.", + bad_set: "A set function takes one parameter.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + escape_mega: "Unexpected escapement in mega literal.", + expected_a: "Expected '{a}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: ( + "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'." + ), + expected_a_before_b: "Expected '{a}' before '{b}'.", + expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.", + expected_digits_after_a: "Expected digits after '{a}'.", + expected_four_digits: "Expected four digits after '\\u'.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.", + expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.", + expected_space_a_b: "Expected one space between '{a}' and '{b}'.", + expected_statements_a: "Expected statements before '{a}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + expected_type_string_a: "Expected a type string and instead saw '{a}'.", + function_in_loop: "Don't make functions within a loop.", + infix_in: ( + "Unexpected 'in'. Compare with undefined, " + + "or use the hasOwnProperty method instead." + ), + label_a: "'{a}' is a statement label.", + misplaced_a: "Place '{a}' at the outermost level.", + misplaced_directive_a: ( + "Place the '/*{a}*/' directive before the first statement." + ), + missing_browser: "/*global*/ requires the Assume a browser option.", + missing_m: "Expected 'm' flag on a multiline regular expression.", + naked_block: "Naked block.", + nested_comment: "Nested comment.", + not_label_a: "'{a}' is not a label.", + number_isNaN: "Use Number.isNaN function to compare with NaN.", + out_of_scope_a: "'{a}' is out of scope.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + required_a_optional_b: ( + "Required parameter '{a}' after optional parameter '{b}'." + ), + reserved_a: "Reserved name '{a}'.", + subscript_a: "['{a}'] is better written in dot notation.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line is longer than 80 characters.", + too_many_digits: "Too many digits.", + unclosed_comment: "Unclosed comment.", + unclosed_mega: "Unclosed mega literal.", + unclosed_string: "Unclosed string.", + undeclared_a: "Undeclared '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_a_after_b: "Unexpected '{a}' after '{b}'.", + unexpected_a_before_b: "Unexpected '{a}' before '{b}'.", + unexpected_at_top_level_a: "Expected '{a}' to be in a function.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_directive_a: "When using modules, don't use directive '/*{a}'.", + unexpected_expression_a: ( + "Unexpected expression '{a}' in statement position." + ), + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_parens: "Don't wrap function literals in parens.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_statement_a: ( + "Unexpected statement '{a}' in expression position." + ), + unexpected_trailing_space: "Unexpected trailing space.", + unexpected_typeof_a: ( + "Unexpected 'typeof'. Use '===' to compare directly with {a}." + ), + uninitialized_a: "Uninitialized '{a}'.", + unreachable_a: "Unreachable '{a}'.", + unregistered_property_a: "Unregistered property name '{a}'.", + unsafe: "Unsafe character '{a}'.", + unused_a: "Unused '{a}'.", + use_double: "Use double quotes, not single quotes.", + use_open: ( + "Wrap a ternary expression in parens, " + + "with a line break after the left paren." + ), + use_spaces: "Use spaces, not tabs.", + use_strict: "This function needs a \"use strict\" pragma.", + var_loop: "Don't declare variables in a loop.", + var_switch: "Don't declare variables in a switch.", + weird_condition_a: "Weird condition '{a}'.", + weird_expression_a: "Weird expression '{a}'.", + weird_loop: "Weird loop.", + weird_relation_a: "Weird relation '{a}'.", + wrap_assignment: "Don't wrap assignment statements in parens.", + wrap_condition: "Wrap the condition in parens.", + wrap_immediate: ( + "Wrap an immediate function invocation in parentheses to assist " + + "the reader in understanding that the expression is the result " + + "of a function, and not the function itself." + ), + wrap_parameter: "Wrap the parameter in parens.", + wrap_regexp: "Wrap this regexp in parens to avoid confusion.", + wrap_unary: "Wrap the unary expression in parens." +}; + +// Regular expression literals: + +// supplant {variables} +const rx_supplant = /\{([^{}]*)\}/g; +// carriage return, carriage return linefeed, or linefeed +const rx_crlf = /\n|\r\n?/; +// unsafe characters that are silently deleted by one or more browsers +const rx_unsafe = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; +// identifier +const rx_identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; +const rx_module = /^[a-zA-Z0-9_$:.@\-\/]+$/; +const rx_bad_property = /^_|\$|Sync\$|_$/; +// star slash +const rx_star_slash = /\*\//; +// slash star +const rx_slash_star = /\/\*/; +// slash star or ending slash +const rx_slash_star_or_slash = /\/\*|\/$/; +// uncompleted work comment +const rx_todo = /\b(?:todo|TO\s?DO|HACK)\b/; +// tab +const rx_tab = /\t/g; +// directive +const rx_directive = /^(jslint|property|global)\s+(.*)$/; +const rx_directive_part = /^([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*(.*)$/; +// token (sorry it is so long) +const rx_token = /^((\s+)|([a-zA-Z_$][a-zA-Z0-9_$]*)|[(){}\[\]?,:;'"~`]|=(?:==?|>)?|\.+|[*\/][*\/=]?|\+[=+]?|-[=\-]?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<= "a" && string <= "z\uffff") + || (string >= "A" && string <= "Z\uffff") + ); +} + +function supplant(string, object) { + return string.replace(rx_supplant, function (found, filling) { + const replacement = object[filling]; + return ( + replacement !== undefined + ? replacement + : found + ); + }); +} + +let anon; // The guessed name for anonymous functions. +let blockage; // The current block. +let block_stack; // The stack of blocks. +let declared_globals; // The object containing the global declarations. +let directives; // The directive comments. +let directive_mode; // true if directives are still allowed. +let early_stop; // true if JSLint cannot finish. +let exports; // The exported names and values. +let froms; // The array collecting all import-from strings. +let fudge; // true if the natural numbers start with 1. +let functionage; // The current function. +let functions; // The array containing all of the functions. +let global; // The global object; the outermost context. +let json_mode; // true if parsing JSON. +let lines; // The array containing source lines. +let lines_extra; // The array containing extra-metadata for each line. +let module_mode; // true if import or export was used. +let next_token; // The next token to be examined in the parse. +let option; // The options parameter. +let property; // The object containing the tallied property names. +let mega_mode; // true if currently parsing a megastring literal. +let stack; // The stack of functions. +let syntax; // The object containing the parser. +let token; // The current token being examined in the parse. +let token_nr; // The number of the next token. +let tokens; // The array of tokens. +let tenure; // The predefined property registry. +let tree; // The abstract parse tree. +let var_mode; // "var" if using var; "let" if using let. +let warnings; // The array collecting all generated warnings. + +// Error reportage functions: + +function artifact(the_token) { + +// Return a string representing an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); +} + +function artifact_line(the_token) { + +// Return the fudged line number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.line + fudge; +} + +function artifact_column(the_token) { + +// Return the fudged column number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.from + fudge; +} + +function autofix(warning) { + +// if option.autofix === true, then try to autofix tedious +// whitespace, single-quote, and regexp warnings + + let source_line; + let tmp; + source_line = lines[warning.line]; + switch (warning.message) { + case "Expected '\\s' and instead saw ' '.": + // autofix regexp - replace " " -> "\u0020" + lines_extra[warning.line].source_autofixed = ( + source_line.slice(0, warning.column) + "\\u0020" + + source_line.slice(warning.column + 1) + ); + break; + } + switch (warning.code) { + case "expected_a_at_b_c": + tmp = warning.b - warning.c; + // autofix indent - increment + if (tmp >= 0) { + lines_extra[warning.line].source_autofixed = ( + " ".repeat(tmp) + source_line + ); + break; + } + tmp = -tmp; + // autofix indent - decrement + if ((/^\u0020*?$/m).test(source_line.slice(0, warning.column))) { + lines_extra[warning.line].source_autofixed = ( + source_line.slice(tmp) + ); + break; + } + // autofix indent - newline + lines_extra[warning.line].source_autofixed = ( + source_line.slice(0, warning.column) + "\n" + + " ".repeat(warning.b) + source_line.slice(warning.column) + ); + break; + // autofix indent - newline + case "expected_a_next_at_b": + lines_extra[warning.line].source_autofixed = ( + source_line.slice(0, warning.column) + "\n" + + " ".repeat(warning.b) + source_line.slice(warning.column) + ); + break; + // autofix key - replace 100: ... -> "100": ... + case "expected_identifier_a": + if (!( + (/^\d+$/m).test(warning.a) + && source_line[warning.column + warning.a.length] === ":" + )) { + break; + } + lines_extra[warning.line].source_autofixed = ( + source_line.slice(0, warning.column) + "\"" + warning.a + "\"" + + source_line.slice(warning.column + warning.a.length) + ); + break; + // autofix whitespace - add + case "expected_space_a_b": + if (!( + (/^\d+$/m).test(warning.a) + && source_line[warning.column + warning.a.length] === ":" + )) { + break; + } + lines_extra[warning.line].source_autofixed = ( + source_line.slice(0, warning.column) + " " + + source_line.slice(warning.column) + ); + break; + // autofix whitespace - remove + case "unexpected_space_a_b": + lines_extra[warning.line].source_autofixed = ( + source_line.slice(0, warning.column - 1) + + source_line.slice(warning.column) + ); + break; + // autofix quote - replace single -> double + case "use_double": + if (warning.a[0].indexOf("\u0000") >= 0) { + break; + } + tmp = warning.a[0] + .replace((/\\\\/g), "\u0000\u0000") + .replace((/\\'/g), "\u0000\u0001") + .match(/^'.*?'/); + if (!tmp) { + break; + } + lines_extra[warning.line].source_autofixed = ( + source_line.slice(0, warning.column - 1) + "\"" + ) + tmp[0].slice(1, -1) + .replace((/\\?"/g), "\\\"") + .replace((/\u0000\u0000/g), "\\\\") + .replace((/\u0000\u0001/g), "'") + + "\"" + source_line.slice(warning.column + tmp[0].length - 1); + break; + // autofix tab - replace tab -> space + case "use_spaces": + lines_extra[warning.line].source_autofixed = ( + source_line.replace((/^(\u0020*?)\t/), "$1 ") + ); + break; + } +} + +function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + const warning = { // ~~ + name: "JSLintError", + column: column, + line: line, + code: code + }; + if (a !== undefined) { + warning.a = a; + } + if (b !== undefined) { + warning.b = b; + } + if (c !== undefined) { + warning.c = c; + } + if (d !== undefined) { + warning.d = d; + } + warning.message = supplant(bundle[code] || code, warning); + warnings.push(warning); + if (option.autofix) { + autofix(warning); + } + return warning; +} + +function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + let error; + error = warn_at(code, line, column, a, b, c, d); + // if autofix, then try to recover from error + if (option.autofix) { + return; + } + throw error; +} + +function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + if (the_token.warning === undefined) { + the_token.warning = warn_at( + code, + the_token.line, + the_token.from, + a || artifact(the_token), + b, + c, + d + ); + return the_token.warning; + } +} + +function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + let error; + if (the_token === undefined) { + the_token = next_token; + } + delete the_token.warning; + error = warn(code, the_token, a, b, c, d); + // if autofix, then try to recover from error + if (option.autofix) { + return the_token; + } + throw error; +} + +// Tokenize: + +function tokenize(source) { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + +// If the source is not an array, then it is split into lines at the +// carriage return/linefeed. + + lines = ( + Array.isArray(source) + ? source + : source.split(rx_crlf) + ); + lines_extra = lines.map(function () { + return {}; + }); + tokens = []; + + let char; // a popular character + let column = 0; // the column number of the next character + let first; // the first token + let from; // the starting column number of the token + let line = -1; // the line number of the next character + let nr = 0; // the next token number + let previous = global; // the previous token including comments + let prior = global; // the previous token excluding comments + let mega_from; // the starting column of megastring + let mega_line; // the starting line of megastring + let snippet; // a piece of string + let source_line; // the current line source string + + function next_line() { + +// Put the next line of source in source_line. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + let at; + column = 0; + line += 1; + source_line = lines[line]; + if (source_line !== undefined) { + at = source_line.search(rx_tab); + if (at >= 0) { + if (!option.white) { + warn_at("use_spaces", line, at + 1); + } + source_line = source_line.replace(rx_tab, " "); + } + at = source_line.search(rx_unsafe); + if (at >= 0) { + warn_at( + "unsafe", + line, + column + at, + "U+" + source_line.charCodeAt(at).toString(16) + ); + } + if (!option.white && source_line.slice(-1) === " ") { + warn_at( + "unexpected_trailing_space", + line, + source_line.length - 1 + ); + } + if ( + !option.long + && source_line.length > 80 + && !json_mode + && first + ) { + warn_at("too_long", line, 80); + } + } + return source_line; + } + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions snip, next_char, prev_char, +// some_digits, and escape help in the parsing of literals. + + function snip() { + +// Remove the last character from snippet. + + snippet = snippet.slice(0, -1); + } + + function next_char(match) { + +// Get the next character from the source line. Remove it from the source_line, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + return stop_at( + ( + char === "" + ? "expected_a" + : "expected_a_b" + ), + line, + column - 1, + match, + char + ); + } + if (source_line) { + char = source_line[0]; + source_line = source_line.slice(1); + snippet += char; + } else { + char = ""; + snippet += " "; + } + column += 1; + return char; + } + + function back_char() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the source_line. + + if (snippet) { + char = snippet.slice(-1); + source_line = char + source_line; + column -= 1; + snip(); + } else { + char = ""; + } + return char; + } + + function some_digits(rx, quiet) { + const result = source_line.match(rx); + if (result) { + char = result[1]; + column += char.length; + source_line = result[2]; + snippet += char; + } else { + char = ""; + if (!quiet) { + warn_at( + "expected_digits_after_a", + line, + column, + snippet + ); + } + } + return char.length; + } + + function escape(extra) { + next_char("\\"); + if (escapeable[char] === true) { + return next_char(); + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "u") { + if (next_char("u") === "{") { + if (json_mode) { + warn_at("unexpected_a", line, column - 1, char); + } + if (some_digits(rx_hexs) > 5) { + warn_at("too_many_digits", line, column - 1); + } + if (next_char() !== "}") { + stop_at("expected_a_before_b", line, column, "}", char); + } + return next_char(); + } + back_char(); + if (some_digits(rx_hexs, true) < 4) { + warn_at("expected_four_digits", line, column - 1); + } + return; + } + if (extra && extra.indexOf(char) >= 0) { + return next_char(); + } + warn_at("unexpected_a_before_b", line, column - 2, "\\", char); + } + + function make(id, value, identifier) { + +// Make the token object and append it to the tokens list. + + const the_token = { + from: from, + id: id, + identifier: Boolean(identifier), + line: line, + nr: nr, + thru: column + }; + tokens[nr] = the_token; + nr += 1; + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + directive_mode = false; + } + +// If the token is to have a value, give it one. + + if (value !== undefined) { + the_token.value = value; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option.white. + + if ( + previous.line === line + && previous.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (previous.id === "(comment)" || previous.id === "(regexp)") + ) { + warn( + "expected_space_a_b", + the_token, + artifact(previous), + artifact(the_token) + ); + } + if (previous.id === "." && id === "(number)") { + warn("expected_a_before_b", previous, "0", "."); + } + if (prior.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// The previous token is used to detect adjacency problems. + + previous = the_token; + +// The prior token is a previous token that was not a comment. The prior token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (previous.id !== "(comment)") { + prior = previous; + } + return the_token; + } + + function parse_directive(the_comment, body) { + +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + + const result = body.match(rx_directive_part); + if (result) { + let allowed; + const name = result[1]; + const value = result[2]; + if (the_comment.directive === "jslint") { + allowed = allowed_option[name]; + if ( + typeof allowed === "boolean" + || typeof allowed === "object" + ) { + if ( + value === "" + || value === "true" + || value === undefined + ) { + option[name] = true; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } else if (value === "false") { + option[name] = false; + } else { + warn("bad_option_a", the_comment, name + ":" + value); + } + } else { + warn("bad_option_a", the_comment, name); + } + } else if (the_comment.directive === "property") { + if (tenure === undefined) { + tenure = empty(); + } + tenure[name] = true; + } else if (the_comment.directive === "global") { + if (value) { + warn("bad_option_a", the_comment, name + ":" + value); + } + declared_globals[name] = false; + module_mode = the_comment; + } + return parse_directive(the_comment, result[3]); + } + if (body) { + return stop("bad_directive_a", the_comment, body); + } + } + + function comment(snippet) { + +// Make a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + + const the_comment = make("(comment)", snippet); + if (Array.isArray(snippet)) { + snippet = snippet.join(" "); + } + if (!option.devel && rx_todo.test(snippet)) { + warn("todo_comment", the_comment); + } + const result = snippet.match(rx_directive); + if (result) { + if (!directive_mode) { + warn_at("misplaced_directive_a", line, from, result[1]); + } else { + the_comment.directive = result[1]; + parse_directive(the_comment, result[2]); + } + directives.push(the_comment); + } + return the_comment; + } + + function regexp() { + +// Parse a regular expression literal. + + let multi_mode = false; + let result; + let value; + + function quantifier() { + +// Match an optional quantifier. + + if (char === "?" || char === "*" || char === "+") { + next_char(); + } else if (char === "{") { + if (some_digits(rx_digits, true) === 0) { + warn_at("expected_a", line, column, "0"); + } + if (next_char() === ",") { + some_digits(rx_digits, true); + next_char(); + } + next_char("}"); + } else { + return; + } + if (char === "?") { + next_char("?"); + } + } + + function subklass() { + +// Match a character in a character class. + + if (char === "\\") { + escape("BbDdSsWw-[]^"); + return true; + } + if ( + char === "" + || char === "[" + || char === "]" + || char === "/" + || char === "^" + || char === "-" + ) { + return false; + } + if (char === " ") { + warn_at("expected_a_b", line, column, "\\u0020", " "); + } else if (char === "`" && mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char(); + return true; + } + + function ranges() { + +// Match a range of subclasses. + + if (subklass()) { + if (char === "-") { + next_char("-"); + if (!subklass()) { + return stop_at( + "unexpected_a", + line, + column - 1, + "-" + ); + } + } + return ranges(); + } + } + + function klass() { + +// Match a class. + + next_char("["); + if (char === "^") { + next_char("^"); + } + (function classy() { + ranges(); + if (char !== "]" && char !== "") { + warn_at( + "expected_a_before_b", + line, + column, + "\\", + char + ); + next_char(); + return classy(); + } + }()); + next_char("]"); + } + + function choice() { + + function group() { + +// Match a group that starts with left paren. + + next_char("("); + if (char === "?") { + next_char("?"); + if (char === "=" || char === "!") { + next_char(); + } else { + next_char(":"); + } + } else if (char === ":") { + warn_at("expected_a_before_b", line, column, "?", ":"); + } + choice(); + next_char(")"); + } + + function factor() { + if ( + char === "" + || char === "/" + || char === "]" + || char === ")" + ) { + return false; + } + if (char === "(") { + group(); + return true; + } + if (char === "[") { + klass(); + return true; + } + if (char === "\\") { + escape("BbDdSsWw^${}[]():=!.-|*+?"); + return true; + } + if ( + char === "?" + || char === "+" + || char === "*" + || char === "}" + || char === "{" + ) { + warn_at( + "expected_a_before_b", + line, + column - 1, + "\\", + char + ); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column - 1, "`"); + } + } else if (char === " ") { + warn_at( + "expected_a_b", + line, + column - 1, + "\\s", + " " + ); + } else if (char === "$") { + if (source_line[0] !== "/") { + multi_mode = true; + } + } else if (char === "^") { + if (snippet !== "^") { + multi_mode = true; + } + } + next_char(); + return true; + } + + function sequence(follow) { + if (factor()) { + quantifier(); + return sequence(true); + } + if (!follow) { + warn_at("expected_regexp_factor_a", line, column, char); + } + } + +// Match a choice (a sequence that can be followed by | and another choice). + + sequence(); + if (char === "|") { + next_char("|"); + return choice(); + } + } + +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + next_char(); + if (char === "=") { + warn_at("expected_a_before_b", line, column, "\\", "="); + } + choice(); + +// Make sure there is a closing slash. + + snip(); + value = snippet; + next_char("/"); + +// Process dangling flag letters. + + const allowed = { + g: true, + i: true, + m: true, + u: true, + y: true + }; + const flag = empty(); + (function make_flag() { + if (is_letter(char)) { + if (allowed[char] !== true) { + warn_at("unexpected_a", line, column, char); + } + allowed[char] = false; + flag[char] = true; + next_char(); + return make_flag(); + } + }()); + back_char(); + if (char === "/" || char === "*") { + return stop_at("unexpected_a", line, from, char); + } + result = make("(regexp)", char); + result.flag = flag; + result.value = value; + if (multi_mode && !flag.m) { + warn_at("missing_m", line, column); + } + return result; + } + + function string(quote) { + +// Make a string token. + + let the_token; + snippet = ""; + next_char(); + + return (function next() { + if (char === quote) { + snip(); + the_token = make("(string)", snippet); + the_token.quote = quote; + return the_token; + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "\\") { + escape(quote); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char("`"); + } else { + next_char(); + } + return next(); + }()); + } + + function frack() { + if (char === ".") { + some_digits(rx_digits); + next_char(); + } + if (char === "E" || char === "e") { + next_char(); + if (char !== "+" && char !== "-") { + back_char(); + } + some_digits(rx_digits); + next_char(); + } + } + + function number() { + if (snippet === "0") { + next_char(); + if (char === ".") { + frack(); + } else if (char === "b") { + some_digits(rx_bits); + next_char(); + } else if (char === "o") { + some_digits(rx_octals); + next_char(); + } else if (char === "x") { + some_digits(rx_hexs); + next_char(); + } + } else { + next_char(); + frack(); + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + return stop_at( + "unexpected_a_after_b", + line, + column - 1, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + back_char(); + return make("(number)", snippet); + } + + function lex() { + let array; + let i = 0; + let j = 0; + let last; + let result; + let the_token; + if (!source_line) { + source_line = next_line(); + from = 0; + return ( + source_line === undefined + ? ( + mega_mode + ? stop_at("unclosed_mega", mega_line, mega_from) + : make("(end)") + ) + : lex() + ); + } + from = column; + result = source_line.match(rx_token); + +// result[1] token +// result[2] whitespace +// result[3] identifier +// result[4] number +// result[5] rest + + if (!result) { + return stop_at( + "unexpected_char_a", + line, + column, + source_line[0] + ); + } + + snippet = result[1]; + column += snippet.length; + source_line = result[5]; + +// Whitespace was matched. Call lex again to get more. + + if (result[2]) { + return lex(); + } + +// The token is an identifier. + + if (result[3]) { + return make(snippet, undefined, true); + } + +// The token is a number. + + if (result[4]) { + return number(snippet); + } + +// The token is a string. + + if (snippet === "\"") { + return string(snippet); + } + if (snippet === "'") { + if (!option.single) { + // preserve "result" argument for autofix + warn_at("use_double", line, column, result); + } + return string(snippet); + } + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (snippet === "`") { + if (mega_mode) { + return stop_at("expected_a_b", line, column, "}", "`"); + } + snippet = ""; + mega_from = from; + mega_line = line; + mega_mode = true; + +// Parsing a mega literal is tricky. First make a ` token. + + make("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + (function part() { + const at = source_line.search(rx_mega); + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + if (at < 0) { + snippet += source_line + "\n"; + return ( + next_line() === undefined + ? stop_at("unclosed_mega", mega_line, mega_from) + : part() + ); + } + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + snippet += source_line.slice(0, at); + column += at; + source_line = source_line.slice(at); + if (source_line[0] === "\\") { + stop_at("escape_mega", line, at); + } + make("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then make tokens that will become part of an expression until +// a } token is made. + + if (source_line[0] === "$") { + column += 2; + make("${"); + source_line = source_line.slice(2); + (function expr() { + const id = lex().id; + if (id === "{") { + return stop_at( + "expected_a_b", + line, + column, + "}", + "{" + ); + } + if (id !== "}") { + return expr(); + } + }()); + return part(); + } + }()); + source_line = source_line.slice(1); + column += 1; + mega_mode = false; + return make("`"); + } + +// The token is a // comment. + + if (snippet === "//") { + snippet = source_line; + source_line = ""; + the_token = comment(snippet); + if (mega_mode) { + warn("unexpected_comment", the_token, "`"); + } + return the_token; + } + +// The token is a /* comment. + + if (snippet === "/*") { + array = []; + if (source_line[0] === "/") { + warn_at("unexpected_a", line, column + i, "/"); + } + (function next() { + if (source_line > "") { + i = source_line.search(rx_star_slash); + if (i >= 0) { + return; + } + j = source_line.search(rx_slash_star); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + } + array.push(source_line); + source_line = next_line(); + if (source_line === undefined) { + return stop_at("unclosed_comment", line, column); + } + return next(); + }()); + snippet = source_line.slice(0, i); + j = snippet.search(rx_slash_star_or_slash); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + array.push(snippet); + column += i + 2; + source_line = source_line.slice(i + 2); + return comment(array); + } + +// The token is a slash. + + if (snippet === "/") { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + if (prior.identifier) { + if (!prior.dot) { + if (prior.id === "return") { + return regexp(); + } + if ( + prior.id === "(begin)" + || prior.id === "case" + || prior.id === "delete" + || prior.id === "in" + || prior.id === "instanceof" + || prior.id === "new" + || prior.id === "typeof" + || prior.id === "void" + || prior.id === "yield" + ) { + the_token = regexp(); + return stop("unexpected_a", the_token); + } + } + } else { + last = prior.id[prior.id.length - 1]; + if ("(,=:?[".indexOf(last) >= 0) { + return regexp(); + } + if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) { + the_token = regexp(); + warn("wrap_regexp", the_token); + return the_token; + } + } + if (source_line[0] === "/") { + column += 1; + source_line = source_line.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + } + return make(snippet); + } + + first = lex(); + json_mode = first.id === "{" || first.id === "["; + +// This is the only loop in JSLint. It will turn into a recursive call to lex +// when ES6 has been finished and widely deployed and adopted. + + while (true) { + if (lex().id === "(end)") { + break; + } + } +} + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + +function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!rx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!rx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property[id] === "number") { + property[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (tenure !== undefined) { + if (tenure[id] !== true) { + warn("unregistered_property_a", name); + } + } else { + if (name.identifier && rx_bad_property.test(id)) { + warn("bad_property_a", name); + } + } + property[id] = 1; + } + return id; +} + +function dispense() { + +// Deliver the next token, skipping the comments. + + const cadet = tokens[token_nr]; + token_nr += 1; + if (cadet.id === "(comment)") { + if (json_mode) { + warn("unexpected_a", cadet); + } + return dispense(); + } else { + return cadet; + } +} + +function lookahead() { + +// Look ahead one token without advancing. + + const old_token_nr = token_nr; + const cadet = dispense(true); + token_nr = old_token_nr; + return cadet; +} + +function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if (token.identifier && token.id !== "function") { + anon = token.id; + } else if (token.id === "(string)" && rx_identifier.test(token.value)) { + anon = token.value; + } + +// Attempt to match next_token with an expected id. + + if (id !== undefined && next_token.id !== id) { + return ( + match === undefined + ? stop("expected_a_b", next_token, id, artifact()) + : stop( + "expected_a_b_from_c_d", + next_token, + id, + artifact(match), + artifact_line(match), + artifact(next_token) + ) + ); + } + +// Promote the tokens, skipping comments. + + token = next_token; + next_token = dispense(); + if (next_token.id === "(end)") { + token_nr -= 1; + } +} + +// Parsing of JSON is simple: + +function json_value() { + let negative; + if (next_token.id === "{") { + return (function json_object() { + const brace = next_token; + const object = empty(); + const properties = []; + brace.expression = properties; + advance("{"); + if (next_token.id !== "}") { + (function next() { + let name; + let value; + if (next_token.quote !== "\"") { + warn( + "unexpected_a", + next_token, + next_token.quote + ); + } + name = next_token; + advance("(string)"); + if (object[token.value] !== undefined) { + warn("duplicate_a", token); + } else if (token.value === "__proto__") { + warn("bad_property_a", token); + } else { + object[token.value] = token; + } + advance(":"); + value = json_value(); + value.label = name; + properties.push(value); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("}", brace); + return brace; + }()); + } + if (next_token.id === "[") { + return (function json_array() { + const bracket = next_token; + const elements = []; + bracket.expression = elements; + advance("["); + if (next_token.id !== "]") { + (function next() { + elements.push(json_value()); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]", bracket); + return bracket; + }()); + } + if ( + next_token.id === "true" + || next_token.id === "false" + || next_token.id === "null" + ) { + advance(); + return token; + } + if (next_token.id === "(number)") { + if (!rx_JSON_number.test(next_token.value)) { + warn("unexpected_a"); + } + advance(); + return token; + } + if (next_token.id === "(string)") { + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + advance(); + return token; + } + if (next_token.id === "-") { + negative = next_token; + negative.arity = "unary"; + advance("-"); + advance("(number)"); + negative.expression = token; + return negative; + } + stop("unexpected_a"); +} + +// Now we parse JavaScript. + +function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + const id = name.id; + +// Reserved words may not be enrolled. + + if (syntax[id] !== undefined && id !== "ignore") { + warn("reserved_a", name); + } else { + +// Has the name been enrolled in this context? + + let earlier = functionage.context[id]; + if (earlier) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + +// Has the name been enrolled in an outer context? + + } else { + stack.forEach(function (value) { + const item = value.context[id]; + if (item !== undefined) { + earlier = item; + } + }); + if (earlier) { + if (id === "ignore") { + if (earlier.role === "variable") { + warn("unexpected_a", name); + } + } else { + if ( + ( + role !== "exception" + || earlier.role !== "exception" + ) + && role !== "parameter" + && role !== "function" + ) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + } + } + } + +// Enroll it. + + functionage.context[id] = name; + name.dead = true; + name.parent = functionage; + name.init = false; + name.role = role; + name.used = 0; + name.writable = !readonly; + } + } +} + +function expression(rbp, initial) { + +// This is the heart of the Pratt parser. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// nud Null denotation +// led Left denotation +// lbp Left binding power +// rbp Right binding power + +// It processes a nud (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop. It +// returns the expression's parse tree. + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement, + + if (!initial) { + advance(); + } + the_symbol = syntax[token.id]; + if (the_symbol !== undefined && the_symbol.nud !== undefined) { + left = the_symbol.nud(); + } else if (token.identifier) { + left = token; + left.arity = "variable"; + } else { + return stop("unexpected_a", token); + } + (function right() { + the_symbol = syntax[next_token.id]; + if ( + the_symbol !== undefined + && the_symbol.led !== undefined + && rbp < the_symbol.lbp + ) { + advance(); + left = the_symbol.led(left); + return right(); + } + }()); + return left; +} + +function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = next_token; + let the_value; + the_paren.free = true; + advance("("); + the_value = expression(0); + advance(")"); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (anticondition[the_value.id] === true) { + warn("unexpected_a", the_value); + } + return the_value; +} + +function is_weird(thing) { + return ( + thing.id === "(regexp)" + || thing.id === "{" + || thing.id === "=>" + || thing.id === "function" + || (thing.id === "[" && thing.arity === "unary") + ); +} + +function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + return ( + Array.isArray(b) + && a.length === b.length + && a.every(function (value, index) { + return are_similar(value, b[index]); + }) + ); + } + if (Array.isArray(b)) { + return false; + } + if (a.id === "(number)" && b.id === "(number)") { + return a.value === b.value; + } + let a_string; + let b_string; + if (a.id === "(string)") { + a_string = a.value; + } else if (a.id === "`" && a.constant) { + a_string = a.value[0]; + } + if (b.id === "(string)") { + b_string = b.value; + } else if (b.id === "`" && b.constant) { + b_string = b.value[0]; + } + if (typeof a_string === "string") { + return a_string === b_string; + } + if (is_weird(a) || is_weird(b)) { + return false; + } + if (a.arity === b.arity && a.id === b.id) { + if (a.id === ".") { + return ( + are_similar(a.expression, b.expression) + && are_similar(a.name, b.name) + ); + } + if (a.arity === "unary") { + return are_similar(a.expression, b.expression); + } + if (a.arity === "binary") { + return ( + a.id !== "(" + && are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + ); + } + if (a.arity === "ternary") { + return ( + are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + && are_similar(a.expression[2], b.expression[2]) + ); + } + if (a.arity === "function" && a.arity === "regexp") { + return false; + } + return true; + } + return false; +} + +function semicolon() { + +// Try to match a semicolon. + + if (next_token.id === ";") { + advance(";"); + } else { + warn_at( + "expected_a_b", + token.line, + token.thru, + ";", + artifact(next_token) + ); + } + anon = "anonymous"; +} + +function statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token.identifier && next_token.id === ":") { + the_label = token; + if (the_label.id === "ignore") { + warn("unexpected_a", the_label); + } + advance(":"); + if ( + next_token.id === "do" + || next_token.id === "for" + || next_token.id === "switch" + || next_token.id === "while" + ) { + enroll(the_label, "label", true); + the_label.init = true; + the_label.dead = false; + the_statement = statement(); + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token; + first.statement = true; + the_symbol = syntax[first.id]; + if (the_symbol !== undefined && the_symbol.fud !== undefined) { + the_symbol.disrupt = false; + the_symbol.statement = true; + the_statement = the_symbol.fud(); + } else { + +// It is an expression statement. + + the_statement = expression(0, true); + if (the_statement.wrapped && the_statement.id !== "(") { + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; +} + +function statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const array = []; + (function next(disrupt) { + if ( + next_token.id !== "}" + && next_token.id !== "case" + && next_token.id !== "default" + && next_token.id !== "else" + && next_token.id !== "(end)" + ) { + let a_statement = statement(); + array.push(a_statement); + if (disrupt) { + warn("unreachable_a", a_statement); + } + return next(a_statement.disrupt); + } + }(false)); + return array; +} + +function not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === global) { + warn("unexpected_at_top_level_a", thing); + } +} + +function top_level_only(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== global) { + warn("misplaced_a", the_thing); + } +} + +function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token; + the_block.arity = "statement"; + the_block.body = special === "body"; + +// All top level function bodies should include the "use strict" pragma unless +// the whole file is strict or the file is a module or the function parameters +// use es6 syntax. + + if ( + special === "body" + && stack.length === 1 + && next_token.value === "use strict" + ) { + the_block.strict = next_token; + next_token.statement = true; + advance("(string)"); + advance(";"); + } + stmts = statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option.devel && special !== "ignore") { + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; +} + +function mutation_check(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + warn("bad_assignment_a", the_thing); + return false; + } + return true; +} + +function left_check(left, right) { + +// Warn if the left is not one of these: +// e.b +// e[b] +// e() +// ?: +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !left_check(left.expression[1]) + && !left_check(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right); + return false; + } + return true; +} + +// These functions are used to specify the grammar of our language: + +function symbol(id, bp) { + +// Make a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax[id] = the_symbol; + } + return the_symbol; +} + +function assignment(id) { + +// Make an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led = function (left) { + const the_token = token; + let right; + the_token.arity = "assignment"; + right = expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "pre" + || right.arity === "post" + ) { + warn("unexpected_a", right); + } + mutation_check(left); + return the_token; + }; + return the_symbol; +} + +function constant(id, type, value) { + +// Make a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud = ( + typeof value === "function" + ? value + : function () { + token.constant = true; + if (value !== undefined) { + token.value = value; + } + return token; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; +} + +function infix(id, bp, f) { + +// Make an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, expression(bp)]; + return the_token; + }; + return the_symbol; +} + +function infixr(id, bp) { + +// Make a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + the_token.expression = [left, expression(bp - 1)]; + return the_token; + }; + return the_symbol; +} + +function post(id) { + +// Make one of the post operators. + + const the_symbol = symbol(id, 150); + the_symbol.led = function (left) { + token.expression = left; + token.arity = "post"; + mutation_check(token.expression); + return token; + }; + return the_symbol; +} + +function pre(id) { + +// Make one of the pre operators. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "pre"; + the_token.expression = expression(150); + mutation_check(the_token.expression); + return the_token; + }; + return the_symbol; +} + +function prefix(id, f) { + +// Make a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = expression(150); + return the_token; + }; + return the_symbol; +} + +function stmt(id, f) { + +// Make a statement. + + const the_symbol = symbol(id); + the_symbol.fud = function () { + token.arity = "statement"; + return f(); + }; + return the_symbol; +} + +function ternary(id1, id2) { + +// Make a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led = function (left) { + const the_token = token; + const second = expression(20); + advance(id2); + token.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, expression(10)]; + return the_token; + }; + return the_symbol; +} + +// Begin defining the language. + +syntax = empty(); + +symbol("}"); +symbol(")"); +symbol("]"); +symbol(","); +symbol(";"); +symbol(":"); +symbol("*/"); +symbol("await"); +symbol("case"); +symbol("catch"); +symbol("class"); +symbol("default"); +symbol("else"); +symbol("enum"); +symbol("finally"); +symbol("implements"); +symbol("interface"); +symbol("package"); +symbol("private"); +symbol("protected"); +symbol("public"); +symbol("static"); +symbol("super"); +symbol("void"); +symbol("yield"); + +constant("(number)", "number"); +constant("(regexp)", "regexp"); +constant("(string)", "string"); +constant("arguments", "object", function () { + warn("unexpected_a", token); + return token; +}); +constant("eval", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("false", "boolean", false); +constant("Function", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("ignore", "undefined", function () { + warn("unexpected_a", token); + return token; +}); +constant("Infinity", "number", Infinity); +constant("isFinite", "function", function () { + warn("expected_a_b", token, "Number.isFinite", "isFinite"); + return token; +}); +constant("isNaN", "function", function () { + warn("number_isNaN", token); + return token; +}); +constant("NaN", "number", NaN); +constant("null", "null", null); +constant("this", "object", function () { + if (!option.this) { + warn("unexpected_a", token); + } + return token; +}); +constant("true", "boolean", true); +constant("undefined", "undefined"); + +assignment("="); +assignment("+="); +assignment("-="); +assignment("*="); +assignment("/="); +assignment("%="); +assignment("&="); +assignment("|="); +assignment("^="); +assignment("<<="); +assignment(">>="); +assignment(">>>="); + +infix("||", 40); +infix("&&", 50); +infix("|", 70); +infix("^", 80); +infix("&", 90); +infix("==", 100); +infix("===", 100); +infix("!=", 100); +infix("!==", 100); +infix("<", 110); +infix(">", 110); +infix("<=", 110); +infix(">=", 110); +infix("in", 110); +infix("instanceof", 110); +infix("<<", 120); +infix(">>", 120); +infix(">>>", 120); +infix("+", 130); +infix("-", 130); +infix("*", 140); +infix("/", 140); +infix("%", 140); +infixr("**", 150); +infix("(", 160, function (left) { + const the_paren = token; + let the_argument; + if (left.id !== "function") { + left_check(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (next_token.id !== ")") { + (function next() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + the_paren.free = true; + if (the_argument.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + the_paren.free = false; + } + return the_paren; +}); +infix(".", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("[", 170, function (left) { + const the_token = token; + const the_subscript = expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + const name = survey(the_subscript); + if (rx_identifier.test(name)) { + warn("subscript_a", the_subscript, name); + } + } + left_check(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; +}); +infix("=>", 170, function (left) { + return stop("wrap_parameter", left); +}); + +function do_tick() { + const the_tick = token; + the_tick.value = []; + the_tick.expression = []; + if (next_token.id !== "`") { + (function part() { + advance("(string)"); + the_tick.value.push(token); + if (next_token.id === "${") { + advance("${"); + the_tick.expression.push(expression(0)); + advance("}"); + return part(); + } + }()); + } + advance("`"); + return the_tick; +} + +infix("`", 160, function (left) { + const the_tick = do_tick(); + left_check(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; +}); + +post("++"); +post("--"); +pre("++"); +pre("--"); + +prefix("+"); +prefix("-"); +prefix("~"); +prefix("!"); +prefix("!!"); +prefix("[", function () { + const the_token = token; + the_token.expression = []; + if (next_token.id !== "]") { + (function next() { + let element; + let ellipsis = false; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + element = expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]"); + return the_token; +}); +prefix("/=", function () { + stop("expected_a_b", token, "/\\=", "/="); +}); +prefix("=>", function () { + return stop("expected_a_before_b", token, "()", "=>"); +}); +prefix("new", function () { + const the_new = token; + const right = expression(160); + if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "()", artifact(next_token)); + } + the_new.expression = right; + return the_new; +}); +prefix("typeof"); +prefix("void", function () { + const the_void = token; + warn("unexpected_a", the_void); + the_void.expression = expression(0); + return the_void; +}); + +function parameter_list() { + let complex = false; + const list = []; + let optional; + const signature = ["("]; + if (next_token.id !== ")" && next_token.id !== "(end)") { + (function parameter() { + let ellipsis = false; + let param; + if (next_token.id === "{") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("{"); + signature.push("{"); + (function subparameter() { + let subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (next_token.id === ":") { + advance(":"); + advance(); + token.label = subparam; + subparam = token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + } + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return subparameter(); + } + }()); + list.push(param); + advance("}"); + signature.push("}"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else if (next_token.id === "[") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("["); + signature.push("[]"); + (function subparameter() { + const subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + return subparameter(); + } + }()); + list.push(param); + advance("]"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else { + if (next_token.id === "...") { + complex = true; + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + } + if (!next_token.identifier) { + return stop("expected_identifier_a"); + } + param = next_token; + list.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (next_token.id === "=") { + complex = true; + optional = param; + advance("="); + param.expression = expression(0); + } else { + if (optional !== undefined) { + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } + } + }()); + } + advance(")"); + signature.push(")"); + return [list, signature.join(""), complex]; +} + +function do_function(the_function) { + let name; + if (the_function === undefined) { + the_function = token; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + name = next_token; + enroll(name, "variable", true); + the_function.name = name; + name.init = true; + name.calls = empty(); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + if (next_token.identifier) { + name = next_token; + the_function.name = name; + advance(); + } else { + the_function.name = anon; + } + } + } else { + name = the_function.name; + } + the_function.level = functionage.level + 1; + if (mega_mode) { + warn("unexpected_a", the_function); + } + +// Don't make functions in loops. It is inefficient, and it can lead to scoping +// errors. + + if (functionage.loop > 0) { + warn("function_in_loop", the_function); + } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + the_function.context = empty(); + the_function.finally = 0; + the_function.loop = 0; + the_function.switch = 0; + the_function.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functions.push(the_function); + functionage = the_function; + if (the_function.arity !== "statement" && typeof name === "object") { + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// Parse the parameter list. + + advance("("); + token.free = false; + token.arity = "function"; + const pl = parameter_list(); + functionage.parameters = pl[0]; + functionage.signature = pl[1]; + functionage.complex = pl[2]; + functionage.parameters.forEach(function enroll_parameter(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + name.names.forEach(enroll_parameter); + } + }); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && next_token.line === token.line + ) { + return stop("unexpected_a", next_token); + } + if (next_token.id === "." || next_token.id === "[") { + warn("unexpected_a"); + } + +// Restore the previous context. + + functionage = stack.pop(); + return the_function; +} + +prefix("function", do_function); + +function fart(pl) { + if (next_token.id === ";") { + stop("wrap_assignment", token); + } + advance("=>"); + const the_fart = token; + the_fart.arity = "binary"; + the_fart.name = "=>"; + the_fart.level = functionage.level + 1; + functions.push(the_fart); + if (functionage.loop > 0) { + warn("function_in_loop", the_fart); + } + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + the_fart.context = empty(); + the_fart.finally = 0; + the_fart.loop = 0; + the_fart.switch = 0; + the_fart.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functionage = the_fart; + the_fart.parameters = pl[0]; + the_fart.signature = pl[1]; + the_fart.complex = true; + the_fart.parameters.forEach(function (name) { + enroll(name, "parameter", true); + }); + if (next_token.id === "{") { + warn("expected_a_b", the_fart, "function", "=>"); + the_fart.block = block("body"); + } else { + the_fart.expression = expression(0); + } + functionage = stack.pop(); + return the_fart; +} + +prefix("(", function () { + const the_paren = token; + let the_value; + const cadet = lookahead().id; + +// We can distinguish between a parameter list for => and a wrapped expression +// with one token of lookahead. + + if ( + next_token.id === ")" + || next_token.id === "..." + || (next_token.identifier && (cadet === "," || cadet === "=")) + ) { + the_paren.free = false; + return fart(parameter_list()); + } + the_paren.free = true; + the_value = expression(0); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + if (next_token.id === "=>") { + if (the_value.arity !== "variable") { + if (the_value.id === "{" || the_value.id === "[") { + warn("expected_a_before_b", the_paren, "function", "("); + return stop("expected_a_b", next_token, "{", "=>"); + } + return stop("expected_identifier_a", the_value); + } + the_paren.expression = [the_value]; + return fart([the_paren.expression, "(" + the_value.id + ")"]); + } + return the_value; +}); +prefix("`", do_tick); +prefix("{", function () { + const the_brace = token; + const seen = empty(); + the_brace.expression = []; + if (next_token.id !== "}") { + (function member() { + let extra; + let full; + let id; + let name = next_token; + let value; + advance(); + if ( + (name.id === "get" || name.id === "set") + && next_token.identifier + ) { + if (!option.getset) { + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + next_token.id; + name = next_token; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (next_token.id === "}" || next_token.id === ",") { + if (typeof extra === "string") { + advance("("); + } + value = expression(Infinity, true); + } else if (next_token.id === "(") { + value = do_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + advance("("); + } + advance(":"); + value = expression(0); + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + advance(":"); + value = expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (next_token.id === ",") { + advance(","); + return member(); + } + }()); + } + advance("}"); + return the_brace; +}); + +stmt(";", function () { + warn("unexpected_a", token); + return token; +}); +stmt("{", function () { + warn("naked_block", token); + return block("naked"); +}); +stmt("break", function () { + const the_break = token; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (next_token.identifier && token.line === next_token.line) { + the_label = functionage.context[next_token.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + warn( + (the_label !== undefined && the_label.dead) + ? "out_of_scope_a" + : "not_label_a" + ); + } else { + the_label.used += 1; + } + the_break.label = next_token; + advance(); + } + advance(";"); + return the_break; +}); + +function do_var() { + const the_statement = token; + const is_const = the_statement.id === "const"; + the_statement.names = []; + +// A program may use var or let, but not both. + + if (!is_const) { + if (var_mode === undefined) { + var_mode = the_statement.id; + } else if (the_statement.id !== var_mode) { + warn( + "expected_a_b", + the_statement, + var_mode, + the_statement.id + ); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + warn("var_switch", the_statement); + } + if (functionage.loop > 0 && the_statement.id === "var") { + warn("var_loop", the_statement); + } + (function next() { + if (next_token.id === "{" && the_statement.id !== "var") { + const the_brace = next_token; + the_brace.names = []; + advance("{"); + (function pair() { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + survey(name); + advance(); + if (next_token.id === ":") { + advance(":"); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + next_token.label = name; + the_brace.names.push(next_token); + enroll(next_token, "variable", is_const); + advance(); + } else { + the_brace.names.push(name); + enroll(name, "variable", is_const); + } + if (next_token.id === ",") { + advance(","); + return pair(); + } + }()); + advance("}"); + advance("="); + the_brace.expression = expression(0); + the_statement.names.push(the_brace); + } else if (next_token.id === "[" && the_statement.id !== "var") { + const the_bracket = next_token; + the_bracket.names = []; + advance("["); + (function element() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + advance(); + the_bracket.names.push(name); + enroll(name, "variable", the_statement.id === "const"); + if (ellipsis) { + name.ellipsis = true; + } else if (next_token.id === ",") { + advance(","); + return element(); + } + }()); + advance("]"); + advance("="); + the_bracket.expression = expression(0); + the_statement.names.push(the_bracket); + } else if (next_token.identifier) { + const name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", is_const); + if (next_token.id === "=" || is_const) { + advance("="); + name.init = true; + name.expression = expression(0); + } + the_statement.names.push(name); + } else { + return stop("expected_identifier_a", next_token); + } + if (next_token.id === ",") { + if (!option.multivar) { + warn("expected_a_b", next_token, ";", ","); + } + advance(","); + return next(); + } + }()); + the_statement.open = ( + the_statement.names.length > 1 + && the_statement.line !== the_statement.names[1].line + ); + semicolon(); + return the_statement; +} + +stmt("const", do_var); +stmt("continue", function () { + const the_continue = token; + if (functionage.loop < 1 || functionage.finally > 0) { + warn("unexpected_a", the_continue); + } + not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; +}); +stmt("debugger", function () { + const the_debug = token; + if (!option.devel) { + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; +}); +stmt("delete", function () { + const the_token = token; + const the_value = expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; +}); +stmt("do", function () { + const the_do = token; + not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; +}); +stmt("export", function () { + const the_export = token; + let the_id; + let the_name; + let the_thing; + + function export_id() { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + the_id = next_token.id; + the_name = global.context[the_id]; + if (the_name === undefined) { + warn("unexpected_a"); + } else { + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a"); + } + exports[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + } + + the_export.expression = []; + if (next_token.id === "default") { + if (exports.default !== undefined) { + warn("duplicate_a"); + } + advance("default"); + the_thing = expression(0); + semicolon(); + exports.default = the_thing; + the_export.expression.push(the_thing); + } else { + if (next_token.id === "function") { + the_thing = statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a", the_name); + } + exports[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + warn("unexpected_a"); + } else if (next_token.id === "{") { + advance("{"); + (function loop() { + export_id(); + if (next_token.id === ",") { + advance(","); + return loop(); + } + }()); + advance("}"); + semicolon(); + } else { + export_id(); + if (the_name.writable !== true) { + warn("unexpected_a", token); + } + semicolon(); + } + } + module_mode = true; + return the_export; +}); +stmt("for", function () { + let first; + const the_for = token; + if (!option.for) { + warn("unexpected_a", the_for); + } + not_top_level(the_for); + functionage.loop += 1; + advance("("); + token.free = true; + if (next_token.id === ";") { + return stop("expected_a_b", the_for, "while (", "for (;"); + } + if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + return stop("unexpected_a"); + } + first = expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = expression(0); + advance(";"); + the_for.inc = expression(0); + if (the_for.inc.id === "++") { + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; +}); +stmt("function", do_function); +stmt("if", function () { + let the_else; + const the_if = token; + the_if.expression = condition(); + the_if.block = block(); + if (next_token.id === "else") { + advance("else"); + the_else = token; + the_if.else = ( + next_token.id === "if" + ? statement() + : block() + ); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + the_if.disrupt = true; + } else { + warn("unexpected_a", the_else); + } + } + } + return the_if; +}); +stmt("import", function () { + const the_import = token; + let name; + if (typeof module_mode === "object") { + warn("unexpected_directive_a", module_mode, module_mode.directive); + } + module_mode = true; + if (next_token.identifier) { + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + const names = []; + advance("{"); + if (next_token.id !== "}") { + while (true) { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (next_token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token; + if (!rx_module.test(token.value)) { + warn("bad_module_name_a", token); + } + froms.push(token.value); + semicolon(); + return the_import; +}); +stmt("let", do_var); +stmt("return", function () { + const the_return = token; + not_top_level(the_return); + if (functionage.finally > 0) { + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (next_token.id !== ";" && the_return.line === next_token.line) { + the_return.expression = expression(10); + } + advance(";"); + return the_return; +}); +stmt("switch", function () { + let dups = []; + let last; + let stmts; + const the_cases = []; + let the_disrupt = true; + const the_switch = token; + not_top_level(the_switch); + if (functionage.finally > 0) { + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token.free = true; + the_switch.expression = expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + (function major() { + const the_case = next_token; + the_case.arity = "statement"; + the_case.expression = []; + (function minor() { + advance("case"); + token.switch = true; + const exp = expression(0); + if (dups.some(function (thing) { + return are_similar(thing, exp); + })) { + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (next_token.id === "case") { + return minor(); + } + }()); + stmts = statements(); + if (stmts.length < 1) { + warn("expected_statements_a"); + return; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "break;", + artifact(next_token) + ); + } + if (next_token.id === "case") { + return major(); + } + }()); + dups = undefined; + if (next_token.id === "default") { + const the_default = next_token; + advance("default"); + token.switch = true; + advance(":"); + the_switch.else = statements(); + if (the_switch.else.length < 1) { + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + const the_last = the_switch.else[the_switch.else.length - 1]; + if (the_last.id === "break" && the_last.label === undefined) { + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; +}); +stmt("throw", function () { + const the_throw = token; + the_throw.disrupt = true; + the_throw.expression = expression(10); + semicolon(); + if (functionage.try > 0) { + warn("unexpected_a", the_throw); + } + return the_throw; +}); +stmt("try", function () { + let the_catch; + let the_disrupt; + const the_try = token; + if (functionage.try > 0) { + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (next_token.id === "catch") { + let ignored = "ignore"; + the_catch = next_token; + the_try.catch = the_catch; + advance("catch"); + advance("("); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + if (next_token.id !== "ignore") { + ignored = undefined; + the_catch.name = next_token; + enroll(next_token, "exception", true); + } + advance(); + advance(")"); + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "catch", + artifact(next_token) + ); + + } + if (next_token.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; +}); +stmt("var", do_var); +stmt("while", function () { + const the_while = token; + not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; +}); +stmt("with", function () { + stop("unexpected_a", token); +}); + +ternary("?", ":"); + +// Ambulation of the parse tree. + +function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; +} + +function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all of the +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + } + }; +} + +const posts = empty(); +const pres = empty(); +const preaction = action(pres); +const postaction = action(posts); +const preamble = amble(pres); +const postamble = amble(posts); + +function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.id === "function") { + walk_statement(thing.block); + } + if (thing.arity === "pre" || thing.arity === "post") { + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + warn("unexpected_statement_a", thing); + } + postamble(thing); + } + } +} + +function walk_statement(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_statement); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + ) { + warn( + (thing.id === "(string)" && thing.value === "use strict") + ? "unexpected_a" + : "unexpected_expression_a", + thing + ); + } + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + } +} + +function lookup(thing) { + if (thing.arity === "variable") { + +// Look up the variable in the current context. + + let the_variable = functionage.context[thing.id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable === undefined) { + stack.forEach(function (outer) { + const a_variable = outer.context[thing.id]; + if ( + a_variable !== undefined + && a_variable.role !== "label" + ) { + the_variable = a_variable; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (the_variable === undefined) { + if (declared_globals[thing.id] === undefined) { + warn("undeclared_a", thing); + return; + } + the_variable = { + dead: false, + parent: global, + id: thing.id, + init: true, + role: "variable", + used: 0, + writable: false + }; + global.context[thing.id] = the_variable; + } + the_variable.closure = true; + functionage.context[thing.id] = the_variable; + } else if (the_variable.role === "label") { + warn("label_a", thing); + } + if ( + the_variable.dead + && ( + the_variable.calls === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + ) { + warn("out_of_scope_a", thing); + } + return the_variable; + } +} + +function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); +} + +function preaction_function(thing) { + if (thing.arity === "statement" && blockage.body !== true) { + warn("unexpected_a", thing); + } + if (thing.level === 1) { + if ( + module_mode === true + || global.strict !== undefined + || thing.complex + ) { + if (thing.id !== "=>" && thing.block.strict !== undefined) { + warn("unexpected_a", thing.block.strict); + } + } else { + if (thing.block.strict === undefined) { + warn("use_strict", thing); + } + } + } + stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); +} + +function bitwise_check(thing) { + if (!option.bitwise && bitwiseop[thing.id] === true) { + warn("unexpected_a", thing); + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + warn("unexpected_a", thing); + } +} + +function pop_block() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); +} + +function activate(name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.init = true; + } + } + blockage.live.push(name); +} + +function action_var(thing) { + thing.names.forEach(activate); +} + +preaction("assignment", bitwise_check); +preaction("binary", bitwise_check); +preaction("binary", function (thing) { + if (relationop[thing.id] === true) { + const left = thing.expression[0]; + const right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + warn("expected_string_a", right); + } + } else { + const value = right.value; + if (value === "null" || value === "undefined") { + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + warn("expected_type_string_a", right, value); + } + } + } + } +}); +preaction("binary", "==", function (thing) { + warn("expected_a_b", thing, "===", "=="); +}); +preaction("binary", "!=", function (thing) { + warn("expected_a_b", thing, "!==", "!="); +}); +preaction("binary", "=>", preaction_function); +preaction("binary", "||", function (thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + warn("and", thang); + } + }); +}); +preaction("binary", "(", function (thing) { + const left = thing.expression[0]; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + const parent = functionage.name.parent; + if (parent) { + const left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + && left_variable.dead + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } +}); +preaction("binary", "in", function (thing) { + warn("infix_in", thing); +}); +preaction("binary", "instanceof", function (thing) { + warn("unexpected_a", thing); +}); +preaction("binary", ".", function (thing) { + if (thing.expression.new) { + thing.new = true; + } +}); +preaction("statement", "{", function (thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; +}); +preaction("statement", "for", function (thing) { + if (thing.name !== undefined) { + const the_variable = lookup(thing.name); + if (the_variable !== undefined) { + the_variable.init = true; + if (!the_variable.writable) { + warn("bad_assignment_a", thing.name); + } + } + } + walk_statement(thing.initial); +}); +preaction("statement", "function", preaction_function); +preaction("unary", "~", bitwise_check); +preaction("unary", "function", preaction_function); +preaction("variable", function (thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } +}); + +function init_variable(name) { + const the_variable = lookup(name); + if (the_variable !== undefined) { + if (the_variable.writable) { + the_variable.init = true; + return; + } + } + warn("bad_assignment_a", name); +} + +postaction("assignment", "+=", function (thing) { + let right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } +}); +postaction("assignment", function (thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + if (thing.id === "=") { + if (thing.names !== undefined) { + if (Array.isArray(thing.names)) { + thing.names.forEach(init_variable); + } else { + init_variable(thing.names); + } + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.writable !== true) { + warn("bad_assignment_a", lvalue); + } + } + const right = syntax[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + warn("unexpected_a", thing.expression[1]); + } + } +}); + +function postaction_function(thing) { + delete functionage.finally; + delete functionage.loop; + delete functionage.switch; + delete functionage.try; + functionage = stack.pop(); + if (thing.wrapped) { + warn("unexpected_parens", thing); + } + return pop_block(); +} + +postaction("binary", function (thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || are_similar(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option.convert) { + if (thing.expression[0].value === "") { + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === ".") { + if (thing.expression.id === "RegExp") { + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } +}); +postaction("binary", "&&", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "||", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "=>", postaction_function); +postaction("binary", "(", function (thing) { + let left = thing.expression[0]; + let the_new; + let arg; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option.eval) { + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + warn( + "expected_a_before_b", + left, + "new", + artifact(left) + ); + } + } + } else if (left.id === ".") { + let cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + cack = !cack; + } + if (rx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + warn("unexpected_a", the_new); + } else { + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + const paren = left.expression; + if (paren.id === "(") { + const array = paren.expression; + if (array.length === 1) { + const new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } +}); +postaction("binary", "[", function (thing) { + if (thing.expression[0].id === "RegExp") { + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + warn("weird_expression_a", thing.expression[1]); + } +}); +postaction("statement", "{", pop_block); +postaction("statement", "const", action_var); +postaction("statement", "export", top_level_only); +postaction("statement", "for", function (thing) { + walk_statement(thing.inc); +}); +postaction("statement", "function", postaction_function); +postaction("statement", "import", function (the_thing) { + const name = the_thing.name; + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return top_level_only(the_thing); +}); +postaction("statement", "let", action_var); +postaction("statement", "try", function (thing) { + if (thing.catch !== undefined) { + const the_name = thing.catch.name; + if (the_name !== undefined) { + const the_variable = functionage.context[the_name.id]; + the_variable.dead = false; + the_variable.init = true; + } + walk_statement(thing.catch.block); + } +}); +postaction("statement", "var", action_var); +postaction("ternary", function (thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || are_similar(thing.expression[1], thing.expression[2]) + ) { + warn("unexpected_a", thing); + } else if (are_similar(thing.expression[0], thing.expression[1])) { + warn("expected_a_b", thing, "||", "?"); + } else if (are_similar(thing.expression[0], thing.expression[2])) { + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + warn("wrap_condition", thing.expression[0]); + } +}); +postaction("unary", function (thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option.convert) { + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } +}); +postaction("unary", "function", postaction_function); +postaction("unary", "+", function (thing) { + if (!option.convert) { + warn("expected_a_b", thing, "Number(...)", "+"); + } + const right = thing.expression; + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } +}); + +function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + if (id !== "ignore") { + const name = the_function.context[id]; + if (name.parent === the_function) { + if ( + name.used === 0 + && ( + name.role !== "function" + || name.parent.arity !== "unary" + ) + ) { + warn("unused_a", name); + } else if (!name.init) { + warn("uninitialized_a", name); + } + } + } + }); +} + +function uninitialized_and_unused() { + +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (module_mode === true || option.node) { + delve(global); + } + functions.forEach(delve); +} + +// Go through the token list, looking at usage of whitespace. + +function whitage() { + let closer = "(end)"; + let free = false; + let left = global; + let margin = 0; + let nr_comments_skipped = 0; + let open = true; + let right; + + function expected_at(at) { + warn( + "expected_a_at_b_c", + right, + artifact(right), + fudge + at, + artifact_column(right) + ); + } + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function no_space() { + if (left.line === right.line) { + if (left.thru !== right.from && nr_comments_skipped === 0) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (open) { + const at = ( + free + ? margin + : margin + 8 + ); + if ( + right.from < at + // if autofix, then indent exactly + || (option.autofix && right.from !== at) + ) { + expected_at(at); + } + } else { + if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (free) { + if ( + right.from < margin + // if autofix, then indent exactly + || (option.autofix && right.from !== margin) + ) { + expected_at(margin); + } + } else { + const mislaid = ( + stack.length > 0 + ? stack[stack.length - 1].right + : undefined + ); + if (!open && mislaid !== undefined) { + warn( + "expected_a_next_at_b", + mislaid, + artifact(mislaid.id), + margin + 4 + fudge + ); + } else if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + stack = []; + tokens.forEach(function (the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + + const new_closer = opener[left.id]; + if (typeof new_closer === "string") { + if (new_closer !== right.id) { + stack.push({ + closer: closer, + free: free, + margin: margin, + open: open, + right: right + }); + closer = new_closer; + if (left.line !== right.line) { + free = closer === ")" && left.free; + open = true; + margin += 4; + if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (right.switch) { + at_margin(-4); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + free = false; + open = false; + no_space_only(); + } + } else { + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like {]) have already been detected. + + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } + } else { + +// If right is a closer, then pop the previous state. + + if (right.id === closer) { + const previous = stack.pop(); + margin = previous.margin; + if (open && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + closer = previous.closer; + free = previous.free; + open = previous.open; + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-4); + } else if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + one_space(); + } else { + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + at_margin(0); + } else { + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + no_space(); + } else if ( + left.id === "." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + ) { + no_space_only(); + } else if (right.id === ".") { + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } else if (left.id === ";") { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + one_space_only(); + } else if (right.statement === true) { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.id === "var" + || left.id === "const" + || left.id === "let" + ) { + stack.push({ + closer: closer, + free: free, + margin: margin, + open: open + }); + closer = ";"; + free = false; + open = left.open; + if (open) { + margin = margin + 4; + at_margin(0); + } else { + one_space_only(); + } + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +// The jslint function itself. + +export default function jslint( + source = "", + option_object = empty(), + global_array = [] +) { + let ii; + let jj; + let source_autofixed; + try { + warnings = []; + option = Object.assign(empty(), option_object); + anon = "anonymous"; + block_stack = []; + declared_globals = empty(); + directive_mode = true; + directives = []; + early_stop = true; + exports = empty(); + froms = []; + fudge = ( + option.fudge + ? 1 + : 0 + ); + functions = []; + global = { + id: "(global)", + body: true, + context: empty(), + from: 0, + level: 0, + line: 0, + live: [], + loop: 0, + switch: 0, + thru: 0 + }; + blockage = global; + functionage = global; + json_mode = false; + mega_mode = false; + module_mode = false; + next_token = global; + property = empty(); + stack = []; + tenure = undefined; + token = global; + token_nr = 0; + var_mode = undefined; + populate(standard, declared_globals, false); + populate(global_array, declared_globals, false); + Object.keys(option).forEach(function (name) { + if (option[name] === true) { + const allowed = allowed_option[name]; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } + }); + // run autofix + if (option.autofix) { + ii = 0; + source_autofixed = source; + while (true) { + ii += 1; + source = source_autofixed; + tokenize(source_autofixed); + whitage(); + source_autofixed = ""; + jj = 0; + while (jj < lines_extra.length) { + source_autofixed += ( + lines_extra[jj].source_autofixed + || lines[jj] + ) + "\n"; + jj += 1; + } + // remove trailine-whitespace + source_autofixed = ( + source_autofixed.slice(0, -1).replace((/\u0020+$/gm), "") + ); + // cleanup + uninitialized_and_unused(); + warnings.length = 0; + // repeat until source stops changing (all autofixes exhausted) + if (ii >= 10 || source_autofixed === source) { + break; + } + } + } + tokenize(source); + advance(); + if (json_mode) { + tree = json_value(); + advance("(end)"); + } else { + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option.browser) { + if (next_token.id === ";") { + advance(";"); + } + } else { + +// If we are not in a browser, then the file form of strict pragma may be used. + + if ( + next_token.value === "use strict" + ) { + global.strict = next_token; + advance("(string)"); + advance(";"); + } + } + tree = statements(); + advance("(end)"); + functionage = global; + walk_statement(tree); + if (module_mode && global.strict !== undefined) { + warn("unexpected_a", global.strict); + } + if (warnings.length === 0) { + uninitialized_and_unused(); + if (!option.white) { + whitage(); + } + } + } + if (!option.browser) { + directives.forEach(function (comment) { + if (comment.directive === "global") { + warn("missing_browser", comment); + } + }); + } + early_stop = false; + } catch (e) { + if (e.name !== "JSLintError") { + warnings.push(e); + } + } + return { + directives, + edition: "2018-09-13", + exports, + froms, + functions, + global, + id: "(JSLint)", + json: json_mode, + lines, + module: module_mode === true, + ok: warnings.length === 0 && !early_stop, + option, + property, + source_autofixed, + stop: early_stop, + tokens, + tree, + warnings: warnings.sort(function (a, b) { + return a.line - b.line || a.column - b.column; + }) + }; +}; diff --git a/branch.autofix/report.js b/branch.autofix/report.js new file mode 100644 index 000000000..f123e9055 --- /dev/null +++ b/branch.autofix/report.js @@ -0,0 +1,221 @@ +// report.js +// 2018-07-29 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Generate JSLint HTML reports. + +/*property + closure, column, context, edition, error, exports, filter, forEach, froms, + fudge, function, functions, global, id, isArray, join, json, keys, length, + level, line, lines, message, module, name, names, option, parameters, + parent, property, push, replace, role, signature, sort, stop, warnings +*/ + +const rx_amp = /&/g; +const rx_gt = />/g; +const rx_lt = / with less destructive entities. + + return String( + string + ).replace( + rx_amp, + "&" + ).replace( + rx_lt, + "<" + ).replace( + rx_gt, + ">" + ); +} + +export default { + error: function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + let fudge = Number(Boolean(data.option.fudge)); + let output = []; + if (data.stop) { + output.push("
    JSLint was unable to finish.
    "); + } + data.warnings.forEach(function (warning) { + output.push( + "
    ", + entityify(warning.line + fudge), + ".", + entityify(warning.column + fudge), + "
    ", + entityify(warning.message), + "
    ", + entityify(data.lines[warning.line] || ""), + "" + ); + }); + return output.join(""); + }, + + function: function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + let fudge = Number(Boolean(data.option.fudge)); + let mode = ( + data.module + ? "module" + : "global" + ); + let output = []; + + if (data.json) { + return ( + data.warnings.length === 0 + ? "
    JSON: good.
    " + : "
    JSON: bad.
    " + ); + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + let global = Object.keys(data.global.context).sort(); + let froms = data.froms.sort(); + let exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + let context = the_function.context; + let list = Object.keys(context); + output.push( + "
    ", + entityify(the_function.line + fudge), + "
    ", + ( + the_function.name === "=>" + ? entityify(the_function.signature) + " =>" + : ( + typeof the_function.name === "string" + ? "«" + entityify(the_function.name) + "»" + : "" + entityify(the_function.name.id) + "" + ) + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + let params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.parent === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.parent === the_function + ); + })); + detail("outer", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.parent !== the_function + && the_variable.parent.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].parent.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + output.push( + "
    JSLint edition ", + entityify(data.edition), + "
    " + ); + return output.join(""); + }, + + property: function property_directive(data) { + +// Produce the /*property*/ directive. + + let not_first = false; + let output = ["/*property"]; + let length = 1111; + let properties = Object.keys(data.property); + + if (properties.length > 0) { + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length >= 80) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); + } + } +}; diff --git a/branch.beta/.build/coverage/coverage-badge.svg b/branch.beta/.build/coverage/coverage-badge.svg new file mode 100644 index 000000000..dfd7294f1 --- /dev/null +++ b/branch.beta/.build/coverage/coverage-badge.svg @@ -0,0 +1,14 @@ + + + + +coverage +99.01 % + + diff --git a/branch.beta/.build/coverage/coverage-report.txt b/branch.beta/.build/coverage/coverage-report.txt new file mode 100644 index 000000000..d4bd8acdd --- /dev/null +++ b/branch.beta/.build/coverage/coverage-report.txt @@ -0,0 +1,13 @@ +coverage-report ++----------------------------------+-------------+ +| files covered | lines | ++----------------------------------+-------------+ +| ./ | 99.01 % | +| ******************************** | 6223 / 6285 | ++----------------------------------+-------------+ +| ./jslint.js | 98.94 % | +| ******************************** | 5821 / 5883 | ++----------------------------------+-------------+ +| ./test.js | 100.00 % | +| ******************************** | 402 / 402 | ++----------------------------------+-------------+ diff --git a/branch.beta/.build/coverage/coverage-v8.json b/branch.beta/.build/coverage/coverage-v8.json new file mode 100644 index 000000000..d0a4c5d56 --- /dev/null +++ b/branch.beta/.build/coverage/coverage-v8.json @@ -0,0 +1 @@ +{"result":[{"scriptId":"6","url":"internal/per_context/primordials.js","functions":[{"functionName":"uncurryThis","ranges":[{"startOffset":1000,"endOffset":1096,"count":5}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":1038,"endOffset":1093,"count":806}],"isBlockCoverage":true}]},{"scriptId":"9","url":"internal/bootstrap/loaders.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":10311,"count":1}],"isBlockCoverage":true},{"functionName":"binding","ranges":[{"startOffset":3652,"endOffset":4049,"count":0}],"isBlockCoverage":false},{"functionName":"_linkedBinding","ranges":[{"startOffset":4079,"endOffset":4287,"count":0}],"isBlockCoverage":false},{"functionName":"internalBinding","ranges":[{"startOffset":4467,"endOffset":4729,"count":96},{"startOffset":4569,"endOffset":4709,"count":30}],"isBlockCoverage":true},{"functionName":"getOwn","ranges":[{"startOffset":4874,"endOffset":5028,"count":198},{"startOffset":5010,"endOffset":5025,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":5395,"endOffset":5493,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":5454,"endOffset":5488,"count":232}],"isBlockCoverage":true},{"functionName":"NativeModule","ranges":[{"startOffset":5498,"endOffset":6250,"count":232}],"isBlockCoverage":true},{"functionName":"exposeInternals","ranges":[{"startOffset":6400,"endOffset":6626,"count":0}],"isBlockCoverage":false},{"functionName":"exists","ranges":[{"startOffset":6637,"endOffset":6690,"count":0}],"isBlockCoverage":false},{"functionName":"canBeRequiredByUsers","ranges":[{"startOffset":6701,"endOffset":6817,"count":7},{"startOffset":6785,"endOffset":6812,"count":5}],"isBlockCoverage":true},{"functionName":"compileForPublicLoader","ranges":[{"startOffset":6889,"endOffset":7583,"count":1},{"startOffset":6952,"endOffset":7144,"count":0},{"startOffset":7467,"endOffset":7471,"count":0}],"isBlockCoverage":true},{"functionName":"getESMFacade","ranges":[{"startOffset":7587,"endOffset":8138,"count":2},{"startOffset":7625,"endOffset":8137,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":7865,"endOffset":7978,"count":1}],"isBlockCoverage":true},{"functionName":"syncExports","ranges":[{"startOffset":8434,"endOffset":8778,"count":2},{"startOffset":8553,"endOffset":8768,"count":198},{"startOffset":8630,"endOffset":8639,"count":0}],"isBlockCoverage":true},{"functionName":"compileForInternalLoader","ranges":[{"startOffset":8782,"endOffset":9367,"count":346},{"startOffset":8831,"endOffset":8846,"count":81},{"startOffset":8848,"endOffset":8882,"count":269},{"startOffset":8882,"endOffset":9021,"count":77},{"startOffset":9021,"endOffset":9056,"count":0},{"startOffset":9057,"endOffset":9078,"count":77},{"startOffset":9232,"endOffset":9366,"count":77}],"isBlockCoverage":true},{"functionName":"nativeModuleRequire","ranges":[{"startOffset":9565,"endOffset":9936,"count":350},{"startOffset":9623,"endOffset":9654,"count":5},{"startOffset":9654,"endOffset":9838,"count":345},{"startOffset":9838,"endOffset":9893,"count":0},{"startOffset":9893,"endOffset":9935,"count":345}],"isBlockCoverage":true},{"functionName":"requireWithFallbackInDeps","ranges":[{"startOffset":10052,"endOffset":10224,"count":0}],"isBlockCoverage":false}]},{"scriptId":"10","url":"internal/bootstrap/node.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":12616,"count":1}],"isBlockCoverage":true},{"functionName":"process.openStdin","ranges":[{"startOffset":3399,"endOffset":3469,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":6160,"endOffset":6322,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":6424,"endOffset":6596,"count":0}],"isBlockCoverage":false},{"functionName":"setupPrepareStackTrace","ranges":[{"startOffset":9383,"endOffset":9969,"count":1}],"isBlockCoverage":true},{"functionName":"setupProcessObject","ranges":[{"startOffset":9971,"endOffset":10576,"count":1}],"isBlockCoverage":true},{"functionName":"setupGlobalProxy","ranges":[{"startOffset":10578,"endOffset":10755,"count":1}],"isBlockCoverage":true},{"functionName":"setupBuffer","ranges":[{"startOffset":10757,"endOffset":11193,"count":1}],"isBlockCoverage":true},{"functionName":"createGlobalConsole","ranges":[{"startOffset":11195,"endOffset":11876,"count":1}],"isBlockCoverage":true},{"functionName":"exposeNamespace","ranges":[{"startOffset":11928,"endOffset":12126,"count":1}],"isBlockCoverage":true},{"functionName":"exposeInterface","ranges":[{"startOffset":12178,"endOffset":12376,"count":4}],"isBlockCoverage":true},{"functionName":"defineOperation","ranges":[{"startOffset":12436,"endOffset":12615,"count":7}],"isBlockCoverage":true}]},{"scriptId":"11","url":"internal/errors.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":53549,"count":1}],"isBlockCoverage":false},{"functionName":"prepareStackTrace","ranges":[{"startOffset":1404,"endOffset":2120,"count":11},{"startOffset":1581,"endOffset":1697,"count":0},{"startOffset":1824,"endOffset":1846,"count":0},{"startOffset":2027,"endOffset":2056,"count":0}],"isBlockCoverage":true},{"functionName":"maybeOverridePrepareStackTrace","ranges":[{"startOffset":2162,"endOffset":2869,"count":11},{"startOffset":2431,"endOffset":2497,"count":0},{"startOffset":2778,"endOffset":2844,"count":0}],"isBlockCoverage":true},{"functionName":"lazyInternalUtil","ranges":[{"startOffset":2959,"endOffset":3085,"count":0}],"isBlockCoverage":false},{"functionName":"lazyInternalUtilInspect","ranges":[{"startOffset":3119,"endOffset":3281,"count":0}],"isBlockCoverage":false},{"functionName":"lazyBuffer","ranges":[{"startOffset":3295,"endOffset":3404,"count":0}],"isBlockCoverage":false},{"functionName":"SystemError","ranges":[{"startOffset":3906,"endOffset":6444,"count":0}],"isBlockCoverage":false},{"functionName":"toString","ranges":[{"startOffset":6448,"endOffset":6523,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":6527,"endOffset":6716,"count":0}],"isBlockCoverage":false},{"functionName":"makeSystemErrorWithCode","ranges":[{"startOffset":6720,"endOffset":6865,"count":4}],"isBlockCoverage":true},{"functionName":"NodeError","ranges":[{"startOffset":6811,"endOffset":6858,"count":0}],"isBlockCoverage":false},{"functionName":"makeNodeErrorWithCode","ranges":[{"startOffset":6867,"endOffset":7622,"count":233}],"isBlockCoverage":true},{"functionName":"NodeError","ranges":[{"startOffset":6955,"endOffset":7536,"count":6},{"startOffset":7045,"endOffset":7254,"count":0}],"isBlockCoverage":true},{"functionName":"toString","ranges":[{"startOffset":7542,"endOffset":7615,"count":0}],"isBlockCoverage":false},{"functionName":"hideStackFrames","ranges":[{"startOffset":7694,"endOffset":8105,"count":26}],"isBlockCoverage":true},{"functionName":"hidden","ranges":[{"startOffset":7734,"endOffset":8102,"count":304},{"startOffset":7898,"endOffset":7962,"count":260},{"startOffset":8046,"endOffset":8092,"count":260}],"isBlockCoverage":true},{"functionName":"addCodeToName","ranges":[{"startOffset":8107,"endOffset":8723,"count":6},{"startOffset":8205,"endOffset":8260,"count":0},{"startOffset":8545,"endOffset":8689,"count":0}],"isBlockCoverage":true},{"functionName":"E","ranges":[{"startOffset":8835,"endOffset":9343,"count":234},{"startOffset":9077,"endOffset":9122,"count":4},{"startOffset":9122,"endOffset":9176,"count":230},{"startOffset":9211,"endOffset":9321,"count":3}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":9238,"endOffset":9315,"count":3}],"isBlockCoverage":true},{"functionName":"getMessage","ranges":[{"startOffset":9345,"endOffset":10149,"count":6},{"startOffset":9446,"endOffset":9482,"count":1},{"startOffset":9773,"endOffset":9831,"count":0},{"startOffset":10053,"endOffset":10148,"count":0}],"isBlockCoverage":true},{"functionName":"lazyUv","ranges":[{"startOffset":10167,"endOffset":10271,"count":0}],"isBlockCoverage":false},{"functionName":"uvErrmapGet","ranges":[{"startOffset":10328,"endOffset":10498,"count":0}],"isBlockCoverage":false},{"functionName":"uvException","ranges":[{"startOffset":10791,"endOffset":11987,"count":0}],"isBlockCoverage":false},{"functionName":"uvExceptionWithHostPort","ranges":[{"startOffset":12300,"endOffset":13205,"count":0}],"isBlockCoverage":false},{"functionName":"errnoException","ranges":[{"startOffset":13384,"endOffset":14090,"count":0}],"isBlockCoverage":false},{"functionName":"exceptionWithHostPort","ranges":[{"startOffset":14443,"endOffset":15659,"count":0}],"isBlockCoverage":false},{"functionName":"dnsException","ranges":[{"startOffset":15823,"endOffset":17338,"count":0}],"isBlockCoverage":false},{"functionName":"connResetException","ranges":[{"startOffset":17340,"endOffset":17495,"count":0}],"isBlockCoverage":false},{"functionName":"isStackOverflowError","ranges":[{"startOffset":17785,"endOffset":18163,"count":0}],"isBlockCoverage":false},{"functionName":"addNumericalSeparator","ranges":[{"startOffset":18244,"endOffset":18480,"count":0}],"isBlockCoverage":false},{"functionName":"beforeInspector","ranges":[{"startOffset":18759,"endOffset":19150,"count":0}],"isBlockCoverage":false},{"functionName":"afterInspector","ranges":[{"startOffset":19154,"endOffset":20492,"count":0}],"isBlockCoverage":false},{"functionName":"AbortError","ranges":[{"startOffset":20728,"endOffset":20846,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":22625,"endOffset":22789,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":25628,"endOffset":25743,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":26032,"endOffset":26126,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":28407,"endOffset":28635,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":30368,"endOffset":30586,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":32336,"endOffset":32636,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":32678,"endOffset":32822,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":32865,"endOffset":36070,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":36111,"endOffset":36367,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":36759,"endOffset":36922,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":37584,"endOffset":37719,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":37760,"endOffset":38084,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":38243,"endOffset":38391,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":38435,"endOffset":39209,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":39587,"endOffset":39751,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":39804,"endOffset":40135,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":40179,"endOffset":40486,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":40856,"endOffset":40931,"count":6}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":40975,"endOffset":41263,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":41591,"endOffset":42022,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":42698,"endOffset":43221,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":43315,"endOffset":43416,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":44176,"endOffset":44874,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":44925,"endOffset":45117,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":45166,"endOffset":45482,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":45515,"endOffset":46392,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":46844,"endOffset":47103,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":48453,"endOffset":48622,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":50520,"endOffset":50651,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":51244,"endOffset":51527,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":52507,"endOffset":52605,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":52799,"endOffset":53154,"count":0}],"isBlockCoverage":false}]},{"scriptId":"12","url":"internal/util.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":12498,"count":1}],"isBlockCoverage":false},{"functionName":"lazyUv","ranges":[{"startOffset":991,"endOffset":1082,"count":0}],"isBlockCoverage":false},{"functionName":"removeColors","ranges":[{"startOffset":1084,"endOffset":1153,"count":0}],"isBlockCoverage":false},{"functionName":"isError","ranges":[{"startOffset":1155,"endOffset":1405,"count":0}],"isBlockCoverage":false},{"functionName":"deprecate","ranges":[{"startOffset":1690,"endOffset":2787,"count":11},{"startOffset":1764,"endOffset":1784,"count":0},{"startOffset":1844,"endOffset":1899,"count":0},{"startOffset":2541,"endOffset":2763,"count":10}],"isBlockCoverage":true},{"functionName":"deprecated","ranges":[{"startOffset":1925,"endOffset":2399,"count":0}],"isBlockCoverage":false},{"functionName":"decorateErrorStack","ranges":[{"startOffset":2789,"endOffset":3128,"count":0}],"isBlockCoverage":false},{"functionName":"assertCrypto","ranges":[{"startOffset":3130,"endOffset":3204,"count":0}],"isBlockCoverage":false},{"functionName":"normalizeEncoding","ranges":[{"startOffset":3383,"endOffset":3514,"count":15},{"startOffset":3453,"endOffset":3471,"count":0},{"startOffset":3487,"endOffset":3513,"count":0}],"isBlockCoverage":true},{"functionName":"slowCases","ranges":[{"startOffset":3516,"endOffset":5096,"count":0}],"isBlockCoverage":false},{"functionName":"emitExperimentalWarning","ranges":[{"startOffset":5098,"endOffset":5386,"count":0}],"isBlockCoverage":false},{"functionName":"filterDuplicateStrings","ranges":[{"startOffset":5388,"endOffset":5696,"count":0}],"isBlockCoverage":false},{"functionName":"cachedResult","ranges":[{"startOffset":5698,"endOffset":5841,"count":0}],"isBlockCoverage":false},{"functionName":"createClassWrapper","ranges":[{"startOffset":6106,"endOffset":6471,"count":0}],"isBlockCoverage":false},{"functionName":"getSignalsToNamesMapping","ranges":[{"startOffset":6500,"endOffset":6778,"count":0}],"isBlockCoverage":false},{"functionName":"convertToValidSignal","ranges":[{"startOffset":6780,"endOffset":7087,"count":0}],"isBlockCoverage":false},{"functionName":"getConstructorOf","ranges":[{"startOffset":7089,"endOffset":7435,"count":0}],"isBlockCoverage":false},{"functionName":"getSystemErrorName","ranges":[{"startOffset":7437,"endOffset":7566,"count":0}],"isBlockCoverage":false},{"functionName":"getSystemErrorMap","ranges":[{"startOffset":7568,"endOffset":7633,"count":0}],"isBlockCoverage":false},{"functionName":"promisify","ranges":[{"startOffset":7778,"endOffset":9249,"count":3},{"startOffset":7851,"endOffset":7916,"count":0},{"startOffset":7960,"endOffset":8281,"count":0}],"isBlockCoverage":true},{"functionName":"fn","ranges":[{"startOffset":8481,"endOffset":8962,"count":0}],"isBlockCoverage":false},{"functionName":"join","ranges":[{"startOffset":9344,"endOffset":9666,"count":0}],"isBlockCoverage":false},{"functionName":"spliceOne","ranges":[{"startOffset":9807,"endOffset":9934,"count":0}],"isBlockCoverage":false},{"functionName":"isInsideNodeModules","ranges":[{"startOffset":10016,"endOffset":11188,"count":0}],"isBlockCoverage":false},{"functionName":"once","ranges":[{"startOffset":11190,"endOffset":11348,"count":0}],"isBlockCoverage":false},{"functionName":"sleep","ranges":[{"startOffset":11371,"endOffset":11586,"count":0}],"isBlockCoverage":false}]},{"scriptId":"13","url":"events.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":26873,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":2207,"endOffset":2367,"count":0}],"isBlockCoverage":false},{"functionName":"EventEmitter","ranges":[{"startOffset":2372,"endOffset":2441,"count":3}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":2805,"endOffset":2861,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":2865,"endOffset":3099,"count":0}],"isBlockCoverage":false},{"functionName":"checkListener","ranges":[{"startOffset":3671,"endOffset":3821,"count":497},{"startOffset":3744,"endOffset":3819,"count":0}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":3910,"endOffset":3958,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":3967,"endOffset":4242,"count":0}],"isBlockCoverage":false},{"functionName":"EventEmitter.setMaxListeners","ranges":[{"startOffset":4618,"endOffset":5532,"count":0}],"isBlockCoverage":false},{"functionName":"EventEmitter.init","ranges":[{"startOffset":5555,"endOffset":6285,"count":3},{"startOffset":5606,"endOffset":5666,"count":1},{"startOffset":5668,"endOffset":5739,"count":2},{"startOffset":5810,"endOffset":5835,"count":2},{"startOffset":5837,"endOffset":6096,"count":0}],"isBlockCoverage":true},{"functionName":"addCatch","ranges":[{"startOffset":6288,"endOffset":6847,"count":0}],"isBlockCoverage":false},{"functionName":"emitUnhandledRejectionOrErr","ranges":[{"startOffset":6849,"endOffset":7507,"count":0}],"isBlockCoverage":false},{"functionName":"setMaxListeners","ranges":[{"startOffset":7678,"endOffset":7877,"count":0}],"isBlockCoverage":false},{"functionName":"_getMaxListeners","ranges":[{"startOffset":7880,"endOffset":8029,"count":0}],"isBlockCoverage":false},{"functionName":"getMaxListeners","ranges":[{"startOffset":8072,"endOffset":8135,"count":0}],"isBlockCoverage":false},{"functionName":"identicalSequenceRange","ranges":[{"startOffset":8263,"endOffset":8839,"count":0}],"isBlockCoverage":false},{"functionName":"enhanceStackTrace","ranges":[{"startOffset":8841,"endOffset":9447,"count":0}],"isBlockCoverage":false},{"functionName":"emit","ranges":[{"startOffset":9479,"endOffset":11762,"count":6},{"startOffset":9624,"endOffset":9662,"count":0},{"startOffset":9670,"endOffset":9704,"count":0},{"startOffset":9728,"endOffset":9757,"count":0},{"startOffset":9763,"endOffset":9800,"count":0},{"startOffset":9872,"endOffset":10804,"count":0},{"startOffset":10872,"endOffset":10885,"count":2},{"startOffset":10885,"endOffset":11213,"count":4},{"startOffset":11140,"endOffset":11158,"count":0},{"startOffset":11160,"endOffset":11209,"count":0},{"startOffset":11213,"endOffset":11744,"count":0},{"startOffset":11744,"endOffset":11761,"count":4}],"isBlockCoverage":true},{"functionName":"_addListener","ranges":[{"startOffset":11765,"endOffset":13820,"count":169},{"startOffset":11945,"endOffset":12029,"count":0},{"startOffset":12214,"endOffset":12494,"count":3},{"startOffset":12291,"endOffset":12310,"count":0},{"startOffset":12697,"endOffset":13800,"count":0}],"isBlockCoverage":true},{"functionName":"addListener","ranges":[{"startOffset":13859,"endOffset":13951,"count":169}],"isBlockCoverage":true},{"functionName":"prependListener","ranges":[{"startOffset":14064,"endOffset":14167,"count":0}],"isBlockCoverage":false},{"functionName":"onceWrapper","ranges":[{"startOffset":14170,"endOffset":14434,"count":0}],"isBlockCoverage":false},{"functionName":"_onceWrap","ranges":[{"startOffset":14436,"endOffset":14677,"count":164}],"isBlockCoverage":true},{"functionName":"once","ranges":[{"startOffset":14709,"endOffset":14835,"count":164}],"isBlockCoverage":true},{"functionName":"prependOnceListener","ranges":[{"startOffset":14887,"endOffset":15057,"count":0}],"isBlockCoverage":false},{"functionName":"removeListener","ranges":[{"startOffset":15179,"endOffset":16473,"count":164},{"startOffset":15328,"endOffset":15340,"count":0},{"startOffset":15413,"endOffset":15425,"count":0},{"startOffset":15455,"endOffset":15484,"count":163},{"startOffset":15537,"endOffset":15571,"count":0},{"startOffset":15667,"endOffset":15728,"count":1},{"startOffset":15746,"endOffset":16447,"count":0}],"isBlockCoverage":true},{"functionName":"removeAllListeners","ranges":[{"startOffset":16593,"endOffset":17919,"count":0}],"isBlockCoverage":false},{"functionName":"_listeners","ranges":[{"startOffset":17922,"endOffset":18317,"count":0}],"isBlockCoverage":false},{"functionName":"listeners","ranges":[{"startOffset":18354,"endOffset":18421,"count":0}],"isBlockCoverage":false},{"functionName":"rawListeners","ranges":[{"startOffset":18462,"endOffset":18533,"count":0}],"isBlockCoverage":false},{"functionName":"EventEmitter.listenerCount","ranges":[{"startOffset":18565,"endOffset":18733,"count":0}],"isBlockCoverage":false},{"functionName":"listenerCount","ranges":[{"startOffset":18790,"endOffset":19080,"count":326},{"startOffset":18963,"endOffset":18986,"count":162},{"startOffset":18986,"endOffset":19061,"count":164},{"startOffset":19022,"endOffset":19061,"count":0},{"startOffset":19065,"endOffset":19079,"count":164}],"isBlockCoverage":true},{"functionName":"eventNames","ranges":[{"startOffset":19118,"endOffset":19211,"count":0}],"isBlockCoverage":false},{"functionName":"arrayClone","ranges":[{"startOffset":19214,"endOffset":19674,"count":0}],"isBlockCoverage":false},{"functionName":"unwrapListeners","ranges":[{"startOffset":19676,"endOffset":19890,"count":0}],"isBlockCoverage":false},{"functionName":"getEventListeners","ranges":[{"startOffset":19892,"endOffset":20687,"count":0}],"isBlockCoverage":false},{"functionName":"once","ranges":[{"startOffset":20689,"endOffset":22475,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":22553,"endOffset":22574,"count":0}],"isBlockCoverage":false},{"functionName":"createIterResult","ranges":[{"startOffset":22589,"endOffset":22657,"count":0}],"isBlockCoverage":false},{"functionName":"addErrorHandlerIfEventEmitter","ranges":[{"startOffset":22659,"endOffset":22842,"count":0}],"isBlockCoverage":false},{"functionName":"eventTargetAgnosticRemoveListener","ranges":[{"startOffset":22844,"endOffset":23229,"count":0}],"isBlockCoverage":false},{"functionName":"eventTargetAgnosticAddListener","ranges":[{"startOffset":23231,"endOffset":23820,"count":0}],"isBlockCoverage":false},{"functionName":"on","ranges":[{"startOffset":23822,"endOffset":26872,"count":0}],"isBlockCoverage":false}]},{"scriptId":"14","url":"internal/util/inspect.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":71637,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":2893,"endOffset":2929,"count":62}],"isBlockCoverage":true},{"functionName":"isUndetectableObject","ranges":[{"startOffset":3020,"endOffset":3070,"count":0}],"isBlockCoverage":false},{"functionName":"getUserOptions","ranges":[{"startOffset":6215,"endOffset":7666,"count":0}],"isBlockCoverage":false},{"functionName":"inspect","ranges":[{"startOffset":7961,"endOffset":9878,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":9970,"endOffset":10015,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":10019,"endOffset":10227,"count":0}],"isBlockCoverage":false},{"functionName":"defineColorAlias","ranges":[{"startOffset":11964,"endOffset":12206,"count":12}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":12059,"endOffset":12099,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":12105,"endOffset":12151,"count":0}],"isBlockCoverage":false},{"functionName":"addQuotes","ranges":[{"startOffset":13216,"endOffset":13374,"count":0}],"isBlockCoverage":false},{"functionName":"escapeFn","ranges":[{"startOffset":13393,"endOffset":13425,"count":0}],"isBlockCoverage":false},{"functionName":"strEscape","ranges":[{"startOffset":13538,"endOffset":15164,"count":0}],"isBlockCoverage":false},{"functionName":"stylizeWithColor","ranges":[{"startOffset":15166,"endOffset":15432,"count":0}],"isBlockCoverage":false},{"functionName":"stylizeNoColor","ranges":[{"startOffset":15434,"endOffset":15480,"count":0}],"isBlockCoverage":false},{"functionName":"getEmptyFormatArray","ranges":[{"startOffset":15559,"endOffset":15606,"count":0}],"isBlockCoverage":false},{"functionName":"isInstanceof","ranges":[{"startOffset":15608,"endOffset":15726,"count":0}],"isBlockCoverage":false},{"functionName":"getConstructorName","ranges":[{"startOffset":15728,"endOffset":16988,"count":0}],"isBlockCoverage":false},{"functionName":"addPrototypeProperties","ranges":[{"startOffset":17175,"endOffset":19018,"count":0}],"isBlockCoverage":false},{"functionName":"getPrefix","ranges":[{"startOffset":19020,"endOffset":19407,"count":0}],"isBlockCoverage":false},{"functionName":"getKeys","ranges":[{"startOffset":19444,"endOffset":20386,"count":0}],"isBlockCoverage":false},{"functionName":"getCtxStyle","ranges":[{"startOffset":20388,"endOffset":20651,"count":0}],"isBlockCoverage":false},{"functionName":"formatProxy","ranges":[{"startOffset":20653,"endOffset":21102,"count":0}],"isBlockCoverage":false},{"functionName":"findTypedConstructor","ranges":[{"startOffset":21104,"endOffset":21627,"count":0}],"isBlockCoverage":false},{"functionName":"formatValue","ranges":[{"startOffset":21809,"endOffset":24348,"count":0}],"isBlockCoverage":false},{"functionName":"formatRaw","ranges":[{"startOffset":24350,"endOffset":34825,"count":0}],"isBlockCoverage":false},{"functionName":"getIteratorBraces","ranges":[{"startOffset":34827,"endOffset":35009,"count":0}],"isBlockCoverage":false},{"functionName":"getBoxedBase","ranges":[{"startOffset":35011,"endOffset":36185,"count":0}],"isBlockCoverage":false},{"functionName":"getClassBase","ranges":[{"startOffset":36187,"endOffset":36787,"count":0}],"isBlockCoverage":false},{"functionName":"getFunctionBase","ranges":[{"startOffset":36789,"endOffset":37882,"count":0}],"isBlockCoverage":false},{"functionName":"formatError","ranges":[{"startOffset":37884,"endOffset":41005,"count":0}],"isBlockCoverage":false},{"functionName":"groupArrayElements","ranges":[{"startOffset":41007,"endOffset":45258,"count":0}],"isBlockCoverage":false},{"functionName":"handleMaxCallStackSize","ranges":[{"startOffset":45260,"endOffset":45648,"count":0}],"isBlockCoverage":false},{"functionName":"formatNumber","ranges":[{"startOffset":45650,"endOffset":45827,"count":0}],"isBlockCoverage":false},{"functionName":"formatBigInt","ranges":[{"startOffset":45829,"endOffset":45901,"count":0}],"isBlockCoverage":false},{"functionName":"formatPrimitive","ranges":[{"startOffset":45903,"endOffset":47086,"count":0}],"isBlockCoverage":false},{"functionName":"formatNamespaceObject","ranges":[{"startOffset":47088,"endOffset":48208,"count":0}],"isBlockCoverage":false},{"functionName":"formatSpecialArray","ranges":[{"startOffset":48255,"endOffset":49462,"count":0}],"isBlockCoverage":false},{"functionName":"formatArrayBuffer","ranges":[{"startOffset":49464,"endOffset":50064,"count":0}],"isBlockCoverage":false},{"functionName":"formatArray","ranges":[{"startOffset":50066,"endOffset":50660,"count":0}],"isBlockCoverage":false},{"functionName":"formatTypedArray","ranges":[{"startOffset":50662,"endOffset":51678,"count":0}],"isBlockCoverage":false},{"functionName":"formatSet","ranges":[{"startOffset":51680,"endOffset":51912,"count":0}],"isBlockCoverage":false},{"functionName":"formatMap","ranges":[{"startOffset":51914,"endOffset":52212,"count":0}],"isBlockCoverage":false},{"functionName":"formatSetIterInner","ranges":[{"startOffset":52214,"endOffset":53027,"count":0}],"isBlockCoverage":false},{"functionName":"formatMapIterInner","ranges":[{"startOffset":53029,"endOffset":54351,"count":0}],"isBlockCoverage":false},{"functionName":"formatWeakCollection","ranges":[{"startOffset":54353,"endOffset":54445,"count":0}],"isBlockCoverage":false},{"functionName":"formatWeakSet","ranges":[{"startOffset":54447,"endOffset":54604,"count":0}],"isBlockCoverage":false},{"functionName":"formatWeakMap","ranges":[{"startOffset":54606,"endOffset":54763,"count":0}],"isBlockCoverage":false},{"functionName":"formatIterator","ranges":[{"startOffset":54765,"endOffset":55156,"count":0}],"isBlockCoverage":false},{"functionName":"formatPromise","ranges":[{"startOffset":55158,"endOffset":55623,"count":0}],"isBlockCoverage":false},{"functionName":"formatProperty","ranges":[{"startOffset":55625,"endOffset":58023,"count":0}],"isBlockCoverage":false},{"functionName":"isBelowBreakLength","ranges":[{"startOffset":58025,"endOffset":58967,"count":0}],"isBlockCoverage":false},{"functionName":"reduceToSingleString","ranges":[{"startOffset":58969,"endOffset":61715,"count":0}],"isBlockCoverage":false},{"functionName":"hasBuiltInToString","ranges":[{"startOffset":61717,"endOffset":62736,"count":0}],"isBlockCoverage":false},{"functionName":"firstErrorLine","ranges":[{"startOffset":62761,"endOffset":62800,"count":0}],"isBlockCoverage":false},{"functionName":"tryStringify","ranges":[{"startOffset":62830,"endOffset":63299,"count":0}],"isBlockCoverage":false},{"functionName":"format","ranges":[{"startOffset":63301,"endOffset":63385,"count":0}],"isBlockCoverage":false},{"functionName":"formatWithOptions","ranges":[{"startOffset":63387,"endOffset":63665,"count":163},{"startOffset":63510,"endOffset":63602,"count":0}],"isBlockCoverage":true},{"functionName":"formatWithOptionsInternal","ranges":[{"startOffset":63667,"endOffset":67451,"count":163},{"startOffset":63890,"endOffset":67250,"count":0},{"startOffset":67254,"endOffset":67450,"count":0}],"isBlockCoverage":true},{"functionName":"getStringWidth","ranges":[{"startOffset":67880,"endOffset":68431,"count":0}],"isBlockCoverage":false},{"functionName":"getStringWidth","ranges":[{"startOffset":68546,"endOffset":68958,"count":0}],"isBlockCoverage":false},{"functionName":"isFullWidthCodePoint","ranges":[{"startOffset":69126,"endOffset":70735,"count":0}],"isBlockCoverage":false},{"functionName":"isZeroWidthCodePoint","ranges":[{"startOffset":70769,"endOffset":71337,"count":0}],"isBlockCoverage":false},{"functionName":"stripVTControlCharacters","ranges":[{"startOffset":71427,"endOffset":71501,"count":0}],"isBlockCoverage":false}]},{"scriptId":"15","url":"internal/util/types.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1915,"count":1}],"isBlockCoverage":false},{"functionName":"isTypedArray","ranges":[{"startOffset":425,"endOffset":516,"count":0}],"isBlockCoverage":false},{"functionName":"isUint8Array","ranges":[{"startOffset":518,"endOffset":612,"count":75}],"isBlockCoverage":true},{"functionName":"isUint8ClampedArray","ranges":[{"startOffset":614,"endOffset":722,"count":0}],"isBlockCoverage":false},{"functionName":"isUint16Array","ranges":[{"startOffset":724,"endOffset":820,"count":0}],"isBlockCoverage":false},{"functionName":"isUint32Array","ranges":[{"startOffset":822,"endOffset":918,"count":0}],"isBlockCoverage":false},{"functionName":"isInt8Array","ranges":[{"startOffset":920,"endOffset":1012,"count":0}],"isBlockCoverage":false},{"functionName":"isInt16Array","ranges":[{"startOffset":1014,"endOffset":1108,"count":0}],"isBlockCoverage":false},{"functionName":"isInt32Array","ranges":[{"startOffset":1110,"endOffset":1204,"count":0}],"isBlockCoverage":false},{"functionName":"isFloat32Array","ranges":[{"startOffset":1206,"endOffset":1304,"count":0}],"isBlockCoverage":false},{"functionName":"isFloat64Array","ranges":[{"startOffset":1306,"endOffset":1404,"count":0}],"isBlockCoverage":false},{"functionName":"isBigInt64Array","ranges":[{"startOffset":1406,"endOffset":1506,"count":0}],"isBlockCoverage":false},{"functionName":"isBigUint64Array","ranges":[{"startOffset":1508,"endOffset":1610,"count":2}],"isBlockCoverage":true}]},{"scriptId":"16","url":"internal/assert.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":474,"count":1}],"isBlockCoverage":false},{"functionName":"lazyError","ranges":[{"startOffset":26,"endOffset":155,"count":0}],"isBlockCoverage":false},{"functionName":"assert","ranges":[{"startOffset":157,"endOffset":307,"count":10},{"startOffset":205,"endOffset":305,"count":0}],"isBlockCoverage":true},{"functionName":"fail","ranges":[{"startOffset":309,"endOffset":426,"count":0}],"isBlockCoverage":false}]},{"scriptId":"17","url":"internal/validators.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":7218,"count":1}],"isBlockCoverage":false},{"functionName":"isInt32","ranges":[{"startOffset":581,"endOffset":640,"count":1}],"isBlockCoverage":true},{"functionName":"isUint32","ranges":[{"startOffset":642,"endOffset":704,"count":17}],"isBlockCoverage":true},{"functionName":"parseFileMode","ranges":[{"startOffset":1326,"endOffset":1807,"count":17},{"startOffset":1389,"endOffset":1409,"count":0},{"startOffset":1411,"endOffset":1432,"count":0},{"startOffset":1480,"endOffset":1806,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":1852,"endOffset":2233,"count":76},{"startOffset":1972,"endOffset":2026,"count":0},{"startOffset":2066,"endOffset":2120,"count":0},{"startOffset":2163,"endOffset":2229,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":2279,"endOffset":2860,"count":1},{"startOffset":2441,"endOffset":2739,"count":0},{"startOffset":2776,"endOffset":2856,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":2904,"endOffset":3414,"count":0}],"isBlockCoverage":false},{"functionName":"validateString","ranges":[{"startOffset":3418,"endOffset":3550,"count":48},{"startOffset":3494,"endOffset":3548,"count":0}],"isBlockCoverage":true},{"functionName":"validateNumber","ranges":[{"startOffset":3552,"endOffset":3684,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":3724,"endOffset":4174,"count":0}],"isBlockCoverage":false},{"functionName":"validateBoolean","ranges":[{"startOffset":4178,"endOffset":4313,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":4357,"endOffset":4582,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":4624,"endOffset":4911,"count":0}],"isBlockCoverage":false},{"functionName":"validateSignalName","ranges":[{"startOffset":4915,"endOffset":5336,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":5377,"endOffset":5607,"count":0}],"isBlockCoverage":false},{"functionName":"validateEncoding","ranges":[{"startOffset":5611,"endOffset":5945,"count":0}],"isBlockCoverage":false},{"functionName":"validatePort","ranges":[{"startOffset":6089,"endOffset":6463,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":6506,"endOffset":6607,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":6655,"endOffset":6872,"count":0}],"isBlockCoverage":false}]},{"scriptId":"18","url":"buffer.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":36751,"count":1}],"isBlockCoverage":false},{"functionName":"validateOffset","ranges":[{"startOffset":2784,"endOffset":2868,"count":0}],"isBlockCoverage":false},{"functionName":"createUnsafeBuffer","ranges":[{"startOffset":4082,"endOffset":4218,"count":8}],"isBlockCoverage":true},{"functionName":"createPool","ranges":[{"startOffset":4220,"endOffset":4379,"count":1}],"isBlockCoverage":true},{"functionName":"alignPool","ranges":[{"startOffset":4395,"endOffset":4517,"count":2}],"isBlockCoverage":true},{"functionName":"showFlaggedDeprecation","ranges":[{"startOffset":4821,"endOffset":5501,"count":0}],"isBlockCoverage":false},{"functionName":"toInteger","ranges":[{"startOffset":5503,"endOffset":5721,"count":0}],"isBlockCoverage":false},{"functionName":"_copy","ranges":[{"startOffset":5723,"endOffset":6988,"count":0}],"isBlockCoverage":false},{"functionName":"_copyActual","ranges":[{"startOffset":6990,"endOffset":7592,"count":51},{"startOffset":7131,"endOffset":7185,"count":0},{"startOffset":7347,"endOffset":7362,"count":0},{"startOffset":7389,"endOffset":7404,"count":0},{"startOffset":7464,"endOffset":7540,"count":0}],"isBlockCoverage":true},{"functionName":"Buffer","ranges":[{"startOffset":8168,"endOffset":8501,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":8594,"endOffset":8622,"count":0}],"isBlockCoverage":false},{"functionName":"from","ranges":[{"startOffset":8879,"endOffset":9843,"count":2},{"startOffset":9008,"endOffset":9059,"count":0},{"startOffset":9061,"endOffset":9842,"count":0}],"isBlockCoverage":true},{"functionName":"of","ranges":[{"startOffset":10214,"endOffset":10366,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":10655,"endOffset":10876,"count":83},{"startOffset":10699,"endOffset":10764,"count":0},{"startOffset":10807,"endOffset":10874,"count":0}],"isBlockCoverage":true},{"functionName":"alloc","ranges":[{"startOffset":10979,"endOffset":11224,"count":76},{"startOffset":11063,"endOffset":11076,"count":0},{"startOffset":11077,"endOffset":11088,"count":0},{"startOffset":11090,"endOffset":11191,"count":0}],"isBlockCoverage":true},{"functionName":"allocUnsafe","ranges":[{"startOffset":11403,"endOffset":11478,"count":7}],"isBlockCoverage":true},{"functionName":"allocUnsafeSlow","ranges":[{"startOffset":11719,"endOffset":11808,"count":0}],"isBlockCoverage":false},{"functionName":"SlowBuffer","ranges":[{"startOffset":11904,"endOffset":11994,"count":0}],"isBlockCoverage":false},{"functionName":"allocate","ranges":[{"startOffset":12108,"endOffset":12440,"count":7},{"startOffset":12151,"endOffset":12185,"count":0},{"startOffset":12224,"endOffset":12403,"count":0}],"isBlockCoverage":true},{"functionName":"fromStringFast","ranges":[{"startOffset":12442,"endOffset":12988,"count":2},{"startOffset":12568,"endOffset":12617,"count":0},{"startOffset":12663,"endOffset":12676,"count":0},{"startOffset":12809,"endOffset":12935,"count":0}],"isBlockCoverage":true},{"functionName":"fromString","ranges":[{"startOffset":12990,"endOffset":13443,"count":2},{"startOffset":13076,"endOffset":13100,"count":0},{"startOffset":13139,"endOffset":13163,"count":0},{"startOffset":13221,"endOffset":13403,"count":0}],"isBlockCoverage":true},{"functionName":"fromArrayBuffer","ranges":[{"startOffset":13445,"endOffset":14142,"count":0}],"isBlockCoverage":false},{"functionName":"fromArrayLike","ranges":[{"startOffset":14144,"endOffset":14518,"count":0}],"isBlockCoverage":false},{"functionName":"fromObject","ranges":[{"startOffset":14520,"endOffset":14826,"count":0}],"isBlockCoverage":false},{"functionName":"isBuffer","ranges":[{"startOffset":14865,"endOffset":14919,"count":0}],"isBlockCoverage":false},{"functionName":"compare","ranges":[{"startOffset":14939,"endOffset":15264,"count":0}],"isBlockCoverage":false},{"functionName":"isEncoding","ranges":[{"startOffset":15287,"endOffset":15438,"count":15}],"isBlockCoverage":true},{"functionName":"concat","ranges":[{"startOffset":15504,"endOffset":16708,"count":8},{"startOffset":15563,"endOffset":15627,"count":0},{"startOffset":15658,"endOffset":15682,"count":1},{"startOffset":15682,"endOffset":15853,"count":7},{"startOffset":15772,"endOffset":15849,"count":51},{"startOffset":15853,"endOffset":15902,"count":0},{"startOffset":15902,"endOffset":16004,"count":7},{"startOffset":16004,"endOffset":16352,"count":51},{"startOffset":16059,"endOffset":16291,"count":0},{"startOffset":16352,"endOffset":16443,"count":7},{"startOffset":16443,"endOffset":16688,"count":0},{"startOffset":16688,"endOffset":16707,"count":7}],"isBlockCoverage":true},{"functionName":"base64ByteLength","ranges":[{"startOffset":16711,"endOffset":16947,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":17082,"endOffset":17146,"count":2}],"isBlockCoverage":true},{"functionName":"slice","ranges":[{"startOffset":17159,"endOffset":17205,"count":13}],"isBlockCoverage":true},{"functionName":"indexOf","ranges":[{"startOffset":17220,"endOffset":17316,"count":0}],"isBlockCoverage":false},{"functionName":"byteLength","ranges":[{"startOffset":17409,"endOffset":17438,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":17451,"endOffset":17515,"count":0}],"isBlockCoverage":false},{"functionName":"slice","ranges":[{"startOffset":17528,"endOffset":17574,"count":2}],"isBlockCoverage":true},{"functionName":"indexOf","ranges":[{"startOffset":17589,"endOffset":17688,"count":0}],"isBlockCoverage":false},{"functionName":"byteLength","ranges":[{"startOffset":17787,"endOffset":17816,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":17829,"endOffset":17893,"count":0}],"isBlockCoverage":false},{"functionName":"slice","ranges":[{"startOffset":17906,"endOffset":17952,"count":0}],"isBlockCoverage":false},{"functionName":"indexOf","ranges":[{"startOffset":17967,"endOffset":18066,"count":0}],"isBlockCoverage":false},{"functionName":"byteLength","ranges":[{"startOffset":18162,"endOffset":18187,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":18200,"endOffset":18266,"count":0}],"isBlockCoverage":false},{"functionName":"slice","ranges":[{"startOffset":18279,"endOffset":18327,"count":0}],"isBlockCoverage":false},{"functionName":"indexOf","ranges":[{"startOffset":18342,"endOffset":18440,"count":0}],"isBlockCoverage":false},{"functionName":"byteLength","ranges":[{"startOffset":18533,"endOffset":18558,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":18571,"endOffset":18636,"count":0}],"isBlockCoverage":false},{"functionName":"slice","ranges":[{"startOffset":18649,"endOffset":18696,"count":0}],"isBlockCoverage":false},{"functionName":"indexOf","ranges":[{"startOffset":18711,"endOffset":18923,"count":0}],"isBlockCoverage":false},{"functionName":"byteLength","ranges":[{"startOffset":19019,"endOffset":19070,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":19083,"endOffset":19149,"count":0}],"isBlockCoverage":false},{"functionName":"slice","ranges":[{"startOffset":19162,"endOffset":19210,"count":0}],"isBlockCoverage":false},{"functionName":"indexOf","ranges":[{"startOffset":19225,"endOffset":19439,"count":0}],"isBlockCoverage":false},{"functionName":"byteLength","ranges":[{"startOffset":19526,"endOffset":19557,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":19570,"endOffset":19633,"count":0}],"isBlockCoverage":false},{"functionName":"slice","ranges":[{"startOffset":19646,"endOffset":19691,"count":0}],"isBlockCoverage":false},{"functionName":"indexOf","ranges":[{"startOffset":19706,"endOffset":19914,"count":0}],"isBlockCoverage":false},{"functionName":"getEncodingOps","ranges":[{"startOffset":19922,"endOffset":21477,"count":15},{"startOffset":20048,"endOffset":20072,"count":13},{"startOffset":20072,"endOffset":20128,"count":2},{"startOffset":20128,"endOffset":20294,"count":0},{"startOffset":20299,"endOffset":20704,"count":0},{"startOffset":20709,"endOffset":20839,"count":0},{"startOffset":20844,"endOffset":20976,"count":0},{"startOffset":20981,"endOffset":21348,"count":0},{"startOffset":21353,"endOffset":21471,"count":0}],"isBlockCoverage":true},{"functionName":"byteLength","ranges":[{"startOffset":21479,"endOffset":22136,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":22276,"endOffset":22370,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":22448,"endOffset":22546,"count":0}],"isBlockCoverage":false},{"functionName":"copy","ranges":[{"startOffset":22578,"endOffset":22711,"count":0}],"isBlockCoverage":false},{"functionName":"toString","ranges":[{"startOffset":22992,"endOffset":23571,"count":16},{"startOffset":23064,"endOffset":23112,"count":0},{"startOffset":23164,"endOffset":23174,"count":0},{"startOffset":23204,"endOffset":23214,"count":0},{"startOffset":23263,"endOffset":23275,"count":0},{"startOffset":23291,"endOffset":23312,"count":0},{"startOffset":23338,"endOffset":23348,"count":1},{"startOffset":23348,"endOffset":23384,"count":15},{"startOffset":23384,"endOffset":23418,"count":0},{"startOffset":23418,"endOffset":23489,"count":15},{"startOffset":23489,"endOffset":23530,"count":0},{"startOffset":23530,"endOffset":23570,"count":15}],"isBlockCoverage":true},{"functionName":"equals","ranges":[{"startOffset":23600,"endOffset":23954,"count":0}],"isBlockCoverage":false},{"functionName":"inspect","ranges":[{"startOffset":24082,"endOffset":25077,"count":0}],"isBlockCoverage":false},{"functionName":"compare","ranges":[{"startOffset":25173,"endOffset":26322,"count":0}],"isBlockCoverage":false},{"functionName":"bidirectionalIndexOf","ranges":[{"startOffset":26750,"endOffset":28057,"count":0}],"isBlockCoverage":false},{"functionName":"indexOf","ranges":[{"startOffset":28086,"endOffset":28203,"count":0}],"isBlockCoverage":false},{"functionName":"lastIndexOf","ranges":[{"startOffset":28237,"endOffset":28359,"count":0}],"isBlockCoverage":false},{"functionName":"includes","ranges":[{"startOffset":28390,"endOffset":28495,"count":0}],"isBlockCoverage":false},{"functionName":"fill","ranges":[{"startOffset":28673,"endOffset":28772,"count":0}],"isBlockCoverage":false},{"functionName":"_fill","ranges":[{"startOffset":28775,"endOffset":30684,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":30711,"endOffset":31726,"count":0}],"isBlockCoverage":false},{"functionName":"toJSON","ranges":[{"startOffset":31755,"endOffset":31989,"count":0}],"isBlockCoverage":false},{"functionName":"adjustOffset","ranges":[{"startOffset":31992,"endOffset":32426,"count":118},{"startOffset":32232,"endOffset":32270,"count":59},{"startOffset":32270,"endOffset":32333,"count":0},{"startOffset":32333,"endOffset":32357,"count":59},{"startOffset":32357,"endOffset":32381,"count":7},{"startOffset":32381,"endOffset":32411,"count":52},{"startOffset":32411,"endOffset":32414,"count":0},{"startOffset":32415,"endOffset":32423,"count":52}],"isBlockCoverage":true},{"functionName":"slice","ranges":[{"startOffset":32453,"endOffset":32753,"count":59},{"startOffset":32614,"endOffset":32625,"count":0},{"startOffset":32673,"endOffset":32676,"count":0}],"isBlockCoverage":true},{"functionName":"swap","ranges":[{"startOffset":32756,"endOffset":32827,"count":0}],"isBlockCoverage":false},{"functionName":"swap16","ranges":[{"startOffset":32855,"endOffset":33259,"count":0}],"isBlockCoverage":false},{"functionName":"swap32","ranges":[{"startOffset":33288,"endOffset":33732,"count":0}],"isBlockCoverage":false},{"functionName":"swap64","ranges":[{"startOffset":33761,"endOffset":34269,"count":0}],"isBlockCoverage":false},{"functionName":"transcode","ranges":[{"startOffset":34582,"endOffset":35322,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":35391,"endOffset":35581,"count":0}],"isBlockCoverage":false},{"functionName":"btoa","ranges":[{"startOffset":35585,"endOffset":35921,"count":0}],"isBlockCoverage":false},{"functionName":"atob","ranges":[{"startOffset":36017,"endOffset":36338,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":36664,"endOffset":36699,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":36705,"endOffset":36742,"count":0}],"isBlockCoverage":false}]},{"scriptId":"19","url":"internal/buffer.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":29666,"count":1}],"isBlockCoverage":false},{"functionName":"checkBounds","ranges":[{"startOffset":1107,"endOffset":1323,"count":0}],"isBlockCoverage":false},{"functionName":"checkInt","ranges":[{"startOffset":1325,"endOffset":1947,"count":0}],"isBlockCoverage":false},{"functionName":"boundsError","ranges":[{"startOffset":1949,"endOffset":2352,"count":0}],"isBlockCoverage":false},{"functionName":"readBigUInt64LE","ranges":[{"startOffset":2372,"endOffset":2871,"count":0}],"isBlockCoverage":false},{"functionName":"readBigUInt64BE","ranges":[{"startOffset":2873,"endOffset":3372,"count":0}],"isBlockCoverage":false},{"functionName":"readBigInt64LE","ranges":[{"startOffset":3374,"endOffset":3875,"count":0}],"isBlockCoverage":false},{"functionName":"readBigInt64BE","ranges":[{"startOffset":3877,"endOffset":4372,"count":0}],"isBlockCoverage":false},{"functionName":"readUIntLE","ranges":[{"startOffset":4374,"endOffset":4926,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt48LE","ranges":[{"startOffset":4928,"endOffset":5311,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt40LE","ranges":[{"startOffset":5313,"endOffset":5669,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt32LE","ranges":[{"startOffset":5671,"endOffset":5997,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt24LE","ranges":[{"startOffset":5999,"endOffset":6287,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt16LE","ranges":[{"startOffset":6289,"endOffset":6549,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt8","ranges":[{"startOffset":6551,"endOffset":6731,"count":0}],"isBlockCoverage":false},{"functionName":"readUIntBE","ranges":[{"startOffset":6733,"endOffset":7285,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt48BE","ranges":[{"startOffset":7287,"endOffset":7670,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt40BE","ranges":[{"startOffset":7672,"endOffset":8028,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt32BE","ranges":[{"startOffset":8030,"endOffset":8356,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt24BE","ranges":[{"startOffset":8358,"endOffset":8646,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt16BE","ranges":[{"startOffset":8648,"endOffset":8908,"count":0}],"isBlockCoverage":false},{"functionName":"readIntLE","ranges":[{"startOffset":8910,"endOffset":9455,"count":0}],"isBlockCoverage":false},{"functionName":"readInt48LE","ranges":[{"startOffset":9457,"endOffset":9888,"count":0}],"isBlockCoverage":false},{"functionName":"readInt40LE","ranges":[{"startOffset":9890,"endOffset":10277,"count":0}],"isBlockCoverage":false},{"functionName":"readInt32LE","ranges":[{"startOffset":10279,"endOffset":10614,"count":0}],"isBlockCoverage":false},{"functionName":"readInt24LE","ranges":[{"startOffset":10616,"endOffset":10948,"count":0}],"isBlockCoverage":false},{"functionName":"readInt16LE","ranges":[{"startOffset":10950,"endOffset":11256,"count":0}],"isBlockCoverage":false},{"functionName":"readInt8","ranges":[{"startOffset":11258,"endOffset":11466,"count":0}],"isBlockCoverage":false},{"functionName":"readIntBE","ranges":[{"startOffset":11468,"endOffset":12013,"count":0}],"isBlockCoverage":false},{"functionName":"readInt48BE","ranges":[{"startOffset":12015,"endOffset":12444,"count":0}],"isBlockCoverage":false},{"functionName":"readInt40BE","ranges":[{"startOffset":12446,"endOffset":12834,"count":0}],"isBlockCoverage":false},{"functionName":"readInt32BE","ranges":[{"startOffset":12836,"endOffset":13171,"count":0}],"isBlockCoverage":false},{"functionName":"readInt24BE","ranges":[{"startOffset":13173,"endOffset":13505,"count":0}],"isBlockCoverage":false},{"functionName":"readInt16BE","ranges":[{"startOffset":13507,"endOffset":13813,"count":0}],"isBlockCoverage":false},{"functionName":"readFloatBackwards","ranges":[{"startOffset":13830,"endOffset":14235,"count":0}],"isBlockCoverage":false},{"functionName":"readFloatForwards","ranges":[{"startOffset":14237,"endOffset":14641,"count":0}],"isBlockCoverage":false},{"functionName":"readDoubleBackwards","ranges":[{"startOffset":14643,"endOffset":15213,"count":0}],"isBlockCoverage":false},{"functionName":"readDoubleForwards","ranges":[{"startOffset":15215,"endOffset":15784,"count":0}],"isBlockCoverage":false},{"functionName":"writeBigU_Int64LE","ranges":[{"startOffset":15805,"endOffset":16287,"count":0}],"isBlockCoverage":false},{"functionName":"writeBigUInt64LE","ranges":[{"startOffset":16289,"endOffset":16411,"count":0}],"isBlockCoverage":false},{"functionName":"writeBigU_Int64BE","ranges":[{"startOffset":16413,"endOffset":16911,"count":0}],"isBlockCoverage":false},{"functionName":"writeBigUInt64BE","ranges":[{"startOffset":16913,"endOffset":17035,"count":0}],"isBlockCoverage":false},{"functionName":"writeBigInt64LE","ranges":[{"startOffset":17037,"endOffset":17181,"count":0}],"isBlockCoverage":false},{"functionName":"writeBigInt64BE","ranges":[{"startOffset":17183,"endOffset":17327,"count":0}],"isBlockCoverage":false},{"functionName":"writeUIntLE","ranges":[{"startOffset":17329,"endOffset":17938,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int48LE","ranges":[{"startOffset":17940,"endOffset":18353,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int40LE","ranges":[{"startOffset":18355,"endOffset":18734,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int32LE","ranges":[{"startOffset":18736,"endOffset":19043,"count":0}],"isBlockCoverage":false},{"functionName":"writeUInt32LE","ranges":[{"startOffset":19045,"endOffset":19151,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int24LE","ranges":[{"startOffset":19153,"endOffset":19412,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int16LE","ranges":[{"startOffset":19414,"endOffset":19610,"count":0}],"isBlockCoverage":false},{"functionName":"writeUInt16LE","ranges":[{"startOffset":19612,"endOffset":19714,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int8","ranges":[{"startOffset":19716,"endOffset":20128,"count":0}],"isBlockCoverage":false},{"functionName":"writeUInt8","ranges":[{"startOffset":20130,"endOffset":20224,"count":0}],"isBlockCoverage":false},{"functionName":"writeUIntBE","ranges":[{"startOffset":20226,"endOffset":20835,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int48BE","ranges":[{"startOffset":20837,"endOffset":21258,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int40BE","ranges":[{"startOffset":21260,"endOffset":21622,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int32BE","ranges":[{"startOffset":21624,"endOffset":21939,"count":0}],"isBlockCoverage":false},{"functionName":"writeUInt32BE","ranges":[{"startOffset":21941,"endOffset":22047,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int24BE","ranges":[{"startOffset":22049,"endOffset":22314,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int16BE","ranges":[{"startOffset":22316,"endOffset":22512,"count":0}],"isBlockCoverage":false},{"functionName":"writeUInt16BE","ranges":[{"startOffset":22514,"endOffset":22616,"count":0}],"isBlockCoverage":false},{"functionName":"writeIntLE","ranges":[{"startOffset":22618,"endOffset":23280,"count":0}],"isBlockCoverage":false},{"functionName":"writeInt32LE","ranges":[{"startOffset":23282,"endOffset":23397,"count":0}],"isBlockCoverage":false},{"functionName":"writeInt16LE","ranges":[{"startOffset":23399,"endOffset":23506,"count":0}],"isBlockCoverage":false},{"functionName":"writeInt8","ranges":[{"startOffset":23508,"endOffset":23605,"count":0}],"isBlockCoverage":false},{"functionName":"writeIntBE","ranges":[{"startOffset":23607,"endOffset":24269,"count":0}],"isBlockCoverage":false},{"functionName":"writeInt32BE","ranges":[{"startOffset":24271,"endOffset":24386,"count":0}],"isBlockCoverage":false},{"functionName":"writeInt16BE","ranges":[{"startOffset":24388,"endOffset":24495,"count":0}],"isBlockCoverage":false},{"functionName":"writeDoubleForwards","ranges":[{"startOffset":24514,"endOffset":24980,"count":0}],"isBlockCoverage":false},{"functionName":"writeDoubleBackwards","ranges":[{"startOffset":24982,"endOffset":25449,"count":0}],"isBlockCoverage":false},{"functionName":"writeFloatForwards","ranges":[{"startOffset":25451,"endOffset":25752,"count":0}],"isBlockCoverage":false},{"functionName":"writeFloatBackwards","ranges":[{"startOffset":25754,"endOffset":26056,"count":0}],"isBlockCoverage":false},{"functionName":"addBufferPrototypeMethods","ranges":[{"startOffset":26098,"endOffset":29155,"count":1},{"startOffset":28181,"endOffset":28201,"count":0},{"startOffset":28255,"endOffset":28274,"count":0},{"startOffset":28330,"endOffset":28351,"count":0},{"startOffset":28407,"endOffset":28427,"count":0},{"startOffset":28484,"endOffset":28505,"count":0},{"startOffset":28561,"endOffset":28581,"count":0},{"startOffset":28639,"endOffset":28661,"count":0},{"startOffset":28719,"endOffset":28740,"count":0}],"isBlockCoverage":true},{"functionName":"markAsUntransferable","ranges":[{"startOffset":29311,"endOffset":29575,"count":1},{"startOffset":29379,"endOffset":29407,"count":0},{"startOffset":29430,"endOffset":29437,"count":0}],"isBlockCoverage":true}]},{"scriptId":"20","url":"internal/worker/js_transferable.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1310,"count":1}],"isBlockCoverage":false},{"functionName":"setup","ranges":[{"startOffset":304,"endOffset":1091,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":585,"endOffset":1087,"count":0}],"isBlockCoverage":false}]},{"scriptId":"21","url":"internal/process/per_thread.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":10598,"count":1}],"isBlockCoverage":false},{"functionName":"assert","ranges":[{"startOffset":796,"endOffset":884,"count":0}],"isBlockCoverage":false},{"functionName":"wrapProcessMethods","ranges":[{"startOffset":962,"endOffset":6703,"count":1}],"isBlockCoverage":true},{"functionName":"_rawDebug","ranges":[{"startOffset":1173,"endOffset":1255,"count":0}],"isBlockCoverage":false},{"functionName":"cpuUsage","ranges":[{"startOffset":1466,"endOffset":3025,"count":0}],"isBlockCoverage":false},{"functionName":"previousValueIsValid","ranges":[{"startOffset":3178,"endOffset":3315,"count":0}],"isBlockCoverage":false},{"functionName":"hrtime","ranges":[{"startOffset":3539,"endOffset":4142,"count":0}],"isBlockCoverage":false},{"functionName":"hrtimeBigInt","ranges":[{"startOffset":4329,"endOffset":4423,"count":0}],"isBlockCoverage":false},{"functionName":"memoryUsage","ranges":[{"startOffset":4468,"endOffset":4694,"count":0}],"isBlockCoverage":false},{"functionName":"exit","ranges":[{"startOffset":4698,"endOffset":5136,"count":0}],"isBlockCoverage":false},{"functionName":"kill","ranges":[{"startOffset":5140,"endOffset":5785,"count":0}],"isBlockCoverage":false},{"functionName":"resourceUsage","ranges":[{"startOffset":5836,"endOffset":6569,"count":0}],"isBlockCoverage":false},{"functionName":"buildAllowedFlags","ranges":[{"startOffset":6914,"endOffset":9974,"count":0}],"isBlockCoverage":false},{"functionName":"toggleTraceCategoryState","ranges":[{"startOffset":10164,"endOffset":10494,"count":1},{"startOffset":10244,"endOffset":10419,"count":0},{"startOffset":10451,"endOffset":10492,"count":0}],"isBlockCoverage":true}]},{"scriptId":"22","url":"internal/async_hooks.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":19081,"count":1}],"isBlockCoverage":false},{"functionName":"useDomainTrampoline","ranges":[{"startOffset":5274,"endOffset":5328,"count":0}],"isBlockCoverage":false},{"functionName":"callbackTrampoline","ranges":[{"startOffset":5330,"endOffset":5923,"count":0}],"isBlockCoverage":false},{"functionName":"executionAsyncResource","ranges":[{"startOffset":5999,"endOffset":6497,"count":0}],"isBlockCoverage":false},{"functionName":"inspectExceptionValue","ranges":[{"startOffset":6499,"endOffset":6635,"count":0}],"isBlockCoverage":false},{"functionName":"fatalError","ranges":[{"startOffset":6696,"endOffset":7082,"count":0}],"isBlockCoverage":false},{"functionName":"lookupPublicResource","ranges":[{"startOffset":7084,"endOffset":7433,"count":0}],"isBlockCoverage":false},{"functionName":"emitInitNative","ranges":[{"startOffset":7624,"endOffset":8717,"count":0}],"isBlockCoverage":false},{"functionName":"emitHook","ranges":[{"startOffset":8818,"endOffset":9768,"count":0}],"isBlockCoverage":false},{"functionName":"emitHookFactory","ranges":[{"startOffset":9770,"endOffset":10030,"count":4}],"isBlockCoverage":true},{"functionName":"getHookArrays","ranges":[{"startOffset":10059,"endOffset":10549,"count":0}],"isBlockCoverage":false},{"functionName":"storeActiveHooks","ranges":[{"startOffset":10552,"endOffset":10852,"count":0}],"isBlockCoverage":false},{"functionName":"copyHooks","ranges":[{"startOffset":10854,"endOffset":11119,"count":0}],"isBlockCoverage":false},{"functionName":"restoreActiveHooks","ranges":[{"startOffset":11234,"endOffset":11439,"count":0}],"isBlockCoverage":false},{"functionName":"trackPromise","ranges":[{"startOffset":11441,"endOffset":11798,"count":0}],"isBlockCoverage":false},{"functionName":"fastPromiseHook","ranges":[{"startOffset":11800,"endOffset":12936,"count":0}],"isBlockCoverage":false},{"functionName":"enableHooks","ranges":[{"startOffset":12967,"endOffset":13027,"count":0}],"isBlockCoverage":false},{"functionName":"updatePromiseHookMode","ranges":[{"startOffset":13055,"endOffset":13346,"count":0}],"isBlockCoverage":false},{"functionName":"disableHooks","ranges":[{"startOffset":13348,"endOffset":13623,"count":0}],"isBlockCoverage":false},{"functionName":"disablePromiseHookIfNecessary","ranges":[{"startOffset":13625,"endOffset":13751,"count":0}],"isBlockCoverage":false},{"functionName":"newAsyncId","ranges":[{"startOffset":13952,"endOffset":14022,"count":12}],"isBlockCoverage":true},{"functionName":"getOrSetAsyncId","ranges":[{"startOffset":14024,"endOffset":14214,"count":0}],"isBlockCoverage":false},{"functionName":"getDefaultTriggerAsyncId","ranges":[{"startOffset":14397,"endOffset":14687,"count":12},{"startOffset":14653,"endOffset":14686,"count":0}],"isBlockCoverage":true},{"functionName":"clearDefaultTriggerAsyncId","ranges":[{"startOffset":14690,"endOffset":14779,"count":0}],"isBlockCoverage":false},{"functionName":"defaultTriggerAsyncIdScope","ranges":[{"startOffset":14782,"endOffset":15257,"count":0}],"isBlockCoverage":false},{"functionName":"hasHooks","ranges":[{"startOffset":15259,"endOffset":15322,"count":60}],"isBlockCoverage":true},{"functionName":"enabledHooksExist","ranges":[{"startOffset":15324,"endOffset":15383,"count":12}],"isBlockCoverage":true},{"functionName":"initHooksExist","ranges":[{"startOffset":15385,"endOffset":15440,"count":12}],"isBlockCoverage":true},{"functionName":"afterHooksExist","ranges":[{"startOffset":15442,"endOffset":15499,"count":0}],"isBlockCoverage":false},{"functionName":"destroyHooksExist","ranges":[{"startOffset":15501,"endOffset":15562,"count":12}],"isBlockCoverage":true},{"functionName":"emitInitScript","ranges":[{"startOffset":15565,"endOffset":15973,"count":0}],"isBlockCoverage":false},{"functionName":"emitBeforeScript","ranges":[{"startOffset":15976,"endOffset":16152,"count":12},{"startOffset":16124,"endOffset":16150,"count":0}],"isBlockCoverage":true},{"functionName":"emitAfterScript","ranges":[{"startOffset":16155,"endOffset":16275,"count":12},{"startOffset":16219,"endOffset":16244,"count":0}],"isBlockCoverage":true},{"functionName":"emitDestroyScript","ranges":[{"startOffset":16278,"endOffset":16488,"count":0}],"isBlockCoverage":false},{"functionName":"hasAsyncIdStack","ranges":[{"startOffset":16491,"endOffset":16554,"count":0}],"isBlockCoverage":false},{"functionName":"pushAsyncContext","ranges":[{"startOffset":16620,"endOffset":17190,"count":12},{"startOffset":16840,"endOffset":16890,"count":0}],"isBlockCoverage":true},{"functionName":"popAsyncContext","ranges":[{"startOffset":17255,"endOffset":17879,"count":12},{"startOffset":17371,"endOffset":17384,"count":0},{"startOffset":17463,"endOffset":17569,"count":0}],"isBlockCoverage":true},{"functionName":"executionAsyncId","ranges":[{"startOffset":17882,"endOffset":17958,"count":0}],"isBlockCoverage":false},{"functionName":"triggerAsyncId","ranges":[{"startOffset":17960,"endOffset":18032,"count":0}],"isBlockCoverage":false}]},{"scriptId":"23","url":"internal/process/task_queues.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":4409,"count":1}],"isBlockCoverage":false},{"functionName":"hasTickScheduled","ranges":[{"startOffset":1007,"endOffset":1082,"count":0}],"isBlockCoverage":false},{"functionName":"setHasTickScheduled","ranges":[{"startOffset":1084,"endOffset":1170,"count":24},{"startOffset":1160,"endOffset":1163,"count":12},{"startOffset":1164,"endOffset":1167,"count":12}],"isBlockCoverage":true},{"functionName":"runNextTicks","ranges":[{"startOffset":1272,"endOffset":1468,"count":0}],"isBlockCoverage":false},{"functionName":"processTicksAndRejections","ranges":[{"startOffset":1470,"endOffset":2438,"count":12},{"startOffset":1762,"endOffset":1795,"count":0},{"startOffset":1928,"endOffset":1970,"count":0},{"startOffset":1983,"endOffset":2034,"count":0},{"startOffset":2047,"endOffset":2107,"count":0},{"startOffset":2120,"endOffset":2147,"count":0},{"startOffset":2231,"endOffset":2252,"count":0}],"isBlockCoverage":true},{"functionName":"nextTick","ranges":[{"startOffset":2582,"endOffset":3497,"count":12},{"startOffset":2654,"endOffset":2695,"count":0},{"startOffset":2725,"endOffset":2732,"count":0},{"startOffset":2780,"endOffset":2794,"count":0},{"startOffset":2841,"endOffset":2892,"count":0},{"startOffset":2897,"endOffset":2962,"count":0},{"startOffset":2967,"endOffset":3110,"count":0},{"startOffset":3409,"endOffset":3469,"count":0}],"isBlockCoverage":true},{"functionName":"runMicrotask","ranges":[{"startOffset":3499,"endOffset":3675,"count":0}],"isBlockCoverage":false},{"functionName":"queueMicrotask","ranges":[{"startOffset":3747,"endOffset":4107,"count":0}],"isBlockCoverage":false},{"functionName":"setupTaskQueue","ranges":[{"startOffset":4130,"endOffset":4387,"count":1}],"isBlockCoverage":true}]},{"scriptId":"24","url":"internal/process/promises.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":9605,"count":1}],"isBlockCoverage":false},{"functionName":"setHasRejectionToWarn","ranges":[{"startOffset":1918,"endOffset":2008,"count":12},{"startOffset":1998,"endOffset":2001,"count":0}],"isBlockCoverage":true},{"functionName":"hasRejectionToWarn","ranges":[{"startOffset":2010,"endOffset":2089,"count":0}],"isBlockCoverage":false},{"functionName":"getUnhandledRejectionsMode","ranges":[{"startOffset":2091,"endOffset":2626,"count":0}],"isBlockCoverage":false},{"functionName":"promiseRejectHandler","ranges":[{"startOffset":2628,"endOffset":3197,"count":0}],"isBlockCoverage":false},{"functionName":"resolveError","ranges":[{"startOffset":3199,"endOffset":3449,"count":0}],"isBlockCoverage":false},{"functionName":"unhandledRejection","ranges":[{"startOffset":3451,"endOffset":3745,"count":0}],"isBlockCoverage":false},{"functionName":"handledRejection","ranges":[{"startOffset":3747,"endOffset":4563,"count":0}],"isBlockCoverage":false},{"functionName":"emitUnhandledRejectionWarning","ranges":[{"startOffset":4635,"endOffset":5531,"count":0}],"isBlockCoverage":false},{"functionName":"emitDeprecationWarning","ranges":[{"startOffset":5564,"endOffset":5849,"count":0}],"isBlockCoverage":false},{"functionName":"processPromiseRejections","ranges":[{"startOffset":6022,"endOffset":8506,"count":12},{"startOffset":6180,"endOffset":6346,"count":0},{"startOffset":6411,"endOffset":8410,"count":0}],"isBlockCoverage":true},{"functionName":"getErrorWithoutStack","ranges":[{"startOffset":8508,"endOffset":8926,"count":0}],"isBlockCoverage":false},{"functionName":"generateUnhandledRejectionError","ranges":[{"startOffset":8928,"endOffset":9398,"count":0}],"isBlockCoverage":false},{"functionName":"listenForRejections","ranges":[{"startOffset":9400,"endOffset":9484,"count":1}],"isBlockCoverage":true}]},{"scriptId":"25","url":"internal/fixed_queue.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":4184,"count":1}],"isBlockCoverage":false},{"functionName":"FixedCircularBuffer","ranges":[{"startOffset":2959,"endOffset":3073,"count":1}],"isBlockCoverage":true},{"functionName":"isEmpty","ranges":[{"startOffset":3077,"endOffset":3129,"count":48}],"isBlockCoverage":true},{"functionName":"isFull","ranges":[{"startOffset":3133,"endOffset":3200,"count":12}],"isBlockCoverage":true},{"functionName":"push","ranges":[{"startOffset":3204,"endOffset":3291,"count":12}],"isBlockCoverage":true},{"functionName":"shift","ranges":[{"startOffset":3295,"endOffset":3510,"count":24},{"startOffset":3388,"endOffset":3509,"count":12}],"isBlockCoverage":true},{"functionName":"FixedQueue","ranges":[{"startOffset":3552,"endOffset":3626,"count":1}],"isBlockCoverage":true},{"functionName":"isEmpty","ranges":[{"startOffset":3630,"endOffset":3677,"count":24}],"isBlockCoverage":true},{"functionName":"push","ranges":[{"startOffset":3681,"endOffset":3945,"count":12},{"startOffset":3722,"endOffset":3915,"count":0}],"isBlockCoverage":true},{"functionName":"shift","ranges":[{"startOffset":3949,"endOffset":4180,"count":24},{"startOffset":4064,"endOffset":4159,"count":0}],"isBlockCoverage":true}]},{"scriptId":"26","url":"async_hooks.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":9502,"count":1}],"isBlockCoverage":false},{"functionName":"AsyncHook","ranges":[{"startOffset":1416,"endOffset":2250,"count":1},{"startOffset":1544,"endOffset":1586,"count":0},{"startOffset":1616,"endOffset":1647,"count":0},{"startOffset":1655,"endOffset":1699,"count":0},{"startOffset":1728,"endOffset":1758,"count":0},{"startOffset":1766,"endOffset":1809,"count":0},{"startOffset":1840,"endOffset":1872,"count":0},{"startOffset":1880,"endOffset":1925,"count":0},{"startOffset":1963,"endOffset":2002,"count":0},{"startOffset":2010,"endOffset":2062,"count":0}],"isBlockCoverage":true},{"functionName":"enable","ranges":[{"startOffset":2254,"endOffset":3491,"count":0}],"isBlockCoverage":false},{"functionName":"disable","ranges":[{"startOffset":3495,"endOffset":4271,"count":0}],"isBlockCoverage":false},{"functionName":"createHook","ranges":[{"startOffset":4276,"endOffset":4333,"count":1}],"isBlockCoverage":true},{"functionName":"AsyncResource","ranges":[{"startOffset":4426,"endOffset":5613,"count":0}],"isBlockCoverage":false},{"functionName":"runInAsyncScope","ranges":[{"startOffset":5617,"endOffset":5979,"count":0}],"isBlockCoverage":false},{"functionName":"emitDestroy","ranges":[{"startOffset":5983,"endOffset":6158,"count":0}],"isBlockCoverage":false},{"functionName":"asyncId","ranges":[{"startOffset":6162,"endOffset":6211,"count":0}],"isBlockCoverage":false},{"functionName":"triggerAsyncId","ranges":[{"startOffset":6215,"endOffset":6279,"count":0}],"isBlockCoverage":false},{"functionName":"bind","ranges":[{"startOffset":6283,"endOffset":6785,"count":0}],"isBlockCoverage":false},{"functionName":"bind","ranges":[{"startOffset":6796,"endOffset":6915,"count":0}],"isBlockCoverage":false},{"functionName":"init","ranges":[{"startOffset":6978,"endOffset":7260,"count":0}],"isBlockCoverage":false},{"functionName":"AsyncLocalStorage","ranges":[{"startOffset":7357,"endOffset":7454,"count":0}],"isBlockCoverage":false},{"functionName":"disable","ranges":[{"startOffset":7458,"endOffset":7783,"count":0}],"isBlockCoverage":false},{"functionName":"_enable","ranges":[{"startOffset":7787,"endOffset":7933,"count":0}],"isBlockCoverage":false},{"functionName":"_propagate","ranges":[{"startOffset":8002,"endOffset":8176,"count":0}],"isBlockCoverage":false},{"functionName":"enterWith","ranges":[{"startOffset":8180,"endOffset":8312,"count":0}],"isBlockCoverage":false},{"functionName":"run","ranges":[{"startOffset":8316,"endOffset":8892,"count":0}],"isBlockCoverage":false},{"functionName":"exit","ranges":[{"startOffset":8896,"endOffset":9094,"count":0}],"isBlockCoverage":false},{"functionName":"getStore","ranges":[{"startOffset":9098,"endOffset":9237,"count":0}],"isBlockCoverage":false}]},{"scriptId":"27","url":"internal/console/global.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1939,"count":1}],"isBlockCoverage":false}]},{"scriptId":"28","url":"internal/console/constructor.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":19933,"count":1}],"isBlockCoverage":false},{"functionName":"Console","ranges":[{"startOffset":2592,"endOffset":4763,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":4972,"endOffset":5026,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":5274,"endOffset":5480,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":5683,"endOffset":6271,"count":1}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":5865,"endOffset":5960,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":5972,"endOffset":6002,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":6104,"endOffset":6203,"count":489},{"startOffset":6137,"endOffset":6164,"count":1}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":6215,"endOffset":6245,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":6341,"endOffset":7523,"count":1}],"isBlockCoverage":true},{"functionName":"value","ranges":[{"startOffset":7593,"endOffset":9039,"count":163},{"startOffset":7804,"endOffset":7818,"count":0},{"startOffset":7872,"endOffset":7906,"count":0},{"startOffset":7972,"endOffset":8161,"count":0},{"startOffset":8219,"endOffset":8247,"count":0},{"startOffset":8604,"endOffset":8631,"count":152},{"startOffset":8685,"endOffset":8969,"count":0}],"isBlockCoverage":true},{"functionName":"value","ranges":[{"startOffset":9112,"endOffset":9602,"count":163},{"startOffset":9226,"endOffset":9332,"count":0},{"startOffset":9406,"endOffset":9527,"count":0},{"startOffset":9548,"endOffset":9570,"count":0}],"isBlockCoverage":true},{"functionName":"value","ranges":[{"startOffset":9673,"endOffset":9801,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":9872,"endOffset":10000,"count":163}],"isBlockCoverage":true},{"functionName":"createWriteErrorHandler","ranges":[{"startOffset":10089,"endOffset":10978,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":10157,"endOffset":10975,"count":163},{"startOffset":10402,"endOffset":10426,"count":0},{"startOffset":10920,"endOffset":10965,"count":12}],"isBlockCoverage":true},{"functionName":"log","ranges":[{"startOffset":11007,"endOffset":11094,"count":0}],"isBlockCoverage":false},{"functionName":"warn","ranges":[{"startOffset":11100,"endOffset":11188,"count":163}],"isBlockCoverage":true},{"functionName":"dir","ranges":[{"startOffset":11194,"endOffset":11379,"count":0}],"isBlockCoverage":false},{"functionName":"time","ranges":[{"startOffset":11384,"endOffset":11742,"count":0}],"isBlockCoverage":false},{"functionName":"timeEnd","ranges":[{"startOffset":11747,"endOffset":12036,"count":0}],"isBlockCoverage":false},{"functionName":"timeLog","ranges":[{"startOffset":12041,"endOffset":12279,"count":0}],"isBlockCoverage":false},{"functionName":"trace","ranges":[{"startOffset":12291,"endOffset":12477,"count":0}],"isBlockCoverage":false},{"functionName":"assert","ranges":[{"startOffset":12482,"endOffset":12700,"count":0}],"isBlockCoverage":false},{"functionName":"clear","ranges":[{"startOffset":12761,"endOffset":13191,"count":0}],"isBlockCoverage":false},{"functionName":"count","ranges":[{"startOffset":13252,"endOffset":13708,"count":0}],"isBlockCoverage":false},{"functionName":"countReset","ranges":[{"startOffset":13774,"endOffset":14062,"count":0}],"isBlockCoverage":false},{"functionName":"group","ranges":[{"startOffset":14067,"endOffset":14235,"count":0}],"isBlockCoverage":false},{"functionName":"groupEnd","ranges":[{"startOffset":14240,"endOffset":14408,"count":0}],"isBlockCoverage":false},{"functionName":"table","ranges":[{"startOffset":14457,"endOffset":17867,"count":0}],"isBlockCoverage":false},{"functionName":"timeLogImpl","ranges":[{"startOffset":17908,"endOffset":18404,"count":0}],"isBlockCoverage":false},{"functionName":"pad","ranges":[{"startOffset":18406,"endOffset":18483,"count":0}],"isBlockCoverage":false},{"functionName":"formatTime","ranges":[{"startOffset":18485,"endOffset":19247,"count":0}],"isBlockCoverage":false},{"functionName":"isArray","ranges":[{"startOffset":19381,"endOffset":19437,"count":0}],"isBlockCoverage":false},{"functionName":"noop","ranges":[{"startOffset":19440,"endOffset":19458,"count":0}],"isBlockCoverage":false}]},{"scriptId":"29","url":"internal/constants.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1582,"count":1}],"isBlockCoverage":false}]},{"scriptId":"30","url":"internal/util/inspector.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2215,"count":1}],"isBlockCoverage":false},{"functionName":"sendInspectorCommand","ranges":[{"startOffset":92,"endOffset":434,"count":0}],"isBlockCoverage":false},{"functionName":"installConsoleExtensions","ranges":[{"startOffset":508,"endOffset":1062,"count":0}],"isBlockCoverage":false},{"functionName":"wrapConsole","ranges":[{"startOffset":1141,"endOffset":1931,"count":1},{"startOffset":1299,"endOffset":1929,"count":23},{"startOffset":1514,"endOffset":1807,"count":19},{"startOffset":1807,"endOffset":1925,"count":4}],"isBlockCoverage":true},{"functionName":"get consoleFromVM","ranges":[{"startOffset":2103,"endOffset":2154,"count":0}],"isBlockCoverage":false},{"functionName":"set consoleFromVM","ranges":[{"startOffset":2158,"endOffset":2211,"count":1}],"isBlockCoverage":true}]},{"scriptId":"31","url":"internal/url.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":41325,"count":1}],"isBlockCoverage":false},{"functionName":"toUSVString","ranges":[{"startOffset":2224,"endOffset":2520,"count":2},{"startOffset":2477,"endOffset":2519,"count":0}],"isBlockCoverage":true},{"functionName":"serializeTupleOrigin","ranges":[{"startOffset":2732,"endOffset":2850,"count":0}],"isBlockCoverage":false},{"functionName":"URLContext","ranges":[{"startOffset":3254,"endOffset":3477,"count":24}],"isBlockCoverage":true},{"functionName":"URLSearchParams","ranges":[{"startOffset":3767,"endOffset":6130,"count":18},{"startOffset":3882,"endOffset":6068,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":6134,"endOffset":7204,"count":0}],"isBlockCoverage":false},{"functionName":"onParseComplete","ranges":[{"startOffset":7208,"endOffset":7901,"count":18},{"startOffset":7463,"endOffset":7473,"count":0},{"startOffset":7536,"endOffset":7546,"count":0},{"startOffset":7627,"endOffset":7631,"count":0}],"isBlockCoverage":true},{"functionName":"onParseError","ranges":[{"startOffset":7903,"endOffset":7978,"count":6}],"isBlockCoverage":true},{"functionName":"onParseProtocolComplete","ranges":[{"startOffset":7980,"endOffset":8325,"count":0}],"isBlockCoverage":false},{"functionName":"onParseHostnameComplete","ranges":[{"startOffset":8327,"endOffset":8673,"count":0}],"isBlockCoverage":false},{"functionName":"onParsePortComplete","ranges":[{"startOffset":8675,"endOffset":8837,"count":0}],"isBlockCoverage":false},{"functionName":"onParseHostComplete","ranges":[{"startOffset":8839,"endOffset":9145,"count":0}],"isBlockCoverage":false},{"functionName":"onParsePathComplete","ranges":[{"startOffset":9147,"endOffset":9641,"count":4},{"startOffset":9413,"endOffset":9481,"count":0}],"isBlockCoverage":true},{"functionName":"onParseSearchComplete","ranges":[{"startOffset":9643,"endOffset":9811,"count":0}],"isBlockCoverage":false},{"functionName":"onParseHashComplete","ranges":[{"startOffset":9813,"endOffset":9983,"count":0}],"isBlockCoverage":false},{"functionName":"URL","ranges":[{"startOffset":9999,"endOffset":10327,"count":24},{"startOffset":10134,"endOffset":10186,"count":3}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":10331,"endOffset":10412,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":10416,"endOffset":10509,"count":54}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":10584,"endOffset":10784,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":10788,"endOffset":11721,"count":0}],"isBlockCoverage":false},{"functionName":"format","ranges":[{"startOffset":11890,"endOffset":13071,"count":18},{"startOffset":11975,"endOffset":12036,"count":0},{"startOffset":12446,"endOffset":12607,"count":0},{"startOffset":12639,"endOffset":12676,"count":0},{"startOffset":12730,"endOffset":12752,"count":0},{"startOffset":12760,"endOffset":12824,"count":0},{"startOffset":12936,"endOffset":12959,"count":0},{"startOffset":13021,"endOffset":13047,"count":0}],"isBlockCoverage":true},{"functionName":"toString","ranges":[{"startOffset":13345,"endOffset":13404,"count":2}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":13470,"endOffset":13515,"count":16}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":13521,"endOffset":13701,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":13782,"endOffset":14410,"count":10},{"startOffset":13922,"endOffset":14166,"count":0},{"startOffset":14175,"endOffset":14187,"count":0},{"startOffset":14196,"endOffset":14211,"count":0},{"startOffset":14220,"endOffset":14233,"count":0},{"startOffset":14242,"endOffset":14256,"count":0},{"startOffset":14265,"endOffset":14276,"count":0},{"startOffset":14285,"endOffset":14368,"count":0}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":14480,"endOffset":14528,"count":17}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":14534,"endOffset":14903,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":14973,"endOffset":15023,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":15029,"endOffset":15427,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":15497,"endOffset":15547,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":15553,"endOffset":15951,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":16017,"endOffset":16173,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":16179,"endOffset":16470,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":16540,"endOffset":16592,"count":8}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":16598,"endOffset":16897,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":16963,"endOffset":17063,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":17069,"endOffset":17386,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":17456,"endOffset":17651,"count":50},{"startOffset":17535,"endOffset":17554,"count":0},{"startOffset":17596,"endOffset":17606,"count":0}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":17657,"endOffset":17876,"count":4},{"startOffset":17767,"endOffset":17774,"count":0}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":17944,"endOffset":18083,"count":2},{"startOffset":18016,"endOffset":18031,"count":0},{"startOffset":18051,"endOffset":18082,"count":0}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":18089,"endOffset":18581,"count":2},{"startOffset":18275,"endOffset":18523,"count":0}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":18668,"endOffset":18714,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":18780,"endOffset":18931,"count":2},{"startOffset":18858,"endOffset":18876,"count":0},{"startOffset":18896,"endOffset":18930,"count":0}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":18937,"endOffset":19355,"count":2},{"startOffset":19159,"endOffset":19354,"count":0}],"isBlockCoverage":true},{"functionName":"toJSON","ranges":[{"startOffset":19501,"endOffset":19558,"count":0}],"isBlockCoverage":false},{"functionName":"update","ranges":[{"startOffset":19568,"endOffset":19873,"count":0}],"isBlockCoverage":false},{"functionName":"initSearchParams","ranges":[{"startOffset":19875,"endOffset":20015,"count":20},{"startOffset":19972,"endOffset":20014,"count":0}],"isBlockCoverage":true},{"functionName":"parseParams","ranges":[{"startOffset":20124,"endOffset":22425,"count":0}],"isBlockCoverage":false},{"functionName":"serializeParams","ranges":[{"startOffset":23404,"endOffset":23964,"count":0}],"isBlockCoverage":false},{"functionName":"defineIDLClass","ranges":[{"startOffset":24019,"endOffset":24707,"count":2},{"startOffset":24357,"endOffset":24503,"count":13},{"startOffset":24558,"endOffset":24705,"count":1}],"isBlockCoverage":true},{"functionName":"merge","ranges":[{"startOffset":24727,"endOffset":25357,"count":0}],"isBlockCoverage":false},{"functionName":"append","ranges":[{"startOffset":25424,"endOffset":25811,"count":0}],"isBlockCoverage":false},{"functionName":"delete","ranges":[{"startOffset":25816,"endOffset":26315,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":26320,"endOffset":26754,"count":0}],"isBlockCoverage":false},{"functionName":"getAll","ranges":[{"startOffset":26759,"endOffset":27227,"count":0}],"isBlockCoverage":false},{"functionName":"has","ranges":[{"startOffset":27232,"endOffset":27660,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":27665,"endOffset":28691,"count":0}],"isBlockCoverage":false},{"functionName":"sort","ranges":[{"startOffset":28696,"endOffset":29873,"count":0}],"isBlockCoverage":false},{"functionName":"entries","ranges":[{"startOffset":30036,"endOffset":30245,"count":0}],"isBlockCoverage":false},{"functionName":"forEach","ranges":[{"startOffset":30250,"endOffset":30822,"count":0}],"isBlockCoverage":false},{"functionName":"keys","ranges":[{"startOffset":30877,"endOffset":31077,"count":0}],"isBlockCoverage":false},{"functionName":"values","ranges":[{"startOffset":31082,"endOffset":31286,"count":0}],"isBlockCoverage":false},{"functionName":"toString","ranges":[{"startOffset":31419,"endOffset":31619,"count":0}],"isBlockCoverage":false},{"functionName":"createSearchParamsIterator","ranges":[{"startOffset":31899,"endOffset":32102,"count":0}],"isBlockCoverage":false},{"functionName":"next","ranges":[{"startOffset":32326,"endOffset":33079,"count":0}],"isBlockCoverage":false},{"functionName":"defineIDLClass","ranges":[{"startOffset":33083,"endOffset":34297,"count":0}],"isBlockCoverage":false},{"functionName":"domainToASCII","ranges":[{"startOffset":34303,"endOffset":34478,"count":0}],"isBlockCoverage":false},{"functionName":"domainToUnicode","ranges":[{"startOffset":34480,"endOffset":34659,"count":0}],"isBlockCoverage":false},{"functionName":"urlToOptions","ranges":[{"startOffset":34802,"endOffset":35345,"count":0}],"isBlockCoverage":false},{"functionName":"getPathFromURLWin32","ranges":[{"startOffset":35381,"endOffset":36774,"count":0}],"isBlockCoverage":false},{"functionName":"getPathFromURLPosix","ranges":[{"startOffset":36776,"endOffset":37280,"count":8},{"startOffset":36839,"endOffset":36895,"count":0},{"startOffset":36973,"endOffset":37239,"count":328},{"startOffset":37004,"endOffset":37235,"count":0}],"isBlockCoverage":true},{"functionName":"fileURLToPath","ranges":[{"startOffset":37282,"endOffset":37629,"count":8},{"startOffset":37349,"endOffset":37370,"count":0},{"startOffset":37408,"endOffset":37472,"count":0},{"startOffset":37510,"endOffset":37551,"count":0},{"startOffset":37571,"endOffset":37598,"count":0}],"isBlockCoverage":true},{"functionName":"encodePathChars","ranges":[{"startOffset":38389,"endOffset":38945,"count":4},{"startOffset":38460,"endOffset":38509,"count":0},{"startOffset":38615,"endOffset":38666,"count":0},{"startOffset":38702,"endOffset":38751,"count":0},{"startOffset":38787,"endOffset":38843,"count":0},{"startOffset":38879,"endOffset":38924,"count":0}],"isBlockCoverage":true},{"functionName":"pathToFileURL","ranges":[{"startOffset":38947,"endOffset":40062,"count":4},{"startOffset":39035,"endOffset":39065,"count":0},{"startOffset":39067,"endOffset":39616,"count":0},{"startOffset":39848,"endOffset":39911,"count":3},{"startOffset":39871,"endOffset":39910,"count":0},{"startOffset":39913,"endOffset":39966,"count":1},{"startOffset":39974,"endOffset":39990,"count":1}],"isBlockCoverage":true},{"functionName":"isURLInstance","ranges":[{"startOffset":40064,"endOffset":40183,"count":32},{"startOffset":40157,"endOffset":40180,"count":10}],"isBlockCoverage":true},{"functionName":"toPathIfFileURL","ranges":[{"startOffset":40185,"endOffset":40330,"count":24},{"startOffset":40268,"endOffset":40289,"count":22},{"startOffset":40289,"endOffset":40329,"count":2}],"isBlockCoverage":true},{"functionName":"constructUrl","ranges":[{"startOffset":40332,"endOffset":41032,"count":0}],"isBlockCoverage":false}]},{"scriptId":"32","url":"internal/querystring.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":3021,"count":1}],"isBlockCoverage":false},{"functionName":"encodeStr","ranges":[{"startOffset":1336,"endOffset":2959,"count":0}],"isBlockCoverage":false}]},{"scriptId":"33","url":"path.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":43399,"count":1}],"isBlockCoverage":false},{"functionName":"isPathSeparator","ranges":[{"startOffset":1492,"endOffset":1596,"count":0}],"isBlockCoverage":false},{"functionName":"isPosixPathSeparator","ranges":[{"startOffset":1598,"endOffset":1675,"count":705}],"isBlockCoverage":true},{"functionName":"isWindowsDeviceRoot","ranges":[{"startOffset":1677,"endOffset":1847,"count":0}],"isBlockCoverage":false},{"functionName":"normalizeString","ranges":[{"startOffset":1910,"endOffset":3714,"count":17},{"startOffset":2127,"endOffset":3698,"count":705},{"startOffset":2160,"endOffset":2186,"count":688},{"startOffset":2186,"endOffset":2277,"count":17},{"startOffset":2235,"endOffset":2277,"count":0},{"startOffset":2277,"endOffset":2310,"count":688},{"startOffset":2310,"endOffset":3599,"count":120},{"startOffset":2342,"endOffset":2355,"count":100},{"startOffset":2357,"endOffset":2382,"count":20},{"startOffset":2382,"endOffset":3556,"count":100},{"startOffset":2404,"endOffset":3343,"count":2},{"startOffset":2460,"endOffset":2518,"count":0},{"startOffset":2519,"endOffset":2577,"count":0},{"startOffset":2715,"endOffset":2791,"count":0},{"startOffset":3024,"endOffset":3193,"count":0},{"startOffset":3203,"endOffset":3335,"count":0},{"startOffset":3343,"endOffset":3556,"count":98},{"startOffset":3389,"endOffset":3442,"count":81},{"startOffset":3442,"endOffset":3501,"count":17},{"startOffset":3556,"endOffset":3599,"count":118},{"startOffset":3599,"endOffset":3694,"count":568},{"startOffset":3627,"endOffset":3641,"count":22},{"startOffset":3643,"endOffset":3664,"count":7},{"startOffset":3664,"endOffset":3694,"count":561}],"isBlockCoverage":true},{"functionName":"_format","ranges":[{"startOffset":3892,"endOffset":4317,"count":0}],"isBlockCoverage":false},{"functionName":"resolve","ranges":[{"startOffset":4435,"endOffset":8838,"count":0}],"isBlockCoverage":false},{"functionName":"normalize","ranges":[{"startOffset":8904,"endOffset":11655,"count":0}],"isBlockCoverage":false},{"functionName":"isAbsolute","ranges":[{"startOffset":11722,"endOffset":12090,"count":0}],"isBlockCoverage":false},{"functionName":"join","ranges":[{"startOffset":12159,"endOffset":14215,"count":0}],"isBlockCoverage":false},{"functionName":"relative","ranges":[{"startOffset":14512,"endOffset":17986,"count":0}],"isBlockCoverage":false},{"functionName":"toNamespacedPath","ranges":[{"startOffset":17991,"endOffset":19019,"count":0}],"isBlockCoverage":false},{"functionName":"dirname","ranges":[{"startOffset":19085,"endOffset":21413,"count":0}],"isBlockCoverage":false},{"functionName":"basename","ranges":[{"startOffset":21506,"endOffset":24141,"count":0}],"isBlockCoverage":false},{"functionName":"extname","ranges":[{"startOffset":24207,"endOffset":26216,"count":0}],"isBlockCoverage":false},{"functionName":"parse","ranges":[{"startOffset":26420,"endOffset":30886,"count":0}],"isBlockCoverage":false},{"functionName":"resolve","ranges":[{"startOffset":31069,"endOffset":31965,"count":17},{"startOffset":31192,"endOffset":31212,"count":42},{"startOffset":31219,"endOffset":31519,"count":26},{"startOffset":31247,"endOffset":31256,"count":25},{"startOffset":31257,"endOffset":31272,"count":1},{"startOffset":31369,"endOffset":31396,"count":0},{"startOffset":31904,"endOffset":31954,"count":0},{"startOffset":31955,"endOffset":31960,"count":0}],"isBlockCoverage":true},{"functionName":"normalize","ranges":[{"startOffset":32031,"endOffset":32606,"count":0}],"isBlockCoverage":false},{"functionName":"isAbsolute","ranges":[{"startOffset":32673,"endOffset":32802,"count":4}],"isBlockCoverage":true},{"functionName":"join","ranges":[{"startOffset":32871,"endOffset":33285,"count":0}],"isBlockCoverage":false},{"functionName":"relative","ranges":[{"startOffset":33375,"endOffset":35585,"count":0}],"isBlockCoverage":false},{"functionName":"toNamespacedPath","ranges":[{"startOffset":35590,"endOffset":35666,"count":35}],"isBlockCoverage":true},{"functionName":"dirname","ranges":[{"startOffset":35732,"endOffset":36360,"count":0}],"isBlockCoverage":false},{"functionName":"basename","ranges":[{"startOffset":36453,"endOffset":38773,"count":0}],"isBlockCoverage":false},{"functionName":"extname","ranges":[{"startOffset":38839,"endOffset":40497,"count":2},{"startOffset":39172,"endOffset":40118,"count":18},{"startOffset":39252,"endOffset":39498,"count":2},{"startOffset":39472,"endOffset":39498,"count":0},{"startOffset":39498,"endOffset":39521,"count":16},{"startOffset":39521,"endOffset":39678,"count":2},{"startOffset":39678,"endOffset":39708,"count":16},{"startOffset":39708,"endOffset":39908,"count":2},{"startOffset":39837,"endOffset":39900,"count":0},{"startOffset":39908,"endOffset":40112,"count":14},{"startOffset":39935,"endOffset":40112,"count":10},{"startOffset":40357,"endOffset":40389,"count":0},{"startOffset":40390,"endOffset":40428,"count":0},{"startOffset":40431,"endOffset":40455,"count":0}],"isBlockCoverage":true},{"functionName":"parse","ranges":[{"startOffset":40706,"endOffset":43060,"count":0}],"isBlockCoverage":false}]},{"scriptId":"34","url":"internal/encoding.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":15916,"count":1}],"isBlockCoverage":false},{"functionName":"lazyBuffer","ranges":[{"startOffset":958,"endOffset":1067,"count":0}],"isBlockCoverage":false},{"functionName":"validateEncoder","ranges":[{"startOffset":1069,"endOffset":1194,"count":0}],"isBlockCoverage":false},{"functionName":"validateDecoder","ranges":[{"startOffset":1196,"endOffset":1321,"count":2},{"startOffset":1277,"endOffset":1319,"count":0}],"isBlockCoverage":true},{"functionName":"validateArgument","ranges":[{"startOffset":1323,"endOffset":1533,"count":3},{"startOffset":1470,"endOffset":1531,"count":0}],"isBlockCoverage":true},{"functionName":"trimAsciiWhitespace","ranges":[{"startOffset":8274,"endOffset":8756,"count":0}],"isBlockCoverage":false},{"functionName":"getEncodingFromLabel","ranges":[{"startOffset":8758,"endOffset":8937,"count":1},{"startOffset":8869,"endOffset":8936,"count":0}],"isBlockCoverage":true},{"functionName":"TextEncoder","ranges":[{"startOffset":9008,"endOffset":9054,"count":0}],"isBlockCoverage":false},{"functionName":"get encoding","ranges":[{"startOffset":9058,"endOffset":9125,"count":0}],"isBlockCoverage":false},{"functionName":"encode","ranges":[{"startOffset":9129,"endOffset":9221,"count":0}],"isBlockCoverage":false},{"functionName":"encodeInto","ranges":[{"startOffset":9225,"endOffset":9535,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":9539,"endOffset":9934,"count":0}],"isBlockCoverage":false},{"functionName":"makeTextDecoderICU","ranges":[{"startOffset":10305,"endOffset":11966,"count":1}],"isBlockCoverage":true},{"functionName":"TextDecoder","ranges":[{"startOffset":10443,"endOffset":11170,"count":1},{"startOffset":10675,"endOffset":10722,"count":0},{"startOffset":10806,"endOffset":10829,"count":0},{"startOffset":10870,"endOffset":10898,"count":0},{"startOffset":11000,"endOffset":11047,"count":0}],"isBlockCoverage":true},{"functionName":"decode","ranges":[{"startOffset":11177,"endOffset":11937,"count":2},{"startOffset":11279,"endOffset":11330,"count":0},{"startOffset":11367,"endOffset":11546,"count":0},{"startOffset":11693,"endOffset":11696,"count":0},{"startOffset":11814,"endOffset":11896,"count":0}],"isBlockCoverage":true},{"functionName":"makeTextDecoderJS","ranges":[{"startOffset":11968,"endOffset":14509,"count":0}],"isBlockCoverage":false},{"functionName":"get encoding","ranges":[{"startOffset":14634,"endOffset":14715,"count":0}],"isBlockCoverage":false},{"functionName":"get fatal","ranges":[{"startOffset":14722,"endOffset":14849,"count":0}],"isBlockCoverage":false},{"functionName":"get ignoreBOM","ranges":[{"startOffset":14856,"endOffset":15011,"count":0}],"isBlockCoverage":false},{"functionName":"ObjectGetOwnPropertyDescriptors","ranges":[{"startOffset":15018,"endOffset":15632,"count":0}],"isBlockCoverage":false}]},{"scriptId":"35","url":"timers.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":8324,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":1797,"endOffset":1822,"count":0}],"isBlockCoverage":false},{"functionName":"unenroll","ranges":[{"startOffset":2319,"endOffset":3503,"count":0}],"isBlockCoverage":false},{"functionName":"enroll","ranges":[{"startOffset":3710,"endOffset":3963,"count":0}],"isBlockCoverage":false},{"functionName":"setTimeout","ranges":[{"startOffset":3994,"endOffset":4639,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":4716,"endOffset":4850,"count":0}],"isBlockCoverage":false},{"functionName":"clearTimeout","ranges":[{"startOffset":4856,"endOffset":5222,"count":0}],"isBlockCoverage":false},{"functionName":"setInterval","ranges":[{"startOffset":5224,"endOffset":5871,"count":0}],"isBlockCoverage":false},{"functionName":"clearInterval","ranges":[{"startOffset":5873,"endOffset":6171,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.close","ranges":[{"startOffset":6199,"endOffset":6250,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.","ranges":[{"startOffset":6292,"endOffset":6453,"count":0}],"isBlockCoverage":false},{"functionName":"setImmediate","ranges":[{"startOffset":6456,"endOffset":6997,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":7076,"endOffset":7212,"count":0}],"isBlockCoverage":false},{"functionName":"clearImmediate","ranges":[{"startOffset":7219,"endOffset":7685,"count":0}],"isBlockCoverage":false}]},{"scriptId":"36","url":"internal/linkedlist.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1112,"count":1}],"isBlockCoverage":false},{"functionName":"init","ranges":[{"startOffset":15,"endOffset":88,"count":0}],"isBlockCoverage":false},{"functionName":"peek","ranges":[{"startOffset":118,"endOffset":210,"count":0}],"isBlockCoverage":false},{"functionName":"remove","ranges":[{"startOffset":245,"endOffset":472,"count":0}],"isBlockCoverage":false},{"functionName":"append","ranges":[{"startOffset":528,"endOffset":980,"count":0}],"isBlockCoverage":false},{"functionName":"isEmpty","ranges":[{"startOffset":982,"endOffset":1042,"count":0}],"isBlockCoverage":false}]},{"scriptId":"37","url":"internal/timers.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":18495,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":4398,"endOffset":4423,"count":0}],"isBlockCoverage":false},{"functionName":"initAsyncResource","ranges":[{"startOffset":5366,"endOffset":5644,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout","ranges":[{"startOffset":5729,"endOffset":6726,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.","ranges":[{"startOffset":6839,"endOffset":7010,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.refresh","ranges":[{"startOffset":7041,"endOffset":7139,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.unref","ranges":[{"startOffset":7168,"endOffset":7296,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.ref","ranges":[{"startOffset":7323,"endOffset":7451,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.hasRef","ranges":[{"startOffset":7481,"endOffset":7518,"count":0}],"isBlockCoverage":false},{"functionName":"TimersList","ranges":[{"startOffset":7521,"endOffset":7821,"count":0}],"isBlockCoverage":false},{"functionName":"TimersList.","ranges":[{"startOffset":7937,"endOffset":8108,"count":0}],"isBlockCoverage":false},{"functionName":"ImmediateList","ranges":[{"startOffset":8166,"endOffset":8234,"count":2}],"isBlockCoverage":true},{"functionName":"ImmediateList.append","ranges":[{"startOffset":8413,"endOffset":8580,"count":0}],"isBlockCoverage":false},{"functionName":"ImmediateList.remove","ranges":[{"startOffset":8758,"endOffset":9095,"count":0}],"isBlockCoverage":false},{"functionName":"incRefCount","ranges":[{"startOffset":9098,"endOffset":9174,"count":0}],"isBlockCoverage":false},{"functionName":"decRefCount","ranges":[{"startOffset":9176,"endOffset":9253,"count":0}],"isBlockCoverage":false},{"functionName":"active","ranges":[{"startOffset":9336,"endOffset":9390,"count":0}],"isBlockCoverage":false},{"functionName":"unrefActive","ranges":[{"startOffset":9537,"endOffset":9597,"count":0}],"isBlockCoverage":false},{"functionName":"insertGuarded","ranges":[{"startOffset":9818,"endOffset":10334,"count":0}],"isBlockCoverage":false},{"functionName":"insert","ranges":[{"startOffset":10336,"endOffset":10987,"count":0}],"isBlockCoverage":false},{"functionName":"setUnrefTimeout","ranges":[{"startOffset":10989,"endOffset":11295,"count":0}],"isBlockCoverage":false},{"functionName":"getTimerDuration","ranges":[{"startOffset":11362,"endOffset":11884,"count":0}],"isBlockCoverage":false},{"functionName":"compareTimersLists","ranges":[{"startOffset":11886,"endOffset":12091,"count":0}],"isBlockCoverage":false},{"functionName":"setPosition","ranges":[{"startOffset":12093,"endOffset":12164,"count":0}],"isBlockCoverage":false},{"functionName":"getTimerCallbacks","ranges":[{"startOffset":12166,"endOffset":17263,"count":1}],"isBlockCoverage":true},{"functionName":"processImmediate","ranges":[{"startOffset":12478,"endOffset":14279,"count":0}],"isBlockCoverage":false},{"functionName":"processTimers","ranges":[{"startOffset":14284,"endOffset":14758,"count":0}],"isBlockCoverage":false},{"functionName":"listOnTimeout","ranges":[{"startOffset":14762,"endOffset":17204,"count":0}],"isBlockCoverage":false},{"functionName":"Immediate","ranges":[{"startOffset":17285,"endOffset":17607,"count":0}],"isBlockCoverage":false},{"functionName":"ref","ranges":[{"startOffset":17611,"endOffset":17784,"count":0}],"isBlockCoverage":false},{"functionName":"unref","ranges":[{"startOffset":17788,"endOffset":17964,"count":0}],"isBlockCoverage":false},{"functionName":"hasRef","ranges":[{"startOffset":17968,"endOffset":18009,"count":0}],"isBlockCoverage":false}]},{"scriptId":"38","url":"internal/priority_queue.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2924,"count":1}],"isBlockCoverage":false},{"functionName":"PriorityQueue","ranges":[{"startOffset":570,"endOffset":811,"count":1}],"isBlockCoverage":true},{"functionName":"module.exports","ranges":[{"startOffset":815,"endOffset":855,"count":0}],"isBlockCoverage":false},{"functionName":"insert","ranges":[{"startOffset":859,"endOffset":1044,"count":0}],"isBlockCoverage":false},{"functionName":"peek","ranges":[{"startOffset":1048,"endOffset":1087,"count":0}],"isBlockCoverage":false},{"functionName":"percolateDown","ranges":[{"startOffset":1091,"endOffset":1759,"count":0}],"isBlockCoverage":false},{"functionName":"percolateUp","ranges":[{"startOffset":1763,"endOffset":2254,"count":0}],"isBlockCoverage":false},{"functionName":"removeAt","ranges":[{"startOffset":2258,"endOffset":2591,"count":0}],"isBlockCoverage":false},{"functionName":"remove","ranges":[{"startOffset":2595,"endOffset":2761,"count":0}],"isBlockCoverage":false},{"functionName":"shift","ranges":[{"startOffset":2765,"endOffset":2920,"count":0}],"isBlockCoverage":false}]},{"scriptId":"39","url":"internal/util/debuglog.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2815,"count":1}],"isBlockCoverage":false},{"functionName":"initializeDebugEnv","ranges":[{"startOffset":500,"endOffset":873,"count":1},{"startOffset":591,"endOffset":790,"count":0}],"isBlockCoverage":true},{"functionName":"emitWarningIfNeeded","ranges":[{"startOffset":947,"endOffset":1272,"count":0}],"isBlockCoverage":false},{"functionName":"noop","ranges":[{"startOffset":1274,"endOffset":1292,"count":8}],"isBlockCoverage":true},{"functionName":"debuglogImpl","ranges":[{"startOffset":1294,"endOffset":1859,"count":3},{"startOffset":1369,"endOffset":1831,"count":2},{"startOffset":1388,"endOffset":1784,"count":0}],"isBlockCoverage":true},{"functionName":"debug","ranges":[{"startOffset":1477,"endOffset":1777,"count":0}],"isBlockCoverage":false},{"functionName":"debuglog","ranges":[{"startOffset":2079,"endOffset":2758,"count":11}],"isBlockCoverage":true},{"functionName":"init","ranges":[{"startOffset":2110,"endOffset":2206,"count":3}],"isBlockCoverage":true},{"functionName":"debug","ranges":[{"startOffset":2221,"endOffset":2458,"count":3}],"isBlockCoverage":true},{"functionName":"test","ranges":[{"startOffset":2488,"endOffset":2557,"count":0}],"isBlockCoverage":false},{"functionName":"logger","ranges":[{"startOffset":2576,"endOffset":2603,"count":3}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":2653,"endOffset":2687,"count":0}],"isBlockCoverage":false}]},{"scriptId":"40","url":"internal/process/execution.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":6944,"count":1}],"isBlockCoverage":false},{"functionName":"tryGetCwd","ranges":[{"startOffset":526,"endOffset":856,"count":1},{"startOffset":587,"endOffset":854,"count":0}],"isBlockCoverage":true},{"functionName":"evalModule","ranges":[{"startOffset":858,"endOffset":1298,"count":0}],"isBlockCoverage":false},{"functionName":"evalScript","ranges":[{"startOffset":1300,"endOffset":2682,"count":0}],"isBlockCoverage":false},{"functionName":"setUncaughtExceptionCaptureCallback","ranges":[{"startOffset":2759,"endOffset":3453,"count":0}],"isBlockCoverage":false},{"functionName":"hasUncaughtExceptionCaptureCallback","ranges":[{"startOffset":3455,"endOffset":3556,"count":0}],"isBlockCoverage":false},{"functionName":"noop","ranges":[{"startOffset":3558,"endOffset":3576,"count":0}],"isBlockCoverage":false},{"functionName":"createOnGlobalUncaughtException","ranges":[{"startOffset":4119,"endOffset":6518,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":4388,"endOffset":6515,"count":0}],"isBlockCoverage":false},{"functionName":"readStdin","ranges":[{"startOffset":6520,"endOffset":6725,"count":0}],"isBlockCoverage":false}]},{"scriptId":"41","url":"internal/process/warning.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":4833,"count":1}],"isBlockCoverage":false},{"functionName":"lazyOption","ranges":[{"startOffset":277,"endOffset":831,"count":0}],"isBlockCoverage":false},{"functionName":"writeOut","ranges":[{"startOffset":932,"endOffset":1054,"count":0}],"isBlockCoverage":false},{"functionName":"writeToFile","ranges":[{"startOffset":1056,"endOffset":1440,"count":0}],"isBlockCoverage":false},{"functionName":"doEmitWarning","ranges":[{"startOffset":1442,"endOffset":1513,"count":0}],"isBlockCoverage":false},{"functionName":"onWarning","ranges":[{"startOffset":1552,"endOffset":2730,"count":0}],"isBlockCoverage":false},{"functionName":"emitWarning","ranges":[{"startOffset":2853,"endOffset":3997,"count":0}],"isBlockCoverage":false},{"functionName":"emitWarningSync","ranges":[{"startOffset":3999,"endOffset":4093,"count":0}],"isBlockCoverage":false},{"functionName":"createWarningObject","ranges":[{"startOffset":4095,"endOffset":4762,"count":0}],"isBlockCoverage":false}]},{"scriptId":"42","url":"internal/bootstrap/switches/is_main_thread.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":6397,"count":1}],"isBlockCoverage":true},{"functionName":"process._startProfilerIdleNotifier","ranges":[{"startOffset":513,"endOffset":521,"count":0}],"isBlockCoverage":false},{"functionName":"process._stopProfilerIdleNotifier","ranges":[{"startOffset":559,"endOffset":567,"count":0}],"isBlockCoverage":false},{"functionName":"defineStream","ranges":[{"startOffset":570,"endOffset":717,"count":3}],"isBlockCoverage":true},{"functionName":"createWritableStdioStream","ranges":[{"startOffset":1278,"endOffset":2845,"count":1},{"startOffset":1430,"endOffset":1556,"count":0},{"startOffset":1562,"endOffset":1748,"count":0},{"startOffset":2053,"endOffset":2081,"count":0},{"startOffset":2083,"endOffset":2303,"count":0},{"startOffset":2479,"endOffset":2724,"count":0}],"isBlockCoverage":true},{"functionName":"write","ranges":[{"startOffset":2667,"endOffset":2714,"count":0}],"isBlockCoverage":false},{"functionName":"dummyDestroy","ranges":[{"startOffset":2847,"endOffset":3230,"count":0}],"isBlockCoverage":false},{"functionName":"getStdout","ranges":[{"startOffset":3268,"endOffset":3599,"count":0}],"isBlockCoverage":false},{"functionName":"getStderr","ranges":[{"startOffset":3601,"endOffset":3932,"count":1},{"startOffset":3638,"endOffset":3652,"count":0},{"startOffset":3851,"endOffset":3913,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":3880,"endOffset":3907,"count":0}],"isBlockCoverage":false},{"functionName":"getStdin","ranges":[{"startOffset":3934,"endOffset":6253,"count":0}],"isBlockCoverage":false},{"functionName":"rawMethods.resetStdioForTesting","ranges":[{"startOffset":6316,"endOffset":6395,"count":0}],"isBlockCoverage":false}]},{"scriptId":"43","url":"internal/process/signal.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1124,"count":1}],"isBlockCoverage":false},{"functionName":"isSignal","ranges":[{"startOffset":205,"endOffset":301,"count":3}],"isBlockCoverage":true},{"functionName":"startListeningIfSignal","ranges":[{"startOffset":365,"endOffset":853,"count":3},{"startOffset":426,"endOffset":451,"count":0},{"startOffset":453,"endOffset":851,"count":0}],"isBlockCoverage":true},{"functionName":"stopListeningIfSignal","ranges":[{"startOffset":855,"endOffset":1050,"count":1},{"startOffset":957,"endOffset":993,"count":0},{"startOffset":995,"endOffset":1048,"count":0}],"isBlockCoverage":true}]},{"scriptId":"44","url":"internal/bootstrap/switches/does_own_process_state.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":3523,"count":1}],"isBlockCoverage":true},{"functionName":"wrapPosixCredentialSetters","ranges":[{"startOffset":817,"endOffset":2957,"count":1}],"isBlockCoverage":true},{"functionName":"initgroups","ranges":[{"startOffset":1278,"endOffset":1695,"count":0}],"isBlockCoverage":false},{"functionName":"setgroups","ranges":[{"startOffset":1699,"endOffset":2179,"count":0}],"isBlockCoverage":false},{"functionName":"wrapIdSetter","ranges":[{"startOffset":2183,"endOffset":2508,"count":4}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":2232,"endOffset":2503,"count":0}],"isBlockCoverage":false},{"functionName":"validateId","ranges":[{"startOffset":2512,"endOffset":2730,"count":0}],"isBlockCoverage":false},{"functionName":"wrappedChdir","ranges":[{"startOffset":3108,"endOffset":3279,"count":0}],"isBlockCoverage":false},{"functionName":"wrappedUmask","ranges":[{"startOffset":3281,"endOffset":3417,"count":0}],"isBlockCoverage":false},{"functionName":"wrappedCwd","ranges":[{"startOffset":3419,"endOffset":3522,"count":3},{"startOffset":3471,"endOffset":3500,"count":1}],"isBlockCoverage":true}]},{"scriptId":"45","url":"internal/main/run_main_module.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":632,"count":1}],"isBlockCoverage":true}]},{"scriptId":"46","url":"internal/bootstrap/pre_execution.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":15185,"count":1}],"isBlockCoverage":true},{"functionName":"prepareMainThreadExecution","ranges":[{"startOffset":410,"endOffset":2164,"count":1}],"isBlockCoverage":true},{"functionName":"patchProcessObject","ranges":[{"startOffset":2166,"endOffset":3743,"count":1},{"startOffset":2762,"endOffset":2770,"count":0}],"isBlockCoverage":true},{"functionName":"addReadOnlyProcessAlias","ranges":[{"startOffset":3745,"endOffset":4002,"count":13},{"startOffset":3866,"endOffset":4000,"count":1}],"isBlockCoverage":true},{"functionName":"setupWarningHandler","ranges":[{"startOffset":4004,"endOffset":4233,"count":1}],"isBlockCoverage":true},{"functionName":"setupCoverageHooks","ranges":[{"startOffset":4345,"endOffset":5022,"count":1},{"startOffset":4815,"endOffset":4992,"count":0}],"isBlockCoverage":true},{"functionName":"setupStacktracePrinterOnSigint","ranges":[{"startOffset":5024,"endOffset":5249,"count":1},{"startOffset":5126,"endOffset":5248,"count":0}],"isBlockCoverage":true},{"functionName":"initializeReport","ranges":[{"startOffset":5251,"endOffset":5475,"count":1}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":5433,"endOffset":5467,"count":0}],"isBlockCoverage":false},{"functionName":"setupDebugEnv","ranges":[{"startOffset":5477,"endOffset":5709,"count":1},{"startOffset":5628,"endOffset":5707,"count":0}],"isBlockCoverage":true},{"functionName":"initializeReportSignalHandlers","ranges":[{"startOffset":5771,"endOffset":5906,"count":1}],"isBlockCoverage":true},{"functionName":"initializeHeapSnapshotSignalHandlers","ranges":[{"startOffset":5908,"endOffset":6215,"count":1},{"startOffset":6043,"endOffset":6214,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":6175,"endOffset":6211,"count":0}],"isBlockCoverage":false},{"functionName":"setupTraceCategoryState","ranges":[{"startOffset":6217,"endOffset":6476,"count":1}],"isBlockCoverage":true},{"functionName":"setupInspectorHooks","ranges":[{"startOffset":6478,"endOffset":7059,"count":1}],"isBlockCoverage":true},{"functionName":"initializeDeprecations","ranges":[{"startOffset":7254,"endOffset":9775,"count":1},{"startOffset":7965,"endOffset":8267,"count":16},{"startOffset":8010,"endOffset":8242,"count":0},{"startOffset":8584,"endOffset":8757,"count":0},{"startOffset":8785,"endOffset":9179,"count":0}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":9428,"endOffset":9464,"count":9}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":9470,"endOffset":9512,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":9638,"endOffset":9673,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":9679,"endOffset":9720,"count":0}],"isBlockCoverage":false},{"functionName":"initializeAbortController","ranges":[{"startOffset":9777,"endOffset":10345,"count":1},{"startOffset":9915,"endOffset":10343,"count":0}],"isBlockCoverage":true},{"functionName":"setupChildProcessIpcChannel","ranges":[{"startOffset":10347,"endOffset":10912,"count":1},{"startOffset":10423,"endOffset":10910,"count":0}],"isBlockCoverage":true},{"functionName":"initializeClusterIPC","ranges":[{"startOffset":10914,"endOffset":11184,"count":1},{"startOffset":11001,"endOffset":11182,"count":0}],"isBlockCoverage":true},{"functionName":"initializePolicy","ranges":[{"startOffset":11186,"endOffset":13010,"count":1},{"startOffset":11312,"endOffset":13008,"count":0}],"isBlockCoverage":true},{"functionName":"initializeWASI","ranges":[{"startOffset":13012,"endOffset":13241,"count":1}],"isBlockCoverage":true},{"functionName":"initializeCJSLoader","ranges":[{"startOffset":13243,"endOffset":13529,"count":1}],"isBlockCoverage":true},{"functionName":"initializeESMLoader","ranges":[{"startOffset":13531,"endOffset":14199,"count":1},{"startOffset":13740,"endOffset":13747,"count":0}],"isBlockCoverage":true},{"functionName":"initializeFrozenIntrinsics","ranges":[{"startOffset":14201,"endOffset":14458,"count":1},{"startOffset":14286,"endOffset":14456,"count":0}],"isBlockCoverage":true},{"functionName":"loadPreloadModules","ranges":[{"startOffset":14460,"endOffset":14807,"count":1},{"startOffset":14654,"endOffset":14805,"count":0}],"isBlockCoverage":true}]},{"scriptId":"47","url":"internal/options.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":898,"count":1}],"isBlockCoverage":false},{"functionName":"getOptionValue","ranges":[{"startOffset":175,"endOffset":314,"count":48},{"startOffset":262,"endOffset":289,"count":0}],"isBlockCoverage":true},{"functionName":"getAllowUnauthorized","ranges":[{"startOffset":316,"endOffset":781,"count":0}],"isBlockCoverage":false}]},{"scriptId":"48","url":"internal/inspector_async_hook.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1972,"count":1}],"isBlockCoverage":false},{"functionName":"lazyHookCreation","ranges":[{"startOffset":75,"endOffset":1257,"count":0}],"isBlockCoverage":false},{"functionName":"enable","ranges":[{"startOffset":1259,"endOffset":1840,"count":0}],"isBlockCoverage":false},{"functionName":"disable","ranges":[{"startOffset":1842,"endOffset":1928,"count":0}],"isBlockCoverage":false}]},{"scriptId":"49","url":"internal/source_map/source_map_cache.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":8419,"count":1}],"isBlockCoverage":false},{"functionName":"ObjectGetValueSafe","ranges":[{"startOffset":305,"endOffset":483,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":665,"endOffset":690,"count":0}],"isBlockCoverage":false},{"functionName":"getSourceMapsEnabled","ranges":[{"startOffset":1236,"endOffset":1740,"count":2},{"startOffset":1309,"endOffset":1710,"count":1},{"startOffset":1402,"endOffset":1706,"count":0}],"isBlockCoverage":true},{"functionName":"maybeCacheSourceMap","ranges":[{"startOffset":1742,"endOffset":2852,"count":2},{"startOffset":1900,"endOffset":1920,"count":0},{"startOffset":1923,"endOffset":1930,"count":0},{"startOffset":1990,"endOffset":2128,"count":0},{"startOffset":2232,"endOffset":2850,"count":0}],"isBlockCoverage":true},{"functionName":"dataFromUrl","ranges":[{"startOffset":2854,"endOffset":3380,"count":0}],"isBlockCoverage":false},{"functionName":"lineLengths","ranges":[{"startOffset":3570,"endOffset":3878,"count":0}],"isBlockCoverage":false},{"functionName":"sourceMapFromFile","ranges":[{"startOffset":3880,"endOffset":4136,"count":0}],"isBlockCoverage":false},{"functionName":"sourceMapFromDataUrl","ranges":[{"startOffset":4230,"endOffset":4867,"count":0}],"isBlockCoverage":false},{"functionName":"sourcesToAbsolute","ranges":[{"startOffset":5052,"endOffset":5379,"count":0}],"isBlockCoverage":false},{"functionName":"rekeySourceMap","ranges":[{"startOffset":5448,"endOffset":5643,"count":0}],"isBlockCoverage":false},{"functionName":"sourceMapCacheToObject","ranges":[{"startOffset":6081,"endOffset":6450,"count":0}],"isBlockCoverage":false},{"functionName":"appendCJSCache","ranges":[{"startOffset":6689,"endOffset":7344,"count":0}],"isBlockCoverage":false},{"functionName":"findSourceMap","ranges":[{"startOffset":7562,"endOffset":8287,"count":0}],"isBlockCoverage":false}]},{"scriptId":"50","url":"fs.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":59762,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":3694,"endOffset":3854,"count":0}],"isBlockCoverage":false},{"functionName":"showTruncateDeprecation","ranges":[{"startOffset":4089,"endOffset":4376,"count":0}],"isBlockCoverage":false},{"functionName":"maybeCallback","ranges":[{"startOffset":4378,"endOffset":4494,"count":0}],"isBlockCoverage":false},{"functionName":"makeCallback","ranges":[{"startOffset":4697,"endOffset":4840,"count":0}],"isBlockCoverage":false},{"functionName":"makeStatsCallback","ranges":[{"startOffset":5021,"endOffset":5236,"count":0}],"isBlockCoverage":false},{"functionName":"isFileType","ranges":[{"startOffset":5262,"endOffset":5522,"count":23},{"startOffset":5461,"endOffset":5481,"count":13}],"isBlockCoverage":true},{"functionName":"access","ranges":[{"startOffset":5524,"endOffset":5882,"count":0}],"isBlockCoverage":false},{"functionName":"accessSync","ranges":[{"startOffset":5884,"endOffset":6122,"count":0}],"isBlockCoverage":false},{"functionName":"exists","ranges":[{"startOffset":6124,"endOffset":6362,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":6435,"endOffset":6513,"count":0}],"isBlockCoverage":false},{"functionName":"existsSync","ranges":[{"startOffset":6930,"endOffset":7460,"count":0}],"isBlockCoverage":false},{"functionName":"readFileAfterOpen","ranges":[{"startOffset":7462,"endOffset":7742,"count":0}],"isBlockCoverage":false},{"functionName":"readFileAfterStat","ranges":[{"startOffset":7744,"endOffset":8245,"count":0}],"isBlockCoverage":false},{"functionName":"readFile","ranges":[{"startOffset":8247,"endOffset":9263,"count":0}],"isBlockCoverage":false},{"functionName":"tryStatSync","ranges":[{"startOffset":9265,"endOffset":9495,"count":0}],"isBlockCoverage":false},{"functionName":"tryCreateBuffer","ranges":[{"startOffset":9497,"endOffset":9808,"count":0}],"isBlockCoverage":false},{"functionName":"tryReadSync","ranges":[{"startOffset":9810,"endOffset":10065,"count":0}],"isBlockCoverage":false},{"functionName":"readFileSync","ranges":[{"startOffset":10067,"endOffset":11450,"count":0}],"isBlockCoverage":false},{"functionName":"defaultCloseCallback","ranges":[{"startOffset":11452,"endOffset":11520,"count":0}],"isBlockCoverage":false},{"functionName":"close","ranges":[{"startOffset":11522,"endOffset":11778,"count":0}],"isBlockCoverage":false},{"functionName":"closeSync","ranges":[{"startOffset":11780,"endOffset":11923,"count":0}],"isBlockCoverage":false},{"functionName":"open","ranges":[{"startOffset":11925,"endOffset":12502,"count":0}],"isBlockCoverage":false},{"functionName":"openSync","ranges":[{"startOffset":12505,"endOffset":12901,"count":0}],"isBlockCoverage":false},{"functionName":"read","ranges":[{"startOffset":13008,"endOffset":14506,"count":0}],"isBlockCoverage":false},{"functionName":"readSync","ranges":[{"startOffset":14775,"endOffset":15690,"count":0}],"isBlockCoverage":false},{"functionName":"readv","ranges":[{"startOffset":15692,"endOffset":16122,"count":0}],"isBlockCoverage":false},{"functionName":"readvSync","ranges":[{"startOffset":16265,"endOffset":16575,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":16721,"endOffset":17989,"count":0}],"isBlockCoverage":false},{"functionName":"writeSync","ranges":[{"startOffset":18266,"endOffset":19132,"count":0}],"isBlockCoverage":false},{"functionName":"writev","ranges":[{"startOffset":19193,"endOffset":19621,"count":0}],"isBlockCoverage":false},{"functionName":"writevSync","ranges":[{"startOffset":19750,"endOffset":20063,"count":0}],"isBlockCoverage":false},{"functionName":"rename","ranges":[{"startOffset":20065,"endOffset":20446,"count":0}],"isBlockCoverage":false},{"functionName":"renameSync","ranges":[{"startOffset":20448,"endOffset":20795,"count":0}],"isBlockCoverage":false},{"functionName":"truncate","ranges":[{"startOffset":20797,"endOffset":21415,"count":0}],"isBlockCoverage":false},{"functionName":"truncateSync","ranges":[{"startOffset":21417,"endOffset":21820,"count":0}],"isBlockCoverage":false},{"functionName":"ftruncate","ranges":[{"startOffset":21822,"endOffset":22162,"count":0}],"isBlockCoverage":false},{"functionName":"ftruncateSync","ranges":[{"startOffset":22164,"endOffset":22384,"count":0}],"isBlockCoverage":false},{"functionName":"lazyLoadRimraf","ranges":[{"startOffset":22387,"endOffset":22506,"count":0}],"isBlockCoverage":false},{"functionName":"rmdir","ranges":[{"startOffset":22508,"endOffset":23148,"count":0}],"isBlockCoverage":false},{"functionName":"rmdirSync","ranges":[{"startOffset":23150,"endOffset":23588,"count":0}],"isBlockCoverage":false},{"functionName":"rm","ranges":[{"startOffset":23590,"endOffset":23928,"count":0}],"isBlockCoverage":false},{"functionName":"rmSync","ranges":[{"startOffset":23930,"endOffset":24100,"count":0}],"isBlockCoverage":false},{"functionName":"fdatasync","ranges":[{"startOffset":24102,"endOffset":24276,"count":0}],"isBlockCoverage":false},{"functionName":"fdatasyncSync","ranges":[{"startOffset":24278,"endOffset":24428,"count":0}],"isBlockCoverage":false},{"functionName":"fsync","ranges":[{"startOffset":24430,"endOffset":24596,"count":0}],"isBlockCoverage":false},{"functionName":"fsyncSync","ranges":[{"startOffset":24598,"endOffset":24740,"count":0}],"isBlockCoverage":false},{"functionName":"mkdir","ranges":[{"startOffset":24742,"endOffset":25523,"count":0}],"isBlockCoverage":false},{"functionName":"mkdirSync","ranges":[{"startOffset":25525,"endOffset":26297,"count":0}],"isBlockCoverage":false},{"functionName":"readdir","ranges":[{"startOffset":26299,"endOffset":26880,"count":0}],"isBlockCoverage":false},{"functionName":"readdirSync","ranges":[{"startOffset":26882,"endOffset":27308,"count":0}],"isBlockCoverage":false},{"functionName":"fstat","ranges":[{"startOffset":27310,"endOffset":27649,"count":0}],"isBlockCoverage":false},{"functionName":"lstat","ranges":[{"startOffset":27651,"endOffset":28026,"count":0}],"isBlockCoverage":false},{"functionName":"stat","ranges":[{"startOffset":28028,"endOffset":28401,"count":0}],"isBlockCoverage":false},{"functionName":"hasNoEntryError","ranges":[{"startOffset":28403,"endOffset":28625,"count":0}],"isBlockCoverage":false},{"functionName":"fstatSync","ranges":[{"startOffset":28627,"endOffset":28891,"count":0}],"isBlockCoverage":false},{"functionName":"lstatSync","ranges":[{"startOffset":28893,"endOffset":29317,"count":0}],"isBlockCoverage":false},{"functionName":"statSync","ranges":[{"startOffset":29319,"endOffset":29740,"count":2},{"startOffset":29618,"endOffset":29641,"count":0},{"startOffset":29643,"endOffset":29670,"count":0}],"isBlockCoverage":true},{"functionName":"readlink","ranges":[{"startOffset":29742,"endOffset":30090,"count":0}],"isBlockCoverage":false},{"functionName":"readlinkSync","ranges":[{"startOffset":30092,"endOffset":30423,"count":0}],"isBlockCoverage":false},{"functionName":"symlink","ranges":[{"startOffset":30425,"endOffset":32035,"count":0}],"isBlockCoverage":false},{"functionName":"symlinkSync","ranges":[{"startOffset":32037,"endOffset":32694,"count":0}],"isBlockCoverage":false},{"functionName":"link","ranges":[{"startOffset":32696,"endOffset":33097,"count":0}],"isBlockCoverage":false},{"functionName":"linkSync","ranges":[{"startOffset":33099,"endOffset":33548,"count":0}],"isBlockCoverage":false},{"functionName":"unlink","ranges":[{"startOffset":33550,"endOffset":33777,"count":0}],"isBlockCoverage":false},{"functionName":"unlinkSync","ranges":[{"startOffset":33779,"endOffset":33965,"count":0}],"isBlockCoverage":false},{"functionName":"fchmod","ranges":[{"startOffset":33967,"endOffset":34209,"count":0}],"isBlockCoverage":false},{"functionName":"fchmodSync","ranges":[{"startOffset":34211,"endOffset":34405,"count":0}],"isBlockCoverage":false},{"functionName":"lchmod","ranges":[{"startOffset":34407,"endOffset":34844,"count":0}],"isBlockCoverage":false},{"functionName":"lchmodSync","ranges":[{"startOffset":34846,"endOffset":35168,"count":0}],"isBlockCoverage":false},{"functionName":"chmod","ranges":[{"startOffset":35171,"endOffset":35447,"count":0}],"isBlockCoverage":false},{"functionName":"chmodSync","ranges":[{"startOffset":35449,"endOffset":35684,"count":0}],"isBlockCoverage":false},{"functionName":"lchown","ranges":[{"startOffset":35686,"endOffset":36027,"count":0}],"isBlockCoverage":false},{"functionName":"lchownSync","ranges":[{"startOffset":36029,"endOffset":36329,"count":0}],"isBlockCoverage":false},{"functionName":"fchown","ranges":[{"startOffset":36331,"endOffset":36637,"count":0}],"isBlockCoverage":false},{"functionName":"fchownSync","ranges":[{"startOffset":36639,"endOffset":36898,"count":0}],"isBlockCoverage":false},{"functionName":"chown","ranges":[{"startOffset":36900,"endOffset":37240,"count":0}],"isBlockCoverage":false},{"functionName":"chownSync","ranges":[{"startOffset":37242,"endOffset":37540,"count":0}],"isBlockCoverage":false},{"functionName":"utimes","ranges":[{"startOffset":37542,"endOffset":37883,"count":0}],"isBlockCoverage":false},{"functionName":"utimesSync","ranges":[{"startOffset":37885,"endOffset":38167,"count":0}],"isBlockCoverage":false},{"functionName":"futimes","ranges":[{"startOffset":38169,"endOffset":38477,"count":0}],"isBlockCoverage":false},{"functionName":"futimesSync","ranges":[{"startOffset":38479,"endOffset":38739,"count":0}],"isBlockCoverage":false},{"functionName":"lutimes","ranges":[{"startOffset":38741,"endOffset":39087,"count":0}],"isBlockCoverage":false},{"functionName":"lutimesSync","ranges":[{"startOffset":39089,"endOffset":39393,"count":0}],"isBlockCoverage":false},{"functionName":"writeAll","ranges":[{"startOffset":39395,"endOffset":40339,"count":0}],"isBlockCoverage":false},{"functionName":"writeFile","ranges":[{"startOffset":40341,"endOffset":41280,"count":0}],"isBlockCoverage":false},{"functionName":"writeFileSync","ranges":[{"startOffset":41282,"endOffset":41992,"count":0}],"isBlockCoverage":false},{"functionName":"appendFile","ranges":[{"startOffset":41994,"endOffset":42434,"count":0}],"isBlockCoverage":false},{"functionName":"appendFileSync","ranges":[{"startOffset":42436,"endOffset":42815,"count":0}],"isBlockCoverage":false},{"functionName":"watch","ranges":[{"startOffset":42817,"endOffset":44050,"count":0}],"isBlockCoverage":false},{"functionName":"watchFile","ranges":[{"startOffset":44086,"endOffset":45199,"count":0}],"isBlockCoverage":false},{"functionName":"unwatchFile","ranges":[{"startOffset":45201,"endOffset":45889,"count":0}],"isBlockCoverage":false},{"functionName":"splitRoot","ranges":[{"startOffset":46105,"endOffset":46171,"count":0}],"isBlockCoverage":false},{"functionName":"splitRoot","ranges":[{"startOffset":46196,"endOffset":46374,"count":3},{"startOffset":46263,"endOffset":46354,"count":6},{"startOffset":46325,"endOffset":46348,"count":3},{"startOffset":46354,"endOffset":46373,"count":0}],"isBlockCoverage":true},{"functionName":"encodeRealpathResult","ranges":[{"startOffset":46379,"endOffset":46666,"count":3},{"startOffset":46464,"endOffset":46494,"count":0},{"startOffset":46514,"endOffset":46665,"count":0}],"isBlockCoverage":true},{"functionName":"nextPart","ranges":[{"startOffset":46789,"endOffset":47032,"count":0}],"isBlockCoverage":false},{"functionName":"nextPart","ranges":[{"startOffset":47056,"endOffset":47109,"count":18}],"isBlockCoverage":true},{"functionName":"realpathSync","ranges":[{"startOffset":47151,"endOffset":51017,"count":4},{"startOffset":47285,"endOffset":47303,"count":0},{"startOffset":47472,"endOffset":47507,"count":1},{"startOffset":47507,"endOffset":48079,"count":3},{"startOffset":48079,"endOffset":48254,"count":0},{"startOffset":48254,"endOffset":48391,"count":3},{"startOffset":48391,"endOffset":50934,"count":18},{"startOffset":48503,"endOffset":48618,"count":3},{"startOffset":48618,"endOffset":48744,"count":15},{"startOffset":48867,"endOffset":49004,"count":5},{"startOffset":48958,"endOffset":48982,"count":0},{"startOffset":49004,"endOffset":49084,"count":13},{"startOffset":49115,"endOffset":49164,"count":0},{"startOffset":49164,"endOffset":50448,"count":13},{"startOffset":49617,"endOffset":50442,"count":0},{"startOffset":50448,"endOffset":50743,"count":0},{"startOffset":50745,"endOffset":50930,"count":0},{"startOffset":50934,"endOffset":51016,"count":3}],"isBlockCoverage":true},{"functionName":"realpathSync.native","ranges":[{"startOffset":51042,"endOffset":51281,"count":0}],"isBlockCoverage":false},{"functionName":"realpath","ranges":[{"startOffset":51285,"endOffset":54720,"count":0}],"isBlockCoverage":false},{"functionName":"realpath.native","ranges":[{"startOffset":54741,"endOffset":55011,"count":0}],"isBlockCoverage":false},{"functionName":"mkdtemp","ranges":[{"startOffset":55014,"endOffset":55485,"count":0}],"isBlockCoverage":false},{"functionName":"mkdtempSync","ranges":[{"startOffset":55488,"endOffset":55962,"count":0}],"isBlockCoverage":false},{"functionName":"copyFile","ranges":[{"startOffset":55965,"endOffset":56519,"count":0}],"isBlockCoverage":false},{"functionName":"copyFileSync","ranges":[{"startOffset":56522,"endOffset":56894,"count":0}],"isBlockCoverage":false},{"functionName":"lazyLoadStreams","ranges":[{"startOffset":56896,"endOffset":57090,"count":8},{"startOffset":56944,"endOffset":57088,"count":1}],"isBlockCoverage":true},{"functionName":"createReadStream","ranges":[{"startOffset":57092,"endOffset":57197,"count":0}],"isBlockCoverage":false},{"functionName":"createWriteStream","ranges":[{"startOffset":57199,"endOffset":57306,"count":0}],"isBlockCoverage":false},{"functionName":"get ReadStream","ranges":[{"startOffset":58529,"endOffset":58597,"count":2}],"isBlockCoverage":true},{"functionName":"set ReadStream","ranges":[{"startOffset":58602,"endOffset":58649,"count":0}],"isBlockCoverage":false},{"functionName":"get WriteStream","ranges":[{"startOffset":58654,"endOffset":58724,"count":2}],"isBlockCoverage":true},{"functionName":"set WriteStream","ranges":[{"startOffset":58729,"endOffset":58778,"count":0}],"isBlockCoverage":false},{"functionName":"get FileReadStream","ranges":[{"startOffset":58916,"endOffset":58992,"count":2}],"isBlockCoverage":true},{"functionName":"set FileReadStream","ranges":[{"startOffset":58997,"endOffset":59052,"count":0}],"isBlockCoverage":false},{"functionName":"get FileWriteStream","ranges":[{"startOffset":59057,"endOffset":59135,"count":2}],"isBlockCoverage":true},{"functionName":"set FileWriteStream","ranges":[{"startOffset":59140,"endOffset":59197,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":59628,"endOffset":59753,"count":3},{"startOffset":59673,"endOffset":59724,"count":1}],"isBlockCoverage":true}]},{"scriptId":"51","url":"internal/fs/utils.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":21731,"count":1}],"isBlockCoverage":false},{"functionName":"lazyLoadFs","ranges":[{"startOffset":2496,"endOffset":2575,"count":0}],"isBlockCoverage":false},{"functionName":"assertEncoding","ranges":[{"startOffset":2577,"endOffset":2724,"count":19},{"startOffset":2628,"endOffset":2659,"count":15},{"startOffset":2661,"endOffset":2722,"count":0}],"isBlockCoverage":true},{"functionName":"Dirent","ranges":[{"startOffset":2743,"endOffset":2818,"count":0}],"isBlockCoverage":false},{"functionName":"isDirectory","ranges":[{"startOffset":2822,"endOffset":2883,"count":0}],"isBlockCoverage":false},{"functionName":"isFile","ranges":[{"startOffset":2887,"endOffset":2944,"count":0}],"isBlockCoverage":false},{"functionName":"isBlockDevice","ranges":[{"startOffset":2948,"endOffset":3013,"count":0}],"isBlockCoverage":false},{"functionName":"isCharacterDevice","ranges":[{"startOffset":3017,"endOffset":3085,"count":0}],"isBlockCoverage":false},{"functionName":"isSymbolicLink","ranges":[{"startOffset":3089,"endOffset":3154,"count":0}],"isBlockCoverage":false},{"functionName":"isFIFO","ranges":[{"startOffset":3158,"endOffset":3215,"count":0}],"isBlockCoverage":false},{"functionName":"isSocket","ranges":[{"startOffset":3219,"endOffset":3280,"count":0}],"isBlockCoverage":false},{"functionName":"DirentFromStats","ranges":[{"startOffset":3325,"endOffset":3404,"count":0}],"isBlockCoverage":false},{"functionName":"DirentFromStats.","ranges":[{"startOffset":3549,"endOffset":3598,"count":0}],"isBlockCoverage":false},{"functionName":"copyObject","ranges":[{"startOffset":3603,"endOffset":3731,"count":0}],"isBlockCoverage":false},{"functionName":"join","ranges":[{"startOffset":3781,"endOffset":4388,"count":0}],"isBlockCoverage":false},{"functionName":"getDirents","ranges":[{"startOffset":4390,"endOffset":5485,"count":0}],"isBlockCoverage":false},{"functionName":"getDirent","ranges":[{"startOffset":5487,"endOffset":6209,"count":0}],"isBlockCoverage":false},{"functionName":"getOptions","ranges":[{"startOffset":6211,"endOffset":6853,"count":22},{"startOffset":6306,"endOffset":6344,"count":19},{"startOffset":6346,"endOffset":6378,"count":3},{"startOffset":6378,"endOffset":6415,"count":19},{"startOffset":6415,"endOffset":6533,"count":15},{"startOffset":6533,"endOffset":6655,"count":4},{"startOffset":6572,"endOffset":6655,"count":0},{"startOffset":6655,"endOffset":6769,"count":19},{"startOffset":6769,"endOffset":6833,"count":0},{"startOffset":6833,"endOffset":6852,"count":19}],"isBlockCoverage":true},{"functionName":"handleErrorFromBinding","ranges":[{"startOffset":6855,"endOffset":7384,"count":15},{"startOffset":6925,"endOffset":7060,"count":0},{"startOffset":7092,"endOffset":7382,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":7525,"endOffset":8078,"count":24},{"startOffset":7755,"endOffset":7775,"count":0},{"startOffset":7829,"endOffset":7877,"count":0},{"startOffset":7896,"endOffset":8077,"count":0}],"isBlockCoverage":true},{"functionName":"preprocessSymlinkDestination","ranges":[{"startOffset":8082,"endOffset":8766,"count":0}],"isBlockCoverage":false},{"functionName":"StatsBase","ranges":[{"startOffset":8799,"endOffset":9106,"count":2}],"isBlockCoverage":true},{"functionName":"StatsBase.isDirectory","ranges":[{"startOffset":9142,"endOffset":9199,"count":2}],"isBlockCoverage":true},{"functionName":"StatsBase.isFile","ranges":[{"startOffset":9231,"endOffset":9288,"count":2}],"isBlockCoverage":true},{"functionName":"StatsBase.isBlockDevice","ranges":[{"startOffset":9327,"endOffset":9384,"count":0}],"isBlockCoverage":false},{"functionName":"StatsBase.isCharacterDevice","ranges":[{"startOffset":9427,"endOffset":9484,"count":0}],"isBlockCoverage":false},{"functionName":"StatsBase.isSymbolicLink","ranges":[{"startOffset":9524,"endOffset":9581,"count":0}],"isBlockCoverage":false},{"functionName":"StatsBase.isFIFO","ranges":[{"startOffset":9613,"endOffset":9670,"count":0}],"isBlockCoverage":false},{"functionName":"StatsBase.isSocket","ranges":[{"startOffset":9704,"endOffset":9762,"count":0}],"isBlockCoverage":false},{"functionName":"msFromTimeSpec","ranges":[{"startOffset":9887,"endOffset":9969,"count":8}],"isBlockCoverage":true},{"functionName":"nsFromTimeSpecBigInt","ranges":[{"startOffset":9971,"endOffset":10054,"count":0}],"isBlockCoverage":false},{"functionName":"dateFromMs","ranges":[{"startOffset":10403,"endOffset":10467,"count":8}],"isBlockCoverage":true},{"functionName":"BigIntStats","ranges":[{"startOffset":10469,"endOffset":11205,"count":0}],"isBlockCoverage":false},{"functionName":"BigIntStats._checkModeProperty","ranges":[{"startOffset":11363,"endOffset":11609,"count":0}],"isBlockCoverage":false},{"functionName":"Stats","ranges":[{"startOffset":11612,"endOffset":12129,"count":2}],"isBlockCoverage":true},{"functionName":"Stats._checkModeProperty","ranges":[{"startOffset":12470,"endOffset":12700,"count":4},{"startOffset":12507,"endOffset":12585,"count":0},{"startOffset":12587,"endOffset":12654,"count":0}],"isBlockCoverage":true},{"functionName":"getStatsFromBinding","ranges":[{"startOffset":12703,"endOffset":13781,"count":2},{"startOffset":12784,"endOffset":13309,"count":0}],"isBlockCoverage":true},{"functionName":"stringToFlags","ranges":[{"startOffset":13783,"endOffset":14991,"count":17},{"startOffset":13848,"endOffset":13871,"count":0},{"startOffset":13894,"endOffset":13920,"count":0},{"startOffset":13977,"endOffset":13988,"count":0},{"startOffset":14010,"endOffset":14047,"count":0},{"startOffset":14052,"endOffset":14078,"count":0},{"startOffset":14083,"endOffset":14095,"count":0},{"startOffset":14117,"endOffset":14153,"count":0},{"startOffset":14159,"endOffset":14206,"count":0},{"startOffset":14211,"endOffset":14222,"count":0},{"startOffset":14244,"endOffset":14301,"count":0},{"startOffset":14307,"endOffset":14353,"count":0},{"startOffset":14358,"endOffset":14369,"count":0},{"startOffset":14391,"endOffset":14446,"count":0},{"startOffset":14452,"endOffset":14500,"count":0},{"startOffset":14505,"endOffset":14516,"count":0},{"startOffset":14538,"endOffset":14596,"count":0},{"startOffset":14601,"endOffset":14612,"count":0},{"startOffset":14634,"endOffset":14692,"count":0},{"startOffset":14698,"endOffset":14745,"count":0},{"startOffset":14750,"endOffset":14761,"count":0},{"startOffset":14783,"endOffset":14839,"count":0},{"startOffset":14844,"endOffset":14855,"count":0},{"startOffset":14877,"endOffset":14933,"count":0},{"startOffset":14937,"endOffset":14990,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":15037,"endOffset":15397,"count":0}],"isBlockCoverage":false},{"functionName":"toUnixTimestamp","ranges":[{"startOffset":15459,"endOffset":15902,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":15956,"endOffset":16335,"count":76},{"startOffset":16012,"endOffset":16079,"count":0},{"startOffset":16100,"endOffset":16167,"count":0},{"startOffset":16208,"endOffset":16331,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":16393,"endOffset":16667,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":16709,"endOffset":16979,"count":24},{"startOffset":16771,"endOffset":16793,"count":0},{"startOffset":16795,"endOffset":16881,"count":0},{"startOffset":16957,"endOffset":16977,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":17024,"endOffset":17159,"count":20}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":17207,"endOffset":17538,"count":0}],"isBlockCoverage":false},{"functionName":"warnOnNonPortableTemplate","ranges":[{"startOffset":17579,"endOffset":18037,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":18273,"endOffset":18893,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":18943,"endOffset":19525,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":19577,"endOffset":20059,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":20100,"endOffset":20678,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":20741,"endOffset":21113,"count":0}],"isBlockCoverage":false}]},{"scriptId":"52","url":"internal/fs/dir.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":6714,"count":1}],"isBlockCoverage":false},{"functionName":"Dir","ranges":[{"startOffset":1109,"endOffset":1881,"count":0}],"isBlockCoverage":false},{"functionName":"get path","ranges":[{"startOffset":1885,"endOffset":1928,"count":0}],"isBlockCoverage":false},{"functionName":"read","ranges":[{"startOffset":1932,"endOffset":1999,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":2003,"endOffset":3375,"count":0}],"isBlockCoverage":false},{"functionName":"readSync","ranges":[{"startOffset":3379,"endOffset":4135,"count":0}],"isBlockCoverage":false},{"functionName":"close","ranges":[{"startOffset":4139,"endOffset":4865,"count":0}],"isBlockCoverage":false},{"functionName":"closeSync","ranges":[{"startOffset":4869,"endOffset":5246,"count":0}],"isBlockCoverage":false},{"functionName":"entries","ranges":[{"startOffset":5250,"endOffset":5513,"count":0}],"isBlockCoverage":false},{"functionName":"opendir","ranges":[{"startOffset":5674,"endOffset":6299,"count":0}],"isBlockCoverage":false},{"functionName":"opendirSync","ranges":[{"startOffset":6301,"endOffset":6658,"count":0}],"isBlockCoverage":false}]},{"scriptId":"53","url":"internal/modules/cjs/helpers.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":5427,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":681,"endOffset":706,"count":1}],"isBlockCoverage":true},{"functionName":"loadNativeModule","ranges":[{"startOffset":860,"endOffset":1066,"count":1}],"isBlockCoverage":true},{"functionName":"makeRequireFunction","ranges":[{"startOffset":1315,"endOffset":3313,"count":0}],"isBlockCoverage":false},{"functionName":"stripBOM","ranges":[{"startOffset":3498,"endOffset":3624,"count":0}],"isBlockCoverage":false},{"functionName":"addBuiltinLibsToObject","ranges":[{"startOffset":3626,"endOffset":5091,"count":0}],"isBlockCoverage":false},{"functionName":"normalizeReferrerURL","ranges":[{"startOffset":5093,"endOffset":5281,"count":2},{"startOffset":5200,"endOffset":5246,"count":0}],"isBlockCoverage":true}]},{"scriptId":"54","url":"url.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":30124,"count":1}],"isBlockCoverage":false},{"functionName":"Url","ranges":[{"startOffset":1879,"endOffset":2155,"count":0}],"isBlockCoverage":false},{"functionName":"urlParse","ranges":[{"startOffset":3833,"endOffset":4047,"count":0}],"isBlockCoverage":false},{"functionName":"isIpv6Hostname","ranges":[{"startOffset":4049,"endOffset":4272,"count":0}],"isBlockCoverage":false},{"functionName":"parse","ranges":[{"startOffset":4296,"endOffset":13617,"count":0}],"isBlockCoverage":false},{"functionName":"getHostname","ranges":[{"startOffset":13620,"endOffset":14345,"count":0}],"isBlockCoverage":false},{"functionName":"autoEscapeStr","ranges":[{"startOffset":15401,"endOffset":16107,"count":0}],"isBlockCoverage":false},{"functionName":"urlFormat","ranges":[{"startOffset":16153,"endOffset":16863,"count":0}],"isBlockCoverage":false},{"functionName":"format","ranges":[{"startOffset":17570,"endOffset":20020,"count":0}],"isBlockCoverage":false},{"functionName":"urlResolve","ranges":[{"startOffset":20023,"endOffset":20122,"count":0}],"isBlockCoverage":false},{"functionName":"resolve","ranges":[{"startOffset":20148,"endOffset":20249,"count":0}],"isBlockCoverage":false},{"functionName":"urlResolveObject","ranges":[{"startOffset":20252,"endOffset":20395,"count":0}],"isBlockCoverage":false},{"functionName":"resolveObject","ranges":[{"startOffset":20427,"endOffset":29546,"count":0}],"isBlockCoverage":false},{"functionName":"parseHost","ranges":[{"startOffset":29575,"endOffset":29848,"count":0}],"isBlockCoverage":false}]},{"scriptId":"55","url":"internal/idna.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":264,"count":1}],"isBlockCoverage":false}]},{"scriptId":"56","url":"internal/process/report.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2945,"count":1}],"isBlockCoverage":false},{"functionName":"writeReport","ranges":[{"startOffset":298,"endOffset":805,"count":0}],"isBlockCoverage":false},{"functionName":"getReport","ranges":[{"startOffset":809,"endOffset":1045,"count":0}],"isBlockCoverage":false},{"functionName":"get directory","ranges":[{"startOffset":1049,"endOffset":1100,"count":0}],"isBlockCoverage":false},{"functionName":"set directory","ranges":[{"startOffset":1104,"endOffset":1192,"count":0}],"isBlockCoverage":false},{"functionName":"get filename","ranges":[{"startOffset":1196,"endOffset":1245,"count":0}],"isBlockCoverage":false},{"functionName":"set filename","ranges":[{"startOffset":1249,"endOffset":1337,"count":0}],"isBlockCoverage":false},{"functionName":"get compact","ranges":[{"startOffset":1341,"endOffset":1388,"count":0}],"isBlockCoverage":false},{"functionName":"set compact","ranges":[{"startOffset":1392,"endOffset":1469,"count":0}],"isBlockCoverage":false},{"functionName":"get signal","ranges":[{"startOffset":1473,"endOffset":1518,"count":0}],"isBlockCoverage":false},{"functionName":"set signal","ranges":[{"startOffset":1522,"endOffset":1659,"count":0}],"isBlockCoverage":false},{"functionName":"get reportOnFatalError","ranges":[{"startOffset":1663,"endOffset":1735,"count":0}],"isBlockCoverage":false},{"functionName":"set reportOnFatalError","ranges":[{"startOffset":1739,"endOffset":1923,"count":0}],"isBlockCoverage":false},{"functionName":"get reportOnSignal","ranges":[{"startOffset":1927,"endOffset":1991,"count":0}],"isBlockCoverage":false},{"functionName":"set reportOnSignal","ranges":[{"startOffset":1995,"endOffset":2222,"count":0}],"isBlockCoverage":false},{"functionName":"get reportOnUncaughtException","ranges":[{"startOffset":2226,"endOffset":2312,"count":0}],"isBlockCoverage":false},{"functionName":"set reportOnUncaughtException","ranges":[{"startOffset":2316,"endOffset":2514,"count":0}],"isBlockCoverage":false},{"functionName":"addSignalHandler","ranges":[{"startOffset":2519,"endOffset":2690,"count":1},{"startOffset":2585,"endOffset":2688,"count":0}],"isBlockCoverage":true},{"functionName":"removeSignalHandler","ranges":[{"startOffset":2692,"endOffset":2816,"count":0}],"isBlockCoverage":false},{"functionName":"signalHandler","ranges":[{"startOffset":2818,"endOffset":2892,"count":0}],"isBlockCoverage":false}]},{"scriptId":"57","url":"internal/modules/cjs/loader.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":38134,"count":1}],"isBlockCoverage":false},{"functionName":"get hasLoadedAnyUserCJSModule","ranges":[{"startOffset":1880,"endOffset":1949,"count":1}],"isBlockCoverage":true},{"functionName":"stat","ranges":[{"startOffset":4257,"endOffset":4574,"count":1},{"startOffset":4355,"endOffset":4449,"count":0},{"startOffset":4523,"endOffset":4555,"count":0}],"isBlockCoverage":true},{"functionName":"updateChildren","ranges":[{"startOffset":4576,"endOffset":4751,"count":0}],"isBlockCoverage":false},{"functionName":"Module","ranges":[{"startOffset":4753,"endOffset":4990,"count":0}],"isBlockCoverage":false},{"functionName":"wrap","ranges":[{"startOffset":5441,"endOffset":5518,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":5663,"endOffset":5785,"count":0}],"isBlockCoverage":false},{"functionName":"defineProperty","ranges":[{"startOffset":5790,"endOffset":5923,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":5970,"endOffset":5998,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":6003,"endOffset":6057,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":6107,"endOffset":6143,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":6148,"endOffset":6210,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":6275,"endOffset":6305,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":6376,"endOffset":6401,"count":0}],"isBlockCoverage":false},{"functionName":"readPackage","ranges":[{"startOffset":6749,"endOffset":7559,"count":1},{"startOffset":6929,"endOffset":6945,"count":0},{"startOffset":7043,"endOffset":7049,"count":0},{"startOffset":7093,"endOffset":7159,"count":0},{"startOffset":7442,"endOffset":7557,"count":0}],"isBlockCoverage":true},{"functionName":"readPackageScope","ranges":[{"startOffset":7561,"endOffset":8104,"count":1},{"startOffset":7903,"endOffset":7916,"count":0},{"startOffset":8041,"endOffset":8103,"count":0}],"isBlockCoverage":true},{"functionName":"tryPackage","ranges":[{"startOffset":8106,"endOffset":9407,"count":0}],"isBlockCoverage":false},{"functionName":"tryFile","ranges":[{"startOffset":9748,"endOffset":9958,"count":0}],"isBlockCoverage":false},{"functionName":"toRealPath","ranges":[{"startOffset":9960,"endOffset":10091,"count":2}],"isBlockCoverage":true},{"functionName":"tryExtensions","ranges":[{"startOffset":10166,"endOffset":10372,"count":0}],"isBlockCoverage":false},{"functionName":"findLongestRegisteredExtension","ranges":[{"startOffset":10461,"endOffset":10897,"count":0}],"isBlockCoverage":false},{"functionName":"trySelfParentPath","ranges":[{"startOffset":10899,"endOffset":11188,"count":0}],"isBlockCoverage":false},{"functionName":"trySelf","ranges":[{"startOffset":11190,"endOffset":12039,"count":0}],"isBlockCoverage":false},{"functionName":"resolveExports","ranges":[{"startOffset":12200,"endOffset":12938,"count":0}],"isBlockCoverage":false},{"functionName":"Module._findPath","ranges":[{"startOffset":13004,"endOffset":15631,"count":1},{"startOffset":13137,"endOffset":13200,"count":0},{"startOffset":13287,"endOffset":13307,"count":0},{"startOffset":13372,"endOffset":13385,"count":0},{"startOffset":13767,"endOffset":13787,"count":0},{"startOffset":13789,"endOffset":13798,"count":0},{"startOffset":13826,"endOffset":13956,"count":0},{"startOffset":14140,"endOffset":14308,"count":0},{"startOffset":14340,"endOffset":14972,"count":0},{"startOffset":15063,"endOffset":15257,"count":0},{"startOffset":15283,"endOffset":15294,"count":0},{"startOffset":15296,"endOffset":15512,"count":0},{"startOffset":15612,"endOffset":15630,"count":0}],"isBlockCoverage":true},{"functionName":"Module._nodeModulePaths","ranges":[{"startOffset":15875,"endOffset":17266,"count":0}],"isBlockCoverage":false},{"functionName":"Module._nodeModulePaths","ranges":[{"startOffset":17358,"endOffset":18399,"count":0}],"isBlockCoverage":false},{"functionName":"Module._resolveLookupPaths","ranges":[{"startOffset":18433,"endOffset":19571,"count":0}],"isBlockCoverage":false},{"functionName":"emitCircularRequireWarning","ranges":[{"startOffset":19574,"endOffset":19757,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":19950,"endOffset":20322,"count":0}],"isBlockCoverage":false},{"functionName":"getOwnPropertyDescriptor","ranges":[{"startOffset":20327,"endOffset":20568,"count":0}],"isBlockCoverage":false},{"functionName":"getExportsForCircularRequire","ranges":[{"startOffset":20769,"endOffset":21419,"count":0}],"isBlockCoverage":false},{"functionName":"Module._load","ranges":[{"startOffset":21831,"endOffset":24853,"count":0}],"isBlockCoverage":false},{"functionName":"Module._resolveFilename","ranges":[{"startOffset":24882,"endOffset":27745,"count":0}],"isBlockCoverage":false},{"functionName":"finalizeEsmResolution","ranges":[{"startOffset":27748,"endOffset":28462,"count":0}],"isBlockCoverage":false},{"functionName":"createEsmNotFoundErr","ranges":[{"startOffset":28464,"endOffset":28754,"count":0}],"isBlockCoverage":false},{"functionName":"Module.load","ranges":[{"startOffset":28843,"endOffset":29647,"count":0}],"isBlockCoverage":false},{"functionName":"Module.require","ranges":[{"startOffset":29765,"endOffset":30064,"count":0}],"isBlockCoverage":false},{"functionName":"wrapSafe","ranges":[{"startOffset":30244,"endOffset":31360,"count":0}],"isBlockCoverage":false},{"functionName":"Module._compile","ranges":[{"startOffset":31560,"endOffset":33402,"count":0}],"isBlockCoverage":false},{"functionName":"Module._extensions..js","ranges":[{"startOffset":33461,"endOffset":34235,"count":0}],"isBlockCoverage":false},{"functionName":"Module._extensions..json","ranges":[{"startOffset":34299,"endOffset":34663,"count":0}],"isBlockCoverage":false},{"functionName":"Module._extensions..node","ranges":[{"startOffset":34727,"endOffset":35045,"count":0}],"isBlockCoverage":false},{"functionName":"createRequireFromPath","ranges":[{"startOffset":35048,"endOffset":35473,"count":0}],"isBlockCoverage":false},{"functionName":"createRequire","ranges":[{"startOffset":35758,"endOffset":36311,"count":0}],"isBlockCoverage":false},{"functionName":"Module._initPaths","ranges":[{"startOffset":36372,"endOffset":37280,"count":1},{"startOffset":36413,"endOffset":36438,"count":0},{"startOffset":36490,"endOffset":36513,"count":0},{"startOffset":36721,"endOffset":36763,"count":0},{"startOffset":37030,"endOffset":37159,"count":0}],"isBlockCoverage":true},{"functionName":"pathsFilterCB","ranges":[{"startOffset":37082,"endOffset":37139,"count":0}],"isBlockCoverage":false},{"functionName":"Module._preloadModules","ranges":[{"startOffset":37308,"endOffset":37890,"count":0}],"isBlockCoverage":false},{"functionName":"syncBuiltinESMExports","ranges":[{"startOffset":37924,"endOffset":38080,"count":0}],"isBlockCoverage":false}]},{"scriptId":"58","url":"vm.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":12941,"count":1}],"isBlockCoverage":false},{"functionName":"Script","ranges":[{"startOffset":1992,"endOffset":4152,"count":0}],"isBlockCoverage":false},{"functionName":"runInThisContext","ranges":[{"startOffset":4156,"endOffset":4436,"count":0}],"isBlockCoverage":false},{"functionName":"runInContext","ranges":[{"startOffset":4440,"endOffset":4846,"count":0}],"isBlockCoverage":false},{"functionName":"runInNewContext","ranges":[{"startOffset":4850,"endOffset":5021,"count":0}],"isBlockCoverage":false},{"functionName":"validateContext","ranges":[{"startOffset":5025,"endOffset":5244,"count":0}],"isBlockCoverage":false},{"functionName":"getRunInContextArgs","ranges":[{"startOffset":5246,"endOffset":5837,"count":0}],"isBlockCoverage":false},{"functionName":"getContextOptions","ranges":[{"startOffset":5839,"endOffset":6907,"count":0}],"isBlockCoverage":false},{"functionName":"isContext","ranges":[{"startOffset":6909,"endOffset":7091,"count":0}],"isBlockCoverage":false},{"functionName":"createContext","ranges":[{"startOffset":7126,"endOffset":8261,"count":0}],"isBlockCoverage":false},{"functionName":"createScript","ranges":[{"startOffset":8263,"endOffset":8339,"count":0}],"isBlockCoverage":false},{"functionName":"sigintHandlersWrap","ranges":[{"startOffset":8493,"endOffset":8939,"count":0}],"isBlockCoverage":false},{"functionName":"runInContext","ranges":[{"startOffset":8941,"endOffset":9338,"count":0}],"isBlockCoverage":false},{"functionName":"runInNewContext","ranges":[{"startOffset":9340,"endOffset":9692,"count":0}],"isBlockCoverage":false},{"functionName":"runInThisContext","ranges":[{"startOffset":9694,"endOffset":9880,"count":0}],"isBlockCoverage":false},{"functionName":"compileFunction","ranges":[{"startOffset":9882,"endOffset":11615,"count":0}],"isBlockCoverage":false},{"functionName":"measureMemory","ranges":[{"startOffset":11892,"endOffset":12454,"count":0}],"isBlockCoverage":false}]},{"scriptId":"59","url":"internal/modules/package_json_reader.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":975,"count":1}],"isBlockCoverage":false},{"functionName":"read","ranges":[{"startOffset":279,"endOffset":946,"count":2},{"startOffset":332,"endOffset":896,"count":1},{"startOffset":686,"endOffset":739,"count":0},{"startOffset":789,"endOffset":892,"count":0},{"startOffset":896,"endOffset":945,"count":1}],"isBlockCoverage":true}]},{"scriptId":"60","url":"internal/process/esm_loader.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2326,"count":1}],"isBlockCoverage":false},{"functionName":"exports.initializeImportMetaObject","ranges":[{"startOffset":405,"endOffset":701,"count":0}],"isBlockCoverage":false},{"functionName":"exports.importModuleDynamicallyCallback","ranges":[{"startOffset":746,"endOffset":1137,"count":4},{"startOffset":1081,"endOffset":1136,"count":0}],"isBlockCoverage":true},{"functionName":"initializeLoader","ranges":[{"startOffset":1202,"endOffset":1969,"count":1},{"startOffset":1388,"endOffset":1968,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":1722,"endOffset":1963,"count":0}],"isBlockCoverage":true},{"functionName":"loadESM","ranges":[{"startOffset":1989,"endOffset":2324,"count":1},{"startOffset":2097,"endOffset":2322,"count":0}],"isBlockCoverage":true}]},{"scriptId":"61","url":"internal/modules/esm/loader.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":8625,"count":1}],"isBlockCoverage":false},{"functionName":"Loader","ranges":[{"startOffset":1416,"endOffset":3189,"count":1}],"isBlockCoverage":true},{"functionName":"resolve","ranges":[{"startOffset":3193,"endOffset":3816,"count":7},{"startOffset":3298,"endOffset":3337,"count":6},{"startOffset":3514,"endOffset":3617,"count":0},{"startOffset":3689,"endOffset":3796,"count":0}],"isBlockCoverage":true},{"functionName":"getFormat","ranges":[{"startOffset":3820,"endOffset":5128,"count":7},{"startOffset":3977,"endOffset":4084,"count":0},{"startOffset":4153,"endOffset":4410,"count":0},{"startOffset":4447,"endOffset":4562,"count":0},{"startOffset":4594,"endOffset":4622,"count":5},{"startOffset":4622,"endOffset":4666,"count":2},{"startOffset":4666,"endOffset":4835,"count":0},{"startOffset":4835,"endOffset":4927,"count":2},{"startOffset":4928,"endOffset":4977,"count":0},{"startOffset":4984,"endOffset":5104,"count":0},{"startOffset":5104,"endOffset":5127,"count":2}],"isBlockCoverage":true},{"functionName":"eval","ranges":[{"startOffset":5132,"endOffset":5807,"count":0}],"isBlockCoverage":false},{"functionName":"import","ranges":[{"startOffset":5811,"endOffset":5982,"count":5}],"isBlockCoverage":true},{"functionName":"hook","ranges":[{"startOffset":5986,"endOffset":6947,"count":0}],"isBlockCoverage":false},{"functionName":"runGlobalPreloadCode","ranges":[{"startOffset":6951,"endOffset":7755,"count":0}],"isBlockCoverage":false},{"functionName":"getModuleJob","ranges":[{"startOffset":7759,"endOffset":8549,"count":7},{"startOffset":8046,"endOffset":8083,"count":0},{"startOffset":8117,"endOffset":8128,"count":4},{"startOffset":8128,"endOffset":8170,"count":3},{"startOffset":8170,"endOffset":8214,"count":0},{"startOffset":8214,"endOffset":8316,"count":3},{"startOffset":8316,"endOffset":8346,"count":1},{"startOffset":8347,"endOffset":8381,"count":1}],"isBlockCoverage":true}]},{"scriptId":"62","url":"internal/modules/esm/module_map.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":878,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":175,"endOffset":200,"count":1}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":419,"endOffset":492,"count":7}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":495,"endOffset":771,"count":3},{"startOffset":585,"endOffset":621,"count":0},{"startOffset":623,"endOffset":693,"count":0}],"isBlockCoverage":true},{"functionName":"has","ranges":[{"startOffset":774,"endOffset":847,"count":0}],"isBlockCoverage":false}]},{"scriptId":"63","url":"internal/modules/esm/module_job.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":5778,"count":1}],"isBlockCoverage":false},{"functionName":"ModuleJob","ranges":[{"startOffset":832,"endOffset":2478,"count":3}],"isBlockCoverage":true},{"functionName":"link","ranges":[{"startOffset":1301,"endOffset":2105,"count":3}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":1757,"endOffset":1984,"count":2}],"isBlockCoverage":true},{"functionName":"instantiate","ranges":[{"startOffset":2482,"endOffset":2627,"count":5},{"startOffset":2539,"endOffset":2593,"count":1}],"isBlockCoverage":true},{"functionName":"_instantiate","ranges":[{"startOffset":2631,"endOffset":5498,"count":1},{"startOffset":3105,"endOffset":3282,"count":0},{"startOffset":3339,"endOffset":5251,"count":0},{"startOffset":5298,"endOffset":5494,"count":3}],"isBlockCoverage":true},{"functionName":"addJobsToDependencyGraph","ranges":[{"startOffset":2730,"endOffset":3004,"count":3},{"startOffset":2791,"endOffset":2816,"count":0}],"isBlockCoverage":true},{"functionName":"run","ranges":[{"startOffset":5502,"endOffset":5698,"count":5}],"isBlockCoverage":true}]},{"scriptId":"64","url":"internal/modules/esm/resolve.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":28247,"count":1}],"isBlockCoverage":false},{"functionName":"getConditionsSet","ranges":[{"startOffset":1873,"endOffset":2225,"count":2},{"startOffset":1982,"endOffset":2190,"count":0}],"isBlockCoverage":true},{"functionName":"tryStatSync","ranges":[{"startOffset":2336,"endOffset":2441,"count":2},{"startOffset":2404,"endOffset":2439,"count":0}],"isBlockCoverage":true},{"functionName":"getPackageConfig","ranges":[{"startOffset":2443,"endOffset":3781,"count":2},{"startOffset":2571,"endOffset":2680,"count":1},{"startOffset":2680,"endOffset":2955,"count":0},{"startOffset":2955,"endOffset":3025,"count":1},{"startOffset":3025,"endOffset":3204,"count":0},{"startOffset":3204,"endOffset":3326,"count":1},{"startOffset":3326,"endOffset":3345,"count":0},{"startOffset":3347,"endOffset":3450,"count":1},{"startOffset":3450,"endOffset":3467,"count":0},{"startOffset":3467,"endOffset":3545,"count":1},{"startOffset":3545,"endOffset":3567,"count":0},{"startOffset":3569,"endOffset":3583,"count":0},{"startOffset":3583,"endOffset":3780,"count":1}],"isBlockCoverage":true},{"functionName":"getPackageScopeConfig","ranges":[{"startOffset":3783,"endOffset":4883,"count":2},{"startOffset":4041,"endOffset":4047,"count":0},{"startOffset":4227,"endOffset":4550,"count":0},{"startOffset":4554,"endOffset":4882,"count":0}],"isBlockCoverage":true},{"functionName":"fileExists","ranges":[{"startOffset":5139,"endOffset":5218,"count":0}],"isBlockCoverage":false},{"functionName":"legacyMainResolve","ranges":[{"startOffset":5220,"endOffset":6891,"count":0}],"isBlockCoverage":false},{"functionName":"resolveExtensionsWithTryExactName","ranges":[{"startOffset":6893,"endOffset":7024,"count":0}],"isBlockCoverage":false},{"functionName":"resolveExtensions","ranges":[{"startOffset":7080,"endOffset":7337,"count":0}],"isBlockCoverage":false},{"functionName":"resolveIndex","ranges":[{"startOffset":7339,"endOffset":7426,"count":0}],"isBlockCoverage":false},{"functionName":"finalizeResolution","ranges":[{"startOffset":7464,"endOffset":8671,"count":2},{"startOffset":7577,"endOffset":7720,"count":0},{"startOffset":7834,"endOffset":8243,"count":0},{"startOffset":8308,"endOffset":8344,"count":0},{"startOffset":8381,"endOffset":8508,"count":0},{"startOffset":8535,"endOffset":8649,"count":0}],"isBlockCoverage":true},{"functionName":"throwImportNotDefined","ranges":[{"startOffset":8673,"endOffset":8888,"count":0}],"isBlockCoverage":false},{"functionName":"throwExportsNotFound","ranges":[{"startOffset":8890,"endOffset":9089,"count":0}],"isBlockCoverage":false},{"functionName":"throwInvalidSubpath","ranges":[{"startOffset":9091,"endOffset":9441,"count":0}],"isBlockCoverage":false},{"functionName":"throwInvalidPackageTarget","ranges":[{"startOffset":9443,"endOffset":9825,"count":0}],"isBlockCoverage":false},{"functionName":"resolvePackageTargetString","ranges":[{"startOffset":9926,"endOffset":11589,"count":0}],"isBlockCoverage":false},{"functionName":"isArrayIndex","ranges":[{"startOffset":11644,"endOffset":11784,"count":0}],"isBlockCoverage":false},{"functionName":"resolvePackageTarget","ranges":[{"startOffset":11786,"endOffset":13918,"count":0}],"isBlockCoverage":false},{"functionName":"isConditionalExportsMainSugar","ranges":[{"startOffset":13920,"endOffset":14855,"count":0}],"isBlockCoverage":false},{"functionName":"packageExportsResolve","ranges":[{"startOffset":15040,"endOffset":16923,"count":0}],"isBlockCoverage":false},{"functionName":"packageImportsResolve","ranges":[{"startOffset":16925,"endOffset":18921,"count":0}],"isBlockCoverage":false},{"functionName":"getPackageType","ranges":[{"startOffset":18923,"endOffset":19036,"count":2}],"isBlockCoverage":true},{"functionName":"packageResolve","ranges":[{"startOffset":19149,"endOffset":21981,"count":0}],"isBlockCoverage":false},{"functionName":"isBareSpecifier","ranges":[{"startOffset":21983,"endOffset":22093,"count":0}],"isBlockCoverage":false},{"functionName":"isRelativeSpecifier","ranges":[{"startOffset":22095,"endOffset":22366,"count":2},{"startOffset":22165,"endOffset":22348,"count":1},{"startOffset":22235,"endOffset":22344,"count":0},{"startOffset":22348,"endOffset":22365,"count":1}],"isBlockCoverage":true},{"functionName":"shouldBeTreatedAsRelativeOrAbsolutePath","ranges":[{"startOffset":22368,"endOffset":22551,"count":2},{"startOffset":22454,"endOffset":22467,"count":0},{"startOffset":22496,"endOffset":22508,"count":0}],"isBlockCoverage":true},{"functionName":"moduleResolve","ranges":[{"startOffset":22664,"endOffset":23235,"count":2},{"startOffset":22892,"endOffset":23188,"count":1},{"startOffset":22970,"endOffset":23048,"count":0},{"startOffset":23109,"endOffset":23184,"count":0}],"isBlockCoverage":true},{"functionName":"resolveAsCommonJS","ranges":[{"startOffset":23381,"endOffset":24789,"count":0}],"isBlockCoverage":false},{"functionName":"defaultResolve","ranges":[{"startOffset":24791,"endOffset":28097,"count":7},{"startOffset":24923,"endOffset":24942,"count":6},{"startOffset":24944,"endOffset":25547,"count":0},{"startOffset":25640,"endOffset":25694,"count":0},{"startOffset":25699,"endOffset":25707,"count":6},{"startOffset":25721,"endOffset":25751,"count":1},{"startOffset":25757,"endOffset":25783,"count":0},{"startOffset":25797,"endOffset":25827,"count":1},{"startOffset":25828,"endOffset":25858,"count":0},{"startOffset":25864,"endOffset":25913,"count":0},{"startOffset":25966,"endOffset":26022,"count":5},{"startOffset":26022,"endOffset":26039,"count":2},{"startOffset":26039,"endOffset":26087,"count":1},{"startOffset":26089,"endOffset":26177,"count":0},{"startOffset":26177,"endOffset":26235,"count":2},{"startOffset":26235,"endOffset":26762,"count":1},{"startOffset":26719,"endOffset":26758,"count":0},{"startOffset":26762,"endOffset":26891,"count":2},{"startOffset":26891,"endOffset":27695,"count":0},{"startOffset":27695,"endOffset":27710,"count":2},{"startOffset":27710,"endOffset":27733,"count":1},{"startOffset":27734,"endOffset":27753,"count":1},{"startOffset":27755,"endOffset":28066,"count":2},{"startOffset":27995,"endOffset":28000,"count":0},{"startOffset":28066,"endOffset":28096,"count":2}],"isBlockCoverage":true}]},{"scriptId":"65","url":"internal/modules/esm/get_format.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2496,"count":1}],"isBlockCoverage":false},{"functionName":"defaultGetFormat","ranges":[{"startOffset":1131,"endOffset":2450,"count":7},{"startOffset":1244,"endOffset":1283,"count":5},{"startOffset":1283,"endOffset":1350,"count":2},{"startOffset":1350,"endOffset":1760,"count":0},{"startOffset":1760,"endOffset":2421,"count":2},{"startOffset":1951,"endOffset":1963,"count":0},{"startOffset":1970,"endOffset":2023,"count":0},{"startOffset":2041,"endOffset":2378,"count":0},{"startOffset":2407,"endOffset":2414,"count":0},{"startOffset":2421,"endOffset":2449,"count":0}],"isBlockCoverage":true}]},{"scriptId":"66","url":"internal/modules/esm/get_source.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1307,"count":1}],"isBlockCoverage":false},{"functionName":"defaultGetSource","ranges":[{"startOffset":609,"endOffset":1261,"count":2},{"startOffset":810,"endOffset":1155,"count":0},{"startOffset":1180,"endOffset":1238,"count":0}],"isBlockCoverage":true}]},{"scriptId":"67","url":"internal/fs/promises.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":20020,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":2272,"endOffset":2432,"count":0}],"isBlockCoverage":false},{"functionName":"FileHandle","ranges":[{"startOffset":2480,"endOffset":2657,"count":17},{"startOffset":2594,"endOffset":2598,"count":0}],"isBlockCoverage":true},{"functionName":"getAsyncId","ranges":[{"startOffset":2661,"endOffset":2718,"count":0}],"isBlockCoverage":false},{"functionName":"get fd","ranges":[{"startOffset":2722,"endOffset":2758,"count":93}],"isBlockCoverage":true},{"functionName":"appendFile","ranges":[{"startOffset":2762,"endOffset":2844,"count":0}],"isBlockCoverage":false},{"functionName":"chmod","ranges":[{"startOffset":2848,"endOffset":2904,"count":0}],"isBlockCoverage":false},{"functionName":"chown","ranges":[{"startOffset":2908,"endOffset":2972,"count":0}],"isBlockCoverage":false},{"functionName":"datasync","ranges":[{"startOffset":2976,"endOffset":3028,"count":0}],"isBlockCoverage":false},{"functionName":"sync","ranges":[{"startOffset":3032,"endOffset":3076,"count":0}],"isBlockCoverage":false},{"functionName":"read","ranges":[{"startOffset":3080,"endOffset":3189,"count":0}],"isBlockCoverage":false},{"functionName":"readv","ranges":[{"startOffset":3193,"endOffset":3274,"count":0}],"isBlockCoverage":false},{"functionName":"readFile","ranges":[{"startOffset":3278,"endOffset":3345,"count":0}],"isBlockCoverage":false},{"functionName":"stat","ranges":[{"startOffset":3349,"endOffset":3409,"count":0}],"isBlockCoverage":false},{"functionName":"truncate","ranges":[{"startOffset":3413,"endOffset":3477,"count":0}],"isBlockCoverage":false},{"functionName":"utimes","ranges":[{"startOffset":3481,"endOffset":3555,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":3559,"endOffset":3670,"count":0}],"isBlockCoverage":false},{"functionName":"writev","ranges":[{"startOffset":3674,"endOffset":3757,"count":0}],"isBlockCoverage":false},{"functionName":"writeFile","ranges":[{"startOffset":3761,"endOffset":3842,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":3846,"endOffset":4538,"count":17}],"isBlockCoverage":false},{"functionName":"close","ranges":[{"startOffset":3854,"endOffset":4538,"count":17},{"startOffset":3888,"endOffset":3926,"count":0},{"startOffset":3957,"endOffset":3998,"count":0},{"startOffset":4192,"endOffset":4501,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":4128,"endOffset":4184,"count":17}],"isBlockCoverage":true},{"functionName":".Promise.finally.","ranges":[{"startOffset":4240,"endOffset":4346,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":4356,"endOffset":4493,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":4542,"endOffset":5011,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":5015,"endOffset":5068,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":5072,"endOffset":5159,"count":0}],"isBlockCoverage":false},{"functionName":"fsCall","ranges":[{"startOffset":5163,"endOffset":5781,"count":0}],"isBlockCoverage":false},{"functionName":"writeFileHandle","ranges":[{"startOffset":5783,"endOffset":6459,"count":0}],"isBlockCoverage":false},{"functionName":"readFileHandle","ranges":[{"startOffset":6461,"endOffset":7737,"count":17},{"startOffset":6572,"endOffset":6589,"count":0},{"startOffset":6591,"endOffset":6667,"count":0},{"startOffset":6760,"endOffset":6777,"count":0},{"startOffset":6779,"endOffset":6855,"count":0},{"startOffset":6923,"endOffset":6964,"count":16},{"startOffset":6964,"endOffset":6989,"count":1},{"startOffset":7022,"endOffset":7060,"count":0},{"startOffset":7114,"endOffset":7141,"count":2},{"startOffset":7142,"endOffset":7184,"count":15},{"startOffset":7216,"endOffset":7567,"count":76},{"startOffset":7233,"endOffset":7250,"count":0},{"startOffset":7252,"endOffset":7332,"count":0},{"startOffset":7460,"endOffset":7523,"count":75},{"startOffset":7523,"endOffset":7563,"count":59},{"startOffset":7567,"endOffset":7626,"count":16},{"startOffset":7626,"endOffset":7637,"count":8},{"startOffset":7638,"endOffset":7661,"count":8},{"startOffset":7690,"endOffset":7725,"count":14},{"startOffset":7726,"endOffset":7734,"count":2}],"isBlockCoverage":true},{"functionName":"access","ranges":[{"startOffset":7890,"endOffset":8111,"count":0}],"isBlockCoverage":false},{"functionName":"copyFile","ranges":[{"startOffset":8113,"endOffset":8471,"count":0}],"isBlockCoverage":false},{"functionName":"open","ranges":[{"startOffset":8591,"endOffset":8916,"count":17}],"isBlockCoverage":true},{"functionName":"read","ranges":[{"startOffset":8918,"endOffset":9972,"count":76},{"startOffset":9057,"endOffset":9339,"count":0},{"startOffset":9363,"endOffset":9384,"count":0},{"startOffset":9476,"endOffset":9513,"count":0},{"startOffset":9542,"endOffset":9672,"count":0},{"startOffset":9776,"endOffset":9790,"count":0},{"startOffset":9930,"endOffset":9932,"count":75},{"startOffset":9932,"endOffset":9936,"count":16}],"isBlockCoverage":true},{"functionName":"readv","ranges":[{"startOffset":9974,"endOffset":10294,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":10296,"endOffset":11217,"count":0}],"isBlockCoverage":false},{"functionName":"writev","ranges":[{"startOffset":11219,"endOffset":11551,"count":0}],"isBlockCoverage":false},{"functionName":"rename","ranges":[{"startOffset":11553,"endOffset":11859,"count":0}],"isBlockCoverage":false},{"functionName":"truncate","ranges":[{"startOffset":11861,"endOffset":12004,"count":0}],"isBlockCoverage":false},{"functionName":"ftruncate","ranges":[{"startOffset":12006,"endOffset":12165,"count":0}],"isBlockCoverage":false},{"functionName":"rm","ranges":[{"startOffset":12167,"endOffset":12364,"count":0}],"isBlockCoverage":false},{"functionName":"rmdir","ranges":[{"startOffset":12366,"endOffset":12629,"count":0}],"isBlockCoverage":false},{"functionName":"fdatasync","ranges":[{"startOffset":12631,"endOffset":12720,"count":0}],"isBlockCoverage":false},{"functionName":"fsync","ranges":[{"startOffset":12722,"endOffset":12803,"count":0}],"isBlockCoverage":false},{"functionName":"mkdir","ranges":[{"startOffset":12805,"endOffset":13337,"count":0}],"isBlockCoverage":false},{"functionName":"readdir","ranges":[{"startOffset":13339,"endOffset":13791,"count":1},{"startOffset":13729,"endOffset":13775,"count":0}],"isBlockCoverage":true},{"functionName":"readlink","ranges":[{"startOffset":13793,"endOffset":14036,"count":0}],"isBlockCoverage":false},{"functionName":"symlink","ranges":[{"startOffset":14038,"endOffset":14451,"count":0}],"isBlockCoverage":false},{"functionName":"fstat","ranges":[{"startOffset":14453,"endOffset":14631,"count":0}],"isBlockCoverage":false},{"functionName":"lstat","ranges":[{"startOffset":14633,"endOffset":14903,"count":0}],"isBlockCoverage":false},{"functionName":"stat","ranges":[{"startOffset":14905,"endOffset":15172,"count":0}],"isBlockCoverage":false},{"functionName":"link","ranges":[{"startOffset":15174,"endOffset":15497,"count":0}],"isBlockCoverage":false},{"functionName":"unlink","ranges":[{"startOffset":15499,"endOffset":15637,"count":0}],"isBlockCoverage":false},{"functionName":"fchmod","ranges":[{"startOffset":15639,"endOffset":15772,"count":0}],"isBlockCoverage":false},{"functionName":"chmod","ranges":[{"startOffset":15774,"endOffset":15960,"count":0}],"isBlockCoverage":false},{"functionName":"lchmod","ranges":[{"startOffset":15962,"endOffset":16200,"count":0}],"isBlockCoverage":false},{"functionName":"lchown","ranges":[{"startOffset":16202,"endOffset":16478,"count":0}],"isBlockCoverage":false},{"functionName":"fchown","ranges":[{"startOffset":16480,"endOffset":16677,"count":0}],"isBlockCoverage":false},{"functionName":"chown","ranges":[{"startOffset":16679,"endOffset":16952,"count":0}],"isBlockCoverage":false},{"functionName":"utimes","ranges":[{"startOffset":16954,"endOffset":17226,"count":0}],"isBlockCoverage":false},{"functionName":"futimes","ranges":[{"startOffset":17228,"endOffset":17427,"count":0}],"isBlockCoverage":false},{"functionName":"lutimes","ranges":[{"startOffset":17429,"endOffset":17706,"count":0}],"isBlockCoverage":false},{"functionName":"realpath","ranges":[{"startOffset":17708,"endOffset":17885,"count":0}],"isBlockCoverage":false},{"functionName":"mkdtemp","ranges":[{"startOffset":17887,"endOffset":18217,"count":0}],"isBlockCoverage":false},{"functionName":"writeFile","ranges":[{"startOffset":18219,"endOffset":18938,"count":0}],"isBlockCoverage":false},{"functionName":"appendFile","ranges":[{"startOffset":18940,"endOffset":19181,"count":0}],"isBlockCoverage":false},{"functionName":"readFile","ranges":[{"startOffset":19183,"endOffset":19613,"count":17},{"startOffset":19300,"endOffset":19306,"count":0},{"startOffset":19347,"endOffset":19384,"count":0},{"startOffset":19417,"endOffset":19493,"count":0}],"isBlockCoverage":true}]},{"scriptId":"68","url":"internal/fs/rimraf.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":7039,"count":1}],"isBlockCoverage":false},{"functionName":"rimraf","ranges":[{"startOffset":1137,"endOffset":1597,"count":0}],"isBlockCoverage":false},{"functionName":"_rimraf","ranges":[{"startOffset":1600,"endOffset":2458,"count":0}],"isBlockCoverage":false},{"functionName":"fixWinEPERM","ranges":[{"startOffset":2461,"endOffset":2896,"count":0}],"isBlockCoverage":false},{"functionName":"_rmdir","ranges":[{"startOffset":2899,"endOffset":3197,"count":0}],"isBlockCoverage":false},{"functionName":"_rmchildren","ranges":[{"startOffset":3200,"endOffset":3872,"count":0}],"isBlockCoverage":false},{"functionName":"rimrafPromises","ranges":[{"startOffset":3875,"endOffset":4073,"count":0}],"isBlockCoverage":false},{"functionName":"rimrafSync","ranges":[{"startOffset":4076,"endOffset":4781,"count":0}],"isBlockCoverage":false},{"functionName":"_unlinkSync","ranges":[{"startOffset":4784,"endOffset":5267,"count":0}],"isBlockCoverage":false},{"functionName":"_rmdirSync","ranges":[{"startOffset":5270,"endOffset":6540,"count":0}],"isBlockCoverage":false},{"functionName":"fixWinEPERMSync","ranges":[{"startOffset":6543,"endOffset":6979,"count":0}],"isBlockCoverage":false}]},{"scriptId":"69","url":"internal/modules/esm/transform_source.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":215,"count":1}],"isBlockCoverage":false},{"functionName":"defaultTransformSource","ranges":[{"startOffset":15,"endOffset":157,"count":2}],"isBlockCoverage":true}]},{"scriptId":"70","url":"internal/modules/esm/translators.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":12048,"count":1}],"isBlockCoverage":false},{"functionName":"lazyTypes","ranges":[{"startOffset":416,"endOffset":528,"count":4},{"startOffset":462,"endOffset":476,"count":3},{"startOffset":476,"endOffset":527,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":1202,"endOffset":1227,"count":1}],"isBlockCoverage":true},{"functionName":"initCJSParse","ranges":[{"startOffset":1860,"endOffset":2152,"count":0}],"isBlockCoverage":false},{"functionName":"assertBufferSource","ranges":[{"startOffset":2286,"endOffset":2706,"count":4},{"startOffset":2363,"endOffset":2390,"count":2},{"startOffset":2392,"endOffset":2409,"count":0},{"startOffset":2503,"endOffset":2528,"count":0},{"startOffset":2547,"endOffset":2626,"count":0},{"startOffset":2627,"endOffset":2631,"count":0}],"isBlockCoverage":true},{"functionName":"stringify","ranges":[{"startOffset":2708,"endOffset":2926,"count":2},{"startOffset":2767,"endOffset":2779,"count":0},{"startOffset":2863,"endOffset":2882,"count":1},{"startOffset":2883,"endOffset":2892,"count":1}],"isBlockCoverage":true},{"functionName":"errPath","ranges":[{"startOffset":2928,"endOffset":3073,"count":0}],"isBlockCoverage":false},{"functionName":"importModuleDynamically","ranges":[{"startOffset":3075,"endOffset":3189,"count":4}],"isBlockCoverage":true},{"functionName":"createImportMetaResolve","ranges":[{"startOffset":3191,"endOffset":3539,"count":0}],"isBlockCoverage":false},{"functionName":"initializeImportMeta","ranges":[{"startOffset":3541,"endOffset":3711,"count":0}],"isBlockCoverage":false},{"functionName":"moduleStrategy","ranges":[{"startOffset":3793,"endOffset":4374,"count":2}],"isBlockCoverage":true},{"functionName":"enrichCJSError","ranges":[{"startOffset":4378,"endOffset":5277,"count":0}],"isBlockCoverage":false},{"functionName":"commonjsStrategy","ranges":[{"startOffset":5435,"endOffset":6741,"count":0}],"isBlockCoverage":false},{"functionName":"cjsPreparseModuleExports","ranges":[{"startOffset":6745,"endOffset":8189,"count":0}],"isBlockCoverage":false},{"functionName":"builtinStrategy","ranges":[{"startOffset":8313,"endOffset":8701,"count":1},{"startOffset":8574,"endOffset":8626,"count":0}],"isBlockCoverage":true},{"functionName":"jsonStrategy","ranges":[{"startOffset":8765,"endOffset":10884,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":10950,"endOffset":12045,"count":0}],"isBlockCoverage":false}]},{"scriptId":"71","url":"internal/modules/esm/create_dynamic_module.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1756,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":190,"endOffset":215,"count":0}],"isBlockCoverage":false},{"functionName":"createImport","ranges":[{"startOffset":219,"endOffset":409,"count":0}],"isBlockCoverage":false},{"functionName":"createExport","ranges":[{"startOffset":411,"endOffset":612,"count":0}],"isBlockCoverage":false},{"functionName":"createDynamicModule","ranges":[{"startOffset":642,"endOffset":1715,"count":0}],"isBlockCoverage":false}]},{"scriptId":"72","url":"internal/vm/module.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":12877,"count":1}],"isBlockCoverage":false},{"functionName":"Module","ranges":[{"startOffset":1618,"endOffset":3804,"count":0}],"isBlockCoverage":false},{"functionName":"get identifier","ranges":[{"startOffset":3808,"endOffset":3945,"count":0}],"isBlockCoverage":false},{"functionName":"get context","ranges":[{"startOffset":3949,"endOffset":4082,"count":0}],"isBlockCoverage":false},{"functionName":"get namespace","ranges":[{"startOffset":4086,"endOffset":4363,"count":0}],"isBlockCoverage":false},{"functionName":"get status","ranges":[{"startOffset":4367,"endOffset":4520,"count":0}],"isBlockCoverage":false},{"functionName":"get error","ranges":[{"startOffset":4524,"endOffset":4774,"count":0}],"isBlockCoverage":false},{"functionName":"link","ranges":[{"startOffset":4778,"endOffset":5257,"count":0}],"isBlockCoverage":false},{"functionName":"evaluate","ranges":[{"startOffset":5261,"endOffset":6213,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":6217,"endOffset":6945,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":7092,"endOffset":7128,"count":0}],"isBlockCoverage":false},{"functionName":"SourceTextModule","ranges":[{"startOffset":7133,"endOffset":9559,"count":0}],"isBlockCoverage":false},{"functionName":"get dependencySpecifiers","ranges":[{"startOffset":9563,"endOffset":9862,"count":0}],"isBlockCoverage":false},{"functionName":"get status","ranges":[{"startOffset":9866,"endOffset":10135,"count":0}],"isBlockCoverage":false},{"functionName":"get error","ranges":[{"startOffset":10139,"endOffset":10335,"count":0}],"isBlockCoverage":false},{"functionName":"createCachedData","ranges":[{"startOffset":10339,"endOffset":10601,"count":0}],"isBlockCoverage":false},{"functionName":"SyntheticModule","ranges":[{"startOffset":10646,"endOffset":11943,"count":0}],"isBlockCoverage":false},{"functionName":"setExport","ranges":[{"startOffset":11947,"endOffset":12249,"count":0}],"isBlockCoverage":false},{"functionName":"importModuleDynamicallyWrap","ranges":[{"startOffset":12253,"endOffset":12715,"count":0}],"isBlockCoverage":false},{"functionName":"getModuleFromWrap","ranges":[{"startOffset":12837,"endOffset":12872,"count":4}],"isBlockCoverage":true}]},{"scriptId":"73","url":"internal/modules/run_main.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2582,"count":1}],"isBlockCoverage":false},{"functionName":"resolveMainPath","ranges":[{"startOffset":220,"endOffset":658,"count":1},{"startOffset":487,"endOffset":494,"count":0}],"isBlockCoverage":true},{"functionName":"shouldUseESMLoader","ranges":[{"startOffset":660,"endOffset":1215,"count":1},{"startOffset":784,"endOffset":796,"count":0},{"startOffset":944,"endOffset":956,"count":0},{"startOffset":1051,"endOffset":1063,"count":0},{"startOffset":1114,"endOffset":1127,"count":0}],"isBlockCoverage":true},{"functionName":"runMainESM","ranges":[{"startOffset":1217,"endOffset":1552,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":1400,"endOffset":1547,"count":1},{"startOffset":1497,"endOffset":1507,"count":0}],"isBlockCoverage":true},{"functionName":"handleMainPromise","ranges":[{"startOffset":1554,"endOffset":1991,"count":1}],"isBlockCoverage":true},{"functionName":"handler","ranges":[{"startOffset":1803,"endOffset":1896,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":1953,"endOffset":1987,"count":1}],"isBlockCoverage":true},{"functionName":"executeUserEntryPoint","ranges":[{"startOffset":2177,"endOffset":2512,"count":1},{"startOffset":2387,"endOffset":2394,"count":0},{"startOffset":2400,"endOffset":2510,"count":0}],"isBlockCoverage":true}]},{"scriptId":"74","url":"file:///home/runner/work/jslint/jslint/test.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":11367,"count":1}],"isBlockCoverage":true},{"functionName":"assertOrThrow","ranges":[{"startOffset":72,"endOffset":228,"count":701},{"startOffset":189,"endOffset":226,"count":1}],"isBlockCoverage":true},{"functionName":"noop","ranges":[{"startOffset":230,"endOffset":301,"count":1}],"isBlockCoverage":true},{"functionName":"testCaseJslintCli","ranges":[{"startOffset":304,"endOffset":916,"count":1}],"isBlockCoverage":true},{"functionName":"process.exit","ranges":[{"startOffset":419,"endOffset":490,"count":1}],"isBlockCoverage":true},{"functionName":"testCaseJslintMisc","ranges":[{"startOffset":923,"endOffset":1155,"count":1}],"isBlockCoverage":true},{"functionName":"testCaseJslintOption","ranges":[{"startOffset":1162,"endOffset":1741,"count":1}],"isBlockCoverage":true},{"functionName":"testCaseJslintCodeValidate","ranges":[{"startOffset":1748,"endOffset":4323,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":4097,"endOffset":4319,"count":16}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":4144,"endOffset":4311,"count":36}],"isBlockCoverage":true},{"functionName":"testCaseJslintWarningsValidate","ranges":[{"startOffset":4330,"endOffset":11362,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":8602,"endOffset":9142,"count":8}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":8700,"endOffset":9134,"count":123}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":8806,"endOffset":8934,"count":197}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":9398,"endOffset":11358,"count":152},{"startOffset":10156,"endOffset":10665,"count":124},{"startOffset":10273,"endOffset":10374,"count":3},{"startOffset":10387,"endOffset":10493,"count":1},{"startOffset":10506,"endOffset":10523,"count":1},{"startOffset":10536,"endOffset":10641,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":10760,"endOffset":11350,"count":193},{"startOffset":10932,"endOffset":10951,"count":187},{"startOffset":10968,"endOffset":11044,"count":6},{"startOffset":11265,"endOffset":11288,"count":52}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":11131,"endOffset":11263,"count":356}],"isBlockCoverage":true}]},{"scriptId":"75","url":"internal/fs/streams.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":11147,"count":1}],"isBlockCoverage":false},{"functionName":"ReadStream","ranges":[{"startOffset":702,"endOffset":2789,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":2928,"endOffset":2963,"count":0}],"isBlockCoverage":false},{"functionName":"_openReadFs","ranges":[{"startOffset":3063,"endOffset":3544,"count":0}],"isBlockCoverage":false},{"functionName":"ReadStream._read","ranges":[{"startOffset":3575,"endOffset":4888,"count":0}],"isBlockCoverage":false},{"functionName":"ReadStream._destroy","ranges":[{"startOffset":4923,"endOffset":5210,"count":0}],"isBlockCoverage":false},{"functionName":"closeFsStream","ranges":[{"startOffset":5213,"endOffset":5369,"count":0}],"isBlockCoverage":false},{"functionName":"ReadStream.close","ranges":[{"startOffset":5400,"endOffset":5486,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":5547,"endOffset":5581,"count":0}],"isBlockCoverage":false},{"functionName":"WriteStream","ranges":[{"startOffset":5609,"endOffset":7859,"count":0}],"isBlockCoverage":false},{"functionName":"WriteStream._final","ranges":[{"startOffset":8002,"endOffset":8159,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":8192,"endOffset":8228,"count":0}],"isBlockCoverage":false},{"functionName":"_openWriteFs","ranges":[{"startOffset":8331,"endOffset":8764,"count":0}],"isBlockCoverage":false},{"functionName":"WriteStream._write","ranges":[{"startOffset":8798,"endOffset":9466,"count":0}],"isBlockCoverage":false},{"functionName":"WriteStream._writev","ranges":[{"startOffset":9502,"endOffset":10392,"count":0}],"isBlockCoverage":false},{"functionName":"WriteStream.close","ranges":[{"startOffset":10490,"endOffset":10872,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":11035,"endOffset":11069,"count":0}],"isBlockCoverage":false}]},{"scriptId":"76","url":"stream.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2187,"count":1}],"isBlockCoverage":false},{"functionName":"_uint8ArrayToBuffer","ranges":[{"startOffset":1978,"endOffset":2185,"count":0}],"isBlockCoverage":false}]},{"scriptId":"77","url":"internal/streams/pipeline.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":7631,"count":1}],"isBlockCoverage":false},{"functionName":"destroyer","ranges":[{"startOffset":543,"endOffset":1935,"count":0}],"isBlockCoverage":false},{"functionName":"popCallback","ranges":[{"startOffset":1937,"endOffset":2308,"count":0}],"isBlockCoverage":false},{"functionName":"isReadable","ranges":[{"startOffset":2310,"endOffset":2390,"count":0}],"isBlockCoverage":false},{"functionName":"isWritable","ranges":[{"startOffset":2392,"endOffset":2473,"count":0}],"isBlockCoverage":false},{"functionName":"isStream","ranges":[{"startOffset":2475,"endOffset":2546,"count":0}],"isBlockCoverage":false},{"functionName":"isIterable","ranges":[{"startOffset":2548,"endOffset":2871,"count":0}],"isBlockCoverage":false},{"functionName":"makeAsyncIterable","ranges":[{"startOffset":2873,"endOffset":3149,"count":0}],"isBlockCoverage":false},{"functionName":"fromReadable","ranges":[{"startOffset":3151,"endOffset":3315,"count":0}],"isBlockCoverage":false},{"functionName":"pump","ranges":[{"startOffset":3317,"endOffset":3794,"count":0}],"isBlockCoverage":false},{"functionName":"pipeline","ranges":[{"startOffset":3796,"endOffset":7602,"count":0}],"isBlockCoverage":false}]},{"scriptId":"78","url":"internal/streams/destroy.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":3954,"count":1}],"isBlockCoverage":false},{"functionName":"destroy","ranges":[{"startOffset":123,"endOffset":1394,"count":0}],"isBlockCoverage":false},{"functionName":"emitErrorCloseNT","ranges":[{"startOffset":1396,"endOffset":1483,"count":0}],"isBlockCoverage":false},{"functionName":"emitCloseNT","ranges":[{"startOffset":1485,"endOffset":1703,"count":0}],"isBlockCoverage":false},{"functionName":"emitErrorNT","ranges":[{"startOffset":1705,"endOffset":1992,"count":0}],"isBlockCoverage":false},{"functionName":"undestroy","ranges":[{"startOffset":1994,"endOffset":2557,"count":1}],"isBlockCoverage":true},{"functionName":"errorOrDestroy","ranges":[{"startOffset":2559,"endOffset":3458,"count":0}],"isBlockCoverage":false},{"functionName":"isRequest","ranges":[{"startOffset":3460,"endOffset":3565,"count":0}],"isBlockCoverage":false},{"functionName":"destroyer","ranges":[{"startOffset":3600,"endOffset":3876,"count":0}],"isBlockCoverage":false}]},{"scriptId":"79","url":"internal/streams/end-of-stream.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":5791,"count":1}],"isBlockCoverage":false},{"functionName":"isRequest","ranges":[{"startOffset":280,"endOffset":375,"count":0}],"isBlockCoverage":false},{"functionName":"isReadable","ranges":[{"startOffset":377,"endOffset":535,"count":0}],"isBlockCoverage":false},{"functionName":"isWritable","ranges":[{"startOffset":537,"endOffset":695,"count":0}],"isBlockCoverage":false},{"functionName":"isWritableFinished","ranges":[{"startOffset":697,"endOffset":934,"count":0}],"isBlockCoverage":false},{"functionName":"nop","ranges":[{"startOffset":936,"endOffset":953,"count":0}],"isBlockCoverage":false},{"functionName":"isReadableEnded","ranges":[{"startOffset":955,"endOffset":1188,"count":0}],"isBlockCoverage":false},{"functionName":"eos","ranges":[{"startOffset":1190,"endOffset":5767,"count":0}],"isBlockCoverage":false}]},{"scriptId":"80","url":"internal/streams/legacy.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2081,"count":1}],"isBlockCoverage":false},{"functionName":"Stream","ranges":[{"startOffset":96,"endOffset":144,"count":2}],"isBlockCoverage":true},{"functionName":"Stream.pipe","ranges":[{"startOffset":258,"endOffset":2053,"count":0}],"isBlockCoverage":false}]},{"scriptId":"81","url":"internal/streams/readable.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":40444,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":1596,"endOffset":1621,"count":0}],"isBlockCoverage":false},{"functionName":"nop","ranges":[{"startOffset":2218,"endOffset":2235,"count":0}],"isBlockCoverage":false},{"functionName":"prependListener","ranges":[{"startOffset":2278,"endOffset":3085,"count":0}],"isBlockCoverage":false},{"functionName":"ReadableState","ranges":[{"startOffset":3087,"endOffset":6664,"count":1},{"startOffset":3486,"endOffset":3529,"count":0},{"startOffset":4062,"endOffset":4098,"count":0},{"startOffset":6476,"endOffset":6662,"count":0}],"isBlockCoverage":true},{"functionName":"Readable","ranges":[{"startOffset":6667,"endOffset":7237,"count":1},{"startOffset":6735,"endOffset":6764,"count":0},{"startOffset":7087,"endOffset":7113,"count":0},{"startOffset":7168,"endOffset":7200,"count":0}],"isBlockCoverage":true},{"functionName":"Readable._destroy","ranges":[{"startOffset":7374,"endOffset":7406,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.","ranges":[{"startOffset":7457,"endOffset":7495,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.push","ranges":[{"startOffset":7724,"endOffset":7810,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.unshift","ranges":[{"startOffset":7906,"endOffset":7991,"count":0}],"isBlockCoverage":false},{"functionName":"readableAddChunk","ranges":[{"startOffset":7994,"endOffset":10247,"count":0}],"isBlockCoverage":false},{"functionName":"addChunk","ranges":[{"startOffset":10249,"endOffset":10949,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.isPaused","ranges":[{"startOffset":10981,"endOffset":11093,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.setEncoding","ranges":[{"startOffset":11157,"endOffset":11801,"count":0}],"isBlockCoverage":false},{"functionName":"computeNewHighWaterMark","ranges":[{"startOffset":11862,"endOffset":12227,"count":0}],"isBlockCoverage":false},{"functionName":"howMuchToRead","ranges":[{"startOffset":12340,"endOffset":12734,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.read","ranges":[{"startOffset":12831,"endOffset":17061,"count":0}],"isBlockCoverage":false},{"functionName":"onEofChunk","ranges":[{"startOffset":17064,"endOffset":17884,"count":0}],"isBlockCoverage":false},{"functionName":"emitReadable","ranges":[{"startOffset":18085,"endOffset":18412,"count":0}],"isBlockCoverage":false},{"functionName":"emitReadable_","ranges":[{"startOffset":18414,"endOffset":19050,"count":0}],"isBlockCoverage":false},{"functionName":"maybeReadMore","ranges":[{"startOffset":19400,"endOffset":19556,"count":0}],"isBlockCoverage":false},{"functionName":"maybeReadMore_","ranges":[{"startOffset":19558,"endOffset":21350,"count":0}],"isBlockCoverage":false},{"functionName":"Readable._read","ranges":[{"startOffset":21621,"endOffset":21687,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.pipe","ranges":[{"startOffset":21716,"endOffset":26193,"count":0}],"isBlockCoverage":false},{"functionName":"pipeOnDrain","ranges":[{"startOffset":26196,"endOffset":26870,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.unpipe","ranges":[{"startOffset":26901,"endOffset":27570,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.on","ranges":[{"startOffset":27696,"endOffset":28603,"count":165},{"startOffset":27828,"endOffset":28160,"count":0},{"startOffset":28189,"endOffset":28586,"count":0}],"isBlockCoverage":true},{"functionName":"Readable.removeListener","ranges":[{"startOffset":28698,"endOffset":29212,"count":163},{"startOffset":28809,"endOffset":29195,"count":0}],"isBlockCoverage":true},{"functionName":"Readable.removeAllListeners","ranges":[{"startOffset":29315,"endOffset":29853,"count":0}],"isBlockCoverage":false},{"functionName":"updateReadableListening","ranges":[{"startOffset":29856,"endOffset":30366,"count":0}],"isBlockCoverage":false},{"functionName":"nReadingNextTick","ranges":[{"startOffset":30368,"endOffset":30456,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.resume","ranges":[{"startOffset":30610,"endOffset":30935,"count":0}],"isBlockCoverage":false},{"functionName":"resume","ranges":[{"startOffset":30938,"endOffset":31088,"count":0}],"isBlockCoverage":false},{"functionName":"resume_","ranges":[{"startOffset":31090,"endOffset":31341,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.pause","ranges":[{"startOffset":31370,"endOffset":31637,"count":0}],"isBlockCoverage":false},{"functionName":"flow","ranges":[{"startOffset":31640,"endOffset":31787,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.wrap","ranges":[{"startOffset":31971,"endOffset":33786,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.","ranges":[{"startOffset":33831,"endOffset":34212,"count":0}],"isBlockCoverage":false},{"functionName":"createAsyncIterator","ranges":[{"startOffset":34215,"endOffset":35508,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":35706,"endOffset":36095,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":36101,"endOffset":36231,"count":1}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":36297,"endOffset":36363,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":36422,"endOffset":36504,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":36564,"endOffset":36624,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":36635,"endOffset":36744,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":36798,"endOffset":36852,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":36910,"endOffset":36998,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":37054,"endOffset":37139,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":37188,"endOffset":37322,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":37328,"endOffset":37617,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":37670,"endOffset":37758,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":37877,"endOffset":37922,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":37980,"endOffset":38031,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":38037,"endOffset":38086,"count":0}],"isBlockCoverage":false},{"functionName":"fromList","ranges":[{"startOffset":38390,"endOffset":38952,"count":0}],"isBlockCoverage":false},{"functionName":"endReadable","ranges":[{"startOffset":38954,"endOffset":39175,"count":0}],"isBlockCoverage":false},{"functionName":"endReadableNT","ranges":[{"startOffset":39177,"endOffset":40109,"count":0}],"isBlockCoverage":false},{"functionName":"endWritableNT","ranges":[{"startOffset":40111,"endOffset":40278,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.from","ranges":[{"startOffset":40296,"endOffset":40442,"count":0}],"isBlockCoverage":false}]},{"scriptId":"82","url":"internal/streams/buffer_list.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":3798,"count":1}],"isBlockCoverage":false},{"functionName":"BufferList","ranges":[{"startOffset":204,"endOffset":288,"count":1}],"isBlockCoverage":true},{"functionName":"push","ranges":[{"startOffset":292,"endOffset":479,"count":0}],"isBlockCoverage":false},{"functionName":"unshift","ranges":[{"startOffset":483,"endOffset":641,"count":0}],"isBlockCoverage":false},{"functionName":"shift","ranges":[{"startOffset":645,"endOffset":872,"count":0}],"isBlockCoverage":false},{"functionName":"clear","ranges":[{"startOffset":876,"endOffset":944,"count":0}],"isBlockCoverage":false},{"functionName":"join","ranges":[{"startOffset":948,"endOffset":1119,"count":0}],"isBlockCoverage":false},{"functionName":"concat","ranges":[{"startOffset":1123,"endOffset":1386,"count":0}],"isBlockCoverage":false},{"functionName":"consume","ranges":[{"startOffset":1470,"endOffset":1924,"count":0}],"isBlockCoverage":false},{"functionName":"first","ranges":[{"startOffset":1928,"endOffset":1968,"count":0}],"isBlockCoverage":false},{"functionName":"module.exports","ranges":[{"startOffset":1972,"endOffset":2068,"count":0}],"isBlockCoverage":false},{"functionName":"_getString","ranges":[{"startOffset":2143,"endOffset":2738,"count":0}],"isBlockCoverage":false},{"functionName":"_getBuffer","ranges":[{"startOffset":2808,"endOffset":3518,"count":0}],"isBlockCoverage":false},{"functionName":"module.exports","ranges":[{"startOffset":3599,"endOffset":3794,"count":0}],"isBlockCoverage":false}]},{"scriptId":"83","url":"internal/streams/state.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":881,"count":1}],"isBlockCoverage":false},{"functionName":"highWaterMarkFrom","ranges":[{"startOffset":142,"endOffset":309,"count":2},{"startOffset":240,"endOffset":263,"count":0},{"startOffset":300,"endOffset":306,"count":0}],"isBlockCoverage":true},{"functionName":"getDefaultHighWaterMark","ranges":[{"startOffset":311,"endOffset":397,"count":2},{"startOffset":378,"endOffset":382,"count":0}],"isBlockCoverage":true},{"functionName":"getHighWaterMark","ranges":[{"startOffset":399,"endOffset":811,"count":2},{"startOffset":546,"endOffset":737,"count":0}],"isBlockCoverage":true}]},{"scriptId":"84","url":"internal/streams/writable.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":22800,"count":1}],"isBlockCoverage":false},{"functionName":"nop","ranges":[{"startOffset":2223,"endOffset":2240,"count":0}],"isBlockCoverage":false},{"functionName":"WritableState","ranges":[{"startOffset":2242,"endOffset":6406,"count":1},{"startOffset":2637,"endOffset":2680,"count":0},{"startOffset":3231,"endOffset":3267,"count":0}],"isBlockCoverage":true},{"functionName":"resetBuffer","ranges":[{"startOffset":6408,"endOffset":6540,"count":1}],"isBlockCoverage":true},{"functionName":"getBuffer","ranges":[{"startOffset":6578,"endOffset":6652,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":6729,"endOffset":6794,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":7121,"endOffset":7335,"count":0}],"isBlockCoverage":false},{"functionName":"realHasInstance","ranges":[{"startOffset":7371,"endOffset":7428,"count":0}],"isBlockCoverage":false},{"functionName":"Writable","ranges":[{"startOffset":7433,"endOffset":8605,"count":1},{"startOffset":8074,"endOffset":8114,"count":0},{"startOffset":8120,"endOffset":8149,"count":0},{"startOffset":8288,"endOffset":8316,"count":0},{"startOffset":8370,"endOffset":8400,"count":0},{"startOffset":8455,"endOffset":8487,"count":0},{"startOffset":8540,"endOffset":8568,"count":0}],"isBlockCoverage":true},{"functionName":"Writable.pipe","ranges":[{"startOffset":8701,"endOffset":8769,"count":0}],"isBlockCoverage":false},{"functionName":"Writable.write","ranges":[{"startOffset":8799,"endOffset":10008,"count":163},{"startOffset":8969,"endOffset":9089,"count":0},{"startOffset":9113,"endOffset":9158,"count":0},{"startOffset":9267,"endOffset":9351,"count":0},{"startOffset":9357,"endOffset":9660,"count":0},{"startOffset":9697,"endOffset":9746,"count":0},{"startOffset":9773,"endOffset":9823,"count":0},{"startOffset":9836,"endOffset":9927,"count":0}],"isBlockCoverage":true},{"functionName":"Writable.cork","ranges":[{"startOffset":10037,"endOffset":10083,"count":0}],"isBlockCoverage":false},{"functionName":"Writable.uncork","ranges":[{"startOffset":10114,"endOffset":10269,"count":0}],"isBlockCoverage":false},{"functionName":"setDefaultEncoding","ranges":[{"startOffset":10312,"endOffset":10623,"count":0}],"isBlockCoverage":false},{"functionName":"writeOrBuffer","ranges":[{"startOffset":10813,"endOffset":11804,"count":163},{"startOffset":10911,"endOffset":10914,"count":0},{"startOffset":11133,"endOffset":11156,"count":0},{"startOffset":11212,"endOffset":11444,"count":0}],"isBlockCoverage":true},{"functionName":"doWrite","ranges":[{"startOffset":11806,"endOffset":12184,"count":0}],"isBlockCoverage":false},{"functionName":"onwriteError","ranges":[{"startOffset":12186,"endOffset":12606,"count":0}],"isBlockCoverage":false},{"functionName":"onwrite","ranges":[{"startOffset":12608,"endOffset":14202,"count":163},{"startOffset":12766,"endOffset":12840,"count":0},{"startOffset":12958,"endOffset":13469,"count":0},{"startOffset":13530,"endOffset":13571,"count":0},{"startOffset":13886,"endOffset":13933,"count":151},{"startOffset":13935,"endOffset":13986,"count":151},{"startOffset":13986,"endOffset":14137,"count":12},{"startOffset":14143,"endOffset":14196,"count":0}],"isBlockCoverage":true},{"functionName":"afterWriteTick","ranges":[{"startOffset":14204,"endOffset":14343,"count":12}],"isBlockCoverage":true},{"functionName":"afterWrite","ranges":[{"startOffset":14345,"endOffset":14755,"count":12},{"startOffset":14511,"endOffset":14571,"count":0},{"startOffset":14595,"endOffset":14633,"count":163},{"startOffset":14658,"endOffset":14722,"count":0}],"isBlockCoverage":true},{"functionName":"errorBuffer","ranges":[{"startOffset":14827,"endOffset":15148,"count":0}],"isBlockCoverage":false},{"functionName":"clearBuffer","ranges":[{"startOffset":15214,"endOffset":16647,"count":0}],"isBlockCoverage":false},{"functionName":"Writable._write","ranges":[{"startOffset":16677,"endOffset":16846,"count":0}],"isBlockCoverage":false},{"functionName":"Writable.end","ranges":[{"startOffset":16910,"endOffset":18094,"count":0}],"isBlockCoverage":false},{"functionName":"needFinish","ranges":[{"startOffset":18097,"endOffset":18310,"count":12},{"startOffset":18149,"endOffset":18180,"count":0},{"startOffset":18181,"endOffset":18208,"count":0},{"startOffset":18209,"endOffset":18249,"count":0},{"startOffset":18250,"endOffset":18278,"count":0},{"startOffset":18279,"endOffset":18306,"count":0}],"isBlockCoverage":true},{"functionName":"callFinal","ranges":[{"startOffset":18312,"endOffset":18572,"count":0}],"isBlockCoverage":false},{"functionName":"prefinish","ranges":[{"startOffset":18574,"endOffset":18922,"count":0}],"isBlockCoverage":false},{"functionName":"finishMaybe","ranges":[{"startOffset":18924,"endOffset":19251,"count":12},{"startOffset":19014,"endOffset":19234,"count":0}],"isBlockCoverage":true},{"functionName":"finish","ranges":[{"startOffset":19253,"endOffset":19871,"count":0}],"isBlockCoverage":false},{"functionName":"onFinished","ranges":[{"startOffset":19937,"endOffset":20401,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":20468,"endOffset":20555,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":20561,"endOffset":20743,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":20768,"endOffset":21160,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":21166,"endOffset":21300,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":21333,"endOffset":21419,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":21454,"endOffset":21542,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":21573,"endOffset":21655,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":21685,"endOffset":21769,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":21803,"endOffset":21961,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":21999,"endOffset":22083,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":22114,"endOffset":22194,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":22225,"endOffset":22302,"count":0}],"isBlockCoverage":false},{"functionName":"Writable.destroy","ranges":[{"startOffset":22378,"endOffset":22589,"count":0}],"isBlockCoverage":false},{"functionName":"Writable._destroy","ranges":[{"startOffset":22677,"endOffset":22709,"count":0}],"isBlockCoverage":false},{"functionName":"Writable.","ranges":[{"startOffset":22760,"endOffset":22798,"count":0}],"isBlockCoverage":false}]},{"scriptId":"85","url":"internal/streams/duplex.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":3759,"count":1}],"isBlockCoverage":false},{"functionName":"Duplex","ranges":[{"startOffset":1936,"endOffset":2360,"count":1},{"startOffset":2000,"endOffset":2027,"count":0},{"startOffset":2248,"endOffset":2270,"count":0},{"startOffset":2313,"endOffset":2354,"count":0}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":3271,"endOffset":3483,"count":12},{"startOffset":3369,"endOffset":3400,"count":0},{"startOffset":3444,"endOffset":3476,"count":0}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":3489,"endOffset":3750,"count":0}],"isBlockCoverage":false}]},{"scriptId":"86","url":"internal/streams/transform.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":8217,"count":1}],"isBlockCoverage":false},{"functionName":"afterTransform","ranges":[{"startOffset":4032,"endOffset":4550,"count":0}],"isBlockCoverage":false},{"functionName":"Transform","ranges":[{"startOffset":4553,"endOffset":5382,"count":0}],"isBlockCoverage":false},{"functionName":"prefinish","ranges":[{"startOffset":5384,"endOffset":5596,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":5691,"endOffset":5741,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":5838,"endOffset":5890,"count":0}],"isBlockCoverage":false},{"functionName":"Transform.push","ranges":[{"startOffset":5988,"endOffset":6124,"count":0}],"isBlockCoverage":false},{"functionName":"Transform._transform","ranges":[{"startOffset":6607,"endOffset":6696,"count":0}],"isBlockCoverage":false},{"functionName":"Transform._write","ranges":[{"startOffset":6728,"endOffset":7067,"count":0}],"isBlockCoverage":false},{"functionName":"Transform._read","ranges":[{"startOffset":7239,"endOffset":7613,"count":0}],"isBlockCoverage":false},{"functionName":"Transform._destroy","ranges":[{"startOffset":7648,"endOffset":7745,"count":0}],"isBlockCoverage":false},{"functionName":"done","ranges":[{"startOffset":7749,"endOffset":8216,"count":0}],"isBlockCoverage":false}]},{"scriptId":"87","url":"internal/streams/passthrough.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1762,"count":1}],"isBlockCoverage":false},{"functionName":"PassThrough","ranges":[{"startOffset":1529,"endOffset":1671,"count":0}],"isBlockCoverage":false},{"functionName":"PassThrough._transform","ranges":[{"startOffset":1708,"endOffset":1760,"count":0}],"isBlockCoverage":false}]},{"scriptId":"88","url":"file:///home/runner/work/jslint/jslint/jslint.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":165592,"count":1}],"isBlockCoverage":true},{"functionName":"assert_or_throw","ranges":[{"startOffset":7096,"endOffset":7423,"count":7925},{"startOffset":7218,"endOffset":7421,"count":1}],"isBlockCoverage":true},{"functionName":"empty","ranges":[{"startOffset":7425,"endOffset":7681,"count":3371}],"isBlockCoverage":true},{"functionName":"populate","ranges":[{"startOffset":7683,"endOffset":7910,"count":762}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":7834,"endOffset":7887,"count":17758}],"isBlockCoverage":true},{"functionName":"tag_regexp","ranges":[{"startOffset":16897,"endOffset":16987,"count":12}],"isBlockCoverage":true},{"functionName":"is_letter","ranges":[{"startOffset":18791,"endOffset":18938,"count":167},{"startOffset":18856,"endOffset":18878,"count":77},{"startOffset":18888,"endOffset":18929,"count":93},{"startOffset":18906,"endOffset":18928,"count":3}],"isBlockCoverage":true},{"functionName":"artifact","ranges":[{"startOffset":20827,"endOffset":21125,"count":771},{"startOffset":20938,"endOffset":20977,"count":15},{"startOffset":21028,"endOffset":21058,"count":764},{"startOffset":21068,"endOffset":21093,"count":75},{"startOffset":21102,"endOffset":21116,"count":696}],"isBlockCoverage":true},{"functionName":"warn_at","ranges":[{"startOffset":21127,"endOffset":22099,"count":811},{"startOffset":21425,"endOffset":21455,"count":794},{"startOffset":21481,"endOffset":21511,"count":308},{"startOffset":21537,"endOffset":21567,"count":30},{"startOffset":21593,"endOffset":21623,"count":3},{"startOffset":21993,"endOffset":22049,"count":4}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":21680,"endOffset":21904,"count":1047}],"isBlockCoverage":true},{"functionName":"stop_at","ranges":[{"startOffset":22101,"endOffset":22261,"count":17}],"isBlockCoverage":true},{"functionName":"warn","ranges":[{"startOffset":22263,"endOffset":22890,"count":756},{"startOffset":22562,"endOffset":22601,"count":11},{"startOffset":22643,"endOffset":22888,"count":606},{"startOffset":22770,"endOffset":22792,"count":458}],"isBlockCoverage":true},{"functionName":"stop","ranges":[{"startOffset":22892,"endOffset":23279,"count":72},{"startOffset":23163,"endOffset":23202,"count":13}],"isBlockCoverage":true},{"functionName":"tokenize","ranges":[{"startOffset":23295,"endOffset":52748,"count":368},{"startOffset":23894,"endOffset":23902,"count":1},{"startOffset":23911,"endOffset":23934,"count":367},{"startOffset":24929,"endOffset":24978,"count":3},{"startOffset":52512,"endOffset":52531,"count":340},{"startOffset":52674,"endOffset":52746,"count":56293},{"startOffset":52710,"endOffset":52740,"count":342}],"isBlockCoverage":true},{"functionName":"next_line","ranges":[{"startOffset":24984,"endOffset":26184,"count":16532},{"startOffset":25270,"endOffset":25295,"count":16529},{"startOffset":25308,"endOffset":25321,"count":1},{"startOffset":25334,"endOffset":25342,"count":1},{"startOffset":25355,"endOffset":25370,"count":1},{"startOffset":25381,"endOffset":25435,"count":1},{"startOffset":25572,"endOffset":25577,"count":4147},{"startOffset":25618,"endOffset":26150,"count":16181},{"startOffset":25690,"endOffset":25898,"count":2},{"startOffset":25727,"endOffset":25820,"count":1},{"startOffset":25929,"endOffset":25961,"count":16179},{"startOffset":25963,"endOffset":26140,"count":1}],"isBlockCoverage":true},{"functionName":"snip","ranges":[{"startOffset":26638,"endOffset":26746,"count":5114}],"isBlockCoverage":true},{"functionName":"next_char","ranges":[{"startOffset":26752,"endOffset":27575,"count":36336},{"startOffset":27002,"endOffset":27019,"count":782},{"startOffset":27021,"endOffset":27310,"count":3},{"startOffset":27121,"endOffset":27135,"count":2},{"startOffset":27156,"endOffset":27172,"count":1},{"startOffset":27310,"endOffset":27336,"count":36333},{"startOffset":27336,"endOffset":27459,"count":36176},{"startOffset":27459,"endOffset":27527,"count":157},{"startOffset":27527,"endOffset":27574,"count":36333}],"isBlockCoverage":true},{"functionName":"back_char","ranges":[{"startOffset":27581,"endOffset":27866,"count":1170}],"isBlockCoverage":true},{"functionName":"some_digits","ranges":[{"startOffset":27872,"endOffset":28280,"count":52},{"startOffset":28012,"endOffset":28027,"count":16},{"startOffset":28029,"endOffset":28128,"count":1}],"isBlockCoverage":true},{"functionName":"escape","ranges":[{"startOffset":28286,"endOffset":29468,"count":413},{"startOffset":28375,"endOffset":28418,"count":233},{"startOffset":28418,"endOffset":28444,"count":180},{"startOffset":28444,"endOffset":28535,"count":1},{"startOffset":28535,"endOffset":28562,"count":179},{"startOffset":28562,"endOffset":29277,"count":35},{"startOffset":28604,"endOffset":29068,"count":3},{"startOffset":28637,"endOffset":28725,"count":1},{"startOffset":28772,"endOffset":28887,"count":1},{"startOffset":28922,"endOffset":29018,"count":1},{"startOffset":29018,"endOffset":29068,"count":2},{"startOffset":29068,"endOffset":29142,"count":32},{"startOffset":29142,"endOffset":29247,"count":1},{"startOffset":29247,"endOffset":29277,"count":32},{"startOffset":29277,"endOffset":29323,"count":144},{"startOffset":29325,"endOffset":29368,"count":143},{"startOffset":29368,"endOffset":29467,"count":1}],"isBlockCoverage":true},{"functionName":"make","ranges":[{"startOffset":29474,"endOffset":31402,"count":56996},{"startOffset":29887,"endOffset":29900,"count":55486},{"startOffset":29902,"endOffset":29949,"count":51169},{"startOffset":30034,"endOffset":30082,"count":6593},{"startOffset":30417,"endOffset":30442,"count":45014},{"startOffset":30455,"endOffset":30513,"count":28690},{"startOffset":30478,"endOffset":30498,"count":28662},{"startOffset":30499,"endOffset":30512,"count":28646},{"startOffset":30526,"endOffset":30588,"count":49},{"startOffset":30558,"endOffset":30587,"count":48},{"startOffset":30599,"endOffset":30780,"count":1},{"startOffset":30813,"endOffset":30833,"count":3333},{"startOffset":30835,"endOffset":30924,"count":3},{"startOffset":30954,"endOffset":30977,"count":3333},{"startOffset":30979,"endOffset":31024,"count":3328},{"startOffset":31329,"endOffset":31370,"count":55486}],"isBlockCoverage":true},{"functionName":"parse_directive","ranges":[{"startOffset":31408,"endOffset":33278,"count":595},{"startOffset":31695,"endOffset":33152,"count":566},{"startOffset":31847,"endOffset":32574,"count":21},{"startOffset":31987,"endOffset":32017,"count":12},{"startOffset":32036,"endOffset":32439,"count":20},{"startOffset":32103,"endOffset":32323,"count":19},{"startOffset":32202,"endOffset":32301,"count":10},{"startOffset":32323,"endOffset":32421,"count":1},{"startOffset":32439,"endOffset":32560,"count":1},{"startOffset":32574,"endOffset":33082,"count":545},{"startOffset":32622,"endOffset":32774,"count":542},{"startOffset":32666,"endOffset":32723,"count":5},{"startOffset":32774,"endOffset":33082,"count":3},{"startOffset":32849,"endOffset":32977,"count":1},{"startOffset":33152,"endOffset":33171,"count":29},{"startOffset":33171,"endOffset":33272,"count":1}],"isBlockCoverage":true},{"functionName":"comment","ranges":[{"startOffset":33284,"endOffset":34166,"count":1510},{"startOffset":33535,"endOffset":33587,"count":47},{"startOffset":33614,"endOffset":33638,"count":1505},{"startOffset":33640,"endOffset":33724,"count":1},{"startOffset":33797,"endOffset":34132,"count":30},{"startOffset":33832,"endOffset":33951,"count":1},{"startOffset":33951,"endOffset":34132,"count":29},{"startOffset":34132,"endOffset":34165,"count":1509}],"isBlockCoverage":true},{"functionName":"regexp","ranges":[{"startOffset":34172,"endOffset":41327,"count":97},{"startOffset":40171,"endOffset":40251,"count":1},{"startOffset":40992,"endOffset":41007,"count":92},{"startOffset":41009,"endOffset":41082,"count":1},{"startOffset":41082,"endOffset":41205,"count":92},{"startOffset":41205,"endOffset":41215,"count":9},{"startOffset":41217,"endOffset":41298,"count":1},{"startOffset":41298,"endOffset":41326,"count":92}],"isBlockCoverage":true},{"functionName":"quantifier","ranges":[{"startOffset":34341,"endOffset":34966,"count":546},{"startOffset":34429,"endOffset":34444,"count":540},{"startOffset":34445,"endOffset":34460,"count":515},{"startOffset":34462,"endOffset":34506,"count":46},{"startOffset":34506,"endOffset":34878,"count":500},{"startOffset":34530,"endOffset":34833,"count":3},{"startOffset":34588,"endOffset":34683,"count":1},{"startOffset":34718,"endOffset":34787,"count":1},{"startOffset":34833,"endOffset":34878,"count":497},{"startOffset":34878,"endOffset":34909,"count":48},{"startOffset":34909,"endOffset":34956,"count":28}],"isBlockCoverage":true},{"functionName":"subklass","ranges":[{"startOffset":34976,"endOffset":35712,"count":103},{"startOffset":35074,"endOffset":35158,"count":23},{"startOffset":35158,"endOffset":35220,"count":80},{"startOffset":35220,"endOffset":35235,"count":79},{"startOffset":35252,"endOffset":35267,"count":79},{"startOffset":35284,"endOffset":35299,"count":48},{"startOffset":35316,"endOffset":35331,"count":47},{"startOffset":35348,"endOffset":35363,"count":47},{"startOffset":35378,"endOffset":35423,"count":33},{"startOffset":35423,"endOffset":35454,"count":47},{"startOffset":35454,"endOffset":35540,"count":1},{"startOffset":35540,"endOffset":35652,"count":46},{"startOffset":35563,"endOffset":35575,"count":3},{"startOffset":35577,"endOffset":35652,"count":1},{"startOffset":35652,"endOffset":35711,"count":47}],"isBlockCoverage":true},{"functionName":"ranges","ranges":[{"startOffset":35722,"endOffset":36230,"count":90},{"startOffset":35804,"endOffset":36220,"count":58},{"startOffset":35840,"endOffset":36173,"count":13},{"startOffset":35915,"endOffset":36155,"count":1},{"startOffset":36173,"endOffset":36220,"count":57}],"isBlockCoverage":true},{"functionName":"klass","ranges":[{"startOffset":36240,"endOffset":36858,"count":32},{"startOffset":36337,"endOffset":36384,"count":3}],"isBlockCoverage":true},{"functionName":"classy","ranges":[{"startOffset":36398,"endOffset":36816,"count":33},{"startOffset":36477,"endOffset":36491,"count":2},{"startOffset":36493,"endOffset":36802,"count":1}],"isBlockCoverage":true},{"functionName":"choice","ranges":[{"startOffset":36868,"endOffset":39971,"count":120},{"startOffset":39881,"endOffset":39961,"count":0}],"isBlockCoverage":true},{"functionName":"group","ranges":[{"startOffset":36901,"endOffset":37464,"count":23},{"startOffset":37034,"endOffset":37273,"count":7},{"startOffset":37109,"endOffset":37124,"count":6},{"startOffset":37126,"endOffset":37186,"count":2},{"startOffset":37186,"endOffset":37255,"count":5},{"startOffset":37273,"endOffset":37392,"count":16},{"startOffset":37297,"endOffset":37392,"count":1}],"isBlockCoverage":true},{"functionName":"factor","ranges":[{"startOffset":37478,"endOffset":39421,"count":665},{"startOffset":37611,"endOffset":37626,"count":664},{"startOffset":37647,"endOffset":37662,"count":571},{"startOffset":37683,"endOffset":37698,"count":571},{"startOffset":37717,"endOffset":37770,"count":117},{"startOffset":37770,"endOffset":37805,"count":548},{"startOffset":37805,"endOffset":37886,"count":23},{"startOffset":37886,"endOffset":37921,"count":525},{"startOffset":37921,"endOffset":38002,"count":32},{"startOffset":38002,"endOffset":38038,"count":493},{"startOffset":38038,"endOffset":38146,"count":100},{"startOffset":38146,"endOffset":38221,"count":393},{"startOffset":38221,"endOffset":38236,"count":392},{"startOffset":38257,"endOffset":38272,"count":392},{"startOffset":38293,"endOffset":38308,"count":392},{"startOffset":38329,"endOffset":38344,"count":392},{"startOffset":38363,"endOffset":38606,"count":1},{"startOffset":38606,"endOffset":39349,"count":392},{"startOffset":38630,"endOffset":38780,"count":13},{"startOffset":38667,"endOffset":38762,"count":1},{"startOffset":38780,"endOffset":39349,"count":379},{"startOffset":38804,"endOffset":39040,"count":1},{"startOffset":39040,"endOffset":39349,"count":378},{"startOffset":39064,"endOffset":39198,"count":24},{"startOffset":39114,"endOffset":39180,"count":8},{"startOffset":39198,"endOffset":39349,"count":354},{"startOffset":39222,"endOffset":39349,"count":24},{"startOffset":39265,"endOffset":39331,"count":3},{"startOffset":39349,"endOffset":39420,"count":393}],"isBlockCoverage":true},{"functionName":"sequence","ranges":[{"startOffset":39435,"endOffset":39747,"count":665},{"startOffset":39493,"endOffset":39589,"count":546},{"startOffset":39589,"endOffset":39619,"count":117},{"startOffset":39619,"endOffset":39733,"count":1}],"isBlockCoverage":true},{"functionName":"make_flag","ranges":[{"startOffset":40592,"endOffset":40941,"count":167},{"startOffset":40648,"endOffset":40931,"count":74},{"startOffset":40694,"endOffset":40778,"count":1}],"isBlockCoverage":true},{"functionName":"string","ranges":[{"startOffset":41333,"endOffset":42174,"count":3853}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":41467,"endOffset":42164,"count":33778},{"startOffset":41517,"endOffset":41686,"count":3850},{"startOffset":41686,"endOffset":41716,"count":29928},{"startOffset":41716,"endOffset":41813,"count":1},{"startOffset":41813,"endOffset":41845,"count":29927},{"startOffset":41845,"endOffset":41891,"count":290},{"startOffset":41891,"endOffset":42127,"count":29637},{"startOffset":41915,"endOffset":42077,"count":72},{"startOffset":41948,"endOffset":42031,"count":1},{"startOffset":42077,"endOffset":42127,"count":29565},{"startOffset":42127,"endOffset":42163,"count":29925}],"isBlockCoverage":true},{"functionName":"frack","ranges":[{"startOffset":42180,"endOffset":42484,"count":472},{"startOffset":42225,"endOffset":42272,"count":6},{"startOffset":42315,"endOffset":42478,"count":1}],"isBlockCoverage":true},{"functionName":"number","ranges":[{"startOffset":42490,"endOffset":43503,"count":1045},{"startOffset":42539,"endOffset":42879,"count":577},{"startOffset":42596,"endOffset":42636,"count":4},{"startOffset":42636,"endOffset":42869,"count":573},{"startOffset":42660,"endOffset":42713,"count":1},{"startOffset":42713,"endOffset":42869,"count":572},{"startOffset":42737,"endOffset":42792,"count":1},{"startOffset":42792,"endOffset":42869,"count":571},{"startOffset":42816,"endOffset":42869,"count":4},{"startOffset":42879,"endOffset":42942,"count":468},{"startOffset":43087,"endOffset":43101,"count":469},{"startOffset":43131,"endOffset":43145,"count":19},{"startOffset":43159,"endOffset":43190,"count":1044},{"startOffset":43175,"endOffset":43189,"count":273},{"startOffset":43201,"endOffset":43434,"count":1},{"startOffset":43434,"endOffset":43502,"count":1044}],"isBlockCoverage":true},{"functionName":"lex","ranges":[{"startOffset":43509,"endOffset":52458,"count":82412},{"startOffset":44334,"endOffset":44644,"count":16093},{"startOffset":44440,"endOffset":44634,"count":347},{"startOffset":44517,"endOffset":44565,"count":1},{"startOffset":44586,"endOffset":44601,"count":346},{"startOffset":44644,"endOffset":44843,"count":82065},{"startOffset":44843,"endOffset":45027,"count":1},{"startOffset":45027,"endOffset":45205,"count":82064},{"startOffset":45205,"endOffset":45242,"count":25595},{"startOffset":45242,"endOffset":45299,"count":56469},{"startOffset":45299,"endOffset":45361,"count":18464},{"startOffset":45361,"endOffset":45413,"count":38005},{"startOffset":45413,"endOffset":45460,"count":1045},{"startOffset":45460,"endOffset":45519,"count":36960},{"startOffset":45519,"endOffset":45566,"count":3851},{"startOffset":45566,"endOffset":45596,"count":33109},{"startOffset":45596,"endOffset":45761,"count":2},{"startOffset":45761,"endOffset":45864,"count":33107},{"startOffset":45864,"endOffset":48457,"count":63},{"startOffset":45893,"endOffset":45980,"count":1},{"startOffset":45980,"endOffset":48457,"count":62},{"startOffset":48457,"endOffset":48520,"count":33044},{"startOffset":48520,"endOffset":48793,"count":1463},{"startOffset":48656,"endOffset":48753,"count":1},{"startOffset":48793,"endOffset":48856,"count":31581},{"startOffset":48856,"endOffset":50111,"count":50},{"startOffset":48922,"endOffset":49001,"count":3},{"startOffset":49855,"endOffset":49952,"count":1},{"startOffset":49952,"endOffset":50111,"count":47},{"startOffset":50111,"endOffset":50168,"count":31531},{"startOffset":50168,"endOffset":52422,"count":107},{"startOffset":50787,"endOffset":51772,"count":13},{"startOffset":50821,"endOffset":51758,"count":11},{"startOffset":50870,"endOffset":50934,"count":1},{"startOffset":50934,"endOffset":51053,"count":10},{"startOffset":51078,"endOffset":51102,"count":9},{"startOffset":51127,"endOffset":51147,"count":8},{"startOffset":51172,"endOffset":51200,"count":7},{"startOffset":51225,"endOffset":51246,"count":6},{"startOffset":51271,"endOffset":51295,"count":5},{"startOffset":51320,"endOffset":51342,"count":4},{"startOffset":51367,"endOffset":51390,"count":3},{"startOffset":51413,"endOffset":51740,"count":8},{"startOffset":51772,"endOffset":52182,"count":94},{"startOffset":51883,"endOffset":51939,"count":83},{"startOffset":51939,"endOffset":51998,"count":11},{"startOffset":51998,"endOffset":52168,"count":5},{"startOffset":52182,"endOffset":52223,"count":10},{"startOffset":52223,"endOffset":52412,"count":2},{"startOffset":52422,"endOffset":52457,"count":31434}],"isBlockCoverage":true},{"functionName":"part","ranges":[{"startOffset":46381,"endOffset":48309,"count":485},{"startOffset":46556,"endOffset":46850,"count":312},{"startOffset":46728,"endOffset":46776,"count":1},{"startOffset":46801,"endOffset":46809,"count":311},{"startOffset":46850,"endOffset":47032,"count":173},{"startOffset":47032,"endOffset":47231,"count":76},{"startOffset":47231,"endOffset":47557,"count":97},{"startOffset":47557,"endOffset":48295,"count":39}],"isBlockCoverage":true},{"functionName":"expr","ranges":[{"startOffset":47701,"endOffset":48238,"count":156},{"startOffset":47804,"endOffset":48105,"count":1},{"startOffset":48105,"endOffset":48146,"count":153},{"startOffset":48146,"endOffset":48216,"count":117}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":49015,"endOffset":49723,"count":174},{"startOffset":49071,"endOffset":49440,"count":159},{"startOffset":49164,"endOffset":49219,"count":47},{"startOffset":49219,"endOffset":49311,"count":112},{"startOffset":49311,"endOffset":49422,"count":1},{"startOffset":49440,"endOffset":49572,"count":127},{"startOffset":49572,"endOffset":49678,"count":3},{"startOffset":49678,"endOffset":49722,"count":124}],"isBlockCoverage":true},{"functionName":"survey","ranges":[{"startOffset":53155,"endOffset":54460,"count":3892},{"startOffset":53337,"endOffset":53441,"count":10},{"startOffset":53441,"endOffset":53728,"count":3882},{"startOffset":53463,"endOffset":53641,"count":2},{"startOffset":53583,"endOffset":53625,"count":1},{"startOffset":53641,"endOffset":53728,"count":3880},{"startOffset":53669,"endOffset":53728,"count":1},{"startOffset":53728,"endOffset":53831,"count":3880},{"startOffset":53831,"endOffset":54054,"count":3082},{"startOffset":54054,"endOffset":54443,"count":798},{"startOffset":54096,"endOffset":54252,"count":538},{"startOffset":54135,"endOffset":54242,"count":1},{"startOffset":54252,"endOffset":54411,"count":260},{"startOffset":54292,"endOffset":54319,"count":259},{"startOffset":54321,"endOffset":54401,"count":1},{"startOffset":54443,"endOffset":54459,"count":3880}],"isBlockCoverage":true},{"functionName":"dispense","ranges":[{"startOffset":54462,"endOffset":54772,"count":57514},{"startOffset":54625,"endOffset":54735,"count":1508},{"startOffset":54650,"endOffset":54702,"count":1},{"startOffset":54735,"endOffset":54770,"count":56006}],"isBlockCoverage":true},{"functionName":"lookahead","ranges":[{"startOffset":54774,"endOffset":54959,"count":392}],"isBlockCoverage":true},{"functionName":"advance","ranges":[{"startOffset":54961,"endOffset":55989,"count":55630},{"startOffset":55103,"endOffset":55129,"count":18440},{"startOffset":55131,"endOffset":55163,"count":17853},{"startOffset":55163,"endOffset":55268,"count":37777},{"startOffset":55197,"endOffset":55231,"count":3944},{"startOffset":55233,"endOffset":55268,"count":1747},{"startOffset":55348,"endOffset":55371,"count":20759},{"startOffset":55373,"endOffset":55824,"count":16},{"startOffset":55453,"endOffset":55503,"count":13},{"startOffset":55540,"endOffset":55807,"count":3},{"startOffset":55824,"endOffset":55957,"count":55614},{"startOffset":55957,"endOffset":55987,"count":587}],"isBlockCoverage":true},{"functionName":"json_value","ranges":[{"startOffset":56022,"endOffset":59125,"count":41},{"startOffset":56095,"endOffset":57616,"count":15},{"startOffset":57616,"endOffset":57648,"count":26},{"startOffset":57648,"endOffset":58223,"count":9},{"startOffset":58223,"endOffset":58302,"count":17},{"startOffset":58311,"endOffset":58338,"count":17},{"startOffset":58345,"endOffset":58393,"count":1},{"startOffset":58393,"endOffset":58432,"count":16},{"startOffset":58432,"endOffset":58578,"count":9},{"startOffset":58486,"endOffset":58531,"count":1},{"startOffset":58578,"endOffset":58617,"count":7},{"startOffset":58617,"endOffset":58780,"count":3},{"startOffset":58658,"endOffset":58733,"count":1},{"startOffset":58780,"endOffset":58812,"count":4},{"startOffset":58812,"endOffset":59097,"count":2},{"startOffset":58977,"endOffset":59029,"count":1},{"startOffset":59097,"endOffset":59124,"count":2}],"isBlockCoverage":true},{"functionName":"json_object","ranges":[{"startOffset":56113,"endOffset":57606,"count":15},{"startOffset":56355,"endOffset":57537,"count":13},{"startOffset":57537,"endOffset":57605,"count":9}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":56374,"endOffset":57519,"count":15},{"startOffset":56504,"endOffset":56713,"count":5},{"startOffset":56853,"endOffset":56962,"count":1},{"startOffset":56962,"endOffset":57193,"count":9},{"startOffset":57001,"endOffset":57111,"count":1},{"startOffset":57111,"endOffset":57193,"count":8},{"startOffset":57193,"endOffset":57401,"count":10},{"startOffset":57401,"endOffset":57501,"count":2}],"isBlockCoverage":true},{"functionName":"json_array","ranges":[{"startOffset":57666,"endOffset":58213,"count":9},{"startOffset":57871,"endOffset":58140,"count":7},{"startOffset":58140,"endOffset":58212,"count":8}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":57890,"endOffset":58122,"count":9},{"startOffset":58004,"endOffset":58104,"count":2}],"isBlockCoverage":true},{"functionName":"enroll","ranges":[{"startOffset":59156,"endOffset":61373,"count":1577},{"startOffset":59485,"endOffset":59503,"count":22},{"startOffset":59505,"endOffset":59574,"count":1},{"startOffset":59574,"endOffset":61371,"count":1576},{"startOffset":59699,"endOffset":59944,"count":5},{"startOffset":59944,"endOffset":61365,"count":1571},{"startOffset":60182,"endOffset":61099,"count":36},{"startOffset":60221,"endOffset":60417,"count":4},{"startOffset":60276,"endOffset":60399,"count":3},{"startOffset":60417,"endOffset":61085,"count":32},{"startOffset":60553,"endOffset":60584,"count":1},{"startOffset":60683,"endOffset":60705,"count":2},{"startOffset":60728,"endOffset":61067,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":59978,"endOffset":60154,"count":1858},{"startOffset":60085,"endOffset":60140,"count":38}],"isBlockCoverage":true},{"functionName":"expression","ranges":[{"startOffset":61375,"endOffset":62713,"count":15648},{"startOffset":62041,"endOffset":62067,"count":12433},{"startOffset":62136,"endOffset":62167,"count":6921},{"startOffset":62169,"endOffset":62209,"count":6908},{"startOffset":62209,"endOffset":62372,"count":8740},{"startOffset":62237,"endOffset":62299,"count":8726},{"startOffset":62299,"endOffset":62372,"count":14},{"startOffset":62372,"endOffset":62712,"count":15622}],"isBlockCoverage":true},{"functionName":"right","ranges":[{"startOffset":62378,"endOffset":62690,"count":26258},{"startOffset":62503,"endOffset":62534,"count":26037},{"startOffset":62547,"endOffset":62570,"count":11704},{"startOffset":62581,"endOffset":62684,"count":10639}],"isBlockCoverage":true},{"functionName":"condition","ranges":[{"startOffset":62715,"endOffset":63205,"count":1222},{"startOffset":63038,"endOffset":63086,"count":1},{"startOffset":63086,"endOffset":63133,"count":1219},{"startOffset":63133,"endOffset":63181,"count":13},{"startOffset":63181,"endOffset":63204,"count":1219}],"isBlockCoverage":true},{"functionName":"is_weird","ranges":[{"startOffset":63207,"endOffset":63436,"count":10461},{"startOffset":63379,"endOffset":63427,"count":10457},{"startOffset":63400,"endOffset":63426,"count":180}],"isBlockCoverage":true},{"functionName":"are_similar","ranges":[{"startOffset":63438,"endOffset":65889,"count":3296},{"startOffset":63609,"endOffset":63856,"count":8},{"startOffset":63856,"endOffset":64012,"count":3288},{"startOffset":64012,"endOffset":64034,"count":33},{"startOffset":64036,"endOffset":64079,"count":31},{"startOffset":64079,"endOffset":64145,"count":3257},{"startOffset":64145,"endOffset":64180,"count":192},{"startOffset":64180,"endOffset":64256,"count":3065},{"startOffset":64203,"endOffset":64216,"count":9},{"startOffset":64218,"endOffset":64256,"count":8},{"startOffset":64256,"endOffset":64286,"count":3257},{"startOffset":64286,"endOffset":64321,"count":1312},{"startOffset":64321,"endOffset":64397,"count":1945},{"startOffset":64344,"endOffset":64357,"count":8},{"startOffset":64359,"endOffset":64397,"count":8},{"startOffset":64397,"endOffset":64436,"count":3257},{"startOffset":64436,"endOffset":64481,"count":192},{"startOffset":64481,"endOffset":64516,"count":3065},{"startOffset":64518,"endOffset":64547,"count":4},{"startOffset":64547,"endOffset":64576,"count":3061},{"startOffset":64576,"endOffset":64592,"count":1386},{"startOffset":64594,"endOffset":65839,"count":818},{"startOffset":64649,"endOffset":64799,"count":219},{"startOffset":64744,"endOffset":64774,"count":139},{"startOffset":64799,"endOffset":64854,"count":599},{"startOffset":64854,"endOffset":64925,"count":13},{"startOffset":64925,"endOffset":64960,"count":586},{"startOffset":64960,"endOffset":65193,"count":290},{"startOffset":65055,"endOffset":65103,"count":264},{"startOffset":65120,"endOffset":65168,"count":151},{"startOffset":65193,"endOffset":65229,"count":296},{"startOffset":65229,"endOffset":65508,"count":3},{"startOffset":65370,"endOffset":65418,"count":2},{"startOffset":65435,"endOffset":65483,"count":2},{"startOffset":65508,"endOffset":65592,"count":293},{"startOffset":65839,"endOffset":65888,"count":2243}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":63717,"endOffset":63838,"count":2}],"isBlockCoverage":true},{"functionName":"semicolon","ranges":[{"startOffset":65891,"endOffset":66231,"count":3895},{"startOffset":65976,"endOffset":66005,"count":3738},{"startOffset":66005,"endOffset":66205,"count":157}],"isBlockCoverage":true},{"functionName":"statement","ranges":[{"startOffset":66233,"endOffset":67968,"count":6014},{"startOffset":66587,"endOffset":66611,"count":5869},{"startOffset":66613,"endOffset":67322,"count":11},{"startOffset":66681,"endOffset":66737,"count":1},{"startOffset":66940,"endOffset":67232,"count":7},{"startOffset":67232,"endOffset":67322,"count":4},{"startOffset":67322,"endOffset":67464,"count":6007},{"startOffset":67464,"endOffset":67495,"count":3052},{"startOffset":67497,"endOffset":67619,"count":2877},{"startOffset":67619,"endOffset":67868,"count":3130},{"startOffset":67742,"endOffset":67769,"count":70},{"startOffset":67771,"endOffset":67841,"count":11},{"startOffset":67841,"endOffset":67868,"count":3110},{"startOffset":67868,"endOffset":67902,"count":5950},{"startOffset":67902,"endOffset":67940,"count":1},{"startOffset":67940,"endOffset":67967,"count":5950}],"isBlockCoverage":true},{"functionName":"statements","ranges":[{"startOffset":67970,"endOffset":68676,"count":2349}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":68132,"endOffset":68647,"count":8158},{"startOffset":68216,"endOffset":68243,"count":6149},{"startOffset":68256,"endOffset":68286,"count":6142},{"startOffset":68299,"endOffset":68326,"count":6135},{"startOffset":68339,"endOffset":68367,"count":6135},{"startOffset":68378,"endOffset":68641,"count":5866},{"startOffset":68485,"endOffset":68585,"count":1},{"startOffset":68585,"endOffset":68641,"count":5809}],"isBlockCoverage":true},{"functionName":"not_top_level","ranges":[{"startOffset":68678,"endOffset":68858,"count":582},{"startOffset":68799,"endOffset":68856,"count":24}],"isBlockCoverage":true},{"functionName":"top_level_only","ranges":[{"startOffset":68860,"endOffset":69075,"count":22},{"startOffset":68982,"endOffset":69073,"count":1}],"isBlockCoverage":true},{"functionName":"block","ranges":[{"startOffset":69077,"endOffset":70205,"count":2004},{"startOffset":69417,"endOffset":69446,"count":1994},{"startOffset":69658,"endOffset":69679,"count":590},{"startOffset":69688,"endOffset":69724,"count":339},{"startOffset":69731,"endOffset":69826,"count":5},{"startOffset":69910,"endOffset":70089,"count":50},{"startOffset":69963,"endOffset":70048,"count":45},{"startOffset":70089,"endOffset":70163,"count":1952},{"startOffset":70163,"endOffset":70204,"count":2002}],"isBlockCoverage":true},{"functionName":"mutation_check","ranges":[{"startOffset":70207,"endOffset":70638,"count":1278},{"startOffset":70433,"endOffset":70456,"count":616},{"startOffset":70465,"endOffset":70488,"count":53},{"startOffset":70497,"endOffset":70520,"count":1},{"startOffset":70527,"endOffset":70619,"count":1},{"startOffset":70619,"endOffset":70637,"count":1277}],"isBlockCoverage":true},{"functionName":"left_check","ranges":[{"startOffset":70640,"endOffset":71254,"count":6556},{"startOffset":70851,"endOffset":71032,"count":1319},{"startOffset":70905,"endOffset":71022,"count":1},{"startOffset":71041,"endOffset":71162,"count":1319},{"startOffset":71094,"endOffset":71152,"count":1312},{"startOffset":71109,"endOffset":71123,"count":236},{"startOffset":71124,"endOffset":71137,"count":235},{"startOffset":71138,"endOffset":71151,"count":81},{"startOffset":71169,"endOffset":71235,"count":7},{"startOffset":71235,"endOffset":71253,"count":6549}],"isBlockCoverage":true},{"functionName":"symbol","ranges":[{"startOffset":71325,"endOffset":71649,"count":129},{"startOffset":71492,"endOffset":71624,"count":113},{"startOffset":71580,"endOffset":71584,"count":67}],"isBlockCoverage":true},{"functionName":"assignment","ranges":[{"startOffset":71651,"endOffset":72673,"count":12}],"isBlockCoverage":true},{"functionName":"the_symbol.led","ranges":[{"startOffset":72038,"endOffset":72647,"count":1274},{"startOffset":72207,"endOffset":72235,"count":1156},{"startOffset":72237,"endOffset":72326,"count":577},{"startOffset":72326,"endOffset":72393,"count":697},{"startOffset":72497,"endOffset":72522,"count":1272},{"startOffset":72533,"endOffset":72585,"count":2}],"isBlockCoverage":true},{"functionName":"constant","ranges":[{"startOffset":72675,"endOffset":73157,"count":16},{"startOffset":72875,"endOffset":72882,"count":7},{"startOffset":72891,"endOffset":73067,"count":9}],"isBlockCoverage":true},{"functionName":"the_symbol.nud","ranges":[{"startOffset":72893,"endOffset":73067,"count":5589},{"startOffset":72979,"endOffset":73031,"count":510}],"isBlockCoverage":true},{"functionName":"infix","ranges":[{"startOffset":73159,"endOffset":73544,"count":30}],"isBlockCoverage":true},{"functionName":"the_symbol.led","ranges":[{"startOffset":73276,"endOffset":73518,"count":9305},{"startOffset":73392,"endOffset":73431,"count":6648},{"startOffset":73431,"endOffset":73517,"count":2657}],"isBlockCoverage":true},{"functionName":"infixr","ranges":[{"startOffset":73546,"endOffset":73881,"count":1}],"isBlockCoverage":true},{"functionName":"the_symbol.led","ranges":[{"startOffset":73678,"endOffset":73855,"count":0}],"isBlockCoverage":false},{"functionName":"post","ranges":[{"startOffset":73883,"endOffset":74177,"count":2}],"isBlockCoverage":true},{"functionName":"the_symbol.led","ranges":[{"startOffset":74001,"endOffset":74151,"count":2}],"isBlockCoverage":true},{"functionName":"pre","ranges":[{"startOffset":74179,"endOffset":74521,"count":2}],"isBlockCoverage":true},{"functionName":"the_symbol.nud","ranges":[{"startOffset":74290,"endOffset":74495,"count":2}],"isBlockCoverage":true},{"functionName":"prefix","ranges":[{"startOffset":74523,"endOffset":74893,"count":17}],"isBlockCoverage":true},{"functionName":"the_symbol.nud","ranges":[{"startOffset":74633,"endOffset":74867,"count":1304},{"startOffset":74752,"endOffset":74787,"count":1061},{"startOffset":74787,"endOffset":74866,"count":243}],"isBlockCoverage":true},{"functionName":"stmt","ranges":[{"startOffset":74895,"endOffset":75097,"count":22}],"isBlockCoverage":true},{"functionName":"the_symbol.fud","ranges":[{"startOffset":74997,"endOffset":75071,"count":2877}],"isBlockCoverage":true},{"functionName":"ternary","ranges":[{"startOffset":75099,"endOffset":75629,"count":1}],"isBlockCoverage":true},{"functionName":"the_symbol.led","ranges":[{"startOffset":75219,"endOffset":75603,"count":58},{"startOffset":75499,"endOffset":75571,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":76246,"endOffset":76312,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":76344,"endOffset":76550,"count":3},{"startOffset":76380,"endOffset":76424,"count":1},{"startOffset":76424,"endOffset":76530,"count":2},{"startOffset":76457,"endOffset":76530,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":76623,"endOffset":76829,"count":4},{"startOffset":76659,"endOffset":76809,"count":2},{"startOffset":76736,"endOffset":76809,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":76864,"endOffset":76930,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":77008,"endOffset":77105,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":77138,"endOffset":77227,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":77321,"endOffset":77421,"count":2},{"startOffset":77357,"endOffset":77401,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":78178,"endOffset":79433,"count":2873},{"startOffset":78279,"endOffset":78323,"count":2807},{"startOffset":78366,"endOffset":78384,"count":957},{"startOffset":78386,"endOffset":78441,"count":701},{"startOffset":78508,"endOffset":79004,"count":2331},{"startOffset":79077,"endOffset":79325,"count":1336},{"startOffset":79173,"endOffset":79229,"count":1},{"startOffset":79267,"endOffset":79319,"count":32},{"startOffset":79325,"endOffset":79409,"count":1537}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":78519,"endOffset":78994,"count":3901},{"startOffset":78604,"endOffset":78684,"count":2},{"startOffset":78754,"endOffset":78815,"count":2},{"startOffset":78908,"endOffset":78984,"count":1570}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":79452,"endOffset":80343,"count":3328},{"startOffset":79594,"endOffset":79644,"count":10},{"startOffset":79620,"endOffset":79643,"count":6},{"startOffset":79663,"endOffset":79889,"count":3318},{"startOffset":79708,"endOffset":79879,"count":83},{"startOffset":79766,"endOffset":79790,"count":81},{"startOffset":79807,"endOffset":79828,"count":80},{"startOffset":79845,"endOffset":79865,"count":80},{"startOffset":79898,"endOffset":79941,"count":3315},{"startOffset":79918,"endOffset":79940,"count":2},{"startOffset":79950,"endOffset":80057,"count":3313},{"startOffset":80002,"endOffset":80047,"count":12},{"startOffset":80025,"endOffset":80046,"count":8},{"startOffset":80064,"endOffset":80108,"count":3301},{"startOffset":80135,"endOffset":80181,"count":1},{"startOffset":80181,"endOffset":80342,"count":3327}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":80363,"endOffset":81254,"count":6},{"startOffset":80505,"endOffset":80555,"count":1},{"startOffset":80619,"endOffset":80790,"count":1},{"startOffset":80829,"endOffset":80851,"count":0},{"startOffset":80913,"endOffset":80958,"count":1},{"startOffset":81046,"endOffset":81092,"count":1},{"startOffset":81092,"endOffset":81253,"count":5}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":81273,"endOffset":81733,"count":416},{"startOffset":81401,"endOffset":81428,"count":414},{"startOffset":81430,"endOffset":81608,"count":4},{"startOffset":81514,"endOffset":81602,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":81753,"endOffset":81833,"count":1}],"isBlockCoverage":true},{"functionName":"do_tick","ranges":[{"startOffset":81837,"endOffset":82339,"count":58}],"isBlockCoverage":true},{"functionName":"part","ranges":[{"startOffset":81983,"endOffset":82288,"count":94},{"startOffset":82114,"endOffset":82278,"count":36}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":82357,"endOffset":82523,"count":24}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":82652,"endOffset":83313,"count":158},{"startOffset":82757,"endOffset":83271,"count":85}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":82768,"endOffset":83261,"count":621},{"startOffset":82886,"endOffset":82966,"count":1},{"startOffset":83031,"endOffset":83087,"count":1},{"startOffset":83175,"endOffset":83251,"count":536}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":83329,"endOffset":83408,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":83424,"endOffset":83498,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":83515,"endOffset":83760,"count":25},{"startOffset":83622,"endOffset":83706,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":83796,"endOffset":83956,"count":2}],"isBlockCoverage":true},{"functionName":"parameter_list","ranges":[{"startOffset":83960,"endOffset":89675,"count":602},{"startOffset":84086,"endOffset":84114,"count":338},{"startOffset":84116,"endOffset":89591,"count":338},{"startOffset":89591,"endOffset":89674,"count":596}],"isBlockCoverage":true},{"functionName":"parameter","ranges":[{"startOffset":84127,"endOffset":89581,"count":485},{"startOffset":84246,"endOffset":86526,"count":27},{"startOffset":84343,"endOffset":84571,"count":1},{"startOffset":86377,"endOffset":86512,"count":8},{"startOffset":86526,"endOffset":89571,"count":458},{"startOffset":86559,"endOffset":87921,"count":10},{"startOffset":86605,"endOffset":86833,"count":1},{"startOffset":87772,"endOffset":87907,"count":3},{"startOffset":87921,"endOffset":89571,"count":448},{"startOffset":87974,"endOffset":88414,"count":4},{"startOffset":88140,"endOffset":88396,"count":1},{"startOffset":88459,"endOffset":88536,"count":2},{"startOffset":88536,"endOffset":88706,"count":446},{"startOffset":88706,"endOffset":88768,"count":3},{"startOffset":88768,"endOffset":89557,"count":443},{"startOffset":88823,"endOffset":88984,"count":20},{"startOffset":88984,"endOffset":89340,"count":423},{"startOffset":89044,"endOffset":89318,"count":1},{"startOffset":89388,"endOffset":89539,"count":136}],"isBlockCoverage":true},{"functionName":"subparameter","ranges":[{"startOffset":84726,"endOffset":86228,"count":63},{"startOffset":84845,"endOffset":84930,"count":1},{"startOffset":84930,"endOffset":85056,"count":62},{"startOffset":85090,"endOffset":85290,"count":1},{"startOffset":85290,"endOffset":85418,"count":62},{"startOffset":85418,"endOffset":85748,"count":2},{"startOffset":85633,"endOffset":85726,"count":1},{"startOffset":85748,"endOffset":85796,"count":61},{"startOffset":85796,"endOffset":85960,"count":16},{"startOffset":85960,"endOffset":86056,"count":61},{"startOffset":86056,"endOffset":86210,"count":36}],"isBlockCoverage":true},{"functionName":"subparameter","ranges":[{"startOffset":86989,"endOffset":87660,"count":19},{"startOffset":87110,"endOffset":87195,"count":2},{"startOffset":87195,"endOffset":87322,"count":17},{"startOffset":87322,"endOffset":87486,"count":1},{"startOffset":87486,"endOffset":87534,"count":17},{"startOffset":87534,"endOffset":87642,"count":9}],"isBlockCoverage":true},{"functionName":"do_function","ranges":[{"startOffset":89677,"endOffset":92523,"count":599},{"startOffset":89764,"endOffset":90609,"count":591},{"startOffset":89923,"endOffset":90286,"count":254},{"startOffset":89965,"endOffset":90076,"count":4},{"startOffset":90076,"endOffset":90286,"count":250},{"startOffset":90286,"endOffset":90603,"count":337},{"startOffset":90411,"endOffset":90530,"count":59},{"startOffset":90530,"endOffset":90593,"count":278},{"startOffset":90609,"endOffset":90656,"count":8},{"startOffset":90656,"endOffset":90724,"count":595},{"startOffset":90724,"endOffset":90775,"count":0},{"startOffset":90775,"endOffset":90899,"count":595},{"startOffset":90899,"endOffset":91000,"count":1},{"startOffset":91000,"endOffset":91461,"count":595},{"startOffset":91461,"endOffset":91488,"count":345},{"startOffset":91490,"endOffset":91613,"count":59},{"startOffset":91613,"endOffset":92184,"count":595},{"startOffset":92184,"endOffset":92217,"count":244},{"startOffset":92224,"endOffset":92280,"count":2},{"startOffset":92280,"endOffset":92353,"count":587},{"startOffset":92362,"endOffset":92386,"count":587},{"startOffset":92393,"endOffset":92430,"count":1},{"startOffset":92430,"endOffset":92522,"count":587}],"isBlockCoverage":true},{"functionName":"enroll_parameter","ranges":[{"startOffset":91851,"endOffset":92044,"count":544},{"startOffset":91914,"endOffset":91971,"count":515},{"startOffset":91971,"endOffset":92038,"count":29}],"isBlockCoverage":true},{"functionName":"do_async","ranges":[{"startOffset":92525,"endOffset":92915,"count":16},{"startOffset":92790,"endOffset":92888,"count":1},{"startOffset":92888,"endOffset":92914,"count":15}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":92993,"endOffset":93237,"count":27},{"startOffset":93080,"endOffset":93135,"count":1},{"startOffset":93135,"endOffset":93236,"count":26}],"isBlockCoverage":true},{"functionName":"fart","ranges":[{"startOffset":93241,"endOffset":94327,"count":8},{"startOffset":93469,"endOffset":93559,"count":1},{"startOffset":93559,"endOffset":94110,"count":6},{"startOffset":94110,"endOffset":94215,"count":1},{"startOffset":94215,"endOffset":94273,"count":5},{"startOffset":94273,"endOffset":94326,"count":6}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":94012,"endOffset":94076,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":94341,"endOffset":95554,"count":392},{"startOffset":94597,"endOffset":94623,"count":385},{"startOffset":94632,"endOffset":94694,"count":385},{"startOffset":94658,"endOffset":94693,"count":264},{"startOffset":94701,"endOffset":94799,"count":7},{"startOffset":94799,"endOffset":94912,"count":385},{"startOffset":94912,"endOffset":94980,"count":2},{"startOffset":94980,"endOffset":95072,"count":384},{"startOffset":95072,"endOffset":95530,"count":3},{"startOffset":95118,"endOffset":95409,"count":2},{"startOffset":95182,"endOffset":95409,"count":1},{"startOffset":95409,"endOffset":95530,"count":1},{"startOffset":95530,"endOffset":95553,"count":380}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":95591,"endOffset":98901,"count":84},{"startOffset":95722,"endOffset":98859,"count":78},{"startOffset":98859,"endOffset":98900,"count":83}],"isBlockCoverage":true},{"functionName":"member","ranges":[{"startOffset":95768,"endOffset":98849,"count":488},{"startOffset":95987,"endOffset":95997,"count":479},{"startOffset":96023,"endOffset":96178,"count":1},{"startOffset":96231,"endOffset":96251,"count":483},{"startOffset":96269,"endOffset":96293,"count":8},{"startOffset":96308,"endOffset":96878,"count":8},{"startOffset":96346,"endOffset":96443,"count":4},{"startOffset":96666,"endOffset":96686,"count":7},{"startOffset":96688,"endOffset":96795,"count":1},{"startOffset":96878,"endOffset":97110,"count":480},{"startOffset":96972,"endOffset":97063,"count":1},{"startOffset":97063,"endOffset":97110,"count":479},{"startOffset":97110,"endOffset":97144,"count":487},{"startOffset":97144,"endOffset":98545,"count":479},{"startOffset":97188,"endOffset":97212,"count":459},{"startOffset":97214,"endOffset":97402,"count":85},{"startOffset":97267,"endOffset":97328,"count":0},{"startOffset":97402,"endOffset":98337,"count":394},{"startOffset":97435,"endOffset":97904,"count":8},{"startOffset":97791,"endOffset":97795,"count":0},{"startOffset":97904,"endOffset":98337,"count":386},{"startOffset":97963,"endOffset":98024,"count":0},{"startOffset":98195,"endOffset":98221,"count":1},{"startOffset":98223,"endOffset":98319,"count":1},{"startOffset":98421,"endOffset":98481,"count":8},{"startOffset":98545,"endOffset":98721,"count":8},{"startOffset":98721,"endOffset":98761,"count":487},{"startOffset":98761,"endOffset":98839,"count":410}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":98915,"endOffset":98981,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":98994,"endOffset":99093,"count":10}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":99135,"endOffset":100051,"count":28},{"startOffset":99236,"endOffset":99261,"count":15},{"startOffset":99271,"endOffset":99297,"count":25},{"startOffset":99304,"endOffset":99352,"count":3},{"startOffset":99413,"endOffset":99446,"count":5},{"startOffset":99448,"endOffset":100009,"count":5},{"startOffset":99567,"endOffset":99596,"count":4},{"startOffset":99609,"endOffset":99626,"count":3},{"startOffset":99637,"endOffset":99896,"count":2},{"startOffset":99679,"endOffset":99696,"count":1},{"startOffset":99698,"endOffset":99886,"count":1},{"startOffset":99896,"endOffset":99946,"count":3}],"isBlockCoverage":true},{"functionName":"do_var","ranges":[{"startOffset":100055,"endOffset":104783,"count":730},{"startOffset":100257,"endOffset":100560,"count":402},{"startOffset":100295,"endOffset":100347,"count":58},{"startOffset":100347,"endOffset":100554,"count":344},{"startOffset":100388,"endOffset":100554,"count":1},{"startOffset":100661,"endOffset":100750,"count":1},{"startOffset":100780,"endOffset":100809,"count":1},{"startOffset":100811,"endOffset":100891,"count":1}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":100897,"endOffset":104734,"count":730},{"startOffset":100949,"endOffset":100978,"count":5},{"startOffset":100980,"endOffset":102769,"count":5},{"startOffset":102769,"endOffset":104728,"count":725},{"startOffset":102801,"endOffset":102830,"count":2},{"startOffset":102832,"endOffset":104092,"count":2},{"startOffset":104092,"endOffset":104728,"count":723},{"startOffset":104225,"endOffset":104332,"count":3},{"startOffset":104419,"endOffset":104430,"count":248},{"startOffset":104432,"endOffset":104595,"count":475},{"startOffset":104595,"endOffset":104649,"count":722},{"startOffset":104649,"endOffset":104728,"count":0}],"isBlockCoverage":true},{"functionName":"pair","ranges":[{"startOffset":101106,"endOffset":102649,"count":7},{"startOffset":101168,"endOffset":101257,"count":1},{"startOffset":101257,"endOffset":101400,"count":6},{"startOffset":101430,"endOffset":101600,"count":1},{"startOffset":101600,"endOffset":101671,"count":6},{"startOffset":101671,"endOffset":102109,"count":2},{"startOffset":101755,"endOffset":102109,"count":1},{"startOffset":102109,"endOffset":102242,"count":4},{"startOffset":102242,"endOffset":102355,"count":5},{"startOffset":102355,"endOffset":102503,"count":0},{"startOffset":102503,"endOffset":102547,"count":5},{"startOffset":102547,"endOffset":102635,"count":2}],"isBlockCoverage":true},{"functionName":"element","ranges":[{"startOffset":102917,"endOffset":103972,"count":3},{"startOffset":103013,"endOffset":103105,"count":1},{"startOffset":103150,"endOffset":103239,"count":0},{"startOffset":103507,"endOffset":103568,"count":1},{"startOffset":103568,"endOffset":103958,"count":2},{"startOffset":103623,"endOffset":103789,"count":1},{"startOffset":103837,"endOffset":103940,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":104825,"endOffset":105130,"count":2},{"startOffset":104928,"endOffset":104979,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":105150,"endOffset":105305,"count":2},{"startOffset":105216,"endOffset":105264,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":105323,"endOffset":105666,"count":25},{"startOffset":105442,"endOffset":105465,"count":1},{"startOffset":105513,"endOffset":105587,"count":1},{"startOffset":105587,"endOffset":105665,"count":24}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":105680,"endOffset":106056,"count":4},{"startOffset":105917,"endOffset":106008,"count":1},{"startOffset":106008,"endOffset":106055,"count":3}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":106074,"endOffset":108767,"count":12},{"startOffset":106813,"endOffset":107498,"count":6},{"startOffset":106858,"endOffset":106950,"count":1},{"startOffset":107072,"endOffset":107109,"count":3},{"startOffset":107122,"endOffset":107175,"count":3},{"startOffset":107188,"endOffset":107235,"count":3},{"startOffset":107246,"endOffset":107336,"count":3},{"startOffset":107372,"endOffset":107408,"count":4},{"startOffset":107498,"endOffset":108718,"count":6},{"startOffset":107548,"endOffset":108111,"count":2},{"startOffset":107867,"endOffset":107929,"count":1},{"startOffset":108111,"endOffset":108712,"count":4},{"startOffset":108248,"endOffset":108330,"count":1},{"startOffset":108330,"endOffset":108712,"count":3},{"startOffset":108363,"endOffset":108661,"count":2},{"startOffset":108661,"endOffset":108712,"count":1},{"startOffset":108718,"endOffset":108766,"count":11}],"isBlockCoverage":true},{"functionName":"export_id","ranges":[{"startOffset":106176,"endOffset":106742,"count":3},{"startOffset":106235,"endOffset":106289,"count":0},{"startOffset":106401,"endOffset":106446,"count":0},{"startOffset":106533,"endOffset":106620,"count":1}],"isBlockCoverage":true},{"functionName":"loop","ranges":[{"startOffset":108404,"endOffset":108596,"count":3},{"startOffset":108494,"endOffset":108582,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":108782,"endOffset":110185,"count":11},{"startOffset":109055,"endOffset":109153,"count":2},{"startOffset":109153,"endOffset":109229,"count":9},{"startOffset":109238,"endOffset":109266,"count":9},{"startOffset":109273,"endOffset":109317,"count":1},{"startOffset":109317,"endOffset":109372,"count":8},{"startOffset":109372,"endOffset":109691,"count":3},{"startOffset":109428,"endOffset":109527,"count":1},{"startOffset":109691,"endOffset":110004,"count":4},{"startOffset":109894,"endOffset":109998,"count":2},{"startOffset":110004,"endOffset":110092,"count":7},{"startOffset":110092,"endOffset":110136,"count":1},{"startOffset":110136,"endOffset":110184,"count":7}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":110230,"endOffset":110809,"count":1192},{"startOffset":110387,"endOffset":110788,"count":288},{"startOffset":110511,"endOffset":110524,"count":138},{"startOffset":110537,"endOffset":110546,"count":150},{"startOffset":110601,"endOffset":110782,"count":2},{"startOffset":110703,"endOffset":110772,"count":0},{"startOffset":110788,"endOffset":110808,"count":1191}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":110827,"endOffset":112865,"count":14},{"startOffset":110902,"endOffset":111442,"count":2},{"startOffset":111112,"endOffset":111195,"count":1},{"startOffset":111442,"endOffset":111498,"count":12},{"startOffset":111498,"endOffset":111632,"count":1},{"startOffset":111632,"endOffset":111688,"count":12},{"startOffset":111688,"endOffset":111899,"count":8},{"startOffset":111770,"endOffset":111821,"count":1},{"startOffset":111899,"endOffset":112592,"count":4},{"startOffset":111990,"endOffset":112531,"count":3},{"startOffset":112017,"endOffset":112521,"count":4},{"startOffset":112063,"endOffset":112133,"count":1},{"startOffset":112133,"endOffset":112238,"count":3},{"startOffset":112238,"endOffset":112305,"count":1},{"startOffset":112305,"endOffset":112431,"count":3},{"startOffset":112431,"endOffset":112477,"count":2},{"startOffset":112477,"endOffset":112521,"count":1},{"startOffset":112531,"endOffset":112592,"count":3},{"startOffset":112592,"endOffset":112708,"count":11},{"startOffset":112708,"endOffset":112794,"count":1},{"startOffset":112794,"endOffset":112864,"count":11}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":112904,"endOffset":113261,"count":527},{"startOffset":113012,"endOffset":113061,"count":1},{"startOffset":113123,"endOffset":113161,"count":487},{"startOffset":113163,"endOffset":113218,"count":487}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":113279,"endOffset":115893,"count":12},{"startOffset":113489,"endOffset":113538,"count":1},{"startOffset":115102,"endOffset":115729,"count":7},{"startOffset":115303,"endOffset":115394,"count":1},{"startOffset":115394,"endOffset":115723,"count":6},{"startOffset":115516,"endOffset":115547,"count":1},{"startOffset":115549,"endOffset":115654,"count":1},{"startOffset":115693,"endOffset":115712,"count":2},{"startOffset":115729,"endOffset":115771,"count":3},{"startOffset":115771,"endOffset":115892,"count":10}],"isBlockCoverage":true},{"functionName":"major","ranges":[{"startOffset":113752,"endOffset":115038,"count":18},{"startOffset":114438,"endOffset":114512,"count":1},{"startOffset":114512,"endOffset":114645,"count":16},{"startOffset":114645,"endOffset":114774,"count":15},{"startOffset":114683,"endOffset":114710,"count":9},{"startOffset":114712,"endOffset":114764,"count":9},{"startOffset":114774,"endOffset":114954,"count":1},{"startOffset":114954,"endOffset":114993,"count":16},{"startOffset":114993,"endOffset":115032,"count":7}],"isBlockCoverage":true},{"functionName":"minor","ranges":[{"startOffset":113889,"endOffset":114373,"count":30},{"startOffset":114118,"endOffset":114176,"count":1},{"startOffset":114316,"endOffset":114363,"count":12}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":114035,"endOffset":114115,"count":44}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":115910,"endOffset":116144,"count":11},{"startOffset":116072,"endOffset":116120,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":116159,"endOffset":117605,"count":13},{"startOffset":116269,"endOffset":116315,"count":1},{"startOffset":116447,"endOffset":117153,"count":11},{"startOffset":116677,"endOffset":116758,"count":1},{"startOffset":116758,"endOffset":116803,"count":10},{"startOffset":116803,"endOffset":116955,"count":3},{"startOffset":116955,"endOffset":117103,"count":10},{"startOffset":117103,"endOffset":117147,"count":8},{"startOffset":117153,"endOffset":117305,"count":1},{"startOffset":117305,"endOffset":117343,"count":11},{"startOffset":117343,"endOffset":117522,"count":4},{"startOffset":117522,"endOffset":117604,"count":11}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":117643,"endOffset":117999,"count":26},{"startOffset":117856,"endOffset":117948,"count":3},{"startOffset":117948,"endOffset":117998,"count":25}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":118015,"endOffset":118082,"count":1}],"isBlockCoverage":true},{"functionName":"action","ranges":[{"startOffset":118140,"endOffset":119033,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":118282,"endOffset":119030,"count":37},{"startOffset":118487,"endOffset":118547,"count":8},{"startOffset":118668,"endOffset":118741,"count":10},{"startOffset":118882,"endOffset":118948,"count":36}],"isBlockCoverage":true},{"functionName":"amble","ranges":[{"startOffset":119035,"endOffset":119988,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":119180,"endOffset":119985,"count":60196},{"startOffset":119466,"endOffset":119979,"count":40840},{"startOffset":119604,"endOffset":119731,"count":13325},{"startOffset":119842,"endOffset":119969,"count":30265}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":119636,"endOffset":119715,"count":13325}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":119874,"endOffset":119953,"count":39570}],"isBlockCoverage":true},{"functionName":"walk_expression","ranges":[{"startOffset":120164,"endOffset":121002,"count":46729},{"startOffset":120213,"endOffset":121000,"count":29167},{"startOffset":120249,"endOffset":120352,"count":7017},{"startOffset":120352,"endOffset":120994,"count":22150},{"startOffset":120477,"endOffset":120567,"count":347},{"startOffset":120606,"endOffset":120631,"count":22148},{"startOffset":120633,"endOffset":120735,"count":2},{"startOffset":120735,"endOffset":120954,"count":22148},{"startOffset":120871,"endOffset":120954,"count":0}],"isBlockCoverage":true},{"functionName":"walk_statement","ranges":[{"startOffset":121004,"endOffset":121883,"count":22348},{"startOffset":121052,"endOffset":121881,"count":10239},{"startOffset":121088,"endOffset":121160,"count":2291},{"startOffset":121160,"endOffset":121875,"count":7948},{"startOffset":121286,"endOffset":121438,"count":1768},{"startOffset":121326,"endOffset":121424,"count":32},{"startOffset":121438,"endOffset":121754,"count":6180},{"startOffset":121509,"endOffset":121540,"count":1348},{"startOffset":121557,"endOffset":121581,"count":74},{"startOffset":121598,"endOffset":121621,"count":73},{"startOffset":121636,"endOffset":121754,"count":63}],"isBlockCoverage":true},{"functionName":"lookup","ranges":[{"startOffset":121885,"endOffset":123880,"count":8718},{"startOffset":121946,"endOffset":123878,"count":8717},{"startOffset":122206,"endOffset":123361,"count":2372},{"startOffset":122691,"endOffset":123252,"count":182},{"startOffset":122755,"endOffset":122891,"count":96},{"startOffset":122891,"endOffset":123252,"count":86},{"startOffset":123252,"endOffset":123361,"count":2276},{"startOffset":123361,"endOffset":123480,"count":6345},{"startOffset":123402,"endOffset":123480,"count":1},{"startOffset":123480,"endOffset":123536,"count":8621},{"startOffset":123536,"endOffset":123726,"count":2},{"startOffset":123606,"endOffset":123639,"count":1},{"startOffset":123656,"endOffset":123712,"count":1},{"startOffset":123737,"endOffset":123843,"count":2},{"startOffset":123843,"endOffset":123878,"count":8621}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":122234,"endOffset":122528,"count":3998},{"startOffset":122399,"endOffset":122429,"count":2390},{"startOffset":122448,"endOffset":122514,"count":2390}],"isBlockCoverage":true},{"functionName":"subactivate","ranges":[{"startOffset":123882,"endOffset":123987,"count":70}],"isBlockCoverage":true},{"functionName":"preaction_function","ranges":[{"startOffset":123989,"endOffset":125135,"count":591},{"startOffset":124140,"endOffset":124165,"count":238},{"startOffset":124167,"endOffset":124250,"count":1},{"startOffset":124420,"endOffset":124492,"count":299},{"startOffset":124524,"endOffset":124673,"count":5},{"startOffset":124569,"endOffset":124667,"count":1},{"startOffset":124673,"endOffset":124853,"count":586},{"startOffset":124706,"endOffset":124853,"count":3},{"startOffset":124751,"endOffset":124847,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":124883,"endOffset":125131,"count":470},{"startOffset":124971,"endOffset":124989,"count":448},{"startOffset":124991,"endOffset":125047,"count":27},{"startOffset":125047,"endOffset":125125,"count":443}],"isBlockCoverage":true},{"functionName":"bitwise_check","ranges":[{"startOffset":125137,"endOffset":125665,"count":10579},{"startOffset":125193,"endOffset":125224,"count":10148},{"startOffset":125226,"endOffset":125270,"count":1},{"startOffset":125313,"endOffset":125333,"count":7706},{"startOffset":125342,"endOffset":125362,"count":7319},{"startOffset":125371,"endOffset":125390,"count":6959},{"startOffset":125399,"endOffset":125433,"count":5803},{"startOffset":125442,"endOffset":125474,"count":2465},{"startOffset":125483,"endOffset":125612,"count":2441},{"startOffset":125619,"endOffset":125663,"count":1}],"isBlockCoverage":true},{"functionName":"pop_block","ranges":[{"startOffset":125667,"endOffset":125829,"count":2584}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":125716,"endOffset":125765,"count":601}],"isBlockCoverage":true},{"functionName":"activate","ranges":[{"startOffset":125831,"endOffset":126152,"count":729},{"startOffset":125919,"endOffset":126120,"count":474},{"startOffset":126011,"endOffset":126067,"count":0}],"isBlockCoverage":true},{"functionName":"action_var","ranges":[{"startOffset":126154,"endOffset":126219,"count":726}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":126317,"endOffset":127491,"count":9305},{"startOffset":126375,"endOffset":127489,"count":1555},{"startOffset":126492,"endOffset":126513,"count":1554},{"startOffset":126515,"endOffset":126591,"count":1},{"startOffset":126591,"endOffset":127483,"count":1554},{"startOffset":126623,"endOffset":127483,"count":43},{"startOffset":126666,"endOffset":126825,"count":1},{"startOffset":126825,"endOffset":127473,"count":42},{"startOffset":126939,"endOffset":127061,"count":1},{"startOffset":127061,"endOffset":127459,"count":41},{"startOffset":127132,"endOffset":127155,"count":37},{"startOffset":127176,"endOffset":127197,"count":33},{"startOffset":127218,"endOffset":127239,"count":31},{"startOffset":127260,"endOffset":127281,"count":18},{"startOffset":127302,"endOffset":127323,"count":1},{"startOffset":127342,"endOffset":127459,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":127520,"endOffset":127605,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":127634,"endOffset":127719,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":127795,"endOffset":127986,"count":360}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":127843,"endOffset":127982,"count":720},{"startOffset":127892,"endOffset":127909,"count":65},{"startOffset":127911,"endOffset":127976,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":128014,"endOffset":128711,"count":2873},{"startOffset":128112,"endOffset":128157,"count":2204},{"startOffset":128166,"endOffset":128205,"count":1100},{"startOffset":128212,"endOffset":128709,"count":622},{"startOffset":128420,"endOffset":128441,"count":286},{"startOffset":128458,"endOffset":128492,"count":3},{"startOffset":128509,"endOffset":128545,"count":3},{"startOffset":128562,"endOffset":128619,"count":3},{"startOffset":128634,"endOffset":128693,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":128740,"endOffset":128812,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":128849,"endOffset":128931,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":128962,"endOffset":129057,"count":1993}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":129090,"endOffset":129477,"count":7},{"startOffset":129143,"endOffset":129440,"count":3},{"startOffset":129234,"endOffset":129434,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":129650,"endOffset":129825,"count":8138},{"startOffset":129745,"endOffset":129823,"count":8077}],"isBlockCoverage":true},{"functionName":"init_variable","ranges":[{"startOffset":129829,"endOffset":130085,"count":577},{"startOffset":129935,"endOffset":130047,"count":543},{"startOffset":130047,"endOffset":130084,"count":34}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":130118,"endOffset":130534,"count":101},{"startOffset":130198,"endOffset":130532,"count":64},{"startOffset":130284,"endOffset":130306,"count":54},{"startOffset":130320,"endOffset":130347,"count":63},{"startOffset":130360,"endOffset":130382,"count":63},{"startOffset":130395,"endOffset":130422,"count":63},{"startOffset":130435,"endOffset":130463,"count":63},{"startOffset":130474,"endOffset":130526,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":130562,"endOffset":132369,"count":1274},{"startOffset":130858,"endOffset":131689,"count":1156},{"startOffset":130899,"endOffset":131087,"count":577},{"startOffset":130945,"endOffset":131012,"count":0},{"startOffset":131087,"endOffset":131683,"count":579},{"startOffset":131129,"endOffset":131149,"count":529},{"startOffset":131151,"endOffset":131363,"count":50},{"startOffset":131363,"endOffset":131673,"count":529},{"startOffset":131424,"endOffset":131465,"count":528},{"startOffset":131480,"endOffset":131673,"count":1},{"startOffset":131689,"endOffset":132367,"count":118},{"startOffset":131738,"endOffset":131886,"count":81},{"startOffset":131773,"endOffset":131809,"count":78},{"startOffset":131811,"endOffset":131876,"count":3},{"startOffset":131998,"endOffset":132284,"count":103},{"startOffset":132156,"endOffset":132182,"count":76},{"startOffset":132203,"endOffset":132252,"count":7},{"startOffset":132231,"endOffset":132251,"count":6},{"startOffset":132295,"endOffset":132361,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":131195,"endOffset":131347,"count":100},{"startOffset":131254,"endOffset":131329,"count":79}],"isBlockCoverage":true},{"functionName":"postaction_function","ranges":[{"startOffset":132373,"endOffset":132692,"count":591},{"startOffset":132585,"endOffset":132666,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":132715,"endOffset":134553,"count":9305},{"startOffset":132779,"endOffset":133191,"count":1555},{"startOffset":132962,"endOffset":133091,"count":1550},{"startOffset":133037,"endOffset":133077,"count":3},{"startOffset":133102,"endOffset":133185,"count":8},{"startOffset":133218,"endOffset":133531,"count":283},{"startOffset":133301,"endOffset":133386,"count":1},{"startOffset":133386,"endOffset":133515,"count":282},{"startOffset":133430,"endOffset":133515,"count":1},{"startOffset":133531,"endOffset":134551,"count":9022},{"startOffset":133559,"endOffset":133860,"count":416},{"startOffset":133610,"endOffset":133710,"count":1},{"startOffset":133758,"endOffset":133854,"count":1},{"startOffset":133860,"endOffset":134551,"count":8606},{"startOffset":133887,"endOffset":133907,"count":5279},{"startOffset":133909,"endOffset":134048,"count":3332},{"startOffset":133957,"endOffset":134042,"count":1},{"startOffset":134048,"endOffset":134551,"count":5274},{"startOffset":134076,"endOffset":134095,"count":5268},{"startOffset":134097,"endOffset":134551,"count":2395},{"startOffset":134212,"endOffset":134236,"count":52},{"startOffset":134249,"endOffset":134275,"count":1},{"startOffset":134288,"endOffset":134305,"count":1},{"startOffset":134316,"endOffset":134386,"count":1},{"startOffset":134462,"endOffset":134488,"count":32},{"startOffset":134499,"endOffset":134545,"count":25}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":134583,"endOffset":134959,"count":387},{"startOffset":134722,"endOffset":134762,"count":374},{"startOffset":134771,"endOffset":134811,"count":373},{"startOffset":134818,"endOffset":134957,"count":14}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":134989,"endOffset":135248,"count":360},{"startOffset":135128,"endOffset":135168,"count":359},{"startOffset":135175,"endOffset":135246,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":135326,"endOffset":138408,"count":2873},{"startOffset":135438,"endOffset":135501,"count":24},{"startOffset":135534,"endOffset":135656,"count":66},{"startOffset":135564,"endOffset":135650,"count":1},{"startOffset":135656,"endOffset":138406,"count":2807},{"startOffset":135683,"endOffset":137149,"count":2137},{"startOffset":135720,"endOffset":136675,"count":23},{"startOffset":135788,"endOffset":135812,"count":22},{"startOffset":135829,"endOffset":135852,"count":22},{"startOffset":135869,"endOffset":135892,"count":22},{"startOffset":135909,"endOffset":135932,"count":22},{"startOffset":135947,"endOffset":136033,"count":2},{"startOffset":136033,"endOffset":136665,"count":21},{"startOffset":136067,"endOffset":136200,"count":2},{"startOffset":136103,"endOffset":136186,"count":1},{"startOffset":136200,"endOffset":136665,"count":19},{"startOffset":136231,"endOffset":136439,"count":2},{"startOffset":136339,"endOffset":136425,"count":1},{"startOffset":136439,"endOffset":136665,"count":17},{"startOffset":136471,"endOffset":136665,"count":1},{"startOffset":136675,"endOffset":137143,"count":2114},{"startOffset":136787,"endOffset":136811,"count":52},{"startOffset":136828,"endOffset":136851,"count":43},{"startOffset":136868,"endOffset":136891,"count":36},{"startOffset":136908,"endOffset":136931,"count":1},{"startOffset":136946,"endOffset":137133,"count":1},{"startOffset":137149,"endOffset":138406,"count":670},{"startOffset":137176,"endOffset":138406,"count":668},{"startOffset":137262,"endOffset":137287,"count":8},{"startOffset":137289,"endOffset":137326,"count":1},{"startOffset":137375,"endOffset":137690,"count":2},{"startOffset":137416,"endOffset":137680,"count":1},{"startOffset":137731,"endOffset":138400,"count":4},{"startOffset":137810,"endOffset":138390,"count":3},{"startOffset":138044,"endOffset":138080,"count":1},{"startOffset":138103,"endOffset":138358,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":138437,"endOffset":138706,"count":416},{"startOffset":138501,"endOffset":138578,"count":1},{"startOffset":138618,"endOffset":138704,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":138878,"endOffset":138929,"count":7}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":139024,"endOffset":139476,"count":11},{"startOffset":139129,"endOffset":139310,"count":3},{"startOffset":139310,"endOffset":139426,"count":8}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":139156,"endOffset":139298,"count":3}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":139554,"endOffset":139895,"count":11},{"startOffset":139608,"endOffset":139893,"count":10},{"startOffset":139689,"endOffset":139844,"count":3}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":139964,"endOffset":141019,"count":58},{"startOffset":140087,"endOffset":140143,"count":52},{"startOffset":140150,"endOffset":140194,"count":6},{"startOffset":140194,"endOffset":141017,"count":52},{"startOffset":140259,"endOffset":140314,"count":1},{"startOffset":140314,"endOffset":141017,"count":51},{"startOffset":140379,"endOffset":140434,"count":1},{"startOffset":140434,"endOffset":141017,"count":50},{"startOffset":140495,"endOffset":140532,"count":1},{"startOffset":140539,"endOffset":140594,"count":1},{"startOffset":140594,"endOffset":141017,"count":49},{"startOffset":140656,"endOffset":140692,"count":1},{"startOffset":140699,"endOffset":140753,"count":1},{"startOffset":140753,"endOffset":141017,"count":48},{"startOffset":140817,"endOffset":140922,"count":45},{"startOffset":140929,"endOffset":141017,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":141042,"endOffset":141775,"count":911},{"startOffset":141087,"endOffset":141242,"count":34},{"startOffset":141190,"endOffset":141236,"count":26},{"startOffset":141242,"endOffset":141773,"count":877},{"startOffset":141270,"endOffset":141378,"count":165},{"startOffset":141320,"endOffset":141372,"count":2},{"startOffset":141378,"endOffset":141773,"count":712},{"startOffset":141407,"endOffset":141536,"count":1},{"startOffset":141536,"endOffset":141773,"count":711},{"startOffset":141580,"endOffset":141599,"count":557},{"startOffset":141608,"endOffset":141634,"count":476},{"startOffset":141643,"endOffset":141664,"count":129},{"startOffset":141671,"endOffset":141773,"count":104},{"startOffset":141721,"endOffset":141767,"count":32}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":141124,"endOffset":141187,"count":16}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":141857,"endOffset":142306,"count":9},{"startOffset":142027,"endOffset":142062,"count":1},{"startOffset":142064,"endOffset":142129,"count":1},{"startOffset":142129,"endOffset":142304,"count":8},{"startOffset":142171,"endOffset":142190,"count":3},{"startOffset":142199,"endOffset":142248,"count":3},{"startOffset":142220,"endOffset":142247,"count":2},{"startOffset":142255,"endOffset":142304,"count":7}],"isBlockCoverage":true},{"functionName":"delve","ranges":[{"startOffset":142310,"endOffset":143070,"count":569}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":142387,"endOffset":143066,"count":3800},{"startOffset":142432,"endOffset":143060,"count":3787},{"startOffset":142531,"endOffset":143050,"count":1530},{"startOffset":142610,"endOffset":142742,"count":11},{"startOffset":142688,"endOffset":142720,"count":0},{"startOffset":142761,"endOffset":142900,"count":11},{"startOffset":142900,"endOffset":143036,"count":1519},{"startOffset":142922,"endOffset":143036,"count":2}],"isBlockCoverage":true},{"functionName":"uninitialized_and_unused","ranges":[{"startOffset":143072,"endOffset":143381,"count":69},{"startOffset":143303,"endOffset":143317,"count":61},{"startOffset":143319,"endOffset":143349,"count":18}],"isBlockCoverage":true},{"functionName":"whitage","ranges":[{"startOffset":143446,"endOffset":155574,"count":67}],"isBlockCoverage":true},{"functionName":"pop","ranges":[{"startOffset":143937,"endOffset":144161,"count":6759}],"isBlockCoverage":true},{"functionName":"push","ranges":[{"startOffset":144167,"endOffset":144319,"count":6759}],"isBlockCoverage":true},{"functionName":"expected_at","ranges":[{"startOffset":144325,"endOffset":144571,"count":32}],"isBlockCoverage":true},{"functionName":"at_margin","ranges":[{"startOffset":144577,"endOffset":144720,"count":9713},{"startOffset":144667,"endOffset":144714,"count":24}],"isBlockCoverage":true},{"functionName":"no_space_only","ranges":[{"startOffset":144726,"endOffset":145322,"count":26076},{"startOffset":144813,"endOffset":144840,"count":26075},{"startOffset":144853,"endOffset":144956,"count":26075},{"startOffset":144915,"endOffset":144942,"count":26068},{"startOffset":144967,"endOffset":145316,"count":13}],"isBlockCoverage":true},{"functionName":"no_space","ranges":[{"startOffset":145328,"endOffset":146201,"count":966},{"startOffset":145388,"endOffset":145702,"count":963},{"startOffset":145431,"endOffset":145459,"count":1},{"startOffset":145461,"endOffset":145692,"count":1},{"startOffset":145702,"endOffset":146195,"count":3},{"startOffset":145837,"endOffset":145849,"count":0},{"startOffset":146039,"endOffset":146185,"count":0}],"isBlockCoverage":true},{"functionName":"one_space_only","ranges":[{"startOffset":146207,"endOffset":146480,"count":2542},{"startOffset":146272,"endOffset":146303,"count":2541},{"startOffset":146305,"endOffset":146474,"count":12}],"isBlockCoverage":true},{"functionName":"one_space","ranges":[{"startOffset":146486,"endOffset":146955,"count":13954},{"startOffset":146546,"endOffset":146554,"count":517},{"startOffset":146556,"endOffset":146840,"count":13437},{"startOffset":146603,"endOffset":146631,"count":15},{"startOffset":146633,"endOffset":146830,"count":15},{"startOffset":146840,"endOffset":146949,"count":517},{"startOffset":146887,"endOffset":146939,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":146992,"endOffset":155570,"count":54979},{"startOffset":147079,"endOffset":147102,"count":53486},{"startOffset":147104,"endOffset":147153,"count":1562},{"startOffset":147153,"endOffset":155564,"count":53417},{"startOffset":147679,"endOffset":149900,"count":7586},{"startOffset":147785,"endOffset":149466,"count":6759},{"startOffset":147890,"endOffset":147919,"count":6752},{"startOffset":148023,"endOffset":148774,"count":2525},{"startOffset":148154,"endOffset":148166,"count":462},{"startOffset":148294,"endOffset":148583,"count":3},{"startOffset":148346,"endOffset":148557,"count":2},{"startOffset":148583,"endOffset":148752,"count":2522},{"startOffset":148607,"endOffset":148677,"count":6},{"startOffset":148677,"endOffset":148752,"count":2516},{"startOffset":148774,"endOffset":149448,"count":4234},{"startOffset":148826,"endOffset":148851,"count":4228},{"startOffset":148853,"endOffset":149212,"count":7},{"startOffset":149466,"endOffset":149886,"count":827},{"startOffset":149742,"endOffset":149801,"count":826},{"startOffset":149801,"endOffset":149868,"count":1},{"startOffset":149900,"endOffset":155310,"count":45831},{"startOffset":149954,"endOffset":150244,"count":3780},{"startOffset":150000,"endOffset":150065,"count":138},{"startOffset":150065,"endOffset":150170,"count":3642},{"startOffset":150244,"endOffset":155296,"count":42051},{"startOffset":150275,"endOffset":150506,"count":6759},{"startOffset":150336,"endOffset":150355,"count":2525},{"startOffset":150357,"endOffset":150418,"count":2525},{"startOffset":150418,"endOffset":150488,"count":4234},{"startOffset":150506,"endOffset":155296,"count":35292},{"startOffset":150805,"endOffset":150867,"count":23},{"startOffset":150867,"endOffset":155278,"count":35269},{"startOffset":150901,"endOffset":151188,"count":2},{"startOffset":151188,"endOffset":155278,"count":35267},{"startOffset":151215,"endOffset":151620,"count":2695},{"startOffset":151251,"endOffset":151390,"count":1245},{"startOffset":151337,"endOffset":151364,"count":526},{"startOffset":151392,"endOffset":151460,"count":1787},{"startOffset":151460,"endOffset":151535,"count":908},{"startOffset":151620,"endOffset":155278,"count":32572},{"startOffset":151657,"endOffset":151870,"count":94},{"startOffset":151693,"endOffset":151762,"count":92},{"startOffset":151762,"endOffset":151848,"count":2},{"startOffset":151870,"endOffset":155278,"count":32478},{"startOffset":151954,"endOffset":151973,"count":9216},{"startOffset":151998,"endOffset":152005,"count":2846},{"startOffset":152028,"endOffset":152087,"count":140},{"startOffset":152087,"endOffset":155278,"count":32338},{"startOffset":152162,"endOffset":152181,"count":29021},{"startOffset":152206,"endOffset":152226,"count":29019},{"startOffset":152251,"endOffset":152270,"count":29012},{"startOffset":152295,"endOffset":152314,"count":26317},{"startOffset":152339,"endOffset":152358,"count":22055},{"startOffset":152383,"endOffset":152536,"count":21626},{"startOffset":152469,"endOffset":152510,"count":9076},{"startOffset":152490,"endOffset":152509,"count":6370},{"startOffset":152561,"endOffset":152700,"count":18513},{"startOffset":152649,"endOffset":152674,"count":550},{"startOffset":152723,"endOffset":152787,"count":14103},{"startOffset":152787,"endOffset":155278,"count":18235},{"startOffset":152814,"endOffset":152834,"count":14918},{"startOffset":152836,"endOffset":152900,"count":3319},{"startOffset":152900,"endOffset":155278,"count":14916},{"startOffset":152927,"endOffset":153067,"count":0},{"startOffset":153151,"endOffset":153172,"count":14822},{"startOffset":153197,"endOffset":153219,"count":14798},{"startOffset":153244,"endOffset":153265,"count":14788},{"startOffset":153290,"endOffset":153314,"count":14632},{"startOffset":153339,"endOffset":153361,"count":14616},{"startOffset":153386,"endOffset":153408,"count":14599},{"startOffset":153433,"endOffset":153456,"count":14573},{"startOffset":153481,"endOffset":153503,"count":14567},{"startOffset":153528,"endOffset":153553,"count":14279},{"startOffset":153578,"endOffset":153623,"count":14279},{"startOffset":153603,"endOffset":153622,"count":2},{"startOffset":153648,"endOffset":153688,"count":14277},{"startOffset":153668,"endOffset":153687,"count":1939},{"startOffset":153711,"endOffset":153863,"count":2404},{"startOffset":153863,"endOffset":155278,"count":12512},{"startOffset":153994,"endOffset":154023,"count":8483},{"startOffset":154048,"endOffset":154198,"count":4480},{"startOffset":154133,"endOffset":154172,"count":356},{"startOffset":154153,"endOffset":154171,"count":75},{"startOffset":154223,"endOffset":154376,"count":4148},{"startOffset":154309,"endOffset":154350,"count":356},{"startOffset":154330,"endOffset":154349,"count":75},{"startOffset":154401,"endOffset":154426,"count":3816},{"startOffset":154451,"endOffset":154469,"count":3268},{"startOffset":154494,"endOffset":154978,"count":2874},{"startOffset":154609,"endOffset":154634,"count":319},{"startOffset":154667,"endOffset":154692,"count":254},{"startOffset":154751,"endOffset":154952,"count":2620},{"startOffset":154837,"endOffset":154863,"count":1360},{"startOffset":154896,"endOffset":154922,"count":1341},{"startOffset":155003,"endOffset":155054,"count":1595},{"startOffset":155034,"endOffset":155053,"count":1250},{"startOffset":155077,"endOffset":155161,"count":12167},{"startOffset":155161,"endOffset":155278,"count":345},{"startOffset":155194,"endOffset":155212,"count":199},{"startOffset":155214,"endOffset":155278,"count":186}],"isBlockCoverage":true},{"functionName":"jslint","ranges":[{"startOffset":155608,"endOffset":160283,"count":368},{"startOffset":156061,"endOffset":156064,"count":1},{"startOffset":156077,"endOffset":156080,"count":367},{"startOffset":157249,"endOffset":157323,"count":22},{"startOffset":157323,"endOffset":158252,"count":320},{"startOffset":157523,"endOffset":157635,"count":4},{"startOffset":157568,"endOffset":157621,"count":1},{"startOffset":157635,"endOffset":157926,"count":316},{"startOffset":157818,"endOffset":157912,"count":1},{"startOffset":158097,"endOffset":158242,"count":69},{"startOffset":158178,"endOffset":158228,"count":67},{"startOffset":158252,"endOffset":158282,"count":279},{"startOffset":158282,"endOffset":158515,"count":275},{"startOffset":158515,"endOffset":158556,"count":279},{"startOffset":158556,"endOffset":158630,"count":1},{"startOffset":158630,"endOffset":158665,"count":278},{"startOffset":158665,"endOffset":158945,"count":90},{"startOffset":158811,"endOffset":158939,"count":1},{"startOffset":160055,"endOffset":160069,"count":54},{"startOffset":160156,"endOffset":160166,"count":3},{"startOffset":160179,"endOffset":160190,"count":365}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":156914,"endOffset":157178,"count":24},{"startOffset":157071,"endOffset":157154,"count":11}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":158315,"endOffset":158503,"count":26},{"startOffset":158388,"endOffset":158489,"count":3}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":159030,"endOffset":159264,"count":837},{"startOffset":159135,"endOffset":159153,"count":777},{"startOffset":159154,"endOffset":159176,"count":650}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":159270,"endOffset":159799,"count":812}],"isBlockCoverage":true},{"functionName":"cli","ranges":[{"startOffset":160285,"endOffset":164794,"count":4},{"startOffset":163354,"endOffset":163370,"count":3},{"startOffset":163388,"endOffset":163536,"count":2},{"startOffset":163536,"endOffset":164771,"count":1},{"startOffset":164771,"endOffset":164793,"count":2}],"isBlockCoverage":true},{"functionName":"string_line_count","ranges":[{"startOffset":160476,"endOffset":160893,"count":8},{"startOffset":160724,"endOffset":160867,"count":2218},{"startOffset":160797,"endOffset":160835,"count":8},{"startOffset":160835,"endOffset":160867,"count":2210}],"isBlockCoverage":true},{"functionName":"jslint_from_file","ranges":[{"startOffset":160898,"endOffset":163319,"count":22},{"startOffset":161109,"endOffset":161663,"count":5},{"startOffset":161672,"endOffset":162143,"count":2},{"startOffset":162152,"endOffset":162707,"count":1},{"startOffset":162716,"endOffset":162851,"count":14},{"startOffset":162861,"endOffset":162935,"count":14},{"startOffset":162935,"endOffset":163313,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":161243,"endOffset":161641,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":161802,"endOffset":162121,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":162289,"endOffset":162685,"count":6}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":163142,"endOffset":163276,"count":4}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":163620,"endOffset":164613,"count":28},{"startOffset":163800,"endOffset":163813,"count":4},{"startOffset":163826,"endOffset":163837,"count":9},{"startOffset":163850,"endOffset":163863,"count":10},{"startOffset":163876,"endOffset":163887,"count":12},{"startOffset":163900,"endOffset":163912,"count":12},{"startOffset":163925,"endOffset":163959,"count":13},{"startOffset":163972,"endOffset":164004,"count":15},{"startOffset":164018,"endOffset":164101,"count":13},{"startOffset":164101,"endOffset":164116,"count":12},{"startOffset":164116,"endOffset":164170,"count":1},{"startOffset":164170,"endOffset":164288,"count":12},{"startOffset":164288,"endOffset":164295,"count":11},{"startOffset":164296,"endOffset":164320,"count":11},{"startOffset":164336,"endOffset":164375,"count":1},{"startOffset":164375,"endOffset":164612,"count":11}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":164824,"endOffset":165086,"count":357},{"startOffset":164939,"endOffset":165028,"count":3},{"startOffset":165028,"endOffset":165085,"count":354}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":165528,"endOffset":165587,"count":1}],"isBlockCoverage":true}]},{"scriptId":"89","url":"net.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":47106,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":1448,"endOffset":1473,"count":0}],"isBlockCoverage":false},{"functionName":"noop","ranges":[{"startOffset":3388,"endOffset":3406,"count":0}],"isBlockCoverage":false},{"functionName":"getFlags","ranges":[{"startOffset":3408,"endOffset":3502,"count":0}],"isBlockCoverage":false},{"functionName":"createHandle","ranges":[{"startOffset":3504,"endOffset":3885,"count":1},{"startOffset":3671,"endOffset":3693,"count":0},{"startOffset":3727,"endOffset":3884,"count":0}],"isBlockCoverage":true},{"functionName":"getNewAsyncId","ranges":[{"startOffset":3888,"endOffset":4026,"count":1},{"startOffset":3983,"endOffset":4001,"count":0}],"isBlockCoverage":true},{"functionName":"isPipeName","ranges":[{"startOffset":4029,"endOffset":4112,"count":0}],"isBlockCoverage":false},{"functionName":"createServer","ranges":[{"startOffset":4114,"endOffset":4218,"count":0}],"isBlockCoverage":false},{"functionName":"connect","ranges":[{"startOffset":4441,"endOffset":4732,"count":0}],"isBlockCoverage":false},{"functionName":"normalizeArgs","ranges":[{"startOffset":5227,"endOffset":5953,"count":0}],"isBlockCoverage":false},{"functionName":"initSocketHandle","ranges":[{"startOffset":6025,"endOffset":6628,"count":1},{"startOffset":6381,"endOffset":6622,"count":0}],"isBlockCoverage":true},{"functionName":"Socket","ranges":[{"startOffset":6763,"endOffset":10585,"count":1},{"startOffset":6823,"endOffset":6850,"count":0},{"startOffset":7400,"endOffset":7426,"count":0},{"startOffset":7970,"endOffset":8078,"count":0},{"startOffset":8175,"endOffset":8254,"count":0},{"startOffset":8255,"endOffset":8303,"count":0},{"startOffset":8305,"endOffset":8536,"count":0},{"startOffset":9087,"endOffset":9121,"count":0},{"startOffset":9272,"endOffset":9801,"count":0},{"startOffset":10167,"endOffset":10432,"count":0}],"isBlockCoverage":true},{"functionName":"_unrefTimer","ranges":[{"startOffset":10758,"endOffset":10888,"count":163},{"startOffset":10860,"endOffset":10882,"count":0}],"isBlockCoverage":true},{"functionName":"Socket._final","ranges":[{"startOffset":11008,"endOffset":11656,"count":0}],"isBlockCoverage":false},{"functionName":"afterShutdown","ranges":[{"startOffset":11660,"endOffset":12039,"count":0}],"isBlockCoverage":false},{"functionName":"writeAfterFIN","ranges":[{"startOffset":12246,"endOffset":12702,"count":0}],"isBlockCoverage":false},{"functionName":"Socket._onTimeout","ranges":[{"startOffset":12784,"endOffset":13288,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.setNoDelay","ranges":[{"startOffset":13322,"endOffset":13771,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.setKeepAlive","ranges":[{"startOffset":13807,"endOffset":14054,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.address","ranges":[{"startOffset":14085,"endOffset":14129,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":14196,"endOffset":14240,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":14300,"endOffset":14356,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":14447,"endOffset":14762,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":14831,"endOffset":14911,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":14979,"endOffset":15024,"count":0}],"isBlockCoverage":false},{"functionName":"tryReadStart","ranges":[{"startOffset":15031,"endOffset":15277,"count":0}],"isBlockCoverage":false},{"functionName":"Socket._read","ranges":[{"startOffset":15369,"endOffset":15598,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.end","ranges":[{"startOffset":15625,"endOffset":15777,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.pause","ranges":[{"startOffset":15806,"endOffset":16140,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.resume","ranges":[{"startOffset":16170,"endOffset":16354,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.read","ranges":[{"startOffset":16382,"endOffset":16568,"count":0}],"isBlockCoverage":false},{"functionName":"onReadableStreamEnd","ranges":[{"startOffset":16615,"endOffset":16900,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.destroySoon","ranges":[{"startOffset":16934,"endOffset":17081,"count":0}],"isBlockCoverage":false},{"functionName":"Socket._destroy","ranges":[{"startOffset":17113,"endOffset":18067,"count":0}],"isBlockCoverage":false},{"functionName":"Socket._getpeername","ranges":[{"startOffset":18102,"endOffset":18393,"count":0}],"isBlockCoverage":false},{"functionName":"protoGetter","ranges":[{"startOffset":18396,"endOffset":18556,"count":8}],"isBlockCoverage":true},{"functionName":"bytesRead","ranges":[{"startOffset":18583,"endOffset":18674,"count":0}],"isBlockCoverage":false},{"functionName":"remoteAddress","ranges":[{"startOffset":18707,"endOffset":18773,"count":0}],"isBlockCoverage":false},{"functionName":"remoteFamily","ranges":[{"startOffset":18805,"endOffset":18869,"count":0}],"isBlockCoverage":false},{"functionName":"remotePort","ranges":[{"startOffset":18899,"endOffset":18959,"count":0}],"isBlockCoverage":false},{"functionName":"Socket._getsockname","ranges":[{"startOffset":18996,"endOffset":19281,"count":0}],"isBlockCoverage":false},{"functionName":"localAddress","ranges":[{"startOffset":19313,"endOffset":19378,"count":0}],"isBlockCoverage":false},{"functionName":"localPort","ranges":[{"startOffset":19408,"endOffset":19467,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.","ranges":[{"startOffset":19509,"endOffset":19556,"count":0}],"isBlockCoverage":false},{"functionName":"Socket._writeGeneric","ranges":[{"startOffset":19592,"endOffset":20353,"count":163},{"startOffset":19814,"endOffset":20007,"count":0},{"startOffset":20088,"endOffset":20144,"count":0},{"startOffset":20198,"endOffset":20234,"count":0},{"startOffset":20313,"endOffset":20351,"count":0}],"isBlockCoverage":true},{"functionName":"connect","ranges":[{"startOffset":19909,"endOffset":19989,"count":0}],"isBlockCoverage":false},{"functionName":"Socket._writev","ranges":[{"startOffset":20384,"endOffset":20452,"count":0}],"isBlockCoverage":false},{"functionName":"Socket._write","ranges":[{"startOffset":20482,"endOffset":20563,"count":163}],"isBlockCoverage":true},{"functionName":"_bytesDispatched","ranges":[{"startOffset":20756,"endOffset":20860,"count":0}],"isBlockCoverage":false},{"functionName":"bytesWritten","ranges":[{"startOffset":20892,"endOffset":21821,"count":0}],"isBlockCoverage":false},{"functionName":"checkBindError","ranges":[{"startOffset":21826,"endOffset":22625,"count":0}],"isBlockCoverage":false},{"functionName":"internalConnect","ranges":[{"startOffset":22628,"endOffset":24333,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.connect","ranges":[{"startOffset":24363,"endOffset":25667,"count":0}],"isBlockCoverage":false},{"functionName":"lookupAndConnect","ranges":[{"startOffset":25671,"endOffset":28698,"count":0}],"isBlockCoverage":false},{"functionName":"connectErrorNT","ranges":[{"startOffset":28701,"endOffset":28760,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.ref","ranges":[{"startOffset":28786,"endOffset":28973,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.unref","ranges":[{"startOffset":29002,"endOffset":29195,"count":0}],"isBlockCoverage":false},{"functionName":"afterConnect","ranges":[{"startOffset":29199,"endOffset":30447,"count":0}],"isBlockCoverage":false},{"functionName":"Server","ranges":[{"startOffset":30450,"endOffset":31779,"count":0}],"isBlockCoverage":false},{"functionName":"toNumber","ranges":[{"startOffset":31890,"endOffset":31955,"count":0}],"isBlockCoverage":false},{"functionName":"createServerHandle","ranges":[{"startOffset":32023,"endOffset":33475,"count":0}],"isBlockCoverage":false},{"functionName":"setupListenHandle","ranges":[{"startOffset":33477,"endOffset":35828,"count":0}],"isBlockCoverage":false},{"functionName":"emitErrorNT","ranges":[{"startOffset":35895,"endOffset":35957,"count":0}],"isBlockCoverage":false},{"functionName":"emitListeningNT","ranges":[{"startOffset":35960,"endOffset":36075,"count":0}],"isBlockCoverage":false},{"functionName":"listenInCluster","ranges":[{"startOffset":36078,"endOffset":37296,"count":0}],"isBlockCoverage":false},{"functionName":"Server.listen","ranges":[{"startOffset":37325,"endOffset":40823,"count":0}],"isBlockCoverage":false},{"functionName":"lookupAndListen","ranges":[{"startOffset":40826,"endOffset":41238,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":41301,"endOffset":41344,"count":0}],"isBlockCoverage":false},{"functionName":"Server.address","ranges":[{"startOffset":41419,"endOffset":41708,"count":0}],"isBlockCoverage":false},{"functionName":"onconnection","ranges":[{"startOffset":41711,"endOffset":42372,"count":0}],"isBlockCoverage":false},{"functionName":"Server.getConnections","ranges":[{"startOffset":42409,"endOffset":43162,"count":0}],"isBlockCoverage":false},{"functionName":"Server.close","ranges":[{"startOffset":43191,"endOffset":44058,"count":0}],"isBlockCoverage":false},{"functionName":"Server._emitCloseIfDrained","ranges":[{"startOffset":44100,"endOffset":44486,"count":0}],"isBlockCoverage":false},{"functionName":"emitCloseNT","ranges":[{"startOffset":44490,"endOffset":44573,"count":0}],"isBlockCoverage":false},{"functionName":"Server.","ranges":[{"startOffset":44632,"endOffset":44794,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":44997,"endOffset":45033,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":45037,"endOffset":45078,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":45138,"endOffset":45169,"count":171}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":45173,"endOffset":45209,"count":1}],"isBlockCoverage":true},{"functionName":"Server._setupWorker","ranges":[{"startOffset":45247,"endOffset":45473,"count":0}],"isBlockCoverage":false},{"functionName":"Server.ref","ranges":[{"startOffset":45499,"endOffset":45597,"count":0}],"isBlockCoverage":false},{"functionName":"Server.unref","ranges":[{"startOffset":45625,"endOffset":45724,"count":0}],"isBlockCoverage":false},{"functionName":"_setSimultaneousAccepts","ranges":[{"startOffset":45866,"endOffset":46535,"count":0}],"isBlockCoverage":false},{"functionName":"_setSimultaneousAccepts","ranges":[{"startOffset":46574,"endOffset":46815,"count":0}],"isBlockCoverage":false}]},{"scriptId":"90","url":"internal/net.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1694,"count":1}],"isBlockCoverage":false},{"functionName":"isIPv4","ranges":[{"startOffset":974,"endOffset":1022,"count":0}],"isBlockCoverage":false},{"functionName":"isIPv6","ranges":[{"startOffset":1024,"endOffset":1072,"count":0}],"isBlockCoverage":false},{"functionName":"isIP","ranges":[{"startOffset":1074,"endOffset":1160,"count":0}],"isBlockCoverage":false},{"functionName":"makeSyncWrite","ranges":[{"startOffset":1162,"endOffset":1576,"count":0}],"isBlockCoverage":false}]},{"scriptId":"91","url":"internal/stream_base_commons.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":7120,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":987,"endOffset":1012,"count":0}],"isBlockCoverage":false},{"functionName":"handleWriteReq","ranges":[{"startOffset":1131,"endOffset":1986,"count":163},{"startOffset":1231,"endOffset":1398,"count":0},{"startOffset":1403,"endOffset":1417,"count":0},{"startOffset":1422,"endOffset":1486,"count":0},{"startOffset":1574,"endOffset":1636,"count":0},{"startOffset":1641,"endOffset":1653,"count":0},{"startOffset":1658,"endOffset":1671,"count":0},{"startOffset":1676,"endOffset":1691,"count":0},{"startOffset":1696,"endOffset":1760,"count":0},{"startOffset":1765,"endOffset":1980,"count":0}],"isBlockCoverage":true},{"functionName":"onWriteComplete","ranges":[{"startOffset":1988,"endOffset":2498,"count":0}],"isBlockCoverage":false},{"functionName":"createWriteWrap","ranges":[{"startOffset":2500,"endOffset":2701,"count":163}],"isBlockCoverage":true},{"functionName":"writevGeneric","ranges":[{"startOffset":2703,"endOffset":3344,"count":0}],"isBlockCoverage":false},{"functionName":"writeGeneric","ranges":[{"startOffset":3346,"endOffset":3553,"count":163}],"isBlockCoverage":true},{"functionName":"afterWriteDispatched","ranges":[{"startOffset":3555,"endOffset":3864,"count":163},{"startOffset":3728,"endOffset":3793,"count":0},{"startOffset":3828,"endOffset":3862,"count":0}],"isBlockCoverage":true},{"functionName":"onStreamRead","ranges":[{"startOffset":3866,"endOffset":5991,"count":0}],"isBlockCoverage":false},{"functionName":"setStreamTimeout","ranges":[{"startOffset":5993,"endOffset":6895,"count":0}],"isBlockCoverage":false}]},{"scriptId":"92","url":"internal/dtrace.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":568,"count":1}],"isBlockCoverage":false},{"functionName":"DTRACE_HTTP_CLIENT_REQUEST","ranges":[{"startOffset":97,"endOffset":105,"count":0}],"isBlockCoverage":false},{"functionName":"DTRACE_HTTP_CLIENT_RESPONSE","ranges":[{"startOffset":139,"endOffset":147,"count":0}],"isBlockCoverage":false},{"functionName":"DTRACE_HTTP_SERVER_REQUEST","ranges":[{"startOffset":180,"endOffset":188,"count":0}],"isBlockCoverage":false},{"functionName":"DTRACE_HTTP_SERVER_RESPONSE","ranges":[{"startOffset":222,"endOffset":230,"count":0}],"isBlockCoverage":false},{"functionName":"DTRACE_NET_SERVER_CONNECTION","ranges":[{"startOffset":265,"endOffset":273,"count":0}],"isBlockCoverage":false},{"functionName":"DTRACE_NET_STREAM_END","ranges":[{"startOffset":301,"endOffset":309,"count":0}],"isBlockCoverage":false}]}],"timestamp":42.638209} \ No newline at end of file diff --git a/branch.beta/.build/coverage/index.html b/branch.beta/.build/coverage/index.html new file mode 100644 index 000000000..7bc79c61c --- /dev/null +++ b/branch.beta/.build/coverage/index.html @@ -0,0 +1,153 @@ + + + +coverage-report + + + +
    +
    coverage-report
    + + + + + + + + + + + + + + + + + + +
    +
    + + diff --git a/branch.beta/.build/coverage/jslint.js.html b/branch.beta/.build/coverage/jslint.js.html new file mode 100644 index 000000000..a0b8cf715 --- /dev/null +++ b/branch.beta/.build/coverage/jslint.js.html @@ -0,0 +1,6018 @@ + + + +coverage-report + + + +
    +
    coverage-report
    +
    files coveredlines
    + ./
    +
    +
    +
    +
    + 99.01 %
    + 6223 / 6285 +
    + ./ jslint.js
    +
    +
    +
    +
    + 98.94 %
    + 5821 / 5883 +
    + ./ test.js
    +
    +
    +
    +
    + 100.00 %
    + 402 / 402 +
    + + + + + + + + + + +
    files coveredlines
    + ./ jslint.js
    +
    +
    +
    +
    + 98.94 %
    + 5821 / 5883 +
    +
    +
    +
        1.      1#!/usr/bin/env node
    +
        2.      1// jslint.js
    +
        3.      1// Copyright (c) 2015 Douglas Crockford  (www.JSLint.com)
    +
        4.      1
    +
        5.      1// Permission is hereby granted, free of charge, to any person obtaining a copy
    +
        6.      1// of this software and associated documentation files (the "Software"), to deal
    +
        7.      1// in the Software without restriction, including without limitation the rights
    +
        8.      1// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +
        9.      1// copies of the Software, and to permit persons to whom the Software is
    +
       10.      1// furnished to do so, subject to the following conditions:
    +
       11.      1
    +
       12.      1// The above copyright notice and this permission notice shall be included in
    +
       13.      1// all copies or substantial portions of the Software.
    +
       14.      1
    +
       15.      1// The Software shall be used for Good, not Evil.
    +
       16.      1
    +
       17.      1// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +
       18.      1// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +
       19.      1// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    +
       20.      1// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +
       21.      1// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +
       22.      1// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    +
       23.      1// SOFTWARE.
    +
       24.      1
    +
       25.      1// jslint(source, option_object, global_array) is a function that takes 3
    +
       26.      1// arguments. The second two arguments are optional.
    +
       27.      1
    +
       28.      1//      source          A text to analyze, a string or an array of strings.
    +
       29.      1//      option_object   An object whose keys correspond to option names.
    +
       30.      1//      global_array    An array of strings containing global variables that
    +
       31.      1//                      the file is allowed readonly access.
    +
       32.      1
    +
       33.      1// jslint returns an object containing its results. The object contains a lot
    +
       34.      1// of valuable information. It can be used to generate reports. The object
    +
       35.      1// contains:
    +
       36.      1
    +
       37.      1//      directives: an array of directive comment tokens.
    +
       38.      1//      edition: the version of JSLint that did the analysis.
    +
       39.      1//      exports: the names exported from the module.
    +
       40.      1//      froms: an array of strings representing each of the imports.
    +
       41.      1//      functions: an array of objects that represent all of the functions
    +
       42.      1//              declared in the file.
    +
       43.      1//      global: an object representing the global object. Its .context property
    +
       44.      1//              is an object containing a property for each global variable.
    +
       45.      1//      id: "(JSLint)"
    +
       46.      1//      json: true if the file is a JSON text.
    +
       47.      1//      lines: an array of strings, the source.
    +
       48.      1//      module: true if an import or export statement was used.
    +
       49.      1//      ok: true if no warnings were generated. This is what you want.
    +
       50.      1//      option: the option argument.
    +
       51.      1//      property: a property object.
    +
       52.      1//      stop: true if JSLint was unable to finish. You don't want this.
    +
       53.      1//      tokens: an array of objects representing the tokens in the file.
    +
       54.      1//      tree: the token objects arranged in a tree.
    +
       55.      1//      warnings: an array of warning objects. A warning object can contain:
    +
       56.      1//          name: "JSLintError"
    +
       57.      1//          column: A column number in the file.
    +
       58.      1//          line: A line number in the file.
    +
       59.      1//          code: A warning code string.
    +
       60.      1//          message: The warning message string.
    +
       61.      1//          a: Exhibit A.
    +
       62.      1//          b: Exhibit B.
    +
       63.      1//          c: Exhibit C.
    +
       64.      1//          d: Exhibit D.
    +
       65.      1
    +
       66.      1// jslint works in several phases. In any of these phases, errors might be
    +
       67.      1// found. Sometimes JSLint is able to recover from an error and continue
    +
       68.      1// parsing. In some cases, it cannot and will stop early. If that should happen,
    +
       69.      1// repair your code and try again.
    +
       70.      1
    +
       71.      1// Phases:
    +
       72.      1
    +
       73.      1//      1. If the source is a single string, split it into an array of strings.
    +
       74.      1//      2. Turn the source into an array of tokens.
    +
       75.      1//      3. Furcate the tokens into a parse tree.
    +
       76.      1//      4. Walk the tree, traversing all of the nodes of the tree. It is a
    +
       77.      1//          recursive traversal. Each node may be processed on the way down
    +
       78.      1//          (preaction) and on the way up (postaction).
    +
       79.      1//      5. Check the whitespace between the tokens.
    +
       80.      1
    +
       81.      1// jslint can also examine JSON text. It decides that a file is JSON text if
    +
       82.      1// the first token is "[" or "{". Processing of JSON text is much simpler than
    +
       83.      1// the processing of JavaScript programs. Only the first three phases are
    +
       84.      1// required.
    +
       85.      1
    +
       86.      1// WARNING: JSLint will hurt your feelings.
    +
       87.      1
    +
       88.      1/*jslint node*/
    +
       89.      1
    +
       90.      1/*property
    +
       91.      1    unordered,
    +
       92.      1    JSLINT_CLI, a, all, and, argv, arity, assign, b, bad_assignment_a,
    +
       93.      1    bad_directive_a, bad_get, bad_module_name_a, bad_option_a, bad_property_a,
    +
       94.      1    bad_set, bitwise, block, body, browser, c, calls, catch, cli_mode, closer,
    +
       95.      1    closure, code, column, concat, console_error, constant, context, convert,
    +
       96.      1    couch, create, d, dead, debug, default, devel, directive, directives,
    +
       97.      1    disrupt, dot, duplicate_a, early_stop, edition, ellipsis, else, empty_block,
    +
       98.      1    env, error, eval, every, exec, exit, expected_a, expected_a_at_b_c,
    +
       99.      1    expected_a_b, expected_a_b_from_c_d, expected_a_before_b,
    +
      100.      1    expected_a_next_at_b, expected_digits_after_a, expected_four_digits,
    +
      101.      1    expected_identifier_a, expected_line_break_a_b, expected_regexp_factor_a,
    +
      102.      1    expected_space_a_b, expected_statements_a, expected_string_a,
    +
      103.      1    expected_type_string_a, exports, expression, extra, file, finally, flag,
    +
      104.      1    for, forEach, formatted_message, free, freeze, freeze_exports, from, froms,
    +
      105.      1    fud, fudge, function_in_loop, functions, g, getset, global, has_await, i,
    +
      106.      1    id, identifier, import, inc, indexOf, infix_in, init, initial, isArray,
    +
      107.      1    isNaN, is_async, join, json, keys, label, label_a, lbp, led, length, level,
    +
      108.      1    line, line_offset, lines, live, long, loop, m, map, margin, match, message,
    +
      109.      1    misplaced_a, misplaced_directive_a, missing_await_statement,
    +
      110.      1    missing_browser, missing_m, module, naked_block, name, names,
    +
      111.      1    nested_comment, node, not_label_a, now, nr, nud, number_isNaN, ok, open,
    +
      112.      1    opening, option, out_of_scope_a, padStart, parameters, parent, pop,
    +
      113.      1    promises, property, push, quote, raw, readFile, readdir, redefinition_a_b,
    +
      114.      1    repeat, replace, required_a_optional_b, reserved_a, role, search, shebang,
    +
      115.      1    signature, single, slice, some, sort, source, split, stack, stack_trace,
    +
      116.      1    startsWith, statement, stop, subscript_a, switch, test, test_internal_error,
    +
      117.      1    then, this, thru, todo_comment, tokens, too_long, too_many_digits, tree,
    +
      118.      1    trim, try, type, u, unclosed_comment, unclosed_mega, unclosed_string,
    +
      119.      1    undeclared_a, unexpected_a, unexpected_a_after_b, unexpected_a_before_b,
    +
      120.      1    unexpected_at_top_level_a, unexpected_char_a, unexpected_comment,
    +
      121.      1    unexpected_directive_a, unexpected_expression_a, unexpected_label_a,
    +
      122.      1    unexpected_parens, unexpected_space_a_b, unexpected_statement_a,
    +
      123.      1    unexpected_trailing_space, unexpected_typeof_a, uninitialized_a,
    +
      124.      1    unordered_param_a, unordered_property_a, unreachable_a,
    +
      125.      1    unregistered_property_a, unused_a, use_double, use_open, use_spaces, used,
    +
      126.      1    value, var_loop, var_switch, variable, versions, warning, warnings,
    +
      127.      1    weird_condition_a, weird_expression_a, weird_loop, weird_relation_a, white,
    +
      128.      1    wrap_condition, wrap_immediate, wrap_parameter, wrap_regexp, wrap_unary,
    +
      129.      1    wrapped, writable, y
    +
      130.      1*/
    +
      131.      1
    +
      132.      1const edition = "v2021.5.27";
    +
      133.      1
    +
      134.   7925function assert_or_throw(passed, message) {
    +
      135.   7925
    +
      136.   7925// this function will throw <message> if <passed> is falsy
    +
      137.   7925
    +
      138.      1    if (!passed) {
    +
      139.      1        throw new Error(`This was caused by a bug in JSLint.
    +
      140.      1Please open an issue with this stack-trace at
    +
      141.      1https://github.com/jslint-org/jslint/issues.
    +
      142.      1edition = "${edition}";` + "\n" + message);
    +
      143.      1    }
    +
      144.   7925}
    +
      145.      1
    +
      146.   3371function empty() {
    +
      147.   3371
    +
      148.   3371// The empty function produces a new empty object that inherits nothing. This is
    +
      149.   3371// much better than '{}' because confusions around accidental method names like
    +
      150.   3371// 'constructor' are completely avoided.
    +
      151.   3371
    +
      152.   3371    return Object.create(null);
    +
      153.   3371}
    +
      154.      1
    +
      155.    762function populate(array, object = empty(), value = true) {
    +
      156.    762
    +
      157.    762// Augment an object by taking property names from an array of strings.
    +
      158.    762
    +
      159.  17758    array.forEach(function (name) {
    +
      160.  17758        object[name] = value;
    +
      161.  17758    });
    +
      162.    762    return object;
    +
      163.    762}
    +
      164.      1
    +
      165.      1const allowed_option = {
    +
      166.      1
    +
      167.      1// These are the options that are recognized in the option object or that may
    +
      168.      1// appear in a /*jslint*/ directive. Most options will have a boolean value,
    +
      169.      1// usually true. Some options will also predefine some number of global
    +
      170.      1// variables.
    +
      171.      1
    +
      172.      1    bitwise: true,
    +
      173.      1    browser: [
    +
      174.      1        "caches", "CharacterData", "clearInterval", "clearTimeout", "document",
    +
      175.      1        "DocumentType", "DOMException", "Element", "Event", "event", "fetch",
    +
      176.      1        "FileReader", "FontFace", "FormData", "history", "IntersectionObserver",
    +
      177.      1        "localStorage", "location", "MutationObserver", "name", "navigator",
    +
      178.      1        "screen", "sessionStorage", "setInterval", "setTimeout", "Storage",
    +
      179.      1        "TextDecoder", "TextEncoder", "URL", "window", "Worker",
    +
      180.      1        "XMLHttpRequest"
    +
      181.      1    ],
    +
      182.      1    convert: true,
    +
      183.      1    couch: [
    +
      184.      1        "emit", "getRow", "isArray", "log", "provides", "registerType",
    +
      185.      1        "require", "send", "start", "sum", "toJSON"
    +
      186.      1    ],
    +
      187.      1    debug: true,
    +
      188.      1    devel: [
    +
      189.      1        "alert", "confirm", "console", "prompt"
    +
      190.      1    ],
    +
      191.      1    eval: true,
    +
      192.      1    for: true,
    +
      193.      1    fudge: true,
    +
      194.      1    getset: true,
    +
      195.      1    long: true,
    +
      196.      1    node: [
    +
      197.      1        "Buffer", "clearImmediate", "clearInterval", "clearTimeout",
    +
      198.      1        "console", "exports", "module", "process", "require",
    +
      199.      1        "setImmediate", "setInterval", "setTimeout", "TextDecoder",
    +
      200.      1        "TextEncoder", "URL", "URLSearchParams", "__dirname", "__filename"
    +
      201.      1    ],
    +
      202.      1    single: true,
    +
      203.      1    test_internal_error: true,
    +
      204.      1    this: true,
    +
      205.      1    unordered: true,
    +
      206.      1    white: true
    +
      207.      1};
    +
      208.      1
    +
      209.      1const anticondition = populate([
    +
      210.      1    "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%",
    +
      211.      1    "typeof", "(number)", "(string)"
    +
      212.      1]);
    +
      213.      1
    +
      214.      1// These are the bitwise operators.
    +
      215.      1
    +
      216.      1const bitwiseop = populate([
    +
      217.      1    "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=",
    +
      218.      1    ">>>", ">>>="
    +
      219.      1]);
    +
      220.      1
    +
      221.      1const escapeable = populate([
    +
      222.      1    "\\", "/", "`", "b", "f", "n", "r", "t"
    +
      223.      1]);
    +
      224.      1
    +
      225.      1const opener = {
    +
      226.      1
    +
      227.      1// The open and close pairs.
    +
      228.      1
    +
      229.      1    "${": "}",      // mega
    +
      230.      1    "(": ")",       // paren
    +
      231.      1    "[": "]",       // bracket
    +
      232.      1    "{": "}"        // brace
    +
      233.      1};
    +
      234.      1
    +
      235.      1// The relational operators.
    +
      236.      1
    +
      237.      1const relationop = populate([
    +
      238.      1    "!=", "!==", "==", "===", "<", "<=", ">", ">="
    +
      239.      1]);
    +
      240.      1
    +
      241.      1// This is the set of infix operators that require a space on each side.
    +
      242.      1
    +
      243.      1const spaceop = populate([
    +
      244.      1    "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/",
    +
      245.      1    "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=",
    +
      246.      1    ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||"
    +
      247.      1]);
    +
      248.      1
    +
      249.      1const standard = [
    +
      250.      1
    +
      251.      1// These are the globals that are provided by the language standard.
    +
      252.      1
    +
      253.      1    "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "Error", "EvalError",
    +
      254.      1    "Float32Array", "Float64Array", "Generator", "GeneratorFunction",
    +
      255.      1    "Int16Array", "Int32Array", "Int8Array", "Intl", "JSON", "Map", "Math",
    +
      256.      1    "Number", "Object", "Promise", "Proxy", "RangeError", "ReferenceError",
    +
      257.      1    "Reflect", "RegExp", "Set", "String", "Symbol", "SyntaxError", "System",
    +
      258.      1    "TypeError", "URIError", "Uint16Array", "Uint32Array", "Uint8Array",
    +
      259.      1    "Uint8ClampedArray", "WeakMap", "WeakSet", "decodeURI",
    +
      260.      1    "decodeURIComponent", "encodeURI", "encodeURIComponent", "globalThis",
    +
      261.      1    "import", "parseFloat", "parseInt"
    +
      262.      1];
    +
      263.      1
    +
      264.      1const bundle = {
    +
      265.      1
    +
      266.      1// The bundle contains the raw text messages that are generated by jslint. It
    +
      267.      1// seems that they are all error messages and warnings. There are no "Atta
    +
      268.      1// boy!" or "You are so awesome!" messages. There is no positive reinforcement
    +
      269.      1// or encouragement. This relentless negativity can undermine self-esteem and
    +
      270.      1// wound the inner child. But if you accept it as sound advice rather than as
    +
      271.      1// personal criticism, it can make your programs better.
    +
      272.      1
    +
      273.      1    and: "The '&&' subexpression should be wrapped in parens.",
    +
      274.      1    bad_assignment_a: "Bad assignment to '{a}'.",
    +
      275.      1    bad_directive_a: "Bad directive '{a}'.",
    +
      276.      1    bad_get: "A get function takes no parameters.",
    +
      277.      1    bad_module_name_a: "Bad module name '{a}'.",
    +
      278.      1    bad_option_a: "Bad option '{a}'.",
    +
      279.      1    bad_property_a: "Bad property name '{a}'.",
    +
      280.      1    bad_set: "A set function takes one parameter.",
    +
      281.      1    duplicate_a: "Duplicate '{a}'.",
    +
      282.      1    empty_block: "Empty block.",
    +
      283.      1    expected_a: "Expected '{a}'.",
    +
      284.      1    expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.",
    +
      285.      1    expected_a_b: "Expected '{a}' and instead saw '{b}'.",
    +
      286.      1    expected_a_b_from_c_d: (
    +
      287.      1        "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'."
    +
      288.      1    ),
    +
      289.      1    expected_a_before_b: "Expected '{a}' before '{b}'.",
    +
      290.      1    expected_digits_after_a: "Expected digits after '{a}'.",
    +
      291.      1    expected_four_digits: "Expected four digits after '\\u'.",
    +
      292.      1    expected_identifier_a: "Expected an identifier and instead saw '{a}'.",
    +
      293.      1    expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.",
    +
      294.      1    expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.",
    +
      295.      1    expected_space_a_b: "Expected one space between '{a}' and '{b}'.",
    +
      296.      1    expected_statements_a: "Expected statements before '{a}'.",
    +
      297.      1    expected_string_a: "Expected a string and instead saw '{a}'.",
    +
      298.      1    expected_type_string_a: "Expected a type string and instead saw '{a}'.",
    +
      299.      1    freeze_exports: (
    +
      300.      1        "Expected 'Object.freeze('. All export values should be frozen."
    +
      301.      1    ),
    +
      302.      1    function_in_loop: "Don't make functions within a loop.",
    +
      303.      1    infix_in: (
    +
      304.      1        "Unexpected 'in'. Compare with undefined, "
    +
      305.      1        + "or use the hasOwnProperty method instead."
    +
      306.      1    ),
    +
      307.      1    label_a: "'{a}' is a statement label.",
    +
      308.      1    misplaced_a: "Place '{a}' at the outermost level.",
    +
      309.      1    misplaced_directive_a: (
    +
      310.      1        "Place the '/*{a}*/' directive before the first statement."
    +
      311.      1    ),
    +
      312.      1    missing_await_statement: "Expected await statement in async function.",
    +
      313.      1    missing_browser: "/*global*/ requires the Assume a browser option.",
    +
      314.      1    missing_m: "Expected 'm' flag on a multiline regular expression.",
    +
      315.      1    naked_block: "Naked block.",
    +
      316.      1    nested_comment: "Nested comment.",
    +
      317.      1    not_label_a: "'{a}' is not a label.",
    +
      318.      1    number_isNaN: "Use Number.isNaN function to compare with NaN.",
    +
      319.      1    out_of_scope_a: "'{a}' is out of scope.",
    +
      320.      1    redefinition_a_b: "Redefinition of '{a}' from line {b}.",
    +
      321.      1    required_a_optional_b: (
    +
      322.      1        "Required parameter '{a}' after optional parameter '{b}'."
    +
      323.      1    ),
    +
      324.      1    reserved_a: "Reserved name '{a}'.",
    +
      325.      1    subscript_a: "['{a}'] is better written in dot notation.",
    +
      326.      1    todo_comment: "Unexpected TODO comment.",
    +
      327.      1    too_long: "Line is longer than 80 characters.",
    +
      328.      1    too_many_digits: "Too many digits.",
    +
      329.      1    unclosed_comment: "Unclosed comment.",
    +
      330.      1    unclosed_mega: "Unclosed mega literal.",
    +
      331.      1    unclosed_string: "Unclosed string.",
    +
      332.      1    undeclared_a: "Undeclared '{a}'.",
    +
      333.      1    unexpected_a: "Unexpected '{a}'.",
    +
      334.      1    unexpected_a_after_b: "Unexpected '{a}' after '{b}'.",
    +
      335.      1    unexpected_a_before_b: "Unexpected '{a}' before '{b}'.",
    +
      336.      1    unexpected_at_top_level_a: "Expected '{a}' to be in a function.",
    +
      337.      1    unexpected_char_a: "Unexpected character '{a}'.",
    +
      338.      1    unexpected_comment: "Unexpected comment.",
    +
      339.      1    unexpected_directive_a: "When using modules, don't use directive '/*{a}'.",
    +
      340.      1    unexpected_expression_a: (
    +
      341.      1        "Unexpected expression '{a}' in statement position."
    +
      342.      1    ),
    +
      343.      1    unexpected_label_a: "Unexpected label '{a}'.",
    +
      344.      1    unexpected_parens: "Don't wrap function literals in parens.",
    +
      345.      1    unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.",
    +
      346.      1    unexpected_statement_a: (
    +
      347.      1        "Unexpected statement '{a}' in expression position."
    +
      348.      1    ),
    +
      349.      1    unexpected_trailing_space: "Unexpected trailing space.",
    +
      350.      1    unexpected_typeof_a: (
    +
      351.      1        "Unexpected 'typeof'. Use '===' to compare directly with {a}."
    +
      352.      1    ),
    +
      353.      1    uninitialized_a: "Uninitialized '{a}'.",
    +
      354.      1    unordered_param_a: (
    +
      355.      1        "Parameter '{a}' not listed in alphabetical order."
    +
      356.      1    ),
    +
      357.      1    unordered_property_a: (
    +
      358.      1        "Property name '{a}' not listed in alphabetical order."
    +
      359.      1    ),
    +
      360.      1    unreachable_a: "Unreachable '{a}'.",
    +
      361.      1    unregistered_property_a: "Unregistered property name '{a}'.",
    +
      362.      1    unused_a: "Unused '{a}'.",
    +
      363.      1    use_double: "Use double quotes, not single quotes.",
    +
      364.      1    use_open: (
    +
      365.      1        "Wrap a ternary expression in parens, "
    +
      366.      1        + "with a line break after the left paren."
    +
      367.      1    ),
    +
      368.      1    use_spaces: "Use spaces, not tabs.",
    +
      369.      1    var_loop: "Don't declare variables in a loop.",
    +
      370.      1    var_switch: "Don't declare variables in a switch.",
    +
      371.      1    weird_condition_a: "Weird condition '{a}'.",
    +
      372.      1    weird_expression_a: "Weird expression '{a}'.",
    +
      373.      1    weird_loop: "Weird loop.",
    +
      374.      1    weird_relation_a: "Weird relation '{a}'.",
    +
      375.      1    wrap_condition: "Wrap the condition in parens.",
    +
      376.      1    wrap_immediate: (
    +
      377.      1        "Wrap an immediate function invocation in parentheses to assist "
    +
      378.      1        + "the reader in understanding that the expression is the result "
    +
      379.      1        + "of a function, and not the function itself."
    +
      380.      1    ),
    +
      381.      1    wrap_parameter: "Wrap the parameter in parens.",
    +
      382.      1    wrap_regexp: "Wrap this regexp in parens to avoid confusion.",
    +
      383.      1    wrap_unary: "Wrap the unary expression in parens."
    +
      384.      1};
    +
      385.      1
    +
      386.      1// Regular expression literals:
    +
      387.      1
    +
      388.     12function tag_regexp(strings) {
    +
      389.     12    return new RegExp(strings.raw[0].replace(/\s/g, ""));
    +
      390.     12}
    +
      391.      1
    +
      392.      1// supplant {variables}
    +
      393.      1const rx_supplant = /\{([^{}]*)\}/g;
    +
      394.      1// carriage return, carriage return linefeed, or linefeed
    +
      395.      1const rx_crlf = tag_regexp `
    +
      396.      1      \n
    +
      397.      1    | \r \n?
    +
      398.      1`;
    +
      399.      1// identifier
    +
      400.      1const rx_identifier = tag_regexp ` ^(
    +
      401.      1    [ a-z A-Z _ $ ]
    +
      402.      1    [ a-z A-Z 0-9 _ $ ]*
    +
      403.      1)$`;
    +
      404.      1const rx_module = tag_regexp ` ^ [ a-z A-Z 0-9 _ $ : . @ \- \/ ]+ $ `;
    +
      405.      1const rx_bad_property = tag_regexp `
    +
      406.      1    ^_
    +
      407.      1  | \$
    +
      408.      1  | Sync $
    +
      409.      1  | _ $
    +
      410.      1`;
    +
      411.      1// star slash
    +
      412.      1const rx_star_slash = tag_regexp ` \* \/ `;
    +
      413.      1// slash star
    +
      414.      1const rx_slash_star = tag_regexp ` \/ \* `;
    +
      415.      1// slash star or ending slash
    +
      416.      1const rx_slash_star_or_slash = tag_regexp ` \/ \* | \/ $ `;
    +
      417.      1// uncompleted work comment
    +
      418.      1const rx_todo = tag_regexp ` \b (?:
    +
      419.      1    todo
    +
      420.      1  | TO \s? DO
    +
      421.      1  | HACK
    +
      422.      1) \b `;
    +
      423.      1// tab
    +
      424.      1const rx_tab = /\t/g;
    +
      425.      1// directive
    +
      426.      1const rx_directive = tag_regexp ` ^ (
    +
      427.      1    jslint
    +
      428.      1  | property
    +
      429.      1  | global
    +
      430.      1) \s+ ( .* ) $ `;
    +
      431.      1const rx_directive_part = tag_regexp ` ^ (
    +
      432.      1    [ a-z A-Z $ _ ] [ a-z A-Z 0-9 $ _ ]*
    +
      433.      1) (?:
    +
      434.      1    : \s* ( true | false )
    +
      435.      1)? ,? \s* ( .* ) $ `;
    +
      436.      1// token
    +
      437.      1const rx_token = tag_regexp ` ^ (
    +
      438.      1    (\s+)
    +
      439.      1  | (
    +
      440.      1      [ a-z A-Z _ $ ]
    +
      441.      1      [ a-z A-Z 0-9 _ $ ]*
    +
      442.      1    )
    +
      443.      1  | [
    +
      444.      1      ( ) { } \[ \] , : ; ' " ~ \`
    +
      445.      1  ]
    +
      446.      1  | \? [ ? . ]?
    +
      447.      1  | = (?:
    +
      448.      1        = =?
    +
      449.      1      | >
    +
      450.      1    )?
    +
      451.      1  | \.+
    +
      452.      1  | \* [ * \/ = ]?
    +
      453.      1  | \/ [ * \/ ]?
    +
      454.      1  | \+ [ = + ]?
    +
      455.      1  | - [ = \- ]?
    +
      456.      1  | [ \^ % ] =?
    +
      457.      1  | & [ & = ]?
    +
      458.      1  | \| [ | = ]?
    +
      459.      1  | >{1,3} =?
    +
      460.      1  | < <? =?
    +
      461.      1  | ! (?:
    +
      462.      1        !
    +
      463.      1      | = =?
    +
      464.      1    )?
    +
      465.      1  | (
    +
      466.      1        0
    +
      467.      1      | [ 1-9 ] [ 0-9 ]*
    +
      468.      1    )
    +
      469.      1) ( .* ) $ `;
    +
      470.      1const rx_digits = /^[0-9]*/;
    +
      471.      1const rx_hexs = /^[0-9A-F]*/i;
    +
      472.      1const rx_octals = /^[0-7]*/;
    +
      473.      1const rx_bits = /^[01]*/;
    +
      474.      1// mega
    +
      475.      1const rx_mega = /[`\\]|\$\{/;
    +
      476.      1// JSON number
    +
      477.      1const rx_JSON_number = tag_regexp ` ^
    +
      478.      1    -?
    +
      479.      1    (?: 0 | [ 1-9 ] \d* )
    +
      480.      1    (?: \. \d* )?
    +
      481.      1    (?: [ e E ] [ \- + ]? \d+ )?
    +
      482.      1$ `;
    +
      483.      1// initial cap
    +
      484.      1const rx_cap = /^[A-Z]/;
    +
      485.      1
    +
      486.    167function is_letter(string) {
    +
      487.    167    return (
    +
      488.     77        (string >= "a" && string <= "z\uffff")
    +
      489.     93        || (string >= "A" && string <= "Z\uffff")
    +
      490.    167    );
    +
      491.    167}
    +
      492.      1
    +
      493.      1let anon;               // The guessed name for anonymous functions.
    +
      494.      1let blockage;           // The current block.
    +
      495.      1let block_stack;        // The stack of blocks.
    +
      496.      1let declared_globals;   // The object containing the global declarations.
    +
      497.      1let directives;         // The directive comments.
    +
      498.      1let directive_mode;     // true if directives are still allowed.
    +
      499.      1let early_stop;         // true if JSLint cannot finish.
    +
      500.      1let exports;            // The exported names and values.
    +
      501.      1let froms;              // The array collecting all import-from strings.
    +
      502.      1let fudge;              // true if the natural numbers start with 1.
    +
      503.      1let functionage;        // The current function.
    +
      504.      1let functions;          // The array containing all of the functions.
    +
      505.      1let global;             // The global object; the outermost context.
    +
      506.      1let json_mode;          // true if parsing JSON.
    +
      507.      1let lines;              // The array containing source lines.
    +
      508.      1let mega_mode;          // true if currently parsing a megastring literal.
    +
      509.      1let module_mode;        // true if import or export was used.
    +
      510.      1let next_token;         // The next token to be examined in the parse.
    +
      511.      1let option;             // The options parameter.
    +
      512.      1let property;           // The object containing the tallied property names.
    +
      513.      1let shebang;            // true if a #! was seen on the first line.
    +
      514.      1let stack;              // The stack of functions.
    +
      515.      1let syntax;             // The object containing the parser.
    +
      516.      1let token;              // The current token being examined in the parse.
    +
      517.      1let token_nr;           // The number of the next token.
    +
      518.      1let tokens;             // The array of tokens.
    +
      519.      1let tenure;             // The predefined property registry.
    +
      520.      1let tree;               // The abstract parse tree.
    +
      521.      1let var_mode;           // "var" if using var; "let" if using let.
    +
      522.      1let warnings;           // The array collecting all generated warnings.
    +
      523.      1
    +
      524.      1// Error reportage functions:
    +
      525.      1
    +
      526.    771function artifact(the_token) {
    +
      527.    771
    +
      528.    771// Return a string representing an artifact.
    +
      529.    771
    +
      530.     15    if (the_token === undefined) {
    +
      531.     15        the_token = next_token;
    +
      532.     15    }
    +
      533.    771    return (
    +
      534.    764        (the_token.id === "(string)" || the_token.id === "(number)")
    +
      535.     75        ? String(the_token.value)
    +
      536.    696        : the_token.id
    +
      537.    771    );
    +
      538.    771}
    +
      539.      1
    +
      540.    811function warn_at(code, line, column, a, b, c, d) {
    +
      541.    811
    +
      542.    811// Report an error at some line and column of the program. The warning object
    +
      543.    811// resembles an exception.
    +
      544.    811
    +
      545.    811    const warning = {         // ~~
    +
      546.    811        code,
    +
      547.    811        column,
    +
      548.    811        line,
    +
      549.    811        name: "JSLintError"
    +
      550.    811    };
    +
      551.    794    if (a !== undefined) {
    +
      552.    794        warning.a = a;
    +
      553.    794    }
    +
      554.    308    if (b !== undefined) {
    +
      555.    308        warning.b = b;
    +
      556.    308    }
    +
      557.     30    if (c !== undefined) {
    +
      558.     30        warning.c = c;
    +
      559.     30    }
    +
      560.      3    if (d !== undefined) {
    +
      561.      3        warning.d = d;
    +
      562.      3    }
    +
      563.   1047    warning.message = bundle[code].replace(rx_supplant, function (
    +
      564.   1047        ignore,
    +
      565.   1047        filling
    +
      566.   1047    ) {
    +
      567.   1047        assert_or_throw(
    +
      568.   1047            warning[filling] !== undefined,
    +
      569.   1047            "Expected warning[filling] !== undefined."
    +
      570.   1047        );
    +
      571.   1047        return warning[filling];
    +
      572.   1047    });
    +
      573.    811
    +
      574.    811// Include stack_trace for jslint to debug itself for errors.
    +
      575.    811
    +
      576.      4    if (option.debug) {
    +
      577.      4        warning.stack_trace = new Error().stack;
    +
      578.      4    }
    +
      579.    811    warnings.push(warning);
    +
      580.    811    return warning;
    +
      581.    811}
    +
      582.      1
    +
      583.     17function stop_at(code, line, column, a, b, c, d) {
    +
      584.     17
    +
      585.     17// Same as warn_at, except that it stops the analysis.
    +
      586.     17
    +
      587.     17    throw warn_at(code, line, column, a, b, c, d);
    +
      588.     17}
    +
      589.      1
    +
      590.    756function warn(code, the_token, a, b, c, d) {
    +
      591.    756
    +
      592.    756// Same as warn_at, except the warning will be associated with a specific token.
    +
      593.    756// If there is already a warning on this token, suppress the new one. It is
    +
      594.    756// likely that the first warning will be the most meaningful.
    +
      595.    756
    +
      596.     11    if (the_token === undefined) {
    +
      597.     11        the_token = next_token;
    +
      598.     11    }
    +
      599.    606    if (the_token.warning === undefined) {
    +
      600.    606        the_token.warning = warn_at(
    +
      601.    606            code,
    +
      602.    606            the_token.line,
    +
      603.    606            the_token.from,
    +
      604.    606            a || artifact(the_token),
    +
      605.    606            b,
    +
      606.    606            c,
    +
      607.    606            d
    +
      608.    606        );
    +
      609.    606        return the_token.warning;
    +
      610.    606    }
    +
      611.    756}
    +
      612.      1
    +
      613.     72function stop(code, the_token, a, b, c, d) {
    +
      614.     72
    +
      615.     72// Similar to warn and stop_at. If the token already had a warning, that
    +
      616.     72// warning will be replaced with this new one. It is likely that the stopping
    +
      617.     72// warning will be the more meaningful.
    +
      618.     72
    +
      619.     13    if (the_token === undefined) {
    +
      620.     13        the_token = next_token;
    +
      621.     13    }
    +
      622.     72    delete the_token.warning;
    +
      623.     72    throw warn(code, the_token, a, b, c, d);
    +
      624.     72}
    +
      625.      1
    +
      626.      1// Tokenize:
    +
      627.      1
    +
      628.    368function tokenize(source) {
    +
      629.    368
    +
      630.    368// tokenize takes a source and produces from it an array of token objects.
    +
      631.    368// JavaScript is notoriously difficult to tokenize because of the horrible
    +
      632.    368// interactions between automatic semicolon insertion, regular expression
    +
      633.    368// literals, and now megastring literals. JSLint benefits from eliminating
    +
      634.    368// automatic semicolon insertion and nested megastring literals, which allows
    +
      635.    368// full tokenization to precede parsing.
    +
      636.    368
    +
      637.    368// If the source is not an array, then it is split into lines at the
    +
      638.    368// carriage return/linefeed.
    +
      639.    368
    +
      640.    368    lines = (
    +
      641.    368        Array.isArray(source)
    +
      642.      1        ? source
    +
      643.    367        : source.split(rx_crlf)
    +
      644.    368    );
    +
      645.    368    tokens = [];
    +
      646.    368
    +
      647.    368    let char;                   // a popular character
    +
      648.    368    let column = 0;             // the column number of the next character
    +
      649.    368    let first;                  // the first token
    +
      650.    368    let from;                   // the starting column number of the token
    +
      651.    368    let line = -1;              // the line number of the next character
    +
      652.    368    let nr = 0;                 // the next token number
    +
      653.    368    let previous = global;      // the previous token including comments
    +
      654.    368    let prior = global;         // the previous token excluding comments
    +
      655.    368    let mega_from;              // the starting column of megastring
    +
      656.    368    let mega_line;              // the starting line of megastring
    +
      657.    368    let regexp_seen;            // regular expression literal seen on this line
    +
      658.    368    let snippet = "";           // a piece of string
    +
      659.    368    let source_line = "";       // the remaining line source string
    +
      660.    368    let whole_line = "";        // the whole line source string
    +
      661.    368
    +
      662.      3    if (lines[0].startsWith("#!")) {
    +
      663.      3        line = 0;
    +
      664.      3        shebang = true;
    +
      665.      3    }
    +
      666.    368
    +
      667.  16532    function next_line() {
    +
      668.  16532
    +
      669.  16532// Put the next line of source in source_line. If the line contains tabs,
    +
      670.  16532// replace them with spaces and give a warning. Also warn if the line contains
    +
      671.  16532// unsafe characters or is too damn long.
    +
      672.  16532
    +
      673.  16532        let at;
    +
      674.  16532        if (
    +
      675.  16532            !option.long
    +
      676.  16529            && whole_line.length > 80
    +
      677.      1            && !json_mode
    +
      678.      1            && first
    +
      679.      1            && !regexp_seen
    +
      680.      1        ) {
    +
      681.      1            warn_at("too_long", line, 80);
    +
      682.      1        }
    +
      683.  16532        column = 0;
    +
      684.  16532        line += 1;
    +
      685.  16532        regexp_seen = false;
    +
      686.  16532        source_line = lines[line];
    +
      687.   4147        whole_line = source_line || "";
    +
      688.  16181        if (source_line !== undefined) {
    +
      689.  16181            at = source_line.search(rx_tab);
    +
      690.  16181            if (at >= 0) {
    +
      691.  16181                if (!option.white) {
    +
      692.  16181
    +
      693.  16181// cause: "\t"
    +
      694.  16181
    +
      695.  16181                    warn_at("use_spaces", line, at + 1);
    +
      696.  16181                }
    +
      697.  16181                source_line = source_line.replace(rx_tab, " ");
    +
      698.  16181            }
    +
      699.  16181            if (!option.white && source_line.slice(-1) === " ") {
    +
      700.  16181                warn_at(
    +
      701.  16181                    "unexpected_trailing_space",
    +
      702.  16181                    line,
    +
      703.  16181                    source_line.length - 1
    +
      704.  16181                );
    +
      705.  16181            }
    +
      706.  16181        }
    +
      707.  16532        return source_line;
    +
      708.  16532    }
    +
      709.    368
    +
      710.    368// Most tokens, including the identifiers, operators, and punctuators, can be
    +
      711.    368// found with a regular expression. Regular expressions cannot correctly match
    +
      712.    368// regular expression literals, so we will match those the hard way. String
    +
      713.    368// literals and number literals can be matched by regular expressions, but they
    +
      714.    368// don't provide good warnings. The functions snip, next_char, back_char,
    +
      715.    368// some_digits, and escape help in the parsing of literals.
    +
      716.    368
    +
      717.   5114    function snip() {
    +
      718.   5114
    +
      719.   5114// Remove the last character from snippet.
    +
      720.   5114
    +
      721.   5114        snippet = snippet.slice(0, -1);
    +
      722.   5114    }
    +
      723.    368
    +
      724.  36336    function next_char(match) {
    +
      725.  36336
    +
      726.  36336// Get the next character from the source line. Remove it from the source_line,
    +
      727.  36336// and append it to the snippet. Optionally check that the previous character
    +
      728.  36336// matched an expected value.
    +
      729.  36336
    +
      730.    782        if (match !== undefined && char !== match) {
    +
      731.      3            return stop_at(
    +
      732.      3                (
    +
      733.      3                    char === ""
    +
      734.      3                    ? "expected_a"
    +
      735.      3                    : "expected_a_b"
    +
      736.      3                ),
    +
      737.      3                line,
    +
      738.      3                column - 1,
    +
      739.      3                match,
    +
      740.      3                char
    +
      741.      3            );
    +
      742.  36333        }
    +
      743.  36333        if (source_line) {
    +
      744.  36176            char = source_line[0];
    +
      745.  36176            source_line = source_line.slice(1);
    +
      746.  36176            snippet += char;
    +
      747.  36176        } else {
    +
      748.    157            char = "";
    +
      749.    157            snippet += " ";
    +
      750.  36333        }
    +
      751.  36333        column += 1;
    +
      752.  36333        return char;
    +
      753.  36333    }
    +
      754.    368
    +
      755.   1170    function back_char() {
    +
      756.   1170
    +
      757.   1170// Back up one character by moving a character from the end of the snippet to
    +
      758.   1170// the front of the source_line.
    +
      759.   1170
    +
      760.   1170        char = snippet.slice(-1);
    +
      761.   1170        source_line = char + source_line;
    +
      762.   1170        column -= char.length;
    +
      763.   1170        snip();
    +
      764.   1170        return char;
    +
      765.   1170    }
    +
      766.    368
    +
      767.     52    function some_digits(rx, quiet) {
    +
      768.     52        const digits = source_line.match(rx)[0];
    +
      769.     52        const length = digits.length;
    +
      770.     16        if (!quiet && length === 0) {
    +
      771.      1
    +
      772.      1// cause: "0x"
    +
      773.      1
    +
      774.      1            warn_at("expected_digits_after_a", line, column, snippet);
    +
      775.      1        }
    +
      776.     52        column += length;
    +
      777.     52        source_line = source_line.slice(length);
    +
      778.     52        snippet += digits;
    +
      779.     52        next_char();
    +
      780.     52        return length;
    +
      781.     52    }
    +
      782.    368
    +
      783.    413    function escape(extra) {
    +
      784.    413        next_char("\\");
    +
      785.    233        if (escapeable[char] === true) {
    +
      786.    233            return next_char();
    +
      787.    233        }
    +
      788.    180        if (char === "") {
    +
      789.      1
    +
      790.      1// cause: "\"\\"
    +
      791.      1
    +
      792.      1            return stop_at("unclosed_string", line, column);
    +
      793.    179        }
    +
      794.    179        if (char === "u") {
    +
      795.     35            if (next_char("u") === "{") {
    +
      796.     35                if (json_mode) {
    +
      797.     35                    warn_at("unexpected_a", line, column - 1, char);
    +
      798.     35                }
    +
      799.     35                if (some_digits(rx_hexs) > 5) {
    +
      800.     35
    +
      801.     35// cause: "\"\\u{123456}\""
    +
      802.     35
    +
      803.     35                    warn_at("too_many_digits", line, column - 1);
    +
      804.     35                }
    +
      805.     35                if (char !== "}") {
    +
      806.     35                    stop_at("expected_a_before_b", line, column, "}", char);
    +
      807.     35                }
    +
      808.     35                return next_char();
    +
      809.     35            }
    +
      810.     35            back_char();
    +
      811.     35            if (some_digits(rx_hexs, true) < 4) {
    +
      812.     35
    +
      813.     35// cause: "\"\\u0\""
    +
      814.     35
    +
      815.     35                warn_at("expected_four_digits", line, column - 1);
    +
      816.     35            }
    +
      817.     35            return;
    +
      818.    144        }
    +
      819.    144        if (extra && extra.indexOf(char) >= 0) {
    +
      820.    143            return next_char();
    +
      821.    143        }
    +
      822.      1
    +
      823.      1// cause: "\"\\a\""
    +
      824.      1
    +
      825.      1        warn_at("unexpected_a_before_b", line, column - 2, "\\", char);
    +
      826.      1    }
    +
      827.    368
    +
      828.  56996    function make(id, value, identifier) {
    +
      829.  56996
    +
      830.  56996// Make the token object and append it to the tokens list.
    +
      831.  56996
    +
      832.  56996        const the_token = {
    +
      833.  56996            from,
    +
      834.  56996            id,
    +
      835.  56996            identifier: Boolean(identifier),
    +
      836.  56996            line,
    +
      837.  56996            nr,
    +
      838.  56996            thru: column
    +
      839.  56996        };
    +
      840.  56996        tokens[nr] = the_token;
    +
      841.  56996        nr += 1;
    +
      842.  56996
    +
      843.  56996// Directives must appear before the first statement.
    +
      844.  56996
    +
      845.  55486        if (id !== "(comment)" && id !== ";") {
    +
      846.  51169            directive_mode = false;
    +
      847.  51169        }
    +
      848.  56996
    +
      849.  56996// If the token is to have a value, give it one.
    +
      850.  56996
    +
      851.   6593        if (value !== undefined) {
    +
      852.   6593            the_token.value = value;
    +
      853.   6593        }
    +
      854.  56996
    +
      855.  56996// If this token is an identifier that touches a preceding number, or
    +
      856.  56996// a "/", comment, or regular expression literal that touches a preceding
    +
      857.  56996// comment or regular expression literal, then give a missing space warning.
    +
      858.  56996// This warning is not suppressed by option.white.
    +
      859.  56996
    +
      860.  56996        if (
    +
      861.  56996            previous.line === line
    +
      862.  45014            && previous.thru === from
    +
      863.  28690            && (id === "(comment)" || id === "(regexp)" || id === "/")
    +
      864.     49            && (previous.id === "(comment)" || previous.id === "(regexp)")
    +
      865.      1        ) {
    +
      866.      1            warn(
    +
      867.      1                "expected_space_a_b",
    +
      868.      1                the_token,
    +
      869.      1                artifact(previous),
    +
      870.      1                artifact(the_token)
    +
      871.      1            );
    +
      872.      1        }
    +
      873.   3333        if (previous.id === "." && id === "(number)") {
    +
      874.      3
    +
      875.      3// cause: ".0"
    +
      876.      3
    +
      877.      3            warn("expected_a_before_b", previous, "0", ".");
    +
      878.      3        }
    +
      879.   3333        if (prior.id === "." && the_token.identifier) {
    +
      880.   3328            the_token.dot = true;
    +
      881.   3328        }
    +
      882.  56996
    +
      883.  56996// The previous token is used to detect adjacency problems.
    +
      884.  56996
    +
      885.  56996        previous = the_token;
    +
      886.  56996
    +
      887.  56996// The prior token is a previous token that was not a comment. The prior token
    +
      888.  56996// is used to disambiguate "/", which can mean division or regular expression
    +
      889.  56996// literal.
    +
      890.  56996
    +
      891.  55486        if (previous.id !== "(comment)") {
    +
      892.  55486            prior = previous;
    +
      893.  55486        }
    +
      894.  56996        return the_token;
    +
      895.  56996    }
    +
      896.    368
    +
      897.    595    function parse_directive(the_comment, body) {
    +
      898.    595
    +
      899.    595// JSLint recognizes three directives that can be encoded in comments. This
    +
      900.    595// function processes one item, and calls itself recursively to process the
    +
      901.    595// next one.
    +
      902.    595
    +
      903.    595        const result = body.match(rx_directive_part);
    +
      904.    566        if (result) {
    +
      905.    566            let allowed;
    +
      906.    566            const name = result[1];
    +
      907.    566            const value = result[2];
    +
      908.    566            if (the_comment.directive === "jslint") {
    +
      909.    566                allowed = allowed_option[name];
    +
      910.    566                if (
    +
      911.    566                    typeof allowed === "boolean"
    +
      912.    566                    || typeof allowed === "object"
    +
      913.    566                ) {
    +
      914.    566                    if (value === "true" || value === undefined) {
    +
      915.    566                        option[name] = true;
    +
      916.    566                        if (Array.isArray(allowed)) {
    +
      917.    566                            populate(allowed, declared_globals, false);
    +
      918.    566                        }
    +
      919.    566                    } else if (value === "false") {
    +
      920.    566                        option[name] = false;
    +
      921.    566                    }
    +
      922.    566                } else {
    +
      923.    566
    +
      924.    566// cause: "/*jslint undefined*/"
    +
      925.    566
    +
      926.    566                    warn("bad_option_a", the_comment, name);
    +
      927.    566                }
    +
      928.    566            } else if (the_comment.directive === "property") {
    +
      929.    566                if (tenure === undefined) {
    +
      930.    566                    tenure = empty();
    +
      931.    566                }
    +
      932.    566                tenure[name] = true;
    +
      933.    566            } else if (the_comment.directive === "global") {
    +
      934.    566                if (value) {
    +
      935.    566
    +
      936.    566// cause: "/*global aa:false*/"
    +
      937.    566
    +
      938.    566                    warn("bad_option_a", the_comment, name + ":" + value);
    +
      939.    566                }
    +
      940.    566                declared_globals[name] = false;
    +
      941.    566                module_mode = the_comment;
    +
      942.    566            }
    +
      943.    566            return parse_directive(the_comment, result[3]);
    +
      944.    566        }
    +
      945.     29        if (body) {
    +
      946.      1
    +
      947.      1// cause: "/*jslint !*/"
    +
      948.      1
    +
      949.      1            return stop("bad_directive_a", the_comment, body);
    +
      950.      1        }
    +
      951.    595    }
    +
      952.    368
    +
      953.   1510    function comment(snippet) {
    +
      954.   1510
    +
      955.   1510// Make a comment object. Comments are not allowed in JSON text. Comments can
    +
      956.   1510// include directives and notices of incompletion.
    +
      957.   1510
    +
      958.   1510        const the_comment = make("(comment)", snippet);
    +
      959.     47        if (Array.isArray(snippet)) {
    +
      960.     47            snippet = snippet.join(" ");
    +
      961.     47        }
    +
      962.   1505        if (!option.devel && rx_todo.test(snippet)) {
    +
      963.      1
    +
      964.      1// cause: "//\u0074odo"
    +
      965.      1
    +
      966.      1            warn("todo_comment", the_comment);
    +
      967.      1        }
    +
      968.   1510        const result = snippet.match(rx_directive);
    +
      969.     30        if (result) {
    +
      970.     30            if (!directive_mode) {
    +
      971.     30
    +
      972.     30// cause: "0\n/*global aa*/"
    +
      973.     30
    +
      974.     30                warn_at("misplaced_directive_a", line, from, result[1]);
    +
      975.     30            } else {
    +
      976.     30                the_comment.directive = result[1];
    +
      977.     30                parse_directive(the_comment, result[2]);
    +
      978.     30            }
    +
      979.     30            directives.push(the_comment);
    +
      980.   1509        }
    +
      981.   1509        return the_comment;
    +
      982.   1509    }
    +
      983.    368
    +
      984.     97    function regexp() {
    +
      985.     97
    +
      986.     97// Parse a regular expression literal.
    +
      987.     97
    +
      988.     97        let multi_mode = false;
    +
      989.     97        let result;
    +
      990.     97        let value;
    +
      991.     97        regexp_seen = true;
    +
      992.     97
    +
      993.    546        function quantifier() {
    +
      994.    546
    +
      995.    546// Match an optional quantifier.
    +
      996.    546
    +
      997.    540            if (char === "?" || char === "*" || char === "+") {
    +
      998.     46                next_char();
    +
      999.    500            } else if (char === "{") {
    +
     1000.    500                if (some_digits(rx_digits, true) === 0) {
    +
     1001.    500                    warn_at("expected_a_before_b", line, column, "0", ",");
    +
     1002.    500                }
    +
     1003.    500                if (char === ",") {
    +
     1004.    500                    some_digits(rx_digits, true);
    +
     1005.    500                }
    +
     1006.    500                next_char("}");
    +
     1007.    500            } else {
    +
     1008.    500                return;
    +
     1009.    500            }
    +
     1010.     48            if (char === "?") {
    +
     1011.     28                next_char("?");
    +
     1012.     28            }
    +
     1013.    546        }
    +
     1014.     97
    +
     1015.    103        function subklass() {
    +
     1016.    103
    +
     1017.    103// Match a character in a character class.
    +
     1018.    103
    +
     1019.     23            if (char === "\\") {
    +
     1020.     23                escape("BbDdSsWw-[]^");
    +
     1021.     23                return true;
    +
     1022.     80            }
    +
     1023.     80            if (
    +
     1024.     80                char === ""
    +
     1025.     80                || char === "["
    +
     1026.     79                || char === "]"
    +
     1027.     48                || char === "/"
    +
     1028.     47                || char === "^"
    +
     1029.     47                || char === "-"
    +
     1030.     33            ) {
    +
     1031.     33                return false;
    +
     1032.     47            }
    +
     1033.     47            if (char === " ") {
    +
     1034.      1                warn_at("expected_a_b", line, column, "\\u0020", " ");
    +
     1035.     46            } else if (char === "`" && mega_mode) {
    +
     1036.     46                warn_at("unexpected_a", line, column, "`");
    +
     1037.     47            }
    +
     1038.     47            next_char();
    +
     1039.     47            return true;
    +
     1040.     47        }
    +
     1041.     97
    +
     1042.     90        function ranges() {
    +
     1043.     90
    +
     1044.     90// Match a range of subclasses.
    +
     1045.     90
    +
     1046.     58            if (subklass()) {
    +
     1047.     58                if (char === "-") {
    +
     1048.     58                    next_char("-");
    +
     1049.     58                    if (!subklass()) {
    +
     1050.     58                        return stop_at(
    +
     1051.     58                            "unexpected_a",
    +
     1052.     58                            line,
    +
     1053.     58                            column - 1,
    +
     1054.     58                            "-"
    +
     1055.     58                        );
    +
     1056.     58                    }
    +
     1057.     58                }
    +
     1058.     58                return ranges();
    +
     1059.     58            }
    +
     1060.     90        }
    +
     1061.     97
    +
     1062.     32        function klass() {
    +
     1063.     32
    +
     1064.     32// Match a class.
    +
     1065.     32
    +
     1066.     32            next_char("[");
    +
     1067.      3            if (char === "^") {
    +
     1068.      3                next_char("^");
    +
     1069.      3            }
    +
     1070.     33            (function classy() {
    +
     1071.     33                ranges();
    +
     1072.      2                if (char !== "]" && char !== "") {
    +
     1073.      1                    warn_at(
    +
     1074.      1                        "expected_a_before_b",
    +
     1075.      1                        line,
    +
     1076.      1                        column,
    +
     1077.      1                        "\\",
    +
     1078.      1                        char
    +
     1079.      1                    );
    +
     1080.      1                    next_char();
    +
     1081.      1                    return classy();
    +
     1082.      1                }
    +
     1083.     33            }());
    +
     1084.     32            next_char("]");
    +
     1085.     32        }
    +
     1086.     97
    +
     1087.    120        function choice() {
    +
     1088.    120
    +
     1089.     23            function group() {
    +
     1090.     23
    +
     1091.     23// Match a group that starts with left paren.
    +
     1092.     23
    +
     1093.     23                next_char("(");
    +
     1094.      7                if (char === "?") {
    +
     1095.      7                    next_char("?");
    +
     1096.      7                    if (char === "=" || char === "!") {
    +
     1097.      7                        next_char();
    +
     1098.      7                    } else {
    +
     1099.      7                        next_char(":");
    +
     1100.      7                    }
    +
     1101.     16                } else if (char === ":") {
    +
     1102.     16                    warn_at("expected_a_before_b", line, column, "?", ":");
    +
     1103.     16                }
    +
     1104.     23                choice();
    +
     1105.     23                next_char(")");
    +
     1106.     23            }
    +
     1107.    120
    +
     1108.    665            function factor() {
    +
     1109.    665
    +
     1110.    665// Parse current character in regexp.
    +
     1111.    665
    +
     1112.    665                if (
    +
     1113.    665                    char === ""
    +
     1114.    664                    || char === "/"
    +
     1115.    571                    || char === "]"
    +
     1116.    571                    || char === ")"
    +
     1117.    117                ) {
    +
     1118.    117                    return false;
    +
     1119.    548                }
    +
     1120.    548                if (char === "(") {
    +
     1121.     23                    group();
    +
     1122.     23                    return true;
    +
     1123.    525                }
    +
     1124.    525                if (char === "[") {
    +
     1125.     32                    klass();
    +
     1126.     32                    return true;
    +
     1127.    493                }
    +
     1128.    493                if (char === "\\") {
    +
     1129.    100                    escape("BbDdSsWw^${}[]():=!.|*+?");
    +
     1130.    100                    return true;
    +
     1131.    393                }
    +
     1132.    393                if (
    +
     1133.    393                    char === "?"
    +
     1134.    393                    || char === "+"
    +
     1135.    392                    || char === "*"
    +
     1136.    392                    || char === "}"
    +
     1137.    392                    || char === "{"
    +
     1138.      1                ) {
    +
     1139.      1                    warn_at(
    +
     1140.      1                        "expected_a_before_b",
    +
     1141.      1                        line,
    +
     1142.      1                        column - 1,
    +
     1143.      1                        "\\",
    +
     1144.      1                        char
    +
     1145.      1                    );
    +
     1146.    392                } else if (char === "`") {
    +
     1147.    392                    if (mega_mode) {
    +
     1148.    392                        warn_at("unexpected_a", line, column - 1, "`");
    +
     1149.    392                    }
    +
     1150.    392                } else if (char === " ") {
    +
     1151.    392                    warn_at(
    +
     1152.    392                        "expected_a_b",
    +
     1153.    392                        line,
    +
     1154.    392                        column - 1,
    +
     1155.    392                        "\\s",
    +
     1156.    392                        " "
    +
     1157.    392                    );
    +
     1158.    392                } else if (char === "$") {
    +
     1159.    392                    if (source_line[0] !== "/") {
    +
     1160.    392                        multi_mode = true;
    +
     1161.    392                    }
    +
     1162.    392                } else if (char === "^") {
    +
     1163.    392                    if (snippet !== "^") {
    +
     1164.    392                        multi_mode = true;
    +
     1165.    392                    }
    +
     1166.    393                }
    +
     1167.    393                next_char();
    +
     1168.    393                return true;
    +
     1169.    393            }
    +
     1170.    120
    +
     1171.    665            function sequence(follow) {
    +
     1172.    546                if (factor()) {
    +
     1173.    546                    quantifier();
    +
     1174.    546                    return sequence(true);
    +
     1175.    546                }
    +
     1176.    117                if (!follow) {
    +
     1177.      1
    +
     1178.      1// cause: "/ /"
    +
     1179.      1
    +
     1180.      1                    warn_at("expected_regexp_factor_a", line, column, char);
    +
     1181.      1                }
    +
     1182.    665            }
    +
     1183.    120
    +
     1184.    120// Match a choice (a sequence that can be followed by | and another choice).
    +
     1185.    120
    +
     1186.    120            sequence();
    +
     1187.      0            if (char === "|") {
    +
     1188.      0                next_char("|");
    +
     1189.      0                return choice();
    +
     1190.      0            }
    +
     1191.    120        }
    +
     1192.     97
    +
     1193.     97// Scan the regexp literal. Give a warning if the first character is = because
    +
     1194.     97// /= looks like a division assignment operator.
    +
     1195.     97
    +
     1196.     97        snippet = "";
    +
     1197.     97        next_char();
    +
     1198.      1        if (char === "=") {
    +
     1199.      1            warn_at("expected_a_before_b", line, column, "\\", "=");
    +
     1200.      1        }
    +
     1201.     97        choice();
    +
     1202.     97
    +
     1203.     97// Make sure there is a closing slash.
    +
     1204.     97
    +
     1205.     97        snip();
    +
     1206.     97        value = snippet;
    +
     1207.     97        next_char("/");
    +
     1208.     97
    +
     1209.     97// Process dangling flag letters.
    +
     1210.     97
    +
     1211.     97        const allowed = {
    +
     1212.     97            g: true,
    +
     1213.     97            i: true,
    +
     1214.     97            m: true,
    +
     1215.     97            u: true,
    +
     1216.     97            y: true
    +
     1217.     97        };
    +
     1218.     97        const flag = empty();
    +
     1219.    167        (function make_flag() {
    +
     1220.     74            if (is_letter(char)) {
    +
     1221.     74                if (allowed[char] !== true) {
    +
     1222.     74                    warn_at("unexpected_a", line, column, char);
    +
     1223.     74                }
    +
     1224.     74                allowed[char] = false;
    +
     1225.     74                flag[char] = true;
    +
     1226.     74                next_char();
    +
     1227.     74                return make_flag();
    +
     1228.     74            }
    +
     1229.    167        }());
    +
     1230.     97        back_char();
    +
     1231.     92        if (char === "/" || char === "*") {
    +
     1232.      1            return stop_at("unexpected_a", line, from, char);
    +
     1233.     92        }
    +
     1234.     92        result = make("(regexp)", char);
    +
     1235.     92        result.flag = flag;
    +
     1236.     92        result.value = value;
    +
     1237.     92        if (multi_mode && !flag.m) {
    +
     1238.      1
    +
     1239.      1// cause: "aa=/$^/"
    +
     1240.      1
    +
     1241.      1            warn_at("missing_m", line, column);
    +
     1242.     92        }
    +
     1243.     92        return result;
    +
     1244.     92    }
    +
     1245.    368
    +
     1246.   3853    function string(quote) {
    +
     1247.   3853
    +
     1248.   3853// Make a string token.
    +
     1249.   3853
    +
     1250.   3853        let the_token;
    +
     1251.   3853        snippet = "";
    +
     1252.   3853        next_char();
    +
     1253.   3853
    +
     1254.  33778        return (function next() {
    +
     1255.   3850            if (char === quote) {
    +
     1256.   3850                snip();
    +
     1257.   3850                the_token = make("(string)", snippet);
    +
     1258.   3850                the_token.quote = quote;
    +
     1259.   3850                return the_token;
    +
     1260.  29928            }
    +
     1261.  29928            if (char === "") {
    +
     1262.      1
    +
     1263.      1// cause: "\""
    +
     1264.      1
    +
     1265.      1                return stop_at("unclosed_string", line, column);
    +
     1266.  29927            }
    +
     1267.  29927            if (char === "\\") {
    +
     1268.    290                escape(quote);
    +
     1269.  29637            } else if (char === "`") {
    +
     1270.  29637                if (mega_mode) {
    +
     1271.  29637                    warn_at("unexpected_a", line, column, "`");
    +
     1272.  29637                }
    +
     1273.  29637                next_char("`");
    +
     1274.  29637            } else {
    +
     1275.  29637                next_char();
    +
     1276.  29925            }
    +
     1277.  29925            return next();
    +
     1278.  29925        }());
    +
     1279.   3853    }
    +
     1280.    368
    +
     1281.    472    function frack() {
    +
     1282.      6        if (char === ".") {
    +
     1283.      6            some_digits(rx_digits);
    +
     1284.      6        }
    +
     1285.      1        if (char === "E" || char === "e") {
    +
     1286.      1            next_char();
    +
     1287.      1            if (char !== "+" && char !== "-") {
    +
     1288.      1                back_char();
    +
     1289.      1            }
    +
     1290.      1            some_digits(rx_digits);
    +
     1291.      1        }
    +
     1292.    472    }
    +
     1293.    368
    +
     1294.   1045    function number() {
    +
     1295.    577        if (snippet === "0") {
    +
     1296.    577            next_char();
    +
     1297.    577            if (char === ".") {
    +
     1298.    577                frack();
    +
     1299.    577            } else if (char === "b") {
    +
     1300.    577                some_digits(rx_bits);
    +
     1301.    577            } else if (char === "o") {
    +
     1302.    577                some_digits(rx_octals);
    +
     1303.    577            } else if (char === "x") {
    +
     1304.    577                some_digits(rx_hexs);
    +
     1305.    577            }
    +
     1306.    577        } else {
    +
     1307.    468            next_char();
    +
     1308.    468            frack();
    +
     1309.    468        }
    +
     1310.   1045
    +
     1311.   1045// If the next character after a number is a digit or letter, then something
    +
     1312.   1045// unexpected is going on.
    +
     1313.   1045
    +
     1314.   1045        if (
    +
     1315.    469            (char >= "0" && char <= "9")
    +
     1316.     19            || (char >= "a" && char <= "z")
    +
     1317.   1044            || (char >= "A" && char <= "Z")
    +
     1318.      1        ) {
    +
     1319.      1
    +
     1320.      1// cause: "0a"
    +
     1321.      1
    +
     1322.      1            return stop_at(
    +
     1323.      1                "unexpected_a_after_b",
    +
     1324.      1                line,
    +
     1325.      1                column - 1,
    +
     1326.      1                snippet.slice(-1),
    +
     1327.      1                snippet.slice(0, -1)
    +
     1328.      1            );
    +
     1329.   1044        }
    +
     1330.   1044        back_char();
    +
     1331.   1044        return make("(number)", snippet);
    +
     1332.   1044    }
    +
     1333.    368
    +
     1334.  82412    function lex() {
    +
     1335.  82412        let array;
    +
     1336.  82412        let i = 0;
    +
     1337.  82412        let j = 0;
    +
     1338.  82412        let last;
    +
     1339.  82412        let result;
    +
     1340.  82412        let the_token;
    +
     1341.  82412
    +
     1342.  82412// This should properly be a tail recursive function, but sadly, conformant
    +
     1343.  82412// implementations of ES6 are still rare. This is the ideal code:
    +
     1344.  82412
    +
     1345.  82412//      if (!source_line) {
    +
     1346.  82412//          source_line = next_line();
    +
     1347.  82412//          from = 0;
    +
     1348.  82412//          return (
    +
     1349.  82412//              source_line === undefined
    +
     1350.  82412//              ? (
    +
     1351.  82412//                  mega_mode
    +
     1352.  82412//                  ? stop_at("unclosed_mega", mega_line, mega_from)
    +
     1353.  82412//                  : make("(end)")
    +
     1354.  82412//              )
    +
     1355.  82412//              : lex()
    +
     1356.  82412//          );
    +
     1357.  82412//      }
    +
     1358.  82412
    +
     1359.  82412// Unfortunately, incompetent JavaScript engines will sometimes fail to execute
    +
     1360.  82412// it correctly. So for now, we do it the old fashioned way.
    +
     1361.  82412
    +
     1362.  16093        while (!source_line) {
    +
     1363.  16093            source_line = next_line();
    +
     1364.  16093            from = 0;
    +
     1365.  16093            if (source_line === undefined) {
    +
     1366.  16093                return (
    +
     1367.  16093                    mega_mode
    +
     1368.  16093                    ? stop_at("unclosed_mega", mega_line, mega_from)
    +
     1369.  16093                    : make("(end)")
    +
     1370.  16093                );
    +
     1371.  16093            }
    +
     1372.  82065        }
    +
     1373.  82065
    +
     1374.  82065        from = column;
    +
     1375.  82065        result = source_line.match(rx_token);
    +
     1376.  82065
    +
     1377.  82065// result[1] token
    +
     1378.  82065// result[2] whitespace
    +
     1379.  82065// result[3] identifier
    +
     1380.  82065// result[4] number
    +
     1381.  82065// result[5] rest
    +
     1382.  82065
    +
     1383.  82065        if (!result) {
    +
     1384.      1
    +
     1385.      1// cause: "#"
    +
     1386.      1
    +
     1387.      1            return stop_at(
    +
     1388.      1                "unexpected_char_a",
    +
     1389.      1                line,
    +
     1390.      1                column,
    +
     1391.      1                source_line[0]
    +
     1392.      1            );
    +
     1393.  82064        }
    +
     1394.  82064
    +
     1395.  82064        snippet = result[1];
    +
     1396.  82064        column += snippet.length;
    +
     1397.  82064        source_line = result[5];
    +
     1398.  82064
    +
     1399.  82064// Whitespace was matched. Call lex again to get more.
    +
     1400.  82064
    +
     1401.  82064        if (result[2]) {
    +
     1402.  25595            return lex();
    +
     1403.  56469        }
    +
     1404.  56469
    +
     1405.  56469// The token is an identifier.
    +
     1406.  56469
    +
     1407.  56469        if (result[3]) {
    +
     1408.  18464            return make(snippet, undefined, true);
    +
     1409.  38005        }
    +
     1410.  38005
    +
     1411.  38005// The token is a number.
    +
     1412.  38005
    +
     1413.  38005        if (result[4]) {
    +
     1414.   1045            return number(snippet);
    +
     1415.  36960        }
    +
     1416.  36960
    +
     1417.  36960// The token is a string.
    +
     1418.  36960
    +
     1419.  36960        if (snippet === "\"") {
    +
     1420.   3851            return string(snippet);
    +
     1421.  33109        }
    +
     1422.  33109        if (snippet === "'") {
    +
     1423.      2            if (!option.single) {
    +
     1424.      2
    +
     1425.      2// cause: "''"
    +
     1426.      2
    +
     1427.      2                warn_at("use_double", line, column);
    +
     1428.      2            }
    +
     1429.      2            return string(snippet);
    +
     1430.  33107        }
    +
     1431.  33107
    +
     1432.  33107// The token is a megastring. We don't allow any kind of mega nesting.
    +
     1433.  33107
    +
     1434.  33107        if (snippet === "`") {
    +
     1435.     63            if (mega_mode) {
    +
     1436.     63                return stop_at("expected_a_b", line, column, "}", "`");
    +
     1437.     63            }
    +
     1438.     63            snippet = "";
    +
     1439.     63            mega_from = from;
    +
     1440.     63            mega_line = line;
    +
     1441.     63            mega_mode = true;
    +
     1442.     63
    +
     1443.     63// Parsing a mega literal is tricky. First make a ` token.
    +
     1444.     63
    +
     1445.     63            make("`");
    +
     1446.     63            from += 1;
    +
     1447.     63
    +
     1448.     63// Then loop, building up a string, possibly from many lines, until seeing
    +
     1449.     63// the end of file, a closing `, or a ${ indicting an expression within the
    +
     1450.     63// string.
    +
     1451.     63
    +
     1452.    485            (function part() {
    +
     1453.    485                const at = source_line.search(rx_mega);
    +
     1454.    485
    +
     1455.    485// If neither ` nor ${ is seen, then the whole line joins the snippet.
    +
     1456.    485
    +
     1457.    312                if (at < 0) {
    +
     1458.    312                    snippet += source_line + "\n";
    +
     1459.    312                    return (
    +
     1460.    312                        next_line() === undefined
    +
     1461.    312
    +
     1462.    312// cause: "`"
    +
     1463.    312
    +
     1464.    312                        ? stop_at("unclosed_mega", mega_line, mega_from)
    +
     1465.    312                        : part()
    +
     1466.    312                    );
    +
     1467.    312                }
    +
     1468.    173                snippet += source_line.slice(0, at);
    +
     1469.    173                column += at;
    +
     1470.    173                source_line = source_line.slice(at);
    +
     1471.    173                if (source_line[0] === "\\") {
    +
     1472.     76                    snippet += source_line.slice(0, 2);
    +
     1473.     76                    source_line = source_line.slice(2);
    +
     1474.     76                    column += 2;
    +
     1475.     76                    return part();
    +
     1476.     97                }
    +
     1477.     97
    +
     1478.     97// if either ` or ${ was found, then the preceding joins the snippet to become
    +
     1479.     97// a string token.
    +
     1480.     97
    +
     1481.     97                make("(string)", snippet).quote = "`";
    +
     1482.     97                snippet = "";
    +
     1483.     97
    +
     1484.     97// If ${, then make tokens that will become part of an expression until
    +
     1485.     97// a } token is made.
    +
     1486.     97
    +
     1487.     97                if (source_line[0] === "$") {
    +
     1488.     63                    column += 2;
    +
     1489.     63                    make("${");
    +
     1490.     63                    source_line = source_line.slice(2);
    +
     1491.    156                    (function expr() {
    +
     1492.    156                        const id = lex().id;
    +
     1493.     63                        if (id === "{") {
    +
     1494.     63                            return stop_at(
    +
     1495.     63                                "expected_a_b",
    +
     1496.     63                                line,
    +
     1497.     63                                column,
    +
     1498.     63                                "}",
    +
     1499.     63                                "{"
    +
     1500.     63                            );
    +
     1501.    153                        }
    +
     1502.    153                        if (id !== "}") {
    +
     1503.    117                            return expr();
    +
     1504.    117                        }
    +
     1505.    156                    }());
    +
     1506.     63                    return part();
    +
     1507.     63                }
    +
     1508.    485            }());
    +
     1509.     63            source_line = source_line.slice(1);
    +
     1510.     63            column += 1;
    +
     1511.     63            mega_mode = false;
    +
     1512.     63            return make("`");
    +
     1513.  33044        }
    +
     1514.  33044
    +
     1515.  33044// The token is a // comment.
    +
     1516.  33044
    +
     1517.  33044        if (snippet === "//") {
    +
     1518.   1463            snippet = source_line;
    +
     1519.   1463            source_line = "";
    +
     1520.   1463            the_token = comment(snippet);
    +
     1521.   1463            if (mega_mode) {
    +
     1522.   1463
    +
     1523.   1463// cause: "`${//}`"
    +
     1524.   1463
    +
     1525.   1463                warn("unexpected_comment", the_token, "`");
    +
     1526.   1463            }
    +
     1527.   1463            return the_token;
    +
     1528.  31581        }
    +
     1529.  31581
    +
     1530.  31581// The token is a /* comment.
    +
     1531.  31581
    +
     1532.  31581        if (snippet === "/*") {
    +
     1533.     50            array = [];
    +
     1534.     50            if (source_line[0] === "/") {
    +
     1535.     50                warn_at("unexpected_a", line, column + i, "/");
    +
     1536.     50            }
    +
     1537.    174            (function next() {
    +
     1538.    159                if (source_line > "") {
    +
     1539.    159                    i = source_line.search(rx_star_slash);
    +
     1540.    159                    if (i >= 0) {
    +
     1541.    159                        return;
    +
     1542.    159                    }
    +
     1543.    159                    j = source_line.search(rx_slash_star);
    +
     1544.    159                    if (j >= 0) {
    +
     1545.    159
    +
     1546.    159// cause: "/*/*"
    +
     1547.    159
    +
     1548.    159                        warn_at("nested_comment", line, column + j);
    +
     1549.    159                    }
    +
     1550.    159                }
    +
     1551.    127                array.push(source_line);
    +
     1552.    127                source_line = next_line();
    +
     1553.    127                if (source_line === undefined) {
    +
     1554.     50
    +
     1555.     50// cause: "/*"
    +
     1556.     50
    +
     1557.     50                    return stop_at("unclosed_comment", line, column);
    +
     1558.    124                }
    +
     1559.    124                return next();
    +
     1560.    124            }());
    +
     1561.     50            snippet = source_line.slice(0, i);
    +
     1562.     50            j = snippet.search(rx_slash_star_or_slash);
    +
     1563.     50            if (j >= 0) {
    +
     1564.     50
    +
     1565.     50// cause: "/*/**/"
    +
     1566.     50
    +
     1567.     50                warn_at("nested_comment", line, column + j);
    +
     1568.     50            }
    +
     1569.     50            array.push(snippet);
    +
     1570.     50            column += i + 2;
    +
     1571.     50            source_line = source_line.slice(i + 2);
    +
     1572.     50            return comment(array);
    +
     1573.  31531        }
    +
     1574.  31531
    +
     1575.  31531// The token is a slash.
    +
     1576.  31531
    +
     1577.  31531        if (snippet === "/") {
    +
     1578.    107
    +
     1579.    107// The / can be a division operator or the beginning of a regular expression
    +
     1580.    107// literal. It is not possible to know which without doing a complete parse.
    +
     1581.    107// We want to complete the tokenization before we begin to parse, so we will
    +
     1582.    107// estimate. This estimator can fail in some cases. For example, it cannot
    +
     1583.    107// know if "}" is ending a block or ending an object literal, so it can
    +
     1584.    107// behave incorrectly in that case; it is not meaningful to divide an
    +
     1585.    107// object, so it is likely that we can get away with it. We avoided the worst
    +
     1586.    107// cases by eliminating automatic semicolon insertion.
    +
     1587.    107
    +
     1588.    107            if (prior.identifier) {
    +
     1589.    107                if (!prior.dot) {
    +
     1590.    107                    if (prior.id === "return") {
    +
     1591.    107                        return regexp();
    +
     1592.    107                    }
    +
     1593.    107                    if (
    +
     1594.    107                        prior.id === "(begin)"
    +
     1595.    107                        || prior.id === "case"
    +
     1596.    107                        || prior.id === "delete"
    +
     1597.    107                        || prior.id === "in"
    +
     1598.    107                        || prior.id === "instanceof"
    +
     1599.    107                        || prior.id === "new"
    +
     1600.    107                        || prior.id === "typeof"
    +
     1601.    107                        || prior.id === "void"
    +
     1602.    107                        || prior.id === "yield"
    +
     1603.    107                    ) {
    +
     1604.    107                        the_token = regexp();
    +
     1605.    107
    +
     1606.    107// cause: "/./"
    +
     1607.    107// cause: "case /./"
    +
     1608.    107// cause: "delete /./"
    +
     1609.    107// cause: "in /./"
    +
     1610.    107// cause: "instanceof /./"
    +
     1611.    107// cause: "new /./"
    +
     1612.    107// cause: "typeof /./"
    +
     1613.    107// cause: "void /./"
    +
     1614.    107// cause: "yield /./"
    +
     1615.    107
    +
     1616.    107                        return stop("unexpected_a", the_token);
    +
     1617.    107                    }
    +
     1618.    107                }
    +
     1619.    107            } else {
    +
     1620.    107                last = prior.id[prior.id.length - 1];
    +
     1621.    107                if ("(,=:?[".indexOf(last) >= 0) {
    +
     1622.    107                    return regexp();
    +
     1623.    107                }
    +
     1624.    107                if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) {
    +
     1625.    107                    the_token = regexp();
    +
     1626.    107
    +
     1627.    107// cause: "!/./"
    +
     1628.    107
    +
     1629.    107                    warn("wrap_regexp", the_token);
    +
     1630.    107                    return the_token;
    +
     1631.    107                }
    +
     1632.    107            }
    +
     1633.    107            if (source_line[0] === "=") {
    +
     1634.    107                column += 1;
    +
     1635.    107                source_line = source_line.slice(1);
    +
     1636.    107                snippet = "/=";
    +
     1637.    107                warn_at("unexpected_a", line, column, "/=");
    +
     1638.    107            }
    +
     1639.  31434        }
    +
     1640.  31434        return make(snippet);
    +
     1641.  31434    }
    +
     1642.    368
    +
     1643.    368    first = lex();
    +
     1644.    340    json_mode = first.id === "{" || first.id === "[";
    +
     1645.    368
    +
     1646.    368// This loop will be replaced with a recursive call to lex when ES6 has been
    +
     1647.    368// finished and widely deployed and adopted.
    +
     1648.    368
    +
     1649.  56293    while (true) {
    +
     1650.  56293        if (lex().id === "(end)") {
    +
     1651.  56293            break;
    +
     1652.  56293        }
    +
     1653.  56293    }
    +
     1654.    368}
    +
     1655.      1
    +
     1656.      1// Parsing:
    +
     1657.      1
    +
     1658.      1// Parsing weaves the tokens into an abstract syntax tree. During that process,
    +
     1659.      1// a token may be given any of these properties:
    +
     1660.      1
    +
     1661.      1//      arity       string
    +
     1662.      1//      label       identifier
    +
     1663.      1//      name        identifier
    +
     1664.      1//      expression  expressions
    +
     1665.      1//      block       statements
    +
     1666.      1//      else        statements (else, default, catch)
    +
     1667.      1
    +
     1668.      1// Specialized tokens may have additional properties.
    +
     1669.      1
    +
     1670.   3892function survey(name) {
    +
     1671.   3892    let id = name.id;
    +
     1672.   3892
    +
     1673.   3892// Tally the property name. If it is a string, only tally strings that conform
    +
     1674.   3892// to the identifier rules.
    +
     1675.   3892
    +
     1676.     10    if (id === "(string)") {
    +
     1677.     10        id = name.value;
    +
     1678.     10        if (!rx_identifier.test(id)) {
    +
     1679.     10            return id;
    +
     1680.     10        }
    +
     1681.   3882    } else if (id === "`") {
    +
     1682.   3882        if (name.value.length === 1) {
    +
     1683.   3882            id = name.value[0].value;
    +
     1684.   3882            if (!rx_identifier.test(id)) {
    +
     1685.   3882                return id;
    +
     1686.   3882            }
    +
     1687.   3882        }
    +
     1688.   3882    } else if (!name.identifier) {
    +
     1689.   3882        return stop("expected_identifier_a", name);
    +
     1690.   3882    }
    +
     1691.   3880
    +
     1692.   3880// If we have seen this name before, increment its count.
    +
     1693.   3880
    +
     1694.   3880    if (typeof property[id] === "number") {
    +
     1695.   3082        property[id] += 1;
    +
     1696.   3082
    +
     1697.   3082// If this is the first time seeing this property name, and if there is a
    +
     1698.   3082// tenure list, then it must be on the list. Otherwise, it must conform to
    +
     1699.   3082// the rules for good property names.
    +
     1700.   3082
    +
     1701.   3082    } else {
    +
     1702.    798        if (tenure !== undefined) {
    +
     1703.    798            if (tenure[id] !== true) {
    +
     1704.    798
    +
     1705.    798// cause: "/*property aa*/\naa.bb"
    +
     1706.    798
    +
     1707.    798                warn("unregistered_property_a", name);
    +
     1708.    798            }
    +
     1709.    798        } else {
    +
     1710.    798            if (name.identifier && rx_bad_property.test(id)) {
    +
     1711.    798
    +
     1712.    798// cause: "aa._"
    +
     1713.    798
    +
     1714.    798                warn("bad_property_a", name);
    +
     1715.    798            }
    +
     1716.    798        }
    +
     1717.    798        property[id] = 1;
    +
     1718.   3880    }
    +
     1719.   3880    return id;
    +
     1720.   3880}
    +
     1721.      1
    +
     1722.  57514function dispense() {
    +
     1723.  57514
    +
     1724.  57514// Deliver the next token, skipping the comments.
    +
     1725.  57514
    +
     1726.  57514    const cadet = tokens[token_nr];
    +
     1727.  57514    token_nr += 1;
    +
     1728.   1508    if (cadet.id === "(comment)") {
    +
     1729.   1508        if (json_mode) {
    +
     1730.   1508            warn("unexpected_a", cadet);
    +
     1731.   1508        }
    +
     1732.   1508        return dispense();
    +
     1733.  56006    } else {
    +
     1734.  56006        return cadet;
    +
     1735.  56006    }
    +
     1736.  57514}
    +
     1737.      1
    +
     1738.    392function lookahead() {
    +
     1739.    392
    +
     1740.    392// Look ahead one token without advancing.
    +
     1741.    392
    +
     1742.    392    const old_token_nr = token_nr;
    +
     1743.    392    const cadet = dispense(true);
    +
     1744.    392    token_nr = old_token_nr;
    +
     1745.    392    return cadet;
    +
     1746.    392}
    +
     1747.      1
    +
     1748.  55630function advance(id, match) {
    +
     1749.  55630
    +
     1750.  55630// Produce the next token.
    +
     1751.  55630
    +
     1752.  55630// Attempt to give helpful names to anonymous functions.
    +
     1753.  55630
    +
     1754.  18440    if (token.identifier && token.id !== "function") {
    +
     1755.  17853        anon = token.id;
    +
     1756.  37777    } else if (token.id === "(string)" && rx_identifier.test(token.value)) {
    +
     1757.  37777        anon = token.value;
    +
     1758.  37777    }
    +
     1759.  55630
    +
     1760.  55630// Attempt to match next_token with an expected id.
    +
     1761.  55630
    +
     1762.  20759    if (id !== undefined && next_token.id !== id) {
    +
     1763.     16        return (
    +
     1764.     16            match === undefined
    +
     1765.     16
    +
     1766.     16// cause: "()"
    +
     1767.     16
    +
     1768.     16            ? stop("expected_a_b", next_token, id, artifact())
    +
     1769.     16
    +
     1770.     16// cause: "{\"aa\":0"
    +
     1771.     16
    +
     1772.     16            : stop(
    +
     1773.     16                "expected_a_b_from_c_d",
    +
     1774.     16                next_token,
    +
     1775.     16                id,
    +
     1776.     16                artifact(match),
    +
     1777.     16
    +
     1778.     16// Return the fudged line number of an artifact.
    +
     1779.     16
    +
     1780.     16                match.line + fudge,
    +
     1781.     16                artifact(next_token)
    +
     1782.     16            )
    +
     1783.     16        );
    +
     1784.  55614    }
    +
     1785.  55614
    +
     1786.  55614// Promote the tokens, skipping comments.
    +
     1787.  55614
    +
     1788.  55614    token = next_token;
    +
     1789.  55614    next_token = dispense();
    +
     1790.  55614    if (next_token.id === "(end)") {
    +
     1791.    587        token_nr -= 1;
    +
     1792.    587    }
    +
     1793.  55630}
    +
     1794.      1
    +
     1795.      1// Parsing of JSON is simple:
    +
     1796.      1
    +
     1797.     41function json_value() {
    +
     1798.     41    let negative;
    +
     1799.     15    if (next_token.id === "{") {
    +
     1800.     15        return (function json_object() {
    +
     1801.     15            const brace = next_token;
    +
     1802.     15            const object = empty();
    +
     1803.     15            const properties = [];
    +
     1804.     15            brace.expression = properties;
    +
     1805.     15            advance("{");
    +
     1806.     15            if (next_token.id !== "}") {
    +
     1807.     15                (function next() {
    +
     1808.     15                    let name;
    +
     1809.     15                    let value;
    +
     1810.     15                    if (next_token.quote !== "\"") {
    +
     1811.     15                        warn(
    +
     1812.     15                            "unexpected_a",
    +
     1813.     15                            next_token,
    +
     1814.     15                            next_token.quote
    +
     1815.     15                        );
    +
     1816.     15                    }
    +
     1817.     15                    name = next_token;
    +
     1818.     15                    advance("(string)");
    +
     1819.     15                    if (object[token.value] !== undefined) {
    +
     1820.     15
    +
     1821.     15// cause: "{\"aa\":0,\"aa\":0}"
    +
     1822.     15
    +
     1823.     15                        warn("duplicate_a", token);
    +
     1824.     15                    } else if (token.value === "__proto__") {
    +
     1825.     15
    +
     1826.     15// cause: "{\"__proto__\":0}"
    +
     1827.     15
    +
     1828.     15                        warn("bad_property_a", token);
    +
     1829.     15                    } else {
    +
     1830.     15                        object[token.value] = token;
    +
     1831.     15                    }
    +
     1832.     15                    advance(":");
    +
     1833.     15                    value = json_value();
    +
     1834.     15                    value.label = name;
    +
     1835.     15                    properties.push(value);
    +
     1836.     15                    if (next_token.id === ",") {
    +
     1837.     15                        advance(",");
    +
     1838.     15                        return next();
    +
     1839.     15                    }
    +
     1840.     15                }());
    +
     1841.     15            }
    +
     1842.     15            advance("}", brace);
    +
     1843.     15            return brace;
    +
     1844.     15        }());
    +
     1845.     26    }
    +
     1846.     26    if (next_token.id === "[") {
    +
     1847.      9        return (function json_array() {
    +
     1848.      9            const bracket = next_token;
    +
     1849.      9            const elements = [];
    +
     1850.      9            bracket.expression = elements;
    +
     1851.      9            advance("[");
    +
     1852.      9            if (next_token.id !== "]") {
    +
     1853.      9                (function next() {
    +
     1854.      9                    elements.push(json_value());
    +
     1855.      9                    if (next_token.id === ",") {
    +
     1856.      9                        advance(",");
    +
     1857.      9                        return next();
    +
     1858.      9                    }
    +
     1859.      9                }());
    +
     1860.      9            }
    +
     1861.      9            advance("]", bracket);
    +
     1862.      9            return bracket;
    +
     1863.      9        }());
    +
     1864.     17    }
    +
     1865.     17    if (
    +
     1866.     17        next_token.id === "true"
    +
     1867.     17        || next_token.id === "false"
    +
     1868.     17        || next_token.id === "null"
    +
     1869.      1    ) {
    +
     1870.      1        advance();
    +
     1871.      1        return token;
    +
     1872.     16    }
    +
     1873.     16    if (next_token.id === "(number)") {
    +
     1874.      9        if (!rx_JSON_number.test(next_token.value)) {
    +
     1875.      9            warn("unexpected_a");
    +
     1876.      9        }
    +
     1877.      9        advance();
    +
     1878.      9        return token;
    +
     1879.      9    }
    +
     1880.      7    if (next_token.id === "(string)") {
    +
     1881.      3        if (next_token.quote !== "\"") {
    +
     1882.      3            warn("unexpected_a", next_token, next_token.quote);
    +
     1883.      3        }
    +
     1884.      3        advance();
    +
     1885.      3        return token;
    +
     1886.      4    }
    +
     1887.      4    if (next_token.id === "-") {
    +
     1888.      2        negative = next_token;
    +
     1889.      2        negative.arity = "unary";
    +
     1890.      2        advance("-");
    +
     1891.      2        advance("(number)");
    +
     1892.      2        if (!rx_JSON_number.test(token.value)) {
    +
     1893.      2            warn("unexpected_a", token);
    +
     1894.      2        }
    +
     1895.      2        negative.expression = token;
    +
     1896.      2        return negative;
    +
     1897.      2    }
    +
     1898.      2    stop("unexpected_a");
    +
     1899.      2}
    +
     1900.      1
    +
     1901.      1// Now we parse JavaScript.
    +
     1902.      1
    +
     1903.   1577function enroll(name, role, readonly) {
    +
     1904.   1577
    +
     1905.   1577// Enroll a name into the current function context. The role can be exception,
    +
     1906.   1577// function, label, parameter, or variable. We look for variable redefinition
    +
     1907.   1577// because it causes confusion.
    +
     1908.   1577
    +
     1909.   1577    const id = name.id;
    +
     1910.   1577
    +
     1911.   1577// Reserved words may not be enrolled.
    +
     1912.   1577
    +
     1913.     22    if (syntax[id] !== undefined && id !== "ignore") {
    +
     1914.      1
    +
     1915.      1// cause: "let undefined"
    +
     1916.      1
    +
     1917.      1        warn("reserved_a", name);
    +
     1918.   1576    } else {
    +
     1919.   1576
    +
     1920.   1576// Has the name been enrolled in this context?
    +
     1921.   1576
    +
     1922.   1576        let earlier = functionage.context[id];
    +
     1923.   1576        if (earlier) {
    +
     1924.   1576
    +
     1925.   1576// cause: "let aa;let aa"
    +
     1926.   1576
    +
     1927.   1576            warn(
    +
     1928.   1576                "redefinition_a_b",
    +
     1929.   1576                name,
    +
     1930.   1576                name.id,
    +
     1931.   1576                earlier.line + fudge
    +
     1932.   1576            );
    +
     1933.   1576
    +
     1934.   1576// Has the name been enrolled in an outer context?
    +
     1935.   1576
    +
     1936.   1576        } else {
    +
     1937.   1858            stack.forEach(function (value) {
    +
     1938.   1858                const item = value.context[id];
    +
     1939.   1576                if (item !== undefined) {
    +
     1940.   1576                    earlier = item;
    +
     1941.   1576                }
    +
     1942.   1858            });
    +
     1943.   1576            if (earlier) {
    +
     1944.   1576                if (id === "ignore") {
    +
     1945.   1576                    if (earlier.role === "variable") {
    +
     1946.   1576
    +
     1947.   1576// cause: "let ignore;function aa(ignore) {}"
    +
     1948.   1576
    +
     1949.   1576                        warn("unexpected_a", name);
    +
     1950.   1576                    }
    +
     1951.   1576                } else {
    +
     1952.   1576                    if (
    +
     1953.   1576                        (
    +
     1954.   1576                            role !== "exception"
    +
     1955.   1576                            || earlier.role !== "exception"
    +
     1956.   1576                        )
    +
     1957.   1576                        && role !== "parameter"
    +
     1958.   1576                        && role !== "function"
    +
     1959.   1576                    ) {
    +
     1960.   1576
    +
     1961.   1576// cause: "function aa(){var aa;}"
    +
     1962.   1576// cause: "function aa(){try{aa();}catch(aa){aa();}}"
    +
     1963.   1576
    +
     1964.   1576                        warn(
    +
     1965.   1576                            "redefinition_a_b",
    +
     1966.   1576                            name,
    +
     1967.   1576                            name.id,
    +
     1968.   1576                            earlier.line + fudge
    +
     1969.   1576                        );
    +
     1970.   1576                    }
    +
     1971.   1576                }
    +
     1972.   1576            }
    +
     1973.   1576
    +
     1974.   1576// Enroll it.
    +
     1975.   1576
    +
     1976.   1576            functionage.context[id] = name;
    +
     1977.   1576            name.dead = true;
    +
     1978.   1576            name.parent = functionage;
    +
     1979.   1576            name.init = false;
    +
     1980.   1576            name.role = role;
    +
     1981.   1576            name.used = 0;
    +
     1982.   1576            name.writable = !readonly;
    +
     1983.   1576        }
    +
     1984.   1576    }
    +
     1985.   1577}
    +
     1986.      1
    +
     1987.  15648function expression(rbp, initial) {
    +
     1988.  15648
    +
     1989.  15648// This is the heart of the Pratt parser. I retained Pratt's nomenclature.
    +
     1990.  15648// They are elements of the parsing method called Top Down Operator Precedence.
    +
     1991.  15648
    +
     1992.  15648// nud     Null denotation
    +
     1993.  15648// led     Left denotation
    +
     1994.  15648// lbp     Left binding power
    +
     1995.  15648// rbp     Right binding power
    +
     1996.  15648
    +
     1997.  15648// It processes a nud (variable, constant, prefix operator). It will then
    +
     1998.  15648// process leds (infix operators) until the bind powers cause it to stop. It
    +
     1999.  15648// returns the expression's parse tree.
    +
     2000.  15648
    +
     2001.  15648    let left;
    +
     2002.  15648    let the_symbol;
    +
     2003.  15648
    +
     2004.  15648// Statements will have already advanced, so advance now only if the token is
    +
     2005.  15648// not the first of a statement,
    +
     2006.  15648
    +
     2007.  12433    if (!initial) {
    +
     2008.  12433        advance();
    +
     2009.  12433    }
    +
     2010.  15648    the_symbol = syntax[token.id];
    +
     2011.   6921    if (the_symbol !== undefined && the_symbol.nud !== undefined) {
    +
     2012.   6908        left = the_symbol.nud();
    +
     2013.   8740    } else if (token.identifier) {
    +
     2014.   8740        left = token;
    +
     2015.   8740        left.arity = "variable";
    +
     2016.   8740    } else {
    +
     2017.   8740
    +
     2018.   8740// cause: "!"
    +
     2019.   8740
    +
     2020.   8740        return stop("unexpected_a", token);
    +
     2021.  15622    }
    +
     2022.  26258    (function right() {
    +
     2023.  26258        the_symbol = syntax[next_token.id];
    +
     2024.  26258        if (
    +
     2025.  26258            the_symbol !== undefined
    +
     2026.  26037            && the_symbol.led !== undefined
    +
     2027.  15622            && rbp < the_symbol.lbp
    +
     2028.  15622        ) {
    +
     2029.  15622            advance();
    +
     2030.  15622            left = the_symbol.led(left);
    +
     2031.  15622            return right();
    +
     2032.  15622        }
    +
     2033.  26258    }());
    +
     2034.  15622    return left;
    +
     2035.  15622}
    +
     2036.      1
    +
     2037.   1222function condition() {
    +
     2038.   1222
    +
     2039.   1222// Parse the condition part of a do, if, while.
    +
     2040.   1222
    +
     2041.   1222    const the_paren = next_token;
    +
     2042.   1222    let the_value;
    +
     2043.   1222
    +
     2044.   1222// cause: "do{}while()"
    +
     2045.   1222// cause: "if(){}"
    +
     2046.   1222// cause: "while(){}"
    +
     2047.   1222
    +
     2048.   1222    the_paren.free = true;
    +
     2049.   1222    advance("(");
    +
     2050.   1222    the_value = expression(0);
    +
     2051.   1222    advance(")");
    +
     2052.      1    if (the_value.wrapped === true) {
    +
     2053.      1        warn("unexpected_a", the_paren);
    +
     2054.   1219    }
    +
     2055.   1219    if (anticondition[the_value.id] === true) {
    +
     2056.     13        warn("unexpected_a", the_value);
    +
     2057.   1219    }
    +
     2058.   1219    return the_value;
    +
     2059.   1219}
    +
     2060.      1
    +
     2061.  10461function is_weird(thing) {
    +
     2062.  10461    return (
    +
     2063.  10461        thing.id === "(regexp)"
    +
     2064.  10461        || thing.id === "{"
    +
     2065.  10461        || thing.id === "=>"
    +
     2066.  10461        || thing.id === "function"
    +
     2067.  10457        || (thing.id === "[" && thing.arity === "unary")
    +
     2068.  10461    );
    +
     2069.  10461}
    +
     2070.      1
    +
     2071.   3296function are_similar(a, b) {
    +
     2072.   3296
    +
     2073.   3296// cause: "0&&0"
    +
     2074.   3296
    +
     2075.   3296    assert_or_throw(a !== b, "Expected a !== b.");
    +
     2076.   3296//  if (a === b) {
    +
     2077.   3296//      return true;
    +
     2078.   3296//  }
    +
     2079.      8    if (Array.isArray(a)) {
    +
     2080.      8        return (
    +
     2081.      8            Array.isArray(b)
    +
     2082.      8            && a.length === b.length
    +
     2083.      8            && a.every(function (value, index) {
    +
     2084.      8
    +
     2085.      8// cause: "`${0}`&&`${0}`"
    +
     2086.      8
    +
     2087.      8                return are_similar(value, b[index]);
    +
     2088.      8            })
    +
     2089.      8        );
    +
     2090.   3288    }
    +
     2091.   3288    assert_or_throw(!Array.isArray(b), "Expected !Array.isArray(b).");
    +
     2092.   3288//  if (Array.isArray(b)) {
    +
     2093.   3288//      return false;
    +
     2094.   3288//  }
    +
     2095.   3288    if (a.id === "(number)" && b.id === "(number)") {
    +
     2096.     31        return a.value === b.value;
    +
     2097.   3257    }
    +
     2098.   3257    let a_string;
    +
     2099.   3257    let b_string;
    +
     2100.   3257    if (a.id === "(string)") {
    +
     2101.    192        a_string = a.value;
    +
     2102.   3065    } else if (a.id === "`" && a.constant) {
    +
     2103.   3065        a_string = a.value[0];
    +
     2104.   3257    }
    +
     2105.   3257    if (b.id === "(string)") {
    +
     2106.   1312        b_string = b.value;
    +
     2107.   1945    } else if (b.id === "`" && b.constant) {
    +
     2108.   1945        b_string = b.value[0];
    +
     2109.   3257    }
    +
     2110.   3257    if (typeof a_string === "string") {
    +
     2111.    192        return a_string === b_string;
    +
     2112.   3065    }
    +
     2113.   3065    if (is_weird(a) || is_weird(b)) {
    +
     2114.      4        return false;
    +
     2115.   3061    }
    +
     2116.   3061    if (a.arity === b.arity && a.id === b.id) {
    +
     2117.    818
    +
     2118.    818// cause: "aa.bb&&aa.bb"
    +
     2119.    818
    +
     2120.    818        if (a.id === ".") {
    +
     2121.    818            return (
    +
     2122.    818                are_similar(a.expression, b.expression)
    +
     2123.    818                && are_similar(a.name, b.name)
    +
     2124.    818            );
    +
     2125.    818        }
    +
     2126.    818
    +
     2127.    818// cause: "+0&&+0"
    +
     2128.    818
    +
     2129.    818        if (a.arity === "unary") {
    +
     2130.    818            return are_similar(a.expression, b.expression);
    +
     2131.    818        }
    +
     2132.    818        if (a.arity === "binary") {
    +
     2133.    818
    +
     2134.    818// cause: "aa[0]&&aa[0]"
    +
     2135.    818
    +
     2136.    818            return (
    +
     2137.    818                a.id !== "("
    +
     2138.    818                && are_similar(a.expression[0], b.expression[0])
    +
     2139.    818                && are_similar(a.expression[1], b.expression[1])
    +
     2140.    818            );
    +
     2141.    818        }
    +
     2142.    818        if (a.arity === "ternary") {
    +
     2143.    818
    +
     2144.    818// cause: "aa=(``?``:``)&&(``?``:``)"
    +
     2145.    818
    +
     2146.    818            return (
    +
     2147.    818                are_similar(a.expression[0], b.expression[0])
    +
     2148.    818                && are_similar(a.expression[1], b.expression[1])
    +
     2149.    818                && are_similar(a.expression[2], b.expression[2])
    +
     2150.    818            );
    +
     2151.    818        }
    +
     2152.    818        assert_or_throw(
    +
     2153.    818            a.arity !== "function" && a.arity !== "regexp",
    +
     2154.    818            "Expected a.arity !== \"function\" && a.arity !== \"regexp\"."
    +
     2155.    818        );
    +
     2156.    818//      if (a.arity === "function" && a.arity === "regexp") {
    +
     2157.    818//          return false;
    +
     2158.    818//      }
    +
     2159.    818
    +
     2160.    818// cause: "undefined&&undefined"
    +
     2161.    818
    +
     2162.    818        return true;
    +
     2163.   2243    }
    +
     2164.   2243
    +
     2165.   2243// cause: "null&&undefined"
    +
     2166.   2243
    +
     2167.   2243    return false;
    +
     2168.   2243}
    +
     2169.      1
    +
     2170.   3895function semicolon() {
    +
     2171.   3895
    +
     2172.   3895// Try to match a semicolon.
    +
     2173.   3895
    +
     2174.   3738    if (next_token.id === ";") {
    +
     2175.   3738        advance(";");
    +
     2176.   3738    } else {
    +
     2177.    157
    +
     2178.    157// cause: "0"
    +
     2179.    157// cause: "0\n0"
    +
     2180.    157
    +
     2181.    157        warn_at(
    +
     2182.    157            "expected_a_b",
    +
     2183.    157            token.line,
    +
     2184.    157            token.thru,
    +
     2185.    157            ";",
    +
     2186.    157            artifact(next_token)
    +
     2187.    157        );
    +
     2188.    157    }
    +
     2189.   3895    anon = "anonymous";
    +
     2190.   3895}
    +
     2191.      1
    +
     2192.   6014function statement() {
    +
     2193.   6014
    +
     2194.   6014// Parse a statement. Any statement may have a label, but only four statements
    +
     2195.   6014// have use for one. A statement can be one of the standard statements, or
    +
     2196.   6014// an assignment expression, or an invocation expression.
    +
     2197.   6014
    +
     2198.   6014    let first;
    +
     2199.   6014    let the_label;
    +
     2200.   6014    let the_statement;
    +
     2201.   6014    let the_symbol;
    +
     2202.   6014    advance();
    +
     2203.   5869    if (token.identifier && next_token.id === ":") {
    +
     2204.     11        the_label = token;
    +
     2205.     11        if (the_label.id === "ignore") {
    +
     2206.     11            warn("unexpected_a", the_label);
    +
     2207.     11        }
    +
     2208.     11        advance(":");
    +
     2209.     11        if (
    +
     2210.     11            next_token.id === "do"
    +
     2211.     11            || next_token.id === "for"
    +
     2212.     11            || next_token.id === "switch"
    +
     2213.     11            || next_token.id === "while"
    +
     2214.     11        ) {
    +
     2215.     11            enroll(the_label, "label", true);
    +
     2216.     11            the_label.init = true;
    +
     2217.     11            the_label.dead = false;
    +
     2218.     11            the_statement = statement();
    +
     2219.     11            the_statement.label = the_label;
    +
     2220.     11            the_statement.statement = true;
    +
     2221.     11            return the_statement;
    +
     2222.     11        }
    +
     2223.     11        advance();
    +
     2224.     11
    +
     2225.     11// cause: "aa:"
    +
     2226.     11
    +
     2227.     11        warn("unexpected_label_a", the_label);
    +
     2228.   6007    }
    +
     2229.   6007
    +
     2230.   6007// Parse the statement.
    +
     2231.   6007
    +
     2232.   6007    first = token;
    +
     2233.   6007    first.statement = true;
    +
     2234.   6007    the_symbol = syntax[first.id];
    +
     2235.   6007    if (the_symbol !== undefined && the_symbol.fud !== undefined) {
    +
     2236.   2877        the_symbol.disrupt = false;
    +
     2237.   2877        the_symbol.statement = true;
    +
     2238.   2877        the_statement = the_symbol.fud();
    +
     2239.   3130    } else {
    +
     2240.   3130
    +
     2241.   3130// It is an expression statement.
    +
     2242.   3130
    +
     2243.   3130        the_statement = expression(0, true);
    +
     2244.   3130        if (the_statement.wrapped && the_statement.id !== "(") {
    +
     2245.   3130
    +
     2246.   3130// cause: "(0)"
    +
     2247.   3130
    +
     2248.   3130            warn("unexpected_a", first);
    +
     2249.   3130        }
    +
     2250.   3130        semicolon();
    +
     2251.   5950    }
    +
     2252.   5950    if (the_label !== undefined) {
    +
     2253.      1        the_label.dead = true;
    +
     2254.   5950    }
    +
     2255.   5950    return the_statement;
    +
     2256.   5950}
    +
     2257.      1
    +
     2258.   2349function statements() {
    +
     2259.   2349
    +
     2260.   2349// Parse a list of statements. Give a warning if an unreachable statement
    +
     2261.   2349// follows a disruptive statement.
    +
     2262.   2349
    +
     2263.   2349    const array = [];
    +
     2264.   8158    (function next(disrupt) {
    +
     2265.   8158        if (
    +
     2266.   8158            next_token.id !== "}"
    +
     2267.   6149            && next_token.id !== "case"
    +
     2268.   6142            && next_token.id !== "default"
    +
     2269.   6135            && next_token.id !== "else"
    +
     2270.   6135            && next_token.id !== "(end)"
    +
     2271.   5866        ) {
    +
     2272.   5866            let a_statement = statement();
    +
     2273.   5866            array.push(a_statement);
    +
     2274.   5866            if (disrupt) {
    +
     2275.   5866
    +
     2276.   5866// cause: "while(0){break;0;}"
    +
     2277.   5866
    +
     2278.   5866                warn("unreachable_a", a_statement);
    +
     2279.   5866            }
    +
     2280.   5866            return next(a_statement.disrupt);
    +
     2281.   5866        }
    +
     2282.   8158    }(false));
    +
     2283.   2349    return array;
    +
     2284.   2349}
    +
     2285.      1
    +
     2286.    582function not_top_level(thing) {
    +
     2287.    582
    +
     2288.    582// Some features should not be at the outermost level.
    +
     2289.    582
    +
     2290.     24    if (functionage === global) {
    +
     2291.     24        warn("unexpected_at_top_level_a", thing);
    +
     2292.     24    }
    +
     2293.    582}
    +
     2294.      1
    +
     2295.     22function top_level_only(the_thing) {
    +
     2296.     22
    +
     2297.     22// Some features must be at the most outermost level.
    +
     2298.     22
    +
     2299.      1    if (blockage !== global) {
    +
     2300.      1
    +
     2301.      1// cause: "if(0){import aa from \"aa\";}"
    +
     2302.      1
    +
     2303.      1        warn("misplaced_a", the_thing);
    +
     2304.      1    }
    +
     2305.     22}
    +
     2306.      1
    +
     2307.   2004function block(special) {
    +
     2308.   2004
    +
     2309.   2004// Parse a block, a sequence of statements wrapped in braces.
    +
     2310.   2004//  special "body"      The block is a function body.
    +
     2311.   2004//          "ignore"    No warning on an empty block.
    +
     2312.   2004//          "naked"     No advance.
    +
     2313.   2004//          undefined   An ordinary block.
    +
     2314.   2004
    +
     2315.   2004    let stmts;
    +
     2316.   2004    let the_block;
    +
     2317.   1994    if (special !== "naked") {
    +
     2318.   1994        advance("{");
    +
     2319.   1994    }
    +
     2320.   2004    the_block = token;
    +
     2321.   2004    the_block.arity = "statement";
    +
     2322.   2004    the_block.body = special === "body";
    +
     2323.   2004
    +
     2324.   2004// Top level function bodies may include the "use strict" pragma.
    +
     2325.   2004
    +
     2326.   2004    if (
    +
     2327.   2004        special === "body"
    +
     2328.    590        && stack.length === 1
    +
     2329.    339        && next_token.value === "use strict"
    +
     2330.      5    ) {
    +
     2331.      5        next_token.statement = true;
    +
     2332.      5        advance("(string)");
    +
     2333.      5        advance(";");
    +
     2334.      5    }
    +
     2335.   2004    stmts = statements();
    +
     2336.   2004    the_block.block = stmts;
    +
     2337.     50    if (stmts.length === 0) {
    +
     2338.     50        if (!option.devel && special !== "ignore") {
    +
     2339.     50
    +
     2340.     50// cause: "function aa(){}"
    +
     2341.     50
    +
     2342.     50            warn("empty_block", the_block);
    +
     2343.     50        }
    +
     2344.     50        the_block.disrupt = false;
    +
     2345.   1952    } else {
    +
     2346.   1952        the_block.disrupt = stmts[stmts.length - 1].disrupt;
    +
     2347.   2002    }
    +
     2348.   2002    advance("}");
    +
     2349.   2002    return the_block;
    +
     2350.   2002}
    +
     2351.      1
    +
     2352.   1278function mutation_check(the_thing) {
    +
     2353.   1278
    +
     2354.   1278// The only expressions that may be assigned to are
    +
     2355.   1278//      e.b
    +
     2356.   1278//      e[b]
    +
     2357.   1278//      v
    +
     2358.   1278//      [destructure]
    +
     2359.   1278//      {destructure}
    +
     2360.   1278
    +
     2361.   1278    if (
    +
     2362.   1278        the_thing.arity !== "variable"
    +
     2363.    616        && the_thing.id !== "."
    +
     2364.     53        && the_thing.id !== "["
    +
     2365.      1        && the_thing.id !== "{"
    +
     2366.      1    ) {
    +
     2367.      1
    +
     2368.      1// cause: "0=0"
    +
     2369.      1
    +
     2370.      1        warn("bad_assignment_a", the_thing);
    +
     2371.      1        return false;
    +
     2372.   1277    }
    +
     2373.   1277    return true;
    +
     2374.   1277}
    +
     2375.      1
    +
     2376.   6556function left_check(left, right) {
    +
     2377.   6556
    +
     2378.   6556// Warn if the left is not one of these:
    +
     2379.   6556//      e.b
    +
     2380.   6556//      e[b]
    +
     2381.   6556//      e()
    +
     2382.   6556//      ?:
    +
     2383.   6556//      identifier
    +
     2384.   6556
    +
     2385.   6556    const id = left.id;
    +
     2386.   6556    if (
    +
     2387.   6556        !left.identifier
    +
     2388.   1319        && (
    +
     2389.   1319            left.arity !== "ternary"
    +
     2390.   1319            || (
    +
     2391.   1319                !left_check(left.expression[1])
    +
     2392.   1319                && !left_check(left.expression[2])
    +
     2393.   1319            )
    +
     2394.   1319        )
    +
     2395.   1319        && (
    +
     2396.   1319            left.arity !== "binary"
    +
     2397.   1319            || (id !== "." && id !== "?." && id !== "(" && id !== "[")
    +
     2398.   1319        )
    +
     2399.      7    ) {
    +
     2400.      7        warn("unexpected_a", right);
    +
     2401.      7        return false;
    +
     2402.   6549    }
    +
     2403.   6549    return true;
    +
     2404.   6549}
    +
     2405.      1
    +
     2406.      1// These functions are used to specify the grammar of our language:
    +
     2407.      1
    +
     2408.    129function symbol(id, bp) {
    +
     2409.    129
    +
     2410.    129// Make a symbol if it does not already exist in the language's syntax.
    +
     2411.    129
    +
     2412.    129    let the_symbol = syntax[id];
    +
     2413.    113    if (the_symbol === undefined) {
    +
     2414.    113        the_symbol = empty();
    +
     2415.    113        the_symbol.id = id;
    +
     2416.    113        the_symbol.lbp = bp || 0;
    +
     2417.    113        syntax[id] = the_symbol;
    +
     2418.    113    }
    +
     2419.    129    return the_symbol;
    +
     2420.    129}
    +
     2421.      1
    +
     2422.     12function assignment(id) {
    +
     2423.     12
    +
     2424.     12// Make an assignment operator. The one true assignment is different because
    +
     2425.     12// its left side, when it is a variable, is not treated as an expression.
    +
     2426.     12// That case is special because that is when a variable gets initialized. The
    +
     2427.     12// other assignment operators can modify, but they cannot initialize.
    +
     2428.     12
    +
     2429.     12    const the_symbol = symbol(id, 20);
    +
     2430.   1274    the_symbol.led = function (left) {
    +
     2431.   1274        const the_token = token;
    +
     2432.   1274        let right;
    +
     2433.   1274        the_token.arity = "assignment";
    +
     2434.   1274        right = expression(20 - 1);
    +
     2435.   1156        if (id === "=" && left.arity === "variable") {
    +
     2436.    577            the_token.names = left;
    +
     2437.    577            the_token.expression = right;
    +
     2438.    697        } else {
    +
     2439.    697            the_token.expression = [left, right];
    +
     2440.    697        }
    +
     2441.   1274        if (
    +
     2442.   1274            right.arity === "assignment"
    +
     2443.   1274            || right.arity === "pre"
    +
     2444.   1272            || right.arity === "post"
    +
     2445.      2        ) {
    +
     2446.      2            warn("unexpected_a", right);
    +
     2447.      2        }
    +
     2448.   1274        mutation_check(left);
    +
     2449.   1274        return the_token;
    +
     2450.   1274    };
    +
     2451.     12    return the_symbol;
    +
     2452.     12}
    +
     2453.      1
    +
     2454.     16function constant(id, type, value) {
    +
     2455.     16
    +
     2456.     16// Make a constant symbol.
    +
     2457.     16
    +
     2458.     16    const the_symbol = symbol(id);
    +
     2459.     16    the_symbol.constant = true;
    +
     2460.     16    the_symbol.nud = (
    +
     2461.     16        typeof value === "function"
    +
     2462.      7        ? value
    +
     2463.   5589        : function () {
    +
     2464.   5589            token.constant = true;
    +
     2465.    510            if (value !== undefined) {
    +
     2466.    510                token.value = value;
    +
     2467.    510            }
    +
     2468.   5589            return token;
    +
     2469.   5589        }
    +
     2470.     16    );
    +
     2471.     16    the_symbol.type = type;
    +
     2472.     16    the_symbol.value = value;
    +
     2473.     16    return the_symbol;
    +
     2474.     16}
    +
     2475.      1
    +
     2476.     30function infix(id, bp, f) {
    +
     2477.     30
    +
     2478.     30// Make an infix operator.
    +
     2479.     30
    +
     2480.     30    const the_symbol = symbol(id, bp);
    +
     2481.   9305    the_symbol.led = function (left) {
    +
     2482.   9305        const the_token = token;
    +
     2483.   9305        the_token.arity = "binary";
    +
     2484.   6648        if (f !== undefined) {
    +
     2485.   6648            return f(left);
    +
     2486.   6648        }
    +
     2487.   2657        the_token.expression = [left, expression(bp)];
    +
     2488.   2657        return the_token;
    +
     2489.   2657    };
    +
     2490.     30    return the_symbol;
    +
     2491.     30}
    +
     2492.      1
    +
     2493.      1function infixr(id, bp) {
    +
     2494.      1
    +
     2495.      1// Make a right associative infix operator.
    +
     2496.      1
    +
     2497.      1    const the_symbol = symbol(id, bp);
    +
     2498.      0    the_symbol.led = function (left) {
    +
     2499.      0        const the_token = token;
    +
     2500.      0        the_token.arity = "binary";
    +
     2501.      0        the_token.expression = [left, expression(bp - 1)];
    +
     2502.      0        return the_token;
    +
     2503.      0    };
    +
     2504.      1    return the_symbol;
    +
     2505.      1}
    +
     2506.      1
    +
     2507.      2function post(id) {
    +
     2508.      2
    +
     2509.      2// Make one of the post operators.
    +
     2510.      2
    +
     2511.      2    const the_symbol = symbol(id, 150);
    +
     2512.      2    the_symbol.led = function (left) {
    +
     2513.      2        token.expression = left;
    +
     2514.      2        token.arity = "post";
    +
     2515.      2        mutation_check(token.expression);
    +
     2516.      2        return token;
    +
     2517.      2    };
    +
     2518.      2    return the_symbol;
    +
     2519.      2}
    +
     2520.      1
    +
     2521.      2function pre(id) {
    +
     2522.      2
    +
     2523.      2// Make one of the pre operators.
    +
     2524.      2
    +
     2525.      2    const the_symbol = symbol(id);
    +
     2526.      2    the_symbol.nud = function () {
    +
     2527.      2        const the_token = token;
    +
     2528.      2        the_token.arity = "pre";
    +
     2529.      2        the_token.expression = expression(150);
    +
     2530.      2        mutation_check(the_token.expression);
    +
     2531.      2        return the_token;
    +
     2532.      2    };
    +
     2533.      2    return the_symbol;
    +
     2534.      2}
    +
     2535.      1
    +
     2536.     17function prefix(id, f) {
    +
     2537.     17
    +
     2538.     17// Make a prefix operator.
    +
     2539.     17
    +
     2540.     17    const the_symbol = symbol(id);
    +
     2541.   1304    the_symbol.nud = function () {
    +
     2542.   1304        const the_token = token;
    +
     2543.   1304        the_token.arity = "unary";
    +
     2544.   1061        if (typeof f === "function") {
    +
     2545.   1061            return f();
    +
     2546.   1061        }
    +
     2547.    243        the_token.expression = expression(150);
    +
     2548.    243        return the_token;
    +
     2549.    243    };
    +
     2550.     17    return the_symbol;
    +
     2551.     17}
    +
     2552.      1
    +
     2553.     22function stmt(id, f) {
    +
     2554.     22
    +
     2555.     22// Make a statement.
    +
     2556.     22
    +
     2557.     22    const the_symbol = symbol(id);
    +
     2558.   2877    the_symbol.fud = function () {
    +
     2559.   2877        token.arity = "statement";
    +
     2560.   2877        return f();
    +
     2561.   2877    };
    +
     2562.     22    return the_symbol;
    +
     2563.     22}
    +
     2564.      1
    +
     2565.      1function ternary(id1, id2) {
    +
     2566.      1
    +
     2567.      1// Make a ternary operator.
    +
     2568.      1
    +
     2569.      1    const the_symbol = symbol(id1, 30);
    +
     2570.     58    the_symbol.led = function (left) {
    +
     2571.     58        const the_token = token;
    +
     2572.     58        const second = expression(20);
    +
     2573.     58        advance(id2);
    +
     2574.     58        token.arity = "ternary";
    +
     2575.     58        the_token.arity = "ternary";
    +
     2576.     58        the_token.expression = [left, second, expression(10)];
    +
     2577.      1        if (next_token.id !== ")") {
    +
     2578.      1
    +
     2579.      1// cause: "0?0:0"
    +
     2580.      1
    +
     2581.      1            warn("use_open", the_token);
    +
     2582.      1        }
    +
     2583.     58        return the_token;
    +
     2584.     58    };
    +
     2585.      1    return the_symbol;
    +
     2586.      1}
    +
     2587.      1
    +
     2588.      1// Begin defining the language.
    +
     2589.      1
    +
     2590.      1syntax = empty();
    +
     2591.      1
    +
     2592.      1symbol("}");
    +
     2593.      1symbol(")");
    +
     2594.      1symbol("]");
    +
     2595.      1symbol(",");
    +
     2596.      1symbol(";");
    +
     2597.      1symbol(":");
    +
     2598.      1symbol("*/");
    +
     2599.      1symbol("async");
    +
     2600.      1symbol("await");
    +
     2601.      1symbol("case");
    +
     2602.      1symbol("catch");
    +
     2603.      1symbol("class");
    +
     2604.      1symbol("default");
    +
     2605.      1symbol("else");
    +
     2606.      1symbol("enum");
    +
     2607.      1symbol("finally");
    +
     2608.      1symbol("implements");
    +
     2609.      1symbol("interface");
    +
     2610.      1symbol("package");
    +
     2611.      1symbol("private");
    +
     2612.      1symbol("protected");
    +
     2613.      1symbol("public");
    +
     2614.      1symbol("static");
    +
     2615.      1symbol("super");
    +
     2616.      1symbol("void");
    +
     2617.      1symbol("yield");
    +
     2618.      1
    +
     2619.      1constant("(number)", "number");
    +
     2620.      1constant("(regexp)", "regexp");
    +
     2621.      1constant("(string)", "string");
    +
     2622.      1constant("arguments", "object", function () {
    +
     2623.      1    warn("unexpected_a", token);
    +
     2624.      1    return token;
    +
     2625.      1});
    +
     2626.      3constant("eval", "function", function () {
    +
     2627.      1    if (!option.eval) {
    +
     2628.      1        warn("unexpected_a", token);
    +
     2629.      2    } else if (next_token.id !== "(") {
    +
     2630.      2        warn("expected_a_before_b", next_token, "(", artifact());
    +
     2631.      2    }
    +
     2632.      3    return token;
    +
     2633.      3});
    +
     2634.      1constant("false", "boolean", false);
    +
     2635.      4constant("Function", "function", function () {
    +
     2636.      2    if (!option.eval) {
    +
     2637.      2        warn("unexpected_a", token);
    +
     2638.      2    } else if (next_token.id !== "(") {
    +
     2639.      2        warn("expected_a_before_b", next_token, "(", artifact());
    +
     2640.      2    }
    +
     2641.      4    return token;
    +
     2642.      4});
    +
     2643.      1constant("ignore", "undefined", function () {
    +
     2644.      1    warn("unexpected_a", token);
    +
     2645.      1    return token;
    +
     2646.      1});
    +
     2647.      1constant("Infinity", "number", Infinity);
    +
     2648.      1constant("isFinite", "function", function () {
    +
     2649.      1    warn("expected_a_b", token, "Number.isFinite", "isFinite");
    +
     2650.      1    return token;
    +
     2651.      1});
    +
     2652.      1constant("isNaN", "function", function () {
    +
     2653.      1
    +
     2654.      1// cause: "isNaN(0)"
    +
     2655.      1
    +
     2656.      1    warn("number_isNaN", token);
    +
     2657.      1    return token;
    +
     2658.      1});
    +
     2659.      1constant("NaN", "number", NaN);
    +
     2660.      1constant("null", "null", null);
    +
     2661.      2constant("this", "object", function () {
    +
     2662.      1    if (!option.this) {
    +
     2663.      1        warn("unexpected_a", token);
    +
     2664.      1    }
    +
     2665.      2    return token;
    +
     2666.      2});
    +
     2667.      1constant("true", "boolean", true);
    +
     2668.      1constant("undefined", "undefined");
    +
     2669.      1
    +
     2670.      1assignment("=");
    +
     2671.      1assignment("+=");
    +
     2672.      1assignment("-=");
    +
     2673.      1assignment("*=");
    +
     2674.      1assignment("/=");
    +
     2675.      1assignment("%=");
    +
     2676.      1assignment("&=");
    +
     2677.      1assignment("|=");
    +
     2678.      1assignment("^=");
    +
     2679.      1assignment("<<=");
    +
     2680.      1assignment(">>=");
    +
     2681.      1assignment(">>>=");
    +
     2682.      1
    +
     2683.      1infix("??", 35);
    +
     2684.      1infix("||", 40);
    +
     2685.      1infix("&&", 50);
    +
     2686.      1infix("|", 70);
    +
     2687.      1infix("^", 80);
    +
     2688.      1infix("&", 90);
    +
     2689.      1infix("==", 100);
    +
     2690.      1infix("===", 100);
    +
     2691.      1infix("!=", 100);
    +
     2692.      1infix("!==", 100);
    +
     2693.      1infix("<", 110);
    +
     2694.      1infix(">", 110);
    +
     2695.      1infix("<=", 110);
    +
     2696.      1infix(">=", 110);
    +
     2697.      1infix("in", 110);
    +
     2698.      1infix("instanceof", 110);
    +
     2699.      1infix("<<", 120);
    +
     2700.      1infix(">>", 120);
    +
     2701.      1infix(">>>", 120);
    +
     2702.      1infix("+", 130);
    +
     2703.      1infix("-", 130);
    +
     2704.      1infix("*", 140);
    +
     2705.      1infix("/", 140);
    +
     2706.      1infix("%", 140);
    +
     2707.      1infixr("**", 150);
    +
     2708.   2873infix("(", 160, function (left) {
    +
     2709.   2873    const the_paren = token;
    +
     2710.   2873    let the_argument;
    +
     2711.   2807    if (left.id !== "function") {
    +
     2712.   2807        left_check(left, the_paren);
    +
     2713.   2807    }
    +
     2714.    957    if (functionage.arity === "statement" && left.identifier) {
    +
     2715.    701        functionage.name.calls[left.id] = left;
    +
     2716.    701    }
    +
     2717.   2873    the_paren.expression = [left];
    +
     2718.   2331    if (next_token.id !== ")") {
    +
     2719.   3901        (function next() {
    +
     2720.   3901            let ellipsis;
    +
     2721.   2331            if (next_token.id === "...") {
    +
     2722.   2331                ellipsis = true;
    +
     2723.   2331                advance("...");
    +
     2724.   2331            }
    +
     2725.   3901            the_argument = expression(10);
    +
     2726.   2331            if (ellipsis) {
    +
     2727.   2331                the_argument.ellipsis = true;
    +
     2728.   2331            }
    +
     2729.   3901            the_paren.expression.push(the_argument);
    +
     2730.   2331            if (next_token.id === ",") {
    +
     2731.   2331                advance(",");
    +
     2732.   2331                return next();
    +
     2733.   2331            }
    +
     2734.   3901        }());
    +
     2735.   2331    }
    +
     2736.   2873    advance(")", the_paren);
    +
     2737.   1336    if (the_paren.expression.length === 2) {
    +
     2738.   1336
    +
     2739.   1336// cause: "aa(0)"
    +
     2740.   1336
    +
     2741.   1336        the_paren.free = true;
    +
     2742.   1336        if (the_argument.wrapped === true) {
    +
     2743.   1336            warn("unexpected_a", the_paren);
    +
     2744.   1336        }
    +
     2745.   1336        if (the_argument.id === "(") {
    +
     2746.   1336            the_argument.wrapped = true;
    +
     2747.   1336        }
    +
     2748.   1537    } else {
    +
     2749.   1537
    +
     2750.   1537// cause: "aa()"
    +
     2751.   1537// cause: "aa(0,0)"
    +
     2752.   1537
    +
     2753.   1537        the_paren.free = false;
    +
     2754.   1537    }
    +
     2755.   2873    return the_paren;
    +
     2756.   2873});
    +
     2757.   3328infix(".", 170, function (left) {
    +
     2758.   3328    const the_token = token;
    +
     2759.   3328    const name = next_token;
    +
     2760.   3328    if (
    +
     2761.   3328        (
    +
     2762.   3328            left.id !== "(string)"
    +
     2763.     10            || (name.id !== "indexOf" && name.id !== "repeat")
    +
     2764.   3328        )
    +
     2765.   3318        && (
    +
     2766.   3318            left.id !== "["
    +
     2767.   3318            || (
    +
     2768.   3318                name.id !== "concat"
    +
     2769.   3318                && name.id !== "forEach"
    +
     2770.   3318                && name.id !== "join"
    +
     2771.   3318                && name.id !== "map"
    +
     2772.   3318            )
    +
     2773.   3318        )
    +
     2774.   3315        && (left.id !== "+" || name.id !== "slice")
    +
     2775.   3313        && (
    +
     2776.   3313            left.id !== "(regexp)"
    +
     2777.   3313            || (name.id !== "exec" && name.id !== "test")
    +
     2778.   3313        )
    +
     2779.   3301    ) {
    +
     2780.   3301        left_check(left, the_token);
    +
     2781.   3301    }
    +
     2782.      1    if (!name.identifier) {
    +
     2783.      1        stop("expected_identifier_a");
    +
     2784.   3327    }
    +
     2785.   3327    advance();
    +
     2786.   3327    survey(name);
    +
     2787.   3327
    +
     2788.   3327// The property name is not an expression.
    +
     2789.   3327
    +
     2790.   3327    the_token.name = name;
    +
     2791.   3327    the_token.expression = left;
    +
     2792.   3327    return the_token;
    +
     2793.   3327});
    +
     2794.      6infix("?.", 170, function (left) {
    +
     2795.      6    const the_token = token;
    +
     2796.      6    const name = next_token;
    +
     2797.      6    if (
    +
     2798.      6        (
    +
     2799.      6            left.id !== "(string)"
    +
     2800.      1            || (name.id !== "indexOf" && name.id !== "repeat")
    +
     2801.      6        )
    +
     2802.      6        && (
    +
     2803.      6            left.id !== "["
    +
     2804.      1            || (
    +
     2805.      1                name.id !== "concat"
    +
     2806.      1                && name.id !== "forEach"
    +
     2807.      1                && name.id !== "join"
    +
     2808.      1                && name.id !== "map"
    +
     2809.      1            )
    +
     2810.      6        )
    +
     2811.      0        && (left.id !== "+" || name.id !== "slice")
    +
     2812.      6        && (
    +
     2813.      6            left.id !== "(regexp)"
    +
     2814.      1            || (name.id !== "exec" && name.id !== "test")
    +
     2815.      6        )
    +
     2816.      6    ) {
    +
     2817.      6        left_check(left, the_token);
    +
     2818.      6    }
    +
     2819.      1    if (!name.identifier) {
    +
     2820.      1        stop("expected_identifier_a");
    +
     2821.      5    }
    +
     2822.      5    advance();
    +
     2823.      5    survey(name);
    +
     2824.      5
    +
     2825.      5// The property name is not an expression.
    +
     2826.      5
    +
     2827.      5    the_token.name = name;
    +
     2828.      5    the_token.expression = left;
    +
     2829.      5    return the_token;
    +
     2830.      5});
    +
     2831.    416infix("[", 170, function (left) {
    +
     2832.    416    const the_token = token;
    +
     2833.    416    const the_subscript = expression(0);
    +
     2834.    414    if (the_subscript.id === "(string)" || the_subscript.id === "`") {
    +
     2835.      4        const name = survey(the_subscript);
    +
     2836.      4        if (rx_identifier.test(name)) {
    +
     2837.      4
    +
     2838.      4// cause: "aa[`aa`]"
    +
     2839.      4
    +
     2840.      4            warn("subscript_a", the_subscript, name);
    +
     2841.      4        }
    +
     2842.      4    }
    +
     2843.    416    left_check(left, the_token);
    +
     2844.    416    the_token.expression = [left, the_subscript];
    +
     2845.    416    advance("]");
    +
     2846.    416    return the_token;
    +
     2847.    416});
    +
     2848.      1infix("=>", 170, function (left) {
    +
     2849.      1
    +
     2850.      1// cause: "aa=>0"
    +
     2851.      1
    +
     2852.      1    return stop("wrap_parameter", left);
    +
     2853.      1});
    +
     2854.      1
    +
     2855.     58function do_tick() {
    +
     2856.     58    const the_tick = token;
    +
     2857.     58    the_tick.value = [];
    +
     2858.     58    the_tick.expression = [];
    +
     2859.     58    if (next_token.id !== "`") {
    +
     2860.     94        (function part() {
    +
     2861.     94            advance("(string)");
    +
     2862.     94            the_tick.value.push(token);
    +
     2863.     36            if (next_token.id === "${") {
    +
     2864.     36                advance("${");
    +
     2865.     36                the_tick.expression.push(expression(0));
    +
     2866.     36                advance("}");
    +
     2867.     36                return part();
    +
     2868.     36            }
    +
     2869.     94        }());
    +
     2870.     58    }
    +
     2871.     58    advance("`");
    +
     2872.     58    return the_tick;
    +
     2873.     58}
    +
     2874.      1
    +
     2875.     24infix("`", 160, function (left) {
    +
     2876.     24    const the_tick = do_tick();
    +
     2877.     24    left_check(left, the_tick);
    +
     2878.     24    the_tick.expression = [left].concat(the_tick.expression);
    +
     2879.     24    return the_tick;
    +
     2880.     24});
    +
     2881.      1
    +
     2882.      1post("++");
    +
     2883.      1post("--");
    +
     2884.      1pre("++");
    +
     2885.      1pre("--");
    +
     2886.      1
    +
     2887.      1prefix("+");
    +
     2888.      1prefix("-");
    +
     2889.      1prefix("~");
    +
     2890.      1prefix("!");
    +
     2891.      1prefix("!!");
    +
     2892.    158prefix("[", function () {
    +
     2893.    158    const the_token = token;
    +
     2894.    158    the_token.expression = [];
    +
     2895.     85    if (next_token.id !== "]") {
    +
     2896.    621        (function next() {
    +
     2897.    621            let element;
    +
     2898.    621            let ellipsis = false;
    +
     2899.     85            if (next_token.id === "...") {
    +
     2900.     85                ellipsis = true;
    +
     2901.     85                advance("...");
    +
     2902.     85            }
    +
     2903.    621            element = expression(10);
    +
     2904.     85            if (ellipsis) {
    +
     2905.     85                element.ellipsis = true;
    +
     2906.     85            }
    +
     2907.    621            the_token.expression.push(element);
    +
     2908.    536            if (next_token.id === ",") {
    +
     2909.    536                advance(",");
    +
     2910.    536                return next();
    +
     2911.    536            }
    +
     2912.    621        }());
    +
     2913.     85    }
    +
     2914.    158    advance("]");
    +
     2915.    158    return the_token;
    +
     2916.    158});
    +
     2917.      1prefix("/=", function () {
    +
     2918.      1
    +
     2919.      1// cause: "/="
    +
     2920.      1
    +
     2921.      1    stop("expected_a_b", token, "/\\=", "/=");
    +
     2922.      1});
    +
     2923.      1prefix("=>", function () {
    +
     2924.      1    return stop("expected_a_before_b", token, "()", "=>");
    +
     2925.      1});
    +
     2926.     25prefix("new", function () {
    +
     2927.     25    const the_new = token;
    +
     2928.     25    const right = expression(160);
    +
     2929.      1    if (next_token.id !== "(") {
    +
     2930.      1        warn("expected_a_before_b", next_token, "()", artifact(next_token));
    +
     2931.      1    }
    +
     2932.     25    the_new.expression = right;
    +
     2933.     25    return the_new;
    +
     2934.     25});
    +
     2935.      1prefix("typeof");
    +
     2936.      2prefix("void", function () {
    +
     2937.      2    const the_void = token;
    +
     2938.      2
    +
     2939.      2// cause: "void"
    +
     2940.      2
    +
     2941.      2    warn("unexpected_a", the_void);
    +
     2942.      2    the_void.expression = expression(0);
    +
     2943.      2    return the_void;
    +
     2944.      2});
    +
     2945.      1
    +
     2946.    602function parameter_list() {
    +
     2947.    602    const list = [];
    +
     2948.    602    let optional;
    +
     2949.    602    const signature = ["("];
    +
     2950.    338    if (next_token.id !== ")" && next_token.id !== "(end)") {
    +
     2951.    485        (function parameter() {
    +
     2952.    485            let ellipsis = false;
    +
     2953.    485            let param;
    +
     2954.    338            if (next_token.id === "{") {
    +
     2955.    338                let a;
    +
     2956.    338                let b = "";
    +
     2957.    338                if (optional !== undefined) {
    +
     2958.    338                    warn(
    +
     2959.    338                        "required_a_optional_b",
    +
     2960.    338                        next_token,
    +
     2961.    338                        next_token.id,
    +
     2962.    338                        optional.id
    +
     2963.    338                    );
    +
     2964.    338                }
    +
     2965.    338                param = next_token;
    +
     2966.    338                param.names = [];
    +
     2967.    338                advance("{");
    +
     2968.    338                signature.push("{");
    +
     2969.    338                (function subparameter() {
    +
     2970.    338                    let subparam = next_token;
    +
     2971.    338                    if (!subparam.identifier) {
    +
     2972.    338                        return stop("expected_identifier_a");
    +
     2973.    338                    }
    +
     2974.    338                    survey(subparam);
    +
     2975.    338                    a = b;
    +
     2976.    338                    b = String(subparam.value || subparam.id);
    +
     2977.    338                    if (a > b) {
    +
     2978.    338                        if (!option.unordered) {
    +
     2979.    338
    +
     2980.    338// cause: "function aa({bb,aa}){}"
    +
     2981.    338
    +
     2982.    338                            warn("unordered_param_a", subparam);
    +
     2983.    338                        }
    +
     2984.    338                    }
    +
     2985.    338                    advance();
    +
     2986.    338                    signature.push(subparam.id);
    +
     2987.    338                    if (next_token.id === ":") {
    +
     2988.    338                        advance(":");
    +
     2989.    338                        advance();
    +
     2990.    338                        token.label = subparam;
    +
     2991.    338                        subparam = token;
    +
     2992.    338                        if (!subparam.identifier) {
    +
     2993.    338                            return stop("expected_identifier_a");
    +
     2994.    338                        }
    +
     2995.    338                    }
    +
     2996.    338                    if (next_token.id === "=") {
    +
     2997.    338                        advance("=");
    +
     2998.    338                        subparam.expression = expression();
    +
     2999.    338                        param.open = true;
    +
     3000.    338                    }
    +
     3001.    338                    param.names.push(subparam);
    +
     3002.    338                    if (next_token.id === ",") {
    +
     3003.    338                        advance(",");
    +
     3004.    338                        signature.push(", ");
    +
     3005.    338                        return subparameter();
    +
     3006.    338                    }
    +
     3007.    338                }());
    +
     3008.    338                list.push(param);
    +
     3009.    338                advance("}");
    +
     3010.    338                signature.push("}");
    +
     3011.    338                if (next_token.id === ",") {
    +
     3012.    338                    advance(",");
    +
     3013.    338                    signature.push(", ");
    +
     3014.    338                    return parameter();
    +
     3015.    338                }
    +
     3016.    458            } else if (next_token.id === "[") {
    +
     3017.    458                if (optional !== undefined) {
    +
     3018.    458                    warn(
    +
     3019.    458                        "required_a_optional_b",
    +
     3020.    458                        next_token,
    +
     3021.    458                        next_token.id,
    +
     3022.    458                        optional.id
    +
     3023.    458                    );
    +
     3024.    458                }
    +
     3025.    458                param = next_token;
    +
     3026.    458                param.names = [];
    +
     3027.    458                advance("[");
    +
     3028.    458                signature.push("[]");
    +
     3029.    458                (function subparameter() {
    +
     3030.    458                    const subparam = next_token;
    +
     3031.    458                    if (!subparam.identifier) {
    +
     3032.    458                        return stop("expected_identifier_a");
    +
     3033.    458                    }
    +
     3034.    458                    advance();
    +
     3035.    458                    param.names.push(subparam);
    +
     3036.    458                    if (next_token.id === "=") {
    +
     3037.    458                        advance("=");
    +
     3038.    458                        subparam.expression = expression();
    +
     3039.    458                        param.open = true;
    +
     3040.    458                    }
    +
     3041.    458                    if (next_token.id === ",") {
    +
     3042.    458                        advance(",");
    +
     3043.    458                        return subparameter();
    +
     3044.    458                    }
    +
     3045.    458                }());
    +
     3046.    458                list.push(param);
    +
     3047.    458                advance("]");
    +
     3048.    458                if (next_token.id === ",") {
    +
     3049.    458                    advance(",");
    +
     3050.    458                    signature.push(", ");
    +
     3051.    458                    return parameter();
    +
     3052.    458                }
    +
     3053.    458            } else {
    +
     3054.    458                if (next_token.id === "...") {
    +
     3055.    458                    ellipsis = true;
    +
     3056.    458                    signature.push("...");
    +
     3057.    458                    advance("...");
    +
     3058.    458                    if (optional !== undefined) {
    +
     3059.    458                        warn(
    +
     3060.    458                            "required_a_optional_b",
    +
     3061.    458                            next_token,
    +
     3062.    458                            next_token.id,
    +
     3063.    458                            optional.id
    +
     3064.    458                        );
    +
     3065.    458                    }
    +
     3066.    458                }
    +
     3067.    458                if (!next_token.identifier) {
    +
     3068.    458                    return stop("expected_identifier_a");
    +
     3069.    458                }
    +
     3070.    458                param = next_token;
    +
     3071.    458                list.push(param);
    +
     3072.    458                advance();
    +
     3073.    458                signature.push(param.id);
    +
     3074.    458                if (ellipsis) {
    +
     3075.    458                    param.ellipsis = true;
    +
     3076.    458                } else {
    +
     3077.    458                    if (next_token.id === "=") {
    +
     3078.    458                        optional = param;
    +
     3079.    458                        advance("=");
    +
     3080.    458                        param.expression = expression(0);
    +
     3081.    458                    } else {
    +
     3082.    458                        if (optional !== undefined) {
    +
     3083.    458                            warn(
    +
     3084.    458                                "required_a_optional_b",
    +
     3085.    458                                param,
    +
     3086.    458                                param.id,
    +
     3087.    458                                optional.id
    +
     3088.    458                            );
    +
     3089.    458                        }
    +
     3090.    458                    }
    +
     3091.    458                    if (next_token.id === ",") {
    +
     3092.    458                        advance(",");
    +
     3093.    458                        signature.push(", ");
    +
     3094.    458                        return parameter();
    +
     3095.    458                    }
    +
     3096.    458                }
    +
     3097.    458            }
    +
     3098.    485        }());
    +
     3099.    596    }
    +
     3100.    596    advance(")");
    +
     3101.    596    signature.push(")");
    +
     3102.    596    return [list, signature.join("")];
    +
     3103.    596}
    +
     3104.      1
    +
     3105.    599function do_function(the_function) {
    +
     3106.    599    let name;
    +
     3107.    591    if (the_function === undefined) {
    +
     3108.    591        the_function = token;
    +
     3109.    591
    +
     3110.    591// A function statement must have a name that will be in the parent's scope.
    +
     3111.    591
    +
     3112.    591        if (the_function.arity === "statement") {
    +
     3113.    591            if (!next_token.identifier) {
    +
     3114.    591
    +
     3115.    591// cause: "function*aa(){}"
    +
     3116.    591
    +
     3117.    591                return stop("expected_identifier_a", next_token);
    +
     3118.    591            }
    +
     3119.    591            name = next_token;
    +
     3120.    591            enroll(name, "variable", true);
    +
     3121.    591            the_function.name = name;
    +
     3122.    591            name.init = true;
    +
     3123.    591            name.calls = empty();
    +
     3124.    591            advance();
    +
     3125.    591        } else if (name === undefined) {
    +
     3126.    591
    +
     3127.    591// A function expression may have an optional name.
    +
     3128.    591
    +
     3129.    591            if (next_token.identifier) {
    +
     3130.    591                name = next_token;
    +
     3131.    591                the_function.name = name;
    +
     3132.    591                advance();
    +
     3133.    591            } else {
    +
     3134.    591                the_function.name = anon;
    +
     3135.    591            }
    +
     3136.    591        }
    +
     3137.    591    } else {
    +
     3138.      8        name = the_function.name;
    +
     3139.    595    }
    +
     3140.    595    the_function.level = functionage.level + 1;
    +
     3141.      0    if (mega_mode) {
    +
     3142.      0        warn("unexpected_a", the_function);
    +
     3143.      0    }
    +
     3144.    595
    +
     3145.    595// Don't make functions in loops. It is inefficient, and it can lead to scoping
    +
     3146.    595// errors.
    +
     3147.    595
    +
     3148.    595    if (functionage.loop > 0) {
    +
     3149.      1
    +
     3150.      1// cause: "while(0){aa.map(function(){});}"
    +
     3151.      1
    +
     3152.      1        warn("function_in_loop", the_function);
    +
     3153.    595    }
    +
     3154.    595
    +
     3155.    595// Give the function properties for storing its names and for observing the
    +
     3156.    595// depth of loops and switches.
    +
     3157.    595
    +
     3158.    595    the_function.context = empty();
    +
     3159.    595    the_function.finally = 0;
    +
     3160.    595    the_function.loop = 0;
    +
     3161.    595    the_function.switch = 0;
    +
     3162.    595    the_function.try = 0;
    +
     3163.    595
    +
     3164.    595// Push the current function context and establish a new one.
    +
     3165.    595
    +
     3166.    595    stack.push(functionage);
    +
     3167.    595    functions.push(the_function);
    +
     3168.    595    functionage = the_function;
    +
     3169.    595    if (the_function.arity !== "statement" && typeof name === "object") {
    +
     3170.     59        enroll(name, "function", true);
    +
     3171.     59        name.dead = false;
    +
     3172.     59        name.init = true;
    +
     3173.     59        name.used = 1;
    +
     3174.    595    }
    +
     3175.    595
    +
     3176.    595// Parse the parameter list.
    +
     3177.    595
    +
     3178.    595    advance("(");
    +
     3179.    595
    +
     3180.    595// cause: "function(){}"
    +
     3181.    595
    +
     3182.    595    token.free = false;
    +
     3183.    595    token.arity = "function";
    +
     3184.    595    [functionage.parameters, functionage.signature] = parameter_list();
    +
     3185.    595    functionage.parameters.forEach(function enroll_parameter(name) {
    +
     3186.    595        if (name.identifier) {
    +
     3187.    595            enroll(name, "parameter", false);
    +
     3188.    595        } else {
    +
     3189.    595            name.names.forEach(enroll_parameter);
    +
     3190.    595        }
    +
     3191.    595    });
    +
     3192.    595
    +
     3193.    595// The function's body is a block.
    +
     3194.    595
    +
     3195.    595    the_function.block = block("body");
    +
     3196.    595    if (
    +
     3197.    595        the_function.arity === "statement"
    +
     3198.    595        && next_token.line === token.line
    +
     3199.      2    ) {
    +
     3200.      2        return stop("unexpected_a", next_token);
    +
     3201.    587    }
    +
     3202.    587    if (
    +
     3203.    587        next_token.id === "."
    +
     3204.    587        || next_token.id === "?."
    +
     3205.    587        || next_token.id === "["
    +
     3206.      1    ) {
    +
     3207.      1        warn("unexpected_a");
    +
     3208.    587    }
    +
     3209.    587
    +
     3210.    587// Restore the previous context.
    +
     3211.    587
    +
     3212.    587    functionage = stack.pop();
    +
     3213.    587    return the_function;
    +
     3214.    587}
    +
     3215.      1
    +
     3216.     16function do_async() {
    +
     3217.     16    let the_async;
    +
     3218.     16    let the_function;
    +
     3219.     16    the_async = token;
    +
     3220.     16    advance("function");
    +
     3221.     16    the_function = token;
    +
     3222.     16    the_function.is_async = true;
    +
     3223.     16    the_function.arity = the_async.arity;
    +
     3224.     16    do_function();
    +
     3225.      1    if (!the_function.has_await) {
    +
     3226.      1
    +
     3227.      1// cause: "async function aa(){}"
    +
     3228.      1
    +
     3229.      1        warn("missing_await_statement", the_function);
    +
     3230.     15    }
    +
     3231.     15    return the_function;
    +
     3232.     15}
    +
     3233.      1
    +
     3234.      1prefix("async", do_async);
    +
     3235.      1prefix("function", do_function);
    +
     3236.     27prefix("await", function () {
    +
     3237.     27    let the_await;
    +
     3238.     27    the_await = token;
    +
     3239.      1    if (!functionage.is_async) {
    +
     3240.      1        return stop("unexpected_a", the_await);
    +
     3241.     26    }
    +
     3242.     26    functionage.has_await = true;
    +
     3243.     26    the_await.expression = expression(150);
    +
     3244.     26    return the_await;
    +
     3245.     26});
    +
     3246.      1
    +
     3247.      8function fart(pl) {
    +
     3248.      8    advance("=>");
    +
     3249.      8    const the_fart = token;
    +
     3250.      8    the_fart.arity = "binary";
    +
     3251.      8    the_fart.name = "=>";
    +
     3252.      8    the_fart.level = functionage.level + 1;
    +
     3253.      8    functions.push(the_fart);
    +
     3254.      1    if (functionage.loop > 0) {
    +
     3255.      1
    +
     3256.      1// cause: "while(0){aa.map(()=>0);}"
    +
     3257.      1
    +
     3258.      1        warn("function_in_loop", the_fart);
    +
     3259.      6    }
    +
     3260.      6
    +
     3261.      6// Give the function properties storing its names and for observing the depth
    +
     3262.      6// of loops and switches.
    +
     3263.      6
    +
     3264.      6    the_fart.context = empty();
    +
     3265.      6    the_fart.finally = 0;
    +
     3266.      6    the_fart.loop = 0;
    +
     3267.      6    the_fart.switch = 0;
    +
     3268.      6    the_fart.try = 0;
    +
     3269.      6
    +
     3270.      6// Push the current function context and establish a new one.
    +
     3271.      6
    +
     3272.      6    stack.push(functionage);
    +
     3273.      6    functionage = the_fart;
    +
     3274.      6    the_fart.parameters = pl[0];
    +
     3275.      6    the_fart.signature = pl[1];
    +
     3276.      6    the_fart.parameters.forEach(function (name) {
    +
     3277.      6        enroll(name, "parameter", true);
    +
     3278.      6    });
    +
     3279.      6    if (next_token.id === "{") {
    +
     3280.      1        warn("expected_a_b", the_fart, "function", "=>");
    +
     3281.      1        the_fart.block = block("body");
    +
     3282.      5    } else {
    +
     3283.      5        the_fart.expression = expression(0);
    +
     3284.      6    }
    +
     3285.      6    functionage = stack.pop();
    +
     3286.      6    return the_fart;
    +
     3287.      6}
    +
     3288.      1
    +
     3289.    392prefix("(", function () {
    +
     3290.    392    const the_paren = token;
    +
     3291.    392    let the_value;
    +
     3292.    392    const cadet = lookahead().id;
    +
     3293.    392
    +
     3294.    392// We can distinguish between a parameter list for => and a wrapped expression
    +
     3295.    392// with one token of lookahead.
    +
     3296.    392
    +
     3297.    392    if (
    +
     3298.    392        next_token.id === ")"
    +
     3299.    385        || next_token.id === "..."
    +
     3300.    385        || (next_token.identifier && (cadet === "," || cadet === "="))
    +
     3301.      7    ) {
    +
     3302.      7
    +
     3303.      7// cause: "()=>0"
    +
     3304.      7
    +
     3305.      7        the_paren.free = false;
    +
     3306.      7        return fart(parameter_list());
    +
     3307.    385    }
    +
     3308.    385
    +
     3309.    385// cause: "(0)"
    +
     3310.    385
    +
     3311.    385    the_paren.free = true;
    +
     3312.    385    the_value = expression(0);
    +
     3313.    385    if (the_value.wrapped === true) {
    +
     3314.      2
    +
     3315.      2// cause: "((0))"
    +
     3316.      2
    +
     3317.      2        warn("unexpected_a", the_paren);
    +
     3318.    384    }
    +
     3319.    384    the_value.wrapped = true;
    +
     3320.    384    advance(")", the_paren);
    +
     3321.    384    if (next_token.id === "=>") {
    +
     3322.      3        if (the_value.arity !== "variable") {
    +
     3323.      3            if (the_value.id === "{" || the_value.id === "[") {
    +
     3324.      3                warn("expected_a_before_b", the_paren, "function", "(");
    +
     3325.      3                return stop("expected_a_b", next_token, "{", "=>");
    +
     3326.      3            }
    +
     3327.      3            return stop("expected_identifier_a", the_value);
    +
     3328.      3        }
    +
     3329.      3        the_paren.expression = [the_value];
    +
     3330.      3        return fart([the_paren.expression, "(" + the_value.id + ")"]);
    +
     3331.    380    }
    +
     3332.    380    return the_value;
    +
     3333.    380});
    +
     3334.      1prefix("`", do_tick);
    +
     3335.     84prefix("{", function () {
    +
     3336.     84    const the_brace = token;
    +
     3337.     84    const seen = empty();
    +
     3338.     84    the_brace.expression = [];
    +
     3339.     78    if (next_token.id !== "}") {
    +
     3340.     78        let a;
    +
     3341.     78        let b = "";
    +
     3342.    488        (function member() {
    +
     3343.    488            let extra;
    +
     3344.    488            let full;
    +
     3345.    488            let id;
    +
     3346.    488            let name = next_token;
    +
     3347.    488            let value;
    +
     3348.    488            advance();
    +
     3349.    488            a = b;
    +
     3350.    479            b = String(name.value || name.id);
    +
     3351.     78            if (a > b) {
    +
     3352.     78                if (!option.unordered) {
    +
     3353.     78
    +
     3354.     78// cause: "aa={bb,aa}"
    +
     3355.     78
    +
     3356.     78                    warn("unordered_property_a", name);
    +
     3357.     78                }
    +
     3358.     78            }
    +
     3359.    488            if (
    +
     3360.    483                (name.id === "get" || name.id === "set")
    +
     3361.     78                && next_token.identifier
    +
     3362.     78            ) {
    +
     3363.     78                if (!option.getset) {
    +
     3364.     78
    +
     3365.     78// cause: "aa={get aa(){}}"
    +
     3366.     78
    +
     3367.     78                    warn("unexpected_a", name);
    +
     3368.     78                }
    +
     3369.     78                extra = name.id;
    +
     3370.     78                full = extra + " " + next_token.id;
    +
     3371.     78                name = next_token;
    +
     3372.     78                advance();
    +
     3373.     78                id = survey(name);
    +
     3374.     78                if (seen[full] === true || seen[id] === true) {
    +
     3375.     78
    +
     3376.     78// cause: "aa={get aa(){},get aa(){}}"
    +
     3377.     78
    +
     3378.     78                    warn("duplicate_a", name);
    +
     3379.     78                }
    +
     3380.     78                seen[id] = false;
    +
     3381.     78                seen[full] = true;
    +
     3382.    480            } else {
    +
     3383.    480                id = survey(name);
    +
     3384.    480                if (typeof seen[id] === "boolean") {
    +
     3385.    480
    +
     3386.    480// cause: "aa={aa,aa}"
    +
     3387.    480
    +
     3388.    480                    warn("duplicate_a", name);
    +
     3389.    480                }
    +
     3390.    480                seen[id] = true;
    +
     3391.    487            }
    +
     3392.    487            if (name.identifier) {
    +
     3393.    479                if (next_token.id === "}" || next_token.id === ",") {
    +
     3394.      0                    if (typeof extra === "string") {
    +
     3395.      0                        advance("(");
    +
     3396.      0                    }
    +
     3397.    479                    value = expression(Infinity, true);
    +
     3398.    479                } else if (next_token.id === "(") {
    +
     3399.    479                    value = do_function({
    +
     3400.    479                        arity: "unary",
    +
     3401.    479                        from: name.from,
    +
     3402.    479                        id: "function",
    +
     3403.    479                        line: name.line,
    +
     3404.    479                        name: (
    +
     3405.    479                            typeof extra === "string"
    +
     3406.    479                            ? extra
    +
     3407.      0                            : id
    +
     3408.    479                        ),
    +
     3409.    479                        thru: name.from
    +
     3410.    479                    });
    +
     3411.    479                } else {
    +
     3412.      0                    if (typeof extra === "string") {
    +
     3413.      0                        advance("(");
    +
     3414.      0                    }
    +
     3415.    479                    let the_colon = next_token;
    +
     3416.    479                    advance(":");
    +
     3417.    479                    value = expression(0);
    +
     3418.    479                    if (value.id === name.id && value.id !== "function") {
    +
     3419.    479                        warn("unexpected_a", the_colon, ": " + name.id);
    +
     3420.    479                    }
    +
     3421.    479                }
    +
     3422.    479                value.label = name;
    +
     3423.    479                if (typeof extra === "string") {
    +
     3424.    479                    value.extra = extra;
    +
     3425.    479                }
    +
     3426.    479                the_brace.expression.push(value);
    +
     3427.    479            } else {
    +
     3428.     78                advance(":");
    +
     3429.     78                value = expression(0);
    +
     3430.     78                value.label = name;
    +
     3431.     78                the_brace.expression.push(value);
    +
     3432.    487            }
    +
     3433.    487            if (next_token.id === ",") {
    +
     3434.    410                advance(",");
    +
     3435.    410                return member();
    +
     3436.    410            }
    +
     3437.    488        }());
    +
     3438.     83    }
    +
     3439.     83    advance("}");
    +
     3440.     83    return the_brace;
    +
     3441.     83});
    +
     3442.      1
    +
     3443.      2stmt(";", function () {
    +
     3444.      2    warn("unexpected_a", token);
    +
     3445.      2    return token;
    +
     3446.      2});
    +
     3447.     10stmt("{", function () {
    +
     3448.     10
    +
     3449.     10// cause: "class aa{}"
    +
     3450.     10
    +
     3451.     10    warn("naked_block", token);
    +
     3452.     10    return block("naked");
    +
     3453.     10});
    +
     3454.      1stmt("async", do_async);
    +
     3455.     28stmt("break", function () {
    +
     3456.     28    const the_break = token;
    +
     3457.     28    let the_label;
    +
     3458.     28    if (
    +
     3459.     15        (functionage.loop < 1 && functionage.switch < 1)
    +
     3460.     25        || functionage.finally > 0
    +
     3461.      3    ) {
    +
     3462.      3        warn("unexpected_a", the_break);
    +
     3463.      3    }
    +
     3464.     28    the_break.disrupt = true;
    +
     3465.      5    if (next_token.identifier && token.line === next_token.line) {
    +
     3466.      5        the_label = functionage.context[next_token.id];
    +
     3467.      5        if (
    +
     3468.      5            the_label === undefined
    +
     3469.      5            || the_label.role !== "label"
    +
     3470.      5            || the_label.dead
    +
     3471.      5        ) {
    +
     3472.      5            if (the_label !== undefined && the_label.dead) {
    +
     3473.      5
    +
     3474.      5// cause: "aa:{function aa(aa){break aa;}}"
    +
     3475.      5
    +
     3476.      5                warn("out_of_scope_a");
    +
     3477.      5            } else {
    +
     3478.      5
    +
     3479.      5// cause: "aa:{break aa;}"
    +
     3480.      5
    +
     3481.      5                warn("not_label_a");
    +
     3482.      5            }
    +
     3483.      5        } else {
    +
     3484.      5            the_label.used += 1;
    +
     3485.      5        }
    +
     3486.      5        the_break.label = next_token;
    +
     3487.      5        advance();
    +
     3488.      5    }
    +
     3489.     28    advance(";");
    +
     3490.     28    return the_break;
    +
     3491.     28});
    +
     3492.      1
    +
     3493.    730function do_var() {
    +
     3494.    730    const the_statement = token;
    +
     3495.    730    const is_const = the_statement.id === "const";
    +
     3496.    730    the_statement.names = [];
    +
     3497.    730
    +
     3498.    730// A program may use var or let, but not both.
    +
     3499.    730
    +
     3500.    402    if (!is_const) {
    +
     3501.    402        if (var_mode === undefined) {
    +
     3502.    402            var_mode = the_statement.id;
    +
     3503.    402        } else if (the_statement.id !== var_mode) {
    +
     3504.    402            warn(
    +
     3505.    402                "expected_a_b",
    +
     3506.    402                the_statement,
    +
     3507.    402                var_mode,
    +
     3508.    402                the_statement.id
    +
     3509.    402            );
    +
     3510.    402        }
    +
     3511.    402    }
    +
     3512.    730
    +
     3513.    730// We don't expect to see variables created in switch statements.
    +
     3514.    730
    +
     3515.      1    if (functionage.switch > 0) {
    +
     3516.      1
    +
     3517.      1// cause: "switch(0){case 0:var aa}"
    +
     3518.      1
    +
     3519.      1        warn("var_switch", the_statement);
    +
     3520.      1    }
    +
     3521.      1    if (functionage.loop > 0 && the_statement.id === "var") {
    +
     3522.      1
    +
     3523.      1// cause: "while(0){var aa;}"
    +
     3524.      1
    +
     3525.      1        warn("var_loop", the_statement);
    +
     3526.      1    }
    +
     3527.    730    (function next() {
    +
     3528.      5        if (next_token.id === "{" && the_statement.id !== "var") {
    +
     3529.      5            let a;
    +
     3530.      5            let b = "";
    +
     3531.      5            const the_brace = next_token;
    +
     3532.      5            advance("{");
    +
     3533.      7            (function pair() {
    +
     3534.      5                if (!next_token.identifier) {
    +
     3535.      5                    return stop("expected_identifier_a", next_token);
    +
     3536.      6                }
    +
     3537.      6                const name = next_token;
    +
     3538.      6                survey(name);
    +
     3539.      6                a = b;
    +
     3540.      6                b = String(name.value || name.id);
    +
     3541.      5                if (a > b) {
    +
     3542.      5                    if (!option.unordered) {
    +
     3543.      5
    +
     3544.      5// cause: "let{bb,aa}=0"
    +
     3545.      5
    +
     3546.      5                        warn("unordered_param_a", name);
    +
     3547.      5                    }
    +
     3548.      6                }
    +
     3549.      6                advance();
    +
     3550.      6                if (next_token.id === ":") {
    +
     3551.      5                    advance(":");
    +
     3552.      5                    if (!next_token.identifier) {
    +
     3553.      5                        return stop("expected_identifier_a", next_token);
    +
     3554.      5                    }
    +
     3555.      5                    next_token.label = name;
    +
     3556.      5                    the_statement.names.push(next_token);
    +
     3557.      5                    enroll(next_token, "variable", is_const);
    +
     3558.      5                    advance();
    +
     3559.      5                    the_brace.open = true;
    +
     3560.      5                } else {
    +
     3561.      5                    the_statement.names.push(name);
    +
     3562.      5                    enroll(name, "variable", is_const);
    +
     3563.      5                }
    +
     3564.      5                name.dead = false;
    +
     3565.      5                name.init = true;
    +
     3566.      0                if (next_token.id === "=") {
    +
     3567.      0                    advance("=");
    +
     3568.      0                    name.expression = expression();
    +
     3569.      0                    the_brace.open = true;
    +
     3570.      0                }
    +
     3571.      5                if (next_token.id === ",") {
    +
     3572.      5                    advance(",");
    +
     3573.      5                    return pair();
    +
     3574.      5                }
    +
     3575.      7            }());
    +
     3576.      5            advance("}");
    +
     3577.      5            advance("=");
    +
     3578.      5            the_statement.expression = expression(0);
    +
     3579.    725        } else if (next_token.id === "[" && the_statement.id !== "var") {
    +
     3580.    725            const the_bracket = next_token;
    +
     3581.    725            advance("[");
    +
     3582.    725            (function element() {
    +
     3583.    725                let ellipsis;
    +
     3584.    725                if (next_token.id === "...") {
    +
     3585.    725                    ellipsis = true;
    +
     3586.    725                    advance("...");
    +
     3587.    725                }
    +
     3588.      0                if (!next_token.identifier) {
    +
     3589.      0                    return stop("expected_identifier_a", next_token);
    +
     3590.      0                }
    +
     3591.    725                const name = next_token;
    +
     3592.    725                advance();
    +
     3593.    725                the_statement.names.push(name);
    +
     3594.    725                enroll(name, "variable", is_const);
    +
     3595.    725                name.dead = false;
    +
     3596.    725                name.init = true;
    +
     3597.    725                if (ellipsis) {
    +
     3598.    725                    name.ellipsis = true;
    +
     3599.    725                } else {
    +
     3600.    725                    if (next_token.id === "=") {
    +
     3601.    725                        advance("=");
    +
     3602.    725                        name.expression = expression();
    +
     3603.    725                        the_bracket.open = true;
    +
     3604.    725                    }
    +
     3605.    725                    if (next_token.id === ",") {
    +
     3606.    725                        advance(",");
    +
     3607.    725                        return element();
    +
     3608.    725                    }
    +
     3609.    725                }
    +
     3610.    725            }());
    +
     3611.    725            advance("]");
    +
     3612.    725            advance("=");
    +
     3613.    725            the_statement.expression = expression(0);
    +
     3614.    725        } else if (next_token.identifier) {
    +
     3615.    725            const name = next_token;
    +
     3616.    725            advance();
    +
     3617.    725            if (name.id === "ignore") {
    +
     3618.    725
    +
     3619.    725// cause: "let ignore;function aa(ignore) {}"
    +
     3620.    725
    +
     3621.    725                warn("unexpected_a", name);
    +
     3622.    725            }
    +
     3623.    725            enroll(name, "variable", is_const);
    +
     3624.    725            if (next_token.id === "=" || is_const) {
    +
     3625.    725                advance("=");
    +
     3626.    725                name.dead = false;
    +
     3627.    725                name.init = true;
    +
     3628.    725                name.expression = expression(0);
    +
     3629.    725            }
    +
     3630.    725            the_statement.names.push(name);
    +
     3631.      0        } else {
    +
     3632.      0            return stop("expected_identifier_a", next_token);
    +
     3633.      0        }
    +
     3634.    730    }());
    +
     3635.    730    semicolon();
    +
     3636.    730    return the_statement;
    +
     3637.    730}
    +
     3638.      1
    +
     3639.      1stmt("const", do_var);
    +
     3640.      2stmt("continue", function () {
    +
     3641.      2    const the_continue = token;
    +
     3642.      1    if (functionage.loop < 1 || functionage.finally > 0) {
    +
     3643.      1        warn("unexpected_a", the_continue);
    +
     3644.      1    }
    +
     3645.      2    not_top_level(the_continue);
    +
     3646.      2    the_continue.disrupt = true;
    +
     3647.      2    warn("unexpected_a", the_continue);
    +
     3648.      2    advance(";");
    +
     3649.      2    return the_continue;
    +
     3650.      2});
    +
     3651.      2stmt("debugger", function () {
    +
     3652.      2    const the_debug = token;
    +
     3653.      1    if (!option.devel) {
    +
     3654.      1        warn("unexpected_a", the_debug);
    +
     3655.      1    }
    +
     3656.      2    semicolon();
    +
     3657.      2    return the_debug;
    +
     3658.      2});
    +
     3659.     25stmt("delete", function () {
    +
     3660.     25    const the_token = token;
    +
     3661.     25    const the_value = expression(0);
    +
     3662.     25    if (
    +
     3663.      1        (the_value.id !== "." && the_value.id !== "[")
    +
     3664.     25        || the_value.arity !== "binary"
    +
     3665.      1    ) {
    +
     3666.      1        stop("expected_a_b", the_value, ".", artifact(the_value));
    +
     3667.     24    }
    +
     3668.     24    the_token.expression = the_value;
    +
     3669.     24    semicolon();
    +
     3670.     24    return the_token;
    +
     3671.     24});
    +
     3672.      4stmt("do", function () {
    +
     3673.      4    const the_do = token;
    +
     3674.      4    not_top_level(the_do);
    +
     3675.      4    functionage.loop += 1;
    +
     3676.      4    the_do.block = block();
    +
     3677.      4    advance("while");
    +
     3678.      4    the_do.expression = condition();
    +
     3679.      4    semicolon();
    +
     3680.      1    if (the_do.block.disrupt === true) {
    +
     3681.      1
    +
     3682.      1// cause: "function aa(){do{break;}while(0)}"
    +
     3683.      1
    +
     3684.      1        warn("weird_loop", the_do);
    +
     3685.      3    }
    +
     3686.      3    functionage.loop -= 1;
    +
     3687.      3    return the_do;
    +
     3688.      3});
    +
     3689.     12stmt("export", function () {
    +
     3690.     12    const the_export = token;
    +
     3691.     12    let the_id;
    +
     3692.     12    let the_name;
    +
     3693.     12    let the_thing;
    +
     3694.     12
    +
     3695.      3    function export_id() {
    +
     3696.      0        if (!next_token.identifier) {
    +
     3697.      0            stop("expected_identifier_a");
    +
     3698.      0        }
    +
     3699.      3        the_id = next_token.id;
    +
     3700.      3        the_name = global.context[the_id];
    +
     3701.      0        if (the_name === undefined) {
    +
     3702.      0            warn("unexpected_a");
    +
     3703.      0        } else {
    +
     3704.      3            the_name.used += 1;
    +
     3705.      1            if (exports[the_id] !== undefined) {
    +
     3706.      1
    +
     3707.      1// cause: "let aa;export{aa,aa}"
    +
     3708.      1
    +
     3709.      1                warn("duplicate_a");
    +
     3710.      1            }
    +
     3711.      3            exports[the_id] = the_name;
    +
     3712.      3        }
    +
     3713.      3        advance();
    +
     3714.      3        the_export.expression.push(the_thing);
    +
     3715.      3    }
    +
     3716.     12
    +
     3717.     12    the_export.expression = [];
    +
     3718.      6    if (next_token.id === "default") {
    +
     3719.      6        if (exports.default !== undefined) {
    +
     3720.      6
    +
     3721.      6// cause: "export default 0;export default 0"
    +
     3722.      6
    +
     3723.      6            warn("duplicate_a");
    +
     3724.      6        }
    +
     3725.      6        advance("default");
    +
     3726.      6        the_thing = expression(0);
    +
     3727.      6        if (
    +
     3728.      6            the_thing.id !== "("
    +
     3729.      6            || the_thing.expression[0].id !== "."
    +
     3730.      6            || the_thing.expression[0].expression.id !== "Object"
    +
     3731.      6            || the_thing.expression[0].name.id !== "freeze"
    +
     3732.      6        ) {
    +
     3733.      6
    +
     3734.      6// cause: "export default {}"
    +
     3735.      6
    +
     3736.      6            warn("freeze_exports", the_thing);
    +
     3737.      6        }
    +
     3738.      6        if (next_token.id === ";") {
    +
     3739.      6            semicolon();
    +
     3740.      6        }
    +
     3741.      6        exports.default = the_thing;
    +
     3742.      6        the_export.expression.push(the_thing);
    +
     3743.      6    } else {
    +
     3744.      6        if (next_token.id === "function") {
    +
     3745.      6
    +
     3746.      6// cause: "export function aa(){}"
    +
     3747.      6
    +
     3748.      6            warn("freeze_exports");
    +
     3749.      6            the_thing = statement();
    +
     3750.      6            the_name = the_thing.name;
    +
     3751.      6            the_id = the_name.id;
    +
     3752.      6
    +
     3753.      6// cause: "let aa;export{aa};export function aa(){}"
    +
     3754.      6
    +
     3755.      6            the_name.used += 1;
    +
     3756.      6            if (exports[the_id] !== undefined) {
    +
     3757.      6                warn("duplicate_a", the_name);
    +
     3758.      6            }
    +
     3759.      6            exports[the_id] = the_thing;
    +
     3760.      6            the_export.expression.push(the_thing);
    +
     3761.      6            the_thing.statement = false;
    +
     3762.      6            the_thing.arity = "unary";
    +
     3763.      6        } else if (
    +
     3764.      6            next_token.id === "var"
    +
     3765.      6            || next_token.id === "let"
    +
     3766.      6            || next_token.id === "const"
    +
     3767.      6        ) {
    +
     3768.      6            warn("unexpected_a", next_token);
    +
     3769.      6            statement();
    +
     3770.      6        } else if (next_token.id === "{") {
    +
     3771.      6            advance("{");
    +
     3772.      6            (function loop() {
    +
     3773.      6                export_id();
    +
     3774.      6                if (next_token.id === ",") {
    +
     3775.      6                    advance(",");
    +
     3776.      6                    return loop();
    +
     3777.      6                }
    +
     3778.      6            }());
    +
     3779.      6            advance("}");
    +
     3780.      6            semicolon();
    +
     3781.      6        } else {
    +
     3782.      6            stop("unexpected_a");
    +
     3783.      6        }
    +
     3784.     11    }
    +
     3785.     11    module_mode = true;
    +
     3786.     11    return the_export;
    +
     3787.     11});
    +
     3788.     11stmt("for", function () {
    +
     3789.     11    let first;
    +
     3790.     11    const the_for = token;
    +
     3791.     11    if (!option.for) {
    +
     3792.     11        warn("unexpected_a", the_for);
    +
     3793.     11    }
    +
     3794.     11    not_top_level(the_for);
    +
     3795.     11    functionage.loop += 1;
    +
     3796.     11    advance("(");
    +
     3797.     11
    +
     3798.     11// cause: "for(){}"
    +
     3799.     11
    +
     3800.     11    token.free = true;
    +
     3801.      2    if (next_token.id === ";") {
    +
     3802.      2
    +
     3803.      2// cause: "for(;;){}"
    +
     3804.      2
    +
     3805.      2        return stop("expected_a_b", the_for, "while (", "for (;");
    +
     3806.      9    }
    +
     3807.      9    if (
    +
     3808.      9        next_token.id === "var"
    +
     3809.      9        || next_token.id === "let"
    +
     3810.      9        || next_token.id === "const"
    +
     3811.      1    ) {
    +
     3812.      1        return stop("unexpected_a");
    +
     3813.      8    }
    +
     3814.      8    first = expression(0);
    +
     3815.      8    if (first.id === "in") {
    +
     3816.      3        if (first.expression[0].arity !== "variable") {
    +
     3817.      3
    +
     3818.      3// cause: "for(0 in aa){}"
    +
     3819.      3
    +
     3820.      3            warn("bad_assignment_a", first.expression[0]);
    +
     3821.      3        }
    +
     3822.      3        the_for.name = first.expression[0];
    +
     3823.      3        the_for.expression = first.expression[1];
    +
     3824.      3        warn("expected_a_b", the_for, "Object.keys", "for in");
    +
     3825.      4    } else {
    +
     3826.      4        the_for.initial = first;
    +
     3827.      4        advance(";");
    +
     3828.      4        the_for.expression = expression(0);
    +
     3829.      4        advance(";");
    +
     3830.      4        the_for.inc = expression(0);
    +
     3831.      4        if (the_for.inc.id === "++") {
    +
     3832.      4
    +
     3833.      4// cause: "for(aa;aa;aa++){}"
    +
     3834.      4
    +
     3835.      4            warn("expected_a_b", the_for.inc, "+= 1", "++");
    +
     3836.      4        }
    +
     3837.      7    }
    +
     3838.      7    advance(")");
    +
     3839.      7    the_for.block = block();
    +
     3840.      7    if (the_for.block.disrupt === true) {
    +
     3841.      1        warn("weird_loop", the_for);
    +
     3842.      7    }
    +
     3843.      7    functionage.loop -= 1;
    +
     3844.      7    return the_for;
    +
     3845.      7});
    +
     3846.      1stmt("function", do_function);
    +
     3847.   1192stmt("if", function () {
    +
     3848.   1192    let the_else;
    +
     3849.   1192    const the_if = token;
    +
     3850.   1192    the_if.expression = condition();
    +
     3851.   1192    the_if.block = block();
    +
     3852.    288    if (next_token.id === "else") {
    +
     3853.    288        advance("else");
    +
     3854.    288        the_else = token;
    +
     3855.    288        the_if.else = (
    +
     3856.    288            next_token.id === "if"
    +
     3857.    288            ? statement()
    +
     3858.    288            : block()
    +
     3859.    288        );
    +
     3860.    288        if (the_if.block.disrupt === true) {
    +
     3861.    288            if (the_if.else.disrupt === true) {
    +
     3862.    288                the_if.disrupt = true;
    +
     3863.      0            } else {
    +
     3864.      0                warn("unexpected_a", the_else);
    +
     3865.      0            }
    +
     3866.    288        }
    +
     3867.   1191    }
    +
     3868.   1191    return the_if;
    +
     3869.   1191});
    +
     3870.     14stmt("import", function () {
    +
     3871.     14    const the_import = token;
    +
     3872.      2    if (next_token.id === "(") {
    +
     3873.      2        the_import.arity = "unary";
    +
     3874.      2        the_import.constant = true;
    +
     3875.      2        the_import.statement = false;
    +
     3876.      2        advance("(");
    +
     3877.      2        const string = expression(0);
    +
     3878.      2        if (string.id !== "(string)") {
    +
     3879.      2
    +
     3880.      2// cause: "import(aa)"
    +
     3881.      2
    +
     3882.      2            warn("expected_string_a", string);
    +
     3883.      2        }
    +
     3884.      2        froms.push(token.value);
    +
     3885.      2        advance(")");
    +
     3886.      2        advance(".");
    +
     3887.      2        advance("then");
    +
     3888.      2        advance("(");
    +
     3889.      2        the_import.expression = expression(0);
    +
     3890.      2        advance(")");
    +
     3891.      2        semicolon();
    +
     3892.      2        return the_import;
    +
     3893.     12    }
    +
     3894.     12    let name;
    +
     3895.     12    if (typeof module_mode === "object") {
    +
     3896.      1
    +
     3897.      1// cause: "/*global aa*/\nimport aa from \"aa\""
    +
     3898.      1
    +
     3899.      1        warn("unexpected_directive_a", module_mode, module_mode.directive);
    +
     3900.     12    }
    +
     3901.     12    module_mode = true;
    +
     3902.     12    if (next_token.identifier) {
    +
     3903.      8        name = next_token;
    +
     3904.      8        advance();
    +
     3905.      8        if (name.id === "ignore") {
    +
     3906.      8            warn("unexpected_a", name);
    +
     3907.      8        }
    +
     3908.      8        enroll(name, "variable", true);
    +
     3909.      8        the_import.name = name;
    +
     3910.      8    } else {
    +
     3911.      4        const names = [];
    +
     3912.      4        advance("{");
    +
     3913.      4        if (next_token.id !== "}") {
    +
     3914.      4            while (true) {
    +
     3915.      4                if (!next_token.identifier) {
    +
     3916.      4                    stop("expected_identifier_a");
    +
     3917.      4                }
    +
     3918.      4                name = next_token;
    +
     3919.      4                advance();
    +
     3920.      4                if (name.id === "ignore") {
    +
     3921.      4                    warn("unexpected_a", name);
    +
     3922.      4                }
    +
     3923.      4                enroll(name, "variable", true);
    +
     3924.      4                names.push(name);
    +
     3925.      4                if (next_token.id !== ",") {
    +
     3926.      4                    break;
    +
     3927.      4                }
    +
     3928.      4                advance(",");
    +
     3929.      4            }
    +
     3930.      4        }
    +
     3931.      4        advance("}");
    +
     3932.      4        the_import.name = names;
    +
     3933.     11    }
    +
     3934.     11    advance("from");
    +
     3935.     11    advance("(string)");
    +
     3936.     11    the_import.import = token;
    +
     3937.     11    if (!rx_module.test(token.value)) {
    +
     3938.      1
    +
     3939.      1// cause: "import aa from \"!aa\""
    +
     3940.      1
    +
     3941.      1        warn("bad_module_name_a", token);
    +
     3942.     11    }
    +
     3943.     11    froms.push(token.value);
    +
     3944.     11    semicolon();
    +
     3945.     11    return the_import;
    +
     3946.     11});
    +
     3947.      1stmt("let", do_var);
    +
     3948.    527stmt("return", function () {
    +
     3949.    527    const the_return = token;
    +
     3950.    527    not_top_level(the_return);
    +
     3951.      1    if (functionage.finally > 0) {
    +
     3952.      1        warn("unexpected_a", the_return);
    +
     3953.      1    }
    +
     3954.    527    the_return.disrupt = true;
    +
     3955.    487    if (next_token.id !== ";" && the_return.line === next_token.line) {
    +
     3956.    487        the_return.expression = expression(10);
    +
     3957.    487    }
    +
     3958.    527    advance(";");
    +
     3959.    527    return the_return;
    +
     3960.    527});
    +
     3961.     12stmt("switch", function () {
    +
     3962.     12    let dups = [];
    +
     3963.     12    let last;
    +
     3964.     12    let stmts;
    +
     3965.     12    const the_cases = [];
    +
     3966.     12    let the_disrupt = true;
    +
     3967.     12    const the_switch = token;
    +
     3968.     12    not_top_level(the_switch);
    +
     3969.      1    if (functionage.finally > 0) {
    +
     3970.      1        warn("unexpected_a", the_switch);
    +
     3971.      1    }
    +
     3972.     12    functionage.switch += 1;
    +
     3973.     12    advance("(");
    +
     3974.     12
    +
     3975.     12// cause: "switch(){}"
    +
     3976.     12
    +
     3977.     12    token.free = true;
    +
     3978.     12    the_switch.expression = expression(0);
    +
     3979.     12    the_switch.block = the_cases;
    +
     3980.     12    advance(")");
    +
     3981.     12    advance("{");
    +
     3982.     18    (function major() {
    +
     3983.     18        const the_case = next_token;
    +
     3984.     18        the_case.arity = "statement";
    +
     3985.     18        the_case.expression = [];
    +
     3986.     30        (function minor() {
    +
     3987.     30            advance("case");
    +
     3988.     30            token.switch = true;
    +
     3989.     30            const exp = expression(0);
    +
     3990.     44            if (dups.some(function (thing) {
    +
     3991.     44                return are_similar(thing, exp);
    +
     3992.     44            })) {
    +
     3993.      1                warn("unexpected_a", exp);
    +
     3994.      1            }
    +
     3995.     30            dups.push(exp);
    +
     3996.     30            the_case.expression.push(exp);
    +
     3997.     30            advance(":");
    +
     3998.     12            if (next_token.id === "case") {
    +
     3999.     12                return minor();
    +
     4000.     12            }
    +
     4001.     30        }());
    +
     4002.     18        stmts = statements();
    +
     4003.      1        if (stmts.length < 1) {
    +
     4004.      1            warn("expected_statements_a");
    +
     4005.      1            return;
    +
     4006.     16        }
    +
     4007.     16        the_case.block = stmts;
    +
     4008.     16        the_cases.push(the_case);
    +
     4009.     16        last = stmts[stmts.length - 1];
    +
     4010.     16        if (last.disrupt) {
    +
     4011.     15            if (last.id === "break" && last.label === undefined) {
    +
     4012.     15                the_disrupt = false;
    +
     4013.     15            }
    +
     4014.     15        } else {
    +
     4015.      1            warn(
    +
     4016.      1                "expected_a_before_b",
    +
     4017.      1                next_token,
    +
     4018.      1                "break;",
    +
     4019.      1                artifact(next_token)
    +
     4020.      1            );
    +
     4021.     16        }
    +
     4022.     16        if (next_token.id === "case") {
    +
     4023.      7            return major();
    +
     4024.      7        }
    +
     4025.     18    }());
    +
     4026.     12    dups = undefined;
    +
     4027.      7    if (next_token.id === "default") {
    +
     4028.      7        const the_default = next_token;
    +
     4029.      7        advance("default");
    +
     4030.      7        token.switch = true;
    +
     4031.      7        advance(":");
    +
     4032.      7        the_switch.else = statements();
    +
     4033.      7        if (the_switch.else.length < 1) {
    +
     4034.      7            warn("unexpected_a", the_default);
    +
     4035.      7            the_disrupt = false;
    +
     4036.      7        } else {
    +
     4037.      7            const the_last = the_switch.else[the_switch.else.length - 1];
    +
     4038.      7            if (the_last.id === "break" && the_last.label === undefined) {
    +
     4039.      7                warn("unexpected_a", the_last);
    +
     4040.      7                the_last.disrupt = false;
    +
     4041.      7            }
    +
     4042.      7            the_disrupt = the_disrupt && the_last.disrupt;
    +
     4043.      7        }
    +
     4044.      7    } else {
    +
     4045.      3        the_disrupt = false;
    +
     4046.     10    }
    +
     4047.     10    advance("}", the_switch);
    +
     4048.     10    functionage.switch -= 1;
    +
     4049.     10    the_switch.disrupt = the_disrupt;
    +
     4050.     10    return the_switch;
    +
     4051.     10});
    +
     4052.     11stmt("throw", function () {
    +
     4053.     11    const the_throw = token;
    +
     4054.     11    the_throw.disrupt = true;
    +
     4055.     11    the_throw.expression = expression(10);
    +
     4056.     11    semicolon();
    +
     4057.      1    if (functionage.try > 0) {
    +
     4058.      1        warn("unexpected_a", the_throw);
    +
     4059.      1    }
    +
     4060.     11    return the_throw;
    +
     4061.     11});
    +
     4062.     13stmt("try", function () {
    +
     4063.     13    let the_catch;
    +
     4064.     13    let the_disrupt;
    +
     4065.     13    const the_try = token;
    +
     4066.      1    if (functionage.try > 0) {
    +
     4067.      1        warn("unexpected_a", the_try);
    +
     4068.      1    }
    +
     4069.     13    functionage.try += 1;
    +
     4070.     13    the_try.block = block();
    +
     4071.     13    the_disrupt = the_try.block.disrupt;
    +
     4072.     11    if (next_token.id === "catch") {
    +
     4073.     11        let ignored = "ignore";
    +
     4074.     11        the_catch = next_token;
    +
     4075.     11        the_try.catch = the_catch;
    +
     4076.     11        advance("catch");
    +
     4077.     11        if (next_token.id === "(") {
    +
     4078.     11            advance("(");
    +
     4079.     11            if (!next_token.identifier) {
    +
     4080.     11                return stop("expected_identifier_a", next_token);
    +
     4081.     11            }
    +
     4082.     11            if (next_token.id !== "ignore") {
    +
     4083.     11                ignored = undefined;
    +
     4084.     11                the_catch.name = next_token;
    +
     4085.     11                enroll(next_token, "exception", true);
    +
     4086.     11            }
    +
     4087.     11            advance();
    +
     4088.     11            advance(")");
    +
     4089.     11        }
    +
     4090.     11        the_catch.block = block(ignored);
    +
     4091.     11        if (the_catch.block.disrupt !== true) {
    +
     4092.     11            the_disrupt = false;
    +
     4093.     11        }
    +
     4094.     11    } else {
    +
     4095.      1        warn(
    +
     4096.      1            "expected_a_before_b",
    +
     4097.      1            next_token,
    +
     4098.      1            "catch",
    +
     4099.      1            artifact(next_token)
    +
     4100.      1        );
    +
     4101.      1
    +
     4102.     11    }
    +
     4103.     11    if (next_token.id === "finally") {
    +
     4104.      4        functionage.finally += 1;
    +
     4105.      4        advance("finally");
    +
     4106.      4        the_try.else = block();
    +
     4107.      4        the_disrupt = the_try.else.disrupt;
    +
     4108.      4        functionage.finally -= 1;
    +
     4109.     11    }
    +
     4110.     11    the_try.disrupt = the_disrupt;
    +
     4111.     11    functionage.try -= 1;
    +
     4112.     11    return the_try;
    +
     4113.     11});
    +
     4114.      1stmt("var", do_var);
    +
     4115.     26stmt("while", function () {
    +
     4116.     26    const the_while = token;
    +
     4117.     26    not_top_level(the_while);
    +
     4118.     26    functionage.loop += 1;
    +
     4119.     26    the_while.expression = condition();
    +
     4120.     26    the_while.block = block();
    +
     4121.      3    if (the_while.block.disrupt === true) {
    +
     4122.      3
    +
     4123.      3// cause: "function aa(){while(0){break;}}"
    +
     4124.      3
    +
     4125.      3        warn("weird_loop", the_while);
    +
     4126.     25    }
    +
     4127.     25    functionage.loop -= 1;
    +
     4128.     25    return the_while;
    +
     4129.     25});
    +
     4130.      1stmt("with", function () {
    +
     4131.      1
    +
     4132.      1// cause: "with"
    +
     4133.      1
    +
     4134.      1    stop("unexpected_a", token);
    +
     4135.      1});
    +
     4136.      1
    +
     4137.      1ternary("?", ":");
    +
     4138.      1
    +
     4139.      1// Ambulation of the parse tree.
    +
     4140.      1
    +
     4141.      2function action(when) {
    +
     4142.      2
    +
     4143.      2// Produce a function that will register task functions that will be called as
    +
     4144.      2// the tree is traversed.
    +
     4145.      2
    +
     4146.     37    return function (arity, id, task) {
    +
     4147.     37        let a_set = when[arity];
    +
     4148.     37        let i_set;
    +
     4149.     37
    +
     4150.     37// The id parameter is optional. If excluded, the task will be applied to all
    +
     4151.     37// ids.
    +
     4152.     37
    +
     4153.      8        if (typeof id !== "string") {
    +
     4154.      8            task = id;
    +
     4155.      8            id = "(all)";
    +
     4156.      8        }
    +
     4157.     37
    +
     4158.     37// If this arity has no registrations yet, then create a set object to hold
    +
     4159.     37// them.
    +
     4160.     37
    +
     4161.     10        if (a_set === undefined) {
    +
     4162.     10            a_set = empty();
    +
     4163.     10            when[arity] = a_set;
    +
     4164.     10        }
    +
     4165.     37
    +
     4166.     37// If this id has no registrations yet, then create a set array to hold them.
    +
     4167.     37
    +
     4168.     37        i_set = a_set[id];
    +
     4169.     36        if (i_set === undefined) {
    +
     4170.     36            i_set = [];
    +
     4171.     36            a_set[id] = i_set;
    +
     4172.     36        }
    +
     4173.     37
    +
     4174.     37// Register the task with the arity and the id.
    +
     4175.     37
    +
     4176.     37        i_set.push(task);
    +
     4177.     37    };
    +
     4178.      2}
    +
     4179.      1
    +
     4180.      2function amble(when) {
    +
     4181.      2
    +
     4182.      2// Produce a function that will act on the tasks registered by an action
    +
     4183.      2// function while walking the tree.
    +
     4184.      2
    +
     4185.  60196    return function (the_token) {
    +
     4186.  60196
    +
     4187.  60196// Given a task set that was built by an action function, run all of the
    +
     4188.  60196// relevant tasks on the token.
    +
     4189.  60196
    +
     4190.  60196        let a_set = when[the_token.arity];
    +
     4191.  60196        let i_set;
    +
     4192.  60196
    +
     4193.  60196// If there are tasks associated with the token's arity...
    +
     4194.  60196
    +
     4195.  40840        if (a_set !== undefined) {
    +
     4196.  40840
    +
     4197.  40840// If there are tasks associated with the token's id...
    +
     4198.  40840
    +
     4199.  40840            i_set = a_set[the_token.id];
    +
     4200.  40840            if (i_set !== undefined) {
    +
     4201.  40840                i_set.forEach(function (task) {
    +
     4202.  40840                    return task(the_token);
    +
     4203.  40840                });
    +
     4204.  40840            }
    +
     4205.  40840
    +
     4206.  40840// If there are tasks for all ids.
    +
     4207.  40840
    +
     4208.  40840            i_set = a_set["(all)"];
    +
     4209.  40840            if (i_set !== undefined) {
    +
     4210.  40840                i_set.forEach(function (task) {
    +
     4211.  40840                    return task(the_token);
    +
     4212.  40840                });
    +
     4213.  40840            }
    +
     4214.  40840        }
    +
     4215.  60196    };
    +
     4216.      2}
    +
     4217.      1
    +
     4218.      1const posts = empty();
    +
     4219.      1const pres = empty();
    +
     4220.      1const preaction = action(pres);
    +
     4221.      1const postaction = action(posts);
    +
     4222.      1const preamble = amble(pres);
    +
     4223.      1const postamble = amble(posts);
    +
     4224.      1
    +
     4225.  46729function walk_expression(thing) {
    +
     4226.  29167    if (thing) {
    +
     4227.  29167        if (Array.isArray(thing)) {
    +
     4228.  29167
    +
     4229.  29167// cause: "0&&0"
    +
     4230.  29167// cause: "(function(){}())"
    +
     4231.  29167
    +
     4232.  29167            thing.forEach(walk_expression);
    +
     4233.  29167        } else {
    +
     4234.  29167            preamble(thing);
    +
     4235.  29167            walk_expression(thing.expression);
    +
     4236.  29167            if (thing.id === "function") {
    +
     4237.  29167
    +
     4238.  29167// cause: "aa=function(){}"
    +
     4239.  29167
    +
     4240.  29167                walk_statement(thing.block);
    +
     4241.  29167            }
    +
     4242.  29167            if (thing.arity === "pre" || thing.arity === "post") {
    +
     4243.  29167
    +
     4244.  29167// cause: "aa=++aa"
    +
     4245.  29167// cause: "aa=--aa"
    +
     4246.  29167
    +
     4247.  29167                warn("unexpected_a", thing);
    +
     4248.  29167            } else if (
    +
     4249.  29167
    +
     4250.  29167// cause: "aa=0"
    +
     4251.  29167
    +
     4252.  29167                thing.arity === "statement"
    +
     4253.  29167                || thing.arity === "assignment"
    +
     4254.      0            ) {
    +
     4255.      0                warn("unexpected_statement_a", thing); // deadcode?
    +
     4256.      0            }
    +
     4257.  29167            postamble(thing);
    +
     4258.  29167        }
    +
     4259.  29167    }
    +
     4260.  46729}
    +
     4261.      1
    +
     4262.  22348function walk_statement(thing) {
    +
     4263.  10239    if (thing) {
    +
     4264.  10239        if (Array.isArray(thing)) {
    +
     4265.  10239
    +
     4266.  10239// cause: "+[]"
    +
     4267.  10239
    +
     4268.  10239            thing.forEach(walk_statement);
    +
     4269.  10239        } else {
    +
     4270.  10239            preamble(thing);
    +
     4271.  10239            walk_expression(thing.expression);
    +
     4272.  10239            if (thing.arity === "binary") {
    +
     4273.  10239                if (thing.id !== "(") {
    +
     4274.  10239
    +
     4275.  10239// cause: "0&&0"
    +
     4276.  10239
    +
     4277.  10239                    warn("unexpected_expression_a", thing);
    +
     4278.  10239                }
    +
     4279.  10239            } else if (
    +
     4280.  10239                thing.arity !== "statement"
    +
     4281.  10239                && thing.arity !== "assignment"
    +
     4282.  10239                && thing.id !== "import"
    +
     4283.  10239                && thing.id !== "await"
    +
     4284.  10239            ) {
    +
     4285.  10239
    +
     4286.  10239// cause: "!0"
    +
     4287.  10239// cause: "+[]"
    +
     4288.  10239// cause: "0"
    +
     4289.  10239
    +
     4290.  10239                warn("unexpected_expression_a", thing);
    +
     4291.  10239            }
    +
     4292.  10239            walk_statement(thing.block);
    +
     4293.  10239            walk_statement(thing.else);
    +
     4294.  10239            postamble(thing);
    +
     4295.  10239        }
    +
     4296.  10239    }
    +
     4297.  22348}
    +
     4298.      1
    +
     4299.   8718function lookup(thing) {
    +
     4300.   8717    if (thing.arity === "variable") {
    +
     4301.   8717
    +
     4302.   8717// Look up the variable in the current context.
    +
     4303.   8717
    +
     4304.   8717        let the_variable = functionage.context[thing.id];
    +
     4305.   8717
    +
     4306.   8717// If it isn't local, search all the other contexts. If there are name
    +
     4307.   8717// collisions, take the most recent.
    +
     4308.   8717
    +
     4309.   8717        if (the_variable === undefined) {
    +
     4310.   8717            stack.forEach(function (outer) {
    +
     4311.   8717                const a_variable = outer.context[thing.id];
    +
     4312.   8717                if (
    +
     4313.   8717                    a_variable !== undefined
    +
     4314.   8717                    && a_variable.role !== "label"
    +
     4315.   8717                ) {
    +
     4316.   8717                    the_variable = a_variable;
    +
     4317.   8717                }
    +
     4318.   8717            });
    +
     4319.   8717
    +
     4320.   8717// If it isn't in any of those either, perhaps it is a predefined global.
    +
     4321.   8717// If so, add it to the global context.
    +
     4322.   8717
    +
     4323.   8717            if (the_variable === undefined) {
    +
     4324.   8717                if (declared_globals[thing.id] === undefined) {
    +
     4325.   8717
    +
     4326.   8717// cause: "aa"
    +
     4327.   8717// cause: "class aa{}"
    +
     4328.   8717
    +
     4329.   8717                    warn("undeclared_a", thing);
    +
     4330.   8717                    return;
    +
     4331.   8717                }
    +
     4332.   8717                the_variable = {
    +
     4333.   8717                    dead: false,
    +
     4334.   8717                    id: thing.id,
    +
     4335.   8717                    init: true,
    +
     4336.   8717                    parent: global,
    +
     4337.   8717                    role: "variable",
    +
     4338.   8717                    used: 0,
    +
     4339.   8717                    writable: false
    +
     4340.   8717                };
    +
     4341.   8717                global.context[thing.id] = the_variable;
    +
     4342.   8717            }
    +
     4343.   8717            the_variable.closure = true;
    +
     4344.   8717            functionage.context[thing.id] = the_variable;
    +
     4345.   8717        } else if (the_variable.role === "label") {
    +
     4346.   8717
    +
     4347.   8717// cause: "aa:while(0){aa;}"
    +
     4348.   8717
    +
     4349.   8717            warn("label_a", thing);
    +
     4350.   8717        }
    +
     4351.   8717        if (
    +
     4352.   8717            the_variable.dead
    +
     4353.   8717            && (
    +
     4354.   8717                the_variable.calls === undefined
    +
     4355.   8717                || functionage.name === undefined
    +
     4356.   8717                || the_variable.calls[functionage.name.id] === undefined
    +
     4357.   8717            )
    +
     4358.   8717        ) {
    +
     4359.   8717
    +
     4360.   8717// cause: "function aa(){bb();}\nfunction bb(){}"
    +
     4361.   8717
    +
     4362.   8717            warn("out_of_scope_a", thing);
    +
     4363.   8717        }
    +
     4364.   8717        return the_variable;
    +
     4365.   8717    }
    +
     4366.   8718}
    +
     4367.      1
    +
     4368.     70function subactivate(name) {
    +
     4369.     70    name.init = true;
    +
     4370.     70    name.dead = false;
    +
     4371.     70    blockage.live.push(name);
    +
     4372.     70}
    +
     4373.      1
    +
     4374.    591function preaction_function(thing) {
    +
     4375.    591
    +
     4376.    591// cause: "()=>0"
    +
     4377.    591// cause: "(function (){}())"
    +
     4378.    591// cause: "function aa(){}"
    +
     4379.    591
    +
     4380.    238    if (thing.arity === "statement" && blockage.body !== true) {
    +
     4381.      1
    +
     4382.      1// cause: "if(0){function aa(){}\n}"
    +
     4383.      1
    +
     4384.      1        warn("unexpected_a", thing);
    +
     4385.      1    }
    +
     4386.    591    stack.push(functionage);
    +
     4387.    591    block_stack.push(blockage);
    +
     4388.    591    functionage = thing;
    +
     4389.    591    blockage = thing;
    +
     4390.    591    thing.live = [];
    +
     4391.    299    if (typeof thing.name === "object") {
    +
     4392.    299        thing.name.dead = false;
    +
     4393.    299        thing.name.init = true;
    +
     4394.    299    }
    +
     4395.      5    if (thing.extra === "get") {
    +
     4396.      5        if (thing.parameters.length !== 0) {
    +
     4397.      5
    +
     4398.      5// cause: "/*jslint getset*/\naa={get aa(aa){}}"
    +
     4399.      5
    +
     4400.      5            warn("bad_get", thing);
    +
     4401.      5        }
    +
     4402.    586    } else if (thing.extra === "set") {
    +
     4403.    586        if (thing.parameters.length !== 1) {
    +
     4404.    586
    +
     4405.    586// cause: "/*jslint getset*/\naa={set aa(){}}"
    +
     4406.    586
    +
     4407.    586            warn("bad_set", thing);
    +
     4408.    586        }
    +
     4409.    586    }
    +
     4410.    470    thing.parameters.forEach(function (name) {
    +
     4411.    470        walk_expression(name.expression);
    +
     4412.    448        if (name.id === "{" || name.id === "[") {
    +
     4413.     27            name.names.forEach(subactivate);
    +
     4414.    443        } else {
    +
     4415.    443            name.dead = false;
    +
     4416.    443            name.init = true;
    +
     4417.    443        }
    +
     4418.    470    });
    +
     4419.    591}
    +
     4420.      1
    +
     4421.  10579function bitwise_check(thing) {
    +
     4422.  10148    if (!option.bitwise && bitwiseop[thing.id] === true) {
    +
     4423.      1        warn("unexpected_a", thing);
    +
     4424.      1    }
    +
     4425.  10579    if (
    +
     4426.  10579        thing.id !== "("
    +
     4427.   7706        && thing.id !== "&&"
    +
     4428.   7319        && thing.id !== "||"
    +
     4429.   6959        && thing.id !== "="
    +
     4430.   5803        && Array.isArray(thing.expression)
    +
     4431.   2465        && thing.expression.length === 2
    +
     4432.   2441        && (
    +
     4433.   2441            relationop[thing.expression[0].id] === true
    +
     4434.   2441            || relationop[thing.expression[1].id] === true
    +
     4435.   2441        )
    +
     4436.      1    ) {
    +
     4437.      1        warn("unexpected_a", thing);
    +
     4438.      1    }
    +
     4439.  10579}
    +
     4440.      1
    +
     4441.   2584function pop_block() {
    +
     4442.    601    blockage.live.forEach(function (name) {
    +
     4443.    601        name.dead = true;
    +
     4444.    601    });
    +
     4445.   2584    delete blockage.live;
    +
     4446.   2584    blockage = block_stack.pop();
    +
     4447.   2584}
    +
     4448.      1
    +
     4449.    729function activate(name) {
    +
     4450.    729    name.dead = false;
    +
     4451.    474    if (name.expression !== undefined) {
    +
     4452.    474        walk_expression(name.expression);
    +
     4453.      0        if (name.id === "{" || name.id === "[") {
    +
     4454.      0            name.names.forEach(subactivate);
    +
     4455.      0        } else {
    +
     4456.    474            name.init = true;
    +
     4457.    474        }
    +
     4458.    474    }
    +
     4459.    729    blockage.live.push(name);
    +
     4460.    729}
    +
     4461.      1
    +
     4462.    726function action_var(thing) {
    +
     4463.    726    thing.names.forEach(activate);
    +
     4464.    726}
    +
     4465.      1
    +
     4466.      1preaction("assignment", bitwise_check);
    +
     4467.      1preaction("binary", bitwise_check);
    +
     4468.   9305preaction("binary", function (thing) {
    +
     4469.   1555    if (relationop[thing.id] === true) {
    +
     4470.   1555        const left = thing.expression[0];
    +
     4471.   1555        const right = thing.expression[1];
    +
     4472.   1555        if (left.id === "NaN" || right.id === "NaN") {
    +
     4473.   1555
    +
     4474.   1555// cause: "NaN===NaN"
    +
     4475.   1555
    +
     4476.   1555            warn("number_isNaN", thing);
    +
     4477.   1555        } else if (left.id === "typeof") {
    +
     4478.   1555            if (right.id !== "(string)") {
    +
     4479.   1555                if (right.id !== "typeof") {
    +
     4480.   1555
    +
     4481.   1555// cause: "typeof 0===0"
    +
     4482.   1555
    +
     4483.   1555                    warn("expected_string_a", right);
    +
     4484.   1555                }
    +
     4485.   1555            } else {
    +
     4486.   1555                const value = right.value;
    +
     4487.   1555                if (value === "null" || value === "undefined") {
    +
     4488.   1555
    +
     4489.   1555// cause: "typeof aa===\"undefined\""
    +
     4490.   1555
    +
     4491.   1555                    warn("unexpected_typeof_a", right, value);
    +
     4492.   1555                } else if (
    +
     4493.   1555                    value !== "boolean"
    +
     4494.   1555                    && value !== "function"
    +
     4495.   1555                    && value !== "number"
    +
     4496.   1555                    && value !== "object"
    +
     4497.   1555                    && value !== "string"
    +
     4498.   1555                    && value !== "symbol"
    +
     4499.   1555                ) {
    +
     4500.   1555
    +
     4501.   1555// cause: "typeof 0===\"aa\""
    +
     4502.   1555
    +
     4503.   1555                    warn("expected_type_string_a", right, value);
    +
     4504.   1555                }
    +
     4505.   1555            }
    +
     4506.   1555        }
    +
     4507.   1555    }
    +
     4508.   9305});
    +
     4509.      2preaction("binary", "==", function (thing) {
    +
     4510.      2
    +
     4511.      2// cause: "0==0"
    +
     4512.      2
    +
     4513.      2    warn("expected_a_b", thing, "===", "==");
    +
     4514.      2});
    +
     4515.      1preaction("binary", "!=", function (thing) {
    +
     4516.      1
    +
     4517.      1// cause: "0!=0"
    +
     4518.      1
    +
     4519.      1    warn("expected_a_b", thing, "!==", "!=");
    +
     4520.      1});
    +
     4521.      1preaction("binary", "=>", preaction_function);
    +
     4522.    360preaction("binary", "||", function (thing) {
    +
     4523.    720    thing.expression.forEach(function (thang) {
    +
     4524.     65        if (thang.id === "&&" && !thang.wrapped) {
    +
     4525.      1
    +
     4526.      1// cause: "0&&0||0"
    +
     4527.      1
    +
     4528.      1            warn("and", thang);
    +
     4529.      1        }
    +
     4530.    720    });
    +
     4531.    360});
    +
     4532.   2873preaction("binary", "(", function (thing) {
    +
     4533.   2873    const left = thing.expression[0];
    +
     4534.   2873    if (
    +
     4535.   2873        left.identifier
    +
     4536.   2204        && functionage.context[left.id] === undefined
    +
     4537.   1100        && typeof functionage.name === "object"
    +
     4538.    622    ) {
    +
     4539.    622        const parent = functionage.name.parent;
    +
     4540.    622        if (parent) {
    +
     4541.    622            const left_variable = parent.context[left.id];
    +
     4542.    622            if (
    +
     4543.    622                left_variable !== undefined
    +
     4544.    622                && left_variable.dead
    +
     4545.    622                && left_variable.parent === parent
    +
     4546.    622                && left_variable.calls !== undefined
    +
     4547.    622                && left_variable.calls[functionage.name.id] !== undefined
    +
     4548.    622            ) {
    +
     4549.    622                left_variable.dead = false;
    +
     4550.    622            }
    +
     4551.    622        }
    +
     4552.    622    }
    +
     4553.   2873});
    +
     4554.      1preaction("binary", "in", function (thing) {
    +
     4555.      1
    +
     4556.      1// cause: "aa in aa"
    +
     4557.      1
    +
     4558.      1    warn("infix_in", thing);
    +
     4559.      1});
    +
     4560.      1preaction("binary", "instanceof", function (thing) {
    +
     4561.      1
    +
     4562.      1// cause: "0 instanceof 0"
    +
     4563.      1
    +
     4564.      1    warn("unexpected_a", thing);
    +
     4565.      1});
    +
     4566.   1993preaction("statement", "{", function (thing) {
    +
     4567.   1993    block_stack.push(blockage);
    +
     4568.   1993    blockage = thing;
    +
     4569.   1993    thing.live = [];
    +
     4570.   1993});
    +
     4571.      7preaction("statement", "for", function (thing) {
    +
     4572.      3    if (thing.name !== undefined) {
    +
     4573.      3        const the_variable = lookup(thing.name);
    +
     4574.      3        if (the_variable !== undefined) {
    +
     4575.      3            the_variable.init = true;
    +
     4576.      3            if (!the_variable.writable) {
    +
     4577.      3
    +
     4578.      3// cause: "const aa=0;for(aa in aa){}"
    +
     4579.      3
    +
     4580.      3                warn("bad_assignment_a", thing.name);
    +
     4581.      3            }
    +
     4582.      3        }
    +
     4583.      3    }
    +
     4584.      7    walk_statement(thing.initial);
    +
     4585.      7});
    +
     4586.      1preaction("statement", "function", preaction_function);
    +
     4587.      1preaction("unary", "~", bitwise_check);
    +
     4588.      1preaction("unary", "function", preaction_function);
    +
     4589.   8138preaction("variable", function (thing) {
    +
     4590.   8138    const the_variable = lookup(thing);
    +
     4591.   8077    if (the_variable !== undefined) {
    +
     4592.   8077        thing.variable = the_variable;
    +
     4593.   8077        the_variable.used += 1;
    +
     4594.   8077    }
    +
     4595.   8138});
    +
     4596.      1
    +
     4597.    577function init_variable(name) {
    +
     4598.    577    const the_variable = lookup(name);
    +
     4599.    543    if (the_variable !== undefined) {
    +
     4600.    543        if (the_variable.writable) {
    +
     4601.    543            the_variable.init = true;
    +
     4602.    543            return;
    +
     4603.    543        }
    +
     4604.    543    }
    +
     4605.     34    warn("bad_assignment_a", name);
    +
     4606.     34}
    +
     4607.      1
    +
     4608.    101postaction("assignment", "+=", function (thing) {
    +
     4609.    101    let right = thing.expression[1];
    +
     4610.     64    if (right.constant) {
    +
     4611.     64        if (
    +
     4612.     64            right.value === ""
    +
     4613.     64            || (right.id === "(number)" && right.value === "0")
    +
     4614.     64            || right.id === "(boolean)"
    +
     4615.     64            || right.id === "null"
    +
     4616.     64            || right.id === "undefined"
    +
     4617.     64            || Number.isNaN(right.value)
    +
     4618.     64        ) {
    +
     4619.     64            warn("unexpected_a", right);
    +
     4620.     64        }
    +
     4621.     64    }
    +
     4622.    101});
    +
     4623.   1274postaction("assignment", function (thing) {
    +
     4624.   1274
    +
     4625.   1274// Assignment using = sets the init property of a variable. No other assignment
    +
     4626.   1274// operator can do this. A = token keeps that variable (or array of variables
    +
     4627.   1274// in case of destructuring) in its name property.
    +
     4628.   1274
    +
     4629.   1274    const lvalue = thing.expression[0];
    +
     4630.   1156    if (thing.id === "=") {
    +
     4631.   1156        if (thing.names !== undefined) {
    +
     4632.      0            if (Array.isArray(thing.names)) {
    +
     4633.      0                thing.names.forEach(init_variable);
    +
     4634.      0            } else {
    +
     4635.   1156                init_variable(thing.names);
    +
     4636.   1156            }
    +
     4637.   1156        } else {
    +
     4638.   1156            if (lvalue.id === "[" || lvalue.id === "{") {
    +
     4639.   1156                lvalue.expression.forEach(function (thing) {
    +
     4640.   1156                    if (thing.variable) {
    +
     4641.   1156                        thing.variable.init = true;
    +
     4642.   1156                    }
    +
     4643.   1156                });
    +
     4644.   1156            } else if (
    +
     4645.   1156                lvalue.id === "."
    +
     4646.   1156                && thing.expression[1].id === "undefined"
    +
     4647.   1156            ) {
    +
     4648.   1156                warn(
    +
     4649.   1156                    "expected_a_b",
    +
     4650.   1156                    lvalue.expression,
    +
     4651.   1156                    "delete",
    +
     4652.   1156                    "undefined"
    +
     4653.   1156                );
    +
     4654.   1156            }
    +
     4655.   1156        }
    +
     4656.   1156    } else {
    +
     4657.    118        if (lvalue.arity === "variable") {
    +
     4658.    118            if (!lvalue.variable || lvalue.variable.writable !== true) {
    +
     4659.    118                warn("bad_assignment_a", lvalue);
    +
     4660.    118            }
    +
     4661.    118        }
    +
     4662.    118        const right = syntax[thing.expression[1].id];
    +
     4663.    118        if (
    +
     4664.    118            right !== undefined
    +
     4665.    118            && (
    +
     4666.    118                right.id === "function"
    +
     4667.    118                || right.id === "=>"
    +
     4668.    118                || (
    +
     4669.    118                    right.constant
    +
     4670.    118                    && right.id !== "(number)"
    +
     4671.    118                    && (right.id !== "(string)" || thing.id !== "+=")
    +
     4672.    118                )
    +
     4673.    118            )
    +
     4674.    118        ) {
    +
     4675.    118            warn("unexpected_a", thing.expression[1]);
    +
     4676.    118        }
    +
     4677.    118    }
    +
     4678.   1274});
    +
     4679.      1
    +
     4680.    591function postaction_function(thing) {
    +
     4681.    591    delete functionage.finally;
    +
     4682.    591    delete functionage.loop;
    +
     4683.    591    delete functionage.switch;
    +
     4684.    591    delete functionage.try;
    +
     4685.    591    functionage = stack.pop();
    +
     4686.      1    if (thing.wrapped) {
    +
     4687.      1
    +
     4688.      1// cause: "aa=(function(){})"
    +
     4689.      1
    +
     4690.      1        warn("unexpected_parens", thing);
    +
     4691.      1    }
    +
     4692.    591    return pop_block();
    +
     4693.    591}
    +
     4694.      1
    +
     4695.   9305postaction("binary", function (thing) {
    +
     4696.   9305    let right;
    +
     4697.   1555    if (relationop[thing.id]) {
    +
     4698.   1555        if (
    +
     4699.   1555            is_weird(thing.expression[0])
    +
     4700.   1555            || is_weird(thing.expression[1])
    +
     4701.   1555            || are_similar(thing.expression[0], thing.expression[1])
    +
     4702.   1555            || (
    +
     4703.   1555                thing.expression[0].constant === true
    +
     4704.   1555                && thing.expression[1].constant === true
    +
     4705.   1555            )
    +
     4706.   1555        ) {
    +
     4707.   1555
    +
     4708.   1555// cause: "if(0===0){0}"
    +
     4709.   1555
    +
     4710.   1555            warn("weird_relation_a", thing);
    +
     4711.   1555        }
    +
     4712.   1555    }
    +
     4713.    283    if (thing.id === "+") {
    +
     4714.    283        if (!option.convert) {
    +
     4715.    283            if (thing.expression[0].value === "") {
    +
     4716.    283                warn("expected_a_b", thing, "String(...)", "\"\" +");
    +
     4717.    283            } else if (thing.expression[1].value === "") {
    +
     4718.    283                warn("expected_a_b", thing, "String(...)", "+ \"\"");
    +
     4719.    283            }
    +
     4720.    283        }
    +
     4721.   9022    } else if (thing.id === "[") {
    +
     4722.   9022        if (thing.expression[0].id === "window") {
    +
     4723.   9022
    +
     4724.   9022// cause: "aa=window[0]"
    +
     4725.   9022
    +
     4726.   9022            warn("weird_expression_a", thing, "window[...]");
    +
     4727.   9022        }
    +
     4728.   9022        if (thing.expression[0].id === "self") {
    +
     4729.   9022
    +
     4730.   9022// cause: "aa=self[0]"
    +
     4731.   9022
    +
     4732.   9022            warn("weird_expression_a", thing, "self[...]");
    +
     4733.   9022        }
    +
     4734.   9022    } else if (thing.id === "." || thing.id === "?.") {
    +
     4735.   9022        if (thing.expression.id === "RegExp") {
    +
     4736.   9022
    +
     4737.   9022// cause: "aa=RegExp.aa"
    +
     4738.   9022
    +
     4739.   9022            warn("weird_expression_a", thing);
    +
     4740.   9022        }
    +
     4741.   9022    } else if (thing.id !== "=>" && thing.id !== "(") {
    +
     4742.   9022        right = thing.expression[1];
    +
     4743.   9022        if (
    +
     4744.   9022            (thing.id === "+" || thing.id === "-")
    +
     4745.   9022            && right.id === thing.id
    +
     4746.   9022            && right.arity === "unary"
    +
     4747.   9022            && !right.wrapped
    +
     4748.   9022        ) {
    +
     4749.   9022
    +
     4750.   9022// cause: "0- -0"
    +
     4751.   9022
    +
     4752.   9022            warn("wrap_unary", right);
    +
     4753.   9022        }
    +
     4754.   9022        if (
    +
     4755.   9022            thing.expression[0].constant === true
    +
     4756.   9022            && right.constant === true
    +
     4757.   9022        ) {
    +
     4758.   9022            thing.constant = true;
    +
     4759.   9022        }
    +
     4760.   9022    }
    +
     4761.   9305});
    +
     4762.    387postaction("binary", "&&", function (thing) {
    +
     4763.    387    if (
    +
     4764.    387        is_weird(thing.expression[0])
    +
     4765.    387        || are_similar(thing.expression[0], thing.expression[1])
    +
     4766.    374        || thing.expression[0].constant === true
    +
     4767.    373        || thing.expression[1].constant === true
    +
     4768.     14    ) {
    +
     4769.     14
    +
     4770.     14// cause: "aa=0&&0"
    +
     4771.     14// cause: "aa=`${0}`&&`${0}`"
    +
     4772.     14// cause: "aa=(``?``:``)&&(``?``:``)"
    +
     4773.     14
    +
     4774.     14        warn("weird_condition_a", thing);
    +
     4775.     14    }
    +
     4776.    387});
    +
     4777.    360postaction("binary", "||", function (thing) {
    +
     4778.    360    if (
    +
     4779.    360        is_weird(thing.expression[0])
    +
     4780.    360        || are_similar(thing.expression[0], thing.expression[1])
    +
     4781.    359        || thing.expression[0].constant === true
    +
     4782.      2    ) {
    +
     4783.      2
    +
     4784.      2// cause: "aa=0||0"
    +
     4785.      2
    +
     4786.      2        warn("weird_condition_a", thing);
    +
     4787.      2    }
    +
     4788.    360});
    +
     4789.      1postaction("binary", "=>", postaction_function);
    +
     4790.   2873postaction("binary", "(", function (thing) {
    +
     4791.   2873    let left = thing.expression[0];
    +
     4792.   2873    let the_new;
    +
     4793.   2873    let arg;
    +
     4794.     24    if (left.id === "new") {
    +
     4795.     24        the_new = left;
    +
     4796.     24        left = left.expression;
    +
     4797.     24    }
    +
     4798.     66    if (left.id === "function") {
    +
     4799.     66        if (!thing.wrapped) {
    +
     4800.     66
    +
     4801.     66// cause: "aa=function(){}()"
    +
     4802.     66
    +
     4803.     66            warn("wrap_immediate", thing);
    +
     4804.     66        }
    +
     4805.   2807    } else if (left.identifier) {
    +
     4806.   2807        if (the_new !== undefined) {
    +
     4807.   2807            if (
    +
     4808.   2807                left.id[0] > "Z"
    +
     4809.   2807                || left.id === "Boolean"
    +
     4810.   2807                || left.id === "Number"
    +
     4811.   2807                || left.id === "String"
    +
     4812.   2807                || left.id === "Symbol"
    +
     4813.   2807            ) {
    +
     4814.   2807
    +
     4815.   2807// cause: "+new aa()"
    +
     4816.   2807
    +
     4817.   2807                warn("unexpected_a", the_new);
    +
     4818.   2807            } else if (left.id === "Function") {
    +
     4819.   2807                if (!option.eval) {
    +
     4820.   2807                    warn("unexpected_a", left, "new Function");
    +
     4821.   2807                }
    +
     4822.   2807            } else if (left.id === "Array") {
    +
     4823.   2807                arg = thing.expression;
    +
     4824.   2807                if (arg.length !== 2 || arg[1].id === "(string)") {
    +
     4825.   2807                    warn("expected_a_b", left, "[]", "new Array");
    +
     4826.   2807                }
    +
     4827.   2807            } else if (left.id === "Object") {
    +
     4828.   2807                warn(
    +
     4829.   2807                    "expected_a_b",
    +
     4830.   2807                    left,
    +
     4831.   2807                    "Object.create(null)",
    +
     4832.   2807                    "new Object"
    +
     4833.   2807                );
    +
     4834.   2807            }
    +
     4835.   2807        } else {
    +
     4836.   2807            if (
    +
     4837.   2807                left.id[0] >= "A"
    +
     4838.   2807                && left.id[0] <= "Z"
    +
     4839.   2807                && left.id !== "Boolean"
    +
     4840.   2807                && left.id !== "Number"
    +
     4841.   2807                && left.id !== "String"
    +
     4842.   2807                && left.id !== "Symbol"
    +
     4843.   2807            ) {
    +
     4844.   2807                warn(
    +
     4845.   2807                    "expected_a_before_b",
    +
     4846.   2807                    left,
    +
     4847.   2807                    "new",
    +
     4848.   2807                    artifact(left)
    +
     4849.   2807                );
    +
     4850.   2807            }
    +
     4851.   2807        }
    +
     4852.   2807    } else if (left.id === ".") {
    +
     4853.   2807        let cack = the_new !== undefined;
    +
     4854.   2807        if (left.expression.id === "Date" && left.name.id === "UTC") {
    +
     4855.   2807            cack = !cack;
    +
     4856.   2807        }
    +
     4857.   2807        if (rx_cap.test(left.name.id) !== cack) {
    +
     4858.   2807            if (the_new !== undefined) {
    +
     4859.   2807                warn("unexpected_a", the_new);
    +
     4860.   2807            } else {
    +
     4861.   2807                warn(
    +
     4862.   2807                    "expected_a_before_b",
    +
     4863.   2807                    left.expression,
    +
     4864.   2807                    "new",
    +
     4865.   2807                    left.name.id
    +
     4866.   2807                );
    +
     4867.   2807            }
    +
     4868.   2807        }
    +
     4869.   2807        if (left.name.id === "getTime") {
    +
     4870.   2807            const paren = left.expression;
    +
     4871.   2807            if (paren.id === "(") {
    +
     4872.   2807                const array = paren.expression;
    +
     4873.   2807                if (array.length === 1) {
    +
     4874.   2807                    const new_date = array[0];
    +
     4875.   2807                    if (
    +
     4876.   2807                        new_date.id === "new"
    +
     4877.   2807                        && new_date.expression.id === "Date"
    +
     4878.   2807                    ) {
    +
     4879.   2807                        warn(
    +
     4880.   2807                            "expected_a_b",
    +
     4881.   2807                            new_date,
    +
     4882.   2807                            "Date.now()",
    +
     4883.   2807                            "new Date().getTime()"
    +
     4884.   2807                        );
    +
     4885.   2807                    }
    +
     4886.   2807                }
    +
     4887.   2807            }
    +
     4888.   2807        }
    +
     4889.   2807    }
    +
     4890.   2873});
    +
     4891.    416postaction("binary", "[", function (thing) {
    +
     4892.      1    if (thing.expression[0].id === "RegExp") {
    +
     4893.      1
    +
     4894.      1// cause: "aa=RegExp[0]"
    +
     4895.      1
    +
     4896.      1        warn("weird_expression_a", thing);
    +
     4897.      1    }
    +
     4898.      1    if (is_weird(thing.expression[1])) {
    +
     4899.      1
    +
     4900.      1// cause: "aa[[0]]"
    +
     4901.      1
    +
     4902.      1        warn("weird_expression_a", thing.expression[1]);
    +
     4903.      1    }
    +
     4904.    416});
    +
     4905.      1postaction("statement", "{", pop_block);
    +
     4906.      1postaction("statement", "const", action_var);
    +
     4907.      1postaction("statement", "export", top_level_only);
    +
     4908.      7postaction("statement", "for", function (thing) {
    +
     4909.      7    walk_statement(thing.inc);
    +
     4910.      7});
    +
     4911.      1postaction("statement", "function", postaction_function);
    +
     4912.     11postaction("statement", "import", function (the_thing) {
    +
     4913.     11    const name = the_thing.name;
    +
     4914.     11    if (name) {
    +
     4915.      3        if (Array.isArray(name)) {
    +
     4916.      3            name.forEach(function (name) {
    +
     4917.      3                name.dead = false;
    +
     4918.      3                name.init = true;
    +
     4919.      3                blockage.live.push(name);
    +
     4920.      3            });
    +
     4921.      8        } else {
    +
     4922.      8            name.dead = false;
    +
     4923.      8            name.init = true;
    +
     4924.      8            blockage.live.push(name);
    +
     4925.      8        }
    +
     4926.     11        return top_level_only(the_thing);
    +
     4927.     11    }
    +
     4928.     11});
    +
     4929.      1postaction("statement", "let", action_var);
    +
     4930.     11postaction("statement", "try", function (thing) {
    +
     4931.     10    if (thing.catch !== undefined) {
    +
     4932.     10        const the_name = thing.catch.name;
    +
     4933.     10        if (the_name !== undefined) {
    +
     4934.     10            const the_variable = functionage.context[the_name.id];
    +
     4935.     10            the_variable.dead = false;
    +
     4936.     10            the_variable.init = true;
    +
     4937.     10        }
    +
     4938.     10        walk_statement(thing.catch.block);
    +
     4939.     10    }
    +
     4940.     11});
    +
     4941.      1postaction("statement", "var", action_var);
    +
     4942.     58postaction("ternary", function (thing) {
    +
     4943.     58    if (
    +
     4944.     58        is_weird(thing.expression[0])
    +
     4945.     58        || thing.expression[0].constant === true
    +
     4946.     52        || are_similar(thing.expression[1], thing.expression[2])
    +
     4947.      6    ) {
    +
     4948.      6        warn("unexpected_a", thing);
    +
     4949.     52    } else if (are_similar(thing.expression[0], thing.expression[1])) {
    +
     4950.     52        warn("expected_a_b", thing, "||", "?");
    +
     4951.     52    } else if (are_similar(thing.expression[0], thing.expression[2])) {
    +
     4952.     52        warn("expected_a_b", thing, "&&", "?");
    +
     4953.     52    } else if (
    +
     4954.     52        thing.expression[1].id === "true"
    +
     4955.     52        && thing.expression[2].id === "false"
    +
     4956.     52    ) {
    +
     4957.     52        warn("expected_a_b", thing, "!!", "?");
    +
     4958.     52    } else if (
    +
     4959.     52        thing.expression[1].id === "false"
    +
     4960.     52        && thing.expression[2].id === "true"
    +
     4961.     52    ) {
    +
     4962.     52        warn("expected_a_b", thing, "!", "?");
    +
     4963.     52    } else if (
    +
     4964.     52        thing.expression[0].wrapped !== true
    +
     4965.     52        && (
    +
     4966.     52            thing.expression[0].id === "||"
    +
     4967.     52            || thing.expression[0].id === "&&"
    +
     4968.     52        )
    +
     4969.     52    ) {
    +
     4970.     52
    +
     4971.     52// cause: "(aa&&!aa?0:1)"
    +
     4972.     52
    +
     4973.     52        warn("wrap_condition", thing.expression[0]);
    +
     4974.     52    }
    +
     4975.     58});
    +
     4976.    911postaction("unary", function (thing) {
    +
     4977.     34    if (thing.id === "`") {
    +
     4978.     34        if (thing.expression.every(function (thing) {
    +
     4979.     34            return thing.constant;
    +
     4980.     34        })) {
    +
     4981.     34            thing.constant = true;
    +
     4982.     34        }
    +
     4983.    877    } else if (thing.id === "!") {
    +
     4984.    877        if (thing.expression.constant === true) {
    +
     4985.    877            warn("unexpected_a", thing);
    +
     4986.    877        }
    +
     4987.    877    } else if (thing.id === "!!") {
    +
     4988.    877        if (!option.convert) {
    +
     4989.    877
    +
     4990.    877// cause: "!!0"
    +
     4991.    877
    +
     4992.    877            warn("expected_a_b", thing, "Boolean(...)", "!!");
    +
     4993.    877        }
    +
     4994.    877    } else if (
    +
     4995.    877        thing.id !== "["
    +
     4996.    877        && thing.id !== "{"
    +
     4997.    877        && thing.id !== "function"
    +
     4998.    877        && thing.id !== "new"
    +
     4999.    877    ) {
    +
     5000.    877        if (thing.expression.constant === true) {
    +
     5001.    877            thing.constant = true;
    +
     5002.    877        }
    +
     5003.    877    }
    +
     5004.    911});
    +
     5005.      1postaction("unary", "function", postaction_function);
    +
     5006.      9postaction("unary", "+", function (thing) {
    +
     5007.      9    if (!option.convert) {
    +
     5008.      9        warn("expected_a_b", thing, "Number(...)", "+");
    +
     5009.      9    }
    +
     5010.      9    const right = thing.expression;
    +
     5011.      1    if (right.id === "(" && right.expression[0].id === "new") {
    +
     5012.      1        warn("unexpected_a_before_b", thing, "+", "new");
    +
     5013.      8    } else if (
    +
     5014.      8        right.constant
    +
     5015.      8        || right.id === "{"
    +
     5016.      8        || (right.id === "[" && right.arity !== "binary")
    +
     5017.      8    ) {
    +
     5018.      8        warn("unexpected_a", thing, "+");
    +
     5019.      8    }
    +
     5020.      9});
    +
     5021.      1
    +
     5022.    569function delve(the_function) {
    +
     5023.   3800    Object.keys(the_function.context).forEach(function (id) {
    +
     5024.   3787        if (id !== "ignore") {
    +
     5025.   3787            const name = the_function.context[id];
    +
     5026.   3787            if (name.parent === the_function) {
    +
     5027.   3787                if (
    +
     5028.   3787                    name.used === 0
    +
     5029.   3787                    && (
    +
     5030.   3787                        name.role !== "function"
    +
     5031.      0                        || name.parent.arity !== "unary"
    +
     5032.   3787                    )
    +
     5033.   3787                ) {
    +
     5034.   3787
    +
     5035.   3787// cause: "/*jslint node*/\nlet aa;"
    +
     5036.   3787// cause: "function aa(aa){return;}"
    +
     5037.   3787
    +
     5038.   3787                    warn("unused_a", name);
    +
     5039.   3787                } else if (!name.init) {
    +
     5040.   3787
    +
     5041.   3787// cause: "/*jslint node*/\nlet aa;aa();"
    +
     5042.   3787
    +
     5043.   3787                    warn("uninitialized_a", name);
    +
     5044.   3787                }
    +
     5045.   3787            }
    +
     5046.   3787        }
    +
     5047.   3800    });
    +
     5048.    569}
    +
     5049.      1
    +
     5050.     69function uninitialized_and_unused() {
    +
     5051.     69
    +
     5052.     69// Delve into the functions looking for variables that were not initialized
    +
     5053.     69// or used. If the file imports or exports, then its global object is also
    +
     5054.     69// delved.
    +
     5055.     69
    +
     5056.     61    if (module_mode === true || option.node) {
    +
     5057.     18        delve(global);
    +
     5058.     18    }
    +
     5059.     69    functions.forEach(delve);
    +
     5060.     69}
    +
     5061.      1
    +
     5062.      1// Go through the token list, looking at usage of whitespace.
    +
     5063.      1
    +
     5064.     67function whitage() {
    +
     5065.     67    let closer = "(end)";
    +
     5066.     67
    +
     5067.     67// free = false
    +
     5068.     67
    +
     5069.     67// cause: "()=>0"
    +
     5070.     67// cause: "aa()"
    +
     5071.     67// cause: "aa(0,0)"
    +
     5072.     67// cause: "function(){}"
    +
     5073.     67
    +
     5074.     67    let free = false;
    +
     5075.     67
    +
     5076.     67// cause: "(0)"
    +
     5077.     67// cause: "(aa)"
    +
     5078.     67// cause: "aa(0)"
    +
     5079.     67// cause: "do{}while()"
    +
     5080.     67// cause: "for(){}"
    +
     5081.     67// cause: "if(){}"
    +
     5082.     67// cause: "switch(){}"
    +
     5083.     67// cause: "while(){}"
    +
     5084.     67
    +
     5085.     67// let free = true;
    +
     5086.     67
    +
     5087.     67    let left = global;
    +
     5088.     67    let margin = 0;
    +
     5089.     67    let nr_comments_skipped = 0;
    +
     5090.     67    let open = true;
    +
     5091.     67    let opening = true;
    +
     5092.     67    let right;
    +
     5093.     67
    +
     5094.   6759    function pop() {
    +
     5095.   6759        const previous = stack.pop();
    +
     5096.   6759        closer = previous.closer;
    +
     5097.   6759        free = previous.free;
    +
     5098.   6759        margin = previous.margin;
    +
     5099.   6759        open = previous.open;
    +
     5100.   6759        opening = previous.opening;
    +
     5101.   6759    }
    +
     5102.     67
    +
     5103.   6759    function push() {
    +
     5104.   6759        stack.push({
    +
     5105.   6759            closer,
    +
     5106.   6759            free,
    +
     5107.   6759            margin,
    +
     5108.   6759            open,
    +
     5109.   6759            opening
    +
     5110.   6759        });
    +
     5111.   6759    }
    +
     5112.     67
    +
     5113.     32    function expected_at(at) {
    +
     5114.     32        warn(
    +
     5115.     32            "expected_a_at_b_c",
    +
     5116.     32            right,
    +
     5117.     32            artifact(right),
    +
     5118.     32            fudge + at,
    +
     5119.     32
    +
     5120.     32// Return the fudged column number of an artifact.
    +
     5121.     32
    +
     5122.     32            right.from + fudge
    +
     5123.     32        );
    +
     5124.     32    }
    +
     5125.     67
    +
     5126.   9713    function at_margin(fit) {
    +
     5127.   9713        const at = margin + fit;
    +
     5128.     24        if (right.from !== at) {
    +
     5129.     24            return expected_at(at);
    +
     5130.     24        }
    +
     5131.   9713    }
    +
     5132.     67
    +
     5133.  26076    function no_space_only() {
    +
     5134.  26076        if (
    +
     5135.  26076            left.id !== "(global)"
    +
     5136.  26075            && left.nr + 1 === right.nr
    +
     5137.  26075            && (
    +
     5138.  26075                left.line !== right.line
    +
     5139.  26075                || left.thru !== right.from
    +
     5140.  26075            )
    +
     5141.     13        ) {
    +
     5142.     13
    +
     5143.     13// cause:
    +
     5144.     13//  (
    +
     5145.     13// function aa (
    +
     5146.     13// bb
    +
     5147.     13// ,
    +
     5148.     13// [
    +
     5149.     13// cc,dd
    +
     5150.     13// ]
    +
     5151.     13// ,
    +
     5152.     13// {
    +
     5153.     13// ee,ff=( 0 )
    +
     5154.     13// }
    +
     5155.     13// ,
    +
     5156.     13// ... zz
    +
     5157.     13// )
    +
     5158.     13// {
    +
     5159.     13// return {
    +
     5160.     13// aa,bb
    +
     5161.     13// }
    +
     5162.     13// ;
    +
     5163.     13// }
    +
     5164.     13// (
    +
     5165.     13// )
    +
     5166.     13//  )
    +
     5167.     13//  ;
    +
     5168.     13
    +
     5169.     13            warn(
    +
     5170.     13                "unexpected_space_a_b",
    +
     5171.     13                right,
    +
     5172.     13                artifact(left),
    +
     5173.     13                artifact(right)
    +
     5174.     13            );
    +
     5175.     13        }
    +
     5176.  26076    }
    +
     5177.     67
    +
     5178.    966    function no_space() {
    +
     5179.    963        if (left.line === right.line) {
    +
     5180.    963            if (left.thru !== right.from && nr_comments_skipped === 0) {
    +
     5181.    963
    +
     5182.    963// cause: "let aa = aa()( );"
    +
     5183.    963
    +
     5184.    963                warn(
    +
     5185.    963                    "unexpected_space_a_b",
    +
     5186.    963                    right,
    +
     5187.    963                    artifact(left),
    +
     5188.    963                    artifact(right)
    +
     5189.    963                );
    +
     5190.    963            }
    +
     5191.    963        } else {
    +
     5192.      3            if (open) {
    +
     5193.      3                const at = (
    +
     5194.      3                    free
    +
     5195.      3                    ? margin
    +
     5196.      0                    : margin + 8 // deadcode?
    +
     5197.      3                );
    +
     5198.      3                if (right.from < at) {
    +
     5199.      3
    +
     5200.      3// cause:
    +
     5201.      3// let aa = aa(
    +
     5202.      3//     aa
    +
     5203.      3// ()
    +
     5204.      3// );
    +
     5205.      3
    +
     5206.      3                    expected_at(at);
    +
     5207.      3                }
    +
     5208.      0            } else { // deadcode?
    +
     5209.      0                if (right.from !== margin + 8) {
    +
     5210.      0                    expected_at(margin + 8);
    +
     5211.      0                }
    +
     5212.      0            }
    +
     5213.      3        }
    +
     5214.    966    }
    +
     5215.     67
    +
     5216.   2542    function one_space_only() {
    +
     5217.   2541        if (left.line !== right.line || left.thru + 1 !== right.from) {
    +
     5218.     12            warn(
    +
     5219.     12                "expected_space_a_b",
    +
     5220.     12                right,
    +
     5221.     12                artifact(left),
    +
     5222.     12                artifact(right)
    +
     5223.     12            );
    +
     5224.     12        }
    +
     5225.   2542    }
    +
     5226.     67
    +
     5227.  13954    function one_space() {
    +
     5228.  13437        if (left.line === right.line || !open) {
    +
     5229.  13437            if (left.thru + 1 !== right.from && nr_comments_skipped === 0) {
    +
     5230.  13437                warn(
    +
     5231.  13437                    "expected_space_a_b",
    +
     5232.  13437                    right,
    +
     5233.  13437                    artifact(left),
    +
     5234.  13437                    artifact(right)
    +
     5235.  13437                );
    +
     5236.  13437            }
    +
     5237.  13437        } else {
    +
     5238.    517            if (right.from !== margin) {
    +
     5239.    517                expected_at(margin);
    +
     5240.    517            }
    +
     5241.    517        }
    +
     5242.  13954    }
    +
     5243.     67
    +
     5244.     67    stack = [];
    +
     5245.  54979    tokens.forEach(function (the_token) {
    +
     5246.  54979        right = the_token;
    +
     5247.  53486        if (right.id === "(comment)" || right.id === "(end)") {
    +
     5248.   1562            nr_comments_skipped += 1;
    +
     5249.  53417        } else {
    +
     5250.  53417
    +
     5251.  53417// If left is an opener and right is not the closer, then push the previous
    +
     5252.  53417// state. If the token following the opener is on the next line, then this is
    +
     5253.  53417// an open form. If the tokens are on the same line, then it is a closed form.
    +
     5254.  53417// Open form is more readable, with each item (statement, argument, parameter,
    +
     5255.  53417// etc) starting on its own line. Closed form is more compact. Statement blocks
    +
     5256.  53417// are always in open form.
    +
     5257.  53417
    +
     5258.  53417            const new_closer = opener[left.id];
    +
     5259.  53417            if (typeof new_closer === "string") {
    +
     5260.  53417
    +
     5261.  53417// cause: "${"
    +
     5262.  53417// cause: "("
    +
     5263.  53417// cause: "["
    +
     5264.  53417// cause: "{"
    +
     5265.  53417
    +
     5266.  53417                if (new_closer !== right.id) {
    +
     5267.  53417
    +
     5268.  53417// cause: "${0"
    +
     5269.  53417// cause: "(0"
    +
     5270.  53417// cause: "[0"
    +
     5271.  53417// cause: "{0"
    +
     5272.  53417
    +
     5273.  53417                    opening = left.open || (left.line !== right.line);
    +
     5274.  53417                    push();
    +
     5275.  53417                    closer = new_closer;
    +
     5276.  53417                    if (opening) {
    +
     5277.  53417
    +
     5278.  53417// cause: "${\n0\n}"
    +
     5279.  53417// cause: "(\n0\n)"
    +
     5280.  53417// cause: "[\n0\n]"
    +
     5281.  53417// cause: "{\n0\n}"
    +
     5282.  53417
    +
     5283.  53417                        free = closer === ")" && left.free;
    +
     5284.  53417                        open = true;
    +
     5285.  53417                        margin += 4;
    +
     5286.  53417                        if (right.role === "label") {
    +
     5287.  53417                            if (right.from !== 0) {
    +
     5288.  53417
    +
     5289.  53417// cause:
    +
     5290.  53417// function aa() {
    +
     5291.  53417//  bb:
    +
     5292.  53417//     while (aa) {
    +
     5293.  53417//         if (aa) {
    +
     5294.  53417//             break bb;
    +
     5295.  53417//         }
    +
     5296.  53417//     }
    +
     5297.  53417// }
    +
     5298.  53417
    +
     5299.  53417                                expected_at(0);
    +
     5300.  53417                            }
    +
     5301.  53417                        } else if (right.switch) {
    +
     5302.  53417                            at_margin(-4);
    +
     5303.  53417                        } else {
    +
     5304.  53417                            at_margin(0);
    +
     5305.  53417                        }
    +
     5306.  53417                    } else {
    +
     5307.  53417                        if (right.statement || right.role === "label") {
    +
     5308.  53417
    +
     5309.  53417// cause:
    +
     5310.  53417// function aa() {bb:
    +
     5311.  53417//     while (aa) {aa();
    +
     5312.  53417//     }
    +
     5313.  53417// }
    +
     5314.  53417
    +
     5315.  53417                            warn(
    +
     5316.  53417                                "expected_line_break_a_b",
    +
     5317.  53417                                right,
    +
     5318.  53417                                artifact(left),
    +
     5319.  53417                                artifact(right)
    +
     5320.  53417                            );
    +
     5321.  53417                        }
    +
     5322.  53417
    +
     5323.  53417// cause: "${0}"
    +
     5324.  53417// cause: "(0)"
    +
     5325.  53417// cause: "[0]"
    +
     5326.  53417// cause: "{0}"
    +
     5327.  53417
    +
     5328.  53417                        free = false;
    +
     5329.  53417                        open = false;
    +
     5330.  53417
    +
     5331.  53417// cause: "let aa = ( 0 );"
    +
     5332.  53417
    +
     5333.  53417                        no_space_only();
    +
     5334.  53417                    }
    +
     5335.  53417                } else {
    +
     5336.  53417
    +
     5337.  53417// If left and right are opener and closer, then the placement of right depends
    +
     5338.  53417// on the openness. Illegal pairs (like '{]') have already been detected.
    +
     5339.  53417
    +
     5340.  53417// cause: "${}"
    +
     5341.  53417// cause: "()"
    +
     5342.  53417// cause: "[]"
    +
     5343.  53417// cause: "{}"
    +
     5344.  53417
    +
     5345.  53417                    if (left.line === right.line) {
    +
     5346.  53417                        no_space();
    +
     5347.  53417                    } else {
    +
     5348.  53417                        at_margin(0);
    +
     5349.  53417                    }
    +
     5350.  53417                }
    +
     5351.  53417            } else {
    +
     5352.  53417                if (right.statement === true) {
    +
     5353.  53417                    if (left.id === "else") {
    +
     5354.  53417                        one_space_only();
    +
     5355.  53417                    } else {
    +
     5356.  53417                        at_margin(0);
    +
     5357.  53417                        open = false;
    +
     5358.  53417                    }
    +
     5359.  53417
    +
     5360.  53417// If right is a closer, then pop the previous state.
    +
     5361.  53417
    +
     5362.  53417                } else if (right.id === closer) {
    +
     5363.  53417                    pop();
    +
     5364.  53417                    if (opening && right.id !== ";") {
    +
     5365.  53417                        at_margin(0);
    +
     5366.  53417                    } else {
    +
     5367.  53417                        no_space_only();
    +
     5368.  53417                    }
    +
     5369.  53417                } else {
    +
     5370.  53417
    +
     5371.  53417// Left is not an opener, and right is not a closer.
    +
     5372.  53417// The nature of left and right will determine the space between them.
    +
     5373.  53417
    +
     5374.  53417// If left is ',' or ';' or right is a statement then if open,
    +
     5375.  53417// right must go at the margin, or if closed, a space between.
    +
     5376.  53417
    +
     5377.  53417                    if (right.switch) {
    +
     5378.  53417                        at_margin(-4);
    +
     5379.  53417                    } else if (right.role === "label") {
    +
     5380.  53417                        if (right.from !== 0) {
    +
     5381.  53417
    +
     5382.  53417// cause:
    +
     5383.  53417// function aa() {
    +
     5384.  53417//     let bb = 0;cc:
    +
     5385.  53417//     while (aa) {
    +
     5386.  53417//         if (aa) {
    +
     5387.  53417//             break cc;
    +
     5388.  53417//         }
    +
     5389.  53417//     }
    +
     5390.  53417// }
    +
     5391.  53417
    +
     5392.  53417                            expected_at(0);
    +
     5393.  53417                        }
    +
     5394.  53417                    } else if (left.id === ",") {
    +
     5395.  53417                        if (!open || (
    +
     5396.  53417                            (free || closer === "]")
    +
     5397.  53417                            && left.line === right.line
    +
     5398.  53417                        )) {
    +
     5399.  53417                            one_space();
    +
     5400.  53417                        } else {
    +
     5401.  53417                            at_margin(0);
    +
     5402.  53417                        }
    +
     5403.  53417
    +
     5404.  53417// If right is a ternary operator, line it up on the margin.
    +
     5405.  53417
    +
     5406.  53417                    } else if (right.arity === "ternary") {
    +
     5407.  53417                        if (open) {
    +
     5408.  53417                            at_margin(0);
    +
     5409.  53417                        } else {
    +
     5410.  53417                            warn("use_open", right);
    +
     5411.  53417                        }
    +
     5412.  53417                    } else if (
    +
     5413.  53417                        right.arity === "binary"
    +
     5414.  53417                        && right.id === "("
    +
     5415.  53417                        && free
    +
     5416.  53417                    ) {
    +
     5417.  53417                        no_space();
    +
     5418.  53417                    } else if (
    +
     5419.  53417                        left.id === "."
    +
     5420.  53417                        || left.id === "?."
    +
     5421.  53417                        || left.id === "..."
    +
     5422.  53417                        || right.id === ","
    +
     5423.  53417                        || right.id === ";"
    +
     5424.  53417                        || right.id === ":"
    +
     5425.  53417                        || (
    +
     5426.  53417                            right.arity === "binary"
    +
     5427.  53417                            && (right.id === "(" || right.id === "[")
    +
     5428.  53417                        )
    +
     5429.  53417                        || (
    +
     5430.  53417                            right.arity === "function"
    +
     5431.  53417                            && left.id !== "function"
    +
     5432.  53417                        )
    +
     5433.  53417                    ) {
    +
     5434.  53417                        no_space_only();
    +
     5435.  53417                    } else if (right.id === "." || right.id === "?.") {
    +
     5436.  53417                        no_space_only();
    +
     5437.      0                    } else if (left.id === ";") { // deadcode?
    +
     5438.      0                        if (open) {
    +
     5439.      0                            at_margin(0);
    +
     5440.      0                        }
    +
     5441.      0                    } else if (
    +
     5442.  53417                        left.arity === "ternary"
    +
     5443.  53417                        || left.id === "case"
    +
     5444.  53417                        || left.id === "catch"
    +
     5445.  53417                        || left.id === "else"
    +
     5446.  53417                        || left.id === "finally"
    +
     5447.  53417                        || left.id === "while"
    +
     5448.  53417                        || left.id === "await"
    +
     5449.  53417                        || right.id === "catch"
    +
     5450.  53417                        || right.id === "else"
    +
     5451.  53417                        || right.id === "finally"
    +
     5452.  53417                        || (right.id === "while" && !right.statement)
    +
     5453.  53417                        || (left.id === ")" && right.id === "{")
    +
     5454.  53417                    ) {
    +
     5455.  53417
    +
     5456.  53417// cause:
    +
     5457.  53417// function aa() {
    +
     5458.  53417//     do {
    +
     5459.  53417//         aa();
    +
     5460.  53417//     } while(aa());
    +
     5461.  53417// }
    +
     5462.  53417
    +
     5463.  53417                        one_space_only();
    +
     5464.  53417                    } else if (
    +
     5465.  53417
    +
     5466.  53417// There is a space between left and right.
    +
     5467.  53417
    +
     5468.  53417                        spaceop[left.id] === true
    +
     5469.  53417                        || spaceop[right.id] === true
    +
     5470.  53417                        || (
    +
     5471.  53417                            left.arity === "binary"
    +
     5472.  53417                            && (left.id === "+" || left.id === "-")
    +
     5473.  53417                        )
    +
     5474.  53417                        || (
    +
     5475.  53417                            right.arity === "binary"
    +
     5476.  53417                            && (right.id === "+" || right.id === "-")
    +
     5477.  53417                        )
    +
     5478.  53417                        || left.id === "function"
    +
     5479.  53417                        || left.id === ":"
    +
     5480.  53417                        || (
    +
     5481.  53417                            (
    +
     5482.  53417                                left.identifier
    +
     5483.  53417                                || left.id === "(string)"
    +
     5484.  53417                                || left.id === "(number)"
    +
     5485.  53417                            )
    +
     5486.  53417                            && (
    +
     5487.  53417                                right.identifier
    +
     5488.  53417                                || right.id === "(string)"
    +
     5489.  53417                                || right.id === "(number)"
    +
     5490.  53417                            )
    +
     5491.  53417                        )
    +
     5492.  53417                        || (left.arity === "statement" && right.id !== ";")
    +
     5493.  53417                    ) {
    +
     5494.  53417
    +
     5495.  53417// cause: "let aa=0;"
    +
     5496.  53417
    +
     5497.  53417                        one_space();
    +
     5498.  53417                    } else if (left.arity === "unary" && left.id !== "`") {
    +
     5499.  53417                        no_space_only();
    +
     5500.  53417                    }
    +
     5501.  53417                }
    +
     5502.  53417            }
    +
     5503.  53417            nr_comments_skipped = 0;
    +
     5504.  53417            delete left.calls;
    +
     5505.  53417            delete left.dead;
    +
     5506.  53417            delete left.free;
    +
     5507.  53417            delete left.init;
    +
     5508.  53417            delete left.open;
    +
     5509.  53417            delete left.used;
    +
     5510.  53417            left = right;
    +
     5511.  53417        }
    +
     5512.  54979    });
    +
     5513.     67}
    +
     5514.      1
    +
     5515.      1// The jslint function itself.
    +
     5516.      1
    +
     5517.    368function jslint(
    +
     5518.    368    source = "",
    +
     5519.    368    option_object = empty(),
    +
     5520.    368    global_array = []
    +
     5521.    368) {
    +
     5522.    368    try {
    +
     5523.    368        warnings = [];
    +
     5524.    368        option = Object.assign(empty(), option_object);
    +
     5525.    368        anon = "anonymous";
    +
     5526.    368        block_stack = [];
    +
     5527.    368        declared_globals = empty();
    +
     5528.    368        directive_mode = true;
    +
     5529.    368        directives = [];
    +
     5530.    368        early_stop = true;
    +
     5531.    368        exports = empty();
    +
     5532.    368        froms = [];
    +
     5533.    368        fudge = (
    +
     5534.    368            option.fudge
    +
     5535.      1            ? 1
    +
     5536.    367            : 0
    +
     5537.    368        );
    +
     5538.    368        functions = [];
    +
     5539.    368        global = {
    +
     5540.    368            body: true,
    +
     5541.    368            context: empty(),
    +
     5542.    368            finally: 0,
    +
     5543.    368            from: 0,
    +
     5544.    368            id: "(global)",
    +
     5545.    368            level: 0,
    +
     5546.    368            line: 0,
    +
     5547.    368            live: [],
    +
     5548.    368            loop: 0,
    +
     5549.    368            switch: 0,
    +
     5550.    368            thru: 0,
    +
     5551.    368            try: 0
    +
     5552.    368        };
    +
     5553.    368        blockage = global;
    +
     5554.    368        functionage = global;
    +
     5555.    368        json_mode = false;
    +
     5556.    368        mega_mode = false;
    +
     5557.    368        module_mode = false;
    +
     5558.    368        next_token = global;
    +
     5559.    368        property = empty();
    +
     5560.    368        shebang = false;
    +
     5561.    368        stack = [];
    +
     5562.    368        tenure = undefined;
    +
     5563.    368        token = global;
    +
     5564.    368        token_nr = 0;
    +
     5565.    368        var_mode = undefined;
    +
     5566.    368        populate(standard, declared_globals, false);
    +
     5567.    368        populate(global_array, declared_globals, false);
    +
     5568.     24        Object.keys(option).forEach(function (name) {
    +
     5569.     24            if (option[name] === true) {
    +
     5570.     24                const allowed = allowed_option[name];
    +
     5571.     11                if (Array.isArray(allowed)) {
    +
     5572.     11                    populate(allowed, declared_globals, false);
    +
     5573.     11                }
    +
     5574.     24            }
    +
     5575.     24        });
    +
     5576.    368        tokenize(source);
    +
     5577.    368        advance();
    +
     5578.     22        if (json_mode) {
    +
     5579.     22            tree = json_value();
    +
     5580.     22            advance("(end)");
    +
     5581.    320        } else {
    +
     5582.    320
    +
     5583.    320// Because browsers encourage combining of script files, the first token might
    +
     5584.    320// be a semicolon to defend against a missing semicolon in the preceding file.
    +
     5585.    320
    +
     5586.    320            if (option.browser) {
    +
     5587.    320                if (next_token.id === ";") {
    +
     5588.    320                    advance(";");
    +
     5589.    320                }
    +
     5590.    320            } else {
    +
     5591.    320
    +
     5592.    320// If we are not in a browser, then the file form of strict pragma may be used.
    +
     5593.    320
    +
     5594.    320                if (
    +
     5595.    320                    next_token.value === "use strict"
    +
     5596.    320                ) {
    +
     5597.    320                    advance("(string)");
    +
     5598.    320                    advance(";");
    +
     5599.    320                }
    +
     5600.    320            }
    +
     5601.    320            tree = statements();
    +
     5602.    320            advance("(end)");
    +
     5603.    320            functionage = global;
    +
     5604.    320            walk_statement(tree);
    +
     5605.    320            if (warnings.length === 0) {
    +
     5606.    320                uninitialized_and_unused();
    +
     5607.    320                if (!option.white) {
    +
     5608.    320                    whitage();
    +
     5609.    320                }
    +
     5610.    320            }
    +
     5611.    320        }
    +
     5612.    279        if (!option.browser) {
    +
     5613.    275            directives.forEach(function (comment) {
    +
     5614.    275                if (comment.directive === "global") {
    +
     5615.    275
    +
     5616.    275// cause: "/*global aa*/"
    +
     5617.    275
    +
     5618.    275                    warn("missing_browser", comment);
    +
     5619.    275                }
    +
     5620.    275            });
    +
     5621.    279        }
    +
     5622.    279        if (option.test_internal_error) {
    +
     5623.      1            assert_or_throw(undefined, "test_internal_error");
    +
     5624.    278        }
    +
     5625.    278        early_stop = false;
    +
     5626.    278    } catch (e) {
    +
     5627.     90        e.early_stop = true;
    +
     5628.     90        e.message = "[JSLint was unable to finish]\n" + e.message;
    +
     5629.     90        if (e.name !== "JSLintError") {
    +
     5630.     90            e.column = 0;
    +
     5631.     90            e.line = 0;
    +
     5632.     90            e.stack_trace = e.stack;
    +
     5633.     90            warnings.push(e);
    +
     5634.     90        }
    +
     5635.     90    }
    +
     5636.    368
    +
     5637.    368// sort warnings by early_stop first, line, column respectively
    +
     5638.    368
    +
     5639.    837    warnings.sort(function (a, b) {
    +
     5640.    837        return (
    +
     5641.    837            Boolean(b.early_stop) - Boolean(a.early_stop)
    +
     5642.    777            || a.line - b.line || a.column - b.column
    +
     5643.    837        );
    +
     5644.    837
    +
     5645.    837// update each warning with a formatted_message ready for use by cli
    +
     5646.    837
    +
     5647.    812    }).map(function ({
    +
     5648.    812        column = 0,
    +
     5649.    812        line = 0,
    +
     5650.    812        message = "",
    +
     5651.    812        stack_trace = ""
    +
     5652.    812    }, ii, list) {
    +
     5653.    812        column += 1;
    +
     5654.    812        line += 1;
    +
     5655.    812        list[ii].formatted_message = String(
    +
     5656.    812            String(ii + 1).padStart(3, " ")
    +
     5657.    812            + " \u001b[31m" + message + "\u001b[39m"
    +
     5658.    812            + " \u001b[90m\/\/ line " + line + ", column " + column
    +
     5659.    812            + "\u001b[39m\n"
    +
     5660.    812            + ("    " + String(lines && lines[line - 1]).trim()).slice(0, 72)
    +
     5661.    812            + "\n" + stack_trace
    +
     5662.    812        ).trim();
    +
     5663.    812    });
    +
     5664.    368    return {
    +
     5665.    368        directives,
    +
     5666.    368        edition,
    +
     5667.    368        exports,
    +
     5668.    368        froms,
    +
     5669.    368        functions,
    +
     5670.    368        global,
    +
     5671.    368        id: "(JSLint)",
    +
     5672.    368        json: json_mode,
    +
     5673.    368        lines,
    +
     5674.    368        module: module_mode === true,
    +
     5675.     54        ok: warnings.length === 0 && !early_stop,
    +
     5676.    368        option,
    +
     5677.    368        property,
    +
     5678.    368        shebang: (
    +
     5679.    368            shebang
    +
     5680.      3            ? lines[0]
    +
     5681.    365            : undefined
    +
     5682.    368        ),
    +
     5683.    368        stop: early_stop,
    +
     5684.    368        tokens,
    +
     5685.    368        tree,
    +
     5686.    368        warnings
    +
     5687.    368    };
    +
     5688.    368}
    +
     5689.      1
    +
     5690.      4async function cli({
    +
     5691.      4    console_error,
    +
     5692.      4    file,
    +
     5693.      4    option,
    +
     5694.      4    source
    +
     5695.      4}) {
    +
     5696.      4/*
    +
     5697.      4 * this function will run jslint from nodejs-cli
    +
     5698.      4 */
    +
     5699.      4    const fs = await import("fs");
    +
     5700.      4    let exitCode;
    +
     5701.      8    function string_line_count(code) {
    +
     5702.      8    /*
    +
     5703.      8     * this function will count number of newlines in <code>
    +
     5704.      8     */
    +
     5705.      8        let cnt;
    +
     5706.      8        let ii;
    +
     5707.      8        // https://jsperf.com/regexp-counting-2/8
    +
     5708.      8        cnt = 0;
    +
     5709.      8        ii = 0;
    +
     5710.   2218        while (true) {
    +
     5711.   2218            ii = code.indexOf("\n", ii) + 1;
    +
     5712.   2218            if (ii === 0) {
    +
     5713.   2218                break;
    +
     5714.   2218            }
    +
     5715.   2218            cnt += 1;
    +
     5716.   2218        }
    +
     5717.      8        return cnt;
    +
     5718.      8    }
    +
     5719.     22    function jslint_from_file({
    +
     5720.     22        code,
    +
     5721.     22        file,
    +
     5722.     22        line_offset = 0,
    +
     5723.     22        option = {},
    +
     5724.     22        warnings = []
    +
     5725.     22    }) {
    +
     5726.     22        switch ((
    +
     5727.     22            /\.\w+?$|$/m
    +
     5728.     22        ).exec(file)[0]) {
    +
     5729.      5        case ".html":
    +
     5730.      5            // recurse
    +
     5731.      5            code.replace((
    +
     5732.      5                /^<script>\n([\S\s]*?\n)<\/script>$/gm
    +
     5733.      5            ), function (ignore, match1, ii) {
    +
     5734.      5                jslint_from_file({
    +
     5735.      5                    code: match1,
    +
     5736.      5                    file: file + ".<script>.js",
    +
     5737.      5                    line_offset: string_line_count(code.slice(0, ii)) + 1,
    +
     5738.      5                    option: Object.assign({
    +
     5739.      5                        browser: true
    +
     5740.      5                    }, option)
    +
     5741.      5                });
    +
     5742.      5                return "";
    +
     5743.      5            });
    +
     5744.      5            return;
    +
     5745.      2        case ".md":
    +
     5746.      2            // recurse
    +
     5747.      2            code.replace((
    +
     5748.      2                /^```javascript\n([\S\s]*?\n)```$/gm
    +
     5749.      2            ), function (ignore, match1, ii) {
    +
     5750.      2                jslint_from_file({
    +
     5751.      2                    code: match1,
    +
     5752.      2                    file: file + ".<```javascript>.js",
    +
     5753.      2                    line_offset: string_line_count(code.slice(0, ii)) + 1,
    +
     5754.      2                    option
    +
     5755.      2                });
    +
     5756.      2                return "";
    +
     5757.      2            });
    +
     5758.      2            return;
    +
     5759.      1        case ".sh":
    +
     5760.      1            // recurse
    +
     5761.      1            code.replace((
    +
     5762.      1                /\bnode\u0020.*?-e\u0020'\n([\S\s]*?\n)'/gm
    +
     5763.      6            ), function (ignore, match1, ii) {
    +
     5764.      6                jslint_from_file({
    +
     5765.      6                    code: match1,
    +
     5766.      6                    file: file + ".<node -e>.js",
    +
     5767.      6                    line_offset: string_line_count(code.slice(0, ii)) + 1,
    +
     5768.      6                    option: Object.assign({
    +
     5769.      6                        node: true
    +
     5770.      6                    }, option)
    +
     5771.      6                });
    +
     5772.      6                return "";
    +
     5773.      6            });
    +
     5774.      1            return;
    +
     5775.     14        default:
    +
     5776.     14            warnings = jslint(
    +
     5777.     14                "\n".repeat(line_offset) + code,
    +
     5778.     14                option
    +
     5779.     14            ).warnings;
    +
     5780.     14        }
    +
     5781.     14        // print only first 10 warnings
    +
     5782.     14        if (warnings.length > 0) {
    +
     5783.      1            exitCode = 1;
    +
     5784.      1            // print first 10 warnings to stderr
    +
     5785.      1            console_error(
    +
     5786.      1                "\u001b[1mjslint " + file + "\u001b[22m\n"
    +
     5787.      4                + warnings.slice(0, 10).map(function ({
    +
     5788.      4                    formatted_message
    +
     5789.      4                }) {
    +
     5790.      4                    return formatted_message;
    +
     5791.      4                }).join("\n")
    +
     5792.      1            );
    +
     5793.      1        }
    +
     5794.     22    }
    +
     5795.      3    console_error = console_error || console.error;
    +
     5796.      2    if (source) {
    +
     5797.      2        jslint_from_file({
    +
     5798.      2            code: source,
    +
     5799.      2            file,
    +
     5800.      2            option
    +
     5801.      2        });
    +
     5802.      2        return;
    +
     5803.      2    }
    +
     5804.      2    if (file === ".") {
    +
     5805.      1        file = await fs.promises.readdir(".");
    +
     5806.     28        await Promise.all(file.map(async function (file) {
    +
     5807.     28            let code;
    +
     5808.     28            let timeStart = Date.now();
    +
     5809.     28            switch ((
    +
     5810.     28                /\.\w+?$|$/m
    +
     5811.     28            ).exec(file)[0]) {
    +
     5812.      4            case ".html":
    +
     5813.      9            case ".js":
    +
     5814.     10            case ".json":
    +
     5815.     12            case ".md":
    +
     5816.     12            case ".mjs":
    +
     5817.     13            case ".sh":
    +
     5818.     13                break;
    +
     5819.     15            default:
    +
     5820.     15                return;
    +
     5821.     13            }
    +
     5822.     13            try {
    +
     5823.     13                code = await fs.promises.readFile(file, "utf8");
    +
     5824.     12            } catch (ignore) {
    +
     5825.      1                return;
    +
     5826.     12            }
    +
     5827.     12            if (!(
    +
     5828.     12                !(
    +
     5829.     12                    /\b(?:lock|min|raw|rollup)\b/
    +
     5830.     12                ).test(file) && code && code.length < 1048576
    +
     5831.      1            )) {
    +
     5832.      1                return;
    +
     5833.     11            }
    +
     5834.     11            jslint_from_file({
    +
     5835.     11                code,
    +
     5836.     11                file,
    +
     5837.     11                option
    +
     5838.     11            });
    +
     5839.     11            console_error(
    +
     5840.     11                "jslint - " + (Date.now() - timeStart) + "ms - " + file
    +
     5841.     11            );
    +
     5842.     11        }));
    +
     5843.      1    } else {
    +
     5844.      1        jslint_from_file({
    +
     5845.      1            code: await fs.promises.readFile(file, "utf8"),
    +
     5846.      1            file,
    +
     5847.      1            option
    +
     5848.      1        });
    +
     5849.      2    }
    +
     5850.      2    return exitCode;
    +
     5851.      2}
    +
     5852.    357export default Object.freeze(function (
    +
     5853.    357    source = "",
    +
     5854.    357    option_object = empty(),
    +
     5855.    357    global_array = []
    +
     5856.    357) {
    +
     5857.      3    if (option_object.cli_mode) {
    +
     5858.      3        return cli(Object.assign({
    +
     5859.      3            source
    +
     5860.      3        }, option_object));
    +
     5861.    354    }
    +
     5862.    354    return jslint(source, option_object, global_array);
    +
     5863.    354});
    +
     5864.      1// feature-detect nodejs-cli
    +
     5865.      1if (
    +
     5866.      1    typeof process === "object"
    +
     5867.      1    // uncomment when nodejs v12 is no longer used in ci
    +
     5868.      1    // && typeof process?.versions?.node === "string"
    +
     5869.      1    && process && process.versions
    +
     5870.      1    && typeof process.versions.node === "string"
    +
     5871.      1    && (
    +
     5872.      1        (/\bjslint.m?js$/m).test(process.argv[1])
    +
     5873.      1        || process.env.JSLINT_CLI === "1"
    +
     5874.      1    )
    +
     5875.      1) {
    +
     5876.      1    // run cli
    +
     5877.      1    cli({
    +
     5878.      1        file: process.argv[2]
    +
     5879.      1    }).then(function (exitCode) {
    +
     5880.      1        process.exit(exitCode);
    +
     5881.      1    });
    +
     5882.      1}
    +
     5883.      1
    + +
    +
    +
    + + diff --git a/branch.beta/.build/coverage/test.js.html b/branch.beta/.build/coverage/test.js.html new file mode 100644 index 000000000..839715aa2 --- /dev/null +++ b/branch.beta/.build/coverage/test.js.html @@ -0,0 +1,537 @@ + + + +coverage-report + + + +
    +
    coverage-report
    + + + + + + + + + + + +
    files coveredlines
    + ./ test.js
    +
    +
    +
    +
    + 100.00 %
    + 402 / 402 +
    +
    +
    +
        1.      1/*jslint node*/
    +
        2.      1import fs from "fs";
    +
        3.      1import jslint from "./jslint.js";
    +
        4.      1
    +
        5.    701function assertOrThrow(passed, msg) {
    +
        6.    701/*
    +
        7.    701 * this function will throw <msg> if <passed> is falsy
    +
        8.    701 */
    +
        9.      1    if (!passed) {
    +
       10.      1        throw new Error(msg);
    +
       11.      1    }
    +
       12.    701}
    +
       13.      1
    +
       14.      1function noop() {
    +
       15.      1/*
    +
       16.      1 * this function will do nothing
    +
       17.      1 */
    +
       18.      1    return;
    +
       19.      1}
    +
       20.      1
    +
       21.      1(function testCaseJslintCli() {
    +
       22.      1/*
    +
       23.      1 * this function will test jslint's cli handling-behavior
    +
       24.      1 */
    +
       25.      1    process.exit = function (exitCode) {
    +
       26.      1        assertOrThrow(!exitCode, exitCode);
    +
       27.      1    };
    +
       28.      1    jslint("", {
    +
       29.      1        cli_mode: true,
    +
       30.      1        file: "jslint.js"
    +
       31.      1    });
    +
       32.      1    jslint("", {
    +
       33.      1        cli_mode: true,
    +
       34.      1        // suppress error
    +
       35.      1        console_error: noop,
    +
       36.      1        file: "syntax_error.js",
    +
       37.      1        option: {
    +
       38.      1            debug: true
    +
       39.      1        },
    +
       40.      1        source: "syntax error"
    +
       41.      1    });
    +
       42.      1    jslint("", {
    +
       43.      1        cli_mode: true,
    +
       44.      1        file: "aa.html",
    +
       45.      1        source: "<script>\nlet aa = 0;\n</script>\n"
    +
       46.      1    });
    +
       47.      1}());
    +
       48.      1
    +
       49.      1(function testCaseJslintMisc() {
    +
       50.      1/*
    +
       51.      1 * this function will test jslint's misc handling-behavior
    +
       52.      1 */
    +
       53.      1    // test assertOrThrow's throw handling-behavior
    +
       54.      1    try {
    +
       55.      1        assertOrThrow(undefined, new Error());
    +
       56.      1    } catch (ignore) {}
    +
       57.      1}());
    +
       58.      1
    +
       59.      1(function testCaseJslintOption() {
    +
       60.      1/*
    +
       61.      1 * this function will test jslint's option handling-behavior
    +
       62.      1 */
    +
       63.      1    assertOrThrow(jslint([""], {
    +
       64.      1        bitwise: true,
    +
       65.      1        browser: true,
    +
       66.      1        convert: true,
    +
       67.      1        couch: true,
    +
       68.      1        debug: true,
    +
       69.      1        devel: true,
    +
       70.      1        eval: true,
    +
       71.      1        for: true,
    +
       72.      1        fudge: true,
    +
       73.      1        getset: true,
    +
       74.      1        long: true,
    +
       75.      1        node: true,
    +
       76.      1        single: true,
    +
       77.      1        this: true,
    +
       78.      1        white: true
    +
       79.      1    }).warnings.length === 0);
    +
       80.      1    assertOrThrow(jslint("", {
    +
       81.      1        test_internal_error: true
    +
       82.      1    }).warnings.length === 1);
    +
       83.      1}());
    +
       84.      1
    +
       85.      1(function testCaseJslintCodeValidate() {
    +
       86.      1/*
    +
       87.      1 * this function will validate each code is valid in jslint
    +
       88.      1 */
    +
       89.      1    Object.values({
    +
       90.      1        Array: [
    +
       91.      1            "new Array(1);"
    +
       92.      1        ],
    +
       93.      1        Date: [
    +
       94.      1            "Date.getTime();",
    +
       95.      1            "let aa = aa().getTime();",
    +
       96.      1            "let aa = aa.aa().getTime();"
    +
       97.      1        ],
    +
       98.      1        Number: [
    +
       99.      1            "let aa = 0.0e0;",
    +
      100.      1            "let aa = 0b0;",
    +
      101.      1            "let aa = 0o0;",
    +
      102.      1            "let aa = 0x0;"
    +
      103.      1        ],
    +
      104.      1        RegExp: [
    +
      105.      1            "function aa() {\n    return /./;\n}",
    +
      106.      1            "let aa = /(?!.)(?:.)(?=.)/;"
    +
      107.      1        ],
    +
      108.      1        async_await: [
    +
      109.      1            "async function aa() {\n    await aa();\n}"
    +
      110.      1        ],
    +
      111.      1        directives: [
    +
      112.      1            "#!\n/*jslint browser:false, node*/\n\"use strict\";",
    +
      113.      1            "/*jslint browser*/\n;",
    +
      114.      1            "/*jslint devel*/\ndebugger;",
    +
      115.      1            "/*jslint eval*/\nnew Function();\neval();",
    +
      116.      1            "/*jslint getset*/\nlet aa = {get aa() {\n    return;\n}};",
    +
      117.      1            "/*jslint getset*/\nlet aa = {set aa(aa) {\n    return aa;\n}};",
    +
      118.      1            "/*jslint this*/\nlet aa = this;",
    +
      119.      1            "/*jslint white*/\n\t",
    +
      120.      1            "/*property aa bb*/"
    +
      121.      1        ],
    +
      122.      1        fart: [
    +
      123.      1            "function aa() {\n    return () => 1;\n}"
    +
      124.      1        ],
    +
      125.      1        json: [
    +
      126.      1            "{\"aa\":[[],-1,null]}"
    +
      127.      1        ],
    +
      128.      1        label: [
    +
      129.      1            "function aa() {\nbb:\n    while (true) {\n        if (true) {\n"
    +
      130.      1            + "            break bb;\n        }\n    }\n}"
    +
      131.      1        ],
    +
      132.      1        loop: [
    +
      133.      1            "function aa() {\n    do {\n        aa();\n    } while (aa());\n}"
    +
      134.      1        ],
    +
      135.      1        misc: [
    +
      136.      1            ""
    +
      137.      1        ],
    +
      138.      1        module: [
    +
      139.      1            "export default Object.freeze();",
    +
      140.      1            "import {aa, bb} from \"aa\";\naa(bb);",
    +
      141.      1            "import {} from \"aa\";",
    +
      142.      1            "import(\"aa\").then(function () {\n    return;\n});"
    +
      143.      1        ],
    +
      144.      1        optional_chaining: [
    +
      145.      1            "let aa = aa?.bb?.cc;"
    +
      146.      1        ],
    +
      147.      1        property: [
    +
      148.      1            "let aa = aa[`!`];"
    +
      149.      1        ],
    +
      150.      1        ternary: [
    +
      151.      1            "let aa = (\n    aa()\n    ? 0\n    : 1\n) "
    +
      152.      1            + "&& (\n    aa()\n    ? 0\n    : 1\n);"
    +
      153.      1        ],
    +
      154.      1        var: [
    +
      155.      1            "let [...aa] = [...aa];",
    +
      156.      1            "let [\n    aa, bb = 1\n] = 0;",
    +
      157.      1            "let {aa, bb} = 0;",
    +
      158.      1            "let {\n    aa: bb\n} = 0;"
    +
      159.      1        ]
    +
      160.     16    }).forEach(function (codeList) {
    +
      161.     36        codeList.forEach(function (code) {
    +
      162.     36            let warnings;
    +
      163.     36            warnings = jslint(code).warnings;
    +
      164.     36            assertOrThrow(warnings.length === 0, [code, warnings]);
    +
      165.     36        });
    +
      166.     16    });
    +
      167.      1}());
    +
      168.      1
    +
      169.      1(async function testCaseJslintWarningsValidate() {
    +
      170.      1/*
    +
      171.      1 * this function will validate each jslint <warning> is raised with given
    +
      172.      1 * malformed <code>
    +
      173.      1 */
    +
      174.      1    Object.entries({
    +
      175.      1        expected_a_at_b_c: [
    +
      176.      1            "(function(){let aa;bb:while(aa()){aa();}}());",
    +
      177.      1            "function aa(){\n bb:while(aa){aa();}}",
    +
      178.      1            "let aa=aa(\naa\n()\n);",
    +
      179.      1            "let aa={\n    aa:\n0\n};"
    +
      180.      1        ],
    +
      181.      1        expected_a_b: [
    +
      182.      1            "([])=>0",
    +
      183.      1            "(aa)=>{}",
    +
      184.      1            "(aa?0:aa)",
    +
      185.      1            "(aa?aa:0)",
    +
      186.      1            "(aa?false:true)",
    +
      187.      1            "(aa?true:false)",
    +
      188.      1            ";{",
    +
      189.      1            "`${/ /}`",
    +
      190.      1            "`${`",
    +
      191.      1            "`${{`",
    +
      192.      1            "aa.aa=undefined",
    +
      193.      1            "aa=+aa",
    +
      194.      1            "aa=/[ ]/",
    +
      195.      1            "aa=/aa{/",
    +
      196.      1            "aa=0+\"\"",
    +
      197.      1            "aa=\"\"+\"\"",
    +
      198.      1            "async",
    +
      199.      1            "delete [0]",
    +
      200.      1            "for(;;){}",
    +
      201.      1            "isFinite(0)",
    +
      202.      1            "let aa;var aa;",
    +
      203.      1            "new Array(\"\")",
    +
      204.      1            "new Date().getTime()",
    +
      205.      1            "new Object()"
    +
      206.      1        ],
    +
      207.      1        expected_a_before_b: [
    +
      208.      1            ".0",
    +
      209.      1            "/*jslint eval*/\nFunction;eval",
    +
      210.      1            "=>0",
    +
      211.      1            "\"\\u{12345\"",
    +
      212.      1            "aa=/(:)/",
    +
      213.      1            "aa=/=/",
    +
      214.      1            "aa=/?/",
    +
      215.      1            "aa=/[/",
    +
      216.      1            "let Aa=Aa()",
    +
      217.      1            "let Aa=Aa.Aa()",
    +
      218.      1            "new Aa"
    +
      219.      1        ],
    +
      220.      1        expected_identifier_a: [
    +
      221.      1            "(0)=>0",
    +
      222.      1            "aa.0",
    +
      223.      1            "aa?.0",
    +
      224.      1            "function aa(0){}",
    +
      225.      1            "function aa([aa]){}\nfunction aa([aa],[aa,aa=aa],[0]){}",
    +
      226.      1            "function aa({aa}){}\nfunction aa({aa},{aa:aa,aa=aa},{aa:0}){}",
    +
      227.      1            "function(){}",
    +
      228.      1            "import {",
    +
      229.      1            "let aa={0:0}",
    +
      230.      1            "let {0}=0",
    +
      231.      1            "let {aa:0}=0"
    +
      232.      1        ],
    +
      233.      1        expected_space_a_b: [
    +
      234.      1            "(function(){return;}());",
    +
      235.      1            "/**//**/",
    +
      236.      1            "let aa=(aa?0:1);",
    +
      237.      1            "let aa=0;"
    +
      238.      1        ],
    +
      239.      1        required_a_optional_b: [
    +
      240.      1            "function aa(aa=0,...){}",
    +
      241.      1            "function aa(aa=0,[]){}",
    +
      242.      1            "function aa(aa=0,{}){}",
    +
      243.      1            "function aa(aa=0,bb){}"
    +
      244.      1        ],
    +
      245.      1        too_long: [
    +
      246.      1            "//".repeat(100)
    +
      247.      1        ],
    +
      248.      1        unexpected_a: [
    +
      249.      1            "((0))",
    +
      250.      1            "(+0?+0:+0)()",
    +
      251.      1            "(/./)?.foo",
    +
      252.      1            "/*/",
    +
      253.      1            "/./",
    +
      254.      1            "0===(0==0)",
    +
      255.      1            "0[0][0]",
    +
      256.      1            "0|0",
    +
      257.      1            ";",
    +
      258.      1            "Function",
    +
      259.      1            "[-0x0]",
    +
      260.      1            "[0x0]",
    +
      261.      1            "\"aa\"?.bb",
    +
      262.      1            "`${/[`]/}`",
    +
      263.      1            "`${/`/}`",
    +
      264.      1            "`${\"`\"}`",
    +
      265.      1            "aa((0))",
    +
      266.      1            "aa+=NaN",
    +
      267.      1            "aa/=0",
    +
      268.      1            "aa=/[0-]/",
    +
      269.      1            "aa=/.//",
    +
      270.      1            "aa=/./z",
    +
      271.      1            "aa={aa:aa}",
    +
      272.      1            "aa={set aa(){}}",
    +
      273.      1            "arguments",
    +
      274.      1            "await",
    +
      275.      1            "debugger",
    +
      276.      1            "eval",
    +
      277.      1            "export aa",
    +
      278.      1            "export const aa=0",
    +
      279.      1            "for(aa in aa){}",
    +
      280.      1            "for(const ii=0;;){}",
    +
      281.      1            "for(ii=0;ii<0;ii++){}",
    +
      282.      1            "for(ii=0;ii<0;ii+=0){}",
    +
      283.      1            "function aa(){for(0;0;0){break;}}",
    +
      284.      1            "function aa(){try{return;}catch(ignore){}finally{return;}}",
    +
      285.      1            "function aa(){try{}catch(ignore){}finally{switch(0){case 0:}}}",
    +
      286.      1            "function aa(){while(0){continue;}}",
    +
      287.      1            "function aa(){while(0){try{0;}catch(ignore){}finally{continue;}}}",
    +
      288.      1            "function aa(){}0",
    +
      289.      1            "function aa(){}\n[]",
    +
      290.      1            "function ignore(){let ignore;}",
    +
      291.      1            "ignore",
    +
      292.      1            "ignore:",
    +
      293.      1            "import ignore from \"aa\"",
    +
      294.      1            "import {ignore} from \"aa\"",
    +
      295.      1            "let aa=[]?.bb",
    +
      296.      1            "new Date.UTC()",
    +
      297.      1            "new Function()",
    +
      298.      1            "new Symbol()",
    +
      299.      1            "switch(0){case 0:break;case 0:break}",
    +
      300.      1            "switch(0){case 0:break;default:break;}",
    +
      301.      1            "switch(0){case 0:break;default:}",
    +
      302.      1            "this",
    +
      303.      1            "try{throw 0;try{}catch(){}}catch(){}",
    +
      304.      1            "try{}finally{break;}",
    +
      305.      1            "void 0",
    +
      306.      1            "while((0)){}",
    +
      307.      1            "while(0){}",
    +
      308.      1            "{//\n}",
    +
      309.      1            "{0:0}",
    +
      310.      1            "{\"\\u{1234}\":0}",
    +
      311.      1            "{\"aa\":",
    +
      312.      1            "{\"aa\":'aa'}"
    +
      313.      1        ]
    +
      314.      8    }).forEach(function ([
    +
      315.      8        expectedWarning, malformedCodeList
    +
      316.      8    ]) {
    +
      317.    123        malformedCodeList.forEach(function (malformedCode) {
    +
      318.    123            assertOrThrow(
    +
      319.    197                jslint(malformedCode).warnings.some(function ({
    +
      320.    197                    code
    +
      321.    197                }) {
    +
      322.    197                    return code === expectedWarning;
    +
      323.    197                }),
    +
      324.    123                new Error(
    +
      325.    123                    `jslint failed to warn "${expectedWarning}" with `
    +
      326.    123                    + `malfomed code "${malformedCode}"`
    +
      327.    123                )
    +
      328.    123            );
    +
      329.    123        });
    +
      330.      8    });
    +
      331.      1    Array.from(String(
    +
      332.      1        await fs.promises.readFile("jslint.js", "utf8")
    +
      333.      1    ).matchAll(new RegExp((
    +
      334.      1        "\\s*?"
    +
      335.      1        + "(\\/\\/\\s*?cause:.*?\\n(?:\\/\\/.*?\\n)*?)"
    +
      336.      1        + "(\\s*?\\n[^\\/].*?(?:\\n\\s*?\".*?)?$)"
    +
      337.    152    ), "gm"))).forEach(function ([
    +
      338.    152        match0, causeList, warning
    +
      339.    152    ]) {
    +
      340.    152        let expectedWarningCode;
    +
      341.    152        let fnc;
    +
      342.    152        // debug match0
    +
      343.    152        console.error(match0.trim().replace((/\n\n/g), "\n"));
    +
      344.    152        assertOrThrow(
    +
      345.    152            match0.indexOf("\n\n" + causeList + "\n    ") === 0,
    +
      346.    152            JSON.stringify([
    +
      347.    152                match0, causeList
    +
      348.    152            ], undefined, 4)
    +
      349.    152        );
    +
      350.    152        warning = warning.match(
    +
      351.    152            "("
    +
      352.    152            + "expected_at"
    +
      353.    152            + "|no_space_only"
    +
      354.    152            + "|one_space_only"
    +
      355.    152            + "|one_space"
    +
      356.    152            + "|stop"
    +
      357.    152            + "|stop_at"
    +
      358.    152            + "|warn"
    +
      359.    152            + "|warn_at"
    +
      360.    152            + ")"
    +
      361.    152            + "\\\u0028\\s*?\"?"
    +
      362.    152            + "(\\S[^\n\"]+)"
    +
      363.    152        );
    +
      364.    124        if (warning) {
    +
      365.    124            expectedWarningCode = warning[2];
    +
      366.    124            fnc = warning[1];
    +
      367.    124            switch (fnc) {
    +
      368.    124            case "expected_at":
    +
      369.    124                expectedWarningCode = "expected_a_at_b_c";
    +
      370.    124                break;
    +
      371.    124            case "no_space_only":
    +
      372.    124                expectedWarningCode = "unexpected_space_a_b";
    +
      373.    124                break;
    +
      374.    124            case "one_space":
    +
      375.    124            case "one_space_only":
    +
      376.    124                expectedWarningCode = "expected_space_a_b";
    +
      377.    124                break;
    +
      378.    124            }
    +
      379.    124        }
    +
      380.    152        causeList.split(
    +
      381.    152            /\/\/\u0020cause:[\n|\u0020]/
    +
      382.    193        ).slice(1).forEach(function (cause) {
    +
      383.    193            assertOrThrow(cause === cause.trim() + "\n", JSON.stringify(cause));
    +
      384.    193            cause = (
    +
      385.    193                cause[0] === "\""
    +
      386.    187                ? JSON.parse(cause)
    +
      387.      6                : cause.replace((
    +
      388.      6                    /^\/\/\u0020/gm
    +
      389.      6                ), "")
    +
      390.    193            );
    +
      391.    193            assertOrThrow(
    +
      392.    356                jslint(cause).warnings.some(function ({
    +
      393.    356                    code
    +
      394.    356                }) {
    +
      395.    356                    return code === expectedWarningCode;
    +
      396.    356                }) || !expectedWarningCode,
    +
      397.    193                "\n" + cause.trim()
    +
      398.    193            );
    +
      399.    193        });
    +
      400.    152    });
    +
      401.      1}());
    +
      402.      1
    + +
    +
    +
    + + diff --git a/branch.beta/.build/screenshot.browser._2fjslint_2fbranch.beta_2findex.html.html b/branch.beta/.build/screenshot.browser._2fjslint_2fbranch.beta_2findex.html.html new file mode 100644 index 000000000..3af9f72aa --- /dev/null +++ b/branch.beta/.build/screenshot.browser._2fjslint_2fbranch.beta_2findex.html.html @@ -0,0 +1,446 @@ + + + + + + + +JSLint: The JavaScript Code Quality Tool + + + + + +
    +
    Source
    +
    + + +
    + +
    + Function Report +
    module
    Promise, console, https, jslint
    import from
    ./jslint.js, https
    4
    «async»()
    variable
    result
    closure
    result
    module
    Promise, jslint
    6
    «Promise»(resolve)
    parameter
    resolve
    closure
    resolve
    module
    https
    7
    «request»(res)
    parameter
    res
    outer
    result
    9
    «data»(chunk)
    parameter
    chunk
    outer
    result
    11
    «end»()
    outer
    resolve, result
    17
    «forEach»({formatted_message})
    parameter
    formatted_message
    module
    console
    JSLint edition v2021.5.27
    +
    +
    + Property Directive + +
    +
    + + + + +
    +
    Options +
    Assume... +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Global variables... + +
    +
    +
    + + + diff --git a/branch.beta/.build/screenshot.browser._2fjslint_2fbranch.beta_2findex.html.png b/branch.beta/.build/screenshot.browser._2fjslint_2fbranch.beta_2findex.html.png new file mode 100644 index 000000000..ac4e732a2 Binary files /dev/null and b/branch.beta/.build/screenshot.browser._2fjslint_2fbranch.beta_2findex.html.png differ diff --git a/branch.beta/.gitconfig b/branch.beta/.gitconfig new file mode 100644 index 000000000..a7cd56f37 --- /dev/null +++ b/branch.beta/.gitconfig @@ -0,0 +1,25 @@ +[branch "alpha"] + merge = refs/heads/alpha + remote = origin +[branch "base"] + merge = refs/heads/base + remote = origin +[core] + # autocrlf = false + autocrlf = input + bare = false + # filemode = false + logallrefupdates = true + repositoryformatversion = 0 +[diff] + algorithm = histogram +[pull] + ff = only +[receive] + denyCurrentBranch = warn +[remote "origin"] + fetch = +refs/heads/*:refs/remotes/origin/* + url = https://github.com/user/jslint +[remote "upstream"] + fetch = +refs/heads/*:refs/remotes/upstream/* + url = https://github.com/jslint-org/jslint diff --git a/branch.beta/.github/workflows/ci.yml b/branch.beta/.github/workflows/ci.yml new file mode 100644 index 000000000..5d4c0ca5e --- /dev/null +++ b/branch.beta/.github/workflows/ci.yml @@ -0,0 +1,51 @@ +# this workflow will run nodejs coverages and tests and +# upload build-artifacts to branch-gh-pages +name: CI +on: + push: + branches: + - alpha + - beta + - master + - sandbox +# shCiBase - start +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + architecture: + # - arm64 + - x64 + # - x86 + node_version: + - 12 + - 14 + - 16 + os: + - macos-latest + - ubuntu-latest + - windows-latest + name: node . v${{ matrix.node_version }} . ${{ matrix.architecture }} . ${{ matrix.os }} + steps: + # disable autocrlf in windows + - run: git config --global core.autocrlf false + # https://github.com/actions/checkout + - uses: actions/checkout@v2 + # https://github.com/actions/setup-node + - uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node_version }} + architecture: ${{ matrix.architecture }} + # fetch ci.sh from trusted source + - run: git fetch origin alpha && git checkout origin/alpha ci.sh + # run nodejs coverages and tests + - run: sh ci.sh shCiBase +# shCiBase - end + # fetch ci.sh from trusted source + - run: git fetch origin alpha && git checkout origin/alpha ci.sh + # upload build-artifacts to branch-gh-pages + - run: sh ci.sh shCiArtifactUpload + env: + CI_NODE_VERSION_ARCH_PLATFORM: v14.x64.linux + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/branch.beta/.github/workflows/on_pull_request.yml b/branch.beta/.github/workflows/on_pull_request.yml new file mode 100644 index 000000000..9274873fc --- /dev/null +++ b/branch.beta/.github/workflows/on_pull_request.yml @@ -0,0 +1,36 @@ +# this workflow will run nodejs coverages and tests +name: on_pull_request +on: + - pull_request +# shCiBase - start +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + architecture: + # - arm64 + - x64 + # - x86 + node_version: + - 12 + - 14 + - 16 + os: + - macos-latest + - ubuntu-latest + - windows-latest + name: node . v${{ matrix.node_version }} . ${{ matrix.architecture }} . ${{ matrix.os }} + steps: + # https://github.com/actions/checkout + - uses: actions/checkout@v2 + # https://github.com/actions/setup-node + - uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node_version }} + architecture: ${{ matrix.architecture }} + # fetch ci.sh from trusted source + - run: git fetch origin alpha && git checkout origin/alpha ci.sh + # run nodejs coverages and tests + - run: sh ci.sh shCiBase +# shCiBase - end diff --git a/branch.beta/.gitignore b/branch.beta/.gitignore new file mode 100644 index 000000000..da69da6f4 --- /dev/null +++ b/branch.beta/.gitignore @@ -0,0 +1,18 @@ +"* +'* +*.[0123456789][0123456789] +*.lock +*.log +*.pyc +*~ +.* +_* +node_modules +package-lock.json +temp* +tmp +undefined + +!.gitconfig +!.github +!.gitignore diff --git a/branch.beta/CHANGELOG.md b/branch.beta/CHANGELOG.md new file mode 100644 index 000000000..f599a7858 --- /dev/null +++ b/branch.beta/CHANGELOG.md @@ -0,0 +1,79 @@ +# Changelog + +## Todo +- app - deploy jslint as chrome-extension. +- ci - continue addng regression tests and improve code-coverage. +- doc - add svg changelog. +- doc - add svg package-listing. +- jslint - add html and css linting back into jslint. +- jslint - cleanup regexp code using switch-statements. +- node - after node-v12 is deprecated, change require("fs").promises to require("fs/promises"). +- node - after node-v14 is deprecated, remove shell-code export "NODE_OPTIONS=--unhandled-rejections=strict". +- none + +## v2021.5.27 +- ci - fix expectedWarningCode not being validated. +- ci - in windows, disable git-autocrlf. +- deadcode - replace with assertion-check in function are_similar() - "if (a === b) { return true }". +- deadcode - replace with assertion-check in function are_similar() superseded by id-check - "if (Array.isArray(b)) { return false; }". +- deadcode - replace with assertion-check in function are_similar() superseded by is_weird() check - "if (a.arity === "function" && a.arity ===...c". +- jslint - add directive `test_internal_error`. +- jslint - add directive `unordered` to tolerate unordered properties and params. +- jslint - inline-document each warning with cause that can reproduce it - part 1. +- style - refactor code moving infix-operators from post-position to pre-position in multiline statements. +- website - add hotkey ctrl-enter to run jslint. +- none + +## v2021.5.26 +- ci - fix ci silently failing in node-v12 and node-v14. +- cli - add env var JSLINT_CLI to force-trigger cli in jslint.js (used for code-coverage of cli). +- jslint - add "globalThis" to default globals. +- jslint - add new rules unordered_param_a, unordered_property_a, that warn if parameters and properties are listed in nonascii-order. +- jslint - fix bug where (global) functionage missing properties finally and try. +- jslint - fix bug failing to parse unicode "\\u{12345}". +- jslint - fix bug falsely warning against conditional-chaining-operator "?.". +- jslint - remove deadcode for preaction-binary-".". +- jslint - remove deadcode warning bad_option_a. +- website - add fork-me ribbon. +- website - load index.html with example code. +- website - merge file report.js into browser.js. + +## v2021.5.23 +- doc - add section Changelog. +- doc - update README.md with installation instructions. +- cli - merge shell-function shJslintCli into jslint.js. +- jslint - update default globals with support for "import". +- jslint - sort warnings with higher priority for early_stop. +- jslint - add async/await support. +- ci - make branch-beta the default branch. +- ci - validate non-http/file links in *.md files. +- ci - add shell-functions shCiBranchPromote. + +## v2021.5.21 +- this ci-release does not change any core-functionality of file jslint.js. +- doc - add file CHANGELOG.md. +- ci - begin addng regression tests and improve code-coverage. +- ci - allow pull-requests to run restricted-ci (cannot upload artifacts). +- gh-pages - fix missing assets and insecure http-links. +- gh-pages - merge file jslint.css into index.html. +- gh-pages - add files image-jslint-xxx.png. +- gh-pages - cleanup asset naming-convention. +- fix missing fonts in function.html and help.html. +- add files .gitconfig, Daley-Bold.woff2, Programma-Bold.woff2, icon-folder-open-solid.svg, icon-window-maximize-regular.svg. +- ci - fix http-links after moving to jslint-org. +- doc - migrate file README to README.md with embedded ci links and screenshots. +- ci - add macos and windows to ci-matrix. +- ci - ci now fails if jslint-check fails for any of the files in branches. +- ci - add github-workflows to generate code-coverage for jslint.js. + +## v2020.11.6 +- last jslint version before jslint-org migration. + +## v2018.4.25 +- last jslint version written in commonjs. + +## v2017.11.6 +- last jslint version written in es5. + +## v2013.3.13 +- last jslint version that can lint .html and .css files. diff --git a/branch.beta/README.md b/branch.beta/README.md new file mode 100644 index 000000000..673adb95f --- /dev/null +++ b/branch.beta/README.md @@ -0,0 +1,84 @@ +# JSLint, The JavaScript Code Quality Tool + +Douglas Crockford +douglas@crockford.com + +## Status +| Branch | [master
    (v2021.5.27)](https://github.com/kaizhu256/jslint/tree/master) | [beta
    (testing)](https://github.com/kaizhu256/jslint/tree/beta) | [alpha
    (development)](https://github.com/kaizhu256/jslint/tree/alpha) | +|--:|:--:|:--:|:--:| +| CI | [![ci](https://github.com/kaizhu256/jslint/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/kaizhu256/jslint/actions?query=branch%3Amaster) | [![ci](https://github.com/kaizhu256/jslint/actions/workflows/ci.yml/badge.svg?branch=beta)](https://github.com/kaizhu256/jslint/actions?query=branch%3Abeta) | [![ci](https://github.com/kaizhu256/jslint/actions/workflows/ci.yml/badge.svg?branch=alpha)](https://github.com/kaizhu256/jslint/actions?query=branch%3Aalpha) | +| Coverage | [![coverage](https://kaizhu256.github.io/jslint/branch.master/.build/coverage/coverage-badge.svg)](https://kaizhu256.github.io/jslint/branch.master/.build/coverage/index.html) | [![coverage](https://kaizhu256.github.io/jslint/branch.beta/.build/coverage/coverage-badge.svg)](https://kaizhu256.github.io/jslint/branch.beta/.build/coverage/index.html) | [![coverage](https://kaizhu256.github.io/jslint/branch.alpha/.build/coverage/coverage-badge.svg)](https://kaizhu256.github.io/jslint/branch.alpha/.build/coverage/index.html) | +| Demo | [](https://kaizhu256.github.io/jslint/branch.master/index.html) | [](https://kaizhu256.github.io/jslint/branch.beta/index.html) | [](https://kaizhu256.github.io/jslint/branch.alpha/index.html) | +| Artifacts | [](https://github.com/kaizhu256/jslint/tree/gh-pages/branch.master/.build) | [](https://github.com/kaizhu256/jslint/tree/gh-pages/branch.beta/.build) | [](https://github.com/kaizhu256/jslint/tree/gh-pages/branch.alpha/.build) | + +## Live Web Demo +- [https://kaizhu256.github.io/jslint/index.html](https://kaizhu256.github.io/jslint/index.html) + +[![screenshot](https://kaizhu256.github.io/jslint/branch.beta/.build/screenshot.browser._2fjslint_2fbranch.beta_2findex.html.png)](https://kaizhu256.github.io/jslint/index.html) + +## Installation +1. Download [https://www.jslint.com/jslint.js](https://www.jslint.com/jslint.js) and rename to `jslint.mjs` +```shell +#!/bin/sh +curl -L https://www.jslint.com/jslint.js > jslint.mjs +``` + +2. To run `jslint.mjs` from command-line: +```shell +#!/bin/sh +node jslint.mjs hello.js + +# stderr: +# jslint hello.js +# 1 Use double quotes, not single quotes. // line 1, column 14 +# console.log('hello world'); +``` + +3. To load `jslint.mjs` as es-module: +```javascript +/*jslint devel*/ +import jslint from "./jslint.mjs"; +let code = "console.log('hello world');\n"; +let result = jslint(code); +result.warnings.forEach(function ({ + formatted_message +}) { + console.error(formatted_message); +}); + +// stderr: +// 1 Undeclared 'console'. // line 1, column 1 +// console.log('hello world'); +// 2 Use double quotes, not single quotes. // line 1, column 14 +// console.log('hello world'); +``` + +## Description +- [jslint.js](jslint.js) contains the jslint function. It parses and analyzes a source file, returning an object with information about the file. It can also take an object that sets options. + +- [index.html](index.html) runs the jslint.js function in a web page. The page also depends on `browser.js`. + +- [browser.js](browser.js) runs the web user interface and generates the results reports in HTML. + +- [help.html](help.html) describes JSLint's usage. Please [read it](https://kaizhu256.github.io/jslint/help.html). + +- [function.html](function.html) describes the jslint function and the results it produces. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[https://www.crockford.com/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. + +## Changelog +- [CHANGELOG.md](CHANGELOG.md) diff --git a/branch.beta/browser.js b/branch.beta/browser.js new file mode 100644 index 000000000..14bceb0e8 --- /dev/null +++ b/branch.beta/browser.js @@ -0,0 +1,406 @@ +// browser.js +// 2018-06-16 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +/*jslint + browser +*/ + +/*property + addEventListener, ctrlKey, key, + checked, closure, column, context, create, disable, display, edition, + exports, filter, focus, forEach, froms, fudge, functions, getElementById, + global, id, innerHTML, isArray, join, json, keys, length, level, line, + lines, map, message, module, name, names, onchange, onclick, onscroll, + option, parameters, parent, property, push, querySelectorAll, replace, role, + scrollTop, select, signature, sort, split, stop, style, title, trim, value, + warnings +*/ + +import jslint from "./jslint.js"; + +// This is the web script companion file for JSLint. It includes code for +// interacting with the browser and displaying the reports. + +let rx_crlf = /\n|\r\n?/; +let rx_separator = /[\s,;'"]+/; + +let fudge_unit = 0; + +const elem_aux = document.getElementById("JSLINT_AUX"); +const elem_boxes = document.querySelectorAll("[type=checkbox]"); +const elem_fudge = document.getElementById("JSLINT_FUDGE"); +const elem_global = document.getElementById("JSLINT_GLOBAL"); +const elem_number = document.getElementById("JSLINT_NUMBER"); +const elem_property = document.getElementById("JSLINT_PROPERTY"); +const elem_property_fieldset = document.getElementById( + "JSLINT_PROPERTYFIELDSET" +); +const elem_report_field = document.getElementById("JSLINT_REPORT"); +const elem_report_list = document.getElementById("JSLINT_REPORT_LIST"); +const rx_amp = /&/g; +const rx_gt = />/g; +const rx_lt = / with less destructive entities. + + return String( + string + ).replace( + rx_amp, + "&" + ).replace( + rx_lt, + "<" + ).replace( + rx_gt, + ">" + ); +} + +function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + let fudge = Number(Boolean(data.option.fudge)); + let output = []; + if (data.stop) { + output.push("
    JSLint was unable to finish.
    "); + } + data.warnings.forEach(function (warning) { + output.push( + "
    ", + entityify(warning.line + fudge), + ".", + entityify(warning.column + fudge), + "
    ", + entityify(warning.message), + "
    ", + entityify(data.lines[warning.line] || ""), + "" + ); + }); + return output.join(""); +} + +function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + let fudge = Number(Boolean(data.option.fudge)); + let mode = ( + data.module + ? "module" + : "global" + ); + let output = []; + + if (data.json) { + return ( + data.warnings.length === 0 + ? "
    JSON: good.
    " + : "
    JSON: bad.
    " + ); + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + let global = Object.keys(data.global.context).sort(); + let froms = data.froms.sort(); + let exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + let context = the_function.context; + let list = Object.keys(context); + output.push( + "
    ", + entityify(the_function.line + fudge), + "
    ", + ( + the_function.name === "=>" + ? entityify(the_function.signature) + " =>" + : ( + typeof the_function.name === "string" + ? "«" + entityify(the_function.name) + "»" + : "" + entityify(the_function.name.id) + "" + ) + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + let params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.parent === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.parent === the_function + ); + })); + detail("outer", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.parent !== the_function + && the_variable.parent.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].parent.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + output.push( + "
    JSLint edition ", + entityify(data.edition), + "
    " + ); + return output.join(""); +} + +function property_directive(data) { + +// Produce the /*property*/ directive. + + let not_first = false; + let output = ["/*property"]; + let length = 1111; + let properties = Object.keys(data.property); + + if (properties.length > 0) { + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length >= 80) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); + } +} + +function show_numbers() { + elem_number.value = elem_source.value.split(rx_crlf).map(function ( + ignore, + index + ) { + return index + fudge_unit; + }).join("\n"); +} + +function clear() { + elem_aux.style.display = "none"; + elem_number.value = ""; + elem_property.value = ""; + elem_property_fieldset.style.display = "none"; + elem_report_field.style.display = "none"; + elem_report_list.innerHTML = ""; + elem_source.focus(); + elem_source.value = ""; + elem_warnings.style.display = "none"; + elem_warnings_list.innerHTML = ""; +} + +function fudge_change() { + fudge_unit = Number(elem_fudge.checked); + show_numbers(); +} + +function clear_options() { + elem_boxes.forEach(function (node) { + node.checked = false; + }); + fudge_change(); + elem_global.innerHTML = ""; +} + +function call_jslint() { + +// First build the option object. + + let option = Object.create(null); + elem_boxes.forEach(function (node) { + if (node.checked) { + option[node.title] = true; + } + }); + +// Call JSLint with the source text, the options, and the predefined globals. + + let global_string = elem_global.value; + let result = jslint( + elem_source.value, + option, + ( + global_string === "" + ? undefined + : global_string.split(rx_separator) + ) + ); + +// Generate the reports. + + let error_html = error_report(result); + let function_html = function_report(result); + let property_text = property_directive(result); + +// Display the reports. + + elem_warnings_list.innerHTML = error_html; + elem_warnings.style.display = ( + error_html.length === 0 + ? "none" + : "block" + ); + + elem_report_list.innerHTML = function_html; + elem_report_field.style.display = "block"; + if (property_text) { + elem_property.value = property_text; + elem_property_fieldset.style.display = "block"; + elem_property.scrollTop = 0; + elem_select.disable = false; + } else { + elem_property_fieldset.style.display = "none"; + elem_select.disable = true; + } + elem_aux.style.display = "block"; + elem_source.select(); +} + +elem_fudge.onchange = fudge_change; + +elem_source.onchange = function (ignore) { + show_numbers(); +}; + +elem_source.onscroll = function () { + let ss = elem_source.scrollTop; + elem_number.scrollTop = ss; + let sn = elem_number.scrollTop; + if (ss > sn) { + show_numbers(); + elem_number.scrollTop = ss; + } +}; + +document.addEventListener("keydown", function (evt) { + if (evt.ctrlKey && evt.key === "Enter") { + call_jslint(); + } +}); + +document.querySelectorAll("[name='JSLint']").forEach(function (node) { + node.onclick = call_jslint; +}); + +document.querySelectorAll("[name='clear']").forEach(function (node) { + node.onclick = clear; +}); + +document.getElementById("JSLINT_SELECT").onclick = function () { + elem_property.focus(); + elem_property.select(); +}; +document.getElementById("JSLINT_CLEAR_OPTIONS").onclick = clear_options; + +fudge_change(); +elem_source.select(); +elem_source.focus(); +elem_source.value = String(` +#!/usr/bin/env node +/*jslint devel*/ +import jslint from "./jslint.js"; +import https from "https"; +(async function () { + let result; + result = await new Promise(function (resolve) { + https.request("https://www.jslint.com/jslint.js", function (res) { + result = ""; + res.on("data", function (chunk) { + result += chunk; + }).on("end", function () { + resolve(result); + }).setEncoding("utf8"); + }).end(); + }); + result = jslint(result); + result.warnings.forEach(function ({ + formatted_message + }) { + console.error(formatted_message); + }); +}()); +`).trim(); +elem_source.onchange(); +call_jslint(); diff --git a/branch.beta/ci.sh b/branch.beta/ci.sh new file mode 100755 index 000000000..d272a30f9 --- /dev/null +++ b/branch.beta/ci.sh @@ -0,0 +1,1085 @@ +#!/bin/sh + +# sh one-liner +# head CHANGELOG.md -n20 +# git fetch origin alpha beta master && git fetch upstream alpha beta master +# sh ci.sh shCiBranchPromote origin alpha beta + +shBrowserScreenshot() {(set -e +# this function will run headless-chrome to screenshot url $1 with +# window-size $2 + node -e ' +// init debugInline +if (!globalThis.debugInline) { + let consoleError; + consoleError = console.error; + globalThis.debugInline = function (...argList) { + /* + * this function will both print to stderr and + * return [0] + */ + consoleError("\n\ndebugInline"); + consoleError(...argList); + consoleError("\n"); + return argList[0]; + }; +} +(function () { + "use strict"; + let file; + let timeStart; + let url; + if (process.platform !== "linux") { + return; + } + timeStart = Date.now(); + url = process.argv[1]; + if (!( + /^\w+?:/ + ).test(url)) { + url = require("path").resolve(url); + } + file = require("url").parse(url).pathname; + // remove prefix $PWD from file + if (String(file + "/").indexOf(process.cwd() + "/") === 0) { + file = file.replace(process.cwd(), ""); + } + file = ".build/screenshot.browser." + encodeURIComponent(file).replace(( + /%/g + ), "_").toLowerCase(); + process.on("exit", function (exitCode) { + if (typeof exitCode === "object" && exitCode) { + console.error(exitCode); + exitCode = 1; + } + console.error( + "shBrowserScreenshot" + + "\n - url - " + url + + "\n - wrote - " + file + ".html" + + "\n - wrote - " + file + ".png" + + "\n - timeElapsed - " + (Date.now() - timeStart) + " ms" + + "\n - EXIT_CODE=" + exitCode + ); + }); + [ + ".html", ".png" + ].forEach(function (extname) { + let argList; + let child; + argList = Array.from([ + "--headless", + "--ignore-certificate-errors", + "--incognito", + "--timeout=30000", + "--user-data-dir=/dev/null", + "--window-size=" + (process.argv[2] || "800x600"), + ( + extname === ".html" + ? "--dump-dom" + : "" + ), + ( + extname === ".png" + ? "--screenshot" + : "" + ), + ( + extname === ".png" + ? "-screenshot=" + file + ".png" + : "" + ), + ( + (process.getuid && process.getuid() === 0) + ? "--no-sandbox" + : "" + ), + url + ]).filter(function (elem) { + return elem; + }); + // debug argList + // console.error(argList); + child = require("child_process").spawn(( + process.platform === "darwin" + ? "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" + : process.platform === "win32" + ? "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe" + : "/usr/bin/google-chrome-stable" + ), argList, { + stdio: [ + "ignore", "pipe", 2 + ] + }); + child.stdout.pipe( + extname === ".html" + ? require("fs").createWriteStream(file + ".html") + : process.stdout + ); + }); +}()); +' "$@" # "' +)} + +shCiArtifactUpload() {(set -e +# this function will upload build-artifacts to branch-gh-pages + export NODE_OPTIONS="--unhandled-rejections=strict" + node -e ' +process.exit( + `${process.version.split(".")[0]}.${process.arch}.${process.platform}` !== + process.env.CI_NODE_VERSION_ARCH_PLATFORM +); +' || return 0 + local BRANCH + # init $BRANCH + BRANCH="$(git rev-parse --abbrev-ref HEAD)" + # init .git/config + git config --local user.email "github-actions@users.noreply.github.com" + git config --local user.name "github-actions" + # update README.md with $GITHUB_REPOSITORY + sed -i \ + -e "s|\bjslint-org/jslint\b|$GITHUB_REPOSITORY|g" \ + -e "s|\bjslint-org\.github\.io/jslint\b|$( + printf "$GITHUB_REPOSITORY" | sed -e "s|/|.github.io/|" + )|g" \ + README.md + # add dir .build + git add -f .build + git commit -am "add dir .build" + # checkout branch-gh-pages + git checkout -b gh-pages + git fetch origin gh-pages + git reset --hard origin/gh-pages + # update dir branch.$BRANCH + rm -rf "branch.$BRANCH" + mkdir "branch.$BRANCH" + (set -e + cd "branch.$BRANCH" + git init -b branch1 + git pull --depth=1 .. "$BRANCH" + rm -rf .git + git add -f . + ) + # update root-dir with branch-master + if [ "$BRANCH" = master ] + then + git rm -rf .build + git checkout master . + fi + git status + git commit -am "update dir branch.$BRANCH" || true + # if branch-gh-pages has more than 100 commits, + # then backup and squash commits + if [ "$(git rev-list --count gh-pages)" -gt 100 ] + then + # backup + shGitCmdWithGithubToken push origin -f gh-pages:gh-pages.backup + # squash commits + git checkout --orphan squash1 + git commit --quiet -am squash || true + # reset branch-gh-pages to squashed-commit + git push . -f squash1:gh-pages + git checkout gh-pages + # force-push squashed-commit + shGitCmdWithGithubToken push origin -f gh-pages + fi + # list files + shGitLsTree + # push branch-gh-pages + shGitCmdWithGithubToken push origin gh-pages + # validate http-links + (set -e + cd "branch.$BRANCH" + sleep 15 + shDirHttplinkValidate + ) +)} + +shCiBase() {(set -e +# this function will run base-ci + export NODE_OPTIONS="--unhandled-rejections=strict" + # run test with coverage-report + # coverage-hack - test jslint's invalid-file handling-behavior + mkdir -p .test-dir.js + # coverage-hack - test jslint's ignore-file handling-behavior + touch .test-min.js + # test jslint's cli handling-behavior + ./jslint.js . + (set -e + # coverage-hack - test jslint's cli handling-behavior + export JSLINT_CLI=1 + shRunWithCoverage node test.js . + ) + # screenshot live-web-demo + shBrowserScreenshot \ + https://jslint-org.github.io/jslint/branch.beta/index.html +)} + +shCiBranchPromote() {(set -e +# this function will promote branch $REMOTE/$BRANCH1 to branch $REMOTE/$BRANCH2 + local BRANCH1 + local BRANCH2 + local REMOTE + REMOTE="$1" + shift + BRANCH1="$1" + shift + BRANCH2="$1" + shift + git fetch "$REMOTE" "$BRANCH1" + git push "$REMOTE" "$REMOTE/$BRANCH1:$BRANCH2" "$@" +)} + +shDirHttplinkValidate() {(set -e +# this function will validate http-links embedded in .html and .md files + node -e ' +(async function () { + "use strict"; + let dict = {}; + Array.from( + await require("fs").promises.readdir(".") + ).forEach(async function (file) { + if (!( + /.\.html$|.\.md$/m + ).test(file)) { + return; + } + let data = await require("fs").promises.readFile(file, "utf8"); + data.replace(( + /\bhttps?:\/\/.*?(?:[\s")\]]|$)/gm + ), function (url) { + url = url.slice(0, -1).replace(( + /[\u0022\u0027]/g + ), "").replace(( + /\/branch\.\w+?\//g + ), "/branch.alpha/").replace(( + /\bjslint-org\/jslint\b/g + ), process.env.GITHUB_REPOSITORY || "jslint-org/jslint").replace(( + /\bjslint-org\.github\.io\/jslint\b/g + ), String( + process.env.GITHUB_REPOSITORY || "jslint-org/jslint" + ).replace("/", ".github.io/")); + if (url.indexOf("http://") === 0) { + throw new Error( + "shDirHttplinkValidate - insecure link " + url + ); + } + // ignore duplicate-link + if (dict.hasOwnProperty(url)) { + return ""; + } + dict[url] = true; + let req = require("https").request(require("url").parse( + url + ), function (res) { + console.error( + "shDirHttplinkValidate " + res.statusCode + " " + url + ); + if (!(res.statusCode < 400)) { + throw new Error( + "shDirHttplinkValidate - " + file + + " - unreachable link " + url + ); + } + req.abort(); + res.destroy(); + }); + req.setTimeout(30000); + req.end(); + return ""; + }); + data.replace(( + /(\bhref=|\bsrc=|\burl\(|\[[^]*?\]\()("?.*?)(?:[")\]]|$)/gm + ), function (ignore, linkType, url) { + if (linkType[0] !== "[") { + url = url.slice(1); + } + // ignore duplicate-link + if (dict.hasOwnProperty(url)) { + return ""; + } + dict[url] = true; + if (!( + /^https?|^mailto:|^[#\/]/m + ).test(url)) { + require("fs").stat(url, function (ignore, exists) { + console.error( + "shDirHttplinkValidate " + Boolean(exists) + " " + url + ); + if (!exists) { + throw new Error( + "shDirHttplinkValidate - " + file + + " - unreachable link " + url + ); + } + }); + } + return ""; + }); + }); +}()); +' # "' +)} + +shGitCmdWithGithubToken() {(set -e +# this function will run git $CMD with $GITHUB_TOKEN + local CMD + local EXIT_CODE + local REMOTE + local URL + printf "shGitCmdWithGithubToken $*\n" + CMD="$1" + shift + REMOTE="$1" + shift + URL="$( + git config "remote.$REMOTE.url" | + sed -e "s|https://|https://x-access-token:$GITHUB_TOKEN@|" + )" + EXIT_CODE=0 + # hide $GITHUB_TOKEN in case of err + git "$CMD" "$URL" "$@" 2>/dev/null || EXIT_CODE="$?" + printf "shGitCmdWithGithubToken - EXIT_CODE=$EXIT_CODE\n" 1>&2 + return "$EXIT_CODE" +)} + +shGitLsTree() {(set -e +# this function will "git ls-tree" all files committed in HEAD +# example use: +# shGitLsTree | sort -rk3 # sort by date +# shGitLsTree | sort -rk4 # sort by size + node -e ' +(async function () { + "use strict"; + let result; + // get file, mode, size + result = await new Promise(function (resolve) { + result = ""; + require("child_process").spawn("git", [ + "ls-tree", "-lr", "HEAD" + ], { + encoding: "utf8", + stdio: [ + "ignore", "pipe", 2 + ] + }).on("exit", function () { + resolve(result); + }).stdout.on("data", function (chunk) { + result += chunk; + }).setEncoding("utf8"); + }); + result = Array.from(result.matchAll( + /^(\S+?)\u0020+?\S+?\u0020+?\S+?\u0020+?(\S+?)\t(\S+?)$/gm + )).map(function ([ + ignore, mode, size, file + ]) { + return { + file, + mode: mode.slice(-3), + size: Number(size) + }; + }); + result = result.sort(function (aa, bb) { + return aa.file > bb.file || -1; + }); + result = result.slice(0, 1000); + result.unshift({ + file: ".", + mode: "755", + size: 0 + }); + // get date + result.forEach(function (elem) { + result[0].size += elem.size; + require("child_process").spawn("git", [ + "log", "--max-count=1", "--format=%at", elem.file + ], { + stdio: [ + "ignore", "pipe", 2 + ] + }).stdout.on("data", function (chunk) { + elem.date = new Date( + Number(chunk) * 1000 + ).toISOString().slice(0, 19) + "Z"; + }); + }); + process.on("exit", function () { + let iiPad; + let sizePad; + iiPad = String(result.length).length + 1; + sizePad = String(Math.ceil(result[0].size / 1024)).length; + process.stdout.write(result.map(function (elem, ii) { + return ( + String(ii + ".").padStart(iiPad, " ") + + " " + elem.mode + + " " + elem.date + + " " + String( + Math.ceil(elem.size / 1024) + ).padStart(sizePad, " ") + " KB" + + " " + elem.file + + "\n" + ); + }).join("")); + }); +}()); +' # "' +)} + +shRunWithCoverage() {(set -e +# this function will run nodejs command $@ with v8-coverage and +# create coverage-report .build/coverage/index.html + local EXIT_CODE + EXIT_CODE=0 + export DIR_COVERAGE=.build/coverage/ + rm -rf "$DIR_COVERAGE" + (set -e + export NODE_V8_COVERAGE="$DIR_COVERAGE" + "$@" + ) || EXIT_CODE="$?" + if [ "$EXIT_CODE" = 0 ] + then + node -e ' +/*jslint bitwise*/ +// init debugInline +if (!globalThis.debugInline) { + let consoleError; + consoleError = console.error; + globalThis.debugInline = function (...argList) { + /* + * this function will both print to stderr and + * return [0] + */ + consoleError("\n\ndebugInline"); + consoleError(...argList); + consoleError("\n"); + return argList[0]; + }; +} +(async function () { + "use strict"; + let DIR_COVERAGE = process.env.DIR_COVERAGE; + let cwd; + let data; + let fileDict; + async function htmlRender({ + fileList, + lineList, + pathname + }) { + let html; + let padLines; + let padPathname; + let txt; + let txtBorder; + function stringHtmlSafe(str) { + /* + * this function will make html-safe + * https://stackoverflow.com/questions/7381974/ + * which-characters-need-to-be-escaped-on-html + */ + return str.replace(( + /&/gu + ), "&").replace(( + /"/gu + ), """).replace(( + /\u0027/gu + ), "'").replace(( + //gu + ), ">").replace(( + /&(amp;|apos;|gt;|lt;|quot;)/igu + ), "&$1"); + } + html = ""; + html += ` + + +coverage-report + + + +
    +
    coverage-report
    + + + + + + + +`; + if (!lineList) { + padLines = String("100.00 %").length; + padPathname = 32; + fileList.unshift({ + linesCovered: 0, + linesTotal: 0, + pathname: "./" + }); + fileList.slice(1).forEach(function ({ + linesCovered, + linesTotal, + pathname + }) { + fileList[0].linesCovered += linesCovered; + fileList[0].linesTotal += linesTotal; + padPathname = Math.max(padPathname, pathname.length + 2); + padLines = Math.max( + padLines, + String(linesCovered + " / " + linesTotal).length + ); + }); + } + txtBorder = ( + "+" + "-".repeat(padPathname + 2) + "+" + + "-".repeat(padLines + 2) + "+\n" + ); + txt = ""; + txt += "coverage-report\n"; + txt += txtBorder; + txt += ( + "| " + String("files covered").padEnd(padPathname, " ") + " | " + + String("lines").padStart(padLines, " ") + " |\n" + ); + txt += txtBorder; + fileList.forEach(function ({ + linesCovered, + linesTotal, + pathname + }, ii) { + let coverageLevel; + let coveragePct; + coveragePct = Math.floor(10000 * linesCovered / linesTotal | 0); + coverageLevel = ( + coveragePct >= 8000 + ? "coverageHigh" + : coveragePct >= 5000 + ? "coverageMedium" + : "coverageLow" + ); + coveragePct = String(coveragePct).replace(( + /..$/m + ), ".$&"); + if (!lineList && ii === 0) { + let fill = ( + // red + "#" + Math.round( + (100 - Number(coveragePct)) * 2.21 + ).toString(16).padStart(2, "0") + // green + + Math.round( + Number(coveragePct) * 2.21 + ).toString(16).padStart(2, "0") + + // blue + "00" + ); + let str1 = "coverage"; + let str2 = coveragePct + " %"; + let xx1 = 6 * str1.length + 20; + let xx2 = 6 * str2.length + 20; + // fs - write coverage-badge.svg + require("fs").promises.writeFile(( + DIR_COVERAGE + "/coverage-badge.svg" + ), String(` + + + + +${str1} +${str2} + + + `).trim() + "\n"); + pathname = ""; + } + txt += ( + "| " + + String("./" + pathname).padEnd(padPathname, " ") + " | " + + String(coveragePct + " %").padStart(padLines, " ") + " |\n" + ); + txt += ( + "| " + "*".repeat( + Math.round(0.01 * coveragePct * padPathname) + ).padEnd(padPathname, "_") + " | " + + String( + linesCovered + " / " + linesTotal + ).padStart(padLines, " ") + " |\n" + ); + txt += txtBorder; + pathname = stringHtmlSafe(pathname); + html += ` + + +`; + }); + if (lineList) { + html += ` +
    files coveredlines
    + ${( + lineList + ? ( + "./ " + + pathname + "
    " + ) + : ( + "./ " + + pathname + "
    " + ) + )} +
    +
    +
    +
    + ${coveragePct} %
    + ${linesCovered} / ${linesTotal} +
    +
    +
    +`; + lineList.forEach(function ({ + count, + holeList, + line, + startOffset + }, ii) { + let chunk; + let inHole; + let lineId; + let lineHtml; + lineHtml = ""; + lineId = "line_" + (ii + 1); + switch (count) { + case -1: + case 0: + if (holeList.length === 0) { + lineHtml += ""; + lineHtml += ""; + lineHtml += stringHtmlSafe(line); + break; + } + line = line.split("").map(function (chr) { + return { + chr, + isHole: undefined + }; + }); + holeList.forEach(function ([ + aa, bb + ]) { + aa = Math.max(aa - startOffset, 0); + bb = Math.min(bb - startOffset, line.length); + while (aa < bb) { + line[aa].isHole = true; + aa += 1; + } + }); + chunk = ""; + line.forEach(function ({ + chr, + isHole + }) { + if (inHole !== isHole) { + lineHtml += stringHtmlSafe(chunk); + lineHtml += ( + isHole + ? "" + : "" + ); + chunk = ""; + inHole = isHole; + } + chunk += chr; + }); + lineHtml += stringHtmlSafe(chunk); + break; + default: + lineHtml += stringHtmlSafe(line); + } + html += String(` +
    +
    +${String(ii + 1).padStart(5, " ")}.
    +
    +
    +${String(count).padStart(7, " ")}
    +
    +${lineHtml}
    +
    + `).replace(( + /\n/g + ), "").trim() + "\n"; + }); + } + html += ` +
    +
    +
    + +`; + html += "\n"; + await require("fs").promises.mkdir(require("path").dirname(pathname), { + recursive: true + }); + // fs - write *.html + require("fs").promises.writeFile(pathname + ".html", html); + if (lineList) { + return; + } + // fs - write coverage.txt + console.error("\n" + txt); + require("fs").promises.writeFile(( + DIR_COVERAGE + "/coverage-report.txt" + ), txt); + } + data = await require("fs").promises.readdir(DIR_COVERAGE); + await Promise.all(data.map(async function (file) { + if (( + /^coverage-.*?\.json$/ + ).test(file)) { + data = await require("fs").promises.readFile(( + DIR_COVERAGE + file + ), "utf8"); + // fs - rename to coverage-v8.json + require("fs").promises.rename( + DIR_COVERAGE + file, + DIR_COVERAGE + "coverage-v8.json" + ); + } + })); + fileDict = {}; + cwd = process.cwd().replace(( + /\\/g + ), "/") + "/"; + await Promise.all(JSON.parse(data).result.map(async function ({ + functions, + url + }) { + let lineList; + let linesCovered; + let linesTotal; + let pathname; + let src; + if (url.indexOf("file:///") !== 0) { + return; + } + pathname = url.replace(( + process.platform === "win32" + ? "file:///" + : "file://" + ), "").replace(( + /\\\\/g + ), "/"); + if ( + pathname.indexOf(cwd) !== 0 + || pathname.indexOf(cwd + "[") === 0 + || ( + process.env.npm_config_mode_coverage !== "all" + && pathname.indexOf("/node_modules/") >= 0 + ) + ) { + return; + } + pathname = pathname.replace(cwd, ""); + src = await require("fs").promises.readFile(pathname, "utf8"); + lineList = [{}]; + src.replace(( + /^.*$/gm + ), function (line, startOffset) { + lineList[lineList.length - 1].endOffset = startOffset - 1; + lineList.push({ + count: -1, + endOffset: 0, + holeList: [], + line, + startOffset + }); + return ""; + }); + lineList.shift(); + lineList[lineList.length - 1].endOffset = src.length; + functions.reverse().forEach(function ({ + ranges + }) { + ranges.reverse().forEach(function ({ + count, + endOffset, + startOffset + }, ii, list) { + lineList.forEach(function (elem) { + if (!( + ( + elem.startOffset <= startOffset + && startOffset <= elem.endOffset + ) || ( + elem.startOffset <= endOffset + && endOffset <= elem.endOffset + ) || ( + startOffset <= elem.startOffset + && elem.endOffset <= endOffset + ) + )) { + return; + } + // handle root-range + if (ii + 1 === list.length) { + if (elem.count === -1) { + elem.count = count; + } + return; + } + // handle non-root-range + if (elem.count !== 0) { + elem.count = Math.max(count, elem.count); + } + if (count === 0) { + elem.count = 0; + elem.holeList.push([ + startOffset, endOffset + ]); + } + }); + }); + }); + linesTotal = lineList.length; + linesCovered = lineList.filter(function ({ + count + }) { + return count > 0; + }).length; + await require("fs").promises.mkdir(( + require("path").dirname(DIR_COVERAGE + pathname) + ), { + recursive: true + }); + await htmlRender({ + fileList: [ + { + linesCovered, + linesTotal, + pathname + } + ], + lineList, + pathname: DIR_COVERAGE + pathname + }); + fileDict[pathname] = { + lineList, + linesCovered, + linesTotal, + pathname, + src + }; + })); + await htmlRender({ + fileList: Object.keys(fileDict).sort().map(function (pathname) { + return fileDict[pathname]; + }), + pathname: DIR_COVERAGE + "index" + }); +}()); +' # "' + find "$DIR_COVERAGE" + fi + printf "shRunWithCoverage - EXIT_CODE=$EXIT_CODE\n" 1>&2 + return "$EXIT_CODE" +)} + +shRunWithScreenshotTxt() {(set -e +# this function will run cmd $@ and screenshot text-output +# https://www.cnx-software.com/2011/09/22/how-to-convert-a-command-line-result-into-an-image-in-linux/ + local EXIT_CODE + EXIT_CODE=0 + export SCREENSHOT_SVG=.build/screenshot.svg + rm -f "$SCREENSHOT_SVG" + printf "0\n" > "$SCREENSHOT_SVG.exit_code" + shCiPrint "shRunWithScreenshotTxt - (shRun $* 2>&1)" + (set -e + (shRun "$@" 2>&1) || printf "$?\n" > "$SCREENSHOT_SVG.exit_code" + ) | tee /tmp/shRunWithScreenshotTxt.txt + EXIT_CODE="$(cat "$SCREENSHOT_SVG.exit_code")" + shCiPrint "shRunWithScreenshotTxt - EXIT_CODE=$EXIT_CODE" + # run shRunWithScreenshotTxtAfter + if (type shRunWithScreenshotTxtAfter > /dev/null 2>&1) + then + eval shRunWithScreenshotTxtAfter + unset shRunWithScreenshotTxtAfter + fi + # format text-output + node -e ' +(async function () { + "use strict"; + let result; + let yy; + yy = 10; + result = await require("fs").promises.readFile( + require("os").tmpdir() + "/shRunWithScreenshotTxt.txt", + "utf8" + ); + // remove ansi escape-code + result = result.replace(( + /\u001b.*?m/g + ), ""); + // format unicode + result = result.replace(( + /\\u[0-9a-f]{4}/g + ), function (match0) { + return String.fromCharCode("0x" + match0.slice(-4)); + }).trimEnd(); + // 96 column wordwrap + result = result.replace(( + /^.*?$/gm + ), function (line) { + return line.replace(( + /.{0,96}/g + ), function (line, ii) { + if (ii && !line) { + return ""; + } + yy += 16; + return "" + line.replace(( + /&/g + ), "&").replace(( + //g + ), ">") + ""; + }).replace(( + /(<\/tspan>\n" + + "\n" + + "\n" + + result + "\n\n" + ); + try { + await require("fs").promises.mkdir(( + require("path").dirname(process.argv[1]) + ), { + recursive: true + }); + } catch (ignore) {} + require("fs").promises.writeFile(process.argv[1], result); +}()); +' "$SCREENSHOT_SVG" # "' + shCiPrint "shRunWithScreenshotTxt - wrote - $SCREENSHOT_SVG" + printf "shRunWithScreenshotTxt - EXIT_CODE=$EXIT_CODE\n" 1>&2 + return "$EXIT_CODE" +)} + +# run $@ +"$@" diff --git a/branch.beta/font-daley-bold.woff2 b/branch.beta/font-daley-bold.woff2 new file mode 100644 index 000000000..783bdd697 Binary files /dev/null and b/branch.beta/font-daley-bold.woff2 differ diff --git a/branch.beta/font-programma-bold.woff2 b/branch.beta/font-programma-bold.woff2 new file mode 100644 index 000000000..dfb5a9753 Binary files /dev/null and b/branch.beta/font-programma-bold.woff2 differ diff --git a/branch.beta/function.html b/branch.beta/function.html new file mode 100644 index 000000000..feb0c13dc --- /dev/null +++ b/branch.beta/function.html @@ -0,0 +1,613 @@ + + + + + + + + +JSLint: jslint function + + + +
    JSLint
    + +
    The jslint Function
    +
    +

    JSLint

    +

    JSLint is delivered as a set of files:

    +
    + + + + + + + + + + + + +
    FilenameContent
    browser.jsBrowser based UI and report object, containing report + generator functions for HTML.
    function.htmlThis page that you are looking at right now.
    help.htmlUsing jslint as a single page JSLint application.
    index.htmlSingle page JSLint application that uses the + js files.
    jslint.jsThe jslint function.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    +
    + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring"(JSLint)"
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    parenttokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    parenttokenThe function in which the variable was declared.variable
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    shebangstringThe first line of text if it started with #!line
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable may be assigned to.variable
    +

    Reports

    +

    The report object contains three functions that all take a + result from the jslint function as input. + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    +
    + +
    + + + +
    + + diff --git a/branch.beta/help.html b/branch.beta/help.html new file mode 100644 index 000000000..63c34f0c6 --- /dev/null +++ b/branch.beta/help.html @@ -0,0 +1,818 @@ + + + + + + + + + +JSLint: Help + + + +
    JSLint
    + +
    Help
    +
    +
    Il semble que la perfection soit atteinte non quand il n’y a + plus rien à ajouter, mais quand il n’y a plus rien à + retrancher.
    +
    + Antoine de Saint-Exupéry +
    +
    + Terre des Hommes (1939) +
    +

    Good Parts

    +

    The Principle of the Good Parts is

    +
    If a feature is sometimes useful and sometimes dangerous and if + there is a better option then always use the better option.
    +

    JSLint is a JavaScript program that looks for problems in + JavaScript programs. It is a code quality tool.

    +

    When C was + a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    +

    As the language matured, the definition of the language was + strengthened to eliminate some insecurities, and compilers got better + at issuing warnings. lint is no longer needed.

    +

    JavaScript is a + young-for-its-age language. It was originally intended to do small tasks in + webpages, tasks for which Java was too heavy and clumsy. But JavaScript is + a surprisingly capable language, and it is now being used in larger + projects. Many of the features that were intended to make the language easy + to use are troublesome when projects become complicated. A lint + for JavaScript is needed: JSLint, a JavaScript syntax checker + and validator.

    +

    JSLint takes a JavaScript source and scans it. If it finds a + problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by the ECMAScript Programming Language + Standard (the strangely named document that governs JavaScript). + JSLint will reject most legal programs. It is a higher + standard.

    +

    JavaScript is a sloppy language, but hidden deep inside there is an elegant, + better language. JSLint helps you to program in that better + language and to avoid most of the slop. JSLint will reject + programs that browsers will accept because JSLint is concerned + with the quality of your code and browsers are not. You should gladly accept + all of JSLint's advice.

    +

    JSLint can operate on JavaScript source + or JSON text.

    +

    ECMAScript Sixth Edition

    +

    Some of + ES6’s features are good, so JSLint will recognize the good + parts of ES6.

    +

    Currently, these features are recognized:

    +
      +
    • The ... ellipsis marker in parameter + lists and argument lists, replacing the arguments object + for variadic functions.
    • +
    • The let statement, which is like the var + statement except that it respects block scope. + You may use let or var but not both.
    • +
    • The const statement is like the let statement + except that it disallows the use of assignment on the variable, although + if the value of the variable is mutable, it can still be mutated. + const is preferred to let. +
    • Destructuring of arrays and objects is allowed in parameter lists and on + the left side of let, and const, but not + var or assignment statements, and not deep destructuring + or eliding.
    • +
    • Enhanced object literals, providing shorter forms for function + declaration and properties that are initialized by variables with the + same name.
    • +
    • The fat arrow => fart functions.
    • +
    • The simplest forms of import and export.
    • +
    • `Megastring` literals, but not nested + `megastring` literals.
    • +
    • New global functions, such as Map, Set, + WeakMap, and WeakSet.
    • +
    • 0b- and 0o- number literals.
    • +
    +

    The most important new feature of ES6 is proper tail calls. This has no + new syntax, so JSLint doesn’t see it. But it makes recursion + much more attractive, which makes loops, particularly for + loops, much less attractive.

    +

    import export

    +

    The ES6 module feature will be an important improvement over JavaScript’s + global variables as a means of linking separate files together. + JSLint recognizes a small but essential subset of the module + syntax.

    +
    +

    import name from stringliteral;

    +

    import {name} from stringliteral;

    +

    import(string).then(function);

    +

    export default function () {};

    +

    export default function name() {};

    +

    export default expression;

    +

    export function name() {}

    +

    export name; // where name is const

    +

    export {name};

    +
    +

    Directives

    +

    JSLint provides three directives that may be placed in a + file to manage JSLint’s behavior. Use of these directives is + optional. If they are used, they should be placed in a source file before + the first statement. They are written in the form of a comment, where the + directive name is placed immediately after the opening of the comment before + any whitespace. The three directives are global, + jslint, and property. Directives in a file are + stronger than options selected from the UI or passed with the option object.

    +

    /*global*/

    +

    The /*global*/ directive is used to specify a set of globals + (usually functions and objects containing functions) that are available to + this file. This was commonly used in browsers to link source files together + before ES6 modules appeared. Use of global variables is strongly + discouraged, but unfortunately web browsers require their use. The + /*global*/ directive can only be used when the + Assume a browser option is selected. +

    Each of the names listed will indicate a read-only global variable. The names + are separated by , comma. This directive + should not be used if import or export is used. + This directive inhibits warnings, but it does not declare the names in the + execution environment. +

    For example: +

    /*global +
    ADSAFE, report, jslint
    +*/
    +

    instructs JSLint to not give warnings about the global + variables ADsafe, report, and jslint. + However, if any of those names are expected to be supplied by other files + and those other files fail to do so, then execution errors will result. It + is usually better to use top-level variable declarations instead: +

        var ADSAFE;
    +    var report;
    +    var jslint; 
    +

    Using var in this way allows comparing a global variable to the + undefined value to determine whether it is has been used in the global context.

    +

    /*jslint*/

    +

    The /*jslint*/ directive allows for the control of several + options. These options can also be set from the + JSLint.com user interface.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Tolerate bitwise operatorsbitwisetrue if bitwise operators should be allowed. The + bitwise operators are rarely used in JavaScript programs, so it + is usually more likely that & is a mistyping of + && than that it indicates a bitwise and. + JSLint will give warnings on the bitwise operators + unless this option is selected.
    Assume a browser browsertrue if the standard browser globals should be + predefined. This option will reject the use of + import and export. This option also + disallows the file form of the "use + strict" pragma. It does not supply + self; you will have to request that unnecessary + alias of the dreaded global object yourself. It adds the same + globals as this directive: +
    /*global +
    + clearInterval, clearTimeout, document, event, FileReader, + FormData, history, localStorage, location, name, navigator, + screen, sessionStorage, setInterval, setTimeout, Storage, + URL, window, XMLHttpRequest +
    + */
    +
    Tolerate conversion operatorsconverttrue if !!, + prefix, + and concatenation with "" are allowed. + The Boolean, Number, and + String functions are preferred
    Assume CouchDBcouchtrue if + Couch DB + globals should be predefined. It adds the same globals as this + directive: +
    /*global +
    emit, getRow, isArray, log, provides, registerType, require, + send, start, sum, toJSON
    + */
    Assume in developmentdeveltrue if browser globals that are useful in + development should be predefined, and if debugger + statements and TODO comments + should be allowed. It adds the + same globals as this directive: +
    /*global +
    alert, confirm, console, prompt
    + */
    Be sure to turn this option off before going + into production.
    Tolerate evalevaltrue if eval should be allowed. In the + past, the eval function was the most misused + feature of the language.
    Tolerate for statementfortrue if the for statement should be + allowed. It is almost always better to use the array methods + instead.
    Fudge the line and column numbersfudgetrue if the origin of a text file is line 1 column + 1. Programmers know that number systems start at zero. + Unfortunately, most text editors start numbering lines and + columns at one. JSLint will by default start + numbering at zero. Use this option to start the numbering at one + in its reports.
    Tolerate get and setgetsettrue if accessor properties are allowed in object literals.
    Tolerate long source lineslongtrue if a line can contain more than 80 characters.
    Assume Node.jsnodetrue if Node.js globals should be predefined. It + will predefine globals that are used in the Node.js environment. + It adds the same globals as this directive: +
    /*global +
    + Buffer, clearImmediate, clearInterval, clearTimeout, console, exports, + module, process, require, setImmediate, setInterval, setTimeout, URL, + URLSearchParams, __dirname, __filename +
    + */
    Tolerate single quote stringssingletrue if 'single quote + should be allowed to enclose string literals.
    Tolerate this
    thistrue if this should be allowed.
    Tolerate unordered properties and params.unorderedtrue if objects and functions are allowed to declare properties and params in non-alphabetical order.
    Tolerate whitespace messwhitetrue if the whitespace rules should be ignored.
    +

    For example:

    +
    /*jslint
    +    bitwise, node
    +*/
    + +

    /*property*/

    +

    The /*property*/ directive is used to declare a list of + property identifiers that are used in the file. Each property name in the + program is looked up in this list. If a name is not found, that indicates an + error, most likely a typing error.

    +

    The list can also be used to evade some of JSLint’s naming + rules.

    +

    JSLint can build the /*property*/ list for you. At + the bottom of its report, JSLint displays a + /*property*/ directive. It contains all of the names that were + used with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. You + can copy the /*property*/ directive to the top of your + script file. JSLint will check the spelling of all property + names against the list. That way, you can have JSLint look for + misspellings for you.

    +

    For example,

    +
    /*property +
    charAt, slice, _$_
    + */
    +

    ignore

    +

    JSLint introduces a new reserved word: ignore. It + is used in parameter lists and in catch clauses to indicate a + parameter that will be ignored. Unused warnings will not be produced for + ignore. +

    function handler(ignore, value) {
    +    return do_something_useful(value);
    +}
    +

    var let const

    +

    JavaScript provides three statements for declaring variables: + var, let, and const. The + var statement suffers from a bad practice called hoisting. The + let statement does not do hoisting and respects block scope. + The const statement is like let except that it + marks the variable (but not its contents) as read only, making it an error + to attempt to assign to the variable. When given a choice, + const is the best, var is the worst.

    +

    JSLint uses the intersection of the var rules and the + let rules, and by doing so avoids the errors related to either. + A name should be declared only once in a function. It should be declared + before it is used. It should not be used outside of the block in which it is + declared. A variable should not have the same name as a variable or + parameter in an outer function. Do not mix var and + let. Declare one name per statement.

    +

    = == ===

    +

    Fortran made a terrible + mistake in using the equality operator as its assignment operator. That + mistake has been replicated in most languages since then. C compounded that + mistake by making == its equality operator. The visual + similarity is a source of errors. JavaScript compounded this further by + making == a type coercing comparison operator that produces + false positive results. This was mitigated by adding the === + operator, leaving the broken == operator in place.

    +

    JSLint attempts to minimize errors by the following rules:

    +

    == is not allowed. This avoids the false positives, and + increases the visual distance between = and ===. + Assignments are not allowed in expression position, and comparisons are not + allowed in statement position. This also reduces confusion. 

    +

    Semicolon

    +

    JavaScript uses a C-like syntax that requires the use of semicolons to + delimit certain statements. JavaScript attempts to make those semicolons + optional with an automatic semicolon insertion mechanism, but it does not + work very well. Automatic semicolon insertion was added to make + things easier for beginners. Unfortunately, it sometimes fails. Do not rely + on it unless you are a beginner.

    +

    JSLint expects that every statement will be followed by + ; except for for, function, + if, switch, try, and + while. JSLint does not expect to see unnecessary + semicolons, the empty statement, or empty blocks.

    +

    function =>

    +

    JavaScript has four syntactic forms for making function objects: function + statements, function expressions, enhanced object literals, and the + => fart operator.

    +

    The function statement creates a variable and assigns the function object to + it. It should be used in a file or function body, but not inside of a block.

    +
    function name(parameters) { +
    statements
    + }
    +

    The function expression unfortunately looks like the function statement. It + may appear anywhere that an expression may appear, but not in statement + position and not in a loop. It produces a function object but does not + create a variable in which to store it.

    +
    function (parameters) { +
    statements
    + }
    +

    The enhanced object literal provides an ES6 shorthand for creating a property + whose value is a function, saving you from having to type + : colon and function.

    +
    { +
    name(parameters) { +
    statements
    + }
    + }
    +

    Finally, ES6 provides an even shorter form of function expression that leaves + out the words function and return:

    +
    (parameters) => + expression
    +

    JSLint requires the parens around the parameters, and forbids a + { left brace after the + => fart to avoid syntactic ambiguity.

    +

    Comma

    +

    The , comma operator is unnecessary and can + mask programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as + an operator. It does not expect to see elided elements in array literals. A + comma should not appear after the last element of an array literal or object + literal.

    +

    Blocks

    +

    JSLint expects blocks with function, + if, switch, while, for, + do, and try statements and nowhere else.

    +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    +

    JavaScript allows an if to be written like this:

    +
    if (condition)
    +    statement;
    +

    That form is known to contribute to mistakes in projects where many + programmers are working on the same code. That is why JSLint + expects the use of a block:

    +
    if (condition) {
    +    statements;
    +}
    +

    Experience shows that this form is more resilient.

    +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call. All other expression statements are considered + to be errors.

    +

    for

    +

    JSLint does not recommend use of the for statement. + Use array methods like forEach instead. The for + option will suppress some warnings. The forms of for that + JSLint accepts are restricted, excluding the new ES6 forms.

    +

    for in

    +

    JSLint does not recommend use of the for + in statement. Use Object.keys instead.

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, it also + loops through all of the properties that were inherited through the + prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written + without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    for (name in object) {
    +    if (object.hasOwnProperty(name)) {
    +        ....
    +    }
    +}
    +

    Note that the above code will fail if the object contains a property + named hasOwnProperty. Use Object.keys instead.

    +

    switch

    +

    A common error in switch statements is to forget to place a + break statement after each case, resulting in unintended + fall-through. JSLint expects that the statement before the next + case or default is one of these: + break, return, or throw.

    +

    with

    +

    The with statement was intended to provide a shorthand in + accessing properties in deeply nested objects. Unfortunately, it behaves + very badly when setting new properties. Never use the with + statement.

    +

    JSLint does not expect to see a with statement.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that + labels will be distinct from vars and parameters.

    +

    Unreachable Code

    +

    JSLint expects that a return, break, + or throw statement will be followed by a + } right brace or case or + default.

    +

    Confusing Pluses and Minuses

    +

    JSLint expects that + will not be followed by + + or ++, and that - will not be + followed by - or --. A misplaced space can turn + + + into ++, an error that is difficult to see. + Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ increment and + -- decrement operators have been known to + contribute to bad code by encouraging excessive trickiness. They are second + only to faulty architecture in enabling to viruses and other security + menaces. Also, preincrement/postincrement confusion can produce off-by-one + errors that are extremely difficult to diagnose. Fortunately, they are also + complete unnecessary. There are better ways to add 1 to a variable.

    +

    It is best to avoid these operators entirely and rely on += and + -= instead.

    +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. + JSLint looks for problems that may cause portability problems. + It also attempts to resolve visual ambiguities by recommending explicit + escapement.

    +

    JavaScript’s syntax for regular expression literals overloads the + / slash character. To avoid ambiguity, + JSLint expects that the character preceding a regular + expression literal is a ( left paren or + = equal or + : colon or + , comma character.

    +

    this

    +
    Having this in the language makes it harder to talk + about the language. It is like pair programming with Abbott and Costello. +
    +

    Avoid using this. Warnings about this can be + suppressed with option.this.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the + new prefix. The new prefix creates a new object + based on the function's prototype, and binds that object to the + function's implied this parameter. If you neglect to use the + new prefix, no new object will be made and this + will be bound to the global object.

    +

    JSLint enforces the convention that constructor functions be + given names with initial uppercase. JSLint does not expect to + see a function invocation with an initial uppercase name unless it has the + new prefix. JSLint does not expect to see the + new prefix used with functions whose names do not start with + initial uppercase.

    +

    JSLint does not expect to see the wrapper forms + new Number, new String, new Boolean.

    +

    JSLint does not expect to see new Object. + Use Object.create(null) instead.

    +

    Whitespace

    +

    JSLint has a specific set of rules about the use of whitespace. + Where possible, these rules are consistent with centuries of good practice + with literary style.

    +

    The indentation increases by 4 spaces when the last token on a line is + { left brace, + [ left bracket, + ( left paren. The matching closing + token will be the first token on a line, restoring the previous + indentation.

    +

    The ternary operator can be visually confusing, so + ? question mark and + : colon always begin a line and increase + the indentation by 4 spaces.

    +
    return (
    +    (the_token.id === "(string)" || the_token.id === "(number)")
    +    ? String(the_token.value)
    +    : the_token.id
    +);
    +

    The word function is always followed with one space.

    +

    Clauses (case, catch, default, + else, finally) are not statements and so should + not be indented like statements.

    +

    Spaces are used to make things that are not invocations look less like + invocations.

    +

    Tabs and spaces should not be mixed. We should pick just one in order to + avoid the problems that come from having both. Personal preference is an + extremely unreliable criterion. Neither offers a powerful advantage over the + other. Fifty years ago, tab had the advantage of consuming less memory, but + Moore's Law has eliminated that advantage. Space has one clear advantage + over tab: there is no reliable standard for how many spaces a tab + represents, but it is universally accepted that a space occupies a space. So + use spaces. You can edit with tabs if you must, but make sure it is spaces + again before you commit. Maybe someday we will finally get a universal + standard for tabs, but until that day comes, the better choice is spaces.

    +

    Report

    +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    +
      +
    • The line number on which it starts.
    • +
    • The function’s name. In the case of anonymous functions, + JSLint will attempt to + «guess» the name.
    • +
    • The parameters.
    • +
    • Variables: The variables that are declared in the function.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Exceptions: The variables that are declared by catch + clauses of try statements.
    • +
    • Outer: Variables used by this function that are declared in + other functions.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements.

    +

    Try it

    +

    Try it. Paste your script + into the window and click the + + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    JSLint is written entirely in JavaScript, so it can run + anywhere that JavaScript (or even Java) can run.

    +

    Perfectly fine

    +

    JSLint was designed to reject code that some would consider to + be perfectly fine. The reason for this is that JSLint's + purpose is to help produce programs that are free of error. That is + difficult in any language and is especially hard in JavaScript. + JSLint attempts to help you increase the visual distance + between correct programs and incorrect programs, making the remaining errors + more obvious. JSLint will give warnings about things that are + not necessarily wrong in the current situation, but which have been observed + to mask or obscure errors. Avoid those things when there are better options + available.

    +

    It is dangerous out there. JSLint is here to help.

    +

    Warning

    +

    JSLint will hurt your feelings. Side effects may include + headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, + dry mouth, cleaner code, and a reduced error rate.

    +

    Further Reading

    + +
    +

    + Code Conventions for the JavaScript Programming Language.

    + + + +
    + + diff --git a/branch.beta/image-folder-open-solid.svg b/branch.beta/image-folder-open-solid.svg new file mode 100644 index 000000000..99d403c81 --- /dev/null +++ b/branch.beta/image-folder-open-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/branch.beta/image-github-brands.svg b/branch.beta/image-github-brands.svg new file mode 100644 index 000000000..7870c06dc --- /dev/null +++ b/branch.beta/image-github-brands.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/branch.beta/image-jslint-128x128.png b/branch.beta/image-jslint-128x128.png new file mode 100644 index 000000000..f7340e0b9 Binary files /dev/null and b/branch.beta/image-jslint-128x128.png differ diff --git a/branch.beta/image-jslint-256x256.png b/branch.beta/image-jslint-256x256.png new file mode 100644 index 000000000..1b2c70aba Binary files /dev/null and b/branch.beta/image-jslint-256x256.png differ diff --git a/branch.beta/image-jslint-32x32.png b/branch.beta/image-jslint-32x32.png new file mode 100644 index 000000000..0d32ee109 Binary files /dev/null and b/branch.beta/image-jslint-32x32.png differ diff --git a/branch.beta/image-jslint-512x512.png b/branch.beta/image-jslint-512x512.png new file mode 100644 index 000000000..f1b440efe Binary files /dev/null and b/branch.beta/image-jslint-512x512.png differ diff --git a/branch.beta/image-jslint-64x64.png b/branch.beta/image-jslint-64x64.png new file mode 100644 index 000000000..7545fffbf Binary files /dev/null and b/branch.beta/image-jslint-64x64.png differ diff --git a/branch.beta/image-jslint.html b/branch.beta/image-jslint.html new file mode 100644 index 000000000..ba774fcb6 --- /dev/null +++ b/branch.beta/image-jslint.html @@ -0,0 +1,52 @@ + + + +logo + + + +
    +
    JS
    +
    Lint
    +
    + + diff --git a/branch.beta/image-json160.gif b/branch.beta/image-json160.gif new file mode 100644 index 000000000..3bb55c8dd Binary files /dev/null and b/branch.beta/image-json160.gif differ diff --git a/branch.beta/image-window-maximize-regular.svg b/branch.beta/image-window-maximize-regular.svg new file mode 100644 index 000000000..7b7e5e166 --- /dev/null +++ b/branch.beta/image-window-maximize-regular.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/branch.beta/index.html b/branch.beta/index.html new file mode 100644 index 000000000..3e55d6191 --- /dev/null +++ b/branch.beta/index.html @@ -0,0 +1,483 @@ + + + + + + + + +JSLint: The JavaScript Code Quality Tool + + + + + +
    +
    Source
    +
    + + +
    +
    + Warnings +
    +
    +
    + Function Report +
    +
    +
    + Property Directive + +
    +
    + + + + +
    +
    Options +
    Assume... +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Global variables... + +
    +
    +
    + + diff --git a/branch.beta/jslint.js b/branch.beta/jslint.js new file mode 100755 index 000000000..0b78c6e71 --- /dev/null +++ b/branch.beta/jslint.js @@ -0,0 +1,5882 @@ +#!/usr/bin/env node +// jslint.js +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// jslint(source, option_object, global_array) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze, a string or an array of strings. +// option_object An object whose keys correspond to option names. +// global_array An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all of the functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// 1. If the source is a single string, split it into an array of strings. +// 2. Turn the source into an array of tokens. +// 3. Furcate the tokens into a parse tree. +// 4. Walk the tree, traversing all of the nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// 5. Check the whitespace between the tokens. + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*jslint node*/ + +/*property + unordered, + JSLINT_CLI, a, all, and, argv, arity, assign, b, bad_assignment_a, + bad_directive_a, bad_get, bad_module_name_a, bad_option_a, bad_property_a, + bad_set, bitwise, block, body, browser, c, calls, catch, cli_mode, closer, + closure, code, column, concat, console_error, constant, context, convert, + couch, create, d, dead, debug, default, devel, directive, directives, + disrupt, dot, duplicate_a, early_stop, edition, ellipsis, else, empty_block, + env, error, eval, every, exec, exit, expected_a, expected_a_at_b_c, + expected_a_b, expected_a_b_from_c_d, expected_a_before_b, + expected_a_next_at_b, expected_digits_after_a, expected_four_digits, + expected_identifier_a, expected_line_break_a_b, expected_regexp_factor_a, + expected_space_a_b, expected_statements_a, expected_string_a, + expected_type_string_a, exports, expression, extra, file, finally, flag, + for, forEach, formatted_message, free, freeze, freeze_exports, from, froms, + fud, fudge, function_in_loop, functions, g, getset, global, has_await, i, + id, identifier, import, inc, indexOf, infix_in, init, initial, isArray, + isNaN, is_async, join, json, keys, label, label_a, lbp, led, length, level, + line, line_offset, lines, live, long, loop, m, map, margin, match, message, + misplaced_a, misplaced_directive_a, missing_await_statement, + missing_browser, missing_m, module, naked_block, name, names, + nested_comment, node, not_label_a, now, nr, nud, number_isNaN, ok, open, + opening, option, out_of_scope_a, padStart, parameters, parent, pop, + promises, property, push, quote, raw, readFile, readdir, redefinition_a_b, + repeat, replace, required_a_optional_b, reserved_a, role, search, shebang, + signature, single, slice, some, sort, source, split, stack, stack_trace, + startsWith, statement, stop, subscript_a, switch, test, test_internal_error, + then, this, thru, todo_comment, tokens, too_long, too_many_digits, tree, + trim, try, type, u, unclosed_comment, unclosed_mega, unclosed_string, + undeclared_a, unexpected_a, unexpected_a_after_b, unexpected_a_before_b, + unexpected_at_top_level_a, unexpected_char_a, unexpected_comment, + unexpected_directive_a, unexpected_expression_a, unexpected_label_a, + unexpected_parens, unexpected_space_a_b, unexpected_statement_a, + unexpected_trailing_space, unexpected_typeof_a, uninitialized_a, + unordered_param_a, unordered_property_a, unreachable_a, + unregistered_property_a, unused_a, use_double, use_open, use_spaces, used, + value, var_loop, var_switch, variable, versions, warning, warnings, + weird_condition_a, weird_expression_a, weird_loop, weird_relation_a, white, + wrap_condition, wrap_immediate, wrap_parameter, wrap_regexp, wrap_unary, + wrapped, writable, y +*/ + +const edition = "v2021.5.27"; + +function assert_or_throw(passed, message) { + +// this function will throw if is falsy + + if (!passed) { + throw new Error(`This was caused by a bug in JSLint. +Please open an issue with this stack-trace at +https://github.com/jslint-org/jslint/issues. +edition = "${edition}";` + "\n" + message); + } +} + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than '{}' because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +function populate(array, object = empty(), value = true) { + +// Augment an object by taking property names from an array of strings. + + array.forEach(function (name) { + object[name] = value; + }); + return object; +} + +const allowed_option = { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + bitwise: true, + browser: [ + "caches", "CharacterData", "clearInterval", "clearTimeout", "document", + "DocumentType", "DOMException", "Element", "Event", "event", "fetch", + "FileReader", "FontFace", "FormData", "history", "IntersectionObserver", + "localStorage", "location", "MutationObserver", "name", "navigator", + "screen", "sessionStorage", "setInterval", "setTimeout", "Storage", + "TextDecoder", "TextEncoder", "URL", "window", "Worker", + "XMLHttpRequest" + ], + convert: true, + couch: [ + "emit", "getRow", "isArray", "log", "provides", "registerType", + "require", "send", "start", "sum", "toJSON" + ], + debug: true, + devel: [ + "alert", "confirm", "console", "prompt" + ], + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + node: [ + "Buffer", "clearImmediate", "clearInterval", "clearTimeout", + "console", "exports", "module", "process", "require", + "setImmediate", "setInterval", "setTimeout", "TextDecoder", + "TextEncoder", "URL", "URLSearchParams", "__dirname", "__filename" + ], + single: true, + test_internal_error: true, + this: true, + unordered: true, + white: true +}; + +const anticondition = populate([ + "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%", + "typeof", "(number)", "(string)" +]); + +// These are the bitwise operators. + +const bitwiseop = populate([ + "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=", + ">>>", ">>>=" +]); + +const escapeable = populate([ + "\\", "/", "`", "b", "f", "n", "r", "t" +]); + +const opener = { + +// The open and close pairs. + + "${": "}", // mega + "(": ")", // paren + "[": "]", // bracket + "{": "}" // brace +}; + +// The relational operators. + +const relationop = populate([ + "!=", "!==", "==", "===", "<", "<=", ">", ">=" +]); + +// This is the set of infix operators that require a space on each side. + +const spaceop = populate([ + "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/", + "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=", + ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" +]); + +const standard = [ + +// These are the globals that are provided by the language standard. + + "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "Error", "EvalError", + "Float32Array", "Float64Array", "Generator", "GeneratorFunction", + "Int16Array", "Int32Array", "Int8Array", "Intl", "JSON", "Map", "Math", + "Number", "Object", "Promise", "Proxy", "RangeError", "ReferenceError", + "Reflect", "RegExp", "Set", "String", "Symbol", "SyntaxError", "System", + "TypeError", "URIError", "Uint16Array", "Uint32Array", "Uint8Array", + "Uint8ClampedArray", "WeakMap", "WeakSet", "decodeURI", + "decodeURIComponent", "encodeURI", "encodeURIComponent", "globalThis", + "import", "parseFloat", "parseInt" +]; + +const bundle = { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + and: "The '&&' subexpression should be wrapped in parens.", + bad_assignment_a: "Bad assignment to '{a}'.", + bad_directive_a: "Bad directive '{a}'.", + bad_get: "A get function takes no parameters.", + bad_module_name_a: "Bad module name '{a}'.", + bad_option_a: "Bad option '{a}'.", + bad_property_a: "Bad property name '{a}'.", + bad_set: "A set function takes one parameter.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + expected_a: "Expected '{a}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: ( + "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'." + ), + expected_a_before_b: "Expected '{a}' before '{b}'.", + expected_digits_after_a: "Expected digits after '{a}'.", + expected_four_digits: "Expected four digits after '\\u'.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.", + expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.", + expected_space_a_b: "Expected one space between '{a}' and '{b}'.", + expected_statements_a: "Expected statements before '{a}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + expected_type_string_a: "Expected a type string and instead saw '{a}'.", + freeze_exports: ( + "Expected 'Object.freeze('. All export values should be frozen." + ), + function_in_loop: "Don't make functions within a loop.", + infix_in: ( + "Unexpected 'in'. Compare with undefined, " + + "or use the hasOwnProperty method instead." + ), + label_a: "'{a}' is a statement label.", + misplaced_a: "Place '{a}' at the outermost level.", + misplaced_directive_a: ( + "Place the '/*{a}*/' directive before the first statement." + ), + missing_await_statement: "Expected await statement in async function.", + missing_browser: "/*global*/ requires the Assume a browser option.", + missing_m: "Expected 'm' flag on a multiline regular expression.", + naked_block: "Naked block.", + nested_comment: "Nested comment.", + not_label_a: "'{a}' is not a label.", + number_isNaN: "Use Number.isNaN function to compare with NaN.", + out_of_scope_a: "'{a}' is out of scope.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + required_a_optional_b: ( + "Required parameter '{a}' after optional parameter '{b}'." + ), + reserved_a: "Reserved name '{a}'.", + subscript_a: "['{a}'] is better written in dot notation.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line is longer than 80 characters.", + too_many_digits: "Too many digits.", + unclosed_comment: "Unclosed comment.", + unclosed_mega: "Unclosed mega literal.", + unclosed_string: "Unclosed string.", + undeclared_a: "Undeclared '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_a_after_b: "Unexpected '{a}' after '{b}'.", + unexpected_a_before_b: "Unexpected '{a}' before '{b}'.", + unexpected_at_top_level_a: "Expected '{a}' to be in a function.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_directive_a: "When using modules, don't use directive '/*{a}'.", + unexpected_expression_a: ( + "Unexpected expression '{a}' in statement position." + ), + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_parens: "Don't wrap function literals in parens.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_statement_a: ( + "Unexpected statement '{a}' in expression position." + ), + unexpected_trailing_space: "Unexpected trailing space.", + unexpected_typeof_a: ( + "Unexpected 'typeof'. Use '===' to compare directly with {a}." + ), + uninitialized_a: "Uninitialized '{a}'.", + unordered_param_a: ( + "Parameter '{a}' not listed in alphabetical order." + ), + unordered_property_a: ( + "Property name '{a}' not listed in alphabetical order." + ), + unreachable_a: "Unreachable '{a}'.", + unregistered_property_a: "Unregistered property name '{a}'.", + unused_a: "Unused '{a}'.", + use_double: "Use double quotes, not single quotes.", + use_open: ( + "Wrap a ternary expression in parens, " + + "with a line break after the left paren." + ), + use_spaces: "Use spaces, not tabs.", + var_loop: "Don't declare variables in a loop.", + var_switch: "Don't declare variables in a switch.", + weird_condition_a: "Weird condition '{a}'.", + weird_expression_a: "Weird expression '{a}'.", + weird_loop: "Weird loop.", + weird_relation_a: "Weird relation '{a}'.", + wrap_condition: "Wrap the condition in parens.", + wrap_immediate: ( + "Wrap an immediate function invocation in parentheses to assist " + + "the reader in understanding that the expression is the result " + + "of a function, and not the function itself." + ), + wrap_parameter: "Wrap the parameter in parens.", + wrap_regexp: "Wrap this regexp in parens to avoid confusion.", + wrap_unary: "Wrap the unary expression in parens." +}; + +// Regular expression literals: + +function tag_regexp(strings) { + return new RegExp(strings.raw[0].replace(/\s/g, "")); +} + +// supplant {variables} +const rx_supplant = /\{([^{}]*)\}/g; +// carriage return, carriage return linefeed, or linefeed +const rx_crlf = tag_regexp ` + \n + | \r \n? +`; +// identifier +const rx_identifier = tag_regexp ` ^( + [ a-z A-Z _ $ ] + [ a-z A-Z 0-9 _ $ ]* +)$`; +const rx_module = tag_regexp ` ^ [ a-z A-Z 0-9 _ $ : . @ \- \/ ]+ $ `; +const rx_bad_property = tag_regexp ` + ^_ + | \$ + | Sync $ + | _ $ +`; +// star slash +const rx_star_slash = tag_regexp ` \* \/ `; +// slash star +const rx_slash_star = tag_regexp ` \/ \* `; +// slash star or ending slash +const rx_slash_star_or_slash = tag_regexp ` \/ \* | \/ $ `; +// uncompleted work comment +const rx_todo = tag_regexp ` \b (?: + todo + | TO \s? DO + | HACK +) \b `; +// tab +const rx_tab = /\t/g; +// directive +const rx_directive = tag_regexp ` ^ ( + jslint + | property + | global +) \s+ ( .* ) $ `; +const rx_directive_part = tag_regexp ` ^ ( + [ a-z A-Z $ _ ] [ a-z A-Z 0-9 $ _ ]* +) (?: + : \s* ( true | false ) +)? ,? \s* ( .* ) $ `; +// token +const rx_token = tag_regexp ` ^ ( + (\s+) + | ( + [ a-z A-Z _ $ ] + [ a-z A-Z 0-9 _ $ ]* + ) + | [ + ( ) { } \[ \] , : ; ' " ~ \` + ] + | \? [ ? . ]? + | = (?: + = =? + | > + )? + | \.+ + | \* [ * \/ = ]? + | \/ [ * \/ ]? + | \+ [ = + ]? + | - [ = \- ]? + | [ \^ % ] =? + | & [ & = ]? + | \| [ | = ]? + | >{1,3} =? + | < = "a" && string <= "z\uffff") + || (string >= "A" && string <= "Z\uffff") + ); +} + +let anon; // The guessed name for anonymous functions. +let blockage; // The current block. +let block_stack; // The stack of blocks. +let declared_globals; // The object containing the global declarations. +let directives; // The directive comments. +let directive_mode; // true if directives are still allowed. +let early_stop; // true if JSLint cannot finish. +let exports; // The exported names and values. +let froms; // The array collecting all import-from strings. +let fudge; // true if the natural numbers start with 1. +let functionage; // The current function. +let functions; // The array containing all of the functions. +let global; // The global object; the outermost context. +let json_mode; // true if parsing JSON. +let lines; // The array containing source lines. +let mega_mode; // true if currently parsing a megastring literal. +let module_mode; // true if import or export was used. +let next_token; // The next token to be examined in the parse. +let option; // The options parameter. +let property; // The object containing the tallied property names. +let shebang; // true if a #! was seen on the first line. +let stack; // The stack of functions. +let syntax; // The object containing the parser. +let token; // The current token being examined in the parse. +let token_nr; // The number of the next token. +let tokens; // The array of tokens. +let tenure; // The predefined property registry. +let tree; // The abstract parse tree. +let var_mode; // "var" if using var; "let" if using let. +let warnings; // The array collecting all generated warnings. + +// Error reportage functions: + +function artifact(the_token) { + +// Return a string representing an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); +} + +function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + const warning = { // ~~ + code, + column, + line, + name: "JSLintError" + }; + if (a !== undefined) { + warning.a = a; + } + if (b !== undefined) { + warning.b = b; + } + if (c !== undefined) { + warning.c = c; + } + if (d !== undefined) { + warning.d = d; + } + warning.message = bundle[code].replace(rx_supplant, function ( + ignore, + filling + ) { + assert_or_throw( + warning[filling] !== undefined, + "Expected warning[filling] !== undefined." + ); + return warning[filling]; + }); + +// Include stack_trace for jslint to debug itself for errors. + + if (option.debug) { + warning.stack_trace = new Error().stack; + } + warnings.push(warning); + return warning; +} + +function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); +} + +function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + if (the_token.warning === undefined) { + the_token.warning = warn_at( + code, + the_token.line, + the_token.from, + a || artifact(the_token), + b, + c, + d + ); + return the_token.warning; + } +} + +function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); +} + +// Tokenize: + +function tokenize(source) { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + +// If the source is not an array, then it is split into lines at the +// carriage return/linefeed. + + lines = ( + Array.isArray(source) + ? source + : source.split(rx_crlf) + ); + tokens = []; + + let char; // a popular character + let column = 0; // the column number of the next character + let first; // the first token + let from; // the starting column number of the token + let line = -1; // the line number of the next character + let nr = 0; // the next token number + let previous = global; // the previous token including comments + let prior = global; // the previous token excluding comments + let mega_from; // the starting column of megastring + let mega_line; // the starting line of megastring + let regexp_seen; // regular expression literal seen on this line + let snippet = ""; // a piece of string + let source_line = ""; // the remaining line source string + let whole_line = ""; // the whole line source string + + if (lines[0].startsWith("#!")) { + line = 0; + shebang = true; + } + + function next_line() { + +// Put the next line of source in source_line. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + let at; + if ( + !option.long + && whole_line.length > 80 + && !json_mode + && first + && !regexp_seen + ) { + warn_at("too_long", line, 80); + } + column = 0; + line += 1; + regexp_seen = false; + source_line = lines[line]; + whole_line = source_line || ""; + if (source_line !== undefined) { + at = source_line.search(rx_tab); + if (at >= 0) { + if (!option.white) { + +// cause: "\t" + + warn_at("use_spaces", line, at + 1); + } + source_line = source_line.replace(rx_tab, " "); + } + if (!option.white && source_line.slice(-1) === " ") { + warn_at( + "unexpected_trailing_space", + line, + source_line.length - 1 + ); + } + } + return source_line; + } + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions snip, next_char, back_char, +// some_digits, and escape help in the parsing of literals. + + function snip() { + +// Remove the last character from snippet. + + snippet = snippet.slice(0, -1); + } + + function next_char(match) { + +// Get the next character from the source line. Remove it from the source_line, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + return stop_at( + ( + char === "" + ? "expected_a" + : "expected_a_b" + ), + line, + column - 1, + match, + char + ); + } + if (source_line) { + char = source_line[0]; + source_line = source_line.slice(1); + snippet += char; + } else { + char = ""; + snippet += " "; + } + column += 1; + return char; + } + + function back_char() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the source_line. + + char = snippet.slice(-1); + source_line = char + source_line; + column -= char.length; + snip(); + return char; + } + + function some_digits(rx, quiet) { + const digits = source_line.match(rx)[0]; + const length = digits.length; + if (!quiet && length === 0) { + +// cause: "0x" + + warn_at("expected_digits_after_a", line, column, snippet); + } + column += length; + source_line = source_line.slice(length); + snippet += digits; + next_char(); + return length; + } + + function escape(extra) { + next_char("\\"); + if (escapeable[char] === true) { + return next_char(); + } + if (char === "") { + +// cause: "\"\\" + + return stop_at("unclosed_string", line, column); + } + if (char === "u") { + if (next_char("u") === "{") { + if (json_mode) { + warn_at("unexpected_a", line, column - 1, char); + } + if (some_digits(rx_hexs) > 5) { + +// cause: "\"\\u{123456}\"" + + warn_at("too_many_digits", line, column - 1); + } + if (char !== "}") { + stop_at("expected_a_before_b", line, column, "}", char); + } + return next_char(); + } + back_char(); + if (some_digits(rx_hexs, true) < 4) { + +// cause: "\"\\u0\"" + + warn_at("expected_four_digits", line, column - 1); + } + return; + } + if (extra && extra.indexOf(char) >= 0) { + return next_char(); + } + +// cause: "\"\\a\"" + + warn_at("unexpected_a_before_b", line, column - 2, "\\", char); + } + + function make(id, value, identifier) { + +// Make the token object and append it to the tokens list. + + const the_token = { + from, + id, + identifier: Boolean(identifier), + line, + nr, + thru: column + }; + tokens[nr] = the_token; + nr += 1; + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + directive_mode = false; + } + +// If the token is to have a value, give it one. + + if (value !== undefined) { + the_token.value = value; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option.white. + + if ( + previous.line === line + && previous.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (previous.id === "(comment)" || previous.id === "(regexp)") + ) { + warn( + "expected_space_a_b", + the_token, + artifact(previous), + artifact(the_token) + ); + } + if (previous.id === "." && id === "(number)") { + +// cause: ".0" + + warn("expected_a_before_b", previous, "0", "."); + } + if (prior.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// The previous token is used to detect adjacency problems. + + previous = the_token; + +// The prior token is a previous token that was not a comment. The prior token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (previous.id !== "(comment)") { + prior = previous; + } + return the_token; + } + + function parse_directive(the_comment, body) { + +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + + const result = body.match(rx_directive_part); + if (result) { + let allowed; + const name = result[1]; + const value = result[2]; + if (the_comment.directive === "jslint") { + allowed = allowed_option[name]; + if ( + typeof allowed === "boolean" + || typeof allowed === "object" + ) { + if (value === "true" || value === undefined) { + option[name] = true; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } else if (value === "false") { + option[name] = false; + } + } else { + +// cause: "/*jslint undefined*/" + + warn("bad_option_a", the_comment, name); + } + } else if (the_comment.directive === "property") { + if (tenure === undefined) { + tenure = empty(); + } + tenure[name] = true; + } else if (the_comment.directive === "global") { + if (value) { + +// cause: "/*global aa:false*/" + + warn("bad_option_a", the_comment, name + ":" + value); + } + declared_globals[name] = false; + module_mode = the_comment; + } + return parse_directive(the_comment, result[3]); + } + if (body) { + +// cause: "/*jslint !*/" + + return stop("bad_directive_a", the_comment, body); + } + } + + function comment(snippet) { + +// Make a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + + const the_comment = make("(comment)", snippet); + if (Array.isArray(snippet)) { + snippet = snippet.join(" "); + } + if (!option.devel && rx_todo.test(snippet)) { + +// cause: "//\u0074odo" + + warn("todo_comment", the_comment); + } + const result = snippet.match(rx_directive); + if (result) { + if (!directive_mode) { + +// cause: "0\n/*global aa*/" + + warn_at("misplaced_directive_a", line, from, result[1]); + } else { + the_comment.directive = result[1]; + parse_directive(the_comment, result[2]); + } + directives.push(the_comment); + } + return the_comment; + } + + function regexp() { + +// Parse a regular expression literal. + + let multi_mode = false; + let result; + let value; + regexp_seen = true; + + function quantifier() { + +// Match an optional quantifier. + + if (char === "?" || char === "*" || char === "+") { + next_char(); + } else if (char === "{") { + if (some_digits(rx_digits, true) === 0) { + warn_at("expected_a_before_b", line, column, "0", ","); + } + if (char === ",") { + some_digits(rx_digits, true); + } + next_char("}"); + } else { + return; + } + if (char === "?") { + next_char("?"); + } + } + + function subklass() { + +// Match a character in a character class. + + if (char === "\\") { + escape("BbDdSsWw-[]^"); + return true; + } + if ( + char === "" + || char === "[" + || char === "]" + || char === "/" + || char === "^" + || char === "-" + ) { + return false; + } + if (char === " ") { + warn_at("expected_a_b", line, column, "\\u0020", " "); + } else if (char === "`" && mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char(); + return true; + } + + function ranges() { + +// Match a range of subclasses. + + if (subklass()) { + if (char === "-") { + next_char("-"); + if (!subklass()) { + return stop_at( + "unexpected_a", + line, + column - 1, + "-" + ); + } + } + return ranges(); + } + } + + function klass() { + +// Match a class. + + next_char("["); + if (char === "^") { + next_char("^"); + } + (function classy() { + ranges(); + if (char !== "]" && char !== "") { + warn_at( + "expected_a_before_b", + line, + column, + "\\", + char + ); + next_char(); + return classy(); + } + }()); + next_char("]"); + } + + function choice() { + + function group() { + +// Match a group that starts with left paren. + + next_char("("); + if (char === "?") { + next_char("?"); + if (char === "=" || char === "!") { + next_char(); + } else { + next_char(":"); + } + } else if (char === ":") { + warn_at("expected_a_before_b", line, column, "?", ":"); + } + choice(); + next_char(")"); + } + + function factor() { + +// Parse current character in regexp. + + if ( + char === "" + || char === "/" + || char === "]" + || char === ")" + ) { + return false; + } + if (char === "(") { + group(); + return true; + } + if (char === "[") { + klass(); + return true; + } + if (char === "\\") { + escape("BbDdSsWw^${}[]():=!.|*+?"); + return true; + } + if ( + char === "?" + || char === "+" + || char === "*" + || char === "}" + || char === "{" + ) { + warn_at( + "expected_a_before_b", + line, + column - 1, + "\\", + char + ); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column - 1, "`"); + } + } else if (char === " ") { + warn_at( + "expected_a_b", + line, + column - 1, + "\\s", + " " + ); + } else if (char === "$") { + if (source_line[0] !== "/") { + multi_mode = true; + } + } else if (char === "^") { + if (snippet !== "^") { + multi_mode = true; + } + } + next_char(); + return true; + } + + function sequence(follow) { + if (factor()) { + quantifier(); + return sequence(true); + } + if (!follow) { + +// cause: "/ /" + + warn_at("expected_regexp_factor_a", line, column, char); + } + } + +// Match a choice (a sequence that can be followed by | and another choice). + + sequence(); + if (char === "|") { + next_char("|"); + return choice(); + } + } + +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + next_char(); + if (char === "=") { + warn_at("expected_a_before_b", line, column, "\\", "="); + } + choice(); + +// Make sure there is a closing slash. + + snip(); + value = snippet; + next_char("/"); + +// Process dangling flag letters. + + const allowed = { + g: true, + i: true, + m: true, + u: true, + y: true + }; + const flag = empty(); + (function make_flag() { + if (is_letter(char)) { + if (allowed[char] !== true) { + warn_at("unexpected_a", line, column, char); + } + allowed[char] = false; + flag[char] = true; + next_char(); + return make_flag(); + } + }()); + back_char(); + if (char === "/" || char === "*") { + return stop_at("unexpected_a", line, from, char); + } + result = make("(regexp)", char); + result.flag = flag; + result.value = value; + if (multi_mode && !flag.m) { + +// cause: "aa=/$^/" + + warn_at("missing_m", line, column); + } + return result; + } + + function string(quote) { + +// Make a string token. + + let the_token; + snippet = ""; + next_char(); + + return (function next() { + if (char === quote) { + snip(); + the_token = make("(string)", snippet); + the_token.quote = quote; + return the_token; + } + if (char === "") { + +// cause: "\"" + + return stop_at("unclosed_string", line, column); + } + if (char === "\\") { + escape(quote); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char("`"); + } else { + next_char(); + } + return next(); + }()); + } + + function frack() { + if (char === ".") { + some_digits(rx_digits); + } + if (char === "E" || char === "e") { + next_char(); + if (char !== "+" && char !== "-") { + back_char(); + } + some_digits(rx_digits); + } + } + + function number() { + if (snippet === "0") { + next_char(); + if (char === ".") { + frack(); + } else if (char === "b") { + some_digits(rx_bits); + } else if (char === "o") { + some_digits(rx_octals); + } else if (char === "x") { + some_digits(rx_hexs); + } + } else { + next_char(); + frack(); + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + +// cause: "0a" + + return stop_at( + "unexpected_a_after_b", + line, + column - 1, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + back_char(); + return make("(number)", snippet); + } + + function lex() { + let array; + let i = 0; + let j = 0; + let last; + let result; + let the_token; + +// This should properly be a tail recursive function, but sadly, conformant +// implementations of ES6 are still rare. This is the ideal code: + +// if (!source_line) { +// source_line = next_line(); +// from = 0; +// return ( +// source_line === undefined +// ? ( +// mega_mode +// ? stop_at("unclosed_mega", mega_line, mega_from) +// : make("(end)") +// ) +// : lex() +// ); +// } + +// Unfortunately, incompetent JavaScript engines will sometimes fail to execute +// it correctly. So for now, we do it the old fashioned way. + + while (!source_line) { + source_line = next_line(); + from = 0; + if (source_line === undefined) { + return ( + mega_mode + ? stop_at("unclosed_mega", mega_line, mega_from) + : make("(end)") + ); + } + } + + from = column; + result = source_line.match(rx_token); + +// result[1] token +// result[2] whitespace +// result[3] identifier +// result[4] number +// result[5] rest + + if (!result) { + +// cause: "#" + + return stop_at( + "unexpected_char_a", + line, + column, + source_line[0] + ); + } + + snippet = result[1]; + column += snippet.length; + source_line = result[5]; + +// Whitespace was matched. Call lex again to get more. + + if (result[2]) { + return lex(); + } + +// The token is an identifier. + + if (result[3]) { + return make(snippet, undefined, true); + } + +// The token is a number. + + if (result[4]) { + return number(snippet); + } + +// The token is a string. + + if (snippet === "\"") { + return string(snippet); + } + if (snippet === "'") { + if (!option.single) { + +// cause: "''" + + warn_at("use_double", line, column); + } + return string(snippet); + } + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (snippet === "`") { + if (mega_mode) { + return stop_at("expected_a_b", line, column, "}", "`"); + } + snippet = ""; + mega_from = from; + mega_line = line; + mega_mode = true; + +// Parsing a mega literal is tricky. First make a ` token. + + make("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + (function part() { + const at = source_line.search(rx_mega); + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + if (at < 0) { + snippet += source_line + "\n"; + return ( + next_line() === undefined + +// cause: "`" + + ? stop_at("unclosed_mega", mega_line, mega_from) + : part() + ); + } + snippet += source_line.slice(0, at); + column += at; + source_line = source_line.slice(at); + if (source_line[0] === "\\") { + snippet += source_line.slice(0, 2); + source_line = source_line.slice(2); + column += 2; + return part(); + } + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + make("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then make tokens that will become part of an expression until +// a } token is made. + + if (source_line[0] === "$") { + column += 2; + make("${"); + source_line = source_line.slice(2); + (function expr() { + const id = lex().id; + if (id === "{") { + return stop_at( + "expected_a_b", + line, + column, + "}", + "{" + ); + } + if (id !== "}") { + return expr(); + } + }()); + return part(); + } + }()); + source_line = source_line.slice(1); + column += 1; + mega_mode = false; + return make("`"); + } + +// The token is a // comment. + + if (snippet === "//") { + snippet = source_line; + source_line = ""; + the_token = comment(snippet); + if (mega_mode) { + +// cause: "`${//}`" + + warn("unexpected_comment", the_token, "`"); + } + return the_token; + } + +// The token is a /* comment. + + if (snippet === "/*") { + array = []; + if (source_line[0] === "/") { + warn_at("unexpected_a", line, column + i, "/"); + } + (function next() { + if (source_line > "") { + i = source_line.search(rx_star_slash); + if (i >= 0) { + return; + } + j = source_line.search(rx_slash_star); + if (j >= 0) { + +// cause: "/*/*" + + warn_at("nested_comment", line, column + j); + } + } + array.push(source_line); + source_line = next_line(); + if (source_line === undefined) { + +// cause: "/*" + + return stop_at("unclosed_comment", line, column); + } + return next(); + }()); + snippet = source_line.slice(0, i); + j = snippet.search(rx_slash_star_or_slash); + if (j >= 0) { + +// cause: "/*/**/" + + warn_at("nested_comment", line, column + j); + } + array.push(snippet); + column += i + 2; + source_line = source_line.slice(i + 2); + return comment(array); + } + +// The token is a slash. + + if (snippet === "/") { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + if (prior.identifier) { + if (!prior.dot) { + if (prior.id === "return") { + return regexp(); + } + if ( + prior.id === "(begin)" + || prior.id === "case" + || prior.id === "delete" + || prior.id === "in" + || prior.id === "instanceof" + || prior.id === "new" + || prior.id === "typeof" + || prior.id === "void" + || prior.id === "yield" + ) { + the_token = regexp(); + +// cause: "/./" +// cause: "case /./" +// cause: "delete /./" +// cause: "in /./" +// cause: "instanceof /./" +// cause: "new /./" +// cause: "typeof /./" +// cause: "void /./" +// cause: "yield /./" + + return stop("unexpected_a", the_token); + } + } + } else { + last = prior.id[prior.id.length - 1]; + if ("(,=:?[".indexOf(last) >= 0) { + return regexp(); + } + if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) { + the_token = regexp(); + +// cause: "!/./" + + warn("wrap_regexp", the_token); + return the_token; + } + } + if (source_line[0] === "=") { + column += 1; + source_line = source_line.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + } + return make(snippet); + } + + first = lex(); + json_mode = first.id === "{" || first.id === "["; + +// This loop will be replaced with a recursive call to lex when ES6 has been +// finished and widely deployed and adopted. + + while (true) { + if (lex().id === "(end)") { + break; + } + } +} + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + +function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!rx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!rx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property[id] === "number") { + property[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (tenure !== undefined) { + if (tenure[id] !== true) { + +// cause: "/*property aa*/\naa.bb" + + warn("unregistered_property_a", name); + } + } else { + if (name.identifier && rx_bad_property.test(id)) { + +// cause: "aa._" + + warn("bad_property_a", name); + } + } + property[id] = 1; + } + return id; +} + +function dispense() { + +// Deliver the next token, skipping the comments. + + const cadet = tokens[token_nr]; + token_nr += 1; + if (cadet.id === "(comment)") { + if (json_mode) { + warn("unexpected_a", cadet); + } + return dispense(); + } else { + return cadet; + } +} + +function lookahead() { + +// Look ahead one token without advancing. + + const old_token_nr = token_nr; + const cadet = dispense(true); + token_nr = old_token_nr; + return cadet; +} + +function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if (token.identifier && token.id !== "function") { + anon = token.id; + } else if (token.id === "(string)" && rx_identifier.test(token.value)) { + anon = token.value; + } + +// Attempt to match next_token with an expected id. + + if (id !== undefined && next_token.id !== id) { + return ( + match === undefined + +// cause: "()" + + ? stop("expected_a_b", next_token, id, artifact()) + +// cause: "{\"aa\":0" + + : stop( + "expected_a_b_from_c_d", + next_token, + id, + artifact(match), + +// Return the fudged line number of an artifact. + + match.line + fudge, + artifact(next_token) + ) + ); + } + +// Promote the tokens, skipping comments. + + token = next_token; + next_token = dispense(); + if (next_token.id === "(end)") { + token_nr -= 1; + } +} + +// Parsing of JSON is simple: + +function json_value() { + let negative; + if (next_token.id === "{") { + return (function json_object() { + const brace = next_token; + const object = empty(); + const properties = []; + brace.expression = properties; + advance("{"); + if (next_token.id !== "}") { + (function next() { + let name; + let value; + if (next_token.quote !== "\"") { + warn( + "unexpected_a", + next_token, + next_token.quote + ); + } + name = next_token; + advance("(string)"); + if (object[token.value] !== undefined) { + +// cause: "{\"aa\":0,\"aa\":0}" + + warn("duplicate_a", token); + } else if (token.value === "__proto__") { + +// cause: "{\"__proto__\":0}" + + warn("bad_property_a", token); + } else { + object[token.value] = token; + } + advance(":"); + value = json_value(); + value.label = name; + properties.push(value); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("}", brace); + return brace; + }()); + } + if (next_token.id === "[") { + return (function json_array() { + const bracket = next_token; + const elements = []; + bracket.expression = elements; + advance("["); + if (next_token.id !== "]") { + (function next() { + elements.push(json_value()); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]", bracket); + return bracket; + }()); + } + if ( + next_token.id === "true" + || next_token.id === "false" + || next_token.id === "null" + ) { + advance(); + return token; + } + if (next_token.id === "(number)") { + if (!rx_JSON_number.test(next_token.value)) { + warn("unexpected_a"); + } + advance(); + return token; + } + if (next_token.id === "(string)") { + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + advance(); + return token; + } + if (next_token.id === "-") { + negative = next_token; + negative.arity = "unary"; + advance("-"); + advance("(number)"); + if (!rx_JSON_number.test(token.value)) { + warn("unexpected_a", token); + } + negative.expression = token; + return negative; + } + stop("unexpected_a"); +} + +// Now we parse JavaScript. + +function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + const id = name.id; + +// Reserved words may not be enrolled. + + if (syntax[id] !== undefined && id !== "ignore") { + +// cause: "let undefined" + + warn("reserved_a", name); + } else { + +// Has the name been enrolled in this context? + + let earlier = functionage.context[id]; + if (earlier) { + +// cause: "let aa;let aa" + + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + +// Has the name been enrolled in an outer context? + + } else { + stack.forEach(function (value) { + const item = value.context[id]; + if (item !== undefined) { + earlier = item; + } + }); + if (earlier) { + if (id === "ignore") { + if (earlier.role === "variable") { + +// cause: "let ignore;function aa(ignore) {}" + + warn("unexpected_a", name); + } + } else { + if ( + ( + role !== "exception" + || earlier.role !== "exception" + ) + && role !== "parameter" + && role !== "function" + ) { + +// cause: "function aa(){var aa;}" +// cause: "function aa(){try{aa();}catch(aa){aa();}}" + + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + } + } + } + +// Enroll it. + + functionage.context[id] = name; + name.dead = true; + name.parent = functionage; + name.init = false; + name.role = role; + name.used = 0; + name.writable = !readonly; + } + } +} + +function expression(rbp, initial) { + +// This is the heart of the Pratt parser. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// nud Null denotation +// led Left denotation +// lbp Left binding power +// rbp Right binding power + +// It processes a nud (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop. It +// returns the expression's parse tree. + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement, + + if (!initial) { + advance(); + } + the_symbol = syntax[token.id]; + if (the_symbol !== undefined && the_symbol.nud !== undefined) { + left = the_symbol.nud(); + } else if (token.identifier) { + left = token; + left.arity = "variable"; + } else { + +// cause: "!" + + return stop("unexpected_a", token); + } + (function right() { + the_symbol = syntax[next_token.id]; + if ( + the_symbol !== undefined + && the_symbol.led !== undefined + && rbp < the_symbol.lbp + ) { + advance(); + left = the_symbol.led(left); + return right(); + } + }()); + return left; +} + +function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = next_token; + let the_value; + +// cause: "do{}while()" +// cause: "if(){}" +// cause: "while(){}" + + the_paren.free = true; + advance("("); + the_value = expression(0); + advance(")"); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (anticondition[the_value.id] === true) { + warn("unexpected_a", the_value); + } + return the_value; +} + +function is_weird(thing) { + return ( + thing.id === "(regexp)" + || thing.id === "{" + || thing.id === "=>" + || thing.id === "function" + || (thing.id === "[" && thing.arity === "unary") + ); +} + +function are_similar(a, b) { + +// cause: "0&&0" + + assert_or_throw(a !== b, "Expected a !== b."); +// if (a === b) { +// return true; +// } + if (Array.isArray(a)) { + return ( + Array.isArray(b) + && a.length === b.length + && a.every(function (value, index) { + +// cause: "`${0}`&&`${0}`" + + return are_similar(value, b[index]); + }) + ); + } + assert_or_throw(!Array.isArray(b), "Expected !Array.isArray(b)."); +// if (Array.isArray(b)) { +// return false; +// } + if (a.id === "(number)" && b.id === "(number)") { + return a.value === b.value; + } + let a_string; + let b_string; + if (a.id === "(string)") { + a_string = a.value; + } else if (a.id === "`" && a.constant) { + a_string = a.value[0]; + } + if (b.id === "(string)") { + b_string = b.value; + } else if (b.id === "`" && b.constant) { + b_string = b.value[0]; + } + if (typeof a_string === "string") { + return a_string === b_string; + } + if (is_weird(a) || is_weird(b)) { + return false; + } + if (a.arity === b.arity && a.id === b.id) { + +// cause: "aa.bb&&aa.bb" + + if (a.id === ".") { + return ( + are_similar(a.expression, b.expression) + && are_similar(a.name, b.name) + ); + } + +// cause: "+0&&+0" + + if (a.arity === "unary") { + return are_similar(a.expression, b.expression); + } + if (a.arity === "binary") { + +// cause: "aa[0]&&aa[0]" + + return ( + a.id !== "(" + && are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + ); + } + if (a.arity === "ternary") { + +// cause: "aa=(``?``:``)&&(``?``:``)" + + return ( + are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + && are_similar(a.expression[2], b.expression[2]) + ); + } + assert_or_throw( + a.arity !== "function" && a.arity !== "regexp", + "Expected a.arity !== \"function\" && a.arity !== \"regexp\"." + ); +// if (a.arity === "function" && a.arity === "regexp") { +// return false; +// } + +// cause: "undefined&&undefined" + + return true; + } + +// cause: "null&&undefined" + + return false; +} + +function semicolon() { + +// Try to match a semicolon. + + if (next_token.id === ";") { + advance(";"); + } else { + +// cause: "0" +// cause: "0\n0" + + warn_at( + "expected_a_b", + token.line, + token.thru, + ";", + artifact(next_token) + ); + } + anon = "anonymous"; +} + +function statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token.identifier && next_token.id === ":") { + the_label = token; + if (the_label.id === "ignore") { + warn("unexpected_a", the_label); + } + advance(":"); + if ( + next_token.id === "do" + || next_token.id === "for" + || next_token.id === "switch" + || next_token.id === "while" + ) { + enroll(the_label, "label", true); + the_label.init = true; + the_label.dead = false; + the_statement = statement(); + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + +// cause: "aa:" + + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token; + first.statement = true; + the_symbol = syntax[first.id]; + if (the_symbol !== undefined && the_symbol.fud !== undefined) { + the_symbol.disrupt = false; + the_symbol.statement = true; + the_statement = the_symbol.fud(); + } else { + +// It is an expression statement. + + the_statement = expression(0, true); + if (the_statement.wrapped && the_statement.id !== "(") { + +// cause: "(0)" + + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; +} + +function statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const array = []; + (function next(disrupt) { + if ( + next_token.id !== "}" + && next_token.id !== "case" + && next_token.id !== "default" + && next_token.id !== "else" + && next_token.id !== "(end)" + ) { + let a_statement = statement(); + array.push(a_statement); + if (disrupt) { + +// cause: "while(0){break;0;}" + + warn("unreachable_a", a_statement); + } + return next(a_statement.disrupt); + } + }(false)); + return array; +} + +function not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === global) { + warn("unexpected_at_top_level_a", thing); + } +} + +function top_level_only(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== global) { + +// cause: "if(0){import aa from \"aa\";}" + + warn("misplaced_a", the_thing); + } +} + +function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token; + the_block.arity = "statement"; + the_block.body = special === "body"; + +// Top level function bodies may include the "use strict" pragma. + + if ( + special === "body" + && stack.length === 1 + && next_token.value === "use strict" + ) { + next_token.statement = true; + advance("(string)"); + advance(";"); + } + stmts = statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option.devel && special !== "ignore") { + +// cause: "function aa(){}" + + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; +} + +function mutation_check(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + +// cause: "0=0" + + warn("bad_assignment_a", the_thing); + return false; + } + return true; +} + +function left_check(left, right) { + +// Warn if the left is not one of these: +// e.b +// e[b] +// e() +// ?: +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !left_check(left.expression[1]) + && !left_check(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "?." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right); + return false; + } + return true; +} + +// These functions are used to specify the grammar of our language: + +function symbol(id, bp) { + +// Make a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax[id] = the_symbol; + } + return the_symbol; +} + +function assignment(id) { + +// Make an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led = function (left) { + const the_token = token; + let right; + the_token.arity = "assignment"; + right = expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "pre" + || right.arity === "post" + ) { + warn("unexpected_a", right); + } + mutation_check(left); + return the_token; + }; + return the_symbol; +} + +function constant(id, type, value) { + +// Make a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud = ( + typeof value === "function" + ? value + : function () { + token.constant = true; + if (value !== undefined) { + token.value = value; + } + return token; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; +} + +function infix(id, bp, f) { + +// Make an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, expression(bp)]; + return the_token; + }; + return the_symbol; +} + +function infixr(id, bp) { + +// Make a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + the_token.expression = [left, expression(bp - 1)]; + return the_token; + }; + return the_symbol; +} + +function post(id) { + +// Make one of the post operators. + + const the_symbol = symbol(id, 150); + the_symbol.led = function (left) { + token.expression = left; + token.arity = "post"; + mutation_check(token.expression); + return token; + }; + return the_symbol; +} + +function pre(id) { + +// Make one of the pre operators. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "pre"; + the_token.expression = expression(150); + mutation_check(the_token.expression); + return the_token; + }; + return the_symbol; +} + +function prefix(id, f) { + +// Make a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = expression(150); + return the_token; + }; + return the_symbol; +} + +function stmt(id, f) { + +// Make a statement. + + const the_symbol = symbol(id); + the_symbol.fud = function () { + token.arity = "statement"; + return f(); + }; + return the_symbol; +} + +function ternary(id1, id2) { + +// Make a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led = function (left) { + const the_token = token; + const second = expression(20); + advance(id2); + token.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, expression(10)]; + if (next_token.id !== ")") { + +// cause: "0?0:0" + + warn("use_open", the_token); + } + return the_token; + }; + return the_symbol; +} + +// Begin defining the language. + +syntax = empty(); + +symbol("}"); +symbol(")"); +symbol("]"); +symbol(","); +symbol(";"); +symbol(":"); +symbol("*/"); +symbol("async"); +symbol("await"); +symbol("case"); +symbol("catch"); +symbol("class"); +symbol("default"); +symbol("else"); +symbol("enum"); +symbol("finally"); +symbol("implements"); +symbol("interface"); +symbol("package"); +symbol("private"); +symbol("protected"); +symbol("public"); +symbol("static"); +symbol("super"); +symbol("void"); +symbol("yield"); + +constant("(number)", "number"); +constant("(regexp)", "regexp"); +constant("(string)", "string"); +constant("arguments", "object", function () { + warn("unexpected_a", token); + return token; +}); +constant("eval", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("false", "boolean", false); +constant("Function", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("ignore", "undefined", function () { + warn("unexpected_a", token); + return token; +}); +constant("Infinity", "number", Infinity); +constant("isFinite", "function", function () { + warn("expected_a_b", token, "Number.isFinite", "isFinite"); + return token; +}); +constant("isNaN", "function", function () { + +// cause: "isNaN(0)" + + warn("number_isNaN", token); + return token; +}); +constant("NaN", "number", NaN); +constant("null", "null", null); +constant("this", "object", function () { + if (!option.this) { + warn("unexpected_a", token); + } + return token; +}); +constant("true", "boolean", true); +constant("undefined", "undefined"); + +assignment("="); +assignment("+="); +assignment("-="); +assignment("*="); +assignment("/="); +assignment("%="); +assignment("&="); +assignment("|="); +assignment("^="); +assignment("<<="); +assignment(">>="); +assignment(">>>="); + +infix("??", 35); +infix("||", 40); +infix("&&", 50); +infix("|", 70); +infix("^", 80); +infix("&", 90); +infix("==", 100); +infix("===", 100); +infix("!=", 100); +infix("!==", 100); +infix("<", 110); +infix(">", 110); +infix("<=", 110); +infix(">=", 110); +infix("in", 110); +infix("instanceof", 110); +infix("<<", 120); +infix(">>", 120); +infix(">>>", 120); +infix("+", 130); +infix("-", 130); +infix("*", 140); +infix("/", 140); +infix("%", 140); +infixr("**", 150); +infix("(", 160, function (left) { + const the_paren = token; + let the_argument; + if (left.id !== "function") { + left_check(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (next_token.id !== ")") { + (function next() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + +// cause: "aa(0)" + + the_paren.free = true; + if (the_argument.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + +// cause: "aa()" +// cause: "aa(0,0)" + + the_paren.free = false; + } + return the_paren; +}); +infix(".", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("?.", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("[", 170, function (left) { + const the_token = token; + const the_subscript = expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + const name = survey(the_subscript); + if (rx_identifier.test(name)) { + +// cause: "aa[`aa`]" + + warn("subscript_a", the_subscript, name); + } + } + left_check(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; +}); +infix("=>", 170, function (left) { + +// cause: "aa=>0" + + return stop("wrap_parameter", left); +}); + +function do_tick() { + const the_tick = token; + the_tick.value = []; + the_tick.expression = []; + if (next_token.id !== "`") { + (function part() { + advance("(string)"); + the_tick.value.push(token); + if (next_token.id === "${") { + advance("${"); + the_tick.expression.push(expression(0)); + advance("}"); + return part(); + } + }()); + } + advance("`"); + return the_tick; +} + +infix("`", 160, function (left) { + const the_tick = do_tick(); + left_check(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; +}); + +post("++"); +post("--"); +pre("++"); +pre("--"); + +prefix("+"); +prefix("-"); +prefix("~"); +prefix("!"); +prefix("!!"); +prefix("[", function () { + const the_token = token; + the_token.expression = []; + if (next_token.id !== "]") { + (function next() { + let element; + let ellipsis = false; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + element = expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]"); + return the_token; +}); +prefix("/=", function () { + +// cause: "/=" + + stop("expected_a_b", token, "/\\=", "/="); +}); +prefix("=>", function () { + return stop("expected_a_before_b", token, "()", "=>"); +}); +prefix("new", function () { + const the_new = token; + const right = expression(160); + if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "()", artifact(next_token)); + } + the_new.expression = right; + return the_new; +}); +prefix("typeof"); +prefix("void", function () { + const the_void = token; + +// cause: "void" + + warn("unexpected_a", the_void); + the_void.expression = expression(0); + return the_void; +}); + +function parameter_list() { + const list = []; + let optional; + const signature = ["("]; + if (next_token.id !== ")" && next_token.id !== "(end)") { + (function parameter() { + let ellipsis = false; + let param; + if (next_token.id === "{") { + let a; + let b = ""; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("{"); + signature.push("{"); + (function subparameter() { + let subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + survey(subparam); + a = b; + b = String(subparam.value || subparam.id); + if (a > b) { + if (!option.unordered) { + +// cause: "function aa({bb,aa}){}" + + warn("unordered_param_a", subparam); + } + } + advance(); + signature.push(subparam.id); + if (next_token.id === ":") { + advance(":"); + advance(); + token.label = subparam; + subparam = token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + } + if (next_token.id === "=") { + advance("="); + subparam.expression = expression(); + param.open = true; + } + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return subparameter(); + } + }()); + list.push(param); + advance("}"); + signature.push("}"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else if (next_token.id === "[") { + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("["); + signature.push("[]"); + (function subparameter() { + const subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + if (next_token.id === "=") { + advance("="); + subparam.expression = expression(); + param.open = true; + } + if (next_token.id === ",") { + advance(","); + return subparameter(); + } + }()); + list.push(param); + advance("]"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else { + if (next_token.id === "...") { + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + } + if (!next_token.identifier) { + return stop("expected_identifier_a"); + } + param = next_token; + list.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (next_token.id === "=") { + optional = param; + advance("="); + param.expression = expression(0); + } else { + if (optional !== undefined) { + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } + } + }()); + } + advance(")"); + signature.push(")"); + return [list, signature.join("")]; +} + +function do_function(the_function) { + let name; + if (the_function === undefined) { + the_function = token; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!next_token.identifier) { + +// cause: "function*aa(){}" + + return stop("expected_identifier_a", next_token); + } + name = next_token; + enroll(name, "variable", true); + the_function.name = name; + name.init = true; + name.calls = empty(); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + if (next_token.identifier) { + name = next_token; + the_function.name = name; + advance(); + } else { + the_function.name = anon; + } + } + } else { + name = the_function.name; + } + the_function.level = functionage.level + 1; + if (mega_mode) { + warn("unexpected_a", the_function); + } + +// Don't make functions in loops. It is inefficient, and it can lead to scoping +// errors. + + if (functionage.loop > 0) { + +// cause: "while(0){aa.map(function(){});}" + + warn("function_in_loop", the_function); + } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + the_function.context = empty(); + the_function.finally = 0; + the_function.loop = 0; + the_function.switch = 0; + the_function.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functions.push(the_function); + functionage = the_function; + if (the_function.arity !== "statement" && typeof name === "object") { + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// Parse the parameter list. + + advance("("); + +// cause: "function(){}" + + token.free = false; + token.arity = "function"; + [functionage.parameters, functionage.signature] = parameter_list(); + functionage.parameters.forEach(function enroll_parameter(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + name.names.forEach(enroll_parameter); + } + }); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && next_token.line === token.line + ) { + return stop("unexpected_a", next_token); + } + if ( + next_token.id === "." + || next_token.id === "?." + || next_token.id === "[" + ) { + warn("unexpected_a"); + } + +// Restore the previous context. + + functionage = stack.pop(); + return the_function; +} + +function do_async() { + let the_async; + let the_function; + the_async = token; + advance("function"); + the_function = token; + the_function.is_async = true; + the_function.arity = the_async.arity; + do_function(); + if (!the_function.has_await) { + +// cause: "async function aa(){}" + + warn("missing_await_statement", the_function); + } + return the_function; +} + +prefix("async", do_async); +prefix("function", do_function); +prefix("await", function () { + let the_await; + the_await = token; + if (!functionage.is_async) { + return stop("unexpected_a", the_await); + } + functionage.has_await = true; + the_await.expression = expression(150); + return the_await; +}); + +function fart(pl) { + advance("=>"); + const the_fart = token; + the_fart.arity = "binary"; + the_fart.name = "=>"; + the_fart.level = functionage.level + 1; + functions.push(the_fart); + if (functionage.loop > 0) { + +// cause: "while(0){aa.map(()=>0);}" + + warn("function_in_loop", the_fart); + } + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + the_fart.context = empty(); + the_fart.finally = 0; + the_fart.loop = 0; + the_fart.switch = 0; + the_fart.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functionage = the_fart; + the_fart.parameters = pl[0]; + the_fart.signature = pl[1]; + the_fart.parameters.forEach(function (name) { + enroll(name, "parameter", true); + }); + if (next_token.id === "{") { + warn("expected_a_b", the_fart, "function", "=>"); + the_fart.block = block("body"); + } else { + the_fart.expression = expression(0); + } + functionage = stack.pop(); + return the_fart; +} + +prefix("(", function () { + const the_paren = token; + let the_value; + const cadet = lookahead().id; + +// We can distinguish between a parameter list for => and a wrapped expression +// with one token of lookahead. + + if ( + next_token.id === ")" + || next_token.id === "..." + || (next_token.identifier && (cadet === "," || cadet === "=")) + ) { + +// cause: "()=>0" + + the_paren.free = false; + return fart(parameter_list()); + } + +// cause: "(0)" + + the_paren.free = true; + the_value = expression(0); + if (the_value.wrapped === true) { + +// cause: "((0))" + + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + if (next_token.id === "=>") { + if (the_value.arity !== "variable") { + if (the_value.id === "{" || the_value.id === "[") { + warn("expected_a_before_b", the_paren, "function", "("); + return stop("expected_a_b", next_token, "{", "=>"); + } + return stop("expected_identifier_a", the_value); + } + the_paren.expression = [the_value]; + return fart([the_paren.expression, "(" + the_value.id + ")"]); + } + return the_value; +}); +prefix("`", do_tick); +prefix("{", function () { + const the_brace = token; + const seen = empty(); + the_brace.expression = []; + if (next_token.id !== "}") { + let a; + let b = ""; + (function member() { + let extra; + let full; + let id; + let name = next_token; + let value; + advance(); + a = b; + b = String(name.value || name.id); + if (a > b) { + if (!option.unordered) { + +// cause: "aa={bb,aa}" + + warn("unordered_property_a", name); + } + } + if ( + (name.id === "get" || name.id === "set") + && next_token.identifier + ) { + if (!option.getset) { + +// cause: "aa={get aa(){}}" + + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + next_token.id; + name = next_token; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + +// cause: "aa={get aa(){},get aa(){}}" + + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + +// cause: "aa={aa,aa}" + + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (next_token.id === "}" || next_token.id === ",") { + if (typeof extra === "string") { + advance("("); + } + value = expression(Infinity, true); + } else if (next_token.id === "(") { + value = do_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + advance("("); + } + let the_colon = next_token; + advance(":"); + value = expression(0); + if (value.id === name.id && value.id !== "function") { + warn("unexpected_a", the_colon, ": " + name.id); + } + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + advance(":"); + value = expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (next_token.id === ",") { + advance(","); + return member(); + } + }()); + } + advance("}"); + return the_brace; +}); + +stmt(";", function () { + warn("unexpected_a", token); + return token; +}); +stmt("{", function () { + +// cause: "class aa{}" + + warn("naked_block", token); + return block("naked"); +}); +stmt("async", do_async); +stmt("break", function () { + const the_break = token; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (next_token.identifier && token.line === next_token.line) { + the_label = functionage.context[next_token.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + if (the_label !== undefined && the_label.dead) { + +// cause: "aa:{function aa(aa){break aa;}}" + + warn("out_of_scope_a"); + } else { + +// cause: "aa:{break aa;}" + + warn("not_label_a"); + } + } else { + the_label.used += 1; + } + the_break.label = next_token; + advance(); + } + advance(";"); + return the_break; +}); + +function do_var() { + const the_statement = token; + const is_const = the_statement.id === "const"; + the_statement.names = []; + +// A program may use var or let, but not both. + + if (!is_const) { + if (var_mode === undefined) { + var_mode = the_statement.id; + } else if (the_statement.id !== var_mode) { + warn( + "expected_a_b", + the_statement, + var_mode, + the_statement.id + ); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + +// cause: "switch(0){case 0:var aa}" + + warn("var_switch", the_statement); + } + if (functionage.loop > 0 && the_statement.id === "var") { + +// cause: "while(0){var aa;}" + + warn("var_loop", the_statement); + } + (function next() { + if (next_token.id === "{" && the_statement.id !== "var") { + let a; + let b = ""; + const the_brace = next_token; + advance("{"); + (function pair() { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + survey(name); + a = b; + b = String(name.value || name.id); + if (a > b) { + if (!option.unordered) { + +// cause: "let{bb,aa}=0" + + warn("unordered_param_a", name); + } + } + advance(); + if (next_token.id === ":") { + advance(":"); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + next_token.label = name; + the_statement.names.push(next_token); + enroll(next_token, "variable", is_const); + advance(); + the_brace.open = true; + } else { + the_statement.names.push(name); + enroll(name, "variable", is_const); + } + name.dead = false; + name.init = true; + if (next_token.id === "=") { + advance("="); + name.expression = expression(); + the_brace.open = true; + } + if (next_token.id === ",") { + advance(","); + return pair(); + } + }()); + advance("}"); + advance("="); + the_statement.expression = expression(0); + } else if (next_token.id === "[" && the_statement.id !== "var") { + const the_bracket = next_token; + advance("["); + (function element() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + advance(); + the_statement.names.push(name); + enroll(name, "variable", is_const); + name.dead = false; + name.init = true; + if (ellipsis) { + name.ellipsis = true; + } else { + if (next_token.id === "=") { + advance("="); + name.expression = expression(); + the_bracket.open = true; + } + if (next_token.id === ",") { + advance(","); + return element(); + } + } + }()); + advance("]"); + advance("="); + the_statement.expression = expression(0); + } else if (next_token.identifier) { + const name = next_token; + advance(); + if (name.id === "ignore") { + +// cause: "let ignore;function aa(ignore) {}" + + warn("unexpected_a", name); + } + enroll(name, "variable", is_const); + if (next_token.id === "=" || is_const) { + advance("="); + name.dead = false; + name.init = true; + name.expression = expression(0); + } + the_statement.names.push(name); + } else { + return stop("expected_identifier_a", next_token); + } + }()); + semicolon(); + return the_statement; +} + +stmt("const", do_var); +stmt("continue", function () { + const the_continue = token; + if (functionage.loop < 1 || functionage.finally > 0) { + warn("unexpected_a", the_continue); + } + not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; +}); +stmt("debugger", function () { + const the_debug = token; + if (!option.devel) { + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; +}); +stmt("delete", function () { + const the_token = token; + const the_value = expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; +}); +stmt("do", function () { + const the_do = token; + not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + +// cause: "function aa(){do{break;}while(0)}" + + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; +}); +stmt("export", function () { + const the_export = token; + let the_id; + let the_name; + let the_thing; + + function export_id() { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + the_id = next_token.id; + the_name = global.context[the_id]; + if (the_name === undefined) { + warn("unexpected_a"); + } else { + the_name.used += 1; + if (exports[the_id] !== undefined) { + +// cause: "let aa;export{aa,aa}" + + warn("duplicate_a"); + } + exports[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + } + + the_export.expression = []; + if (next_token.id === "default") { + if (exports.default !== undefined) { + +// cause: "export default 0;export default 0" + + warn("duplicate_a"); + } + advance("default"); + the_thing = expression(0); + if ( + the_thing.id !== "(" + || the_thing.expression[0].id !== "." + || the_thing.expression[0].expression.id !== "Object" + || the_thing.expression[0].name.id !== "freeze" + ) { + +// cause: "export default {}" + + warn("freeze_exports", the_thing); + } + if (next_token.id === ";") { + semicolon(); + } + exports.default = the_thing; + the_export.expression.push(the_thing); + } else { + if (next_token.id === "function") { + +// cause: "export function aa(){}" + + warn("freeze_exports"); + the_thing = statement(); + the_name = the_thing.name; + the_id = the_name.id; + +// cause: "let aa;export{aa};export function aa(){}" + + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a", the_name); + } + exports[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + warn("unexpected_a", next_token); + statement(); + } else if (next_token.id === "{") { + advance("{"); + (function loop() { + export_id(); + if (next_token.id === ",") { + advance(","); + return loop(); + } + }()); + advance("}"); + semicolon(); + } else { + stop("unexpected_a"); + } + } + module_mode = true; + return the_export; +}); +stmt("for", function () { + let first; + const the_for = token; + if (!option.for) { + warn("unexpected_a", the_for); + } + not_top_level(the_for); + functionage.loop += 1; + advance("("); + +// cause: "for(){}" + + token.free = true; + if (next_token.id === ";") { + +// cause: "for(;;){}" + + return stop("expected_a_b", the_for, "while (", "for (;"); + } + if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + return stop("unexpected_a"); + } + first = expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + +// cause: "for(0 in aa){}" + + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = expression(0); + advance(";"); + the_for.inc = expression(0); + if (the_for.inc.id === "++") { + +// cause: "for(aa;aa;aa++){}" + + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; +}); +stmt("function", do_function); +stmt("if", function () { + let the_else; + const the_if = token; + the_if.expression = condition(); + the_if.block = block(); + if (next_token.id === "else") { + advance("else"); + the_else = token; + the_if.else = ( + next_token.id === "if" + ? statement() + : block() + ); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + the_if.disrupt = true; + } else { + warn("unexpected_a", the_else); + } + } + } + return the_if; +}); +stmt("import", function () { + const the_import = token; + if (next_token.id === "(") { + the_import.arity = "unary"; + the_import.constant = true; + the_import.statement = false; + advance("("); + const string = expression(0); + if (string.id !== "(string)") { + +// cause: "import(aa)" + + warn("expected_string_a", string); + } + froms.push(token.value); + advance(")"); + advance("."); + advance("then"); + advance("("); + the_import.expression = expression(0); + advance(")"); + semicolon(); + return the_import; + } + let name; + if (typeof module_mode === "object") { + +// cause: "/*global aa*/\nimport aa from \"aa\"" + + warn("unexpected_directive_a", module_mode, module_mode.directive); + } + module_mode = true; + if (next_token.identifier) { + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + const names = []; + advance("{"); + if (next_token.id !== "}") { + while (true) { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (next_token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token; + if (!rx_module.test(token.value)) { + +// cause: "import aa from \"!aa\"" + + warn("bad_module_name_a", token); + } + froms.push(token.value); + semicolon(); + return the_import; +}); +stmt("let", do_var); +stmt("return", function () { + const the_return = token; + not_top_level(the_return); + if (functionage.finally > 0) { + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (next_token.id !== ";" && the_return.line === next_token.line) { + the_return.expression = expression(10); + } + advance(";"); + return the_return; +}); +stmt("switch", function () { + let dups = []; + let last; + let stmts; + const the_cases = []; + let the_disrupt = true; + const the_switch = token; + not_top_level(the_switch); + if (functionage.finally > 0) { + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + +// cause: "switch(){}" + + token.free = true; + the_switch.expression = expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + (function major() { + const the_case = next_token; + the_case.arity = "statement"; + the_case.expression = []; + (function minor() { + advance("case"); + token.switch = true; + const exp = expression(0); + if (dups.some(function (thing) { + return are_similar(thing, exp); + })) { + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (next_token.id === "case") { + return minor(); + } + }()); + stmts = statements(); + if (stmts.length < 1) { + warn("expected_statements_a"); + return; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "break;", + artifact(next_token) + ); + } + if (next_token.id === "case") { + return major(); + } + }()); + dups = undefined; + if (next_token.id === "default") { + const the_default = next_token; + advance("default"); + token.switch = true; + advance(":"); + the_switch.else = statements(); + if (the_switch.else.length < 1) { + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + const the_last = the_switch.else[the_switch.else.length - 1]; + if (the_last.id === "break" && the_last.label === undefined) { + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; +}); +stmt("throw", function () { + const the_throw = token; + the_throw.disrupt = true; + the_throw.expression = expression(10); + semicolon(); + if (functionage.try > 0) { + warn("unexpected_a", the_throw); + } + return the_throw; +}); +stmt("try", function () { + let the_catch; + let the_disrupt; + const the_try = token; + if (functionage.try > 0) { + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (next_token.id === "catch") { + let ignored = "ignore"; + the_catch = next_token; + the_try.catch = the_catch; + advance("catch"); + if (next_token.id === "(") { + advance("("); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + if (next_token.id !== "ignore") { + ignored = undefined; + the_catch.name = next_token; + enroll(next_token, "exception", true); + } + advance(); + advance(")"); + } + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "catch", + artifact(next_token) + ); + + } + if (next_token.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; +}); +stmt("var", do_var); +stmt("while", function () { + const the_while = token; + not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + +// cause: "function aa(){while(0){break;}}" + + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; +}); +stmt("with", function () { + +// cause: "with" + + stop("unexpected_a", token); +}); + +ternary("?", ":"); + +// Ambulation of the parse tree. + +function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; +} + +function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all of the +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + } + }; +} + +const posts = empty(); +const pres = empty(); +const preaction = action(pres); +const postaction = action(posts); +const preamble = amble(pres); +const postamble = amble(posts); + +function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + +// cause: "0&&0" +// cause: "(function(){}())" + + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.id === "function") { + +// cause: "aa=function(){}" + + walk_statement(thing.block); + } + if (thing.arity === "pre" || thing.arity === "post") { + +// cause: "aa=++aa" +// cause: "aa=--aa" + + warn("unexpected_a", thing); + } else if ( + +// cause: "aa=0" + + thing.arity === "statement" + || thing.arity === "assignment" + ) { + warn("unexpected_statement_a", thing); // deadcode? + } + postamble(thing); + } + } +} + +function walk_statement(thing) { + if (thing) { + if (Array.isArray(thing)) { + +// cause: "+[]" + + thing.forEach(walk_statement); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + +// cause: "0&&0" + + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + && thing.id !== "import" + && thing.id !== "await" + ) { + +// cause: "!0" +// cause: "+[]" +// cause: "0" + + warn("unexpected_expression_a", thing); + } + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + } +} + +function lookup(thing) { + if (thing.arity === "variable") { + +// Look up the variable in the current context. + + let the_variable = functionage.context[thing.id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable === undefined) { + stack.forEach(function (outer) { + const a_variable = outer.context[thing.id]; + if ( + a_variable !== undefined + && a_variable.role !== "label" + ) { + the_variable = a_variable; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (the_variable === undefined) { + if (declared_globals[thing.id] === undefined) { + +// cause: "aa" +// cause: "class aa{}" + + warn("undeclared_a", thing); + return; + } + the_variable = { + dead: false, + id: thing.id, + init: true, + parent: global, + role: "variable", + used: 0, + writable: false + }; + global.context[thing.id] = the_variable; + } + the_variable.closure = true; + functionage.context[thing.id] = the_variable; + } else if (the_variable.role === "label") { + +// cause: "aa:while(0){aa;}" + + warn("label_a", thing); + } + if ( + the_variable.dead + && ( + the_variable.calls === undefined + || functionage.name === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + ) { + +// cause: "function aa(){bb();}\nfunction bb(){}" + + warn("out_of_scope_a", thing); + } + return the_variable; + } +} + +function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); +} + +function preaction_function(thing) { + +// cause: "()=>0" +// cause: "(function (){}())" +// cause: "function aa(){}" + + if (thing.arity === "statement" && blockage.body !== true) { + +// cause: "if(0){function aa(){}\n}" + + warn("unexpected_a", thing); + } + stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + +// cause: "/*jslint getset*/\naa={get aa(aa){}}" + + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + +// cause: "/*jslint getset*/\naa={set aa(){}}" + + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); +} + +function bitwise_check(thing) { + if (!option.bitwise && bitwiseop[thing.id] === true) { + warn("unexpected_a", thing); + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + warn("unexpected_a", thing); + } +} + +function pop_block() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); +} + +function activate(name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.init = true; + } + } + blockage.live.push(name); +} + +function action_var(thing) { + thing.names.forEach(activate); +} + +preaction("assignment", bitwise_check); +preaction("binary", bitwise_check); +preaction("binary", function (thing) { + if (relationop[thing.id] === true) { + const left = thing.expression[0]; + const right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + +// cause: "NaN===NaN" + + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + +// cause: "typeof 0===0" + + warn("expected_string_a", right); + } + } else { + const value = right.value; + if (value === "null" || value === "undefined") { + +// cause: "typeof aa===\"undefined\"" + + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + +// cause: "typeof 0===\"aa\"" + + warn("expected_type_string_a", right, value); + } + } + } + } +}); +preaction("binary", "==", function (thing) { + +// cause: "0==0" + + warn("expected_a_b", thing, "===", "=="); +}); +preaction("binary", "!=", function (thing) { + +// cause: "0!=0" + + warn("expected_a_b", thing, "!==", "!="); +}); +preaction("binary", "=>", preaction_function); +preaction("binary", "||", function (thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + +// cause: "0&&0||0" + + warn("and", thang); + } + }); +}); +preaction("binary", "(", function (thing) { + const left = thing.expression[0]; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + const parent = functionage.name.parent; + if (parent) { + const left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + && left_variable.dead + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } +}); +preaction("binary", "in", function (thing) { + +// cause: "aa in aa" + + warn("infix_in", thing); +}); +preaction("binary", "instanceof", function (thing) { + +// cause: "0 instanceof 0" + + warn("unexpected_a", thing); +}); +preaction("statement", "{", function (thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; +}); +preaction("statement", "for", function (thing) { + if (thing.name !== undefined) { + const the_variable = lookup(thing.name); + if (the_variable !== undefined) { + the_variable.init = true; + if (!the_variable.writable) { + +// cause: "const aa=0;for(aa in aa){}" + + warn("bad_assignment_a", thing.name); + } + } + } + walk_statement(thing.initial); +}); +preaction("statement", "function", preaction_function); +preaction("unary", "~", bitwise_check); +preaction("unary", "function", preaction_function); +preaction("variable", function (thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } +}); + +function init_variable(name) { + const the_variable = lookup(name); + if (the_variable !== undefined) { + if (the_variable.writable) { + the_variable.init = true; + return; + } + } + warn("bad_assignment_a", name); +} + +postaction("assignment", "+=", function (thing) { + let right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } +}); +postaction("assignment", function (thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + if (thing.id === "=") { + if (thing.names !== undefined) { + if (Array.isArray(thing.names)) { + thing.names.forEach(init_variable); + } else { + init_variable(thing.names); + } + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.writable !== true) { + warn("bad_assignment_a", lvalue); + } + } + const right = syntax[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + warn("unexpected_a", thing.expression[1]); + } + } +}); + +function postaction_function(thing) { + delete functionage.finally; + delete functionage.loop; + delete functionage.switch; + delete functionage.try; + functionage = stack.pop(); + if (thing.wrapped) { + +// cause: "aa=(function(){})" + + warn("unexpected_parens", thing); + } + return pop_block(); +} + +postaction("binary", function (thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || are_similar(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + +// cause: "if(0===0){0}" + + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option.convert) { + if (thing.expression[0].value === "") { + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + +// cause: "aa=window[0]" + + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + +// cause: "aa=self[0]" + + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === "." || thing.id === "?.") { + if (thing.expression.id === "RegExp") { + +// cause: "aa=RegExp.aa" + + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + +// cause: "0- -0" + + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } +}); +postaction("binary", "&&", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + +// cause: "aa=0&&0" +// cause: "aa=`${0}`&&`${0}`" +// cause: "aa=(``?``:``)&&(``?``:``)" + + warn("weird_condition_a", thing); + } +}); +postaction("binary", "||", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + +// cause: "aa=0||0" + + warn("weird_condition_a", thing); + } +}); +postaction("binary", "=>", postaction_function); +postaction("binary", "(", function (thing) { + let left = thing.expression[0]; + let the_new; + let arg; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + +// cause: "aa=function(){}()" + + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + +// cause: "+new aa()" + + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option.eval) { + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + warn( + "expected_a_before_b", + left, + "new", + artifact(left) + ); + } + } + } else if (left.id === ".") { + let cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + cack = !cack; + } + if (rx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + warn("unexpected_a", the_new); + } else { + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + const paren = left.expression; + if (paren.id === "(") { + const array = paren.expression; + if (array.length === 1) { + const new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } +}); +postaction("binary", "[", function (thing) { + if (thing.expression[0].id === "RegExp") { + +// cause: "aa=RegExp[0]" + + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + +// cause: "aa[[0]]" + + warn("weird_expression_a", thing.expression[1]); + } +}); +postaction("statement", "{", pop_block); +postaction("statement", "const", action_var); +postaction("statement", "export", top_level_only); +postaction("statement", "for", function (thing) { + walk_statement(thing.inc); +}); +postaction("statement", "function", postaction_function); +postaction("statement", "import", function (the_thing) { + const name = the_thing.name; + if (name) { + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return top_level_only(the_thing); + } +}); +postaction("statement", "let", action_var); +postaction("statement", "try", function (thing) { + if (thing.catch !== undefined) { + const the_name = thing.catch.name; + if (the_name !== undefined) { + const the_variable = functionage.context[the_name.id]; + the_variable.dead = false; + the_variable.init = true; + } + walk_statement(thing.catch.block); + } +}); +postaction("statement", "var", action_var); +postaction("ternary", function (thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || are_similar(thing.expression[1], thing.expression[2]) + ) { + warn("unexpected_a", thing); + } else if (are_similar(thing.expression[0], thing.expression[1])) { + warn("expected_a_b", thing, "||", "?"); + } else if (are_similar(thing.expression[0], thing.expression[2])) { + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + +// cause: "(aa&&!aa?0:1)" + + warn("wrap_condition", thing.expression[0]); + } +}); +postaction("unary", function (thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option.convert) { + +// cause: "!!0" + + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } +}); +postaction("unary", "function", postaction_function); +postaction("unary", "+", function (thing) { + if (!option.convert) { + warn("expected_a_b", thing, "Number(...)", "+"); + } + const right = thing.expression; + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } +}); + +function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + if (id !== "ignore") { + const name = the_function.context[id]; + if (name.parent === the_function) { + if ( + name.used === 0 + && ( + name.role !== "function" + || name.parent.arity !== "unary" + ) + ) { + +// cause: "/*jslint node*/\nlet aa;" +// cause: "function aa(aa){return;}" + + warn("unused_a", name); + } else if (!name.init) { + +// cause: "/*jslint node*/\nlet aa;aa();" + + warn("uninitialized_a", name); + } + } + } + }); +} + +function uninitialized_and_unused() { + +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (module_mode === true || option.node) { + delve(global); + } + functions.forEach(delve); +} + +// Go through the token list, looking at usage of whitespace. + +function whitage() { + let closer = "(end)"; + +// free = false + +// cause: "()=>0" +// cause: "aa()" +// cause: "aa(0,0)" +// cause: "function(){}" + + let free = false; + +// cause: "(0)" +// cause: "(aa)" +// cause: "aa(0)" +// cause: "do{}while()" +// cause: "for(){}" +// cause: "if(){}" +// cause: "switch(){}" +// cause: "while(){}" + +// let free = true; + + let left = global; + let margin = 0; + let nr_comments_skipped = 0; + let open = true; + let opening = true; + let right; + + function pop() { + const previous = stack.pop(); + closer = previous.closer; + free = previous.free; + margin = previous.margin; + open = previous.open; + opening = previous.opening; + } + + function push() { + stack.push({ + closer, + free, + margin, + open, + opening + }); + } + + function expected_at(at) { + warn( + "expected_a_at_b_c", + right, + artifact(right), + fudge + at, + +// Return the fudged column number of an artifact. + + right.from + fudge + ); + } + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + +// cause: +// ( +// function aa ( +// bb +// , +// [ +// cc,dd +// ] +// , +// { +// ee,ff=( 0 ) +// } +// , +// ... zz +// ) +// { +// return { +// aa,bb +// } +// ; +// } +// ( +// ) +// ) +// ; + + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function no_space() { + if (left.line === right.line) { + if (left.thru !== right.from && nr_comments_skipped === 0) { + +// cause: "let aa = aa()( );" + + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (open) { + const at = ( + free + ? margin + : margin + 8 // deadcode? + ); + if (right.from < at) { + +// cause: +// let aa = aa( +// aa +// () +// ); + + expected_at(at); + } + } else { // deadcode? + if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line || !open) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (right.from !== margin) { + expected_at(margin); + } + } + } + + stack = []; + tokens.forEach(function (the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + + const new_closer = opener[left.id]; + if (typeof new_closer === "string") { + +// cause: "${" +// cause: "(" +// cause: "[" +// cause: "{" + + if (new_closer !== right.id) { + +// cause: "${0" +// cause: "(0" +// cause: "[0" +// cause: "{0" + + opening = left.open || (left.line !== right.line); + push(); + closer = new_closer; + if (opening) { + +// cause: "${\n0\n}" +// cause: "(\n0\n)" +// cause: "[\n0\n]" +// cause: "{\n0\n}" + + free = closer === ")" && left.free; + open = true; + margin += 4; + if (right.role === "label") { + if (right.from !== 0) { + +// cause: +// function aa() { +// bb: +// while (aa) { +// if (aa) { +// break bb; +// } +// } +// } + + expected_at(0); + } + } else if (right.switch) { + at_margin(-4); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + +// cause: +// function aa() {bb: +// while (aa) {aa(); +// } +// } + + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + +// cause: "${0}" +// cause: "(0)" +// cause: "[0]" +// cause: "{0}" + + free = false; + open = false; + +// cause: "let aa = ( 0 );" + + no_space_only(); + } + } else { + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like '{]') have already been detected. + +// cause: "${}" +// cause: "()" +// cause: "[]" +// cause: "{}" + + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } + } else { + if (right.statement === true) { + if (left.id === "else") { + one_space_only(); + } else { + at_margin(0); + open = false; + } + +// If right is a closer, then pop the previous state. + + } else if (right.id === closer) { + pop(); + if (opening && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-4); + } else if (right.role === "label") { + if (right.from !== 0) { + +// cause: +// function aa() { +// let bb = 0;cc: +// while (aa) { +// if (aa) { +// break cc; +// } +// } +// } + + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + one_space(); + } else { + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + at_margin(0); + } else { + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + no_space(); + } else if ( + left.id === "." + || left.id === "?." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + ) { + no_space_only(); + } else if (right.id === "." || right.id === "?.") { + no_space_only(); + } else if (left.id === ";") { // deadcode? + if (open) { + at_margin(0); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || left.id === "await" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + +// cause: +// function aa() { +// do { +// aa(); +// } while(aa()); +// } + + one_space_only(); + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + +// cause: "let aa=0;" + + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +// The jslint function itself. + +function jslint( + source = "", + option_object = empty(), + global_array = [] +) { + try { + warnings = []; + option = Object.assign(empty(), option_object); + anon = "anonymous"; + block_stack = []; + declared_globals = empty(); + directive_mode = true; + directives = []; + early_stop = true; + exports = empty(); + froms = []; + fudge = ( + option.fudge + ? 1 + : 0 + ); + functions = []; + global = { + body: true, + context: empty(), + finally: 0, + from: 0, + id: "(global)", + level: 0, + line: 0, + live: [], + loop: 0, + switch: 0, + thru: 0, + try: 0 + }; + blockage = global; + functionage = global; + json_mode = false; + mega_mode = false; + module_mode = false; + next_token = global; + property = empty(); + shebang = false; + stack = []; + tenure = undefined; + token = global; + token_nr = 0; + var_mode = undefined; + populate(standard, declared_globals, false); + populate(global_array, declared_globals, false); + Object.keys(option).forEach(function (name) { + if (option[name] === true) { + const allowed = allowed_option[name]; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } + }); + tokenize(source); + advance(); + if (json_mode) { + tree = json_value(); + advance("(end)"); + } else { + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option.browser) { + if (next_token.id === ";") { + advance(";"); + } + } else { + +// If we are not in a browser, then the file form of strict pragma may be used. + + if ( + next_token.value === "use strict" + ) { + advance("(string)"); + advance(";"); + } + } + tree = statements(); + advance("(end)"); + functionage = global; + walk_statement(tree); + if (warnings.length === 0) { + uninitialized_and_unused(); + if (!option.white) { + whitage(); + } + } + } + if (!option.browser) { + directives.forEach(function (comment) { + if (comment.directive === "global") { + +// cause: "/*global aa*/" + + warn("missing_browser", comment); + } + }); + } + if (option.test_internal_error) { + assert_or_throw(undefined, "test_internal_error"); + } + early_stop = false; + } catch (e) { + e.early_stop = true; + e.message = "[JSLint was unable to finish]\n" + e.message; + if (e.name !== "JSLintError") { + e.column = 0; + e.line = 0; + e.stack_trace = e.stack; + warnings.push(e); + } + } + +// sort warnings by early_stop first, line, column respectively + + warnings.sort(function (a, b) { + return ( + Boolean(b.early_stop) - Boolean(a.early_stop) + || a.line - b.line || a.column - b.column + ); + +// update each warning with a formatted_message ready for use by cli + + }).map(function ({ + column = 0, + line = 0, + message = "", + stack_trace = "" + }, ii, list) { + column += 1; + line += 1; + list[ii].formatted_message = String( + String(ii + 1).padStart(3, " ") + + " \u001b[31m" + message + "\u001b[39m" + + " \u001b[90m\/\/ line " + line + ", column " + column + + "\u001b[39m\n" + + (" " + String(lines && lines[line - 1]).trim()).slice(0, 72) + + "\n" + stack_trace + ).trim(); + }); + return { + directives, + edition, + exports, + froms, + functions, + global, + id: "(JSLint)", + json: json_mode, + lines, + module: module_mode === true, + ok: warnings.length === 0 && !early_stop, + option, + property, + shebang: ( + shebang + ? lines[0] + : undefined + ), + stop: early_stop, + tokens, + tree, + warnings + }; +} + +async function cli({ + console_error, + file, + option, + source +}) { +/* + * this function will run jslint from nodejs-cli + */ + const fs = await import("fs"); + let exitCode; + function string_line_count(code) { + /* + * this function will count number of newlines in + */ + let cnt; + let ii; + // https://jsperf.com/regexp-counting-2/8 + cnt = 0; + ii = 0; + while (true) { + ii = code.indexOf("\n", ii) + 1; + if (ii === 0) { + break; + } + cnt += 1; + } + return cnt; + } + function jslint_from_file({ + code, + file, + line_offset = 0, + option = {}, + warnings = [] + }) { + switch (( + /\.\w+?$|$/m + ).exec(file)[0]) { + case ".html": + // recurse + code.replace(( + /^\n" + }); +}()); + +(function testCaseJslintMisc() { +/* + * this function will test jslint's misc handling-behavior + */ + // test assertOrThrow's throw handling-behavior + try { + assertOrThrow(undefined, new Error()); + } catch (ignore) {} +}()); + +(function testCaseJslintOption() { +/* + * this function will test jslint's option handling-behavior + */ + assertOrThrow(jslint([""], { + bitwise: true, + browser: true, + convert: true, + couch: true, + debug: true, + devel: true, + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + node: true, + single: true, + this: true, + white: true + }).warnings.length === 0); + assertOrThrow(jslint("", { + test_internal_error: true + }).warnings.length === 1); +}()); + +(function testCaseJslintCodeValidate() { +/* + * this function will validate each code is valid in jslint + */ + Object.values({ + Array: [ + "new Array(1);" + ], + Date: [ + "Date.getTime();", + "let aa = aa().getTime();", + "let aa = aa.aa().getTime();" + ], + Number: [ + "let aa = 0.0e0;", + "let aa = 0b0;", + "let aa = 0o0;", + "let aa = 0x0;" + ], + RegExp: [ + "function aa() {\n return /./;\n}", + "let aa = /(?!.)(?:.)(?=.)/;" + ], + async_await: [ + "async function aa() {\n await aa();\n}" + ], + directives: [ + "#!\n/*jslint browser:false, node*/\n\"use strict\";", + "/*jslint browser*/\n;", + "/*jslint devel*/\ndebugger;", + "/*jslint eval*/\nnew Function();\neval();", + "/*jslint getset*/\nlet aa = {get aa() {\n return;\n}};", + "/*jslint getset*/\nlet aa = {set aa(aa) {\n return aa;\n}};", + "/*jslint this*/\nlet aa = this;", + "/*jslint white*/\n\t", + "/*property aa bb*/" + ], + fart: [ + "function aa() {\n return () => 1;\n}" + ], + json: [ + "{\"aa\":[[],-1,null]}" + ], + label: [ + "function aa() {\nbb:\n while (true) {\n if (true) {\n" + + " break bb;\n }\n }\n}" + ], + loop: [ + "function aa() {\n do {\n aa();\n } while (aa());\n}" + ], + misc: [ + "" + ], + module: [ + "export default Object.freeze();", + "import {aa, bb} from \"aa\";\naa(bb);", + "import {} from \"aa\";", + "import(\"aa\").then(function () {\n return;\n});" + ], + optional_chaining: [ + "let aa = aa?.bb?.cc;" + ], + property: [ + "let aa = aa[`!`];" + ], + ternary: [ + "let aa = (\n aa()\n ? 0\n : 1\n) " + + "&& (\n aa()\n ? 0\n : 1\n);" + ], + var: [ + "let [...aa] = [...aa];", + "let [\n aa, bb = 1\n] = 0;", + "let {aa, bb} = 0;", + "let {\n aa: bb\n} = 0;" + ] + }).forEach(function (codeList) { + codeList.forEach(function (code) { + let warnings; + warnings = jslint(code).warnings; + assertOrThrow(warnings.length === 0, [code, warnings]); + }); + }); +}()); + +(async function testCaseJslintWarningsValidate() { +/* + * this function will validate each jslint is raised with given + * malformed + */ + Object.entries({ + expected_a_at_b_c: [ + "(function(){let aa;bb:while(aa()){aa();}}());", + "function aa(){\n bb:while(aa){aa();}}", + "let aa=aa(\naa\n()\n);", + "let aa={\n aa:\n0\n};" + ], + expected_a_b: [ + "([])=>0", + "(aa)=>{}", + "(aa?0:aa)", + "(aa?aa:0)", + "(aa?false:true)", + "(aa?true:false)", + ";{", + "`${/ /}`", + "`${`", + "`${{`", + "aa.aa=undefined", + "aa=+aa", + "aa=/[ ]/", + "aa=/aa{/", + "aa=0+\"\"", + "aa=\"\"+\"\"", + "async", + "delete [0]", + "for(;;){}", + "isFinite(0)", + "let aa;var aa;", + "new Array(\"\")", + "new Date().getTime()", + "new Object()" + ], + expected_a_before_b: [ + ".0", + "/*jslint eval*/\nFunction;eval", + "=>0", + "\"\\u{12345\"", + "aa=/(:)/", + "aa=/=/", + "aa=/?/", + "aa=/[/", + "let Aa=Aa()", + "let Aa=Aa.Aa()", + "new Aa" + ], + expected_identifier_a: [ + "(0)=>0", + "aa.0", + "aa?.0", + "function aa(0){}", + "function aa([aa]){}\nfunction aa([aa],[aa,aa=aa],[0]){}", + "function aa({aa}){}\nfunction aa({aa},{aa:aa,aa=aa},{aa:0}){}", + "function(){}", + "import {", + "let aa={0:0}", + "let {0}=0", + "let {aa:0}=0" + ], + expected_space_a_b: [ + "(function(){return;}());", + "/**//**/", + "let aa=(aa?0:1);", + "let aa=0;" + ], + required_a_optional_b: [ + "function aa(aa=0,...){}", + "function aa(aa=0,[]){}", + "function aa(aa=0,{}){}", + "function aa(aa=0,bb){}" + ], + too_long: [ + "//".repeat(100) + ], + unexpected_a: [ + "((0))", + "(+0?+0:+0)()", + "(/./)?.foo", + "/*/", + "/./", + "0===(0==0)", + "0[0][0]", + "0|0", + ";", + "Function", + "[-0x0]", + "[0x0]", + "\"aa\"?.bb", + "`${/[`]/}`", + "`${/`/}`", + "`${\"`\"}`", + "aa((0))", + "aa+=NaN", + "aa/=0", + "aa=/[0-]/", + "aa=/.//", + "aa=/./z", + "aa={aa:aa}", + "aa={set aa(){}}", + "arguments", + "await", + "debugger", + "eval", + "export aa", + "export const aa=0", + "for(aa in aa){}", + "for(const ii=0;;){}", + "for(ii=0;ii<0;ii++){}", + "for(ii=0;ii<0;ii+=0){}", + "function aa(){for(0;0;0){break;}}", + "function aa(){try{return;}catch(ignore){}finally{return;}}", + "function aa(){try{}catch(ignore){}finally{switch(0){case 0:}}}", + "function aa(){while(0){continue;}}", + "function aa(){while(0){try{0;}catch(ignore){}finally{continue;}}}", + "function aa(){}0", + "function aa(){}\n[]", + "function ignore(){let ignore;}", + "ignore", + "ignore:", + "import ignore from \"aa\"", + "import {ignore} from \"aa\"", + "let aa=[]?.bb", + "new Date.UTC()", + "new Function()", + "new Symbol()", + "switch(0){case 0:break;case 0:break}", + "switch(0){case 0:break;default:break;}", + "switch(0){case 0:break;default:}", + "this", + "try{throw 0;try{}catch(){}}catch(){}", + "try{}finally{break;}", + "void 0", + "while((0)){}", + "while(0){}", + "{//\n}", + "{0:0}", + "{\"\\u{1234}\":0}", + "{\"aa\":", + "{\"aa\":'aa'}" + ] + }).forEach(function ([ + expectedWarning, malformedCodeList + ]) { + malformedCodeList.forEach(function (malformedCode) { + assertOrThrow( + jslint(malformedCode).warnings.some(function ({ + code + }) { + return code === expectedWarning; + }), + new Error( + `jslint failed to warn "${expectedWarning}" with ` + + `malfomed code "${malformedCode}"` + ) + ); + }); + }); + Array.from(String( + await fs.promises.readFile("jslint.js", "utf8") + ).matchAll(new RegExp(( + "\\s*?" + + "(\\/\\/\\s*?cause:.*?\\n(?:\\/\\/.*?\\n)*?)" + + "(\\s*?\\n[^\\/].*?(?:\\n\\s*?\".*?)?$)" + ), "gm"))).forEach(function ([ + match0, causeList, warning + ]) { + let expectedWarningCode; + let fnc; + // debug match0 + console.error(match0.trim().replace((/\n\n/g), "\n")); + assertOrThrow( + match0.indexOf("\n\n" + causeList + "\n ") === 0, + JSON.stringify([ + match0, causeList + ], undefined, 4) + ); + warning = warning.match( + "(" + + "expected_at" + + "|no_space_only" + + "|one_space_only" + + "|one_space" + + "|stop" + + "|stop_at" + + "|warn" + + "|warn_at" + + ")" + + "\\\u0028\\s*?\"?" + + "(\\S[^\n\"]+)" + ); + if (warning) { + expectedWarningCode = warning[2]; + fnc = warning[1]; + switch (fnc) { + case "expected_at": + expectedWarningCode = "expected_a_at_b_c"; + break; + case "no_space_only": + expectedWarningCode = "unexpected_space_a_b"; + break; + case "one_space": + case "one_space_only": + expectedWarningCode = "expected_space_a_b"; + break; + } + } + causeList.split( + /\/\/\u0020cause:[\n|\u0020]/ + ).slice(1).forEach(function (cause) { + assertOrThrow(cause === cause.trim() + "\n", JSON.stringify(cause)); + cause = ( + cause[0] === "\"" + ? JSON.parse(cause) + : cause.replace(( + /^\/\/\u0020/gm + ), "") + ); + assertOrThrow( + jslint(cause).warnings.some(function ({ + code + }) { + return code === expectedWarningCode; + }) || !expectedWarningCode, + "\n" + cause.trim() + ); + }); + }); +}()); diff --git a/branch.biginit/README b/branch.biginit/README new file mode 100644 index 000000000..cd6b06c8b --- /dev/null +++ b/branch.biginit/README @@ -0,0 +1,39 @@ +JSLint, The JavaScript Code Quality Tool + +Douglas Crockford +douglas@crockford.com + +2019-03-15 + +jslint.js contains the jslint function. It parses and analyzes a source file, +returning an object with information about the file. It can also take an object +that sets options. + +index.html runs the jslint.js function in a web page. The page also depends +browser.js and report.js and jslint.css. + +jslint.css provides styling for index.html. + +browser.js runs the web user interface. + +report.js generates the results reports in HTML. + +help.html describes JSLint's usage. Please read it. + +function.html describes the jslint function and the results it produces. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[http://www.crockford.com/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. diff --git a/branch.biginit/_config.yml b/branch.biginit/_config.yml new file mode 100644 index 000000000..c74188174 --- /dev/null +++ b/branch.biginit/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-slate \ No newline at end of file diff --git a/branch.biginit/browser.js b/branch.biginit/browser.js new file mode 100644 index 000000000..5086fed85 --- /dev/null +++ b/branch.biginit/browser.js @@ -0,0 +1,158 @@ +// browser.js +// 2018-06-16 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +/*jslint + browser +*/ + +/*property + checked, create, disable, display, error, focus, forEach, function, + getElementById, innerHTML, join, length, map, onchange, onclick, onscroll, + property, querySelectorAll, scrollTop, select, split, style, title, value +*/ + +import jslint from "./jslint.js"; +import report from "./report.js"; + +// This is the web script companion file for JSLint. It includes code for +// interacting with the browser and displaying the reports. + +let rx_crlf = /\n|\r\n?/; +let rx_separator = /[\s,;'"]+/; + +let fudge_unit = 0; + +const aux = document.getElementById("JSLINT_AUX"); +const boxes = document.querySelectorAll("[type=checkbox]"); +const fudge = document.getElementById("JSLINT_FUDGE"); +const global = document.getElementById("JSLINT_GLOBAL"); +const number = document.getElementById("JSLINT_NUMBER"); +const property = document.getElementById("JSLINT_PROPERTY"); +const property_fieldset = document.getElementById("JSLINT_PROPERTYFIELDSET"); +const report_field = document.getElementById("JSLINT_REPORT"); +const report_list = document.getElementById("JSLINT_REPORT_LIST"); +const source = document.getElementById("JSLINT_SOURCE"); +const select = document.getElementById("JSLINT_SELECT"); +const warnings = document.getElementById("JSLINT_WARNINGS"); +const warnings_list = document.getElementById("JSLINT_WARNINGS_LIST"); + +function show_numbers() { + number.value = source.value.split(rx_crlf).map(function (ignore, index) { + return index + fudge_unit; + }).join("\n"); +} + +function clear() { + aux.style.display = "none"; + number.value = ""; + property.value = ""; + property_fieldset.style.display = "none"; + report_field.style.display = "none"; + report_list.innerHTML = ""; + source.focus(); + source.value = ""; + warnings.style.display = "none"; + warnings_list.innerHTML = ""; +} + +function fudge_change() { + fudge_unit = Number(fudge.checked); + show_numbers(); +} + +function clear_options() { + boxes.forEach(function (node) { + node.checked = false; + }); + fudge_change(); + global.innerHTML = ""; +} + +function call_jslint() { + +// First build the option object. + + let option = Object.create(null); + boxes.forEach(function (node) { + if (node.checked) { + option[node.title] = true; + } + }); + +// Call JSLint with the source text, the options, and the predefined globals. + + let global_string = global.value; + let result = jslint( + source.value, + option, + ( + global_string === "" + ? undefined + : global_string.split(rx_separator) + ) + ); + +// Generate the reports. + + let error_html = report.error(result); + let function_html = report.function(result); + let property_text = report.property(result); + +// Display the reports. + + warnings_list.innerHTML = error_html; + warnings.style.display = ( + error_html.length === 0 + ? "none" + : "block" + ); + + report_list.innerHTML = function_html; + report_field.style.display = "block"; + if (property_text) { + property.value = property_text; + property_fieldset.style.display = "block"; + property.scrollTop = 0; + select.disable = false; + } else { + property_fieldset.style.display = "none"; + select.disable = true; + } + aux.style.display = "block"; + source.select(); +} + +fudge.onchange = fudge_change; + +source.onchange = function (ignore) { + show_numbers(); +}; + +source.onscroll = function () { + let ss = source.scrollTop; + number.scrollTop = ss; + let sn = number.scrollTop; + if (ss > sn) { + show_numbers(); + number.scrollTop = ss; + } +}; + +document.querySelectorAll("[name='JSLint']").forEach(function (node) { + node.onclick = call_jslint; +}); + +document.querySelectorAll("[name='clear']").forEach(function (node) { + node.onclick = clear; +}); + +document.getElementById("JSLINT_SELECT").onclick = function () { + property.focus(); + property.select(); +}; +document.getElementById("JSLINT_CLEAR_OPTIONS").onclick = clear_options; + +fudge_change(); +source.select(); +source.focus(); diff --git a/branch.biginit/function.html b/branch.biginit/function.html new file mode 100644 index 000000000..7097c62e9 --- /dev/null +++ b/branch.biginit/function.html @@ -0,0 +1,615 @@ + + + + + + + + +JSLint: jslint function + + + +
    JSLint
    + +
    The jslint Function
    +
    +

    JSLint

    +

    JSLint is delivered as a set of files:

    +
    + + + + + + + + + + + + + + +
    FilenameContent
    browser.jsBrowser based UI.
    function.htmlThis page that you are looking at right now.
    help.htmlUsing jslint as a single page JSLint application.
    index.htmlSingle page JSLint application that uses the + js files.
    jslint.jsThe jslint function.
    report.jsThe report object, containing report generator functions for + HTML.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    +
    + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring"(JSLint)"
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    parenttokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    parenttokenThe function in which the variable was declared.variable
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    shebangstringThe first line of text if it started with #!line
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable may be assigned to.variable
    +

    Reports

    +

    The report object contains three functions that all take a + result from the jslint function as input. + report.js has no other dependence on other files.

    + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    +
    + +
    + + + +
    + + diff --git a/branch.biginit/help.html b/branch.biginit/help.html new file mode 100644 index 000000000..364dfe255 --- /dev/null +++ b/branch.biginit/help.html @@ -0,0 +1,826 @@ + + + + + + + + + +JSLint: Help + + + +
    JSLint
    + +
    Help
    +
    +
    Il semble que la perfection soit atteinte non quand il n’y a + plus rien à ajouter, mais quand il n’y a plus rien à + retrancher.
    +
    + Antoine de Saint-Exupéry +
    +
    + Terre des Hommes (1939) +
    +

    Good Parts

    +

    The Principle of the Good Parts is

    +
    If a feature is sometimes useful and sometimes dangerous and if + there is a better option then always use the better option.
    +

    JSLint is a JavaScript program that looks for problems in + JavaScript programs. It is a code quality tool.

    +

    When C was + a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    +

    As the language matured, the definition of the language was + strengthened to eliminate some insecurities, and compilers got better + at issuing warnings. lint is no longer needed.

    +

    JavaScript is a + young-for-its-age language. It was originally intended to do small tasks in + webpages, tasks for which Java was too heavy and clumsy. But JavaScript is + a surprisingly capable language, and it is now being used in larger + projects. Many of the features that were intended to make the language easy + to use are troublesome when projects become complicated. A lint + for JavaScript is needed: JSLint, a JavaScript syntax checker + and validator.

    +

    JSLint takes a JavaScript source and scans it. If it finds a + problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by the ECMAScript Programming Language + Standard (the strangely named document that governs JavaScript). + JSLint will reject most legal programs. It is a higher + standard.

    +

    JavaScript is a sloppy language, but hidden deep inside there is an elegant, + better language. JSLint helps you to program in that better + language and to avoid most of the slop. JSLint will reject + programs that browsers will accept because JSLint is concerned + with the quality of your code and browsers are not. You should gladly accept + all of JSLint's advice.

    +

    JSLint can operate on JavaScript source + or JSON text.

    +

    ECMAScript Sixth Edition

    +

    Some of + ES6’s features are good, so JSLint will recognize the good + parts of ES6.

    +

    Currently, these features are recognized:

    +
      +
    • The ... ellipsis marker in parameter + lists and argument lists, replacing the arguments object + for variadic functions.
    • +
    • The let statement, which is like the var + statement except that it respects block scope. + You may use let or var but not both.
    • +
    • The const statement is like the let statement + except that it disallows the use of assignment on the variable, although + if the value of the variable is mutable, it can still be mutated. + const is preferred to let. +
    • Destructuring of arrays and objects is allowed in parameter lists and on + the left side of let, and const, but not + var or assignment statements, and not deep destructuring + or eliding.
    • +
    • Enhanced object literals, providing shorter forms for function + declaration and properties that are initialized by variables with the + same name.
    • +
    • The fat arrow => fart functions.
    • +
    • The simplest forms of import and export.
    • +
    • `Megastring` literals, but not nested + `megastring` literals.
    • +
    • New global functions, such as Map, Set, + WeakMap, and WeakSet.
    • +
    • 0b- and 0o- number literals.
    • +
    +

    The most important new feature of ES6 is proper tail calls. This has no + new syntax, so JSLint doesn’t see it. But it makes recursion + much more attractive, which makes loops, particularly for + loops, much less attractive.

    +

    import export

    +

    The ES6 module feature will be an important improvement over JavaScript’s + global variables as a means of linking separate files together. + JSLint recognizes a small but essential subset of the module + syntax.

    +
    +

    import name from stringliteral;

    +

    import {name} from stringliteral;

    +

    export default function () {};

    +

    export default function name() {};

    +

    export default expression;

    +

    export function name() {}

    +

    export name; // where name is const

    +

    export {name};

    +
    +

    Directives

    +

    JSLint provides three directives that may be placed in a + file to manage JSLint’s behavior. Use of these directives is + optional. If they are used, they should be placed in a source file before + the first statement. They are written in the form of a comment, where the + directive name is placed immediately after the opening of the comment before + any whitespace. The three directives are global, + jslint, and property. Directives in a file are + stronger than options selected from the UI or passed with the option object.

    +

    /*global*/

    +

    The /*global*/ directive is used to specify a set of globals + (usually functions and objects containing functions) that are available to + this file. This was commonly used in browsers to link source files together + before ES6 modules appeared. Use of global variables is strongly + discouraged, but unfortunately web browsers require their use. The + /*global*/ directive can only be used when the + Assume a browser option is selected. +

    Each of the names listed will indicate a read-only global variable. The names + are separated by , comma. This directive + should not be used if import or export is used. + This directive inhibits warnings, but it does not declare the names in the + execution environment. +

    For example: +

    /*global +
    ADSAFE, report, jslint
    +*/
    +

    instructs JSLint to not give warnings about the global + variables ADsafe, report, and jslint. + However, if any of those names are expected to be supplied by other files + and those other files fail to do so, then execution errors will result. It + is usually better to use top-level variable declarations instead: +

        var ADSAFE;
    +    var report;
    +    var jslint; 
    +

    Using var in this way allows comparing a global variable to the + undefined value to determine whether it is has been used in the global context.

    +

    /*jslint*/

    +

    The /*jslint*/ directive allows for the control of several + options. These options can also be set from the + JSLint.com user interface.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Tolerate bitwise operatorsbitwisetrue if bitwise operators should be allowed. The + bitwise operators are rarely used in JavaScript programs, so it + is usually more likely that & is a mistyping of + && than that it indicates a bitwise and. + JSLint will give warnings on the bitwise operators + unless this option is selected.
    Assume a browser browsertrue if the standard browser globals should be + predefined. This option will reject the use of + import and export. This option also + disallows the file form of the "use + strict" pragma. It does not supply + self; you will have to request that unnecessary + alias of the dreaded global object yourself. It adds the same + globals as this directive: +
    /*global +
    + clearInterval, clearTimeout, document, event, FileReader, + FormData, history, localStorage, location, name, navigator, + screen, sessionStorage, setInterval, setTimeout, Storage, + URL, window, XMLHttpRequest +
    + */
    +
    Tolerate conversion operatorsconverttrue if !!, + prefix, + and concatenation with "" are allowed. + The Boolean, Number, and + String functions are preferred
    Assume CouchDBcouchtrue if + Couch DB + globals should be predefined. It adds the same globals as this + directive: +
    /*global +
    emit, getRow, isArray, log, provides, registerType, require, + send, start, sum, toJSON
    + */
    Assume in developmentdeveltrue if browser globals that are useful in + development should be predefined, and if debugger + statements and TODO comments + should be allowed. It adds the + same globals as this directive: +
    /*global +
    alert, confirm, console, prompt
    + */
    Be sure to turn this option off before going + into production.
    Tolerate evalevaltrue if eval should be allowed. In the + past, the eval function was the most misused + feature of the language.
    Tolerate for statementfortrue if the for statement should be + allowed. It is almost always better to use the array methods + instead.
    Fudge the line and column numbersfudgetrue if the origin of a text file is line 1 column + 1. Programmers know that number systems start at zero. + Unfortunately, most text editors start numbering lines and + columns at one. JSLint will by default start + numbering at zero. Use this option to start the numbering at one + in its reports.
    Tolerate get and setgetsettrue if accessor properties are allowed in object literals.
    Tolerate long source lineslongtrue if a line can contain more than 80 characters.
    Assume Node.jsnodetrue if Node.js globals should be predefined. It + will predefine globals that are used in the Node.js environment. + It adds the same globals as this directive: +
    /*global +
    + Buffer, clearImmediate, clearInterval, clearTimeout, console, exports, + module, process, require, setImmediate, setInterval, setTimeout, URL, + URLSearchParams, __dirname, __filename +
    + */
    Tolerate single quote stringssingletrue if 'single quote + should be allowed to enclose string literals.
    Tolerate this
    thistrue if this should be allowed.
    Tolerate whitespace messwhitetrue if the whitespace rules should be ignored.
    +

    For example:

    +
    /*jslint
    +    bitwise, node
    +*/
    + +

    /*property*/

    +

    The /*property*/ directive is used to declare a list of + property identifiers that are used in the file. Each property name in the + program is looked up in this list. If a name is not found, that indicates an + error, most likely a typing error.

    +

    The list can also be used to evade some of JSLint’s naming + rules.

    +

    JSLint can build the /*property*/ list for you. At + the bottom of its report, JSLint displays a + /*property*/ directive. It contains all of the names that were + used with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. You + can copy the /*property*/ directive to the top of your + script file. JSLint will check the spelling of all property + names against the list. That way, you can have JSLint look for + misspellings for you.

    +

    For example,

    +
    /*property +
    charAt, slice, _$_
    + */
    +

    ignore

    +

    JSLint introduces a new reserved word: ignore. It + is used in parameter lists and in catch clauses to indicate a + parameter that will be ignored. Unused warnings will not be produced for + ignore. +

    function handler(ignore, value) {
    +    return do_something_useful(value);
    +}
    +

    var let const

    +

    JavaScript provides three statements for declaring variables: + var, let, and const. The + var statement suffers from a bad practice called hoisting. The + let statement does not do hoisting and respects block scope. + The const statement is like let except that it + marks the variable (but not its contents) as read only, making it an error + to attempt to assign to the variable. When given a choice, + const is the best, var is the worst.

    +

    JSLint uses the intersection of the var rules and the + let rules, and by doing so avoids the errors related to either. + A name should be declared only once in a function. It should be declared + before it is used. It should not be used outside of the block in which it is + declared. A variable should not have the same name as a variable or + parameter in an outer function. Do not mix var and + let. Declare one name per statement.

    +

    = == ===

    +

    Fortran made a terrible + mistake in using the equality operator as its assignment operator. That + mistake has been replicated in most languages since then. C compounded that + mistake by making == its equality operator. The visual + similarity is a source of errors. JavaScript compounded this further by + making == a type coercing comparison operator that produces + false positive results. This was mitigated by adding the === + operator, leaving the broken == operator in place.

    +

    JSLint attempts to minimize errors by the following rules:

    +

    == is not allowed. This avoids the false positives, and + increases the visual distance between = and ===. + Assignments are not allowed in expression position, and comparisons are not + allowed in statement position. This also reduces confusion. 

    +

    Semicolon

    +

    JavaScript uses a C-like syntax that requires the use of semicolons to + delimit certain statements. JavaScript attempts to make those semicolons + optional with an automatic semicolon insertion mechanism, but it does not + work very well. Automatic semicolon insertion was added to make + things easier for beginners. Unfortunately, it sometimes fails. Do not rely + on it unless you are a beginner.

    +

    JSLint expects that every statement will be followed by + ; except for for, function, + if, switch, try, and + while. JSLint does not expect to see unnecessary + semicolons, the empty statement, or empty blocks.

    +

    function =>

    +

    JavaScript has four syntactic forms for making function objects: function + statements, function expressions, enhanced object literals, and the + => fart operator.

    +

    The function statement creates a variable and assigns the function object to + it. It should be used in a file or function body, but not inside of a block.

    +
    function name(parameters) { +
    statements
    + }
    +

    The function expression unfortunately looks like the function statement. It + may appear anywhere that an expression may appear, but not in statement + position and not in a loop. It produces a function object but does not + create a variable in which to store it.

    +
    function (parameters) { +
    statements
    + }
    +

    The enhanced object literal provides an ES6 shorthand for creating a property + whose value is a function, saving you from having to type + : colon and function.

    +
    { +
    name(parameters) { +
    statements
    + }
    + }
    +

    Finally, ES6 provides an even shorter form of function expression that leaves + out the words function and return:

    +
    (parameters) => + expression
    +

    JSLint requires the parens around the parameters, and forbids a + { left brace after the + => fart to avoid syntactic ambiguity.

    +

    Comma

    +

    The , comma operator is unnecessary and can + mask programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as + an operator. It does not expect to see elided elements in array literals. A + comma should not appear after the last element of an array literal or object + literal.

    +

    Blocks

    +

    JSLint expects blocks with function, + if, switch, while, for, + do, and try statements and nowhere else.

    +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    +

    JavaScript allows an if to be written like this:

    +
    if (condition)
    +    statement;
    +

    That form is known to contribute to mistakes in projects where many + programmers are working on the same code. That is why JSLint + expects the use of a block:

    +
    if (condition) {
    +    statements;
    +}
    +

    Experience shows that this form is more resilient.

    +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call. All other expression statements are considered + to be errors.

    +

    for

    +

    JSLint does not recommend use of the for statement. + Use array methods like forEach instead. The for + option will suppress some warnings. The forms of for that + JSLint accepts are restricted, excluding the new ES6 forms.

    +

    for in

    +

    JSLint does not recommend use of the for + in statement. Use Object.keys instead.

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, it also + loops through all of the properties that were inherited through the + prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written + without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    for (name in object) {
    +    if (object.hasOwnProperty(name)) {
    +        ....
    +    }
    +}
    +

    Note that the above code will fail if the object contains a property + named hasOwnProperty. Use Object.keys instead.

    +

    switch

    +

    A common error in switch statements is to forget to place a + break statement after each case, resulting in unintended + fall-through. JSLint expects that the statement before the next + case or default is one of these: + break, return, or throw.

    +

    with

    +

    The with statement was intended to provide a shorthand in + accessing properties in deeply nested objects. Unfortunately, it behaves + very badly when setting new properties. Never use the with + statement.

    +

    JSLint does not expect to see a with statement.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that + labels will be distinct from vars and parameters.

    +

    Unreachable Code

    +

    JSLint expects that a return, break, + or throw statement will be followed by a + } right brace or case or + default.

    +

    Confusing Pluses and Minuses

    +

    JSLint expects that + will not be followed by + + or ++, and that - will not be + followed by - or --. A misplaced space can turn + + + into ++, an error that is difficult to see. + Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ increment and + -- decrement operators have been known to + contribute to bad code by encouraging excessive trickiness. They are second + only to faulty architecture in enabling to viruses and other security + menaces. Also, preincrement/postincrement confusion can produce off-by-one + errors that are extremely difficult to diagnose. Fortunately, they are also + complete unnecessary. There are better ways to add 1 to a variable.

    +

    It is best to avoid these operators entirely and rely on += and + -= instead.

    +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. + JSLint looks for problems that may cause portability problems. + It also attempts to resolve visual ambiguities by recommending explicit + escapement.

    +

    JavaScript’s syntax for regular expression literals overloads the + / slash character. To avoid ambiguity, + JSLint expects that the character preceding a regular + expression literal is a ( left paren or + = equal or + : colon or + , comma character.

    +

    this

    +
    Having this in the language makes it harder to talk + about the language. It is like pair programming with Abbott and Costello. +
    +

    Avoid using this. Warnings about this can be + suppressed with option.this.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the + new prefix. The new prefix creates a new object + based on the function's prototype, and binds that object to the + function's implied this parameter. If you neglect to use the + new prefix, no new object will be made and this + will be bound to the global object.

    +

    JSLint enforces the convention that constructor functions be + given names with initial uppercase. JSLint does not expect to + see a function invocation with an initial uppercase name unless it has the + new prefix. JSLint does not expect to see the + new prefix used with functions whose names do not start with + initial uppercase.

    +

    JSLint does not expect to see the wrapper forms + new Number, new String, new Boolean.

    +

    JSLint does not expect to see new Object. + Use Object.create(null) instead.

    +

    Whitespace

    +

    JSLint has a specific set of rules about the use of whitespace. + Where possible, these rules are consistent with centuries of good practice + with literary style.

    +

    The indentation increases by 4 spaces when the last token on a line is + { left brace, + [ left bracket, + ( left paren. The matching closing + token will be the first token on a line, restoring the previous + indentation.

    +

    The ternary operator can be visually confusing, so + ? question mark and + : colon always begin a line and increase + the indentation by 4 spaces.

    +
    return (
    +    (the_token.id === "(string)" || the_token.id === "(number)")
    +    ? String(the_token.value)
    +    : the_token.id
    +);
    +

    The word function is always followed with one space.

    +

    Clauses (case, catch, default, + else, finally) are not statements and so should + not be indented like statements.

    +

    Spaces are used to make things that are not invocations look less like + invocations.

    +

    Tabs and spaces should not be mixed. We should pick just one in order to + avoid the problems that come from having both. Personal preference is an + extremely unreliable criterion. Neither offers a powerful advantage over the + other. Fifty years ago, tab had the advantage of consuming less memory, but + Moore's Law has eliminated that advantage. Space has one clear advantage + over tab: there is no reliable standard for how many spaces a tab + represents, but it is universally accepted that a space occupies a space. So + use spaces. You can edit with tabs if you must, but make sure it is spaces + again before you commit. Maybe someday we will finally get a universal + standard for tabs, but until that day comes, the better choice is spaces.

    +

    Unsafe Characters

    +

    There are characters that are handled inconsistently in some browsers, and + so must be escaped when placed in strings.

    +
    \u0000-\u001f
    +\u007f-\u009f
    +\u00ad
    +\u0600-\u0604
    +\u070f
    +\u17b4
    +\u17b5
    +\u200c-\u200f
    +\u2028-\u202f
    +\u2060-\u206f
    +\ufeff
    +\ufff0-\uffff
    +

    Report

    +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    +
      +
    • The line number on which it starts.
    • +
    • The function’s name. In the case of anonymous functions, + JSLint will attempt to + «guess» the name.
    • +
    • The parameters.
    • +
    • Variables: The variables that are declared in the function.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Exceptions: The variables that are declared by catch + clauses of try statements.
    • +
    • Outer: Variables used by this function that are declared in + other functions.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements.

    +

    Try it

    +

    Try it. Paste your script + into the window and click the + + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    JSLint is written entirely in JavaScript, so it can run + anywhere that JavaScript (or even Java) can run.

    +

    Perfectly fine

    +

    JSLint was designed to reject code that some would consider to + be perfectly fine. The reason for this is that JSLint's + purpose is to help produce programs that are free of error. That is + difficult in any language and is especially hard in JavaScript. + JSLint attempts to help you increase the visual distance + between correct programs and incorrect programs, making the remaining errors + more obvious. JSLint will give warnings about things that are + not necessarily wrong in the current situation, but which have been observed + to mask or obscure errors. Avoid those things when there are better options + available.

    +

    It is dangerous out there. JSLint is here to help.

    +

    Warning

    +

    JSLint will hurt your feelings. Side effects may include + headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, + dry mouth, cleaner code, and a reduced error rate.

    +

    Further Reading

    + +
    +

    + Code Conventions for the JavaScript Programming Language.

    + + + +
    + + diff --git a/branch.biginit/index.html b/branch.biginit/index.html new file mode 100644 index 000000000..2c79dd57f --- /dev/null +++ b/branch.biginit/index.html @@ -0,0 +1,128 @@ + + + + + + + + + + + +JSLint: The JavaScript Code Quality Tool + + +
    +
    Source
    +
    + + +
    +
    + Warnings +
    +
    +
    + Function Report +
    +
    +
    + Property Directive + +
    +
    + + + + +
    +
    Options +
    Assume... +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Global variables... + +
    +
    +
    +
    + + + + +
    + + diff --git a/branch.biginit/jslint.css b/branch.biginit/jslint.css new file mode 100644 index 000000000..cc3bf1696 --- /dev/null +++ b/branch.biginit/jslint.css @@ -0,0 +1,341 @@ +@font-face { + font-family: 'Programma'; + font-weight: bold; + src: url('Programma-Bold.woff2') format('woff2'); +} +@font-face { + font-family: 'Daley'; + font-weight: bold; + src: url('Daley-Bold.woff2') format('woff2'); +} + +body { + background-color: antiquewhite; + color: black; + font-family: 'Daley', sans-serif; + margin: 0; + padding: 0; +} + +#JSLINT_TITLEBOX { + font-family: 'Daley', sans-serif; + margin: 0; + margin-left: 3%; + margin-right: 3%; + padding: 0; +} + +#JSLINT_TITLEBOX ul { + float: right; + font-size: 12pt; +} + +#JSLINT_TITLEBOX div { + color: darkslategray; + float: left; + font-size: 48pt; +} + +#JSLINT_ fieldset { + background-color: gainsboro; + border: 0; + clear: both; + margin-bottom: 1em; + margin-left: 3%; + margin-right: 3%; + margin-top: 1em; + padding: 0; + width: auto; +} +#JSLINT_ legend { + background-color: darkslategray; + border: 0; + color: white; + font-size: 100%; + font-style: normal; + font-weight: normal; + margin: 0; + padding-bottom: 0.25em; + padding-left: 0; + padding-right: 0; + padding-top: 0.25em; + text-align: center; + width: 100%; +} +#JSLINT_ button { + background-color: darkslategray; + border: 0; + color: white; + cursor: pointer; + font-family: 'Daley', sans-serif; + font-size: 100%; + font-style: normal; + margin-left: 1em; + margin-right: 1em; + padding-left: 1em; + padding-right: 1em; + padding-bottom: 0.25em; + padding-top: 0.25em; + text-align: center; +} +#JSLINT_ button:hover { + background-color: slategray; + color: white; + border: 0; +} + +#JSLINT_ button:active { + border: 0; + color: black; +} + +#JSLINT_ button:disabled { + background-color: gray; + border: 0; + color: gainsboro; +} + +a:visited { + color: #69565c; +} + +a:link { + color: black; +} + +#JSLINT_ input[type="text"] { + background-color: white; + border: 0; + color: black; + margin-bottom: 0.2em; + margin-right: 0.5em; + padding-left: 0.5em; + padding-right: 0.5em; + text-align: right; + width: 3em; +} + +#JSLINT_ textarea { + border: 0; + background-color: white; + border: 0; + font-family: Programma, monospace; + font-size: 100%; + font-weight: bold; + resize: none; +} + +#JSLINT_ textarea::selection { + background-color: yellow; + color: black; +} + +#JSLINT_NUMBER { + color: darkgray; + display: inline-block; + height: 4in; + margin: 2%; + margin-right: 0; + overflow: hidden; + padding-right: 0.5%; + text-align: right; + white-space: pre; + width: 4%; +} + +#JSLINT_SOURCE { + color: black; + display: inline-block; + font-size: 100%; + height: 4in; + margin: 2%; + margin-left: 0; + margin-right: 0; + overflow: auto; + padding-left: 0.5%; + white-space: pre; + width: 91%; +} + +#JSLINT_PROPERTY { + background-color: honeydew; + border: 0; + margin: 2%; + padding: 0.5%; + white-space: pre; + width: 95%; +} + +#JSLINT_GLOBAL { + white-space: normal; + width: 95%; +} +#JSLINT_ label { + font-family: sans-serif; + font-size: 90%; + padding-left: 0.25em; +} +#JSLINT_OPTIONS>div { + float: left; + margin: 0.5em; +} + +#JSLINT_ address { + color: black; + display: block; + float: right; + font-family: serif; + font-size: 90%; + margin-left: 1em; +} +#JSLINT_ dl { + background-color: cornsilk; + color: white; + margin: 0; + padding-bottom: 2pt; + padding-left: 1em; + padding-right: 1em; + padding-top: 2pt; +} +#JSLINT_ dfn { + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + margin-bottom: 2pt; +} +#JSLINT_ dt { + color: black; + display: block; + float: left; + font-family: serif; + font-size: 75%; + font-style: italic; + margin: 0; + width: 8em; + text-align: right; +} +#JSLINT_ dd { + color: black; + display: block; + font-family: Programma, monospace; + font-weight: bold; + margin-left: 8em; + padding-bottom: 2pt; +} +#JSLINT_WARNINGS>legend { + background-color: indianred; +} +#JSLINT_WARNINGS>div { + background-color: pink; + padding: 1em; +} +#JSLINT_WARNINGS cite { + color: black; + display: block; + font-family: serif; + font-size: 100%; + font-style: normal; + margin-bottom: 4pt; + margin-left: 20pt; + margin-right: 20pt; + margin-top: 4pt; + overflow-x: hidden; +} +#JSLINT_WARNINGS samp { + background-color: lavenderblush; + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + padding: 4pt; + margin-bottom: 0; + margin-left: 16pt; + margin-right: 16pt; + margin-top: 0; + white-space: pre-wrap; +} + +#JSLINT_REPORT center { + margin-top: 1em; +} + +#JSLINT_WARNINGS dl address { + color: black; + display: inline; + float: none; + font-size: 80%; + margin: 0; +} + +#JSLINT_REPORT>div { + padding: 1em; +} + +#JSLINT_ dl.level0 { + color: black; + background-color: white; +} + +#JSLINT_ dl.level1 { + color: black; + background-color: #ffffe0; /* yellow */ + margin-left: 1em; +} + +#JSLINT_ dl.level2 { + color: black; + background-color: #e0ffe0; /* green */ + margin-left: 2em; +} + +#JSLINT_ dl.level3 { + color: black; + background-color: #D0D0ff; /* blue */ + margin-left: 3em; +} + +#JSLINT_ dl.level4 { + background-color: #ffe0ff; /* purple */ + color: black; + margin-left: 4em; +} + +#JSLINT_ dl.level5 { + background-color: #ffe0e0; /* red */ + color: black; + margin-left: 5em; +} + +#JSLINT_ dl.level6 { + background-color: #ffe390; /* orange */ + color: black; + margin-left: 6em; +} + +#JSLINT_ dl.level7 { + background-color: #e0e0e0; /* gray */ + color: black; + margin-left: 7em; +} + +#JSLINT_ dl.level8 { + color: black; + margin-left: 8em; +} + +#JSLINT_ dl.level9 { + color: black; + margin-left: 9em; +} + +.none { + display: none; +} +.center { + text-align: center; +} diff --git a/branch.biginit/jslint.js b/branch.biginit/jslint.js new file mode 100644 index 000000000..163ed3c83 --- /dev/null +++ b/branch.biginit/jslint.js @@ -0,0 +1,4967 @@ +// jslint.js +// 2019-09-17 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// jslint(source, option_object, global_array) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze, a string or an array of strings. +// option_object An object whose keys correspond to option names. +// global_array An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all of the functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// 1. If the source is a single string, split it into an array of strings. +// 2. Turn the source into an array of tokens. +// 3. Furcate the tokens into a parse tree. +// 4. Walk the tree, traversing all of the nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// 5. Check the whitespace between the tokens. + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*property + a, and, arity, assign, b, bad_assignment_a, bad_directive_a, bad_get, + bad_module_name_a, bad_option_a, bad_property_a, bad_set, bitwise, block, + body, browser, c, calls, catch, charCodeAt, closer, closure, code, column, + concat, constant, context, convert, couch, create, d, dead, default, devel, + directive, directives, disrupt, dot, duplicate_a, edition, ellipsis, else, + empty_block, escape_mega, eval, every, expected_a, expected_a_at_b_c, + expected_a_b, expected_a_b_from_c_d, expected_a_before_b, + expected_a_next_at_b, expected_digits_after_a, expected_four_digits, + expected_identifier_a, expected_line_break_a_b, expected_regexp_factor_a, + expected_space_a_b, expected_statements_a, expected_string_a, + expected_type_string_a, exports, expression, extra, finally, flag, for, + forEach, free, freeze, freeze_exports, from, froms, fud, fudge, + function_in_loop, functions, g, getset, global, i, id, identifier, import, + inc, indexOf, infix_in, init, initial, isArray, isNaN, join, json, keys, + label, label_a, lbp, led, length, level, line, lines, live, long, loop, m, + margin, match, message, misplaced_a, misplaced_directive_a, missing_browser, + missing_m, module, naked_block, name, names, nested_comment, new, node, + not_label_a, nr, nud, number_isNaN, ok, open, opening, option, + out_of_scope_a, parameters, parent, pop, property, push, quote, + redefinition_a_b, replace, required_a_optional_b, reserved_a, role, search, + shebang, signature, single, slice, some, sort, split, startsWith, statement, + stop, subscript_a, switch, test, this, thru, toString, todo_comment, + tokens, too_long, too_many_digits, tree, try, type, u, unclosed_comment, + unclosed_mega, unclosed_string, undeclared_a, unexpected_a, + unexpected_a_after_b, unexpected_a_before_b, unexpected_at_top_level_a, + unexpected_char_a, unexpected_comment, unexpected_directive_a, + unexpected_expression_a, unexpected_label_a, unexpected_parens, + unexpected_space_a_b, unexpected_statement_a, unexpected_trailing_space, + unexpected_typeof_a, uninitialized_a, unreachable_a, + unregistered_property_a, unsafe, unused_a, use_double, use_open, use_spaces, + used, value, var_loop, var_switch, variable, warning, warnings, + weird_condition_a, weird_expression_a, weird_loop, weird_relation_a, white, + wrap_condition, wrap_immediate, wrap_parameter, wrap_regexp, wrap_unary, + wrapped, writable, y +*/ + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than '{}' because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +function populate(array, object = empty(), value = true) { + +// Augment an object by taking property names from an array of strings. + + array.forEach(function (name) { + object[name] = value; + }); + return object; +} + +const allowed_option = { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + bitwise: true, + browser: [ + "caches", "clearInterval", "clearTimeout", "document", "DOMException", + "Element", "Event", "event", "FileReader", "FormData", "history", + "localStorage", "location", "MutationObserver", "name", "navigator", + "screen", "sessionStorage", "setInterval", "setTimeout", "Storage", + "TextDecoder", "TextEncoder", "URL", "window", "Worker", + "XMLHttpRequest" + ], + couch: [ + "emit", "getRow", "isArray", "log", "provides", "registerType", + "require", "send", "start", "sum", "toJSON" + ], + convert: true, + devel: [ + "alert", "confirm", "console", "prompt" + ], + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + node: [ + "Buffer", "clearImmediate", "clearInterval", "clearTimeout", + "console", "exports", "module", "process", "require", + "setImmediate", "setInterval", "setTimeout", "TextDecoder", + "TextEncoder", "URL", "URLSearchParams", "__dirname", "__filename" + ], + single: true, + this: true, + white: true +}; + +const anticondition = populate([ + "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%", + "typeof", "(number)", "(string)" +]); + +// These are the bitwise operators. + +const bitwiseop = populate([ + "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=", + ">>>", ">>>=" +]); + +const escapeable = populate([ + "\\", "/", "`", "b", "f", "n", "r", "t" +]); + +const opener = { + +// The open and close pairs. + + "(": ")", // paren + "[": "]", // bracket + "{": "}", // brace + "${": "}" // mega +}; + +// The relational operators. + +const relationop = populate([ + "!=", "!==", "==", "===", "<", "<=", ">", ">=" +]); + +// This is the set of infix operators that require a space on each side. + +const spaceop = populate([ + "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/", + "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=", + ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" +]); + +const standard = [ + +// These are the globals that are provided by the language standard. + + "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI", + "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error", + "EvalError", "Float32Array", "Float64Array", "Generator", + "GeneratorFunction", "Int8Array", "Int16Array", "Int32Array", "Intl", + "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat", + "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", + "Set", "String", "Symbol", "SyntaxError", "System", "TypeError", + "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", + "URIError", "WeakMap", "WeakSet" +]; + +const bundle = { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + and: "The '&&' subexpression should be wrapped in parens.", + bad_assignment_a: "Bad assignment to '{a}'.", + bad_directive_a: "Bad directive '{a}'.", + bad_get: "A get function takes no parameters.", + bad_module_name_a: "Bad module name '{a}'.", + bad_option_a: "Bad option '{a}'.", + bad_property_a: "Bad property name '{a}'.", + bad_set: "A set function takes one parameter.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + escape_mega: "Unexpected escapement in mega literal.", + expected_a: "Expected '{a}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: ( + "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'." + ), + expected_a_before_b: "Expected '{a}' before '{b}'.", + expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.", + expected_digits_after_a: "Expected digits after '{a}'.", + expected_four_digits: "Expected four digits after '\\u'.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.", + expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.", + expected_space_a_b: "Expected one space between '{a}' and '{b}'.", + expected_statements_a: "Expected statements before '{a}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + expected_type_string_a: "Expected a type string and instead saw '{a}'.", + freeze_exports: ( + "Expected 'Object.freeze('. All export values should be frozen." + ), + function_in_loop: "Don't make functions within a loop.", + infix_in: ( + "Unexpected 'in'. Compare with undefined, " + + "or use the hasOwnProperty method instead." + ), + label_a: "'{a}' is a statement label.", + misplaced_a: "Place '{a}' at the outermost level.", + misplaced_directive_a: ( + "Place the '/*{a}*/' directive before the first statement." + ), + missing_browser: "/*global*/ requires the Assume a browser option.", + missing_m: "Expected 'm' flag on a multiline regular expression.", + naked_block: "Naked block.", + nested_comment: "Nested comment.", + not_label_a: "'{a}' is not a label.", + number_isNaN: "Use Number.isNaN function to compare with NaN.", + out_of_scope_a: "'{a}' is out of scope.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + required_a_optional_b: ( + "Required parameter '{a}' after optional parameter '{b}'." + ), + reserved_a: "Reserved name '{a}'.", + subscript_a: "['{a}'] is better written in dot notation.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line is longer than 80 characters.", + too_many_digits: "Too many digits.", + unclosed_comment: "Unclosed comment.", + unclosed_mega: "Unclosed mega literal.", + unclosed_string: "Unclosed string.", + undeclared_a: "Undeclared '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_a_after_b: "Unexpected '{a}' after '{b}'.", + unexpected_a_before_b: "Unexpected '{a}' before '{b}'.", + unexpected_at_top_level_a: "Expected '{a}' to be in a function.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_directive_a: "When using modules, don't use directive '/*{a}'.", + unexpected_expression_a: ( + "Unexpected expression '{a}' in statement position." + ), + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_parens: "Don't wrap function literals in parens.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_statement_a: ( + "Unexpected statement '{a}' in expression position." + ), + unexpected_trailing_space: "Unexpected trailing space.", + unexpected_typeof_a: ( + "Unexpected 'typeof'. Use '===' to compare directly with {a}." + ), + uninitialized_a: "Uninitialized '{a}'.", + unreachable_a: "Unreachable '{a}'.", + unregistered_property_a: "Unregistered property name '{a}'.", + unsafe: "Unsafe character '{a}'.", + unused_a: "Unused '{a}'.", + use_double: "Use double quotes, not single quotes.", + use_open: ( + "Wrap a ternary expression in parens, " + + "with a line break after the left paren." + ), + use_spaces: "Use spaces, not tabs.", + var_loop: "Don't declare variables in a loop.", + var_switch: "Don't declare variables in a switch.", + weird_condition_a: "Weird condition '{a}'.", + weird_expression_a: "Weird expression '{a}'.", + weird_loop: "Weird loop.", + weird_relation_a: "Weird relation '{a}'.", + wrap_condition: "Wrap the condition in parens.", + wrap_immediate: ( + "Wrap an immediate function invocation in parentheses to assist " + + "the reader in understanding that the expression is the result " + + "of a function, and not the function itself." + ), + wrap_parameter: "Wrap the parameter in parens.", + wrap_regexp: "Wrap this regexp in parens to avoid confusion.", + wrap_unary: "Wrap the unary expression in parens." +}; + +// Regular expression literals: + +// supplant {variables} +const rx_supplant = /\{([^{}]*)\}/g; +// carriage return, carriage return linefeed, or linefeed +const rx_crlf = /\n|\r\n?/; +// unsafe characters that are silently deleted by one or more browsers +const rx_unsafe = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; +// identifier +const rx_identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; +const rx_module = /^[a-zA-Z0-9_$:.@\-\/]+$/; +const rx_bad_property = /^_|\$|Sync\$|_$/; +// star slash +const rx_star_slash = /\*\//; +// slash star +const rx_slash_star = /\/\*/; +// slash star or ending slash +const rx_slash_star_or_slash = /\/\*|\/$/; +// uncompleted work comment +const rx_todo = /\b(?:todo|TO\s?DO|HACK)\b/; +// tab +const rx_tab = /\t/g; +// directive +const rx_directive = /^(jslint|property|global)\s+(.*)$/; +const rx_directive_part = /^([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*(.*)$/; +// token (sorry it is so long) +const rx_token = /^((\s+)|([a-zA-Z_$][a-zA-Z0-9_$]*)|[(){}\[\],:;'"~`]|\?\.?|=(?:==?|>)?|\.+|[*\/][*\/=]?|\+[=+]?|-[=\-]?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<= "a" && string <= "z\uffff") + || (string >= "A" && string <= "Z\uffff") + ); +} + +function supplant(string, object) { + return string.replace(rx_supplant, function (found, filling) { + const replacement = object[filling]; + return ( + replacement !== undefined + ? replacement + : found + ); + }); +} + +let anon; // The guessed name for anonymous functions. +let blockage; // The current block. +let block_stack; // The stack of blocks. +let declared_globals; // The object containing the global declarations. +let directives; // The directive comments. +let directive_mode; // true if directives are still allowed. +let early_stop; // true if JSLint cannot finish. +let exports; // The exported names and values. +let froms; // The array collecting all import-from strings. +let fudge; // true if the natural numbers start with 1. +let functionage; // The current function. +let functions; // The array containing all of the functions. +let global; // The global object; the outermost context. +let json_mode; // true if parsing JSON. +let lines; // The array containing source lines. +let mega_mode; // true if currently parsing a megastring literal. +let module_mode; // true if import or export was used. +let next_token; // The next token to be examined in the parse. +let option; // The options parameter. +let property; // The object containing the tallied property names. +let shebang; // true if a #! was seen on the first line. +let stack; // The stack of functions. +let syntax; // The object containing the parser. +let token; // The current token being examined in the parse. +let token_nr; // The number of the next token. +let tokens; // The array of tokens. +let tenure; // The predefined property registry. +let tree; // The abstract parse tree. +let var_mode; // "var" if using var; "let" if using let. +let warnings; // The array collecting all generated warnings. + +// Error reportage functions: + +function artifact(the_token) { + +// Return a string representing an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); +} + +function artifact_line(the_token) { + +// Return the fudged line number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.line + fudge; +} + +function artifact_column(the_token) { + +// Return the fudged column number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.from + fudge; +} + +function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + const warning = { // ~~ + name: "JSLintError", + column, + line, + code + }; + if (a !== undefined) { + warning.a = a; + } + if (b !== undefined) { + warning.b = b; + } + if (c !== undefined) { + warning.c = c; + } + if (d !== undefined) { + warning.d = d; + } + warning.message = supplant(bundle[code] || code, warning); + warnings.push(warning); + return warning; +} + +function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); +} + +function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + if (the_token.warning === undefined) { + the_token.warning = warn_at( + code, + the_token.line, + the_token.from, + a || artifact(the_token), + b, + c, + d + ); + return the_token.warning; + } +} + +function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); +} + +// Tokenize: + +function tokenize(source) { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + +// If the source is not an array, then it is split into lines at the +// carriage return/linefeed. + + lines = ( + Array.isArray(source) + ? source + : source.split(rx_crlf) + ); + tokens = []; + + let char; // a popular character + let column = 0; // the column number of the next character + let first; // the first token + let from; // the starting column number of the token + let line = -1; // the line number of the next character + let nr = 0; // the next token number + let previous = global; // the previous token including comments + let prior = global; // the previous token excluding comments + let mega_from; // the starting column of megastring + let mega_line; // the starting line of megastring + let regexp_seen; // regular expression literal seen on this line + let snippet; // a piece of string + let source_line = ""; // the remaining line source string + let whole_line = ""; // the whole line source string + + if (lines[0].startsWith("#!")) { + line = 0; + shebang = true; + } + + function next_line() { + +// Put the next line of source in source_line. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + let at; + if ( + !option.long + && whole_line.length > 80 + && !json_mode + && first + && !regexp_seen + ) { + warn_at("too_long", line, 80); + } + column = 0; + line += 1; + regexp_seen = false; + source_line = lines[line]; + whole_line = source_line || ""; + if (source_line !== undefined) { + at = source_line.search(rx_tab); + if (at >= 0) { + if (!option.white) { + warn_at("use_spaces", line, at + 1); + } + source_line = source_line.replace(rx_tab, " "); + } + at = source_line.search(rx_unsafe); + if (at >= 0) { + warn_at( + "unsafe", + line, + column + at, + "U+" + source_line.charCodeAt(at).toString(16) + ); + } + if (!option.white && source_line.slice(-1) === " ") { + warn_at( + "unexpected_trailing_space", + line, + source_line.length - 1 + ); + } + } + return source_line; + } + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions snip, next_char, prev_char, +// some_digits, and escape help in the parsing of literals. + + function snip() { + +// Remove the last character from snippet. + + snippet = snippet.slice(0, -1); + } + + function next_char(match) { + +// Get the next character from the source line. Remove it from the source_line, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + return stop_at( + ( + char === "" + ? "expected_a" + : "expected_a_b" + ), + line, + column - 1, + match, + char + ); + } + if (source_line) { + char = source_line[0]; + source_line = source_line.slice(1); + snippet += char; + } else { + char = ""; + snippet += " "; + } + column += 1; + return char; + } + + function back_char() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the source_line. + + if (snippet) { + char = snippet.slice(-1); + source_line = char + source_line; + column -= 1; + snip(); + } else { + char = ""; + } + return char; + } + + function some_digits(rx, quiet) { + const result = source_line.match(rx); + if (result) { + char = result[1]; + column += char.length; + source_line = result[2]; + snippet += char; + } else { + char = ""; + if (!quiet) { + warn_at( + "expected_digits_after_a", + line, + column, + snippet + ); + } + } + return char.length; + } + + function escape(extra) { + next_char("\\"); + if (escapeable[char] === true) { + return next_char(); + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "u") { + if (next_char("u") === "{") { + if (json_mode) { + warn_at("unexpected_a", line, column - 1, char); + } + if (some_digits(rx_hexs) > 5) { + warn_at("too_many_digits", line, column - 1); + } + if (next_char() !== "}") { + stop_at("expected_a_before_b", line, column, "}", char); + } + return next_char(); + } + back_char(); + if (some_digits(rx_hexs, true) < 4) { + warn_at("expected_four_digits", line, column - 1); + } + return; + } + if (extra && extra.indexOf(char) >= 0) { + return next_char(); + } + warn_at("unexpected_a_before_b", line, column - 2, "\\", char); + } + + function make(id, value, identifier) { + +// Make the token object and append it to the tokens list. + + const the_token = { + from, + id, + identifier: Boolean(identifier), + line, + nr, + thru: column + }; + tokens[nr] = the_token; + nr += 1; + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + directive_mode = false; + } + +// If the token is to have a value, give it one. + + if (value !== undefined) { + the_token.value = value; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option.white. + + if ( + previous.line === line + && previous.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (previous.id === "(comment)" || previous.id === "(regexp)") + ) { + warn( + "expected_space_a_b", + the_token, + artifact(previous), + artifact(the_token) + ); + } + if (previous.id === "." && id === "(number)") { + warn("expected_a_before_b", previous, "0", "."); + } + if (prior.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// The previous token is used to detect adjacency problems. + + previous = the_token; + +// The prior token is a previous token that was not a comment. The prior token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (previous.id !== "(comment)") { + prior = previous; + } + return the_token; + } + + function parse_directive(the_comment, body) { + +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + + const result = body.match(rx_directive_part); + if (result) { + let allowed; + const name = result[1]; + const value = result[2]; + if (the_comment.directive === "jslint") { + allowed = allowed_option[name]; + if ( + typeof allowed === "boolean" + || typeof allowed === "object" + ) { + if ( + value === "" + || value === "true" + || value === undefined + ) { + option[name] = true; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } else if (value === "false") { + option[name] = false; + } else { + warn("bad_option_a", the_comment, name + ":" + value); + } + } else { + warn("bad_option_a", the_comment, name); + } + } else if (the_comment.directive === "property") { + if (tenure === undefined) { + tenure = empty(); + } + tenure[name] = true; + } else if (the_comment.directive === "global") { + if (value) { + warn("bad_option_a", the_comment, name + ":" + value); + } + declared_globals[name] = false; + module_mode = the_comment; + } + return parse_directive(the_comment, result[3]); + } + if (body) { + return stop("bad_directive_a", the_comment, body); + } + } + + function comment(snippet) { + +// Make a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + + const the_comment = make("(comment)", snippet); + if (Array.isArray(snippet)) { + snippet = snippet.join(" "); + } + if (!option.devel && rx_todo.test(snippet)) { + warn("todo_comment", the_comment); + } + const result = snippet.match(rx_directive); + if (result) { + if (!directive_mode) { + warn_at("misplaced_directive_a", line, from, result[1]); + } else { + the_comment.directive = result[1]; + parse_directive(the_comment, result[2]); + } + directives.push(the_comment); + } + return the_comment; + } + + function regexp() { + +// Parse a regular expression literal. + + let multi_mode = false; + let result; + let value; + regexp_seen = true; + + function quantifier() { + +// Match an optional quantifier. + + if (char === "?" || char === "*" || char === "+") { + next_char(); + } else if (char === "{") { + if (some_digits(rx_digits, true) === 0) { + warn_at("expected_a", line, column, "0"); + } + if (next_char() === ",") { + some_digits(rx_digits, true); + next_char(); + } + next_char("}"); + } else { + return; + } + if (char === "?") { + next_char("?"); + } + } + + function subklass() { + +// Match a character in a character class. + + if (char === "\\") { + escape("BbDdSsWw-[]^"); + return true; + } + if ( + char === "" + || char === "[" + || char === "]" + || char === "/" + || char === "^" + || char === "-" + ) { + return false; + } + if (char === " ") { + warn_at("expected_a_b", line, column, "\\u0020", " "); + } else if (char === "`" && mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char(); + return true; + } + + function ranges() { + +// Match a range of subclasses. + + if (subklass()) { + if (char === "-") { + next_char("-"); + if (!subklass()) { + return stop_at( + "unexpected_a", + line, + column - 1, + "-" + ); + } + } + return ranges(); + } + } + + function klass() { + +// Match a class. + + next_char("["); + if (char === "^") { + next_char("^"); + } + (function classy() { + ranges(); + if (char !== "]" && char !== "") { + warn_at( + "expected_a_before_b", + line, + column, + "\\", + char + ); + next_char(); + return classy(); + } + }()); + next_char("]"); + } + + function choice() { + + function group() { + +// Match a group that starts with left paren. + + next_char("("); + if (char === "?") { + next_char("?"); + if (char === "=" || char === "!") { + next_char(); + } else { + next_char(":"); + } + } else if (char === ":") { + warn_at("expected_a_before_b", line, column, "?", ":"); + } + choice(); + next_char(")"); + } + + function factor() { + if ( + char === "" + || char === "/" + || char === "]" + || char === ")" + ) { + return false; + } + if (char === "(") { + group(); + return true; + } + if (char === "[") { + klass(); + return true; + } + if (char === "\\") { + escape("BbDdSsWw^${}[]():=!.|*+?"); + return true; + } + if ( + char === "?" + || char === "+" + || char === "*" + || char === "}" + || char === "{" + ) { + warn_at( + "expected_a_before_b", + line, + column - 1, + "\\", + char + ); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column - 1, "`"); + } + } else if (char === " ") { + warn_at( + "expected_a_b", + line, + column - 1, + "\\s", + " " + ); + } else if (char === "$") { + if (source_line[0] !== "/") { + multi_mode = true; + } + } else if (char === "^") { + if (snippet !== "^") { + multi_mode = true; + } + } + next_char(); + return true; + } + + function sequence(follow) { + if (factor()) { + quantifier(); + return sequence(true); + } + if (!follow) { + warn_at("expected_regexp_factor_a", line, column, char); + } + } + +// Match a choice (a sequence that can be followed by | and another choice). + + sequence(); + if (char === "|") { + next_char("|"); + return choice(); + } + } + +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + next_char(); + if (char === "=") { + warn_at("expected_a_before_b", line, column, "\\", "="); + } + choice(); + +// Make sure there is a closing slash. + + snip(); + value = snippet; + next_char("/"); + +// Process dangling flag letters. + + const allowed = { + g: true, + i: true, + m: true, + u: true, + y: true + }; + const flag = empty(); + (function make_flag() { + if (is_letter(char)) { + if (allowed[char] !== true) { + warn_at("unexpected_a", line, column, char); + } + allowed[char] = false; + flag[char] = true; + next_char(); + return make_flag(); + } + }()); + back_char(); + if (char === "/" || char === "*") { + return stop_at("unexpected_a", line, from, char); + } + result = make("(regexp)", char); + result.flag = flag; + result.value = value; + if (multi_mode && !flag.m) { + warn_at("missing_m", line, column); + } + return result; + } + + function string(quote) { + +// Make a string token. + + let the_token; + snippet = ""; + next_char(); + + return (function next() { + if (char === quote) { + snip(); + the_token = make("(string)", snippet); + the_token.quote = quote; + return the_token; + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "\\") { + escape(quote); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char("`"); + } else { + next_char(); + } + return next(); + }()); + } + + function frack() { + if (char === ".") { + some_digits(rx_digits); + next_char(); + } + if (char === "E" || char === "e") { + next_char(); + if (char !== "+" && char !== "-") { + back_char(); + } + some_digits(rx_digits); + next_char(); + } + } + + function number() { + if (snippet === "0") { + next_char(); + if (char === ".") { + frack(); + } else if (char === "b") { + some_digits(rx_bits); + next_char(); + } else if (char === "o") { + some_digits(rx_octals); + next_char(); + } else if (char === "x") { + some_digits(rx_hexs); + next_char(); + } + } else { + next_char(); + frack(); + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + return stop_at( + "unexpected_a_after_b", + line, + column - 1, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + back_char(); + return make("(number)", snippet); + } + + function lex() { + let array; + let i = 0; + let j = 0; + let last; + let result; + let the_token; + +// This should properly be a tail recursive function, but sadly, conformant +// implementations of ES6 are still rare. This is the ideal code: + +// if (!source_line) { +// source_line = next_line(); +// from = 0; +// return ( +// source_line === undefined +// ? ( +// mega_mode +// ? stop_at("unclosed_mega", mega_line, mega_from) +// : make("(end)") +// ) +// : lex() +// ); +// } + +// Unfortunately, incompetent JavaScript engines will sometimes fail to execute +// it correctly. So for now, we do it the old fashioned way. + + while (!source_line) { + source_line = next_line(); + from = 0; + if (source_line === undefined) { + return ( + mega_mode + ? stop_at("unclosed_mega", mega_line, mega_from) + : make("(end)") + ); + } + } + + from = column; + result = source_line.match(rx_token); + +// result[1] token +// result[2] whitespace +// result[3] identifier +// result[4] number +// result[5] rest + + if (!result) { + return stop_at( + "unexpected_char_a", + line, + column, + source_line[0] + ); + } + + snippet = result[1]; + column += snippet.length; + source_line = result[5]; + +// Whitespace was matched. Call lex again to get more. + + if (result[2]) { + return lex(); + } + +// The token is an identifier. + + if (result[3]) { + return make(snippet, undefined, true); + } + +// The token is a number. + + if (result[4]) { + return number(snippet); + } + +// The token is a string. + + if (snippet === "\"") { + return string(snippet); + } + if (snippet === "'") { + if (!option.single) { + warn_at("use_double", line, column); + } + return string(snippet); + } + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (snippet === "`") { + if (mega_mode) { + return stop_at("expected_a_b", line, column, "}", "`"); + } + snippet = ""; + mega_from = from; + mega_line = line; + mega_mode = true; + +// Parsing a mega literal is tricky. First make a ` token. + + make("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + (function part() { + const at = source_line.search(rx_mega); + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + if (at < 0) { + snippet += source_line + "\n"; + return ( + next_line() === undefined + ? stop_at("unclosed_mega", mega_line, mega_from) + : part() + ); + } + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + snippet += source_line.slice(0, at); + column += at; + source_line = source_line.slice(at); + if (source_line[0] === "\\") { + stop_at("escape_mega", line, at); + } + make("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then make tokens that will become part of an expression until +// a } token is made. + + if (source_line[0] === "$") { + column += 2; + make("${"); + source_line = source_line.slice(2); + (function expr() { + const id = lex().id; + if (id === "{") { + return stop_at( + "expected_a_b", + line, + column, + "}", + "{" + ); + } + if (id !== "}") { + return expr(); + } + }()); + return part(); + } + }()); + source_line = source_line.slice(1); + column += 1; + mega_mode = false; + return make("`"); + } + +// The token is a // comment. + + if (snippet === "//") { + snippet = source_line; + source_line = ""; + the_token = comment(snippet); + if (mega_mode) { + warn("unexpected_comment", the_token, "`"); + } + return the_token; + } + +// The token is a /* comment. + + if (snippet === "/*") { + array = []; + if (source_line[0] === "/") { + warn_at("unexpected_a", line, column + i, "/"); + } + (function next() { + if (source_line > "") { + i = source_line.search(rx_star_slash); + if (i >= 0) { + return; + } + j = source_line.search(rx_slash_star); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + } + array.push(source_line); + source_line = next_line(); + if (source_line === undefined) { + return stop_at("unclosed_comment", line, column); + } + return next(); + }()); + snippet = source_line.slice(0, i); + j = snippet.search(rx_slash_star_or_slash); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + array.push(snippet); + column += i + 2; + source_line = source_line.slice(i + 2); + return comment(array); + } + +// The token is a slash. + + if (snippet === "/") { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + if (prior.identifier) { + if (!prior.dot) { + if (prior.id === "return") { + return regexp(); + } + if ( + prior.id === "(begin)" + || prior.id === "case" + || prior.id === "delete" + || prior.id === "in" + || prior.id === "instanceof" + || prior.id === "new" + || prior.id === "typeof" + || prior.id === "void" + || prior.id === "yield" + ) { + the_token = regexp(); + return stop("unexpected_a", the_token); + } + } + } else { + last = prior.id[prior.id.length - 1]; + if ("(,=:?[".indexOf(last) >= 0) { + return regexp(); + } + if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) { + the_token = regexp(); + warn("wrap_regexp", the_token); + return the_token; + } + } + if (source_line[0] === "/") { + column += 1; + source_line = source_line.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + } + return make(snippet); + } + + first = lex(); + json_mode = first.id === "{" || first.id === "["; + +// This loop will be replaced with a recursive call to lex when ES6 has been +// finished and widely deployed and adopted. + + while (true) { + if (lex().id === "(end)") { + break; + } + } +} + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + +function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!rx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!rx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property[id] === "number") { + property[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (tenure !== undefined) { + if (tenure[id] !== true) { + warn("unregistered_property_a", name); + } + } else { + if (name.identifier && rx_bad_property.test(id)) { + warn("bad_property_a", name); + } + } + property[id] = 1; + } + return id; +} + +function dispense() { + +// Deliver the next token, skipping the comments. + + const cadet = tokens[token_nr]; + token_nr += 1; + if (cadet.id === "(comment)") { + if (json_mode) { + warn("unexpected_a", cadet); + } + return dispense(); + } else { + return cadet; + } +} + +function lookahead() { + +// Look ahead one token without advancing. + + const old_token_nr = token_nr; + const cadet = dispense(true); + token_nr = old_token_nr; + return cadet; +} + +function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if (token.identifier && token.id !== "function") { + anon = token.id; + } else if (token.id === "(string)" && rx_identifier.test(token.value)) { + anon = token.value; + } + +// Attempt to match next_token with an expected id. + + if (id !== undefined && next_token.id !== id) { + return ( + match === undefined + ? stop("expected_a_b", next_token, id, artifact()) + : stop( + "expected_a_b_from_c_d", + next_token, + id, + artifact(match), + artifact_line(match), + artifact(next_token) + ) + ); + } + +// Promote the tokens, skipping comments. + + token = next_token; + next_token = dispense(); + if (next_token.id === "(end)") { + token_nr -= 1; + } +} + +// Parsing of JSON is simple: + +function json_value() { + let negative; + if (next_token.id === "{") { + return (function json_object() { + const brace = next_token; + const object = empty(); + const properties = []; + brace.expression = properties; + advance("{"); + if (next_token.id !== "}") { + (function next() { + let name; + let value; + if (next_token.quote !== "\"") { + warn( + "unexpected_a", + next_token, + next_token.quote + ); + } + name = next_token; + advance("(string)"); + if (object[token.value] !== undefined) { + warn("duplicate_a", token); + } else if (token.value === "__proto__") { + warn("bad_property_a", token); + } else { + object[token.value] = token; + } + advance(":"); + value = json_value(); + value.label = name; + properties.push(value); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("}", brace); + return brace; + }()); + } + if (next_token.id === "[") { + return (function json_array() { + const bracket = next_token; + const elements = []; + bracket.expression = elements; + advance("["); + if (next_token.id !== "]") { + (function next() { + elements.push(json_value()); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]", bracket); + return bracket; + }()); + } + if ( + next_token.id === "true" + || next_token.id === "false" + || next_token.id === "null" + ) { + advance(); + return token; + } + if (next_token.id === "(number)") { + if (!rx_JSON_number.test(next_token.value)) { + warn("unexpected_a"); + } + advance(); + return token; + } + if (next_token.id === "(string)") { + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + advance(); + return token; + } + if (next_token.id === "-") { + negative = next_token; + negative.arity = "unary"; + advance("-"); + advance("(number)"); + negative.expression = token; + return negative; + } + stop("unexpected_a"); +} + +// Now we parse JavaScript. + +function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + const id = name.id; + +// Reserved words may not be enrolled. + + if (syntax[id] !== undefined && id !== "ignore") { + warn("reserved_a", name); + } else { + +// Has the name been enrolled in this context? + + let earlier = functionage.context[id]; + if (earlier) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + +// Has the name been enrolled in an outer context? + + } else { + stack.forEach(function (value) { + const item = value.context[id]; + if (item !== undefined) { + earlier = item; + } + }); + if (earlier) { + if (id === "ignore") { + if (earlier.role === "variable") { + warn("unexpected_a", name); + } + } else { + if ( + ( + role !== "exception" + || earlier.role !== "exception" + ) + && role !== "parameter" + && role !== "function" + ) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + } + } + } + +// Enroll it. + + functionage.context[id] = name; + name.dead = true; + name.parent = functionage; + name.init = false; + name.role = role; + name.used = 0; + name.writable = !readonly; + } + } +} + +function expression(rbp, initial) { + +// This is the heart of the Pratt parser. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// nud Null denotation +// led Left denotation +// lbp Left binding power +// rbp Right binding power + +// It processes a nud (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop. It +// returns the expression's parse tree. + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement, + + if (!initial) { + advance(); + } + the_symbol = syntax[token.id]; + if (the_symbol !== undefined && the_symbol.nud !== undefined) { + left = the_symbol.nud(); + } else if (token.identifier) { + left = token; + left.arity = "variable"; + } else { + return stop("unexpected_a", token); + } + (function right() { + the_symbol = syntax[next_token.id]; + if ( + the_symbol !== undefined + && the_symbol.led !== undefined + && rbp < the_symbol.lbp + ) { + advance(); + left = the_symbol.led(left); + return right(); + } + }()); + return left; +} + +function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = next_token; + let the_value; + the_paren.free = true; + advance("("); + the_value = expression(0); + advance(")"); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (anticondition[the_value.id] === true) { + warn("unexpected_a", the_value); + } + return the_value; +} + +function is_weird(thing) { + return ( + thing.id === "(regexp)" + || thing.id === "{" + || thing.id === "=>" + || thing.id === "function" + || (thing.id === "[" && thing.arity === "unary") + ); +} + +function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + return ( + Array.isArray(b) + && a.length === b.length + && a.every(function (value, index) { + return are_similar(value, b[index]); + }) + ); + } + if (Array.isArray(b)) { + return false; + } + if (a.id === "(number)" && b.id === "(number)") { + return a.value === b.value; + } + let a_string; + let b_string; + if (a.id === "(string)") { + a_string = a.value; + } else if (a.id === "`" && a.constant) { + a_string = a.value[0]; + } + if (b.id === "(string)") { + b_string = b.value; + } else if (b.id === "`" && b.constant) { + b_string = b.value[0]; + } + if (typeof a_string === "string") { + return a_string === b_string; + } + if (is_weird(a) || is_weird(b)) { + return false; + } + if (a.arity === b.arity && a.id === b.id) { + if (a.id === ".") { + return ( + are_similar(a.expression, b.expression) + && are_similar(a.name, b.name) + ); + } + if (a.arity === "unary") { + return are_similar(a.expression, b.expression); + } + if (a.arity === "binary") { + return ( + a.id !== "(" + && are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + ); + } + if (a.arity === "ternary") { + return ( + are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + && are_similar(a.expression[2], b.expression[2]) + ); + } + if (a.arity === "function" && a.arity === "regexp") { + return false; + } + return true; + } + return false; +} + +function semicolon() { + +// Try to match a semicolon. + + if (next_token.id === ";") { + advance(";"); + } else { + warn_at( + "expected_a_b", + token.line, + token.thru, + ";", + artifact(next_token) + ); + } + anon = "anonymous"; +} + +function statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token.identifier && next_token.id === ":") { + the_label = token; + if (the_label.id === "ignore") { + warn("unexpected_a", the_label); + } + advance(":"); + if ( + next_token.id === "do" + || next_token.id === "for" + || next_token.id === "switch" + || next_token.id === "while" + ) { + enroll(the_label, "label", true); + the_label.init = true; + the_label.dead = false; + the_statement = statement(); + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token; + first.statement = true; + the_symbol = syntax[first.id]; + if (the_symbol !== undefined && the_symbol.fud !== undefined) { + the_symbol.disrupt = false; + the_symbol.statement = true; + the_statement = the_symbol.fud(); + } else { + +// It is an expression statement. + + the_statement = expression(0, true); + if (the_statement.wrapped && the_statement.id !== "(") { + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; +} + +function statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const array = []; + (function next(disrupt) { + if ( + next_token.id !== "}" + && next_token.id !== "case" + && next_token.id !== "default" + && next_token.id !== "else" + && next_token.id !== "(end)" + ) { + let a_statement = statement(); + array.push(a_statement); + if (disrupt) { + warn("unreachable_a", a_statement); + } + return next(a_statement.disrupt); + } + }(false)); + return array; +} + +function not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === global) { + warn("unexpected_at_top_level_a", thing); + } +} + +function top_level_only(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== global) { + warn("misplaced_a", the_thing); + } +} + +function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token; + the_block.arity = "statement"; + the_block.body = special === "body"; + +// Top level function bodies may include the "use strict" pragma. + + if ( + special === "body" + && stack.length === 1 + && next_token.value === "use strict" + ) { + next_token.statement = true; + advance("(string)"); + advance(";"); + } + stmts = statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option.devel && special !== "ignore") { + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; +} + +function mutation_check(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + warn("bad_assignment_a", the_thing); + return false; + } + return true; +} + +function left_check(left, right) { + +// Warn if the left is not one of these: +// e.b +// e[b] +// e() +// ?: +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !left_check(left.expression[1]) + && !left_check(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right); + return false; + } + return true; +} + +// These functions are used to specify the grammar of our language: + +function symbol(id, bp) { + +// Make a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax[id] = the_symbol; + } + return the_symbol; +} + +function assignment(id) { + +// Make an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led = function (left) { + const the_token = token; + let right; + the_token.arity = "assignment"; + right = expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "pre" + || right.arity === "post" + ) { + warn("unexpected_a", right); + } + mutation_check(left); + return the_token; + }; + return the_symbol; +} + +function constant(id, type, value) { + +// Make a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud = ( + typeof value === "function" + ? value + : function () { + token.constant = true; + if (value !== undefined) { + token.value = value; + } + return token; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; +} + +function infix(id, bp, f) { + +// Make an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, expression(bp)]; + return the_token; + }; + return the_symbol; +} + +function infixr(id, bp) { + +// Make a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + the_token.expression = [left, expression(bp - 1)]; + return the_token; + }; + return the_symbol; +} + +function post(id) { + +// Make one of the post operators. + + const the_symbol = symbol(id, 150); + the_symbol.led = function (left) { + token.expression = left; + token.arity = "post"; + mutation_check(token.expression); + return token; + }; + return the_symbol; +} + +function pre(id) { + +// Make one of the pre operators. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "pre"; + the_token.expression = expression(150); + mutation_check(the_token.expression); + return the_token; + }; + return the_symbol; +} + +function prefix(id, f) { + +// Make a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = expression(150); + return the_token; + }; + return the_symbol; +} + +function stmt(id, f) { + +// Make a statement. + + const the_symbol = symbol(id); + the_symbol.fud = function () { + token.arity = "statement"; + return f(); + }; + return the_symbol; +} + +function ternary(id1, id2) { + +// Make a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led = function (left) { + const the_token = token; + const second = expression(20); + advance(id2); + token.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, expression(10)]; + if (next_token.id !== ")") { + warn("use_open", the_token); + } + return the_token; + }; + return the_symbol; +} + +// Begin defining the language. + +syntax = empty(); + +symbol("}"); +symbol(")"); +symbol("]"); +symbol(","); +symbol(";"); +symbol(":"); +symbol("*/"); +symbol("await"); +symbol("case"); +symbol("catch"); +symbol("class"); +symbol("default"); +symbol("else"); +symbol("enum"); +symbol("finally"); +symbol("implements"); +symbol("interface"); +symbol("package"); +symbol("private"); +symbol("protected"); +symbol("public"); +symbol("static"); +symbol("super"); +symbol("void"); +symbol("yield"); + +constant("(number)", "number"); +constant("(regexp)", "regexp"); +constant("(string)", "string"); +constant("arguments", "object", function () { + warn("unexpected_a", token); + return token; +}); +constant("eval", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("false", "boolean", false); +constant("Function", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("ignore", "undefined", function () { + warn("unexpected_a", token); + return token; +}); +constant("Infinity", "number", Infinity); +constant("isFinite", "function", function () { + warn("expected_a_b", token, "Number.isFinite", "isFinite"); + return token; +}); +constant("isNaN", "function", function () { + warn("number_isNaN", token); + return token; +}); +constant("NaN", "number", NaN); +constant("null", "null", null); +constant("this", "object", function () { + if (!option.this) { + warn("unexpected_a", token); + } + return token; +}); +constant("true", "boolean", true); +constant("undefined", "undefined"); + +assignment("="); +assignment("+="); +assignment("-="); +assignment("*="); +assignment("/="); +assignment("%="); +assignment("&="); +assignment("|="); +assignment("^="); +assignment("<<="); +assignment(">>="); +assignment(">>>="); + +infix("||", 40); +infix("&&", 50); +infix("|", 70); +infix("^", 80); +infix("&", 90); +infix("==", 100); +infix("===", 100); +infix("!=", 100); +infix("!==", 100); +infix("<", 110); +infix(">", 110); +infix("<=", 110); +infix(">=", 110); +infix("in", 110); +infix("instanceof", 110); +infix("<<", 120); +infix(">>", 120); +infix(">>>", 120); +infix("+", 130); +infix("-", 130); +infix("*", 140); +infix("/", 140); +infix("%", 140); +infixr("**", 150); +infix("(", 160, function (left) { + const the_paren = token; + let the_argument; + if (left.id !== "function") { + left_check(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (next_token.id !== ")") { + (function next() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + the_paren.free = true; + if (the_argument.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + the_paren.free = false; + } + return the_paren; +}); +infix(".", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("?.", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("[", 170, function (left) { + const the_token = token; + const the_subscript = expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + const name = survey(the_subscript); + if (rx_identifier.test(name)) { + warn("subscript_a", the_subscript, name); + } + } + left_check(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; +}); +infix("=>", 170, function (left) { + return stop("wrap_parameter", left); +}); + +function do_tick() { + const the_tick = token; + the_tick.value = []; + the_tick.expression = []; + if (next_token.id !== "`") { + (function part() { + advance("(string)"); + the_tick.value.push(token); + if (next_token.id === "${") { + advance("${"); + the_tick.expression.push(expression(0)); + advance("}"); + return part(); + } + }()); + } + advance("`"); + return the_tick; +} + +infix("`", 160, function (left) { + const the_tick = do_tick(); + left_check(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; +}); + +post("++"); +post("--"); +pre("++"); +pre("--"); + +prefix("+"); +prefix("-"); +prefix("~"); +prefix("!"); +prefix("!!"); +prefix("[", function () { + const the_token = token; + the_token.expression = []; + if (next_token.id !== "]") { + (function next() { + let element; + let ellipsis = false; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + element = expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]"); + return the_token; +}); +prefix("/=", function () { + stop("expected_a_b", token, "/\\=", "/="); +}); +prefix("=>", function () { + return stop("expected_a_before_b", token, "()", "=>"); +}); +prefix("new", function () { + const the_new = token; + const right = expression(160); + if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "()", artifact(next_token)); + } + the_new.expression = right; + return the_new; +}); +prefix("typeof"); +prefix("void", function () { + const the_void = token; + warn("unexpected_a", the_void); + the_void.expression = expression(0); + return the_void; +}); + +function parameter_list() { + const list = []; + let optional; + const signature = ["("]; + if (next_token.id !== ")" && next_token.id !== "(end)") { + (function parameter() { + let ellipsis = false; + let param; + if (next_token.id === "{") { + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("{"); + signature.push("{"); + (function subparameter() { + let subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (next_token.id === ":") { + advance(":"); + advance(); + token.label = subparam; + subparam = token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + } + if (next_token.id === "=") { + advance("="); + subparam.expression = expression(); + param.open = true; + } + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return subparameter(); + } + }()); + list.push(param); + advance("}"); + signature.push("}"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else if (next_token.id === "[") { + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("["); + signature.push("[]"); + (function subparameter() { + const subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + if (next_token.id === "=") { + advance("="); + subparam.expression = expression(); + param.open = true; + } + if (next_token.id === ",") { + advance(","); + return subparameter(); + } + }()); + list.push(param); + advance("]"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else { + if (next_token.id === "...") { + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + } + if (!next_token.identifier) { + return stop("expected_identifier_a"); + } + param = next_token; + list.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (next_token.id === "=") { + optional = param; + advance("="); + param.expression = expression(0); + } else { + if (optional !== undefined) { + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } + } + }()); + } + advance(")"); + signature.push(")"); + return [list, signature.join("")]; +} + +function do_function(the_function) { + let name; + if (the_function === undefined) { + the_function = token; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + name = next_token; + enroll(name, "variable", true); + the_function.name = name; + name.init = true; + name.calls = empty(); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + if (next_token.identifier) { + name = next_token; + the_function.name = name; + advance(); + } else { + the_function.name = anon; + } + } + } else { + name = the_function.name; + } + the_function.level = functionage.level + 1; + if (mega_mode) { + warn("unexpected_a", the_function); + } + +// Don't make functions in loops. It is inefficient, and it can lead to scoping +// errors. + + if (functionage.loop > 0) { + warn("function_in_loop", the_function); + } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + the_function.context = empty(); + the_function.finally = 0; + the_function.loop = 0; + the_function.switch = 0; + the_function.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functions.push(the_function); + functionage = the_function; + if (the_function.arity !== "statement" && typeof name === "object") { + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// Parse the parameter list. + + advance("("); + token.free = false; + token.arity = "function"; + [functionage.parameters, functionage.signature] = parameter_list(); + functionage.parameters.forEach(function enroll_parameter(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + name.names.forEach(enroll_parameter); + } + }); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && next_token.line === token.line + ) { + return stop("unexpected_a", next_token); + } + if ( + next_token.id === "." + || next_token.id === "?." + || next_token.id === "[" + ) { + warn("unexpected_a"); + } + +// Restore the previous context. + + functionage = stack.pop(); + return the_function; +} + +prefix("function", do_function); + +function fart(pl) { + advance("=>"); + const the_fart = token; + the_fart.arity = "binary"; + the_fart.name = "=>"; + the_fart.level = functionage.level + 1; + functions.push(the_fart); + if (functionage.loop > 0) { + warn("function_in_loop", the_fart); + } + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + the_fart.context = empty(); + the_fart.finally = 0; + the_fart.loop = 0; + the_fart.switch = 0; + the_fart.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functionage = the_fart; + the_fart.parameters = pl[0]; + the_fart.signature = pl[1]; + the_fart.parameters.forEach(function (name) { + enroll(name, "parameter", true); + }); + if (next_token.id === "{") { + warn("expected_a_b", the_fart, "function", "=>"); + the_fart.block = block("body"); + } else { + the_fart.expression = expression(0); + } + functionage = stack.pop(); + return the_fart; +} + +prefix("(", function () { + const the_paren = token; + let the_value; + const cadet = lookahead().id; + +// We can distinguish between a parameter list for => and a wrapped expression +// with one token of lookahead. + + if ( + next_token.id === ")" + || next_token.id === "..." + || (next_token.identifier && (cadet === "," || cadet === "=")) + ) { + the_paren.free = false; + return fart(parameter_list()); + } + the_paren.free = true; + the_value = expression(0); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + if (next_token.id === "=>") { + if (the_value.arity !== "variable") { + if (the_value.id === "{" || the_value.id === "[") { + warn("expected_a_before_b", the_paren, "function", "("); + return stop("expected_a_b", next_token, "{", "=>"); + } + return stop("expected_identifier_a", the_value); + } + the_paren.expression = [the_value]; + return fart([the_paren.expression, "(" + the_value.id + ")"]); + } + return the_value; +}); +prefix("`", do_tick); +prefix("{", function () { + const the_brace = token; + const seen = empty(); + the_brace.expression = []; + if (next_token.id !== "}") { + (function member() { + let extra; + let full; + let id; + let name = next_token; + let value; + advance(); + if ( + (name.id === "get" || name.id === "set") + && next_token.identifier + ) { + if (!option.getset) { + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + next_token.id; + name = next_token; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (next_token.id === "}" || next_token.id === ",") { + if (typeof extra === "string") { + advance("("); + } + value = expression(Infinity, true); + } else if (next_token.id === "(") { + value = do_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + advance("("); + } + let the_colon = next_token; + advance(":"); + value = expression(0); + if (value.id === name.id && value.id !== "function") { + warn("unexpected_a", the_colon, ": " + name.id); + } + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + advance(":"); + value = expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (next_token.id === ",") { + advance(","); + return member(); + } + }()); + } + advance("}"); + return the_brace; +}); + +stmt(";", function () { + warn("unexpected_a", token); + return token; +}); +stmt("{", function () { + warn("naked_block", token); + return block("naked"); +}); +stmt("break", function () { + const the_break = token; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (next_token.identifier && token.line === next_token.line) { + the_label = functionage.context[next_token.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + warn( + (the_label !== undefined && the_label.dead) + ? "out_of_scope_a" + : "not_label_a" + ); + } else { + the_label.used += 1; + } + the_break.label = next_token; + advance(); + } + advance(";"); + return the_break; +}); + +function do_var() { + const the_statement = token; + const is_const = the_statement.id === "const"; + the_statement.names = []; + +// A program may use var or let, but not both. + + if (!is_const) { + if (var_mode === undefined) { + var_mode = the_statement.id; + } else if (the_statement.id !== var_mode) { + warn( + "expected_a_b", + the_statement, + var_mode, + the_statement.id + ); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + warn("var_switch", the_statement); + } + if (functionage.loop > 0 && the_statement.id === "var") { + warn("var_loop", the_statement); + } + (function next() { + if (next_token.id === "{" && the_statement.id !== "var") { + const the_brace = next_token; + advance("{"); + (function pair() { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + survey(name); + advance(); + if (next_token.id === ":") { + advance(":"); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + next_token.label = name; + the_statement.names.push(next_token); + enroll(next_token, "variable", is_const); + advance(); + the_brace.open = true; + } else { + the_statement.names.push(name); + enroll(name, "variable", is_const); + } + name.dead = false; + name.init = true; + if (next_token.id === "=") { + advance("="); + name.expression = expression(); + the_brace.open = true; + } + if (next_token.id === ",") { + advance(","); + return pair(); + } + }()); + advance("}"); + advance("="); + the_statement.expression = expression(0); + } else if (next_token.id === "[" && the_statement.id !== "var") { + const the_bracket = next_token; + advance("["); + (function element() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + advance(); + the_statement.names.push(name); + enroll(name, "variable", is_const); + name.dead = false; + name.init = true; + if (ellipsis) { + name.ellipsis = true; + } else { + if (next_token.id === "=") { + advance("="); + name.expression = expression(); + the_bracket.open = true; + } + if (next_token.id === ",") { + advance(","); + return element(); + } + } + }()); + advance("]"); + advance("="); + the_statement.expression = expression(0); + } else if (next_token.identifier) { + const name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", is_const); + if (next_token.id === "=" || is_const) { + advance("="); + name.dead = false; + name.init = true; + name.expression = expression(0); + } + the_statement.names.push(name); + } else { + return stop("expected_identifier_a", next_token); + } + }()); + semicolon(); + return the_statement; +} + +stmt("const", do_var); +stmt("continue", function () { + const the_continue = token; + if (functionage.loop < 1 || functionage.finally > 0) { + warn("unexpected_a", the_continue); + } + not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; +}); +stmt("debugger", function () { + const the_debug = token; + if (!option.devel) { + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; +}); +stmt("delete", function () { + const the_token = token; + const the_value = expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; +}); +stmt("do", function () { + const the_do = token; + not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; +}); +stmt("export", function () { + const the_export = token; + let the_id; + let the_name; + let the_thing; + + function export_id() { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + the_id = next_token.id; + the_name = global.context[the_id]; + if (the_name === undefined) { + warn("unexpected_a"); + } else { + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a"); + } + exports[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + } + + the_export.expression = []; + if (next_token.id === "default") { + if (exports.default !== undefined) { + warn("duplicate_a"); + } + advance("default"); + the_thing = expression(0); + if ( + the_thing.id !== "(" + || the_thing.expression[0].id !== "." + || the_thing.expression[0].expression.id !== "Object" + || the_thing.expression[0].name.id !== "freeze" + ) { + warn("freeze_exports", the_thing); + } + if (next_token.id === ";") { + semicolon(); + } + exports.default = the_thing; + the_export.expression.push(the_thing); + } else { + if (next_token.id === "function") { + warn("freeze_exports"); + the_thing = statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a", the_name); + } + exports[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + warn("unexpected_a", next_token); + statement(); + } else if (next_token.id === "{") { + advance("{"); + (function loop() { + export_id(); + if (next_token.id === ",") { + advance(","); + return loop(); + } + }()); + advance("}"); + semicolon(); + } else { + stop("unexpected_a"); + } + } + module_mode = true; + return the_export; +}); +stmt("for", function () { + let first; + const the_for = token; + if (!option.for) { + warn("unexpected_a", the_for); + } + not_top_level(the_for); + functionage.loop += 1; + advance("("); + token.free = true; + if (next_token.id === ";") { + return stop("expected_a_b", the_for, "while (", "for (;"); + } + if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + return stop("unexpected_a"); + } + first = expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = expression(0); + advance(";"); + the_for.inc = expression(0); + if (the_for.inc.id === "++") { + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; +}); +stmt("function", do_function); +stmt("if", function () { + let the_else; + const the_if = token; + the_if.expression = condition(); + the_if.block = block(); + if (next_token.id === "else") { + advance("else"); + the_else = token; + the_if.else = ( + next_token.id === "if" + ? statement() + : block() + ); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + the_if.disrupt = true; + } else { + warn("unexpected_a", the_else); + } + } + } + return the_if; +}); +stmt("import", function () { + const the_import = token; + let name; + if (typeof module_mode === "object") { + warn("unexpected_directive_a", module_mode, module_mode.directive); + } + module_mode = true; + if (next_token.identifier) { + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + const names = []; + advance("{"); + if (next_token.id !== "}") { + while (true) { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (next_token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token; + if (!rx_module.test(token.value)) { + warn("bad_module_name_a", token); + } + froms.push(token.value); + semicolon(); + return the_import; +}); +stmt("let", do_var); +stmt("return", function () { + const the_return = token; + not_top_level(the_return); + if (functionage.finally > 0) { + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (next_token.id !== ";" && the_return.line === next_token.line) { + the_return.expression = expression(10); + } + advance(";"); + return the_return; +}); +stmt("switch", function () { + let dups = []; + let last; + let stmts; + const the_cases = []; + let the_disrupt = true; + const the_switch = token; + not_top_level(the_switch); + if (functionage.finally > 0) { + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token.free = true; + the_switch.expression = expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + (function major() { + const the_case = next_token; + the_case.arity = "statement"; + the_case.expression = []; + (function minor() { + advance("case"); + token.switch = true; + const exp = expression(0); + if (dups.some(function (thing) { + return are_similar(thing, exp); + })) { + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (next_token.id === "case") { + return minor(); + } + }()); + stmts = statements(); + if (stmts.length < 1) { + warn("expected_statements_a"); + return; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "break;", + artifact(next_token) + ); + } + if (next_token.id === "case") { + return major(); + } + }()); + dups = undefined; + if (next_token.id === "default") { + const the_default = next_token; + advance("default"); + token.switch = true; + advance(":"); + the_switch.else = statements(); + if (the_switch.else.length < 1) { + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + const the_last = the_switch.else[the_switch.else.length - 1]; + if (the_last.id === "break" && the_last.label === undefined) { + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; +}); +stmt("throw", function () { + const the_throw = token; + the_throw.disrupt = true; + the_throw.expression = expression(10); + semicolon(); + if (functionage.try > 0) { + warn("unexpected_a", the_throw); + } + return the_throw; +}); +stmt("try", function () { + let the_catch; + let the_disrupt; + const the_try = token; + if (functionage.try > 0) { + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (next_token.id === "catch") { + let ignored = "ignore"; + the_catch = next_token; + the_try.catch = the_catch; + advance("catch"); + if (next_token.id === "(") { + advance("("); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + if (next_token.id !== "ignore") { + ignored = undefined; + the_catch.name = next_token; + enroll(next_token, "exception", true); + } + advance(); + advance(")"); + } + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "catch", + artifact(next_token) + ); + + } + if (next_token.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; +}); +stmt("var", do_var); +stmt("while", function () { + const the_while = token; + not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; +}); +stmt("with", function () { + stop("unexpected_a", token); +}); + +ternary("?", ":"); + +// Ambulation of the parse tree. + +function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; +} + +function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all of the +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + } + }; +} + +const posts = empty(); +const pres = empty(); +const preaction = action(pres); +const postaction = action(posts); +const preamble = amble(pres); +const postamble = amble(posts); + +function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.id === "function") { + walk_statement(thing.block); + } + if (thing.arity === "pre" || thing.arity === "post") { + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + warn("unexpected_statement_a", thing); + } + postamble(thing); + } + } +} + +function walk_statement(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_statement); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + ) { + warn("unexpected_expression_a", thing); + } + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + } +} + +function lookup(thing) { + if (thing.arity === "variable") { + +// Look up the variable in the current context. + + let the_variable = functionage.context[thing.id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable === undefined) { + stack.forEach(function (outer) { + const a_variable = outer.context[thing.id]; + if ( + a_variable !== undefined + && a_variable.role !== "label" + ) { + the_variable = a_variable; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (the_variable === undefined) { + if (declared_globals[thing.id] === undefined) { + warn("undeclared_a", thing); + return; + } + the_variable = { + dead: false, + parent: global, + id: thing.id, + init: true, + role: "variable", + used: 0, + writable: false + }; + global.context[thing.id] = the_variable; + } + the_variable.closure = true; + functionage.context[thing.id] = the_variable; + } else if (the_variable.role === "label") { + warn("label_a", thing); + } + if ( + the_variable.dead + && ( + the_variable.calls === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + ) { + warn("out_of_scope_a", thing); + } + return the_variable; + } +} + +function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); +} + +function preaction_function(thing) { + if (thing.arity === "statement" && blockage.body !== true) { + warn("unexpected_a", thing); + } + stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); +} + +function bitwise_check(thing) { + if (!option.bitwise && bitwiseop[thing.id] === true) { + warn("unexpected_a", thing); + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + warn("unexpected_a", thing); + } +} + +function pop_block() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); +} + +function activate(name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.init = true; + } + } + blockage.live.push(name); +} + +function action_var(thing) { + thing.names.forEach(activate); +} + +preaction("assignment", bitwise_check); +preaction("binary", bitwise_check); +preaction("binary", function (thing) { + if (relationop[thing.id] === true) { + const left = thing.expression[0]; + const right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + warn("expected_string_a", right); + } + } else { + const value = right.value; + if (value === "null" || value === "undefined") { + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + warn("expected_type_string_a", right, value); + } + } + } + } +}); +preaction("binary", "==", function (thing) { + warn("expected_a_b", thing, "===", "=="); +}); +preaction("binary", "!=", function (thing) { + warn("expected_a_b", thing, "!==", "!="); +}); +preaction("binary", "=>", preaction_function); +preaction("binary", "||", function (thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + warn("and", thang); + } + }); +}); +preaction("binary", "(", function (thing) { + const left = thing.expression[0]; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + const parent = functionage.name.parent; + if (parent) { + const left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + && left_variable.dead + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } +}); +preaction("binary", "in", function (thing) { + warn("infix_in", thing); +}); +preaction("binary", "instanceof", function (thing) { + warn("unexpected_a", thing); +}); +preaction("binary", ".", function (thing) { + if (thing.expression.new) { + thing.new = true; + } +}); +preaction("statement", "{", function (thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; +}); +preaction("statement", "for", function (thing) { + if (thing.name !== undefined) { + const the_variable = lookup(thing.name); + if (the_variable !== undefined) { + the_variable.init = true; + if (!the_variable.writable) { + warn("bad_assignment_a", thing.name); + } + } + } + walk_statement(thing.initial); +}); +preaction("statement", "function", preaction_function); +preaction("unary", "~", bitwise_check); +preaction("unary", "function", preaction_function); +preaction("variable", function (thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } +}); + +function init_variable(name) { + const the_variable = lookup(name); + if (the_variable !== undefined) { + if (the_variable.writable) { + the_variable.init = true; + return; + } + } + warn("bad_assignment_a", name); +} + +postaction("assignment", "+=", function (thing) { + let right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } +}); +postaction("assignment", function (thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + if (thing.id === "=") { + if (thing.names !== undefined) { + if (Array.isArray(thing.names)) { + thing.names.forEach(init_variable); + } else { + init_variable(thing.names); + } + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.writable !== true) { + warn("bad_assignment_a", lvalue); + } + } + const right = syntax[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + warn("unexpected_a", thing.expression[1]); + } + } +}); + +function postaction_function(thing) { + delete functionage.finally; + delete functionage.loop; + delete functionage.switch; + delete functionage.try; + functionage = stack.pop(); + if (thing.wrapped) { + warn("unexpected_parens", thing); + } + return pop_block(); +} + +postaction("binary", function (thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || are_similar(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option.convert) { + if (thing.expression[0].value === "") { + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === "." || thing.id === "?.") { + if (thing.expression.id === "RegExp") { + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } +}); +postaction("binary", "&&", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "||", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "=>", postaction_function); +postaction("binary", "(", function (thing) { + let left = thing.expression[0]; + let the_new; + let arg; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option.eval) { + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + warn( + "expected_a_before_b", + left, + "new", + artifact(left) + ); + } + } + } else if (left.id === ".") { + let cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + cack = !cack; + } + if (rx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + warn("unexpected_a", the_new); + } else { + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + const paren = left.expression; + if (paren.id === "(") { + const array = paren.expression; + if (array.length === 1) { + const new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } +}); +postaction("binary", "[", function (thing) { + if (thing.expression[0].id === "RegExp") { + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + warn("weird_expression_a", thing.expression[1]); + } +}); +postaction("statement", "{", pop_block); +postaction("statement", "const", action_var); +postaction("statement", "export", top_level_only); +postaction("statement", "for", function (thing) { + walk_statement(thing.inc); +}); +postaction("statement", "function", postaction_function); +postaction("statement", "import", function (the_thing) { + const name = the_thing.name; + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return top_level_only(the_thing); +}); +postaction("statement", "let", action_var); +postaction("statement", "try", function (thing) { + if (thing.catch !== undefined) { + const the_name = thing.catch.name; + if (the_name !== undefined) { + const the_variable = functionage.context[the_name.id]; + the_variable.dead = false; + the_variable.init = true; + } + walk_statement(thing.catch.block); + } +}); +postaction("statement", "var", action_var); +postaction("ternary", function (thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || are_similar(thing.expression[1], thing.expression[2]) + ) { + warn("unexpected_a", thing); + } else if (are_similar(thing.expression[0], thing.expression[1])) { + warn("expected_a_b", thing, "||", "?"); + } else if (are_similar(thing.expression[0], thing.expression[2])) { + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + warn("wrap_condition", thing.expression[0]); + } +}); +postaction("unary", function (thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option.convert) { + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } +}); +postaction("unary", "function", postaction_function); +postaction("unary", "+", function (thing) { + if (!option.convert) { + warn("expected_a_b", thing, "Number(...)", "+"); + } + const right = thing.expression; + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } +}); + +function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + if (id !== "ignore") { + const name = the_function.context[id]; + if (name.parent === the_function) { + if ( + name.used === 0 + && ( + name.role !== "function" + || name.parent.arity !== "unary" + ) + ) { + warn("unused_a", name); + } else if (!name.init) { + warn("uninitialized_a", name); + } + } + } + }); +} + +function uninitialized_and_unused() { + +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (module_mode === true || option.node) { + delve(global); + } + functions.forEach(delve); +} + +// Go through the token list, looking at usage of whitespace. + +function whitage() { + let closer = "(end)"; + let free = false; + let left = global; + let margin = 0; + let nr_comments_skipped = 0; + let open = true; + let opening = true; + let right; + + function pop() { + const previous = stack.pop(); + closer = previous.closer; + free = previous.free; + margin = previous.margin; + open = previous.open; + opening = previous.opening; + } + + function push() { + stack.push({ + closer, + free, + margin, + open, + opening + }); + } + + function expected_at(at) { + warn( + "expected_a_at_b_c", + right, + artifact(right), + fudge + at, + artifact_column(right) + ); + } + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function no_space() { + if (left.line === right.line) { + if (left.thru !== right.from && nr_comments_skipped === 0) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (open) { + const at = ( + free + ? margin + : margin + 8 + ); + if (right.from < at) { + expected_at(at); + } + } else { + if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line || !open) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (right.from !== margin) { + expected_at(margin); + } + } + } + + stack = []; + tokens.forEach(function (the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + + const new_closer = opener[left.id]; + if (typeof new_closer === "string") { + if (new_closer !== right.id) { + opening = left.open || (left.line !== right.line); + push(); + closer = new_closer; + if (opening) { + free = closer === ")" && left.free; + open = true; + margin += 4; + if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (right.switch) { + at_margin(-4); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + free = false; + open = false; + no_space_only(); + } + } else { + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like '{]') have already been detected. + + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } + } else { + if (right.statement === true) { + if (left.id === "else") { + one_space_only(); + } else { + at_margin(0); + open = false; + } + +// If right is a closer, then pop the previous state. + + } else if (right.id === closer) { + pop(); + if (opening && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-4); + } else if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + one_space(); + } else { + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + at_margin(0); + } else { + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + no_space(); + } else if ( + left.id === "." + || left.id === "?." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + ) { + no_space_only(); + } else if (right.id === "." || right.id === "?.") { + no_space_only(); + } else if (left.id === ";") { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + one_space_only(); + } else if ( + left.id === "var" + || left.id === "const" + || left.id === "let" + ) { + push(); + closer = ";"; + free = false; + open = left.open; + if (open) { + margin = margin + 4; + at_margin(0); + } else { + one_space_only(); + } + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +// The jslint function itself. + +export default Object.freeze(function jslint( + source = "", + option_object = empty(), + global_array = [] +) { + try { + warnings = []; + option = Object.assign(empty(), option_object); + anon = "anonymous"; + block_stack = []; + declared_globals = empty(); + directive_mode = true; + directives = []; + early_stop = true; + exports = empty(); + froms = []; + fudge = ( + option.fudge + ? 1 + : 0 + ); + functions = []; + global = { + id: "(global)", + body: true, + context: empty(), + from: 0, + level: 0, + line: 0, + live: [], + loop: 0, + switch: 0, + thru: 0 + }; + blockage = global; + functionage = global; + json_mode = false; + mega_mode = false; + module_mode = false; + next_token = global; + property = empty(); + shebang = false; + stack = []; + tenure = undefined; + token = global; + token_nr = 0; + var_mode = undefined; + populate(standard, declared_globals, false); + populate(global_array, declared_globals, false); + Object.keys(option).forEach(function (name) { + if (option[name] === true) { + const allowed = allowed_option[name]; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } + }); + tokenize(source); + advance(); + if (json_mode) { + tree = json_value(); + advance("(end)"); + } else { + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option.browser) { + if (next_token.id === ";") { + advance(";"); + } + } else { + +// If we are not in a browser, then the file form of strict pragma may be used. + + if ( + next_token.value === "use strict" + ) { + advance("(string)"); + advance(";"); + } + } + tree = statements(); + advance("(end)"); + functionage = global; + walk_statement(tree); + if (warnings.length === 0) { + uninitialized_and_unused(); + if (!option.white) { + whitage(); + } + } + } + if (!option.browser) { + directives.forEach(function (comment) { + if (comment.directive === "global") { + warn("missing_browser", comment); + } + }); + } + early_stop = false; + } catch (e) { + if (e.name !== "JSLintError") { + warnings.push(e); + } + } + return { + directives, + edition: "2019-09-17", + exports, + froms, + functions, + global, + id: "(JSLint)", + json: json_mode, + lines, + module: module_mode === true, + ok: warnings.length === 0 && !early_stop, + option, + property, + shebang: ( + shebang + ? lines[0] + : undefined + ), + stop: early_stop, + tokens, + tree, + warnings: warnings.sort(function (a, b) { + return a.line - b.line || a.column - b.column; + }) + }; +}); diff --git a/branch.biginit/report.js b/branch.biginit/report.js new file mode 100644 index 000000000..5e487d143 --- /dev/null +++ b/branch.biginit/report.js @@ -0,0 +1,222 @@ +// report.js +// 2018-10-22 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Generate JSLint HTML reports. + +/*property + closure, column, context, edition, error, exports, filter, forEach, freeze, + froms, fudge, function, functions, global, id, isArray, join, json, keys, + length, level, line, lines, message, module, name, names, option, + parameters, parent, property, push, replace, role, signature, sort, stop, + warnings +*/ + +const rx_amp = /&/g; +const rx_gt = />/g; +const rx_lt = / with less destructive entities. + + return String( + string + ).replace( + rx_amp, + "&" + ).replace( + rx_lt, + "<" + ).replace( + rx_gt, + ">" + ); +} + +export default Object.freeze({ + error: function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + let fudge = Number(Boolean(data.option.fudge)); + let output = []; + if (data.stop) { + output.push("
    JSLint was unable to finish.
    "); + } + data.warnings.forEach(function (warning) { + output.push( + "
    ", + entityify(warning.line + fudge), + ".", + entityify(warning.column + fudge), + "
    ", + entityify(warning.message), + "
    ", + entityify(data.lines[warning.line] || ""), + "" + ); + }); + return output.join(""); + }, + + function: function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + let fudge = Number(Boolean(data.option.fudge)); + let mode = ( + data.module + ? "module" + : "global" + ); + let output = []; + + if (data.json) { + return ( + data.warnings.length === 0 + ? "
    JSON: good.
    " + : "
    JSON: bad.
    " + ); + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + let global = Object.keys(data.global.context).sort(); + let froms = data.froms.sort(); + let exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + let context = the_function.context; + let list = Object.keys(context); + output.push( + "
    ", + entityify(the_function.line + fudge), + "
    ", + ( + the_function.name === "=>" + ? entityify(the_function.signature) + " =>" + : ( + typeof the_function.name === "string" + ? "«" + entityify(the_function.name) + "»" + : "" + entityify(the_function.name.id) + "" + ) + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + let params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.parent === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.parent === the_function + ); + })); + detail("outer", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.parent !== the_function + && the_variable.parent.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].parent.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + output.push( + "
    JSLint edition ", + entityify(data.edition), + "
    " + ); + return output.join(""); + }, + + property: function property_directive(data) { + +// Produce the /*property*/ directive. + + let not_first = false; + let output = ["/*property"]; + let length = 1111; + let properties = Object.keys(data.property); + + if (properties.length > 0) { + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length >= 80) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); + } + } +}); diff --git a/branch.bug_ternary_use_open/README b/branch.bug_ternary_use_open/README new file mode 100644 index 000000000..f46e91069 --- /dev/null +++ b/branch.bug_ternary_use_open/README @@ -0,0 +1,42 @@ +JSLint, The JavaScript Code Quality Tool + +Douglas Crockford +douglas@crockford.com + +2018-05-28 + +jslint.js contains the jslint function. It parses and analyzes a source file, +returning an object with information about the file. It can also take an object +that sets options. + +index.html runs the jslint.js function in a web page. The page also depends +browser.js and report.js and jslint.css. + +jslint.css provides styling for index.html. + +browser.js runs the web user interface. + +report.js generates the results reports in HTML. + +help.html describes JSLint's usage. Please read it. + +function.html describes the jslint function and the results it produces. + +Please direct questions and comments to +https://plus.google.com/communities/104441363299760713736. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[http://www.crockford.com/wrrrld/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. diff --git a/branch.bug_ternary_use_open/browser.js b/branch.bug_ternary_use_open/browser.js new file mode 100644 index 000000000..5086fed85 --- /dev/null +++ b/branch.bug_ternary_use_open/browser.js @@ -0,0 +1,158 @@ +// browser.js +// 2018-06-16 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +/*jslint + browser +*/ + +/*property + checked, create, disable, display, error, focus, forEach, function, + getElementById, innerHTML, join, length, map, onchange, onclick, onscroll, + property, querySelectorAll, scrollTop, select, split, style, title, value +*/ + +import jslint from "./jslint.js"; +import report from "./report.js"; + +// This is the web script companion file for JSLint. It includes code for +// interacting with the browser and displaying the reports. + +let rx_crlf = /\n|\r\n?/; +let rx_separator = /[\s,;'"]+/; + +let fudge_unit = 0; + +const aux = document.getElementById("JSLINT_AUX"); +const boxes = document.querySelectorAll("[type=checkbox]"); +const fudge = document.getElementById("JSLINT_FUDGE"); +const global = document.getElementById("JSLINT_GLOBAL"); +const number = document.getElementById("JSLINT_NUMBER"); +const property = document.getElementById("JSLINT_PROPERTY"); +const property_fieldset = document.getElementById("JSLINT_PROPERTYFIELDSET"); +const report_field = document.getElementById("JSLINT_REPORT"); +const report_list = document.getElementById("JSLINT_REPORT_LIST"); +const source = document.getElementById("JSLINT_SOURCE"); +const select = document.getElementById("JSLINT_SELECT"); +const warnings = document.getElementById("JSLINT_WARNINGS"); +const warnings_list = document.getElementById("JSLINT_WARNINGS_LIST"); + +function show_numbers() { + number.value = source.value.split(rx_crlf).map(function (ignore, index) { + return index + fudge_unit; + }).join("\n"); +} + +function clear() { + aux.style.display = "none"; + number.value = ""; + property.value = ""; + property_fieldset.style.display = "none"; + report_field.style.display = "none"; + report_list.innerHTML = ""; + source.focus(); + source.value = ""; + warnings.style.display = "none"; + warnings_list.innerHTML = ""; +} + +function fudge_change() { + fudge_unit = Number(fudge.checked); + show_numbers(); +} + +function clear_options() { + boxes.forEach(function (node) { + node.checked = false; + }); + fudge_change(); + global.innerHTML = ""; +} + +function call_jslint() { + +// First build the option object. + + let option = Object.create(null); + boxes.forEach(function (node) { + if (node.checked) { + option[node.title] = true; + } + }); + +// Call JSLint with the source text, the options, and the predefined globals. + + let global_string = global.value; + let result = jslint( + source.value, + option, + ( + global_string === "" + ? undefined + : global_string.split(rx_separator) + ) + ); + +// Generate the reports. + + let error_html = report.error(result); + let function_html = report.function(result); + let property_text = report.property(result); + +// Display the reports. + + warnings_list.innerHTML = error_html; + warnings.style.display = ( + error_html.length === 0 + ? "none" + : "block" + ); + + report_list.innerHTML = function_html; + report_field.style.display = "block"; + if (property_text) { + property.value = property_text; + property_fieldset.style.display = "block"; + property.scrollTop = 0; + select.disable = false; + } else { + property_fieldset.style.display = "none"; + select.disable = true; + } + aux.style.display = "block"; + source.select(); +} + +fudge.onchange = fudge_change; + +source.onchange = function (ignore) { + show_numbers(); +}; + +source.onscroll = function () { + let ss = source.scrollTop; + number.scrollTop = ss; + let sn = number.scrollTop; + if (ss > sn) { + show_numbers(); + number.scrollTop = ss; + } +}; + +document.querySelectorAll("[name='JSLint']").forEach(function (node) { + node.onclick = call_jslint; +}); + +document.querySelectorAll("[name='clear']").forEach(function (node) { + node.onclick = clear; +}); + +document.getElementById("JSLINT_SELECT").onclick = function () { + property.focus(); + property.select(); +}; +document.getElementById("JSLINT_CLEAR_OPTIONS").onclick = clear_options; + +fudge_change(); +source.select(); +source.focus(); diff --git a/branch.bug_ternary_use_open/cli.js b/branch.bug_ternary_use_open/cli.js new file mode 100755 index 000000000..a220c2547 --- /dev/null +++ b/branch.bug_ternary_use_open/cli.js @@ -0,0 +1,130 @@ +#!/usr/bin/env node +/* + * cli.js + * simple cli-tool to run jslint.js from command-line + * + * instruction: + * 1. save this file as cli.js + * 2. download and save in same directory, latest jslint.js from + * https://github.com/douglascrockford/JSLint/blob/master/jslint.js + * + * example usage: + * $ node cli.js ./jslint.js // jslint local-file + * $ node cli.js https://code.jquery.com/jquery-1.0.0.js // jslint remote-file + */ + + + +/*jslint node */ +/*property + argv, basename, column, concat, end, error, exit, filter, forEach, fudge, + join, jslint, line, lines, main, match, message, on, option, parse, push, + readFile, replace, request, runInNewContext, slice, trim, trimRight, + warnings +*/ +"use strict"; +let chunk_list; +let file; +let fs; +let fudge; +let local; +let modeNext; +let onNext; +let path; +let url; +let vm; +let warning_text; +onNext = function (error, data) { + if (error) { + console.error(error.message || error); + process.exit(1); + } + modeNext += 1; + switch (modeNext) { + case 1: + // run this program only in cli-mode + if (module !== require.main) { + console.error( + "jslint-cli can only be run from the command-line" + ); + return; + } + // require builtins + fs = require("fs"); + path = require("path"); + url = require("url"); + vm = require("vm"); + // init local namespace + local = {}; + if (!process.argv[2]) { + console.error( + "error: no specified\n" + + "usage: " + path.basename(__filename) + " \n" + + "example: " + path.basename(__filename) + " example.js\n" + + "example: " + path.basename(__filename) + + " https://jslint.com/jslint.js" + ); + process.exit(1); + } + // init jslint + fs.readFile(path.join(__dirname, "jslint.js"), "utf8", onNext); + break; + case 2: + // bug-workaround - nodejs does not widely support es-modules + vm.runInNewContext( + data.replace("export default function", "function"), + local + ); + // read data from file + file = process.argv[2]; + console.error("jslint " + file); + data = file.match(/^(https?):\/\//); + if (!data) { + fs.readFile(file, "utf8", onNext); + return; + } + // read data from http/https url + require(data[1]).request(url.parse(file), function (response) { + chunk_list = []; + response + .on("data", function (chunk) { + chunk_list.push(chunk); + }) + .on("end", function () { + onNext(null, String(Buffer.concat(chunk_list))); + }) + .on("error", onNext); + }).on("error", onNext).end(); + break; + case 3: + // jslint data + data = local.jslint(data + // ignore first-line shebang in nodejs scripts + .replace((/^#!/), "//")); + fudge = data.option.fudge || 0; + // init warning_text + warning_text = ""; + // print warnings to stderrr + data.warnings + .filter(function (warning) { + return warning && warning.message; + }) + // print only first 10 warnings + .slice(0, 10) + .forEach(function (warning, ii) { + warning_text += ( + (" #" + (ii + 1)).slice(-3) + + " \u001b[31m" + warning.message + "\u001b[39m\n" + + " " + String(data.lines[warning.line] || "").trim() + + "\u001b[90m \/\/ line " + + (warning.line + fudge) + + ", col " + (warning.column + fudge) + + "\u001b[39m\n" + ); + }); + console.error(warning_text.trimRight() || "ok"); + break; + } +}; +modeNext = 0; +onNext(); diff --git a/branch.bug_ternary_use_open/function.html b/branch.bug_ternary_use_open/function.html new file mode 100644 index 000000000..7d8271246 --- /dev/null +++ b/branch.bug_ternary_use_open/function.html @@ -0,0 +1,618 @@ + + + + + + + + +JSLint: jslint function + + + +
    JSLint
    + +
    The jslint Function
    +
    +

    JSLint

    +

    JSLint is delivered as a set of files:

    +
    + + + + + + + + + + + + + + +
    FilenameContent
    browser.jsBrowser based UI.
    function.htmlThis page that you are looking at right now.
    help.htmlUsing jslint as a single page JSLint application.
    index.htmlSingle page JSLint application that uses the + js files.
    jslint.jsThe jslint function.
    report.jsThe report object, containing report generator functions for + HTML.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    +
    + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring"(JSLint)"
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    parenttokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    parenttokenThe function in which the variable was declared.variable
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    shebangstringThe first line of text if it started with #!line
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable can be assigned to.variable
    +

    Reports

    +

    The report object contains three functions that all take a + result from the jslint function as input. + report.js has no other dependence on other files.

    + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    +
    + +
    + + + + + +
    + + diff --git a/branch.bug_ternary_use_open/help.html b/branch.bug_ternary_use_open/help.html new file mode 100644 index 000000000..94b431214 --- /dev/null +++ b/branch.bug_ternary_use_open/help.html @@ -0,0 +1,835 @@ + + + + + + + + + +JSLint: Help + + + +
    JSLint
    + +
    Help
    +
    +
    Il semble que la perfection soit atteinte non quand il n’y a + plus rien à ajouter, mais quand il n’y a plus rien à + retrancher.
    +
    + Antoine de Saint-Exupéry +
    +
    + Terre des Hommes (1939) +
    +

    Good Parts

    +

    The Principle of the Good Parts is

    +
    If a feature is sometimes useful and sometimes dangerous and if + there is a better option then always use the better option.
    +

    JSLint is a JavaScript program that looks for problems in + JavaScript programs. It is a code quality tool.

    +

    When C was + a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    +

    As the language matured, the definition of the language was + strengthened to eliminate some insecurities, and compilers got better + at issuing warnings. lint is no longer needed.

    +

    JavaScript is a + young-for-its-age language. It was originally intended to do small tasks in + webpages, tasks for which Java was too heavy and clumsy. But JavaScript is + a surprisingly capable language, and it is now being used in larger + projects. Many of the features that were intended to make the language easy + to use are troublesome when projects become complicated. A lint + for JavaScript is needed: JSLint, a JavaScript syntax checker + and validator.

    +

    JSLint takes a JavaScript source and scans it. If it finds a + problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by the ECMAScript Programming Language + Standard (the strangely named document that governs JavaScript). + JSLint will reject most legal programs. It is a higher + standard.

    +

    JavaScript is a sloppy language, but hidden deep inside there is an elegant, + better language. JSLint helps you to program in that better + language and to avoid most of the slop. JSLint will reject + programs that browsers will accept because JSLint is concerned + with the quality of your code and browsers are not. You should gladly accept + all of JSLint's advice.

    +

    JSLint can operate on JavaScript source + or JSON text.

    +

    ECMAScript Sixth Edition

    +

    Some of + ES6’s features are good, so JSLint will recognize the good + parts of ES6.

    +

    Currently, these features are recognized:

    +
      +
    • The ... ellipsis marker in parameter + lists and argument lists, replacing the arguments object + for variadic functions.
    • +
    • The let statement, which is like the var + statement except that it respects block scope. + You may use let or var but not both.
    • +
    • The const statement is like the let statement + except that it disallows the use of assignment on the variable, although + if the value of the variable is mutable, it can still be mutated. + const is preferred to let. +
    • Destructuring of arrays and objects is allowed in parameter lists and on + the left side of let, and const, but not + var or assignment statements, and not deep destructuring + or eliding.
    • +
    • Enhanced object literals, providing shorter forms for function + declaration and properties that are initialized by variables with the + same name.
    • +
    • The fat arrow => fart functions.
    • +
    • The simplest forms of import and export.
    • +
    • `Megastring` literals, but not nested + `megastring` literals.
    • +
    • New global functions, such as Map, Set, + WeakMap, and WeakSet.
    • +
    • 0b- and 0o- number literals.
    • +
    +

    The most important new feature of ES6 is proper tail calls. This has no + new syntax, so JSLint doesn’t see it. But it makes recursion + much more attractive, which makes loops, particularly for + loops, much less attractive.

    +

    import export

    +

    The ES6 module feature will be an important improvement over JavaScript’s + global variables as a means of linking separate files together. + JSLint recognizes a small but essential subset of the module + syntax.

    +
    +

    import name from stringliteral;

    +

    import {name} from stringliteral;

    +

    export default function () {};

    +

    export default function name() {};

    +

    export default expression;

    +

    export function name() {}

    +

    export name; // where name is const

    +

    export {name};

    +
    +

    Directives

    +

    JSLint provides three directives that may be placed in a + file to manage JSLint’s behavior. Use of these directives is + optional. If they are used, they should be placed in a source file before + the first statement. They are written in the form of a comment, where the + directive name is placed immediately after the opening of the comment before + any whitespace. The three directives are global, + jslint, and property. Directives in a file are + stronger than options selected from the UI or passed with the option object.

    +

    /*global*/

    +

    The /*global*/ directive is used to specify a set of globals + (usually functions and objects containing functions) that are available to + this file. This was commonly used in browsers to link source files together + before ES6 modules appeared. Use of global variables is strongly + discouraged, but unfortunately web browsers require their use. The + /*global*/ directive can only be used when the + Assume a browser option is selected. +

    Each of the names listed will indicate a read-only global variable. The names + are separated by , comma. This directive + should not be used if import or export is used. + This directive inhibits warnings, but it does not declare the names in the + execution environment. +

    For example: +

    /*global +
    ADSAFE, report, jslint
    +*/
    +

    instructs JSLint to not give warnings about the global + variables ADsafe, report, and jslint. + However, if any of those names are expected to be supplied by other files + and those other files fail to do so, then execution errors will result. It + is usually better to use top-level variable declarations instead: +

        var ADSAFE;
    +    var report;
    +    var jslint; 
    +

    Using var in this way allows comparing a global variable to the + undefined value to determine whether it is has been used in the global context.

    +

    /*jslint*/

    +

    The /*jslint*/ directive allows for the control of several + options. These options can also be set from the + JSLint.com user interface.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Tolerate bitwise operatorsbitwisetrue if bitwise operators should be allowed. The + bitwise operators are rarely used in JavaScript programs, so it + is usually more likely that & is a mistyping of + && than that it indicates a bitwise and. + JSLint will give warnings on the bitwise operators + unless this option is selected.
    Assume a browser browsertrue if the standard browser globals should be + predefined. This option will reject the use of + import and export. This option also + disallows the file form of the "use + strict" pragma. It does not supply + self; you will have to request that unnecessary + alias of the dreaded global object yourself. It adds the same + globals as this directive: +
    /*global +
    + clearInterval, clearTimeout, document, event, FileReader, + FormData, history, localStorage, location, name, navigator, + screen, sessionStorage, setInterval, setTimeout, Storage, + URL, window, XMLHttpRequest +
    + */
    +
    Tolerate conversion operatorsconverttrue if !!, + prefix, + and concatenation with "" are allowed. + The Boolean, Number, and + String functions are preferred
    Assume CouchDBcouchtrue if + Couch DB + globals should be predefined. It adds the same globals as this + directive: +
    /*global +
    emit, getRow, isArray, log, provides, registerType, require, + send, start, sum, toJSON
    + */
    Assume in developmentdeveltrue if browser globals that are useful in + development should be predefined, and if debugger + statements and TODO comments + should be allowed. It adds the + same globals as this directive: +
    /*global +
    alert, confirm, console, prompt
    + */
    Be sure to turn this option off before going + into production.
    Tolerate evalevaltrue if eval should be allowed. In the + past, the eval function was the most misused + feature of the language.
    Tolerate for statementfortrue if the for statement should be + allowed. It is almost always better to use the array methods + instead.
    Fudge the line and column numbersfudgetrue if the origin of a text file is line 1 column + 1. Programmers know that number systems start at zero. + Unfortunately, most text editors start numbering lines and + columns at one. JSLint will by default start + numbering at zero. Use this option to start the numbering at one + in its reports.
    Tolerate get and setgetsettrue if accessor properties are allowed in object literals.
    Tolerate long source lineslongtrue if a line can contain more than 80 characters.
    Tolerate multiple variables declarations per statementmultivartrue if a var, let, or + const statement can declare two or more variables + in a single statement.
    Assume Node.jsnodetrue if Node.js globals should be predefined. It + will predefine globals that are used in the Node.js environment. + It adds the same globals as this directive: +
    /*global +
    + Buffer, clearImmediate, clearInterval, clearTimeout, console, exports, + module, process, require, setImmediate, setInterval, setTimeout, URL, + URLSearchParams, __dirname, __filename +
    + */
    Tolerate single quote stringssingletrue if 'single quote + should be allowed to enclose string literals.
    Tolerate this
    thistrue if this should be allowed.
    Tolerate whitespace messwhitetrue if the whitespace rules should be ignored.
    +

    For example:

    +
    /*jslint
    +    bitwise, node
    +*/
    + +

    /*property*/

    +

    The /*property*/ directive is used to declare a list of + property identifiers that are used in the file. Each property name in the + program is looked up in this list. If a name is not found, that indicates an + error, most likely a typing error.

    +

    The list can also be used to evade some of JSLint’s naming + rules.

    +

    JSLint can build the /*property*/ list for you. At + the bottom of its report, JSLint displays a + /*property*/ directive. It contains all of the names that were + used with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. You + can copy the /*property*/ directive to the top of your + script file. JSLint will check the spelling of all property + names against the list. That way, you can have JSLint look for + misspellings for you.

    +

    For example,

    +
    /*property +
    charAt, slice, _$_
    + */
    +

    ignore

    +

    JSLint introduces a new reserved word: ignore. It + is used in parameter lists and in catch clauses to indicate a + parameter that will be ignored. Unused warnings will not be produced for + ignore. +

    function handler(ignore, value) {
    +    return do_something_useful(value);
    +}
    +

    var let const

    +

    JavaScript provides three statements for declaring variables: + var, let, and const. The + var statement suffers from a bad practice called hoisting. The + let statement does not do hoisting and respects block scope. + The const statement is like let except that it + marks the variable (but not its contents) as read only, making it an error + to attempt to assign to the variable. When given a choice, + const is the best, var is the worst.

    +

    JSLint uses the intersection of the var rules and the + let rules, and by doing so avoids the errors related to either. + A name should be declared only once in a function. It should be declared + before it is used. It should not be used outside of the block in which it is + declared. A variable should not have the same name as a variable or + parameter in an outer function. Do not mix var and + let. Declare one name per statement.

    +

    = == ===

    +

    Fortran made a terrible + mistake in using the equality operator as its assignment operator. That + mistake has been replicated in most languages since then. C compounded that + mistake by making == its equality operator. The visual + similarity is a source of errors. JavaScript compounded this further by + making == a type coercing comparison operator that produces + false positive results. This was mitigated by adding the === + operator, leaving the broken == operator in place.

    +

    JSLint attempts to minimize errors by the following rules:

    +

    == is not allowed. This avoids the false positives, and + increases the visual distance between = and ===. + Assignments are not allowed in expression position, and comparisons are not + allowed in statement position. This also reduces confusion. 

    +

    Semicolon

    +

    JavaScript uses a C-like syntax that requires the use of semicolons to + delimit certain statements. JavaScript attempts to make those semicolons + optional with an automatic semicolon insertion mechanism, but it does not + work very well. Automatic semicolon insertion was added to make + things easier for beginners. Unfortunately, it sometimes fails. Do not rely + on it unless you are a beginner.

    +

    JSLint expects that every statement will be followed by + ; except for for, function, + if, switch, try, and + while. JSLint does not expect to see unnecessary + semicolons, the empty statement, or empty blocks.

    +

    function =>

    +

    JavaScript has four syntactic forms for making function objects: function + statements, function expressions, enhanced object literals, and the + => fart operator.

    +

    The function statement creates a variable and assigns the function object to + it. It should be used in a file or function body, but not inside of a block.

    +
    function name(parameters) { +
    statements
    + }
    +

    The function expression unfortunately looks like the function statement. It + may appear anywhere that an expression may appear, but not in statement + position and not in a loop. It produces a function object but does not + create a variable in which to store it.

    +
    function (parameters) { +
    statements
    + }
    +

    The enhanced object literal provides an ES6 shorthand for creating a property + whose value is a function, saving you from having to type + : colon and function.

    +
    { +
    name(parameters) { +
    statements
    + }
    + }
    +

    Finally, ES6 provides an even shorter form of function expression that leaves + out the words function and return:

    +
    (parameters) => + expression
    +

    JSLint requires the parens around the parameters, and forbids a + { left brace after the + => fart to avoid syntactic ambiguity.

    +

    Comma

    +

    The , comma operator is unnecessary and can + mask programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as + an operator. It does not expect to see elided elements in array literals. A + comma should not appear after the last element of an array literal or object + literal.

    +

    Blocks

    +

    JSLint expects blocks with function, + if, switch, while, for, + do, and try statements and nowhere else.

    +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    +

    JavaScript allows an if to be written like this:

    +
    if (condition)
    +    statement;
    +

    That form is known to contribute to mistakes in projects where many + programmers are working on the same code. That is why JSLint + expects the use of a block:

    +
    if (condition) {
    +    statements;
    +}
    +

    Experience shows that this form is more resilient.

    +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call. All other expression statements are considered + to be errors.

    +

    for

    +

    JSLint does not recommend use of the for statement. + Use array methods like forEach instead. The for + option will suppress some warnings. The forms of for that + JSLint accepts are restricted, excluding the new ES6 forms.

    +

    for in

    +

    JSLint does not recommend use of the for + in statement. Use Object.keys instead.

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, it also + loops through all of the properties that were inherited through the + prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written + without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    for (name in object) {
    +    if (object.hasOwnProperty(name)) {
    +        ....
    +    }
    +}
    +

    Note that the above code will fail if the object contains a property + named hasOwnProperty. Use Object.keys instead.

    +

    switch

    +

    A common error in switch statements is to forget to place a + break statement after each case, resulting in unintended + fall-through. JSLint expects that the statement before the next + case or default is one of these: + break, return, or throw.

    +

    with

    +

    The with statement was intended to provide a shorthand in + accessing properties in deeply nested objects. Unfortunately, it behaves + very badly when setting new properties. Never use the with + statement.

    +

    JSLint does not expect to see a with statement.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that + labels will be distinct from vars and parameters.

    +

    Unreachable Code

    +

    JSLint expects that a return, break, + or throw statement will be followed by a + } right brace or case or + default.

    +

    Confusing Pluses and Minuses

    +

    JSLint expects that + will not be followed by + + or ++, and that - will not be + followed by - or --. A misplaced space can turn + + + into ++, an error that is difficult to see. + Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ increment and + -- decrement operators have been known to + contribute to bad code by encouraging excessive trickiness. They are second + only to faulty architecture in enabling to viruses and other security + menaces. Also, preincrement/postincrement confusion can produce off-by-one + errors that are extremely difficult to diagnose. Fortunately, they are also + complete unnecessary. There are better ways to add 1 to a variable.

    +

    It is best to avoid these operators entirely and rely on += and + -= instead.

    +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. + JSLint looks for problems that may cause portability problems. + It also attempts to resolve visual ambiguities by recommending explicit + escapement.

    +

    JavaScript’s syntax for regular expression literals overloads the + / slash character. To avoid ambiguity, + JSLint expects that the character preceding a regular + expression literal is a ( left paren or + = equal or + : colon or + , comma character.

    +

    this

    +
    Having this in the language makes it harder to talk + about the language. It is like pair programming with Abbott and Costello. +
    +

    Avoid using this. Warnings about this can be + suppressed with option.this.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the + new prefix. The new prefix creates a new object + based on the function's prototype, and binds that object to the + function's implied this parameter. If you neglect to use the + new prefix, no new object will be made and this + will be bound to the global object.

    +

    JSLint enforces the convention that constructor functions be + given names with initial uppercase. JSLint does not expect to + see a function invocation with an initial uppercase name unless it has the + new prefix. JSLint does not expect to see the + new prefix used with functions whose names do not start with + initial uppercase.

    +

    JSLint does not expect to see the wrapper forms + new Number, new String, new Boolean.

    +

    JSLint does not expect to see new Object. + Use Object.create(null) instead.

    +

    Whitespace

    +

    JSLint has a specific set of rules about the use of whitespace. + Where possible, these rules are consistent with centuries of good practice + with literary style.

    +

    The indentation increases by 4 spaces when the last token on a line is + { left brace, + [ left bracket, + ( left paren. The matching closing + token will be the first token on a line, restoring the previous + indentation.

    +

    The ternary operator can be visually confusing, so + ? question mark and + : colon always begin a line and increase + the indentation by 4 spaces.

    +
    return (
    +    (the_token.id === "(string)" || the_token.id === "(number)")
    +    ? String(the_token.value)
    +    : the_token.id
    +);
    +

    The word function is always followed with one space.

    +

    Clauses (case, catch, default, + else, finally) are not statements and so should + not be indented like statements.

    +

    Spaces are used to make things that are not invocations look less like + invocations.

    +

    Tabs and spaces should not be mixed. We should pick just one in order to + avoid the problems that come from having both. Personal preference is an + extremely unreliable criterion. Neither offers a powerful advantage over the + other. Fifty years ago, tab had the advantage of consuming less memory, but + Moore's Law has eliminated that advantage. Space has one clear advantage + over tab: there is no reliable standard for how many spaces a tab + represents, but it is universally accepted that a space occupies a space. So + use spaces. You can edit with tabs if you must, but make sure it is spaces + again before you commit. Maybe someday we will finally get a universal + standard for tabs, but until that day comes, the better choice is spaces.

    +

    Unsafe Characters

    +

    There are characters that are handled inconsistently in some browsers, and + so must be escaped when placed in strings.

    +
    \u0000-\u001f
    +\u007f-\u009f
    +\u00ad
    +\u0600-\u0604
    +\u070f
    +\u17b4
    +\u17b5
    +\u200c-\u200f
    +\u2028-\u202f
    +\u2060-\u206f
    +\ufeff
    +\ufff0-\uffff
    +

    Report

    +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    +
      +
    • The line number on which it starts.
    • +
    • The function’s name. In the case of anonymous functions, + JSLint will attempt to + «guess» the name.
    • +
    • The parameters.
    • +
    • Variables: The variables that are declared in the function.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Exceptions: The variables that are declared by catch + clauses of try statements.
    • +
    • Outer: Variables used by this function that are declared in + other functions.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements. Updates are announced at + https://plus.google.com/communities/104441363299760713736.

    +

    Try it

    +

    Try it. Paste your script + into the window and click the + + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    JSLint is written entirely in JavaScript, so it can run + anywhere that JavaScript (or even Java) can run.

    +

    Perfectly fine

    +

    JSLint was designed to reject code that some would consider to + be perfectly fine. The reason for this is that JSLint's + purpose is to help produce programs that are free of error. That is + difficult in any language and is especially hard in JavaScript. + JSLint attempts to help you increase the visual distance + between correct programs and incorrect programs, making the remaining errors + more obvious. JSLint will give warnings about things that are + not necessarily wrong in the current situation, but which have been observed + to mask or obscure errors. Avoid those things when there are better options + available.

    +

    It is dangerous out there. JSLint is here to help.

    +

    Warning

    +

    JSLint will hurt your feelings. Side effects may include + headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, + dry mouth, cleaner code, and a reduced error rate.

    +

    Further Reading

    + +
    +

    + Code Conventions for the JavaScript Programming Language.

    + + + + + +
    + + diff --git a/branch.bug_ternary_use_open/index.html b/branch.bug_ternary_use_open/index.html new file mode 100644 index 000000000..235f44b54 --- /dev/null +++ b/branch.bug_ternary_use_open/index.html @@ -0,0 +1,125 @@ + + + + + + + + + + + +JSLint: The JavaScript Code Quality Tool + + +
    +
    Source
    +
    + + +
    +
    + Warnings +
    +
    +
    + Function Report +
    +
    +
    + Property Directive + +
    +
    + + + + +
    +
    Options +
    Assume... +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Global variables... + +
    +
    +
    +
    + + + + + +
    + + diff --git a/branch.bug_ternary_use_open/jslint.css b/branch.bug_ternary_use_open/jslint.css new file mode 100644 index 000000000..9c1b1a2f9 --- /dev/null +++ b/branch.bug_ternary_use_open/jslint.css @@ -0,0 +1,340 @@ +@font-face { + font-family: 'Programma'; + src: url('Programma.woff') format('woff'); +} +@font-face { + font-family: 'Daley'; + font-weight: bold; + src: url('Daley-Bold.woff') format('woff'); +} + +body { + background-color: antiquewhite; + color: black; + font-family: 'Daley', sans-serif; + margin: 0; + padding: 0; +} + +#JSLINT_TITLEBOX { + font-family: 'Daley', sans-serif; + margin: 0; + margin-left: 3%; + margin-right: 3%; + padding: 0; +} + +#JSLINT_TITLEBOX ul { + float: right; + font-size: 12pt; +} + +#JSLINT_TITLEBOX div { + color: darkslategray; + float: left; + font-size: 48pt; +} + +#JSLINT_ fieldset { + background-color: gainsboro; + border: 0; + clear: both; + margin-bottom: 1em; + margin-left: 3%; + margin-right: 3%; + margin-top: 1em; + padding: 0; + width: auto; +} +#JSLINT_ legend { + background-color: darkslategray; + border: 0; + color: white; + font-size: 100%; + font-style: normal; + font-weight: normal; + margin: 0; + padding-bottom: 0.25em; + padding-left: 0; + padding-right: 0; + padding-top: 0.25em; + text-align: center; + width: 100%; +} +#JSLINT_ button { + background-color: darkslategray; + border: 0; + color: white; + cursor: pointer; + font-family: 'Daley', sans-serif; + font-size: 100%; + font-style: normal; + margin-left: 1em; + margin-right: 1em; + padding-left: 1em; + padding-right: 1em; + padding-bottom: 0.25em; + padding-top: 0.25em; + text-align: center; +} +#JSLINT_ button:hover { + background-color: slategray; + color: white; + border: 0; +} + +#JSLINT_ button:active { + border: 0; + color: black; +} + +#JSLINT_ button:disabled { + background-color: gray; + border: 0; + color: gainsboro; +} + +a:visited { + color: #69565c; +} + +a:link { + color: black; +} + +#JSLINT_ input[type="text"] { + background-color: white; + border: 0; + color: black; + margin-bottom: 0.2em; + margin-right: 0.5em; + padding-left: 0.5em; + padding-right: 0.5em; + text-align: right; + width: 3em; +} + +#JSLINT_ textarea { + border: 0; + background-color: white; + border: 0; + font-family: Programma, monospace; + font-size: 100%; + font-weight: bold; + resize: none; +} + +#JSLINT_ textarea::selection { + background-color: yellow; + color: black; +} + +#JSLINT_NUMBER { + color: darkgray; + display: inline-block; + height: 4in; + margin: 2%; + margin-right: 0; + overflow: hidden; + padding-right: 0.5%; + text-align: right; + white-space: pre; + width: 4%; +} + +#JSLINT_SOURCE { + color: black; + display: inline-block; + font-size: 100%; + height: 4in; + margin: 2%; + margin-left: 0; + margin-right: 0; + overflow: auto; + padding-left: 0.5%; + white-space: pre; + width: 91%; +} + +#JSLINT_PROPERTY { + background-color: honeydew; + border: 0; + margin: 2%; + padding: 0.5%; + white-space: pre; + width: 95%; +} + +#JSLINT_GLOBAL { + white-space: normal; + width: 95%; +} +#JSLINT_ label { + font-family: sans-serif; + font-size: 90%; + padding-left: 0.25em; +} +#JSLINT_OPTIONS>div { + float: left; + margin: 0.5em; +} + +#JSLINT_ address { + color: black; + display: block; + float: right; + font-family: serif; + font-size: 90%; + margin-left: 1em; +} +#JSLINT_ dl { + background-color: cornsilk; + color: white; + margin: 0; + padding-bottom: 2pt; + padding-left: 1em; + padding-right: 1em; + padding-top: 2pt; +} +#JSLINT_ dfn { + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + margin-bottom: 2pt; +} +#JSLINT_ dt { + color: black; + display: block; + float: left; + font-family: serif; + font-size: 75%; + font-style: italic; + margin: 0; + width: 8em; + text-align: right; +} +#JSLINT_ dd { + color: black; + display: block; + font-family: Programma, monospace; + font-weight: bold; + margin-left: 8em; + padding-bottom: 2pt; +} +#JSLINT_WARNINGS>legend { + background-color: indianred; +} +#JSLINT_WARNINGS>div { + background-color: pink; + padding: 1em; +} +#JSLINT_WARNINGS cite { + color: black; + display: block; + font-family: serif; + font-size: 100%; + font-style: normal; + margin-bottom: 4pt; + margin-left: 20pt; + margin-right: 20pt; + margin-top: 4pt; + overflow-x: hidden; +} +#JSLINT_WARNINGS samp { + background-color: lavenderblush; + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + padding: 4pt; + margin-bottom: 0; + margin-left: 16pt; + margin-right: 16pt; + margin-top: 0; + white-space: pre-wrap; +} + +#JSLINT_REPORT center { + margin-top: 1em; +} + +#JSLINT_WARNINGS dl address { + color: black; + display: inline; + float: none; + font-size: 80%; + margin: 0; +} + +#JSLINT_REPORT>div { + padding: 1em; +} + +#JSLINT_ dl.level0 { + color: black; + background-color: white; +} + +#JSLINT_ dl.level1 { + color: black; + background-color: #ffffe0; /* yellow */ + margin-left: 1em; +} + +#JSLINT_ dl.level2 { + color: black; + background-color: #e0ffe0; /* green */ + margin-left: 2em; +} + +#JSLINT_ dl.level3 { + color: black; + background-color: #e0e0ff; /* blue */ + margin-left: 3em; +} + +#JSLINT_ dl.level4 { + background-color: #ffe0ff; /* purple */ + color: black; + margin-left: 4em; +} + +#JSLINT_ dl.level5 { + background-color: #ffe0e0; /* red */ + color: black; + margin-left: 5em; +} + +#JSLINT_ dl.level6 { + background-color: #ffe390; /* orange */ + color: black; + margin-left: 6em; +} + +#JSLINT_ dl.level7 { + background-color: #e0e0e0; /* gray */ + color: black; + margin-left: 7em; +} + +#JSLINT_ dl.level8 { + color: black; + margin-left: 8em; +} + +#JSLINT_ dl.level9 { + color: black; + margin-left: 9em; +} + +.none { + display: none; +} +.center { + text-align: center; +} diff --git a/branch.bug_ternary_use_open/jslint.js b/branch.bug_ternary_use_open/jslint.js new file mode 100644 index 000000000..4d8e3124f --- /dev/null +++ b/branch.bug_ternary_use_open/jslint.js @@ -0,0 +1,4925 @@ +// jslint.js +// 2018-09-26 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// jslint(source, option_object, global_array) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze, a string or an array of strings. +// option_object An object whose keys correspond to option names. +// global_array An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all of the functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// 1. If the source is a single string, split it into an array of strings. +// 2. Turn the source into an array of tokens. +// 3. Furcate the tokens into a parse tree. +// 4. Walk the tree, traversing all of the nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// 5. Check the whitespace between the tokens. + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*property + a, and, arity, assign, b, bad_assignment_a, bad_directive_a, bad_get, + bad_module_name_a, bad_option_a, bad_property_a, bad_set, bitwise, block, + body, browser, c, calls, catch, charCodeAt, closer, closure, code, column, + complex, concat, constant, context, convert, couch, create, d, dead, + default, devel, directive, directives, disrupt, dot, duplicate_a, edition, + ellipsis, else, empty_block, escape_mega, eval, every, expected_a, + expected_a_at_b_c, expected_a_b, expected_a_b_from_c_d, expected_a_before_b, + expected_a_next_at_b, expected_digits_after_a, expected_four_digits, + expected_identifier_a, expected_line_break_a_b, expected_regexp_factor_a, + expected_space_a_b, expected_statements_a, expected_string_a, + expected_type_string_a, exports, expression, extra, finally, flag, for, + forEach, free, from, froms, fud, fudge, function_in_loop, functions, g, + getset, global, i, id, identifier, import, inc, indexOf, infix_in, init, + initial, isArray, isNaN, join, json, keys, label, label_a, lbp, led, length, + level, line, lines, live, long, loop, m, margin, match, message, + misplaced_a, misplaced_directive_a, missing_browser, missing_m, module, + multivar, naked_block, name, names, nested_comment, new, node, not_label_a, + nr, nud, number_isNaN, ok, open, option, out_of_scope_a, parameters, parent, + pop, property, push, quote, redefinition_a_b, replace, + required_a_optional_b, reserved_a, right, role, search, shebang, signature, + single, slice, some, sort, split, startsWith, statement, stop, strict, + subscript_a, switch, test, this, thru, toString, todo_comment, tokens, + too_long, too_many_digits, tree, try, type, u, unclosed_comment, + unclosed_mega, unclosed_string, undeclared_a, unexpected_a, + unexpected_a_after_b, unexpected_a_before_b, unexpected_at_top_level_a, + unexpected_char_a, unexpected_comment, unexpected_directive_a, + unexpected_expression_a, unexpected_label_a, unexpected_parens, + unexpected_space_a_b, unexpected_statement_a, unexpected_trailing_space, + unexpected_typeof_a, uninitialized_a, unreachable_a, + unregistered_property_a, unsafe, unused_a, use_double, use_open, use_spaces, + use_strict, used, value, var_loop, var_switch, variable, warning, warnings, + weird_condition_a, weird_expression_a, weird_loop, weird_relation_a, white, + wrap_assignment, wrap_condition, wrap_immediate, wrap_parameter, + wrap_regexp, wrap_unary, wrapped, writable, y +*/ + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than {} because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +function populate(array, object = empty(), value = true) { + +// Augment an object by taking property names from an array of strings. + + array.forEach(function (name) { + object[name] = value; + }); + return object; +} + +const allowed_option = { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + bitwise: true, + browser: [ + "caches", "clearInterval", "clearTimeout", "document", "DOMException", + "Element", "Event", "event", "FileReader", "FormData", "history", + "localStorage", "location", "MutationObserver", "name", "navigator", + "screen", "sessionStorage", "setInterval", "setTimeout", "Storage", + "URL", "window", "Worker", "XMLHttpRequest" + ], + couch: [ + "emit", "getRow", "isArray", "log", "provides", "registerType", + "require", "send", "start", "sum", "toJSON" + ], + convert: true, + devel: [ + "alert", "confirm", "console", "prompt" + ], + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + multivar: true, + node: [ + "Buffer", "clearImmediate", "clearInterval", "clearTimeout", + "console", "exports", "module", "process", "require", + "setImmediate", "setInterval", "setTimeout", "URL", + "URLSearchParams", "__dirname", "__filename" + ], + single: true, + this: true, + white: true +}; + +const anticondition = populate([ + "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%", + "typeof", "(number)", "(string)" +]); + +// These are the bitwise operators. + +const bitwiseop = populate([ + "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=", + ">>>", ">>>=" +]); + +const escapeable = populate([ + "\\", "/", "`", "b", "f", "n", "r", "t" +]); + +const opener = { + +// The open and close pairs. + + "(": ")", // paren + "[": "]", // bracket + "{": "}", // brace + "${": "}" // mega +}; + +// The relational operators. + +const relationop = populate([ + "!=", "!==", "==", "===", "<", "<=", ">", ">=" +]); + +// This is the set of infix operators that require a space on each side. + +const spaceop = populate([ + "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/", + "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=", + ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" +]); + +const standard = [ + +// These are the globals that are provided by the language standard. + + "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI", + "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error", + "EvalError", "Float32Array", "Float64Array", "Generator", + "GeneratorFunction", "Int8Array", "Int16Array", "Int32Array", "Intl", + "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat", + "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", + "Set", "String", "Symbol", "SyntaxError", "System", "TypeError", + "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", + "URIError", "WeakMap", "WeakSet" +]; + +const bundle = { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + and: "The '&&' subexpression should be wrapped in parens.", + bad_assignment_a: "Bad assignment to '{a}'.", + bad_directive_a: "Bad directive '{a}'.", + bad_get: "A get function takes no parameters.", + bad_module_name_a: "Bad module name '{a}'.", + bad_option_a: "Bad option '{a}'.", + bad_property_a: "Bad property name '{a}'.", + bad_set: "A set function takes one parameter.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + escape_mega: "Unexpected escapement in mega literal.", + expected_a: "Expected '{a}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: ( + "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'." + ), + expected_a_before_b: "Expected '{a}' before '{b}'.", + expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.", + expected_digits_after_a: "Expected digits after '{a}'.", + expected_four_digits: "Expected four digits after '\\u'.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.", + expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.", + expected_space_a_b: "Expected one space between '{a}' and '{b}'.", + expected_statements_a: "Expected statements before '{a}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + expected_type_string_a: "Expected a type string and instead saw '{a}'.", + function_in_loop: "Don't make functions within a loop.", + infix_in: ( + "Unexpected 'in'. Compare with undefined, " + + "or use the hasOwnProperty method instead." + ), + label_a: "'{a}' is a statement label.", + misplaced_a: "Place '{a}' at the outermost level.", + misplaced_directive_a: ( + "Place the '/*{a}*/' directive before the first statement." + ), + missing_browser: "/*global*/ requires the Assume a browser option.", + missing_m: "Expected 'm' flag on a multiline regular expression.", + naked_block: "Naked block.", + nested_comment: "Nested comment.", + not_label_a: "'{a}' is not a label.", + number_isNaN: "Use Number.isNaN function to compare with NaN.", + out_of_scope_a: "'{a}' is out of scope.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + required_a_optional_b: ( + "Required parameter '{a}' after optional parameter '{b}'." + ), + reserved_a: "Reserved name '{a}'.", + subscript_a: "['{a}'] is better written in dot notation.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line is longer than 80 characters.", + too_many_digits: "Too many digits.", + unclosed_comment: "Unclosed comment.", + unclosed_mega: "Unclosed mega literal.", + unclosed_string: "Unclosed string.", + undeclared_a: "Undeclared '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_a_after_b: "Unexpected '{a}' after '{b}'.", + unexpected_a_before_b: "Unexpected '{a}' before '{b}'.", + unexpected_at_top_level_a: "Expected '{a}' to be in a function.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_directive_a: "When using modules, don't use directive '/*{a}'.", + unexpected_expression_a: ( + "Unexpected expression '{a}' in statement position." + ), + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_parens: "Don't wrap function literals in parens.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_statement_a: ( + "Unexpected statement '{a}' in expression position." + ), + unexpected_trailing_space: "Unexpected trailing space.", + unexpected_typeof_a: ( + "Unexpected 'typeof'. Use '===' to compare directly with {a}." + ), + uninitialized_a: "Uninitialized '{a}'.", + unreachable_a: "Unreachable '{a}'.", + unregistered_property_a: "Unregistered property name '{a}'.", + unsafe: "Unsafe character '{a}'.", + unused_a: "Unused '{a}'.", + use_double: "Use double quotes, not single quotes.", + use_open: ( + "Wrap a ternary expression in parens, " + + "with a line break after the left paren." + ), + use_spaces: "Use spaces, not tabs.", + use_strict: "This function needs a \"use strict\" pragma.", + var_loop: "Don't declare variables in a loop.", + var_switch: "Don't declare variables in a switch.", + weird_condition_a: "Weird condition '{a}'.", + weird_expression_a: "Weird expression '{a}'.", + weird_loop: "Weird loop.", + weird_relation_a: "Weird relation '{a}'.", + wrap_assignment: "Don't wrap assignment statements in parens.", + wrap_condition: "Wrap the condition in parens.", + wrap_immediate: ( + "Wrap an immediate function invocation in parentheses to assist " + + "the reader in understanding that the expression is the result " + + "of a function, and not the function itself." + ), + wrap_parameter: "Wrap the parameter in parens.", + wrap_regexp: "Wrap this regexp in parens to avoid confusion.", + wrap_unary: "Wrap the unary expression in parens." +}; + +// Regular expression literals: + +// supplant {variables} +const rx_supplant = /\{([^{}]*)\}/g; +// carriage return, carriage return linefeed, or linefeed +const rx_crlf = /\n|\r\n?/; +// unsafe characters that are silently deleted by one or more browsers +const rx_unsafe = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; +// identifier +const rx_identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; +const rx_module = /^[a-zA-Z0-9_$:.@\-\/]+$/; +const rx_bad_property = /^_|\$|Sync\$|_$/; +// star slash +const rx_star_slash = /\*\//; +// slash star +const rx_slash_star = /\/\*/; +// slash star or ending slash +const rx_slash_star_or_slash = /\/\*|\/$/; +// uncompleted work comment +const rx_todo = /\b(?:todo|TO\s?DO|HACK)\b/; +// tab +const rx_tab = /\t/g; +// directive +const rx_directive = /^(jslint|property|global)\s+(.*)$/; +const rx_directive_part = /^([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*(.*)$/; +// token (sorry it is so long) +const rx_token = /^((\s+)|([a-zA-Z_$][a-zA-Z0-9_$]*)|[(){}\[\]?,:;'"~`]|=(?:==?|>)?|\.+|[*\/][*\/=]?|\+[=+]?|-[=\-]?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<= "a" && string <= "z\uffff") + || (string >= "A" && string <= "Z\uffff") + ); +} + +function supplant(string, object) { + return string.replace(rx_supplant, function (found, filling) { + const replacement = object[filling]; + return ( + replacement !== undefined + ? replacement + : found + ); + }); +} + +let anon; // The guessed name for anonymous functions. +let blockage; // The current block. +let block_stack; // The stack of blocks. +let declared_globals; // The object containing the global declarations. +let directives; // The directive comments. +let directive_mode; // true if directives are still allowed. +let early_stop; // true if JSLint cannot finish. +let exports; // The exported names and values. +let froms; // The array collecting all import-from strings. +let fudge; // true if the natural numbers start with 1. +let functionage; // The current function. +let functions; // The array containing all of the functions. +let global; // The global object; the outermost context. +let json_mode; // true if parsing JSON. +let lines; // The array containing source lines. +let module_mode; // true if import or export was used. +let next_token; // The next token to be examined in the parse. +let option; // The options parameter. +let property; // The object containing the tallied property names. +let mega_mode; // true if currently parsing a megastring literal. +let shebang; // true if a #! was seen on the first line. +let stack; // The stack of functions. +let syntax; // The object containing the parser. +let token; // The current token being examined in the parse. +let token_nr; // The number of the next token. +let tokens; // The array of tokens. +let tenure; // The predefined property registry. +let tree; // The abstract parse tree. +let var_mode; // "var" if using var; "let" if using let. +let warnings; // The array collecting all generated warnings. + +// Error reportage functions: + +function artifact(the_token) { + +// Return a string representing an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); +} + +function artifact_line(the_token) { + +// Return the fudged line number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.line + fudge; +} + +function artifact_column(the_token) { + +// Return the fudged column number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.from + fudge; +} + +function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + const warning = { // ~~ + name: "JSLintError", + column: column, + line: line, + code: code + }; + if (a !== undefined) { + warning.a = a; + } + if (b !== undefined) { + warning.b = b; + } + if (c !== undefined) { + warning.c = c; + } + if (d !== undefined) { + warning.d = d; + } + warning.message = supplant(bundle[code] || code, warning); + warnings.push(warning); + return warning; +} + +function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); +} + +function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + if (the_token.warning === undefined) { + the_token.warning = warn_at( + code, + the_token.line, + the_token.from, + a || artifact(the_token), + b, + c, + d + ); + return the_token.warning; + } +} + +function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); +} + +// Tokenize: + +function tokenize(source) { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + +// If the source is not an array, then it is split into lines at the +// carriage return/linefeed. + + lines = ( + Array.isArray(source) + ? source + : source.split(rx_crlf) + ); + tokens = []; + + let char; // a popular character + let column = 0; // the column number of the next character + let first; // the first token + let from; // the starting column number of the token + let line = -1; // the line number of the next character + let nr = 0; // the next token number + let previous = global; // the previous token including comments + let prior = global; // the previous token excluding comments + let mega_from; // the starting column of megastring + let mega_line; // the starting line of megastring + let regexp_seen; // regular expression literal seen on this line + let snippet; // a piece of string + let source_line = ""; // the remaining line source string + let whole_line = ""; // the whole line source string + + if (lines[0].startsWith("#!")) { + line = 0; + shebang = true; + } + + function next_line() { + +// Put the next line of source in source_line. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + let at; + if ( + !option.long + && whole_line.length > 80 + && !json_mode + && first + && !regexp_seen + ) { + warn_at("too_long", line, 80); + } + column = 0; + line += 1; + regexp_seen = false; + whole_line = lines[line]; + source_line = whole_line; + if (source_line !== undefined) { + at = source_line.search(rx_tab); + if (at >= 0) { + if (!option.white) { + warn_at("use_spaces", line, at + 1); + } + source_line = source_line.replace(rx_tab, " "); + } + at = source_line.search(rx_unsafe); + if (at >= 0) { + warn_at( + "unsafe", + line, + column + at, + "U+" + source_line.charCodeAt(at).toString(16) + ); + } + if (!option.white && source_line.slice(-1) === " ") { + warn_at( + "unexpected_trailing_space", + line, + source_line.length - 1 + ); + } + } + return source_line; + } + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions snip, next_char, prev_char, +// some_digits, and escape help in the parsing of literals. + + function snip() { + +// Remove the last character from snippet. + + snippet = snippet.slice(0, -1); + } + + function next_char(match) { + +// Get the next character from the source line. Remove it from the source_line, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + return stop_at( + ( + char === "" + ? "expected_a" + : "expected_a_b" + ), + line, + column - 1, + match, + char + ); + } + if (source_line) { + char = source_line[0]; + source_line = source_line.slice(1); + snippet += char; + } else { + char = ""; + snippet += " "; + } + column += 1; + return char; + } + + function back_char() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the source_line. + + if (snippet) { + char = snippet.slice(-1); + source_line = char + source_line; + column -= 1; + snip(); + } else { + char = ""; + } + return char; + } + + function some_digits(rx, quiet) { + const result = source_line.match(rx); + if (result) { + char = result[1]; + column += char.length; + source_line = result[2]; + snippet += char; + } else { + char = ""; + if (!quiet) { + warn_at( + "expected_digits_after_a", + line, + column, + snippet + ); + } + } + return char.length; + } + + function escape(extra) { + next_char("\\"); + if (escapeable[char] === true) { + return next_char(); + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "u") { + if (next_char("u") === "{") { + if (json_mode) { + warn_at("unexpected_a", line, column - 1, char); + } + if (some_digits(rx_hexs) > 5) { + warn_at("too_many_digits", line, column - 1); + } + if (next_char() !== "}") { + stop_at("expected_a_before_b", line, column, "}", char); + } + return next_char(); + } + back_char(); + if (some_digits(rx_hexs, true) < 4) { + warn_at("expected_four_digits", line, column - 1); + } + return; + } + if (extra && extra.indexOf(char) >= 0) { + return next_char(); + } + warn_at("unexpected_a_before_b", line, column - 2, "\\", char); + } + + function make(id, value, identifier) { + +// Make the token object and append it to the tokens list. + + const the_token = { + from: from, + id: id, + identifier: Boolean(identifier), + line: line, + nr: nr, + thru: column + }; + tokens[nr] = the_token; + nr += 1; + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + directive_mode = false; + } + +// If the token is to have a value, give it one. + + if (value !== undefined) { + the_token.value = value; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option.white. + + if ( + previous.line === line + && previous.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (previous.id === "(comment)" || previous.id === "(regexp)") + ) { + warn( + "expected_space_a_b", + the_token, + artifact(previous), + artifact(the_token) + ); + } + if (previous.id === "." && id === "(number)") { + warn("expected_a_before_b", previous, "0", "."); + } + if (prior.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// The previous token is used to detect adjacency problems. + + previous = the_token; + +// The prior token is a previous token that was not a comment. The prior token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (previous.id !== "(comment)") { + prior = previous; + } + return the_token; + } + + function parse_directive(the_comment, body) { + +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + + const result = body.match(rx_directive_part); + if (result) { + let allowed; + const name = result[1]; + const value = result[2]; + if (the_comment.directive === "jslint") { + allowed = allowed_option[name]; + if ( + typeof allowed === "boolean" + || typeof allowed === "object" + ) { + if ( + value === "" + || value === "true" + || value === undefined + ) { + option[name] = true; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } else if (value === "false") { + option[name] = false; + } else { + warn("bad_option_a", the_comment, name + ":" + value); + } + } else { + warn("bad_option_a", the_comment, name); + } + } else if (the_comment.directive === "property") { + if (tenure === undefined) { + tenure = empty(); + } + tenure[name] = true; + } else if (the_comment.directive === "global") { + if (value) { + warn("bad_option_a", the_comment, name + ":" + value); + } + declared_globals[name] = false; + module_mode = the_comment; + } + return parse_directive(the_comment, result[3]); + } + if (body) { + return stop("bad_directive_a", the_comment, body); + } + } + + function comment(snippet) { + +// Make a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + + const the_comment = make("(comment)", snippet); + if (Array.isArray(snippet)) { + snippet = snippet.join(" "); + } + if (!option.devel && rx_todo.test(snippet)) { + warn("todo_comment", the_comment); + } + const result = snippet.match(rx_directive); + if (result) { + if (!directive_mode) { + warn_at("misplaced_directive_a", line, from, result[1]); + } else { + the_comment.directive = result[1]; + parse_directive(the_comment, result[2]); + } + directives.push(the_comment); + } + return the_comment; + } + + function regexp() { + +// Parse a regular expression literal. + + let multi_mode = false; + let result; + let value; + regexp_seen = true; + + function quantifier() { + +// Match an optional quantifier. + + if (char === "?" || char === "*" || char === "+") { + next_char(); + } else if (char === "{") { + if (some_digits(rx_digits, true) === 0) { + warn_at("expected_a", line, column, "0"); + } + if (next_char() === ",") { + some_digits(rx_digits, true); + next_char(); + } + next_char("}"); + } else { + return; + } + if (char === "?") { + next_char("?"); + } + } + + function subklass() { + +// Match a character in a character class. + + if (char === "\\") { + escape("BbDdSsWw-[]^"); + return true; + } + if ( + char === "" + || char === "[" + || char === "]" + || char === "/" + || char === "^" + || char === "-" + ) { + return false; + } + if (char === " ") { + warn_at("expected_a_b", line, column, "\\u0020", " "); + } else if (char === "`" && mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char(); + return true; + } + + function ranges() { + +// Match a range of subclasses. + + if (subklass()) { + if (char === "-") { + next_char("-"); + if (!subklass()) { + return stop_at( + "unexpected_a", + line, + column - 1, + "-" + ); + } + } + return ranges(); + } + } + + function klass() { + +// Match a class. + + next_char("["); + if (char === "^") { + next_char("^"); + } + (function classy() { + ranges(); + if (char !== "]" && char !== "") { + warn_at( + "expected_a_before_b", + line, + column, + "\\", + char + ); + next_char(); + return classy(); + } + }()); + next_char("]"); + } + + function choice() { + + function group() { + +// Match a group that starts with left paren. + + next_char("("); + if (char === "?") { + next_char("?"); + if (char === "=" || char === "!") { + next_char(); + } else { + next_char(":"); + } + } else if (char === ":") { + warn_at("expected_a_before_b", line, column, "?", ":"); + } + choice(); + next_char(")"); + } + + function factor() { + if ( + char === "" + || char === "/" + || char === "]" + || char === ")" + ) { + return false; + } + if (char === "(") { + group(); + return true; + } + if (char === "[") { + klass(); + return true; + } + if (char === "\\") { + escape("BbDdSsWw^${}[]():=!.-|*+?"); + return true; + } + if ( + char === "?" + || char === "+" + || char === "*" + || char === "}" + || char === "{" + ) { + warn_at( + "expected_a_before_b", + line, + column - 1, + "\\", + char + ); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column - 1, "`"); + } + } else if (char === " ") { + warn_at( + "expected_a_b", + line, + column - 1, + "\\s", + " " + ); + } else if (char === "$") { + if (source_line[0] !== "/") { + multi_mode = true; + } + } else if (char === "^") { + if (snippet !== "^") { + multi_mode = true; + } + } + next_char(); + return true; + } + + function sequence(follow) { + if (factor()) { + quantifier(); + return sequence(true); + } + if (!follow) { + warn_at("expected_regexp_factor_a", line, column, char); + } + } + +// Match a choice (a sequence that can be followed by | and another choice). + + sequence(); + if (char === "|") { + next_char("|"); + return choice(); + } + } + +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + next_char(); + if (char === "=") { + warn_at("expected_a_before_b", line, column, "\\", "="); + } + choice(); + +// Make sure there is a closing slash. + + snip(); + value = snippet; + next_char("/"); + +// Process dangling flag letters. + + const allowed = { + g: true, + i: true, + m: true, + u: true, + y: true + }; + const flag = empty(); + (function make_flag() { + if (is_letter(char)) { + if (allowed[char] !== true) { + warn_at("unexpected_a", line, column, char); + } + allowed[char] = false; + flag[char] = true; + next_char(); + return make_flag(); + } + }()); + back_char(); + if (char === "/" || char === "*") { + return stop_at("unexpected_a", line, from, char); + } + result = make("(regexp)", char); + result.flag = flag; + result.value = value; + if (multi_mode && !flag.m) { + warn_at("missing_m", line, column); + } + return result; + } + + function string(quote) { + +// Make a string token. + + let the_token; + snippet = ""; + next_char(); + + return (function next() { + if (char === quote) { + snip(); + the_token = make("(string)", snippet); + the_token.quote = quote; + return the_token; + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "\\") { + escape(quote); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char("`"); + } else { + next_char(); + } + return next(); + }()); + } + + function frack() { + if (char === ".") { + some_digits(rx_digits); + next_char(); + } + if (char === "E" || char === "e") { + next_char(); + if (char !== "+" && char !== "-") { + back_char(); + } + some_digits(rx_digits); + next_char(); + } + } + + function number() { + if (snippet === "0") { + next_char(); + if (char === ".") { + frack(); + } else if (char === "b") { + some_digits(rx_bits); + next_char(); + } else if (char === "o") { + some_digits(rx_octals); + next_char(); + } else if (char === "x") { + some_digits(rx_hexs); + next_char(); + } + } else { + next_char(); + frack(); + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + return stop_at( + "unexpected_a_after_b", + line, + column - 1, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + back_char(); + return make("(number)", snippet); + } + + function lex() { + let array; + let i = 0; + let j = 0; + let last; + let result; + let the_token; + if (!source_line) { + source_line = next_line(); + from = 0; + return ( + source_line === undefined + ? ( + mega_mode + ? stop_at("unclosed_mega", mega_line, mega_from) + : make("(end)") + ) + : lex() + ); + } + from = column; + result = source_line.match(rx_token); + +// result[1] token +// result[2] whitespace +// result[3] identifier +// result[4] number +// result[5] rest + + if (!result) { + return stop_at( + "unexpected_char_a", + line, + column, + source_line[0] + ); + } + + snippet = result[1]; + column += snippet.length; + source_line = result[5]; + +// Whitespace was matched. Call lex again to get more. + + if (result[2]) { + return lex(); + } + +// The token is an identifier. + + if (result[3]) { + return make(snippet, undefined, true); + } + +// The token is a number. + + if (result[4]) { + return number(snippet); + } + +// The token is a string. + + if (snippet === "\"") { + return string(snippet); + } + if (snippet === "'") { + if (!option.single) { + warn_at("use_double", line, column); + } + return string(snippet); + } + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (snippet === "`") { + if (mega_mode) { + return stop_at("expected_a_b", line, column, "}", "`"); + } + snippet = ""; + mega_from = from; + mega_line = line; + mega_mode = true; + +// Parsing a mega literal is tricky. First make a ` token. + + make("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + (function part() { + const at = source_line.search(rx_mega); + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + if (at < 0) { + snippet += source_line + "\n"; + return ( + next_line() === undefined + ? stop_at("unclosed_mega", mega_line, mega_from) + : part() + ); + } + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + snippet += source_line.slice(0, at); + column += at; + source_line = source_line.slice(at); + if (source_line[0] === "\\") { + stop_at("escape_mega", line, at); + } + make("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then make tokens that will become part of an expression until +// a } token is made. + + if (source_line[0] === "$") { + column += 2; + make("${"); + source_line = source_line.slice(2); + (function expr() { + const id = lex().id; + if (id === "{") { + return stop_at( + "expected_a_b", + line, + column, + "}", + "{" + ); + } + if (id !== "}") { + return expr(); + } + }()); + return part(); + } + }()); + source_line = source_line.slice(1); + column += 1; + mega_mode = false; + return make("`"); + } + +// The token is a // comment. + + if (snippet === "//") { + snippet = source_line; + source_line = ""; + the_token = comment(snippet); + if (mega_mode) { + warn("unexpected_comment", the_token, "`"); + } + return the_token; + } + +// The token is a /* comment. + + if (snippet === "/*") { + array = []; + if (source_line[0] === "/") { + warn_at("unexpected_a", line, column + i, "/"); + } + (function next() { + if (source_line > "") { + i = source_line.search(rx_star_slash); + if (i >= 0) { + return; + } + j = source_line.search(rx_slash_star); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + } + array.push(source_line); + source_line = next_line(); + if (source_line === undefined) { + return stop_at("unclosed_comment", line, column); + } + return next(); + }()); + snippet = source_line.slice(0, i); + j = snippet.search(rx_slash_star_or_slash); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + array.push(snippet); + column += i + 2; + source_line = source_line.slice(i + 2); + return comment(array); + } + +// The token is a slash. + + if (snippet === "/") { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + if (prior.identifier) { + if (!prior.dot) { + if (prior.id === "return") { + return regexp(); + } + if ( + prior.id === "(begin)" + || prior.id === "case" + || prior.id === "delete" + || prior.id === "in" + || prior.id === "instanceof" + || prior.id === "new" + || prior.id === "typeof" + || prior.id === "void" + || prior.id === "yield" + ) { + the_token = regexp(); + return stop("unexpected_a", the_token); + } + } + } else { + last = prior.id[prior.id.length - 1]; + if ("(,=:?[".indexOf(last) >= 0) { + return regexp(); + } + if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) { + the_token = regexp(); + warn("wrap_regexp", the_token); + return the_token; + } + } + if (source_line[0] === "/") { + column += 1; + source_line = source_line.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + } + return make(snippet); + } + + first = lex(); + json_mode = first.id === "{" || first.id === "["; + +// This is the only loop in JSLint. It will turn into a recursive call to lex +// when ES6 has been finished and widely deployed and adopted. + + while (true) { + if (lex().id === "(end)") { + break; + } + } +} + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + +function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!rx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!rx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property[id] === "number") { + property[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (tenure !== undefined) { + if (tenure[id] !== true) { + warn("unregistered_property_a", name); + } + } else { + if (name.identifier && rx_bad_property.test(id)) { + warn("bad_property_a", name); + } + } + property[id] = 1; + } + return id; +} + +function dispense() { + +// Deliver the next token, skipping the comments. + + const cadet = tokens[token_nr]; + token_nr += 1; + if (cadet.id === "(comment)") { + if (json_mode) { + warn("unexpected_a", cadet); + } + return dispense(); + } else { + return cadet; + } +} + +function lookahead() { + +// Look ahead one token without advancing. + + const old_token_nr = token_nr; + const cadet = dispense(true); + token_nr = old_token_nr; + return cadet; +} + +function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if (token.identifier && token.id !== "function") { + anon = token.id; + } else if (token.id === "(string)" && rx_identifier.test(token.value)) { + anon = token.value; + } + +// Attempt to match next_token with an expected id. + + if (id !== undefined && next_token.id !== id) { + return ( + match === undefined + ? stop("expected_a_b", next_token, id, artifact()) + : stop( + "expected_a_b_from_c_d", + next_token, + id, + artifact(match), + artifact_line(match), + artifact(next_token) + ) + ); + } + +// Promote the tokens, skipping comments. + + token = next_token; + next_token = dispense(); + if (next_token.id === "(end)") { + token_nr -= 1; + } +} + +// Parsing of JSON is simple: + +function json_value() { + let negative; + if (next_token.id === "{") { + return (function json_object() { + const brace = next_token; + const object = empty(); + const properties = []; + brace.expression = properties; + advance("{"); + if (next_token.id !== "}") { + (function next() { + let name; + let value; + if (next_token.quote !== "\"") { + warn( + "unexpected_a", + next_token, + next_token.quote + ); + } + name = next_token; + advance("(string)"); + if (object[token.value] !== undefined) { + warn("duplicate_a", token); + } else if (token.value === "__proto__") { + warn("bad_property_a", token); + } else { + object[token.value] = token; + } + advance(":"); + value = json_value(); + value.label = name; + properties.push(value); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("}", brace); + return brace; + }()); + } + if (next_token.id === "[") { + return (function json_array() { + const bracket = next_token; + const elements = []; + bracket.expression = elements; + advance("["); + if (next_token.id !== "]") { + (function next() { + elements.push(json_value()); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]", bracket); + return bracket; + }()); + } + if ( + next_token.id === "true" + || next_token.id === "false" + || next_token.id === "null" + ) { + advance(); + return token; + } + if (next_token.id === "(number)") { + if (!rx_JSON_number.test(next_token.value)) { + warn("unexpected_a"); + } + advance(); + return token; + } + if (next_token.id === "(string)") { + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + advance(); + return token; + } + if (next_token.id === "-") { + negative = next_token; + negative.arity = "unary"; + advance("-"); + advance("(number)"); + negative.expression = token; + return negative; + } + stop("unexpected_a"); +} + +// Now we parse JavaScript. + +function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + const id = name.id; + +// Reserved words may not be enrolled. + + if (syntax[id] !== undefined && id !== "ignore") { + warn("reserved_a", name); + } else { + +// Has the name been enrolled in this context? + + let earlier = functionage.context[id]; + if (earlier) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + +// Has the name been enrolled in an outer context? + + } else { + stack.forEach(function (value) { + const item = value.context[id]; + if (item !== undefined) { + earlier = item; + } + }); + if (earlier) { + if (id === "ignore") { + if (earlier.role === "variable") { + warn("unexpected_a", name); + } + } else { + if ( + ( + role !== "exception" + || earlier.role !== "exception" + ) + && role !== "parameter" + && role !== "function" + ) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + } + } + } + +// Enroll it. + + functionage.context[id] = name; + name.dead = true; + name.parent = functionage; + name.init = false; + name.role = role; + name.used = 0; + name.writable = !readonly; + } + } +} + +function expression(rbp, initial) { + +// This is the heart of the Pratt parser. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// nud Null denotation +// led Left denotation +// lbp Left binding power +// rbp Right binding power + +// It processes a nud (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop. It +// returns the expression's parse tree. + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement, + + if (!initial) { + advance(); + } + the_symbol = syntax[token.id]; + if (the_symbol !== undefined && the_symbol.nud !== undefined) { + left = the_symbol.nud(); + } else if (token.identifier) { + left = token; + left.arity = "variable"; + } else { + return stop("unexpected_a", token); + } + (function right() { + the_symbol = syntax[next_token.id]; + if ( + the_symbol !== undefined + && the_symbol.led !== undefined + && rbp < the_symbol.lbp + ) { + advance(); + left = the_symbol.led(left); + return right(); + } + }()); + return left; +} + +function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = next_token; + let the_value; + the_paren.free = true; + advance("("); + the_value = expression(0); + advance(")"); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (anticondition[the_value.id] === true) { + warn("unexpected_a", the_value); + } + return the_value; +} + +function is_weird(thing) { + return ( + thing.id === "(regexp)" + || thing.id === "{" + || thing.id === "=>" + || thing.id === "function" + || (thing.id === "[" && thing.arity === "unary") + ); +} + +function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + return ( + Array.isArray(b) + && a.length === b.length + && a.every(function (value, index) { + return are_similar(value, b[index]); + }) + ); + } + if (Array.isArray(b)) { + return false; + } + if (a.id === "(number)" && b.id === "(number)") { + return a.value === b.value; + } + let a_string; + let b_string; + if (a.id === "(string)") { + a_string = a.value; + } else if (a.id === "`" && a.constant) { + a_string = a.value[0]; + } + if (b.id === "(string)") { + b_string = b.value; + } else if (b.id === "`" && b.constant) { + b_string = b.value[0]; + } + if (typeof a_string === "string") { + return a_string === b_string; + } + if (is_weird(a) || is_weird(b)) { + return false; + } + if (a.arity === b.arity && a.id === b.id) { + if (a.id === ".") { + return ( + are_similar(a.expression, b.expression) + && are_similar(a.name, b.name) + ); + } + if (a.arity === "unary") { + return are_similar(a.expression, b.expression); + } + if (a.arity === "binary") { + return ( + a.id !== "(" + && are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + ); + } + if (a.arity === "ternary") { + return ( + are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + && are_similar(a.expression[2], b.expression[2]) + ); + } + if (a.arity === "function" && a.arity === "regexp") { + return false; + } + return true; + } + return false; +} + +function semicolon() { + +// Try to match a semicolon. + + if (next_token.id === ";") { + advance(";"); + } else { + warn_at( + "expected_a_b", + token.line, + token.thru, + ";", + artifact(next_token) + ); + } + anon = "anonymous"; +} + +function statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token.identifier && next_token.id === ":") { + the_label = token; + if (the_label.id === "ignore") { + warn("unexpected_a", the_label); + } + advance(":"); + if ( + next_token.id === "do" + || next_token.id === "for" + || next_token.id === "switch" + || next_token.id === "while" + ) { + enroll(the_label, "label", true); + the_label.init = true; + the_label.dead = false; + the_statement = statement(); + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token; + first.statement = true; + the_symbol = syntax[first.id]; + if (the_symbol !== undefined && the_symbol.fud !== undefined) { + the_symbol.disrupt = false; + the_symbol.statement = true; + the_statement = the_symbol.fud(); + } else { + +// It is an expression statement. + + the_statement = expression(0, true); + if (the_statement.wrapped && the_statement.id !== "(") { + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; +} + +function statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const array = []; + (function next(disrupt) { + if ( + next_token.id !== "}" + && next_token.id !== "case" + && next_token.id !== "default" + && next_token.id !== "else" + && next_token.id !== "(end)" + ) { + let a_statement = statement(); + array.push(a_statement); + if (disrupt) { + warn("unreachable_a", a_statement); + } + return next(a_statement.disrupt); + } + }(false)); + return array; +} + +function not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === global) { + warn("unexpected_at_top_level_a", thing); + } +} + +function top_level_only(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== global) { + warn("misplaced_a", the_thing); + } +} + +function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token; + the_block.arity = "statement"; + the_block.body = special === "body"; + +// All top level function bodies should include the "use strict" pragma unless +// the whole file is strict or the file is a module or the function parameters +// use es6 syntax. + + if ( + special === "body" + && stack.length === 1 + && next_token.value === "use strict" + ) { + the_block.strict = next_token; + next_token.statement = true; + advance("(string)"); + advance(";"); + } + stmts = statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option.devel && special !== "ignore") { + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; +} + +function mutation_check(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + warn("bad_assignment_a", the_thing); + return false; + } + return true; +} + +function left_check(left, right) { + +// Warn if the left is not one of these: +// e.b +// e[b] +// e() +// ?: +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !left_check(left.expression[1]) + && !left_check(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right); + return false; + } + return true; +} + +// These functions are used to specify the grammar of our language: + +function symbol(id, bp) { + +// Make a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax[id] = the_symbol; + } + return the_symbol; +} + +function assignment(id) { + +// Make an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led = function (left) { + const the_token = token; + let right; + the_token.arity = "assignment"; + right = expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "pre" + || right.arity === "post" + ) { + warn("unexpected_a", right); + } + mutation_check(left); + return the_token; + }; + return the_symbol; +} + +function constant(id, type, value) { + +// Make a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud = ( + typeof value === "function" + ? value + : function () { + token.constant = true; + if (value !== undefined) { + token.value = value; + } + return token; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; +} + +function infix(id, bp, f) { + +// Make an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, expression(bp)]; + return the_token; + }; + return the_symbol; +} + +function infixr(id, bp) { + +// Make a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + the_token.expression = [left, expression(bp - 1)]; + return the_token; + }; + return the_symbol; +} + +function post(id) { + +// Make one of the post operators. + + const the_symbol = symbol(id, 150); + the_symbol.led = function (left) { + token.expression = left; + token.arity = "post"; + mutation_check(token.expression); + return token; + }; + return the_symbol; +} + +function pre(id) { + +// Make one of the pre operators. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "pre"; + the_token.expression = expression(150); + mutation_check(the_token.expression); + return the_token; + }; + return the_symbol; +} + +function prefix(id, f) { + +// Make a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = expression(150); + return the_token; + }; + return the_symbol; +} + +function stmt(id, f) { + +// Make a statement. + + const the_symbol = symbol(id); + the_symbol.fud = function () { + token.arity = "statement"; + return f(); + }; + return the_symbol; +} + +function ternary(id1, id2) { + +// Make a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led = function (left) { + const the_token = token; + const second = expression(20); + advance(id2); + token.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, expression(10)]; + return the_token; + }; + return the_symbol; +} + +// Begin defining the language. + +syntax = empty(); + +symbol("}"); +symbol(")"); +symbol("]"); +symbol(","); +symbol(";"); +symbol(":"); +symbol("*/"); +symbol("await"); +symbol("case"); +symbol("catch"); +symbol("class"); +symbol("default"); +symbol("else"); +symbol("enum"); +symbol("finally"); +symbol("implements"); +symbol("interface"); +symbol("package"); +symbol("private"); +symbol("protected"); +symbol("public"); +symbol("static"); +symbol("super"); +symbol("void"); +symbol("yield"); + +constant("(number)", "number"); +constant("(regexp)", "regexp"); +constant("(string)", "string"); +constant("arguments", "object", function () { + warn("unexpected_a", token); + return token; +}); +constant("eval", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("false", "boolean", false); +constant("Function", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("ignore", "undefined", function () { + warn("unexpected_a", token); + return token; +}); +constant("Infinity", "number", Infinity); +constant("isFinite", "function", function () { + warn("expected_a_b", token, "Number.isFinite", "isFinite"); + return token; +}); +constant("isNaN", "function", function () { + warn("number_isNaN", token); + return token; +}); +constant("NaN", "number", NaN); +constant("null", "null", null); +constant("this", "object", function () { + if (!option.this) { + warn("unexpected_a", token); + } + return token; +}); +constant("true", "boolean", true); +constant("undefined", "undefined"); + +assignment("="); +assignment("+="); +assignment("-="); +assignment("*="); +assignment("/="); +assignment("%="); +assignment("&="); +assignment("|="); +assignment("^="); +assignment("<<="); +assignment(">>="); +assignment(">>>="); + +infix("||", 40); +infix("&&", 50); +infix("|", 70); +infix("^", 80); +infix("&", 90); +infix("==", 100); +infix("===", 100); +infix("!=", 100); +infix("!==", 100); +infix("<", 110); +infix(">", 110); +infix("<=", 110); +infix(">=", 110); +infix("in", 110); +infix("instanceof", 110); +infix("<<", 120); +infix(">>", 120); +infix(">>>", 120); +infix("+", 130); +infix("-", 130); +infix("*", 140); +infix("/", 140); +infix("%", 140); +infixr("**", 150); +infix("(", 160, function (left) { + const the_paren = token; + let the_argument; + if (left.id !== "function") { + left_check(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (next_token.id !== ")") { + (function next() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + the_paren.free = true; + if (the_argument.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + the_paren.free = false; + } + return the_paren; +}); +infix(".", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("[", 170, function (left) { + const the_token = token; + const the_subscript = expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + const name = survey(the_subscript); + if (rx_identifier.test(name)) { + warn("subscript_a", the_subscript, name); + } + } + left_check(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; +}); +infix("=>", 170, function (left) { + return stop("wrap_parameter", left); +}); + +function do_tick() { + const the_tick = token; + the_tick.value = []; + the_tick.expression = []; + if (next_token.id !== "`") { + (function part() { + advance("(string)"); + the_tick.value.push(token); + if (next_token.id === "${") { + advance("${"); + the_tick.expression.push(expression(0)); + advance("}"); + return part(); + } + }()); + } + advance("`"); + return the_tick; +} + +infix("`", 160, function (left) { + const the_tick = do_tick(); + left_check(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; +}); + +post("++"); +post("--"); +pre("++"); +pre("--"); + +prefix("+"); +prefix("-"); +prefix("~"); +prefix("!"); +prefix("!!"); +prefix("[", function () { + const the_token = token; + the_token.expression = []; + if (next_token.id !== "]") { + (function next() { + let element; + let ellipsis = false; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + element = expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]"); + return the_token; +}); +prefix("/=", function () { + stop("expected_a_b", token, "/\\=", "/="); +}); +prefix("=>", function () { + return stop("expected_a_before_b", token, "()", "=>"); +}); +prefix("new", function () { + const the_new = token; + const right = expression(160); + if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "()", artifact(next_token)); + } + the_new.expression = right; + return the_new; +}); +prefix("typeof"); +prefix("void", function () { + const the_void = token; + warn("unexpected_a", the_void); + the_void.expression = expression(0); + return the_void; +}); + +function parameter_list() { + let complex = false; + const list = []; + let optional; + const signature = ["("]; + if (next_token.id !== ")" && next_token.id !== "(end)") { + (function parameter() { + let ellipsis = false; + let param; + if (next_token.id === "{") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("{"); + signature.push("{"); + (function subparameter() { + let subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (next_token.id === ":") { + advance(":"); + advance(); + token.label = subparam; + subparam = token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + } + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return subparameter(); + } + }()); + list.push(param); + advance("}"); + signature.push("}"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else if (next_token.id === "[") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("["); + signature.push("[]"); + (function subparameter() { + const subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + return subparameter(); + } + }()); + list.push(param); + advance("]"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else { + if (next_token.id === "...") { + complex = true; + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + } + if (!next_token.identifier) { + return stop("expected_identifier_a"); + } + param = next_token; + list.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (next_token.id === "=") { + complex = true; + optional = param; + advance("="); + param.expression = expression(0); + } else { + if (optional !== undefined) { + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } + } + }()); + } + advance(")"); + signature.push(")"); + return [list, signature.join(""), complex]; +} + +function do_function(the_function) { + let name; + if (the_function === undefined) { + the_function = token; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + name = next_token; + enroll(name, "variable", true); + the_function.name = name; + name.init = true; + name.calls = empty(); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + if (next_token.identifier) { + name = next_token; + the_function.name = name; + advance(); + } else { + the_function.name = anon; + } + } + } else { + name = the_function.name; + } + the_function.level = functionage.level + 1; + if (mega_mode) { + warn("unexpected_a", the_function); + } + +// Don't make functions in loops. It is inefficient, and it can lead to scoping +// errors. + + if (functionage.loop > 0) { + warn("function_in_loop", the_function); + } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + the_function.context = empty(); + the_function.finally = 0; + the_function.loop = 0; + the_function.switch = 0; + the_function.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functions.push(the_function); + functionage = the_function; + if (the_function.arity !== "statement" && typeof name === "object") { + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// Parse the parameter list. + + advance("("); + token.free = false; + token.arity = "function"; + const pl = parameter_list(); + functionage.parameters = pl[0]; + functionage.signature = pl[1]; + functionage.complex = pl[2]; + functionage.parameters.forEach(function enroll_parameter(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + name.names.forEach(enroll_parameter); + } + }); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && next_token.line === token.line + ) { + return stop("unexpected_a", next_token); + } + if (next_token.id === "." || next_token.id === "[") { + warn("unexpected_a"); + } + +// Restore the previous context. + + functionage = stack.pop(); + return the_function; +} + +prefix("function", do_function); + +function fart(pl) { + if (next_token.id === ";") { + stop("wrap_assignment", token); + } + advance("=>"); + const the_fart = token; + the_fart.arity = "binary"; + the_fart.name = "=>"; + the_fart.level = functionage.level + 1; + functions.push(the_fart); + if (functionage.loop > 0) { + warn("function_in_loop", the_fart); + } + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + the_fart.context = empty(); + the_fart.finally = 0; + the_fart.loop = 0; + the_fart.switch = 0; + the_fart.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functionage = the_fart; + the_fart.parameters = pl[0]; + the_fart.signature = pl[1]; + the_fart.complex = true; + the_fart.parameters.forEach(function (name) { + enroll(name, "parameter", true); + }); + if (next_token.id === "{") { + warn("expected_a_b", the_fart, "function", "=>"); + the_fart.block = block("body"); + } else { + the_fart.expression = expression(0); + } + functionage = stack.pop(); + return the_fart; +} + +prefix("(", function () { + const the_paren = token; + let the_value; + const cadet = lookahead().id; + +// We can distinguish between a parameter list for => and a wrapped expression +// with one token of lookahead. + + if ( + next_token.id === ")" + || next_token.id === "..." + || (next_token.identifier && (cadet === "," || cadet === "=")) + ) { + the_paren.free = false; + return fart(parameter_list()); + } + the_paren.free = true; + the_value = expression(0); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + if (next_token.id === "=>") { + if (the_value.arity !== "variable") { + if (the_value.id === "{" || the_value.id === "[") { + warn("expected_a_before_b", the_paren, "function", "("); + return stop("expected_a_b", next_token, "{", "=>"); + } + return stop("expected_identifier_a", the_value); + } + the_paren.expression = [the_value]; + return fart([the_paren.expression, "(" + the_value.id + ")"]); + } + return the_value; +}); +prefix("`", do_tick); +prefix("{", function () { + const the_brace = token; + const seen = empty(); + the_brace.expression = []; + if (next_token.id !== "}") { + (function member() { + let extra; + let full; + let id; + let name = next_token; + let value; + advance(); + if ( + (name.id === "get" || name.id === "set") + && next_token.identifier + ) { + if (!option.getset) { + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + next_token.id; + name = next_token; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (next_token.id === "}" || next_token.id === ",") { + if (typeof extra === "string") { + advance("("); + } + value = expression(Infinity, true); + } else if (next_token.id === "(") { + value = do_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + advance("("); + } + advance(":"); + value = expression(0); + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + advance(":"); + value = expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (next_token.id === ",") { + advance(","); + return member(); + } + }()); + } + advance("}"); + return the_brace; +}); + +stmt(";", function () { + warn("unexpected_a", token); + return token; +}); +stmt("{", function () { + warn("naked_block", token); + return block("naked"); +}); +stmt("break", function () { + const the_break = token; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (next_token.identifier && token.line === next_token.line) { + the_label = functionage.context[next_token.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + warn( + (the_label !== undefined && the_label.dead) + ? "out_of_scope_a" + : "not_label_a" + ); + } else { + the_label.used += 1; + } + the_break.label = next_token; + advance(); + } + advance(";"); + return the_break; +}); + +function do_var() { + const the_statement = token; + const is_const = the_statement.id === "const"; + the_statement.names = []; + +// A program may use var or let, but not both. + + if (!is_const) { + if (var_mode === undefined) { + var_mode = the_statement.id; + } else if (the_statement.id !== var_mode) { + warn( + "expected_a_b", + the_statement, + var_mode, + the_statement.id + ); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + warn("var_switch", the_statement); + } + if (functionage.loop > 0 && the_statement.id === "var") { + warn("var_loop", the_statement); + } + (function next() { + if (next_token.id === "{" && the_statement.id !== "var") { + const the_brace = next_token; + the_brace.names = []; + advance("{"); + (function pair() { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + survey(name); + advance(); + if (next_token.id === ":") { + advance(":"); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + next_token.label = name; + the_brace.names.push(next_token); + enroll(next_token, "variable", is_const); + advance(); + } else { + the_brace.names.push(name); + enroll(name, "variable", is_const); + } + if (next_token.id === ",") { + advance(","); + return pair(); + } + }()); + advance("}"); + advance("="); + the_brace.expression = expression(0); + the_statement.names.push(the_brace); + } else if (next_token.id === "[" && the_statement.id !== "var") { + const the_bracket = next_token; + the_bracket.names = []; + advance("["); + (function element() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + advance(); + the_bracket.names.push(name); + enroll(name, "variable", the_statement.id === "const"); + if (ellipsis) { + name.ellipsis = true; + } else if (next_token.id === ",") { + advance(","); + return element(); + } + }()); + advance("]"); + advance("="); + the_bracket.expression = expression(0); + the_statement.names.push(the_bracket); + } else if (next_token.identifier) { + const name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", is_const); + if (next_token.id === "=" || is_const) { + advance("="); + name.init = true; + name.expression = expression(0); + } + the_statement.names.push(name); + } else { + return stop("expected_identifier_a", next_token); + } + if (next_token.id === ",") { + if (!option.multivar) { + warn("expected_a_b", next_token, ";", ","); + } + advance(","); + return next(); + } + }()); + the_statement.open = ( + the_statement.names.length > 1 + && the_statement.line !== the_statement.names[1].line + ); + semicolon(); + return the_statement; +} + +stmt("const", do_var); +stmt("continue", function () { + const the_continue = token; + if (functionage.loop < 1 || functionage.finally > 0) { + warn("unexpected_a", the_continue); + } + not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; +}); +stmt("debugger", function () { + const the_debug = token; + if (!option.devel) { + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; +}); +stmt("delete", function () { + const the_token = token; + const the_value = expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; +}); +stmt("do", function () { + const the_do = token; + not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; +}); +stmt("export", function () { + const the_export = token; + let the_id; + let the_name; + let the_thing; + + function export_id() { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + the_id = next_token.id; + the_name = global.context[the_id]; + if (the_name === undefined) { + warn("unexpected_a"); + } else { + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a"); + } + exports[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + } + + the_export.expression = []; + if (next_token.id === "default") { + if (exports.default !== undefined) { + warn("duplicate_a"); + } + advance("default"); + the_thing = expression(0); + semicolon(); + exports.default = the_thing; + the_export.expression.push(the_thing); + } else { + if (next_token.id === "function") { + the_thing = statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a", the_name); + } + exports[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + warn("unexpected_a"); + } else if (next_token.id === "{") { + advance("{"); + (function loop() { + export_id(); + if (next_token.id === ",") { + advance(","); + return loop(); + } + }()); + advance("}"); + semicolon(); + } else { + export_id(); + if (the_name.writable !== true) { + warn("unexpected_a", token); + } + semicolon(); + } + } + module_mode = true; + return the_export; +}); +stmt("for", function () { + let first; + const the_for = token; + if (!option.for) { + warn("unexpected_a", the_for); + } + not_top_level(the_for); + functionage.loop += 1; + advance("("); + token.free = true; + if (next_token.id === ";") { + return stop("expected_a_b", the_for, "while (", "for (;"); + } + if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + return stop("unexpected_a"); + } + first = expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = expression(0); + advance(";"); + the_for.inc = expression(0); + if (the_for.inc.id === "++") { + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; +}); +stmt("function", do_function); +stmt("if", function () { + let the_else; + const the_if = token; + the_if.expression = condition(); + the_if.block = block(); + if (next_token.id === "else") { + advance("else"); + the_else = token; + the_if.else = ( + next_token.id === "if" + ? statement() + : block() + ); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + the_if.disrupt = true; + } else { + warn("unexpected_a", the_else); + } + } + } + return the_if; +}); +stmt("import", function () { + const the_import = token; + let name; + if (typeof module_mode === "object") { + warn("unexpected_directive_a", module_mode, module_mode.directive); + } + module_mode = true; + if (next_token.identifier) { + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + const names = []; + advance("{"); + if (next_token.id !== "}") { + while (true) { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (next_token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token; + if (!rx_module.test(token.value)) { + warn("bad_module_name_a", token); + } + froms.push(token.value); + semicolon(); + return the_import; +}); +stmt("let", do_var); +stmt("return", function () { + const the_return = token; + not_top_level(the_return); + if (functionage.finally > 0) { + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (next_token.id !== ";" && the_return.line === next_token.line) { + the_return.expression = expression(10); + } + advance(";"); + return the_return; +}); +stmt("switch", function () { + let dups = []; + let last; + let stmts; + const the_cases = []; + let the_disrupt = true; + const the_switch = token; + not_top_level(the_switch); + if (functionage.finally > 0) { + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token.free = true; + the_switch.expression = expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + (function major() { + const the_case = next_token; + the_case.arity = "statement"; + the_case.expression = []; + (function minor() { + advance("case"); + token.switch = true; + const exp = expression(0); + if (dups.some(function (thing) { + return are_similar(thing, exp); + })) { + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (next_token.id === "case") { + return minor(); + } + }()); + stmts = statements(); + if (stmts.length < 1) { + warn("expected_statements_a"); + return; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "break;", + artifact(next_token) + ); + } + if (next_token.id === "case") { + return major(); + } + }()); + dups = undefined; + if (next_token.id === "default") { + const the_default = next_token; + advance("default"); + token.switch = true; + advance(":"); + the_switch.else = statements(); + if (the_switch.else.length < 1) { + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + const the_last = the_switch.else[the_switch.else.length - 1]; + if (the_last.id === "break" && the_last.label === undefined) { + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; +}); +stmt("throw", function () { + const the_throw = token; + the_throw.disrupt = true; + the_throw.expression = expression(10); + semicolon(); + if (functionage.try > 0) { + warn("unexpected_a", the_throw); + } + return the_throw; +}); +stmt("try", function () { + let the_catch; + let the_disrupt; + const the_try = token; + if (functionage.try > 0) { + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (next_token.id === "catch") { + let ignored = "ignore"; + the_catch = next_token; + the_try.catch = the_catch; + advance("catch"); + advance("("); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + if (next_token.id !== "ignore") { + ignored = undefined; + the_catch.name = next_token; + enroll(next_token, "exception", true); + } + advance(); + advance(")"); + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "catch", + artifact(next_token) + ); + + } + if (next_token.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; +}); +stmt("var", do_var); +stmt("while", function () { + const the_while = token; + not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; +}); +stmt("with", function () { + stop("unexpected_a", token); +}); + +ternary("?", ":"); + +// Ambulation of the parse tree. + +function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; +} + +function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all of the +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + } + }; +} + +const posts = empty(); +const pres = empty(); +const preaction = action(pres); +const postaction = action(posts); +const preamble = amble(pres); +const postamble = amble(posts); + +function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.id === "function") { + walk_statement(thing.block); + } + if (thing.arity === "pre" || thing.arity === "post") { + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + warn("unexpected_statement_a", thing); + } + postamble(thing); + } + } +} + +function walk_statement(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_statement); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + ) { + warn( + (thing.id === "(string)" && thing.value === "use strict") + ? "unexpected_a" + : "unexpected_expression_a", + thing + ); + } + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + } +} + +function lookup(thing) { + if (thing.arity === "variable") { + +// Look up the variable in the current context. + + let the_variable = functionage.context[thing.id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable === undefined) { + stack.forEach(function (outer) { + const a_variable = outer.context[thing.id]; + if ( + a_variable !== undefined + && a_variable.role !== "label" + ) { + the_variable = a_variable; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (the_variable === undefined) { + if (declared_globals[thing.id] === undefined) { + warn("undeclared_a", thing); + return; + } + the_variable = { + dead: false, + parent: global, + id: thing.id, + init: true, + role: "variable", + used: 0, + writable: false + }; + global.context[thing.id] = the_variable; + } + the_variable.closure = true; + functionage.context[thing.id] = the_variable; + } else if (the_variable.role === "label") { + warn("label_a", thing); + } + if ( + the_variable.dead + && ( + the_variable.calls === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + ) { + warn("out_of_scope_a", thing); + } + return the_variable; + } +} + +function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); +} + +function preaction_function(thing) { + if (thing.arity === "statement" && blockage.body !== true) { + warn("unexpected_a", thing); + } + if (thing.level === 1) { + if ( + module_mode === true + || global.strict !== undefined + || thing.complex + ) { + if (thing.id !== "=>" && thing.block.strict !== undefined) { + warn("unexpected_a", thing.block.strict); + } + } else { + if (thing.block.strict === undefined) { + warn("use_strict", thing); + } + } + } + stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); +} + +function bitwise_check(thing) { + if (!option.bitwise && bitwiseop[thing.id] === true) { + warn("unexpected_a", thing); + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + warn("unexpected_a", thing); + } +} + +function pop_block() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); +} + +function activate(name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.init = true; + } + } + blockage.live.push(name); +} + +function action_var(thing) { + thing.names.forEach(activate); +} + +preaction("assignment", bitwise_check); +preaction("binary", bitwise_check); +preaction("binary", function (thing) { + if (relationop[thing.id] === true) { + const left = thing.expression[0]; + const right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + warn("expected_string_a", right); + } + } else { + const value = right.value; + if (value === "null" || value === "undefined") { + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + warn("expected_type_string_a", right, value); + } + } + } + } +}); +preaction("binary", "==", function (thing) { + warn("expected_a_b", thing, "===", "=="); +}); +preaction("binary", "!=", function (thing) { + warn("expected_a_b", thing, "!==", "!="); +}); +preaction("binary", "=>", preaction_function); +preaction("binary", "||", function (thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + warn("and", thang); + } + }); +}); +preaction("binary", "(", function (thing) { + const left = thing.expression[0]; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + const parent = functionage.name.parent; + if (parent) { + const left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + && left_variable.dead + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } +}); +preaction("binary", "in", function (thing) { + warn("infix_in", thing); +}); +preaction("binary", "instanceof", function (thing) { + warn("unexpected_a", thing); +}); +preaction("binary", ".", function (thing) { + if (thing.expression.new) { + thing.new = true; + } +}); +preaction("statement", "{", function (thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; +}); +preaction("statement", "for", function (thing) { + if (thing.name !== undefined) { + const the_variable = lookup(thing.name); + if (the_variable !== undefined) { + the_variable.init = true; + if (!the_variable.writable) { + warn("bad_assignment_a", thing.name); + } + } + } + walk_statement(thing.initial); +}); +preaction("statement", "function", preaction_function); +preaction("unary", "~", bitwise_check); +preaction("unary", "function", preaction_function); +preaction("variable", function (thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } +}); + +function init_variable(name) { + const the_variable = lookup(name); + if (the_variable !== undefined) { + if (the_variable.writable) { + the_variable.init = true; + return; + } + } + warn("bad_assignment_a", name); +} + +postaction("assignment", "+=", function (thing) { + let right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } +}); +postaction("assignment", function (thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + if (thing.id === "=") { + if (thing.names !== undefined) { + if (Array.isArray(thing.names)) { + thing.names.forEach(init_variable); + } else { + init_variable(thing.names); + } + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.writable !== true) { + warn("bad_assignment_a", lvalue); + } + } + const right = syntax[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + warn("unexpected_a", thing.expression[1]); + } + } +}); + +function postaction_function(thing) { + delete functionage.finally; + delete functionage.loop; + delete functionage.switch; + delete functionage.try; + functionage = stack.pop(); + if (thing.wrapped) { + warn("unexpected_parens", thing); + } + return pop_block(); +} + +postaction("binary", function (thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || are_similar(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option.convert) { + if (thing.expression[0].value === "") { + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === ".") { + if (thing.expression.id === "RegExp") { + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } +}); +postaction("binary", "&&", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "||", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "=>", postaction_function); +postaction("binary", "(", function (thing) { + let left = thing.expression[0]; + let the_new; + let arg; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option.eval) { + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + warn( + "expected_a_before_b", + left, + "new", + artifact(left) + ); + } + } + } else if (left.id === ".") { + let cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + cack = !cack; + } + if (rx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + warn("unexpected_a", the_new); + } else { + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + const paren = left.expression; + if (paren.id === "(") { + const array = paren.expression; + if (array.length === 1) { + const new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } +}); +postaction("binary", "[", function (thing) { + if (thing.expression[0].id === "RegExp") { + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + warn("weird_expression_a", thing.expression[1]); + } +}); +postaction("statement", "{", pop_block); +postaction("statement", "const", action_var); +postaction("statement", "export", top_level_only); +postaction("statement", "for", function (thing) { + walk_statement(thing.inc); +}); +postaction("statement", "function", postaction_function); +postaction("statement", "import", function (the_thing) { + const name = the_thing.name; + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return top_level_only(the_thing); +}); +postaction("statement", "let", action_var); +postaction("statement", "try", function (thing) { + if (thing.catch !== undefined) { + const the_name = thing.catch.name; + if (the_name !== undefined) { + const the_variable = functionage.context[the_name.id]; + the_variable.dead = false; + the_variable.init = true; + } + walk_statement(thing.catch.block); + } +}); +postaction("statement", "var", action_var); +postaction("ternary", function (thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || are_similar(thing.expression[1], thing.expression[2]) + ) { + warn("unexpected_a", thing); + } else if (are_similar(thing.expression[0], thing.expression[1])) { + warn("expected_a_b", thing, "||", "?"); + } else if (are_similar(thing.expression[0], thing.expression[2])) { + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + warn("wrap_condition", thing.expression[0]); + } +}); +postaction("unary", function (thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option.convert) { + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } +}); +postaction("unary", "function", postaction_function); +postaction("unary", "+", function (thing) { + if (!option.convert) { + warn("expected_a_b", thing, "Number(...)", "+"); + } + const right = thing.expression; + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } +}); + +function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + if (id !== "ignore") { + const name = the_function.context[id]; + if (name.parent === the_function) { + if ( + name.used === 0 + && ( + name.role !== "function" + || name.parent.arity !== "unary" + ) + ) { + warn("unused_a", name); + } else if (!name.init) { + warn("uninitialized_a", name); + } + } + } + }); +} + +function uninitialized_and_unused() { + +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (module_mode === true || option.node) { + delve(global); + } + functions.forEach(delve); +} + +// Go through the token list, looking at usage of whitespace. + +function whitage() { + let closer = "(end)"; + let free = false; + let left = global; + let margin = 0; + let nr_comments_skipped = 0; + let open = true; + let right; + + function expected_at(at) { + warn( + "expected_a_at_b_c", + right, + artifact(right), + fudge + at, + artifact_column(right) + ); + } + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function no_space() { + if (left.line === right.line) { + if (left.thru !== right.from && nr_comments_skipped === 0) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (open) { + const at = ( + free + ? margin + : margin + 8 + ); + if (right.from < at) { + expected_at(at); + } + } else { + if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (free) { + if (right.from < margin) { + expected_at(margin); + } + } else { + const mislaid = ( + stack.length > 0 + ? stack[stack.length - 1].right + : undefined + ); + if (!open && mislaid !== undefined) { + warn( + "expected_a_next_at_b", + mislaid, + artifact(mislaid.id), + margin + 4 + fudge + ); + } else if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + stack = []; + tokens.forEach(function (the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + + const new_closer = opener[left.id]; + if (typeof new_closer === "string") { + if (new_closer !== right.id) { + stack.push({ + closer: closer, + free: free, + margin: margin, + open: open, + right: right + }); + closer = new_closer; + if (left.line !== right.line) { + free = closer === ")" && left.free; + open = true; + margin += 4; + if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (right.switch) { + at_margin(-4); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + free = false; + open = false; + no_space_only(); + } + } else { + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like {]) have already been detected. + + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } + } else { + +// If right is a closer, then pop the previous state. + + if (right.id === closer) { + const previous = stack.pop(); + margin = previous.margin; + if (open && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + closer = previous.closer; + free = previous.free; + open = previous.open; + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-4); + } else if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + one_space(); + } else { + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + at_margin(0); + } else { + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + no_space(); + } else if ( + left.id === "." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + ) { + no_space_only(); + } else if (right.id === ".") { + no_space_only(); + } else if (left.id === ";") { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + one_space_only(); + } else if (right.statement === true) { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.id === "var" + || left.id === "const" + || left.id === "let" + || left.id === "return" + ) { + stack.push({ + closer: closer, + free: free, + margin: margin, + open: open + }); + closer = ";"; + free = false; + open = left.open; + if (open) { + margin = margin + 4; + at_margin(0); + } else { + one_space_only(); + } + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +// The jslint function itself. + +export default function jslint( + source = "", + option_object = empty(), + global_array = [] +) { + try { + warnings = []; + option = Object.assign(empty(), option_object); + anon = "anonymous"; + block_stack = []; + declared_globals = empty(); + directive_mode = true; + directives = []; + early_stop = true; + exports = empty(); + froms = []; + fudge = ( + option.fudge + ? 1 + : 0 + ); + functions = []; + global = { + id: "(global)", + body: true, + context: empty(), + from: 0, + level: 0, + line: 0, + live: [], + loop: 0, + switch: 0, + thru: 0 + }; + blockage = global; + functionage = global; + json_mode = false; + mega_mode = false; + module_mode = false; + next_token = global; + property = empty(); + shebang = false; + stack = []; + tenure = undefined; + token = global; + token_nr = 0; + var_mode = undefined; + populate(standard, declared_globals, false); + populate(global_array, declared_globals, false); + Object.keys(option).forEach(function (name) { + if (option[name] === true) { + const allowed = allowed_option[name]; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } + }); + tokenize(source); + advance(); + if (json_mode) { + tree = json_value(); + advance("(end)"); + } else { + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option.browser) { + if (next_token.id === ";") { + advance(";"); + } + } else { + +// If we are not in a browser, then the file form of strict pragma may be used. + + if ( + next_token.value === "use strict" + ) { + global.strict = next_token; + advance("(string)"); + advance(";"); + } + } + tree = statements(); + advance("(end)"); + functionage = global; + walk_statement(tree); + if (module_mode && global.strict !== undefined) { + warn("unexpected_a", global.strict); + } + if (warnings.length === 0) { + uninitialized_and_unused(); + if (!option.white) { + whitage(); + } + } + } + if (!option.browser) { + directives.forEach(function (comment) { + if (comment.directive === "global") { + warn("missing_browser", comment); + } + }); + } + early_stop = false; + } catch (e) { + if (e.name !== "JSLintError") { + warnings.push(e); + } + } + return { + directives, + edition: "2018-09-26", + exports, + froms, + functions, + global, + id: "(JSLint)", + json: json_mode, + lines, + module: module_mode === true, + ok: warnings.length === 0 && !early_stop, + option, + property, + shebang: ( + shebang + ? lines[0] + : undefined + ), + stop: early_stop, + tokens, + tree, + warnings: warnings.sort(function (a, b) { + return a.line - b.line || a.column - b.column; + }) + }; +}; diff --git a/branch.bug_ternary_use_open/report.js b/branch.bug_ternary_use_open/report.js new file mode 100644 index 000000000..f123e9055 --- /dev/null +++ b/branch.bug_ternary_use_open/report.js @@ -0,0 +1,221 @@ +// report.js +// 2018-07-29 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Generate JSLint HTML reports. + +/*property + closure, column, context, edition, error, exports, filter, forEach, froms, + fudge, function, functions, global, id, isArray, join, json, keys, length, + level, line, lines, message, module, name, names, option, parameters, + parent, property, push, replace, role, signature, sort, stop, warnings +*/ + +const rx_amp = /&/g; +const rx_gt = />/g; +const rx_lt = / with less destructive entities. + + return String( + string + ).replace( + rx_amp, + "&" + ).replace( + rx_lt, + "<" + ).replace( + rx_gt, + ">" + ); +} + +export default { + error: function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + let fudge = Number(Boolean(data.option.fudge)); + let output = []; + if (data.stop) { + output.push("
    JSLint was unable to finish.
    "); + } + data.warnings.forEach(function (warning) { + output.push( + "
    ", + entityify(warning.line + fudge), + ".", + entityify(warning.column + fudge), + "
    ", + entityify(warning.message), + "
    ", + entityify(data.lines[warning.line] || ""), + "" + ); + }); + return output.join(""); + }, + + function: function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + let fudge = Number(Boolean(data.option.fudge)); + let mode = ( + data.module + ? "module" + : "global" + ); + let output = []; + + if (data.json) { + return ( + data.warnings.length === 0 + ? "
    JSON: good.
    " + : "
    JSON: bad.
    " + ); + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + let global = Object.keys(data.global.context).sort(); + let froms = data.froms.sort(); + let exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + let context = the_function.context; + let list = Object.keys(context); + output.push( + "
    ", + entityify(the_function.line + fudge), + "
    ", + ( + the_function.name === "=>" + ? entityify(the_function.signature) + " =>" + : ( + typeof the_function.name === "string" + ? "«" + entityify(the_function.name) + "»" + : "" + entityify(the_function.name.id) + "" + ) + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + let params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.parent === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.parent === the_function + ); + })); + detail("outer", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.parent !== the_function + && the_variable.parent.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].parent.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + output.push( + "
    JSLint edition ", + entityify(data.edition), + "
    " + ); + return output.join(""); + }, + + property: function property_directive(data) { + +// Produce the /*property*/ directive. + + let not_first = false; + let output = ["/*property"]; + let length = 1111; + let properties = Object.keys(data.property); + + if (properties.length > 0) { + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length >= 80) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); + } + } +}; diff --git a/branch.ci/.coverage/coverage.txt b/branch.ci/.coverage/coverage.txt new file mode 100644 index 000000000..4045ea631 --- /dev/null +++ b/branch.ci/.coverage/coverage.txt @@ -0,0 +1,10 @@ +coverage-report ++----------------------------------+-------------+ +| files covered | lines | ++----------------------------------+-------------+ +| ./ | 60.15 % | +| *******************_____________ | 3019 / 5019 | ++----------------------------------+-------------+ +| ./jslint.js | 60.15 % | +| *******************_____________ | 3019 / 5019 | ++----------------------------------+-------------+ diff --git a/branch.ci/.coverage/coverage_v8.json b/branch.ci/.coverage/coverage_v8.json new file mode 100644 index 000000000..5ca0e8d11 --- /dev/null +++ b/branch.ci/.coverage/coverage_v8.json @@ -0,0 +1 @@ +{"result":[{"scriptId":"6","url":"internal/per_context/primordials.js","functions":[{"functionName":"uncurryThis","ranges":[{"startOffset":1000,"endOffset":1096,"count":5}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":1038,"endOffset":1093,"count":714}],"isBlockCoverage":true}]},{"scriptId":"9","url":"internal/bootstrap/loaders.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":10314,"count":1}],"isBlockCoverage":true},{"functionName":"binding","ranges":[{"startOffset":3655,"endOffset":4052,"count":0}],"isBlockCoverage":false},{"functionName":"_linkedBinding","ranges":[{"startOffset":4082,"endOffset":4290,"count":0}],"isBlockCoverage":false},{"functionName":"internalBinding","ranges":[{"startOffset":4470,"endOffset":4732,"count":84},{"startOffset":4572,"endOffset":4712,"count":26}],"isBlockCoverage":true},{"functionName":"getOwn","ranges":[{"startOffset":4877,"endOffset":5031,"count":198},{"startOffset":5013,"endOffset":5028,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":5398,"endOffset":5496,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":5457,"endOffset":5491,"count":229}],"isBlockCoverage":true},{"functionName":"NativeModule","ranges":[{"startOffset":5501,"endOffset":6253,"count":229}],"isBlockCoverage":true},{"functionName":"exposeInternals","ranges":[{"startOffset":6403,"endOffset":6629,"count":0}],"isBlockCoverage":false},{"functionName":"exists","ranges":[{"startOffset":6640,"endOffset":6693,"count":0}],"isBlockCoverage":false},{"functionName":"canBeRequiredByUsers","ranges":[{"startOffset":6704,"endOffset":6820,"count":2},{"startOffset":6788,"endOffset":6815,"count":1}],"isBlockCoverage":true},{"functionName":"compileForPublicLoader","ranges":[{"startOffset":6892,"endOffset":7586,"count":1},{"startOffset":6955,"endOffset":7147,"count":0},{"startOffset":7470,"endOffset":7474,"count":0}],"isBlockCoverage":true},{"functionName":"getESMFacade","ranges":[{"startOffset":7590,"endOffset":8141,"count":2},{"startOffset":7628,"endOffset":8140,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":7868,"endOffset":7981,"count":1}],"isBlockCoverage":true},{"functionName":"syncExports","ranges":[{"startOffset":8437,"endOffset":8781,"count":2},{"startOffset":8556,"endOffset":8771,"count":198},{"startOffset":8633,"endOffset":8642,"count":0}],"isBlockCoverage":true},{"functionName":"compileForInternalLoader","ranges":[{"startOffset":8785,"endOffset":9370,"count":319},{"startOffset":8834,"endOffset":8849,"count":76},{"startOffset":8851,"endOffset":8885,"count":247},{"startOffset":8885,"endOffset":9024,"count":72},{"startOffset":9024,"endOffset":9059,"count":0},{"startOffset":9060,"endOffset":9081,"count":72},{"startOffset":9235,"endOffset":9369,"count":72}],"isBlockCoverage":true},{"functionName":"nativeModuleRequire","ranges":[{"startOffset":9568,"endOffset":9939,"count":323},{"startOffset":9626,"endOffset":9657,"count":5},{"startOffset":9657,"endOffset":9841,"count":318},{"startOffset":9841,"endOffset":9896,"count":0},{"startOffset":9896,"endOffset":9938,"count":318}],"isBlockCoverage":true},{"functionName":"requireWithFallbackInDeps","ranges":[{"startOffset":10055,"endOffset":10227,"count":0}],"isBlockCoverage":false}]},{"scriptId":"10","url":"internal/bootstrap/node.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":12616,"count":1}],"isBlockCoverage":true},{"functionName":"process.openStdin","ranges":[{"startOffset":3399,"endOffset":3469,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":6160,"endOffset":6322,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":6424,"endOffset":6596,"count":0}],"isBlockCoverage":false},{"functionName":"setupPrepareStackTrace","ranges":[{"startOffset":9383,"endOffset":9969,"count":1}],"isBlockCoverage":true},{"functionName":"setupProcessObject","ranges":[{"startOffset":9971,"endOffset":10576,"count":1}],"isBlockCoverage":true},{"functionName":"setupGlobalProxy","ranges":[{"startOffset":10578,"endOffset":10755,"count":1}],"isBlockCoverage":true},{"functionName":"setupBuffer","ranges":[{"startOffset":10757,"endOffset":11193,"count":1}],"isBlockCoverage":true},{"functionName":"createGlobalConsole","ranges":[{"startOffset":11195,"endOffset":11876,"count":1}],"isBlockCoverage":true},{"functionName":"exposeNamespace","ranges":[{"startOffset":11928,"endOffset":12126,"count":1}],"isBlockCoverage":true},{"functionName":"exposeInterface","ranges":[{"startOffset":12178,"endOffset":12376,"count":4}],"isBlockCoverage":true},{"functionName":"defineOperation","ranges":[{"startOffset":12436,"endOffset":12615,"count":7}],"isBlockCoverage":true}]},{"scriptId":"11","url":"internal/errors.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":53129,"count":1}],"isBlockCoverage":false},{"functionName":"prepareStackTrace","ranges":[{"startOffset":1404,"endOffset":2120,"count":2},{"startOffset":1581,"endOffset":1697,"count":0},{"startOffset":1824,"endOffset":1846,"count":0},{"startOffset":2027,"endOffset":2056,"count":0}],"isBlockCoverage":true},{"functionName":"maybeOverridePrepareStackTrace","ranges":[{"startOffset":2162,"endOffset":2869,"count":2},{"startOffset":2431,"endOffset":2497,"count":0},{"startOffset":2778,"endOffset":2844,"count":0}],"isBlockCoverage":true},{"functionName":"lazyInternalUtil","ranges":[{"startOffset":2959,"endOffset":3085,"count":0}],"isBlockCoverage":false},{"functionName":"lazyInternalUtilInspect","ranges":[{"startOffset":3119,"endOffset":3281,"count":0}],"isBlockCoverage":false},{"functionName":"lazyBuffer","ranges":[{"startOffset":3295,"endOffset":3404,"count":0}],"isBlockCoverage":false},{"functionName":"SystemError","ranges":[{"startOffset":3906,"endOffset":6444,"count":0}],"isBlockCoverage":false},{"functionName":"toString","ranges":[{"startOffset":6448,"endOffset":6523,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":6527,"endOffset":6716,"count":0}],"isBlockCoverage":false},{"functionName":"makeSystemErrorWithCode","ranges":[{"startOffset":6720,"endOffset":6865,"count":4}],"isBlockCoverage":true},{"functionName":"NodeError","ranges":[{"startOffset":6811,"endOffset":6858,"count":0}],"isBlockCoverage":false},{"functionName":"makeNodeErrorWithCode","ranges":[{"startOffset":6867,"endOffset":7622,"count":232}],"isBlockCoverage":true},{"functionName":"NodeError","ranges":[{"startOffset":6955,"endOffset":7536,"count":2},{"startOffset":7045,"endOffset":7254,"count":0}],"isBlockCoverage":true},{"functionName":"toString","ranges":[{"startOffset":7542,"endOffset":7615,"count":0}],"isBlockCoverage":false},{"functionName":"hideStackFrames","ranges":[{"startOffset":7694,"endOffset":8105,"count":21}],"isBlockCoverage":true},{"functionName":"hidden","ranges":[{"startOffset":7734,"endOffset":8102,"count":51},{"startOffset":7898,"endOffset":7962,"count":44},{"startOffset":8046,"endOffset":8092,"count":44}],"isBlockCoverage":true},{"functionName":"addCodeToName","ranges":[{"startOffset":8107,"endOffset":8723,"count":2},{"startOffset":8205,"endOffset":8260,"count":0},{"startOffset":8545,"endOffset":8689,"count":0}],"isBlockCoverage":true},{"functionName":"E","ranges":[{"startOffset":8835,"endOffset":9343,"count":233},{"startOffset":9077,"endOffset":9122,"count":4},{"startOffset":9122,"endOffset":9176,"count":229},{"startOffset":9211,"endOffset":9321,"count":3}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":9238,"endOffset":9315,"count":3}],"isBlockCoverage":true},{"functionName":"getMessage","ranges":[{"startOffset":9345,"endOffset":10149,"count":2},{"startOffset":9446,"endOffset":9482,"count":1},{"startOffset":9773,"endOffset":9831,"count":0},{"startOffset":10053,"endOffset":10148,"count":0}],"isBlockCoverage":true},{"functionName":"lazyUv","ranges":[{"startOffset":10167,"endOffset":10271,"count":0}],"isBlockCoverage":false},{"functionName":"uvErrmapGet","ranges":[{"startOffset":10328,"endOffset":10498,"count":0}],"isBlockCoverage":false},{"functionName":"uvException","ranges":[{"startOffset":10791,"endOffset":11988,"count":0}],"isBlockCoverage":false},{"functionName":"uvExceptionWithHostPort","ranges":[{"startOffset":12301,"endOffset":13207,"count":0}],"isBlockCoverage":false},{"functionName":"errnoException","ranges":[{"startOffset":13386,"endOffset":14092,"count":0}],"isBlockCoverage":false},{"functionName":"exceptionWithHostPort","ranges":[{"startOffset":14445,"endOffset":15662,"count":0}],"isBlockCoverage":false},{"functionName":"dnsException","ranges":[{"startOffset":15826,"endOffset":17342,"count":0}],"isBlockCoverage":false},{"functionName":"connResetException","ranges":[{"startOffset":17344,"endOffset":17499,"count":0}],"isBlockCoverage":false},{"functionName":"isStackOverflowError","ranges":[{"startOffset":17789,"endOffset":18167,"count":0}],"isBlockCoverage":false},{"functionName":"addNumericalSeparator","ranges":[{"startOffset":18248,"endOffset":18484,"count":0}],"isBlockCoverage":false},{"functionName":"beforeInspector","ranges":[{"startOffset":18763,"endOffset":19154,"count":0}],"isBlockCoverage":false},{"functionName":"afterInspector","ranges":[{"startOffset":19158,"endOffset":20496,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":22263,"endOffset":22427,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":25266,"endOffset":25381,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":25670,"endOffset":25764,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":28045,"endOffset":28273,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":30006,"endOffset":30224,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":31974,"endOffset":32274,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":32316,"endOffset":32460,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":32503,"endOffset":35708,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":35749,"endOffset":36005,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":36397,"endOffset":36560,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":37222,"endOffset":37357,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":37398,"endOffset":37722,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":37881,"endOffset":38029,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":38073,"endOffset":38847,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":39225,"endOffset":39389,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":39442,"endOffset":39773,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":39817,"endOffset":40124,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":40494,"endOffset":40569,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":40613,"endOffset":40901,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":41229,"endOffset":41660,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":42336,"endOffset":42859,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":42953,"endOffset":43054,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":43756,"endOffset":44454,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":44505,"endOffset":44697,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":44746,"endOffset":45062,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":45095,"endOffset":45972,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":46424,"endOffset":46683,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":48033,"endOffset":48202,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":50100,"endOffset":50231,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":50824,"endOffset":51107,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":52087,"endOffset":52185,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":52379,"endOffset":52734,"count":0}],"isBlockCoverage":false}]},{"scriptId":"12","url":"internal/util.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":12301,"count":1}],"isBlockCoverage":false},{"functionName":"removeColors","ranges":[{"startOffset":975,"endOffset":1044,"count":0}],"isBlockCoverage":false},{"functionName":"isError","ranges":[{"startOffset":1046,"endOffset":1296,"count":0}],"isBlockCoverage":false},{"functionName":"deprecate","ranges":[{"startOffset":1581,"endOffset":2678,"count":11},{"startOffset":1655,"endOffset":1675,"count":0},{"startOffset":1735,"endOffset":1790,"count":0},{"startOffset":2432,"endOffset":2654,"count":10}],"isBlockCoverage":true},{"functionName":"deprecated","ranges":[{"startOffset":1816,"endOffset":2290,"count":0}],"isBlockCoverage":false},{"functionName":"decorateErrorStack","ranges":[{"startOffset":2680,"endOffset":3019,"count":0}],"isBlockCoverage":false},{"functionName":"assertCrypto","ranges":[{"startOffset":3021,"endOffset":3095,"count":0}],"isBlockCoverage":false},{"functionName":"normalizeEncoding","ranges":[{"startOffset":3274,"endOffset":3405,"count":1},{"startOffset":3344,"endOffset":3362,"count":0},{"startOffset":3378,"endOffset":3404,"count":0}],"isBlockCoverage":true},{"functionName":"slowCases","ranges":[{"startOffset":3407,"endOffset":4987,"count":0}],"isBlockCoverage":false},{"functionName":"emitExperimentalWarning","ranges":[{"startOffset":4989,"endOffset":5277,"count":0}],"isBlockCoverage":false},{"functionName":"filterDuplicateStrings","ranges":[{"startOffset":5279,"endOffset":5587,"count":0}],"isBlockCoverage":false},{"functionName":"cachedResult","ranges":[{"startOffset":5589,"endOffset":5732,"count":0}],"isBlockCoverage":false},{"functionName":"createClassWrapper","ranges":[{"startOffset":5997,"endOffset":6362,"count":0}],"isBlockCoverage":false},{"functionName":"getSignalsToNamesMapping","ranges":[{"startOffset":6391,"endOffset":6669,"count":0}],"isBlockCoverage":false},{"functionName":"convertToValidSignal","ranges":[{"startOffset":6671,"endOffset":6978,"count":0}],"isBlockCoverage":false},{"functionName":"getConstructorOf","ranges":[{"startOffset":6980,"endOffset":7326,"count":0}],"isBlockCoverage":false},{"functionName":"getSystemErrorName","ranges":[{"startOffset":7328,"endOffset":7457,"count":0}],"isBlockCoverage":false},{"functionName":"promisify","ranges":[{"startOffset":7602,"endOffset":9073,"count":3},{"startOffset":7675,"endOffset":7740,"count":0},{"startOffset":7784,"endOffset":8105,"count":0}],"isBlockCoverage":true},{"functionName":"fn","ranges":[{"startOffset":8305,"endOffset":8786,"count":0}],"isBlockCoverage":false},{"functionName":"join","ranges":[{"startOffset":9168,"endOffset":9490,"count":0}],"isBlockCoverage":false},{"functionName":"spliceOne","ranges":[{"startOffset":9631,"endOffset":9758,"count":0}],"isBlockCoverage":false},{"functionName":"isInsideNodeModules","ranges":[{"startOffset":9840,"endOffset":11012,"count":0}],"isBlockCoverage":false},{"functionName":"once","ranges":[{"startOffset":11014,"endOffset":11172,"count":0}],"isBlockCoverage":false},{"functionName":"sleep","ranges":[{"startOffset":11195,"endOffset":11410,"count":0}],"isBlockCoverage":false}]},{"scriptId":"13","url":"events.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":22365,"count":1}],"isBlockCoverage":false},{"functionName":"EventEmitter","ranges":[{"startOffset":1850,"endOffset":1919,"count":1}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":2230,"endOffset":2286,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":2290,"endOffset":2524,"count":0}],"isBlockCoverage":false},{"functionName":"checkListener","ranges":[{"startOffset":3077,"endOffset":3227,"count":4},{"startOffset":3150,"endOffset":3225,"count":0}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":3316,"endOffset":3364,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":3373,"endOffset":3648,"count":0}],"isBlockCoverage":false},{"functionName":"EventEmitter.init","ranges":[{"startOffset":3674,"endOffset":4404,"count":1},{"startOffset":3725,"endOffset":3785,"count":0},{"startOffset":3929,"endOffset":3954,"count":0},{"startOffset":3956,"endOffset":4215,"count":0}],"isBlockCoverage":true},{"functionName":"addCatch","ranges":[{"startOffset":4407,"endOffset":4966,"count":0}],"isBlockCoverage":false},{"functionName":"emitUnhandledRejectionOrErr","ranges":[{"startOffset":4968,"endOffset":5626,"count":0}],"isBlockCoverage":false},{"functionName":"setMaxListeners","ranges":[{"startOffset":5797,"endOffset":5996,"count":0}],"isBlockCoverage":false},{"functionName":"_getMaxListeners","ranges":[{"startOffset":5999,"endOffset":6148,"count":0}],"isBlockCoverage":false},{"functionName":"getMaxListeners","ranges":[{"startOffset":6191,"endOffset":6254,"count":0}],"isBlockCoverage":false},{"functionName":"identicalSequenceRange","ranges":[{"startOffset":6382,"endOffset":6958,"count":0}],"isBlockCoverage":false},{"functionName":"enhanceStackTrace","ranges":[{"startOffset":6960,"endOffset":7566,"count":0}],"isBlockCoverage":false},{"functionName":"emit","ranges":[{"startOffset":7598,"endOffset":9881,"count":4},{"startOffset":7743,"endOffset":7781,"count":0},{"startOffset":7789,"endOffset":7823,"count":0},{"startOffset":7847,"endOffset":7876,"count":0},{"startOffset":7882,"endOffset":7919,"count":0},{"startOffset":7991,"endOffset":8923,"count":0},{"startOffset":8991,"endOffset":9004,"count":0},{"startOffset":9259,"endOffset":9277,"count":0},{"startOffset":9279,"endOffset":9328,"count":0},{"startOffset":9332,"endOffset":9863,"count":0}],"isBlockCoverage":true},{"functionName":"_addListener","ranges":[{"startOffset":9884,"endOffset":11939,"count":4},{"startOffset":10064,"endOffset":10148,"count":0},{"startOffset":10333,"endOffset":10613,"count":3},{"startOffset":10410,"endOffset":10429,"count":0},{"startOffset":10816,"endOffset":11919,"count":0}],"isBlockCoverage":true},{"functionName":"addListener","ranges":[{"startOffset":11978,"endOffset":12070,"count":4}],"isBlockCoverage":true},{"functionName":"prependListener","ranges":[{"startOffset":12183,"endOffset":12286,"count":0}],"isBlockCoverage":false},{"functionName":"onceWrapper","ranges":[{"startOffset":12289,"endOffset":12553,"count":0}],"isBlockCoverage":false},{"functionName":"_onceWrap","ranges":[{"startOffset":12555,"endOffset":12796,"count":0}],"isBlockCoverage":false},{"functionName":"once","ranges":[{"startOffset":12828,"endOffset":12954,"count":0}],"isBlockCoverage":false},{"functionName":"prependOnceListener","ranges":[{"startOffset":13006,"endOffset":13176,"count":0}],"isBlockCoverage":false},{"functionName":"removeListener","ranges":[{"startOffset":13298,"endOffset":14592,"count":0}],"isBlockCoverage":false},{"functionName":"removeAllListeners","ranges":[{"startOffset":14712,"endOffset":16038,"count":0}],"isBlockCoverage":false},{"functionName":"_listeners","ranges":[{"startOffset":16041,"endOffset":16436,"count":0}],"isBlockCoverage":false},{"functionName":"listeners","ranges":[{"startOffset":16473,"endOffset":16540,"count":0}],"isBlockCoverage":false},{"functionName":"rawListeners","ranges":[{"startOffset":16581,"endOffset":16652,"count":0}],"isBlockCoverage":false},{"functionName":"EventEmitter.listenerCount","ranges":[{"startOffset":16684,"endOffset":16852,"count":0}],"isBlockCoverage":false},{"functionName":"listenerCount","ranges":[{"startOffset":16909,"endOffset":17199,"count":0}],"isBlockCoverage":false},{"functionName":"eventNames","ranges":[{"startOffset":17237,"endOffset":17330,"count":0}],"isBlockCoverage":false},{"functionName":"arrayClone","ranges":[{"startOffset":17333,"endOffset":17793,"count":0}],"isBlockCoverage":false},{"functionName":"unwrapListeners","ranges":[{"startOffset":17795,"endOffset":18009,"count":0}],"isBlockCoverage":false},{"functionName":"once","ranges":[{"startOffset":18011,"endOffset":18576,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":18654,"endOffset":18675,"count":0}],"isBlockCoverage":false},{"functionName":"createIterResult","ranges":[{"startOffset":18690,"endOffset":18758,"count":0}],"isBlockCoverage":false},{"functionName":"addErrorHandlerIfEventEmitter","ranges":[{"startOffset":18760,"endOffset":18943,"count":0}],"isBlockCoverage":false},{"functionName":"eventTargetAgnosticRemoveListener","ranges":[{"startOffset":18945,"endOffset":19330,"count":0}],"isBlockCoverage":false},{"functionName":"eventTargetAgnosticAddListener","ranges":[{"startOffset":19332,"endOffset":19921,"count":0}],"isBlockCoverage":false},{"functionName":"on","ranges":[{"startOffset":19923,"endOffset":22364,"count":0}],"isBlockCoverage":false}]},{"scriptId":"14","url":"internal/util/inspect.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":71569,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":2893,"endOffset":2929,"count":62}],"isBlockCoverage":true},{"functionName":"isUndetectableObject","ranges":[{"startOffset":3020,"endOffset":3070,"count":0}],"isBlockCoverage":false},{"functionName":"getUserOptions","ranges":[{"startOffset":6215,"endOffset":7666,"count":0}],"isBlockCoverage":false},{"functionName":"inspect","ranges":[{"startOffset":7961,"endOffset":9878,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":9970,"endOffset":10015,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":10019,"endOffset":10227,"count":0}],"isBlockCoverage":false},{"functionName":"defineColorAlias","ranges":[{"startOffset":11964,"endOffset":12206,"count":12}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":12059,"endOffset":12099,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":12105,"endOffset":12151,"count":0}],"isBlockCoverage":false},{"functionName":"addQuotes","ranges":[{"startOffset":13216,"endOffset":13374,"count":0}],"isBlockCoverage":false},{"functionName":"escapeFn","ranges":[{"startOffset":13393,"endOffset":13425,"count":0}],"isBlockCoverage":false},{"functionName":"strEscape","ranges":[{"startOffset":13538,"endOffset":15164,"count":0}],"isBlockCoverage":false},{"functionName":"stylizeWithColor","ranges":[{"startOffset":15166,"endOffset":15432,"count":0}],"isBlockCoverage":false},{"functionName":"stylizeNoColor","ranges":[{"startOffset":15434,"endOffset":15480,"count":0}],"isBlockCoverage":false},{"functionName":"getEmptyFormatArray","ranges":[{"startOffset":15559,"endOffset":15606,"count":0}],"isBlockCoverage":false},{"functionName":"isInstanceof","ranges":[{"startOffset":15608,"endOffset":15726,"count":0}],"isBlockCoverage":false},{"functionName":"getConstructorName","ranges":[{"startOffset":15728,"endOffset":16988,"count":0}],"isBlockCoverage":false},{"functionName":"addPrototypeProperties","ranges":[{"startOffset":17175,"endOffset":19018,"count":0}],"isBlockCoverage":false},{"functionName":"getPrefix","ranges":[{"startOffset":19020,"endOffset":19407,"count":0}],"isBlockCoverage":false},{"functionName":"getKeys","ranges":[{"startOffset":19444,"endOffset":20386,"count":0}],"isBlockCoverage":false},{"functionName":"getCtxStyle","ranges":[{"startOffset":20388,"endOffset":20651,"count":0}],"isBlockCoverage":false},{"functionName":"formatProxy","ranges":[{"startOffset":20653,"endOffset":21102,"count":0}],"isBlockCoverage":false},{"functionName":"findTypedConstructor","ranges":[{"startOffset":21104,"endOffset":21627,"count":0}],"isBlockCoverage":false},{"functionName":"formatValue","ranges":[{"startOffset":21809,"endOffset":24348,"count":0}],"isBlockCoverage":false},{"functionName":"formatRaw","ranges":[{"startOffset":24350,"endOffset":34825,"count":0}],"isBlockCoverage":false},{"functionName":"getIteratorBraces","ranges":[{"startOffset":34827,"endOffset":35009,"count":0}],"isBlockCoverage":false},{"functionName":"getBoxedBase","ranges":[{"startOffset":35011,"endOffset":36185,"count":0}],"isBlockCoverage":false},{"functionName":"getClassBase","ranges":[{"startOffset":36187,"endOffset":36787,"count":0}],"isBlockCoverage":false},{"functionName":"getFunctionBase","ranges":[{"startOffset":36789,"endOffset":37882,"count":0}],"isBlockCoverage":false},{"functionName":"formatError","ranges":[{"startOffset":37884,"endOffset":41005,"count":0}],"isBlockCoverage":false},{"functionName":"groupArrayElements","ranges":[{"startOffset":41007,"endOffset":45258,"count":0}],"isBlockCoverage":false},{"functionName":"handleMaxCallStackSize","ranges":[{"startOffset":45260,"endOffset":45612,"count":0}],"isBlockCoverage":false},{"functionName":"formatNumber","ranges":[{"startOffset":45614,"endOffset":45791,"count":0}],"isBlockCoverage":false},{"functionName":"formatBigInt","ranges":[{"startOffset":45793,"endOffset":45865,"count":0}],"isBlockCoverage":false},{"functionName":"formatPrimitive","ranges":[{"startOffset":45867,"endOffset":47050,"count":0}],"isBlockCoverage":false},{"functionName":"formatNamespaceObject","ranges":[{"startOffset":47052,"endOffset":48200,"count":0}],"isBlockCoverage":false},{"functionName":"formatSpecialArray","ranges":[{"startOffset":48247,"endOffset":49454,"count":0}],"isBlockCoverage":false},{"functionName":"formatArrayBuffer","ranges":[{"startOffset":49456,"endOffset":50056,"count":0}],"isBlockCoverage":false},{"functionName":"formatArray","ranges":[{"startOffset":50058,"endOffset":50652,"count":0}],"isBlockCoverage":false},{"functionName":"formatTypedArray","ranges":[{"startOffset":50654,"endOffset":51670,"count":0}],"isBlockCoverage":false},{"functionName":"formatSet","ranges":[{"startOffset":51672,"endOffset":51904,"count":0}],"isBlockCoverage":false},{"functionName":"formatMap","ranges":[{"startOffset":51906,"endOffset":52204,"count":0}],"isBlockCoverage":false},{"functionName":"formatSetIterInner","ranges":[{"startOffset":52206,"endOffset":53019,"count":0}],"isBlockCoverage":false},{"functionName":"formatMapIterInner","ranges":[{"startOffset":53021,"endOffset":54343,"count":0}],"isBlockCoverage":false},{"functionName":"formatWeakCollection","ranges":[{"startOffset":54345,"endOffset":54437,"count":0}],"isBlockCoverage":false},{"functionName":"formatWeakSet","ranges":[{"startOffset":54439,"endOffset":54596,"count":0}],"isBlockCoverage":false},{"functionName":"formatWeakMap","ranges":[{"startOffset":54598,"endOffset":54755,"count":0}],"isBlockCoverage":false},{"functionName":"formatIterator","ranges":[{"startOffset":54757,"endOffset":55148,"count":0}],"isBlockCoverage":false},{"functionName":"formatPromise","ranges":[{"startOffset":55150,"endOffset":55615,"count":0}],"isBlockCoverage":false},{"functionName":"formatProperty","ranges":[{"startOffset":55617,"endOffset":57951,"count":0}],"isBlockCoverage":false},{"functionName":"isBelowBreakLength","ranges":[{"startOffset":57953,"endOffset":58895,"count":0}],"isBlockCoverage":false},{"functionName":"reduceToSingleString","ranges":[{"startOffset":58897,"endOffset":61647,"count":0}],"isBlockCoverage":false},{"functionName":"hasBuiltInToString","ranges":[{"startOffset":61649,"endOffset":62668,"count":0}],"isBlockCoverage":false},{"functionName":"firstErrorLine","ranges":[{"startOffset":62693,"endOffset":62732,"count":0}],"isBlockCoverage":false},{"functionName":"tryStringify","ranges":[{"startOffset":62762,"endOffset":63231,"count":0}],"isBlockCoverage":false},{"functionName":"format","ranges":[{"startOffset":63233,"endOffset":63317,"count":0}],"isBlockCoverage":false},{"functionName":"formatWithOptions","ranges":[{"startOffset":63319,"endOffset":63597,"count":0}],"isBlockCoverage":false},{"functionName":"formatWithOptionsInternal","ranges":[{"startOffset":63599,"endOffset":67383,"count":0}],"isBlockCoverage":false},{"functionName":"getStringWidth","ranges":[{"startOffset":67812,"endOffset":68363,"count":0}],"isBlockCoverage":false},{"functionName":"getStringWidth","ranges":[{"startOffset":68478,"endOffset":68890,"count":0}],"isBlockCoverage":false},{"functionName":"isFullWidthCodePoint","ranges":[{"startOffset":69058,"endOffset":70667,"count":0}],"isBlockCoverage":false},{"functionName":"isZeroWidthCodePoint","ranges":[{"startOffset":70701,"endOffset":71269,"count":0}],"isBlockCoverage":false},{"functionName":"stripVTControlCharacters","ranges":[{"startOffset":71359,"endOffset":71433,"count":0}],"isBlockCoverage":false}]},{"scriptId":"15","url":"internal/util/types.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1915,"count":1}],"isBlockCoverage":false},{"functionName":"isTypedArray","ranges":[{"startOffset":425,"endOffset":516,"count":0}],"isBlockCoverage":false},{"functionName":"isUint8Array","ranges":[{"startOffset":518,"endOffset":612,"count":14}],"isBlockCoverage":true},{"functionName":"isUint8ClampedArray","ranges":[{"startOffset":614,"endOffset":722,"count":0}],"isBlockCoverage":false},{"functionName":"isUint16Array","ranges":[{"startOffset":724,"endOffset":820,"count":0}],"isBlockCoverage":false},{"functionName":"isUint32Array","ranges":[{"startOffset":822,"endOffset":918,"count":0}],"isBlockCoverage":false},{"functionName":"isInt8Array","ranges":[{"startOffset":920,"endOffset":1012,"count":0}],"isBlockCoverage":false},{"functionName":"isInt16Array","ranges":[{"startOffset":1014,"endOffset":1108,"count":0}],"isBlockCoverage":false},{"functionName":"isInt32Array","ranges":[{"startOffset":1110,"endOffset":1204,"count":0}],"isBlockCoverage":false},{"functionName":"isFloat32Array","ranges":[{"startOffset":1206,"endOffset":1304,"count":0}],"isBlockCoverage":false},{"functionName":"isFloat64Array","ranges":[{"startOffset":1306,"endOffset":1404,"count":0}],"isBlockCoverage":false},{"functionName":"isBigInt64Array","ranges":[{"startOffset":1406,"endOffset":1506,"count":0}],"isBlockCoverage":false},{"functionName":"isBigUint64Array","ranges":[{"startOffset":1508,"endOffset":1610,"count":1}],"isBlockCoverage":true}]},{"scriptId":"16","url":"internal/assert.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":474,"count":1}],"isBlockCoverage":false},{"functionName":"lazyError","ranges":[{"startOffset":26,"endOffset":155,"count":0}],"isBlockCoverage":false},{"functionName":"assert","ranges":[{"startOffset":157,"endOffset":307,"count":6},{"startOffset":205,"endOffset":305,"count":0}],"isBlockCoverage":true},{"functionName":"fail","ranges":[{"startOffset":309,"endOffset":426,"count":0}],"isBlockCoverage":false}]},{"scriptId":"17","url":"buffer.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":35721,"count":1}],"isBlockCoverage":false},{"functionName":"validateOffset","ranges":[{"startOffset":2784,"endOffset":2868,"count":0}],"isBlockCoverage":false},{"functionName":"createUnsafeBuffer","ranges":[{"startOffset":4082,"endOffset":4218,"count":3}],"isBlockCoverage":true},{"functionName":"createPool","ranges":[{"startOffset":4220,"endOffset":4379,"count":1}],"isBlockCoverage":true},{"functionName":"alignPool","ranges":[{"startOffset":4395,"endOffset":4517,"count":2}],"isBlockCoverage":true},{"functionName":"showFlaggedDeprecation","ranges":[{"startOffset":4821,"endOffset":5501,"count":0}],"isBlockCoverage":false},{"functionName":"toInteger","ranges":[{"startOffset":5503,"endOffset":5721,"count":0}],"isBlockCoverage":false},{"functionName":"_copy","ranges":[{"startOffset":5723,"endOffset":6988,"count":0}],"isBlockCoverage":false},{"functionName":"_copyActual","ranges":[{"startOffset":6990,"endOffset":7592,"count":10},{"startOffset":7131,"endOffset":7185,"count":0},{"startOffset":7347,"endOffset":7362,"count":0},{"startOffset":7389,"endOffset":7404,"count":0},{"startOffset":7464,"endOffset":7540,"count":0}],"isBlockCoverage":true},{"functionName":"Buffer","ranges":[{"startOffset":8168,"endOffset":8501,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":8594,"endOffset":8622,"count":0}],"isBlockCoverage":false},{"functionName":"from","ranges":[{"startOffset":8879,"endOffset":9843,"count":2},{"startOffset":9008,"endOffset":9059,"count":0},{"startOffset":9061,"endOffset":9842,"count":0}],"isBlockCoverage":true},{"functionName":"of","ranges":[{"startOffset":10214,"endOffset":10366,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":10655,"endOffset":10876,"count":13},{"startOffset":10699,"endOffset":10764,"count":0},{"startOffset":10807,"endOffset":10874,"count":0}],"isBlockCoverage":true},{"functionName":"alloc","ranges":[{"startOffset":10979,"endOffset":11224,"count":11},{"startOffset":11063,"endOffset":11076,"count":0},{"startOffset":11077,"endOffset":11088,"count":0},{"startOffset":11090,"endOffset":11191,"count":0}],"isBlockCoverage":true},{"functionName":"allocUnsafe","ranges":[{"startOffset":11403,"endOffset":11478,"count":2}],"isBlockCoverage":true},{"functionName":"allocUnsafeSlow","ranges":[{"startOffset":11719,"endOffset":11808,"count":0}],"isBlockCoverage":false},{"functionName":"SlowBuffer","ranges":[{"startOffset":11904,"endOffset":11994,"count":0}],"isBlockCoverage":false},{"functionName":"allocate","ranges":[{"startOffset":12108,"endOffset":12440,"count":2},{"startOffset":12151,"endOffset":12185,"count":0},{"startOffset":12224,"endOffset":12403,"count":0}],"isBlockCoverage":true},{"functionName":"fromStringFast","ranges":[{"startOffset":12442,"endOffset":12988,"count":2},{"startOffset":12568,"endOffset":12617,"count":0},{"startOffset":12663,"endOffset":12676,"count":0},{"startOffset":12809,"endOffset":12935,"count":0}],"isBlockCoverage":true},{"functionName":"fromString","ranges":[{"startOffset":12990,"endOffset":13443,"count":2},{"startOffset":13076,"endOffset":13100,"count":0},{"startOffset":13139,"endOffset":13163,"count":0},{"startOffset":13221,"endOffset":13403,"count":0}],"isBlockCoverage":true},{"functionName":"fromArrayBuffer","ranges":[{"startOffset":13445,"endOffset":14142,"count":0}],"isBlockCoverage":false},{"functionName":"fromArrayLike","ranges":[{"startOffset":14144,"endOffset":14518,"count":0}],"isBlockCoverage":false},{"functionName":"fromObject","ranges":[{"startOffset":14520,"endOffset":14826,"count":0}],"isBlockCoverage":false},{"functionName":"isBuffer","ranges":[{"startOffset":14865,"endOffset":14919,"count":0}],"isBlockCoverage":false},{"functionName":"compare","ranges":[{"startOffset":14939,"endOffset":15264,"count":0}],"isBlockCoverage":false},{"functionName":"isEncoding","ranges":[{"startOffset":15287,"endOffset":15438,"count":1}],"isBlockCoverage":true},{"functionName":"concat","ranges":[{"startOffset":15504,"endOffset":16708,"count":1},{"startOffset":15563,"endOffset":15627,"count":0},{"startOffset":15658,"endOffset":15682,"count":0},{"startOffset":15772,"endOffset":15849,"count":10},{"startOffset":15853,"endOffset":15902,"count":0},{"startOffset":16004,"endOffset":16352,"count":10},{"startOffset":16059,"endOffset":16291,"count":0},{"startOffset":16443,"endOffset":16688,"count":0}],"isBlockCoverage":true},{"functionName":"base64ByteLength","ranges":[{"startOffset":16711,"endOffset":16947,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":17082,"endOffset":17146,"count":2}],"isBlockCoverage":true},{"functionName":"slice","ranges":[{"startOffset":17159,"endOffset":17205,"count":1}],"isBlockCoverage":true},{"functionName":"indexOf","ranges":[{"startOffset":17220,"endOffset":17316,"count":0}],"isBlockCoverage":false},{"functionName":"byteLength","ranges":[{"startOffset":17409,"endOffset":17438,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":17451,"endOffset":17515,"count":0}],"isBlockCoverage":false},{"functionName":"slice","ranges":[{"startOffset":17528,"endOffset":17574,"count":1}],"isBlockCoverage":true},{"functionName":"indexOf","ranges":[{"startOffset":17589,"endOffset":17688,"count":0}],"isBlockCoverage":false},{"functionName":"byteLength","ranges":[{"startOffset":17787,"endOffset":17816,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":17829,"endOffset":17893,"count":0}],"isBlockCoverage":false},{"functionName":"slice","ranges":[{"startOffset":17906,"endOffset":17952,"count":0}],"isBlockCoverage":false},{"functionName":"indexOf","ranges":[{"startOffset":17967,"endOffset":18066,"count":0}],"isBlockCoverage":false},{"functionName":"byteLength","ranges":[{"startOffset":18162,"endOffset":18187,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":18200,"endOffset":18266,"count":0}],"isBlockCoverage":false},{"functionName":"slice","ranges":[{"startOffset":18279,"endOffset":18327,"count":0}],"isBlockCoverage":false},{"functionName":"indexOf","ranges":[{"startOffset":18342,"endOffset":18440,"count":0}],"isBlockCoverage":false},{"functionName":"byteLength","ranges":[{"startOffset":18533,"endOffset":18558,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":18571,"endOffset":18636,"count":0}],"isBlockCoverage":false},{"functionName":"slice","ranges":[{"startOffset":18649,"endOffset":18696,"count":0}],"isBlockCoverage":false},{"functionName":"indexOf","ranges":[{"startOffset":18711,"endOffset":18923,"count":0}],"isBlockCoverage":false},{"functionName":"byteLength","ranges":[{"startOffset":19019,"endOffset":19070,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":19083,"endOffset":19149,"count":0}],"isBlockCoverage":false},{"functionName":"slice","ranges":[{"startOffset":19162,"endOffset":19210,"count":0}],"isBlockCoverage":false},{"functionName":"indexOf","ranges":[{"startOffset":19225,"endOffset":19439,"count":0}],"isBlockCoverage":false},{"functionName":"byteLength","ranges":[{"startOffset":19526,"endOffset":19557,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":19570,"endOffset":19633,"count":0}],"isBlockCoverage":false},{"functionName":"slice","ranges":[{"startOffset":19646,"endOffset":19691,"count":0}],"isBlockCoverage":false},{"functionName":"indexOf","ranges":[{"startOffset":19706,"endOffset":19914,"count":0}],"isBlockCoverage":false},{"functionName":"getEncodingOps","ranges":[{"startOffset":19922,"endOffset":21477,"count":2},{"startOffset":20048,"endOffset":20128,"count":1},{"startOffset":20128,"endOffset":20294,"count":0},{"startOffset":20299,"endOffset":20704,"count":0},{"startOffset":20709,"endOffset":20839,"count":0},{"startOffset":20844,"endOffset":20976,"count":0},{"startOffset":20981,"endOffset":21348,"count":0},{"startOffset":21353,"endOffset":21471,"count":0}],"isBlockCoverage":true},{"functionName":"byteLength","ranges":[{"startOffset":21479,"endOffset":22136,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":22276,"endOffset":22370,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":22448,"endOffset":22546,"count":0}],"isBlockCoverage":false},{"functionName":"copy","ranges":[{"startOffset":22578,"endOffset":22711,"count":0}],"isBlockCoverage":false},{"functionName":"toString","ranges":[{"startOffset":22992,"endOffset":23571,"count":2},{"startOffset":23064,"endOffset":23112,"count":0},{"startOffset":23164,"endOffset":23174,"count":0},{"startOffset":23204,"endOffset":23214,"count":0},{"startOffset":23263,"endOffset":23275,"count":0},{"startOffset":23291,"endOffset":23312,"count":0},{"startOffset":23338,"endOffset":23348,"count":0},{"startOffset":23384,"endOffset":23418,"count":0},{"startOffset":23489,"endOffset":23530,"count":0}],"isBlockCoverage":true},{"functionName":"equals","ranges":[{"startOffset":23600,"endOffset":23954,"count":0}],"isBlockCoverage":false},{"functionName":"inspect","ranges":[{"startOffset":24082,"endOffset":25077,"count":0}],"isBlockCoverage":false},{"functionName":"compare","ranges":[{"startOffset":25173,"endOffset":26322,"count":0}],"isBlockCoverage":false},{"functionName":"bidirectionalIndexOf","ranges":[{"startOffset":26750,"endOffset":28057,"count":0}],"isBlockCoverage":false},{"functionName":"indexOf","ranges":[{"startOffset":28086,"endOffset":28203,"count":0}],"isBlockCoverage":false},{"functionName":"lastIndexOf","ranges":[{"startOffset":28237,"endOffset":28359,"count":0}],"isBlockCoverage":false},{"functionName":"includes","ranges":[{"startOffset":28390,"endOffset":28495,"count":0}],"isBlockCoverage":false},{"functionName":"fill","ranges":[{"startOffset":28673,"endOffset":28772,"count":0}],"isBlockCoverage":false},{"functionName":"_fill","ranges":[{"startOffset":28775,"endOffset":30684,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":30711,"endOffset":31726,"count":0}],"isBlockCoverage":false},{"functionName":"toJSON","ranges":[{"startOffset":31755,"endOffset":31989,"count":0}],"isBlockCoverage":false},{"functionName":"adjustOffset","ranges":[{"startOffset":31992,"endOffset":32426,"count":20},{"startOffset":32232,"endOffset":32270,"count":10},{"startOffset":32270,"endOffset":32333,"count":0},{"startOffset":32333,"endOffset":32357,"count":10},{"startOffset":32357,"endOffset":32381,"count":1},{"startOffset":32381,"endOffset":32411,"count":9},{"startOffset":32411,"endOffset":32414,"count":0},{"startOffset":32415,"endOffset":32423,"count":9}],"isBlockCoverage":true},{"functionName":"slice","ranges":[{"startOffset":32453,"endOffset":32753,"count":10},{"startOffset":32614,"endOffset":32625,"count":0},{"startOffset":32673,"endOffset":32676,"count":0}],"isBlockCoverage":true},{"functionName":"swap","ranges":[{"startOffset":32756,"endOffset":32827,"count":0}],"isBlockCoverage":false},{"functionName":"swap16","ranges":[{"startOffset":32855,"endOffset":33259,"count":0}],"isBlockCoverage":false},{"functionName":"swap32","ranges":[{"startOffset":33288,"endOffset":33732,"count":0}],"isBlockCoverage":false},{"functionName":"swap64","ranges":[{"startOffset":33761,"endOffset":34269,"count":0}],"isBlockCoverage":false},{"functionName":"transcode","ranges":[{"startOffset":34582,"endOffset":35322,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":35634,"endOffset":35669,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":35675,"endOffset":35712,"count":0}],"isBlockCoverage":false}]},{"scriptId":"18","url":"internal/validators.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":6930,"count":1}],"isBlockCoverage":false},{"functionName":"isInt32","ranges":[{"startOffset":581,"endOffset":640,"count":2}],"isBlockCoverage":true},{"functionName":"isUint32","ranges":[{"startOffset":642,"endOffset":704,"count":3}],"isBlockCoverage":true},{"functionName":"parseFileMode","ranges":[{"startOffset":1326,"endOffset":1807,"count":2},{"startOffset":1389,"endOffset":1409,"count":0},{"startOffset":1411,"endOffset":1432,"count":0},{"startOffset":1480,"endOffset":1806,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":1852,"endOffset":2233,"count":12},{"startOffset":1972,"endOffset":2026,"count":0},{"startOffset":2066,"endOffset":2120,"count":0},{"startOffset":2163,"endOffset":2229,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":2279,"endOffset":2860,"count":2},{"startOffset":2441,"endOffset":2739,"count":0},{"startOffset":2776,"endOffset":2856,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":2904,"endOffset":3414,"count":0}],"isBlockCoverage":false},{"functionName":"validateString","ranges":[{"startOffset":3418,"endOffset":3550,"count":25},{"startOffset":3494,"endOffset":3548,"count":0}],"isBlockCoverage":true},{"functionName":"validateNumber","ranges":[{"startOffset":3552,"endOffset":3684,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":3724,"endOffset":4174,"count":0}],"isBlockCoverage":false},{"functionName":"validateBoolean","ranges":[{"startOffset":4178,"endOffset":4313,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":4357,"endOffset":4582,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":4624,"endOffset":4911,"count":0}],"isBlockCoverage":false},{"functionName":"validateSignalName","ranges":[{"startOffset":4915,"endOffset":5336,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":5377,"endOffset":5607,"count":1},{"startOffset":5442,"endOffset":5605,"count":0}],"isBlockCoverage":true},{"functionName":"validateEncoding","ranges":[{"startOffset":5611,"endOffset":5945,"count":0}],"isBlockCoverage":false},{"functionName":"validatePort","ranges":[{"startOffset":6089,"endOffset":6463,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":6506,"endOffset":6607,"count":0}],"isBlockCoverage":false}]},{"scriptId":"19","url":"internal/buffer.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":29662,"count":1}],"isBlockCoverage":false},{"functionName":"checkBounds","ranges":[{"startOffset":1107,"endOffset":1323,"count":0}],"isBlockCoverage":false},{"functionName":"checkInt","ranges":[{"startOffset":1325,"endOffset":1943,"count":0}],"isBlockCoverage":false},{"functionName":"boundsError","ranges":[{"startOffset":1945,"endOffset":2348,"count":0}],"isBlockCoverage":false},{"functionName":"readBigUInt64LE","ranges":[{"startOffset":2368,"endOffset":2867,"count":0}],"isBlockCoverage":false},{"functionName":"readBigUInt64BE","ranges":[{"startOffset":2869,"endOffset":3368,"count":0}],"isBlockCoverage":false},{"functionName":"readBigInt64LE","ranges":[{"startOffset":3370,"endOffset":3871,"count":0}],"isBlockCoverage":false},{"functionName":"readBigInt64BE","ranges":[{"startOffset":3873,"endOffset":4368,"count":0}],"isBlockCoverage":false},{"functionName":"readUIntLE","ranges":[{"startOffset":4370,"endOffset":4922,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt48LE","ranges":[{"startOffset":4924,"endOffset":5307,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt40LE","ranges":[{"startOffset":5309,"endOffset":5665,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt32LE","ranges":[{"startOffset":5667,"endOffset":5993,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt24LE","ranges":[{"startOffset":5995,"endOffset":6283,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt16LE","ranges":[{"startOffset":6285,"endOffset":6545,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt8","ranges":[{"startOffset":6547,"endOffset":6727,"count":0}],"isBlockCoverage":false},{"functionName":"readUIntBE","ranges":[{"startOffset":6729,"endOffset":7281,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt48BE","ranges":[{"startOffset":7283,"endOffset":7666,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt40BE","ranges":[{"startOffset":7668,"endOffset":8024,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt32BE","ranges":[{"startOffset":8026,"endOffset":8352,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt24BE","ranges":[{"startOffset":8354,"endOffset":8642,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt16BE","ranges":[{"startOffset":8644,"endOffset":8904,"count":0}],"isBlockCoverage":false},{"functionName":"readIntLE","ranges":[{"startOffset":8906,"endOffset":9451,"count":0}],"isBlockCoverage":false},{"functionName":"readInt48LE","ranges":[{"startOffset":9453,"endOffset":9884,"count":0}],"isBlockCoverage":false},{"functionName":"readInt40LE","ranges":[{"startOffset":9886,"endOffset":10273,"count":0}],"isBlockCoverage":false},{"functionName":"readInt32LE","ranges":[{"startOffset":10275,"endOffset":10610,"count":0}],"isBlockCoverage":false},{"functionName":"readInt24LE","ranges":[{"startOffset":10612,"endOffset":10944,"count":0}],"isBlockCoverage":false},{"functionName":"readInt16LE","ranges":[{"startOffset":10946,"endOffset":11252,"count":0}],"isBlockCoverage":false},{"functionName":"readInt8","ranges":[{"startOffset":11254,"endOffset":11462,"count":0}],"isBlockCoverage":false},{"functionName":"readIntBE","ranges":[{"startOffset":11464,"endOffset":12009,"count":0}],"isBlockCoverage":false},{"functionName":"readInt48BE","ranges":[{"startOffset":12011,"endOffset":12440,"count":0}],"isBlockCoverage":false},{"functionName":"readInt40BE","ranges":[{"startOffset":12442,"endOffset":12830,"count":0}],"isBlockCoverage":false},{"functionName":"readInt32BE","ranges":[{"startOffset":12832,"endOffset":13167,"count":0}],"isBlockCoverage":false},{"functionName":"readInt24BE","ranges":[{"startOffset":13169,"endOffset":13501,"count":0}],"isBlockCoverage":false},{"functionName":"readInt16BE","ranges":[{"startOffset":13503,"endOffset":13809,"count":0}],"isBlockCoverage":false},{"functionName":"readFloatBackwards","ranges":[{"startOffset":13826,"endOffset":14231,"count":0}],"isBlockCoverage":false},{"functionName":"readFloatForwards","ranges":[{"startOffset":14233,"endOffset":14637,"count":0}],"isBlockCoverage":false},{"functionName":"readDoubleBackwards","ranges":[{"startOffset":14639,"endOffset":15209,"count":0}],"isBlockCoverage":false},{"functionName":"readDoubleForwards","ranges":[{"startOffset":15211,"endOffset":15780,"count":0}],"isBlockCoverage":false},{"functionName":"writeBigU_Int64LE","ranges":[{"startOffset":15801,"endOffset":16283,"count":0}],"isBlockCoverage":false},{"functionName":"writeBigUInt64LE","ranges":[{"startOffset":16285,"endOffset":16407,"count":0}],"isBlockCoverage":false},{"functionName":"writeBigU_Int64BE","ranges":[{"startOffset":16409,"endOffset":16907,"count":0}],"isBlockCoverage":false},{"functionName":"writeBigUInt64BE","ranges":[{"startOffset":16909,"endOffset":17031,"count":0}],"isBlockCoverage":false},{"functionName":"writeBigInt64LE","ranges":[{"startOffset":17033,"endOffset":17177,"count":0}],"isBlockCoverage":false},{"functionName":"writeBigInt64BE","ranges":[{"startOffset":17179,"endOffset":17323,"count":0}],"isBlockCoverage":false},{"functionName":"writeUIntLE","ranges":[{"startOffset":17325,"endOffset":17934,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int48LE","ranges":[{"startOffset":17936,"endOffset":18349,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int40LE","ranges":[{"startOffset":18351,"endOffset":18730,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int32LE","ranges":[{"startOffset":18732,"endOffset":19039,"count":0}],"isBlockCoverage":false},{"functionName":"writeUInt32LE","ranges":[{"startOffset":19041,"endOffset":19147,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int24LE","ranges":[{"startOffset":19149,"endOffset":19408,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int16LE","ranges":[{"startOffset":19410,"endOffset":19606,"count":0}],"isBlockCoverage":false},{"functionName":"writeUInt16LE","ranges":[{"startOffset":19608,"endOffset":19710,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int8","ranges":[{"startOffset":19712,"endOffset":20124,"count":0}],"isBlockCoverage":false},{"functionName":"writeUInt8","ranges":[{"startOffset":20126,"endOffset":20220,"count":0}],"isBlockCoverage":false},{"functionName":"writeUIntBE","ranges":[{"startOffset":20222,"endOffset":20831,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int48BE","ranges":[{"startOffset":20833,"endOffset":21254,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int40BE","ranges":[{"startOffset":21256,"endOffset":21618,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int32BE","ranges":[{"startOffset":21620,"endOffset":21935,"count":0}],"isBlockCoverage":false},{"functionName":"writeUInt32BE","ranges":[{"startOffset":21937,"endOffset":22043,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int24BE","ranges":[{"startOffset":22045,"endOffset":22310,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int16BE","ranges":[{"startOffset":22312,"endOffset":22508,"count":0}],"isBlockCoverage":false},{"functionName":"writeUInt16BE","ranges":[{"startOffset":22510,"endOffset":22612,"count":0}],"isBlockCoverage":false},{"functionName":"writeIntLE","ranges":[{"startOffset":22614,"endOffset":23276,"count":0}],"isBlockCoverage":false},{"functionName":"writeInt32LE","ranges":[{"startOffset":23278,"endOffset":23393,"count":0}],"isBlockCoverage":false},{"functionName":"writeInt16LE","ranges":[{"startOffset":23395,"endOffset":23502,"count":0}],"isBlockCoverage":false},{"functionName":"writeInt8","ranges":[{"startOffset":23504,"endOffset":23601,"count":0}],"isBlockCoverage":false},{"functionName":"writeIntBE","ranges":[{"startOffset":23603,"endOffset":24265,"count":0}],"isBlockCoverage":false},{"functionName":"writeInt32BE","ranges":[{"startOffset":24267,"endOffset":24382,"count":0}],"isBlockCoverage":false},{"functionName":"writeInt16BE","ranges":[{"startOffset":24384,"endOffset":24491,"count":0}],"isBlockCoverage":false},{"functionName":"writeDoubleForwards","ranges":[{"startOffset":24510,"endOffset":24976,"count":0}],"isBlockCoverage":false},{"functionName":"writeDoubleBackwards","ranges":[{"startOffset":24978,"endOffset":25445,"count":0}],"isBlockCoverage":false},{"functionName":"writeFloatForwards","ranges":[{"startOffset":25447,"endOffset":25748,"count":0}],"isBlockCoverage":false},{"functionName":"writeFloatBackwards","ranges":[{"startOffset":25750,"endOffset":26052,"count":0}],"isBlockCoverage":false},{"functionName":"addBufferPrototypeMethods","ranges":[{"startOffset":26094,"endOffset":29151,"count":1},{"startOffset":28177,"endOffset":28197,"count":0},{"startOffset":28251,"endOffset":28270,"count":0},{"startOffset":28326,"endOffset":28347,"count":0},{"startOffset":28403,"endOffset":28423,"count":0},{"startOffset":28480,"endOffset":28501,"count":0},{"startOffset":28557,"endOffset":28577,"count":0},{"startOffset":28635,"endOffset":28657,"count":0},{"startOffset":28715,"endOffset":28736,"count":0}],"isBlockCoverage":true},{"functionName":"markAsUntransferable","ranges":[{"startOffset":29307,"endOffset":29571,"count":1},{"startOffset":29375,"endOffset":29403,"count":0},{"startOffset":29426,"endOffset":29433,"count":0}],"isBlockCoverage":true}]},{"scriptId":"20","url":"internal/worker/js_transferable.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1310,"count":1}],"isBlockCoverage":false},{"functionName":"setup","ranges":[{"startOffset":304,"endOffset":1091,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":585,"endOffset":1087,"count":0}],"isBlockCoverage":false}]},{"scriptId":"21","url":"internal/process/per_thread.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":10598,"count":1}],"isBlockCoverage":false},{"functionName":"assert","ranges":[{"startOffset":796,"endOffset":884,"count":0}],"isBlockCoverage":false},{"functionName":"wrapProcessMethods","ranges":[{"startOffset":962,"endOffset":6703,"count":1}],"isBlockCoverage":true},{"functionName":"_rawDebug","ranges":[{"startOffset":1173,"endOffset":1255,"count":0}],"isBlockCoverage":false},{"functionName":"cpuUsage","ranges":[{"startOffset":1466,"endOffset":3025,"count":0}],"isBlockCoverage":false},{"functionName":"previousValueIsValid","ranges":[{"startOffset":3178,"endOffset":3315,"count":0}],"isBlockCoverage":false},{"functionName":"hrtime","ranges":[{"startOffset":3539,"endOffset":4142,"count":0}],"isBlockCoverage":false},{"functionName":"hrtimeBigInt","ranges":[{"startOffset":4329,"endOffset":4423,"count":0}],"isBlockCoverage":false},{"functionName":"memoryUsage","ranges":[{"startOffset":4468,"endOffset":4694,"count":0}],"isBlockCoverage":false},{"functionName":"exit","ranges":[{"startOffset":4698,"endOffset":5136,"count":1}],"isBlockCoverage":true},{"functionName":"kill","ranges":[{"startOffset":5140,"endOffset":5785,"count":0}],"isBlockCoverage":false},{"functionName":"resourceUsage","ranges":[{"startOffset":5836,"endOffset":6569,"count":0}],"isBlockCoverage":false},{"functionName":"buildAllowedFlags","ranges":[{"startOffset":6914,"endOffset":9974,"count":0}],"isBlockCoverage":false},{"functionName":"toggleTraceCategoryState","ranges":[{"startOffset":10164,"endOffset":10494,"count":1},{"startOffset":10244,"endOffset":10419,"count":0},{"startOffset":10451,"endOffset":10492,"count":0}],"isBlockCoverage":true}]},{"scriptId":"22","url":"internal/async_hooks.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":18879,"count":1}],"isBlockCoverage":false},{"functionName":"useDomainTrampoline","ranges":[{"startOffset":5221,"endOffset":5275,"count":0}],"isBlockCoverage":false},{"functionName":"callbackTrampoline","ranges":[{"startOffset":5277,"endOffset":5870,"count":0}],"isBlockCoverage":false},{"functionName":"executionAsyncResource","ranges":[{"startOffset":5946,"endOffset":6444,"count":0}],"isBlockCoverage":false},{"functionName":"fatalError","ranges":[{"startOffset":6505,"endOffset":6880,"count":0}],"isBlockCoverage":false},{"functionName":"lookupPublicResource","ranges":[{"startOffset":6882,"endOffset":7231,"count":0}],"isBlockCoverage":false},{"functionName":"emitInitNative","ranges":[{"startOffset":7422,"endOffset":8515,"count":0}],"isBlockCoverage":false},{"functionName":"emitHook","ranges":[{"startOffset":8616,"endOffset":9566,"count":0}],"isBlockCoverage":false},{"functionName":"emitHookFactory","ranges":[{"startOffset":9568,"endOffset":9828,"count":4}],"isBlockCoverage":true},{"functionName":"getHookArrays","ranges":[{"startOffset":9857,"endOffset":10347,"count":0}],"isBlockCoverage":false},{"functionName":"storeActiveHooks","ranges":[{"startOffset":10350,"endOffset":10650,"count":0}],"isBlockCoverage":false},{"functionName":"copyHooks","ranges":[{"startOffset":10652,"endOffset":10917,"count":0}],"isBlockCoverage":false},{"functionName":"restoreActiveHooks","ranges":[{"startOffset":11032,"endOffset":11237,"count":0}],"isBlockCoverage":false},{"functionName":"trackPromise","ranges":[{"startOffset":11239,"endOffset":11596,"count":0}],"isBlockCoverage":false},{"functionName":"fastPromiseHook","ranges":[{"startOffset":11598,"endOffset":12734,"count":0}],"isBlockCoverage":false},{"functionName":"enableHooks","ranges":[{"startOffset":12765,"endOffset":12825,"count":0}],"isBlockCoverage":false},{"functionName":"updatePromiseHookMode","ranges":[{"startOffset":12853,"endOffset":13144,"count":0}],"isBlockCoverage":false},{"functionName":"disableHooks","ranges":[{"startOffset":13146,"endOffset":13421,"count":0}],"isBlockCoverage":false},{"functionName":"disablePromiseHookIfNecessary","ranges":[{"startOffset":13423,"endOffset":13549,"count":0}],"isBlockCoverage":false},{"functionName":"newAsyncId","ranges":[{"startOffset":13750,"endOffset":13820,"count":0}],"isBlockCoverage":false},{"functionName":"getOrSetAsyncId","ranges":[{"startOffset":13822,"endOffset":14012,"count":0}],"isBlockCoverage":false},{"functionName":"getDefaultTriggerAsyncId","ranges":[{"startOffset":14195,"endOffset":14485,"count":0}],"isBlockCoverage":false},{"functionName":"clearDefaultTriggerAsyncId","ranges":[{"startOffset":14488,"endOffset":14577,"count":0}],"isBlockCoverage":false},{"functionName":"defaultTriggerAsyncIdScope","ranges":[{"startOffset":14580,"endOffset":15055,"count":0}],"isBlockCoverage":false},{"functionName":"hasHooks","ranges":[{"startOffset":15057,"endOffset":15120,"count":0}],"isBlockCoverage":false},{"functionName":"enabledHooksExist","ranges":[{"startOffset":15122,"endOffset":15181,"count":0}],"isBlockCoverage":false},{"functionName":"initHooksExist","ranges":[{"startOffset":15183,"endOffset":15238,"count":0}],"isBlockCoverage":false},{"functionName":"afterHooksExist","ranges":[{"startOffset":15240,"endOffset":15297,"count":0}],"isBlockCoverage":false},{"functionName":"destroyHooksExist","ranges":[{"startOffset":15299,"endOffset":15360,"count":0}],"isBlockCoverage":false},{"functionName":"emitInitScript","ranges":[{"startOffset":15363,"endOffset":15771,"count":0}],"isBlockCoverage":false},{"functionName":"emitBeforeScript","ranges":[{"startOffset":15774,"endOffset":15950,"count":0}],"isBlockCoverage":false},{"functionName":"emitAfterScript","ranges":[{"startOffset":15953,"endOffset":16073,"count":0}],"isBlockCoverage":false},{"functionName":"emitDestroyScript","ranges":[{"startOffset":16076,"endOffset":16286,"count":0}],"isBlockCoverage":false},{"functionName":"hasAsyncIdStack","ranges":[{"startOffset":16289,"endOffset":16352,"count":0}],"isBlockCoverage":false},{"functionName":"pushAsyncContext","ranges":[{"startOffset":16418,"endOffset":16988,"count":0}],"isBlockCoverage":false},{"functionName":"popAsyncContext","ranges":[{"startOffset":17053,"endOffset":17677,"count":0}],"isBlockCoverage":false},{"functionName":"executionAsyncId","ranges":[{"startOffset":17680,"endOffset":17756,"count":0}],"isBlockCoverage":false},{"functionName":"triggerAsyncId","ranges":[{"startOffset":17758,"endOffset":17830,"count":0}],"isBlockCoverage":false}]},{"scriptId":"23","url":"internal/process/task_queues.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":4577,"count":1}],"isBlockCoverage":false},{"functionName":"hasTickScheduled","ranges":[{"startOffset":956,"endOffset":1031,"count":0}],"isBlockCoverage":false},{"functionName":"setHasTickScheduled","ranges":[{"startOffset":1033,"endOffset":1119,"count":0}],"isBlockCoverage":false},{"functionName":"runNextTicks","ranges":[{"startOffset":1221,"endOffset":1417,"count":0}],"isBlockCoverage":false},{"functionName":"processTicksAndRejections","ranges":[{"startOffset":1419,"endOffset":2387,"count":0}],"isBlockCoverage":false},{"functionName":"nextTick","ranges":[{"startOffset":2531,"endOffset":3446,"count":0}],"isBlockCoverage":false},{"functionName":"createMicrotaskResource","ranges":[{"startOffset":3536,"endOffset":3782,"count":0}],"isBlockCoverage":false},{"functionName":"runMicrotask","ranges":[{"startOffset":3784,"endOffset":3960,"count":0}],"isBlockCoverage":false},{"functionName":"queueMicrotask","ranges":[{"startOffset":3962,"endOffset":4275,"count":0}],"isBlockCoverage":false},{"functionName":"setupTaskQueue","ranges":[{"startOffset":4298,"endOffset":4555,"count":1}],"isBlockCoverage":true}]},{"scriptId":"24","url":"internal/process/promises.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":9605,"count":1}],"isBlockCoverage":false},{"functionName":"setHasRejectionToWarn","ranges":[{"startOffset":1918,"endOffset":2008,"count":0}],"isBlockCoverage":false},{"functionName":"hasRejectionToWarn","ranges":[{"startOffset":2010,"endOffset":2089,"count":0}],"isBlockCoverage":false},{"functionName":"getUnhandledRejectionsMode","ranges":[{"startOffset":2091,"endOffset":2626,"count":0}],"isBlockCoverage":false},{"functionName":"promiseRejectHandler","ranges":[{"startOffset":2628,"endOffset":3197,"count":0}],"isBlockCoverage":false},{"functionName":"resolveError","ranges":[{"startOffset":3199,"endOffset":3449,"count":0}],"isBlockCoverage":false},{"functionName":"unhandledRejection","ranges":[{"startOffset":3451,"endOffset":3745,"count":0}],"isBlockCoverage":false},{"functionName":"handledRejection","ranges":[{"startOffset":3747,"endOffset":4563,"count":0}],"isBlockCoverage":false},{"functionName":"emitUnhandledRejectionWarning","ranges":[{"startOffset":4635,"endOffset":5531,"count":0}],"isBlockCoverage":false},{"functionName":"emitDeprecationWarning","ranges":[{"startOffset":5564,"endOffset":5849,"count":0}],"isBlockCoverage":false},{"functionName":"processPromiseRejections","ranges":[{"startOffset":6022,"endOffset":8506,"count":0}],"isBlockCoverage":false},{"functionName":"getErrorWithoutStack","ranges":[{"startOffset":8508,"endOffset":8926,"count":0}],"isBlockCoverage":false},{"functionName":"generateUnhandledRejectionError","ranges":[{"startOffset":8928,"endOffset":9398,"count":0}],"isBlockCoverage":false},{"functionName":"listenForRejections","ranges":[{"startOffset":9400,"endOffset":9484,"count":1}],"isBlockCoverage":true}]},{"scriptId":"25","url":"internal/fixed_queue.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":4184,"count":1}],"isBlockCoverage":false},{"functionName":"FixedCircularBuffer","ranges":[{"startOffset":2959,"endOffset":3073,"count":1}],"isBlockCoverage":true},{"functionName":"isEmpty","ranges":[{"startOffset":3077,"endOffset":3129,"count":0}],"isBlockCoverage":false},{"functionName":"isFull","ranges":[{"startOffset":3133,"endOffset":3200,"count":0}],"isBlockCoverage":false},{"functionName":"push","ranges":[{"startOffset":3204,"endOffset":3291,"count":0}],"isBlockCoverage":false},{"functionName":"shift","ranges":[{"startOffset":3295,"endOffset":3510,"count":0}],"isBlockCoverage":false},{"functionName":"FixedQueue","ranges":[{"startOffset":3552,"endOffset":3626,"count":1}],"isBlockCoverage":true},{"functionName":"isEmpty","ranges":[{"startOffset":3630,"endOffset":3677,"count":0}],"isBlockCoverage":false},{"functionName":"push","ranges":[{"startOffset":3681,"endOffset":3945,"count":0}],"isBlockCoverage":false},{"functionName":"shift","ranges":[{"startOffset":3949,"endOffset":4180,"count":0}],"isBlockCoverage":false}]},{"scriptId":"26","url":"internal/console/global.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1939,"count":1}],"isBlockCoverage":false}]},{"scriptId":"27","url":"internal/console/constructor.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":19933,"count":1}],"isBlockCoverage":false},{"functionName":"Console","ranges":[{"startOffset":2592,"endOffset":4763,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":4972,"endOffset":5026,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":5274,"endOffset":5480,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":5683,"endOffset":6271,"count":1}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":5865,"endOffset":5960,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":5972,"endOffset":6002,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":6104,"endOffset":6203,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":6215,"endOffset":6245,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":6341,"endOffset":7523,"count":1}],"isBlockCoverage":true},{"functionName":"value","ranges":[{"startOffset":7593,"endOffset":9039,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":9112,"endOffset":9602,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":9673,"endOffset":9801,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":9872,"endOffset":10000,"count":0}],"isBlockCoverage":false},{"functionName":"createWriteErrorHandler","ranges":[{"startOffset":10089,"endOffset":10978,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":10157,"endOffset":10975,"count":0}],"isBlockCoverage":false},{"functionName":"log","ranges":[{"startOffset":11007,"endOffset":11094,"count":0}],"isBlockCoverage":false},{"functionName":"warn","ranges":[{"startOffset":11100,"endOffset":11188,"count":0}],"isBlockCoverage":false},{"functionName":"dir","ranges":[{"startOffset":11194,"endOffset":11379,"count":0}],"isBlockCoverage":false},{"functionName":"time","ranges":[{"startOffset":11384,"endOffset":11742,"count":0}],"isBlockCoverage":false},{"functionName":"timeEnd","ranges":[{"startOffset":11747,"endOffset":12036,"count":0}],"isBlockCoverage":false},{"functionName":"timeLog","ranges":[{"startOffset":12041,"endOffset":12279,"count":0}],"isBlockCoverage":false},{"functionName":"trace","ranges":[{"startOffset":12291,"endOffset":12477,"count":0}],"isBlockCoverage":false},{"functionName":"assert","ranges":[{"startOffset":12482,"endOffset":12700,"count":0}],"isBlockCoverage":false},{"functionName":"clear","ranges":[{"startOffset":12761,"endOffset":13191,"count":0}],"isBlockCoverage":false},{"functionName":"count","ranges":[{"startOffset":13252,"endOffset":13708,"count":0}],"isBlockCoverage":false},{"functionName":"countReset","ranges":[{"startOffset":13774,"endOffset":14062,"count":0}],"isBlockCoverage":false},{"functionName":"group","ranges":[{"startOffset":14067,"endOffset":14235,"count":0}],"isBlockCoverage":false},{"functionName":"groupEnd","ranges":[{"startOffset":14240,"endOffset":14408,"count":0}],"isBlockCoverage":false},{"functionName":"table","ranges":[{"startOffset":14457,"endOffset":17867,"count":0}],"isBlockCoverage":false},{"functionName":"timeLogImpl","ranges":[{"startOffset":17908,"endOffset":18404,"count":0}],"isBlockCoverage":false},{"functionName":"pad","ranges":[{"startOffset":18406,"endOffset":18483,"count":0}],"isBlockCoverage":false},{"functionName":"formatTime","ranges":[{"startOffset":18485,"endOffset":19247,"count":0}],"isBlockCoverage":false},{"functionName":"isArray","ranges":[{"startOffset":19381,"endOffset":19437,"count":0}],"isBlockCoverage":false},{"functionName":"noop","ranges":[{"startOffset":19440,"endOffset":19458,"count":0}],"isBlockCoverage":false}]},{"scriptId":"28","url":"internal/constants.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1582,"count":1}],"isBlockCoverage":false}]},{"scriptId":"29","url":"internal/util/inspector.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2215,"count":1}],"isBlockCoverage":false},{"functionName":"sendInspectorCommand","ranges":[{"startOffset":92,"endOffset":434,"count":0}],"isBlockCoverage":false},{"functionName":"installConsoleExtensions","ranges":[{"startOffset":508,"endOffset":1062,"count":0}],"isBlockCoverage":false},{"functionName":"wrapConsole","ranges":[{"startOffset":1141,"endOffset":1931,"count":1},{"startOffset":1299,"endOffset":1929,"count":23},{"startOffset":1514,"endOffset":1807,"count":19},{"startOffset":1807,"endOffset":1925,"count":4}],"isBlockCoverage":true},{"functionName":"get consoleFromVM","ranges":[{"startOffset":2103,"endOffset":2154,"count":0}],"isBlockCoverage":false},{"functionName":"set consoleFromVM","ranges":[{"startOffset":2158,"endOffset":2211,"count":1}],"isBlockCoverage":true}]},{"scriptId":"30","url":"internal/url.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":41335,"count":1}],"isBlockCoverage":false},{"functionName":"toUSVString","ranges":[{"startOffset":2224,"endOffset":2520,"count":1},{"startOffset":2477,"endOffset":2519,"count":0}],"isBlockCoverage":true},{"functionName":"serializeTupleOrigin","ranges":[{"startOffset":2732,"endOffset":2850,"count":0}],"isBlockCoverage":false},{"functionName":"URLContext","ranges":[{"startOffset":3254,"endOffset":3477,"count":12}],"isBlockCoverage":true},{"functionName":"URLSearchParams","ranges":[{"startOffset":3767,"endOffset":6130,"count":10},{"startOffset":3882,"endOffset":6068,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":6134,"endOffset":7204,"count":0}],"isBlockCoverage":false},{"functionName":"onParseComplete","ranges":[{"startOffset":7208,"endOffset":7901,"count":10},{"startOffset":7463,"endOffset":7473,"count":0},{"startOffset":7536,"endOffset":7546,"count":0},{"startOffset":7627,"endOffset":7631,"count":0}],"isBlockCoverage":true},{"functionName":"onParseError","ranges":[{"startOffset":7903,"endOffset":7978,"count":2}],"isBlockCoverage":true},{"functionName":"onParseProtocolComplete","ranges":[{"startOffset":7980,"endOffset":8325,"count":0}],"isBlockCoverage":false},{"functionName":"onParseHostnameComplete","ranges":[{"startOffset":8327,"endOffset":8673,"count":0}],"isBlockCoverage":false},{"functionName":"onParsePortComplete","ranges":[{"startOffset":8675,"endOffset":8837,"count":0}],"isBlockCoverage":false},{"functionName":"onParseHostComplete","ranges":[{"startOffset":8839,"endOffset":9145,"count":0}],"isBlockCoverage":false},{"functionName":"onParsePathComplete","ranges":[{"startOffset":9147,"endOffset":9641,"count":2},{"startOffset":9413,"endOffset":9481,"count":0}],"isBlockCoverage":true},{"functionName":"onParseSearchComplete","ranges":[{"startOffset":9643,"endOffset":9811,"count":0}],"isBlockCoverage":false},{"functionName":"onParseHashComplete","ranges":[{"startOffset":9813,"endOffset":9983,"count":0}],"isBlockCoverage":false},{"functionName":"URL","ranges":[{"startOffset":9999,"endOffset":10327,"count":12},{"startOffset":10134,"endOffset":10186,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":10331,"endOffset":10412,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":10416,"endOffset":10509,"count":28}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":10584,"endOffset":10784,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":10788,"endOffset":11721,"count":0}],"isBlockCoverage":false},{"functionName":"format","ranges":[{"startOffset":11890,"endOffset":13081,"count":9},{"startOffset":11975,"endOffset":12036,"count":0},{"startOffset":12446,"endOffset":12607,"count":0},{"startOffset":12639,"endOffset":12681,"count":0},{"startOffset":12740,"endOffset":12762,"count":0},{"startOffset":12770,"endOffset":12834,"count":0},{"startOffset":12946,"endOffset":12969,"count":0},{"startOffset":13031,"endOffset":13057,"count":0}],"isBlockCoverage":true},{"functionName":"toString","ranges":[{"startOffset":13355,"endOffset":13414,"count":1}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":13480,"endOffset":13525,"count":8}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":13531,"endOffset":13711,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":13792,"endOffset":14420,"count":5},{"startOffset":13932,"endOffset":14176,"count":0},{"startOffset":14185,"endOffset":14197,"count":0},{"startOffset":14206,"endOffset":14221,"count":0},{"startOffset":14230,"endOffset":14243,"count":0},{"startOffset":14252,"endOffset":14266,"count":0},{"startOffset":14275,"endOffset":14286,"count":0},{"startOffset":14295,"endOffset":14378,"count":0}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":14490,"endOffset":14538,"count":8}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":14544,"endOffset":14913,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":14983,"endOffset":15033,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":15039,"endOffset":15437,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":15507,"endOffset":15557,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":15563,"endOffset":15961,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":16027,"endOffset":16183,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":16189,"endOffset":16480,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":16550,"endOffset":16602,"count":14}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":16608,"endOffset":16907,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":16973,"endOffset":17073,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":17079,"endOffset":17396,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":17466,"endOffset":17661,"count":26},{"startOffset":17545,"endOffset":17564,"count":0},{"startOffset":17606,"endOffset":17616,"count":0}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":17667,"endOffset":17886,"count":2},{"startOffset":17777,"endOffset":17784,"count":0}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":17954,"endOffset":18093,"count":1},{"startOffset":18026,"endOffset":18041,"count":0},{"startOffset":18061,"endOffset":18092,"count":0}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":18099,"endOffset":18591,"count":1},{"startOffset":18285,"endOffset":18533,"count":0}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":18678,"endOffset":18724,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":18790,"endOffset":18941,"count":1},{"startOffset":18868,"endOffset":18886,"count":0},{"startOffset":18906,"endOffset":18940,"count":0}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":18947,"endOffset":19365,"count":1},{"startOffset":19169,"endOffset":19364,"count":0}],"isBlockCoverage":true},{"functionName":"toJSON","ranges":[{"startOffset":19511,"endOffset":19568,"count":0}],"isBlockCoverage":false},{"functionName":"update","ranges":[{"startOffset":19578,"endOffset":19883,"count":0}],"isBlockCoverage":false},{"functionName":"initSearchParams","ranges":[{"startOffset":19885,"endOffset":20025,"count":11},{"startOffset":19982,"endOffset":20024,"count":0}],"isBlockCoverage":true},{"functionName":"parseParams","ranges":[{"startOffset":20134,"endOffset":22435,"count":0}],"isBlockCoverage":false},{"functionName":"serializeParams","ranges":[{"startOffset":23414,"endOffset":23974,"count":0}],"isBlockCoverage":false},{"functionName":"defineIDLClass","ranges":[{"startOffset":24029,"endOffset":24717,"count":2},{"startOffset":24367,"endOffset":24513,"count":13},{"startOffset":24568,"endOffset":24715,"count":1}],"isBlockCoverage":true},{"functionName":"merge","ranges":[{"startOffset":24737,"endOffset":25367,"count":0}],"isBlockCoverage":false},{"functionName":"append","ranges":[{"startOffset":25434,"endOffset":25821,"count":0}],"isBlockCoverage":false},{"functionName":"delete","ranges":[{"startOffset":25826,"endOffset":26325,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":26330,"endOffset":26764,"count":0}],"isBlockCoverage":false},{"functionName":"getAll","ranges":[{"startOffset":26769,"endOffset":27237,"count":0}],"isBlockCoverage":false},{"functionName":"has","ranges":[{"startOffset":27242,"endOffset":27670,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":27675,"endOffset":28701,"count":0}],"isBlockCoverage":false},{"functionName":"sort","ranges":[{"startOffset":28706,"endOffset":29883,"count":0}],"isBlockCoverage":false},{"functionName":"entries","ranges":[{"startOffset":30046,"endOffset":30255,"count":0}],"isBlockCoverage":false},{"functionName":"forEach","ranges":[{"startOffset":30260,"endOffset":30832,"count":0}],"isBlockCoverage":false},{"functionName":"keys","ranges":[{"startOffset":30887,"endOffset":31087,"count":0}],"isBlockCoverage":false},{"functionName":"values","ranges":[{"startOffset":31092,"endOffset":31296,"count":0}],"isBlockCoverage":false},{"functionName":"toString","ranges":[{"startOffset":31429,"endOffset":31629,"count":0}],"isBlockCoverage":false},{"functionName":"createSearchParamsIterator","ranges":[{"startOffset":31909,"endOffset":32112,"count":0}],"isBlockCoverage":false},{"functionName":"next","ranges":[{"startOffset":32336,"endOffset":33089,"count":0}],"isBlockCoverage":false},{"functionName":"defineIDLClass","ranges":[{"startOffset":33093,"endOffset":34307,"count":0}],"isBlockCoverage":false},{"functionName":"domainToASCII","ranges":[{"startOffset":34313,"endOffset":34488,"count":0}],"isBlockCoverage":false},{"functionName":"domainToUnicode","ranges":[{"startOffset":34490,"endOffset":34669,"count":0}],"isBlockCoverage":false},{"functionName":"urlToOptions","ranges":[{"startOffset":34812,"endOffset":35355,"count":0}],"isBlockCoverage":false},{"functionName":"getPathFromURLWin32","ranges":[{"startOffset":35391,"endOffset":36784,"count":0}],"isBlockCoverage":false},{"functionName":"getPathFromURLPosix","ranges":[{"startOffset":36786,"endOffset":37290,"count":5},{"startOffset":36849,"endOffset":36905,"count":0},{"startOffset":36983,"endOffset":37249,"count":208},{"startOffset":37014,"endOffset":37245,"count":0}],"isBlockCoverage":true},{"functionName":"fileURLToPath","ranges":[{"startOffset":37292,"endOffset":37639,"count":5},{"startOffset":37359,"endOffset":37380,"count":1},{"startOffset":37380,"endOffset":37482,"count":4},{"startOffset":37418,"endOffset":37482,"count":0},{"startOffset":37520,"endOffset":37561,"count":0},{"startOffset":37581,"endOffset":37608,"count":0}],"isBlockCoverage":true},{"functionName":"encodePathChars","ranges":[{"startOffset":38399,"endOffset":38955,"count":2},{"startOffset":38470,"endOffset":38519,"count":0},{"startOffset":38625,"endOffset":38676,"count":0},{"startOffset":38712,"endOffset":38761,"count":0},{"startOffset":38797,"endOffset":38853,"count":0},{"startOffset":38889,"endOffset":38934,"count":0}],"isBlockCoverage":true},{"functionName":"pathToFileURL","ranges":[{"startOffset":38957,"endOffset":40072,"count":2},{"startOffset":39045,"endOffset":39075,"count":0},{"startOffset":39077,"endOffset":39626,"count":0},{"startOffset":39881,"endOffset":39920,"count":0},{"startOffset":39923,"endOffset":39976,"count":0},{"startOffset":39984,"endOffset":40000,"count":0}],"isBlockCoverage":true},{"functionName":"isURLInstance","ranges":[{"startOffset":40074,"endOffset":40193,"count":8},{"startOffset":40167,"endOffset":40190,"count":5}],"isBlockCoverage":true},{"functionName":"toPathIfFileURL","ranges":[{"startOffset":40195,"endOffset":40340,"count":4},{"startOffset":40278,"endOffset":40299,"count":3},{"startOffset":40299,"endOffset":40339,"count":1}],"isBlockCoverage":true},{"functionName":"constructUrl","ranges":[{"startOffset":40342,"endOffset":41042,"count":0}],"isBlockCoverage":false}]},{"scriptId":"31","url":"internal/querystring.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2903,"count":1}],"isBlockCoverage":false},{"functionName":"encodeStr","ranges":[{"startOffset":1218,"endOffset":2841,"count":0}],"isBlockCoverage":false}]},{"scriptId":"32","url":"path.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":41803,"count":1}],"isBlockCoverage":false},{"functionName":"isPathSeparator","ranges":[{"startOffset":1492,"endOffset":1596,"count":0}],"isBlockCoverage":false},{"functionName":"isPosixPathSeparator","ranges":[{"startOffset":1598,"endOffset":1675,"count":331}],"isBlockCoverage":true},{"functionName":"isWindowsDeviceRoot","ranges":[{"startOffset":1677,"endOffset":1847,"count":0}],"isBlockCoverage":false},{"functionName":"normalizeString","ranges":[{"startOffset":1910,"endOffset":3714,"count":8},{"startOffset":2127,"endOffset":3698,"count":331},{"startOffset":2160,"endOffset":2186,"count":323},{"startOffset":2186,"endOffset":2277,"count":8},{"startOffset":2235,"endOffset":2277,"count":0},{"startOffset":2277,"endOffset":2310,"count":323},{"startOffset":2310,"endOffset":3599,"count":55},{"startOffset":2342,"endOffset":2355,"count":46},{"startOffset":2357,"endOffset":2382,"count":9},{"startOffset":2382,"endOffset":3556,"count":46},{"startOffset":2404,"endOffset":3343,"count":2},{"startOffset":2460,"endOffset":2518,"count":0},{"startOffset":2519,"endOffset":2577,"count":0},{"startOffset":2715,"endOffset":2791,"count":0},{"startOffset":3024,"endOffset":3193,"count":0},{"startOffset":3203,"endOffset":3335,"count":0},{"startOffset":3343,"endOffset":3556,"count":44},{"startOffset":3389,"endOffset":3442,"count":36},{"startOffset":3442,"endOffset":3501,"count":8},{"startOffset":3556,"endOffset":3599,"count":53},{"startOffset":3599,"endOffset":3694,"count":268},{"startOffset":3627,"endOffset":3641,"count":13},{"startOffset":3643,"endOffset":3664,"count":7},{"startOffset":3664,"endOffset":3694,"count":261}],"isBlockCoverage":true},{"functionName":"_format","ranges":[{"startOffset":3716,"endOffset":4141,"count":0}],"isBlockCoverage":false},{"functionName":"resolve","ranges":[{"startOffset":4195,"endOffset":8598,"count":0}],"isBlockCoverage":false},{"functionName":"normalize","ranges":[{"startOffset":8603,"endOffset":11354,"count":0}],"isBlockCoverage":false},{"functionName":"isAbsolute","ranges":[{"startOffset":11359,"endOffset":11727,"count":0}],"isBlockCoverage":false},{"functionName":"join","ranges":[{"startOffset":11732,"endOffset":13788,"count":0}],"isBlockCoverage":false},{"functionName":"relative","ranges":[{"startOffset":14002,"endOffset":17476,"count":0}],"isBlockCoverage":false},{"functionName":"toNamespacedPath","ranges":[{"startOffset":17481,"endOffset":18509,"count":0}],"isBlockCoverage":false},{"functionName":"dirname","ranges":[{"startOffset":18514,"endOffset":20842,"count":0}],"isBlockCoverage":false},{"functionName":"basename","ranges":[{"startOffset":20847,"endOffset":23482,"count":0}],"isBlockCoverage":false},{"functionName":"extname","ranges":[{"startOffset":23487,"endOffset":25496,"count":0}],"isBlockCoverage":false},{"functionName":"parse","ranges":[{"startOffset":25538,"endOffset":30004,"count":0}],"isBlockCoverage":false},{"functionName":"resolve","ranges":[{"startOffset":30123,"endOffset":31019,"count":8},{"startOffset":30246,"endOffset":30266,"count":23},{"startOffset":30273,"endOffset":30573,"count":15},{"startOffset":30311,"endOffset":30326,"count":0},{"startOffset":30423,"endOffset":30450,"count":0},{"startOffset":30958,"endOffset":31008,"count":0},{"startOffset":31009,"endOffset":31014,"count":0}],"isBlockCoverage":true},{"functionName":"normalize","ranges":[{"startOffset":31024,"endOffset":31599,"count":0}],"isBlockCoverage":false},{"functionName":"isAbsolute","ranges":[{"startOffset":31604,"endOffset":31733,"count":1}],"isBlockCoverage":true},{"functionName":"join","ranges":[{"startOffset":31738,"endOffset":32152,"count":0}],"isBlockCoverage":false},{"functionName":"relative","ranges":[{"startOffset":32157,"endOffset":34367,"count":0}],"isBlockCoverage":false},{"functionName":"toNamespacedPath","ranges":[{"startOffset":34372,"endOffset":34448,"count":10}],"isBlockCoverage":true},{"functionName":"dirname","ranges":[{"startOffset":34453,"endOffset":35081,"count":1},{"startOffset":34536,"endOffset":34547,"count":0},{"startOffset":34705,"endOffset":34942,"count":10},{"startOffset":34760,"endOffset":34844,"count":1},{"startOffset":34844,"endOffset":34936,"count":9},{"startOffset":34970,"endOffset":34997,"count":0},{"startOffset":35034,"endOffset":35046,"count":0}],"isBlockCoverage":true},{"functionName":"basename","ranges":[{"startOffset":35086,"endOffset":37406,"count":0}],"isBlockCoverage":false},{"functionName":"extname","ranges":[{"startOffset":37411,"endOffset":39069,"count":1},{"startOffset":37744,"endOffset":38690,"count":10},{"startOffset":37824,"endOffset":38070,"count":1},{"startOffset":38044,"endOffset":38070,"count":0},{"startOffset":38070,"endOffset":38093,"count":9},{"startOffset":38093,"endOffset":38250,"count":1},{"startOffset":38250,"endOffset":38280,"count":9},{"startOffset":38280,"endOffset":38480,"count":1},{"startOffset":38409,"endOffset":38472,"count":0},{"startOffset":38480,"endOffset":38684,"count":8},{"startOffset":38507,"endOffset":38684,"count":6},{"startOffset":38929,"endOffset":38961,"count":0},{"startOffset":38962,"endOffset":39000,"count":0},{"startOffset":39003,"endOffset":39027,"count":0}],"isBlockCoverage":true},{"functionName":"parse","ranges":[{"startOffset":39110,"endOffset":41464,"count":0}],"isBlockCoverage":false}]},{"scriptId":"33","url":"internal/encoding.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":15873,"count":1}],"isBlockCoverage":false},{"functionName":"lazyBuffer","ranges":[{"startOffset":958,"endOffset":1067,"count":0}],"isBlockCoverage":false},{"functionName":"validateEncoder","ranges":[{"startOffset":1069,"endOffset":1194,"count":0}],"isBlockCoverage":false},{"functionName":"validateDecoder","ranges":[{"startOffset":1196,"endOffset":1321,"count":1},{"startOffset":1277,"endOffset":1319,"count":0}],"isBlockCoverage":true},{"functionName":"validateArgument","ranges":[{"startOffset":1323,"endOffset":1490,"count":2},{"startOffset":1427,"endOffset":1488,"count":0}],"isBlockCoverage":true},{"functionName":"trimAsciiWhitespace","ranges":[{"startOffset":8231,"endOffset":8713,"count":0}],"isBlockCoverage":false},{"functionName":"getEncodingFromLabel","ranges":[{"startOffset":8715,"endOffset":8894,"count":1},{"startOffset":8826,"endOffset":8893,"count":0}],"isBlockCoverage":true},{"functionName":"TextEncoder","ranges":[{"startOffset":8965,"endOffset":9011,"count":0}],"isBlockCoverage":false},{"functionName":"get encoding","ranges":[{"startOffset":9015,"endOffset":9082,"count":0}],"isBlockCoverage":false},{"functionName":"encode","ranges":[{"startOffset":9086,"endOffset":9178,"count":0}],"isBlockCoverage":false},{"functionName":"encodeInto","ranges":[{"startOffset":9182,"endOffset":9492,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":9496,"endOffset":9891,"count":0}],"isBlockCoverage":false},{"functionName":"makeTextDecoderICU","ranges":[{"startOffset":10262,"endOffset":11923,"count":1}],"isBlockCoverage":true},{"functionName":"TextDecoder","ranges":[{"startOffset":10400,"endOffset":11127,"count":1},{"startOffset":10632,"endOffset":10679,"count":0},{"startOffset":10763,"endOffset":10786,"count":0},{"startOffset":10827,"endOffset":10855,"count":0},{"startOffset":10957,"endOffset":11004,"count":0}],"isBlockCoverage":true},{"functionName":"decode","ranges":[{"startOffset":11134,"endOffset":11894,"count":1},{"startOffset":11236,"endOffset":11287,"count":0},{"startOffset":11324,"endOffset":11503,"count":0},{"startOffset":11650,"endOffset":11653,"count":0},{"startOffset":11771,"endOffset":11853,"count":0}],"isBlockCoverage":true},{"functionName":"makeTextDecoderJS","ranges":[{"startOffset":11925,"endOffset":14466,"count":0}],"isBlockCoverage":false},{"functionName":"get encoding","ranges":[{"startOffset":14591,"endOffset":14672,"count":0}],"isBlockCoverage":false},{"functionName":"get fatal","ranges":[{"startOffset":14679,"endOffset":14806,"count":0}],"isBlockCoverage":false},{"functionName":"get ignoreBOM","ranges":[{"startOffset":14813,"endOffset":14968,"count":0}],"isBlockCoverage":false},{"functionName":"ObjectGetOwnPropertyDescriptors","ranges":[{"startOffset":14975,"endOffset":15589,"count":0}],"isBlockCoverage":false}]},{"scriptId":"34","url":"timers.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":8988,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":1805,"endOffset":1830,"count":0}],"isBlockCoverage":false},{"functionName":"unenroll","ranges":[{"startOffset":2306,"endOffset":3490,"count":0}],"isBlockCoverage":false},{"functionName":"enroll","ranges":[{"startOffset":3697,"endOffset":3950,"count":0}],"isBlockCoverage":false},{"functionName":"setTimeout","ranges":[{"startOffset":3982,"endOffset":4627,"count":0}],"isBlockCoverage":false},{"functionName":"setTimeout.","ranges":[{"startOffset":4659,"endOffset":4892,"count":0}],"isBlockCoverage":false},{"functionName":"clearTimeout","ranges":[{"startOffset":4895,"endOffset":5261,"count":0}],"isBlockCoverage":false},{"functionName":"setInterval","ranges":[{"startOffset":5263,"endOffset":5910,"count":0}],"isBlockCoverage":false},{"functionName":"clearInterval","ranges":[{"startOffset":5912,"endOffset":6210,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.close","ranges":[{"startOffset":6238,"endOffset":6289,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.","ranges":[{"startOffset":6331,"endOffset":6492,"count":0}],"isBlockCoverage":false},{"functionName":"Immediate","ranges":[{"startOffset":6533,"endOffset":6855,"count":0}],"isBlockCoverage":false},{"functionName":"ref","ranges":[{"startOffset":6859,"endOffset":7032,"count":0}],"isBlockCoverage":false},{"functionName":"unref","ranges":[{"startOffset":7036,"endOffset":7212,"count":0}],"isBlockCoverage":false},{"functionName":"hasRef","ranges":[{"startOffset":7216,"endOffset":7257,"count":0}],"isBlockCoverage":false},{"functionName":"setImmediate","ranges":[{"startOffset":7262,"endOffset":7803,"count":0}],"isBlockCoverage":false},{"functionName":"setImmediate.","ranges":[{"startOffset":7837,"endOffset":7924,"count":0}],"isBlockCoverage":false},{"functionName":"clearImmediate","ranges":[{"startOffset":7927,"endOffset":8349,"count":0}],"isBlockCoverage":false}]},{"scriptId":"35","url":"internal/linkedlist.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1112,"count":1}],"isBlockCoverage":false},{"functionName":"init","ranges":[{"startOffset":15,"endOffset":88,"count":0}],"isBlockCoverage":false},{"functionName":"peek","ranges":[{"startOffset":118,"endOffset":210,"count":0}],"isBlockCoverage":false},{"functionName":"remove","ranges":[{"startOffset":245,"endOffset":472,"count":0}],"isBlockCoverage":false},{"functionName":"append","ranges":[{"startOffset":528,"endOffset":980,"count":0}],"isBlockCoverage":false},{"functionName":"isEmpty","ranges":[{"startOffset":982,"endOffset":1042,"count":0}],"isBlockCoverage":false}]},{"scriptId":"36","url":"internal/timers.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":17730,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":4376,"endOffset":4401,"count":0}],"isBlockCoverage":false},{"functionName":"initAsyncResource","ranges":[{"startOffset":5344,"endOffset":5622,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout","ranges":[{"startOffset":5707,"endOffset":6704,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.","ranges":[{"startOffset":6817,"endOffset":6988,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.refresh","ranges":[{"startOffset":7019,"endOffset":7117,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.unref","ranges":[{"startOffset":7146,"endOffset":7274,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.ref","ranges":[{"startOffset":7301,"endOffset":7429,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.hasRef","ranges":[{"startOffset":7459,"endOffset":7496,"count":0}],"isBlockCoverage":false},{"functionName":"TimersList","ranges":[{"startOffset":7499,"endOffset":7799,"count":0}],"isBlockCoverage":false},{"functionName":"TimersList.","ranges":[{"startOffset":7915,"endOffset":8086,"count":0}],"isBlockCoverage":false},{"functionName":"ImmediateList","ranges":[{"startOffset":8144,"endOffset":8212,"count":2}],"isBlockCoverage":true},{"functionName":"ImmediateList.append","ranges":[{"startOffset":8391,"endOffset":8558,"count":0}],"isBlockCoverage":false},{"functionName":"ImmediateList.remove","ranges":[{"startOffset":8736,"endOffset":9091,"count":0}],"isBlockCoverage":false},{"functionName":"incRefCount","ranges":[{"startOffset":9094,"endOffset":9170,"count":0}],"isBlockCoverage":false},{"functionName":"decRefCount","ranges":[{"startOffset":9172,"endOffset":9249,"count":0}],"isBlockCoverage":false},{"functionName":"active","ranges":[{"startOffset":9332,"endOffset":9386,"count":0}],"isBlockCoverage":false},{"functionName":"unrefActive","ranges":[{"startOffset":9533,"endOffset":9593,"count":0}],"isBlockCoverage":false},{"functionName":"insertGuarded","ranges":[{"startOffset":9814,"endOffset":10330,"count":0}],"isBlockCoverage":false},{"functionName":"insert","ranges":[{"startOffset":10332,"endOffset":10983,"count":0}],"isBlockCoverage":false},{"functionName":"setUnrefTimeout","ranges":[{"startOffset":10985,"endOffset":11291,"count":0}],"isBlockCoverage":false},{"functionName":"getTimerDuration","ranges":[{"startOffset":11358,"endOffset":11880,"count":0}],"isBlockCoverage":false},{"functionName":"compareTimersLists","ranges":[{"startOffset":11882,"endOffset":12087,"count":0}],"isBlockCoverage":false},{"functionName":"setPosition","ranges":[{"startOffset":12089,"endOffset":12160,"count":0}],"isBlockCoverage":false},{"functionName":"getTimerCallbacks","ranges":[{"startOffset":12162,"endOffset":17259,"count":1}],"isBlockCoverage":true},{"functionName":"processImmediate","ranges":[{"startOffset":12474,"endOffset":14275,"count":0}],"isBlockCoverage":false},{"functionName":"processTimers","ranges":[{"startOffset":14280,"endOffset":14754,"count":0}],"isBlockCoverage":false},{"functionName":"listOnTimeout","ranges":[{"startOffset":14758,"endOffset":17200,"count":0}],"isBlockCoverage":false}]},{"scriptId":"37","url":"internal/priority_queue.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2924,"count":1}],"isBlockCoverage":false},{"functionName":"PriorityQueue","ranges":[{"startOffset":570,"endOffset":811,"count":1}],"isBlockCoverage":true},{"functionName":"module.exports","ranges":[{"startOffset":815,"endOffset":855,"count":0}],"isBlockCoverage":false},{"functionName":"insert","ranges":[{"startOffset":859,"endOffset":1044,"count":0}],"isBlockCoverage":false},{"functionName":"peek","ranges":[{"startOffset":1048,"endOffset":1087,"count":0}],"isBlockCoverage":false},{"functionName":"percolateDown","ranges":[{"startOffset":1091,"endOffset":1759,"count":0}],"isBlockCoverage":false},{"functionName":"percolateUp","ranges":[{"startOffset":1763,"endOffset":2254,"count":0}],"isBlockCoverage":false},{"functionName":"removeAt","ranges":[{"startOffset":2258,"endOffset":2591,"count":0}],"isBlockCoverage":false},{"functionName":"remove","ranges":[{"startOffset":2595,"endOffset":2761,"count":0}],"isBlockCoverage":false},{"functionName":"shift","ranges":[{"startOffset":2765,"endOffset":2920,"count":0}],"isBlockCoverage":false}]},{"scriptId":"38","url":"internal/util/debuglog.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2815,"count":1}],"isBlockCoverage":false},{"functionName":"initializeDebugEnv","ranges":[{"startOffset":500,"endOffset":873,"count":1},{"startOffset":591,"endOffset":790,"count":0}],"isBlockCoverage":true},{"functionName":"emitWarningIfNeeded","ranges":[{"startOffset":947,"endOffset":1272,"count":0}],"isBlockCoverage":false},{"functionName":"noop","ranges":[{"startOffset":1274,"endOffset":1292,"count":7}],"isBlockCoverage":true},{"functionName":"debuglogImpl","ranges":[{"startOffset":1294,"endOffset":1859,"count":3},{"startOffset":1369,"endOffset":1831,"count":2},{"startOffset":1388,"endOffset":1784,"count":0}],"isBlockCoverage":true},{"functionName":"debug","ranges":[{"startOffset":1477,"endOffset":1777,"count":0}],"isBlockCoverage":false},{"functionName":"debuglog","ranges":[{"startOffset":2079,"endOffset":2758,"count":9}],"isBlockCoverage":true},{"functionName":"init","ranges":[{"startOffset":2110,"endOffset":2206,"count":3}],"isBlockCoverage":true},{"functionName":"debug","ranges":[{"startOffset":2221,"endOffset":2458,"count":3}],"isBlockCoverage":true},{"functionName":"test","ranges":[{"startOffset":2488,"endOffset":2557,"count":0}],"isBlockCoverage":false},{"functionName":"logger","ranges":[{"startOffset":2576,"endOffset":2603,"count":3}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":2653,"endOffset":2687,"count":0}],"isBlockCoverage":false}]},{"scriptId":"39","url":"internal/process/execution.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":6944,"count":1}],"isBlockCoverage":false},{"functionName":"tryGetCwd","ranges":[{"startOffset":526,"endOffset":856,"count":1},{"startOffset":587,"endOffset":854,"count":0}],"isBlockCoverage":true},{"functionName":"evalModule","ranges":[{"startOffset":858,"endOffset":1298,"count":1},{"startOffset":908,"endOffset":956,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":1178,"endOffset":1293,"count":1},{"startOffset":1247,"endOffset":1289,"count":0}],"isBlockCoverage":true},{"functionName":"evalScript","ranges":[{"startOffset":1300,"endOffset":2682,"count":0}],"isBlockCoverage":false},{"functionName":"setUncaughtExceptionCaptureCallback","ranges":[{"startOffset":2759,"endOffset":3453,"count":0}],"isBlockCoverage":false},{"functionName":"hasUncaughtExceptionCaptureCallback","ranges":[{"startOffset":3455,"endOffset":3556,"count":0}],"isBlockCoverage":false},{"functionName":"noop","ranges":[{"startOffset":3558,"endOffset":3576,"count":0}],"isBlockCoverage":false},{"functionName":"createOnGlobalUncaughtException","ranges":[{"startOffset":4119,"endOffset":6518,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":4388,"endOffset":6515,"count":0}],"isBlockCoverage":false},{"functionName":"readStdin","ranges":[{"startOffset":6520,"endOffset":6725,"count":0}],"isBlockCoverage":false}]},{"scriptId":"40","url":"internal/process/warning.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":4833,"count":1}],"isBlockCoverage":false},{"functionName":"lazyOption","ranges":[{"startOffset":277,"endOffset":831,"count":0}],"isBlockCoverage":false},{"functionName":"writeOut","ranges":[{"startOffset":932,"endOffset":1054,"count":0}],"isBlockCoverage":false},{"functionName":"writeToFile","ranges":[{"startOffset":1056,"endOffset":1440,"count":0}],"isBlockCoverage":false},{"functionName":"doEmitWarning","ranges":[{"startOffset":1442,"endOffset":1513,"count":0}],"isBlockCoverage":false},{"functionName":"onWarning","ranges":[{"startOffset":1552,"endOffset":2730,"count":0}],"isBlockCoverage":false},{"functionName":"emitWarning","ranges":[{"startOffset":2853,"endOffset":3997,"count":0}],"isBlockCoverage":false},{"functionName":"emitWarningSync","ranges":[{"startOffset":3999,"endOffset":4093,"count":0}],"isBlockCoverage":false},{"functionName":"createWarningObject","ranges":[{"startOffset":4095,"endOffset":4762,"count":0}],"isBlockCoverage":false}]},{"scriptId":"41","url":"internal/bootstrap/switches/is_main_thread.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":6397,"count":1}],"isBlockCoverage":true},{"functionName":"process._startProfilerIdleNotifier","ranges":[{"startOffset":513,"endOffset":521,"count":0}],"isBlockCoverage":false},{"functionName":"process._stopProfilerIdleNotifier","ranges":[{"startOffset":559,"endOffset":567,"count":0}],"isBlockCoverage":false},{"functionName":"defineStream","ranges":[{"startOffset":570,"endOffset":717,"count":3}],"isBlockCoverage":true},{"functionName":"createWritableStdioStream","ranges":[{"startOffset":1278,"endOffset":2845,"count":0}],"isBlockCoverage":false},{"functionName":"dummyDestroy","ranges":[{"startOffset":2847,"endOffset":3230,"count":0}],"isBlockCoverage":false},{"functionName":"getStdout","ranges":[{"startOffset":3268,"endOffset":3599,"count":0}],"isBlockCoverage":false},{"functionName":"getStderr","ranges":[{"startOffset":3601,"endOffset":3932,"count":0}],"isBlockCoverage":false},{"functionName":"getStdin","ranges":[{"startOffset":3934,"endOffset":6253,"count":0}],"isBlockCoverage":false},{"functionName":"rawMethods.resetStdioForTesting","ranges":[{"startOffset":6316,"endOffset":6395,"count":0}],"isBlockCoverage":false}]},{"scriptId":"42","url":"internal/process/signal.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1124,"count":1}],"isBlockCoverage":false},{"functionName":"isSignal","ranges":[{"startOffset":205,"endOffset":301,"count":3}],"isBlockCoverage":true},{"functionName":"startListeningIfSignal","ranges":[{"startOffset":365,"endOffset":853,"count":3},{"startOffset":426,"endOffset":451,"count":0},{"startOffset":453,"endOffset":851,"count":0}],"isBlockCoverage":true},{"functionName":"stopListeningIfSignal","ranges":[{"startOffset":855,"endOffset":1050,"count":0}],"isBlockCoverage":false}]},{"scriptId":"43","url":"internal/bootstrap/switches/does_own_process_state.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":3480,"count":1}],"isBlockCoverage":true},{"functionName":"wrapPosixCredentialSetters","ranges":[{"startOffset":817,"endOffset":2914,"count":1}],"isBlockCoverage":true},{"functionName":"initgroups","ranges":[{"startOffset":1278,"endOffset":1695,"count":0}],"isBlockCoverage":false},{"functionName":"setgroups","ranges":[{"startOffset":1699,"endOffset":2179,"count":0}],"isBlockCoverage":false},{"functionName":"wrapIdSetter","ranges":[{"startOffset":2183,"endOffset":2465,"count":4}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":2232,"endOffset":2460,"count":0}],"isBlockCoverage":false},{"functionName":"validateId","ranges":[{"startOffset":2469,"endOffset":2687,"count":0}],"isBlockCoverage":false},{"functionName":"wrappedChdir","ranges":[{"startOffset":3065,"endOffset":3236,"count":0}],"isBlockCoverage":false},{"functionName":"wrappedUmask","ranges":[{"startOffset":3238,"endOffset":3374,"count":0}],"isBlockCoverage":false},{"functionName":"wrappedCwd","ranges":[{"startOffset":3376,"endOffset":3479,"count":2},{"startOffset":3428,"endOffset":3457,"count":1}],"isBlockCoverage":true}]},{"scriptId":"44","url":"internal/main/eval_string.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":760,"count":1},{"startOffset":643,"endOffset":759,"count":0}],"isBlockCoverage":true}]},{"scriptId":"45","url":"internal/bootstrap/pre_execution.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":14933,"count":1}],"isBlockCoverage":true},{"functionName":"prepareMainThreadExecution","ranges":[{"startOffset":384,"endOffset":2511,"count":1},{"startOffset":1122,"endOffset":1344,"count":0}],"isBlockCoverage":true},{"functionName":"patchProcessObject","ranges":[{"startOffset":2513,"endOffset":4090,"count":1},{"startOffset":2876,"endOffset":2894,"count":0},{"startOffset":2895,"endOffset":2952,"count":0},{"startOffset":2954,"endOffset":3121,"count":0}],"isBlockCoverage":true},{"functionName":"addReadOnlyProcessAlias","ranges":[{"startOffset":4092,"endOffset":4349,"count":13},{"startOffset":4213,"endOffset":4347,"count":2}],"isBlockCoverage":true},{"functionName":"setupWarningHandler","ranges":[{"startOffset":4351,"endOffset":4580,"count":1}],"isBlockCoverage":true},{"functionName":"setupCoverageHooks","ranges":[{"startOffset":4692,"endOffset":5369,"count":1},{"startOffset":5162,"endOffset":5339,"count":0}],"isBlockCoverage":true},{"functionName":"setupStacktracePrinterOnSigint","ranges":[{"startOffset":5371,"endOffset":5596,"count":1},{"startOffset":5473,"endOffset":5595,"count":0}],"isBlockCoverage":true},{"functionName":"initializeReport","ranges":[{"startOffset":5598,"endOffset":5822,"count":1}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":5780,"endOffset":5814,"count":0}],"isBlockCoverage":false},{"functionName":"setupDebugEnv","ranges":[{"startOffset":5824,"endOffset":6056,"count":1},{"startOffset":5975,"endOffset":6054,"count":0}],"isBlockCoverage":true},{"functionName":"initializeReportSignalHandlers","ranges":[{"startOffset":6118,"endOffset":6253,"count":1}],"isBlockCoverage":true},{"functionName":"initializeHeapSnapshotSignalHandlers","ranges":[{"startOffset":6255,"endOffset":6562,"count":1},{"startOffset":6390,"endOffset":6561,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":6522,"endOffset":6558,"count":0}],"isBlockCoverage":false},{"functionName":"setupTraceCategoryState","ranges":[{"startOffset":6564,"endOffset":6823,"count":1}],"isBlockCoverage":true},{"functionName":"setupInspectorHooks","ranges":[{"startOffset":6825,"endOffset":7406,"count":1}],"isBlockCoverage":true},{"functionName":"initializeDeprecations","ranges":[{"startOffset":7601,"endOffset":10122,"count":1},{"startOffset":8312,"endOffset":8614,"count":16},{"startOffset":8357,"endOffset":8589,"count":0},{"startOffset":8931,"endOffset":9104,"count":0},{"startOffset":9132,"endOffset":9526,"count":0}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":9775,"endOffset":9811,"count":2}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":9817,"endOffset":9859,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":9985,"endOffset":10020,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":10026,"endOffset":10067,"count":0}],"isBlockCoverage":false},{"functionName":"setupChildProcessIpcChannel","ranges":[{"startOffset":10124,"endOffset":10689,"count":1},{"startOffset":10200,"endOffset":10687,"count":0}],"isBlockCoverage":true},{"functionName":"initializeClusterIPC","ranges":[{"startOffset":10691,"endOffset":10961,"count":1},{"startOffset":10778,"endOffset":10959,"count":0}],"isBlockCoverage":true},{"functionName":"initializePolicy","ranges":[{"startOffset":10963,"endOffset":12787,"count":1},{"startOffset":11089,"endOffset":12785,"count":0}],"isBlockCoverage":true},{"functionName":"initializeWASI","ranges":[{"startOffset":12789,"endOffset":13018,"count":1}],"isBlockCoverage":true},{"functionName":"initializeCJSLoader","ranges":[{"startOffset":13020,"endOffset":13306,"count":1}],"isBlockCoverage":true},{"functionName":"initializeESMLoader","ranges":[{"startOffset":13308,"endOffset":13976,"count":1},{"startOffset":13517,"endOffset":13524,"count":0}],"isBlockCoverage":true},{"functionName":"initializeFrozenIntrinsics","ranges":[{"startOffset":13978,"endOffset":14235,"count":1},{"startOffset":14063,"endOffset":14233,"count":0}],"isBlockCoverage":true},{"functionName":"loadPreloadModules","ranges":[{"startOffset":14237,"endOffset":14584,"count":1},{"startOffset":14431,"endOffset":14582,"count":0}],"isBlockCoverage":true}]},{"scriptId":"46","url":"internal/options.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":898,"count":1}],"isBlockCoverage":false},{"functionName":"getOptionValue","ranges":[{"startOffset":175,"endOffset":314,"count":47},{"startOffset":262,"endOffset":289,"count":0}],"isBlockCoverage":true},{"functionName":"getAllowUnauthorized","ranges":[{"startOffset":316,"endOffset":781,"count":0}],"isBlockCoverage":false}]},{"scriptId":"47","url":"internal/modules/cjs/helpers.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":5427,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":681,"endOffset":706,"count":1}],"isBlockCoverage":true},{"functionName":"loadNativeModule","ranges":[{"startOffset":860,"endOffset":1066,"count":1}],"isBlockCoverage":true},{"functionName":"makeRequireFunction","ranges":[{"startOffset":1315,"endOffset":3313,"count":0}],"isBlockCoverage":false},{"functionName":"stripBOM","ranges":[{"startOffset":3498,"endOffset":3624,"count":0}],"isBlockCoverage":false},{"functionName":"addBuiltinLibsToObject","ranges":[{"startOffset":3626,"endOffset":5091,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":3832,"endOffset":5087,"count":55},{"startOffset":4006,"endOffset":4035,"count":41},{"startOffset":4036,"endOffset":4090,"count":40},{"startOffset":4092,"endOffset":4113,"count":17},{"startOffset":4113,"endOffset":5086,"count":38}],"isBlockCoverage":true},{"functionName":"setReal","ranges":[{"startOffset":4415,"endOffset":4585,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":4640,"endOffset":5004,"count":0}],"isBlockCoverage":false},{"functionName":"normalizeReferrerURL","ranges":[{"startOffset":5093,"endOffset":5281,"count":1},{"startOffset":5200,"endOffset":5246,"count":0}],"isBlockCoverage":true}]},{"scriptId":"48","url":"url.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":29943,"count":1}],"isBlockCoverage":false},{"functionName":"Url","ranges":[{"startOffset":1850,"endOffset":2126,"count":0}],"isBlockCoverage":false},{"functionName":"urlParse","ranges":[{"startOffset":3804,"endOffset":4018,"count":0}],"isBlockCoverage":false},{"functionName":"parse","ranges":[{"startOffset":4042,"endOffset":13470,"count":0}],"isBlockCoverage":false},{"functionName":"getHostname","ranges":[{"startOffset":13473,"endOffset":14198,"count":0}],"isBlockCoverage":false},{"functionName":"autoEscapeStr","ranges":[{"startOffset":15254,"endOffset":15960,"count":0}],"isBlockCoverage":false},{"functionName":"urlFormat","ranges":[{"startOffset":16006,"endOffset":16716,"count":0}],"isBlockCoverage":false},{"functionName":"format","ranges":[{"startOffset":17423,"endOffset":19839,"count":0}],"isBlockCoverage":false},{"functionName":"urlResolve","ranges":[{"startOffset":19842,"endOffset":19941,"count":0}],"isBlockCoverage":false},{"functionName":"resolve","ranges":[{"startOffset":19967,"endOffset":20068,"count":0}],"isBlockCoverage":false},{"functionName":"urlResolveObject","ranges":[{"startOffset":20071,"endOffset":20214,"count":0}],"isBlockCoverage":false},{"functionName":"resolveObject","ranges":[{"startOffset":20246,"endOffset":29365,"count":0}],"isBlockCoverage":false},{"functionName":"parseHost","ranges":[{"startOffset":29394,"endOffset":29667,"count":0}],"isBlockCoverage":false}]},{"scriptId":"49","url":"internal/idna.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":264,"count":1}],"isBlockCoverage":false}]},{"scriptId":"50","url":"internal/inspector_async_hook.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1972,"count":1}],"isBlockCoverage":false},{"functionName":"lazyHookCreation","ranges":[{"startOffset":75,"endOffset":1257,"count":0}],"isBlockCoverage":false},{"functionName":"enable","ranges":[{"startOffset":1259,"endOffset":1840,"count":0}],"isBlockCoverage":false},{"functionName":"disable","ranges":[{"startOffset":1842,"endOffset":1928,"count":0}],"isBlockCoverage":false}]},{"scriptId":"51","url":"internal/source_map/source_map_cache.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":8219,"count":1}],"isBlockCoverage":false},{"functionName":"ObjectGetValueSafe","ranges":[{"startOffset":305,"endOffset":483,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":665,"endOffset":690,"count":0}],"isBlockCoverage":false},{"functionName":"maybeCacheSourceMap","ranges":[{"startOffset":1287,"endOffset":2534,"count":1},{"startOffset":1512,"endOffset":1537,"count":0},{"startOffset":1540,"endOffset":1547,"count":0},{"startOffset":1672,"endOffset":1810,"count":0},{"startOffset":1914,"endOffset":2532,"count":0}],"isBlockCoverage":true},{"functionName":"dataFromUrl","ranges":[{"startOffset":2536,"endOffset":3068,"count":0}],"isBlockCoverage":false},{"functionName":"lineLengths","ranges":[{"startOffset":3258,"endOffset":3566,"count":0}],"isBlockCoverage":false},{"functionName":"sourceMapFromFile","ranges":[{"startOffset":3568,"endOffset":3839,"count":0}],"isBlockCoverage":false},{"functionName":"sourceMapFromDataUrl","ranges":[{"startOffset":3933,"endOffset":4568,"count":0}],"isBlockCoverage":false},{"functionName":"sourcesToAbsolute","ranges":[{"startOffset":4753,"endOffset":5203,"count":0}],"isBlockCoverage":false},{"functionName":"rekeySourceMap","ranges":[{"startOffset":5272,"endOffset":5467,"count":0}],"isBlockCoverage":false},{"functionName":"sourceMapCacheToObject","ranges":[{"startOffset":5905,"endOffset":6274,"count":0}],"isBlockCoverage":false},{"functionName":"appendCJSCache","ranges":[{"startOffset":6513,"endOffset":7168,"count":0}],"isBlockCoverage":false},{"functionName":"findSourceMap","ranges":[{"startOffset":7386,"endOffset":8111,"count":0}],"isBlockCoverage":false}]},{"scriptId":"52","url":"fs.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":57858,"count":1}],"isBlockCoverage":false},{"functionName":"showTruncateDeprecation","ranges":[{"startOffset":3832,"endOffset":4119,"count":0}],"isBlockCoverage":false},{"functionName":"maybeCallback","ranges":[{"startOffset":4121,"endOffset":4237,"count":0}],"isBlockCoverage":false},{"functionName":"makeCallback","ranges":[{"startOffset":4440,"endOffset":4583,"count":0}],"isBlockCoverage":false},{"functionName":"makeStatsCallback","ranges":[{"startOffset":4764,"endOffset":4979,"count":0}],"isBlockCoverage":false},{"functionName":"isFileType","ranges":[{"startOffset":5005,"endOffset":5265,"count":7},{"startOffset":5204,"endOffset":5224,"count":6}],"isBlockCoverage":true},{"functionName":"access","ranges":[{"startOffset":5267,"endOffset":5625,"count":0}],"isBlockCoverage":false},{"functionName":"accessSync","ranges":[{"startOffset":5627,"endOffset":5865,"count":0}],"isBlockCoverage":false},{"functionName":"exists","ranges":[{"startOffset":5867,"endOffset":6105,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":6178,"endOffset":6256,"count":0}],"isBlockCoverage":false},{"functionName":"existsSync","ranges":[{"startOffset":6673,"endOffset":7203,"count":0}],"isBlockCoverage":false},{"functionName":"readFileAfterOpen","ranges":[{"startOffset":7205,"endOffset":7485,"count":0}],"isBlockCoverage":false},{"functionName":"readFileAfterStat","ranges":[{"startOffset":7487,"endOffset":7988,"count":0}],"isBlockCoverage":false},{"functionName":"readFile","ranges":[{"startOffset":7990,"endOffset":8816,"count":0}],"isBlockCoverage":false},{"functionName":"tryStatSync","ranges":[{"startOffset":8818,"endOffset":9048,"count":1},{"startOffset":8961,"endOffset":8973,"count":0},{"startOffset":8975,"endOffset":9030,"count":0}],"isBlockCoverage":true},{"functionName":"tryCreateBuffer","ranges":[{"startOffset":9050,"endOffset":9361,"count":1},{"startOffset":9168,"endOffset":9220,"count":0},{"startOffset":9307,"endOffset":9319,"count":0},{"startOffset":9321,"endOffset":9338,"count":0}],"isBlockCoverage":true},{"functionName":"tryReadSync","ranges":[{"startOffset":9363,"endOffset":9618,"count":1},{"startOffset":9561,"endOffset":9573,"count":0},{"startOffset":9575,"endOffset":9592,"count":0}],"isBlockCoverage":true},{"functionName":"readFileSync","ranges":[{"startOffset":9620,"endOffset":11003,"count":1},{"startOffset":9789,"endOffset":9795,"count":0},{"startOffset":9935,"endOffset":9938,"count":0},{"startOffset":10068,"endOffset":10091,"count":0},{"startOffset":10343,"endOffset":10697,"count":0},{"startOffset":10757,"endOffset":10853,"count":0},{"startOffset":10875,"endOffset":10915,"count":0}],"isBlockCoverage":true},{"functionName":"close","ranges":[{"startOffset":11005,"endOffset":11195,"count":0}],"isBlockCoverage":false},{"functionName":"closeSync","ranges":[{"startOffset":11197,"endOffset":11340,"count":1}],"isBlockCoverage":true},{"functionName":"open","ranges":[{"startOffset":11342,"endOffset":11919,"count":0}],"isBlockCoverage":false},{"functionName":"openSync","ranges":[{"startOffset":11922,"endOffset":12318,"count":1}],"isBlockCoverage":true},{"functionName":"read","ranges":[{"startOffset":12425,"endOffset":13923,"count":0}],"isBlockCoverage":false},{"functionName":"readSync","ranges":[{"startOffset":14192,"endOffset":15107,"count":1},{"startOffset":14310,"endOffset":14459,"count":0},{"startOffset":14510,"endOffset":14531,"count":0},{"startOffset":14619,"endOffset":14638,"count":0},{"startOffset":14671,"endOffset":14801,"count":0}],"isBlockCoverage":true},{"functionName":"readv","ranges":[{"startOffset":15109,"endOffset":15539,"count":0}],"isBlockCoverage":false},{"functionName":"readvSync","ranges":[{"startOffset":15682,"endOffset":15992,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":16138,"endOffset":17406,"count":0}],"isBlockCoverage":false},{"functionName":"writeSync","ranges":[{"startOffset":17683,"endOffset":18549,"count":0}],"isBlockCoverage":false},{"functionName":"writev","ranges":[{"startOffset":18610,"endOffset":19038,"count":0}],"isBlockCoverage":false},{"functionName":"writevSync","ranges":[{"startOffset":19167,"endOffset":19480,"count":0}],"isBlockCoverage":false},{"functionName":"rename","ranges":[{"startOffset":19482,"endOffset":19863,"count":0}],"isBlockCoverage":false},{"functionName":"renameSync","ranges":[{"startOffset":19865,"endOffset":20212,"count":0}],"isBlockCoverage":false},{"functionName":"truncate","ranges":[{"startOffset":20214,"endOffset":20832,"count":0}],"isBlockCoverage":false},{"functionName":"truncateSync","ranges":[{"startOffset":20834,"endOffset":21237,"count":0}],"isBlockCoverage":false},{"functionName":"ftruncate","ranges":[{"startOffset":21239,"endOffset":21579,"count":0}],"isBlockCoverage":false},{"functionName":"ftruncateSync","ranges":[{"startOffset":21581,"endOffset":21801,"count":0}],"isBlockCoverage":false},{"functionName":"lazyLoadRimraf","ranges":[{"startOffset":21804,"endOffset":21923,"count":0}],"isBlockCoverage":false},{"functionName":"rmdir","ranges":[{"startOffset":21925,"endOffset":22565,"count":0}],"isBlockCoverage":false},{"functionName":"rmdirSync","ranges":[{"startOffset":22567,"endOffset":23005,"count":0}],"isBlockCoverage":false},{"functionName":"rm","ranges":[{"startOffset":23007,"endOffset":23345,"count":0}],"isBlockCoverage":false},{"functionName":"rmSync","ranges":[{"startOffset":23347,"endOffset":23517,"count":0}],"isBlockCoverage":false},{"functionName":"fdatasync","ranges":[{"startOffset":23519,"endOffset":23693,"count":0}],"isBlockCoverage":false},{"functionName":"fdatasyncSync","ranges":[{"startOffset":23695,"endOffset":23845,"count":0}],"isBlockCoverage":false},{"functionName":"fsync","ranges":[{"startOffset":23847,"endOffset":24013,"count":0}],"isBlockCoverage":false},{"functionName":"fsyncSync","ranges":[{"startOffset":24015,"endOffset":24157,"count":0}],"isBlockCoverage":false},{"functionName":"mkdir","ranges":[{"startOffset":24159,"endOffset":24940,"count":0}],"isBlockCoverage":false},{"functionName":"mkdirSync","ranges":[{"startOffset":24942,"endOffset":25714,"count":0}],"isBlockCoverage":false},{"functionName":"readdir","ranges":[{"startOffset":25716,"endOffset":26297,"count":0}],"isBlockCoverage":false},{"functionName":"readdirSync","ranges":[{"startOffset":26299,"endOffset":26725,"count":0}],"isBlockCoverage":false},{"functionName":"fstat","ranges":[{"startOffset":26727,"endOffset":27066,"count":0}],"isBlockCoverage":false},{"functionName":"lstat","ranges":[{"startOffset":27068,"endOffset":27443,"count":0}],"isBlockCoverage":false},{"functionName":"stat","ranges":[{"startOffset":27445,"endOffset":27818,"count":0}],"isBlockCoverage":false},{"functionName":"fstatSync","ranges":[{"startOffset":27820,"endOffset":28062,"count":0}],"isBlockCoverage":false},{"functionName":"lstatSync","ranges":[{"startOffset":28064,"endOffset":28374,"count":0}],"isBlockCoverage":false},{"functionName":"statSync","ranges":[{"startOffset":28376,"endOffset":28683,"count":1}],"isBlockCoverage":true},{"functionName":"readlink","ranges":[{"startOffset":28685,"endOffset":29033,"count":0}],"isBlockCoverage":false},{"functionName":"readlinkSync","ranges":[{"startOffset":29035,"endOffset":29366,"count":0}],"isBlockCoverage":false},{"functionName":"symlink","ranges":[{"startOffset":29368,"endOffset":30978,"count":0}],"isBlockCoverage":false},{"functionName":"symlinkSync","ranges":[{"startOffset":30980,"endOffset":31637,"count":0}],"isBlockCoverage":false},{"functionName":"link","ranges":[{"startOffset":31639,"endOffset":32040,"count":0}],"isBlockCoverage":false},{"functionName":"linkSync","ranges":[{"startOffset":32042,"endOffset":32491,"count":0}],"isBlockCoverage":false},{"functionName":"unlink","ranges":[{"startOffset":32493,"endOffset":32720,"count":0}],"isBlockCoverage":false},{"functionName":"unlinkSync","ranges":[{"startOffset":32722,"endOffset":32908,"count":0}],"isBlockCoverage":false},{"functionName":"fchmod","ranges":[{"startOffset":32910,"endOffset":33152,"count":0}],"isBlockCoverage":false},{"functionName":"fchmodSync","ranges":[{"startOffset":33154,"endOffset":33348,"count":0}],"isBlockCoverage":false},{"functionName":"lchmod","ranges":[{"startOffset":33350,"endOffset":33787,"count":0}],"isBlockCoverage":false},{"functionName":"lchmodSync","ranges":[{"startOffset":33789,"endOffset":34111,"count":0}],"isBlockCoverage":false},{"functionName":"chmod","ranges":[{"startOffset":34114,"endOffset":34390,"count":0}],"isBlockCoverage":false},{"functionName":"chmodSync","ranges":[{"startOffset":34392,"endOffset":34627,"count":0}],"isBlockCoverage":false},{"functionName":"lchown","ranges":[{"startOffset":34629,"endOffset":34970,"count":0}],"isBlockCoverage":false},{"functionName":"lchownSync","ranges":[{"startOffset":34972,"endOffset":35272,"count":0}],"isBlockCoverage":false},{"functionName":"fchown","ranges":[{"startOffset":35274,"endOffset":35580,"count":0}],"isBlockCoverage":false},{"functionName":"fchownSync","ranges":[{"startOffset":35582,"endOffset":35841,"count":0}],"isBlockCoverage":false},{"functionName":"chown","ranges":[{"startOffset":35843,"endOffset":36183,"count":0}],"isBlockCoverage":false},{"functionName":"chownSync","ranges":[{"startOffset":36185,"endOffset":36483,"count":0}],"isBlockCoverage":false},{"functionName":"utimes","ranges":[{"startOffset":36485,"endOffset":36826,"count":0}],"isBlockCoverage":false},{"functionName":"utimesSync","ranges":[{"startOffset":36828,"endOffset":37110,"count":0}],"isBlockCoverage":false},{"functionName":"futimes","ranges":[{"startOffset":37112,"endOffset":37420,"count":0}],"isBlockCoverage":false},{"functionName":"futimesSync","ranges":[{"startOffset":37422,"endOffset":37682,"count":0}],"isBlockCoverage":false},{"functionName":"lutimes","ranges":[{"startOffset":37684,"endOffset":38030,"count":0}],"isBlockCoverage":false},{"functionName":"lutimesSync","ranges":[{"startOffset":38032,"endOffset":38336,"count":0}],"isBlockCoverage":false},{"functionName":"writeAll","ranges":[{"startOffset":38338,"endOffset":38988,"count":0}],"isBlockCoverage":false},{"functionName":"writeFile","ranges":[{"startOffset":38990,"endOffset":39717,"count":0}],"isBlockCoverage":false},{"functionName":"writeFileSync","ranges":[{"startOffset":39719,"endOffset":40429,"count":0}],"isBlockCoverage":false},{"functionName":"appendFile","ranges":[{"startOffset":40431,"endOffset":40871,"count":0}],"isBlockCoverage":false},{"functionName":"appendFileSync","ranges":[{"startOffset":40873,"endOffset":41252,"count":0}],"isBlockCoverage":false},{"functionName":"watch","ranges":[{"startOffset":41254,"endOffset":42146,"count":0}],"isBlockCoverage":false},{"functionName":"watchFile","ranges":[{"startOffset":42182,"endOffset":43295,"count":0}],"isBlockCoverage":false},{"functionName":"unwatchFile","ranges":[{"startOffset":43297,"endOffset":43985,"count":0}],"isBlockCoverage":false},{"functionName":"splitRoot","ranges":[{"startOffset":44201,"endOffset":44267,"count":0}],"isBlockCoverage":false},{"functionName":"splitRoot","ranges":[{"startOffset":44292,"endOffset":44470,"count":1},{"startOffset":44359,"endOffset":44450,"count":2},{"startOffset":44421,"endOffset":44444,"count":1},{"startOffset":44450,"endOffset":44469,"count":0}],"isBlockCoverage":true},{"functionName":"encodeRealpathResult","ranges":[{"startOffset":44475,"endOffset":44762,"count":1},{"startOffset":44560,"endOffset":44590,"count":0},{"startOffset":44610,"endOffset":44761,"count":0}],"isBlockCoverage":true},{"functionName":"nextPart","ranges":[{"startOffset":44885,"endOffset":45128,"count":0}],"isBlockCoverage":false},{"functionName":"nextPart","ranges":[{"startOffset":45152,"endOffset":45205,"count":6}],"isBlockCoverage":true},{"functionName":"realpathSync","ranges":[{"startOffset":45247,"endOffset":49113,"count":1},{"startOffset":45381,"endOffset":45399,"count":0},{"startOffset":45568,"endOffset":45603,"count":0},{"startOffset":46175,"endOffset":46350,"count":0},{"startOffset":46487,"endOffset":49030,"count":6},{"startOffset":46599,"endOffset":46714,"count":1},{"startOffset":46714,"endOffset":46840,"count":5},{"startOffset":46963,"endOffset":47100,"count":0},{"startOffset":47211,"endOffset":47260,"count":0},{"startOffset":47713,"endOffset":48538,"count":0},{"startOffset":48544,"endOffset":48839,"count":0},{"startOffset":48841,"endOffset":49026,"count":0}],"isBlockCoverage":true},{"functionName":"realpathSync.native","ranges":[{"startOffset":49138,"endOffset":49377,"count":0}],"isBlockCoverage":false},{"functionName":"realpath","ranges":[{"startOffset":49381,"endOffset":52816,"count":0}],"isBlockCoverage":false},{"functionName":"realpath.native","ranges":[{"startOffset":52837,"endOffset":53107,"count":0}],"isBlockCoverage":false},{"functionName":"mkdtemp","ranges":[{"startOffset":53110,"endOffset":53581,"count":0}],"isBlockCoverage":false},{"functionName":"mkdtempSync","ranges":[{"startOffset":53584,"endOffset":54058,"count":0}],"isBlockCoverage":false},{"functionName":"copyFile","ranges":[{"startOffset":54061,"endOffset":54615,"count":0}],"isBlockCoverage":false},{"functionName":"copyFileSync","ranges":[{"startOffset":54618,"endOffset":54990,"count":0}],"isBlockCoverage":false},{"functionName":"lazyLoadStreams","ranges":[{"startOffset":54992,"endOffset":55186,"count":8},{"startOffset":55040,"endOffset":55184,"count":1}],"isBlockCoverage":true},{"functionName":"createReadStream","ranges":[{"startOffset":55188,"endOffset":55293,"count":0}],"isBlockCoverage":false},{"functionName":"createWriteStream","ranges":[{"startOffset":55295,"endOffset":55402,"count":0}],"isBlockCoverage":false},{"functionName":"get ReadStream","ranges":[{"startOffset":56625,"endOffset":56693,"count":2}],"isBlockCoverage":true},{"functionName":"set ReadStream","ranges":[{"startOffset":56698,"endOffset":56745,"count":0}],"isBlockCoverage":false},{"functionName":"get WriteStream","ranges":[{"startOffset":56750,"endOffset":56820,"count":2}],"isBlockCoverage":true},{"functionName":"set WriteStream","ranges":[{"startOffset":56825,"endOffset":56874,"count":0}],"isBlockCoverage":false},{"functionName":"get FileReadStream","ranges":[{"startOffset":57012,"endOffset":57088,"count":2}],"isBlockCoverage":true},{"functionName":"set FileReadStream","ranges":[{"startOffset":57093,"endOffset":57148,"count":0}],"isBlockCoverage":false},{"functionName":"get FileWriteStream","ranges":[{"startOffset":57153,"endOffset":57231,"count":2}],"isBlockCoverage":true},{"functionName":"set FileWriteStream","ranges":[{"startOffset":57236,"endOffset":57293,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":57724,"endOffset":57849,"count":2},{"startOffset":57769,"endOffset":57820,"count":1}],"isBlockCoverage":true}]},{"scriptId":"53","url":"internal/fs/utils.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":21593,"count":1}],"isBlockCoverage":false},{"functionName":"lazyLoadFs","ranges":[{"startOffset":2473,"endOffset":2552,"count":0}],"isBlockCoverage":false},{"functionName":"assertEncoding","ranges":[{"startOffset":2554,"endOffset":2701,"count":2},{"startOffset":2605,"endOffset":2636,"count":1},{"startOffset":2638,"endOffset":2699,"count":0}],"isBlockCoverage":true},{"functionName":"Dirent","ranges":[{"startOffset":2720,"endOffset":2795,"count":0}],"isBlockCoverage":false},{"functionName":"isDirectory","ranges":[{"startOffset":2799,"endOffset":2860,"count":0}],"isBlockCoverage":false},{"functionName":"isFile","ranges":[{"startOffset":2864,"endOffset":2921,"count":0}],"isBlockCoverage":false},{"functionName":"isBlockDevice","ranges":[{"startOffset":2925,"endOffset":2990,"count":0}],"isBlockCoverage":false},{"functionName":"isCharacterDevice","ranges":[{"startOffset":2994,"endOffset":3062,"count":0}],"isBlockCoverage":false},{"functionName":"isSymbolicLink","ranges":[{"startOffset":3066,"endOffset":3131,"count":0}],"isBlockCoverage":false},{"functionName":"isFIFO","ranges":[{"startOffset":3135,"endOffset":3192,"count":0}],"isBlockCoverage":false},{"functionName":"isSocket","ranges":[{"startOffset":3196,"endOffset":3257,"count":0}],"isBlockCoverage":false},{"functionName":"DirentFromStats","ranges":[{"startOffset":3302,"endOffset":3381,"count":0}],"isBlockCoverage":false},{"functionName":"DirentFromStats.","ranges":[{"startOffset":3526,"endOffset":3575,"count":0}],"isBlockCoverage":false},{"functionName":"copyObject","ranges":[{"startOffset":3580,"endOffset":3708,"count":0}],"isBlockCoverage":false},{"functionName":"join","ranges":[{"startOffset":3758,"endOffset":4365,"count":0}],"isBlockCoverage":false},{"functionName":"getDirents","ranges":[{"startOffset":4367,"endOffset":5462,"count":0}],"isBlockCoverage":false},{"functionName":"getDirent","ranges":[{"startOffset":5464,"endOffset":6186,"count":0}],"isBlockCoverage":false},{"functionName":"getOptions","ranges":[{"startOffset":6188,"endOffset":6728,"count":3},{"startOffset":6283,"endOffset":6321,"count":2},{"startOffset":6323,"endOffset":6355,"count":1},{"startOffset":6355,"endOffset":6392,"count":2},{"startOffset":6392,"endOffset":6632,"count":1},{"startOffset":6549,"endOffset":6632,"count":0},{"startOffset":6632,"endOffset":6727,"count":2}],"isBlockCoverage":true},{"functionName":"handleErrorFromBinding","ranges":[{"startOffset":6730,"endOffset":7259,"count":10},{"startOffset":6800,"endOffset":6935,"count":0},{"startOffset":6967,"endOffset":7257,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":7400,"endOffset":7953,"count":4},{"startOffset":7630,"endOffset":7650,"count":0},{"startOffset":7704,"endOffset":7752,"count":0},{"startOffset":7771,"endOffset":7952,"count":0}],"isBlockCoverage":true},{"functionName":"preprocessSymlinkDestination","ranges":[{"startOffset":7957,"endOffset":8628,"count":0}],"isBlockCoverage":false},{"functionName":"StatsBase","ranges":[{"startOffset":8661,"endOffset":8968,"count":1}],"isBlockCoverage":true},{"functionName":"StatsBase.isDirectory","ranges":[{"startOffset":9004,"endOffset":9061,"count":1}],"isBlockCoverage":true},{"functionName":"StatsBase.isFile","ranges":[{"startOffset":9093,"endOffset":9150,"count":1}],"isBlockCoverage":true},{"functionName":"StatsBase.isBlockDevice","ranges":[{"startOffset":9189,"endOffset":9246,"count":0}],"isBlockCoverage":false},{"functionName":"StatsBase.isCharacterDevice","ranges":[{"startOffset":9289,"endOffset":9346,"count":0}],"isBlockCoverage":false},{"functionName":"StatsBase.isSymbolicLink","ranges":[{"startOffset":9386,"endOffset":9443,"count":0}],"isBlockCoverage":false},{"functionName":"StatsBase.isFIFO","ranges":[{"startOffset":9475,"endOffset":9532,"count":0}],"isBlockCoverage":false},{"functionName":"StatsBase.isSocket","ranges":[{"startOffset":9566,"endOffset":9624,"count":0}],"isBlockCoverage":false},{"functionName":"msFromTimeSpec","ranges":[{"startOffset":9749,"endOffset":9831,"count":4}],"isBlockCoverage":true},{"functionName":"nsFromTimeSpecBigInt","ranges":[{"startOffset":9833,"endOffset":9916,"count":0}],"isBlockCoverage":false},{"functionName":"dateFromMs","ranges":[{"startOffset":10265,"endOffset":10329,"count":4}],"isBlockCoverage":true},{"functionName":"BigIntStats","ranges":[{"startOffset":10331,"endOffset":11067,"count":0}],"isBlockCoverage":false},{"functionName":"BigIntStats._checkModeProperty","ranges":[{"startOffset":11225,"endOffset":11471,"count":0}],"isBlockCoverage":false},{"functionName":"Stats","ranges":[{"startOffset":11474,"endOffset":11991,"count":1}],"isBlockCoverage":true},{"functionName":"Stats._checkModeProperty","ranges":[{"startOffset":12332,"endOffset":12562,"count":2},{"startOffset":12369,"endOffset":12447,"count":0},{"startOffset":12449,"endOffset":12516,"count":0}],"isBlockCoverage":true},{"functionName":"getStatsFromBinding","ranges":[{"startOffset":12565,"endOffset":13643,"count":1},{"startOffset":12646,"endOffset":13171,"count":0}],"isBlockCoverage":true},{"functionName":"stringToFlags","ranges":[{"startOffset":13645,"endOffset":14853,"count":2},{"startOffset":13710,"endOffset":13733,"count":0},{"startOffset":13756,"endOffset":13782,"count":0},{"startOffset":13839,"endOffset":13850,"count":0},{"startOffset":13872,"endOffset":13909,"count":0},{"startOffset":13914,"endOffset":13940,"count":0},{"startOffset":13945,"endOffset":13957,"count":0},{"startOffset":13979,"endOffset":14015,"count":0},{"startOffset":14021,"endOffset":14068,"count":0},{"startOffset":14073,"endOffset":14084,"count":0},{"startOffset":14106,"endOffset":14163,"count":0},{"startOffset":14169,"endOffset":14215,"count":0},{"startOffset":14220,"endOffset":14231,"count":0},{"startOffset":14253,"endOffset":14308,"count":0},{"startOffset":14314,"endOffset":14362,"count":0},{"startOffset":14367,"endOffset":14378,"count":0},{"startOffset":14400,"endOffset":14458,"count":0},{"startOffset":14463,"endOffset":14474,"count":0},{"startOffset":14496,"endOffset":14554,"count":0},{"startOffset":14560,"endOffset":14607,"count":0},{"startOffset":14612,"endOffset":14623,"count":0},{"startOffset":14645,"endOffset":14701,"count":0},{"startOffset":14706,"endOffset":14717,"count":0},{"startOffset":14739,"endOffset":14795,"count":0},{"startOffset":14799,"endOffset":14852,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":14899,"endOffset":15259,"count":0}],"isBlockCoverage":false},{"functionName":"toUnixTimestamp","ranges":[{"startOffset":15321,"endOffset":15764,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":15818,"endOffset":16197,"count":12},{"startOffset":15874,"endOffset":15941,"count":0},{"startOffset":15962,"endOffset":16029,"count":0},{"startOffset":16070,"endOffset":16193,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":16255,"endOffset":16529,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":16571,"endOffset":16841,"count":4},{"startOffset":16633,"endOffset":16655,"count":0},{"startOffset":16657,"endOffset":16743,"count":0},{"startOffset":16819,"endOffset":16839,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":16886,"endOffset":17021,"count":3}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":17069,"endOffset":17400,"count":0}],"isBlockCoverage":false},{"functionName":"warnOnNonPortableTemplate","ranges":[{"startOffset":17441,"endOffset":17899,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":18135,"endOffset":18755,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":18805,"endOffset":19387,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":19439,"endOffset":19921,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":19962,"endOffset":20540,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":20603,"endOffset":20975,"count":0}],"isBlockCoverage":false}]},{"scriptId":"54","url":"internal/fs/dir.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":6531,"count":1}],"isBlockCoverage":false},{"functionName":"Dir","ranges":[{"startOffset":1092,"endOffset":1864,"count":0}],"isBlockCoverage":false},{"functionName":"get path","ranges":[{"startOffset":1868,"endOffset":1911,"count":0}],"isBlockCoverage":false},{"functionName":"read","ranges":[{"startOffset":1915,"endOffset":1982,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":1986,"endOffset":3358,"count":0}],"isBlockCoverage":false},{"functionName":"readSync","ranges":[{"startOffset":3362,"endOffset":4118,"count":0}],"isBlockCoverage":false},{"functionName":"close","ranges":[{"startOffset":4122,"endOffset":4682,"count":0}],"isBlockCoverage":false},{"functionName":"closeSync","ranges":[{"startOffset":4686,"endOffset":5063,"count":0}],"isBlockCoverage":false},{"functionName":"entries","ranges":[{"startOffset":5067,"endOffset":5330,"count":0}],"isBlockCoverage":false},{"functionName":"opendir","ranges":[{"startOffset":5491,"endOffset":6116,"count":0}],"isBlockCoverage":false},{"functionName":"opendirSync","ranges":[{"startOffset":6118,"endOffset":6475,"count":0}],"isBlockCoverage":false}]},{"scriptId":"55","url":"internal/process/report.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2945,"count":1}],"isBlockCoverage":false},{"functionName":"writeReport","ranges":[{"startOffset":298,"endOffset":805,"count":0}],"isBlockCoverage":false},{"functionName":"getReport","ranges":[{"startOffset":809,"endOffset":1045,"count":0}],"isBlockCoverage":false},{"functionName":"get directory","ranges":[{"startOffset":1049,"endOffset":1100,"count":0}],"isBlockCoverage":false},{"functionName":"set directory","ranges":[{"startOffset":1104,"endOffset":1192,"count":0}],"isBlockCoverage":false},{"functionName":"get filename","ranges":[{"startOffset":1196,"endOffset":1245,"count":0}],"isBlockCoverage":false},{"functionName":"set filename","ranges":[{"startOffset":1249,"endOffset":1337,"count":0}],"isBlockCoverage":false},{"functionName":"get compact","ranges":[{"startOffset":1341,"endOffset":1388,"count":0}],"isBlockCoverage":false},{"functionName":"set compact","ranges":[{"startOffset":1392,"endOffset":1469,"count":0}],"isBlockCoverage":false},{"functionName":"get signal","ranges":[{"startOffset":1473,"endOffset":1518,"count":0}],"isBlockCoverage":false},{"functionName":"set signal","ranges":[{"startOffset":1522,"endOffset":1659,"count":0}],"isBlockCoverage":false},{"functionName":"get reportOnFatalError","ranges":[{"startOffset":1663,"endOffset":1735,"count":0}],"isBlockCoverage":false},{"functionName":"set reportOnFatalError","ranges":[{"startOffset":1739,"endOffset":1923,"count":0}],"isBlockCoverage":false},{"functionName":"get reportOnSignal","ranges":[{"startOffset":1927,"endOffset":1991,"count":0}],"isBlockCoverage":false},{"functionName":"set reportOnSignal","ranges":[{"startOffset":1995,"endOffset":2222,"count":0}],"isBlockCoverage":false},{"functionName":"get reportOnUncaughtException","ranges":[{"startOffset":2226,"endOffset":2312,"count":0}],"isBlockCoverage":false},{"functionName":"set reportOnUncaughtException","ranges":[{"startOffset":2316,"endOffset":2514,"count":0}],"isBlockCoverage":false},{"functionName":"addSignalHandler","ranges":[{"startOffset":2519,"endOffset":2690,"count":1},{"startOffset":2585,"endOffset":2688,"count":0}],"isBlockCoverage":true},{"functionName":"removeSignalHandler","ranges":[{"startOffset":2692,"endOffset":2816,"count":0}],"isBlockCoverage":false},{"functionName":"signalHandler","ranges":[{"startOffset":2818,"endOffset":2892,"count":0}],"isBlockCoverage":false}]},{"scriptId":"56","url":"internal/modules/cjs/loader.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":37972,"count":1}],"isBlockCoverage":false},{"functionName":"get hasLoadedAnyUserCJSModule","ranges":[{"startOffset":1880,"endOffset":1949,"count":1}],"isBlockCoverage":true},{"functionName":"stat","ranges":[{"startOffset":4272,"endOffset":4589,"count":0}],"isBlockCoverage":false},{"functionName":"updateChildren","ranges":[{"startOffset":4591,"endOffset":4766,"count":0}],"isBlockCoverage":false},{"functionName":"Module","ranges":[{"startOffset":4768,"endOffset":5005,"count":0}],"isBlockCoverage":false},{"functionName":"wrap","ranges":[{"startOffset":5456,"endOffset":5533,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":5678,"endOffset":5800,"count":0}],"isBlockCoverage":false},{"functionName":"defineProperty","ranges":[{"startOffset":5805,"endOffset":5938,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":5985,"endOffset":6013,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":6018,"endOffset":6072,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":6122,"endOffset":6158,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":6163,"endOffset":6225,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":6296,"endOffset":6321,"count":0}],"isBlockCoverage":false},{"functionName":"readPackage","ranges":[{"startOffset":6669,"endOffset":7479,"count":0}],"isBlockCoverage":false},{"functionName":"readPackageScope","ranges":[{"startOffset":7481,"endOffset":8024,"count":0}],"isBlockCoverage":false},{"functionName":"tryPackage","ranges":[{"startOffset":8026,"endOffset":9327,"count":0}],"isBlockCoverage":false},{"functionName":"tryFile","ranges":[{"startOffset":9668,"endOffset":9878,"count":0}],"isBlockCoverage":false},{"functionName":"toRealPath","ranges":[{"startOffset":9880,"endOffset":10011,"count":0}],"isBlockCoverage":false},{"functionName":"tryExtensions","ranges":[{"startOffset":10086,"endOffset":10292,"count":0}],"isBlockCoverage":false},{"functionName":"findLongestRegisteredExtension","ranges":[{"startOffset":10381,"endOffset":10817,"count":0}],"isBlockCoverage":false},{"functionName":"trySelfParentPath","ranges":[{"startOffset":10819,"endOffset":11108,"count":0}],"isBlockCoverage":false},{"functionName":"trySelf","ranges":[{"startOffset":11110,"endOffset":11959,"count":0}],"isBlockCoverage":false},{"functionName":"resolveExports","ranges":[{"startOffset":12120,"endOffset":12858,"count":0}],"isBlockCoverage":false},{"functionName":"Module._findPath","ranges":[{"startOffset":12924,"endOffset":15551,"count":0}],"isBlockCoverage":false},{"functionName":"Module._nodeModulePaths","ranges":[{"startOffset":15795,"endOffset":17186,"count":0}],"isBlockCoverage":false},{"functionName":"Module._nodeModulePaths","ranges":[{"startOffset":17278,"endOffset":18319,"count":0}],"isBlockCoverage":false},{"functionName":"Module._resolveLookupPaths","ranges":[{"startOffset":18353,"endOffset":19491,"count":0}],"isBlockCoverage":false},{"functionName":"emitCircularRequireWarning","ranges":[{"startOffset":19494,"endOffset":19677,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":19870,"endOffset":20242,"count":0}],"isBlockCoverage":false},{"functionName":"getOwnPropertyDescriptor","ranges":[{"startOffset":20247,"endOffset":20488,"count":0}],"isBlockCoverage":false},{"functionName":"getExportsForCircularRequire","ranges":[{"startOffset":20689,"endOffset":21339,"count":0}],"isBlockCoverage":false},{"functionName":"Module._load","ranges":[{"startOffset":21751,"endOffset":24767,"count":0}],"isBlockCoverage":false},{"functionName":"Module._resolveFilename","ranges":[{"startOffset":24796,"endOffset":27659,"count":0}],"isBlockCoverage":false},{"functionName":"finalizeEsmResolution","ranges":[{"startOffset":27662,"endOffset":28376,"count":0}],"isBlockCoverage":false},{"functionName":"createEsmNotFoundErr","ranges":[{"startOffset":28378,"endOffset":28668,"count":0}],"isBlockCoverage":false},{"functionName":"Module.load","ranges":[{"startOffset":28757,"endOffset":29561,"count":0}],"isBlockCoverage":false},{"functionName":"Module.require","ranges":[{"startOffset":29679,"endOffset":29978,"count":0}],"isBlockCoverage":false},{"functionName":"wrapSafe","ranges":[{"startOffset":30158,"endOffset":31274,"count":0}],"isBlockCoverage":false},{"functionName":"Module._compile","ranges":[{"startOffset":31474,"endOffset":33316,"count":0}],"isBlockCoverage":false},{"functionName":"Module._extensions..js","ranges":[{"startOffset":33375,"endOffset":34149,"count":0}],"isBlockCoverage":false},{"functionName":"Module._extensions..json","ranges":[{"startOffset":34213,"endOffset":34577,"count":0}],"isBlockCoverage":false},{"functionName":"Module._extensions..node","ranges":[{"startOffset":34641,"endOffset":34959,"count":0}],"isBlockCoverage":false},{"functionName":"createRequireFromPath","ranges":[{"startOffset":34962,"endOffset":35387,"count":0}],"isBlockCoverage":false},{"functionName":"createRequire","ranges":[{"startOffset":35672,"endOffset":36225,"count":0}],"isBlockCoverage":false},{"functionName":"Module._initPaths","ranges":[{"startOffset":36286,"endOffset":37194,"count":1},{"startOffset":36327,"endOffset":36352,"count":0},{"startOffset":36404,"endOffset":36427,"count":0},{"startOffset":36635,"endOffset":36677,"count":0},{"startOffset":36944,"endOffset":37073,"count":0}],"isBlockCoverage":true},{"functionName":"pathsFilterCB","ranges":[{"startOffset":36996,"endOffset":37053,"count":0}],"isBlockCoverage":false},{"functionName":"Module._preloadModules","ranges":[{"startOffset":37222,"endOffset":37728,"count":0}],"isBlockCoverage":false},{"functionName":"syncBuiltinESMExports","ranges":[{"startOffset":37762,"endOffset":37918,"count":0}],"isBlockCoverage":false}]},{"scriptId":"57","url":"vm.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":12941,"count":1}],"isBlockCoverage":false},{"functionName":"Script","ranges":[{"startOffset":1992,"endOffset":4152,"count":0}],"isBlockCoverage":false},{"functionName":"runInThisContext","ranges":[{"startOffset":4156,"endOffset":4436,"count":0}],"isBlockCoverage":false},{"functionName":"runInContext","ranges":[{"startOffset":4440,"endOffset":4846,"count":0}],"isBlockCoverage":false},{"functionName":"runInNewContext","ranges":[{"startOffset":4850,"endOffset":5021,"count":0}],"isBlockCoverage":false},{"functionName":"validateContext","ranges":[{"startOffset":5025,"endOffset":5244,"count":0}],"isBlockCoverage":false},{"functionName":"getRunInContextArgs","ranges":[{"startOffset":5246,"endOffset":5837,"count":0}],"isBlockCoverage":false},{"functionName":"getContextOptions","ranges":[{"startOffset":5839,"endOffset":6907,"count":0}],"isBlockCoverage":false},{"functionName":"isContext","ranges":[{"startOffset":6909,"endOffset":7091,"count":0}],"isBlockCoverage":false},{"functionName":"createContext","ranges":[{"startOffset":7126,"endOffset":8261,"count":0}],"isBlockCoverage":false},{"functionName":"createScript","ranges":[{"startOffset":8263,"endOffset":8339,"count":0}],"isBlockCoverage":false},{"functionName":"sigintHandlersWrap","ranges":[{"startOffset":8493,"endOffset":8939,"count":0}],"isBlockCoverage":false},{"functionName":"runInContext","ranges":[{"startOffset":8941,"endOffset":9338,"count":0}],"isBlockCoverage":false},{"functionName":"runInNewContext","ranges":[{"startOffset":9340,"endOffset":9692,"count":0}],"isBlockCoverage":false},{"functionName":"runInThisContext","ranges":[{"startOffset":9694,"endOffset":9880,"count":0}],"isBlockCoverage":false},{"functionName":"compileFunction","ranges":[{"startOffset":9882,"endOffset":11615,"count":0}],"isBlockCoverage":false},{"functionName":"measureMemory","ranges":[{"startOffset":11892,"endOffset":12454,"count":0}],"isBlockCoverage":false}]},{"scriptId":"58","url":"internal/modules/package_json_reader.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":975,"count":1}],"isBlockCoverage":false},{"functionName":"read","ranges":[{"startOffset":279,"endOffset":946,"count":1},{"startOffset":332,"endOffset":369,"count":0},{"startOffset":686,"endOffset":739,"count":0},{"startOffset":789,"endOffset":892,"count":0}],"isBlockCoverage":true}]},{"scriptId":"59","url":"internal/process/esm_loader.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2326,"count":1}],"isBlockCoverage":false},{"functionName":"exports.initializeImportMetaObject","ranges":[{"startOffset":405,"endOffset":701,"count":0}],"isBlockCoverage":false},{"functionName":"exports.importModuleDynamicallyCallback","ranges":[{"startOffset":746,"endOffset":1137,"count":0}],"isBlockCoverage":false},{"functionName":"initializeLoader","ranges":[{"startOffset":1202,"endOffset":1969,"count":1},{"startOffset":1388,"endOffset":1968,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":1722,"endOffset":1963,"count":0}],"isBlockCoverage":true},{"functionName":"loadESM","ranges":[{"startOffset":1989,"endOffset":2324,"count":1},{"startOffset":2092,"endOffset":2322,"count":0}],"isBlockCoverage":true}]},{"scriptId":"60","url":"internal/modules/esm/loader.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":8287,"count":1}],"isBlockCoverage":false},{"functionName":"Loader","ranges":[{"startOffset":1361,"endOffset":3134,"count":1}],"isBlockCoverage":true},{"functionName":"resolve","ranges":[{"startOffset":3138,"endOffset":3761,"count":2},{"startOffset":3459,"endOffset":3562,"count":0},{"startOffset":3634,"endOffset":3741,"count":0}],"isBlockCoverage":true},{"functionName":"getFormat","ranges":[{"startOffset":3765,"endOffset":4790,"count":2},{"startOffset":3922,"endOffset":4029,"count":0},{"startOffset":4109,"endOffset":4224,"count":0},{"startOffset":4256,"endOffset":4328,"count":1},{"startOffset":4328,"endOffset":4497,"count":0},{"startOffset":4497,"endOffset":4589,"count":1},{"startOffset":4590,"endOffset":4639,"count":0},{"startOffset":4646,"endOffset":4766,"count":0},{"startOffset":4766,"endOffset":4789,"count":1}],"isBlockCoverage":true},{"functionName":"eval","ranges":[{"startOffset":4794,"endOffset":5469,"count":1},{"startOffset":5405,"endOffset":5468,"count":0}],"isBlockCoverage":true},{"functionName":"evalInstance","ranges":[{"startOffset":4924,"endOffset":5260,"count":1}],"isBlockCoverage":true},{"functionName":"importModuleDynamically","ranges":[{"startOffset":5141,"endOffset":5222,"count":0}],"isBlockCoverage":false},{"functionName":"import","ranges":[{"startOffset":5473,"endOffset":5644,"count":0}],"isBlockCoverage":false},{"functionName":"hook","ranges":[{"startOffset":5648,"endOffset":6609,"count":0}],"isBlockCoverage":false},{"functionName":"runGlobalPreloadCode","ranges":[{"startOffset":6613,"endOffset":7417,"count":0}],"isBlockCoverage":false},{"functionName":"getModuleJob","ranges":[{"startOffset":7421,"endOffset":8211,"count":2},{"startOffset":7708,"endOffset":7745,"count":0},{"startOffset":7779,"endOffset":7790,"count":0},{"startOffset":7832,"endOffset":7876,"count":0},{"startOffset":7978,"endOffset":8008,"count":0},{"startOffset":8009,"endOffset":8043,"count":0}],"isBlockCoverage":true}]},{"scriptId":"61","url":"internal/modules/esm/module_map.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":878,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":175,"endOffset":200,"count":1}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":419,"endOffset":492,"count":2}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":495,"endOffset":771,"count":3},{"startOffset":585,"endOffset":621,"count":0},{"startOffset":623,"endOffset":693,"count":0}],"isBlockCoverage":true},{"functionName":"has","ranges":[{"startOffset":774,"endOffset":847,"count":0}],"isBlockCoverage":false}]},{"scriptId":"62","url":"internal/modules/esm/module_job.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":5778,"count":1}],"isBlockCoverage":false},{"functionName":"ModuleJob","ranges":[{"startOffset":832,"endOffset":2478,"count":3}],"isBlockCoverage":true},{"functionName":"link","ranges":[{"startOffset":1301,"endOffset":2105,"count":3}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":1757,"endOffset":1984,"count":2}],"isBlockCoverage":true},{"functionName":"instantiate","ranges":[{"startOffset":2482,"endOffset":2627,"count":1}],"isBlockCoverage":true},{"functionName":"_instantiate","ranges":[{"startOffset":2631,"endOffset":5498,"count":1},{"startOffset":3105,"endOffset":3282,"count":0},{"startOffset":3339,"endOffset":5251,"count":0},{"startOffset":5298,"endOffset":5494,"count":3}],"isBlockCoverage":true},{"functionName":"addJobsToDependencyGraph","ranges":[{"startOffset":2730,"endOffset":3004,"count":3},{"startOffset":2791,"endOffset":2816,"count":0}],"isBlockCoverage":true},{"functionName":"run","ranges":[{"startOffset":5502,"endOffset":5698,"count":1},{"startOffset":5658,"endOffset":5697,"count":0}],"isBlockCoverage":true}]},{"scriptId":"63","url":"internal/modules/esm/resolve.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":28247,"count":1}],"isBlockCoverage":false},{"functionName":"getConditionsSet","ranges":[{"startOffset":1873,"endOffset":2225,"count":1},{"startOffset":1982,"endOffset":2190,"count":0}],"isBlockCoverage":true},{"functionName":"tryStatSync","ranges":[{"startOffset":2336,"endOffset":2441,"count":1},{"startOffset":2404,"endOffset":2439,"count":0}],"isBlockCoverage":true},{"functionName":"getPackageConfig","ranges":[{"startOffset":2443,"endOffset":3781,"count":1},{"startOffset":2571,"endOffset":2597,"count":0},{"startOffset":2680,"endOffset":2955,"count":0},{"startOffset":3025,"endOffset":3204,"count":0},{"startOffset":3326,"endOffset":3345,"count":0},{"startOffset":3450,"endOffset":3467,"count":0},{"startOffset":3545,"endOffset":3567,"count":0},{"startOffset":3569,"endOffset":3583,"count":0}],"isBlockCoverage":true},{"functionName":"getPackageScopeConfig","ranges":[{"startOffset":3783,"endOffset":4883,"count":1},{"startOffset":4041,"endOffset":4047,"count":0},{"startOffset":4227,"endOffset":4550,"count":0},{"startOffset":4554,"endOffset":4882,"count":0}],"isBlockCoverage":true},{"functionName":"fileExists","ranges":[{"startOffset":5139,"endOffset":5218,"count":0}],"isBlockCoverage":false},{"functionName":"legacyMainResolve","ranges":[{"startOffset":5220,"endOffset":6891,"count":0}],"isBlockCoverage":false},{"functionName":"resolveExtensionsWithTryExactName","ranges":[{"startOffset":6893,"endOffset":7024,"count":0}],"isBlockCoverage":false},{"functionName":"resolveExtensions","ranges":[{"startOffset":7080,"endOffset":7337,"count":0}],"isBlockCoverage":false},{"functionName":"resolveIndex","ranges":[{"startOffset":7339,"endOffset":7426,"count":0}],"isBlockCoverage":false},{"functionName":"finalizeResolution","ranges":[{"startOffset":7464,"endOffset":8671,"count":1},{"startOffset":7577,"endOffset":7720,"count":0},{"startOffset":7834,"endOffset":8243,"count":0},{"startOffset":8308,"endOffset":8344,"count":0},{"startOffset":8381,"endOffset":8508,"count":0},{"startOffset":8535,"endOffset":8649,"count":0}],"isBlockCoverage":true},{"functionName":"throwImportNotDefined","ranges":[{"startOffset":8673,"endOffset":8888,"count":0}],"isBlockCoverage":false},{"functionName":"throwExportsNotFound","ranges":[{"startOffset":8890,"endOffset":9089,"count":0}],"isBlockCoverage":false},{"functionName":"throwInvalidSubpath","ranges":[{"startOffset":9091,"endOffset":9441,"count":0}],"isBlockCoverage":false},{"functionName":"throwInvalidPackageTarget","ranges":[{"startOffset":9443,"endOffset":9825,"count":0}],"isBlockCoverage":false},{"functionName":"resolvePackageTargetString","ranges":[{"startOffset":9926,"endOffset":11589,"count":0}],"isBlockCoverage":false},{"functionName":"isArrayIndex","ranges":[{"startOffset":11644,"endOffset":11784,"count":0}],"isBlockCoverage":false},{"functionName":"resolvePackageTarget","ranges":[{"startOffset":11786,"endOffset":13918,"count":0}],"isBlockCoverage":false},{"functionName":"isConditionalExportsMainSugar","ranges":[{"startOffset":13920,"endOffset":14855,"count":0}],"isBlockCoverage":false},{"functionName":"packageExportsResolve","ranges":[{"startOffset":15040,"endOffset":16923,"count":0}],"isBlockCoverage":false},{"functionName":"packageImportsResolve","ranges":[{"startOffset":16925,"endOffset":18921,"count":0}],"isBlockCoverage":false},{"functionName":"getPackageType","ranges":[{"startOffset":18923,"endOffset":19036,"count":1}],"isBlockCoverage":true},{"functionName":"packageResolve","ranges":[{"startOffset":19149,"endOffset":21981,"count":0}],"isBlockCoverage":false},{"functionName":"isBareSpecifier","ranges":[{"startOffset":21983,"endOffset":22093,"count":0}],"isBlockCoverage":false},{"functionName":"isRelativeSpecifier","ranges":[{"startOffset":22095,"endOffset":22366,"count":1},{"startOffset":22235,"endOffset":22344,"count":0},{"startOffset":22348,"endOffset":22365,"count":0}],"isBlockCoverage":true},{"functionName":"shouldBeTreatedAsRelativeOrAbsolutePath","ranges":[{"startOffset":22368,"endOffset":22551,"count":1},{"startOffset":22454,"endOffset":22467,"count":0},{"startOffset":22496,"endOffset":22508,"count":0}],"isBlockCoverage":true},{"functionName":"moduleResolve","ranges":[{"startOffset":22664,"endOffset":23235,"count":1},{"startOffset":22938,"endOffset":23188,"count":0}],"isBlockCoverage":true},{"functionName":"resolveAsCommonJS","ranges":[{"startOffset":23381,"endOffset":24789,"count":0}],"isBlockCoverage":false},{"functionName":"defaultResolve","ranges":[{"startOffset":24791,"endOffset":28097,"count":2},{"startOffset":24944,"endOffset":25547,"count":0},{"startOffset":25640,"endOffset":25694,"count":0},{"startOffset":25721,"endOffset":25751,"count":0},{"startOffset":25757,"endOffset":25783,"count":0},{"startOffset":25797,"endOffset":25827,"count":0},{"startOffset":25828,"endOffset":25858,"count":0},{"startOffset":25864,"endOffset":25913,"count":0},{"startOffset":25966,"endOffset":26087,"count":1},{"startOffset":26089,"endOffset":26177,"count":0},{"startOffset":26177,"endOffset":26235,"count":1},{"startOffset":26235,"endOffset":26762,"count":0},{"startOffset":26762,"endOffset":26891,"count":1},{"startOffset":26891,"endOffset":27695,"count":0},{"startOffset":27695,"endOffset":27710,"count":1},{"startOffset":27710,"endOffset":27733,"count":0},{"startOffset":27734,"endOffset":27753,"count":1},{"startOffset":27755,"endOffset":28066,"count":1},{"startOffset":27995,"endOffset":28000,"count":0},{"startOffset":28066,"endOffset":28096,"count":1}],"isBlockCoverage":true}]},{"scriptId":"64","url":"internal/modules/esm/get_format.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2496,"count":1}],"isBlockCoverage":false},{"functionName":"defaultGetFormat","ranges":[{"startOffset":1131,"endOffset":2450,"count":2},{"startOffset":1244,"endOffset":1350,"count":1},{"startOffset":1350,"endOffset":1760,"count":0},{"startOffset":1760,"endOffset":2421,"count":1},{"startOffset":1951,"endOffset":1963,"count":0},{"startOffset":1970,"endOffset":2023,"count":0},{"startOffset":2041,"endOffset":2378,"count":0},{"startOffset":2407,"endOffset":2414,"count":0},{"startOffset":2421,"endOffset":2449,"count":0}],"isBlockCoverage":true}]},{"scriptId":"65","url":"internal/modules/esm/get_source.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1307,"count":1}],"isBlockCoverage":false},{"functionName":"defaultGetSource","ranges":[{"startOffset":609,"endOffset":1261,"count":1},{"startOffset":810,"endOffset":1155,"count":0},{"startOffset":1180,"endOffset":1238,"count":0}],"isBlockCoverage":true}]},{"scriptId":"66","url":"internal/fs/promises.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":18758,"count":1}],"isBlockCoverage":false},{"functionName":"FileHandle","ranges":[{"startOffset":2166,"endOffset":2343,"count":1},{"startOffset":2280,"endOffset":2284,"count":0}],"isBlockCoverage":true},{"functionName":"getAsyncId","ranges":[{"startOffset":2347,"endOffset":2404,"count":0}],"isBlockCoverage":false},{"functionName":"get fd","ranges":[{"startOffset":2408,"endOffset":2444,"count":12}],"isBlockCoverage":true},{"functionName":"appendFile","ranges":[{"startOffset":2448,"endOffset":2530,"count":0}],"isBlockCoverage":false},{"functionName":"chmod","ranges":[{"startOffset":2534,"endOffset":2590,"count":0}],"isBlockCoverage":false},{"functionName":"chown","ranges":[{"startOffset":2594,"endOffset":2658,"count":0}],"isBlockCoverage":false},{"functionName":"datasync","ranges":[{"startOffset":2662,"endOffset":2714,"count":0}],"isBlockCoverage":false},{"functionName":"sync","ranges":[{"startOffset":2718,"endOffset":2762,"count":0}],"isBlockCoverage":false},{"functionName":"read","ranges":[{"startOffset":2766,"endOffset":2875,"count":0}],"isBlockCoverage":false},{"functionName":"readv","ranges":[{"startOffset":2879,"endOffset":2960,"count":0}],"isBlockCoverage":false},{"functionName":"readFile","ranges":[{"startOffset":2964,"endOffset":3031,"count":0}],"isBlockCoverage":false},{"functionName":"stat","ranges":[{"startOffset":3035,"endOffset":3095,"count":0}],"isBlockCoverage":false},{"functionName":"truncate","ranges":[{"startOffset":3099,"endOffset":3163,"count":0}],"isBlockCoverage":false},{"functionName":"utimes","ranges":[{"startOffset":3167,"endOffset":3241,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":3245,"endOffset":3356,"count":0}],"isBlockCoverage":false},{"functionName":"writev","ranges":[{"startOffset":3360,"endOffset":3443,"count":0}],"isBlockCoverage":false},{"functionName":"writeFile","ranges":[{"startOffset":3447,"endOffset":3528,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":3532,"endOffset":4224,"count":1}],"isBlockCoverage":false},{"functionName":"close","ranges":[{"startOffset":3540,"endOffset":4224,"count":1},{"startOffset":3574,"endOffset":3612,"count":0},{"startOffset":3643,"endOffset":3684,"count":0},{"startOffset":3878,"endOffset":4187,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":3814,"endOffset":3870,"count":1}],"isBlockCoverage":true},{"functionName":".Promise.finally.","ranges":[{"startOffset":3926,"endOffset":4032,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":4042,"endOffset":4179,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":4228,"endOffset":4697,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":4701,"endOffset":4754,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":4758,"endOffset":4845,"count":0}],"isBlockCoverage":false},{"functionName":"fsCall","ranges":[{"startOffset":4849,"endOffset":5467,"count":0}],"isBlockCoverage":false},{"functionName":"writeFileHandle","ranges":[{"startOffset":5469,"endOffset":6027,"count":0}],"isBlockCoverage":false},{"functionName":"readFileHandle","ranges":[{"startOffset":6029,"endOffset":6892,"count":1},{"startOffset":6268,"endOffset":6293,"count":0},{"startOffset":6326,"endOffset":6364,"count":0},{"startOffset":6418,"endOffset":6445,"count":0},{"startOffset":6520,"endOffset":6756,"count":11},{"startOffset":6712,"endOffset":6752,"count":10},{"startOffset":6845,"endOffset":6880,"count":0}],"isBlockCoverage":true},{"functionName":"access","ranges":[{"startOffset":7045,"endOffset":7266,"count":0}],"isBlockCoverage":false},{"functionName":"copyFile","ranges":[{"startOffset":7268,"endOffset":7626,"count":0}],"isBlockCoverage":false},{"functionName":"open","ranges":[{"startOffset":7746,"endOffset":8071,"count":1}],"isBlockCoverage":true},{"functionName":"read","ranges":[{"startOffset":8073,"endOffset":9127,"count":11},{"startOffset":8212,"endOffset":8494,"count":0},{"startOffset":8518,"endOffset":8539,"count":0},{"startOffset":8631,"endOffset":8668,"count":0},{"startOffset":8697,"endOffset":8827,"count":0},{"startOffset":8931,"endOffset":8945,"count":0},{"startOffset":9087,"endOffset":9091,"count":1}],"isBlockCoverage":true},{"functionName":"readv","ranges":[{"startOffset":9129,"endOffset":9449,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":9451,"endOffset":10372,"count":0}],"isBlockCoverage":false},{"functionName":"writev","ranges":[{"startOffset":10374,"endOffset":10706,"count":0}],"isBlockCoverage":false},{"functionName":"rename","ranges":[{"startOffset":10708,"endOffset":11014,"count":0}],"isBlockCoverage":false},{"functionName":"truncate","ranges":[{"startOffset":11016,"endOffset":11159,"count":0}],"isBlockCoverage":false},{"functionName":"ftruncate","ranges":[{"startOffset":11161,"endOffset":11320,"count":0}],"isBlockCoverage":false},{"functionName":"rm","ranges":[{"startOffset":11322,"endOffset":11519,"count":0}],"isBlockCoverage":false},{"functionName":"rmdir","ranges":[{"startOffset":11521,"endOffset":11784,"count":0}],"isBlockCoverage":false},{"functionName":"fdatasync","ranges":[{"startOffset":11786,"endOffset":11875,"count":0}],"isBlockCoverage":false},{"functionName":"fsync","ranges":[{"startOffset":11877,"endOffset":11958,"count":0}],"isBlockCoverage":false},{"functionName":"mkdir","ranges":[{"startOffset":11960,"endOffset":12492,"count":0}],"isBlockCoverage":false},{"functionName":"readdir","ranges":[{"startOffset":12494,"endOffset":12946,"count":0}],"isBlockCoverage":false},{"functionName":"readlink","ranges":[{"startOffset":12948,"endOffset":13191,"count":0}],"isBlockCoverage":false},{"functionName":"symlink","ranges":[{"startOffset":13193,"endOffset":13606,"count":0}],"isBlockCoverage":false},{"functionName":"fstat","ranges":[{"startOffset":13608,"endOffset":13786,"count":0}],"isBlockCoverage":false},{"functionName":"lstat","ranges":[{"startOffset":13788,"endOffset":14058,"count":0}],"isBlockCoverage":false},{"functionName":"stat","ranges":[{"startOffset":14060,"endOffset":14327,"count":0}],"isBlockCoverage":false},{"functionName":"link","ranges":[{"startOffset":14329,"endOffset":14652,"count":0}],"isBlockCoverage":false},{"functionName":"unlink","ranges":[{"startOffset":14654,"endOffset":14792,"count":0}],"isBlockCoverage":false},{"functionName":"fchmod","ranges":[{"startOffset":14794,"endOffset":14927,"count":0}],"isBlockCoverage":false},{"functionName":"chmod","ranges":[{"startOffset":14929,"endOffset":15115,"count":0}],"isBlockCoverage":false},{"functionName":"lchmod","ranges":[{"startOffset":15117,"endOffset":15355,"count":0}],"isBlockCoverage":false},{"functionName":"lchown","ranges":[{"startOffset":15357,"endOffset":15599,"count":0}],"isBlockCoverage":false},{"functionName":"fchown","ranges":[{"startOffset":15601,"endOffset":15764,"count":0}],"isBlockCoverage":false},{"functionName":"chown","ranges":[{"startOffset":15766,"endOffset":16005,"count":0}],"isBlockCoverage":false},{"functionName":"utimes","ranges":[{"startOffset":16007,"endOffset":16279,"count":0}],"isBlockCoverage":false},{"functionName":"futimes","ranges":[{"startOffset":16281,"endOffset":16480,"count":0}],"isBlockCoverage":false},{"functionName":"lutimes","ranges":[{"startOffset":16482,"endOffset":16759,"count":0}],"isBlockCoverage":false},{"functionName":"realpath","ranges":[{"startOffset":16761,"endOffset":16938,"count":0}],"isBlockCoverage":false},{"functionName":"mkdtemp","ranges":[{"startOffset":16940,"endOffset":17270,"count":0}],"isBlockCoverage":false},{"functionName":"writeFile","ranges":[{"startOffset":17272,"endOffset":17785,"count":0}],"isBlockCoverage":false},{"functionName":"appendFile","ranges":[{"startOffset":17787,"endOffset":18028,"count":0}],"isBlockCoverage":false},{"functionName":"readFile","ranges":[{"startOffset":18030,"endOffset":18351,"count":1},{"startOffset":18147,"endOffset":18153,"count":0},{"startOffset":18194,"endOffset":18231,"count":0}],"isBlockCoverage":true}]},{"scriptId":"67","url":"internal/fs/rimraf.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":7039,"count":1}],"isBlockCoverage":false},{"functionName":"rimraf","ranges":[{"startOffset":1137,"endOffset":1597,"count":0}],"isBlockCoverage":false},{"functionName":"_rimraf","ranges":[{"startOffset":1600,"endOffset":2458,"count":0}],"isBlockCoverage":false},{"functionName":"fixWinEPERM","ranges":[{"startOffset":2461,"endOffset":2896,"count":0}],"isBlockCoverage":false},{"functionName":"_rmdir","ranges":[{"startOffset":2899,"endOffset":3197,"count":0}],"isBlockCoverage":false},{"functionName":"_rmchildren","ranges":[{"startOffset":3200,"endOffset":3872,"count":0}],"isBlockCoverage":false},{"functionName":"rimrafPromises","ranges":[{"startOffset":3875,"endOffset":4073,"count":0}],"isBlockCoverage":false},{"functionName":"rimrafSync","ranges":[{"startOffset":4076,"endOffset":4781,"count":0}],"isBlockCoverage":false},{"functionName":"_unlinkSync","ranges":[{"startOffset":4784,"endOffset":5267,"count":0}],"isBlockCoverage":false},{"functionName":"_rmdirSync","ranges":[{"startOffset":5270,"endOffset":6540,"count":0}],"isBlockCoverage":false},{"functionName":"fixWinEPERMSync","ranges":[{"startOffset":6543,"endOffset":6979,"count":0}],"isBlockCoverage":false}]},{"scriptId":"68","url":"internal/modules/esm/transform_source.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":215,"count":1}],"isBlockCoverage":false},{"functionName":"defaultTransformSource","ranges":[{"startOffset":15,"endOffset":157,"count":1}],"isBlockCoverage":true}]},{"scriptId":"69","url":"internal/modules/esm/translators.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":12048,"count":1}],"isBlockCoverage":false},{"functionName":"lazyTypes","ranges":[{"startOffset":416,"endOffset":528,"count":2},{"startOffset":462,"endOffset":527,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":1202,"endOffset":1227,"count":1}],"isBlockCoverage":true},{"functionName":"initCJSParse","ranges":[{"startOffset":1860,"endOffset":2152,"count":0}],"isBlockCoverage":false},{"functionName":"assertBufferSource","ranges":[{"startOffset":2286,"endOffset":2706,"count":2},{"startOffset":2363,"endOffset":2390,"count":1},{"startOffset":2392,"endOffset":2409,"count":0},{"startOffset":2503,"endOffset":2528,"count":0},{"startOffset":2547,"endOffset":2626,"count":0},{"startOffset":2627,"endOffset":2631,"count":0}],"isBlockCoverage":true},{"functionName":"stringify","ranges":[{"startOffset":2708,"endOffset":2926,"count":1},{"startOffset":2767,"endOffset":2779,"count":0},{"startOffset":2883,"endOffset":2892,"count":0}],"isBlockCoverage":true},{"functionName":"errPath","ranges":[{"startOffset":2928,"endOffset":3073,"count":0}],"isBlockCoverage":false},{"functionName":"importModuleDynamically","ranges":[{"startOffset":3075,"endOffset":3189,"count":0}],"isBlockCoverage":false},{"functionName":"createImportMetaResolve","ranges":[{"startOffset":3191,"endOffset":3539,"count":0}],"isBlockCoverage":false},{"functionName":"initializeImportMeta","ranges":[{"startOffset":3541,"endOffset":3711,"count":0}],"isBlockCoverage":false},{"functionName":"moduleStrategy","ranges":[{"startOffset":3793,"endOffset":4374,"count":1}],"isBlockCoverage":true},{"functionName":"enrichCJSError","ranges":[{"startOffset":4378,"endOffset":5277,"count":0}],"isBlockCoverage":false},{"functionName":"commonjsStrategy","ranges":[{"startOffset":5435,"endOffset":6741,"count":0}],"isBlockCoverage":false},{"functionName":"cjsPreparseModuleExports","ranges":[{"startOffset":6745,"endOffset":8189,"count":0}],"isBlockCoverage":false},{"functionName":"builtinStrategy","ranges":[{"startOffset":8313,"endOffset":8701,"count":1},{"startOffset":8574,"endOffset":8626,"count":0}],"isBlockCoverage":true},{"functionName":"jsonStrategy","ranges":[{"startOffset":8765,"endOffset":10884,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":10950,"endOffset":12045,"count":0}],"isBlockCoverage":false}]},{"scriptId":"70","url":"internal/modules/esm/create_dynamic_module.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1756,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":190,"endOffset":215,"count":0}],"isBlockCoverage":false},{"functionName":"createImport","ranges":[{"startOffset":219,"endOffset":409,"count":0}],"isBlockCoverage":false},{"functionName":"createExport","ranges":[{"startOffset":411,"endOffset":612,"count":0}],"isBlockCoverage":false},{"functionName":"createDynamicModule","ranges":[{"startOffset":642,"endOffset":1715,"count":0}],"isBlockCoverage":false}]},{"scriptId":"71","url":"internal/vm/module.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":12770,"count":1}],"isBlockCoverage":false},{"functionName":"Module","ranges":[{"startOffset":1618,"endOffset":3804,"count":0}],"isBlockCoverage":false},{"functionName":"get identifier","ranges":[{"startOffset":3808,"endOffset":3945,"count":0}],"isBlockCoverage":false},{"functionName":"get context","ranges":[{"startOffset":3949,"endOffset":4082,"count":0}],"isBlockCoverage":false},{"functionName":"get namespace","ranges":[{"startOffset":4086,"endOffset":4363,"count":0}],"isBlockCoverage":false},{"functionName":"get status","ranges":[{"startOffset":4367,"endOffset":4520,"count":0}],"isBlockCoverage":false},{"functionName":"get error","ranges":[{"startOffset":4524,"endOffset":4774,"count":0}],"isBlockCoverage":false},{"functionName":"link","ranges":[{"startOffset":4778,"endOffset":5257,"count":0}],"isBlockCoverage":false},{"functionName":"evaluate","ranges":[{"startOffset":5261,"endOffset":6213,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":6217,"endOffset":6945,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":7092,"endOffset":7128,"count":0}],"isBlockCoverage":false},{"functionName":"SourceTextModule","ranges":[{"startOffset":7133,"endOffset":9452,"count":0}],"isBlockCoverage":false},{"functionName":"get dependencySpecifiers","ranges":[{"startOffset":9456,"endOffset":9755,"count":0}],"isBlockCoverage":false},{"functionName":"get status","ranges":[{"startOffset":9759,"endOffset":10028,"count":0}],"isBlockCoverage":false},{"functionName":"get error","ranges":[{"startOffset":10032,"endOffset":10228,"count":0}],"isBlockCoverage":false},{"functionName":"createCachedData","ranges":[{"startOffset":10232,"endOffset":10494,"count":0}],"isBlockCoverage":false},{"functionName":"SyntheticModule","ranges":[{"startOffset":10539,"endOffset":11836,"count":0}],"isBlockCoverage":false},{"functionName":"setExport","ranges":[{"startOffset":11840,"endOffset":12142,"count":0}],"isBlockCoverage":false},{"functionName":"importModuleDynamicallyWrap","ranges":[{"startOffset":12146,"endOffset":12608,"count":0}],"isBlockCoverage":false},{"functionName":"getModuleFromWrap","ranges":[{"startOffset":12730,"endOffset":12765,"count":0}],"isBlockCoverage":false}]},{"scriptId":"72","url":"internal/modules/run_main.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2582,"count":1}],"isBlockCoverage":false},{"functionName":"resolveMainPath","ranges":[{"startOffset":220,"endOffset":658,"count":0}],"isBlockCoverage":false},{"functionName":"shouldUseESMLoader","ranges":[{"startOffset":660,"endOffset":1215,"count":0}],"isBlockCoverage":false},{"functionName":"runMainESM","ranges":[{"startOffset":1217,"endOffset":1552,"count":0}],"isBlockCoverage":false},{"functionName":"handleMainPromise","ranges":[{"startOffset":1554,"endOffset":1991,"count":1}],"isBlockCoverage":true},{"functionName":"handler","ranges":[{"startOffset":1803,"endOffset":1896,"count":1},{"startOffset":1870,"endOffset":1892,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":1953,"endOffset":1987,"count":0}],"isBlockCoverage":false},{"functionName":"executeUserEntryPoint","ranges":[{"startOffset":2177,"endOffset":2512,"count":0}],"isBlockCoverage":false}]},{"scriptId":"73","url":"file:///home/runner/work/JSLint/JSLint/[eval1]","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":8501,"count":1},{"startOffset":7322,"endOffset":8379,"count":0}],"isBlockCoverage":true},{"functionName":"globalThis.debugInline","ranges":[{"startOffset":227,"endOffset":489,"count":0}],"isBlockCoverage":false},{"functionName":"stringLineCount","ranges":[{"startOffset":526,"endOffset":873,"count":0}],"isBlockCoverage":false},{"functionName":"jslint2","ranges":[{"startOffset":874,"endOffset":7269,"count":1},{"startOffset":1046,"endOffset":4357,"count":0},{"startOffset":4362,"endOffset":5091,"count":0},{"startOffset":5096,"endOffset":5542,"count":0},{"startOffset":5547,"endOffset":5991,"count":0},{"startOffset":7153,"endOffset":7267,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":1155,"endOffset":1360,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":1481,"endOffset":1619,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":1708,"endOffset":2425,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":2585,"endOffset":3148,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":3242,"endOffset":4340,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":4478,"endOffset":4722,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":4829,"endOffset":5073,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":5210,"endOffset":5524,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":5665,"endOffset":5973,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":6318,"endOffset":6415,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":6434,"endOffset":7123,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":7367,"endOffset":8374,"count":0}],"isBlockCoverage":false}]},{"scriptId":"74","url":"internal/fs/streams.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":12971,"count":1}],"isBlockCoverage":false},{"functionName":"allocNewPool","ranges":[{"startOffset":1090,"endOffset":1254,"count":0}],"isBlockCoverage":false},{"functionName":"roundUpToMultipleOf8","ranges":[{"startOffset":1256,"endOffset":1345,"count":0}],"isBlockCoverage":false},{"functionName":"ReadStream","ranges":[{"startOffset":1347,"endOffset":3434,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":3573,"endOffset":3608,"count":0}],"isBlockCoverage":false},{"functionName":"_openReadFs","ranges":[{"startOffset":3708,"endOffset":4189,"count":0}],"isBlockCoverage":false},{"functionName":"ReadStream._read","ranges":[{"startOffset":4220,"endOffset":6712,"count":0}],"isBlockCoverage":false},{"functionName":"ReadStream._destroy","ranges":[{"startOffset":6747,"endOffset":7034,"count":0}],"isBlockCoverage":false},{"functionName":"closeFsStream","ranges":[{"startOffset":7037,"endOffset":7193,"count":0}],"isBlockCoverage":false},{"functionName":"ReadStream.close","ranges":[{"startOffset":7224,"endOffset":7310,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":7371,"endOffset":7405,"count":0}],"isBlockCoverage":false},{"functionName":"WriteStream","ranges":[{"startOffset":7433,"endOffset":9683,"count":0}],"isBlockCoverage":false},{"functionName":"WriteStream._final","ranges":[{"startOffset":9826,"endOffset":9983,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":10016,"endOffset":10052,"count":0}],"isBlockCoverage":false},{"functionName":"_openWriteFs","ranges":[{"startOffset":10155,"endOffset":10588,"count":0}],"isBlockCoverage":false},{"functionName":"WriteStream._write","ranges":[{"startOffset":10622,"endOffset":11290,"count":0}],"isBlockCoverage":false},{"functionName":"WriteStream._writev","ranges":[{"startOffset":11326,"endOffset":12216,"count":0}],"isBlockCoverage":false},{"functionName":"WriteStream.close","ranges":[{"startOffset":12314,"endOffset":12696,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":12859,"endOffset":12893,"count":0}],"isBlockCoverage":false}]},{"scriptId":"75","url":"stream.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2187,"count":1}],"isBlockCoverage":false},{"functionName":"_uint8ArrayToBuffer","ranges":[{"startOffset":1978,"endOffset":2185,"count":0}],"isBlockCoverage":false}]},{"scriptId":"76","url":"internal/streams/pipeline.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":7429,"count":1}],"isBlockCoverage":false},{"functionName":"destroyer","ranges":[{"startOffset":526,"endOffset":1918,"count":0}],"isBlockCoverage":false},{"functionName":"popCallback","ranges":[{"startOffset":1920,"endOffset":2291,"count":0}],"isBlockCoverage":false},{"functionName":"isPromise","ranges":[{"startOffset":2293,"endOffset":2372,"count":0}],"isBlockCoverage":false},{"functionName":"isReadable","ranges":[{"startOffset":2374,"endOffset":2454,"count":0}],"isBlockCoverage":false},{"functionName":"isWritable","ranges":[{"startOffset":2456,"endOffset":2537,"count":0}],"isBlockCoverage":false},{"functionName":"isStream","ranges":[{"startOffset":2539,"endOffset":2610,"count":0}],"isBlockCoverage":false},{"functionName":"isIterable","ranges":[{"startOffset":2612,"endOffset":2935,"count":0}],"isBlockCoverage":false},{"functionName":"makeAsyncIterable","ranges":[{"startOffset":2937,"endOffset":3213,"count":0}],"isBlockCoverage":false},{"functionName":"fromReadable","ranges":[{"startOffset":3215,"endOffset":3379,"count":0}],"isBlockCoverage":false},{"functionName":"pump","ranges":[{"startOffset":3381,"endOffset":3764,"count":0}],"isBlockCoverage":false},{"functionName":"pipeline","ranges":[{"startOffset":3766,"endOffset":7400,"count":0}],"isBlockCoverage":false}]},{"scriptId":"77","url":"internal/streams/destroy.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":3954,"count":1}],"isBlockCoverage":false},{"functionName":"destroy","ranges":[{"startOffset":123,"endOffset":1394,"count":0}],"isBlockCoverage":false},{"functionName":"emitErrorCloseNT","ranges":[{"startOffset":1396,"endOffset":1483,"count":0}],"isBlockCoverage":false},{"functionName":"emitCloseNT","ranges":[{"startOffset":1485,"endOffset":1703,"count":0}],"isBlockCoverage":false},{"functionName":"emitErrorNT","ranges":[{"startOffset":1705,"endOffset":1992,"count":0}],"isBlockCoverage":false},{"functionName":"undestroy","ranges":[{"startOffset":1994,"endOffset":2557,"count":0}],"isBlockCoverage":false},{"functionName":"errorOrDestroy","ranges":[{"startOffset":2559,"endOffset":3458,"count":0}],"isBlockCoverage":false},{"functionName":"isRequest","ranges":[{"startOffset":3460,"endOffset":3565,"count":0}],"isBlockCoverage":false},{"functionName":"destroyer","ranges":[{"startOffset":3600,"endOffset":3876,"count":0}],"isBlockCoverage":false}]},{"scriptId":"78","url":"internal/streams/end-of-stream.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":5791,"count":1}],"isBlockCoverage":false},{"functionName":"isRequest","ranges":[{"startOffset":280,"endOffset":375,"count":0}],"isBlockCoverage":false},{"functionName":"isReadable","ranges":[{"startOffset":377,"endOffset":535,"count":0}],"isBlockCoverage":false},{"functionName":"isWritable","ranges":[{"startOffset":537,"endOffset":695,"count":0}],"isBlockCoverage":false},{"functionName":"isWritableFinished","ranges":[{"startOffset":697,"endOffset":934,"count":0}],"isBlockCoverage":false},{"functionName":"nop","ranges":[{"startOffset":936,"endOffset":953,"count":0}],"isBlockCoverage":false},{"functionName":"isReadableEnded","ranges":[{"startOffset":955,"endOffset":1188,"count":0}],"isBlockCoverage":false},{"functionName":"eos","ranges":[{"startOffset":1190,"endOffset":5767,"count":0}],"isBlockCoverage":false}]},{"scriptId":"79","url":"internal/streams/legacy.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2081,"count":1}],"isBlockCoverage":false},{"functionName":"Stream","ranges":[{"startOffset":96,"endOffset":144,"count":0}],"isBlockCoverage":false},{"functionName":"Stream.pipe","ranges":[{"startOffset":258,"endOffset":2053,"count":0}],"isBlockCoverage":false}]},{"scriptId":"80","url":"internal/streams/readable.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":40358,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":1596,"endOffset":1621,"count":0}],"isBlockCoverage":false},{"functionName":"nop","ranges":[{"startOffset":2218,"endOffset":2235,"count":0}],"isBlockCoverage":false},{"functionName":"prependListener","ranges":[{"startOffset":2278,"endOffset":3085,"count":0}],"isBlockCoverage":false},{"functionName":"ReadableState","ranges":[{"startOffset":3087,"endOffset":6664,"count":0}],"isBlockCoverage":false},{"functionName":"Readable","ranges":[{"startOffset":6667,"endOffset":7237,"count":0}],"isBlockCoverage":false},{"functionName":"Readable._destroy","ranges":[{"startOffset":7374,"endOffset":7406,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.","ranges":[{"startOffset":7457,"endOffset":7495,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.push","ranges":[{"startOffset":7724,"endOffset":7810,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.unshift","ranges":[{"startOffset":7906,"endOffset":7991,"count":0}],"isBlockCoverage":false},{"functionName":"readableAddChunk","ranges":[{"startOffset":7994,"endOffset":10247,"count":0}],"isBlockCoverage":false},{"functionName":"addChunk","ranges":[{"startOffset":10249,"endOffset":10949,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.isPaused","ranges":[{"startOffset":10981,"endOffset":11093,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.setEncoding","ranges":[{"startOffset":11157,"endOffset":11801,"count":0}],"isBlockCoverage":false},{"functionName":"computeNewHighWaterMark","ranges":[{"startOffset":11862,"endOffset":12227,"count":0}],"isBlockCoverage":false},{"functionName":"howMuchToRead","ranges":[{"startOffset":12340,"endOffset":12734,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.read","ranges":[{"startOffset":12831,"endOffset":17061,"count":0}],"isBlockCoverage":false},{"functionName":"onEofChunk","ranges":[{"startOffset":17064,"endOffset":17884,"count":0}],"isBlockCoverage":false},{"functionName":"emitReadable","ranges":[{"startOffset":18085,"endOffset":18412,"count":0}],"isBlockCoverage":false},{"functionName":"emitReadable_","ranges":[{"startOffset":18414,"endOffset":19050,"count":0}],"isBlockCoverage":false},{"functionName":"maybeReadMore","ranges":[{"startOffset":19400,"endOffset":19556,"count":0}],"isBlockCoverage":false},{"functionName":"maybeReadMore_","ranges":[{"startOffset":19558,"endOffset":21354,"count":0}],"isBlockCoverage":false},{"functionName":"Readable._read","ranges":[{"startOffset":21625,"endOffset":21691,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.pipe","ranges":[{"startOffset":21720,"endOffset":26107,"count":0}],"isBlockCoverage":false},{"functionName":"pipeOnDrain","ranges":[{"startOffset":26110,"endOffset":26784,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.unpipe","ranges":[{"startOffset":26815,"endOffset":27484,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.on","ranges":[{"startOffset":27610,"endOffset":28517,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.removeListener","ranges":[{"startOffset":28612,"endOffset":29126,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.removeAllListeners","ranges":[{"startOffset":29229,"endOffset":29767,"count":0}],"isBlockCoverage":false},{"functionName":"updateReadableListening","ranges":[{"startOffset":29770,"endOffset":30280,"count":0}],"isBlockCoverage":false},{"functionName":"nReadingNextTick","ranges":[{"startOffset":30282,"endOffset":30370,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.resume","ranges":[{"startOffset":30524,"endOffset":30849,"count":0}],"isBlockCoverage":false},{"functionName":"resume","ranges":[{"startOffset":30852,"endOffset":31002,"count":0}],"isBlockCoverage":false},{"functionName":"resume_","ranges":[{"startOffset":31004,"endOffset":31255,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.pause","ranges":[{"startOffset":31284,"endOffset":31551,"count":0}],"isBlockCoverage":false},{"functionName":"flow","ranges":[{"startOffset":31554,"endOffset":31701,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.wrap","ranges":[{"startOffset":31885,"endOffset":33700,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.","ranges":[{"startOffset":33745,"endOffset":34126,"count":0}],"isBlockCoverage":false},{"functionName":"createAsyncIterator","ranges":[{"startOffset":34129,"endOffset":35422,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":35620,"endOffset":36009,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":36015,"endOffset":36145,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":36211,"endOffset":36277,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":36336,"endOffset":36418,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":36478,"endOffset":36538,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":36549,"endOffset":36658,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":36712,"endOffset":36766,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":36824,"endOffset":36912,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":36968,"endOffset":37053,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":37102,"endOffset":37236,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":37242,"endOffset":37531,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":37584,"endOffset":37672,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":37791,"endOffset":37836,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":37894,"endOffset":37945,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":37951,"endOffset":38000,"count":0}],"isBlockCoverage":false},{"functionName":"fromList","ranges":[{"startOffset":38304,"endOffset":38866,"count":0}],"isBlockCoverage":false},{"functionName":"endReadable","ranges":[{"startOffset":38868,"endOffset":39089,"count":0}],"isBlockCoverage":false},{"functionName":"endReadableNT","ranges":[{"startOffset":39091,"endOffset":40023,"count":0}],"isBlockCoverage":false},{"functionName":"endWritableNT","ranges":[{"startOffset":40025,"endOffset":40192,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.from","ranges":[{"startOffset":40210,"endOffset":40356,"count":0}],"isBlockCoverage":false}]},{"scriptId":"81","url":"internal/streams/buffer_list.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":3798,"count":1}],"isBlockCoverage":false},{"functionName":"BufferList","ranges":[{"startOffset":204,"endOffset":288,"count":0}],"isBlockCoverage":false},{"functionName":"push","ranges":[{"startOffset":292,"endOffset":479,"count":0}],"isBlockCoverage":false},{"functionName":"unshift","ranges":[{"startOffset":483,"endOffset":641,"count":0}],"isBlockCoverage":false},{"functionName":"shift","ranges":[{"startOffset":645,"endOffset":872,"count":0}],"isBlockCoverage":false},{"functionName":"clear","ranges":[{"startOffset":876,"endOffset":944,"count":0}],"isBlockCoverage":false},{"functionName":"join","ranges":[{"startOffset":948,"endOffset":1119,"count":0}],"isBlockCoverage":false},{"functionName":"concat","ranges":[{"startOffset":1123,"endOffset":1386,"count":0}],"isBlockCoverage":false},{"functionName":"consume","ranges":[{"startOffset":1470,"endOffset":1924,"count":0}],"isBlockCoverage":false},{"functionName":"first","ranges":[{"startOffset":1928,"endOffset":1968,"count":0}],"isBlockCoverage":false},{"functionName":"module.exports","ranges":[{"startOffset":1972,"endOffset":2068,"count":0}],"isBlockCoverage":false},{"functionName":"_getString","ranges":[{"startOffset":2143,"endOffset":2738,"count":0}],"isBlockCoverage":false},{"functionName":"_getBuffer","ranges":[{"startOffset":2808,"endOffset":3518,"count":0}],"isBlockCoverage":false},{"functionName":"module.exports","ranges":[{"startOffset":3599,"endOffset":3794,"count":0}],"isBlockCoverage":false}]},{"scriptId":"82","url":"internal/streams/state.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":881,"count":1}],"isBlockCoverage":false},{"functionName":"highWaterMarkFrom","ranges":[{"startOffset":142,"endOffset":309,"count":0}],"isBlockCoverage":false},{"functionName":"getDefaultHighWaterMark","ranges":[{"startOffset":311,"endOffset":397,"count":0}],"isBlockCoverage":false},{"functionName":"getHighWaterMark","ranges":[{"startOffset":399,"endOffset":811,"count":0}],"isBlockCoverage":false}]},{"scriptId":"83","url":"internal/streams/writable.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":22608,"count":1}],"isBlockCoverage":false},{"functionName":"nop","ranges":[{"startOffset":2223,"endOffset":2240,"count":0}],"isBlockCoverage":false},{"functionName":"WritableState","ranges":[{"startOffset":2242,"endOffset":6406,"count":0}],"isBlockCoverage":false},{"functionName":"resetBuffer","ranges":[{"startOffset":6408,"endOffset":6540,"count":0}],"isBlockCoverage":false},{"functionName":"getBuffer","ranges":[{"startOffset":6578,"endOffset":6652,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":6729,"endOffset":6794,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":7121,"endOffset":7335,"count":0}],"isBlockCoverage":false},{"functionName":"realHasInstance","ranges":[{"startOffset":7371,"endOffset":7428,"count":0}],"isBlockCoverage":false},{"functionName":"Writable","ranges":[{"startOffset":7433,"endOffset":8605,"count":0}],"isBlockCoverage":false},{"functionName":"Writable.pipe","ranges":[{"startOffset":8701,"endOffset":8769,"count":0}],"isBlockCoverage":false},{"functionName":"Writable.write","ranges":[{"startOffset":8799,"endOffset":10008,"count":0}],"isBlockCoverage":false},{"functionName":"Writable.cork","ranges":[{"startOffset":10037,"endOffset":10083,"count":0}],"isBlockCoverage":false},{"functionName":"Writable.uncork","ranges":[{"startOffset":10114,"endOffset":10269,"count":0}],"isBlockCoverage":false},{"functionName":"setDefaultEncoding","ranges":[{"startOffset":10312,"endOffset":10623,"count":0}],"isBlockCoverage":false},{"functionName":"writeOrBuffer","ranges":[{"startOffset":10813,"endOffset":11804,"count":0}],"isBlockCoverage":false},{"functionName":"doWrite","ranges":[{"startOffset":11806,"endOffset":12184,"count":0}],"isBlockCoverage":false},{"functionName":"onwriteError","ranges":[{"startOffset":12186,"endOffset":12606,"count":0}],"isBlockCoverage":false},{"functionName":"onwrite","ranges":[{"startOffset":12608,"endOffset":14202,"count":0}],"isBlockCoverage":false},{"functionName":"afterWriteTick","ranges":[{"startOffset":14204,"endOffset":14343,"count":0}],"isBlockCoverage":false},{"functionName":"afterWrite","ranges":[{"startOffset":14345,"endOffset":14755,"count":0}],"isBlockCoverage":false},{"functionName":"errorBuffer","ranges":[{"startOffset":14827,"endOffset":15148,"count":0}],"isBlockCoverage":false},{"functionName":"clearBuffer","ranges":[{"startOffset":15214,"endOffset":16647,"count":0}],"isBlockCoverage":false},{"functionName":"Writable._write","ranges":[{"startOffset":16677,"endOffset":16846,"count":0}],"isBlockCoverage":false},{"functionName":"Writable.end","ranges":[{"startOffset":16910,"endOffset":18094,"count":0}],"isBlockCoverage":false},{"functionName":"needFinish","ranges":[{"startOffset":18097,"endOffset":18310,"count":0}],"isBlockCoverage":false},{"functionName":"callFinal","ranges":[{"startOffset":18312,"endOffset":18572,"count":0}],"isBlockCoverage":false},{"functionName":"prefinish","ranges":[{"startOffset":18574,"endOffset":18922,"count":0}],"isBlockCoverage":false},{"functionName":"finishMaybe","ranges":[{"startOffset":18924,"endOffset":19251,"count":0}],"isBlockCoverage":false},{"functionName":"finish","ranges":[{"startOffset":19253,"endOffset":19871,"count":0}],"isBlockCoverage":false},{"functionName":"onFinished","ranges":[{"startOffset":19937,"endOffset":20401,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":20468,"endOffset":20555,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":20561,"endOffset":20743,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":20768,"endOffset":21160,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":21166,"endOffset":21300,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":21333,"endOffset":21419,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":21454,"endOffset":21542,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":21573,"endOffset":21655,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":21685,"endOffset":21769,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":21807,"endOffset":21891,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":21922,"endOffset":22002,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":22033,"endOffset":22110,"count":0}],"isBlockCoverage":false},{"functionName":"Writable.destroy","ranges":[{"startOffset":22186,"endOffset":22397,"count":0}],"isBlockCoverage":false},{"functionName":"Writable._destroy","ranges":[{"startOffset":22485,"endOffset":22517,"count":0}],"isBlockCoverage":false},{"functionName":"Writable.","ranges":[{"startOffset":22568,"endOffset":22606,"count":0}],"isBlockCoverage":false}]},{"scriptId":"84","url":"internal/streams/duplex.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":3661,"count":1}],"isBlockCoverage":false},{"functionName":"Duplex","ranges":[{"startOffset":1936,"endOffset":2360,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":3173,"endOffset":3385,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":3391,"endOffset":3652,"count":0}],"isBlockCoverage":false}]},{"scriptId":"85","url":"internal/streams/transform.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":8217,"count":1}],"isBlockCoverage":false},{"functionName":"afterTransform","ranges":[{"startOffset":4032,"endOffset":4550,"count":0}],"isBlockCoverage":false},{"functionName":"Transform","ranges":[{"startOffset":4553,"endOffset":5382,"count":0}],"isBlockCoverage":false},{"functionName":"prefinish","ranges":[{"startOffset":5384,"endOffset":5596,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":5691,"endOffset":5741,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":5838,"endOffset":5890,"count":0}],"isBlockCoverage":false},{"functionName":"Transform.push","ranges":[{"startOffset":5988,"endOffset":6124,"count":0}],"isBlockCoverage":false},{"functionName":"Transform._transform","ranges":[{"startOffset":6607,"endOffset":6696,"count":0}],"isBlockCoverage":false},{"functionName":"Transform._write","ranges":[{"startOffset":6728,"endOffset":7067,"count":0}],"isBlockCoverage":false},{"functionName":"Transform._read","ranges":[{"startOffset":7239,"endOffset":7613,"count":0}],"isBlockCoverage":false},{"functionName":"Transform._destroy","ranges":[{"startOffset":7648,"endOffset":7745,"count":0}],"isBlockCoverage":false},{"functionName":"done","ranges":[{"startOffset":7749,"endOffset":8216,"count":0}],"isBlockCoverage":false}]},{"scriptId":"86","url":"internal/streams/passthrough.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1762,"count":1}],"isBlockCoverage":false},{"functionName":"PassThrough","ranges":[{"startOffset":1529,"endOffset":1671,"count":0}],"isBlockCoverage":false},{"functionName":"PassThrough._transform","ranges":[{"startOffset":1708,"endOffset":1760,"count":0}],"isBlockCoverage":false}]},{"scriptId":"87","url":"file:///home/runner/work/JSLint/JSLint/jslint.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":151041,"count":1}],"isBlockCoverage":true},{"functionName":"empty","ranges":[{"startOffset":6694,"endOffset":6950,"count":461}],"isBlockCoverage":true},{"functionName":"populate","ranges":[{"startOffset":6952,"endOffset":7179,"count":9}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":7103,"endOffset":7156,"count":174}],"isBlockCoverage":true},{"functionName":"tag_regexp","ranges":[{"startOffset":15882,"endOffset":15972,"count":12}],"isBlockCoverage":true},{"functionName":"is_letter","ranges":[{"startOffset":17776,"endOffset":17923,"count":13},{"startOffset":17841,"endOffset":17863,"count":4},{"startOffset":17873,"endOffset":17914,"count":9},{"startOffset":17891,"endOffset":17913,"count":0}],"isBlockCoverage":true},{"functionName":"supplant","ranges":[{"startOffset":17925,"endOffset":18194,"count":0}],"isBlockCoverage":false},{"functionName":"artifact","ranges":[{"startOffset":20083,"endOffset":20381,"count":0}],"isBlockCoverage":false},{"functionName":"artifact_line","ranges":[{"startOffset":20383,"endOffset":20579,"count":0}],"isBlockCoverage":false},{"functionName":"artifact_column","ranges":[{"startOffset":20581,"endOffset":20781,"count":0}],"isBlockCoverage":false},{"functionName":"warn_at","ranges":[{"startOffset":20783,"endOffset":21392,"count":0}],"isBlockCoverage":false},{"functionName":"stop_at","ranges":[{"startOffset":21394,"endOffset":21554,"count":0}],"isBlockCoverage":false},{"functionName":"warn","ranges":[{"startOffset":21556,"endOffset":22183,"count":0}],"isBlockCoverage":false},{"functionName":"stop","ranges":[{"startOffset":22185,"endOffset":22572,"count":0}],"isBlockCoverage":false},{"functionName":"tokenize","ranges":[{"startOffset":22588,"endOffset":51592,"count":1},{"startOffset":23187,"endOffset":23195,"count":0},{"startOffset":24222,"endOffset":24271,"count":0},{"startOffset":51518,"endOffset":51590,"count":22313},{"startOffset":51554,"endOffset":51584,"count":1}],"isBlockCoverage":true},{"functionName":"next_line","ranges":[{"startOffset":24277,"endOffset":25460,"count":5020},{"startOffset":24601,"endOffset":24614,"count":0},{"startOffset":24627,"endOffset":24635,"count":0},{"startOffset":24648,"endOffset":24663,"count":0},{"startOffset":24674,"endOffset":24728,"count":0},{"startOffset":24865,"endOffset":24870,"count":432},{"startOffset":24911,"endOffset":25426,"count":5019},{"startOffset":24983,"endOffset":25174,"count":0},{"startOffset":25239,"endOffset":25416,"count":0}],"isBlockCoverage":true},{"functionName":"snip","ranges":[{"startOffset":25914,"endOffset":26022,"count":1919}],"isBlockCoverage":true},{"functionName":"next_char","ranges":[{"startOffset":26028,"endOffset":26851,"count":13386},{"startOffset":26278,"endOffset":26295,"count":88},{"startOffset":26297,"endOffset":26586,"count":0},{"startOffset":26612,"endOffset":26735,"count":13376},{"startOffset":26735,"endOffset":26803,"count":10}],"isBlockCoverage":true},{"functionName":"back_char","ranges":[{"startOffset":26857,"endOffset":27221,"count":343},{"startOffset":27154,"endOffset":27194,"count":0}],"isBlockCoverage":true},{"functionName":"some_digits","ranges":[{"startOffset":27227,"endOffset":27618,"count":2},{"startOffset":27367,"endOffset":27382,"count":0},{"startOffset":27384,"endOffset":27466,"count":0}],"isBlockCoverage":true},{"functionName":"escape","ranges":[{"startOffset":27624,"endOffset":28719,"count":31},{"startOffset":27713,"endOffset":27756,"count":17},{"startOffset":27756,"endOffset":27782,"count":14},{"startOffset":27782,"endOffset":27854,"count":0},{"startOffset":27854,"endOffset":27881,"count":14},{"startOffset":27881,"endOffset":28550,"count":2},{"startOffset":27923,"endOffset":28364,"count":0},{"startOffset":28438,"endOffset":28520,"count":0},{"startOffset":28550,"endOffset":28596,"count":12},{"startOffset":28598,"endOffset":28641,"count":12},{"startOffset":28641,"endOffset":28718,"count":0}],"isBlockCoverage":true},{"functionName":"make","ranges":[{"startOffset":28725,"endOffset":30636,"count":22338},{"startOffset":29138,"endOffset":29151,"count":21914},{"startOffset":29153,"endOffset":29200,"count":20115},{"startOffset":29285,"endOffset":29333,"count":2344},{"startOffset":29668,"endOffset":29693,"count":17837},{"startOffset":29706,"endOffset":29764,"count":10996},{"startOffset":29729,"endOffset":29749,"count":10995},{"startOffset":29750,"endOffset":29763,"count":10994},{"startOffset":29777,"endOffset":29839,"count":2},{"startOffset":29850,"endOffset":30031,"count":0},{"startOffset":30064,"endOffset":30084,"count":1327},{"startOffset":30086,"endOffset":30158,"count":0},{"startOffset":30188,"endOffset":30211,"count":1327},{"startOffset":30213,"endOffset":30258,"count":1327},{"startOffset":30563,"endOffset":30604,"count":21914}],"isBlockCoverage":true},{"functionName":"parse_directive","ranges":[{"startOffset":30642,"endOffset":32634,"count":211},{"startOffset":30929,"endOffset":32535,"count":210},{"startOffset":31081,"endOffset":31991,"count":0},{"startOffset":32083,"endOffset":32140,"count":1},{"startOffset":32191,"endOffset":32465,"count":0},{"startOffset":32535,"endOffset":32554,"count":1},{"startOffset":32554,"endOffset":32628,"count":0}],"isBlockCoverage":true},{"functionName":"comment","ranges":[{"startOffset":32640,"endOffset":33465,"count":424},{"startOffset":32891,"endOffset":32943,"count":1},{"startOffset":32996,"endOffset":33054,"count":0},{"startOffset":33127,"endOffset":33431,"count":1},{"startOffset":33162,"endOffset":33250,"count":0}],"isBlockCoverage":true},{"functionName":"regexp","ranges":[{"startOffset":33471,"endOffset":40546,"count":9},{"startOffset":39412,"endOffset":39492,"count":0},{"startOffset":40250,"endOffset":40323,"count":0},{"startOffset":40446,"endOffset":40456,"count":0},{"startOffset":40458,"endOffset":40517,"count":0}],"isBlockCoverage":true},{"functionName":"quantifier","ranges":[{"startOffset":33640,"endOffset":34265,"count":20},{"startOffset":33744,"endOffset":33759,"count":15},{"startOffset":33761,"endOffset":33805,"count":5},{"startOffset":33805,"endOffset":34177,"count":15},{"startOffset":33829,"endOffset":34132,"count":0},{"startOffset":34177,"endOffset":34208,"count":5},{"startOffset":34208,"endOffset":34255,"count":0}],"isBlockCoverage":true},{"functionName":"subklass","ranges":[{"startOffset":34275,"endOffset":35011,"count":23},{"startOffset":34373,"endOffset":34457,"count":1},{"startOffset":34457,"endOffset":34534,"count":22},{"startOffset":34551,"endOffset":34566,"count":22},{"startOffset":34583,"endOffset":34598,"count":15},{"startOffset":34615,"endOffset":34630,"count":15},{"startOffset":34647,"endOffset":34662,"count":15},{"startOffset":34677,"endOffset":34722,"count":7},{"startOffset":34722,"endOffset":34753,"count":15},{"startOffset":34753,"endOffset":34839,"count":0},{"startOffset":34839,"endOffset":34951,"count":15},{"startOffset":34862,"endOffset":34874,"count":1},{"startOffset":34876,"endOffset":34951,"count":0},{"startOffset":34951,"endOffset":35010,"count":15}],"isBlockCoverage":true},{"functionName":"ranges","ranges":[{"startOffset":35021,"endOffset":35529,"count":18},{"startOffset":35103,"endOffset":35519,"count":11},{"startOffset":35139,"endOffset":35472,"count":5},{"startOffset":35214,"endOffset":35454,"count":0}],"isBlockCoverage":true},{"functionName":"klass","ranges":[{"startOffset":35539,"endOffset":36157,"count":7},{"startOffset":35636,"endOffset":35683,"count":1}],"isBlockCoverage":true},{"functionName":"classy","ranges":[{"startOffset":35697,"endOffset":36115,"count":7},{"startOffset":35776,"endOffset":35790,"count":0},{"startOffset":35792,"endOffset":36101,"count":0}],"isBlockCoverage":true},{"functionName":"choice","ranges":[{"startOffset":36167,"endOffset":39212,"count":10},{"startOffset":39122,"endOffset":39202,"count":0}],"isBlockCoverage":true},{"functionName":"group","ranges":[{"startOffset":36200,"endOffset":36763,"count":1},{"startOffset":36333,"endOffset":36572,"count":0},{"startOffset":36596,"endOffset":36691,"count":0}],"isBlockCoverage":true},{"functionName":"factor","ranges":[{"startOffset":36777,"endOffset":38680,"count":30},{"startOffset":36906,"endOffset":36921,"count":21},{"startOffset":36942,"endOffset":36957,"count":21},{"startOffset":36976,"endOffset":37029,"count":10},{"startOffset":37029,"endOffset":37064,"count":20},{"startOffset":37064,"endOffset":37145,"count":1},{"startOffset":37145,"endOffset":37180,"count":19},{"startOffset":37180,"endOffset":37261,"count":7},{"startOffset":37261,"endOffset":37297,"count":12},{"startOffset":37297,"endOffset":37495,"count":6},{"startOffset":37516,"endOffset":37531,"count":6},{"startOffset":37552,"endOffset":37567,"count":6},{"startOffset":37588,"endOffset":37603,"count":6},{"startOffset":37622,"endOffset":37865,"count":0},{"startOffset":37865,"endOffset":38608,"count":6},{"startOffset":37889,"endOffset":38039,"count":0},{"startOffset":38063,"endOffset":38299,"count":0},{"startOffset":38323,"endOffset":38457,"count":0},{"startOffset":38481,"endOffset":38608,"count":5},{"startOffset":38524,"endOffset":38590,"count":0},{"startOffset":38608,"endOffset":38679,"count":6}],"isBlockCoverage":true},{"functionName":"sequence","ranges":[{"startOffset":38694,"endOffset":38988,"count":30},{"startOffset":38752,"endOffset":38848,"count":20},{"startOffset":38848,"endOffset":38878,"count":10},{"startOffset":38878,"endOffset":38974,"count":0}],"isBlockCoverage":true},{"functionName":"make_flag","ranges":[{"startOffset":39833,"endOffset":40182,"count":13},{"startOffset":39889,"endOffset":40172,"count":4},{"startOffset":39935,"endOffset":40019,"count":0}],"isBlockCoverage":true},{"functionName":"string","ranges":[{"startOffset":40552,"endOffset":41376,"count":1567}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":40686,"endOffset":41366,"count":12944},{"startOffset":40736,"endOffset":40905,"count":1567},{"startOffset":40905,"endOffset":40935,"count":11377},{"startOffset":40935,"endOffset":41015,"count":0},{"startOffset":41015,"endOffset":41047,"count":11377},{"startOffset":41047,"endOffset":41093,"count":24},{"startOffset":41093,"endOffset":41329,"count":11353},{"startOffset":41117,"endOffset":41279,"count":24},{"startOffset":41150,"endOffset":41233,"count":0},{"startOffset":41279,"endOffset":41329,"count":11329},{"startOffset":41329,"endOffset":41365,"count":11377}],"isBlockCoverage":true},{"functionName":"frack","ranges":[{"startOffset":41382,"endOffset":41686,"count":181},{"startOffset":41427,"endOffset":41474,"count":0},{"startOffset":41517,"endOffset":41680,"count":0}],"isBlockCoverage":true},{"functionName":"number","ranges":[{"startOffset":41692,"endOffset":42688,"count":332},{"startOffset":41741,"endOffset":42081,"count":151},{"startOffset":41798,"endOffset":41838,"count":0},{"startOffset":41862,"endOffset":41915,"count":0},{"startOffset":41939,"endOffset":41994,"count":0},{"startOffset":42018,"endOffset":42071,"count":0},{"startOffset":42081,"endOffset":42144,"count":181},{"startOffset":42289,"endOffset":42303,"count":164},{"startOffset":42333,"endOffset":42347,"count":0},{"startOffset":42377,"endOffset":42391,"count":107},{"startOffset":42403,"endOffset":42619,"count":0}],"isBlockCoverage":true},{"functionName":"lex","ranges":[{"startOffset":42694,"endOffset":51302,"count":32845},{"startOffset":43519,"endOffset":43829,"count":4925},{"startOffset":43625,"endOffset":43819,"count":1},{"startOffset":43702,"endOffset":43750,"count":0},{"startOffset":43829,"endOffset":44028,"count":32844},{"startOffset":44028,"endOffset":44196,"count":0},{"startOffset":44196,"endOffset":44374,"count":32844},{"startOffset":44374,"endOffset":44411,"count":10531},{"startOffset":44411,"endOffset":44468,"count":22313},{"startOffset":44468,"endOffset":44530,"count":7484},{"startOffset":44530,"endOffset":44582,"count":14829},{"startOffset":44582,"endOffset":44629,"count":332},{"startOffset":44629,"endOffset":44688,"count":14497},{"startOffset":44688,"endOffset":44735,"count":1567},{"startOffset":44735,"endOffset":44765,"count":12930},{"startOffset":44765,"endOffset":44913,"count":0},{"startOffset":44913,"endOffset":45016,"count":12930},{"startOffset":45016,"endOffset":47593,"count":12},{"startOffset":45045,"endOffset":45132,"count":0},{"startOffset":47593,"endOffset":47656,"count":12918},{"startOffset":47656,"endOffset":47907,"count":423},{"startOffset":47792,"endOffset":47867,"count":0},{"startOffset":47907,"endOffset":47970,"count":12495},{"startOffset":47970,"endOffset":49168,"count":1},{"startOffset":48036,"endOffset":48115,"count":0},{"startOffset":48933,"endOffset":49009,"count":0},{"startOffset":49168,"endOffset":49225,"count":12494},{"startOffset":49225,"endOffset":51266,"count":9},{"startOffset":49844,"endOffset":50635,"count":0},{"startOffset":50802,"endOffset":51012,"count":0},{"startOffset":51026,"endOffset":51256,"count":0},{"startOffset":51266,"endOffset":51301,"count":12485}],"isBlockCoverage":true},{"functionName":"part","ranges":[{"startOffset":45533,"endOffset":47445,"count":110},{"startOffset":45708,"endOffset":45986,"count":60},{"startOffset":45864,"endOffset":45912,"count":0},{"startOffset":45986,"endOffset":46168,"count":50},{"startOffset":46168,"endOffset":46367,"count":38},{"startOffset":46367,"endOffset":46693,"count":12},{"startOffset":46693,"endOffset":47431,"count":0}],"isBlockCoverage":true},{"functionName":"expr","ranges":[{"startOffset":46837,"endOffset":47374,"count":0}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":48129,"endOffset":48801,"count":36},{"startOffset":48278,"endOffset":48333,"count":1},{"startOffset":48333,"endOffset":48425,"count":35},{"startOffset":48425,"endOffset":48517,"count":0},{"startOffset":48535,"endOffset":48667,"count":35},{"startOffset":48667,"endOffset":48756,"count":0},{"startOffset":48756,"endOffset":48800,"count":35}],"isBlockCoverage":true},{"functionName":"survey","ranges":[{"startOffset":51999,"endOffset":53248,"count":1488},{"startOffset":52181,"endOffset":52285,"count":5},{"startOffset":52285,"endOffset":52572,"count":1483},{"startOffset":52307,"endOffset":52485,"count":0},{"startOffset":52513,"endOffset":52572,"count":0},{"startOffset":52572,"endOffset":52675,"count":1483},{"startOffset":52675,"endOffset":52898,"count":1275},{"startOffset":52898,"endOffset":53231,"count":208},{"startOffset":52979,"endOffset":53049,"count":0},{"startOffset":53059,"endOffset":53199,"count":0},{"startOffset":53231,"endOffset":53247,"count":1483}],"isBlockCoverage":true},{"functionName":"dispense","ranges":[{"startOffset":53250,"endOffset":53560,"count":22455},{"startOffset":53413,"endOffset":53523,"count":424},{"startOffset":53438,"endOffset":53490,"count":0},{"startOffset":53523,"endOffset":53558,"count":22031}],"isBlockCoverage":true},{"functionName":"lookahead","ranges":[{"startOffset":53562,"endOffset":53747,"count":116}],"isBlockCoverage":true},{"functionName":"advance","ranges":[{"startOffset":53749,"endOffset":54687,"count":21915},{"startOffset":53891,"endOffset":53917,"count":7484},{"startOffset":53919,"endOffset":53951,"count":7272},{"startOffset":53951,"endOffset":54056,"count":14643},{"startOffset":53985,"endOffset":54019,"count":1579},{"startOffset":54021,"endOffset":54056,"count":781},{"startOffset":54136,"endOffset":54159,"count":8120},{"startOffset":54161,"endOffset":54522,"count":0},{"startOffset":54655,"endOffset":54685,"count":2}],"isBlockCoverage":true},{"functionName":"json_value","ranges":[{"startOffset":54720,"endOffset":57757,"count":0}],"isBlockCoverage":false},{"functionName":"enroll","ranges":[{"startOffset":57788,"endOffset":59810,"count":569},{"startOffset":58117,"endOffset":58135,"count":0},{"startOffset":58137,"endOffset":58178,"count":0},{"startOffset":58303,"endOffset":58520,"count":0},{"startOffset":58758,"endOffset":59536,"count":5},{"startOffset":58797,"endOffset":58945,"count":0},{"startOffset":59081,"endOffset":59112,"count":0},{"startOffset":59211,"endOffset":59233,"count":0},{"startOffset":59256,"endOffset":59504,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":58554,"endOffset":58730,"count":633},{"startOffset":58661,"endOffset":58716,"count":5}],"isBlockCoverage":true},{"functionName":"expression","ranges":[{"startOffset":59812,"endOffset":61134,"count":6289},{"startOffset":60478,"endOffset":60504,"count":4990},{"startOffset":60573,"endOffset":60604,"count":2605},{"startOffset":60606,"endOffset":60646,"count":2605},{"startOffset":60646,"endOffset":60793,"count":3684},{"startOffset":60736,"endOffset":60793,"count":0}],"isBlockCoverage":true},{"functionName":"right","ranges":[{"startOffset":60799,"endOffset":61111,"count":10584},{"startOffset":60968,"endOffset":60991,"count":4726},{"startOffset":61002,"endOffset":61105,"count":4295}],"isBlockCoverage":true},{"functionName":"condition","ranges":[{"startOffset":61136,"endOffset":61559,"count":560},{"startOffset":61392,"endOffset":61440,"count":0},{"startOffset":61487,"endOffset":61535,"count":0}],"isBlockCoverage":true},{"functionName":"is_weird","ranges":[{"startOffset":61561,"endOffset":61790,"count":4789},{"startOffset":61733,"endOffset":61781,"count":4787},{"startOffset":61754,"endOffset":61780,"count":83}],"isBlockCoverage":true},{"functionName":"are_similar","ranges":[{"startOffset":61792,"endOffset":63722,"count":1476},{"startOffset":61838,"endOffset":61866,"count":0},{"startOffset":61893,"endOffset":62111,"count":0},{"startOffset":62138,"endOffset":62167,"count":0},{"startOffset":62196,"endOffset":62218,"count":6},{"startOffset":62220,"endOffset":62263,"count":6},{"startOffset":62263,"endOffset":62329,"count":1470},{"startOffset":62329,"endOffset":62364,"count":68},{"startOffset":62364,"endOffset":62440,"count":1402},{"startOffset":62387,"endOffset":62400,"count":0},{"startOffset":62402,"endOffset":62440,"count":0},{"startOffset":62440,"endOffset":62470,"count":1470},{"startOffset":62470,"endOffset":62505,"count":598},{"startOffset":62505,"endOffset":62581,"count":872},{"startOffset":62528,"endOffset":62541,"count":0},{"startOffset":62543,"endOffset":62581,"count":0},{"startOffset":62581,"endOffset":62620,"count":1470},{"startOffset":62620,"endOffset":62665,"count":68},{"startOffset":62665,"endOffset":62700,"count":1402},{"startOffset":62702,"endOffset":62731,"count":2},{"startOffset":62731,"endOffset":62760,"count":1400},{"startOffset":62760,"endOffset":62776,"count":637},{"startOffset":62778,"endOffset":63702,"count":381},{"startOffset":62806,"endOffset":62956,"count":104},{"startOffset":62901,"endOffset":62931,"count":65},{"startOffset":62956,"endOffset":62990,"count":277},{"startOffset":62990,"endOffset":63061,"count":2},{"startOffset":63061,"endOffset":63096,"count":275},{"startOffset":63096,"endOffset":63302,"count":137},{"startOffset":63164,"endOffset":63212,"count":125},{"startOffset":63229,"endOffset":63277,"count":74},{"startOffset":63302,"endOffset":63338,"count":138},{"startOffset":63338,"endOffset":63577,"count":0},{"startOffset":63577,"endOffset":63613,"count":138},{"startOffset":63613,"endOffset":63636,"count":0},{"startOffset":63638,"endOffset":63675,"count":0},{"startOffset":63675,"endOffset":63702,"count":138},{"startOffset":63702,"endOffset":63721,"count":1019}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":62001,"endOffset":62093,"count":0}],"isBlockCoverage":false},{"functionName":"semicolon","ranges":[{"startOffset":63724,"endOffset":64031,"count":1571},{"startOffset":63838,"endOffset":64005,"count":0}],"isBlockCoverage":true},{"functionName":"statement","ranges":[{"startOffset":64033,"endOffset":65732,"count":2453},{"startOffset":64387,"endOffset":64411,"count":2430},{"startOffset":64413,"endOffset":65104,"count":0},{"startOffset":65246,"endOffset":65277,"count":1199},{"startOffset":65279,"endOffset":65401,"count":1176},{"startOffset":65401,"endOffset":65632,"count":1277},{"startOffset":65524,"endOffset":65551,"count":22},{"startOffset":65553,"endOffset":65605,"count":0},{"startOffset":65666,"endOffset":65704,"count":0}],"isBlockCoverage":true},{"functionName":"statements","ranges":[{"startOffset":65734,"endOffset":66407,"count":849}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":65896,"endOffset":66378,"count":3233},{"startOffset":65980,"endOffset":66007,"count":2385},{"startOffset":66020,"endOffset":66050,"count":2385},{"startOffset":66063,"endOffset":66090,"count":2385},{"startOffset":66103,"endOffset":66131,"count":2385},{"startOffset":66142,"endOffset":66372,"count":2384},{"startOffset":66249,"endOffset":66316,"count":0}],"isBlockCoverage":true},{"functionName":"not_top_level","ranges":[{"startOffset":66409,"endOffset":66589,"count":229},{"startOffset":66530,"endOffset":66587,"count":0}],"isBlockCoverage":true},{"functionName":"top_level_only","ranges":[{"startOffset":66591,"endOffset":66762,"count":1},{"startOffset":66713,"endOffset":66760,"count":0}],"isBlockCoverage":true},{"functionName":"block","ranges":[{"startOffset":66764,"endOffset":67862,"count":848},{"startOffset":67345,"endOffset":67366,"count":212},{"startOffset":67375,"endOffset":67411,"count":124},{"startOffset":67418,"endOffset":67513,"count":0},{"startOffset":67597,"endOffset":67746,"count":0}],"isBlockCoverage":true},{"functionName":"mutation_check","ranges":[{"startOffset":67864,"endOffset":68277,"count":519},{"startOffset":68090,"endOffset":68113,"count":272},{"startOffset":68122,"endOffset":68145,"count":24},{"startOffset":68154,"endOffset":68177,"count":0},{"startOffset":68184,"endOffset":68258,"count":0}],"isBlockCoverage":true},{"functionName":"left_check","ranges":[{"startOffset":68279,"endOffset":68878,"count":2616},{"startOffset":68490,"endOffset":68671,"count":364},{"startOffset":68544,"endOffset":68661,"count":0},{"startOffset":68680,"endOffset":68786,"count":364},{"startOffset":68748,"endOffset":68761,"count":40},{"startOffset":68762,"endOffset":68775,"count":33},{"startOffset":68793,"endOffset":68859,"count":0}],"isBlockCoverage":true},{"functionName":"symbol","ranges":[{"startOffset":68949,"endOffset":69273,"count":125},{"startOffset":69116,"endOffset":69248,"count":112},{"startOffset":69204,"endOffset":69208,"count":66}],"isBlockCoverage":true},{"functionName":"assignment","ranges":[{"startOffset":69275,"endOffset":70297,"count":12}],"isBlockCoverage":true},{"functionName":"the_symbol.led","ranges":[{"startOffset":69662,"endOffset":70271,"count":519},{"startOffset":69831,"endOffset":69859,"count":479},{"startOffset":69861,"endOffset":69950,"count":224},{"startOffset":69950,"endOffset":70017,"count":295},{"startOffset":70157,"endOffset":70209,"count":0}],"isBlockCoverage":true},{"functionName":"constant","ranges":[{"startOffset":70299,"endOffset":70781,"count":16},{"startOffset":70499,"endOffset":70506,"count":7},{"startOffset":70515,"endOffset":70691,"count":9}],"isBlockCoverage":true},{"functionName":"the_symbol.nud","ranges":[{"startOffset":70517,"endOffset":70691,"count":2216},{"startOffset":70603,"endOffset":70655,"count":228}],"isBlockCoverage":true},{"functionName":"infix","ranges":[{"startOffset":70783,"endOffset":71168,"count":30}],"isBlockCoverage":true},{"functionName":"the_symbol.led","ranges":[{"startOffset":70900,"endOffset":71142,"count":3762},{"startOffset":71016,"endOffset":71055,"count":2644},{"startOffset":71055,"endOffset":71141,"count":1118}],"isBlockCoverage":true},{"functionName":"infixr","ranges":[{"startOffset":71170,"endOffset":71505,"count":1}],"isBlockCoverage":true},{"functionName":"the_symbol.led","ranges":[{"startOffset":71302,"endOffset":71479,"count":0}],"isBlockCoverage":false},{"functionName":"post","ranges":[{"startOffset":71507,"endOffset":71801,"count":2}],"isBlockCoverage":true},{"functionName":"the_symbol.led","ranges":[{"startOffset":71625,"endOffset":71775,"count":0}],"isBlockCoverage":false},{"functionName":"pre","ranges":[{"startOffset":71803,"endOffset":72145,"count":2}],"isBlockCoverage":true},{"functionName":"the_symbol.nud","ranges":[{"startOffset":71914,"endOffset":72119,"count":0}],"isBlockCoverage":false},{"functionName":"prefix","ranges":[{"startOffset":72147,"endOffset":72517,"count":15}],"isBlockCoverage":true},{"functionName":"the_symbol.nud","ranges":[{"startOffset":72257,"endOffset":72491,"count":389},{"startOffset":72376,"endOffset":72411,"count":299},{"startOffset":72411,"endOffset":72490,"count":90}],"isBlockCoverage":true},{"functionName":"stmt","ranges":[{"startOffset":72519,"endOffset":72721,"count":21}],"isBlockCoverage":true},{"functionName":"the_symbol.fud","ranges":[{"startOffset":72621,"endOffset":72695,"count":1176}],"isBlockCoverage":true},{"functionName":"ternary","ranges":[{"startOffset":72723,"endOffset":73233,"count":1}],"isBlockCoverage":true},{"functionName":"the_symbol.led","ranges":[{"startOffset":72843,"endOffset":73207,"count":14},{"startOffset":73123,"endOffset":73175,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":73833,"endOffset":73899,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":73931,"endOffset":74137,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":74210,"endOffset":74416,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":74451,"endOffset":74517,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":74595,"endOffset":74692,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":74725,"endOffset":74791,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":74885,"endOffset":74985,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":75742,"endOffset":76938,"count":1124},{"startOffset":75843,"endOffset":75887,"count":1099},{"startOffset":75930,"endOffset":75948,"count":385},{"startOffset":75950,"endOffset":76005,"count":304},{"startOffset":76072,"endOffset":76568,"count":909},{"startOffset":76641,"endOffset":76869,"count":494},{"startOffset":76717,"endOffset":76773,"count":0},{"startOffset":76811,"endOffset":76863,"count":4},{"startOffset":76869,"endOffset":76914,"count":630}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":76083,"endOffset":76558,"count":1602},{"startOffset":76168,"endOffset":76248,"count":0},{"startOffset":76318,"endOffset":76379,"count":0},{"startOffset":76472,"endOffset":76548,"count":693}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":76957,"endOffset":77848,"count":1327},{"startOffset":77099,"endOffset":77149,"count":2},{"startOffset":77125,"endOffset":77148,"count":0},{"startOffset":77168,"endOffset":77394,"count":1325},{"startOffset":77213,"endOffset":77384,"count":34},{"startOffset":77271,"endOffset":77295,"count":33},{"startOffset":77312,"endOffset":77333,"count":33},{"startOffset":77350,"endOffset":77370,"count":33},{"startOffset":77403,"endOffset":77446,"count":1324},{"startOffset":77423,"endOffset":77445,"count":0},{"startOffset":77455,"endOffset":77562,"count":1324},{"startOffset":77507,"endOffset":77552,"count":0},{"startOffset":77569,"endOffset":77613,"count":1324},{"startOffset":77640,"endOffset":77686,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":77868,"endOffset":78759,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":78778,"endOffset":79215,"count":181},{"startOffset":78906,"endOffset":78933,"count":180},{"startOffset":78935,"endOffset":79090,"count":1},{"startOffset":79019,"endOffset":79084,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":79235,"endOffset":79295,"count":0}],"isBlockCoverage":false},{"functionName":"do_tick","ranges":[{"startOffset":79299,"endOffset":79801,"count":12}],"isBlockCoverage":true},{"functionName":"part","ranges":[{"startOffset":79445,"endOffset":79750,"count":12},{"startOffset":79576,"endOffset":79740,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":79819,"endOffset":79985,"count":12}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":80114,"endOffset":80775,"count":52},{"startOffset":80219,"endOffset":80733,"count":22}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":80230,"endOffset":80723,"count":208},{"startOffset":80348,"endOffset":80428,"count":0},{"startOffset":80493,"endOffset":80549,"count":0},{"startOffset":80637,"endOffset":80713,"count":186}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":80791,"endOffset":80853,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":80869,"endOffset":80943,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":80960,"endOffset":81205,"count":1},{"startOffset":81067,"endOffset":81151,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":81241,"endOffset":81382,"count":0}],"isBlockCoverage":false},{"functionName":"parameter_list","ranges":[{"startOffset":81386,"endOffset":86728,"count":212},{"startOffset":81512,"endOffset":81540,"count":118},{"startOffset":81542,"endOffset":86644,"count":118}],"isBlockCoverage":true},{"functionName":"parameter","ranges":[{"startOffset":81553,"endOffset":86634,"count":169},{"startOffset":81672,"endOffset":83579,"count":0},{"startOffset":83612,"endOffset":84974,"count":0},{"startOffset":85027,"endOffset":85467,"count":0},{"startOffset":85512,"endOffset":85589,"count":0},{"startOffset":85759,"endOffset":85821,"count":0},{"startOffset":85876,"endOffset":86037,"count":5},{"startOffset":86037,"endOffset":86393,"count":164},{"startOffset":86097,"endOffset":86371,"count":0},{"startOffset":86441,"endOffset":86592,"count":51}],"isBlockCoverage":true},{"functionName":"subparameter","ranges":[{"startOffset":82101,"endOffset":83281,"count":0}],"isBlockCoverage":true},{"functionName":"subparameter","ranges":[{"startOffset":84042,"endOffset":84713,"count":0}],"isBlockCoverage":true},{"functionName":"do_function","ranges":[{"startOffset":86730,"endOffset":89473,"count":212},{"startOffset":86976,"endOffset":87309,"count":93},{"startOffset":87018,"endOffset":87099,"count":0},{"startOffset":87309,"endOffset":87626,"count":119},{"startOffset":87434,"endOffset":87553,"count":27},{"startOffset":87553,"endOffset":87616,"count":92},{"startOffset":87632,"endOffset":87679,"count":0},{"startOffset":87747,"endOffset":87798,"count":0},{"startOffset":87922,"endOffset":87977,"count":0},{"startOffset":88438,"endOffset":88465,"count":119},{"startOffset":88467,"endOffset":88590,"count":27},{"startOffset":89134,"endOffset":89167,"count":93},{"startOffset":89174,"endOffset":89230,"count":0},{"startOffset":89343,"endOffset":89380,"count":0}],"isBlockCoverage":true},{"functionName":"enroll_parameter","ranges":[{"startOffset":88801,"endOffset":88994,"count":169},{"startOffset":88921,"endOffset":88988,"count":0}],"isBlockCoverage":true},{"functionName":"fart","ranges":[{"startOffset":89509,"endOffset":90556,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":90570,"endOffset":91725,"count":116},{"startOffset":90887,"endOffset":90922,"count":101},{"startOffset":90930,"endOffset":91008,"count":0},{"startOffset":91103,"endOffset":91151,"count":0},{"startOffset":91243,"endOffset":91701,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":91762,"endOffset":94696,"count":11}],"isBlockCoverage":true},{"functionName":"member","ranges":[{"startOffset":91904,"endOffset":94644,"count":160},{"startOffset":92160,"endOffset":92184,"count":0},{"startOffset":92199,"endOffset":92698,"count":0},{"startOffset":92792,"endOffset":92858,"count":0},{"startOffset":92939,"endOffset":94340,"count":156},{"startOffset":92983,"endOffset":93007,"count":154},{"startOffset":93009,"endOffset":93197,"count":22},{"startOffset":93062,"endOffset":93123,"count":0},{"startOffset":93197,"endOffset":94132,"count":134},{"startOffset":93230,"endOffset":93699,"count":0},{"startOffset":93758,"endOffset":93819,"count":0},{"startOffset":93990,"endOffset":94016,"count":0},{"startOffset":94018,"endOffset":94114,"count":0},{"startOffset":94216,"endOffset":94276,"count":0},{"startOffset":94340,"endOffset":94516,"count":4},{"startOffset":94556,"endOffset":94634,"count":149}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":94710,"endOffset":94776,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":94789,"endOffset":94863,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":94880,"endOffset":95708,"count":2},{"startOffset":94981,"endOffset":95006,"count":0},{"startOffset":95049,"endOffset":95097,"count":0},{"startOffset":95158,"endOffset":95191,"count":0},{"startOffset":95193,"endOffset":95666,"count":0}],"isBlockCoverage":true},{"functionName":"do_var","ranges":[{"startOffset":95712,"endOffset":100006,"count":279},{"startOffset":95914,"endOffset":96217,"count":125},{"startOffset":95952,"endOffset":96004,"count":1},{"startOffset":96004,"endOffset":96211,"count":124},{"startOffset":96045,"endOffset":96211,"count":0},{"startOffset":96318,"endOffset":96368,"count":0},{"startOffset":96398,"endOffset":96427,"count":0},{"startOffset":96429,"endOffset":96477,"count":0}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":96483,"endOffset":99957,"count":279},{"startOffset":96535,"endOffset":96564,"count":0},{"startOffset":96566,"endOffset":98040,"count":0},{"startOffset":98072,"endOffset":98101,"count":0},{"startOffset":98103,"endOffset":99363,"count":0},{"startOffset":99496,"endOffset":99555,"count":0},{"startOffset":99642,"endOffset":99653,"count":90},{"startOffset":99655,"endOffset":99818,"count":189},{"startOffset":99872,"endOffset":99951,"count":0}],"isBlockCoverage":true},{"functionName":"pair","ranges":[{"startOffset":96649,"endOffset":97920,"count":0}],"isBlockCoverage":true},{"functionName":"element","ranges":[{"startOffset":98188,"endOffset":99243,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":100048,"endOffset":100353,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":100373,"endOffset":100528,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":100546,"endOffset":100889,"count":12},{"startOffset":100665,"endOffset":100688,"count":0},{"startOffset":100736,"endOffset":100810,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":100903,"endOffset":101231,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":101249,"endOffset":103735,"count":1},{"startOffset":101998,"endOffset":102042,"count":0},{"startOffset":102338,"endOffset":102396,"count":0},{"startOffset":102558,"endOffset":103686,"count":0}],"isBlockCoverage":true},{"functionName":"export_id","ranges":[{"startOffset":101351,"endOffset":101882,"count":0}],"isBlockCoverage":false},{"functionName":"loop","ranges":[{"startOffset":103372,"endOffset":103564,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":103750,"endOffset":105046,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":105091,"endOffset":105670,"count":557},{"startOffset":105248,"endOffset":105649,"count":143},{"startOffset":105372,"endOffset":105385,"count":69},{"startOffset":105398,"endOffset":105407,"count":74},{"startOffset":105462,"endOffset":105643,"count":1},{"startOffset":105564,"endOffset":105633,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":105688,"endOffset":107613,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":107652,"endOffset":108009,"count":226},{"startOffset":107760,"endOffset":107809,"count":0},{"startOffset":107871,"endOffset":107909,"count":220},{"startOffset":107911,"endOffset":107966,"count":220}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":108027,"endOffset":110616,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":110633,"endOffset":110867,"count":2},{"startOffset":110795,"endOffset":110843,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":110882,"endOffset":112328,"count":1},{"startOffset":110992,"endOffset":111038,"count":0},{"startOffset":111400,"endOffset":111481,"count":0},{"startOffset":111876,"endOffset":112028,"count":0},{"startOffset":112066,"endOffset":112245,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":112366,"endOffset":112676,"count":3},{"startOffset":112579,"endOffset":112625,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":112692,"endOffset":112740,"count":0}],"isBlockCoverage":false},{"functionName":"action","ranges":[{"startOffset":112798,"endOffset":113691,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":112940,"endOffset":113688,"count":38},{"startOffset":113145,"endOffset":113205,"count":8},{"startOffset":113326,"endOffset":113399,"count":10},{"startOffset":113540,"endOffset":113606,"count":37}],"isBlockCoverage":true},{"functionName":"amble","ranges":[{"startOffset":113693,"endOffset":114646,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":113838,"endOffset":114643,"count":24536},{"startOffset":114124,"endOffset":114637,"count":16630},{"startOffset":114262,"endOffset":114389,"count":6695},{"startOffset":114500,"endOffset":114627,"count":12309}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":114294,"endOffset":114373,"count":6695}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":114532,"endOffset":114611,"count":16071}],"isBlockCoverage":true},{"functionName":"walk_expression","ranges":[{"startOffset":114822,"endOffset":115508,"count":18963},{"startOffset":114871,"endOffset":115506,"count":11775},{"startOffset":114907,"endOffset":114962,"count":2808},{"startOffset":114962,"endOffset":115500,"count":8967},{"startOffset":115087,"endOffset":115147,"count":119},{"startOffset":115213,"endOffset":115273,"count":0},{"startOffset":115390,"endOffset":115460,"count":0}],"isBlockCoverage":true},{"functionName":"walk_statement","ranges":[{"startOffset":115510,"endOffset":116265,"count":9107},{"startOffset":115558,"endOffset":116263,"count":4150},{"startOffset":115594,"endOffset":115648,"count":849},{"startOffset":115648,"endOffset":116257,"count":3301},{"startOffset":115774,"endOffset":115907,"count":758},{"startOffset":115814,"endOffset":115893,"count":0},{"startOffset":115907,"endOffset":116136,"count":2543},{"startOffset":115978,"endOffset":116009,"count":519},{"startOffset":116026,"endOffset":116050,"count":0},{"startOffset":116065,"endOffset":116136,"count":0}],"isBlockCoverage":true},{"functionName":"lookup","ranges":[{"startOffset":116267,"endOffset":118139,"count":3684},{"startOffset":116588,"endOffset":117703,"count":950},{"startOffset":117073,"endOffset":117594,"count":6},{"startOffset":117137,"endOffset":117233,"count":0},{"startOffset":117703,"endOffset":117791,"count":2734},{"startOffset":117744,"endOffset":117791,"count":0},{"startOffset":117847,"endOffset":118037,"count":0},{"startOffset":118048,"endOffset":118102,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":116616,"endOffset":116910,"count":1622},{"startOffset":116781,"endOffset":116811,"count":1020},{"startOffset":116830,"endOffset":116896,"count":1020}],"isBlockCoverage":true},{"functionName":"subactivate","ranges":[{"startOffset":118141,"endOffset":118246,"count":0}],"isBlockCoverage":false},{"functionName":"preaction_function","ranges":[{"startOffset":118248,"endOffset":119177,"count":212},{"startOffset":118321,"endOffset":118346,"count":93},{"startOffset":118348,"endOffset":118392,"count":0},{"startOffset":118562,"endOffset":118634,"count":120},{"startOffset":118666,"endOffset":118764,"count":0},{"startOffset":118797,"endOffset":118895,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":118925,"endOffset":119173,"count":169},{"startOffset":119033,"endOffset":119089,"count":0}],"isBlockCoverage":true},{"functionName":"bitwise_check","ranges":[{"startOffset":119179,"endOffset":119707,"count":4281},{"startOffset":119235,"endOffset":119266,"count":0},{"startOffset":119268,"endOffset":119312,"count":0},{"startOffset":119355,"endOffset":119375,"count":3157},{"startOffset":119384,"endOffset":119404,"count":2987},{"startOffset":119413,"endOffset":119432,"count":2819},{"startOffset":119441,"endOffset":119475,"count":2340},{"startOffset":119484,"endOffset":119516,"count":1013},{"startOffset":119525,"endOffset":119654,"count":1001},{"startOffset":119661,"endOffset":119705,"count":0}],"isBlockCoverage":true},{"functionName":"pop_block","ranges":[{"startOffset":119709,"endOffset":119871,"count":1060}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":119758,"endOffset":119807,"count":214}],"isBlockCoverage":true},{"functionName":"activate","ranges":[{"startOffset":119873,"endOffset":120194,"count":279},{"startOffset":119961,"endOffset":120162,"count":189},{"startOffset":120053,"endOffset":120109,"count":0}],"isBlockCoverage":true},{"functionName":"action_var","ranges":[{"startOffset":120196,"endOffset":120261,"count":279}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":120359,"endOffset":121410,"count":3762},{"startOffset":120417,"endOffset":121408,"count":726},{"startOffset":120557,"endOffset":120609,"count":0},{"startOffset":120641,"endOffset":121402,"count":17},{"startOffset":120684,"endOffset":120816,"count":0},{"startOffset":120930,"endOffset":121012,"count":0},{"startOffset":121083,"endOffset":121106,"count":15},{"startOffset":121127,"endOffset":121148,"count":13},{"startOffset":121169,"endOffset":121190,"count":12},{"startOffset":121211,"endOffset":121232,"count":7},{"startOffset":121253,"endOffset":121274,"count":0},{"startOffset":121293,"endOffset":121378,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":121439,"endOffset":121505,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":121534,"endOffset":121600,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":121676,"endOffset":121845,"count":168}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":121724,"endOffset":121841,"count":336},{"startOffset":121773,"endOffset":121790,"count":30},{"startOffset":121792,"endOffset":121835,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":121873,"endOffset":122570,"count":1124},{"startOffset":121971,"endOffset":122016,"count":968},{"startOffset":122025,"endOffset":122064,"count":465},{"startOffset":122071,"endOffset":122568,"count":285},{"startOffset":122279,"endOffset":122300,"count":123},{"startOffset":122317,"endOffset":122351,"count":1},{"startOffset":122368,"endOffset":122404,"count":1},{"startOffset":122421,"endOffset":122478,"count":1},{"startOffset":122493,"endOffset":122552,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":122599,"endOffset":122648,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":122685,"endOffset":122738,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":122766,"endOffset":122850,"count":1327},{"startOffset":122815,"endOffset":122848,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":122881,"endOffset":122976,"count":848}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":123009,"endOffset":123355,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":123528,"endOffset":123703,"count":3460}],"isBlockCoverage":true},{"functionName":"init_variable","ranges":[{"startOffset":123707,"endOffset":123963,"count":224},{"startOffset":123925,"endOffset":123962,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":123996,"endOffset":124412,"count":32},{"startOffset":124076,"endOffset":124410,"count":23},{"startOffset":124162,"endOffset":124184,"count":22},{"startOffset":124352,"endOffset":124404,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":124440,"endOffset":126247,"count":519},{"startOffset":124736,"endOffset":125567,"count":479},{"startOffset":124777,"endOffset":124965,"count":224},{"startOffset":124823,"endOffset":124890,"count":0},{"startOffset":124965,"endOffset":125561,"count":255},{"startOffset":125007,"endOffset":125027,"count":232},{"startOffset":125029,"endOffset":125241,"count":23},{"startOffset":125241,"endOffset":125551,"count":232},{"startOffset":125358,"endOffset":125551,"count":0},{"startOffset":125567,"endOffset":126245,"count":40},{"startOffset":125616,"endOffset":125764,"count":23},{"startOffset":125689,"endOffset":125754,"count":0},{"startOffset":125876,"endOffset":126162,"count":36},{"startOffset":126034,"endOffset":126060,"count":31},{"startOffset":126081,"endOffset":126130,"count":1},{"startOffset":126173,"endOffset":126239,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":125073,"endOffset":125225,"count":46},{"startOffset":125132,"endOffset":125207,"count":36}],"isBlockCoverage":true},{"functionName":"postaction_function","ranges":[{"startOffset":126251,"endOffset":126538,"count":212},{"startOffset":126463,"endOffset":126512,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":126561,"endOffset":128273,"count":3762},{"startOffset":126625,"endOffset":127010,"count":726},{"startOffset":126883,"endOffset":126923,"count":0},{"startOffset":126948,"endOffset":127004,"count":0},{"startOffset":127037,"endOffset":127350,"count":35},{"startOffset":127120,"endOffset":127205,"count":0},{"startOffset":127249,"endOffset":127334,"count":0},{"startOffset":127350,"endOffset":128271,"count":3727},{"startOffset":127378,"endOffset":127627,"count":181},{"startOffset":127429,"endOffset":127502,"count":0},{"startOffset":127550,"endOffset":127621,"count":0},{"startOffset":127627,"endOffset":128271,"count":3546},{"startOffset":127654,"endOffset":127674,"count":2219},{"startOffset":127676,"endOffset":127788,"count":1327},{"startOffset":127724,"endOffset":127782,"count":0},{"startOffset":127788,"endOffset":128271,"count":2219},{"startOffset":127837,"endOffset":128271,"count":1095},{"startOffset":127952,"endOffset":127976,"count":19},{"startOffset":127989,"endOffset":128015,"count":0},{"startOffset":128028,"endOffset":128045,"count":0},{"startOffset":128056,"endOffset":128106,"count":0},{"startOffset":128182,"endOffset":128208,"count":1},{"startOffset":128219,"endOffset":128265,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":128303,"endOffset":128589,"count":170},{"startOffset":128538,"endOffset":128587,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":128619,"endOffset":128856,"count":168},{"startOffset":128805,"endOffset":128854,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":128934,"endOffset":131960,"count":1124},{"startOffset":129046,"endOffset":129109,"count":1},{"startOffset":129142,"endOffset":129232,"count":25},{"startOffset":129172,"endOffset":129226,"count":0},{"startOffset":129232,"endOffset":131958,"count":1099},{"startOffset":129259,"endOffset":130701,"count":943},{"startOffset":129296,"endOffset":130227,"count":1},{"startOffset":129523,"endOffset":129585,"count":0},{"startOffset":129619,"endOffset":129752,"count":0},{"startOffset":129783,"endOffset":129991,"count":0},{"startOffset":130023,"endOffset":130217,"count":0},{"startOffset":130227,"endOffset":130695,"count":942},{"startOffset":130339,"endOffset":130363,"count":2},{"startOffset":130380,"endOffset":130403,"count":1},{"startOffset":130420,"endOffset":130443,"count":1},{"startOffset":130460,"endOffset":130483,"count":0},{"startOffset":130498,"endOffset":130685,"count":0},{"startOffset":130701,"endOffset":131958,"count":156},{"startOffset":130814,"endOffset":130839,"count":0},{"startOffset":130841,"endOffset":130878,"count":0},{"startOffset":130927,"endOffset":131242,"count":0},{"startOffset":131283,"endOffset":131952,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":131989,"endOffset":132209,"count":181},{"startOffset":132053,"endOffset":132103,"count":0},{"startOffset":132143,"endOffset":132207,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":132381,"endOffset":132432,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":132527,"endOffset":132979,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":133057,"endOffset":133398,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":133467,"endOffset":134494,"count":14},{"startOffset":133653,"endOffset":133697,"count":0},{"startOffset":133762,"endOffset":133817,"count":0},{"startOffset":133882,"endOffset":133937,"count":0},{"startOffset":133998,"endOffset":134035,"count":0},{"startOffset":134042,"endOffset":134097,"count":0},{"startOffset":134159,"endOffset":134195,"count":0},{"startOffset":134202,"endOffset":134256,"count":0},{"startOffset":134320,"endOffset":134425,"count":12},{"startOffset":134432,"endOffset":134492,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":134517,"endOffset":135232,"count":273},{"startOffset":134562,"endOffset":134717,"count":0},{"startOffset":134745,"endOffset":134853,"count":65},{"startOffset":134795,"endOffset":134847,"count":0},{"startOffset":134853,"endOffset":135230,"count":208},{"startOffset":134882,"endOffset":134993,"count":0},{"startOffset":135037,"endOffset":135056,"count":156},{"startOffset":135065,"endOffset":135091,"count":145},{"startOffset":135100,"endOffset":135121,"count":26},{"startOffset":135128,"endOffset":135230,"count":25},{"startOffset":135178,"endOffset":135224,"count":8}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":134599,"endOffset":134662,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":135314,"endOffset":135763,"count":0}],"isBlockCoverage":false},{"functionName":"delve","ranges":[{"startOffset":135767,"endOffset":136407,"count":213}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":135844,"endOffset":136403,"count":1525},{"startOffset":135988,"endOffset":136387,"count":575},{"startOffset":136067,"endOffset":136199,"count":0},{"startOffset":136218,"endOffset":136281,"count":0},{"startOffset":136303,"endOffset":136373,"count":0}],"isBlockCoverage":true},{"functionName":"uninitialized_and_unused","ranges":[{"startOffset":136409,"endOffset":136718,"count":1},{"startOffset":136640,"endOffset":136654,"count":0}],"isBlockCoverage":true},{"functionName":"whitage","ranges":[{"startOffset":136783,"endOffset":147409,"count":1}],"isBlockCoverage":true},{"functionName":"pop","ranges":[{"startOffset":136993,"endOffset":137217,"count":2766}],"isBlockCoverage":true},{"functionName":"push","ranges":[{"startOffset":137223,"endOffset":137375,"count":2766}],"isBlockCoverage":true},{"functionName":"expected_at","ranges":[{"startOffset":137381,"endOffset":137578,"count":0}],"isBlockCoverage":false},{"functionName":"at_margin","ranges":[{"startOffset":137584,"endOffset":137727,"count":3898},{"startOffset":137674,"endOffset":137721,"count":0}],"isBlockCoverage":true},{"functionName":"no_space_only","ranges":[{"startOffset":137733,"endOffset":138151,"count":10618},{"startOffset":137974,"endOffset":138145,"count":0}],"isBlockCoverage":true},{"functionName":"no_space","ranges":[{"startOffset":138157,"endOffset":138922,"count":373},{"startOffset":138260,"endOffset":138288,"count":0},{"startOffset":138290,"endOffset":138489,"count":0},{"startOffset":138499,"endOffset":138916,"count":0}],"isBlockCoverage":true},{"functionName":"one_space_only","ranges":[{"startOffset":138928,"endOffset":139201,"count":1105},{"startOffset":139026,"endOffset":139195,"count":0}],"isBlockCoverage":true},{"functionName":"one_space","ranges":[{"startOffset":139207,"endOffset":139676,"count":5883},{"startOffset":139267,"endOffset":139275,"count":220},{"startOffset":139277,"endOffset":139561,"count":5663},{"startOffset":139324,"endOffset":139352,"count":0},{"startOffset":139354,"endOffset":139551,"count":0},{"startOffset":139561,"endOffset":139670,"count":220},{"startOffset":139608,"endOffset":139660,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":139713,"endOffset":147405,"count":22338},{"startOffset":139800,"endOffset":139823,"count":21914},{"startOffset":139825,"endOffset":139874,"count":425},{"startOffset":139874,"endOffset":147399,"count":21913},{"startOffset":140400,"endOffset":142052,"count":3105},{"startOffset":140447,"endOffset":141680,"count":2766},{"startOffset":140622,"endOffset":141158,"count":1024},{"startOffset":140670,"endOffset":140682,"count":155},{"startOffset":140810,"endOffset":140967,"count":0},{"startOffset":140991,"endOffset":141061,"count":0},{"startOffset":141158,"endOffset":141662,"count":1742},{"startOffset":141237,"endOffset":141523,"count":0},{"startOffset":141680,"endOffset":142038,"count":339},{"startOffset":141953,"endOffset":142020,"count":0},{"startOffset":142052,"endOffset":147145,"count":18808},{"startOffset":142106,"endOffset":142396,"count":1605},{"startOffset":142152,"endOffset":142217,"count":69},{"startOffset":142217,"endOffset":142322,"count":1536},{"startOffset":142396,"endOffset":147131,"count":17203},{"startOffset":142427,"endOffset":142658,"count":2766},{"startOffset":142488,"endOffset":142507,"count":1024},{"startOffset":142509,"endOffset":142570,"count":1024},{"startOffset":142570,"endOffset":142640,"count":1742},{"startOffset":142658,"endOffset":147131,"count":14437},{"startOffset":142957,"endOffset":143019,"count":0},{"startOffset":143053,"endOffset":143194,"count":0},{"startOffset":143221,"endOffset":143626,"count":1079},{"startOffset":143257,"endOffset":143396,"count":441},{"startOffset":143343,"endOffset":143370,"count":177},{"startOffset":143398,"endOffset":143466,"count":793},{"startOffset":143466,"endOffset":143541,"count":286},{"startOffset":143626,"endOffset":147113,"count":13358},{"startOffset":143663,"endOffset":143876,"count":28},{"startOffset":143768,"endOffset":143854,"count":0},{"startOffset":143876,"endOffset":147113,"count":13330},{"startOffset":143960,"endOffset":143979,"count":3762},{"startOffset":144004,"endOffset":144011,"count":1124},{"startOffset":144034,"endOffset":144093,"count":34},{"startOffset":144093,"endOffset":147113,"count":13296},{"startOffset":144168,"endOffset":144187,"count":11969},{"startOffset":144212,"endOffset":144232,"count":11969},{"startOffset":144257,"endOffset":144276,"count":11969},{"startOffset":144301,"endOffset":144320,"count":10890},{"startOffset":144345,"endOffset":144364,"count":9091},{"startOffset":144389,"endOffset":144542,"count":8953},{"startOffset":144475,"endOffset":144516,"count":3728},{"startOffset":144496,"endOffset":144515,"count":2638},{"startOffset":144567,"endOffset":144706,"count":7682},{"startOffset":144655,"endOffset":144680,"count":212},{"startOffset":144729,"endOffset":144793,"count":5734},{"startOffset":144793,"endOffset":147113,"count":7562},{"startOffset":144820,"endOffset":144840,"count":6235},{"startOffset":144842,"endOffset":144906,"count":1327},{"startOffset":144906,"endOffset":147113,"count":6235},{"startOffset":144933,"endOffset":145060,"count":0},{"startOffset":145144,"endOffset":145165,"count":6207},{"startOffset":145190,"endOffset":145212,"count":6207},{"startOffset":145237,"endOffset":145258,"count":6204},{"startOffset":145283,"endOffset":145307,"count":6127},{"startOffset":145332,"endOffset":145354,"count":6119},{"startOffset":145379,"endOffset":145402,"count":6116},{"startOffset":145427,"endOffset":145449,"count":6115},{"startOffset":145474,"endOffset":145499,"count":5972},{"startOffset":145524,"endOffset":145569,"count":5972},{"startOffset":145549,"endOffset":145568,"count":0},{"startOffset":145594,"endOffset":145634,"count":5972},{"startOffset":145614,"endOffset":145633,"count":824},{"startOffset":145657,"endOffset":145722,"count":1036},{"startOffset":145722,"endOffset":147113,"count":5199},{"startOffset":145853,"endOffset":145882,"count":3422},{"startOffset":145907,"endOffset":146057,"count":1658},{"startOffset":145992,"endOffset":146031,"count":66},{"startOffset":146012,"endOffset":146030,"count":31},{"startOffset":146082,"endOffset":146235,"count":1604},{"startOffset":146168,"endOffset":146209,"count":66},{"startOffset":146189,"endOffset":146208,"count":31},{"startOffset":146260,"endOffset":146285,"count":1550},{"startOffset":146310,"endOffset":146328,"count":1338},{"startOffset":146353,"endOffset":146837,"count":1200},{"startOffset":146468,"endOffset":146493,"count":97},{"startOffset":146526,"endOffset":146551,"count":85},{"startOffset":146610,"endOffset":146811,"count":1115},{"startOffset":146696,"endOffset":146722,"count":598},{"startOffset":146755,"endOffset":146781,"count":598},{"startOffset":146862,"endOffset":146913,"count":683},{"startOffset":146893,"endOffset":146912,"count":574},{"startOffset":146936,"endOffset":146996,"count":5090},{"startOffset":146996,"endOffset":147113,"count":109},{"startOffset":147029,"endOffset":147047,"count":73},{"startOffset":147049,"endOffset":147113,"count":73}],"isBlockCoverage":true},{"functionName":"jslint","ranges":[{"startOffset":147472,"endOffset":151038,"count":1},{"startOffset":147941,"endOffset":147944,"count":0},{"startOffset":149069,"endOffset":149143,"count":0},{"startOffset":149388,"endOffset":149441,"count":0},{"startOffset":149455,"endOffset":149746,"count":0},{"startOffset":150102,"endOffset":150307,"count":0},{"startOffset":150342,"endOffset":150439,"count":0},{"startOffset":150808,"endOffset":150818,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":148734,"endOffset":148998,"count":6},{"startOffset":148891,"endOffset":148974,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":150135,"endOffset":150295,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":150942,"endOffset":151028,"count":0}],"isBlockCoverage":false}]}],"timestamp":46.417047} \ No newline at end of file diff --git a/branch.ci/.coverage/index.html b/branch.ci/.coverage/index.html new file mode 100644 index 000000000..545c4beef --- /dev/null +++ b/branch.ci/.coverage/index.html @@ -0,0 +1,139 @@ + + + +coverage-report + + + +
    +
    coverage-report
    + + + + + + + + + + + + + + + +
    +
    + + diff --git a/branch.ci/.coverage/jslint.js.html b/branch.ci/.coverage/jslint.js.html new file mode 100644 index 000000000..02646be44 --- /dev/null +++ b/branch.ci/.coverage/jslint.js.html @@ -0,0 +1,5151 @@ + + + +coverage-report + + + +
    +
    coverage-report
    +
    files coveredlines
    + ./
    +
    +
    +
    +
    + 60.15 %
    + 3019 / 5019 +
    + ./ jslint.js
    +
    +
    +
    +
    + 60.15 %
    + 3019 / 5019 +
    + + + + + + + + + + +
    files coveredlines
    + ./ jslint.js
    +
    +
    +
    +
    + 60.15 %
    + 3019 / 5019 +
    +
    +
    +
        1.      1// jslint.js
    +
        2.      1// 2020-11-06
    +
        3.      1// Copyright (c) 2015 Douglas Crockford  (www.JSLint.com)
    +
        4.      1
    +
        5.      1// Permission is hereby granted, free of charge, to any person obtaining a copy
    +
        6.      1// of this software and associated documentation files (the "Software"), to deal
    +
        7.      1// in the Software without restriction, including without limitation the rights
    +
        8.      1// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +
        9.      1// copies of the Software, and to permit persons to whom the Software is
    +
       10.      1// furnished to do so, subject to the following conditions:
    +
       11.      1
    +
       12.      1// The above copyright notice and this permission notice shall be included in
    +
       13.      1// all copies or substantial portions of the Software.
    +
       14.      1
    +
       15.      1// The Software shall be used for Good, not Evil.
    +
       16.      1
    +
       17.      1// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +
       18.      1// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +
       19.      1// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    +
       20.      1// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +
       21.      1// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +
       22.      1// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    +
       23.      1// SOFTWARE.
    +
       24.      1
    +
       25.      1// jslint(source, option_object, global_array) is a function that takes 3
    +
       26.      1// arguments. The second two arguments are optional.
    +
       27.      1
    +
       28.      1//      source          A text to analyze, a string or an array of strings.
    +
       29.      1//      option_object   An object whose keys correspond to option names.
    +
       30.      1//      global_array    An array of strings containing global variables that
    +
       31.      1//                      the file is allowed readonly access.
    +
       32.      1
    +
       33.      1// jslint returns an object containing its results. The object contains a lot
    +
       34.      1// of valuable information. It can be used to generate reports. The object
    +
       35.      1// contains:
    +
       36.      1
    +
       37.      1//      directives: an array of directive comment tokens.
    +
       38.      1//      edition: the version of JSLint that did the analysis.
    +
       39.      1//      exports: the names exported from the module.
    +
       40.      1//      froms: an array of strings representing each of the imports.
    +
       41.      1//      functions: an array of objects that represent all of the functions
    +
       42.      1//              declared in the file.
    +
       43.      1//      global: an object representing the global object. Its .context property
    +
       44.      1//              is an object containing a property for each global variable.
    +
       45.      1//      id: "(JSLint)"
    +
       46.      1//      json: true if the file is a JSON text.
    +
       47.      1//      lines: an array of strings, the source.
    +
       48.      1//      module: true if an import or export statement was used.
    +
       49.      1//      ok: true if no warnings were generated. This is what you want.
    +
       50.      1//      option: the option argument.
    +
       51.      1//      property: a property object.
    +
       52.      1//      stop: true if JSLint was unable to finish. You don't want this.
    +
       53.      1//      tokens: an array of objects representing the tokens in the file.
    +
       54.      1//      tree: the token objects arranged in a tree.
    +
       55.      1//      warnings: an array of warning objects. A warning object can contain:
    +
       56.      1//          name: "JSLintError"
    +
       57.      1//          column: A column number in the file.
    +
       58.      1//          line: A line number in the file.
    +
       59.      1//          code: A warning code string.
    +
       60.      1//          message: The warning message string.
    +
       61.      1//          a: Exhibit A.
    +
       62.      1//          b: Exhibit B.
    +
       63.      1//          c: Exhibit C.
    +
       64.      1//          d: Exhibit D.
    +
       65.      1
    +
       66.      1// jslint works in several phases. In any of these phases, errors might be
    +
       67.      1// found. Sometimes JSLint is able to recover from an error and continue
    +
       68.      1// parsing. In some cases, it cannot and will stop early. If that should happen,
    +
       69.      1// repair your code and try again.
    +
       70.      1
    +
       71.      1// Phases:
    +
       72.      1
    +
       73.      1//      1. If the source is a single string, split it into an array of strings.
    +
       74.      1//      2. Turn the source into an array of tokens.
    +
       75.      1//      3. Furcate the tokens into a parse tree.
    +
       76.      1//      4. Walk the tree, traversing all of the nodes of the tree. It is a
    +
       77.      1//          recursive traversal. Each node may be processed on the way down
    +
       78.      1//          (preaction) and on the way up (postaction).
    +
       79.      1//      5. Check the whitespace between the tokens.
    +
       80.      1
    +
       81.      1// jslint can also examine JSON text. It decides that a file is JSON text if
    +
       82.      1// the first token is "[" or "{". Processing of JSON text is much simpler than
    +
       83.      1// the processing of JavaScript programs. Only the first three phases are
    +
       84.      1// required.
    +
       85.      1
    +
       86.      1// WARNING: JSLint will hurt your feelings.
    +
       87.      1
    +
       88.      1/*property
    +
       89.      1    a, and, arity, assign, b, bad_assignment_a, bad_directive_a, bad_get,
    +
       90.      1    bad_module_name_a, bad_option_a, bad_property_a, bad_set, bitwise, block,
    +
       91.      1    body, browser, c, calls, catch, charCodeAt, closer, closure, code, column,
    +
       92.      1    concat, constant, context, convert, couch, create, d, dead, default, devel,
    +
       93.      1    directive, directives, disrupt, dot, duplicate_a, edition, ellipsis, else,
    +
       94.      1    empty_block, eval, every, expected_a, expected_a_at_b_c,
    +
       95.      1    expected_a_b, expected_a_b_from_c_d, expected_a_before_b,
    +
       96.      1    expected_a_next_at_b, expected_digits_after_a, expected_four_digits,
    +
       97.      1    expected_identifier_a, expected_line_break_a_b, expected_regexp_factor_a,
    +
       98.      1    expected_space_a_b, expected_statements_a, expected_string_a,
    +
       99.      1    expected_type_string_a, exports, expression, extra, finally, flag, for,
    +
      100.      1    forEach, free, freeze, freeze_exports, from, froms, fud, fudge,
    +
      101.      1    function_in_loop, functions, g, getset, global, i, id, identifier, import,
    +
      102.      1    inc, indexOf, infix_in, init, initial, isArray, isNaN, join, json, keys,
    +
      103.      1    label, label_a, lbp, led, length, level, line, lines, live, long, loop, m,
    +
      104.      1    margin, match, message, misplaced_a, misplaced_directive_a, missing_browser,
    +
      105.      1    missing_m, module, naked_block, name, names, nested_comment, new, node,
    +
      106.      1    not_label_a, nr, nud, number_isNaN, ok, open, opening, option,
    +
      107.      1    out_of_scope_a, parameters, parent, pop, property, push, quote, raw,
    +
      108.      1    redefinition_a_b, replace, required_a_optional_b, reserved_a, role, search,
    +
      109.      1    shebang, signature, single, slice, some, sort, split, startsWith, statement,
    +
      110.      1    stop, subscript_a, switch, test, this, thru, toString, todo_comment,
    +
      111.      1    tokens, too_long, too_many_digits, tree, try, type, u, unclosed_comment,
    +
      112.      1    unclosed_mega, unclosed_string, undeclared_a, unexpected_a,
    +
      113.      1    unexpected_a_after_b, unexpected_a_before_b, unexpected_at_top_level_a,
    +
      114.      1    unexpected_char_a, unexpected_comment, unexpected_directive_a,
    +
      115.      1    unexpected_expression_a, unexpected_label_a, unexpected_parens,
    +
      116.      1    unexpected_space_a_b, unexpected_statement_a, unexpected_trailing_space,
    +
      117.      1    unexpected_typeof_a, uninitialized_a, unreachable_a,
    +
      118.      1    unregistered_property_a, unused_a, use_double, use_open, use_spaces,
    +
      119.      1    used, value, var_loop, var_switch, variable, warning, warnings,
    +
      120.      1    weird_condition_a, weird_expression_a, weird_loop, weird_relation_a, white,
    +
      121.      1    wrap_condition, wrap_immediate, wrap_parameter, wrap_regexp, wrap_unary,
    +
      122.      1    wrapped, writable, y
    +
      123.      1*/
    +
      124.      1
    +
      125.    461function empty() {
    +
      126.    461
    +
      127.    461// The empty function produces a new empty object that inherits nothing. This is
    +
      128.    461// much better than '{}' because confusions around accidental method names like
    +
      129.    461// 'constructor' are completely avoided.
    +
      130.    461
    +
      131.    461    return Object.create(null);
    +
      132.    461}
    +
      133.      1
    +
      134.      9function populate(array, object = empty(), value = true) {
    +
      135.      9
    +
      136.      9// Augment an object by taking property names from an array of strings.
    +
      137.      9
    +
      138.    174    array.forEach(function (name) {
    +
      139.    174        object[name] = value;
    +
      140.    174    });
    +
      141.      9    return object;
    +
      142.      9}
    +
      143.      1
    +
      144.      1const allowed_option = {
    +
      145.      1
    +
      146.      1// These are the options that are recognized in the option object or that may
    +
      147.      1// appear in a /*jslint*/ directive. Most options will have a boolean value,
    +
      148.      1// usually true. Some options will also predefine some number of global
    +
      149.      1// variables.
    +
      150.      1
    +
      151.      1    bitwise: true,
    +
      152.      1    browser: [
    +
      153.      1        "caches", "CharacterData", "clearInterval", "clearTimeout", "document",
    +
      154.      1        "DocumentType", "DOMException", "Element", "Event", "event", "fetch",
    +
      155.      1        "FileReader", "FontFace", "FormData", "history", "IntersectionObserver",
    +
      156.      1        "localStorage", "location", "MutationObserver", "name", "navigator",
    +
      157.      1        "screen", "sessionStorage", "setInterval", "setTimeout", "Storage",
    +
      158.      1        "TextDecoder", "TextEncoder", "URL", "window", "Worker",
    +
      159.      1        "XMLHttpRequest"
    +
      160.      1    ],
    +
      161.      1    couch: [
    +
      162.      1        "emit", "getRow", "isArray", "log", "provides", "registerType",
    +
      163.      1        "require", "send", "start", "sum", "toJSON"
    +
      164.      1    ],
    +
      165.      1    convert: true,
    +
      166.      1    devel: [
    +
      167.      1        "alert", "confirm", "console", "prompt"
    +
      168.      1    ],
    +
      169.      1    eval: true,
    +
      170.      1    for: true,
    +
      171.      1    fudge: true,
    +
      172.      1    getset: true,
    +
      173.      1    long: true,
    +
      174.      1    node: [
    +
      175.      1        "Buffer", "clearImmediate", "clearInterval", "clearTimeout",
    +
      176.      1        "console", "exports", "module", "process", "require",
    +
      177.      1        "setImmediate", "setInterval", "setTimeout", "TextDecoder",
    +
      178.      1        "TextEncoder", "URL", "URLSearchParams", "__dirname", "__filename"
    +
      179.      1    ],
    +
      180.      1    single: true,
    +
      181.      1    this: true,
    +
      182.      1    white: true
    +
      183.      1};
    +
      184.      1
    +
      185.      1const anticondition = populate([
    +
      186.      1    "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%",
    +
      187.      1    "typeof", "(number)", "(string)"
    +
      188.      1]);
    +
      189.      1
    +
      190.      1// These are the bitwise operators.
    +
      191.      1
    +
      192.      1const bitwiseop = populate([
    +
      193.      1    "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=",
    +
      194.      1    ">>>", ">>>="
    +
      195.      1]);
    +
      196.      1
    +
      197.      1const escapeable = populate([
    +
      198.      1    "\\", "/", "`", "b", "f", "n", "r", "t"
    +
      199.      1]);
    +
      200.      1
    +
      201.      1const opener = {
    +
      202.      1
    +
      203.      1// The open and close pairs.
    +
      204.      1
    +
      205.      1    "(": ")",       // paren
    +
      206.      1    "[": "]",       // bracket
    +
      207.      1    "{": "}",       // brace
    +
      208.      1    "${": "}"       // mega
    +
      209.      1};
    +
      210.      1
    +
      211.      1// The relational operators.
    +
      212.      1
    +
      213.      1const relationop = populate([
    +
      214.      1    "!=", "!==", "==", "===", "<", "<=", ">", ">="
    +
      215.      1]);
    +
      216.      1
    +
      217.      1// This is the set of infix operators that require a space on each side.
    +
      218.      1
    +
      219.      1const spaceop = populate([
    +
      220.      1    "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/",
    +
      221.      1    "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=",
    +
      222.      1    ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||"
    +
      223.      1]);
    +
      224.      1
    +
      225.      1const standard = [
    +
      226.      1
    +
      227.      1// These are the globals that are provided by the language standard.
    +
      228.      1
    +
      229.      1    "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI",
    +
      230.      1    "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error",
    +
      231.      1    "EvalError", "Float32Array", "Float64Array", "Generator",
    +
      232.      1    "GeneratorFunction", "Int8Array", "Int16Array", "Int32Array", "Intl",
    +
      233.      1    "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat",
    +
      234.      1    "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp",
    +
      235.      1    "Set", "String", "Symbol", "SyntaxError", "System", "TypeError",
    +
      236.      1    "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array",
    +
      237.      1    "URIError", "WeakMap", "WeakSet"
    +
      238.      1];
    +
      239.      1
    +
      240.      1const bundle = {
    +
      241.      1
    +
      242.      1// The bundle contains the raw text messages that are generated by jslint. It
    +
      243.      1// seems that they are all error messages and warnings. There are no "Atta
    +
      244.      1// boy!" or "You are so awesome!" messages. There is no positive reinforcement
    +
      245.      1// or encouragement. This relentless negativity can undermine self-esteem and
    +
      246.      1// wound the inner child. But if you accept it as sound advice rather than as
    +
      247.      1// personal criticism, it can make your programs better.
    +
      248.      1
    +
      249.      1    and: "The '&&' subexpression should be wrapped in parens.",
    +
      250.      1    bad_assignment_a: "Bad assignment to '{a}'.",
    +
      251.      1    bad_directive_a: "Bad directive '{a}'.",
    +
      252.      1    bad_get: "A get function takes no parameters.",
    +
      253.      1    bad_module_name_a: "Bad module name '{a}'.",
    +
      254.      1    bad_option_a: "Bad option '{a}'.",
    +
      255.      1    bad_property_a: "Bad property name '{a}'.",
    +
      256.      1    bad_set: "A set function takes one parameter.",
    +
      257.      1    duplicate_a: "Duplicate '{a}'.",
    +
      258.      1    empty_block: "Empty block.",
    +
      259.      1    expected_a: "Expected '{a}'.",
    +
      260.      1    expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.",
    +
      261.      1    expected_a_b: "Expected '{a}' and instead saw '{b}'.",
    +
      262.      1    expected_a_b_from_c_d: (
    +
      263.      1        "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'."
    +
      264.      1    ),
    +
      265.      1    expected_a_before_b: "Expected '{a}' before '{b}'.",
    +
      266.      1    expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.",
    +
      267.      1    expected_digits_after_a: "Expected digits after '{a}'.",
    +
      268.      1    expected_four_digits: "Expected four digits after '\\u'.",
    +
      269.      1    expected_identifier_a: "Expected an identifier and instead saw '{a}'.",
    +
      270.      1    expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.",
    +
      271.      1    expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.",
    +
      272.      1    expected_space_a_b: "Expected one space between '{a}' and '{b}'.",
    +
      273.      1    expected_statements_a: "Expected statements before '{a}'.",
    +
      274.      1    expected_string_a: "Expected a string and instead saw '{a}'.",
    +
      275.      1    expected_type_string_a: "Expected a type string and instead saw '{a}'.",
    +
      276.      1    freeze_exports: (
    +
      277.      1        "Expected 'Object.freeze('. All export values should be frozen."
    +
      278.      1    ),
    +
      279.      1    function_in_loop: "Don't make functions within a loop.",
    +
      280.      1    infix_in: (
    +
      281.      1        "Unexpected 'in'. Compare with undefined, "
    +
      282.      1        + "or use the hasOwnProperty method instead."
    +
      283.      1    ),
    +
      284.      1    label_a: "'{a}' is a statement label.",
    +
      285.      1    misplaced_a: "Place '{a}' at the outermost level.",
    +
      286.      1    misplaced_directive_a: (
    +
      287.      1        "Place the '/*{a}*/' directive before the first statement."
    +
      288.      1    ),
    +
      289.      1    missing_browser: "/*global*/ requires the Assume a browser option.",
    +
      290.      1    missing_m: "Expected 'm' flag on a multiline regular expression.",
    +
      291.      1    naked_block: "Naked block.",
    +
      292.      1    nested_comment: "Nested comment.",
    +
      293.      1    not_label_a: "'{a}' is not a label.",
    +
      294.      1    number_isNaN: "Use Number.isNaN function to compare with NaN.",
    +
      295.      1    out_of_scope_a: "'{a}' is out of scope.",
    +
      296.      1    redefinition_a_b: "Redefinition of '{a}' from line {b}.",
    +
      297.      1    required_a_optional_b: (
    +
      298.      1        "Required parameter '{a}' after optional parameter '{b}'."
    +
      299.      1    ),
    +
      300.      1    reserved_a: "Reserved name '{a}'.",
    +
      301.      1    subscript_a: "['{a}'] is better written in dot notation.",
    +
      302.      1    todo_comment: "Unexpected TODO comment.",
    +
      303.      1    too_long: "Line is longer than 80 characters.",
    +
      304.      1    too_many_digits: "Too many digits.",
    +
      305.      1    unclosed_comment: "Unclosed comment.",
    +
      306.      1    unclosed_mega: "Unclosed mega literal.",
    +
      307.      1    unclosed_string: "Unclosed string.",
    +
      308.      1    undeclared_a: "Undeclared '{a}'.",
    +
      309.      1    unexpected_a: "Unexpected '{a}'.",
    +
      310.      1    unexpected_a_after_b: "Unexpected '{a}' after '{b}'.",
    +
      311.      1    unexpected_a_before_b: "Unexpected '{a}' before '{b}'.",
    +
      312.      1    unexpected_at_top_level_a: "Expected '{a}' to be in a function.",
    +
      313.      1    unexpected_char_a: "Unexpected character '{a}'.",
    +
      314.      1    unexpected_comment: "Unexpected comment.",
    +
      315.      1    unexpected_directive_a: "When using modules, don't use directive '/*{a}'.",
    +
      316.      1    unexpected_expression_a: (
    +
      317.      1        "Unexpected expression '{a}' in statement position."
    +
      318.      1    ),
    +
      319.      1    unexpected_label_a: "Unexpected label '{a}'.",
    +
      320.      1    unexpected_parens: "Don't wrap function literals in parens.",
    +
      321.      1    unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.",
    +
      322.      1    unexpected_statement_a: (
    +
      323.      1        "Unexpected statement '{a}' in expression position."
    +
      324.      1    ),
    +
      325.      1    unexpected_trailing_space: "Unexpected trailing space.",
    +
      326.      1    unexpected_typeof_a: (
    +
      327.      1        "Unexpected 'typeof'. Use '===' to compare directly with {a}."
    +
      328.      1    ),
    +
      329.      1    uninitialized_a: "Uninitialized '{a}'.",
    +
      330.      1    unreachable_a: "Unreachable '{a}'.",
    +
      331.      1    unregistered_property_a: "Unregistered property name '{a}'.",
    +
      332.      1    unused_a: "Unused '{a}'.",
    +
      333.      1    use_double: "Use double quotes, not single quotes.",
    +
      334.      1    use_open: (
    +
      335.      1        "Wrap a ternary expression in parens, "
    +
      336.      1        + "with a line break after the left paren."
    +
      337.      1    ),
    +
      338.      1    use_spaces: "Use spaces, not tabs.",
    +
      339.      1    var_loop: "Don't declare variables in a loop.",
    +
      340.      1    var_switch: "Don't declare variables in a switch.",
    +
      341.      1    weird_condition_a: "Weird condition '{a}'.",
    +
      342.      1    weird_expression_a: "Weird expression '{a}'.",
    +
      343.      1    weird_loop: "Weird loop.",
    +
      344.      1    weird_relation_a: "Weird relation '{a}'.",
    +
      345.      1    wrap_condition: "Wrap the condition in parens.",
    +
      346.      1    wrap_immediate: (
    +
      347.      1        "Wrap an immediate function invocation in parentheses to assist "
    +
      348.      1        + "the reader in understanding that the expression is the result "
    +
      349.      1        + "of a function, and not the function itself."
    +
      350.      1    ),
    +
      351.      1    wrap_parameter: "Wrap the parameter in parens.",
    +
      352.      1    wrap_regexp: "Wrap this regexp in parens to avoid confusion.",
    +
      353.      1    wrap_unary: "Wrap the unary expression in parens."
    +
      354.      1};
    +
      355.      1
    +
      356.      1// Regular expression literals:
    +
      357.      1
    +
      358.     12function tag_regexp(strings) {
    +
      359.     12    return new RegExp(strings.raw[0].replace(/\s/g, ""));
    +
      360.     12}
    +
      361.      1
    +
      362.      1// supplant {variables}
    +
      363.      1const rx_supplant = /\{([^{}]*)\}/g;
    +
      364.      1// carriage return, carriage return linefeed, or linefeed
    +
      365.      1const rx_crlf = tag_regexp `
    +
      366.      1      \n
    +
      367.      1    | \r \n?
    +
      368.      1`;
    +
      369.      1// identifier
    +
      370.      1const rx_identifier = tag_regexp ` ^(
    +
      371.      1    [ a-z A-Z _ $ ]
    +
      372.      1    [ a-z A-Z 0-9 _ $ ]*
    +
      373.      1)$`;
    +
      374.      1const rx_module = tag_regexp ` ^ [ a-z A-Z 0-9 _ $ : . @ \- \/ ]+ $ `;
    +
      375.      1const rx_bad_property = tag_regexp `
    +
      376.      1    ^_
    +
      377.      1  | \$
    +
      378.      1  | Sync $
    +
      379.      1  | _ $
    +
      380.      1`;
    +
      381.      1// star slash
    +
      382.      1const rx_star_slash = tag_regexp ` \* \/ `;
    +
      383.      1// slash star
    +
      384.      1const rx_slash_star = tag_regexp ` \/ \* `;
    +
      385.      1// slash star or ending slash
    +
      386.      1const rx_slash_star_or_slash = tag_regexp ` \/ \* | \/ $ `;
    +
      387.      1// uncompleted work comment
    +
      388.      1const rx_todo = tag_regexp ` \b (?:
    +
      389.      1    todo
    +
      390.      1  | TO \s? DO
    +
      391.      1  | HACK
    +
      392.      1) \b `;
    +
      393.      1// tab
    +
      394.      1const rx_tab = /\t/g;
    +
      395.      1// directive
    +
      396.      1const rx_directive = tag_regexp ` ^ (
    +
      397.      1    jslint
    +
      398.      1  | property
    +
      399.      1  | global
    +
      400.      1) \s+ ( .* ) $ `;
    +
      401.      1const rx_directive_part = tag_regexp ` ^ (
    +
      402.      1    [ a-z A-Z $ _ ] [ a-z A-Z 0-9 $ _ ]*
    +
      403.      1) (?:
    +
      404.      1    : \s* ( true | false )
    +
      405.      1)? ,? \s* ( .* ) $ `;
    +
      406.      1// token
    +
      407.      1const rx_token = tag_regexp ` ^ (
    +
      408.      1    (\s+)
    +
      409.      1  | (
    +
      410.      1      [ a-z A-Z _ $ ]
    +
      411.      1      [ a-z A-Z 0-9 _ $ ]*
    +
      412.      1    )
    +
      413.      1  | [
    +
      414.      1      ( ) { } \[ \] , : ; ' " ~ \`
    +
      415.      1  ]
    +
      416.      1  | \? [ ? . ]?
    +
      417.      1  | = (?:
    +
      418.      1        = =?
    +
      419.      1      | >
    +
      420.      1    )?
    +
      421.      1  | \.+
    +
      422.      1  | \* [ * \/ = ]?
    +
      423.      1  | \/ [ * \/ ]?
    +
      424.      1  | \+ [ = + ]?
    +
      425.      1  | - [ = \- ]?
    +
      426.      1  | [ \^ % ] =?
    +
      427.      1  | & [ & = ]?
    +
      428.      1  | \| [ | = ]?
    +
      429.      1  | >{1,3} =?
    +
      430.      1  | < <? =?
    +
      431.      1  | ! (?:
    +
      432.      1        !
    +
      433.      1      | = =?
    +
      434.      1    )?
    +
      435.      1  | (
    +
      436.      1        0
    +
      437.      1      | [ 1-9 ] [ 0-9 ]*
    +
      438.      1    )
    +
      439.      1) ( .* ) $ `;
    +
      440.      1const rx_digits = /^[0-9]*/;
    +
      441.      1const rx_hexs = /^[0-9A-F]*/i;
    +
      442.      1const rx_octals = /^[0-7]*/;
    +
      443.      1const rx_bits = /^[01]*/;
    +
      444.      1// mega
    +
      445.      1const rx_mega = /[`\\]|\$\{/;
    +
      446.      1// JSON number
    +
      447.      1const rx_JSON_number = tag_regexp ` ^
    +
      448.      1    -?
    +
      449.      1    (?: 0 | [ 1-9 ] \d* )
    +
      450.      1    (?: \. \d* )?
    +
      451.      1    (?: [ e E ] [ \- + ]? \d+ )?
    +
      452.      1$ `;
    +
      453.      1// initial cap
    +
      454.      1const rx_cap = /^[A-Z]/;
    +
      455.      1
    +
      456.     13function is_letter(string) {
    +
      457.     13    return (
    +
      458.      4        (string >= "a" && string <= "z\uffff")
    +
      459.      0        || (string >= "A" && string <= "Z\uffff")
    +
      460.     13    );
    +
      461.     13}
    +
      462.      1
    +
      463.      0function supplant(string, object) {
    +
      464.      0    return string.replace(rx_supplant, function (found, filling) {
    +
      465.      0        const replacement = object[filling];
    +
      466.      0        return (
    +
      467.      0            replacement !== undefined
    +
      468.      0            ? replacement
    +
      469.      0            : found
    +
      470.      0        );
    +
      471.      0    });
    +
      472.      0}
    +
      473.      1
    +
      474.      1let anon;               // The guessed name for anonymous functions.
    +
      475.      1let blockage;           // The current block.
    +
      476.      1let block_stack;        // The stack of blocks.
    +
      477.      1let declared_globals;   // The object containing the global declarations.
    +
      478.      1let directives;         // The directive comments.
    +
      479.      1let directive_mode;     // true if directives are still allowed.
    +
      480.      1let early_stop;         // true if JSLint cannot finish.
    +
      481.      1let exports;            // The exported names and values.
    +
      482.      1let froms;              // The array collecting all import-from strings.
    +
      483.      1let fudge;              // true if the natural numbers start with 1.
    +
      484.      1let functionage;        // The current function.
    +
      485.      1let functions;          // The array containing all of the functions.
    +
      486.      1let global;             // The global object; the outermost context.
    +
      487.      1let json_mode;          // true if parsing JSON.
    +
      488.      1let lines;              // The array containing source lines.
    +
      489.      1let mega_mode;          // true if currently parsing a megastring literal.
    +
      490.      1let module_mode;        // true if import or export was used.
    +
      491.      1let next_token;         // The next token to be examined in the parse.
    +
      492.      1let option;             // The options parameter.
    +
      493.      1let property;           // The object containing the tallied property names.
    +
      494.      1let shebang;            // true if a #! was seen on the first line.
    +
      495.      1let stack;              // The stack of functions.
    +
      496.      1let syntax;             // The object containing the parser.
    +
      497.      1let token;              // The current token being examined in the parse.
    +
      498.      1let token_nr;           // The number of the next token.
    +
      499.      1let tokens;             // The array of tokens.
    +
      500.      1let tenure;             // The predefined property registry.
    +
      501.      1let tree;               // The abstract parse tree.
    +
      502.      1let var_mode;           // "var" if using var; "let" if using let.
    +
      503.      1let warnings;           // The array collecting all generated warnings.
    +
      504.      1
    +
      505.      1// Error reportage functions:
    +
      506.      1
    +
      507.      0function artifact(the_token) {
    +
      508.      0
    +
      509.      0// Return a string representing an artifact.
    +
      510.      0
    +
      511.      0    if (the_token === undefined) {
    +
      512.      0        the_token = next_token;
    +
      513.      0    }
    +
      514.      0    return (
    +
      515.      0        (the_token.id === "(string)" || the_token.id === "(number)")
    +
      516.      0        ? String(the_token.value)
    +
      517.      0        : the_token.id
    +
      518.      0    );
    +
      519.      0}
    +
      520.      1
    +
      521.      0function artifact_line(the_token) {
    +
      522.      0
    +
      523.      0// Return the fudged line number of an artifact.
    +
      524.      0
    +
      525.      0    if (the_token === undefined) {
    +
      526.      0        the_token = next_token;
    +
      527.      0    }
    +
      528.      0    return the_token.line + fudge;
    +
      529.      0}
    +
      530.      1
    +
      531.      0function artifact_column(the_token) {
    +
      532.      0
    +
      533.      0// Return the fudged column number of an artifact.
    +
      534.      0
    +
      535.      0    if (the_token === undefined) {
    +
      536.      0        the_token = next_token;
    +
      537.      0    }
    +
      538.      0    return the_token.from + fudge;
    +
      539.      0}
    +
      540.      1
    +
      541.      0function warn_at(code, line, column, a, b, c, d) {
    +
      542.      0
    +
      543.      0// Report an error at some line and column of the program. The warning object
    +
      544.      0// resembles an exception.
    +
      545.      0
    +
      546.      0    const warning = {         // ~~
    +
      547.      0        name: "JSLintError",
    +
      548.      0        column,
    +
      549.      0        line,
    +
      550.      0        code
    +
      551.      0    };
    +
      552.      0    if (a !== undefined) {
    +
      553.      0        warning.a = a;
    +
      554.      0    }
    +
      555.      0    if (b !== undefined) {
    +
      556.      0        warning.b = b;
    +
      557.      0    }
    +
      558.      0    if (c !== undefined) {
    +
      559.      0        warning.c = c;
    +
      560.      0    }
    +
      561.      0    if (d !== undefined) {
    +
      562.      0        warning.d = d;
    +
      563.      0    }
    +
      564.      0    warning.message = supplant(bundle[code] || code, warning);
    +
      565.      0    warnings.push(warning);
    +
      566.      0    return warning;
    +
      567.      0}
    +
      568.      1
    +
      569.      0function stop_at(code, line, column, a, b, c, d) {
    +
      570.      0
    +
      571.      0// Same as warn_at, except that it stops the analysis.
    +
      572.      0
    +
      573.      0    throw warn_at(code, line, column, a, b, c, d);
    +
      574.      0}
    +
      575.      1
    +
      576.      0function warn(code, the_token, a, b, c, d) {
    +
      577.      0
    +
      578.      0// Same as warn_at, except the warning will be associated with a specific token.
    +
      579.      0// If there is already a warning on this token, suppress the new one. It is
    +
      580.      0// likely that the first warning will be the most meaningful.
    +
      581.      0
    +
      582.      0    if (the_token === undefined) {
    +
      583.      0        the_token = next_token;
    +
      584.      0    }
    +
      585.      0    if (the_token.warning === undefined) {
    +
      586.      0        the_token.warning = warn_at(
    +
      587.      0            code,
    +
      588.      0            the_token.line,
    +
      589.      0            the_token.from,
    +
      590.      0            a || artifact(the_token),
    +
      591.      0            b,
    +
      592.      0            c,
    +
      593.      0            d
    +
      594.      0        );
    +
      595.      0        return the_token.warning;
    +
      596.      0    }
    +
      597.      0}
    +
      598.      1
    +
      599.      0function stop(code, the_token, a, b, c, d) {
    +
      600.      0
    +
      601.      0// Similar to warn and stop_at. If the token already had a warning, that
    +
      602.      0// warning will be replaced with this new one. It is likely that the stopping
    +
      603.      0// warning will be the more meaningful.
    +
      604.      0
    +
      605.      0    if (the_token === undefined) {
    +
      606.      0        the_token = next_token;
    +
      607.      0    }
    +
      608.      0    delete the_token.warning;
    +
      609.      0    throw warn(code, the_token, a, b, c, d);
    +
      610.      0}
    +
      611.      1
    +
      612.      1// Tokenize:
    +
      613.      1
    +
      614.      1function tokenize(source) {
    +
      615.      1
    +
      616.      1// tokenize takes a source and produces from it an array of token objects.
    +
      617.      1// JavaScript is notoriously difficult to tokenize because of the horrible
    +
      618.      1// interactions between automatic semicolon insertion, regular expression
    +
      619.      1// literals, and now megastring literals. JSLint benefits from eliminating
    +
      620.      1// automatic semicolon insertion and nested megastring literals, which allows
    +
      621.      1// full tokenization to precede parsing.
    +
      622.      1
    +
      623.      1// If the source is not an array, then it is split into lines at the
    +
      624.      1// carriage return/linefeed.
    +
      625.      1
    +
      626.      1    lines = (
    +
      627.      1        Array.isArray(source)
    +
      628.      0        ? source
    +
      629.      1        : source.split(rx_crlf)
    +
      630.      1    );
    +
      631.      1    tokens = [];
    +
      632.      1
    +
      633.      1    let char;                   // a popular character
    +
      634.      1    let column = 0;             // the column number of the next character
    +
      635.      1    let first;                  // the first token
    +
      636.      1    let from;                   // the starting column number of the token
    +
      637.      1    let line = -1;              // the line number of the next character
    +
      638.      1    let nr = 0;                 // the next token number
    +
      639.      1    let previous = global;      // the previous token including comments
    +
      640.      1    let prior = global;         // the previous token excluding comments
    +
      641.      1    let mega_from;              // the starting column of megastring
    +
      642.      1    let mega_line;              // the starting line of megastring
    +
      643.      1    let regexp_seen;            // regular expression literal seen on this line
    +
      644.      1    let snippet;                // a piece of string
    +
      645.      1    let source_line = "";       // the remaining line source string
    +
      646.      1    let whole_line = "";        // the whole line source string
    +
      647.      1
    +
      648.      0    if (lines[0].startsWith("#!")) {
    +
      649.      0        line = 0;
    +
      650.      0        shebang = true;
    +
      651.      0    }
    +
      652.      1
    +
      653.   5020    function next_line() {
    +
      654.   5020
    +
      655.   5020// Put the next line of source in source_line. If the line contains tabs,
    +
      656.   5020// replace them with spaces and give a warning. Also warn if the line contains
    +
      657.   5020// unsafe characters or is too damn long.
    +
      658.   5020
    +
      659.   5020        let at;
    +
      660.   5020        if (
    +
      661.   5020            !option.long
    +
      662.   5020            && whole_line.length > 80
    +
      663.      0            && !json_mode
    +
      664.      0            && first
    +
      665.      0            && !regexp_seen
    +
      666.      0        ) {
    +
      667.      0            warn_at("too_long", line, 80);
    +
      668.      0        }
    +
      669.   5020        column = 0;
    +
      670.   5020        line += 1;
    +
      671.   5020        regexp_seen = false;
    +
      672.   5020        source_line = lines[line];
    +
      673.    432        whole_line = source_line || "";
    +
      674.   5019        if (source_line !== undefined) {
    +
      675.   5019            at = source_line.search(rx_tab);
    +
      676.      0            if (at >= 0) {
    +
      677.      0                if (!option.white) {
    +
      678.      0                    warn_at("use_spaces", line, at + 1);
    +
      679.      0                }
    +
      680.      0                source_line = source_line.replace(rx_tab, " ");
    +
      681.      0            }
    +
      682.      0            if (!option.white && source_line.slice(-1) === " ") {
    +
      683.      0                warn_at(
    +
      684.      0                    "unexpected_trailing_space",
    +
      685.      0                    line,
    +
      686.      0                    source_line.length - 1
    +
      687.      0                );
    +
      688.      0            }
    +
      689.   5019        }
    +
      690.   5020        return source_line;
    +
      691.   5020    }
    +
      692.      1
    +
      693.      1// Most tokens, including the identifiers, operators, and punctuators, can be
    +
      694.      1// found with a regular expression. Regular expressions cannot correctly match
    +
      695.      1// regular expression literals, so we will match those the hard way. String
    +
      696.      1// literals and number literals can be matched by regular expressions, but they
    +
      697.      1// don't provide good warnings. The functions snip, next_char, back_char,
    +
      698.      1// some_digits, and escape help in the parsing of literals.
    +
      699.      1
    +
      700.   1919    function snip() {
    +
      701.   1919
    +
      702.   1919// Remove the last character from snippet.
    +
      703.   1919
    +
      704.   1919        snippet = snippet.slice(0, -1);
    +
      705.   1919    }
    +
      706.      1
    +
      707.  13386    function next_char(match) {
    +
      708.  13386
    +
      709.  13386// Get the next character from the source line. Remove it from the source_line,
    +
      710.  13386// and append it to the snippet. Optionally check that the previous character
    +
      711.  13386// matched an expected value.
    +
      712.  13386
    +
      713.      0        if (match !== undefined && char !== match) {
    +
      714.      0            return stop_at(
    +
      715.      0                (
    +
      716.      0                    char === ""
    +
      717.      0                    ? "expected_a"
    +
      718.      0                    : "expected_a_b"
    +
      719.      0                ),
    +
      720.      0                line,
    +
      721.      0                column - 1,
    +
      722.      0                match,
    +
      723.      0                char
    +
      724.      0            );
    +
      725.      0        }
    +
      726.  13376        if (source_line) {
    +
      727.  13376            char = source_line[0];
    +
      728.  13376            source_line = source_line.slice(1);
    +
      729.  13376            snippet += char;
    +
      730.  13376        } else {
    +
      731.     10            char = "";
    +
      732.     10            snippet += " ";
    +
      733.     10        }
    +
      734.  13386        column += 1;
    +
      735.  13386        return char;
    +
      736.  13386    }
    +
      737.      1
    +
      738.    343    function back_char() {
    +
      739.    343
    +
      740.    343// Back up one character by moving a character from the end of the snippet to
    +
      741.    343// the front of the source_line.
    +
      742.    343
    +
      743.    343        if (snippet) {
    +
      744.    343            char = snippet.slice(-1);
    +
      745.    343            source_line = char + source_line;
    +
      746.    343            column -= 1;
    +
      747.    343            snip();
    +
      748.      0        } else {
    +
      749.      0            char = "";
    +
      750.      0        }
    +
      751.    343        return char;
    +
      752.    343    }
    +
      753.      1
    +
      754.      2    function some_digits(rx, quiet) {
    +
      755.      2        const digits = source_line.match(rx)[0];
    +
      756.      2        const length = digits.length;
    +
      757.      0        if (!quiet && length === 0) {
    +
      758.      0            warn_at("expected_digits_after_a", line, column, snippet);
    +
      759.      0        }
    +
      760.      2        column += length;
    +
      761.      2        source_line = source_line.slice(length);
    +
      762.      2        snippet += digits;
    +
      763.      2        next_char();
    +
      764.      2        return length;
    +
      765.      2    }
    +
      766.      1
    +
      767.     31    function escape(extra) {
    +
      768.     31        next_char("\\");
    +
      769.     17        if (escapeable[char] === true) {
    +
      770.     17            return next_char();
    +
      771.     17        }
    +
      772.      0        if (char === "") {
    +
      773.      0            return stop_at("unclosed_string", line, column);
    +
      774.      0        }
    +
      775.     14        if (char === "u") {
    +
      776.      0            if (next_char("u") === "{") {
    +
      777.      0                if (json_mode) {
    +
      778.      0                    warn_at("unexpected_a", line, column - 1, char);
    +
      779.      0                }
    +
      780.      0                if (some_digits(rx_hexs) > 5) {
    +
      781.      0                    warn_at("too_many_digits", line, column - 1);
    +
      782.      0                }
    +
      783.      0                if (next_char() !== "}") {
    +
      784.      0                    stop_at("expected_a_before_b", line, column, "}", char);
    +
      785.      0                }
    +
      786.      0                return next_char();
    +
      787.      0            }
    +
      788.      2            back_char();
    +
      789.      0            if (some_digits(rx_hexs, true) < 4) {
    +
      790.      0                warn_at("expected_four_digits", line, column - 1);
    +
      791.      0            }
    +
      792.      2            return;
    +
      793.     12        }
    +
      794.     12        if (extra && extra.indexOf(char) >= 0) {
    +
      795.     12            return next_char();
    +
      796.      0        }
    +
      797.      0        warn_at("unexpected_a_before_b", line, column - 2, "\\", char);
    +
      798.      0    }
    +
      799.      1
    +
      800.  22338    function make(id, value, identifier) {
    +
      801.  22338
    +
      802.  22338// Make the token object and append it to the tokens list.
    +
      803.  22338
    +
      804.  22338        const the_token = {
    +
      805.  22338            from,
    +
      806.  22338            id,
    +
      807.  22338            identifier: Boolean(identifier),
    +
      808.  22338            line,
    +
      809.  22338            nr,
    +
      810.  22338            thru: column
    +
      811.  22338        };
    +
      812.  22338        tokens[nr] = the_token;
    +
      813.  22338        nr += 1;
    +
      814.  22338
    +
      815.  22338// Directives must appear before the first statement.
    +
      816.  22338
    +
      817.  21914        if (id !== "(comment)" && id !== ";") {
    +
      818.  20115            directive_mode = false;
    +
      819.  20115        }
    +
      820.  22338
    +
      821.  22338// If the token is to have a value, give it one.
    +
      822.  22338
    +
      823.   2344        if (value !== undefined) {
    +
      824.   2344            the_token.value = value;
    +
      825.   2344        }
    +
      826.  22338
    +
      827.  22338// If this token is an identifier that touches a preceding number, or
    +
      828.  22338// a "/", comment, or regular expression literal that touches a preceding
    +
      829.  22338// comment or regular expression literal, then give a missing space warning.
    +
      830.  22338// This warning is not suppressed by option.white.
    +
      831.  22338
    +
      832.  22338        if (
    +
      833.  22338            previous.line === line
    +
      834.  17837            && previous.thru === from
    +
      835.  10996            && (id === "(comment)" || id === "(regexp)" || id === "/")
    +
      836.      2            && (previous.id === "(comment)" || previous.id === "(regexp)")
    +
      837.      0        ) {
    +
      838.      0            warn(
    +
      839.      0                "expected_space_a_b",
    +
      840.      0                the_token,
    +
      841.      0                artifact(previous),
    +
      842.      0                artifact(the_token)
    +
      843.      0            );
    +
      844.      0        }
    +
      845.      0        if (previous.id === "." && id === "(number)") {
    +
      846.      0            warn("expected_a_before_b", previous, "0", ".");
    +
      847.      0        }
    +
      848.   1327        if (prior.id === "." && the_token.identifier) {
    +
      849.   1327            the_token.dot = true;
    +
      850.   1327        }
    +
      851.  22338
    +
      852.  22338// The previous token is used to detect adjacency problems.
    +
      853.  22338
    +
      854.  22338        previous = the_token;
    +
      855.  22338
    +
      856.  22338// The prior token is a previous token that was not a comment. The prior token
    +
      857.  22338// is used to disambiguate "/", which can mean division or regular expression
    +
      858.  22338// literal.
    +
      859.  22338
    +
      860.  21914        if (previous.id !== "(comment)") {
    +
      861.  21914            prior = previous;
    +
      862.  21914        }
    +
      863.  22338        return the_token;
    +
      864.  22338    }
    +
      865.      1
    +
      866.    211    function parse_directive(the_comment, body) {
    +
      867.    211
    +
      868.    211// JSLint recognizes three directives that can be encoded in comments. This
    +
      869.    211// function processes one item, and calls itself recursively to process the
    +
      870.    211// next one.
    +
      871.    211
    +
      872.    211        const result = body.match(rx_directive_part);
    +
      873.    210        if (result) {
    +
      874.    210            let allowed;
    +
      875.    210            const name = result[1];
    +
      876.    210            const value = result[2];
    +
      877.      0            if (the_comment.directive === "jslint") {
    +
      878.      0                allowed = allowed_option[name];
    +
      879.      0                if (
    +
      880.      0                    typeof allowed === "boolean"
    +
      881.      0                    || typeof allowed === "object"
    +
      882.      0                ) {
    +
      883.      0                    if (
    +
      884.      0                        value === ""
    +
      885.      0                        || value === "true"
    +
      886.      0                        || value === undefined
    +
      887.      0                    ) {
    +
      888.      0                        option[name] = true;
    +
      889.      0                        if (Array.isArray(allowed)) {
    +
      890.      0                            populate(allowed, declared_globals, false);
    +
      891.      0                        }
    +
      892.      0                    } else if (value === "false") {
    +
      893.      0                        option[name] = false;
    +
      894.      0                    } else {
    +
      895.      0                        warn("bad_option_a", the_comment, name + ":" + value);
    +
      896.      0                    }
    +
      897.      0                } else {
    +
      898.      0                    warn("bad_option_a", the_comment, name);
    +
      899.      0                }
    +
      900.      0            } else if (the_comment.directive === "property") {
    +
      901.    210                if (tenure === undefined) {
    +
      902.    210                    tenure = empty();
    +
      903.    210                }
    +
      904.    210                tenure[name] = true;
    +
      905.      0            } else if (the_comment.directive === "global") {
    +
      906.      0                if (value) {
    +
      907.      0                    warn("bad_option_a", the_comment, name + ":" + value);
    +
      908.      0                }
    +
      909.      0                declared_globals[name] = false;
    +
      910.      0                module_mode = the_comment;
    +
      911.      0            }
    +
      912.    210            return parse_directive(the_comment, result[3]);
    +
      913.    210        }
    +
      914.      0        if (body) {
    +
      915.      0            return stop("bad_directive_a", the_comment, body);
    +
      916.      0        }
    +
      917.    211    }
    +
      918.      1
    +
      919.    424    function comment(snippet) {
    +
      920.    424
    +
      921.    424// Make a comment object. Comments are not allowed in JSON text. Comments can
    +
      922.    424// include directives and notices of incompletion.
    +
      923.    424
    +
      924.    424        const the_comment = make("(comment)", snippet);
    +
      925.      1        if (Array.isArray(snippet)) {
    +
      926.      1            snippet = snippet.join(" ");
    +
      927.      1        }
    +
      928.      0        if (!option.devel && rx_todo.test(snippet)) {
    +
      929.      0            warn("todo_comment", the_comment);
    +
      930.      0        }
    +
      931.    424        const result = snippet.match(rx_directive);
    +
      932.      1        if (result) {
    +
      933.      0            if (!directive_mode) {
    +
      934.      0                warn_at("misplaced_directive_a", line, from, result[1]);
    +
      935.      0            } else {
    +
      936.      1                the_comment.directive = result[1];
    +
      937.      1                parse_directive(the_comment, result[2]);
    +
      938.      1            }
    +
      939.      1            directives.push(the_comment);
    +
      940.      1        }
    +
      941.    424        return the_comment;
    +
      942.    424    }
    +
      943.      1
    +
      944.      9    function regexp() {
    +
      945.      9
    +
      946.      9// Parse a regular expression literal.
    +
      947.      9
    +
      948.      9        let multi_mode = false;
    +
      949.      9        let result;
    +
      950.      9        let value;
    +
      951.      9        regexp_seen = true;
    +
      952.      9
    +
      953.     20        function quantifier() {
    +
      954.     20
    +
      955.     20// Match an optional quantifier.
    +
      956.     20
    +
      957.     15            if (char === "?" || char === "*" || char === "+") {
    +
      958.      5                next_char();
    +
      959.      0            } else if (char === "{") {
    +
      960.      0                if (some_digits(rx_digits, true) === 0) {
    +
      961.      0                    warn_at("expected_a_before_b", line, column, "0", ",");
    +
      962.      0                }
    +
      963.      0                if (char === ",") {
    +
      964.      0                    some_digits(rx_digits, true);
    +
      965.      0                }
    +
      966.      0                next_char("}");
    +
      967.      0            } else {
    +
      968.     15                return;
    +
      969.     15            }
    +
      970.      0            if (char === "?") {
    +
      971.      0                next_char("?");
    +
      972.      0            }
    +
      973.     20        }
    +
      974.      9
    +
      975.     23        function subklass() {
    +
      976.     23
    +
      977.     23// Match a character in a character class.
    +
      978.     23
    +
      979.      1            if (char === "\\") {
    +
      980.      1                escape("BbDdSsWw-[]^");
    +
      981.      1                return true;
    +
      982.     22            }
    +
      983.     22            if (
    +
      984.     22                char === ""
    +
      985.     22                || char === "["
    +
      986.     22                || char === "]"
    +
      987.     15                || char === "/"
    +
      988.     15                || char === "^"
    +
      989.     15                || char === "-"
    +
      990.      7            ) {
    +
      991.      7                return false;
    +
      992.     15            }
    +
      993.      0            if (char === " ") {
    +
      994.      0                warn_at("expected_a_b", line, column, "\\u0020", " ");
    +
      995.      0            } else if (char === "`" && mega_mode) {
    +
      996.      0                warn_at("unexpected_a", line, column, "`");
    +
      997.      0            }
    +
      998.     15            next_char();
    +
      999.     15            return true;
    +
     1000.     15        }
    +
     1001.      9
    +
     1002.     18        function ranges() {
    +
     1003.     18
    +
     1004.     18// Match a range of subclasses.
    +
     1005.     18
    +
     1006.     11            if (subklass()) {
    +
     1007.     11                if (char === "-") {
    +
     1008.     11                    next_char("-");
    +
     1009.      0                    if (!subklass()) {
    +
     1010.      0                        return stop_at(
    +
     1011.      0                            "unexpected_a",
    +
     1012.      0                            line,
    +
     1013.      0                            column - 1,
    +
     1014.      0                            "-"
    +
     1015.      0                        );
    +
     1016.      0                    }
    +
     1017.     11                }
    +
     1018.     11                return ranges();
    +
     1019.     11            }
    +
     1020.     18        }
    +
     1021.      9
    +
     1022.      7        function klass() {
    +
     1023.      7
    +
     1024.      7// Match a class.
    +
     1025.      7
    +
     1026.      7            next_char("[");
    +
     1027.      1            if (char === "^") {
    +
     1028.      1                next_char("^");
    +
     1029.      1            }
    +
     1030.      7            (function classy() {
    +
     1031.      7                ranges();
    +
     1032.      0                if (char !== "]" && char !== "") {
    +
     1033.      0                    warn_at(
    +
     1034.      0                        "expected_a_before_b",
    +
     1035.      0                        line,
    +
     1036.      0                        column,
    +
     1037.      0                        "\\",
    +
     1038.      0                        char
    +
     1039.      0                    );
    +
     1040.      0                    next_char();
    +
     1041.      0                    return classy();
    +
     1042.      0                }
    +
     1043.      7            }());
    +
     1044.      7            next_char("]");
    +
     1045.      7        }
    +
     1046.      9
    +
     1047.     10        function choice() {
    +
     1048.     10
    +
     1049.      1            function group() {
    +
     1050.      1
    +
     1051.      1// Match a group that starts with left paren.
    +
     1052.      1
    +
     1053.      1                next_char("(");
    +
     1054.      0                if (char === "?") {
    +
     1055.      0                    next_char("?");
    +
     1056.      0                    if (char === "=" || char === "!") {
    +
     1057.      0                        next_char();
    +
     1058.      0                    } else {
    +
     1059.      0                        next_char(":");
    +
     1060.      0                    }
    +
     1061.      0                } else if (char === ":") {
    +
     1062.      0                    warn_at("expected_a_before_b", line, column, "?", ":");
    +
     1063.      0                }
    +
     1064.      1                choice();
    +
     1065.      1                next_char(")");
    +
     1066.      1            }
    +
     1067.     10
    +
     1068.     30            function factor() {
    +
     1069.     30                if (
    +
     1070.     30                    char === ""
    +
     1071.     30                    || char === "/"
    +
     1072.     21                    || char === "]"
    +
     1073.     21                    || char === ")"
    +
     1074.     10                ) {
    +
     1075.     10                    return false;
    +
     1076.     20                }
    +
     1077.     20                if (char === "(") {
    +
     1078.      1                    group();
    +
     1079.      1                    return true;
    +
     1080.     19                }
    +
     1081.     19                if (char === "[") {
    +
     1082.      7                    klass();
    +
     1083.      7                    return true;
    +
     1084.     12                }
    +
     1085.     12                if (char === "\\") {
    +
     1086.      6                    escape("BbDdSsWw^${}[]():=!.|*+?");
    +
     1087.      6                    return true;
    +
     1088.      6                }
    +
     1089.      6                if (
    +
     1090.      6                    char === "?"
    +
     1091.      6                    || char === "+"
    +
     1092.      6                    || char === "*"
    +
     1093.      6                    || char === "}"
    +
     1094.      6                    || char === "{"
    +
     1095.      0                ) {
    +
     1096.      0                    warn_at(
    +
     1097.      0                        "expected_a_before_b",
    +
     1098.      0                        line,
    +
     1099.      0                        column - 1,
    +
     1100.      0                        "\\",
    +
     1101.      0                        char
    +
     1102.      0                    );
    +
     1103.      0                } else if (char === "`") {
    +
     1104.      0                    if (mega_mode) {
    +
     1105.      0                        warn_at("unexpected_a", line, column - 1, "`");
    +
     1106.      0                    }
    +
     1107.      0                } else if (char === " ") {
    +
     1108.      0                    warn_at(
    +
     1109.      0                        "expected_a_b",
    +
     1110.      0                        line,
    +
     1111.      0                        column - 1,
    +
     1112.      0                        "\\s",
    +
     1113.      0                        " "
    +
     1114.      0                    );
    +
     1115.      0                } else if (char === "$") {
    +
     1116.      0                    if (source_line[0] !== "/") {
    +
     1117.      0                        multi_mode = true;
    +
     1118.      0                    }
    +
     1119.      0                } else if (char === "^") {
    +
     1120.      0                    if (snippet !== "^") {
    +
     1121.      0                        multi_mode = true;
    +
     1122.      0                    }
    +
     1123.      6                }
    +
     1124.      6                next_char();
    +
     1125.      6                return true;
    +
     1126.      6            }
    +
     1127.     10
    +
     1128.     30            function sequence(follow) {
    +
     1129.     20                if (factor()) {
    +
     1130.     20                    quantifier();
    +
     1131.     20                    return sequence(true);
    +
     1132.     20                }
    +
     1133.      0                if (!follow) {
    +
     1134.      0                    warn_at("expected_regexp_factor_a", line, column, char);
    +
     1135.      0                }
    +
     1136.     30            }
    +
     1137.     10
    +
     1138.     10// Match a choice (a sequence that can be followed by | and another choice).
    +
     1139.     10
    +
     1140.     10            sequence();
    +
     1141.      0            if (char === "|") {
    +
     1142.      0                next_char("|");
    +
     1143.      0                return choice();
    +
     1144.      0            }
    +
     1145.     10        }
    +
     1146.      9
    +
     1147.      9// Scan the regexp literal. Give a warning if the first character is = because
    +
     1148.      9// /= looks like a division assignment operator.
    +
     1149.      9
    +
     1150.      9        snippet = "";
    +
     1151.      9        next_char();
    +
     1152.      0        if (char === "=") {
    +
     1153.      0            warn_at("expected_a_before_b", line, column, "\\", "=");
    +
     1154.      0        }
    +
     1155.      9        choice();
    +
     1156.      9
    +
     1157.      9// Make sure there is a closing slash.
    +
     1158.      9
    +
     1159.      9        snip();
    +
     1160.      9        value = snippet;
    +
     1161.      9        next_char("/");
    +
     1162.      9
    +
     1163.      9// Process dangling flag letters.
    +
     1164.      9
    +
     1165.      9        const allowed = {
    +
     1166.      9            g: true,
    +
     1167.      9            i: true,
    +
     1168.      9            m: true,
    +
     1169.      9            u: true,
    +
     1170.      9            y: true
    +
     1171.      9        };
    +
     1172.      9        const flag = empty();
    +
     1173.     13        (function make_flag() {
    +
     1174.      4            if (is_letter(char)) {
    +
     1175.      0                if (allowed[char] !== true) {
    +
     1176.      0                    warn_at("unexpected_a", line, column, char);
    +
     1177.      0                }
    +
     1178.      4                allowed[char] = false;
    +
     1179.      4                flag[char] = true;
    +
     1180.      4                next_char();
    +
     1181.      4                return make_flag();
    +
     1182.      4            }
    +
     1183.     13        }());
    +
     1184.      9        back_char();
    +
     1185.      0        if (char === "/" || char === "*") {
    +
     1186.      0            return stop_at("unexpected_a", line, from, char);
    +
     1187.      0        }
    +
     1188.      9        result = make("(regexp)", char);
    +
     1189.      9        result.flag = flag;
    +
     1190.      9        result.value = value;
    +
     1191.      0        if (multi_mode && !flag.m) {
    +
     1192.      0            warn_at("missing_m", line, column);
    +
     1193.      0        }
    +
     1194.      9        return result;
    +
     1195.      9    }
    +
     1196.      1
    +
     1197.   1567    function string(quote) {
    +
     1198.   1567
    +
     1199.   1567// Make a string token.
    +
     1200.   1567
    +
     1201.   1567        let the_token;
    +
     1202.   1567        snippet = "";
    +
     1203.   1567        next_char();
    +
     1204.   1567
    +
     1205.  12944        return (function next() {
    +
     1206.   1567            if (char === quote) {
    +
     1207.   1567                snip();
    +
     1208.   1567                the_token = make("(string)", snippet);
    +
     1209.   1567                the_token.quote = quote;
    +
     1210.   1567                return the_token;
    +
     1211.  11377            }
    +
     1212.      0            if (char === "") {
    +
     1213.      0                return stop_at("unclosed_string", line, column);
    +
     1214.      0            }
    +
     1215.  11377            if (char === "\\") {
    +
     1216.     24                escape(quote);
    +
     1217.  11353            } else if (char === "`") {
    +
     1218.      0                if (mega_mode) {
    +
     1219.      0                    warn_at("unexpected_a", line, column, "`");
    +
     1220.      0                }
    +
     1221.  11353                next_char("`");
    +
     1222.  11353            } else {
    +
     1223.  11353                next_char();
    +
     1224.  11377            }
    +
     1225.  11377            return next();
    +
     1226.  11377        }());
    +
     1227.   1567    }
    +
     1228.      1
    +
     1229.    181    function frack() {
    +
     1230.      0        if (char === ".") {
    +
     1231.      0            some_digits(rx_digits);
    +
     1232.      0        }
    +
     1233.      0        if (char === "E" || char === "e") {
    +
     1234.      0            next_char();
    +
     1235.      0            if (char !== "+" && char !== "-") {
    +
     1236.      0                back_char();
    +
     1237.      0            }
    +
     1238.      0            some_digits(rx_digits);
    +
     1239.      0        }
    +
     1240.    181    }
    +
     1241.      1
    +
     1242.    332    function number() {
    +
     1243.    151        if (snippet === "0") {
    +
     1244.    151            next_char();
    +
     1245.      0            if (char === ".") {
    +
     1246.      0                frack();
    +
     1247.      0            } else if (char === "b") {
    +
     1248.      0                some_digits(rx_bits);
    +
     1249.      0            } else if (char === "o") {
    +
     1250.      0                some_digits(rx_octals);
    +
     1251.      0            } else if (char === "x") {
    +
     1252.      0                some_digits(rx_hexs);
    +
     1253.      0            }
    +
     1254.    181        } else {
    +
     1255.    181            next_char();
    +
     1256.    181            frack();
    +
     1257.    181        }
    +
     1258.    332
    +
     1259.    332// If the next character after a number is a digit or letter, then something
    +
     1260.    332// unexpected is going on.
    +
     1261.    332
    +
     1262.    332        if (
    +
     1263.    164            (char >= "0" && char <= "9")
    +
     1264.      0            || (char >= "a" && char <= "z")
    +
     1265.    107            || (char >= "A" && char <= "Z")
    +
     1266.      0        ) {
    +
     1267.      0            return stop_at(
    +
     1268.      0                "unexpected_a_after_b",
    +
     1269.      0                line,
    +
     1270.      0                column - 1,
    +
     1271.      0                snippet.slice(-1),
    +
     1272.      0                snippet.slice(0, -1)
    +
     1273.      0            );
    +
     1274.      0        }
    +
     1275.    332        back_char();
    +
     1276.    332        return make("(number)", snippet);
    +
     1277.    332    }
    +
     1278.      1
    +
     1279.  32845    function lex() {
    +
     1280.  32845        let array;
    +
     1281.  32845        let i = 0;
    +
     1282.  32845        let j = 0;
    +
     1283.  32845        let last;
    +
     1284.  32845        let result;
    +
     1285.  32845        let the_token;
    +
     1286.  32845
    +
     1287.  32845// This should properly be a tail recursive function, but sadly, conformant
    +
     1288.  32845// implementations of ES6 are still rare. This is the ideal code:
    +
     1289.  32845
    +
     1290.  32845//      if (!source_line) {
    +
     1291.  32845//          source_line = next_line();
    +
     1292.  32845//          from = 0;
    +
     1293.  32845//          return (
    +
     1294.  32845//              source_line === undefined
    +
     1295.  32845//              ? (
    +
     1296.  32845//                  mega_mode
    +
     1297.  32845//                  ? stop_at("unclosed_mega", mega_line, mega_from)
    +
     1298.  32845//                  : make("(end)")
    +
     1299.  32845//              )
    +
     1300.  32845//              : lex()
    +
     1301.  32845//          );
    +
     1302.  32845//      }
    +
     1303.  32845
    +
     1304.  32845// Unfortunately, incompetent JavaScript engines will sometimes fail to execute
    +
     1305.  32845// it correctly. So for now, we do it the old fashioned way.
    +
     1306.  32845
    +
     1307.   4925        while (!source_line) {
    +
     1308.   4925            source_line = next_line();
    +
     1309.   4925            from = 0;
    +
     1310.   4925            if (source_line === undefined) {
    +
     1311.   4925                return (
    +
     1312.   4925                    mega_mode
    +
     1313.      0                    ? stop_at("unclosed_mega", mega_line, mega_from)
    +
     1314.   4925                    : make("(end)")
    +
     1315.   4925                );
    +
     1316.   4925            }
    +
     1317.  32844        }
    +
     1318.  32844
    +
     1319.  32844        from = column;
    +
     1320.  32844        result = source_line.match(rx_token);
    +
     1321.  32844
    +
     1322.  32844// result[1] token
    +
     1323.  32844// result[2] whitespace
    +
     1324.  32844// result[3] identifier
    +
     1325.  32844// result[4] number
    +
     1326.  32844// result[5] rest
    +
     1327.  32844
    +
     1328.      0        if (!result) {
    +
     1329.      0            return stop_at(
    +
     1330.      0                "unexpected_char_a",
    +
     1331.      0                line,
    +
     1332.      0                column,
    +
     1333.      0                source_line[0]
    +
     1334.      0            );
    +
     1335.      0        }
    +
     1336.  32844
    +
     1337.  32844        snippet = result[1];
    +
     1338.  32844        column += snippet.length;
    +
     1339.  32844        source_line = result[5];
    +
     1340.  32844
    +
     1341.  32844// Whitespace was matched. Call lex again to get more.
    +
     1342.  32844
    +
     1343.  32844        if (result[2]) {
    +
     1344.  10531            return lex();
    +
     1345.  22313        }
    +
     1346.  22313
    +
     1347.  22313// The token is an identifier.
    +
     1348.  22313
    +
     1349.  22313        if (result[3]) {
    +
     1350.   7484            return make(snippet, undefined, true);
    +
     1351.  14829        }
    +
     1352.  14829
    +
     1353.  14829// The token is a number.
    +
     1354.  14829
    +
     1355.  14829        if (result[4]) {
    +
     1356.    332            return number(snippet);
    +
     1357.  14497        }
    +
     1358.  14497
    +
     1359.  14497// The token is a string.
    +
     1360.  14497
    +
     1361.  14497        if (snippet === "\"") {
    +
     1362.   1567            return string(snippet);
    +
     1363.  12930        }
    +
     1364.      0        if (snippet === "'") {
    +
     1365.      0            if (!option.single) {
    +
     1366.      0                warn_at("use_double", line, column);
    +
     1367.      0            }
    +
     1368.      0            return string(snippet);
    +
     1369.      0        }
    +
     1370.  12930
    +
     1371.  12930// The token is a megastring. We don't allow any kind of mega nesting.
    +
     1372.  12930
    +
     1373.  12930        if (snippet === "`") {
    +
     1374.      0            if (mega_mode) {
    +
     1375.      0                return stop_at("expected_a_b", line, column, "}", "`");
    +
     1376.      0            }
    +
     1377.     12            snippet = "";
    +
     1378.     12            mega_from = from;
    +
     1379.     12            mega_line = line;
    +
     1380.     12            mega_mode = true;
    +
     1381.     12
    +
     1382.     12// Parsing a mega literal is tricky. First make a ` token.
    +
     1383.     12
    +
     1384.     12            make("`");
    +
     1385.     12            from += 1;
    +
     1386.     12
    +
     1387.     12// Then loop, building up a string, possibly from many lines, until seeing
    +
     1388.     12// the end of file, a closing `, or a ${ indicting an expression within the
    +
     1389.     12// string.
    +
     1390.     12
    +
     1391.    110            (function part() {
    +
     1392.    110                const at = source_line.search(rx_mega);
    +
     1393.    110
    +
     1394.    110// If neither ` nor ${ is seen, then the whole line joins the snippet.
    +
     1395.    110
    +
     1396.     60                if (at < 0) {
    +
     1397.     60                    snippet += source_line + "\n";
    +
     1398.     60                    return (
    +
     1399.     60                        next_line() === undefined
    +
     1400.      0                        ? stop_at("unclosed_mega", mega_line, mega_from)
    +
     1401.     60                        : part()
    +
     1402.     60                    );
    +
     1403.     60                }
    +
     1404.     50                snippet += source_line.slice(0, at);
    +
     1405.     50                column += at;
    +
     1406.     50                source_line = source_line.slice(at);
    +
     1407.     50                if (source_line[0] === "\\") {
    +
     1408.     38                    snippet += source_line.slice(0, 2);
    +
     1409.     38                    source_line = source_line.slice(2);
    +
     1410.     38                    column += 2;
    +
     1411.     38                    return part();
    +
     1412.     38                }
    +
     1413.     12
    +
     1414.     12// if either ` or ${ was found, then the preceding joins the snippet to become
    +
     1415.     12// a string token.
    +
     1416.     12
    +
     1417.     12                make("(string)", snippet).quote = "`";
    +
     1418.     12                snippet = "";
    +
     1419.     12
    +
     1420.     12// If ${, then make tokens that will become part of an expression until
    +
     1421.     12// a } token is made.
    +
     1422.     12
    +
     1423.      0                if (source_line[0] === "$") {
    +
     1424.      0                    column += 2;
    +
     1425.      0                    make("${");
    +
     1426.      0                    source_line = source_line.slice(2);
    +
     1427.      0                    (function expr() {
    +
     1428.      0                        const id = lex().id;
    +
     1429.      0                        if (id === "{") {
    +
     1430.      0                            return stop_at(
    +
     1431.      0                                "expected_a_b",
    +
     1432.      0                                line,
    +
     1433.      0                                column,
    +
     1434.      0                                "}",
    +
     1435.      0                                "{"
    +
     1436.      0                            );
    +
     1437.      0                        }
    +
     1438.      0                        if (id !== "}") {
    +
     1439.      0                            return expr();
    +
     1440.      0                        }
    +
     1441.      0                    }());
    +
     1442.      0                    return part();
    +
     1443.      0                }
    +
     1444.    110            }());
    +
     1445.     12            source_line = source_line.slice(1);
    +
     1446.     12            column += 1;
    +
     1447.     12            mega_mode = false;
    +
     1448.     12            return make("`");
    +
     1449.  12918        }
    +
     1450.  12918
    +
     1451.  12918// The token is a // comment.
    +
     1452.  12918
    +
     1453.  12918        if (snippet === "//") {
    +
     1454.    423            snippet = source_line;
    +
     1455.    423            source_line = "";
    +
     1456.    423            the_token = comment(snippet);
    +
     1457.      0            if (mega_mode) {
    +
     1458.      0                warn("unexpected_comment", the_token, "`");
    +
     1459.      0            }
    +
     1460.    423            return the_token;
    +
     1461.  12495        }
    +
     1462.  12495
    +
     1463.  12495// The token is a /* comment.
    +
     1464.  12495
    +
     1465.  12495        if (snippet === "/*") {
    +
     1466.      1            array = [];
    +
     1467.      0            if (source_line[0] === "/") {
    +
     1468.      0                warn_at("unexpected_a", line, column + i, "/");
    +
     1469.      0            }
    +
     1470.     36            (function next() {
    +
     1471.     36                if (source_line > "") {
    +
     1472.     36                    i = source_line.search(rx_star_slash);
    +
     1473.      1                    if (i >= 0) {
    +
     1474.      1                        return;
    +
     1475.     35                    }
    +
     1476.     35                    j = source_line.search(rx_slash_star);
    +
     1477.      0                    if (j >= 0) {
    +
     1478.      0                        warn_at("nested_comment", line, column + j);
    +
     1479.      0                    }
    +
     1480.     35                }
    +
     1481.     35                array.push(source_line);
    +
     1482.     35                source_line = next_line();
    +
     1483.      0                if (source_line === undefined) {
    +
     1484.      0                    return stop_at("unclosed_comment", line, column);
    +
     1485.      0                }
    +
     1486.     35                return next();
    +
     1487.     35            }());
    +
     1488.      1            snippet = source_line.slice(0, i);
    +
     1489.      1            j = snippet.search(rx_slash_star_or_slash);
    +
     1490.      0            if (j >= 0) {
    +
     1491.      0                warn_at("nested_comment", line, column + j);
    +
     1492.      0            }
    +
     1493.      1            array.push(snippet);
    +
     1494.      1            column += i + 2;
    +
     1495.      1            source_line = source_line.slice(i + 2);
    +
     1496.      1            return comment(array);
    +
     1497.  12494        }
    +
     1498.  12494
    +
     1499.  12494// The token is a slash.
    +
     1500.  12494
    +
     1501.  12494        if (snippet === "/") {
    +
     1502.      9
    +
     1503.      9// The / can be a division operator or the beginning of a regular expression
    +
     1504.      9// literal. It is not possible to know which without doing a complete parse.
    +
     1505.      9// We want to complete the tokenization before we begin to parse, so we will
    +
     1506.      9// estimate. This estimator can fail in some cases. For example, it cannot
    +
     1507.      9// know if "}" is ending a block or ending an object literal, so it can
    +
     1508.      9// behave incorrectly in that case; it is not meaningful to divide an
    +
     1509.      9// object, so it is likely that we can get away with it. We avoided the worst
    +
     1510.      9// cases by eliminating automatic semicolon insertion.
    +
     1511.      9
    +
     1512.      0            if (prior.identifier) {
    +
     1513.      0                if (!prior.dot) {
    +
     1514.      0                    if (prior.id === "return") {
    +
     1515.      0                        return regexp();
    +
     1516.      0                    }
    +
     1517.      0                    if (
    +
     1518.      0                        prior.id === "(begin)"
    +
     1519.      0                        || prior.id === "case"
    +
     1520.      0                        || prior.id === "delete"
    +
     1521.      0                        || prior.id === "in"
    +
     1522.      0                        || prior.id === "instanceof"
    +
     1523.      0                        || prior.id === "new"
    +
     1524.      0                        || prior.id === "typeof"
    +
     1525.      0                        || prior.id === "void"
    +
     1526.      0                        || prior.id === "yield"
    +
     1527.      0                    ) {
    +
     1528.      0                        the_token = regexp();
    +
     1529.      0                        return stop("unexpected_a", the_token);
    +
     1530.      0                    }
    +
     1531.      0                }
    +
     1532.      0            } else {
    +
     1533.      9                last = prior.id[prior.id.length - 1];
    +
     1534.      9                if ("(,=:?[".indexOf(last) >= 0) {
    +
     1535.      9                    return regexp();
    +
     1536.      0                }
    +
     1537.      0                if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) {
    +
     1538.      0                    the_token = regexp();
    +
     1539.      0                    warn("wrap_regexp", the_token);
    +
     1540.      0                    return the_token;
    +
     1541.      0                }
    +
     1542.      0            }
    +
     1543.      0            if (source_line[0] === "=") {
    +
     1544.      0                column += 1;
    +
     1545.      0                source_line = source_line.slice(1);
    +
     1546.      0                snippet = "/=";
    +
     1547.      0                warn_at("unexpected_a", line, column, "/=");
    +
     1548.      0            }
    +
     1549.  12485        }
    +
     1550.  12485        return make(snippet);
    +
     1551.  12485    }
    +
     1552.      1
    +
     1553.      1    first = lex();
    +
     1554.      1    json_mode = first.id === "{" || first.id === "[";
    +
     1555.      1
    +
     1556.      1// This loop will be replaced with a recursive call to lex when ES6 has been
    +
     1557.      1// finished and widely deployed and adopted.
    +
     1558.      1
    +
     1559.  22313    while (true) {
    +
     1560.  22313        if (lex().id === "(end)") {
    +
     1561.  22313            break;
    +
     1562.  22313        }
    +
     1563.  22313    }
    +
     1564.      1}
    +
     1565.      1
    +
     1566.      1// Parsing:
    +
     1567.      1
    +
     1568.      1// Parsing weaves the tokens into an abstract syntax tree. During that process,
    +
     1569.      1// a token may be given any of these properties:
    +
     1570.      1
    +
     1571.      1//      arity       string
    +
     1572.      1//      label       identifier
    +
     1573.      1//      name        identifier
    +
     1574.      1//      expression  expressions
    +
     1575.      1//      block       statements
    +
     1576.      1//      else        statements (else, default, catch)
    +
     1577.      1
    +
     1578.      1// Specialized tokens may have additional properties.
    +
     1579.      1
    +
     1580.   1488function survey(name) {
    +
     1581.   1488    let id = name.id;
    +
     1582.   1488
    +
     1583.   1488// Tally the property name. If it is a string, only tally strings that conform
    +
     1584.   1488// to the identifier rules.
    +
     1585.   1488
    +
     1586.      5    if (id === "(string)") {
    +
     1587.      5        id = name.value;
    +
     1588.      5        if (!rx_identifier.test(id)) {
    +
     1589.      5            return id;
    +
     1590.      5        }
    +
     1591.      0    } else if (id === "`") {
    +
     1592.      0        if (name.value.length === 1) {
    +
     1593.      0            id = name.value[0].value;
    +
     1594.      0            if (!rx_identifier.test(id)) {
    +
     1595.      0                return id;
    +
     1596.      0            }
    +
     1597.      0        }
    +
     1598.      0    } else if (!name.identifier) {
    +
     1599.      0        return stop("expected_identifier_a", name);
    +
     1600.      0    }
    +
     1601.   1483
    +
     1602.   1483// If we have seen this name before, increment its count.
    +
     1603.   1483
    +
     1604.   1483    if (typeof property[id] === "number") {
    +
     1605.   1275        property[id] += 1;
    +
     1606.   1275
    +
     1607.   1275// If this is the first time seeing this property name, and if there is a
    +
     1608.   1275// tenure list, then it must be on the list. Otherwise, it must conform to
    +
     1609.   1275// the rules for good property names.
    +
     1610.   1275
    +
     1611.   1275    } else {
    +
     1612.    208        if (tenure !== undefined) {
    +
     1613.      0            if (tenure[id] !== true) {
    +
     1614.      0                warn("unregistered_property_a", name);
    +
     1615.      0            }
    +
     1616.      0        } else {
    +
     1617.      0            if (name.identifier && rx_bad_property.test(id)) {
    +
     1618.      0                warn("bad_property_a", name);
    +
     1619.      0            }
    +
     1620.      0        }
    +
     1621.    208        property[id] = 1;
    +
     1622.   1483    }
    +
     1623.   1483    return id;
    +
     1624.   1483}
    +
     1625.      1
    +
     1626.  22455function dispense() {
    +
     1627.  22455
    +
     1628.  22455// Deliver the next token, skipping the comments.
    +
     1629.  22455
    +
     1630.  22455    const cadet = tokens[token_nr];
    +
     1631.  22455    token_nr += 1;
    +
     1632.    424    if (cadet.id === "(comment)") {
    +
     1633.      0        if (json_mode) {
    +
     1634.      0            warn("unexpected_a", cadet);
    +
     1635.      0        }
    +
     1636.    424        return dispense();
    +
     1637.  22031    } else {
    +
     1638.  22031        return cadet;
    +
     1639.  22031    }
    +
     1640.  22455}
    +
     1641.      1
    +
     1642.    116function lookahead() {
    +
     1643.    116
    +
     1644.    116// Look ahead one token without advancing.
    +
     1645.    116
    +
     1646.    116    const old_token_nr = token_nr;
    +
     1647.    116    const cadet = dispense(true);
    +
     1648.    116    token_nr = old_token_nr;
    +
     1649.    116    return cadet;
    +
     1650.    116}
    +
     1651.      1
    +
     1652.  21915function advance(id, match) {
    +
     1653.  21915
    +
     1654.  21915// Produce the next token.
    +
     1655.  21915
    +
     1656.  21915// Attempt to give helpful names to anonymous functions.
    +
     1657.  21915
    +
     1658.   7484    if (token.identifier && token.id !== "function") {
    +
     1659.   7272        anon = token.id;
    +
     1660.  14643    } else if (token.id === "(string)" && rx_identifier.test(token.value)) {
    +
     1661.  14643        anon = token.value;
    +
     1662.  14643    }
    +
     1663.  21915
    +
     1664.  21915// Attempt to match next_token with an expected id.
    +
     1665.  21915
    +
     1666.      0    if (id !== undefined && next_token.id !== id) {
    +
     1667.      0        return (
    +
     1668.      0            match === undefined
    +
     1669.      0            ? stop("expected_a_b", next_token, id, artifact())
    +
     1670.      0            : stop(
    +
     1671.      0                "expected_a_b_from_c_d",
    +
     1672.      0                next_token,
    +
     1673.      0                id,
    +
     1674.      0                artifact(match),
    +
     1675.      0                artifact_line(match),
    +
     1676.      0                artifact(next_token)
    +
     1677.      0            )
    +
     1678.      0        );
    +
     1679.      0    }
    +
     1680.  21915
    +
     1681.  21915// Promote the tokens, skipping comments.
    +
     1682.  21915
    +
     1683.  21915    token = next_token;
    +
     1684.  21915    next_token = dispense();
    +
     1685.      2    if (next_token.id === "(end)") {
    +
     1686.      2        token_nr -= 1;
    +
     1687.      2    }
    +
     1688.  21915}
    +
     1689.      1
    +
     1690.      1// Parsing of JSON is simple:
    +
     1691.      1
    +
     1692.      0function json_value() {
    +
     1693.      0    let negative;
    +
     1694.      0    if (next_token.id === "{") {
    +
     1695.      0        return (function json_object() {
    +
     1696.      0            const brace = next_token;
    +
     1697.      0            const object = empty();
    +
     1698.      0            const properties = [];
    +
     1699.      0            brace.expression = properties;
    +
     1700.      0            advance("{");
    +
     1701.      0            if (next_token.id !== "}") {
    +
     1702.      0                (function next() {
    +
     1703.      0                    let name;
    +
     1704.      0                    let value;
    +
     1705.      0                    if (next_token.quote !== "\"") {
    +
     1706.      0                        warn(
    +
     1707.      0                            "unexpected_a",
    +
     1708.      0                            next_token,
    +
     1709.      0                            next_token.quote
    +
     1710.      0                        );
    +
     1711.      0                    }
    +
     1712.      0                    name = next_token;
    +
     1713.      0                    advance("(string)");
    +
     1714.      0                    if (object[token.value] !== undefined) {
    +
     1715.      0                        warn("duplicate_a", token);
    +
     1716.      0                    } else if (token.value === "__proto__") {
    +
     1717.      0                        warn("bad_property_a", token);
    +
     1718.      0                    } else {
    +
     1719.      0                        object[token.value] = token;
    +
     1720.      0                    }
    +
     1721.      0                    advance(":");
    +
     1722.      0                    value = json_value();
    +
     1723.      0                    value.label = name;
    +
     1724.      0                    properties.push(value);
    +
     1725.      0                    if (next_token.id === ",") {
    +
     1726.      0                        advance(",");
    +
     1727.      0                        return next();
    +
     1728.      0                    }
    +
     1729.      0                }());
    +
     1730.      0            }
    +
     1731.      0            advance("}", brace);
    +
     1732.      0            return brace;
    +
     1733.      0        }());
    +
     1734.      0    }
    +
     1735.      0    if (next_token.id === "[") {
    +
     1736.      0        return (function json_array() {
    +
     1737.      0            const bracket = next_token;
    +
     1738.      0            const elements = [];
    +
     1739.      0            bracket.expression = elements;
    +
     1740.      0            advance("[");
    +
     1741.      0            if (next_token.id !== "]") {
    +
     1742.      0                (function next() {
    +
     1743.      0                    elements.push(json_value());
    +
     1744.      0                    if (next_token.id === ",") {
    +
     1745.      0                        advance(",");
    +
     1746.      0                        return next();
    +
     1747.      0                    }
    +
     1748.      0                }());
    +
     1749.      0            }
    +
     1750.      0            advance("]", bracket);
    +
     1751.      0            return bracket;
    +
     1752.      0        }());
    +
     1753.      0    }
    +
     1754.      0    if (
    +
     1755.      0        next_token.id === "true"
    +
     1756.      0        || next_token.id === "false"
    +
     1757.      0        || next_token.id === "null"
    +
     1758.      0    ) {
    +
     1759.      0        advance();
    +
     1760.      0        return token;
    +
     1761.      0    }
    +
     1762.      0    if (next_token.id === "(number)") {
    +
     1763.      0        if (!rx_JSON_number.test(next_token.value)) {
    +
     1764.      0            warn("unexpected_a");
    +
     1765.      0        }
    +
     1766.      0        advance();
    +
     1767.      0        return token;
    +
     1768.      0    }
    +
     1769.      0    if (next_token.id === "(string)") {
    +
     1770.      0        if (next_token.quote !== "\"") {
    +
     1771.      0            warn("unexpected_a", next_token, next_token.quote);
    +
     1772.      0        }
    +
     1773.      0        advance();
    +
     1774.      0        return token;
    +
     1775.      0    }
    +
     1776.      0    if (next_token.id === "-") {
    +
     1777.      0        negative = next_token;
    +
     1778.      0        negative.arity = "unary";
    +
     1779.      0        advance("-");
    +
     1780.      0        advance("(number)");
    +
     1781.      0        if (!rx_JSON_number.test(token.value)) {
    +
     1782.      0            warn("unexpected_a", token);
    +
     1783.      0        }
    +
     1784.      0        negative.expression = token;
    +
     1785.      0        return negative;
    +
     1786.      0    }
    +
     1787.      0    stop("unexpected_a");
    +
     1788.      0}
    +
     1789.      1
    +
     1790.      1// Now we parse JavaScript.
    +
     1791.      1
    +
     1792.    569function enroll(name, role, readonly) {
    +
     1793.    569
    +
     1794.    569// Enroll a name into the current function context. The role can be exception,
    +
     1795.    569// function, label, parameter, or variable. We look for variable redefinition
    +
     1796.    569// because it causes confusion.
    +
     1797.    569
    +
     1798.    569    const id = name.id;
    +
     1799.    569
    +
     1800.    569// Reserved words may not be enrolled.
    +
     1801.    569
    +
     1802.      0    if (syntax[id] !== undefined && id !== "ignore") {
    +
     1803.      0        warn("reserved_a", name);
    +
     1804.      0    } else {
    +
     1805.    569
    +
     1806.    569// Has the name been enrolled in this context?
    +
     1807.    569
    +
     1808.    569        let earlier = functionage.context[id];
    +
     1809.      0        if (earlier) {
    +
     1810.      0            warn(
    +
     1811.      0                "redefinition_a_b",
    +
     1812.      0                name,
    +
     1813.      0                name.id,
    +
     1814.      0                earlier.line + fudge
    +
     1815.      0            );
    +
     1816.      0
    +
     1817.      0// Has the name been enrolled in an outer context?
    +
     1818.      0
    +
     1819.      0        } else {
    +
     1820.    633            stack.forEach(function (value) {
    +
     1821.    633                const item = value.context[id];
    +
     1822.      5                if (item !== undefined) {
    +
     1823.      5                    earlier = item;
    +
     1824.      5                }
    +
     1825.    633            });
    +
     1826.      5            if (earlier) {
    +
     1827.      0                if (id === "ignore") {
    +
     1828.      0                    if (earlier.role === "variable") {
    +
     1829.      0                        warn("unexpected_a", name);
    +
     1830.      0                    }
    +
     1831.      0                } else {
    +
     1832.      5                    if (
    +
     1833.      5                        (
    +
     1834.      5                            role !== "exception"
    +
     1835.      0                            || earlier.role !== "exception"
    +
     1836.      5                        )
    +
     1837.      5                        && role !== "parameter"
    +
     1838.      0                        && role !== "function"
    +
     1839.      0                    ) {
    +
     1840.      0                        warn(
    +
     1841.      0                            "redefinition_a_b",
    +
     1842.      0                            name,
    +
     1843.      0                            name.id,
    +
     1844.      0                            earlier.line + fudge
    +
     1845.      0                        );
    +
     1846.      0                    }
    +
     1847.      5                }
    +
     1848.      5            }
    +
     1849.    569
    +
     1850.    569// Enroll it.
    +
     1851.    569
    +
     1852.    569            functionage.context[id] = name;
    +
     1853.    569            name.dead = true;
    +
     1854.    569            name.parent = functionage;
    +
     1855.    569            name.init = false;
    +
     1856.    569            name.role = role;
    +
     1857.    569            name.used = 0;
    +
     1858.    569            name.writable = !readonly;
    +
     1859.    569        }
    +
     1860.    569    }
    +
     1861.    569}
    +
     1862.      1
    +
     1863.   6289function expression(rbp, initial) {
    +
     1864.   6289
    +
     1865.   6289// This is the heart of the Pratt parser. I retained Pratt's nomenclature.
    +
     1866.   6289// They are elements of the parsing method called Top Down Operator Precedence.
    +
     1867.   6289
    +
     1868.   6289// nud     Null denotation
    +
     1869.   6289// led     Left denotation
    +
     1870.   6289// lbp     Left binding power
    +
     1871.   6289// rbp     Right binding power
    +
     1872.   6289
    +
     1873.   6289// It processes a nud (variable, constant, prefix operator). It will then
    +
     1874.   6289// process leds (infix operators) until the bind powers cause it to stop. It
    +
     1875.   6289// returns the expression's parse tree.
    +
     1876.   6289
    +
     1877.   6289    let left;
    +
     1878.   6289    let the_symbol;
    +
     1879.   6289
    +
     1880.   6289// Statements will have already advanced, so advance now only if the token is
    +
     1881.   6289// not the first of a statement,
    +
     1882.   6289
    +
     1883.   4990    if (!initial) {
    +
     1884.   4990        advance();
    +
     1885.   4990    }
    +
     1886.   6289    the_symbol = syntax[token.id];
    +
     1887.   2605    if (the_symbol !== undefined && the_symbol.nud !== undefined) {
    +
     1888.   2605        left = the_symbol.nud();
    +
     1889.   3684    } else if (token.identifier) {
    +
     1890.   3684        left = token;
    +
     1891.   3684        left.arity = "variable";
    +
     1892.      0    } else {
    +
     1893.      0        return stop("unexpected_a", token);
    +
     1894.      0    }
    +
     1895.  10584    (function right() {
    +
     1896.  10584        the_symbol = syntax[next_token.id];
    +
     1897.  10584        if (
    +
     1898.  10584            the_symbol !== undefined
    +
     1899.  10584            && the_symbol.led !== undefined
    +
     1900.   4726            && rbp < the_symbol.lbp
    +
     1901.   4295        ) {
    +
     1902.   4295            advance();
    +
     1903.   4295            left = the_symbol.led(left);
    +
     1904.   4295            return right();
    +
     1905.   4295        }
    +
     1906.  10584    }());
    +
     1907.   6289    return left;
    +
     1908.   6289}
    +
     1909.      1
    +
     1910.    560function condition() {
    +
     1911.    560
    +
     1912.    560// Parse the condition part of a do, if, while.
    +
     1913.    560
    +
     1914.    560    const the_paren = next_token;
    +
     1915.    560    let the_value;
    +
     1916.    560    the_paren.free = true;
    +
     1917.    560    advance("(");
    +
     1918.    560    the_value = expression(0);
    +
     1919.    560    advance(")");
    +
     1920.      0    if (the_value.wrapped === true) {
    +
     1921.      0        warn("unexpected_a", the_paren);
    +
     1922.      0    }
    +
     1923.      0    if (anticondition[the_value.id] === true) {
    +
     1924.      0        warn("unexpected_a", the_value);
    +
     1925.      0    }
    +
     1926.    560    return the_value;
    +
     1927.    560}
    +
     1928.      1
    +
     1929.   4789function is_weird(thing) {
    +
     1930.   4789    return (
    +
     1931.   4789        thing.id === "(regexp)"
    +
     1932.   4789        || thing.id === "{"
    +
     1933.   4789        || thing.id === "=>"
    +
     1934.   4789        || thing.id === "function"
    +
     1935.   4787        || (thing.id === "[" && thing.arity === "unary")
    +
     1936.   4789    );
    +
     1937.   4789}
    +
     1938.      1
    +
     1939.   1476function are_similar(a, b) {
    +
     1940.      0    if (a === b) {
    +
     1941.      0        return true;
    +
     1942.      0    }
    +
     1943.      0    if (Array.isArray(a)) {
    +
     1944.      0        return (
    +
     1945.      0            Array.isArray(b)
    +
     1946.      0            && a.length === b.length
    +
     1947.      0            && a.every(function (value, index) {
    +
     1948.      0                return are_similar(value, b[index]);
    +
     1949.      0            })
    +
     1950.      0        );
    +
     1951.      0    }
    +
     1952.      0    if (Array.isArray(b)) {
    +
     1953.      0        return false;
    +
     1954.      0    }
    +
     1955.      6    if (a.id === "(number)" && b.id === "(number)") {
    +
     1956.      6        return a.value === b.value;
    +
     1957.   1470    }
    +
     1958.   1470    let a_string;
    +
     1959.   1470    let b_string;
    +
     1960.   1470    if (a.id === "(string)") {
    +
     1961.     68        a_string = a.value;
    +
     1962.      0    } else if (a.id === "`" && a.constant) {
    +
     1963.      0        a_string = a.value[0];
    +
     1964.      0    }
    +
     1965.   1470    if (b.id === "(string)") {
    +
     1966.    598        b_string = b.value;
    +
     1967.      0    } else if (b.id === "`" && b.constant) {
    +
     1968.      0        b_string = b.value[0];
    +
     1969.      0    }
    +
     1970.   1470    if (typeof a_string === "string") {
    +
     1971.     68        return a_string === b_string;
    +
     1972.   1402    }
    +
     1973.   1402    if (is_weird(a) || is_weird(b)) {
    +
     1974.      2        return false;
    +
     1975.   1400    }
    +
     1976.   1400    if (a.arity === b.arity && a.id === b.id) {
    +
     1977.    381        if (a.id === ".") {
    +
     1978.    381            return (
    +
     1979.    381                are_similar(a.expression, b.expression)
    +
     1980.    381                && are_similar(a.name, b.name)
    +
     1981.    381            );
    +
     1982.    381        }
    +
     1983.    381        if (a.arity === "unary") {
    +
     1984.    381            return are_similar(a.expression, b.expression);
    +
     1985.    381        }
    +
     1986.    381        if (a.arity === "binary") {
    +
     1987.    381            return (
    +
     1988.    381                a.id !== "("
    +
     1989.    381                && are_similar(a.expression[0], b.expression[0])
    +
     1990.    381                && are_similar(a.expression[1], b.expression[1])
    +
     1991.    381            );
    +
     1992.    381        }
    +
     1993.      0        if (a.arity === "ternary") {
    +
     1994.      0            return (
    +
     1995.      0                are_similar(a.expression[0], b.expression[0])
    +
     1996.      0                && are_similar(a.expression[1], b.expression[1])
    +
     1997.      0                && are_similar(a.expression[2], b.expression[2])
    +
     1998.      0            );
    +
     1999.      0        }
    +
     2000.      0        if (a.arity === "function" && a.arity === "regexp") {
    +
     2001.      0            return false;
    +
     2002.      0        }
    +
     2003.    381        return true;
    +
     2004.   1019    }
    +
     2005.   1019    return false;
    +
     2006.   1019}
    +
     2007.      1
    +
     2008.   1571function semicolon() {
    +
     2009.   1571
    +
     2010.   1571// Try to match a semicolon.
    +
     2011.   1571
    +
     2012.   1571    if (next_token.id === ";") {
    +
     2013.   1571        advance(";");
    +
     2014.      0    } else {
    +
     2015.      0        warn_at(
    +
     2016.      0            "expected_a_b",
    +
     2017.      0            token.line,
    +
     2018.      0            token.thru,
    +
     2019.      0            ";",
    +
     2020.      0            artifact(next_token)
    +
     2021.      0        );
    +
     2022.      0    }
    +
     2023.   1571    anon = "anonymous";
    +
     2024.   1571}
    +
     2025.      1
    +
     2026.   2453function statement() {
    +
     2027.   2453
    +
     2028.   2453// Parse a statement. Any statement may have a label, but only four statements
    +
     2029.   2453// have use for one. A statement can be one of the standard statements, or
    +
     2030.   2453// an assignment expression, or an invocation expression.
    +
     2031.   2453
    +
     2032.   2453    let first;
    +
     2033.   2453    let the_label;
    +
     2034.   2453    let the_statement;
    +
     2035.   2453    let the_symbol;
    +
     2036.   2453    advance();
    +
     2037.      0    if (token.identifier && next_token.id === ":") {
    +
     2038.      0        the_label = token;
    +
     2039.      0        if (the_label.id === "ignore") {
    +
     2040.      0            warn("unexpected_a", the_label);
    +
     2041.      0        }
    +
     2042.      0        advance(":");
    +
     2043.      0        if (
    +
     2044.      0            next_token.id === "do"
    +
     2045.      0            || next_token.id === "for"
    +
     2046.      0            || next_token.id === "switch"
    +
     2047.      0            || next_token.id === "while"
    +
     2048.      0        ) {
    +
     2049.      0            enroll(the_label, "label", true);
    +
     2050.      0            the_label.init = true;
    +
     2051.      0            the_label.dead = false;
    +
     2052.      0            the_statement = statement();
    +
     2053.      0            the_statement.label = the_label;
    +
     2054.      0            the_statement.statement = true;
    +
     2055.      0            return the_statement;
    +
     2056.      0        }
    +
     2057.      0        advance();
    +
     2058.      0        warn("unexpected_label_a", the_label);
    +
     2059.      0    }
    +
     2060.   2453
    +
     2061.   2453// Parse the statement.
    +
     2062.   2453
    +
     2063.   2453    first = token;
    +
     2064.   2453    first.statement = true;
    +
     2065.   2453    the_symbol = syntax[first.id];
    +
     2066.   1199    if (the_symbol !== undefined && the_symbol.fud !== undefined) {
    +
     2067.   1176        the_symbol.disrupt = false;
    +
     2068.   1176        the_symbol.statement = true;
    +
     2069.   1176        the_statement = the_symbol.fud();
    +
     2070.   1277    } else {
    +
     2071.   1277
    +
     2072.   1277// It is an expression statement.
    +
     2073.   1277
    +
     2074.   1277        the_statement = expression(0, true);
    +
     2075.      0        if (the_statement.wrapped && the_statement.id !== "(") {
    +
     2076.      0            warn("unexpected_a", first);
    +
     2077.      0        }
    +
     2078.   1277        semicolon();
    +
     2079.   1277    }
    +
     2080.      0    if (the_label !== undefined) {
    +
     2081.      0        the_label.dead = true;
    +
     2082.      0    }
    +
     2083.   2453    return the_statement;
    +
     2084.   2453}
    +
     2085.      1
    +
     2086.    849function statements() {
    +
     2087.    849
    +
     2088.    849// Parse a list of statements. Give a warning if an unreachable statement
    +
     2089.    849// follows a disruptive statement.
    +
     2090.    849
    +
     2091.    849    const array = [];
    +
     2092.   3233    (function next(disrupt) {
    +
     2093.   3233        if (
    +
     2094.   3233            next_token.id !== "}"
    +
     2095.   2385            && next_token.id !== "case"
    +
     2096.   2385            && next_token.id !== "default"
    +
     2097.   2385            && next_token.id !== "else"
    +
     2098.   2385            && next_token.id !== "(end)"
    +
     2099.   2384        ) {
    +
     2100.   2384            let a_statement = statement();
    +
     2101.   2384            array.push(a_statement);
    +
     2102.      0            if (disrupt) {
    +
     2103.      0                warn("unreachable_a", a_statement);
    +
     2104.      0            }
    +
     2105.   2384            return next(a_statement.disrupt);
    +
     2106.   2384        }
    +
     2107.   3233    }(false));
    +
     2108.    849    return array;
    +
     2109.    849}
    +
     2110.      1
    +
     2111.    229function not_top_level(thing) {
    +
     2112.    229
    +
     2113.    229// Some features should not be at the outermost level.
    +
     2114.    229
    +
     2115.      0    if (functionage === global) {
    +
     2116.      0        warn("unexpected_at_top_level_a", thing);
    +
     2117.      0    }
    +
     2118.    229}
    +
     2119.      1
    +
     2120.      1function top_level_only(the_thing) {
    +
     2121.      1
    +
     2122.      1// Some features must be at the most outermost level.
    +
     2123.      1
    +
     2124.      0    if (blockage !== global) {
    +
     2125.      0        warn("misplaced_a", the_thing);
    +
     2126.      0    }
    +
     2127.      1}
    +
     2128.      1
    +
     2129.    848function block(special) {
    +
     2130.    848
    +
     2131.    848// Parse a block, a sequence of statements wrapped in braces.
    +
     2132.    848//  special "body"      The block is a function body.
    +
     2133.    848//          "ignore"    No warning on an empty block.
    +
     2134.    848//          "naked"     No advance.
    +
     2135.    848//          undefined   An ordinary block.
    +
     2136.    848
    +
     2137.    848    let stmts;
    +
     2138.    848    let the_block;
    +
     2139.    848    if (special !== "naked") {
    +
     2140.    848        advance("{");
    +
     2141.    848    }
    +
     2142.    848    the_block = token;
    +
     2143.    848    the_block.arity = "statement";
    +
     2144.    848    the_block.body = special === "body";
    +
     2145.    848
    +
     2146.    848// Top level function bodies may include the "use strict" pragma.
    +
     2147.    848
    +
     2148.    848    if (
    +
     2149.    848        special === "body"
    +
     2150.    212        && stack.length === 1
    +
     2151.    124        && next_token.value === "use strict"
    +
     2152.      0    ) {
    +
     2153.      0        next_token.statement = true;
    +
     2154.      0        advance("(string)");
    +
     2155.      0        advance(";");
    +
     2156.      0    }
    +
     2157.    848    stmts = statements();
    +
     2158.    848    the_block.block = stmts;
    +
     2159.      0    if (stmts.length === 0) {
    +
     2160.      0        if (!option.devel && special !== "ignore") {
    +
     2161.      0            warn("empty_block", the_block);
    +
     2162.      0        }
    +
     2163.      0        the_block.disrupt = false;
    +
     2164.      0    } else {
    +
     2165.    848        the_block.disrupt = stmts[stmts.length - 1].disrupt;
    +
     2166.    848    }
    +
     2167.    848    advance("}");
    +
     2168.    848    return the_block;
    +
     2169.    848}
    +
     2170.      1
    +
     2171.    519function mutation_check(the_thing) {
    +
     2172.    519
    +
     2173.    519// The only expressions that may be assigned to are
    +
     2174.    519//      e.b
    +
     2175.    519//      e[b]
    +
     2176.    519//      v
    +
     2177.    519//      [destructure]
    +
     2178.    519//      {destructure}
    +
     2179.    519
    +
     2180.    519    if (
    +
     2181.    519        the_thing.arity !== "variable"
    +
     2182.    272        && the_thing.id !== "."
    +
     2183.     24        && the_thing.id !== "["
    +
     2184.      0        && the_thing.id !== "{"
    +
     2185.      0    ) {
    +
     2186.      0        warn("bad_assignment_a", the_thing);
    +
     2187.      0        return false;
    +
     2188.      0    }
    +
     2189.    519    return true;
    +
     2190.    519}
    +
     2191.      1
    +
     2192.   2616function left_check(left, right) {
    +
     2193.   2616
    +
     2194.   2616// Warn if the left is not one of these:
    +
     2195.   2616//      e.b
    +
     2196.   2616//      e[b]
    +
     2197.   2616//      e()
    +
     2198.   2616//      ?:
    +
     2199.   2616//      identifier
    +
     2200.   2616
    +
     2201.   2616    const id = left.id;
    +
     2202.   2616    if (
    +
     2203.   2616        !left.identifier
    +
     2204.    364        && (
    +
     2205.    364            left.arity !== "ternary"
    +
     2206.      0            || (
    +
     2207.      0                !left_check(left.expression[1])
    +
     2208.      0                && !left_check(left.expression[2])
    +
     2209.      0            )
    +
     2210.    364        )
    +
     2211.    364        && (
    +
     2212.    364            left.arity !== "binary"
    +
     2213.    364            || (id !== "." && id !== "(" && id !== "[")
    +
     2214.    364        )
    +
     2215.      0    ) {
    +
     2216.      0        warn("unexpected_a", right);
    +
     2217.      0        return false;
    +
     2218.      0    }
    +
     2219.   2616    return true;
    +
     2220.   2616}
    +
     2221.      1
    +
     2222.      1// These functions are used to specify the grammar of our language:
    +
     2223.      1
    +
     2224.    125function symbol(id, bp) {
    +
     2225.    125
    +
     2226.    125// Make a symbol if it does not already exist in the language's syntax.
    +
     2227.    125
    +
     2228.    125    let the_symbol = syntax[id];
    +
     2229.    112    if (the_symbol === undefined) {
    +
     2230.    112        the_symbol = empty();
    +
     2231.    112        the_symbol.id = id;
    +
     2232.    112        the_symbol.lbp = bp || 0;
    +
     2233.    112        syntax[id] = the_symbol;
    +
     2234.    112    }
    +
     2235.    125    return the_symbol;
    +
     2236.    125}
    +
     2237.      1
    +
     2238.     12function assignment(id) {
    +
     2239.     12
    +
     2240.     12// Make an assignment operator. The one true assignment is different because
    +
     2241.     12// its left side, when it is a variable, is not treated as an expression.
    +
     2242.     12// That case is special because that is when a variable gets initialized. The
    +
     2243.     12// other assignment operators can modify, but they cannot initialize.
    +
     2244.     12
    +
     2245.     12    const the_symbol = symbol(id, 20);
    +
     2246.    519    the_symbol.led = function (left) {
    +
     2247.    519        const the_token = token;
    +
     2248.    519        let right;
    +
     2249.    519        the_token.arity = "assignment";
    +
     2250.    519        right = expression(20 - 1);
    +
     2251.    479        if (id === "=" && left.arity === "variable") {
    +
     2252.    224            the_token.names = left;
    +
     2253.    224            the_token.expression = right;
    +
     2254.    295        } else {
    +
     2255.    295            the_token.expression = [left, right];
    +
     2256.    295        }
    +
     2257.    519        if (
    +
     2258.    519            right.arity === "assignment"
    +
     2259.    519            || right.arity === "pre"
    +
     2260.    519            || right.arity === "post"
    +
     2261.      0        ) {
    +
     2262.      0            warn("unexpected_a", right);
    +
     2263.      0        }
    +
     2264.    519        mutation_check(left);
    +
     2265.    519        return the_token;
    +
     2266.    519    };
    +
     2267.     12    return the_symbol;
    +
     2268.     12}
    +
     2269.      1
    +
     2270.     16function constant(id, type, value) {
    +
     2271.     16
    +
     2272.     16// Make a constant symbol.
    +
     2273.     16
    +
     2274.     16    const the_symbol = symbol(id);
    +
     2275.     16    the_symbol.constant = true;
    +
     2276.     16    the_symbol.nud = (
    +
     2277.     16        typeof value === "function"
    +
     2278.      7        ? value
    +
     2279.   2216        : function () {
    +
     2280.   2216            token.constant = true;
    +
     2281.    228            if (value !== undefined) {
    +
     2282.    228                token.value = value;
    +
     2283.    228            }
    +
     2284.   2216            return token;
    +
     2285.   2216        }
    +
     2286.     16    );
    +
     2287.     16    the_symbol.type = type;
    +
     2288.     16    the_symbol.value = value;
    +
     2289.     16    return the_symbol;
    +
     2290.     16}
    +
     2291.      1
    +
     2292.     30function infix(id, bp, f) {
    +
     2293.     30
    +
     2294.     30// Make an infix operator.
    +
     2295.     30
    +
     2296.     30    const the_symbol = symbol(id, bp);
    +
     2297.   3762    the_symbol.led = function (left) {
    +
     2298.   3762        const the_token = token;
    +
     2299.   3762        the_token.arity = "binary";
    +
     2300.   2644        if (f !== undefined) {
    +
     2301.   2644            return f(left);
    +
     2302.   2644        }
    +
     2303.   1118        the_token.expression = [left, expression(bp)];
    +
     2304.   1118        return the_token;
    +
     2305.   1118    };
    +
     2306.     30    return the_symbol;
    +
     2307.     30}
    +
     2308.      1
    +
     2309.      1function infixr(id, bp) {
    +
     2310.      1
    +
     2311.      1// Make a right associative infix operator.
    +
     2312.      1
    +
     2313.      1    const the_symbol = symbol(id, bp);
    +
     2314.      0    the_symbol.led = function (left) {
    +
     2315.      0        const the_token = token;
    +
     2316.      0        the_token.arity = "binary";
    +
     2317.      0        the_token.expression = [left, expression(bp - 1)];
    +
     2318.      0        return the_token;
    +
     2319.      0    };
    +
     2320.      1    return the_symbol;
    +
     2321.      1}
    +
     2322.      1
    +
     2323.      2function post(id) {
    +
     2324.      2
    +
     2325.      2// Make one of the post operators.
    +
     2326.      2
    +
     2327.      2    const the_symbol = symbol(id, 150);
    +
     2328.      0    the_symbol.led = function (left) {
    +
     2329.      0        token.expression = left;
    +
     2330.      0        token.arity = "post";
    +
     2331.      0        mutation_check(token.expression);
    +
     2332.      0        return token;
    +
     2333.      0    };
    +
     2334.      2    return the_symbol;
    +
     2335.      2}
    +
     2336.      1
    +
     2337.      2function pre(id) {
    +
     2338.      2
    +
     2339.      2// Make one of the pre operators.
    +
     2340.      2
    +
     2341.      2    const the_symbol = symbol(id);
    +
     2342.      0    the_symbol.nud = function () {
    +
     2343.      0        const the_token = token;
    +
     2344.      0        the_token.arity = "pre";
    +
     2345.      0        the_token.expression = expression(150);
    +
     2346.      0        mutation_check(the_token.expression);
    +
     2347.      0        return the_token;
    +
     2348.      0    };
    +
     2349.      2    return the_symbol;
    +
     2350.      2}
    +
     2351.      1
    +
     2352.     15function prefix(id, f) {
    +
     2353.     15
    +
     2354.     15// Make a prefix operator.
    +
     2355.     15
    +
     2356.     15    const the_symbol = symbol(id);
    +
     2357.    389    the_symbol.nud = function () {
    +
     2358.    389        const the_token = token;
    +
     2359.    389        the_token.arity = "unary";
    +
     2360.    299        if (typeof f === "function") {
    +
     2361.    299            return f();
    +
     2362.    299        }
    +
     2363.     90        the_token.expression = expression(150);
    +
     2364.     90        return the_token;
    +
     2365.     90    };
    +
     2366.     15    return the_symbol;
    +
     2367.     15}
    +
     2368.      1
    +
     2369.     21function stmt(id, f) {
    +
     2370.     21
    +
     2371.     21// Make a statement.
    +
     2372.     21
    +
     2373.     21    const the_symbol = symbol(id);
    +
     2374.   1176    the_symbol.fud = function () {
    +
     2375.   1176        token.arity = "statement";
    +
     2376.   1176        return f();
    +
     2377.   1176    };
    +
     2378.     21    return the_symbol;
    +
     2379.     21}
    +
     2380.      1
    +
     2381.      1function ternary(id1, id2) {
    +
     2382.      1
    +
     2383.      1// Make a ternary operator.
    +
     2384.      1
    +
     2385.      1    const the_symbol = symbol(id1, 30);
    +
     2386.     14    the_symbol.led = function (left) {
    +
     2387.     14        const the_token = token;
    +
     2388.     14        const second = expression(20);
    +
     2389.     14        advance(id2);
    +
     2390.     14        token.arity = "ternary";
    +
     2391.     14        the_token.arity = "ternary";
    +
     2392.     14        the_token.expression = [left, second, expression(10)];
    +
     2393.      0        if (next_token.id !== ")") {
    +
     2394.      0            warn("use_open", the_token);
    +
     2395.      0        }
    +
     2396.     14        return the_token;
    +
     2397.     14    };
    +
     2398.      1    return the_symbol;
    +
     2399.      1}
    +
     2400.      1
    +
     2401.      1// Begin defining the language.
    +
     2402.      1
    +
     2403.      1syntax = empty();
    +
     2404.      1
    +
     2405.      1symbol("}");
    +
     2406.      1symbol(")");
    +
     2407.      1symbol("]");
    +
     2408.      1symbol(",");
    +
     2409.      1symbol(";");
    +
     2410.      1symbol(":");
    +
     2411.      1symbol("*/");
    +
     2412.      1symbol("await");
    +
     2413.      1symbol("case");
    +
     2414.      1symbol("catch");
    +
     2415.      1symbol("class");
    +
     2416.      1symbol("default");
    +
     2417.      1symbol("else");
    +
     2418.      1symbol("enum");
    +
     2419.      1symbol("finally");
    +
     2420.      1symbol("implements");
    +
     2421.      1symbol("interface");
    +
     2422.      1symbol("package");
    +
     2423.      1symbol("private");
    +
     2424.      1symbol("protected");
    +
     2425.      1symbol("public");
    +
     2426.      1symbol("static");
    +
     2427.      1symbol("super");
    +
     2428.      1symbol("void");
    +
     2429.      1symbol("yield");
    +
     2430.      1
    +
     2431.      1constant("(number)", "number");
    +
     2432.      1constant("(regexp)", "regexp");
    +
     2433.      1constant("(string)", "string");
    +
     2434.      0constant("arguments", "object", function () {
    +
     2435.      0    warn("unexpected_a", token);
    +
     2436.      0    return token;
    +
     2437.      0});
    +
     2438.      0constant("eval", "function", function () {
    +
     2439.      0    if (!option.eval) {
    +
     2440.      0        warn("unexpected_a", token);
    +
     2441.      0    } else if (next_token.id !== "(") {
    +
     2442.      0        warn("expected_a_before_b", next_token, "(", artifact());
    +
     2443.      0    }
    +
     2444.      0    return token;
    +
     2445.      0});
    +
     2446.      1constant("false", "boolean", false);
    +
     2447.      0constant("Function", "function", function () {
    +
     2448.      0    if (!option.eval) {
    +
     2449.      0        warn("unexpected_a", token);
    +
     2450.      0    } else if (next_token.id !== "(") {
    +
     2451.      0        warn("expected_a_before_b", next_token, "(", artifact());
    +
     2452.      0    }
    +
     2453.      0    return token;
    +
     2454.      0});
    +
     2455.      0constant("ignore", "undefined", function () {
    +
     2456.      0    warn("unexpected_a", token);
    +
     2457.      0    return token;
    +
     2458.      0});
    +
     2459.      1constant("Infinity", "number", Infinity);
    +
     2460.      0constant("isFinite", "function", function () {
    +
     2461.      0    warn("expected_a_b", token, "Number.isFinite", "isFinite");
    +
     2462.      0    return token;
    +
     2463.      0});
    +
     2464.      0constant("isNaN", "function", function () {
    +
     2465.      0    warn("number_isNaN", token);
    +
     2466.      0    return token;
    +
     2467.      0});
    +
     2468.      1constant("NaN", "number", NaN);
    +
     2469.      1constant("null", "null", null);
    +
     2470.      0constant("this", "object", function () {
    +
     2471.      0    if (!option.this) {
    +
     2472.      0        warn("unexpected_a", token);
    +
     2473.      0    }
    +
     2474.      0    return token;
    +
     2475.      0});
    +
     2476.      1constant("true", "boolean", true);
    +
     2477.      1constant("undefined", "undefined");
    +
     2478.      1
    +
     2479.      1assignment("=");
    +
     2480.      1assignment("+=");
    +
     2481.      1assignment("-=");
    +
     2482.      1assignment("*=");
    +
     2483.      1assignment("/=");
    +
     2484.      1assignment("%=");
    +
     2485.      1assignment("&=");
    +
     2486.      1assignment("|=");
    +
     2487.      1assignment("^=");
    +
     2488.      1assignment("<<=");
    +
     2489.      1assignment(">>=");
    +
     2490.      1assignment(">>>=");
    +
     2491.      1
    +
     2492.      1infix("??", 35);
    +
     2493.      1infix("||", 40);
    +
     2494.      1infix("&&", 50);
    +
     2495.      1infix("|", 70);
    +
     2496.      1infix("^", 80);
    +
     2497.      1infix("&", 90);
    +
     2498.      1infix("==", 100);
    +
     2499.      1infix("===", 100);
    +
     2500.      1infix("!=", 100);
    +
     2501.      1infix("!==", 100);
    +
     2502.      1infix("<", 110);
    +
     2503.      1infix(">", 110);
    +
     2504.      1infix("<=", 110);
    +
     2505.      1infix(">=", 110);
    +
     2506.      1infix("in", 110);
    +
     2507.      1infix("instanceof", 110);
    +
     2508.      1infix("<<", 120);
    +
     2509.      1infix(">>", 120);
    +
     2510.      1infix(">>>", 120);
    +
     2511.      1infix("+", 130);
    +
     2512.      1infix("-", 130);
    +
     2513.      1infix("*", 140);
    +
     2514.      1infix("/", 140);
    +
     2515.      1infix("%", 140);
    +
     2516.      1infixr("**", 150);
    +
     2517.   1124infix("(", 160, function (left) {
    +
     2518.   1124    const the_paren = token;
    +
     2519.   1124    let the_argument;
    +
     2520.   1099    if (left.id !== "function") {
    +
     2521.   1099        left_check(left, the_paren);
    +
     2522.   1099    }
    +
     2523.    385    if (functionage.arity === "statement" && left.identifier) {
    +
     2524.    304        functionage.name.calls[left.id] = left;
    +
     2525.    304    }
    +
     2526.   1124    the_paren.expression = [left];
    +
     2527.    909    if (next_token.id !== ")") {
    +
     2528.   1602        (function next() {
    +
     2529.   1602            let ellipsis;
    +
     2530.      0            if (next_token.id === "...") {
    +
     2531.      0                ellipsis = true;
    +
     2532.      0                advance("...");
    +
     2533.      0            }
    +
     2534.   1602            the_argument = expression(10);
    +
     2535.      0            if (ellipsis) {
    +
     2536.      0                the_argument.ellipsis = true;
    +
     2537.      0            }
    +
     2538.   1602            the_paren.expression.push(the_argument);
    +
     2539.    909            if (next_token.id === ",") {
    +
     2540.    909                advance(",");
    +
     2541.    909                return next();
    +
     2542.    909            }
    +
     2543.   1602        }());
    +
     2544.    909    }
    +
     2545.   1124    advance(")", the_paren);
    +
     2546.    494    if (the_paren.expression.length === 2) {
    +
     2547.    494        the_paren.free = true;
    +
     2548.      0        if (the_argument.wrapped === true) {
    +
     2549.      0            warn("unexpected_a", the_paren);
    +
     2550.      0        }
    +
     2551.    494        if (the_argument.id === "(") {
    +
     2552.    494            the_argument.wrapped = true;
    +
     2553.    494        }
    +
     2554.    630    } else {
    +
     2555.    630        the_paren.free = false;
    +
     2556.    630    }
    +
     2557.   1124    return the_paren;
    +
     2558.   1124});
    +
     2559.   1327infix(".", 170, function (left) {
    +
     2560.   1327    const the_token = token;
    +
     2561.   1327    const name = next_token;
    +
     2562.   1327    if (
    +
     2563.   1327        (
    +
     2564.   1327            left.id !== "(string)"
    +
     2565.      0            || (name.id !== "indexOf" && name.id !== "repeat")
    +
     2566.   1327        )
    +
     2567.   1325        && (
    +
     2568.   1325            left.id !== "["
    +
     2569.   1325            || (
    +
     2570.   1325                name.id !== "concat"
    +
     2571.   1325                && name.id !== "forEach"
    +
     2572.   1325                && name.id !== "join"
    +
     2573.   1325                && name.id !== "map"
    +
     2574.   1325            )
    +
     2575.   1325        )
    +
     2576.      0        && (left.id !== "+" || name.id !== "slice")
    +
     2577.   1324        && (
    +
     2578.   1324            left.id !== "(regexp)"
    +
     2579.      0            || (name.id !== "exec" && name.id !== "test")
    +
     2580.   1324        )
    +
     2581.   1324    ) {
    +
     2582.   1324        left_check(left, the_token);
    +
     2583.   1324    }
    +
     2584.      0    if (!name.identifier) {
    +
     2585.      0        stop("expected_identifier_a");
    +
     2586.      0    }
    +
     2587.   1327    advance();
    +
     2588.   1327    survey(name);
    +
     2589.   1327
    +
     2590.   1327// The property name is not an expression.
    +
     2591.   1327
    +
     2592.   1327    the_token.name = name;
    +
     2593.   1327    the_token.expression = left;
    +
     2594.   1327    return the_token;
    +
     2595.   1327});
    +
     2596.      0infix("?.", 170, function (left) {
    +
     2597.      0    const the_token = token;
    +
     2598.      0    const name = next_token;
    +
     2599.      0    if (
    +
     2600.      0        (
    +
     2601.      0            left.id !== "(string)"
    +
     2602.      0            || (name.id !== "indexOf" && name.id !== "repeat")
    +
     2603.      0        )
    +
     2604.      0        && (
    +
     2605.      0            left.id !== "["
    +
     2606.      0            || (
    +
     2607.      0                name.id !== "concat"
    +
     2608.      0                && name.id !== "forEach"
    +
     2609.      0                && name.id !== "join"
    +
     2610.      0                && name.id !== "map"
    +
     2611.      0            )
    +
     2612.      0        )
    +
     2613.      0        && (left.id !== "+" || name.id !== "slice")
    +
     2614.      0        && (
    +
     2615.      0            left.id !== "(regexp)"
    +
     2616.      0            || (name.id !== "exec" && name.id !== "test")
    +
     2617.      0        )
    +
     2618.      0    ) {
    +
     2619.      0        left_check(left, the_token);
    +
     2620.      0    }
    +
     2621.      0    if (!name.identifier) {
    +
     2622.      0        stop("expected_identifier_a");
    +
     2623.      0    }
    +
     2624.      0    advance();
    +
     2625.      0    survey(name);
    +
     2626.      0
    +
     2627.      0// The property name is not an expression.
    +
     2628.      0
    +
     2629.      0    the_token.name = name;
    +
     2630.      0    the_token.expression = left;
    +
     2631.      0    return the_token;
    +
     2632.      0});
    +
     2633.    181infix("[", 170, function (left) {
    +
     2634.    181    const the_token = token;
    +
     2635.    181    const the_subscript = expression(0);
    +
     2636.    180    if (the_subscript.id === "(string)" || the_subscript.id === "`") {
    +
     2637.      1        const name = survey(the_subscript);
    +
     2638.      0        if (rx_identifier.test(name)) {
    +
     2639.      0            warn("subscript_a", the_subscript, name);
    +
     2640.      0        }
    +
     2641.      1    }
    +
     2642.    181    left_check(left, the_token);
    +
     2643.    181    the_token.expression = [left, the_subscript];
    +
     2644.    181    advance("]");
    +
     2645.    181    return the_token;
    +
     2646.    181});
    +
     2647.      0infix("=>", 170, function (left) {
    +
     2648.      0    return stop("wrap_parameter", left);
    +
     2649.      0});
    +
     2650.      1
    +
     2651.     12function do_tick() {
    +
     2652.     12    const the_tick = token;
    +
     2653.     12    the_tick.value = [];
    +
     2654.     12    the_tick.expression = [];
    +
     2655.     12    if (next_token.id !== "`") {
    +
     2656.     12        (function part() {
    +
     2657.     12            advance("(string)");
    +
     2658.     12            the_tick.value.push(token);
    +
     2659.      0            if (next_token.id === "${") {
    +
     2660.      0                advance("${");
    +
     2661.      0                the_tick.expression.push(expression(0));
    +
     2662.      0                advance("}");
    +
     2663.      0                return part();
    +
     2664.      0            }
    +
     2665.     12        }());
    +
     2666.     12    }
    +
     2667.     12    advance("`");
    +
     2668.     12    return the_tick;
    +
     2669.     12}
    +
     2670.      1
    +
     2671.     12infix("`", 160, function (left) {
    +
     2672.     12    const the_tick = do_tick();
    +
     2673.     12    left_check(left, the_tick);
    +
     2674.     12    the_tick.expression = [left].concat(the_tick.expression);
    +
     2675.     12    return the_tick;
    +
     2676.     12});
    +
     2677.      1
    +
     2678.      1post("++");
    +
     2679.      1post("--");
    +
     2680.      1pre("++");
    +
     2681.      1pre("--");
    +
     2682.      1
    +
     2683.      1prefix("+");
    +
     2684.      1prefix("-");
    +
     2685.      1prefix("~");
    +
     2686.      1prefix("!");
    +
     2687.      1prefix("!!");
    +
     2688.     52prefix("[", function () {
    +
     2689.     52    const the_token = token;
    +
     2690.     52    the_token.expression = [];
    +
     2691.     22    if (next_token.id !== "]") {
    +
     2692.    208        (function next() {
    +
     2693.    208            let element;
    +
     2694.    208            let ellipsis = false;
    +
     2695.      0            if (next_token.id === "...") {
    +
     2696.      0                ellipsis = true;
    +
     2697.      0                advance("...");
    +
     2698.      0            }
    +
     2699.    208            element = expression(10);
    +
     2700.      0            if (ellipsis) {
    +
     2701.      0                element.ellipsis = true;
    +
     2702.      0            }
    +
     2703.    208            the_token.expression.push(element);
    +
     2704.    186            if (next_token.id === ",") {
    +
     2705.    186                advance(",");
    +
     2706.    186                return next();
    +
     2707.    186            }
    +
     2708.    208        }());
    +
     2709.     22    }
    +
     2710.     52    advance("]");
    +
     2711.     52    return the_token;
    +
     2712.     52});
    +
     2713.      0prefix("/=", function () {
    +
     2714.      0    stop("expected_a_b", token, "/\\=", "/=");
    +
     2715.      0});
    +
     2716.      0prefix("=>", function () {
    +
     2717.      0    return stop("expected_a_before_b", token, "()", "=>");
    +
     2718.      0});
    +
     2719.      1prefix("new", function () {
    +
     2720.      1    const the_new = token;
    +
     2721.      1    const right = expression(160);
    +
     2722.      0    if (next_token.id !== "(") {
    +
     2723.      0        warn("expected_a_before_b", next_token, "()", artifact(next_token));
    +
     2724.      0    }
    +
     2725.      1    the_new.expression = right;
    +
     2726.      1    return the_new;
    +
     2727.      1});
    +
     2728.      1prefix("typeof");
    +
     2729.      0prefix("void", function () {
    +
     2730.      0    const the_void = token;
    +
     2731.      0    warn("unexpected_a", the_void);
    +
     2732.      0    the_void.expression = expression(0);
    +
     2733.      0    return the_void;
    +
     2734.      0});
    +
     2735.      1
    +
     2736.    212function parameter_list() {
    +
     2737.    212    const list = [];
    +
     2738.    212    let optional;
    +
     2739.    212    const signature = ["("];
    +
     2740.    118    if (next_token.id !== ")" && next_token.id !== "(end)") {
    +
     2741.    169        (function parameter() {
    +
     2742.    169            let ellipsis = false;
    +
     2743.    169            let param;
    +
     2744.      0            if (next_token.id === "{") {
    +
     2745.      0                if (optional !== undefined) {
    +
     2746.      0                    warn(
    +
     2747.      0                        "required_a_optional_b",
    +
     2748.      0                        next_token,
    +
     2749.      0                        next_token.id,
    +
     2750.      0                        optional.id
    +
     2751.      0                    );
    +
     2752.      0                }
    +
     2753.      0                param = next_token;
    +
     2754.      0                param.names = [];
    +
     2755.      0                advance("{");
    +
     2756.      0                signature.push("{");
    +
     2757.      0                (function subparameter() {
    +
     2758.      0                    let subparam = next_token;
    +
     2759.      0                    if (!subparam.identifier) {
    +
     2760.      0                        return stop("expected_identifier_a");
    +
     2761.      0                    }
    +
     2762.      0                    survey(subparam);
    +
     2763.      0                    advance();
    +
     2764.      0                    signature.push(subparam.id);
    +
     2765.      0                    if (next_token.id === ":") {
    +
     2766.      0                        advance(":");
    +
     2767.      0                        advance();
    +
     2768.      0                        token.label = subparam;
    +
     2769.      0                        subparam = token;
    +
     2770.      0                        if (!subparam.identifier) {
    +
     2771.      0                            return stop("expected_identifier_a");
    +
     2772.      0                        }
    +
     2773.      0                    }
    +
     2774.      0                    if (next_token.id === "=") {
    +
     2775.      0                        advance("=");
    +
     2776.      0                        subparam.expression = expression();
    +
     2777.      0                        param.open = true;
    +
     2778.      0                    }
    +
     2779.      0                    param.names.push(subparam);
    +
     2780.      0                    if (next_token.id === ",") {
    +
     2781.      0                        advance(",");
    +
     2782.      0                        signature.push(", ");
    +
     2783.      0                        return subparameter();
    +
     2784.      0                    }
    +
     2785.      0                }());
    +
     2786.      0                list.push(param);
    +
     2787.      0                advance("}");
    +
     2788.      0                signature.push("}");
    +
     2789.      0                if (next_token.id === ",") {
    +
     2790.      0                    advance(",");
    +
     2791.      0                    signature.push(", ");
    +
     2792.      0                    return parameter();
    +
     2793.      0                }
    +
     2794.      0            } else if (next_token.id === "[") {
    +
     2795.      0                if (optional !== undefined) {
    +
     2796.      0                    warn(
    +
     2797.      0                        "required_a_optional_b",
    +
     2798.      0                        next_token,
    +
     2799.      0                        next_token.id,
    +
     2800.      0                        optional.id
    +
     2801.      0                    );
    +
     2802.      0                }
    +
     2803.      0                param = next_token;
    +
     2804.      0                param.names = [];
    +
     2805.      0                advance("[");
    +
     2806.      0                signature.push("[]");
    +
     2807.      0                (function subparameter() {
    +
     2808.      0                    const subparam = next_token;
    +
     2809.      0                    if (!subparam.identifier) {
    +
     2810.      0                        return stop("expected_identifier_a");
    +
     2811.      0                    }
    +
     2812.      0                    advance();
    +
     2813.      0                    param.names.push(subparam);
    +
     2814.      0                    if (next_token.id === "=") {
    +
     2815.      0                        advance("=");
    +
     2816.      0                        subparam.expression = expression();
    +
     2817.      0                        param.open = true;
    +
     2818.      0                    }
    +
     2819.      0                    if (next_token.id === ",") {
    +
     2820.      0                        advance(",");
    +
     2821.      0                        return subparameter();
    +
     2822.      0                    }
    +
     2823.      0                }());
    +
     2824.      0                list.push(param);
    +
     2825.      0                advance("]");
    +
     2826.      0                if (next_token.id === ",") {
    +
     2827.      0                    advance(",");
    +
     2828.      0                    signature.push(", ");
    +
     2829.      0                    return parameter();
    +
     2830.      0                }
    +
     2831.      0            } else {
    +
     2832.      0                if (next_token.id === "...") {
    +
     2833.      0                    ellipsis = true;
    +
     2834.      0                    signature.push("...");
    +
     2835.      0                    advance("...");
    +
     2836.      0                    if (optional !== undefined) {
    +
     2837.      0                        warn(
    +
     2838.      0                            "required_a_optional_b",
    +
     2839.      0                            next_token,
    +
     2840.      0                            next_token.id,
    +
     2841.      0                            optional.id
    +
     2842.      0                        );
    +
     2843.      0                    }
    +
     2844.      0                }
    +
     2845.      0                if (!next_token.identifier) {
    +
     2846.      0                    return stop("expected_identifier_a");
    +
     2847.      0                }
    +
     2848.    169                param = next_token;
    +
     2849.    169                list.push(param);
    +
     2850.    169                advance();
    +
     2851.    169                signature.push(param.id);
    +
     2852.      0                if (ellipsis) {
    +
     2853.      0                    param.ellipsis = true;
    +
     2854.      0                } else {
    +
     2855.    118                    if (next_token.id === "=") {
    +
     2856.    118                        optional = param;
    +
     2857.    118                        advance("=");
    +
     2858.    118                        param.expression = expression(0);
    +
     2859.    164                    } else {
    +
     2860.      0                        if (optional !== undefined) {
    +
     2861.      0                            warn(
    +
     2862.      0                                "required_a_optional_b",
    +
     2863.      0                                param,
    +
     2864.      0                                param.id,
    +
     2865.      0                                optional.id
    +
     2866.      0                            );
    +
     2867.      0                        }
    +
     2868.    164                    }
    +
     2869.    118                    if (next_token.id === ",") {
    +
     2870.    118                        advance(",");
    +
     2871.    118                        signature.push(", ");
    +
     2872.    118                        return parameter();
    +
     2873.    118                    }
    +
     2874.    169                }
    +
     2875.    169            }
    +
     2876.    169        }());
    +
     2877.    118    }
    +
     2878.    212    advance(")");
    +
     2879.    212    signature.push(")");
    +
     2880.    212    return [list, signature.join("")];
    +
     2881.    212}
    +
     2882.      1
    +
     2883.    212function do_function(the_function) {
    +
     2884.    212    let name;
    +
     2885.    212    if (the_function === undefined) {
    +
     2886.    212        the_function = token;
    +
     2887.    212
    +
     2888.    212// A function statement must have a name that will be in the parent's scope.
    +
     2889.    212
    +
     2890.     93        if (the_function.arity === "statement") {
    +
     2891.      0            if (!next_token.identifier) {
    +
     2892.      0                return stop("expected_identifier_a", next_token);
    +
     2893.      0            }
    +
     2894.     93            name = next_token;
    +
     2895.     93            enroll(name, "variable", true);
    +
     2896.     93            the_function.name = name;
    +
     2897.     93            name.init = true;
    +
     2898.     93            name.calls = empty();
    +
     2899.     93            advance();
    +
     2900.    119        } else if (name === undefined) {
    +
     2901.    119
    +
     2902.    119// A function expression may have an optional name.
    +
     2903.    119
    +
     2904.    119            if (next_token.identifier) {
    +
     2905.    119                name = next_token;
    +
     2906.    119                the_function.name = name;
    +
     2907.    119                advance();
    +
     2908.    119            } else {
    +
     2909.    119                the_function.name = anon;
    +
     2910.    119            }
    +
     2911.    119        }
    +
     2912.      0    } else {
    +
     2913.      0        name = the_function.name;
    +
     2914.      0    }
    +
     2915.    212    the_function.level = functionage.level + 1;
    +
     2916.      0    if (mega_mode) {
    +
     2917.      0        warn("unexpected_a", the_function);
    +
     2918.      0    }
    +
     2919.    212
    +
     2920.    212// Don't make functions in loops. It is inefficient, and it can lead to scoping
    +
     2921.    212// errors.
    +
     2922.    212
    +
     2923.      0    if (functionage.loop > 0) {
    +
     2924.      0        warn("function_in_loop", the_function);
    +
     2925.      0    }
    +
     2926.    212
    +
     2927.    212// Give the function properties for storing its names and for observing the
    +
     2928.    212// depth of loops and switches.
    +
     2929.    212
    +
     2930.    212    the_function.context = empty();
    +
     2931.    212    the_function.finally = 0;
    +
     2932.    212    the_function.loop = 0;
    +
     2933.    212    the_function.switch = 0;
    +
     2934.    212    the_function.try = 0;
    +
     2935.    212
    +
     2936.    212// Push the current function context and establish a new one.
    +
     2937.    212
    +
     2938.    212    stack.push(functionage);
    +
     2939.    212    functions.push(the_function);
    +
     2940.    212    functionage = the_function;
    +
     2941.    119    if (the_function.arity !== "statement" && typeof name === "object") {
    +
     2942.     27        enroll(name, "function", true);
    +
     2943.     27        name.dead = false;
    +
     2944.     27        name.init = true;
    +
     2945.     27        name.used = 1;
    +
     2946.     27    }
    +
     2947.    212
    +
     2948.    212// Parse the parameter list.
    +
     2949.    212
    +
     2950.    212    advance("(");
    +
     2951.    212    token.free = false;
    +
     2952.    212    token.arity = "function";
    +
     2953.    212    [functionage.parameters, functionage.signature] = parameter_list();
    +
     2954.    169    functionage.parameters.forEach(function enroll_parameter(name) {
    +
     2955.    169        if (name.identifier) {
    +
     2956.    169            enroll(name, "parameter", false);
    +
     2957.      0        } else {
    +
     2958.      0            name.names.forEach(enroll_parameter);
    +
     2959.      0        }
    +
     2960.    169    });
    +
     2961.    212
    +
     2962.    212// The function's body is a block.
    +
     2963.    212
    +
     2964.    212    the_function.block = block("body");
    +
     2965.    212    if (
    +
     2966.    212        the_function.arity === "statement"
    +
     2967.     93        && next_token.line === token.line
    +
     2968.      0    ) {
    +
     2969.      0        return stop("unexpected_a", next_token);
    +
     2970.      0    }
    +
     2971.    212    if (
    +
     2972.    212        next_token.id === "."
    +
     2973.    212        || next_token.id === "?."
    +
     2974.    212        || next_token.id === "["
    +
     2975.      0    ) {
    +
     2976.      0        warn("unexpected_a");
    +
     2977.      0    }
    +
     2978.    212
    +
     2979.    212// Restore the previous context.
    +
     2980.    212
    +
     2981.    212    functionage = stack.pop();
    +
     2982.    212    return the_function;
    +
     2983.    212}
    +
     2984.      1
    +
     2985.      1prefix("function", do_function);
    +
     2986.      1
    +
     2987.      0function fart(pl) {
    +
     2988.      0    advance("=>");
    +
     2989.      0    const the_fart = token;
    +
     2990.      0    the_fart.arity = "binary";
    +
     2991.      0    the_fart.name = "=>";
    +
     2992.      0    the_fart.level = functionage.level + 1;
    +
     2993.      0    functions.push(the_fart);
    +
     2994.      0    if (functionage.loop > 0) {
    +
     2995.      0        warn("function_in_loop", the_fart);
    +
     2996.      0    }
    +
     2997.      0
    +
     2998.      0// Give the function properties storing its names and for observing the depth
    +
     2999.      0// of loops and switches.
    +
     3000.      0
    +
     3001.      0    the_fart.context = empty();
    +
     3002.      0    the_fart.finally = 0;
    +
     3003.      0    the_fart.loop = 0;
    +
     3004.      0    the_fart.switch = 0;
    +
     3005.      0    the_fart.try = 0;
    +
     3006.      0
    +
     3007.      0// Push the current function context and establish a new one.
    +
     3008.      0
    +
     3009.      0    stack.push(functionage);
    +
     3010.      0    functionage = the_fart;
    +
     3011.      0    the_fart.parameters = pl[0];
    +
     3012.      0    the_fart.signature = pl[1];
    +
     3013.      0    the_fart.parameters.forEach(function (name) {
    +
     3014.      0        enroll(name, "parameter", true);
    +
     3015.      0    });
    +
     3016.      0    if (next_token.id === "{") {
    +
     3017.      0        warn("expected_a_b", the_fart, "function", "=>");
    +
     3018.      0        the_fart.block = block("body");
    +
     3019.      0    } else {
    +
     3020.      0        the_fart.expression = expression(0);
    +
     3021.      0    }
    +
     3022.      0    functionage = stack.pop();
    +
     3023.      0    return the_fart;
    +
     3024.      0}
    +
     3025.      1
    +
     3026.    116prefix("(", function () {
    +
     3027.    116    const the_paren = token;
    +
     3028.    116    let the_value;
    +
     3029.    116    const cadet = lookahead().id;
    +
     3030.    116
    +
     3031.    116// We can distinguish between a parameter list for => and a wrapped expression
    +
     3032.    116// with one token of lookahead.
    +
     3033.    116
    +
     3034.    116    if (
    +
     3035.    116        next_token.id === ")"
    +
     3036.    116        || next_token.id === "..."
    +
     3037.    101        || (next_token.identifier && (cadet === "," || cadet === "="))
    +
     3038.      0    ) {
    +
     3039.      0        the_paren.free = false;
    +
     3040.      0        return fart(parameter_list());
    +
     3041.      0    }
    +
     3042.    116    the_paren.free = true;
    +
     3043.    116    the_value = expression(0);
    +
     3044.      0    if (the_value.wrapped === true) {
    +
     3045.      0        warn("unexpected_a", the_paren);
    +
     3046.      0    }
    +
     3047.    116    the_value.wrapped = true;
    +
     3048.    116    advance(")", the_paren);
    +
     3049.      0    if (next_token.id === "=>") {
    +
     3050.      0        if (the_value.arity !== "variable") {
    +
     3051.      0            if (the_value.id === "{" || the_value.id === "[") {
    +
     3052.      0                warn("expected_a_before_b", the_paren, "function", "(");
    +
     3053.      0                return stop("expected_a_b", next_token, "{", "=>");
    +
     3054.      0            }
    +
     3055.      0            return stop("expected_identifier_a", the_value);
    +
     3056.      0        }
    +
     3057.      0        the_paren.expression = [the_value];
    +
     3058.      0        return fart([the_paren.expression, "(" + the_value.id + ")"]);
    +
     3059.      0    }
    +
     3060.    116    return the_value;
    +
     3061.    116});
    +
     3062.      1prefix("`", do_tick);
    +
     3063.     11prefix("{", function () {
    +
     3064.     11    const the_brace = token;
    +
     3065.     11    const seen = empty();
    +
     3066.     11    the_brace.expression = [];
    +
     3067.     11    if (next_token.id !== "}") {
    +
     3068.    160        (function member() {
    +
     3069.    160            let extra;
    +
     3070.    160            let full;
    +
     3071.    160            let id;
    +
     3072.    160            let name = next_token;
    +
     3073.    160            let value;
    +
     3074.    160            advance();
    +
     3075.    160            if (
    +
     3076.    160                (name.id === "get" || name.id === "set")
    +
     3077.      0                && next_token.identifier
    +
     3078.      0            ) {
    +
     3079.      0                if (!option.getset) {
    +
     3080.      0                    warn("unexpected_a", name);
    +
     3081.      0                }
    +
     3082.      0                extra = name.id;
    +
     3083.      0                full = extra + " " + next_token.id;
    +
     3084.      0                name = next_token;
    +
     3085.      0                advance();
    +
     3086.      0                id = survey(name);
    +
     3087.      0                if (seen[full] === true || seen[id] === true) {
    +
     3088.      0                    warn("duplicate_a", name);
    +
     3089.      0                }
    +
     3090.      0                seen[id] = false;
    +
     3091.      0                seen[full] = true;
    +
     3092.      0            } else {
    +
     3093.    160                id = survey(name);
    +
     3094.      0                if (typeof seen[id] === "boolean") {
    +
     3095.      0                    warn("duplicate_a", name);
    +
     3096.      0                }
    +
     3097.    160                seen[id] = true;
    +
     3098.    160            }
    +
     3099.    156            if (name.identifier) {
    +
     3100.    156                if (next_token.id === "}" || next_token.id === ",") {
    +
     3101.      0                    if (typeof extra === "string") {
    +
     3102.      0                        advance("(");
    +
     3103.      0                    }
    +
     3104.    156                    value = expression(Infinity, true);
    +
     3105.      0                } else if (next_token.id === "(") {
    +
     3106.      0                    value = do_function({
    +
     3107.      0                        arity: "unary",
    +
     3108.      0                        from: name.from,
    +
     3109.      0                        id: "function",
    +
     3110.      0                        line: name.line,
    +
     3111.      0                        name: (
    +
     3112.      0                            typeof extra === "string"
    +
     3113.      0                            ? extra
    +
     3114.      0                            : id
    +
     3115.      0                        ),
    +
     3116.      0                        thru: name.from
    +
     3117.      0                    });
    +
     3118.      0                } else {
    +
     3119.      0                    if (typeof extra === "string") {
    +
     3120.      0                        advance("(");
    +
     3121.      0                    }
    +
     3122.    156                    let the_colon = next_token;
    +
     3123.    156                    advance(":");
    +
     3124.    156                    value = expression(0);
    +
     3125.      0                    if (value.id === name.id && value.id !== "function") {
    +
     3126.      0                        warn("unexpected_a", the_colon, ": " + name.id);
    +
     3127.      0                    }
    +
     3128.    156                }
    +
     3129.    156                value.label = name;
    +
     3130.      0                if (typeof extra === "string") {
    +
     3131.      0                    value.extra = extra;
    +
     3132.      0                }
    +
     3133.    156                the_brace.expression.push(value);
    +
     3134.    156            } else {
    +
     3135.      4                advance(":");
    +
     3136.      4                value = expression(0);
    +
     3137.      4                value.label = name;
    +
     3138.      4                the_brace.expression.push(value);
    +
     3139.      4            }
    +
     3140.    149            if (next_token.id === ",") {
    +
     3141.    149                advance(",");
    +
     3142.    149                return member();
    +
     3143.    149            }
    +
     3144.    160        }());
    +
     3145.     11    }
    +
     3146.     11    advance("}");
    +
     3147.     11    return the_brace;
    +
     3148.     11});
    +
     3149.      1
    +
     3150.      0stmt(";", function () {
    +
     3151.      0    warn("unexpected_a", token);
    +
     3152.      0    return token;
    +
     3153.      0});
    +
     3154.      0stmt("{", function () {
    +
     3155.      0    warn("naked_block", token);
    +
     3156.      0    return block("naked");
    +
     3157.      0});
    +
     3158.      2stmt("break", function () {
    +
     3159.      2    const the_break = token;
    +
     3160.      2    let the_label;
    +
     3161.      2    if (
    +
     3162.      0        (functionage.loop < 1 && functionage.switch < 1)
    +
     3163.      2        || functionage.finally > 0
    +
     3164.      0    ) {
    +
     3165.      0        warn("unexpected_a", the_break);
    +
     3166.      0    }
    +
     3167.      2    the_break.disrupt = true;
    +
     3168.      0    if (next_token.identifier && token.line === next_token.line) {
    +
     3169.      0        the_label = functionage.context[next_token.id];
    +
     3170.      0        if (
    +
     3171.      0            the_label === undefined
    +
     3172.      0            || the_label.role !== "label"
    +
     3173.      0            || the_label.dead
    +
     3174.      0        ) {
    +
     3175.      0            warn(
    +
     3176.      0                (the_label !== undefined && the_label.dead)
    +
     3177.      0                ? "out_of_scope_a"
    +
     3178.      0                : "not_label_a"
    +
     3179.      0            );
    +
     3180.      0        } else {
    +
     3181.      0            the_label.used += 1;
    +
     3182.      0        }
    +
     3183.      0        the_break.label = next_token;
    +
     3184.      0        advance();
    +
     3185.      0    }
    +
     3186.      2    advance(";");
    +
     3187.      2    return the_break;
    +
     3188.      2});
    +
     3189.      1
    +
     3190.    279function do_var() {
    +
     3191.    279    const the_statement = token;
    +
     3192.    279    const is_const = the_statement.id === "const";
    +
     3193.    279    the_statement.names = [];
    +
     3194.    279
    +
     3195.    279// A program may use var or let, but not both.
    +
     3196.    279
    +
     3197.    125    if (!is_const) {
    +
     3198.    125        if (var_mode === undefined) {
    +
     3199.    125            var_mode = the_statement.id;
    +
     3200.      0        } else if (the_statement.id !== var_mode) {
    +
     3201.      0            warn(
    +
     3202.      0                "expected_a_b",
    +
     3203.      0                the_statement,
    +
     3204.      0                var_mode,
    +
     3205.      0                the_statement.id
    +
     3206.      0            );
    +
     3207.      0        }
    +
     3208.    125    }
    +
     3209.    279
    +
     3210.    279// We don't expect to see variables created in switch statements.
    +
     3211.    279
    +
     3212.      0    if (functionage.switch > 0) {
    +
     3213.      0        warn("var_switch", the_statement);
    +
     3214.      0    }
    +
     3215.      0    if (functionage.loop > 0 && the_statement.id === "var") {
    +
     3216.      0        warn("var_loop", the_statement);
    +
     3217.      0    }
    +
     3218.    279    (function next() {
    +
     3219.      0        if (next_token.id === "{" && the_statement.id !== "var") {
    +
     3220.      0            const the_brace = next_token;
    +
     3221.      0            advance("{");
    +
     3222.      0            (function pair() {
    +
     3223.      0                if (!next_token.identifier) {
    +
     3224.      0                    return stop("expected_identifier_a", next_token);
    +
     3225.      0                }
    +
     3226.      0                const name = next_token;
    +
     3227.      0                survey(name);
    +
     3228.      0                advance();
    +
     3229.      0                if (next_token.id === ":") {
    +
     3230.      0                    advance(":");
    +
     3231.      0                    if (!next_token.identifier) {
    +
     3232.      0                        return stop("expected_identifier_a", next_token);
    +
     3233.      0                    }
    +
     3234.      0                    next_token.label = name;
    +
     3235.      0                    the_statement.names.push(next_token);
    +
     3236.      0                    enroll(next_token, "variable", is_const);
    +
     3237.      0                    advance();
    +
     3238.      0                    the_brace.open = true;
    +
     3239.      0                } else {
    +
     3240.      0                    the_statement.names.push(name);
    +
     3241.      0                    enroll(name, "variable", is_const);
    +
     3242.      0                }
    +
     3243.      0                name.dead = false;
    +
     3244.      0                name.init = true;
    +
     3245.      0                if (next_token.id === "=") {
    +
     3246.      0                    advance("=");
    +
     3247.      0                    name.expression = expression();
    +
     3248.      0                    the_brace.open = true;
    +
     3249.      0                }
    +
     3250.      0                if (next_token.id === ",") {
    +
     3251.      0                    advance(",");
    +
     3252.      0                    return pair();
    +
     3253.      0                }
    +
     3254.      0            }());
    +
     3255.      0            advance("}");
    +
     3256.      0            advance("=");
    +
     3257.      0            the_statement.expression = expression(0);
    +
     3258.      0        } else if (next_token.id === "[" && the_statement.id !== "var") {
    +
     3259.      0            const the_bracket = next_token;
    +
     3260.      0            advance("[");
    +
     3261.      0            (function element() {
    +
     3262.      0                let ellipsis;
    +
     3263.      0                if (next_token.id === "...") {
    +
     3264.      0                    ellipsis = true;
    +
     3265.      0                    advance("...");
    +
     3266.      0                }
    +
     3267.      0                if (!next_token.identifier) {
    +
     3268.      0                    return stop("expected_identifier_a", next_token);
    +
     3269.      0                }
    +
     3270.      0                const name = next_token;
    +
     3271.      0                advance();
    +
     3272.      0                the_statement.names.push(name);
    +
     3273.      0                enroll(name, "variable", is_const);
    +
     3274.      0                name.dead = false;
    +
     3275.      0                name.init = true;
    +
     3276.      0                if (ellipsis) {
    +
     3277.      0                    name.ellipsis = true;
    +
     3278.      0                } else {
    +
     3279.      0                    if (next_token.id === "=") {
    +
     3280.      0                        advance("=");
    +
     3281.      0                        name.expression = expression();
    +
     3282.      0                        the_bracket.open = true;
    +
     3283.      0                    }
    +
     3284.      0                    if (next_token.id === ",") {
    +
     3285.      0                        advance(",");
    +
     3286.      0                        return element();
    +
     3287.      0                    }
    +
     3288.      0                }
    +
     3289.      0            }());
    +
     3290.      0            advance("]");
    +
     3291.      0            advance("=");
    +
     3292.      0            the_statement.expression = expression(0);
    +
     3293.      0        } else if (next_token.identifier) {
    +
     3294.    279            const name = next_token;
    +
     3295.    279            advance();
    +
     3296.      0            if (name.id === "ignore") {
    +
     3297.      0                warn("unexpected_a", name);
    +
     3298.      0            }
    +
     3299.    279            enroll(name, "variable", is_const);
    +
     3300.    189            if (next_token.id === "=" || is_const) {
    +
     3301.    189                advance("=");
    +
     3302.    189                name.dead = false;
    +
     3303.    189                name.init = true;
    +
     3304.    189                name.expression = expression(0);
    +
     3305.    189            }
    +
     3306.    279            the_statement.names.push(name);
    +
     3307.      0        } else {
    +
     3308.      0            return stop("expected_identifier_a", next_token);
    +
     3309.      0        }
    +
     3310.    279    }());
    +
     3311.    279    semicolon();
    +
     3312.    279    return the_statement;
    +
     3313.    279}
    +
     3314.      1
    +
     3315.      1stmt("const", do_var);
    +
     3316.      0stmt("continue", function () {
    +
     3317.      0    const the_continue = token;
    +
     3318.      0    if (functionage.loop < 1 || functionage.finally > 0) {
    +
     3319.      0        warn("unexpected_a", the_continue);
    +
     3320.      0    }
    +
     3321.      0    not_top_level(the_continue);
    +
     3322.      0    the_continue.disrupt = true;
    +
     3323.      0    warn("unexpected_a", the_continue);
    +
     3324.      0    advance(";");
    +
     3325.      0    return the_continue;
    +
     3326.      0});
    +
     3327.      0stmt("debugger", function () {
    +
     3328.      0    const the_debug = token;
    +
     3329.      0    if (!option.devel) {
    +
     3330.      0        warn("unexpected_a", the_debug);
    +
     3331.      0    }
    +
     3332.      0    semicolon();
    +
     3333.      0    return the_debug;
    +
     3334.      0});
    +
     3335.     12stmt("delete", function () {
    +
     3336.     12    const the_token = token;
    +
     3337.     12    const the_value = expression(0);
    +
     3338.     12    if (
    +
     3339.      0        (the_value.id !== "." && the_value.id !== "[")
    +
     3340.     12        || the_value.arity !== "binary"
    +
     3341.      0    ) {
    +
     3342.      0        stop("expected_a_b", the_value, ".", artifact(the_value));
    +
     3343.      0    }
    +
     3344.     12    the_token.expression = the_value;
    +
     3345.     12    semicolon();
    +
     3346.     12    return the_token;
    +
     3347.     12});
    +
     3348.      0stmt("do", function () {
    +
     3349.      0    const the_do = token;
    +
     3350.      0    not_top_level(the_do);
    +
     3351.      0    functionage.loop += 1;
    +
     3352.      0    the_do.block = block();
    +
     3353.      0    advance("while");
    +
     3354.      0    the_do.expression = condition();
    +
     3355.      0    semicolon();
    +
     3356.      0    if (the_do.block.disrupt === true) {
    +
     3357.      0        warn("weird_loop", the_do);
    +
     3358.      0    }
    +
     3359.      0    functionage.loop -= 1;
    +
     3360.      0    return the_do;
    +
     3361.      0});
    +
     3362.      1stmt("export", function () {
    +
     3363.      1    const the_export = token;
    +
     3364.      1    let the_id;
    +
     3365.      1    let the_name;
    +
     3366.      1    let the_thing;
    +
     3367.      1
    +
     3368.      0    function export_id() {
    +
     3369.      0        if (!next_token.identifier) {
    +
     3370.      0            stop("expected_identifier_a");
    +
     3371.      0        }
    +
     3372.      0        the_id = next_token.id;
    +
     3373.      0        the_name = global.context[the_id];
    +
     3374.      0        if (the_name === undefined) {
    +
     3375.      0            warn("unexpected_a");
    +
     3376.      0        } else {
    +
     3377.      0            the_name.used += 1;
    +
     3378.      0            if (exports[the_id] !== undefined) {
    +
     3379.      0                warn("duplicate_a");
    +
     3380.      0            }
    +
     3381.      0            exports[the_id] = the_name;
    +
     3382.      0        }
    +
     3383.      0        advance();
    +
     3384.      0        the_export.expression.push(the_thing);
    +
     3385.      0    }
    +
     3386.      1
    +
     3387.      1    the_export.expression = [];
    +
     3388.      1    if (next_token.id === "default") {
    +
     3389.      0        if (exports.default !== undefined) {
    +
     3390.      0            warn("duplicate_a");
    +
     3391.      0        }
    +
     3392.      1        advance("default");
    +
     3393.      1        the_thing = expression(0);
    +
     3394.      1        if (
    +
     3395.      1            the_thing.id !== "("
    +
     3396.      1            || the_thing.expression[0].id !== "."
    +
     3397.      1            || the_thing.expression[0].expression.id !== "Object"
    +
     3398.      1            || the_thing.expression[0].name.id !== "freeze"
    +
     3399.      0        ) {
    +
     3400.      0            warn("freeze_exports", the_thing);
    +
     3401.      0        }
    +
     3402.      1        if (next_token.id === ";") {
    +
     3403.      1            semicolon();
    +
     3404.      1        }
    +
     3405.      1        exports.default = the_thing;
    +
     3406.      1        the_export.expression.push(the_thing);
    +
     3407.      0    } else {
    +
     3408.      0        if (next_token.id === "function") {
    +
     3409.      0            warn("freeze_exports");
    +
     3410.      0            the_thing = statement();
    +
     3411.      0            the_name = the_thing.name;
    +
     3412.      0            the_id = the_name.id;
    +
     3413.      0            the_name.used += 1;
    +
     3414.      0            if (exports[the_id] !== undefined) {
    +
     3415.      0                warn("duplicate_a", the_name);
    +
     3416.      0            }
    +
     3417.      0            exports[the_id] = the_thing;
    +
     3418.      0            the_export.expression.push(the_thing);
    +
     3419.      0            the_thing.statement = false;
    +
     3420.      0            the_thing.arity = "unary";
    +
     3421.      0        } else if (
    +
     3422.      0            next_token.id === "var"
    +
     3423.      0            || next_token.id === "let"
    +
     3424.      0            || next_token.id === "const"
    +
     3425.      0        ) {
    +
     3426.      0            warn("unexpected_a", next_token);
    +
     3427.      0            statement();
    +
     3428.      0        } else if (next_token.id === "{") {
    +
     3429.      0            advance("{");
    +
     3430.      0            (function loop() {
    +
     3431.      0                export_id();
    +
     3432.      0                if (next_token.id === ",") {
    +
     3433.      0                    advance(",");
    +
     3434.      0                    return loop();
    +
     3435.      0                }
    +
     3436.      0            }());
    +
     3437.      0            advance("}");
    +
     3438.      0            semicolon();
    +
     3439.      0        } else {
    +
     3440.      0            stop("unexpected_a");
    +
     3441.      0        }
    +
     3442.      0    }
    +
     3443.      1    module_mode = true;
    +
     3444.      1    return the_export;
    +
     3445.      1});
    +
     3446.      0stmt("for", function () {
    +
     3447.      0    let first;
    +
     3448.      0    const the_for = token;
    +
     3449.      0    if (!option.for) {
    +
     3450.      0        warn("unexpected_a", the_for);
    +
     3451.      0    }
    +
     3452.      0    not_top_level(the_for);
    +
     3453.      0    functionage.loop += 1;
    +
     3454.      0    advance("(");
    +
     3455.      0    token.free = true;
    +
     3456.      0    if (next_token.id === ";") {
    +
     3457.      0        return stop("expected_a_b", the_for, "while (", "for (;");
    +
     3458.      0    }
    +
     3459.      0    if (
    +
     3460.      0        next_token.id === "var"
    +
     3461.      0        || next_token.id === "let"
    +
     3462.      0        || next_token.id === "const"
    +
     3463.      0    ) {
    +
     3464.      0        return stop("unexpected_a");
    +
     3465.      0    }
    +
     3466.      0    first = expression(0);
    +
     3467.      0    if (first.id === "in") {
    +
     3468.      0        if (first.expression[0].arity !== "variable") {
    +
     3469.      0            warn("bad_assignment_a", first.expression[0]);
    +
     3470.      0        }
    +
     3471.      0        the_for.name = first.expression[0];
    +
     3472.      0        the_for.expression = first.expression[1];
    +
     3473.      0        warn("expected_a_b", the_for, "Object.keys", "for in");
    +
     3474.      0    } else {
    +
     3475.      0        the_for.initial = first;
    +
     3476.      0        advance(";");
    +
     3477.      0        the_for.expression = expression(0);
    +
     3478.      0        advance(";");
    +
     3479.      0        the_for.inc = expression(0);
    +
     3480.      0        if (the_for.inc.id === "++") {
    +
     3481.      0            warn("expected_a_b", the_for.inc, "+= 1", "++");
    +
     3482.      0        }
    +
     3483.      0    }
    +
     3484.      0    advance(")");
    +
     3485.      0    the_for.block = block();
    +
     3486.      0    if (the_for.block.disrupt === true) {
    +
     3487.      0        warn("weird_loop", the_for);
    +
     3488.      0    }
    +
     3489.      0    functionage.loop -= 1;
    +
     3490.      0    return the_for;
    +
     3491.      0});
    +
     3492.      1stmt("function", do_function);
    +
     3493.    557stmt("if", function () {
    +
     3494.    557    let the_else;
    +
     3495.    557    const the_if = token;
    +
     3496.    557    the_if.expression = condition();
    +
     3497.    557    the_if.block = block();
    +
     3498.    143    if (next_token.id === "else") {
    +
     3499.    143        advance("else");
    +
     3500.    143        the_else = token;
    +
     3501.    143        the_if.else = (
    +
     3502.    143            next_token.id === "if"
    +
     3503.    143            ? statement()
    +
     3504.    143            : block()
    +
     3505.    143        );
    +
     3506.    143        if (the_if.block.disrupt === true) {
    +
     3507.    143            if (the_if.else.disrupt === true) {
    +
     3508.    143                the_if.disrupt = true;
    +
     3509.      0            } else {
    +
     3510.      0                warn("unexpected_a", the_else);
    +
     3511.      0            }
    +
     3512.    143        }
    +
     3513.    143    }
    +
     3514.    557    return the_if;
    +
     3515.    557});
    +
     3516.      0stmt("import", function () {
    +
     3517.      0    const the_import = token;
    +
     3518.      0    if (next_token.id === "(") {
    +
     3519.      0        the_import.arity = "unary";
    +
     3520.      0        the_import.constant = true;
    +
     3521.      0        the_import.statement = false;
    +
     3522.      0        advance("(");
    +
     3523.      0        const string = expression(0);
    +
     3524.      0        if (string.id !== "(string)") {
    +
     3525.      0            warn("expected_string_a", string);
    +
     3526.      0        }
    +
     3527.      0        froms.push(token.value);
    +
     3528.      0        advance(")");
    +
     3529.      0        advance(".");
    +
     3530.      0        advance("then");
    +
     3531.      0        advance("(");
    +
     3532.      0        the_import.expression = expression(0);
    +
     3533.      0        advance(")");
    +
     3534.      0        semicolon();
    +
     3535.      0        return the_import;
    +
     3536.      0    }
    +
     3537.      0    let name;
    +
     3538.      0    if (typeof module_mode === "object") {
    +
     3539.      0        warn("unexpected_directive_a", module_mode, module_mode.directive);
    +
     3540.      0    }
    +
     3541.      0    module_mode = true;
    +
     3542.      0    if (next_token.identifier) {
    +
     3543.      0        name = next_token;
    +
     3544.      0        advance();
    +
     3545.      0        if (name.id === "ignore") {
    +
     3546.      0            warn("unexpected_a", name);
    +
     3547.      0        }
    +
     3548.      0        enroll(name, "variable", true);
    +
     3549.      0        the_import.name = name;
    +
     3550.      0    } else {
    +
     3551.      0        const names = [];
    +
     3552.      0        advance("{");
    +
     3553.      0        if (next_token.id !== "}") {
    +
     3554.      0            while (true) {
    +
     3555.      0                if (!next_token.identifier) {
    +
     3556.      0                    stop("expected_identifier_a");
    +
     3557.      0                }
    +
     3558.      0                name = next_token;
    +
     3559.      0                advance();
    +
     3560.      0                if (name.id === "ignore") {
    +
     3561.      0                    warn("unexpected_a", name);
    +
     3562.      0                }
    +
     3563.      0                enroll(name, "variable", true);
    +
     3564.      0                names.push(name);
    +
     3565.      0                if (next_token.id !== ",") {
    +
     3566.      0                    break;
    +
     3567.      0                }
    +
     3568.      0                advance(",");
    +
     3569.      0            }
    +
     3570.      0        }
    +
     3571.      0        advance("}");
    +
     3572.      0        the_import.name = names;
    +
     3573.      0    }
    +
     3574.      0    advance("from");
    +
     3575.      0    advance("(string)");
    +
     3576.      0    the_import.import = token;
    +
     3577.      0    if (!rx_module.test(token.value)) {
    +
     3578.      0        warn("bad_module_name_a", token);
    +
     3579.      0    }
    +
     3580.      0    froms.push(token.value);
    +
     3581.      0    semicolon();
    +
     3582.      0    return the_import;
    +
     3583.      0});
    +
     3584.      1stmt("let", do_var);
    +
     3585.    226stmt("return", function () {
    +
     3586.    226    const the_return = token;
    +
     3587.    226    not_top_level(the_return);
    +
     3588.      0    if (functionage.finally > 0) {
    +
     3589.      0        warn("unexpected_a", the_return);
    +
     3590.      0    }
    +
     3591.    226    the_return.disrupt = true;
    +
     3592.    220    if (next_token.id !== ";" && the_return.line === next_token.line) {
    +
     3593.    220        the_return.expression = expression(10);
    +
     3594.    220    }
    +
     3595.    226    advance(";");
    +
     3596.    226    return the_return;
    +
     3597.    226});
    +
     3598.      0stmt("switch", function () {
    +
     3599.      0    let dups = [];
    +
     3600.      0    let last;
    +
     3601.      0    let stmts;
    +
     3602.      0    const the_cases = [];
    +
     3603.      0    let the_disrupt = true;
    +
     3604.      0    const the_switch = token;
    +
     3605.      0    not_top_level(the_switch);
    +
     3606.      0    if (functionage.finally > 0) {
    +
     3607.      0        warn("unexpected_a", the_switch);
    +
     3608.      0    }
    +
     3609.      0    functionage.switch += 1;
    +
     3610.      0    advance("(");
    +
     3611.      0    token.free = true;
    +
     3612.      0    the_switch.expression = expression(0);
    +
     3613.      0    the_switch.block = the_cases;
    +
     3614.      0    advance(")");
    +
     3615.      0    advance("{");
    +
     3616.      0    (function major() {
    +
     3617.      0        const the_case = next_token;
    +
     3618.      0        the_case.arity = "statement";
    +
     3619.      0        the_case.expression = [];
    +
     3620.      0        (function minor() {
    +
     3621.      0            advance("case");
    +
     3622.      0            token.switch = true;
    +
     3623.      0            const exp = expression(0);
    +
     3624.      0            if (dups.some(function (thing) {
    +
     3625.      0                return are_similar(thing, exp);
    +
     3626.      0            })) {
    +
     3627.      0                warn("unexpected_a", exp);
    +
     3628.      0            }
    +
     3629.      0            dups.push(exp);
    +
     3630.      0            the_case.expression.push(exp);
    +
     3631.      0            advance(":");
    +
     3632.      0            if (next_token.id === "case") {
    +
     3633.      0                return minor();
    +
     3634.      0            }
    +
     3635.      0        }());
    +
     3636.      0        stmts = statements();
    +
     3637.      0        if (stmts.length < 1) {
    +
     3638.      0            warn("expected_statements_a");
    +
     3639.      0            return;
    +
     3640.      0        }
    +
     3641.      0        the_case.block = stmts;
    +
     3642.      0        the_cases.push(the_case);
    +
     3643.      0        last = stmts[stmts.length - 1];
    +
     3644.      0        if (last.disrupt) {
    +
     3645.      0            if (last.id === "break" && last.label === undefined) {
    +
     3646.      0                the_disrupt = false;
    +
     3647.      0            }
    +
     3648.      0        } else {
    +
     3649.      0            warn(
    +
     3650.      0                "expected_a_before_b",
    +
     3651.      0                next_token,
    +
     3652.      0                "break;",
    +
     3653.      0                artifact(next_token)
    +
     3654.      0            );
    +
     3655.      0        }
    +
     3656.      0        if (next_token.id === "case") {
    +
     3657.      0            return major();
    +
     3658.      0        }
    +
     3659.      0    }());
    +
     3660.      0    dups = undefined;
    +
     3661.      0    if (next_token.id === "default") {
    +
     3662.      0        const the_default = next_token;
    +
     3663.      0        advance("default");
    +
     3664.      0        token.switch = true;
    +
     3665.      0        advance(":");
    +
     3666.      0        the_switch.else = statements();
    +
     3667.      0        if (the_switch.else.length < 1) {
    +
     3668.      0            warn("unexpected_a", the_default);
    +
     3669.      0            the_disrupt = false;
    +
     3670.      0        } else {
    +
     3671.      0            const the_last = the_switch.else[the_switch.else.length - 1];
    +
     3672.      0            if (the_last.id === "break" && the_last.label === undefined) {
    +
     3673.      0                warn("unexpected_a", the_last);
    +
     3674.      0                the_last.disrupt = false;
    +
     3675.      0            }
    +
     3676.      0            the_disrupt = the_disrupt && the_last.disrupt;
    +
     3677.      0        }
    +
     3678.      0    } else {
    +
     3679.      0        the_disrupt = false;
    +
     3680.      0    }
    +
     3681.      0    advance("}", the_switch);
    +
     3682.      0    functionage.switch -= 1;
    +
     3683.      0    the_switch.disrupt = the_disrupt;
    +
     3684.      0    return the_switch;
    +
     3685.      0});
    +
     3686.      2stmt("throw", function () {
    +
     3687.      2    const the_throw = token;
    +
     3688.      2    the_throw.disrupt = true;
    +
     3689.      2    the_throw.expression = expression(10);
    +
     3690.      2    semicolon();
    +
     3691.      0    if (functionage.try > 0) {
    +
     3692.      0        warn("unexpected_a", the_throw);
    +
     3693.      0    }
    +
     3694.      2    return the_throw;
    +
     3695.      2});
    +
     3696.      1stmt("try", function () {
    +
     3697.      1    let the_catch;
    +
     3698.      1    let the_disrupt;
    +
     3699.      1    const the_try = token;
    +
     3700.      0    if (functionage.try > 0) {
    +
     3701.      0        warn("unexpected_a", the_try);
    +
     3702.      0    }
    +
     3703.      1    functionage.try += 1;
    +
     3704.      1    the_try.block = block();
    +
     3705.      1    the_disrupt = the_try.block.disrupt;
    +
     3706.      1    if (next_token.id === "catch") {
    +
     3707.      1        let ignored = "ignore";
    +
     3708.      1        the_catch = next_token;
    +
     3709.      1        the_try.catch = the_catch;
    +
     3710.      1        advance("catch");
    +
     3711.      1        if (next_token.id === "(") {
    +
     3712.      1            advance("(");
    +
     3713.      0            if (!next_token.identifier) {
    +
     3714.      0                return stop("expected_identifier_a", next_token);
    +
     3715.      0            }
    +
     3716.      1            if (next_token.id !== "ignore") {
    +
     3717.      1                ignored = undefined;
    +
     3718.      1                the_catch.name = next_token;
    +
     3719.      1                enroll(next_token, "exception", true);
    +
     3720.      1            }
    +
     3721.      1            advance();
    +
     3722.      1            advance(")");
    +
     3723.      1        }
    +
     3724.      1        the_catch.block = block(ignored);
    +
     3725.      1        if (the_catch.block.disrupt !== true) {
    +
     3726.      1            the_disrupt = false;
    +
     3727.      1        }
    +
     3728.      0    } else {
    +
     3729.      0        warn(
    +
     3730.      0            "expected_a_before_b",
    +
     3731.      0            next_token,
    +
     3732.      0            "catch",
    +
     3733.      0            artifact(next_token)
    +
     3734.      0        );
    +
     3735.      0
    +
     3736.      0    }
    +
     3737.      0    if (next_token.id === "finally") {
    +
     3738.      0        functionage.finally += 1;
    +
     3739.      0        advance("finally");
    +
     3740.      0        the_try.else = block();
    +
     3741.      0        the_disrupt = the_try.else.disrupt;
    +
     3742.      0        functionage.finally -= 1;
    +
     3743.      0    }
    +
     3744.      1    the_try.disrupt = the_disrupt;
    +
     3745.      1    functionage.try -= 1;
    +
     3746.      1    return the_try;
    +
     3747.      1});
    +
     3748.      1stmt("var", do_var);
    +
     3749.      3stmt("while", function () {
    +
     3750.      3    const the_while = token;
    +
     3751.      3    not_top_level(the_while);
    +
     3752.      3    functionage.loop += 1;
    +
     3753.      3    the_while.expression = condition();
    +
     3754.      3    the_while.block = block();
    +
     3755.      0    if (the_while.block.disrupt === true) {
    +
     3756.      0        warn("weird_loop", the_while);
    +
     3757.      0    }
    +
     3758.      3    functionage.loop -= 1;
    +
     3759.      3    return the_while;
    +
     3760.      3});
    +
     3761.      0stmt("with", function () {
    +
     3762.      0    stop("unexpected_a", token);
    +
     3763.      0});
    +
     3764.      1
    +
     3765.      1ternary("?", ":");
    +
     3766.      1
    +
     3767.      1// Ambulation of the parse tree.
    +
     3768.      1
    +
     3769.      2function action(when) {
    +
     3770.      2
    +
     3771.      2// Produce a function that will register task functions that will be called as
    +
     3772.      2// the tree is traversed.
    +
     3773.      2
    +
     3774.     38    return function (arity, id, task) {
    +
     3775.     38        let a_set = when[arity];
    +
     3776.     38        let i_set;
    +
     3777.     38
    +
     3778.     38// The id parameter is optional. If excluded, the task will be applied to all
    +
     3779.     38// ids.
    +
     3780.     38
    +
     3781.      8        if (typeof id !== "string") {
    +
     3782.      8            task = id;
    +
     3783.      8            id = "(all)";
    +
     3784.      8        }
    +
     3785.     38
    +
     3786.     38// If this arity has no registrations yet, then create a set object to hold
    +
     3787.     38// them.
    +
     3788.     38
    +
     3789.     10        if (a_set === undefined) {
    +
     3790.     10            a_set = empty();
    +
     3791.     10            when[arity] = a_set;
    +
     3792.     10        }
    +
     3793.     38
    +
     3794.     38// If this id has no registrations yet, then create a set array to hold them.
    +
     3795.     38
    +
     3796.     38        i_set = a_set[id];
    +
     3797.     37        if (i_set === undefined) {
    +
     3798.     37            i_set = [];
    +
     3799.     37            a_set[id] = i_set;
    +
     3800.     37        }
    +
     3801.     38
    +
     3802.     38// Register the task with the arity and the id.
    +
     3803.     38
    +
     3804.     38        i_set.push(task);
    +
     3805.     38    };
    +
     3806.      2}
    +
     3807.      1
    +
     3808.      2function amble(when) {
    +
     3809.      2
    +
     3810.      2// Produce a function that will act on the tasks registered by an action
    +
     3811.      2// function while walking the tree.
    +
     3812.      2
    +
     3813.  24536    return function (the_token) {
    +
     3814.  24536
    +
     3815.  24536// Given a task set that was built by an action function, run all of the
    +
     3816.  24536// relevant tasks on the token.
    +
     3817.  24536
    +
     3818.  24536        let a_set = when[the_token.arity];
    +
     3819.  24536        let i_set;
    +
     3820.  24536
    +
     3821.  24536// If there are tasks associated with the token's arity...
    +
     3822.  24536
    +
     3823.  16630        if (a_set !== undefined) {
    +
     3824.  16630
    +
     3825.  16630// If there are tasks associated with the token's id...
    +
     3826.  16630
    +
     3827.  16630            i_set = a_set[the_token.id];
    +
     3828.  16630            if (i_set !== undefined) {
    +
     3829.  16630                i_set.forEach(function (task) {
    +
     3830.  16630                    return task(the_token);
    +
     3831.  16630                });
    +
     3832.  16630            }
    +
     3833.  16630
    +
     3834.  16630// If there are tasks for all ids.
    +
     3835.  16630
    +
     3836.  16630            i_set = a_set["(all)"];
    +
     3837.  16630            if (i_set !== undefined) {
    +
     3838.  16630                i_set.forEach(function (task) {
    +
     3839.  16630                    return task(the_token);
    +
     3840.  16630                });
    +
     3841.  16630            }
    +
     3842.  16630        }
    +
     3843.  24536    };
    +
     3844.      2}
    +
     3845.      1
    +
     3846.      1const posts = empty();
    +
     3847.      1const pres = empty();
    +
     3848.      1const preaction = action(pres);
    +
     3849.      1const postaction = action(posts);
    +
     3850.      1const preamble = amble(pres);
    +
     3851.      1const postamble = amble(posts);
    +
     3852.      1
    +
     3853.  18963function walk_expression(thing) {
    +
     3854.  11775    if (thing) {
    +
     3855.  11775        if (Array.isArray(thing)) {
    +
     3856.  11775            thing.forEach(walk_expression);
    +
     3857.  11775        } else {
    +
     3858.  11775            preamble(thing);
    +
     3859.  11775            walk_expression(thing.expression);
    +
     3860.  11775            if (thing.id === "function") {
    +
     3861.  11775                walk_statement(thing.block);
    +
     3862.  11775            }
    +
     3863.      0            if (thing.arity === "pre" || thing.arity === "post") {
    +
     3864.      0                warn("unexpected_a", thing);
    +
     3865.      0            } else if (
    +
     3866.  11775                thing.arity === "statement"
    +
     3867.  11775                || thing.arity === "assignment"
    +
     3868.      0            ) {
    +
     3869.      0                warn("unexpected_statement_a", thing);
    +
     3870.      0            }
    +
     3871.  11775            postamble(thing);
    +
     3872.  11775        }
    +
     3873.  11775    }
    +
     3874.  18963}
    +
     3875.      1
    +
     3876.   9107function walk_statement(thing) {
    +
     3877.   4150    if (thing) {
    +
     3878.   4150        if (Array.isArray(thing)) {
    +
     3879.   4150            thing.forEach(walk_statement);
    +
     3880.   4150        } else {
    +
     3881.   4150            preamble(thing);
    +
     3882.   4150            walk_expression(thing.expression);
    +
     3883.   4150            if (thing.arity === "binary") {
    +
     3884.      0                if (thing.id !== "(") {
    +
     3885.      0                    warn("unexpected_expression_a", thing);
    +
     3886.      0                }
    +
     3887.   4150            } else if (
    +
     3888.   4150                thing.arity !== "statement"
    +
     3889.   4150                && thing.arity !== "assignment"
    +
     3890.      0                && thing.id !== "import"
    +
     3891.      0            ) {
    +
     3892.      0                warn("unexpected_expression_a", thing);
    +
     3893.      0            }
    +
     3894.   4150            walk_statement(thing.block);
    +
     3895.   4150            walk_statement(thing.else);
    +
     3896.   4150            postamble(thing);
    +
     3897.   4150        }
    +
     3898.   4150    }
    +
     3899.   9107}
    +
     3900.      1
    +
     3901.   3684function lookup(thing) {
    +
     3902.   3684    if (thing.arity === "variable") {
    +
     3903.   3684
    +
     3904.   3684// Look up the variable in the current context.
    +
     3905.   3684
    +
     3906.   3684        let the_variable = functionage.context[thing.id];
    +
     3907.   3684
    +
     3908.   3684// If it isn't local, search all the other contexts. If there are name
    +
     3909.   3684// collisions, take the most recent.
    +
     3910.   3684
    +
     3911.    950        if (the_variable === undefined) {
    +
     3912.   1622            stack.forEach(function (outer) {
    +
     3913.   1622                const a_variable = outer.context[thing.id];
    +
     3914.   1622                if (
    +
     3915.   1622                    a_variable !== undefined
    +
     3916.   1020                    && a_variable.role !== "label"
    +
     3917.   1020                ) {
    +
     3918.   1020                    the_variable = a_variable;
    +
     3919.   1020                }
    +
     3920.   1622            });
    +
     3921.    950
    +
     3922.    950// If it isn't in any of those either, perhaps it is a predefined global.
    +
     3923.    950// If so, add it to the global context.
    +
     3924.    950
    +
     3925.    950            if (the_variable === undefined) {
    +
     3926.      0                if (declared_globals[thing.id] === undefined) {
    +
     3927.      0                    warn("undeclared_a", thing);
    +
     3928.      0                    return;
    +
     3929.      0                }
    +
     3930.    950                the_variable = {
    +
     3931.    950                    dead: false,
    +
     3932.    950                    parent: global,
    +
     3933.    950                    id: thing.id,
    +
     3934.    950                    init: true,
    +
     3935.    950                    role: "variable",
    +
     3936.    950                    used: 0,
    +
     3937.    950                    writable: false
    +
     3938.    950                };
    +
     3939.    950                global.context[thing.id] = the_variable;
    +
     3940.    950            }
    +
     3941.    950            the_variable.closure = true;
    +
     3942.    950            functionage.context[thing.id] = the_variable;
    +
     3943.      0        } else if (the_variable.role === "label") {
    +
     3944.      0            warn("label_a", thing);
    +
     3945.      0        }
    +
     3946.   3684        if (
    +
     3947.   3684            the_variable.dead
    +
     3948.      0            && (
    +
     3949.      0                the_variable.calls === undefined
    +
     3950.      0                || functionage.name === undefined
    +
     3951.      0                || the_variable.calls[functionage.name.id] === undefined
    +
     3952.      0            )
    +
     3953.      0        ) {
    +
     3954.      0            warn("out_of_scope_a", thing);
    +
     3955.      0        }
    +
     3956.   3684        return the_variable;
    +
     3957.   3684    }
    +
     3958.   3684}
    +
     3959.      1
    +
     3960.      0function subactivate(name) {
    +
     3961.      0    name.init = true;
    +
     3962.      0    name.dead = false;
    +
     3963.      0    blockage.live.push(name);
    +
     3964.      0}
    +
     3965.      1
    +
     3966.    212function preaction_function(thing) {
    +
     3967.      0    if (thing.arity === "statement" && blockage.body !== true) {
    +
     3968.      0        warn("unexpected_a", thing);
    +
     3969.      0    }
    +
     3970.    212    stack.push(functionage);
    +
     3971.    212    block_stack.push(blockage);
    +
     3972.    212    functionage = thing;
    +
     3973.    212    blockage = thing;
    +
     3974.    212    thing.live = [];
    +
     3975.    120    if (typeof thing.name === "object") {
    +
     3976.    120        thing.name.dead = false;
    +
     3977.    120        thing.name.init = true;
    +
     3978.    120    }
    +
     3979.      0    if (thing.extra === "get") {
    +
     3980.      0        if (thing.parameters.length !== 0) {
    +
     3981.      0            warn("bad_get", thing);
    +
     3982.      0        }
    +
     3983.      0    } else if (thing.extra === "set") {
    +
     3984.      0        if (thing.parameters.length !== 1) {
    +
     3985.      0            warn("bad_set", thing);
    +
     3986.      0        }
    +
     3987.      0    }
    +
     3988.    169    thing.parameters.forEach(function (name) {
    +
     3989.    169        walk_expression(name.expression);
    +
     3990.      0        if (name.id === "{" || name.id === "[") {
    +
     3991.      0            name.names.forEach(subactivate);
    +
     3992.      0        } else {
    +
     3993.    169            name.dead = false;
    +
     3994.    169            name.init = true;
    +
     3995.    169        }
    +
     3996.    169    });
    +
     3997.    212}
    +
     3998.      1
    +
     3999.   4281function bitwise_check(thing) {
    +
     4000.      0    if (!option.bitwise && bitwiseop[thing.id] === true) {
    +
     4001.      0        warn("unexpected_a", thing);
    +
     4002.      0    }
    +
     4003.   4281    if (
    +
     4004.   4281        thing.id !== "("
    +
     4005.   3157        && thing.id !== "&&"
    +
     4006.   2987        && thing.id !== "||"
    +
     4007.   2819        && thing.id !== "="
    +
     4008.   2340        && Array.isArray(thing.expression)
    +
     4009.   1013        && thing.expression.length === 2
    +
     4010.   1001        && (
    +
     4011.   1001            relationop[thing.expression[0].id] === true
    +
     4012.   1001            || relationop[thing.expression[1].id] === true
    +
     4013.   1001        )
    +
     4014.      0    ) {
    +
     4015.      0        warn("unexpected_a", thing);
    +
     4016.      0    }
    +
     4017.   4281}
    +
     4018.      1
    +
     4019.   1060function pop_block() {
    +
     4020.    214    blockage.live.forEach(function (name) {
    +
     4021.    214        name.dead = true;
    +
     4022.    214    });
    +
     4023.   1060    delete blockage.live;
    +
     4024.   1060    blockage = block_stack.pop();
    +
     4025.   1060}
    +
     4026.      1
    +
     4027.    279function activate(name) {
    +
     4028.    279    name.dead = false;
    +
     4029.    189    if (name.expression !== undefined) {
    +
     4030.    189        walk_expression(name.expression);
    +
     4031.      0        if (name.id === "{" || name.id === "[") {
    +
     4032.      0            name.names.forEach(subactivate);
    +
     4033.      0        } else {
    +
     4034.    189            name.init = true;
    +
     4035.    189        }
    +
     4036.    189    }
    +
     4037.    279    blockage.live.push(name);
    +
     4038.    279}
    +
     4039.      1
    +
     4040.    279function action_var(thing) {
    +
     4041.    279    thing.names.forEach(activate);
    +
     4042.    279}
    +
     4043.      1
    +
     4044.      1preaction("assignment", bitwise_check);
    +
     4045.      1preaction("binary", bitwise_check);
    +
     4046.   3762preaction("binary", function (thing) {
    +
     4047.    726    if (relationop[thing.id] === true) {
    +
     4048.    726        const left = thing.expression[0];
    +
     4049.    726        const right = thing.expression[1];
    +
     4050.      0        if (left.id === "NaN" || right.id === "NaN") {
    +
     4051.      0            warn("number_isNaN", thing);
    +
     4052.      0        } else if (left.id === "typeof") {
    +
     4053.      0            if (right.id !== "(string)") {
    +
     4054.      0                if (right.id !== "typeof") {
    +
     4055.      0                    warn("expected_string_a", right);
    +
     4056.      0                }
    +
     4057.      0            } else {
    +
     4058.    726                const value = right.value;
    +
     4059.      0                if (value === "null" || value === "undefined") {
    +
     4060.      0                    warn("unexpected_typeof_a", right, value);
    +
     4061.      0                } else if (
    +
     4062.    726                    value !== "boolean"
    +
     4063.    726                    && value !== "function"
    +
     4064.    726                    && value !== "number"
    +
     4065.    726                    && value !== "object"
    +
     4066.    726                    && value !== "string"
    +
     4067.      0                    && value !== "symbol"
    +
     4068.      0                ) {
    +
     4069.      0                    warn("expected_type_string_a", right, value);
    +
     4070.      0                }
    +
     4071.    726            }
    +
     4072.    726        }
    +
     4073.    726    }
    +
     4074.   3762});
    +
     4075.      0preaction("binary", "==", function (thing) {
    +
     4076.      0    warn("expected_a_b", thing, "===", "==");
    +
     4077.      0});
    +
     4078.      0preaction("binary", "!=", function (thing) {
    +
     4079.      0    warn("expected_a_b", thing, "!==", "!=");
    +
     4080.      0});
    +
     4081.      1preaction("binary", "=>", preaction_function);
    +
     4082.    168preaction("binary", "||", function (thing) {
    +
     4083.    336    thing.expression.forEach(function (thang) {
    +
     4084.      0        if (thang.id === "&&" && !thang.wrapped) {
    +
     4085.      0            warn("and", thang);
    +
     4086.      0        }
    +
     4087.    336    });
    +
     4088.    168});
    +
     4089.   1124preaction("binary", "(", function (thing) {
    +
     4090.   1124    const left = thing.expression[0];
    +
     4091.   1124    if (
    +
     4092.   1124        left.identifier
    +
     4093.    968        && functionage.context[left.id] === undefined
    +
     4094.    465        && typeof functionage.name === "object"
    +
     4095.    285    ) {
    +
     4096.    285        const parent = functionage.name.parent;
    +
     4097.    285        if (parent) {
    +
     4098.    285            const left_variable = parent.context[left.id];
    +
     4099.    285            if (
    +
     4100.    285                left_variable !== undefined
    +
     4101.    285                && left_variable.dead
    +
     4102.    285                && left_variable.parent === parent
    +
     4103.    285                && left_variable.calls !== undefined
    +
     4104.    285                && left_variable.calls[functionage.name.id] !== undefined
    +
     4105.    285            ) {
    +
     4106.    285                left_variable.dead = false;
    +
     4107.    285            }
    +
     4108.    285        }
    +
     4109.    285    }
    +
     4110.   1124});
    +
     4111.      0preaction("binary", "in", function (thing) {
    +
     4112.      0    warn("infix_in", thing);
    +
     4113.      0});
    +
     4114.      0preaction("binary", "instanceof", function (thing) {
    +
     4115.      0    warn("unexpected_a", thing);
    +
     4116.      0});
    +
     4117.   1327preaction("binary", ".", function (thing) {
    +
     4118.      0    if (thing.expression.new) {
    +
     4119.      0        thing.new = true;
    +
     4120.      0    }
    +
     4121.   1327});
    +
     4122.    848preaction("statement", "{", function (thing) {
    +
     4123.    848    block_stack.push(blockage);
    +
     4124.    848    blockage = thing;
    +
     4125.    848    thing.live = [];
    +
     4126.    848});
    +
     4127.      0preaction("statement", "for", function (thing) {
    +
     4128.      0    if (thing.name !== undefined) {
    +
     4129.      0        const the_variable = lookup(thing.name);
    +
     4130.      0        if (the_variable !== undefined) {
    +
     4131.      0            the_variable.init = true;
    +
     4132.      0            if (!the_variable.writable) {
    +
     4133.      0                warn("bad_assignment_a", thing.name);
    +
     4134.      0            }
    +
     4135.      0        }
    +
     4136.      0    }
    +
     4137.      0    walk_statement(thing.initial);
    +
     4138.      0});
    +
     4139.      1preaction("statement", "function", preaction_function);
    +
     4140.      1preaction("unary", "~", bitwise_check);
    +
     4141.      1preaction("unary", "function", preaction_function);
    +
     4142.   3460preaction("variable", function (thing) {
    +
     4143.   3460    const the_variable = lookup(thing);
    +
     4144.   3460    if (the_variable !== undefined) {
    +
     4145.   3460        thing.variable = the_variable;
    +
     4146.   3460        the_variable.used += 1;
    +
     4147.   3460    }
    +
     4148.   3460});
    +
     4149.      1
    +
     4150.    224function init_variable(name) {
    +
     4151.    224    const the_variable = lookup(name);
    +
     4152.    224    if (the_variable !== undefined) {
    +
     4153.    224        if (the_variable.writable) {
    +
     4154.    224            the_variable.init = true;
    +
     4155.    224            return;
    +
     4156.    224        }
    +
     4157.      0    }
    +
     4158.      0    warn("bad_assignment_a", name);
    +
     4159.      0}
    +
     4160.      1
    +
     4161.     32postaction("assignment", "+=", function (thing) {
    +
     4162.     32    let right = thing.expression[1];
    +
     4163.     23    if (right.constant) {
    +
     4164.     23        if (
    +
     4165.     23            right.value === ""
    +
     4166.     23            || (right.id === "(number)" && right.value === "0")
    +
     4167.     23            || right.id === "(boolean)"
    +
     4168.     23            || right.id === "null"
    +
     4169.     23            || right.id === "undefined"
    +
     4170.     23            || Number.isNaN(right.value)
    +
     4171.      0        ) {
    +
     4172.      0            warn("unexpected_a", right);
    +
     4173.      0        }
    +
     4174.     23    }
    +
     4175.     32});
    +
     4176.    519postaction("assignment", function (thing) {
    +
     4177.    519
    +
     4178.    519// Assignment using = sets the init property of a variable. No other assignment
    +
     4179.    519// operator can do this. A = token keeps that variable (or array of variables
    +
     4180.    519// in case of destructuring) in its name property.
    +
     4181.    519
    +
     4182.    519    const lvalue = thing.expression[0];
    +
     4183.    479    if (thing.id === "=") {
    +
     4184.    479        if (thing.names !== undefined) {
    +
     4185.      0            if (Array.isArray(thing.names)) {
    +
     4186.      0                thing.names.forEach(init_variable);
    +
     4187.      0            } else {
    +
     4188.    479                init_variable(thing.names);
    +
     4189.    479            }
    +
     4190.    479        } else {
    +
     4191.    479            if (lvalue.id === "[" || lvalue.id === "{") {
    +
     4192.    479                lvalue.expression.forEach(function (thing) {
    +
     4193.    479                    if (thing.variable) {
    +
     4194.    479                        thing.variable.init = true;
    +
     4195.    479                    }
    +
     4196.    479                });
    +
     4197.    479            } else if (
    +
     4198.    479                lvalue.id === "."
    +
     4199.    479                && thing.expression[1].id === "undefined"
    +
     4200.      0            ) {
    +
     4201.      0                warn(
    +
     4202.      0                    "expected_a_b",
    +
     4203.      0                    lvalue.expression,
    +
     4204.      0                    "delete",
    +
     4205.      0                    "undefined"
    +
     4206.      0                );
    +
     4207.      0            }
    +
     4208.    479        }
    +
     4209.    479    } else {
    +
     4210.     40        if (lvalue.arity === "variable") {
    +
     4211.      0            if (!lvalue.variable || lvalue.variable.writable !== true) {
    +
     4212.      0                warn("bad_assignment_a", lvalue);
    +
     4213.      0            }
    +
     4214.     40        }
    +
     4215.     40        const right = syntax[thing.expression[1].id];
    +
     4216.     40        if (
    +
     4217.     40            right !== undefined
    +
     4218.     40            && (
    +
     4219.     40                right.id === "function"
    +
     4220.     40                || right.id === "=>"
    +
     4221.     40                || (
    +
     4222.     40                    right.constant
    +
     4223.     40                    && right.id !== "(number)"
    +
     4224.     40                    && (right.id !== "(string)" || thing.id !== "+=")
    +
     4225.     40                )
    +
     4226.     40            )
    +
     4227.      0        ) {
    +
     4228.      0            warn("unexpected_a", thing.expression[1]);
    +
     4229.      0        }
    +
     4230.     40    }
    +
     4231.    519});
    +
     4232.      1
    +
     4233.    212function postaction_function(thing) {
    +
     4234.    212    delete functionage.finally;
    +
     4235.    212    delete functionage.loop;
    +
     4236.    212    delete functionage.switch;
    +
     4237.    212    delete functionage.try;
    +
     4238.    212    functionage = stack.pop();
    +
     4239.      0    if (thing.wrapped) {
    +
     4240.      0        warn("unexpected_parens", thing);
    +
     4241.      0    }
    +
     4242.    212    return pop_block();
    +
     4243.    212}
    +
     4244.      1
    +
     4245.   3762postaction("binary", function (thing) {
    +
     4246.   3762    let right;
    +
     4247.    726    if (relationop[thing.id]) {
    +
     4248.    726        if (
    +
     4249.    726            is_weird(thing.expression[0])
    +
     4250.    726            || is_weird(thing.expression[1])
    +
     4251.    726            || are_similar(thing.expression[0], thing.expression[1])
    +
     4252.    726            || (
    +
     4253.    726                thing.expression[0].constant === true
    +
     4254.      0                && thing.expression[1].constant === true
    +
     4255.    726            )
    +
     4256.      0        ) {
    +
     4257.      0            warn("weird_relation_a", thing);
    +
     4258.      0        }
    +
     4259.    726    }
    +
     4260.     35    if (thing.id === "+") {
    +
     4261.     35        if (!option.convert) {
    +
     4262.      0            if (thing.expression[0].value === "") {
    +
     4263.      0                warn("expected_a_b", thing, "String(...)", "\"\" +");
    +
     4264.      0            } else if (thing.expression[1].value === "") {
    +
     4265.      0                warn("expected_a_b", thing, "String(...)", "+ \"\"");
    +
     4266.      0            }
    +
     4267.     35        }
    +
     4268.   3727    } else if (thing.id === "[") {
    +
     4269.      0        if (thing.expression[0].id === "window") {
    +
     4270.      0            warn("weird_expression_a", thing, "window[...]");
    +
     4271.      0        }
    +
     4272.      0        if (thing.expression[0].id === "self") {
    +
     4273.      0            warn("weird_expression_a", thing, "self[...]");
    +
     4274.      0        }
    +
     4275.   3727    } else if (thing.id === "." || thing.id === "?.") {
    +
     4276.      0        if (thing.expression.id === "RegExp") {
    +
     4277.      0            warn("weird_expression_a", thing);
    +
     4278.      0        }
    +
     4279.   3727    } else if (thing.id !== "=>" && thing.id !== "(") {
    +
     4280.   3727        right = thing.expression[1];
    +
     4281.   3727        if (
    +
     4282.   3727            (thing.id === "+" || thing.id === "-")
    +
     4283.   3727            && right.id === thing.id
    +
     4284.      0            && right.arity === "unary"
    +
     4285.      0            && !right.wrapped
    +
     4286.      0        ) {
    +
     4287.      0            warn("wrap_unary", right);
    +
     4288.      0        }
    +
     4289.   3727        if (
    +
     4290.   3727            thing.expression[0].constant === true
    +
     4291.   3727            && right.constant === true
    +
     4292.   3727        ) {
    +
     4293.   3727            thing.constant = true;
    +
     4294.   3727        }
    +
     4295.   3727    }
    +
     4296.   3762});
    +
     4297.    170postaction("binary", "&&", function (thing) {
    +
     4298.    170    if (
    +
     4299.    170        is_weird(thing.expression[0])
    +
     4300.    170        || are_similar(thing.expression[0], thing.expression[1])
    +
     4301.    170        || thing.expression[0].constant === true
    +
     4302.    170        || thing.expression[1].constant === true
    +
     4303.      0    ) {
    +
     4304.      0        warn("weird_condition_a", thing);
    +
     4305.      0    }
    +
     4306.    170});
    +
     4307.    168postaction("binary", "||", function (thing) {
    +
     4308.    168    if (
    +
     4309.    168        is_weird(thing.expression[0])
    +
     4310.    168        || are_similar(thing.expression[0], thing.expression[1])
    +
     4311.    168        || thing.expression[0].constant === true
    +
     4312.      0    ) {
    +
     4313.      0        warn("weird_condition_a", thing);
    +
     4314.      0    }
    +
     4315.    168});
    +
     4316.      1postaction("binary", "=>", postaction_function);
    +
     4317.   1124postaction("binary", "(", function (thing) {
    +
     4318.   1124    let left = thing.expression[0];
    +
     4319.   1124    let the_new;
    +
     4320.   1124    let arg;
    +
     4321.      1    if (left.id === "new") {
    +
     4322.      1        the_new = left;
    +
     4323.      1        left = left.expression;
    +
     4324.      1    }
    +
     4325.     25    if (left.id === "function") {
    +
     4326.      0        if (!thing.wrapped) {
    +
     4327.      0            warn("wrap_immediate", thing);
    +
     4328.      0        }
    +
     4329.   1099    } else if (left.identifier) {
    +
     4330.   1099        if (the_new !== undefined) {
    +
     4331.   1099            if (
    +
     4332.   1099                left.id[0] > "Z"
    +
     4333.   1099                || left.id === "Boolean"
    +
     4334.   1099                || left.id === "Number"
    +
     4335.   1099                || left.id === "String"
    +
     4336.   1099                || left.id === "Symbol"
    +
     4337.      0            ) {
    +
     4338.      0                warn("unexpected_a", the_new);
    +
     4339.      0            } else if (left.id === "Function") {
    +
     4340.      0                if (!option.eval) {
    +
     4341.      0                    warn("unexpected_a", left, "new Function");
    +
     4342.      0                }
    +
     4343.      0            } else if (left.id === "Array") {
    +
     4344.      0                arg = thing.expression;
    +
     4345.      0                if (arg.length !== 2 || arg[1].id === "(string)") {
    +
     4346.      0                    warn("expected_a_b", left, "[]", "new Array");
    +
     4347.      0                }
    +
     4348.      0            } else if (left.id === "Object") {
    +
     4349.      0                warn(
    +
     4350.      0                    "expected_a_b",
    +
     4351.      0                    left,
    +
     4352.      0                    "Object.create(null)",
    +
     4353.      0                    "new Object"
    +
     4354.      0                );
    +
     4355.      0            }
    +
     4356.   1099        } else {
    +
     4357.   1099            if (
    +
     4358.   1099                left.id[0] >= "A"
    +
     4359.   1099                && left.id[0] <= "Z"
    +
     4360.   1099                && left.id !== "Boolean"
    +
     4361.   1099                && left.id !== "Number"
    +
     4362.   1099                && left.id !== "String"
    +
     4363.      0                && left.id !== "Symbol"
    +
     4364.      0            ) {
    +
     4365.      0                warn(
    +
     4366.      0                    "expected_a_before_b",
    +
     4367.      0                    left,
    +
     4368.      0                    "new",
    +
     4369.      0                    artifact(left)
    +
     4370.      0                );
    +
     4371.      0            }
    +
     4372.   1099        }
    +
     4373.   1099    } else if (left.id === ".") {
    +
     4374.   1099        let cack = the_new !== undefined;
    +
     4375.      0        if (left.expression.id === "Date" && left.name.id === "UTC") {
    +
     4376.      0            cack = !cack;
    +
     4377.      0        }
    +
     4378.      0        if (rx_cap.test(left.name.id) !== cack) {
    +
     4379.      0            if (the_new !== undefined) {
    +
     4380.      0                warn("unexpected_a", the_new);
    +
     4381.      0            } else {
    +
     4382.      0                warn(
    +
     4383.      0                    "expected_a_before_b",
    +
     4384.      0                    left.expression,
    +
     4385.      0                    "new",
    +
     4386.      0                    left.name.id
    +
     4387.      0                );
    +
     4388.      0            }
    +
     4389.      0        }
    +
     4390.      0        if (left.name.id === "getTime") {
    +
     4391.      0            const paren = left.expression;
    +
     4392.      0            if (paren.id === "(") {
    +
     4393.      0                const array = paren.expression;
    +
     4394.      0                if (array.length === 1) {
    +
     4395.      0                    const new_date = array[0];
    +
     4396.      0                    if (
    +
     4397.      0                        new_date.id === "new"
    +
     4398.      0                        && new_date.expression.id === "Date"
    +
     4399.      0                    ) {
    +
     4400.      0                        warn(
    +
     4401.      0                            "expected_a_b",
    +
     4402.      0                            new_date,
    +
     4403.      0                            "Date.now()",
    +
     4404.      0                            "new Date().getTime()"
    +
     4405.      0                        );
    +
     4406.      0                    }
    +
     4407.      0                }
    +
     4408.      0            }
    +
     4409.      0        }
    +
     4410.   1099    }
    +
     4411.   1124});
    +
     4412.    181postaction("binary", "[", function (thing) {
    +
     4413.      0    if (thing.expression[0].id === "RegExp") {
    +
     4414.      0        warn("weird_expression_a", thing);
    +
     4415.      0    }
    +
     4416.      0    if (is_weird(thing.expression[1])) {
    +
     4417.      0        warn("weird_expression_a", thing.expression[1]);
    +
     4418.      0    }
    +
     4419.    181});
    +
     4420.      1postaction("statement", "{", pop_block);
    +
     4421.      1postaction("statement", "const", action_var);
    +
     4422.      1postaction("statement", "export", top_level_only);
    +
     4423.      0postaction("statement", "for", function (thing) {
    +
     4424.      0    walk_statement(thing.inc);
    +
     4425.      0});
    +
     4426.      1postaction("statement", "function", postaction_function);
    +
     4427.      0postaction("statement", "import", function (the_thing) {
    +
     4428.      0    const name = the_thing.name;
    +
     4429.      0    if (name) {
    +
     4430.      0        if (Array.isArray(name)) {
    +
     4431.      0            name.forEach(function (name) {
    +
     4432.      0                name.dead = false;
    +
     4433.      0                name.init = true;
    +
     4434.      0                blockage.live.push(name);
    +
     4435.      0            });
    +
     4436.      0        } else {
    +
     4437.      0            name.dead = false;
    +
     4438.      0            name.init = true;
    +
     4439.      0            blockage.live.push(name);
    +
     4440.      0        }
    +
     4441.      0        return top_level_only(the_thing);
    +
     4442.      0    }
    +
     4443.      0});
    +
     4444.      1postaction("statement", "let", action_var);
    +
     4445.      1postaction("statement", "try", function (thing) {
    +
     4446.      1    if (thing.catch !== undefined) {
    +
     4447.      1        const the_name = thing.catch.name;
    +
     4448.      1        if (the_name !== undefined) {
    +
     4449.      1            const the_variable = functionage.context[the_name.id];
    +
     4450.      1            the_variable.dead = false;
    +
     4451.      1            the_variable.init = true;
    +
     4452.      1        }
    +
     4453.      1        walk_statement(thing.catch.block);
    +
     4454.      1    }
    +
     4455.      1});
    +
     4456.      1postaction("statement", "var", action_var);
    +
     4457.     14postaction("ternary", function (thing) {
    +
     4458.     14    if (
    +
     4459.     14        is_weird(thing.expression[0])
    +
     4460.     14        || thing.expression[0].constant === true
    +
     4461.     14        || are_similar(thing.expression[1], thing.expression[2])
    +
     4462.      0    ) {
    +
     4463.      0        warn("unexpected_a", thing);
    +
     4464.      0    } else if (are_similar(thing.expression[0], thing.expression[1])) {
    +
     4465.      0        warn("expected_a_b", thing, "||", "?");
    +
     4466.      0    } else if (are_similar(thing.expression[0], thing.expression[2])) {
    +
     4467.      0        warn("expected_a_b", thing, "&&", "?");
    +
     4468.      0    } else if (
    +
     4469.     14        thing.expression[1].id === "true"
    +
     4470.      0        && thing.expression[2].id === "false"
    +
     4471.      0    ) {
    +
     4472.      0        warn("expected_a_b", thing, "!!", "?");
    +
     4473.      0    } else if (
    +
     4474.     14        thing.expression[1].id === "false"
    +
     4475.      0        && thing.expression[2].id === "true"
    +
     4476.      0    ) {
    +
     4477.      0        warn("expected_a_b", thing, "!", "?");
    +
     4478.      0    } else if (
    +
     4479.     14        thing.expression[0].wrapped !== true
    +
     4480.     12        && (
    +
     4481.     12            thing.expression[0].id === "||"
    +
     4482.     12            || thing.expression[0].id === "&&"
    +
     4483.     12        )
    +
     4484.      0    ) {
    +
     4485.      0        warn("wrap_condition", thing.expression[0]);
    +
     4486.      0    }
    +
     4487.     14});
    +
     4488.    273postaction("unary", function (thing) {
    +
     4489.      0    if (thing.id === "`") {
    +
     4490.      0        if (thing.expression.every(function (thing) {
    +
     4491.      0            return thing.constant;
    +
     4492.      0        })) {
    +
     4493.      0            thing.constant = true;
    +
     4494.      0        }
    +
     4495.      0    } else if (thing.id === "!") {
    +
     4496.      0        if (thing.expression.constant === true) {
    +
     4497.      0            warn("unexpected_a", thing);
    +
     4498.      0        }
    +
     4499.      0    } else if (thing.id === "!!") {
    +
     4500.      0        if (!option.convert) {
    +
     4501.      0            warn("expected_a_b", thing, "Boolean(...)", "!!");
    +
     4502.      0        }
    +
     4503.      0    } else if (
    +
     4504.    208        thing.id !== "["
    +
     4505.    208        && thing.id !== "{"
    +
     4506.    208        && thing.id !== "function"
    +
     4507.    208        && thing.id !== "new"
    +
     4508.    208    ) {
    +
     4509.    208        if (thing.expression.constant === true) {
    +
     4510.    208            thing.constant = true;
    +
     4511.    208        }
    +
     4512.    208    }
    +
     4513.    273});
    +
     4514.      1postaction("unary", "function", postaction_function);
    +
     4515.      0postaction("unary", "+", function (thing) {
    +
     4516.      0    if (!option.convert) {
    +
     4517.      0        warn("expected_a_b", thing, "Number(...)", "+");
    +
     4518.      0    }
    +
     4519.      0    const right = thing.expression;
    +
     4520.      0    if (right.id === "(" && right.expression[0].id === "new") {
    +
     4521.      0        warn("unexpected_a_before_b", thing, "+", "new");
    +
     4522.      0    } else if (
    +
     4523.      0        right.constant
    +
     4524.      0        || right.id === "{"
    +
     4525.      0        || (right.id === "[" && right.arity !== "binary")
    +
     4526.      0    ) {
    +
     4527.      0        warn("unexpected_a", thing, "+");
    +
     4528.      0    }
    +
     4529.      0});
    +
     4530.      1
    +
     4531.    213function delve(the_function) {
    +
     4532.   1525    Object.keys(the_function.context).forEach(function (id) {
    +
     4533.   1525        if (id !== "ignore") {
    +
     4534.   1525            const name = the_function.context[id];
    +
     4535.    575            if (name.parent === the_function) {
    +
     4536.    575                if (
    +
     4537.    575                    name.used === 0
    +
     4538.      0                    && (
    +
     4539.      0                        name.role !== "function"
    +
     4540.      0                        || name.parent.arity !== "unary"
    +
     4541.      0                    )
    +
     4542.      0                ) {
    +
     4543.      0                    warn("unused_a", name);
    +
     4544.      0                } else if (!name.init) {
    +
     4545.      0                    warn("uninitialized_a", name);
    +
     4546.      0                }
    +
     4547.    575            }
    +
     4548.   1525        }
    +
     4549.   1525    });
    +
     4550.    213}
    +
     4551.      1
    +
     4552.      1function uninitialized_and_unused() {
    +
     4553.      1
    +
     4554.      1// Delve into the functions looking for variables that were not initialized
    +
     4555.      1// or used. If the file imports or exports, then its global object is also
    +
     4556.      1// delved.
    +
     4557.      1
    +
     4558.      0    if (module_mode === true || option.node) {
    +
     4559.      1        delve(global);
    +
     4560.      1    }
    +
     4561.      1    functions.forEach(delve);
    +
     4562.      1}
    +
     4563.      1
    +
     4564.      1// Go through the token list, looking at usage of whitespace.
    +
     4565.      1
    +
     4566.      1function whitage() {
    +
     4567.      1    let closer = "(end)";
    +
     4568.      1    let free = false;
    +
     4569.      1    let left = global;
    +
     4570.      1    let margin = 0;
    +
     4571.      1    let nr_comments_skipped = 0;
    +
     4572.      1    let open = true;
    +
     4573.      1    let opening = true;
    +
     4574.      1    let right;
    +
     4575.      1
    +
     4576.   2766    function pop() {
    +
     4577.   2766        const previous = stack.pop();
    +
     4578.   2766        closer = previous.closer;
    +
     4579.   2766        free = previous.free;
    +
     4580.   2766        margin = previous.margin;
    +
     4581.   2766        open = previous.open;
    +
     4582.   2766        opening = previous.opening;
    +
     4583.   2766    }
    +
     4584.      1
    +
     4585.   2766    function push() {
    +
     4586.   2766        stack.push({
    +
     4587.   2766            closer,
    +
     4588.   2766            free,
    +
     4589.   2766            margin,
    +
     4590.   2766            open,
    +
     4591.   2766            opening
    +
     4592.   2766        });
    +
     4593.   2766    }
    +
     4594.      1
    +
     4595.      0    function expected_at(at) {
    +
     4596.      0        warn(
    +
     4597.      0            "expected_a_at_b_c",
    +
     4598.      0            right,
    +
     4599.      0            artifact(right),
    +
     4600.      0            fudge + at,
    +
     4601.      0            artifact_column(right)
    +
     4602.      0        );
    +
     4603.      0    }
    +
     4604.      1
    +
     4605.   3898    function at_margin(fit) {
    +
     4606.   3898        const at = margin + fit;
    +
     4607.      0        if (right.from !== at) {
    +
     4608.      0            return expected_at(at);
    +
     4609.      0        }
    +
     4610.   3898    }
    +
     4611.      1
    +
     4612.  10618    function no_space_only() {
    +
     4613.  10618        if (
    +
     4614.  10618            left.id !== "(global)"
    +
     4615.  10618            && left.nr + 1 === right.nr
    +
     4616.  10618            && (
    +
     4617.  10618                left.line !== right.line
    +
     4618.  10618                || left.thru !== right.from
    +
     4619.  10618            )
    +
     4620.      0        ) {
    +
     4621.      0            warn(
    +
     4622.      0                "unexpected_space_a_b",
    +
     4623.      0                right,
    +
     4624.      0                artifact(left),
    +
     4625.      0                artifact(right)
    +
     4626.      0            );
    +
     4627.      0        }
    +
     4628.  10618    }
    +
     4629.      1
    +
     4630.    373    function no_space() {
    +
     4631.    373        if (left.line === right.line) {
    +
     4632.      0            if (left.thru !== right.from && nr_comments_skipped === 0) {
    +
     4633.      0                warn(
    +
     4634.      0                    "unexpected_space_a_b",
    +
     4635.      0                    right,
    +
     4636.      0                    artifact(left),
    +
     4637.      0                    artifact(right)
    +
     4638.      0                );
    +
     4639.      0            }
    +
     4640.      0        } else {
    +
     4641.      0            if (open) {
    +
     4642.      0                const at = (
    +
     4643.      0                    free
    +
     4644.      0                    ? margin
    +
     4645.      0                    : margin + 8
    +
     4646.      0                );
    +
     4647.      0                if (right.from < at) {
    +
     4648.      0                    expected_at(at);
    +
     4649.      0                }
    +
     4650.      0            } else {
    +
     4651.      0                if (right.from !== margin + 8) {
    +
     4652.      0                    expected_at(margin + 8);
    +
     4653.      0                }
    +
     4654.      0            }
    +
     4655.      0        }
    +
     4656.    373    }
    +
     4657.      1
    +
     4658.   1105    function one_space_only() {
    +
     4659.      0        if (left.line !== right.line || left.thru + 1 !== right.from) {
    +
     4660.      0            warn(
    +
     4661.      0                "expected_space_a_b",
    +
     4662.      0                right,
    +
     4663.      0                artifact(left),
    +
     4664.      0                artifact(right)
    +
     4665.      0            );
    +
     4666.      0        }
    +
     4667.   1105    }
    +
     4668.      1
    +
     4669.   5883    function one_space() {
    +
     4670.   5663        if (left.line === right.line || !open) {
    +
     4671.      0            if (left.thru + 1 !== right.from && nr_comments_skipped === 0) {
    +
     4672.      0                warn(
    +
     4673.      0                    "expected_space_a_b",
    +
     4674.      0                    right,
    +
     4675.      0                    artifact(left),
    +
     4676.      0                    artifact(right)
    +
     4677.      0                );
    +
     4678.      0            }
    +
     4679.   5663        } else {
    +
     4680.      0            if (right.from !== margin) {
    +
     4681.      0                expected_at(margin);
    +
     4682.      0            }
    +
     4683.    220        }
    +
     4684.   5883    }
    +
     4685.      1
    +
     4686.      1    stack = [];
    +
     4687.  22338    tokens.forEach(function (the_token) {
    +
     4688.  22338        right = the_token;
    +
     4689.  21914        if (right.id === "(comment)" || right.id === "(end)") {
    +
     4690.    425            nr_comments_skipped += 1;
    +
     4691.  21913        } else {
    +
     4692.  21913
    +
     4693.  21913// If left is an opener and right is not the closer, then push the previous
    +
     4694.  21913// state. If the token following the opener is on the next line, then this is
    +
     4695.  21913// an open form. If the tokens are on the same line, then it is a closed form.
    +
     4696.  21913// Open form is more readable, with each item (statement, argument, parameter,
    +
     4697.  21913// etc) starting on its own line. Closed form is more compact. Statement blocks
    +
     4698.  21913// are always in open form.
    +
     4699.  21913
    +
     4700.  21913            const new_closer = opener[left.id];
    +
     4701.  21913            if (typeof new_closer === "string") {
    +
     4702.  21913                if (new_closer !== right.id) {
    +
     4703.  21913                    opening = left.open || (left.line !== right.line);
    +
     4704.  21913                    push();
    +
     4705.  21913                    closer = new_closer;
    +
     4706.  21913                    if (opening) {
    +
     4707.  21913                        free = closer === ")" && left.free;
    +
     4708.  21913                        open = true;
    +
     4709.  21913                        margin += 4;
    +
     4710.      0                        if (right.role === "label") {
    +
     4711.      0                            if (right.from !== 0) {
    +
     4712.      0                                expected_at(0);
    +
     4713.      0                            }
    +
     4714.      0                        } else if (right.switch) {
    +
     4715.      0                            at_margin(-4);
    +
     4716.      0                        } else {
    +
     4717.  21913                            at_margin(0);
    +
     4718.  21913                        }
    +
     4719.  21913                    } else {
    +
     4720.      0                        if (right.statement || right.role === "label") {
    +
     4721.      0                            warn(
    +
     4722.      0                                "expected_line_break_a_b",
    +
     4723.      0                                right,
    +
     4724.      0                                artifact(left),
    +
     4725.      0                                artifact(right)
    +
     4726.      0                            );
    +
     4727.      0                        }
    +
     4728.  21913                        free = false;
    +
     4729.  21913                        open = false;
    +
     4730.  21913                        no_space_only();
    +
     4731.  21913                    }
    +
     4732.  21913                } else {
    +
     4733.  21913
    +
     4734.  21913// If left and right are opener and closer, then the placement of right depends
    +
     4735.  21913// on the openness. Illegal pairs (like '{]') have already been detected.
    +
     4736.  21913
    +
     4737.  21913                    if (left.line === right.line) {
    +
     4738.  21913                        no_space();
    +
     4739.      0                    } else {
    +
     4740.      0                        at_margin(0);
    +
     4741.      0                    }
    +
     4742.  21913                }
    +
     4743.  21913            } else {
    +
     4744.  21913                if (right.statement === true) {
    +
     4745.  21913                    if (left.id === "else") {
    +
     4746.  21913                        one_space_only();
    +
     4747.  21913                    } else {
    +
     4748.  21913                        at_margin(0);
    +
     4749.  21913                        open = false;
    +
     4750.  21913                    }
    +
     4751.  21913
    +
     4752.  21913// If right is a closer, then pop the previous state.
    +
     4753.  21913
    +
     4754.  21913                } else if (right.id === closer) {
    +
     4755.  21913                    pop();
    +
     4756.  21913                    if (opening && right.id !== ";") {
    +
     4757.  21913                        at_margin(0);
    +
     4758.  21913                    } else {
    +
     4759.  21913                        no_space_only();
    +
     4760.  21913                    }
    +
     4761.  21913                } else {
    +
     4762.  21913
    +
     4763.  21913// Left is not an opener, and right is not a closer.
    +
     4764.  21913// The nature of left and right will determine the space between them.
    +
     4765.  21913
    +
     4766.  21913// If left is ',' or ';' or right is a statement then if open,
    +
     4767.  21913// right must go at the margin, or if closed, a space between.
    +
     4768.  21913
    +
     4769.      0                    if (right.switch) {
    +
     4770.      0                        at_margin(-4);
    +
     4771.      0                    } else if (right.role === "label") {
    +
     4772.      0                        if (right.from !== 0) {
    +
     4773.      0                            expected_at(0);
    +
     4774.      0                        }
    +
     4775.      0                    } else if (left.id === ",") {
    +
     4776.  21913                        if (!open || (
    +
     4777.  21913                            (free || closer === "]")
    +
     4778.  21913                            && left.line === right.line
    +
     4779.  21913                        )) {
    +
     4780.  21913                            one_space();
    +
     4781.  21913                        } else {
    +
     4782.  21913                            at_margin(0);
    +
     4783.  21913                        }
    +
     4784.  21913
    +
     4785.  21913// If right is a ternary operator, line it up on the margin.
    +
     4786.  21913
    +
     4787.  21913                    } else if (right.arity === "ternary") {
    +
     4788.  21913                        if (open) {
    +
     4789.  21913                            at_margin(0);
    +
     4790.      0                        } else {
    +
     4791.      0                            warn("use_open", right);
    +
     4792.      0                        }
    +
     4793.  21913                    } else if (
    +
     4794.  21913                        right.arity === "binary"
    +
     4795.  21913                        && right.id === "("
    +
     4796.  21913                        && free
    +
     4797.  21913                    ) {
    +
     4798.  21913                        no_space();
    +
     4799.  21913                    } else if (
    +
     4800.  21913                        left.id === "."
    +
     4801.  21913                        || left.id === "?."
    +
     4802.  21913                        || left.id === "..."
    +
     4803.  21913                        || right.id === ","
    +
     4804.  21913                        || right.id === ";"
    +
     4805.  21913                        || right.id === ":"
    +
     4806.  21913                        || (
    +
     4807.  21913                            right.arity === "binary"
    +
     4808.  21913                            && (right.id === "(" || right.id === "[")
    +
     4809.  21913                        )
    +
     4810.  21913                        || (
    +
     4811.  21913                            right.arity === "function"
    +
     4812.  21913                            && left.id !== "function"
    +
     4813.  21913                        )
    +
     4814.  21913                    ) {
    +
     4815.  21913                        no_space_only();
    +
     4816.  21913                    } else if (right.id === "." || right.id === "?.") {
    +
     4817.  21913                        no_space_only();
    +
     4818.      0                    } else if (left.id === ";") {
    +
     4819.      0                        if (open) {
    +
     4820.      0                            at_margin(0);
    +
     4821.      0                        }
    +
     4822.      0                    } else if (
    +
     4823.  21913                        left.arity === "ternary"
    +
     4824.  21913                        || left.id === "case"
    +
     4825.  21913                        || left.id === "catch"
    +
     4826.  21913                        || left.id === "else"
    +
     4827.  21913                        || left.id === "finally"
    +
     4828.  21913                        || left.id === "while"
    +
     4829.  21913                        || right.id === "catch"
    +
     4830.  21913                        || right.id === "else"
    +
     4831.  21913                        || right.id === "finally"
    +
     4832.      0                        || (right.id === "while" && !right.statement)
    +
     4833.  21913                        || (left.id === ")" && right.id === "{")
    +
     4834.  21913                    ) {
    +
     4835.  21913                        one_space_only();
    +
     4836.  21913                    } else if (
    +
     4837.  21913
    +
     4838.  21913// There is a space between left and right.
    +
     4839.  21913
    +
     4840.  21913                        spaceop[left.id] === true
    +
     4841.  21913                        || spaceop[right.id] === true
    +
     4842.  21913                        || (
    +
     4843.  21913                            left.arity === "binary"
    +
     4844.  21913                            && (left.id === "+" || left.id === "-")
    +
     4845.  21913                        )
    +
     4846.  21913                        || (
    +
     4847.  21913                            right.arity === "binary"
    +
     4848.  21913                            && (right.id === "+" || right.id === "-")
    +
     4849.  21913                        )
    +
     4850.  21913                        || left.id === "function"
    +
     4851.  21913                        || left.id === ":"
    +
     4852.  21913                        || (
    +
     4853.  21913                            (
    +
     4854.  21913                                left.identifier
    +
     4855.  21913                                || left.id === "(string)"
    +
     4856.  21913                                || left.id === "(number)"
    +
     4857.  21913                            )
    +
     4858.  21913                            && (
    +
     4859.  21913                                right.identifier
    +
     4860.  21913                                || right.id === "(string)"
    +
     4861.  21913                                || right.id === "(number)"
    +
     4862.  21913                            )
    +
     4863.  21913                        )
    +
     4864.  21913                        || (left.arity === "statement" && right.id !== ";")
    +
     4865.  21913                    ) {
    +
     4866.  21913                        one_space();
    +
     4867.  21913                    } else if (left.arity === "unary" && left.id !== "`") {
    +
     4868.  21913                        no_space_only();
    +
     4869.  21913                    }
    +
     4870.  21913                }
    +
     4871.  21913            }
    +
     4872.  21913            nr_comments_skipped = 0;
    +
     4873.  21913            delete left.calls;
    +
     4874.  21913            delete left.dead;
    +
     4875.  21913            delete left.free;
    +
     4876.  21913            delete left.init;
    +
     4877.  21913            delete left.open;
    +
     4878.  21913            delete left.used;
    +
     4879.  21913            left = right;
    +
     4880.  21913        }
    +
     4881.  22338    });
    +
     4882.      1}
    +
     4883.      1
    +
     4884.      1// The jslint function itself.
    +
     4885.      1
    +
     4886.      1export default Object.freeze(function jslint(
    +
     4887.      1    source = "",
    +
     4888.      1    option_object = empty(),
    +
     4889.      1    global_array = []
    +
     4890.      1) {
    +
     4891.      1    try {
    +
     4892.      1        warnings = [];
    +
     4893.      1        option = Object.assign(empty(), option_object);
    +
     4894.      1        anon = "anonymous";
    +
     4895.      1        block_stack = [];
    +
     4896.      1        declared_globals = empty();
    +
     4897.      1        directive_mode = true;
    +
     4898.      1        directives = [];
    +
     4899.      1        early_stop = true;
    +
     4900.      1        exports = empty();
    +
     4901.      1        froms = [];
    +
     4902.      1        fudge = (
    +
     4903.      1            option.fudge
    +
     4904.      1            ? 1
    +
     4905.      0            : 0
    +
     4906.      1        );
    +
     4907.      1        functions = [];
    +
     4908.      1        global = {
    +
     4909.      1            id: "(global)",
    +
     4910.      1            body: true,
    +
     4911.      1            context: empty(),
    +
     4912.      1            from: 0,
    +
     4913.      1            level: 0,
    +
     4914.      1            line: 0,
    +
     4915.      1            live: [],
    +
     4916.      1            loop: 0,
    +
     4917.      1            switch: 0,
    +
     4918.      1            thru: 0
    +
     4919.      1        };
    +
     4920.      1        blockage = global;
    +
     4921.      1        functionage = global;
    +
     4922.      1        json_mode = false;
    +
     4923.      1        mega_mode = false;
    +
     4924.      1        module_mode = false;
    +
     4925.      1        next_token = global;
    +
     4926.      1        property = empty();
    +
     4927.      1        shebang = false;
    +
     4928.      1        stack = [];
    +
     4929.      1        tenure = undefined;
    +
     4930.      1        token = global;
    +
     4931.      1        token_nr = 0;
    +
     4932.      1        var_mode = undefined;
    +
     4933.      1        populate(standard, declared_globals, false);
    +
     4934.      1        populate(global_array, declared_globals, false);
    +
     4935.      6        Object.keys(option).forEach(function (name) {
    +
     4936.      6            if (option[name] === true) {
    +
     4937.      6                const allowed = allowed_option[name];
    +
     4938.      2                if (Array.isArray(allowed)) {
    +
     4939.      2                    populate(allowed, declared_globals, false);
    +
     4940.      2                }
    +
     4941.      6            }
    +
     4942.      6        });
    +
     4943.      1        tokenize(source);
    +
     4944.      1        advance();
    +
     4945.      0        if (json_mode) {
    +
     4946.      0            tree = json_value();
    +
     4947.      0            advance("(end)");
    +
     4948.      0        } else {
    +
     4949.      1
    +
     4950.      1// Because browsers encourage combining of script files, the first token might
    +
     4951.      1// be a semicolon to defend against a missing semicolon in the preceding file.
    +
     4952.      1
    +
     4953.      1            if (option.browser) {
    +
     4954.      0                if (next_token.id === ";") {
    +
     4955.      0                    advance(";");
    +
     4956.      0                }
    +
     4957.      0            } else {
    +
     4958.      0
    +
     4959.      0// If we are not in a browser, then the file form of strict pragma may be used.
    +
     4960.      0
    +
     4961.      0                if (
    +
     4962.      0                    next_token.value === "use strict"
    +
     4963.      0                ) {
    +
     4964.      0                    advance("(string)");
    +
     4965.      0                    advance(";");
    +
     4966.      0                }
    +
     4967.      0            }
    +
     4968.      1            tree = statements();
    +
     4969.      1            advance("(end)");
    +
     4970.      1            functionage = global;
    +
     4971.      1            walk_statement(tree);
    +
     4972.      1            if (warnings.length === 0) {
    +
     4973.      1                uninitialized_and_unused();
    +
     4974.      1                if (!option.white) {
    +
     4975.      1                    whitage();
    +
     4976.      1                }
    +
     4977.      1            }
    +
     4978.      1        }
    +
     4979.      0        if (!option.browser) {
    +
     4980.      0            directives.forEach(function (comment) {
    +
     4981.      0                if (comment.directive === "global") {
    +
     4982.      0                    warn("missing_browser", comment);
    +
     4983.      0                }
    +
     4984.      0            });
    +
     4985.      0        }
    +
     4986.      1        early_stop = false;
    +
     4987.      0    } catch (e) {
    +
     4988.      0        if (e.name !== "JSLintError") {
    +
     4989.      0            warnings.push(e);
    +
     4990.      0        }
    +
     4991.      0    }
    +
     4992.      1    return {
    +
     4993.      1        directives,
    +
     4994.      1        edition: "2020-11-06",
    +
     4995.      1        exports,
    +
     4996.      1        froms,
    +
     4997.      1        functions,
    +
     4998.      1        global,
    +
     4999.      1        id: "(JSLint)",
    +
     5000.      1        json: json_mode,
    +
     5001.      1        lines,
    +
     5002.      1        module: module_mode === true,
    +
     5003.      1        ok: warnings.length === 0 && !early_stop,
    +
     5004.      1        option,
    +
     5005.      1        property,
    +
     5006.      1        shebang: (
    +
     5007.      1            shebang
    +
     5008.      0            ? lines[0]
    +
     5009.      1            : undefined
    +
     5010.      1        ),
    +
     5011.      1        stop: early_stop,
    +
     5012.      1        tokens,
    +
     5013.      1        tree,
    +
     5014.      0        warnings: warnings.sort(function (a, b) {
    +
     5015.      0            return a.line - b.line || a.column - b.column;
    +
     5016.      0        })
    +
     5017.      1    };
    +
     5018.      1});
    +
     5019.      1
    + +
    +
    +
    + + diff --git a/branch.ci/.github/workflows/node.js.yml b/branch.ci/.github/workflows/node.js.yml new file mode 100644 index 000000000..670ae3e76 --- /dev/null +++ b/branch.ci/.github/workflows/node.js.yml @@ -0,0 +1,42 @@ +# derived from +# https://github.com/actions/starter-workflows/blob/main/ci/node.js.yml +# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions +name: Node.js CI +on: + push: + branches: + - alpha + # - beta + - ci + # - none + # pull_request: + # branches: + # - alpha + # - beta +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: + # - 12.x + - 14.x + # - 16.x + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - shell: sh + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # do not trust current-branch ci.sh + git fetch origin ci && git checkout origin/ci ci.sh + # run ci + sh ci.sh shGithubCi + # - run: npm ci + # - run: npm run build --if-present + # - run: npm test diff --git a/branch.ci/README b/branch.ci/README new file mode 100644 index 000000000..cd6b06c8b --- /dev/null +++ b/branch.ci/README @@ -0,0 +1,39 @@ +JSLint, The JavaScript Code Quality Tool + +Douglas Crockford +douglas@crockford.com + +2019-03-15 + +jslint.js contains the jslint function. It parses and analyzes a source file, +returning an object with information about the file. It can also take an object +that sets options. + +index.html runs the jslint.js function in a web page. The page also depends +browser.js and report.js and jslint.css. + +jslint.css provides styling for index.html. + +browser.js runs the web user interface. + +report.js generates the results reports in HTML. + +help.html describes JSLint's usage. Please read it. + +function.html describes the jslint function and the results it produces. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[http://www.crockford.com/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. diff --git a/branch.ci/browser.js b/branch.ci/browser.js new file mode 100644 index 000000000..5086fed85 --- /dev/null +++ b/branch.ci/browser.js @@ -0,0 +1,158 @@ +// browser.js +// 2018-06-16 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +/*jslint + browser +*/ + +/*property + checked, create, disable, display, error, focus, forEach, function, + getElementById, innerHTML, join, length, map, onchange, onclick, onscroll, + property, querySelectorAll, scrollTop, select, split, style, title, value +*/ + +import jslint from "./jslint.js"; +import report from "./report.js"; + +// This is the web script companion file for JSLint. It includes code for +// interacting with the browser and displaying the reports. + +let rx_crlf = /\n|\r\n?/; +let rx_separator = /[\s,;'"]+/; + +let fudge_unit = 0; + +const aux = document.getElementById("JSLINT_AUX"); +const boxes = document.querySelectorAll("[type=checkbox]"); +const fudge = document.getElementById("JSLINT_FUDGE"); +const global = document.getElementById("JSLINT_GLOBAL"); +const number = document.getElementById("JSLINT_NUMBER"); +const property = document.getElementById("JSLINT_PROPERTY"); +const property_fieldset = document.getElementById("JSLINT_PROPERTYFIELDSET"); +const report_field = document.getElementById("JSLINT_REPORT"); +const report_list = document.getElementById("JSLINT_REPORT_LIST"); +const source = document.getElementById("JSLINT_SOURCE"); +const select = document.getElementById("JSLINT_SELECT"); +const warnings = document.getElementById("JSLINT_WARNINGS"); +const warnings_list = document.getElementById("JSLINT_WARNINGS_LIST"); + +function show_numbers() { + number.value = source.value.split(rx_crlf).map(function (ignore, index) { + return index + fudge_unit; + }).join("\n"); +} + +function clear() { + aux.style.display = "none"; + number.value = ""; + property.value = ""; + property_fieldset.style.display = "none"; + report_field.style.display = "none"; + report_list.innerHTML = ""; + source.focus(); + source.value = ""; + warnings.style.display = "none"; + warnings_list.innerHTML = ""; +} + +function fudge_change() { + fudge_unit = Number(fudge.checked); + show_numbers(); +} + +function clear_options() { + boxes.forEach(function (node) { + node.checked = false; + }); + fudge_change(); + global.innerHTML = ""; +} + +function call_jslint() { + +// First build the option object. + + let option = Object.create(null); + boxes.forEach(function (node) { + if (node.checked) { + option[node.title] = true; + } + }); + +// Call JSLint with the source text, the options, and the predefined globals. + + let global_string = global.value; + let result = jslint( + source.value, + option, + ( + global_string === "" + ? undefined + : global_string.split(rx_separator) + ) + ); + +// Generate the reports. + + let error_html = report.error(result); + let function_html = report.function(result); + let property_text = report.property(result); + +// Display the reports. + + warnings_list.innerHTML = error_html; + warnings.style.display = ( + error_html.length === 0 + ? "none" + : "block" + ); + + report_list.innerHTML = function_html; + report_field.style.display = "block"; + if (property_text) { + property.value = property_text; + property_fieldset.style.display = "block"; + property.scrollTop = 0; + select.disable = false; + } else { + property_fieldset.style.display = "none"; + select.disable = true; + } + aux.style.display = "block"; + source.select(); +} + +fudge.onchange = fudge_change; + +source.onchange = function (ignore) { + show_numbers(); +}; + +source.onscroll = function () { + let ss = source.scrollTop; + number.scrollTop = ss; + let sn = number.scrollTop; + if (ss > sn) { + show_numbers(); + number.scrollTop = ss; + } +}; + +document.querySelectorAll("[name='JSLint']").forEach(function (node) { + node.onclick = call_jslint; +}); + +document.querySelectorAll("[name='clear']").forEach(function (node) { + node.onclick = clear; +}); + +document.getElementById("JSLINT_SELECT").onclick = function () { + property.focus(); + property.select(); +}; +document.getElementById("JSLINT_CLEAR_OPTIONS").onclick = clear_options; + +fudge_change(); +source.select(); +source.focus(); diff --git a/branch.ci/ci.sh b/branch.ci/ci.sh new file mode 100644 index 000000000..084a2c2f3 --- /dev/null +++ b/branch.ci/ci.sh @@ -0,0 +1,901 @@ +#!/bin/sh + +shGithubCi() {(set -e +# this function will run github-ci + local BRANCH + # jslint all files + shJslintCli . + # create coverage-report + shV8CoverageReport shJslintCli jslint.js + if [ ! "$GITHUB_ACTIONS" ] + then + return + fi + # init $BRANCH + BRANCH="$(git rev-parse --abbrev-ref HEAD)" + # init .git/config + git config --local user.email "github-actions@users.noreply.github.com" + git config --local user.name "github-actions" + # commit coverage-report + git add -f .coverage/ + git commit -am "add coverage-report" + # add to gh-pages + git checkout -b gh-pages + git fetch origin gh-pages + git reset origin/gh-pages --hard + rm -rf "branch.$BRANCH" + mkdir "branch.$BRANCH" + cp -a .git "branch.$BRANCH" + (cd "branch.$BRANCH" && git reset "$BRANCH" --hard) + rm -rf "branch.$BRANCH/.git" + git add -f "branch.$BRANCH" + git commit -am "add branch" || true + shGitCmdWithGithubToken push origin HEAD:gh-pages +)} + +shGitCmdWithGithubToken() {(set -e +# this function will run git $CMD with $GITHUB_TOKEN + local CMD + local EXIT_CODE + local REMOTE + local URL + printf "shGitCmdWithGithubToken $*\n" + CMD="$1" + shift + REMOTE="$1" + shift + URL="$( + git config "remote.$REMOTE.url" | + sed -e "s|https://|https://x-access-token:$GITHUB_TOKEN@|" + )" + EXIT_CODE=0 + # hide $GITHUB_TOKEN in case of err + git "$CMD" "$URL" "$@" 2>/dev/null || EXIT_CODE="$?" + printf "EXIT_CODE=$EXIT_CODE\n" + return "$EXIT_CODE" +)} + +shJslintCli() {(set -e +# this function will run jslint from nodejs + node -e ' +import jslint from "./jslint.js"; +import {promises, readFileSync, readdirSync} from "fs"; +// init debugInline +if (!globalThis.debugInline) { + let consoleError; + consoleError = console.error; + globalThis.debugInline = function (...argList) { + /* + * this function will both print to stderr and + * return [0] + */ + consoleError("\n\ndebugInline"); + consoleError(...argList); + consoleError("\n"); + return argList[0]; + }; +} +// hack-jslint - cli +let errMsg; +function stringLineCount(data) { +/* + * this function will count number of newlines in + */ + let cnt; + let ii; + // https://jsperf.com/regexp-counting-2/8 + cnt = 0; + ii = 0; + while (true) { + ii = data.indexOf("\n", ii) + 1; + if (ii === 0) { + break; + } + cnt += 1; + } + return cnt; +} +async function jslint2({ + code, + err, + errList = [], + file, + lineOffset = 0, + previous +}) { + switch (( + /\.\w+?$|$/m + ).exec(file)[0]) { + case ".css": + errList = CSSLint.verify( // jslint ignore:line + code + ).messages.map(function (err) { + err.message = ( + err.type + " - " + err.rule.id + " - " + err.message + + "\n " + err.rule.desc + ); + return err; + }); + // ignore comment + code = code.replace(( + /^\u0020*?\/\*[\S\s]*?\*\/\u0020*?$/gm + ), function (match0) { + // preserve lineno + return match0.replace(( + /.+/g + ), ""); + }); + code.replace(( + /\S\u0020{2}|\u0020,|^\S.*?,.|[;{}]./gm + ), function (match0, ii) { + switch (match0.slice(-2)) { + case " ": + err = { + message: "unexpected multi-whitespace" + }; + break; + case " ,": + err = { + message: "unexpected whitespace before comma" + }; + break; + default: + err = { + message: "unexpected multiline-statement" + }; + } + errList.push(Object.assign(err, { + col: 1, + evidence: match0, + line: stringLineCount(code.slice(0, ii)) + })); + return ""; + }); + // validate line-sorted - css-selector + previous = ""; + code = code.replace(( + /^.|[#.>]|[,}]$|\u0020\{$|\b\w/gm + ), function (match0) { + switch (match0) { + case " ": + return match0; + case " {": + return "\u0001" + match0; + case "#": + return "\u0002" + match0; + case ",": + return "\u0000" + match0; + case ".": + return "\u0001" + match0; + case ">": + return "\u0003" + match0; + case "}": + return match0; + default: + return "\u0000" + match0; + } + }); + code.replace(( + /\n{2,}|^\u0000@|^\}\n\}|\}|^(?:\S.*?\n)+/gm + ), function (match0, ii) { + switch (match0.slice(0, 2)) { + case "\n\n": + case "\u0000@": + case "}\n": + previous = ""; + return ""; + case "}": + return ""; + } + match0 = match0.trim(); + err = ( + !(previous < match0) + ? { + message: "lines not sorted\n" + previous + "\n" + match0 + } + : match0.split("\n").sort().join("\n") !== match0 + ? { + message: "lines not sorted\n" + match0 + } + : undefined + ); + if (err) { + errList.push(Object.assign(err, { + col: 1, + evidence: match0, + line: stringLineCount(code.slice(0, ii)), + message: err.message.replace(( + /[\u0000-\u0007]/g + ), "") + })); + } + previous = match0; + return ""; + }); + break; + case ".html": + // recurse + code.replace(( + /^ + + +
    +
    coverage-report
    + + + + + + + +`; + if (!lineList) { + padLines = String("100.00 %").length; + padPathname = 32; + fileList.unshift({ + linesCovered: 0, + linesTotal: 0, + pathname: "./" + }); + fileList.slice(1).forEach(function ({ + linesCovered, + linesTotal, + pathname + }) { + fileList[0].linesCovered += linesCovered; + fileList[0].linesTotal += linesTotal; + padPathname = Math.max(padPathname, pathname.length + 2); + padLines = Math.max( + padLines, + String(linesCovered + " / " + linesTotal).length + ); + }); + } + txtBorder = ( + "+" + "-".repeat(padPathname + 2) + "+" + + "-".repeat(padLines + 2) + "+\n" + ); + txt = ""; + txt += "coverage-report\n"; + txt += txtBorder; + txt += ( + "| " + String("files covered").padEnd(padPathname, " ") + " | " + + String("lines").padStart(padLines, " ") + " |\n" + ); + txt += txtBorder; + fileList.forEach(function ({ + linesCovered, + linesTotal, + pathname + }, ii) { + let coverageLevel; + let coveragePct; + coveragePct = Math.floor(10000 * linesCovered / linesTotal | 0); + coverageLevel = ( + coveragePct >= 8000 + ? "coverageHigh" + : coveragePct >= 5000 + ? "coverageMedium" + : "coverageLow" + ); + coveragePct = String(coveragePct).replace(( + /..$/m + ), ".$&"); + if (!lineList && ii === 0) { + pathname = ""; + } + txt += ( + "| " + + String("./" + pathname).padEnd(padPathname, " ") + " | " + + String(coveragePct + " %").padStart(padLines, " ") + " |\n" + ); + txt += ( + "| " + "*".repeat( + Math.round(0.01 * coveragePct * padPathname) + ).padEnd(padPathname, "_") + " | " + + String( + linesCovered + " / " + linesTotal + ).padStart(padLines, " ") + " |\n" + ); + txt += txtBorder; + pathname = stringHtmlSafe(pathname); + html += ` + + +`; + }); + if (lineList) { + html += ` +
    files coveredlines
    + ${( + lineList + ? ( + "./ " + + pathname + "
    " + ) + : ( + "./ " + + pathname + "
    " + ) + )} +
    +
    +
    +
    + ${coveragePct} %
    + ${linesCovered} / ${linesTotal} +
    +
    +
    +`; + lineList.forEach(function ({ + count, + holeList, + line, + startOffset + }, ii) { + let chunk; + let inHole; + let lineId; + let lineHtml; + lineHtml = ""; + lineId = "line_" + (ii + 1); + switch (count) { + case -1: + case 0: + if (holeList.length === 0) { + lineHtml += ""; + lineHtml += ""; + lineHtml += stringHtmlSafe(line); + break; + } + line = line.split("").map(function (chr) { + return { + chr, + isHole: undefined + }; + }); + holeList.forEach(function ([ + aa, bb + ]) { + aa = Math.max(aa - startOffset, 0); + bb = Math.min(bb - startOffset, line.length); + while (aa < bb) { + line[aa].isHole = true; + aa += 1; + } + }); + chunk = ""; + line.forEach(function ({ + chr, + isHole + }) { + if (inHole !== isHole) { + lineHtml += stringHtmlSafe(chunk); + lineHtml += ( + isHole + ? "" + : "" + ); + chunk = ""; + inHole = isHole; + } + chunk += chr; + }); + lineHtml += stringHtmlSafe(chunk); + break; + default: + lineHtml += stringHtmlSafe(line); + } + html += String(` +
    +
    +${String(ii + 1).padStart(5, " ")}.
    +
    +
    +${String(count).padStart(7, " ")}
    +
    +${lineHtml}
    +
    + `).replace(( + /\n/g + ), "").trim() + "\n"; + }); + } + html += ` +
    +
    +
    + +`; + html += "\n"; + await require("fs").promises.mkdir(require("path").dirname(pathname), { + recursive: true + }); + if (!lineList) { + console.error("\n" + txt); + await require("fs").promises.writeFile(( + ".coverage/coverage.txt" + ), txt); + } + await require("fs").promises.writeFile(pathname + ".html", html); + } + data = await require("fs").promises.readdir(".coverage/"); + await Promise.all(data.map(async function (file) { + if (( + /^coverage-.*?\.json$/ + ).test(file)) { + data = await require("fs").promises.readFile(( + ".coverage/" + file + ), "utf8"); + require("fs").promises.rename( + ".coverage/" + file, + ".coverage/coverage_v8.json" + ); + } + })); + fileDict = {}; + cwd = process.cwd().replace(( + /\\/g + ), "/") + "/"; + await Promise.all(JSON.parse(data).result.map(async function ({ + functions, + url + }) { + let lineList; + let linesCovered; + let linesTotal; + let pathname; + let src; + if (url.indexOf("file:///") !== 0) { + return; + } + pathname = url.replace(( + process.platform === "win32" + ? "file:///" + : "file://" + ), "").replace(( + /\\\\/g + ), "/"); + if ( + pathname.indexOf(cwd) !== 0 || + pathname.indexOf(cwd + "[") === 0 || + ( + process.env.npm_config_mode_coverage !== "all" && + pathname.indexOf("/node_modules/") >= 0 + ) + ) { + return; + } + pathname = pathname.replace(cwd, ""); + src = await require("fs").promises.readFile(pathname, "utf8"); + lineList = [{}]; + src.replace(( + /^.*$/gm + ), function (line, startOffset) { + lineList[lineList.length - 1].endOffset = startOffset - 1; + lineList.push({ + count: -1, + endOffset: 0, + holeList: [], + line, + startOffset + }); + return ""; + }); + lineList.shift(); + lineList[lineList.length - 1].endOffset = src.length; + functions.reverse().forEach(function ({ + ranges + }) { + ranges.reverse().forEach(function ({ + count, + endOffset, + startOffset + }, ii, list) { + lineList.forEach(function (elem) { + /* + debugInline( + count, + [elem.startOffset, startOffset], + [elem.endOffset, endOffset], + elem.line + ); + */ + if (!( + ( + elem.startOffset <= startOffset && + startOffset <= elem.endOffset + ) || ( + elem.startOffset <= endOffset && + endOffset <= elem.endOffset + ) || ( + startOffset <= elem.startOffset && + elem.endOffset <= endOffset + ) + )) { + return; + } + // handle root-range + if (ii + 1 === list.length) { + if (elem.count === -1) { + elem.count = count; + } + return; + } + // handle non-root-range + if (elem.count !== 0) { + elem.count = Math.max(count, elem.count); + } + if (count === 0) { + elem.count = 0; + elem.holeList.push([ + startOffset, endOffset + ]); + } + }); + }); + }); + linesTotal = lineList.length; + linesCovered = lineList.filter(function ({ + count + }) { + return count > 0; + }).length; + await require("fs").promises.mkdir(( + require("path").dirname(".coverage/" + pathname) + ), { + recursive: true + }); + await htmlRender({ + fileList: [ + { + linesCovered, + linesTotal, + pathname + } + ], + lineList, + pathname: ".coverage/" + pathname + }); + fileDict[pathname] = { + lineList, + linesCovered, + linesTotal, + pathname, + src + }; + })); + await htmlRender({ + fileList: Object.keys(fileDict).sort().map(function (pathname) { + return fileDict[pathname]; + }), + pathname: ".coverage/index" + }); +}()); +' # ' +)} + +shWinpty() {(set -e +# this function will run "$@" in current-env with winpty + winpty -Xallow-non-tty -Xplain sh "$HOME/lib.utility2.sh" "$@" +)} + +# run "$@" +"$@" diff --git a/branch.ci/function.html b/branch.ci/function.html new file mode 100644 index 000000000..75bf39360 --- /dev/null +++ b/branch.ci/function.html @@ -0,0 +1,615 @@ + + + + + + + + +JSLint: jslint function + + + +
    JSLint
    + +
    The jslint Function
    +
    +

    JSLint

    +

    JSLint is delivered as a set of files:

    +
    + + + + + + + + + + + + + + +
    FilenameContent
    browser.jsBrowser based UI.
    function.htmlThis page that you are looking at right now.
    help.htmlUsing jslint as a single page JSLint application.
    index.htmlSingle page JSLint application that uses the + js files.
    jslint.jsThe jslint function.
    report.jsThe report object, containing report generator functions for + HTML.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    +
    + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring"(JSLint)"
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    parenttokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    parenttokenThe function in which the variable was declared.variable
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    shebangstringThe first line of text if it started with #!line
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable may be assigned to.variable
    +

    Reports

    +

    The report object contains three functions that all take a + result from the jslint function as input. + report.js has no other dependence on other files.

    + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    +
    + +
    + + + +
    + + diff --git a/branch.ci/help.html b/branch.ci/help.html new file mode 100644 index 000000000..358a35baa --- /dev/null +++ b/branch.ci/help.html @@ -0,0 +1,812 @@ + + + + + + + + + +JSLint: Help + + + +
    JSLint
    + +
    Help
    +
    +
    Il semble que la perfection soit atteinte non quand il n’y a + plus rien à ajouter, mais quand il n’y a plus rien à + retrancher.
    +
    + Antoine de Saint-Exupéry +
    +
    + Terre des Hommes (1939) +
    +

    Good Parts

    +

    The Principle of the Good Parts is

    +
    If a feature is sometimes useful and sometimes dangerous and if + there is a better option then always use the better option.
    +

    JSLint is a JavaScript program that looks for problems in + JavaScript programs. It is a code quality tool.

    +

    When C was + a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    +

    As the language matured, the definition of the language was + strengthened to eliminate some insecurities, and compilers got better + at issuing warnings. lint is no longer needed.

    +

    JavaScript is a + young-for-its-age language. It was originally intended to do small tasks in + webpages, tasks for which Java was too heavy and clumsy. But JavaScript is + a surprisingly capable language, and it is now being used in larger + projects. Many of the features that were intended to make the language easy + to use are troublesome when projects become complicated. A lint + for JavaScript is needed: JSLint, a JavaScript syntax checker + and validator.

    +

    JSLint takes a JavaScript source and scans it. If it finds a + problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by the ECMAScript Programming Language + Standard (the strangely named document that governs JavaScript). + JSLint will reject most legal programs. It is a higher + standard.

    +

    JavaScript is a sloppy language, but hidden deep inside there is an elegant, + better language. JSLint helps you to program in that better + language and to avoid most of the slop. JSLint will reject + programs that browsers will accept because JSLint is concerned + with the quality of your code and browsers are not. You should gladly accept + all of JSLint's advice.

    +

    JSLint can operate on JavaScript source + or JSON text.

    +

    ECMAScript Sixth Edition

    +

    Some of + ES6’s features are good, so JSLint will recognize the good + parts of ES6.

    +

    Currently, these features are recognized:

    +
      +
    • The ... ellipsis marker in parameter + lists and argument lists, replacing the arguments object + for variadic functions.
    • +
    • The let statement, which is like the var + statement except that it respects block scope. + You may use let or var but not both.
    • +
    • The const statement is like the let statement + except that it disallows the use of assignment on the variable, although + if the value of the variable is mutable, it can still be mutated. + const is preferred to let. +
    • Destructuring of arrays and objects is allowed in parameter lists and on + the left side of let, and const, but not + var or assignment statements, and not deep destructuring + or eliding.
    • +
    • Enhanced object literals, providing shorter forms for function + declaration and properties that are initialized by variables with the + same name.
    • +
    • The fat arrow => fart functions.
    • +
    • The simplest forms of import and export.
    • +
    • `Megastring` literals, but not nested + `megastring` literals.
    • +
    • New global functions, such as Map, Set, + WeakMap, and WeakSet.
    • +
    • 0b- and 0o- number literals.
    • +
    +

    The most important new feature of ES6 is proper tail calls. This has no + new syntax, so JSLint doesn’t see it. But it makes recursion + much more attractive, which makes loops, particularly for + loops, much less attractive.

    +

    import export

    +

    The ES6 module feature will be an important improvement over JavaScript’s + global variables as a means of linking separate files together. + JSLint recognizes a small but essential subset of the module + syntax.

    +
    +

    import name from stringliteral;

    +

    import {name} from stringliteral;

    +

    import(string).then(function);

    +

    export default function () {};

    +

    export default function name() {};

    +

    export default expression;

    +

    export function name() {}

    +

    export name; // where name is const

    +

    export {name};

    +
    +

    Directives

    +

    JSLint provides three directives that may be placed in a + file to manage JSLint’s behavior. Use of these directives is + optional. If they are used, they should be placed in a source file before + the first statement. They are written in the form of a comment, where the + directive name is placed immediately after the opening of the comment before + any whitespace. The three directives are global, + jslint, and property. Directives in a file are + stronger than options selected from the UI or passed with the option object.

    +

    /*global*/

    +

    The /*global*/ directive is used to specify a set of globals + (usually functions and objects containing functions) that are available to + this file. This was commonly used in browsers to link source files together + before ES6 modules appeared. Use of global variables is strongly + discouraged, but unfortunately web browsers require their use. The + /*global*/ directive can only be used when the + Assume a browser option is selected. +

    Each of the names listed will indicate a read-only global variable. The names + are separated by , comma. This directive + should not be used if import or export is used. + This directive inhibits warnings, but it does not declare the names in the + execution environment. +

    For example: +

    /*global +
    ADSAFE, report, jslint
    +*/
    +

    instructs JSLint to not give warnings about the global + variables ADsafe, report, and jslint. + However, if any of those names are expected to be supplied by other files + and those other files fail to do so, then execution errors will result. It + is usually better to use top-level variable declarations instead: +

        var ADSAFE;
    +    var report;
    +    var jslint; 
    +

    Using var in this way allows comparing a global variable to the + undefined value to determine whether it is has been used in the global context.

    +

    /*jslint*/

    +

    The /*jslint*/ directive allows for the control of several + options. These options can also be set from the + JSLint.com user interface.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Tolerate bitwise operatorsbitwisetrue if bitwise operators should be allowed. The + bitwise operators are rarely used in JavaScript programs, so it + is usually more likely that & is a mistyping of + && than that it indicates a bitwise and. + JSLint will give warnings on the bitwise operators + unless this option is selected.
    Assume a browser browsertrue if the standard browser globals should be + predefined. This option will reject the use of + import and export. This option also + disallows the file form of the "use + strict" pragma. It does not supply + self; you will have to request that unnecessary + alias of the dreaded global object yourself. It adds the same + globals as this directive: +
    /*global +
    + clearInterval, clearTimeout, document, event, FileReader, + FormData, history, localStorage, location, name, navigator, + screen, sessionStorage, setInterval, setTimeout, Storage, + URL, window, XMLHttpRequest +
    + */
    +
    Tolerate conversion operatorsconverttrue if !!, + prefix, + and concatenation with "" are allowed. + The Boolean, Number, and + String functions are preferred
    Assume CouchDBcouchtrue if + Couch DB + globals should be predefined. It adds the same globals as this + directive: +
    /*global +
    emit, getRow, isArray, log, provides, registerType, require, + send, start, sum, toJSON
    + */
    Assume in developmentdeveltrue if browser globals that are useful in + development should be predefined, and if debugger + statements and TODO comments + should be allowed. It adds the + same globals as this directive: +
    /*global +
    alert, confirm, console, prompt
    + */
    Be sure to turn this option off before going + into production.
    Tolerate evalevaltrue if eval should be allowed. In the + past, the eval function was the most misused + feature of the language.
    Tolerate for statementfortrue if the for statement should be + allowed. It is almost always better to use the array methods + instead.
    Fudge the line and column numbersfudgetrue if the origin of a text file is line 1 column + 1. Programmers know that number systems start at zero. + Unfortunately, most text editors start numbering lines and + columns at one. JSLint will by default start + numbering at zero. Use this option to start the numbering at one + in its reports.
    Tolerate get and setgetsettrue if accessor properties are allowed in object literals.
    Tolerate long source lineslongtrue if a line can contain more than 80 characters.
    Assume Node.jsnodetrue if Node.js globals should be predefined. It + will predefine globals that are used in the Node.js environment. + It adds the same globals as this directive: +
    /*global +
    + Buffer, clearImmediate, clearInterval, clearTimeout, console, exports, + module, process, require, setImmediate, setInterval, setTimeout, URL, + URLSearchParams, __dirname, __filename +
    + */
    Tolerate single quote stringssingletrue if 'single quote + should be allowed to enclose string literals.
    Tolerate this
    thistrue if this should be allowed.
    Tolerate whitespace messwhitetrue if the whitespace rules should be ignored.
    +

    For example:

    +
    /*jslint
    +    bitwise, node
    +*/
    + +

    /*property*/

    +

    The /*property*/ directive is used to declare a list of + property identifiers that are used in the file. Each property name in the + program is looked up in this list. If a name is not found, that indicates an + error, most likely a typing error.

    +

    The list can also be used to evade some of JSLint’s naming + rules.

    +

    JSLint can build the /*property*/ list for you. At + the bottom of its report, JSLint displays a + /*property*/ directive. It contains all of the names that were + used with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. You + can copy the /*property*/ directive to the top of your + script file. JSLint will check the spelling of all property + names against the list. That way, you can have JSLint look for + misspellings for you.

    +

    For example,

    +
    /*property +
    charAt, slice, _$_
    + */
    +

    ignore

    +

    JSLint introduces a new reserved word: ignore. It + is used in parameter lists and in catch clauses to indicate a + parameter that will be ignored. Unused warnings will not be produced for + ignore. +

    function handler(ignore, value) {
    +    return do_something_useful(value);
    +}
    +

    var let const

    +

    JavaScript provides three statements for declaring variables: + var, let, and const. The + var statement suffers from a bad practice called hoisting. The + let statement does not do hoisting and respects block scope. + The const statement is like let except that it + marks the variable (but not its contents) as read only, making it an error + to attempt to assign to the variable. When given a choice, + const is the best, var is the worst.

    +

    JSLint uses the intersection of the var rules and the + let rules, and by doing so avoids the errors related to either. + A name should be declared only once in a function. It should be declared + before it is used. It should not be used outside of the block in which it is + declared. A variable should not have the same name as a variable or + parameter in an outer function. Do not mix var and + let. Declare one name per statement.

    +

    = == ===

    +

    Fortran made a terrible + mistake in using the equality operator as its assignment operator. That + mistake has been replicated in most languages since then. C compounded that + mistake by making == its equality operator. The visual + similarity is a source of errors. JavaScript compounded this further by + making == a type coercing comparison operator that produces + false positive results. This was mitigated by adding the === + operator, leaving the broken == operator in place.

    +

    JSLint attempts to minimize errors by the following rules:

    +

    == is not allowed. This avoids the false positives, and + increases the visual distance between = and ===. + Assignments are not allowed in expression position, and comparisons are not + allowed in statement position. This also reduces confusion. 

    +

    Semicolon

    +

    JavaScript uses a C-like syntax that requires the use of semicolons to + delimit certain statements. JavaScript attempts to make those semicolons + optional with an automatic semicolon insertion mechanism, but it does not + work very well. Automatic semicolon insertion was added to make + things easier for beginners. Unfortunately, it sometimes fails. Do not rely + on it unless you are a beginner.

    +

    JSLint expects that every statement will be followed by + ; except for for, function, + if, switch, try, and + while. JSLint does not expect to see unnecessary + semicolons, the empty statement, or empty blocks.

    +

    function =>

    +

    JavaScript has four syntactic forms for making function objects: function + statements, function expressions, enhanced object literals, and the + => fart operator.

    +

    The function statement creates a variable and assigns the function object to + it. It should be used in a file or function body, but not inside of a block.

    +
    function name(parameters) { +
    statements
    + }
    +

    The function expression unfortunately looks like the function statement. It + may appear anywhere that an expression may appear, but not in statement + position and not in a loop. It produces a function object but does not + create a variable in which to store it.

    +
    function (parameters) { +
    statements
    + }
    +

    The enhanced object literal provides an ES6 shorthand for creating a property + whose value is a function, saving you from having to type + : colon and function.

    +
    { +
    name(parameters) { +
    statements
    + }
    + }
    +

    Finally, ES6 provides an even shorter form of function expression that leaves + out the words function and return:

    +
    (parameters) => + expression
    +

    JSLint requires the parens around the parameters, and forbids a + { left brace after the + => fart to avoid syntactic ambiguity.

    +

    Comma

    +

    The , comma operator is unnecessary and can + mask programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as + an operator. It does not expect to see elided elements in array literals. A + comma should not appear after the last element of an array literal or object + literal.

    +

    Blocks

    +

    JSLint expects blocks with function, + if, switch, while, for, + do, and try statements and nowhere else.

    +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    +

    JavaScript allows an if to be written like this:

    +
    if (condition)
    +    statement;
    +

    That form is known to contribute to mistakes in projects where many + programmers are working on the same code. That is why JSLint + expects the use of a block:

    +
    if (condition) {
    +    statements;
    +}
    +

    Experience shows that this form is more resilient.

    +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call. All other expression statements are considered + to be errors.

    +

    for

    +

    JSLint does not recommend use of the for statement. + Use array methods like forEach instead. The for + option will suppress some warnings. The forms of for that + JSLint accepts are restricted, excluding the new ES6 forms.

    +

    for in

    +

    JSLint does not recommend use of the for + in statement. Use Object.keys instead.

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, it also + loops through all of the properties that were inherited through the + prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written + without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    for (name in object) {
    +    if (object.hasOwnProperty(name)) {
    +        ....
    +    }
    +}
    +

    Note that the above code will fail if the object contains a property + named hasOwnProperty. Use Object.keys instead.

    +

    switch

    +

    A common error in switch statements is to forget to place a + break statement after each case, resulting in unintended + fall-through. JSLint expects that the statement before the next + case or default is one of these: + break, return, or throw.

    +

    with

    +

    The with statement was intended to provide a shorthand in + accessing properties in deeply nested objects. Unfortunately, it behaves + very badly when setting new properties. Never use the with + statement.

    +

    JSLint does not expect to see a with statement.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that + labels will be distinct from vars and parameters.

    +

    Unreachable Code

    +

    JSLint expects that a return, break, + or throw statement will be followed by a + } right brace or case or + default.

    +

    Confusing Pluses and Minuses

    +

    JSLint expects that + will not be followed by + + or ++, and that - will not be + followed by - or --. A misplaced space can turn + + + into ++, an error that is difficult to see. + Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ increment and + -- decrement operators have been known to + contribute to bad code by encouraging excessive trickiness. They are second + only to faulty architecture in enabling to viruses and other security + menaces. Also, preincrement/postincrement confusion can produce off-by-one + errors that are extremely difficult to diagnose. Fortunately, they are also + complete unnecessary. There are better ways to add 1 to a variable.

    +

    It is best to avoid these operators entirely and rely on += and + -= instead.

    +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. + JSLint looks for problems that may cause portability problems. + It also attempts to resolve visual ambiguities by recommending explicit + escapement.

    +

    JavaScript’s syntax for regular expression literals overloads the + / slash character. To avoid ambiguity, + JSLint expects that the character preceding a regular + expression literal is a ( left paren or + = equal or + : colon or + , comma character.

    +

    this

    +
    Having this in the language makes it harder to talk + about the language. It is like pair programming with Abbott and Costello. +
    +

    Avoid using this. Warnings about this can be + suppressed with option.this.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the + new prefix. The new prefix creates a new object + based on the function's prototype, and binds that object to the + function's implied this parameter. If you neglect to use the + new prefix, no new object will be made and this + will be bound to the global object.

    +

    JSLint enforces the convention that constructor functions be + given names with initial uppercase. JSLint does not expect to + see a function invocation with an initial uppercase name unless it has the + new prefix. JSLint does not expect to see the + new prefix used with functions whose names do not start with + initial uppercase.

    +

    JSLint does not expect to see the wrapper forms + new Number, new String, new Boolean.

    +

    JSLint does not expect to see new Object. + Use Object.create(null) instead.

    +

    Whitespace

    +

    JSLint has a specific set of rules about the use of whitespace. + Where possible, these rules are consistent with centuries of good practice + with literary style.

    +

    The indentation increases by 4 spaces when the last token on a line is + { left brace, + [ left bracket, + ( left paren. The matching closing + token will be the first token on a line, restoring the previous + indentation.

    +

    The ternary operator can be visually confusing, so + ? question mark and + : colon always begin a line and increase + the indentation by 4 spaces.

    +
    return (
    +    (the_token.id === "(string)" || the_token.id === "(number)")
    +    ? String(the_token.value)
    +    : the_token.id
    +);
    +

    The word function is always followed with one space.

    +

    Clauses (case, catch, default, + else, finally) are not statements and so should + not be indented like statements.

    +

    Spaces are used to make things that are not invocations look less like + invocations.

    +

    Tabs and spaces should not be mixed. We should pick just one in order to + avoid the problems that come from having both. Personal preference is an + extremely unreliable criterion. Neither offers a powerful advantage over the + other. Fifty years ago, tab had the advantage of consuming less memory, but + Moore's Law has eliminated that advantage. Space has one clear advantage + over tab: there is no reliable standard for how many spaces a tab + represents, but it is universally accepted that a space occupies a space. So + use spaces. You can edit with tabs if you must, but make sure it is spaces + again before you commit. Maybe someday we will finally get a universal + standard for tabs, but until that day comes, the better choice is spaces.

    +

    Report

    +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    +
      +
    • The line number on which it starts.
    • +
    • The function’s name. In the case of anonymous functions, + JSLint will attempt to + «guess» the name.
    • +
    • The parameters.
    • +
    • Variables: The variables that are declared in the function.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Exceptions: The variables that are declared by catch + clauses of try statements.
    • +
    • Outer: Variables used by this function that are declared in + other functions.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements.

    +

    Try it

    +

    Try it. Paste your script + into the window and click the + + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    JSLint is written entirely in JavaScript, so it can run + anywhere that JavaScript (or even Java) can run.

    +

    Perfectly fine

    +

    JSLint was designed to reject code that some would consider to + be perfectly fine. The reason for this is that JSLint's + purpose is to help produce programs that are free of error. That is + difficult in any language and is especially hard in JavaScript. + JSLint attempts to help you increase the visual distance + between correct programs and incorrect programs, making the remaining errors + more obvious. JSLint will give warnings about things that are + not necessarily wrong in the current situation, but which have been observed + to mask or obscure errors. Avoid those things when there are better options + available.

    +

    It is dangerous out there. JSLint is here to help.

    +

    Warning

    +

    JSLint will hurt your feelings. Side effects may include + headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, + dry mouth, cleaner code, and a reduced error rate.

    +

    Further Reading

    + +
    +

    + Code Conventions for the JavaScript Programming Language.

    + + + +
    + + diff --git a/branch.ci/index.html b/branch.ci/index.html new file mode 100644 index 000000000..822acaad6 --- /dev/null +++ b/branch.ci/index.html @@ -0,0 +1,108 @@ + + + + + + + + + +JSLint: The JavaScript Code Quality Tool + + +
    +
    Source
    +
    + + +
    +
    + Warnings +
    +
    +
    + Function Report +
    +
    +
    + Property Directive + +
    +
    + + + + +
    +
    Options +
    Assume... +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Global variables... + +
    +
    +
    + + diff --git a/branch.ci/jslint.css b/branch.ci/jslint.css new file mode 100644 index 000000000..cc3bf1696 --- /dev/null +++ b/branch.ci/jslint.css @@ -0,0 +1,341 @@ +@font-face { + font-family: 'Programma'; + font-weight: bold; + src: url('Programma-Bold.woff2') format('woff2'); +} +@font-face { + font-family: 'Daley'; + font-weight: bold; + src: url('Daley-Bold.woff2') format('woff2'); +} + +body { + background-color: antiquewhite; + color: black; + font-family: 'Daley', sans-serif; + margin: 0; + padding: 0; +} + +#JSLINT_TITLEBOX { + font-family: 'Daley', sans-serif; + margin: 0; + margin-left: 3%; + margin-right: 3%; + padding: 0; +} + +#JSLINT_TITLEBOX ul { + float: right; + font-size: 12pt; +} + +#JSLINT_TITLEBOX div { + color: darkslategray; + float: left; + font-size: 48pt; +} + +#JSLINT_ fieldset { + background-color: gainsboro; + border: 0; + clear: both; + margin-bottom: 1em; + margin-left: 3%; + margin-right: 3%; + margin-top: 1em; + padding: 0; + width: auto; +} +#JSLINT_ legend { + background-color: darkslategray; + border: 0; + color: white; + font-size: 100%; + font-style: normal; + font-weight: normal; + margin: 0; + padding-bottom: 0.25em; + padding-left: 0; + padding-right: 0; + padding-top: 0.25em; + text-align: center; + width: 100%; +} +#JSLINT_ button { + background-color: darkslategray; + border: 0; + color: white; + cursor: pointer; + font-family: 'Daley', sans-serif; + font-size: 100%; + font-style: normal; + margin-left: 1em; + margin-right: 1em; + padding-left: 1em; + padding-right: 1em; + padding-bottom: 0.25em; + padding-top: 0.25em; + text-align: center; +} +#JSLINT_ button:hover { + background-color: slategray; + color: white; + border: 0; +} + +#JSLINT_ button:active { + border: 0; + color: black; +} + +#JSLINT_ button:disabled { + background-color: gray; + border: 0; + color: gainsboro; +} + +a:visited { + color: #69565c; +} + +a:link { + color: black; +} + +#JSLINT_ input[type="text"] { + background-color: white; + border: 0; + color: black; + margin-bottom: 0.2em; + margin-right: 0.5em; + padding-left: 0.5em; + padding-right: 0.5em; + text-align: right; + width: 3em; +} + +#JSLINT_ textarea { + border: 0; + background-color: white; + border: 0; + font-family: Programma, monospace; + font-size: 100%; + font-weight: bold; + resize: none; +} + +#JSLINT_ textarea::selection { + background-color: yellow; + color: black; +} + +#JSLINT_NUMBER { + color: darkgray; + display: inline-block; + height: 4in; + margin: 2%; + margin-right: 0; + overflow: hidden; + padding-right: 0.5%; + text-align: right; + white-space: pre; + width: 4%; +} + +#JSLINT_SOURCE { + color: black; + display: inline-block; + font-size: 100%; + height: 4in; + margin: 2%; + margin-left: 0; + margin-right: 0; + overflow: auto; + padding-left: 0.5%; + white-space: pre; + width: 91%; +} + +#JSLINT_PROPERTY { + background-color: honeydew; + border: 0; + margin: 2%; + padding: 0.5%; + white-space: pre; + width: 95%; +} + +#JSLINT_GLOBAL { + white-space: normal; + width: 95%; +} +#JSLINT_ label { + font-family: sans-serif; + font-size: 90%; + padding-left: 0.25em; +} +#JSLINT_OPTIONS>div { + float: left; + margin: 0.5em; +} + +#JSLINT_ address { + color: black; + display: block; + float: right; + font-family: serif; + font-size: 90%; + margin-left: 1em; +} +#JSLINT_ dl { + background-color: cornsilk; + color: white; + margin: 0; + padding-bottom: 2pt; + padding-left: 1em; + padding-right: 1em; + padding-top: 2pt; +} +#JSLINT_ dfn { + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + margin-bottom: 2pt; +} +#JSLINT_ dt { + color: black; + display: block; + float: left; + font-family: serif; + font-size: 75%; + font-style: italic; + margin: 0; + width: 8em; + text-align: right; +} +#JSLINT_ dd { + color: black; + display: block; + font-family: Programma, monospace; + font-weight: bold; + margin-left: 8em; + padding-bottom: 2pt; +} +#JSLINT_WARNINGS>legend { + background-color: indianred; +} +#JSLINT_WARNINGS>div { + background-color: pink; + padding: 1em; +} +#JSLINT_WARNINGS cite { + color: black; + display: block; + font-family: serif; + font-size: 100%; + font-style: normal; + margin-bottom: 4pt; + margin-left: 20pt; + margin-right: 20pt; + margin-top: 4pt; + overflow-x: hidden; +} +#JSLINT_WARNINGS samp { + background-color: lavenderblush; + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + padding: 4pt; + margin-bottom: 0; + margin-left: 16pt; + margin-right: 16pt; + margin-top: 0; + white-space: pre-wrap; +} + +#JSLINT_REPORT center { + margin-top: 1em; +} + +#JSLINT_WARNINGS dl address { + color: black; + display: inline; + float: none; + font-size: 80%; + margin: 0; +} + +#JSLINT_REPORT>div { + padding: 1em; +} + +#JSLINT_ dl.level0 { + color: black; + background-color: white; +} + +#JSLINT_ dl.level1 { + color: black; + background-color: #ffffe0; /* yellow */ + margin-left: 1em; +} + +#JSLINT_ dl.level2 { + color: black; + background-color: #e0ffe0; /* green */ + margin-left: 2em; +} + +#JSLINT_ dl.level3 { + color: black; + background-color: #D0D0ff; /* blue */ + margin-left: 3em; +} + +#JSLINT_ dl.level4 { + background-color: #ffe0ff; /* purple */ + color: black; + margin-left: 4em; +} + +#JSLINT_ dl.level5 { + background-color: #ffe0e0; /* red */ + color: black; + margin-left: 5em; +} + +#JSLINT_ dl.level6 { + background-color: #ffe390; /* orange */ + color: black; + margin-left: 6em; +} + +#JSLINT_ dl.level7 { + background-color: #e0e0e0; /* gray */ + color: black; + margin-left: 7em; +} + +#JSLINT_ dl.level8 { + color: black; + margin-left: 8em; +} + +#JSLINT_ dl.level9 { + color: black; + margin-left: 9em; +} + +.none { + display: none; +} +.center { + text-align: center; +} diff --git a/branch.ci/jslint.js b/branch.ci/jslint.js new file mode 100644 index 000000000..4f365ef07 --- /dev/null +++ b/branch.ci/jslint.js @@ -0,0 +1,5018 @@ +// jslint.js +// 2020-11-06 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// jslint(source, option_object, global_array) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze, a string or an array of strings. +// option_object An object whose keys correspond to option names. +// global_array An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all of the functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// 1. If the source is a single string, split it into an array of strings. +// 2. Turn the source into an array of tokens. +// 3. Furcate the tokens into a parse tree. +// 4. Walk the tree, traversing all of the nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// 5. Check the whitespace between the tokens. + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*property + a, and, arity, assign, b, bad_assignment_a, bad_directive_a, bad_get, + bad_module_name_a, bad_option_a, bad_property_a, bad_set, bitwise, block, + body, browser, c, calls, catch, charCodeAt, closer, closure, code, column, + concat, constant, context, convert, couch, create, d, dead, default, devel, + directive, directives, disrupt, dot, duplicate_a, edition, ellipsis, else, + empty_block, eval, every, expected_a, expected_a_at_b_c, + expected_a_b, expected_a_b_from_c_d, expected_a_before_b, + expected_a_next_at_b, expected_digits_after_a, expected_four_digits, + expected_identifier_a, expected_line_break_a_b, expected_regexp_factor_a, + expected_space_a_b, expected_statements_a, expected_string_a, + expected_type_string_a, exports, expression, extra, finally, flag, for, + forEach, free, freeze, freeze_exports, from, froms, fud, fudge, + function_in_loop, functions, g, getset, global, i, id, identifier, import, + inc, indexOf, infix_in, init, initial, isArray, isNaN, join, json, keys, + label, label_a, lbp, led, length, level, line, lines, live, long, loop, m, + margin, match, message, misplaced_a, misplaced_directive_a, missing_browser, + missing_m, module, naked_block, name, names, nested_comment, new, node, + not_label_a, nr, nud, number_isNaN, ok, open, opening, option, + out_of_scope_a, parameters, parent, pop, property, push, quote, raw, + redefinition_a_b, replace, required_a_optional_b, reserved_a, role, search, + shebang, signature, single, slice, some, sort, split, startsWith, statement, + stop, subscript_a, switch, test, this, thru, toString, todo_comment, + tokens, too_long, too_many_digits, tree, try, type, u, unclosed_comment, + unclosed_mega, unclosed_string, undeclared_a, unexpected_a, + unexpected_a_after_b, unexpected_a_before_b, unexpected_at_top_level_a, + unexpected_char_a, unexpected_comment, unexpected_directive_a, + unexpected_expression_a, unexpected_label_a, unexpected_parens, + unexpected_space_a_b, unexpected_statement_a, unexpected_trailing_space, + unexpected_typeof_a, uninitialized_a, unreachable_a, + unregistered_property_a, unused_a, use_double, use_open, use_spaces, + used, value, var_loop, var_switch, variable, warning, warnings, + weird_condition_a, weird_expression_a, weird_loop, weird_relation_a, white, + wrap_condition, wrap_immediate, wrap_parameter, wrap_regexp, wrap_unary, + wrapped, writable, y +*/ + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than '{}' because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +function populate(array, object = empty(), value = true) { + +// Augment an object by taking property names from an array of strings. + + array.forEach(function (name) { + object[name] = value; + }); + return object; +} + +const allowed_option = { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + bitwise: true, + browser: [ + "caches", "CharacterData", "clearInterval", "clearTimeout", "document", + "DocumentType", "DOMException", "Element", "Event", "event", "fetch", + "FileReader", "FontFace", "FormData", "history", "IntersectionObserver", + "localStorage", "location", "MutationObserver", "name", "navigator", + "screen", "sessionStorage", "setInterval", "setTimeout", "Storage", + "TextDecoder", "TextEncoder", "URL", "window", "Worker", + "XMLHttpRequest" + ], + couch: [ + "emit", "getRow", "isArray", "log", "provides", "registerType", + "require", "send", "start", "sum", "toJSON" + ], + convert: true, + devel: [ + "alert", "confirm", "console", "prompt" + ], + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + node: [ + "Buffer", "clearImmediate", "clearInterval", "clearTimeout", + "console", "exports", "module", "process", "require", + "setImmediate", "setInterval", "setTimeout", "TextDecoder", + "TextEncoder", "URL", "URLSearchParams", "__dirname", "__filename" + ], + single: true, + this: true, + white: true +}; + +const anticondition = populate([ + "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%", + "typeof", "(number)", "(string)" +]); + +// These are the bitwise operators. + +const bitwiseop = populate([ + "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=", + ">>>", ">>>=" +]); + +const escapeable = populate([ + "\\", "/", "`", "b", "f", "n", "r", "t" +]); + +const opener = { + +// The open and close pairs. + + "(": ")", // paren + "[": "]", // bracket + "{": "}", // brace + "${": "}" // mega +}; + +// The relational operators. + +const relationop = populate([ + "!=", "!==", "==", "===", "<", "<=", ">", ">=" +]); + +// This is the set of infix operators that require a space on each side. + +const spaceop = populate([ + "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/", + "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=", + ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" +]); + +const standard = [ + +// These are the globals that are provided by the language standard. + + "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI", + "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error", + "EvalError", "Float32Array", "Float64Array", "Generator", + "GeneratorFunction", "Int8Array", "Int16Array", "Int32Array", "Intl", + "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat", + "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", + "Set", "String", "Symbol", "SyntaxError", "System", "TypeError", + "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", + "URIError", "WeakMap", "WeakSet" +]; + +const bundle = { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + and: "The '&&' subexpression should be wrapped in parens.", + bad_assignment_a: "Bad assignment to '{a}'.", + bad_directive_a: "Bad directive '{a}'.", + bad_get: "A get function takes no parameters.", + bad_module_name_a: "Bad module name '{a}'.", + bad_option_a: "Bad option '{a}'.", + bad_property_a: "Bad property name '{a}'.", + bad_set: "A set function takes one parameter.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + expected_a: "Expected '{a}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: ( + "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'." + ), + expected_a_before_b: "Expected '{a}' before '{b}'.", + expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.", + expected_digits_after_a: "Expected digits after '{a}'.", + expected_four_digits: "Expected four digits after '\\u'.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.", + expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.", + expected_space_a_b: "Expected one space between '{a}' and '{b}'.", + expected_statements_a: "Expected statements before '{a}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + expected_type_string_a: "Expected a type string and instead saw '{a}'.", + freeze_exports: ( + "Expected 'Object.freeze('. All export values should be frozen." + ), + function_in_loop: "Don't make functions within a loop.", + infix_in: ( + "Unexpected 'in'. Compare with undefined, " + + "or use the hasOwnProperty method instead." + ), + label_a: "'{a}' is a statement label.", + misplaced_a: "Place '{a}' at the outermost level.", + misplaced_directive_a: ( + "Place the '/*{a}*/' directive before the first statement." + ), + missing_browser: "/*global*/ requires the Assume a browser option.", + missing_m: "Expected 'm' flag on a multiline regular expression.", + naked_block: "Naked block.", + nested_comment: "Nested comment.", + not_label_a: "'{a}' is not a label.", + number_isNaN: "Use Number.isNaN function to compare with NaN.", + out_of_scope_a: "'{a}' is out of scope.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + required_a_optional_b: ( + "Required parameter '{a}' after optional parameter '{b}'." + ), + reserved_a: "Reserved name '{a}'.", + subscript_a: "['{a}'] is better written in dot notation.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line is longer than 80 characters.", + too_many_digits: "Too many digits.", + unclosed_comment: "Unclosed comment.", + unclosed_mega: "Unclosed mega literal.", + unclosed_string: "Unclosed string.", + undeclared_a: "Undeclared '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_a_after_b: "Unexpected '{a}' after '{b}'.", + unexpected_a_before_b: "Unexpected '{a}' before '{b}'.", + unexpected_at_top_level_a: "Expected '{a}' to be in a function.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_directive_a: "When using modules, don't use directive '/*{a}'.", + unexpected_expression_a: ( + "Unexpected expression '{a}' in statement position." + ), + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_parens: "Don't wrap function literals in parens.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_statement_a: ( + "Unexpected statement '{a}' in expression position." + ), + unexpected_trailing_space: "Unexpected trailing space.", + unexpected_typeof_a: ( + "Unexpected 'typeof'. Use '===' to compare directly with {a}." + ), + uninitialized_a: "Uninitialized '{a}'.", + unreachable_a: "Unreachable '{a}'.", + unregistered_property_a: "Unregistered property name '{a}'.", + unused_a: "Unused '{a}'.", + use_double: "Use double quotes, not single quotes.", + use_open: ( + "Wrap a ternary expression in parens, " + + "with a line break after the left paren." + ), + use_spaces: "Use spaces, not tabs.", + var_loop: "Don't declare variables in a loop.", + var_switch: "Don't declare variables in a switch.", + weird_condition_a: "Weird condition '{a}'.", + weird_expression_a: "Weird expression '{a}'.", + weird_loop: "Weird loop.", + weird_relation_a: "Weird relation '{a}'.", + wrap_condition: "Wrap the condition in parens.", + wrap_immediate: ( + "Wrap an immediate function invocation in parentheses to assist " + + "the reader in understanding that the expression is the result " + + "of a function, and not the function itself." + ), + wrap_parameter: "Wrap the parameter in parens.", + wrap_regexp: "Wrap this regexp in parens to avoid confusion.", + wrap_unary: "Wrap the unary expression in parens." +}; + +// Regular expression literals: + +function tag_regexp(strings) { + return new RegExp(strings.raw[0].replace(/\s/g, "")); +} + +// supplant {variables} +const rx_supplant = /\{([^{}]*)\}/g; +// carriage return, carriage return linefeed, or linefeed +const rx_crlf = tag_regexp ` + \n + | \r \n? +`; +// identifier +const rx_identifier = tag_regexp ` ^( + [ a-z A-Z _ $ ] + [ a-z A-Z 0-9 _ $ ]* +)$`; +const rx_module = tag_regexp ` ^ [ a-z A-Z 0-9 _ $ : . @ \- \/ ]+ $ `; +const rx_bad_property = tag_regexp ` + ^_ + | \$ + | Sync $ + | _ $ +`; +// star slash +const rx_star_slash = tag_regexp ` \* \/ `; +// slash star +const rx_slash_star = tag_regexp ` \/ \* `; +// slash star or ending slash +const rx_slash_star_or_slash = tag_regexp ` \/ \* | \/ $ `; +// uncompleted work comment +const rx_todo = tag_regexp ` \b (?: + todo + | TO \s? DO + | HACK +) \b `; +// tab +const rx_tab = /\t/g; +// directive +const rx_directive = tag_regexp ` ^ ( + jslint + | property + | global +) \s+ ( .* ) $ `; +const rx_directive_part = tag_regexp ` ^ ( + [ a-z A-Z $ _ ] [ a-z A-Z 0-9 $ _ ]* +) (?: + : \s* ( true | false ) +)? ,? \s* ( .* ) $ `; +// token +const rx_token = tag_regexp ` ^ ( + (\s+) + | ( + [ a-z A-Z _ $ ] + [ a-z A-Z 0-9 _ $ ]* + ) + | [ + ( ) { } \[ \] , : ; ' " ~ \` + ] + | \? [ ? . ]? + | = (?: + = =? + | > + )? + | \.+ + | \* [ * \/ = ]? + | \/ [ * \/ ]? + | \+ [ = + ]? + | - [ = \- ]? + | [ \^ % ] =? + | & [ & = ]? + | \| [ | = ]? + | >{1,3} =? + | < = "a" && string <= "z\uffff") + || (string >= "A" && string <= "Z\uffff") + ); +} + +function supplant(string, object) { + return string.replace(rx_supplant, function (found, filling) { + const replacement = object[filling]; + return ( + replacement !== undefined + ? replacement + : found + ); + }); +} + +let anon; // The guessed name for anonymous functions. +let blockage; // The current block. +let block_stack; // The stack of blocks. +let declared_globals; // The object containing the global declarations. +let directives; // The directive comments. +let directive_mode; // true if directives are still allowed. +let early_stop; // true if JSLint cannot finish. +let exports; // The exported names and values. +let froms; // The array collecting all import-from strings. +let fudge; // true if the natural numbers start with 1. +let functionage; // The current function. +let functions; // The array containing all of the functions. +let global; // The global object; the outermost context. +let json_mode; // true if parsing JSON. +let lines; // The array containing source lines. +let mega_mode; // true if currently parsing a megastring literal. +let module_mode; // true if import or export was used. +let next_token; // The next token to be examined in the parse. +let option; // The options parameter. +let property; // The object containing the tallied property names. +let shebang; // true if a #! was seen on the first line. +let stack; // The stack of functions. +let syntax; // The object containing the parser. +let token; // The current token being examined in the parse. +let token_nr; // The number of the next token. +let tokens; // The array of tokens. +let tenure; // The predefined property registry. +let tree; // The abstract parse tree. +let var_mode; // "var" if using var; "let" if using let. +let warnings; // The array collecting all generated warnings. + +// Error reportage functions: + +function artifact(the_token) { + +// Return a string representing an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); +} + +function artifact_line(the_token) { + +// Return the fudged line number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.line + fudge; +} + +function artifact_column(the_token) { + +// Return the fudged column number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.from + fudge; +} + +function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + const warning = { // ~~ + name: "JSLintError", + column, + line, + code + }; + if (a !== undefined) { + warning.a = a; + } + if (b !== undefined) { + warning.b = b; + } + if (c !== undefined) { + warning.c = c; + } + if (d !== undefined) { + warning.d = d; + } + warning.message = supplant(bundle[code] || code, warning); + warnings.push(warning); + return warning; +} + +function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); +} + +function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + if (the_token.warning === undefined) { + the_token.warning = warn_at( + code, + the_token.line, + the_token.from, + a || artifact(the_token), + b, + c, + d + ); + return the_token.warning; + } +} + +function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); +} + +// Tokenize: + +function tokenize(source) { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + +// If the source is not an array, then it is split into lines at the +// carriage return/linefeed. + + lines = ( + Array.isArray(source) + ? source + : source.split(rx_crlf) + ); + tokens = []; + + let char; // a popular character + let column = 0; // the column number of the next character + let first; // the first token + let from; // the starting column number of the token + let line = -1; // the line number of the next character + let nr = 0; // the next token number + let previous = global; // the previous token including comments + let prior = global; // the previous token excluding comments + let mega_from; // the starting column of megastring + let mega_line; // the starting line of megastring + let regexp_seen; // regular expression literal seen on this line + let snippet; // a piece of string + let source_line = ""; // the remaining line source string + let whole_line = ""; // the whole line source string + + if (lines[0].startsWith("#!")) { + line = 0; + shebang = true; + } + + function next_line() { + +// Put the next line of source in source_line. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + let at; + if ( + !option.long + && whole_line.length > 80 + && !json_mode + && first + && !regexp_seen + ) { + warn_at("too_long", line, 80); + } + column = 0; + line += 1; + regexp_seen = false; + source_line = lines[line]; + whole_line = source_line || ""; + if (source_line !== undefined) { + at = source_line.search(rx_tab); + if (at >= 0) { + if (!option.white) { + warn_at("use_spaces", line, at + 1); + } + source_line = source_line.replace(rx_tab, " "); + } + if (!option.white && source_line.slice(-1) === " ") { + warn_at( + "unexpected_trailing_space", + line, + source_line.length - 1 + ); + } + } + return source_line; + } + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions snip, next_char, back_char, +// some_digits, and escape help in the parsing of literals. + + function snip() { + +// Remove the last character from snippet. + + snippet = snippet.slice(0, -1); + } + + function next_char(match) { + +// Get the next character from the source line. Remove it from the source_line, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + return stop_at( + ( + char === "" + ? "expected_a" + : "expected_a_b" + ), + line, + column - 1, + match, + char + ); + } + if (source_line) { + char = source_line[0]; + source_line = source_line.slice(1); + snippet += char; + } else { + char = ""; + snippet += " "; + } + column += 1; + return char; + } + + function back_char() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the source_line. + + if (snippet) { + char = snippet.slice(-1); + source_line = char + source_line; + column -= 1; + snip(); + } else { + char = ""; + } + return char; + } + + function some_digits(rx, quiet) { + const digits = source_line.match(rx)[0]; + const length = digits.length; + if (!quiet && length === 0) { + warn_at("expected_digits_after_a", line, column, snippet); + } + column += length; + source_line = source_line.slice(length); + snippet += digits; + next_char(); + return length; + } + + function escape(extra) { + next_char("\\"); + if (escapeable[char] === true) { + return next_char(); + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "u") { + if (next_char("u") === "{") { + if (json_mode) { + warn_at("unexpected_a", line, column - 1, char); + } + if (some_digits(rx_hexs) > 5) { + warn_at("too_many_digits", line, column - 1); + } + if (next_char() !== "}") { + stop_at("expected_a_before_b", line, column, "}", char); + } + return next_char(); + } + back_char(); + if (some_digits(rx_hexs, true) < 4) { + warn_at("expected_four_digits", line, column - 1); + } + return; + } + if (extra && extra.indexOf(char) >= 0) { + return next_char(); + } + warn_at("unexpected_a_before_b", line, column - 2, "\\", char); + } + + function make(id, value, identifier) { + +// Make the token object and append it to the tokens list. + + const the_token = { + from, + id, + identifier: Boolean(identifier), + line, + nr, + thru: column + }; + tokens[nr] = the_token; + nr += 1; + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + directive_mode = false; + } + +// If the token is to have a value, give it one. + + if (value !== undefined) { + the_token.value = value; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option.white. + + if ( + previous.line === line + && previous.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (previous.id === "(comment)" || previous.id === "(regexp)") + ) { + warn( + "expected_space_a_b", + the_token, + artifact(previous), + artifact(the_token) + ); + } + if (previous.id === "." && id === "(number)") { + warn("expected_a_before_b", previous, "0", "."); + } + if (prior.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// The previous token is used to detect adjacency problems. + + previous = the_token; + +// The prior token is a previous token that was not a comment. The prior token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (previous.id !== "(comment)") { + prior = previous; + } + return the_token; + } + + function parse_directive(the_comment, body) { + +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + + const result = body.match(rx_directive_part); + if (result) { + let allowed; + const name = result[1]; + const value = result[2]; + if (the_comment.directive === "jslint") { + allowed = allowed_option[name]; + if ( + typeof allowed === "boolean" + || typeof allowed === "object" + ) { + if ( + value === "" + || value === "true" + || value === undefined + ) { + option[name] = true; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } else if (value === "false") { + option[name] = false; + } else { + warn("bad_option_a", the_comment, name + ":" + value); + } + } else { + warn("bad_option_a", the_comment, name); + } + } else if (the_comment.directive === "property") { + if (tenure === undefined) { + tenure = empty(); + } + tenure[name] = true; + } else if (the_comment.directive === "global") { + if (value) { + warn("bad_option_a", the_comment, name + ":" + value); + } + declared_globals[name] = false; + module_mode = the_comment; + } + return parse_directive(the_comment, result[3]); + } + if (body) { + return stop("bad_directive_a", the_comment, body); + } + } + + function comment(snippet) { + +// Make a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + + const the_comment = make("(comment)", snippet); + if (Array.isArray(snippet)) { + snippet = snippet.join(" "); + } + if (!option.devel && rx_todo.test(snippet)) { + warn("todo_comment", the_comment); + } + const result = snippet.match(rx_directive); + if (result) { + if (!directive_mode) { + warn_at("misplaced_directive_a", line, from, result[1]); + } else { + the_comment.directive = result[1]; + parse_directive(the_comment, result[2]); + } + directives.push(the_comment); + } + return the_comment; + } + + function regexp() { + +// Parse a regular expression literal. + + let multi_mode = false; + let result; + let value; + regexp_seen = true; + + function quantifier() { + +// Match an optional quantifier. + + if (char === "?" || char === "*" || char === "+") { + next_char(); + } else if (char === "{") { + if (some_digits(rx_digits, true) === 0) { + warn_at("expected_a_before_b", line, column, "0", ","); + } + if (char === ",") { + some_digits(rx_digits, true); + } + next_char("}"); + } else { + return; + } + if (char === "?") { + next_char("?"); + } + } + + function subklass() { + +// Match a character in a character class. + + if (char === "\\") { + escape("BbDdSsWw-[]^"); + return true; + } + if ( + char === "" + || char === "[" + || char === "]" + || char === "/" + || char === "^" + || char === "-" + ) { + return false; + } + if (char === " ") { + warn_at("expected_a_b", line, column, "\\u0020", " "); + } else if (char === "`" && mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char(); + return true; + } + + function ranges() { + +// Match a range of subclasses. + + if (subklass()) { + if (char === "-") { + next_char("-"); + if (!subklass()) { + return stop_at( + "unexpected_a", + line, + column - 1, + "-" + ); + } + } + return ranges(); + } + } + + function klass() { + +// Match a class. + + next_char("["); + if (char === "^") { + next_char("^"); + } + (function classy() { + ranges(); + if (char !== "]" && char !== "") { + warn_at( + "expected_a_before_b", + line, + column, + "\\", + char + ); + next_char(); + return classy(); + } + }()); + next_char("]"); + } + + function choice() { + + function group() { + +// Match a group that starts with left paren. + + next_char("("); + if (char === "?") { + next_char("?"); + if (char === "=" || char === "!") { + next_char(); + } else { + next_char(":"); + } + } else if (char === ":") { + warn_at("expected_a_before_b", line, column, "?", ":"); + } + choice(); + next_char(")"); + } + + function factor() { + if ( + char === "" + || char === "/" + || char === "]" + || char === ")" + ) { + return false; + } + if (char === "(") { + group(); + return true; + } + if (char === "[") { + klass(); + return true; + } + if (char === "\\") { + escape("BbDdSsWw^${}[]():=!.|*+?"); + return true; + } + if ( + char === "?" + || char === "+" + || char === "*" + || char === "}" + || char === "{" + ) { + warn_at( + "expected_a_before_b", + line, + column - 1, + "\\", + char + ); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column - 1, "`"); + } + } else if (char === " ") { + warn_at( + "expected_a_b", + line, + column - 1, + "\\s", + " " + ); + } else if (char === "$") { + if (source_line[0] !== "/") { + multi_mode = true; + } + } else if (char === "^") { + if (snippet !== "^") { + multi_mode = true; + } + } + next_char(); + return true; + } + + function sequence(follow) { + if (factor()) { + quantifier(); + return sequence(true); + } + if (!follow) { + warn_at("expected_regexp_factor_a", line, column, char); + } + } + +// Match a choice (a sequence that can be followed by | and another choice). + + sequence(); + if (char === "|") { + next_char("|"); + return choice(); + } + } + +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + next_char(); + if (char === "=") { + warn_at("expected_a_before_b", line, column, "\\", "="); + } + choice(); + +// Make sure there is a closing slash. + + snip(); + value = snippet; + next_char("/"); + +// Process dangling flag letters. + + const allowed = { + g: true, + i: true, + m: true, + u: true, + y: true + }; + const flag = empty(); + (function make_flag() { + if (is_letter(char)) { + if (allowed[char] !== true) { + warn_at("unexpected_a", line, column, char); + } + allowed[char] = false; + flag[char] = true; + next_char(); + return make_flag(); + } + }()); + back_char(); + if (char === "/" || char === "*") { + return stop_at("unexpected_a", line, from, char); + } + result = make("(regexp)", char); + result.flag = flag; + result.value = value; + if (multi_mode && !flag.m) { + warn_at("missing_m", line, column); + } + return result; + } + + function string(quote) { + +// Make a string token. + + let the_token; + snippet = ""; + next_char(); + + return (function next() { + if (char === quote) { + snip(); + the_token = make("(string)", snippet); + the_token.quote = quote; + return the_token; + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "\\") { + escape(quote); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char("`"); + } else { + next_char(); + } + return next(); + }()); + } + + function frack() { + if (char === ".") { + some_digits(rx_digits); + } + if (char === "E" || char === "e") { + next_char(); + if (char !== "+" && char !== "-") { + back_char(); + } + some_digits(rx_digits); + } + } + + function number() { + if (snippet === "0") { + next_char(); + if (char === ".") { + frack(); + } else if (char === "b") { + some_digits(rx_bits); + } else if (char === "o") { + some_digits(rx_octals); + } else if (char === "x") { + some_digits(rx_hexs); + } + } else { + next_char(); + frack(); + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + return stop_at( + "unexpected_a_after_b", + line, + column - 1, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + back_char(); + return make("(number)", snippet); + } + + function lex() { + let array; + let i = 0; + let j = 0; + let last; + let result; + let the_token; + +// This should properly be a tail recursive function, but sadly, conformant +// implementations of ES6 are still rare. This is the ideal code: + +// if (!source_line) { +// source_line = next_line(); +// from = 0; +// return ( +// source_line === undefined +// ? ( +// mega_mode +// ? stop_at("unclosed_mega", mega_line, mega_from) +// : make("(end)") +// ) +// : lex() +// ); +// } + +// Unfortunately, incompetent JavaScript engines will sometimes fail to execute +// it correctly. So for now, we do it the old fashioned way. + + while (!source_line) { + source_line = next_line(); + from = 0; + if (source_line === undefined) { + return ( + mega_mode + ? stop_at("unclosed_mega", mega_line, mega_from) + : make("(end)") + ); + } + } + + from = column; + result = source_line.match(rx_token); + +// result[1] token +// result[2] whitespace +// result[3] identifier +// result[4] number +// result[5] rest + + if (!result) { + return stop_at( + "unexpected_char_a", + line, + column, + source_line[0] + ); + } + + snippet = result[1]; + column += snippet.length; + source_line = result[5]; + +// Whitespace was matched. Call lex again to get more. + + if (result[2]) { + return lex(); + } + +// The token is an identifier. + + if (result[3]) { + return make(snippet, undefined, true); + } + +// The token is a number. + + if (result[4]) { + return number(snippet); + } + +// The token is a string. + + if (snippet === "\"") { + return string(snippet); + } + if (snippet === "'") { + if (!option.single) { + warn_at("use_double", line, column); + } + return string(snippet); + } + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (snippet === "`") { + if (mega_mode) { + return stop_at("expected_a_b", line, column, "}", "`"); + } + snippet = ""; + mega_from = from; + mega_line = line; + mega_mode = true; + +// Parsing a mega literal is tricky. First make a ` token. + + make("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + (function part() { + const at = source_line.search(rx_mega); + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + if (at < 0) { + snippet += source_line + "\n"; + return ( + next_line() === undefined + ? stop_at("unclosed_mega", mega_line, mega_from) + : part() + ); + } + snippet += source_line.slice(0, at); + column += at; + source_line = source_line.slice(at); + if (source_line[0] === "\\") { + snippet += source_line.slice(0, 2); + source_line = source_line.slice(2); + column += 2; + return part(); + } + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + make("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then make tokens that will become part of an expression until +// a } token is made. + + if (source_line[0] === "$") { + column += 2; + make("${"); + source_line = source_line.slice(2); + (function expr() { + const id = lex().id; + if (id === "{") { + return stop_at( + "expected_a_b", + line, + column, + "}", + "{" + ); + } + if (id !== "}") { + return expr(); + } + }()); + return part(); + } + }()); + source_line = source_line.slice(1); + column += 1; + mega_mode = false; + return make("`"); + } + +// The token is a // comment. + + if (snippet === "//") { + snippet = source_line; + source_line = ""; + the_token = comment(snippet); + if (mega_mode) { + warn("unexpected_comment", the_token, "`"); + } + return the_token; + } + +// The token is a /* comment. + + if (snippet === "/*") { + array = []; + if (source_line[0] === "/") { + warn_at("unexpected_a", line, column + i, "/"); + } + (function next() { + if (source_line > "") { + i = source_line.search(rx_star_slash); + if (i >= 0) { + return; + } + j = source_line.search(rx_slash_star); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + } + array.push(source_line); + source_line = next_line(); + if (source_line === undefined) { + return stop_at("unclosed_comment", line, column); + } + return next(); + }()); + snippet = source_line.slice(0, i); + j = snippet.search(rx_slash_star_or_slash); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + array.push(snippet); + column += i + 2; + source_line = source_line.slice(i + 2); + return comment(array); + } + +// The token is a slash. + + if (snippet === "/") { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + if (prior.identifier) { + if (!prior.dot) { + if (prior.id === "return") { + return regexp(); + } + if ( + prior.id === "(begin)" + || prior.id === "case" + || prior.id === "delete" + || prior.id === "in" + || prior.id === "instanceof" + || prior.id === "new" + || prior.id === "typeof" + || prior.id === "void" + || prior.id === "yield" + ) { + the_token = regexp(); + return stop("unexpected_a", the_token); + } + } + } else { + last = prior.id[prior.id.length - 1]; + if ("(,=:?[".indexOf(last) >= 0) { + return regexp(); + } + if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) { + the_token = regexp(); + warn("wrap_regexp", the_token); + return the_token; + } + } + if (source_line[0] === "=") { + column += 1; + source_line = source_line.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + } + return make(snippet); + } + + first = lex(); + json_mode = first.id === "{" || first.id === "["; + +// This loop will be replaced with a recursive call to lex when ES6 has been +// finished and widely deployed and adopted. + + while (true) { + if (lex().id === "(end)") { + break; + } + } +} + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + +function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!rx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!rx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property[id] === "number") { + property[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (tenure !== undefined) { + if (tenure[id] !== true) { + warn("unregistered_property_a", name); + } + } else { + if (name.identifier && rx_bad_property.test(id)) { + warn("bad_property_a", name); + } + } + property[id] = 1; + } + return id; +} + +function dispense() { + +// Deliver the next token, skipping the comments. + + const cadet = tokens[token_nr]; + token_nr += 1; + if (cadet.id === "(comment)") { + if (json_mode) { + warn("unexpected_a", cadet); + } + return dispense(); + } else { + return cadet; + } +} + +function lookahead() { + +// Look ahead one token without advancing. + + const old_token_nr = token_nr; + const cadet = dispense(true); + token_nr = old_token_nr; + return cadet; +} + +function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if (token.identifier && token.id !== "function") { + anon = token.id; + } else if (token.id === "(string)" && rx_identifier.test(token.value)) { + anon = token.value; + } + +// Attempt to match next_token with an expected id. + + if (id !== undefined && next_token.id !== id) { + return ( + match === undefined + ? stop("expected_a_b", next_token, id, artifact()) + : stop( + "expected_a_b_from_c_d", + next_token, + id, + artifact(match), + artifact_line(match), + artifact(next_token) + ) + ); + } + +// Promote the tokens, skipping comments. + + token = next_token; + next_token = dispense(); + if (next_token.id === "(end)") { + token_nr -= 1; + } +} + +// Parsing of JSON is simple: + +function json_value() { + let negative; + if (next_token.id === "{") { + return (function json_object() { + const brace = next_token; + const object = empty(); + const properties = []; + brace.expression = properties; + advance("{"); + if (next_token.id !== "}") { + (function next() { + let name; + let value; + if (next_token.quote !== "\"") { + warn( + "unexpected_a", + next_token, + next_token.quote + ); + } + name = next_token; + advance("(string)"); + if (object[token.value] !== undefined) { + warn("duplicate_a", token); + } else if (token.value === "__proto__") { + warn("bad_property_a", token); + } else { + object[token.value] = token; + } + advance(":"); + value = json_value(); + value.label = name; + properties.push(value); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("}", brace); + return brace; + }()); + } + if (next_token.id === "[") { + return (function json_array() { + const bracket = next_token; + const elements = []; + bracket.expression = elements; + advance("["); + if (next_token.id !== "]") { + (function next() { + elements.push(json_value()); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]", bracket); + return bracket; + }()); + } + if ( + next_token.id === "true" + || next_token.id === "false" + || next_token.id === "null" + ) { + advance(); + return token; + } + if (next_token.id === "(number)") { + if (!rx_JSON_number.test(next_token.value)) { + warn("unexpected_a"); + } + advance(); + return token; + } + if (next_token.id === "(string)") { + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + advance(); + return token; + } + if (next_token.id === "-") { + negative = next_token; + negative.arity = "unary"; + advance("-"); + advance("(number)"); + if (!rx_JSON_number.test(token.value)) { + warn("unexpected_a", token); + } + negative.expression = token; + return negative; + } + stop("unexpected_a"); +} + +// Now we parse JavaScript. + +function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + const id = name.id; + +// Reserved words may not be enrolled. + + if (syntax[id] !== undefined && id !== "ignore") { + warn("reserved_a", name); + } else { + +// Has the name been enrolled in this context? + + let earlier = functionage.context[id]; + if (earlier) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + +// Has the name been enrolled in an outer context? + + } else { + stack.forEach(function (value) { + const item = value.context[id]; + if (item !== undefined) { + earlier = item; + } + }); + if (earlier) { + if (id === "ignore") { + if (earlier.role === "variable") { + warn("unexpected_a", name); + } + } else { + if ( + ( + role !== "exception" + || earlier.role !== "exception" + ) + && role !== "parameter" + && role !== "function" + ) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + } + } + } + +// Enroll it. + + functionage.context[id] = name; + name.dead = true; + name.parent = functionage; + name.init = false; + name.role = role; + name.used = 0; + name.writable = !readonly; + } + } +} + +function expression(rbp, initial) { + +// This is the heart of the Pratt parser. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// nud Null denotation +// led Left denotation +// lbp Left binding power +// rbp Right binding power + +// It processes a nud (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop. It +// returns the expression's parse tree. + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement, + + if (!initial) { + advance(); + } + the_symbol = syntax[token.id]; + if (the_symbol !== undefined && the_symbol.nud !== undefined) { + left = the_symbol.nud(); + } else if (token.identifier) { + left = token; + left.arity = "variable"; + } else { + return stop("unexpected_a", token); + } + (function right() { + the_symbol = syntax[next_token.id]; + if ( + the_symbol !== undefined + && the_symbol.led !== undefined + && rbp < the_symbol.lbp + ) { + advance(); + left = the_symbol.led(left); + return right(); + } + }()); + return left; +} + +function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = next_token; + let the_value; + the_paren.free = true; + advance("("); + the_value = expression(0); + advance(")"); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (anticondition[the_value.id] === true) { + warn("unexpected_a", the_value); + } + return the_value; +} + +function is_weird(thing) { + return ( + thing.id === "(regexp)" + || thing.id === "{" + || thing.id === "=>" + || thing.id === "function" + || (thing.id === "[" && thing.arity === "unary") + ); +} + +function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + return ( + Array.isArray(b) + && a.length === b.length + && a.every(function (value, index) { + return are_similar(value, b[index]); + }) + ); + } + if (Array.isArray(b)) { + return false; + } + if (a.id === "(number)" && b.id === "(number)") { + return a.value === b.value; + } + let a_string; + let b_string; + if (a.id === "(string)") { + a_string = a.value; + } else if (a.id === "`" && a.constant) { + a_string = a.value[0]; + } + if (b.id === "(string)") { + b_string = b.value; + } else if (b.id === "`" && b.constant) { + b_string = b.value[0]; + } + if (typeof a_string === "string") { + return a_string === b_string; + } + if (is_weird(a) || is_weird(b)) { + return false; + } + if (a.arity === b.arity && a.id === b.id) { + if (a.id === ".") { + return ( + are_similar(a.expression, b.expression) + && are_similar(a.name, b.name) + ); + } + if (a.arity === "unary") { + return are_similar(a.expression, b.expression); + } + if (a.arity === "binary") { + return ( + a.id !== "(" + && are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + ); + } + if (a.arity === "ternary") { + return ( + are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + && are_similar(a.expression[2], b.expression[2]) + ); + } + if (a.arity === "function" && a.arity === "regexp") { + return false; + } + return true; + } + return false; +} + +function semicolon() { + +// Try to match a semicolon. + + if (next_token.id === ";") { + advance(";"); + } else { + warn_at( + "expected_a_b", + token.line, + token.thru, + ";", + artifact(next_token) + ); + } + anon = "anonymous"; +} + +function statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token.identifier && next_token.id === ":") { + the_label = token; + if (the_label.id === "ignore") { + warn("unexpected_a", the_label); + } + advance(":"); + if ( + next_token.id === "do" + || next_token.id === "for" + || next_token.id === "switch" + || next_token.id === "while" + ) { + enroll(the_label, "label", true); + the_label.init = true; + the_label.dead = false; + the_statement = statement(); + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token; + first.statement = true; + the_symbol = syntax[first.id]; + if (the_symbol !== undefined && the_symbol.fud !== undefined) { + the_symbol.disrupt = false; + the_symbol.statement = true; + the_statement = the_symbol.fud(); + } else { + +// It is an expression statement. + + the_statement = expression(0, true); + if (the_statement.wrapped && the_statement.id !== "(") { + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; +} + +function statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const array = []; + (function next(disrupt) { + if ( + next_token.id !== "}" + && next_token.id !== "case" + && next_token.id !== "default" + && next_token.id !== "else" + && next_token.id !== "(end)" + ) { + let a_statement = statement(); + array.push(a_statement); + if (disrupt) { + warn("unreachable_a", a_statement); + } + return next(a_statement.disrupt); + } + }(false)); + return array; +} + +function not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === global) { + warn("unexpected_at_top_level_a", thing); + } +} + +function top_level_only(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== global) { + warn("misplaced_a", the_thing); + } +} + +function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token; + the_block.arity = "statement"; + the_block.body = special === "body"; + +// Top level function bodies may include the "use strict" pragma. + + if ( + special === "body" + && stack.length === 1 + && next_token.value === "use strict" + ) { + next_token.statement = true; + advance("(string)"); + advance(";"); + } + stmts = statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option.devel && special !== "ignore") { + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; +} + +function mutation_check(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + warn("bad_assignment_a", the_thing); + return false; + } + return true; +} + +function left_check(left, right) { + +// Warn if the left is not one of these: +// e.b +// e[b] +// e() +// ?: +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !left_check(left.expression[1]) + && !left_check(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right); + return false; + } + return true; +} + +// These functions are used to specify the grammar of our language: + +function symbol(id, bp) { + +// Make a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax[id] = the_symbol; + } + return the_symbol; +} + +function assignment(id) { + +// Make an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led = function (left) { + const the_token = token; + let right; + the_token.arity = "assignment"; + right = expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "pre" + || right.arity === "post" + ) { + warn("unexpected_a", right); + } + mutation_check(left); + return the_token; + }; + return the_symbol; +} + +function constant(id, type, value) { + +// Make a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud = ( + typeof value === "function" + ? value + : function () { + token.constant = true; + if (value !== undefined) { + token.value = value; + } + return token; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; +} + +function infix(id, bp, f) { + +// Make an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, expression(bp)]; + return the_token; + }; + return the_symbol; +} + +function infixr(id, bp) { + +// Make a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + the_token.expression = [left, expression(bp - 1)]; + return the_token; + }; + return the_symbol; +} + +function post(id) { + +// Make one of the post operators. + + const the_symbol = symbol(id, 150); + the_symbol.led = function (left) { + token.expression = left; + token.arity = "post"; + mutation_check(token.expression); + return token; + }; + return the_symbol; +} + +function pre(id) { + +// Make one of the pre operators. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "pre"; + the_token.expression = expression(150); + mutation_check(the_token.expression); + return the_token; + }; + return the_symbol; +} + +function prefix(id, f) { + +// Make a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = expression(150); + return the_token; + }; + return the_symbol; +} + +function stmt(id, f) { + +// Make a statement. + + const the_symbol = symbol(id); + the_symbol.fud = function () { + token.arity = "statement"; + return f(); + }; + return the_symbol; +} + +function ternary(id1, id2) { + +// Make a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led = function (left) { + const the_token = token; + const second = expression(20); + advance(id2); + token.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, expression(10)]; + if (next_token.id !== ")") { + warn("use_open", the_token); + } + return the_token; + }; + return the_symbol; +} + +// Begin defining the language. + +syntax = empty(); + +symbol("}"); +symbol(")"); +symbol("]"); +symbol(","); +symbol(";"); +symbol(":"); +symbol("*/"); +symbol("await"); +symbol("case"); +symbol("catch"); +symbol("class"); +symbol("default"); +symbol("else"); +symbol("enum"); +symbol("finally"); +symbol("implements"); +symbol("interface"); +symbol("package"); +symbol("private"); +symbol("protected"); +symbol("public"); +symbol("static"); +symbol("super"); +symbol("void"); +symbol("yield"); + +constant("(number)", "number"); +constant("(regexp)", "regexp"); +constant("(string)", "string"); +constant("arguments", "object", function () { + warn("unexpected_a", token); + return token; +}); +constant("eval", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("false", "boolean", false); +constant("Function", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("ignore", "undefined", function () { + warn("unexpected_a", token); + return token; +}); +constant("Infinity", "number", Infinity); +constant("isFinite", "function", function () { + warn("expected_a_b", token, "Number.isFinite", "isFinite"); + return token; +}); +constant("isNaN", "function", function () { + warn("number_isNaN", token); + return token; +}); +constant("NaN", "number", NaN); +constant("null", "null", null); +constant("this", "object", function () { + if (!option.this) { + warn("unexpected_a", token); + } + return token; +}); +constant("true", "boolean", true); +constant("undefined", "undefined"); + +assignment("="); +assignment("+="); +assignment("-="); +assignment("*="); +assignment("/="); +assignment("%="); +assignment("&="); +assignment("|="); +assignment("^="); +assignment("<<="); +assignment(">>="); +assignment(">>>="); + +infix("??", 35); +infix("||", 40); +infix("&&", 50); +infix("|", 70); +infix("^", 80); +infix("&", 90); +infix("==", 100); +infix("===", 100); +infix("!=", 100); +infix("!==", 100); +infix("<", 110); +infix(">", 110); +infix("<=", 110); +infix(">=", 110); +infix("in", 110); +infix("instanceof", 110); +infix("<<", 120); +infix(">>", 120); +infix(">>>", 120); +infix("+", 130); +infix("-", 130); +infix("*", 140); +infix("/", 140); +infix("%", 140); +infixr("**", 150); +infix("(", 160, function (left) { + const the_paren = token; + let the_argument; + if (left.id !== "function") { + left_check(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (next_token.id !== ")") { + (function next() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + the_paren.free = true; + if (the_argument.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + the_paren.free = false; + } + return the_paren; +}); +infix(".", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("?.", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("[", 170, function (left) { + const the_token = token; + const the_subscript = expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + const name = survey(the_subscript); + if (rx_identifier.test(name)) { + warn("subscript_a", the_subscript, name); + } + } + left_check(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; +}); +infix("=>", 170, function (left) { + return stop("wrap_parameter", left); +}); + +function do_tick() { + const the_tick = token; + the_tick.value = []; + the_tick.expression = []; + if (next_token.id !== "`") { + (function part() { + advance("(string)"); + the_tick.value.push(token); + if (next_token.id === "${") { + advance("${"); + the_tick.expression.push(expression(0)); + advance("}"); + return part(); + } + }()); + } + advance("`"); + return the_tick; +} + +infix("`", 160, function (left) { + const the_tick = do_tick(); + left_check(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; +}); + +post("++"); +post("--"); +pre("++"); +pre("--"); + +prefix("+"); +prefix("-"); +prefix("~"); +prefix("!"); +prefix("!!"); +prefix("[", function () { + const the_token = token; + the_token.expression = []; + if (next_token.id !== "]") { + (function next() { + let element; + let ellipsis = false; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + element = expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]"); + return the_token; +}); +prefix("/=", function () { + stop("expected_a_b", token, "/\\=", "/="); +}); +prefix("=>", function () { + return stop("expected_a_before_b", token, "()", "=>"); +}); +prefix("new", function () { + const the_new = token; + const right = expression(160); + if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "()", artifact(next_token)); + } + the_new.expression = right; + return the_new; +}); +prefix("typeof"); +prefix("void", function () { + const the_void = token; + warn("unexpected_a", the_void); + the_void.expression = expression(0); + return the_void; +}); + +function parameter_list() { + const list = []; + let optional; + const signature = ["("]; + if (next_token.id !== ")" && next_token.id !== "(end)") { + (function parameter() { + let ellipsis = false; + let param; + if (next_token.id === "{") { + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("{"); + signature.push("{"); + (function subparameter() { + let subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (next_token.id === ":") { + advance(":"); + advance(); + token.label = subparam; + subparam = token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + } + if (next_token.id === "=") { + advance("="); + subparam.expression = expression(); + param.open = true; + } + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return subparameter(); + } + }()); + list.push(param); + advance("}"); + signature.push("}"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else if (next_token.id === "[") { + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("["); + signature.push("[]"); + (function subparameter() { + const subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + if (next_token.id === "=") { + advance("="); + subparam.expression = expression(); + param.open = true; + } + if (next_token.id === ",") { + advance(","); + return subparameter(); + } + }()); + list.push(param); + advance("]"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else { + if (next_token.id === "...") { + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + } + if (!next_token.identifier) { + return stop("expected_identifier_a"); + } + param = next_token; + list.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (next_token.id === "=") { + optional = param; + advance("="); + param.expression = expression(0); + } else { + if (optional !== undefined) { + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } + } + }()); + } + advance(")"); + signature.push(")"); + return [list, signature.join("")]; +} + +function do_function(the_function) { + let name; + if (the_function === undefined) { + the_function = token; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + name = next_token; + enroll(name, "variable", true); + the_function.name = name; + name.init = true; + name.calls = empty(); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + if (next_token.identifier) { + name = next_token; + the_function.name = name; + advance(); + } else { + the_function.name = anon; + } + } + } else { + name = the_function.name; + } + the_function.level = functionage.level + 1; + if (mega_mode) { + warn("unexpected_a", the_function); + } + +// Don't make functions in loops. It is inefficient, and it can lead to scoping +// errors. + + if (functionage.loop > 0) { + warn("function_in_loop", the_function); + } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + the_function.context = empty(); + the_function.finally = 0; + the_function.loop = 0; + the_function.switch = 0; + the_function.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functions.push(the_function); + functionage = the_function; + if (the_function.arity !== "statement" && typeof name === "object") { + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// Parse the parameter list. + + advance("("); + token.free = false; + token.arity = "function"; + [functionage.parameters, functionage.signature] = parameter_list(); + functionage.parameters.forEach(function enroll_parameter(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + name.names.forEach(enroll_parameter); + } + }); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && next_token.line === token.line + ) { + return stop("unexpected_a", next_token); + } + if ( + next_token.id === "." + || next_token.id === "?." + || next_token.id === "[" + ) { + warn("unexpected_a"); + } + +// Restore the previous context. + + functionage = stack.pop(); + return the_function; +} + +prefix("function", do_function); + +function fart(pl) { + advance("=>"); + const the_fart = token; + the_fart.arity = "binary"; + the_fart.name = "=>"; + the_fart.level = functionage.level + 1; + functions.push(the_fart); + if (functionage.loop > 0) { + warn("function_in_loop", the_fart); + } + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + the_fart.context = empty(); + the_fart.finally = 0; + the_fart.loop = 0; + the_fart.switch = 0; + the_fart.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functionage = the_fart; + the_fart.parameters = pl[0]; + the_fart.signature = pl[1]; + the_fart.parameters.forEach(function (name) { + enroll(name, "parameter", true); + }); + if (next_token.id === "{") { + warn("expected_a_b", the_fart, "function", "=>"); + the_fart.block = block("body"); + } else { + the_fart.expression = expression(0); + } + functionage = stack.pop(); + return the_fart; +} + +prefix("(", function () { + const the_paren = token; + let the_value; + const cadet = lookahead().id; + +// We can distinguish between a parameter list for => and a wrapped expression +// with one token of lookahead. + + if ( + next_token.id === ")" + || next_token.id === "..." + || (next_token.identifier && (cadet === "," || cadet === "=")) + ) { + the_paren.free = false; + return fart(parameter_list()); + } + the_paren.free = true; + the_value = expression(0); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + if (next_token.id === "=>") { + if (the_value.arity !== "variable") { + if (the_value.id === "{" || the_value.id === "[") { + warn("expected_a_before_b", the_paren, "function", "("); + return stop("expected_a_b", next_token, "{", "=>"); + } + return stop("expected_identifier_a", the_value); + } + the_paren.expression = [the_value]; + return fart([the_paren.expression, "(" + the_value.id + ")"]); + } + return the_value; +}); +prefix("`", do_tick); +prefix("{", function () { + const the_brace = token; + const seen = empty(); + the_brace.expression = []; + if (next_token.id !== "}") { + (function member() { + let extra; + let full; + let id; + let name = next_token; + let value; + advance(); + if ( + (name.id === "get" || name.id === "set") + && next_token.identifier + ) { + if (!option.getset) { + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + next_token.id; + name = next_token; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (next_token.id === "}" || next_token.id === ",") { + if (typeof extra === "string") { + advance("("); + } + value = expression(Infinity, true); + } else if (next_token.id === "(") { + value = do_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + advance("("); + } + let the_colon = next_token; + advance(":"); + value = expression(0); + if (value.id === name.id && value.id !== "function") { + warn("unexpected_a", the_colon, ": " + name.id); + } + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + advance(":"); + value = expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (next_token.id === ",") { + advance(","); + return member(); + } + }()); + } + advance("}"); + return the_brace; +}); + +stmt(";", function () { + warn("unexpected_a", token); + return token; +}); +stmt("{", function () { + warn("naked_block", token); + return block("naked"); +}); +stmt("break", function () { + const the_break = token; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (next_token.identifier && token.line === next_token.line) { + the_label = functionage.context[next_token.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + warn( + (the_label !== undefined && the_label.dead) + ? "out_of_scope_a" + : "not_label_a" + ); + } else { + the_label.used += 1; + } + the_break.label = next_token; + advance(); + } + advance(";"); + return the_break; +}); + +function do_var() { + const the_statement = token; + const is_const = the_statement.id === "const"; + the_statement.names = []; + +// A program may use var or let, but not both. + + if (!is_const) { + if (var_mode === undefined) { + var_mode = the_statement.id; + } else if (the_statement.id !== var_mode) { + warn( + "expected_a_b", + the_statement, + var_mode, + the_statement.id + ); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + warn("var_switch", the_statement); + } + if (functionage.loop > 0 && the_statement.id === "var") { + warn("var_loop", the_statement); + } + (function next() { + if (next_token.id === "{" && the_statement.id !== "var") { + const the_brace = next_token; + advance("{"); + (function pair() { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + survey(name); + advance(); + if (next_token.id === ":") { + advance(":"); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + next_token.label = name; + the_statement.names.push(next_token); + enroll(next_token, "variable", is_const); + advance(); + the_brace.open = true; + } else { + the_statement.names.push(name); + enroll(name, "variable", is_const); + } + name.dead = false; + name.init = true; + if (next_token.id === "=") { + advance("="); + name.expression = expression(); + the_brace.open = true; + } + if (next_token.id === ",") { + advance(","); + return pair(); + } + }()); + advance("}"); + advance("="); + the_statement.expression = expression(0); + } else if (next_token.id === "[" && the_statement.id !== "var") { + const the_bracket = next_token; + advance("["); + (function element() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + advance(); + the_statement.names.push(name); + enroll(name, "variable", is_const); + name.dead = false; + name.init = true; + if (ellipsis) { + name.ellipsis = true; + } else { + if (next_token.id === "=") { + advance("="); + name.expression = expression(); + the_bracket.open = true; + } + if (next_token.id === ",") { + advance(","); + return element(); + } + } + }()); + advance("]"); + advance("="); + the_statement.expression = expression(0); + } else if (next_token.identifier) { + const name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", is_const); + if (next_token.id === "=" || is_const) { + advance("="); + name.dead = false; + name.init = true; + name.expression = expression(0); + } + the_statement.names.push(name); + } else { + return stop("expected_identifier_a", next_token); + } + }()); + semicolon(); + return the_statement; +} + +stmt("const", do_var); +stmt("continue", function () { + const the_continue = token; + if (functionage.loop < 1 || functionage.finally > 0) { + warn("unexpected_a", the_continue); + } + not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; +}); +stmt("debugger", function () { + const the_debug = token; + if (!option.devel) { + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; +}); +stmt("delete", function () { + const the_token = token; + const the_value = expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; +}); +stmt("do", function () { + const the_do = token; + not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; +}); +stmt("export", function () { + const the_export = token; + let the_id; + let the_name; + let the_thing; + + function export_id() { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + the_id = next_token.id; + the_name = global.context[the_id]; + if (the_name === undefined) { + warn("unexpected_a"); + } else { + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a"); + } + exports[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + } + + the_export.expression = []; + if (next_token.id === "default") { + if (exports.default !== undefined) { + warn("duplicate_a"); + } + advance("default"); + the_thing = expression(0); + if ( + the_thing.id !== "(" + || the_thing.expression[0].id !== "." + || the_thing.expression[0].expression.id !== "Object" + || the_thing.expression[0].name.id !== "freeze" + ) { + warn("freeze_exports", the_thing); + } + if (next_token.id === ";") { + semicolon(); + } + exports.default = the_thing; + the_export.expression.push(the_thing); + } else { + if (next_token.id === "function") { + warn("freeze_exports"); + the_thing = statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a", the_name); + } + exports[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + warn("unexpected_a", next_token); + statement(); + } else if (next_token.id === "{") { + advance("{"); + (function loop() { + export_id(); + if (next_token.id === ",") { + advance(","); + return loop(); + } + }()); + advance("}"); + semicolon(); + } else { + stop("unexpected_a"); + } + } + module_mode = true; + return the_export; +}); +stmt("for", function () { + let first; + const the_for = token; + if (!option.for) { + warn("unexpected_a", the_for); + } + not_top_level(the_for); + functionage.loop += 1; + advance("("); + token.free = true; + if (next_token.id === ";") { + return stop("expected_a_b", the_for, "while (", "for (;"); + } + if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + return stop("unexpected_a"); + } + first = expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = expression(0); + advance(";"); + the_for.inc = expression(0); + if (the_for.inc.id === "++") { + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; +}); +stmt("function", do_function); +stmt("if", function () { + let the_else; + const the_if = token; + the_if.expression = condition(); + the_if.block = block(); + if (next_token.id === "else") { + advance("else"); + the_else = token; + the_if.else = ( + next_token.id === "if" + ? statement() + : block() + ); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + the_if.disrupt = true; + } else { + warn("unexpected_a", the_else); + } + } + } + return the_if; +}); +stmt("import", function () { + const the_import = token; + if (next_token.id === "(") { + the_import.arity = "unary"; + the_import.constant = true; + the_import.statement = false; + advance("("); + const string = expression(0); + if (string.id !== "(string)") { + warn("expected_string_a", string); + } + froms.push(token.value); + advance(")"); + advance("."); + advance("then"); + advance("("); + the_import.expression = expression(0); + advance(")"); + semicolon(); + return the_import; + } + let name; + if (typeof module_mode === "object") { + warn("unexpected_directive_a", module_mode, module_mode.directive); + } + module_mode = true; + if (next_token.identifier) { + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + const names = []; + advance("{"); + if (next_token.id !== "}") { + while (true) { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (next_token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token; + if (!rx_module.test(token.value)) { + warn("bad_module_name_a", token); + } + froms.push(token.value); + semicolon(); + return the_import; +}); +stmt("let", do_var); +stmt("return", function () { + const the_return = token; + not_top_level(the_return); + if (functionage.finally > 0) { + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (next_token.id !== ";" && the_return.line === next_token.line) { + the_return.expression = expression(10); + } + advance(";"); + return the_return; +}); +stmt("switch", function () { + let dups = []; + let last; + let stmts; + const the_cases = []; + let the_disrupt = true; + const the_switch = token; + not_top_level(the_switch); + if (functionage.finally > 0) { + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token.free = true; + the_switch.expression = expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + (function major() { + const the_case = next_token; + the_case.arity = "statement"; + the_case.expression = []; + (function minor() { + advance("case"); + token.switch = true; + const exp = expression(0); + if (dups.some(function (thing) { + return are_similar(thing, exp); + })) { + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (next_token.id === "case") { + return minor(); + } + }()); + stmts = statements(); + if (stmts.length < 1) { + warn("expected_statements_a"); + return; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "break;", + artifact(next_token) + ); + } + if (next_token.id === "case") { + return major(); + } + }()); + dups = undefined; + if (next_token.id === "default") { + const the_default = next_token; + advance("default"); + token.switch = true; + advance(":"); + the_switch.else = statements(); + if (the_switch.else.length < 1) { + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + const the_last = the_switch.else[the_switch.else.length - 1]; + if (the_last.id === "break" && the_last.label === undefined) { + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; +}); +stmt("throw", function () { + const the_throw = token; + the_throw.disrupt = true; + the_throw.expression = expression(10); + semicolon(); + if (functionage.try > 0) { + warn("unexpected_a", the_throw); + } + return the_throw; +}); +stmt("try", function () { + let the_catch; + let the_disrupt; + const the_try = token; + if (functionage.try > 0) { + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (next_token.id === "catch") { + let ignored = "ignore"; + the_catch = next_token; + the_try.catch = the_catch; + advance("catch"); + if (next_token.id === "(") { + advance("("); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + if (next_token.id !== "ignore") { + ignored = undefined; + the_catch.name = next_token; + enroll(next_token, "exception", true); + } + advance(); + advance(")"); + } + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "catch", + artifact(next_token) + ); + + } + if (next_token.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; +}); +stmt("var", do_var); +stmt("while", function () { + const the_while = token; + not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; +}); +stmt("with", function () { + stop("unexpected_a", token); +}); + +ternary("?", ":"); + +// Ambulation of the parse tree. + +function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; +} + +function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all of the +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + } + }; +} + +const posts = empty(); +const pres = empty(); +const preaction = action(pres); +const postaction = action(posts); +const preamble = amble(pres); +const postamble = amble(posts); + +function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.id === "function") { + walk_statement(thing.block); + } + if (thing.arity === "pre" || thing.arity === "post") { + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + warn("unexpected_statement_a", thing); + } + postamble(thing); + } + } +} + +function walk_statement(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_statement); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + && thing.id !== "import" + ) { + warn("unexpected_expression_a", thing); + } + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + } +} + +function lookup(thing) { + if (thing.arity === "variable") { + +// Look up the variable in the current context. + + let the_variable = functionage.context[thing.id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable === undefined) { + stack.forEach(function (outer) { + const a_variable = outer.context[thing.id]; + if ( + a_variable !== undefined + && a_variable.role !== "label" + ) { + the_variable = a_variable; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (the_variable === undefined) { + if (declared_globals[thing.id] === undefined) { + warn("undeclared_a", thing); + return; + } + the_variable = { + dead: false, + parent: global, + id: thing.id, + init: true, + role: "variable", + used: 0, + writable: false + }; + global.context[thing.id] = the_variable; + } + the_variable.closure = true; + functionage.context[thing.id] = the_variable; + } else if (the_variable.role === "label") { + warn("label_a", thing); + } + if ( + the_variable.dead + && ( + the_variable.calls === undefined + || functionage.name === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + ) { + warn("out_of_scope_a", thing); + } + return the_variable; + } +} + +function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); +} + +function preaction_function(thing) { + if (thing.arity === "statement" && blockage.body !== true) { + warn("unexpected_a", thing); + } + stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); +} + +function bitwise_check(thing) { + if (!option.bitwise && bitwiseop[thing.id] === true) { + warn("unexpected_a", thing); + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + warn("unexpected_a", thing); + } +} + +function pop_block() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); +} + +function activate(name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.init = true; + } + } + blockage.live.push(name); +} + +function action_var(thing) { + thing.names.forEach(activate); +} + +preaction("assignment", bitwise_check); +preaction("binary", bitwise_check); +preaction("binary", function (thing) { + if (relationop[thing.id] === true) { + const left = thing.expression[0]; + const right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + warn("expected_string_a", right); + } + } else { + const value = right.value; + if (value === "null" || value === "undefined") { + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + warn("expected_type_string_a", right, value); + } + } + } + } +}); +preaction("binary", "==", function (thing) { + warn("expected_a_b", thing, "===", "=="); +}); +preaction("binary", "!=", function (thing) { + warn("expected_a_b", thing, "!==", "!="); +}); +preaction("binary", "=>", preaction_function); +preaction("binary", "||", function (thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + warn("and", thang); + } + }); +}); +preaction("binary", "(", function (thing) { + const left = thing.expression[0]; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + const parent = functionage.name.parent; + if (parent) { + const left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + && left_variable.dead + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } +}); +preaction("binary", "in", function (thing) { + warn("infix_in", thing); +}); +preaction("binary", "instanceof", function (thing) { + warn("unexpected_a", thing); +}); +preaction("binary", ".", function (thing) { + if (thing.expression.new) { + thing.new = true; + } +}); +preaction("statement", "{", function (thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; +}); +preaction("statement", "for", function (thing) { + if (thing.name !== undefined) { + const the_variable = lookup(thing.name); + if (the_variable !== undefined) { + the_variable.init = true; + if (!the_variable.writable) { + warn("bad_assignment_a", thing.name); + } + } + } + walk_statement(thing.initial); +}); +preaction("statement", "function", preaction_function); +preaction("unary", "~", bitwise_check); +preaction("unary", "function", preaction_function); +preaction("variable", function (thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } +}); + +function init_variable(name) { + const the_variable = lookup(name); + if (the_variable !== undefined) { + if (the_variable.writable) { + the_variable.init = true; + return; + } + } + warn("bad_assignment_a", name); +} + +postaction("assignment", "+=", function (thing) { + let right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } +}); +postaction("assignment", function (thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + if (thing.id === "=") { + if (thing.names !== undefined) { + if (Array.isArray(thing.names)) { + thing.names.forEach(init_variable); + } else { + init_variable(thing.names); + } + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.writable !== true) { + warn("bad_assignment_a", lvalue); + } + } + const right = syntax[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + warn("unexpected_a", thing.expression[1]); + } + } +}); + +function postaction_function(thing) { + delete functionage.finally; + delete functionage.loop; + delete functionage.switch; + delete functionage.try; + functionage = stack.pop(); + if (thing.wrapped) { + warn("unexpected_parens", thing); + } + return pop_block(); +} + +postaction("binary", function (thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || are_similar(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option.convert) { + if (thing.expression[0].value === "") { + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === "." || thing.id === "?.") { + if (thing.expression.id === "RegExp") { + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } +}); +postaction("binary", "&&", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "||", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "=>", postaction_function); +postaction("binary", "(", function (thing) { + let left = thing.expression[0]; + let the_new; + let arg; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option.eval) { + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + warn( + "expected_a_before_b", + left, + "new", + artifact(left) + ); + } + } + } else if (left.id === ".") { + let cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + cack = !cack; + } + if (rx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + warn("unexpected_a", the_new); + } else { + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + const paren = left.expression; + if (paren.id === "(") { + const array = paren.expression; + if (array.length === 1) { + const new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } +}); +postaction("binary", "[", function (thing) { + if (thing.expression[0].id === "RegExp") { + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + warn("weird_expression_a", thing.expression[1]); + } +}); +postaction("statement", "{", pop_block); +postaction("statement", "const", action_var); +postaction("statement", "export", top_level_only); +postaction("statement", "for", function (thing) { + walk_statement(thing.inc); +}); +postaction("statement", "function", postaction_function); +postaction("statement", "import", function (the_thing) { + const name = the_thing.name; + if (name) { + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return top_level_only(the_thing); + } +}); +postaction("statement", "let", action_var); +postaction("statement", "try", function (thing) { + if (thing.catch !== undefined) { + const the_name = thing.catch.name; + if (the_name !== undefined) { + const the_variable = functionage.context[the_name.id]; + the_variable.dead = false; + the_variable.init = true; + } + walk_statement(thing.catch.block); + } +}); +postaction("statement", "var", action_var); +postaction("ternary", function (thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || are_similar(thing.expression[1], thing.expression[2]) + ) { + warn("unexpected_a", thing); + } else if (are_similar(thing.expression[0], thing.expression[1])) { + warn("expected_a_b", thing, "||", "?"); + } else if (are_similar(thing.expression[0], thing.expression[2])) { + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + warn("wrap_condition", thing.expression[0]); + } +}); +postaction("unary", function (thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option.convert) { + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } +}); +postaction("unary", "function", postaction_function); +postaction("unary", "+", function (thing) { + if (!option.convert) { + warn("expected_a_b", thing, "Number(...)", "+"); + } + const right = thing.expression; + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } +}); + +function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + if (id !== "ignore") { + const name = the_function.context[id]; + if (name.parent === the_function) { + if ( + name.used === 0 + && ( + name.role !== "function" + || name.parent.arity !== "unary" + ) + ) { + warn("unused_a", name); + } else if (!name.init) { + warn("uninitialized_a", name); + } + } + } + }); +} + +function uninitialized_and_unused() { + +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (module_mode === true || option.node) { + delve(global); + } + functions.forEach(delve); +} + +// Go through the token list, looking at usage of whitespace. + +function whitage() { + let closer = "(end)"; + let free = false; + let left = global; + let margin = 0; + let nr_comments_skipped = 0; + let open = true; + let opening = true; + let right; + + function pop() { + const previous = stack.pop(); + closer = previous.closer; + free = previous.free; + margin = previous.margin; + open = previous.open; + opening = previous.opening; + } + + function push() { + stack.push({ + closer, + free, + margin, + open, + opening + }); + } + + function expected_at(at) { + warn( + "expected_a_at_b_c", + right, + artifact(right), + fudge + at, + artifact_column(right) + ); + } + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function no_space() { + if (left.line === right.line) { + if (left.thru !== right.from && nr_comments_skipped === 0) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (open) { + const at = ( + free + ? margin + : margin + 8 + ); + if (right.from < at) { + expected_at(at); + } + } else { + if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line || !open) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (right.from !== margin) { + expected_at(margin); + } + } + } + + stack = []; + tokens.forEach(function (the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + + const new_closer = opener[left.id]; + if (typeof new_closer === "string") { + if (new_closer !== right.id) { + opening = left.open || (left.line !== right.line); + push(); + closer = new_closer; + if (opening) { + free = closer === ")" && left.free; + open = true; + margin += 4; + if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (right.switch) { + at_margin(-4); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + free = false; + open = false; + no_space_only(); + } + } else { + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like '{]') have already been detected. + + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } + } else { + if (right.statement === true) { + if (left.id === "else") { + one_space_only(); + } else { + at_margin(0); + open = false; + } + +// If right is a closer, then pop the previous state. + + } else if (right.id === closer) { + pop(); + if (opening && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-4); + } else if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + one_space(); + } else { + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + at_margin(0); + } else { + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + no_space(); + } else if ( + left.id === "." + || left.id === "?." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + ) { + no_space_only(); + } else if (right.id === "." || right.id === "?.") { + no_space_only(); + } else if (left.id === ";") { + if (open) { + at_margin(0); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + one_space_only(); + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +// The jslint function itself. + +export default Object.freeze(function jslint( + source = "", + option_object = empty(), + global_array = [] +) { + try { + warnings = []; + option = Object.assign(empty(), option_object); + anon = "anonymous"; + block_stack = []; + declared_globals = empty(); + directive_mode = true; + directives = []; + early_stop = true; + exports = empty(); + froms = []; + fudge = ( + option.fudge + ? 1 + : 0 + ); + functions = []; + global = { + id: "(global)", + body: true, + context: empty(), + from: 0, + level: 0, + line: 0, + live: [], + loop: 0, + switch: 0, + thru: 0 + }; + blockage = global; + functionage = global; + json_mode = false; + mega_mode = false; + module_mode = false; + next_token = global; + property = empty(); + shebang = false; + stack = []; + tenure = undefined; + token = global; + token_nr = 0; + var_mode = undefined; + populate(standard, declared_globals, false); + populate(global_array, declared_globals, false); + Object.keys(option).forEach(function (name) { + if (option[name] === true) { + const allowed = allowed_option[name]; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } + }); + tokenize(source); + advance(); + if (json_mode) { + tree = json_value(); + advance("(end)"); + } else { + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option.browser) { + if (next_token.id === ";") { + advance(";"); + } + } else { + +// If we are not in a browser, then the file form of strict pragma may be used. + + if ( + next_token.value === "use strict" + ) { + advance("(string)"); + advance(";"); + } + } + tree = statements(); + advance("(end)"); + functionage = global; + walk_statement(tree); + if (warnings.length === 0) { + uninitialized_and_unused(); + if (!option.white) { + whitage(); + } + } + } + if (!option.browser) { + directives.forEach(function (comment) { + if (comment.directive === "global") { + warn("missing_browser", comment); + } + }); + } + early_stop = false; + } catch (e) { + if (e.name !== "JSLintError") { + warnings.push(e); + } + } + return { + directives, + edition: "2020-11-06", + exports, + froms, + functions, + global, + id: "(JSLint)", + json: json_mode, + lines, + module: module_mode === true, + ok: warnings.length === 0 && !early_stop, + option, + property, + shebang: ( + shebang + ? lines[0] + : undefined + ), + stop: early_stop, + tokens, + tree, + warnings: warnings.sort(function (a, b) { + return a.line - b.line || a.column - b.column; + }) + }; +}); diff --git a/branch.ci/package.json b/branch.ci/package.json new file mode 100644 index 000000000..916752521 --- /dev/null +++ b/branch.ci/package.json @@ -0,0 +1,5 @@ +{ + "name": "jslint", + "type": "module", + "version": "2020.11.6" +} diff --git a/branch.ci/report.js b/branch.ci/report.js new file mode 100644 index 000000000..5e487d143 --- /dev/null +++ b/branch.ci/report.js @@ -0,0 +1,222 @@ +// report.js +// 2018-10-22 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Generate JSLint HTML reports. + +/*property + closure, column, context, edition, error, exports, filter, forEach, freeze, + froms, fudge, function, functions, global, id, isArray, join, json, keys, + length, level, line, lines, message, module, name, names, option, + parameters, parent, property, push, replace, role, signature, sort, stop, + warnings +*/ + +const rx_amp = /&/g; +const rx_gt = />/g; +const rx_lt = / with less destructive entities. + + return String( + string + ).replace( + rx_amp, + "&" + ).replace( + rx_lt, + "<" + ).replace( + rx_gt, + ">" + ); +} + +export default Object.freeze({ + error: function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + let fudge = Number(Boolean(data.option.fudge)); + let output = []; + if (data.stop) { + output.push("
    JSLint was unable to finish.
    "); + } + data.warnings.forEach(function (warning) { + output.push( + "
    ", + entityify(warning.line + fudge), + ".", + entityify(warning.column + fudge), + "
    ", + entityify(warning.message), + "
    ", + entityify(data.lines[warning.line] || ""), + "" + ); + }); + return output.join(""); + }, + + function: function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + let fudge = Number(Boolean(data.option.fudge)); + let mode = ( + data.module + ? "module" + : "global" + ); + let output = []; + + if (data.json) { + return ( + data.warnings.length === 0 + ? "
    JSON: good.
    " + : "
    JSON: bad.
    " + ); + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + let global = Object.keys(data.global.context).sort(); + let froms = data.froms.sort(); + let exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + let context = the_function.context; + let list = Object.keys(context); + output.push( + "
    ", + entityify(the_function.line + fudge), + "
    ", + ( + the_function.name === "=>" + ? entityify(the_function.signature) + " =>" + : ( + typeof the_function.name === "string" + ? "«" + entityify(the_function.name) + "»" + : "" + entityify(the_function.name.id) + "" + ) + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + let params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.parent === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.parent === the_function + ); + })); + detail("outer", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.parent !== the_function + && the_variable.parent.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].parent.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + output.push( + "
    JSLint edition ", + entityify(data.edition), + "
    " + ); + return output.join(""); + }, + + property: function property_directive(data) { + +// Produce the /*property*/ directive. + + let not_first = false; + let output = ["/*property"]; + let length = 1111; + let properties = Object.keys(data.property); + + if (properties.length > 0) { + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length >= 80) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); + } + } +}); diff --git a/branch.debug/README b/branch.debug/README new file mode 100644 index 000000000..f46e91069 --- /dev/null +++ b/branch.debug/README @@ -0,0 +1,42 @@ +JSLint, The JavaScript Code Quality Tool + +Douglas Crockford +douglas@crockford.com + +2018-05-28 + +jslint.js contains the jslint function. It parses and analyzes a source file, +returning an object with information about the file. It can also take an object +that sets options. + +index.html runs the jslint.js function in a web page. The page also depends +browser.js and report.js and jslint.css. + +jslint.css provides styling for index.html. + +browser.js runs the web user interface. + +report.js generates the results reports in HTML. + +help.html describes JSLint's usage. Please read it. + +function.html describes the jslint function and the results it produces. + +Please direct questions and comments to +https://plus.google.com/communities/104441363299760713736. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[http://www.crockford.com/wrrrld/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. diff --git a/branch.debug/browser.js b/branch.debug/browser.js new file mode 100644 index 000000000..5086fed85 --- /dev/null +++ b/branch.debug/browser.js @@ -0,0 +1,158 @@ +// browser.js +// 2018-06-16 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +/*jslint + browser +*/ + +/*property + checked, create, disable, display, error, focus, forEach, function, + getElementById, innerHTML, join, length, map, onchange, onclick, onscroll, + property, querySelectorAll, scrollTop, select, split, style, title, value +*/ + +import jslint from "./jslint.js"; +import report from "./report.js"; + +// This is the web script companion file for JSLint. It includes code for +// interacting with the browser and displaying the reports. + +let rx_crlf = /\n|\r\n?/; +let rx_separator = /[\s,;'"]+/; + +let fudge_unit = 0; + +const aux = document.getElementById("JSLINT_AUX"); +const boxes = document.querySelectorAll("[type=checkbox]"); +const fudge = document.getElementById("JSLINT_FUDGE"); +const global = document.getElementById("JSLINT_GLOBAL"); +const number = document.getElementById("JSLINT_NUMBER"); +const property = document.getElementById("JSLINT_PROPERTY"); +const property_fieldset = document.getElementById("JSLINT_PROPERTYFIELDSET"); +const report_field = document.getElementById("JSLINT_REPORT"); +const report_list = document.getElementById("JSLINT_REPORT_LIST"); +const source = document.getElementById("JSLINT_SOURCE"); +const select = document.getElementById("JSLINT_SELECT"); +const warnings = document.getElementById("JSLINT_WARNINGS"); +const warnings_list = document.getElementById("JSLINT_WARNINGS_LIST"); + +function show_numbers() { + number.value = source.value.split(rx_crlf).map(function (ignore, index) { + return index + fudge_unit; + }).join("\n"); +} + +function clear() { + aux.style.display = "none"; + number.value = ""; + property.value = ""; + property_fieldset.style.display = "none"; + report_field.style.display = "none"; + report_list.innerHTML = ""; + source.focus(); + source.value = ""; + warnings.style.display = "none"; + warnings_list.innerHTML = ""; +} + +function fudge_change() { + fudge_unit = Number(fudge.checked); + show_numbers(); +} + +function clear_options() { + boxes.forEach(function (node) { + node.checked = false; + }); + fudge_change(); + global.innerHTML = ""; +} + +function call_jslint() { + +// First build the option object. + + let option = Object.create(null); + boxes.forEach(function (node) { + if (node.checked) { + option[node.title] = true; + } + }); + +// Call JSLint with the source text, the options, and the predefined globals. + + let global_string = global.value; + let result = jslint( + source.value, + option, + ( + global_string === "" + ? undefined + : global_string.split(rx_separator) + ) + ); + +// Generate the reports. + + let error_html = report.error(result); + let function_html = report.function(result); + let property_text = report.property(result); + +// Display the reports. + + warnings_list.innerHTML = error_html; + warnings.style.display = ( + error_html.length === 0 + ? "none" + : "block" + ); + + report_list.innerHTML = function_html; + report_field.style.display = "block"; + if (property_text) { + property.value = property_text; + property_fieldset.style.display = "block"; + property.scrollTop = 0; + select.disable = false; + } else { + property_fieldset.style.display = "none"; + select.disable = true; + } + aux.style.display = "block"; + source.select(); +} + +fudge.onchange = fudge_change; + +source.onchange = function (ignore) { + show_numbers(); +}; + +source.onscroll = function () { + let ss = source.scrollTop; + number.scrollTop = ss; + let sn = number.scrollTop; + if (ss > sn) { + show_numbers(); + number.scrollTop = ss; + } +}; + +document.querySelectorAll("[name='JSLint']").forEach(function (node) { + node.onclick = call_jslint; +}); + +document.querySelectorAll("[name='clear']").forEach(function (node) { + node.onclick = clear; +}); + +document.getElementById("JSLINT_SELECT").onclick = function () { + property.focus(); + property.select(); +}; +document.getElementById("JSLINT_CLEAR_OPTIONS").onclick = clear_options; + +fudge_change(); +source.select(); +source.focus(); diff --git a/branch.debug/cli.js b/branch.debug/cli.js new file mode 100755 index 000000000..a220c2547 --- /dev/null +++ b/branch.debug/cli.js @@ -0,0 +1,130 @@ +#!/usr/bin/env node +/* + * cli.js + * simple cli-tool to run jslint.js from command-line + * + * instruction: + * 1. save this file as cli.js + * 2. download and save in same directory, latest jslint.js from + * https://github.com/douglascrockford/JSLint/blob/master/jslint.js + * + * example usage: + * $ node cli.js ./jslint.js // jslint local-file + * $ node cli.js https://code.jquery.com/jquery-1.0.0.js // jslint remote-file + */ + + + +/*jslint node */ +/*property + argv, basename, column, concat, end, error, exit, filter, forEach, fudge, + join, jslint, line, lines, main, match, message, on, option, parse, push, + readFile, replace, request, runInNewContext, slice, trim, trimRight, + warnings +*/ +"use strict"; +let chunk_list; +let file; +let fs; +let fudge; +let local; +let modeNext; +let onNext; +let path; +let url; +let vm; +let warning_text; +onNext = function (error, data) { + if (error) { + console.error(error.message || error); + process.exit(1); + } + modeNext += 1; + switch (modeNext) { + case 1: + // run this program only in cli-mode + if (module !== require.main) { + console.error( + "jslint-cli can only be run from the command-line" + ); + return; + } + // require builtins + fs = require("fs"); + path = require("path"); + url = require("url"); + vm = require("vm"); + // init local namespace + local = {}; + if (!process.argv[2]) { + console.error( + "error: no specified\n" + + "usage: " + path.basename(__filename) + " \n" + + "example: " + path.basename(__filename) + " example.js\n" + + "example: " + path.basename(__filename) + + " https://jslint.com/jslint.js" + ); + process.exit(1); + } + // init jslint + fs.readFile(path.join(__dirname, "jslint.js"), "utf8", onNext); + break; + case 2: + // bug-workaround - nodejs does not widely support es-modules + vm.runInNewContext( + data.replace("export default function", "function"), + local + ); + // read data from file + file = process.argv[2]; + console.error("jslint " + file); + data = file.match(/^(https?):\/\//); + if (!data) { + fs.readFile(file, "utf8", onNext); + return; + } + // read data from http/https url + require(data[1]).request(url.parse(file), function (response) { + chunk_list = []; + response + .on("data", function (chunk) { + chunk_list.push(chunk); + }) + .on("end", function () { + onNext(null, String(Buffer.concat(chunk_list))); + }) + .on("error", onNext); + }).on("error", onNext).end(); + break; + case 3: + // jslint data + data = local.jslint(data + // ignore first-line shebang in nodejs scripts + .replace((/^#!/), "//")); + fudge = data.option.fudge || 0; + // init warning_text + warning_text = ""; + // print warnings to stderrr + data.warnings + .filter(function (warning) { + return warning && warning.message; + }) + // print only first 10 warnings + .slice(0, 10) + .forEach(function (warning, ii) { + warning_text += ( + (" #" + (ii + 1)).slice(-3) + + " \u001b[31m" + warning.message + "\u001b[39m\n" + + " " + String(data.lines[warning.line] || "").trim() + + "\u001b[90m \/\/ line " + + (warning.line + fudge) + + ", col " + (warning.column + fudge) + + "\u001b[39m\n" + ); + }); + console.error(warning_text.trimRight() || "ok"); + break; + } +}; +modeNext = 0; +onNext(); diff --git a/branch.debug/function.html b/branch.debug/function.html new file mode 100644 index 000000000..7d8271246 --- /dev/null +++ b/branch.debug/function.html @@ -0,0 +1,618 @@ + + + + + + + + +JSLint: jslint function + + + +
    JSLint
    + +
    The jslint Function
    +
    +

    JSLint

    +

    JSLint is delivered as a set of files:

    +
    + + + + + + + + + + + + + + +
    FilenameContent
    browser.jsBrowser based UI.
    function.htmlThis page that you are looking at right now.
    help.htmlUsing jslint as a single page JSLint application.
    index.htmlSingle page JSLint application that uses the + js files.
    jslint.jsThe jslint function.
    report.jsThe report object, containing report generator functions for + HTML.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    +
    + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring"(JSLint)"
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    parenttokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    parenttokenThe function in which the variable was declared.variable
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    shebangstringThe first line of text if it started with #!line
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable can be assigned to.variable
    +

    Reports

    +

    The report object contains three functions that all take a + result from the jslint function as input. + report.js has no other dependence on other files.

    + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    +
    + +
    + + + + + +
    + + diff --git a/branch.debug/help.html b/branch.debug/help.html new file mode 100644 index 000000000..94b431214 --- /dev/null +++ b/branch.debug/help.html @@ -0,0 +1,835 @@ + + + + + + + + + +JSLint: Help + + + +
    JSLint
    + +
    Help
    +
    +
    Il semble que la perfection soit atteinte non quand il n’y a + plus rien à ajouter, mais quand il n’y a plus rien à + retrancher.
    +
    + Antoine de Saint-Exupéry +
    +
    + Terre des Hommes (1939) +
    +

    Good Parts

    +

    The Principle of the Good Parts is

    +
    If a feature is sometimes useful and sometimes dangerous and if + there is a better option then always use the better option.
    +

    JSLint is a JavaScript program that looks for problems in + JavaScript programs. It is a code quality tool.

    +

    When C was + a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    +

    As the language matured, the definition of the language was + strengthened to eliminate some insecurities, and compilers got better + at issuing warnings. lint is no longer needed.

    +

    JavaScript is a + young-for-its-age language. It was originally intended to do small tasks in + webpages, tasks for which Java was too heavy and clumsy. But JavaScript is + a surprisingly capable language, and it is now being used in larger + projects. Many of the features that were intended to make the language easy + to use are troublesome when projects become complicated. A lint + for JavaScript is needed: JSLint, a JavaScript syntax checker + and validator.

    +

    JSLint takes a JavaScript source and scans it. If it finds a + problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by the ECMAScript Programming Language + Standard (the strangely named document that governs JavaScript). + JSLint will reject most legal programs. It is a higher + standard.

    +

    JavaScript is a sloppy language, but hidden deep inside there is an elegant, + better language. JSLint helps you to program in that better + language and to avoid most of the slop. JSLint will reject + programs that browsers will accept because JSLint is concerned + with the quality of your code and browsers are not. You should gladly accept + all of JSLint's advice.

    +

    JSLint can operate on JavaScript source + or JSON text.

    +

    ECMAScript Sixth Edition

    +

    Some of + ES6’s features are good, so JSLint will recognize the good + parts of ES6.

    +

    Currently, these features are recognized:

    +
      +
    • The ... ellipsis marker in parameter + lists and argument lists, replacing the arguments object + for variadic functions.
    • +
    • The let statement, which is like the var + statement except that it respects block scope. + You may use let or var but not both.
    • +
    • The const statement is like the let statement + except that it disallows the use of assignment on the variable, although + if the value of the variable is mutable, it can still be mutated. + const is preferred to let. +
    • Destructuring of arrays and objects is allowed in parameter lists and on + the left side of let, and const, but not + var or assignment statements, and not deep destructuring + or eliding.
    • +
    • Enhanced object literals, providing shorter forms for function + declaration and properties that are initialized by variables with the + same name.
    • +
    • The fat arrow => fart functions.
    • +
    • The simplest forms of import and export.
    • +
    • `Megastring` literals, but not nested + `megastring` literals.
    • +
    • New global functions, such as Map, Set, + WeakMap, and WeakSet.
    • +
    • 0b- and 0o- number literals.
    • +
    +

    The most important new feature of ES6 is proper tail calls. This has no + new syntax, so JSLint doesn’t see it. But it makes recursion + much more attractive, which makes loops, particularly for + loops, much less attractive.

    +

    import export

    +

    The ES6 module feature will be an important improvement over JavaScript’s + global variables as a means of linking separate files together. + JSLint recognizes a small but essential subset of the module + syntax.

    +
    +

    import name from stringliteral;

    +

    import {name} from stringliteral;

    +

    export default function () {};

    +

    export default function name() {};

    +

    export default expression;

    +

    export function name() {}

    +

    export name; // where name is const

    +

    export {name};

    +
    +

    Directives

    +

    JSLint provides three directives that may be placed in a + file to manage JSLint’s behavior. Use of these directives is + optional. If they are used, they should be placed in a source file before + the first statement. They are written in the form of a comment, where the + directive name is placed immediately after the opening of the comment before + any whitespace. The three directives are global, + jslint, and property. Directives in a file are + stronger than options selected from the UI or passed with the option object.

    +

    /*global*/

    +

    The /*global*/ directive is used to specify a set of globals + (usually functions and objects containing functions) that are available to + this file. This was commonly used in browsers to link source files together + before ES6 modules appeared. Use of global variables is strongly + discouraged, but unfortunately web browsers require their use. The + /*global*/ directive can only be used when the + Assume a browser option is selected. +

    Each of the names listed will indicate a read-only global variable. The names + are separated by , comma. This directive + should not be used if import or export is used. + This directive inhibits warnings, but it does not declare the names in the + execution environment. +

    For example: +

    /*global +
    ADSAFE, report, jslint
    +*/
    +

    instructs JSLint to not give warnings about the global + variables ADsafe, report, and jslint. + However, if any of those names are expected to be supplied by other files + and those other files fail to do so, then execution errors will result. It + is usually better to use top-level variable declarations instead: +

        var ADSAFE;
    +    var report;
    +    var jslint; 
    +

    Using var in this way allows comparing a global variable to the + undefined value to determine whether it is has been used in the global context.

    +

    /*jslint*/

    +

    The /*jslint*/ directive allows for the control of several + options. These options can also be set from the + JSLint.com user interface.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Tolerate bitwise operatorsbitwisetrue if bitwise operators should be allowed. The + bitwise operators are rarely used in JavaScript programs, so it + is usually more likely that & is a mistyping of + && than that it indicates a bitwise and. + JSLint will give warnings on the bitwise operators + unless this option is selected.
    Assume a browser browsertrue if the standard browser globals should be + predefined. This option will reject the use of + import and export. This option also + disallows the file form of the "use + strict" pragma. It does not supply + self; you will have to request that unnecessary + alias of the dreaded global object yourself. It adds the same + globals as this directive: +
    /*global +
    + clearInterval, clearTimeout, document, event, FileReader, + FormData, history, localStorage, location, name, navigator, + screen, sessionStorage, setInterval, setTimeout, Storage, + URL, window, XMLHttpRequest +
    + */
    +
    Tolerate conversion operatorsconverttrue if !!, + prefix, + and concatenation with "" are allowed. + The Boolean, Number, and + String functions are preferred
    Assume CouchDBcouchtrue if + Couch DB + globals should be predefined. It adds the same globals as this + directive: +
    /*global +
    emit, getRow, isArray, log, provides, registerType, require, + send, start, sum, toJSON
    + */
    Assume in developmentdeveltrue if browser globals that are useful in + development should be predefined, and if debugger + statements and TODO comments + should be allowed. It adds the + same globals as this directive: +
    /*global +
    alert, confirm, console, prompt
    + */
    Be sure to turn this option off before going + into production.
    Tolerate evalevaltrue if eval should be allowed. In the + past, the eval function was the most misused + feature of the language.
    Tolerate for statementfortrue if the for statement should be + allowed. It is almost always better to use the array methods + instead.
    Fudge the line and column numbersfudgetrue if the origin of a text file is line 1 column + 1. Programmers know that number systems start at zero. + Unfortunately, most text editors start numbering lines and + columns at one. JSLint will by default start + numbering at zero. Use this option to start the numbering at one + in its reports.
    Tolerate get and setgetsettrue if accessor properties are allowed in object literals.
    Tolerate long source lineslongtrue if a line can contain more than 80 characters.
    Tolerate multiple variables declarations per statementmultivartrue if a var, let, or + const statement can declare two or more variables + in a single statement.
    Assume Node.jsnodetrue if Node.js globals should be predefined. It + will predefine globals that are used in the Node.js environment. + It adds the same globals as this directive: +
    /*global +
    + Buffer, clearImmediate, clearInterval, clearTimeout, console, exports, + module, process, require, setImmediate, setInterval, setTimeout, URL, + URLSearchParams, __dirname, __filename +
    + */
    Tolerate single quote stringssingletrue if 'single quote + should be allowed to enclose string literals.
    Tolerate this
    thistrue if this should be allowed.
    Tolerate whitespace messwhitetrue if the whitespace rules should be ignored.
    +

    For example:

    +
    /*jslint
    +    bitwise, node
    +*/
    + +

    /*property*/

    +

    The /*property*/ directive is used to declare a list of + property identifiers that are used in the file. Each property name in the + program is looked up in this list. If a name is not found, that indicates an + error, most likely a typing error.

    +

    The list can also be used to evade some of JSLint’s naming + rules.

    +

    JSLint can build the /*property*/ list for you. At + the bottom of its report, JSLint displays a + /*property*/ directive. It contains all of the names that were + used with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. You + can copy the /*property*/ directive to the top of your + script file. JSLint will check the spelling of all property + names against the list. That way, you can have JSLint look for + misspellings for you.

    +

    For example,

    +
    /*property +
    charAt, slice, _$_
    + */
    +

    ignore

    +

    JSLint introduces a new reserved word: ignore. It + is used in parameter lists and in catch clauses to indicate a + parameter that will be ignored. Unused warnings will not be produced for + ignore. +

    function handler(ignore, value) {
    +    return do_something_useful(value);
    +}
    +

    var let const

    +

    JavaScript provides three statements for declaring variables: + var, let, and const. The + var statement suffers from a bad practice called hoisting. The + let statement does not do hoisting and respects block scope. + The const statement is like let except that it + marks the variable (but not its contents) as read only, making it an error + to attempt to assign to the variable. When given a choice, + const is the best, var is the worst.

    +

    JSLint uses the intersection of the var rules and the + let rules, and by doing so avoids the errors related to either. + A name should be declared only once in a function. It should be declared + before it is used. It should not be used outside of the block in which it is + declared. A variable should not have the same name as a variable or + parameter in an outer function. Do not mix var and + let. Declare one name per statement.

    +

    = == ===

    +

    Fortran made a terrible + mistake in using the equality operator as its assignment operator. That + mistake has been replicated in most languages since then. C compounded that + mistake by making == its equality operator. The visual + similarity is a source of errors. JavaScript compounded this further by + making == a type coercing comparison operator that produces + false positive results. This was mitigated by adding the === + operator, leaving the broken == operator in place.

    +

    JSLint attempts to minimize errors by the following rules:

    +

    == is not allowed. This avoids the false positives, and + increases the visual distance between = and ===. + Assignments are not allowed in expression position, and comparisons are not + allowed in statement position. This also reduces confusion. 

    +

    Semicolon

    +

    JavaScript uses a C-like syntax that requires the use of semicolons to + delimit certain statements. JavaScript attempts to make those semicolons + optional with an automatic semicolon insertion mechanism, but it does not + work very well. Automatic semicolon insertion was added to make + things easier for beginners. Unfortunately, it sometimes fails. Do not rely + on it unless you are a beginner.

    +

    JSLint expects that every statement will be followed by + ; except for for, function, + if, switch, try, and + while. JSLint does not expect to see unnecessary + semicolons, the empty statement, or empty blocks.

    +

    function =>

    +

    JavaScript has four syntactic forms for making function objects: function + statements, function expressions, enhanced object literals, and the + => fart operator.

    +

    The function statement creates a variable and assigns the function object to + it. It should be used in a file or function body, but not inside of a block.

    +
    function name(parameters) { +
    statements
    + }
    +

    The function expression unfortunately looks like the function statement. It + may appear anywhere that an expression may appear, but not in statement + position and not in a loop. It produces a function object but does not + create a variable in which to store it.

    +
    function (parameters) { +
    statements
    + }
    +

    The enhanced object literal provides an ES6 shorthand for creating a property + whose value is a function, saving you from having to type + : colon and function.

    +
    { +
    name(parameters) { +
    statements
    + }
    + }
    +

    Finally, ES6 provides an even shorter form of function expression that leaves + out the words function and return:

    +
    (parameters) => + expression
    +

    JSLint requires the parens around the parameters, and forbids a + { left brace after the + => fart to avoid syntactic ambiguity.

    +

    Comma

    +

    The , comma operator is unnecessary and can + mask programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as + an operator. It does not expect to see elided elements in array literals. A + comma should not appear after the last element of an array literal or object + literal.

    +

    Blocks

    +

    JSLint expects blocks with function, + if, switch, while, for, + do, and try statements and nowhere else.

    +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    +

    JavaScript allows an if to be written like this:

    +
    if (condition)
    +    statement;
    +

    That form is known to contribute to mistakes in projects where many + programmers are working on the same code. That is why JSLint + expects the use of a block:

    +
    if (condition) {
    +    statements;
    +}
    +

    Experience shows that this form is more resilient.

    +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call. All other expression statements are considered + to be errors.

    +

    for

    +

    JSLint does not recommend use of the for statement. + Use array methods like forEach instead. The for + option will suppress some warnings. The forms of for that + JSLint accepts are restricted, excluding the new ES6 forms.

    +

    for in

    +

    JSLint does not recommend use of the for + in statement. Use Object.keys instead.

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, it also + loops through all of the properties that were inherited through the + prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written + without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    for (name in object) {
    +    if (object.hasOwnProperty(name)) {
    +        ....
    +    }
    +}
    +

    Note that the above code will fail if the object contains a property + named hasOwnProperty. Use Object.keys instead.

    +

    switch

    +

    A common error in switch statements is to forget to place a + break statement after each case, resulting in unintended + fall-through. JSLint expects that the statement before the next + case or default is one of these: + break, return, or throw.

    +

    with

    +

    The with statement was intended to provide a shorthand in + accessing properties in deeply nested objects. Unfortunately, it behaves + very badly when setting new properties. Never use the with + statement.

    +

    JSLint does not expect to see a with statement.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that + labels will be distinct from vars and parameters.

    +

    Unreachable Code

    +

    JSLint expects that a return, break, + or throw statement will be followed by a + } right brace or case or + default.

    +

    Confusing Pluses and Minuses

    +

    JSLint expects that + will not be followed by + + or ++, and that - will not be + followed by - or --. A misplaced space can turn + + + into ++, an error that is difficult to see. + Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ increment and + -- decrement operators have been known to + contribute to bad code by encouraging excessive trickiness. They are second + only to faulty architecture in enabling to viruses and other security + menaces. Also, preincrement/postincrement confusion can produce off-by-one + errors that are extremely difficult to diagnose. Fortunately, they are also + complete unnecessary. There are better ways to add 1 to a variable.

    +

    It is best to avoid these operators entirely and rely on += and + -= instead.

    +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. + JSLint looks for problems that may cause portability problems. + It also attempts to resolve visual ambiguities by recommending explicit + escapement.

    +

    JavaScript’s syntax for regular expression literals overloads the + / slash character. To avoid ambiguity, + JSLint expects that the character preceding a regular + expression literal is a ( left paren or + = equal or + : colon or + , comma character.

    +

    this

    +
    Having this in the language makes it harder to talk + about the language. It is like pair programming with Abbott and Costello. +
    +

    Avoid using this. Warnings about this can be + suppressed with option.this.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the + new prefix. The new prefix creates a new object + based on the function's prototype, and binds that object to the + function's implied this parameter. If you neglect to use the + new prefix, no new object will be made and this + will be bound to the global object.

    +

    JSLint enforces the convention that constructor functions be + given names with initial uppercase. JSLint does not expect to + see a function invocation with an initial uppercase name unless it has the + new prefix. JSLint does not expect to see the + new prefix used with functions whose names do not start with + initial uppercase.

    +

    JSLint does not expect to see the wrapper forms + new Number, new String, new Boolean.

    +

    JSLint does not expect to see new Object. + Use Object.create(null) instead.

    +

    Whitespace

    +

    JSLint has a specific set of rules about the use of whitespace. + Where possible, these rules are consistent with centuries of good practice + with literary style.

    +

    The indentation increases by 4 spaces when the last token on a line is + { left brace, + [ left bracket, + ( left paren. The matching closing + token will be the first token on a line, restoring the previous + indentation.

    +

    The ternary operator can be visually confusing, so + ? question mark and + : colon always begin a line and increase + the indentation by 4 spaces.

    +
    return (
    +    (the_token.id === "(string)" || the_token.id === "(number)")
    +    ? String(the_token.value)
    +    : the_token.id
    +);
    +

    The word function is always followed with one space.

    +

    Clauses (case, catch, default, + else, finally) are not statements and so should + not be indented like statements.

    +

    Spaces are used to make things that are not invocations look less like + invocations.

    +

    Tabs and spaces should not be mixed. We should pick just one in order to + avoid the problems that come from having both. Personal preference is an + extremely unreliable criterion. Neither offers a powerful advantage over the + other. Fifty years ago, tab had the advantage of consuming less memory, but + Moore's Law has eliminated that advantage. Space has one clear advantage + over tab: there is no reliable standard for how many spaces a tab + represents, but it is universally accepted that a space occupies a space. So + use spaces. You can edit with tabs if you must, but make sure it is spaces + again before you commit. Maybe someday we will finally get a universal + standard for tabs, but until that day comes, the better choice is spaces.

    +

    Unsafe Characters

    +

    There are characters that are handled inconsistently in some browsers, and + so must be escaped when placed in strings.

    +
    \u0000-\u001f
    +\u007f-\u009f
    +\u00ad
    +\u0600-\u0604
    +\u070f
    +\u17b4
    +\u17b5
    +\u200c-\u200f
    +\u2028-\u202f
    +\u2060-\u206f
    +\ufeff
    +\ufff0-\uffff
    +

    Report

    +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    +
      +
    • The line number on which it starts.
    • +
    • The function’s name. In the case of anonymous functions, + JSLint will attempt to + «guess» the name.
    • +
    • The parameters.
    • +
    • Variables: The variables that are declared in the function.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Exceptions: The variables that are declared by catch + clauses of try statements.
    • +
    • Outer: Variables used by this function that are declared in + other functions.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements. Updates are announced at + https://plus.google.com/communities/104441363299760713736.

    +

    Try it

    +

    Try it. Paste your script + into the window and click the + + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    JSLint is written entirely in JavaScript, so it can run + anywhere that JavaScript (or even Java) can run.

    +

    Perfectly fine

    +

    JSLint was designed to reject code that some would consider to + be perfectly fine. The reason for this is that JSLint's + purpose is to help produce programs that are free of error. That is + difficult in any language and is especially hard in JavaScript. + JSLint attempts to help you increase the visual distance + between correct programs and incorrect programs, making the remaining errors + more obvious. JSLint will give warnings about things that are + not necessarily wrong in the current situation, but which have been observed + to mask or obscure errors. Avoid those things when there are better options + available.

    +

    It is dangerous out there. JSLint is here to help.

    +

    Warning

    +

    JSLint will hurt your feelings. Side effects may include + headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, + dry mouth, cleaner code, and a reduced error rate.

    +

    Further Reading

    + +
    +

    + Code Conventions for the JavaScript Programming Language.

    + + + + + +
    + + diff --git a/branch.debug/index.html b/branch.debug/index.html new file mode 100644 index 000000000..235f44b54 --- /dev/null +++ b/branch.debug/index.html @@ -0,0 +1,125 @@ + + + + + + + + + + + +JSLint: The JavaScript Code Quality Tool + + +
    +
    Source
    +
    + + +
    +
    + Warnings +
    +
    +
    + Function Report +
    +
    +
    + Property Directive + +
    +
    + + + + +
    +
    Options +
    Assume... +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Global variables... + +
    +
    +
    +
    + + + + + +
    + + diff --git a/branch.debug/jslint.css b/branch.debug/jslint.css new file mode 100644 index 000000000..9c1b1a2f9 --- /dev/null +++ b/branch.debug/jslint.css @@ -0,0 +1,340 @@ +@font-face { + font-family: 'Programma'; + src: url('Programma.woff') format('woff'); +} +@font-face { + font-family: 'Daley'; + font-weight: bold; + src: url('Daley-Bold.woff') format('woff'); +} + +body { + background-color: antiquewhite; + color: black; + font-family: 'Daley', sans-serif; + margin: 0; + padding: 0; +} + +#JSLINT_TITLEBOX { + font-family: 'Daley', sans-serif; + margin: 0; + margin-left: 3%; + margin-right: 3%; + padding: 0; +} + +#JSLINT_TITLEBOX ul { + float: right; + font-size: 12pt; +} + +#JSLINT_TITLEBOX div { + color: darkslategray; + float: left; + font-size: 48pt; +} + +#JSLINT_ fieldset { + background-color: gainsboro; + border: 0; + clear: both; + margin-bottom: 1em; + margin-left: 3%; + margin-right: 3%; + margin-top: 1em; + padding: 0; + width: auto; +} +#JSLINT_ legend { + background-color: darkslategray; + border: 0; + color: white; + font-size: 100%; + font-style: normal; + font-weight: normal; + margin: 0; + padding-bottom: 0.25em; + padding-left: 0; + padding-right: 0; + padding-top: 0.25em; + text-align: center; + width: 100%; +} +#JSLINT_ button { + background-color: darkslategray; + border: 0; + color: white; + cursor: pointer; + font-family: 'Daley', sans-serif; + font-size: 100%; + font-style: normal; + margin-left: 1em; + margin-right: 1em; + padding-left: 1em; + padding-right: 1em; + padding-bottom: 0.25em; + padding-top: 0.25em; + text-align: center; +} +#JSLINT_ button:hover { + background-color: slategray; + color: white; + border: 0; +} + +#JSLINT_ button:active { + border: 0; + color: black; +} + +#JSLINT_ button:disabled { + background-color: gray; + border: 0; + color: gainsboro; +} + +a:visited { + color: #69565c; +} + +a:link { + color: black; +} + +#JSLINT_ input[type="text"] { + background-color: white; + border: 0; + color: black; + margin-bottom: 0.2em; + margin-right: 0.5em; + padding-left: 0.5em; + padding-right: 0.5em; + text-align: right; + width: 3em; +} + +#JSLINT_ textarea { + border: 0; + background-color: white; + border: 0; + font-family: Programma, monospace; + font-size: 100%; + font-weight: bold; + resize: none; +} + +#JSLINT_ textarea::selection { + background-color: yellow; + color: black; +} + +#JSLINT_NUMBER { + color: darkgray; + display: inline-block; + height: 4in; + margin: 2%; + margin-right: 0; + overflow: hidden; + padding-right: 0.5%; + text-align: right; + white-space: pre; + width: 4%; +} + +#JSLINT_SOURCE { + color: black; + display: inline-block; + font-size: 100%; + height: 4in; + margin: 2%; + margin-left: 0; + margin-right: 0; + overflow: auto; + padding-left: 0.5%; + white-space: pre; + width: 91%; +} + +#JSLINT_PROPERTY { + background-color: honeydew; + border: 0; + margin: 2%; + padding: 0.5%; + white-space: pre; + width: 95%; +} + +#JSLINT_GLOBAL { + white-space: normal; + width: 95%; +} +#JSLINT_ label { + font-family: sans-serif; + font-size: 90%; + padding-left: 0.25em; +} +#JSLINT_OPTIONS>div { + float: left; + margin: 0.5em; +} + +#JSLINT_ address { + color: black; + display: block; + float: right; + font-family: serif; + font-size: 90%; + margin-left: 1em; +} +#JSLINT_ dl { + background-color: cornsilk; + color: white; + margin: 0; + padding-bottom: 2pt; + padding-left: 1em; + padding-right: 1em; + padding-top: 2pt; +} +#JSLINT_ dfn { + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + margin-bottom: 2pt; +} +#JSLINT_ dt { + color: black; + display: block; + float: left; + font-family: serif; + font-size: 75%; + font-style: italic; + margin: 0; + width: 8em; + text-align: right; +} +#JSLINT_ dd { + color: black; + display: block; + font-family: Programma, monospace; + font-weight: bold; + margin-left: 8em; + padding-bottom: 2pt; +} +#JSLINT_WARNINGS>legend { + background-color: indianred; +} +#JSLINT_WARNINGS>div { + background-color: pink; + padding: 1em; +} +#JSLINT_WARNINGS cite { + color: black; + display: block; + font-family: serif; + font-size: 100%; + font-style: normal; + margin-bottom: 4pt; + margin-left: 20pt; + margin-right: 20pt; + margin-top: 4pt; + overflow-x: hidden; +} +#JSLINT_WARNINGS samp { + background-color: lavenderblush; + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + padding: 4pt; + margin-bottom: 0; + margin-left: 16pt; + margin-right: 16pt; + margin-top: 0; + white-space: pre-wrap; +} + +#JSLINT_REPORT center { + margin-top: 1em; +} + +#JSLINT_WARNINGS dl address { + color: black; + display: inline; + float: none; + font-size: 80%; + margin: 0; +} + +#JSLINT_REPORT>div { + padding: 1em; +} + +#JSLINT_ dl.level0 { + color: black; + background-color: white; +} + +#JSLINT_ dl.level1 { + color: black; + background-color: #ffffe0; /* yellow */ + margin-left: 1em; +} + +#JSLINT_ dl.level2 { + color: black; + background-color: #e0ffe0; /* green */ + margin-left: 2em; +} + +#JSLINT_ dl.level3 { + color: black; + background-color: #e0e0ff; /* blue */ + margin-left: 3em; +} + +#JSLINT_ dl.level4 { + background-color: #ffe0ff; /* purple */ + color: black; + margin-left: 4em; +} + +#JSLINT_ dl.level5 { + background-color: #ffe0e0; /* red */ + color: black; + margin-left: 5em; +} + +#JSLINT_ dl.level6 { + background-color: #ffe390; /* orange */ + color: black; + margin-left: 6em; +} + +#JSLINT_ dl.level7 { + background-color: #e0e0e0; /* gray */ + color: black; + margin-left: 7em; +} + +#JSLINT_ dl.level8 { + color: black; + margin-left: 8em; +} + +#JSLINT_ dl.level9 { + color: black; + margin-left: 9em; +} + +.none { + display: none; +} +.center { + text-align: center; +} diff --git a/branch.debug/jslint.js b/branch.debug/jslint.js new file mode 100644 index 000000000..9eebc6596 --- /dev/null +++ b/branch.debug/jslint.js @@ -0,0 +1,4924 @@ +// jslint.js +// 2018-09-26 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// jslint(source, option_object, global_array) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze, a string or an array of strings. +// option_object An object whose keys correspond to option names. +// global_array An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all of the functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// 1. If the source is a single string, split it into an array of strings. +// 2. Turn the source into an array of tokens. +// 3. Furcate the tokens into a parse tree. +// 4. Walk the tree, traversing all of the nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// 5. Check the whitespace between the tokens. + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*property + a, and, arity, assign, b, bad_assignment_a, bad_directive_a, bad_get, + bad_module_name_a, bad_option_a, bad_property_a, bad_set, bitwise, block, + body, browser, c, calls, catch, charCodeAt, closer, closure, code, column, + complex, concat, constant, context, convert, couch, create, d, dead, + default, devel, directive, directives, disrupt, dot, duplicate_a, edition, + ellipsis, else, empty_block, escape_mega, eval, every, expected_a, + expected_a_at_b_c, expected_a_b, expected_a_b_from_c_d, expected_a_before_b, + expected_a_next_at_b, expected_digits_after_a, expected_four_digits, + expected_identifier_a, expected_line_break_a_b, expected_regexp_factor_a, + expected_space_a_b, expected_statements_a, expected_string_a, + expected_type_string_a, exports, expression, extra, finally, flag, for, + forEach, free, from, froms, fud, fudge, function_in_loop, functions, g, + getset, global, i, id, identifier, import, inc, indexOf, infix_in, init, + initial, isArray, isNaN, join, json, keys, label, label_a, lbp, led, length, + level, line, lines, live, long, loop, m, margin, match, message, + misplaced_a, misplaced_directive_a, missing_browser, missing_m, module, + multivar, naked_block, name, names, nested_comment, new, node, not_label_a, + nr, nud, number_isNaN, ok, open, option, out_of_scope_a, parameters, parent, + pop, property, push, quote, redefinition_a_b, replace, + required_a_optional_b, reserved_a, right, role, search, shebang, signature, + single, slice, some, sort, split, startsWith, statement, stop, strict, + subscript_a, switch, test, this, thru, toString, todo_comment, tokens, + too_long, too_many_digits, tree, try, type, u, unclosed_comment, + unclosed_mega, unclosed_string, undeclared_a, unexpected_a, + unexpected_a_after_b, unexpected_a_before_b, unexpected_at_top_level_a, + unexpected_char_a, unexpected_comment, unexpected_directive_a, + unexpected_expression_a, unexpected_label_a, unexpected_parens, + unexpected_space_a_b, unexpected_statement_a, unexpected_trailing_space, + unexpected_typeof_a, uninitialized_a, unreachable_a, + unregistered_property_a, unsafe, unused_a, use_double, use_open, use_spaces, + use_strict, used, value, var_loop, var_switch, variable, warning, warnings, + weird_condition_a, weird_expression_a, weird_loop, weird_relation_a, white, + wrap_assignment, wrap_condition, wrap_immediate, wrap_parameter, + wrap_regexp, wrap_unary, wrapped, writable, y +*/ + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than {} because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +function populate(array, object = empty(), value = true) { + +// Augment an object by taking property names from an array of strings. + + array.forEach(function (name) { + object[name] = value; + }); + return object; +} + +const allowed_option = { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + bitwise: true, + browser: [ + "caches", "clearInterval", "clearTimeout", "document", "DOMException", + "Element", "Event", "event", "FileReader", "FormData", "history", + "localStorage", "location", "MutationObserver", "name", "navigator", + "screen", "sessionStorage", "setInterval", "setTimeout", "Storage", + "URL", "window", "Worker", "XMLHttpRequest" + ], + couch: [ + "emit", "getRow", "isArray", "log", "provides", "registerType", + "require", "send", "start", "sum", "toJSON" + ], + convert: true, + devel: [ + "alert", "confirm", "console", "prompt" + ], + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + multivar: true, + node: [ + "Buffer", "clearImmediate", "clearInterval", "clearTimeout", + "console", "exports", "module", "process", "require", + "setImmediate", "setInterval", "setTimeout", "URL", + "URLSearchParams", "__dirname", "__filename" + ], + single: true, + this: true, + white: true +}; + +const anticondition = populate([ + "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%", + "typeof", "(number)", "(string)" +]); + +// These are the bitwise operators. + +const bitwiseop = populate([ + "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=", + ">>>", ">>>=" +]); + +const escapeable = populate([ + "\\", "/", "`", "b", "f", "n", "r", "t" +]); + +const opener = { + +// The open and close pairs. + + "(": ")", // paren + "[": "]", // bracket + "{": "}", // brace + "${": "}" // mega +}; + +// The relational operators. + +const relationop = populate([ + "!=", "!==", "==", "===", "<", "<=", ">", ">=" +]); + +// This is the set of infix operators that require a space on each side. + +const spaceop = populate([ + "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/", + "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=", + ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" +]); + +const standard = [ + +// These are the globals that are provided by the language standard. + + "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI", + "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error", + "EvalError", "Float32Array", "Float64Array", "Generator", + "GeneratorFunction", "Int8Array", "Int16Array", "Int32Array", "Intl", + "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat", + "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", + "Set", "String", "Symbol", "SyntaxError", "System", "TypeError", + "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", + "URIError", "WeakMap", "WeakSet" +]; + +const bundle = { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + and: "The '&&' subexpression should be wrapped in parens.", + bad_assignment_a: "Bad assignment to '{a}'.", + bad_directive_a: "Bad directive '{a}'.", + bad_get: "A get function takes no parameters.", + bad_module_name_a: "Bad module name '{a}'.", + bad_option_a: "Bad option '{a}'.", + bad_property_a: "Bad property name '{a}'.", + bad_set: "A set function takes one parameter.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + escape_mega: "Unexpected escapement in mega literal.", + expected_a: "Expected '{a}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: ( + "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'." + ), + expected_a_before_b: "Expected '{a}' before '{b}'.", + expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.", + expected_digits_after_a: "Expected digits after '{a}'.", + expected_four_digits: "Expected four digits after '\\u'.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.", + expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.", + expected_space_a_b: "Expected one space between '{a}' and '{b}'.", + expected_statements_a: "Expected statements before '{a}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + expected_type_string_a: "Expected a type string and instead saw '{a}'.", + function_in_loop: "Don't make functions within a loop.", + infix_in: ( + "Unexpected 'in'. Compare with undefined, " + + "or use the hasOwnProperty method instead." + ), + label_a: "'{a}' is a statement label.", + misplaced_a: "Place '{a}' at the outermost level.", + misplaced_directive_a: ( + "Place the '/*{a}*/' directive before the first statement." + ), + missing_browser: "/*global*/ requires the Assume a browser option.", + missing_m: "Expected 'm' flag on a multiline regular expression.", + naked_block: "Naked block.", + nested_comment: "Nested comment.", + not_label_a: "'{a}' is not a label.", + number_isNaN: "Use Number.isNaN function to compare with NaN.", + out_of_scope_a: "'{a}' is out of scope.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + required_a_optional_b: ( + "Required parameter '{a}' after optional parameter '{b}'." + ), + reserved_a: "Reserved name '{a}'.", + subscript_a: "['{a}'] is better written in dot notation.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line is longer than 80 characters.", + too_many_digits: "Too many digits.", + unclosed_comment: "Unclosed comment.", + unclosed_mega: "Unclosed mega literal.", + unclosed_string: "Unclosed string.", + undeclared_a: "Undeclared '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_a_after_b: "Unexpected '{a}' after '{b}'.", + unexpected_a_before_b: "Unexpected '{a}' before '{b}'.", + unexpected_at_top_level_a: "Expected '{a}' to be in a function.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_directive_a: "When using modules, don't use directive '/*{a}'.", + unexpected_expression_a: ( + "Unexpected expression '{a}' in statement position." + ), + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_parens: "Don't wrap function literals in parens.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_statement_a: ( + "Unexpected statement '{a}' in expression position." + ), + unexpected_trailing_space: "Unexpected trailing space.", + unexpected_typeof_a: ( + "Unexpected 'typeof'. Use '===' to compare directly with {a}." + ), + uninitialized_a: "Uninitialized '{a}'.", + unreachable_a: "Unreachable '{a}'.", + unregistered_property_a: "Unregistered property name '{a}'.", + unsafe: "Unsafe character '{a}'.", + unused_a: "Unused '{a}'.", + use_double: "Use double quotes, not single quotes.", + use_open: ( + "Wrap a ternary expression in parens, " + + "with a line break after the left paren." + ), + use_spaces: "Use spaces, not tabs.", + use_strict: "This function needs a \"use strict\" pragma.", + var_loop: "Don't declare variables in a loop.", + var_switch: "Don't declare variables in a switch.", + weird_condition_a: "Weird condition '{a}'.", + weird_expression_a: "Weird expression '{a}'.", + weird_loop: "Weird loop.", + weird_relation_a: "Weird relation '{a}'.", + wrap_assignment: "Don't wrap assignment statements in parens.", + wrap_condition: "Wrap the condition in parens.", + wrap_immediate: ( + "Wrap an immediate function invocation in parentheses to assist " + + "the reader in understanding that the expression is the result " + + "of a function, and not the function itself." + ), + wrap_parameter: "Wrap the parameter in parens.", + wrap_regexp: "Wrap this regexp in parens to avoid confusion.", + wrap_unary: "Wrap the unary expression in parens." +}; + +// Regular expression literals: + +// supplant {variables} +const rx_supplant = /\{([^{}]*)\}/g; +// carriage return, carriage return linefeed, or linefeed +const rx_crlf = /\n|\r\n?/; +// unsafe characters that are silently deleted by one or more browsers +const rx_unsafe = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; +// identifier +const rx_identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; +const rx_module = /^[a-zA-Z0-9_$:.@\-\/]+$/; +const rx_bad_property = /^_|\$|Sync\$|_$/; +// star slash +const rx_star_slash = /\*\//; +// slash star +const rx_slash_star = /\/\*/; +// slash star or ending slash +const rx_slash_star_or_slash = /\/\*|\/$/; +// uncompleted work comment +const rx_todo = /\b(?:todo|TO\s?DO|HACK)\b/; +// tab +const rx_tab = /\t/g; +// directive +const rx_directive = /^(jslint|property|global)\s+(.*)$/; +const rx_directive_part = /^([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*(.*)$/; +// token (sorry it is so long) +const rx_token = /^((\s+)|([a-zA-Z_$][a-zA-Z0-9_$]*)|[(){}\[\]?,:;'"~`]|=(?:==?|>)?|\.+|[*\/][*\/=]?|\+[=+]?|-[=\-]?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<= "a" && string <= "z\uffff") + || (string >= "A" && string <= "Z\uffff") + ); +} + +function supplant(string, object) { + return string.replace(rx_supplant, function (found, filling) { + const replacement = object[filling]; + return ( + replacement !== undefined + ? replacement + : found + ); + }); +} + +let anon; // The guessed name for anonymous functions. +let blockage; // The current block. +let block_stack; // The stack of blocks. +let declared_globals; // The object containing the global declarations. +let directives; // The directive comments. +let directive_mode; // true if directives are still allowed. +let early_stop; // true if JSLint cannot finish. +let exports; // The exported names and values. +let froms; // The array collecting all import-from strings. +let fudge; // true if the natural numbers start with 1. +let functionage; // The current function. +let functions; // The array containing all of the functions. +let global; // The global object; the outermost context. +let json_mode; // true if parsing JSON. +let lines; // The array containing source lines. +let module_mode; // true if import or export was used. +let next_token; // The next token to be examined in the parse. +let option; // The options parameter. +let property; // The object containing the tallied property names. +let mega_mode; // true if currently parsing a megastring literal. +let shebang; // true if a #! was seen on the first line. +let stack; // The stack of functions. +let syntax; // The object containing the parser. +let token; // The current token being examined in the parse. +let token_nr; // The number of the next token. +let tokens; // The array of tokens. +let tenure; // The predefined property registry. +let tree; // The abstract parse tree. +let var_mode; // "var" if using var; "let" if using let. +let warnings; // The array collecting all generated warnings. + +// Error reportage functions: + +function artifact(the_token) { + +// Return a string representing an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); +} + +function artifact_line(the_token) { + +// Return the fudged line number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.line + fudge; +} + +function artifact_column(the_token) { + +// Return the fudged column number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.from + fudge; +} + +function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + const warning = { // ~~ + name: "JSLintError", + column: column, + line: line, + code: code + }; + if (a !== undefined) { + warning.a = a; + } + if (b !== undefined) { + warning.b = b; + } + if (c !== undefined) { + warning.c = c; + } + if (d !== undefined) { + warning.d = d; + } + warning.message = supplant(bundle[code] || code, warning); console.error(JSON.stringify(warning, null, 4)); console.error(new Error().stack); + warnings.push(warning); + return warning; +} + +function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); +} + +function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + if (the_token.warning === undefined) { + the_token.warning = warn_at( + code, + the_token.line, + the_token.from, + a || artifact(the_token), + b, + c, + d + ); + return the_token.warning; + } +} + +function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); +} + +// Tokenize: + +function tokenize(source) { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + +// If the source is not an array, then it is split into lines at the +// carriage return/linefeed. + + lines = ( + Array.isArray(source) + ? source + : source.split(rx_crlf) + ); + tokens = []; + + let char; // a popular character + let column = 0; // the column number of the next character + let first; // the first token + let from; // the starting column number of the token + let line = -1; // the line number of the next character + let nr = 0; // the next token number + let previous = global; // the previous token including comments + let prior = global; // the previous token excluding comments + let mega_from; // the starting column of megastring + let mega_line; // the starting line of megastring + let regexp_seen; // regular expression literal seen on this line + let snippet; // a piece of string + let source_line = ""; // the remaining line source string + let whole_line = ""; // the whole line source string + + if (lines[0].startsWith("#!")) { + line = 0; + shebang = true; + } + + function next_line() { + +// Put the next line of source in source_line. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + let at; + if ( + !option.long + && whole_line.length > 80 + && !json_mode + && first + && !regexp_seen + ) { + warn_at("too_long", line, 80); + } + column = 0; + line += 1; + regexp_seen = false; + whole_line = lines[line]; + source_line = whole_line; + if (source_line !== undefined) { + at = source_line.search(rx_tab); + if (at >= 0) { + if (!option.white) { + warn_at("use_spaces", line, at + 1); + } + source_line = source_line.replace(rx_tab, " "); + } + at = source_line.search(rx_unsafe); + if (at >= 0) { + warn_at( + "unsafe", + line, + column + at, + "U+" + source_line.charCodeAt(at).toString(16) + ); + } + if (!option.white && source_line.slice(-1) === " ") { + warn_at( + "unexpected_trailing_space", + line, + source_line.length - 1 + ); + } + } + return source_line; + } + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions snip, next_char, prev_char, +// some_digits, and escape help in the parsing of literals. + + function snip() { + +// Remove the last character from snippet. + + snippet = snippet.slice(0, -1); + } + + function next_char(match) { + +// Get the next character from the source line. Remove it from the source_line, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + return stop_at( + ( + char === "" + ? "expected_a" + : "expected_a_b" + ), + line, + column - 1, + match, + char + ); + } + if (source_line) { + char = source_line[0]; + source_line = source_line.slice(1); + snippet += char; + } else { + char = ""; + snippet += " "; + } + column += 1; + return char; + } + + function back_char() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the source_line. + + if (snippet) { + char = snippet.slice(-1); + source_line = char + source_line; + column -= 1; + snip(); + } else { + char = ""; + } + return char; + } + + function some_digits(rx, quiet) { + const result = source_line.match(rx); + if (result) { + char = result[1]; + column += char.length; + source_line = result[2]; + snippet += char; + } else { + char = ""; + if (!quiet) { + warn_at( + "expected_digits_after_a", + line, + column, + snippet + ); + } + } + return char.length; + } + + function escape(extra) { + next_char("\\"); + if (escapeable[char] === true) { + return next_char(); + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "u") { + if (next_char("u") === "{") { + if (json_mode) { + warn_at("unexpected_a", line, column - 1, char); + } + if (some_digits(rx_hexs) > 5) { + warn_at("too_many_digits", line, column - 1); + } + if (next_char() !== "}") { + stop_at("expected_a_before_b", line, column, "}", char); + } + return next_char(); + } + back_char(); + if (some_digits(rx_hexs, true) < 4) { + warn_at("expected_four_digits", line, column - 1); + } + return; + } + if (extra && extra.indexOf(char) >= 0) { + return next_char(); + } + warn_at("unexpected_a_before_b", line, column - 2, "\\", char); + } + + function make(id, value, identifier) { + +// Make the token object and append it to the tokens list. + + const the_token = { + from: from, + id: id, + identifier: Boolean(identifier), + line: line, + nr: nr, + thru: column + }; + tokens[nr] = the_token; + nr += 1; + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + directive_mode = false; + } + +// If the token is to have a value, give it one. + + if (value !== undefined) { + the_token.value = value; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option.white. + + if ( + previous.line === line + && previous.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (previous.id === "(comment)" || previous.id === "(regexp)") + ) { + warn( + "expected_space_a_b", + the_token, + artifact(previous), + artifact(the_token) + ); + } + if (previous.id === "." && id === "(number)") { + warn("expected_a_before_b", previous, "0", "."); + } + if (prior.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// The previous token is used to detect adjacency problems. + + previous = the_token; + +// The prior token is a previous token that was not a comment. The prior token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (previous.id !== "(comment)") { + prior = previous; + } + return the_token; + } + + function parse_directive(the_comment, body) { + +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + + const result = body.match(rx_directive_part); + if (result) { + let allowed; + const name = result[1]; + const value = result[2]; + if (the_comment.directive === "jslint") { + allowed = allowed_option[name]; + if ( + typeof allowed === "boolean" + || typeof allowed === "object" + ) { + if ( + value === "" + || value === "true" + || value === undefined + ) { + option[name] = true; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } else if (value === "false") { + option[name] = false; + } else { + warn("bad_option_a", the_comment, name + ":" + value); + } + } else { + warn("bad_option_a", the_comment, name); + } + } else if (the_comment.directive === "property") { + if (tenure === undefined) { + tenure = empty(); + } + tenure[name] = true; + } else if (the_comment.directive === "global") { + if (value) { + warn("bad_option_a", the_comment, name + ":" + value); + } + declared_globals[name] = false; + module_mode = the_comment; + } + return parse_directive(the_comment, result[3]); + } + if (body) { + return stop("bad_directive_a", the_comment, body); + } + } + + function comment(snippet) { + +// Make a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + + const the_comment = make("(comment)", snippet); + if (Array.isArray(snippet)) { + snippet = snippet.join(" "); + } + if (!option.devel && rx_todo.test(snippet)) { + warn("todo_comment", the_comment); + } + const result = snippet.match(rx_directive); + if (result) { + if (!directive_mode) { + warn_at("misplaced_directive_a", line, from, result[1]); + } else { + the_comment.directive = result[1]; + parse_directive(the_comment, result[2]); + } + directives.push(the_comment); + } + return the_comment; + } + + function regexp() { + +// Parse a regular expression literal. + + let multi_mode = false; + let result; + let value; + regexp_seen = true; + + function quantifier() { + +// Match an optional quantifier. + + if (char === "?" || char === "*" || char === "+") { + next_char(); + } else if (char === "{") { + if (some_digits(rx_digits, true) === 0) { + warn_at("expected_a", line, column, "0"); + } + if (next_char() === ",") { + some_digits(rx_digits, true); + next_char(); + } + next_char("}"); + } else { + return; + } + if (char === "?") { + next_char("?"); + } + } + + function subklass() { + +// Match a character in a character class. + + if (char === "\\") { + escape("BbDdSsWw-[]^"); + return true; + } + if ( + char === "" + || char === "[" + || char === "]" + || char === "/" + || char === "^" + || char === "-" + ) { + return false; + } + if (char === " ") { + warn_at("expected_a_b", line, column, "\\u0020", " "); + } else if (char === "`" && mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char(); + return true; + } + + function ranges() { + +// Match a range of subclasses. + + if (subklass()) { + if (char === "-") { + next_char("-"); + if (!subklass()) { + return stop_at( + "unexpected_a", + line, + column - 1, + "-" + ); + } + } + return ranges(); + } + } + + function klass() { + +// Match a class. + + next_char("["); + if (char === "^") { + next_char("^"); + } + (function classy() { + ranges(); + if (char !== "]" && char !== "") { + warn_at( + "expected_a_before_b", + line, + column, + "\\", + char + ); + next_char(); + return classy(); + } + }()); + next_char("]"); + } + + function choice() { + + function group() { + +// Match a group that starts with left paren. + + next_char("("); + if (char === "?") { + next_char("?"); + if (char === "=" || char === "!") { + next_char(); + } else { + next_char(":"); + } + } else if (char === ":") { + warn_at("expected_a_before_b", line, column, "?", ":"); + } + choice(); + next_char(")"); + } + + function factor() { + if ( + char === "" + || char === "/" + || char === "]" + || char === ")" + ) { + return false; + } + if (char === "(") { + group(); + return true; + } + if (char === "[") { + klass(); + return true; + } + if (char === "\\") { + escape("BbDdSsWw^${}[]():=!.-|*+?"); + return true; + } + if ( + char === "?" + || char === "+" + || char === "*" + || char === "}" + || char === "{" + ) { + warn_at( + "expected_a_before_b", + line, + column - 1, + "\\", + char + ); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column - 1, "`"); + } + } else if (char === " ") { + warn_at( + "expected_a_b", + line, + column - 1, + "\\s", + " " + ); + } else if (char === "$") { + if (source_line[0] !== "/") { + multi_mode = true; + } + } else if (char === "^") { + if (snippet !== "^") { + multi_mode = true; + } + } + next_char(); + return true; + } + + function sequence(follow) { + if (factor()) { + quantifier(); + return sequence(true); + } + if (!follow) { + warn_at("expected_regexp_factor_a", line, column, char); + } + } + +// Match a choice (a sequence that can be followed by | and another choice). + + sequence(); + if (char === "|") { + next_char("|"); + return choice(); + } + } + +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + next_char(); + if (char === "=") { + warn_at("expected_a_before_b", line, column, "\\", "="); + } + choice(); + +// Make sure there is a closing slash. + + snip(); + value = snippet; + next_char("/"); + +// Process dangling flag letters. + + const allowed = { + g: true, + i: true, + m: true, + u: true, + y: true + }; + const flag = empty(); + (function make_flag() { + if (is_letter(char)) { + if (allowed[char] !== true) { + warn_at("unexpected_a", line, column, char); + } + allowed[char] = false; + flag[char] = true; + next_char(); + return make_flag(); + } + }()); + back_char(); + if (char === "/" || char === "*") { + return stop_at("unexpected_a", line, from, char); + } + result = make("(regexp)", char); + result.flag = flag; + result.value = value; + if (multi_mode && !flag.m) { + warn_at("missing_m", line, column); + } + return result; + } + + function string(quote) { + +// Make a string token. + + let the_token; + snippet = ""; + next_char(); + + return (function next() { + if (char === quote) { + snip(); + the_token = make("(string)", snippet); + the_token.quote = quote; + return the_token; + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "\\") { + escape(quote); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char("`"); + } else { + next_char(); + } + return next(); + }()); + } + + function frack() { + if (char === ".") { + some_digits(rx_digits); + next_char(); + } + if (char === "E" || char === "e") { + next_char(); + if (char !== "+" && char !== "-") { + back_char(); + } + some_digits(rx_digits); + next_char(); + } + } + + function number() { + if (snippet === "0") { + next_char(); + if (char === ".") { + frack(); + } else if (char === "b") { + some_digits(rx_bits); + next_char(); + } else if (char === "o") { + some_digits(rx_octals); + next_char(); + } else if (char === "x") { + some_digits(rx_hexs); + next_char(); + } + } else { + next_char(); + frack(); + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + return stop_at( + "unexpected_a_after_b", + line, + column - 1, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + back_char(); + return make("(number)", snippet); + } + + function lex() { + let array; + let i = 0; + let j = 0; + let last; + let result; + let the_token; + if (!source_line) { + source_line = next_line(); + from = 0; + return ( + source_line === undefined + ? ( + mega_mode + ? stop_at("unclosed_mega", mega_line, mega_from) + : make("(end)") + ) + : lex() + ); + } + from = column; + result = source_line.match(rx_token); + +// result[1] token +// result[2] whitespace +// result[3] identifier +// result[4] number +// result[5] rest + + if (!result) { + return stop_at( + "unexpected_char_a", + line, + column, + source_line[0] + ); + } + + snippet = result[1]; + column += snippet.length; + source_line = result[5]; + +// Whitespace was matched. Call lex again to get more. + + if (result[2]) { + return lex(); + } + +// The token is an identifier. + + if (result[3]) { + return make(snippet, undefined, true); + } + +// The token is a number. + + if (result[4]) { + return number(snippet); + } + +// The token is a string. + + if (snippet === "\"") { + return string(snippet); + } + if (snippet === "'") { + if (!option.single) { + warn_at("use_double", line, column); + } + return string(snippet); + } + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (snippet === "`") { + if (mega_mode) { + return stop_at("expected_a_b", line, column, "}", "`"); + } + snippet = ""; + mega_from = from; + mega_line = line; + mega_mode = true; + +// Parsing a mega literal is tricky. First make a ` token. + + make("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + (function part() { + const at = source_line.search(rx_mega); + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + if (at < 0) { + snippet += source_line + "\n"; + return ( + next_line() === undefined + ? stop_at("unclosed_mega", mega_line, mega_from) + : part() + ); + } + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + snippet += source_line.slice(0, at); + column += at; + source_line = source_line.slice(at); + if (source_line[0] === "\\") { + stop_at("escape_mega", line, at); + } + make("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then make tokens that will become part of an expression until +// a } token is made. + + if (source_line[0] === "$") { + column += 2; + make("${"); + source_line = source_line.slice(2); + (function expr() { + const id = lex().id; + if (id === "{") { + return stop_at( + "expected_a_b", + line, + column, + "}", + "{" + ); + } + if (id !== "}") { + return expr(); + } + }()); + return part(); + } + }()); + source_line = source_line.slice(1); + column += 1; + mega_mode = false; + return make("`"); + } + +// The token is a // comment. + + if (snippet === "//") { + snippet = source_line; + source_line = ""; + the_token = comment(snippet); + if (mega_mode) { + warn("unexpected_comment", the_token, "`"); + } + return the_token; + } + +// The token is a /* comment. + + if (snippet === "/*") { + array = []; + if (source_line[0] === "/") { + warn_at("unexpected_a", line, column + i, "/"); + } + (function next() { + if (source_line > "") { + i = source_line.search(rx_star_slash); + if (i >= 0) { + return; + } + j = source_line.search(rx_slash_star); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + } + array.push(source_line); + source_line = next_line(); + if (source_line === undefined) { + return stop_at("unclosed_comment", line, column); + } + return next(); + }()); + snippet = source_line.slice(0, i); + j = snippet.search(rx_slash_star_or_slash); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + array.push(snippet); + column += i + 2; + source_line = source_line.slice(i + 2); + return comment(array); + } + +// The token is a slash. + + if (snippet === "/") { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + if (prior.identifier) { + if (!prior.dot) { + if (prior.id === "return") { + return regexp(); + } + if ( + prior.id === "(begin)" + || prior.id === "case" + || prior.id === "delete" + || prior.id === "in" + || prior.id === "instanceof" + || prior.id === "new" + || prior.id === "typeof" + || prior.id === "void" + || prior.id === "yield" + ) { + the_token = regexp(); + return stop("unexpected_a", the_token); + } + } + } else { + last = prior.id[prior.id.length - 1]; + if ("(,=:?[".indexOf(last) >= 0) { + return regexp(); + } + if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) { + the_token = regexp(); + warn("wrap_regexp", the_token); + return the_token; + } + } + if (source_line[0] === "/") { + column += 1; + source_line = source_line.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + } + return make(snippet); + } + + first = lex(); + json_mode = first.id === "{" || first.id === "["; + +// This is the only loop in JSLint. It will turn into a recursive call to lex +// when ES6 has been finished and widely deployed and adopted. + + while (true) { + if (lex().id === "(end)") { + break; + } + } +} + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + +function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!rx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!rx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property[id] === "number") { + property[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (tenure !== undefined) { + if (tenure[id] !== true) { + warn("unregistered_property_a", name); + } + } else { + if (name.identifier && rx_bad_property.test(id)) { + warn("bad_property_a", name); + } + } + property[id] = 1; + } + return id; +} + +function dispense() { + +// Deliver the next token, skipping the comments. + + const cadet = tokens[token_nr]; + token_nr += 1; + if (cadet.id === "(comment)") { + if (json_mode) { + warn("unexpected_a", cadet); + } + return dispense(); + } else { + return cadet; + } +} + +function lookahead() { + +// Look ahead one token without advancing. + + const old_token_nr = token_nr; + const cadet = dispense(true); + token_nr = old_token_nr; + return cadet; +} + +function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if (token.identifier && token.id !== "function") { + anon = token.id; + } else if (token.id === "(string)" && rx_identifier.test(token.value)) { + anon = token.value; + } + +// Attempt to match next_token with an expected id. + + if (id !== undefined && next_token.id !== id) { + return ( + match === undefined + ? stop("expected_a_b", next_token, id, artifact()) + : stop( + "expected_a_b_from_c_d", + next_token, + id, + artifact(match), + artifact_line(match), + artifact(next_token) + ) + ); + } + +// Promote the tokens, skipping comments. + + token = next_token; + next_token = dispense(); + if (next_token.id === "(end)") { + token_nr -= 1; + } +} + +// Parsing of JSON is simple: + +function json_value() { + let negative; + if (next_token.id === "{") { + return (function json_object() { + const brace = next_token; + const object = empty(); + const properties = []; + brace.expression = properties; + advance("{"); + if (next_token.id !== "}") { + (function next() { + let name; + let value; + if (next_token.quote !== "\"") { + warn( + "unexpected_a", + next_token, + next_token.quote + ); + } + name = next_token; + advance("(string)"); + if (object[token.value] !== undefined) { + warn("duplicate_a", token); + } else if (token.value === "__proto__") { + warn("bad_property_a", token); + } else { + object[token.value] = token; + } + advance(":"); + value = json_value(); + value.label = name; + properties.push(value); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("}", brace); + return brace; + }()); + } + if (next_token.id === "[") { + return (function json_array() { + const bracket = next_token; + const elements = []; + bracket.expression = elements; + advance("["); + if (next_token.id !== "]") { + (function next() { + elements.push(json_value()); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]", bracket); + return bracket; + }()); + } + if ( + next_token.id === "true" + || next_token.id === "false" + || next_token.id === "null" + ) { + advance(); + return token; + } + if (next_token.id === "(number)") { + if (!rx_JSON_number.test(next_token.value)) { + warn("unexpected_a"); + } + advance(); + return token; + } + if (next_token.id === "(string)") { + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + advance(); + return token; + } + if (next_token.id === "-") { + negative = next_token; + negative.arity = "unary"; + advance("-"); + advance("(number)"); + negative.expression = token; + return negative; + } + stop("unexpected_a"); +} + +// Now we parse JavaScript. + +function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + const id = name.id; + +// Reserved words may not be enrolled. + + if (syntax[id] !== undefined && id !== "ignore") { + warn("reserved_a", name); + } else { + +// Has the name been enrolled in this context? + + let earlier = functionage.context[id]; + if (earlier) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + +// Has the name been enrolled in an outer context? + + } else { + stack.forEach(function (value) { + const item = value.context[id]; + if (item !== undefined) { + earlier = item; + } + }); + if (earlier) { + if (id === "ignore") { + if (earlier.role === "variable") { + warn("unexpected_a", name); + } + } else { + if ( + ( + role !== "exception" + || earlier.role !== "exception" + ) + && role !== "parameter" + && role !== "function" + ) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + } + } + } + +// Enroll it. + + functionage.context[id] = name; + name.dead = true; + name.parent = functionage; + name.init = false; + name.role = role; + name.used = 0; + name.writable = !readonly; + } + } +} + +function expression(rbp, initial) { + +// This is the heart of the Pratt parser. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// nud Null denotation +// led Left denotation +// lbp Left binding power +// rbp Right binding power + +// It processes a nud (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop. It +// returns the expression's parse tree. + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement, + + if (!initial) { + advance(); + } + the_symbol = syntax[token.id]; + if (the_symbol !== undefined && the_symbol.nud !== undefined) { + left = the_symbol.nud(); + } else if (token.identifier) { + left = token; + left.arity = "variable"; + } else { + return stop("unexpected_a", token); + } + (function right() { + the_symbol = syntax[next_token.id]; + if ( + the_symbol !== undefined + && the_symbol.led !== undefined + && rbp < the_symbol.lbp + ) { + advance(); + left = the_symbol.led(left); + return right(); + } + }()); + return left; +} + +function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = next_token; + let the_value; + the_paren.free = true; + advance("("); + the_value = expression(0); + advance(")"); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (anticondition[the_value.id] === true) { + warn("unexpected_a", the_value); + } + return the_value; +} + +function is_weird(thing) { + return ( + thing.id === "(regexp)" + || thing.id === "{" + || thing.id === "=>" + || thing.id === "function" + || (thing.id === "[" && thing.arity === "unary") + ); +} + +function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + return ( + Array.isArray(b) + && a.length === b.length + && a.every(function (value, index) { + return are_similar(value, b[index]); + }) + ); + } + if (Array.isArray(b)) { + return false; + } + if (a.id === "(number)" && b.id === "(number)") { + return a.value === b.value; + } + let a_string; + let b_string; + if (a.id === "(string)") { + a_string = a.value; + } else if (a.id === "`" && a.constant) { + a_string = a.value[0]; + } + if (b.id === "(string)") { + b_string = b.value; + } else if (b.id === "`" && b.constant) { + b_string = b.value[0]; + } + if (typeof a_string === "string") { + return a_string === b_string; + } + if (is_weird(a) || is_weird(b)) { + return false; + } + if (a.arity === b.arity && a.id === b.id) { + if (a.id === ".") { + return ( + are_similar(a.expression, b.expression) + && are_similar(a.name, b.name) + ); + } + if (a.arity === "unary") { + return are_similar(a.expression, b.expression); + } + if (a.arity === "binary") { + return ( + a.id !== "(" + && are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + ); + } + if (a.arity === "ternary") { + return ( + are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + && are_similar(a.expression[2], b.expression[2]) + ); + } + if (a.arity === "function" && a.arity === "regexp") { + return false; + } + return true; + } + return false; +} + +function semicolon() { + +// Try to match a semicolon. + + if (next_token.id === ";") { + advance(";"); + } else { + warn_at( + "expected_a_b", + token.line, + token.thru, + ";", + artifact(next_token) + ); + } + anon = "anonymous"; +} + +function statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token.identifier && next_token.id === ":") { + the_label = token; + if (the_label.id === "ignore") { + warn("unexpected_a", the_label); + } + advance(":"); + if ( + next_token.id === "do" + || next_token.id === "for" + || next_token.id === "switch" + || next_token.id === "while" + ) { + enroll(the_label, "label", true); + the_label.init = true; + the_label.dead = false; + the_statement = statement(); + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token; + first.statement = true; + the_symbol = syntax[first.id]; + if (the_symbol !== undefined && the_symbol.fud !== undefined) { + the_symbol.disrupt = false; + the_symbol.statement = true; + the_statement = the_symbol.fud(); + } else { + +// It is an expression statement. + + the_statement = expression(0, true); + if (the_statement.wrapped && the_statement.id !== "(") { + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; +} + +function statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const array = []; + (function next(disrupt) { + if ( + next_token.id !== "}" + && next_token.id !== "case" + && next_token.id !== "default" + && next_token.id !== "else" + && next_token.id !== "(end)" + ) { + let a_statement = statement(); + array.push(a_statement); + if (disrupt) { + warn("unreachable_a", a_statement); + } + return next(a_statement.disrupt); + } + }(false)); + return array; +} + +function not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === global) { + warn("unexpected_at_top_level_a", thing); + } +} + +function top_level_only(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== global) { + warn("misplaced_a", the_thing); + } +} + +function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token; + the_block.arity = "statement"; + the_block.body = special === "body"; + +// All top level function bodies should include the "use strict" pragma unless +// the whole file is strict or the file is a module or the function parameters +// use es6 syntax. + + if ( + special === "body" + && stack.length === 1 + && next_token.value === "use strict" + ) { + the_block.strict = next_token; + next_token.statement = true; + advance("(string)"); + advance(";"); + } + stmts = statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option.devel && special !== "ignore") { + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; +} + +function mutation_check(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + warn("bad_assignment_a", the_thing); + return false; + } + return true; +} + +function left_check(left, right) { + +// Warn if the left is not one of these: +// e.b +// e[b] +// e() +// ?: +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !left_check(left.expression[1]) + && !left_check(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right); + return false; + } + return true; +} + +// These functions are used to specify the grammar of our language: + +function symbol(id, bp) { + +// Make a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax[id] = the_symbol; + } + return the_symbol; +} + +function assignment(id) { + +// Make an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led = function (left) { + const the_token = token; + let right; + the_token.arity = "assignment"; + right = expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "pre" + || right.arity === "post" + ) { + warn("unexpected_a", right); + } + mutation_check(left); + return the_token; + }; + return the_symbol; +} + +function constant(id, type, value) { + +// Make a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud = ( + typeof value === "function" + ? value + : function () { + token.constant = true; + if (value !== undefined) { + token.value = value; + } + return token; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; +} + +function infix(id, bp, f) { + +// Make an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, expression(bp)]; + return the_token; + }; + return the_symbol; +} + +function infixr(id, bp) { + +// Make a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + the_token.expression = [left, expression(bp - 1)]; + return the_token; + }; + return the_symbol; +} + +function post(id) { + +// Make one of the post operators. + + const the_symbol = symbol(id, 150); + the_symbol.led = function (left) { + token.expression = left; + token.arity = "post"; + mutation_check(token.expression); + return token; + }; + return the_symbol; +} + +function pre(id) { + +// Make one of the pre operators. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "pre"; + the_token.expression = expression(150); + mutation_check(the_token.expression); + return the_token; + }; + return the_symbol; +} + +function prefix(id, f) { + +// Make a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = expression(150); + return the_token; + }; + return the_symbol; +} + +function stmt(id, f) { + +// Make a statement. + + const the_symbol = symbol(id); + the_symbol.fud = function () { + token.arity = "statement"; + return f(); + }; + return the_symbol; +} + +function ternary(id1, id2) { + +// Make a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led = function (left) { + const the_token = token; + const second = expression(20); + advance(id2); + token.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, expression(10)]; + return the_token; + }; + return the_symbol; +} + +// Begin defining the language. + +syntax = empty(); + +symbol("}"); +symbol(")"); +symbol("]"); +symbol(","); +symbol(";"); +symbol(":"); +symbol("*/"); +symbol("await"); +symbol("case"); +symbol("catch"); +symbol("class"); +symbol("default"); +symbol("else"); +symbol("enum"); +symbol("finally"); +symbol("implements"); +symbol("interface"); +symbol("package"); +symbol("private"); +symbol("protected"); +symbol("public"); +symbol("static"); +symbol("super"); +symbol("void"); +symbol("yield"); + +constant("(number)", "number"); +constant("(regexp)", "regexp"); +constant("(string)", "string"); +constant("arguments", "object", function () { + warn("unexpected_a", token); + return token; +}); +constant("eval", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("false", "boolean", false); +constant("Function", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("ignore", "undefined", function () { + warn("unexpected_a", token); + return token; +}); +constant("Infinity", "number", Infinity); +constant("isFinite", "function", function () { + warn("expected_a_b", token, "Number.isFinite", "isFinite"); + return token; +}); +constant("isNaN", "function", function () { + warn("number_isNaN", token); + return token; +}); +constant("NaN", "number", NaN); +constant("null", "null", null); +constant("this", "object", function () { + if (!option.this) { + warn("unexpected_a", token); + } + return token; +}); +constant("true", "boolean", true); +constant("undefined", "undefined"); + +assignment("="); +assignment("+="); +assignment("-="); +assignment("*="); +assignment("/="); +assignment("%="); +assignment("&="); +assignment("|="); +assignment("^="); +assignment("<<="); +assignment(">>="); +assignment(">>>="); + +infix("||", 40); +infix("&&", 50); +infix("|", 70); +infix("^", 80); +infix("&", 90); +infix("==", 100); +infix("===", 100); +infix("!=", 100); +infix("!==", 100); +infix("<", 110); +infix(">", 110); +infix("<=", 110); +infix(">=", 110); +infix("in", 110); +infix("instanceof", 110); +infix("<<", 120); +infix(">>", 120); +infix(">>>", 120); +infix("+", 130); +infix("-", 130); +infix("*", 140); +infix("/", 140); +infix("%", 140); +infixr("**", 150); +infix("(", 160, function (left) { + const the_paren = token; + let the_argument; + if (left.id !== "function") { + left_check(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (next_token.id !== ")") { + (function next() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + the_paren.free = true; + if (the_argument.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + the_paren.free = false; + } + return the_paren; +}); +infix(".", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("[", 170, function (left) { + const the_token = token; + const the_subscript = expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + const name = survey(the_subscript); + if (rx_identifier.test(name)) { + warn("subscript_a", the_subscript, name); + } + } + left_check(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; +}); +infix("=>", 170, function (left) { + return stop("wrap_parameter", left); +}); + +function do_tick() { + const the_tick = token; + the_tick.value = []; + the_tick.expression = []; + if (next_token.id !== "`") { + (function part() { + advance("(string)"); + the_tick.value.push(token); + if (next_token.id === "${") { + advance("${"); + the_tick.expression.push(expression(0)); + advance("}"); + return part(); + } + }()); + } + advance("`"); + return the_tick; +} + +infix("`", 160, function (left) { + const the_tick = do_tick(); + left_check(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; +}); + +post("++"); +post("--"); +pre("++"); +pre("--"); + +prefix("+"); +prefix("-"); +prefix("~"); +prefix("!"); +prefix("!!"); +prefix("[", function () { + const the_token = token; + the_token.expression = []; + if (next_token.id !== "]") { + (function next() { + let element; + let ellipsis = false; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + element = expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]"); + return the_token; +}); +prefix("/=", function () { + stop("expected_a_b", token, "/\\=", "/="); +}); +prefix("=>", function () { + return stop("expected_a_before_b", token, "()", "=>"); +}); +prefix("new", function () { + const the_new = token; + const right = expression(160); + if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "()", artifact(next_token)); + } + the_new.expression = right; + return the_new; +}); +prefix("typeof"); +prefix("void", function () { + const the_void = token; + warn("unexpected_a", the_void); + the_void.expression = expression(0); + return the_void; +}); + +function parameter_list() { + let complex = false; + const list = []; + let optional; + const signature = ["("]; + if (next_token.id !== ")" && next_token.id !== "(end)") { + (function parameter() { + let ellipsis = false; + let param; + if (next_token.id === "{") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("{"); + signature.push("{"); + (function subparameter() { + let subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (next_token.id === ":") { + advance(":"); + advance(); + token.label = subparam; + subparam = token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + } + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return subparameter(); + } + }()); + list.push(param); + advance("}"); + signature.push("}"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else if (next_token.id === "[") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("["); + signature.push("[]"); + (function subparameter() { + const subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + return subparameter(); + } + }()); + list.push(param); + advance("]"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else { + if (next_token.id === "...") { + complex = true; + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + } + if (!next_token.identifier) { + return stop("expected_identifier_a"); + } + param = next_token; + list.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (next_token.id === "=") { + complex = true; + optional = param; + advance("="); + param.expression = expression(0); + } else { + if (optional !== undefined) { + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } + } + }()); + } + advance(")"); + signature.push(")"); + return [list, signature.join(""), complex]; +} + +function do_function(the_function) { + let name; + if (the_function === undefined) { + the_function = token; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + name = next_token; + enroll(name, "variable", true); + the_function.name = name; + name.init = true; + name.calls = empty(); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + if (next_token.identifier) { + name = next_token; + the_function.name = name; + advance(); + } else { + the_function.name = anon; + } + } + } else { + name = the_function.name; + } + the_function.level = functionage.level + 1; + if (mega_mode) { + warn("unexpected_a", the_function); + } + +// Don't make functions in loops. It is inefficient, and it can lead to scoping +// errors. + + if (functionage.loop > 0) { + warn("function_in_loop", the_function); + } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + the_function.context = empty(); + the_function.finally = 0; + the_function.loop = 0; + the_function.switch = 0; + the_function.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functions.push(the_function); + functionage = the_function; + if (the_function.arity !== "statement" && typeof name === "object") { + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// Parse the parameter list. + + advance("("); + token.free = false; + token.arity = "function"; + const pl = parameter_list(); + functionage.parameters = pl[0]; + functionage.signature = pl[1]; + functionage.complex = pl[2]; + functionage.parameters.forEach(function enroll_parameter(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + name.names.forEach(enroll_parameter); + } + }); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && next_token.line === token.line + ) { + return stop("unexpected_a", next_token); + } + if (next_token.id === "." || next_token.id === "[") { + warn("unexpected_a"); + } + +// Restore the previous context. + + functionage = stack.pop(); + return the_function; +} + +prefix("function", do_function); + +function fart(pl) { + if (next_token.id === ";") { + stop("wrap_assignment", token); + } + advance("=>"); + const the_fart = token; + the_fart.arity = "binary"; + the_fart.name = "=>"; + the_fart.level = functionage.level + 1; + functions.push(the_fart); + if (functionage.loop > 0) { + warn("function_in_loop", the_fart); + } + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + the_fart.context = empty(); + the_fart.finally = 0; + the_fart.loop = 0; + the_fart.switch = 0; + the_fart.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functionage = the_fart; + the_fart.parameters = pl[0]; + the_fart.signature = pl[1]; + the_fart.complex = true; + the_fart.parameters.forEach(function (name) { + enroll(name, "parameter", true); + }); + if (next_token.id === "{") { + warn("expected_a_b", the_fart, "function", "=>"); + the_fart.block = block("body"); + } else { + the_fart.expression = expression(0); + } + functionage = stack.pop(); + return the_fart; +} + +prefix("(", function () { + const the_paren = token; + let the_value; + const cadet = lookahead().id; + +// We can distinguish between a parameter list for => and a wrapped expression +// with one token of lookahead. + + if ( + next_token.id === ")" + || next_token.id === "..." + || (next_token.identifier && (cadet === "," || cadet === "=")) + ) { + the_paren.free = false; + return fart(parameter_list()); + } + the_paren.free = true; + the_value = expression(0); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + if (next_token.id === "=>") { + if (the_value.arity !== "variable") { + if (the_value.id === "{" || the_value.id === "[") { + warn("expected_a_before_b", the_paren, "function", "("); + return stop("expected_a_b", next_token, "{", "=>"); + } + return stop("expected_identifier_a", the_value); + } + the_paren.expression = [the_value]; + return fart([the_paren.expression, "(" + the_value.id + ")"]); + } + return the_value; +}); +prefix("`", do_tick); +prefix("{", function () { + const the_brace = token; + const seen = empty(); + the_brace.expression = []; + if (next_token.id !== "}") { + (function member() { + let extra; + let full; + let id; + let name = next_token; + let value; + advance(); + if ( + (name.id === "get" || name.id === "set") + && next_token.identifier + ) { + if (!option.getset) { + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + next_token.id; + name = next_token; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (next_token.id === "}" || next_token.id === ",") { + if (typeof extra === "string") { + advance("("); + } + value = expression(Infinity, true); + } else if (next_token.id === "(") { + value = do_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + advance("("); + } + advance(":"); + value = expression(0); + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + advance(":"); + value = expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (next_token.id === ",") { + advance(","); + return member(); + } + }()); + } + advance("}"); + return the_brace; +}); + +stmt(";", function () { + warn("unexpected_a", token); + return token; +}); +stmt("{", function () { + warn("naked_block", token); + return block("naked"); +}); +stmt("break", function () { + const the_break = token; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (next_token.identifier && token.line === next_token.line) { + the_label = functionage.context[next_token.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + warn( + (the_label !== undefined && the_label.dead) + ? "out_of_scope_a" + : "not_label_a" + ); + } else { + the_label.used += 1; + } + the_break.label = next_token; + advance(); + } + advance(";"); + return the_break; +}); + +function do_var() { + const the_statement = token; + const is_const = the_statement.id === "const"; + the_statement.names = []; + +// A program may use var or let, but not both. + + if (!is_const) { + if (var_mode === undefined) { + var_mode = the_statement.id; + } else if (the_statement.id !== var_mode) { + warn( + "expected_a_b", + the_statement, + var_mode, + the_statement.id + ); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + warn("var_switch", the_statement); + } + if (functionage.loop > 0 && the_statement.id === "var") { + warn("var_loop", the_statement); + } + (function next() { + if (next_token.id === "{" && the_statement.id !== "var") { + const the_brace = next_token; + the_brace.names = []; + advance("{"); + (function pair() { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + survey(name); + advance(); + if (next_token.id === ":") { + advance(":"); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + next_token.label = name; + the_brace.names.push(next_token); + enroll(next_token, "variable", is_const); + advance(); + } else { + the_brace.names.push(name); + enroll(name, "variable", is_const); + } + if (next_token.id === ",") { + advance(","); + return pair(); + } + }()); + advance("}"); + advance("="); + the_brace.expression = expression(0); + the_statement.names.push(the_brace); + } else if (next_token.id === "[" && the_statement.id !== "var") { + const the_bracket = next_token; + the_bracket.names = []; + advance("["); + (function element() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + advance(); + the_bracket.names.push(name); + enroll(name, "variable", the_statement.id === "const"); + if (ellipsis) { + name.ellipsis = true; + } else if (next_token.id === ",") { + advance(","); + return element(); + } + }()); + advance("]"); + advance("="); + the_bracket.expression = expression(0); + the_statement.names.push(the_bracket); + } else if (next_token.identifier) { + const name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", is_const); + if (next_token.id === "=" || is_const) { + advance("="); + name.init = true; + name.expression = expression(0); + } + the_statement.names.push(name); + } else { + return stop("expected_identifier_a", next_token); + } + if (next_token.id === ",") { + if (!option.multivar) { + warn("expected_a_b", next_token, ";", ","); + } + advance(","); + return next(); + } + }()); + the_statement.open = ( + the_statement.names.length > 1 + && the_statement.line !== the_statement.names[1].line + ); + semicolon(); + return the_statement; +} + +stmt("const", do_var); +stmt("continue", function () { + const the_continue = token; + if (functionage.loop < 1 || functionage.finally > 0) { + warn("unexpected_a", the_continue); + } + not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; +}); +stmt("debugger", function () { + const the_debug = token; + if (!option.devel) { + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; +}); +stmt("delete", function () { + const the_token = token; + const the_value = expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; +}); +stmt("do", function () { + const the_do = token; + not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; +}); +stmt("export", function () { + const the_export = token; + let the_id; + let the_name; + let the_thing; + + function export_id() { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + the_id = next_token.id; + the_name = global.context[the_id]; + if (the_name === undefined) { + warn("unexpected_a"); + } else { + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a"); + } + exports[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + } + + the_export.expression = []; + if (next_token.id === "default") { + if (exports.default !== undefined) { + warn("duplicate_a"); + } + advance("default"); + the_thing = expression(0); + semicolon(); + exports.default = the_thing; + the_export.expression.push(the_thing); + } else { + if (next_token.id === "function") { + the_thing = statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a", the_name); + } + exports[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + warn("unexpected_a"); + } else if (next_token.id === "{") { + advance("{"); + (function loop() { + export_id(); + if (next_token.id === ",") { + advance(","); + return loop(); + } + }()); + advance("}"); + semicolon(); + } else { + export_id(); + if (the_name.writable !== true) { + warn("unexpected_a", token); + } + semicolon(); + } + } + module_mode = true; + return the_export; +}); +stmt("for", function () { + let first; + const the_for = token; + if (!option.for) { + warn("unexpected_a", the_for); + } + not_top_level(the_for); + functionage.loop += 1; + advance("("); + token.free = true; + if (next_token.id === ";") { + return stop("expected_a_b", the_for, "while (", "for (;"); + } + if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + return stop("unexpected_a"); + } + first = expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = expression(0); + advance(";"); + the_for.inc = expression(0); + if (the_for.inc.id === "++") { + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; +}); +stmt("function", do_function); +stmt("if", function () { + let the_else; + const the_if = token; + the_if.expression = condition(); + the_if.block = block(); + if (next_token.id === "else") { + advance("else"); + the_else = token; + the_if.else = ( + next_token.id === "if" + ? statement() + : block() + ); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + the_if.disrupt = true; + } else { + warn("unexpected_a", the_else); + } + } + } + return the_if; +}); +stmt("import", function () { + const the_import = token; + let name; + if (typeof module_mode === "object") { + warn("unexpected_directive_a", module_mode, module_mode.directive); + } + module_mode = true; + if (next_token.identifier) { + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + const names = []; + advance("{"); + if (next_token.id !== "}") { + while (true) { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (next_token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token; + if (!rx_module.test(token.value)) { + warn("bad_module_name_a", token); + } + froms.push(token.value); + semicolon(); + return the_import; +}); +stmt("let", do_var); +stmt("return", function () { + const the_return = token; + not_top_level(the_return); + if (functionage.finally > 0) { + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (next_token.id !== ";" && the_return.line === next_token.line) { + the_return.expression = expression(10); + } + advance(";"); + return the_return; +}); +stmt("switch", function () { + let dups = []; + let last; + let stmts; + const the_cases = []; + let the_disrupt = true; + const the_switch = token; + not_top_level(the_switch); + if (functionage.finally > 0) { + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token.free = true; + the_switch.expression = expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + (function major() { + const the_case = next_token; + the_case.arity = "statement"; + the_case.expression = []; + (function minor() { + advance("case"); + token.switch = true; + const exp = expression(0); + if (dups.some(function (thing) { + return are_similar(thing, exp); + })) { + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (next_token.id === "case") { + return minor(); + } + }()); + stmts = statements(); + if (stmts.length < 1) { + warn("expected_statements_a"); + return; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "break;", + artifact(next_token) + ); + } + if (next_token.id === "case") { + return major(); + } + }()); + dups = undefined; + if (next_token.id === "default") { + const the_default = next_token; + advance("default"); + token.switch = true; + advance(":"); + the_switch.else = statements(); + if (the_switch.else.length < 1) { + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + const the_last = the_switch.else[the_switch.else.length - 1]; + if (the_last.id === "break" && the_last.label === undefined) { + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; +}); +stmt("throw", function () { + const the_throw = token; + the_throw.disrupt = true; + the_throw.expression = expression(10); + semicolon(); + if (functionage.try > 0) { + warn("unexpected_a", the_throw); + } + return the_throw; +}); +stmt("try", function () { + let the_catch; + let the_disrupt; + const the_try = token; + if (functionage.try > 0) { + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (next_token.id === "catch") { + let ignored = "ignore"; + the_catch = next_token; + the_try.catch = the_catch; + advance("catch"); + advance("("); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + if (next_token.id !== "ignore") { + ignored = undefined; + the_catch.name = next_token; + enroll(next_token, "exception", true); + } + advance(); + advance(")"); + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "catch", + artifact(next_token) + ); + + } + if (next_token.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; +}); +stmt("var", do_var); +stmt("while", function () { + const the_while = token; + not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; +}); +stmt("with", function () { + stop("unexpected_a", token); +}); + +ternary("?", ":"); + +// Ambulation of the parse tree. + +function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; +} + +function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all of the +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + } + }; +} + +const posts = empty(); +const pres = empty(); +const preaction = action(pres); +const postaction = action(posts); +const preamble = amble(pres); +const postamble = amble(posts); + +function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.id === "function") { + walk_statement(thing.block); + } + if (thing.arity === "pre" || thing.arity === "post") { + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + warn("unexpected_statement_a", thing); + } + postamble(thing); + } + } +} + +function walk_statement(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_statement); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + ) { + warn( + (thing.id === "(string)" && thing.value === "use strict") + ? "unexpected_a" + : "unexpected_expression_a", + thing + ); + } + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + } +} + +function lookup(thing) { + if (thing.arity === "variable") { + +// Look up the variable in the current context. + + let the_variable = functionage.context[thing.id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable === undefined) { + stack.forEach(function (outer) { + const a_variable = outer.context[thing.id]; + if ( + a_variable !== undefined + && a_variable.role !== "label" + ) { + the_variable = a_variable; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (the_variable === undefined) { + if (declared_globals[thing.id] === undefined) { + warn("undeclared_a", thing); + return; + } + the_variable = { + dead: false, + parent: global, + id: thing.id, + init: true, + role: "variable", + used: 0, + writable: false + }; + global.context[thing.id] = the_variable; + } + the_variable.closure = true; + functionage.context[thing.id] = the_variable; + } else if (the_variable.role === "label") { + warn("label_a", thing); + } + if ( + the_variable.dead + && ( + the_variable.calls === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + ) { + warn("out_of_scope_a", thing); + } + return the_variable; + } +} + +function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); +} + +function preaction_function(thing) { + if (thing.arity === "statement" && blockage.body !== true) { + warn("unexpected_a", thing); + } + if (thing.level === 1) { + if ( + module_mode === true + || global.strict !== undefined + || thing.complex + ) { + if (thing.id !== "=>" && thing.block.strict !== undefined) { + warn("unexpected_a", thing.block.strict); + } + } else { + if (thing.block.strict === undefined) { + warn("use_strict", thing); + } + } + } + stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); +} + +function bitwise_check(thing) { + if (!option.bitwise && bitwiseop[thing.id] === true) { + warn("unexpected_a", thing); + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + warn("unexpected_a", thing); + } +} + +function pop_block() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); +} + +function activate(name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.init = true; + } + } + blockage.live.push(name); +} + +function action_var(thing) { + thing.names.forEach(activate); +} + +preaction("assignment", bitwise_check); +preaction("binary", bitwise_check); +preaction("binary", function (thing) { + if (relationop[thing.id] === true) { + const left = thing.expression[0]; + const right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + warn("expected_string_a", right); + } + } else { + const value = right.value; + if (value === "null" || value === "undefined") { + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + warn("expected_type_string_a", right, value); + } + } + } + } +}); +preaction("binary", "==", function (thing) { + warn("expected_a_b", thing, "===", "=="); +}); +preaction("binary", "!=", function (thing) { + warn("expected_a_b", thing, "!==", "!="); +}); +preaction("binary", "=>", preaction_function); +preaction("binary", "||", function (thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + warn("and", thang); + } + }); +}); +preaction("binary", "(", function (thing) { + const left = thing.expression[0]; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + const parent = functionage.name.parent; + if (parent) { + const left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + && left_variable.dead + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } +}); +preaction("binary", "in", function (thing) { + warn("infix_in", thing); +}); +preaction("binary", "instanceof", function (thing) { + warn("unexpected_a", thing); +}); +preaction("binary", ".", function (thing) { + if (thing.expression.new) { + thing.new = true; + } +}); +preaction("statement", "{", function (thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; +}); +preaction("statement", "for", function (thing) { + if (thing.name !== undefined) { + const the_variable = lookup(thing.name); + if (the_variable !== undefined) { + the_variable.init = true; + if (!the_variable.writable) { + warn("bad_assignment_a", thing.name); + } + } + } + walk_statement(thing.initial); +}); +preaction("statement", "function", preaction_function); +preaction("unary", "~", bitwise_check); +preaction("unary", "function", preaction_function); +preaction("variable", function (thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } +}); + +function init_variable(name) { + const the_variable = lookup(name); + if (the_variable !== undefined) { + if (the_variable.writable) { + the_variable.init = true; + return; + } + } + warn("bad_assignment_a", name); +} + +postaction("assignment", "+=", function (thing) { + let right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } +}); +postaction("assignment", function (thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + if (thing.id === "=") { + if (thing.names !== undefined) { + if (Array.isArray(thing.names)) { + thing.names.forEach(init_variable); + } else { + init_variable(thing.names); + } + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.writable !== true) { + warn("bad_assignment_a", lvalue); + } + } + const right = syntax[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + warn("unexpected_a", thing.expression[1]); + } + } +}); + +function postaction_function(thing) { + delete functionage.finally; + delete functionage.loop; + delete functionage.switch; + delete functionage.try; + functionage = stack.pop(); + if (thing.wrapped) { + warn("unexpected_parens", thing); + } + return pop_block(); +} + +postaction("binary", function (thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || are_similar(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option.convert) { + if (thing.expression[0].value === "") { + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === ".") { + if (thing.expression.id === "RegExp") { + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } +}); +postaction("binary", "&&", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "||", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "=>", postaction_function); +postaction("binary", "(", function (thing) { + let left = thing.expression[0]; + let the_new; + let arg; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option.eval) { + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + warn( + "expected_a_before_b", + left, + "new", + artifact(left) + ); + } + } + } else if (left.id === ".") { + let cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + cack = !cack; + } + if (rx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + warn("unexpected_a", the_new); + } else { + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + const paren = left.expression; + if (paren.id === "(") { + const array = paren.expression; + if (array.length === 1) { + const new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } +}); +postaction("binary", "[", function (thing) { + if (thing.expression[0].id === "RegExp") { + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + warn("weird_expression_a", thing.expression[1]); + } +}); +postaction("statement", "{", pop_block); +postaction("statement", "const", action_var); +postaction("statement", "export", top_level_only); +postaction("statement", "for", function (thing) { + walk_statement(thing.inc); +}); +postaction("statement", "function", postaction_function); +postaction("statement", "import", function (the_thing) { + const name = the_thing.name; + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return top_level_only(the_thing); +}); +postaction("statement", "let", action_var); +postaction("statement", "try", function (thing) { + if (thing.catch !== undefined) { + const the_name = thing.catch.name; + if (the_name !== undefined) { + const the_variable = functionage.context[the_name.id]; + the_variable.dead = false; + the_variable.init = true; + } + walk_statement(thing.catch.block); + } +}); +postaction("statement", "var", action_var); +postaction("ternary", function (thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || are_similar(thing.expression[1], thing.expression[2]) + ) { + warn("unexpected_a", thing); + } else if (are_similar(thing.expression[0], thing.expression[1])) { + warn("expected_a_b", thing, "||", "?"); + } else if (are_similar(thing.expression[0], thing.expression[2])) { + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + warn("wrap_condition", thing.expression[0]); + } +}); +postaction("unary", function (thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option.convert) { + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } +}); +postaction("unary", "function", postaction_function); +postaction("unary", "+", function (thing) { + if (!option.convert) { + warn("expected_a_b", thing, "Number(...)", "+"); + } + const right = thing.expression; + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } +}); + +function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + if (id !== "ignore") { + const name = the_function.context[id]; + if (name.parent === the_function) { + if ( + name.used === 0 + && ( + name.role !== "function" + || name.parent.arity !== "unary" + ) + ) { + warn("unused_a", name); + } else if (!name.init) { + warn("uninitialized_a", name); + } + } + } + }); +} + +function uninitialized_and_unused() { + +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (module_mode === true || option.node) { + delve(global); + } + functions.forEach(delve); +} + +// Go through the token list, looking at usage of whitespace. + +function whitage() { + let closer = "(end)"; + let free = false; + let left = global; + let margin = 0; + let nr_comments_skipped = 0; + let open = true; + let right; + + function expected_at(at) { + warn( + "expected_a_at_b_c", + right, + artifact(right), + fudge + at, + artifact_column(right) + ); + } + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function no_space() { + if (left.line === right.line) { + if (left.thru !== right.from && nr_comments_skipped === 0) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (open) { + const at = ( + free + ? margin + : margin + 8 + ); + if (right.from < at) { + expected_at(at); + } + } else { + if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (free) { + if (right.from < margin) { + expected_at(margin); + } + } else { + const mislaid = ( + stack.length > 0 + ? stack[stack.length - 1].right + : undefined + ); + if (!open && mislaid !== undefined) { + warn( + "expected_a_next_at_b", + mislaid, + artifact(mislaid.id), + margin + 4 + fudge + ); + } else if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + stack = []; + tokens.forEach(function (the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + + const new_closer = opener[left.id]; + if (typeof new_closer === "string") { + if (new_closer !== right.id) { + stack.push({ + closer: closer, + free: free, + margin: margin, + open: open, + right: right + }); + closer = new_closer; + if (left.line !== right.line) { + free = closer === ")" && left.free; + open = true; + margin += 4; + if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (right.switch) { + at_margin(-4); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + free = false; + open = false; + no_space_only(); + } + } else { + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like {]) have already been detected. + + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } + } else { + +// If right is a closer, then pop the previous state. + + if (right.id === closer) { + const previous = stack.pop(); + margin = previous.margin; + if (open && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + closer = previous.closer; + free = previous.free; + open = previous.open; + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-4); + } else if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + one_space(); + } else { + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + at_margin(0); + } else { + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + no_space(); + } else if ( + left.id === "." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + ) { + no_space_only(); + } else if (right.id === ".") { + no_space_only(); + } else if (left.id === ";") { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + one_space_only(); + } else if (right.statement === true) { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.id === "var" + || left.id === "const" + || left.id === "let" + ) { + stack.push({ + closer: closer, + free: free, + margin: margin, + open: open + }); + closer = ";"; + free = false; + open = left.open; + if (open) { + margin = margin + 4; + at_margin(0); + } else { + one_space_only(); + } + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +// The jslint function itself. + +export default function jslint( + source = "", + option_object = empty(), + global_array = [] +) { + try { + warnings = []; + option = Object.assign(empty(), option_object); + anon = "anonymous"; + block_stack = []; + declared_globals = empty(); + directive_mode = true; + directives = []; + early_stop = true; + exports = empty(); + froms = []; + fudge = ( + option.fudge + ? 1 + : 0 + ); + functions = []; + global = { + id: "(global)", + body: true, + context: empty(), + from: 0, + level: 0, + line: 0, + live: [], + loop: 0, + switch: 0, + thru: 0 + }; + blockage = global; + functionage = global; + json_mode = false; + mega_mode = false; + module_mode = false; + next_token = global; + property = empty(); + shebang = false; + stack = []; + tenure = undefined; + token = global; + token_nr = 0; + var_mode = undefined; + populate(standard, declared_globals, false); + populate(global_array, declared_globals, false); + Object.keys(option).forEach(function (name) { + if (option[name] === true) { + const allowed = allowed_option[name]; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } + }); + tokenize(source); + advance(); + if (json_mode) { + tree = json_value(); + advance("(end)"); + } else { + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option.browser) { + if (next_token.id === ";") { + advance(";"); + } + } else { + +// If we are not in a browser, then the file form of strict pragma may be used. + + if ( + next_token.value === "use strict" + ) { + global.strict = next_token; + advance("(string)"); + advance(";"); + } + } + tree = statements(); + advance("(end)"); + functionage = global; + walk_statement(tree); + if (module_mode && global.strict !== undefined) { + warn("unexpected_a", global.strict); + } + if (warnings.length >= 0) { + uninitialized_and_unused(); + if (!option.white) { + whitage(); + } + } + } + if (!option.browser) { + directives.forEach(function (comment) { + if (comment.directive === "global") { + warn("missing_browser", comment); + } + }); + } + early_stop = false; + } catch (e) { + if (e.name !== "JSLintError") { + warnings.push(e); + } + } + return { + directives, + edition: "2018-09-26", + exports, + froms, + functions, + global, + id: "(JSLint)", + json: json_mode, + lines, + module: module_mode === true, + ok: warnings.length === 0 && !early_stop, + option, + property, + shebang: ( + shebang + ? lines[0] + : undefined + ), + stop: early_stop, + tokens, + tree, + warnings: warnings.sort(function (a, b) { + return a.line - b.line || a.column - b.column; + }) + }; +}; diff --git a/branch.debug/report.js b/branch.debug/report.js new file mode 100644 index 000000000..f123e9055 --- /dev/null +++ b/branch.debug/report.js @@ -0,0 +1,221 @@ +// report.js +// 2018-07-29 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Generate JSLint HTML reports. + +/*property + closure, column, context, edition, error, exports, filter, forEach, froms, + fudge, function, functions, global, id, isArray, join, json, keys, length, + level, line, lines, message, module, name, names, option, parameters, + parent, property, push, replace, role, signature, sort, stop, warnings +*/ + +const rx_amp = /&/g; +const rx_gt = />/g; +const rx_lt = / with less destructive entities. + + return String( + string + ).replace( + rx_amp, + "&" + ).replace( + rx_lt, + "<" + ).replace( + rx_gt, + ">" + ); +} + +export default { + error: function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + let fudge = Number(Boolean(data.option.fudge)); + let output = []; + if (data.stop) { + output.push("
    JSLint was unable to finish.
    "); + } + data.warnings.forEach(function (warning) { + output.push( + "
    ", + entityify(warning.line + fudge), + ".", + entityify(warning.column + fudge), + "
    ", + entityify(warning.message), + "
    ", + entityify(data.lines[warning.line] || ""), + "" + ); + }); + return output.join(""); + }, + + function: function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + let fudge = Number(Boolean(data.option.fudge)); + let mode = ( + data.module + ? "module" + : "global" + ); + let output = []; + + if (data.json) { + return ( + data.warnings.length === 0 + ? "
    JSON: good.
    " + : "
    JSON: bad.
    " + ); + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + let global = Object.keys(data.global.context).sort(); + let froms = data.froms.sort(); + let exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + let context = the_function.context; + let list = Object.keys(context); + output.push( + "
    ", + entityify(the_function.line + fudge), + "
    ", + ( + the_function.name === "=>" + ? entityify(the_function.signature) + " =>" + : ( + typeof the_function.name === "string" + ? "«" + entityify(the_function.name) + "»" + : "" + entityify(the_function.name.id) + "" + ) + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + let params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.parent === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.parent === the_function + ); + })); + detail("outer", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.parent !== the_function + && the_variable.parent.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].parent.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + output.push( + "
    JSLint edition ", + entityify(data.edition), + "
    " + ); + return output.join(""); + }, + + property: function property_directive(data) { + +// Produce the /*property*/ directive. + + let not_first = false; + let output = ["/*property"]; + let length = 1111; + let properties = Object.keys(data.property); + + if (properties.length > 0) { + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length >= 80) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); + } + } +}; diff --git a/branch.es5_web/README b/branch.es5_web/README new file mode 100644 index 000000000..7871db0e2 --- /dev/null +++ b/branch.es5_web/README @@ -0,0 +1,45 @@ +JSLint, The JavaScript Code Quality Tool + +Douglas Crockford +douglas@crockford.com + +2013-04-05 + +jslint.js contains the fully commented global JSLINT function. + +jslint.html runs the JSLINT function in a web page. The page also depends +on adsafe.js [www.ADsafe.org] and json2.js [www.JSON.org] (which are not +included in this project) and intercept.js and init_ui.js (which are). The +js files should all be minified, and all except init_ui.js are concatenated +together to form web_jslint.js. + +adsafe.js https://github.com/douglascrockford/ADsafe +json2.js https://github.com/douglascrockford/JSON-js + +intercept.js augments ADsafe, giving widgets access to the clock, cookies, +and the JSLINT function. + +init_ui.js hooks the HTML ui components to ADsafe. + +lint.html describes JSLint's usage. Please read it. + +Please direct questions and comments to +https://plus.google.com/communities/104441363299760713736. + +JSLint can be run anywhere that JavaScript (or Java) can run. See for example +https://github.com/douglascrockford/JSLint/wiki/JSLINT + +The place to express yourself in programming is in the quality of your ideas, +and the efficiency of execution. The role of style is the same as in +literature. A great writer doesn't express himself by putting the spaces +before his commas instead of after, or by putting extra spaces inside his +parentheses. A great writer will slavishly conform to some rules of style, +and that in no way constrains his power to express himself creatively. +See for example William Strunk's The Elements of Style +[http://www.crockford.com/wrrrld/style.html]. + +This applies to programming as well. Conforming to a consistent style +improves readability, and frees you to express yourself in ways that matter. +JSLint here plays the part of a stern but benevolent editor, helping you to +get the style right so that you can focus your creative energy where it is +most needed. diff --git a/branch.es5_web/adsafe.js b/branch.es5_web/adsafe.js new file mode 100644 index 000000000..d751bb5d1 --- /dev/null +++ b/branch.es5_web/adsafe.js @@ -0,0 +1,2060 @@ +// adsafe.js +// 2017-06-12 + +// Public Domain. + +// NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. +// SUBJECT TO CHANGE WITHOUT NOTICE. + +// Original url: http://www.ADsafe.org/adsafe.js + +// This file implements the core ADSAFE runtime. A site may add additional +// methods understanding that those methods will be made available to guest +// code. + +// This code should be minified before deployment. +// See http://javascript.crockford.com/jsmin.html + +// USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO +// NOT CONTROL. + +/*global window*/ + +/*jslint browser, devel, for, this +*/ + +/*property + _, ___nodes___, ___star___, _intercept, a, abbr, acronym, addEventListener, + address, altKey, append, appendChild, apply, area, arguments, autocomplete, + b, bdo, big, blockquote, blur, br, bubble, button, call, callee, caller, + cancelBubble, canvas, caption, center, change, charAt, charCode, check, + checked, childNodes, cite, class, className, clientX, clientY, clone, + cloneNode, code, col, colgroup, combine, concat, console, constructor, + count, create, createDocumentFragment, createElement, createRange, + createTextNode, createTextRange, cssFloat, ctrlKey, currentStyle, dd, + defaultView, del, dfn, dir, disabled, div, dl, dt, each, em, empty, enable, + ephemeral, eval, exec, expand, explode, fieldset, fire, firstChild, focus, + font, form, fragment, fromCharCode, get, getCheck, getChecks, getClass, + getClasses, getComputedStyle, getElementById, getElementsByTagName, + getMark, getMarks, getName, getNames, getOffsetHeight, getOffsetHeights, + getOffsetWidth, getOffsetWidths, getParent, getSelection, getStyle, + getStyles, getTagName, getTagNames, getTitle, getTitles, getValue, + getValues, go, h1, h2, h3, h4, h5, h6, has, hasOwnProperty, hr, i, id, img, + inRange, indeterminate, indexOf, input, ins, insertBefore, isArray, kbd, + key, keyCode, keys, klass, label, later, legend, length, li, lib, log, map, + mark, menu, message, name, nextSibling, nodeName, nodeValue, object, off, + offsetHeight, offsetWidth, ol, on, onclick, ondblclick, onfocusin, + onfocusout, onkeypress, onmousedown, onmousemove, onmouseout, onmouseover, + onmouseup, op, optgroup, option, p, parent, parentNode, postError, pre, + prepend, preventDefault, protect, prototype, push, q, remove, removeChild, + removeElement, replace, replaceChild, returnValue, row, samp, select, + selection, selectionEnd, selectionStart, set, shiftKey, slice, small, span, + srcElement, stack, stopPropagation, strong, style, styleFloat, sub, sup, + table, tag, tagName, target, tbody, td, test, text, textarea, tfoot, th, + that, thead, title, toLowerCase, toString, toUpperCase, tr, tt, type, u, + ul, unwatch, value, valueOf, var, visibility, watch, window, writeln, x, y +*/ + +var ADSAFE; +ADSAFE = (function () { + "use strict"; + + var adsafe_id; // The id of the current widget + var adsafe_lib; // The script libraries loaded by the current widget + +// These member names are banned from guest scripts. The ADSAFE.get and +// ADSAFE.put methods will not allow access to these properties. + + var banned = { + arguments: true, + callee: true, + caller: true, + constructor: true, + eval: true, + prototype: true, + stack: true, + unwatch: true, + valueOf: true, + watch: true + }; + + var cache_style_object; + var cache_style_node; + var defaultView = document.defaultView; + var ephemeral; + var flipflop; // Used in :even/:odd processing + var has_focus; + var hunter; // Set of hunter patterns + var interceptors = []; + + var makeableTagName = { + +// This is the whitelist of elements that may be created with the .tag(tagName) +// method. + + a: true, + abbr: true, + acronym: true, + address: true, + area: true, + b: true, + bdo: true, + big: true, + blockquote: true, + br: true, + button: true, + canvas: true, + caption: true, + center: true, + cite: true, + code: true, + col: true, + colgroup: true, + dd: true, + del: true, + dfn: true, + dir: true, + div: true, + dl: true, + dt: true, + em: true, + fieldset: true, + font: true, + form: true, + h1: true, + h2: true, + h3: true, + h4: true, + h5: true, + h6: true, + hr: true, + i: true, + img: true, + input: true, + ins: true, + kbd: true, + label: true, + legend: true, + li: true, + map: true, + menu: true, + object: true, + ol: true, + optgroup: true, + option: true, + p: true, + pre: true, + q: true, + samp: true, + select: true, + small: true, + span: true, + strong: true, + sub: true, + sup: true, + table: true, + tbody: true, + td: true, + textarea: true, + tfoot: true, + th: true, + thead: true, + tr: true, + tt: true, + u: true, + ul: true, + var: true + }; + var name; + var pecker; // set of pecker patterns + var result; + var star; + var the_range; + var value; + + +// The error function is called if there is a violation or confusion. +// It throws an exception. + + function error(message) { + ADSAFE.log("ADsafe error: " + (message || "ADsafe violation.")); + throw { + name: "ADsafe", + message: message || "ADsafe violation." + }; + } + + +// Some of JavaScript's implicit string conversions can grant extraordinary +// powers to untrusted code. So we use the string_check function to prevent +// such abuses. + + function string_check(string) { + if (typeof string !== "string") { + error("ADsafe string violation."); + } + return string; + } + + +// The object.hasOwnProperty method has a number of hazards. So we wrap it in +// the owns function. + + function owns(object, string) { + return ( + object + && typeof object === "object" + && Object.prototype.hasOwnProperty.call(object, string_check(string)) + ); + } + +// The reject functions enforce the restriction on property names. +// reject_property allows access only to objects and arrays. It does not allow +// use of the banned names, or names that are not strings and not numbers, +// or strings that start or end with _. + + function reject_name(name) { + return ( + typeof name !== "number" + && ( + typeof name !== "string" + || banned[name] + || name.charAt(0) === "_" + || name.slice(-1) === "_" + ) + ); + } + + + function reject_property(object, name) { + return typeof object !== "object" || reject_name(name); + } + + + function reject_global(that) { + if (that.window) { + error(); + } + } + + + function getStyleObject(node) { + +// The getStyleObject function returns the computed style object for a node. + + if (node === cache_style_node) { + return cache_style_object; + } + cache_style_node = node; + cache_style_object = ( + node.currentStyle + || defaultView.getComputedStyle(node, "") + ); + return cache_style_object; + } + + + function walkTheDOM(node, func, skip) { + +// Recursively traverse the DOM tree, starting with the node, in document +// source order, calling the func on each node visisted. + + if (!skip) { + func(node); + } + node = node.firstChild; + while (node) { + walkTheDOM(node, func); + node = node.nextSibling; + } + } + + + function purge_event_handlers(node) { + +// We attach all event handlers to an "___ on ___" property. The property name +// contains spaces to insure that there is no collision with HTML attribues. +// Keeping the handlers in a single property makes it easy to remove them +// all at once. Removal is required to avoid memory leakage on IE6 and IE7. + + walkTheDOM(node, function (node) { + if (node.tagName) { + node["___ on ___"] = null; + node.change = null; + } + }); + } + + + function parse_query(text, id) { + +// Convert a query string into an array of op/name/value selectors. +// A query string is a sequence of triples wrapped in brackets; or names, +// possibly prefixed by # . & > _, or :option, or * or /. A triple is a name, +// and operator (one of [=, [!=, [*=, [~=, [|=, [$=, or [^=) and a value. + +// If the id parameter is supplied, then the name following # must have the +// id as a prefix and must match the ADsafe rule for id: being all uppercase +// letters and digits with one underbar. + +// A name must be all lower case and may contain digits, -, or _. + + var match; // A match array + var query = []; // The resulting query array + var selector; + var qx = (id) + ? /^\s*(?:([*\/])|\[\s*([a-z][0-9a-z_\-]*)\s*(?:([!*~|$\^]?=)\s*([0-9A-Za-z_\-*%&;.\/:!]+)\s*)?\]|#\s*([A-Z]+_[A-Z0-9]+)|:\s*([a-z]+)|([.&_>+]?)\s*([a-z][0-9a-z\-]*))\s*/ + : /^\s*(?:([*\/])|\[\s*([a-z][0-9a-z_\-]*)\s*(?:([!*~|$\^]?=)\s*([0-9A-Za-z_\-*%&;.\/:!]+)\s*)?\]|#\s*([\-A-Za-z0-9_]+)|:\s*([a-z]+)|([.&_>+]?)\s*([a-z][0-9a-z\-]*))\s*/; + +// Loop over all of the selectors in the text. + + do { + +// The qx teases the components of one selector out of the text, ignoring +// whitespace. + +// match[0] the whole selector +// match[1] * / +// match[2] attribute name +// match[3] = != *= ~= |= $= ^= +// match[4] attribute value +// match[5] # id +// match[6] : option +// match[7] . & _ > + +// match[8] name + + match = qx.exec(string_check(text)); + if (!match) { + error("ADsafe: Bad query:" + text); + } + +// Make a selector object and stuff it in the query. + + if (match[1]) { + +// The selector is * or / + + selector = { + op: match[1] + }; + } else if (match[2]) { + +// The selector is in brackets. + + selector = (match[3]) + ? { + op: "[" + match[3], + name: match[2], + value: match[4] + } + : { + op: "[", + name: match[2] + }; + } else if (match[5]) { + +// The selector is an id. + + if ( + query.length > 0 + || match[5].length <= id.length + || match[5].slice(0, id.length) !== id + ) { + error("ADsafe: Bad query: " + text); + } + selector = { + op: "#", + name: match[5] + }; + +// The selector is a colon. + + } else if (match[6]) { + selector = { + op: ":" + match[6] + }; + +// The selector is one of > + . & _ or a naked tag name + + } else { + selector = { + op: match[7], + name: match[8] + }; + } + +// Add the selector to the query. + + query.push(selector); + +// Remove the selector from the text. If there is more text, have another go. + + text = text.slice(match[0].length); + } while (text); + return query; + } + + + hunter = { + +// These functions implement the hunter behaviors. + + "": function (node) { + var array; + var nodelist = node.getElementsByTagName(name); + var i; + var length; + +// getElementsByTagName produces a nodeList, which is one of the world's most +// inefficient data structures. It is so slow that JavaScript's pseudo arrays +// look terrifically swift by comparison. So we do the conversion. This is +// easily done on some browsers, less easily on others. + + try { + array = Array.prototype.slice.call(nodelist, 0); + result = (result.length) + ? result.concat(array) + : array; + } catch (ignore) { + length = nodelist.length; + for (i = 0; i < length; i += 1) { + result.push(nodelist[i]); + } + } + }, + "+": function (node) { + node = node.nextSibling; + name = name.toUpperCase(); + while (node && !node.tagName) { + node = node.nextSibling; + } + if (node && node.tagName === name) { + result.push(node); + } + }, + ">": function (node) { + node = node.firstChild; + name = name.toUpperCase(); + while (node) { + if (node.tagName === name) { + result.push(node); + } + node = node.nextSibling; + } + }, + "#": function () { + var n = document.getElementById(name); + if (n.tagName) { + result.push(n); + } + }, + "/": function (node) { + var nodes = node.childNodes; + var i; + var length = nodes.length; + for (i = 0; i < length; i += 1) { + result.push(nodes[i]); + } + }, + "*": function (node) { + star = true; + walkTheDOM(node, function (node) { + result.push(node); + }, true); + } + }; + + pecker = { + ".": function (node) { + var classy = " " + node.className + " "; + return classy.indexOf(" " + name + " ") >= 0; + }, + "&": function (node) { + return node.name === name; + }, + "_": function (node) { + return node.type === name; + }, + "[": function (node) { + return typeof node[name] === "string"; + }, + "[=": function (node) { + var member = node[name]; + return typeof member === "string" && member === value; + }, + "[!=": function (node) { + var member = node[name]; + return typeof member === "string" && member !== value; + }, + "[^=": function (node) { + var member = node[name]; + return typeof member === "string" && + member.slice(0, member.length) === value; + }, + "[$=": function (node) { + var member = node[name]; + return typeof member === "string" && + member.slice(-member.length) === value; + }, + "[*=": function (node) { + var member = node[name]; + return typeof member === "string" && + member.indexOf(value) >= 0; + }, + "[~=": function (node) { + var member = node[name]; + if (typeof member === "string") { + member = " " + member + " "; + return member.indexOf(" " + value + " ") >= 0; + } + }, + "[|=": function (node) { + var member = node[name]; + if (typeof member === "string") { + member = "-" + member + "-"; + return member.indexOf("-" + value + "-") >= 0; + } + }, + ":blur": function (node) { + return node !== has_focus; + }, + ":checked": function (node) { + return node.checked; + }, + ":disabled": function (node) { + return node.tagName && node.disabled; + }, + ":enabled": function (node) { + return node.tagName && !node.disabled; + }, + ":even": function (node) { + var f; + if (node.tagName) { + f = flipflop; + flipflop = !flipflop; + return f; + } + return false; + }, + ":focus": function (node) { + return node === has_focus; + }, + ":hidden": function (node) { + return node.tagName && getStyleObject(node).visibility !== "visible"; + }, + ":odd": function (node) { + if (node.tagName) { + flipflop = !flipflop; + return flipflop; + } + return false; + }, + ":tag": function (node) { + return node.tagName; + }, + ":text": function (node) { + return node.nodeName === "#text"; + }, + ":trim": function (node) { + return node.nodeName !== "#text" || (/\W/.test(node.nodeValue)); + }, + ":unchecked": function (node) { + return node.tagName && !node.checked; + }, + ":visible": function (node) { + return node.tagName && getStyleObject(node).visibility === "visible"; + } + }; + + + function quest(query, nodes) { + var selector; + var func; + var i; + var j; + +// Step through each selector. + + for (i = 0; i < query.length; i += 1) { + selector = query[i]; + name = selector.name; + func = hunter[selector.op]; + +// There are two kinds of selectors: hunters and peckers. If this is a hunter, +// loop through the the nodes, passing each node to the hunter function. +// Accumulate all the nodes it finds. + + if (typeof func === "function") { + if (star) { + error( + "ADsafe: Query violation: *" + + selector.op + + (selector.name || "") + ); + } + result = []; + for (j = 0; j < nodes.length; j += 1) { + func(nodes[j]); + } + } else { + +// If this is a pecker, get its function. There is a special case for +// the :first and :rest selectors because they are so simple. + + value = selector.value; + flipflop = false; + func = pecker[selector.op]; + if (typeof func !== "function") { + switch (selector.op) { + case ":first": + result = nodes.slice(0, 1); + break; + case ":rest": + result = nodes.slice(1); + break; + default: + error("ADsafe: Query violation: :" + selector.op); + } + } else { + +// For the other selectors, make an array of nodes that are filtered by +// the pecker function. + + result = []; + for (j = 0; j < nodes.length; j += 1) { + if (func(nodes[j])) { + result.push(nodes[j]); + } + } + } + } + nodes = result; + } + return result; + } + + + function make_root(root, id) { + + if (id) { + if (root.tagName !== "DIV") { + error("ADsafe: Bad node."); + } + } else { + if (root.tagName !== "BODY") { + error("ADsafe: Bad node."); + } + } + +// A Bunch is a container that holds zero or more dom nodes. +// It has many useful methods. + + function Bunch(nodes) { + this.___nodes___ = nodes; + this.___star___ = star && nodes.length > 1; + star = false; + } + + var allow_focus = true; + var dom; + var dom_event = function (ev) { + var key; + var target; + var that; + var the_event; + var the_target; + var the_actual_event = ev || event; + var type = the_actual_event.type; + +// Get the target node and wrap it in a bunch. + + the_target = the_actual_event.target || the_actual_event.srcElement; + target = new Bunch([the_target]); + that = target; + +// Use the PPK hack to make focus bubbly on IE. +// When a widget has focus, it can use the focus method. + + switch (type) { + case "mousedown": + allow_focus = true; + if (document.selection) { + the_range = document.selection.createRange(); + } + break; + case "focus": + case "focusin": + allow_focus = true; + has_focus = the_target; + the_actual_event.cancelBubble = false; + type = "focus"; + break; + case "blur": + case "focusout": + allow_focus = false; + has_focus = null; + type = "blur"; + break; + case "keypress": + allow_focus = true; + has_focus = the_target; + key = String.fromCharCode( + the_actual_event.charCode || the_actual_event.keyCode + ); + switch (key) { + case "\u000d": + case "\u000a": + type = "enterkey"; + break; + case "\u001b": + type = "escapekey"; + break; + } + break; + +// This is a workaround for Safari. + + case "click": + allow_focus = true; + break; + } + if ( + the_actual_event.cancelBubble + && the_actual_event.stopPropagation + ) { + the_actual_event.stopPropagation(); + } + +// Make the event object. + + the_event = { + altKey: the_actual_event.altKey, + ctrlKey: the_actual_event.ctrlKey, + bubble: function () { + +// Bubble up. Get the parent of that node. It becomes the new that. +// getParent throws when bubbling is not possible. + + try { + var parent = that.getParent(); + var b = parent.___nodes___[0]; + that = parent; + the_event.that = that; + +// If that node has an event handler, fire it. Otherwise, bubble up. + + if ( + b["___ on ___"] && b["___ on ___"][type] + ) { + that.fire(the_event); + } else { + the_event.bubble(); + } + } catch (e) { + error(e); + } + }, + key: key, + preventDefault: function () { + if (the_actual_event.preventDefault) { + the_actual_event.preventDefault(); + } + the_actual_event.returnValue = false; + }, + shiftKey: the_actual_event.shiftKey, + target: target, + that: that, + type: type, + x: the_actual_event.clientX, + y: the_actual_event.clientY + }; + +// If the target has event handlers, then fire them. Otherwise, bubble up. + + if ( + the_target["___ on ___"] + && the_target["___ on ___"][the_event.type] + ) { + target.fire(the_event); + } else { + while (true) { + the_target = the_target.parentNode; + if (!the_target) { + break; + } + if ( + the_target["___ on ___"] + && the_target["___ on ___"][the_event.type] + ) { + that = new Bunch([the_target]); + the_event.that = that; + that.fire(the_event); + break; + } + if (the_target["___adsafe root___"]) { + break; + } + } + } + if (the_event.type === "escapekey") { + if (ephemeral) { + ephemeral.remove(); + } + ephemeral = null; + } + that = null; + the_actual_event = null; + the_event = null; + the_target = null; + return; + }; + +// Mark the node as a root. This prevents event bubbling from propagating +// past it. + + root["___adsafe root___"] = "___adsafe root___"; + + Bunch.prototype = { + append: function (appendage) { + reject_global(this); + var b = this.___nodes___; + var flag = false; + var i; + var j; + var node; + var rep; + if (b.length === 0 || !appendage) { + return this; + } + if (Array.isArray(appendage)) { + if (appendage.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + rep = appendage[i].___nodes___; + for (j = 0; j < rep.length; j += 1) { + b[i].appendChild(rep[j]); + } + } + } else { + if (typeof appendage !== "string") { + rep = appendage.___nodes___; + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (rep) { + for (j = 0; j < rep.length; j += 1) { + node.appendChild((flag) + ? rep[j].cloneNode(true) + : rep[j]); + } + flag = true; + } else { + node.appendChild(document.createTextNode(appendage)); + } + } + } + return this; + }, + blur: function () { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + has_focus = null; + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.blur) { + node.blur(); + } + } + return this; + }, + check: function (value) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.checked = !!value[i]; + } + } + } else { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.checked = !!value; + } + } + } + return this; + }, + "class": function (value) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + if (/url/i.test(string_check(value[i]))) { + error("ADsafe error."); + } + node = b[i]; + if (node.tagName) { + node.className = value[i]; + } + } + } else { + if (/url/i.test(string_check(value))) { + error("ADsafe error."); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.className = value; + } + } + } + return this; + }, + clone: function (deep, n) { + var a = []; + var b = this.___nodes___; + var c; + var i; + var j; + var k = n || 1; + for (i = 0; i < k; i += 1) { + c = []; + for (j = 0; j < b.length; j += 1) { + c.push(b[j].cloneNode(deep)); + } + a.push(new Bunch(c)); + } + return (n) + ? a + : a[0]; + }, + count: function () { + reject_global(this); + return this.___nodes___.length; + }, + each: function (func) { + reject_global(this); + var b = this.___nodes___; + var i; + if (typeof func === "function") { + for (i = 0; i < b.length; i += 1) { + func(new Bunch([b[i]])); + } + return this; + } + error(); + }, + empty: function () { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + while (node.firstChild) { + purge_event_handlers(node); + node.removeChild(node.firstChild); + } + } + } else { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + while (node.firstChild) { + purge_event_handlers(node); + node.removeChild(node.firstChild); + } + } + } + return this; + }, + enable: function (enable) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(enable)) { + if (enable.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + "-" + + enable.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.disabled = !enable[i]; + } + } + } else { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.disabled = !enable; + } + } + } + return this; + }, + ephemeral: function () { + reject_global(this); + if (ephemeral) { + ephemeral.remove(); + } + ephemeral = this; + return this; + }, + explode: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = new Bunch([b[i]]); + } + return a; + }, + fire: function (event) { + + // Fire an event on an object. The event can be either + // a string containing the name of the event, or an + // object containing a type property containing the + // name of the event. Handlers registered by the "on" + // method that match the event name will be invoked. + + reject_global(this); + var array; + var b; + var i; + var j; + var n; + var node; + var on; + var type; + + if (typeof event === "string") { + type = event; + event = {type: type}; + } else if (typeof event === "object") { + type = event.type; + } else { + error(); + } + b = this.___nodes___; + n = b.length; + for (i = 0; i < n; i += 1) { + node = b[i]; + on = node["___ on ___"]; + + // If an array of handlers exist for this event, then + // loop through it and execute the handlers in order. + + if (owns(on, type)) { + array = on[type]; + for (j = 0; j < array.length; j += 1) { + + // Invoke a handler. Pass the event object. + + array[j].call(this, event); + } + } + } + return this; + }, + focus: function () { + reject_global(this); + var b = this.___nodes___; + if (b.length > 0 && allow_focus) { + has_focus = b[0].focus(); + return this; + } + error(); + }, + fragment: function () { + reject_global(this); + return new Bunch([document.createDocumentFragment()]); + }, + getCheck: function () { + return this.getChecks()[0]; + }, + getChecks: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i].checked; + } + return a; + }, + getClass: function () { + return this.getClasses()[0]; + }, + getClasses: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i].className; + } + return a; + }, + getMark: function () { + return this.getMarks()[0]; + }, + getMarks: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i]["_adsafe mark_"]; + } + return a; + }, + getName: function () { + return this.getNames()[0]; + }, + getNames: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i].name; + } + return a; + }, + getOffsetHeight: function () { + return this.getOffsetHeights()[0]; + }, + getOffsetHeights: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i].offsetHeight; + } + return a; + }, + getOffsetWidth: function () { + return this.getOffsetWidths()[0]; + }, + getOffsetWidths: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i].offsetWidth; + } + return a; + }, + getParent: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + var n; + for (i = 0; i < b.length; i += 1) { + n = b[i].parentNode; + if (n["___adsafe root___"]) { + error("ADsafe parent violation."); + } + a[i] = n; + } + return new Bunch(a); + }, + getSelection: function () { + reject_global(this); + var b = this.___nodes___; + var end; + var node; + var start; + var range; + if (b.length === 1 && allow_focus) { + node = b[0]; + if (typeof node.selectionStart === "number") { + start = node.selectionStart; + end = node.selectionEnd; + return node.value.slice(start, end); + } + range = node.createTextRange(); + range.expand("textedit"); + if (range.inRange(the_range)) { + return the_range.text; + } + } + return null; + }, + getStyle: function (name) { + return this.getStyles(name)[0]; + }, + getStyles: function (name) { + reject_global(this); + if (reject_name(name)) { + error("ADsafe style violation."); + } + var a = []; + var b = this.___nodes___; + var i; + var node; + var s; + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + s = (name !== "float") + ? getStyleObject(node)[name] + : getStyleObject(node).cssFloat + || getStyleObject(node).styleFloat; + if (typeof s === "string") { + a[i] = s; + } + } + } + return a; + }, + getTagName: function () { + return this.getTagNames()[0]; + }, + getTagNames: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + var tagName; + for (i = 0; i < b.length; i += 1) { + tagName = b[i].tagName; + a[i] = (typeof tagName === "string") + ? tagName.toLowerCase() + : tagName; + } + return a; + }, + getTitle: function () { + return this.getTitles()[0]; + }, + getTitles: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i].title; + } + return a; + }, + getValue: function () { + return this.getValues()[0]; + }, + getValues: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + var node; + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.nodeName === "#text") { + a[i] = node.nodeValue; + } else if (node.tagName && node.type !== "password") { + a[i] = node.value; + if ( + !a[i] + && node.firstChild + && node.firstChild.nodeName === "#text" + ) { + a[i] = node.firstChild.nodeValue; + } + } + } + return a; + }, + indeterminate: function (value) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.indeterminate = !!value[i]; + } + } + } else { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.indeterminate = !!value; + } + } + } + return this; + }, + klass: function (value) { + return this.class(value); + }, + mark: function (value) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node["_adsafe mark_"] = String(value[i]); + } + } + } else { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node["_adsafe mark_"] = String(value); + } + } + } + return this; + }, + off: function (type) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (typeof type === "string") { + if (typeof node["___ on ___"] === "object") { + node["___ on ___"][type] = null; + } + } else { + node["___ on ___"] = null; + } + } + return this; + }, + on: function (type, func) { + reject_global(this); + if (typeof type !== "string" || typeof func !== "function") { + error(); + } + + var b = this.___nodes___; + var i; + var node; + var on; + var ontype; + for (i = 0; i < b.length; i += 1) { + node = b[i]; + +// The change event does not propogate, so we must put the handler on the +// instance. + + if (type === "change") { + ontype = "on" + type; + if (node[ontype] !== dom_event) { + node[ontype] = dom_event; + } + } + +// Register an event. Put the function in a handler array, making one if it +// doesn't yet exist for this type on this node. + + on = node["___ on ___"]; + if (!on) { + on = {}; + node["___ on ___"] = on; + } + if (owns(on, type)) { + on[type].push(func); + } else { + on[type] = [func]; + } + } + return this; + }, + protect: function () { + reject_global(this); + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + b[i]["___adsafe root___"] = "___adsafe root___"; + } + return this; + }, + q: function (text) { + reject_global(this); + star = this.___star___; + return new Bunch( + quest(parse_query(string_check(text), id), this.___nodes___) + ); + }, + remove: function () { + reject_global(this); + this.replace(); + }, + replace: function (replacement) { + reject_global(this); + var b = this.___nodes___; + var flag = false; + var i; + var j; + var newnode; + var node; + var parent; + var rep; + if (b.length === 0) { + return; + } + for (i = 0; i < b.length; i += 1) { + purge_event_handlers(b[i]); + } + if ( + !replacement || replacement.length === 0 + || ( + replacement.___nodes___ + && replacement.___nodes___.length === 0 + ) + ) { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + purge_event_handlers(node); + if (node.parentNode) { + node.parentNode.removeChild(node); + } + } + } else if (Array.isArray(replacement)) { + if (replacement.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + parent = node.parentNode; + purge_event_handlers(node); + if (parent) { + rep = replacement[i].___nodes___; + if (rep.length > 0) { + newnode = rep[0]; + parent.replaceChild(newnode, node); + for (j = 1; j < rep.length; j += 1) { + node = newnode; + newnode = rep[j]; + parent.insertBefore(newnode, node.nextSibling); + } + } else { + parent.removeChild(node); + } + } + } + } else { + rep = replacement.___nodes___; + for (i = 0; i < b.length; i += 1) { + node = b[i]; + purge_event_handlers(node); + parent = node.parentNode; + if (parent) { + newnode = (flag) + ? rep[0].cloneNode(true) + : rep[0]; + parent.replaceChild(newnode, node); + for (j = 1; j < rep.length; j += 1) { + node = newnode; + newnode = (flag) + ? rep[j].clone(true) + : rep[j]; + parent.insertBefore(newnode, node.nextSibling); + } + flag = true; + } + } + } + return this; + }, + select: function () { + reject_global(this); + var b = this.___nodes___; + if (b.length < 1 || !allow_focus) { + error(); + } + b[0].focus(); + b[0].select(); + return this; + }, + selection: function (string) { + reject_global(this); + string_check(string); + var b = this.___nodes___; + var end; + var node; + var old; + var start; + var range; + if (b.length === 1 && allow_focus) { + node = b[0]; + if (typeof node.selectionStart === "number") { + start = node.selectionStart; + end = node.selectionEnd; + old = node.value; + node.value = old.slice(0, start) + string + old.slice(end); + node.selectionStart = start + string.length; + node.selectionEnd = start + string.length; + node.focus(); + } else { + range = node.createTextRange(); + range.expand("textedit"); + if (range.inRange(the_range)) { + the_range.select(); + the_range.text = string; + the_range.select(); + } + } + } + return this; + }, + style: function (name, value) { + reject_global(this); + if (reject_name(name)) { + error("ADsafe style violation."); + } + if (value === undefined || (/url/i.test(string_check(value)))) { + error(); + } + var b = this.___nodes___; + var i; + var node; + var v; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + v = string_check(value[i]); + if (/url/i.test(v)) { + error(); + } + if (node.tagName) { + if (name !== "float") { + node.style[name] = v; + } else { + node.style.cssFloat = v; + node.style.styleFloat = v; + } + } + } + } else { + v = string_check(value); + if (/url/i.test(v)) { + error(); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + if (name !== "float") { + node.style[name] = v; + } else { + node.style.cssFloat = v; + node.style.styleFloat = v; + } + } + } + } + return this; + }, + tag: function (tag, type, name) { + reject_global(this); + var node; + if (typeof tag !== "string") { + error(); + } + if (makeableTagName[tag] !== true) { + error("ADsafe: Bad tag: " + tag); + } + node = document.createElement(tag); + if (name) { + node.autocomplete = "off"; + node.name = string_check(name); + } + if (type) { + node.type = string_check(type); + } + return new Bunch([node]); + }, + text: function (text) { + reject_global(this); + var a; + var i; + if (Array.isArray(text)) { + a = []; + for (i = 0; i < text.length; i += 1) { + a[i] = document.createTextNode(string_check(text[i])); + } + return new Bunch(a); + } + return new Bunch([document.createTextNode(string_check(text))]); + }, + title: function (value) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.title = string_check(value[i]); + } + } + } else { + string_check(value); + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.title = value; + } + } + } + return this; + }, + value: function (value) { + reject_global(this); + if (value === undefined) { + error(); + } + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value) && b.length === value.length) { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + if (node.type !== "password") { + if (typeof node.value === "string") { + node.value = value[i]; + } else { + while (node.firstChild) { + purge_event_handlers(node.firstChild); + node.removeChild(node.firstChild); + } + node.appendChild(document.createTextNode( + String(value[i]) + )); + } + } + } else if (node.nodeName === "#text") { + node.nodeValue = String(value[i]); + } + } + } else { + value = String(value); + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + if ( + node.tagName !== "BUTTON" + && typeof node.value === "string" + ) { + node.value = value; + } else { + while (node.firstChild) { + purge_event_handlers(node.firstChild); + node.removeChild(node.firstChild); + } + node.appendChild(document.createTextNode(value)); + } + } else if (node.nodeName === "#text") { + node.nodeValue = value; + } + } + } + return this; + } + }; + +// Return an ADsafe dom object. + + dom = { + append: function (bunch) { + var b = (typeof bunch === "string") + ? [document.createTextNode(bunch)] + : bunch.___nodes___; + var i; + var n; + for (i = 0; i < b.length; i += 1) { + n = b[i]; + if (typeof n === "string" || typeof n === "number") { + n = document.createTextNode(String(n)); + } + root.appendChild(n); + } + return dom; + }, + combine: function (array) { + if (!array || !array.length) { + error("ADsafe: Bad combination."); + } + var b = array[0].___nodes___; + var i; + for (i = 0; i < array.length; i += 1) { + b = b.concat(array[i].___nodes___); + } + return new Bunch(b); + }, + count: function () { + return 1; + }, + ephemeral: function (bunch) { + if (ephemeral) { + ephemeral.remove(); + } + ephemeral = bunch; + return dom; + }, + fragment: function () { + return new Bunch([document.createDocumentFragment()]); + }, + prepend: function (bunch) { + var b = bunch.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + root.insertBefore(b[i], root.firstChild); + } + return dom; + }, + q: function (text) { + star = false; + var query = parse_query(text, id); + if (typeof hunter[query[0].op] !== "function") { + error("ADsafe: Bad query: " + query[0]); + } + return new Bunch(quest(query, [root])); + }, + remove: function () { + purge_event_handlers(root); + root.parent.removeElement(root); + root = null; + }, + row: function (values) { + var tr = document.createElement("tr"); + var td; + var i; + for (i = 0; i < values.length; i += 1) { + td = document.createElement("td"); + td.appendChild(document.createTextNode(String(values[i]))); + tr.appendChild(td); + } + return new Bunch([tr]); + }, + tag: function (tag, type, name) { + var node; + if (typeof tag !== "string") { + error(); + } + if (makeableTagName[tag] !== true) { + error("ADsafe: Bad tag: " + tag); + } + node = document.createElement(tag); + if (name) { + node.autocomplete = "off"; + node.name = name; + } + if (type) { + node.type = type; + } + return new Bunch([node]); + }, + text: function (text) { + var a; + var i; + if (Array.isArray(text)) { + a = []; + for (i = 0; i < text.length; i += 1) { + a[i] = document.createTextNode(string_check(text[i])); + } + return new Bunch(a); + } + return new Bunch([document.createTextNode(string_check(text))]); + } + }; + + if (typeof root.addEventListener === "function") { + root.addEventListener("focus", dom_event, true); + root.addEventListener("blur", dom_event, true); + root.addEventListener("mouseover", dom_event, true); + root.addEventListener("mouseout", dom_event, true); + root.addEventListener("mouseup", dom_event, true); + root.addEventListener("mousedown", dom_event, true); + root.addEventListener("mousemove", dom_event, true); + root.addEventListener("click", dom_event, true); + root.addEventListener("dblclick", dom_event, true); + root.addEventListener("keypress", dom_event, true); + } else { + root.onclick = dom_event; + root.ondblclick = dom_event; + root.onfocusin = dom_event; + root.onfocusout = dom_event; + root.onkeypress = dom_event; + root.onmouseout = dom_event; + root.onmousedown = dom_event; + root.onmousemove = dom_event; + root.onmouseover = dom_event; + root.onmouseup = dom_event; + } + return [dom, Bunch.prototype]; + } + + +// Return the ADSAFE object. + + return { + create: function (o) { + reject_global(o); + return Object.create(o); + }, + +// ADSAFE.get retrieves a value from an object. + + get: function (object, name) { + reject_global(object); + if (!reject_property(object, name)) { + return object[name]; + } + error(); + }, + +// ADSAFE.go allows a guest widget to get access to a wrapped dom node and +// approved ADsafe libraries. It is passed an id and a function. The function +// will be passed the wrapped dom node and an object containing the libraries. + + go: function (id, f) { + var dom; + var fun; + var root; + var i; + var scripts; + +// If ADSAFE.id was called, the id better match. + + if (adsafe_id && adsafe_id !== id) { + error(); + } + +// Get the dom node for the widget's div container. + + root = document.getElementById(id); + if (root.tagName !== "DIV") { + error(); + } + adsafe_id = null; + +// Delete the scripts held in the div. They have all run, so we don't need +// them any more. If the div had no scripts, then something is wrong. +// This provides some protection against mishaps due to weakness in the +// document.getElementById function. + + scripts = root.getElementsByTagName("script"); + i = scripts.length - 1; + if (i < 0) { + error(); + } + do { + root.removeChild(scripts[i]); + i -= 1; + } while (i >= 0); + root = make_root(root, id); + dom = root[0]; + +// If the page has registered interceptors, call then. + + for (i = 0; i < interceptors.length; i += 1) { + fun = interceptors[i]; + if (typeof fun === "function") { + try { + fun(id, dom, adsafe_lib, root[1]); + } catch (e1) { + ADSAFE.log(e1); + } + } + } + +// Call the supplied function. + + try { + f(dom, adsafe_lib); + } catch (e2) { + ADSAFE.log(e2); + } + root = null; + adsafe_lib = null; + }, + +// ADSAFE.has returns true if the object contains an own property with the +// given name. + + has: function (object, name) { + return owns(object, name); + }, + +// ADSAFE.id allows a guest widget to indicate that it wants to load +// ADsafe approved libraries. + + id: function (id) { + +// Calls to ADSAFE.id must be balanced with calls to ADSAFE.go. +// Only one id can be active at a time. + + if (adsafe_id) { + error(); + } + adsafe_id = id; + adsafe_lib = {}; + }, + +// ADSAFE.isArray returns true if the operand is an array. + + isArray: Array.isArray || function (value) { + return Object.prototype.toString.apply(value) === "[object Array]"; + }, + +// ADSAFE.keys returns an array of keys. + + keys: Object.keys, + +// ADSAFE.later calls a function at a later time. + + later: function (func, timeout) { + if (typeof func === "function") { + setTimeout(func, timeout || 0); + } else { + error(); + } + }, + +// ADSAFE.lib allows an approved ADsafe library to make itself available +// to a widget. The library provides a name and a function. The result of +// calling that function will be made available to the widget via the name. + + lib: function (name, f) { + if (!adsafe_id || reject_name(name)) { + error("ADsafe lib violation."); + } + adsafe_lib[name] = f(adsafe_lib); + }, + +// ADSAFE.log is a debugging aid that spams text to the browser's log. +// Overwrite this function to send log matter somewhere else. + + log: function log(s) { + if (window.console) { + console.log(s); + } else if (typeof Debug === "object") { + Debug.writeln(s); /* IE */ + } else { + opera.postError(s); /* Opera */ + } + }, + +// ADSAFE.remove deletes a value from an object. + + remove: function (object, name) { + if (!reject_property(object, name)) { + delete object[name]; + return; + } + error(); + }, + +// ADSAFE.set stores a value in an object. + + set: function (object, name, value) { + reject_global(object); + if (!reject_property(object, name)) { + object[name] = value; + return; + } + error(); + }, + +// ADSAFE._intercept allows the page to register a function that will +// see the widget's capabilities. + + _intercept: function (f) { + interceptors.push(f); + } + + }; +}()); diff --git a/branch.es5_web/init_ui.js b/branch.es5_web/init_ui.js new file mode 100644 index 000000000..509c81d72 --- /dev/null +++ b/branch.es5_web/init_ui.js @@ -0,0 +1,240 @@ +// init_ui.js +// 2012-05-09 + +// This is the web browser companion to fulljslint.js. It is an ADsafe +// lib file that implements a web ui by adding behavior to the widget's +// html tags. + +// It stores a function in lib.init_ui. Calling that function will +// start up the JSLint widget ui. + +// option = {adsafe: true, fragment: false} + +/*properties + cookie, each, edition, forEach, get, getStyle, getTitle, getValue, + indent, isArray, join, jslint, keys, klass, length, lib, maxerr, maxlen, on, + predef, preventDefault, push, q, select, set, split, style, target, value +*/ + +ADSAFE.lib("init_ui", function (lib) { + 'use strict'; + + return function (dom) { + var edition = dom.q('#JSLINT_EDITION'), + errors = dom.q('#JSLINT_ERRORS'), + errors_div = errors.q('>div'), + indent = dom.q('#JSLINT_INDENT'), + jslint_dir = dom.q('#JSLINT_JSLINT'), + jslint_str = jslint_dir.q('>textarea'), + maxerr = dom.q('#JSLINT_MAXERR'), + maxlen = dom.q('#JSLINT_MAXLEN'), + option = lib.cookie.get(), + options = dom.q('#JSLINT_OPTIONS'), + predefined = dom.q('#JSLINT_PREDEF'), + properties = dom.q('#JSLINT_PROPERTIES'), + properties_str = properties.q('>textarea'), + report = dom.q('#JSLINT_REPORT'), + report_div = report.q('>div'), + source = dom.q('#JSLINT_SOURCE'), + source_str = source.q('>textarea'), + tristate = {}; + + function clear() { + errors.style('display', 'none'); + report.style('display', 'none'); + properties.style('display', 'none'); + errors_div.value(''); + report_div.value(''); + properties_str.value(''); + source_str.select(''); + } + + function clear_all() { + source_str.value(''); + clear(); + } + + function preventDefault(e) { + return e.preventDefault(); + } + + function show_jslint_directive() { + +// Build and display a /*jslint*/ control comment. +// The comment can be copied into a .js file. + + var a = [], result; + + ADSAFE.keys(tristate).forEach(function (title) { + var value = ADSAFE.get(option, title); + if (typeof value === 'boolean') { + a.push(title + ': ' + String(value)); + } + }); + if (typeof option.indent === 'number' && option.indent >= 0) { + a.push('indent: ' + String(option.indent)); + } + if (typeof option.maxerr === 'number' && option.maxerr >= 0) { + a.push('maxerr: ' + String(option.maxerr)); + } + if (typeof option.maxlen === 'number' && option.maxlen >= 0) { + a.push('maxlen: ' + String(option.maxlen)); + } + result = '/*jslint ' + a.join(', ') + ' */'; + jslint_str.value(result); + jslint_dir.style('display', result.length > 12 ? 'block' : 'none'); + +// Make a JSON cookie of the option object. + + lib.cookie.set(option); + } + + function wire_tristate(bunch) { + bunch.each(function (b) { + var title = b.getTitle(), + dfn = b.q('>button'), + label = b.q('>var'); + ADSAFE.set(tristate, title, label); + + function mouseover(e) { + dfn.style('backgroundColor', 'cornflowerblue'); + e.preventDefault(); + } + + function mouseout() { + dfn.style('backgroundColor', 'lightsteelblue'); + } + + function mousedown() { + dfn.style('backgroundColor', 'steelblue'); + } + + function click() { + var state = ADSAFE.get(option, title); + if (state === true) { + ADSAFE.set(option, title, false); + label + .value('false') + .klass('false'); + } else if (state === false) { + ADSAFE.set(option, title, undefined); + label + .value('default') + .klass(''); + } else { + ADSAFE.set(option, title, true); + label + .value('true') + .klass('true'); + } + show_jslint_directive(); + } + + dfn + .on('mouseover', mouseover) + .on('mouseout', mouseout) + .on('mousedown', mousedown) + .on('mouseup', mouseover) + .on('mousemove', preventDefault) + .on('click', click); + label + .on('mouseover', mouseover) + .on('mouseout', mouseout) + .on('mousedown', mousedown) + .on('mouseup', mouseover) + .on('mousemove', preventDefault) + .on('click', click); + mouseout(); + }); + } + + + function show_options() { + indent.value(String(option.indent || '')); + maxlen.value(String(option.maxlen || '')); + maxerr.value(String(option.maxerr || '')); + predefined.value(ADSAFE.isArray(option.predef) + ? option.predef.join(' ') + : ''); + ADSAFE.keys(tristate).forEach(function (title) { + var value = ADSAFE.get(option, title); + if (typeof value === 'boolean') { + ADSAFE.get(tristate, title) + .klass(String(value)) + .value(String(value)); + } else { + ADSAFE.get(tristate, title) + .klass('') + .value('default'); + } + }); + show_jslint_directive(); + } + + function clear_options() { + option = {}; + show_options(); + } + + function update_number(event) { + var value = event.target.getValue(); + if (value.length === 0 || +value < 0 || !isFinite(value)) { + value = ''; + ADSAFE.set(option, event.target.getTitle(), ''); + } else { + ADSAFE.set(option, event.target.getTitle(), +value); + } + event.target.value(String(value)); + show_jslint_directive(); + } + + function update_list(event) { + var value = event.target.getValue().split(/[\s,;'"]+/); + ADSAFE.set(option, event.target.getTitle(), value); + event.target.value(value.join(' ')); + show_jslint_directive(); + } + + +// Restore the options from a JSON cookie. + + if (!option || typeof option !== 'object') { + option = {}; + } + wire_tristate(options.q('div.tristate>div[title]')); + source.q('>button').on('click', clear_all); + dom.q('#JSLINT_BUTTON').on('click', function () { + clear(); + if (lib.jslint(source_str.getValue(), option, + errors_div, report_div, properties_str, edition)) { + errors.style('display', 'block'); + } + report.style('display', 'block'); + if (properties_str.getValue().length > 21) { + properties.style('display', 'block'); + } + source_str.select(); + return false; + }); + errors.q('>button').on('click', clear); + report.q('>button').on('click', clear); + properties.q('>button').on('click', function () { + properties_str.select(); + }); + options.q('>button').on('click', clear_options); + jslint_dir.q('>button').on('click', function () { + jslint_str.select(); + }); + clear(); + show_options(); + +// Display the edition. + + edition.value('Edition ' + lib.edition()); + + indent.on('change', update_number); + maxerr.on('change', update_number); + maxlen.on('change', update_number); + predefined.on('change', update_list); + }; +}); diff --git a/branch.es5_web/intercept.js b/branch.es5_web/intercept.js new file mode 100644 index 000000000..77b026db1 --- /dev/null +++ b/branch.es5_web/intercept.js @@ -0,0 +1,102 @@ +// intercept.js +// 2012-05-09 + +// This file makes it possible for JSLint to run as an ADsafe widget by +// adding lib features. + +// It provides a JSON cookie facility. Each widget is allowed to create a +// single JSON cookie. + +// It also provides a way for the widget to call JSLint. The widget cannot +// call JSLint directly because it is loaded as a global variable. I don't +// want to change that because other versions of JSLint depend on that. + +// And it provides access to the syntax tree that JSLint constructed. + +/*jslint nomen: true, unparam: true */ + +/*global ADSAFE, document, JSLINT */ + +/*properties + ___nodes___, _intercept, cookie, data, edition, error_report, get, getTime, + indexOf, innerHTML, jslint, length, now, parse, properties_report, property, + replace, report, set, setTime, slice, stringify, toGMTString, value +*/ + +ADSAFE._intercept(function (id, dom, lib, bunch) { + 'use strict'; + +// Give every widget access to a JSON cookie. The name of the cookie will be +// the same as the id of the widget. + + lib.cookie = { + get: function () { + +// Get the raw cookie. Extract this widget's cookie, and parse it. + + var c = ' ' + document.cookie + ';', + s = c.indexOf((' ' + id + '=')), + v; + try { + if (s >= 0) { + s += id.length + 2; + v = JSON.parse(c.slice(s, c.indexOf(';', s))); + } + } catch (ignore) {} + return v; + }, + set: function (value) { + +// Set a cookie. It must be under 2000 in length. Escapify equal sign +// and semicolon if necessary. + + var d, + j = JSON.stringify(value) + .replace(/[=]/g, '\\u003d') + .replace(/[;]/g, '\\u003b'); + + if (j.length < 2000) { + d = new Date(); + d.setTime(d.getTime() + 1e9); + document.cookie = id + "=" + j + ';expires=' + d.toGMTString(); + } + } + }; +}); + +ADSAFE._intercept(function (id, dom, lib, bunch) { + 'use strict'; + +// Give only the JSLINT_ widget access to the JSLINT function. +// We add a jslint function to its lib that calls JSLINT and +// then gets the reports, and stuffs the results into nodes +// provided by the widget. We do not trust a widget to stuff +// just any HTML content. + +// We also add an edition function to the lib that gives the +// widget access to the current edition string. + + var now = Date.now || function () { + return new Date().getTime(); + }; + + if (id === 'JSLINT_') { + lib.jslint = function (source, options, errors, report, properties, edition) { + var after, before = now(), data, errtext, protext, retext; + JSLINT(source, options); + data = JSLINT.data(); + errtext = JSLINT.error_report(data); + retext = JSLINT.report(data); + protext = JSLINT.properties_report(JSLINT.property); + after = now(); + edition.value(((after - before) / 1000) + ' seconds.'); + errors.___nodes___[0].innerHTML = errtext; + report.___nodes___[0].innerHTML = retext; + properties.value(protext); + return errtext !== ''; + }; + lib.edition = function () { + return JSLINT.edition; + }; + } +}); diff --git a/branch.es5_web/jslint.html b/branch.es5_web/jslint.html new file mode 100644 index 000000000..3a95a9d9c --- /dev/null +++ b/branch.es5_web/jslint.html @@ -0,0 +1,357 @@ + + + +JSLint, The JavaScript Code Quality Tool + + + + +
    +
     
    + +
    + +
    + The JavaScript Code Quality Tool
    +
    +
    +

    Source

    + +
    +
    + + + +

    Options

    +
    Assume... +
    a browser
    +
    CouchDB
    +
    console,alert, ...
    +
    Node.js
    +
    Rhino
    +
    +
    Stop on first error
    +
    +
    Tolerate... +
    assignment expressions
    +
    bitwise operators
    +
    Google Closure
    +
    continue
    +
    debugger statements
    +
    == and !=
    +
    +
    Tolerate... +
    eval
    +
    unfiltered for in
    +
    uncapitalized constructors
    +
    dangling _ in identifiers
    +
    ++ and --
    +
    . and [^ ... ] in /RegExp/
    +
    unused parameters
    +
    +
    Tolerate... +
    missing 'use strict' pragma
    +
    stupidity
    +
    inefficient subscripting
    +
    TODO comments
    +
    many var statements per function
    +
    messy white space
    +
    +
    + + +
    + + +
    + + +
    + +
    +

    JSLint Directive

    + +
    + + + + + +
    +
    +

    Copyright 2002 Douglas + Crockford. All + Rights Reserved Wrrrldwide and Beyond!
    + Code + Conventions for the JavaScript Programming Language.
    + Join + the JSLint Community.

    + + + + + +
    + diff --git a/branch.es5_web/jslint.js b/branch.es5_web/jslint.js new file mode 100644 index 000000000..0c6a94328 --- /dev/null +++ b/branch.es5_web/jslint.js @@ -0,0 +1,4288 @@ +// jslint.js +// 2014-07-08 + +// Copyright (c) 2002 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// WARNING: JSLint will hurt your feelings. + +// JSLINT is a global function. It takes two parameters. + +// var myResult = JSLINT(source, option); + +// The first parameter is either a string or an array of strings. If it is a +// string, it will be split on '\n' or '\r'. If it is an array of strings, it +// is assumed that each string represents one line. The source can be a +// JavaScript text or a JSON text. + +// The second parameter is an optional object of options that control the +// operation of JSLINT. Most of the options are booleans: They are all +// optional and have a default value of false. One of the options, predef, +// can be an array of names, which will be used to declare global variables, +// or an object whose keys are used as global names, with a boolean value +// that determines if they are assignable. + +// If it checks out, JSLINT returns true. Otherwise, it returns false. + +// If false, you can inspect JSLINT.errors to find out the problems. +// JSLINT.errors is an array of objects containing these properties: + +// { +// line : The line (relative to 0) at which the lint was found +// character : The character (relative to 0) at which the lint was found +// reason : The problem +// evidence : The text line in which the problem occurred +// raw : The raw message before the details were inserted +// a : The first detail +// b : The second detail +// c : The third detail +// d : The fourth detail +// } + +// If a stopping error was found, a null will be the last element of the +// JSLINT.errors array. A stopping error means that JSLint was not confident +// enough to continue. It does not necessarily mean that the error was +// especially heinous. + +// You can request a data structure that contains JSLint's results. + +// var myData = JSLINT.data(); + +// It returns a structure with this form: + +// { +// errors: [ +// { +// line: NUMBER, +// character: NUMBER, +// reason: STRING, +// evidence: STRING +// } +// ], +// functions: [ +// { +// name: STRING, +// line: NUMBER, +// level: NUMBER, +// parameter: [ +// STRING +// ], +// var: [ +// STRING +// ], +// exception: [ +// STRING +// ], +// closure: [ +// STRING +// ], +// outer: [ +// STRING +// ], +// global: [ +// STRING +// ], +// label: [ +// STRING +// ] +// } +// ], +// global: [ +// STRING +// ], +// member: { +// STRING: NUMBER +// }, +// json: BOOLEAN +// } + +// You can request a Function Report, which shows all of the functions +// and the parameters and vars that they use. This can be used to find +// implied global variables and other problems. The report is in HTML and +// can be inserted into an HTML . It should be given the result of the +// JSLINT.data function. + +// var myReport = JSLINT.report(data); + +// You can request an HTML error report. + +// var myErrorReport = JSLINT.error_report(data); + +// You can obtain an object containing all of the properties found in the +// file. JSLINT.property contains an object containing a key for each +// property used in the program, the value being the number of times that +// property name was used in the file. + +// You can request a properties report, which produces a list of the program's +// properties in the form of a /*properties*/ declaration. + +// var myPropertyReport = JSLINT.properties_report(JSLINT.property); + +// You can obtain the parse tree that JSLint constructed while parsing. The +// latest tree is kept in JSLINT.tree. A nice stringification can be produced +// with + +// JSON.stringify(JSLINT.tree, [ +// 'string', 'arity', 'name', 'first', +// 'second', 'third', 'block', 'else' +// ], 4)); + +// You can request a context coloring table. It contains information that can be +// applied to the file that was analyzed. Context coloring colors functions +// based on their nesting level, and variables on the color of the functions +// in which they are defined. + +// var myColorization = JSLINT.color(data); + +// It returns an array containing objects of this form: + +// { +// from: COLUMN, +// thru: COLUMN, +// line: ROW, +// level: 0 or higher +// } + +// JSLint provides three inline directives. They look like slashstar comments, +// and allow for setting options, declaring global variables, and establishing a +// set of allowed property names. + +// These directives respect function scope. + +// The jslint directive is a special comment that can set one or more options. +// For example: + +/*jslint + evil: true, nomen: true, regexp: true, todo: true +*/ + +// The current option set is + +// ass true, if assignment expressions should be allowed +// bitwise true, if bitwise operators should be allowed +// browser true, if the standard browser globals should be predefined +// closure true, if Google Closure idioms should be tolerated +// continue true, if the continuation statement should be tolerated +// debug true, if debugger statements should be allowed +// devel true, if logging should be allowed (console, alert, etc.) +// eqeq true, if == should be allowed +// evil true, if eval should be allowed +// forin true, if for in statements need not filter +// indent the indentation factor +// maxerr the maximum number of errors to allow +// maxlen the maximum length of a source line +// newcap true, if constructor names capitalization is ignored +// node true, if Node.js globals should be predefined +// nomen true, if names may have dangling _ +// passfail true, if the scan should stop on first error +// plusplus true, if increment/decrement should be allowed +// properties true, if all property names must be declared with /*properties*/ +// regexp true, if the . should be allowed in regexp literals +// rhino true, if the Rhino environment globals should be predefined +// unparam true, if unused parameters should be tolerated +// sloppy true, if the 'use strict'; pragma is optional +// stupid true, if really stupid practices are tolerated +// sub true, if all forms of subscript notation are tolerated +// todo true, if TODO comments are tolerated +// vars true, if multiple var statements per function should be allowed +// white true, if sloppy whitespace is tolerated + +// The properties directive declares an exclusive list of property names. +// Any properties named in the program that are not in the list will +// produce a warning. + +// For example: + +/*properties + '\b', '\t', '\n', '\f', '\r', '!', '!=', '!==', '"', '%', '\'', '(begin)', + '(error)', '*', '+', '-', '/', '<', '<=', '==', '===', '>', '>=', '\\', a, + a_label, a_scope, already_defined, and, apply, arguments, arity, ass, + assign, assignment_expression, assignment_function_expression, at, avoid_a, + b, bad_assignment, bad_constructor, bad_in_a, bad_invocation, bad_new, + bad_number, bad_operand, bad_wrap, bitwise, block, break, breakage, browser, + c, call, charAt, charCodeAt, character, closure, code, color, combine_var, + comments, conditional_assignment, confusing_a, confusing_regexp, + constructor_name_a, continue, control_a, couch, create, d, dangling_a, data, + dead, debug, deleted, devel, disrupt, duplicate_a, edge, edition, elif, + else, empty_block, empty_case, empty_class, entityify, eqeq, error_report, + errors, evidence, evil, exception, exec, expected_a_at_b_c, expected_a_b, + expected_a_b_from_c_d, expected_id_a, expected_identifier_a, + expected_identifier_a_reserved, expected_number_a, expected_operator_a, + expected_positive_a, expected_small_a, expected_space_a_b, + expected_string_a, f, first, flag, floor, forEach, for_if, forin, from, + fromCharCode, fud, function, function_block, function_eval, function_loop, + function_statement, function_strict, functions, global, hasOwnProperty, id, + identifier, identifier_function, immed, implied_evil, indent, indexOf, + infix_in, init, insecure_a, isAlpha, isArray, isDigit, isNaN, join, jslint, + json, keys, kind, label, labeled, lbp, leading_decimal_a, led, left, length, + level, line, loopage, master, match, maxerr, maxlen, message, missing_a, + missing_a_after_b, missing_property, missing_space_a_b, missing_use_strict, + mode, move_invocation, move_var, n, name, name_function, nested_comment, + newcap, node, nomen, not, not_a_constructor, not_a_defined, not_a_function, + not_a_label, not_a_scope, not_greater, nud, number, octal_a, open, outer, + parameter, parameter_a_get_b, parameter_arguments_a, parameter_set_a, + params, paren, passfail, plusplus, pop, postscript, predef, properties, + properties_report, property, prototype, push, quote, r, radix, raw, + read_only, reason, redefinition_a_b, regexp, relation, replace, report, + reserved, reserved_a, rhino, right, scanned_a_b, scope, search, second, + shift, slash_equal, slice, sloppy, sort, split, statement, statement_block, + stop, stopping, strange_loop, strict, string, stupid, sub, subscript, + substr, supplant, sync_a, t, tag_a_in_b, test, third, thru, toString, todo, + todo_comment, token, tokens, too_long, too_many, trailing_decimal_a, tree, + unclosed, unclosed_comment, unclosed_regexp, unescaped_a, unexpected_a, + unexpected_char_a, unexpected_comment, unexpected_label_a, + unexpected_property_a, unexpected_space_a_b, unexpected_typeof_a, + uninitialized_a, unnecessary_else, unnecessary_initialize, unnecessary_use, + unparam, unreachable_a_b, unsafe, unused_a, url, use_array, use_braces, + use_nested_if, use_object, use_or, use_param, use_spaces, used, + used_before_a, var, var_a_not, var_loop, vars, varstatement, warn, warning, + was, weird_assignment, weird_condition, weird_new, weird_program, + weird_relation, weird_ternary, white, wrap, wrap_immediate, wrap_regexp, + write_is_wrong, writeable +*/ + +// The global directive is used to declare global variables that can +// be accessed by the program. If a declaration is true, then the variable +// is writeable. Otherwise, it is read-only. + +// We build the application inside a function so that we produce only a single +// global variable. That function will be invoked immediately, and its return +// value is the JSLINT function itself. That function is also an object that +// can contain data and other functions. + +var JSLINT = (function () { + 'use strict'; + + function array_to_object(array, value) { + +// Make an object from an array of keys and a common value. + + var i, length = array.length, object = Object.create(null); + for (i = 0; i < length; i += 1) { + object[array[i]] = value; + } + return object; + } + + + var allowed_option = { + ass : true, + bitwise : true, + browser : true, + closure : true, + continue : true, + couch : true, + debug : true, + devel : true, + eqeq : true, + evil : true, + forin : true, + indent : 10, + maxerr : 1000, + maxlen : 256, + newcap : true, + node : true, + nomen : true, + passfail : true, + plusplus : true, + properties: true, + regexp : true, + rhino : true, + unparam : true, + sloppy : true, + stupid : true, + sub : true, + todo : true, + vars : true, + white : true + }, + anonname, // The guessed name for anonymous functions. + +// These are operators that should not be used with the ! operator. + + bang = { + '<' : true, + '<=' : true, + '==' : true, + '===': true, + '!==': true, + '!=' : true, + '>' : true, + '>=' : true, + '+' : true, + '-' : true, + '*' : true, + '/' : true, + '%' : true + }, + begin, // The root token + block_var, // vars defined in the current block + +// browser contains a set of global names that are commonly provided by a +// web browser environment. + + browser = array_to_object([ + 'clearInterval', 'clearTimeout', 'document', 'event', 'FormData', + 'frames', 'history', 'Image', 'localStorage', 'location', 'name', + 'navigator', 'Option', 'parent', 'screen', 'sessionStorage', + 'setInterval', 'setTimeout', 'Storage', 'window', 'XMLHttpRequest' + ], false), + +// bundle contains the text messages. + + bundle = { + a_label: "'{a}' is a statement label.", + a_scope: "'{a}' used out of scope.", + already_defined: "'{a}' is already defined.", + and: "The '&&' subexpression should be wrapped in parens.", + assignment_expression: "Unexpected assignment expression.", + assignment_function_expression: "Expected an assignment or " + + "function call and instead saw an expression.", + avoid_a: "Avoid '{a}'.", + bad_assignment: "Bad assignment.", + bad_constructor: "Bad constructor.", + bad_in_a: "Bad for in variable '{a}'.", + bad_invocation: "Bad invocation.", + bad_new: "Do not use 'new' for side effects.", + bad_number: "Bad number '{a}'.", + bad_operand: "Bad operand.", + bad_wrap: "Do not wrap function literals in parens unless they " + + "are to be immediately invoked.", + combine_var: "Combine this with the previous 'var' statement.", + conditional_assignment: "Expected a conditional expression and " + + "instead saw an assignment.", + confusing_a: "Confusing use of '{a}'.", + confusing_regexp: "Confusing regular expression.", + constructor_name_a: "A constructor name '{a}' should start with " + + "an uppercase letter.", + control_a: "Unexpected control character '{a}'.", + dangling_a: "Unexpected dangling '_' in '{a}'.", + deleted: "Only properties should be deleted.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + empty_case: "Empty case.", + empty_class: "Empty class.", + evil: "eval is evil.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: "Expected '{a}' to match '{b}' from line " + + "{c} and instead saw '{d}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_id_a: "Expected an id, and instead saw #{a}.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_identifier_a_reserved: "Expected an identifier and " + + "instead saw '{a}' (a reserved word).", + expected_number_a: "Expected a number and instead saw '{a}'.", + expected_operator_a: "Expected an operator and instead saw '{a}'.", + expected_positive_a: "Expected a positive number and instead saw '{a}'", + expected_small_a: "Expected a small positive integer and instead saw '{a}'", + expected_space_a_b: "Expected exactly one space between '{a}' and '{b}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + for_if: "The body of a for in should be wrapped in an if " + + "statement to filter unwanted properties from the prototype.", + function_block: "Function statements should not be placed in blocks." + + "Use a function expression or move the statement to the top of " + + "the outer function.", + function_eval: "The Function constructor is eval.", + function_loop: "Don't make functions within a loop.", + function_statement: "Function statements are not invocable. " + + "Wrap the whole function invocation in parens.", + function_strict: "Use the function form of 'use strict'.", + identifier_function: "Expected an identifier in an assignment " + + "and instead saw a function invocation.", + implied_evil: "Implied eval is evil. Pass a function instead of a string.", + infix_in: "Unexpected 'in'. Compare with undefined, or use the " + + "hasOwnProperty method instead.", + insecure_a: "Insecure '{a}'.", + isNaN: "Use the isNaN function to compare with NaN.", + leading_decimal_a: "A leading decimal point can be confused with a dot: '.{a}'.", + missing_a: "Missing '{a}'.", + missing_a_after_b: "Missing '{a}' after '{b}'.", + missing_property: "Missing property name.", + missing_space_a_b: "Missing space between '{a}' and '{b}'.", + missing_use_strict: "Missing 'use strict' statement.", + move_invocation: "Move the invocation into the parens that " + + "contain the function.", + move_var: "Move 'var' declarations to the top of the function.", + name_function: "Missing name in function statement.", + nested_comment: "Nested comment.", + not: "Nested not.", + not_a_constructor: "Do not use {a} as a constructor.", + not_a_defined: "'{a}' has not been fully defined yet.", + not_a_function: "'{a}' is not a function.", + not_a_label: "'{a}' is not a label.", + not_a_scope: "'{a}' is out of scope.", + not_greater: "'{a}' should not be greater than '{b}'.", + octal_a: "Don't use octal: '{a}'. Use '\\u....' instead.", + parameter_arguments_a: "Do not mutate parameter '{a}' when using 'arguments'.", + parameter_a_get_b: "Unexpected parameter '{a}' in get {b} function.", + parameter_set_a: "Expected parameter (value) in set {a} function.", + radix: "Missing radix parameter.", + read_only: "Read only.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + reserved_a: "Reserved name '{a}'.", + scanned_a_b: "{a} ({b}% scanned).", + slash_equal: "A regular expression literal can be confused with '/='.", + statement_block: "Expected to see a statement and instead saw a block.", + stopping: "Stopping.", + strange_loop: "Strange loop.", + strict: "Strict violation.", + subscript: "['{a}'] is better written in dot notation.", + sync_a: "Unexpected sync method: '{a}'.", + tag_a_in_b: "A '<{a}>' must be within '<{b}>'.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line too long.", + too_many: "Too many errors.", + trailing_decimal_a: "A trailing decimal point can be confused " + + "with a dot: '.{a}'.", + unclosed: "Unclosed string.", + unclosed_comment: "Unclosed comment.", + unclosed_regexp: "Unclosed regular expression.", + unescaped_a: "Unescaped '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_property_a: "Unexpected /*property*/ '{a}'.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_typeof_a: "Unexpected 'typeof'. " + + "Use '===' to compare directly with {a}.", + uninitialized_a: "Uninitialized '{a}'.", + unnecessary_else: "Unnecessary 'else' after disruption.", + unnecessary_initialize: "It is not necessary to initialize '{a}' " + + "to 'undefined'.", + unnecessary_use: "Unnecessary 'use strict'.", + unreachable_a_b: "Unreachable '{a}' after '{b}'.", + unsafe: "Unsafe character.", + unused_a: "Unused '{a}'.", + url: "JavaScript URL.", + use_array: "Use the array literal notation [].", + use_braces: "Spaces are hard to count. Use {{a}}.", + use_nested_if: "Expected 'else { if' and instead saw 'else if'.", + use_object: "Use the object literal notation {} or Object.create(null).", + use_or: "Use the || operator.", + use_param: "Use a named parameter.", + use_spaces: "Use spaces, not tabs.", + used_before_a: "'{a}' was used before it was defined.", + var_a_not: "Variable {a} was not declared correctly.", + var_loop: "Don't declare variables in a loop.", + weird_assignment: "Weird assignment.", + weird_condition: "Weird condition.", + weird_new: "Weird construction. Delete 'new'.", + weird_program: "Weird program.", + weird_relation: "Weird relation.", + weird_ternary: "Weird ternary.", + wrap_immediate: "Wrap an immediate function invocation in " + + "parentheses to assist the reader in understanding that the " + + "expression is the result of a function, and not the " + + "function itself.", + wrap_regexp: "Wrap the /regexp/ literal in parens to " + + "disambiguate the slash operator.", + write_is_wrong: "document.write can be a form of eval." + }, + closure = array_to_object([ + 'goog' + ], false), + comments, + comments_off, + couch = array_to_object([ + 'emit', 'getRow', 'isArray', 'log', 'provides', 'registerType', + 'require', 'send', 'start', 'sum', 'toJSON' + ], false), + + descapes = { + 'b': '\b', + 't': '\t', + 'n': '\n', + 'f': '\f', + 'r': '\r', + '"': '"', + '/': '/', + '\\': '\\', + '!': '!' + }, + + devel = array_to_object([ + 'alert', 'confirm', 'console', 'Debug', 'opera', 'prompt', 'WSH' + ], false), + directive, + escapes = { + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '\'': '\\\'', + '"' : '\\"', + '/' : '\\/', + '\\': '\\\\' + }, + + funct, // The current function + + functions, // All of the functions + global_funct, // The global body + global_scope, // The global scope + in_block, // Where function statements are not allowed + indent, + itself, // JSLINT itself + json_mode, + lex, // the tokenizer + lines, + lookahead, + node = array_to_object([ + 'Buffer', 'clearImmediate', 'clearInterval', 'clearTimeout', + 'console', 'exports', 'global', 'module', 'process', + 'require', 'setImmediate', 'setInterval', 'setTimeout', + '__dirname', '__filename' + ], false), + node_js, + numbery = array_to_object(['indexOf', 'lastIndexOf', 'search'], true), + next_token, + option, + predefined, // Global variables defined by option + prereg, + prev_token, + property, + protosymbol, + regexp_flag = array_to_object(['g', 'i', 'm'], true), + return_this = function return_this() { + return this; + }, + rhino = array_to_object([ + 'defineClass', 'deserialize', 'gc', 'help', 'load', 'loadClass', + 'print', 'quit', 'readFile', 'readUrl', 'runCommand', 'seal', + 'serialize', 'spawn', 'sync', 'toint32', 'version' + ], false), + + scope, // An object containing an object for each variable in scope + semicolon_coda = array_to_object([';', '"', '\'', ')'], true), + +// standard contains the global names that are provided by the +// ECMAScript standard. + + standard = array_to_object([ + 'Array', 'Boolean', 'Date', 'decodeURI', 'decodeURIComponent', + 'encodeURI', 'encodeURIComponent', 'Error', 'eval', 'EvalError', + 'Function', 'isFinite', 'isNaN', 'JSON', 'Map', 'Math', 'Number', + 'Object', 'parseInt', 'parseFloat', 'Promise', 'Proxy', + 'RangeError', 'ReferenceError', 'Reflect', 'RegExp', 'Set', + 'String', 'Symbol', 'SyntaxError', 'System', 'TypeError', + 'URIError', 'WeakMap', 'WeakSet' + ], false), + + strict_mode, + syntax = Object.create(null), + token, + tokens, + var_mode, + warnings, + +// Regular expressions. Some of these are stupidly long. + +// carriage return, carriage return linefeed, or linefeed + crlfx = /\r\n?|\n/, +// unsafe characters that are silently deleted by one or more browsers + cx = /[\u0000-\u0008\u000a-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/, +// identifier + ix = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/, +// javascript url + jx = /^(?:javascript|jscript|ecmascript|vbscript)\s*:/i, +// star slash + lx = /\*\/|\/\*/, +// characters in strings that need escapement + nx = /[\u0000-\u001f'\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, +// sync + syx = /Sync$/, +// comment todo + tox = /^\W*to\s*do(?:\W|$)/i, +// token + tx = /^\s*([(){}\[\]\?.,:;'"~#@`]|={1,3}|\/(\*(jslint|properties|property|members?|globals?)?|=|\/)?|\*[\/=]?|\+(?:=|\++)?|-(?:=|-+)?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<(?:[\/=!]|\!(\[|--)?|<=?)?|\!(\!|==?)?|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+(?:[xX][0-9a-fA-F]+|\.[0-9]*)?(?:[eE][+\-]?[0-9]+)?)/; + + + if (typeof String.prototype.entityify !== 'function') { + String.prototype.entityify = function () { + return this + .replace(/&/g, '&') + .replace(//g, '>'); + }; + } + + if (typeof String.prototype.isAlpha !== 'function') { + String.prototype.isAlpha = function () { + return (this >= 'a' && this <= 'z\uffff') || + (this >= 'A' && this <= 'Z\uffff'); + }; + } + + if (typeof String.prototype.isDigit !== 'function') { + String.prototype.isDigit = function () { + return (this >= '0' && this <= '9'); + }; + } + + if (typeof String.prototype.supplant !== 'function') { + String.prototype.supplant = function (o) { + return this.replace(/\{([^{}]*)\}/g, function (a, b) { + var replacement = o[b]; + return typeof replacement === 'string' || + typeof replacement === 'number' ? replacement : a; + }); + }; + } + + + function sanitize(a) { + +// Escapify a troublesome character. + + return escapes[a] || + '\\u' + ('0000' + a.charCodeAt().toString(16)).slice(-4); + } + + + function add_to_predefined(group) { + Object.keys(group).forEach(function (name) { + predefined[name] = group[name]; + }); + } + + + function assume() { + if (option.browser) { + add_to_predefined(browser); + option.browser = false; + } + if (option.closure) { + add_to_predefined(closure); + } + if (option.couch) { + add_to_predefined(couch); + option.couch = false; + } + if (option.devel) { + add_to_predefined(devel); + option.devel = false; + } + if (option.node) { + add_to_predefined(node); + option.node = false; + node_js = true; + } + if (option.rhino) { + add_to_predefined(rhino); + option.rhino = false; + } + } + + +// Produce an error warning. + + function artifact(tok) { + if (!tok) { + tok = next_token; + } + return tok.id === '(number)' ? tok.number : tok.string; + } + + function quit(message, line, character) { + throw { + name: 'JSLintError', + line: line, + character: character, + message: bundle.scanned_a_b.supplant({ + a: bundle[message] || message, + b: Math.floor((line / lines.length) * 100) + }) + }; + } + + function warn(code, line, character, a, b, c, d) { + var warning = { // ~~ + id: '(error)', + raw: bundle[code] || code, + code: code, + evidence: lines[line - 1] || '', + line: line, + character: character, + a: a || artifact(this), + b: b, + c: c, + d: d + }; + warning.reason = warning.raw.supplant(warning); + itself.errors.push(warning); + if (option.passfail) { + quit('stopping', line, character); + } + warnings += 1; + if (warnings >= option.maxerr) { + quit('too_many', line, character); + } + return warning; + } + + function stop(code, line, character, a, b, c, d) { + var warning = warn(code, line, character, a, b, c, d); + quit('stopping', warning.line, warning.character); + } + + function expected_at(at) { + if (!option.white && next_token.from !== at) { + next_token.warn('expected_a_at_b_c', '', at, next_token.from); + } + } + +// lexical analysis and token construction + + lex = (function lex() { + var character, c, from, length, line, pos, source_row; + +// Private lex methods + + function next_line() { + var at; + character = 1; + source_row = lines[line]; + line += 1; + if (source_row === undefined) { + return false; + } + at = source_row.search(/\t/); + if (at >= 0) { + if (option.white) { + source_row = source_row.replace(/\t/g, ' '); + } else { + warn('use_spaces', line, at + 1); + } + } + at = source_row.search(cx); + if (at >= 0) { + warn('unsafe', line, at); + } + if (option.maxlen && option.maxlen < source_row.length) { + warn('too_long', line, source_row.length); + } + return true; + } + +// Produce a token object. The token inherits from a syntax symbol. + + function it(type, value) { + var id, the_token; + if (type === '(string)') { + if (jx.test(value)) { + warn('url', line, from); + } + } + the_token = Object.create(syntax[( + type === '(punctuator)' || (type === '(identifier)' && + Object.prototype.hasOwnProperty.call(syntax, value)) + ? value + : type + )] || syntax['(error)']); + if (type === '(identifier)') { + the_token.identifier = true; + if (value === '__iterator__' || value === '__proto__') { + stop('reserved_a', line, from, value); + } else if (!option.nomen && + (value.charAt(0) === '_' || + value.charAt(value.length - 1) === '_')) { + warn('dangling_a', line, from, value); + } + } + if (type === '(number)') { + the_token.number = +value; + } else if (value !== undefined) { + the_token.string = String(value); + } + the_token.line = line; + the_token.from = from; + the_token.thru = character; + if (comments.length) { + the_token.comments = comments; + comments = []; + } + id = the_token.id; + prereg = id && ( + ('(,=:[!&|?{};~+-*%^<>'.indexOf(id.charAt(id.length - 1)) >= 0) || + id === 'return' || id === 'case' + ); + return the_token; + } + + function match(x) { + var exec = x.exec(source_row), first; + if (exec) { + length = exec[0].length; + first = exec[1]; + c = first.charAt(0); + source_row = source_row.slice(length); + from = character + length - first.length; + character += length; + return first; + } + for (;;) { + if (!source_row) { + if (!option.white) { + warn('unexpected_char_a', line, character - 1, '(space)'); + } + return; + } + c = source_row.charAt(0); + if (c !== ' ') { + break; + } + source_row = source_row.slice(1); + character += 1; + } + stop('unexpected_char_a', line, character, c); + + } + + function string(x) { + var ch, at = 0, r = '', result; + + function hex(n) { + var i = parseInt(source_row.substr(at + 1, n), 16); + at += n; + if (i >= 32 && i <= 126 && + i !== 34 && i !== 92 && i !== 39) { + warn('unexpected_a', line, character, '\\'); + } + character += n; + ch = String.fromCharCode(i); + } + + if (json_mode && x !== '"') { + warn('expected_a_b', line, character, '"', x); + } + + for (;;) { + while (at >= source_row.length) { + at = 0; + if (!next_line()) { + stop('unclosed', line - 1, from); + } + } + ch = source_row.charAt(at); + if (ch === x) { + character += 1; + source_row = source_row.slice(at + 1); + result = it('(string)', r); + result.quote = x; + return result; + } + if (ch < ' ') { + if (ch === '\n' || ch === '\r') { + break; + } + warn('control_a', line, character + at, + source_row.slice(0, at)); + } else if (ch === '\\') { + at += 1; + character += 1; + ch = source_row.charAt(at); + switch (ch) { + case '': + warn('unexpected_a', line, character, '\\'); + next_line(); + at = -1; + break; + case '\'': + if (json_mode) { + warn('unexpected_a', line, character, '\\\''); + } + break; + case 'u': + hex(4); + break; + case 'v': + if (json_mode) { + warn('unexpected_a', line, character, '\\v'); + } + ch = '\v'; + break; + case 'x': + if (json_mode) { + warn('unexpected_a', line, character, '\\x'); + } + hex(2); + break; + default: + if (typeof descapes[ch] !== 'string') { + warn(ch >= '0' && ch <= '7' ? 'octal_a' : 'unexpected_a', + line, character, '\\' + ch); + } else { + ch = descapes[ch]; + } + } + } + r += ch; + character += 1; + at += 1; + } + } + + function number(snippet) { + var digit; + if (source_row.charAt(0).isAlpha()) { + warn('expected_space_a_b', + line, character, c, source_row.charAt(0)); + } + if (c === '0') { + digit = snippet.charAt(1); + if (digit.isDigit()) { + if (token.id !== '.') { + warn('unexpected_a', line, character, snippet); + } + } else if (json_mode && (digit === 'x' || digit === 'X')) { + warn('unexpected_a', line, character, '0x'); + } + } + if (snippet.slice(snippet.length - 1) === '.') { + warn('trailing_decimal_a', line, character, snippet); + } + digit = +snippet; + if (!isFinite(digit)) { + warn('bad_number', line, character, snippet); + } + snippet = digit; + return it('(number)', snippet); + } + + function comment(snippet, type) { + if (comments_off) { + warn('unexpected_comment', line, character); + } else if (!option.todo && tox.test(snippet)) { + warn('todo_comment', line, character); + } + comments.push({ + id: type, + from: from, + thru: character, + line: line, + string: snippet + }); + } + + function regexp() { + var at = 0, + b, + bit, + depth = 0, + flag = '', + high, + letter, + low, + potential, + quote, + result; + for (;;) { + b = true; + c = source_row.charAt(at); + at += 1; + switch (c) { + case '': + stop('unclosed_regexp', line, from); + return; + case '/': + if (depth > 0) { + warn('unescaped_a', line, from + at, '/'); + } + c = source_row.slice(0, at - 1); + potential = Object.create(regexp_flag); + for (;;) { + letter = source_row.charAt(at); + if (potential[letter] !== true) { + break; + } + potential[letter] = false; + at += 1; + flag += letter; + } + if (source_row.charAt(at).isAlpha()) { + stop('unexpected_a', line, from, source_row.charAt(at)); + } + character += at; + source_row = source_row.slice(at); + quote = source_row.charAt(0); + if (quote === '/' || quote === '*') { + stop('confusing_regexp', line, from); + } + result = it('(regexp)', c); + result.flag = flag; + return result; + case '\\': + c = source_row.charAt(at); + if (c < ' ') { + warn('control_a', line, from + at, String(c)); + } else if (c === '<') { + warn('unexpected_a', line, from + at, '\\'); + } + at += 1; + break; + case '(': + depth += 1; + b = false; + if (source_row.charAt(at) === '?') { + at += 1; + switch (source_row.charAt(at)) { + case ':': + case '=': + case '!': + at += 1; + break; + default: + warn('expected_a_b', line, from + at, + ':', source_row.charAt(at)); + } + } + break; + case '|': + b = false; + break; + case ')': + if (depth === 0) { + warn('unescaped_a', line, from + at, ')'); + } else { + depth -= 1; + } + break; + case ' ': + pos = 1; + while (source_row.charAt(at) === ' ') { + at += 1; + pos += 1; + } + if (pos > 1) { + warn('use_braces', line, from + at, pos); + } + break; + case '[': + c = source_row.charAt(at); + if (c === '^') { + at += 1; + if (!option.regexp) { + warn('insecure_a', line, from + at, c); + } else if (source_row.charAt(at) === ']') { + stop('unescaped_a', line, from + at, '^'); + } + } + bit = false; + if (c === ']') { + warn('empty_class', line, from + at - 1); + bit = true; + } +klass: do { + c = source_row.charAt(at); + at += 1; + switch (c) { + case '[': + case '^': + warn('unescaped_a', line, from + at, c); + bit = true; + break; + case '-': + if (bit) { + bit = false; + } else { + warn('unescaped_a', line, from + at, '-'); + bit = true; + } + break; + case ']': + if (!bit) { + warn('unescaped_a', line, from + at - 1, '-'); + } + break klass; + case '\\': + c = source_row.charAt(at); + if (c < ' ') { + warn('control_a', line, from + at, String(c)); + } else if (c === '<') { + warn('unexpected_a', line, from + at, '\\'); + } + at += 1; + bit = true; + break; + case '/': + warn('unescaped_a', line, from + at - 1, '/'); + bit = true; + break; + default: + bit = true; + } + } while (c); + break; + case '.': + if (!option.regexp) { + warn('insecure_a', line, from + at, c); + } + break; + case ']': + case '?': + case '{': + case '}': + case '+': + case '*': + warn('unescaped_a', line, from + at, c); + break; + } + if (b) { + switch (source_row.charAt(at)) { + case '?': + case '+': + case '*': + at += 1; + if (source_row.charAt(at) === '?') { + at += 1; + } + break; + case '{': + at += 1; + c = source_row.charAt(at); + if (c < '0' || c > '9') { + warn('expected_number_a', line, + from + at, c); + } + at += 1; + low = +c; + for (;;) { + c = source_row.charAt(at); + if (c < '0' || c > '9') { + break; + } + at += 1; + low = +c + (low * 10); + } + high = low; + if (c === ',') { + at += 1; + high = Infinity; + c = source_row.charAt(at); + if (c >= '0' && c <= '9') { + at += 1; + high = +c; + for (;;) { + c = source_row.charAt(at); + if (c < '0' || c > '9') { + break; + } + at += 1; + high = +c + (high * 10); + } + } + } + if (source_row.charAt(at) !== '}') { + warn('expected_a_b', line, from + at, + '}', c); + } else { + at += 1; + } + if (source_row.charAt(at) === '?') { + at += 1; + } + if (low > high) { + warn('not_greater', line, from + at, + low, high); + } + break; + } + } + } + c = source_row.slice(0, at - 1); + character += at; + source_row = source_row.slice(at); + return it('(regexp)', c); + } + +// Public lex methods + + return { + init: function (source) { + if (typeof source === 'string') { + lines = source.split(crlfx); + } else { + lines = source; + } + line = 0; + next_line(); + from = 1; + }, + +// token -- this is called by advance to get the next token. + + token: function () { + var first, i, snippet; + + for (;;) { + while (!source_row) { + if (!next_line()) { + return it('(end)'); + } + } + snippet = match(tx); + if (snippet) { + +// identifier + + first = snippet.charAt(0); + if (first.isAlpha() || first === '_' || first === '$') { + return it('(identifier)', snippet); + } + +// number + + if (first.isDigit()) { + return number(snippet); + } + switch (snippet) { + +// string + + case '"': + case "'": + return string(snippet); + +// // comment + + case '//': + comment(source_row, '//'); + source_row = ''; + break; + +// /* comment + + case '/*': + for (;;) { + i = source_row.search(lx); + if (i >= 0) { + break; + } + character = source_row.length; + comment(source_row); + from = 0; + if (!next_line()) { + stop('unclosed_comment', line, character); + } + } + comment(source_row.slice(0, i), '/*'); + character += i + 2; + if (source_row.charAt(i) === '/') { + stop('nested_comment', line, character); + } + source_row = source_row.slice(i + 2); + break; + + case '': + break; +// / + case '/': + if (token.id === '/=') { + stop('slash_equal', line, from); + } + return prereg + ? regexp() + : it('(punctuator)', snippet); + +// punctuator + default: + return it('(punctuator)', snippet); + } + } + } + } + }; + }()); + + function define(kind, token) { + +// Define a name. + + var name = token.string, + master = scope[name]; // The current definition of the name + +// vars are created with a deadzone, so that the expression that initializes +// the var cannot access the var. Functions are not writeable. + + token.dead = false; + token.init = false; + token.kind = kind; + token.master = master; + token.used = 0; + token.writeable = true; + +// Global variables are a little weird. They can be defined multiple times. +// Some predefined global vars are (or should) not be writeable. + + if (kind === 'var' && funct === global_funct) { + if (!master) { + if (predefined[name] === false) { + token.writeable = false; + } + global_scope[name] = token; + } + } else { + +// It is an error if the name has already been defined in this scope, except +// when reusing an exception variable name. + + if (master) { + if (master.function === funct) { + if (master.kind !== 'exception' || kind !== 'exception' || + !master.dead) { + token.warn('already_defined', name); + } + } else if (master.function !== global_funct) { + if (kind === 'var') { + token.warn('redefinition_a_b', name, master.line); + } + } + } + scope[name] = token; + if (kind === 'var') { + block_var.push(name); + } + } + } + + function peek(distance) { + +// Peek ahead to a future token. The distance is how far ahead to look. The +// default is the next token. + + var found, slot = 0; + + distance = distance || 0; + while (slot <= distance) { + found = lookahead[slot]; + if (!found) { + found = lookahead[slot] = lex.token(); + } + slot += 1; + } + return found; + } + + + function advance(id, match) { + +// Produce the next token, also looking for programming errors. + + if (indent) { + +// If indentation checking was requested, then inspect all of the line breakings. +// The var statement is tricky because the names might be aligned or not. We +// look at the first line break after the var to determine the programmer's +// intention. + + if (var_mode && next_token.line !== token.line) { + if ((var_mode !== indent || !next_token.edge) && + next_token.from === indent.at - + (next_token.edge ? option.indent : 0)) { + var dent = indent; + for (;;) { + dent.at -= option.indent; + if (dent === var_mode) { + break; + } + dent = dent.was; + } + dent.open = false; + } + var_mode = null; + } + if (next_token.id === '?' && indent.mode === ':' && + token.line !== next_token.line) { + indent.at -= option.indent; + } + if (indent.open) { + +// If the token is an edge. + + if (next_token.edge) { + if (next_token.edge === 'label') { + expected_at(1); + } else if (next_token.edge === 'case' || indent.mode === 'statement') { + expected_at(indent.at - option.indent); + } else if (indent.mode !== 'array' || next_token.line !== token.line) { + expected_at(indent.at); + } + +// If the token is not an edge, but is the first token on the line. + + } else if (next_token.line !== token.line) { + if (next_token.from < indent.at + (indent.mode === + 'expression' ? 0 : option.indent)) { + expected_at(indent.at + option.indent); + } + indent.wrap = true; + } + } else if (next_token.line !== token.line) { + if (next_token.edge) { + expected_at(indent.at); + } else { + indent.wrap = true; + if (indent.mode === 'statement' || indent.mode === 'var') { + expected_at(indent.at + option.indent); + } else if (next_token.from < indent.at + (indent.mode === + 'expression' ? 0 : option.indent)) { + expected_at(indent.at + option.indent); + } + } + } + } + + switch (token.id) { + case '(number)': + if (next_token.id === '.') { + next_token.warn('trailing_decimal_a'); + } + break; + case '-': + if (next_token.id === '-' || next_token.id === '--') { + next_token.warn('confusing_a'); + } + break; + case '+': + if (next_token.id === '+' || next_token.id === '++') { + next_token.warn('confusing_a'); + } + break; + } + if (token.id === '(string)' || token.identifier) { + anonname = token.string; + } + + if (id && next_token.id !== id) { + if (match) { + next_token.warn('expected_a_b_from_c_d', id, + match.id, match.line, artifact()); + } else if (!next_token.identifier || next_token.string !== id) { + next_token.warn('expected_a_b', id, artifact()); + } + } + prev_token = token; + token = next_token; + next_token = lookahead.shift() || lex.token(); + next_token.function = funct; + tokens.push(next_token); + } + + + function do_globals() { + var name, writeable; + for (;;) { + if (next_token.id !== '(string)' && !next_token.identifier) { + return; + } + name = next_token.string; + advance(); + writeable = false; + if (next_token.id === ':') { + advance(':'); + switch (next_token.id) { + case 'true': + writeable = predefined[name] !== false; + advance('true'); + break; + case 'false': + advance('false'); + break; + default: + next_token.stop('unexpected_a'); + } + } + predefined[name] = writeable; + if (next_token.id !== ',') { + return; + } + advance(','); + } + } + + + function do_jslint() { + var name, value; + while (next_token.id === '(string)' || next_token.identifier) { + name = next_token.string; + if (!allowed_option[name]) { + next_token.stop('unexpected_a'); + } + advance(); + if (next_token.id !== ':') { + next_token.stop('expected_a_b', ':', artifact()); + } + advance(':'); + if (typeof allowed_option[name] === 'number') { + value = next_token.number; + if (value > allowed_option[name] || value <= 0 || + Math.floor(value) !== value) { + next_token.stop('expected_small_a'); + } + option[name] = value; + } else { + if (next_token.id === 'true') { + option[name] = true; + } else if (next_token.id === 'false') { + option[name] = false; + } else { + next_token.stop('unexpected_a'); + } + } + advance(); + if (next_token.id === ',') { + advance(','); + } + } + assume(); + } + + + function do_properties() { + var name; + option.properties = true; + for (;;) { + if (next_token.id !== '(string)' && !next_token.identifier) { + return; + } + name = next_token.string; + advance(); + if (next_token.id === ':') { + for (;;) { + advance(); + if (next_token.id !== '(string)' && !next_token.identifier) { + break; + } + } + } + property[name] = 0; + if (next_token.id !== ',') { + return; + } + advance(','); + } + } + + + directive = function directive() { + var command = this.id, + old_comments_off = comments_off, + old_indent = indent; + comments_off = true; + indent = null; + if (next_token.line === token.line && next_token.from === token.thru) { + next_token.warn('missing_space_a_b', artifact(token), artifact()); + } + if (lookahead.length > 0) { + this.warn('unexpected_a'); + } + switch (command) { + case '/*properties': + case '/*property': + case '/*members': + case '/*member': + do_properties(); + break; + case '/*jslint': + do_jslint(); + break; + case '/*globals': + case '/*global': + do_globals(); + break; + default: + this.stop('unexpected_a'); + } + comments_off = old_comments_off; + advance('*/'); + indent = old_indent; + }; + + +// Indentation intention + + function edge(mode) { + next_token.edge = indent ? indent.open && (mode || 'edge') : ''; + } + + + function step_in(mode) { + var open; + if (typeof mode === 'number') { + indent = { + at: +mode, + open: true, + was: indent + }; + } else if (!indent) { + indent = { + at: 1, + mode: 'statement', + open: true + }; + } else if (mode === 'statement') { + indent = { + at: indent.at, + open: true, + was: indent + }; + } else { + open = mode === 'var' || next_token.line !== token.line; + indent = { + at: (open || mode === 'control' + ? indent.at + option.indent + : indent.at) + (indent.wrap ? option.indent : 0), + mode: mode, + open: open, + was: indent + }; + if (mode === 'var' && open) { + var_mode = indent; + } + } + } + + function step_out(id, symbol) { + if (id) { + if (indent && indent.open) { + indent.at -= option.indent; + edge(); + } + advance(id, symbol); + } + if (indent) { + indent = indent.was; + } + } + +// Functions for conformance of whitespace. + + function one_space(left, right) { + left = left || token; + right = right || next_token; + if (right.id !== '(end)' && !option.white && + (token.line !== right.line || + token.thru + 1 !== right.from)) { + right.warn('expected_space_a_b', artifact(token), artifact(right)); + } + } + + function one_space_only(left, right) { + left = left || token; + right = right || next_token; + if (right.id !== '(end)' && (left.line !== right.line || + (!option.white && left.thru + 1 !== right.from))) { + right.warn('expected_space_a_b', artifact(left), artifact(right)); + } + } + + function no_space(left, right) { + left = left || token; + right = right || next_token; + if ((!option.white) && + left.thru !== right.from && left.line === right.line) { + right.warn('unexpected_space_a_b', artifact(left), artifact(right)); + } + } + + function no_space_only(left, right) { + left = left || token; + right = right || next_token; + if (right.id !== '(end)' && (left.line !== right.line || + (!option.white && left.thru !== right.from))) { + right.warn('unexpected_space_a_b', artifact(left), artifact(right)); + } + } + + function spaces(left, right) { + if (!option.white) { + left = left || token; + right = right || next_token; + if (left.thru === right.from && left.line === right.line) { + right.warn('missing_space_a_b', artifact(left), artifact(right)); + } + } + } + + function comma() { + if (next_token.id !== ',') { + warn('expected_a_b', token.line, token.thru, ',', artifact()); + } else { + if (!option.white) { + no_space_only(); + } + advance(','); + spaces(); + } + } + + + function semicolon() { + if (next_token.id !== ';') { + warn('expected_a_b', token.line, token.thru, ';', artifact()); + } else { + if (!option.white) { + no_space_only(); + } + advance(';'); + if (semicolon_coda[next_token.id] !== true) { + spaces(); + } + } + } + + function use_strict() { + if (next_token.string === 'use strict') { + if (strict_mode) { + next_token.warn('unnecessary_use'); + } + edge(); + advance(); + semicolon(); + strict_mode = true; + return true; + } + return false; + } + + + function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + if (Array.isArray(b) && a.length === b.length) { + var i; + for (i = 0; i < a.length; i += 1) { + if (!are_similar(a[i], b[i])) { + return false; + } + } + return true; + } + return false; + } + if (Array.isArray(b)) { + return false; + } + if (a.id === '(number)' && b.id === '(number)') { + return a.number === b.number; + } + if (a.arity === b.arity && a.string === b.string) { + switch (a.arity) { + case undefined: + return a.string === b.string; + case 'prefix': + case 'suffix': + return a.id === b.id && are_similar(a.first, b.first) && + a.id !== '{' && a.id !== '['; + case 'infix': + return are_similar(a.first, b.first) && + are_similar(a.second, b.second); + case 'ternary': + return are_similar(a.first, b.first) && + are_similar(a.second, b.second) && + are_similar(a.third, b.third); + case 'function': + case 'regexp': + return false; + default: + return true; + } + } + if (a.id === '.' && b.id === '[' && b.arity === 'infix') { + return a.second.string === b.second.string && b.second.id === '(string)'; + } + if (a.id === '[' && a.arity === 'infix' && b.id === '.') { + return a.second.string === b.second.string && a.second.id === '(string)'; + } + return false; + } + + +// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it +// is looking for ad hoc lint patterns. We add .fud to Pratt's model, which is +// like .nud except that it is only used on the first token of a statement. +// Having .fud makes it much easier to define statement-oriented languages like +// JavaScript. I retained Pratt's nomenclature. + +// .nud Null denotation +// .fud First null denotation +// .led Left denotation +// lbp Left binding power +// rbp Right binding power + +// They are elements of the parsing method called Top Down Operator Precedence. + + function expression(rbp, initial) { + +// rbp is the right binding power. +// initial indicates that this is the first expression of a statement. + + var left; + if (next_token.id === '(end)') { + token.stop('unexpected_a', next_token.id); + } + advance(); + if (initial) { + anonname = 'anonymous'; + } + if (initial === true && token.fud) { + left = token.fud(); + } else { + if (token.nud) { + left = token.nud(); + } else { + if (next_token.id === '(number)' && token.id === '.') { + token.warn('leading_decimal_a', artifact()); + advance(); + return token; + } + token.stop('expected_identifier_a', artifact(token)); + } + while (rbp < next_token.lbp) { + advance(); + left = token.led(left); + } + } + if (left && left.assign && !initial) { + if (!option.ass) { + left.warn('assignment_expression'); + } + if (left.id !== '=' && left.first.master) { + left.first.master.used = true; + } + } + return left; + } + + protosymbol = { + nud: function () { + this.stop('unexpected_a'); + }, + led: function () { + this.stop('expected_operator_a'); + }, + warn: function (code, a, b, c, d) { + if (!this.warning) { + this.warning = warn(code, this.line || 0, this.from || 0, + a || artifact(this), b, c, d); + } + }, + stop: function (code, a, b, c, d) { + this.warning = undefined; + this.warn(code, a, b, c, d); + return quit('stopping', this.line, this.character); + }, + lbp: 0 + }; + +// Functional constructors for making the symbols that will be inherited by +// tokens. + + function symbol(s, bp) { + var x = syntax[s]; + if (!x) { + x = Object.create(protosymbol); + x.id = x.string = s; + x.lbp = bp || 0; + syntax[s] = x; + } + return x; + } + + function postscript(x) { + x.postscript = true; + return x; + } + + function ultimate(s) { + var x = symbol(s, 0); + x.from = 1; + x.thru = 1; + x.line = 0; + x.edge = 'edge'; + x.string = s; + return postscript(x); + } + + function reserve_name(x) { + var c = x.id.charAt(0); + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { + x.identifier = x.reserved = true; + } + return x; + } + + function stmt(s, f) { + var x = symbol(s); + x.fud = f; + return reserve_name(x); + } + + function disrupt_stmt(s, f) { + var x = stmt(s, f); + x.disrupt = true; + } + + function labeled_stmt(s, f) { + var x = stmt(s, function labeled() { + var the_statement; + if (funct.breakage) { + funct.breakage.push(this); + } else { + funct.breakage = [this]; + } + the_statement = f.apply(this); + if (funct.breakage.length > 1) { + funct.breakage.pop(); + } else { + delete funct.breakage; + } + return the_statement; + }); + x.labeled = true; + } + + function prefix(s, f) { + var x = symbol(s, 150); + reserve_name(x); + x.nud = function () { + var that = this; + that.arity = 'prefix'; + if (typeof f === 'function') { + that = f(that); + if (that.arity !== 'prefix') { + return that; + } + } else { + if (s === 'typeof') { + one_space(); + } else { + no_space_only(); + } + that.first = expression(150); + } + switch (that.id) { + case '++': + case '--': + if (!option.plusplus) { + that.warn('unexpected_a'); + } else if ((!that.first.identifier || that.first.reserved) && + that.first.id !== '.' && that.first.id !== '[') { + that.warn('bad_operand'); + } + break; + default: + if (that.first.arity === 'prefix' || + that.first.arity === 'function') { + that.warn('unexpected_a'); + } + } + return that; + }; + return x; + } + + + function type(s, t, nud) { + var x = symbol(s); + x.arity = t; + if (nud) { + x.nud = nud; + } + return x; + } + + + function reserve(s, f) { + var x = symbol(s); + x.identifier = x.reserved = true; + if (typeof f === 'function') { + x.nud = f; + } + return x; + } + + + function constant(name) { + var x = reserve(name); + x.string = name; + x.nud = return_this; + return x; + } + + + function reservevar(s, v) { + return reserve(s, function () { + if (typeof v === 'function') { + v(this); + } + return this; + }); + } + + + function infix(s, p, f, w) { + var x = symbol(s, p); + reserve_name(x); + x.led = function (left) { + this.arity = 'infix'; + if (!w) { + spaces(prev_token, token); + spaces(); + } + if (!option.bitwise && this.bitwise) { + this.warn('unexpected_a'); + } + if (typeof f === 'function') { + return f(left, this); + } + this.first = left; + this.second = expression(p); + return this; + }; + return x; + } + + function expected_relation(node, message) { + if (node.assign) { + node.warn(message || 'conditional_assignment'); + } + return node; + } + + function expected_condition(node, message) { + switch (node.id) { + case '[': + case '-': + if (node.arity !== 'infix') { + node.warn(message || 'weird_condition'); + } + break; + case 'false': + case 'function': + case 'Infinity': + case 'NaN': + case 'null': + case 'true': + case 'undefined': + case 'void': + case '(number)': + case '(regexp)': + case '(string)': + case '{': + case '?': + case '~': + node.warn(message || 'weird_condition'); + break; + case '(': + if (node.first.id === 'new' || + (node.first.string === 'Boolean') || + (node.first.id === '.' && + numbery[node.first.second.string] === true)) { + node.warn(message || 'weird_condition'); + } + break; + } + return node; + } + + function check_relation(node) { + switch (node.arity) { + case 'prefix': + switch (node.id) { + case '{': + case '[': + node.warn('unexpected_a'); + break; + case '!': + node.warn('confusing_a'); + break; + } + break; + case 'function': + case 'regexp': + node.warn('unexpected_a'); + break; + default: + if (node.id === 'NaN') { + node.warn('isNaN'); + } else if (node.relation) { + node.warn('weird_relation'); + } + } + return node; + } + + + function relation(s, eqeq) { + var x = infix(s, 100, function (left, that) { + check_relation(left); + if (eqeq && !option.eqeq) { + that.warn('expected_a_b', eqeq, that.id); + } + var right = expression(100); + if (are_similar(left, right) || + ((left.id === '(string)' || left.id === '(number)') && + (right.id === '(string)' || right.id === '(number)'))) { + that.warn('weird_relation'); + } else if (left.id === 'typeof') { + if (right.id !== '(string)') { + right.warn("expected_string_a", artifact(right)); + } else if (right.string === 'undefined' || + right.string === 'null') { + left.warn("unexpected_typeof_a", right.string); + } + } else if (right.id === 'typeof') { + if (left.id !== '(string)') { + left.warn("expected_string_a", artifact(left)); + } else if (left.string === 'undefined' || + left.string === 'null') { + right.warn("unexpected_typeof_a", left.string); + } + } + that.first = left; + that.second = check_relation(right); + return that; + }); + x.relation = true; + return x; + } + + function lvalue(that, s) { + var master; + if (that.identifier) { + master = scope[that.string]; + if (master) { + if (scope[that.string].writeable !== true) { + that.warn('read_only'); + } + master.used -= 1; + if (s === '=') { + master.init = true; + } + } else if (that.reserved) { + that.warn('expected_identifier_a_reserved'); + } + } else if (that.id === '.' || that.id === '[') { + if (!that.first || that.first.string === 'arguments') { + that.warn('bad_assignment'); + } + } else { + that.warn('bad_assignment'); + } + } + + + function assignop(s, op) { + var x = infix(s, 20, function (left, that) { + var next; + that.first = left; + lvalue(left, s); + that.second = expression(20); + if (that.id === '=' && are_similar(that.first, that.second)) { + that.warn('weird_assignment'); + } + next = that; + while (next_token.id === '=') { + lvalue(next.second, '='); + next_token.first = next.second; + next.second = next_token; + next = next_token; + advance('='); + next.second = expression(20); + } + return that; + }); + x.assign = true; + if (op) { + if (syntax[op].bitwise) { + x.bitwise = true; + } + } + return x; + } + + + function bitwise(s, p) { + var x = infix(s, p, 'number'); + x.bitwise = true; + return x; + } + + + function suffix(s) { + var x = symbol(s, 150); + x.led = function (left) { + no_space_only(prev_token, token); + if (!option.plusplus) { + this.warn('unexpected_a'); + } else if ((!left.identifier || left.reserved) && + left.id !== '.' && left.id !== '[') { + this.warn('bad_operand'); + } + this.first = left; + this.arity = 'suffix'; + return this; + }; + return x; + } + + + function optional_identifier(variable) { + if (next_token.identifier) { + advance(); + if (token.reserved && variable) { + token.warn('expected_identifier_a_reserved'); + } + return token.string; + } + } + + + function identifier(variable) { + var i = optional_identifier(variable); + if (!i) { + next_token.stop(token.id === 'function' && next_token.id === '(' + ? 'name_function' + : 'expected_identifier_a'); + } + return i; + } + + + function statement() { + + var label, preamble, the_statement; + +// We don't like the empty statement. + + if (next_token.id === ';') { + next_token.warn('unexpected_a'); + semicolon(); + return; + } + +// Is this a labeled statement? + + if (next_token.identifier && !next_token.reserved && peek().id === ':') { + edge('label'); + label = next_token; + advance(); + advance(':'); + define('label', label); + if (next_token.labeled !== true || funct === global_funct) { + label.stop('unexpected_label_a'); + } else if (jx.test(label.string + ':')) { + label.warn('url'); + } + next_token.label = label; + label.init = true; + label.statement = next_token; + } + +// Parse the statement. + + preamble = next_token; + if (token.id !== 'else') { + edge(); + } + step_in('statement'); + the_statement = expression(0, true); + if (the_statement) { + +// Look for the final semicolon. + + if (the_statement.arity === 'statement') { + if (the_statement.id === 'switch' || + (the_statement.block && the_statement.id !== 'do')) { + spaces(); + } else { + semicolon(); + } + } else { + +// If this is an expression statement, determine if it is acceptable. +// We do not like +// new Blah; +// statements. If it is to be used at all, new should only be used to make +// objects, not side effects. The expression statements we do like do +// assignment or invocation or delete. + + if (the_statement.id === '(') { + if (the_statement.first.id === 'new') { + next_token.warn('bad_new'); + } + } else if (the_statement.id === '++' || + the_statement.id === '--') { + lvalue(the_statement.first); + } else if (!the_statement.assign && + the_statement.id !== 'delete') { + if (!option.closure || !preamble.comments) { + preamble.warn('assignment_function_expression'); + } + } + semicolon(); + } + } + step_out(); + if (label) { + label.dead = true; + } + return the_statement; + } + + + function statements() { + var array = [], disruptor, the_statement; + +// A disrupt statement may not be followed by any other statement. +// If the last statement is disrupt, then the sequence is disrupt. + + while (next_token.postscript !== true) { + if (next_token.id === ';') { + next_token.warn('unexpected_a'); + semicolon(); + } else { + if (next_token.string === 'use strict') { + if ((!node_js) || funct !== global_funct || array.length > 0) { + next_token.warn('function_strict'); + } + use_strict(); + } + if (disruptor) { + next_token.warn('unreachable_a_b', next_token.string, + disruptor.string); + disruptor = null; + } + the_statement = statement(); + if (the_statement) { + array.push(the_statement); + if (the_statement.disrupt) { + disruptor = the_statement; + array.disrupt = true; + } + } + } + } + return array; + } + + + function block(kind) { + +// A block is a sequence of statements wrapped in braces. + + var array, + curly = next_token, + old_block_var = block_var, + old_in_block = in_block, + old_strict_mode = strict_mode; + + in_block = kind !== 'function' && kind !== 'try' && kind !== 'catch'; + block_var = []; + if (curly.id === '{') { + spaces(); + advance('{'); + step_in(); + if (kind === 'function' && !use_strict() && !old_strict_mode && + !option.sloppy && funct.level === 1) { + next_token.warn('missing_use_strict'); + } + array = statements(); + strict_mode = old_strict_mode; + step_out('}', curly); + } else if (in_block) { + curly.stop('expected_a_b', '{', artifact()); + } else { + curly.warn('expected_a_b', '{', artifact()); + array = [statement()]; + array.disrupt = array[0].disrupt; + } + if (kind !== 'catch' && array.length === 0 && !option.debug) { + curly.warn('empty_block'); + } + block_var.forEach(function (name) { + scope[name].dead = true; + }); + block_var = old_block_var; + in_block = old_in_block; + return array; + } + + + function tally_property(name) { + if (option.properties && typeof property[name] !== 'number') { + token.warn('unexpected_property_a', name); + } + if (property[name]) { + property[name] += 1; + } else { + property[name] = 1; + } + } + + +// ECMAScript parser + + (function () { + var x = symbol('(identifier)'); + x.nud = function () { + var name = this.string, + master = scope[name], + writeable; + +// If the master is not in scope, then we may have an undeclared variable. +// Check the predefined list. If it was predefined, create the global +// variable. + + if (!master) { + writeable = predefined[name]; + if (typeof writeable === 'boolean') { + global_scope[name] = master = { + dead: false, + function: global_funct, + kind: 'var', + string: name, + writeable: writeable + }; + +// But if the variable is not in scope, and is not predefined, and if we are not +// in the global scope, then we have an undefined variable error. + + } else { + token.warn('used_before_a'); + } + } else { + this.master = master; + } + +// Annotate uses that cross scope boundaries. + + if (master) { + if (master.kind === 'label') { + this.warn('a_label'); + } else { + if (master.dead === true || master.dead === funct) { + this.warn('a_scope'); + } + master.used += 1; + if (master.function !== funct) { + if (master.function === global_funct) { + funct.global.push(name); + } else { + master.function.closure.push(name); + funct.outer.push(name); + } + } + } + } + return this; + }; + x.identifier = true; + }()); + + +// Build the syntax table by declaring the syntactic elements. + + type('(array)', 'array'); + type('(function)', 'function'); + type('(number)', 'number', return_this); + type('(object)', 'object'); + type('(string)', 'string', return_this); + type('(boolean)', 'boolean', return_this); + type('(regexp)', 'regexp', return_this); + + ultimate('(begin)'); + ultimate('(end)'); + ultimate('(error)'); + postscript(symbol('}')); + symbol(')'); + symbol(']'); + postscript(symbol('"')); + postscript(symbol('\'')); + symbol(';'); + symbol(':'); + symbol(','); + symbol('#'); + symbol('@'); + symbol('*/'); + postscript(reserve('case')); + reserve('catch'); + postscript(reserve('default')); + reserve('else'); + reserve('finally'); + + reservevar('arguments', function (x) { + if (strict_mode && funct === global_funct) { + x.warn('strict'); + } + funct.arguments = true; + }); + reservevar('eval'); + constant('false', 'boolean'); + constant('Infinity', 'number'); + constant('NaN', 'number'); + constant('null', ''); + reservevar('this', function (x) { + if (strict_mode && funct.statement && funct.name.charAt(0) > 'Z') { + x.warn('strict'); + } + }); + constant('true', 'boolean'); + constant('undefined', ''); + + infix('?', 30, function (left, that) { + step_in('?'); + that.first = expected_condition(expected_relation(left)); + that.second = expression(0); + spaces(); + step_out(); + var colon = next_token; + advance(':'); + step_in(':'); + spaces(); + that.third = expression(10); + that.arity = 'ternary'; + if (are_similar(that.second, that.third)) { + colon.warn('weird_ternary'); + } else if (are_similar(that.first, that.second)) { + that.warn('use_or'); + } + step_out(); + return that; + }); + + infix('||', 40, function (left, that) { + function paren_check(that) { + if (that.id === '&&' && !that.paren) { + that.warn('and'); + } + return that; + } + + that.first = paren_check(expected_condition(expected_relation(left))); + that.second = paren_check(expected_relation(expression(40))); + if (are_similar(that.first, that.second)) { + that.warn('weird_condition'); + } + return that; + }); + + infix('&&', 50, function (left, that) { + that.first = expected_condition(expected_relation(left)); + that.second = expected_relation(expression(50)); + if (are_similar(that.first, that.second)) { + that.warn('weird_condition'); + } + return that; + }); + + prefix('void', function (that) { + that.first = expression(0); + that.warn('expected_a_b', 'undefined', 'void'); + return that; + }); + + bitwise('|', 70); + bitwise('^', 80); + bitwise('&', 90); + + relation('==', '==='); + relation('==='); + relation('!=', '!=='); + relation('!=='); + relation('<'); + relation('>'); + relation('<='); + relation('>='); + + bitwise('<<', 120); + bitwise('>>', 120); + bitwise('>>>', 120); + + infix('in', 120, function (left, that) { + that.warn('infix_in'); + that.left = left; + that.right = expression(130); + return that; + }); + infix('instanceof', 120); + infix('+', 130, function (left, that) { + if (left.id === '(number)') { + if (left.number === 0) { + left.warn('unexpected_a', '0'); + } + } else if (left.id === '(string)') { + if (left.string === '') { + left.warn('expected_a_b', 'String', '\'\''); + } + } + var right = expression(130); + if (right.id === '(number)') { + if (right.number === 0) { + right.warn('unexpected_a', '0'); + } + } else if (right.id === '(string)') { + if (right.string === '') { + right.warn('expected_a_b', 'String', '\'\''); + } + } + if (left.id === right.id) { + if (left.id === '(string)' || left.id === '(number)') { + if (left.id === '(string)') { + left.string += right.string; + if (jx.test(left.string)) { + left.warn('url'); + } + } else { + left.number += right.number; + } + left.thru = right.thru; + return left; + } + } + that.first = left; + that.second = right; + return that; + }); + prefix('+'); + prefix('+++', function () { + token.warn('confusing_a'); + this.first = expression(150); + this.arity = 'prefix'; + return this; + }); + infix('+++', 130, function (left) { + token.warn('confusing_a'); + this.first = left; + this.second = expression(130); + return this; + }); + infix('-', 130, function (left, that) { + if ((left.id === '(number)' && left.number === 0) || left.id === '(string)') { + left.warn('unexpected_a'); + } + var right = expression(130); + if ((right.id === '(number)' && right.number === 0) || right.id === '(string)') { + right.warn('unexpected_a'); + } + if (left.id === right.id && left.id === '(number)') { + left.number -= right.number; + left.thru = right.thru; + return left; + } + that.first = left; + that.second = right; + return that; + }); + prefix('-'); + prefix('---', function () { + token.warn('confusing_a'); + this.first = expression(150); + this.arity = 'prefix'; + return this; + }); + infix('---', 130, function (left) { + token.warn('confusing_a'); + this.first = left; + this.second = expression(130); + return this; + }); + infix('*', 140, function (left, that) { + if ((left.id === '(number)' && (left.number === 0 || left.number === 1)) || left.id === '(string)') { + left.warn('unexpected_a'); + } + var right = expression(140); + if ((right.id === '(number)' && (right.number === 0 || right.number === 1)) || right.id === '(string)') { + right.warn('unexpected_a'); + } + if (left.id === right.id && left.id === '(number)') { + left.number *= right.number; + left.thru = right.thru; + return left; + } + that.first = left; + that.second = right; + return that; + }); + infix('/', 140, function (left, that) { + if ((left.id === '(number)' && left.number === 0) || left.id === '(string)') { + left.warn('unexpected_a'); + } + var right = expression(140); + if ((right.id === '(number)' && (right.number === 0 || right.number === 1)) || right.id === '(string)') { + right.warn('unexpected_a'); + } + if (left.id === right.id && left.id === '(number)') { + left.number /= right.number; + left.thru = right.thru; + return left; + } + that.first = left; + that.second = right; + return that; + }); + infix('%', 140, function (left, that) { + if ((left.id === '(number)' && (left.number === 0 || left.number === 1)) || left.id === '(string)') { + left.warn('unexpected_a'); + } + var right = expression(140); + if ((right.id === '(number)' && right.number === 0) || right.id === '(string)') { + right.warn('unexpected_a'); + } + if (left.id === right.id && left.id === '(number)') { + left.number %= right.number; + left.thru = right.thru; + return left; + } + that.first = left; + that.second = right; + return that; + }); + + suffix('++'); + prefix('++'); + + suffix('--'); + prefix('--'); + prefix('delete', function (that) { + one_space(); + var p = expression(0); + if (!p || (p.id !== '.' && p.id !== '[')) { + next_token.warn('deleted'); + } + that.first = p; + return that; + }); + + + prefix('~', function (that) { + no_space_only(); + if (!option.bitwise) { + that.warn('unexpected_a'); + } + that.first = expression(150); + return that; + }); + function banger(that) { + no_space_only(); + that.first = expected_condition(expression(150)); + if (bang[that.first.id] === that || that.first.assign) { + that.warn('confusing_a'); + } + return that; + } + prefix('!', banger); + prefix('!!', banger); + prefix('typeof'); + prefix('new', function (that) { + one_space(); + var c = expression(160), n, p, v; + that.first = c; + if (c.id !== 'function') { + if (c.identifier) { + switch (c.string) { + case 'Object': + token.warn('use_object'); + break; + case 'Array': + if (next_token.id === '(') { + p = next_token; + p.first = this; + advance('('); + if (next_token.id !== ')') { + n = expression(0); + p.second = [n]; + if (n.id === '(string)' || next_token.id === ',') { + p.warn('use_array'); + } + while (next_token.id === ',') { + advance(','); + p.second.push(expression(0)); + } + } else { + token.warn('use_array'); + } + advance(')', p); + return p; + } + token.warn('use_array'); + break; + case 'Number': + case 'String': + case 'Boolean': + case 'Math': + case 'JSON': + c.warn('not_a_constructor'); + break; + case 'Function': + if (!option.evil) { + next_token.warn('function_eval'); + } + break; + case 'Date': + case 'RegExp': + case 'this': + break; + default: + if (c.id !== 'function') { + v = c.string.charAt(0); + if (!option.newcap && (v < 'A' || v > 'Z')) { + token.warn('constructor_name_a'); + } + } + } + } else { + if (c.id !== '.' && c.id !== '[' && c.id !== '(') { + token.warn('bad_constructor'); + } + } + } else { + that.warn('weird_new'); + } + if (next_token.id !== '(') { + next_token.warn('missing_a', '()'); + } + return that; + }); + + infix('(', 160, function (left, that) { + var e, p; + if (indent && indent.mode === 'expression') { + no_space(prev_token, token); + } else { + no_space_only(prev_token, token); + } + if (!left.immed && left.id === 'function') { + next_token.warn('wrap_immediate'); + } + p = []; + if (left.identifier) { + if (left.string.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) { + if (left.string !== 'Number' && left.string !== 'String' && + left.string !== 'Boolean' && left.string !== 'Date') { + if (left.string === 'Math') { + left.warn('not_a_function'); + } else if (left.string === 'Object') { + token.warn('use_object'); + } else if (left.string === 'Array' || !option.newcap) { + left.warn('missing_a', 'new'); + } + } + } else if (left.string === 'JSON') { + left.warn('not_a_function'); + } + } else if (left.id === '.') { + if (left.second.string === 'split' && + left.first.id === '(string)') { + left.second.warn('use_array'); + } + } + step_in(); + if (next_token.id !== ')') { + no_space(); + for (;;) { + edge(); + e = expression(10); + if (left.string === 'Boolean' && (e.id === '!' || e.id === '~')) { + e.warn('weird_condition'); + } + p.push(e); + if (next_token.id !== ',') { + break; + } + comma(); + } + } + no_space(); + step_out(')', that); + if (typeof left === 'object') { + if (left.string === 'parseInt' && p.length === 1) { + left.warn('radix'); + } else if (left.string === 'String' && p.length >= 1 && p[0].id === '(string)') { + left.warn('unexpected_a'); + } + if (!option.evil) { + if (left.string === 'eval' || left.string === 'Function' || + left.string === 'execScript') { + left.warn('evil'); + } else if (p[0] && p[0].id === '(string)' && + (left.string === 'setTimeout' || + left.string === 'setInterval')) { + left.warn('implied_evil'); + } + } + if (!left.identifier && left.id !== '.' && left.id !== '[' && + left.id !== '(' && left.id !== '&&' && left.id !== '||' && + left.id !== '?') { + left.warn('bad_invocation'); + } + if (left.id === '.') { + if (p.length > 0 && + left.first && left.first.first && + are_similar(p[0], left.first.first)) { + if (left.second.string === 'call' || + (left.second.string === 'apply' && (p.length === 1 || + (p[1].arity === 'prefix' && p[1].id === '[')))) { + left.second.warn('unexpected_a'); + } + } + if (left.second.string === 'toString') { + if (left.first.id === '(string)' || left.first.id === '(number)') { + left.second.warn('unexpected_a'); + } + } + } + } + that.first = left; + that.second = p; + return that; + }, true); + + prefix('(', function (that) { + step_in('expression'); + no_space(); + edge(); + if (next_token.id === 'function') { + next_token.immed = true; + } + var value = expression(0); + value.paren = true; + no_space(); + step_out(')', that); + if (value.id === 'function') { + switch (next_token.id) { + case '(': + next_token.warn('move_invocation'); + break; + case '.': + case '[': + next_token.warn('unexpected_a'); + break; + default: + that.warn('bad_wrap'); + } + } else if (!value.arity) { + if (!option.closure || !that.comments) { + that.warn('unexpected_a'); + } + } + return value; + }); + + infix('.', 170, function (left, that) { + no_space(prev_token, token); + no_space(); + var name = identifier(); + if (typeof name === 'string') { + tally_property(name); + } + that.first = left; + that.second = token; + if (left && left.string === 'arguments' && + (name === 'callee' || name === 'caller')) { + left.warn('avoid_a', 'arguments.' + name); + } else if (!option.evil && left && left.string === 'document' && + (name === 'write' || name === 'writeln')) { + left.warn('write_is_wrong'); + } else if (!option.stupid && syx.test(name)) { + token.warn('sync_a'); + } else if (left && left.id === '{') { + that.warn('unexpected_a'); + } + if (!option.evil && (name === 'eval' || name === 'execScript')) { + next_token.warn('evil'); + } + return that; + }, true); + + infix('[', 170, function (left, that) { + var e, s; + no_space_only(prev_token, token); + no_space(); + step_in(); + edge(); + e = expression(0); + switch (e.id) { + case '(number)': + if (e.id === '(number)' && left.id === 'arguments') { + left.warn('use_param'); + } + break; + case '(string)': + if (!option.evil && + (e.string === 'eval' || e.string === 'execScript')) { + e.warn('evil'); + } else if (!option.sub && ix.test(e.string)) { + s = syntax[e.string]; + if (!s || !s.reserved) { + e.warn('subscript'); + } + } + tally_property(e.string); + break; + } + if (left && (left.id === '{' || (left.id === '[' && left.arity === 'prefix'))) { + that.warn('unexpected_a'); + } + step_out(']', that); + no_space(prev_token, token); + that.first = left; + that.second = e; + return that; + }, true); + + prefix('[', function (that) { + that.first = []; + step_in('array'); + while (next_token.id !== '(end)') { + while (next_token.id === ',') { + next_token.warn('unexpected_a'); + advance(','); + } + if (next_token.id === ']') { + break; + } + indent.wrap = false; + edge(); + that.first.push(expression(10)); + if (next_token.id === ',') { + comma(); + if (next_token.id === ']') { + token.warn('unexpected_a'); + break; + } + } else { + break; + } + } + step_out(']', that); + return that; + }, 170); + + + function property_name() { + var id = optional_identifier(); + if (!id) { + if (next_token.id === '(string)') { + id = next_token.string; + advance(); + } else if (next_token.id === '(number)') { + id = next_token.number.toString(); + advance(); + } + } + return id; + } + + + + assignop('='); + assignop('+=', '+'); + assignop('-=', '-'); + assignop('*=', '*'); + assignop('/=', '/').nud = function () { + next_token.stop('slash_equal'); + }; + assignop('%=', '%'); + assignop('&=', '&'); + assignop('|=', '|'); + assignop('^=', '^'); + assignop('<<=', '<<'); + assignop('>>=', '>>'); + assignop('>>>=', '>>>'); + + function function_parameters() { + var id, parameters = [], paren = next_token; + advance('('); + token.function = funct; + step_in(); + no_space(); + if (next_token.id !== ')') { + for (;;) { + edge(); + id = identifier(); + if (token.reserved) { + token.warn('expected_identifier_a_reserved'); + } + define('parameter', token); + parameters.push(id); + token.init = true; + token.writeable = true; + if (next_token.id !== ',') { + break; + } + comma(); + } + } + no_space(); + step_out(')', paren); + return parameters; + } + + function do_function(func, name) { + var old_funct = funct, + old_option = option, + old_scope = scope; + scope = Object.create(old_scope); + funct = { + closure: [], + global: [], + level: old_funct.level + 1, + line: next_token.line, + loopage: 0, + name: name || '\'' + (anonname || '').replace(nx, sanitize) + '\'', + outer: [], + scope: scope + }; + funct.parameter = function_parameters(); + func.function = funct; + option = Object.create(old_option); + functions.push(funct); + if (name) { + func.name = name; + func.string = name; + define('function', func); + func.init = true; + func.used += 1; + } + func.writeable = false; + one_space(); + func.block = block('function'); + Object.keys(scope).forEach(function (name) { + var master = scope[name]; + if (!master.used && master.kind !== 'exception' && + (master.kind !== 'parameter' || !option.unparam)) { + master.warn('unused_a'); + } else if (!master.init) { + master.warn('uninitialized_a'); + } + }); + funct = old_funct; + option = old_option; + scope = old_scope; + } + + prefix('{', function (that) { + var get, i, j, name, set, seen = Object.create(null); + that.first = []; + step_in(); + while (next_token.id !== '}') { + indent.wrap = false; + +// JSLint recognizes the ES5 extension for get/set in object literals, +// but requires that they be used in pairs. + + edge(); + if (next_token.string === 'get' && peek().id !== ':') { + get = next_token; + advance('get'); + one_space_only(); + name = next_token; + i = property_name(); + if (!i) { + next_token.stop('missing_property'); + } + get.string = ''; + do_function(get); + if (funct.loopage) { + get.warn('function_loop'); + } + if (get.function.parameter.length) { + get.warn('parameter_a_get_b', get.function.parameter[0], i); + } + comma(); + set = next_token; + spaces(); + edge(); + advance('set'); + set.string = ''; + one_space_only(); + j = property_name(); + if (i !== j) { + token.stop('expected_a_b', i, j || next_token.string); + } + do_function(set); + if (set.block.length === 0) { + token.warn('missing_a', 'throw'); + } + if (set.function.parameter.length === 0) { + set.stop('parameter_set_a', 'value'); + } else if (set.function.parameter[0] !== 'value') { + set.stop('expected_a_b', 'value', + set.function.parameter[0]); + } + name.first = [get, set]; + } else { + name = next_token; + i = property_name(); + if (typeof i !== 'string') { + next_token.stop('missing_property'); + } + advance(':'); + spaces(); + name.first = expression(10); + } + that.first.push(name); + if (seen[i] === true) { + next_token.warn('duplicate_a', i); + } + seen[i] = true; + tally_property(i); + if (next_token.id !== ',') { + break; + } + for (;;) { + comma(); + if (next_token.id !== ',') { + break; + } + next_token.warn('unexpected_a'); + } + if (next_token.id === '}') { + token.warn('unexpected_a'); + } + } + step_out('}', that); + return that; + }); + + stmt('{', function () { + next_token.warn('statement_block'); + this.arity = 'statement'; + this.block = statements(); + this.disrupt = this.block.disrupt; + advance('}', this); + return this; + }); + + stmt('/*global', directive); + stmt('/*globals', directive); + stmt('/*jslint', directive); + stmt('/*member', directive); + stmt('/*members', directive); + stmt('/*property', directive); + stmt('/*properties', directive); + + stmt('var', function () { + +// JavaScript does not have block scope. It only has function scope. So, +// declaring a variable in a block can have unexpected consequences. + +// var.first will contain an array, the array containing name tokens +// and assignment tokens. + + var assign, id, name; + + if (funct.loopage) { + next_token.warn('var_loop'); + } else if (funct.varstatement && !option.vars) { + next_token.warn('combine_var'); + } + if (funct !== global_funct) { + funct.varstatement = true; + } + this.arity = 'statement'; + this.first = []; + step_in('var'); + for (;;) { + name = next_token; + id = identifier(true); + define('var', name); + name.dead = funct; + if (next_token.id === '=') { + if (funct === global_funct && !name.writeable) { + name.warn('read_only'); + } + assign = next_token; + assign.first = name; + spaces(); + advance('='); + spaces(); + if (next_token.id === 'undefined') { + token.warn('unnecessary_initialize', id); + } + if (peek(0).id === '=' && next_token.identifier) { + next_token.stop('var_a_not'); + } + assign.second = expression(0); + assign.arity = 'infix'; + name.init = true; + this.first.push(assign); + } else { + this.first.push(name); + } + name.dead = false; + name.writeable = true; + if (next_token.id !== ',') { + break; + } + comma(); + indent.wrap = false; + if (var_mode && next_token.line === token.line && + this.first.length === 1) { + var_mode = null; + indent.open = false; + indent.at -= option.indent; + } + spaces(); + edge(); + } + var_mode = null; + step_out(); + return this; + }); + + stmt('function', function () { + one_space(); + if (in_block) { + token.warn('function_block'); + } + var name = next_token, + id = identifier(true); + define('var', name); + if (!name.writeable) { + name.warn('read_only'); + } + name.init = true; + name.statement = true; + no_space(); + this.arity = 'statement'; + do_function(this, id); + if (next_token.id === '(' && next_token.line === token.line) { + next_token.stop('function_statement'); + } + return this; + }); + + prefix('function', function (that) { + var id = optional_identifier(true), name; + if (id) { + name = token; + no_space(); + } else { + id = ''; + one_space(); + } + do_function(that, id); + if (name) { + name.function = that.function; + } + if (funct.loopage) { + that.warn('function_loop'); + } + switch (next_token.id) { + case ';': + case '(': + case ')': + case ',': + case ']': + case '}': + case ':': + case '(end)': + break; + case '.': + if (peek().string !== 'bind' || peek(1).id !== '(') { + next_token.warn('unexpected_a'); + } + break; + default: + next_token.stop('unexpected_a'); + } + that.arity = 'function'; + return that; + }); + + stmt('if', function () { + var paren = next_token; + one_space(); + advance('('); + step_in('control'); + no_space(); + edge(); + this.arity = 'statement'; + this.first = expected_condition(expected_relation(expression(0))); + no_space(); + step_out(')', paren); + one_space(); + this.block = block('if'); + if (next_token.id === 'else') { + if (this.block.disrupt) { + next_token.warn(this.elif ? 'use_nested_if' : 'unnecessary_else'); + } + one_space(); + advance('else'); + one_space(); + if (next_token.id === 'if') { + next_token.elif = true; + this.else = statement(true); + } else { + this.else = block('else'); + } + if (this.else.disrupt && this.block.disrupt) { + this.disrupt = true; + } + } + return this; + }); + + stmt('try', function () { + +// try.first The catch variable +// try.second The catch clause +// try.third The finally clause +// try.block The try block + + var exception_variable, paren; + one_space(); + this.arity = 'statement'; + this.block = block('try'); + if (next_token.id === 'catch') { + one_space(); + advance('catch'); + one_space(); + paren = next_token; + advance('('); + step_in('control'); + no_space(); + edge(); + exception_variable = next_token; + this.first = identifier(); + define('exception', exception_variable); + exception_variable.init = true; + no_space(); + step_out(')', paren); + one_space(); + this.second = block('catch'); + if (this.second.length) { + if (this.first === 'ignore') { + exception_variable.warn('unexpected_a'); + } + } else { + if (this.first !== 'ignore') { + exception_variable.warn('expected_a_b', 'ignore', + exception_variable.string); + } + } + exception_variable.dead = true; + } + if (next_token.id === 'finally') { + one_space(); + advance('finally'); + one_space(); + this.third = block('finally'); + } else if (!this.second) { + next_token.stop('expected_a_b', 'catch', artifact()); + } + return this; + }); + + labeled_stmt('while', function () { + one_space(); + var paren = next_token; + funct.loopage += 1; + advance('('); + step_in('control'); + no_space(); + edge(); + this.arity = 'statement'; + this.first = expected_relation(expression(0)); + if (this.first.id !== 'true') { + expected_condition(this.first, 'unexpected_a'); + } + no_space(); + step_out(')', paren); + one_space(); + this.block = block('while'); + if (this.block.disrupt) { + prev_token.warn('strange_loop'); + } + funct.loopage -= 1; + return this; + }); + + reserve('with'); + + labeled_stmt('switch', function () { + +// switch.first the switch expression +// switch.second the array of cases. A case is 'case' or 'default' token: +// case.first the array of case expressions +// case.second the array of statements +// If all of the arrays of statements are disrupt, then the switch is disrupt. + + var cases = [], + old_in_block = in_block, + particular, + that = token, + the_case = next_token; + + function find_duplicate_case(value) { + if (are_similar(particular, value)) { + value.warn('duplicate_a'); + } + } + + one_space(); + advance('('); + no_space(); + step_in(); + this.arity = 'statement'; + this.first = expected_condition(expected_relation(expression(0))); + no_space(); + step_out(')', the_case); + one_space(); + advance('{'); + step_in(); + in_block = true; + this.second = []; + if (that.from !== next_token.from && !option.white) { + next_token.warn('expected_a_at_b_c', next_token.string, that.from, next_token.from); + } + while (next_token.id === 'case') { + the_case = next_token; + the_case.first = []; + the_case.arity = 'case'; + for (;;) { + spaces(); + edge('case'); + advance('case'); + one_space(); + particular = expression(0); + cases.forEach(find_duplicate_case); + cases.push(particular); + the_case.first.push(particular); + if (particular.id === 'NaN') { + particular.warn('unexpected_a'); + } + no_space_only(); + advance(':'); + if (next_token.id !== 'case') { + break; + } + } + spaces(); + the_case.second = statements(); + if (the_case.second && the_case.second.length > 0) { + if (!the_case.second[the_case.second.length - 1].disrupt) { + next_token.warn('missing_a_after_b', 'break', 'case'); + } + } else { + next_token.warn('empty_case'); + } + this.second.push(the_case); + } + if (this.second.length === 0) { + next_token.warn('missing_a', 'case'); + } + if (next_token.id === 'default') { + spaces(); + the_case = next_token; + the_case.arity = 'case'; + edge('case'); + advance('default'); + no_space_only(); + advance(':'); + spaces(); + the_case.second = statements(); + if (the_case.second && the_case.second.length > 0) { + this.disrupt = the_case.second[the_case.second.length - 1].disrupt; + } else { + the_case.warn('empty_case'); + } + this.second.push(the_case); + } + if (this.break) { + this.disrupt = false; + } + spaces(); + step_out('}', this); + in_block = old_in_block; + return this; + }); + + stmt('debugger', function () { + if (!option.debug) { + this.warn('unexpected_a'); + } + this.arity = 'statement'; + return this; + }); + + labeled_stmt('do', function () { + funct.loopage += 1; + one_space(); + this.arity = 'statement'; + this.block = block('do'); + if (this.block.disrupt) { + prev_token.warn('strange_loop'); + } + one_space(); + advance('while'); + var paren = next_token; + one_space(); + advance('('); + step_in(); + no_space(); + edge(); + this.first = expected_condition(expected_relation(expression(0)), 'unexpected_a'); + no_space(); + step_out(')', paren); + funct.loopage -= 1; + return this; + }); + + labeled_stmt('for', function () { + + var blok, filter, master, ok = false, paren = next_token, value; + this.arity = 'statement'; + funct.loopage += 1; + advance('('); + if (next_token.id === ';') { + no_space(); + advance(';'); + no_space(); + advance(';'); + no_space(); + advance(')'); + blok = block('for'); + } else { + step_in('control'); + spaces(this, paren); + no_space(); + if (next_token.id === 'var') { + next_token.stop('move_var'); + } + edge(); + if (peek(0).id === 'in') { + this.forin = true; + value = expression(1000); + master = value.master; + if (!master) { + value.stop('bad_in_a'); + } + if (master.kind !== 'var' || master.function !== funct || + !master.writeable || master.dead) { + value.warn('bad_in_a'); + } + master.init = true; + master.used -= 1; + this.first = value; + advance('in'); + this.second = expression(20); + step_out(')', paren); + blok = block('for'); + if (!option.forin) { + if (blok.length === 1 && typeof blok[0] === 'object') { + if (blok[0].id === 'if' && !blok[0].else) { + filter = blok[0].first; + while (filter.id === '&&') { + filter = filter.first; + } + switch (filter.id) { + case '===': + case '!==': + ok = filter.first.id === '[' + ? are_similar(filter.first.first, this.second) && + are_similar(filter.first.second, this.first) + : filter.first.id === 'typeof' && + filter.first.first.id === '[' && + are_similar(filter.first.first.first, this.second) && + are_similar(filter.first.first.second, this.first); + break; + case '(': + ok = filter.first.id === '.' && (( + are_similar(filter.first.first, this.second) && + filter.first.second.string === 'hasOwnProperty' && + are_similar(filter.second[0], this.first) + ) || ( + filter.first.first.id === '.' && + filter.first.first.first.first && + filter.first.first.first.first.string === 'Object' && + filter.first.first.first.id === '.' && + filter.first.first.first.second.string === 'prototype' && + filter.first.first.second.string === 'hasOwnProperty' && + filter.first.second.string === 'call' && + are_similar(filter.second[0], this.second) && + are_similar(filter.second[1], this.first) + )); + break; + } + } else if (blok[0].id === 'switch') { + ok = blok[0].id === 'switch' && + blok[0].first.id === 'typeof' && + blok[0].first.first.id === '[' && + are_similar(blok[0].first.first.first, this.second) && + are_similar(blok[0].first.first.second, this.first); + } + } + if (!ok) { + this.warn('for_if'); + } + } + } else { + edge(); + this.first = []; + for (;;) { + this.first.push(expression(0, 'for')); + if (next_token.id !== ',') { + break; + } + comma(); + } + semicolon(); + edge(); + this.second = expected_relation(expression(0)); + if (this.second.id !== 'true') { + expected_condition(this.second, 'unexpected_a'); + } + semicolon(token); + if (next_token.id === ';') { + next_token.stop('expected_a_b', ')', ';'); + } + this.third = []; + edge(); + for (;;) { + this.third.push(expression(0, 'for')); + if (next_token.id !== ',') { + break; + } + comma(); + } + no_space(); + step_out(')', paren); + one_space(); + blok = block('for'); + } + } + if (blok.disrupt) { + prev_token.warn('strange_loop'); + } + this.block = blok; + funct.loopage -= 1; + return this; + }); + + function optional_label(that) { + var label = next_token.string, + master; + that.arity = 'statement'; + if (!funct.breakage || (!option.continue && that.id === 'continue')) { + that.warn('unexpected_a'); + } else if (next_token.identifier && token.line === next_token.line) { + one_space_only(); + master = scope[label]; + if (!master || master.kind !== 'label') { + next_token.warn('not_a_label'); + } else if (master.dead || master.function !== funct) { + next_token.warn('not_a_scope'); + } else { + master.used += 1; + if (that.id === 'break') { + master.statement.break = true; + } + if (funct.breakage[funct.breakage.length - 1] === master.statement) { + next_token.warn('unexpected_a'); + } + } + that.first = next_token; + advance(); + } else { + if (that.id === 'break') { + funct.breakage[funct.breakage.length - 1].break = true; + } + } + return that; + + } + + disrupt_stmt('break', function () { + return optional_label(this); + }); + + disrupt_stmt('continue', function () { + return optional_label(this); + }); + + disrupt_stmt('return', function () { + if (funct === global_funct) { + this.warn('unexpected_a'); + } + this.arity = 'statement'; + if (next_token.id !== ';' && next_token.line === token.line) { + if (option.closure) { + spaces(); + } else { + one_space_only(); + } + if (next_token.id === '/' || next_token.id === '(regexp)') { + next_token.warn('wrap_regexp'); + } + this.first = expression(0); + if (this.first.assign) { + this.first.warn('unexpected_a'); + } + } + return this; + }); + + disrupt_stmt('throw', function () { + this.arity = 'statement'; + one_space_only(); + this.first = expression(20); + return this; + }); + + +// Superfluous reserved words + + reserve('class'); + reserve('const'); + reserve('enum'); + reserve('export'); + reserve('extends'); + reserve('import'); + reserve('super'); + +// Harmony reserved words + + reserve('implements'); + reserve('interface'); + reserve('let'); + reserve('package'); + reserve('private'); + reserve('protected'); + reserve('public'); + reserve('static'); + reserve('yield'); + + +// Parse JSON + + function json_value() { + + function json_object() { + var brace = next_token, object = Object.create(null); + advance('{'); + if (next_token.id !== '}') { + while (next_token.id !== '(end)') { + while (next_token.id === ',') { + next_token.warn('unexpected_a'); + advance(','); + } + if (next_token.id !== '(string)') { + next_token.warn('expected_string_a'); + } + if (object[next_token.string] === true) { + next_token.warn('duplicate_a'); + } else if (next_token.string === '__proto__') { + next_token.warn('dangling_a'); + } else { + object[next_token.string] = true; + } + advance(); + advance(':'); + json_value(); + if (next_token.id !== ',') { + break; + } + advance(','); + if (next_token.id === '}') { + token.warn('unexpected_a'); + break; + } + } + } + advance('}', brace); + } + + function json_array() { + var bracket = next_token; + advance('['); + if (next_token.id !== ']') { + while (next_token.id !== '(end)') { + while (next_token.id === ',') { + next_token.warn('unexpected_a'); + advance(','); + } + json_value(); + if (next_token.id !== ',') { + break; + } + advance(','); + if (next_token.id === ']') { + token.warn('unexpected_a'); + break; + } + } + } + advance(']', bracket); + } + + switch (next_token.id) { + case '{': + json_object(); + break; + case '[': + json_array(); + break; + case 'true': + case 'false': + case 'null': + case '(number)': + case '(string)': + advance(); + break; + case '-': + advance('-'); + no_space_only(); + advance('(number)'); + break; + default: + next_token.stop('unexpected_a'); + } + } + + +// The actual JSLINT function itself. + + itself = function JSLint(the_source, the_option) { + + var i, predef, tree; + itself.errors = []; + itself.tree = ''; + itself.properties = ''; + begin = prev_token = token = next_token = + Object.create(syntax['(begin)']); + tokens = []; + predefined = Object.create(null); + add_to_predefined(standard); + property = Object.create(null); + if (the_option) { + option = Object.create(the_option); + predef = option.predef; + if (predef) { + if (Array.isArray(predef)) { + for (i = 0; i < predef.length; i += 1) { + predefined[predef[i]] = true; + } + } else if (typeof predef === 'object') { + add_to_predefined(predef); + } + } + } else { + option = Object.create(null); + } + option.indent = +option.indent || 4; + option.maxerr = +option.maxerr || 50; + global_scope = scope = Object.create(null); + global_funct = funct = { + scope: scope, + loopage: 0, + level: 0 + }; + functions = [funct]; + block_var = []; + + comments = []; + comments_off = false; + in_block = false; + indent = null; + json_mode = false; + lookahead = []; + node_js = false; + prereg = true; + strict_mode = false; + var_mode = null; + warnings = 0; + lex.init(the_source); + + assume(); + + try { + advance(); + if (next_token.id === '(number)') { + next_token.stop('unexpected_a'); + } else { + switch (next_token.id) { + case '{': + case '[': + comments_off = true; + json_mode = true; + json_value(); + break; + default: + +// If the first token is a semicolon, ignore it. This is sometimes used when +// files are intended to be appended to files that may be sloppy. A sloppy +// file may be depending on semicolon insertion on its last line. + + step_in(1); + if (next_token.id === ';' && !node_js) { + next_token.edge = true; + advance(';'); + } + tree = statements(); + begin.first = tree; + itself.tree = begin; + if (tree.disrupt) { + prev_token.warn('weird_program'); + } + } + } + indent = null; + advance('(end)'); + itself.property = property; + } catch (e) { + if (e) { // ~~ + itself.errors.push({ + reason : e.message, + line : e.line || next_token.line, + character : e.character || next_token.from + }, null); + } + } + return itself.errors.length === 0; + }; + + function unique(array) { + array = array.sort(); + var i, length = 0, previous, value; + for (i = 0; i < array.length; i += 1) { + value = array[i]; + if (value !== previous) { + array[length] = value; + previous = value; + length += 1; + } + } + array.length = length; + return array; + } + +// Data summary. + + itself.data = function () { + var data = {functions: []}, + function_data, + i, + the_function, + the_scope; + data.errors = itself.errors; + data.json = json_mode; + data.global = unique(Object.keys(global_scope)); + + function selects(name) { + var kind = the_scope[name].kind; + switch (kind) { + case 'var': + case 'exception': + case 'label': + function_data[kind].push(name); + break; + } + } + + for (i = 1; i < functions.length; i += 1) { + the_function = functions[i]; + function_data = { + name: the_function.name, + line: the_function.line, + level: the_function.level, + parameter: the_function.parameter, + var: [], + exception: [], + closure: unique(the_function.closure), + outer: unique(the_function.outer), + global: unique(the_function.global), + label: [] + }; + the_scope = the_function.scope; + Object.keys(the_scope).forEach(selects); + function_data.var.sort(); + function_data.exception.sort(); + function_data.label.sort(); + data.functions.push(function_data); + } + data.tokens = tokens; + return data; + }; + + itself.error_report = function (data) { + var evidence, i, output = [], warning; + if (data.errors.length) { + if (data.json) { + output.push('JSON: bad.
    '); + } + for (i = 0; i < data.errors.length; i += 1) { + warning = data.errors[i]; + if (warning) { + evidence = warning.evidence || ''; + output.push(''); + if (isFinite(warning.line)) { + output.push('
    line ' + + String(warning.line) + + ' character ' + String(warning.character) + + '
    '); + } + output.push(warning.reason.entityify() + '
    '); + if (evidence) { + output.push('
    ' + evidence.entityify() + '
    '); + } + } + } + } + return output.join(''); + }; + + + itself.report = function (data) { + var dl, i, j, names, output = [], the_function; + + function detail(h, array) { + var comma_needed = false; + if (array.length) { + output.push("
    " + h + "
    "); + array.forEach(function (item) { + output.push((comma_needed ? ', ' : '') + item); + comma_needed = true; + }); + output.push("
    "); + } + } + + output.push('
    '); + if (data.global.length) { + detail('global', data.global); + dl = true; + } else if (data.json) { + if (!data.errors.length) { + output.push("
    JSON: good.
    "); + } + } else { + output.push("
    No new global variables introduced.
    "); + } + if (dl) { + output.push("
    "); + } else { + output[0] = ''; + } + + if (data.functions) { + for (i = 0; i < data.functions.length; i += 1) { + the_function = data.functions[i]; + names = []; + if (the_function.params) { + for (j = 0; j < the_function.params.length; j += 1) { + names[j] = the_function.params[j].string; + } + } + output.push('
    line ' + String(the_function.line) + + '
    ' + the_function.name.entityify()); + detail('parameter', the_function.parameter); + detail('variable', the_function.var); + detail('exception', the_function.exception); + detail('closure', the_function.closure); + detail('outer', the_function.outer); + detail('global', the_function.global); + detail('label', the_function.label); + output.push('
    '); + } + } + return output.join(''); + }; + + itself.properties_report = function (property) { + if (!property) { + return ''; + } + var i, + key, + keys = Object.keys(property).sort(), + mem = ' ', + name, + not_first = false, + output = ['/*properties']; + for (i = 0; i < keys.length; i += 1) { + key = keys[i]; + if (property[key] > 0) { + if (not_first) { + mem += ','; + } + name = ix.test(key) + ? key + : '\'' + key.replace(nx, sanitize) + '\''; + if (mem.length + name.length >= 80) { + output.push(mem); + mem = ' '; + } else { + mem += ' '; + } + mem += name; + not_first = true; + } + } + output.push(mem, '*/\n'); + return output.join('\n'); + }; + + itself.color = function (data) { + var from, + i = 1, + level, + line, + result = [], + thru, + data_token = data.tokens[0]; + while (data_token && data_token.id !== '(end)') { + from = data_token.from; + line = data_token.line; + thru = data_token.thru; + level = data_token.function.level; + do { + thru = data_token.thru; + data_token = data.tokens[i]; + i += 1; + } while (data_token && data_token.line === line && + data_token.from - thru < 5 && + level === data_token.function.level); + result.push({ + line: line, + level: level, + from: from, + thru: thru + }); + } + return result; + }; + + itself.jslint = itself; + + itself.edition = '2014-07-08'; + + return itself; +}()); diff --git a/branch.es5_web/json2.js b/branch.es5_web/json2.js new file mode 100644 index 000000000..f6fada687 --- /dev/null +++ b/branch.es5_web/json2.js @@ -0,0 +1,530 @@ +// json2.js +// 2017-06-12 +// Public Domain. +// NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + +// USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO +// NOT CONTROL. + +// This file creates a global JSON object containing two methods: stringify +// and parse. This file provides the ES5 JSON capability to ES3 systems. +// If a project might run on IE8 or earlier, then this file should be included. +// This file does nothing on ES5 systems. + +// JSON.stringify(value, replacer, space) +// value any JavaScript value, usually an object or array. +// replacer an optional parameter that determines how object +// values are stringified for objects. It can be a +// function or an array of strings. +// space an optional parameter that specifies the indentation +// of nested structures. If it is omitted, the text will +// be packed without extra whitespace. If it is a number, +// it will specify the number of spaces to indent at each +// level. If it is a string (such as "\t" or " "), +// it contains the characters used to indent at each level. +// This method produces a JSON text from a JavaScript value. +// When an object value is found, if the object contains a toJSON +// method, its toJSON method will be called and the result will be +// stringified. A toJSON method does not serialize: it returns the +// value represented by the name/value pair that should be serialized, +// or undefined if nothing should be serialized. The toJSON method +// will be passed the key associated with the value, and this will be +// bound to the value. + +// For example, this would serialize Dates as ISO strings. + +// Date.prototype.toJSON = function (key) { +// function f(n) { +// // Format integers to have at least two digits. +// return (n < 10) +// ? "0" + n +// : n; +// } +// return this.getUTCFullYear() + "-" + +// f(this.getUTCMonth() + 1) + "-" + +// f(this.getUTCDate()) + "T" + +// f(this.getUTCHours()) + ":" + +// f(this.getUTCMinutes()) + ":" + +// f(this.getUTCSeconds()) + "Z"; +// }; + +// You can provide an optional replacer method. It will be passed the +// key and value of each member, with this bound to the containing +// object. The value that is returned from your method will be +// serialized. If your method returns undefined, then the member will +// be excluded from the serialization. + +// If the replacer parameter is an array of strings, then it will be +// used to select the members to be serialized. It filters the results +// such that only members with keys listed in the replacer array are +// stringified. + +// Values that do not have JSON representations, such as undefined or +// functions, will not be serialized. Such values in objects will be +// dropped; in arrays they will be replaced with null. You can use +// a replacer function to replace those with JSON values. + +// JSON.stringify(undefined) returns undefined. + +// The optional space parameter produces a stringification of the +// value that is filled with line breaks and indentation to make it +// easier to read. + +// If the space parameter is a non-empty string, then that string will +// be used for indentation. If the space parameter is a number, then +// the indentation will be that many spaces. + +// Example: + +// text = JSON.stringify(["e", {pluribus: "unum"}]); +// // text is '["e",{"pluribus":"unum"}]' + +// text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t"); +// // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' + +// text = JSON.stringify([new Date()], function (key, value) { +// return this[key] instanceof Date +// ? "Date(" + this[key] + ")" +// : value; +// }); +// // text is '["Date(---current time---)"]' + +// JSON.parse(text, reviver) +// This method parses a JSON text to produce an object or array. +// It can throw a SyntaxError exception. + +// The optional reviver parameter is a function that can filter and +// transform the results. It receives each of the keys and values, +// and its return value is used instead of the original value. +// If it returns what it received, then the structure is not modified. +// If it returns undefined then the member is deleted. + +// Example: + +// // Parse the text. Values that look like ISO date strings will +// // be converted to Date objects. + +// myData = JSON.parse(text, function (key, value) { +// var a; +// if (typeof value === "string") { +// a = +// /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); +// if (a) { +// return new Date(Date.UTC( +// +a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6] +// )); +// } +// return value; +// } +// }); + +// myData = JSON.parse( +// "[\"Date(09/09/2001)\"]", +// function (key, value) { +// var d; +// if ( +// typeof value === "string" +// && value.slice(0, 5) === "Date(" +// && value.slice(-1) === ")" +// ) { +// d = new Date(value.slice(5, -1)); +// if (d) { +// return d; +// } +// } +// return value; +// } +// ); + +// This is a reference implementation. You are free to copy, modify, or +// redistribute. + +/*jslint + eval, for, this +*/ + +/*property + JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, + getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, + lastIndex, length, parse, prototype, push, replace, slice, stringify, + test, toJSON, toString, valueOf +*/ + + +// Create a JSON object only if one does not already exist. We create the +// methods in a closure to avoid creating global variables. + +if (typeof JSON !== "object") { + JSON = {}; +} + +(function () { + "use strict"; + + var rx_one = /^[\],:{}\s]*$/; + var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g; + var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g; + var rx_four = /(?:^|:|,)(?:\s*\[)+/g; + var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; + var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; + + function f(n) { + // Format integers to have at least two digits. + return (n < 10) + ? "0" + n + : n; + } + + function this_value() { + return this.valueOf(); + } + + if (typeof Date.prototype.toJSON !== "function") { + + Date.prototype.toJSON = function () { + + return isFinite(this.valueOf()) + ? ( + this.getUTCFullYear() + + "-" + + f(this.getUTCMonth() + 1) + + "-" + + f(this.getUTCDate()) + + "T" + + f(this.getUTCHours()) + + ":" + + f(this.getUTCMinutes()) + + ":" + + f(this.getUTCSeconds()) + + "Z" + ) + : null; + }; + + Boolean.prototype.toJSON = this_value; + Number.prototype.toJSON = this_value; + String.prototype.toJSON = this_value; + } + + var gap; + var indent; + var meta; + var rep; + + + function quote(string) { + +// If the string contains no control characters, no quote characters, and no +// backslash characters, then we can safely slap some quotes around it. +// Otherwise we must also replace the offending characters with safe escape +// sequences. + + rx_escapable.lastIndex = 0; + return rx_escapable.test(string) + ? "\"" + string.replace(rx_escapable, function (a) { + var c = meta[a]; + return typeof c === "string" + ? c + : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4); + }) + "\"" + : "\"" + string + "\""; + } + + + function str(key, holder) { + +// Produce a string from holder[key]. + + var i; // The loop counter. + var k; // The member key. + var v; // The member value. + var length; + var mind = gap; + var partial; + var value = holder[key]; + +// If the value has a toJSON method, call it to obtain a replacement value. + + if ( + value + && typeof value === "object" + && typeof value.toJSON === "function" + ) { + value = value.toJSON(key); + } + +// If we were called with a replacer function, then call the replacer to +// obtain a replacement value. + + if (typeof rep === "function") { + value = rep.call(holder, key, value); + } + +// What happens next depends on the value's type. + + switch (typeof value) { + case "string": + return quote(value); + + case "number": + +// JSON numbers must be finite. Encode non-finite numbers as null. + + return (isFinite(value)) + ? String(value) + : "null"; + + case "boolean": + case "null": + +// If the value is a boolean or null, convert it to a string. Note: +// typeof null does not produce "null". The case is included here in +// the remote chance that this gets fixed someday. + + return String(value); + +// If the type is "object", we might be dealing with an object or an array or +// null. + + case "object": + +// Due to a specification blunder in ECMAScript, typeof null is "object", +// so watch out for that case. + + if (!value) { + return "null"; + } + +// Make an array to hold the partial results of stringifying this object value. + + gap += indent; + partial = []; + +// Is the value an array? + + if (Object.prototype.toString.apply(value) === "[object Array]") { + +// The value is an array. Stringify every element. Use null as a placeholder +// for non-JSON values. + + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value) || "null"; + } + +// Join all of the elements together, separated with commas, and wrap them in +// brackets. + + v = partial.length === 0 + ? "[]" + : gap + ? ( + "[\n" + + gap + + partial.join(",\n" + gap) + + "\n" + + mind + + "]" + ) + : "[" + partial.join(",") + "]"; + gap = mind; + return v; + } + +// If the replacer is an array, use it to select the members to be stringified. + + if (rep && typeof rep === "object") { + length = rep.length; + for (i = 0; i < length; i += 1) { + if (typeof rep[i] === "string") { + k = rep[i]; + v = str(k, value); + if (v) { + partial.push(quote(k) + ( + (gap) + ? ": " + : ":" + ) + v); + } + } + } + } else { + +// Otherwise, iterate through all of the keys in the object. + + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + ( + (gap) + ? ": " + : ":" + ) + v); + } + } + } + } + +// Join all of the member texts together, separated with commas, +// and wrap them in braces. + + v = partial.length === 0 + ? "{}" + : gap + ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" + : "{" + partial.join(",") + "}"; + gap = mind; + return v; + } + } + +// If the JSON object does not yet have a stringify method, give it one. + + if (typeof JSON.stringify !== "function") { + meta = { // table of character substitutions + "\b": "\\b", + "\t": "\\t", + "\n": "\\n", + "\f": "\\f", + "\r": "\\r", + "\"": "\\\"", + "\\": "\\\\" + }; + JSON.stringify = function (value, replacer, space) { + +// The stringify method takes a value and an optional replacer, and an optional +// space parameter, and returns a JSON text. The replacer can be a function +// that can replace values, or an array of strings that will select the keys. +// A default replacer method can be provided. Use of the space parameter can +// produce text that is more easily readable. + + var i; + gap = ""; + indent = ""; + +// If the space parameter is a number, make an indent string containing that +// many spaces. + + if (typeof space === "number") { + for (i = 0; i < space; i += 1) { + indent += " "; + } + +// If the space parameter is a string, it will be used as the indent string. + + } else if (typeof space === "string") { + indent = space; + } + +// If there is a replacer, it must be a function or an array. +// Otherwise, throw an error. + + rep = replacer; + if (replacer && typeof replacer !== "function" && ( + typeof replacer !== "object" + || typeof replacer.length !== "number" + )) { + throw new Error("JSON.stringify"); + } + +// Make a fake root object containing our value under the key of "". +// Return the result of stringifying the value. + + return str("", {"": value}); + }; + } + + +// If the JSON object does not yet have a parse method, give it one. + + if (typeof JSON.parse !== "function") { + JSON.parse = function (text, reviver) { + +// The parse method takes a text and an optional reviver function, and returns +// a JavaScript value if the text is a valid JSON text. + + var j; + + function walk(holder, key) { + +// The walk method is used to recursively walk the resulting structure so +// that modifications can be made. + + var k; + var v; + var value = holder[key]; + if (value && typeof value === "object") { + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } + } + } + } + return reviver.call(holder, key, value); + } + + +// Parsing happens in four stages. In the first stage, we replace certain +// Unicode characters with escape sequences. JavaScript handles many characters +// incorrectly, either silently deleting them, or treating them as line endings. + + text = String(text); + rx_dangerous.lastIndex = 0; + if (rx_dangerous.test(text)) { + text = text.replace(rx_dangerous, function (a) { + return ( + "\\u" + + ("0000" + a.charCodeAt(0).toString(16)).slice(-4) + ); + }); + } + +// In the second stage, we run the text against regular expressions that look +// for non-JSON patterns. We are especially concerned with "()" and "new" +// because they can cause invocation, and "=" because it can cause mutation. +// But just to be safe, we want to reject all unexpected forms. + +// We split the second stage into 4 regexp operations in order to work around +// crippling inefficiencies in IE's and Safari's regexp engines. First we +// replace the JSON backslash pairs with "@" (a non-JSON character). Second, we +// replace all simple value tokens with "]" characters. Third, we delete all +// open brackets that follow a colon or comma or that begin the text. Finally, +// we look to see that the remaining characters are only whitespace or "]" or +// "," or ":" or "{" or "}". If that is so, then the text is safe for eval. + + if ( + rx_one.test( + text + .replace(rx_two, "@") + .replace(rx_three, "]") + .replace(rx_four, "") + ) + ) { + +// In the third stage we use the eval function to compile the text into a +// JavaScript structure. The "{" operator is subject to a syntactic ambiguity +// in JavaScript: it can begin a block or an object literal. We wrap the text +// in parens to eliminate the ambiguity. + + j = eval("(" + text + ")"); + +// In the optional fourth stage, we recursively walk the new structure, passing +// each name/value pair to a reviver function for possible transformation. + + return (typeof reviver === "function") + ? walk({"": j}, "") + : j; + } + +// If the text is not JSON parseable, then a SyntaxError is thrown. + + throw new SyntaxError("JSON.parse"); + }; + } +}()); diff --git a/branch.es5_web/lint.html b/branch.es5_web/lint.html new file mode 100644 index 000000000..c7aa4c33b --- /dev/null +++ b/branch.es5_web/lint.html @@ -0,0 +1,726 @@ + + +JSLint: The JavaScript Code Quality Tool + + + + +
    +
     
    + +
    + The JavaScript Code Quality Tool +

    + Warning: JSLint will hurt your feelings.
    +
    +

    What is JSLint?

    + +

    JSLint + is a JavaScript program that looks for problems in JavaScript programs. + It is a code quality tool.

    + +

    When C + was a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    + +

    As the language matured, the definition of the language was +strengthened to eliminate some insecurities, and compilers got better +at issuing warnings. lint is no longer needed.

    + +

    JavaScript is a young-for-its-age + language. It was originally intended to do small tasks in webpages, tasks + for which Java was too heavy and clumsy. But JavaScript is a surprisingly capable + language, and it is now being used in larger projects. Many of the features + that were intended to make the language easy to use are troublesome when projects become complicated. A lint for JavaScript is needed: JSLint, + a JavaScript syntax checker and validator.

    + +

    JSLint takes a JavaScript source and scans it. If it finds + a problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    + +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by Third + Edition of the ECMAScript Programming Language Standard. The + subset is related to recommendations found in Code + Conventions for the JavaScript Programming Language.

    +

    JavaScript is a sloppy language, but inside it there is an elegant, better + language. JSLint helps you to program in that better language + and to avoid most of the slop. JSLint will reject programs that browsers will accept because JSLint is concerned with the quality of your code and browsers are not. You should accept all of JSLint's advice.

    +

    JSLint can operate on JavaScript source or JSON + text.

    +

    Global Variables

    +

    JavaScript's biggest + problem is its dependence on global variables, particularly implied + global variables. If a variable is not explicitly declared (usually with + the var statement), then JavaScript assumes that the variable + was global. This can mask misspelled names and other problems.

    +

    JSLint expects that all variables and functions are declared + before they are used or invoked. This allows it to detect implied global + variables. It is also good practice because it makes programs easier to + read.

    +

    Sometimes a file is dependent on global variables and functions that + are defined elsewhere. You can identify these to JSLint with a var statement that lists the global functions and objects + that your program depends on.

    +

    A global declaration can look like this:

    +
    var getElementByAttribute, breakCycles, hanoi;
    +

    The declaration should appear near the top of the file. It must appear before the use of the variables + it declares.

    +

    It is necessary to use a var statement to declare a variable before that variable is assigned to.

    +

    JSLint also recognizes a /*global*/ directive that can indicate to JSLint that variables used in this file were defined in other files. The + directive can contain a comma separated list of names. Each name can optionally be followed by a colon and either true or false, true indicating that the variable may be assigned to by this file, and false indicating that assignment is not allowed (which is the default). The directive respects function scope.

    +

    Some globals can be predefined for you. Select the Assume + a browser (browser) option to + predefine the standard global properties that are supplied by web browsers, + such as document and addEventListener. It has the same + effect as this directive:

    +
    /*global +clearInterval: false, clearTimeout: false, document: false, event: false, frames: false, history: false, Image: false, location: false, name: false, navigator: false, Option: false, parent: false, screen: false, setInterval: false, setTimeout: false, window: false, XMLHttpRequest: false +*/
    +

    Select the + Assume console, alert, ... +(devel) option to predefine globals that are useful in development but that should be avoided in production, such as console and alert. It has the same +effect as this directive:

    +
    /*global alert: false, confirm: false, console: false, Debug: false, opera: false, prompt: false, WSH: false */
    +

    Select the + Assume Node.js + (node) option to predefine globals that are used in the Node.js environment.

    +
    /*global Buffer: false, clearInterval: false, clearTimeout: false, console: false, exports: false, global: false, module: false, process: false, querystring: false, require: false, setInterval: false, setTimeout: false, __filename: false, __dirname: false */ +
    +

    Select the Assume Couch (couch) + option to predefine the global properties provided by Couch DB. It has the same effect as this directive:

    +
    + /*global emit: false, getRow: false, isArray: false, log: false, provides: false, registerType: false, require: false, send: false, start: false, sum: false, toJSON: false */ +
    +

    Select the Assume Rhino (rhino) option + to predefine the global properties provided by the Rhino environment. + It has the same effect as this directive:

    +
    + /*global defineClass: false, deserialize: false, gc: false, help: false, load: false, loadClass: false, print: false, quit: false, readFile: false, readUrl: false, runCommand: false, seal: false, serialize: false, spawn: false, sync: false, toint32: false, version: false */ +
    +

    Semicolon

    +

    JavaScript uses a C-like syntax which requires the use of semicolons to delimit certain + statements. JavaScript attempts to make those semicolons optional with a semicolon + insertion mechanism. This is dangerous because it can mask errors.

    +

    Like C, JavaScript has ++ and -- and ( operators + which can be prefixes or suffixes. The disambiguation is done by the semicolon.

    +

    In JavaScript, a linefeed can be whitespace or it can act as a semicolon. + This replaces one ambiguity with another.

    +

    JSLint expects that every statement be followed by ; except + for for, function, if, switch, try, and + while. JSLint does not expect to see unnecessary semicolons or the + empty statement.

    +

    Comma

    +

    The comma operator can lead to excessively tricky expressions. It can also + mask some programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as an + operator (except in the initialization and incrementation parts of the for + statement). It does not expect to see elided elements in array literals. Extra + commas should not be used. A comma should not appear after the last element + of an array literal or object literal because it can be misinterpreted by some + browsers.

    +

    Scope

    + +

    In many languages, a block introduces a scope. Variables introduced in + a block are not visible outside of the block.

    + +

    In JavaScript, blocks do not introduce a scope. There is only function-scope. + A variable introduced anywhere in a function is visible everywhere in + the function. JavaScript's blocks confuse experienced programmers and + lead to errors because the familiar syntax makes a false promise.

    + +

    JSLint expects blocks with function, if, + switch, while, for, do, + and try statements and nowhere else.

    +

    In languages with block scope, it is usually recommended that variables + be declared at the site of first use. But because JavaScript does not + have block scope, it is wiser to declare all of a function's variables + at the top of the function. It is recommended that a single var + statement be used per function. This can be declined with the vars + option.

    + +

    Required Blocks

    + +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    + +

    JavaScript allows an if to be written like this:

    + +
    if (condition) + statement;
    + +

    That form is known to contribute to mistakes in projects where many programmers + are working on the same code. That is why JSLint expects the use of + a block:

    + +
    if (condition) { + statements; +}
    + +

    Experience shows that this form is more resilient.

    + +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call or delete. All other expression statements are considered + to be errors.

    +

    for in

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, + it also loops through all of the properties that were inherited through + the prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    + for (name in object) { + if (object.hasOwnProperty(name)) { + .... + } + + }
    +

    In most cases, the for statement should be avoided completely. You should rely on methods like Object.keys and Array.prototype.forEach instead.

    + + +

    switch

    +

    A common + error in switch statements is to forget to place a break + statement after each case, resulting in unintended fall-through. JSLint + expects that the statement before the next case or default + is one of these: break, return, or throw. +

    +

    var

    + +

    JavaScript allows var definitions to occur anywhere + within a function. JSLint is more strict.

    + +

    JSLint expects that a var will be declared + only once, and that it will be declared before it is used.

    +

    JSLint expects that a function + will be declared before it is used.

    +

    JSLint expects that parameters will not also be declared + as vars.

    + +

    JSLint does not expect the arguments array to be declared + as a var.

    +

    JSLint does not expect that a var will be defined in a block. + This is because JavaScript blocks do not have block scope. This can have + unexpected consequences. Define all variables at the top of the function. It is easier to comply with this convention is if you use one var statement per function.

    + +

    with

    + +

    The with statement was intended to provide a shorthand in accessing + properties in deeply nested objects. Unfortunately, it behaves very + badly when setting new properties. Never use the with statement. Use + a var instead.

    + +

    JSLint does not expect to see a with statement.

    + +

    =

    +

    JSLint does not expect to see an assignment statement in + the condition part of an if or for or while + or do statement. This is because it is more + likely that

    +
    if (a = b) { + ... +}
    +

    was intended to be

    +
    if (a == b) { + ... +}
    +

    It is difficult to write correct programs while using idioms that are + hard to distinguish from obvious errors.

    +

    == and !=

    +

    The == and != operators do type coercion before + comparing. This is bad because it causes ' \t\r\n' == 0 to + be true. This can mask type errors. JSLint cannot reliably determine if == is being used correctly, so it is best to not use == and != at all and to always use the more reliable === and !== operators instead.

    +

    If you only care that a value is truthy or falsy, + then use the short form. Instead of

    +
    (foo != 0)
    +

    just say

    +
    (foo)
    +

    and instead of

    +
    (foo == 0)
    +

    say

    +
    (!foo)
    +

    There is an eqeq option that allows the use of == and !=.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    + +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that labels + will be distinct from vars and parameters.

    + +

    Unreachable Code

    +

    JSLint expects that + a return, break, continue, + or throw statement will be followed by + a } or case or default.

    + +

    Confusing Pluses and Minuses

    + +

    JSLint expects that + will not be followed by ++ or ++, and that - will not be followed +by - or --. A misplaced space can turn + + into ++, an error that is difficult to see. Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ (increment) and -- (decrement) + operators have been known to contribute to bad code by encouraging excessive + trickiness. They are second only to faulty architecture in enabling to + viruses and other security menaces. Also, preincrement/postincrement confusion can produce off-by-one errors that are extremely difficult to diagnose. There is a plusplus option + that allows the use of these operators.

    +

    Bitwise Operators

    +

    JavaScript does not have an integer type, but it does have bitwise operators. + The bitwise operators convert their operands from floating point to integers + and back, so they are not as efficient as in C or other languages. They + are rarely useful in browser applications. The similarity to the logical + operators can mask some programming errors. The bitwise option + allows the use of these operators: << >> >>> + ~ & |.

    +

    eval is evil

    +

    The eval function (and its relatives, Function, + setTimeout, and setInterval) provide access + to the JavaScript compiler. This is sometimes necessary, but in most cases + it indicates the presence of extremely bad coding. The eval + function is the most misused feature of JavaScript.

    + +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    + +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. JSLint + looks for problems that may cause portability problems. It also attempts + to resolve visual ambiguities by recommending explicit escapement.

    +

    JavaScript's syntax for regular expression literals overloads the / + character. To avoid ambiguity, JSLint expects that the character + preceding a regular expression literal is a ( or = + or : or , character.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the new + prefix. The new prefix creates a new object based on the + function's prototype, and binds that object to the function's + implied this parameter. If you neglect to use the new + prefix, no new object will be made and this will be bound + to the global object. This is a serious + mistake.

    +

    JSLint enforces the convention that constructor functions + be given names with initial uppercase. JSLint does not expect + to see a function invocation with an initial uppercase name unless it + has the new prefix. JSLint does not expect to + see the new prefix used with functions whose names do not + start with initial uppercase. This can be disabled with the newcap + option.

    +

    JSLint does not expect to see the wrapper forms new Number, + new String, new Boolean.

    +

    JSLint does not expect to see new Object. Use {} + instead.

    +

    Properties

    +

    Since JavaScript is a loosely-typed, dynamic-object language, it is not + possible to determine at compile time if property names are spelled correctly. + JSLint provides some assistance with this.

    +

    At the bottom of its report, JSLint displays a /*properties*/ + directive. It contains all of the names and string literals that were used + with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. This is to make misspellings + easier to spot.

    +

    You can copy the /*properties*/ directive into the top of your script file. + JSLint will check the spelling of all property names against + the list. That way, you can have JSLint look for misspellings + for you.

    +

    For example,

    +
    /*properties + charAt, slice +*/
    + +

    Unsafe Characters

    +

    There are characters that are handled inconsistently in browsers, and + so must be escaped when placed in strings.

    +
    \u0000-\u001f +\u007f-\u009f +\u00ad +\u0600-\u0604 +\u070f +\u17b4 +\u17b5 +\u200c-\u200f +\u2028-\u202f +\u2060-\u206f +\ufeff +\ufff0-\uffff
    +

    Not Looked For

    + +

    JSLint does not do flow analysis to determine that variables are assigned + values before used. This is because variables are given a value (undefined) + that is a reasonable default for many applications.

    + +

    JSLint does not do any kind of global analysis. It does + not attempt to determine that functions used with new are + really constructors (except by enforcing capitalization + conventions), or that property names are spelled correctly (except + for matching against the /*properties*/ directive).

    + +

    Options

    +

    JSLint provides several options that control its operation and + its sensitivity. In the web edition, the +options are selected with several checkboxes and two fields.

    +

    It also provides assistance in constructing /*jslint*/ and /*properties*/ directives.

    +

    When JSLINT is called as a function, it accepts an option object + parameter that allows you to determine the subset of JavaScript that is + acceptable to you. The web page version of JSLint at http://www.JSLint.com/ + does this for you.

    +

    Options can also be specified within a script with a /*jslint*/ + directive:

    +
    /*jslint nomen: true, debug: true, + evil: false, vars: true */
    +

    An option directive starts with /*jslint. Notice that + there is no space before the j. The specification contains + a sequence of name value pairs, where the names are JSLint + options, and the values are true or false. The + indent option can take a number. A /*jslint*/ + directive takes precedence over the option object. The directive respects function scope.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Tolerate assignment expressions asstrue if assignment should be allowed outside of statement position.
    Tolerate bitwise operators bitwisetrue if bitwise operators should be allowed. (more)
    Assume a browser browsertrue if the standard browser globals should be predefined. + (more)
    Tolerate Google Closure idiomsclosuretrue if Google Closure annotations should be allowed.
    Tolerate continuecontinuetrue if the continue statement should be allowed.
    Assume CouchDBcouchtrue if Couch DB globals should be predefined.
    Tolerate debugger statementsdebugtrue if debugger statements should be + allowed. Set this option to false before going into production.
    Assume console, alert, ...develtrue if browser globals that are useful in development should be + predefined. (more)
    Tolerate == and !=eqeqtrue if the == and != operators should be tolerated. (more)
    Tolerate eval eviltrue if eval should be allowed. (more)
    Tolerate unfiltered for in forintrue if unfiltered for in + statements should be allowed. (more)
    Strict white space indentationindentThe number of spaces used for indentation (default is 4).
    Maximum number of errorsmaxerrThe maximum number of warnings reported. (default is 50)
    Maximum line lengthmaxlenThe maximum number of characters in a line.
    Tolerate uncapitalized constructorsnewcaptrue if Initial Caps with constructor + functions is optional. (more)
    Assume Node.jsnodetrue if Node.js globals should be predefined. (more)
    Tolerate dangling _ in identifiers nomentrue if names should not be checked for initial or trailing underbars.
    Stop on first error passfailtrue if the scan should stop on first error.
    Tolerate ++ and -- plusplustrue if ++ and -- should + be allowed. (more)
    PredefinedpredefAn array of strings, the names of predefined global variables, or an object whose keys are global variable names, and whose values are booleans that determine if each variable is assignable (also see global). predef is used with the option object, but not + with the /*jslint*/ directive. You can also use the var + statement to declare global variables in a script file.
    Tolerate . and [^...]. in /RegExp/ regexptrue if . and [^...] should be allowed in RegExp + literals. They match more material than might be expected, allowing attackers to confuse applications. These forms should not be used when validating in secure applications.
    Assume Rhinorhinotrue if the Rhino + environment globals should be predefined. (more)
    Tolerate missing 'use strict' pragma sloppytrue if the 'use strict'; pragma + is not required.
    Tolerate stupidity
    +
    stupidtrue if blocking (-Sync) methods can be used.
    Tolerate inefficient subscripting
    +
    subtrue if subscript notation may be used for expressions + better expressed in dot notation.
    Tolerate TODO comments
    +
    todotrue if comments starting with TODO should be allowed.
    Tolerate unused parametersunparamtrue if warnings should not be given for unused parameters.
    Tolerate many var statements per functionvarstrue if multiple var statement per function + should be allowed. (more)
    Tolerate messy white spacewhitetrue if strict whitespace rules should be ignored.
    +

    Report

    + +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    + +
      +
    • The line number on which it starts.
    • +
    • Its name. In the case of anonymous functions, JSLint + will 'guess' the name.
    • +
    • The parameters.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Variables: The variables that are declared in the function + that are used only by the function.
    • +
    • Exceptions: The variables that are declared by try statements.
    • +
    • Outer: Variables used by this function that are declared in + another function.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used. There is a list of JSLint + messages.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements. Updates are announced at + https://plus.google.com/communities/104441363299760713736.

    + +

    Try it

    + +

    Try it. Paste your script + into the window and click the + + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    + JSLint is written entirely in JavaScript, so it can run anywhere that JavaScript (or Java) can run. +

    +

    Implementation

    +

    JSLint uses a Pratt + Parser (Top Down Operator Precedence). It is written in JavaScript. + The full source code is available: https://github.com/douglascrockford/JSLint.

    +
    + + + + + diff --git a/branch.es5_web/title.png b/branch.es5_web/title.png new file mode 100644 index 000000000..84f31f79a Binary files /dev/null and b/branch.es5_web/title.png differ diff --git a/branch.es5_web/web_jslint.js b/branch.es5_web/web_jslint.js new file mode 100644 index 000000000..7433cf602 --- /dev/null +++ b/branch.es5_web/web_jslint.js @@ -0,0 +1,6980 @@ +// adsafe.js +// 2017-06-12 + +// Public Domain. + +// NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. +// SUBJECT TO CHANGE WITHOUT NOTICE. + +// Original url: http://www.ADsafe.org/adsafe.js + +// This file implements the core ADSAFE runtime. A site may add additional +// methods understanding that those methods will be made available to guest +// code. + +// This code should be minified before deployment. +// See http://javascript.crockford.com/jsmin.html + +// USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO +// NOT CONTROL. + +/*global window*/ + +/*jslint browser, devel, for, this +*/ + +/*property + _, ___nodes___, ___star___, _intercept, a, abbr, acronym, addEventListener, + address, altKey, append, appendChild, apply, area, arguments, autocomplete, + b, bdo, big, blockquote, blur, br, bubble, button, call, callee, caller, + cancelBubble, canvas, caption, center, change, charAt, charCode, check, + checked, childNodes, cite, class, className, clientX, clientY, clone, + cloneNode, code, col, colgroup, combine, concat, console, constructor, + count, create, createDocumentFragment, createElement, createRange, + createTextNode, createTextRange, cssFloat, ctrlKey, currentStyle, dd, + defaultView, del, dfn, dir, disabled, div, dl, dt, each, em, empty, enable, + ephemeral, eval, exec, expand, explode, fieldset, fire, firstChild, focus, + font, form, fragment, fromCharCode, get, getCheck, getChecks, getClass, + getClasses, getComputedStyle, getElementById, getElementsByTagName, + getMark, getMarks, getName, getNames, getOffsetHeight, getOffsetHeights, + getOffsetWidth, getOffsetWidths, getParent, getSelection, getStyle, + getStyles, getTagName, getTagNames, getTitle, getTitles, getValue, + getValues, go, h1, h2, h3, h4, h5, h6, has, hasOwnProperty, hr, i, id, img, + inRange, indeterminate, indexOf, input, ins, insertBefore, isArray, kbd, + key, keyCode, keys, klass, label, later, legend, length, li, lib, log, map, + mark, menu, message, name, nextSibling, nodeName, nodeValue, object, off, + offsetHeight, offsetWidth, ol, on, onclick, ondblclick, onfocusin, + onfocusout, onkeypress, onmousedown, onmousemove, onmouseout, onmouseover, + onmouseup, op, optgroup, option, p, parent, parentNode, postError, pre, + prepend, preventDefault, protect, prototype, push, q, remove, removeChild, + removeElement, replace, replaceChild, returnValue, row, samp, select, + selection, selectionEnd, selectionStart, set, shiftKey, slice, small, span, + srcElement, stack, stopPropagation, strong, style, styleFloat, sub, sup, + table, tag, tagName, target, tbody, td, test, text, textarea, tfoot, th, + that, thead, title, toLowerCase, toString, toUpperCase, tr, tt, type, u, + ul, unwatch, value, valueOf, var, visibility, watch, window, writeln, x, y +*/ + +var ADSAFE; +ADSAFE = (function () { + "use strict"; + + var adsafe_id; // The id of the current widget + var adsafe_lib; // The script libraries loaded by the current widget + +// These member names are banned from guest scripts. The ADSAFE.get and +// ADSAFE.put methods will not allow access to these properties. + + var banned = { + arguments: true, + callee: true, + caller: true, + constructor: true, + eval: true, + prototype: true, + stack: true, + unwatch: true, + valueOf: true, + watch: true + }; + + var cache_style_object; + var cache_style_node; + var defaultView = document.defaultView; + var ephemeral; + var flipflop; // Used in :even/:odd processing + var has_focus; + var hunter; // Set of hunter patterns + var interceptors = []; + + var makeableTagName = { + +// This is the whitelist of elements that may be created with the .tag(tagName) +// method. + + a: true, + abbr: true, + acronym: true, + address: true, + area: true, + b: true, + bdo: true, + big: true, + blockquote: true, + br: true, + button: true, + canvas: true, + caption: true, + center: true, + cite: true, + code: true, + col: true, + colgroup: true, + dd: true, + del: true, + dfn: true, + dir: true, + div: true, + dl: true, + dt: true, + em: true, + fieldset: true, + font: true, + form: true, + h1: true, + h2: true, + h3: true, + h4: true, + h5: true, + h6: true, + hr: true, + i: true, + img: true, + input: true, + ins: true, + kbd: true, + label: true, + legend: true, + li: true, + map: true, + menu: true, + object: true, + ol: true, + optgroup: true, + option: true, + p: true, + pre: true, + q: true, + samp: true, + select: true, + small: true, + span: true, + strong: true, + sub: true, + sup: true, + table: true, + tbody: true, + td: true, + textarea: true, + tfoot: true, + th: true, + thead: true, + tr: true, + tt: true, + u: true, + ul: true, + var: true + }; + var name; + var pecker; // set of pecker patterns + var result; + var star; + var the_range; + var value; + + +// The error function is called if there is a violation or confusion. +// It throws an exception. + + function error(message) { + ADSAFE.log("ADsafe error: " + (message || "ADsafe violation.")); + throw { + name: "ADsafe", + message: message || "ADsafe violation." + }; + } + + +// Some of JavaScript's implicit string conversions can grant extraordinary +// powers to untrusted code. So we use the string_check function to prevent +// such abuses. + + function string_check(string) { + if (typeof string !== "string") { + error("ADsafe string violation."); + } + return string; + } + + +// The object.hasOwnProperty method has a number of hazards. So we wrap it in +// the owns function. + + function owns(object, string) { + return ( + object + && typeof object === "object" + && Object.prototype.hasOwnProperty.call(object, string_check(string)) + ); + } + +// The reject functions enforce the restriction on property names. +// reject_property allows access only to objects and arrays. It does not allow +// use of the banned names, or names that are not strings and not numbers, +// or strings that start or end with _. + + function reject_name(name) { + return ( + typeof name !== "number" + && ( + typeof name !== "string" + || banned[name] + || name.charAt(0) === "_" + || name.slice(-1) === "_" + ) + ); + } + + + function reject_property(object, name) { + return typeof object !== "object" || reject_name(name); + } + + + function reject_global(that) { + if (that.window) { + error(); + } + } + + + function getStyleObject(node) { + +// The getStyleObject function returns the computed style object for a node. + + if (node === cache_style_node) { + return cache_style_object; + } + cache_style_node = node; + cache_style_object = ( + node.currentStyle + || defaultView.getComputedStyle(node, "") + ); + return cache_style_object; + } + + + function walkTheDOM(node, func, skip) { + +// Recursively traverse the DOM tree, starting with the node, in document +// source order, calling the func on each node visisted. + + if (!skip) { + func(node); + } + node = node.firstChild; + while (node) { + walkTheDOM(node, func); + node = node.nextSibling; + } + } + + + function purge_event_handlers(node) { + +// We attach all event handlers to an "___ on ___" property. The property name +// contains spaces to insure that there is no collision with HTML attribues. +// Keeping the handlers in a single property makes it easy to remove them +// all at once. Removal is required to avoid memory leakage on IE6 and IE7. + + walkTheDOM(node, function (node) { + if (node.tagName) { + node["___ on ___"] = null; + node.change = null; + } + }); + } + + + function parse_query(text, id) { + +// Convert a query string into an array of op/name/value selectors. +// A query string is a sequence of triples wrapped in brackets; or names, +// possibly prefixed by # . & > _, or :option, or * or /. A triple is a name, +// and operator (one of [=, [!=, [*=, [~=, [|=, [$=, or [^=) and a value. + +// If the id parameter is supplied, then the name following # must have the +// id as a prefix and must match the ADsafe rule for id: being all uppercase +// letters and digits with one underbar. + +// A name must be all lower case and may contain digits, -, or _. + + var match; // A match array + var query = []; // The resulting query array + var selector; + var qx = (id) + ? /^\s*(?:([*\/])|\[\s*([a-z][0-9a-z_\-]*)\s*(?:([!*~|$\^]?=)\s*([0-9A-Za-z_\-*%&;.\/:!]+)\s*)?\]|#\s*([A-Z]+_[A-Z0-9]+)|:\s*([a-z]+)|([.&_>+]?)\s*([a-z][0-9a-z\-]*))\s*/ + : /^\s*(?:([*\/])|\[\s*([a-z][0-9a-z_\-]*)\s*(?:([!*~|$\^]?=)\s*([0-9A-Za-z_\-*%&;.\/:!]+)\s*)?\]|#\s*([\-A-Za-z0-9_]+)|:\s*([a-z]+)|([.&_>+]?)\s*([a-z][0-9a-z\-]*))\s*/; + +// Loop over all of the selectors in the text. + + do { + +// The qx teases the components of one selector out of the text, ignoring +// whitespace. + +// match[0] the whole selector +// match[1] * / +// match[2] attribute name +// match[3] = != *= ~= |= $= ^= +// match[4] attribute value +// match[5] # id +// match[6] : option +// match[7] . & _ > + +// match[8] name + + match = qx.exec(string_check(text)); + if (!match) { + error("ADsafe: Bad query:" + text); + } + +// Make a selector object and stuff it in the query. + + if (match[1]) { + +// The selector is * or / + + selector = { + op: match[1] + }; + } else if (match[2]) { + +// The selector is in brackets. + + selector = (match[3]) + ? { + op: "[" + match[3], + name: match[2], + value: match[4] + } + : { + op: "[", + name: match[2] + }; + } else if (match[5]) { + +// The selector is an id. + + if ( + query.length > 0 + || match[5].length <= id.length + || match[5].slice(0, id.length) !== id + ) { + error("ADsafe: Bad query: " + text); + } + selector = { + op: "#", + name: match[5] + }; + +// The selector is a colon. + + } else if (match[6]) { + selector = { + op: ":" + match[6] + }; + +// The selector is one of > + . & _ or a naked tag name + + } else { + selector = { + op: match[7], + name: match[8] + }; + } + +// Add the selector to the query. + + query.push(selector); + +// Remove the selector from the text. If there is more text, have another go. + + text = text.slice(match[0].length); + } while (text); + return query; + } + + + hunter = { + +// These functions implement the hunter behaviors. + + "": function (node) { + var array; + var nodelist = node.getElementsByTagName(name); + var i; + var length; + +// getElementsByTagName produces a nodeList, which is one of the world's most +// inefficient data structures. It is so slow that JavaScript's pseudo arrays +// look terrifically swift by comparison. So we do the conversion. This is +// easily done on some browsers, less easily on others. + + try { + array = Array.prototype.slice.call(nodelist, 0); + result = (result.length) + ? result.concat(array) + : array; + } catch (ignore) { + length = nodelist.length; + for (i = 0; i < length; i += 1) { + result.push(nodelist[i]); + } + } + }, + "+": function (node) { + node = node.nextSibling; + name = name.toUpperCase(); + while (node && !node.tagName) { + node = node.nextSibling; + } + if (node && node.tagName === name) { + result.push(node); + } + }, + ">": function (node) { + node = node.firstChild; + name = name.toUpperCase(); + while (node) { + if (node.tagName === name) { + result.push(node); + } + node = node.nextSibling; + } + }, + "#": function () { + var n = document.getElementById(name); + if (n.tagName) { + result.push(n); + } + }, + "/": function (node) { + var nodes = node.childNodes; + var i; + var length = nodes.length; + for (i = 0; i < length; i += 1) { + result.push(nodes[i]); + } + }, + "*": function (node) { + star = true; + walkTheDOM(node, function (node) { + result.push(node); + }, true); + } + }; + + pecker = { + ".": function (node) { + var classy = " " + node.className + " "; + return classy.indexOf(" " + name + " ") >= 0; + }, + "&": function (node) { + return node.name === name; + }, + "_": function (node) { + return node.type === name; + }, + "[": function (node) { + return typeof node[name] === "string"; + }, + "[=": function (node) { + var member = node[name]; + return typeof member === "string" && member === value; + }, + "[!=": function (node) { + var member = node[name]; + return typeof member === "string" && member !== value; + }, + "[^=": function (node) { + var member = node[name]; + return typeof member === "string" && + member.slice(0, member.length) === value; + }, + "[$=": function (node) { + var member = node[name]; + return typeof member === "string" && + member.slice(-member.length) === value; + }, + "[*=": function (node) { + var member = node[name]; + return typeof member === "string" && + member.indexOf(value) >= 0; + }, + "[~=": function (node) { + var member = node[name]; + if (typeof member === "string") { + member = " " + member + " "; + return member.indexOf(" " + value + " ") >= 0; + } + }, + "[|=": function (node) { + var member = node[name]; + if (typeof member === "string") { + member = "-" + member + "-"; + return member.indexOf("-" + value + "-") >= 0; + } + }, + ":blur": function (node) { + return node !== has_focus; + }, + ":checked": function (node) { + return node.checked; + }, + ":disabled": function (node) { + return node.tagName && node.disabled; + }, + ":enabled": function (node) { + return node.tagName && !node.disabled; + }, + ":even": function (node) { + var f; + if (node.tagName) { + f = flipflop; + flipflop = !flipflop; + return f; + } + return false; + }, + ":focus": function (node) { + return node === has_focus; + }, + ":hidden": function (node) { + return node.tagName && getStyleObject(node).visibility !== "visible"; + }, + ":odd": function (node) { + if (node.tagName) { + flipflop = !flipflop; + return flipflop; + } + return false; + }, + ":tag": function (node) { + return node.tagName; + }, + ":text": function (node) { + return node.nodeName === "#text"; + }, + ":trim": function (node) { + return node.nodeName !== "#text" || (/\W/.test(node.nodeValue)); + }, + ":unchecked": function (node) { + return node.tagName && !node.checked; + }, + ":visible": function (node) { + return node.tagName && getStyleObject(node).visibility === "visible"; + } + }; + + + function quest(query, nodes) { + var selector; + var func; + var i; + var j; + +// Step through each selector. + + for (i = 0; i < query.length; i += 1) { + selector = query[i]; + name = selector.name; + func = hunter[selector.op]; + +// There are two kinds of selectors: hunters and peckers. If this is a hunter, +// loop through the the nodes, passing each node to the hunter function. +// Accumulate all the nodes it finds. + + if (typeof func === "function") { + if (star) { + error( + "ADsafe: Query violation: *" + + selector.op + + (selector.name || "") + ); + } + result = []; + for (j = 0; j < nodes.length; j += 1) { + func(nodes[j]); + } + } else { + +// If this is a pecker, get its function. There is a special case for +// the :first and :rest selectors because they are so simple. + + value = selector.value; + flipflop = false; + func = pecker[selector.op]; + if (typeof func !== "function") { + switch (selector.op) { + case ":first": + result = nodes.slice(0, 1); + break; + case ":rest": + result = nodes.slice(1); + break; + default: + error("ADsafe: Query violation: :" + selector.op); + } + } else { + +// For the other selectors, make an array of nodes that are filtered by +// the pecker function. + + result = []; + for (j = 0; j < nodes.length; j += 1) { + if (func(nodes[j])) { + result.push(nodes[j]); + } + } + } + } + nodes = result; + } + return result; + } + + + function make_root(root, id) { + + if (id) { + if (root.tagName !== "DIV") { + error("ADsafe: Bad node."); + } + } else { + if (root.tagName !== "BODY") { + error("ADsafe: Bad node."); + } + } + +// A Bunch is a container that holds zero or more dom nodes. +// It has many useful methods. + + function Bunch(nodes) { + this.___nodes___ = nodes; + this.___star___ = star && nodes.length > 1; + star = false; + } + + var allow_focus = true; + var dom; + var dom_event = function (ev) { + var key; + var target; + var that; + var the_event; + var the_target; + var the_actual_event = ev || event; + var type = the_actual_event.type; + +// Get the target node and wrap it in a bunch. + + the_target = the_actual_event.target || the_actual_event.srcElement; + target = new Bunch([the_target]); + that = target; + +// Use the PPK hack to make focus bubbly on IE. +// When a widget has focus, it can use the focus method. + + switch (type) { + case "mousedown": + allow_focus = true; + if (document.selection) { + the_range = document.selection.createRange(); + } + break; + case "focus": + case "focusin": + allow_focus = true; + has_focus = the_target; + the_actual_event.cancelBubble = false; + type = "focus"; + break; + case "blur": + case "focusout": + allow_focus = false; + has_focus = null; + type = "blur"; + break; + case "keypress": + allow_focus = true; + has_focus = the_target; + key = String.fromCharCode( + the_actual_event.charCode || the_actual_event.keyCode + ); + switch (key) { + case "\u000d": + case "\u000a": + type = "enterkey"; + break; + case "\u001b": + type = "escapekey"; + break; + } + break; + +// This is a workaround for Safari. + + case "click": + allow_focus = true; + break; + } + if ( + the_actual_event.cancelBubble + && the_actual_event.stopPropagation + ) { + the_actual_event.stopPropagation(); + } + +// Make the event object. + + the_event = { + altKey: the_actual_event.altKey, + ctrlKey: the_actual_event.ctrlKey, + bubble: function () { + +// Bubble up. Get the parent of that node. It becomes the new that. +// getParent throws when bubbling is not possible. + + try { + var parent = that.getParent(); + var b = parent.___nodes___[0]; + that = parent; + the_event.that = that; + +// If that node has an event handler, fire it. Otherwise, bubble up. + + if ( + b["___ on ___"] && b["___ on ___"][type] + ) { + that.fire(the_event); + } else { + the_event.bubble(); + } + } catch (e) { + error(e); + } + }, + key: key, + preventDefault: function () { + if (the_actual_event.preventDefault) { + the_actual_event.preventDefault(); + } + the_actual_event.returnValue = false; + }, + shiftKey: the_actual_event.shiftKey, + target: target, + that: that, + type: type, + x: the_actual_event.clientX, + y: the_actual_event.clientY + }; + +// If the target has event handlers, then fire them. Otherwise, bubble up. + + if ( + the_target["___ on ___"] + && the_target["___ on ___"][the_event.type] + ) { + target.fire(the_event); + } else { + while (true) { + the_target = the_target.parentNode; + if (!the_target) { + break; + } + if ( + the_target["___ on ___"] + && the_target["___ on ___"][the_event.type] + ) { + that = new Bunch([the_target]); + the_event.that = that; + that.fire(the_event); + break; + } + if (the_target["___adsafe root___"]) { + break; + } + } + } + if (the_event.type === "escapekey") { + if (ephemeral) { + ephemeral.remove(); + } + ephemeral = null; + } + that = null; + the_actual_event = null; + the_event = null; + the_target = null; + return; + }; + +// Mark the node as a root. This prevents event bubbling from propagating +// past it. + + root["___adsafe root___"] = "___adsafe root___"; + + Bunch.prototype = { + append: function (appendage) { + reject_global(this); + var b = this.___nodes___; + var flag = false; + var i; + var j; + var node; + var rep; + if (b.length === 0 || !appendage) { + return this; + } + if (Array.isArray(appendage)) { + if (appendage.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + rep = appendage[i].___nodes___; + for (j = 0; j < rep.length; j += 1) { + b[i].appendChild(rep[j]); + } + } + } else { + if (typeof appendage !== "string") { + rep = appendage.___nodes___; + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (rep) { + for (j = 0; j < rep.length; j += 1) { + node.appendChild((flag) + ? rep[j].cloneNode(true) + : rep[j]); + } + flag = true; + } else { + node.appendChild(document.createTextNode(appendage)); + } + } + } + return this; + }, + blur: function () { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + has_focus = null; + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.blur) { + node.blur(); + } + } + return this; + }, + check: function (value) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.checked = !!value[i]; + } + } + } else { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.checked = !!value; + } + } + } + return this; + }, + "class": function (value) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + if (/url/i.test(string_check(value[i]))) { + error("ADsafe error."); + } + node = b[i]; + if (node.tagName) { + node.className = value[i]; + } + } + } else { + if (/url/i.test(string_check(value))) { + error("ADsafe error."); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.className = value; + } + } + } + return this; + }, + clone: function (deep, n) { + var a = []; + var b = this.___nodes___; + var c; + var i; + var j; + var k = n || 1; + for (i = 0; i < k; i += 1) { + c = []; + for (j = 0; j < b.length; j += 1) { + c.push(b[j].cloneNode(deep)); + } + a.push(new Bunch(c)); + } + return (n) + ? a + : a[0]; + }, + count: function () { + reject_global(this); + return this.___nodes___.length; + }, + each: function (func) { + reject_global(this); + var b = this.___nodes___; + var i; + if (typeof func === "function") { + for (i = 0; i < b.length; i += 1) { + func(new Bunch([b[i]])); + } + return this; + } + error(); + }, + empty: function () { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + while (node.firstChild) { + purge_event_handlers(node); + node.removeChild(node.firstChild); + } + } + } else { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + while (node.firstChild) { + purge_event_handlers(node); + node.removeChild(node.firstChild); + } + } + } + return this; + }, + enable: function (enable) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(enable)) { + if (enable.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + "-" + + enable.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.disabled = !enable[i]; + } + } + } else { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.disabled = !enable; + } + } + } + return this; + }, + ephemeral: function () { + reject_global(this); + if (ephemeral) { + ephemeral.remove(); + } + ephemeral = this; + return this; + }, + explode: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = new Bunch([b[i]]); + } + return a; + }, + fire: function (event) { + + // Fire an event on an object. The event can be either + // a string containing the name of the event, or an + // object containing a type property containing the + // name of the event. Handlers registered by the "on" + // method that match the event name will be invoked. + + reject_global(this); + var array; + var b; + var i; + var j; + var n; + var node; + var on; + var type; + + if (typeof event === "string") { + type = event; + event = {type: type}; + } else if (typeof event === "object") { + type = event.type; + } else { + error(); + } + b = this.___nodes___; + n = b.length; + for (i = 0; i < n; i += 1) { + node = b[i]; + on = node["___ on ___"]; + + // If an array of handlers exist for this event, then + // loop through it and execute the handlers in order. + + if (owns(on, type)) { + array = on[type]; + for (j = 0; j < array.length; j += 1) { + + // Invoke a handler. Pass the event object. + + array[j].call(this, event); + } + } + } + return this; + }, + focus: function () { + reject_global(this); + var b = this.___nodes___; + if (b.length > 0 && allow_focus) { + has_focus = b[0].focus(); + return this; + } + error(); + }, + fragment: function () { + reject_global(this); + return new Bunch([document.createDocumentFragment()]); + }, + getCheck: function () { + return this.getChecks()[0]; + }, + getChecks: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i].checked; + } + return a; + }, + getClass: function () { + return this.getClasses()[0]; + }, + getClasses: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i].className; + } + return a; + }, + getMark: function () { + return this.getMarks()[0]; + }, + getMarks: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i]["_adsafe mark_"]; + } + return a; + }, + getName: function () { + return this.getNames()[0]; + }, + getNames: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i].name; + } + return a; + }, + getOffsetHeight: function () { + return this.getOffsetHeights()[0]; + }, + getOffsetHeights: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i].offsetHeight; + } + return a; + }, + getOffsetWidth: function () { + return this.getOffsetWidths()[0]; + }, + getOffsetWidths: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i].offsetWidth; + } + return a; + }, + getParent: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + var n; + for (i = 0; i < b.length; i += 1) { + n = b[i].parentNode; + if (n["___adsafe root___"]) { + error("ADsafe parent violation."); + } + a[i] = n; + } + return new Bunch(a); + }, + getSelection: function () { + reject_global(this); + var b = this.___nodes___; + var end; + var node; + var start; + var range; + if (b.length === 1 && allow_focus) { + node = b[0]; + if (typeof node.selectionStart === "number") { + start = node.selectionStart; + end = node.selectionEnd; + return node.value.slice(start, end); + } + range = node.createTextRange(); + range.expand("textedit"); + if (range.inRange(the_range)) { + return the_range.text; + } + } + return null; + }, + getStyle: function (name) { + return this.getStyles(name)[0]; + }, + getStyles: function (name) { + reject_global(this); + if (reject_name(name)) { + error("ADsafe style violation."); + } + var a = []; + var b = this.___nodes___; + var i; + var node; + var s; + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + s = (name !== "float") + ? getStyleObject(node)[name] + : getStyleObject(node).cssFloat + || getStyleObject(node).styleFloat; + if (typeof s === "string") { + a[i] = s; + } + } + } + return a; + }, + getTagName: function () { + return this.getTagNames()[0]; + }, + getTagNames: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + var tagName; + for (i = 0; i < b.length; i += 1) { + tagName = b[i].tagName; + a[i] = (typeof tagName === "string") + ? tagName.toLowerCase() + : tagName; + } + return a; + }, + getTitle: function () { + return this.getTitles()[0]; + }, + getTitles: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i].title; + } + return a; + }, + getValue: function () { + return this.getValues()[0]; + }, + getValues: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + var node; + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.nodeName === "#text") { + a[i] = node.nodeValue; + } else if (node.tagName && node.type !== "password") { + a[i] = node.value; + if ( + !a[i] + && node.firstChild + && node.firstChild.nodeName === "#text" + ) { + a[i] = node.firstChild.nodeValue; + } + } + } + return a; + }, + indeterminate: function (value) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.indeterminate = !!value[i]; + } + } + } else { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.indeterminate = !!value; + } + } + } + return this; + }, + klass: function (value) { + return this.class(value); + }, + mark: function (value) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node["_adsafe mark_"] = String(value[i]); + } + } + } else { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node["_adsafe mark_"] = String(value); + } + } + } + return this; + }, + off: function (type) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (typeof type === "string") { + if (typeof node["___ on ___"] === "object") { + node["___ on ___"][type] = null; + } + } else { + node["___ on ___"] = null; + } + } + return this; + }, + on: function (type, func) { + reject_global(this); + if (typeof type !== "string" || typeof func !== "function") { + error(); + } + + var b = this.___nodes___; + var i; + var node; + var on; + var ontype; + for (i = 0; i < b.length; i += 1) { + node = b[i]; + +// The change event does not propogate, so we must put the handler on the +// instance. + + if (type === "change") { + ontype = "on" + type; + if (node[ontype] !== dom_event) { + node[ontype] = dom_event; + } + } + +// Register an event. Put the function in a handler array, making one if it +// doesn't yet exist for this type on this node. + + on = node["___ on ___"]; + if (!on) { + on = {}; + node["___ on ___"] = on; + } + if (owns(on, type)) { + on[type].push(func); + } else { + on[type] = [func]; + } + } + return this; + }, + protect: function () { + reject_global(this); + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + b[i]["___adsafe root___"] = "___adsafe root___"; + } + return this; + }, + q: function (text) { + reject_global(this); + star = this.___star___; + return new Bunch( + quest(parse_query(string_check(text), id), this.___nodes___) + ); + }, + remove: function () { + reject_global(this); + this.replace(); + }, + replace: function (replacement) { + reject_global(this); + var b = this.___nodes___; + var flag = false; + var i; + var j; + var newnode; + var node; + var parent; + var rep; + if (b.length === 0) { + return; + } + for (i = 0; i < b.length; i += 1) { + purge_event_handlers(b[i]); + } + if ( + !replacement || replacement.length === 0 + || ( + replacement.___nodes___ + && replacement.___nodes___.length === 0 + ) + ) { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + purge_event_handlers(node); + if (node.parentNode) { + node.parentNode.removeChild(node); + } + } + } else if (Array.isArray(replacement)) { + if (replacement.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + parent = node.parentNode; + purge_event_handlers(node); + if (parent) { + rep = replacement[i].___nodes___; + if (rep.length > 0) { + newnode = rep[0]; + parent.replaceChild(newnode, node); + for (j = 1; j < rep.length; j += 1) { + node = newnode; + newnode = rep[j]; + parent.insertBefore(newnode, node.nextSibling); + } + } else { + parent.removeChild(node); + } + } + } + } else { + rep = replacement.___nodes___; + for (i = 0; i < b.length; i += 1) { + node = b[i]; + purge_event_handlers(node); + parent = node.parentNode; + if (parent) { + newnode = (flag) + ? rep[0].cloneNode(true) + : rep[0]; + parent.replaceChild(newnode, node); + for (j = 1; j < rep.length; j += 1) { + node = newnode; + newnode = (flag) + ? rep[j].clone(true) + : rep[j]; + parent.insertBefore(newnode, node.nextSibling); + } + flag = true; + } + } + } + return this; + }, + select: function () { + reject_global(this); + var b = this.___nodes___; + if (b.length < 1 || !allow_focus) { + error(); + } + b[0].focus(); + b[0].select(); + return this; + }, + selection: function (string) { + reject_global(this); + string_check(string); + var b = this.___nodes___; + var end; + var node; + var old; + var start; + var range; + if (b.length === 1 && allow_focus) { + node = b[0]; + if (typeof node.selectionStart === "number") { + start = node.selectionStart; + end = node.selectionEnd; + old = node.value; + node.value = old.slice(0, start) + string + old.slice(end); + node.selectionStart = start + string.length; + node.selectionEnd = start + string.length; + node.focus(); + } else { + range = node.createTextRange(); + range.expand("textedit"); + if (range.inRange(the_range)) { + the_range.select(); + the_range.text = string; + the_range.select(); + } + } + } + return this; + }, + style: function (name, value) { + reject_global(this); + if (reject_name(name)) { + error("ADsafe style violation."); + } + if (value === undefined || (/url/i.test(string_check(value)))) { + error(); + } + var b = this.___nodes___; + var i; + var node; + var v; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + v = string_check(value[i]); + if (/url/i.test(v)) { + error(); + } + if (node.tagName) { + if (name !== "float") { + node.style[name] = v; + } else { + node.style.cssFloat = v; + node.style.styleFloat = v; + } + } + } + } else { + v = string_check(value); + if (/url/i.test(v)) { + error(); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + if (name !== "float") { + node.style[name] = v; + } else { + node.style.cssFloat = v; + node.style.styleFloat = v; + } + } + } + } + return this; + }, + tag: function (tag, type, name) { + reject_global(this); + var node; + if (typeof tag !== "string") { + error(); + } + if (makeableTagName[tag] !== true) { + error("ADsafe: Bad tag: " + tag); + } + node = document.createElement(tag); + if (name) { + node.autocomplete = "off"; + node.name = string_check(name); + } + if (type) { + node.type = string_check(type); + } + return new Bunch([node]); + }, + text: function (text) { + reject_global(this); + var a; + var i; + if (Array.isArray(text)) { + a = []; + for (i = 0; i < text.length; i += 1) { + a[i] = document.createTextNode(string_check(text[i])); + } + return new Bunch(a); + } + return new Bunch([document.createTextNode(string_check(text))]); + }, + title: function (value) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.title = string_check(value[i]); + } + } + } else { + string_check(value); + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.title = value; + } + } + } + return this; + }, + value: function (value) { + reject_global(this); + if (value === undefined) { + error(); + } + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value) && b.length === value.length) { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + if (node.type !== "password") { + if (typeof node.value === "string") { + node.value = value[i]; + } else { + while (node.firstChild) { + purge_event_handlers(node.firstChild); + node.removeChild(node.firstChild); + } + node.appendChild(document.createTextNode( + String(value[i]) + )); + } + } + } else if (node.nodeName === "#text") { + node.nodeValue = String(value[i]); + } + } + } else { + value = String(value); + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + if ( + node.tagName !== "BUTTON" + && typeof node.value === "string" + ) { + node.value = value; + } else { + while (node.firstChild) { + purge_event_handlers(node.firstChild); + node.removeChild(node.firstChild); + } + node.appendChild(document.createTextNode(value)); + } + } else if (node.nodeName === "#text") { + node.nodeValue = value; + } + } + } + return this; + } + }; + +// Return an ADsafe dom object. + + dom = { + append: function (bunch) { + var b = (typeof bunch === "string") + ? [document.createTextNode(bunch)] + : bunch.___nodes___; + var i; + var n; + for (i = 0; i < b.length; i += 1) { + n = b[i]; + if (typeof n === "string" || typeof n === "number") { + n = document.createTextNode(String(n)); + } + root.appendChild(n); + } + return dom; + }, + combine: function (array) { + if (!array || !array.length) { + error("ADsafe: Bad combination."); + } + var b = array[0].___nodes___; + var i; + for (i = 0; i < array.length; i += 1) { + b = b.concat(array[i].___nodes___); + } + return new Bunch(b); + }, + count: function () { + return 1; + }, + ephemeral: function (bunch) { + if (ephemeral) { + ephemeral.remove(); + } + ephemeral = bunch; + return dom; + }, + fragment: function () { + return new Bunch([document.createDocumentFragment()]); + }, + prepend: function (bunch) { + var b = bunch.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + root.insertBefore(b[i], root.firstChild); + } + return dom; + }, + q: function (text) { + star = false; + var query = parse_query(text, id); + if (typeof hunter[query[0].op] !== "function") { + error("ADsafe: Bad query: " + query[0]); + } + return new Bunch(quest(query, [root])); + }, + remove: function () { + purge_event_handlers(root); + root.parent.removeElement(root); + root = null; + }, + row: function (values) { + var tr = document.createElement("tr"); + var td; + var i; + for (i = 0; i < values.length; i += 1) { + td = document.createElement("td"); + td.appendChild(document.createTextNode(String(values[i]))); + tr.appendChild(td); + } + return new Bunch([tr]); + }, + tag: function (tag, type, name) { + var node; + if (typeof tag !== "string") { + error(); + } + if (makeableTagName[tag] !== true) { + error("ADsafe: Bad tag: " + tag); + } + node = document.createElement(tag); + if (name) { + node.autocomplete = "off"; + node.name = name; + } + if (type) { + node.type = type; + } + return new Bunch([node]); + }, + text: function (text) { + var a; + var i; + if (Array.isArray(text)) { + a = []; + for (i = 0; i < text.length; i += 1) { + a[i] = document.createTextNode(string_check(text[i])); + } + return new Bunch(a); + } + return new Bunch([document.createTextNode(string_check(text))]); + } + }; + + if (typeof root.addEventListener === "function") { + root.addEventListener("focus", dom_event, true); + root.addEventListener("blur", dom_event, true); + root.addEventListener("mouseover", dom_event, true); + root.addEventListener("mouseout", dom_event, true); + root.addEventListener("mouseup", dom_event, true); + root.addEventListener("mousedown", dom_event, true); + root.addEventListener("mousemove", dom_event, true); + root.addEventListener("click", dom_event, true); + root.addEventListener("dblclick", dom_event, true); + root.addEventListener("keypress", dom_event, true); + } else { + root.onclick = dom_event; + root.ondblclick = dom_event; + root.onfocusin = dom_event; + root.onfocusout = dom_event; + root.onkeypress = dom_event; + root.onmouseout = dom_event; + root.onmousedown = dom_event; + root.onmousemove = dom_event; + root.onmouseover = dom_event; + root.onmouseup = dom_event; + } + return [dom, Bunch.prototype]; + } + + +// Return the ADSAFE object. + + return { + create: function (o) { + reject_global(o); + return Object.create(o); + }, + +// ADSAFE.get retrieves a value from an object. + + get: function (object, name) { + reject_global(object); + if (!reject_property(object, name)) { + return object[name]; + } + error(); + }, + +// ADSAFE.go allows a guest widget to get access to a wrapped dom node and +// approved ADsafe libraries. It is passed an id and a function. The function +// will be passed the wrapped dom node and an object containing the libraries. + + go: function (id, f) { + var dom; + var fun; + var root; + var i; + var scripts; + +// If ADSAFE.id was called, the id better match. + + if (adsafe_id && adsafe_id !== id) { + error(); + } + +// Get the dom node for the widget's div container. + + root = document.getElementById(id); + if (root.tagName !== "DIV") { + error(); + } + adsafe_id = null; + +// Delete the scripts held in the div. They have all run, so we don't need +// them any more. If the div had no scripts, then something is wrong. +// This provides some protection against mishaps due to weakness in the +// document.getElementById function. + + scripts = root.getElementsByTagName("script"); + i = scripts.length - 1; + if (i < 0) { + error(); + } + do { + root.removeChild(scripts[i]); + i -= 1; + } while (i >= 0); + root = make_root(root, id); + dom = root[0]; + +// If the page has registered interceptors, call then. + + for (i = 0; i < interceptors.length; i += 1) { + fun = interceptors[i]; + if (typeof fun === "function") { + try { + fun(id, dom, adsafe_lib, root[1]); + } catch (e1) { + ADSAFE.log(e1); + } + } + } + +// Call the supplied function. + + try { + f(dom, adsafe_lib); + } catch (e2) { + ADSAFE.log(e2); + } + root = null; + adsafe_lib = null; + }, + +// ADSAFE.has returns true if the object contains an own property with the +// given name. + + has: function (object, name) { + return owns(object, name); + }, + +// ADSAFE.id allows a guest widget to indicate that it wants to load +// ADsafe approved libraries. + + id: function (id) { + +// Calls to ADSAFE.id must be balanced with calls to ADSAFE.go. +// Only one id can be active at a time. + + if (adsafe_id) { + error(); + } + adsafe_id = id; + adsafe_lib = {}; + }, + +// ADSAFE.isArray returns true if the operand is an array. + + isArray: Array.isArray || function (value) { + return Object.prototype.toString.apply(value) === "[object Array]"; + }, + +// ADSAFE.keys returns an array of keys. + + keys: Object.keys, + +// ADSAFE.later calls a function at a later time. + + later: function (func, timeout) { + if (typeof func === "function") { + setTimeout(func, timeout || 0); + } else { + error(); + } + }, + +// ADSAFE.lib allows an approved ADsafe library to make itself available +// to a widget. The library provides a name and a function. The result of +// calling that function will be made available to the widget via the name. + + lib: function (name, f) { + if (!adsafe_id || reject_name(name)) { + error("ADsafe lib violation."); + } + adsafe_lib[name] = f(adsafe_lib); + }, + +// ADSAFE.log is a debugging aid that spams text to the browser's log. +// Overwrite this function to send log matter somewhere else. + + log: function log(s) { + if (window.console) { + console.log(s); + } else if (typeof Debug === "object") { + Debug.writeln(s); /* IE */ + } else { + opera.postError(s); /* Opera */ + } + }, + +// ADSAFE.remove deletes a value from an object. + + remove: function (object, name) { + if (!reject_property(object, name)) { + delete object[name]; + return; + } + error(); + }, + +// ADSAFE.set stores a value in an object. + + set: function (object, name, value) { + reject_global(object); + if (!reject_property(object, name)) { + object[name] = value; + return; + } + error(); + }, + +// ADSAFE._intercept allows the page to register a function that will +// see the widget's capabilities. + + _intercept: function (f) { + interceptors.push(f); + } + + }; +}()); +// intercept.js +// 2012-05-09 + +// This file makes it possible for JSLint to run as an ADsafe widget by +// adding lib features. + +// It provides a JSON cookie facility. Each widget is allowed to create a +// single JSON cookie. + +// It also provides a way for the widget to call JSLint. The widget cannot +// call JSLint directly because it is loaded as a global variable. I don't +// want to change that because other versions of JSLint depend on that. + +// And it provides access to the syntax tree that JSLint constructed. + +/*jslint nomen: true, unparam: true */ + +/*global ADSAFE, document, JSLINT */ + +/*properties + ___nodes___, _intercept, cookie, data, edition, error_report, get, getTime, + indexOf, innerHTML, jslint, length, now, parse, properties_report, property, + replace, report, set, setTime, slice, stringify, toGMTString, value +*/ + +ADSAFE._intercept(function (id, dom, lib, bunch) { + 'use strict'; + +// Give every widget access to a JSON cookie. The name of the cookie will be +// the same as the id of the widget. + + lib.cookie = { + get: function () { + +// Get the raw cookie. Extract this widget's cookie, and parse it. + + var c = ' ' + document.cookie + ';', + s = c.indexOf((' ' + id + '=')), + v; + try { + if (s >= 0) { + s += id.length + 2; + v = JSON.parse(c.slice(s, c.indexOf(';', s))); + } + } catch (ignore) {} + return v; + }, + set: function (value) { + +// Set a cookie. It must be under 2000 in length. Escapify equal sign +// and semicolon if necessary. + + var d, + j = JSON.stringify(value) + .replace(/[=]/g, '\\u003d') + .replace(/[;]/g, '\\u003b'); + + if (j.length < 2000) { + d = new Date(); + d.setTime(d.getTime() + 1e9); + document.cookie = id + "=" + j + ';expires=' + d.toGMTString(); + } + } + }; +}); + +ADSAFE._intercept(function (id, dom, lib, bunch) { + 'use strict'; + +// Give only the JSLINT_ widget access to the JSLINT function. +// We add a jslint function to its lib that calls JSLINT and +// then gets the reports, and stuffs the results into nodes +// provided by the widget. We do not trust a widget to stuff +// just any HTML content. + +// We also add an edition function to the lib that gives the +// widget access to the current edition string. + + var now = Date.now || function () { + return new Date().getTime(); + }; + + if (id === 'JSLINT_') { + lib.jslint = function (source, options, errors, report, properties, edition) { + var after, before = now(), data, errtext, protext, retext; + JSLINT(source, options); + data = JSLINT.data(); + errtext = JSLINT.error_report(data); + retext = JSLINT.report(data); + protext = JSLINT.properties_report(JSLINT.property); + after = now(); + edition.value(((after - before) / 1000) + ' seconds.'); + errors.___nodes___[0].innerHTML = errtext; + report.___nodes___[0].innerHTML = retext; + properties.value(protext); + return errtext !== ''; + }; + lib.edition = function () { + return JSLINT.edition; + }; + } +}); +// jslint.js +// 2014-07-08 + +// Copyright (c) 2002 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// WARNING: JSLint will hurt your feelings. + +// JSLINT is a global function. It takes two parameters. + +// var myResult = JSLINT(source, option); + +// The first parameter is either a string or an array of strings. If it is a +// string, it will be split on '\n' or '\r'. If it is an array of strings, it +// is assumed that each string represents one line. The source can be a +// JavaScript text or a JSON text. + +// The second parameter is an optional object of options that control the +// operation of JSLINT. Most of the options are booleans: They are all +// optional and have a default value of false. One of the options, predef, +// can be an array of names, which will be used to declare global variables, +// or an object whose keys are used as global names, with a boolean value +// that determines if they are assignable. + +// If it checks out, JSLINT returns true. Otherwise, it returns false. + +// If false, you can inspect JSLINT.errors to find out the problems. +// JSLINT.errors is an array of objects containing these properties: + +// { +// line : The line (relative to 0) at which the lint was found +// character : The character (relative to 0) at which the lint was found +// reason : The problem +// evidence : The text line in which the problem occurred +// raw : The raw message before the details were inserted +// a : The first detail +// b : The second detail +// c : The third detail +// d : The fourth detail +// } + +// If a stopping error was found, a null will be the last element of the +// JSLINT.errors array. A stopping error means that JSLint was not confident +// enough to continue. It does not necessarily mean that the error was +// especially heinous. + +// You can request a data structure that contains JSLint's results. + +// var myData = JSLINT.data(); + +// It returns a structure with this form: + +// { +// errors: [ +// { +// line: NUMBER, +// character: NUMBER, +// reason: STRING, +// evidence: STRING +// } +// ], +// functions: [ +// { +// name: STRING, +// line: NUMBER, +// level: NUMBER, +// parameter: [ +// STRING +// ], +// var: [ +// STRING +// ], +// exception: [ +// STRING +// ], +// closure: [ +// STRING +// ], +// outer: [ +// STRING +// ], +// global: [ +// STRING +// ], +// label: [ +// STRING +// ] +// } +// ], +// global: [ +// STRING +// ], +// member: { +// STRING: NUMBER +// }, +// json: BOOLEAN +// } + +// You can request a Function Report, which shows all of the functions +// and the parameters and vars that they use. This can be used to find +// implied global variables and other problems. The report is in HTML and +// can be inserted into an HTML . It should be given the result of the +// JSLINT.data function. + +// var myReport = JSLINT.report(data); + +// You can request an HTML error report. + +// var myErrorReport = JSLINT.error_report(data); + +// You can obtain an object containing all of the properties found in the +// file. JSLINT.property contains an object containing a key for each +// property used in the program, the value being the number of times that +// property name was used in the file. + +// You can request a properties report, which produces a list of the program's +// properties in the form of a /*properties*/ declaration. + +// var myPropertyReport = JSLINT.properties_report(JSLINT.property); + +// You can obtain the parse tree that JSLint constructed while parsing. The +// latest tree is kept in JSLINT.tree. A nice stringification can be produced +// with + +// JSON.stringify(JSLINT.tree, [ +// 'string', 'arity', 'name', 'first', +// 'second', 'third', 'block', 'else' +// ], 4)); + +// You can request a context coloring table. It contains information that can be +// applied to the file that was analyzed. Context coloring colors functions +// based on their nesting level, and variables on the color of the functions +// in which they are defined. + +// var myColorization = JSLINT.color(data); + +// It returns an array containing objects of this form: + +// { +// from: COLUMN, +// thru: COLUMN, +// line: ROW, +// level: 0 or higher +// } + +// JSLint provides three inline directives. They look like slashstar comments, +// and allow for setting options, declaring global variables, and establishing a +// set of allowed property names. + +// These directives respect function scope. + +// The jslint directive is a special comment that can set one or more options. +// For example: + +/*jslint + evil: true, nomen: true, regexp: true, todo: true +*/ + +// The current option set is + +// ass true, if assignment expressions should be allowed +// bitwise true, if bitwise operators should be allowed +// browser true, if the standard browser globals should be predefined +// closure true, if Google Closure idioms should be tolerated +// continue true, if the continuation statement should be tolerated +// debug true, if debugger statements should be allowed +// devel true, if logging should be allowed (console, alert, etc.) +// eqeq true, if == should be allowed +// evil true, if eval should be allowed +// forin true, if for in statements need not filter +// indent the indentation factor +// maxerr the maximum number of errors to allow +// maxlen the maximum length of a source line +// newcap true, if constructor names capitalization is ignored +// node true, if Node.js globals should be predefined +// nomen true, if names may have dangling _ +// passfail true, if the scan should stop on first error +// plusplus true, if increment/decrement should be allowed +// properties true, if all property names must be declared with /*properties*/ +// regexp true, if the . should be allowed in regexp literals +// rhino true, if the Rhino environment globals should be predefined +// unparam true, if unused parameters should be tolerated +// sloppy true, if the 'use strict'; pragma is optional +// stupid true, if really stupid practices are tolerated +// sub true, if all forms of subscript notation are tolerated +// todo true, if TODO comments are tolerated +// vars true, if multiple var statements per function should be allowed +// white true, if sloppy whitespace is tolerated + +// The properties directive declares an exclusive list of property names. +// Any properties named in the program that are not in the list will +// produce a warning. + +// For example: + +/*properties + '\b', '\t', '\n', '\f', '\r', '!', '!=', '!==', '"', '%', '\'', '(begin)', + '(error)', '*', '+', '-', '/', '<', '<=', '==', '===', '>', '>=', '\\', a, + a_label, a_scope, already_defined, and, apply, arguments, arity, ass, + assign, assignment_expression, assignment_function_expression, at, avoid_a, + b, bad_assignment, bad_constructor, bad_in_a, bad_invocation, bad_new, + bad_number, bad_operand, bad_wrap, bitwise, block, break, breakage, browser, + c, call, charAt, charCodeAt, character, closure, code, color, combine_var, + comments, conditional_assignment, confusing_a, confusing_regexp, + constructor_name_a, continue, control_a, couch, create, d, dangling_a, data, + dead, debug, deleted, devel, disrupt, duplicate_a, edge, edition, elif, + else, empty_block, empty_case, empty_class, entityify, eqeq, error_report, + errors, evidence, evil, exception, exec, expected_a_at_b_c, expected_a_b, + expected_a_b_from_c_d, expected_id_a, expected_identifier_a, + expected_identifier_a_reserved, expected_number_a, expected_operator_a, + expected_positive_a, expected_small_a, expected_space_a_b, + expected_string_a, f, first, flag, floor, forEach, for_if, forin, from, + fromCharCode, fud, function, function_block, function_eval, function_loop, + function_statement, function_strict, functions, global, hasOwnProperty, id, + identifier, identifier_function, immed, implied_evil, indent, indexOf, + infix_in, init, insecure_a, isAlpha, isArray, isDigit, isNaN, join, jslint, + json, keys, kind, label, labeled, lbp, leading_decimal_a, led, left, length, + level, line, loopage, master, match, maxerr, maxlen, message, missing_a, + missing_a_after_b, missing_property, missing_space_a_b, missing_use_strict, + mode, move_invocation, move_var, n, name, name_function, nested_comment, + newcap, node, nomen, not, not_a_constructor, not_a_defined, not_a_function, + not_a_label, not_a_scope, not_greater, nud, number, octal_a, open, outer, + parameter, parameter_a_get_b, parameter_arguments_a, parameter_set_a, + params, paren, passfail, plusplus, pop, postscript, predef, properties, + properties_report, property, prototype, push, quote, r, radix, raw, + read_only, reason, redefinition_a_b, regexp, relation, replace, report, + reserved, reserved_a, rhino, right, scanned_a_b, scope, search, second, + shift, slash_equal, slice, sloppy, sort, split, statement, statement_block, + stop, stopping, strange_loop, strict, string, stupid, sub, subscript, + substr, supplant, sync_a, t, tag_a_in_b, test, third, thru, toString, todo, + todo_comment, token, tokens, too_long, too_many, trailing_decimal_a, tree, + unclosed, unclosed_comment, unclosed_regexp, unescaped_a, unexpected_a, + unexpected_char_a, unexpected_comment, unexpected_label_a, + unexpected_property_a, unexpected_space_a_b, unexpected_typeof_a, + uninitialized_a, unnecessary_else, unnecessary_initialize, unnecessary_use, + unparam, unreachable_a_b, unsafe, unused_a, url, use_array, use_braces, + use_nested_if, use_object, use_or, use_param, use_spaces, used, + used_before_a, var, var_a_not, var_loop, vars, varstatement, warn, warning, + was, weird_assignment, weird_condition, weird_new, weird_program, + weird_relation, weird_ternary, white, wrap, wrap_immediate, wrap_regexp, + write_is_wrong, writeable +*/ + +// The global directive is used to declare global variables that can +// be accessed by the program. If a declaration is true, then the variable +// is writeable. Otherwise, it is read-only. + +// We build the application inside a function so that we produce only a single +// global variable. That function will be invoked immediately, and its return +// value is the JSLINT function itself. That function is also an object that +// can contain data and other functions. + +var JSLINT = (function () { + 'use strict'; + + function array_to_object(array, value) { + +// Make an object from an array of keys and a common value. + + var i, length = array.length, object = Object.create(null); + for (i = 0; i < length; i += 1) { + object[array[i]] = value; + } + return object; + } + + + var allowed_option = { + ass : true, + bitwise : true, + browser : true, + closure : true, + continue : true, + couch : true, + debug : true, + devel : true, + eqeq : true, + evil : true, + forin : true, + indent : 10, + maxerr : 1000, + maxlen : 256, + newcap : true, + node : true, + nomen : true, + passfail : true, + plusplus : true, + properties: true, + regexp : true, + rhino : true, + unparam : true, + sloppy : true, + stupid : true, + sub : true, + todo : true, + vars : true, + white : true + }, + anonname, // The guessed name for anonymous functions. + +// These are operators that should not be used with the ! operator. + + bang = { + '<' : true, + '<=' : true, + '==' : true, + '===': true, + '!==': true, + '!=' : true, + '>' : true, + '>=' : true, + '+' : true, + '-' : true, + '*' : true, + '/' : true, + '%' : true + }, + begin, // The root token + block_var, // vars defined in the current block + +// browser contains a set of global names that are commonly provided by a +// web browser environment. + + browser = array_to_object([ + 'clearInterval', 'clearTimeout', 'document', 'event', 'FormData', + 'frames', 'history', 'Image', 'localStorage', 'location', 'name', + 'navigator', 'Option', 'parent', 'screen', 'sessionStorage', + 'setInterval', 'setTimeout', 'Storage', 'window', 'XMLHttpRequest' + ], false), + +// bundle contains the text messages. + + bundle = { + a_label: "'{a}' is a statement label.", + a_scope: "'{a}' used out of scope.", + already_defined: "'{a}' is already defined.", + and: "The '&&' subexpression should be wrapped in parens.", + assignment_expression: "Unexpected assignment expression.", + assignment_function_expression: "Expected an assignment or " + + "function call and instead saw an expression.", + avoid_a: "Avoid '{a}'.", + bad_assignment: "Bad assignment.", + bad_constructor: "Bad constructor.", + bad_in_a: "Bad for in variable '{a}'.", + bad_invocation: "Bad invocation.", + bad_new: "Do not use 'new' for side effects.", + bad_number: "Bad number '{a}'.", + bad_operand: "Bad operand.", + bad_wrap: "Do not wrap function literals in parens unless they " + + "are to be immediately invoked.", + combine_var: "Combine this with the previous 'var' statement.", + conditional_assignment: "Expected a conditional expression and " + + "instead saw an assignment.", + confusing_a: "Confusing use of '{a}'.", + confusing_regexp: "Confusing regular expression.", + constructor_name_a: "A constructor name '{a}' should start with " + + "an uppercase letter.", + control_a: "Unexpected control character '{a}'.", + dangling_a: "Unexpected dangling '_' in '{a}'.", + deleted: "Only properties should be deleted.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + empty_case: "Empty case.", + empty_class: "Empty class.", + evil: "eval is evil.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: "Expected '{a}' to match '{b}' from line " + + "{c} and instead saw '{d}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_id_a: "Expected an id, and instead saw #{a}.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_identifier_a_reserved: "Expected an identifier and " + + "instead saw '{a}' (a reserved word).", + expected_number_a: "Expected a number and instead saw '{a}'.", + expected_operator_a: "Expected an operator and instead saw '{a}'.", + expected_positive_a: "Expected a positive number and instead saw '{a}'", + expected_small_a: "Expected a small positive integer and instead saw '{a}'", + expected_space_a_b: "Expected exactly one space between '{a}' and '{b}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + for_if: "The body of a for in should be wrapped in an if " + + "statement to filter unwanted properties from the prototype.", + function_block: "Function statements should not be placed in blocks." + + "Use a function expression or move the statement to the top of " + + "the outer function.", + function_eval: "The Function constructor is eval.", + function_loop: "Don't make functions within a loop.", + function_statement: "Function statements are not invocable. " + + "Wrap the whole function invocation in parens.", + function_strict: "Use the function form of 'use strict'.", + identifier_function: "Expected an identifier in an assignment " + + "and instead saw a function invocation.", + implied_evil: "Implied eval is evil. Pass a function instead of a string.", + infix_in: "Unexpected 'in'. Compare with undefined, or use the " + + "hasOwnProperty method instead.", + insecure_a: "Insecure '{a}'.", + isNaN: "Use the isNaN function to compare with NaN.", + leading_decimal_a: "A leading decimal point can be confused with a dot: '.{a}'.", + missing_a: "Missing '{a}'.", + missing_a_after_b: "Missing '{a}' after '{b}'.", + missing_property: "Missing property name.", + missing_space_a_b: "Missing space between '{a}' and '{b}'.", + missing_use_strict: "Missing 'use strict' statement.", + move_invocation: "Move the invocation into the parens that " + + "contain the function.", + move_var: "Move 'var' declarations to the top of the function.", + name_function: "Missing name in function statement.", + nested_comment: "Nested comment.", + not: "Nested not.", + not_a_constructor: "Do not use {a} as a constructor.", + not_a_defined: "'{a}' has not been fully defined yet.", + not_a_function: "'{a}' is not a function.", + not_a_label: "'{a}' is not a label.", + not_a_scope: "'{a}' is out of scope.", + not_greater: "'{a}' should not be greater than '{b}'.", + octal_a: "Don't use octal: '{a}'. Use '\\u....' instead.", + parameter_arguments_a: "Do not mutate parameter '{a}' when using 'arguments'.", + parameter_a_get_b: "Unexpected parameter '{a}' in get {b} function.", + parameter_set_a: "Expected parameter (value) in set {a} function.", + radix: "Missing radix parameter.", + read_only: "Read only.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + reserved_a: "Reserved name '{a}'.", + scanned_a_b: "{a} ({b}% scanned).", + slash_equal: "A regular expression literal can be confused with '/='.", + statement_block: "Expected to see a statement and instead saw a block.", + stopping: "Stopping.", + strange_loop: "Strange loop.", + strict: "Strict violation.", + subscript: "['{a}'] is better written in dot notation.", + sync_a: "Unexpected sync method: '{a}'.", + tag_a_in_b: "A '<{a}>' must be within '<{b}>'.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line too long.", + too_many: "Too many errors.", + trailing_decimal_a: "A trailing decimal point can be confused " + + "with a dot: '.{a}'.", + unclosed: "Unclosed string.", + unclosed_comment: "Unclosed comment.", + unclosed_regexp: "Unclosed regular expression.", + unescaped_a: "Unescaped '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_property_a: "Unexpected /*property*/ '{a}'.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_typeof_a: "Unexpected 'typeof'. " + + "Use '===' to compare directly with {a}.", + uninitialized_a: "Uninitialized '{a}'.", + unnecessary_else: "Unnecessary 'else' after disruption.", + unnecessary_initialize: "It is not necessary to initialize '{a}' " + + "to 'undefined'.", + unnecessary_use: "Unnecessary 'use strict'.", + unreachable_a_b: "Unreachable '{a}' after '{b}'.", + unsafe: "Unsafe character.", + unused_a: "Unused '{a}'.", + url: "JavaScript URL.", + use_array: "Use the array literal notation [].", + use_braces: "Spaces are hard to count. Use {{a}}.", + use_nested_if: "Expected 'else { if' and instead saw 'else if'.", + use_object: "Use the object literal notation {} or Object.create(null).", + use_or: "Use the || operator.", + use_param: "Use a named parameter.", + use_spaces: "Use spaces, not tabs.", + used_before_a: "'{a}' was used before it was defined.", + var_a_not: "Variable {a} was not declared correctly.", + var_loop: "Don't declare variables in a loop.", + weird_assignment: "Weird assignment.", + weird_condition: "Weird condition.", + weird_new: "Weird construction. Delete 'new'.", + weird_program: "Weird program.", + weird_relation: "Weird relation.", + weird_ternary: "Weird ternary.", + wrap_immediate: "Wrap an immediate function invocation in " + + "parentheses to assist the reader in understanding that the " + + "expression is the result of a function, and not the " + + "function itself.", + wrap_regexp: "Wrap the /regexp/ literal in parens to " + + "disambiguate the slash operator.", + write_is_wrong: "document.write can be a form of eval." + }, + closure = array_to_object([ + 'goog' + ], false), + comments, + comments_off, + couch = array_to_object([ + 'emit', 'getRow', 'isArray', 'log', 'provides', 'registerType', + 'require', 'send', 'start', 'sum', 'toJSON' + ], false), + + descapes = { + 'b': '\b', + 't': '\t', + 'n': '\n', + 'f': '\f', + 'r': '\r', + '"': '"', + '/': '/', + '\\': '\\', + '!': '!' + }, + + devel = array_to_object([ + 'alert', 'confirm', 'console', 'Debug', 'opera', 'prompt', 'WSH' + ], false), + directive, + escapes = { + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '\'': '\\\'', + '"' : '\\"', + '/' : '\\/', + '\\': '\\\\' + }, + + funct, // The current function + + functions, // All of the functions + global_funct, // The global body + global_scope, // The global scope + in_block, // Where function statements are not allowed + indent, + itself, // JSLINT itself + json_mode, + lex, // the tokenizer + lines, + lookahead, + node = array_to_object([ + 'Buffer', 'clearImmediate', 'clearInterval', 'clearTimeout', + 'console', 'exports', 'global', 'module', 'process', + 'require', 'setImmediate', 'setInterval', 'setTimeout', + '__dirname', '__filename' + ], false), + node_js, + numbery = array_to_object(['indexOf', 'lastIndexOf', 'search'], true), + next_token, + option, + predefined, // Global variables defined by option + prereg, + prev_token, + property, + protosymbol, + regexp_flag = array_to_object(['g', 'i', 'm'], true), + return_this = function return_this() { + return this; + }, + rhino = array_to_object([ + 'defineClass', 'deserialize', 'gc', 'help', 'load', 'loadClass', + 'print', 'quit', 'readFile', 'readUrl', 'runCommand', 'seal', + 'serialize', 'spawn', 'sync', 'toint32', 'version' + ], false), + + scope, // An object containing an object for each variable in scope + semicolon_coda = array_to_object([';', '"', '\'', ')'], true), + +// standard contains the global names that are provided by the +// ECMAScript standard. + + standard = array_to_object([ + 'Array', 'Boolean', 'Date', 'decodeURI', 'decodeURIComponent', + 'encodeURI', 'encodeURIComponent', 'Error', 'eval', 'EvalError', + 'Function', 'isFinite', 'isNaN', 'JSON', 'Map', 'Math', 'Number', + 'Object', 'parseInt', 'parseFloat', 'Promise', 'Proxy', + 'RangeError', 'ReferenceError', 'Reflect', 'RegExp', 'Set', + 'String', 'Symbol', 'SyntaxError', 'System', 'TypeError', + 'URIError', 'WeakMap', 'WeakSet' + ], false), + + strict_mode, + syntax = Object.create(null), + token, + tokens, + var_mode, + warnings, + +// Regular expressions. Some of these are stupidly long. + +// carriage return, carriage return linefeed, or linefeed + crlfx = /\r\n?|\n/, +// unsafe characters that are silently deleted by one or more browsers + cx = /[\u0000-\u0008\u000a-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/, +// identifier + ix = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/, +// javascript url + jx = /^(?:javascript|jscript|ecmascript|vbscript)\s*:/i, +// star slash + lx = /\*\/|\/\*/, +// characters in strings that need escapement + nx = /[\u0000-\u001f'\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, +// sync + syx = /Sync$/, +// comment todo + tox = /^\W*to\s*do(?:\W|$)/i, +// token + tx = /^\s*([(){}\[\]\?.,:;'"~#@`]|={1,3}|\/(\*(jslint|properties|property|members?|globals?)?|=|\/)?|\*[\/=]?|\+(?:=|\++)?|-(?:=|-+)?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<(?:[\/=!]|\!(\[|--)?|<=?)?|\!(\!|==?)?|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+(?:[xX][0-9a-fA-F]+|\.[0-9]*)?(?:[eE][+\-]?[0-9]+)?)/; + + + if (typeof String.prototype.entityify !== 'function') { + String.prototype.entityify = function () { + return this + .replace(/&/g, '&') + .replace(//g, '>'); + }; + } + + if (typeof String.prototype.isAlpha !== 'function') { + String.prototype.isAlpha = function () { + return (this >= 'a' && this <= 'z\uffff') || + (this >= 'A' && this <= 'Z\uffff'); + }; + } + + if (typeof String.prototype.isDigit !== 'function') { + String.prototype.isDigit = function () { + return (this >= '0' && this <= '9'); + }; + } + + if (typeof String.prototype.supplant !== 'function') { + String.prototype.supplant = function (o) { + return this.replace(/\{([^{}]*)\}/g, function (a, b) { + var replacement = o[b]; + return typeof replacement === 'string' || + typeof replacement === 'number' ? replacement : a; + }); + }; + } + + + function sanitize(a) { + +// Escapify a troublesome character. + + return escapes[a] || + '\\u' + ('0000' + a.charCodeAt().toString(16)).slice(-4); + } + + + function add_to_predefined(group) { + Object.keys(group).forEach(function (name) { + predefined[name] = group[name]; + }); + } + + + function assume() { + if (option.browser) { + add_to_predefined(browser); + option.browser = false; + } + if (option.closure) { + add_to_predefined(closure); + } + if (option.couch) { + add_to_predefined(couch); + option.couch = false; + } + if (option.devel) { + add_to_predefined(devel); + option.devel = false; + } + if (option.node) { + add_to_predefined(node); + option.node = false; + node_js = true; + } + if (option.rhino) { + add_to_predefined(rhino); + option.rhino = false; + } + } + + +// Produce an error warning. + + function artifact(tok) { + if (!tok) { + tok = next_token; + } + return tok.id === '(number)' ? tok.number : tok.string; + } + + function quit(message, line, character) { + throw { + name: 'JSLintError', + line: line, + character: character, + message: bundle.scanned_a_b.supplant({ + a: bundle[message] || message, + b: Math.floor((line / lines.length) * 100) + }) + }; + } + + function warn(code, line, character, a, b, c, d) { + var warning = { // ~~ + id: '(error)', + raw: bundle[code] || code, + code: code, + evidence: lines[line - 1] || '', + line: line, + character: character, + a: a || artifact(this), + b: b, + c: c, + d: d + }; + warning.reason = warning.raw.supplant(warning); + itself.errors.push(warning); + if (option.passfail) { + quit('stopping', line, character); + } + warnings += 1; + if (warnings >= option.maxerr) { + quit('too_many', line, character); + } + return warning; + } + + function stop(code, line, character, a, b, c, d) { + var warning = warn(code, line, character, a, b, c, d); + quit('stopping', warning.line, warning.character); + } + + function expected_at(at) { + if (!option.white && next_token.from !== at) { + next_token.warn('expected_a_at_b_c', '', at, next_token.from); + } + } + +// lexical analysis and token construction + + lex = (function lex() { + var character, c, from, length, line, pos, source_row; + +// Private lex methods + + function next_line() { + var at; + character = 1; + source_row = lines[line]; + line += 1; + if (source_row === undefined) { + return false; + } + at = source_row.search(/\t/); + if (at >= 0) { + if (option.white) { + source_row = source_row.replace(/\t/g, ' '); + } else { + warn('use_spaces', line, at + 1); + } + } + at = source_row.search(cx); + if (at >= 0) { + warn('unsafe', line, at); + } + if (option.maxlen && option.maxlen < source_row.length) { + warn('too_long', line, source_row.length); + } + return true; + } + +// Produce a token object. The token inherits from a syntax symbol. + + function it(type, value) { + var id, the_token; + if (type === '(string)') { + if (jx.test(value)) { + warn('url', line, from); + } + } + the_token = Object.create(syntax[( + type === '(punctuator)' || (type === '(identifier)' && + Object.prototype.hasOwnProperty.call(syntax, value)) + ? value + : type + )] || syntax['(error)']); + if (type === '(identifier)') { + the_token.identifier = true; + if (value === '__iterator__' || value === '__proto__') { + stop('reserved_a', line, from, value); + } else if (!option.nomen && + (value.charAt(0) === '_' || + value.charAt(value.length - 1) === '_')) { + warn('dangling_a', line, from, value); + } + } + if (type === '(number)') { + the_token.number = +value; + } else if (value !== undefined) { + the_token.string = String(value); + } + the_token.line = line; + the_token.from = from; + the_token.thru = character; + if (comments.length) { + the_token.comments = comments; + comments = []; + } + id = the_token.id; + prereg = id && ( + ('(,=:[!&|?{};~+-*%^<>'.indexOf(id.charAt(id.length - 1)) >= 0) || + id === 'return' || id === 'case' + ); + return the_token; + } + + function match(x) { + var exec = x.exec(source_row), first; + if (exec) { + length = exec[0].length; + first = exec[1]; + c = first.charAt(0); + source_row = source_row.slice(length); + from = character + length - first.length; + character += length; + return first; + } + for (;;) { + if (!source_row) { + if (!option.white) { + warn('unexpected_char_a', line, character - 1, '(space)'); + } + return; + } + c = source_row.charAt(0); + if (c !== ' ') { + break; + } + source_row = source_row.slice(1); + character += 1; + } + stop('unexpected_char_a', line, character, c); + + } + + function string(x) { + var ch, at = 0, r = '', result; + + function hex(n) { + var i = parseInt(source_row.substr(at + 1, n), 16); + at += n; + if (i >= 32 && i <= 126 && + i !== 34 && i !== 92 && i !== 39) { + warn('unexpected_a', line, character, '\\'); + } + character += n; + ch = String.fromCharCode(i); + } + + if (json_mode && x !== '"') { + warn('expected_a_b', line, character, '"', x); + } + + for (;;) { + while (at >= source_row.length) { + at = 0; + if (!next_line()) { + stop('unclosed', line - 1, from); + } + } + ch = source_row.charAt(at); + if (ch === x) { + character += 1; + source_row = source_row.slice(at + 1); + result = it('(string)', r); + result.quote = x; + return result; + } + if (ch < ' ') { + if (ch === '\n' || ch === '\r') { + break; + } + warn('control_a', line, character + at, + source_row.slice(0, at)); + } else if (ch === '\\') { + at += 1; + character += 1; + ch = source_row.charAt(at); + switch (ch) { + case '': + warn('unexpected_a', line, character, '\\'); + next_line(); + at = -1; + break; + case '\'': + if (json_mode) { + warn('unexpected_a', line, character, '\\\''); + } + break; + case 'u': + hex(4); + break; + case 'v': + if (json_mode) { + warn('unexpected_a', line, character, '\\v'); + } + ch = '\v'; + break; + case 'x': + if (json_mode) { + warn('unexpected_a', line, character, '\\x'); + } + hex(2); + break; + default: + if (typeof descapes[ch] !== 'string') { + warn(ch >= '0' && ch <= '7' ? 'octal_a' : 'unexpected_a', + line, character, '\\' + ch); + } else { + ch = descapes[ch]; + } + } + } + r += ch; + character += 1; + at += 1; + } + } + + function number(snippet) { + var digit; + if (source_row.charAt(0).isAlpha()) { + warn('expected_space_a_b', + line, character, c, source_row.charAt(0)); + } + if (c === '0') { + digit = snippet.charAt(1); + if (digit.isDigit()) { + if (token.id !== '.') { + warn('unexpected_a', line, character, snippet); + } + } else if (json_mode && (digit === 'x' || digit === 'X')) { + warn('unexpected_a', line, character, '0x'); + } + } + if (snippet.slice(snippet.length - 1) === '.') { + warn('trailing_decimal_a', line, character, snippet); + } + digit = +snippet; + if (!isFinite(digit)) { + warn('bad_number', line, character, snippet); + } + snippet = digit; + return it('(number)', snippet); + } + + function comment(snippet, type) { + if (comments_off) { + warn('unexpected_comment', line, character); + } else if (!option.todo && tox.test(snippet)) { + warn('todo_comment', line, character); + } + comments.push({ + id: type, + from: from, + thru: character, + line: line, + string: snippet + }); + } + + function regexp() { + var at = 0, + b, + bit, + depth = 0, + flag = '', + high, + letter, + low, + potential, + quote, + result; + for (;;) { + b = true; + c = source_row.charAt(at); + at += 1; + switch (c) { + case '': + stop('unclosed_regexp', line, from); + return; + case '/': + if (depth > 0) { + warn('unescaped_a', line, from + at, '/'); + } + c = source_row.slice(0, at - 1); + potential = Object.create(regexp_flag); + for (;;) { + letter = source_row.charAt(at); + if (potential[letter] !== true) { + break; + } + potential[letter] = false; + at += 1; + flag += letter; + } + if (source_row.charAt(at).isAlpha()) { + stop('unexpected_a', line, from, source_row.charAt(at)); + } + character += at; + source_row = source_row.slice(at); + quote = source_row.charAt(0); + if (quote === '/' || quote === '*') { + stop('confusing_regexp', line, from); + } + result = it('(regexp)', c); + result.flag = flag; + return result; + case '\\': + c = source_row.charAt(at); + if (c < ' ') { + warn('control_a', line, from + at, String(c)); + } else if (c === '<') { + warn('unexpected_a', line, from + at, '\\'); + } + at += 1; + break; + case '(': + depth += 1; + b = false; + if (source_row.charAt(at) === '?') { + at += 1; + switch (source_row.charAt(at)) { + case ':': + case '=': + case '!': + at += 1; + break; + default: + warn('expected_a_b', line, from + at, + ':', source_row.charAt(at)); + } + } + break; + case '|': + b = false; + break; + case ')': + if (depth === 0) { + warn('unescaped_a', line, from + at, ')'); + } else { + depth -= 1; + } + break; + case ' ': + pos = 1; + while (source_row.charAt(at) === ' ') { + at += 1; + pos += 1; + } + if (pos > 1) { + warn('use_braces', line, from + at, pos); + } + break; + case '[': + c = source_row.charAt(at); + if (c === '^') { + at += 1; + if (!option.regexp) { + warn('insecure_a', line, from + at, c); + } else if (source_row.charAt(at) === ']') { + stop('unescaped_a', line, from + at, '^'); + } + } + bit = false; + if (c === ']') { + warn('empty_class', line, from + at - 1); + bit = true; + } +klass: do { + c = source_row.charAt(at); + at += 1; + switch (c) { + case '[': + case '^': + warn('unescaped_a', line, from + at, c); + bit = true; + break; + case '-': + if (bit) { + bit = false; + } else { + warn('unescaped_a', line, from + at, '-'); + bit = true; + } + break; + case ']': + if (!bit) { + warn('unescaped_a', line, from + at - 1, '-'); + } + break klass; + case '\\': + c = source_row.charAt(at); + if (c < ' ') { + warn('control_a', line, from + at, String(c)); + } else if (c === '<') { + warn('unexpected_a', line, from + at, '\\'); + } + at += 1; + bit = true; + break; + case '/': + warn('unescaped_a', line, from + at - 1, '/'); + bit = true; + break; + default: + bit = true; + } + } while (c); + break; + case '.': + if (!option.regexp) { + warn('insecure_a', line, from + at, c); + } + break; + case ']': + case '?': + case '{': + case '}': + case '+': + case '*': + warn('unescaped_a', line, from + at, c); + break; + } + if (b) { + switch (source_row.charAt(at)) { + case '?': + case '+': + case '*': + at += 1; + if (source_row.charAt(at) === '?') { + at += 1; + } + break; + case '{': + at += 1; + c = source_row.charAt(at); + if (c < '0' || c > '9') { + warn('expected_number_a', line, + from + at, c); + } + at += 1; + low = +c; + for (;;) { + c = source_row.charAt(at); + if (c < '0' || c > '9') { + break; + } + at += 1; + low = +c + (low * 10); + } + high = low; + if (c === ',') { + at += 1; + high = Infinity; + c = source_row.charAt(at); + if (c >= '0' && c <= '9') { + at += 1; + high = +c; + for (;;) { + c = source_row.charAt(at); + if (c < '0' || c > '9') { + break; + } + at += 1; + high = +c + (high * 10); + } + } + } + if (source_row.charAt(at) !== '}') { + warn('expected_a_b', line, from + at, + '}', c); + } else { + at += 1; + } + if (source_row.charAt(at) === '?') { + at += 1; + } + if (low > high) { + warn('not_greater', line, from + at, + low, high); + } + break; + } + } + } + c = source_row.slice(0, at - 1); + character += at; + source_row = source_row.slice(at); + return it('(regexp)', c); + } + +// Public lex methods + + return { + init: function (source) { + if (typeof source === 'string') { + lines = source.split(crlfx); + } else { + lines = source; + } + line = 0; + next_line(); + from = 1; + }, + +// token -- this is called by advance to get the next token. + + token: function () { + var first, i, snippet; + + for (;;) { + while (!source_row) { + if (!next_line()) { + return it('(end)'); + } + } + snippet = match(tx); + if (snippet) { + +// identifier + + first = snippet.charAt(0); + if (first.isAlpha() || first === '_' || first === '$') { + return it('(identifier)', snippet); + } + +// number + + if (first.isDigit()) { + return number(snippet); + } + switch (snippet) { + +// string + + case '"': + case "'": + return string(snippet); + +// // comment + + case '//': + comment(source_row, '//'); + source_row = ''; + break; + +// /* comment + + case '/*': + for (;;) { + i = source_row.search(lx); + if (i >= 0) { + break; + } + character = source_row.length; + comment(source_row); + from = 0; + if (!next_line()) { + stop('unclosed_comment', line, character); + } + } + comment(source_row.slice(0, i), '/*'); + character += i + 2; + if (source_row.charAt(i) === '/') { + stop('nested_comment', line, character); + } + source_row = source_row.slice(i + 2); + break; + + case '': + break; +// / + case '/': + if (token.id === '/=') { + stop('slash_equal', line, from); + } + return prereg + ? regexp() + : it('(punctuator)', snippet); + +// punctuator + default: + return it('(punctuator)', snippet); + } + } + } + } + }; + }()); + + function define(kind, token) { + +// Define a name. + + var name = token.string, + master = scope[name]; // The current definition of the name + +// vars are created with a deadzone, so that the expression that initializes +// the var cannot access the var. Functions are not writeable. + + token.dead = false; + token.init = false; + token.kind = kind; + token.master = master; + token.used = 0; + token.writeable = true; + +// Global variables are a little weird. They can be defined multiple times. +// Some predefined global vars are (or should) not be writeable. + + if (kind === 'var' && funct === global_funct) { + if (!master) { + if (predefined[name] === false) { + token.writeable = false; + } + global_scope[name] = token; + } + } else { + +// It is an error if the name has already been defined in this scope, except +// when reusing an exception variable name. + + if (master) { + if (master.function === funct) { + if (master.kind !== 'exception' || kind !== 'exception' || + !master.dead) { + token.warn('already_defined', name); + } + } else if (master.function !== global_funct) { + if (kind === 'var') { + token.warn('redefinition_a_b', name, master.line); + } + } + } + scope[name] = token; + if (kind === 'var') { + block_var.push(name); + } + } + } + + function peek(distance) { + +// Peek ahead to a future token. The distance is how far ahead to look. The +// default is the next token. + + var found, slot = 0; + + distance = distance || 0; + while (slot <= distance) { + found = lookahead[slot]; + if (!found) { + found = lookahead[slot] = lex.token(); + } + slot += 1; + } + return found; + } + + + function advance(id, match) { + +// Produce the next token, also looking for programming errors. + + if (indent) { + +// If indentation checking was requested, then inspect all of the line breakings. +// The var statement is tricky because the names might be aligned or not. We +// look at the first line break after the var to determine the programmer's +// intention. + + if (var_mode && next_token.line !== token.line) { + if ((var_mode !== indent || !next_token.edge) && + next_token.from === indent.at - + (next_token.edge ? option.indent : 0)) { + var dent = indent; + for (;;) { + dent.at -= option.indent; + if (dent === var_mode) { + break; + } + dent = dent.was; + } + dent.open = false; + } + var_mode = null; + } + if (next_token.id === '?' && indent.mode === ':' && + token.line !== next_token.line) { + indent.at -= option.indent; + } + if (indent.open) { + +// If the token is an edge. + + if (next_token.edge) { + if (next_token.edge === 'label') { + expected_at(1); + } else if (next_token.edge === 'case' || indent.mode === 'statement') { + expected_at(indent.at - option.indent); + } else if (indent.mode !== 'array' || next_token.line !== token.line) { + expected_at(indent.at); + } + +// If the token is not an edge, but is the first token on the line. + + } else if (next_token.line !== token.line) { + if (next_token.from < indent.at + (indent.mode === + 'expression' ? 0 : option.indent)) { + expected_at(indent.at + option.indent); + } + indent.wrap = true; + } + } else if (next_token.line !== token.line) { + if (next_token.edge) { + expected_at(indent.at); + } else { + indent.wrap = true; + if (indent.mode === 'statement' || indent.mode === 'var') { + expected_at(indent.at + option.indent); + } else if (next_token.from < indent.at + (indent.mode === + 'expression' ? 0 : option.indent)) { + expected_at(indent.at + option.indent); + } + } + } + } + + switch (token.id) { + case '(number)': + if (next_token.id === '.') { + next_token.warn('trailing_decimal_a'); + } + break; + case '-': + if (next_token.id === '-' || next_token.id === '--') { + next_token.warn('confusing_a'); + } + break; + case '+': + if (next_token.id === '+' || next_token.id === '++') { + next_token.warn('confusing_a'); + } + break; + } + if (token.id === '(string)' || token.identifier) { + anonname = token.string; + } + + if (id && next_token.id !== id) { + if (match) { + next_token.warn('expected_a_b_from_c_d', id, + match.id, match.line, artifact()); + } else if (!next_token.identifier || next_token.string !== id) { + next_token.warn('expected_a_b', id, artifact()); + } + } + prev_token = token; + token = next_token; + next_token = lookahead.shift() || lex.token(); + next_token.function = funct; + tokens.push(next_token); + } + + + function do_globals() { + var name, writeable; + for (;;) { + if (next_token.id !== '(string)' && !next_token.identifier) { + return; + } + name = next_token.string; + advance(); + writeable = false; + if (next_token.id === ':') { + advance(':'); + switch (next_token.id) { + case 'true': + writeable = predefined[name] !== false; + advance('true'); + break; + case 'false': + advance('false'); + break; + default: + next_token.stop('unexpected_a'); + } + } + predefined[name] = writeable; + if (next_token.id !== ',') { + return; + } + advance(','); + } + } + + + function do_jslint() { + var name, value; + while (next_token.id === '(string)' || next_token.identifier) { + name = next_token.string; + if (!allowed_option[name]) { + next_token.stop('unexpected_a'); + } + advance(); + if (next_token.id !== ':') { + next_token.stop('expected_a_b', ':', artifact()); + } + advance(':'); + if (typeof allowed_option[name] === 'number') { + value = next_token.number; + if (value > allowed_option[name] || value <= 0 || + Math.floor(value) !== value) { + next_token.stop('expected_small_a'); + } + option[name] = value; + } else { + if (next_token.id === 'true') { + option[name] = true; + } else if (next_token.id === 'false') { + option[name] = false; + } else { + next_token.stop('unexpected_a'); + } + } + advance(); + if (next_token.id === ',') { + advance(','); + } + } + assume(); + } + + + function do_properties() { + var name; + option.properties = true; + for (;;) { + if (next_token.id !== '(string)' && !next_token.identifier) { + return; + } + name = next_token.string; + advance(); + if (next_token.id === ':') { + for (;;) { + advance(); + if (next_token.id !== '(string)' && !next_token.identifier) { + break; + } + } + } + property[name] = 0; + if (next_token.id !== ',') { + return; + } + advance(','); + } + } + + + directive = function directive() { + var command = this.id, + old_comments_off = comments_off, + old_indent = indent; + comments_off = true; + indent = null; + if (next_token.line === token.line && next_token.from === token.thru) { + next_token.warn('missing_space_a_b', artifact(token), artifact()); + } + if (lookahead.length > 0) { + this.warn('unexpected_a'); + } + switch (command) { + case '/*properties': + case '/*property': + case '/*members': + case '/*member': + do_properties(); + break; + case '/*jslint': + do_jslint(); + break; + case '/*globals': + case '/*global': + do_globals(); + break; + default: + this.stop('unexpected_a'); + } + comments_off = old_comments_off; + advance('*/'); + indent = old_indent; + }; + + +// Indentation intention + + function edge(mode) { + next_token.edge = indent ? indent.open && (mode || 'edge') : ''; + } + + + function step_in(mode) { + var open; + if (typeof mode === 'number') { + indent = { + at: +mode, + open: true, + was: indent + }; + } else if (!indent) { + indent = { + at: 1, + mode: 'statement', + open: true + }; + } else if (mode === 'statement') { + indent = { + at: indent.at, + open: true, + was: indent + }; + } else { + open = mode === 'var' || next_token.line !== token.line; + indent = { + at: (open || mode === 'control' + ? indent.at + option.indent + : indent.at) + (indent.wrap ? option.indent : 0), + mode: mode, + open: open, + was: indent + }; + if (mode === 'var' && open) { + var_mode = indent; + } + } + } + + function step_out(id, symbol) { + if (id) { + if (indent && indent.open) { + indent.at -= option.indent; + edge(); + } + advance(id, symbol); + } + if (indent) { + indent = indent.was; + } + } + +// Functions for conformance of whitespace. + + function one_space(left, right) { + left = left || token; + right = right || next_token; + if (right.id !== '(end)' && !option.white && + (token.line !== right.line || + token.thru + 1 !== right.from)) { + right.warn('expected_space_a_b', artifact(token), artifact(right)); + } + } + + function one_space_only(left, right) { + left = left || token; + right = right || next_token; + if (right.id !== '(end)' && (left.line !== right.line || + (!option.white && left.thru + 1 !== right.from))) { + right.warn('expected_space_a_b', artifact(left), artifact(right)); + } + } + + function no_space(left, right) { + left = left || token; + right = right || next_token; + if ((!option.white) && + left.thru !== right.from && left.line === right.line) { + right.warn('unexpected_space_a_b', artifact(left), artifact(right)); + } + } + + function no_space_only(left, right) { + left = left || token; + right = right || next_token; + if (right.id !== '(end)' && (left.line !== right.line || + (!option.white && left.thru !== right.from))) { + right.warn('unexpected_space_a_b', artifact(left), artifact(right)); + } + } + + function spaces(left, right) { + if (!option.white) { + left = left || token; + right = right || next_token; + if (left.thru === right.from && left.line === right.line) { + right.warn('missing_space_a_b', artifact(left), artifact(right)); + } + } + } + + function comma() { + if (next_token.id !== ',') { + warn('expected_a_b', token.line, token.thru, ',', artifact()); + } else { + if (!option.white) { + no_space_only(); + } + advance(','); + spaces(); + } + } + + + function semicolon() { + if (next_token.id !== ';') { + warn('expected_a_b', token.line, token.thru, ';', artifact()); + } else { + if (!option.white) { + no_space_only(); + } + advance(';'); + if (semicolon_coda[next_token.id] !== true) { + spaces(); + } + } + } + + function use_strict() { + if (next_token.string === 'use strict') { + if (strict_mode) { + next_token.warn('unnecessary_use'); + } + edge(); + advance(); + semicolon(); + strict_mode = true; + return true; + } + return false; + } + + + function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + if (Array.isArray(b) && a.length === b.length) { + var i; + for (i = 0; i < a.length; i += 1) { + if (!are_similar(a[i], b[i])) { + return false; + } + } + return true; + } + return false; + } + if (Array.isArray(b)) { + return false; + } + if (a.id === '(number)' && b.id === '(number)') { + return a.number === b.number; + } + if (a.arity === b.arity && a.string === b.string) { + switch (a.arity) { + case undefined: + return a.string === b.string; + case 'prefix': + case 'suffix': + return a.id === b.id && are_similar(a.first, b.first) && + a.id !== '{' && a.id !== '['; + case 'infix': + return are_similar(a.first, b.first) && + are_similar(a.second, b.second); + case 'ternary': + return are_similar(a.first, b.first) && + are_similar(a.second, b.second) && + are_similar(a.third, b.third); + case 'function': + case 'regexp': + return false; + default: + return true; + } + } + if (a.id === '.' && b.id === '[' && b.arity === 'infix') { + return a.second.string === b.second.string && b.second.id === '(string)'; + } + if (a.id === '[' && a.arity === 'infix' && b.id === '.') { + return a.second.string === b.second.string && a.second.id === '(string)'; + } + return false; + } + + +// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it +// is looking for ad hoc lint patterns. We add .fud to Pratt's model, which is +// like .nud except that it is only used on the first token of a statement. +// Having .fud makes it much easier to define statement-oriented languages like +// JavaScript. I retained Pratt's nomenclature. + +// .nud Null denotation +// .fud First null denotation +// .led Left denotation +// lbp Left binding power +// rbp Right binding power + +// They are elements of the parsing method called Top Down Operator Precedence. + + function expression(rbp, initial) { + +// rbp is the right binding power. +// initial indicates that this is the first expression of a statement. + + var left; + if (next_token.id === '(end)') { + token.stop('unexpected_a', next_token.id); + } + advance(); + if (initial) { + anonname = 'anonymous'; + } + if (initial === true && token.fud) { + left = token.fud(); + } else { + if (token.nud) { + left = token.nud(); + } else { + if (next_token.id === '(number)' && token.id === '.') { + token.warn('leading_decimal_a', artifact()); + advance(); + return token; + } + token.stop('expected_identifier_a', artifact(token)); + } + while (rbp < next_token.lbp) { + advance(); + left = token.led(left); + } + } + if (left && left.assign && !initial) { + if (!option.ass) { + left.warn('assignment_expression'); + } + if (left.id !== '=' && left.first.master) { + left.first.master.used = true; + } + } + return left; + } + + protosymbol = { + nud: function () { + this.stop('unexpected_a'); + }, + led: function () { + this.stop('expected_operator_a'); + }, + warn: function (code, a, b, c, d) { + if (!this.warning) { + this.warning = warn(code, this.line || 0, this.from || 0, + a || artifact(this), b, c, d); + } + }, + stop: function (code, a, b, c, d) { + this.warning = undefined; + this.warn(code, a, b, c, d); + return quit('stopping', this.line, this.character); + }, + lbp: 0 + }; + +// Functional constructors for making the symbols that will be inherited by +// tokens. + + function symbol(s, bp) { + var x = syntax[s]; + if (!x) { + x = Object.create(protosymbol); + x.id = x.string = s; + x.lbp = bp || 0; + syntax[s] = x; + } + return x; + } + + function postscript(x) { + x.postscript = true; + return x; + } + + function ultimate(s) { + var x = symbol(s, 0); + x.from = 1; + x.thru = 1; + x.line = 0; + x.edge = 'edge'; + x.string = s; + return postscript(x); + } + + function reserve_name(x) { + var c = x.id.charAt(0); + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { + x.identifier = x.reserved = true; + } + return x; + } + + function stmt(s, f) { + var x = symbol(s); + x.fud = f; + return reserve_name(x); + } + + function disrupt_stmt(s, f) { + var x = stmt(s, f); + x.disrupt = true; + } + + function labeled_stmt(s, f) { + var x = stmt(s, function labeled() { + var the_statement; + if (funct.breakage) { + funct.breakage.push(this); + } else { + funct.breakage = [this]; + } + the_statement = f.apply(this); + if (funct.breakage.length > 1) { + funct.breakage.pop(); + } else { + delete funct.breakage; + } + return the_statement; + }); + x.labeled = true; + } + + function prefix(s, f) { + var x = symbol(s, 150); + reserve_name(x); + x.nud = function () { + var that = this; + that.arity = 'prefix'; + if (typeof f === 'function') { + that = f(that); + if (that.arity !== 'prefix') { + return that; + } + } else { + if (s === 'typeof') { + one_space(); + } else { + no_space_only(); + } + that.first = expression(150); + } + switch (that.id) { + case '++': + case '--': + if (!option.plusplus) { + that.warn('unexpected_a'); + } else if ((!that.first.identifier || that.first.reserved) && + that.first.id !== '.' && that.first.id !== '[') { + that.warn('bad_operand'); + } + break; + default: + if (that.first.arity === 'prefix' || + that.first.arity === 'function') { + that.warn('unexpected_a'); + } + } + return that; + }; + return x; + } + + + function type(s, t, nud) { + var x = symbol(s); + x.arity = t; + if (nud) { + x.nud = nud; + } + return x; + } + + + function reserve(s, f) { + var x = symbol(s); + x.identifier = x.reserved = true; + if (typeof f === 'function') { + x.nud = f; + } + return x; + } + + + function constant(name) { + var x = reserve(name); + x.string = name; + x.nud = return_this; + return x; + } + + + function reservevar(s, v) { + return reserve(s, function () { + if (typeof v === 'function') { + v(this); + } + return this; + }); + } + + + function infix(s, p, f, w) { + var x = symbol(s, p); + reserve_name(x); + x.led = function (left) { + this.arity = 'infix'; + if (!w) { + spaces(prev_token, token); + spaces(); + } + if (!option.bitwise && this.bitwise) { + this.warn('unexpected_a'); + } + if (typeof f === 'function') { + return f(left, this); + } + this.first = left; + this.second = expression(p); + return this; + }; + return x; + } + + function expected_relation(node, message) { + if (node.assign) { + node.warn(message || 'conditional_assignment'); + } + return node; + } + + function expected_condition(node, message) { + switch (node.id) { + case '[': + case '-': + if (node.arity !== 'infix') { + node.warn(message || 'weird_condition'); + } + break; + case 'false': + case 'function': + case 'Infinity': + case 'NaN': + case 'null': + case 'true': + case 'undefined': + case 'void': + case '(number)': + case '(regexp)': + case '(string)': + case '{': + case '?': + case '~': + node.warn(message || 'weird_condition'); + break; + case '(': + if (node.first.id === 'new' || + (node.first.string === 'Boolean') || + (node.first.id === '.' && + numbery[node.first.second.string] === true)) { + node.warn(message || 'weird_condition'); + } + break; + } + return node; + } + + function check_relation(node) { + switch (node.arity) { + case 'prefix': + switch (node.id) { + case '{': + case '[': + node.warn('unexpected_a'); + break; + case '!': + node.warn('confusing_a'); + break; + } + break; + case 'function': + case 'regexp': + node.warn('unexpected_a'); + break; + default: + if (node.id === 'NaN') { + node.warn('isNaN'); + } else if (node.relation) { + node.warn('weird_relation'); + } + } + return node; + } + + + function relation(s, eqeq) { + var x = infix(s, 100, function (left, that) { + check_relation(left); + if (eqeq && !option.eqeq) { + that.warn('expected_a_b', eqeq, that.id); + } + var right = expression(100); + if (are_similar(left, right) || + ((left.id === '(string)' || left.id === '(number)') && + (right.id === '(string)' || right.id === '(number)'))) { + that.warn('weird_relation'); + } else if (left.id === 'typeof') { + if (right.id !== '(string)') { + right.warn("expected_string_a", artifact(right)); + } else if (right.string === 'undefined' || + right.string === 'null') { + left.warn("unexpected_typeof_a", right.string); + } + } else if (right.id === 'typeof') { + if (left.id !== '(string)') { + left.warn("expected_string_a", artifact(left)); + } else if (left.string === 'undefined' || + left.string === 'null') { + right.warn("unexpected_typeof_a", left.string); + } + } + that.first = left; + that.second = check_relation(right); + return that; + }); + x.relation = true; + return x; + } + + function lvalue(that, s) { + var master; + if (that.identifier) { + master = scope[that.string]; + if (master) { + if (scope[that.string].writeable !== true) { + that.warn('read_only'); + } + master.used -= 1; + if (s === '=') { + master.init = true; + } + } else if (that.reserved) { + that.warn('expected_identifier_a_reserved'); + } + } else if (that.id === '.' || that.id === '[') { + if (!that.first || that.first.string === 'arguments') { + that.warn('bad_assignment'); + } + } else { + that.warn('bad_assignment'); + } + } + + + function assignop(s, op) { + var x = infix(s, 20, function (left, that) { + var next; + that.first = left; + lvalue(left, s); + that.second = expression(20); + if (that.id === '=' && are_similar(that.first, that.second)) { + that.warn('weird_assignment'); + } + next = that; + while (next_token.id === '=') { + lvalue(next.second, '='); + next_token.first = next.second; + next.second = next_token; + next = next_token; + advance('='); + next.second = expression(20); + } + return that; + }); + x.assign = true; + if (op) { + if (syntax[op].bitwise) { + x.bitwise = true; + } + } + return x; + } + + + function bitwise(s, p) { + var x = infix(s, p, 'number'); + x.bitwise = true; + return x; + } + + + function suffix(s) { + var x = symbol(s, 150); + x.led = function (left) { + no_space_only(prev_token, token); + if (!option.plusplus) { + this.warn('unexpected_a'); + } else if ((!left.identifier || left.reserved) && + left.id !== '.' && left.id !== '[') { + this.warn('bad_operand'); + } + this.first = left; + this.arity = 'suffix'; + return this; + }; + return x; + } + + + function optional_identifier(variable) { + if (next_token.identifier) { + advance(); + if (token.reserved && variable) { + token.warn('expected_identifier_a_reserved'); + } + return token.string; + } + } + + + function identifier(variable) { + var i = optional_identifier(variable); + if (!i) { + next_token.stop(token.id === 'function' && next_token.id === '(' + ? 'name_function' + : 'expected_identifier_a'); + } + return i; + } + + + function statement() { + + var label, preamble, the_statement; + +// We don't like the empty statement. + + if (next_token.id === ';') { + next_token.warn('unexpected_a'); + semicolon(); + return; + } + +// Is this a labeled statement? + + if (next_token.identifier && !next_token.reserved && peek().id === ':') { + edge('label'); + label = next_token; + advance(); + advance(':'); + define('label', label); + if (next_token.labeled !== true || funct === global_funct) { + label.stop('unexpected_label_a'); + } else if (jx.test(label.string + ':')) { + label.warn('url'); + } + next_token.label = label; + label.init = true; + label.statement = next_token; + } + +// Parse the statement. + + preamble = next_token; + if (token.id !== 'else') { + edge(); + } + step_in('statement'); + the_statement = expression(0, true); + if (the_statement) { + +// Look for the final semicolon. + + if (the_statement.arity === 'statement') { + if (the_statement.id === 'switch' || + (the_statement.block && the_statement.id !== 'do')) { + spaces(); + } else { + semicolon(); + } + } else { + +// If this is an expression statement, determine if it is acceptable. +// We do not like +// new Blah; +// statements. If it is to be used at all, new should only be used to make +// objects, not side effects. The expression statements we do like do +// assignment or invocation or delete. + + if (the_statement.id === '(') { + if (the_statement.first.id === 'new') { + next_token.warn('bad_new'); + } + } else if (the_statement.id === '++' || + the_statement.id === '--') { + lvalue(the_statement.first); + } else if (!the_statement.assign && + the_statement.id !== 'delete') { + if (!option.closure || !preamble.comments) { + preamble.warn('assignment_function_expression'); + } + } + semicolon(); + } + } + step_out(); + if (label) { + label.dead = true; + } + return the_statement; + } + + + function statements() { + var array = [], disruptor, the_statement; + +// A disrupt statement may not be followed by any other statement. +// If the last statement is disrupt, then the sequence is disrupt. + + while (next_token.postscript !== true) { + if (next_token.id === ';') { + next_token.warn('unexpected_a'); + semicolon(); + } else { + if (next_token.string === 'use strict') { + if ((!node_js) || funct !== global_funct || array.length > 0) { + next_token.warn('function_strict'); + } + use_strict(); + } + if (disruptor) { + next_token.warn('unreachable_a_b', next_token.string, + disruptor.string); + disruptor = null; + } + the_statement = statement(); + if (the_statement) { + array.push(the_statement); + if (the_statement.disrupt) { + disruptor = the_statement; + array.disrupt = true; + } + } + } + } + return array; + } + + + function block(kind) { + +// A block is a sequence of statements wrapped in braces. + + var array, + curly = next_token, + old_block_var = block_var, + old_in_block = in_block, + old_strict_mode = strict_mode; + + in_block = kind !== 'function' && kind !== 'try' && kind !== 'catch'; + block_var = []; + if (curly.id === '{') { + spaces(); + advance('{'); + step_in(); + if (kind === 'function' && !use_strict() && !old_strict_mode && + !option.sloppy && funct.level === 1) { + next_token.warn('missing_use_strict'); + } + array = statements(); + strict_mode = old_strict_mode; + step_out('}', curly); + } else if (in_block) { + curly.stop('expected_a_b', '{', artifact()); + } else { + curly.warn('expected_a_b', '{', artifact()); + array = [statement()]; + array.disrupt = array[0].disrupt; + } + if (kind !== 'catch' && array.length === 0 && !option.debug) { + curly.warn('empty_block'); + } + block_var.forEach(function (name) { + scope[name].dead = true; + }); + block_var = old_block_var; + in_block = old_in_block; + return array; + } + + + function tally_property(name) { + if (option.properties && typeof property[name] !== 'number') { + token.warn('unexpected_property_a', name); + } + if (property[name]) { + property[name] += 1; + } else { + property[name] = 1; + } + } + + +// ECMAScript parser + + (function () { + var x = symbol('(identifier)'); + x.nud = function () { + var name = this.string, + master = scope[name], + writeable; + +// If the master is not in scope, then we may have an undeclared variable. +// Check the predefined list. If it was predefined, create the global +// variable. + + if (!master) { + writeable = predefined[name]; + if (typeof writeable === 'boolean') { + global_scope[name] = master = { + dead: false, + function: global_funct, + kind: 'var', + string: name, + writeable: writeable + }; + +// But if the variable is not in scope, and is not predefined, and if we are not +// in the global scope, then we have an undefined variable error. + + } else { + token.warn('used_before_a'); + } + } else { + this.master = master; + } + +// Annotate uses that cross scope boundaries. + + if (master) { + if (master.kind === 'label') { + this.warn('a_label'); + } else { + if (master.dead === true || master.dead === funct) { + this.warn('a_scope'); + } + master.used += 1; + if (master.function !== funct) { + if (master.function === global_funct) { + funct.global.push(name); + } else { + master.function.closure.push(name); + funct.outer.push(name); + } + } + } + } + return this; + }; + x.identifier = true; + }()); + + +// Build the syntax table by declaring the syntactic elements. + + type('(array)', 'array'); + type('(function)', 'function'); + type('(number)', 'number', return_this); + type('(object)', 'object'); + type('(string)', 'string', return_this); + type('(boolean)', 'boolean', return_this); + type('(regexp)', 'regexp', return_this); + + ultimate('(begin)'); + ultimate('(end)'); + ultimate('(error)'); + postscript(symbol('}')); + symbol(')'); + symbol(']'); + postscript(symbol('"')); + postscript(symbol('\'')); + symbol(';'); + symbol(':'); + symbol(','); + symbol('#'); + symbol('@'); + symbol('*/'); + postscript(reserve('case')); + reserve('catch'); + postscript(reserve('default')); + reserve('else'); + reserve('finally'); + + reservevar('arguments', function (x) { + if (strict_mode && funct === global_funct) { + x.warn('strict'); + } + funct.arguments = true; + }); + reservevar('eval'); + constant('false', 'boolean'); + constant('Infinity', 'number'); + constant('NaN', 'number'); + constant('null', ''); + reservevar('this', function (x) { + if (strict_mode && funct.statement && funct.name.charAt(0) > 'Z') { + x.warn('strict'); + } + }); + constant('true', 'boolean'); + constant('undefined', ''); + + infix('?', 30, function (left, that) { + step_in('?'); + that.first = expected_condition(expected_relation(left)); + that.second = expression(0); + spaces(); + step_out(); + var colon = next_token; + advance(':'); + step_in(':'); + spaces(); + that.third = expression(10); + that.arity = 'ternary'; + if (are_similar(that.second, that.third)) { + colon.warn('weird_ternary'); + } else if (are_similar(that.first, that.second)) { + that.warn('use_or'); + } + step_out(); + return that; + }); + + infix('||', 40, function (left, that) { + function paren_check(that) { + if (that.id === '&&' && !that.paren) { + that.warn('and'); + } + return that; + } + + that.first = paren_check(expected_condition(expected_relation(left))); + that.second = paren_check(expected_relation(expression(40))); + if (are_similar(that.first, that.second)) { + that.warn('weird_condition'); + } + return that; + }); + + infix('&&', 50, function (left, that) { + that.first = expected_condition(expected_relation(left)); + that.second = expected_relation(expression(50)); + if (are_similar(that.first, that.second)) { + that.warn('weird_condition'); + } + return that; + }); + + prefix('void', function (that) { + that.first = expression(0); + that.warn('expected_a_b', 'undefined', 'void'); + return that; + }); + + bitwise('|', 70); + bitwise('^', 80); + bitwise('&', 90); + + relation('==', '==='); + relation('==='); + relation('!=', '!=='); + relation('!=='); + relation('<'); + relation('>'); + relation('<='); + relation('>='); + + bitwise('<<', 120); + bitwise('>>', 120); + bitwise('>>>', 120); + + infix('in', 120, function (left, that) { + that.warn('infix_in'); + that.left = left; + that.right = expression(130); + return that; + }); + infix('instanceof', 120); + infix('+', 130, function (left, that) { + if (left.id === '(number)') { + if (left.number === 0) { + left.warn('unexpected_a', '0'); + } + } else if (left.id === '(string)') { + if (left.string === '') { + left.warn('expected_a_b', 'String', '\'\''); + } + } + var right = expression(130); + if (right.id === '(number)') { + if (right.number === 0) { + right.warn('unexpected_a', '0'); + } + } else if (right.id === '(string)') { + if (right.string === '') { + right.warn('expected_a_b', 'String', '\'\''); + } + } + if (left.id === right.id) { + if (left.id === '(string)' || left.id === '(number)') { + if (left.id === '(string)') { + left.string += right.string; + if (jx.test(left.string)) { + left.warn('url'); + } + } else { + left.number += right.number; + } + left.thru = right.thru; + return left; + } + } + that.first = left; + that.second = right; + return that; + }); + prefix('+'); + prefix('+++', function () { + token.warn('confusing_a'); + this.first = expression(150); + this.arity = 'prefix'; + return this; + }); + infix('+++', 130, function (left) { + token.warn('confusing_a'); + this.first = left; + this.second = expression(130); + return this; + }); + infix('-', 130, function (left, that) { + if ((left.id === '(number)' && left.number === 0) || left.id === '(string)') { + left.warn('unexpected_a'); + } + var right = expression(130); + if ((right.id === '(number)' && right.number === 0) || right.id === '(string)') { + right.warn('unexpected_a'); + } + if (left.id === right.id && left.id === '(number)') { + left.number -= right.number; + left.thru = right.thru; + return left; + } + that.first = left; + that.second = right; + return that; + }); + prefix('-'); + prefix('---', function () { + token.warn('confusing_a'); + this.first = expression(150); + this.arity = 'prefix'; + return this; + }); + infix('---', 130, function (left) { + token.warn('confusing_a'); + this.first = left; + this.second = expression(130); + return this; + }); + infix('*', 140, function (left, that) { + if ((left.id === '(number)' && (left.number === 0 || left.number === 1)) || left.id === '(string)') { + left.warn('unexpected_a'); + } + var right = expression(140); + if ((right.id === '(number)' && (right.number === 0 || right.number === 1)) || right.id === '(string)') { + right.warn('unexpected_a'); + } + if (left.id === right.id && left.id === '(number)') { + left.number *= right.number; + left.thru = right.thru; + return left; + } + that.first = left; + that.second = right; + return that; + }); + infix('/', 140, function (left, that) { + if ((left.id === '(number)' && left.number === 0) || left.id === '(string)') { + left.warn('unexpected_a'); + } + var right = expression(140); + if ((right.id === '(number)' && (right.number === 0 || right.number === 1)) || right.id === '(string)') { + right.warn('unexpected_a'); + } + if (left.id === right.id && left.id === '(number)') { + left.number /= right.number; + left.thru = right.thru; + return left; + } + that.first = left; + that.second = right; + return that; + }); + infix('%', 140, function (left, that) { + if ((left.id === '(number)' && (left.number === 0 || left.number === 1)) || left.id === '(string)') { + left.warn('unexpected_a'); + } + var right = expression(140); + if ((right.id === '(number)' && right.number === 0) || right.id === '(string)') { + right.warn('unexpected_a'); + } + if (left.id === right.id && left.id === '(number)') { + left.number %= right.number; + left.thru = right.thru; + return left; + } + that.first = left; + that.second = right; + return that; + }); + + suffix('++'); + prefix('++'); + + suffix('--'); + prefix('--'); + prefix('delete', function (that) { + one_space(); + var p = expression(0); + if (!p || (p.id !== '.' && p.id !== '[')) { + next_token.warn('deleted'); + } + that.first = p; + return that; + }); + + + prefix('~', function (that) { + no_space_only(); + if (!option.bitwise) { + that.warn('unexpected_a'); + } + that.first = expression(150); + return that; + }); + function banger(that) { + no_space_only(); + that.first = expected_condition(expression(150)); + if (bang[that.first.id] === that || that.first.assign) { + that.warn('confusing_a'); + } + return that; + } + prefix('!', banger); + prefix('!!', banger); + prefix('typeof'); + prefix('new', function (that) { + one_space(); + var c = expression(160), n, p, v; + that.first = c; + if (c.id !== 'function') { + if (c.identifier) { + switch (c.string) { + case 'Object': + token.warn('use_object'); + break; + case 'Array': + if (next_token.id === '(') { + p = next_token; + p.first = this; + advance('('); + if (next_token.id !== ')') { + n = expression(0); + p.second = [n]; + if (n.id === '(string)' || next_token.id === ',') { + p.warn('use_array'); + } + while (next_token.id === ',') { + advance(','); + p.second.push(expression(0)); + } + } else { + token.warn('use_array'); + } + advance(')', p); + return p; + } + token.warn('use_array'); + break; + case 'Number': + case 'String': + case 'Boolean': + case 'Math': + case 'JSON': + c.warn('not_a_constructor'); + break; + case 'Function': + if (!option.evil) { + next_token.warn('function_eval'); + } + break; + case 'Date': + case 'RegExp': + case 'this': + break; + default: + if (c.id !== 'function') { + v = c.string.charAt(0); + if (!option.newcap && (v < 'A' || v > 'Z')) { + token.warn('constructor_name_a'); + } + } + } + } else { + if (c.id !== '.' && c.id !== '[' && c.id !== '(') { + token.warn('bad_constructor'); + } + } + } else { + that.warn('weird_new'); + } + if (next_token.id !== '(') { + next_token.warn('missing_a', '()'); + } + return that; + }); + + infix('(', 160, function (left, that) { + var e, p; + if (indent && indent.mode === 'expression') { + no_space(prev_token, token); + } else { + no_space_only(prev_token, token); + } + if (!left.immed && left.id === 'function') { + next_token.warn('wrap_immediate'); + } + p = []; + if (left.identifier) { + if (left.string.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) { + if (left.string !== 'Number' && left.string !== 'String' && + left.string !== 'Boolean' && left.string !== 'Date') { + if (left.string === 'Math') { + left.warn('not_a_function'); + } else if (left.string === 'Object') { + token.warn('use_object'); + } else if (left.string === 'Array' || !option.newcap) { + left.warn('missing_a', 'new'); + } + } + } else if (left.string === 'JSON') { + left.warn('not_a_function'); + } + } else if (left.id === '.') { + if (left.second.string === 'split' && + left.first.id === '(string)') { + left.second.warn('use_array'); + } + } + step_in(); + if (next_token.id !== ')') { + no_space(); + for (;;) { + edge(); + e = expression(10); + if (left.string === 'Boolean' && (e.id === '!' || e.id === '~')) { + e.warn('weird_condition'); + } + p.push(e); + if (next_token.id !== ',') { + break; + } + comma(); + } + } + no_space(); + step_out(')', that); + if (typeof left === 'object') { + if (left.string === 'parseInt' && p.length === 1) { + left.warn('radix'); + } else if (left.string === 'String' && p.length >= 1 && p[0].id === '(string)') { + left.warn('unexpected_a'); + } + if (!option.evil) { + if (left.string === 'eval' || left.string === 'Function' || + left.string === 'execScript') { + left.warn('evil'); + } else if (p[0] && p[0].id === '(string)' && + (left.string === 'setTimeout' || + left.string === 'setInterval')) { + left.warn('implied_evil'); + } + } + if (!left.identifier && left.id !== '.' && left.id !== '[' && + left.id !== '(' && left.id !== '&&' && left.id !== '||' && + left.id !== '?') { + left.warn('bad_invocation'); + } + if (left.id === '.') { + if (p.length > 0 && + left.first && left.first.first && + are_similar(p[0], left.first.first)) { + if (left.second.string === 'call' || + (left.second.string === 'apply' && (p.length === 1 || + (p[1].arity === 'prefix' && p[1].id === '[')))) { + left.second.warn('unexpected_a'); + } + } + if (left.second.string === 'toString') { + if (left.first.id === '(string)' || left.first.id === '(number)') { + left.second.warn('unexpected_a'); + } + } + } + } + that.first = left; + that.second = p; + return that; + }, true); + + prefix('(', function (that) { + step_in('expression'); + no_space(); + edge(); + if (next_token.id === 'function') { + next_token.immed = true; + } + var value = expression(0); + value.paren = true; + no_space(); + step_out(')', that); + if (value.id === 'function') { + switch (next_token.id) { + case '(': + next_token.warn('move_invocation'); + break; + case '.': + case '[': + next_token.warn('unexpected_a'); + break; + default: + that.warn('bad_wrap'); + } + } else if (!value.arity) { + if (!option.closure || !that.comments) { + that.warn('unexpected_a'); + } + } + return value; + }); + + infix('.', 170, function (left, that) { + no_space(prev_token, token); + no_space(); + var name = identifier(); + if (typeof name === 'string') { + tally_property(name); + } + that.first = left; + that.second = token; + if (left && left.string === 'arguments' && + (name === 'callee' || name === 'caller')) { + left.warn('avoid_a', 'arguments.' + name); + } else if (!option.evil && left && left.string === 'document' && + (name === 'write' || name === 'writeln')) { + left.warn('write_is_wrong'); + } else if (!option.stupid && syx.test(name)) { + token.warn('sync_a'); + } else if (left && left.id === '{') { + that.warn('unexpected_a'); + } + if (!option.evil && (name === 'eval' || name === 'execScript')) { + next_token.warn('evil'); + } + return that; + }, true); + + infix('[', 170, function (left, that) { + var e, s; + no_space_only(prev_token, token); + no_space(); + step_in(); + edge(); + e = expression(0); + switch (e.id) { + case '(number)': + if (e.id === '(number)' && left.id === 'arguments') { + left.warn('use_param'); + } + break; + case '(string)': + if (!option.evil && + (e.string === 'eval' || e.string === 'execScript')) { + e.warn('evil'); + } else if (!option.sub && ix.test(e.string)) { + s = syntax[e.string]; + if (!s || !s.reserved) { + e.warn('subscript'); + } + } + tally_property(e.string); + break; + } + if (left && (left.id === '{' || (left.id === '[' && left.arity === 'prefix'))) { + that.warn('unexpected_a'); + } + step_out(']', that); + no_space(prev_token, token); + that.first = left; + that.second = e; + return that; + }, true); + + prefix('[', function (that) { + that.first = []; + step_in('array'); + while (next_token.id !== '(end)') { + while (next_token.id === ',') { + next_token.warn('unexpected_a'); + advance(','); + } + if (next_token.id === ']') { + break; + } + indent.wrap = false; + edge(); + that.first.push(expression(10)); + if (next_token.id === ',') { + comma(); + if (next_token.id === ']') { + token.warn('unexpected_a'); + break; + } + } else { + break; + } + } + step_out(']', that); + return that; + }, 170); + + + function property_name() { + var id = optional_identifier(); + if (!id) { + if (next_token.id === '(string)') { + id = next_token.string; + advance(); + } else if (next_token.id === '(number)') { + id = next_token.number.toString(); + advance(); + } + } + return id; + } + + + + assignop('='); + assignop('+=', '+'); + assignop('-=', '-'); + assignop('*=', '*'); + assignop('/=', '/').nud = function () { + next_token.stop('slash_equal'); + }; + assignop('%=', '%'); + assignop('&=', '&'); + assignop('|=', '|'); + assignop('^=', '^'); + assignop('<<=', '<<'); + assignop('>>=', '>>'); + assignop('>>>=', '>>>'); + + function function_parameters() { + var id, parameters = [], paren = next_token; + advance('('); + token.function = funct; + step_in(); + no_space(); + if (next_token.id !== ')') { + for (;;) { + edge(); + id = identifier(); + if (token.reserved) { + token.warn('expected_identifier_a_reserved'); + } + define('parameter', token); + parameters.push(id); + token.init = true; + token.writeable = true; + if (next_token.id !== ',') { + break; + } + comma(); + } + } + no_space(); + step_out(')', paren); + return parameters; + } + + function do_function(func, name) { + var old_funct = funct, + old_option = option, + old_scope = scope; + scope = Object.create(old_scope); + funct = { + closure: [], + global: [], + level: old_funct.level + 1, + line: next_token.line, + loopage: 0, + name: name || '\'' + (anonname || '').replace(nx, sanitize) + '\'', + outer: [], + scope: scope + }; + funct.parameter = function_parameters(); + func.function = funct; + option = Object.create(old_option); + functions.push(funct); + if (name) { + func.name = name; + func.string = name; + define('function', func); + func.init = true; + func.used += 1; + } + func.writeable = false; + one_space(); + func.block = block('function'); + Object.keys(scope).forEach(function (name) { + var master = scope[name]; + if (!master.used && master.kind !== 'exception' && + (master.kind !== 'parameter' || !option.unparam)) { + master.warn('unused_a'); + } else if (!master.init) { + master.warn('uninitialized_a'); + } + }); + funct = old_funct; + option = old_option; + scope = old_scope; + } + + prefix('{', function (that) { + var get, i, j, name, set, seen = Object.create(null); + that.first = []; + step_in(); + while (next_token.id !== '}') { + indent.wrap = false; + +// JSLint recognizes the ES5 extension for get/set in object literals, +// but requires that they be used in pairs. + + edge(); + if (next_token.string === 'get' && peek().id !== ':') { + get = next_token; + advance('get'); + one_space_only(); + name = next_token; + i = property_name(); + if (!i) { + next_token.stop('missing_property'); + } + get.string = ''; + do_function(get); + if (funct.loopage) { + get.warn('function_loop'); + } + if (get.function.parameter.length) { + get.warn('parameter_a_get_b', get.function.parameter[0], i); + } + comma(); + set = next_token; + spaces(); + edge(); + advance('set'); + set.string = ''; + one_space_only(); + j = property_name(); + if (i !== j) { + token.stop('expected_a_b', i, j || next_token.string); + } + do_function(set); + if (set.block.length === 0) { + token.warn('missing_a', 'throw'); + } + if (set.function.parameter.length === 0) { + set.stop('parameter_set_a', 'value'); + } else if (set.function.parameter[0] !== 'value') { + set.stop('expected_a_b', 'value', + set.function.parameter[0]); + } + name.first = [get, set]; + } else { + name = next_token; + i = property_name(); + if (typeof i !== 'string') { + next_token.stop('missing_property'); + } + advance(':'); + spaces(); + name.first = expression(10); + } + that.first.push(name); + if (seen[i] === true) { + next_token.warn('duplicate_a', i); + } + seen[i] = true; + tally_property(i); + if (next_token.id !== ',') { + break; + } + for (;;) { + comma(); + if (next_token.id !== ',') { + break; + } + next_token.warn('unexpected_a'); + } + if (next_token.id === '}') { + token.warn('unexpected_a'); + } + } + step_out('}', that); + return that; + }); + + stmt('{', function () { + next_token.warn('statement_block'); + this.arity = 'statement'; + this.block = statements(); + this.disrupt = this.block.disrupt; + advance('}', this); + return this; + }); + + stmt('/*global', directive); + stmt('/*globals', directive); + stmt('/*jslint', directive); + stmt('/*member', directive); + stmt('/*members', directive); + stmt('/*property', directive); + stmt('/*properties', directive); + + stmt('var', function () { + +// JavaScript does not have block scope. It only has function scope. So, +// declaring a variable in a block can have unexpected consequences. + +// var.first will contain an array, the array containing name tokens +// and assignment tokens. + + var assign, id, name; + + if (funct.loopage) { + next_token.warn('var_loop'); + } else if (funct.varstatement && !option.vars) { + next_token.warn('combine_var'); + } + if (funct !== global_funct) { + funct.varstatement = true; + } + this.arity = 'statement'; + this.first = []; + step_in('var'); + for (;;) { + name = next_token; + id = identifier(true); + define('var', name); + name.dead = funct; + if (next_token.id === '=') { + if (funct === global_funct && !name.writeable) { + name.warn('read_only'); + } + assign = next_token; + assign.first = name; + spaces(); + advance('='); + spaces(); + if (next_token.id === 'undefined') { + token.warn('unnecessary_initialize', id); + } + if (peek(0).id === '=' && next_token.identifier) { + next_token.stop('var_a_not'); + } + assign.second = expression(0); + assign.arity = 'infix'; + name.init = true; + this.first.push(assign); + } else { + this.first.push(name); + } + name.dead = false; + name.writeable = true; + if (next_token.id !== ',') { + break; + } + comma(); + indent.wrap = false; + if (var_mode && next_token.line === token.line && + this.first.length === 1) { + var_mode = null; + indent.open = false; + indent.at -= option.indent; + } + spaces(); + edge(); + } + var_mode = null; + step_out(); + return this; + }); + + stmt('function', function () { + one_space(); + if (in_block) { + token.warn('function_block'); + } + var name = next_token, + id = identifier(true); + define('var', name); + if (!name.writeable) { + name.warn('read_only'); + } + name.init = true; + name.statement = true; + no_space(); + this.arity = 'statement'; + do_function(this, id); + if (next_token.id === '(' && next_token.line === token.line) { + next_token.stop('function_statement'); + } + return this; + }); + + prefix('function', function (that) { + var id = optional_identifier(true), name; + if (id) { + name = token; + no_space(); + } else { + id = ''; + one_space(); + } + do_function(that, id); + if (name) { + name.function = that.function; + } + if (funct.loopage) { + that.warn('function_loop'); + } + switch (next_token.id) { + case ';': + case '(': + case ')': + case ',': + case ']': + case '}': + case ':': + case '(end)': + break; + case '.': + if (peek().string !== 'bind' || peek(1).id !== '(') { + next_token.warn('unexpected_a'); + } + break; + default: + next_token.stop('unexpected_a'); + } + that.arity = 'function'; + return that; + }); + + stmt('if', function () { + var paren = next_token; + one_space(); + advance('('); + step_in('control'); + no_space(); + edge(); + this.arity = 'statement'; + this.first = expected_condition(expected_relation(expression(0))); + no_space(); + step_out(')', paren); + one_space(); + this.block = block('if'); + if (next_token.id === 'else') { + if (this.block.disrupt) { + next_token.warn(this.elif ? 'use_nested_if' : 'unnecessary_else'); + } + one_space(); + advance('else'); + one_space(); + if (next_token.id === 'if') { + next_token.elif = true; + this.else = statement(true); + } else { + this.else = block('else'); + } + if (this.else.disrupt && this.block.disrupt) { + this.disrupt = true; + } + } + return this; + }); + + stmt('try', function () { + +// try.first The catch variable +// try.second The catch clause +// try.third The finally clause +// try.block The try block + + var exception_variable, paren; + one_space(); + this.arity = 'statement'; + this.block = block('try'); + if (next_token.id === 'catch') { + one_space(); + advance('catch'); + one_space(); + paren = next_token; + advance('('); + step_in('control'); + no_space(); + edge(); + exception_variable = next_token; + this.first = identifier(); + define('exception', exception_variable); + exception_variable.init = true; + no_space(); + step_out(')', paren); + one_space(); + this.second = block('catch'); + if (this.second.length) { + if (this.first === 'ignore') { + exception_variable.warn('unexpected_a'); + } + } else { + if (this.first !== 'ignore') { + exception_variable.warn('expected_a_b', 'ignore', + exception_variable.string); + } + } + exception_variable.dead = true; + } + if (next_token.id === 'finally') { + one_space(); + advance('finally'); + one_space(); + this.third = block('finally'); + } else if (!this.second) { + next_token.stop('expected_a_b', 'catch', artifact()); + } + return this; + }); + + labeled_stmt('while', function () { + one_space(); + var paren = next_token; + funct.loopage += 1; + advance('('); + step_in('control'); + no_space(); + edge(); + this.arity = 'statement'; + this.first = expected_relation(expression(0)); + if (this.first.id !== 'true') { + expected_condition(this.first, 'unexpected_a'); + } + no_space(); + step_out(')', paren); + one_space(); + this.block = block('while'); + if (this.block.disrupt) { + prev_token.warn('strange_loop'); + } + funct.loopage -= 1; + return this; + }); + + reserve('with'); + + labeled_stmt('switch', function () { + +// switch.first the switch expression +// switch.second the array of cases. A case is 'case' or 'default' token: +// case.first the array of case expressions +// case.second the array of statements +// If all of the arrays of statements are disrupt, then the switch is disrupt. + + var cases = [], + old_in_block = in_block, + particular, + that = token, + the_case = next_token; + + function find_duplicate_case(value) { + if (are_similar(particular, value)) { + value.warn('duplicate_a'); + } + } + + one_space(); + advance('('); + no_space(); + step_in(); + this.arity = 'statement'; + this.first = expected_condition(expected_relation(expression(0))); + no_space(); + step_out(')', the_case); + one_space(); + advance('{'); + step_in(); + in_block = true; + this.second = []; + if (that.from !== next_token.from && !option.white) { + next_token.warn('expected_a_at_b_c', next_token.string, that.from, next_token.from); + } + while (next_token.id === 'case') { + the_case = next_token; + the_case.first = []; + the_case.arity = 'case'; + for (;;) { + spaces(); + edge('case'); + advance('case'); + one_space(); + particular = expression(0); + cases.forEach(find_duplicate_case); + cases.push(particular); + the_case.first.push(particular); + if (particular.id === 'NaN') { + particular.warn('unexpected_a'); + } + no_space_only(); + advance(':'); + if (next_token.id !== 'case') { + break; + } + } + spaces(); + the_case.second = statements(); + if (the_case.second && the_case.second.length > 0) { + if (!the_case.second[the_case.second.length - 1].disrupt) { + next_token.warn('missing_a_after_b', 'break', 'case'); + } + } else { + next_token.warn('empty_case'); + } + this.second.push(the_case); + } + if (this.second.length === 0) { + next_token.warn('missing_a', 'case'); + } + if (next_token.id === 'default') { + spaces(); + the_case = next_token; + the_case.arity = 'case'; + edge('case'); + advance('default'); + no_space_only(); + advance(':'); + spaces(); + the_case.second = statements(); + if (the_case.second && the_case.second.length > 0) { + this.disrupt = the_case.second[the_case.second.length - 1].disrupt; + } else { + the_case.warn('empty_case'); + } + this.second.push(the_case); + } + if (this.break) { + this.disrupt = false; + } + spaces(); + step_out('}', this); + in_block = old_in_block; + return this; + }); + + stmt('debugger', function () { + if (!option.debug) { + this.warn('unexpected_a'); + } + this.arity = 'statement'; + return this; + }); + + labeled_stmt('do', function () { + funct.loopage += 1; + one_space(); + this.arity = 'statement'; + this.block = block('do'); + if (this.block.disrupt) { + prev_token.warn('strange_loop'); + } + one_space(); + advance('while'); + var paren = next_token; + one_space(); + advance('('); + step_in(); + no_space(); + edge(); + this.first = expected_condition(expected_relation(expression(0)), 'unexpected_a'); + no_space(); + step_out(')', paren); + funct.loopage -= 1; + return this; + }); + + labeled_stmt('for', function () { + + var blok, filter, master, ok = false, paren = next_token, value; + this.arity = 'statement'; + funct.loopage += 1; + advance('('); + if (next_token.id === ';') { + no_space(); + advance(';'); + no_space(); + advance(';'); + no_space(); + advance(')'); + blok = block('for'); + } else { + step_in('control'); + spaces(this, paren); + no_space(); + if (next_token.id === 'var') { + next_token.stop('move_var'); + } + edge(); + if (peek(0).id === 'in') { + this.forin = true; + value = expression(1000); + master = value.master; + if (!master) { + value.stop('bad_in_a'); + } + if (master.kind !== 'var' || master.function !== funct || + !master.writeable || master.dead) { + value.warn('bad_in_a'); + } + master.init = true; + master.used -= 1; + this.first = value; + advance('in'); + this.second = expression(20); + step_out(')', paren); + blok = block('for'); + if (!option.forin) { + if (blok.length === 1 && typeof blok[0] === 'object') { + if (blok[0].id === 'if' && !blok[0].else) { + filter = blok[0].first; + while (filter.id === '&&') { + filter = filter.first; + } + switch (filter.id) { + case '===': + case '!==': + ok = filter.first.id === '[' + ? are_similar(filter.first.first, this.second) && + are_similar(filter.first.second, this.first) + : filter.first.id === 'typeof' && + filter.first.first.id === '[' && + are_similar(filter.first.first.first, this.second) && + are_similar(filter.first.first.second, this.first); + break; + case '(': + ok = filter.first.id === '.' && (( + are_similar(filter.first.first, this.second) && + filter.first.second.string === 'hasOwnProperty' && + are_similar(filter.second[0], this.first) + ) || ( + filter.first.first.id === '.' && + filter.first.first.first.first && + filter.first.first.first.first.string === 'Object' && + filter.first.first.first.id === '.' && + filter.first.first.first.second.string === 'prototype' && + filter.first.first.second.string === 'hasOwnProperty' && + filter.first.second.string === 'call' && + are_similar(filter.second[0], this.second) && + are_similar(filter.second[1], this.first) + )); + break; + } + } else if (blok[0].id === 'switch') { + ok = blok[0].id === 'switch' && + blok[0].first.id === 'typeof' && + blok[0].first.first.id === '[' && + are_similar(blok[0].first.first.first, this.second) && + are_similar(blok[0].first.first.second, this.first); + } + } + if (!ok) { + this.warn('for_if'); + } + } + } else { + edge(); + this.first = []; + for (;;) { + this.first.push(expression(0, 'for')); + if (next_token.id !== ',') { + break; + } + comma(); + } + semicolon(); + edge(); + this.second = expected_relation(expression(0)); + if (this.second.id !== 'true') { + expected_condition(this.second, 'unexpected_a'); + } + semicolon(token); + if (next_token.id === ';') { + next_token.stop('expected_a_b', ')', ';'); + } + this.third = []; + edge(); + for (;;) { + this.third.push(expression(0, 'for')); + if (next_token.id !== ',') { + break; + } + comma(); + } + no_space(); + step_out(')', paren); + one_space(); + blok = block('for'); + } + } + if (blok.disrupt) { + prev_token.warn('strange_loop'); + } + this.block = blok; + funct.loopage -= 1; + return this; + }); + + function optional_label(that) { + var label = next_token.string, + master; + that.arity = 'statement'; + if (!funct.breakage || (!option.continue && that.id === 'continue')) { + that.warn('unexpected_a'); + } else if (next_token.identifier && token.line === next_token.line) { + one_space_only(); + master = scope[label]; + if (!master || master.kind !== 'label') { + next_token.warn('not_a_label'); + } else if (master.dead || master.function !== funct) { + next_token.warn('not_a_scope'); + } else { + master.used += 1; + if (that.id === 'break') { + master.statement.break = true; + } + if (funct.breakage[funct.breakage.length - 1] === master.statement) { + next_token.warn('unexpected_a'); + } + } + that.first = next_token; + advance(); + } else { + if (that.id === 'break') { + funct.breakage[funct.breakage.length - 1].break = true; + } + } + return that; + + } + + disrupt_stmt('break', function () { + return optional_label(this); + }); + + disrupt_stmt('continue', function () { + return optional_label(this); + }); + + disrupt_stmt('return', function () { + if (funct === global_funct) { + this.warn('unexpected_a'); + } + this.arity = 'statement'; + if (next_token.id !== ';' && next_token.line === token.line) { + if (option.closure) { + spaces(); + } else { + one_space_only(); + } + if (next_token.id === '/' || next_token.id === '(regexp)') { + next_token.warn('wrap_regexp'); + } + this.first = expression(0); + if (this.first.assign) { + this.first.warn('unexpected_a'); + } + } + return this; + }); + + disrupt_stmt('throw', function () { + this.arity = 'statement'; + one_space_only(); + this.first = expression(20); + return this; + }); + + +// Superfluous reserved words + + reserve('class'); + reserve('const'); + reserve('enum'); + reserve('export'); + reserve('extends'); + reserve('import'); + reserve('super'); + +// Harmony reserved words + + reserve('implements'); + reserve('interface'); + reserve('let'); + reserve('package'); + reserve('private'); + reserve('protected'); + reserve('public'); + reserve('static'); + reserve('yield'); + + +// Parse JSON + + function json_value() { + + function json_object() { + var brace = next_token, object = Object.create(null); + advance('{'); + if (next_token.id !== '}') { + while (next_token.id !== '(end)') { + while (next_token.id === ',') { + next_token.warn('unexpected_a'); + advance(','); + } + if (next_token.id !== '(string)') { + next_token.warn('expected_string_a'); + } + if (object[next_token.string] === true) { + next_token.warn('duplicate_a'); + } else if (next_token.string === '__proto__') { + next_token.warn('dangling_a'); + } else { + object[next_token.string] = true; + } + advance(); + advance(':'); + json_value(); + if (next_token.id !== ',') { + break; + } + advance(','); + if (next_token.id === '}') { + token.warn('unexpected_a'); + break; + } + } + } + advance('}', brace); + } + + function json_array() { + var bracket = next_token; + advance('['); + if (next_token.id !== ']') { + while (next_token.id !== '(end)') { + while (next_token.id === ',') { + next_token.warn('unexpected_a'); + advance(','); + } + json_value(); + if (next_token.id !== ',') { + break; + } + advance(','); + if (next_token.id === ']') { + token.warn('unexpected_a'); + break; + } + } + } + advance(']', bracket); + } + + switch (next_token.id) { + case '{': + json_object(); + break; + case '[': + json_array(); + break; + case 'true': + case 'false': + case 'null': + case '(number)': + case '(string)': + advance(); + break; + case '-': + advance('-'); + no_space_only(); + advance('(number)'); + break; + default: + next_token.stop('unexpected_a'); + } + } + + +// The actual JSLINT function itself. + + itself = function JSLint(the_source, the_option) { + + var i, predef, tree; + itself.errors = []; + itself.tree = ''; + itself.properties = ''; + begin = prev_token = token = next_token = + Object.create(syntax['(begin)']); + tokens = []; + predefined = Object.create(null); + add_to_predefined(standard); + property = Object.create(null); + if (the_option) { + option = Object.create(the_option); + predef = option.predef; + if (predef) { + if (Array.isArray(predef)) { + for (i = 0; i < predef.length; i += 1) { + predefined[predef[i]] = true; + } + } else if (typeof predef === 'object') { + add_to_predefined(predef); + } + } + } else { + option = Object.create(null); + } + option.indent = +option.indent || 4; + option.maxerr = +option.maxerr || 50; + global_scope = scope = Object.create(null); + global_funct = funct = { + scope: scope, + loopage: 0, + level: 0 + }; + functions = [funct]; + block_var = []; + + comments = []; + comments_off = false; + in_block = false; + indent = null; + json_mode = false; + lookahead = []; + node_js = false; + prereg = true; + strict_mode = false; + var_mode = null; + warnings = 0; + lex.init(the_source); + + assume(); + + try { + advance(); + if (next_token.id === '(number)') { + next_token.stop('unexpected_a'); + } else { + switch (next_token.id) { + case '{': + case '[': + comments_off = true; + json_mode = true; + json_value(); + break; + default: + +// If the first token is a semicolon, ignore it. This is sometimes used when +// files are intended to be appended to files that may be sloppy. A sloppy +// file may be depending on semicolon insertion on its last line. + + step_in(1); + if (next_token.id === ';' && !node_js) { + next_token.edge = true; + advance(';'); + } + tree = statements(); + begin.first = tree; + itself.tree = begin; + if (tree.disrupt) { + prev_token.warn('weird_program'); + } + } + } + indent = null; + advance('(end)'); + itself.property = property; + } catch (e) { + if (e) { // ~~ + itself.errors.push({ + reason : e.message, + line : e.line || next_token.line, + character : e.character || next_token.from + }, null); + } + } + return itself.errors.length === 0; + }; + + function unique(array) { + array = array.sort(); + var i, length = 0, previous, value; + for (i = 0; i < array.length; i += 1) { + value = array[i]; + if (value !== previous) { + array[length] = value; + previous = value; + length += 1; + } + } + array.length = length; + return array; + } + +// Data summary. + + itself.data = function () { + var data = {functions: []}, + function_data, + i, + the_function, + the_scope; + data.errors = itself.errors; + data.json = json_mode; + data.global = unique(Object.keys(global_scope)); + + function selects(name) { + var kind = the_scope[name].kind; + switch (kind) { + case 'var': + case 'exception': + case 'label': + function_data[kind].push(name); + break; + } + } + + for (i = 1; i < functions.length; i += 1) { + the_function = functions[i]; + function_data = { + name: the_function.name, + line: the_function.line, + level: the_function.level, + parameter: the_function.parameter, + var: [], + exception: [], + closure: unique(the_function.closure), + outer: unique(the_function.outer), + global: unique(the_function.global), + label: [] + }; + the_scope = the_function.scope; + Object.keys(the_scope).forEach(selects); + function_data.var.sort(); + function_data.exception.sort(); + function_data.label.sort(); + data.functions.push(function_data); + } + data.tokens = tokens; + return data; + }; + + itself.error_report = function (data) { + var evidence, i, output = [], warning; + if (data.errors.length) { + if (data.json) { + output.push('JSON: bad.
    '); + } + for (i = 0; i < data.errors.length; i += 1) { + warning = data.errors[i]; + if (warning) { + evidence = warning.evidence || ''; + output.push(''); + if (isFinite(warning.line)) { + output.push('
    line ' + + String(warning.line) + + ' character ' + String(warning.character) + + '
    '); + } + output.push(warning.reason.entityify() + '
    '); + if (evidence) { + output.push('
    ' + evidence.entityify() + '
    '); + } + } + } + } + return output.join(''); + }; + + + itself.report = function (data) { + var dl, i, j, names, output = [], the_function; + + function detail(h, array) { + var comma_needed = false; + if (array.length) { + output.push("
    " + h + "
    "); + array.forEach(function (item) { + output.push((comma_needed ? ', ' : '') + item); + comma_needed = true; + }); + output.push("
    "); + } + } + + output.push('
    '); + if (data.global.length) { + detail('global', data.global); + dl = true; + } else if (data.json) { + if (!data.errors.length) { + output.push("
    JSON: good.
    "); + } + } else { + output.push("
    No new global variables introduced.
    "); + } + if (dl) { + output.push("
    "); + } else { + output[0] = ''; + } + + if (data.functions) { + for (i = 0; i < data.functions.length; i += 1) { + the_function = data.functions[i]; + names = []; + if (the_function.params) { + for (j = 0; j < the_function.params.length; j += 1) { + names[j] = the_function.params[j].string; + } + } + output.push('
    line ' + String(the_function.line) + + '
    ' + the_function.name.entityify()); + detail('parameter', the_function.parameter); + detail('variable', the_function.var); + detail('exception', the_function.exception); + detail('closure', the_function.closure); + detail('outer', the_function.outer); + detail('global', the_function.global); + detail('label', the_function.label); + output.push('
    '); + } + } + return output.join(''); + }; + + itself.properties_report = function (property) { + if (!property) { + return ''; + } + var i, + key, + keys = Object.keys(property).sort(), + mem = ' ', + name, + not_first = false, + output = ['/*properties']; + for (i = 0; i < keys.length; i += 1) { + key = keys[i]; + if (property[key] > 0) { + if (not_first) { + mem += ','; + } + name = ix.test(key) + ? key + : '\'' + key.replace(nx, sanitize) + '\''; + if (mem.length + name.length >= 80) { + output.push(mem); + mem = ' '; + } else { + mem += ' '; + } + mem += name; + not_first = true; + } + } + output.push(mem, '*/\n'); + return output.join('\n'); + }; + + itself.color = function (data) { + var from, + i = 1, + level, + line, + result = [], + thru, + data_token = data.tokens[0]; + while (data_token && data_token.id !== '(end)') { + from = data_token.from; + line = data_token.line; + thru = data_token.thru; + level = data_token.function.level; + do { + thru = data_token.thru; + data_token = data.tokens[i]; + i += 1; + } while (data_token && data_token.line === line && + data_token.from - thru < 5 && + level === data_token.function.level); + result.push({ + line: line, + level: level, + from: from, + thru: thru + }); + } + return result; + }; + + itself.jslint = itself; + + itself.edition = '2014-07-08'; + + return itself; +}()); +// json2.js +// 2017-06-12 +// Public Domain. +// NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + +// USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO +// NOT CONTROL. + +// This file creates a global JSON object containing two methods: stringify +// and parse. This file provides the ES5 JSON capability to ES3 systems. +// If a project might run on IE8 or earlier, then this file should be included. +// This file does nothing on ES5 systems. + +// JSON.stringify(value, replacer, space) +// value any JavaScript value, usually an object or array. +// replacer an optional parameter that determines how object +// values are stringified for objects. It can be a +// function or an array of strings. +// space an optional parameter that specifies the indentation +// of nested structures. If it is omitted, the text will +// be packed without extra whitespace. If it is a number, +// it will specify the number of spaces to indent at each +// level. If it is a string (such as "\t" or " "), +// it contains the characters used to indent at each level. +// This method produces a JSON text from a JavaScript value. +// When an object value is found, if the object contains a toJSON +// method, its toJSON method will be called and the result will be +// stringified. A toJSON method does not serialize: it returns the +// value represented by the name/value pair that should be serialized, +// or undefined if nothing should be serialized. The toJSON method +// will be passed the key associated with the value, and this will be +// bound to the value. + +// For example, this would serialize Dates as ISO strings. + +// Date.prototype.toJSON = function (key) { +// function f(n) { +// // Format integers to have at least two digits. +// return (n < 10) +// ? "0" + n +// : n; +// } +// return this.getUTCFullYear() + "-" + +// f(this.getUTCMonth() + 1) + "-" + +// f(this.getUTCDate()) + "T" + +// f(this.getUTCHours()) + ":" + +// f(this.getUTCMinutes()) + ":" + +// f(this.getUTCSeconds()) + "Z"; +// }; + +// You can provide an optional replacer method. It will be passed the +// key and value of each member, with this bound to the containing +// object. The value that is returned from your method will be +// serialized. If your method returns undefined, then the member will +// be excluded from the serialization. + +// If the replacer parameter is an array of strings, then it will be +// used to select the members to be serialized. It filters the results +// such that only members with keys listed in the replacer array are +// stringified. + +// Values that do not have JSON representations, such as undefined or +// functions, will not be serialized. Such values in objects will be +// dropped; in arrays they will be replaced with null. You can use +// a replacer function to replace those with JSON values. + +// JSON.stringify(undefined) returns undefined. + +// The optional space parameter produces a stringification of the +// value that is filled with line breaks and indentation to make it +// easier to read. + +// If the space parameter is a non-empty string, then that string will +// be used for indentation. If the space parameter is a number, then +// the indentation will be that many spaces. + +// Example: + +// text = JSON.stringify(["e", {pluribus: "unum"}]); +// // text is '["e",{"pluribus":"unum"}]' + +// text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t"); +// // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' + +// text = JSON.stringify([new Date()], function (key, value) { +// return this[key] instanceof Date +// ? "Date(" + this[key] + ")" +// : value; +// }); +// // text is '["Date(---current time---)"]' + +// JSON.parse(text, reviver) +// This method parses a JSON text to produce an object or array. +// It can throw a SyntaxError exception. + +// The optional reviver parameter is a function that can filter and +// transform the results. It receives each of the keys and values, +// and its return value is used instead of the original value. +// If it returns what it received, then the structure is not modified. +// If it returns undefined then the member is deleted. + +// Example: + +// // Parse the text. Values that look like ISO date strings will +// // be converted to Date objects. + +// myData = JSON.parse(text, function (key, value) { +// var a; +// if (typeof value === "string") { +// a = +// /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); +// if (a) { +// return new Date(Date.UTC( +// +a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6] +// )); +// } +// return value; +// } +// }); + +// myData = JSON.parse( +// "[\"Date(09/09/2001)\"]", +// function (key, value) { +// var d; +// if ( +// typeof value === "string" +// && value.slice(0, 5) === "Date(" +// && value.slice(-1) === ")" +// ) { +// d = new Date(value.slice(5, -1)); +// if (d) { +// return d; +// } +// } +// return value; +// } +// ); + +// This is a reference implementation. You are free to copy, modify, or +// redistribute. + +/*jslint + eval, for, this +*/ + +/*property + JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, + getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, + lastIndex, length, parse, prototype, push, replace, slice, stringify, + test, toJSON, toString, valueOf +*/ + + +// Create a JSON object only if one does not already exist. We create the +// methods in a closure to avoid creating global variables. + +if (typeof JSON !== "object") { + JSON = {}; +} + +(function () { + "use strict"; + + var rx_one = /^[\],:{}\s]*$/; + var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g; + var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g; + var rx_four = /(?:^|:|,)(?:\s*\[)+/g; + var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; + var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; + + function f(n) { + // Format integers to have at least two digits. + return (n < 10) + ? "0" + n + : n; + } + + function this_value() { + return this.valueOf(); + } + + if (typeof Date.prototype.toJSON !== "function") { + + Date.prototype.toJSON = function () { + + return isFinite(this.valueOf()) + ? ( + this.getUTCFullYear() + + "-" + + f(this.getUTCMonth() + 1) + + "-" + + f(this.getUTCDate()) + + "T" + + f(this.getUTCHours()) + + ":" + + f(this.getUTCMinutes()) + + ":" + + f(this.getUTCSeconds()) + + "Z" + ) + : null; + }; + + Boolean.prototype.toJSON = this_value; + Number.prototype.toJSON = this_value; + String.prototype.toJSON = this_value; + } + + var gap; + var indent; + var meta; + var rep; + + + function quote(string) { + +// If the string contains no control characters, no quote characters, and no +// backslash characters, then we can safely slap some quotes around it. +// Otherwise we must also replace the offending characters with safe escape +// sequences. + + rx_escapable.lastIndex = 0; + return rx_escapable.test(string) + ? "\"" + string.replace(rx_escapable, function (a) { + var c = meta[a]; + return typeof c === "string" + ? c + : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4); + }) + "\"" + : "\"" + string + "\""; + } + + + function str(key, holder) { + +// Produce a string from holder[key]. + + var i; // The loop counter. + var k; // The member key. + var v; // The member value. + var length; + var mind = gap; + var partial; + var value = holder[key]; + +// If the value has a toJSON method, call it to obtain a replacement value. + + if ( + value + && typeof value === "object" + && typeof value.toJSON === "function" + ) { + value = value.toJSON(key); + } + +// If we were called with a replacer function, then call the replacer to +// obtain a replacement value. + + if (typeof rep === "function") { + value = rep.call(holder, key, value); + } + +// What happens next depends on the value's type. + + switch (typeof value) { + case "string": + return quote(value); + + case "number": + +// JSON numbers must be finite. Encode non-finite numbers as null. + + return (isFinite(value)) + ? String(value) + : "null"; + + case "boolean": + case "null": + +// If the value is a boolean or null, convert it to a string. Note: +// typeof null does not produce "null". The case is included here in +// the remote chance that this gets fixed someday. + + return String(value); + +// If the type is "object", we might be dealing with an object or an array or +// null. + + case "object": + +// Due to a specification blunder in ECMAScript, typeof null is "object", +// so watch out for that case. + + if (!value) { + return "null"; + } + +// Make an array to hold the partial results of stringifying this object value. + + gap += indent; + partial = []; + +// Is the value an array? + + if (Object.prototype.toString.apply(value) === "[object Array]") { + +// The value is an array. Stringify every element. Use null as a placeholder +// for non-JSON values. + + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value) || "null"; + } + +// Join all of the elements together, separated with commas, and wrap them in +// brackets. + + v = partial.length === 0 + ? "[]" + : gap + ? ( + "[\n" + + gap + + partial.join(",\n" + gap) + + "\n" + + mind + + "]" + ) + : "[" + partial.join(",") + "]"; + gap = mind; + return v; + } + +// If the replacer is an array, use it to select the members to be stringified. + + if (rep && typeof rep === "object") { + length = rep.length; + for (i = 0; i < length; i += 1) { + if (typeof rep[i] === "string") { + k = rep[i]; + v = str(k, value); + if (v) { + partial.push(quote(k) + ( + (gap) + ? ": " + : ":" + ) + v); + } + } + } + } else { + +// Otherwise, iterate through all of the keys in the object. + + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + ( + (gap) + ? ": " + : ":" + ) + v); + } + } + } + } + +// Join all of the member texts together, separated with commas, +// and wrap them in braces. + + v = partial.length === 0 + ? "{}" + : gap + ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" + : "{" + partial.join(",") + "}"; + gap = mind; + return v; + } + } + +// If the JSON object does not yet have a stringify method, give it one. + + if (typeof JSON.stringify !== "function") { + meta = { // table of character substitutions + "\b": "\\b", + "\t": "\\t", + "\n": "\\n", + "\f": "\\f", + "\r": "\\r", + "\"": "\\\"", + "\\": "\\\\" + }; + JSON.stringify = function (value, replacer, space) { + +// The stringify method takes a value and an optional replacer, and an optional +// space parameter, and returns a JSON text. The replacer can be a function +// that can replace values, or an array of strings that will select the keys. +// A default replacer method can be provided. Use of the space parameter can +// produce text that is more easily readable. + + var i; + gap = ""; + indent = ""; + +// If the space parameter is a number, make an indent string containing that +// many spaces. + + if (typeof space === "number") { + for (i = 0; i < space; i += 1) { + indent += " "; + } + +// If the space parameter is a string, it will be used as the indent string. + + } else if (typeof space === "string") { + indent = space; + } + +// If there is a replacer, it must be a function or an array. +// Otherwise, throw an error. + + rep = replacer; + if (replacer && typeof replacer !== "function" && ( + typeof replacer !== "object" + || typeof replacer.length !== "number" + )) { + throw new Error("JSON.stringify"); + } + +// Make a fake root object containing our value under the key of "". +// Return the result of stringifying the value. + + return str("", {"": value}); + }; + } + + +// If the JSON object does not yet have a parse method, give it one. + + if (typeof JSON.parse !== "function") { + JSON.parse = function (text, reviver) { + +// The parse method takes a text and an optional reviver function, and returns +// a JavaScript value if the text is a valid JSON text. + + var j; + + function walk(holder, key) { + +// The walk method is used to recursively walk the resulting structure so +// that modifications can be made. + + var k; + var v; + var value = holder[key]; + if (value && typeof value === "object") { + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } + } + } + } + return reviver.call(holder, key, value); + } + + +// Parsing happens in four stages. In the first stage, we replace certain +// Unicode characters with escape sequences. JavaScript handles many characters +// incorrectly, either silently deleting them, or treating them as line endings. + + text = String(text); + rx_dangerous.lastIndex = 0; + if (rx_dangerous.test(text)) { + text = text.replace(rx_dangerous, function (a) { + return ( + "\\u" + + ("0000" + a.charCodeAt(0).toString(16)).slice(-4) + ); + }); + } + +// In the second stage, we run the text against regular expressions that look +// for non-JSON patterns. We are especially concerned with "()" and "new" +// because they can cause invocation, and "=" because it can cause mutation. +// But just to be safe, we want to reject all unexpected forms. + +// We split the second stage into 4 regexp operations in order to work around +// crippling inefficiencies in IE's and Safari's regexp engines. First we +// replace the JSON backslash pairs with "@" (a non-JSON character). Second, we +// replace all simple value tokens with "]" characters. Third, we delete all +// open brackets that follow a colon or comma or that begin the text. Finally, +// we look to see that the remaining characters are only whitespace or "]" or +// "," or ":" or "{" or "}". If that is so, then the text is safe for eval. + + if ( + rx_one.test( + text + .replace(rx_two, "@") + .replace(rx_three, "]") + .replace(rx_four, "") + ) + ) { + +// In the third stage we use the eval function to compile the text into a +// JavaScript structure. The "{" operator is subject to a syntactic ambiguity +// in JavaScript: it can begin a block or an object literal. We wrap the text +// in parens to eliminate the ambiguity. + + j = eval("(" + text + ")"); + +// In the optional fourth stage, we recursively walk the new structure, passing +// each name/value pair to a reviver function for possible transformation. + + return (typeof reviver === "function") + ? walk({"": j}, "") + : j; + } + +// If the text is not JSON parseable, then a SyntaxError is thrown. + + throw new SyntaxError("JSON.parse"); + }; + } +}()); diff --git a/branch.f4a2c5bb/README b/branch.f4a2c5bb/README new file mode 100644 index 000000000..3daeeadd9 --- /dev/null +++ b/branch.f4a2c5bb/README @@ -0,0 +1,41 @@ +JSLint, The JavaScript Code Quality Tool + +Douglas Crockford +douglas@crockford.com + +2015-10-20 + +jslint.js contains the jslint function. It parses and analyzes a source file, +returning an object with information about the file. It can also take an object +that sets options. + +jslint.html runs the jslint.js function in a web page. The page also depends +on adsafe.js [https://github.com/douglascrockford/ADsafe] (which is not included +in this project) and browser.js and report.js (which are). + +browser.js runs the web user interface. + +report.js generates the results reports in HTML. + +help.html describes JSLint's usage. Please read it. + +function.html describes the jslint function and the results it produces. + +Please direct questions and comments to +https://plus.google.com/communities/104441363299760713736. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[http://www.crockford.com/wrrrld/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. diff --git a/branch.f4a2c5bb/adsafe.js b/branch.f4a2c5bb/adsafe.js new file mode 100644 index 000000000..d751bb5d1 --- /dev/null +++ b/branch.f4a2c5bb/adsafe.js @@ -0,0 +1,2060 @@ +// adsafe.js +// 2017-06-12 + +// Public Domain. + +// NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. +// SUBJECT TO CHANGE WITHOUT NOTICE. + +// Original url: http://www.ADsafe.org/adsafe.js + +// This file implements the core ADSAFE runtime. A site may add additional +// methods understanding that those methods will be made available to guest +// code. + +// This code should be minified before deployment. +// See http://javascript.crockford.com/jsmin.html + +// USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO +// NOT CONTROL. + +/*global window*/ + +/*jslint browser, devel, for, this +*/ + +/*property + _, ___nodes___, ___star___, _intercept, a, abbr, acronym, addEventListener, + address, altKey, append, appendChild, apply, area, arguments, autocomplete, + b, bdo, big, blockquote, blur, br, bubble, button, call, callee, caller, + cancelBubble, canvas, caption, center, change, charAt, charCode, check, + checked, childNodes, cite, class, className, clientX, clientY, clone, + cloneNode, code, col, colgroup, combine, concat, console, constructor, + count, create, createDocumentFragment, createElement, createRange, + createTextNode, createTextRange, cssFloat, ctrlKey, currentStyle, dd, + defaultView, del, dfn, dir, disabled, div, dl, dt, each, em, empty, enable, + ephemeral, eval, exec, expand, explode, fieldset, fire, firstChild, focus, + font, form, fragment, fromCharCode, get, getCheck, getChecks, getClass, + getClasses, getComputedStyle, getElementById, getElementsByTagName, + getMark, getMarks, getName, getNames, getOffsetHeight, getOffsetHeights, + getOffsetWidth, getOffsetWidths, getParent, getSelection, getStyle, + getStyles, getTagName, getTagNames, getTitle, getTitles, getValue, + getValues, go, h1, h2, h3, h4, h5, h6, has, hasOwnProperty, hr, i, id, img, + inRange, indeterminate, indexOf, input, ins, insertBefore, isArray, kbd, + key, keyCode, keys, klass, label, later, legend, length, li, lib, log, map, + mark, menu, message, name, nextSibling, nodeName, nodeValue, object, off, + offsetHeight, offsetWidth, ol, on, onclick, ondblclick, onfocusin, + onfocusout, onkeypress, onmousedown, onmousemove, onmouseout, onmouseover, + onmouseup, op, optgroup, option, p, parent, parentNode, postError, pre, + prepend, preventDefault, protect, prototype, push, q, remove, removeChild, + removeElement, replace, replaceChild, returnValue, row, samp, select, + selection, selectionEnd, selectionStart, set, shiftKey, slice, small, span, + srcElement, stack, stopPropagation, strong, style, styleFloat, sub, sup, + table, tag, tagName, target, tbody, td, test, text, textarea, tfoot, th, + that, thead, title, toLowerCase, toString, toUpperCase, tr, tt, type, u, + ul, unwatch, value, valueOf, var, visibility, watch, window, writeln, x, y +*/ + +var ADSAFE; +ADSAFE = (function () { + "use strict"; + + var adsafe_id; // The id of the current widget + var adsafe_lib; // The script libraries loaded by the current widget + +// These member names are banned from guest scripts. The ADSAFE.get and +// ADSAFE.put methods will not allow access to these properties. + + var banned = { + arguments: true, + callee: true, + caller: true, + constructor: true, + eval: true, + prototype: true, + stack: true, + unwatch: true, + valueOf: true, + watch: true + }; + + var cache_style_object; + var cache_style_node; + var defaultView = document.defaultView; + var ephemeral; + var flipflop; // Used in :even/:odd processing + var has_focus; + var hunter; // Set of hunter patterns + var interceptors = []; + + var makeableTagName = { + +// This is the whitelist of elements that may be created with the .tag(tagName) +// method. + + a: true, + abbr: true, + acronym: true, + address: true, + area: true, + b: true, + bdo: true, + big: true, + blockquote: true, + br: true, + button: true, + canvas: true, + caption: true, + center: true, + cite: true, + code: true, + col: true, + colgroup: true, + dd: true, + del: true, + dfn: true, + dir: true, + div: true, + dl: true, + dt: true, + em: true, + fieldset: true, + font: true, + form: true, + h1: true, + h2: true, + h3: true, + h4: true, + h5: true, + h6: true, + hr: true, + i: true, + img: true, + input: true, + ins: true, + kbd: true, + label: true, + legend: true, + li: true, + map: true, + menu: true, + object: true, + ol: true, + optgroup: true, + option: true, + p: true, + pre: true, + q: true, + samp: true, + select: true, + small: true, + span: true, + strong: true, + sub: true, + sup: true, + table: true, + tbody: true, + td: true, + textarea: true, + tfoot: true, + th: true, + thead: true, + tr: true, + tt: true, + u: true, + ul: true, + var: true + }; + var name; + var pecker; // set of pecker patterns + var result; + var star; + var the_range; + var value; + + +// The error function is called if there is a violation or confusion. +// It throws an exception. + + function error(message) { + ADSAFE.log("ADsafe error: " + (message || "ADsafe violation.")); + throw { + name: "ADsafe", + message: message || "ADsafe violation." + }; + } + + +// Some of JavaScript's implicit string conversions can grant extraordinary +// powers to untrusted code. So we use the string_check function to prevent +// such abuses. + + function string_check(string) { + if (typeof string !== "string") { + error("ADsafe string violation."); + } + return string; + } + + +// The object.hasOwnProperty method has a number of hazards. So we wrap it in +// the owns function. + + function owns(object, string) { + return ( + object + && typeof object === "object" + && Object.prototype.hasOwnProperty.call(object, string_check(string)) + ); + } + +// The reject functions enforce the restriction on property names. +// reject_property allows access only to objects and arrays. It does not allow +// use of the banned names, or names that are not strings and not numbers, +// or strings that start or end with _. + + function reject_name(name) { + return ( + typeof name !== "number" + && ( + typeof name !== "string" + || banned[name] + || name.charAt(0) === "_" + || name.slice(-1) === "_" + ) + ); + } + + + function reject_property(object, name) { + return typeof object !== "object" || reject_name(name); + } + + + function reject_global(that) { + if (that.window) { + error(); + } + } + + + function getStyleObject(node) { + +// The getStyleObject function returns the computed style object for a node. + + if (node === cache_style_node) { + return cache_style_object; + } + cache_style_node = node; + cache_style_object = ( + node.currentStyle + || defaultView.getComputedStyle(node, "") + ); + return cache_style_object; + } + + + function walkTheDOM(node, func, skip) { + +// Recursively traverse the DOM tree, starting with the node, in document +// source order, calling the func on each node visisted. + + if (!skip) { + func(node); + } + node = node.firstChild; + while (node) { + walkTheDOM(node, func); + node = node.nextSibling; + } + } + + + function purge_event_handlers(node) { + +// We attach all event handlers to an "___ on ___" property. The property name +// contains spaces to insure that there is no collision with HTML attribues. +// Keeping the handlers in a single property makes it easy to remove them +// all at once. Removal is required to avoid memory leakage on IE6 and IE7. + + walkTheDOM(node, function (node) { + if (node.tagName) { + node["___ on ___"] = null; + node.change = null; + } + }); + } + + + function parse_query(text, id) { + +// Convert a query string into an array of op/name/value selectors. +// A query string is a sequence of triples wrapped in brackets; or names, +// possibly prefixed by # . & > _, or :option, or * or /. A triple is a name, +// and operator (one of [=, [!=, [*=, [~=, [|=, [$=, or [^=) and a value. + +// If the id parameter is supplied, then the name following # must have the +// id as a prefix and must match the ADsafe rule for id: being all uppercase +// letters and digits with one underbar. + +// A name must be all lower case and may contain digits, -, or _. + + var match; // A match array + var query = []; // The resulting query array + var selector; + var qx = (id) + ? /^\s*(?:([*\/])|\[\s*([a-z][0-9a-z_\-]*)\s*(?:([!*~|$\^]?=)\s*([0-9A-Za-z_\-*%&;.\/:!]+)\s*)?\]|#\s*([A-Z]+_[A-Z0-9]+)|:\s*([a-z]+)|([.&_>+]?)\s*([a-z][0-9a-z\-]*))\s*/ + : /^\s*(?:([*\/])|\[\s*([a-z][0-9a-z_\-]*)\s*(?:([!*~|$\^]?=)\s*([0-9A-Za-z_\-*%&;.\/:!]+)\s*)?\]|#\s*([\-A-Za-z0-9_]+)|:\s*([a-z]+)|([.&_>+]?)\s*([a-z][0-9a-z\-]*))\s*/; + +// Loop over all of the selectors in the text. + + do { + +// The qx teases the components of one selector out of the text, ignoring +// whitespace. + +// match[0] the whole selector +// match[1] * / +// match[2] attribute name +// match[3] = != *= ~= |= $= ^= +// match[4] attribute value +// match[5] # id +// match[6] : option +// match[7] . & _ > + +// match[8] name + + match = qx.exec(string_check(text)); + if (!match) { + error("ADsafe: Bad query:" + text); + } + +// Make a selector object and stuff it in the query. + + if (match[1]) { + +// The selector is * or / + + selector = { + op: match[1] + }; + } else if (match[2]) { + +// The selector is in brackets. + + selector = (match[3]) + ? { + op: "[" + match[3], + name: match[2], + value: match[4] + } + : { + op: "[", + name: match[2] + }; + } else if (match[5]) { + +// The selector is an id. + + if ( + query.length > 0 + || match[5].length <= id.length + || match[5].slice(0, id.length) !== id + ) { + error("ADsafe: Bad query: " + text); + } + selector = { + op: "#", + name: match[5] + }; + +// The selector is a colon. + + } else if (match[6]) { + selector = { + op: ":" + match[6] + }; + +// The selector is one of > + . & _ or a naked tag name + + } else { + selector = { + op: match[7], + name: match[8] + }; + } + +// Add the selector to the query. + + query.push(selector); + +// Remove the selector from the text. If there is more text, have another go. + + text = text.slice(match[0].length); + } while (text); + return query; + } + + + hunter = { + +// These functions implement the hunter behaviors. + + "": function (node) { + var array; + var nodelist = node.getElementsByTagName(name); + var i; + var length; + +// getElementsByTagName produces a nodeList, which is one of the world's most +// inefficient data structures. It is so slow that JavaScript's pseudo arrays +// look terrifically swift by comparison. So we do the conversion. This is +// easily done on some browsers, less easily on others. + + try { + array = Array.prototype.slice.call(nodelist, 0); + result = (result.length) + ? result.concat(array) + : array; + } catch (ignore) { + length = nodelist.length; + for (i = 0; i < length; i += 1) { + result.push(nodelist[i]); + } + } + }, + "+": function (node) { + node = node.nextSibling; + name = name.toUpperCase(); + while (node && !node.tagName) { + node = node.nextSibling; + } + if (node && node.tagName === name) { + result.push(node); + } + }, + ">": function (node) { + node = node.firstChild; + name = name.toUpperCase(); + while (node) { + if (node.tagName === name) { + result.push(node); + } + node = node.nextSibling; + } + }, + "#": function () { + var n = document.getElementById(name); + if (n.tagName) { + result.push(n); + } + }, + "/": function (node) { + var nodes = node.childNodes; + var i; + var length = nodes.length; + for (i = 0; i < length; i += 1) { + result.push(nodes[i]); + } + }, + "*": function (node) { + star = true; + walkTheDOM(node, function (node) { + result.push(node); + }, true); + } + }; + + pecker = { + ".": function (node) { + var classy = " " + node.className + " "; + return classy.indexOf(" " + name + " ") >= 0; + }, + "&": function (node) { + return node.name === name; + }, + "_": function (node) { + return node.type === name; + }, + "[": function (node) { + return typeof node[name] === "string"; + }, + "[=": function (node) { + var member = node[name]; + return typeof member === "string" && member === value; + }, + "[!=": function (node) { + var member = node[name]; + return typeof member === "string" && member !== value; + }, + "[^=": function (node) { + var member = node[name]; + return typeof member === "string" && + member.slice(0, member.length) === value; + }, + "[$=": function (node) { + var member = node[name]; + return typeof member === "string" && + member.slice(-member.length) === value; + }, + "[*=": function (node) { + var member = node[name]; + return typeof member === "string" && + member.indexOf(value) >= 0; + }, + "[~=": function (node) { + var member = node[name]; + if (typeof member === "string") { + member = " " + member + " "; + return member.indexOf(" " + value + " ") >= 0; + } + }, + "[|=": function (node) { + var member = node[name]; + if (typeof member === "string") { + member = "-" + member + "-"; + return member.indexOf("-" + value + "-") >= 0; + } + }, + ":blur": function (node) { + return node !== has_focus; + }, + ":checked": function (node) { + return node.checked; + }, + ":disabled": function (node) { + return node.tagName && node.disabled; + }, + ":enabled": function (node) { + return node.tagName && !node.disabled; + }, + ":even": function (node) { + var f; + if (node.tagName) { + f = flipflop; + flipflop = !flipflop; + return f; + } + return false; + }, + ":focus": function (node) { + return node === has_focus; + }, + ":hidden": function (node) { + return node.tagName && getStyleObject(node).visibility !== "visible"; + }, + ":odd": function (node) { + if (node.tagName) { + flipflop = !flipflop; + return flipflop; + } + return false; + }, + ":tag": function (node) { + return node.tagName; + }, + ":text": function (node) { + return node.nodeName === "#text"; + }, + ":trim": function (node) { + return node.nodeName !== "#text" || (/\W/.test(node.nodeValue)); + }, + ":unchecked": function (node) { + return node.tagName && !node.checked; + }, + ":visible": function (node) { + return node.tagName && getStyleObject(node).visibility === "visible"; + } + }; + + + function quest(query, nodes) { + var selector; + var func; + var i; + var j; + +// Step through each selector. + + for (i = 0; i < query.length; i += 1) { + selector = query[i]; + name = selector.name; + func = hunter[selector.op]; + +// There are two kinds of selectors: hunters and peckers. If this is a hunter, +// loop through the the nodes, passing each node to the hunter function. +// Accumulate all the nodes it finds. + + if (typeof func === "function") { + if (star) { + error( + "ADsafe: Query violation: *" + + selector.op + + (selector.name || "") + ); + } + result = []; + for (j = 0; j < nodes.length; j += 1) { + func(nodes[j]); + } + } else { + +// If this is a pecker, get its function. There is a special case for +// the :first and :rest selectors because they are so simple. + + value = selector.value; + flipflop = false; + func = pecker[selector.op]; + if (typeof func !== "function") { + switch (selector.op) { + case ":first": + result = nodes.slice(0, 1); + break; + case ":rest": + result = nodes.slice(1); + break; + default: + error("ADsafe: Query violation: :" + selector.op); + } + } else { + +// For the other selectors, make an array of nodes that are filtered by +// the pecker function. + + result = []; + for (j = 0; j < nodes.length; j += 1) { + if (func(nodes[j])) { + result.push(nodes[j]); + } + } + } + } + nodes = result; + } + return result; + } + + + function make_root(root, id) { + + if (id) { + if (root.tagName !== "DIV") { + error("ADsafe: Bad node."); + } + } else { + if (root.tagName !== "BODY") { + error("ADsafe: Bad node."); + } + } + +// A Bunch is a container that holds zero or more dom nodes. +// It has many useful methods. + + function Bunch(nodes) { + this.___nodes___ = nodes; + this.___star___ = star && nodes.length > 1; + star = false; + } + + var allow_focus = true; + var dom; + var dom_event = function (ev) { + var key; + var target; + var that; + var the_event; + var the_target; + var the_actual_event = ev || event; + var type = the_actual_event.type; + +// Get the target node and wrap it in a bunch. + + the_target = the_actual_event.target || the_actual_event.srcElement; + target = new Bunch([the_target]); + that = target; + +// Use the PPK hack to make focus bubbly on IE. +// When a widget has focus, it can use the focus method. + + switch (type) { + case "mousedown": + allow_focus = true; + if (document.selection) { + the_range = document.selection.createRange(); + } + break; + case "focus": + case "focusin": + allow_focus = true; + has_focus = the_target; + the_actual_event.cancelBubble = false; + type = "focus"; + break; + case "blur": + case "focusout": + allow_focus = false; + has_focus = null; + type = "blur"; + break; + case "keypress": + allow_focus = true; + has_focus = the_target; + key = String.fromCharCode( + the_actual_event.charCode || the_actual_event.keyCode + ); + switch (key) { + case "\u000d": + case "\u000a": + type = "enterkey"; + break; + case "\u001b": + type = "escapekey"; + break; + } + break; + +// This is a workaround for Safari. + + case "click": + allow_focus = true; + break; + } + if ( + the_actual_event.cancelBubble + && the_actual_event.stopPropagation + ) { + the_actual_event.stopPropagation(); + } + +// Make the event object. + + the_event = { + altKey: the_actual_event.altKey, + ctrlKey: the_actual_event.ctrlKey, + bubble: function () { + +// Bubble up. Get the parent of that node. It becomes the new that. +// getParent throws when bubbling is not possible. + + try { + var parent = that.getParent(); + var b = parent.___nodes___[0]; + that = parent; + the_event.that = that; + +// If that node has an event handler, fire it. Otherwise, bubble up. + + if ( + b["___ on ___"] && b["___ on ___"][type] + ) { + that.fire(the_event); + } else { + the_event.bubble(); + } + } catch (e) { + error(e); + } + }, + key: key, + preventDefault: function () { + if (the_actual_event.preventDefault) { + the_actual_event.preventDefault(); + } + the_actual_event.returnValue = false; + }, + shiftKey: the_actual_event.shiftKey, + target: target, + that: that, + type: type, + x: the_actual_event.clientX, + y: the_actual_event.clientY + }; + +// If the target has event handlers, then fire them. Otherwise, bubble up. + + if ( + the_target["___ on ___"] + && the_target["___ on ___"][the_event.type] + ) { + target.fire(the_event); + } else { + while (true) { + the_target = the_target.parentNode; + if (!the_target) { + break; + } + if ( + the_target["___ on ___"] + && the_target["___ on ___"][the_event.type] + ) { + that = new Bunch([the_target]); + the_event.that = that; + that.fire(the_event); + break; + } + if (the_target["___adsafe root___"]) { + break; + } + } + } + if (the_event.type === "escapekey") { + if (ephemeral) { + ephemeral.remove(); + } + ephemeral = null; + } + that = null; + the_actual_event = null; + the_event = null; + the_target = null; + return; + }; + +// Mark the node as a root. This prevents event bubbling from propagating +// past it. + + root["___adsafe root___"] = "___adsafe root___"; + + Bunch.prototype = { + append: function (appendage) { + reject_global(this); + var b = this.___nodes___; + var flag = false; + var i; + var j; + var node; + var rep; + if (b.length === 0 || !appendage) { + return this; + } + if (Array.isArray(appendage)) { + if (appendage.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + rep = appendage[i].___nodes___; + for (j = 0; j < rep.length; j += 1) { + b[i].appendChild(rep[j]); + } + } + } else { + if (typeof appendage !== "string") { + rep = appendage.___nodes___; + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (rep) { + for (j = 0; j < rep.length; j += 1) { + node.appendChild((flag) + ? rep[j].cloneNode(true) + : rep[j]); + } + flag = true; + } else { + node.appendChild(document.createTextNode(appendage)); + } + } + } + return this; + }, + blur: function () { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + has_focus = null; + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.blur) { + node.blur(); + } + } + return this; + }, + check: function (value) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.checked = !!value[i]; + } + } + } else { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.checked = !!value; + } + } + } + return this; + }, + "class": function (value) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + if (/url/i.test(string_check(value[i]))) { + error("ADsafe error."); + } + node = b[i]; + if (node.tagName) { + node.className = value[i]; + } + } + } else { + if (/url/i.test(string_check(value))) { + error("ADsafe error."); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.className = value; + } + } + } + return this; + }, + clone: function (deep, n) { + var a = []; + var b = this.___nodes___; + var c; + var i; + var j; + var k = n || 1; + for (i = 0; i < k; i += 1) { + c = []; + for (j = 0; j < b.length; j += 1) { + c.push(b[j].cloneNode(deep)); + } + a.push(new Bunch(c)); + } + return (n) + ? a + : a[0]; + }, + count: function () { + reject_global(this); + return this.___nodes___.length; + }, + each: function (func) { + reject_global(this); + var b = this.___nodes___; + var i; + if (typeof func === "function") { + for (i = 0; i < b.length; i += 1) { + func(new Bunch([b[i]])); + } + return this; + } + error(); + }, + empty: function () { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + while (node.firstChild) { + purge_event_handlers(node); + node.removeChild(node.firstChild); + } + } + } else { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + while (node.firstChild) { + purge_event_handlers(node); + node.removeChild(node.firstChild); + } + } + } + return this; + }, + enable: function (enable) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(enable)) { + if (enable.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + "-" + + enable.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.disabled = !enable[i]; + } + } + } else { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.disabled = !enable; + } + } + } + return this; + }, + ephemeral: function () { + reject_global(this); + if (ephemeral) { + ephemeral.remove(); + } + ephemeral = this; + return this; + }, + explode: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = new Bunch([b[i]]); + } + return a; + }, + fire: function (event) { + + // Fire an event on an object. The event can be either + // a string containing the name of the event, or an + // object containing a type property containing the + // name of the event. Handlers registered by the "on" + // method that match the event name will be invoked. + + reject_global(this); + var array; + var b; + var i; + var j; + var n; + var node; + var on; + var type; + + if (typeof event === "string") { + type = event; + event = {type: type}; + } else if (typeof event === "object") { + type = event.type; + } else { + error(); + } + b = this.___nodes___; + n = b.length; + for (i = 0; i < n; i += 1) { + node = b[i]; + on = node["___ on ___"]; + + // If an array of handlers exist for this event, then + // loop through it and execute the handlers in order. + + if (owns(on, type)) { + array = on[type]; + for (j = 0; j < array.length; j += 1) { + + // Invoke a handler. Pass the event object. + + array[j].call(this, event); + } + } + } + return this; + }, + focus: function () { + reject_global(this); + var b = this.___nodes___; + if (b.length > 0 && allow_focus) { + has_focus = b[0].focus(); + return this; + } + error(); + }, + fragment: function () { + reject_global(this); + return new Bunch([document.createDocumentFragment()]); + }, + getCheck: function () { + return this.getChecks()[0]; + }, + getChecks: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i].checked; + } + return a; + }, + getClass: function () { + return this.getClasses()[0]; + }, + getClasses: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i].className; + } + return a; + }, + getMark: function () { + return this.getMarks()[0]; + }, + getMarks: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i]["_adsafe mark_"]; + } + return a; + }, + getName: function () { + return this.getNames()[0]; + }, + getNames: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i].name; + } + return a; + }, + getOffsetHeight: function () { + return this.getOffsetHeights()[0]; + }, + getOffsetHeights: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i].offsetHeight; + } + return a; + }, + getOffsetWidth: function () { + return this.getOffsetWidths()[0]; + }, + getOffsetWidths: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i].offsetWidth; + } + return a; + }, + getParent: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + var n; + for (i = 0; i < b.length; i += 1) { + n = b[i].parentNode; + if (n["___adsafe root___"]) { + error("ADsafe parent violation."); + } + a[i] = n; + } + return new Bunch(a); + }, + getSelection: function () { + reject_global(this); + var b = this.___nodes___; + var end; + var node; + var start; + var range; + if (b.length === 1 && allow_focus) { + node = b[0]; + if (typeof node.selectionStart === "number") { + start = node.selectionStart; + end = node.selectionEnd; + return node.value.slice(start, end); + } + range = node.createTextRange(); + range.expand("textedit"); + if (range.inRange(the_range)) { + return the_range.text; + } + } + return null; + }, + getStyle: function (name) { + return this.getStyles(name)[0]; + }, + getStyles: function (name) { + reject_global(this); + if (reject_name(name)) { + error("ADsafe style violation."); + } + var a = []; + var b = this.___nodes___; + var i; + var node; + var s; + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + s = (name !== "float") + ? getStyleObject(node)[name] + : getStyleObject(node).cssFloat + || getStyleObject(node).styleFloat; + if (typeof s === "string") { + a[i] = s; + } + } + } + return a; + }, + getTagName: function () { + return this.getTagNames()[0]; + }, + getTagNames: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + var tagName; + for (i = 0; i < b.length; i += 1) { + tagName = b[i].tagName; + a[i] = (typeof tagName === "string") + ? tagName.toLowerCase() + : tagName; + } + return a; + }, + getTitle: function () { + return this.getTitles()[0]; + }, + getTitles: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + a[i] = b[i].title; + } + return a; + }, + getValue: function () { + return this.getValues()[0]; + }, + getValues: function () { + reject_global(this); + var a = []; + var b = this.___nodes___; + var i; + var node; + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.nodeName === "#text") { + a[i] = node.nodeValue; + } else if (node.tagName && node.type !== "password") { + a[i] = node.value; + if ( + !a[i] + && node.firstChild + && node.firstChild.nodeName === "#text" + ) { + a[i] = node.firstChild.nodeValue; + } + } + } + return a; + }, + indeterminate: function (value) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.indeterminate = !!value[i]; + } + } + } else { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.indeterminate = !!value; + } + } + } + return this; + }, + klass: function (value) { + return this.class(value); + }, + mark: function (value) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node["_adsafe mark_"] = String(value[i]); + } + } + } else { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node["_adsafe mark_"] = String(value); + } + } + } + return this; + }, + off: function (type) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (typeof type === "string") { + if (typeof node["___ on ___"] === "object") { + node["___ on ___"][type] = null; + } + } else { + node["___ on ___"] = null; + } + } + return this; + }, + on: function (type, func) { + reject_global(this); + if (typeof type !== "string" || typeof func !== "function") { + error(); + } + + var b = this.___nodes___; + var i; + var node; + var on; + var ontype; + for (i = 0; i < b.length; i += 1) { + node = b[i]; + +// The change event does not propogate, so we must put the handler on the +// instance. + + if (type === "change") { + ontype = "on" + type; + if (node[ontype] !== dom_event) { + node[ontype] = dom_event; + } + } + +// Register an event. Put the function in a handler array, making one if it +// doesn't yet exist for this type on this node. + + on = node["___ on ___"]; + if (!on) { + on = {}; + node["___ on ___"] = on; + } + if (owns(on, type)) { + on[type].push(func); + } else { + on[type] = [func]; + } + } + return this; + }, + protect: function () { + reject_global(this); + var b = this.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + b[i]["___adsafe root___"] = "___adsafe root___"; + } + return this; + }, + q: function (text) { + reject_global(this); + star = this.___star___; + return new Bunch( + quest(parse_query(string_check(text), id), this.___nodes___) + ); + }, + remove: function () { + reject_global(this); + this.replace(); + }, + replace: function (replacement) { + reject_global(this); + var b = this.___nodes___; + var flag = false; + var i; + var j; + var newnode; + var node; + var parent; + var rep; + if (b.length === 0) { + return; + } + for (i = 0; i < b.length; i += 1) { + purge_event_handlers(b[i]); + } + if ( + !replacement || replacement.length === 0 + || ( + replacement.___nodes___ + && replacement.___nodes___.length === 0 + ) + ) { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + purge_event_handlers(node); + if (node.parentNode) { + node.parentNode.removeChild(node); + } + } + } else if (Array.isArray(replacement)) { + if (replacement.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + parent = node.parentNode; + purge_event_handlers(node); + if (parent) { + rep = replacement[i].___nodes___; + if (rep.length > 0) { + newnode = rep[0]; + parent.replaceChild(newnode, node); + for (j = 1; j < rep.length; j += 1) { + node = newnode; + newnode = rep[j]; + parent.insertBefore(newnode, node.nextSibling); + } + } else { + parent.removeChild(node); + } + } + } + } else { + rep = replacement.___nodes___; + for (i = 0; i < b.length; i += 1) { + node = b[i]; + purge_event_handlers(node); + parent = node.parentNode; + if (parent) { + newnode = (flag) + ? rep[0].cloneNode(true) + : rep[0]; + parent.replaceChild(newnode, node); + for (j = 1; j < rep.length; j += 1) { + node = newnode; + newnode = (flag) + ? rep[j].clone(true) + : rep[j]; + parent.insertBefore(newnode, node.nextSibling); + } + flag = true; + } + } + } + return this; + }, + select: function () { + reject_global(this); + var b = this.___nodes___; + if (b.length < 1 || !allow_focus) { + error(); + } + b[0].focus(); + b[0].select(); + return this; + }, + selection: function (string) { + reject_global(this); + string_check(string); + var b = this.___nodes___; + var end; + var node; + var old; + var start; + var range; + if (b.length === 1 && allow_focus) { + node = b[0]; + if (typeof node.selectionStart === "number") { + start = node.selectionStart; + end = node.selectionEnd; + old = node.value; + node.value = old.slice(0, start) + string + old.slice(end); + node.selectionStart = start + string.length; + node.selectionEnd = start + string.length; + node.focus(); + } else { + range = node.createTextRange(); + range.expand("textedit"); + if (range.inRange(the_range)) { + the_range.select(); + the_range.text = string; + the_range.select(); + } + } + } + return this; + }, + style: function (name, value) { + reject_global(this); + if (reject_name(name)) { + error("ADsafe style violation."); + } + if (value === undefined || (/url/i.test(string_check(value)))) { + error(); + } + var b = this.___nodes___; + var i; + var node; + var v; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + v = string_check(value[i]); + if (/url/i.test(v)) { + error(); + } + if (node.tagName) { + if (name !== "float") { + node.style[name] = v; + } else { + node.style.cssFloat = v; + node.style.styleFloat = v; + } + } + } + } else { + v = string_check(value); + if (/url/i.test(v)) { + error(); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + if (name !== "float") { + node.style[name] = v; + } else { + node.style.cssFloat = v; + node.style.styleFloat = v; + } + } + } + } + return this; + }, + tag: function (tag, type, name) { + reject_global(this); + var node; + if (typeof tag !== "string") { + error(); + } + if (makeableTagName[tag] !== true) { + error("ADsafe: Bad tag: " + tag); + } + node = document.createElement(tag); + if (name) { + node.autocomplete = "off"; + node.name = string_check(name); + } + if (type) { + node.type = string_check(type); + } + return new Bunch([node]); + }, + text: function (text) { + reject_global(this); + var a; + var i; + if (Array.isArray(text)) { + a = []; + for (i = 0; i < text.length; i += 1) { + a[i] = document.createTextNode(string_check(text[i])); + } + return new Bunch(a); + } + return new Bunch([document.createTextNode(string_check(text))]); + }, + title: function (value) { + reject_global(this); + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value)) { + if (value.length !== b.length) { + error( + "ADsafe: Array length: " + + b.length + + "-" + + value.length + ); + } + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.title = string_check(value[i]); + } + } + } else { + string_check(value); + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + node.title = value; + } + } + } + return this; + }, + value: function (value) { + reject_global(this); + if (value === undefined) { + error(); + } + var b = this.___nodes___; + var i; + var node; + if (Array.isArray(value) && b.length === value.length) { + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + if (node.type !== "password") { + if (typeof node.value === "string") { + node.value = value[i]; + } else { + while (node.firstChild) { + purge_event_handlers(node.firstChild); + node.removeChild(node.firstChild); + } + node.appendChild(document.createTextNode( + String(value[i]) + )); + } + } + } else if (node.nodeName === "#text") { + node.nodeValue = String(value[i]); + } + } + } else { + value = String(value); + for (i = 0; i < b.length; i += 1) { + node = b[i]; + if (node.tagName) { + if ( + node.tagName !== "BUTTON" + && typeof node.value === "string" + ) { + node.value = value; + } else { + while (node.firstChild) { + purge_event_handlers(node.firstChild); + node.removeChild(node.firstChild); + } + node.appendChild(document.createTextNode(value)); + } + } else if (node.nodeName === "#text") { + node.nodeValue = value; + } + } + } + return this; + } + }; + +// Return an ADsafe dom object. + + dom = { + append: function (bunch) { + var b = (typeof bunch === "string") + ? [document.createTextNode(bunch)] + : bunch.___nodes___; + var i; + var n; + for (i = 0; i < b.length; i += 1) { + n = b[i]; + if (typeof n === "string" || typeof n === "number") { + n = document.createTextNode(String(n)); + } + root.appendChild(n); + } + return dom; + }, + combine: function (array) { + if (!array || !array.length) { + error("ADsafe: Bad combination."); + } + var b = array[0].___nodes___; + var i; + for (i = 0; i < array.length; i += 1) { + b = b.concat(array[i].___nodes___); + } + return new Bunch(b); + }, + count: function () { + return 1; + }, + ephemeral: function (bunch) { + if (ephemeral) { + ephemeral.remove(); + } + ephemeral = bunch; + return dom; + }, + fragment: function () { + return new Bunch([document.createDocumentFragment()]); + }, + prepend: function (bunch) { + var b = bunch.___nodes___; + var i; + for (i = 0; i < b.length; i += 1) { + root.insertBefore(b[i], root.firstChild); + } + return dom; + }, + q: function (text) { + star = false; + var query = parse_query(text, id); + if (typeof hunter[query[0].op] !== "function") { + error("ADsafe: Bad query: " + query[0]); + } + return new Bunch(quest(query, [root])); + }, + remove: function () { + purge_event_handlers(root); + root.parent.removeElement(root); + root = null; + }, + row: function (values) { + var tr = document.createElement("tr"); + var td; + var i; + for (i = 0; i < values.length; i += 1) { + td = document.createElement("td"); + td.appendChild(document.createTextNode(String(values[i]))); + tr.appendChild(td); + } + return new Bunch([tr]); + }, + tag: function (tag, type, name) { + var node; + if (typeof tag !== "string") { + error(); + } + if (makeableTagName[tag] !== true) { + error("ADsafe: Bad tag: " + tag); + } + node = document.createElement(tag); + if (name) { + node.autocomplete = "off"; + node.name = name; + } + if (type) { + node.type = type; + } + return new Bunch([node]); + }, + text: function (text) { + var a; + var i; + if (Array.isArray(text)) { + a = []; + for (i = 0; i < text.length; i += 1) { + a[i] = document.createTextNode(string_check(text[i])); + } + return new Bunch(a); + } + return new Bunch([document.createTextNode(string_check(text))]); + } + }; + + if (typeof root.addEventListener === "function") { + root.addEventListener("focus", dom_event, true); + root.addEventListener("blur", dom_event, true); + root.addEventListener("mouseover", dom_event, true); + root.addEventListener("mouseout", dom_event, true); + root.addEventListener("mouseup", dom_event, true); + root.addEventListener("mousedown", dom_event, true); + root.addEventListener("mousemove", dom_event, true); + root.addEventListener("click", dom_event, true); + root.addEventListener("dblclick", dom_event, true); + root.addEventListener("keypress", dom_event, true); + } else { + root.onclick = dom_event; + root.ondblclick = dom_event; + root.onfocusin = dom_event; + root.onfocusout = dom_event; + root.onkeypress = dom_event; + root.onmouseout = dom_event; + root.onmousedown = dom_event; + root.onmousemove = dom_event; + root.onmouseover = dom_event; + root.onmouseup = dom_event; + } + return [dom, Bunch.prototype]; + } + + +// Return the ADSAFE object. + + return { + create: function (o) { + reject_global(o); + return Object.create(o); + }, + +// ADSAFE.get retrieves a value from an object. + + get: function (object, name) { + reject_global(object); + if (!reject_property(object, name)) { + return object[name]; + } + error(); + }, + +// ADSAFE.go allows a guest widget to get access to a wrapped dom node and +// approved ADsafe libraries. It is passed an id and a function. The function +// will be passed the wrapped dom node and an object containing the libraries. + + go: function (id, f) { + var dom; + var fun; + var root; + var i; + var scripts; + +// If ADSAFE.id was called, the id better match. + + if (adsafe_id && adsafe_id !== id) { + error(); + } + +// Get the dom node for the widget's div container. + + root = document.getElementById(id); + if (root.tagName !== "DIV") { + error(); + } + adsafe_id = null; + +// Delete the scripts held in the div. They have all run, so we don't need +// them any more. If the div had no scripts, then something is wrong. +// This provides some protection against mishaps due to weakness in the +// document.getElementById function. + + scripts = root.getElementsByTagName("script"); + i = scripts.length - 1; + if (i < 0) { + error(); + } + do { + root.removeChild(scripts[i]); + i -= 1; + } while (i >= 0); + root = make_root(root, id); + dom = root[0]; + +// If the page has registered interceptors, call then. + + for (i = 0; i < interceptors.length; i += 1) { + fun = interceptors[i]; + if (typeof fun === "function") { + try { + fun(id, dom, adsafe_lib, root[1]); + } catch (e1) { + ADSAFE.log(e1); + } + } + } + +// Call the supplied function. + + try { + f(dom, adsafe_lib); + } catch (e2) { + ADSAFE.log(e2); + } + root = null; + adsafe_lib = null; + }, + +// ADSAFE.has returns true if the object contains an own property with the +// given name. + + has: function (object, name) { + return owns(object, name); + }, + +// ADSAFE.id allows a guest widget to indicate that it wants to load +// ADsafe approved libraries. + + id: function (id) { + +// Calls to ADSAFE.id must be balanced with calls to ADSAFE.go. +// Only one id can be active at a time. + + if (adsafe_id) { + error(); + } + adsafe_id = id; + adsafe_lib = {}; + }, + +// ADSAFE.isArray returns true if the operand is an array. + + isArray: Array.isArray || function (value) { + return Object.prototype.toString.apply(value) === "[object Array]"; + }, + +// ADSAFE.keys returns an array of keys. + + keys: Object.keys, + +// ADSAFE.later calls a function at a later time. + + later: function (func, timeout) { + if (typeof func === "function") { + setTimeout(func, timeout || 0); + } else { + error(); + } + }, + +// ADSAFE.lib allows an approved ADsafe library to make itself available +// to a widget. The library provides a name and a function. The result of +// calling that function will be made available to the widget via the name. + + lib: function (name, f) { + if (!adsafe_id || reject_name(name)) { + error("ADsafe lib violation."); + } + adsafe_lib[name] = f(adsafe_lib); + }, + +// ADSAFE.log is a debugging aid that spams text to the browser's log. +// Overwrite this function to send log matter somewhere else. + + log: function log(s) { + if (window.console) { + console.log(s); + } else if (typeof Debug === "object") { + Debug.writeln(s); /* IE */ + } else { + opera.postError(s); /* Opera */ + } + }, + +// ADSAFE.remove deletes a value from an object. + + remove: function (object, name) { + if (!reject_property(object, name)) { + delete object[name]; + return; + } + error(); + }, + +// ADSAFE.set stores a value in an object. + + set: function (object, name, value) { + reject_global(object); + if (!reject_property(object, name)) { + object[name] = value; + return; + } + error(); + }, + +// ADSAFE._intercept allows the page to register a function that will +// see the widget's capabilities. + + _intercept: function (f) { + interceptors.push(f); + } + + }; +}()); diff --git a/branch.f4a2c5bb/browser.js b/branch.f4a2c5bb/browser.js new file mode 100644 index 000000000..a7b1e4305 --- /dev/null +++ b/branch.f4a2c5bb/browser.js @@ -0,0 +1,190 @@ +// browser.js +// 2016-08-08 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +/*jslint + browser, for +*/ + +/*global + ADSAFE, REPORT, jslint +*/ + +/*property + ___nodes___, check, create, each, enable, error, focus, function, getCheck, + getName, getTitle, getValue, innerHTML, length, lib, lines, on, onscroll, + property, q, scrollTop, select, split, style, value +*/ + +// This is the web script companion file for JSLint. It includes code for +// interacting with the browser and generating the reports. + +ADSAFE.lib("browser_ui", function () { + "use strict"; + + var rx_crlf = /\n|\r\n?/; + var rx_separator = /[\s,;'"]+/; + + function setHTML(bunch, html) { + bunch.___nodes___[0].innerHTML = html; + } + + function setScrollTop(bunch, number) { + bunch.___nodes___[0].scrollTop = number; + } + + function getScrollTop(bunch) { + return bunch.___nodes___[0].scrollTop; + } + + function onscroll(bunch, handler) { + bunch.___nodes___[0].onscroll = handler; + } + + return function (dom) { + +// This function is the entry point to this web module. + +// First get handles to some of the page features. + + var warnings = dom.q("#JSLINT_WARNINGS"); + var warnings_div = warnings.q(">div"); + var options = dom.q("#JSLINT_OPTIONS"); + var global = dom.q("#JSLINT_GLOBAL"); + var property_fieldset = dom.q("#JSLINT_PROPERTYFIELDSET"); + var property = dom.q("#JSLINT_PROPERTY"); + var report_field = dom.q("#JSLINT_REPORT"); + var report_div = report_field.q(">div"); + var select = dom.q("#JSLINT_SELECT"); + var source = dom.q("#JSLINT_SOURCE"); + var number = dom.q("#JSLINT_NUMBER"); + var fudge = dom.q("#JSLINT_FUDGE"); + var aux = dom.q("#JSLINT_AUX"); + + function clear() { + warnings.style("display", "none"); + report_field.style("display", "none"); + property_fieldset.style("display", "none"); + aux.style("display", "none"); + number.value(""); + property.value(""); + report_div.value(""); + source.value(""); + source.focus(); + warnings_div.value(""); + } + + function clear_options() { + options.q("input_checkbox").check(false); + options.q("input_text").value(""); + global.value(""); + } + + function show_numbers() { + var f = +(fudge.getCheck()); + var n = source.getValue().split(rx_crlf).length + f; + var text = ""; + var i; + for (i = f; i <= n; i += 1) { + text += i + "\n"; + } + number.value(text); + } + + function mark_scroll() { + var ss = getScrollTop(source); + setScrollTop(number, ss); + var sn = getScrollTop(number); + if (ss > sn) { + show_numbers(); + setScrollTop(number, ss); + } + } + + function call_jslint() { + +// First build the option object. + + var option = Object.create(null); + options.q("input_checkbox:checked").each(function (node) { + option[node.getTitle()] = true; + }); + options.q("input_text").each(function (node) { + var value = +node.getValue(); + if (isFinite(value) && value > 0) { + option[node.getTitle()] = value; + } + }); + +// Call JSLint with the source text, the options, and the predefined globals. + + var global_string = global.getValue(); + var result = jslint( + source.getValue(), + option, + (global_string === "") + ? undefined + : global_string.split(rx_separator) + ); + +// Generate the reports. + + var error_html = REPORT.error(result); + var function_html = REPORT.function(result); + var property_text = REPORT.property(result); + +// Display the reports. + + setHTML(warnings_div, error_html); + warnings.style("display", (error_html.length === 0) + ? "none" + : "block"); + setHTML(report_div, function_html); + report_field.style("display", "block"); + if (property_text) { + property.value(property_text); + property_fieldset.style("display", "block"); + setScrollTop(property, 0); + select.enable(true); + } else { + property_fieldset.style("display", "none"); + select.enable(false); + } + aux.style("display", "block"); + source.select(); + } + + function select_property_directive() { + property.select(); + } + +// Lay in the click handlers. + + dom.q("button").each(function (button) { + switch (button.getName()) { + case "JSLint": + button.on("click", call_jslint); + break; + case "clear": + button.on("click", clear); + break; + case "options": + button.on("click", clear_options); + break; + case "select": + button.on("click", select_property_directive); + break; + } + }); + fudge.on("change", function (ignore) { + show_numbers(); + }); + source.on("change", function (ignore) { + show_numbers(); + }); + onscroll(source, function (ignore) { + mark_scroll(); + }); + source.select(); + }; +}); diff --git a/branch.f4a2c5bb/cli.js b/branch.f4a2c5bb/cli.js new file mode 100755 index 000000000..3f558e9f9 --- /dev/null +++ b/branch.f4a2c5bb/cli.js @@ -0,0 +1,134 @@ +#!/usr/bin/env node +/* + * cli.js + * simple cli-tool to run jslint.js from command-line + * + * instruction: + * 1. save this file as cli.js + * 2. download and save in same directory, latest jslint.js from + * https://github.com/douglascrockford/JSLint/blob/master/jslint.js + * + * example usage: + * $ node cli.js ./jslint.js // jslint local-file + * $ node cli.js https://code.jquery.com/jquery-1.0.0.js // jslint remote-file + */ + + + +/*jslint node */ +/*property + argv, basename, column, concat, end, error, exit, filter, forEach, fudge, + join, jslint, line, lines, main, match, message, on, option, parse, push, + readFile, replace, request, runInNewContext, slice, trim, trimRight, + warnings +*/ +"use strict"; +let chunk_list; +let file; +let fs; +let fudge; +let local; +let modeNext; +let onNext; +let path; +let url; +let vm; +let warning_text; +onNext = function (error, data) { + if (error) { + console.error(error.message || error); + process.exit(1); + } + modeNext += 1; + switch (modeNext) { + case 1: + // run this program only in cli-mode + if (module !== require.main) { + console.error( + "jslint-cli can only be run from the command-line" + ); + return; + } + // require builtins + fs = require("fs"); + path = require("path"); + url = require("url"); + vm = require("vm"); + // init local namespace + local = {}; + if (!process.argv[2]) { + console.error( + "error: no specified\n" + + "usage: " + path.basename(__filename) + " \n" + + "example: " + path.basename(__filename) + " example.js\n" + + "example: " + path.basename(__filename) + + " https://jslint.com/jslint.js" + ); + process.exit(1); + } + // init jslint + fs.readFile(path.join(__dirname, "jslint.js"), "utf8", onNext); + break; + case 2: + // bug-workaround - nodejs does not widely support es-modules + vm.runInNewContext( + data.replace("export default function", "function"), + local + ); + // read data from file + file = process.argv[2]; + console.error("jslint " + file); + data = file.match( + /^(https?):\/\// + ); + if (!data) { + fs.readFile(file, "utf8", onNext); + return; + } + // read data from http/https url + require(data[1]).request(url.parse(file), function (response) { + chunk_list = []; + response + .on("data", function (chunk) { + chunk_list.push(chunk); + }) + .on("end", function () { + onNext(null, String(Buffer.concat(chunk_list))); + }) + .on("error", onNext); + }).on("error", onNext).end(); + break; + case 3: + // jslint data + data = local.jslint(data + // ignore first-line shebang in nodejs scripts + .replace(( + /^#!/ + ), "//")); + fudge = data.option.fudge || 0; + // init warning_text + warning_text = ""; + // print warnings to stderrr + data.warnings + .filter(function (warning) { + return warning && warning.message; + }) + // print only first 10 warnings + .slice(0, 10) + .forEach(function (warning, ii) { + warning_text += ( + (" #" + (ii + 1)).slice(-3) + + " \u001b[31m" + warning.message + "\u001b[39m\n" + + " " + String(data.lines[warning.line] || "").trim() + + "\u001b[90m \/\/ line " + + (warning.line + fudge) + + ", col " + (warning.column + fudge) + + "\u001b[39m\n" + ); + }); + console.error(warning_text.trimRight() || "ok"); + break; + } +}; +modeNext = 0; +onNext(); diff --git a/branch.f4a2c5bb/function.html b/branch.f4a2c5bb/function.html new file mode 100644 index 000000000..294df454b --- /dev/null +++ b/branch.f4a2c5bb/function.html @@ -0,0 +1,593 @@ + + + + + + + + + +JSLint: jslint function + + + +
    JSLint
    + +
    The jslint Function
    +
    +

    JSLint

    +

    JSLint is delivered as a set of files:

    +
    + + + + + + + + + + + + + + +
    FilenameContent
    browser.jsBrowser based UI, uses +adsafe.js +for DOM manipulation.
    function.htmlThis page that you are looking at right now.
    help.htmlUsing jslint as a single page JSLint application.
    jslint.htmlSingle page JSLint application that uses the + js files.
    jslint.jsThe jslint function.
    report.jsThe REPORT object, containing report generator functions for + HTML.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    +
    + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring(JSLint)
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    functiontokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable can be assigned to.variable
    +

    Reports

    +

    The REPORT object contains three functions that all take a + result from the jslint function as input. + report.js has no other dependence on other files.

    + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    +
    + +
    + + + + + + + +
    + + diff --git a/branch.f4a2c5bb/help.html b/branch.f4a2c5bb/help.html new file mode 100644 index 000000000..bf0d8b041 --- /dev/null +++ b/branch.f4a2c5bb/help.html @@ -0,0 +1,839 @@ + + + + + + + + + +JSLint: Help + + + +
    JSLint
    + +
    Help
    +
    +
    Il semble que la perfection soit atteinte non quand il n’y a + plus rien à ajouter, mais quand il n’y a plus rien à + retrancher.
    +
    + Antoine de Saint-Exupéry +
    +
    + Terre des Hommes (1939) +
    +

    Good Parts

    +

    The Principle of the Good Parts is

    +
    If a feature is sometimes useful and sometimes dangerous and if + there is a better option then always use the better option.
    +

    JSLint is a JavaScript program that looks for problems in + JavaScript programs. It is a code quality tool.

    +

    When C was + a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    +

    As the language matured, the definition of the language was + strengthened to eliminate some insecurities, and compilers got better + at issuing warnings. lint is no longer needed.

    +

    JavaScript is a + young-for-its-age language. It was originally intended to do small tasks in + webpages, tasks for which Java was too heavy and clumsy. But JavaScript is + a surprisingly capable language, and it is now being used in larger + projects. Many of the features that were intended to make the language easy + to use are troublesome when projects become complicated. A lint + for JavaScript is needed: JSLint, a JavaScript syntax checker + and validator.

    +

    JSLint takes a JavaScript source and scans it. If it finds a + problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by the ECMAScript Programming Language + Standard (the strangely named document that governs JavaScript). + JSLint will reject most legal programs. It is a higher + standard.

    +

    JavaScript is a sloppy language, but hidden deep inside there is an elegant, + better language. JSLint helps you to program in that better + language and to avoid most of the slop. JSLint will reject + programs that browsers will accept because JSLint is concerned + with the quality of your code and browsers are not. You should gladly accept + all of JSLint's advice.

    +

    JSLint can operate on JavaScript source + or JSON text.

    +

    ECMAScript Sixth Edition

    +

    It may take time for the sixth edition of ECMAScript [ES6] to + reach ubiquity. Using the new features in enviroments that do not fully + implement the new standard will result in failure. This is why + JSLint gives warnings when ES6 features are used. Some of + ES6’s features are good, so JSLint will recognize the good + parts of ES6 with the es6 option. As implementations of the new + standard become more stable and better understood, the set of features + recognized by JSLint may increase. After the transition to ES6 is complete, + the es6 option will be dropped.

    +

    Currently, these features are recognized:

    +
      +
    • The ... ellipsis marker in parameter + lists and argument lists, replacing the arguments object + for variadic functions.
    • +
    • The let statement, which is like the var + statement except that it respects block scope. + You may use let or var but not both.
    • +
    • The const statement is like the let statement + except that it disallows the use of assignment on the variable, although + if the value of the variable is mutable, but can still be mutated. + const is preferred to let. +
    • Destructuring of arrays and objects is allowed in parameter lists and on + the left side of let, and const, but not + var or assignment statements, and not deep destructuring + or eliding.
    • +
    • Enhanced object literals, providing shorter forms for function + declaration and properties that are initialized by variables with the + same name.
    • +
    • The fat arrow => fart functions.
    • +
    • The simplest forms of import and export.
    • +
    • `Megastring` literals, but not nested + `megastring` literals.
    • +
    • New global functions, such as Map, Set, + WeakMap, and WeakSet.
    • +
    • 0b- and 0o- number literals.
    • +
    +

    The most important new feature of ES6 is proper tail calls. This has no + new syntax, so JSLint doesn’t see it. But it makes recursion + much more attractive, which makes loops, particularly for + loops, much less attractive.

    +

    import export

    +

    The ES6 module feature will be an important improvement over JavaScript’s + global variables as a means of linking separate files together. + JSLint recognizes a small but essential subset of the module + syntax.

    +
    +

    import name from stringliteral;

    +

    import {name} from stringliteral;

    +

    export default function () {}

    +

    export default function name() {}

    +

    export default expression;

    +

    export function name() {}

    +

    export name;

    +

    export {name};

    +
    +

    Directives

    +

    JSLint provides three directives that may be placed in a + file to manage JSLint’s behavior. Use of these directives is + optional. If they are used, they should be placed in a source file before + the first statement. They are written in the form of a comment, where the + directive name is placed immediately after the opening of the comment before + any whitespace. The three directives are global, + jslint, and property. Directives in a file are + stronger than options selected from the UI or passed with the option object.

    +

    /*global*/

    +

    The /*global*/ directive is used to specify a set of globals + (usually functions and objects containing functions) that are available to + this file. This was commonly used in browsers to link source files together + before ES6 modules appeared. Use of global variables is strongly + discouraged, but unfortunately web browsers require their use. The + /*global*/ directive can only be used when the + Assume a browser option is selected. +

    Each of the names listed will indicate a read-only global variable. The names + are separated by , comma. This directive + should not be used if import or export is used. + This directive inhibits warnings, but it does not declare the names in the + execution environment. +

    For example: +

    /*global +
    ADSAFE, report, jslint
    +*/
    +

    instructs JSLint to not give warnings about the global + variables ADsafe, report, and jslint. + However, if any of those names are expected to be supplied by other files + and those other files fail to do so, then execution errors will result. It + is usually better to use top-level variable declarations instead: +

        var ADSAFE;
    +    var report;
    +    var jslint; 
    +

    Using var in this way allows comparing a global variable to the + undefined value to determine whether it is has been used in the global context.

    +

    /*jslint*/

    +

    The /*jslint*/ directive allows for the control of several + options. These options can also be set from the + JSLint.com user interface.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Tolerate bitwise operatorsbitwisetrue if bitwise operators should be allowed. The + bitwise operators are rarely used in JavaScript programs, so it + is usually more likely that & is a mistyping of + && than that it indicates a bitwise and. + JSLint will give warnings on the bitwise operators + unless this option is selected.
    Assume a browser browsertrue if the standard browser globals should be + predefined. This option will reject the use of + import and export. This option also + disallows the file form of the "use + strict" pragma. It does not supply self + or window; you will have to request these aliases + of the dreaded global object yourself. It adds the same globals + as this directive: +
    /*global +
    + Audio, clearInterval, clearTimeout, document, event, FileReader, + FormData, history, Image, location, name, navigator, Option, screen, + sessionStorage, setInterval, setTimeout, Storage, XMLHttpRequest +
    + */
    +
    Assume CouchDBcouchtrue if + Couch DB + globals should be predefined. It adds the same globals as this + directive: +
    /*global +
    emit, getRow, isArray, log, provides, registerType, require, + send, start, sum, toJSON
    + */
    Assume in developmentdeveltrue if browser globals that are useful in + development should be predefined, and if debugger + statements and TODO comments + should be allowed. It adds the + same globals as this directive: +
    /*global +
    alert, confirm, console, prompt
    + */
    Be sure to turn this option off before going + into production.
    Tolerate the good parts of ES6es6true if using the good parts of ECMAScript Sixth + Edition. Some ES6 features are still under probation. Some + ES6 features will never be good parts. It adds the + same globals as this directive: +
    /*global +
    ArrayBuffer, DataView, Float32Array, Float64Array, + Int8Array, Int16Array, Int32Array, Intl, Map, Promise, + Proxy, Reflect, Set, Symbol, System, Uint8Array, + Uint8ClampedArray, Uint16Array, Uint32Array, WeakMap, + WeakSet
    + */
    Tolerate evalevaltrue if eval should be allowed. In the + past, the eval function was the most misused + feature of the language.
    Tolerate for statementfortrue if the for statement should be + allowed. It is almost always better to use the array methods + instead.
    Fudge the line and column numbersfudgetrue if the origin of a text file is line 1 column + 1. Programmers know that number systems start at zero. + Unfortunately, most text editors start numbering lines and + columns at one. JSLint will by default start + numbering at zero. Use this option to start the numbering at one + in its reports.
    Maximum number of errorsmaxerrThe maximum number of warnings reported. + (The default is unlimited)
    Maximum line lengthmaxlenThe maximum number of characters in a line. + (The default is unlimited)
    Tolerate multiple variables declarations per statementmultivartrue if a var, let, or + const statement can declare two or more variables + in a single statement.
    Assume Node.jsnodetrue if Node.js globals should be predefined. It + will predefine globals that are used in the Node.js environment. + It adds the same globals as this directive: +
    /*global +
    Buffer, clearImmediate, clearInterval, clearTimeout, console, + exports, global, module, process, querystring, require, setImmediate, + setInterval, setTimeout, __dirname, __filename
    + */
    Tolerate single quote stringssingletrue if 'single quote + should be allowed to enclose string literals.
    Tolerate this
    thistrue if this should be allowed.
    Tolerate whitespace messwhitetrue if the whitespace rules should be ignored.
    +

    For example:

    +
    /*jslint
    +    es6, maxerr: 10, node
    +*/
    + +

    /*property*/

    +

    The /*property*/ directive is used to declare a list of + property identifiers that are used in the file. Each property name in the + program is looked up in this list. If a name is not found, that indicates an + error, most likely a typing error.

    +

    The list can also be used to evade some of JSLint’s naming + rules.

    +

    JSLint can build the /*property*/ list for you. At + the bottom of its report, JSLint displays a + /*property*/ directive. It contains all of the names that were + used with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. You + can copy the /*property*/ directive to the top of your + script file. JSLint will check the spelling of all property + names against the list. That way, you can have JSLint look for + misspellings for you.

    +

    For example,

    +
    /*property +
    charAt, slice, _$_
    + */
    +

    ignore

    +

    JSLint introduces a new reserved word: ignore. It + is used in parameter lists and in catch clauses to indicate a + parameter that will be ignored. Unused warnings will not be produced for + ignore. +

    function handler(ignore, value) {
    +    return do_something_useful(value);
    +}
    +

    var let const

    +

    JavaScript provides three statements for declaring variables: + var, let, and const. The + var statement suffers from a bad practice called hoisting. The + let statement does not do hoisting and respects block scope. + The const statement is like let except that it + marks the variable (but not its contents) as read only, making it an error + to attempt to assign to the variable. When given a choice, + const is the best, var is the worst. Use of + let and const require the es6 option.

    +

    JSLint uses the intersection of the var rules and the + let rules, and by doing so avoids the errors related to either. + A name should be declared only once in a function. It should be declared + before it is used. It should not be used outside of the block in which it is + declared. A variable should not have the same name as a variable or + parameter in an outer function. Do not mix var and + let. Declare one name per statement.

    +

    = == ===

    +

    Fortran made a terrible + mistake in using the equality operator as its assignment operator. That + mistake has been replicated in most languages since then. C compounded that + mistake by making == its equality operator. The visual + similarity is a source of errors. JavaScript compounded this further by + making == a type coercing comparison operator that produces + false positive results. This was mitigated by adding the === + operator, leaving the broken == operator in place.

    +

    JSLint attempts to minimize errors by the following rules:

    +

    == is not allowed. This avoids the false positives, and + increases the visual distance between = and ===. + Assignments are not allowed in expression position, and comparisons are not + allowed in statement position. This also reduces confusion. 

    +

    Semicolon

    +

    JavaScript uses a C-like syntax that requires the use of semicolons to + delimit certain statements. JavaScript attempts to make those semicolons + optional with an automatic semicolon insertion mechanism, but it does not + work very well. Automatic semicolon insertion was added to make + things easier for beginners. Unfortunately, it sometimes fails. Do not rely + on it unless you are a beginner.

    +

    JSLint expects that every statement will be followed by + ; except for for, function, + if, switch, try, and + while. JSLint does not expect to see unnecessary + semicolons, the empty statement, or empty blocks.

    +

    function =>

    +

    JavaScript has four syntactic forms for making function objects: function + statements, function expressions, enhanced object literals, and the + => fart operator.

    +

    The function statement creates a variable and assigns the function object to + it. It should be used in a file or function body, but not inside of a block.

    +
    function name(parameters) { +
    statements
    + }
    +

    The function expression unfortunately looks like the function statement. It + may appear anywhere that an expression may appear, but not in statement + position and not in a loop. It produces a function object but does not + create a variable in which to store it.

    +
    function (parameters) { +
    statements
    + }
    +

    The enhanced object literal provides an ES6 shorthand for creating a property + whose value is a function, saving you from having to type + : colon and function.

    +
    { +
    name(parameters) { +
    statements
    + }
    + }
    +

    Finally, ES6 provides an even shorter form of function expression that leaves + out the words function and return:

    +
    (parameters) => + expression
    +

    JSLint requires the parens around the parameters, and forbids a + { left brace after the + => fart to avoid syntactic ambiguity.

    +

    Comma

    +

    The , comma operator is unnecessary and can + mask programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as + an operator. It does not expect to see elided elements in array literals. A + comma should not appear after the last element of an array literal or object + literal.

    +

    Blocks

    +

    JSLint expects blocks with function, + if, switch, while, for, + do, and try statements and nowhere else.

    +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    +

    JavaScript allows an if to be written like this:

    +
    if (condition)
    +    statement;
    +

    That form is known to contribute to mistakes in projects where many + programmers are working on the same code. That is why JSLint + expects the use of a block:

    +
    if (condition) {
    +    statements;
    +}
    +

    Experience shows that this form is more resilient.

    +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call. All other expression statements are considered + to be errors.

    +

    for

    +

    JSLint does not recommend use of the for statement. + Use array methods like forEach instead. The for + option will suppress some warnings. The forms of for that + JSLint accepts are restricted, excluding the new ES6 forms.

    +

    for in

    +

    JSLint does not recommend use of the for + in statement. Use Object.keys instead.

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, it also + loops through all of the properties that were inherited through the + prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written + without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    for (name in object) {
    +    if (object.hasOwnProperty(name)) {
    +        ....
    +    }
    +}
    +

    Note that the above code will fail if the object contains a property + named hasOwnProperty. Use Object.keys instead.

    +

    switch

    +

    A common error in switch statements is to forget to place a + break statement after each case, resulting in unintended + fall-through. JSLint expects that the statement before the next + case or default is one of these: + break, return, or throw.

    +

    with

    +

    The with statement was intended to provide a shorthand in + accessing properties in deeply nested objects. Unfortunately, it behaves + very badly when setting new properties. Never use the with + statement.

    +

    JSLint does not expect to see a with statement.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that + labels will be distinct from vars and parameters.

    +

    Unreachable Code

    +

    JSLint expects that a return, break, + or throw statement will be followed by a + } right brace or case or + default.

    +

    Confusing Pluses and Minuses

    +

    JSLint expects that + will not be followed by + + or ++, and that - will not be + followed by - or --. A misplaced space can turn + + + into ++, an error that is difficult to see. + Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ increment and + -- decrement operators have been known to + contribute to bad code by encouraging excessive trickiness. They are second + only to faulty architecture in enabling to viruses and other security + menaces. Also, preincrement/postincrement confusion can produce off-by-one + errors that are extremely difficult to diagnose. Fortunately, they are also + complete unnecessary. There are better ways to add 1 to a variable.

    +

    It is best to avoid these operators entirely and rely on += and + -= instead.

    +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. + JSLint looks for problems that may cause portability problems. + It also attempts to resolve visual ambiguities by recommending explicit + escapement.

    +

    JavaScript’s syntax for regular expression literals overloads the + / slash character. To avoid ambiguity, + JSLint expects that the character preceding a regular + expression literal is a ( left paren or + = equal or + : colon or + , comma character.

    +

    this

    +
    Having this in the language makes it harder to talk + about the language. It is like pair programming with Abbott and Costello. +
    +

    Avoid using this. Warnings about this can be + suppressed with option.this.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the + new prefix. The new prefix creates a new object + based on the function's prototype, and binds that object to the + function's implied this parameter. If you neglect to use the + new prefix, no new object will be made and this + will be bound to the global object.

    +

    JSLint enforces the convention that constructor functions be + given names with initial uppercase. JSLint does not expect to + see a function invocation with an initial uppercase name unless it has the + new prefix. JSLint does not expect to see the + new prefix used with functions whose names do not start with + initial uppercase.

    +

    JSLint does not expect to see the wrapper forms + new Number, new String, new Boolean.

    +

    JSLint does not expect to see new Object. + Use Object.create(null) instead.

    +

    Whitespace

    +

    JSLint has a specific set of rules about the use of whitespace. + Where possible, these rules are consistent with centuries of good practice + with literary style.

    +

    The indentation increases by 4 spaces when the last token on a line is + { left brace, + [ left bracket, + ( left paren. The matching closing + token will be the first token on a line, restoring the previous + indentation.

    +

    The ternary operator can be visually confusing, so + ? question mark and + : colon always begin a line and increase + the indentation by 4 spaces.

    +
    return (the_token.id === "(string)" || the_token.id === "(number)")
    +    ? String(the_token.value)
    +    : the_token.id;
    +

    If . period is the first character on a line, the + indentation is increased by 4 spaces.

    +

    A var, let, or const statement will + also cause an indentation if it declares two or more variables, and if the + second variable is not on the same line as the start of the statement.

    +

    Long lines can be continued on the next line with 8 spaces added to the + current indentation. Those 8 spaces do not change the current indentation. + It is 8 to avoid confusion with ordinary indentation.

    +

    The word function is always followed with one space.

    +

    Clauses (case, catch, default, + else, finally) are not statements and so should + not be indented like statements.

    +

    Spaces are used to make things that are not invocations look less like + invocations.

    +

    Tabs and spaces should not be mixed. We should pick just one in order to + avoid the problems that come from having both. Personal preference is an + extremely unreliable criterion. Neither offers a powerful advantage over the + other. Fifty years ago, tab had the advantage of consuming less memory, but + Moore's Law has eliminated that advantage. Space has one clear advantage + over tab: there is no reliable standard for how many spaces a tab + represents, but it is universally accepted that a space occupies a space. So + use spaces. You can edit with tabs if you must, but make sure it is spaces + again before you commit. Maybe someday we will finally get a universal + standard for tabs, but until that day comes, the better choice is spaces.

    +

    Unsafe Characters

    +

    There are characters that are handled inconsistently in some browsers, and + so must be escaped when placed in strings.

    +
    \u0000-\u001f
    +\u007f-\u009f
    +\u00ad
    +\u0600-\u0604
    +\u070f
    +\u17b4
    +\u17b5
    +\u200c-\u200f
    +\u2028-\u202f
    +\u2060-\u206f
    +\ufeff
    +\ufff0-\uffff
    +

    Report

    +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    +
      +
    • The line number on which it starts.
    • +
    • The function’s name. In the case of anonymous functions, + JSLint will attempt to + «guess» the name.
    • +
    • The parameters.
    • +
    • Variables: The variables that are declared in the function.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Exceptions: The variables that are declared by catch + clauses of try statements.
    • +
    • Outer: Variables used by this function that are declared in + other functions.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements. Updates are announced at + https://plus.google.com/communities/104441363299760713736.

    +

    Try it

    +

    Try it. Paste your script + into the window and click the + + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    JSLint is written entirely in JavaScript, so it can run + anywhere that JavaScript (or even Java) can run.

    +

    Perfectly fine

    +

    JSLint was designed to reject code that some would consider to + be perfectly fine. The reason for this is that JSLint's + purpose is to help produce programs that are free of error. That is + difficult in any language and is especially hard in JavaScript. + JSLint attempts to help you increase the visual distance + between correct programs and incorrect programs, making the remaining errors + more obvious. JSLint will give warnings about things that are + not necessarily wrong in the current situation, but which have been observed + to mask or obscure errors. Avoid those things when there are better options + available.

    +

    It is dangerous out there. JSLint is here to help.

    +

    Warning

    +

    JSLint will hurt your feelings. Side effects may include + headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, + dry mouth, cleaner code, and a reduced error rate.

    +

    Further Reading

    + +
    +

    + Code Conventions for the JavaScript Programming Language.

    + + + + + + + +
    + + diff --git a/branch.f4a2c5bb/jslint.html b/branch.f4a2c5bb/jslint.html new file mode 100644 index 000000000..de83df9e1 --- /dev/null +++ b/branch.f4a2c5bb/jslint.html @@ -0,0 +1,416 @@ + + + + + + +JSLint: The JavaScript Code Quality Tool + + + + + + + +
    JSLint
    +
    +
    +
    + +
    Source + +
    +
    + + +
    + + + + +
    Options +
    Assume... +
    +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    Number... +
    +
    +
    +
    Global variables... + +
    +
    + + + +
    +
    + + + + + + + +
    + diff --git a/branch.f4a2c5bb/jslint.js b/branch.f4a2c5bb/jslint.js new file mode 100644 index 000000000..c6615b627 --- /dev/null +++ b/branch.f4a2c5bb/jslint.js @@ -0,0 +1,4995 @@ +// jslint.js +// 2017-06-27 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// jslint(source, option_object, global_array) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze, a string or an array of strings. +// option_object An object whose keys correspond to option names. +// global_array An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all of the functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// 1. If the source is a single string, split it into an array of strings. +// 2. Turn the source into an array of tokens. +// 3. Furcate the tokens into a parse tree. +// 4. Walk the tree, traversing all of the nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// 5. Check the whitespace between the tokens. + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*jslint bitwise*/ + +/*property + a, and, arity, assign, b, bad_assignment_a, bad_directive_a, bad_get, + bad_module_name_a, bad_option_a, bad_property_a, bad_set, bitwise, block, + body, browser, c, calls, catch, charAt, charCodeAt, closer, closure, code, + column, complex, concat, constant, context, couch, create, d, dead, + default, devel, directive, directives, disrupt, dot, duplicate_a, edition, + ellipsis, else, empty_block, es6, escape_mega, eval, every, expected_a, + expected_a_at_b_c, expected_a_b, expected_a_b_from_c_d, + expected_a_before_b, expected_a_next_at_b, expected_digits_after_a, + expected_four_digits, expected_identifier_a, expected_line_break_a_b, + expected_regexp_factor_a, expected_space_a_b, expected_statements_a, + expected_string_a, expected_type_string_a, exports, expression, extra, + finally, flag, for, forEach, free, from, froms, fud, fudge, function, + function_in_loop, functions, g, global, i, id, identifier, import, inc, + indexOf, infix_in, init, initial, isArray, isNaN, join, json, keys, label, + label_a, lbp, led, length, level, line, lines, live, loop, m, margin, match, + maxerr, maxlen, message, misplaced_a, misplaced_directive_a, + missing_browser, missing_m, module, multivar, naked_block, name, names, + nested_comment, new, node, not_label_a, nr, nud, number_isNaN, ok, open, + option, out_of_scope_a, + parameters, pop, property, push, qmark, quote, redefinition_a_b, replace, + required_a_optional_b, reserved_a, right, role, search, signature, single, + slice, some, sort, split, statement, stop, strict, subscript_a, switch, + test, this, thru, toString, todo_comment, tokens, too_long, too_many, + too_many_digits, tree, try, type, u, unclosed_comment, unclosed_mega, + unclosed_string, undeclared_a, unexpected_a, unexpected_a_after_b, + unexpected_a_before_b, unexpected_at_top_level_a, unexpected_char_a, + unexpected_comment, unexpected_directive_a, unexpected_expression_a, + unexpected_label_a, unexpected_parens, unexpected_space_a_b, + unexpected_statement_a, unexpected_trailing_space, unexpected_typeof_a, + uninitialized_a, unreachable_a, unregistered_property_a, unsafe, unused_a, + use_double, use_spaces, use_strict, used, value, var_loop, var_switch, + variable, warning, warnings, weird_condition_a, weird_expression_a, + weird_loop, weird_relation_a, white, wrap_assignment, wrap_condition, + wrap_immediate, wrap_parameter, wrap_regexp, wrap_unary, wrapped, writable, + y +*/ + +var jslint = (function JSLint() { + "use strict"; + + function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than {} because confusions around accidental method names like +// "constructor" are completely avoided. + + return Object.create(null); + } + + function populate(object, array, value) { + +// Augment an object by taking property names from an array of strings. + + array.forEach(function (name) { + object[name] = value; + }); + } + + var allowed_option = { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + bitwise: true, + browser: [ + "Audio", + "clearInterval", + "clearTimeout", + "document", + "event", + "FileReader", + "FormData", + "history", + "Image", + "localStorage", + "location", + "name", + "navigator", + "Option", + "screen", + "sessionStorage", + "setInterval", + "setTimeout", + "Storage", + "XMLHttpRequest" + ], + couch: [ + "emit", "getRow", "isArray", "log", "provides", "registerType", + "require", "send", "start", "sum", "toJSON" + ], + devel: [ + "alert", "confirm", "console", "prompt" + ], + es6: [ + "ArrayBuffer", "DataView", "Float32Array", "Float64Array", + "Generator", "GeneratorFunction", "Int8Array", "Int16Array", + "Int32Array", "Intl", "Map", "Promise", "Proxy", "Reflect", + "Set", "Symbol", "System", "Uint8Array", "Uint8ClampedArray", + "Uint16Array", "Uint32Array", "WeakMap", "WeakSet" + ], + eval: true, + for: true, + fudge: true, + maxerr: 10000, + maxlen: 10000, + multivar: true, + node: [ + "Buffer", "clearImmediate", "clearInterval", "clearTimeout", + "console", "exports", "global", "module", "process", "querystring", + "require", "setImmediate", "setInterval", "setTimeout", + "__dirname", "__filename" + ], + single: true, + this: true, + white: true + }; + + var spaceop = { + +// This is the set of infix operators that require a space on each side. + + "!=": true, + "!==": true, + "%": true, + "%=": true, + "&": true, + "&=": true, + "&&": true, + "*": true, + "*=": true, + "+=": true, + "-=": true, + "/": true, + "/=": true, + "<": true, + "<=": true, + "<<": true, + "<<=": true, + "=": true, + "==": true, + "===": true, + "=>": true, + ">": true, + ">=": true, + ">>": true, + ">>=": true, + ">>>": true, + ">>>=": true, + "^": true, + "^=": true, + "|": true, + "|=": true, + "||": true + }; + + var bitwiseop = { + +// These are the bitwise operators. + + "~": true, + "^": true, + "^=": true, + "&": true, + "&=": true, + "|": true, + "|=": true, + "<<": true, + "<<=": true, + ">>": true, + ">>=": true, + ">>>": true, + ">>>=": true + }; + + var opener = { + +// The open and close pairs. + + "(": ")", // paren + "[": "]", // bracket + "{": "}", // brace + "${": "}" // mega + }; + + var relationop = { + +// The relational operators. + + "!=": true, + "!==": true, + "==": true, + "===": true, + "<": true, + "<=": true, + ">": true, + ">=": true + }; + + var standard = [ + +// These are the globals that are provided by the ES5 language standard. + + "Array", "Boolean", "Date", "decodeURI", "decodeURIComponent", + "encodeURI", "encodeURIComponent", "Error", "EvalError", "isFinite", + "JSON", "Math", "Number", "Object", "parseInt", "parseFloat", + "RangeError", "ReferenceError", "RegExp", "String", "SyntaxError", + "TypeError", "URIError" + ]; + + var bundle = { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + and: "The '&&' subexpression should be wrapped in parens.", + bad_assignment_a: "Bad assignment to '{a}'.", + bad_directive_a: "Bad directive '{a}'.", + bad_get: "A get function takes no parameters.", + bad_module_name_a: "Bad module name '{a}'.", + bad_option_a: "Bad option '{a}'.", + bad_property_a: "Bad property name '{a}'.", + bad_set: "A set function takes one parameter.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + es6: "Unexpected ES6 feature '{a}'.", + escape_mega: "Unexpected escapement in mega literal.", + expected_a: "Expected '{a}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.", + expected_a_before_b: "Expected '{a}' before '{b}'.", + expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.", + expected_digits_after_a: "Expected digits after '{a}'.", + expected_four_digits: "Expected four digits after '\\u'.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.", + expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.", + expected_space_a_b: "Expected one space between '{a}' and '{b}'.", + expected_statements_a: "Expected statements before '{a}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + expected_type_string_a: "Expected a type string and instead saw '{a}'.", + function_in_loop: "Don't make functions within a loop.", + infix_in: "Unexpected 'in'. Compare with undefined, or use the hasOwnProperty method instead.", + isNaN: "Use the isNaN function to compare with NaN.", + label_a: "'{a}' is a statement label.", + misplaced_a: "Place '{a}' at the outermost level.", + misplaced_directive_a: "Place the '/*{a}*/' directive before the first statement.", + missing_browser: "/*global*/ requires the Assume a browser option.", + missing_m: "Expected 'm' flag on a multiline regular expression.", + naked_block: "Naked block.", + nested_comment: "Nested comment.", + not_label_a: "'{a}' is not a label.", + number_isNaN: "Use Number.isNaN function to compare with NaN.", + out_of_scope_a: "'{a}' is out of scope.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + required_a_optional_b: "Required parameter '{a}' after optional parameter '{b}'.", + reserved_a: "Reserved name '{a}'.", + subscript_a: "['{a}'] is better written in dot notation.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line too long.", + too_many: "Too many warnings.", + too_many_digits: "Too many digits.", + unclosed_comment: "Unclosed comment.", + unclosed_mega: "Unclosed mega literal.", + unclosed_string: "Unclosed string.", + undeclared_a: "Undeclared '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_a_after_b: "Unexpected '{a}' after '{b}'.", + unexpected_a_before_b: "Unexpected '{a}' before '{b}'.", + unexpected_at_top_level_a: "Expected '{a}' to be in a function.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_directive_a: "When using modules, don't use directive '/*{a}'.", + unexpected_expression_a: "Unexpected expression '{a}' in statement position.", + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_parens: "Don't wrap function literals in parens.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_statement_a: "Unexpected statement '{a}' in expression position.", + unexpected_trailing_space: "Unexpected trailing space.", + unexpected_typeof_a: "Unexpected 'typeof'. Use '===' to compare directly with {a}.", + uninitialized_a: "Uninitialized '{a}'.", + unreachable_a: "Unreachable '{a}'.", + unregistered_property_a: "Unregistered property name '{a}'.", + unsafe: "Unsafe character '{a}'.", + unused_a: "Unused '{a}'.", + use_double: "Use double quotes, not single quotes.", + use_spaces: "Use spaces, not tabs.", + use_strict: "This function needs a \"use strict\" pragma.", + var_loop: "Don't declare variables in a loop.", + var_switch: "Don't declare variables in a switch.", + weird_condition_a: "Weird condition '{a}'.", + weird_expression_a: "Weird expression '{a}'.", + weird_loop: "Weird loop.", + weird_relation_a: "Weird relation '{a}'.", + wrap_assignment: "Don't wrap assignment statements in parens.", + wrap_condition: "Wrap the condition in parens.", + wrap_immediate: "Wrap an immediate function invocation in " + + "parentheses to assist the reader in understanding that the " + + "expression is the result of a function, and not the " + + "function itself.", + wrap_parameter: "Wrap the parameter in parens.", + wrap_regexp: "Wrap this regexp in parens to avoid confusion.", + wrap_unary: "Wrap the unary expression in parens." + }; + +// Regular expression literals: + +// supplant {variables} + var rx_supplant = /\{([^{}]*)\}/g; +// carriage return, carriage return linefeed, or linefeed + var rx_crlf = /\n|\r\n?/; +// unsafe characters that are silently deleted by one or more browsers + var rx_unsafe = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; +// identifier + var rx_identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; + var rx_module = /^[a-zA-Z0-9_$:.@\-\/]+$/; + var rx_bad_property = /^_|\$|Sync\$|_$/; +// star slash + var rx_star_slash = /\*\//; +// slash star + var rx_slash_star = /\/\*/; +// slash star or ending slash + var rx_slash_star_or_slash = /\/\*|\/$/; +// uncompleted work comment + var rx_todo = /\b(?:todo|TO\s?DO|HACK)\b/; +// tab + var rx_tab = /\t/g; +// directive + var rx_directive = /^(jslint|property|global)\s+(.*)$/; + var rx_directive_part = /^([a-zA-Z$_][a-zA-Z0-9$_]*)\s*(?::\s*(true|false|[0-9]+)\s*)?(?:,\s*)?(.*)$/; +// token (sorry it is so long) + var rx_token = /^((\s+)|([a-zA-Z_$][a-zA-Z0-9_$]*)|[(){}\[\]?,:;'"~`]|=(?:==?|>)?|\.+|\/[=*\/]?|\*[\/=]?|\+(?:=|\++)?|-(?:=|-+)?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<= "a" && string <= "z\uffff") + || (string >= "A" && string <= "Z\uffff") + ); + } + + function supplant(string, object) { + return string.replace(rx_supplant, function (found, filling) { + var replacement = object[filling]; + return (replacement !== undefined) + ? replacement + : found; + }); + } + + var anon = "anonymous"; // The guessed name for anonymous functions. + var blockage; // The current block. + var block_stack; // The stack of blocks. + var declared_globals; // The object containing the global declarations. + var directives; // The directive comments. + var directive_mode; // true if directives are still allowed. + var early_stop; // true if JSLint cannot finish. + var exports; // The exported names and values. + var froms; // The array collecting all import-from strings. + var fudge; // true if the natural numbers start with 1. + var functionage; // The current function. + var functions; // The array containing all of the functions. + var global; // The global object; the outermost context. + var json_mode; // true if parsing JSON. + var lines; // The array containing source lines. + var module_mode; // true if import or export was used. + var next_token; // The next token to be examined in the parse. + var option; // The options parameter. + var property; // The object containing the tallied property names. + var mega_mode; // true if currently parsing a megastring literal. + var stack; // The stack of functions. + var syntax; // The object containing the parser. + var token; // The current token being examined in the parse. + var token_nr; // The number of the next token. + var tokens; // The array of tokens. + var tenure; // The predefined property registry. + var tree; // The abstract parse tree. + var var_mode; // true if using var; false if using let. + var warnings; // The array collecting all generated warnings. + +// Error reportage functions: + + function artifact(the_token) { + +// Return a string representing an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id; + } + + function artifact_line(the_token) { + +// Return the fudged line number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.line + fudge; + } + + function artifact_column(the_token) { + +// Return the fudged column number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.from + fudge; + } + + function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + var warning = { // ~~ + name: "JSLintError", + column: column, + line: line, + code: code + }; + if (a !== undefined) { + warning.a = a; + } + if (b !== undefined) { + warning.b = b; + } + if (c !== undefined) { + warning.c = c; + } + if (d !== undefined) { + warning.d = d; + } + warning.message = supplant(bundle[code] || code, warning); + warnings.push(warning); + return ( + typeof option.maxerr === "number" + && warnings.length === option.maxerr + ) ? stop_at("too_many", line, column) + : warning; + } + + function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); + } + + function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + if (the_token.warning === undefined) { + the_token.warning = warn_at( + code, + the_token.line, + the_token.from, + a || artifact(the_token), + b, + c, + d + ); + return the_token.warning; + } + } + + function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); + } + +// Tokenize: + + function tokenize(source) { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + +// If the source is not an array, then it is split into lines at the +// carriage return/linefeed. + + lines = (Array.isArray(source)) + ? source + : source.split(rx_crlf); + tokens = []; + + var char; // a popular character + var column = 0; // the column number of the next character + var first; // the first token + var from; // the starting column number of the token + var line = -1; // the line number of the next character + var nr = 0; // the next token number + var previous = global; // the previous token including comments + var prior = global; // the previous token excluding comments + var mega_from; // the starting column of megastring + var mega_line; // the starting line of megastring + var snippet; // a piece of string + var source_line; // the current line source string + + function next_line() { + +// Put the next line of source in source_line. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + var at; + column = 0; + line += 1; + source_line = lines[line]; + if (source_line !== undefined) { + at = source_line.search(rx_tab); + if (at >= 0) { + if (!option.white) { + warn_at("use_spaces", line, at + 1); + } + source_line = source_line.replace(rx_tab, " "); + } + at = source_line.search(rx_unsafe); + if (at >= 0) { + warn_at( + "unsafe", + line, + column + at, + "U+" + source_line.charCodeAt(at).toString(16) + ); + } + if (option.maxlen && option.maxlen < source_line.length) { + warn_at("too_long", line, source_line.length); + } else if (!option.white && source_line.slice(-1) === " ") { + warn_at( + "unexpected_trailing_space", + line, + source_line.length - 1 + ); + } + } + return source_line; + } + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions snip, next_char, prev_char, +// some_digits, and escape help in the parsing of literals. + + function snip() { + +// Remove the last character from snippet. + + snippet = snippet.slice(0, -1); + } + + function next_char(match) { + +// Get the next character from the source line. Remove it from the source_line, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + return stop_at( + (char === "") + ? "expected_a" + : "expected_a_b", + line, + column - 1, + match, + char + ); + } + if (source_line) { + char = source_line.charAt(0); + source_line = source_line.slice(1); + snippet += char; + } else { + char = ""; + snippet += " "; + } + column += 1; + return char; + } + + function back_char() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the source_line. + + if (snippet) { + char = snippet.slice(-1); + source_line = char + source_line; + column -= 1; + snip(); + } else { + char = ""; + } + return char; + } + + function some_digits(rx, quiet) { + var result = source_line.match(rx); + if (result) { + char = result[1]; + column += char.length; + source_line = result[2]; + snippet += char; + } else { + char = ""; + if (!quiet) { + warn_at( + "expected_digits_after_a", + line, + column, + snippet + ); + } + } + return char.length; + } + + function escape(extra) { + switch (next_char("\\")) { + case "\\": + case "/": + case "b": + case "f": + case "n": + case "r": + case "t": + break; + case "u": + if (next_char("u") === "{") { + if (json_mode) { + warn_at("unexpected_a", line, column - 1, char); + } + if (some_digits(rx_hexs) > 5) { + warn_at("too_many_digits", line, column - 1); + } + if (!option.es6) { + warn_at("es6", line, column, "u{"); + } + if (next_char() !== "}") { + stop_at("expected_a_before_b", line, column, "}", char); + } + next_char(); + return; + } + back_char(); + if (some_digits(rx_hexs, true) < 4) { + warn_at("expected_four_digits", line, column - 1); + } + break; + case "": + return stop_at("unclosed_string", line, column); + default: + if (!extra || extra.indexOf(char) < 0) { + warn_at( + "unexpected_a_before_b", + line, + column - 2, + "\\", + char + ); + } + } + next_char(); + } + + function make(id, value, identifier) { + +// Make the token object and append it to the tokens list. + + var the_token = { + from: from, + id: id, + identifier: !!identifier, + line: line, + nr: nr, + thru: column + }; + tokens[nr] = the_token; + nr += 1; + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + directive_mode = false; + } + +// If the token is to have a value, give it one. + + if (value !== undefined) { + the_token.value = value; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option.white. + + if ( + previous.line === line + && previous.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (previous.id === "(comment)" || previous.id === "(regexp)") + ) { + warn( + "expected_space_a_b", + the_token, + artifact(previous), + artifact(the_token) + ); + } + if (previous.id === "." && id === "(number)") { + warn("expected_a_before_b", previous, "0", "."); + } + if (prior.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// The previous token is used to detect adjacency problems. + + previous = the_token; + +// The prior token is a previous token that was not a comment. The prior token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (previous.id !== "(comment)") { + prior = previous; + } + return the_token; + } + + function parse_directive(the_comment, body) { + +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + + var result = body.match(rx_directive_part); + if (result) { + var allowed; + var name = result[1]; + var value = result[2]; + switch (the_comment.directive) { + case "jslint": + allowed = allowed_option[name]; + switch (typeof allowed) { + case "boolean": + case "object": + switch (value) { + case "true": + case "": + case undefined: + option[name] = true; + if (Array.isArray(allowed)) { + populate(declared_globals, allowed, false); + } + break; + case "false": + option[name] = false; + break; + default: + warn( + "bad_option_a", + the_comment, + name + ":" + value + ); + } + break; + case "number": + if (isFinite(+value)) { + option[name] = +value; + } else { + warn( + "bad_option_a", + the_comment, + name + ":" + value + ); + } + break; + default: + warn("bad_option_a", the_comment, name); + } + break; + case "property": + if (tenure === undefined) { + tenure = empty(); + } + tenure[name] = true; + break; + case "global": + if (value) { + warn("bad_option_a", the_comment, name + ":" + value); + } + declared_globals[name] = false; + module_mode = the_comment; + break; + } + return parse_directive(the_comment, result[3]); + } + if (body) { + return stop("bad_directive_a", the_comment, body); + } + } + + function comment(snippet) { + +// Make a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + + var the_comment = make("(comment)", snippet); + if (Array.isArray(snippet)) { + snippet = snippet.join(" "); + } + if (!option.devel && rx_todo.test(snippet)) { + warn("todo_comment", the_comment); + } + var result = snippet.match(rx_directive); + if (result) { + if (!directive_mode) { + warn_at("misplaced_directive_a", line, from, result[1]); + } else { + the_comment.directive = result[1]; + parse_directive(the_comment, result[2]); + } + directives.push(the_comment); + } + return the_comment; + } + + function regexp() { + +// Parse a regular expression literal. + + var multi_mode = false; + var result; + var value; + + function quantifier() { + +// Match an optional quantifier. + + switch (char) { + case "?": + case "*": + case "+": + next_char(); + break; + case "{": + if (some_digits(rx_digits, true) === 0) { + warn_at("expected_a", line, column, "0"); + } + if (next_char() === ",") { + some_digits(rx_digits, true); + next_char(); + } + next_char("}"); + break; + default: + return; + } + if (char === "?") { + next_char("?"); + } + } + + function subklass() { + +// Match a character in a character class. + + switch (char) { + case "\\": + escape("BbDdSsWw-[]^"); + return true; + case "[": + case "]": + case "/": + case "^": + case "-": + case "": + return false; + case "`": + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char(); + return true; + case " ": + warn_at("expected_a_before_b", line, column, "\\", " "); + next_char(); + return true; + default: + next_char(); + return true; + } + } + + function ranges() { + +// Match a range of subclasses. + + if (subklass()) { + if (char === "-") { + next_char("-"); + if (!subklass()) { + return stop_at( + "unexpected_a", + line, + column - 1, + "-" + ); + } + } + return ranges(); + } + } + + function klass() { + +// Match a class. + + next_char("["); + if (char === "^") { + next_char("^"); + } + (function classy() { + ranges(); + if (char !== "]" && char !== "") { + warn_at( + "expected_a_before_b", + line, + column, + "\\", + char + ); + next_char(); + return classy(); + } + }()); + next_char("]"); + } + + function choice() { + + function group() { + +// Match a group that starts with left paren. + + next_char("("); + if (char === "?") { + next_char("?"); + switch (char) { + case ":": + case "=": + case "!": + next_char(); + break; + default: + next_char(":"); + } + } else if (char === ":") { + warn_at("expected_a_before_b", line, column, "?", ":"); + } + choice(); + next_char(")"); + } + + function factor() { + switch (char) { + case "[": + klass(); + return true; + case "\\": + escape("BbDdSsWw^${}[]():=!.-|*+?"); + return true; + case "(": + group(); + return true; + case "?": + case "+": + case "*": + case "}": + case "{": + warn_at("expected_a_before_b", line, column - 1, "\\", char); + break; + case "/": + case "|": + case "]": + case ")": + case "": + return false; + case "`": + if (mega_mode) { + warn_at("unexpected_a", line, column - 1, "`"); + } + break; + case " ": + warn_at( + "expected_a_b", + line, + column - 1, + "\\s", + " " + ); + break; + case "$": + if (source_line.charAt(0) !== "/") { + multi_mode = true; + } + break; + case "^": + if (snippet !== "^") { + multi_mode = true; + } + break; + } + next_char(); + return true; + } + + function sequence(follow) { + if (factor()) { + quantifier(); + return sequence(true); + } + if (!follow) { + warn_at("expected_regexp_factor_a", line, column, char); + } + } + +// Match a choice (a sequence that can be followed by | and another choice). + + sequence(); + if (char === "|") { + next_char("|"); + return choice(); + } + } + +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + next_char(); + if (char === "=") { + warn_at("expected_a_before_b", line, column, "\\", "="); + } + choice(); + +// Make sure there is a closing slash. + + snip(); + value = snippet; + next_char("/"); + +// Process dangling flag letters. + + var allowed = { + g: true, + i: true, + m: true, + u: 6, + y: 6 + }; + var flag = empty(); + (function make_flag() { + if (is_letter(char)) { + switch (allowed[char]) { + case true: + break; + case 6: + if (!option.es6) { + warn_at("es6", line, column, char); + } + break; + default: + warn_at("unexpected_a", line, column, char); + } + allowed[char] = false; + flag[char] = true; + next_char(); + return make_flag(); + } + }()); + back_char(); + if (char === "/" || char === "*") { + return stop_at("unexpected_a", line, from, char); + } + result = make("(regexp)", char); + result.flag = flag; + result.value = value; + if (multi_mode && !flag.m) { + warn_at("missing_m", line, column); + } + return result; + } + + function string(quote) { + +// Make a string token. + + var the_token; + snippet = ""; + next_char(); + + return (function next() { + switch (char) { + case quote: + snip(); + the_token = make("(string)", snippet); + the_token.quote = quote; + return the_token; + case "\\": + escape(quote); + break; + case "": + return stop_at("unclosed_string", line, column); + case "`": + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char("`"); + break; + default: + next_char(); + } + return next(); + }()); + } + + function frack() { + if (char === ".") { + some_digits(rx_digits); + next_char(); + } + if (char === "E" || char === "e") { + next_char(); + if (char !== "+" && char !== "-") { + back_char(); + } + some_digits(rx_digits); + next_char(); + } + } + + function number() { + if (snippet === "0") { + switch (next_char()) { + case ".": + frack(); + break; + case "b": + some_digits(rx_bits); + next_char(); + break; + case "o": + some_digits(rx_octals); + next_char(); + break; + case "x": + some_digits(rx_hexs); + next_char(); + break; + } + } else { + next_char(); + frack(); + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") || + (char >= "a" && char <= "z") || + (char >= "A" && char <= "Z") + ) { + return stop_at( + "unexpected_a_after_b", + line, + column - 1, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + back_char(); + return make("(number)", snippet); + } + + function lex() { + var array; + var i = 0; + var j = 0; + var last; + var result; + var the_token; + if (!source_line) { + source_line = next_line(); + from = 0; + return (source_line === undefined) + ? (mega_mode) + ? stop_at("unclosed_mega", mega_line, mega_from) + : make("(end)") + : lex(); + } + from = column; + result = source_line.match(rx_token); + +// result[1] token +// result[2] whitespace +// result[3] identifier +// result[4] number +// result[5] rest + + if (!result) { + return stop_at( + "unexpected_char_a", + line, + column, + source_line.charAt(0) + ); + } + + snippet = result[1]; + column += snippet.length; + source_line = result[5]; + +// Whitespace was matched. Call lex again to get more. + + if (result[2]) { + return lex(); + } + +// The token is an identifier. + + if (result[3]) { + return make(snippet, undefined, true); + } + +// The token is a number. + + if (result[4]) { + return number(snippet); + } + +// The token is something miscellaneous. + + switch (snippet) { + +// The token is a single or double quote string. + + case "\"": + return string(snippet); + + case "'": + if (!option.single) { + warn_at("use_double", line, column); + } + return string(snippet); + +// The token is a megastring. We don't allow any kind of mega nesting. + + case "`": + if (mega_mode) { + return stop_at("expected_a_b", line, column, "}", "`"); + } + snippet = ""; + mega_from = from; + mega_line = line; + mega_mode = true; + +// Parsing a mega literal is tricky. First make a ` token. + + make("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + (function part() { + var at = source_line.search(rx_mega); + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + if (at < 0) { + snippet += source_line + "\n"; + return (next_line() === undefined) + ? stop_at("unclosed_mega", mega_line, mega_from) + : part(); + } + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + snippet += source_line.slice(0, at); + column += at; + source_line = source_line.slice(at); + if (source_line.charAt(0) === "\\") { + stop_at("escape_mega", line, at); + } + make("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then make tokens that will become part of an expression until +// a } token is made. + + if (source_line.charAt(0) === "$") { + column += 2; + make("${"); + source_line = source_line.slice(2); + (function expr() { + var id = lex().id; + if (id === "{") { + return stop_at( + "expected_a_b", + line, + column, + "}", + "{" + ); + } + if (id !== "}") { + return expr(); + } + }()); + return part(); + } + }()); + source_line = source_line.slice(1); + column += 1; + mega_mode = false; + return make("`"); + +// The token is a // comment. + + case "//": + snippet = source_line; + source_line = ""; + the_token = comment(snippet); + if (mega_mode) { + warn("unexpected_comment", the_token, "`"); + } + return the_token; + +// The token is a /* comment. + + case "/*": + array = []; + if (source_line.charAt(0) === "/") { + warn_at("unexpected_a", line, column + i, "/"); + } + (function next() { + if (source_line > "") { + i = source_line.search(rx_star_slash); + if (i >= 0) { + return; + } + j = source_line.search(rx_slash_star); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + } + array.push(source_line); + source_line = next_line(); + if (source_line === undefined) { + return stop_at("unclosed_comment", line, column); + } + return next(); + }()); + snippet = source_line.slice(0, i); + j = snippet.search(rx_slash_star_or_slash); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + array.push(snippet); + column += i + 2; + source_line = source_line.slice(i + 2); + return comment(array); + +// The token is a slash. + + case "/": + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + if (prior.identifier) { + if (!prior.dot) { + switch (prior.id) { + case "return": + return regexp(); + case "(begin)": + case "case": + case "delete": + case "in": + case "instanceof": + case "new": + case "typeof": + case "void": + case "yield": + the_token = regexp(); + return stop("unexpected_a", the_token); + } + } + } else { + last = prior.id.charAt(prior.id.length - 1); + if ("(,=:?[".indexOf(last) >= 0) { + return regexp(); + } + if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) { + the_token = regexp(); + warn("wrap_regexp", the_token); + return the_token; + } + } + if (source_line.charAt(0) === "/") { + column += 1; + source_line = source_line.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + break; + } + return make(snippet); + } + + first = lex(); + json_mode = first.id === "{" || first.id === "["; + +// This is the only loop in JSLint. It will turn into a recursive call to lex +// when ES6 has been finished and widely deployed and adopted. + + while (true) { + if (lex().id === "(end)") { + break; + } + } + } + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + + function survey(name) { + var id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!rx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!rx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property[id] === "number") { + property[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (tenure !== undefined) { + if (tenure[id] !== true) { + warn("unregistered_property_a", name); + } + } else { + if (name.identifier && rx_bad_property.test(id)) { + warn("bad_property_a", name); + } + } + property[id] = 1; + } + return id; + } + + function dispense() { + +// Deliver the next token, skipping the comments. + + var cadet = tokens[token_nr]; + token_nr += 1; + if (cadet.id === "(comment)") { + if (json_mode) { + warn("unexpected_a", cadet); + } + return dispense(); + } else { + return cadet; + } + } + + function lookahead() { + +// Look ahead one token without advancing. + + var old_token_nr = token_nr; + var cadet = dispense(true); + token_nr = old_token_nr; + return cadet; + } + + function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if (token.identifier && token.id !== "function") { + anon = token.id; + } else if (token.id === "(string)" && rx_identifier.test(token.value)) { + anon = token.value; + } + +// Attempt to match next_token with an expected id. + + if (id !== undefined && next_token.id !== id) { + return (match === undefined) + ? stop("expected_a_b", next_token, id, artifact()) + : stop( + "expected_a_b_from_c_d", + next_token, + id, + artifact(match), + artifact_line(match), + artifact(next_token) + ); + } + +// Promote the tokens, skipping comments. + + token = next_token; + next_token = dispense(); + if (next_token.id === "(end)") { + token_nr -= 1; + } + } + +// Parsing of JSON is simple: + + function json_value() { + var negative; + + function json_object() { + var brace = next_token; + var object = empty(); + var properties = []; + brace.expression = properties; + advance("{"); + if (next_token.id !== "}") { + (function next() { + var name; + var value; + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + name = next_token; + advance("(string)"); + if (object[token.value] !== undefined) { + warn("duplicate_a", token); + } else if (token.value === "__proto__") { + warn("bad_property_a", token); + } else { + object[token.value] = token; + } + advance(":"); + value = json_value(); + value.label = name; + properties.push(value); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("}", brace); + return brace; + } + + function json_array() { + var bracket = next_token; + var elements = []; + bracket.expression = elements; + advance("["); + if (next_token.id !== "]") { + (function next() { + elements.push(json_value()); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]", bracket); + return bracket; + } + + switch (next_token.id) { + case "{": + return json_object(); + case "[": + return json_array(); + case "true": + case "false": + case "null": + advance(); + return token; + case "(number)": + if (!rx_JSON_number.test(next_token.value)) { + warn("unexpected_a"); + } + advance(); + return token; + case "(string)": + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + advance(); + return token; + case "-": + negative = next_token; + negative.arity = "unary"; + advance("-"); + advance("(number)"); + negative.expression = token; + return negative; + default: + stop("unexpected_a"); + } + } + +// Now we parse JavaScript. + + function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + var id = name.id; + +// Reserved words may not be enrolled. + + if (syntax[id] !== undefined && id !== "ignore") { + warn("reserved_a", name); + } else { + +// Has the name been enrolled in this context? + + var earlier = functionage.context[id]; + if (earlier) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + +// Has the name been enrolled in an outer context? + + } else { + stack.forEach(function (value) { + var item = value.context[id]; + if (item !== undefined) { + earlier = item; + } + }); + if (earlier) { + if (id === "ignore") { + if (earlier.role === "variable") { + warn("unexpected_a", name); + } + } else { + if (( + role !== "exception" || + earlier.role !== "exception" + ) && role !== "parameter") { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + } + } + } + +// Enroll it. + + functionage.context[id] = name; + name.dead = true; + name.function = functionage; + name.init = false; + name.role = role; + name.used = 0; + name.writable = !readonly; + } + } + } + + function expression(rbp, initial) { + +// This is the heart of the Pratt parser. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// nud Null denotation +// led Left denotation +// lbp Left binding power +// rbp Right binding power + +// It processes a nud (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop. It +// returns the expression's parse tree. + + var left; + var the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement, + + if (!initial) { + advance(); + } + the_symbol = syntax[token.id]; + if (the_symbol !== undefined && the_symbol.nud !== undefined) { + left = the_symbol.nud(); + } else if (token.identifier) { + left = token; + left.arity = "variable"; + } else { + return stop("unexpected_a", token); + } + (function right() { + the_symbol = syntax[next_token.id]; + if ( + the_symbol !== undefined && + the_symbol.led !== undefined && + rbp < the_symbol.lbp + ) { + advance(); + left = the_symbol.led(left); + return right(); + } + }()); + return left; + } + + function condition() { + +// Parse the condition part of a do, if, while. + + var the_paren = next_token; + var the_value; + the_paren.free = true; + advance("("); + the_value = expression(0); + advance(")"); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + switch (the_value.id) { + case "?": + case "~": + case "&": + case "|": + case "^": + case "<<": + case ">>": + case ">>>": + case "+": + case "-": + case "*": + case "/": + case "%": + case "typeof": + case "(number)": + case "(string)": + warn("unexpected_a", the_value); + break; + } + return the_value; + } + + function is_weird(thing) { + return ( + thing.id === "(regexp)" || + thing.id === "{" || + thing.id === "=>" || + thing.id === "function" || + (thing.id === "[" && thing.arity === "unary") + ); + } + + function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + return ( + Array.isArray(b) && + a.length === b.length && + a.every(function (value, index) { + return are_similar(value, b[index]); + }) + ); + } + if (Array.isArray(b)) { + return false; + } + if (a.id === "(number)" && b.id === "(number)") { + return a.value === b.value; + } + var a_string; + var b_string; + if (a.id === "(string)") { + a_string = a.value; + } else if (a.id === "`" && a.constant) { + a_string = a.value[0]; + } + if (b.id === "(string)") { + b_string = b.value; + } else if (b.id === "`" && b.constant) { + b_string = b.value[0]; + } + if (typeof a_string === "string") { + return a_string === b_string; + } + if (is_weird(a) || is_weird(b)) { + return false; + } + if (a.arity === b.arity && a.id === b.id) { + if (a.id === ".") { + return ( + are_similar(a.expression, b.expression) + && are_similar(a.name, b.name) + ); + } + switch (a.arity) { + case "unary": + return are_similar(a.expression, b.expression); + case "binary": + return ( + a.id !== "(" + && are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + ); + case "ternary": + return ( + are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + && are_similar(a.expression[2], b.expression[2]) + ); + case "function": + case "regexp": + return false; + default: + return true; + } + } + return false; + } + + function semicolon() { + +// Try to match a semicolon. + + if (next_token.id === ";") { + advance(";"); + } else { + warn_at( + "expected_a_b", + token.line, + token.thru, + ";", + artifact(next_token) + ); + } + anon = "anonymous"; + } + + function statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + var first; + var the_label; + var the_statement; + var the_symbol; + advance(); + if (token.identifier && next_token.id === ":") { + the_label = token; + if (the_label.id === "ignore") { + warn("unexpected_a", the_label); + } + advance(":"); + switch (next_token.id) { + case "do": + case "for": + case "switch": + case "while": + enroll(the_label, "label", true); + the_label.init = true; + the_label.dead = false; + the_statement = statement(); + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + default: + advance(); + warn("unexpected_label_a", the_label); + } + } + +// Parse the statement. + + first = token; + first.statement = true; + the_symbol = syntax[first.id]; + if (the_symbol !== undefined && the_symbol.fud !== undefined) { + the_symbol.disrupt = false; + the_symbol.statement = true; + the_statement = the_symbol.fud(); + } else { + +// It is an expression statement. + + the_statement = expression(0, true); + if (the_statement.wrapped && the_statement.id !== "(") { + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; + } + + function statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + var array = []; + (function next(disrupt) { + var a_statement; + switch (next_token.id) { + case "}": + case "case": + case "default": + case "else": + case "(end)": + break; + default: + a_statement = statement(); + array.push(a_statement); + if (disrupt) { + warn("unreachable_a", a_statement); + } + return next(a_statement.disrupt); + } + }(false)); + return array; + } + + function not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === global) { + warn("unexpected_at_top_level_a", thing); + } + } + + function top_level_only(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== global) { + warn("misplaced_a", the_thing); + } + } + + function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + var stmts; + var the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token; + the_block.arity = "statement"; + the_block.body = special === "body"; + +// All top level function bodies should include the "use strict" pragma unless +// the whole file is strict or the file is a module or the function parameters +// use es6 syntax. + + if ( + special === "body" + && stack.length === 1 + && next_token.value === "use strict" + ) { + the_block.strict = next_token; + next_token.statement = true; + advance("(string)"); + advance(";"); + } + stmts = statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option.devel && special !== "ignore") { + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; + } + + function mutation_check(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v + + if ( + the_thing.id !== "." + && the_thing.arity !== "variable" + && (the_thing.id !== "[" || the_thing.arity !== "binary") + ) { + warn("bad_assignment_a", the_thing); + return false; + } + return true; + } + + function left_check(left, right) { + +// Warn if the left is not one of these: +// e.b +// e[b] +// e() +// identifier + + var id = left.id; + if ( + !left.identifier + && ( + left.arity !== "binary" + || (id !== "." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right); + return false; + } + return true; + } + +// These functions are used to specify the grammar of our language: + + function symbol(id, bp) { + +// Make a symbol if it does not already exist in the language's syntax. + + var the_symbol = syntax[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax[id] = the_symbol; + } + return the_symbol; + } + + function assignment(id) { + +// Make an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + var the_symbol = symbol(id, 20); + the_symbol.led = function (left) { + var the_token = token; + var right; + the_token.arity = "assignment"; + right = expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + switch (right.arity) { + case "assignment": + case "pre": + case "post": + warn("unexpected_a", right); + break; + } + if ( + option.es6 && + left.arity === "unary" && + (left.id === "[" || left.id === "{") + ) { + warn("expected_a_before_b", left, "const", left.id); + } else { + mutation_check(left); + } + return the_token; + }; + return the_symbol; + } + + function constant(id, type, value) { + +// Make a constant symbol. + + var the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud = (typeof value === "function") + ? value + : function () { + token.constant = true; + if (value !== undefined) { + token.value = value; + } + return token; + }; + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; + } + + function infix(id, bp, f) { + +// Make an infix operator. + + var the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + var the_token = token; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, expression(bp)]; + return the_token; + }; + return the_symbol; + } + + function post(id) { + +// Make one of the post operators. + + var the_symbol = symbol(id, 150); + the_symbol.led = function (left) { + token.expression = left; + token.arity = "post"; + mutation_check(token.expression); + return token; + }; + return the_symbol; + } + + function pre(id) { + +// Make one of the pre operators. + + var the_symbol = symbol(id); + the_symbol.nud = function () { + var the_token = token; + the_token.arity = "pre"; + the_token.expression = expression(150); + mutation_check(the_token.expression); + return the_token; + }; + return the_symbol; + } + + function prefix(id, f) { + +// Make a prefix operator. + + var the_symbol = symbol(id); + the_symbol.nud = function () { + var the_token = token; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = expression(150); + return the_token; + }; + return the_symbol; + } + + function stmt(id, f) { + +// Make a statement. + + var the_symbol = symbol(id); + the_symbol.fud = function () { + token.arity = "statement"; + return f(); + }; + return the_symbol; + } + + function ternary(id1, id2) { + +// Make a ternary operator. + + var the_symbol = symbol(id1, 30); + the_symbol.led = function (left) { + var the_token = token; + var second = expression(20); + advance(id2); + token.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, expression(10)]; + return the_token; + }; + return the_symbol; + } + +// Begin defining the language. + + syntax = empty(); + + symbol("}"); + symbol(")"); + symbol("]"); + symbol(","); + symbol(";"); + symbol(":"); + symbol("*/"); + symbol("await"); + symbol("case"); + symbol("catch"); + symbol("class"); + symbol("default"); + symbol("else"); + symbol("enum"); + symbol("finally"); + symbol("implements"); + symbol("interface"); + symbol("package"); + symbol("private"); + symbol("protected"); + symbol("public"); + symbol("static"); + symbol("super"); + symbol("void"); + symbol("yield"); + + constant("(number)", "number"); + constant("(regexp)", "regexp"); + constant("(string)", "string"); + constant("arguments", "object", function () { + if (option.es6) { + warn("unexpected_a", token); + } + return token; + }); + constant("eval", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; + }); + constant("false", "boolean", false); + constant("Function", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; + }); + constant("ignore", "undefined", function () { + warn("unexpected_a", token); + return token; + }); + constant("Infinity", "number", Infinity); + constant("isNaN", "function", function () { + if (option.es6) { + warn("expected_a_b", token, "Number.isNaN", "isNaN"); + } + return token; + }); + constant("NaN", "number", NaN); + constant("null", "null", null); + constant("this", "object", function () { + if (!option.this) { + warn("unexpected_a", token); + } + return token; + }); + constant("true", "boolean", true); + constant("undefined", "undefined"); + + assignment("="); + assignment("+="); + assignment("-="); + assignment("*="); + assignment("/="); + assignment("%="); + assignment("&="); + assignment("|="); + assignment("^="); + assignment("<<="); + assignment(">>="); + assignment(">>>="); + + infix("||", 40); + infix("&&", 50); + infix("|", 70); + infix("^", 80); + infix("&", 90); + infix("==", 100); + infix("===", 100); + infix("!=", 100); + infix("!==", 100); + infix("<", 110); + infix(">", 110); + infix("<=", 110); + infix(">=", 110); + infix("in", 110); + infix("instanceof", 110); + infix("<<", 120); + infix(">>", 120); + infix(">>>", 120); + infix("+", 130); + infix("-", 130); + infix("*", 140); + infix("/", 140); + infix("%", 140); + infix("(", 160, function (left) { + var the_paren = token; + var the_argument; + if (left.id !== "function") { + left_check(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (next_token.id !== ")") { + (function next() { + var ellipsis; + if (next_token.id === "...") { + if (!option.es6) { + warn("es6"); + } + ellipsis = true; + advance("..."); + } + the_argument = expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + the_paren.free = true; + if (the_argument.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + the_paren.free = false; + } + return the_paren; + }); + infix(".", 170, function (left) { + var the_token = token; + var name = next_token; + if ( + (left.id !== "(string)" || name.id !== "indexOf") && + (left.id !== "[" || ( + name.id !== "concat" && name.id !== "forEach" + )) && + (left.id !== "+" || name.id !== "slice") && + (left.id !== "(regexp)" || ( + name.id !== "exec" && name.id !== "test" + )) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; + }); + infix("[", 170, function (left) { + var the_token = token; + var the_subscript = expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + var name = survey(the_subscript); + if (rx_identifier.test(name)) { + warn("subscript_a", the_subscript, name); + } + } + left_check(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; + }); + infix("=>", 170, function (left) { + return stop("wrap_parameter", left); + }); + + function do_tick() { + var the_tick = token; + if (!option.es6) { + warn("es6", the_tick); + } + the_tick.value = []; + the_tick.expression = []; + if (next_token.id !== "`") { + (function part() { + advance("(string)"); + the_tick.value.push(token); + if (next_token.id === "${") { + advance("${"); + the_tick.expression.push(expression(0)); + advance("}"); + return part(); + } + }()); + } + advance("`"); + return the_tick; + } + + infix("`", 160, function (left) { + var the_tick = do_tick(); + left_check(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; + }); + + post("++"); + post("--"); + pre("++"); + pre("--"); + + prefix("+"); + prefix("-"); + prefix("~"); + prefix("!"); + prefix("!!"); + prefix("[", function () { + var the_token = token; + the_token.expression = []; + if (next_token.id !== "]") { + (function next() { + var element; + var ellipsis = false; + if (next_token.id === "...") { + ellipsis = true; + if (!option.es6) { + warn("es6"); + } + advance("..."); + } + element = expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]"); + return the_token; + }); + prefix("/=", function () { + stop("expected_a_b", token, "/\\=", "/="); + }); + prefix("=>", function () { + return stop("expected_a_before_b", token, "()", "=>"); + }); + prefix("new", function () { + var the_new = token; + var right = expression(160); + if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "()", artifact(next_token)); + } + the_new.expression = right; + return the_new; + }); + prefix("typeof"); + prefix("void", function () { + var the_void = token; + warn("unexpected_a", the_void); + the_void.expression = expression(0); + return the_void; + }); + + function parameter_list() { + var complex = false; + var list = []; + var optional; + var signature = ["("]; + if (next_token.id !== ")" && next_token.id !== "(end)") { + (function parameter() { + var ellipsis = false; + var param; + if (next_token.id === "{") { + complex = true; + if (!option.es6) { + warn("es6"); + } else if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("{"); + signature.push("{"); + (function subparameter() { + var subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (next_token.id === ":") { + advance(":"); + advance(); + token.label = subparam; + subparam = token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + } + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return subparameter(); + } + }()); + list.push(param); + advance("}"); + signature.push("}"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else if (next_token.id === "[") { + complex = true; + if (!option.es6) { + warn("es6"); + } else if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("["); + signature.push("[]"); + (function subparameter() { + var subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + return subparameter(); + } + }()); + list.push(param); + advance("]"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else { + if (next_token.id === "...") { + complex = true; + if (!option.es6) { + warn("es6"); + } + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + } + if (!next_token.identifier) { + return stop("expected_identifier_a"); + } + param = next_token; + list.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (next_token.id === "=") { + complex = true; + optional = param; + if (!option.es6) { + stop("unexpected_statement_a"); + } + advance("="); + param.expression = expression(0); + } else { + if (optional !== undefined) { + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } + } + }()); + } + advance(")"); + signature.push(")"); + return [list, signature.join(""), complex]; + } + + function do_function(the_function) { + var name; + if (the_function === undefined) { + the_function = token; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + name = next_token; + enroll(name, "variable", true); + the_function.name = name; + name.init = true; + name.calls = empty(); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + if (next_token.identifier) { + name = next_token; + the_function.name = name; + advance(); + } else { + the_function.name = anon; + } + } + } else { + name = the_function.name; + } + the_function.level = functionage.level + 1; + if (mega_mode) { + warn("unexpected_a", the_function); + } + +// Don't make functions in loops. It is inefficient, and it can lead to scoping +// errors. + + if (functionage.loop > 0) { + warn("function_in_loop", the_function); + } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + the_function.context = empty(); + the_function.finally = 0; + the_function.loop = 0; + the_function.switch = 0; + the_function.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functions.push(the_function); + functionage = the_function; + if (the_function.arity !== "statement" && typeof name === "object") { + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// Parse the parameter list. + + advance("("); + token.free = false; + token.arity = "function"; + var pl = parameter_list(); + functionage.parameters = pl[0]; + functionage.signature = pl[1]; + functionage.complex = pl[2]; + functionage.parameters.forEach(function enroll_parameter(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + name.names.forEach(enroll_parameter); + } + }); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" && + next_token.line === token.line + ) { + return stop("unexpected_a", next_token); + } + if (next_token.id === "." || next_token.id === "[") { + warn("unexpected_a"); + } + +// Restore the previous context. + + functionage = stack.pop(); + return the_function; + } + + prefix("function", do_function); + + function fart(pl) { + if (next_token.id === ";") { + stop("wrap_assignment", token); + } + advance("=>"); + var the_fart = token; + the_fart.arity = "binary"; + the_fart.name = "=>"; + the_fart.level = functionage.level + 1; + functions.push(the_fart); + if (functionage.loop > 0) { + warn("function_in_loop", the_fart); + } + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + the_fart.context = empty(); + the_fart.finally = 0; + the_fart.loop = 0; + the_fart.switch = 0; + the_fart.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functionage = the_fart; + the_fart.parameters = pl[0]; + the_fart.signature = pl[1]; + the_fart.complex = true; + the_fart.parameters.forEach(function (name) { + enroll(name, "parameter", true); + }); + if (!option.es6) { + warn("es6", the_fart); + } + if (next_token.id === "{") { + warn("expected_a_b", the_fart, "function", "=>"); + the_fart.block = block("body"); + } else { + the_fart.expression = expression(0); + } + functionage = stack.pop(); + return the_fart; + } + + prefix("(", function () { + var the_paren = token; + var the_value; + var cadet = lookahead().id; + +// We can distinguish between a parameter list for => and a wrapped expression +// with one token of lookahead. + + if ( + next_token.id === ")" + || next_token.id === "..." + || (next_token.identifier && (cadet === "," || cadet === "=")) + ) { + the_paren.free = false; + return fart(parameter_list()); + } + the_paren.free = true; + the_value = expression(0); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + if (next_token.id === "=>") { + if (the_value.arity !== "variable") { + if (the_value.id === "{" || the_value.id === "[") { + warn("expected_a_before_b", the_paren, "function", "("); + return stop("expected_a_b", next_token, "{", "=>"); + } + return stop("expected_identifier_a", the_value); + } + the_paren.expression = [the_value]; + return fart([the_paren.expression, "(" + the_value.id + ")"]); + } + return the_value; + }); + prefix("`", do_tick); + prefix("{", function () { + var the_brace = token; + var seen = empty(); + the_brace.expression = []; + if (next_token.id !== "}") { + (function member() { + var extra; + var id; + var name = next_token; + var value; + advance(); + if ( + (name.id === "get" || name.id === "set") && + next_token.identifier + ) { + extra = name.id + " " + next_token.id; + name = next_token; + advance(); + id = survey(name); + if (seen[extra] === true || seen[id] === true) { + warn("duplicate_a", name); + } + seen[id] = false; + seen[extra] = true; + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + switch (next_token.id) { + case "}": + case ",": + if (!option.es6) { + warn("es6"); + } + if (typeof extra === "string") { + advance("("); + } + value = expression(Infinity, true); + break; + case "(": + if (!option.es6 && typeof extra !== "string") { + warn("es6"); + } + value = do_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: (typeof extra === "string") + ? extra + : id, + thru: name.from + }); + break; + default: + if (typeof extra === "string") { + advance("("); + } + advance(":"); + value = expression(0); + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + advance(":"); + value = expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (next_token.id === ",") { + advance(","); + return member(); + } + }()); + } + advance("}"); + return the_brace; + }); + + stmt(";", function () { + warn("unexpected_a", token); + return token; + }); + stmt("{", function () { + warn("naked_block", token); + return block("naked"); + }); + stmt("break", function () { + var the_break = token; + var the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (next_token.identifier && token.line === next_token.line) { + the_label = functionage.context[next_token.id]; + if ( + the_label === undefined || + the_label.role !== "label" || + the_label.dead + ) { + warn((the_label !== undefined && the_label.dead) + ? "out_of_scope_a" + : "not_label_a"); + } else { + the_label.used += 1; + } + the_break.label = next_token; + advance(); + } + advance(";"); + return the_break; + }); + + function do_var() { + var the_statement = token; + var is_const = the_statement.id === "const"; + the_statement.names = []; + +// A program may use var or let, but not both, and let and const require +// option.es6. + + if (is_const) { + if (!option.es6) { + warn("es6", the_statement); + } + } else if (var_mode === undefined) { + var_mode = the_statement.id; + if (!option.es6 && var_mode !== "var") { + warn("es6", the_statement); + } + } else if (the_statement.id !== var_mode) { + warn( + "expected_a_b", + the_statement, + var_mode, + the_statement.id + ); + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + warn("var_switch", the_statement); + } + if (functionage.loop > 0 && the_statement.id === "var") { + warn("var_loop", the_statement); + } + (function next() { + if (next_token.id === "{" && the_statement.id !== "var") { + var the_brace = next_token; + the_brace.names = []; + advance("{"); + (function pair() { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + var name = next_token; + survey(name); + advance(); + if (next_token.id === ":") { + advance(":"); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + next_token.label = name; + the_brace.names.push(next_token); + enroll(next_token, "variable", is_const); + advance(); + } else { + the_brace.names.push(name); + enroll(name, "variable", is_const); + } + if (next_token.id === ",") { + advance(","); + return pair(); + } + }()); + advance("}"); + advance("="); + the_brace.expression = expression(0); + the_statement.names.push(the_brace); + } else if (next_token.id === "[" && the_statement.id !== "var") { + var the_bracket = next_token; + the_bracket.names = []; + advance("["); + (function element() { + var ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + var name = next_token; + advance(); + the_bracket.names.push(name); + enroll(name, "variable", the_statement.id === "const"); + if (ellipsis) { + name.ellipsis = true; + } else if (next_token.id === ",") { + advance(","); + return element(); + } + }()); + advance("]"); + advance("="); + the_bracket.expression = expression(0); + the_statement.names.push(the_bracket); + } else if (next_token.identifier) { + var name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", is_const); + if (next_token.id === "=" || is_const) { + advance("="); + name.expression = expression(0); + name.init = true; + } + the_statement.names.push(name); + } else { + return stop("expected_identifier_a", next_token); + } + if (next_token.id === ",") { + if (!option.multivar) { + warn("expected_a_b", next_token, ";", ","); + } + advance(","); + return next(); + } + }()); + the_statement.open = + the_statement.names.length > 1 && + the_statement.line !== the_statement.names[1].line; + semicolon(); + return the_statement; + } + + stmt("const", do_var); + stmt("continue", function () { + var the_continue = token; + if (functionage.loop < 1 || functionage.finally > 0) { + warn("unexpected_a", the_continue); + } + not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; + }); + stmt("debugger", function () { + var the_debug = token; + if (!option.devel) { + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; + }); + stmt("delete", function () { + var the_token = token; + var the_value = expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; + }); + stmt("do", function () { + var the_do = token; + not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; + }); + stmt("export", function () { + var the_export = token; + var the_id; + var the_name; + var the_thing; + + function export_id() { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + the_id = next_token.id; + the_name = global.context[the_id]; + if (the_name === undefined) { + warn("unexpected_a"); + } else { + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a"); + } + exports[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + } + + if (!option.es6) { + warn("es6", the_export); + } + the_export.expression = []; + if (next_token.id === "default") { + if (exports.default !== undefined) { + warn("duplicate_a"); + } + advance("default"); + the_thing = expression(); + if (the_thing.id !== "function") { + semicolon(); + } + exports.default = the_thing; + the_export.expression.push(the_thing); + } else { + switch (next_token.id) { + case "function": + the_thing = statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a", the_name); + } + exports[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + break; + case "var": + case "let": + case "const": + warn("unexpected_a"); + break; + case "{": + advance("{"); + (function loop() { + export_id(); + if (next_token.id === ",") { + advance(","); + return loop(); + } + }()); + advance("}"); + semicolon(); + break; + default: + export_id(); + if (the_name.writable !== true) { + warn("unexpected_a", token); + } + semicolon(); + } + } + module_mode = true; + return the_export; + }); + stmt("for", function () { + var first; + var the_for = token; + if (!option.for) { + warn("unexpected_a", the_for); + } + not_top_level(the_for); + functionage.loop += 1; + advance("("); + token.free = true; + if (next_token.id === ";") { + return stop("expected_a_b", the_for, "while (", "for (;"); + } + switch (next_token.id) { + case "var": + case "let": + case "const": + return stop("unexpected_a"); + } + first = expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = expression(0); + advance(";"); + the_for.inc = expression(0); + if (the_for.inc.id === "++") { + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; + }); + stmt("function", do_function); + stmt("if", function () { + var the_else; + var the_if = token; + the_if.expression = condition(); + the_if.block = block(); + if (next_token.id === "else") { + advance("else"); + the_else = token; + the_if.else = (next_token.id === "if") + ? statement() + : block(); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + the_if.disrupt = true; + } else { + warn("unexpected_a", the_else); + } + } + } + return the_if; + }); + stmt("import", function () { + var the_import = token; + var name; + if (!option.es6) { + warn("es6", the_import); + } else if (typeof module_mode === "object") { + warn("unexpected_directive_a", module_mode, module_mode.directive); + } + module_mode = true; + if (next_token.identifier) { + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + var names = []; + advance("{"); + if (next_token.id !== "}") { + while (true) { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (next_token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token; + if (!rx_module.test(token.value)) { + warn("bad_module_name_a", token); + } + froms.push(token.value); + semicolon(); + return the_import; + }); + stmt("let", do_var); + stmt("return", function () { + var the_return = token; + not_top_level(the_return); + if (functionage.finally > 0) { + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (next_token.id !== ";" && the_return.line === next_token.line) { + the_return.expression = expression(10); + } + advance(";"); + return the_return; + }); + stmt("switch", function () { + var dups = []; + var last; + var stmts; + var the_cases = []; + var the_disrupt = true; + var the_switch = token; + not_top_level(the_switch); + if (functionage.finally > 0) { + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token.free = true; + the_switch.expression = expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + (function major() { + var the_case = next_token; + the_case.arity = "statement"; + the_case.expression = []; + (function minor() { + advance("case"); + token.switch = true; + var exp = expression(0); + if (dups.some(function (thing) { + return are_similar(thing, exp); + })) { + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (next_token.id === "case") { + return minor(); + } + }()); + stmts = statements(); + if (stmts.length < 1) { + warn("expected_statements_a"); + return; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "break;", + artifact(next_token) + ); + } + if (next_token.id === "case") { + return major(); + } + }()); + dups = undefined; + if (next_token.id === "default") { + var the_default = next_token; + advance("default"); + token.switch = true; + advance(":"); + the_switch.else = statements(); + if (the_switch.else.length < 1) { + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + var the_last = the_switch.else[the_switch.else.length - 1]; + if (the_last.id === "break" && the_last.label === undefined) { + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; + }); + stmt("throw", function () { + var the_throw = token; + the_throw.disrupt = true; + the_throw.expression = expression(10); + semicolon(); + return the_throw; + }); + stmt("try", function () { + var the_catch; + var the_disrupt; + var the_try = token; + if (functionage.try > 0) { + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (next_token.id === "catch") { + var ignored = "ignore"; + the_catch = next_token; + the_try.catch = the_catch; + advance("catch"); + advance("("); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + if (next_token.id !== "ignore") { + ignored = undefined; + the_catch.name = next_token; + enroll(next_token, "exception", true); + } + advance(); + advance(")"); + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "catch", + artifact(next_token) + ); + + } + if (next_token.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; + }); + stmt("var", do_var); + stmt("while", function () { + var the_while = token; + not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; + }); + stmt("with", function () { + stop("unexpected_a", token); + }); + + ternary("?", ":"); + +// Ambulation of the parse tree. + + function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + var a_set = when[arity]; + var i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; + } + + function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all of the +// relevant tasks on the token. + + var a_set = when[the_token.arity]; + var i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + } + }; + } + + var posts = empty(); + var pres = empty(); + var preaction = action(pres); + var postaction = action(posts); + var preamble = amble(pres); + var postamble = amble(posts); + + function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.id === "function") { + walk_statement(thing.block); + } + switch (thing.arity) { + case "post": + case "pre": + warn("unexpected_a", thing); + break; + case "statement": + case "assignment": + warn("unexpected_statement_a", thing); + break; + } + postamble(thing); + } + } + } + + function walk_statement(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_statement); + } else { + preamble(thing); + walk_expression(thing.expression); + switch (thing.arity) { + case "statement": + case "assignment": + break; + case "binary": + if (thing.id !== "(") { + warn("unexpected_expression_a", thing); + } + break; + default: + warn(( + thing.id === "(string)" + && thing.value === "use strict" + ) + ? "unexpected_a" + : "unexpected_expression_a", thing); + } + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + } + } + + function lookup(thing) { + if (thing.arity === "variable") { + +// Look up the variable in the current context. + + var the_variable = functionage.context[thing.id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable === undefined) { + stack.forEach(function (outer) { + var a_variable = outer.context[thing.id]; + if ( + a_variable !== undefined + && a_variable.role !== "label" + ) { + the_variable = a_variable; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (the_variable === undefined) { + if (declared_globals[thing.id] === undefined) { + warn("undeclared_a", thing); + return; + } + the_variable = { + dead: false, + function: global, + id: thing.id, + init: true, + role: "variable", + used: 0, + writable: false + }; + global.context[thing.id] = the_variable; + } + the_variable.closure = true; + functionage.context[thing.id] = the_variable; + } else if (the_variable.role === "label") { + warn("label_a", thing); + } + if (the_variable.dead) { + warn("out_of_scope_a", thing); + } + return the_variable; + } + } + + function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); + } + + function preaction_function(thing) { + if (thing.arity === "statement" && blockage.body !== true) { + warn("unexpected_a", thing); + } + if (thing.level === 1) { + if ( + module_mode === true || + global.strict !== undefined || + thing.complex + ) { + if (thing.id !== "=>" && thing.block.strict !== undefined) { + warn("unexpected_a", thing.block.strict); + } + } else { + if (thing.block.strict === undefined) { + warn("use_strict", thing); + } + } + } + stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + switch (thing.extra) { + case "get": + if (thing.parameters.length !== 0) { + warn("bad_get", thing); + } + break; + case "set": + if (thing.parameters.length !== 1) { + warn("bad_set", thing); + } + break; + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); + } + + function bitwise_check(thing) { + if (!option.bitwise && bitwiseop[thing.id] === true) { + warn("unexpected_a", thing); + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + warn("unexpected_a", thing); + } + } + + function pop_block() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); + } + + function activate(name) { + if (name.expression !== undefined) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.init = true; + } + } + name.dead = false; + blockage.live.push(name); + } + + function action_var(thing) { + thing.names.forEach(activate); + } + + preaction("assignment", bitwise_check); + preaction("binary", bitwise_check); + preaction("binary", function (thing) { + if (relationop[thing.id] === true) { + var left = thing.expression[0]; + var right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + if (option.es6) { + warn("number_isNaN", thing); + } else { + warn("isNaN", thing); + } + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + warn("expected_string_a", right); + } + } else { + var value = right.value; + if (value === "symbol") { + if (!option.es6) { + warn("es6", right, value); + } + } else if (value === "null" || value === "undefined") { + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + ) { + warn("expected_type_string_a", right, value); + } + } + } + } + }); + preaction("binary", "==", function (thing) { + warn("expected_a_b", thing, "===", "=="); + }); + preaction("binary", "!=", function (thing) { + warn("expected_a_b", thing, "!==", "!="); + }); + preaction("binary", "=>", preaction_function); + preaction("binary", "||", function (thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + warn("and", thang); + } + }); + }); + preaction("binary", "(", function (thing) { + var left = thing.expression[0]; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + var parent = functionage.name.function; + if (parent) { + var left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + && left_variable.dead + && left_variable.function === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } + }); + preaction("binary", "in", function (thing) { + warn("infix_in", thing); + }); + preaction("binary", "instanceof", function (thing) { + warn("unexpected_a", thing); + }); + preaction("binary", ".", function (thing) { + if (thing.expression.new) { + thing.new = true; + } + }); + preaction("statement", "{", function (thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; + }); + preaction("statement", "for", function (thing) { + if (thing.name !== undefined) { + var the_variable = lookup(thing.name); + if (the_variable !== undefined) { + the_variable.init = true; + if (!the_variable.writable) { + warn("bad_assignment_a", thing.name); + } + } + } + walk_statement(thing.initial); + }); + preaction("statement", "function", preaction_function); + preaction("unary", "~", bitwise_check); + preaction("unary", "function", preaction_function); + preaction("variable", function (thing) { + var the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } + }); + + function init_variable(name) { + var the_variable = lookup(name); + if (the_variable !== undefined) { + if (the_variable.writable) { + the_variable.init = true; + return; + } + } + warn("bad_assignment_a", name); + } + + postaction("assignment", function (thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + var lvalue = thing.expression[0]; + if (thing.id === "=") { + if (thing.names !== undefined) { + if (Array.isArray(thing.names)) { + thing.names.forEach(init_variable); + } else { + init_variable(thing.names); + } + } else { + if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.writable !== true) { + warn("bad_assignment_a", lvalue); + } + } + var right = syntax[thing.expression[1].id]; + if ( + right !== undefined && + ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + warn("unexpected_a", thing.expression[1]); + } + } + }); + + function postaction_function(thing) { + delete functionage.finally; + delete functionage.loop; + delete functionage.switch; + delete functionage.try; + functionage = stack.pop(); + if (thing.wrapped) { + warn("unexpected_parens", thing); + } + return pop_block(); + } + + postaction("binary", function (thing) { + var right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || are_similar(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true && + thing.expression[1].constant === true + ) + ) { + warn("weird_relation_a", thing); + } + } + switch (thing.id) { + case "=>": + case "(": + case "[": + break; + case ".": + if (thing.expression.id === "RegExp") { + warn("weird_expression_a", thing); + } + break; + default: + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") && + right.id === thing.id && + right.arity === "unary" && + !right.wrapped + ) { + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true && + right.constant === true + ) { + thing.constant = true; + } + } + }); + postaction("binary", "&&", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + warn("weird_condition_a", thing); + } + }); + postaction("binary", "||", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + warn("weird_condition_a", thing); + } + }); + postaction("binary", "=>", postaction_function); + postaction("binary", "(", function (thing) { + var left = thing.expression[0]; + var the_new; + var arg; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id.charAt(0) > "Z" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || (left.id === "Symbol" && option.es6) + ) { + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option.eval) { + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || ( + ( + arg[1].id !== "(number)" + || +arg[1].value !== (arg[1].value | 0) + ) && + arg[1].arity !== "binary" + )) { + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id.charAt(0) >= "A" + && left.id.charAt(0) <= "Z" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + warn( + "expected_a_before_b", + left, + "new", + artifact(left) + ); + } + } + } else if (left.id === ".") { + var cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + cack = !cack; + } + if (rx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + warn("unexpected_a", the_new); + } else { + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + var l1 = left.expression; + if (l1.id === "(") { + var l2 = l1.expression; + if (l2.length === 1) { + var l3 = l2[0]; + if (l3.id === "new" && l3.expression.id === "Date") { + warn( + "expected_a_b", + l3, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } + }); + postaction("binary", "[", function (thing) { + if (thing.expression[0].id === "RegExp") { + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + warn("weird_expression_a", thing.expression[1]); + } + }); + postaction("statement", "{", pop_block); + postaction("statement", "const", action_var); + postaction("statement", "export", top_level_only); + postaction("statement", "for", function (thing) { + walk_statement(thing.inc); + }); + postaction("statement", "function", postaction_function); + postaction("statement", "import", function (the_thing) { + var name = the_thing.name; + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return top_level_only(the_thing); + }); + postaction("statement", "let", action_var); + postaction("statement", "try", function (thing) { + if (thing.catch !== undefined) { + var the_name = thing.catch.name; + if (the_name !== undefined) { + var the_variable = functionage.context[the_name.id]; + the_variable.dead = false; + the_variable.init = true; + } + walk_statement(thing.catch.block); + } + }); + postaction("statement", "var", action_var); + postaction("ternary", function (thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || are_similar(thing.expression[1], thing.expression[2]) + ) { + warn("unexpected_a", thing); + } else if (are_similar(thing.expression[0], thing.expression[1])) { + warn("expected_a_b", thing, "||", "?"); + } else if (are_similar(thing.expression[0], thing.expression[2])) { + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" && + thing.expression[2].id === "false" + ) { + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" && + thing.expression[2].id === "true" + ) { + warn("expected_a_b", thing, "!", "?"); + } else if (thing.expression[0].wrapped !== true && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + )) { + warn("wrap_condition", thing.expression[0]); + } + }); + postaction("unary", function (thing) { + switch (thing.id) { + case "[": + case "{": + case "function": + case "new": + break; + case "`": + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + break; + case "!": + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + break; + default: + if (thing.expression.constant === true) { + thing.constant = true; + } + } + }); + postaction("unary", "function", postaction_function); + postaction("unary", "+", function (thing) { + var right = thing.expression; + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if (right.constant || right.id === "{" || right.id === "[") { + warn("unexpected_a", thing, "+"); + } + }); + + function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + if (id !== "ignore") { + var name = the_function.context[id]; + if (name.function === the_function) { + if (name.used === 0 && ( + name.role !== "function" + || name.function.arity !== "unary" + )) { + warn("unused_a", name); + } else if (!name.init) { + warn("uninitialized_a", name); + } + } + } + }); + } + + function uninitialized_and_unused() { + +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (module_mode === true || option.node) { + delve(global); + } + functions.forEach(delve); + } + +// Go through the token list, looking at usage of whitespace. + + function whitage() { + var closer = "(end)"; + var free = false; + var left = global; + var margin = 0; + var nr_comments_skipped = 0; + var open = true; + var qmark = ""; + var result; + var right; + + function expected_at(at) { + warn( + "expected_a_at_b_c", + right, + artifact(right), + fudge + at, + artifact_column(right) + ); + } + + function at_margin(fit) { + var at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function no_space_only() { + if ( + left.id !== "(global)" && + left.nr + 1 === right.nr && ( + left.line !== right.line || + left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function no_space() { + if (left.line === right.line) { + if (left.thru !== right.from && nr_comments_skipped === 0) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (open) { + var at = (free) + ? margin + : margin + 8; + if (right.from < at) { + expected_at(at); + } + } else { + if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (free) { + if (right.from < margin) { + expected_at(margin); + } + } else { + var mislaid = stack[stack.length - 1].right; + if (!open && mislaid !== undefined) { + warn( + "expected_a_next_at_b", + mislaid, + artifact(mislaid.id), + margin + 4 + fudge + ); + } else if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function unqmark() { + +// Undo the effects of dangling nested ternary operators. + + var level = qmark.length; + if (level > 0) { + margin -= level * 4; + } + qmark = ""; + } + + stack = []; + tokens.forEach(function (the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + + var new_closer = opener[left.id]; + if (typeof new_closer === "string") { + if (new_closer !== right.id) { + stack.push({ + closer: closer, + free: free, + margin: margin, + open: open, + qmark: qmark, + right: right + }); + qmark = ""; + closer = new_closer; + if (left.line !== right.line) { + free = closer === ")" && left.free; + open = true; + margin += 4; + if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (right.switch) { + unqmark(); + at_margin(-4); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + free = false; + open = false; + no_space_only(); + } + } else { + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like {]) have already been detected. + + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } + } else { + +// If right is a closer, then pop the previous state. + + if (right.id === closer) { + var previous = stack.pop(); + margin = previous.margin; + if (open && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + closer = previous.closer; + free = previous.free; + open = previous.open; + qmark = previous.qmark; + } else { + +// Left is not an opener, and right is not a closer. The nature of left and +// right will determine the space between them. + +// If left is , or ; or right is a statement then if open, right must go at the +// margin, or if closed, a space between. + + + if (right.switch) { + unqmark(); + at_margin(-4); + } else if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (left.id === ",") { + unqmark(); + if (!open || ( + (free || closer === "]") && + left.line === right.line + )) { + one_space(); + } else { + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. Use qmark to +// deal with nested ternary operators. + + } else if (right.arity === "ternary") { + if (right.id === "?") { + margin += 4; + qmark += "?"; + } else { + result = qmark.match(rx_colons); + qmark = result[1] + ":"; + margin -= 4 * result[2].length; + } + at_margin(0); + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + no_space(); + } else if ( + left.id === "." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || (right.arity === "binary" && ( + right.id === "(" + || right.id === "[" + )) + || ( + right.arity === "function" && + left.id !== "function" + ) + ) { + no_space_only(); + } else if (right.id === ".") { + if (left.line === right.line) { + no_space(); + } else { + if (!rx_dot.test(qmark)) { + qmark += "."; + margin += 4; + } + at_margin(0); + } + } else if (left.id === ";") { + unqmark(); + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + one_space_only(); + } else if (right.statement === true) { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.id === "var" + || left.id === "const" + || left.id === "let" + ) { + stack.push({ + closer: closer, + free: free, + margin: margin, + open: open, + qmark: qmark + }); + closer = ";"; + free = false; + open = left.open; + qmark = ""; + if (open) { + margin = margin + 4; + at_margin(0); + } else { + one_space_only(); + } + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" && + (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" && + (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); + } + +// The jslint function itself. + + return function (source, option_object, global_array) { + try { + warnings = []; + option = Object.assign(empty(), option_object); + anon = "anonymous"; + block_stack = []; + declared_globals = empty(); + directive_mode = true; + directives = []; + early_stop = true; + exports = empty(); + froms = []; + fudge = (option.fudge) + ? 1 + : 0; + functions = []; + global = { + id: "(global)", + body: true, + context: empty(), + from: 0, + level: 0, + line: 0, + live: [], + loop: 0, + switch: 0, + thru: 0 + }; + blockage = global; + functionage = global; + json_mode = false; + mega_mode = false; + module_mode = false; + next_token = global; + property = empty(); + stack = []; + tenure = undefined; + token = global; + token_nr = 0; + var_mode = undefined; + populate(declared_globals, standard, false); + if (global_array !== undefined) { + populate(declared_globals, global_array, false); + } + Object.keys(option).forEach(function (name) { + if (option[name] === true) { + var allowed = allowed_option[name]; + if (Array.isArray(allowed)) { + populate(declared_globals, allowed, false); + } + } + }); + tokenize(source); + advance(); + if (json_mode) { + tree = json_value(); + advance("(end)"); + } else { + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option.browser) { + if (next_token.id === ";") { + advance(";"); + } + } else { + +// If we are not in a browser, then the file form of strict pragma may be used. + + if ( + next_token.value === "use strict" + ) { + global.strict = next_token; + advance("(string)"); + advance(";"); + } + } + tree = statements(); + advance("(end)"); + functionage = global; + walk_statement(tree); + if (module_mode && global.strict !== undefined) { + warn("unexpected_a", global.strict); + } + uninitialized_and_unused(); + if (!option.white) { + whitage(); + } + } + if (!option.browser) { + directives.forEach(function (comment) { + if (comment.directive === "global") { + warn("missing_browser", comment); + } + }); + } + early_stop = false; + } catch (e) { + if (e.name !== "JSLintError") { + warnings.push(e); + } + } + return { + directives: directives, + edition: "2017-06-27", + exports: exports, + froms: froms, + functions: functions, + global: global, + id: "(JSLint)", + json: json_mode, + lines: lines, + module: module_mode === true, + ok: warnings.length === 0 && !early_stop, + option: option, + property: property, + stop: early_stop, + tokens: tokens, + tree: tree, + warnings: warnings.sort(function (a, b) { + return a.line - b.line || a.column - b.column; + }) + }; + }; +}()); + +/*node module.exports = jslint;*/ diff --git a/branch.f4a2c5bb/report.js b/branch.f4a2c5bb/report.js new file mode 100644 index 000000000..a5fd0b34b --- /dev/null +++ b/branch.f4a2c5bb/report.js @@ -0,0 +1,214 @@ +// report.js +// 2017-06-13 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Generate JSLint HTML reports. + +/*property + closure, column, context, edition, error, exports, filter, forEach, fudge, + function, functions, global, id, froms, isArray, join, json, keys, length, + level, line, lines, message, module, name, names, option, parameters, + property, push, replace, role, signature, sort, stop, warnings +*/ + +var REPORT = (function () { + "use strict"; + + var rx_amp = /&/g; + var rx_gt = />/g; + var rx_lt = / with less destructive entities. + + return String(string) + .replace(rx_amp, "&") + .replace(rx_lt, "<") + .replace(rx_gt, ">"); + } + + return { + + error: function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + var fudge = +!!data.option.fudge; + var output = []; + if (data.stop) { + output.push("
    JSLint was unable to finish.
    "); + } + data.warnings.forEach(function (warning) { + output.push( + "
    ", + entityify(warning.line + fudge), + ".", + entityify(warning.column + fudge), + "
    ", + entityify(warning.message), + "
    ", + entityify(data.lines[warning.line] || ""), + "" + ); + }); + return output.join(""); + }, + + function: function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + var fudge = +!!data.option.fudge; + var mode = (data.module) + ? "module" + : "global"; + var output = []; + + if (data.json) { + return (data.warnings.length === 0) + ? "
    JSON: good.
    " + : "
    JSON: bad.
    "; + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + var global = Object.keys(data.global.context).sort(); + var froms = data.froms.sort(); + var exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + var context = the_function.context; + var list = Object.keys(context); + output.push( + "
    ", + entityify(the_function.line + fudge), + "
    ", + (the_function.name === "=>") + ? entityify(the_function.signature) + " =>" + : ( + (typeof the_function.name === "string") + ? "«" + entityify(the_function.name) + + "»" + : "" + entityify(the_function.name.id) + + "" + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + var params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + var the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.function === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + var the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.function === the_function + ); + })); + detail("outer", list.filter(function (id) { + var the_variable = context[id]; + return ( + the_variable.function !== the_function + && the_variable.function.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].function.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + output.push( + "
    JSLint edition ", + entityify(data.edition), + "
    " + ); + return output.join(""); + }, + + property: function property_directive(data) { + +// Produce the /*property*/ directive. + + var not_first = false; + var output = ["/*property"]; + var length = 1111; + var properties = Object.keys(data.property); + + if (properties.length > 0) { + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length > 78) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); + } + } + }; +}()); diff --git a/branch.ignore_macro/README b/branch.ignore_macro/README new file mode 100644 index 000000000..cd6b06c8b --- /dev/null +++ b/branch.ignore_macro/README @@ -0,0 +1,39 @@ +JSLint, The JavaScript Code Quality Tool + +Douglas Crockford +douglas@crockford.com + +2019-03-15 + +jslint.js contains the jslint function. It parses and analyzes a source file, +returning an object with information about the file. It can also take an object +that sets options. + +index.html runs the jslint.js function in a web page. The page also depends +browser.js and report.js and jslint.css. + +jslint.css provides styling for index.html. + +browser.js runs the web user interface. + +report.js generates the results reports in HTML. + +help.html describes JSLint's usage. Please read it. + +function.html describes the jslint function and the results it produces. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[http://www.crockford.com/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. diff --git a/branch.ignore_macro/browser.js b/branch.ignore_macro/browser.js new file mode 100644 index 000000000..5086fed85 --- /dev/null +++ b/branch.ignore_macro/browser.js @@ -0,0 +1,158 @@ +// browser.js +// 2018-06-16 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +/*jslint + browser +*/ + +/*property + checked, create, disable, display, error, focus, forEach, function, + getElementById, innerHTML, join, length, map, onchange, onclick, onscroll, + property, querySelectorAll, scrollTop, select, split, style, title, value +*/ + +import jslint from "./jslint.js"; +import report from "./report.js"; + +// This is the web script companion file for JSLint. It includes code for +// interacting with the browser and displaying the reports. + +let rx_crlf = /\n|\r\n?/; +let rx_separator = /[\s,;'"]+/; + +let fudge_unit = 0; + +const aux = document.getElementById("JSLINT_AUX"); +const boxes = document.querySelectorAll("[type=checkbox]"); +const fudge = document.getElementById("JSLINT_FUDGE"); +const global = document.getElementById("JSLINT_GLOBAL"); +const number = document.getElementById("JSLINT_NUMBER"); +const property = document.getElementById("JSLINT_PROPERTY"); +const property_fieldset = document.getElementById("JSLINT_PROPERTYFIELDSET"); +const report_field = document.getElementById("JSLINT_REPORT"); +const report_list = document.getElementById("JSLINT_REPORT_LIST"); +const source = document.getElementById("JSLINT_SOURCE"); +const select = document.getElementById("JSLINT_SELECT"); +const warnings = document.getElementById("JSLINT_WARNINGS"); +const warnings_list = document.getElementById("JSLINT_WARNINGS_LIST"); + +function show_numbers() { + number.value = source.value.split(rx_crlf).map(function (ignore, index) { + return index + fudge_unit; + }).join("\n"); +} + +function clear() { + aux.style.display = "none"; + number.value = ""; + property.value = ""; + property_fieldset.style.display = "none"; + report_field.style.display = "none"; + report_list.innerHTML = ""; + source.focus(); + source.value = ""; + warnings.style.display = "none"; + warnings_list.innerHTML = ""; +} + +function fudge_change() { + fudge_unit = Number(fudge.checked); + show_numbers(); +} + +function clear_options() { + boxes.forEach(function (node) { + node.checked = false; + }); + fudge_change(); + global.innerHTML = ""; +} + +function call_jslint() { + +// First build the option object. + + let option = Object.create(null); + boxes.forEach(function (node) { + if (node.checked) { + option[node.title] = true; + } + }); + +// Call JSLint with the source text, the options, and the predefined globals. + + let global_string = global.value; + let result = jslint( + source.value, + option, + ( + global_string === "" + ? undefined + : global_string.split(rx_separator) + ) + ); + +// Generate the reports. + + let error_html = report.error(result); + let function_html = report.function(result); + let property_text = report.property(result); + +// Display the reports. + + warnings_list.innerHTML = error_html; + warnings.style.display = ( + error_html.length === 0 + ? "none" + : "block" + ); + + report_list.innerHTML = function_html; + report_field.style.display = "block"; + if (property_text) { + property.value = property_text; + property_fieldset.style.display = "block"; + property.scrollTop = 0; + select.disable = false; + } else { + property_fieldset.style.display = "none"; + select.disable = true; + } + aux.style.display = "block"; + source.select(); +} + +fudge.onchange = fudge_change; + +source.onchange = function (ignore) { + show_numbers(); +}; + +source.onscroll = function () { + let ss = source.scrollTop; + number.scrollTop = ss; + let sn = number.scrollTop; + if (ss > sn) { + show_numbers(); + number.scrollTop = ss; + } +}; + +document.querySelectorAll("[name='JSLint']").forEach(function (node) { + node.onclick = call_jslint; +}); + +document.querySelectorAll("[name='clear']").forEach(function (node) { + node.onclick = clear; +}); + +document.getElementById("JSLINT_SELECT").onclick = function () { + property.focus(); + property.select(); +}; +document.getElementById("JSLINT_CLEAR_OPTIONS").onclick = clear_options; + +fudge_change(); +source.select(); +source.focus(); diff --git a/branch.ignore_macro/function.html b/branch.ignore_macro/function.html new file mode 100644 index 000000000..75bf39360 --- /dev/null +++ b/branch.ignore_macro/function.html @@ -0,0 +1,615 @@ + + + + + + + + +JSLint: jslint function + + + +
    JSLint
    + +
    The jslint Function
    +
    +

    JSLint

    +

    JSLint is delivered as a set of files:

    +
    + + + + + + + + + + + + + + +
    FilenameContent
    browser.jsBrowser based UI.
    function.htmlThis page that you are looking at right now.
    help.htmlUsing jslint as a single page JSLint application.
    index.htmlSingle page JSLint application that uses the + js files.
    jslint.jsThe jslint function.
    report.jsThe report object, containing report generator functions for + HTML.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    +
    + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring"(JSLint)"
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    parenttokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    parenttokenThe function in which the variable was declared.variable
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    shebangstringThe first line of text if it started with #!line
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable may be assigned to.variable
    +

    Reports

    +

    The report object contains three functions that all take a + result from the jslint function as input. + report.js has no other dependence on other files.

    + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    +
    + +
    + + + +
    + + diff --git a/branch.ignore_macro/help.html b/branch.ignore_macro/help.html new file mode 100644 index 000000000..358a35baa --- /dev/null +++ b/branch.ignore_macro/help.html @@ -0,0 +1,812 @@ + + + + + + + + + +JSLint: Help + + + +
    JSLint
    + +
    Help
    +
    +
    Il semble que la perfection soit atteinte non quand il n’y a + plus rien à ajouter, mais quand il n’y a plus rien à + retrancher.
    +
    + Antoine de Saint-Exupéry +
    +
    + Terre des Hommes (1939) +
    +

    Good Parts

    +

    The Principle of the Good Parts is

    +
    If a feature is sometimes useful and sometimes dangerous and if + there is a better option then always use the better option.
    +

    JSLint is a JavaScript program that looks for problems in + JavaScript programs. It is a code quality tool.

    +

    When C was + a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    +

    As the language matured, the definition of the language was + strengthened to eliminate some insecurities, and compilers got better + at issuing warnings. lint is no longer needed.

    +

    JavaScript is a + young-for-its-age language. It was originally intended to do small tasks in + webpages, tasks for which Java was too heavy and clumsy. But JavaScript is + a surprisingly capable language, and it is now being used in larger + projects. Many of the features that were intended to make the language easy + to use are troublesome when projects become complicated. A lint + for JavaScript is needed: JSLint, a JavaScript syntax checker + and validator.

    +

    JSLint takes a JavaScript source and scans it. If it finds a + problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by the ECMAScript Programming Language + Standard (the strangely named document that governs JavaScript). + JSLint will reject most legal programs. It is a higher + standard.

    +

    JavaScript is a sloppy language, but hidden deep inside there is an elegant, + better language. JSLint helps you to program in that better + language and to avoid most of the slop. JSLint will reject + programs that browsers will accept because JSLint is concerned + with the quality of your code and browsers are not. You should gladly accept + all of JSLint's advice.

    +

    JSLint can operate on JavaScript source + or JSON text.

    +

    ECMAScript Sixth Edition

    +

    Some of + ES6’s features are good, so JSLint will recognize the good + parts of ES6.

    +

    Currently, these features are recognized:

    +
      +
    • The ... ellipsis marker in parameter + lists and argument lists, replacing the arguments object + for variadic functions.
    • +
    • The let statement, which is like the var + statement except that it respects block scope. + You may use let or var but not both.
    • +
    • The const statement is like the let statement + except that it disallows the use of assignment on the variable, although + if the value of the variable is mutable, it can still be mutated. + const is preferred to let. +
    • Destructuring of arrays and objects is allowed in parameter lists and on + the left side of let, and const, but not + var or assignment statements, and not deep destructuring + or eliding.
    • +
    • Enhanced object literals, providing shorter forms for function + declaration and properties that are initialized by variables with the + same name.
    • +
    • The fat arrow => fart functions.
    • +
    • The simplest forms of import and export.
    • +
    • `Megastring` literals, but not nested + `megastring` literals.
    • +
    • New global functions, such as Map, Set, + WeakMap, and WeakSet.
    • +
    • 0b- and 0o- number literals.
    • +
    +

    The most important new feature of ES6 is proper tail calls. This has no + new syntax, so JSLint doesn’t see it. But it makes recursion + much more attractive, which makes loops, particularly for + loops, much less attractive.

    +

    import export

    +

    The ES6 module feature will be an important improvement over JavaScript’s + global variables as a means of linking separate files together. + JSLint recognizes a small but essential subset of the module + syntax.

    +
    +

    import name from stringliteral;

    +

    import {name} from stringliteral;

    +

    import(string).then(function);

    +

    export default function () {};

    +

    export default function name() {};

    +

    export default expression;

    +

    export function name() {}

    +

    export name; // where name is const

    +

    export {name};

    +
    +

    Directives

    +

    JSLint provides three directives that may be placed in a + file to manage JSLint’s behavior. Use of these directives is + optional. If they are used, they should be placed in a source file before + the first statement. They are written in the form of a comment, where the + directive name is placed immediately after the opening of the comment before + any whitespace. The three directives are global, + jslint, and property. Directives in a file are + stronger than options selected from the UI or passed with the option object.

    +

    /*global*/

    +

    The /*global*/ directive is used to specify a set of globals + (usually functions and objects containing functions) that are available to + this file. This was commonly used in browsers to link source files together + before ES6 modules appeared. Use of global variables is strongly + discouraged, but unfortunately web browsers require their use. The + /*global*/ directive can only be used when the + Assume a browser option is selected. +

    Each of the names listed will indicate a read-only global variable. The names + are separated by , comma. This directive + should not be used if import or export is used. + This directive inhibits warnings, but it does not declare the names in the + execution environment. +

    For example: +

    /*global +
    ADSAFE, report, jslint
    +*/
    +

    instructs JSLint to not give warnings about the global + variables ADsafe, report, and jslint. + However, if any of those names are expected to be supplied by other files + and those other files fail to do so, then execution errors will result. It + is usually better to use top-level variable declarations instead: +

        var ADSAFE;
    +    var report;
    +    var jslint; 
    +

    Using var in this way allows comparing a global variable to the + undefined value to determine whether it is has been used in the global context.

    +

    /*jslint*/

    +

    The /*jslint*/ directive allows for the control of several + options. These options can also be set from the + JSLint.com user interface.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Tolerate bitwise operatorsbitwisetrue if bitwise operators should be allowed. The + bitwise operators are rarely used in JavaScript programs, so it + is usually more likely that & is a mistyping of + && than that it indicates a bitwise and. + JSLint will give warnings on the bitwise operators + unless this option is selected.
    Assume a browser browsertrue if the standard browser globals should be + predefined. This option will reject the use of + import and export. This option also + disallows the file form of the "use + strict" pragma. It does not supply + self; you will have to request that unnecessary + alias of the dreaded global object yourself. It adds the same + globals as this directive: +
    /*global +
    + clearInterval, clearTimeout, document, event, FileReader, + FormData, history, localStorage, location, name, navigator, + screen, sessionStorage, setInterval, setTimeout, Storage, + URL, window, XMLHttpRequest +
    + */
    +
    Tolerate conversion operatorsconverttrue if !!, + prefix, + and concatenation with "" are allowed. + The Boolean, Number, and + String functions are preferred
    Assume CouchDBcouchtrue if + Couch DB + globals should be predefined. It adds the same globals as this + directive: +
    /*global +
    emit, getRow, isArray, log, provides, registerType, require, + send, start, sum, toJSON
    + */
    Assume in developmentdeveltrue if browser globals that are useful in + development should be predefined, and if debugger + statements and TODO comments + should be allowed. It adds the + same globals as this directive: +
    /*global +
    alert, confirm, console, prompt
    + */
    Be sure to turn this option off before going + into production.
    Tolerate evalevaltrue if eval should be allowed. In the + past, the eval function was the most misused + feature of the language.
    Tolerate for statementfortrue if the for statement should be + allowed. It is almost always better to use the array methods + instead.
    Fudge the line and column numbersfudgetrue if the origin of a text file is line 1 column + 1. Programmers know that number systems start at zero. + Unfortunately, most text editors start numbering lines and + columns at one. JSLint will by default start + numbering at zero. Use this option to start the numbering at one + in its reports.
    Tolerate get and setgetsettrue if accessor properties are allowed in object literals.
    Tolerate long source lineslongtrue if a line can contain more than 80 characters.
    Assume Node.jsnodetrue if Node.js globals should be predefined. It + will predefine globals that are used in the Node.js environment. + It adds the same globals as this directive: +
    /*global +
    + Buffer, clearImmediate, clearInterval, clearTimeout, console, exports, + module, process, require, setImmediate, setInterval, setTimeout, URL, + URLSearchParams, __dirname, __filename +
    + */
    Tolerate single quote stringssingletrue if 'single quote + should be allowed to enclose string literals.
    Tolerate this
    thistrue if this should be allowed.
    Tolerate whitespace messwhitetrue if the whitespace rules should be ignored.
    +

    For example:

    +
    /*jslint
    +    bitwise, node
    +*/
    + +

    /*property*/

    +

    The /*property*/ directive is used to declare a list of + property identifiers that are used in the file. Each property name in the + program is looked up in this list. If a name is not found, that indicates an + error, most likely a typing error.

    +

    The list can also be used to evade some of JSLint’s naming + rules.

    +

    JSLint can build the /*property*/ list for you. At + the bottom of its report, JSLint displays a + /*property*/ directive. It contains all of the names that were + used with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. You + can copy the /*property*/ directive to the top of your + script file. JSLint will check the spelling of all property + names against the list. That way, you can have JSLint look for + misspellings for you.

    +

    For example,

    +
    /*property +
    charAt, slice, _$_
    + */
    +

    ignore

    +

    JSLint introduces a new reserved word: ignore. It + is used in parameter lists and in catch clauses to indicate a + parameter that will be ignored. Unused warnings will not be produced for + ignore. +

    function handler(ignore, value) {
    +    return do_something_useful(value);
    +}
    +

    var let const

    +

    JavaScript provides three statements for declaring variables: + var, let, and const. The + var statement suffers from a bad practice called hoisting. The + let statement does not do hoisting and respects block scope. + The const statement is like let except that it + marks the variable (but not its contents) as read only, making it an error + to attempt to assign to the variable. When given a choice, + const is the best, var is the worst.

    +

    JSLint uses the intersection of the var rules and the + let rules, and by doing so avoids the errors related to either. + A name should be declared only once in a function. It should be declared + before it is used. It should not be used outside of the block in which it is + declared. A variable should not have the same name as a variable or + parameter in an outer function. Do not mix var and + let. Declare one name per statement.

    +

    = == ===

    +

    Fortran made a terrible + mistake in using the equality operator as its assignment operator. That + mistake has been replicated in most languages since then. C compounded that + mistake by making == its equality operator. The visual + similarity is a source of errors. JavaScript compounded this further by + making == a type coercing comparison operator that produces + false positive results. This was mitigated by adding the === + operator, leaving the broken == operator in place.

    +

    JSLint attempts to minimize errors by the following rules:

    +

    == is not allowed. This avoids the false positives, and + increases the visual distance between = and ===. + Assignments are not allowed in expression position, and comparisons are not + allowed in statement position. This also reduces confusion. 

    +

    Semicolon

    +

    JavaScript uses a C-like syntax that requires the use of semicolons to + delimit certain statements. JavaScript attempts to make those semicolons + optional with an automatic semicolon insertion mechanism, but it does not + work very well. Automatic semicolon insertion was added to make + things easier for beginners. Unfortunately, it sometimes fails. Do not rely + on it unless you are a beginner.

    +

    JSLint expects that every statement will be followed by + ; except for for, function, + if, switch, try, and + while. JSLint does not expect to see unnecessary + semicolons, the empty statement, or empty blocks.

    +

    function =>

    +

    JavaScript has four syntactic forms for making function objects: function + statements, function expressions, enhanced object literals, and the + => fart operator.

    +

    The function statement creates a variable and assigns the function object to + it. It should be used in a file or function body, but not inside of a block.

    +
    function name(parameters) { +
    statements
    + }
    +

    The function expression unfortunately looks like the function statement. It + may appear anywhere that an expression may appear, but not in statement + position and not in a loop. It produces a function object but does not + create a variable in which to store it.

    +
    function (parameters) { +
    statements
    + }
    +

    The enhanced object literal provides an ES6 shorthand for creating a property + whose value is a function, saving you from having to type + : colon and function.

    +
    { +
    name(parameters) { +
    statements
    + }
    + }
    +

    Finally, ES6 provides an even shorter form of function expression that leaves + out the words function and return:

    +
    (parameters) => + expression
    +

    JSLint requires the parens around the parameters, and forbids a + { left brace after the + => fart to avoid syntactic ambiguity.

    +

    Comma

    +

    The , comma operator is unnecessary and can + mask programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as + an operator. It does not expect to see elided elements in array literals. A + comma should not appear after the last element of an array literal or object + literal.

    +

    Blocks

    +

    JSLint expects blocks with function, + if, switch, while, for, + do, and try statements and nowhere else.

    +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    +

    JavaScript allows an if to be written like this:

    +
    if (condition)
    +    statement;
    +

    That form is known to contribute to mistakes in projects where many + programmers are working on the same code. That is why JSLint + expects the use of a block:

    +
    if (condition) {
    +    statements;
    +}
    +

    Experience shows that this form is more resilient.

    +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call. All other expression statements are considered + to be errors.

    +

    for

    +

    JSLint does not recommend use of the for statement. + Use array methods like forEach instead. The for + option will suppress some warnings. The forms of for that + JSLint accepts are restricted, excluding the new ES6 forms.

    +

    for in

    +

    JSLint does not recommend use of the for + in statement. Use Object.keys instead.

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, it also + loops through all of the properties that were inherited through the + prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written + without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    for (name in object) {
    +    if (object.hasOwnProperty(name)) {
    +        ....
    +    }
    +}
    +

    Note that the above code will fail if the object contains a property + named hasOwnProperty. Use Object.keys instead.

    +

    switch

    +

    A common error in switch statements is to forget to place a + break statement after each case, resulting in unintended + fall-through. JSLint expects that the statement before the next + case or default is one of these: + break, return, or throw.

    +

    with

    +

    The with statement was intended to provide a shorthand in + accessing properties in deeply nested objects. Unfortunately, it behaves + very badly when setting new properties. Never use the with + statement.

    +

    JSLint does not expect to see a with statement.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that + labels will be distinct from vars and parameters.

    +

    Unreachable Code

    +

    JSLint expects that a return, break, + or throw statement will be followed by a + } right brace or case or + default.

    +

    Confusing Pluses and Minuses

    +

    JSLint expects that + will not be followed by + + or ++, and that - will not be + followed by - or --. A misplaced space can turn + + + into ++, an error that is difficult to see. + Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ increment and + -- decrement operators have been known to + contribute to bad code by encouraging excessive trickiness. They are second + only to faulty architecture in enabling to viruses and other security + menaces. Also, preincrement/postincrement confusion can produce off-by-one + errors that are extremely difficult to diagnose. Fortunately, they are also + complete unnecessary. There are better ways to add 1 to a variable.

    +

    It is best to avoid these operators entirely and rely on += and + -= instead.

    +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. + JSLint looks for problems that may cause portability problems. + It also attempts to resolve visual ambiguities by recommending explicit + escapement.

    +

    JavaScript’s syntax for regular expression literals overloads the + / slash character. To avoid ambiguity, + JSLint expects that the character preceding a regular + expression literal is a ( left paren or + = equal or + : colon or + , comma character.

    +

    this

    +
    Having this in the language makes it harder to talk + about the language. It is like pair programming with Abbott and Costello. +
    +

    Avoid using this. Warnings about this can be + suppressed with option.this.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the + new prefix. The new prefix creates a new object + based on the function's prototype, and binds that object to the + function's implied this parameter. If you neglect to use the + new prefix, no new object will be made and this + will be bound to the global object.

    +

    JSLint enforces the convention that constructor functions be + given names with initial uppercase. JSLint does not expect to + see a function invocation with an initial uppercase name unless it has the + new prefix. JSLint does not expect to see the + new prefix used with functions whose names do not start with + initial uppercase.

    +

    JSLint does not expect to see the wrapper forms + new Number, new String, new Boolean.

    +

    JSLint does not expect to see new Object. + Use Object.create(null) instead.

    +

    Whitespace

    +

    JSLint has a specific set of rules about the use of whitespace. + Where possible, these rules are consistent with centuries of good practice + with literary style.

    +

    The indentation increases by 4 spaces when the last token on a line is + { left brace, + [ left bracket, + ( left paren. The matching closing + token will be the first token on a line, restoring the previous + indentation.

    +

    The ternary operator can be visually confusing, so + ? question mark and + : colon always begin a line and increase + the indentation by 4 spaces.

    +
    return (
    +    (the_token.id === "(string)" || the_token.id === "(number)")
    +    ? String(the_token.value)
    +    : the_token.id
    +);
    +

    The word function is always followed with one space.

    +

    Clauses (case, catch, default, + else, finally) are not statements and so should + not be indented like statements.

    +

    Spaces are used to make things that are not invocations look less like + invocations.

    +

    Tabs and spaces should not be mixed. We should pick just one in order to + avoid the problems that come from having both. Personal preference is an + extremely unreliable criterion. Neither offers a powerful advantage over the + other. Fifty years ago, tab had the advantage of consuming less memory, but + Moore's Law has eliminated that advantage. Space has one clear advantage + over tab: there is no reliable standard for how many spaces a tab + represents, but it is universally accepted that a space occupies a space. So + use spaces. You can edit with tabs if you must, but make sure it is spaces + again before you commit. Maybe someday we will finally get a universal + standard for tabs, but until that day comes, the better choice is spaces.

    +

    Report

    +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    +
      +
    • The line number on which it starts.
    • +
    • The function’s name. In the case of anonymous functions, + JSLint will attempt to + «guess» the name.
    • +
    • The parameters.
    • +
    • Variables: The variables that are declared in the function.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Exceptions: The variables that are declared by catch + clauses of try statements.
    • +
    • Outer: Variables used by this function that are declared in + other functions.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements.

    +

    Try it

    +

    Try it. Paste your script + into the window and click the + + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    JSLint is written entirely in JavaScript, so it can run + anywhere that JavaScript (or even Java) can run.

    +

    Perfectly fine

    +

    JSLint was designed to reject code that some would consider to + be perfectly fine. The reason for this is that JSLint's + purpose is to help produce programs that are free of error. That is + difficult in any language and is especially hard in JavaScript. + JSLint attempts to help you increase the visual distance + between correct programs and incorrect programs, making the remaining errors + more obvious. JSLint will give warnings about things that are + not necessarily wrong in the current situation, but which have been observed + to mask or obscure errors. Avoid those things when there are better options + available.

    +

    It is dangerous out there. JSLint is here to help.

    +

    Warning

    +

    JSLint will hurt your feelings. Side effects may include + headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, + dry mouth, cleaner code, and a reduced error rate.

    +

    Further Reading

    + +
    +

    + Code Conventions for the JavaScript Programming Language.

    + + + +
    + + diff --git a/branch.ignore_macro/index.html b/branch.ignore_macro/index.html new file mode 100644 index 000000000..822acaad6 --- /dev/null +++ b/branch.ignore_macro/index.html @@ -0,0 +1,108 @@ + + + + + + + + + +JSLint: The JavaScript Code Quality Tool + + +
    +
    Source
    +
    + + +
    +
    + Warnings +
    +
    +
    + Function Report +
    +
    +
    + Property Directive + +
    +
    + + + + +
    +
    Options +
    Assume... +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Global variables... + +
    +
    +
    + + diff --git a/branch.ignore_macro/jslint.css b/branch.ignore_macro/jslint.css new file mode 100644 index 000000000..cc3bf1696 --- /dev/null +++ b/branch.ignore_macro/jslint.css @@ -0,0 +1,341 @@ +@font-face { + font-family: 'Programma'; + font-weight: bold; + src: url('Programma-Bold.woff2') format('woff2'); +} +@font-face { + font-family: 'Daley'; + font-weight: bold; + src: url('Daley-Bold.woff2') format('woff2'); +} + +body { + background-color: antiquewhite; + color: black; + font-family: 'Daley', sans-serif; + margin: 0; + padding: 0; +} + +#JSLINT_TITLEBOX { + font-family: 'Daley', sans-serif; + margin: 0; + margin-left: 3%; + margin-right: 3%; + padding: 0; +} + +#JSLINT_TITLEBOX ul { + float: right; + font-size: 12pt; +} + +#JSLINT_TITLEBOX div { + color: darkslategray; + float: left; + font-size: 48pt; +} + +#JSLINT_ fieldset { + background-color: gainsboro; + border: 0; + clear: both; + margin-bottom: 1em; + margin-left: 3%; + margin-right: 3%; + margin-top: 1em; + padding: 0; + width: auto; +} +#JSLINT_ legend { + background-color: darkslategray; + border: 0; + color: white; + font-size: 100%; + font-style: normal; + font-weight: normal; + margin: 0; + padding-bottom: 0.25em; + padding-left: 0; + padding-right: 0; + padding-top: 0.25em; + text-align: center; + width: 100%; +} +#JSLINT_ button { + background-color: darkslategray; + border: 0; + color: white; + cursor: pointer; + font-family: 'Daley', sans-serif; + font-size: 100%; + font-style: normal; + margin-left: 1em; + margin-right: 1em; + padding-left: 1em; + padding-right: 1em; + padding-bottom: 0.25em; + padding-top: 0.25em; + text-align: center; +} +#JSLINT_ button:hover { + background-color: slategray; + color: white; + border: 0; +} + +#JSLINT_ button:active { + border: 0; + color: black; +} + +#JSLINT_ button:disabled { + background-color: gray; + border: 0; + color: gainsboro; +} + +a:visited { + color: #69565c; +} + +a:link { + color: black; +} + +#JSLINT_ input[type="text"] { + background-color: white; + border: 0; + color: black; + margin-bottom: 0.2em; + margin-right: 0.5em; + padding-left: 0.5em; + padding-right: 0.5em; + text-align: right; + width: 3em; +} + +#JSLINT_ textarea { + border: 0; + background-color: white; + border: 0; + font-family: Programma, monospace; + font-size: 100%; + font-weight: bold; + resize: none; +} + +#JSLINT_ textarea::selection { + background-color: yellow; + color: black; +} + +#JSLINT_NUMBER { + color: darkgray; + display: inline-block; + height: 4in; + margin: 2%; + margin-right: 0; + overflow: hidden; + padding-right: 0.5%; + text-align: right; + white-space: pre; + width: 4%; +} + +#JSLINT_SOURCE { + color: black; + display: inline-block; + font-size: 100%; + height: 4in; + margin: 2%; + margin-left: 0; + margin-right: 0; + overflow: auto; + padding-left: 0.5%; + white-space: pre; + width: 91%; +} + +#JSLINT_PROPERTY { + background-color: honeydew; + border: 0; + margin: 2%; + padding: 0.5%; + white-space: pre; + width: 95%; +} + +#JSLINT_GLOBAL { + white-space: normal; + width: 95%; +} +#JSLINT_ label { + font-family: sans-serif; + font-size: 90%; + padding-left: 0.25em; +} +#JSLINT_OPTIONS>div { + float: left; + margin: 0.5em; +} + +#JSLINT_ address { + color: black; + display: block; + float: right; + font-family: serif; + font-size: 90%; + margin-left: 1em; +} +#JSLINT_ dl { + background-color: cornsilk; + color: white; + margin: 0; + padding-bottom: 2pt; + padding-left: 1em; + padding-right: 1em; + padding-top: 2pt; +} +#JSLINT_ dfn { + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + margin-bottom: 2pt; +} +#JSLINT_ dt { + color: black; + display: block; + float: left; + font-family: serif; + font-size: 75%; + font-style: italic; + margin: 0; + width: 8em; + text-align: right; +} +#JSLINT_ dd { + color: black; + display: block; + font-family: Programma, monospace; + font-weight: bold; + margin-left: 8em; + padding-bottom: 2pt; +} +#JSLINT_WARNINGS>legend { + background-color: indianred; +} +#JSLINT_WARNINGS>div { + background-color: pink; + padding: 1em; +} +#JSLINT_WARNINGS cite { + color: black; + display: block; + font-family: serif; + font-size: 100%; + font-style: normal; + margin-bottom: 4pt; + margin-left: 20pt; + margin-right: 20pt; + margin-top: 4pt; + overflow-x: hidden; +} +#JSLINT_WARNINGS samp { + background-color: lavenderblush; + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + padding: 4pt; + margin-bottom: 0; + margin-left: 16pt; + margin-right: 16pt; + margin-top: 0; + white-space: pre-wrap; +} + +#JSLINT_REPORT center { + margin-top: 1em; +} + +#JSLINT_WARNINGS dl address { + color: black; + display: inline; + float: none; + font-size: 80%; + margin: 0; +} + +#JSLINT_REPORT>div { + padding: 1em; +} + +#JSLINT_ dl.level0 { + color: black; + background-color: white; +} + +#JSLINT_ dl.level1 { + color: black; + background-color: #ffffe0; /* yellow */ + margin-left: 1em; +} + +#JSLINT_ dl.level2 { + color: black; + background-color: #e0ffe0; /* green */ + margin-left: 2em; +} + +#JSLINT_ dl.level3 { + color: black; + background-color: #D0D0ff; /* blue */ + margin-left: 3em; +} + +#JSLINT_ dl.level4 { + background-color: #ffe0ff; /* purple */ + color: black; + margin-left: 4em; +} + +#JSLINT_ dl.level5 { + background-color: #ffe0e0; /* red */ + color: black; + margin-left: 5em; +} + +#JSLINT_ dl.level6 { + background-color: #ffe390; /* orange */ + color: black; + margin-left: 6em; +} + +#JSLINT_ dl.level7 { + background-color: #e0e0e0; /* gray */ + color: black; + margin-left: 7em; +} + +#JSLINT_ dl.level8 { + color: black; + margin-left: 8em; +} + +#JSLINT_ dl.level9 { + color: black; + margin-left: 9em; +} + +.none { + display: none; +} +.center { + text-align: center; +} diff --git a/branch.ignore_macro/jslint.js b/branch.ignore_macro/jslint.js new file mode 100644 index 000000000..d5773794b --- /dev/null +++ b/branch.ignore_macro/jslint.js @@ -0,0 +1,5068 @@ +// jslint.js +// 2020-11-06 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// jslint(source, option_object, global_array) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze, a string or an array of strings. +// option_object An object whose keys correspond to option names. +// global_array An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all of the functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// 1. If the source is a single string, split it into an array of strings. +// 2. Turn the source into an array of tokens. +// 3. Furcate the tokens into a parse tree. +// 4. Walk the tree, traversing all of the nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// 5. Check the whitespace between the tokens. + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*property + ignore, source_line, map + a, and, arity, assign, b, bad_assignment_a, bad_directive_a, bad_get, + bad_module_name_a, bad_option_a, bad_property_a, bad_set, bitwise, block, + body, browser, c, calls, catch, charCodeAt, closer, closure, code, column, + concat, constant, context, convert, couch, create, d, dead, default, devel, + directive, directives, disrupt, dot, duplicate_a, edition, ellipsis, else, + empty_block, eval, every, expected_a, expected_a_at_b_c, + expected_a_b, expected_a_b_from_c_d, expected_a_before_b, + expected_a_next_at_b, expected_digits_after_a, expected_four_digits, + expected_identifier_a, expected_line_break_a_b, expected_regexp_factor_a, + expected_space_a_b, expected_statements_a, expected_string_a, + expected_type_string_a, exports, expression, extra, finally, flag, for, + forEach, free, freeze, freeze_exports, from, froms, fud, fudge, + function_in_loop, functions, g, getset, global, i, id, identifier, import, + inc, indexOf, infix_in, init, initial, isArray, isNaN, join, json, keys, + label, label_a, lbp, led, length, level, line, lines, live, long, loop, m, + margin, match, message, misplaced_a, misplaced_directive_a, missing_browser, + missing_m, module, naked_block, name, names, nested_comment, new, node, + not_label_a, nr, nud, number_isNaN, ok, open, opening, option, + out_of_scope_a, parameters, parent, pop, property, push, quote, raw, + redefinition_a_b, replace, required_a_optional_b, reserved_a, role, search, + shebang, signature, single, slice, some, sort, split, startsWith, statement, + stop, subscript_a, switch, test, this, thru, toString, todo_comment, + tokens, too_long, too_many_digits, tree, try, type, u, unclosed_comment, + unclosed_mega, unclosed_string, undeclared_a, unexpected_a, + unexpected_a_after_b, unexpected_a_before_b, unexpected_at_top_level_a, + unexpected_char_a, unexpected_comment, unexpected_directive_a, + unexpected_expression_a, unexpected_label_a, unexpected_parens, + unexpected_space_a_b, unexpected_statement_a, unexpected_trailing_space, + unexpected_typeof_a, uninitialized_a, unreachable_a, + unregistered_property_a, unused_a, use_double, use_open, use_spaces, + used, value, var_loop, var_switch, variable, warning, warnings, + weird_condition_a, weird_expression_a, weird_loop, weird_relation_a, white, + wrap_condition, wrap_immediate, wrap_parameter, wrap_regexp, wrap_unary, + wrapped, writable, y +*/ + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than '{}' because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +function populate(array, object = empty(), value = true) { + +// Augment an object by taking property names from an array of strings. + + array.forEach(function (name) { + object[name] = value; + }); + return object; +} + +const allowed_option = { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + bitwise: true, + browser: [ + "caches", "CharacterData", "clearInterval", "clearTimeout", "document", + "DocumentType", "DOMException", "Element", "Event", "event", "fetch", + "FileReader", "FontFace", "FormData", "history", "IntersectionObserver", + "localStorage", "location", "MutationObserver", "name", "navigator", + "screen", "sessionStorage", "setInterval", "setTimeout", "Storage", + "TextDecoder", "TextEncoder", "URL", "window", "Worker", + "XMLHttpRequest" + ], + couch: [ + "emit", "getRow", "isArray", "log", "provides", "registerType", + "require", "send", "start", "sum", "toJSON" + ], + convert: true, + devel: [ + "alert", "confirm", "console", "prompt" + ], + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + node: [ + "Buffer", "clearImmediate", "clearInterval", "clearTimeout", + "console", "exports", "module", "process", "require", + "setImmediate", "setInterval", "setTimeout", "TextDecoder", + "TextEncoder", "URL", "URLSearchParams", "__dirname", "__filename" + ], + single: true, + this: true, + white: true +}; + +const anticondition = populate([ + "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%", + "typeof", "(number)", "(string)" +]); + +// These are the bitwise operators. + +const bitwiseop = populate([ + "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=", + ">>>", ">>>=" +]); + +const escapeable = populate([ + "\\", "/", "`", "b", "f", "n", "r", "t" +]); + +const opener = { + +// The open and close pairs. + + "(": ")", // paren + "[": "]", // bracket + "{": "}", // brace + "${": "}" // mega +}; + +// The relational operators. + +const relationop = populate([ + "!=", "!==", "==", "===", "<", "<=", ">", ">=" +]); + +// This is the set of infix operators that require a space on each side. + +const spaceop = populate([ + "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/", + "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=", + ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" +]); + +const standard = [ + +// These are the globals that are provided by the language standard. + + "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI", + "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error", + "EvalError", "Float32Array", "Float64Array", "Generator", + "GeneratorFunction", "Int8Array", "Int16Array", "Int32Array", "Intl", + "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat", + "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", + "Set", "String", "Symbol", "SyntaxError", "System", "TypeError", + "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", + "URIError", "WeakMap", "WeakSet" +]; + +const bundle = { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + and: "The '&&' subexpression should be wrapped in parens.", + bad_assignment_a: "Bad assignment to '{a}'.", + bad_directive_a: "Bad directive '{a}'.", + bad_get: "A get function takes no parameters.", + bad_module_name_a: "Bad module name '{a}'.", + bad_option_a: "Bad option '{a}'.", + bad_property_a: "Bad property name '{a}'.", + bad_set: "A set function takes one parameter.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + expected_a: "Expected '{a}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: ( + "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'." + ), + expected_a_before_b: "Expected '{a}' before '{b}'.", + expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.", + expected_digits_after_a: "Expected digits after '{a}'.", + expected_four_digits: "Expected four digits after '\\u'.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.", + expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.", + expected_space_a_b: "Expected one space between '{a}' and '{b}'.", + expected_statements_a: "Expected statements before '{a}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + expected_type_string_a: "Expected a type string and instead saw '{a}'.", + freeze_exports: ( + "Expected 'Object.freeze('. All export values should be frozen." + ), + function_in_loop: "Don't make functions within a loop.", + infix_in: ( + "Unexpected 'in'. Compare with undefined, " + + "or use the hasOwnProperty method instead." + ), + label_a: "'{a}' is a statement label.", + misplaced_a: "Place '{a}' at the outermost level.", + misplaced_directive_a: ( + "Place the '/*{a}*/' directive before the first statement." + ), + missing_browser: "/*global*/ requires the Assume a browser option.", + missing_m: "Expected 'm' flag on a multiline regular expression.", + naked_block: "Naked block.", + nested_comment: "Nested comment.", + not_label_a: "'{a}' is not a label.", + number_isNaN: "Use Number.isNaN function to compare with NaN.", + out_of_scope_a: "'{a}' is out of scope.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + required_a_optional_b: ( + "Required parameter '{a}' after optional parameter '{b}'." + ), + reserved_a: "Reserved name '{a}'.", + subscript_a: "['{a}'] is better written in dot notation.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line is longer than 80 characters.", + too_many_digits: "Too many digits.", + unclosed_comment: "Unclosed comment.", + unclosed_mega: "Unclosed mega literal.", + unclosed_string: "Unclosed string.", + undeclared_a: "Undeclared '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_a_after_b: "Unexpected '{a}' after '{b}'.", + unexpected_a_before_b: "Unexpected '{a}' before '{b}'.", + unexpected_at_top_level_a: "Expected '{a}' to be in a function.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_directive_a: "When using modules, don't use directive '/*{a}'.", + unexpected_expression_a: ( + "Unexpected expression '{a}' in statement position." + ), + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_parens: "Don't wrap function literals in parens.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_statement_a: ( + "Unexpected statement '{a}' in expression position." + ), + unexpected_trailing_space: "Unexpected trailing space.", + unexpected_typeof_a: ( + "Unexpected 'typeof'. Use '===' to compare directly with {a}." + ), + uninitialized_a: "Uninitialized '{a}'.", + unreachable_a: "Unreachable '{a}'.", + unregistered_property_a: "Unregistered property name '{a}'.", + unused_a: "Unused '{a}'.", + use_double: "Use double quotes, not single quotes.", + use_open: ( + "Wrap a ternary expression in parens, " + + "with a line break after the left paren." + ), + use_spaces: "Use spaces, not tabs.", + var_loop: "Don't declare variables in a loop.", + var_switch: "Don't declare variables in a switch.", + weird_condition_a: "Weird condition '{a}'.", + weird_expression_a: "Weird expression '{a}'.", + weird_loop: "Weird loop.", + weird_relation_a: "Weird relation '{a}'.", + wrap_condition: "Wrap the condition in parens.", + wrap_immediate: ( + "Wrap an immediate function invocation in parentheses to assist " + + "the reader in understanding that the expression is the result " + + "of a function, and not the function itself." + ), + wrap_parameter: "Wrap the parameter in parens.", + wrap_regexp: "Wrap this regexp in parens to avoid confusion.", + wrap_unary: "Wrap the unary expression in parens." +}; + +// Regular expression literals: + +function tag_regexp(strings) { + return new RegExp(strings.raw[0].replace(/\s/g, "")); +} + +// supplant {variables} +const rx_supplant = /\{([^{}]*)\}/g; +// carriage return, carriage return linefeed, or linefeed +const rx_crlf = tag_regexp ` + \n + | \r \n? +`; +// identifier +const rx_identifier = tag_regexp ` ^( + [ a-z A-Z _ $ ] + [ a-z A-Z 0-9 _ $ ]* +)$`; +const rx_module = tag_regexp ` ^ [ a-z A-Z 0-9 _ $ : . @ \- \/ ]+ $ `; +const rx_bad_property = tag_regexp ` + ^_ + | \$ + | Sync $ + | _ $ +`; +// star slash +const rx_star_slash = tag_regexp ` \* \/ `; +// slash star +const rx_slash_star = tag_regexp ` \/ \* `; +// slash star or ending slash +const rx_slash_star_or_slash = tag_regexp ` \/ \* | \/ $ `; +// uncompleted work comment +const rx_todo = tag_regexp ` \b (?: + todo + | TO \s? DO + | HACK +) \b `; +// tab +const rx_tab = /\t/g; +// directive +const rx_directive = tag_regexp ` ^ ( + jslint + | property + | global +) \s+ ( .* ) $ `; +const rx_directive_part = tag_regexp ` ^ ( + [ a-z A-Z $ _ ] [ a-z A-Z 0-9 $ _ ]* +) (?: + : \s* ( true | false ) +)? ,? \s* ( .* ) $ `; +// token +const rx_token = tag_regexp ` ^ ( + (\s+) + | ( + [ a-z A-Z _ $ ] + [ a-z A-Z 0-9 _ $ ]* + ) + | [ + ( ) { } \[ \] , : ; ' " ~ \` + ] + | \? [ ? . ]? + | = (?: + = =? + | > + )? + | \.+ + | \* [ * \/ = ]? + | \/ [ * \/ ]? + | \+ [ = + ]? + | - [ = \- ]? + | [ \^ % ] =? + | & [ & = ]? + | \| [ | = ]? + | >{1,3} =? + | < = "a" && string <= "z\uffff") + || (string >= "A" && string <= "Z\uffff") + ); +} + +function supplant(string, object) { + return string.replace(rx_supplant, function (found, filling) { + const replacement = object[filling]; + return ( + replacement !== undefined + ? replacement + : found + ); + }); +} + +let anon; // The guessed name for anonymous functions. +let blockage; // The current block. +let block_stack; // The stack of blocks. +let declared_globals; // The object containing the global declarations. +let directives; // The directive comments. +let directive_mode; // true if directives are still allowed. +let early_stop; // true if JSLint cannot finish. +let exports; // The exported names and values. +let froms; // The array collecting all import-from strings. +let fudge; // true if the natural numbers start with 1. +let functionage; // The current function. +let functions; // The array containing all of the functions. +let global; // The global object; the outermost context. +let json_mode; // true if parsing JSON. +let lines; // The array containing source lines. +let lines_extra; // The array containing source lines metadata. +let mega_mode; // true if currently parsing a megastring literal. +let module_mode; // true if import or export was used. +let next_token; // The next token to be examined in the parse. +let option; // The options parameter. +let property; // The object containing the tallied property names. +let shebang; // true if a #! was seen on the first line. +let stack; // The stack of functions. +let syntax; // The object containing the parser. +let token; // The current token being examined in the parse. +let token_nr; // The number of the next token. +let tokens; // The array of tokens. +let tenure; // The predefined property registry. +let tree; // The abstract parse tree. +let var_mode; // "var" if using var; "let" if using let. +let warnings; // The array collecting all generated warnings. + +// Error reportage functions: + +function artifact(the_token) { + +// Return a string representing an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); +} + +function artifact_line(the_token) { + +// Return the fudged line number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.line + fudge; +} + +function artifact_column(the_token) { + +// Return the fudged column number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.from + fudge; +} + +function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + const warning = { // ~~ + name: "JSLintError", + column, + line, + code + }; + if (a !== undefined) { + warning.a = a; + } + if (b !== undefined) { + warning.b = b; + } + if (c !== undefined) { + warning.c = c; + } + if (d !== undefined) { + warning.d = d; + } + warning.message = supplant(bundle[code] || code, warning); + // hack-jslint - ignore warning + if (!Object.assign(warning, lines_extra[warning.line]).ignore) { + warnings.push(warning); + } + return warning; +} + +function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); +} + +function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + if (the_token.warning === undefined) { + the_token.warning = warn_at( + code, + the_token.line, + the_token.from, + a || artifact(the_token), + b, + c, + d + ); + return the_token.warning; + } +} + +function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); +} + +// Tokenize: + +function tokenize(source) { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + +// If the source is not an array, then it is split into lines at the +// carriage return/linefeed. + + lines = ( + Array.isArray(source) + ? source + : source.split(rx_crlf) + ); + tokens = []; + + let char; // a popular character + let column = 0; // the column number of the next character + let first; // the first token + let from; // the starting column number of the token + let line = -1; // the line number of the next character + let line_ignore; // flag indicating whether current line should + // be ignored + let nr = 0; // the next token number + let previous = global; // the previous token including comments + let prior = global; // the previous token excluding comments + let mega_from; // the starting column of megastring + let mega_line; // the starting line of megastring + let regexp_seen; // regular expression literal seen on this line + let snippet; // a piece of string + let source_line = ""; // the remaining line source string + let whole_line = ""; // the whole line source string + + if (lines[0].startsWith("#!")) { + line = 0; + shebang = true; + } + + function next_line() { + +// Put the next line of source in source_line. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + let at; + let match; + if ( + !option.long + && whole_line.length > 80 + && !json_mode + && first + && !regexp_seen + ) { + warn_at("too_long", line, 80); + } + column = 0; + line += 1; + regexp_seen = false; + source_line = lines[line]; + whole_line = source_line || ""; + if (source_line !== undefined) { + // hack-jslint - parse source_line for ignore-macro + lines_extra[line] = { + line, + source_line + }; + match = ( + source_line.match( + /^\/\*\u0020jslint\u0020(ignore:start|ignore:end)\u0020\*\/$/m + ) || + source_line.slice(-50).match( + /\u0020\/\/\u0020jslint\u0020(ignore:line)$/m + ) + ); + switch (match && match[1]) { + case "ignore:end": + line_ignore = undefined; + break; + case "ignore:line": + line_ignore = "line"; + break; + case "ignore:start": + line_ignore = true; + break; + } + lines_extra[line].ignore = line_ignore; + switch (line_ignore) { + case "line": + line_ignore = undefined; + break; + case true: + source_line = ""; + break; + } + at = source_line.search(rx_tab); + if (at >= 0) { + if (!option.white) { + warn_at("use_spaces", line, at + 1); + } + source_line = source_line.replace(rx_tab, " "); + } + if (!option.white && source_line.slice(-1) === " ") { + warn_at( + "unexpected_trailing_space", + line, + source_line.length - 1 + ); + } + } + return source_line; + } + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions snip, next_char, back_char, +// some_digits, and escape help in the parsing of literals. + + function snip() { + +// Remove the last character from snippet. + + snippet = snippet.slice(0, -1); + } + + function next_char(match) { + +// Get the next character from the source line. Remove it from the source_line, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + return stop_at( + ( + char === "" + ? "expected_a" + : "expected_a_b" + ), + line, + column - 1, + match, + char + ); + } + if (source_line) { + char = source_line[0]; + source_line = source_line.slice(1); + snippet += char; + } else { + char = ""; + snippet += " "; + } + column += 1; + return char; + } + + function back_char() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the source_line. + + if (snippet) { + char = snippet.slice(-1); + source_line = char + source_line; + column -= 1; + snip(); + } else { + char = ""; + } + return char; + } + + function some_digits(rx, quiet) { + const digits = source_line.match(rx)[0]; + const length = digits.length; + if (!quiet && length === 0) { + warn_at("expected_digits_after_a", line, column, snippet); + } + column += length; + source_line = source_line.slice(length); + snippet += digits; + next_char(); + return length; + } + + function escape(extra) { + next_char("\\"); + if (escapeable[char] === true) { + return next_char(); + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "u") { + if (next_char("u") === "{") { + if (json_mode) { + warn_at("unexpected_a", line, column - 1, char); + } + if (some_digits(rx_hexs) > 5) { + warn_at("too_many_digits", line, column - 1); + } + if (next_char() !== "}") { + stop_at("expected_a_before_b", line, column, "}", char); + } + return next_char(); + } + back_char(); + if (some_digits(rx_hexs, true) < 4) { + warn_at("expected_four_digits", line, column - 1); + } + return; + } + if (extra && extra.indexOf(char) >= 0) { + return next_char(); + } + warn_at("unexpected_a_before_b", line, column - 2, "\\", char); + } + + function make(id, value, identifier) { + +// Make the token object and append it to the tokens list. + + const the_token = { + from, + id, + identifier: Boolean(identifier), + line, + nr, + thru: column + }; + tokens[nr] = the_token; + nr += 1; + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + directive_mode = false; + } + +// If the token is to have a value, give it one. + + if (value !== undefined) { + the_token.value = value; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option.white. + + if ( + previous.line === line + && previous.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (previous.id === "(comment)" || previous.id === "(regexp)") + ) { + warn( + "expected_space_a_b", + the_token, + artifact(previous), + artifact(the_token) + ); + } + if (previous.id === "." && id === "(number)") { + warn("expected_a_before_b", previous, "0", "."); + } + if (prior.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// The previous token is used to detect adjacency problems. + + previous = the_token; + +// The prior token is a previous token that was not a comment. The prior token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (previous.id !== "(comment)") { + prior = previous; + } + return the_token; + } + + function parse_directive(the_comment, body) { + +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + + const result = body.match(rx_directive_part); + if (result) { + let allowed; + const name = result[1]; + const value = result[2]; + if (the_comment.directive === "jslint") { + allowed = allowed_option[name]; + if ( + typeof allowed === "boolean" + || typeof allowed === "object" + ) { + if ( + value === "" + || value === "true" + || value === undefined + ) { + option[name] = true; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } else if (value === "false") { + option[name] = false; + } else { + warn("bad_option_a", the_comment, name + ":" + value); + } + } else { + warn("bad_option_a", the_comment, name); + } + } else if (the_comment.directive === "property") { + if (tenure === undefined) { + tenure = empty(); + } + tenure[name] = true; + } else if (the_comment.directive === "global") { + if (value) { + warn("bad_option_a", the_comment, name + ":" + value); + } + declared_globals[name] = false; + module_mode = the_comment; + } + return parse_directive(the_comment, result[3]); + } + if (body) { + return stop("bad_directive_a", the_comment, body); + } + } + + function comment(snippet) { + +// Make a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + + const the_comment = make("(comment)", snippet); + if (Array.isArray(snippet)) { + snippet = snippet.join(" "); + } + if (!option.devel && rx_todo.test(snippet)) { + warn("todo_comment", the_comment); + } + const result = snippet.match(rx_directive); + if (result) { + if (!directive_mode) { + warn_at("misplaced_directive_a", line, from, result[1]); + } else { + the_comment.directive = result[1]; + parse_directive(the_comment, result[2]); + } + directives.push(the_comment); + } + return the_comment; + } + + function regexp() { + +// Parse a regular expression literal. + + let multi_mode = false; + let result; + let value; + regexp_seen = true; + + function quantifier() { + +// Match an optional quantifier. + + if (char === "?" || char === "*" || char === "+") { + next_char(); + } else if (char === "{") { + if (some_digits(rx_digits, true) === 0) { + warn_at("expected_a_before_b", line, column, "0", ","); + } + if (char === ",") { + some_digits(rx_digits, true); + } + next_char("}"); + } else { + return; + } + if (char === "?") { + next_char("?"); + } + } + + function subklass() { + +// Match a character in a character class. + + if (char === "\\") { + escape("BbDdSsWw-[]^"); + return true; + } + if ( + char === "" + || char === "[" + || char === "]" + || char === "/" + || char === "^" + || char === "-" + ) { + return false; + } + if (char === " ") { + warn_at("expected_a_b", line, column, "\\u0020", " "); + } else if (char === "`" && mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char(); + return true; + } + + function ranges() { + +// Match a range of subclasses. + + if (subklass()) { + if (char === "-") { + next_char("-"); + if (!subklass()) { + return stop_at( + "unexpected_a", + line, + column - 1, + "-" + ); + } + } + return ranges(); + } + } + + function klass() { + +// Match a class. + + next_char("["); + if (char === "^") { + next_char("^"); + } + (function classy() { + ranges(); + if (char !== "]" && char !== "") { + warn_at( + "expected_a_before_b", + line, + column, + "\\", + char + ); + next_char(); + return classy(); + } + }()); + next_char("]"); + } + + function choice() { + + function group() { + +// Match a group that starts with left paren. + + next_char("("); + if (char === "?") { + next_char("?"); + if (char === "=" || char === "!") { + next_char(); + } else { + next_char(":"); + } + } else if (char === ":") { + warn_at("expected_a_before_b", line, column, "?", ":"); + } + choice(); + next_char(")"); + } + + function factor() { + if ( + char === "" + || char === "/" + || char === "]" + || char === ")" + ) { + return false; + } + if (char === "(") { + group(); + return true; + } + if (char === "[") { + klass(); + return true; + } + if (char === "\\") { + escape("BbDdSsWw^${}[]():=!.|*+?"); + return true; + } + if ( + char === "?" + || char === "+" + || char === "*" + || char === "}" + || char === "{" + ) { + warn_at( + "expected_a_before_b", + line, + column - 1, + "\\", + char + ); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column - 1, "`"); + } + } else if (char === " ") { + warn_at( + "expected_a_b", + line, + column - 1, + "\\s", + " " + ); + } else if (char === "$") { + if (source_line[0] !== "/") { + multi_mode = true; + } + } else if (char === "^") { + if (snippet !== "^") { + multi_mode = true; + } + } + next_char(); + return true; + } + + function sequence(follow) { + if (factor()) { + quantifier(); + return sequence(true); + } + if (!follow) { + warn_at("expected_regexp_factor_a", line, column, char); + } + } + +// Match a choice (a sequence that can be followed by | and another choice). + + sequence(); + if (char === "|") { + next_char("|"); + return choice(); + } + } + +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + next_char(); + if (char === "=") { + warn_at("expected_a_before_b", line, column, "\\", "="); + } + choice(); + +// Make sure there is a closing slash. + + snip(); + value = snippet; + next_char("/"); + +// Process dangling flag letters. + + const allowed = { + g: true, + i: true, + m: true, + u: true, + y: true + }; + const flag = empty(); + (function make_flag() { + if (is_letter(char)) { + if (allowed[char] !== true) { + warn_at("unexpected_a", line, column, char); + } + allowed[char] = false; + flag[char] = true; + next_char(); + return make_flag(); + } + }()); + back_char(); + if (char === "/" || char === "*") { + return stop_at("unexpected_a", line, from, char); + } + result = make("(regexp)", char); + result.flag = flag; + result.value = value; + if (multi_mode && !flag.m) { + warn_at("missing_m", line, column); + } + return result; + } + + function string(quote) { + +// Make a string token. + + let the_token; + snippet = ""; + next_char(); + + return (function next() { + if (char === quote) { + snip(); + the_token = make("(string)", snippet); + the_token.quote = quote; + return the_token; + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "\\") { + escape(quote); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char("`"); + } else { + next_char(); + } + return next(); + }()); + } + + function frack() { + if (char === ".") { + some_digits(rx_digits); + } + if (char === "E" || char === "e") { + next_char(); + if (char !== "+" && char !== "-") { + back_char(); + } + some_digits(rx_digits); + } + } + + function number() { + if (snippet === "0") { + next_char(); + if (char === ".") { + frack(); + } else if (char === "b") { + some_digits(rx_bits); + } else if (char === "o") { + some_digits(rx_octals); + } else if (char === "x") { + some_digits(rx_hexs); + } + } else { + next_char(); + frack(); + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + return stop_at( + "unexpected_a_after_b", + line, + column - 1, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + back_char(); + return make("(number)", snippet); + } + + function lex() { + let array; + let i = 0; + let j = 0; + let last; + let result; + let the_token; + +// This should properly be a tail recursive function, but sadly, conformant +// implementations of ES6 are still rare. This is the ideal code: + +// if (!source_line) { +// source_line = next_line(); +// from = 0; +// return ( +// source_line === undefined +// ? ( +// mega_mode +// ? stop_at("unclosed_mega", mega_line, mega_from) +// : make("(end)") +// ) +// : lex() +// ); +// } + +// Unfortunately, incompetent JavaScript engines will sometimes fail to execute +// it correctly. So for now, we do it the old fashioned way. + + while (!source_line) { + source_line = next_line(); + from = 0; + if (source_line === undefined) { + return ( + mega_mode + ? stop_at("unclosed_mega", mega_line, mega_from) + : make("(end)") + ); + } + } + + from = column; + result = source_line.match(rx_token); + +// result[1] token +// result[2] whitespace +// result[3] identifier +// result[4] number +// result[5] rest + + if (!result) { + return stop_at( + "unexpected_char_a", + line, + column, + source_line[0] + ); + } + + snippet = result[1]; + column += snippet.length; + source_line = result[5]; + +// Whitespace was matched. Call lex again to get more. + + if (result[2]) { + return lex(); + } + +// The token is an identifier. + + if (result[3]) { + return make(snippet, undefined, true); + } + +// The token is a number. + + if (result[4]) { + return number(snippet); + } + +// The token is a string. + + if (snippet === "\"") { + return string(snippet); + } + if (snippet === "'") { + if (!option.single) { + warn_at("use_double", line, column); + } + return string(snippet); + } + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (snippet === "`") { + if (mega_mode) { + return stop_at("expected_a_b", line, column, "}", "`"); + } + snippet = ""; + mega_from = from; + mega_line = line; + mega_mode = true; + +// Parsing a mega literal is tricky. First make a ` token. + + make("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + (function part() { + const at = source_line.search(rx_mega); + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + if (at < 0) { + snippet += source_line + "\n"; + return ( + next_line() === undefined + ? stop_at("unclosed_mega", mega_line, mega_from) + : part() + ); + } + snippet += source_line.slice(0, at); + column += at; + source_line = source_line.slice(at); + if (source_line[0] === "\\") { + snippet += source_line.slice(0, 2); + source_line = source_line.slice(2); + column += 2; + return part(); + } + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + make("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then make tokens that will become part of an expression until +// a } token is made. + + if (source_line[0] === "$") { + column += 2; + make("${"); + source_line = source_line.slice(2); + (function expr() { + const id = lex().id; + if (id === "{") { + return stop_at( + "expected_a_b", + line, + column, + "}", + "{" + ); + } + if (id !== "}") { + return expr(); + } + }()); + return part(); + } + }()); + source_line = source_line.slice(1); + column += 1; + mega_mode = false; + return make("`"); + } + +// The token is a // comment. + + if (snippet === "//") { + snippet = source_line; + source_line = ""; + the_token = comment(snippet); + if (mega_mode) { + warn("unexpected_comment", the_token, "`"); + } + return the_token; + } + +// The token is a /* comment. + + if (snippet === "/*") { + array = []; + if (source_line[0] === "/") { + warn_at("unexpected_a", line, column + i, "/"); + } + (function next() { + if (source_line > "") { + i = source_line.search(rx_star_slash); + if (i >= 0) { + return; + } + j = source_line.search(rx_slash_star); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + } + array.push(source_line); + source_line = next_line(); + if (source_line === undefined) { + return stop_at("unclosed_comment", line, column); + } + return next(); + }()); + snippet = source_line.slice(0, i); + j = snippet.search(rx_slash_star_or_slash); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + array.push(snippet); + column += i + 2; + source_line = source_line.slice(i + 2); + return comment(array); + } + +// The token is a slash. + + if (snippet === "/") { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + if (prior.identifier) { + if (!prior.dot) { + if (prior.id === "return") { + return regexp(); + } + if ( + prior.id === "(begin)" + || prior.id === "case" + || prior.id === "delete" + || prior.id === "in" + || prior.id === "instanceof" + || prior.id === "new" + || prior.id === "typeof" + || prior.id === "void" + || prior.id === "yield" + ) { + the_token = regexp(); + return stop("unexpected_a", the_token); + } + } + } else { + last = prior.id[prior.id.length - 1]; + if ("(,=:?[".indexOf(last) >= 0) { + return regexp(); + } + if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) { + the_token = regexp(); + warn("wrap_regexp", the_token); + return the_token; + } + } + if (source_line[0] === "=") { + column += 1; + source_line = source_line.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + } + return make(snippet); + } + + first = lex(); + json_mode = first.id === "{" || first.id === "["; + +// This loop will be replaced with a recursive call to lex when ES6 has been +// finished and widely deployed and adopted. + + while (true) { + if (lex().id === "(end)") { + break; + } + } +} + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + +function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!rx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!rx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property[id] === "number") { + property[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (tenure !== undefined) { + if (tenure[id] !== true) { + warn("unregistered_property_a", name); + } + } else { + if (name.identifier && rx_bad_property.test(id)) { + warn("bad_property_a", name); + } + } + property[id] = 1; + } + return id; +} + +function dispense() { + +// Deliver the next token, skipping the comments. + + const cadet = tokens[token_nr]; + token_nr += 1; + if (cadet.id === "(comment)") { + if (json_mode) { + warn("unexpected_a", cadet); + } + return dispense(); + } else { + return cadet; + } +} + +function lookahead() { + +// Look ahead one token without advancing. + + const old_token_nr = token_nr; + const cadet = dispense(true); + token_nr = old_token_nr; + return cadet; +} + +function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if (token.identifier && token.id !== "function") { + anon = token.id; + } else if (token.id === "(string)" && rx_identifier.test(token.value)) { + anon = token.value; + } + +// Attempt to match next_token with an expected id. + + if (id !== undefined && next_token.id !== id) { + return ( + match === undefined + ? stop("expected_a_b", next_token, id, artifact()) + : stop( + "expected_a_b_from_c_d", + next_token, + id, + artifact(match), + artifact_line(match), + artifact(next_token) + ) + ); + } + +// Promote the tokens, skipping comments. + + token = next_token; + next_token = dispense(); + if (next_token.id === "(end)") { + token_nr -= 1; + } +} + +// Parsing of JSON is simple: + +function json_value() { + let negative; + if (next_token.id === "{") { + return (function json_object() { + const brace = next_token; + const object = empty(); + const properties = []; + brace.expression = properties; + advance("{"); + if (next_token.id !== "}") { + (function next() { + let name; + let value; + if (next_token.quote !== "\"") { + warn( + "unexpected_a", + next_token, + next_token.quote + ); + } + name = next_token; + advance("(string)"); + if (object[token.value] !== undefined) { + warn("duplicate_a", token); + } else if (token.value === "__proto__") { + warn("bad_property_a", token); + } else { + object[token.value] = token; + } + advance(":"); + value = json_value(); + value.label = name; + properties.push(value); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("}", brace); + return brace; + }()); + } + if (next_token.id === "[") { + return (function json_array() { + const bracket = next_token; + const elements = []; + bracket.expression = elements; + advance("["); + if (next_token.id !== "]") { + (function next() { + elements.push(json_value()); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]", bracket); + return bracket; + }()); + } + if ( + next_token.id === "true" + || next_token.id === "false" + || next_token.id === "null" + ) { + advance(); + return token; + } + if (next_token.id === "(number)") { + if (!rx_JSON_number.test(next_token.value)) { + warn("unexpected_a"); + } + advance(); + return token; + } + if (next_token.id === "(string)") { + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + advance(); + return token; + } + if (next_token.id === "-") { + negative = next_token; + negative.arity = "unary"; + advance("-"); + advance("(number)"); + if (!rx_JSON_number.test(token.value)) { + warn("unexpected_a", token); + } + negative.expression = token; + return negative; + } + stop("unexpected_a"); +} + +// Now we parse JavaScript. + +function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + const id = name.id; + +// Reserved words may not be enrolled. + + if (syntax[id] !== undefined && id !== "ignore") { + warn("reserved_a", name); + } else { + +// Has the name been enrolled in this context? + + let earlier = functionage.context[id]; + if (earlier) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + +// Has the name been enrolled in an outer context? + + } else { + stack.forEach(function (value) { + const item = value.context[id]; + if (item !== undefined) { + earlier = item; + } + }); + if (earlier) { + if (id === "ignore") { + if (earlier.role === "variable") { + warn("unexpected_a", name); + } + } else { + if ( + ( + role !== "exception" + || earlier.role !== "exception" + ) + && role !== "parameter" + && role !== "function" + ) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + } + } + } + +// Enroll it. + + functionage.context[id] = name; + name.dead = true; + name.parent = functionage; + name.init = false; + name.role = role; + name.used = 0; + name.writable = !readonly; + } + } +} + +function expression(rbp, initial) { + +// This is the heart of the Pratt parser. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// nud Null denotation +// led Left denotation +// lbp Left binding power +// rbp Right binding power + +// It processes a nud (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop. It +// returns the expression's parse tree. + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement, + + if (!initial) { + advance(); + } + the_symbol = syntax[token.id]; + if (the_symbol !== undefined && the_symbol.nud !== undefined) { + left = the_symbol.nud(); + } else if (token.identifier) { + left = token; + left.arity = "variable"; + } else { + return stop("unexpected_a", token); + } + (function right() { + the_symbol = syntax[next_token.id]; + if ( + the_symbol !== undefined + && the_symbol.led !== undefined + && rbp < the_symbol.lbp + ) { + advance(); + left = the_symbol.led(left); + return right(); + } + }()); + return left; +} + +function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = next_token; + let the_value; + the_paren.free = true; + advance("("); + the_value = expression(0); + advance(")"); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (anticondition[the_value.id] === true) { + warn("unexpected_a", the_value); + } + return the_value; +} + +function is_weird(thing) { + return ( + thing.id === "(regexp)" + || thing.id === "{" + || thing.id === "=>" + || thing.id === "function" + || (thing.id === "[" && thing.arity === "unary") + ); +} + +function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + return ( + Array.isArray(b) + && a.length === b.length + && a.every(function (value, index) { + return are_similar(value, b[index]); + }) + ); + } + if (Array.isArray(b)) { + return false; + } + if (a.id === "(number)" && b.id === "(number)") { + return a.value === b.value; + } + let a_string; + let b_string; + if (a.id === "(string)") { + a_string = a.value; + } else if (a.id === "`" && a.constant) { + a_string = a.value[0]; + } + if (b.id === "(string)") { + b_string = b.value; + } else if (b.id === "`" && b.constant) { + b_string = b.value[0]; + } + if (typeof a_string === "string") { + return a_string === b_string; + } + if (is_weird(a) || is_weird(b)) { + return false; + } + if (a.arity === b.arity && a.id === b.id) { + if (a.id === ".") { + return ( + are_similar(a.expression, b.expression) + && are_similar(a.name, b.name) + ); + } + if (a.arity === "unary") { + return are_similar(a.expression, b.expression); + } + if (a.arity === "binary") { + return ( + a.id !== "(" + && are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + ); + } + if (a.arity === "ternary") { + return ( + are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + && are_similar(a.expression[2], b.expression[2]) + ); + } + if (a.arity === "function" && a.arity === "regexp") { + return false; + } + return true; + } + return false; +} + +function semicolon() { + +// Try to match a semicolon. + + if (next_token.id === ";") { + advance(";"); + } else { + warn_at( + "expected_a_b", + token.line, + token.thru, + ";", + artifact(next_token) + ); + } + anon = "anonymous"; +} + +function statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token.identifier && next_token.id === ":") { + the_label = token; + if (the_label.id === "ignore") { + warn("unexpected_a", the_label); + } + advance(":"); + if ( + next_token.id === "do" + || next_token.id === "for" + || next_token.id === "switch" + || next_token.id === "while" + ) { + enroll(the_label, "label", true); + the_label.init = true; + the_label.dead = false; + the_statement = statement(); + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token; + first.statement = true; + the_symbol = syntax[first.id]; + if (the_symbol !== undefined && the_symbol.fud !== undefined) { + the_symbol.disrupt = false; + the_symbol.statement = true; + the_statement = the_symbol.fud(); + } else { + +// It is an expression statement. + + the_statement = expression(0, true); + if (the_statement.wrapped && the_statement.id !== "(") { + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; +} + +function statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const array = []; + (function next(disrupt) { + if ( + next_token.id !== "}" + && next_token.id !== "case" + && next_token.id !== "default" + && next_token.id !== "else" + && next_token.id !== "(end)" + ) { + let a_statement = statement(); + array.push(a_statement); + if (disrupt) { + warn("unreachable_a", a_statement); + } + return next(a_statement.disrupt); + } + }(false)); + return array; +} + +function not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === global) { + warn("unexpected_at_top_level_a", thing); + } +} + +function top_level_only(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== global) { + warn("misplaced_a", the_thing); + } +} + +function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token; + the_block.arity = "statement"; + the_block.body = special === "body"; + +// Top level function bodies may include the "use strict" pragma. + + if ( + special === "body" + && stack.length === 1 + && next_token.value === "use strict" + ) { + next_token.statement = true; + advance("(string)"); + advance(";"); + } + stmts = statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option.devel && special !== "ignore") { + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; +} + +function mutation_check(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + warn("bad_assignment_a", the_thing); + return false; + } + return true; +} + +function left_check(left, right) { + +// Warn if the left is not one of these: +// e.b +// e[b] +// e() +// ?: +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !left_check(left.expression[1]) + && !left_check(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right); + return false; + } + return true; +} + +// These functions are used to specify the grammar of our language: + +function symbol(id, bp) { + +// Make a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax[id] = the_symbol; + } + return the_symbol; +} + +function assignment(id) { + +// Make an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led = function (left) { + const the_token = token; + let right; + the_token.arity = "assignment"; + right = expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "pre" + || right.arity === "post" + ) { + warn("unexpected_a", right); + } + mutation_check(left); + return the_token; + }; + return the_symbol; +} + +function constant(id, type, value) { + +// Make a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud = ( + typeof value === "function" + ? value + : function () { + token.constant = true; + if (value !== undefined) { + token.value = value; + } + return token; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; +} + +function infix(id, bp, f) { + +// Make an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, expression(bp)]; + return the_token; + }; + return the_symbol; +} + +function infixr(id, bp) { + +// Make a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + the_token.expression = [left, expression(bp - 1)]; + return the_token; + }; + return the_symbol; +} + +function post(id) { + +// Make one of the post operators. + + const the_symbol = symbol(id, 150); + the_symbol.led = function (left) { + token.expression = left; + token.arity = "post"; + mutation_check(token.expression); + return token; + }; + return the_symbol; +} + +function pre(id) { + +// Make one of the pre operators. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "pre"; + the_token.expression = expression(150); + mutation_check(the_token.expression); + return the_token; + }; + return the_symbol; +} + +function prefix(id, f) { + +// Make a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = expression(150); + return the_token; + }; + return the_symbol; +} + +function stmt(id, f) { + +// Make a statement. + + const the_symbol = symbol(id); + the_symbol.fud = function () { + token.arity = "statement"; + return f(); + }; + return the_symbol; +} + +function ternary(id1, id2) { + +// Make a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led = function (left) { + const the_token = token; + const second = expression(20); + advance(id2); + token.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, expression(10)]; + if (next_token.id !== ")") { + warn("use_open", the_token); + } + return the_token; + }; + return the_symbol; +} + +// Begin defining the language. + +syntax = empty(); + +symbol("}"); +symbol(")"); +symbol("]"); +symbol(","); +symbol(";"); +symbol(":"); +symbol("*/"); +symbol("await"); +symbol("case"); +symbol("catch"); +symbol("class"); +symbol("default"); +symbol("else"); +symbol("enum"); +symbol("finally"); +symbol("implements"); +symbol("interface"); +symbol("package"); +symbol("private"); +symbol("protected"); +symbol("public"); +symbol("static"); +symbol("super"); +symbol("void"); +symbol("yield"); + +constant("(number)", "number"); +constant("(regexp)", "regexp"); +constant("(string)", "string"); +constant("arguments", "object", function () { + warn("unexpected_a", token); + return token; +}); +constant("eval", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("false", "boolean", false); +constant("Function", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("ignore", "undefined", function () { + warn("unexpected_a", token); + return token; +}); +constant("Infinity", "number", Infinity); +constant("isFinite", "function", function () { + warn("expected_a_b", token, "Number.isFinite", "isFinite"); + return token; +}); +constant("isNaN", "function", function () { + warn("number_isNaN", token); + return token; +}); +constant("NaN", "number", NaN); +constant("null", "null", null); +constant("this", "object", function () { + if (!option.this) { + warn("unexpected_a", token); + } + return token; +}); +constant("true", "boolean", true); +constant("undefined", "undefined"); + +assignment("="); +assignment("+="); +assignment("-="); +assignment("*="); +assignment("/="); +assignment("%="); +assignment("&="); +assignment("|="); +assignment("^="); +assignment("<<="); +assignment(">>="); +assignment(">>>="); + +infix("??", 35); +infix("||", 40); +infix("&&", 50); +infix("|", 70); +infix("^", 80); +infix("&", 90); +infix("==", 100); +infix("===", 100); +infix("!=", 100); +infix("!==", 100); +infix("<", 110); +infix(">", 110); +infix("<=", 110); +infix(">=", 110); +infix("in", 110); +infix("instanceof", 110); +infix("<<", 120); +infix(">>", 120); +infix(">>>", 120); +infix("+", 130); +infix("-", 130); +infix("*", 140); +infix("/", 140); +infix("%", 140); +infixr("**", 150); +infix("(", 160, function (left) { + const the_paren = token; + let the_argument; + if (left.id !== "function") { + left_check(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (next_token.id !== ")") { + (function next() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + the_paren.free = true; + if (the_argument.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + the_paren.free = false; + } + return the_paren; +}); +infix(".", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("?.", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("[", 170, function (left) { + const the_token = token; + const the_subscript = expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + const name = survey(the_subscript); + if (rx_identifier.test(name)) { + warn("subscript_a", the_subscript, name); + } + } + left_check(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; +}); +infix("=>", 170, function (left) { + return stop("wrap_parameter", left); +}); + +function do_tick() { + const the_tick = token; + the_tick.value = []; + the_tick.expression = []; + if (next_token.id !== "`") { + (function part() { + advance("(string)"); + the_tick.value.push(token); + if (next_token.id === "${") { + advance("${"); + the_tick.expression.push(expression(0)); + advance("}"); + return part(); + } + }()); + } + advance("`"); + return the_tick; +} + +infix("`", 160, function (left) { + const the_tick = do_tick(); + left_check(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; +}); + +post("++"); +post("--"); +pre("++"); +pre("--"); + +prefix("+"); +prefix("-"); +prefix("~"); +prefix("!"); +prefix("!!"); +prefix("[", function () { + const the_token = token; + the_token.expression = []; + if (next_token.id !== "]") { + (function next() { + let element; + let ellipsis = false; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + element = expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]"); + return the_token; +}); +prefix("/=", function () { + stop("expected_a_b", token, "/\\=", "/="); +}); +prefix("=>", function () { + return stop("expected_a_before_b", token, "()", "=>"); +}); +prefix("new", function () { + const the_new = token; + const right = expression(160); + if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "()", artifact(next_token)); + } + the_new.expression = right; + return the_new; +}); +prefix("typeof"); +prefix("void", function () { + const the_void = token; + warn("unexpected_a", the_void); + the_void.expression = expression(0); + return the_void; +}); + +function parameter_list() { + const list = []; + let optional; + const signature = ["("]; + if (next_token.id !== ")" && next_token.id !== "(end)") { + (function parameter() { + let ellipsis = false; + let param; + if (next_token.id === "{") { + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("{"); + signature.push("{"); + (function subparameter() { + let subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (next_token.id === ":") { + advance(":"); + advance(); + token.label = subparam; + subparam = token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + } + if (next_token.id === "=") { + advance("="); + subparam.expression = expression(); + param.open = true; + } + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return subparameter(); + } + }()); + list.push(param); + advance("}"); + signature.push("}"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else if (next_token.id === "[") { + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("["); + signature.push("[]"); + (function subparameter() { + const subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + if (next_token.id === "=") { + advance("="); + subparam.expression = expression(); + param.open = true; + } + if (next_token.id === ",") { + advance(","); + return subparameter(); + } + }()); + list.push(param); + advance("]"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else { + if (next_token.id === "...") { + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + } + if (!next_token.identifier) { + return stop("expected_identifier_a"); + } + param = next_token; + list.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (next_token.id === "=") { + optional = param; + advance("="); + param.expression = expression(0); + } else { + if (optional !== undefined) { + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } + } + }()); + } + advance(")"); + signature.push(")"); + return [list, signature.join("")]; +} + +function do_function(the_function) { + let name; + if (the_function === undefined) { + the_function = token; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + name = next_token; + enroll(name, "variable", true); + the_function.name = name; + name.init = true; + name.calls = empty(); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + if (next_token.identifier) { + name = next_token; + the_function.name = name; + advance(); + } else { + the_function.name = anon; + } + } + } else { + name = the_function.name; + } + the_function.level = functionage.level + 1; + if (mega_mode) { + warn("unexpected_a", the_function); + } + +// Don't make functions in loops. It is inefficient, and it can lead to scoping +// errors. + + if (functionage.loop > 0) { + warn("function_in_loop", the_function); + } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + the_function.context = empty(); + the_function.finally = 0; + the_function.loop = 0; + the_function.switch = 0; + the_function.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functions.push(the_function); + functionage = the_function; + if (the_function.arity !== "statement" && typeof name === "object") { + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// Parse the parameter list. + + advance("("); + token.free = false; + token.arity = "function"; + [functionage.parameters, functionage.signature] = parameter_list(); + functionage.parameters.forEach(function enroll_parameter(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + name.names.forEach(enroll_parameter); + } + }); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && next_token.line === token.line + ) { + return stop("unexpected_a", next_token); + } + if ( + next_token.id === "." + || next_token.id === "?." + || next_token.id === "[" + ) { + warn("unexpected_a"); + } + +// Restore the previous context. + + functionage = stack.pop(); + return the_function; +} + +prefix("function", do_function); + +function fart(pl) { + advance("=>"); + const the_fart = token; + the_fart.arity = "binary"; + the_fart.name = "=>"; + the_fart.level = functionage.level + 1; + functions.push(the_fart); + if (functionage.loop > 0) { + warn("function_in_loop", the_fart); + } + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + the_fart.context = empty(); + the_fart.finally = 0; + the_fart.loop = 0; + the_fart.switch = 0; + the_fart.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functionage = the_fart; + the_fart.parameters = pl[0]; + the_fart.signature = pl[1]; + the_fart.parameters.forEach(function (name) { + enroll(name, "parameter", true); + }); + if (next_token.id === "{") { + warn("expected_a_b", the_fart, "function", "=>"); + the_fart.block = block("body"); + } else { + the_fart.expression = expression(0); + } + functionage = stack.pop(); + return the_fart; +} + +prefix("(", function () { + const the_paren = token; + let the_value; + const cadet = lookahead().id; + +// We can distinguish between a parameter list for => and a wrapped expression +// with one token of lookahead. + + if ( + next_token.id === ")" + || next_token.id === "..." + || (next_token.identifier && (cadet === "," || cadet === "=")) + ) { + the_paren.free = false; + return fart(parameter_list()); + } + the_paren.free = true; + the_value = expression(0); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + if (next_token.id === "=>") { + if (the_value.arity !== "variable") { + if (the_value.id === "{" || the_value.id === "[") { + warn("expected_a_before_b", the_paren, "function", "("); + return stop("expected_a_b", next_token, "{", "=>"); + } + return stop("expected_identifier_a", the_value); + } + the_paren.expression = [the_value]; + return fart([the_paren.expression, "(" + the_value.id + ")"]); + } + return the_value; +}); +prefix("`", do_tick); +prefix("{", function () { + const the_brace = token; + const seen = empty(); + the_brace.expression = []; + if (next_token.id !== "}") { + (function member() { + let extra; + let full; + let id; + let name = next_token; + let value; + advance(); + if ( + (name.id === "get" || name.id === "set") + && next_token.identifier + ) { + if (!option.getset) { + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + next_token.id; + name = next_token; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (next_token.id === "}" || next_token.id === ",") { + if (typeof extra === "string") { + advance("("); + } + value = expression(Infinity, true); + } else if (next_token.id === "(") { + value = do_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + advance("("); + } + let the_colon = next_token; + advance(":"); + value = expression(0); + if (value.id === name.id && value.id !== "function") { + warn("unexpected_a", the_colon, ": " + name.id); + } + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + advance(":"); + value = expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (next_token.id === ",") { + advance(","); + return member(); + } + }()); + } + advance("}"); + return the_brace; +}); + +stmt(";", function () { + warn("unexpected_a", token); + return token; +}); +stmt("{", function () { + warn("naked_block", token); + return block("naked"); +}); +stmt("break", function () { + const the_break = token; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (next_token.identifier && token.line === next_token.line) { + the_label = functionage.context[next_token.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + warn( + (the_label !== undefined && the_label.dead) + ? "out_of_scope_a" + : "not_label_a" + ); + } else { + the_label.used += 1; + } + the_break.label = next_token; + advance(); + } + advance(";"); + return the_break; +}); + +function do_var() { + const the_statement = token; + const is_const = the_statement.id === "const"; + the_statement.names = []; + +// A program may use var or let, but not both. + + if (!is_const) { + if (var_mode === undefined) { + var_mode = the_statement.id; + } else if (the_statement.id !== var_mode) { + warn( + "expected_a_b", + the_statement, + var_mode, + the_statement.id + ); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + warn("var_switch", the_statement); + } + if (functionage.loop > 0 && the_statement.id === "var") { + warn("var_loop", the_statement); + } + (function next() { + if (next_token.id === "{" && the_statement.id !== "var") { + const the_brace = next_token; + advance("{"); + (function pair() { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + survey(name); + advance(); + if (next_token.id === ":") { + advance(":"); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + next_token.label = name; + the_statement.names.push(next_token); + enroll(next_token, "variable", is_const); + advance(); + the_brace.open = true; + } else { + the_statement.names.push(name); + enroll(name, "variable", is_const); + } + name.dead = false; + name.init = true; + if (next_token.id === "=") { + advance("="); + name.expression = expression(); + the_brace.open = true; + } + if (next_token.id === ",") { + advance(","); + return pair(); + } + }()); + advance("}"); + advance("="); + the_statement.expression = expression(0); + } else if (next_token.id === "[" && the_statement.id !== "var") { + const the_bracket = next_token; + advance("["); + (function element() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + advance(); + the_statement.names.push(name); + enroll(name, "variable", is_const); + name.dead = false; + name.init = true; + if (ellipsis) { + name.ellipsis = true; + } else { + if (next_token.id === "=") { + advance("="); + name.expression = expression(); + the_bracket.open = true; + } + if (next_token.id === ",") { + advance(","); + return element(); + } + } + }()); + advance("]"); + advance("="); + the_statement.expression = expression(0); + } else if (next_token.identifier) { + const name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", is_const); + if (next_token.id === "=" || is_const) { + advance("="); + name.dead = false; + name.init = true; + name.expression = expression(0); + } + the_statement.names.push(name); + } else { + return stop("expected_identifier_a", next_token); + } + }()); + semicolon(); + return the_statement; +} + +stmt("const", do_var); +stmt("continue", function () { + const the_continue = token; + if (functionage.loop < 1 || functionage.finally > 0) { + warn("unexpected_a", the_continue); + } + not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; +}); +stmt("debugger", function () { + const the_debug = token; + if (!option.devel) { + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; +}); +stmt("delete", function () { + const the_token = token; + const the_value = expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; +}); +stmt("do", function () { + const the_do = token; + not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; +}); +stmt("export", function () { + const the_export = token; + let the_id; + let the_name; + let the_thing; + + function export_id() { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + the_id = next_token.id; + the_name = global.context[the_id]; + if (the_name === undefined) { + warn("unexpected_a"); + } else { + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a"); + } + exports[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + } + + the_export.expression = []; + if (next_token.id === "default") { + if (exports.default !== undefined) { + warn("duplicate_a"); + } + advance("default"); + the_thing = expression(0); + if ( + the_thing.id !== "(" + || the_thing.expression[0].id !== "." + || the_thing.expression[0].expression.id !== "Object" + || the_thing.expression[0].name.id !== "freeze" + ) { + warn("freeze_exports", the_thing); + } + if (next_token.id === ";") { + semicolon(); + } + exports.default = the_thing; + the_export.expression.push(the_thing); + } else { + if (next_token.id === "function") { + warn("freeze_exports"); + the_thing = statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a", the_name); + } + exports[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + warn("unexpected_a", next_token); + statement(); + } else if (next_token.id === "{") { + advance("{"); + (function loop() { + export_id(); + if (next_token.id === ",") { + advance(","); + return loop(); + } + }()); + advance("}"); + semicolon(); + } else { + stop("unexpected_a"); + } + } + module_mode = true; + return the_export; +}); +stmt("for", function () { + let first; + const the_for = token; + if (!option.for) { + warn("unexpected_a", the_for); + } + not_top_level(the_for); + functionage.loop += 1; + advance("("); + token.free = true; + if (next_token.id === ";") { + return stop("expected_a_b", the_for, "while (", "for (;"); + } + if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + return stop("unexpected_a"); + } + first = expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = expression(0); + advance(";"); + the_for.inc = expression(0); + if (the_for.inc.id === "++") { + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; +}); +stmt("function", do_function); +stmt("if", function () { + let the_else; + const the_if = token; + the_if.expression = condition(); + the_if.block = block(); + if (next_token.id === "else") { + advance("else"); + the_else = token; + the_if.else = ( + next_token.id === "if" + ? statement() + : block() + ); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + the_if.disrupt = true; + } else { + warn("unexpected_a", the_else); + } + } + } + return the_if; +}); +stmt("import", function () { + const the_import = token; + if (next_token.id === "(") { + the_import.arity = "unary"; + the_import.constant = true; + the_import.statement = false; + advance("("); + const string = expression(0); + if (string.id !== "(string)") { + warn("expected_string_a", string); + } + froms.push(token.value); + advance(")"); + advance("."); + advance("then"); + advance("("); + the_import.expression = expression(0); + advance(")"); + semicolon(); + return the_import; + } + let name; + if (typeof module_mode === "object") { + warn("unexpected_directive_a", module_mode, module_mode.directive); + } + module_mode = true; + if (next_token.identifier) { + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + const names = []; + advance("{"); + if (next_token.id !== "}") { + while (true) { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (next_token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token; + if (!rx_module.test(token.value)) { + warn("bad_module_name_a", token); + } + froms.push(token.value); + semicolon(); + return the_import; +}); +stmt("let", do_var); +stmt("return", function () { + const the_return = token; + not_top_level(the_return); + if (functionage.finally > 0) { + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (next_token.id !== ";" && the_return.line === next_token.line) { + the_return.expression = expression(10); + } + advance(";"); + return the_return; +}); +stmt("switch", function () { + let dups = []; + let last; + let stmts; + const the_cases = []; + let the_disrupt = true; + const the_switch = token; + not_top_level(the_switch); + if (functionage.finally > 0) { + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token.free = true; + the_switch.expression = expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + (function major() { + const the_case = next_token; + the_case.arity = "statement"; + the_case.expression = []; + (function minor() { + advance("case"); + token.switch = true; + const exp = expression(0); + if (dups.some(function (thing) { + return are_similar(thing, exp); + })) { + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (next_token.id === "case") { + return minor(); + } + }()); + stmts = statements(); + if (stmts.length < 1) { + warn("expected_statements_a"); + return; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "break;", + artifact(next_token) + ); + } + if (next_token.id === "case") { + return major(); + } + }()); + dups = undefined; + if (next_token.id === "default") { + const the_default = next_token; + advance("default"); + token.switch = true; + advance(":"); + the_switch.else = statements(); + if (the_switch.else.length < 1) { + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + const the_last = the_switch.else[the_switch.else.length - 1]; + if (the_last.id === "break" && the_last.label === undefined) { + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; +}); +stmt("throw", function () { + const the_throw = token; + the_throw.disrupt = true; + the_throw.expression = expression(10); + semicolon(); + if (functionage.try > 0) { + warn("unexpected_a", the_throw); + } + return the_throw; +}); +stmt("try", function () { + let the_catch; + let the_disrupt; + const the_try = token; + if (functionage.try > 0) { + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (next_token.id === "catch") { + let ignored = "ignore"; + the_catch = next_token; + the_try.catch = the_catch; + advance("catch"); + if (next_token.id === "(") { + advance("("); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + if (next_token.id !== "ignore") { + ignored = undefined; + the_catch.name = next_token; + enroll(next_token, "exception", true); + } + advance(); + advance(")"); + } + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "catch", + artifact(next_token) + ); + + } + if (next_token.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; +}); +stmt("var", do_var); +stmt("while", function () { + const the_while = token; + not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; +}); +stmt("with", function () { + stop("unexpected_a", token); +}); + +ternary("?", ":"); + +// Ambulation of the parse tree. + +function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; +} + +function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all of the +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + } + }; +} + +const posts = empty(); +const pres = empty(); +const preaction = action(pres); +const postaction = action(posts); +const preamble = amble(pres); +const postamble = amble(posts); + +function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.id === "function") { + walk_statement(thing.block); + } + if (thing.arity === "pre" || thing.arity === "post") { + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + warn("unexpected_statement_a", thing); + } + postamble(thing); + } + } +} + +function walk_statement(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_statement); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + && thing.id !== "import" + ) { + warn("unexpected_expression_a", thing); + } + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + } +} + +function lookup(thing) { + if (thing.arity === "variable") { + +// Look up the variable in the current context. + + let the_variable = functionage.context[thing.id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable === undefined) { + stack.forEach(function (outer) { + const a_variable = outer.context[thing.id]; + if ( + a_variable !== undefined + && a_variable.role !== "label" + ) { + the_variable = a_variable; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (the_variable === undefined) { + if (declared_globals[thing.id] === undefined) { + warn("undeclared_a", thing); + return; + } + the_variable = { + dead: false, + parent: global, + id: thing.id, + init: true, + role: "variable", + used: 0, + writable: false + }; + global.context[thing.id] = the_variable; + } + the_variable.closure = true; + functionage.context[thing.id] = the_variable; + } else if (the_variable.role === "label") { + warn("label_a", thing); + } + if ( + the_variable.dead + && ( + the_variable.calls === undefined + || functionage.name === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + ) { + warn("out_of_scope_a", thing); + } + return the_variable; + } +} + +function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); +} + +function preaction_function(thing) { + if (thing.arity === "statement" && blockage.body !== true) { + warn("unexpected_a", thing); + } + stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); +} + +function bitwise_check(thing) { + if (!option.bitwise && bitwiseop[thing.id] === true) { + warn("unexpected_a", thing); + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + warn("unexpected_a", thing); + } +} + +function pop_block() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); +} + +function activate(name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.init = true; + } + } + blockage.live.push(name); +} + +function action_var(thing) { + thing.names.forEach(activate); +} + +preaction("assignment", bitwise_check); +preaction("binary", bitwise_check); +preaction("binary", function (thing) { + if (relationop[thing.id] === true) { + const left = thing.expression[0]; + const right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + warn("expected_string_a", right); + } + } else { + const value = right.value; + if (value === "null" || value === "undefined") { + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + warn("expected_type_string_a", right, value); + } + } + } + } +}); +preaction("binary", "==", function (thing) { + warn("expected_a_b", thing, "===", "=="); +}); +preaction("binary", "!=", function (thing) { + warn("expected_a_b", thing, "!==", "!="); +}); +preaction("binary", "=>", preaction_function); +preaction("binary", "||", function (thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + warn("and", thang); + } + }); +}); +preaction("binary", "(", function (thing) { + const left = thing.expression[0]; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + const parent = functionage.name.parent; + if (parent) { + const left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + && left_variable.dead + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } +}); +preaction("binary", "in", function (thing) { + warn("infix_in", thing); +}); +preaction("binary", "instanceof", function (thing) { + warn("unexpected_a", thing); +}); +preaction("binary", ".", function (thing) { + if (thing.expression.new) { + thing.new = true; + } +}); +preaction("statement", "{", function (thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; +}); +preaction("statement", "for", function (thing) { + if (thing.name !== undefined) { + const the_variable = lookup(thing.name); + if (the_variable !== undefined) { + the_variable.init = true; + if (!the_variable.writable) { + warn("bad_assignment_a", thing.name); + } + } + } + walk_statement(thing.initial); +}); +preaction("statement", "function", preaction_function); +preaction("unary", "~", bitwise_check); +preaction("unary", "function", preaction_function); +preaction("variable", function (thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } +}); + +function init_variable(name) { + const the_variable = lookup(name); + if (the_variable !== undefined) { + if (the_variable.writable) { + the_variable.init = true; + return; + } + } + warn("bad_assignment_a", name); +} + +postaction("assignment", "+=", function (thing) { + let right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } +}); +postaction("assignment", function (thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + if (thing.id === "=") { + if (thing.names !== undefined) { + if (Array.isArray(thing.names)) { + thing.names.forEach(init_variable); + } else { + init_variable(thing.names); + } + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.writable !== true) { + warn("bad_assignment_a", lvalue); + } + } + const right = syntax[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + warn("unexpected_a", thing.expression[1]); + } + } +}); + +function postaction_function(thing) { + delete functionage.finally; + delete functionage.loop; + delete functionage.switch; + delete functionage.try; + functionage = stack.pop(); + if (thing.wrapped) { + warn("unexpected_parens", thing); + } + return pop_block(); +} + +postaction("binary", function (thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || are_similar(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option.convert) { + if (thing.expression[0].value === "") { + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === "." || thing.id === "?.") { + if (thing.expression.id === "RegExp") { + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } +}); +postaction("binary", "&&", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "||", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "=>", postaction_function); +postaction("binary", "(", function (thing) { + let left = thing.expression[0]; + let the_new; + let arg; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option.eval) { + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + warn( + "expected_a_before_b", + left, + "new", + artifact(left) + ); + } + } + } else if (left.id === ".") { + let cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + cack = !cack; + } + if (rx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + warn("unexpected_a", the_new); + } else { + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + const paren = left.expression; + if (paren.id === "(") { + const array = paren.expression; + if (array.length === 1) { + const new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } +}); +postaction("binary", "[", function (thing) { + if (thing.expression[0].id === "RegExp") { + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + warn("weird_expression_a", thing.expression[1]); + } +}); +postaction("statement", "{", pop_block); +postaction("statement", "const", action_var); +postaction("statement", "export", top_level_only); +postaction("statement", "for", function (thing) { + walk_statement(thing.inc); +}); +postaction("statement", "function", postaction_function); +postaction("statement", "import", function (the_thing) { + const name = the_thing.name; + if (name) { + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return top_level_only(the_thing); + } +}); +postaction("statement", "let", action_var); +postaction("statement", "try", function (thing) { + if (thing.catch !== undefined) { + const the_name = thing.catch.name; + if (the_name !== undefined) { + const the_variable = functionage.context[the_name.id]; + the_variable.dead = false; + the_variable.init = true; + } + walk_statement(thing.catch.block); + } +}); +postaction("statement", "var", action_var); +postaction("ternary", function (thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || are_similar(thing.expression[1], thing.expression[2]) + ) { + warn("unexpected_a", thing); + } else if (are_similar(thing.expression[0], thing.expression[1])) { + warn("expected_a_b", thing, "||", "?"); + } else if (are_similar(thing.expression[0], thing.expression[2])) { + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + warn("wrap_condition", thing.expression[0]); + } +}); +postaction("unary", function (thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option.convert) { + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } +}); +postaction("unary", "function", postaction_function); +postaction("unary", "+", function (thing) { + if (!option.convert) { + warn("expected_a_b", thing, "Number(...)", "+"); + } + const right = thing.expression; + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } +}); + +function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + if (id !== "ignore") { + const name = the_function.context[id]; + if (name.parent === the_function) { + if ( + name.used === 0 + && ( + name.role !== "function" + || name.parent.arity !== "unary" + ) + ) { + warn("unused_a", name); + } else if (!name.init) { + warn("uninitialized_a", name); + } + } + } + }); +} + +function uninitialized_and_unused() { + +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (module_mode === true || option.node) { + delve(global); + } + functions.forEach(delve); +} + +// Go through the token list, looking at usage of whitespace. + +function whitage() { + let closer = "(end)"; + let free = false; + let left = global; + let margin = 0; + let nr_comments_skipped = 0; + let open = true; + let opening = true; + let right; + + function pop() { + const previous = stack.pop(); + closer = previous.closer; + free = previous.free; + margin = previous.margin; + open = previous.open; + opening = previous.opening; + } + + function push() { + stack.push({ + closer, + free, + margin, + open, + opening + }); + } + + function expected_at(at) { + warn( + "expected_a_at_b_c", + right, + artifact(right), + fudge + at, + artifact_column(right) + ); + } + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function no_space() { + if (left.line === right.line) { + if (left.thru !== right.from && nr_comments_skipped === 0) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (open) { + const at = ( + free + ? margin + : margin + 8 + ); + if (right.from < at) { + expected_at(at); + } + } else { + if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line || !open) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (right.from !== margin) { + expected_at(margin); + } + } + } + + stack = []; + tokens.forEach(function (the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + + const new_closer = opener[left.id]; + if (typeof new_closer === "string") { + if (new_closer !== right.id) { + opening = left.open || (left.line !== right.line); + push(); + closer = new_closer; + if (opening) { + free = closer === ")" && left.free; + open = true; + margin += 4; + if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (right.switch) { + at_margin(-4); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + free = false; + open = false; + no_space_only(); + } + } else { + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like '{]') have already been detected. + + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } + } else { + if (right.statement === true) { + if (left.id === "else") { + one_space_only(); + } else { + at_margin(0); + open = false; + } + +// If right is a closer, then pop the previous state. + + } else if (right.id === closer) { + pop(); + if (opening && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-4); + } else if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + one_space(); + } else { + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + at_margin(0); + } else { + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + no_space(); + } else if ( + left.id === "." + || left.id === "?." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + ) { + no_space_only(); + } else if (right.id === "." || right.id === "?.") { + no_space_only(); + } else if (left.id === ";") { + if (open) { + at_margin(0); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + one_space_only(); + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +// The jslint function itself. + +export default Object.freeze(function jslint( + source = "", + option_object = empty(), + global_array = [] +) { + // hack-jslint - init lines_extra + lines = ( + Array.isArray(source) + ? source + : source.split(rx_crlf) + ); + lines_extra = lines.map(function () { + return {}; + }); + try { + warnings = []; + option = Object.assign(empty(), option_object); + anon = "anonymous"; + block_stack = []; + declared_globals = empty(); + directive_mode = true; + directives = []; + early_stop = true; + exports = empty(); + froms = []; + fudge = ( + option.fudge + ? 1 + : 0 + ); + functions = []; + global = { + id: "(global)", + body: true, + context: empty(), + from: 0, + level: 0, + line: 0, + live: [], + loop: 0, + switch: 0, + thru: 0 + }; + blockage = global; + functionage = global; + json_mode = false; + mega_mode = false; + module_mode = false; + next_token = global; + property = empty(); + shebang = false; + stack = []; + tenure = undefined; + token = global; + token_nr = 0; + var_mode = undefined; + populate(standard, declared_globals, false); + populate(global_array, declared_globals, false); + Object.keys(option).forEach(function (name) { + if (option[name] === true) { + const allowed = allowed_option[name]; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } + }); + tokenize(source); + advance(); + if (json_mode) { + tree = json_value(); + advance("(end)"); + } else { + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option.browser) { + if (next_token.id === ";") { + advance(";"); + } + } else { + +// If we are not in a browser, then the file form of strict pragma may be used. + + if ( + next_token.value === "use strict" + ) { + advance("(string)"); + advance(";"); + } + } + tree = statements(); + advance("(end)"); + functionage = global; + walk_statement(tree); + if (warnings.length === 0) { + uninitialized_and_unused(); + if (!option.white) { + whitage(); + } + } + } + if (!option.browser) { + directives.forEach(function (comment) { + if (comment.directive === "global") { + warn("missing_browser", comment); + } + }); + } + early_stop = false; + } catch (e) { + if (e.name !== "JSLintError") { + warnings.push(e); + } + } + return { + directives, + edition: "2020-11-06", + exports, + froms, + functions, + global, + id: "(JSLint)", + json: json_mode, + lines, + module: module_mode === true, + ok: warnings.length === 0 && !early_stop, + option, + property, + shebang: ( + shebang + ? lines[0] + : undefined + ), + stop: early_stop, + tokens, + tree, + warnings: warnings.sort(function (a, b) { + return a.line - b.line || a.column - b.column; + }) + }; +}); diff --git a/branch.ignore_macro/report.js b/branch.ignore_macro/report.js new file mode 100644 index 000000000..5e487d143 --- /dev/null +++ b/branch.ignore_macro/report.js @@ -0,0 +1,222 @@ +// report.js +// 2018-10-22 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Generate JSLint HTML reports. + +/*property + closure, column, context, edition, error, exports, filter, forEach, freeze, + froms, fudge, function, functions, global, id, isArray, join, json, keys, + length, level, line, lines, message, module, name, names, option, + parameters, parent, property, push, replace, role, signature, sort, stop, + warnings +*/ + +const rx_amp = /&/g; +const rx_gt = />/g; +const rx_lt = / with less destructive entities. + + return String( + string + ).replace( + rx_amp, + "&" + ).replace( + rx_lt, + "<" + ).replace( + rx_gt, + ">" + ); +} + +export default Object.freeze({ + error: function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + let fudge = Number(Boolean(data.option.fudge)); + let output = []; + if (data.stop) { + output.push("
    JSLint was unable to finish.
    "); + } + data.warnings.forEach(function (warning) { + output.push( + "
    ", + entityify(warning.line + fudge), + ".", + entityify(warning.column + fudge), + "
    ", + entityify(warning.message), + "
    ", + entityify(data.lines[warning.line] || ""), + "" + ); + }); + return output.join(""); + }, + + function: function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + let fudge = Number(Boolean(data.option.fudge)); + let mode = ( + data.module + ? "module" + : "global" + ); + let output = []; + + if (data.json) { + return ( + data.warnings.length === 0 + ? "
    JSON: good.
    " + : "
    JSON: bad.
    " + ); + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + let global = Object.keys(data.global.context).sort(); + let froms = data.froms.sort(); + let exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + let context = the_function.context; + let list = Object.keys(context); + output.push( + "
    ", + entityify(the_function.line + fudge), + "
    ", + ( + the_function.name === "=>" + ? entityify(the_function.signature) + " =>" + : ( + typeof the_function.name === "string" + ? "«" + entityify(the_function.name) + "»" + : "" + entityify(the_function.name.id) + "" + ) + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + let params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.parent === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.parent === the_function + ); + })); + detail("outer", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.parent !== the_function + && the_variable.parent.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].parent.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + output.push( + "
    JSLint edition ", + entityify(data.edition), + "
    " + ); + return output.join(""); + }, + + property: function property_directive(data) { + +// Produce the /*property*/ directive. + + let not_first = false; + let output = ["/*property"]; + let length = 1111; + let properties = Object.keys(data.property); + + if (properties.length > 0) { + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length >= 80) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); + } + } +}); diff --git a/branch.ignore_shebang/README b/branch.ignore_shebang/README new file mode 100644 index 000000000..f46e91069 --- /dev/null +++ b/branch.ignore_shebang/README @@ -0,0 +1,42 @@ +JSLint, The JavaScript Code Quality Tool + +Douglas Crockford +douglas@crockford.com + +2018-05-28 + +jslint.js contains the jslint function. It parses and analyzes a source file, +returning an object with information about the file. It can also take an object +that sets options. + +index.html runs the jslint.js function in a web page. The page also depends +browser.js and report.js and jslint.css. + +jslint.css provides styling for index.html. + +browser.js runs the web user interface. + +report.js generates the results reports in HTML. + +help.html describes JSLint's usage. Please read it. + +function.html describes the jslint function and the results it produces. + +Please direct questions and comments to +https://plus.google.com/communities/104441363299760713736. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[http://www.crockford.com/wrrrld/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. diff --git a/branch.ignore_shebang/browser.js b/branch.ignore_shebang/browser.js new file mode 100644 index 000000000..5086fed85 --- /dev/null +++ b/branch.ignore_shebang/browser.js @@ -0,0 +1,158 @@ +// browser.js +// 2018-06-16 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +/*jslint + browser +*/ + +/*property + checked, create, disable, display, error, focus, forEach, function, + getElementById, innerHTML, join, length, map, onchange, onclick, onscroll, + property, querySelectorAll, scrollTop, select, split, style, title, value +*/ + +import jslint from "./jslint.js"; +import report from "./report.js"; + +// This is the web script companion file for JSLint. It includes code for +// interacting with the browser and displaying the reports. + +let rx_crlf = /\n|\r\n?/; +let rx_separator = /[\s,;'"]+/; + +let fudge_unit = 0; + +const aux = document.getElementById("JSLINT_AUX"); +const boxes = document.querySelectorAll("[type=checkbox]"); +const fudge = document.getElementById("JSLINT_FUDGE"); +const global = document.getElementById("JSLINT_GLOBAL"); +const number = document.getElementById("JSLINT_NUMBER"); +const property = document.getElementById("JSLINT_PROPERTY"); +const property_fieldset = document.getElementById("JSLINT_PROPERTYFIELDSET"); +const report_field = document.getElementById("JSLINT_REPORT"); +const report_list = document.getElementById("JSLINT_REPORT_LIST"); +const source = document.getElementById("JSLINT_SOURCE"); +const select = document.getElementById("JSLINT_SELECT"); +const warnings = document.getElementById("JSLINT_WARNINGS"); +const warnings_list = document.getElementById("JSLINT_WARNINGS_LIST"); + +function show_numbers() { + number.value = source.value.split(rx_crlf).map(function (ignore, index) { + return index + fudge_unit; + }).join("\n"); +} + +function clear() { + aux.style.display = "none"; + number.value = ""; + property.value = ""; + property_fieldset.style.display = "none"; + report_field.style.display = "none"; + report_list.innerHTML = ""; + source.focus(); + source.value = ""; + warnings.style.display = "none"; + warnings_list.innerHTML = ""; +} + +function fudge_change() { + fudge_unit = Number(fudge.checked); + show_numbers(); +} + +function clear_options() { + boxes.forEach(function (node) { + node.checked = false; + }); + fudge_change(); + global.innerHTML = ""; +} + +function call_jslint() { + +// First build the option object. + + let option = Object.create(null); + boxes.forEach(function (node) { + if (node.checked) { + option[node.title] = true; + } + }); + +// Call JSLint with the source text, the options, and the predefined globals. + + let global_string = global.value; + let result = jslint( + source.value, + option, + ( + global_string === "" + ? undefined + : global_string.split(rx_separator) + ) + ); + +// Generate the reports. + + let error_html = report.error(result); + let function_html = report.function(result); + let property_text = report.property(result); + +// Display the reports. + + warnings_list.innerHTML = error_html; + warnings.style.display = ( + error_html.length === 0 + ? "none" + : "block" + ); + + report_list.innerHTML = function_html; + report_field.style.display = "block"; + if (property_text) { + property.value = property_text; + property_fieldset.style.display = "block"; + property.scrollTop = 0; + select.disable = false; + } else { + property_fieldset.style.display = "none"; + select.disable = true; + } + aux.style.display = "block"; + source.select(); +} + +fudge.onchange = fudge_change; + +source.onchange = function (ignore) { + show_numbers(); +}; + +source.onscroll = function () { + let ss = source.scrollTop; + number.scrollTop = ss; + let sn = number.scrollTop; + if (ss > sn) { + show_numbers(); + number.scrollTop = ss; + } +}; + +document.querySelectorAll("[name='JSLint']").forEach(function (node) { + node.onclick = call_jslint; +}); + +document.querySelectorAll("[name='clear']").forEach(function (node) { + node.onclick = clear; +}); + +document.getElementById("JSLINT_SELECT").onclick = function () { + property.focus(); + property.select(); +}; +document.getElementById("JSLINT_CLEAR_OPTIONS").onclick = clear_options; + +fudge_change(); +source.select(); +source.focus(); diff --git a/branch.ignore_shebang/function.html b/branch.ignore_shebang/function.html new file mode 100644 index 000000000..8d7287cbc --- /dev/null +++ b/branch.ignore_shebang/function.html @@ -0,0 +1,612 @@ + + + + + + + + +JSLint: jslint function + + + +
    JSLint
    + +
    The jslint Function
    +
    +

    JSLint

    +

    JSLint is delivered as a set of files:

    +
    + + + + + + + + + + + + + + +
    FilenameContent
    browser.jsBrowser based UI.
    function.htmlThis page that you are looking at right now.
    help.htmlUsing jslint as a single page JSLint application.
    index.htmlSingle page JSLint application that uses the + js files.
    jslint.jsThe jslint function.
    report.jsThe report object, containing report generator functions for + HTML.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    +
    + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring"(JSLint)"
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    parenttokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    parenttokenThe function in which the variable was declared.variable
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable can be assigned to.variable
    +

    Reports

    +

    The report object contains three functions that all take a + result from the jslint function as input. + report.js has no other dependence on other files.

    + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    +
    + +
    + + + + + +
    + + diff --git a/branch.ignore_shebang/help.html b/branch.ignore_shebang/help.html new file mode 100644 index 000000000..ee236ff95 --- /dev/null +++ b/branch.ignore_shebang/help.html @@ -0,0 +1,841 @@ + + + + + + + + + +JSLint: Help + + + +
    JSLint
    + +
    Help
    +
    +
    Il semble que la perfection soit atteinte non quand il n’y a + plus rien à ajouter, mais quand il n’y a plus rien à + retrancher.
    +
    + Antoine de Saint-Exupéry +
    +
    + Terre des Hommes (1939) +
    +

    Good Parts

    +

    The Principle of the Good Parts is

    +
    If a feature is sometimes useful and sometimes dangerous and if + there is a better option then always use the better option.
    +

    JSLint is a JavaScript program that looks for problems in + JavaScript programs. It is a code quality tool.

    +

    When C was + a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    +

    As the language matured, the definition of the language was + strengthened to eliminate some insecurities, and compilers got better + at issuing warnings. lint is no longer needed.

    +

    JavaScript is a + young-for-its-age language. It was originally intended to do small tasks in + webpages, tasks for which Java was too heavy and clumsy. But JavaScript is + a surprisingly capable language, and it is now being used in larger + projects. Many of the features that were intended to make the language easy + to use are troublesome when projects become complicated. A lint + for JavaScript is needed: JSLint, a JavaScript syntax checker + and validator.

    +

    JSLint takes a JavaScript source and scans it. If it finds a + problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by the ECMAScript Programming Language + Standard (the strangely named document that governs JavaScript). + JSLint will reject most legal programs. It is a higher + standard.

    +

    JavaScript is a sloppy language, but hidden deep inside there is an elegant, + better language. JSLint helps you to program in that better + language and to avoid most of the slop. JSLint will reject + programs that browsers will accept because JSLint is concerned + with the quality of your code and browsers are not. You should gladly accept + all of JSLint's advice.

    +

    JSLint can operate on JavaScript source + or JSON text.

    +

    ECMAScript Sixth Edition

    +

    Some of + ES6’s features are good, so JSLint will recognize the good + parts of ES6.

    +

    Currently, these features are recognized:

    +
      +
    • The ... ellipsis marker in parameter + lists and argument lists, replacing the arguments object + for variadic functions.
    • +
    • The let statement, which is like the var + statement except that it respects block scope. + You may use let or var but not both.
    • +
    • The const statement is like the let statement + except that it disallows the use of assignment on the variable, although + if the value of the variable is mutable, it can still be mutated. + const is preferred to let. +
    • Destructuring of arrays and objects is allowed in parameter lists and on + the left side of let, and const, but not + var or assignment statements, and not deep destructuring + or eliding.
    • +
    • Enhanced object literals, providing shorter forms for function + declaration and properties that are initialized by variables with the + same name.
    • +
    • The fat arrow => fart functions.
    • +
    • The simplest forms of import and export.
    • +
    • `Megastring` literals, but not nested + `megastring` literals.
    • +
    • New global functions, such as Map, Set, + WeakMap, and WeakSet.
    • +
    • 0b- and 0o- number literals.
    • +
    +

    The most important new feature of ES6 is proper tail calls. This has no + new syntax, so JSLint doesn’t see it. But it makes recursion + much more attractive, which makes loops, particularly for + loops, much less attractive.

    +

    import export

    +

    The ES6 module feature will be an important improvement over JavaScript’s + global variables as a means of linking separate files together. + JSLint recognizes a small but essential subset of the module + syntax.

    +
    +

    import name from stringliteral;

    +

    import {name} from stringliteral;

    +

    export default function () {};

    +

    export default function name() {};

    +

    export default expression;

    +

    export function name() {}

    +

    export name; // where name is const

    +

    export {name};

    +
    +

    Directives

    +

    JSLint provides three directives that may be placed in a + file to manage JSLint’s behavior. Use of these directives is + optional. If they are used, they should be placed in a source file before + the first statement. They are written in the form of a comment, where the + directive name is placed immediately after the opening of the comment before + any whitespace. The three directives are global, + jslint, and property. Directives in a file are + stronger than options selected from the UI or passed with the option object.

    +

    /*global*/

    +

    The /*global*/ directive is used to specify a set of globals + (usually functions and objects containing functions) that are available to + this file. This was commonly used in browsers to link source files together + before ES6 modules appeared. Use of global variables is strongly + discouraged, but unfortunately web browsers require their use. The + /*global*/ directive can only be used when the + Assume a browser option is selected. +

    Each of the names listed will indicate a read-only global variable. The names + are separated by , comma. This directive + should not be used if import or export is used. + This directive inhibits warnings, but it does not declare the names in the + execution environment. +

    For example: +

    /*global +
    ADSAFE, report, jslint
    +*/
    +

    instructs JSLint to not give warnings about the global + variables ADsafe, report, and jslint. + However, if any of those names are expected to be supplied by other files + and those other files fail to do so, then execution errors will result. It + is usually better to use top-level variable declarations instead: +

        var ADSAFE;
    +    var report;
    +    var jslint; 
    +

    Using var in this way allows comparing a global variable to the + undefined value to determine whether it is has been used in the global context.

    +

    /*jslint*/

    +

    The /*jslint*/ directive allows for the control of several + options. These options can also be set from the + JSLint.com user interface.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Tolerate bitwise operatorsbitwisetrue if bitwise operators should be allowed. The + bitwise operators are rarely used in JavaScript programs, so it + is usually more likely that & is a mistyping of + && than that it indicates a bitwise and. + JSLint will give warnings on the bitwise operators + unless this option is selected.
    Assume a browser browsertrue if the standard browser globals should be + predefined. This option will reject the use of + import and export. This option also + disallows the file form of the "use + strict" pragma. It does not supply + self; you will have to request that unnecessary + alias of the dreaded global object yourself. It adds the same + globals as this directive: +
    /*global +
    + clearInterval, clearTimeout, document, event, FileReader, + FormData, history, localStorage, location, name, navigator, + screen, sessionStorage, setInterval, setTimeout, Storage, + URL, window, XMLHttpRequest +
    + */
    +
    Tolerate conversion operatorsconverttrue if !!, + prefix, + and concatenation with "" are allowed. + The Boolean, Number, and + String functions are preferred
    Assume CouchDBcouchtrue if + Couch DB + globals should be predefined. It adds the same globals as this + directive: +
    /*global +
    emit, getRow, isArray, log, provides, registerType, require, + send, start, sum, toJSON
    + */
    Assume in developmentdeveltrue if browser globals that are useful in + development should be predefined, and if debugger + statements and TODO comments + should be allowed. It adds the + same globals as this directive: +
    /*global +
    alert, confirm, console, prompt
    + */
    Be sure to turn this option off before going + into production.
    Tolerate evalevaltrue if eval should be allowed. In the + past, the eval function was the most misused + feature of the language.
    Tolerate for statementfortrue if the for statement should be + allowed. It is almost always better to use the array methods + instead.
    Fudge the line and column numbersfudgetrue if the origin of a text file is line 1 column + 1. Programmers know that number systems start at zero. + Unfortunately, most text editors start numbering lines and + columns at one. JSLint will by default start + numbering at zero. Use this option to start the numbering at one + in its reports.
    Tolerate get and setgetsettrue if accessor properties are allowed in object literals.
    Tolerate long source lineslongtrue if a line can contain more than 80 characters.
    Tolerate multiple variables declarations per statementmultivartrue if a var, let, or + const statement can declare two or more variables + in a single statement.
    Assume Node.jsnodetrue if Node.js globals should be predefined. It + will predefine globals that are used in the Node.js environment. + It adds the same globals as this directive: +
    /*global +
    + Buffer, clearImmediate, clearInterval, clearTimeout, console, exports, + module, process, require, setImmediate, setInterval, setTimeout, URL, + URLSearchParams, __dirname, __filename +
    + */
    Tolerate single quote stringssingletrue if 'single quote + should be allowed to enclose string literals.
    Tolerate this
    thistrue if this should be allowed.
    Tolerate whitespace messwhitetrue if the whitespace rules should be ignored.
    +

    For example:

    +
    /*jslint
    +    bitwise, node
    +*/
    + +

    /*property*/

    +

    The /*property*/ directive is used to declare a list of + property identifiers that are used in the file. Each property name in the + program is looked up in this list. If a name is not found, that indicates an + error, most likely a typing error.

    +

    The list can also be used to evade some of JSLint’s naming + rules.

    +

    JSLint can build the /*property*/ list for you. At + the bottom of its report, JSLint displays a + /*property*/ directive. It contains all of the names that were + used with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. You + can copy the /*property*/ directive to the top of your + script file. JSLint will check the spelling of all property + names against the list. That way, you can have JSLint look for + misspellings for you.

    +

    For example,

    +
    /*property +
    charAt, slice, _$_
    + */
    +

    ignore

    +

    JSLint introduces a new reserved word: ignore. It + is used in parameter lists and in catch clauses to indicate a + parameter that will be ignored. Unused warnings will not be produced for + ignore. +

    function handler(ignore, value) {
    +    return do_something_useful(value);
    +}
    +

    var let const

    +

    JavaScript provides three statements for declaring variables: + var, let, and const. The + var statement suffers from a bad practice called hoisting. The + let statement does not do hoisting and respects block scope. + The const statement is like let except that it + marks the variable (but not its contents) as read only, making it an error + to attempt to assign to the variable. When given a choice, + const is the best, var is the worst.

    +

    JSLint uses the intersection of the var rules and the + let rules, and by doing so avoids the errors related to either. + A name should be declared only once in a function. It should be declared + before it is used. It should not be used outside of the block in which it is + declared. A variable should not have the same name as a variable or + parameter in an outer function. Do not mix var and + let. Declare one name per statement.

    +

    = == ===

    +

    Fortran made a terrible + mistake in using the equality operator as its assignment operator. That + mistake has been replicated in most languages since then. C compounded that + mistake by making == its equality operator. The visual + similarity is a source of errors. JavaScript compounded this further by + making == a type coercing comparison operator that produces + false positive results. This was mitigated by adding the === + operator, leaving the broken == operator in place.

    +

    JSLint attempts to minimize errors by the following rules:

    +

    == is not allowed. This avoids the false positives, and + increases the visual distance between = and ===. + Assignments are not allowed in expression position, and comparisons are not + allowed in statement position. This also reduces confusion. 

    +

    Semicolon

    +

    JavaScript uses a C-like syntax that requires the use of semicolons to + delimit certain statements. JavaScript attempts to make those semicolons + optional with an automatic semicolon insertion mechanism, but it does not + work very well. Automatic semicolon insertion was added to make + things easier for beginners. Unfortunately, it sometimes fails. Do not rely + on it unless you are a beginner.

    +

    JSLint expects that every statement will be followed by + ; except for for, function, + if, switch, try, and + while. JSLint does not expect to see unnecessary + semicolons, the empty statement, or empty blocks.

    +

    function =>

    +

    JavaScript has four syntactic forms for making function objects: function + statements, function expressions, enhanced object literals, and the + => fart operator.

    +

    The function statement creates a variable and assigns the function object to + it. It should be used in a file or function body, but not inside of a block.

    +
    function name(parameters) { +
    statements
    + }
    +

    The function expression unfortunately looks like the function statement. It + may appear anywhere that an expression may appear, but not in statement + position and not in a loop. It produces a function object but does not + create a variable in which to store it.

    +
    function (parameters) { +
    statements
    + }
    +

    The enhanced object literal provides an ES6 shorthand for creating a property + whose value is a function, saving you from having to type + : colon and function.

    +
    { +
    name(parameters) { +
    statements
    + }
    + }
    +

    Finally, ES6 provides an even shorter form of function expression that leaves + out the words function and return:

    +
    (parameters) => + expression
    +

    JSLint requires the parens around the parameters, and forbids a + { left brace after the + => fart to avoid syntactic ambiguity.

    +

    Comma

    +

    The , comma operator is unnecessary and can + mask programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as + an operator. It does not expect to see elided elements in array literals. A + comma should not appear after the last element of an array literal or object + literal.

    +

    Blocks

    +

    JSLint expects blocks with function, + if, switch, while, for, + do, and try statements and nowhere else.

    +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    +

    JavaScript allows an if to be written like this:

    +
    if (condition)
    +    statement;
    +

    That form is known to contribute to mistakes in projects where many + programmers are working on the same code. That is why JSLint + expects the use of a block:

    +
    if (condition) {
    +    statements;
    +}
    +

    Experience shows that this form is more resilient.

    +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call. All other expression statements are considered + to be errors.

    +

    for

    +

    JSLint does not recommend use of the for statement. + Use array methods like forEach instead. The for + option will suppress some warnings. The forms of for that + JSLint accepts are restricted, excluding the new ES6 forms.

    +

    for in

    +

    JSLint does not recommend use of the for + in statement. Use Object.keys instead.

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, it also + loops through all of the properties that were inherited through the + prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written + without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    for (name in object) {
    +    if (object.hasOwnProperty(name)) {
    +        ....
    +    }
    +}
    +

    Note that the above code will fail if the object contains a property + named hasOwnProperty. Use Object.keys instead.

    +

    switch

    +

    A common error in switch statements is to forget to place a + break statement after each case, resulting in unintended + fall-through. JSLint expects that the statement before the next + case or default is one of these: + break, return, or throw.

    +

    with

    +

    The with statement was intended to provide a shorthand in + accessing properties in deeply nested objects. Unfortunately, it behaves + very badly when setting new properties. Never use the with + statement.

    +

    JSLint does not expect to see a with statement.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that + labels will be distinct from vars and parameters.

    +

    Unreachable Code

    +

    JSLint expects that a return, break, + or throw statement will be followed by a + } right brace or case or + default.

    +

    Confusing Pluses and Minuses

    +

    JSLint expects that + will not be followed by + + or ++, and that - will not be + followed by - or --. A misplaced space can turn + + + into ++, an error that is difficult to see. + Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ increment and + -- decrement operators have been known to + contribute to bad code by encouraging excessive trickiness. They are second + only to faulty architecture in enabling to viruses and other security + menaces. Also, preincrement/postincrement confusion can produce off-by-one + errors that are extremely difficult to diagnose. Fortunately, they are also + complete unnecessary. There are better ways to add 1 to a variable.

    +

    It is best to avoid these operators entirely and rely on += and + -= instead.

    +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. + JSLint looks for problems that may cause portability problems. + It also attempts to resolve visual ambiguities by recommending explicit + escapement.

    +

    JavaScript’s syntax for regular expression literals overloads the + / slash character. To avoid ambiguity, + JSLint expects that the character preceding a regular + expression literal is a ( left paren or + = equal or + : colon or + , comma character.

    +

    this

    +
    Having this in the language makes it harder to talk + about the language. It is like pair programming with Abbott and Costello. +
    +

    Avoid using this. Warnings about this can be + suppressed with option.this.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the + new prefix. The new prefix creates a new object + based on the function's prototype, and binds that object to the + function's implied this parameter. If you neglect to use the + new prefix, no new object will be made and this + will be bound to the global object.

    +

    JSLint enforces the convention that constructor functions be + given names with initial uppercase. JSLint does not expect to + see a function invocation with an initial uppercase name unless it has the + new prefix. JSLint does not expect to see the + new prefix used with functions whose names do not start with + initial uppercase.

    +

    JSLint does not expect to see the wrapper forms + new Number, new String, new Boolean.

    +

    JSLint does not expect to see new Object. + Use Object.create(null) instead.

    +

    Whitespace

    +

    JSLint has a specific set of rules about the use of whitespace. + Where possible, these rules are consistent with centuries of good practice + with literary style.

    +

    The indentation increases by 4 spaces when the last token on a line is + { left brace, + [ left bracket, + ( left paren. The matching closing + token will be the first token on a line, restoring the previous + indentation.

    +

    The ternary operator can be visually confusing, so + ? question mark and + : colon always begin a line and increase + the indentation by 4 spaces.

    +
    return (the_token.id === "(string)" || the_token.id === "(number)")
    +    ? String(the_token.value)
    +    : the_token.id;
    +

    If . period is the first character on a line, the + indentation is increased by 4 spaces.

    +

    A var, let, or const statement will + also cause an indentation if it declares two or more variables, and if the + second variable is not on the same line as the start of the statement.

    +

    Long lines can be continued on the next line with 8 spaces added to the + current indentation. Those 8 spaces do not change the current indentation. + It is 8 to avoid confusion with ordinary indentation.

    +

    The word function is always followed with one space.

    +

    Clauses (case, catch, default, + else, finally) are not statements and so should + not be indented like statements.

    +

    Spaces are used to make things that are not invocations look less like + invocations.

    +

    Tabs and spaces should not be mixed. We should pick just one in order to + avoid the problems that come from having both. Personal preference is an + extremely unreliable criterion. Neither offers a powerful advantage over the + other. Fifty years ago, tab had the advantage of consuming less memory, but + Moore's Law has eliminated that advantage. Space has one clear advantage + over tab: there is no reliable standard for how many spaces a tab + represents, but it is universally accepted that a space occupies a space. So + use spaces. You can edit with tabs if you must, but make sure it is spaces + again before you commit. Maybe someday we will finally get a universal + standard for tabs, but until that day comes, the better choice is spaces.

    +

    Unsafe Characters

    +

    There are characters that are handled inconsistently in some browsers, and + so must be escaped when placed in strings.

    +
    \u0000-\u001f
    +\u007f-\u009f
    +\u00ad
    +\u0600-\u0604
    +\u070f
    +\u17b4
    +\u17b5
    +\u200c-\u200f
    +\u2028-\u202f
    +\u2060-\u206f
    +\ufeff
    +\ufff0-\uffff
    +

    Report

    +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    +
      +
    • The line number on which it starts.
    • +
    • The function’s name. In the case of anonymous functions, + JSLint will attempt to + «guess» the name.
    • +
    • The parameters.
    • +
    • Variables: The variables that are declared in the function.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Exceptions: The variables that are declared by catch + clauses of try statements.
    • +
    • Outer: Variables used by this function that are declared in + other functions.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements. Updates are announced at + https://plus.google.com/communities/104441363299760713736.

    +

    Try it

    +

    Try it. Paste your script + into the window and click the + + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    JSLint is written entirely in JavaScript, so it can run + anywhere that JavaScript (or even Java) can run.

    +

    Perfectly fine

    +

    JSLint was designed to reject code that some would consider to + be perfectly fine. The reason for this is that JSLint's + purpose is to help produce programs that are free of error. That is + difficult in any language and is especially hard in JavaScript. + JSLint attempts to help you increase the visual distance + between correct programs and incorrect programs, making the remaining errors + more obvious. JSLint will give warnings about things that are + not necessarily wrong in the current situation, but which have been observed + to mask or obscure errors. Avoid those things when there are better options + available.

    +

    It is dangerous out there. JSLint is here to help.

    +

    Warning

    +

    JSLint will hurt your feelings. Side effects may include + headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, + dry mouth, cleaner code, and a reduced error rate.

    +

    Further Reading

    + +
    +

    + Code Conventions for the JavaScript Programming Language.

    + + + + + +
    + + diff --git a/branch.ignore_shebang/index.html b/branch.ignore_shebang/index.html new file mode 100644 index 000000000..235f44b54 --- /dev/null +++ b/branch.ignore_shebang/index.html @@ -0,0 +1,125 @@ + + + + + + + + + + + +JSLint: The JavaScript Code Quality Tool + + +
    +
    Source
    +
    + + +
    +
    + Warnings +
    +
    +
    + Function Report +
    +
    +
    + Property Directive + +
    +
    + + + + +
    +
    Options +
    Assume... +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Global variables... + +
    +
    +
    +
    + + + + + +
    + + diff --git a/branch.ignore_shebang/jslint.css b/branch.ignore_shebang/jslint.css new file mode 100644 index 000000000..9c1b1a2f9 --- /dev/null +++ b/branch.ignore_shebang/jslint.css @@ -0,0 +1,340 @@ +@font-face { + font-family: 'Programma'; + src: url('Programma.woff') format('woff'); +} +@font-face { + font-family: 'Daley'; + font-weight: bold; + src: url('Daley-Bold.woff') format('woff'); +} + +body { + background-color: antiquewhite; + color: black; + font-family: 'Daley', sans-serif; + margin: 0; + padding: 0; +} + +#JSLINT_TITLEBOX { + font-family: 'Daley', sans-serif; + margin: 0; + margin-left: 3%; + margin-right: 3%; + padding: 0; +} + +#JSLINT_TITLEBOX ul { + float: right; + font-size: 12pt; +} + +#JSLINT_TITLEBOX div { + color: darkslategray; + float: left; + font-size: 48pt; +} + +#JSLINT_ fieldset { + background-color: gainsboro; + border: 0; + clear: both; + margin-bottom: 1em; + margin-left: 3%; + margin-right: 3%; + margin-top: 1em; + padding: 0; + width: auto; +} +#JSLINT_ legend { + background-color: darkslategray; + border: 0; + color: white; + font-size: 100%; + font-style: normal; + font-weight: normal; + margin: 0; + padding-bottom: 0.25em; + padding-left: 0; + padding-right: 0; + padding-top: 0.25em; + text-align: center; + width: 100%; +} +#JSLINT_ button { + background-color: darkslategray; + border: 0; + color: white; + cursor: pointer; + font-family: 'Daley', sans-serif; + font-size: 100%; + font-style: normal; + margin-left: 1em; + margin-right: 1em; + padding-left: 1em; + padding-right: 1em; + padding-bottom: 0.25em; + padding-top: 0.25em; + text-align: center; +} +#JSLINT_ button:hover { + background-color: slategray; + color: white; + border: 0; +} + +#JSLINT_ button:active { + border: 0; + color: black; +} + +#JSLINT_ button:disabled { + background-color: gray; + border: 0; + color: gainsboro; +} + +a:visited { + color: #69565c; +} + +a:link { + color: black; +} + +#JSLINT_ input[type="text"] { + background-color: white; + border: 0; + color: black; + margin-bottom: 0.2em; + margin-right: 0.5em; + padding-left: 0.5em; + padding-right: 0.5em; + text-align: right; + width: 3em; +} + +#JSLINT_ textarea { + border: 0; + background-color: white; + border: 0; + font-family: Programma, monospace; + font-size: 100%; + font-weight: bold; + resize: none; +} + +#JSLINT_ textarea::selection { + background-color: yellow; + color: black; +} + +#JSLINT_NUMBER { + color: darkgray; + display: inline-block; + height: 4in; + margin: 2%; + margin-right: 0; + overflow: hidden; + padding-right: 0.5%; + text-align: right; + white-space: pre; + width: 4%; +} + +#JSLINT_SOURCE { + color: black; + display: inline-block; + font-size: 100%; + height: 4in; + margin: 2%; + margin-left: 0; + margin-right: 0; + overflow: auto; + padding-left: 0.5%; + white-space: pre; + width: 91%; +} + +#JSLINT_PROPERTY { + background-color: honeydew; + border: 0; + margin: 2%; + padding: 0.5%; + white-space: pre; + width: 95%; +} + +#JSLINT_GLOBAL { + white-space: normal; + width: 95%; +} +#JSLINT_ label { + font-family: sans-serif; + font-size: 90%; + padding-left: 0.25em; +} +#JSLINT_OPTIONS>div { + float: left; + margin: 0.5em; +} + +#JSLINT_ address { + color: black; + display: block; + float: right; + font-family: serif; + font-size: 90%; + margin-left: 1em; +} +#JSLINT_ dl { + background-color: cornsilk; + color: white; + margin: 0; + padding-bottom: 2pt; + padding-left: 1em; + padding-right: 1em; + padding-top: 2pt; +} +#JSLINT_ dfn { + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + margin-bottom: 2pt; +} +#JSLINT_ dt { + color: black; + display: block; + float: left; + font-family: serif; + font-size: 75%; + font-style: italic; + margin: 0; + width: 8em; + text-align: right; +} +#JSLINT_ dd { + color: black; + display: block; + font-family: Programma, monospace; + font-weight: bold; + margin-left: 8em; + padding-bottom: 2pt; +} +#JSLINT_WARNINGS>legend { + background-color: indianred; +} +#JSLINT_WARNINGS>div { + background-color: pink; + padding: 1em; +} +#JSLINT_WARNINGS cite { + color: black; + display: block; + font-family: serif; + font-size: 100%; + font-style: normal; + margin-bottom: 4pt; + margin-left: 20pt; + margin-right: 20pt; + margin-top: 4pt; + overflow-x: hidden; +} +#JSLINT_WARNINGS samp { + background-color: lavenderblush; + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + padding: 4pt; + margin-bottom: 0; + margin-left: 16pt; + margin-right: 16pt; + margin-top: 0; + white-space: pre-wrap; +} + +#JSLINT_REPORT center { + margin-top: 1em; +} + +#JSLINT_WARNINGS dl address { + color: black; + display: inline; + float: none; + font-size: 80%; + margin: 0; +} + +#JSLINT_REPORT>div { + padding: 1em; +} + +#JSLINT_ dl.level0 { + color: black; + background-color: white; +} + +#JSLINT_ dl.level1 { + color: black; + background-color: #ffffe0; /* yellow */ + margin-left: 1em; +} + +#JSLINT_ dl.level2 { + color: black; + background-color: #e0ffe0; /* green */ + margin-left: 2em; +} + +#JSLINT_ dl.level3 { + color: black; + background-color: #e0e0ff; /* blue */ + margin-left: 3em; +} + +#JSLINT_ dl.level4 { + background-color: #ffe0ff; /* purple */ + color: black; + margin-left: 4em; +} + +#JSLINT_ dl.level5 { + background-color: #ffe0e0; /* red */ + color: black; + margin-left: 5em; +} + +#JSLINT_ dl.level6 { + background-color: #ffe390; /* orange */ + color: black; + margin-left: 6em; +} + +#JSLINT_ dl.level7 { + background-color: #e0e0e0; /* gray */ + color: black; + margin-left: 7em; +} + +#JSLINT_ dl.level8 { + color: black; + margin-left: 8em; +} + +#JSLINT_ dl.level9 { + color: black; + margin-left: 9em; +} + +.none { + display: none; +} +.center { + text-align: center; +} diff --git a/branch.ignore_shebang/jslint.js b/branch.ignore_shebang/jslint.js new file mode 100644 index 000000000..7817c0c32 --- /dev/null +++ b/branch.ignore_shebang/jslint.js @@ -0,0 +1,4921 @@ +// jslint.js +// 2018-09-13 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// jslint(source, option_object, global_array) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze, a string or an array of strings. +// option_object An object whose keys correspond to option names. +// global_array An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all of the functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// 1. If the source is a single string, split it into an array of strings. +// 2. Turn the source into an array of tokens. +// 3. Furcate the tokens into a parse tree. +// 4. Walk the tree, traversing all of the nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// 5. Check the whitespace between the tokens. + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*jslint long */ + +/*property + a, and, arity, assign, b, bad_assignment_a, bad_directive_a, + bad_get, bad_module_name_a, bad_option_a, bad_property_a, bad_set, + bitwise, block, body, browser, c, calls, catch, charCodeAt, closer, + closure, code, column, complex, concat, constant, context, convert, + couch, create, d, dead, default, devel, directive, directives, + disrupt, dot, duplicate_a, edition, ellipsis, else, empty_block, + escape_mega, eval, every, expected_a, expected_a_at_b_c, + expected_a_b, expected_a_b_from_c_d, expected_a_before_b, + expected_a_next_at_b, expected_digits_after_a, expected_four_digits, + expected_identifier_a, expected_line_break_a_b, + expected_regexp_factor_a, expected_space_a_b, expected_statements_a, + expected_string_a, expected_type_string_a, exports, expression, + extra, finally, flag, for, forEach, free, from, froms, fud, fudge, + function_in_loop, functions, g, getset, global, i, id, identifier, + import, inc, indexOf, infix_in, init, initial, isArray, isNaN, join, + json, keys, label, label_a, lbp, led, length, level, line, lines, + live, long, loop, m, margin, match, message, misplaced_a, + misplaced_directive_a, missing_browser, missing_m, module, multivar, + naked_block, name, names, nested_comment, new, node, not_label_a, + nr, nud, number_isNaN, ok, open, option, out_of_scope_a, parameters, + parent, pop, property, push, quote, redefinition_a_b, replace, + required_a_optional_b, reserved_a, right, role, search, signature, + single, slice, some, sort, split, statement, stop, strict, + subscript_a, switch, test, this, thru, toString, todo_comment, + tokens, too_long, too_many_digits, tree, try, type, u, + unclosed_comment, unclosed_mega, unclosed_string, undeclared_a, + unexpected_a, unexpected_a_after_b, unexpected_a_before_b, + unexpected_at_top_level_a, unexpected_char_a, unexpected_comment, + unexpected_directive_a, unexpected_expression_a, unexpected_label_a, + unexpected_parens, unexpected_space_a_b, unexpected_statement_a, + unexpected_trailing_space, unexpected_typeof_a, uninitialized_a, + unreachable_a, unregistered_property_a, unsafe, unused_a, + use_double, use_open, use_spaces, use_strict, used, value, var_loop, + var_switch, variable, warning, warnings, weird_condition_a, + weird_expression_a, weird_loop, weird_relation_a, white, + wrap_assignment, wrap_condition, wrap_immediate, wrap_parameter, + wrap_regexp, wrap_unary, wrapped, writable, y +*/ + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than {} because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +function populate(array, object = empty(), value = true) { + +// Augment an object by taking property names from an array of strings. + + array.forEach(function (name) { + object[name] = value; + }); + return object; +} + +const allowed_option = { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + bitwise: true, + browser: [ + "caches", "clearInterval", "clearTimeout", "document", "DOMException", + "Element", "Event", "event", "FileReader", "FormData", "history", + "localStorage", "location", "MutationObserver", "name", "navigator", + "screen", "sessionStorage", "setInterval", "setTimeout", "Storage", + "URL", "window", "Worker", "XMLHttpRequest" + ], + couch: [ + "emit", "getRow", "isArray", "log", "provides", "registerType", + "require", "send", "start", "sum", "toJSON" + ], + convert: true, + devel: [ + "alert", "confirm", "console", "prompt" + ], + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + multivar: true, + node: [ + "Buffer", "clearImmediate", "clearInterval", "clearTimeout", + "console", "exports", "module", "process", "require", + "setImmediate", "setInterval", "setTimeout", "URL", + "URLSearchParams", "__dirname", "__filename" + ], + single: true, + this: true, + white: true +}; + +const anticondition = populate([ + "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%", + "typeof", "(number)", "(string)" +]); + +// These are the bitwise operators. + +const bitwiseop = populate([ + "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=", + ">>>", ">>>=" +]); + +const escapeable = populate([ + "\\", "/", "`", "b", "f", "n", "r", "t" +]); + +const opener = { + +// The open and close pairs. + + "(": ")", // paren + "[": "]", // bracket + "{": "}", // brace + "${": "}" // mega +}; + +// The relational operators. + +const relationop = populate([ + "!=", "!==", "==", "===", "<", "<=", ">", ">=" +]); + +// This is the set of infix operators that require a space on each side. + +const spaceop = populate([ + "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/", + "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=", + ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" +]); + +const standard = [ + +// These are the globals that are provided by the language standard. + + "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI", + "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error", + "EvalError", "Float32Array", "Float64Array", "Generator", + "GeneratorFunction", "Int8Array", "Int16Array", "Int32Array", "Intl", + "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat", + "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", + "Set", "String", "Symbol", "SyntaxError", "System", "TypeError", + "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", + "URIError", "WeakMap", "WeakSet" +]; + +const bundle = { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + and: "The '&&' subexpression should be wrapped in parens.", + bad_assignment_a: "Bad assignment to '{a}'.", + bad_directive_a: "Bad directive '{a}'.", + bad_get: "A get function takes no parameters.", + bad_module_name_a: "Bad module name '{a}'.", + bad_option_a: "Bad option '{a}'.", + bad_property_a: "Bad property name '{a}'.", + bad_set: "A set function takes one parameter.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + escape_mega: "Unexpected escapement in mega literal.", + expected_a: "Expected '{a}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: ( + "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'." + ), + expected_a_before_b: "Expected '{a}' before '{b}'.", + expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.", + expected_digits_after_a: "Expected digits after '{a}'.", + expected_four_digits: "Expected four digits after '\\u'.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.", + expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.", + expected_space_a_b: "Expected one space between '{a}' and '{b}'.", + expected_statements_a: "Expected statements before '{a}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + expected_type_string_a: "Expected a type string and instead saw '{a}'.", + function_in_loop: "Don't make functions within a loop.", + infix_in: ( + "Unexpected 'in'. Compare with undefined, " + + "or use the hasOwnProperty method instead." + ), + label_a: "'{a}' is a statement label.", + misplaced_a: "Place '{a}' at the outermost level.", + misplaced_directive_a: ( + "Place the '/*{a}*/' directive before the first statement." + ), + missing_browser: "/*global*/ requires the Assume a browser option.", + missing_m: "Expected 'm' flag on a multiline regular expression.", + naked_block: "Naked block.", + nested_comment: "Nested comment.", + not_label_a: "'{a}' is not a label.", + number_isNaN: "Use Number.isNaN function to compare with NaN.", + out_of_scope_a: "'{a}' is out of scope.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + required_a_optional_b: ( + "Required parameter '{a}' after optional parameter '{b}'." + ), + reserved_a: "Reserved name '{a}'.", + subscript_a: "['{a}'] is better written in dot notation.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line is longer than 80 characters.", + too_many_digits: "Too many digits.", + unclosed_comment: "Unclosed comment.", + unclosed_mega: "Unclosed mega literal.", + unclosed_string: "Unclosed string.", + undeclared_a: "Undeclared '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_a_after_b: "Unexpected '{a}' after '{b}'.", + unexpected_a_before_b: "Unexpected '{a}' before '{b}'.", + unexpected_at_top_level_a: "Expected '{a}' to be in a function.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_directive_a: "When using modules, don't use directive '/*{a}'.", + unexpected_expression_a: ( + "Unexpected expression '{a}' in statement position." + ), + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_parens: "Don't wrap function literals in parens.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_statement_a: ( + "Unexpected statement '{a}' in expression position." + ), + unexpected_trailing_space: "Unexpected trailing space.", + unexpected_typeof_a: ( + "Unexpected 'typeof'. Use '===' to compare directly with {a}." + ), + uninitialized_a: "Uninitialized '{a}'.", + unreachable_a: "Unreachable '{a}'.", + unregistered_property_a: "Unregistered property name '{a}'.", + unsafe: "Unsafe character '{a}'.", + unused_a: "Unused '{a}'.", + use_double: "Use double quotes, not single quotes.", + use_open: ( + "Wrap a ternary expression in parens, " + + "with a line break after the left paren." + ), + use_spaces: "Use spaces, not tabs.", + use_strict: "This function needs a \"use strict\" pragma.", + var_loop: "Don't declare variables in a loop.", + var_switch: "Don't declare variables in a switch.", + weird_condition_a: "Weird condition '{a}'.", + weird_expression_a: "Weird expression '{a}'.", + weird_loop: "Weird loop.", + weird_relation_a: "Weird relation '{a}'.", + wrap_assignment: "Don't wrap assignment statements in parens.", + wrap_condition: "Wrap the condition in parens.", + wrap_immediate: ( + "Wrap an immediate function invocation in parentheses to assist " + + "the reader in understanding that the expression is the result " + + "of a function, and not the function itself." + ), + wrap_parameter: "Wrap the parameter in parens.", + wrap_regexp: "Wrap this regexp in parens to avoid confusion.", + wrap_unary: "Wrap the unary expression in parens." +}; + +// Regular expression literals: + +// supplant {variables} +const rx_supplant = /\{([^{}]*)\}/g; +// carriage return, carriage return linefeed, or linefeed +const rx_crlf = /\n|\r\n?/; +// unsafe characters that are silently deleted by one or more browsers +const rx_unsafe = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; +// identifier +const rx_identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; +const rx_module = /^[a-zA-Z0-9_$:.@\-\/]+$/; +const rx_bad_property = /^_|\$|Sync\$|_$/; +// star slash +const rx_star_slash = /\*\//; +// slash star +const rx_slash_star = /\/\*/; +// slash star or ending slash +const rx_slash_star_or_slash = /\/\*|\/$/; +// uncompleted work comment +const rx_todo = /\b(?:todo|TO\s?DO|HACK)\b/; +// tab +const rx_tab = /\t/g; +// directive +const rx_directive = /^(jslint|property|global)\s+(.*)$/; +const rx_directive_part = /^([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*(.*)$/; +// token (sorry it is so long) +const rx_token = /^((\s+)|([a-zA-Z_$][a-zA-Z0-9_$]*)|[(){}\[\]?,:;'"~`]|=(?:==?|>)?|\.+|[*\/][*\/=]?|\+[=+]?|-[=\-]?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<= "a" && string <= "z\uffff") + || (string >= "A" && string <= "Z\uffff") + ); +} + +function supplant(string, object) { + return string.replace(rx_supplant, function (found, filling) { + const replacement = object[filling]; + return ( + replacement !== undefined + ? replacement + : found + ); + }); +} + +let anon; // The guessed name for anonymous functions. +let blockage; // The current block. +let block_stack; // The stack of blocks. +let declared_globals; // The object containing the global declarations. +let directives; // The directive comments. +let directive_mode; // true if directives are still allowed. +let early_stop; // true if JSLint cannot finish. +let exports; // The exported names and values. +let froms; // The array collecting all import-from strings. +let fudge; // true if the natural numbers start with 1. +let functionage; // The current function. +let functions; // The array containing all of the functions. +let global; // The global object; the outermost context. +let json_mode; // true if parsing JSON. +let lines; // The array containing source lines. +let module_mode; // true if import or export was used. +let next_token; // The next token to be examined in the parse. +let option; // The options parameter. +let property; // The object containing the tallied property names. +let mega_mode; // true if currently parsing a megastring literal. +let stack; // The stack of functions. +let syntax; // The object containing the parser. +let token; // The current token being examined in the parse. +let token_nr; // The number of the next token. +let tokens; // The array of tokens. +let tenure; // The predefined property registry. +let tree; // The abstract parse tree. +let var_mode; // "var" if using var; "let" if using let. +let warnings; // The array collecting all generated warnings. + +// Error reportage functions: + +function artifact(the_token) { + +// Return a string representing an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); +} + +function artifact_line(the_token) { + +// Return the fudged line number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.line + fudge; +} + +function artifact_column(the_token) { + +// Return the fudged column number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.from + fudge; +} + +function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + const warning = { // ~~ + name: "JSLintError", + column: column, + line: line, + code: code + }; + if (a !== undefined) { + warning.a = a; + } + if (b !== undefined) { + warning.b = b; + } + if (c !== undefined) { + warning.c = c; + } + if (d !== undefined) { + warning.d = d; + } + warning.message = supplant(bundle[code] || code, warning); + warnings.push(warning); + return warning; +} + +function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); +} + +function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + if (the_token.warning === undefined) { + the_token.warning = warn_at( + code, + the_token.line, + the_token.from, + a || artifact(the_token), + b, + c, + d + ); + return the_token.warning; + } +} + +function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); +} + +// Tokenize: + +function tokenize(source) { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + +// If the source is not an array, then it is split into lines at the +// carriage return/linefeed. + + lines = ( + Array.isArray(source) + ? source + : source.split(rx_crlf) + ); + tokens = []; + + let char; // a popular character + let column = 0; // the column number of the next character + let first; // the first token + let from; // the starting column number of the token + let line = -1; // the line number of the next character + let nr = 0; // the next token number + let previous = global; // the previous token including comments + let prior = global; // the previous token excluding comments + let mega_from; // the starting column of megastring + let mega_line; // the starting line of megastring + let snippet; // a piece of string + let source_line; // the current line source string + + function next_line() { + +// Put the next line of source in source_line. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + let at; + column = 0; + line += 1; + source_line = lines[line]; + if (source_line !== undefined) { + // ignore shebang at line=0 common in node scripts + if (line === 0 && rx_shebang.test(source_line)) { + source_line = ""; + } + at = source_line.search(rx_tab); + if (at >= 0) { + if (!option.white) { + warn_at("use_spaces", line, at + 1); + } + source_line = source_line.replace(rx_tab, " "); + } + at = source_line.search(rx_unsafe); + if (at >= 0) { + warn_at( + "unsafe", + line, + column + at, + "U+" + source_line.charCodeAt(at).toString(16) + ); + } + if (!option.white && source_line.slice(-1) === " ") { + warn_at( + "unexpected_trailing_space", + line, + source_line.length - 1 + ); + } + if ( + !option.long + && source_line.length > 80 + && !json_mode + && first + ) { + warn_at("too_long", line, 80); + } + } + return source_line; + } + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions snip, next_char, prev_char, +// some_digits, and escape help in the parsing of literals. + + function snip() { + +// Remove the last character from snippet. + + snippet = snippet.slice(0, -1); + } + + function next_char(match) { + +// Get the next character from the source line. Remove it from the source_line, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + return stop_at( + ( + char === "" + ? "expected_a" + : "expected_a_b" + ), + line, + column - 1, + match, + char + ); + } + if (source_line) { + char = source_line[0]; + source_line = source_line.slice(1); + snippet += char; + } else { + char = ""; + snippet += " "; + } + column += 1; + return char; + } + + function back_char() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the source_line. + + if (snippet) { + char = snippet.slice(-1); + source_line = char + source_line; + column -= 1; + snip(); + } else { + char = ""; + } + return char; + } + + function some_digits(rx, quiet) { + const result = source_line.match(rx); + if (result) { + char = result[1]; + column += char.length; + source_line = result[2]; + snippet += char; + } else { + char = ""; + if (!quiet) { + warn_at( + "expected_digits_after_a", + line, + column, + snippet + ); + } + } + return char.length; + } + + function escape(extra) { + next_char("\\"); + if (escapeable[char] === true) { + return next_char(); + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "u") { + if (next_char("u") === "{") { + if (json_mode) { + warn_at("unexpected_a", line, column - 1, char); + } + if (some_digits(rx_hexs) > 5) { + warn_at("too_many_digits", line, column - 1); + } + if (next_char() !== "}") { + stop_at("expected_a_before_b", line, column, "}", char); + } + return next_char(); + } + back_char(); + if (some_digits(rx_hexs, true) < 4) { + warn_at("expected_four_digits", line, column - 1); + } + return; + } + if (extra && extra.indexOf(char) >= 0) { + return next_char(); + } + warn_at("unexpected_a_before_b", line, column - 2, "\\", char); + } + + function make(id, value, identifier) { + +// Make the token object and append it to the tokens list. + + const the_token = { + from: from, + id: id, + identifier: Boolean(identifier), + line: line, + nr: nr, + thru: column + }; + tokens[nr] = the_token; + nr += 1; + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + directive_mode = false; + } + +// If the token is to have a value, give it one. + + if (value !== undefined) { + the_token.value = value; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option.white. + + if ( + previous.line === line + && previous.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (previous.id === "(comment)" || previous.id === "(regexp)") + ) { + warn( + "expected_space_a_b", + the_token, + artifact(previous), + artifact(the_token) + ); + } + if (previous.id === "." && id === "(number)") { + warn("expected_a_before_b", previous, "0", "."); + } + if (prior.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// The previous token is used to detect adjacency problems. + + previous = the_token; + +// The prior token is a previous token that was not a comment. The prior token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (previous.id !== "(comment)") { + prior = previous; + } + return the_token; + } + + function parse_directive(the_comment, body) { + +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + + const result = body.match(rx_directive_part); + if (result) { + let allowed; + const name = result[1]; + const value = result[2]; + if (the_comment.directive === "jslint") { + allowed = allowed_option[name]; + if ( + typeof allowed === "boolean" + || typeof allowed === "object" + ) { + if ( + value === "" + || value === "true" + || value === undefined + ) { + option[name] = true; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } else if (value === "false") { + option[name] = false; + } else { + warn("bad_option_a", the_comment, name + ":" + value); + } + } else { + warn("bad_option_a", the_comment, name); + } + } else if (the_comment.directive === "property") { + if (tenure === undefined) { + tenure = empty(); + } + tenure[name] = true; + } else if (the_comment.directive === "global") { + if (value) { + warn("bad_option_a", the_comment, name + ":" + value); + } + declared_globals[name] = false; + module_mode = the_comment; + } + return parse_directive(the_comment, result[3]); + } + if (body) { + return stop("bad_directive_a", the_comment, body); + } + } + + function comment(snippet) { + +// Make a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + + const the_comment = make("(comment)", snippet); + if (Array.isArray(snippet)) { + snippet = snippet.join(" "); + } + if (!option.devel && rx_todo.test(snippet)) { + warn("todo_comment", the_comment); + } + const result = snippet.match(rx_directive); + if (result) { + if (!directive_mode) { + warn_at("misplaced_directive_a", line, from, result[1]); + } else { + the_comment.directive = result[1]; + parse_directive(the_comment, result[2]); + } + directives.push(the_comment); + } + return the_comment; + } + + function regexp() { + +// Parse a regular expression literal. + + let multi_mode = false; + let result; + let value; + + function quantifier() { + +// Match an optional quantifier. + + if (char === "?" || char === "*" || char === "+") { + next_char(); + } else if (char === "{") { + if (some_digits(rx_digits, true) === 0) { + warn_at("expected_a", line, column, "0"); + } + if (next_char() === ",") { + some_digits(rx_digits, true); + next_char(); + } + next_char("}"); + } else { + return; + } + if (char === "?") { + next_char("?"); + } + } + + function subklass() { + +// Match a character in a character class. + + if (char === "\\") { + escape("BbDdSsWw-[]^"); + return true; + } + if ( + char === "" + || char === "[" + || char === "]" + || char === "/" + || char === "^" + || char === "-" + ) { + return false; + } + if (char === " ") { + warn_at("expected_a_b", line, column, "\\u0020", " "); + } else if (char === "`" && mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char(); + return true; + } + + function ranges() { + +// Match a range of subclasses. + + if (subklass()) { + if (char === "-") { + next_char("-"); + if (!subklass()) { + return stop_at( + "unexpected_a", + line, + column - 1, + "-" + ); + } + } + return ranges(); + } + } + + function klass() { + +// Match a class. + + next_char("["); + if (char === "^") { + next_char("^"); + } + (function classy() { + ranges(); + if (char !== "]" && char !== "") { + warn_at( + "expected_a_before_b", + line, + column, + "\\", + char + ); + next_char(); + return classy(); + } + }()); + next_char("]"); + } + + function choice() { + + function group() { + +// Match a group that starts with left paren. + + next_char("("); + if (char === "?") { + next_char("?"); + if (char === "=" || char === "!") { + next_char(); + } else { + next_char(":"); + } + } else if (char === ":") { + warn_at("expected_a_before_b", line, column, "?", ":"); + } + choice(); + next_char(")"); + } + + function factor() { + if ( + char === "" + || char === "/" + || char === "]" + || char === ")" + ) { + return false; + } + if (char === "(") { + group(); + return true; + } + if (char === "[") { + klass(); + return true; + } + if (char === "\\") { + escape("BbDdSsWw^${}[]():=!.-|*+?"); + return true; + } + if ( + char === "?" + || char === "+" + || char === "*" + || char === "}" + || char === "{" + ) { + warn_at( + "expected_a_before_b", + line, + column - 1, + "\\", + char + ); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column - 1, "`"); + } + } else if (char === " ") { + warn_at( + "expected_a_b", + line, + column - 1, + "\\s", + " " + ); + } else if (char === "$") { + if (source_line[0] !== "/") { + multi_mode = true; + } + } else if (char === "^") { + if (snippet !== "^") { + multi_mode = true; + } + } + next_char(); + return true; + } + + function sequence(follow) { + if (factor()) { + quantifier(); + return sequence(true); + } + if (!follow) { + warn_at("expected_regexp_factor_a", line, column, char); + } + } + +// Match a choice (a sequence that can be followed by | and another choice). + + sequence(); + if (char === "|") { + next_char("|"); + return choice(); + } + } + +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + next_char(); + if (char === "=") { + warn_at("expected_a_before_b", line, column, "\\", "="); + } + choice(); + +// Make sure there is a closing slash. + + snip(); + value = snippet; + next_char("/"); + +// Process dangling flag letters. + + const allowed = { + g: true, + i: true, + m: true, + u: true, + y: true + }; + const flag = empty(); + (function make_flag() { + if (is_letter(char)) { + if (allowed[char] !== true) { + warn_at("unexpected_a", line, column, char); + } + allowed[char] = false; + flag[char] = true; + next_char(); + return make_flag(); + } + }()); + back_char(); + if (char === "/" || char === "*") { + return stop_at("unexpected_a", line, from, char); + } + result = make("(regexp)", char); + result.flag = flag; + result.value = value; + if (multi_mode && !flag.m) { + warn_at("missing_m", line, column); + } + return result; + } + + function string(quote) { + +// Make a string token. + + let the_token; + snippet = ""; + next_char(); + + return (function next() { + if (char === quote) { + snip(); + the_token = make("(string)", snippet); + the_token.quote = quote; + return the_token; + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "\\") { + escape(quote); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char("`"); + } else { + next_char(); + } + return next(); + }()); + } + + function frack() { + if (char === ".") { + some_digits(rx_digits); + next_char(); + } + if (char === "E" || char === "e") { + next_char(); + if (char !== "+" && char !== "-") { + back_char(); + } + some_digits(rx_digits); + next_char(); + } + } + + function number() { + if (snippet === "0") { + next_char(); + if (char === ".") { + frack(); + } else if (char === "b") { + some_digits(rx_bits); + next_char(); + } else if (char === "o") { + some_digits(rx_octals); + next_char(); + } else if (char === "x") { + some_digits(rx_hexs); + next_char(); + } + } else { + next_char(); + frack(); + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + return stop_at( + "unexpected_a_after_b", + line, + column - 1, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + back_char(); + return make("(number)", snippet); + } + + function lex() { + let array; + let i = 0; + let j = 0; + let last; + let result; + let the_token; + if (!source_line) { + source_line = next_line(); + from = 0; + return ( + source_line === undefined + ? ( + mega_mode + ? stop_at("unclosed_mega", mega_line, mega_from) + : make("(end)") + ) + : lex() + ); + } + from = column; + result = source_line.match(rx_token); + +// result[1] token +// result[2] whitespace +// result[3] identifier +// result[4] number +// result[5] rest + + if (!result) { + return stop_at( + "unexpected_char_a", + line, + column, + source_line[0] + ); + } + + snippet = result[1]; + column += snippet.length; + source_line = result[5]; + +// Whitespace was matched. Call lex again to get more. + + if (result[2]) { + return lex(); + } + +// The token is an identifier. + + if (result[3]) { + return make(snippet, undefined, true); + } + +// The token is a number. + + if (result[4]) { + return number(snippet); + } + +// The token is a string. + + if (snippet === "\"") { + return string(snippet); + } + if (snippet === "'") { + if (!option.single) { + warn_at("use_double", line, column); + } + return string(snippet); + } + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (snippet === "`") { + if (mega_mode) { + return stop_at("expected_a_b", line, column, "}", "`"); + } + snippet = ""; + mega_from = from; + mega_line = line; + mega_mode = true; + +// Parsing a mega literal is tricky. First make a ` token. + + make("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + (function part() { + const at = source_line.search(rx_mega); + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + if (at < 0) { + snippet += source_line + "\n"; + return ( + next_line() === undefined + ? stop_at("unclosed_mega", mega_line, mega_from) + : part() + ); + } + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + snippet += source_line.slice(0, at); + column += at; + source_line = source_line.slice(at); + if (source_line[0] === "\\") { + stop_at("escape_mega", line, at); + } + make("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then make tokens that will become part of an expression until +// a } token is made. + + if (source_line[0] === "$") { + column += 2; + make("${"); + source_line = source_line.slice(2); + (function expr() { + const id = lex().id; + if (id === "{") { + return stop_at( + "expected_a_b", + line, + column, + "}", + "{" + ); + } + if (id !== "}") { + return expr(); + } + }()); + return part(); + } + }()); + source_line = source_line.slice(1); + column += 1; + mega_mode = false; + return make("`"); + } + +// The token is a // comment. + + if (snippet === "//") { + snippet = source_line; + source_line = ""; + the_token = comment(snippet); + if (mega_mode) { + warn("unexpected_comment", the_token, "`"); + } + return the_token; + } + +// The token is a /* comment. + + if (snippet === "/*") { + array = []; + if (source_line[0] === "/") { + warn_at("unexpected_a", line, column + i, "/"); + } + (function next() { + if (source_line > "") { + i = source_line.search(rx_star_slash); + if (i >= 0) { + return; + } + j = source_line.search(rx_slash_star); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + } + array.push(source_line); + source_line = next_line(); + if (source_line === undefined) { + return stop_at("unclosed_comment", line, column); + } + return next(); + }()); + snippet = source_line.slice(0, i); + j = snippet.search(rx_slash_star_or_slash); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + array.push(snippet); + column += i + 2; + source_line = source_line.slice(i + 2); + return comment(array); + } + +// The token is a slash. + + if (snippet === "/") { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + if (prior.identifier) { + if (!prior.dot) { + if (prior.id === "return") { + return regexp(); + } + if ( + prior.id === "(begin)" + || prior.id === "case" + || prior.id === "delete" + || prior.id === "in" + || prior.id === "instanceof" + || prior.id === "new" + || prior.id === "typeof" + || prior.id === "void" + || prior.id === "yield" + ) { + the_token = regexp(); + return stop("unexpected_a", the_token); + } + } + } else { + last = prior.id[prior.id.length - 1]; + if ("(,=:?[".indexOf(last) >= 0) { + return regexp(); + } + if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) { + the_token = regexp(); + warn("wrap_regexp", the_token); + return the_token; + } + } + if (source_line[0] === "/") { + column += 1; + source_line = source_line.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + } + return make(snippet); + } + + first = lex(); + json_mode = first.id === "{" || first.id === "["; + +// This is the only loop in JSLint. It will turn into a recursive call to lex +// when ES6 has been finished and widely deployed and adopted. + + while (true) { + if (lex().id === "(end)") { + break; + } + } +} + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + +function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!rx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!rx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property[id] === "number") { + property[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (tenure !== undefined) { + if (tenure[id] !== true) { + warn("unregistered_property_a", name); + } + } else { + if (name.identifier && rx_bad_property.test(id)) { + warn("bad_property_a", name); + } + } + property[id] = 1; + } + return id; +} + +function dispense() { + +// Deliver the next token, skipping the comments. + + const cadet = tokens[token_nr]; + token_nr += 1; + if (cadet.id === "(comment)") { + if (json_mode) { + warn("unexpected_a", cadet); + } + return dispense(); + } else { + return cadet; + } +} + +function lookahead() { + +// Look ahead one token without advancing. + + const old_token_nr = token_nr; + const cadet = dispense(true); + token_nr = old_token_nr; + return cadet; +} + +function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if (token.identifier && token.id !== "function") { + anon = token.id; + } else if (token.id === "(string)" && rx_identifier.test(token.value)) { + anon = token.value; + } + +// Attempt to match next_token with an expected id. + + if (id !== undefined && next_token.id !== id) { + return ( + match === undefined + ? stop("expected_a_b", next_token, id, artifact()) + : stop( + "expected_a_b_from_c_d", + next_token, + id, + artifact(match), + artifact_line(match), + artifact(next_token) + ) + ); + } + +// Promote the tokens, skipping comments. + + token = next_token; + next_token = dispense(); + if (next_token.id === "(end)") { + token_nr -= 1; + } +} + +// Parsing of JSON is simple: + +function json_value() { + let negative; + if (next_token.id === "{") { + return (function json_object() { + const brace = next_token; + const object = empty(); + const properties = []; + brace.expression = properties; + advance("{"); + if (next_token.id !== "}") { + (function next() { + let name; + let value; + if (next_token.quote !== "\"") { + warn( + "unexpected_a", + next_token, + next_token.quote + ); + } + name = next_token; + advance("(string)"); + if (object[token.value] !== undefined) { + warn("duplicate_a", token); + } else if (token.value === "__proto__") { + warn("bad_property_a", token); + } else { + object[token.value] = token; + } + advance(":"); + value = json_value(); + value.label = name; + properties.push(value); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("}", brace); + return brace; + }()); + } + if (next_token.id === "[") { + return (function json_array() { + const bracket = next_token; + const elements = []; + bracket.expression = elements; + advance("["); + if (next_token.id !== "]") { + (function next() { + elements.push(json_value()); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]", bracket); + return bracket; + }()); + } + if ( + next_token.id === "true" + || next_token.id === "false" + || next_token.id === "null" + ) { + advance(); + return token; + } + if (next_token.id === "(number)") { + if (!rx_JSON_number.test(next_token.value)) { + warn("unexpected_a"); + } + advance(); + return token; + } + if (next_token.id === "(string)") { + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + advance(); + return token; + } + if (next_token.id === "-") { + negative = next_token; + negative.arity = "unary"; + advance("-"); + advance("(number)"); + negative.expression = token; + return negative; + } + stop("unexpected_a"); +} + +// Now we parse JavaScript. + +function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + const id = name.id; + +// Reserved words may not be enrolled. + + if (syntax[id] !== undefined && id !== "ignore") { + warn("reserved_a", name); + } else { + +// Has the name been enrolled in this context? + + let earlier = functionage.context[id]; + if (earlier) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + +// Has the name been enrolled in an outer context? + + } else { + stack.forEach(function (value) { + const item = value.context[id]; + if (item !== undefined) { + earlier = item; + } + }); + if (earlier) { + if (id === "ignore") { + if (earlier.role === "variable") { + warn("unexpected_a", name); + } + } else { + if ( + ( + role !== "exception" + || earlier.role !== "exception" + ) + && role !== "parameter" + && role !== "function" + ) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + } + } + } + +// Enroll it. + + functionage.context[id] = name; + name.dead = true; + name.parent = functionage; + name.init = false; + name.role = role; + name.used = 0; + name.writable = !readonly; + } + } +} + +function expression(rbp, initial) { + +// This is the heart of the Pratt parser. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// nud Null denotation +// led Left denotation +// lbp Left binding power +// rbp Right binding power + +// It processes a nud (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop. It +// returns the expression's parse tree. + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement, + + if (!initial) { + advance(); + } + the_symbol = syntax[token.id]; + if (the_symbol !== undefined && the_symbol.nud !== undefined) { + left = the_symbol.nud(); + } else if (token.identifier) { + left = token; + left.arity = "variable"; + } else { + return stop("unexpected_a", token); + } + (function right() { + the_symbol = syntax[next_token.id]; + if ( + the_symbol !== undefined + && the_symbol.led !== undefined + && rbp < the_symbol.lbp + ) { + advance(); + left = the_symbol.led(left); + return right(); + } + }()); + return left; +} + +function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = next_token; + let the_value; + the_paren.free = true; + advance("("); + the_value = expression(0); + advance(")"); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (anticondition[the_value.id] === true) { + warn("unexpected_a", the_value); + } + return the_value; +} + +function is_weird(thing) { + return ( + thing.id === "(regexp)" + || thing.id === "{" + || thing.id === "=>" + || thing.id === "function" + || (thing.id === "[" && thing.arity === "unary") + ); +} + +function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + return ( + Array.isArray(b) + && a.length === b.length + && a.every(function (value, index) { + return are_similar(value, b[index]); + }) + ); + } + if (Array.isArray(b)) { + return false; + } + if (a.id === "(number)" && b.id === "(number)") { + return a.value === b.value; + } + let a_string; + let b_string; + if (a.id === "(string)") { + a_string = a.value; + } else if (a.id === "`" && a.constant) { + a_string = a.value[0]; + } + if (b.id === "(string)") { + b_string = b.value; + } else if (b.id === "`" && b.constant) { + b_string = b.value[0]; + } + if (typeof a_string === "string") { + return a_string === b_string; + } + if (is_weird(a) || is_weird(b)) { + return false; + } + if (a.arity === b.arity && a.id === b.id) { + if (a.id === ".") { + return ( + are_similar(a.expression, b.expression) + && are_similar(a.name, b.name) + ); + } + if (a.arity === "unary") { + return are_similar(a.expression, b.expression); + } + if (a.arity === "binary") { + return ( + a.id !== "(" + && are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + ); + } + if (a.arity === "ternary") { + return ( + are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + && are_similar(a.expression[2], b.expression[2]) + ); + } + if (a.arity === "function" && a.arity === "regexp") { + return false; + } + return true; + } + return false; +} + +function semicolon() { + +// Try to match a semicolon. + + if (next_token.id === ";") { + advance(";"); + } else { + warn_at( + "expected_a_b", + token.line, + token.thru, + ";", + artifact(next_token) + ); + } + anon = "anonymous"; +} + +function statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token.identifier && next_token.id === ":") { + the_label = token; + if (the_label.id === "ignore") { + warn("unexpected_a", the_label); + } + advance(":"); + if ( + next_token.id === "do" + || next_token.id === "for" + || next_token.id === "switch" + || next_token.id === "while" + ) { + enroll(the_label, "label", true); + the_label.init = true; + the_label.dead = false; + the_statement = statement(); + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token; + first.statement = true; + the_symbol = syntax[first.id]; + if (the_symbol !== undefined && the_symbol.fud !== undefined) { + the_symbol.disrupt = false; + the_symbol.statement = true; + the_statement = the_symbol.fud(); + } else { + +// It is an expression statement. + + the_statement = expression(0, true); + if (the_statement.wrapped && the_statement.id !== "(") { + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; +} + +function statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const array = []; + (function next(disrupt) { + if ( + next_token.id !== "}" + && next_token.id !== "case" + && next_token.id !== "default" + && next_token.id !== "else" + && next_token.id !== "(end)" + ) { + let a_statement = statement(); + array.push(a_statement); + if (disrupt) { + warn("unreachable_a", a_statement); + } + return next(a_statement.disrupt); + } + }(false)); + return array; +} + +function not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === global) { + warn("unexpected_at_top_level_a", thing); + } +} + +function top_level_only(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== global) { + warn("misplaced_a", the_thing); + } +} + +function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token; + the_block.arity = "statement"; + the_block.body = special === "body"; + +// All top level function bodies should include the "use strict" pragma unless +// the whole file is strict or the file is a module or the function parameters +// use es6 syntax. + + if ( + special === "body" + && stack.length === 1 + && next_token.value === "use strict" + ) { + the_block.strict = next_token; + next_token.statement = true; + advance("(string)"); + advance(";"); + } + stmts = statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option.devel && special !== "ignore") { + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; +} + +function mutation_check(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + warn("bad_assignment_a", the_thing); + return false; + } + return true; +} + +function left_check(left, right) { + +// Warn if the left is not one of these: +// e.b +// e[b] +// e() +// ?: +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !left_check(left.expression[1]) + && !left_check(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right); + return false; + } + return true; +} + +// These functions are used to specify the grammar of our language: + +function symbol(id, bp) { + +// Make a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax[id] = the_symbol; + } + return the_symbol; +} + +function assignment(id) { + +// Make an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led = function (left) { + const the_token = token; + let right; + the_token.arity = "assignment"; + right = expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "pre" + || right.arity === "post" + ) { + warn("unexpected_a", right); + } + mutation_check(left); + return the_token; + }; + return the_symbol; +} + +function constant(id, type, value) { + +// Make a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud = ( + typeof value === "function" + ? value + : function () { + token.constant = true; + if (value !== undefined) { + token.value = value; + } + return token; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; +} + +function infix(id, bp, f) { + +// Make an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, expression(bp)]; + return the_token; + }; + return the_symbol; +} + +function infixr(id, bp) { + +// Make a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + the_token.expression = [left, expression(bp - 1)]; + return the_token; + }; + return the_symbol; +} + +function post(id) { + +// Make one of the post operators. + + const the_symbol = symbol(id, 150); + the_symbol.led = function (left) { + token.expression = left; + token.arity = "post"; + mutation_check(token.expression); + return token; + }; + return the_symbol; +} + +function pre(id) { + +// Make one of the pre operators. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "pre"; + the_token.expression = expression(150); + mutation_check(the_token.expression); + return the_token; + }; + return the_symbol; +} + +function prefix(id, f) { + +// Make a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = expression(150); + return the_token; + }; + return the_symbol; +} + +function stmt(id, f) { + +// Make a statement. + + const the_symbol = symbol(id); + the_symbol.fud = function () { + token.arity = "statement"; + return f(); + }; + return the_symbol; +} + +function ternary(id1, id2) { + +// Make a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led = function (left) { + const the_token = token; + const second = expression(20); + advance(id2); + token.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, expression(10)]; + return the_token; + }; + return the_symbol; +} + +// Begin defining the language. + +syntax = empty(); + +symbol("}"); +symbol(")"); +symbol("]"); +symbol(","); +symbol(";"); +symbol(":"); +symbol("*/"); +symbol("await"); +symbol("case"); +symbol("catch"); +symbol("class"); +symbol("default"); +symbol("else"); +symbol("enum"); +symbol("finally"); +symbol("implements"); +symbol("interface"); +symbol("package"); +symbol("private"); +symbol("protected"); +symbol("public"); +symbol("static"); +symbol("super"); +symbol("void"); +symbol("yield"); + +constant("(number)", "number"); +constant("(regexp)", "regexp"); +constant("(string)", "string"); +constant("arguments", "object", function () { + warn("unexpected_a", token); + return token; +}); +constant("eval", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("false", "boolean", false); +constant("Function", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("ignore", "undefined", function () { + warn("unexpected_a", token); + return token; +}); +constant("Infinity", "number", Infinity); +constant("isFinite", "function", function () { + warn("expected_a_b", token, "Number.isFinite", "isFinite"); + return token; +}); +constant("isNaN", "function", function () { + warn("number_isNaN", token); + return token; +}); +constant("NaN", "number", NaN); +constant("null", "null", null); +constant("this", "object", function () { + if (!option.this) { + warn("unexpected_a", token); + } + return token; +}); +constant("true", "boolean", true); +constant("undefined", "undefined"); + +assignment("="); +assignment("+="); +assignment("-="); +assignment("*="); +assignment("/="); +assignment("%="); +assignment("&="); +assignment("|="); +assignment("^="); +assignment("<<="); +assignment(">>="); +assignment(">>>="); + +infix("||", 40); +infix("&&", 50); +infix("|", 70); +infix("^", 80); +infix("&", 90); +infix("==", 100); +infix("===", 100); +infix("!=", 100); +infix("!==", 100); +infix("<", 110); +infix(">", 110); +infix("<=", 110); +infix(">=", 110); +infix("in", 110); +infix("instanceof", 110); +infix("<<", 120); +infix(">>", 120); +infix(">>>", 120); +infix("+", 130); +infix("-", 130); +infix("*", 140); +infix("/", 140); +infix("%", 140); +infixr("**", 150); +infix("(", 160, function (left) { + const the_paren = token; + let the_argument; + if (left.id !== "function") { + left_check(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (next_token.id !== ")") { + (function next() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + the_paren.free = true; + if (the_argument.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + the_paren.free = false; + } + return the_paren; +}); +infix(".", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("[", 170, function (left) { + const the_token = token; + const the_subscript = expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + const name = survey(the_subscript); + if (rx_identifier.test(name)) { + warn("subscript_a", the_subscript, name); + } + } + left_check(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; +}); +infix("=>", 170, function (left) { + return stop("wrap_parameter", left); +}); + +function do_tick() { + const the_tick = token; + the_tick.value = []; + the_tick.expression = []; + if (next_token.id !== "`") { + (function part() { + advance("(string)"); + the_tick.value.push(token); + if (next_token.id === "${") { + advance("${"); + the_tick.expression.push(expression(0)); + advance("}"); + return part(); + } + }()); + } + advance("`"); + return the_tick; +} + +infix("`", 160, function (left) { + const the_tick = do_tick(); + left_check(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; +}); + +post("++"); +post("--"); +pre("++"); +pre("--"); + +prefix("+"); +prefix("-"); +prefix("~"); +prefix("!"); +prefix("!!"); +prefix("[", function () { + const the_token = token; + the_token.expression = []; + if (next_token.id !== "]") { + (function next() { + let element; + let ellipsis = false; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + element = expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]"); + return the_token; +}); +prefix("/=", function () { + stop("expected_a_b", token, "/\\=", "/="); +}); +prefix("=>", function () { + return stop("expected_a_before_b", token, "()", "=>"); +}); +prefix("new", function () { + const the_new = token; + const right = expression(160); + if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "()", artifact(next_token)); + } + the_new.expression = right; + return the_new; +}); +prefix("typeof"); +prefix("void", function () { + const the_void = token; + warn("unexpected_a", the_void); + the_void.expression = expression(0); + return the_void; +}); + +function parameter_list() { + let complex = false; + const list = []; + let optional; + const signature = ["("]; + if (next_token.id !== ")" && next_token.id !== "(end)") { + (function parameter() { + let ellipsis = false; + let param; + if (next_token.id === "{") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("{"); + signature.push("{"); + (function subparameter() { + let subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (next_token.id === ":") { + advance(":"); + advance(); + token.label = subparam; + subparam = token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + } + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return subparameter(); + } + }()); + list.push(param); + advance("}"); + signature.push("}"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else if (next_token.id === "[") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("["); + signature.push("[]"); + (function subparameter() { + const subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + return subparameter(); + } + }()); + list.push(param); + advance("]"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else { + if (next_token.id === "...") { + complex = true; + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + } + if (!next_token.identifier) { + return stop("expected_identifier_a"); + } + param = next_token; + list.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (next_token.id === "=") { + complex = true; + optional = param; + advance("="); + param.expression = expression(0); + } else { + if (optional !== undefined) { + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } + } + }()); + } + advance(")"); + signature.push(")"); + return [list, signature.join(""), complex]; +} + +function do_function(the_function) { + let name; + if (the_function === undefined) { + the_function = token; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + name = next_token; + enroll(name, "variable", true); + the_function.name = name; + name.init = true; + name.calls = empty(); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + if (next_token.identifier) { + name = next_token; + the_function.name = name; + advance(); + } else { + the_function.name = anon; + } + } + } else { + name = the_function.name; + } + the_function.level = functionage.level + 1; + if (mega_mode) { + warn("unexpected_a", the_function); + } + +// Don't make functions in loops. It is inefficient, and it can lead to scoping +// errors. + + if (functionage.loop > 0) { + warn("function_in_loop", the_function); + } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + the_function.context = empty(); + the_function.finally = 0; + the_function.loop = 0; + the_function.switch = 0; + the_function.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functions.push(the_function); + functionage = the_function; + if (the_function.arity !== "statement" && typeof name === "object") { + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// Parse the parameter list. + + advance("("); + token.free = false; + token.arity = "function"; + const pl = parameter_list(); + functionage.parameters = pl[0]; + functionage.signature = pl[1]; + functionage.complex = pl[2]; + functionage.parameters.forEach(function enroll_parameter(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + name.names.forEach(enroll_parameter); + } + }); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && next_token.line === token.line + ) { + return stop("unexpected_a", next_token); + } + if (next_token.id === "." || next_token.id === "[") { + warn("unexpected_a"); + } + +// Restore the previous context. + + functionage = stack.pop(); + return the_function; +} + +prefix("function", do_function); + +function fart(pl) { + if (next_token.id === ";") { + stop("wrap_assignment", token); + } + advance("=>"); + const the_fart = token; + the_fart.arity = "binary"; + the_fart.name = "=>"; + the_fart.level = functionage.level + 1; + functions.push(the_fart); + if (functionage.loop > 0) { + warn("function_in_loop", the_fart); + } + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + the_fart.context = empty(); + the_fart.finally = 0; + the_fart.loop = 0; + the_fart.switch = 0; + the_fart.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functionage = the_fart; + the_fart.parameters = pl[0]; + the_fart.signature = pl[1]; + the_fart.complex = true; + the_fart.parameters.forEach(function (name) { + enroll(name, "parameter", true); + }); + if (next_token.id === "{") { + warn("expected_a_b", the_fart, "function", "=>"); + the_fart.block = block("body"); + } else { + the_fart.expression = expression(0); + } + functionage = stack.pop(); + return the_fart; +} + +prefix("(", function () { + const the_paren = token; + let the_value; + const cadet = lookahead().id; + +// We can distinguish between a parameter list for => and a wrapped expression +// with one token of lookahead. + + if ( + next_token.id === ")" + || next_token.id === "..." + || (next_token.identifier && (cadet === "," || cadet === "=")) + ) { + the_paren.free = false; + return fart(parameter_list()); + } + the_paren.free = true; + the_value = expression(0); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + if (next_token.id === "=>") { + if (the_value.arity !== "variable") { + if (the_value.id === "{" || the_value.id === "[") { + warn("expected_a_before_b", the_paren, "function", "("); + return stop("expected_a_b", next_token, "{", "=>"); + } + return stop("expected_identifier_a", the_value); + } + the_paren.expression = [the_value]; + return fart([the_paren.expression, "(" + the_value.id + ")"]); + } + return the_value; +}); +prefix("`", do_tick); +prefix("{", function () { + const the_brace = token; + const seen = empty(); + the_brace.expression = []; + if (next_token.id !== "}") { + (function member() { + let extra; + let full; + let id; + let name = next_token; + let value; + advance(); + if ( + (name.id === "get" || name.id === "set") + && next_token.identifier + ) { + if (!option.getset) { + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + next_token.id; + name = next_token; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (next_token.id === "}" || next_token.id === ",") { + if (typeof extra === "string") { + advance("("); + } + value = expression(Infinity, true); + } else if (next_token.id === "(") { + value = do_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + advance("("); + } + advance(":"); + value = expression(0); + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + advance(":"); + value = expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (next_token.id === ",") { + advance(","); + return member(); + } + }()); + } + advance("}"); + return the_brace; +}); + +stmt(";", function () { + warn("unexpected_a", token); + return token; +}); +stmt("{", function () { + warn("naked_block", token); + return block("naked"); +}); +stmt("break", function () { + const the_break = token; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (next_token.identifier && token.line === next_token.line) { + the_label = functionage.context[next_token.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + warn( + (the_label !== undefined && the_label.dead) + ? "out_of_scope_a" + : "not_label_a" + ); + } else { + the_label.used += 1; + } + the_break.label = next_token; + advance(); + } + advance(";"); + return the_break; +}); + +function do_var() { + const the_statement = token; + const is_const = the_statement.id === "const"; + the_statement.names = []; + +// A program may use var or let, but not both. + + if (!is_const) { + if (var_mode === undefined) { + var_mode = the_statement.id; + } else if (the_statement.id !== var_mode) { + warn( + "expected_a_b", + the_statement, + var_mode, + the_statement.id + ); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + warn("var_switch", the_statement); + } + if (functionage.loop > 0 && the_statement.id === "var") { + warn("var_loop", the_statement); + } + (function next() { + if (next_token.id === "{" && the_statement.id !== "var") { + const the_brace = next_token; + the_brace.names = []; + advance("{"); + (function pair() { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + survey(name); + advance(); + if (next_token.id === ":") { + advance(":"); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + next_token.label = name; + the_brace.names.push(next_token); + enroll(next_token, "variable", is_const); + advance(); + } else { + the_brace.names.push(name); + enroll(name, "variable", is_const); + } + if (next_token.id === ",") { + advance(","); + return pair(); + } + }()); + advance("}"); + advance("="); + the_brace.expression = expression(0); + the_statement.names.push(the_brace); + } else if (next_token.id === "[" && the_statement.id !== "var") { + const the_bracket = next_token; + the_bracket.names = []; + advance("["); + (function element() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + advance(); + the_bracket.names.push(name); + enroll(name, "variable", the_statement.id === "const"); + if (ellipsis) { + name.ellipsis = true; + } else if (next_token.id === ",") { + advance(","); + return element(); + } + }()); + advance("]"); + advance("="); + the_bracket.expression = expression(0); + the_statement.names.push(the_bracket); + } else if (next_token.identifier) { + const name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", is_const); + if (next_token.id === "=" || is_const) { + advance("="); + name.init = true; + name.expression = expression(0); + } + the_statement.names.push(name); + } else { + return stop("expected_identifier_a", next_token); + } + if (next_token.id === ",") { + if (!option.multivar) { + warn("expected_a_b", next_token, ";", ","); + } + advance(","); + return next(); + } + }()); + the_statement.open = ( + the_statement.names.length > 1 + && the_statement.line !== the_statement.names[1].line + ); + semicolon(); + return the_statement; +} + +stmt("const", do_var); +stmt("continue", function () { + const the_continue = token; + if (functionage.loop < 1 || functionage.finally > 0) { + warn("unexpected_a", the_continue); + } + not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; +}); +stmt("debugger", function () { + const the_debug = token; + if (!option.devel) { + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; +}); +stmt("delete", function () { + const the_token = token; + const the_value = expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; +}); +stmt("do", function () { + const the_do = token; + not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; +}); +stmt("export", function () { + const the_export = token; + let the_id; + let the_name; + let the_thing; + + function export_id() { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + the_id = next_token.id; + the_name = global.context[the_id]; + if (the_name === undefined) { + warn("unexpected_a"); + } else { + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a"); + } + exports[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + } + + the_export.expression = []; + if (next_token.id === "default") { + if (exports.default !== undefined) { + warn("duplicate_a"); + } + advance("default"); + the_thing = expression(0); + semicolon(); + exports.default = the_thing; + the_export.expression.push(the_thing); + } else { + if (next_token.id === "function") { + the_thing = statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a", the_name); + } + exports[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + warn("unexpected_a"); + } else if (next_token.id === "{") { + advance("{"); + (function loop() { + export_id(); + if (next_token.id === ",") { + advance(","); + return loop(); + } + }()); + advance("}"); + semicolon(); + } else { + export_id(); + if (the_name.writable !== true) { + warn("unexpected_a", token); + } + semicolon(); + } + } + module_mode = true; + return the_export; +}); +stmt("for", function () { + let first; + const the_for = token; + if (!option.for) { + warn("unexpected_a", the_for); + } + not_top_level(the_for); + functionage.loop += 1; + advance("("); + token.free = true; + if (next_token.id === ";") { + return stop("expected_a_b", the_for, "while (", "for (;"); + } + if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + return stop("unexpected_a"); + } + first = expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = expression(0); + advance(";"); + the_for.inc = expression(0); + if (the_for.inc.id === "++") { + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; +}); +stmt("function", do_function); +stmt("if", function () { + let the_else; + const the_if = token; + the_if.expression = condition(); + the_if.block = block(); + if (next_token.id === "else") { + advance("else"); + the_else = token; + the_if.else = ( + next_token.id === "if" + ? statement() + : block() + ); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + the_if.disrupt = true; + } else { + warn("unexpected_a", the_else); + } + } + } + return the_if; +}); +stmt("import", function () { + const the_import = token; + let name; + if (typeof module_mode === "object") { + warn("unexpected_directive_a", module_mode, module_mode.directive); + } + module_mode = true; + if (next_token.identifier) { + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + const names = []; + advance("{"); + if (next_token.id !== "}") { + while (true) { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (next_token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token; + if (!rx_module.test(token.value)) { + warn("bad_module_name_a", token); + } + froms.push(token.value); + semicolon(); + return the_import; +}); +stmt("let", do_var); +stmt("return", function () { + const the_return = token; + not_top_level(the_return); + if (functionage.finally > 0) { + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (next_token.id !== ";" && the_return.line === next_token.line) { + the_return.expression = expression(10); + } + advance(";"); + return the_return; +}); +stmt("switch", function () { + let dups = []; + let last; + let stmts; + const the_cases = []; + let the_disrupt = true; + const the_switch = token; + not_top_level(the_switch); + if (functionage.finally > 0) { + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token.free = true; + the_switch.expression = expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + (function major() { + const the_case = next_token; + the_case.arity = "statement"; + the_case.expression = []; + (function minor() { + advance("case"); + token.switch = true; + const exp = expression(0); + if (dups.some(function (thing) { + return are_similar(thing, exp); + })) { + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (next_token.id === "case") { + return minor(); + } + }()); + stmts = statements(); + if (stmts.length < 1) { + warn("expected_statements_a"); + return; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "break;", + artifact(next_token) + ); + } + if (next_token.id === "case") { + return major(); + } + }()); + dups = undefined; + if (next_token.id === "default") { + const the_default = next_token; + advance("default"); + token.switch = true; + advance(":"); + the_switch.else = statements(); + if (the_switch.else.length < 1) { + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + const the_last = the_switch.else[the_switch.else.length - 1]; + if (the_last.id === "break" && the_last.label === undefined) { + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; +}); +stmt("throw", function () { + const the_throw = token; + the_throw.disrupt = true; + the_throw.expression = expression(10); + semicolon(); + if (functionage.try > 0) { + warn("unexpected_a", the_throw); + } + return the_throw; +}); +stmt("try", function () { + let the_catch; + let the_disrupt; + const the_try = token; + if (functionage.try > 0) { + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (next_token.id === "catch") { + let ignored = "ignore"; + the_catch = next_token; + the_try.catch = the_catch; + advance("catch"); + advance("("); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + if (next_token.id !== "ignore") { + ignored = undefined; + the_catch.name = next_token; + enroll(next_token, "exception", true); + } + advance(); + advance(")"); + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "catch", + artifact(next_token) + ); + + } + if (next_token.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; +}); +stmt("var", do_var); +stmt("while", function () { + const the_while = token; + not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; +}); +stmt("with", function () { + stop("unexpected_a", token); +}); + +ternary("?", ":"); + +// Ambulation of the parse tree. + +function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; +} + +function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all of the +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + } + }; +} + +const posts = empty(); +const pres = empty(); +const preaction = action(pres); +const postaction = action(posts); +const preamble = amble(pres); +const postamble = amble(posts); + +function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.id === "function") { + walk_statement(thing.block); + } + if (thing.arity === "pre" || thing.arity === "post") { + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + warn("unexpected_statement_a", thing); + } + postamble(thing); + } + } +} + +function walk_statement(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_statement); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + ) { + warn( + (thing.id === "(string)" && thing.value === "use strict") + ? "unexpected_a" + : "unexpected_expression_a", + thing + ); + } + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + } +} + +function lookup(thing) { + if (thing.arity === "variable") { + +// Look up the variable in the current context. + + let the_variable = functionage.context[thing.id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable === undefined) { + stack.forEach(function (outer) { + const a_variable = outer.context[thing.id]; + if ( + a_variable !== undefined + && a_variable.role !== "label" + ) { + the_variable = a_variable; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (the_variable === undefined) { + if (declared_globals[thing.id] === undefined) { + warn("undeclared_a", thing); + return; + } + the_variable = { + dead: false, + parent: global, + id: thing.id, + init: true, + role: "variable", + used: 0, + writable: false + }; + global.context[thing.id] = the_variable; + } + the_variable.closure = true; + functionage.context[thing.id] = the_variable; + } else if (the_variable.role === "label") { + warn("label_a", thing); + } + if ( + the_variable.dead + && ( + the_variable.calls === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + ) { + warn("out_of_scope_a", thing); + } + return the_variable; + } +} + +function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); +} + +function preaction_function(thing) { + if (thing.arity === "statement" && blockage.body !== true) { + warn("unexpected_a", thing); + } + if (thing.level === 1) { + if ( + module_mode === true + || global.strict !== undefined + || thing.complex + ) { + if (thing.id !== "=>" && thing.block.strict !== undefined) { + warn("unexpected_a", thing.block.strict); + } + } else { + if (thing.block.strict === undefined) { + warn("use_strict", thing); + } + } + } + stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); +} + +function bitwise_check(thing) { + if (!option.bitwise && bitwiseop[thing.id] === true) { + warn("unexpected_a", thing); + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + warn("unexpected_a", thing); + } +} + +function pop_block() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); +} + +function activate(name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.init = true; + } + } + blockage.live.push(name); +} + +function action_var(thing) { + thing.names.forEach(activate); +} + +preaction("assignment", bitwise_check); +preaction("binary", bitwise_check); +preaction("binary", function (thing) { + if (relationop[thing.id] === true) { + const left = thing.expression[0]; + const right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + warn("expected_string_a", right); + } + } else { + const value = right.value; + if (value === "null" || value === "undefined") { + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + warn("expected_type_string_a", right, value); + } + } + } + } +}); +preaction("binary", "==", function (thing) { + warn("expected_a_b", thing, "===", "=="); +}); +preaction("binary", "!=", function (thing) { + warn("expected_a_b", thing, "!==", "!="); +}); +preaction("binary", "=>", preaction_function); +preaction("binary", "||", function (thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + warn("and", thang); + } + }); +}); +preaction("binary", "(", function (thing) { + const left = thing.expression[0]; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + const parent = functionage.name.parent; + if (parent) { + const left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + && left_variable.dead + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } +}); +preaction("binary", "in", function (thing) { + warn("infix_in", thing); +}); +preaction("binary", "instanceof", function (thing) { + warn("unexpected_a", thing); +}); +preaction("binary", ".", function (thing) { + if (thing.expression.new) { + thing.new = true; + } +}); +preaction("statement", "{", function (thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; +}); +preaction("statement", "for", function (thing) { + if (thing.name !== undefined) { + const the_variable = lookup(thing.name); + if (the_variable !== undefined) { + the_variable.init = true; + if (!the_variable.writable) { + warn("bad_assignment_a", thing.name); + } + } + } + walk_statement(thing.initial); +}); +preaction("statement", "function", preaction_function); +preaction("unary", "~", bitwise_check); +preaction("unary", "function", preaction_function); +preaction("variable", function (thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } +}); + +function init_variable(name) { + const the_variable = lookup(name); + if (the_variable !== undefined) { + if (the_variable.writable) { + the_variable.init = true; + return; + } + } + warn("bad_assignment_a", name); +} + +postaction("assignment", "+=", function (thing) { + let right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } +}); +postaction("assignment", function (thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + if (thing.id === "=") { + if (thing.names !== undefined) { + if (Array.isArray(thing.names)) { + thing.names.forEach(init_variable); + } else { + init_variable(thing.names); + } + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.writable !== true) { + warn("bad_assignment_a", lvalue); + } + } + const right = syntax[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + warn("unexpected_a", thing.expression[1]); + } + } +}); + +function postaction_function(thing) { + delete functionage.finally; + delete functionage.loop; + delete functionage.switch; + delete functionage.try; + functionage = stack.pop(); + if (thing.wrapped) { + warn("unexpected_parens", thing); + } + return pop_block(); +} + +postaction("binary", function (thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || are_similar(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option.convert) { + if (thing.expression[0].value === "") { + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === ".") { + if (thing.expression.id === "RegExp") { + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } +}); +postaction("binary", "&&", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "||", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "=>", postaction_function); +postaction("binary", "(", function (thing) { + let left = thing.expression[0]; + let the_new; + let arg; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option.eval) { + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + warn( + "expected_a_before_b", + left, + "new", + artifact(left) + ); + } + } + } else if (left.id === ".") { + let cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + cack = !cack; + } + if (rx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + warn("unexpected_a", the_new); + } else { + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + const paren = left.expression; + if (paren.id === "(") { + const array = paren.expression; + if (array.length === 1) { + const new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } +}); +postaction("binary", "[", function (thing) { + if (thing.expression[0].id === "RegExp") { + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + warn("weird_expression_a", thing.expression[1]); + } +}); +postaction("statement", "{", pop_block); +postaction("statement", "const", action_var); +postaction("statement", "export", top_level_only); +postaction("statement", "for", function (thing) { + walk_statement(thing.inc); +}); +postaction("statement", "function", postaction_function); +postaction("statement", "import", function (the_thing) { + const name = the_thing.name; + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return top_level_only(the_thing); +}); +postaction("statement", "let", action_var); +postaction("statement", "try", function (thing) { + if (thing.catch !== undefined) { + const the_name = thing.catch.name; + if (the_name !== undefined) { + const the_variable = functionage.context[the_name.id]; + the_variable.dead = false; + the_variable.init = true; + } + walk_statement(thing.catch.block); + } +}); +postaction("statement", "var", action_var); +postaction("ternary", function (thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || are_similar(thing.expression[1], thing.expression[2]) + ) { + warn("unexpected_a", thing); + } else if (are_similar(thing.expression[0], thing.expression[1])) { + warn("expected_a_b", thing, "||", "?"); + } else if (are_similar(thing.expression[0], thing.expression[2])) { + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + warn("wrap_condition", thing.expression[0]); + } +}); +postaction("unary", function (thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option.convert) { + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } +}); +postaction("unary", "function", postaction_function); +postaction("unary", "+", function (thing) { + if (!option.convert) { + warn("expected_a_b", thing, "Number(...)", "+"); + } + const right = thing.expression; + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } +}); + +function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + if (id !== "ignore") { + const name = the_function.context[id]; + if (name.parent === the_function) { + if ( + name.used === 0 + && ( + name.role !== "function" + || name.parent.arity !== "unary" + ) + ) { + warn("unused_a", name); + } else if (!name.init) { + warn("uninitialized_a", name); + } + } + } + }); +} + +function uninitialized_and_unused() { + +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (module_mode === true || option.node) { + delve(global); + } + functions.forEach(delve); +} + +// Go through the token list, looking at usage of whitespace. + +function whitage() { + let closer = "(end)"; + let free = false; + let left = global; + let margin = 0; + let nr_comments_skipped = 0; + let open = true; + let right; + + function expected_at(at) { + warn( + "expected_a_at_b_c", + right, + artifact(right), + fudge + at, + artifact_column(right) + ); + } + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function no_space() { + if (left.line === right.line) { + if (left.thru !== right.from && nr_comments_skipped === 0) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (open) { + const at = ( + free + ? margin + : margin + 8 + ); + if (right.from < at) { + expected_at(at); + } + } else { + if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (free) { + if (right.from < margin) { + expected_at(margin); + } + } else { + const mislaid = ( + stack.length > 0 + ? stack[stack.length - 1].right + : undefined + ); + if (!open && mislaid !== undefined) { + warn( + "expected_a_next_at_b", + mislaid, + artifact(mislaid.id), + margin + 4 + fudge + ); + } else if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + stack = []; + tokens.forEach(function (the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + + const new_closer = opener[left.id]; + if (typeof new_closer === "string") { + if (new_closer !== right.id) { + stack.push({ + closer: closer, + free: free, + margin: margin, + open: open, + right: right + }); + closer = new_closer; + if (left.line !== right.line) { + free = closer === ")" && left.free; + open = true; + margin += 4; + if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (right.switch) { + at_margin(-4); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + free = false; + open = false; + no_space_only(); + } + } else { + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like {]) have already been detected. + + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } + } else { + +// If right is a closer, then pop the previous state. + + if (right.id === closer) { + const previous = stack.pop(); + margin = previous.margin; + if (open && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + closer = previous.closer; + free = previous.free; + open = previous.open; + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-4); + } else if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + one_space(); + } else { + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + at_margin(0); + } else { + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + no_space(); + } else if ( + left.id === "." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + ) { + no_space_only(); + } else if (right.id === ".") { + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } else if (left.id === ";") { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + one_space_only(); + } else if (right.statement === true) { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.id === "var" + || left.id === "const" + || left.id === "let" + ) { + stack.push({ + closer: closer, + free: free, + margin: margin, + open: open + }); + closer = ";"; + free = false; + open = left.open; + if (open) { + margin = margin + 4; + at_margin(0); + } else { + one_space_only(); + } + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +// The jslint function itself. + +export default function jslint( + source = "", + option_object = empty(), + global_array = [] +) { + try { + warnings = []; + option = Object.assign(empty(), option_object); + anon = "anonymous"; + block_stack = []; + declared_globals = empty(); + directive_mode = true; + directives = []; + early_stop = true; + exports = empty(); + froms = []; + fudge = ( + option.fudge + ? 1 + : 0 + ); + functions = []; + global = { + id: "(global)", + body: true, + context: empty(), + from: 0, + level: 0, + line: 0, + live: [], + loop: 0, + switch: 0, + thru: 0 + }; + blockage = global; + functionage = global; + json_mode = false; + mega_mode = false; + module_mode = false; + next_token = global; + property = empty(); + stack = []; + tenure = undefined; + token = global; + token_nr = 0; + var_mode = undefined; + populate(standard, declared_globals, false); + populate(global_array, declared_globals, false); + Object.keys(option).forEach(function (name) { + if (option[name] === true) { + const allowed = allowed_option[name]; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } + }); + tokenize(source); + advance(); + if (json_mode) { + tree = json_value(); + advance("(end)"); + } else { + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option.browser) { + if (next_token.id === ";") { + advance(";"); + } + } else { + +// If we are not in a browser, then the file form of strict pragma may be used. + + if ( + next_token.value === "use strict" + ) { + global.strict = next_token; + advance("(string)"); + advance(";"); + } + } + tree = statements(); + advance("(end)"); + functionage = global; + walk_statement(tree); + if (module_mode && global.strict !== undefined) { + warn("unexpected_a", global.strict); + } + if (warnings.length === 0) { + uninitialized_and_unused(); + if (!option.white) { + whitage(); + } + } + } + if (!option.browser) { + directives.forEach(function (comment) { + if (comment.directive === "global") { + warn("missing_browser", comment); + } + }); + } + early_stop = false; + } catch (e) { + if (e.name !== "JSLintError") { + warnings.push(e); + } + } + return { + directives, + edition: "2018-09-13", + exports, + froms, + functions, + global, + id: "(JSLint)", + json: json_mode, + lines, + module: module_mode === true, + ok: warnings.length === 0 && !early_stop, + option, + property, + stop: early_stop, + tokens, + tree, + warnings: warnings.sort(function (a, b) { + return a.line - b.line || a.column - b.column; + }) + }; +}; diff --git a/branch.ignore_shebang/report.js b/branch.ignore_shebang/report.js new file mode 100644 index 000000000..f123e9055 --- /dev/null +++ b/branch.ignore_shebang/report.js @@ -0,0 +1,221 @@ +// report.js +// 2018-07-29 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Generate JSLint HTML reports. + +/*property + closure, column, context, edition, error, exports, filter, forEach, froms, + fudge, function, functions, global, id, isArray, join, json, keys, length, + level, line, lines, message, module, name, names, option, parameters, + parent, property, push, replace, role, signature, sort, stop, warnings +*/ + +const rx_amp = /&/g; +const rx_gt = />/g; +const rx_lt = / with less destructive entities. + + return String( + string + ).replace( + rx_amp, + "&" + ).replace( + rx_lt, + "<" + ).replace( + rx_gt, + ">" + ); +} + +export default { + error: function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + let fudge = Number(Boolean(data.option.fudge)); + let output = []; + if (data.stop) { + output.push("
    JSLint was unable to finish.
    "); + } + data.warnings.forEach(function (warning) { + output.push( + "
    ", + entityify(warning.line + fudge), + ".", + entityify(warning.column + fudge), + "
    ", + entityify(warning.message), + "
    ", + entityify(data.lines[warning.line] || ""), + "" + ); + }); + return output.join(""); + }, + + function: function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + let fudge = Number(Boolean(data.option.fudge)); + let mode = ( + data.module + ? "module" + : "global" + ); + let output = []; + + if (data.json) { + return ( + data.warnings.length === 0 + ? "
    JSON: good.
    " + : "
    JSON: bad.
    " + ); + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + let global = Object.keys(data.global.context).sort(); + let froms = data.froms.sort(); + let exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + let context = the_function.context; + let list = Object.keys(context); + output.push( + "
    ", + entityify(the_function.line + fudge), + "
    ", + ( + the_function.name === "=>" + ? entityify(the_function.signature) + " =>" + : ( + typeof the_function.name === "string" + ? "«" + entityify(the_function.name) + "»" + : "" + entityify(the_function.name.id) + "" + ) + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + let params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.parent === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.parent === the_function + ); + })); + detail("outer", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.parent !== the_function + && the_variable.parent.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].parent.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + output.push( + "
    JSLint edition ", + entityify(data.edition), + "
    " + ); + return output.join(""); + }, + + property: function property_directive(data) { + +// Produce the /*property*/ directive. + + let not_first = false; + let output = ["/*property"]; + let length = 1111; + let properties = Object.keys(data.property); + + if (properties.length > 0) { + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length >= 80) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); + } + } +}; diff --git a/branch.jslint_pass_itself_without_long_option/README b/branch.jslint_pass_itself_without_long_option/README new file mode 100644 index 000000000..f46e91069 --- /dev/null +++ b/branch.jslint_pass_itself_without_long_option/README @@ -0,0 +1,42 @@ +JSLint, The JavaScript Code Quality Tool + +Douglas Crockford +douglas@crockford.com + +2018-05-28 + +jslint.js contains the jslint function. It parses and analyzes a source file, +returning an object with information about the file. It can also take an object +that sets options. + +index.html runs the jslint.js function in a web page. The page also depends +browser.js and report.js and jslint.css. + +jslint.css provides styling for index.html. + +browser.js runs the web user interface. + +report.js generates the results reports in HTML. + +help.html describes JSLint's usage. Please read it. + +function.html describes the jslint function and the results it produces. + +Please direct questions and comments to +https://plus.google.com/communities/104441363299760713736. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[http://www.crockford.com/wrrrld/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. diff --git a/branch.jslint_pass_itself_without_long_option/browser.js b/branch.jslint_pass_itself_without_long_option/browser.js new file mode 100644 index 000000000..5086fed85 --- /dev/null +++ b/branch.jslint_pass_itself_without_long_option/browser.js @@ -0,0 +1,158 @@ +// browser.js +// 2018-06-16 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +/*jslint + browser +*/ + +/*property + checked, create, disable, display, error, focus, forEach, function, + getElementById, innerHTML, join, length, map, onchange, onclick, onscroll, + property, querySelectorAll, scrollTop, select, split, style, title, value +*/ + +import jslint from "./jslint.js"; +import report from "./report.js"; + +// This is the web script companion file for JSLint. It includes code for +// interacting with the browser and displaying the reports. + +let rx_crlf = /\n|\r\n?/; +let rx_separator = /[\s,;'"]+/; + +let fudge_unit = 0; + +const aux = document.getElementById("JSLINT_AUX"); +const boxes = document.querySelectorAll("[type=checkbox]"); +const fudge = document.getElementById("JSLINT_FUDGE"); +const global = document.getElementById("JSLINT_GLOBAL"); +const number = document.getElementById("JSLINT_NUMBER"); +const property = document.getElementById("JSLINT_PROPERTY"); +const property_fieldset = document.getElementById("JSLINT_PROPERTYFIELDSET"); +const report_field = document.getElementById("JSLINT_REPORT"); +const report_list = document.getElementById("JSLINT_REPORT_LIST"); +const source = document.getElementById("JSLINT_SOURCE"); +const select = document.getElementById("JSLINT_SELECT"); +const warnings = document.getElementById("JSLINT_WARNINGS"); +const warnings_list = document.getElementById("JSLINT_WARNINGS_LIST"); + +function show_numbers() { + number.value = source.value.split(rx_crlf).map(function (ignore, index) { + return index + fudge_unit; + }).join("\n"); +} + +function clear() { + aux.style.display = "none"; + number.value = ""; + property.value = ""; + property_fieldset.style.display = "none"; + report_field.style.display = "none"; + report_list.innerHTML = ""; + source.focus(); + source.value = ""; + warnings.style.display = "none"; + warnings_list.innerHTML = ""; +} + +function fudge_change() { + fudge_unit = Number(fudge.checked); + show_numbers(); +} + +function clear_options() { + boxes.forEach(function (node) { + node.checked = false; + }); + fudge_change(); + global.innerHTML = ""; +} + +function call_jslint() { + +// First build the option object. + + let option = Object.create(null); + boxes.forEach(function (node) { + if (node.checked) { + option[node.title] = true; + } + }); + +// Call JSLint with the source text, the options, and the predefined globals. + + let global_string = global.value; + let result = jslint( + source.value, + option, + ( + global_string === "" + ? undefined + : global_string.split(rx_separator) + ) + ); + +// Generate the reports. + + let error_html = report.error(result); + let function_html = report.function(result); + let property_text = report.property(result); + +// Display the reports. + + warnings_list.innerHTML = error_html; + warnings.style.display = ( + error_html.length === 0 + ? "none" + : "block" + ); + + report_list.innerHTML = function_html; + report_field.style.display = "block"; + if (property_text) { + property.value = property_text; + property_fieldset.style.display = "block"; + property.scrollTop = 0; + select.disable = false; + } else { + property_fieldset.style.display = "none"; + select.disable = true; + } + aux.style.display = "block"; + source.select(); +} + +fudge.onchange = fudge_change; + +source.onchange = function (ignore) { + show_numbers(); +}; + +source.onscroll = function () { + let ss = source.scrollTop; + number.scrollTop = ss; + let sn = number.scrollTop; + if (ss > sn) { + show_numbers(); + number.scrollTop = ss; + } +}; + +document.querySelectorAll("[name='JSLint']").forEach(function (node) { + node.onclick = call_jslint; +}); + +document.querySelectorAll("[name='clear']").forEach(function (node) { + node.onclick = clear; +}); + +document.getElementById("JSLINT_SELECT").onclick = function () { + property.focus(); + property.select(); +}; +document.getElementById("JSLINT_CLEAR_OPTIONS").onclick = clear_options; + +fudge_change(); +source.select(); +source.focus(); diff --git a/branch.jslint_pass_itself_without_long_option/function.html b/branch.jslint_pass_itself_without_long_option/function.html new file mode 100644 index 000000000..8d7287cbc --- /dev/null +++ b/branch.jslint_pass_itself_without_long_option/function.html @@ -0,0 +1,612 @@ + + + + + + + + +JSLint: jslint function + + + +
    JSLint
    + +
    The jslint Function
    +
    +

    JSLint

    +

    JSLint is delivered as a set of files:

    +
    + + + + + + + + + + + + + + +
    FilenameContent
    browser.jsBrowser based UI.
    function.htmlThis page that you are looking at right now.
    help.htmlUsing jslint as a single page JSLint application.
    index.htmlSingle page JSLint application that uses the + js files.
    jslint.jsThe jslint function.
    report.jsThe report object, containing report generator functions for + HTML.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    +
    + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring"(JSLint)"
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    parenttokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    parenttokenThe function in which the variable was declared.variable
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable can be assigned to.variable
    +

    Reports

    +

    The report object contains three functions that all take a + result from the jslint function as input. + report.js has no other dependence on other files.

    + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    +
    + +
    + + + + + +
    + + diff --git a/branch.jslint_pass_itself_without_long_option/help.html b/branch.jslint_pass_itself_without_long_option/help.html new file mode 100644 index 000000000..ee236ff95 --- /dev/null +++ b/branch.jslint_pass_itself_without_long_option/help.html @@ -0,0 +1,841 @@ + + + + + + + + + +JSLint: Help + + + +
    JSLint
    + +
    Help
    +
    +
    Il semble que la perfection soit atteinte non quand il n’y a + plus rien à ajouter, mais quand il n’y a plus rien à + retrancher.
    +
    + Antoine de Saint-Exupéry +
    +
    + Terre des Hommes (1939) +
    +

    Good Parts

    +

    The Principle of the Good Parts is

    +
    If a feature is sometimes useful and sometimes dangerous and if + there is a better option then always use the better option.
    +

    JSLint is a JavaScript program that looks for problems in + JavaScript programs. It is a code quality tool.

    +

    When C was + a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    +

    As the language matured, the definition of the language was + strengthened to eliminate some insecurities, and compilers got better + at issuing warnings. lint is no longer needed.

    +

    JavaScript is a + young-for-its-age language. It was originally intended to do small tasks in + webpages, tasks for which Java was too heavy and clumsy. But JavaScript is + a surprisingly capable language, and it is now being used in larger + projects. Many of the features that were intended to make the language easy + to use are troublesome when projects become complicated. A lint + for JavaScript is needed: JSLint, a JavaScript syntax checker + and validator.

    +

    JSLint takes a JavaScript source and scans it. If it finds a + problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by the ECMAScript Programming Language + Standard (the strangely named document that governs JavaScript). + JSLint will reject most legal programs. It is a higher + standard.

    +

    JavaScript is a sloppy language, but hidden deep inside there is an elegant, + better language. JSLint helps you to program in that better + language and to avoid most of the slop. JSLint will reject + programs that browsers will accept because JSLint is concerned + with the quality of your code and browsers are not. You should gladly accept + all of JSLint's advice.

    +

    JSLint can operate on JavaScript source + or JSON text.

    +

    ECMAScript Sixth Edition

    +

    Some of + ES6’s features are good, so JSLint will recognize the good + parts of ES6.

    +

    Currently, these features are recognized:

    +
      +
    • The ... ellipsis marker in parameter + lists and argument lists, replacing the arguments object + for variadic functions.
    • +
    • The let statement, which is like the var + statement except that it respects block scope. + You may use let or var but not both.
    • +
    • The const statement is like the let statement + except that it disallows the use of assignment on the variable, although + if the value of the variable is mutable, it can still be mutated. + const is preferred to let. +
    • Destructuring of arrays and objects is allowed in parameter lists and on + the left side of let, and const, but not + var or assignment statements, and not deep destructuring + or eliding.
    • +
    • Enhanced object literals, providing shorter forms for function + declaration and properties that are initialized by variables with the + same name.
    • +
    • The fat arrow => fart functions.
    • +
    • The simplest forms of import and export.
    • +
    • `Megastring` literals, but not nested + `megastring` literals.
    • +
    • New global functions, such as Map, Set, + WeakMap, and WeakSet.
    • +
    • 0b- and 0o- number literals.
    • +
    +

    The most important new feature of ES6 is proper tail calls. This has no + new syntax, so JSLint doesn’t see it. But it makes recursion + much more attractive, which makes loops, particularly for + loops, much less attractive.

    +

    import export

    +

    The ES6 module feature will be an important improvement over JavaScript’s + global variables as a means of linking separate files together. + JSLint recognizes a small but essential subset of the module + syntax.

    +
    +

    import name from stringliteral;

    +

    import {name} from stringliteral;

    +

    export default function () {};

    +

    export default function name() {};

    +

    export default expression;

    +

    export function name() {}

    +

    export name; // where name is const

    +

    export {name};

    +
    +

    Directives

    +

    JSLint provides three directives that may be placed in a + file to manage JSLint’s behavior. Use of these directives is + optional. If they are used, they should be placed in a source file before + the first statement. They are written in the form of a comment, where the + directive name is placed immediately after the opening of the comment before + any whitespace. The three directives are global, + jslint, and property. Directives in a file are + stronger than options selected from the UI or passed with the option object.

    +

    /*global*/

    +

    The /*global*/ directive is used to specify a set of globals + (usually functions and objects containing functions) that are available to + this file. This was commonly used in browsers to link source files together + before ES6 modules appeared. Use of global variables is strongly + discouraged, but unfortunately web browsers require their use. The + /*global*/ directive can only be used when the + Assume a browser option is selected. +

    Each of the names listed will indicate a read-only global variable. The names + are separated by , comma. This directive + should not be used if import or export is used. + This directive inhibits warnings, but it does not declare the names in the + execution environment. +

    For example: +

    /*global +
    ADSAFE, report, jslint
    +*/
    +

    instructs JSLint to not give warnings about the global + variables ADsafe, report, and jslint. + However, if any of those names are expected to be supplied by other files + and those other files fail to do so, then execution errors will result. It + is usually better to use top-level variable declarations instead: +

        var ADSAFE;
    +    var report;
    +    var jslint; 
    +

    Using var in this way allows comparing a global variable to the + undefined value to determine whether it is has been used in the global context.

    +

    /*jslint*/

    +

    The /*jslint*/ directive allows for the control of several + options. These options can also be set from the + JSLint.com user interface.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Tolerate bitwise operatorsbitwisetrue if bitwise operators should be allowed. The + bitwise operators are rarely used in JavaScript programs, so it + is usually more likely that & is a mistyping of + && than that it indicates a bitwise and. + JSLint will give warnings on the bitwise operators + unless this option is selected.
    Assume a browser browsertrue if the standard browser globals should be + predefined. This option will reject the use of + import and export. This option also + disallows the file form of the "use + strict" pragma. It does not supply + self; you will have to request that unnecessary + alias of the dreaded global object yourself. It adds the same + globals as this directive: +
    /*global +
    + clearInterval, clearTimeout, document, event, FileReader, + FormData, history, localStorage, location, name, navigator, + screen, sessionStorage, setInterval, setTimeout, Storage, + URL, window, XMLHttpRequest +
    + */
    +
    Tolerate conversion operatorsconverttrue if !!, + prefix, + and concatenation with "" are allowed. + The Boolean, Number, and + String functions are preferred
    Assume CouchDBcouchtrue if + Couch DB + globals should be predefined. It adds the same globals as this + directive: +
    /*global +
    emit, getRow, isArray, log, provides, registerType, require, + send, start, sum, toJSON
    + */
    Assume in developmentdeveltrue if browser globals that are useful in + development should be predefined, and if debugger + statements and TODO comments + should be allowed. It adds the + same globals as this directive: +
    /*global +
    alert, confirm, console, prompt
    + */
    Be sure to turn this option off before going + into production.
    Tolerate evalevaltrue if eval should be allowed. In the + past, the eval function was the most misused + feature of the language.
    Tolerate for statementfortrue if the for statement should be + allowed. It is almost always better to use the array methods + instead.
    Fudge the line and column numbersfudgetrue if the origin of a text file is line 1 column + 1. Programmers know that number systems start at zero. + Unfortunately, most text editors start numbering lines and + columns at one. JSLint will by default start + numbering at zero. Use this option to start the numbering at one + in its reports.
    Tolerate get and setgetsettrue if accessor properties are allowed in object literals.
    Tolerate long source lineslongtrue if a line can contain more than 80 characters.
    Tolerate multiple variables declarations per statementmultivartrue if a var, let, or + const statement can declare two or more variables + in a single statement.
    Assume Node.jsnodetrue if Node.js globals should be predefined. It + will predefine globals that are used in the Node.js environment. + It adds the same globals as this directive: +
    /*global +
    + Buffer, clearImmediate, clearInterval, clearTimeout, console, exports, + module, process, require, setImmediate, setInterval, setTimeout, URL, + URLSearchParams, __dirname, __filename +
    + */
    Tolerate single quote stringssingletrue if 'single quote + should be allowed to enclose string literals.
    Tolerate this
    thistrue if this should be allowed.
    Tolerate whitespace messwhitetrue if the whitespace rules should be ignored.
    +

    For example:

    +
    /*jslint
    +    bitwise, node
    +*/
    + +

    /*property*/

    +

    The /*property*/ directive is used to declare a list of + property identifiers that are used in the file. Each property name in the + program is looked up in this list. If a name is not found, that indicates an + error, most likely a typing error.

    +

    The list can also be used to evade some of JSLint’s naming + rules.

    +

    JSLint can build the /*property*/ list for you. At + the bottom of its report, JSLint displays a + /*property*/ directive. It contains all of the names that were + used with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. You + can copy the /*property*/ directive to the top of your + script file. JSLint will check the spelling of all property + names against the list. That way, you can have JSLint look for + misspellings for you.

    +

    For example,

    +
    /*property +
    charAt, slice, _$_
    + */
    +

    ignore

    +

    JSLint introduces a new reserved word: ignore. It + is used in parameter lists and in catch clauses to indicate a + parameter that will be ignored. Unused warnings will not be produced for + ignore. +

    function handler(ignore, value) {
    +    return do_something_useful(value);
    +}
    +

    var let const

    +

    JavaScript provides three statements for declaring variables: + var, let, and const. The + var statement suffers from a bad practice called hoisting. The + let statement does not do hoisting and respects block scope. + The const statement is like let except that it + marks the variable (but not its contents) as read only, making it an error + to attempt to assign to the variable. When given a choice, + const is the best, var is the worst.

    +

    JSLint uses the intersection of the var rules and the + let rules, and by doing so avoids the errors related to either. + A name should be declared only once in a function. It should be declared + before it is used. It should not be used outside of the block in which it is + declared. A variable should not have the same name as a variable or + parameter in an outer function. Do not mix var and + let. Declare one name per statement.

    +

    = == ===

    +

    Fortran made a terrible + mistake in using the equality operator as its assignment operator. That + mistake has been replicated in most languages since then. C compounded that + mistake by making == its equality operator. The visual + similarity is a source of errors. JavaScript compounded this further by + making == a type coercing comparison operator that produces + false positive results. This was mitigated by adding the === + operator, leaving the broken == operator in place.

    +

    JSLint attempts to minimize errors by the following rules:

    +

    == is not allowed. This avoids the false positives, and + increases the visual distance between = and ===. + Assignments are not allowed in expression position, and comparisons are not + allowed in statement position. This also reduces confusion. 

    +

    Semicolon

    +

    JavaScript uses a C-like syntax that requires the use of semicolons to + delimit certain statements. JavaScript attempts to make those semicolons + optional with an automatic semicolon insertion mechanism, but it does not + work very well. Automatic semicolon insertion was added to make + things easier for beginners. Unfortunately, it sometimes fails. Do not rely + on it unless you are a beginner.

    +

    JSLint expects that every statement will be followed by + ; except for for, function, + if, switch, try, and + while. JSLint does not expect to see unnecessary + semicolons, the empty statement, or empty blocks.

    +

    function =>

    +

    JavaScript has four syntactic forms for making function objects: function + statements, function expressions, enhanced object literals, and the + => fart operator.

    +

    The function statement creates a variable and assigns the function object to + it. It should be used in a file or function body, but not inside of a block.

    +
    function name(parameters) { +
    statements
    + }
    +

    The function expression unfortunately looks like the function statement. It + may appear anywhere that an expression may appear, but not in statement + position and not in a loop. It produces a function object but does not + create a variable in which to store it.

    +
    function (parameters) { +
    statements
    + }
    +

    The enhanced object literal provides an ES6 shorthand for creating a property + whose value is a function, saving you from having to type + : colon and function.

    +
    { +
    name(parameters) { +
    statements
    + }
    + }
    +

    Finally, ES6 provides an even shorter form of function expression that leaves + out the words function and return:

    +
    (parameters) => + expression
    +

    JSLint requires the parens around the parameters, and forbids a + { left brace after the + => fart to avoid syntactic ambiguity.

    +

    Comma

    +

    The , comma operator is unnecessary and can + mask programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as + an operator. It does not expect to see elided elements in array literals. A + comma should not appear after the last element of an array literal or object + literal.

    +

    Blocks

    +

    JSLint expects blocks with function, + if, switch, while, for, + do, and try statements and nowhere else.

    +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    +

    JavaScript allows an if to be written like this:

    +
    if (condition)
    +    statement;
    +

    That form is known to contribute to mistakes in projects where many + programmers are working on the same code. That is why JSLint + expects the use of a block:

    +
    if (condition) {
    +    statements;
    +}
    +

    Experience shows that this form is more resilient.

    +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call. All other expression statements are considered + to be errors.

    +

    for

    +

    JSLint does not recommend use of the for statement. + Use array methods like forEach instead. The for + option will suppress some warnings. The forms of for that + JSLint accepts are restricted, excluding the new ES6 forms.

    +

    for in

    +

    JSLint does not recommend use of the for + in statement. Use Object.keys instead.

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, it also + loops through all of the properties that were inherited through the + prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written + without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    for (name in object) {
    +    if (object.hasOwnProperty(name)) {
    +        ....
    +    }
    +}
    +

    Note that the above code will fail if the object contains a property + named hasOwnProperty. Use Object.keys instead.

    +

    switch

    +

    A common error in switch statements is to forget to place a + break statement after each case, resulting in unintended + fall-through. JSLint expects that the statement before the next + case or default is one of these: + break, return, or throw.

    +

    with

    +

    The with statement was intended to provide a shorthand in + accessing properties in deeply nested objects. Unfortunately, it behaves + very badly when setting new properties. Never use the with + statement.

    +

    JSLint does not expect to see a with statement.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that + labels will be distinct from vars and parameters.

    +

    Unreachable Code

    +

    JSLint expects that a return, break, + or throw statement will be followed by a + } right brace or case or + default.

    +

    Confusing Pluses and Minuses

    +

    JSLint expects that + will not be followed by + + or ++, and that - will not be + followed by - or --. A misplaced space can turn + + + into ++, an error that is difficult to see. + Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ increment and + -- decrement operators have been known to + contribute to bad code by encouraging excessive trickiness. They are second + only to faulty architecture in enabling to viruses and other security + menaces. Also, preincrement/postincrement confusion can produce off-by-one + errors that are extremely difficult to diagnose. Fortunately, they are also + complete unnecessary. There are better ways to add 1 to a variable.

    +

    It is best to avoid these operators entirely and rely on += and + -= instead.

    +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. + JSLint looks for problems that may cause portability problems. + It also attempts to resolve visual ambiguities by recommending explicit + escapement.

    +

    JavaScript’s syntax for regular expression literals overloads the + / slash character. To avoid ambiguity, + JSLint expects that the character preceding a regular + expression literal is a ( left paren or + = equal or + : colon or + , comma character.

    +

    this

    +
    Having this in the language makes it harder to talk + about the language. It is like pair programming with Abbott and Costello. +
    +

    Avoid using this. Warnings about this can be + suppressed with option.this.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the + new prefix. The new prefix creates a new object + based on the function's prototype, and binds that object to the + function's implied this parameter. If you neglect to use the + new prefix, no new object will be made and this + will be bound to the global object.

    +

    JSLint enforces the convention that constructor functions be + given names with initial uppercase. JSLint does not expect to + see a function invocation with an initial uppercase name unless it has the + new prefix. JSLint does not expect to see the + new prefix used with functions whose names do not start with + initial uppercase.

    +

    JSLint does not expect to see the wrapper forms + new Number, new String, new Boolean.

    +

    JSLint does not expect to see new Object. + Use Object.create(null) instead.

    +

    Whitespace

    +

    JSLint has a specific set of rules about the use of whitespace. + Where possible, these rules are consistent with centuries of good practice + with literary style.

    +

    The indentation increases by 4 spaces when the last token on a line is + { left brace, + [ left bracket, + ( left paren. The matching closing + token will be the first token on a line, restoring the previous + indentation.

    +

    The ternary operator can be visually confusing, so + ? question mark and + : colon always begin a line and increase + the indentation by 4 spaces.

    +
    return (the_token.id === "(string)" || the_token.id === "(number)")
    +    ? String(the_token.value)
    +    : the_token.id;
    +

    If . period is the first character on a line, the + indentation is increased by 4 spaces.

    +

    A var, let, or const statement will + also cause an indentation if it declares two or more variables, and if the + second variable is not on the same line as the start of the statement.

    +

    Long lines can be continued on the next line with 8 spaces added to the + current indentation. Those 8 spaces do not change the current indentation. + It is 8 to avoid confusion with ordinary indentation.

    +

    The word function is always followed with one space.

    +

    Clauses (case, catch, default, + else, finally) are not statements and so should + not be indented like statements.

    +

    Spaces are used to make things that are not invocations look less like + invocations.

    +

    Tabs and spaces should not be mixed. We should pick just one in order to + avoid the problems that come from having both. Personal preference is an + extremely unreliable criterion. Neither offers a powerful advantage over the + other. Fifty years ago, tab had the advantage of consuming less memory, but + Moore's Law has eliminated that advantage. Space has one clear advantage + over tab: there is no reliable standard for how many spaces a tab + represents, but it is universally accepted that a space occupies a space. So + use spaces. You can edit with tabs if you must, but make sure it is spaces + again before you commit. Maybe someday we will finally get a universal + standard for tabs, but until that day comes, the better choice is spaces.

    +

    Unsafe Characters

    +

    There are characters that are handled inconsistently in some browsers, and + so must be escaped when placed in strings.

    +
    \u0000-\u001f
    +\u007f-\u009f
    +\u00ad
    +\u0600-\u0604
    +\u070f
    +\u17b4
    +\u17b5
    +\u200c-\u200f
    +\u2028-\u202f
    +\u2060-\u206f
    +\ufeff
    +\ufff0-\uffff
    +

    Report

    +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    +
      +
    • The line number on which it starts.
    • +
    • The function’s name. In the case of anonymous functions, + JSLint will attempt to + «guess» the name.
    • +
    • The parameters.
    • +
    • Variables: The variables that are declared in the function.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Exceptions: The variables that are declared by catch + clauses of try statements.
    • +
    • Outer: Variables used by this function that are declared in + other functions.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements. Updates are announced at + https://plus.google.com/communities/104441363299760713736.

    +

    Try it

    +

    Try it. Paste your script + into the window and click the + + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    JSLint is written entirely in JavaScript, so it can run + anywhere that JavaScript (or even Java) can run.

    +

    Perfectly fine

    +

    JSLint was designed to reject code that some would consider to + be perfectly fine. The reason for this is that JSLint's + purpose is to help produce programs that are free of error. That is + difficult in any language and is especially hard in JavaScript. + JSLint attempts to help you increase the visual distance + between correct programs and incorrect programs, making the remaining errors + more obvious. JSLint will give warnings about things that are + not necessarily wrong in the current situation, but which have been observed + to mask or obscure errors. Avoid those things when there are better options + available.

    +

    It is dangerous out there. JSLint is here to help.

    +

    Warning

    +

    JSLint will hurt your feelings. Side effects may include + headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, + dry mouth, cleaner code, and a reduced error rate.

    +

    Further Reading

    + +
    +

    + Code Conventions for the JavaScript Programming Language.

    + + + + + +
    + + diff --git a/branch.jslint_pass_itself_without_long_option/index.html b/branch.jslint_pass_itself_without_long_option/index.html new file mode 100644 index 000000000..235f44b54 --- /dev/null +++ b/branch.jslint_pass_itself_without_long_option/index.html @@ -0,0 +1,125 @@ + + + + + + + + + + + +JSLint: The JavaScript Code Quality Tool + + +
    +
    Source
    +
    + + +
    +
    + Warnings +
    +
    +
    + Function Report +
    +
    +
    + Property Directive + +
    +
    + + + + +
    +
    Options +
    Assume... +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Global variables... + +
    +
    +
    +
    + + + + + +
    + + diff --git a/branch.jslint_pass_itself_without_long_option/jslint.css b/branch.jslint_pass_itself_without_long_option/jslint.css new file mode 100644 index 000000000..9c1b1a2f9 --- /dev/null +++ b/branch.jslint_pass_itself_without_long_option/jslint.css @@ -0,0 +1,340 @@ +@font-face { + font-family: 'Programma'; + src: url('Programma.woff') format('woff'); +} +@font-face { + font-family: 'Daley'; + font-weight: bold; + src: url('Daley-Bold.woff') format('woff'); +} + +body { + background-color: antiquewhite; + color: black; + font-family: 'Daley', sans-serif; + margin: 0; + padding: 0; +} + +#JSLINT_TITLEBOX { + font-family: 'Daley', sans-serif; + margin: 0; + margin-left: 3%; + margin-right: 3%; + padding: 0; +} + +#JSLINT_TITLEBOX ul { + float: right; + font-size: 12pt; +} + +#JSLINT_TITLEBOX div { + color: darkslategray; + float: left; + font-size: 48pt; +} + +#JSLINT_ fieldset { + background-color: gainsboro; + border: 0; + clear: both; + margin-bottom: 1em; + margin-left: 3%; + margin-right: 3%; + margin-top: 1em; + padding: 0; + width: auto; +} +#JSLINT_ legend { + background-color: darkslategray; + border: 0; + color: white; + font-size: 100%; + font-style: normal; + font-weight: normal; + margin: 0; + padding-bottom: 0.25em; + padding-left: 0; + padding-right: 0; + padding-top: 0.25em; + text-align: center; + width: 100%; +} +#JSLINT_ button { + background-color: darkslategray; + border: 0; + color: white; + cursor: pointer; + font-family: 'Daley', sans-serif; + font-size: 100%; + font-style: normal; + margin-left: 1em; + margin-right: 1em; + padding-left: 1em; + padding-right: 1em; + padding-bottom: 0.25em; + padding-top: 0.25em; + text-align: center; +} +#JSLINT_ button:hover { + background-color: slategray; + color: white; + border: 0; +} + +#JSLINT_ button:active { + border: 0; + color: black; +} + +#JSLINT_ button:disabled { + background-color: gray; + border: 0; + color: gainsboro; +} + +a:visited { + color: #69565c; +} + +a:link { + color: black; +} + +#JSLINT_ input[type="text"] { + background-color: white; + border: 0; + color: black; + margin-bottom: 0.2em; + margin-right: 0.5em; + padding-left: 0.5em; + padding-right: 0.5em; + text-align: right; + width: 3em; +} + +#JSLINT_ textarea { + border: 0; + background-color: white; + border: 0; + font-family: Programma, monospace; + font-size: 100%; + font-weight: bold; + resize: none; +} + +#JSLINT_ textarea::selection { + background-color: yellow; + color: black; +} + +#JSLINT_NUMBER { + color: darkgray; + display: inline-block; + height: 4in; + margin: 2%; + margin-right: 0; + overflow: hidden; + padding-right: 0.5%; + text-align: right; + white-space: pre; + width: 4%; +} + +#JSLINT_SOURCE { + color: black; + display: inline-block; + font-size: 100%; + height: 4in; + margin: 2%; + margin-left: 0; + margin-right: 0; + overflow: auto; + padding-left: 0.5%; + white-space: pre; + width: 91%; +} + +#JSLINT_PROPERTY { + background-color: honeydew; + border: 0; + margin: 2%; + padding: 0.5%; + white-space: pre; + width: 95%; +} + +#JSLINT_GLOBAL { + white-space: normal; + width: 95%; +} +#JSLINT_ label { + font-family: sans-serif; + font-size: 90%; + padding-left: 0.25em; +} +#JSLINT_OPTIONS>div { + float: left; + margin: 0.5em; +} + +#JSLINT_ address { + color: black; + display: block; + float: right; + font-family: serif; + font-size: 90%; + margin-left: 1em; +} +#JSLINT_ dl { + background-color: cornsilk; + color: white; + margin: 0; + padding-bottom: 2pt; + padding-left: 1em; + padding-right: 1em; + padding-top: 2pt; +} +#JSLINT_ dfn { + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + margin-bottom: 2pt; +} +#JSLINT_ dt { + color: black; + display: block; + float: left; + font-family: serif; + font-size: 75%; + font-style: italic; + margin: 0; + width: 8em; + text-align: right; +} +#JSLINT_ dd { + color: black; + display: block; + font-family: Programma, monospace; + font-weight: bold; + margin-left: 8em; + padding-bottom: 2pt; +} +#JSLINT_WARNINGS>legend { + background-color: indianred; +} +#JSLINT_WARNINGS>div { + background-color: pink; + padding: 1em; +} +#JSLINT_WARNINGS cite { + color: black; + display: block; + font-family: serif; + font-size: 100%; + font-style: normal; + margin-bottom: 4pt; + margin-left: 20pt; + margin-right: 20pt; + margin-top: 4pt; + overflow-x: hidden; +} +#JSLINT_WARNINGS samp { + background-color: lavenderblush; + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + padding: 4pt; + margin-bottom: 0; + margin-left: 16pt; + margin-right: 16pt; + margin-top: 0; + white-space: pre-wrap; +} + +#JSLINT_REPORT center { + margin-top: 1em; +} + +#JSLINT_WARNINGS dl address { + color: black; + display: inline; + float: none; + font-size: 80%; + margin: 0; +} + +#JSLINT_REPORT>div { + padding: 1em; +} + +#JSLINT_ dl.level0 { + color: black; + background-color: white; +} + +#JSLINT_ dl.level1 { + color: black; + background-color: #ffffe0; /* yellow */ + margin-left: 1em; +} + +#JSLINT_ dl.level2 { + color: black; + background-color: #e0ffe0; /* green */ + margin-left: 2em; +} + +#JSLINT_ dl.level3 { + color: black; + background-color: #e0e0ff; /* blue */ + margin-left: 3em; +} + +#JSLINT_ dl.level4 { + background-color: #ffe0ff; /* purple */ + color: black; + margin-left: 4em; +} + +#JSLINT_ dl.level5 { + background-color: #ffe0e0; /* red */ + color: black; + margin-left: 5em; +} + +#JSLINT_ dl.level6 { + background-color: #ffe390; /* orange */ + color: black; + margin-left: 6em; +} + +#JSLINT_ dl.level7 { + background-color: #e0e0e0; /* gray */ + color: black; + margin-left: 7em; +} + +#JSLINT_ dl.level8 { + color: black; + margin-left: 8em; +} + +#JSLINT_ dl.level9 { + color: black; + margin-left: 9em; +} + +.none { + display: none; +} +.center { + text-align: center; +} diff --git a/branch.jslint_pass_itself_without_long_option/jslint.js b/branch.jslint_pass_itself_without_long_option/jslint.js new file mode 100644 index 000000000..85c0fc1b5 --- /dev/null +++ b/branch.jslint_pass_itself_without_long_option/jslint.js @@ -0,0 +1,4950 @@ +// jslint.js +// 2018-09-13 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// jslint(source, option_object, global_array) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze, a string or an array of strings. +// option_object An object whose keys correspond to option names. +// global_array An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all of the functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// 1. If the source is a single string, split it into an array of strings. +// 2. Turn the source into an array of tokens. +// 3. Furcate the tokens into a parse tree. +// 4. Walk the tree, traversing all of the nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// 5. Check the whitespace between the tokens. + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*jslint long */ + +/*property + a, and, arity, assign, b, bad_assignment_a, bad_directive_a, + bad_get, bad_module_name_a, bad_option_a, bad_property_a, bad_set, + bitwise, block, body, browser, c, calls, catch, charCodeAt, closer, + closure, code, column, complex, concat, constant, context, convert, + couch, create, d, dead, default, devel, directive, directives, + disrupt, dot, duplicate_a, edition, ellipsis, else, empty_block, + escape_mega, eval, every, expected_a, expected_a_at_b_c, + expected_a_b, expected_a_b_from_c_d, expected_a_before_b, + expected_a_next_at_b, expected_digits_after_a, expected_four_digits, + expected_identifier_a, expected_line_break_a_b, + expected_regexp_factor_a, expected_space_a_b, expected_statements_a, + expected_string_a, expected_type_string_a, exports, expression, + extra, finally, flag, for, forEach, free, from, froms, fud, fudge, + function_in_loop, functions, g, getset, global, i, id, identifier, + import, inc, indexOf, infix_in, init, initial, isArray, isNaN, join, + json, keys, label, label_a, lbp, led, length, level, line, lines, + live, long, loop, m, margin, match, message, misplaced_a, + misplaced_directive_a, missing_browser, missing_m, module, multivar, + naked_block, name, names, nested_comment, new, node, not_label_a, + nr, nud, number_isNaN, ok, open, option, out_of_scope_a, parameters, + parent, pop, property, push, quote, redefinition_a_b, replace, + required_a_optional_b, reserved_a, right, role, search, signature, + single, slice, some, sort, splice, split, statement, stop, strict, + subscript_a, switch, test, this, thru, toString, todo_comment, + tokens, too_long, too_many_digits, tree, try, type, u, + unclosed_comment, unclosed_mega, unclosed_string, undeclared_a, + unexpected_a, unexpected_a_after_b, unexpected_a_before_b, + unexpected_at_top_level_a, unexpected_char_a, unexpected_comment, + unexpected_directive_a, unexpected_expression_a, unexpected_label_a, + unexpected_parens, unexpected_space_a_b, unexpected_statement_a, + unexpected_trailing_space, unexpected_typeof_a, uninitialized_a, + unreachable_a, unregistered_property_a, unsafe, unused_a, + use_double, use_open, use_spaces, use_strict, used, value, var_loop, + var_switch, variable, warning, warnings, weird_condition_a, + weird_expression_a, weird_loop, weird_relation_a, white, + wrap_assignment, wrap_condition, wrap_immediate, wrap_parameter, + wrap_regexp, wrap_unary, wrapped, writable, y +*/ + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than {} because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +function populate(array, object = empty(), value = true) { + +// Augment an object by taking property names from an array of strings. + + array.forEach(function (name) { + object[name] = value; + }); + return object; +} + +const allowed_option = { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + bitwise: true, + browser: [ + "caches", "clearInterval", "clearTimeout", "document", "DOMException", + "Element", "Event", "event", "FileReader", "FormData", "history", + "localStorage", "location", "MutationObserver", "name", "navigator", + "screen", "sessionStorage", "setInterval", "setTimeout", "Storage", + "URL", "window", "Worker", "XMLHttpRequest" + ], + couch: [ + "emit", "getRow", "isArray", "log", "provides", "registerType", + "require", "send", "start", "sum", "toJSON" + ], + convert: true, + devel: [ + "alert", "confirm", "console", "prompt" + ], + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + multivar: true, + node: [ + "Buffer", "clearImmediate", "clearInterval", "clearTimeout", + "console", "exports", "module", "process", "require", + "setImmediate", "setInterval", "setTimeout", "URL", + "URLSearchParams", "__dirname", "__filename" + ], + single: true, + this: true, + white: true +}; + +const anticondition = populate([ + "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%", + "typeof", "(number)", "(string)" +]); + +// These are the bitwise operators. + +const bitwiseop = populate([ + "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=", + ">>>", ">>>=" +]); + +const escapeable = populate([ + "\\", "/", "`", "b", "f", "n", "r", "t" +]); + +const opener = { + +// The open and close pairs. + + "(": ")", // paren + "[": "]", // bracket + "{": "}", // brace + "${": "}" // mega +}; + +// The relational operators. + +const relationop = populate([ + "!=", "!==", "==", "===", "<", "<=", ">", ">=" +]); + +// This is the set of infix operators that require a space on each side. + +const spaceop = populate([ + "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/", + "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=", + ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" +]); + +const standard = [ + +// These are the globals that are provided by the language standard. + + "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI", + "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error", + "EvalError", "Float32Array", "Float64Array", "Generator", + "GeneratorFunction", "Int8Array", "Int16Array", "Int32Array", "Intl", + "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat", + "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", + "Set", "String", "Symbol", "SyntaxError", "System", "TypeError", + "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", + "URIError", "WeakMap", "WeakSet" +]; + +const bundle = { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + and: "The '&&' subexpression should be wrapped in parens.", + bad_assignment_a: "Bad assignment to '{a}'.", + bad_directive_a: "Bad directive '{a}'.", + bad_get: "A get function takes no parameters.", + bad_module_name_a: "Bad module name '{a}'.", + bad_option_a: "Bad option '{a}'.", + bad_property_a: "Bad property name '{a}'.", + bad_set: "A set function takes one parameter.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + escape_mega: "Unexpected escapement in mega literal.", + expected_a: "Expected '{a}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: ( + "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'." + ), + expected_a_before_b: "Expected '{a}' before '{b}'.", + expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.", + expected_digits_after_a: "Expected digits after '{a}'.", + expected_four_digits: "Expected four digits after '\\u'.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.", + expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.", + expected_space_a_b: "Expected one space between '{a}' and '{b}'.", + expected_statements_a: "Expected statements before '{a}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + expected_type_string_a: "Expected a type string and instead saw '{a}'.", + function_in_loop: "Don't make functions within a loop.", + infix_in: ( + "Unexpected 'in'. Compare with undefined, " + + "or use the hasOwnProperty method instead." + ), + label_a: "'{a}' is a statement label.", + misplaced_a: "Place '{a}' at the outermost level.", + misplaced_directive_a: ( + "Place the '/*{a}*/' directive before the first statement." + ), + missing_browser: "/*global*/ requires the Assume a browser option.", + missing_m: "Expected 'm' flag on a multiline regular expression.", + naked_block: "Naked block.", + nested_comment: "Nested comment.", + not_label_a: "'{a}' is not a label.", + number_isNaN: "Use Number.isNaN function to compare with NaN.", + out_of_scope_a: "'{a}' is out of scope.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + required_a_optional_b: ( + "Required parameter '{a}' after optional parameter '{b}'." + ), + reserved_a: "Reserved name '{a}'.", + subscript_a: "['{a}'] is better written in dot notation.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line is longer than 80 characters.", + too_many_digits: "Too many digits.", + unclosed_comment: "Unclosed comment.", + unclosed_mega: "Unclosed mega literal.", + unclosed_string: "Unclosed string.", + undeclared_a: "Undeclared '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_a_after_b: "Unexpected '{a}' after '{b}'.", + unexpected_a_before_b: "Unexpected '{a}' before '{b}'.", + unexpected_at_top_level_a: "Expected '{a}' to be in a function.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_directive_a: "When using modules, don't use directive '/*{a}'.", + unexpected_expression_a: ( + "Unexpected expression '{a}' in statement position." + ), + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_parens: "Don't wrap function literals in parens.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_statement_a: ( + "Unexpected statement '{a}' in expression position." + ), + unexpected_trailing_space: "Unexpected trailing space.", + unexpected_typeof_a: ( + "Unexpected 'typeof'. Use '===' to compare directly with {a}." + ), + uninitialized_a: "Uninitialized '{a}'.", + unreachable_a: "Unreachable '{a}'.", + unregistered_property_a: "Unregistered property name '{a}'.", + unsafe: "Unsafe character '{a}'.", + unused_a: "Unused '{a}'.", + use_double: "Use double quotes, not single quotes.", + use_open: ( + "Wrap a ternary expression in parens, " + + "with a line break after the left paren." + ), + use_spaces: "Use spaces, not tabs.", + use_strict: "This function needs a \"use strict\" pragma.", + var_loop: "Don't declare variables in a loop.", + var_switch: "Don't declare variables in a switch.", + weird_condition_a: "Weird condition '{a}'.", + weird_expression_a: "Weird expression '{a}'.", + weird_loop: "Weird loop.", + weird_relation_a: "Weird relation '{a}'.", + wrap_assignment: "Don't wrap assignment statements in parens.", + wrap_condition: "Wrap the condition in parens.", + wrap_immediate: ( + "Wrap an immediate function invocation in parentheses to assist " + + "the reader in understanding that the expression is the result " + + "of a function, and not the function itself." + ), + wrap_parameter: "Wrap the parameter in parens.", + wrap_regexp: "Wrap this regexp in parens to avoid confusion.", + wrap_unary: "Wrap the unary expression in parens." +}; + +// Regular expression literals: + +// supplant {variables} +const rx_supplant = /\{([^{}]*)\}/g; +// carriage return, carriage return linefeed, or linefeed +const rx_crlf = /\n|\r\n?/; +// unsafe characters that are silently deleted by one or more browsers +const rx_unsafe = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; +// identifier +const rx_identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; +const rx_module = /^[a-zA-Z0-9_$:.@\-\/]+$/; +const rx_bad_property = /^_|\$|Sync\$|_$/; +// star slash +const rx_star_slash = /\*\//; +// slash star +const rx_slash_star = /\/\*/; +// slash star or ending slash +const rx_slash_star_or_slash = /\/\*|\/$/; +// uncompleted work comment +const rx_todo = /\b(?:todo|TO\s?DO|HACK)\b/; +// tab +const rx_tab = /\t/g; +// directive +const rx_directive = /^(jslint|property|global)\s+(.*)$/; +const rx_directive_part = /^([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*(.*)$/; +// token (sorry it is so long) +const rx_token = /^((\s+)|([a-zA-Z_$][a-zA-Z0-9_$]*)|[(){}\[\]?,:;'"~`]|=(?:==?|>)?|\.+|[*\/][*\/=]?|\+[=+]?|-[=\-]?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<= "a" && string <= "z\uffff") + || (string >= "A" && string <= "Z\uffff") + ); +} + +function supplant(string, object) { + return string.replace(rx_supplant, function (found, filling) { + const replacement = object[filling]; + return ( + replacement !== undefined + ? replacement + : found + ); + }); +} + +let anon; // The guessed name for anonymous functions. +let blockage; // The current block. +let block_stack; // The stack of blocks. +let declared_globals; // The object containing the global declarations. +let directives; // The directive comments. +let directive_mode; // true if directives are still allowed. +let early_stop; // true if JSLint cannot finish. +let exports; // The exported names and values. +let froms; // The array collecting all import-from strings. +let fudge; // true if the natural numbers start with 1. +let functionage; // The current function. +let functions; // The array containing all of the functions. +let global; // The global object; the outermost context. +let json_mode; // true if parsing JSON. +let lines; // The array containing source lines. +let module_mode; // true if import or export was used. +let next_token; // The next token to be examined in the parse. +let option; // The options parameter. +let property; // The object containing the tallied property names. +let mega_mode; // true if currently parsing a megastring literal. +let stack; // The stack of functions. +let syntax; // The object containing the parser. +let token; // The current token being examined in the parse. +let token_nr; // The number of the next token. +let tokens; // The array of tokens. +let tenure; // The predefined property registry. +let tree; // The abstract parse tree. +let var_mode; // "var" if using var; "let" if using let. +let warnings; // The array collecting all generated warnings. + +// Error reportage functions: + +function artifact(the_token) { + +// Return a string representing an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); +} + +function artifact_line(the_token) { + +// Return the fudged line number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.line + fudge; +} + +function artifact_column(the_token) { + +// Return the fudged column number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.from + fudge; +} + +function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + const warning = { // ~~ + name: "JSLintError", + column: column, + line: line, + code: code + }; + if (a !== undefined) { + warning.a = a; + } + if (b !== undefined) { + warning.b = b; + } + if (c !== undefined) { + warning.c = c; + } + if (d !== undefined) { + warning.d = d; + } + warning.message = supplant(bundle[code] || code, warning); + warnings.push(warning); + return warning; +} + +function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); +} + +function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + if (the_token.warning === undefined) { + the_token.warning = warn_at( + code, + the_token.line, + the_token.from, + a || artifact(the_token), + b, + c, + d + ); + return the_token.warning; + } +} + +function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); +} + +// Tokenize: + +function tokenize(source) { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + +// If the source is not an array, then it is split into lines at the +// carriage return/linefeed. + + lines = ( + Array.isArray(source) + ? source + : source.split(rx_crlf) + ); + tokens = []; + + let char; // a popular character + let column = 0; // the column number of the next character + let first; // the first token + let from; // the starting column number of the token + let line = -1; // the line number of the next character + let nr = 0; // the next token number + let previous = global; // the previous token including comments + let prior = global; // the previous token excluding comments + let mega_from; // the starting column of megastring + let mega_line; // the starting line of megastring + let snippet; // a piece of string + let source_line; // the current line source string + + function next_line() { + +// Put the next line of source in source_line. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + let at; + column = 0; + line += 1; + source_line = lines[line]; + if (source_line !== undefined) { + at = source_line.search(rx_tab); + if (at >= 0) { + if (!option.white) { + warn_at("use_spaces", line, at + 1); + } + source_line = source_line.replace(rx_tab, " "); + } + at = source_line.search(rx_unsafe); + if (at >= 0) { + warn_at( + "unsafe", + line, + column + at, + "U+" + source_line.charCodeAt(at).toString(16) + ); + } + if (!option.white && source_line.slice(-1) === " ") { + warn_at( + "unexpected_trailing_space", + line, + source_line.length - 1 + ); + } + if ( + !option.long + && source_line.length > 80 + && !json_mode + && first + ) { + warn_at("too_long", line, 80); + } + } + return source_line; + } + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions snip, next_char, prev_char, +// some_digits, and escape help in the parsing of literals. + + function snip() { + +// Remove the last character from snippet. + + snippet = snippet.slice(0, -1); + } + + function next_char(match) { + +// Get the next character from the source line. Remove it from the source_line, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + return stop_at( + ( + char === "" + ? "expected_a" + : "expected_a_b" + ), + line, + column - 1, + match, + char + ); + } + if (source_line) { + char = source_line[0]; + source_line = source_line.slice(1); + snippet += char; + } else { + char = ""; + snippet += " "; + } + column += 1; + return char; + } + + function back_char() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the source_line. + + if (snippet) { + char = snippet.slice(-1); + source_line = char + source_line; + column -= 1; + snip(); + } else { + char = ""; + } + return char; + } + + function some_digits(rx, quiet) { + const result = source_line.match(rx); + if (result) { + char = result[1]; + column += char.length; + source_line = result[2]; + snippet += char; + } else { + char = ""; + if (!quiet) { + warn_at( + "expected_digits_after_a", + line, + column, + snippet + ); + } + } + return char.length; + } + + function escape(extra) { + next_char("\\"); + if (escapeable[char] === true) { + return next_char(); + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "u") { + if (next_char("u") === "{") { + if (json_mode) { + warn_at("unexpected_a", line, column - 1, char); + } + if (some_digits(rx_hexs) > 5) { + warn_at("too_many_digits", line, column - 1); + } + if (next_char() !== "}") { + stop_at("expected_a_before_b", line, column, "}", char); + } + return next_char(); + } + back_char(); + if (some_digits(rx_hexs, true) < 4) { + warn_at("expected_four_digits", line, column - 1); + } + return; + } + if (extra && extra.indexOf(char) >= 0) { + return next_char(); + } + warn_at("unexpected_a_before_b", line, column - 2, "\\", char); + } + + function make(id, value, identifier) { + +// Make the token object and append it to the tokens list. + + const the_token = { + from: from, + id: id, + identifier: Boolean(identifier), + line: line, + nr: nr, + thru: column + }; + tokens[nr] = the_token; + nr += 1; + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + directive_mode = false; + } + +// If the token is to have a value, give it one. + + if (value !== undefined) { + the_token.value = value; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option.white. + + if ( + previous.line === line + && previous.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (previous.id === "(comment)" || previous.id === "(regexp)") + ) { + warn( + "expected_space_a_b", + the_token, + artifact(previous), + artifact(the_token) + ); + } + if (previous.id === "." && id === "(number)") { + warn("expected_a_before_b", previous, "0", "."); + } + if (prior.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// The previous token is used to detect adjacency problems. + + previous = the_token; + +// The prior token is a previous token that was not a comment. The prior token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (previous.id !== "(comment)") { + prior = previous; + } + return the_token; + } + + function parse_directive(the_comment, body) { + +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + + const result = body.match(rx_directive_part); + if (result) { + let allowed; + const name = result[1]; + const value = result[2]; + if (the_comment.directive === "jslint") { + allowed = allowed_option[name]; + if ( + typeof allowed === "boolean" + || typeof allowed === "object" + ) { + if ( + value === "" + || value === "true" + || value === undefined + ) { + option[name] = true; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } else if (value === "false") { + option[name] = false; + } else { + warn("bad_option_a", the_comment, name + ":" + value); + } + } else { + warn("bad_option_a", the_comment, name); + } + } else if (the_comment.directive === "property") { + if (tenure === undefined) { + tenure = empty(); + } + tenure[name] = true; + } else if (the_comment.directive === "global") { + if (value) { + warn("bad_option_a", the_comment, name + ":" + value); + } + declared_globals[name] = false; + module_mode = the_comment; + } + return parse_directive(the_comment, result[3]); + } + if (body) { + return stop("bad_directive_a", the_comment, body); + } + } + + function warnings_remove_too_long(line1, line2) { + /* + * this function will remove too_long warnings in the inclusive-range + * [line1, line2] + */ + let ii; + let warning; + // loop backwards in warnings-list to keep index-counter ii invariant, + // as we mutate it + ii = warnings.length; + while (true) { + ii -= 1; + warning = warnings[ii]; + // optimization - break early when we move out of range + // [line1, line2] + if (!(warning && warning.line >= line1)) { + break; + } + // remove too_long warning in inclusive-range [line1, line2] + if (warning.line <= line2 && warnings[ii].code === "too_long") { + warnings.splice(ii, 1); + } + } + } + + function comment(snippet) { + +// Make a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + + // ignore long comments in inclusive line-range + // [line - snippet.length + 1, line] + warnings_remove_too_long( + typeof snippet === "string" + ? line + : line - snippet.length + 1, + line + ); + const the_comment = make("(comment)", snippet); + if (Array.isArray(snippet)) { + snippet = snippet.join(" "); + } + if (!option.devel && rx_todo.test(snippet)) { + warn("todo_comment", the_comment); + } + const result = snippet.match(rx_directive); + if (result) { + if (!directive_mode) { + warn_at("misplaced_directive_a", line, from, result[1]); + } else { + the_comment.directive = result[1]; + parse_directive(the_comment, result[2]); + } + directives.push(the_comment); + } + return the_comment; + } + + function regexp() { + +// Parse a regular expression literal. + + // ignore long regexp in inclusive line-range [line, line] + warnings_remove_too_long(line, line); + let multi_mode = false; + let result; + let value; + + function quantifier() { + +// Match an optional quantifier. + + if (char === "?" || char === "*" || char === "+") { + next_char(); + } else if (char === "{") { + if (some_digits(rx_digits, true) === 0) { + warn_at("expected_a", line, column, "0"); + } + if (next_char() === ",") { + some_digits(rx_digits, true); + next_char(); + } + next_char("}"); + } else { + return; + } + if (char === "?") { + next_char("?"); + } + } + + function subklass() { + +// Match a character in a character class. + + if (char === "\\") { + escape("BbDdSsWw-[]^"); + return true; + } + if ( + char === "" + || char === "[" + || char === "]" + || char === "/" + || char === "^" + || char === "-" + ) { + return false; + } + if (char === " ") { + warn_at("expected_a_b", line, column, "\\u0020", " "); + } else if (char === "`" && mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char(); + return true; + } + + function ranges() { + +// Match a range of subclasses. + + if (subklass()) { + if (char === "-") { + next_char("-"); + if (!subklass()) { + return stop_at( + "unexpected_a", + line, + column - 1, + "-" + ); + } + } + return ranges(); + } + } + + function klass() { + +// Match a class. + + next_char("["); + if (char === "^") { + next_char("^"); + } + (function classy() { + ranges(); + if (char !== "]" && char !== "") { + warn_at( + "expected_a_before_b", + line, + column, + "\\", + char + ); + next_char(); + return classy(); + } + }()); + next_char("]"); + } + + function choice() { + + function group() { + +// Match a group that starts with left paren. + + next_char("("); + if (char === "?") { + next_char("?"); + if (char === "=" || char === "!") { + next_char(); + } else { + next_char(":"); + } + } else if (char === ":") { + warn_at("expected_a_before_b", line, column, "?", ":"); + } + choice(); + next_char(")"); + } + + function factor() { + if ( + char === "" + || char === "/" + || char === "]" + || char === ")" + ) { + return false; + } + if (char === "(") { + group(); + return true; + } + if (char === "[") { + klass(); + return true; + } + if (char === "\\") { + escape("BbDdSsWw^${}[]():=!.-|*+?"); + return true; + } + if ( + char === "?" + || char === "+" + || char === "*" + || char === "}" + || char === "{" + ) { + warn_at( + "expected_a_before_b", + line, + column - 1, + "\\", + char + ); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column - 1, "`"); + } + } else if (char === " ") { + warn_at( + "expected_a_b", + line, + column - 1, + "\\s", + " " + ); + } else if (char === "$") { + if (source_line[0] !== "/") { + multi_mode = true; + } + } else if (char === "^") { + if (snippet !== "^") { + multi_mode = true; + } + } + next_char(); + return true; + } + + function sequence(follow) { + if (factor()) { + quantifier(); + return sequence(true); + } + if (!follow) { + warn_at("expected_regexp_factor_a", line, column, char); + } + } + +// Match a choice (a sequence that can be followed by | and another choice). + + sequence(); + if (char === "|") { + next_char("|"); + return choice(); + } + } + +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + next_char(); + if (char === "=") { + warn_at("expected_a_before_b", line, column, "\\", "="); + } + choice(); + +// Make sure there is a closing slash. + + snip(); + value = snippet; + next_char("/"); + +// Process dangling flag letters. + + const allowed = { + g: true, + i: true, + m: true, + u: true, + y: true + }; + const flag = empty(); + (function make_flag() { + if (is_letter(char)) { + if (allowed[char] !== true) { + warn_at("unexpected_a", line, column, char); + } + allowed[char] = false; + flag[char] = true; + next_char(); + return make_flag(); + } + }()); + back_char(); + if (char === "/" || char === "*") { + return stop_at("unexpected_a", line, from, char); + } + result = make("(regexp)", char); + result.flag = flag; + result.value = value; + if (multi_mode && !flag.m) { + warn_at("missing_m", line, column); + } + return result; + } + + function string(quote) { + +// Make a string token. + + let the_token; + snippet = ""; + next_char(); + + return (function next() { + if (char === quote) { + snip(); + the_token = make("(string)", snippet); + the_token.quote = quote; + return the_token; + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "\\") { + escape(quote); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char("`"); + } else { + next_char(); + } + return next(); + }()); + } + + function frack() { + if (char === ".") { + some_digits(rx_digits); + next_char(); + } + if (char === "E" || char === "e") { + next_char(); + if (char !== "+" && char !== "-") { + back_char(); + } + some_digits(rx_digits); + next_char(); + } + } + + function number() { + if (snippet === "0") { + next_char(); + if (char === ".") { + frack(); + } else if (char === "b") { + some_digits(rx_bits); + next_char(); + } else if (char === "o") { + some_digits(rx_octals); + next_char(); + } else if (char === "x") { + some_digits(rx_hexs); + next_char(); + } + } else { + next_char(); + frack(); + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + return stop_at( + "unexpected_a_after_b", + line, + column - 1, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + back_char(); + return make("(number)", snippet); + } + + function lex() { + let array; + let i = 0; + let j = 0; + let last; + let result; + let the_token; + if (!source_line) { + source_line = next_line(); + from = 0; + return ( + source_line === undefined + ? ( + mega_mode + ? stop_at("unclosed_mega", mega_line, mega_from) + : make("(end)") + ) + : lex() + ); + } + from = column; + result = source_line.match(rx_token); + +// result[1] token +// result[2] whitespace +// result[3] identifier +// result[4] number +// result[5] rest + + if (!result) { + return stop_at( + "unexpected_char_a", + line, + column, + source_line[0] + ); + } + + snippet = result[1]; + column += snippet.length; + source_line = result[5]; + +// Whitespace was matched. Call lex again to get more. + + if (result[2]) { + return lex(); + } + +// The token is an identifier. + + if (result[3]) { + return make(snippet, undefined, true); + } + +// The token is a number. + + if (result[4]) { + return number(snippet); + } + +// The token is a string. + + if (snippet === "\"") { + return string(snippet); + } + if (snippet === "'") { + if (!option.single) { + warn_at("use_double", line, column); + } + return string(snippet); + } + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (snippet === "`") { + if (mega_mode) { + return stop_at("expected_a_b", line, column, "}", "`"); + } + snippet = ""; + mega_from = from; + mega_line = line; + mega_mode = true; + +// Parsing a mega literal is tricky. First make a ` token. + + make("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + (function part() { + const at = source_line.search(rx_mega); + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + if (at < 0) { + snippet += source_line + "\n"; + return ( + next_line() === undefined + ? stop_at("unclosed_mega", mega_line, mega_from) + : part() + ); + } + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + snippet += source_line.slice(0, at); + column += at; + source_line = source_line.slice(at); + if (source_line[0] === "\\") { + stop_at("escape_mega", line, at); + } + make("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then make tokens that will become part of an expression until +// a } token is made. + + if (source_line[0] === "$") { + column += 2; + make("${"); + source_line = source_line.slice(2); + (function expr() { + const id = lex().id; + if (id === "{") { + return stop_at( + "expected_a_b", + line, + column, + "}", + "{" + ); + } + if (id !== "}") { + return expr(); + } + }()); + return part(); + } + }()); + source_line = source_line.slice(1); + column += 1; + mega_mode = false; + return make("`"); + } + +// The token is a // comment. + + if (snippet === "//") { + snippet = source_line; + source_line = ""; + the_token = comment(snippet); + if (mega_mode) { + warn("unexpected_comment", the_token, "`"); + } + return the_token; + } + +// The token is a /* comment. + + if (snippet === "/*") { + array = []; + if (source_line[0] === "/") { + warn_at("unexpected_a", line, column + i, "/"); + } + (function next() { + if (source_line > "") { + i = source_line.search(rx_star_slash); + if (i >= 0) { + return; + } + j = source_line.search(rx_slash_star); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + } + array.push(source_line); + source_line = next_line(); + if (source_line === undefined) { + return stop_at("unclosed_comment", line, column); + } + return next(); + }()); + snippet = source_line.slice(0, i); + j = snippet.search(rx_slash_star_or_slash); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + array.push(snippet); + column += i + 2; + source_line = source_line.slice(i + 2); + return comment(array); + } + +// The token is a slash. + + if (snippet === "/") { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + if (prior.identifier) { + if (!prior.dot) { + if (prior.id === "return") { + return regexp(); + } + if ( + prior.id === "(begin)" + || prior.id === "case" + || prior.id === "delete" + || prior.id === "in" + || prior.id === "instanceof" + || prior.id === "new" + || prior.id === "typeof" + || prior.id === "void" + || prior.id === "yield" + ) { + the_token = regexp(); + return stop("unexpected_a", the_token); + } + } + } else { + last = prior.id[prior.id.length - 1]; + if ("(,=:?[".indexOf(last) >= 0) { + return regexp(); + } + if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) { + the_token = regexp(); + warn("wrap_regexp", the_token); + return the_token; + } + } + if (source_line[0] === "/") { + column += 1; + source_line = source_line.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + } + return make(snippet); + } + + first = lex(); + json_mode = first.id === "{" || first.id === "["; + +// This is the only loop in JSLint. It will turn into a recursive call to lex +// when ES6 has been finished and widely deployed and adopted. + + while (true) { + if (lex().id === "(end)") { + break; + } + } +} + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + +function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!rx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!rx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property[id] === "number") { + property[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (tenure !== undefined) { + if (tenure[id] !== true) { + warn("unregistered_property_a", name); + } + } else { + if (name.identifier && rx_bad_property.test(id)) { + warn("bad_property_a", name); + } + } + property[id] = 1; + } + return id; +} + +function dispense() { + +// Deliver the next token, skipping the comments. + + const cadet = tokens[token_nr]; + token_nr += 1; + if (cadet.id === "(comment)") { + if (json_mode) { + warn("unexpected_a", cadet); + } + return dispense(); + } else { + return cadet; + } +} + +function lookahead() { + +// Look ahead one token without advancing. + + const old_token_nr = token_nr; + const cadet = dispense(true); + token_nr = old_token_nr; + return cadet; +} + +function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if (token.identifier && token.id !== "function") { + anon = token.id; + } else if (token.id === "(string)" && rx_identifier.test(token.value)) { + anon = token.value; + } + +// Attempt to match next_token with an expected id. + + if (id !== undefined && next_token.id !== id) { + return ( + match === undefined + ? stop("expected_a_b", next_token, id, artifact()) + : stop( + "expected_a_b_from_c_d", + next_token, + id, + artifact(match), + artifact_line(match), + artifact(next_token) + ) + ); + } + +// Promote the tokens, skipping comments. + + token = next_token; + next_token = dispense(); + if (next_token.id === "(end)") { + token_nr -= 1; + } +} + +// Parsing of JSON is simple: + +function json_value() { + let negative; + if (next_token.id === "{") { + return (function json_object() { + const brace = next_token; + const object = empty(); + const properties = []; + brace.expression = properties; + advance("{"); + if (next_token.id !== "}") { + (function next() { + let name; + let value; + if (next_token.quote !== "\"") { + warn( + "unexpected_a", + next_token, + next_token.quote + ); + } + name = next_token; + advance("(string)"); + if (object[token.value] !== undefined) { + warn("duplicate_a", token); + } else if (token.value === "__proto__") { + warn("bad_property_a", token); + } else { + object[token.value] = token; + } + advance(":"); + value = json_value(); + value.label = name; + properties.push(value); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("}", brace); + return brace; + }()); + } + if (next_token.id === "[") { + return (function json_array() { + const bracket = next_token; + const elements = []; + bracket.expression = elements; + advance("["); + if (next_token.id !== "]") { + (function next() { + elements.push(json_value()); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]", bracket); + return bracket; + }()); + } + if ( + next_token.id === "true" + || next_token.id === "false" + || next_token.id === "null" + ) { + advance(); + return token; + } + if (next_token.id === "(number)") { + if (!rx_JSON_number.test(next_token.value)) { + warn("unexpected_a"); + } + advance(); + return token; + } + if (next_token.id === "(string)") { + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + advance(); + return token; + } + if (next_token.id === "-") { + negative = next_token; + negative.arity = "unary"; + advance("-"); + advance("(number)"); + negative.expression = token; + return negative; + } + stop("unexpected_a"); +} + +// Now we parse JavaScript. + +function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + const id = name.id; + +// Reserved words may not be enrolled. + + if (syntax[id] !== undefined && id !== "ignore") { + warn("reserved_a", name); + } else { + +// Has the name been enrolled in this context? + + let earlier = functionage.context[id]; + if (earlier) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + +// Has the name been enrolled in an outer context? + + } else { + stack.forEach(function (value) { + const item = value.context[id]; + if (item !== undefined) { + earlier = item; + } + }); + if (earlier) { + if (id === "ignore") { + if (earlier.role === "variable") { + warn("unexpected_a", name); + } + } else { + if ( + ( + role !== "exception" + || earlier.role !== "exception" + ) + && role !== "parameter" + && role !== "function" + ) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + } + } + } + +// Enroll it. + + functionage.context[id] = name; + name.dead = true; + name.parent = functionage; + name.init = false; + name.role = role; + name.used = 0; + name.writable = !readonly; + } + } +} + +function expression(rbp, initial) { + +// This is the heart of the Pratt parser. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// nud Null denotation +// led Left denotation +// lbp Left binding power +// rbp Right binding power + +// It processes a nud (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop. It +// returns the expression's parse tree. + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement, + + if (!initial) { + advance(); + } + the_symbol = syntax[token.id]; + if (the_symbol !== undefined && the_symbol.nud !== undefined) { + left = the_symbol.nud(); + } else if (token.identifier) { + left = token; + left.arity = "variable"; + } else { + return stop("unexpected_a", token); + } + (function right() { + the_symbol = syntax[next_token.id]; + if ( + the_symbol !== undefined + && the_symbol.led !== undefined + && rbp < the_symbol.lbp + ) { + advance(); + left = the_symbol.led(left); + return right(); + } + }()); + return left; +} + +function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = next_token; + let the_value; + the_paren.free = true; + advance("("); + the_value = expression(0); + advance(")"); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (anticondition[the_value.id] === true) { + warn("unexpected_a", the_value); + } + return the_value; +} + +function is_weird(thing) { + return ( + thing.id === "(regexp)" + || thing.id === "{" + || thing.id === "=>" + || thing.id === "function" + || (thing.id === "[" && thing.arity === "unary") + ); +} + +function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + return ( + Array.isArray(b) + && a.length === b.length + && a.every(function (value, index) { + return are_similar(value, b[index]); + }) + ); + } + if (Array.isArray(b)) { + return false; + } + if (a.id === "(number)" && b.id === "(number)") { + return a.value === b.value; + } + let a_string; + let b_string; + if (a.id === "(string)") { + a_string = a.value; + } else if (a.id === "`" && a.constant) { + a_string = a.value[0]; + } + if (b.id === "(string)") { + b_string = b.value; + } else if (b.id === "`" && b.constant) { + b_string = b.value[0]; + } + if (typeof a_string === "string") { + return a_string === b_string; + } + if (is_weird(a) || is_weird(b)) { + return false; + } + if (a.arity === b.arity && a.id === b.id) { + if (a.id === ".") { + return ( + are_similar(a.expression, b.expression) + && are_similar(a.name, b.name) + ); + } + if (a.arity === "unary") { + return are_similar(a.expression, b.expression); + } + if (a.arity === "binary") { + return ( + a.id !== "(" + && are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + ); + } + if (a.arity === "ternary") { + return ( + are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + && are_similar(a.expression[2], b.expression[2]) + ); + } + if (a.arity === "function" && a.arity === "regexp") { + return false; + } + return true; + } + return false; +} + +function semicolon() { + +// Try to match a semicolon. + + if (next_token.id === ";") { + advance(";"); + } else { + warn_at( + "expected_a_b", + token.line, + token.thru, + ";", + artifact(next_token) + ); + } + anon = "anonymous"; +} + +function statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token.identifier && next_token.id === ":") { + the_label = token; + if (the_label.id === "ignore") { + warn("unexpected_a", the_label); + } + advance(":"); + if ( + next_token.id === "do" + || next_token.id === "for" + || next_token.id === "switch" + || next_token.id === "while" + ) { + enroll(the_label, "label", true); + the_label.init = true; + the_label.dead = false; + the_statement = statement(); + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token; + first.statement = true; + the_symbol = syntax[first.id]; + if (the_symbol !== undefined && the_symbol.fud !== undefined) { + the_symbol.disrupt = false; + the_symbol.statement = true; + the_statement = the_symbol.fud(); + } else { + +// It is an expression statement. + + the_statement = expression(0, true); + if (the_statement.wrapped && the_statement.id !== "(") { + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; +} + +function statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const array = []; + (function next(disrupt) { + if ( + next_token.id !== "}" + && next_token.id !== "case" + && next_token.id !== "default" + && next_token.id !== "else" + && next_token.id !== "(end)" + ) { + let a_statement = statement(); + array.push(a_statement); + if (disrupt) { + warn("unreachable_a", a_statement); + } + return next(a_statement.disrupt); + } + }(false)); + return array; +} + +function not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === global) { + warn("unexpected_at_top_level_a", thing); + } +} + +function top_level_only(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== global) { + warn("misplaced_a", the_thing); + } +} + +function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token; + the_block.arity = "statement"; + the_block.body = special === "body"; + +// All top level function bodies should include the "use strict" pragma unless +// the whole file is strict or the file is a module or the function parameters +// use es6 syntax. + + if ( + special === "body" + && stack.length === 1 + && next_token.value === "use strict" + ) { + the_block.strict = next_token; + next_token.statement = true; + advance("(string)"); + advance(";"); + } + stmts = statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option.devel && special !== "ignore") { + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; +} + +function mutation_check(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + warn("bad_assignment_a", the_thing); + return false; + } + return true; +} + +function left_check(left, right) { + +// Warn if the left is not one of these: +// e.b +// e[b] +// e() +// ?: +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !left_check(left.expression[1]) + && !left_check(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right); + return false; + } + return true; +} + +// These functions are used to specify the grammar of our language: + +function symbol(id, bp) { + +// Make a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax[id] = the_symbol; + } + return the_symbol; +} + +function assignment(id) { + +// Make an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led = function (left) { + const the_token = token; + let right; + the_token.arity = "assignment"; + right = expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "pre" + || right.arity === "post" + ) { + warn("unexpected_a", right); + } + mutation_check(left); + return the_token; + }; + return the_symbol; +} + +function constant(id, type, value) { + +// Make a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud = ( + typeof value === "function" + ? value + : function () { + token.constant = true; + if (value !== undefined) { + token.value = value; + } + return token; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; +} + +function infix(id, bp, f) { + +// Make an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, expression(bp)]; + return the_token; + }; + return the_symbol; +} + +function infixr(id, bp) { + +// Make a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + the_token.expression = [left, expression(bp - 1)]; + return the_token; + }; + return the_symbol; +} + +function post(id) { + +// Make one of the post operators. + + const the_symbol = symbol(id, 150); + the_symbol.led = function (left) { + token.expression = left; + token.arity = "post"; + mutation_check(token.expression); + return token; + }; + return the_symbol; +} + +function pre(id) { + +// Make one of the pre operators. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "pre"; + the_token.expression = expression(150); + mutation_check(the_token.expression); + return the_token; + }; + return the_symbol; +} + +function prefix(id, f) { + +// Make a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = expression(150); + return the_token; + }; + return the_symbol; +} + +function stmt(id, f) { + +// Make a statement. + + const the_symbol = symbol(id); + the_symbol.fud = function () { + token.arity = "statement"; + return f(); + }; + return the_symbol; +} + +function ternary(id1, id2) { + +// Make a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led = function (left) { + const the_token = token; + const second = expression(20); + advance(id2); + token.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, expression(10)]; + return the_token; + }; + return the_symbol; +} + +// Begin defining the language. + +syntax = empty(); + +symbol("}"); +symbol(")"); +symbol("]"); +symbol(","); +symbol(";"); +symbol(":"); +symbol("*/"); +symbol("await"); +symbol("case"); +symbol("catch"); +symbol("class"); +symbol("default"); +symbol("else"); +symbol("enum"); +symbol("finally"); +symbol("implements"); +symbol("interface"); +symbol("package"); +symbol("private"); +symbol("protected"); +symbol("public"); +symbol("static"); +symbol("super"); +symbol("void"); +symbol("yield"); + +constant("(number)", "number"); +constant("(regexp)", "regexp"); +constant("(string)", "string"); +constant("arguments", "object", function () { + warn("unexpected_a", token); + return token; +}); +constant("eval", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("false", "boolean", false); +constant("Function", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("ignore", "undefined", function () { + warn("unexpected_a", token); + return token; +}); +constant("Infinity", "number", Infinity); +constant("isFinite", "function", function () { + warn("expected_a_b", token, "Number.isFinite", "isFinite"); + return token; +}); +constant("isNaN", "function", function () { + warn("number_isNaN", token); + return token; +}); +constant("NaN", "number", NaN); +constant("null", "null", null); +constant("this", "object", function () { + if (!option.this) { + warn("unexpected_a", token); + } + return token; +}); +constant("true", "boolean", true); +constant("undefined", "undefined"); + +assignment("="); +assignment("+="); +assignment("-="); +assignment("*="); +assignment("/="); +assignment("%="); +assignment("&="); +assignment("|="); +assignment("^="); +assignment("<<="); +assignment(">>="); +assignment(">>>="); + +infix("||", 40); +infix("&&", 50); +infix("|", 70); +infix("^", 80); +infix("&", 90); +infix("==", 100); +infix("===", 100); +infix("!=", 100); +infix("!==", 100); +infix("<", 110); +infix(">", 110); +infix("<=", 110); +infix(">=", 110); +infix("in", 110); +infix("instanceof", 110); +infix("<<", 120); +infix(">>", 120); +infix(">>>", 120); +infix("+", 130); +infix("-", 130); +infix("*", 140); +infix("/", 140); +infix("%", 140); +infixr("**", 150); +infix("(", 160, function (left) { + const the_paren = token; + let the_argument; + if (left.id !== "function") { + left_check(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (next_token.id !== ")") { + (function next() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + the_paren.free = true; + if (the_argument.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + the_paren.free = false; + } + return the_paren; +}); +infix(".", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("[", 170, function (left) { + const the_token = token; + const the_subscript = expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + const name = survey(the_subscript); + if (rx_identifier.test(name)) { + warn("subscript_a", the_subscript, name); + } + } + left_check(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; +}); +infix("=>", 170, function (left) { + return stop("wrap_parameter", left); +}); + +function do_tick() { + const the_tick = token; + the_tick.value = []; + the_tick.expression = []; + if (next_token.id !== "`") { + (function part() { + advance("(string)"); + the_tick.value.push(token); + if (next_token.id === "${") { + advance("${"); + the_tick.expression.push(expression(0)); + advance("}"); + return part(); + } + }()); + } + advance("`"); + return the_tick; +} + +infix("`", 160, function (left) { + const the_tick = do_tick(); + left_check(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; +}); + +post("++"); +post("--"); +pre("++"); +pre("--"); + +prefix("+"); +prefix("-"); +prefix("~"); +prefix("!"); +prefix("!!"); +prefix("[", function () { + const the_token = token; + the_token.expression = []; + if (next_token.id !== "]") { + (function next() { + let element; + let ellipsis = false; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + element = expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]"); + return the_token; +}); +prefix("/=", function () { + stop("expected_a_b", token, "/\\=", "/="); +}); +prefix("=>", function () { + return stop("expected_a_before_b", token, "()", "=>"); +}); +prefix("new", function () { + const the_new = token; + const right = expression(160); + if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "()", artifact(next_token)); + } + the_new.expression = right; + return the_new; +}); +prefix("typeof"); +prefix("void", function () { + const the_void = token; + warn("unexpected_a", the_void); + the_void.expression = expression(0); + return the_void; +}); + +function parameter_list() { + let complex = false; + const list = []; + let optional; + const signature = ["("]; + if (next_token.id !== ")" && next_token.id !== "(end)") { + (function parameter() { + let ellipsis = false; + let param; + if (next_token.id === "{") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("{"); + signature.push("{"); + (function subparameter() { + let subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (next_token.id === ":") { + advance(":"); + advance(); + token.label = subparam; + subparam = token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + } + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return subparameter(); + } + }()); + list.push(param); + advance("}"); + signature.push("}"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else if (next_token.id === "[") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("["); + signature.push("[]"); + (function subparameter() { + const subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + return subparameter(); + } + }()); + list.push(param); + advance("]"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else { + if (next_token.id === "...") { + complex = true; + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + } + if (!next_token.identifier) { + return stop("expected_identifier_a"); + } + param = next_token; + list.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (next_token.id === "=") { + complex = true; + optional = param; + advance("="); + param.expression = expression(0); + } else { + if (optional !== undefined) { + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } + } + }()); + } + advance(")"); + signature.push(")"); + return [list, signature.join(""), complex]; +} + +function do_function(the_function) { + let name; + if (the_function === undefined) { + the_function = token; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + name = next_token; + enroll(name, "variable", true); + the_function.name = name; + name.init = true; + name.calls = empty(); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + if (next_token.identifier) { + name = next_token; + the_function.name = name; + advance(); + } else { + the_function.name = anon; + } + } + } else { + name = the_function.name; + } + the_function.level = functionage.level + 1; + if (mega_mode) { + warn("unexpected_a", the_function); + } + +// Don't make functions in loops. It is inefficient, and it can lead to scoping +// errors. + + if (functionage.loop > 0) { + warn("function_in_loop", the_function); + } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + the_function.context = empty(); + the_function.finally = 0; + the_function.loop = 0; + the_function.switch = 0; + the_function.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functions.push(the_function); + functionage = the_function; + if (the_function.arity !== "statement" && typeof name === "object") { + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// Parse the parameter list. + + advance("("); + token.free = false; + token.arity = "function"; + const pl = parameter_list(); + functionage.parameters = pl[0]; + functionage.signature = pl[1]; + functionage.complex = pl[2]; + functionage.parameters.forEach(function enroll_parameter(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + name.names.forEach(enroll_parameter); + } + }); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && next_token.line === token.line + ) { + return stop("unexpected_a", next_token); + } + if (next_token.id === "." || next_token.id === "[") { + warn("unexpected_a"); + } + +// Restore the previous context. + + functionage = stack.pop(); + return the_function; +} + +prefix("function", do_function); + +function fart(pl) { + if (next_token.id === ";") { + stop("wrap_assignment", token); + } + advance("=>"); + const the_fart = token; + the_fart.arity = "binary"; + the_fart.name = "=>"; + the_fart.level = functionage.level + 1; + functions.push(the_fart); + if (functionage.loop > 0) { + warn("function_in_loop", the_fart); + } + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + the_fart.context = empty(); + the_fart.finally = 0; + the_fart.loop = 0; + the_fart.switch = 0; + the_fart.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functionage = the_fart; + the_fart.parameters = pl[0]; + the_fart.signature = pl[1]; + the_fart.complex = true; + the_fart.parameters.forEach(function (name) { + enroll(name, "parameter", true); + }); + if (next_token.id === "{") { + warn("expected_a_b", the_fart, "function", "=>"); + the_fart.block = block("body"); + } else { + the_fart.expression = expression(0); + } + functionage = stack.pop(); + return the_fart; +} + +prefix("(", function () { + const the_paren = token; + let the_value; + const cadet = lookahead().id; + +// We can distinguish between a parameter list for => and a wrapped expression +// with one token of lookahead. + + if ( + next_token.id === ")" + || next_token.id === "..." + || (next_token.identifier && (cadet === "," || cadet === "=")) + ) { + the_paren.free = false; + return fart(parameter_list()); + } + the_paren.free = true; + the_value = expression(0); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + if (next_token.id === "=>") { + if (the_value.arity !== "variable") { + if (the_value.id === "{" || the_value.id === "[") { + warn("expected_a_before_b", the_paren, "function", "("); + return stop("expected_a_b", next_token, "{", "=>"); + } + return stop("expected_identifier_a", the_value); + } + the_paren.expression = [the_value]; + return fart([the_paren.expression, "(" + the_value.id + ")"]); + } + return the_value; +}); +prefix("`", do_tick); +prefix("{", function () { + const the_brace = token; + const seen = empty(); + the_brace.expression = []; + if (next_token.id !== "}") { + (function member() { + let extra; + let full; + let id; + let name = next_token; + let value; + advance(); + if ( + (name.id === "get" || name.id === "set") + && next_token.identifier + ) { + if (!option.getset) { + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + next_token.id; + name = next_token; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (next_token.id === "}" || next_token.id === ",") { + if (typeof extra === "string") { + advance("("); + } + value = expression(Infinity, true); + } else if (next_token.id === "(") { + value = do_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + advance("("); + } + advance(":"); + value = expression(0); + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + advance(":"); + value = expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (next_token.id === ",") { + advance(","); + return member(); + } + }()); + } + advance("}"); + return the_brace; +}); + +stmt(";", function () { + warn("unexpected_a", token); + return token; +}); +stmt("{", function () { + warn("naked_block", token); + return block("naked"); +}); +stmt("break", function () { + const the_break = token; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (next_token.identifier && token.line === next_token.line) { + the_label = functionage.context[next_token.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + warn( + (the_label !== undefined && the_label.dead) + ? "out_of_scope_a" + : "not_label_a" + ); + } else { + the_label.used += 1; + } + the_break.label = next_token; + advance(); + } + advance(";"); + return the_break; +}); + +function do_var() { + const the_statement = token; + const is_const = the_statement.id === "const"; + the_statement.names = []; + +// A program may use var or let, but not both. + + if (!is_const) { + if (var_mode === undefined) { + var_mode = the_statement.id; + } else if (the_statement.id !== var_mode) { + warn( + "expected_a_b", + the_statement, + var_mode, + the_statement.id + ); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + warn("var_switch", the_statement); + } + if (functionage.loop > 0 && the_statement.id === "var") { + warn("var_loop", the_statement); + } + (function next() { + if (next_token.id === "{" && the_statement.id !== "var") { + const the_brace = next_token; + the_brace.names = []; + advance("{"); + (function pair() { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + survey(name); + advance(); + if (next_token.id === ":") { + advance(":"); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + next_token.label = name; + the_brace.names.push(next_token); + enroll(next_token, "variable", is_const); + advance(); + } else { + the_brace.names.push(name); + enroll(name, "variable", is_const); + } + if (next_token.id === ",") { + advance(","); + return pair(); + } + }()); + advance("}"); + advance("="); + the_brace.expression = expression(0); + the_statement.names.push(the_brace); + } else if (next_token.id === "[" && the_statement.id !== "var") { + const the_bracket = next_token; + the_bracket.names = []; + advance("["); + (function element() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + advance(); + the_bracket.names.push(name); + enroll(name, "variable", the_statement.id === "const"); + if (ellipsis) { + name.ellipsis = true; + } else if (next_token.id === ",") { + advance(","); + return element(); + } + }()); + advance("]"); + advance("="); + the_bracket.expression = expression(0); + the_statement.names.push(the_bracket); + } else if (next_token.identifier) { + const name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", is_const); + if (next_token.id === "=" || is_const) { + advance("="); + name.init = true; + name.expression = expression(0); + } + the_statement.names.push(name); + } else { + return stop("expected_identifier_a", next_token); + } + if (next_token.id === ",") { + if (!option.multivar) { + warn("expected_a_b", next_token, ";", ","); + } + advance(","); + return next(); + } + }()); + the_statement.open = ( + the_statement.names.length > 1 + && the_statement.line !== the_statement.names[1].line + ); + semicolon(); + return the_statement; +} + +stmt("const", do_var); +stmt("continue", function () { + const the_continue = token; + if (functionage.loop < 1 || functionage.finally > 0) { + warn("unexpected_a", the_continue); + } + not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; +}); +stmt("debugger", function () { + const the_debug = token; + if (!option.devel) { + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; +}); +stmt("delete", function () { + const the_token = token; + const the_value = expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; +}); +stmt("do", function () { + const the_do = token; + not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; +}); +stmt("export", function () { + const the_export = token; + let the_id; + let the_name; + let the_thing; + + function export_id() { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + the_id = next_token.id; + the_name = global.context[the_id]; + if (the_name === undefined) { + warn("unexpected_a"); + } else { + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a"); + } + exports[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + } + + the_export.expression = []; + if (next_token.id === "default") { + if (exports.default !== undefined) { + warn("duplicate_a"); + } + advance("default"); + the_thing = expression(0); + semicolon(); + exports.default = the_thing; + the_export.expression.push(the_thing); + } else { + if (next_token.id === "function") { + the_thing = statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a", the_name); + } + exports[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + warn("unexpected_a"); + } else if (next_token.id === "{") { + advance("{"); + (function loop() { + export_id(); + if (next_token.id === ",") { + advance(","); + return loop(); + } + }()); + advance("}"); + semicolon(); + } else { + export_id(); + if (the_name.writable !== true) { + warn("unexpected_a", token); + } + semicolon(); + } + } + module_mode = true; + return the_export; +}); +stmt("for", function () { + let first; + const the_for = token; + if (!option.for) { + warn("unexpected_a", the_for); + } + not_top_level(the_for); + functionage.loop += 1; + advance("("); + token.free = true; + if (next_token.id === ";") { + return stop("expected_a_b", the_for, "while (", "for (;"); + } + if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + return stop("unexpected_a"); + } + first = expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = expression(0); + advance(";"); + the_for.inc = expression(0); + if (the_for.inc.id === "++") { + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; +}); +stmt("function", do_function); +stmt("if", function () { + let the_else; + const the_if = token; + the_if.expression = condition(); + the_if.block = block(); + if (next_token.id === "else") { + advance("else"); + the_else = token; + the_if.else = ( + next_token.id === "if" + ? statement() + : block() + ); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + the_if.disrupt = true; + } else { + warn("unexpected_a", the_else); + } + } + } + return the_if; +}); +stmt("import", function () { + const the_import = token; + let name; + if (typeof module_mode === "object") { + warn("unexpected_directive_a", module_mode, module_mode.directive); + } + module_mode = true; + if (next_token.identifier) { + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + const names = []; + advance("{"); + if (next_token.id !== "}") { + while (true) { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (next_token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token; + if (!rx_module.test(token.value)) { + warn("bad_module_name_a", token); + } + froms.push(token.value); + semicolon(); + return the_import; +}); +stmt("let", do_var); +stmt("return", function () { + const the_return = token; + not_top_level(the_return); + if (functionage.finally > 0) { + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (next_token.id !== ";" && the_return.line === next_token.line) { + the_return.expression = expression(10); + } + advance(";"); + return the_return; +}); +stmt("switch", function () { + let dups = []; + let last; + let stmts; + const the_cases = []; + let the_disrupt = true; + const the_switch = token; + not_top_level(the_switch); + if (functionage.finally > 0) { + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token.free = true; + the_switch.expression = expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + (function major() { + const the_case = next_token; + the_case.arity = "statement"; + the_case.expression = []; + (function minor() { + advance("case"); + token.switch = true; + const exp = expression(0); + if (dups.some(function (thing) { + return are_similar(thing, exp); + })) { + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (next_token.id === "case") { + return minor(); + } + }()); + stmts = statements(); + if (stmts.length < 1) { + warn("expected_statements_a"); + return; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "break;", + artifact(next_token) + ); + } + if (next_token.id === "case") { + return major(); + } + }()); + dups = undefined; + if (next_token.id === "default") { + const the_default = next_token; + advance("default"); + token.switch = true; + advance(":"); + the_switch.else = statements(); + if (the_switch.else.length < 1) { + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + const the_last = the_switch.else[the_switch.else.length - 1]; + if (the_last.id === "break" && the_last.label === undefined) { + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; +}); +stmt("throw", function () { + const the_throw = token; + the_throw.disrupt = true; + the_throw.expression = expression(10); + semicolon(); + if (functionage.try > 0) { + warn("unexpected_a", the_throw); + } + return the_throw; +}); +stmt("try", function () { + let the_catch; + let the_disrupt; + const the_try = token; + if (functionage.try > 0) { + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (next_token.id === "catch") { + let ignored = "ignore"; + the_catch = next_token; + the_try.catch = the_catch; + advance("catch"); + advance("("); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + if (next_token.id !== "ignore") { + ignored = undefined; + the_catch.name = next_token; + enroll(next_token, "exception", true); + } + advance(); + advance(")"); + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "catch", + artifact(next_token) + ); + + } + if (next_token.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; +}); +stmt("var", do_var); +stmt("while", function () { + const the_while = token; + not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; +}); +stmt("with", function () { + stop("unexpected_a", token); +}); + +ternary("?", ":"); + +// Ambulation of the parse tree. + +function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; +} + +function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all of the +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + } + }; +} + +const posts = empty(); +const pres = empty(); +const preaction = action(pres); +const postaction = action(posts); +const preamble = amble(pres); +const postamble = amble(posts); + +function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.id === "function") { + walk_statement(thing.block); + } + if (thing.arity === "pre" || thing.arity === "post") { + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + warn("unexpected_statement_a", thing); + } + postamble(thing); + } + } +} + +function walk_statement(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_statement); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + ) { + warn( + (thing.id === "(string)" && thing.value === "use strict") + ? "unexpected_a" + : "unexpected_expression_a", + thing + ); + } + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + } +} + +function lookup(thing) { + if (thing.arity === "variable") { + +// Look up the variable in the current context. + + let the_variable = functionage.context[thing.id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable === undefined) { + stack.forEach(function (outer) { + const a_variable = outer.context[thing.id]; + if ( + a_variable !== undefined + && a_variable.role !== "label" + ) { + the_variable = a_variable; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (the_variable === undefined) { + if (declared_globals[thing.id] === undefined) { + warn("undeclared_a", thing); + return; + } + the_variable = { + dead: false, + parent: global, + id: thing.id, + init: true, + role: "variable", + used: 0, + writable: false + }; + global.context[thing.id] = the_variable; + } + the_variable.closure = true; + functionage.context[thing.id] = the_variable; + } else if (the_variable.role === "label") { + warn("label_a", thing); + } + if ( + the_variable.dead + && ( + the_variable.calls === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + ) { + warn("out_of_scope_a", thing); + } + return the_variable; + } +} + +function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); +} + +function preaction_function(thing) { + if (thing.arity === "statement" && blockage.body !== true) { + warn("unexpected_a", thing); + } + if (thing.level === 1) { + if ( + module_mode === true + || global.strict !== undefined + || thing.complex + ) { + if (thing.id !== "=>" && thing.block.strict !== undefined) { + warn("unexpected_a", thing.block.strict); + } + } else { + if (thing.block.strict === undefined) { + warn("use_strict", thing); + } + } + } + stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); +} + +function bitwise_check(thing) { + if (!option.bitwise && bitwiseop[thing.id] === true) { + warn("unexpected_a", thing); + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + warn("unexpected_a", thing); + } +} + +function pop_block() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); +} + +function activate(name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.init = true; + } + } + blockage.live.push(name); +} + +function action_var(thing) { + thing.names.forEach(activate); +} + +preaction("assignment", bitwise_check); +preaction("binary", bitwise_check); +preaction("binary", function (thing) { + if (relationop[thing.id] === true) { + const left = thing.expression[0]; + const right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + warn("expected_string_a", right); + } + } else { + const value = right.value; + if (value === "null" || value === "undefined") { + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + warn("expected_type_string_a", right, value); + } + } + } + } +}); +preaction("binary", "==", function (thing) { + warn("expected_a_b", thing, "===", "=="); +}); +preaction("binary", "!=", function (thing) { + warn("expected_a_b", thing, "!==", "!="); +}); +preaction("binary", "=>", preaction_function); +preaction("binary", "||", function (thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + warn("and", thang); + } + }); +}); +preaction("binary", "(", function (thing) { + const left = thing.expression[0]; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + const parent = functionage.name.parent; + if (parent) { + const left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + && left_variable.dead + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } +}); +preaction("binary", "in", function (thing) { + warn("infix_in", thing); +}); +preaction("binary", "instanceof", function (thing) { + warn("unexpected_a", thing); +}); +preaction("binary", ".", function (thing) { + if (thing.expression.new) { + thing.new = true; + } +}); +preaction("statement", "{", function (thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; +}); +preaction("statement", "for", function (thing) { + if (thing.name !== undefined) { + const the_variable = lookup(thing.name); + if (the_variable !== undefined) { + the_variable.init = true; + if (!the_variable.writable) { + warn("bad_assignment_a", thing.name); + } + } + } + walk_statement(thing.initial); +}); +preaction("statement", "function", preaction_function); +preaction("unary", "~", bitwise_check); +preaction("unary", "function", preaction_function); +preaction("variable", function (thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } +}); + +function init_variable(name) { + const the_variable = lookup(name); + if (the_variable !== undefined) { + if (the_variable.writable) { + the_variable.init = true; + return; + } + } + warn("bad_assignment_a", name); +} + +postaction("assignment", "+=", function (thing) { + let right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } +}); +postaction("assignment", function (thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + if (thing.id === "=") { + if (thing.names !== undefined) { + if (Array.isArray(thing.names)) { + thing.names.forEach(init_variable); + } else { + init_variable(thing.names); + } + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.writable !== true) { + warn("bad_assignment_a", lvalue); + } + } + const right = syntax[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + warn("unexpected_a", thing.expression[1]); + } + } +}); + +function postaction_function(thing) { + delete functionage.finally; + delete functionage.loop; + delete functionage.switch; + delete functionage.try; + functionage = stack.pop(); + if (thing.wrapped) { + warn("unexpected_parens", thing); + } + return pop_block(); +} + +postaction("binary", function (thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || are_similar(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option.convert) { + if (thing.expression[0].value === "") { + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === ".") { + if (thing.expression.id === "RegExp") { + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } +}); +postaction("binary", "&&", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "||", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "=>", postaction_function); +postaction("binary", "(", function (thing) { + let left = thing.expression[0]; + let the_new; + let arg; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option.eval) { + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + warn( + "expected_a_before_b", + left, + "new", + artifact(left) + ); + } + } + } else if (left.id === ".") { + let cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + cack = !cack; + } + if (rx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + warn("unexpected_a", the_new); + } else { + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + const paren = left.expression; + if (paren.id === "(") { + const array = paren.expression; + if (array.length === 1) { + const new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } +}); +postaction("binary", "[", function (thing) { + if (thing.expression[0].id === "RegExp") { + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + warn("weird_expression_a", thing.expression[1]); + } +}); +postaction("statement", "{", pop_block); +postaction("statement", "const", action_var); +postaction("statement", "export", top_level_only); +postaction("statement", "for", function (thing) { + walk_statement(thing.inc); +}); +postaction("statement", "function", postaction_function); +postaction("statement", "import", function (the_thing) { + const name = the_thing.name; + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return top_level_only(the_thing); +}); +postaction("statement", "let", action_var); +postaction("statement", "try", function (thing) { + if (thing.catch !== undefined) { + const the_name = thing.catch.name; + if (the_name !== undefined) { + const the_variable = functionage.context[the_name.id]; + the_variable.dead = false; + the_variable.init = true; + } + walk_statement(thing.catch.block); + } +}); +postaction("statement", "var", action_var); +postaction("ternary", function (thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || are_similar(thing.expression[1], thing.expression[2]) + ) { + warn("unexpected_a", thing); + } else if (are_similar(thing.expression[0], thing.expression[1])) { + warn("expected_a_b", thing, "||", "?"); + } else if (are_similar(thing.expression[0], thing.expression[2])) { + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + warn("wrap_condition", thing.expression[0]); + } +}); +postaction("unary", function (thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option.convert) { + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } +}); +postaction("unary", "function", postaction_function); +postaction("unary", "+", function (thing) { + if (!option.convert) { + warn("expected_a_b", thing, "Number(...)", "+"); + } + const right = thing.expression; + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } +}); + +function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + if (id !== "ignore") { + const name = the_function.context[id]; + if (name.parent === the_function) { + if ( + name.used === 0 + && ( + name.role !== "function" + || name.parent.arity !== "unary" + ) + ) { + warn("unused_a", name); + } else if (!name.init) { + warn("uninitialized_a", name); + } + } + } + }); +} + +function uninitialized_and_unused() { + +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (module_mode === true || option.node) { + delve(global); + } + functions.forEach(delve); +} + +// Go through the token list, looking at usage of whitespace. + +function whitage() { + let closer = "(end)"; + let free = false; + let left = global; + let margin = 0; + let nr_comments_skipped = 0; + let open = true; + let right; + + function expected_at(at) { + warn( + "expected_a_at_b_c", + right, + artifact(right), + fudge + at, + artifact_column(right) + ); + } + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function no_space() { + if (left.line === right.line) { + if (left.thru !== right.from && nr_comments_skipped === 0) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (open) { + const at = ( + free + ? margin + : margin + 8 + ); + if (right.from < at) { + expected_at(at); + } + } else { + if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (free) { + if (right.from < margin) { + expected_at(margin); + } + } else { + const mislaid = ( + stack.length > 0 + ? stack[stack.length - 1].right + : undefined + ); + if (!open && mislaid !== undefined) { + warn( + "expected_a_next_at_b", + mislaid, + artifact(mislaid.id), + margin + 4 + fudge + ); + } else if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + stack = []; + tokens.forEach(function (the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + + const new_closer = opener[left.id]; + if (typeof new_closer === "string") { + if (new_closer !== right.id) { + stack.push({ + closer: closer, + free: free, + margin: margin, + open: open, + right: right + }); + closer = new_closer; + if (left.line !== right.line) { + free = closer === ")" && left.free; + open = true; + margin += 4; + if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (right.switch) { + at_margin(-4); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + free = false; + open = false; + no_space_only(); + } + } else { + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like {]) have already been detected. + + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } + } else { + +// If right is a closer, then pop the previous state. + + if (right.id === closer) { + const previous = stack.pop(); + margin = previous.margin; + if (open && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + closer = previous.closer; + free = previous.free; + open = previous.open; + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-4); + } else if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + one_space(); + } else { + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + at_margin(0); + } else { + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + no_space(); + } else if ( + left.id === "." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + ) { + no_space_only(); + } else if (right.id === ".") { + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } else if (left.id === ";") { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + one_space_only(); + } else if (right.statement === true) { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.id === "var" + || left.id === "const" + || left.id === "let" + ) { + stack.push({ + closer: closer, + free: free, + margin: margin, + open: open + }); + closer = ";"; + free = false; + open = left.open; + if (open) { + margin = margin + 4; + at_margin(0); + } else { + one_space_only(); + } + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +// The jslint function itself. + +export default function jslint( + source = "", + option_object = empty(), + global_array = [] +) { + try { + warnings = []; + option = Object.assign(empty(), option_object); + anon = "anonymous"; + block_stack = []; + declared_globals = empty(); + directive_mode = true; + directives = []; + early_stop = true; + exports = empty(); + froms = []; + fudge = ( + option.fudge + ? 1 + : 0 + ); + functions = []; + global = { + id: "(global)", + body: true, + context: empty(), + from: 0, + level: 0, + line: 0, + live: [], + loop: 0, + switch: 0, + thru: 0 + }; + blockage = global; + functionage = global; + json_mode = false; + mega_mode = false; + module_mode = false; + next_token = global; + property = empty(); + stack = []; + tenure = undefined; + token = global; + token_nr = 0; + var_mode = undefined; + populate(standard, declared_globals, false); + populate(global_array, declared_globals, false); + Object.keys(option).forEach(function (name) { + if (option[name] === true) { + const allowed = allowed_option[name]; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } + }); + tokenize(source); + advance(); + if (json_mode) { + tree = json_value(); + advance("(end)"); + } else { + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option.browser) { + if (next_token.id === ";") { + advance(";"); + } + } else { + +// If we are not in a browser, then the file form of strict pragma may be used. + + if ( + next_token.value === "use strict" + ) { + global.strict = next_token; + advance("(string)"); + advance(";"); + } + } + tree = statements(); + advance("(end)"); + functionage = global; + walk_statement(tree); + if (module_mode && global.strict !== undefined) { + warn("unexpected_a", global.strict); + } + if (warnings.length === 0) { + uninitialized_and_unused(); + if (!option.white) { + whitage(); + } + } + } + if (!option.browser) { + directives.forEach(function (comment) { + if (comment.directive === "global") { + warn("missing_browser", comment); + } + }); + } + early_stop = false; + } catch (e) { + if (e.name !== "JSLintError") { + warnings.push(e); + } + } + return { + directives, + edition: "2018-09-13", + exports, + froms, + functions, + global, + id: "(JSLint)", + json: json_mode, + lines, + module: module_mode === true, + ok: warnings.length === 0 && !early_stop, + option, + property, + stop: early_stop, + tokens, + tree, + warnings: warnings.sort(function (a, b) { + return a.line - b.line || a.column - b.column; + }) + }; +}; diff --git a/branch.jslint_pass_itself_without_long_option/report.js b/branch.jslint_pass_itself_without_long_option/report.js new file mode 100644 index 000000000..f123e9055 --- /dev/null +++ b/branch.jslint_pass_itself_without_long_option/report.js @@ -0,0 +1,221 @@ +// report.js +// 2018-07-29 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Generate JSLint HTML reports. + +/*property + closure, column, context, edition, error, exports, filter, forEach, froms, + fudge, function, functions, global, id, isArray, join, json, keys, length, + level, line, lines, message, module, name, names, option, parameters, + parent, property, push, replace, role, signature, sort, stop, warnings +*/ + +const rx_amp = /&/g; +const rx_gt = />/g; +const rx_lt = / with less destructive entities. + + return String( + string + ).replace( + rx_amp, + "&" + ).replace( + rx_lt, + "<" + ).replace( + rx_gt, + ">" + ); +} + +export default { + error: function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + let fudge = Number(Boolean(data.option.fudge)); + let output = []; + if (data.stop) { + output.push("
    JSLint was unable to finish.
    "); + } + data.warnings.forEach(function (warning) { + output.push( + "
    ", + entityify(warning.line + fudge), + ".", + entityify(warning.column + fudge), + "
    ", + entityify(warning.message), + "
    ", + entityify(data.lines[warning.line] || ""), + "" + ); + }); + return output.join(""); + }, + + function: function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + let fudge = Number(Boolean(data.option.fudge)); + let mode = ( + data.module + ? "module" + : "global" + ); + let output = []; + + if (data.json) { + return ( + data.warnings.length === 0 + ? "
    JSON: good.
    " + : "
    JSON: bad.
    " + ); + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + let global = Object.keys(data.global.context).sort(); + let froms = data.froms.sort(); + let exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + let context = the_function.context; + let list = Object.keys(context); + output.push( + "
    ", + entityify(the_function.line + fudge), + "
    ", + ( + the_function.name === "=>" + ? entityify(the_function.signature) + " =>" + : ( + typeof the_function.name === "string" + ? "«" + entityify(the_function.name) + "»" + : "" + entityify(the_function.name.id) + "" + ) + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + let params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.parent === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.parent === the_function + ); + })); + detail("outer", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.parent !== the_function + && the_variable.parent.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].parent.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + output.push( + "
    JSLint edition ", + entityify(data.edition), + "
    " + ); + return output.join(""); + }, + + property: function property_directive(data) { + +// Produce the /*property*/ directive. + + let not_first = false; + let output = ["/*property"]; + let length = 1111; + let properties = Object.keys(data.property); + + if (properties.length > 0) { + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length >= 80) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); + } + } +}; diff --git a/branch.json_mode_ignore_long_lines/README b/branch.json_mode_ignore_long_lines/README new file mode 100644 index 000000000..f46e91069 --- /dev/null +++ b/branch.json_mode_ignore_long_lines/README @@ -0,0 +1,42 @@ +JSLint, The JavaScript Code Quality Tool + +Douglas Crockford +douglas@crockford.com + +2018-05-28 + +jslint.js contains the jslint function. It parses and analyzes a source file, +returning an object with information about the file. It can also take an object +that sets options. + +index.html runs the jslint.js function in a web page. The page also depends +browser.js and report.js and jslint.css. + +jslint.css provides styling for index.html. + +browser.js runs the web user interface. + +report.js generates the results reports in HTML. + +help.html describes JSLint's usage. Please read it. + +function.html describes the jslint function and the results it produces. + +Please direct questions and comments to +https://plus.google.com/communities/104441363299760713736. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[http://www.crockford.com/wrrrld/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. diff --git a/branch.json_mode_ignore_long_lines/browser.js b/branch.json_mode_ignore_long_lines/browser.js new file mode 100644 index 000000000..5086fed85 --- /dev/null +++ b/branch.json_mode_ignore_long_lines/browser.js @@ -0,0 +1,158 @@ +// browser.js +// 2018-06-16 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +/*jslint + browser +*/ + +/*property + checked, create, disable, display, error, focus, forEach, function, + getElementById, innerHTML, join, length, map, onchange, onclick, onscroll, + property, querySelectorAll, scrollTop, select, split, style, title, value +*/ + +import jslint from "./jslint.js"; +import report from "./report.js"; + +// This is the web script companion file for JSLint. It includes code for +// interacting with the browser and displaying the reports. + +let rx_crlf = /\n|\r\n?/; +let rx_separator = /[\s,;'"]+/; + +let fudge_unit = 0; + +const aux = document.getElementById("JSLINT_AUX"); +const boxes = document.querySelectorAll("[type=checkbox]"); +const fudge = document.getElementById("JSLINT_FUDGE"); +const global = document.getElementById("JSLINT_GLOBAL"); +const number = document.getElementById("JSLINT_NUMBER"); +const property = document.getElementById("JSLINT_PROPERTY"); +const property_fieldset = document.getElementById("JSLINT_PROPERTYFIELDSET"); +const report_field = document.getElementById("JSLINT_REPORT"); +const report_list = document.getElementById("JSLINT_REPORT_LIST"); +const source = document.getElementById("JSLINT_SOURCE"); +const select = document.getElementById("JSLINT_SELECT"); +const warnings = document.getElementById("JSLINT_WARNINGS"); +const warnings_list = document.getElementById("JSLINT_WARNINGS_LIST"); + +function show_numbers() { + number.value = source.value.split(rx_crlf).map(function (ignore, index) { + return index + fudge_unit; + }).join("\n"); +} + +function clear() { + aux.style.display = "none"; + number.value = ""; + property.value = ""; + property_fieldset.style.display = "none"; + report_field.style.display = "none"; + report_list.innerHTML = ""; + source.focus(); + source.value = ""; + warnings.style.display = "none"; + warnings_list.innerHTML = ""; +} + +function fudge_change() { + fudge_unit = Number(fudge.checked); + show_numbers(); +} + +function clear_options() { + boxes.forEach(function (node) { + node.checked = false; + }); + fudge_change(); + global.innerHTML = ""; +} + +function call_jslint() { + +// First build the option object. + + let option = Object.create(null); + boxes.forEach(function (node) { + if (node.checked) { + option[node.title] = true; + } + }); + +// Call JSLint with the source text, the options, and the predefined globals. + + let global_string = global.value; + let result = jslint( + source.value, + option, + ( + global_string === "" + ? undefined + : global_string.split(rx_separator) + ) + ); + +// Generate the reports. + + let error_html = report.error(result); + let function_html = report.function(result); + let property_text = report.property(result); + +// Display the reports. + + warnings_list.innerHTML = error_html; + warnings.style.display = ( + error_html.length === 0 + ? "none" + : "block" + ); + + report_list.innerHTML = function_html; + report_field.style.display = "block"; + if (property_text) { + property.value = property_text; + property_fieldset.style.display = "block"; + property.scrollTop = 0; + select.disable = false; + } else { + property_fieldset.style.display = "none"; + select.disable = true; + } + aux.style.display = "block"; + source.select(); +} + +fudge.onchange = fudge_change; + +source.onchange = function (ignore) { + show_numbers(); +}; + +source.onscroll = function () { + let ss = source.scrollTop; + number.scrollTop = ss; + let sn = number.scrollTop; + if (ss > sn) { + show_numbers(); + number.scrollTop = ss; + } +}; + +document.querySelectorAll("[name='JSLint']").forEach(function (node) { + node.onclick = call_jslint; +}); + +document.querySelectorAll("[name='clear']").forEach(function (node) { + node.onclick = clear; +}); + +document.getElementById("JSLINT_SELECT").onclick = function () { + property.focus(); + property.select(); +}; +document.getElementById("JSLINT_CLEAR_OPTIONS").onclick = clear_options; + +fudge_change(); +source.select(); +source.focus(); diff --git a/branch.json_mode_ignore_long_lines/function.html b/branch.json_mode_ignore_long_lines/function.html new file mode 100644 index 000000000..8d7287cbc --- /dev/null +++ b/branch.json_mode_ignore_long_lines/function.html @@ -0,0 +1,612 @@ + + + + + + + + +JSLint: jslint function + + + +
    JSLint
    + +
    The jslint Function
    +
    +

    JSLint

    +

    JSLint is delivered as a set of files:

    +
    + + + + + + + + + + + + + + +
    FilenameContent
    browser.jsBrowser based UI.
    function.htmlThis page that you are looking at right now.
    help.htmlUsing jslint as a single page JSLint application.
    index.htmlSingle page JSLint application that uses the + js files.
    jslint.jsThe jslint function.
    report.jsThe report object, containing report generator functions for + HTML.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    +
    + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring"(JSLint)"
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    parenttokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    parenttokenThe function in which the variable was declared.variable
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable can be assigned to.variable
    +

    Reports

    +

    The report object contains three functions that all take a + result from the jslint function as input. + report.js has no other dependence on other files.

    + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    +
    + +
    + + + + + +
    + + diff --git a/branch.json_mode_ignore_long_lines/help.html b/branch.json_mode_ignore_long_lines/help.html new file mode 100644 index 000000000..ee236ff95 --- /dev/null +++ b/branch.json_mode_ignore_long_lines/help.html @@ -0,0 +1,841 @@ + + + + + + + + + +JSLint: Help + + + +
    JSLint
    + +
    Help
    +
    +
    Il semble que la perfection soit atteinte non quand il n’y a + plus rien à ajouter, mais quand il n’y a plus rien à + retrancher.
    +
    + Antoine de Saint-Exupéry +
    +
    + Terre des Hommes (1939) +
    +

    Good Parts

    +

    The Principle of the Good Parts is

    +
    If a feature is sometimes useful and sometimes dangerous and if + there is a better option then always use the better option.
    +

    JSLint is a JavaScript program that looks for problems in + JavaScript programs. It is a code quality tool.

    +

    When C was + a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    +

    As the language matured, the definition of the language was + strengthened to eliminate some insecurities, and compilers got better + at issuing warnings. lint is no longer needed.

    +

    JavaScript is a + young-for-its-age language. It was originally intended to do small tasks in + webpages, tasks for which Java was too heavy and clumsy. But JavaScript is + a surprisingly capable language, and it is now being used in larger + projects. Many of the features that were intended to make the language easy + to use are troublesome when projects become complicated. A lint + for JavaScript is needed: JSLint, a JavaScript syntax checker + and validator.

    +

    JSLint takes a JavaScript source and scans it. If it finds a + problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by the ECMAScript Programming Language + Standard (the strangely named document that governs JavaScript). + JSLint will reject most legal programs. It is a higher + standard.

    +

    JavaScript is a sloppy language, but hidden deep inside there is an elegant, + better language. JSLint helps you to program in that better + language and to avoid most of the slop. JSLint will reject + programs that browsers will accept because JSLint is concerned + with the quality of your code and browsers are not. You should gladly accept + all of JSLint's advice.

    +

    JSLint can operate on JavaScript source + or JSON text.

    +

    ECMAScript Sixth Edition

    +

    Some of + ES6’s features are good, so JSLint will recognize the good + parts of ES6.

    +

    Currently, these features are recognized:

    +
      +
    • The ... ellipsis marker in parameter + lists and argument lists, replacing the arguments object + for variadic functions.
    • +
    • The let statement, which is like the var + statement except that it respects block scope. + You may use let or var but not both.
    • +
    • The const statement is like the let statement + except that it disallows the use of assignment on the variable, although + if the value of the variable is mutable, it can still be mutated. + const is preferred to let. +
    • Destructuring of arrays and objects is allowed in parameter lists and on + the left side of let, and const, but not + var or assignment statements, and not deep destructuring + or eliding.
    • +
    • Enhanced object literals, providing shorter forms for function + declaration and properties that are initialized by variables with the + same name.
    • +
    • The fat arrow => fart functions.
    • +
    • The simplest forms of import and export.
    • +
    • `Megastring` literals, but not nested + `megastring` literals.
    • +
    • New global functions, such as Map, Set, + WeakMap, and WeakSet.
    • +
    • 0b- and 0o- number literals.
    • +
    +

    The most important new feature of ES6 is proper tail calls. This has no + new syntax, so JSLint doesn’t see it. But it makes recursion + much more attractive, which makes loops, particularly for + loops, much less attractive.

    +

    import export

    +

    The ES6 module feature will be an important improvement over JavaScript’s + global variables as a means of linking separate files together. + JSLint recognizes a small but essential subset of the module + syntax.

    +
    +

    import name from stringliteral;

    +

    import {name} from stringliteral;

    +

    export default function () {};

    +

    export default function name() {};

    +

    export default expression;

    +

    export function name() {}

    +

    export name; // where name is const

    +

    export {name};

    +
    +

    Directives

    +

    JSLint provides three directives that may be placed in a + file to manage JSLint’s behavior. Use of these directives is + optional. If they are used, they should be placed in a source file before + the first statement. They are written in the form of a comment, where the + directive name is placed immediately after the opening of the comment before + any whitespace. The three directives are global, + jslint, and property. Directives in a file are + stronger than options selected from the UI or passed with the option object.

    +

    /*global*/

    +

    The /*global*/ directive is used to specify a set of globals + (usually functions and objects containing functions) that are available to + this file. This was commonly used in browsers to link source files together + before ES6 modules appeared. Use of global variables is strongly + discouraged, but unfortunately web browsers require their use. The + /*global*/ directive can only be used when the + Assume a browser option is selected. +

    Each of the names listed will indicate a read-only global variable. The names + are separated by , comma. This directive + should not be used if import or export is used. + This directive inhibits warnings, but it does not declare the names in the + execution environment. +

    For example: +

    /*global +
    ADSAFE, report, jslint
    +*/
    +

    instructs JSLint to not give warnings about the global + variables ADsafe, report, and jslint. + However, if any of those names are expected to be supplied by other files + and those other files fail to do so, then execution errors will result. It + is usually better to use top-level variable declarations instead: +

        var ADSAFE;
    +    var report;
    +    var jslint; 
    +

    Using var in this way allows comparing a global variable to the + undefined value to determine whether it is has been used in the global context.

    +

    /*jslint*/

    +

    The /*jslint*/ directive allows for the control of several + options. These options can also be set from the + JSLint.com user interface.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Tolerate bitwise operatorsbitwisetrue if bitwise operators should be allowed. The + bitwise operators are rarely used in JavaScript programs, so it + is usually more likely that & is a mistyping of + && than that it indicates a bitwise and. + JSLint will give warnings on the bitwise operators + unless this option is selected.
    Assume a browser browsertrue if the standard browser globals should be + predefined. This option will reject the use of + import and export. This option also + disallows the file form of the "use + strict" pragma. It does not supply + self; you will have to request that unnecessary + alias of the dreaded global object yourself. It adds the same + globals as this directive: +
    /*global +
    + clearInterval, clearTimeout, document, event, FileReader, + FormData, history, localStorage, location, name, navigator, + screen, sessionStorage, setInterval, setTimeout, Storage, + URL, window, XMLHttpRequest +
    + */
    +
    Tolerate conversion operatorsconverttrue if !!, + prefix, + and concatenation with "" are allowed. + The Boolean, Number, and + String functions are preferred
    Assume CouchDBcouchtrue if + Couch DB + globals should be predefined. It adds the same globals as this + directive: +
    /*global +
    emit, getRow, isArray, log, provides, registerType, require, + send, start, sum, toJSON
    + */
    Assume in developmentdeveltrue if browser globals that are useful in + development should be predefined, and if debugger + statements and TODO comments + should be allowed. It adds the + same globals as this directive: +
    /*global +
    alert, confirm, console, prompt
    + */
    Be sure to turn this option off before going + into production.
    Tolerate evalevaltrue if eval should be allowed. In the + past, the eval function was the most misused + feature of the language.
    Tolerate for statementfortrue if the for statement should be + allowed. It is almost always better to use the array methods + instead.
    Fudge the line and column numbersfudgetrue if the origin of a text file is line 1 column + 1. Programmers know that number systems start at zero. + Unfortunately, most text editors start numbering lines and + columns at one. JSLint will by default start + numbering at zero. Use this option to start the numbering at one + in its reports.
    Tolerate get and setgetsettrue if accessor properties are allowed in object literals.
    Tolerate long source lineslongtrue if a line can contain more than 80 characters.
    Tolerate multiple variables declarations per statementmultivartrue if a var, let, or + const statement can declare two or more variables + in a single statement.
    Assume Node.jsnodetrue if Node.js globals should be predefined. It + will predefine globals that are used in the Node.js environment. + It adds the same globals as this directive: +
    /*global +
    + Buffer, clearImmediate, clearInterval, clearTimeout, console, exports, + module, process, require, setImmediate, setInterval, setTimeout, URL, + URLSearchParams, __dirname, __filename +
    + */
    Tolerate single quote stringssingletrue if 'single quote + should be allowed to enclose string literals.
    Tolerate this
    thistrue if this should be allowed.
    Tolerate whitespace messwhitetrue if the whitespace rules should be ignored.
    +

    For example:

    +
    /*jslint
    +    bitwise, node
    +*/
    + +

    /*property*/

    +

    The /*property*/ directive is used to declare a list of + property identifiers that are used in the file. Each property name in the + program is looked up in this list. If a name is not found, that indicates an + error, most likely a typing error.

    +

    The list can also be used to evade some of JSLint’s naming + rules.

    +

    JSLint can build the /*property*/ list for you. At + the bottom of its report, JSLint displays a + /*property*/ directive. It contains all of the names that were + used with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. You + can copy the /*property*/ directive to the top of your + script file. JSLint will check the spelling of all property + names against the list. That way, you can have JSLint look for + misspellings for you.

    +

    For example,

    +
    /*property +
    charAt, slice, _$_
    + */
    +

    ignore

    +

    JSLint introduces a new reserved word: ignore. It + is used in parameter lists and in catch clauses to indicate a + parameter that will be ignored. Unused warnings will not be produced for + ignore. +

    function handler(ignore, value) {
    +    return do_something_useful(value);
    +}
    +

    var let const

    +

    JavaScript provides three statements for declaring variables: + var, let, and const. The + var statement suffers from a bad practice called hoisting. The + let statement does not do hoisting and respects block scope. + The const statement is like let except that it + marks the variable (but not its contents) as read only, making it an error + to attempt to assign to the variable. When given a choice, + const is the best, var is the worst.

    +

    JSLint uses the intersection of the var rules and the + let rules, and by doing so avoids the errors related to either. + A name should be declared only once in a function. It should be declared + before it is used. It should not be used outside of the block in which it is + declared. A variable should not have the same name as a variable or + parameter in an outer function. Do not mix var and + let. Declare one name per statement.

    +

    = == ===

    +

    Fortran made a terrible + mistake in using the equality operator as its assignment operator. That + mistake has been replicated in most languages since then. C compounded that + mistake by making == its equality operator. The visual + similarity is a source of errors. JavaScript compounded this further by + making == a type coercing comparison operator that produces + false positive results. This was mitigated by adding the === + operator, leaving the broken == operator in place.

    +

    JSLint attempts to minimize errors by the following rules:

    +

    == is not allowed. This avoids the false positives, and + increases the visual distance between = and ===. + Assignments are not allowed in expression position, and comparisons are not + allowed in statement position. This also reduces confusion. 

    +

    Semicolon

    +

    JavaScript uses a C-like syntax that requires the use of semicolons to + delimit certain statements. JavaScript attempts to make those semicolons + optional with an automatic semicolon insertion mechanism, but it does not + work very well. Automatic semicolon insertion was added to make + things easier for beginners. Unfortunately, it sometimes fails. Do not rely + on it unless you are a beginner.

    +

    JSLint expects that every statement will be followed by + ; except for for, function, + if, switch, try, and + while. JSLint does not expect to see unnecessary + semicolons, the empty statement, or empty blocks.

    +

    function =>

    +

    JavaScript has four syntactic forms for making function objects: function + statements, function expressions, enhanced object literals, and the + => fart operator.

    +

    The function statement creates a variable and assigns the function object to + it. It should be used in a file or function body, but not inside of a block.

    +
    function name(parameters) { +
    statements
    + }
    +

    The function expression unfortunately looks like the function statement. It + may appear anywhere that an expression may appear, but not in statement + position and not in a loop. It produces a function object but does not + create a variable in which to store it.

    +
    function (parameters) { +
    statements
    + }
    +

    The enhanced object literal provides an ES6 shorthand for creating a property + whose value is a function, saving you from having to type + : colon and function.

    +
    { +
    name(parameters) { +
    statements
    + }
    + }
    +

    Finally, ES6 provides an even shorter form of function expression that leaves + out the words function and return:

    +
    (parameters) => + expression
    +

    JSLint requires the parens around the parameters, and forbids a + { left brace after the + => fart to avoid syntactic ambiguity.

    +

    Comma

    +

    The , comma operator is unnecessary and can + mask programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as + an operator. It does not expect to see elided elements in array literals. A + comma should not appear after the last element of an array literal or object + literal.

    +

    Blocks

    +

    JSLint expects blocks with function, + if, switch, while, for, + do, and try statements and nowhere else.

    +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    +

    JavaScript allows an if to be written like this:

    +
    if (condition)
    +    statement;
    +

    That form is known to contribute to mistakes in projects where many + programmers are working on the same code. That is why JSLint + expects the use of a block:

    +
    if (condition) {
    +    statements;
    +}
    +

    Experience shows that this form is more resilient.

    +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call. All other expression statements are considered + to be errors.

    +

    for

    +

    JSLint does not recommend use of the for statement. + Use array methods like forEach instead. The for + option will suppress some warnings. The forms of for that + JSLint accepts are restricted, excluding the new ES6 forms.

    +

    for in

    +

    JSLint does not recommend use of the for + in statement. Use Object.keys instead.

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, it also + loops through all of the properties that were inherited through the + prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written + without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    for (name in object) {
    +    if (object.hasOwnProperty(name)) {
    +        ....
    +    }
    +}
    +

    Note that the above code will fail if the object contains a property + named hasOwnProperty. Use Object.keys instead.

    +

    switch

    +

    A common error in switch statements is to forget to place a + break statement after each case, resulting in unintended + fall-through. JSLint expects that the statement before the next + case or default is one of these: + break, return, or throw.

    +

    with

    +

    The with statement was intended to provide a shorthand in + accessing properties in deeply nested objects. Unfortunately, it behaves + very badly when setting new properties. Never use the with + statement.

    +

    JSLint does not expect to see a with statement.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that + labels will be distinct from vars and parameters.

    +

    Unreachable Code

    +

    JSLint expects that a return, break, + or throw statement will be followed by a + } right brace or case or + default.

    +

    Confusing Pluses and Minuses

    +

    JSLint expects that + will not be followed by + + or ++, and that - will not be + followed by - or --. A misplaced space can turn + + + into ++, an error that is difficult to see. + Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ increment and + -- decrement operators have been known to + contribute to bad code by encouraging excessive trickiness. They are second + only to faulty architecture in enabling to viruses and other security + menaces. Also, preincrement/postincrement confusion can produce off-by-one + errors that are extremely difficult to diagnose. Fortunately, they are also + complete unnecessary. There are better ways to add 1 to a variable.

    +

    It is best to avoid these operators entirely and rely on += and + -= instead.

    +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. + JSLint looks for problems that may cause portability problems. + It also attempts to resolve visual ambiguities by recommending explicit + escapement.

    +

    JavaScript’s syntax for regular expression literals overloads the + / slash character. To avoid ambiguity, + JSLint expects that the character preceding a regular + expression literal is a ( left paren or + = equal or + : colon or + , comma character.

    +

    this

    +
    Having this in the language makes it harder to talk + about the language. It is like pair programming with Abbott and Costello. +
    +

    Avoid using this. Warnings about this can be + suppressed with option.this.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the + new prefix. The new prefix creates a new object + based on the function's prototype, and binds that object to the + function's implied this parameter. If you neglect to use the + new prefix, no new object will be made and this + will be bound to the global object.

    +

    JSLint enforces the convention that constructor functions be + given names with initial uppercase. JSLint does not expect to + see a function invocation with an initial uppercase name unless it has the + new prefix. JSLint does not expect to see the + new prefix used with functions whose names do not start with + initial uppercase.

    +

    JSLint does not expect to see the wrapper forms + new Number, new String, new Boolean.

    +

    JSLint does not expect to see new Object. + Use Object.create(null) instead.

    +

    Whitespace

    +

    JSLint has a specific set of rules about the use of whitespace. + Where possible, these rules are consistent with centuries of good practice + with literary style.

    +

    The indentation increases by 4 spaces when the last token on a line is + { left brace, + [ left bracket, + ( left paren. The matching closing + token will be the first token on a line, restoring the previous + indentation.

    +

    The ternary operator can be visually confusing, so + ? question mark and + : colon always begin a line and increase + the indentation by 4 spaces.

    +
    return (the_token.id === "(string)" || the_token.id === "(number)")
    +    ? String(the_token.value)
    +    : the_token.id;
    +

    If . period is the first character on a line, the + indentation is increased by 4 spaces.

    +

    A var, let, or const statement will + also cause an indentation if it declares two or more variables, and if the + second variable is not on the same line as the start of the statement.

    +

    Long lines can be continued on the next line with 8 spaces added to the + current indentation. Those 8 spaces do not change the current indentation. + It is 8 to avoid confusion with ordinary indentation.

    +

    The word function is always followed with one space.

    +

    Clauses (case, catch, default, + else, finally) are not statements and so should + not be indented like statements.

    +

    Spaces are used to make things that are not invocations look less like + invocations.

    +

    Tabs and spaces should not be mixed. We should pick just one in order to + avoid the problems that come from having both. Personal preference is an + extremely unreliable criterion. Neither offers a powerful advantage over the + other. Fifty years ago, tab had the advantage of consuming less memory, but + Moore's Law has eliminated that advantage. Space has one clear advantage + over tab: there is no reliable standard for how many spaces a tab + represents, but it is universally accepted that a space occupies a space. So + use spaces. You can edit with tabs if you must, but make sure it is spaces + again before you commit. Maybe someday we will finally get a universal + standard for tabs, but until that day comes, the better choice is spaces.

    +

    Unsafe Characters

    +

    There are characters that are handled inconsistently in some browsers, and + so must be escaped when placed in strings.

    +
    \u0000-\u001f
    +\u007f-\u009f
    +\u00ad
    +\u0600-\u0604
    +\u070f
    +\u17b4
    +\u17b5
    +\u200c-\u200f
    +\u2028-\u202f
    +\u2060-\u206f
    +\ufeff
    +\ufff0-\uffff
    +

    Report

    +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    +
      +
    • The line number on which it starts.
    • +
    • The function’s name. In the case of anonymous functions, + JSLint will attempt to + «guess» the name.
    • +
    • The parameters.
    • +
    • Variables: The variables that are declared in the function.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Exceptions: The variables that are declared by catch + clauses of try statements.
    • +
    • Outer: Variables used by this function that are declared in + other functions.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements. Updates are announced at + https://plus.google.com/communities/104441363299760713736.

    +

    Try it

    +

    Try it. Paste your script + into the window and click the + + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    JSLint is written entirely in JavaScript, so it can run + anywhere that JavaScript (or even Java) can run.

    +

    Perfectly fine

    +

    JSLint was designed to reject code that some would consider to + be perfectly fine. The reason for this is that JSLint's + purpose is to help produce programs that are free of error. That is + difficult in any language and is especially hard in JavaScript. + JSLint attempts to help you increase the visual distance + between correct programs and incorrect programs, making the remaining errors + more obvious. JSLint will give warnings about things that are + not necessarily wrong in the current situation, but which have been observed + to mask or obscure errors. Avoid those things when there are better options + available.

    +

    It is dangerous out there. JSLint is here to help.

    +

    Warning

    +

    JSLint will hurt your feelings. Side effects may include + headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, + dry mouth, cleaner code, and a reduced error rate.

    +

    Further Reading

    + +
    +

    + Code Conventions for the JavaScript Programming Language.

    + + + + + +
    + + diff --git a/branch.json_mode_ignore_long_lines/index.html b/branch.json_mode_ignore_long_lines/index.html new file mode 100644 index 000000000..235f44b54 --- /dev/null +++ b/branch.json_mode_ignore_long_lines/index.html @@ -0,0 +1,125 @@ + + + + + + + + + + + +JSLint: The JavaScript Code Quality Tool + + +
    +
    Source
    +
    + + +
    +
    + Warnings +
    +
    +
    + Function Report +
    +
    +
    + Property Directive + +
    +
    + + + + +
    +
    Options +
    Assume... +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Global variables... + +
    +
    +
    +
    + + + + + +
    + + diff --git a/branch.json_mode_ignore_long_lines/jslint.css b/branch.json_mode_ignore_long_lines/jslint.css new file mode 100644 index 000000000..cbcf4c042 --- /dev/null +++ b/branch.json_mode_ignore_long_lines/jslint.css @@ -0,0 +1,341 @@ +@font-face { + font-family: 'Programma'; + font-weight: bold; + src: url('Programma-Bold.woff') format('woff'); +} +@font-face { + font-family: 'Daley'; + font-weight: bold; + src: url('Daley-Bold.woff') format('woff'); +} + +body { + background-color: antiquewhite; + color: black; + font-family: 'Daley', sans-serif; + margin: 0; + padding: 0; +} + +#JSLINT_TITLEBOX { + font-family: 'Daley', sans-serif; + margin: 0; + margin-left: 3%; + margin-right: 3%; + padding: 0; +} + +#JSLINT_TITLEBOX ul { + float: right; + font-size: 12pt; +} + +#JSLINT_TITLEBOX div { + color: darkslategray; + float: left; + font-size: 48pt; +} + +#JSLINT_ fieldset { + background-color: gainsboro; + border: 0; + clear: both; + margin-bottom: 1em; + margin-left: 3%; + margin-right: 3%; + margin-top: 1em; + padding: 0; + width: auto; +} +#JSLINT_ legend { + background-color: darkslategray; + border: 0; + color: white; + font-size: 100%; + font-style: normal; + font-weight: normal; + margin: 0; + padding-bottom: 0.25em; + padding-left: 0; + padding-right: 0; + padding-top: 0.25em; + text-align: center; + width: 100%; +} +#JSLINT_ button { + background-color: darkslategray; + border: 0; + color: white; + cursor: pointer; + font-family: 'Daley', sans-serif; + font-size: 100%; + font-style: normal; + margin-left: 1em; + margin-right: 1em; + padding-left: 1em; + padding-right: 1em; + padding-bottom: 0.25em; + padding-top: 0.25em; + text-align: center; +} +#JSLINT_ button:hover { + background-color: slategray; + color: white; + border: 0; +} + +#JSLINT_ button:active { + border: 0; + color: black; +} + +#JSLINT_ button:disabled { + background-color: gray; + border: 0; + color: gainsboro; +} + +a:visited { + color: #69565c; +} + +a:link { + color: black; +} + +#JSLINT_ input[type="text"] { + background-color: white; + border: 0; + color: black; + margin-bottom: 0.2em; + margin-right: 0.5em; + padding-left: 0.5em; + padding-right: 0.5em; + text-align: right; + width: 3em; +} + +#JSLINT_ textarea { + border: 0; + background-color: white; + border: 0; + font-family: Programma, monospace; + font-size: 100%; + font-weight: bold; + resize: none; +} + +#JSLINT_ textarea::selection { + background-color: yellow; + color: black; +} + +#JSLINT_NUMBER { + color: darkgray; + display: inline-block; + height: 4in; + margin: 2%; + margin-right: 0; + overflow: hidden; + padding-right: 0.5%; + text-align: right; + white-space: pre; + width: 4%; +} + +#JSLINT_SOURCE { + color: black; + display: inline-block; + font-size: 100%; + height: 4in; + margin: 2%; + margin-left: 0; + margin-right: 0; + overflow: auto; + padding-left: 0.5%; + white-space: pre; + width: 91%; +} + +#JSLINT_PROPERTY { + background-color: honeydew; + border: 0; + margin: 2%; + padding: 0.5%; + white-space: pre; + width: 95%; +} + +#JSLINT_GLOBAL { + white-space: normal; + width: 95%; +} +#JSLINT_ label { + font-family: sans-serif; + font-size: 90%; + padding-left: 0.25em; +} +#JSLINT_OPTIONS>div { + float: left; + margin: 0.5em; +} + +#JSLINT_ address { + color: black; + display: block; + float: right; + font-family: serif; + font-size: 90%; + margin-left: 1em; +} +#JSLINT_ dl { + background-color: cornsilk; + color: white; + margin: 0; + padding-bottom: 2pt; + padding-left: 1em; + padding-right: 1em; + padding-top: 2pt; +} +#JSLINT_ dfn { + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + margin-bottom: 2pt; +} +#JSLINT_ dt { + color: black; + display: block; + float: left; + font-family: serif; + font-size: 75%; + font-style: italic; + margin: 0; + width: 8em; + text-align: right; +} +#JSLINT_ dd { + color: black; + display: block; + font-family: Programma, monospace; + font-weight: bold; + margin-left: 8em; + padding-bottom: 2pt; +} +#JSLINT_WARNINGS>legend { + background-color: indianred; +} +#JSLINT_WARNINGS>div { + background-color: pink; + padding: 1em; +} +#JSLINT_WARNINGS cite { + color: black; + display: block; + font-family: serif; + font-size: 100%; + font-style: normal; + margin-bottom: 4pt; + margin-left: 20pt; + margin-right: 20pt; + margin-top: 4pt; + overflow-x: hidden; +} +#JSLINT_WARNINGS samp { + background-color: lavenderblush; + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + padding: 4pt; + margin-bottom: 0; + margin-left: 16pt; + margin-right: 16pt; + margin-top: 0; + white-space: pre-wrap; +} + +#JSLINT_REPORT center { + margin-top: 1em; +} + +#JSLINT_WARNINGS dl address { + color: black; + display: inline; + float: none; + font-size: 80%; + margin: 0; +} + +#JSLINT_REPORT>div { + padding: 1em; +} + +#JSLINT_ dl.level0 { + color: black; + background-color: white; +} + +#JSLINT_ dl.level1 { + color: black; + background-color: #ffffe0; /* yellow */ + margin-left: 1em; +} + +#JSLINT_ dl.level2 { + color: black; + background-color: #e0ffe0; /* green */ + margin-left: 2em; +} + +#JSLINT_ dl.level3 { + color: black; + background-color: #e0e0ff; /* blue */ + margin-left: 3em; +} + +#JSLINT_ dl.level4 { + background-color: #ffe0ff; /* purple */ + color: black; + margin-left: 4em; +} + +#JSLINT_ dl.level5 { + background-color: #ffe0e0; /* red */ + color: black; + margin-left: 5em; +} + +#JSLINT_ dl.level6 { + background-color: #ffe390; /* orange */ + color: black; + margin-left: 6em; +} + +#JSLINT_ dl.level7 { + background-color: #e0e0e0; /* gray */ + color: black; + margin-left: 7em; +} + +#JSLINT_ dl.level8 { + color: black; + margin-left: 8em; +} + +#JSLINT_ dl.level9 { + color: black; + margin-left: 9em; +} + +.none { + display: none; +} +.center { + text-align: center; +} diff --git a/branch.json_mode_ignore_long_lines/jslint.js b/branch.json_mode_ignore_long_lines/jslint.js new file mode 100644 index 000000000..1d6bf8cd2 --- /dev/null +++ b/branch.json_mode_ignore_long_lines/jslint.js @@ -0,0 +1,4914 @@ +// jslint.js +// 2018-08-11 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// jslint(source, option_object, global_array) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze, a string or an array of strings. +// option_object An object whose keys correspond to option names. +// global_array An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all of the functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// 1. If the source is a single string, split it into an array of strings. +// 2. Turn the source into an array of tokens. +// 3. Furcate the tokens into a parse tree. +// 4. Walk the tree, traversing all of the nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// 5. Check the whitespace between the tokens. + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*jslint long */ + +/*property + a, and, arity, assign, b, bad_assignment_a, bad_directive_a, + bad_get, bad_module_name_a, bad_option_a, bad_property_a, bad_set, + bitwise, block, body, browser, c, calls, catch, charCodeAt, closer, + closure, code, column, complex, concat, constant, context, convert, + couch, create, d, dead, default, devel, directive, directives, + disrupt, dot, duplicate_a, edition, ellipsis, else, empty_block, + escape_mega, eval, every, expected_a, expected_a_at_b_c, + expected_a_b, expected_a_b_from_c_d, expected_a_before_b, + expected_a_next_at_b, expected_digits_after_a, expected_four_digits, + expected_identifier_a, expected_line_break_a_b, + expected_regexp_factor_a, expected_space_a_b, expected_statements_a, + expected_string_a, expected_type_string_a, exports, expression, + extra, finally, flag, for, forEach, free, from, froms, fud, fudge, + function_in_loop, functions, g, getset, global, i, id, identifier, + import, inc, indexOf, infix_in, init, initial, isArray, isNaN, join, + json, keys, label, label_a, lbp, led, length, level, line, lines, + live, long, loop, m, margin, match, message, misplaced_a, + misplaced_directive_a, missing_browser, missing_m, module, multivar, + naked_block, name, names, nested_comment, new, node, not_label_a, + nr, nud, number_isNaN, ok, open, option, out_of_scope_a, parameters, + parent, pop, property, push, quote, redefinition_a_b, replace, + required_a_optional_b, reserved_a, right, role, search, signature, + single, slice, some, sort, split, statement, stop, strict, + subscript_a, switch, test, this, thru, toString, todo_comment, + tokens, too_long, too_many_digits, tree, try, type, u, + unclosed_comment, unclosed_mega, unclosed_string, undeclared_a, + unexpected_a, unexpected_a_after_b, unexpected_a_before_b, + unexpected_at_top_level_a, unexpected_char_a, unexpected_comment, + unexpected_directive_a, unexpected_expression_a, unexpected_label_a, + unexpected_parens, unexpected_space_a_b, unexpected_statement_a, + unexpected_trailing_space, unexpected_typeof_a, uninitialized_a, + unreachable_a, unregistered_property_a, unsafe, unused_a, + use_double, use_open, use_spaces, use_strict, used, value, var_loop, + var_switch, variable, warning, warnings, weird_condition_a, + weird_expression_a, weird_loop, weird_relation_a, white, + wrap_assignment, wrap_condition, wrap_immediate, wrap_parameter, + wrap_regexp, wrap_unary, wrapped, writable, y +*/ + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than {} because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +function populate(array, object = empty(), value = true) { + +// Augment an object by taking property names from an array of strings. + + array.forEach(function (name) { + object[name] = value; + }); + return object; +} + +const allowed_option = { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + bitwise: true, + browser: [ + "caches", "clearInterval", "clearTimeout", "document", "DOMException", + "Element", "Event", "event", "FileReader", "FormData", "history", + "localStorage", "location", "MutationObserver", "name", "navigator", + "screen", "sessionStorage", "setInterval", "setTimeout", "Storage", + "URL", "window", "Worker", "XMLHttpRequest" + ], + couch: [ + "emit", "getRow", "isArray", "log", "provides", "registerType", + "require", "send", "start", "sum", "toJSON" + ], + convert: true, + devel: [ + "alert", "confirm", "console", "prompt" + ], + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + multivar: true, + node: [ + "Buffer", "clearImmediate", "clearInterval", "clearTimeout", + "console", "exports", "module", "process", "require", + "setImmediate", "setInterval", "setTimeout", "URL", + "URLSearchParams", "__dirname", "__filename" + ], + single: true, + this: true, + white: true +}; + +const anticondition = populate([ + "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%", + "typeof", "(number)", "(string)" +]); + +// These are the bitwise operators. + +const bitwiseop = populate([ + "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=", + ">>>", ">>>=" +]); + +const escapeable = populate([ + "\\", "/", "`", "b", "f", "n", "r", "t" +]); + +const opener = { + +// The open and close pairs. + + "(": ")", // paren + "[": "]", // bracket + "{": "}", // brace + "${": "}" // mega +}; + +// The relational operators. + +const relationop = populate([ + "!=", "!==", "==", "===", "<", "<=", ">", ">=" +]); + +// This is the set of infix operators that require a space on each side. + +const spaceop = populate([ + "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/", + "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=", + ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" +]); + +const standard = [ + +// These are the globals that are provided by the language standard. + + "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI", + "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error", + "EvalError", "Float32Array", "Float64Array", "Generator", + "GeneratorFunction", "Int8Array", "Int16Array", "Int32Array", "Intl", + "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat", + "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", + "Set", "String", "Symbol", "SyntaxError", "System", "TypeError", + "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", + "URIError", "WeakMap", "WeakSet" +]; + +const bundle = { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + and: "The '&&' subexpression should be wrapped in parens.", + bad_assignment_a: "Bad assignment to '{a}'.", + bad_directive_a: "Bad directive '{a}'.", + bad_get: "A get function takes no parameters.", + bad_module_name_a: "Bad module name '{a}'.", + bad_option_a: "Bad option '{a}'.", + bad_property_a: "Bad property name '{a}'.", + bad_set: "A set function takes one parameter.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + escape_mega: "Unexpected escapement in mega literal.", + expected_a: "Expected '{a}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: ( + "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'." + ), + expected_a_before_b: "Expected '{a}' before '{b}'.", + expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.", + expected_digits_after_a: "Expected digits after '{a}'.", + expected_four_digits: "Expected four digits after '\\u'.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.", + expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.", + expected_space_a_b: "Expected one space between '{a}' and '{b}'.", + expected_statements_a: "Expected statements before '{a}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + expected_type_string_a: "Expected a type string and instead saw '{a}'.", + function_in_loop: "Don't make functions within a loop.", + infix_in: ( + "Unexpected 'in'. Compare with undefined, " + + "or use the hasOwnProperty method instead." + ), + label_a: "'{a}' is a statement label.", + misplaced_a: "Place '{a}' at the outermost level.", + misplaced_directive_a: ( + "Place the '/*{a}*/' directive before the first statement." + ), + missing_browser: "/*global*/ requires the Assume a browser option.", + missing_m: "Expected 'm' flag on a multiline regular expression.", + naked_block: "Naked block.", + nested_comment: "Nested comment.", + not_label_a: "'{a}' is not a label.", + number_isNaN: "Use Number.isNaN function to compare with NaN.", + out_of_scope_a: "'{a}' is out of scope.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + required_a_optional_b: ( + "Required parameter '{a}' after optional parameter '{b}'." + ), + reserved_a: "Reserved name '{a}'.", + subscript_a: "['{a}'] is better written in dot notation.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line is longer than 80 characters.", + too_many_digits: "Too many digits.", + unclosed_comment: "Unclosed comment.", + unclosed_mega: "Unclosed mega literal.", + unclosed_string: "Unclosed string.", + undeclared_a: "Undeclared '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_a_after_b: "Unexpected '{a}' after '{b}'.", + unexpected_a_before_b: "Unexpected '{a}' before '{b}'.", + unexpected_at_top_level_a: "Expected '{a}' to be in a function.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_directive_a: "When using modules, don't use directive '/*{a}'.", + unexpected_expression_a: ( + "Unexpected expression '{a}' in statement position." + ), + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_parens: "Don't wrap function literals in parens.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_statement_a: ( + "Unexpected statement '{a}' in expression position." + ), + unexpected_trailing_space: "Unexpected trailing space.", + unexpected_typeof_a: ( + "Unexpected 'typeof'. Use '===' to compare directly with {a}." + ), + uninitialized_a: "Uninitialized '{a}'.", + unreachable_a: "Unreachable '{a}'.", + unregistered_property_a: "Unregistered property name '{a}'.", + unsafe: "Unsafe character '{a}'.", + unused_a: "Unused '{a}'.", + use_double: "Use double quotes, not single quotes.", + use_open: ( + "Wrap a ternary expression in parens, " + + "with a line break after the left paren." + ), + use_spaces: "Use spaces, not tabs.", + use_strict: "This function needs a \"use strict\" pragma.", + var_loop: "Don't declare variables in a loop.", + var_switch: "Don't declare variables in a switch.", + weird_condition_a: "Weird condition '{a}'.", + weird_expression_a: "Weird expression '{a}'.", + weird_loop: "Weird loop.", + weird_relation_a: "Weird relation '{a}'.", + wrap_assignment: "Don't wrap assignment statements in parens.", + wrap_condition: "Wrap the condition in parens.", + wrap_immediate: ( + "Wrap an immediate function invocation in parentheses to assist " + + "the reader in understanding that the expression is the result " + + "of a function, and not the function itself." + ), + wrap_parameter: "Wrap the parameter in parens.", + wrap_regexp: "Wrap this regexp in parens to avoid confusion.", + wrap_unary: "Wrap the unary expression in parens." +}; + +// Regular expression literals: + +// supplant {variables} +const rx_supplant = /\{([^{}]*)\}/g; +// carriage return, carriage return linefeed, or linefeed +const rx_crlf = /\n|\r\n?/; +// unsafe characters that are silently deleted by one or more browsers +const rx_unsafe = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; +// identifier +const rx_identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; +const rx_module = /^[a-zA-Z0-9_$:.@\-\/]+$/; +const rx_bad_property = /^_|\$|Sync\$|_$/; +// star slash +const rx_star_slash = /\*\//; +// slash star +const rx_slash_star = /\/\*/; +// slash star or ending slash +const rx_slash_star_or_slash = /\/\*|\/$/; +// uncompleted work comment +const rx_todo = /\b(?:todo|TO\s?DO|HACK)\b/; +// tab +const rx_tab = /\t/g; +// directive +const rx_directive = /^(jslint|property|global)\s+(.*)$/; +const rx_directive_part = /^([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*(.*)$/; +// token (sorry it is so long) +const rx_token = /^((\s+)|([a-zA-Z_$][a-zA-Z0-9_$]*)|[(){}\[\]?,:;'"~`]|=(?:==?|>)?|\.+|[*\/][*\/=]?|\+[=+]?|-[=\-]?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<= "a" && string <= "z\uffff") + || (string >= "A" && string <= "Z\uffff") + ); +} + +function supplant(string, object) { + return string.replace(rx_supplant, function (found, filling) { + const replacement = object[filling]; + return ( + replacement !== undefined + ? replacement + : found + ); + }); +} + +let anon; // The guessed name for anonymous functions. +let blockage; // The current block. +let block_stack; // The stack of blocks. +let declared_globals; // The object containing the global declarations. +let directives; // The directive comments. +let directive_mode; // true if directives are still allowed. +let early_stop; // true if JSLint cannot finish. +let exports; // The exported names and values. +let froms; // The array collecting all import-from strings. +let fudge; // true if the natural numbers start with 1. +let functionage; // The current function. +let functions; // The array containing all of the functions. +let global; // The global object; the outermost context. +let json_mode; // true if parsing JSON. +let lines; // The array containing source lines. +let module_mode; // true if import or export was used. +let next_token; // The next token to be examined in the parse. +let option; // The options parameter. +let property; // The object containing the tallied property names. +let mega_mode; // true if currently parsing a megastring literal. +let stack; // The stack of functions. +let syntax; // The object containing the parser. +let token; // The current token being examined in the parse. +let token_nr; // The number of the next token. +let tokens; // The array of tokens. +let tenure; // The predefined property registry. +let tree; // The abstract parse tree. +let var_mode; // "var" if using var; "let" if using let. +let warnings; // The array collecting all generated warnings. + +// Error reportage functions: + +function artifact(the_token) { + +// Return a string representing an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); +} + +function artifact_line(the_token) { + +// Return the fudged line number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.line + fudge; +} + +function artifact_column(the_token) { + +// Return the fudged column number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.from + fudge; +} + +function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + const warning = { // ~~ + name: "JSLintError", + column: column, + line: line, + code: code + }; + if (a !== undefined) { + warning.a = a; + } + if (b !== undefined) { + warning.b = b; + } + if (c !== undefined) { + warning.c = c; + } + if (d !== undefined) { + warning.d = d; + } + warning.message = supplant(bundle[code] || code, warning); + warnings.push(warning); + return warning; +} + +function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); +} + +function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + if (the_token.warning === undefined) { + the_token.warning = warn_at( + code, + the_token.line, + the_token.from, + a || artifact(the_token), + b, + c, + d + ); + return the_token.warning; + } +} + +function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); +} + +// Tokenize: + +function tokenize(source) { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + +// If the source is not an array, then it is split into lines at the +// carriage return/linefeed. + + lines = ( + Array.isArray(source) + ? source + : source.split(rx_crlf) + ); + tokens = []; + + let char; // a popular character + let column = 0; // the column number of the next character + let first; // the first token + let from; // the starting column number of the token + let line = -1; // the line number of the next character + let nr = 0; // the next token number + let previous = global; // the previous token including comments + let prior = global; // the previous token excluding comments + let mega_from; // the starting column of megastring + let mega_line; // the starting line of megastring + let snippet; // a piece of string + let source_line; // the current line source string + + function next_line() { + +// Put the next line of source in source_line. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + let at; + column = 0; + line += 1; + source_line = lines[line]; + if (source_line !== undefined) { + at = source_line.search(rx_tab); + if (at >= 0) { + if (!option.white) { + warn_at("use_spaces", line, at + 1); + } + source_line = source_line.replace(rx_tab, " "); + } + at = source_line.search(rx_unsafe); + if (at >= 0) { + warn_at( + "unsafe", + line, + column + at, + "U+" + source_line.charCodeAt(at).toString(16) + ); + } + if (!option.white && source_line.slice(-1) === " ") { + warn_at( + "unexpected_trailing_space", + line, + source_line.length - 1 + ); + } + if ( + first + && !json_mode + && !option.long + && source_line.length > 80 + ) { + warn_at("too_long", line, 80); + } + } + return source_line; + } + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions snip, next_char, prev_char, +// some_digits, and escape help in the parsing of literals. + + function snip() { + +// Remove the last character from snippet. + + snippet = snippet.slice(0, -1); + } + + function next_char(match) { + +// Get the next character from the source line. Remove it from the source_line, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + return stop_at( + ( + char === "" + ? "expected_a" + : "expected_a_b" + ), + line, + column - 1, + match, + char + ); + } + if (source_line) { + char = source_line[0]; + source_line = source_line.slice(1); + snippet += char; + } else { + char = ""; + snippet += " "; + } + column += 1; + return char; + } + + function back_char() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the source_line. + + if (snippet) { + char = snippet.slice(-1); + source_line = char + source_line; + column -= 1; + snip(); + } else { + char = ""; + } + return char; + } + + function some_digits(rx, quiet) { + const result = source_line.match(rx); + if (result) { + char = result[1]; + column += char.length; + source_line = result[2]; + snippet += char; + } else { + char = ""; + if (!quiet) { + warn_at( + "expected_digits_after_a", + line, + column, + snippet + ); + } + } + return char.length; + } + + function escape(extra) { + next_char("\\"); + if (escapeable[char] === true) { + return next_char(); + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "u") { + if (next_char("u") === "{") { + if (json_mode) { + warn_at("unexpected_a", line, column - 1, char); + } + if (some_digits(rx_hexs) > 5) { + warn_at("too_many_digits", line, column - 1); + } + if (next_char() !== "}") { + stop_at("expected_a_before_b", line, column, "}", char); + } + return next_char(); + } + back_char(); + if (some_digits(rx_hexs, true) < 4) { + warn_at("expected_four_digits", line, column - 1); + } + return; + } + if (extra && extra.indexOf(char) >= 0) { + return next_char(); + } + warn_at("unexpected_a_before_b", line, column - 2, "\\", char); + } + + function make(id, value, identifier) { + +// Make the token object and append it to the tokens list. + + const the_token = { + from: from, + id: id, + identifier: Boolean(identifier), + line: line, + nr: nr, + thru: column + }; + tokens[nr] = the_token; + nr += 1; + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + directive_mode = false; + } + +// If the token is to have a value, give it one. + + if (value !== undefined) { + the_token.value = value; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option.white. + + if ( + previous.line === line + && previous.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (previous.id === "(comment)" || previous.id === "(regexp)") + ) { + warn( + "expected_space_a_b", + the_token, + artifact(previous), + artifact(the_token) + ); + } + if (previous.id === "." && id === "(number)") { + warn("expected_a_before_b", previous, "0", "."); + } + if (prior.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// The previous token is used to detect adjacency problems. + + previous = the_token; + +// The prior token is a previous token that was not a comment. The prior token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (previous.id !== "(comment)") { + prior = previous; + } + return the_token; + } + + function parse_directive(the_comment, body) { + +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + + const result = body.match(rx_directive_part); + if (result) { + let allowed; + const name = result[1]; + const value = result[2]; + if (the_comment.directive === "jslint") { + allowed = allowed_option[name]; + if ( + typeof allowed === "boolean" + || typeof allowed === "object" + ) { + if ( + value === "" + || value === "true" + || value === undefined + ) { + option[name] = true; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } else if (value === "false") { + option[name] = false; + } else { + warn("bad_option_a", the_comment, name + ":" + value); + } + } else { + warn("bad_option_a", the_comment, name); + } + } else if (the_comment.directive === "property") { + if (tenure === undefined) { + tenure = empty(); + } + tenure[name] = true; + } else if (the_comment.directive === "global") { + if (value) { + warn("bad_option_a", the_comment, name + ":" + value); + } + declared_globals[name] = false; + module_mode = the_comment; + } + return parse_directive(the_comment, result[3]); + } + if (body) { + return stop("bad_directive_a", the_comment, body); + } + } + + function comment(snippet) { + +// Make a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + + const the_comment = make("(comment)", snippet); + if (Array.isArray(snippet)) { + snippet = snippet.join(" "); + } + if (!option.devel && rx_todo.test(snippet)) { + warn("todo_comment", the_comment); + } + const result = snippet.match(rx_directive); + if (result) { + if (!directive_mode) { + warn_at("misplaced_directive_a", line, from, result[1]); + } else { + the_comment.directive = result[1]; + parse_directive(the_comment, result[2]); + } + directives.push(the_comment); + } + return the_comment; + } + + function regexp() { + +// Parse a regular expression literal. + + let multi_mode = false; + let result; + let value; + + function quantifier() { + +// Match an optional quantifier. + + if (char === "?" || char === "*" || char === "+") { + next_char(); + } else if (char === "{") { + if (some_digits(rx_digits, true) === 0) { + warn_at("expected_a", line, column, "0"); + } + if (next_char() === ",") { + some_digits(rx_digits, true); + next_char(); + } + next_char("}"); + } else { + return; + } + if (char === "?") { + next_char("?"); + } + } + + function subklass() { + +// Match a character in a character class. + + if (char === "\\") { + escape("BbDdSsWw-[]^"); + return true; + } + if ( + char === "" + || char === "[" + || char === "]" + || char === "/" + || char === "^" + || char === "-" + ) { + return false; + } + if (char === " ") { + warn_at("expected_a_b", line, column, "\\u0020", " "); + } else if (char === "`" && mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char(); + return true; + } + + function ranges() { + +// Match a range of subclasses. + + if (subklass()) { + if (char === "-") { + next_char("-"); + if (!subklass()) { + return stop_at( + "unexpected_a", + line, + column - 1, + "-" + ); + } + } + return ranges(); + } + } + + function klass() { + +// Match a class. + + next_char("["); + if (char === "^") { + next_char("^"); + } + (function classy() { + ranges(); + if (char !== "]" && char !== "") { + warn_at( + "expected_a_before_b", + line, + column, + "\\", + char + ); + next_char(); + return classy(); + } + }()); + next_char("]"); + } + + function choice() { + + function group() { + +// Match a group that starts with left paren. + + next_char("("); + if (char === "?") { + next_char("?"); + if (char === "=" || char === "!") { + next_char(); + } else { + next_char(":"); + } + } else if (char === ":") { + warn_at("expected_a_before_b", line, column, "?", ":"); + } + choice(); + next_char(")"); + } + + function factor() { + if ( + char === "" + || char === "/" + || char === "]" + || char === ")" + ) { + return false; + } + if (char === "(") { + group(); + return true; + } + if (char === "[") { + klass(); + return true; + } + if (char === "\\") { + escape("BbDdSsWw^${}[]():=!.-|*+?"); + return true; + } + if ( + char === "?" + || char === "+" + || char === "*" + || char === "}" + || char === "{" + ) { + warn_at( + "expected_a_before_b", + line, + column - 1, + "\\", + char + ); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column - 1, "`"); + } + } else if (char === " ") { + warn_at( + "expected_a_b", + line, + column - 1, + "\\s", + " " + ); + } else if (char === "$") { + if (source_line[0] !== "/") { + multi_mode = true; + } + } else if (char === "^") { + if (snippet !== "^") { + multi_mode = true; + } + } + next_char(); + return true; + } + + function sequence(follow) { + if (factor()) { + quantifier(); + return sequence(true); + } + if (!follow) { + warn_at("expected_regexp_factor_a", line, column, char); + } + } + +// Match a choice (a sequence that can be followed by | and another choice). + + sequence(); + if (char === "|") { + next_char("|"); + return choice(); + } + } + +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + next_char(); + if (char === "=") { + warn_at("expected_a_before_b", line, column, "\\", "="); + } + choice(); + +// Make sure there is a closing slash. + + snip(); + value = snippet; + next_char("/"); + +// Process dangling flag letters. + + const allowed = { + g: true, + i: true, + m: true, + u: true, + y: true + }; + const flag = empty(); + (function make_flag() { + if (is_letter(char)) { + if (allowed[char] !== true) { + warn_at("unexpected_a", line, column, char); + } + allowed[char] = false; + flag[char] = true; + next_char(); + return make_flag(); + } + }()); + back_char(); + if (char === "/" || char === "*") { + return stop_at("unexpected_a", line, from, char); + } + result = make("(regexp)", char); + result.flag = flag; + result.value = value; + if (multi_mode && !flag.m) { + warn_at("missing_m", line, column); + } + return result; + } + + function string(quote) { + +// Make a string token. + + let the_token; + snippet = ""; + next_char(); + + return (function next() { + if (char === quote) { + snip(); + the_token = make("(string)", snippet); + the_token.quote = quote; + return the_token; + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "\\") { + escape(quote); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char("`"); + } else { + next_char(); + } + return next(); + }()); + } + + function frack() { + if (char === ".") { + some_digits(rx_digits); + next_char(); + } + if (char === "E" || char === "e") { + next_char(); + if (char !== "+" && char !== "-") { + back_char(); + } + some_digits(rx_digits); + next_char(); + } + } + + function number() { + if (snippet === "0") { + next_char(); + if (char === ".") { + frack(); + } else if (char === "b") { + some_digits(rx_bits); + next_char(); + } else if (char === "o") { + some_digits(rx_octals); + next_char(); + } else if (char === "x") { + some_digits(rx_hexs); + next_char(); + } + } else { + next_char(); + frack(); + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + return stop_at( + "unexpected_a_after_b", + line, + column - 1, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + back_char(); + return make("(number)", snippet); + } + + function lex() { + let array; + let i = 0; + let j = 0; + let last; + let result; + let the_token; + if (!source_line) { + source_line = next_line(); + from = 0; + return ( + source_line === undefined + ? ( + mega_mode + ? stop_at("unclosed_mega", mega_line, mega_from) + : make("(end)") + ) + : lex() + ); + } + from = column; + result = source_line.match(rx_token); + +// result[1] token +// result[2] whitespace +// result[3] identifier +// result[4] number +// result[5] rest + + if (!result) { + return stop_at( + "unexpected_char_a", + line, + column, + source_line[0] + ); + } + + snippet = result[1]; + column += snippet.length; + source_line = result[5]; + +// Whitespace was matched. Call lex again to get more. + + if (result[2]) { + return lex(); + } + +// The token is an identifier. + + if (result[3]) { + return make(snippet, undefined, true); + } + +// The token is a number. + + if (result[4]) { + return number(snippet); + } + +// The token is a string. + + if (snippet === "\"") { + return string(snippet); + } + if (snippet === "'") { + if (!option.single) { + warn_at("use_double", line, column); + } + return string(snippet); + } + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (snippet === "`") { + if (mega_mode) { + return stop_at("expected_a_b", line, column, "}", "`"); + } + snippet = ""; + mega_from = from; + mega_line = line; + mega_mode = true; + +// Parsing a mega literal is tricky. First make a ` token. + + make("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + (function part() { + const at = source_line.search(rx_mega); + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + if (at < 0) { + snippet += source_line + "\n"; + return ( + next_line() === undefined + ? stop_at("unclosed_mega", mega_line, mega_from) + : part() + ); + } + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + snippet += source_line.slice(0, at); + column += at; + source_line = source_line.slice(at); + if (source_line[0] === "\\") { + stop_at("escape_mega", line, at); + } + make("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then make tokens that will become part of an expression until +// a } token is made. + + if (source_line[0] === "$") { + column += 2; + make("${"); + source_line = source_line.slice(2); + (function expr() { + const id = lex().id; + if (id === "{") { + return stop_at( + "expected_a_b", + line, + column, + "}", + "{" + ); + } + if (id !== "}") { + return expr(); + } + }()); + return part(); + } + }()); + source_line = source_line.slice(1); + column += 1; + mega_mode = false; + return make("`"); + } + +// The token is a // comment. + + if (snippet === "//") { + snippet = source_line; + source_line = ""; + the_token = comment(snippet); + if (mega_mode) { + warn("unexpected_comment", the_token, "`"); + } + return the_token; + } + +// The token is a /* comment. + + if (snippet === "/*") { + array = []; + if (source_line[0] === "/") { + warn_at("unexpected_a", line, column + i, "/"); + } + (function next() { + if (source_line > "") { + i = source_line.search(rx_star_slash); + if (i >= 0) { + return; + } + j = source_line.search(rx_slash_star); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + } + array.push(source_line); + source_line = next_line(); + if (source_line === undefined) { + return stop_at("unclosed_comment", line, column); + } + return next(); + }()); + snippet = source_line.slice(0, i); + j = snippet.search(rx_slash_star_or_slash); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + array.push(snippet); + column += i + 2; + source_line = source_line.slice(i + 2); + return comment(array); + } + +// The token is a slash. + + if (snippet === "/") { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + if (prior.identifier) { + if (!prior.dot) { + if (prior.id === "return") { + return regexp(); + } + if ( + prior.id === "(begin)" + || prior.id === "case" + || prior.id === "delete" + || prior.id === "in" + || prior.id === "instanceof" + || prior.id === "new" + || prior.id === "typeof" + || prior.id === "void" + || prior.id === "yield" + ) { + the_token = regexp(); + return stop("unexpected_a", the_token); + } + } + } else { + last = prior.id[prior.id.length - 1]; + if ("(,=:?[".indexOf(last) >= 0) { + return regexp(); + } + if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) { + the_token = regexp(); + warn("wrap_regexp", the_token); + return the_token; + } + } + if (source_line[0] === "/") { + column += 1; + source_line = source_line.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + } + return make(snippet); + } + + first = lex(); + json_mode = first.id === "{" || first.id === "["; + +// This is the only loop in JSLint. It will turn into a recursive call to lex +// when ES6 has been finished and widely deployed and adopted. + + while (true) { + if (lex().id === "(end)") { + break; + } + } +} + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + +function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!rx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!rx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property[id] === "number") { + property[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (tenure !== undefined) { + if (tenure[id] !== true) { + warn("unregistered_property_a", name); + } + } else { + if (name.identifier && rx_bad_property.test(id)) { + warn("bad_property_a", name); + } + } + property[id] = 1; + } + return id; +} + +function dispense() { + +// Deliver the next token, skipping the comments. + + const cadet = tokens[token_nr]; + token_nr += 1; + if (cadet.id === "(comment)") { + if (json_mode) { + warn("unexpected_a", cadet); + } + return dispense(); + } else { + return cadet; + } +} + +function lookahead() { + +// Look ahead one token without advancing. + + const old_token_nr = token_nr; + const cadet = dispense(true); + token_nr = old_token_nr; + return cadet; +} + +function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if (token.identifier && token.id !== "function") { + anon = token.id; + } else if (token.id === "(string)" && rx_identifier.test(token.value)) { + anon = token.value; + } + +// Attempt to match next_token with an expected id. + + if (id !== undefined && next_token.id !== id) { + return ( + match === undefined + ? stop("expected_a_b", next_token, id, artifact()) + : stop( + "expected_a_b_from_c_d", + next_token, + id, + artifact(match), + artifact_line(match), + artifact(next_token) + ) + ); + } + +// Promote the tokens, skipping comments. + + token = next_token; + next_token = dispense(); + if (next_token.id === "(end)") { + token_nr -= 1; + } +} + +// Parsing of JSON is simple: + +function json_value() { + let negative; + if (next_token.id === "{") { + return (function json_object() { + const brace = next_token; + const object = empty(); + const properties = []; + brace.expression = properties; + advance("{"); + if (next_token.id !== "}") { + (function next() { + let name; + let value; + if (next_token.quote !== "\"") { + warn( + "unexpected_a", + next_token, + next_token.quote + ); + } + name = next_token; + advance("(string)"); + if (object[token.value] !== undefined) { + warn("duplicate_a", token); + } else if (token.value === "__proto__") { + warn("bad_property_a", token); + } else { + object[token.value] = token; + } + advance(":"); + value = json_value(); + value.label = name; + properties.push(value); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("}", brace); + return brace; + }()); + } + if (next_token.id === "[") { + return (function json_array() { + const bracket = next_token; + const elements = []; + bracket.expression = elements; + advance("["); + if (next_token.id !== "]") { + (function next() { + elements.push(json_value()); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]", bracket); + return bracket; + }()); + } + if ( + next_token.id === "true" + || next_token.id === "false" + || next_token.id === "null" + ) { + advance(); + return token; + } + if (next_token.id === "(number)") { + if (!rx_JSON_number.test(next_token.value)) { + warn("unexpected_a"); + } + advance(); + return token; + } + if (next_token.id === "(string)") { + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + advance(); + return token; + } + if (next_token.id === "-") { + negative = next_token; + negative.arity = "unary"; + advance("-"); + advance("(number)"); + negative.expression = token; + return negative; + } + stop("unexpected_a"); +} + +// Now we parse JavaScript. + +function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + const id = name.id; + +// Reserved words may not be enrolled. + + if (syntax[id] !== undefined && id !== "ignore") { + warn("reserved_a", name); + } else { + +// Has the name been enrolled in this context? + + let earlier = functionage.context[id]; + if (earlier) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + +// Has the name been enrolled in an outer context? + + } else { + stack.forEach(function (value) { + const item = value.context[id]; + if (item !== undefined) { + earlier = item; + } + }); + if (earlier) { + if (id === "ignore") { + if (earlier.role === "variable") { + warn("unexpected_a", name); + } + } else { + if ( + ( + role !== "exception" + || earlier.role !== "exception" + ) + && role !== "parameter" + && role !== "function" + ) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + } + } + } + +// Enroll it. + + functionage.context[id] = name; + name.dead = true; + name.parent = functionage; + name.init = false; + name.role = role; + name.used = 0; + name.writable = !readonly; + } + } +} + +function expression(rbp, initial) { + +// This is the heart of the Pratt parser. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// nud Null denotation +// led Left denotation +// lbp Left binding power +// rbp Right binding power + +// It processes a nud (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop. It +// returns the expression's parse tree. + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement, + + if (!initial) { + advance(); + } + the_symbol = syntax[token.id]; + if (the_symbol !== undefined && the_symbol.nud !== undefined) { + left = the_symbol.nud(); + } else if (token.identifier) { + left = token; + left.arity = "variable"; + } else { + return stop("unexpected_a", token); + } + (function right() { + the_symbol = syntax[next_token.id]; + if ( + the_symbol !== undefined + && the_symbol.led !== undefined + && rbp < the_symbol.lbp + ) { + advance(); + left = the_symbol.led(left); + return right(); + } + }()); + return left; +} + +function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = next_token; + let the_value; + the_paren.free = true; + advance("("); + the_value = expression(0); + advance(")"); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (anticondition[the_value.id] === true) { + warn("unexpected_a", the_value); + } + return the_value; +} + +function is_weird(thing) { + return ( + thing.id === "(regexp)" + || thing.id === "{" + || thing.id === "=>" + || thing.id === "function" + || (thing.id === "[" && thing.arity === "unary") + ); +} + +function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + return ( + Array.isArray(b) + && a.length === b.length + && a.every(function (value, index) { + return are_similar(value, b[index]); + }) + ); + } + if (Array.isArray(b)) { + return false; + } + if (a.id === "(number)" && b.id === "(number)") { + return a.value === b.value; + } + let a_string; + let b_string; + if (a.id === "(string)") { + a_string = a.value; + } else if (a.id === "`" && a.constant) { + a_string = a.value[0]; + } + if (b.id === "(string)") { + b_string = b.value; + } else if (b.id === "`" && b.constant) { + b_string = b.value[0]; + } + if (typeof a_string === "string") { + return a_string === b_string; + } + if (is_weird(a) || is_weird(b)) { + return false; + } + if (a.arity === b.arity && a.id === b.id) { + if (a.id === ".") { + return ( + are_similar(a.expression, b.expression) + && are_similar(a.name, b.name) + ); + } + if (a.arity === "unary") { + return are_similar(a.expression, b.expression); + } + if (a.arity === "binary") { + return ( + a.id !== "(" + && are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + ); + } + if (a.arity === "ternary") { + return ( + are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + && are_similar(a.expression[2], b.expression[2]) + ); + } + if (a.arity === "function" && a.arity === "regexp") { + return false; + } + return true; + } + return false; +} + +function semicolon() { + +// Try to match a semicolon. + + if (next_token.id === ";") { + advance(";"); + } else { + warn_at( + "expected_a_b", + token.line, + token.thru, + ";", + artifact(next_token) + ); + } + anon = "anonymous"; +} + +function statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token.identifier && next_token.id === ":") { + the_label = token; + if (the_label.id === "ignore") { + warn("unexpected_a", the_label); + } + advance(":"); + if ( + next_token.id === "do" + || next_token.id === "for" + || next_token.id === "switch" + || next_token.id === "while" + ) { + enroll(the_label, "label", true); + the_label.init = true; + the_label.dead = false; + the_statement = statement(); + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token; + first.statement = true; + the_symbol = syntax[first.id]; + if (the_symbol !== undefined && the_symbol.fud !== undefined) { + the_symbol.disrupt = false; + the_symbol.statement = true; + the_statement = the_symbol.fud(); + } else { + +// It is an expression statement. + + the_statement = expression(0, true); + if (the_statement.wrapped && the_statement.id !== "(") { + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; +} + +function statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const array = []; + (function next(disrupt) { + if ( + next_token.id !== "}" + && next_token.id !== "case" + && next_token.id !== "default" + && next_token.id !== "else" + && next_token.id !== "(end)" + ) { + let a_statement = statement(); + array.push(a_statement); + if (disrupt) { + warn("unreachable_a", a_statement); + } + return next(a_statement.disrupt); + } + }(false)); + return array; +} + +function not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === global) { + warn("unexpected_at_top_level_a", thing); + } +} + +function top_level_only(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== global) { + warn("misplaced_a", the_thing); + } +} + +function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token; + the_block.arity = "statement"; + the_block.body = special === "body"; + +// All top level function bodies should include the "use strict" pragma unless +// the whole file is strict or the file is a module or the function parameters +// use es6 syntax. + + if ( + special === "body" + && stack.length === 1 + && next_token.value === "use strict" + ) { + the_block.strict = next_token; + next_token.statement = true; + advance("(string)"); + advance(";"); + } + stmts = statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option.devel && special !== "ignore") { + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; +} + +function mutation_check(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + warn("bad_assignment_a", the_thing); + return false; + } + return true; +} + +function left_check(left, right) { + +// Warn if the left is not one of these: +// e.b +// e[b] +// e() +// ?: +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !left_check(left.expression[1]) + && !left_check(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right); + return false; + } + return true; +} + +// These functions are used to specify the grammar of our language: + +function symbol(id, bp) { + +// Make a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax[id] = the_symbol; + } + return the_symbol; +} + +function assignment(id) { + +// Make an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led = function (left) { + const the_token = token; + let right; + the_token.arity = "assignment"; + right = expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "pre" + || right.arity === "post" + ) { + warn("unexpected_a", right); + } + mutation_check(left); + return the_token; + }; + return the_symbol; +} + +function constant(id, type, value) { + +// Make a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud = ( + typeof value === "function" + ? value + : function () { + token.constant = true; + if (value !== undefined) { + token.value = value; + } + return token; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; +} + +function infix(id, bp, f) { + +// Make an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, expression(bp)]; + return the_token; + }; + return the_symbol; +} + +function infixr(id, bp) { + +// Make a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + the_token.expression = [left, expression(bp - 1)]; + return the_token; + }; + return the_symbol; +} + +function post(id) { + +// Make one of the post operators. + + const the_symbol = symbol(id, 150); + the_symbol.led = function (left) { + token.expression = left; + token.arity = "post"; + mutation_check(token.expression); + return token; + }; + return the_symbol; +} + +function pre(id) { + +// Make one of the pre operators. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "pre"; + the_token.expression = expression(150); + mutation_check(the_token.expression); + return the_token; + }; + return the_symbol; +} + +function prefix(id, f) { + +// Make a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = expression(150); + return the_token; + }; + return the_symbol; +} + +function stmt(id, f) { + +// Make a statement. + + const the_symbol = symbol(id); + the_symbol.fud = function () { + token.arity = "statement"; + return f(); + }; + return the_symbol; +} + +function ternary(id1, id2) { + +// Make a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led = function (left) { + const the_token = token; + const second = expression(20); + advance(id2); + token.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, expression(10)]; + return the_token; + }; + return the_symbol; +} + +// Begin defining the language. + +syntax = empty(); + +symbol("}"); +symbol(")"); +symbol("]"); +symbol(","); +symbol(";"); +symbol(":"); +symbol("*/"); +symbol("await"); +symbol("case"); +symbol("catch"); +symbol("class"); +symbol("default"); +symbol("else"); +symbol("enum"); +symbol("finally"); +symbol("implements"); +symbol("interface"); +symbol("package"); +symbol("private"); +symbol("protected"); +symbol("public"); +symbol("static"); +symbol("super"); +symbol("void"); +symbol("yield"); + +constant("(number)", "number"); +constant("(regexp)", "regexp"); +constant("(string)", "string"); +constant("arguments", "object", function () { + warn("unexpected_a", token); + return token; +}); +constant("eval", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("false", "boolean", false); +constant("Function", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("ignore", "undefined", function () { + warn("unexpected_a", token); + return token; +}); +constant("Infinity", "number", Infinity); +constant("isFinite", "function", function () { + warn("expected_a_b", token, "Number.isFinite", "isFinite"); + return token; +}); +constant("isNaN", "function", function () { + warn("number_isNaN", token); + return token; +}); +constant("NaN", "number", NaN); +constant("null", "null", null); +constant("this", "object", function () { + if (!option.this) { + warn("unexpected_a", token); + } + return token; +}); +constant("true", "boolean", true); +constant("undefined", "undefined"); + +assignment("="); +assignment("+="); +assignment("-="); +assignment("*="); +assignment("/="); +assignment("%="); +assignment("&="); +assignment("|="); +assignment("^="); +assignment("<<="); +assignment(">>="); +assignment(">>>="); + +infix("||", 40); +infix("&&", 50); +infix("|", 70); +infix("^", 80); +infix("&", 90); +infix("==", 100); +infix("===", 100); +infix("!=", 100); +infix("!==", 100); +infix("<", 110); +infix(">", 110); +infix("<=", 110); +infix(">=", 110); +infix("in", 110); +infix("instanceof", 110); +infix("<<", 120); +infix(">>", 120); +infix(">>>", 120); +infix("+", 130); +infix("-", 130); +infix("*", 140); +infix("/", 140); +infix("%", 140); +infixr("**", 150); +infix("(", 160, function (left) { + const the_paren = token; + let the_argument; + if (left.id !== "function") { + left_check(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (next_token.id !== ")") { + (function next() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + the_paren.free = true; + if (the_argument.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + the_paren.free = false; + } + return the_paren; +}); +infix(".", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("[", 170, function (left) { + const the_token = token; + const the_subscript = expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + const name = survey(the_subscript); + if (rx_identifier.test(name)) { + warn("subscript_a", the_subscript, name); + } + } + left_check(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; +}); +infix("=>", 170, function (left) { + return stop("wrap_parameter", left); +}); + +function do_tick() { + const the_tick = token; + the_tick.value = []; + the_tick.expression = []; + if (next_token.id !== "`") { + (function part() { + advance("(string)"); + the_tick.value.push(token); + if (next_token.id === "${") { + advance("${"); + the_tick.expression.push(expression(0)); + advance("}"); + return part(); + } + }()); + } + advance("`"); + return the_tick; +} + +infix("`", 160, function (left) { + const the_tick = do_tick(); + left_check(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; +}); + +post("++"); +post("--"); +pre("++"); +pre("--"); + +prefix("+"); +prefix("-"); +prefix("~"); +prefix("!"); +prefix("!!"); +prefix("[", function () { + const the_token = token; + the_token.expression = []; + if (next_token.id !== "]") { + (function next() { + let element; + let ellipsis = false; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + element = expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]"); + return the_token; +}); +prefix("/=", function () { + stop("expected_a_b", token, "/\\=", "/="); +}); +prefix("=>", function () { + return stop("expected_a_before_b", token, "()", "=>"); +}); +prefix("new", function () { + const the_new = token; + const right = expression(160); + if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "()", artifact(next_token)); + } + the_new.expression = right; + return the_new; +}); +prefix("typeof"); +prefix("void", function () { + const the_void = token; + warn("unexpected_a", the_void); + the_void.expression = expression(0); + return the_void; +}); + +function parameter_list() { + let complex = false; + const list = []; + let optional; + const signature = ["("]; + if (next_token.id !== ")" && next_token.id !== "(end)") { + (function parameter() { + let ellipsis = false; + let param; + if (next_token.id === "{") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("{"); + signature.push("{"); + (function subparameter() { + let subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (next_token.id === ":") { + advance(":"); + advance(); + token.label = subparam; + subparam = token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + } + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return subparameter(); + } + }()); + list.push(param); + advance("}"); + signature.push("}"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else if (next_token.id === "[") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("["); + signature.push("[]"); + (function subparameter() { + const subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + return subparameter(); + } + }()); + list.push(param); + advance("]"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else { + if (next_token.id === "...") { + complex = true; + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + } + if (!next_token.identifier) { + return stop("expected_identifier_a"); + } + param = next_token; + list.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (next_token.id === "=") { + complex = true; + optional = param; + advance("="); + param.expression = expression(0); + } else { + if (optional !== undefined) { + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } + } + }()); + } + advance(")"); + signature.push(")"); + return [list, signature.join(""), complex]; +} + +function do_function(the_function) { + let name; + if (the_function === undefined) { + the_function = token; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + name = next_token; + enroll(name, "variable", true); + the_function.name = name; + name.init = true; + name.calls = empty(); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + if (next_token.identifier) { + name = next_token; + the_function.name = name; + advance(); + } else { + the_function.name = anon; + } + } + } else { + name = the_function.name; + } + the_function.level = functionage.level + 1; + if (mega_mode) { + warn("unexpected_a", the_function); + } + +// Don't make functions in loops. It is inefficient, and it can lead to scoping +// errors. + + if (functionage.loop > 0) { + warn("function_in_loop", the_function); + } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + the_function.context = empty(); + the_function.finally = 0; + the_function.loop = 0; + the_function.switch = 0; + the_function.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functions.push(the_function); + functionage = the_function; + if (the_function.arity !== "statement" && typeof name === "object") { + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// Parse the parameter list. + + advance("("); + token.free = false; + token.arity = "function"; + const pl = parameter_list(); + functionage.parameters = pl[0]; + functionage.signature = pl[1]; + functionage.complex = pl[2]; + functionage.parameters.forEach(function enroll_parameter(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + name.names.forEach(enroll_parameter); + } + }); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && next_token.line === token.line + ) { + return stop("unexpected_a", next_token); + } + if (next_token.id === "." || next_token.id === "[") { + warn("unexpected_a"); + } + +// Restore the previous context. + + functionage = stack.pop(); + return the_function; +} + +prefix("function", do_function); + +function fart(pl) { + if (next_token.id === ";") { + stop("wrap_assignment", token); + } + advance("=>"); + const the_fart = token; + the_fart.arity = "binary"; + the_fart.name = "=>"; + the_fart.level = functionage.level + 1; + functions.push(the_fart); + if (functionage.loop > 0) { + warn("function_in_loop", the_fart); + } + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + the_fart.context = empty(); + the_fart.finally = 0; + the_fart.loop = 0; + the_fart.switch = 0; + the_fart.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functionage = the_fart; + the_fart.parameters = pl[0]; + the_fart.signature = pl[1]; + the_fart.complex = true; + the_fart.parameters.forEach(function (name) { + enroll(name, "parameter", true); + }); + if (next_token.id === "{") { + warn("expected_a_b", the_fart, "function", "=>"); + the_fart.block = block("body"); + } else { + the_fart.expression = expression(0); + } + functionage = stack.pop(); + return the_fart; +} + +prefix("(", function () { + const the_paren = token; + let the_value; + const cadet = lookahead().id; + +// We can distinguish between a parameter list for => and a wrapped expression +// with one token of lookahead. + + if ( + next_token.id === ")" + || next_token.id === "..." + || (next_token.identifier && (cadet === "," || cadet === "=")) + ) { + the_paren.free = false; + return fart(parameter_list()); + } + the_paren.free = true; + the_value = expression(0); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + if (next_token.id === "=>") { + if (the_value.arity !== "variable") { + if (the_value.id === "{" || the_value.id === "[") { + warn("expected_a_before_b", the_paren, "function", "("); + return stop("expected_a_b", next_token, "{", "=>"); + } + return stop("expected_identifier_a", the_value); + } + the_paren.expression = [the_value]; + return fart([the_paren.expression, "(" + the_value.id + ")"]); + } + return the_value; +}); +prefix("`", do_tick); +prefix("{", function () { + const the_brace = token; + const seen = empty(); + the_brace.expression = []; + if (next_token.id !== "}") { + (function member() { + let extra; + let full; + let id; + let name = next_token; + let value; + advance(); + if ( + (name.id === "get" || name.id === "set") + && next_token.identifier + ) { + if (!option.getset) { + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + next_token.id; + name = next_token; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (next_token.id === "}" || next_token.id === ",") { + if (typeof extra === "string") { + advance("("); + } + value = expression(Infinity, true); + } else if (next_token.id === "(") { + value = do_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + advance("("); + } + advance(":"); + value = expression(0); + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + advance(":"); + value = expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (next_token.id === ",") { + advance(","); + return member(); + } + }()); + } + advance("}"); + return the_brace; +}); + +stmt(";", function () { + warn("unexpected_a", token); + return token; +}); +stmt("{", function () { + warn("naked_block", token); + return block("naked"); +}); +stmt("break", function () { + const the_break = token; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (next_token.identifier && token.line === next_token.line) { + the_label = functionage.context[next_token.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + warn( + (the_label !== undefined && the_label.dead) + ? "out_of_scope_a" + : "not_label_a" + ); + } else { + the_label.used += 1; + } + the_break.label = next_token; + advance(); + } + advance(";"); + return the_break; +}); + +function do_var() { + const the_statement = token; + const is_const = the_statement.id === "const"; + the_statement.names = []; + +// A program may use var or let, but not both. + + if (!is_const) { + if (var_mode === undefined) { + var_mode = the_statement.id; + } else if (the_statement.id !== var_mode) { + warn( + "expected_a_b", + the_statement, + var_mode, + the_statement.id + ); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + warn("var_switch", the_statement); + } + if (functionage.loop > 0 && the_statement.id === "var") { + warn("var_loop", the_statement); + } + (function next() { + if (next_token.id === "{" && the_statement.id !== "var") { + const the_brace = next_token; + the_brace.names = []; + advance("{"); + (function pair() { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + survey(name); + advance(); + if (next_token.id === ":") { + advance(":"); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + next_token.label = name; + the_brace.names.push(next_token); + enroll(next_token, "variable", is_const); + advance(); + } else { + the_brace.names.push(name); + enroll(name, "variable", is_const); + } + if (next_token.id === ",") { + advance(","); + return pair(); + } + }()); + advance("}"); + advance("="); + the_brace.expression = expression(0); + the_statement.names.push(the_brace); + } else if (next_token.id === "[" && the_statement.id !== "var") { + const the_bracket = next_token; + the_bracket.names = []; + advance("["); + (function element() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + advance(); + the_bracket.names.push(name); + enroll(name, "variable", the_statement.id === "const"); + if (ellipsis) { + name.ellipsis = true; + } else if (next_token.id === ",") { + advance(","); + return element(); + } + }()); + advance("]"); + advance("="); + the_bracket.expression = expression(0); + the_statement.names.push(the_bracket); + } else if (next_token.identifier) { + const name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", is_const); + if (next_token.id === "=" || is_const) { + advance("="); + name.init = true; + name.expression = expression(0); + } + the_statement.names.push(name); + } else { + return stop("expected_identifier_a", next_token); + } + if (next_token.id === ",") { + if (!option.multivar) { + warn("expected_a_b", next_token, ";", ","); + } + advance(","); + return next(); + } + }()); + the_statement.open = ( + the_statement.names.length > 1 + && the_statement.line !== the_statement.names[1].line + ); + semicolon(); + return the_statement; +} + +stmt("const", do_var); +stmt("continue", function () { + const the_continue = token; + if (functionage.loop < 1 || functionage.finally > 0) { + warn("unexpected_a", the_continue); + } + not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; +}); +stmt("debugger", function () { + const the_debug = token; + if (!option.devel) { + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; +}); +stmt("delete", function () { + const the_token = token; + const the_value = expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; +}); +stmt("do", function () { + const the_do = token; + not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; +}); +stmt("export", function () { + const the_export = token; + let the_id; + let the_name; + let the_thing; + + function export_id() { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + the_id = next_token.id; + the_name = global.context[the_id]; + if (the_name === undefined) { + warn("unexpected_a"); + } else { + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a"); + } + exports[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + } + + the_export.expression = []; + if (next_token.id === "default") { + if (exports.default !== undefined) { + warn("duplicate_a"); + } + advance("default"); + the_thing = expression(0); + semicolon(); + exports.default = the_thing; + the_export.expression.push(the_thing); + } else { + if (next_token.id === "function") { + the_thing = statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a", the_name); + } + exports[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + warn("unexpected_a"); + } else if (next_token.id === "{") { + advance("{"); + (function loop() { + export_id(); + if (next_token.id === ",") { + advance(","); + return loop(); + } + }()); + advance("}"); + semicolon(); + } else { + export_id(); + if (the_name.writable !== true) { + warn("unexpected_a", token); + } + semicolon(); + } + } + module_mode = true; + return the_export; +}); +stmt("for", function () { + let first; + const the_for = token; + if (!option.for) { + warn("unexpected_a", the_for); + } + not_top_level(the_for); + functionage.loop += 1; + advance("("); + token.free = true; + if (next_token.id === ";") { + return stop("expected_a_b", the_for, "while (", "for (;"); + } + if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + return stop("unexpected_a"); + } + first = expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = expression(0); + advance(";"); + the_for.inc = expression(0); + if (the_for.inc.id === "++") { + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; +}); +stmt("function", do_function); +stmt("if", function () { + let the_else; + const the_if = token; + the_if.expression = condition(); + the_if.block = block(); + if (next_token.id === "else") { + advance("else"); + the_else = token; + the_if.else = ( + next_token.id === "if" + ? statement() + : block() + ); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + the_if.disrupt = true; + } else { + warn("unexpected_a", the_else); + } + } + } + return the_if; +}); +stmt("import", function () { + const the_import = token; + let name; + if (typeof module_mode === "object") { + warn("unexpected_directive_a", module_mode, module_mode.directive); + } + module_mode = true; + if (next_token.identifier) { + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + const names = []; + advance("{"); + if (next_token.id !== "}") { + while (true) { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (next_token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token; + if (!rx_module.test(token.value)) { + warn("bad_module_name_a", token); + } + froms.push(token.value); + semicolon(); + return the_import; +}); +stmt("let", do_var); +stmt("return", function () { + const the_return = token; + not_top_level(the_return); + if (functionage.finally > 0) { + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (next_token.id !== ";" && the_return.line === next_token.line) { + the_return.expression = expression(10); + } + advance(";"); + return the_return; +}); +stmt("switch", function () { + let dups = []; + let last; + let stmts; + const the_cases = []; + let the_disrupt = true; + const the_switch = token; + not_top_level(the_switch); + if (functionage.finally > 0) { + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token.free = true; + the_switch.expression = expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + (function major() { + const the_case = next_token; + the_case.arity = "statement"; + the_case.expression = []; + (function minor() { + advance("case"); + token.switch = true; + const exp = expression(0); + if (dups.some(function (thing) { + return are_similar(thing, exp); + })) { + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (next_token.id === "case") { + return minor(); + } + }()); + stmts = statements(); + if (stmts.length < 1) { + warn("expected_statements_a"); + return; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "break;", + artifact(next_token) + ); + } + if (next_token.id === "case") { + return major(); + } + }()); + dups = undefined; + if (next_token.id === "default") { + const the_default = next_token; + advance("default"); + token.switch = true; + advance(":"); + the_switch.else = statements(); + if (the_switch.else.length < 1) { + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + const the_last = the_switch.else[the_switch.else.length - 1]; + if (the_last.id === "break" && the_last.label === undefined) { + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; +}); +stmt("throw", function () { + const the_throw = token; + the_throw.disrupt = true; + the_throw.expression = expression(10); + semicolon(); + if (functionage.try > 0) { + warn("unexpected_a", the_throw); + } + return the_throw; +}); +stmt("try", function () { + let the_catch; + let the_disrupt; + const the_try = token; + if (functionage.try > 0) { + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (next_token.id === "catch") { + let ignored = "ignore"; + the_catch = next_token; + the_try.catch = the_catch; + advance("catch"); + advance("("); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + if (next_token.id !== "ignore") { + ignored = undefined; + the_catch.name = next_token; + enroll(next_token, "exception", true); + } + advance(); + advance(")"); + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "catch", + artifact(next_token) + ); + + } + if (next_token.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; +}); +stmt("var", do_var); +stmt("while", function () { + const the_while = token; + not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; +}); +stmt("with", function () { + stop("unexpected_a", token); +}); + +ternary("?", ":"); + +// Ambulation of the parse tree. + +function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; +} + +function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all of the +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + } + }; +} + +const posts = empty(); +const pres = empty(); +const preaction = action(pres); +const postaction = action(posts); +const preamble = amble(pres); +const postamble = amble(posts); + +function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.id === "function") { + walk_statement(thing.block); + } + if (thing.arity === "pre" || thing.arity === "post") { + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + warn("unexpected_statement_a", thing); + } + postamble(thing); + } + } +} + +function walk_statement(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_statement); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + ) { + warn( + (thing.id === "(string)" && thing.value === "use strict") + ? "unexpected_a" + : "unexpected_expression_a", + thing + ); + } + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + } +} + +function lookup(thing) { + if (thing.arity === "variable") { + +// Look up the variable in the current context. + + let the_variable = functionage.context[thing.id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable === undefined) { + stack.forEach(function (outer) { + const a_variable = outer.context[thing.id]; + if ( + a_variable !== undefined + && a_variable.role !== "label" + ) { + the_variable = a_variable; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (the_variable === undefined) { + if (declared_globals[thing.id] === undefined) { + warn("undeclared_a", thing); + return; + } + the_variable = { + dead: false, + parent: global, + id: thing.id, + init: true, + role: "variable", + used: 0, + writable: false + }; + global.context[thing.id] = the_variable; + } + the_variable.closure = true; + functionage.context[thing.id] = the_variable; + } else if (the_variable.role === "label") { + warn("label_a", thing); + } + if ( + the_variable.dead + && ( + the_variable.calls === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + ) { + warn("out_of_scope_a", thing); + } + return the_variable; + } +} + +function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); +} + +function preaction_function(thing) { + if (thing.arity === "statement" && blockage.body !== true) { + warn("unexpected_a", thing); + } + if (thing.level === 1) { + if ( + module_mode === true + || global.strict !== undefined + || thing.complex + ) { + if (thing.id !== "=>" && thing.block.strict !== undefined) { + warn("unexpected_a", thing.block.strict); + } + } else { + if (thing.block.strict === undefined) { + warn("use_strict", thing); + } + } + } + stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); +} + +function bitwise_check(thing) { + if (!option.bitwise && bitwiseop[thing.id] === true) { + warn("unexpected_a", thing); + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + warn("unexpected_a", thing); + } +} + +function pop_block() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); +} + +function activate(name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.init = true; + } + } + blockage.live.push(name); +} + +function action_var(thing) { + thing.names.forEach(activate); +} + +preaction("assignment", bitwise_check); +preaction("binary", bitwise_check); +preaction("binary", function (thing) { + if (relationop[thing.id] === true) { + const left = thing.expression[0]; + const right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + warn("expected_string_a", right); + } + } else { + const value = right.value; + if (value === "null" || value === "undefined") { + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + warn("expected_type_string_a", right, value); + } + } + } + } +}); +preaction("binary", "==", function (thing) { + warn("expected_a_b", thing, "===", "=="); +}); +preaction("binary", "!=", function (thing) { + warn("expected_a_b", thing, "!==", "!="); +}); +preaction("binary", "=>", preaction_function); +preaction("binary", "||", function (thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + warn("and", thang); + } + }); +}); +preaction("binary", "(", function (thing) { + const left = thing.expression[0]; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + const parent = functionage.name.parent; + if (parent) { + const left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + && left_variable.dead + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } +}); +preaction("binary", "in", function (thing) { + warn("infix_in", thing); +}); +preaction("binary", "instanceof", function (thing) { + warn("unexpected_a", thing); +}); +preaction("binary", ".", function (thing) { + if (thing.expression.new) { + thing.new = true; + } +}); +preaction("statement", "{", function (thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; +}); +preaction("statement", "for", function (thing) { + if (thing.name !== undefined) { + const the_variable = lookup(thing.name); + if (the_variable !== undefined) { + the_variable.init = true; + if (!the_variable.writable) { + warn("bad_assignment_a", thing.name); + } + } + } + walk_statement(thing.initial); +}); +preaction("statement", "function", preaction_function); +preaction("unary", "~", bitwise_check); +preaction("unary", "function", preaction_function); +preaction("variable", function (thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } +}); + +function init_variable(name) { + const the_variable = lookup(name); + if (the_variable !== undefined) { + if (the_variable.writable) { + the_variable.init = true; + return; + } + } + warn("bad_assignment_a", name); +} + +postaction("assignment", "+=", function (thing) { + let right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } +}); +postaction("assignment", function (thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + if (thing.id === "=") { + if (thing.names !== undefined) { + if (Array.isArray(thing.names)) { + thing.names.forEach(init_variable); + } else { + init_variable(thing.names); + } + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.writable !== true) { + warn("bad_assignment_a", lvalue); + } + } + const right = syntax[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + warn("unexpected_a", thing.expression[1]); + } + } +}); + +function postaction_function(thing) { + delete functionage.finally; + delete functionage.loop; + delete functionage.switch; + delete functionage.try; + functionage = stack.pop(); + if (thing.wrapped) { + warn("unexpected_parens", thing); + } + return pop_block(); +} + +postaction("binary", function (thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || are_similar(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option.convert) { + if (thing.expression[0].value === "") { + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === ".") { + if (thing.expression.id === "RegExp") { + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } +}); +postaction("binary", "&&", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "||", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "=>", postaction_function); +postaction("binary", "(", function (thing) { + let left = thing.expression[0]; + let the_new; + let arg; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option.eval) { + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + warn( + "expected_a_before_b", + left, + "new", + artifact(left) + ); + } + } + } else if (left.id === ".") { + let cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + cack = !cack; + } + if (rx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + warn("unexpected_a", the_new); + } else { + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + const paren = left.expression; + if (paren.id === "(") { + const array = paren.expression; + if (array.length === 1) { + const new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } +}); +postaction("binary", "[", function (thing) { + if (thing.expression[0].id === "RegExp") { + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + warn("weird_expression_a", thing.expression[1]); + } +}); +postaction("statement", "{", pop_block); +postaction("statement", "const", action_var); +postaction("statement", "export", top_level_only); +postaction("statement", "for", function (thing) { + walk_statement(thing.inc); +}); +postaction("statement", "function", postaction_function); +postaction("statement", "import", function (the_thing) { + const name = the_thing.name; + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return top_level_only(the_thing); +}); +postaction("statement", "let", action_var); +postaction("statement", "try", function (thing) { + if (thing.catch !== undefined) { + const the_name = thing.catch.name; + if (the_name !== undefined) { + const the_variable = functionage.context[the_name.id]; + the_variable.dead = false; + the_variable.init = true; + } + walk_statement(thing.catch.block); + } +}); +postaction("statement", "var", action_var); +postaction("ternary", function (thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || are_similar(thing.expression[1], thing.expression[2]) + ) { + warn("unexpected_a", thing); + } else if (are_similar(thing.expression[0], thing.expression[1])) { + warn("expected_a_b", thing, "||", "?"); + } else if (are_similar(thing.expression[0], thing.expression[2])) { + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + warn("wrap_condition", thing.expression[0]); + } +}); +postaction("unary", function (thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option.convert) { + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } +}); +postaction("unary", "function", postaction_function); +postaction("unary", "+", function (thing) { + if (!option.convert) { + warn("expected_a_b", thing, "Number(...)", "+"); + } + const right = thing.expression; + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } +}); + +function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + if (id !== "ignore") { + const name = the_function.context[id]; + if (name.parent === the_function) { + if ( + name.used === 0 + && ( + name.role !== "function" + || name.parent.arity !== "unary" + ) + ) { + warn("unused_a", name); + } else if (!name.init) { + warn("uninitialized_a", name); + } + } + } + }); +} + +function uninitialized_and_unused() { + +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (module_mode === true || option.node) { + delve(global); + } + functions.forEach(delve); +} + +// Go through the token list, looking at usage of whitespace. + +function whitage() { + let closer = "(end)"; + let free = false; + let left = global; + let margin = 0; + let nr_comments_skipped = 0; + let open = true; + let right; + + function expected_at(at) { + warn( + "expected_a_at_b_c", + right, + artifact(right), + fudge + at, + artifact_column(right) + ); + } + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function no_space() { + if (left.line === right.line) { + if (left.thru !== right.from && nr_comments_skipped === 0) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (open) { + const at = ( + free + ? margin + : margin + 8 + ); + if (right.from < at) { + expected_at(at); + } + } else { + if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (free) { + if (right.from < margin) { + expected_at(margin); + } + } else { + const mislaid = ( + stack.length > 0 + ? stack[stack.length - 1].right + : undefined + ); + if (!open && mislaid !== undefined) { + warn( + "expected_a_next_at_b", + mislaid, + artifact(mislaid.id), + margin + 4 + fudge + ); + } else if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + stack = []; + tokens.forEach(function (the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + + const new_closer = opener[left.id]; + if (typeof new_closer === "string") { + if (new_closer !== right.id) { + stack.push({ + closer: closer, + free: free, + margin: margin, + open: open, + right: right + }); + closer = new_closer; + if (left.line !== right.line) { + free = closer === ")" && left.free; + open = true; + margin += 4; + if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (right.switch) { + at_margin(-4); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + free = false; + open = false; + no_space_only(); + } + } else { + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like {]) have already been detected. + + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } + } else { + +// If right is a closer, then pop the previous state. + + if (right.id === closer) { + const previous = stack.pop(); + margin = previous.margin; + if (open && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + closer = previous.closer; + free = previous.free; + open = previous.open; + } else { + +// Left is not an opener, and right is not a closer. The nature of left and +// right will determine the space between them. + +// If left is , or ; or right is a statement then if open, right must go at the +// margin, or if closed, a space between. + + + if (right.switch) { + at_margin(-4); + } else if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + one_space(); + } else { + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + at_margin(0); + } else { + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + no_space(); + } else if ( + left.id === "." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + ) { + no_space_only(); + } else if (right.id === ".") { + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } else if (left.id === ";") { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + one_space_only(); + } else if (right.statement === true) { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.id === "var" + || left.id === "const" + || left.id === "let" + ) { + stack.push({ + closer: closer, + free: free, + margin: margin, + open: open + }); + closer = ";"; + free = false; + open = left.open; + if (open) { + margin = margin + 4; + at_margin(0); + } else { + one_space_only(); + } + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +// The jslint function itself. + +export default function jslint(source, option_object, global_array) { + try { + warnings = []; + option = Object.assign(empty(), option_object); + anon = "anonymous"; + block_stack = []; + declared_globals = empty(); + directive_mode = true; + directives = []; + early_stop = true; + exports = empty(); + froms = []; + fudge = ( + option.fudge + ? 1 + : 0 + ); + functions = []; + global = { + id: "(global)", + body: true, + context: empty(), + from: 0, + level: 0, + line: 0, + live: [], + loop: 0, + switch: 0, + thru: 0 + }; + blockage = global; + functionage = global; + json_mode = false; + mega_mode = false; + module_mode = false; + next_token = global; + property = empty(); + stack = []; + tenure = undefined; + token = global; + token_nr = 0; + var_mode = undefined; + populate(standard, declared_globals, false); + if (global_array !== undefined) { + populate(global_array, declared_globals, false); + } + Object.keys(option).forEach(function (name) { + if (option[name] === true) { + const allowed = allowed_option[name]; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } + }); + tokenize(source); + advance(); + if (json_mode) { + tree = json_value(); + advance("(end)"); + } else { + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option.browser) { + if (next_token.id === ";") { + advance(";"); + } + } else { + +// If we are not in a browser, then the file form of strict pragma may be used. + + if ( + next_token.value === "use strict" + ) { + global.strict = next_token; + advance("(string)"); + advance(";"); + } + } + tree = statements(); + advance("(end)"); + functionage = global; + walk_statement(tree); + if (module_mode && global.strict !== undefined) { + warn("unexpected_a", global.strict); + } + if (warnings.length === 0) { + uninitialized_and_unused(); + if (!option.white) { + whitage(); + } + } + } + if (!option.browser) { + directives.forEach(function (comment) { + if (comment.directive === "global") { + warn("missing_browser", comment); + } + }); + } + early_stop = false; + } catch (e) { + if (e.name !== "JSLintError") { + warnings.push(e); + } + } + return { + directives: directives, + edition: "2018-08-11", + exports: exports, + froms: froms, + functions: functions, + global: global, + id: "(JSLint)", + json: json_mode, + lines: lines, + module: module_mode === true, + ok: warnings.length === 0 && !early_stop, + option: option, + property: property, + stop: early_stop, + tokens: tokens, + tree: tree, + warnings: warnings.sort(function (a, b) { + return a.line - b.line || a.column - b.column; + }) + }; +}; diff --git a/branch.json_mode_ignore_long_lines/report.js b/branch.json_mode_ignore_long_lines/report.js new file mode 100644 index 000000000..f123e9055 --- /dev/null +++ b/branch.json_mode_ignore_long_lines/report.js @@ -0,0 +1,221 @@ +// report.js +// 2018-07-29 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Generate JSLint HTML reports. + +/*property + closure, column, context, edition, error, exports, filter, forEach, froms, + fudge, function, functions, global, id, isArray, join, json, keys, length, + level, line, lines, message, module, name, names, option, parameters, + parent, property, push, replace, role, signature, sort, stop, warnings +*/ + +const rx_amp = /&/g; +const rx_gt = />/g; +const rx_lt = / with less destructive entities. + + return String( + string + ).replace( + rx_amp, + "&" + ).replace( + rx_lt, + "<" + ).replace( + rx_gt, + ">" + ); +} + +export default { + error: function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + let fudge = Number(Boolean(data.option.fudge)); + let output = []; + if (data.stop) { + output.push("
    JSLint was unable to finish.
    "); + } + data.warnings.forEach(function (warning) { + output.push( + "
    ", + entityify(warning.line + fudge), + ".", + entityify(warning.column + fudge), + "
    ", + entityify(warning.message), + "
    ", + entityify(data.lines[warning.line] || ""), + "" + ); + }); + return output.join(""); + }, + + function: function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + let fudge = Number(Boolean(data.option.fudge)); + let mode = ( + data.module + ? "module" + : "global" + ); + let output = []; + + if (data.json) { + return ( + data.warnings.length === 0 + ? "
    JSON: good.
    " + : "
    JSON: bad.
    " + ); + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + let global = Object.keys(data.global.context).sort(); + let froms = data.froms.sort(); + let exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + let context = the_function.context; + let list = Object.keys(context); + output.push( + "
    ", + entityify(the_function.line + fudge), + "
    ", + ( + the_function.name === "=>" + ? entityify(the_function.signature) + " =>" + : ( + typeof the_function.name === "string" + ? "«" + entityify(the_function.name) + "»" + : "" + entityify(the_function.name.id) + "" + ) + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + let params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.parent === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.parent === the_function + ); + })); + detail("outer", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.parent !== the_function + && the_variable.parent.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].parent.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + output.push( + "
    JSLint edition ", + entityify(data.edition), + "
    " + ); + return output.join(""); + }, + + property: function property_directive(data) { + +// Produce the /*property*/ directive. + + let not_first = false; + let output = ["/*property"]; + let length = 1111; + let properties = Object.keys(data.property); + + if (properties.length > 0) { + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length >= 80) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); + } + } +}; diff --git a/branch.master/.build/coverage/coverage-badge.svg b/branch.master/.build/coverage/coverage-badge.svg new file mode 100644 index 000000000..6df21e4c4 --- /dev/null +++ b/branch.master/.build/coverage/coverage-badge.svg @@ -0,0 +1,14 @@ + + + + +coverage +93.42 % + + diff --git a/branch.master/.build/coverage/coverage-report.txt b/branch.master/.build/coverage/coverage-report.txt new file mode 100644 index 000000000..54a148191 --- /dev/null +++ b/branch.master/.build/coverage/coverage-report.txt @@ -0,0 +1,13 @@ +coverage-report ++----------------------------------+-------------+ +| files covered | lines | ++----------------------------------+-------------+ +| ./ | 93.42 % | +| ******************************__ | 5286 / 5658 | ++----------------------------------+-------------+ +| ./jslint.js | 93.04 % | +| ******************************__ | 4899 / 5265 | ++----------------------------------+-------------+ +| ./test.js | 98.47 % | +| ******************************** | 387 / 393 | ++----------------------------------+-------------+ diff --git a/branch.master/.build/coverage/coverage-v8.json b/branch.master/.build/coverage/coverage-v8.json new file mode 100644 index 000000000..d1f924b85 --- /dev/null +++ b/branch.master/.build/coverage/coverage-v8.json @@ -0,0 +1 @@ +{"result":[{"scriptId":"6","url":"internal/per_context/primordials.js","functions":[{"functionName":"uncurryThis","ranges":[{"startOffset":1000,"endOffset":1096,"count":5}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":1038,"endOffset":1093,"count":618}],"isBlockCoverage":true}]},{"scriptId":"9","url":"internal/bootstrap/loaders.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":10311,"count":1}],"isBlockCoverage":true},{"functionName":"binding","ranges":[{"startOffset":3652,"endOffset":4049,"count":0}],"isBlockCoverage":false},{"functionName":"_linkedBinding","ranges":[{"startOffset":4079,"endOffset":4287,"count":0}],"isBlockCoverage":false},{"functionName":"internalBinding","ranges":[{"startOffset":4467,"endOffset":4729,"count":93},{"startOffset":4569,"endOffset":4709,"count":30}],"isBlockCoverage":true},{"functionName":"getOwn","ranges":[{"startOffset":4874,"endOffset":5028,"count":54},{"startOffset":5010,"endOffset":5025,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":5395,"endOffset":5493,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":5454,"endOffset":5488,"count":232}],"isBlockCoverage":true},{"functionName":"NativeModule","ranges":[{"startOffset":5498,"endOffset":6250,"count":232}],"isBlockCoverage":true},{"functionName":"exposeInternals","ranges":[{"startOffset":6400,"endOffset":6626,"count":0}],"isBlockCoverage":false},{"functionName":"exists","ranges":[{"startOffset":6637,"endOffset":6690,"count":0}],"isBlockCoverage":false},{"functionName":"canBeRequiredByUsers","ranges":[{"startOffset":6701,"endOffset":6817,"count":3},{"startOffset":6785,"endOffset":6812,"count":1}],"isBlockCoverage":true},{"functionName":"compileForPublicLoader","ranges":[{"startOffset":6889,"endOffset":7583,"count":1},{"startOffset":6952,"endOffset":7144,"count":0},{"startOffset":7467,"endOffset":7471,"count":0}],"isBlockCoverage":true},{"functionName":"getESMFacade","ranges":[{"startOffset":7587,"endOffset":8138,"count":2},{"startOffset":7625,"endOffset":8137,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":7865,"endOffset":7978,"count":1}],"isBlockCoverage":true},{"functionName":"syncExports","ranges":[{"startOffset":8434,"endOffset":8778,"count":2},{"startOffset":8553,"endOffset":8768,"count":54},{"startOffset":8630,"endOffset":8639,"count":0}],"isBlockCoverage":true},{"functionName":"compileForInternalLoader","ranges":[{"startOffset":8782,"endOffset":9367,"count":337},{"startOffset":8831,"endOffset":8846,"count":81},{"startOffset":8848,"endOffset":8882,"count":260},{"startOffset":8882,"endOffset":9021,"count":77},{"startOffset":9021,"endOffset":9056,"count":0},{"startOffset":9057,"endOffset":9078,"count":77},{"startOffset":9232,"endOffset":9366,"count":77}],"isBlockCoverage":true},{"functionName":"nativeModuleRequire","ranges":[{"startOffset":9565,"endOffset":9936,"count":341},{"startOffset":9623,"endOffset":9654,"count":5},{"startOffset":9654,"endOffset":9838,"count":336},{"startOffset":9838,"endOffset":9893,"count":0},{"startOffset":9893,"endOffset":9935,"count":336}],"isBlockCoverage":true},{"functionName":"requireWithFallbackInDeps","ranges":[{"startOffset":10052,"endOffset":10224,"count":0}],"isBlockCoverage":false}]},{"scriptId":"10","url":"internal/bootstrap/node.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":12616,"count":1}],"isBlockCoverage":true},{"functionName":"process.openStdin","ranges":[{"startOffset":3399,"endOffset":3469,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":6160,"endOffset":6322,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":6424,"endOffset":6596,"count":0}],"isBlockCoverage":false},{"functionName":"setupPrepareStackTrace","ranges":[{"startOffset":9383,"endOffset":9969,"count":1}],"isBlockCoverage":true},{"functionName":"setupProcessObject","ranges":[{"startOffset":9971,"endOffset":10576,"count":1}],"isBlockCoverage":true},{"functionName":"setupGlobalProxy","ranges":[{"startOffset":10578,"endOffset":10755,"count":1}],"isBlockCoverage":true},{"functionName":"setupBuffer","ranges":[{"startOffset":10757,"endOffset":11193,"count":1}],"isBlockCoverage":true},{"functionName":"createGlobalConsole","ranges":[{"startOffset":11195,"endOffset":11876,"count":1}],"isBlockCoverage":true},{"functionName":"exposeNamespace","ranges":[{"startOffset":11928,"endOffset":12126,"count":1}],"isBlockCoverage":true},{"functionName":"exposeInterface","ranges":[{"startOffset":12178,"endOffset":12376,"count":4}],"isBlockCoverage":true},{"functionName":"defineOperation","ranges":[{"startOffset":12436,"endOffset":12615,"count":7}],"isBlockCoverage":true}]},{"scriptId":"11","url":"internal/errors.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":53549,"count":1}],"isBlockCoverage":false},{"functionName":"prepareStackTrace","ranges":[{"startOffset":1404,"endOffset":2120,"count":2},{"startOffset":1581,"endOffset":1697,"count":0},{"startOffset":1824,"endOffset":1846,"count":0},{"startOffset":2027,"endOffset":2056,"count":0}],"isBlockCoverage":true},{"functionName":"maybeOverridePrepareStackTrace","ranges":[{"startOffset":2162,"endOffset":2869,"count":2},{"startOffset":2431,"endOffset":2497,"count":0},{"startOffset":2778,"endOffset":2844,"count":0}],"isBlockCoverage":true},{"functionName":"lazyInternalUtil","ranges":[{"startOffset":2959,"endOffset":3085,"count":0}],"isBlockCoverage":false},{"functionName":"lazyInternalUtilInspect","ranges":[{"startOffset":3119,"endOffset":3281,"count":0}],"isBlockCoverage":false},{"functionName":"lazyBuffer","ranges":[{"startOffset":3295,"endOffset":3404,"count":0}],"isBlockCoverage":false},{"functionName":"SystemError","ranges":[{"startOffset":3906,"endOffset":6444,"count":0}],"isBlockCoverage":false},{"functionName":"toString","ranges":[{"startOffset":6448,"endOffset":6523,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":6527,"endOffset":6716,"count":0}],"isBlockCoverage":false},{"functionName":"makeSystemErrorWithCode","ranges":[{"startOffset":6720,"endOffset":6865,"count":4}],"isBlockCoverage":true},{"functionName":"NodeError","ranges":[{"startOffset":6811,"endOffset":6858,"count":0}],"isBlockCoverage":false},{"functionName":"makeNodeErrorWithCode","ranges":[{"startOffset":6867,"endOffset":7622,"count":233}],"isBlockCoverage":true},{"functionName":"NodeError","ranges":[{"startOffset":6955,"endOffset":7536,"count":2},{"startOffset":7045,"endOffset":7254,"count":0}],"isBlockCoverage":true},{"functionName":"toString","ranges":[{"startOffset":7542,"endOffset":7615,"count":0}],"isBlockCoverage":false},{"functionName":"hideStackFrames","ranges":[{"startOffset":7694,"endOffset":8105,"count":26}],"isBlockCoverage":true},{"functionName":"hidden","ranges":[{"startOffset":7734,"endOffset":8102,"count":215},{"startOffset":7898,"endOffset":7962,"count":177},{"startOffset":8046,"endOffset":8092,"count":177}],"isBlockCoverage":true},{"functionName":"addCodeToName","ranges":[{"startOffset":8107,"endOffset":8723,"count":2},{"startOffset":8205,"endOffset":8260,"count":0},{"startOffset":8545,"endOffset":8689,"count":0}],"isBlockCoverage":true},{"functionName":"E","ranges":[{"startOffset":8835,"endOffset":9343,"count":234},{"startOffset":9077,"endOffset":9122,"count":4},{"startOffset":9122,"endOffset":9176,"count":230},{"startOffset":9211,"endOffset":9321,"count":3}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":9238,"endOffset":9315,"count":3}],"isBlockCoverage":true},{"functionName":"getMessage","ranges":[{"startOffset":9345,"endOffset":10149,"count":2},{"startOffset":9446,"endOffset":9482,"count":1},{"startOffset":9773,"endOffset":9831,"count":0},{"startOffset":10053,"endOffset":10148,"count":0}],"isBlockCoverage":true},{"functionName":"lazyUv","ranges":[{"startOffset":10167,"endOffset":10271,"count":0}],"isBlockCoverage":false},{"functionName":"uvErrmapGet","ranges":[{"startOffset":10328,"endOffset":10498,"count":0}],"isBlockCoverage":false},{"functionName":"uvException","ranges":[{"startOffset":10791,"endOffset":11987,"count":0}],"isBlockCoverage":false},{"functionName":"uvExceptionWithHostPort","ranges":[{"startOffset":12300,"endOffset":13205,"count":0}],"isBlockCoverage":false},{"functionName":"errnoException","ranges":[{"startOffset":13384,"endOffset":14090,"count":0}],"isBlockCoverage":false},{"functionName":"exceptionWithHostPort","ranges":[{"startOffset":14443,"endOffset":15659,"count":0}],"isBlockCoverage":false},{"functionName":"dnsException","ranges":[{"startOffset":15823,"endOffset":17338,"count":0}],"isBlockCoverage":false},{"functionName":"connResetException","ranges":[{"startOffset":17340,"endOffset":17495,"count":0}],"isBlockCoverage":false},{"functionName":"isStackOverflowError","ranges":[{"startOffset":17785,"endOffset":18163,"count":0}],"isBlockCoverage":false},{"functionName":"addNumericalSeparator","ranges":[{"startOffset":18244,"endOffset":18480,"count":0}],"isBlockCoverage":false},{"functionName":"beforeInspector","ranges":[{"startOffset":18759,"endOffset":19150,"count":0}],"isBlockCoverage":false},{"functionName":"afterInspector","ranges":[{"startOffset":19154,"endOffset":20492,"count":0}],"isBlockCoverage":false},{"functionName":"AbortError","ranges":[{"startOffset":20728,"endOffset":20846,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":22625,"endOffset":22789,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":25628,"endOffset":25743,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":26032,"endOffset":26126,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":28407,"endOffset":28635,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":30368,"endOffset":30586,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":32336,"endOffset":32636,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":32678,"endOffset":32822,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":32865,"endOffset":36070,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":36111,"endOffset":36367,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":36759,"endOffset":36922,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":37584,"endOffset":37719,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":37760,"endOffset":38084,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":38243,"endOffset":38391,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":38435,"endOffset":39209,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":39587,"endOffset":39751,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":39804,"endOffset":40135,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":40179,"endOffset":40486,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":40856,"endOffset":40931,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":40975,"endOffset":41263,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":41591,"endOffset":42022,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":42698,"endOffset":43221,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":43315,"endOffset":43416,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":44176,"endOffset":44874,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":44925,"endOffset":45117,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":45166,"endOffset":45482,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":45515,"endOffset":46392,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":46844,"endOffset":47103,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":48453,"endOffset":48622,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":50520,"endOffset":50651,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":51244,"endOffset":51527,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":52507,"endOffset":52605,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":52799,"endOffset":53154,"count":0}],"isBlockCoverage":false}]},{"scriptId":"12","url":"internal/util.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":12498,"count":1}],"isBlockCoverage":false},{"functionName":"lazyUv","ranges":[{"startOffset":991,"endOffset":1082,"count":0}],"isBlockCoverage":false},{"functionName":"removeColors","ranges":[{"startOffset":1084,"endOffset":1153,"count":0}],"isBlockCoverage":false},{"functionName":"isError","ranges":[{"startOffset":1155,"endOffset":1405,"count":0}],"isBlockCoverage":false},{"functionName":"deprecate","ranges":[{"startOffset":1690,"endOffset":2787,"count":9},{"startOffset":1764,"endOffset":1784,"count":0},{"startOffset":1844,"endOffset":1899,"count":0},{"startOffset":2541,"endOffset":2763,"count":8}],"isBlockCoverage":true},{"functionName":"deprecated","ranges":[{"startOffset":1925,"endOffset":2399,"count":0}],"isBlockCoverage":false},{"functionName":"decorateErrorStack","ranges":[{"startOffset":2789,"endOffset":3128,"count":0}],"isBlockCoverage":false},{"functionName":"assertCrypto","ranges":[{"startOffset":3130,"endOffset":3204,"count":0}],"isBlockCoverage":false},{"functionName":"normalizeEncoding","ranges":[{"startOffset":3383,"endOffset":3514,"count":12},{"startOffset":3453,"endOffset":3471,"count":0},{"startOffset":3487,"endOffset":3513,"count":0}],"isBlockCoverage":true},{"functionName":"slowCases","ranges":[{"startOffset":3516,"endOffset":5096,"count":0}],"isBlockCoverage":false},{"functionName":"emitExperimentalWarning","ranges":[{"startOffset":5098,"endOffset":5386,"count":0}],"isBlockCoverage":false},{"functionName":"filterDuplicateStrings","ranges":[{"startOffset":5388,"endOffset":5696,"count":0}],"isBlockCoverage":false},{"functionName":"cachedResult","ranges":[{"startOffset":5698,"endOffset":5841,"count":0}],"isBlockCoverage":false},{"functionName":"createClassWrapper","ranges":[{"startOffset":6106,"endOffset":6471,"count":0}],"isBlockCoverage":false},{"functionName":"getSignalsToNamesMapping","ranges":[{"startOffset":6500,"endOffset":6778,"count":0}],"isBlockCoverage":false},{"functionName":"convertToValidSignal","ranges":[{"startOffset":6780,"endOffset":7087,"count":0}],"isBlockCoverage":false},{"functionName":"getConstructorOf","ranges":[{"startOffset":7089,"endOffset":7435,"count":0}],"isBlockCoverage":false},{"functionName":"getSystemErrorName","ranges":[{"startOffset":7437,"endOffset":7566,"count":0}],"isBlockCoverage":false},{"functionName":"getSystemErrorMap","ranges":[{"startOffset":7568,"endOffset":7633,"count":0}],"isBlockCoverage":false},{"functionName":"promisify","ranges":[{"startOffset":7778,"endOffset":9249,"count":3},{"startOffset":7851,"endOffset":7916,"count":0},{"startOffset":7960,"endOffset":8281,"count":0}],"isBlockCoverage":true},{"functionName":"fn","ranges":[{"startOffset":8481,"endOffset":8962,"count":0}],"isBlockCoverage":false},{"functionName":"join","ranges":[{"startOffset":9344,"endOffset":9666,"count":0}],"isBlockCoverage":false},{"functionName":"spliceOne","ranges":[{"startOffset":9807,"endOffset":9934,"count":0}],"isBlockCoverage":false},{"functionName":"isInsideNodeModules","ranges":[{"startOffset":10016,"endOffset":11188,"count":0}],"isBlockCoverage":false},{"functionName":"once","ranges":[{"startOffset":11190,"endOffset":11348,"count":0}],"isBlockCoverage":false},{"functionName":"sleep","ranges":[{"startOffset":11371,"endOffset":11586,"count":0}],"isBlockCoverage":false}]},{"scriptId":"13","url":"events.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":26873,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":2207,"endOffset":2367,"count":0}],"isBlockCoverage":false},{"functionName":"EventEmitter","ranges":[{"startOffset":2372,"endOffset":2441,"count":3}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":2805,"endOffset":2861,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":2865,"endOffset":3099,"count":0}],"isBlockCoverage":false},{"functionName":"checkListener","ranges":[{"startOffset":3671,"endOffset":3821,"count":44},{"startOffset":3744,"endOffset":3819,"count":0}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":3910,"endOffset":3958,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":3967,"endOffset":4242,"count":0}],"isBlockCoverage":false},{"functionName":"EventEmitter.setMaxListeners","ranges":[{"startOffset":4618,"endOffset":5532,"count":0}],"isBlockCoverage":false},{"functionName":"EventEmitter.init","ranges":[{"startOffset":5555,"endOffset":6285,"count":3},{"startOffset":5606,"endOffset":5666,"count":1},{"startOffset":5668,"endOffset":5739,"count":2},{"startOffset":5810,"endOffset":5835,"count":2},{"startOffset":5837,"endOffset":6096,"count":0}],"isBlockCoverage":true},{"functionName":"addCatch","ranges":[{"startOffset":6288,"endOffset":6847,"count":0}],"isBlockCoverage":false},{"functionName":"emitUnhandledRejectionOrErr","ranges":[{"startOffset":6849,"endOffset":7507,"count":0}],"isBlockCoverage":false},{"functionName":"setMaxListeners","ranges":[{"startOffset":7678,"endOffset":7877,"count":0}],"isBlockCoverage":false},{"functionName":"_getMaxListeners","ranges":[{"startOffset":7880,"endOffset":8029,"count":0}],"isBlockCoverage":false},{"functionName":"getMaxListeners","ranges":[{"startOffset":8072,"endOffset":8135,"count":0}],"isBlockCoverage":false},{"functionName":"identicalSequenceRange","ranges":[{"startOffset":8263,"endOffset":8839,"count":0}],"isBlockCoverage":false},{"functionName":"enhanceStackTrace","ranges":[{"startOffset":8841,"endOffset":9447,"count":0}],"isBlockCoverage":false},{"functionName":"emit","ranges":[{"startOffset":9479,"endOffset":11762,"count":6},{"startOffset":9624,"endOffset":9662,"count":0},{"startOffset":9670,"endOffset":9704,"count":0},{"startOffset":9728,"endOffset":9757,"count":0},{"startOffset":9763,"endOffset":9800,"count":0},{"startOffset":9872,"endOffset":10804,"count":0},{"startOffset":10872,"endOffset":10885,"count":2},{"startOffset":10885,"endOffset":11213,"count":4},{"startOffset":11140,"endOffset":11158,"count":0},{"startOffset":11160,"endOffset":11209,"count":0},{"startOffset":11213,"endOffset":11744,"count":0},{"startOffset":11744,"endOffset":11761,"count":4}],"isBlockCoverage":true},{"functionName":"_addListener","ranges":[{"startOffset":11765,"endOffset":13820,"count":18},{"startOffset":11945,"endOffset":12029,"count":0},{"startOffset":12214,"endOffset":12494,"count":3},{"startOffset":12291,"endOffset":12310,"count":0},{"startOffset":12697,"endOffset":13800,"count":0}],"isBlockCoverage":true},{"functionName":"addListener","ranges":[{"startOffset":13859,"endOffset":13951,"count":18}],"isBlockCoverage":true},{"functionName":"prependListener","ranges":[{"startOffset":14064,"endOffset":14167,"count":0}],"isBlockCoverage":false},{"functionName":"onceWrapper","ranges":[{"startOffset":14170,"endOffset":14434,"count":0}],"isBlockCoverage":false},{"functionName":"_onceWrap","ranges":[{"startOffset":14436,"endOffset":14677,"count":13}],"isBlockCoverage":true},{"functionName":"once","ranges":[{"startOffset":14709,"endOffset":14835,"count":13}],"isBlockCoverage":true},{"functionName":"prependOnceListener","ranges":[{"startOffset":14887,"endOffset":15057,"count":0}],"isBlockCoverage":false},{"functionName":"removeListener","ranges":[{"startOffset":15179,"endOffset":16473,"count":13},{"startOffset":15328,"endOffset":15340,"count":0},{"startOffset":15413,"endOffset":15425,"count":0},{"startOffset":15455,"endOffset":15484,"count":12},{"startOffset":15537,"endOffset":15571,"count":0},{"startOffset":15667,"endOffset":15728,"count":1},{"startOffset":15746,"endOffset":16447,"count":0}],"isBlockCoverage":true},{"functionName":"removeAllListeners","ranges":[{"startOffset":16593,"endOffset":17919,"count":0}],"isBlockCoverage":false},{"functionName":"_listeners","ranges":[{"startOffset":17922,"endOffset":18317,"count":0}],"isBlockCoverage":false},{"functionName":"listeners","ranges":[{"startOffset":18354,"endOffset":18421,"count":0}],"isBlockCoverage":false},{"functionName":"rawListeners","ranges":[{"startOffset":18462,"endOffset":18533,"count":0}],"isBlockCoverage":false},{"functionName":"EventEmitter.listenerCount","ranges":[{"startOffset":18565,"endOffset":18733,"count":0}],"isBlockCoverage":false},{"functionName":"listenerCount","ranges":[{"startOffset":18790,"endOffset":19080,"count":24},{"startOffset":18963,"endOffset":18986,"count":11},{"startOffset":18986,"endOffset":19061,"count":13},{"startOffset":19022,"endOffset":19061,"count":0},{"startOffset":19065,"endOffset":19079,"count":13}],"isBlockCoverage":true},{"functionName":"eventNames","ranges":[{"startOffset":19118,"endOffset":19211,"count":0}],"isBlockCoverage":false},{"functionName":"arrayClone","ranges":[{"startOffset":19214,"endOffset":19674,"count":0}],"isBlockCoverage":false},{"functionName":"unwrapListeners","ranges":[{"startOffset":19676,"endOffset":19890,"count":0}],"isBlockCoverage":false},{"functionName":"getEventListeners","ranges":[{"startOffset":19892,"endOffset":20687,"count":0}],"isBlockCoverage":false},{"functionName":"once","ranges":[{"startOffset":20689,"endOffset":22475,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":22553,"endOffset":22574,"count":0}],"isBlockCoverage":false},{"functionName":"createIterResult","ranges":[{"startOffset":22589,"endOffset":22657,"count":0}],"isBlockCoverage":false},{"functionName":"addErrorHandlerIfEventEmitter","ranges":[{"startOffset":22659,"endOffset":22842,"count":0}],"isBlockCoverage":false},{"functionName":"eventTargetAgnosticRemoveListener","ranges":[{"startOffset":22844,"endOffset":23229,"count":0}],"isBlockCoverage":false},{"functionName":"eventTargetAgnosticAddListener","ranges":[{"startOffset":23231,"endOffset":23820,"count":0}],"isBlockCoverage":false},{"functionName":"on","ranges":[{"startOffset":23822,"endOffset":26872,"count":0}],"isBlockCoverage":false}]},{"scriptId":"14","url":"internal/util/inspect.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":71637,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":2893,"endOffset":2929,"count":62}],"isBlockCoverage":true},{"functionName":"isUndetectableObject","ranges":[{"startOffset":3020,"endOffset":3070,"count":0}],"isBlockCoverage":false},{"functionName":"getUserOptions","ranges":[{"startOffset":6215,"endOffset":7666,"count":0}],"isBlockCoverage":false},{"functionName":"inspect","ranges":[{"startOffset":7961,"endOffset":9878,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":9970,"endOffset":10015,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":10019,"endOffset":10227,"count":0}],"isBlockCoverage":false},{"functionName":"defineColorAlias","ranges":[{"startOffset":11964,"endOffset":12206,"count":12}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":12059,"endOffset":12099,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":12105,"endOffset":12151,"count":0}],"isBlockCoverage":false},{"functionName":"addQuotes","ranges":[{"startOffset":13216,"endOffset":13374,"count":0}],"isBlockCoverage":false},{"functionName":"escapeFn","ranges":[{"startOffset":13393,"endOffset":13425,"count":0}],"isBlockCoverage":false},{"functionName":"strEscape","ranges":[{"startOffset":13538,"endOffset":15164,"count":0}],"isBlockCoverage":false},{"functionName":"stylizeWithColor","ranges":[{"startOffset":15166,"endOffset":15432,"count":0}],"isBlockCoverage":false},{"functionName":"stylizeNoColor","ranges":[{"startOffset":15434,"endOffset":15480,"count":0}],"isBlockCoverage":false},{"functionName":"getEmptyFormatArray","ranges":[{"startOffset":15559,"endOffset":15606,"count":0}],"isBlockCoverage":false},{"functionName":"isInstanceof","ranges":[{"startOffset":15608,"endOffset":15726,"count":0}],"isBlockCoverage":false},{"functionName":"getConstructorName","ranges":[{"startOffset":15728,"endOffset":16988,"count":0}],"isBlockCoverage":false},{"functionName":"addPrototypeProperties","ranges":[{"startOffset":17175,"endOffset":19018,"count":0}],"isBlockCoverage":false},{"functionName":"getPrefix","ranges":[{"startOffset":19020,"endOffset":19407,"count":0}],"isBlockCoverage":false},{"functionName":"getKeys","ranges":[{"startOffset":19444,"endOffset":20386,"count":0}],"isBlockCoverage":false},{"functionName":"getCtxStyle","ranges":[{"startOffset":20388,"endOffset":20651,"count":0}],"isBlockCoverage":false},{"functionName":"formatProxy","ranges":[{"startOffset":20653,"endOffset":21102,"count":0}],"isBlockCoverage":false},{"functionName":"findTypedConstructor","ranges":[{"startOffset":21104,"endOffset":21627,"count":0}],"isBlockCoverage":false},{"functionName":"formatValue","ranges":[{"startOffset":21809,"endOffset":24348,"count":0}],"isBlockCoverage":false},{"functionName":"formatRaw","ranges":[{"startOffset":24350,"endOffset":34825,"count":0}],"isBlockCoverage":false},{"functionName":"getIteratorBraces","ranges":[{"startOffset":34827,"endOffset":35009,"count":0}],"isBlockCoverage":false},{"functionName":"getBoxedBase","ranges":[{"startOffset":35011,"endOffset":36185,"count":0}],"isBlockCoverage":false},{"functionName":"getClassBase","ranges":[{"startOffset":36187,"endOffset":36787,"count":0}],"isBlockCoverage":false},{"functionName":"getFunctionBase","ranges":[{"startOffset":36789,"endOffset":37882,"count":0}],"isBlockCoverage":false},{"functionName":"formatError","ranges":[{"startOffset":37884,"endOffset":41005,"count":0}],"isBlockCoverage":false},{"functionName":"groupArrayElements","ranges":[{"startOffset":41007,"endOffset":45258,"count":0}],"isBlockCoverage":false},{"functionName":"handleMaxCallStackSize","ranges":[{"startOffset":45260,"endOffset":45648,"count":0}],"isBlockCoverage":false},{"functionName":"formatNumber","ranges":[{"startOffset":45650,"endOffset":45827,"count":0}],"isBlockCoverage":false},{"functionName":"formatBigInt","ranges":[{"startOffset":45829,"endOffset":45901,"count":0}],"isBlockCoverage":false},{"functionName":"formatPrimitive","ranges":[{"startOffset":45903,"endOffset":47086,"count":0}],"isBlockCoverage":false},{"functionName":"formatNamespaceObject","ranges":[{"startOffset":47088,"endOffset":48208,"count":0}],"isBlockCoverage":false},{"functionName":"formatSpecialArray","ranges":[{"startOffset":48255,"endOffset":49462,"count":0}],"isBlockCoverage":false},{"functionName":"formatArrayBuffer","ranges":[{"startOffset":49464,"endOffset":50064,"count":0}],"isBlockCoverage":false},{"functionName":"formatArray","ranges":[{"startOffset":50066,"endOffset":50660,"count":0}],"isBlockCoverage":false},{"functionName":"formatTypedArray","ranges":[{"startOffset":50662,"endOffset":51678,"count":0}],"isBlockCoverage":false},{"functionName":"formatSet","ranges":[{"startOffset":51680,"endOffset":51912,"count":0}],"isBlockCoverage":false},{"functionName":"formatMap","ranges":[{"startOffset":51914,"endOffset":52212,"count":0}],"isBlockCoverage":false},{"functionName":"formatSetIterInner","ranges":[{"startOffset":52214,"endOffset":53027,"count":0}],"isBlockCoverage":false},{"functionName":"formatMapIterInner","ranges":[{"startOffset":53029,"endOffset":54351,"count":0}],"isBlockCoverage":false},{"functionName":"formatWeakCollection","ranges":[{"startOffset":54353,"endOffset":54445,"count":0}],"isBlockCoverage":false},{"functionName":"formatWeakSet","ranges":[{"startOffset":54447,"endOffset":54604,"count":0}],"isBlockCoverage":false},{"functionName":"formatWeakMap","ranges":[{"startOffset":54606,"endOffset":54763,"count":0}],"isBlockCoverage":false},{"functionName":"formatIterator","ranges":[{"startOffset":54765,"endOffset":55156,"count":0}],"isBlockCoverage":false},{"functionName":"formatPromise","ranges":[{"startOffset":55158,"endOffset":55623,"count":0}],"isBlockCoverage":false},{"functionName":"formatProperty","ranges":[{"startOffset":55625,"endOffset":58023,"count":0}],"isBlockCoverage":false},{"functionName":"isBelowBreakLength","ranges":[{"startOffset":58025,"endOffset":58967,"count":0}],"isBlockCoverage":false},{"functionName":"reduceToSingleString","ranges":[{"startOffset":58969,"endOffset":61715,"count":0}],"isBlockCoverage":false},{"functionName":"hasBuiltInToString","ranges":[{"startOffset":61717,"endOffset":62736,"count":0}],"isBlockCoverage":false},{"functionName":"firstErrorLine","ranges":[{"startOffset":62761,"endOffset":62800,"count":0}],"isBlockCoverage":false},{"functionName":"tryStringify","ranges":[{"startOffset":62830,"endOffset":63299,"count":0}],"isBlockCoverage":false},{"functionName":"format","ranges":[{"startOffset":63301,"endOffset":63385,"count":0}],"isBlockCoverage":false},{"functionName":"formatWithOptions","ranges":[{"startOffset":63387,"endOffset":63665,"count":12},{"startOffset":63510,"endOffset":63602,"count":0}],"isBlockCoverage":true},{"functionName":"formatWithOptionsInternal","ranges":[{"startOffset":63667,"endOffset":67451,"count":12},{"startOffset":63890,"endOffset":67250,"count":0},{"startOffset":67254,"endOffset":67450,"count":0}],"isBlockCoverage":true},{"functionName":"getStringWidth","ranges":[{"startOffset":67880,"endOffset":68431,"count":0}],"isBlockCoverage":false},{"functionName":"getStringWidth","ranges":[{"startOffset":68546,"endOffset":68958,"count":0}],"isBlockCoverage":false},{"functionName":"isFullWidthCodePoint","ranges":[{"startOffset":69126,"endOffset":70735,"count":0}],"isBlockCoverage":false},{"functionName":"isZeroWidthCodePoint","ranges":[{"startOffset":70769,"endOffset":71337,"count":0}],"isBlockCoverage":false},{"functionName":"stripVTControlCharacters","ranges":[{"startOffset":71427,"endOffset":71501,"count":0}],"isBlockCoverage":false}]},{"scriptId":"15","url":"internal/util/types.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1915,"count":1}],"isBlockCoverage":false},{"functionName":"isTypedArray","ranges":[{"startOffset":425,"endOffset":516,"count":0}],"isBlockCoverage":false},{"functionName":"isUint8Array","ranges":[{"startOffset":518,"endOffset":612,"count":48}],"isBlockCoverage":true},{"functionName":"isUint8ClampedArray","ranges":[{"startOffset":614,"endOffset":722,"count":0}],"isBlockCoverage":false},{"functionName":"isUint16Array","ranges":[{"startOffset":724,"endOffset":820,"count":0}],"isBlockCoverage":false},{"functionName":"isUint32Array","ranges":[{"startOffset":822,"endOffset":918,"count":0}],"isBlockCoverage":false},{"functionName":"isInt8Array","ranges":[{"startOffset":920,"endOffset":1012,"count":0}],"isBlockCoverage":false},{"functionName":"isInt16Array","ranges":[{"startOffset":1014,"endOffset":1108,"count":0}],"isBlockCoverage":false},{"functionName":"isInt32Array","ranges":[{"startOffset":1110,"endOffset":1204,"count":0}],"isBlockCoverage":false},{"functionName":"isFloat32Array","ranges":[{"startOffset":1206,"endOffset":1304,"count":0}],"isBlockCoverage":false},{"functionName":"isFloat64Array","ranges":[{"startOffset":1306,"endOffset":1404,"count":0}],"isBlockCoverage":false},{"functionName":"isBigInt64Array","ranges":[{"startOffset":1406,"endOffset":1506,"count":0}],"isBlockCoverage":false},{"functionName":"isBigUint64Array","ranges":[{"startOffset":1508,"endOffset":1610,"count":2}],"isBlockCoverage":true}]},{"scriptId":"16","url":"internal/assert.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":474,"count":1}],"isBlockCoverage":false},{"functionName":"lazyError","ranges":[{"startOffset":26,"endOffset":155,"count":0}],"isBlockCoverage":false},{"functionName":"assert","ranges":[{"startOffset":157,"endOffset":307,"count":6},{"startOffset":205,"endOffset":305,"count":0}],"isBlockCoverage":true},{"functionName":"fail","ranges":[{"startOffset":309,"endOffset":426,"count":0}],"isBlockCoverage":false}]},{"scriptId":"17","url":"internal/validators.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":7218,"count":1}],"isBlockCoverage":false},{"functionName":"isInt32","ranges":[{"startOffset":581,"endOffset":640,"count":1}],"isBlockCoverage":true},{"functionName":"isUint32","ranges":[{"startOffset":642,"endOffset":704,"count":14}],"isBlockCoverage":true},{"functionName":"parseFileMode","ranges":[{"startOffset":1326,"endOffset":1807,"count":14},{"startOffset":1389,"endOffset":1409,"count":0},{"startOffset":1411,"endOffset":1432,"count":0},{"startOffset":1480,"endOffset":1806,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":1852,"endOffset":2233,"count":50},{"startOffset":1972,"endOffset":2026,"count":0},{"startOffset":2066,"endOffset":2120,"count":0},{"startOffset":2163,"endOffset":2229,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":2279,"endOffset":2860,"count":1},{"startOffset":2441,"endOffset":2739,"count":0},{"startOffset":2776,"endOffset":2856,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":2904,"endOffset":3414,"count":0}],"isBlockCoverage":false},{"functionName":"validateString","ranges":[{"startOffset":3418,"endOffset":3550,"count":40},{"startOffset":3494,"endOffset":3548,"count":0}],"isBlockCoverage":true},{"functionName":"validateNumber","ranges":[{"startOffset":3552,"endOffset":3684,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":3724,"endOffset":4174,"count":0}],"isBlockCoverage":false},{"functionName":"validateBoolean","ranges":[{"startOffset":4178,"endOffset":4313,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":4357,"endOffset":4582,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":4624,"endOffset":4911,"count":0}],"isBlockCoverage":false},{"functionName":"validateSignalName","ranges":[{"startOffset":4915,"endOffset":5336,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":5377,"endOffset":5607,"count":0}],"isBlockCoverage":false},{"functionName":"validateEncoding","ranges":[{"startOffset":5611,"endOffset":5945,"count":0}],"isBlockCoverage":false},{"functionName":"validatePort","ranges":[{"startOffset":6089,"endOffset":6463,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":6506,"endOffset":6607,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":6655,"endOffset":6872,"count":0}],"isBlockCoverage":false}]},{"scriptId":"18","url":"buffer.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":36751,"count":1}],"isBlockCoverage":false},{"functionName":"validateOffset","ranges":[{"startOffset":2784,"endOffset":2868,"count":0}],"isBlockCoverage":false},{"functionName":"createUnsafeBuffer","ranges":[{"startOffset":4082,"endOffset":4218,"count":6}],"isBlockCoverage":true},{"functionName":"createPool","ranges":[{"startOffset":4220,"endOffset":4379,"count":1}],"isBlockCoverage":true},{"functionName":"alignPool","ranges":[{"startOffset":4395,"endOffset":4517,"count":2}],"isBlockCoverage":true},{"functionName":"showFlaggedDeprecation","ranges":[{"startOffset":4821,"endOffset":5501,"count":0}],"isBlockCoverage":false},{"functionName":"toInteger","ranges":[{"startOffset":5503,"endOffset":5721,"count":0}],"isBlockCoverage":false},{"functionName":"_copy","ranges":[{"startOffset":5723,"endOffset":6988,"count":0}],"isBlockCoverage":false},{"functionName":"_copyActual","ranges":[{"startOffset":6990,"endOffset":7592,"count":27},{"startOffset":7131,"endOffset":7185,"count":0},{"startOffset":7347,"endOffset":7362,"count":0},{"startOffset":7389,"endOffset":7404,"count":0},{"startOffset":7464,"endOffset":7540,"count":0}],"isBlockCoverage":true},{"functionName":"Buffer","ranges":[{"startOffset":8168,"endOffset":8501,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":8594,"endOffset":8622,"count":0}],"isBlockCoverage":false},{"functionName":"from","ranges":[{"startOffset":8879,"endOffset":9843,"count":2},{"startOffset":9008,"endOffset":9059,"count":0},{"startOffset":9061,"endOffset":9842,"count":0}],"isBlockCoverage":true},{"functionName":"of","ranges":[{"startOffset":10214,"endOffset":10366,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":10655,"endOffset":10876,"count":55},{"startOffset":10699,"endOffset":10764,"count":0},{"startOffset":10807,"endOffset":10874,"count":0}],"isBlockCoverage":true},{"functionName":"alloc","ranges":[{"startOffset":10979,"endOffset":11224,"count":50},{"startOffset":11063,"endOffset":11076,"count":0},{"startOffset":11077,"endOffset":11088,"count":0},{"startOffset":11090,"endOffset":11191,"count":0}],"isBlockCoverage":true},{"functionName":"allocUnsafe","ranges":[{"startOffset":11403,"endOffset":11478,"count":5}],"isBlockCoverage":true},{"functionName":"allocUnsafeSlow","ranges":[{"startOffset":11719,"endOffset":11808,"count":0}],"isBlockCoverage":false},{"functionName":"SlowBuffer","ranges":[{"startOffset":11904,"endOffset":11994,"count":0}],"isBlockCoverage":false},{"functionName":"allocate","ranges":[{"startOffset":12108,"endOffset":12440,"count":5},{"startOffset":12151,"endOffset":12185,"count":0},{"startOffset":12224,"endOffset":12403,"count":0}],"isBlockCoverage":true},{"functionName":"fromStringFast","ranges":[{"startOffset":12442,"endOffset":12988,"count":2},{"startOffset":12568,"endOffset":12617,"count":0},{"startOffset":12663,"endOffset":12676,"count":0},{"startOffset":12809,"endOffset":12935,"count":0}],"isBlockCoverage":true},{"functionName":"fromString","ranges":[{"startOffset":12990,"endOffset":13443,"count":2},{"startOffset":13076,"endOffset":13100,"count":0},{"startOffset":13139,"endOffset":13163,"count":0},{"startOffset":13221,"endOffset":13403,"count":0}],"isBlockCoverage":true},{"functionName":"fromArrayBuffer","ranges":[{"startOffset":13445,"endOffset":14142,"count":0}],"isBlockCoverage":false},{"functionName":"fromArrayLike","ranges":[{"startOffset":14144,"endOffset":14518,"count":0}],"isBlockCoverage":false},{"functionName":"fromObject","ranges":[{"startOffset":14520,"endOffset":14826,"count":0}],"isBlockCoverage":false},{"functionName":"isBuffer","ranges":[{"startOffset":14865,"endOffset":14919,"count":0}],"isBlockCoverage":false},{"functionName":"compare","ranges":[{"startOffset":14939,"endOffset":15264,"count":0}],"isBlockCoverage":false},{"functionName":"isEncoding","ranges":[{"startOffset":15287,"endOffset":15438,"count":12}],"isBlockCoverage":true},{"functionName":"concat","ranges":[{"startOffset":15504,"endOffset":16708,"count":5},{"startOffset":15563,"endOffset":15627,"count":0},{"startOffset":15658,"endOffset":15682,"count":0},{"startOffset":15772,"endOffset":15849,"count":27},{"startOffset":15853,"endOffset":15902,"count":0},{"startOffset":16004,"endOffset":16352,"count":27},{"startOffset":16059,"endOffset":16291,"count":0},{"startOffset":16443,"endOffset":16688,"count":0}],"isBlockCoverage":true},{"functionName":"base64ByteLength","ranges":[{"startOffset":16711,"endOffset":16947,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":17082,"endOffset":17146,"count":2}],"isBlockCoverage":true},{"functionName":"slice","ranges":[{"startOffset":17159,"endOffset":17205,"count":12}],"isBlockCoverage":true},{"functionName":"indexOf","ranges":[{"startOffset":17220,"endOffset":17316,"count":0}],"isBlockCoverage":false},{"functionName":"byteLength","ranges":[{"startOffset":17409,"endOffset":17438,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":17451,"endOffset":17515,"count":0}],"isBlockCoverage":false},{"functionName":"slice","ranges":[{"startOffset":17528,"endOffset":17574,"count":2}],"isBlockCoverage":true},{"functionName":"indexOf","ranges":[{"startOffset":17589,"endOffset":17688,"count":0}],"isBlockCoverage":false},{"functionName":"byteLength","ranges":[{"startOffset":17787,"endOffset":17816,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":17829,"endOffset":17893,"count":0}],"isBlockCoverage":false},{"functionName":"slice","ranges":[{"startOffset":17906,"endOffset":17952,"count":0}],"isBlockCoverage":false},{"functionName":"indexOf","ranges":[{"startOffset":17967,"endOffset":18066,"count":0}],"isBlockCoverage":false},{"functionName":"byteLength","ranges":[{"startOffset":18162,"endOffset":18187,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":18200,"endOffset":18266,"count":0}],"isBlockCoverage":false},{"functionName":"slice","ranges":[{"startOffset":18279,"endOffset":18327,"count":0}],"isBlockCoverage":false},{"functionName":"indexOf","ranges":[{"startOffset":18342,"endOffset":18440,"count":0}],"isBlockCoverage":false},{"functionName":"byteLength","ranges":[{"startOffset":18533,"endOffset":18558,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":18571,"endOffset":18636,"count":0}],"isBlockCoverage":false},{"functionName":"slice","ranges":[{"startOffset":18649,"endOffset":18696,"count":0}],"isBlockCoverage":false},{"functionName":"indexOf","ranges":[{"startOffset":18711,"endOffset":18923,"count":0}],"isBlockCoverage":false},{"functionName":"byteLength","ranges":[{"startOffset":19019,"endOffset":19070,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":19083,"endOffset":19149,"count":0}],"isBlockCoverage":false},{"functionName":"slice","ranges":[{"startOffset":19162,"endOffset":19210,"count":0}],"isBlockCoverage":false},{"functionName":"indexOf","ranges":[{"startOffset":19225,"endOffset":19439,"count":0}],"isBlockCoverage":false},{"functionName":"byteLength","ranges":[{"startOffset":19526,"endOffset":19557,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":19570,"endOffset":19633,"count":0}],"isBlockCoverage":false},{"functionName":"slice","ranges":[{"startOffset":19646,"endOffset":19691,"count":0}],"isBlockCoverage":false},{"functionName":"indexOf","ranges":[{"startOffset":19706,"endOffset":19914,"count":0}],"isBlockCoverage":false},{"functionName":"getEncodingOps","ranges":[{"startOffset":19922,"endOffset":21477,"count":14},{"startOffset":20048,"endOffset":20072,"count":12},{"startOffset":20072,"endOffset":20128,"count":2},{"startOffset":20128,"endOffset":20294,"count":0},{"startOffset":20299,"endOffset":20704,"count":0},{"startOffset":20709,"endOffset":20839,"count":0},{"startOffset":20844,"endOffset":20976,"count":0},{"startOffset":20981,"endOffset":21348,"count":0},{"startOffset":21353,"endOffset":21471,"count":0}],"isBlockCoverage":true},{"functionName":"byteLength","ranges":[{"startOffset":21479,"endOffset":22136,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":22276,"endOffset":22370,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":22448,"endOffset":22546,"count":0}],"isBlockCoverage":false},{"functionName":"copy","ranges":[{"startOffset":22578,"endOffset":22711,"count":0}],"isBlockCoverage":false},{"functionName":"toString","ranges":[{"startOffset":22992,"endOffset":23571,"count":14},{"startOffset":23064,"endOffset":23112,"count":0},{"startOffset":23164,"endOffset":23174,"count":0},{"startOffset":23204,"endOffset":23214,"count":0},{"startOffset":23263,"endOffset":23275,"count":0},{"startOffset":23291,"endOffset":23312,"count":0},{"startOffset":23338,"endOffset":23348,"count":0},{"startOffset":23384,"endOffset":23418,"count":0},{"startOffset":23489,"endOffset":23530,"count":0}],"isBlockCoverage":true},{"functionName":"equals","ranges":[{"startOffset":23600,"endOffset":23954,"count":0}],"isBlockCoverage":false},{"functionName":"inspect","ranges":[{"startOffset":24082,"endOffset":25077,"count":0}],"isBlockCoverage":false},{"functionName":"compare","ranges":[{"startOffset":25173,"endOffset":26322,"count":0}],"isBlockCoverage":false},{"functionName":"bidirectionalIndexOf","ranges":[{"startOffset":26750,"endOffset":28057,"count":0}],"isBlockCoverage":false},{"functionName":"indexOf","ranges":[{"startOffset":28086,"endOffset":28203,"count":0}],"isBlockCoverage":false},{"functionName":"lastIndexOf","ranges":[{"startOffset":28237,"endOffset":28359,"count":0}],"isBlockCoverage":false},{"functionName":"includes","ranges":[{"startOffset":28390,"endOffset":28495,"count":0}],"isBlockCoverage":false},{"functionName":"fill","ranges":[{"startOffset":28673,"endOffset":28772,"count":0}],"isBlockCoverage":false},{"functionName":"_fill","ranges":[{"startOffset":28775,"endOffset":30684,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":30711,"endOffset":31726,"count":0}],"isBlockCoverage":false},{"functionName":"toJSON","ranges":[{"startOffset":31755,"endOffset":31989,"count":0}],"isBlockCoverage":false},{"functionName":"adjustOffset","ranges":[{"startOffset":31992,"endOffset":32426,"count":72},{"startOffset":32232,"endOffset":32270,"count":36},{"startOffset":32270,"endOffset":32333,"count":0},{"startOffset":32333,"endOffset":32357,"count":36},{"startOffset":32357,"endOffset":32381,"count":5},{"startOffset":32381,"endOffset":32411,"count":31},{"startOffset":32411,"endOffset":32414,"count":0},{"startOffset":32415,"endOffset":32423,"count":31}],"isBlockCoverage":true},{"functionName":"slice","ranges":[{"startOffset":32453,"endOffset":32753,"count":36},{"startOffset":32614,"endOffset":32625,"count":0},{"startOffset":32673,"endOffset":32676,"count":0}],"isBlockCoverage":true},{"functionName":"swap","ranges":[{"startOffset":32756,"endOffset":32827,"count":0}],"isBlockCoverage":false},{"functionName":"swap16","ranges":[{"startOffset":32855,"endOffset":33259,"count":0}],"isBlockCoverage":false},{"functionName":"swap32","ranges":[{"startOffset":33288,"endOffset":33732,"count":0}],"isBlockCoverage":false},{"functionName":"swap64","ranges":[{"startOffset":33761,"endOffset":34269,"count":0}],"isBlockCoverage":false},{"functionName":"transcode","ranges":[{"startOffset":34582,"endOffset":35322,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":35391,"endOffset":35581,"count":0}],"isBlockCoverage":false},{"functionName":"btoa","ranges":[{"startOffset":35585,"endOffset":35921,"count":0}],"isBlockCoverage":false},{"functionName":"atob","ranges":[{"startOffset":36017,"endOffset":36338,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":36664,"endOffset":36699,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":36705,"endOffset":36742,"count":0}],"isBlockCoverage":false}]},{"scriptId":"19","url":"internal/buffer.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":29666,"count":1}],"isBlockCoverage":false},{"functionName":"checkBounds","ranges":[{"startOffset":1107,"endOffset":1323,"count":0}],"isBlockCoverage":false},{"functionName":"checkInt","ranges":[{"startOffset":1325,"endOffset":1947,"count":0}],"isBlockCoverage":false},{"functionName":"boundsError","ranges":[{"startOffset":1949,"endOffset":2352,"count":0}],"isBlockCoverage":false},{"functionName":"readBigUInt64LE","ranges":[{"startOffset":2372,"endOffset":2871,"count":0}],"isBlockCoverage":false},{"functionName":"readBigUInt64BE","ranges":[{"startOffset":2873,"endOffset":3372,"count":0}],"isBlockCoverage":false},{"functionName":"readBigInt64LE","ranges":[{"startOffset":3374,"endOffset":3875,"count":0}],"isBlockCoverage":false},{"functionName":"readBigInt64BE","ranges":[{"startOffset":3877,"endOffset":4372,"count":0}],"isBlockCoverage":false},{"functionName":"readUIntLE","ranges":[{"startOffset":4374,"endOffset":4926,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt48LE","ranges":[{"startOffset":4928,"endOffset":5311,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt40LE","ranges":[{"startOffset":5313,"endOffset":5669,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt32LE","ranges":[{"startOffset":5671,"endOffset":5997,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt24LE","ranges":[{"startOffset":5999,"endOffset":6287,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt16LE","ranges":[{"startOffset":6289,"endOffset":6549,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt8","ranges":[{"startOffset":6551,"endOffset":6731,"count":0}],"isBlockCoverage":false},{"functionName":"readUIntBE","ranges":[{"startOffset":6733,"endOffset":7285,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt48BE","ranges":[{"startOffset":7287,"endOffset":7670,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt40BE","ranges":[{"startOffset":7672,"endOffset":8028,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt32BE","ranges":[{"startOffset":8030,"endOffset":8356,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt24BE","ranges":[{"startOffset":8358,"endOffset":8646,"count":0}],"isBlockCoverage":false},{"functionName":"readUInt16BE","ranges":[{"startOffset":8648,"endOffset":8908,"count":0}],"isBlockCoverage":false},{"functionName":"readIntLE","ranges":[{"startOffset":8910,"endOffset":9455,"count":0}],"isBlockCoverage":false},{"functionName":"readInt48LE","ranges":[{"startOffset":9457,"endOffset":9888,"count":0}],"isBlockCoverage":false},{"functionName":"readInt40LE","ranges":[{"startOffset":9890,"endOffset":10277,"count":0}],"isBlockCoverage":false},{"functionName":"readInt32LE","ranges":[{"startOffset":10279,"endOffset":10614,"count":0}],"isBlockCoverage":false},{"functionName":"readInt24LE","ranges":[{"startOffset":10616,"endOffset":10948,"count":0}],"isBlockCoverage":false},{"functionName":"readInt16LE","ranges":[{"startOffset":10950,"endOffset":11256,"count":0}],"isBlockCoverage":false},{"functionName":"readInt8","ranges":[{"startOffset":11258,"endOffset":11466,"count":0}],"isBlockCoverage":false},{"functionName":"readIntBE","ranges":[{"startOffset":11468,"endOffset":12013,"count":0}],"isBlockCoverage":false},{"functionName":"readInt48BE","ranges":[{"startOffset":12015,"endOffset":12444,"count":0}],"isBlockCoverage":false},{"functionName":"readInt40BE","ranges":[{"startOffset":12446,"endOffset":12834,"count":0}],"isBlockCoverage":false},{"functionName":"readInt32BE","ranges":[{"startOffset":12836,"endOffset":13171,"count":0}],"isBlockCoverage":false},{"functionName":"readInt24BE","ranges":[{"startOffset":13173,"endOffset":13505,"count":0}],"isBlockCoverage":false},{"functionName":"readInt16BE","ranges":[{"startOffset":13507,"endOffset":13813,"count":0}],"isBlockCoverage":false},{"functionName":"readFloatBackwards","ranges":[{"startOffset":13830,"endOffset":14235,"count":0}],"isBlockCoverage":false},{"functionName":"readFloatForwards","ranges":[{"startOffset":14237,"endOffset":14641,"count":0}],"isBlockCoverage":false},{"functionName":"readDoubleBackwards","ranges":[{"startOffset":14643,"endOffset":15213,"count":0}],"isBlockCoverage":false},{"functionName":"readDoubleForwards","ranges":[{"startOffset":15215,"endOffset":15784,"count":0}],"isBlockCoverage":false},{"functionName":"writeBigU_Int64LE","ranges":[{"startOffset":15805,"endOffset":16287,"count":0}],"isBlockCoverage":false},{"functionName":"writeBigUInt64LE","ranges":[{"startOffset":16289,"endOffset":16411,"count":0}],"isBlockCoverage":false},{"functionName":"writeBigU_Int64BE","ranges":[{"startOffset":16413,"endOffset":16911,"count":0}],"isBlockCoverage":false},{"functionName":"writeBigUInt64BE","ranges":[{"startOffset":16913,"endOffset":17035,"count":0}],"isBlockCoverage":false},{"functionName":"writeBigInt64LE","ranges":[{"startOffset":17037,"endOffset":17181,"count":0}],"isBlockCoverage":false},{"functionName":"writeBigInt64BE","ranges":[{"startOffset":17183,"endOffset":17327,"count":0}],"isBlockCoverage":false},{"functionName":"writeUIntLE","ranges":[{"startOffset":17329,"endOffset":17938,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int48LE","ranges":[{"startOffset":17940,"endOffset":18353,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int40LE","ranges":[{"startOffset":18355,"endOffset":18734,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int32LE","ranges":[{"startOffset":18736,"endOffset":19043,"count":0}],"isBlockCoverage":false},{"functionName":"writeUInt32LE","ranges":[{"startOffset":19045,"endOffset":19151,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int24LE","ranges":[{"startOffset":19153,"endOffset":19412,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int16LE","ranges":[{"startOffset":19414,"endOffset":19610,"count":0}],"isBlockCoverage":false},{"functionName":"writeUInt16LE","ranges":[{"startOffset":19612,"endOffset":19714,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int8","ranges":[{"startOffset":19716,"endOffset":20128,"count":0}],"isBlockCoverage":false},{"functionName":"writeUInt8","ranges":[{"startOffset":20130,"endOffset":20224,"count":0}],"isBlockCoverage":false},{"functionName":"writeUIntBE","ranges":[{"startOffset":20226,"endOffset":20835,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int48BE","ranges":[{"startOffset":20837,"endOffset":21258,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int40BE","ranges":[{"startOffset":21260,"endOffset":21622,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int32BE","ranges":[{"startOffset":21624,"endOffset":21939,"count":0}],"isBlockCoverage":false},{"functionName":"writeUInt32BE","ranges":[{"startOffset":21941,"endOffset":22047,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int24BE","ranges":[{"startOffset":22049,"endOffset":22314,"count":0}],"isBlockCoverage":false},{"functionName":"writeU_Int16BE","ranges":[{"startOffset":22316,"endOffset":22512,"count":0}],"isBlockCoverage":false},{"functionName":"writeUInt16BE","ranges":[{"startOffset":22514,"endOffset":22616,"count":0}],"isBlockCoverage":false},{"functionName":"writeIntLE","ranges":[{"startOffset":22618,"endOffset":23280,"count":0}],"isBlockCoverage":false},{"functionName":"writeInt32LE","ranges":[{"startOffset":23282,"endOffset":23397,"count":0}],"isBlockCoverage":false},{"functionName":"writeInt16LE","ranges":[{"startOffset":23399,"endOffset":23506,"count":0}],"isBlockCoverage":false},{"functionName":"writeInt8","ranges":[{"startOffset":23508,"endOffset":23605,"count":0}],"isBlockCoverage":false},{"functionName":"writeIntBE","ranges":[{"startOffset":23607,"endOffset":24269,"count":0}],"isBlockCoverage":false},{"functionName":"writeInt32BE","ranges":[{"startOffset":24271,"endOffset":24386,"count":0}],"isBlockCoverage":false},{"functionName":"writeInt16BE","ranges":[{"startOffset":24388,"endOffset":24495,"count":0}],"isBlockCoverage":false},{"functionName":"writeDoubleForwards","ranges":[{"startOffset":24514,"endOffset":24980,"count":0}],"isBlockCoverage":false},{"functionName":"writeDoubleBackwards","ranges":[{"startOffset":24982,"endOffset":25449,"count":0}],"isBlockCoverage":false},{"functionName":"writeFloatForwards","ranges":[{"startOffset":25451,"endOffset":25752,"count":0}],"isBlockCoverage":false},{"functionName":"writeFloatBackwards","ranges":[{"startOffset":25754,"endOffset":26056,"count":0}],"isBlockCoverage":false},{"functionName":"addBufferPrototypeMethods","ranges":[{"startOffset":26098,"endOffset":29155,"count":1},{"startOffset":28181,"endOffset":28201,"count":0},{"startOffset":28255,"endOffset":28274,"count":0},{"startOffset":28330,"endOffset":28351,"count":0},{"startOffset":28407,"endOffset":28427,"count":0},{"startOffset":28484,"endOffset":28505,"count":0},{"startOffset":28561,"endOffset":28581,"count":0},{"startOffset":28639,"endOffset":28661,"count":0},{"startOffset":28719,"endOffset":28740,"count":0}],"isBlockCoverage":true},{"functionName":"markAsUntransferable","ranges":[{"startOffset":29311,"endOffset":29575,"count":1},{"startOffset":29379,"endOffset":29407,"count":0},{"startOffset":29430,"endOffset":29437,"count":0}],"isBlockCoverage":true}]},{"scriptId":"20","url":"internal/worker/js_transferable.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1310,"count":1}],"isBlockCoverage":false},{"functionName":"setup","ranges":[{"startOffset":304,"endOffset":1091,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":585,"endOffset":1087,"count":0}],"isBlockCoverage":false}]},{"scriptId":"21","url":"internal/process/per_thread.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":10598,"count":1}],"isBlockCoverage":false},{"functionName":"assert","ranges":[{"startOffset":796,"endOffset":884,"count":0}],"isBlockCoverage":false},{"functionName":"wrapProcessMethods","ranges":[{"startOffset":962,"endOffset":6703,"count":1}],"isBlockCoverage":true},{"functionName":"_rawDebug","ranges":[{"startOffset":1173,"endOffset":1255,"count":0}],"isBlockCoverage":false},{"functionName":"cpuUsage","ranges":[{"startOffset":1466,"endOffset":3025,"count":0}],"isBlockCoverage":false},{"functionName":"previousValueIsValid","ranges":[{"startOffset":3178,"endOffset":3315,"count":0}],"isBlockCoverage":false},{"functionName":"hrtime","ranges":[{"startOffset":3539,"endOffset":4142,"count":0}],"isBlockCoverage":false},{"functionName":"hrtimeBigInt","ranges":[{"startOffset":4329,"endOffset":4423,"count":0}],"isBlockCoverage":false},{"functionName":"memoryUsage","ranges":[{"startOffset":4468,"endOffset":4694,"count":0}],"isBlockCoverage":false},{"functionName":"exit","ranges":[{"startOffset":4698,"endOffset":5136,"count":0}],"isBlockCoverage":false},{"functionName":"kill","ranges":[{"startOffset":5140,"endOffset":5785,"count":0}],"isBlockCoverage":false},{"functionName":"resourceUsage","ranges":[{"startOffset":5836,"endOffset":6569,"count":0}],"isBlockCoverage":false},{"functionName":"buildAllowedFlags","ranges":[{"startOffset":6914,"endOffset":9974,"count":0}],"isBlockCoverage":false},{"functionName":"toggleTraceCategoryState","ranges":[{"startOffset":10164,"endOffset":10494,"count":1},{"startOffset":10244,"endOffset":10419,"count":0},{"startOffset":10451,"endOffset":10492,"count":0}],"isBlockCoverage":true}]},{"scriptId":"22","url":"internal/async_hooks.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":19081,"count":1}],"isBlockCoverage":false},{"functionName":"useDomainTrampoline","ranges":[{"startOffset":5274,"endOffset":5328,"count":0}],"isBlockCoverage":false},{"functionName":"callbackTrampoline","ranges":[{"startOffset":5330,"endOffset":5923,"count":0}],"isBlockCoverage":false},{"functionName":"executionAsyncResource","ranges":[{"startOffset":5999,"endOffset":6497,"count":0}],"isBlockCoverage":false},{"functionName":"inspectExceptionValue","ranges":[{"startOffset":6499,"endOffset":6635,"count":0}],"isBlockCoverage":false},{"functionName":"fatalError","ranges":[{"startOffset":6696,"endOffset":7082,"count":0}],"isBlockCoverage":false},{"functionName":"lookupPublicResource","ranges":[{"startOffset":7084,"endOffset":7433,"count":0}],"isBlockCoverage":false},{"functionName":"emitInitNative","ranges":[{"startOffset":7624,"endOffset":8717,"count":0}],"isBlockCoverage":false},{"functionName":"emitHook","ranges":[{"startOffset":8818,"endOffset":9768,"count":0}],"isBlockCoverage":false},{"functionName":"emitHookFactory","ranges":[{"startOffset":9770,"endOffset":10030,"count":4}],"isBlockCoverage":true},{"functionName":"getHookArrays","ranges":[{"startOffset":10059,"endOffset":10549,"count":0}],"isBlockCoverage":false},{"functionName":"storeActiveHooks","ranges":[{"startOffset":10552,"endOffset":10852,"count":0}],"isBlockCoverage":false},{"functionName":"copyHooks","ranges":[{"startOffset":10854,"endOffset":11119,"count":0}],"isBlockCoverage":false},{"functionName":"restoreActiveHooks","ranges":[{"startOffset":11234,"endOffset":11439,"count":0}],"isBlockCoverage":false},{"functionName":"trackPromise","ranges":[{"startOffset":11441,"endOffset":11798,"count":0}],"isBlockCoverage":false},{"functionName":"fastPromiseHook","ranges":[{"startOffset":11800,"endOffset":12936,"count":0}],"isBlockCoverage":false},{"functionName":"enableHooks","ranges":[{"startOffset":12967,"endOffset":13027,"count":0}],"isBlockCoverage":false},{"functionName":"updatePromiseHookMode","ranges":[{"startOffset":13055,"endOffset":13346,"count":0}],"isBlockCoverage":false},{"functionName":"disableHooks","ranges":[{"startOffset":13348,"endOffset":13623,"count":0}],"isBlockCoverage":false},{"functionName":"disablePromiseHookIfNecessary","ranges":[{"startOffset":13625,"endOffset":13751,"count":0}],"isBlockCoverage":false},{"functionName":"newAsyncId","ranges":[{"startOffset":13952,"endOffset":14022,"count":12}],"isBlockCoverage":true},{"functionName":"getOrSetAsyncId","ranges":[{"startOffset":14024,"endOffset":14214,"count":0}],"isBlockCoverage":false},{"functionName":"getDefaultTriggerAsyncId","ranges":[{"startOffset":14397,"endOffset":14687,"count":12},{"startOffset":14653,"endOffset":14686,"count":0}],"isBlockCoverage":true},{"functionName":"clearDefaultTriggerAsyncId","ranges":[{"startOffset":14690,"endOffset":14779,"count":0}],"isBlockCoverage":false},{"functionName":"defaultTriggerAsyncIdScope","ranges":[{"startOffset":14782,"endOffset":15257,"count":0}],"isBlockCoverage":false},{"functionName":"hasHooks","ranges":[{"startOffset":15259,"endOffset":15322,"count":60}],"isBlockCoverage":true},{"functionName":"enabledHooksExist","ranges":[{"startOffset":15324,"endOffset":15383,"count":12}],"isBlockCoverage":true},{"functionName":"initHooksExist","ranges":[{"startOffset":15385,"endOffset":15440,"count":12}],"isBlockCoverage":true},{"functionName":"afterHooksExist","ranges":[{"startOffset":15442,"endOffset":15499,"count":0}],"isBlockCoverage":false},{"functionName":"destroyHooksExist","ranges":[{"startOffset":15501,"endOffset":15562,"count":12}],"isBlockCoverage":true},{"functionName":"emitInitScript","ranges":[{"startOffset":15565,"endOffset":15973,"count":0}],"isBlockCoverage":false},{"functionName":"emitBeforeScript","ranges":[{"startOffset":15976,"endOffset":16152,"count":12},{"startOffset":16124,"endOffset":16150,"count":0}],"isBlockCoverage":true},{"functionName":"emitAfterScript","ranges":[{"startOffset":16155,"endOffset":16275,"count":12},{"startOffset":16219,"endOffset":16244,"count":0}],"isBlockCoverage":true},{"functionName":"emitDestroyScript","ranges":[{"startOffset":16278,"endOffset":16488,"count":0}],"isBlockCoverage":false},{"functionName":"hasAsyncIdStack","ranges":[{"startOffset":16491,"endOffset":16554,"count":0}],"isBlockCoverage":false},{"functionName":"pushAsyncContext","ranges":[{"startOffset":16620,"endOffset":17190,"count":12},{"startOffset":16840,"endOffset":16890,"count":0}],"isBlockCoverage":true},{"functionName":"popAsyncContext","ranges":[{"startOffset":17255,"endOffset":17879,"count":12},{"startOffset":17371,"endOffset":17384,"count":0},{"startOffset":17463,"endOffset":17569,"count":0}],"isBlockCoverage":true},{"functionName":"executionAsyncId","ranges":[{"startOffset":17882,"endOffset":17958,"count":0}],"isBlockCoverage":false},{"functionName":"triggerAsyncId","ranges":[{"startOffset":17960,"endOffset":18032,"count":0}],"isBlockCoverage":false}]},{"scriptId":"23","url":"internal/process/task_queues.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":4409,"count":1}],"isBlockCoverage":false},{"functionName":"hasTickScheduled","ranges":[{"startOffset":1007,"endOffset":1082,"count":0}],"isBlockCoverage":false},{"functionName":"setHasTickScheduled","ranges":[{"startOffset":1084,"endOffset":1170,"count":24},{"startOffset":1160,"endOffset":1163,"count":12},{"startOffset":1164,"endOffset":1167,"count":12}],"isBlockCoverage":true},{"functionName":"runNextTicks","ranges":[{"startOffset":1272,"endOffset":1468,"count":0}],"isBlockCoverage":false},{"functionName":"processTicksAndRejections","ranges":[{"startOffset":1470,"endOffset":2438,"count":12},{"startOffset":1762,"endOffset":1795,"count":0},{"startOffset":1928,"endOffset":1970,"count":0},{"startOffset":1983,"endOffset":2034,"count":0},{"startOffset":2047,"endOffset":2107,"count":0},{"startOffset":2120,"endOffset":2147,"count":0},{"startOffset":2231,"endOffset":2252,"count":0}],"isBlockCoverage":true},{"functionName":"nextTick","ranges":[{"startOffset":2582,"endOffset":3497,"count":12},{"startOffset":2654,"endOffset":2695,"count":0},{"startOffset":2725,"endOffset":2732,"count":0},{"startOffset":2780,"endOffset":2794,"count":0},{"startOffset":2841,"endOffset":2892,"count":0},{"startOffset":2897,"endOffset":2962,"count":0},{"startOffset":2967,"endOffset":3110,"count":0},{"startOffset":3409,"endOffset":3469,"count":0}],"isBlockCoverage":true},{"functionName":"runMicrotask","ranges":[{"startOffset":3499,"endOffset":3675,"count":0}],"isBlockCoverage":false},{"functionName":"queueMicrotask","ranges":[{"startOffset":3747,"endOffset":4107,"count":0}],"isBlockCoverage":false},{"functionName":"setupTaskQueue","ranges":[{"startOffset":4130,"endOffset":4387,"count":1}],"isBlockCoverage":true}]},{"scriptId":"24","url":"internal/process/promises.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":9605,"count":1}],"isBlockCoverage":false},{"functionName":"setHasRejectionToWarn","ranges":[{"startOffset":1918,"endOffset":2008,"count":12},{"startOffset":1998,"endOffset":2001,"count":0}],"isBlockCoverage":true},{"functionName":"hasRejectionToWarn","ranges":[{"startOffset":2010,"endOffset":2089,"count":0}],"isBlockCoverage":false},{"functionName":"getUnhandledRejectionsMode","ranges":[{"startOffset":2091,"endOffset":2626,"count":0}],"isBlockCoverage":false},{"functionName":"promiseRejectHandler","ranges":[{"startOffset":2628,"endOffset":3197,"count":0}],"isBlockCoverage":false},{"functionName":"resolveError","ranges":[{"startOffset":3199,"endOffset":3449,"count":0}],"isBlockCoverage":false},{"functionName":"unhandledRejection","ranges":[{"startOffset":3451,"endOffset":3745,"count":0}],"isBlockCoverage":false},{"functionName":"handledRejection","ranges":[{"startOffset":3747,"endOffset":4563,"count":0}],"isBlockCoverage":false},{"functionName":"emitUnhandledRejectionWarning","ranges":[{"startOffset":4635,"endOffset":5531,"count":0}],"isBlockCoverage":false},{"functionName":"emitDeprecationWarning","ranges":[{"startOffset":5564,"endOffset":5849,"count":0}],"isBlockCoverage":false},{"functionName":"processPromiseRejections","ranges":[{"startOffset":6022,"endOffset":8506,"count":12},{"startOffset":6180,"endOffset":6346,"count":0},{"startOffset":6411,"endOffset":8410,"count":0}],"isBlockCoverage":true},{"functionName":"getErrorWithoutStack","ranges":[{"startOffset":8508,"endOffset":8926,"count":0}],"isBlockCoverage":false},{"functionName":"generateUnhandledRejectionError","ranges":[{"startOffset":8928,"endOffset":9398,"count":0}],"isBlockCoverage":false},{"functionName":"listenForRejections","ranges":[{"startOffset":9400,"endOffset":9484,"count":1}],"isBlockCoverage":true}]},{"scriptId":"25","url":"internal/fixed_queue.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":4184,"count":1}],"isBlockCoverage":false},{"functionName":"FixedCircularBuffer","ranges":[{"startOffset":2959,"endOffset":3073,"count":1}],"isBlockCoverage":true},{"functionName":"isEmpty","ranges":[{"startOffset":3077,"endOffset":3129,"count":48}],"isBlockCoverage":true},{"functionName":"isFull","ranges":[{"startOffset":3133,"endOffset":3200,"count":12}],"isBlockCoverage":true},{"functionName":"push","ranges":[{"startOffset":3204,"endOffset":3291,"count":12}],"isBlockCoverage":true},{"functionName":"shift","ranges":[{"startOffset":3295,"endOffset":3510,"count":24},{"startOffset":3388,"endOffset":3509,"count":12}],"isBlockCoverage":true},{"functionName":"FixedQueue","ranges":[{"startOffset":3552,"endOffset":3626,"count":1}],"isBlockCoverage":true},{"functionName":"isEmpty","ranges":[{"startOffset":3630,"endOffset":3677,"count":24}],"isBlockCoverage":true},{"functionName":"push","ranges":[{"startOffset":3681,"endOffset":3945,"count":12},{"startOffset":3722,"endOffset":3915,"count":0}],"isBlockCoverage":true},{"functionName":"shift","ranges":[{"startOffset":3949,"endOffset":4180,"count":24},{"startOffset":4064,"endOffset":4159,"count":0}],"isBlockCoverage":true}]},{"scriptId":"26","url":"async_hooks.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":9502,"count":1}],"isBlockCoverage":false},{"functionName":"AsyncHook","ranges":[{"startOffset":1416,"endOffset":2250,"count":1},{"startOffset":1544,"endOffset":1586,"count":0},{"startOffset":1616,"endOffset":1647,"count":0},{"startOffset":1655,"endOffset":1699,"count":0},{"startOffset":1728,"endOffset":1758,"count":0},{"startOffset":1766,"endOffset":1809,"count":0},{"startOffset":1840,"endOffset":1872,"count":0},{"startOffset":1880,"endOffset":1925,"count":0},{"startOffset":1963,"endOffset":2002,"count":0},{"startOffset":2010,"endOffset":2062,"count":0}],"isBlockCoverage":true},{"functionName":"enable","ranges":[{"startOffset":2254,"endOffset":3491,"count":0}],"isBlockCoverage":false},{"functionName":"disable","ranges":[{"startOffset":3495,"endOffset":4271,"count":0}],"isBlockCoverage":false},{"functionName":"createHook","ranges":[{"startOffset":4276,"endOffset":4333,"count":1}],"isBlockCoverage":true},{"functionName":"AsyncResource","ranges":[{"startOffset":4426,"endOffset":5613,"count":0}],"isBlockCoverage":false},{"functionName":"runInAsyncScope","ranges":[{"startOffset":5617,"endOffset":5979,"count":0}],"isBlockCoverage":false},{"functionName":"emitDestroy","ranges":[{"startOffset":5983,"endOffset":6158,"count":0}],"isBlockCoverage":false},{"functionName":"asyncId","ranges":[{"startOffset":6162,"endOffset":6211,"count":0}],"isBlockCoverage":false},{"functionName":"triggerAsyncId","ranges":[{"startOffset":6215,"endOffset":6279,"count":0}],"isBlockCoverage":false},{"functionName":"bind","ranges":[{"startOffset":6283,"endOffset":6785,"count":0}],"isBlockCoverage":false},{"functionName":"bind","ranges":[{"startOffset":6796,"endOffset":6915,"count":0}],"isBlockCoverage":false},{"functionName":"init","ranges":[{"startOffset":6978,"endOffset":7260,"count":0}],"isBlockCoverage":false},{"functionName":"AsyncLocalStorage","ranges":[{"startOffset":7357,"endOffset":7454,"count":0}],"isBlockCoverage":false},{"functionName":"disable","ranges":[{"startOffset":7458,"endOffset":7783,"count":0}],"isBlockCoverage":false},{"functionName":"_enable","ranges":[{"startOffset":7787,"endOffset":7933,"count":0}],"isBlockCoverage":false},{"functionName":"_propagate","ranges":[{"startOffset":8002,"endOffset":8176,"count":0}],"isBlockCoverage":false},{"functionName":"enterWith","ranges":[{"startOffset":8180,"endOffset":8312,"count":0}],"isBlockCoverage":false},{"functionName":"run","ranges":[{"startOffset":8316,"endOffset":8892,"count":0}],"isBlockCoverage":false},{"functionName":"exit","ranges":[{"startOffset":8896,"endOffset":9094,"count":0}],"isBlockCoverage":false},{"functionName":"getStore","ranges":[{"startOffset":9098,"endOffset":9237,"count":0}],"isBlockCoverage":false}]},{"scriptId":"27","url":"internal/console/global.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1939,"count":1}],"isBlockCoverage":false}]},{"scriptId":"28","url":"internal/console/constructor.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":19933,"count":1}],"isBlockCoverage":false},{"functionName":"Console","ranges":[{"startOffset":2592,"endOffset":4763,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":4972,"endOffset":5026,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":5274,"endOffset":5480,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":5683,"endOffset":6271,"count":1}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":5865,"endOffset":5960,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":5972,"endOffset":6002,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":6104,"endOffset":6203,"count":36},{"startOffset":6137,"endOffset":6164,"count":1}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":6215,"endOffset":6245,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":6341,"endOffset":7523,"count":1}],"isBlockCoverage":true},{"functionName":"value","ranges":[{"startOffset":7593,"endOffset":9039,"count":12},{"startOffset":7804,"endOffset":7818,"count":0},{"startOffset":7872,"endOffset":7906,"count":0},{"startOffset":7972,"endOffset":8161,"count":0},{"startOffset":8219,"endOffset":8247,"count":0},{"startOffset":8604,"endOffset":8631,"count":1},{"startOffset":8685,"endOffset":8969,"count":0}],"isBlockCoverage":true},{"functionName":"value","ranges":[{"startOffset":9112,"endOffset":9602,"count":12},{"startOffset":9226,"endOffset":9332,"count":0},{"startOffset":9406,"endOffset":9527,"count":0},{"startOffset":9548,"endOffset":9570,"count":0}],"isBlockCoverage":true},{"functionName":"value","ranges":[{"startOffset":9673,"endOffset":9801,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":9872,"endOffset":10000,"count":12}],"isBlockCoverage":true},{"functionName":"createWriteErrorHandler","ranges":[{"startOffset":10089,"endOffset":10978,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":10157,"endOffset":10975,"count":12},{"startOffset":10402,"endOffset":10426,"count":0}],"isBlockCoverage":true},{"functionName":"log","ranges":[{"startOffset":11007,"endOffset":11094,"count":0}],"isBlockCoverage":false},{"functionName":"warn","ranges":[{"startOffset":11100,"endOffset":11188,"count":12}],"isBlockCoverage":true},{"functionName":"dir","ranges":[{"startOffset":11194,"endOffset":11379,"count":0}],"isBlockCoverage":false},{"functionName":"time","ranges":[{"startOffset":11384,"endOffset":11742,"count":0}],"isBlockCoverage":false},{"functionName":"timeEnd","ranges":[{"startOffset":11747,"endOffset":12036,"count":0}],"isBlockCoverage":false},{"functionName":"timeLog","ranges":[{"startOffset":12041,"endOffset":12279,"count":0}],"isBlockCoverage":false},{"functionName":"trace","ranges":[{"startOffset":12291,"endOffset":12477,"count":0}],"isBlockCoverage":false},{"functionName":"assert","ranges":[{"startOffset":12482,"endOffset":12700,"count":0}],"isBlockCoverage":false},{"functionName":"clear","ranges":[{"startOffset":12761,"endOffset":13191,"count":0}],"isBlockCoverage":false},{"functionName":"count","ranges":[{"startOffset":13252,"endOffset":13708,"count":0}],"isBlockCoverage":false},{"functionName":"countReset","ranges":[{"startOffset":13774,"endOffset":14062,"count":0}],"isBlockCoverage":false},{"functionName":"group","ranges":[{"startOffset":14067,"endOffset":14235,"count":0}],"isBlockCoverage":false},{"functionName":"groupEnd","ranges":[{"startOffset":14240,"endOffset":14408,"count":0}],"isBlockCoverage":false},{"functionName":"table","ranges":[{"startOffset":14457,"endOffset":17867,"count":0}],"isBlockCoverage":false},{"functionName":"timeLogImpl","ranges":[{"startOffset":17908,"endOffset":18404,"count":0}],"isBlockCoverage":false},{"functionName":"pad","ranges":[{"startOffset":18406,"endOffset":18483,"count":0}],"isBlockCoverage":false},{"functionName":"formatTime","ranges":[{"startOffset":18485,"endOffset":19247,"count":0}],"isBlockCoverage":false},{"functionName":"isArray","ranges":[{"startOffset":19381,"endOffset":19437,"count":0}],"isBlockCoverage":false},{"functionName":"noop","ranges":[{"startOffset":19440,"endOffset":19458,"count":0}],"isBlockCoverage":false}]},{"scriptId":"29","url":"internal/constants.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1582,"count":1}],"isBlockCoverage":false}]},{"scriptId":"30","url":"internal/util/inspector.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2215,"count":1}],"isBlockCoverage":false},{"functionName":"sendInspectorCommand","ranges":[{"startOffset":92,"endOffset":434,"count":0}],"isBlockCoverage":false},{"functionName":"installConsoleExtensions","ranges":[{"startOffset":508,"endOffset":1062,"count":0}],"isBlockCoverage":false},{"functionName":"wrapConsole","ranges":[{"startOffset":1141,"endOffset":1931,"count":1},{"startOffset":1299,"endOffset":1929,"count":23},{"startOffset":1514,"endOffset":1807,"count":19},{"startOffset":1807,"endOffset":1925,"count":4}],"isBlockCoverage":true},{"functionName":"get consoleFromVM","ranges":[{"startOffset":2103,"endOffset":2154,"count":0}],"isBlockCoverage":false},{"functionName":"set consoleFromVM","ranges":[{"startOffset":2158,"endOffset":2211,"count":1}],"isBlockCoverage":true}]},{"scriptId":"31","url":"internal/url.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":41325,"count":1}],"isBlockCoverage":false},{"functionName":"toUSVString","ranges":[{"startOffset":2224,"endOffset":2520,"count":2},{"startOffset":2477,"endOffset":2519,"count":0}],"isBlockCoverage":true},{"functionName":"serializeTupleOrigin","ranges":[{"startOffset":2732,"endOffset":2850,"count":0}],"isBlockCoverage":false},{"functionName":"URLContext","ranges":[{"startOffset":3254,"endOffset":3477,"count":20}],"isBlockCoverage":true},{"functionName":"URLSearchParams","ranges":[{"startOffset":3767,"endOffset":6130,"count":18},{"startOffset":3882,"endOffset":6068,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":6134,"endOffset":7204,"count":0}],"isBlockCoverage":false},{"functionName":"onParseComplete","ranges":[{"startOffset":7208,"endOffset":7901,"count":18},{"startOffset":7463,"endOffset":7473,"count":0},{"startOffset":7536,"endOffset":7546,"count":0},{"startOffset":7627,"endOffset":7631,"count":0}],"isBlockCoverage":true},{"functionName":"onParseError","ranges":[{"startOffset":7903,"endOffset":7978,"count":2}],"isBlockCoverage":true},{"functionName":"onParseProtocolComplete","ranges":[{"startOffset":7980,"endOffset":8325,"count":0}],"isBlockCoverage":false},{"functionName":"onParseHostnameComplete","ranges":[{"startOffset":8327,"endOffset":8673,"count":0}],"isBlockCoverage":false},{"functionName":"onParsePortComplete","ranges":[{"startOffset":8675,"endOffset":8837,"count":0}],"isBlockCoverage":false},{"functionName":"onParseHostComplete","ranges":[{"startOffset":8839,"endOffset":9145,"count":0}],"isBlockCoverage":false},{"functionName":"onParsePathComplete","ranges":[{"startOffset":9147,"endOffset":9641,"count":4},{"startOffset":9413,"endOffset":9481,"count":0}],"isBlockCoverage":true},{"functionName":"onParseSearchComplete","ranges":[{"startOffset":9643,"endOffset":9811,"count":0}],"isBlockCoverage":false},{"functionName":"onParseHashComplete","ranges":[{"startOffset":9813,"endOffset":9983,"count":0}],"isBlockCoverage":false},{"functionName":"URL","ranges":[{"startOffset":9999,"endOffset":10327,"count":20},{"startOffset":10134,"endOffset":10186,"count":3}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":10331,"endOffset":10412,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":10416,"endOffset":10509,"count":54}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":10584,"endOffset":10784,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":10788,"endOffset":11721,"count":0}],"isBlockCoverage":false},{"functionName":"format","ranges":[{"startOffset":11890,"endOffset":13071,"count":18},{"startOffset":11975,"endOffset":12036,"count":0},{"startOffset":12446,"endOffset":12607,"count":0},{"startOffset":12639,"endOffset":12676,"count":0},{"startOffset":12730,"endOffset":12752,"count":0},{"startOffset":12760,"endOffset":12824,"count":0},{"startOffset":12936,"endOffset":12959,"count":0},{"startOffset":13021,"endOffset":13047,"count":0}],"isBlockCoverage":true},{"functionName":"toString","ranges":[{"startOffset":13345,"endOffset":13404,"count":2}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":13470,"endOffset":13515,"count":16}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":13521,"endOffset":13701,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":13782,"endOffset":14410,"count":10},{"startOffset":13922,"endOffset":14166,"count":0},{"startOffset":14175,"endOffset":14187,"count":0},{"startOffset":14196,"endOffset":14211,"count":0},{"startOffset":14220,"endOffset":14233,"count":0},{"startOffset":14242,"endOffset":14256,"count":0},{"startOffset":14265,"endOffset":14276,"count":0},{"startOffset":14285,"endOffset":14368,"count":0}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":14480,"endOffset":14528,"count":17}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":14534,"endOffset":14903,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":14973,"endOffset":15023,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":15029,"endOffset":15427,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":15497,"endOffset":15547,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":15553,"endOffset":15951,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":16017,"endOffset":16173,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":16179,"endOffset":16470,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":16540,"endOffset":16592,"count":8}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":16598,"endOffset":16897,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":16963,"endOffset":17063,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":17069,"endOffset":17386,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":17456,"endOffset":17651,"count":50},{"startOffset":17535,"endOffset":17554,"count":0},{"startOffset":17596,"endOffset":17606,"count":0}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":17657,"endOffset":17876,"count":4},{"startOffset":17767,"endOffset":17774,"count":0}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":17944,"endOffset":18083,"count":2},{"startOffset":18016,"endOffset":18031,"count":0},{"startOffset":18051,"endOffset":18082,"count":0}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":18089,"endOffset":18581,"count":2},{"startOffset":18275,"endOffset":18523,"count":0}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":18668,"endOffset":18714,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":18780,"endOffset":18931,"count":2},{"startOffset":18858,"endOffset":18876,"count":0},{"startOffset":18896,"endOffset":18930,"count":0}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":18937,"endOffset":19355,"count":2},{"startOffset":19159,"endOffset":19354,"count":0}],"isBlockCoverage":true},{"functionName":"toJSON","ranges":[{"startOffset":19501,"endOffset":19558,"count":0}],"isBlockCoverage":false},{"functionName":"update","ranges":[{"startOffset":19568,"endOffset":19873,"count":0}],"isBlockCoverage":false},{"functionName":"initSearchParams","ranges":[{"startOffset":19875,"endOffset":20015,"count":20},{"startOffset":19972,"endOffset":20014,"count":0}],"isBlockCoverage":true},{"functionName":"parseParams","ranges":[{"startOffset":20124,"endOffset":22425,"count":0}],"isBlockCoverage":false},{"functionName":"serializeParams","ranges":[{"startOffset":23404,"endOffset":23964,"count":0}],"isBlockCoverage":false},{"functionName":"defineIDLClass","ranges":[{"startOffset":24019,"endOffset":24707,"count":2},{"startOffset":24357,"endOffset":24503,"count":13},{"startOffset":24558,"endOffset":24705,"count":1}],"isBlockCoverage":true},{"functionName":"merge","ranges":[{"startOffset":24727,"endOffset":25357,"count":0}],"isBlockCoverage":false},{"functionName":"append","ranges":[{"startOffset":25424,"endOffset":25811,"count":0}],"isBlockCoverage":false},{"functionName":"delete","ranges":[{"startOffset":25816,"endOffset":26315,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":26320,"endOffset":26754,"count":0}],"isBlockCoverage":false},{"functionName":"getAll","ranges":[{"startOffset":26759,"endOffset":27227,"count":0}],"isBlockCoverage":false},{"functionName":"has","ranges":[{"startOffset":27232,"endOffset":27660,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":27665,"endOffset":28691,"count":0}],"isBlockCoverage":false},{"functionName":"sort","ranges":[{"startOffset":28696,"endOffset":29873,"count":0}],"isBlockCoverage":false},{"functionName":"entries","ranges":[{"startOffset":30036,"endOffset":30245,"count":0}],"isBlockCoverage":false},{"functionName":"forEach","ranges":[{"startOffset":30250,"endOffset":30822,"count":0}],"isBlockCoverage":false},{"functionName":"keys","ranges":[{"startOffset":30877,"endOffset":31077,"count":0}],"isBlockCoverage":false},{"functionName":"values","ranges":[{"startOffset":31082,"endOffset":31286,"count":0}],"isBlockCoverage":false},{"functionName":"toString","ranges":[{"startOffset":31419,"endOffset":31619,"count":0}],"isBlockCoverage":false},{"functionName":"createSearchParamsIterator","ranges":[{"startOffset":31899,"endOffset":32102,"count":0}],"isBlockCoverage":false},{"functionName":"next","ranges":[{"startOffset":32326,"endOffset":33079,"count":0}],"isBlockCoverage":false},{"functionName":"defineIDLClass","ranges":[{"startOffset":33083,"endOffset":34297,"count":0}],"isBlockCoverage":false},{"functionName":"domainToASCII","ranges":[{"startOffset":34303,"endOffset":34478,"count":0}],"isBlockCoverage":false},{"functionName":"domainToUnicode","ranges":[{"startOffset":34480,"endOffset":34659,"count":0}],"isBlockCoverage":false},{"functionName":"urlToOptions","ranges":[{"startOffset":34802,"endOffset":35345,"count":0}],"isBlockCoverage":false},{"functionName":"getPathFromURLWin32","ranges":[{"startOffset":35381,"endOffset":36774,"count":0}],"isBlockCoverage":false},{"functionName":"getPathFromURLPosix","ranges":[{"startOffset":36776,"endOffset":37280,"count":8},{"startOffset":36839,"endOffset":36895,"count":0},{"startOffset":36973,"endOffset":37239,"count":328},{"startOffset":37004,"endOffset":37235,"count":0}],"isBlockCoverage":true},{"functionName":"fileURLToPath","ranges":[{"startOffset":37282,"endOffset":37629,"count":8},{"startOffset":37349,"endOffset":37370,"count":0},{"startOffset":37408,"endOffset":37472,"count":0},{"startOffset":37510,"endOffset":37551,"count":0},{"startOffset":37571,"endOffset":37598,"count":0}],"isBlockCoverage":true},{"functionName":"encodePathChars","ranges":[{"startOffset":38389,"endOffset":38945,"count":4},{"startOffset":38460,"endOffset":38509,"count":0},{"startOffset":38615,"endOffset":38666,"count":0},{"startOffset":38702,"endOffset":38751,"count":0},{"startOffset":38787,"endOffset":38843,"count":0},{"startOffset":38879,"endOffset":38924,"count":0}],"isBlockCoverage":true},{"functionName":"pathToFileURL","ranges":[{"startOffset":38947,"endOffset":40062,"count":4},{"startOffset":39035,"endOffset":39065,"count":0},{"startOffset":39067,"endOffset":39616,"count":0},{"startOffset":39848,"endOffset":39911,"count":3},{"startOffset":39871,"endOffset":39910,"count":0},{"startOffset":39913,"endOffset":39966,"count":1},{"startOffset":39974,"endOffset":39990,"count":1}],"isBlockCoverage":true},{"functionName":"isURLInstance","ranges":[{"startOffset":40064,"endOffset":40183,"count":29},{"startOffset":40157,"endOffset":40180,"count":10}],"isBlockCoverage":true},{"functionName":"toPathIfFileURL","ranges":[{"startOffset":40185,"endOffset":40330,"count":21},{"startOffset":40268,"endOffset":40289,"count":19},{"startOffset":40289,"endOffset":40329,"count":2}],"isBlockCoverage":true},{"functionName":"constructUrl","ranges":[{"startOffset":40332,"endOffset":41032,"count":0}],"isBlockCoverage":false}]},{"scriptId":"32","url":"internal/querystring.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":3021,"count":1}],"isBlockCoverage":false},{"functionName":"encodeStr","ranges":[{"startOffset":1336,"endOffset":2959,"count":0}],"isBlockCoverage":false}]},{"scriptId":"33","url":"path.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":43399,"count":1}],"isBlockCoverage":false},{"functionName":"isPathSeparator","ranges":[{"startOffset":1492,"endOffset":1596,"count":0}],"isBlockCoverage":false},{"functionName":"isPosixPathSeparator","ranges":[{"startOffset":1598,"endOffset":1675,"count":705}],"isBlockCoverage":true},{"functionName":"isWindowsDeviceRoot","ranges":[{"startOffset":1677,"endOffset":1847,"count":0}],"isBlockCoverage":false},{"functionName":"normalizeString","ranges":[{"startOffset":1910,"endOffset":3714,"count":17},{"startOffset":2127,"endOffset":3698,"count":705},{"startOffset":2160,"endOffset":2186,"count":688},{"startOffset":2186,"endOffset":2277,"count":17},{"startOffset":2235,"endOffset":2277,"count":0},{"startOffset":2277,"endOffset":2310,"count":688},{"startOffset":2310,"endOffset":3599,"count":120},{"startOffset":2342,"endOffset":2355,"count":100},{"startOffset":2357,"endOffset":2382,"count":20},{"startOffset":2382,"endOffset":3556,"count":100},{"startOffset":2404,"endOffset":3343,"count":2},{"startOffset":2460,"endOffset":2518,"count":0},{"startOffset":2519,"endOffset":2577,"count":0},{"startOffset":2715,"endOffset":2791,"count":0},{"startOffset":3024,"endOffset":3193,"count":0},{"startOffset":3203,"endOffset":3335,"count":0},{"startOffset":3343,"endOffset":3556,"count":98},{"startOffset":3389,"endOffset":3442,"count":81},{"startOffset":3442,"endOffset":3501,"count":17},{"startOffset":3556,"endOffset":3599,"count":118},{"startOffset":3599,"endOffset":3694,"count":568},{"startOffset":3627,"endOffset":3641,"count":22},{"startOffset":3643,"endOffset":3664,"count":7},{"startOffset":3664,"endOffset":3694,"count":561}],"isBlockCoverage":true},{"functionName":"_format","ranges":[{"startOffset":3892,"endOffset":4317,"count":0}],"isBlockCoverage":false},{"functionName":"resolve","ranges":[{"startOffset":4435,"endOffset":8838,"count":0}],"isBlockCoverage":false},{"functionName":"normalize","ranges":[{"startOffset":8904,"endOffset":11655,"count":0}],"isBlockCoverage":false},{"functionName":"isAbsolute","ranges":[{"startOffset":11722,"endOffset":12090,"count":0}],"isBlockCoverage":false},{"functionName":"join","ranges":[{"startOffset":12159,"endOffset":14215,"count":0}],"isBlockCoverage":false},{"functionName":"relative","ranges":[{"startOffset":14512,"endOffset":17986,"count":0}],"isBlockCoverage":false},{"functionName":"toNamespacedPath","ranges":[{"startOffset":17991,"endOffset":19019,"count":0}],"isBlockCoverage":false},{"functionName":"dirname","ranges":[{"startOffset":19085,"endOffset":21413,"count":0}],"isBlockCoverage":false},{"functionName":"basename","ranges":[{"startOffset":21506,"endOffset":24141,"count":0}],"isBlockCoverage":false},{"functionName":"extname","ranges":[{"startOffset":24207,"endOffset":26216,"count":0}],"isBlockCoverage":false},{"functionName":"parse","ranges":[{"startOffset":26420,"endOffset":30886,"count":0}],"isBlockCoverage":false},{"functionName":"resolve","ranges":[{"startOffset":31069,"endOffset":31965,"count":17},{"startOffset":31192,"endOffset":31212,"count":42},{"startOffset":31219,"endOffset":31519,"count":26},{"startOffset":31247,"endOffset":31256,"count":25},{"startOffset":31257,"endOffset":31272,"count":1},{"startOffset":31369,"endOffset":31396,"count":0},{"startOffset":31904,"endOffset":31954,"count":0},{"startOffset":31955,"endOffset":31960,"count":0}],"isBlockCoverage":true},{"functionName":"normalize","ranges":[{"startOffset":32031,"endOffset":32606,"count":0}],"isBlockCoverage":false},{"functionName":"isAbsolute","ranges":[{"startOffset":32673,"endOffset":32802,"count":4}],"isBlockCoverage":true},{"functionName":"join","ranges":[{"startOffset":32871,"endOffset":33285,"count":0}],"isBlockCoverage":false},{"functionName":"relative","ranges":[{"startOffset":33375,"endOffset":35585,"count":0}],"isBlockCoverage":false},{"functionName":"toNamespacedPath","ranges":[{"startOffset":35590,"endOffset":35666,"count":32}],"isBlockCoverage":true},{"functionName":"dirname","ranges":[{"startOffset":35732,"endOffset":36360,"count":0}],"isBlockCoverage":false},{"functionName":"basename","ranges":[{"startOffset":36453,"endOffset":38773,"count":0}],"isBlockCoverage":false},{"functionName":"extname","ranges":[{"startOffset":38839,"endOffset":40497,"count":2},{"startOffset":39172,"endOffset":40118,"count":18},{"startOffset":39252,"endOffset":39498,"count":2},{"startOffset":39472,"endOffset":39498,"count":0},{"startOffset":39498,"endOffset":39521,"count":16},{"startOffset":39521,"endOffset":39678,"count":2},{"startOffset":39678,"endOffset":39708,"count":16},{"startOffset":39708,"endOffset":39908,"count":2},{"startOffset":39837,"endOffset":39900,"count":0},{"startOffset":39908,"endOffset":40112,"count":14},{"startOffset":39935,"endOffset":40112,"count":10},{"startOffset":40357,"endOffset":40389,"count":0},{"startOffset":40390,"endOffset":40428,"count":0},{"startOffset":40431,"endOffset":40455,"count":0}],"isBlockCoverage":true},{"functionName":"parse","ranges":[{"startOffset":40706,"endOffset":43060,"count":0}],"isBlockCoverage":false}]},{"scriptId":"34","url":"internal/encoding.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":15916,"count":1}],"isBlockCoverage":false},{"functionName":"lazyBuffer","ranges":[{"startOffset":958,"endOffset":1067,"count":0}],"isBlockCoverage":false},{"functionName":"validateEncoder","ranges":[{"startOffset":1069,"endOffset":1194,"count":0}],"isBlockCoverage":false},{"functionName":"validateDecoder","ranges":[{"startOffset":1196,"endOffset":1321,"count":2},{"startOffset":1277,"endOffset":1319,"count":0}],"isBlockCoverage":true},{"functionName":"validateArgument","ranges":[{"startOffset":1323,"endOffset":1533,"count":3},{"startOffset":1470,"endOffset":1531,"count":0}],"isBlockCoverage":true},{"functionName":"trimAsciiWhitespace","ranges":[{"startOffset":8274,"endOffset":8756,"count":0}],"isBlockCoverage":false},{"functionName":"getEncodingFromLabel","ranges":[{"startOffset":8758,"endOffset":8937,"count":1},{"startOffset":8869,"endOffset":8936,"count":0}],"isBlockCoverage":true},{"functionName":"TextEncoder","ranges":[{"startOffset":9008,"endOffset":9054,"count":0}],"isBlockCoverage":false},{"functionName":"get encoding","ranges":[{"startOffset":9058,"endOffset":9125,"count":0}],"isBlockCoverage":false},{"functionName":"encode","ranges":[{"startOffset":9129,"endOffset":9221,"count":0}],"isBlockCoverage":false},{"functionName":"encodeInto","ranges":[{"startOffset":9225,"endOffset":9535,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":9539,"endOffset":9934,"count":0}],"isBlockCoverage":false},{"functionName":"makeTextDecoderICU","ranges":[{"startOffset":10305,"endOffset":11966,"count":1}],"isBlockCoverage":true},{"functionName":"TextDecoder","ranges":[{"startOffset":10443,"endOffset":11170,"count":1},{"startOffset":10675,"endOffset":10722,"count":0},{"startOffset":10806,"endOffset":10829,"count":0},{"startOffset":10870,"endOffset":10898,"count":0},{"startOffset":11000,"endOffset":11047,"count":0}],"isBlockCoverage":true},{"functionName":"decode","ranges":[{"startOffset":11177,"endOffset":11937,"count":2},{"startOffset":11279,"endOffset":11330,"count":0},{"startOffset":11367,"endOffset":11546,"count":0},{"startOffset":11693,"endOffset":11696,"count":0},{"startOffset":11814,"endOffset":11896,"count":0}],"isBlockCoverage":true},{"functionName":"makeTextDecoderJS","ranges":[{"startOffset":11968,"endOffset":14509,"count":0}],"isBlockCoverage":false},{"functionName":"get encoding","ranges":[{"startOffset":14634,"endOffset":14715,"count":0}],"isBlockCoverage":false},{"functionName":"get fatal","ranges":[{"startOffset":14722,"endOffset":14849,"count":0}],"isBlockCoverage":false},{"functionName":"get ignoreBOM","ranges":[{"startOffset":14856,"endOffset":15011,"count":0}],"isBlockCoverage":false},{"functionName":"ObjectGetOwnPropertyDescriptors","ranges":[{"startOffset":15018,"endOffset":15632,"count":0}],"isBlockCoverage":false}]},{"scriptId":"35","url":"timers.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":8324,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":1797,"endOffset":1822,"count":0}],"isBlockCoverage":false},{"functionName":"unenroll","ranges":[{"startOffset":2319,"endOffset":3503,"count":0}],"isBlockCoverage":false},{"functionName":"enroll","ranges":[{"startOffset":3710,"endOffset":3963,"count":0}],"isBlockCoverage":false},{"functionName":"setTimeout","ranges":[{"startOffset":3994,"endOffset":4639,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":4716,"endOffset":4850,"count":0}],"isBlockCoverage":false},{"functionName":"clearTimeout","ranges":[{"startOffset":4856,"endOffset":5222,"count":0}],"isBlockCoverage":false},{"functionName":"setInterval","ranges":[{"startOffset":5224,"endOffset":5871,"count":0}],"isBlockCoverage":false},{"functionName":"clearInterval","ranges":[{"startOffset":5873,"endOffset":6171,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.close","ranges":[{"startOffset":6199,"endOffset":6250,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.","ranges":[{"startOffset":6292,"endOffset":6453,"count":0}],"isBlockCoverage":false},{"functionName":"setImmediate","ranges":[{"startOffset":6456,"endOffset":6997,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":7076,"endOffset":7212,"count":0}],"isBlockCoverage":false},{"functionName":"clearImmediate","ranges":[{"startOffset":7219,"endOffset":7685,"count":0}],"isBlockCoverage":false}]},{"scriptId":"36","url":"internal/linkedlist.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1112,"count":1}],"isBlockCoverage":false},{"functionName":"init","ranges":[{"startOffset":15,"endOffset":88,"count":0}],"isBlockCoverage":false},{"functionName":"peek","ranges":[{"startOffset":118,"endOffset":210,"count":0}],"isBlockCoverage":false},{"functionName":"remove","ranges":[{"startOffset":245,"endOffset":472,"count":0}],"isBlockCoverage":false},{"functionName":"append","ranges":[{"startOffset":528,"endOffset":980,"count":0}],"isBlockCoverage":false},{"functionName":"isEmpty","ranges":[{"startOffset":982,"endOffset":1042,"count":0}],"isBlockCoverage":false}]},{"scriptId":"37","url":"internal/timers.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":18495,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":4398,"endOffset":4423,"count":0}],"isBlockCoverage":false},{"functionName":"initAsyncResource","ranges":[{"startOffset":5366,"endOffset":5644,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout","ranges":[{"startOffset":5729,"endOffset":6726,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.","ranges":[{"startOffset":6839,"endOffset":7010,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.refresh","ranges":[{"startOffset":7041,"endOffset":7139,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.unref","ranges":[{"startOffset":7168,"endOffset":7296,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.ref","ranges":[{"startOffset":7323,"endOffset":7451,"count":0}],"isBlockCoverage":false},{"functionName":"Timeout.hasRef","ranges":[{"startOffset":7481,"endOffset":7518,"count":0}],"isBlockCoverage":false},{"functionName":"TimersList","ranges":[{"startOffset":7521,"endOffset":7821,"count":0}],"isBlockCoverage":false},{"functionName":"TimersList.","ranges":[{"startOffset":7937,"endOffset":8108,"count":0}],"isBlockCoverage":false},{"functionName":"ImmediateList","ranges":[{"startOffset":8166,"endOffset":8234,"count":2}],"isBlockCoverage":true},{"functionName":"ImmediateList.append","ranges":[{"startOffset":8413,"endOffset":8580,"count":0}],"isBlockCoverage":false},{"functionName":"ImmediateList.remove","ranges":[{"startOffset":8758,"endOffset":9095,"count":0}],"isBlockCoverage":false},{"functionName":"incRefCount","ranges":[{"startOffset":9098,"endOffset":9174,"count":0}],"isBlockCoverage":false},{"functionName":"decRefCount","ranges":[{"startOffset":9176,"endOffset":9253,"count":0}],"isBlockCoverage":false},{"functionName":"active","ranges":[{"startOffset":9336,"endOffset":9390,"count":0}],"isBlockCoverage":false},{"functionName":"unrefActive","ranges":[{"startOffset":9537,"endOffset":9597,"count":0}],"isBlockCoverage":false},{"functionName":"insertGuarded","ranges":[{"startOffset":9818,"endOffset":10334,"count":0}],"isBlockCoverage":false},{"functionName":"insert","ranges":[{"startOffset":10336,"endOffset":10987,"count":0}],"isBlockCoverage":false},{"functionName":"setUnrefTimeout","ranges":[{"startOffset":10989,"endOffset":11295,"count":0}],"isBlockCoverage":false},{"functionName":"getTimerDuration","ranges":[{"startOffset":11362,"endOffset":11884,"count":0}],"isBlockCoverage":false},{"functionName":"compareTimersLists","ranges":[{"startOffset":11886,"endOffset":12091,"count":0}],"isBlockCoverage":false},{"functionName":"setPosition","ranges":[{"startOffset":12093,"endOffset":12164,"count":0}],"isBlockCoverage":false},{"functionName":"getTimerCallbacks","ranges":[{"startOffset":12166,"endOffset":17263,"count":1}],"isBlockCoverage":true},{"functionName":"processImmediate","ranges":[{"startOffset":12478,"endOffset":14279,"count":0}],"isBlockCoverage":false},{"functionName":"processTimers","ranges":[{"startOffset":14284,"endOffset":14758,"count":0}],"isBlockCoverage":false},{"functionName":"listOnTimeout","ranges":[{"startOffset":14762,"endOffset":17204,"count":0}],"isBlockCoverage":false},{"functionName":"Immediate","ranges":[{"startOffset":17285,"endOffset":17607,"count":0}],"isBlockCoverage":false},{"functionName":"ref","ranges":[{"startOffset":17611,"endOffset":17784,"count":0}],"isBlockCoverage":false},{"functionName":"unref","ranges":[{"startOffset":17788,"endOffset":17964,"count":0}],"isBlockCoverage":false},{"functionName":"hasRef","ranges":[{"startOffset":17968,"endOffset":18009,"count":0}],"isBlockCoverage":false}]},{"scriptId":"38","url":"internal/priority_queue.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2924,"count":1}],"isBlockCoverage":false},{"functionName":"PriorityQueue","ranges":[{"startOffset":570,"endOffset":811,"count":1}],"isBlockCoverage":true},{"functionName":"module.exports","ranges":[{"startOffset":815,"endOffset":855,"count":0}],"isBlockCoverage":false},{"functionName":"insert","ranges":[{"startOffset":859,"endOffset":1044,"count":0}],"isBlockCoverage":false},{"functionName":"peek","ranges":[{"startOffset":1048,"endOffset":1087,"count":0}],"isBlockCoverage":false},{"functionName":"percolateDown","ranges":[{"startOffset":1091,"endOffset":1759,"count":0}],"isBlockCoverage":false},{"functionName":"percolateUp","ranges":[{"startOffset":1763,"endOffset":2254,"count":0}],"isBlockCoverage":false},{"functionName":"removeAt","ranges":[{"startOffset":2258,"endOffset":2591,"count":0}],"isBlockCoverage":false},{"functionName":"remove","ranges":[{"startOffset":2595,"endOffset":2761,"count":0}],"isBlockCoverage":false},{"functionName":"shift","ranges":[{"startOffset":2765,"endOffset":2920,"count":0}],"isBlockCoverage":false}]},{"scriptId":"39","url":"internal/util/debuglog.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2815,"count":1}],"isBlockCoverage":false},{"functionName":"initializeDebugEnv","ranges":[{"startOffset":500,"endOffset":873,"count":1},{"startOffset":591,"endOffset":790,"count":0}],"isBlockCoverage":true},{"functionName":"emitWarningIfNeeded","ranges":[{"startOffset":947,"endOffset":1272,"count":0}],"isBlockCoverage":false},{"functionName":"noop","ranges":[{"startOffset":1274,"endOffset":1292,"count":8}],"isBlockCoverage":true},{"functionName":"debuglogImpl","ranges":[{"startOffset":1294,"endOffset":1859,"count":3},{"startOffset":1369,"endOffset":1831,"count":2},{"startOffset":1388,"endOffset":1784,"count":0}],"isBlockCoverage":true},{"functionName":"debug","ranges":[{"startOffset":1477,"endOffset":1777,"count":0}],"isBlockCoverage":false},{"functionName":"debuglog","ranges":[{"startOffset":2079,"endOffset":2758,"count":11}],"isBlockCoverage":true},{"functionName":"init","ranges":[{"startOffset":2110,"endOffset":2206,"count":3}],"isBlockCoverage":true},{"functionName":"debug","ranges":[{"startOffset":2221,"endOffset":2458,"count":3}],"isBlockCoverage":true},{"functionName":"test","ranges":[{"startOffset":2488,"endOffset":2557,"count":0}],"isBlockCoverage":false},{"functionName":"logger","ranges":[{"startOffset":2576,"endOffset":2603,"count":3}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":2653,"endOffset":2687,"count":0}],"isBlockCoverage":false}]},{"scriptId":"40","url":"internal/process/execution.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":6944,"count":1}],"isBlockCoverage":false},{"functionName":"tryGetCwd","ranges":[{"startOffset":526,"endOffset":856,"count":1},{"startOffset":587,"endOffset":854,"count":0}],"isBlockCoverage":true},{"functionName":"evalModule","ranges":[{"startOffset":858,"endOffset":1298,"count":0}],"isBlockCoverage":false},{"functionName":"evalScript","ranges":[{"startOffset":1300,"endOffset":2682,"count":0}],"isBlockCoverage":false},{"functionName":"setUncaughtExceptionCaptureCallback","ranges":[{"startOffset":2759,"endOffset":3453,"count":0}],"isBlockCoverage":false},{"functionName":"hasUncaughtExceptionCaptureCallback","ranges":[{"startOffset":3455,"endOffset":3556,"count":0}],"isBlockCoverage":false},{"functionName":"noop","ranges":[{"startOffset":3558,"endOffset":3576,"count":0}],"isBlockCoverage":false},{"functionName":"createOnGlobalUncaughtException","ranges":[{"startOffset":4119,"endOffset":6518,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":4388,"endOffset":6515,"count":0}],"isBlockCoverage":false},{"functionName":"readStdin","ranges":[{"startOffset":6520,"endOffset":6725,"count":0}],"isBlockCoverage":false}]},{"scriptId":"41","url":"internal/process/warning.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":4833,"count":1}],"isBlockCoverage":false},{"functionName":"lazyOption","ranges":[{"startOffset":277,"endOffset":831,"count":0}],"isBlockCoverage":false},{"functionName":"writeOut","ranges":[{"startOffset":932,"endOffset":1054,"count":0}],"isBlockCoverage":false},{"functionName":"writeToFile","ranges":[{"startOffset":1056,"endOffset":1440,"count":0}],"isBlockCoverage":false},{"functionName":"doEmitWarning","ranges":[{"startOffset":1442,"endOffset":1513,"count":0}],"isBlockCoverage":false},{"functionName":"onWarning","ranges":[{"startOffset":1552,"endOffset":2730,"count":0}],"isBlockCoverage":false},{"functionName":"emitWarning","ranges":[{"startOffset":2853,"endOffset":3997,"count":0}],"isBlockCoverage":false},{"functionName":"emitWarningSync","ranges":[{"startOffset":3999,"endOffset":4093,"count":0}],"isBlockCoverage":false},{"functionName":"createWarningObject","ranges":[{"startOffset":4095,"endOffset":4762,"count":0}],"isBlockCoverage":false}]},{"scriptId":"42","url":"internal/bootstrap/switches/is_main_thread.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":6397,"count":1}],"isBlockCoverage":true},{"functionName":"process._startProfilerIdleNotifier","ranges":[{"startOffset":513,"endOffset":521,"count":0}],"isBlockCoverage":false},{"functionName":"process._stopProfilerIdleNotifier","ranges":[{"startOffset":559,"endOffset":567,"count":0}],"isBlockCoverage":false},{"functionName":"defineStream","ranges":[{"startOffset":570,"endOffset":717,"count":3}],"isBlockCoverage":true},{"functionName":"createWritableStdioStream","ranges":[{"startOffset":1278,"endOffset":2845,"count":1},{"startOffset":1430,"endOffset":1556,"count":0},{"startOffset":1562,"endOffset":1748,"count":0},{"startOffset":2053,"endOffset":2081,"count":0},{"startOffset":2083,"endOffset":2303,"count":0},{"startOffset":2479,"endOffset":2724,"count":0}],"isBlockCoverage":true},{"functionName":"write","ranges":[{"startOffset":2667,"endOffset":2714,"count":0}],"isBlockCoverage":false},{"functionName":"dummyDestroy","ranges":[{"startOffset":2847,"endOffset":3230,"count":0}],"isBlockCoverage":false},{"functionName":"getStdout","ranges":[{"startOffset":3268,"endOffset":3599,"count":0}],"isBlockCoverage":false},{"functionName":"getStderr","ranges":[{"startOffset":3601,"endOffset":3932,"count":1},{"startOffset":3638,"endOffset":3652,"count":0},{"startOffset":3851,"endOffset":3913,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":3880,"endOffset":3907,"count":0}],"isBlockCoverage":false},{"functionName":"getStdin","ranges":[{"startOffset":3934,"endOffset":6253,"count":0}],"isBlockCoverage":false},{"functionName":"rawMethods.resetStdioForTesting","ranges":[{"startOffset":6316,"endOffset":6395,"count":0}],"isBlockCoverage":false}]},{"scriptId":"43","url":"internal/process/signal.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1124,"count":1}],"isBlockCoverage":false},{"functionName":"isSignal","ranges":[{"startOffset":205,"endOffset":301,"count":3}],"isBlockCoverage":true},{"functionName":"startListeningIfSignal","ranges":[{"startOffset":365,"endOffset":853,"count":3},{"startOffset":426,"endOffset":451,"count":0},{"startOffset":453,"endOffset":851,"count":0}],"isBlockCoverage":true},{"functionName":"stopListeningIfSignal","ranges":[{"startOffset":855,"endOffset":1050,"count":1},{"startOffset":957,"endOffset":993,"count":0},{"startOffset":995,"endOffset":1048,"count":0}],"isBlockCoverage":true}]},{"scriptId":"44","url":"internal/bootstrap/switches/does_own_process_state.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":3523,"count":1}],"isBlockCoverage":true},{"functionName":"wrapPosixCredentialSetters","ranges":[{"startOffset":817,"endOffset":2957,"count":1}],"isBlockCoverage":true},{"functionName":"initgroups","ranges":[{"startOffset":1278,"endOffset":1695,"count":0}],"isBlockCoverage":false},{"functionName":"setgroups","ranges":[{"startOffset":1699,"endOffset":2179,"count":0}],"isBlockCoverage":false},{"functionName":"wrapIdSetter","ranges":[{"startOffset":2183,"endOffset":2508,"count":4}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":2232,"endOffset":2503,"count":0}],"isBlockCoverage":false},{"functionName":"validateId","ranges":[{"startOffset":2512,"endOffset":2730,"count":0}],"isBlockCoverage":false},{"functionName":"wrappedChdir","ranges":[{"startOffset":3108,"endOffset":3279,"count":0}],"isBlockCoverage":false},{"functionName":"wrappedUmask","ranges":[{"startOffset":3281,"endOffset":3417,"count":0}],"isBlockCoverage":false},{"functionName":"wrappedCwd","ranges":[{"startOffset":3419,"endOffset":3522,"count":3},{"startOffset":3471,"endOffset":3500,"count":1}],"isBlockCoverage":true}]},{"scriptId":"45","url":"internal/main/run_main_module.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":632,"count":1}],"isBlockCoverage":true}]},{"scriptId":"46","url":"internal/bootstrap/pre_execution.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":15185,"count":1}],"isBlockCoverage":true},{"functionName":"prepareMainThreadExecution","ranges":[{"startOffset":410,"endOffset":2164,"count":1}],"isBlockCoverage":true},{"functionName":"patchProcessObject","ranges":[{"startOffset":2166,"endOffset":3743,"count":1},{"startOffset":2762,"endOffset":2770,"count":0}],"isBlockCoverage":true},{"functionName":"addReadOnlyProcessAlias","ranges":[{"startOffset":3745,"endOffset":4002,"count":13},{"startOffset":3866,"endOffset":4000,"count":1}],"isBlockCoverage":true},{"functionName":"setupWarningHandler","ranges":[{"startOffset":4004,"endOffset":4233,"count":1}],"isBlockCoverage":true},{"functionName":"setupCoverageHooks","ranges":[{"startOffset":4345,"endOffset":5022,"count":1},{"startOffset":4815,"endOffset":4992,"count":0}],"isBlockCoverage":true},{"functionName":"setupStacktracePrinterOnSigint","ranges":[{"startOffset":5024,"endOffset":5249,"count":1},{"startOffset":5126,"endOffset":5248,"count":0}],"isBlockCoverage":true},{"functionName":"initializeReport","ranges":[{"startOffset":5251,"endOffset":5475,"count":1}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":5433,"endOffset":5467,"count":0}],"isBlockCoverage":false},{"functionName":"setupDebugEnv","ranges":[{"startOffset":5477,"endOffset":5709,"count":1},{"startOffset":5628,"endOffset":5707,"count":0}],"isBlockCoverage":true},{"functionName":"initializeReportSignalHandlers","ranges":[{"startOffset":5771,"endOffset":5906,"count":1}],"isBlockCoverage":true},{"functionName":"initializeHeapSnapshotSignalHandlers","ranges":[{"startOffset":5908,"endOffset":6215,"count":1},{"startOffset":6043,"endOffset":6214,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":6175,"endOffset":6211,"count":0}],"isBlockCoverage":false},{"functionName":"setupTraceCategoryState","ranges":[{"startOffset":6217,"endOffset":6476,"count":1}],"isBlockCoverage":true},{"functionName":"setupInspectorHooks","ranges":[{"startOffset":6478,"endOffset":7059,"count":1}],"isBlockCoverage":true},{"functionName":"initializeDeprecations","ranges":[{"startOffset":7254,"endOffset":9775,"count":1},{"startOffset":7965,"endOffset":8267,"count":16},{"startOffset":8010,"endOffset":8242,"count":0},{"startOffset":8584,"endOffset":8757,"count":0},{"startOffset":8785,"endOffset":9179,"count":0}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":9428,"endOffset":9464,"count":6}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":9470,"endOffset":9512,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":9638,"endOffset":9673,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":9679,"endOffset":9720,"count":0}],"isBlockCoverage":false},{"functionName":"initializeAbortController","ranges":[{"startOffset":9777,"endOffset":10345,"count":1},{"startOffset":9915,"endOffset":10343,"count":0}],"isBlockCoverage":true},{"functionName":"setupChildProcessIpcChannel","ranges":[{"startOffset":10347,"endOffset":10912,"count":1},{"startOffset":10423,"endOffset":10910,"count":0}],"isBlockCoverage":true},{"functionName":"initializeClusterIPC","ranges":[{"startOffset":10914,"endOffset":11184,"count":1},{"startOffset":11001,"endOffset":11182,"count":0}],"isBlockCoverage":true},{"functionName":"initializePolicy","ranges":[{"startOffset":11186,"endOffset":13010,"count":1},{"startOffset":11312,"endOffset":13008,"count":0}],"isBlockCoverage":true},{"functionName":"initializeWASI","ranges":[{"startOffset":13012,"endOffset":13241,"count":1}],"isBlockCoverage":true},{"functionName":"initializeCJSLoader","ranges":[{"startOffset":13243,"endOffset":13529,"count":1}],"isBlockCoverage":true},{"functionName":"initializeESMLoader","ranges":[{"startOffset":13531,"endOffset":14199,"count":1},{"startOffset":13740,"endOffset":13747,"count":0}],"isBlockCoverage":true},{"functionName":"initializeFrozenIntrinsics","ranges":[{"startOffset":14201,"endOffset":14458,"count":1},{"startOffset":14286,"endOffset":14456,"count":0}],"isBlockCoverage":true},{"functionName":"loadPreloadModules","ranges":[{"startOffset":14460,"endOffset":14807,"count":1},{"startOffset":14654,"endOffset":14805,"count":0}],"isBlockCoverage":true}]},{"scriptId":"47","url":"internal/options.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":898,"count":1}],"isBlockCoverage":false},{"functionName":"getOptionValue","ranges":[{"startOffset":175,"endOffset":314,"count":48},{"startOffset":262,"endOffset":289,"count":0}],"isBlockCoverage":true},{"functionName":"getAllowUnauthorized","ranges":[{"startOffset":316,"endOffset":781,"count":0}],"isBlockCoverage":false}]},{"scriptId":"48","url":"internal/inspector_async_hook.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1972,"count":1}],"isBlockCoverage":false},{"functionName":"lazyHookCreation","ranges":[{"startOffset":75,"endOffset":1257,"count":0}],"isBlockCoverage":false},{"functionName":"enable","ranges":[{"startOffset":1259,"endOffset":1840,"count":0}],"isBlockCoverage":false},{"functionName":"disable","ranges":[{"startOffset":1842,"endOffset":1928,"count":0}],"isBlockCoverage":false}]},{"scriptId":"49","url":"internal/source_map/source_map_cache.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":8419,"count":1}],"isBlockCoverage":false},{"functionName":"ObjectGetValueSafe","ranges":[{"startOffset":305,"endOffset":483,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":665,"endOffset":690,"count":0}],"isBlockCoverage":false},{"functionName":"getSourceMapsEnabled","ranges":[{"startOffset":1236,"endOffset":1740,"count":2},{"startOffset":1309,"endOffset":1710,"count":1},{"startOffset":1402,"endOffset":1706,"count":0}],"isBlockCoverage":true},{"functionName":"maybeCacheSourceMap","ranges":[{"startOffset":1742,"endOffset":2852,"count":2},{"startOffset":1900,"endOffset":1920,"count":0},{"startOffset":1923,"endOffset":1930,"count":0},{"startOffset":1990,"endOffset":2128,"count":0},{"startOffset":2232,"endOffset":2850,"count":0}],"isBlockCoverage":true},{"functionName":"dataFromUrl","ranges":[{"startOffset":2854,"endOffset":3380,"count":0}],"isBlockCoverage":false},{"functionName":"lineLengths","ranges":[{"startOffset":3570,"endOffset":3878,"count":0}],"isBlockCoverage":false},{"functionName":"sourceMapFromFile","ranges":[{"startOffset":3880,"endOffset":4136,"count":0}],"isBlockCoverage":false},{"functionName":"sourceMapFromDataUrl","ranges":[{"startOffset":4230,"endOffset":4867,"count":0}],"isBlockCoverage":false},{"functionName":"sourcesToAbsolute","ranges":[{"startOffset":5052,"endOffset":5379,"count":0}],"isBlockCoverage":false},{"functionName":"rekeySourceMap","ranges":[{"startOffset":5448,"endOffset":5643,"count":0}],"isBlockCoverage":false},{"functionName":"sourceMapCacheToObject","ranges":[{"startOffset":6081,"endOffset":6450,"count":0}],"isBlockCoverage":false},{"functionName":"appendCJSCache","ranges":[{"startOffset":6689,"endOffset":7344,"count":0}],"isBlockCoverage":false},{"functionName":"findSourceMap","ranges":[{"startOffset":7562,"endOffset":8287,"count":0}],"isBlockCoverage":false}]},{"scriptId":"50","url":"fs.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":59762,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":3694,"endOffset":3854,"count":0}],"isBlockCoverage":false},{"functionName":"showTruncateDeprecation","ranges":[{"startOffset":4089,"endOffset":4376,"count":0}],"isBlockCoverage":false},{"functionName":"maybeCallback","ranges":[{"startOffset":4378,"endOffset":4494,"count":0}],"isBlockCoverage":false},{"functionName":"makeCallback","ranges":[{"startOffset":4697,"endOffset":4840,"count":0}],"isBlockCoverage":false},{"functionName":"makeStatsCallback","ranges":[{"startOffset":5021,"endOffset":5236,"count":0}],"isBlockCoverage":false},{"functionName":"isFileType","ranges":[{"startOffset":5262,"endOffset":5522,"count":23},{"startOffset":5461,"endOffset":5481,"count":13}],"isBlockCoverage":true},{"functionName":"access","ranges":[{"startOffset":5524,"endOffset":5882,"count":0}],"isBlockCoverage":false},{"functionName":"accessSync","ranges":[{"startOffset":5884,"endOffset":6122,"count":0}],"isBlockCoverage":false},{"functionName":"exists","ranges":[{"startOffset":6124,"endOffset":6362,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":6435,"endOffset":6513,"count":0}],"isBlockCoverage":false},{"functionName":"existsSync","ranges":[{"startOffset":6930,"endOffset":7460,"count":0}],"isBlockCoverage":false},{"functionName":"readFileAfterOpen","ranges":[{"startOffset":7462,"endOffset":7742,"count":0}],"isBlockCoverage":false},{"functionName":"readFileAfterStat","ranges":[{"startOffset":7744,"endOffset":8245,"count":0}],"isBlockCoverage":false},{"functionName":"readFile","ranges":[{"startOffset":8247,"endOffset":9263,"count":0}],"isBlockCoverage":false},{"functionName":"tryStatSync","ranges":[{"startOffset":9265,"endOffset":9495,"count":0}],"isBlockCoverage":false},{"functionName":"tryCreateBuffer","ranges":[{"startOffset":9497,"endOffset":9808,"count":0}],"isBlockCoverage":false},{"functionName":"tryReadSync","ranges":[{"startOffset":9810,"endOffset":10065,"count":0}],"isBlockCoverage":false},{"functionName":"readFileSync","ranges":[{"startOffset":10067,"endOffset":11450,"count":0}],"isBlockCoverage":false},{"functionName":"defaultCloseCallback","ranges":[{"startOffset":11452,"endOffset":11520,"count":0}],"isBlockCoverage":false},{"functionName":"close","ranges":[{"startOffset":11522,"endOffset":11778,"count":0}],"isBlockCoverage":false},{"functionName":"closeSync","ranges":[{"startOffset":11780,"endOffset":11923,"count":0}],"isBlockCoverage":false},{"functionName":"open","ranges":[{"startOffset":11925,"endOffset":12502,"count":0}],"isBlockCoverage":false},{"functionName":"openSync","ranges":[{"startOffset":12505,"endOffset":12901,"count":0}],"isBlockCoverage":false},{"functionName":"read","ranges":[{"startOffset":13008,"endOffset":14506,"count":0}],"isBlockCoverage":false},{"functionName":"readSync","ranges":[{"startOffset":14775,"endOffset":15690,"count":0}],"isBlockCoverage":false},{"functionName":"readv","ranges":[{"startOffset":15692,"endOffset":16122,"count":0}],"isBlockCoverage":false},{"functionName":"readvSync","ranges":[{"startOffset":16265,"endOffset":16575,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":16721,"endOffset":17989,"count":0}],"isBlockCoverage":false},{"functionName":"writeSync","ranges":[{"startOffset":18266,"endOffset":19132,"count":0}],"isBlockCoverage":false},{"functionName":"writev","ranges":[{"startOffset":19193,"endOffset":19621,"count":0}],"isBlockCoverage":false},{"functionName":"writevSync","ranges":[{"startOffset":19750,"endOffset":20063,"count":0}],"isBlockCoverage":false},{"functionName":"rename","ranges":[{"startOffset":20065,"endOffset":20446,"count":0}],"isBlockCoverage":false},{"functionName":"renameSync","ranges":[{"startOffset":20448,"endOffset":20795,"count":0}],"isBlockCoverage":false},{"functionName":"truncate","ranges":[{"startOffset":20797,"endOffset":21415,"count":0}],"isBlockCoverage":false},{"functionName":"truncateSync","ranges":[{"startOffset":21417,"endOffset":21820,"count":0}],"isBlockCoverage":false},{"functionName":"ftruncate","ranges":[{"startOffset":21822,"endOffset":22162,"count":0}],"isBlockCoverage":false},{"functionName":"ftruncateSync","ranges":[{"startOffset":22164,"endOffset":22384,"count":0}],"isBlockCoverage":false},{"functionName":"lazyLoadRimraf","ranges":[{"startOffset":22387,"endOffset":22506,"count":0}],"isBlockCoverage":false},{"functionName":"rmdir","ranges":[{"startOffset":22508,"endOffset":23148,"count":0}],"isBlockCoverage":false},{"functionName":"rmdirSync","ranges":[{"startOffset":23150,"endOffset":23588,"count":0}],"isBlockCoverage":false},{"functionName":"rm","ranges":[{"startOffset":23590,"endOffset":23928,"count":0}],"isBlockCoverage":false},{"functionName":"rmSync","ranges":[{"startOffset":23930,"endOffset":24100,"count":0}],"isBlockCoverage":false},{"functionName":"fdatasync","ranges":[{"startOffset":24102,"endOffset":24276,"count":0}],"isBlockCoverage":false},{"functionName":"fdatasyncSync","ranges":[{"startOffset":24278,"endOffset":24428,"count":0}],"isBlockCoverage":false},{"functionName":"fsync","ranges":[{"startOffset":24430,"endOffset":24596,"count":0}],"isBlockCoverage":false},{"functionName":"fsyncSync","ranges":[{"startOffset":24598,"endOffset":24740,"count":0}],"isBlockCoverage":false},{"functionName":"mkdir","ranges":[{"startOffset":24742,"endOffset":25523,"count":0}],"isBlockCoverage":false},{"functionName":"mkdirSync","ranges":[{"startOffset":25525,"endOffset":26297,"count":0}],"isBlockCoverage":false},{"functionName":"readdir","ranges":[{"startOffset":26299,"endOffset":26880,"count":0}],"isBlockCoverage":false},{"functionName":"readdirSync","ranges":[{"startOffset":26882,"endOffset":27308,"count":0}],"isBlockCoverage":false},{"functionName":"fstat","ranges":[{"startOffset":27310,"endOffset":27649,"count":0}],"isBlockCoverage":false},{"functionName":"lstat","ranges":[{"startOffset":27651,"endOffset":28026,"count":0}],"isBlockCoverage":false},{"functionName":"stat","ranges":[{"startOffset":28028,"endOffset":28401,"count":0}],"isBlockCoverage":false},{"functionName":"hasNoEntryError","ranges":[{"startOffset":28403,"endOffset":28625,"count":0}],"isBlockCoverage":false},{"functionName":"fstatSync","ranges":[{"startOffset":28627,"endOffset":28891,"count":0}],"isBlockCoverage":false},{"functionName":"lstatSync","ranges":[{"startOffset":28893,"endOffset":29317,"count":0}],"isBlockCoverage":false},{"functionName":"statSync","ranges":[{"startOffset":29319,"endOffset":29740,"count":2},{"startOffset":29618,"endOffset":29641,"count":0},{"startOffset":29643,"endOffset":29670,"count":0}],"isBlockCoverage":true},{"functionName":"readlink","ranges":[{"startOffset":29742,"endOffset":30090,"count":0}],"isBlockCoverage":false},{"functionName":"readlinkSync","ranges":[{"startOffset":30092,"endOffset":30423,"count":0}],"isBlockCoverage":false},{"functionName":"symlink","ranges":[{"startOffset":30425,"endOffset":32035,"count":0}],"isBlockCoverage":false},{"functionName":"symlinkSync","ranges":[{"startOffset":32037,"endOffset":32694,"count":0}],"isBlockCoverage":false},{"functionName":"link","ranges":[{"startOffset":32696,"endOffset":33097,"count":0}],"isBlockCoverage":false},{"functionName":"linkSync","ranges":[{"startOffset":33099,"endOffset":33548,"count":0}],"isBlockCoverage":false},{"functionName":"unlink","ranges":[{"startOffset":33550,"endOffset":33777,"count":0}],"isBlockCoverage":false},{"functionName":"unlinkSync","ranges":[{"startOffset":33779,"endOffset":33965,"count":0}],"isBlockCoverage":false},{"functionName":"fchmod","ranges":[{"startOffset":33967,"endOffset":34209,"count":0}],"isBlockCoverage":false},{"functionName":"fchmodSync","ranges":[{"startOffset":34211,"endOffset":34405,"count":0}],"isBlockCoverage":false},{"functionName":"lchmod","ranges":[{"startOffset":34407,"endOffset":34844,"count":0}],"isBlockCoverage":false},{"functionName":"lchmodSync","ranges":[{"startOffset":34846,"endOffset":35168,"count":0}],"isBlockCoverage":false},{"functionName":"chmod","ranges":[{"startOffset":35171,"endOffset":35447,"count":0}],"isBlockCoverage":false},{"functionName":"chmodSync","ranges":[{"startOffset":35449,"endOffset":35684,"count":0}],"isBlockCoverage":false},{"functionName":"lchown","ranges":[{"startOffset":35686,"endOffset":36027,"count":0}],"isBlockCoverage":false},{"functionName":"lchownSync","ranges":[{"startOffset":36029,"endOffset":36329,"count":0}],"isBlockCoverage":false},{"functionName":"fchown","ranges":[{"startOffset":36331,"endOffset":36637,"count":0}],"isBlockCoverage":false},{"functionName":"fchownSync","ranges":[{"startOffset":36639,"endOffset":36898,"count":0}],"isBlockCoverage":false},{"functionName":"chown","ranges":[{"startOffset":36900,"endOffset":37240,"count":0}],"isBlockCoverage":false},{"functionName":"chownSync","ranges":[{"startOffset":37242,"endOffset":37540,"count":0}],"isBlockCoverage":false},{"functionName":"utimes","ranges":[{"startOffset":37542,"endOffset":37883,"count":0}],"isBlockCoverage":false},{"functionName":"utimesSync","ranges":[{"startOffset":37885,"endOffset":38167,"count":0}],"isBlockCoverage":false},{"functionName":"futimes","ranges":[{"startOffset":38169,"endOffset":38477,"count":0}],"isBlockCoverage":false},{"functionName":"futimesSync","ranges":[{"startOffset":38479,"endOffset":38739,"count":0}],"isBlockCoverage":false},{"functionName":"lutimes","ranges":[{"startOffset":38741,"endOffset":39087,"count":0}],"isBlockCoverage":false},{"functionName":"lutimesSync","ranges":[{"startOffset":39089,"endOffset":39393,"count":0}],"isBlockCoverage":false},{"functionName":"writeAll","ranges":[{"startOffset":39395,"endOffset":40339,"count":0}],"isBlockCoverage":false},{"functionName":"writeFile","ranges":[{"startOffset":40341,"endOffset":41280,"count":0}],"isBlockCoverage":false},{"functionName":"writeFileSync","ranges":[{"startOffset":41282,"endOffset":41992,"count":0}],"isBlockCoverage":false},{"functionName":"appendFile","ranges":[{"startOffset":41994,"endOffset":42434,"count":0}],"isBlockCoverage":false},{"functionName":"appendFileSync","ranges":[{"startOffset":42436,"endOffset":42815,"count":0}],"isBlockCoverage":false},{"functionName":"watch","ranges":[{"startOffset":42817,"endOffset":44050,"count":0}],"isBlockCoverage":false},{"functionName":"watchFile","ranges":[{"startOffset":44086,"endOffset":45199,"count":0}],"isBlockCoverage":false},{"functionName":"unwatchFile","ranges":[{"startOffset":45201,"endOffset":45889,"count":0}],"isBlockCoverage":false},{"functionName":"splitRoot","ranges":[{"startOffset":46105,"endOffset":46171,"count":0}],"isBlockCoverage":false},{"functionName":"splitRoot","ranges":[{"startOffset":46196,"endOffset":46374,"count":3},{"startOffset":46263,"endOffset":46354,"count":6},{"startOffset":46325,"endOffset":46348,"count":3},{"startOffset":46354,"endOffset":46373,"count":0}],"isBlockCoverage":true},{"functionName":"encodeRealpathResult","ranges":[{"startOffset":46379,"endOffset":46666,"count":3},{"startOffset":46464,"endOffset":46494,"count":0},{"startOffset":46514,"endOffset":46665,"count":0}],"isBlockCoverage":true},{"functionName":"nextPart","ranges":[{"startOffset":46789,"endOffset":47032,"count":0}],"isBlockCoverage":false},{"functionName":"nextPart","ranges":[{"startOffset":47056,"endOffset":47109,"count":18}],"isBlockCoverage":true},{"functionName":"realpathSync","ranges":[{"startOffset":47151,"endOffset":51017,"count":4},{"startOffset":47285,"endOffset":47303,"count":0},{"startOffset":47472,"endOffset":47507,"count":1},{"startOffset":47507,"endOffset":48079,"count":3},{"startOffset":48079,"endOffset":48254,"count":0},{"startOffset":48254,"endOffset":48391,"count":3},{"startOffset":48391,"endOffset":50934,"count":18},{"startOffset":48503,"endOffset":48618,"count":3},{"startOffset":48618,"endOffset":48744,"count":15},{"startOffset":48867,"endOffset":49004,"count":5},{"startOffset":48958,"endOffset":48982,"count":0},{"startOffset":49004,"endOffset":49084,"count":13},{"startOffset":49115,"endOffset":49164,"count":0},{"startOffset":49164,"endOffset":50448,"count":13},{"startOffset":49617,"endOffset":50442,"count":0},{"startOffset":50448,"endOffset":50743,"count":0},{"startOffset":50745,"endOffset":50930,"count":0},{"startOffset":50934,"endOffset":51016,"count":3}],"isBlockCoverage":true},{"functionName":"realpathSync.native","ranges":[{"startOffset":51042,"endOffset":51281,"count":0}],"isBlockCoverage":false},{"functionName":"realpath","ranges":[{"startOffset":51285,"endOffset":54720,"count":0}],"isBlockCoverage":false},{"functionName":"realpath.native","ranges":[{"startOffset":54741,"endOffset":55011,"count":0}],"isBlockCoverage":false},{"functionName":"mkdtemp","ranges":[{"startOffset":55014,"endOffset":55485,"count":0}],"isBlockCoverage":false},{"functionName":"mkdtempSync","ranges":[{"startOffset":55488,"endOffset":55962,"count":0}],"isBlockCoverage":false},{"functionName":"copyFile","ranges":[{"startOffset":55965,"endOffset":56519,"count":0}],"isBlockCoverage":false},{"functionName":"copyFileSync","ranges":[{"startOffset":56522,"endOffset":56894,"count":0}],"isBlockCoverage":false},{"functionName":"lazyLoadStreams","ranges":[{"startOffset":56896,"endOffset":57090,"count":0}],"isBlockCoverage":false},{"functionName":"createReadStream","ranges":[{"startOffset":57092,"endOffset":57197,"count":0}],"isBlockCoverage":false},{"functionName":"createWriteStream","ranges":[{"startOffset":57199,"endOffset":57306,"count":0}],"isBlockCoverage":false},{"functionName":"get ReadStream","ranges":[{"startOffset":58529,"endOffset":58597,"count":0}],"isBlockCoverage":false},{"functionName":"set ReadStream","ranges":[{"startOffset":58602,"endOffset":58649,"count":0}],"isBlockCoverage":false},{"functionName":"get WriteStream","ranges":[{"startOffset":58654,"endOffset":58724,"count":0}],"isBlockCoverage":false},{"functionName":"set WriteStream","ranges":[{"startOffset":58729,"endOffset":58778,"count":0}],"isBlockCoverage":false},{"functionName":"get FileReadStream","ranges":[{"startOffset":58916,"endOffset":58992,"count":0}],"isBlockCoverage":false},{"functionName":"set FileReadStream","ranges":[{"startOffset":58997,"endOffset":59052,"count":0}],"isBlockCoverage":false},{"functionName":"get FileWriteStream","ranges":[{"startOffset":59057,"endOffset":59135,"count":0}],"isBlockCoverage":false},{"functionName":"set FileWriteStream","ranges":[{"startOffset":59140,"endOffset":59197,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":59628,"endOffset":59753,"count":0}],"isBlockCoverage":false}]},{"scriptId":"51","url":"internal/fs/utils.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":21731,"count":1}],"isBlockCoverage":false},{"functionName":"lazyLoadFs","ranges":[{"startOffset":2496,"endOffset":2575,"count":0}],"isBlockCoverage":false},{"functionName":"assertEncoding","ranges":[{"startOffset":2577,"endOffset":2724,"count":16},{"startOffset":2628,"endOffset":2659,"count":12},{"startOffset":2661,"endOffset":2722,"count":0}],"isBlockCoverage":true},{"functionName":"Dirent","ranges":[{"startOffset":2743,"endOffset":2818,"count":0}],"isBlockCoverage":false},{"functionName":"isDirectory","ranges":[{"startOffset":2822,"endOffset":2883,"count":0}],"isBlockCoverage":false},{"functionName":"isFile","ranges":[{"startOffset":2887,"endOffset":2944,"count":0}],"isBlockCoverage":false},{"functionName":"isBlockDevice","ranges":[{"startOffset":2948,"endOffset":3013,"count":0}],"isBlockCoverage":false},{"functionName":"isCharacterDevice","ranges":[{"startOffset":3017,"endOffset":3085,"count":0}],"isBlockCoverage":false},{"functionName":"isSymbolicLink","ranges":[{"startOffset":3089,"endOffset":3154,"count":0}],"isBlockCoverage":false},{"functionName":"isFIFO","ranges":[{"startOffset":3158,"endOffset":3215,"count":0}],"isBlockCoverage":false},{"functionName":"isSocket","ranges":[{"startOffset":3219,"endOffset":3280,"count":0}],"isBlockCoverage":false},{"functionName":"DirentFromStats","ranges":[{"startOffset":3325,"endOffset":3404,"count":0}],"isBlockCoverage":false},{"functionName":"DirentFromStats.","ranges":[{"startOffset":3549,"endOffset":3598,"count":0}],"isBlockCoverage":false},{"functionName":"copyObject","ranges":[{"startOffset":3603,"endOffset":3731,"count":0}],"isBlockCoverage":false},{"functionName":"join","ranges":[{"startOffset":3781,"endOffset":4388,"count":0}],"isBlockCoverage":false},{"functionName":"getDirents","ranges":[{"startOffset":4390,"endOffset":5485,"count":0}],"isBlockCoverage":false},{"functionName":"getDirent","ranges":[{"startOffset":5487,"endOffset":6209,"count":0}],"isBlockCoverage":false},{"functionName":"getOptions","ranges":[{"startOffset":6211,"endOffset":6853,"count":19},{"startOffset":6306,"endOffset":6344,"count":16},{"startOffset":6346,"endOffset":6378,"count":3},{"startOffset":6378,"endOffset":6415,"count":16},{"startOffset":6415,"endOffset":6533,"count":12},{"startOffset":6533,"endOffset":6655,"count":4},{"startOffset":6572,"endOffset":6655,"count":0},{"startOffset":6655,"endOffset":6769,"count":16},{"startOffset":6769,"endOffset":6833,"count":0},{"startOffset":6833,"endOffset":6852,"count":16}],"isBlockCoverage":true},{"functionName":"handleErrorFromBinding","ranges":[{"startOffset":6855,"endOffset":7384,"count":15},{"startOffset":6925,"endOffset":7060,"count":0},{"startOffset":7092,"endOffset":7382,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":7525,"endOffset":8078,"count":21},{"startOffset":7755,"endOffset":7775,"count":0},{"startOffset":7829,"endOffset":7877,"count":0},{"startOffset":7896,"endOffset":8077,"count":0}],"isBlockCoverage":true},{"functionName":"preprocessSymlinkDestination","ranges":[{"startOffset":8082,"endOffset":8766,"count":0}],"isBlockCoverage":false},{"functionName":"StatsBase","ranges":[{"startOffset":8799,"endOffset":9106,"count":2}],"isBlockCoverage":true},{"functionName":"StatsBase.isDirectory","ranges":[{"startOffset":9142,"endOffset":9199,"count":2}],"isBlockCoverage":true},{"functionName":"StatsBase.isFile","ranges":[{"startOffset":9231,"endOffset":9288,"count":2}],"isBlockCoverage":true},{"functionName":"StatsBase.isBlockDevice","ranges":[{"startOffset":9327,"endOffset":9384,"count":0}],"isBlockCoverage":false},{"functionName":"StatsBase.isCharacterDevice","ranges":[{"startOffset":9427,"endOffset":9484,"count":0}],"isBlockCoverage":false},{"functionName":"StatsBase.isSymbolicLink","ranges":[{"startOffset":9524,"endOffset":9581,"count":0}],"isBlockCoverage":false},{"functionName":"StatsBase.isFIFO","ranges":[{"startOffset":9613,"endOffset":9670,"count":0}],"isBlockCoverage":false},{"functionName":"StatsBase.isSocket","ranges":[{"startOffset":9704,"endOffset":9762,"count":0}],"isBlockCoverage":false},{"functionName":"msFromTimeSpec","ranges":[{"startOffset":9887,"endOffset":9969,"count":8}],"isBlockCoverage":true},{"functionName":"nsFromTimeSpecBigInt","ranges":[{"startOffset":9971,"endOffset":10054,"count":0}],"isBlockCoverage":false},{"functionName":"dateFromMs","ranges":[{"startOffset":10403,"endOffset":10467,"count":8}],"isBlockCoverage":true},{"functionName":"BigIntStats","ranges":[{"startOffset":10469,"endOffset":11205,"count":0}],"isBlockCoverage":false},{"functionName":"BigIntStats._checkModeProperty","ranges":[{"startOffset":11363,"endOffset":11609,"count":0}],"isBlockCoverage":false},{"functionName":"Stats","ranges":[{"startOffset":11612,"endOffset":12129,"count":2}],"isBlockCoverage":true},{"functionName":"Stats._checkModeProperty","ranges":[{"startOffset":12470,"endOffset":12700,"count":4},{"startOffset":12507,"endOffset":12585,"count":0},{"startOffset":12587,"endOffset":12654,"count":0}],"isBlockCoverage":true},{"functionName":"getStatsFromBinding","ranges":[{"startOffset":12703,"endOffset":13781,"count":2},{"startOffset":12784,"endOffset":13309,"count":0}],"isBlockCoverage":true},{"functionName":"stringToFlags","ranges":[{"startOffset":13783,"endOffset":14991,"count":14},{"startOffset":13848,"endOffset":13871,"count":0},{"startOffset":13894,"endOffset":13920,"count":0},{"startOffset":13977,"endOffset":13988,"count":0},{"startOffset":14010,"endOffset":14047,"count":0},{"startOffset":14052,"endOffset":14078,"count":0},{"startOffset":14083,"endOffset":14095,"count":0},{"startOffset":14117,"endOffset":14153,"count":0},{"startOffset":14159,"endOffset":14206,"count":0},{"startOffset":14211,"endOffset":14222,"count":0},{"startOffset":14244,"endOffset":14301,"count":0},{"startOffset":14307,"endOffset":14353,"count":0},{"startOffset":14358,"endOffset":14369,"count":0},{"startOffset":14391,"endOffset":14446,"count":0},{"startOffset":14452,"endOffset":14500,"count":0},{"startOffset":14505,"endOffset":14516,"count":0},{"startOffset":14538,"endOffset":14596,"count":0},{"startOffset":14601,"endOffset":14612,"count":0},{"startOffset":14634,"endOffset":14692,"count":0},{"startOffset":14698,"endOffset":14745,"count":0},{"startOffset":14750,"endOffset":14761,"count":0},{"startOffset":14783,"endOffset":14839,"count":0},{"startOffset":14844,"endOffset":14855,"count":0},{"startOffset":14877,"endOffset":14933,"count":0},{"startOffset":14937,"endOffset":14990,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":15037,"endOffset":15397,"count":0}],"isBlockCoverage":false},{"functionName":"toUnixTimestamp","ranges":[{"startOffset":15459,"endOffset":15902,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":15956,"endOffset":16335,"count":50},{"startOffset":16012,"endOffset":16079,"count":0},{"startOffset":16100,"endOffset":16167,"count":0},{"startOffset":16208,"endOffset":16331,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":16393,"endOffset":16667,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":16709,"endOffset":16979,"count":21},{"startOffset":16771,"endOffset":16793,"count":0},{"startOffset":16795,"endOffset":16881,"count":0},{"startOffset":16957,"endOffset":16977,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":17024,"endOffset":17159,"count":17}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":17207,"endOffset":17538,"count":0}],"isBlockCoverage":false},{"functionName":"warnOnNonPortableTemplate","ranges":[{"startOffset":17579,"endOffset":18037,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":18273,"endOffset":18893,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":18943,"endOffset":19525,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":19577,"endOffset":20059,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":20100,"endOffset":20678,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":20741,"endOffset":21113,"count":0}],"isBlockCoverage":false}]},{"scriptId":"52","url":"internal/fs/dir.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":6714,"count":1}],"isBlockCoverage":false},{"functionName":"Dir","ranges":[{"startOffset":1109,"endOffset":1881,"count":0}],"isBlockCoverage":false},{"functionName":"get path","ranges":[{"startOffset":1885,"endOffset":1928,"count":0}],"isBlockCoverage":false},{"functionName":"read","ranges":[{"startOffset":1932,"endOffset":1999,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":2003,"endOffset":3375,"count":0}],"isBlockCoverage":false},{"functionName":"readSync","ranges":[{"startOffset":3379,"endOffset":4135,"count":0}],"isBlockCoverage":false},{"functionName":"close","ranges":[{"startOffset":4139,"endOffset":4865,"count":0}],"isBlockCoverage":false},{"functionName":"closeSync","ranges":[{"startOffset":4869,"endOffset":5246,"count":0}],"isBlockCoverage":false},{"functionName":"entries","ranges":[{"startOffset":5250,"endOffset":5513,"count":0}],"isBlockCoverage":false},{"functionName":"opendir","ranges":[{"startOffset":5674,"endOffset":6299,"count":0}],"isBlockCoverage":false},{"functionName":"opendirSync","ranges":[{"startOffset":6301,"endOffset":6658,"count":0}],"isBlockCoverage":false}]},{"scriptId":"53","url":"internal/modules/cjs/helpers.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":5427,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":681,"endOffset":706,"count":1}],"isBlockCoverage":true},{"functionName":"loadNativeModule","ranges":[{"startOffset":860,"endOffset":1066,"count":1}],"isBlockCoverage":true},{"functionName":"makeRequireFunction","ranges":[{"startOffset":1315,"endOffset":3313,"count":0}],"isBlockCoverage":false},{"functionName":"stripBOM","ranges":[{"startOffset":3498,"endOffset":3624,"count":0}],"isBlockCoverage":false},{"functionName":"addBuiltinLibsToObject","ranges":[{"startOffset":3626,"endOffset":5091,"count":0}],"isBlockCoverage":false},{"functionName":"normalizeReferrerURL","ranges":[{"startOffset":5093,"endOffset":5281,"count":2},{"startOffset":5200,"endOffset":5246,"count":0}],"isBlockCoverage":true}]},{"scriptId":"54","url":"url.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":30124,"count":1}],"isBlockCoverage":false},{"functionName":"Url","ranges":[{"startOffset":1879,"endOffset":2155,"count":0}],"isBlockCoverage":false},{"functionName":"urlParse","ranges":[{"startOffset":3833,"endOffset":4047,"count":0}],"isBlockCoverage":false},{"functionName":"isIpv6Hostname","ranges":[{"startOffset":4049,"endOffset":4272,"count":0}],"isBlockCoverage":false},{"functionName":"parse","ranges":[{"startOffset":4296,"endOffset":13617,"count":0}],"isBlockCoverage":false},{"functionName":"getHostname","ranges":[{"startOffset":13620,"endOffset":14345,"count":0}],"isBlockCoverage":false},{"functionName":"autoEscapeStr","ranges":[{"startOffset":15401,"endOffset":16107,"count":0}],"isBlockCoverage":false},{"functionName":"urlFormat","ranges":[{"startOffset":16153,"endOffset":16863,"count":0}],"isBlockCoverage":false},{"functionName":"format","ranges":[{"startOffset":17570,"endOffset":20020,"count":0}],"isBlockCoverage":false},{"functionName":"urlResolve","ranges":[{"startOffset":20023,"endOffset":20122,"count":0}],"isBlockCoverage":false},{"functionName":"resolve","ranges":[{"startOffset":20148,"endOffset":20249,"count":0}],"isBlockCoverage":false},{"functionName":"urlResolveObject","ranges":[{"startOffset":20252,"endOffset":20395,"count":0}],"isBlockCoverage":false},{"functionName":"resolveObject","ranges":[{"startOffset":20427,"endOffset":29546,"count":0}],"isBlockCoverage":false},{"functionName":"parseHost","ranges":[{"startOffset":29575,"endOffset":29848,"count":0}],"isBlockCoverage":false}]},{"scriptId":"55","url":"internal/idna.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":264,"count":1}],"isBlockCoverage":false}]},{"scriptId":"56","url":"internal/process/report.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2945,"count":1}],"isBlockCoverage":false},{"functionName":"writeReport","ranges":[{"startOffset":298,"endOffset":805,"count":0}],"isBlockCoverage":false},{"functionName":"getReport","ranges":[{"startOffset":809,"endOffset":1045,"count":0}],"isBlockCoverage":false},{"functionName":"get directory","ranges":[{"startOffset":1049,"endOffset":1100,"count":0}],"isBlockCoverage":false},{"functionName":"set directory","ranges":[{"startOffset":1104,"endOffset":1192,"count":0}],"isBlockCoverage":false},{"functionName":"get filename","ranges":[{"startOffset":1196,"endOffset":1245,"count":0}],"isBlockCoverage":false},{"functionName":"set filename","ranges":[{"startOffset":1249,"endOffset":1337,"count":0}],"isBlockCoverage":false},{"functionName":"get compact","ranges":[{"startOffset":1341,"endOffset":1388,"count":0}],"isBlockCoverage":false},{"functionName":"set compact","ranges":[{"startOffset":1392,"endOffset":1469,"count":0}],"isBlockCoverage":false},{"functionName":"get signal","ranges":[{"startOffset":1473,"endOffset":1518,"count":0}],"isBlockCoverage":false},{"functionName":"set signal","ranges":[{"startOffset":1522,"endOffset":1659,"count":0}],"isBlockCoverage":false},{"functionName":"get reportOnFatalError","ranges":[{"startOffset":1663,"endOffset":1735,"count":0}],"isBlockCoverage":false},{"functionName":"set reportOnFatalError","ranges":[{"startOffset":1739,"endOffset":1923,"count":0}],"isBlockCoverage":false},{"functionName":"get reportOnSignal","ranges":[{"startOffset":1927,"endOffset":1991,"count":0}],"isBlockCoverage":false},{"functionName":"set reportOnSignal","ranges":[{"startOffset":1995,"endOffset":2222,"count":0}],"isBlockCoverage":false},{"functionName":"get reportOnUncaughtException","ranges":[{"startOffset":2226,"endOffset":2312,"count":0}],"isBlockCoverage":false},{"functionName":"set reportOnUncaughtException","ranges":[{"startOffset":2316,"endOffset":2514,"count":0}],"isBlockCoverage":false},{"functionName":"addSignalHandler","ranges":[{"startOffset":2519,"endOffset":2690,"count":1},{"startOffset":2585,"endOffset":2688,"count":0}],"isBlockCoverage":true},{"functionName":"removeSignalHandler","ranges":[{"startOffset":2692,"endOffset":2816,"count":0}],"isBlockCoverage":false},{"functionName":"signalHandler","ranges":[{"startOffset":2818,"endOffset":2892,"count":0}],"isBlockCoverage":false}]},{"scriptId":"57","url":"internal/modules/cjs/loader.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":38134,"count":1}],"isBlockCoverage":false},{"functionName":"get hasLoadedAnyUserCJSModule","ranges":[{"startOffset":1880,"endOffset":1949,"count":1}],"isBlockCoverage":true},{"functionName":"stat","ranges":[{"startOffset":4257,"endOffset":4574,"count":1},{"startOffset":4355,"endOffset":4449,"count":0},{"startOffset":4523,"endOffset":4555,"count":0}],"isBlockCoverage":true},{"functionName":"updateChildren","ranges":[{"startOffset":4576,"endOffset":4751,"count":0}],"isBlockCoverage":false},{"functionName":"Module","ranges":[{"startOffset":4753,"endOffset":4990,"count":0}],"isBlockCoverage":false},{"functionName":"wrap","ranges":[{"startOffset":5441,"endOffset":5518,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":5663,"endOffset":5785,"count":0}],"isBlockCoverage":false},{"functionName":"defineProperty","ranges":[{"startOffset":5790,"endOffset":5923,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":5970,"endOffset":5998,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":6003,"endOffset":6057,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":6107,"endOffset":6143,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":6148,"endOffset":6210,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":6275,"endOffset":6305,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":6376,"endOffset":6401,"count":0}],"isBlockCoverage":false},{"functionName":"readPackage","ranges":[{"startOffset":6749,"endOffset":7559,"count":1},{"startOffset":6929,"endOffset":6945,"count":0},{"startOffset":7043,"endOffset":7049,"count":0},{"startOffset":7093,"endOffset":7159,"count":0},{"startOffset":7442,"endOffset":7557,"count":0}],"isBlockCoverage":true},{"functionName":"readPackageScope","ranges":[{"startOffset":7561,"endOffset":8104,"count":1},{"startOffset":7903,"endOffset":7916,"count":0},{"startOffset":8041,"endOffset":8103,"count":0}],"isBlockCoverage":true},{"functionName":"tryPackage","ranges":[{"startOffset":8106,"endOffset":9407,"count":0}],"isBlockCoverage":false},{"functionName":"tryFile","ranges":[{"startOffset":9748,"endOffset":9958,"count":0}],"isBlockCoverage":false},{"functionName":"toRealPath","ranges":[{"startOffset":9960,"endOffset":10091,"count":2}],"isBlockCoverage":true},{"functionName":"tryExtensions","ranges":[{"startOffset":10166,"endOffset":10372,"count":0}],"isBlockCoverage":false},{"functionName":"findLongestRegisteredExtension","ranges":[{"startOffset":10461,"endOffset":10897,"count":0}],"isBlockCoverage":false},{"functionName":"trySelfParentPath","ranges":[{"startOffset":10899,"endOffset":11188,"count":0}],"isBlockCoverage":false},{"functionName":"trySelf","ranges":[{"startOffset":11190,"endOffset":12039,"count":0}],"isBlockCoverage":false},{"functionName":"resolveExports","ranges":[{"startOffset":12200,"endOffset":12938,"count":0}],"isBlockCoverage":false},{"functionName":"Module._findPath","ranges":[{"startOffset":13004,"endOffset":15631,"count":1},{"startOffset":13137,"endOffset":13200,"count":0},{"startOffset":13287,"endOffset":13307,"count":0},{"startOffset":13372,"endOffset":13385,"count":0},{"startOffset":13767,"endOffset":13787,"count":0},{"startOffset":13789,"endOffset":13798,"count":0},{"startOffset":13826,"endOffset":13956,"count":0},{"startOffset":14140,"endOffset":14308,"count":0},{"startOffset":14340,"endOffset":14972,"count":0},{"startOffset":15063,"endOffset":15257,"count":0},{"startOffset":15283,"endOffset":15294,"count":0},{"startOffset":15296,"endOffset":15512,"count":0},{"startOffset":15612,"endOffset":15630,"count":0}],"isBlockCoverage":true},{"functionName":"Module._nodeModulePaths","ranges":[{"startOffset":15875,"endOffset":17266,"count":0}],"isBlockCoverage":false},{"functionName":"Module._nodeModulePaths","ranges":[{"startOffset":17358,"endOffset":18399,"count":0}],"isBlockCoverage":false},{"functionName":"Module._resolveLookupPaths","ranges":[{"startOffset":18433,"endOffset":19571,"count":0}],"isBlockCoverage":false},{"functionName":"emitCircularRequireWarning","ranges":[{"startOffset":19574,"endOffset":19757,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":19950,"endOffset":20322,"count":0}],"isBlockCoverage":false},{"functionName":"getOwnPropertyDescriptor","ranges":[{"startOffset":20327,"endOffset":20568,"count":0}],"isBlockCoverage":false},{"functionName":"getExportsForCircularRequire","ranges":[{"startOffset":20769,"endOffset":21419,"count":0}],"isBlockCoverage":false},{"functionName":"Module._load","ranges":[{"startOffset":21831,"endOffset":24853,"count":0}],"isBlockCoverage":false},{"functionName":"Module._resolveFilename","ranges":[{"startOffset":24882,"endOffset":27745,"count":0}],"isBlockCoverage":false},{"functionName":"finalizeEsmResolution","ranges":[{"startOffset":27748,"endOffset":28462,"count":0}],"isBlockCoverage":false},{"functionName":"createEsmNotFoundErr","ranges":[{"startOffset":28464,"endOffset":28754,"count":0}],"isBlockCoverage":false},{"functionName":"Module.load","ranges":[{"startOffset":28843,"endOffset":29647,"count":0}],"isBlockCoverage":false},{"functionName":"Module.require","ranges":[{"startOffset":29765,"endOffset":30064,"count":0}],"isBlockCoverage":false},{"functionName":"wrapSafe","ranges":[{"startOffset":30244,"endOffset":31360,"count":0}],"isBlockCoverage":false},{"functionName":"Module._compile","ranges":[{"startOffset":31560,"endOffset":33402,"count":0}],"isBlockCoverage":false},{"functionName":"Module._extensions..js","ranges":[{"startOffset":33461,"endOffset":34235,"count":0}],"isBlockCoverage":false},{"functionName":"Module._extensions..json","ranges":[{"startOffset":34299,"endOffset":34663,"count":0}],"isBlockCoverage":false},{"functionName":"Module._extensions..node","ranges":[{"startOffset":34727,"endOffset":35045,"count":0}],"isBlockCoverage":false},{"functionName":"createRequireFromPath","ranges":[{"startOffset":35048,"endOffset":35473,"count":0}],"isBlockCoverage":false},{"functionName":"createRequire","ranges":[{"startOffset":35758,"endOffset":36311,"count":0}],"isBlockCoverage":false},{"functionName":"Module._initPaths","ranges":[{"startOffset":36372,"endOffset":37280,"count":1},{"startOffset":36413,"endOffset":36438,"count":0},{"startOffset":36490,"endOffset":36513,"count":0},{"startOffset":36721,"endOffset":36763,"count":0},{"startOffset":37030,"endOffset":37159,"count":0}],"isBlockCoverage":true},{"functionName":"pathsFilterCB","ranges":[{"startOffset":37082,"endOffset":37139,"count":0}],"isBlockCoverage":false},{"functionName":"Module._preloadModules","ranges":[{"startOffset":37308,"endOffset":37890,"count":0}],"isBlockCoverage":false},{"functionName":"syncBuiltinESMExports","ranges":[{"startOffset":37924,"endOffset":38080,"count":0}],"isBlockCoverage":false}]},{"scriptId":"58","url":"vm.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":12941,"count":1}],"isBlockCoverage":false},{"functionName":"Script","ranges":[{"startOffset":1992,"endOffset":4152,"count":0}],"isBlockCoverage":false},{"functionName":"runInThisContext","ranges":[{"startOffset":4156,"endOffset":4436,"count":0}],"isBlockCoverage":false},{"functionName":"runInContext","ranges":[{"startOffset":4440,"endOffset":4846,"count":0}],"isBlockCoverage":false},{"functionName":"runInNewContext","ranges":[{"startOffset":4850,"endOffset":5021,"count":0}],"isBlockCoverage":false},{"functionName":"validateContext","ranges":[{"startOffset":5025,"endOffset":5244,"count":0}],"isBlockCoverage":false},{"functionName":"getRunInContextArgs","ranges":[{"startOffset":5246,"endOffset":5837,"count":0}],"isBlockCoverage":false},{"functionName":"getContextOptions","ranges":[{"startOffset":5839,"endOffset":6907,"count":0}],"isBlockCoverage":false},{"functionName":"isContext","ranges":[{"startOffset":6909,"endOffset":7091,"count":0}],"isBlockCoverage":false},{"functionName":"createContext","ranges":[{"startOffset":7126,"endOffset":8261,"count":0}],"isBlockCoverage":false},{"functionName":"createScript","ranges":[{"startOffset":8263,"endOffset":8339,"count":0}],"isBlockCoverage":false},{"functionName":"sigintHandlersWrap","ranges":[{"startOffset":8493,"endOffset":8939,"count":0}],"isBlockCoverage":false},{"functionName":"runInContext","ranges":[{"startOffset":8941,"endOffset":9338,"count":0}],"isBlockCoverage":false},{"functionName":"runInNewContext","ranges":[{"startOffset":9340,"endOffset":9692,"count":0}],"isBlockCoverage":false},{"functionName":"runInThisContext","ranges":[{"startOffset":9694,"endOffset":9880,"count":0}],"isBlockCoverage":false},{"functionName":"compileFunction","ranges":[{"startOffset":9882,"endOffset":11615,"count":0}],"isBlockCoverage":false},{"functionName":"measureMemory","ranges":[{"startOffset":11892,"endOffset":12454,"count":0}],"isBlockCoverage":false}]},{"scriptId":"59","url":"internal/modules/package_json_reader.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":975,"count":1}],"isBlockCoverage":false},{"functionName":"read","ranges":[{"startOffset":279,"endOffset":946,"count":2},{"startOffset":332,"endOffset":896,"count":1},{"startOffset":686,"endOffset":739,"count":0},{"startOffset":789,"endOffset":892,"count":0},{"startOffset":896,"endOffset":945,"count":1}],"isBlockCoverage":true}]},{"scriptId":"60","url":"internal/process/esm_loader.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2326,"count":1}],"isBlockCoverage":false},{"functionName":"exports.initializeImportMetaObject","ranges":[{"startOffset":405,"endOffset":701,"count":0}],"isBlockCoverage":false},{"functionName":"exports.importModuleDynamicallyCallback","ranges":[{"startOffset":746,"endOffset":1137,"count":1},{"startOffset":1081,"endOffset":1136,"count":0}],"isBlockCoverage":true},{"functionName":"initializeLoader","ranges":[{"startOffset":1202,"endOffset":1969,"count":1},{"startOffset":1388,"endOffset":1968,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":1722,"endOffset":1963,"count":0}],"isBlockCoverage":true},{"functionName":"loadESM","ranges":[{"startOffset":1989,"endOffset":2324,"count":1},{"startOffset":2097,"endOffset":2322,"count":0}],"isBlockCoverage":true}]},{"scriptId":"61","url":"internal/modules/esm/loader.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":8625,"count":1}],"isBlockCoverage":false},{"functionName":"Loader","ranges":[{"startOffset":1416,"endOffset":3189,"count":1}],"isBlockCoverage":true},{"functionName":"resolve","ranges":[{"startOffset":3193,"endOffset":3816,"count":3},{"startOffset":3298,"endOffset":3337,"count":2},{"startOffset":3514,"endOffset":3617,"count":0},{"startOffset":3689,"endOffset":3796,"count":0}],"isBlockCoverage":true},{"functionName":"getFormat","ranges":[{"startOffset":3820,"endOffset":5128,"count":3},{"startOffset":3977,"endOffset":4084,"count":0},{"startOffset":4153,"endOffset":4410,"count":0},{"startOffset":4447,"endOffset":4562,"count":0},{"startOffset":4594,"endOffset":4622,"count":1},{"startOffset":4622,"endOffset":4666,"count":2},{"startOffset":4666,"endOffset":4835,"count":0},{"startOffset":4835,"endOffset":4927,"count":2},{"startOffset":4928,"endOffset":4977,"count":0},{"startOffset":4984,"endOffset":5104,"count":0},{"startOffset":5104,"endOffset":5127,"count":2}],"isBlockCoverage":true},{"functionName":"eval","ranges":[{"startOffset":5132,"endOffset":5807,"count":0}],"isBlockCoverage":false},{"functionName":"import","ranges":[{"startOffset":5811,"endOffset":5982,"count":2}],"isBlockCoverage":true},{"functionName":"hook","ranges":[{"startOffset":5986,"endOffset":6947,"count":0}],"isBlockCoverage":false},{"functionName":"runGlobalPreloadCode","ranges":[{"startOffset":6951,"endOffset":7755,"count":0}],"isBlockCoverage":false},{"functionName":"getModuleJob","ranges":[{"startOffset":7759,"endOffset":8549,"count":3},{"startOffset":8046,"endOffset":8083,"count":0},{"startOffset":8117,"endOffset":8128,"count":0},{"startOffset":8170,"endOffset":8214,"count":0},{"startOffset":8316,"endOffset":8346,"count":1},{"startOffset":8347,"endOffset":8381,"count":1}],"isBlockCoverage":true}]},{"scriptId":"62","url":"internal/modules/esm/module_map.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":878,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":175,"endOffset":200,"count":1}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":419,"endOffset":492,"count":3}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":495,"endOffset":771,"count":3},{"startOffset":585,"endOffset":621,"count":0},{"startOffset":623,"endOffset":693,"count":0}],"isBlockCoverage":true},{"functionName":"has","ranges":[{"startOffset":774,"endOffset":847,"count":0}],"isBlockCoverage":false}]},{"scriptId":"63","url":"internal/modules/esm/module_job.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":5778,"count":1}],"isBlockCoverage":false},{"functionName":"ModuleJob","ranges":[{"startOffset":832,"endOffset":2478,"count":3}],"isBlockCoverage":true},{"functionName":"link","ranges":[{"startOffset":1301,"endOffset":2105,"count":3}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":1757,"endOffset":1984,"count":1}],"isBlockCoverage":true},{"functionName":"instantiate","ranges":[{"startOffset":2482,"endOffset":2627,"count":2}],"isBlockCoverage":true},{"functionName":"_instantiate","ranges":[{"startOffset":2631,"endOffset":5498,"count":2},{"startOffset":3105,"endOffset":3282,"count":0},{"startOffset":3339,"endOffset":5251,"count":0},{"startOffset":5298,"endOffset":5494,"count":3}],"isBlockCoverage":true},{"functionName":"addJobsToDependencyGraph","ranges":[{"startOffset":2730,"endOffset":3004,"count":3},{"startOffset":2791,"endOffset":2816,"count":0}],"isBlockCoverage":true},{"functionName":"run","ranges":[{"startOffset":5502,"endOffset":5698,"count":2}],"isBlockCoverage":true}]},{"scriptId":"64","url":"internal/modules/esm/resolve.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":28247,"count":1}],"isBlockCoverage":false},{"functionName":"getConditionsSet","ranges":[{"startOffset":1873,"endOffset":2225,"count":2},{"startOffset":1982,"endOffset":2190,"count":0}],"isBlockCoverage":true},{"functionName":"tryStatSync","ranges":[{"startOffset":2336,"endOffset":2441,"count":2},{"startOffset":2404,"endOffset":2439,"count":0}],"isBlockCoverage":true},{"functionName":"getPackageConfig","ranges":[{"startOffset":2443,"endOffset":3781,"count":2},{"startOffset":2571,"endOffset":2680,"count":1},{"startOffset":2680,"endOffset":2955,"count":0},{"startOffset":2955,"endOffset":3025,"count":1},{"startOffset":3025,"endOffset":3204,"count":0},{"startOffset":3204,"endOffset":3326,"count":1},{"startOffset":3326,"endOffset":3345,"count":0},{"startOffset":3347,"endOffset":3450,"count":1},{"startOffset":3450,"endOffset":3467,"count":0},{"startOffset":3467,"endOffset":3545,"count":1},{"startOffset":3545,"endOffset":3567,"count":0},{"startOffset":3569,"endOffset":3583,"count":0},{"startOffset":3583,"endOffset":3780,"count":1}],"isBlockCoverage":true},{"functionName":"getPackageScopeConfig","ranges":[{"startOffset":3783,"endOffset":4883,"count":2},{"startOffset":4041,"endOffset":4047,"count":0},{"startOffset":4227,"endOffset":4550,"count":0},{"startOffset":4554,"endOffset":4882,"count":0}],"isBlockCoverage":true},{"functionName":"fileExists","ranges":[{"startOffset":5139,"endOffset":5218,"count":0}],"isBlockCoverage":false},{"functionName":"legacyMainResolve","ranges":[{"startOffset":5220,"endOffset":6891,"count":0}],"isBlockCoverage":false},{"functionName":"resolveExtensionsWithTryExactName","ranges":[{"startOffset":6893,"endOffset":7024,"count":0}],"isBlockCoverage":false},{"functionName":"resolveExtensions","ranges":[{"startOffset":7080,"endOffset":7337,"count":0}],"isBlockCoverage":false},{"functionName":"resolveIndex","ranges":[{"startOffset":7339,"endOffset":7426,"count":0}],"isBlockCoverage":false},{"functionName":"finalizeResolution","ranges":[{"startOffset":7464,"endOffset":8671,"count":2},{"startOffset":7577,"endOffset":7720,"count":0},{"startOffset":7834,"endOffset":8243,"count":0},{"startOffset":8308,"endOffset":8344,"count":0},{"startOffset":8381,"endOffset":8508,"count":0},{"startOffset":8535,"endOffset":8649,"count":0}],"isBlockCoverage":true},{"functionName":"throwImportNotDefined","ranges":[{"startOffset":8673,"endOffset":8888,"count":0}],"isBlockCoverage":false},{"functionName":"throwExportsNotFound","ranges":[{"startOffset":8890,"endOffset":9089,"count":0}],"isBlockCoverage":false},{"functionName":"throwInvalidSubpath","ranges":[{"startOffset":9091,"endOffset":9441,"count":0}],"isBlockCoverage":false},{"functionName":"throwInvalidPackageTarget","ranges":[{"startOffset":9443,"endOffset":9825,"count":0}],"isBlockCoverage":false},{"functionName":"resolvePackageTargetString","ranges":[{"startOffset":9926,"endOffset":11589,"count":0}],"isBlockCoverage":false},{"functionName":"isArrayIndex","ranges":[{"startOffset":11644,"endOffset":11784,"count":0}],"isBlockCoverage":false},{"functionName":"resolvePackageTarget","ranges":[{"startOffset":11786,"endOffset":13918,"count":0}],"isBlockCoverage":false},{"functionName":"isConditionalExportsMainSugar","ranges":[{"startOffset":13920,"endOffset":14855,"count":0}],"isBlockCoverage":false},{"functionName":"packageExportsResolve","ranges":[{"startOffset":15040,"endOffset":16923,"count":0}],"isBlockCoverage":false},{"functionName":"packageImportsResolve","ranges":[{"startOffset":16925,"endOffset":18921,"count":0}],"isBlockCoverage":false},{"functionName":"getPackageType","ranges":[{"startOffset":18923,"endOffset":19036,"count":2}],"isBlockCoverage":true},{"functionName":"packageResolve","ranges":[{"startOffset":19149,"endOffset":21981,"count":0}],"isBlockCoverage":false},{"functionName":"isBareSpecifier","ranges":[{"startOffset":21983,"endOffset":22093,"count":0}],"isBlockCoverage":false},{"functionName":"isRelativeSpecifier","ranges":[{"startOffset":22095,"endOffset":22366,"count":2},{"startOffset":22165,"endOffset":22348,"count":1},{"startOffset":22235,"endOffset":22344,"count":0},{"startOffset":22348,"endOffset":22365,"count":1}],"isBlockCoverage":true},{"functionName":"shouldBeTreatedAsRelativeOrAbsolutePath","ranges":[{"startOffset":22368,"endOffset":22551,"count":2},{"startOffset":22454,"endOffset":22467,"count":0},{"startOffset":22496,"endOffset":22508,"count":0}],"isBlockCoverage":true},{"functionName":"moduleResolve","ranges":[{"startOffset":22664,"endOffset":23235,"count":2},{"startOffset":22892,"endOffset":23188,"count":1},{"startOffset":22970,"endOffset":23048,"count":0},{"startOffset":23109,"endOffset":23184,"count":0}],"isBlockCoverage":true},{"functionName":"resolveAsCommonJS","ranges":[{"startOffset":23381,"endOffset":24789,"count":0}],"isBlockCoverage":false},{"functionName":"defaultResolve","ranges":[{"startOffset":24791,"endOffset":28097,"count":3},{"startOffset":24923,"endOffset":24942,"count":2},{"startOffset":24944,"endOffset":25547,"count":0},{"startOffset":25640,"endOffset":25694,"count":0},{"startOffset":25699,"endOffset":25707,"count":2},{"startOffset":25721,"endOffset":25751,"count":1},{"startOffset":25757,"endOffset":25783,"count":0},{"startOffset":25797,"endOffset":25827,"count":1},{"startOffset":25828,"endOffset":25858,"count":0},{"startOffset":25864,"endOffset":25913,"count":0},{"startOffset":25966,"endOffset":26022,"count":1},{"startOffset":26022,"endOffset":26039,"count":2},{"startOffset":26039,"endOffset":26087,"count":1},{"startOffset":26089,"endOffset":26177,"count":0},{"startOffset":26177,"endOffset":26235,"count":2},{"startOffset":26235,"endOffset":26762,"count":1},{"startOffset":26719,"endOffset":26758,"count":0},{"startOffset":26762,"endOffset":26891,"count":2},{"startOffset":26891,"endOffset":27695,"count":0},{"startOffset":27695,"endOffset":27710,"count":2},{"startOffset":27710,"endOffset":27733,"count":1},{"startOffset":27734,"endOffset":27753,"count":1},{"startOffset":27755,"endOffset":28066,"count":2},{"startOffset":27995,"endOffset":28000,"count":0},{"startOffset":28066,"endOffset":28096,"count":2}],"isBlockCoverage":true}]},{"scriptId":"65","url":"internal/modules/esm/get_format.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2496,"count":1}],"isBlockCoverage":false},{"functionName":"defaultGetFormat","ranges":[{"startOffset":1131,"endOffset":2450,"count":3},{"startOffset":1244,"endOffset":1283,"count":1},{"startOffset":1283,"endOffset":1350,"count":2},{"startOffset":1350,"endOffset":1760,"count":0},{"startOffset":1760,"endOffset":2421,"count":2},{"startOffset":1951,"endOffset":1963,"count":0},{"startOffset":1970,"endOffset":2023,"count":0},{"startOffset":2041,"endOffset":2378,"count":0},{"startOffset":2407,"endOffset":2414,"count":0},{"startOffset":2421,"endOffset":2449,"count":0}],"isBlockCoverage":true}]},{"scriptId":"66","url":"internal/modules/esm/get_source.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1307,"count":1}],"isBlockCoverage":false},{"functionName":"defaultGetSource","ranges":[{"startOffset":609,"endOffset":1261,"count":2},{"startOffset":810,"endOffset":1155,"count":0},{"startOffset":1180,"endOffset":1238,"count":0}],"isBlockCoverage":true}]},{"scriptId":"67","url":"internal/fs/promises.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":20020,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":2272,"endOffset":2432,"count":0}],"isBlockCoverage":false},{"functionName":"FileHandle","ranges":[{"startOffset":2480,"endOffset":2657,"count":14},{"startOffset":2594,"endOffset":2598,"count":0}],"isBlockCoverage":true},{"functionName":"getAsyncId","ranges":[{"startOffset":2661,"endOffset":2718,"count":0}],"isBlockCoverage":false},{"functionName":"get fd","ranges":[{"startOffset":2722,"endOffset":2758,"count":64}],"isBlockCoverage":true},{"functionName":"appendFile","ranges":[{"startOffset":2762,"endOffset":2844,"count":0}],"isBlockCoverage":false},{"functionName":"chmod","ranges":[{"startOffset":2848,"endOffset":2904,"count":0}],"isBlockCoverage":false},{"functionName":"chown","ranges":[{"startOffset":2908,"endOffset":2972,"count":0}],"isBlockCoverage":false},{"functionName":"datasync","ranges":[{"startOffset":2976,"endOffset":3028,"count":0}],"isBlockCoverage":false},{"functionName":"sync","ranges":[{"startOffset":3032,"endOffset":3076,"count":0}],"isBlockCoverage":false},{"functionName":"read","ranges":[{"startOffset":3080,"endOffset":3189,"count":0}],"isBlockCoverage":false},{"functionName":"readv","ranges":[{"startOffset":3193,"endOffset":3274,"count":0}],"isBlockCoverage":false},{"functionName":"readFile","ranges":[{"startOffset":3278,"endOffset":3345,"count":0}],"isBlockCoverage":false},{"functionName":"stat","ranges":[{"startOffset":3349,"endOffset":3409,"count":0}],"isBlockCoverage":false},{"functionName":"truncate","ranges":[{"startOffset":3413,"endOffset":3477,"count":0}],"isBlockCoverage":false},{"functionName":"utimes","ranges":[{"startOffset":3481,"endOffset":3555,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":3559,"endOffset":3670,"count":0}],"isBlockCoverage":false},{"functionName":"writev","ranges":[{"startOffset":3674,"endOffset":3757,"count":0}],"isBlockCoverage":false},{"functionName":"writeFile","ranges":[{"startOffset":3761,"endOffset":3842,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":3846,"endOffset":4538,"count":14}],"isBlockCoverage":false},{"functionName":"close","ranges":[{"startOffset":3854,"endOffset":4538,"count":14},{"startOffset":3888,"endOffset":3926,"count":0},{"startOffset":3957,"endOffset":3998,"count":0},{"startOffset":4192,"endOffset":4501,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":4128,"endOffset":4184,"count":14}],"isBlockCoverage":true},{"functionName":".Promise.finally.","ranges":[{"startOffset":4240,"endOffset":4346,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":4356,"endOffset":4493,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":4542,"endOffset":5011,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":5015,"endOffset":5068,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":5072,"endOffset":5159,"count":0}],"isBlockCoverage":false},{"functionName":"fsCall","ranges":[{"startOffset":5163,"endOffset":5781,"count":0}],"isBlockCoverage":false},{"functionName":"writeFileHandle","ranges":[{"startOffset":5783,"endOffset":6459,"count":0}],"isBlockCoverage":false},{"functionName":"readFileHandle","ranges":[{"startOffset":6461,"endOffset":7737,"count":14},{"startOffset":6572,"endOffset":6589,"count":0},{"startOffset":6591,"endOffset":6667,"count":0},{"startOffset":6760,"endOffset":6777,"count":0},{"startOffset":6779,"endOffset":6855,"count":0},{"startOffset":6964,"endOffset":6989,"count":0},{"startOffset":7022,"endOffset":7060,"count":0},{"startOffset":7114,"endOffset":7141,"count":0},{"startOffset":7216,"endOffset":7567,"count":50},{"startOffset":7233,"endOffset":7250,"count":0},{"startOffset":7252,"endOffset":7332,"count":0},{"startOffset":7523,"endOffset":7563,"count":36},{"startOffset":7626,"endOffset":7637,"count":9},{"startOffset":7638,"endOffset":7661,"count":5},{"startOffset":7690,"endOffset":7725,"count":12},{"startOffset":7726,"endOffset":7734,"count":2}],"isBlockCoverage":true},{"functionName":"access","ranges":[{"startOffset":7890,"endOffset":8111,"count":0}],"isBlockCoverage":false},{"functionName":"copyFile","ranges":[{"startOffset":8113,"endOffset":8471,"count":0}],"isBlockCoverage":false},{"functionName":"open","ranges":[{"startOffset":8591,"endOffset":8916,"count":14}],"isBlockCoverage":true},{"functionName":"read","ranges":[{"startOffset":8918,"endOffset":9972,"count":50},{"startOffset":9057,"endOffset":9339,"count":0},{"startOffset":9363,"endOffset":9384,"count":0},{"startOffset":9476,"endOffset":9513,"count":0},{"startOffset":9542,"endOffset":9672,"count":0},{"startOffset":9776,"endOffset":9790,"count":0},{"startOffset":9932,"endOffset":9936,"count":14}],"isBlockCoverage":true},{"functionName":"readv","ranges":[{"startOffset":9974,"endOffset":10294,"count":0}],"isBlockCoverage":false},{"functionName":"write","ranges":[{"startOffset":10296,"endOffset":11217,"count":0}],"isBlockCoverage":false},{"functionName":"writev","ranges":[{"startOffset":11219,"endOffset":11551,"count":0}],"isBlockCoverage":false},{"functionName":"rename","ranges":[{"startOffset":11553,"endOffset":11859,"count":0}],"isBlockCoverage":false},{"functionName":"truncate","ranges":[{"startOffset":11861,"endOffset":12004,"count":0}],"isBlockCoverage":false},{"functionName":"ftruncate","ranges":[{"startOffset":12006,"endOffset":12165,"count":0}],"isBlockCoverage":false},{"functionName":"rm","ranges":[{"startOffset":12167,"endOffset":12364,"count":0}],"isBlockCoverage":false},{"functionName":"rmdir","ranges":[{"startOffset":12366,"endOffset":12629,"count":0}],"isBlockCoverage":false},{"functionName":"fdatasync","ranges":[{"startOffset":12631,"endOffset":12720,"count":0}],"isBlockCoverage":false},{"functionName":"fsync","ranges":[{"startOffset":12722,"endOffset":12803,"count":0}],"isBlockCoverage":false},{"functionName":"mkdir","ranges":[{"startOffset":12805,"endOffset":13337,"count":0}],"isBlockCoverage":false},{"functionName":"readdir","ranges":[{"startOffset":13339,"endOffset":13791,"count":1},{"startOffset":13729,"endOffset":13775,"count":0}],"isBlockCoverage":true},{"functionName":"readlink","ranges":[{"startOffset":13793,"endOffset":14036,"count":0}],"isBlockCoverage":false},{"functionName":"symlink","ranges":[{"startOffset":14038,"endOffset":14451,"count":0}],"isBlockCoverage":false},{"functionName":"fstat","ranges":[{"startOffset":14453,"endOffset":14631,"count":0}],"isBlockCoverage":false},{"functionName":"lstat","ranges":[{"startOffset":14633,"endOffset":14903,"count":0}],"isBlockCoverage":false},{"functionName":"stat","ranges":[{"startOffset":14905,"endOffset":15172,"count":0}],"isBlockCoverage":false},{"functionName":"link","ranges":[{"startOffset":15174,"endOffset":15497,"count":0}],"isBlockCoverage":false},{"functionName":"unlink","ranges":[{"startOffset":15499,"endOffset":15637,"count":0}],"isBlockCoverage":false},{"functionName":"fchmod","ranges":[{"startOffset":15639,"endOffset":15772,"count":0}],"isBlockCoverage":false},{"functionName":"chmod","ranges":[{"startOffset":15774,"endOffset":15960,"count":0}],"isBlockCoverage":false},{"functionName":"lchmod","ranges":[{"startOffset":15962,"endOffset":16200,"count":0}],"isBlockCoverage":false},{"functionName":"lchown","ranges":[{"startOffset":16202,"endOffset":16478,"count":0}],"isBlockCoverage":false},{"functionName":"fchown","ranges":[{"startOffset":16480,"endOffset":16677,"count":0}],"isBlockCoverage":false},{"functionName":"chown","ranges":[{"startOffset":16679,"endOffset":16952,"count":0}],"isBlockCoverage":false},{"functionName":"utimes","ranges":[{"startOffset":16954,"endOffset":17226,"count":0}],"isBlockCoverage":false},{"functionName":"futimes","ranges":[{"startOffset":17228,"endOffset":17427,"count":0}],"isBlockCoverage":false},{"functionName":"lutimes","ranges":[{"startOffset":17429,"endOffset":17706,"count":0}],"isBlockCoverage":false},{"functionName":"realpath","ranges":[{"startOffset":17708,"endOffset":17885,"count":0}],"isBlockCoverage":false},{"functionName":"mkdtemp","ranges":[{"startOffset":17887,"endOffset":18217,"count":0}],"isBlockCoverage":false},{"functionName":"writeFile","ranges":[{"startOffset":18219,"endOffset":18938,"count":0}],"isBlockCoverage":false},{"functionName":"appendFile","ranges":[{"startOffset":18940,"endOffset":19181,"count":0}],"isBlockCoverage":false},{"functionName":"readFile","ranges":[{"startOffset":19183,"endOffset":19613,"count":14},{"startOffset":19300,"endOffset":19306,"count":0},{"startOffset":19347,"endOffset":19384,"count":0},{"startOffset":19417,"endOffset":19493,"count":0}],"isBlockCoverage":true}]},{"scriptId":"68","url":"internal/fs/rimraf.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":7039,"count":1}],"isBlockCoverage":false},{"functionName":"rimraf","ranges":[{"startOffset":1137,"endOffset":1597,"count":0}],"isBlockCoverage":false},{"functionName":"_rimraf","ranges":[{"startOffset":1600,"endOffset":2458,"count":0}],"isBlockCoverage":false},{"functionName":"fixWinEPERM","ranges":[{"startOffset":2461,"endOffset":2896,"count":0}],"isBlockCoverage":false},{"functionName":"_rmdir","ranges":[{"startOffset":2899,"endOffset":3197,"count":0}],"isBlockCoverage":false},{"functionName":"_rmchildren","ranges":[{"startOffset":3200,"endOffset":3872,"count":0}],"isBlockCoverage":false},{"functionName":"rimrafPromises","ranges":[{"startOffset":3875,"endOffset":4073,"count":0}],"isBlockCoverage":false},{"functionName":"rimrafSync","ranges":[{"startOffset":4076,"endOffset":4781,"count":0}],"isBlockCoverage":false},{"functionName":"_unlinkSync","ranges":[{"startOffset":4784,"endOffset":5267,"count":0}],"isBlockCoverage":false},{"functionName":"_rmdirSync","ranges":[{"startOffset":5270,"endOffset":6540,"count":0}],"isBlockCoverage":false},{"functionName":"fixWinEPERMSync","ranges":[{"startOffset":6543,"endOffset":6979,"count":0}],"isBlockCoverage":false}]},{"scriptId":"69","url":"internal/modules/esm/transform_source.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":215,"count":1}],"isBlockCoverage":false},{"functionName":"defaultTransformSource","ranges":[{"startOffset":15,"endOffset":157,"count":2}],"isBlockCoverage":true}]},{"scriptId":"70","url":"internal/modules/esm/translators.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":12048,"count":1}],"isBlockCoverage":false},{"functionName":"lazyTypes","ranges":[{"startOffset":416,"endOffset":528,"count":4},{"startOffset":462,"endOffset":476,"count":3},{"startOffset":476,"endOffset":527,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":1202,"endOffset":1227,"count":1}],"isBlockCoverage":true},{"functionName":"initCJSParse","ranges":[{"startOffset":1860,"endOffset":2152,"count":0}],"isBlockCoverage":false},{"functionName":"assertBufferSource","ranges":[{"startOffset":2286,"endOffset":2706,"count":4},{"startOffset":2363,"endOffset":2390,"count":2},{"startOffset":2392,"endOffset":2409,"count":0},{"startOffset":2503,"endOffset":2528,"count":0},{"startOffset":2547,"endOffset":2626,"count":0},{"startOffset":2627,"endOffset":2631,"count":0}],"isBlockCoverage":true},{"functionName":"stringify","ranges":[{"startOffset":2708,"endOffset":2926,"count":2},{"startOffset":2767,"endOffset":2779,"count":0},{"startOffset":2863,"endOffset":2882,"count":1},{"startOffset":2883,"endOffset":2892,"count":1}],"isBlockCoverage":true},{"functionName":"errPath","ranges":[{"startOffset":2928,"endOffset":3073,"count":0}],"isBlockCoverage":false},{"functionName":"importModuleDynamically","ranges":[{"startOffset":3075,"endOffset":3189,"count":1}],"isBlockCoverage":true},{"functionName":"createImportMetaResolve","ranges":[{"startOffset":3191,"endOffset":3539,"count":0}],"isBlockCoverage":false},{"functionName":"initializeImportMeta","ranges":[{"startOffset":3541,"endOffset":3711,"count":0}],"isBlockCoverage":false},{"functionName":"moduleStrategy","ranges":[{"startOffset":3793,"endOffset":4374,"count":2}],"isBlockCoverage":true},{"functionName":"enrichCJSError","ranges":[{"startOffset":4378,"endOffset":5277,"count":0}],"isBlockCoverage":false},{"functionName":"commonjsStrategy","ranges":[{"startOffset":5435,"endOffset":6741,"count":0}],"isBlockCoverage":false},{"functionName":"cjsPreparseModuleExports","ranges":[{"startOffset":6745,"endOffset":8189,"count":0}],"isBlockCoverage":false},{"functionName":"builtinStrategy","ranges":[{"startOffset":8313,"endOffset":8701,"count":1},{"startOffset":8574,"endOffset":8626,"count":0}],"isBlockCoverage":true},{"functionName":"jsonStrategy","ranges":[{"startOffset":8765,"endOffset":10884,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":10950,"endOffset":12045,"count":0}],"isBlockCoverage":false}]},{"scriptId":"71","url":"internal/modules/esm/create_dynamic_module.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1756,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":190,"endOffset":215,"count":0}],"isBlockCoverage":false},{"functionName":"createImport","ranges":[{"startOffset":219,"endOffset":409,"count":0}],"isBlockCoverage":false},{"functionName":"createExport","ranges":[{"startOffset":411,"endOffset":612,"count":0}],"isBlockCoverage":false},{"functionName":"createDynamicModule","ranges":[{"startOffset":642,"endOffset":1715,"count":0}],"isBlockCoverage":false}]},{"scriptId":"72","url":"internal/vm/module.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":12877,"count":1}],"isBlockCoverage":false},{"functionName":"Module","ranges":[{"startOffset":1618,"endOffset":3804,"count":0}],"isBlockCoverage":false},{"functionName":"get identifier","ranges":[{"startOffset":3808,"endOffset":3945,"count":0}],"isBlockCoverage":false},{"functionName":"get context","ranges":[{"startOffset":3949,"endOffset":4082,"count":0}],"isBlockCoverage":false},{"functionName":"get namespace","ranges":[{"startOffset":4086,"endOffset":4363,"count":0}],"isBlockCoverage":false},{"functionName":"get status","ranges":[{"startOffset":4367,"endOffset":4520,"count":0}],"isBlockCoverage":false},{"functionName":"get error","ranges":[{"startOffset":4524,"endOffset":4774,"count":0}],"isBlockCoverage":false},{"functionName":"link","ranges":[{"startOffset":4778,"endOffset":5257,"count":0}],"isBlockCoverage":false},{"functionName":"evaluate","ranges":[{"startOffset":5261,"endOffset":6213,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":6217,"endOffset":6945,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":7092,"endOffset":7128,"count":0}],"isBlockCoverage":false},{"functionName":"SourceTextModule","ranges":[{"startOffset":7133,"endOffset":9559,"count":0}],"isBlockCoverage":false},{"functionName":"get dependencySpecifiers","ranges":[{"startOffset":9563,"endOffset":9862,"count":0}],"isBlockCoverage":false},{"functionName":"get status","ranges":[{"startOffset":9866,"endOffset":10135,"count":0}],"isBlockCoverage":false},{"functionName":"get error","ranges":[{"startOffset":10139,"endOffset":10335,"count":0}],"isBlockCoverage":false},{"functionName":"createCachedData","ranges":[{"startOffset":10339,"endOffset":10601,"count":0}],"isBlockCoverage":false},{"functionName":"SyntheticModule","ranges":[{"startOffset":10646,"endOffset":11943,"count":0}],"isBlockCoverage":false},{"functionName":"setExport","ranges":[{"startOffset":11947,"endOffset":12249,"count":0}],"isBlockCoverage":false},{"functionName":"importModuleDynamicallyWrap","ranges":[{"startOffset":12253,"endOffset":12715,"count":0}],"isBlockCoverage":false},{"functionName":"getModuleFromWrap","ranges":[{"startOffset":12837,"endOffset":12872,"count":1}],"isBlockCoverage":true}]},{"scriptId":"73","url":"internal/modules/run_main.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2582,"count":1}],"isBlockCoverage":false},{"functionName":"resolveMainPath","ranges":[{"startOffset":220,"endOffset":658,"count":1},{"startOffset":487,"endOffset":494,"count":0}],"isBlockCoverage":true},{"functionName":"shouldUseESMLoader","ranges":[{"startOffset":660,"endOffset":1215,"count":1},{"startOffset":784,"endOffset":796,"count":0},{"startOffset":944,"endOffset":956,"count":0},{"startOffset":1051,"endOffset":1063,"count":0},{"startOffset":1114,"endOffset":1127,"count":0}],"isBlockCoverage":true},{"functionName":"runMainESM","ranges":[{"startOffset":1217,"endOffset":1552,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":1400,"endOffset":1547,"count":1},{"startOffset":1497,"endOffset":1507,"count":0}],"isBlockCoverage":true},{"functionName":"handleMainPromise","ranges":[{"startOffset":1554,"endOffset":1991,"count":1}],"isBlockCoverage":true},{"functionName":"handler","ranges":[{"startOffset":1803,"endOffset":1896,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":1953,"endOffset":1987,"count":1}],"isBlockCoverage":true},{"functionName":"executeUserEntryPoint","ranges":[{"startOffset":2177,"endOffset":2512,"count":1},{"startOffset":2387,"endOffset":2394,"count":0},{"startOffset":2400,"endOffset":2510,"count":0}],"isBlockCoverage":true}]},{"scriptId":"74","url":"file:///home/runner/work/jslint/jslint/test.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":10107,"count":1}],"isBlockCoverage":true},{"functionName":"testCaseCoverage","ranges":[{"startOffset":36,"endOffset":218,"count":1}],"isBlockCoverage":true},{"functionName":"testCaseJslintWarningsValidate","ranges":[{"startOffset":225,"endOffset":10102,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":9592,"endOffset":10098,"count":84}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":9690,"endOffset":10090,"count":199},{"startOffset":9885,"endOffset":10080,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":9770,"endOffset":9882,"count":323}],"isBlockCoverage":true}]},{"scriptId":"75","url":"file:///home/runner/work/jslint/jslint/jslint.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":158087,"count":1},{"startOffset":158001,"endOffset":158086,"count":0}],"isBlockCoverage":true},{"functionName":"empty","ranges":[{"startOffset":6932,"endOffset":7188,"count":1980}],"isBlockCoverage":true},{"functionName":"populate","ranges":[{"startOffset":7190,"endOffset":7417,"count":454}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":7341,"endOffset":7394,"count":10475}],"isBlockCoverage":true},{"functionName":"tag_regexp","ranges":[{"startOffset":16223,"endOffset":16313,"count":12}],"isBlockCoverage":true},{"functionName":"is_letter","ranges":[{"startOffset":18117,"endOffset":18264,"count":126},{"startOffset":18182,"endOffset":18204,"count":63},{"startOffset":18214,"endOffset":18255,"count":66},{"startOffset":18232,"endOffset":18254,"count":3}],"isBlockCoverage":true},{"functionName":"supplant","ranges":[{"startOffset":18266,"endOffset":18535,"count":490}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":18341,"endOffset":18531,"count":584},{"startOffset":18507,"endOffset":18514,"count":0}],"isBlockCoverage":true},{"functionName":"artifact","ranges":[{"startOffset":20424,"endOffset":20722,"count":405},{"startOffset":20535,"endOffset":20574,"count":5},{"startOffset":20625,"endOffset":20655,"count":400},{"startOffset":20665,"endOffset":20690,"count":43},{"startOffset":20699,"endOffset":20713,"count":362}],"isBlockCoverage":true},{"functionName":"artifact_line","ranges":[{"startOffset":20724,"endOffset":20920,"count":1},{"startOffset":20844,"endOffset":20883,"count":0}],"isBlockCoverage":true},{"functionName":"artifact_column","ranges":[{"startOffset":20922,"endOffset":21122,"count":2},{"startOffset":21046,"endOffset":21085,"count":0}],"isBlockCoverage":true},{"functionName":"warn_at","ranges":[{"startOffset":21124,"endOffset":21874,"count":490},{"startOffset":21422,"endOffset":21452,"count":473},{"startOffset":21478,"endOffset":21508,"count":168},{"startOffset":21534,"endOffset":21564,"count":3},{"startOffset":21590,"endOffset":21620,"count":1},{"startOffset":21665,"endOffset":21672,"count":0},{"startOffset":21768,"endOffset":21824,"count":0}],"isBlockCoverage":true},{"functionName":"stop_at","ranges":[{"startOffset":21876,"endOffset":22036,"count":18}],"isBlockCoverage":true},{"functionName":"warn","ranges":[{"startOffset":22038,"endOffset":22665,"count":434},{"startOffset":22337,"endOffset":22376,"count":9},{"startOffset":22418,"endOffset":22663,"count":345},{"startOffset":22545,"endOffset":22567,"count":277}],"isBlockCoverage":true},{"functionName":"stop","ranges":[{"startOffset":22667,"endOffset":23054,"count":33},{"startOffset":22938,"endOffset":22977,"count":12}],"isBlockCoverage":true},{"functionName":"tokenize","ranges":[{"startOffset":23070,"endOffset":52074,"count":211},{"startOffset":23669,"endOffset":23677,"count":0},{"startOffset":24704,"endOffset":24753,"count":1},{"startOffset":51838,"endOffset":51857,"count":190},{"startOffset":52000,"endOffset":52072,"count":30641},{"startOffset":52036,"endOffset":52066,"count":191}],"isBlockCoverage":true},{"functionName":"next_line","ranges":[{"startOffset":24759,"endOffset":25942,"count":9509},{"startOffset":25083,"endOffset":25096,"count":1},{"startOffset":25109,"endOffset":25117,"count":1},{"startOffset":25130,"endOffset":25145,"count":1},{"startOffset":25156,"endOffset":25210,"count":1},{"startOffset":25347,"endOffset":25352,"count":2861},{"startOffset":25393,"endOffset":25908,"count":9312},{"startOffset":25465,"endOffset":25656,"count":1},{"startOffset":25721,"endOffset":25898,"count":1}],"isBlockCoverage":true},{"functionName":"snip","ranges":[{"startOffset":26396,"endOffset":26504,"count":3029}],"isBlockCoverage":true},{"functionName":"next_char","ranges":[{"startOffset":26510,"endOffset":27333,"count":22445},{"startOffset":26760,"endOffset":26777,"count":539},{"startOffset":26779,"endOffset":27068,"count":3},{"startOffset":26879,"endOffset":26893,"count":2},{"startOffset":26914,"endOffset":26930,"count":1},{"startOffset":27068,"endOffset":27094,"count":22442},{"startOffset":27094,"endOffset":27217,"count":22339},{"startOffset":27217,"endOffset":27285,"count":103},{"startOffset":27285,"endOffset":27332,"count":22442}],"isBlockCoverage":true},{"functionName":"back_char","ranges":[{"startOffset":27339,"endOffset":27703,"count":679},{"startOffset":27636,"endOffset":27676,"count":0}],"isBlockCoverage":true},{"functionName":"some_digits","ranges":[{"startOffset":27709,"endOffset":28100,"count":42},{"startOffset":27849,"endOffset":27864,"count":10},{"startOffset":27866,"endOffset":27948,"count":1}],"isBlockCoverage":true},{"functionName":"escape","ranges":[{"startOffset":28106,"endOffset":29201,"count":279},{"startOffset":28195,"endOffset":28238,"count":123},{"startOffset":28238,"endOffset":28264,"count":156},{"startOffset":28264,"endOffset":28336,"count":1},{"startOffset":28336,"endOffset":28363,"count":155},{"startOffset":28363,"endOffset":29032,"count":30},{"startOffset":28405,"endOffset":28846,"count":2},{"startOffset":28438,"endOffset":28526,"count":1},{"startOffset":28573,"endOffset":28658,"count":1},{"startOffset":28796,"endOffset":28846,"count":0},{"startOffset":28846,"endOffset":28920,"count":28},{"startOffset":28920,"endOffset":29002,"count":1},{"startOffset":29002,"endOffset":29032,"count":28},{"startOffset":29032,"endOffset":29078,"count":125},{"startOffset":29080,"endOffset":29123,"count":124},{"startOffset":29123,"endOffset":29200,"count":1}],"isBlockCoverage":true},{"functionName":"make","ranges":[{"startOffset":29207,"endOffset":31118,"count":31098},{"startOffset":29620,"endOffset":29633,"count":30585},{"startOffset":29635,"endOffset":29682,"count":28281},{"startOffset":29767,"endOffset":29815,"count":3505},{"startOffset":30150,"endOffset":30175,"count":24716},{"startOffset":30188,"endOffset":30246,"count":15888},{"startOffset":30211,"endOffset":30231,"count":15867},{"startOffset":30232,"endOffset":30245,"count":15855},{"startOffset":30259,"endOffset":30321,"count":36},{"startOffset":30291,"endOffset":30320,"count":35},{"startOffset":30332,"endOffset":30513,"count":1},{"startOffset":30546,"endOffset":30566,"count":1866},{"startOffset":30568,"endOffset":30640,"count":2},{"startOffset":30670,"endOffset":30693,"count":1866},{"startOffset":30695,"endOffset":30740,"count":1864},{"startOffset":31045,"endOffset":31086,"count":30585}],"isBlockCoverage":true},{"functionName":"parse_directive","ranges":[{"startOffset":31124,"endOffset":33116,"count":323},{"startOffset":31411,"endOffset":33017,"count":307},{"startOffset":31563,"endOffset":32473,"count":8},{"startOffset":31703,"endOffset":31733,"count":4},{"startOffset":31752,"endOffset":32373,"count":7},{"startOffset":32028,"endOffset":32127,"count":3},{"startOffset":32149,"endOffset":32355,"count":0},{"startOffset":32373,"endOffset":32459,"count":1},{"startOffset":32473,"endOffset":32947,"count":299},{"startOffset":32521,"endOffset":32673,"count":296},{"startOffset":32565,"endOffset":32622,"count":4},{"startOffset":32673,"endOffset":32947,"count":3},{"startOffset":32748,"endOffset":32842,"count":1},{"startOffset":33017,"endOffset":33036,"count":16},{"startOffset":33036,"endOffset":33110,"count":1}],"isBlockCoverage":true},{"functionName":"comment","ranges":[{"startOffset":33122,"endOffset":33947,"count":513},{"startOffset":33373,"endOffset":33425,"count":27},{"startOffset":33478,"endOffset":33536,"count":1},{"startOffset":33609,"endOffset":33913,"count":17},{"startOffset":33644,"endOffset":33732,"count":1},{"startOffset":33732,"endOffset":33913,"count":16},{"startOffset":33913,"endOffset":33946,"count":512}],"isBlockCoverage":true},{"functionName":"regexp","ranges":[{"startOffset":33953,"endOffset":41028,"count":70},{"startOffset":39894,"endOffset":39974,"count":1},{"startOffset":40715,"endOffset":40730,"count":65},{"startOffset":40732,"endOffset":40805,"count":1},{"startOffset":40805,"endOffset":40928,"count":65},{"startOffset":40928,"endOffset":40938,"count":7},{"startOffset":40940,"endOffset":40999,"count":1},{"startOffset":40999,"endOffset":41027,"count":65}],"isBlockCoverage":true},{"functionName":"quantifier","ranges":[{"startOffset":34122,"endOffset":34747,"count":417},{"startOffset":34210,"endOffset":34225,"count":412},{"startOffset":34226,"endOffset":34241,"count":396},{"startOffset":34243,"endOffset":34287,"count":34},{"startOffset":34287,"endOffset":34659,"count":383},{"startOffset":34311,"endOffset":34614,"count":3},{"startOffset":34369,"endOffset":34464,"count":1},{"startOffset":34499,"endOffset":34568,"count":1},{"startOffset":34614,"endOffset":34659,"count":380},{"startOffset":34659,"endOffset":34690,"count":36},{"startOffset":34690,"endOffset":34737,"count":22}],"isBlockCoverage":true},{"functionName":"subklass","ranges":[{"startOffset":34757,"endOffset":35493,"count":68},{"startOffset":34855,"endOffset":34939,"count":13},{"startOffset":34939,"endOffset":35001,"count":55},{"startOffset":35001,"endOffset":35016,"count":54},{"startOffset":35033,"endOffset":35048,"count":54},{"startOffset":35065,"endOffset":35080,"count":33},{"startOffset":35097,"endOffset":35112,"count":32},{"startOffset":35129,"endOffset":35144,"count":32},{"startOffset":35159,"endOffset":35204,"count":23},{"startOffset":35204,"endOffset":35235,"count":32},{"startOffset":35235,"endOffset":35321,"count":1},{"startOffset":35321,"endOffset":35433,"count":31},{"startOffset":35344,"endOffset":35356,"count":2},{"startOffset":35358,"endOffset":35433,"count":1},{"startOffset":35433,"endOffset":35492,"count":32}],"isBlockCoverage":true},{"functionName":"ranges","ranges":[{"startOffset":35503,"endOffset":36011,"count":60},{"startOffset":35585,"endOffset":36001,"count":38},{"startOffset":35621,"endOffset":35954,"count":8},{"startOffset":35696,"endOffset":35936,"count":1},{"startOffset":35954,"endOffset":36001,"count":37}],"isBlockCoverage":true},{"functionName":"klass","ranges":[{"startOffset":36021,"endOffset":36639,"count":22},{"startOffset":36118,"endOffset":36165,"count":3}],"isBlockCoverage":true},{"functionName":"classy","ranges":[{"startOffset":36179,"endOffset":36597,"count":23},{"startOffset":36258,"endOffset":36272,"count":2},{"startOffset":36274,"endOffset":36583,"count":1}],"isBlockCoverage":true},{"functionName":"choice","ranges":[{"startOffset":36649,"endOffset":39694,"count":85},{"startOffset":39604,"endOffset":39684,"count":0}],"isBlockCoverage":true},{"functionName":"group","ranges":[{"startOffset":36682,"endOffset":37245,"count":15},{"startOffset":36815,"endOffset":37054,"count":3},{"startOffset":36907,"endOffset":36967,"count":0},{"startOffset":37054,"endOffset":37173,"count":12},{"startOffset":37078,"endOffset":37173,"count":1}],"isBlockCoverage":true},{"functionName":"factor","ranges":[{"startOffset":37259,"endOffset":39162,"count":501},{"startOffset":37352,"endOffset":37367,"count":500},{"startOffset":37388,"endOffset":37403,"count":434},{"startOffset":37424,"endOffset":37439,"count":434},{"startOffset":37458,"endOffset":37511,"count":82},{"startOffset":37511,"endOffset":37546,"count":419},{"startOffset":37546,"endOffset":37627,"count":15},{"startOffset":37627,"endOffset":37662,"count":404},{"startOffset":37662,"endOffset":37743,"count":22},{"startOffset":37743,"endOffset":37779,"count":382},{"startOffset":37779,"endOffset":37887,"count":80},{"startOffset":37887,"endOffset":37962,"count":302},{"startOffset":37962,"endOffset":37977,"count":301},{"startOffset":37998,"endOffset":38013,"count":301},{"startOffset":38034,"endOffset":38049,"count":301},{"startOffset":38070,"endOffset":38085,"count":301},{"startOffset":38104,"endOffset":38347,"count":1},{"startOffset":38347,"endOffset":39090,"count":301},{"startOffset":38371,"endOffset":38521,"count":7},{"startOffset":38408,"endOffset":38503,"count":1},{"startOffset":38521,"endOffset":39090,"count":294},{"startOffset":38545,"endOffset":38781,"count":1},{"startOffset":38781,"endOffset":39090,"count":293},{"startOffset":38805,"endOffset":38939,"count":17},{"startOffset":38855,"endOffset":38921,"count":6},{"startOffset":38939,"endOffset":39090,"count":276},{"startOffset":38963,"endOffset":39090,"count":16},{"startOffset":39006,"endOffset":39072,"count":3},{"startOffset":39090,"endOffset":39161,"count":302}],"isBlockCoverage":true},{"functionName":"sequence","ranges":[{"startOffset":39176,"endOffset":39470,"count":501},{"startOffset":39234,"endOffset":39330,"count":417},{"startOffset":39330,"endOffset":39360,"count":82},{"startOffset":39360,"endOffset":39456,"count":1}],"isBlockCoverage":true},{"functionName":"make_flag","ranges":[{"startOffset":40315,"endOffset":40664,"count":126},{"startOffset":40371,"endOffset":40654,"count":60},{"startOffset":40417,"endOffset":40501,"count":1}],"isBlockCoverage":true},{"functionName":"string","ranges":[{"startOffset":41034,"endOffset":41858,"count":2287}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":41168,"endOffset":41848,"count":20761},{"startOffset":41218,"endOffset":41387,"count":2283},{"startOffset":41387,"endOffset":41417,"count":18478},{"startOffset":41417,"endOffset":41497,"count":1},{"startOffset":41497,"endOffset":41529,"count":18477},{"startOffset":41529,"endOffset":41575,"count":186},{"startOffset":41575,"endOffset":41811,"count":18291},{"startOffset":41599,"endOffset":41761,"count":48},{"startOffset":41632,"endOffset":41715,"count":1},{"startOffset":41761,"endOffset":41811,"count":18243},{"startOffset":41811,"endOffset":41847,"count":18474}],"isBlockCoverage":true},{"functionName":"frack","ranges":[{"startOffset":41864,"endOffset":42168,"count":265},{"startOffset":41909,"endOffset":41956,"count":5},{"startOffset":41999,"endOffset":42162,"count":0}],"isBlockCoverage":true},{"functionName":"number","ranges":[{"startOffset":42174,"endOffset":43170,"count":586},{"startOffset":42223,"endOffset":42563,"count":324},{"startOffset":42280,"endOffset":42320,"count":3},{"startOffset":42320,"endOffset":42553,"count":321},{"startOffset":42344,"endOffset":42397,"count":0},{"startOffset":42421,"endOffset":42476,"count":0},{"startOffset":42500,"endOffset":42553,"count":3},{"startOffset":42563,"endOffset":42626,"count":262},{"startOffset":42771,"endOffset":42785,"count":261},{"startOffset":42815,"endOffset":42829,"count":7},{"startOffset":42843,"endOffset":42874,"count":585},{"startOffset":42859,"endOffset":42873,"count":144},{"startOffset":42885,"endOffset":43101,"count":1},{"startOffset":43101,"endOffset":43169,"count":585}],"isBlockCoverage":true},{"functionName":"lex","ranges":[{"startOffset":43176,"endOffset":51784,"count":45073},{"startOffset":44001,"endOffset":44311,"count":9220},{"startOffset":44107,"endOffset":44301,"count":193},{"startOffset":44184,"endOffset":44232,"count":1},{"startOffset":44253,"endOffset":44268,"count":192},{"startOffset":44311,"endOffset":44510,"count":44880},{"startOffset":44510,"endOffset":44678,"count":1},{"startOffset":44678,"endOffset":44856,"count":44879},{"startOffset":44856,"endOffset":44893,"count":14077},{"startOffset":44893,"endOffset":44950,"count":30802},{"startOffset":44950,"endOffset":45012,"count":9962},{"startOffset":45012,"endOffset":45064,"count":20840},{"startOffset":45064,"endOffset":45111,"count":586},{"startOffset":45111,"endOffset":45170,"count":20254},{"startOffset":45170,"endOffset":45217,"count":2285},{"startOffset":45217,"endOffset":45247,"count":17969},{"startOffset":45247,"endOffset":45395,"count":2},{"startOffset":45395,"endOffset":45498,"count":17967},{"startOffset":45498,"endOffset":48075,"count":31},{"startOffset":45527,"endOffset":45614,"count":1},{"startOffset":45614,"endOffset":48075,"count":30},{"startOffset":48075,"endOffset":48138,"count":17936},{"startOffset":48138,"endOffset":48389,"count":486},{"startOffset":48274,"endOffset":48349,"count":1},{"startOffset":48389,"endOffset":48452,"count":17450},{"startOffset":48452,"endOffset":49650,"count":30},{"startOffset":48518,"endOffset":48597,"count":3},{"startOffset":49415,"endOffset":49491,"count":1},{"startOffset":49491,"endOffset":49650,"count":27},{"startOffset":49650,"endOffset":49707,"count":17420},{"startOffset":49707,"endOffset":51748,"count":78},{"startOffset":50326,"endOffset":51117,"count":6},{"startOffset":50360,"endOffset":51103,"count":4},{"startOffset":50409,"endOffset":50473,"count":0},{"startOffset":50952,"endOffset":51085,"count":1},{"startOffset":51117,"endOffset":51508,"count":72},{"startOffset":51228,"endOffset":51284,"count":64},{"startOffset":51284,"endOffset":51343,"count":8},{"startOffset":51343,"endOffset":51494,"count":5},{"startOffset":51508,"endOffset":51549,"count":8},{"startOffset":51549,"endOffset":51738,"count":2},{"startOffset":51748,"endOffset":51783,"count":17350}],"isBlockCoverage":true},{"functionName":"part","ranges":[{"startOffset":46015,"endOffset":47927,"count":315},{"startOffset":46190,"endOffset":46468,"count":218},{"startOffset":46346,"endOffset":46394,"count":1},{"startOffset":46419,"endOffset":46427,"count":217},{"startOffset":46468,"endOffset":46650,"count":97},{"startOffset":46650,"endOffset":46849,"count":38},{"startOffset":46849,"endOffset":47175,"count":59},{"startOffset":47175,"endOffset":47913,"count":33}],"isBlockCoverage":true},{"functionName":"expr","ranges":[{"startOffset":47319,"endOffset":47856,"count":144},{"startOffset":47422,"endOffset":47723,"count":1},{"startOffset":47723,"endOffset":47764,"count":141},{"startOffset":47764,"endOffset":47834,"count":111}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":48611,"endOffset":49283,"count":98},{"startOffset":48667,"endOffset":49017,"count":90},{"startOffset":48760,"endOffset":48815,"count":27},{"startOffset":48815,"endOffset":48907,"count":63},{"startOffset":48907,"endOffset":48999,"count":1},{"startOffset":49017,"endOffset":49149,"count":71},{"startOffset":49149,"endOffset":49238,"count":3},{"startOffset":49238,"endOffset":49282,"count":68}],"isBlockCoverage":true},{"functionName":"survey","ranges":[{"startOffset":52481,"endOffset":53730,"count":2219},{"startOffset":52663,"endOffset":52767,"count":91},{"startOffset":52727,"endOffset":52761,"count":5},{"startOffset":52767,"endOffset":53054,"count":2128},{"startOffset":52789,"endOffset":52967,"count":1},{"startOffset":52909,"endOffset":52951,"count":0},{"startOffset":52967,"endOffset":53054,"count":2127},{"startOffset":52995,"endOffset":53054,"count":0},{"startOffset":53054,"endOffset":53157,"count":2214},{"startOffset":53157,"endOffset":53380,"count":1663},{"startOffset":53380,"endOffset":53713,"count":551},{"startOffset":53422,"endOffset":53541,"count":296},{"startOffset":53461,"endOffset":53531,"count":1},{"startOffset":53541,"endOffset":53681,"count":255},{"startOffset":53581,"endOffset":53608,"count":169},{"startOffset":53610,"endOffset":53671,"count":1},{"startOffset":53713,"endOffset":53729,"count":2214}],"isBlockCoverage":true},{"functionName":"dispense","ranges":[{"startOffset":53732,"endOffset":54042,"count":31397},{"startOffset":53895,"endOffset":54005,"count":511},{"startOffset":53920,"endOffset":53972,"count":1},{"startOffset":54005,"endOffset":54040,"count":30886}],"isBlockCoverage":true},{"functionName":"lookahead","ranges":[{"startOffset":54044,"endOffset":54229,"count":230}],"isBlockCoverage":true},{"functionName":"advance","ranges":[{"startOffset":54231,"endOffset":55169,"count":30660},{"startOffset":54373,"endOffset":54399,"count":9950},{"startOffset":54401,"endOffset":54433,"count":9616},{"startOffset":54433,"endOffset":54538,"count":21044},{"startOffset":54467,"endOffset":54501,"count":2339},{"startOffset":54503,"endOffset":54538,"count":1009},{"startOffset":54618,"endOffset":54641,"count":11396},{"startOffset":54643,"endOffset":55004,"count":4},{"startOffset":54706,"endOffset":54756,"count":3},{"startOffset":54769,"endOffset":54987,"count":1},{"startOffset":55004,"endOffset":55137,"count":30656},{"startOffset":55137,"endOffset":55167,"count":329}],"isBlockCoverage":true},{"functionName":"json_value","ranges":[{"startOffset":55202,"endOffset":58239,"count":18},{"startOffset":55275,"endOffset":56730,"count":7},{"startOffset":56730,"endOffset":56762,"count":11},{"startOffset":56762,"endOffset":57337,"count":2},{"startOffset":57337,"endOffset":57416,"count":9},{"startOffset":57425,"endOffset":57452,"count":9},{"startOffset":57459,"endOffset":57507,"count":0},{"startOffset":57507,"endOffset":57546,"count":9},{"startOffset":57546,"endOffset":57692,"count":4},{"startOffset":57600,"endOffset":57645,"count":1},{"startOffset":57692,"endOffset":57731,"count":5},{"startOffset":57731,"endOffset":57894,"count":3},{"startOffset":57772,"endOffset":57847,"count":1},{"startOffset":57894,"endOffset":57926,"count":2},{"startOffset":57926,"endOffset":58238,"count":1}],"isBlockCoverage":true},{"functionName":"json_object","ranges":[{"startOffset":55293,"endOffset":56720,"count":7},{"startOffset":55535,"endOffset":56651,"count":6},{"startOffset":56651,"endOffset":56719,"count":5}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":55554,"endOffset":56633,"count":8},{"startOffset":55684,"endOffset":55893,"count":1},{"startOffset":56033,"endOffset":56108,"count":1},{"startOffset":56108,"endOffset":56307,"count":6},{"startOffset":56147,"endOffset":56225,"count":0},{"startOffset":56307,"endOffset":56515,"count":7},{"startOffset":56515,"endOffset":56615,"count":2}],"isBlockCoverage":true},{"functionName":"json_array","ranges":[{"startOffset":56780,"endOffset":57327,"count":2}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":57004,"endOffset":57236,"count":2},{"startOffset":57118,"endOffset":57218,"count":0}],"isBlockCoverage":true},{"functionName":"enroll","ranges":[{"startOffset":58270,"endOffset":60292,"count":867},{"startOffset":58599,"endOffset":58617,"count":13},{"startOffset":58619,"endOffset":58660,"count":1},{"startOffset":58660,"endOffset":60290,"count":866},{"startOffset":58785,"endOffset":59002,"count":4},{"startOffset":59002,"endOffset":60284,"count":862},{"startOffset":59240,"endOffset":60018,"count":18},{"startOffset":59279,"endOffset":59427,"count":2},{"startOffset":59334,"endOffset":59409,"count":1},{"startOffset":59427,"endOffset":60004,"count":16},{"startOffset":59563,"endOffset":59594,"count":0},{"startOffset":59693,"endOffset":59715,"count":2},{"startOffset":59738,"endOffset":59986,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":59036,"endOffset":59212,"count":1078},{"startOffset":59143,"endOffset":59198,"count":18}],"isBlockCoverage":true},{"functionName":"expression","ranges":[{"startOffset":60294,"endOffset":61616,"count":8645},{"startOffset":60960,"endOffset":60986,"count":6936},{"startOffset":61055,"endOffset":61086,"count":3971},{"startOffset":61088,"endOffset":61128,"count":3968},{"startOffset":61128,"endOffset":61275,"count":4677},{"startOffset":61156,"endOffset":61218,"count":4674},{"startOffset":61218,"endOffset":61275,"count":3},{"startOffset":61275,"endOffset":61615,"count":8637}],"isBlockCoverage":true},{"functionName":"right","ranges":[{"startOffset":61281,"endOffset":61593,"count":14499},{"startOffset":61406,"endOffset":61437,"count":14359},{"startOffset":61450,"endOffset":61473,"count":6451},{"startOffset":61484,"endOffset":61587,"count":5865}],"isBlockCoverage":true},{"functionName":"condition","ranges":[{"startOffset":61618,"endOffset":62041,"count":631},{"startOffset":61874,"endOffset":61922,"count":1},{"startOffset":61969,"endOffset":62017,"count":12}],"isBlockCoverage":true},{"functionName":"is_weird","ranges":[{"startOffset":62043,"endOffset":62272,"count":5459},{"startOffset":62215,"endOffset":62263,"count":5457},{"startOffset":62236,"endOffset":62262,"count":91}],"isBlockCoverage":true},{"functionName":"are_similar","ranges":[{"startOffset":62274,"endOffset":64204,"count":1715},{"startOffset":62320,"endOffset":62348,"count":0},{"startOffset":62375,"endOffset":62593,"count":0},{"startOffset":62620,"endOffset":62649,"count":0},{"startOffset":62678,"endOffset":62700,"count":16},{"startOffset":62702,"endOffset":62745,"count":14},{"startOffset":62745,"endOffset":62811,"count":1701},{"startOffset":62811,"endOffset":62846,"count":102},{"startOffset":62846,"endOffset":62922,"count":1599},{"startOffset":62869,"endOffset":62882,"count":1},{"startOffset":62884,"endOffset":62922,"count":0},{"startOffset":62922,"endOffset":62952,"count":1701},{"startOffset":62952,"endOffset":62987,"count":689},{"startOffset":62987,"endOffset":63063,"count":1012},{"startOffset":63010,"endOffset":63023,"count":0},{"startOffset":63025,"endOffset":63063,"count":0},{"startOffset":63063,"endOffset":63102,"count":1701},{"startOffset":63102,"endOffset":63147,"count":102},{"startOffset":63147,"endOffset":63182,"count":1599},{"startOffset":63184,"endOffset":63213,"count":2},{"startOffset":63213,"endOffset":63242,"count":1597},{"startOffset":63242,"endOffset":63258,"count":698},{"startOffset":63260,"endOffset":64184,"count":409},{"startOffset":63288,"endOffset":63438,"count":109},{"startOffset":63383,"endOffset":63413,"count":68},{"startOffset":63438,"endOffset":63472,"count":300},{"startOffset":63472,"endOffset":63543,"count":3},{"startOffset":63543,"endOffset":63578,"count":297},{"startOffset":63578,"endOffset":63784,"count":150},{"startOffset":63646,"endOffset":63694,"count":138},{"startOffset":63711,"endOffset":63759,"count":76},{"startOffset":63784,"endOffset":63820,"count":147},{"startOffset":63820,"endOffset":64059,"count":0},{"startOffset":64059,"endOffset":64095,"count":147},{"startOffset":64095,"endOffset":64118,"count":0},{"startOffset":64120,"endOffset":64157,"count":0},{"startOffset":64157,"endOffset":64184,"count":147},{"startOffset":64184,"endOffset":64203,"count":1188}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":62483,"endOffset":62575,"count":0}],"isBlockCoverage":false},{"functionName":"semicolon","ranges":[{"startOffset":64206,"endOffset":64513,"count":2087},{"startOffset":64291,"endOffset":64320,"count":1991},{"startOffset":64320,"endOffset":64487,"count":96}],"isBlockCoverage":true},{"functionName":"statement","ranges":[{"startOffset":64515,"endOffset":66214,"count":3196},{"startOffset":64869,"endOffset":64893,"count":3124},{"startOffset":64895,"endOffset":65586,"count":5},{"startOffset":64963,"endOffset":65019,"count":1},{"startOffset":65222,"endOffset":65514,"count":1},{"startOffset":65514,"endOffset":65586,"count":4},{"startOffset":65586,"endOffset":65728,"count":3195},{"startOffset":65728,"endOffset":65759,"count":1626},{"startOffset":65761,"endOffset":65883,"count":1525},{"startOffset":65883,"endOffset":66114,"count":1670},{"startOffset":66006,"endOffset":66033,"count":35},{"startOffset":66035,"endOffset":66087,"count":6},{"startOffset":66087,"endOffset":66114,"count":1659},{"startOffset":66114,"endOffset":66148,"count":3166},{"startOffset":66148,"endOffset":66186,"count":2},{"startOffset":66186,"endOffset":66213,"count":3166}],"isBlockCoverage":true},{"functionName":"statements","ranges":[{"startOffset":66216,"endOffset":66889,"count":1255}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":66378,"endOffset":66860,"count":4350},{"startOffset":66462,"endOffset":66489,"count":3284},{"startOffset":66502,"endOffset":66532,"count":3282},{"startOffset":66545,"endOffset":66572,"count":3279},{"startOffset":66585,"endOffset":66613,"count":3279},{"startOffset":66624,"endOffset":66854,"count":3124},{"startOffset":66731,"endOffset":66798,"count":1},{"startOffset":66798,"endOffset":66854,"count":3095}],"isBlockCoverage":true},{"functionName":"not_top_level","ranges":[{"startOffset":66891,"endOffset":67071,"count":314},{"startOffset":67012,"endOffset":67069,"count":9}],"isBlockCoverage":true},{"functionName":"top_level_only","ranges":[{"startOffset":67073,"endOffset":67244,"count":15},{"startOffset":67195,"endOffset":67242,"count":1}],"isBlockCoverage":true},{"functionName":"block","ranges":[{"startOffset":67246,"endOffset":68344,"count":1063},{"startOffset":67586,"endOffset":67615,"count":1060},{"startOffset":67827,"endOffset":67848,"count":330},{"startOffset":67857,"endOffset":67893,"count":181},{"startOffset":67900,"endOffset":67995,"count":5},{"startOffset":68079,"endOffset":68228,"count":29},{"startOffset":68132,"endOffset":68187,"count":25},{"startOffset":68228,"endOffset":68302,"count":1033},{"startOffset":68302,"endOffset":68343,"count":1062}],"isBlockCoverage":true},{"functionName":"mutation_check","ranges":[{"startOffset":68346,"endOffset":68759,"count":707},{"startOffset":68572,"endOffset":68595,"count":332},{"startOffset":68604,"endOffset":68627,"count":29},{"startOffset":68636,"endOffset":68659,"count":1},{"startOffset":68666,"endOffset":68740,"count":1},{"startOffset":68740,"endOffset":68758,"count":706}],"isBlockCoverage":true},{"functionName":"left_check","ranges":[{"startOffset":68761,"endOffset":69360,"count":3627},{"startOffset":68972,"endOffset":69153,"count":840},{"startOffset":69026,"endOffset":69143,"count":1},{"startOffset":69162,"endOffset":69268,"count":840},{"startOffset":69215,"endOffset":69258,"count":836},{"startOffset":69230,"endOffset":69243,"count":164},{"startOffset":69244,"endOffset":69257,"count":45},{"startOffset":69275,"endOffset":69341,"count":4},{"startOffset":69341,"endOffset":69359,"count":3623}],"isBlockCoverage":true},{"functionName":"symbol","ranges":[{"startOffset":69431,"endOffset":69755,"count":129},{"startOffset":69598,"endOffset":69730,"count":113},{"startOffset":69686,"endOffset":69690,"count":67}],"isBlockCoverage":true},{"functionName":"assignment","ranges":[{"startOffset":69757,"endOffset":70779,"count":12}],"isBlockCoverage":true},{"functionName":"the_symbol.led","ranges":[{"startOffset":70144,"endOffset":70753,"count":704},{"startOffset":70313,"endOffset":70341,"count":630},{"startOffset":70343,"endOffset":70432,"count":318},{"startOffset":70432,"endOffset":70499,"count":386},{"startOffset":70639,"endOffset":70691,"count":1}],"isBlockCoverage":true},{"functionName":"constant","ranges":[{"startOffset":70781,"endOffset":71263,"count":16},{"startOffset":70981,"endOffset":70988,"count":7},{"startOffset":70997,"endOffset":71173,"count":9}],"isBlockCoverage":true},{"functionName":"the_symbol.nud","ranges":[{"startOffset":70999,"endOffset":71173,"count":3144},{"startOffset":71085,"endOffset":71137,"count":260}],"isBlockCoverage":true},{"functionName":"infix","ranges":[{"startOffset":71265,"endOffset":71650,"count":30}],"isBlockCoverage":true},{"functionName":"the_symbol.led","ranges":[{"startOffset":71382,"endOffset":71624,"count":5117},{"startOffset":71498,"endOffset":71537,"count":3677},{"startOffset":71537,"endOffset":71623,"count":1440}],"isBlockCoverage":true},{"functionName":"infixr","ranges":[{"startOffset":71652,"endOffset":71987,"count":1}],"isBlockCoverage":true},{"functionName":"the_symbol.led","ranges":[{"startOffset":71784,"endOffset":71961,"count":0}],"isBlockCoverage":false},{"functionName":"post","ranges":[{"startOffset":71989,"endOffset":72283,"count":2}],"isBlockCoverage":true},{"functionName":"the_symbol.led","ranges":[{"startOffset":72107,"endOffset":72257,"count":3}],"isBlockCoverage":true},{"functionName":"pre","ranges":[{"startOffset":72285,"endOffset":72627,"count":2}],"isBlockCoverage":true},{"functionName":"the_symbol.nud","ranges":[{"startOffset":72396,"endOffset":72601,"count":0}],"isBlockCoverage":false},{"functionName":"prefix","ranges":[{"startOffset":72629,"endOffset":72999,"count":17}],"isBlockCoverage":true},{"functionName":"the_symbol.nud","ranges":[{"startOffset":72739,"endOffset":72973,"count":814},{"startOffset":72858,"endOffset":72893,"count":682},{"startOffset":72893,"endOffset":72972,"count":132}],"isBlockCoverage":true},{"functionName":"stmt","ranges":[{"startOffset":73001,"endOffset":73203,"count":22}],"isBlockCoverage":true},{"functionName":"the_symbol.fud","ranges":[{"startOffset":73103,"endOffset":73177,"count":1525}],"isBlockCoverage":true},{"functionName":"ternary","ranges":[{"startOffset":73205,"endOffset":73715,"count":1}],"isBlockCoverage":true},{"functionName":"the_symbol.led","ranges":[{"startOffset":73325,"endOffset":73689,"count":41},{"startOffset":73605,"endOffset":73657,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":74332,"endOffset":74398,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":74430,"endOffset":74636,"count":2},{"startOffset":74466,"endOffset":74616,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":74709,"endOffset":74915,"count":3},{"startOffset":74745,"endOffset":74789,"count":2},{"startOffset":74789,"endOffset":74895,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":74950,"endOffset":75016,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":75094,"endOffset":75191,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":75224,"endOffset":75290,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":75384,"endOffset":75484,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":76241,"endOffset":77437,"count":1578},{"startOffset":76342,"endOffset":76386,"count":1545},{"startOffset":76429,"endOffset":76447,"count":487},{"startOffset":76449,"endOffset":76504,"count":347},{"startOffset":76571,"endOffset":77067,"count":1299},{"startOffset":77140,"endOffset":77368,"count":772},{"startOffset":77216,"endOffset":77272,"count":1},{"startOffset":77310,"endOffset":77362,"count":17},{"startOffset":77368,"endOffset":77413,"count":806}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":76582,"endOffset":77057,"count":2126},{"startOffset":76667,"endOffset":76747,"count":2},{"startOffset":76817,"endOffset":76878,"count":2},{"startOffset":76971,"endOffset":77047,"count":827}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":77456,"endOffset":78347,"count":1864},{"startOffset":77598,"endOffset":77648,"count":7},{"startOffset":77624,"endOffset":77647,"count":5},{"startOffset":77667,"endOffset":77893,"count":1857},{"startOffset":77712,"endOffset":77883,"count":46},{"startOffset":77770,"endOffset":77794,"count":45},{"startOffset":77811,"endOffset":77832,"count":44},{"startOffset":77849,"endOffset":77869,"count":44},{"startOffset":77902,"endOffset":77945,"count":1855},{"startOffset":77922,"endOffset":77944,"count":1},{"startOffset":77954,"endOffset":78061,"count":1854},{"startOffset":78006,"endOffset":78051,"count":8},{"startOffset":78029,"endOffset":78050,"count":6},{"startOffset":78068,"endOffset":78112,"count":1846},{"startOffset":78139,"endOffset":78185,"count":1},{"startOffset":78185,"endOffset":78346,"count":1863}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":78367,"endOffset":79258,"count":1},{"startOffset":78509,"endOffset":78559,"count":0},{"startOffset":78623,"endOffset":78794,"count":0},{"startOffset":78833,"endOffset":78855,"count":0},{"startOffset":78917,"endOffset":78962,"count":0},{"startOffset":79096,"endOffset":79257,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":79277,"endOffset":79714,"count":221},{"startOffset":79405,"endOffset":79432,"count":220},{"startOffset":79434,"endOffset":79589,"count":2},{"startOffset":79518,"endOffset":79583,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":79734,"endOffset":79794,"count":1}],"isBlockCoverage":true},{"functionName":"do_tick","ranges":[{"startOffset":79798,"endOffset":80300,"count":26}],"isBlockCoverage":true},{"functionName":"part","ranges":[{"startOffset":79944,"endOffset":80249,"count":56},{"startOffset":80075,"endOffset":80239,"count":30}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":80318,"endOffset":80484,"count":12}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":80613,"endOffset":81274,"count":158},{"startOffset":80718,"endOffset":81232,"count":110}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":80729,"endOffset":81222,"count":446},{"startOffset":80847,"endOffset":80927,"count":0},{"startOffset":80992,"endOffset":81048,"count":0},{"startOffset":81136,"endOffset":81212,"count":336}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":81290,"endOffset":81352,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":81368,"endOffset":81442,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":81459,"endOffset":81704,"count":15},{"startOffset":81566,"endOffset":81650,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":81740,"endOffset":81881,"count":1}],"isBlockCoverage":true},{"functionName":"parameter_list","ranges":[{"startOffset":81885,"endOffset":87227,"count":336},{"startOffset":82011,"endOffset":82039,"count":198},{"startOffset":82041,"endOffset":87143,"count":198},{"startOffset":87143,"endOffset":87226,"count":330}],"isBlockCoverage":true},{"functionName":"parameter","ranges":[{"startOffset":82052,"endOffset":87133,"count":279},{"startOffset":82171,"endOffset":84078,"count":20},{"startOffset":82217,"endOffset":82445,"count":1},{"startOffset":83929,"endOffset":84064,"count":6},{"startOffset":84078,"endOffset":87123,"count":259},{"startOffset":84111,"endOffset":85473,"count":8},{"startOffset":84157,"endOffset":84385,"count":1},{"startOffset":85324,"endOffset":85459,"count":2},{"startOffset":85473,"endOffset":87123,"count":251},{"startOffset":85526,"endOffset":85966,"count":3},{"startOffset":85692,"endOffset":85948,"count":1},{"startOffset":86011,"endOffset":86088,"count":2},{"startOffset":86088,"endOffset":86258,"count":249},{"startOffset":86258,"endOffset":86320,"count":2},{"startOffset":86320,"endOffset":87109,"count":247},{"startOffset":86375,"endOffset":86536,"count":11},{"startOffset":86536,"endOffset":86892,"count":236},{"startOffset":86596,"endOffset":86870,"count":0},{"startOffset":86940,"endOffset":87091,"count":73}],"isBlockCoverage":true},{"functionName":"subparameter","ranges":[{"startOffset":82600,"endOffset":83780,"count":40},{"startOffset":82719,"endOffset":82804,"count":1},{"startOffset":82804,"endOffset":82970,"count":39},{"startOffset":82970,"endOffset":83300,"count":2},{"startOffset":83185,"endOffset":83278,"count":1},{"startOffset":83300,"endOffset":83348,"count":38},{"startOffset":83348,"endOffset":83512,"count":7},{"startOffset":83512,"endOffset":83608,"count":38},{"startOffset":83608,"endOffset":83762,"count":20}],"isBlockCoverage":true},{"functionName":"subparameter","ranges":[{"startOffset":84541,"endOffset":85212,"count":14},{"startOffset":84662,"endOffset":84747,"count":2},{"startOffset":84747,"endOffset":84874,"count":12},{"startOffset":84874,"endOffset":85038,"count":1},{"startOffset":85038,"endOffset":85086,"count":12},{"startOffset":85086,"endOffset":85194,"count":6}],"isBlockCoverage":true},{"functionName":"do_function","ranges":[{"startOffset":87229,"endOffset":89972,"count":336},{"startOffset":87316,"endOffset":88131,"count":333},{"startOffset":87475,"endOffset":87808,"count":137},{"startOffset":87517,"endOffset":87598,"count":1},{"startOffset":87598,"endOffset":87808,"count":136},{"startOffset":87808,"endOffset":88125,"count":196},{"startOffset":87933,"endOffset":88052,"count":32},{"startOffset":88052,"endOffset":88115,"count":164},{"startOffset":88131,"endOffset":88178,"count":3},{"startOffset":88178,"endOffset":88246,"count":335},{"startOffset":88246,"endOffset":88297,"count":0},{"startOffset":88297,"endOffset":88421,"count":335},{"startOffset":88421,"endOffset":88476,"count":1},{"startOffset":88476,"endOffset":88937,"count":335},{"startOffset":88937,"endOffset":88964,"count":199},{"startOffset":88966,"endOffset":89089,"count":32},{"startOffset":89089,"endOffset":89633,"count":335},{"startOffset":89633,"endOffset":89666,"count":130},{"startOffset":89673,"endOffset":89729,"count":2},{"startOffset":89729,"endOffset":89802,"count":327},{"startOffset":89811,"endOffset":89835,"count":327},{"startOffset":89842,"endOffset":89879,"count":1},{"startOffset":89879,"endOffset":89971,"count":327}],"isBlockCoverage":true},{"functionName":"enroll_parameter","ranges":[{"startOffset":89300,"endOffset":89493,"count":310},{"startOffset":89363,"endOffset":89420,"count":290},{"startOffset":89420,"endOffset":89487,"count":20}],"isBlockCoverage":true},{"functionName":"do_async","ranges":[{"startOffset":89974,"endOffset":90328,"count":13},{"startOffset":90239,"endOffset":90301,"count":1},{"startOffset":90301,"endOffset":90327,"count":12}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":90406,"endOffset":90650,"count":21},{"startOffset":90493,"endOffset":90548,"count":1},{"startOffset":90548,"endOffset":90649,"count":20}],"isBlockCoverage":true},{"functionName":"fart","ranges":[{"startOffset":90654,"endOffset":91701,"count":2},{"startOffset":90882,"endOffset":90933,"count":1},{"startOffset":91484,"endOffset":91647,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":91386,"endOffset":91450,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":91715,"endOffset":92870,"count":230},{"startOffset":91971,"endOffset":91997,"count":229},{"startOffset":92006,"endOffset":92068,"count":229},{"startOffset":92032,"endOffset":92067,"count":153},{"startOffset":92075,"endOffset":92153,"count":1},{"startOffset":92153,"endOffset":92248,"count":229},{"startOffset":92248,"endOffset":92296,"count":1},{"startOffset":92296,"endOffset":92388,"count":229},{"startOffset":92388,"endOffset":92846,"count":3},{"startOffset":92434,"endOffset":92725,"count":2},{"startOffset":92498,"endOffset":92725,"count":1},{"startOffset":92725,"endOffset":92846,"count":1},{"startOffset":92846,"endOffset":92869,"count":226}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":92907,"endOffset":95841,"count":45},{"startOffset":93038,"endOffset":95799,"count":42}],"isBlockCoverage":true},{"functionName":"member","ranges":[{"startOffset":93049,"endOffset":95789,"count":313},{"startOffset":93267,"endOffset":93287,"count":312},{"startOffset":93305,"endOffset":93329,"count":3},{"startOffset":93344,"endOffset":93843,"count":3},{"startOffset":93382,"endOffset":93449,"count":1},{"startOffset":93694,"endOffset":93760,"count":0},{"startOffset":93843,"endOffset":94050,"count":310},{"startOffset":93937,"endOffset":94003,"count":1},{"startOffset":94084,"endOffset":95485,"count":223},{"startOffset":94128,"endOffset":94152,"count":215},{"startOffset":94154,"endOffset":94342,"count":39},{"startOffset":94207,"endOffset":94268,"count":0},{"startOffset":94342,"endOffset":95277,"count":184},{"startOffset":94375,"endOffset":94844,"count":3},{"startOffset":94731,"endOffset":94735,"count":0},{"startOffset":94844,"endOffset":95277,"count":181},{"startOffset":94903,"endOffset":94964,"count":0},{"startOffset":95135,"endOffset":95161,"count":2},{"startOffset":95163,"endOffset":95259,"count":1},{"startOffset":95361,"endOffset":95421,"count":3},{"startOffset":95485,"endOffset":95661,"count":90},{"startOffset":95701,"endOffset":95779,"count":271}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":95855,"endOffset":95921,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":95934,"endOffset":96008,"count":3}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":96050,"endOffset":96878,"count":12},{"startOffset":96151,"endOffset":96176,"count":6},{"startOffset":96186,"endOffset":96212,"count":9},{"startOffset":96219,"endOffset":96267,"count":3},{"startOffset":96328,"endOffset":96361,"count":2},{"startOffset":96363,"endOffset":96836,"count":2},{"startOffset":96482,"endOffset":96511,"count":1},{"startOffset":96524,"endOffset":96541,"count":0},{"startOffset":96613,"endOffset":96630,"count":1},{"startOffset":96648,"endOffset":96666,"count":1},{"startOffset":96683,"endOffset":96698,"count":1},{"startOffset":96723,"endOffset":96773,"count":0}],"isBlockCoverage":true},{"functionName":"do_var","ranges":[{"startOffset":96882,"endOffset":101176,"count":396},{"startOffset":97084,"endOffset":97387,"count":223},{"startOffset":97122,"endOffset":97174,"count":25},{"startOffset":97174,"endOffset":97381,"count":198},{"startOffset":97215,"endOffset":97381,"count":1},{"startOffset":97488,"endOffset":97538,"count":1},{"startOffset":97568,"endOffset":97597,"count":1},{"startOffset":97599,"endOffset":97647,"count":1}],"isBlockCoverage":true},{"functionName":"next","ranges":[{"startOffset":97653,"endOffset":101127,"count":396},{"startOffset":97705,"endOffset":97734,"count":1},{"startOffset":97736,"endOffset":99210,"count":1},{"startOffset":99210,"endOffset":101121,"count":395},{"startOffset":99242,"endOffset":99271,"count":1},{"startOffset":99273,"endOffset":100533,"count":1},{"startOffset":100533,"endOffset":101121,"count":394},{"startOffset":100666,"endOffset":100725,"count":1},{"startOffset":100812,"endOffset":100823,"count":141},{"startOffset":100825,"endOffset":100988,"count":253},{"startOffset":101042,"endOffset":101121,"count":0}],"isBlockCoverage":true},{"functionName":"pair","ranges":[{"startOffset":97819,"endOffset":99090,"count":2},{"startOffset":97881,"endOffset":97970,"count":0},{"startOffset":98112,"endOffset":98550,"count":0},{"startOffset":98796,"endOffset":98944,"count":0},{"startOffset":98988,"endOffset":99076,"count":1}],"isBlockCoverage":true},{"functionName":"element","ranges":[{"startOffset":99358,"endOffset":100413,"count":2},{"startOffset":99454,"endOffset":99546,"count":0},{"startOffset":99591,"endOffset":99948,"count":1},{"startOffset":99948,"endOffset":100009,"count":0},{"startOffset":100009,"endOffset":100399,"count":1},{"startOffset":100064,"endOffset":100230,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":101218,"endOffset":101523,"count":2},{"startOffset":101321,"endOffset":101372,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":101543,"endOffset":101698,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":101716,"endOffset":102059,"count":13},{"startOffset":101835,"endOffset":101858,"count":1},{"startOffset":101906,"endOffset":101980,"count":1},{"startOffset":101980,"endOffset":102058,"count":12}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":102073,"endOffset":102401,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":102419,"endOffset":104905,"count":7},{"startOffset":103123,"endOffset":103728,"count":3},{"startOffset":103168,"endOffset":103212,"count":0},{"startOffset":103508,"endOffset":103566,"count":1},{"startOffset":103602,"endOffset":103638,"count":2},{"startOffset":103728,"endOffset":104856,"count":4},{"startOffset":103778,"endOffset":104249,"count":1},{"startOffset":104005,"endOffset":104067,"count":0},{"startOffset":104249,"endOffset":104850,"count":3},{"startOffset":104386,"endOffset":104468,"count":1},{"startOffset":104468,"endOffset":104850,"count":2},{"startOffset":104501,"endOffset":104850,"count":1},{"startOffset":104856,"endOffset":104904,"count":6}],"isBlockCoverage":true},{"functionName":"export_id","ranges":[{"startOffset":102521,"endOffset":103052,"count":2},{"startOffset":102580,"endOffset":102634,"count":0},{"startOffset":102746,"endOffset":102791,"count":0},{"startOffset":102878,"endOffset":102930,"count":1}],"isBlockCoverage":true},{"functionName":"loop","ranges":[{"startOffset":104542,"endOffset":104734,"count":2},{"startOffset":104632,"endOffset":104720,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":104920,"endOffset":106216,"count":7},{"startOffset":104997,"endOffset":105043,"count":6},{"startOffset":105171,"endOffset":105245,"count":1},{"startOffset":105245,"endOffset":105321,"count":6},{"startOffset":105330,"endOffset":105358,"count":6},{"startOffset":105365,"endOffset":105409,"count":1},{"startOffset":105409,"endOffset":105464,"count":5},{"startOffset":105464,"endOffset":105754,"count":3},{"startOffset":105520,"endOffset":105590,"count":1},{"startOffset":105754,"endOffset":106035,"count":2},{"startOffset":105957,"endOffset":106029,"count":1},{"startOffset":106035,"endOffset":106123,"count":5},{"startOffset":106123,"endOffset":106167,"count":0},{"startOffset":106167,"endOffset":106215,"count":5}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":106261,"endOffset":106840,"count":615},{"startOffset":106418,"endOffset":106819,"count":146},{"startOffset":106542,"endOffset":106555,"count":69},{"startOffset":106568,"endOffset":106577,"count":77},{"startOffset":106632,"endOffset":106813,"count":1},{"startOffset":106734,"endOffset":106803,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":106858,"endOffset":108783,"count":11},{"startOffset":106933,"endOffset":107448,"count":1},{"startOffset":107448,"endOffset":107504,"count":10},{"startOffset":107504,"endOffset":107587,"count":1},{"startOffset":107587,"endOffset":107643,"count":10},{"startOffset":107643,"endOffset":107854,"count":8},{"startOffset":107725,"endOffset":107776,"count":1},{"startOffset":107854,"endOffset":108547,"count":2},{"startOffset":108018,"endOffset":108432,"count":1},{"startOffset":108432,"endOffset":108476,"count":0},{"startOffset":108486,"endOffset":108547,"count":1},{"startOffset":108547,"endOffset":108663,"count":9},{"startOffset":108663,"endOffset":108712,"count":1},{"startOffset":108712,"endOffset":108782,"count":9}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":108822,"endOffset":109179,"count":284},{"startOffset":108930,"endOffset":108979,"count":1},{"startOffset":109041,"endOffset":109079,"count":263},{"startOffset":109081,"endOffset":109136,"count":263}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":109197,"endOffset":111786,"count":5},{"startOffset":109407,"endOffset":109456,"count":1},{"startOffset":110995,"endOffset":111622,"count":3},{"startOffset":111196,"endOffset":111287,"count":0},{"startOffset":111409,"endOffset":111440,"count":0},{"startOffset":111442,"endOffset":111547,"count":0},{"startOffset":111586,"endOffset":111605,"count":1},{"startOffset":111622,"endOffset":111664,"count":2}],"isBlockCoverage":true},{"functionName":"major","ranges":[{"startOffset":109645,"endOffset":110931,"count":7},{"startOffset":110331,"endOffset":110405,"count":1},{"startOffset":110405,"endOffset":110538,"count":6},{"startOffset":110538,"endOffset":110667,"count":5},{"startOffset":110576,"endOffset":110603,"count":2},{"startOffset":110605,"endOffset":110657,"count":2},{"startOffset":110667,"endOffset":110847,"count":1},{"startOffset":110847,"endOffset":110886,"count":6},{"startOffset":110886,"endOffset":110925,"count":2}],"isBlockCoverage":true},{"functionName":"minor","ranges":[{"startOffset":109782,"endOffset":110266,"count":13},{"startOffset":110011,"endOffset":110069,"count":0},{"startOffset":110209,"endOffset":110256,"count":6}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":109928,"endOffset":110008,"count":19}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":111803,"endOffset":112037,"count":6},{"startOffset":111965,"endOffset":112013,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":112052,"endOffset":113498,"count":7},{"startOffset":112162,"endOffset":112208,"count":0},{"startOffset":112340,"endOffset":113046,"count":6},{"startOffset":112570,"endOffset":112651,"count":0},{"startOffset":112696,"endOffset":112848,"count":1},{"startOffset":112996,"endOffset":113040,"count":5},{"startOffset":113046,"endOffset":113198,"count":1},{"startOffset":113236,"endOffset":113415,"count":4}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":113536,"endOffset":113846,"count":15},{"startOffset":113749,"endOffset":113795,"count":3}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":113862,"endOffset":113910,"count":0}],"isBlockCoverage":false},{"functionName":"action","ranges":[{"startOffset":113968,"endOffset":114861,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":114110,"endOffset":114858,"count":38},{"startOffset":114315,"endOffset":114375,"count":8},{"startOffset":114496,"endOffset":114569,"count":10},{"startOffset":114710,"endOffset":114776,"count":37}],"isBlockCoverage":true},{"functionName":"amble","ranges":[{"startOffset":114863,"endOffset":115816,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":115008,"endOffset":115813,"count":32988},{"startOffset":115294,"endOffset":115807,"count":22312},{"startOffset":115432,"endOffset":115559,"count":9072},{"startOffset":115670,"endOffset":115797,"count":16605}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":115464,"endOffset":115543,"count":9072}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":115702,"endOffset":115781,"count":21718}],"isBlockCoverage":true},{"functionName":"walk_expression","ranges":[{"startOffset":115992,"endOffset":116678,"count":25743},{"startOffset":116041,"endOffset":116676,"count":16169},{"startOffset":116077,"endOffset":116132,"count":3901},{"startOffset":116132,"endOffset":116670,"count":12268},{"startOffset":116257,"endOffset":116317,"count":200},{"startOffset":116383,"endOffset":116443,"count":1},{"startOffset":116443,"endOffset":116630,"count":12267},{"startOffset":116560,"endOffset":116630,"count":0}],"isBlockCoverage":true},{"functionName":"walk_statement","ranges":[{"startOffset":116680,"endOffset":117475,"count":11919},{"startOffset":116728,"endOffset":117473,"count":5451},{"startOffset":116764,"endOffset":116818,"count":1225},{"startOffset":116818,"endOffset":117467,"count":4226},{"startOffset":116944,"endOffset":117077,"count":914},{"startOffset":116984,"endOffset":117063,"count":19},{"startOffset":117077,"endOffset":117346,"count":3312},{"startOffset":117148,"endOffset":117179,"count":750},{"startOffset":117196,"endOffset":117220,"count":46},{"startOffset":117237,"endOffset":117260,"count":45},{"startOffset":117275,"endOffset":117346,"count":36}],"isBlockCoverage":true},{"functionName":"lookup","ranges":[{"startOffset":117477,"endOffset":119349,"count":4668},{"startOffset":117538,"endOffset":119347,"count":4667},{"startOffset":117798,"endOffset":118913,"count":1274},{"startOffset":118283,"endOffset":118804,"count":132},{"startOffset":118347,"endOffset":118443,"count":64},{"startOffset":118443,"endOffset":118804,"count":68},{"startOffset":118804,"endOffset":118913,"count":1210},{"startOffset":118913,"endOffset":119001,"count":3393},{"startOffset":118954,"endOffset":119001,"count":1},{"startOffset":119001,"endOffset":119057,"count":4603},{"startOffset":119057,"endOffset":119247,"count":1},{"startOffset":119258,"endOffset":119312,"count":1},{"startOffset":119312,"endOffset":119347,"count":4603}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":117826,"endOffset":118120,"count":2136},{"startOffset":117991,"endOffset":118021,"count":1241},{"startOffset":118040,"endOffset":118106,"count":1241}],"isBlockCoverage":true},{"functionName":"subactivate","ranges":[{"startOffset":119351,"endOffset":119456,"count":42}],"isBlockCoverage":true},{"functionName":"preaction_function","ranges":[{"startOffset":119458,"endOffset":120387,"count":327},{"startOffset":119531,"endOffset":119556,"count":125},{"startOffset":119558,"endOffset":119602,"count":0},{"startOffset":119772,"endOffset":119844,"count":158},{"startOffset":119876,"endOffset":119974,"count":1},{"startOffset":119974,"endOffset":120105,"count":326},{"startOffset":120007,"endOffset":120105,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":120135,"endOffset":120383,"count":264},{"startOffset":120223,"endOffset":120241,"count":249},{"startOffset":120243,"endOffset":120299,"count":18},{"startOffset":120299,"endOffset":120377,"count":246}],"isBlockCoverage":true},{"functionName":"bitwise_check","ranges":[{"startOffset":120389,"endOffset":120917,"count":5817},{"startOffset":120445,"endOffset":120476,"count":91},{"startOffset":120478,"endOffset":120522,"count":1},{"startOffset":120565,"endOffset":120585,"count":4239},{"startOffset":120594,"endOffset":120614,"count":4045},{"startOffset":120623,"endOffset":120642,"count":3860},{"startOffset":120651,"endOffset":120685,"count":3230},{"startOffset":120694,"endOffset":120726,"count":1365},{"startOffset":120735,"endOffset":120864,"count":1353},{"startOffset":120871,"endOffset":120915,"count":1}],"isBlockCoverage":true},{"functionName":"pop_block","ranges":[{"startOffset":120919,"endOffset":121081,"count":1384}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":120968,"endOffset":121017,"count":337}],"isBlockCoverage":true},{"functionName":"activate","ranges":[{"startOffset":121083,"endOffset":121404,"count":395},{"startOffset":121171,"endOffset":121372,"count":252},{"startOffset":121263,"endOffset":121319,"count":0}],"isBlockCoverage":true},{"functionName":"action_var","ranges":[{"startOffset":121406,"endOffset":121471,"count":394}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":121569,"endOffset":122620,"count":5113},{"startOffset":121627,"endOffset":122618,"count":810},{"startOffset":121744,"endOffset":121765,"count":809},{"startOffset":121767,"endOffset":121819,"count":1},{"startOffset":121819,"endOffset":122612,"count":809},{"startOffset":121851,"endOffset":122612,"count":25},{"startOffset":121894,"endOffset":122026,"count":2},{"startOffset":121939,"endOffset":122012,"count":1},{"startOffset":122026,"endOffset":122602,"count":23},{"startOffset":122140,"endOffset":122222,"count":1},{"startOffset":122222,"endOffset":122588,"count":22},{"startOffset":122293,"endOffset":122316,"count":20},{"startOffset":122337,"endOffset":122358,"count":18},{"startOffset":122379,"endOffset":122400,"count":17},{"startOffset":122421,"endOffset":122442,"count":10},{"startOffset":122463,"endOffset":122484,"count":1},{"startOffset":122503,"endOffset":122588,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":122649,"endOffset":122715,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":122744,"endOffset":122810,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":122886,"endOffset":123055,"count":185}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":122934,"endOffset":123051,"count":370},{"startOffset":122983,"endOffset":123000,"count":35},{"startOffset":123002,"endOffset":123045,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":123083,"endOffset":123780,"count":1578},{"startOffset":123181,"endOffset":123226,"count":1128},{"startOffset":123235,"endOffset":123274,"count":562},{"startOffset":123281,"endOffset":123778,"count":306},{"startOffset":123489,"endOffset":123510,"count":139},{"startOffset":123527,"endOffset":123561,"count":2},{"startOffset":123578,"endOffset":123614,"count":2},{"startOffset":123631,"endOffset":123688,"count":2},{"startOffset":123703,"endOffset":123762,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":123809,"endOffset":123858,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":123895,"endOffset":123948,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":123976,"endOffset":124060,"count":1863},{"startOffset":124025,"endOffset":124058,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":124091,"endOffset":124186,"count":1057}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":124219,"endOffset":124565,"count":5},{"startOffset":124272,"endOffset":124528,"count":3},{"startOffset":124363,"endOffset":124522,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":124738,"endOffset":124913,"count":4347},{"startOffset":124833,"endOffset":124911,"count":4309}],"isBlockCoverage":true},{"functionName":"init_variable","ranges":[{"startOffset":124917,"endOffset":125173,"count":318},{"startOffset":125023,"endOffset":125135,"count":293},{"startOffset":125135,"endOffset":125172,"count":25}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":125206,"endOffset":125622,"count":65},{"startOffset":125286,"endOffset":125620,"count":38},{"startOffset":125372,"endOffset":125394,"count":29},{"startOffset":125408,"endOffset":125435,"count":37},{"startOffset":125448,"endOffset":125470,"count":37},{"startOffset":125483,"endOffset":125510,"count":37},{"startOffset":125523,"endOffset":125551,"count":37},{"startOffset":125562,"endOffset":125614,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":125650,"endOffset":127457,"count":704},{"startOffset":125946,"endOffset":126777,"count":630},{"startOffset":125987,"endOffset":126175,"count":318},{"startOffset":126033,"endOffset":126100,"count":0},{"startOffset":126175,"endOffset":126771,"count":312},{"startOffset":126217,"endOffset":126237,"count":285},{"startOffset":126239,"endOffset":126451,"count":27},{"startOffset":126451,"endOffset":126761,"count":285},{"startOffset":126512,"endOffset":126553,"count":284},{"startOffset":126568,"endOffset":126761,"count":1},{"startOffset":126777,"endOffset":127455,"count":74},{"startOffset":126826,"endOffset":126974,"count":54},{"startOffset":126861,"endOffset":126897,"count":51},{"startOffset":126899,"endOffset":126964,"count":3},{"startOffset":127086,"endOffset":127372,"count":64},{"startOffset":127244,"endOffset":127270,"count":44},{"startOffset":127291,"endOffset":127340,"count":6},{"startOffset":127319,"endOffset":127339,"count":5},{"startOffset":127383,"endOffset":127449,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":126283,"endOffset":126435,"count":54},{"startOffset":126342,"endOffset":126417,"count":43}],"isBlockCoverage":true},{"functionName":"postaction_function","ranges":[{"startOffset":127461,"endOffset":127748,"count":327},{"startOffset":127673,"endOffset":127722,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":127771,"endOffset":129483,"count":5113},{"startOffset":127835,"endOffset":128220,"count":810},{"startOffset":128018,"endOffset":128147,"count":804},{"startOffset":128093,"endOffset":128133,"count":3},{"startOffset":128158,"endOffset":128214,"count":9},{"startOffset":128247,"endOffset":128560,"count":201},{"startOffset":128330,"endOffset":128415,"count":1},{"startOffset":128415,"endOffset":128544,"count":200},{"startOffset":128459,"endOffset":128544,"count":1},{"startOffset":128560,"endOffset":129481,"count":4912},{"startOffset":128588,"endOffset":128837,"count":221},{"startOffset":128639,"endOffset":128712,"count":1},{"startOffset":128760,"endOffset":128831,"count":1},{"startOffset":128837,"endOffset":129481,"count":4691},{"startOffset":128864,"endOffset":128884,"count":2828},{"startOffset":128886,"endOffset":128998,"count":1863},{"startOffset":128934,"endOffset":128992,"count":1},{"startOffset":128998,"endOffset":129481,"count":2828},{"startOffset":129026,"endOffset":129045,"count":2826},{"startOffset":129047,"endOffset":129481,"count":1248},{"startOffset":129162,"endOffset":129186,"count":30},{"startOffset":129199,"endOffset":129225,"count":1},{"startOffset":129238,"endOffset":129255,"count":1},{"startOffset":129266,"endOffset":129316,"count":1},{"startOffset":129392,"endOffset":129418,"count":22},{"startOffset":129429,"endOffset":129475,"count":15}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":129513,"endOffset":129799,"count":194},{"startOffset":129652,"endOffset":129692,"count":192},{"startOffset":129701,"endOffset":129741,"count":192},{"startOffset":129748,"endOffset":129797,"count":2}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":129829,"endOffset":130066,"count":185},{"startOffset":129968,"endOffset":130008,"count":184},{"startOffset":130015,"endOffset":130064,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":130144,"endOffset":133170,"count":1578},{"startOffset":130256,"endOffset":130319,"count":14},{"startOffset":130352,"endOffset":130442,"count":33},{"startOffset":130382,"endOffset":130436,"count":1},{"startOffset":130442,"endOffset":133168,"count":1545},{"startOffset":130469,"endOffset":131911,"count":1094},{"startOffset":130506,"endOffset":131437,"count":13},{"startOffset":130733,"endOffset":130795,"count":1},{"startOffset":130795,"endOffset":131427,"count":12},{"startOffset":130829,"endOffset":130962,"count":1},{"startOffset":130962,"endOffset":131427,"count":11},{"startOffset":130993,"endOffset":131201,"count":1},{"startOffset":131201,"endOffset":131427,"count":10},{"startOffset":131233,"endOffset":131427,"count":1},{"startOffset":131437,"endOffset":131905,"count":1081},{"startOffset":131549,"endOffset":131573,"count":37},{"startOffset":131590,"endOffset":131613,"count":31},{"startOffset":131630,"endOffset":131653,"count":24},{"startOffset":131670,"endOffset":131693,"count":1},{"startOffset":131708,"endOffset":131895,"count":1},{"startOffset":131911,"endOffset":133168,"count":451},{"startOffset":131938,"endOffset":133168,"count":450},{"startOffset":132024,"endOffset":132049,"count":5},{"startOffset":132051,"endOffset":132088,"count":1},{"startOffset":132137,"endOffset":132452,"count":2},{"startOffset":132178,"endOffset":132442,"count":1},{"startOffset":132493,"endOffset":133162,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":133199,"endOffset":133419,"count":221},{"startOffset":133263,"endOffset":133313,"count":1},{"startOffset":133353,"endOffset":133417,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":133591,"endOffset":133642,"count":5}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":133737,"endOffset":134189,"count":9},{"startOffset":133842,"endOffset":134023,"count":1},{"startOffset":134023,"endOffset":134139,"count":8}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":133869,"endOffset":134011,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":134267,"endOffset":134608,"count":7},{"startOffset":134321,"endOffset":134606,"count":6},{"startOffset":134402,"endOffset":134557,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":134677,"endOffset":135704,"count":41},{"startOffset":134800,"endOffset":134856,"count":38},{"startOffset":134863,"endOffset":134907,"count":3},{"startOffset":134907,"endOffset":135702,"count":38},{"startOffset":134972,"endOffset":135027,"count":1},{"startOffset":135027,"endOffset":135702,"count":37},{"startOffset":135092,"endOffset":135147,"count":1},{"startOffset":135147,"endOffset":135702,"count":36},{"startOffset":135208,"endOffset":135245,"count":1},{"startOffset":135252,"endOffset":135307,"count":1},{"startOffset":135307,"endOffset":135702,"count":35},{"startOffset":135369,"endOffset":135405,"count":1},{"startOffset":135412,"endOffset":135466,"count":1},{"startOffset":135466,"endOffset":135702,"count":34},{"startOffset":135530,"endOffset":135635,"count":31},{"startOffset":135642,"endOffset":135702,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":135727,"endOffset":136442,"count":583},{"startOffset":135772,"endOffset":135927,"count":14},{"startOffset":135875,"endOffset":135921,"count":8},{"startOffset":135927,"endOffset":136440,"count":569},{"startOffset":135955,"endOffset":136063,"count":83},{"startOffset":136005,"endOffset":136057,"count":1},{"startOffset":136063,"endOffset":136440,"count":486},{"startOffset":136092,"endOffset":136203,"count":1},{"startOffset":136203,"endOffset":136440,"count":485},{"startOffset":136247,"endOffset":136266,"count":330},{"startOffset":136275,"endOffset":136301,"count":285},{"startOffset":136310,"endOffset":136331,"count":85},{"startOffset":136338,"endOffset":136440,"count":70},{"startOffset":136388,"endOffset":136434,"count":26}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":135809,"endOffset":135872,"count":10}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":136524,"endOffset":136973,"count":4},{"startOffset":136694,"endOffset":136729,"count":0},{"startOffset":136731,"endOffset":136796,"count":0},{"startOffset":136838,"endOffset":136857,"count":1},{"startOffset":136866,"endOffset":136915,"count":1},{"startOffset":136887,"endOffset":136914,"count":0},{"startOffset":136922,"endOffset":136971,"count":3}],"isBlockCoverage":true},{"functionName":"delve","ranges":[{"startOffset":136977,"endOffset":137617,"count":313}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":137054,"endOffset":137613,"count":2059},{"startOffset":137099,"endOffset":137607,"count":2051},{"startOffset":137198,"endOffset":137597,"count":861},{"startOffset":137277,"endOffset":137409,"count":1},{"startOffset":137355,"endOffset":137387,"count":0},{"startOffset":137428,"endOffset":137491,"count":1},{"startOffset":137491,"endOffset":137583,"count":860},{"startOffset":137513,"endOffset":137583,"count":1}],"isBlockCoverage":true},{"functionName":"uninitialized_and_unused","ranges":[{"startOffset":137619,"endOffset":137928,"count":17},{"startOffset":137850,"endOffset":137864,"count":12},{"startOffset":137866,"endOffset":137896,"count":13}],"isBlockCoverage":true},{"functionName":"whitage","ranges":[{"startOffset":137993,"endOffset":148666,"count":17}],"isBlockCoverage":true},{"functionName":"pop","ranges":[{"startOffset":138203,"endOffset":138427,"count":3722}],"isBlockCoverage":true},{"functionName":"push","ranges":[{"startOffset":138433,"endOffset":138585,"count":3722}],"isBlockCoverage":true},{"functionName":"expected_at","ranges":[{"startOffset":138591,"endOffset":138788,"count":2}],"isBlockCoverage":true},{"functionName":"at_margin","ranges":[{"startOffset":138794,"endOffset":138937,"count":5437},{"startOffset":138884,"endOffset":138931,"count":1}],"isBlockCoverage":true},{"functionName":"no_space_only","ranges":[{"startOffset":138943,"endOffset":139361,"count":14314},{"startOffset":139184,"endOffset":139355,"count":2}],"isBlockCoverage":true},{"functionName":"no_space","ranges":[{"startOffset":139367,"endOffset":140132,"count":515},{"startOffset":139470,"endOffset":139498,"count":0},{"startOffset":139500,"endOffset":139699,"count":0},{"startOffset":139709,"endOffset":140126,"count":0}],"isBlockCoverage":true},{"functionName":"one_space_only","ranges":[{"startOffset":140138,"endOffset":140411,"count":1335},{"startOffset":140236,"endOffset":140405,"count":0}],"isBlockCoverage":true},{"functionName":"one_space","ranges":[{"startOffset":140417,"endOffset":140886,"count":7533},{"startOffset":140477,"endOffset":140485,"count":273},{"startOffset":140487,"endOffset":140771,"count":7260},{"startOffset":140534,"endOffset":140562,"count":6},{"startOffset":140564,"endOffset":140761,"count":6},{"startOffset":140771,"endOffset":140880,"count":273},{"startOffset":140818,"endOffset":140870,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":140923,"endOffset":148662,"count":29753},{"startOffset":141010,"endOffset":141033,"count":29257},{"startOffset":141035,"endOffset":141084,"count":513},{"startOffset":141084,"endOffset":148656,"count":29240},{"startOffset":141610,"endOffset":143262,"count":4151},{"startOffset":141657,"endOffset":142890,"count":3722},{"startOffset":141699,"endOffset":141728,"count":3720},{"startOffset":141832,"endOffset":142368,"count":1421},{"startOffset":141880,"endOffset":141892,"count":270},{"startOffset":142020,"endOffset":142177,"count":0},{"startOffset":142201,"endOffset":142271,"count":3},{"startOffset":142271,"endOffset":142346,"count":1418},{"startOffset":142368,"endOffset":142872,"count":2301},{"startOffset":142447,"endOffset":142733,"count":0},{"startOffset":142890,"endOffset":143248,"count":429},{"startOffset":143163,"endOffset":143230,"count":0},{"startOffset":143262,"endOffset":148402,"count":25089},{"startOffset":143316,"endOffset":143606,"count":1994},{"startOffset":143362,"endOffset":143427,"count":69},{"startOffset":143427,"endOffset":143532,"count":1925},{"startOffset":143606,"endOffset":148388,"count":23095},{"startOffset":143637,"endOffset":143868,"count":3722},{"startOffset":143698,"endOffset":143717,"count":1421},{"startOffset":143719,"endOffset":143780,"count":1421},{"startOffset":143780,"endOffset":143850,"count":2301},{"startOffset":143868,"endOffset":148388,"count":19373},{"startOffset":144167,"endOffset":144229,"count":11},{"startOffset":144229,"endOffset":148370,"count":19362},{"startOffset":144263,"endOffset":144404,"count":0},{"startOffset":144431,"endOffset":144836,"count":1532},{"startOffset":144467,"endOffset":144606,"count":768},{"startOffset":144553,"endOffset":144580,"count":332},{"startOffset":144608,"endOffset":144676,"count":939},{"startOffset":144676,"endOffset":144751,"count":593},{"startOffset":144836,"endOffset":148370,"count":17830},{"startOffset":144873,"endOffset":145086,"count":66},{"startOffset":144978,"endOffset":145064,"count":0},{"startOffset":145086,"endOffset":148370,"count":17764},{"startOffset":145170,"endOffset":145189,"count":5053},{"startOffset":145214,"endOffset":145221,"count":1560},{"startOffset":145244,"endOffset":145303,"count":86},{"startOffset":145303,"endOffset":148370,"count":17678},{"startOffset":145378,"endOffset":145397,"count":15825},{"startOffset":145422,"endOffset":145442,"count":15825},{"startOffset":145467,"endOffset":145486,"count":15821},{"startOffset":145511,"endOffset":145530,"count":14289},{"startOffset":145555,"endOffset":145574,"count":12026},{"startOffset":145599,"endOffset":145752,"count":11744},{"startOffset":145685,"endOffset":145726,"count":4967},{"startOffset":145706,"endOffset":145725,"count":3493},{"startOffset":145777,"endOffset":145916,"count":10056},{"startOffset":145865,"endOffset":145890,"count":300},{"startOffset":145939,"endOffset":146003,"count":7761},{"startOffset":146003,"endOffset":148370,"count":9917},{"startOffset":146030,"endOffset":146050,"count":8064},{"startOffset":146052,"endOffset":146116,"count":1853},{"startOffset":146116,"endOffset":148370,"count":8064},{"startOffset":146143,"endOffset":146270,"count":0},{"startOffset":146354,"endOffset":146375,"count":7998},{"startOffset":146400,"endOffset":146422,"count":7987},{"startOffset":146447,"endOffset":146468,"count":7982},{"startOffset":146493,"endOffset":146517,"count":7902},{"startOffset":146542,"endOffset":146564,"count":7894},{"startOffset":146589,"endOffset":146611,"count":7889},{"startOffset":146636,"endOffset":146659,"count":7869},{"startOffset":146684,"endOffset":146706,"count":7866},{"startOffset":146731,"endOffset":146756,"count":7720},{"startOffset":146781,"endOffset":146826,"count":7720},{"startOffset":146806,"endOffset":146825,"count":0},{"startOffset":146851,"endOffset":146891,"count":7720},{"startOffset":146871,"endOffset":146890,"count":1028},{"startOffset":146914,"endOffset":146979,"count":1266},{"startOffset":146979,"endOffset":148370,"count":6798},{"startOffset":147110,"endOffset":147139,"count":4674},{"startOffset":147164,"endOffset":147314,"count":2563},{"startOffset":147249,"endOffset":147288,"count":240},{"startOffset":147269,"endOffset":147287,"count":41},{"startOffset":147339,"endOffset":147492,"count":2335},{"startOffset":147425,"endOffset":147466,"count":240},{"startOffset":147446,"endOffset":147465,"count":41},{"startOffset":147517,"endOffset":147542,"count":2107},{"startOffset":147567,"endOffset":147585,"count":1807},{"startOffset":147610,"endOffset":148094,"count":1539},{"startOffset":147725,"endOffset":147750,"count":192},{"startOffset":147783,"endOffset":147808,"count":145},{"startOffset":147867,"endOffset":148068,"count":1394},{"startOffset":147953,"endOffset":147979,"count":715},{"startOffset":148012,"endOffset":148038,"count":701},{"startOffset":148119,"endOffset":148170,"count":846},{"startOffset":148150,"endOffset":148169,"count":642},{"startOffset":148193,"endOffset":148253,"count":6594},{"startOffset":148253,"endOffset":148370,"count":204},{"startOffset":148286,"endOffset":148304,"count":107},{"startOffset":148306,"endOffset":148370,"count":98}],"isBlockCoverage":true},{"functionName":"jslint","ranges":[{"startOffset":148700,"endOffset":153182,"count":211},{"startOffset":149153,"endOffset":149156,"count":12},{"startOffset":149169,"endOffset":149172,"count":199},{"startOffset":150297,"endOffset":150371,"count":9},{"startOffset":150371,"endOffset":151300,"count":182},{"startOffset":150571,"endOffset":150683,"count":11},{"startOffset":150616,"endOffset":150669,"count":0},{"startOffset":150683,"endOffset":150974,"count":171},{"startOffset":150866,"endOffset":150960,"count":0},{"startOffset":151145,"endOffset":151290,"count":17},{"startOffset":151300,"endOffset":151330,"count":160},{"startOffset":151330,"endOffset":151535,"count":148},{"startOffset":151535,"endOffset":151570,"count":160},{"startOffset":151570,"endOffset":151830,"count":51},{"startOffset":151610,"endOffset":151615,"count":20},{"startOffset":151670,"endOffset":151675,"count":40},{"startOffset":151783,"endOffset":151824,"count":0},{"startOffset":152954,"endOffset":152968,"count":12},{"startOffset":153055,"endOffset":153065,"count":1},{"startOffset":153078,"endOffset":153089,"count":210}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":149962,"endOffset":150226,"count":60},{"startOffset":150119,"endOffset":150202,"count":24}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":151363,"endOffset":151523,"count":12},{"startOffset":151436,"endOffset":151509,"count":3}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":151915,"endOffset":152149,"count":457},{"startOffset":152020,"endOffset":152038,"count":422},{"startOffset":152039,"endOffset":152061,"count":412}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":152155,"endOffset":152684,"count":490}],"isBlockCoverage":true},{"functionName":"cli","ranges":[{"startOffset":153184,"endOffset":157528,"count":1},{"startOffset":157388,"endOffset":157505,"count":0}],"isBlockCoverage":true},{"functionName":"string_line_count","ranges":[{"startOffset":153380,"endOffset":153797,"count":7},{"startOffset":153628,"endOffset":153771,"count":2161},{"startOffset":153701,"endOffset":153739,"count":7},{"startOffset":153739,"endOffset":153771,"count":2154}],"isBlockCoverage":true},{"functionName":"jslint_from_file","ranges":[{"startOffset":153802,"endOffset":156311,"count":19},{"startOffset":153992,"endOffset":154440,"count":4},{"startOffset":154449,"endOffset":154983,"count":2},{"startOffset":154992,"endOffset":155534,"count":1},{"startOffset":155543,"endOffset":155843,"count":12},{"startOffset":155853,"endOffset":155927,"count":12},{"startOffset":155927,"endOffset":156305,"count":0}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":154134,"endOffset":154418,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":154579,"endOffset":154961,"count":1}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":155136,"endOffset":155512,"count":6}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":156134,"endOffset":156268,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":156406,"endOffset":157379,"count":27},{"startOffset":156586,"endOffset":156599,"count":4},{"startOffset":156612,"endOffset":156623,"count":8},{"startOffset":156636,"endOffset":156649,"count":9},{"startOffset":156662,"endOffset":156673,"count":11},{"startOffset":156686,"endOffset":156698,"count":11},{"startOffset":156711,"endOffset":156745,"count":12},{"startOffset":156758,"endOffset":156790,"count":15},{"startOffset":156804,"endOffset":156890,"count":12},{"startOffset":156890,"endOffset":156944,"count":0},{"startOffset":156944,"endOffset":157085,"count":12},{"startOffset":157086,"endOffset":157110,"count":12},{"startOffset":157126,"endOffset":157165,"count":0},{"startOffset":157165,"endOffset":157378,"count":12}],"isBlockCoverage":true},{"functionName":"","ranges":[{"startOffset":157558,"endOffset":157773,"count":200},{"startOffset":157673,"endOffset":157715,"count":1},{"startOffset":157715,"endOffset":157772,"count":199}],"isBlockCoverage":true}]},{"scriptId":"76","url":"fs/promises.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":73,"count":1}],"isBlockCoverage":false}]},{"scriptId":"77","url":"net.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":47106,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":1448,"endOffset":1473,"count":0}],"isBlockCoverage":false},{"functionName":"noop","ranges":[{"startOffset":3388,"endOffset":3406,"count":0}],"isBlockCoverage":false},{"functionName":"getFlags","ranges":[{"startOffset":3408,"endOffset":3502,"count":0}],"isBlockCoverage":false},{"functionName":"createHandle","ranges":[{"startOffset":3504,"endOffset":3885,"count":1},{"startOffset":3671,"endOffset":3693,"count":0},{"startOffset":3727,"endOffset":3884,"count":0}],"isBlockCoverage":true},{"functionName":"getNewAsyncId","ranges":[{"startOffset":3888,"endOffset":4026,"count":1},{"startOffset":3983,"endOffset":4001,"count":0}],"isBlockCoverage":true},{"functionName":"isPipeName","ranges":[{"startOffset":4029,"endOffset":4112,"count":0}],"isBlockCoverage":false},{"functionName":"createServer","ranges":[{"startOffset":4114,"endOffset":4218,"count":0}],"isBlockCoverage":false},{"functionName":"connect","ranges":[{"startOffset":4441,"endOffset":4732,"count":0}],"isBlockCoverage":false},{"functionName":"normalizeArgs","ranges":[{"startOffset":5227,"endOffset":5953,"count":0}],"isBlockCoverage":false},{"functionName":"initSocketHandle","ranges":[{"startOffset":6025,"endOffset":6628,"count":1},{"startOffset":6381,"endOffset":6622,"count":0}],"isBlockCoverage":true},{"functionName":"Socket","ranges":[{"startOffset":6763,"endOffset":10585,"count":1},{"startOffset":6823,"endOffset":6850,"count":0},{"startOffset":7400,"endOffset":7426,"count":0},{"startOffset":7970,"endOffset":8078,"count":0},{"startOffset":8175,"endOffset":8254,"count":0},{"startOffset":8255,"endOffset":8303,"count":0},{"startOffset":8305,"endOffset":8536,"count":0},{"startOffset":9087,"endOffset":9121,"count":0},{"startOffset":9272,"endOffset":9801,"count":0},{"startOffset":10167,"endOffset":10432,"count":0}],"isBlockCoverage":true},{"functionName":"_unrefTimer","ranges":[{"startOffset":10758,"endOffset":10888,"count":12},{"startOffset":10860,"endOffset":10882,"count":0}],"isBlockCoverage":true},{"functionName":"Socket._final","ranges":[{"startOffset":11008,"endOffset":11656,"count":0}],"isBlockCoverage":false},{"functionName":"afterShutdown","ranges":[{"startOffset":11660,"endOffset":12039,"count":0}],"isBlockCoverage":false},{"functionName":"writeAfterFIN","ranges":[{"startOffset":12246,"endOffset":12702,"count":0}],"isBlockCoverage":false},{"functionName":"Socket._onTimeout","ranges":[{"startOffset":12784,"endOffset":13288,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.setNoDelay","ranges":[{"startOffset":13322,"endOffset":13771,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.setKeepAlive","ranges":[{"startOffset":13807,"endOffset":14054,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.address","ranges":[{"startOffset":14085,"endOffset":14129,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":14196,"endOffset":14240,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":14300,"endOffset":14356,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":14447,"endOffset":14762,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":14831,"endOffset":14911,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":14979,"endOffset":15024,"count":0}],"isBlockCoverage":false},{"functionName":"tryReadStart","ranges":[{"startOffset":15031,"endOffset":15277,"count":0}],"isBlockCoverage":false},{"functionName":"Socket._read","ranges":[{"startOffset":15369,"endOffset":15598,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.end","ranges":[{"startOffset":15625,"endOffset":15777,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.pause","ranges":[{"startOffset":15806,"endOffset":16140,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.resume","ranges":[{"startOffset":16170,"endOffset":16354,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.read","ranges":[{"startOffset":16382,"endOffset":16568,"count":0}],"isBlockCoverage":false},{"functionName":"onReadableStreamEnd","ranges":[{"startOffset":16615,"endOffset":16900,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.destroySoon","ranges":[{"startOffset":16934,"endOffset":17081,"count":0}],"isBlockCoverage":false},{"functionName":"Socket._destroy","ranges":[{"startOffset":17113,"endOffset":18067,"count":0}],"isBlockCoverage":false},{"functionName":"Socket._getpeername","ranges":[{"startOffset":18102,"endOffset":18393,"count":0}],"isBlockCoverage":false},{"functionName":"protoGetter","ranges":[{"startOffset":18396,"endOffset":18556,"count":8}],"isBlockCoverage":true},{"functionName":"bytesRead","ranges":[{"startOffset":18583,"endOffset":18674,"count":0}],"isBlockCoverage":false},{"functionName":"remoteAddress","ranges":[{"startOffset":18707,"endOffset":18773,"count":0}],"isBlockCoverage":false},{"functionName":"remoteFamily","ranges":[{"startOffset":18805,"endOffset":18869,"count":0}],"isBlockCoverage":false},{"functionName":"remotePort","ranges":[{"startOffset":18899,"endOffset":18959,"count":0}],"isBlockCoverage":false},{"functionName":"Socket._getsockname","ranges":[{"startOffset":18996,"endOffset":19281,"count":0}],"isBlockCoverage":false},{"functionName":"localAddress","ranges":[{"startOffset":19313,"endOffset":19378,"count":0}],"isBlockCoverage":false},{"functionName":"localPort","ranges":[{"startOffset":19408,"endOffset":19467,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.","ranges":[{"startOffset":19509,"endOffset":19556,"count":0}],"isBlockCoverage":false},{"functionName":"Socket._writeGeneric","ranges":[{"startOffset":19592,"endOffset":20353,"count":12},{"startOffset":19814,"endOffset":20007,"count":0},{"startOffset":20088,"endOffset":20144,"count":0},{"startOffset":20198,"endOffset":20234,"count":0},{"startOffset":20313,"endOffset":20351,"count":0}],"isBlockCoverage":true},{"functionName":"connect","ranges":[{"startOffset":19909,"endOffset":19989,"count":0}],"isBlockCoverage":false},{"functionName":"Socket._writev","ranges":[{"startOffset":20384,"endOffset":20452,"count":0}],"isBlockCoverage":false},{"functionName":"Socket._write","ranges":[{"startOffset":20482,"endOffset":20563,"count":12}],"isBlockCoverage":true},{"functionName":"_bytesDispatched","ranges":[{"startOffset":20756,"endOffset":20860,"count":0}],"isBlockCoverage":false},{"functionName":"bytesWritten","ranges":[{"startOffset":20892,"endOffset":21821,"count":0}],"isBlockCoverage":false},{"functionName":"checkBindError","ranges":[{"startOffset":21826,"endOffset":22625,"count":0}],"isBlockCoverage":false},{"functionName":"internalConnect","ranges":[{"startOffset":22628,"endOffset":24333,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.connect","ranges":[{"startOffset":24363,"endOffset":25667,"count":0}],"isBlockCoverage":false},{"functionName":"lookupAndConnect","ranges":[{"startOffset":25671,"endOffset":28698,"count":0}],"isBlockCoverage":false},{"functionName":"connectErrorNT","ranges":[{"startOffset":28701,"endOffset":28760,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.ref","ranges":[{"startOffset":28786,"endOffset":28973,"count":0}],"isBlockCoverage":false},{"functionName":"Socket.unref","ranges":[{"startOffset":29002,"endOffset":29195,"count":0}],"isBlockCoverage":false},{"functionName":"afterConnect","ranges":[{"startOffset":29199,"endOffset":30447,"count":0}],"isBlockCoverage":false},{"functionName":"Server","ranges":[{"startOffset":30450,"endOffset":31779,"count":0}],"isBlockCoverage":false},{"functionName":"toNumber","ranges":[{"startOffset":31890,"endOffset":31955,"count":0}],"isBlockCoverage":false},{"functionName":"createServerHandle","ranges":[{"startOffset":32023,"endOffset":33475,"count":0}],"isBlockCoverage":false},{"functionName":"setupListenHandle","ranges":[{"startOffset":33477,"endOffset":35828,"count":0}],"isBlockCoverage":false},{"functionName":"emitErrorNT","ranges":[{"startOffset":35895,"endOffset":35957,"count":0}],"isBlockCoverage":false},{"functionName":"emitListeningNT","ranges":[{"startOffset":35960,"endOffset":36075,"count":0}],"isBlockCoverage":false},{"functionName":"listenInCluster","ranges":[{"startOffset":36078,"endOffset":37296,"count":0}],"isBlockCoverage":false},{"functionName":"Server.listen","ranges":[{"startOffset":37325,"endOffset":40823,"count":0}],"isBlockCoverage":false},{"functionName":"lookupAndListen","ranges":[{"startOffset":40826,"endOffset":41238,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":41301,"endOffset":41344,"count":0}],"isBlockCoverage":false},{"functionName":"Server.address","ranges":[{"startOffset":41419,"endOffset":41708,"count":0}],"isBlockCoverage":false},{"functionName":"onconnection","ranges":[{"startOffset":41711,"endOffset":42372,"count":0}],"isBlockCoverage":false},{"functionName":"Server.getConnections","ranges":[{"startOffset":42409,"endOffset":43162,"count":0}],"isBlockCoverage":false},{"functionName":"Server.close","ranges":[{"startOffset":43191,"endOffset":44058,"count":0}],"isBlockCoverage":false},{"functionName":"Server._emitCloseIfDrained","ranges":[{"startOffset":44100,"endOffset":44486,"count":0}],"isBlockCoverage":false},{"functionName":"emitCloseNT","ranges":[{"startOffset":44490,"endOffset":44573,"count":0}],"isBlockCoverage":false},{"functionName":"Server.","ranges":[{"startOffset":44632,"endOffset":44794,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":44997,"endOffset":45033,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":45037,"endOffset":45078,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":45138,"endOffset":45169,"count":20}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":45173,"endOffset":45209,"count":1}],"isBlockCoverage":true},{"functionName":"Server._setupWorker","ranges":[{"startOffset":45247,"endOffset":45473,"count":0}],"isBlockCoverage":false},{"functionName":"Server.ref","ranges":[{"startOffset":45499,"endOffset":45597,"count":0}],"isBlockCoverage":false},{"functionName":"Server.unref","ranges":[{"startOffset":45625,"endOffset":45724,"count":0}],"isBlockCoverage":false},{"functionName":"_setSimultaneousAccepts","ranges":[{"startOffset":45866,"endOffset":46535,"count":0}],"isBlockCoverage":false},{"functionName":"_setSimultaneousAccepts","ranges":[{"startOffset":46574,"endOffset":46815,"count":0}],"isBlockCoverage":false}]},{"scriptId":"78","url":"stream.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2187,"count":1}],"isBlockCoverage":false},{"functionName":"_uint8ArrayToBuffer","ranges":[{"startOffset":1978,"endOffset":2185,"count":0}],"isBlockCoverage":false}]},{"scriptId":"79","url":"internal/streams/pipeline.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":7631,"count":1}],"isBlockCoverage":false},{"functionName":"destroyer","ranges":[{"startOffset":543,"endOffset":1935,"count":0}],"isBlockCoverage":false},{"functionName":"popCallback","ranges":[{"startOffset":1937,"endOffset":2308,"count":0}],"isBlockCoverage":false},{"functionName":"isReadable","ranges":[{"startOffset":2310,"endOffset":2390,"count":0}],"isBlockCoverage":false},{"functionName":"isWritable","ranges":[{"startOffset":2392,"endOffset":2473,"count":0}],"isBlockCoverage":false},{"functionName":"isStream","ranges":[{"startOffset":2475,"endOffset":2546,"count":0}],"isBlockCoverage":false},{"functionName":"isIterable","ranges":[{"startOffset":2548,"endOffset":2871,"count":0}],"isBlockCoverage":false},{"functionName":"makeAsyncIterable","ranges":[{"startOffset":2873,"endOffset":3149,"count":0}],"isBlockCoverage":false},{"functionName":"fromReadable","ranges":[{"startOffset":3151,"endOffset":3315,"count":0}],"isBlockCoverage":false},{"functionName":"pump","ranges":[{"startOffset":3317,"endOffset":3794,"count":0}],"isBlockCoverage":false},{"functionName":"pipeline","ranges":[{"startOffset":3796,"endOffset":7602,"count":0}],"isBlockCoverage":false}]},{"scriptId":"80","url":"internal/streams/destroy.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":3954,"count":1}],"isBlockCoverage":false},{"functionName":"destroy","ranges":[{"startOffset":123,"endOffset":1394,"count":0}],"isBlockCoverage":false},{"functionName":"emitErrorCloseNT","ranges":[{"startOffset":1396,"endOffset":1483,"count":0}],"isBlockCoverage":false},{"functionName":"emitCloseNT","ranges":[{"startOffset":1485,"endOffset":1703,"count":0}],"isBlockCoverage":false},{"functionName":"emitErrorNT","ranges":[{"startOffset":1705,"endOffset":1992,"count":0}],"isBlockCoverage":false},{"functionName":"undestroy","ranges":[{"startOffset":1994,"endOffset":2557,"count":1}],"isBlockCoverage":true},{"functionName":"errorOrDestroy","ranges":[{"startOffset":2559,"endOffset":3458,"count":0}],"isBlockCoverage":false},{"functionName":"isRequest","ranges":[{"startOffset":3460,"endOffset":3565,"count":0}],"isBlockCoverage":false},{"functionName":"destroyer","ranges":[{"startOffset":3600,"endOffset":3876,"count":0}],"isBlockCoverage":false}]},{"scriptId":"81","url":"internal/streams/end-of-stream.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":5791,"count":1}],"isBlockCoverage":false},{"functionName":"isRequest","ranges":[{"startOffset":280,"endOffset":375,"count":0}],"isBlockCoverage":false},{"functionName":"isReadable","ranges":[{"startOffset":377,"endOffset":535,"count":0}],"isBlockCoverage":false},{"functionName":"isWritable","ranges":[{"startOffset":537,"endOffset":695,"count":0}],"isBlockCoverage":false},{"functionName":"isWritableFinished","ranges":[{"startOffset":697,"endOffset":934,"count":0}],"isBlockCoverage":false},{"functionName":"nop","ranges":[{"startOffset":936,"endOffset":953,"count":0}],"isBlockCoverage":false},{"functionName":"isReadableEnded","ranges":[{"startOffset":955,"endOffset":1188,"count":0}],"isBlockCoverage":false},{"functionName":"eos","ranges":[{"startOffset":1190,"endOffset":5767,"count":0}],"isBlockCoverage":false}]},{"scriptId":"82","url":"internal/streams/legacy.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":2081,"count":1}],"isBlockCoverage":false},{"functionName":"Stream","ranges":[{"startOffset":96,"endOffset":144,"count":2}],"isBlockCoverage":true},{"functionName":"Stream.pipe","ranges":[{"startOffset":258,"endOffset":2053,"count":0}],"isBlockCoverage":false}]},{"scriptId":"83","url":"internal/streams/readable.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":40444,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":1596,"endOffset":1621,"count":0}],"isBlockCoverage":false},{"functionName":"nop","ranges":[{"startOffset":2218,"endOffset":2235,"count":0}],"isBlockCoverage":false},{"functionName":"prependListener","ranges":[{"startOffset":2278,"endOffset":3085,"count":0}],"isBlockCoverage":false},{"functionName":"ReadableState","ranges":[{"startOffset":3087,"endOffset":6664,"count":1},{"startOffset":3486,"endOffset":3529,"count":0},{"startOffset":4062,"endOffset":4098,"count":0},{"startOffset":6476,"endOffset":6662,"count":0}],"isBlockCoverage":true},{"functionName":"Readable","ranges":[{"startOffset":6667,"endOffset":7237,"count":1},{"startOffset":6735,"endOffset":6764,"count":0},{"startOffset":7087,"endOffset":7113,"count":0},{"startOffset":7168,"endOffset":7200,"count":0}],"isBlockCoverage":true},{"functionName":"Readable._destroy","ranges":[{"startOffset":7374,"endOffset":7406,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.","ranges":[{"startOffset":7457,"endOffset":7495,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.push","ranges":[{"startOffset":7724,"endOffset":7810,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.unshift","ranges":[{"startOffset":7906,"endOffset":7991,"count":0}],"isBlockCoverage":false},{"functionName":"readableAddChunk","ranges":[{"startOffset":7994,"endOffset":10247,"count":0}],"isBlockCoverage":false},{"functionName":"addChunk","ranges":[{"startOffset":10249,"endOffset":10949,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.isPaused","ranges":[{"startOffset":10981,"endOffset":11093,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.setEncoding","ranges":[{"startOffset":11157,"endOffset":11801,"count":0}],"isBlockCoverage":false},{"functionName":"computeNewHighWaterMark","ranges":[{"startOffset":11862,"endOffset":12227,"count":0}],"isBlockCoverage":false},{"functionName":"howMuchToRead","ranges":[{"startOffset":12340,"endOffset":12734,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.read","ranges":[{"startOffset":12831,"endOffset":17061,"count":0}],"isBlockCoverage":false},{"functionName":"onEofChunk","ranges":[{"startOffset":17064,"endOffset":17884,"count":0}],"isBlockCoverage":false},{"functionName":"emitReadable","ranges":[{"startOffset":18085,"endOffset":18412,"count":0}],"isBlockCoverage":false},{"functionName":"emitReadable_","ranges":[{"startOffset":18414,"endOffset":19050,"count":0}],"isBlockCoverage":false},{"functionName":"maybeReadMore","ranges":[{"startOffset":19400,"endOffset":19556,"count":0}],"isBlockCoverage":false},{"functionName":"maybeReadMore_","ranges":[{"startOffset":19558,"endOffset":21350,"count":0}],"isBlockCoverage":false},{"functionName":"Readable._read","ranges":[{"startOffset":21621,"endOffset":21687,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.pipe","ranges":[{"startOffset":21716,"endOffset":26193,"count":0}],"isBlockCoverage":false},{"functionName":"pipeOnDrain","ranges":[{"startOffset":26196,"endOffset":26870,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.unpipe","ranges":[{"startOffset":26901,"endOffset":27570,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.on","ranges":[{"startOffset":27696,"endOffset":28603,"count":14},{"startOffset":27828,"endOffset":28160,"count":0},{"startOffset":28189,"endOffset":28586,"count":0}],"isBlockCoverage":true},{"functionName":"Readable.removeListener","ranges":[{"startOffset":28698,"endOffset":29212,"count":12},{"startOffset":28809,"endOffset":29195,"count":0}],"isBlockCoverage":true},{"functionName":"Readable.removeAllListeners","ranges":[{"startOffset":29315,"endOffset":29853,"count":0}],"isBlockCoverage":false},{"functionName":"updateReadableListening","ranges":[{"startOffset":29856,"endOffset":30366,"count":0}],"isBlockCoverage":false},{"functionName":"nReadingNextTick","ranges":[{"startOffset":30368,"endOffset":30456,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.resume","ranges":[{"startOffset":30610,"endOffset":30935,"count":0}],"isBlockCoverage":false},{"functionName":"resume","ranges":[{"startOffset":30938,"endOffset":31088,"count":0}],"isBlockCoverage":false},{"functionName":"resume_","ranges":[{"startOffset":31090,"endOffset":31341,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.pause","ranges":[{"startOffset":31370,"endOffset":31637,"count":0}],"isBlockCoverage":false},{"functionName":"flow","ranges":[{"startOffset":31640,"endOffset":31787,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.wrap","ranges":[{"startOffset":31971,"endOffset":33786,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.","ranges":[{"startOffset":33831,"endOffset":34212,"count":0}],"isBlockCoverage":false},{"functionName":"createAsyncIterator","ranges":[{"startOffset":34215,"endOffset":35508,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":35706,"endOffset":36095,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":36101,"endOffset":36231,"count":1}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":36297,"endOffset":36363,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":36422,"endOffset":36504,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":36564,"endOffset":36624,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":36635,"endOffset":36744,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":36798,"endOffset":36852,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":36910,"endOffset":36998,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":37054,"endOffset":37139,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":37188,"endOffset":37322,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":37328,"endOffset":37617,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":37670,"endOffset":37758,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":37877,"endOffset":37922,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":37980,"endOffset":38031,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":38037,"endOffset":38086,"count":0}],"isBlockCoverage":false},{"functionName":"fromList","ranges":[{"startOffset":38390,"endOffset":38952,"count":0}],"isBlockCoverage":false},{"functionName":"endReadable","ranges":[{"startOffset":38954,"endOffset":39175,"count":0}],"isBlockCoverage":false},{"functionName":"endReadableNT","ranges":[{"startOffset":39177,"endOffset":40109,"count":0}],"isBlockCoverage":false},{"functionName":"endWritableNT","ranges":[{"startOffset":40111,"endOffset":40278,"count":0}],"isBlockCoverage":false},{"functionName":"Readable.from","ranges":[{"startOffset":40296,"endOffset":40442,"count":0}],"isBlockCoverage":false}]},{"scriptId":"84","url":"internal/streams/buffer_list.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":3798,"count":1}],"isBlockCoverage":false},{"functionName":"BufferList","ranges":[{"startOffset":204,"endOffset":288,"count":1}],"isBlockCoverage":true},{"functionName":"push","ranges":[{"startOffset":292,"endOffset":479,"count":0}],"isBlockCoverage":false},{"functionName":"unshift","ranges":[{"startOffset":483,"endOffset":641,"count":0}],"isBlockCoverage":false},{"functionName":"shift","ranges":[{"startOffset":645,"endOffset":872,"count":0}],"isBlockCoverage":false},{"functionName":"clear","ranges":[{"startOffset":876,"endOffset":944,"count":0}],"isBlockCoverage":false},{"functionName":"join","ranges":[{"startOffset":948,"endOffset":1119,"count":0}],"isBlockCoverage":false},{"functionName":"concat","ranges":[{"startOffset":1123,"endOffset":1386,"count":0}],"isBlockCoverage":false},{"functionName":"consume","ranges":[{"startOffset":1470,"endOffset":1924,"count":0}],"isBlockCoverage":false},{"functionName":"first","ranges":[{"startOffset":1928,"endOffset":1968,"count":0}],"isBlockCoverage":false},{"functionName":"module.exports","ranges":[{"startOffset":1972,"endOffset":2068,"count":0}],"isBlockCoverage":false},{"functionName":"_getString","ranges":[{"startOffset":2143,"endOffset":2738,"count":0}],"isBlockCoverage":false},{"functionName":"_getBuffer","ranges":[{"startOffset":2808,"endOffset":3518,"count":0}],"isBlockCoverage":false},{"functionName":"module.exports","ranges":[{"startOffset":3599,"endOffset":3794,"count":0}],"isBlockCoverage":false}]},{"scriptId":"85","url":"internal/streams/state.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":881,"count":1}],"isBlockCoverage":false},{"functionName":"highWaterMarkFrom","ranges":[{"startOffset":142,"endOffset":309,"count":2},{"startOffset":240,"endOffset":263,"count":0},{"startOffset":300,"endOffset":306,"count":0}],"isBlockCoverage":true},{"functionName":"getDefaultHighWaterMark","ranges":[{"startOffset":311,"endOffset":397,"count":2},{"startOffset":378,"endOffset":382,"count":0}],"isBlockCoverage":true},{"functionName":"getHighWaterMark","ranges":[{"startOffset":399,"endOffset":811,"count":2},{"startOffset":546,"endOffset":737,"count":0}],"isBlockCoverage":true}]},{"scriptId":"86","url":"internal/streams/writable.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":22800,"count":1}],"isBlockCoverage":false},{"functionName":"nop","ranges":[{"startOffset":2223,"endOffset":2240,"count":0}],"isBlockCoverage":false},{"functionName":"WritableState","ranges":[{"startOffset":2242,"endOffset":6406,"count":1},{"startOffset":2637,"endOffset":2680,"count":0},{"startOffset":3231,"endOffset":3267,"count":0}],"isBlockCoverage":true},{"functionName":"resetBuffer","ranges":[{"startOffset":6408,"endOffset":6540,"count":1}],"isBlockCoverage":true},{"functionName":"getBuffer","ranges":[{"startOffset":6578,"endOffset":6652,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":6729,"endOffset":6794,"count":0}],"isBlockCoverage":false},{"functionName":"value","ranges":[{"startOffset":7121,"endOffset":7335,"count":0}],"isBlockCoverage":false},{"functionName":"realHasInstance","ranges":[{"startOffset":7371,"endOffset":7428,"count":0}],"isBlockCoverage":false},{"functionName":"Writable","ranges":[{"startOffset":7433,"endOffset":8605,"count":1},{"startOffset":8074,"endOffset":8114,"count":0},{"startOffset":8120,"endOffset":8149,"count":0},{"startOffset":8288,"endOffset":8316,"count":0},{"startOffset":8370,"endOffset":8400,"count":0},{"startOffset":8455,"endOffset":8487,"count":0},{"startOffset":8540,"endOffset":8568,"count":0}],"isBlockCoverage":true},{"functionName":"Writable.pipe","ranges":[{"startOffset":8701,"endOffset":8769,"count":0}],"isBlockCoverage":false},{"functionName":"Writable.write","ranges":[{"startOffset":8799,"endOffset":10008,"count":12},{"startOffset":8969,"endOffset":9089,"count":0},{"startOffset":9113,"endOffset":9158,"count":0},{"startOffset":9267,"endOffset":9351,"count":0},{"startOffset":9357,"endOffset":9660,"count":0},{"startOffset":9697,"endOffset":9746,"count":0},{"startOffset":9773,"endOffset":9823,"count":0},{"startOffset":9836,"endOffset":9927,"count":0}],"isBlockCoverage":true},{"functionName":"Writable.cork","ranges":[{"startOffset":10037,"endOffset":10083,"count":0}],"isBlockCoverage":false},{"functionName":"Writable.uncork","ranges":[{"startOffset":10114,"endOffset":10269,"count":0}],"isBlockCoverage":false},{"functionName":"setDefaultEncoding","ranges":[{"startOffset":10312,"endOffset":10623,"count":0}],"isBlockCoverage":false},{"functionName":"writeOrBuffer","ranges":[{"startOffset":10813,"endOffset":11804,"count":12},{"startOffset":10911,"endOffset":10914,"count":0},{"startOffset":11133,"endOffset":11156,"count":0},{"startOffset":11212,"endOffset":11444,"count":0}],"isBlockCoverage":true},{"functionName":"doWrite","ranges":[{"startOffset":11806,"endOffset":12184,"count":0}],"isBlockCoverage":false},{"functionName":"onwriteError","ranges":[{"startOffset":12186,"endOffset":12606,"count":0}],"isBlockCoverage":false},{"functionName":"onwrite","ranges":[{"startOffset":12608,"endOffset":14202,"count":12},{"startOffset":12766,"endOffset":12840,"count":0},{"startOffset":12958,"endOffset":13469,"count":0},{"startOffset":13530,"endOffset":13571,"count":0},{"startOffset":13886,"endOffset":13933,"count":0},{"startOffset":13935,"endOffset":13986,"count":0},{"startOffset":14143,"endOffset":14196,"count":0}],"isBlockCoverage":true},{"functionName":"afterWriteTick","ranges":[{"startOffset":14204,"endOffset":14343,"count":12}],"isBlockCoverage":true},{"functionName":"afterWrite","ranges":[{"startOffset":14345,"endOffset":14755,"count":12},{"startOffset":14511,"endOffset":14571,"count":0},{"startOffset":14658,"endOffset":14722,"count":0}],"isBlockCoverage":true},{"functionName":"errorBuffer","ranges":[{"startOffset":14827,"endOffset":15148,"count":0}],"isBlockCoverage":false},{"functionName":"clearBuffer","ranges":[{"startOffset":15214,"endOffset":16647,"count":0}],"isBlockCoverage":false},{"functionName":"Writable._write","ranges":[{"startOffset":16677,"endOffset":16846,"count":0}],"isBlockCoverage":false},{"functionName":"Writable.end","ranges":[{"startOffset":16910,"endOffset":18094,"count":0}],"isBlockCoverage":false},{"functionName":"needFinish","ranges":[{"startOffset":18097,"endOffset":18310,"count":12},{"startOffset":18149,"endOffset":18180,"count":0},{"startOffset":18181,"endOffset":18208,"count":0},{"startOffset":18209,"endOffset":18249,"count":0},{"startOffset":18250,"endOffset":18278,"count":0},{"startOffset":18279,"endOffset":18306,"count":0}],"isBlockCoverage":true},{"functionName":"callFinal","ranges":[{"startOffset":18312,"endOffset":18572,"count":0}],"isBlockCoverage":false},{"functionName":"prefinish","ranges":[{"startOffset":18574,"endOffset":18922,"count":0}],"isBlockCoverage":false},{"functionName":"finishMaybe","ranges":[{"startOffset":18924,"endOffset":19251,"count":12},{"startOffset":19014,"endOffset":19234,"count":0}],"isBlockCoverage":true},{"functionName":"finish","ranges":[{"startOffset":19253,"endOffset":19871,"count":0}],"isBlockCoverage":false},{"functionName":"onFinished","ranges":[{"startOffset":19937,"endOffset":20401,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":20468,"endOffset":20555,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":20561,"endOffset":20743,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":20768,"endOffset":21160,"count":0}],"isBlockCoverage":false},{"functionName":"set","ranges":[{"startOffset":21166,"endOffset":21300,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":21333,"endOffset":21419,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":21454,"endOffset":21542,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":21573,"endOffset":21655,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":21685,"endOffset":21769,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":21803,"endOffset":21961,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":21999,"endOffset":22083,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":22114,"endOffset":22194,"count":0}],"isBlockCoverage":false},{"functionName":"get","ranges":[{"startOffset":22225,"endOffset":22302,"count":0}],"isBlockCoverage":false},{"functionName":"Writable.destroy","ranges":[{"startOffset":22378,"endOffset":22589,"count":0}],"isBlockCoverage":false},{"functionName":"Writable._destroy","ranges":[{"startOffset":22677,"endOffset":22709,"count":0}],"isBlockCoverage":false},{"functionName":"Writable.","ranges":[{"startOffset":22760,"endOffset":22798,"count":0}],"isBlockCoverage":false}]},{"scriptId":"87","url":"internal/streams/duplex.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":3759,"count":1}],"isBlockCoverage":false},{"functionName":"Duplex","ranges":[{"startOffset":1936,"endOffset":2360,"count":1},{"startOffset":2000,"endOffset":2027,"count":0},{"startOffset":2248,"endOffset":2270,"count":0},{"startOffset":2313,"endOffset":2354,"count":0}],"isBlockCoverage":true},{"functionName":"get","ranges":[{"startOffset":3271,"endOffset":3483,"count":12},{"startOffset":3369,"endOffset":3400,"count":0},{"startOffset":3444,"endOffset":3476,"count":0}],"isBlockCoverage":true},{"functionName":"set","ranges":[{"startOffset":3489,"endOffset":3750,"count":0}],"isBlockCoverage":false}]},{"scriptId":"88","url":"internal/streams/transform.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":8217,"count":1}],"isBlockCoverage":false},{"functionName":"afterTransform","ranges":[{"startOffset":4032,"endOffset":4550,"count":0}],"isBlockCoverage":false},{"functionName":"Transform","ranges":[{"startOffset":4553,"endOffset":5382,"count":0}],"isBlockCoverage":false},{"functionName":"prefinish","ranges":[{"startOffset":5384,"endOffset":5596,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":5691,"endOffset":5741,"count":0}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":5838,"endOffset":5890,"count":0}],"isBlockCoverage":false},{"functionName":"Transform.push","ranges":[{"startOffset":5988,"endOffset":6124,"count":0}],"isBlockCoverage":false},{"functionName":"Transform._transform","ranges":[{"startOffset":6607,"endOffset":6696,"count":0}],"isBlockCoverage":false},{"functionName":"Transform._write","ranges":[{"startOffset":6728,"endOffset":7067,"count":0}],"isBlockCoverage":false},{"functionName":"Transform._read","ranges":[{"startOffset":7239,"endOffset":7613,"count":0}],"isBlockCoverage":false},{"functionName":"Transform._destroy","ranges":[{"startOffset":7648,"endOffset":7745,"count":0}],"isBlockCoverage":false},{"functionName":"done","ranges":[{"startOffset":7749,"endOffset":8216,"count":0}],"isBlockCoverage":false}]},{"scriptId":"89","url":"internal/streams/passthrough.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1762,"count":1}],"isBlockCoverage":false},{"functionName":"PassThrough","ranges":[{"startOffset":1529,"endOffset":1671,"count":0}],"isBlockCoverage":false},{"functionName":"PassThrough._transform","ranges":[{"startOffset":1708,"endOffset":1760,"count":0}],"isBlockCoverage":false}]},{"scriptId":"90","url":"internal/net.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":1694,"count":1}],"isBlockCoverage":false},{"functionName":"isIPv4","ranges":[{"startOffset":974,"endOffset":1022,"count":0}],"isBlockCoverage":false},{"functionName":"isIPv6","ranges":[{"startOffset":1024,"endOffset":1072,"count":0}],"isBlockCoverage":false},{"functionName":"isIP","ranges":[{"startOffset":1074,"endOffset":1160,"count":0}],"isBlockCoverage":false},{"functionName":"makeSyncWrite","ranges":[{"startOffset":1162,"endOffset":1576,"count":0}],"isBlockCoverage":false}]},{"scriptId":"91","url":"internal/stream_base_commons.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":7120,"count":1}],"isBlockCoverage":false},{"functionName":"","ranges":[{"startOffset":987,"endOffset":1012,"count":0}],"isBlockCoverage":false},{"functionName":"handleWriteReq","ranges":[{"startOffset":1131,"endOffset":1986,"count":12},{"startOffset":1231,"endOffset":1398,"count":0},{"startOffset":1403,"endOffset":1417,"count":0},{"startOffset":1422,"endOffset":1486,"count":0},{"startOffset":1574,"endOffset":1636,"count":0},{"startOffset":1641,"endOffset":1653,"count":0},{"startOffset":1658,"endOffset":1671,"count":0},{"startOffset":1676,"endOffset":1691,"count":0},{"startOffset":1696,"endOffset":1760,"count":0},{"startOffset":1765,"endOffset":1980,"count":0}],"isBlockCoverage":true},{"functionName":"onWriteComplete","ranges":[{"startOffset":1988,"endOffset":2498,"count":0}],"isBlockCoverage":false},{"functionName":"createWriteWrap","ranges":[{"startOffset":2500,"endOffset":2701,"count":12}],"isBlockCoverage":true},{"functionName":"writevGeneric","ranges":[{"startOffset":2703,"endOffset":3344,"count":0}],"isBlockCoverage":false},{"functionName":"writeGeneric","ranges":[{"startOffset":3346,"endOffset":3553,"count":12}],"isBlockCoverage":true},{"functionName":"afterWriteDispatched","ranges":[{"startOffset":3555,"endOffset":3864,"count":12},{"startOffset":3728,"endOffset":3793,"count":0},{"startOffset":3828,"endOffset":3862,"count":0}],"isBlockCoverage":true},{"functionName":"onStreamRead","ranges":[{"startOffset":3866,"endOffset":5991,"count":0}],"isBlockCoverage":false},{"functionName":"setStreamTimeout","ranges":[{"startOffset":5993,"endOffset":6895,"count":0}],"isBlockCoverage":false}]},{"scriptId":"92","url":"internal/dtrace.js","functions":[{"functionName":"","ranges":[{"startOffset":0,"endOffset":568,"count":1}],"isBlockCoverage":false},{"functionName":"DTRACE_HTTP_CLIENT_REQUEST","ranges":[{"startOffset":97,"endOffset":105,"count":0}],"isBlockCoverage":false},{"functionName":"DTRACE_HTTP_CLIENT_RESPONSE","ranges":[{"startOffset":139,"endOffset":147,"count":0}],"isBlockCoverage":false},{"functionName":"DTRACE_HTTP_SERVER_REQUEST","ranges":[{"startOffset":180,"endOffset":188,"count":0}],"isBlockCoverage":false},{"functionName":"DTRACE_HTTP_SERVER_RESPONSE","ranges":[{"startOffset":222,"endOffset":230,"count":0}],"isBlockCoverage":false},{"functionName":"DTRACE_NET_SERVER_CONNECTION","ranges":[{"startOffset":265,"endOffset":273,"count":0}],"isBlockCoverage":false},{"functionName":"DTRACE_NET_STREAM_END","ranges":[{"startOffset":301,"endOffset":309,"count":0}],"isBlockCoverage":false}]}],"timestamp":149.747094} \ No newline at end of file diff --git a/branch.master/.build/coverage/index.html b/branch.master/.build/coverage/index.html new file mode 100644 index 000000000..88169d967 --- /dev/null +++ b/branch.master/.build/coverage/index.html @@ -0,0 +1,149 @@ + + + +coverage-report + + + +
    +
    coverage-report
    + + + + + + + + + + + + + + + + + + +
    +
    + + diff --git a/branch.master/.build/coverage/jslint.js.html b/branch.master/.build/coverage/jslint.js.html new file mode 100644 index 000000000..e8b75702e --- /dev/null +++ b/branch.master/.build/coverage/jslint.js.html @@ -0,0 +1,5396 @@ + + + +coverage-report + + + +
    +
    coverage-report
    +
    files coveredlines
    + ./
    +
    +
    +
    +
    + 93.42 %
    + 5286 / 5658 +
    + ./ jslint.js
    +
    +
    +
    +
    + 93.04 %
    + 4899 / 5265 +
    + ./ test.js
    +
    +
    +
    +
    + 98.47 %
    + 387 / 393 +
    + + + + + + + + + + +
    files coveredlines
    + ./ jslint.js
    +
    +
    +
    +
    + 93.04 %
    + 4899 / 5265 +
    +
    +
    +
        1.      1#!/usr/bin/env node
    +
        2.      1// jslint.js
    +
        3.      1// v2021.5.23
    +
        4.      1// Copyright (c) 2015 Douglas Crockford  (www.JSLint.com)
    +
        5.      1
    +
        6.      1// Permission is hereby granted, free of charge, to any person obtaining a copy
    +
        7.      1// of this software and associated documentation files (the "Software"), to deal
    +
        8.      1// in the Software without restriction, including without limitation the rights
    +
        9.      1// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +
       10.      1// copies of the Software, and to permit persons to whom the Software is
    +
       11.      1// furnished to do so, subject to the following conditions:
    +
       12.      1
    +
       13.      1// The above copyright notice and this permission notice shall be included in
    +
       14.      1// all copies or substantial portions of the Software.
    +
       15.      1
    +
       16.      1// The Software shall be used for Good, not Evil.
    +
       17.      1
    +
       18.      1// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +
       19.      1// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +
       20.      1// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    +
       21.      1// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +
       22.      1// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +
       23.      1// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    +
       24.      1// SOFTWARE.
    +
       25.      1
    +
       26.      1// jslint(source, option_object, global_array) is a function that takes 3
    +
       27.      1// arguments. The second two arguments are optional.
    +
       28.      1
    +
       29.      1//      source          A text to analyze, a string or an array of strings.
    +
       30.      1//      option_object   An object whose keys correspond to option names.
    +
       31.      1//      global_array    An array of strings containing global variables that
    +
       32.      1//                      the file is allowed readonly access.
    +
       33.      1
    +
       34.      1// jslint returns an object containing its results. The object contains a lot
    +
       35.      1// of valuable information. It can be used to generate reports. The object
    +
       36.      1// contains:
    +
       37.      1
    +
       38.      1//      directives: an array of directive comment tokens.
    +
       39.      1//      edition: the version of JSLint that did the analysis.
    +
       40.      1//      exports: the names exported from the module.
    +
       41.      1//      froms: an array of strings representing each of the imports.
    +
       42.      1//      functions: an array of objects that represent all of the functions
    +
       43.      1//              declared in the file.
    +
       44.      1//      global: an object representing the global object. Its .context property
    +
       45.      1//              is an object containing a property for each global variable.
    +
       46.      1//      id: "(JSLint)"
    +
       47.      1//      json: true if the file is a JSON text.
    +
       48.      1//      lines: an array of strings, the source.
    +
       49.      1//      module: true if an import or export statement was used.
    +
       50.      1//      ok: true if no warnings were generated. This is what you want.
    +
       51.      1//      option: the option argument.
    +
       52.      1//      property: a property object.
    +
       53.      1//      stop: true if JSLint was unable to finish. You don't want this.
    +
       54.      1//      tokens: an array of objects representing the tokens in the file.
    +
       55.      1//      tree: the token objects arranged in a tree.
    +
       56.      1//      warnings: an array of warning objects. A warning object can contain:
    +
       57.      1//          name: "JSLintError"
    +
       58.      1//          column: A column number in the file.
    +
       59.      1//          line: A line number in the file.
    +
       60.      1//          code: A warning code string.
    +
       61.      1//          message: The warning message string.
    +
       62.      1//          a: Exhibit A.
    +
       63.      1//          b: Exhibit B.
    +
       64.      1//          c: Exhibit C.
    +
       65.      1//          d: Exhibit D.
    +
       66.      1
    +
       67.      1// jslint works in several phases. In any of these phases, errors might be
    +
       68.      1// found. Sometimes JSLint is able to recover from an error and continue
    +
       69.      1// parsing. In some cases, it cannot and will stop early. If that should happen,
    +
       70.      1// repair your code and try again.
    +
       71.      1
    +
       72.      1// Phases:
    +
       73.      1
    +
       74.      1//      1. If the source is a single string, split it into an array of strings.
    +
       75.      1//      2. Turn the source into an array of tokens.
    +
       76.      1//      3. Furcate the tokens into a parse tree.
    +
       77.      1//      4. Walk the tree, traversing all of the nodes of the tree. It is a
    +
       78.      1//          recursive traversal. Each node may be processed on the way down
    +
       79.      1//          (preaction) and on the way up (postaction).
    +
       80.      1//      5. Check the whitespace between the tokens.
    +
       81.      1
    +
       82.      1// jslint can also examine JSON text. It decides that a file is JSON text if
    +
       83.      1// the first token is "[" or "{". Processing of JSON text is much simpler than
    +
       84.      1// the processing of JavaScript programs. Only the first three phases are
    +
       85.      1// required.
    +
       86.      1
    +
       87.      1// WARNING: JSLint will hurt your feelings.
    +
       88.      1
    +
       89.      1/*property
    +
       90.      1    a, all, and, argv, arity, assign, b, bad_assignment_a, bad_directive_a,
    +
       91.      1    bad_get, bad_module_name_a, bad_option_a, bad_property_a, bad_set, bitwise,
    +
       92.      1    block, body, browser, c, calls, catch, cli_mode, closer, closure, code,
    +
       93.      1    column, concat, constant, context, convert, couch, create, d, dead, debug,
    +
       94.      1    default, devel, directive, directives, disrupt, dot, duplicate_a,
    +
       95.      1    early_stop, edition, ellipsis, else, empty_block, error, eval, every, exec,
    +
       96.      1    exit, expected_a, expected_a_at_b_c, expected_a_b, expected_a_b_from_c_d,
    +
       97.      1    expected_a_before_b, expected_a_next_at_b, expected_digits_after_a,
    +
       98.      1    expected_four_digits, expected_identifier_a, expected_line_break_a_b,
    +
       99.      1    expected_regexp_factor_a, expected_space_a_b, expected_statements_a,
    +
      100.      1    expected_string_a, expected_type_string_a, exports, expression, extra, file,
    +
      101.      1    finally, flag, for, forEach, formatted_message, free, freeze,
    +
      102.      1    freeze_exports, from, froms, fud, fudge, function_in_loop, functions, g,
    +
      103.      1    getset, global, has_await, i, id, identifier, import, inc, indexOf,
    +
      104.      1    infix_in, init, initial, isArray, isNaN, is_async, join, json, keys, label,
    +
      105.      1    label_a, lbp, led, length, level, line, line_offset, lines, live, long,
    +
      106.      1    loop, m, map, margin, match, message, misplaced_a, misplaced_directive_a,
    +
      107.      1    missing_await_statement, missing_browser, missing_m, module, naked_block,
    +
      108.      1    name, names, nested_comment, new, node, not_label_a, now, nr, nud,
    +
      109.      1    number_isNaN, ok, open, opening, option, out_of_scope_a, padStart,
    +
      110.      1    parameters, parent, pop, property, push, quote, raw, readFile, readdir,
    +
      111.      1    redefinition_a_b, repeat, replace, required_a_optional_b, reserved_a, role,
    +
      112.      1    search, shebang, signature, single, slice, some, sort, split, stack,
    +
      113.      1    stack_trace, startsWith, statement, stop, subscript_a, switch, test, then,
    +
      114.      1    this, thru, todo_comment, tokens, too_long, too_many_digits, tree, trim,
    +
      115.      1    try, type, u, unclosed_comment, unclosed_mega, unclosed_string,
    +
      116.      1    undeclared_a, unexpected_a, unexpected_a_after_b, unexpected_a_before_b,
    +
      117.      1    unexpected_at_top_level_a, unexpected_char_a, unexpected_comment,
    +
      118.      1    unexpected_directive_a, unexpected_expression_a, unexpected_label_a,
    +
      119.      1    unexpected_parens, unexpected_space_a_b, unexpected_statement_a,
    +
      120.      1    unexpected_trailing_space, unexpected_typeof_a, uninitialized_a,
    +
      121.      1    unreachable_a, unregistered_property_a, unused_a, use_double, use_open,
    +
      122.      1    use_spaces, used, value, var_loop, var_switch, variable, versions, warning,
    +
      123.      1    warnings, weird_condition_a, weird_expression_a, weird_loop,
    +
      124.      1    weird_relation_a, white, wrap_condition, wrap_immediate, wrap_parameter,
    +
      125.      1    wrap_regexp, wrap_unary, wrapped, writable, y
    +
      126.      1*/
    +
      127.      1
    +
      128.   1980function empty() {
    +
      129.   1980
    +
      130.   1980// The empty function produces a new empty object that inherits nothing. This is
    +
      131.   1980// much better than '{}' because confusions around accidental method names like
    +
      132.   1980// 'constructor' are completely avoided.
    +
      133.   1980
    +
      134.   1980    return Object.create(null);
    +
      135.   1980}
    +
      136.      1
    +
      137.    454function populate(array, object = empty(), value = true) {
    +
      138.    454
    +
      139.    454// Augment an object by taking property names from an array of strings.
    +
      140.    454
    +
      141.  10475    array.forEach(function (name) {
    +
      142.  10475        object[name] = value;
    +
      143.  10475    });
    +
      144.    454    return object;
    +
      145.    454}
    +
      146.      1
    +
      147.      1const allowed_option = {
    +
      148.      1
    +
      149.      1// These are the options that are recognized in the option object or that may
    +
      150.      1// appear in a /*jslint*/ directive. Most options will have a boolean value,
    +
      151.      1// usually true. Some options will also predefine some number of global
    +
      152.      1// variables.
    +
      153.      1
    +
      154.      1    bitwise: true,
    +
      155.      1    browser: [
    +
      156.      1        "caches", "CharacterData", "clearInterval", "clearTimeout", "document",
    +
      157.      1        "DocumentType", "DOMException", "Element", "Event", "event", "fetch",
    +
      158.      1        "FileReader", "FontFace", "FormData", "history", "IntersectionObserver",
    +
      159.      1        "localStorage", "location", "MutationObserver", "name", "navigator",
    +
      160.      1        "screen", "sessionStorage", "setInterval", "setTimeout", "Storage",
    +
      161.      1        "TextDecoder", "TextEncoder", "URL", "window", "Worker",
    +
      162.      1        "XMLHttpRequest"
    +
      163.      1    ],
    +
      164.      1    couch: [
    +
      165.      1        "emit", "getRow", "isArray", "log", "provides", "registerType",
    +
      166.      1        "require", "send", "start", "sum", "toJSON"
    +
      167.      1    ],
    +
      168.      1    convert: true,
    +
      169.      1    debug: true,
    +
      170.      1    devel: [
    +
      171.      1        "alert", "confirm", "console", "prompt"
    +
      172.      1    ],
    +
      173.      1    eval: true,
    +
      174.      1    for: true,
    +
      175.      1    fudge: true,
    +
      176.      1    getset: true,
    +
      177.      1    long: true,
    +
      178.      1    node: [
    +
      179.      1        "Buffer", "clearImmediate", "clearInterval", "clearTimeout",
    +
      180.      1        "console", "exports", "module", "process", "require",
    +
      181.      1        "setImmediate", "setInterval", "setTimeout", "TextDecoder",
    +
      182.      1        "TextEncoder", "URL", "URLSearchParams", "__dirname", "__filename"
    +
      183.      1    ],
    +
      184.      1    single: true,
    +
      185.      1    this: true,
    +
      186.      1    white: true
    +
      187.      1};
    +
      188.      1
    +
      189.      1const anticondition = populate([
    +
      190.      1    "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%",
    +
      191.      1    "typeof", "(number)", "(string)"
    +
      192.      1]);
    +
      193.      1
    +
      194.      1// These are the bitwise operators.
    +
      195.      1
    +
      196.      1const bitwiseop = populate([
    +
      197.      1    "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=",
    +
      198.      1    ">>>", ">>>="
    +
      199.      1]);
    +
      200.      1
    +
      201.      1const escapeable = populate([
    +
      202.      1    "\\", "/", "`", "b", "f", "n", "r", "t"
    +
      203.      1]);
    +
      204.      1
    +
      205.      1const opener = {
    +
      206.      1
    +
      207.      1// The open and close pairs.
    +
      208.      1
    +
      209.      1    "(": ")",       // paren
    +
      210.      1    "[": "]",       // bracket
    +
      211.      1    "{": "}",       // brace
    +
      212.      1    "${": "}"       // mega
    +
      213.      1};
    +
      214.      1
    +
      215.      1// The relational operators.
    +
      216.      1
    +
      217.      1const relationop = populate([
    +
      218.      1    "!=", "!==", "==", "===", "<", "<=", ">", ">="
    +
      219.      1]);
    +
      220.      1
    +
      221.      1// This is the set of infix operators that require a space on each side.
    +
      222.      1
    +
      223.      1const spaceop = populate([
    +
      224.      1    "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/",
    +
      225.      1    "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=",
    +
      226.      1    ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||"
    +
      227.      1]);
    +
      228.      1
    +
      229.      1const standard = [
    +
      230.      1
    +
      231.      1// These are the globals that are provided by the language standard.
    +
      232.      1
    +
      233.      1    "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI",
    +
      234.      1    "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error",
    +
      235.      1    "EvalError", "Float32Array", "Float64Array", "Generator",
    +
      236.      1    "GeneratorFunction", "import", "Int8Array", "Int16Array", "Int32Array",
    +
      237.      1    "Intl", "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat",
    +
      238.      1    "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp",
    +
      239.      1    "Set", "String", "Symbol", "SyntaxError", "System", "TypeError",
    +
      240.      1    "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array",
    +
      241.      1    "URIError", "WeakMap", "WeakSet"
    +
      242.      1];
    +
      243.      1
    +
      244.      1const bundle = {
    +
      245.      1
    +
      246.      1// The bundle contains the raw text messages that are generated by jslint. It
    +
      247.      1// seems that they are all error messages and warnings. There are no "Atta
    +
      248.      1// boy!" or "You are so awesome!" messages. There is no positive reinforcement
    +
      249.      1// or encouragement. This relentless negativity can undermine self-esteem and
    +
      250.      1// wound the inner child. But if you accept it as sound advice rather than as
    +
      251.      1// personal criticism, it can make your programs better.
    +
      252.      1
    +
      253.      1    and: "The '&&' subexpression should be wrapped in parens.",
    +
      254.      1    bad_assignment_a: "Bad assignment to '{a}'.",
    +
      255.      1    bad_directive_a: "Bad directive '{a}'.",
    +
      256.      1    bad_get: "A get function takes no parameters.",
    +
      257.      1    bad_module_name_a: "Bad module name '{a}'.",
    +
      258.      1    bad_option_a: "Bad option '{a}'.",
    +
      259.      1    bad_property_a: "Bad property name '{a}'.",
    +
      260.      1    bad_set: "A set function takes one parameter.",
    +
      261.      1    duplicate_a: "Duplicate '{a}'.",
    +
      262.      1    empty_block: "Empty block.",
    +
      263.      1    expected_a: "Expected '{a}'.",
    +
      264.      1    expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.",
    +
      265.      1    expected_a_b: "Expected '{a}' and instead saw '{b}'.",
    +
      266.      1    expected_a_b_from_c_d: (
    +
      267.      1        "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'."
    +
      268.      1    ),
    +
      269.      1    expected_a_before_b: "Expected '{a}' before '{b}'.",
    +
      270.      1    expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.",
    +
      271.      1    expected_digits_after_a: "Expected digits after '{a}'.",
    +
      272.      1    expected_four_digits: "Expected four digits after '\\u'.",
    +
      273.      1    expected_identifier_a: "Expected an identifier and instead saw '{a}'.",
    +
      274.      1    expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.",
    +
      275.      1    expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.",
    +
      276.      1    expected_space_a_b: "Expected one space between '{a}' and '{b}'.",
    +
      277.      1    expected_statements_a: "Expected statements before '{a}'.",
    +
      278.      1    expected_string_a: "Expected a string and instead saw '{a}'.",
    +
      279.      1    expected_type_string_a: "Expected a type string and instead saw '{a}'.",
    +
      280.      1    freeze_exports: (
    +
      281.      1        "Expected 'Object.freeze('. All export values should be frozen."
    +
      282.      1    ),
    +
      283.      1    function_in_loop: "Don't make functions within a loop.",
    +
      284.      1    infix_in: (
    +
      285.      1        "Unexpected 'in'. Compare with undefined, "
    +
      286.      1        + "or use the hasOwnProperty method instead."
    +
      287.      1    ),
    +
      288.      1    label_a: "'{a}' is a statement label.",
    +
      289.      1    misplaced_a: "Place '{a}' at the outermost level.",
    +
      290.      1    misplaced_directive_a: (
    +
      291.      1        "Place the '/*{a}*/' directive before the first statement."
    +
      292.      1    ),
    +
      293.      1    missing_await_statement: "Expected await statement in async function.",
    +
      294.      1    missing_browser: "/*global*/ requires the Assume a browser option.",
    +
      295.      1    missing_m: "Expected 'm' flag on a multiline regular expression.",
    +
      296.      1    naked_block: "Naked block.",
    +
      297.      1    nested_comment: "Nested comment.",
    +
      298.      1    not_label_a: "'{a}' is not a label.",
    +
      299.      1    number_isNaN: "Use Number.isNaN function to compare with NaN.",
    +
      300.      1    out_of_scope_a: "'{a}' is out of scope.",
    +
      301.      1    redefinition_a_b: "Redefinition of '{a}' from line {b}.",
    +
      302.      1    required_a_optional_b: (
    +
      303.      1        "Required parameter '{a}' after optional parameter '{b}'."
    +
      304.      1    ),
    +
      305.      1    reserved_a: "Reserved name '{a}'.",
    +
      306.      1    subscript_a: "['{a}'] is better written in dot notation.",
    +
      307.      1    todo_comment: "Unexpected TODO comment.",
    +
      308.      1    too_long: "Line is longer than 80 characters.",
    +
      309.      1    too_many_digits: "Too many digits.",
    +
      310.      1    unclosed_comment: "Unclosed comment.",
    +
      311.      1    unclosed_mega: "Unclosed mega literal.",
    +
      312.      1    unclosed_string: "Unclosed string.",
    +
      313.      1    undeclared_a: "Undeclared '{a}'.",
    +
      314.      1    unexpected_a: "Unexpected '{a}'.",
    +
      315.      1    unexpected_a_after_b: "Unexpected '{a}' after '{b}'.",
    +
      316.      1    unexpected_a_before_b: "Unexpected '{a}' before '{b}'.",
    +
      317.      1    unexpected_at_top_level_a: "Expected '{a}' to be in a function.",
    +
      318.      1    unexpected_char_a: "Unexpected character '{a}'.",
    +
      319.      1    unexpected_comment: "Unexpected comment.",
    +
      320.      1    unexpected_directive_a: "When using modules, don't use directive '/*{a}'.",
    +
      321.      1    unexpected_expression_a: (
    +
      322.      1        "Unexpected expression '{a}' in statement position."
    +
      323.      1    ),
    +
      324.      1    unexpected_label_a: "Unexpected label '{a}'.",
    +
      325.      1    unexpected_parens: "Don't wrap function literals in parens.",
    +
      326.      1    unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.",
    +
      327.      1    unexpected_statement_a: (
    +
      328.      1        "Unexpected statement '{a}' in expression position."
    +
      329.      1    ),
    +
      330.      1    unexpected_trailing_space: "Unexpected trailing space.",
    +
      331.      1    unexpected_typeof_a: (
    +
      332.      1        "Unexpected 'typeof'. Use '===' to compare directly with {a}."
    +
      333.      1    ),
    +
      334.      1    uninitialized_a: "Uninitialized '{a}'.",
    +
      335.      1    unreachable_a: "Unreachable '{a}'.",
    +
      336.      1    unregistered_property_a: "Unregistered property name '{a}'.",
    +
      337.      1    unused_a: "Unused '{a}'.",
    +
      338.      1    use_double: "Use double quotes, not single quotes.",
    +
      339.      1    use_open: (
    +
      340.      1        "Wrap a ternary expression in parens, "
    +
      341.      1        + "with a line break after the left paren."
    +
      342.      1    ),
    +
      343.      1    use_spaces: "Use spaces, not tabs.",
    +
      344.      1    var_loop: "Don't declare variables in a loop.",
    +
      345.      1    var_switch: "Don't declare variables in a switch.",
    +
      346.      1    weird_condition_a: "Weird condition '{a}'.",
    +
      347.      1    weird_expression_a: "Weird expression '{a}'.",
    +
      348.      1    weird_loop: "Weird loop.",
    +
      349.      1    weird_relation_a: "Weird relation '{a}'.",
    +
      350.      1    wrap_condition: "Wrap the condition in parens.",
    +
      351.      1    wrap_immediate: (
    +
      352.      1        "Wrap an immediate function invocation in parentheses to assist "
    +
      353.      1        + "the reader in understanding that the expression is the result "
    +
      354.      1        + "of a function, and not the function itself."
    +
      355.      1    ),
    +
      356.      1    wrap_parameter: "Wrap the parameter in parens.",
    +
      357.      1    wrap_regexp: "Wrap this regexp in parens to avoid confusion.",
    +
      358.      1    wrap_unary: "Wrap the unary expression in parens."
    +
      359.      1};
    +
      360.      1
    +
      361.      1// Regular expression literals:
    +
      362.      1
    +
      363.     12function tag_regexp(strings) {
    +
      364.     12    return new RegExp(strings.raw[0].replace(/\s/g, ""));
    +
      365.     12}
    +
      366.      1
    +
      367.      1// supplant {variables}
    +
      368.      1const rx_supplant = /\{([^{}]*)\}/g;
    +
      369.      1// carriage return, carriage return linefeed, or linefeed
    +
      370.      1const rx_crlf = tag_regexp `
    +
      371.      1      \n
    +
      372.      1    | \r \n?
    +
      373.      1`;
    +
      374.      1// identifier
    +
      375.      1const rx_identifier = tag_regexp ` ^(
    +
      376.      1    [ a-z A-Z _ $ ]
    +
      377.      1    [ a-z A-Z 0-9 _ $ ]*
    +
      378.      1)$`;
    +
      379.      1const rx_module = tag_regexp ` ^ [ a-z A-Z 0-9 _ $ : . @ \- \/ ]+ $ `;
    +
      380.      1const rx_bad_property = tag_regexp `
    +
      381.      1    ^_
    +
      382.      1  | \$
    +
      383.      1  | Sync $
    +
      384.      1  | _ $
    +
      385.      1`;
    +
      386.      1// star slash
    +
      387.      1const rx_star_slash = tag_regexp ` \* \/ `;
    +
      388.      1// slash star
    +
      389.      1const rx_slash_star = tag_regexp ` \/ \* `;
    +
      390.      1// slash star or ending slash
    +
      391.      1const rx_slash_star_or_slash = tag_regexp ` \/ \* | \/ $ `;
    +
      392.      1// uncompleted work comment
    +
      393.      1const rx_todo = tag_regexp ` \b (?:
    +
      394.      1    todo
    +
      395.      1  | TO \s? DO
    +
      396.      1  | HACK
    +
      397.      1) \b `;
    +
      398.      1// tab
    +
      399.      1const rx_tab = /\t/g;
    +
      400.      1// directive
    +
      401.      1const rx_directive = tag_regexp ` ^ (
    +
      402.      1    jslint
    +
      403.      1  | property
    +
      404.      1  | global
    +
      405.      1) \s+ ( .* ) $ `;
    +
      406.      1const rx_directive_part = tag_regexp ` ^ (
    +
      407.      1    [ a-z A-Z $ _ ] [ a-z A-Z 0-9 $ _ ]*
    +
      408.      1) (?:
    +
      409.      1    : \s* ( true | false )
    +
      410.      1)? ,? \s* ( .* ) $ `;
    +
      411.      1// token
    +
      412.      1const rx_token = tag_regexp ` ^ (
    +
      413.      1    (\s+)
    +
      414.      1  | (
    +
      415.      1      [ a-z A-Z _ $ ]
    +
      416.      1      [ a-z A-Z 0-9 _ $ ]*
    +
      417.      1    )
    +
      418.      1  | [
    +
      419.      1      ( ) { } \[ \] , : ; ' " ~ \`
    +
      420.      1  ]
    +
      421.      1  | \? [ ? . ]?
    +
      422.      1  | = (?:
    +
      423.      1        = =?
    +
      424.      1      | >
    +
      425.      1    )?
    +
      426.      1  | \.+
    +
      427.      1  | \* [ * \/ = ]?
    +
      428.      1  | \/ [ * \/ ]?
    +
      429.      1  | \+ [ = + ]?
    +
      430.      1  | - [ = \- ]?
    +
      431.      1  | [ \^ % ] =?
    +
      432.      1  | & [ & = ]?
    +
      433.      1  | \| [ | = ]?
    +
      434.      1  | >{1,3} =?
    +
      435.      1  | < <? =?
    +
      436.      1  | ! (?:
    +
      437.      1        !
    +
      438.      1      | = =?
    +
      439.      1    )?
    +
      440.      1  | (
    +
      441.      1        0
    +
      442.      1      | [ 1-9 ] [ 0-9 ]*
    +
      443.      1    )
    +
      444.      1) ( .* ) $ `;
    +
      445.      1const rx_digits = /^[0-9]*/;
    +
      446.      1const rx_hexs = /^[0-9A-F]*/i;
    +
      447.      1const rx_octals = /^[0-7]*/;
    +
      448.      1const rx_bits = /^[01]*/;
    +
      449.      1// mega
    +
      450.      1const rx_mega = /[`\\]|\$\{/;
    +
      451.      1// JSON number
    +
      452.      1const rx_JSON_number = tag_regexp ` ^
    +
      453.      1    -?
    +
      454.      1    (?: 0 | [ 1-9 ] \d* )
    +
      455.      1    (?: \. \d* )?
    +
      456.      1    (?: [ e E ] [ \- + ]? \d+ )?
    +
      457.      1$ `;
    +
      458.      1// initial cap
    +
      459.      1const rx_cap = /^[A-Z]/;
    +
      460.      1
    +
      461.    126function is_letter(string) {
    +
      462.    126    return (
    +
      463.     63        (string >= "a" && string <= "z\uffff")
    +
      464.     66        || (string >= "A" && string <= "Z\uffff")
    +
      465.    126    );
    +
      466.    126}
    +
      467.      1
    +
      468.    490function supplant(string, object) {
    +
      469.    584    return string.replace(rx_supplant, function (found, filling) {
    +
      470.    584        const replacement = object[filling];
    +
      471.    584        return (
    +
      472.    584            replacement !== undefined
    +
      473.    584            ? replacement
    +
      474.      0            : found
    +
      475.    584        );
    +
      476.    584    });
    +
      477.    490}
    +
      478.      1
    +
      479.      1let anon;               // The guessed name for anonymous functions.
    +
      480.      1let blockage;           // The current block.
    +
      481.      1let block_stack;        // The stack of blocks.
    +
      482.      1let declared_globals;   // The object containing the global declarations.
    +
      483.      1let directives;         // The directive comments.
    +
      484.      1let directive_mode;     // true if directives are still allowed.
    +
      485.      1let early_stop;         // true if JSLint cannot finish.
    +
      486.      1let exports;            // The exported names and values.
    +
      487.      1let froms;              // The array collecting all import-from strings.
    +
      488.      1let fudge;              // true if the natural numbers start with 1.
    +
      489.      1let functionage;        // The current function.
    +
      490.      1let functions;          // The array containing all of the functions.
    +
      491.      1let global;             // The global object; the outermost context.
    +
      492.      1let json_mode;          // true if parsing JSON.
    +
      493.      1let lines;              // The array containing source lines.
    +
      494.      1let mega_mode;          // true if currently parsing a megastring literal.
    +
      495.      1let module_mode;        // true if import or export was used.
    +
      496.      1let next_token;         // The next token to be examined in the parse.
    +
      497.      1let option;             // The options parameter.
    +
      498.      1let property;           // The object containing the tallied property names.
    +
      499.      1let shebang;            // true if a #! was seen on the first line.
    +
      500.      1let stack;              // The stack of functions.
    +
      501.      1let syntax;             // The object containing the parser.
    +
      502.      1let token;              // The current token being examined in the parse.
    +
      503.      1let token_nr;           // The number of the next token.
    +
      504.      1let tokens;             // The array of tokens.
    +
      505.      1let tenure;             // The predefined property registry.
    +
      506.      1let tree;               // The abstract parse tree.
    +
      507.      1let var_mode;           // "var" if using var; "let" if using let.
    +
      508.      1let warnings;           // The array collecting all generated warnings.
    +
      509.      1
    +
      510.      1// Error reportage functions:
    +
      511.      1
    +
      512.    405function artifact(the_token) {
    +
      513.    405
    +
      514.    405// Return a string representing an artifact.
    +
      515.    405
    +
      516.      5    if (the_token === undefined) {
    +
      517.      5        the_token = next_token;
    +
      518.      5    }
    +
      519.    405    return (
    +
      520.    400        (the_token.id === "(string)" || the_token.id === "(number)")
    +
      521.     43        ? String(the_token.value)
    +
      522.    362        : the_token.id
    +
      523.    405    );
    +
      524.    405}
    +
      525.      1
    +
      526.      1function artifact_line(the_token) {
    +
      527.      1
    +
      528.      1// Return the fudged line number of an artifact.
    +
      529.      1
    +
      530.      0    if (the_token === undefined) {
    +
      531.      0        the_token = next_token;
    +
      532.      0    }
    +
      533.      1    return the_token.line + fudge;
    +
      534.      1}
    +
      535.      1
    +
      536.      2function artifact_column(the_token) {
    +
      537.      2
    +
      538.      2// Return the fudged column number of an artifact.
    +
      539.      2
    +
      540.      0    if (the_token === undefined) {
    +
      541.      0        the_token = next_token;
    +
      542.      0    }
    +
      543.      2    return the_token.from + fudge;
    +
      544.      2}
    +
      545.      1
    +
      546.    490function warn_at(code, line, column, a, b, c, d) {
    +
      547.    490
    +
      548.    490// Report an error at some line and column of the program. The warning object
    +
      549.    490// resembles an exception.
    +
      550.    490
    +
      551.    490    const warning = {         // ~~
    +
      552.    490        name: "JSLintError",
    +
      553.    490        column,
    +
      554.    490        line,
    +
      555.    490        code
    +
      556.    490    };
    +
      557.    473    if (a !== undefined) {
    +
      558.    473        warning.a = a;
    +
      559.    473    }
    +
      560.    168    if (b !== undefined) {
    +
      561.    168        warning.b = b;
    +
      562.    168    }
    +
      563.      3    if (c !== undefined) {
    +
      564.      3        warning.c = c;
    +
      565.      3    }
    +
      566.      1    if (d !== undefined) {
    +
      567.      1        warning.d = d;
    +
      568.      1    }
    +
      569.      0    warning.message = supplant(bundle[code] || code, warning);
    +
      570.    490// Include stack_trace for jslint to debug itself for errors.
    +
      571.      0    if (option.debug) {
    +
      572.      0        warning.stack_trace = new Error().stack;
    +
      573.      0    }
    +
      574.    490    warnings.push(warning);
    +
      575.    490    return warning;
    +
      576.    490}
    +
      577.      1
    +
      578.     18function stop_at(code, line, column, a, b, c, d) {
    +
      579.     18
    +
      580.     18// Same as warn_at, except that it stops the analysis.
    +
      581.     18
    +
      582.     18    throw warn_at(code, line, column, a, b, c, d);
    +
      583.     18}
    +
      584.      1
    +
      585.    434function warn(code, the_token, a, b, c, d) {
    +
      586.    434
    +
      587.    434// Same as warn_at, except the warning will be associated with a specific token.
    +
      588.    434// If there is already a warning on this token, suppress the new one. It is
    +
      589.    434// likely that the first warning will be the most meaningful.
    +
      590.    434
    +
      591.      9    if (the_token === undefined) {
    +
      592.      9        the_token = next_token;
    +
      593.      9    }
    +
      594.    345    if (the_token.warning === undefined) {
    +
      595.    345        the_token.warning = warn_at(
    +
      596.    345            code,
    +
      597.    345            the_token.line,
    +
      598.    345            the_token.from,
    +
      599.    345            a || artifact(the_token),
    +
      600.    345            b,
    +
      601.    345            c,
    +
      602.    345            d
    +
      603.    345        );
    +
      604.    345        return the_token.warning;
    +
      605.    345    }
    +
      606.    434}
    +
      607.      1
    +
      608.     33function stop(code, the_token, a, b, c, d) {
    +
      609.     33
    +
      610.     33// Similar to warn and stop_at. If the token already had a warning, that
    +
      611.     33// warning will be replaced with this new one. It is likely that the stopping
    +
      612.     33// warning will be the more meaningful.
    +
      613.     33
    +
      614.     12    if (the_token === undefined) {
    +
      615.     12        the_token = next_token;
    +
      616.     12    }
    +
      617.     33    delete the_token.warning;
    +
      618.     33    throw warn(code, the_token, a, b, c, d);
    +
      619.     33}
    +
      620.      1
    +
      621.      1// Tokenize:
    +
      622.      1
    +
      623.    211function tokenize(source) {
    +
      624.    211
    +
      625.    211// tokenize takes a source and produces from it an array of token objects.
    +
      626.    211// JavaScript is notoriously difficult to tokenize because of the horrible
    +
      627.    211// interactions between automatic semicolon insertion, regular expression
    +
      628.    211// literals, and now megastring literals. JSLint benefits from eliminating
    +
      629.    211// automatic semicolon insertion and nested megastring literals, which allows
    +
      630.    211// full tokenization to precede parsing.
    +
      631.    211
    +
      632.    211// If the source is not an array, then it is split into lines at the
    +
      633.    211// carriage return/linefeed.
    +
      634.    211
    +
      635.    211    lines = (
    +
      636.    211        Array.isArray(source)
    +
      637.      0        ? source
    +
      638.    211        : source.split(rx_crlf)
    +
      639.    211    );
    +
      640.    211    tokens = [];
    +
      641.    211
    +
      642.    211    let char;                   // a popular character
    +
      643.    211    let column = 0;             // the column number of the next character
    +
      644.    211    let first;                  // the first token
    +
      645.    211    let from;                   // the starting column number of the token
    +
      646.    211    let line = -1;              // the line number of the next character
    +
      647.    211    let nr = 0;                 // the next token number
    +
      648.    211    let previous = global;      // the previous token including comments
    +
      649.    211    let prior = global;         // the previous token excluding comments
    +
      650.    211    let mega_from;              // the starting column of megastring
    +
      651.    211    let mega_line;              // the starting line of megastring
    +
      652.    211    let regexp_seen;            // regular expression literal seen on this line
    +
      653.    211    let snippet;                // a piece of string
    +
      654.    211    let source_line = "";       // the remaining line source string
    +
      655.    211    let whole_line = "";        // the whole line source string
    +
      656.    211
    +
      657.      1    if (lines[0].startsWith("#!")) {
    +
      658.      1        line = 0;
    +
      659.      1        shebang = true;
    +
      660.      1    }
    +
      661.    211
    +
      662.   9509    function next_line() {
    +
      663.   9509
    +
      664.   9509// Put the next line of source in source_line. If the line contains tabs,
    +
      665.   9509// replace them with spaces and give a warning. Also warn if the line contains
    +
      666.   9509// unsafe characters or is too damn long.
    +
      667.   9509
    +
      668.   9509        let at;
    +
      669.   9509        if (
    +
      670.   9509            !option.long
    +
      671.   9509            && whole_line.length > 80
    +
      672.      1            && !json_mode
    +
      673.      1            && first
    +
      674.      1            && !regexp_seen
    +
      675.      1        ) {
    +
      676.      1            warn_at("too_long", line, 80);
    +
      677.      1        }
    +
      678.   9509        column = 0;
    +
      679.   9509        line += 1;
    +
      680.   9509        regexp_seen = false;
    +
      681.   9509        source_line = lines[line];
    +
      682.   2861        whole_line = source_line || "";
    +
      683.   9312        if (source_line !== undefined) {
    +
      684.   9312            at = source_line.search(rx_tab);
    +
      685.   9312            if (at >= 0) {
    +
      686.   9312                if (!option.white) {
    +
      687.   9312                    warn_at("use_spaces", line, at + 1);
    +
      688.   9312                }
    +
      689.   9312                source_line = source_line.replace(rx_tab, " ");
    +
      690.   9312            }
    +
      691.   9312            if (!option.white && source_line.slice(-1) === " ") {
    +
      692.   9312                warn_at(
    +
      693.   9312                    "unexpected_trailing_space",
    +
      694.   9312                    line,
    +
      695.   9312                    source_line.length - 1
    +
      696.   9312                );
    +
      697.   9312            }
    +
      698.   9312        }
    +
      699.   9509        return source_line;
    +
      700.   9509    }
    +
      701.    211
    +
      702.    211// Most tokens, including the identifiers, operators, and punctuators, can be
    +
      703.    211// found with a regular expression. Regular expressions cannot correctly match
    +
      704.    211// regular expression literals, so we will match those the hard way. String
    +
      705.    211// literals and number literals can be matched by regular expressions, but they
    +
      706.    211// don't provide good warnings. The functions snip, next_char, back_char,
    +
      707.    211// some_digits, and escape help in the parsing of literals.
    +
      708.    211
    +
      709.   3029    function snip() {
    +
      710.   3029
    +
      711.   3029// Remove the last character from snippet.
    +
      712.   3029
    +
      713.   3029        snippet = snippet.slice(0, -1);
    +
      714.   3029    }
    +
      715.    211
    +
      716.  22445    function next_char(match) {
    +
      717.  22445
    +
      718.  22445// Get the next character from the source line. Remove it from the source_line,
    +
      719.  22445// and append it to the snippet. Optionally check that the previous character
    +
      720.  22445// matched an expected value.
    +
      721.  22445
    +
      722.    539        if (match !== undefined && char !== match) {
    +
      723.      3            return stop_at(
    +
      724.      3                (
    +
      725.      3                    char === ""
    +
      726.      3                    ? "expected_a"
    +
      727.      3                    : "expected_a_b"
    +
      728.      3                ),
    +
      729.      3                line,
    +
      730.      3                column - 1,
    +
      731.      3                match,
    +
      732.      3                char
    +
      733.      3            );
    +
      734.  22442        }
    +
      735.  22442        if (source_line) {
    +
      736.  22339            char = source_line[0];
    +
      737.  22339            source_line = source_line.slice(1);
    +
      738.  22339            snippet += char;
    +
      739.  22339        } else {
    +
      740.    103            char = "";
    +
      741.    103            snippet += " ";
    +
      742.  22442        }
    +
      743.  22442        column += 1;
    +
      744.  22442        return char;
    +
      745.  22442    }
    +
      746.    211
    +
      747.    679    function back_char() {
    +
      748.    679
    +
      749.    679// Back up one character by moving a character from the end of the snippet to
    +
      750.    679// the front of the source_line.
    +
      751.    679
    +
      752.    679        if (snippet) {
    +
      753.    679            char = snippet.slice(-1);
    +
      754.    679            source_line = char + source_line;
    +
      755.    679            column -= 1;
    +
      756.    679            snip();
    +
      757.      0        } else {
    +
      758.      0            char = "";
    +
      759.      0        }
    +
      760.    679        return char;
    +
      761.    679    }
    +
      762.    211
    +
      763.     42    function some_digits(rx, quiet) {
    +
      764.     42        const digits = source_line.match(rx)[0];
    +
      765.     42        const length = digits.length;
    +
      766.     10        if (!quiet && length === 0) {
    +
      767.      1            warn_at("expected_digits_after_a", line, column, snippet);
    +
      768.      1        }
    +
      769.     42        column += length;
    +
      770.     42        source_line = source_line.slice(length);
    +
      771.     42        snippet += digits;
    +
      772.     42        next_char();
    +
      773.     42        return length;
    +
      774.     42    }
    +
      775.    211
    +
      776.    279    function escape(extra) {
    +
      777.    279        next_char("\\");
    +
      778.    123        if (escapeable[char] === true) {
    +
      779.    123            return next_char();
    +
      780.    156        }
    +
      781.    156        if (char === "") {
    +
      782.      1            return stop_at("unclosed_string", line, column);
    +
      783.    155        }
    +
      784.    155        if (char === "u") {
    +
      785.     30            if (next_char("u") === "{") {
    +
      786.     30                if (json_mode) {
    +
      787.     30                    warn_at("unexpected_a", line, column - 1, char);
    +
      788.     30                }
    +
      789.     30                if (some_digits(rx_hexs) > 5) {
    +
      790.     30                    warn_at("too_many_digits", line, column - 1);
    +
      791.     30                }
    +
      792.     30                if (next_char() !== "}") {
    +
      793.     30                    stop_at("expected_a_before_b", line, column, "}", char);
    +
      794.      0                }
    +
      795.      0                return next_char();
    +
      796.      0            }
    +
      797.     30            back_char();
    +
      798.     30            if (some_digits(rx_hexs, true) < 4) {
    +
      799.     30                warn_at("expected_four_digits", line, column - 1);
    +
      800.     30            }
    +
      801.     30            return;
    +
      802.    125        }
    +
      803.    125        if (extra && extra.indexOf(char) >= 0) {
    +
      804.    124            return next_char();
    +
      805.    124        }
    +
      806.      1        warn_at("unexpected_a_before_b", line, column - 2, "\\", char);
    +
      807.      1    }
    +
      808.    211
    +
      809.  31098    function make(id, value, identifier) {
    +
      810.  31098
    +
      811.  31098// Make the token object and append it to the tokens list.
    +
      812.  31098
    +
      813.  31098        const the_token = {
    +
      814.  31098            from,
    +
      815.  31098            id,
    +
      816.  31098            identifier: Boolean(identifier),
    +
      817.  31098            line,
    +
      818.  31098            nr,
    +
      819.  31098            thru: column
    +
      820.  31098        };
    +
      821.  31098        tokens[nr] = the_token;
    +
      822.  31098        nr += 1;
    +
      823.  31098
    +
      824.  31098// Directives must appear before the first statement.
    +
      825.  31098
    +
      826.  30585        if (id !== "(comment)" && id !== ";") {
    +
      827.  28281            directive_mode = false;
    +
      828.  28281        }
    +
      829.  31098
    +
      830.  31098// If the token is to have a value, give it one.
    +
      831.  31098
    +
      832.   3505        if (value !== undefined) {
    +
      833.   3505            the_token.value = value;
    +
      834.   3505        }
    +
      835.  31098
    +
      836.  31098// If this token is an identifier that touches a preceding number, or
    +
      837.  31098// a "/", comment, or regular expression literal that touches a preceding
    +
      838.  31098// comment or regular expression literal, then give a missing space warning.
    +
      839.  31098// This warning is not suppressed by option.white.
    +
      840.  31098
    +
      841.  31098        if (
    +
      842.  31098            previous.line === line
    +
      843.  24716            && previous.thru === from
    +
      844.  15888            && (id === "(comment)" || id === "(regexp)" || id === "/")
    +
      845.     36            && (previous.id === "(comment)" || previous.id === "(regexp)")
    +
      846.      1        ) {
    +
      847.      1            warn(
    +
      848.      1                "expected_space_a_b",
    +
      849.      1                the_token,
    +
      850.      1                artifact(previous),
    +
      851.      1                artifact(the_token)
    +
      852.      1            );
    +
      853.      1        }
    +
      854.   1866        if (previous.id === "." && id === "(number)") {
    +
      855.      2            warn("expected_a_before_b", previous, "0", ".");
    +
      856.      2        }
    +
      857.   1866        if (prior.id === "." && the_token.identifier) {
    +
      858.   1864            the_token.dot = true;
    +
      859.   1864        }
    +
      860.  31098
    +
      861.  31098// The previous token is used to detect adjacency problems.
    +
      862.  31098
    +
      863.  31098        previous = the_token;
    +
      864.  31098
    +
      865.  31098// The prior token is a previous token that was not a comment. The prior token
    +
      866.  31098// is used to disambiguate "/", which can mean division or regular expression
    +
      867.  31098// literal.
    +
      868.  31098
    +
      869.  30585        if (previous.id !== "(comment)") {
    +
      870.  30585            prior = previous;
    +
      871.  30585        }
    +
      872.  31098        return the_token;
    +
      873.  31098    }
    +
      874.    211
    +
      875.    323    function parse_directive(the_comment, body) {
    +
      876.    323
    +
      877.    323// JSLint recognizes three directives that can be encoded in comments. This
    +
      878.    323// function processes one item, and calls itself recursively to process the
    +
      879.    323// next one.
    +
      880.    323
    +
      881.    323        const result = body.match(rx_directive_part);
    +
      882.    307        if (result) {
    +
      883.    307            let allowed;
    +
      884.    307            const name = result[1];
    +
      885.    307            const value = result[2];
    +
      886.    307            if (the_comment.directive === "jslint") {
    +
      887.    307                allowed = allowed_option[name];
    +
      888.    307                if (
    +
      889.    307                    typeof allowed === "boolean"
    +
      890.    307                    || typeof allowed === "object"
    +
      891.    307                ) {
    +
      892.    307                    if (
    +
      893.    307                        value === ""
    +
      894.    307                        || value === "true"
    +
      895.    307                        || value === undefined
    +
      896.    307                    ) {
    +
      897.    307                        option[name] = true;
    +
      898.    307                        if (Array.isArray(allowed)) {
    +
      899.    307                            populate(allowed, declared_globals, false);
    +
      900.    307                        }
    +
      901.      0                    } else if (value === "false") {
    +
      902.      0                        option[name] = false;
    +
      903.      0                    } else {
    +
      904.      0                        warn("bad_option_a", the_comment, name + ":" + value);
    +
      905.      0                    }
    +
      906.    307                } else {
    +
      907.    307                    warn("bad_option_a", the_comment, name);
    +
      908.    307                }
    +
      909.    307            } else if (the_comment.directive === "property") {
    +
      910.    307                if (tenure === undefined) {
    +
      911.    307                    tenure = empty();
    +
      912.    307                }
    +
      913.    307                tenure[name] = true;
    +
      914.    307            } else if (the_comment.directive === "global") {
    +
      915.    307                if (value) {
    +
      916.    307                    warn("bad_option_a", the_comment, name + ":" + value);
    +
      917.    307                }
    +
      918.    307                declared_globals[name] = false;
    +
      919.    307                module_mode = the_comment;
    +
      920.    307            }
    +
      921.    307            return parse_directive(the_comment, result[3]);
    +
      922.    307        }
    +
      923.     16        if (body) {
    +
      924.      1            return stop("bad_directive_a", the_comment, body);
    +
      925.      1        }
    +
      926.    323    }
    +
      927.    211
    +
      928.    513    function comment(snippet) {
    +
      929.    513
    +
      930.    513// Make a comment object. Comments are not allowed in JSON text. Comments can
    +
      931.    513// include directives and notices of incompletion.
    +
      932.    513
    +
      933.    513        const the_comment = make("(comment)", snippet);
    +
      934.     27        if (Array.isArray(snippet)) {
    +
      935.     27            snippet = snippet.join(" ");
    +
      936.     27        }
    +
      937.      1        if (!option.devel && rx_todo.test(snippet)) {
    +
      938.      1            warn("todo_comment", the_comment);
    +
      939.      1        }
    +
      940.    513        const result = snippet.match(rx_directive);
    +
      941.     17        if (result) {
    +
      942.     17            if (!directive_mode) {
    +
      943.     17                warn_at("misplaced_directive_a", line, from, result[1]);
    +
      944.     17            } else {
    +
      945.     17                the_comment.directive = result[1];
    +
      946.     17                parse_directive(the_comment, result[2]);
    +
      947.     17            }
    +
      948.     17            directives.push(the_comment);
    +
      949.    512        }
    +
      950.    512        return the_comment;
    +
      951.    512    }
    +
      952.    211
    +
      953.     70    function regexp() {
    +
      954.     70
    +
      955.     70// Parse a regular expression literal.
    +
      956.     70
    +
      957.     70        let multi_mode = false;
    +
      958.     70        let result;
    +
      959.     70        let value;
    +
      960.     70        regexp_seen = true;
    +
      961.     70
    +
      962.    417        function quantifier() {
    +
      963.    417
    +
      964.    417// Match an optional quantifier.
    +
      965.    417
    +
      966.    412            if (char === "?" || char === "*" || char === "+") {
    +
      967.     34                next_char();
    +
      968.    383            } else if (char === "{") {
    +
      969.    383                if (some_digits(rx_digits, true) === 0) {
    +
      970.    383                    warn_at("expected_a_before_b", line, column, "0", ",");
    +
      971.    383                }
    +
      972.    383                if (char === ",") {
    +
      973.    383                    some_digits(rx_digits, true);
    +
      974.    383                }
    +
      975.    383                next_char("}");
    +
      976.    383            } else {
    +
      977.    383                return;
    +
      978.    383            }
    +
      979.     36            if (char === "?") {
    +
      980.     22                next_char("?");
    +
      981.     22            }
    +
      982.    417        }
    +
      983.     70
    +
      984.     68        function subklass() {
    +
      985.     68
    +
      986.     68// Match a character in a character class.
    +
      987.     68
    +
      988.     13            if (char === "\\") {
    +
      989.     13                escape("BbDdSsWw-[]^");
    +
      990.     13                return true;
    +
      991.     55            }
    +
      992.     55            if (
    +
      993.     55                char === ""
    +
      994.     55                || char === "["
    +
      995.     54                || char === "]"
    +
      996.     33                || char === "/"
    +
      997.     32                || char === "^"
    +
      998.     32                || char === "-"
    +
      999.     23            ) {
    +
     1000.     23                return false;
    +
     1001.     32            }
    +
     1002.     32            if (char === " ") {
    +
     1003.      1                warn_at("expected_a_b", line, column, "\\u0020", " ");
    +
     1004.     31            } else if (char === "`" && mega_mode) {
    +
     1005.     31                warn_at("unexpected_a", line, column, "`");
    +
     1006.     32            }
    +
     1007.     32            next_char();
    +
     1008.     32            return true;
    +
     1009.     32        }
    +
     1010.     70
    +
     1011.     60        function ranges() {
    +
     1012.     60
    +
     1013.     60// Match a range of subclasses.
    +
     1014.     60
    +
     1015.     38            if (subklass()) {
    +
     1016.     38                if (char === "-") {
    +
     1017.     38                    next_char("-");
    +
     1018.     38                    if (!subklass()) {
    +
     1019.     38                        return stop_at(
    +
     1020.     38                            "unexpected_a",
    +
     1021.     38                            line,
    +
     1022.     38                            column - 1,
    +
     1023.     38                            "-"
    +
     1024.     38                        );
    +
     1025.     38                    }
    +
     1026.     38                }
    +
     1027.     38                return ranges();
    +
     1028.     38            }
    +
     1029.     60        }
    +
     1030.     70
    +
     1031.     22        function klass() {
    +
     1032.     22
    +
     1033.     22// Match a class.
    +
     1034.     22
    +
     1035.     22            next_char("[");
    +
     1036.      3            if (char === "^") {
    +
     1037.      3                next_char("^");
    +
     1038.      3            }
    +
     1039.     23            (function classy() {
    +
     1040.     23                ranges();
    +
     1041.      2                if (char !== "]" && char !== "") {
    +
     1042.      1                    warn_at(
    +
     1043.      1                        "expected_a_before_b",
    +
     1044.      1                        line,
    +
     1045.      1                        column,
    +
     1046.      1                        "\\",
    +
     1047.      1                        char
    +
     1048.      1                    );
    +
     1049.      1                    next_char();
    +
     1050.      1                    return classy();
    +
     1051.      1                }
    +
     1052.     23            }());
    +
     1053.     22            next_char("]");
    +
     1054.     22        }
    +
     1055.     70
    +
     1056.     85        function choice() {
    +
     1057.     85
    +
     1058.     15            function group() {
    +
     1059.     15
    +
     1060.     15// Match a group that starts with left paren.
    +
     1061.     15
    +
     1062.     15                next_char("(");
    +
     1063.      3                if (char === "?") {
    +
     1064.      3                    next_char("?");
    +
     1065.      0                    if (char === "=" || char === "!") {
    +
     1066.      0                        next_char();
    +
     1067.      0                    } else {
    +
     1068.      3                        next_char(":");
    +
     1069.      3                    }
    +
     1070.     12                } else if (char === ":") {
    +
     1071.     12                    warn_at("expected_a_before_b", line, column, "?", ":");
    +
     1072.     12                }
    +
     1073.     15                choice();
    +
     1074.     15                next_char(")");
    +
     1075.     15            }
    +
     1076.     85
    +
     1077.    501            function factor() {
    +
     1078.    501                if (
    +
     1079.    501                    char === ""
    +
     1080.    500                    || char === "/"
    +
     1081.    434                    || char === "]"
    +
     1082.    434                    || char === ")"
    +
     1083.     82                ) {
    +
     1084.     82                    return false;
    +
     1085.    419                }
    +
     1086.    419                if (char === "(") {
    +
     1087.     15                    group();
    +
     1088.     15                    return true;
    +
     1089.    404                }
    +
     1090.    404                if (char === "[") {
    +
     1091.     22                    klass();
    +
     1092.     22                    return true;
    +
     1093.    382                }
    +
     1094.    382                if (char === "\\") {
    +
     1095.     80                    escape("BbDdSsWw^${}[]():=!.|*+?");
    +
     1096.     80                    return true;
    +
     1097.    302                }
    +
     1098.    302                if (
    +
     1099.    302                    char === "?"
    +
     1100.    302                    || char === "+"
    +
     1101.    301                    || char === "*"
    +
     1102.    301                    || char === "}"
    +
     1103.    301                    || char === "{"
    +
     1104.      1                ) {
    +
     1105.      1                    warn_at(
    +
     1106.      1                        "expected_a_before_b",
    +
     1107.      1                        line,
    +
     1108.      1                        column - 1,
    +
     1109.      1                        "\\",
    +
     1110.      1                        char
    +
     1111.      1                    );
    +
     1112.    301                } else if (char === "`") {
    +
     1113.    301                    if (mega_mode) {
    +
     1114.    301                        warn_at("unexpected_a", line, column - 1, "`");
    +
     1115.    301                    }
    +
     1116.    301                } else if (char === " ") {
    +
     1117.    301                    warn_at(
    +
     1118.    301                        "expected_a_b",
    +
     1119.    301                        line,
    +
     1120.    301                        column - 1,
    +
     1121.    301                        "\\s",
    +
     1122.    301                        " "
    +
     1123.    301                    );
    +
     1124.    301                } else if (char === "$") {
    +
     1125.    301                    if (source_line[0] !== "/") {
    +
     1126.    301                        multi_mode = true;
    +
     1127.    301                    }
    +
     1128.    301                } else if (char === "^") {
    +
     1129.    301                    if (snippet !== "^") {
    +
     1130.    301                        multi_mode = true;
    +
     1131.    301                    }
    +
     1132.    302                }
    +
     1133.    302                next_char();
    +
     1134.    302                return true;
    +
     1135.    302            }
    +
     1136.     85
    +
     1137.    501            function sequence(follow) {
    +
     1138.    417                if (factor()) {
    +
     1139.    417                    quantifier();
    +
     1140.    417                    return sequence(true);
    +
     1141.    417                }
    +
     1142.     82                if (!follow) {
    +
     1143.      1                    warn_at("expected_regexp_factor_a", line, column, char);
    +
     1144.      1                }
    +
     1145.    501            }
    +
     1146.     85
    +
     1147.     85// Match a choice (a sequence that can be followed by | and another choice).
    +
     1148.     85
    +
     1149.     85            sequence();
    +
     1150.      0            if (char === "|") {
    +
     1151.      0                next_char("|");
    +
     1152.      0                return choice();
    +
     1153.      0            }
    +
     1154.     85        }
    +
     1155.     70
    +
     1156.     70// Scan the regexp literal. Give a warning if the first character is = because
    +
     1157.     70// /= looks like a division assignment operator.
    +
     1158.     70
    +
     1159.     70        snippet = "";
    +
     1160.     70        next_char();
    +
     1161.      1        if (char === "=") {
    +
     1162.      1            warn_at("expected_a_before_b", line, column, "\\", "=");
    +
     1163.      1        }
    +
     1164.     70        choice();
    +
     1165.     70
    +
     1166.     70// Make sure there is a closing slash.
    +
     1167.     70
    +
     1168.     70        snip();
    +
     1169.     70        value = snippet;
    +
     1170.     70        next_char("/");
    +
     1171.     70
    +
     1172.     70// Process dangling flag letters.
    +
     1173.     70
    +
     1174.     70        const allowed = {
    +
     1175.     70            g: true,
    +
     1176.     70            i: true,
    +
     1177.     70            m: true,
    +
     1178.     70            u: true,
    +
     1179.     70            y: true
    +
     1180.     70        };
    +
     1181.     70        const flag = empty();
    +
     1182.    126        (function make_flag() {
    +
     1183.     60            if (is_letter(char)) {
    +
     1184.     60                if (allowed[char] !== true) {
    +
     1185.     60                    warn_at("unexpected_a", line, column, char);
    +
     1186.     60                }
    +
     1187.     60                allowed[char] = false;
    +
     1188.     60                flag[char] = true;
    +
     1189.     60                next_char();
    +
     1190.     60                return make_flag();
    +
     1191.     60            }
    +
     1192.    126        }());
    +
     1193.     70        back_char();
    +
     1194.     65        if (char === "/" || char === "*") {
    +
     1195.      1            return stop_at("unexpected_a", line, from, char);
    +
     1196.     65        }
    +
     1197.     65        result = make("(regexp)", char);
    +
     1198.     65        result.flag = flag;
    +
     1199.     65        result.value = value;
    +
     1200.     65        if (multi_mode && !flag.m) {
    +
     1201.      1            warn_at("missing_m", line, column);
    +
     1202.     65        }
    +
     1203.     65        return result;
    +
     1204.     65    }
    +
     1205.    211
    +
     1206.   2287    function string(quote) {
    +
     1207.   2287
    +
     1208.   2287// Make a string token.
    +
     1209.   2287
    +
     1210.   2287        let the_token;
    +
     1211.   2287        snippet = "";
    +
     1212.   2287        next_char();
    +
     1213.   2287
    +
     1214.  20761        return (function next() {
    +
     1215.   2283            if (char === quote) {
    +
     1216.   2283                snip();
    +
     1217.   2283                the_token = make("(string)", snippet);
    +
     1218.   2283                the_token.quote = quote;
    +
     1219.   2283                return the_token;
    +
     1220.  18478            }
    +
     1221.  18478            if (char === "") {
    +
     1222.      1                return stop_at("unclosed_string", line, column);
    +
     1223.  18477            }
    +
     1224.  18477            if (char === "\\") {
    +
     1225.    186                escape(quote);
    +
     1226.  18291            } else if (char === "`") {
    +
     1227.  18291                if (mega_mode) {
    +
     1228.  18291                    warn_at("unexpected_a", line, column, "`");
    +
     1229.  18291                }
    +
     1230.  18291                next_char("`");
    +
     1231.  18291            } else {
    +
     1232.  18291                next_char();
    +
     1233.  18474            }
    +
     1234.  18474            return next();
    +
     1235.  18474        }());
    +
     1236.   2287    }
    +
     1237.    211
    +
     1238.    265    function frack() {
    +
     1239.      5        if (char === ".") {
    +
     1240.      5            some_digits(rx_digits);
    +
     1241.      5        }
    +
     1242.      0        if (char === "E" || char === "e") {
    +
     1243.      0            next_char();
    +
     1244.      0            if (char !== "+" && char !== "-") {
    +
     1245.      0                back_char();
    +
     1246.      0            }
    +
     1247.      0            some_digits(rx_digits);
    +
     1248.      0        }
    +
     1249.    265    }
    +
     1250.    211
    +
     1251.    586    function number() {
    +
     1252.    324        if (snippet === "0") {
    +
     1253.    324            next_char();
    +
     1254.    324            if (char === ".") {
    +
     1255.    324                frack();
    +
     1256.      0            } else if (char === "b") {
    +
     1257.      0                some_digits(rx_bits);
    +
     1258.      0            } else if (char === "o") {
    +
     1259.      0                some_digits(rx_octals);
    +
     1260.      0            } else if (char === "x") {
    +
     1261.    324                some_digits(rx_hexs);
    +
     1262.    324            }
    +
     1263.    324        } else {
    +
     1264.    262            next_char();
    +
     1265.    262            frack();
    +
     1266.    262        }
    +
     1267.    586
    +
     1268.    586// If the next character after a number is a digit or letter, then something
    +
     1269.    586// unexpected is going on.
    +
     1270.    586
    +
     1271.    586        if (
    +
     1272.    261            (char >= "0" && char <= "9")
    +
     1273.      7            || (char >= "a" && char <= "z")
    +
     1274.    585            || (char >= "A" && char <= "Z")
    +
     1275.      1        ) {
    +
     1276.      1            return stop_at(
    +
     1277.      1                "unexpected_a_after_b",
    +
     1278.      1                line,
    +
     1279.      1                column - 1,
    +
     1280.      1                snippet.slice(-1),
    +
     1281.      1                snippet.slice(0, -1)
    +
     1282.      1            );
    +
     1283.    585        }
    +
     1284.    585        back_char();
    +
     1285.    585        return make("(number)", snippet);
    +
     1286.    585    }
    +
     1287.    211
    +
     1288.  45073    function lex() {
    +
     1289.  45073        let array;
    +
     1290.  45073        let i = 0;
    +
     1291.  45073        let j = 0;
    +
     1292.  45073        let last;
    +
     1293.  45073        let result;
    +
     1294.  45073        let the_token;
    +
     1295.  45073
    +
     1296.  45073// This should properly be a tail recursive function, but sadly, conformant
    +
     1297.  45073// implementations of ES6 are still rare. This is the ideal code:
    +
     1298.  45073
    +
     1299.  45073//      if (!source_line) {
    +
     1300.  45073//          source_line = next_line();
    +
     1301.  45073//          from = 0;
    +
     1302.  45073//          return (
    +
     1303.  45073//              source_line === undefined
    +
     1304.  45073//              ? (
    +
     1305.  45073//                  mega_mode
    +
     1306.  45073//                  ? stop_at("unclosed_mega", mega_line, mega_from)
    +
     1307.  45073//                  : make("(end)")
    +
     1308.  45073//              )
    +
     1309.  45073//              : lex()
    +
     1310.  45073//          );
    +
     1311.  45073//      }
    +
     1312.  45073
    +
     1313.  45073// Unfortunately, incompetent JavaScript engines will sometimes fail to execute
    +
     1314.  45073// it correctly. So for now, we do it the old fashioned way.
    +
     1315.  45073
    +
     1316.   9220        while (!source_line) {
    +
     1317.   9220            source_line = next_line();
    +
     1318.   9220            from = 0;
    +
     1319.   9220            if (source_line === undefined) {
    +
     1320.   9220                return (
    +
     1321.   9220                    mega_mode
    +
     1322.   9220                    ? stop_at("unclosed_mega", mega_line, mega_from)
    +
     1323.   9220                    : make("(end)")
    +
     1324.   9220                );
    +
     1325.   9220            }
    +
     1326.  44880        }
    +
     1327.  44880
    +
     1328.  44880        from = column;
    +
     1329.  44880        result = source_line.match(rx_token);
    +
     1330.  44880
    +
     1331.  44880// result[1] token
    +
     1332.  44880// result[2] whitespace
    +
     1333.  44880// result[3] identifier
    +
     1334.  44880// result[4] number
    +
     1335.  44880// result[5] rest
    +
     1336.  44880
    +
     1337.  44880        if (!result) {
    +
     1338.      1            return stop_at(
    +
     1339.      1                "unexpected_char_a",
    +
     1340.      1                line,
    +
     1341.      1                column,
    +
     1342.      1                source_line[0]
    +
     1343.      1            );
    +
     1344.  44879        }
    +
     1345.  44879
    +
     1346.  44879        snippet = result[1];
    +
     1347.  44879        column += snippet.length;
    +
     1348.  44879        source_line = result[5];
    +
     1349.  44879
    +
     1350.  44879// Whitespace was matched. Call lex again to get more.
    +
     1351.  44879
    +
     1352.  44879        if (result[2]) {
    +
     1353.  14077            return lex();
    +
     1354.  30802        }
    +
     1355.  30802
    +
     1356.  30802// The token is an identifier.
    +
     1357.  30802
    +
     1358.  30802        if (result[3]) {
    +
     1359.   9962            return make(snippet, undefined, true);
    +
     1360.  20840        }
    +
     1361.  20840
    +
     1362.  20840// The token is a number.
    +
     1363.  20840
    +
     1364.  20840        if (result[4]) {
    +
     1365.    586            return number(snippet);
    +
     1366.  20254        }
    +
     1367.  20254
    +
     1368.  20254// The token is a string.
    +
     1369.  20254
    +
     1370.  20254        if (snippet === "\"") {
    +
     1371.   2285            return string(snippet);
    +
     1372.  17969        }
    +
     1373.  17969        if (snippet === "'") {
    +
     1374.      2            if (!option.single) {
    +
     1375.      2                warn_at("use_double", line, column);
    +
     1376.      2            }
    +
     1377.      2            return string(snippet);
    +
     1378.  17967        }
    +
     1379.  17967
    +
     1380.  17967// The token is a megastring. We don't allow any kind of mega nesting.
    +
     1381.  17967
    +
     1382.  17967        if (snippet === "`") {
    +
     1383.     31            if (mega_mode) {
    +
     1384.     31                return stop_at("expected_a_b", line, column, "}", "`");
    +
     1385.     31            }
    +
     1386.     31            snippet = "";
    +
     1387.     31            mega_from = from;
    +
     1388.     31            mega_line = line;
    +
     1389.     31            mega_mode = true;
    +
     1390.     31
    +
     1391.     31// Parsing a mega literal is tricky. First make a ` token.
    +
     1392.     31
    +
     1393.     31            make("`");
    +
     1394.     31            from += 1;
    +
     1395.     31
    +
     1396.     31// Then loop, building up a string, possibly from many lines, until seeing
    +
     1397.     31// the end of file, a closing `, or a ${ indicting an expression within the
    +
     1398.     31// string.
    +
     1399.     31
    +
     1400.    315            (function part() {
    +
     1401.    315                const at = source_line.search(rx_mega);
    +
     1402.    315
    +
     1403.    315// If neither ` nor ${ is seen, then the whole line joins the snippet.
    +
     1404.    315
    +
     1405.    218                if (at < 0) {
    +
     1406.    218                    snippet += source_line + "\n";
    +
     1407.    218                    return (
    +
     1408.    218                        next_line() === undefined
    +
     1409.    218                        ? stop_at("unclosed_mega", mega_line, mega_from)
    +
     1410.    218                        : part()
    +
     1411.    218                    );
    +
     1412.    218                }
    +
     1413.     97                snippet += source_line.slice(0, at);
    +
     1414.     97                column += at;
    +
     1415.     97                source_line = source_line.slice(at);
    +
     1416.     97                if (source_line[0] === "\\") {
    +
     1417.     38                    snippet += source_line.slice(0, 2);
    +
     1418.     38                    source_line = source_line.slice(2);
    +
     1419.     38                    column += 2;
    +
     1420.     38                    return part();
    +
     1421.     59                }
    +
     1422.     59
    +
     1423.     59// if either ` or ${ was found, then the preceding joins the snippet to become
    +
     1424.     59// a string token.
    +
     1425.     59
    +
     1426.     59                make("(string)", snippet).quote = "`";
    +
     1427.     59                snippet = "";
    +
     1428.     59
    +
     1429.     59// If ${, then make tokens that will become part of an expression until
    +
     1430.     59// a } token is made.
    +
     1431.     59
    +
     1432.     59                if (source_line[0] === "$") {
    +
     1433.     33                    column += 2;
    +
     1434.     33                    make("${");
    +
     1435.     33                    source_line = source_line.slice(2);
    +
     1436.    144                    (function expr() {
    +
     1437.    144                        const id = lex().id;
    +
     1438.     33                        if (id === "{") {
    +
     1439.     33                            return stop_at(
    +
     1440.     33                                "expected_a_b",
    +
     1441.     33                                line,
    +
     1442.     33                                column,
    +
     1443.     33                                "}",
    +
     1444.     33                                "{"
    +
     1445.     33                            );
    +
     1446.    141                        }
    +
     1447.    141                        if (id !== "}") {
    +
     1448.    111                            return expr();
    +
     1449.    111                        }
    +
     1450.    144                    }());
    +
     1451.     33                    return part();
    +
     1452.     33                }
    +
     1453.    315            }());
    +
     1454.     31            source_line = source_line.slice(1);
    +
     1455.     31            column += 1;
    +
     1456.     31            mega_mode = false;
    +
     1457.     31            return make("`");
    +
     1458.  17936        }
    +
     1459.  17936
    +
     1460.  17936// The token is a // comment.
    +
     1461.  17936
    +
     1462.  17936        if (snippet === "//") {
    +
     1463.    486            snippet = source_line;
    +
     1464.    486            source_line = "";
    +
     1465.    486            the_token = comment(snippet);
    +
     1466.    486            if (mega_mode) {
    +
     1467.    486                warn("unexpected_comment", the_token, "`");
    +
     1468.    486            }
    +
     1469.    486            return the_token;
    +
     1470.  17450        }
    +
     1471.  17450
    +
     1472.  17450// The token is a /* comment.
    +
     1473.  17450
    +
     1474.  17450        if (snippet === "/*") {
    +
     1475.     30            array = [];
    +
     1476.     30            if (source_line[0] === "/") {
    +
     1477.     30                warn_at("unexpected_a", line, column + i, "/");
    +
     1478.     30            }
    +
     1479.     98            (function next() {
    +
     1480.     90                if (source_line > "") {
    +
     1481.     90                    i = source_line.search(rx_star_slash);
    +
     1482.     90                    if (i >= 0) {
    +
     1483.     90                        return;
    +
     1484.     90                    }
    +
     1485.     90                    j = source_line.search(rx_slash_star);
    +
     1486.     90                    if (j >= 0) {
    +
     1487.     90                        warn_at("nested_comment", line, column + j);
    +
     1488.     90                    }
    +
     1489.     90                }
    +
     1490.     71                array.push(source_line);
    +
     1491.     71                source_line = next_line();
    +
     1492.     71                if (source_line === undefined) {
    +
     1493.     30                    return stop_at("unclosed_comment", line, column);
    +
     1494.     68                }
    +
     1495.     68                return next();
    +
     1496.     68            }());
    +
     1497.     30            snippet = source_line.slice(0, i);
    +
     1498.     30            j = snippet.search(rx_slash_star_or_slash);
    +
     1499.     30            if (j >= 0) {
    +
     1500.     30                warn_at("nested_comment", line, column + j);
    +
     1501.     30            }
    +
     1502.     30            array.push(snippet);
    +
     1503.     30            column += i + 2;
    +
     1504.     30            source_line = source_line.slice(i + 2);
    +
     1505.     30            return comment(array);
    +
     1506.  17420        }
    +
     1507.  17420
    +
     1508.  17420// The token is a slash.
    +
     1509.  17420
    +
     1510.  17420        if (snippet === "/") {
    +
     1511.     78
    +
     1512.     78// The / can be a division operator or the beginning of a regular expression
    +
     1513.     78// literal. It is not possible to know which without doing a complete parse.
    +
     1514.     78// We want to complete the tokenization before we begin to parse, so we will
    +
     1515.     78// estimate. This estimator can fail in some cases. For example, it cannot
    +
     1516.     78// know if "}" is ending a block or ending an object literal, so it can
    +
     1517.     78// behave incorrectly in that case; it is not meaningful to divide an
    +
     1518.     78// object, so it is likely that we can get away with it. We avoided the worst
    +
     1519.     78// cases by eliminating automatic semicolon insertion.
    +
     1520.     78
    +
     1521.     78            if (prior.identifier) {
    +
     1522.     78                if (!prior.dot) {
    +
     1523.      0                    if (prior.id === "return") {
    +
     1524.      0                        return regexp();
    +
     1525.      0                    }
    +
     1526.     78                    if (
    +
     1527.     78                        prior.id === "(begin)"
    +
     1528.     78                        || prior.id === "case"
    +
     1529.     78                        || prior.id === "delete"
    +
     1530.     78                        || prior.id === "in"
    +
     1531.     78                        || prior.id === "instanceof"
    +
     1532.     78                        || prior.id === "new"
    +
     1533.     78                        || prior.id === "typeof"
    +
     1534.     78                        || prior.id === "void"
    +
     1535.     78                        || prior.id === "yield"
    +
     1536.     78                    ) {
    +
     1537.     78                        the_token = regexp();
    +
     1538.     78                        return stop("unexpected_a", the_token);
    +
     1539.     78                    }
    +
     1540.     78                }
    +
     1541.     78            } else {
    +
     1542.     78                last = prior.id[prior.id.length - 1];
    +
     1543.     78                if ("(,=:?[".indexOf(last) >= 0) {
    +
     1544.     78                    return regexp();
    +
     1545.     78                }
    +
     1546.     78                if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) {
    +
     1547.     78                    the_token = regexp();
    +
     1548.     78                    warn("wrap_regexp", the_token);
    +
     1549.     78                    return the_token;
    +
     1550.     78                }
    +
     1551.     78            }
    +
     1552.     78            if (source_line[0] === "=") {
    +
     1553.     78                column += 1;
    +
     1554.     78                source_line = source_line.slice(1);
    +
     1555.     78                snippet = "/=";
    +
     1556.     78                warn_at("unexpected_a", line, column, "/=");
    +
     1557.     78            }
    +
     1558.  17350        }
    +
     1559.  17350        return make(snippet);
    +
     1560.  17350    }
    +
     1561.    211
    +
     1562.    211    first = lex();
    +
     1563.    190    json_mode = first.id === "{" || first.id === "[";
    +
     1564.    211
    +
     1565.    211// This loop will be replaced with a recursive call to lex when ES6 has been
    +
     1566.    211// finished and widely deployed and adopted.
    +
     1567.    211
    +
     1568.  30641    while (true) {
    +
     1569.  30641        if (lex().id === "(end)") {
    +
     1570.  30641            break;
    +
     1571.  30641        }
    +
     1572.  30641    }
    +
     1573.    211}
    +
     1574.      1
    +
     1575.      1// Parsing:
    +
     1576.      1
    +
     1577.      1// Parsing weaves the tokens into an abstract syntax tree. During that process,
    +
     1578.      1// a token may be given any of these properties:
    +
     1579.      1
    +
     1580.      1//      arity       string
    +
     1581.      1//      label       identifier
    +
     1582.      1//      name        identifier
    +
     1583.      1//      expression  expressions
    +
     1584.      1//      block       statements
    +
     1585.      1//      else        statements (else, default, catch)
    +
     1586.      1
    +
     1587.      1// Specialized tokens may have additional properties.
    +
     1588.      1
    +
     1589.   2219function survey(name) {
    +
     1590.   2219    let id = name.id;
    +
     1591.   2219
    +
     1592.   2219// Tally the property name. If it is a string, only tally strings that conform
    +
     1593.   2219// to the identifier rules.
    +
     1594.   2219
    +
     1595.     91    if (id === "(string)") {
    +
     1596.     91        id = name.value;
    +
     1597.     91        if (!rx_identifier.test(id)) {
    +
     1598.     91            return id;
    +
     1599.     91        }
    +
     1600.   2128    } else if (id === "`") {
    +
     1601.   2128        if (name.value.length === 1) {
    +
     1602.   2128            id = name.value[0].value;
    +
     1603.      0            if (!rx_identifier.test(id)) {
    +
     1604.      0                return id;
    +
     1605.      0            }
    +
     1606.   2128        }
    +
     1607.      0    } else if (!name.identifier) {
    +
     1608.      0        return stop("expected_identifier_a", name);
    +
     1609.      0    }
    +
     1610.   2214
    +
     1611.   2214// If we have seen this name before, increment its count.
    +
     1612.   2214
    +
     1613.   2214    if (typeof property[id] === "number") {
    +
     1614.   1663        property[id] += 1;
    +
     1615.   1663
    +
     1616.   1663// If this is the first time seeing this property name, and if there is a
    +
     1617.   1663// tenure list, then it must be on the list. Otherwise, it must conform to
    +
     1618.   1663// the rules for good property names.
    +
     1619.   1663
    +
     1620.   1663    } else {
    +
     1621.    551        if (tenure !== undefined) {
    +
     1622.    551            if (tenure[id] !== true) {
    +
     1623.    551                warn("unregistered_property_a", name);
    +
     1624.    551            }
    +
     1625.    551        } else {
    +
     1626.    551            if (name.identifier && rx_bad_property.test(id)) {
    +
     1627.    551                warn("bad_property_a", name);
    +
     1628.    551            }
    +
     1629.    551        }
    +
     1630.    551        property[id] = 1;
    +
     1631.   2214    }
    +
     1632.   2214    return id;
    +
     1633.   2214}
    +
     1634.      1
    +
     1635.  31397function dispense() {
    +
     1636.  31397
    +
     1637.  31397// Deliver the next token, skipping the comments.
    +
     1638.  31397
    +
     1639.  31397    const cadet = tokens[token_nr];
    +
     1640.  31397    token_nr += 1;
    +
     1641.    511    if (cadet.id === "(comment)") {
    +
     1642.    511        if (json_mode) {
    +
     1643.    511            warn("unexpected_a", cadet);
    +
     1644.    511        }
    +
     1645.    511        return dispense();
    +
     1646.  30886    } else {
    +
     1647.  30886        return cadet;
    +
     1648.  30886    }
    +
     1649.  31397}
    +
     1650.      1
    +
     1651.    230function lookahead() {
    +
     1652.    230
    +
     1653.    230// Look ahead one token without advancing.
    +
     1654.    230
    +
     1655.    230    const old_token_nr = token_nr;
    +
     1656.    230    const cadet = dispense(true);
    +
     1657.    230    token_nr = old_token_nr;
    +
     1658.    230    return cadet;
    +
     1659.    230}
    +
     1660.      1
    +
     1661.  30660function advance(id, match) {
    +
     1662.  30660
    +
     1663.  30660// Produce the next token.
    +
     1664.  30660
    +
     1665.  30660// Attempt to give helpful names to anonymous functions.
    +
     1666.  30660
    +
     1667.   9950    if (token.identifier && token.id !== "function") {
    +
     1668.   9616        anon = token.id;
    +
     1669.  21044    } else if (token.id === "(string)" && rx_identifier.test(token.value)) {
    +
     1670.  21044        anon = token.value;
    +
     1671.  21044    }
    +
     1672.  30660
    +
     1673.  30660// Attempt to match next_token with an expected id.
    +
     1674.  30660
    +
     1675.  11396    if (id !== undefined && next_token.id !== id) {
    +
     1676.      4        return (
    +
     1677.      4            match === undefined
    +
     1678.      4            ? stop("expected_a_b", next_token, id, artifact())
    +
     1679.      4            : stop(
    +
     1680.      4                "expected_a_b_from_c_d",
    +
     1681.      4                next_token,
    +
     1682.      4                id,
    +
     1683.      4                artifact(match),
    +
     1684.      4                artifact_line(match),
    +
     1685.      4                artifact(next_token)
    +
     1686.      4            )
    +
     1687.      4        );
    +
     1688.  30656    }
    +
     1689.  30656
    +
     1690.  30656// Promote the tokens, skipping comments.
    +
     1691.  30656
    +
     1692.  30656    token = next_token;
    +
     1693.  30656    next_token = dispense();
    +
     1694.  30656    if (next_token.id === "(end)") {
    +
     1695.    329        token_nr -= 1;
    +
     1696.    329    }
    +
     1697.  30660}
    +
     1698.      1
    +
     1699.      1// Parsing of JSON is simple:
    +
     1700.      1
    +
     1701.     18function json_value() {
    +
     1702.     18    let negative;
    +
     1703.      7    if (next_token.id === "{") {
    +
     1704.      7        return (function json_object() {
    +
     1705.      7            const brace = next_token;
    +
     1706.      7            const object = empty();
    +
     1707.      7            const properties = [];
    +
     1708.      7            brace.expression = properties;
    +
     1709.      7            advance("{");
    +
     1710.      7            if (next_token.id !== "}") {
    +
     1711.      8                (function next() {
    +
     1712.      8                    let name;
    +
     1713.      8                    let value;
    +
     1714.      7                    if (next_token.quote !== "\"") {
    +
     1715.      7                        warn(
    +
     1716.      7                            "unexpected_a",
    +
     1717.      7                            next_token,
    +
     1718.      7                            next_token.quote
    +
     1719.      7                        );
    +
     1720.      7                    }
    +
     1721.      8                    name = next_token;
    +
     1722.      8                    advance("(string)");
    +
     1723.      7                    if (object[token.value] !== undefined) {
    +
     1724.      7                        warn("duplicate_a", token);
    +
     1725.      0                    } else if (token.value === "__proto__") {
    +
     1726.      0                        warn("bad_property_a", token);
    +
     1727.      0                    } else {
    +
     1728.      7                        object[token.value] = token;
    +
     1729.      7                    }
    +
     1730.      7                    advance(":");
    +
     1731.      7                    value = json_value();
    +
     1732.      7                    value.label = name;
    +
     1733.      7                    properties.push(value);
    +
     1734.      7                    if (next_token.id === ",") {
    +
     1735.      7                        advance(",");
    +
     1736.      7                        return next();
    +
     1737.      7                    }
    +
     1738.      8                }());
    +
     1739.      7            }
    +
     1740.      7            advance("}", brace);
    +
     1741.      7            return brace;
    +
     1742.      7        }());
    +
     1743.     11    }
    +
     1744.     11    if (next_token.id === "[") {
    +
     1745.      2        return (function json_array() {
    +
     1746.      2            const bracket = next_token;
    +
     1747.      2            const elements = [];
    +
     1748.      2            bracket.expression = elements;
    +
     1749.      2            advance("[");
    +
     1750.      2            if (next_token.id !== "]") {
    +
     1751.      2                (function next() {
    +
     1752.      2                    elements.push(json_value());
    +
     1753.      0                    if (next_token.id === ",") {
    +
     1754.      0                        advance(",");
    +
     1755.      0                        return next();
    +
     1756.      0                    }
    +
     1757.      2                }());
    +
     1758.      2            }
    +
     1759.      2            advance("]", bracket);
    +
     1760.      2            return bracket;
    +
     1761.      2        }());
    +
     1762.      9    }
    +
     1763.      9    if (
    +
     1764.      9        next_token.id === "true"
    +
     1765.      9        || next_token.id === "false"
    +
     1766.      9        || next_token.id === "null"
    +
     1767.      0    ) {
    +
     1768.      0        advance();
    +
     1769.      0        return token;
    +
     1770.      0    }
    +
     1771.      9    if (next_token.id === "(number)") {
    +
     1772.      4        if (!rx_JSON_number.test(next_token.value)) {
    +
     1773.      4            warn("unexpected_a");
    +
     1774.      4        }
    +
     1775.      4        advance();
    +
     1776.      4        return token;
    +
     1777.      5    }
    +
     1778.      5    if (next_token.id === "(string)") {
    +
     1779.      3        if (next_token.quote !== "\"") {
    +
     1780.      3            warn("unexpected_a", next_token, next_token.quote);
    +
     1781.      3        }
    +
     1782.      3        advance();
    +
     1783.      3        return token;
    +
     1784.      3    }
    +
     1785.      2    if (next_token.id === "-") {
    +
     1786.      1        negative = next_token;
    +
     1787.      1        negative.arity = "unary";
    +
     1788.      1        advance("-");
    +
     1789.      1        advance("(number)");
    +
     1790.      1        if (!rx_JSON_number.test(token.value)) {
    +
     1791.      1            warn("unexpected_a", token);
    +
     1792.      1        }
    +
     1793.      1        negative.expression = token;
    +
     1794.      1        return negative;
    +
     1795.      1    }
    +
     1796.      1    stop("unexpected_a");
    +
     1797.      1}
    +
     1798.      1
    +
     1799.      1// Now we parse JavaScript.
    +
     1800.      1
    +
     1801.    867function enroll(name, role, readonly) {
    +
     1802.    867
    +
     1803.    867// Enroll a name into the current function context. The role can be exception,
    +
     1804.    867// function, label, parameter, or variable. We look for variable redefinition
    +
     1805.    867// because it causes confusion.
    +
     1806.    867
    +
     1807.    867    const id = name.id;
    +
     1808.    867
    +
     1809.    867// Reserved words may not be enrolled.
    +
     1810.    867
    +
     1811.     13    if (syntax[id] !== undefined && id !== "ignore") {
    +
     1812.      1        warn("reserved_a", name);
    +
     1813.    866    } else {
    +
     1814.    866
    +
     1815.    866// Has the name been enrolled in this context?
    +
     1816.    866
    +
     1817.    866        let earlier = functionage.context[id];
    +
     1818.    866        if (earlier) {
    +
     1819.    866            warn(
    +
     1820.    866                "redefinition_a_b",
    +
     1821.    866                name,
    +
     1822.    866                name.id,
    +
     1823.    866                earlier.line + fudge
    +
     1824.    866            );
    +
     1825.    866
    +
     1826.    866// Has the name been enrolled in an outer context?
    +
     1827.    866
    +
     1828.    866        } else {
    +
     1829.   1078            stack.forEach(function (value) {
    +
     1830.   1078                const item = value.context[id];
    +
     1831.    866                if (item !== undefined) {
    +
     1832.    866                    earlier = item;
    +
     1833.    866                }
    +
     1834.   1078            });
    +
     1835.    866            if (earlier) {
    +
     1836.    866                if (id === "ignore") {
    +
     1837.    866                    if (earlier.role === "variable") {
    +
     1838.    866                        warn("unexpected_a", name);
    +
     1839.    866                    }
    +
     1840.    866                } else {
    +
     1841.    866                    if (
    +
     1842.    866                        (
    +
     1843.    866                            role !== "exception"
    +
     1844.      0                            || earlier.role !== "exception"
    +
     1845.    866                        )
    +
     1846.    866                        && role !== "parameter"
    +
     1847.    866                        && role !== "function"
    +
     1848.    866                    ) {
    +
     1849.    866                        warn(
    +
     1850.    866                            "redefinition_a_b",
    +
     1851.    866                            name,
    +
     1852.    866                            name.id,
    +
     1853.    866                            earlier.line + fudge
    +
     1854.    866                        );
    +
     1855.    866                    }
    +
     1856.    866                }
    +
     1857.    866            }
    +
     1858.    866
    +
     1859.    866// Enroll it.
    +
     1860.    866
    +
     1861.    866            functionage.context[id] = name;
    +
     1862.    866            name.dead = true;
    +
     1863.    866            name.parent = functionage;
    +
     1864.    866            name.init = false;
    +
     1865.    866            name.role = role;
    +
     1866.    866            name.used = 0;
    +
     1867.    866            name.writable = !readonly;
    +
     1868.    866        }
    +
     1869.    866    }
    +
     1870.    867}
    +
     1871.      1
    +
     1872.   8645function expression(rbp, initial) {
    +
     1873.   8645
    +
     1874.   8645// This is the heart of the Pratt parser. I retained Pratt's nomenclature.
    +
     1875.   8645// They are elements of the parsing method called Top Down Operator Precedence.
    +
     1876.   8645
    +
     1877.   8645// nud     Null denotation
    +
     1878.   8645// led     Left denotation
    +
     1879.   8645// lbp     Left binding power
    +
     1880.   8645// rbp     Right binding power
    +
     1881.   8645
    +
     1882.   8645// It processes a nud (variable, constant, prefix operator). It will then
    +
     1883.   8645// process leds (infix operators) until the bind powers cause it to stop. It
    +
     1884.   8645// returns the expression's parse tree.
    +
     1885.   8645
    +
     1886.   8645    let left;
    +
     1887.   8645    let the_symbol;
    +
     1888.   8645
    +
     1889.   8645// Statements will have already advanced, so advance now only if the token is
    +
     1890.   8645// not the first of a statement,
    +
     1891.   8645
    +
     1892.   6936    if (!initial) {
    +
     1893.   6936        advance();
    +
     1894.   6936    }
    +
     1895.   8645    the_symbol = syntax[token.id];
    +
     1896.   3971    if (the_symbol !== undefined && the_symbol.nud !== undefined) {
    +
     1897.   3968        left = the_symbol.nud();
    +
     1898.   4677    } else if (token.identifier) {
    +
     1899.   4677        left = token;
    +
     1900.   4677        left.arity = "variable";
    +
     1901.   4677    } else {
    +
     1902.   4677        return stop("unexpected_a", token);
    +
     1903.   8637    }
    +
     1904.  14499    (function right() {
    +
     1905.  14499        the_symbol = syntax[next_token.id];
    +
     1906.  14499        if (
    +
     1907.  14499            the_symbol !== undefined
    +
     1908.  14359            && the_symbol.led !== undefined
    +
     1909.   8637            && rbp < the_symbol.lbp
    +
     1910.   8637        ) {
    +
     1911.   8637            advance();
    +
     1912.   8637            left = the_symbol.led(left);
    +
     1913.   8637            return right();
    +
     1914.   8637        }
    +
     1915.  14499    }());
    +
     1916.   8637    return left;
    +
     1917.   8637}
    +
     1918.      1
    +
     1919.    631function condition() {
    +
     1920.    631
    +
     1921.    631// Parse the condition part of a do, if, while.
    +
     1922.    631
    +
     1923.    631    const the_paren = next_token;
    +
     1924.    631    let the_value;
    +
     1925.    631    the_paren.free = true;
    +
     1926.    631    advance("(");
    +
     1927.    631    the_value = expression(0);
    +
     1928.    631    advance(")");
    +
     1929.      1    if (the_value.wrapped === true) {
    +
     1930.      1        warn("unexpected_a", the_paren);
    +
     1931.      1    }
    +
     1932.     12    if (anticondition[the_value.id] === true) {
    +
     1933.     12        warn("unexpected_a", the_value);
    +
     1934.     12    }
    +
     1935.    631    return the_value;
    +
     1936.    631}
    +
     1937.      1
    +
     1938.   5459function is_weird(thing) {
    +
     1939.   5459    return (
    +
     1940.   5459        thing.id === "(regexp)"
    +
     1941.   5459        || thing.id === "{"
    +
     1942.   5459        || thing.id === "=>"
    +
     1943.   5459        || thing.id === "function"
    +
     1944.   5457        || (thing.id === "[" && thing.arity === "unary")
    +
     1945.   5459    );
    +
     1946.   5459}
    +
     1947.      1
    +
     1948.   1715function are_similar(a, b) {
    +
     1949.      0    if (a === b) {
    +
     1950.      0        return true;
    +
     1951.      0    }
    +
     1952.      0    if (Array.isArray(a)) {
    +
     1953.      0        return (
    +
     1954.      0            Array.isArray(b)
    +
     1955.      0            && a.length === b.length
    +
     1956.      0            && a.every(function (value, index) {
    +
     1957.      0                return are_similar(value, b[index]);
    +
     1958.      0            })
    +
     1959.      0        );
    +
     1960.      0    }
    +
     1961.      0    if (Array.isArray(b)) {
    +
     1962.      0        return false;
    +
     1963.      0    }
    +
     1964.     16    if (a.id === "(number)" && b.id === "(number)") {
    +
     1965.     14        return a.value === b.value;
    +
     1966.   1701    }
    +
     1967.   1701    let a_string;
    +
     1968.   1701    let b_string;
    +
     1969.   1701    if (a.id === "(string)") {
    +
     1970.    102        a_string = a.value;
    +
     1971.      0    } else if (a.id === "`" && a.constant) {
    +
     1972.      0        a_string = a.value[0];
    +
     1973.      0    }
    +
     1974.   1701    if (b.id === "(string)") {
    +
     1975.    689        b_string = b.value;
    +
     1976.      0    } else if (b.id === "`" && b.constant) {
    +
     1977.      0        b_string = b.value[0];
    +
     1978.      0    }
    +
     1979.   1701    if (typeof a_string === "string") {
    +
     1980.    102        return a_string === b_string;
    +
     1981.   1599    }
    +
     1982.   1599    if (is_weird(a) || is_weird(b)) {
    +
     1983.      2        return false;
    +
     1984.   1597    }
    +
     1985.   1597    if (a.arity === b.arity && a.id === b.id) {
    +
     1986.    409        if (a.id === ".") {
    +
     1987.    409            return (
    +
     1988.    409                are_similar(a.expression, b.expression)
    +
     1989.    409                && are_similar(a.name, b.name)
    +
     1990.    409            );
    +
     1991.    409        }
    +
     1992.    409        if (a.arity === "unary") {
    +
     1993.    409            return are_similar(a.expression, b.expression);
    +
     1994.    409        }
    +
     1995.    409        if (a.arity === "binary") {
    +
     1996.    409            return (
    +
     1997.    409                a.id !== "("
    +
     1998.    409                && are_similar(a.expression[0], b.expression[0])
    +
     1999.    409                && are_similar(a.expression[1], b.expression[1])
    +
     2000.    409            );
    +
     2001.    409        }
    +
     2002.      0        if (a.arity === "ternary") {
    +
     2003.      0            return (
    +
     2004.      0                are_similar(a.expression[0], b.expression[0])
    +
     2005.      0                && are_similar(a.expression[1], b.expression[1])
    +
     2006.      0                && are_similar(a.expression[2], b.expression[2])
    +
     2007.      0            );
    +
     2008.      0        }
    +
     2009.      0        if (a.arity === "function" && a.arity === "regexp") {
    +
     2010.      0            return false;
    +
     2011.      0        }
    +
     2012.    409        return true;
    +
     2013.   1188    }
    +
     2014.   1188    return false;
    +
     2015.   1188}
    +
     2016.      1
    +
     2017.   2087function semicolon() {
    +
     2018.   2087
    +
     2019.   2087// Try to match a semicolon.
    +
     2020.   2087
    +
     2021.   1991    if (next_token.id === ";") {
    +
     2022.   1991        advance(";");
    +
     2023.   1991    } else {
    +
     2024.     96        warn_at(
    +
     2025.     96            "expected_a_b",
    +
     2026.     96            token.line,
    +
     2027.     96            token.thru,
    +
     2028.     96            ";",
    +
     2029.     96            artifact(next_token)
    +
     2030.     96        );
    +
     2031.     96    }
    +
     2032.   2087    anon = "anonymous";
    +
     2033.   2087}
    +
     2034.      1
    +
     2035.   3196function statement() {
    +
     2036.   3196
    +
     2037.   3196// Parse a statement. Any statement may have a label, but only four statements
    +
     2038.   3196// have use for one. A statement can be one of the standard statements, or
    +
     2039.   3196// an assignment expression, or an invocation expression.
    +
     2040.   3196
    +
     2041.   3196    let first;
    +
     2042.   3196    let the_label;
    +
     2043.   3196    let the_statement;
    +
     2044.   3196    let the_symbol;
    +
     2045.   3196    advance();
    +
     2046.   3124    if (token.identifier && next_token.id === ":") {
    +
     2047.      5        the_label = token;
    +
     2048.      5        if (the_label.id === "ignore") {
    +
     2049.      5            warn("unexpected_a", the_label);
    +
     2050.      5        }
    +
     2051.      5        advance(":");
    +
     2052.      5        if (
    +
     2053.      5            next_token.id === "do"
    +
     2054.      5            || next_token.id === "for"
    +
     2055.      5            || next_token.id === "switch"
    +
     2056.      5            || next_token.id === "while"
    +
     2057.      5        ) {
    +
     2058.      5            enroll(the_label, "label", true);
    +
     2059.      5            the_label.init = true;
    +
     2060.      5            the_label.dead = false;
    +
     2061.      5            the_statement = statement();
    +
     2062.      5            the_statement.label = the_label;
    +
     2063.      5            the_statement.statement = true;
    +
     2064.      5            return the_statement;
    +
     2065.      5        }
    +
     2066.      5        advance();
    +
     2067.      5        warn("unexpected_label_a", the_label);
    +
     2068.   3195    }
    +
     2069.   3195
    +
     2070.   3195// Parse the statement.
    +
     2071.   3195
    +
     2072.   3195    first = token;
    +
     2073.   3195    first.statement = true;
    +
     2074.   3195    the_symbol = syntax[first.id];
    +
     2075.   3195    if (the_symbol !== undefined && the_symbol.fud !== undefined) {
    +
     2076.   1525        the_symbol.disrupt = false;
    +
     2077.   1525        the_symbol.statement = true;
    +
     2078.   1525        the_statement = the_symbol.fud();
    +
     2079.   1670    } else {
    +
     2080.   1670
    +
     2081.   1670// It is an expression statement.
    +
     2082.   1670
    +
     2083.   1670        the_statement = expression(0, true);
    +
     2084.   1670        if (the_statement.wrapped && the_statement.id !== "(") {
    +
     2085.   1670            warn("unexpected_a", first);
    +
     2086.   1670        }
    +
     2087.   1670        semicolon();
    +
     2088.   3166    }
    +
     2089.   3166    if (the_label !== undefined) {
    +
     2090.      2        the_label.dead = true;
    +
     2091.   3166    }
    +
     2092.   3166    return the_statement;
    +
     2093.   3166}
    +
     2094.      1
    +
     2095.   1255function statements() {
    +
     2096.   1255
    +
     2097.   1255// Parse a list of statements. Give a warning if an unreachable statement
    +
     2098.   1255// follows a disruptive statement.
    +
     2099.   1255
    +
     2100.   1255    const array = [];
    +
     2101.   4350    (function next(disrupt) {
    +
     2102.   4350        if (
    +
     2103.   4350            next_token.id !== "}"
    +
     2104.   3284            && next_token.id !== "case"
    +
     2105.   3282            && next_token.id !== "default"
    +
     2106.   3279            && next_token.id !== "else"
    +
     2107.   3279            && next_token.id !== "(end)"
    +
     2108.   3124        ) {
    +
     2109.   3124            let a_statement = statement();
    +
     2110.   3124            array.push(a_statement);
    +
     2111.   3124            if (disrupt) {
    +
     2112.   3124                warn("unreachable_a", a_statement);
    +
     2113.   3124            }
    +
     2114.   3124            return next(a_statement.disrupt);
    +
     2115.   3124        }
    +
     2116.   4350    }(false));
    +
     2117.   1255    return array;
    +
     2118.   1255}
    +
     2119.      1
    +
     2120.    314function not_top_level(thing) {
    +
     2121.    314
    +
     2122.    314// Some features should not be at the outermost level.
    +
     2123.    314
    +
     2124.      9    if (functionage === global) {
    +
     2125.      9        warn("unexpected_at_top_level_a", thing);
    +
     2126.      9    }
    +
     2127.    314}
    +
     2128.      1
    +
     2129.     15function top_level_only(the_thing) {
    +
     2130.     15
    +
     2131.     15// Some features must be at the most outermost level.
    +
     2132.     15
    +
     2133.      1    if (blockage !== global) {
    +
     2134.      1        warn("misplaced_a", the_thing);
    +
     2135.      1    }
    +
     2136.     15}
    +
     2137.      1
    +
     2138.   1063function block(special) {
    +
     2139.   1063
    +
     2140.   1063// Parse a block, a sequence of statements wrapped in braces.
    +
     2141.   1063//  special "body"      The block is a function body.
    +
     2142.   1063//          "ignore"    No warning on an empty block.
    +
     2143.   1063//          "naked"     No advance.
    +
     2144.   1063//          undefined   An ordinary block.
    +
     2145.   1063
    +
     2146.   1063    let stmts;
    +
     2147.   1063    let the_block;
    +
     2148.   1060    if (special !== "naked") {
    +
     2149.   1060        advance("{");
    +
     2150.   1060    }
    +
     2151.   1063    the_block = token;
    +
     2152.   1063    the_block.arity = "statement";
    +
     2153.   1063    the_block.body = special === "body";
    +
     2154.   1063
    +
     2155.   1063// Top level function bodies may include the "use strict" pragma.
    +
     2156.   1063
    +
     2157.   1063    if (
    +
     2158.   1063        special === "body"
    +
     2159.    330        && stack.length === 1
    +
     2160.    181        && next_token.value === "use strict"
    +
     2161.      5    ) {
    +
     2162.      5        next_token.statement = true;
    +
     2163.      5        advance("(string)");
    +
     2164.      5        advance(";");
    +
     2165.      5    }
    +
     2166.   1063    stmts = statements();
    +
     2167.   1063    the_block.block = stmts;
    +
     2168.     29    if (stmts.length === 0) {
    +
     2169.     29        if (!option.devel && special !== "ignore") {
    +
     2170.     29            warn("empty_block", the_block);
    +
     2171.     29        }
    +
     2172.     29        the_block.disrupt = false;
    +
     2173.   1033    } else {
    +
     2174.   1033        the_block.disrupt = stmts[stmts.length - 1].disrupt;
    +
     2175.   1062    }
    +
     2176.   1062    advance("}");
    +
     2177.   1062    return the_block;
    +
     2178.   1062}
    +
     2179.      1
    +
     2180.    707function mutation_check(the_thing) {
    +
     2181.    707
    +
     2182.    707// The only expressions that may be assigned to are
    +
     2183.    707//      e.b
    +
     2184.    707//      e[b]
    +
     2185.    707//      v
    +
     2186.    707//      [destructure]
    +
     2187.    707//      {destructure}
    +
     2188.    707
    +
     2189.    707    if (
    +
     2190.    707        the_thing.arity !== "variable"
    +
     2191.    332        && the_thing.id !== "."
    +
     2192.     29        && the_thing.id !== "["
    +
     2193.      1        && the_thing.id !== "{"
    +
     2194.      1    ) {
    +
     2195.      1        warn("bad_assignment_a", the_thing);
    +
     2196.      1        return false;
    +
     2197.    706    }
    +
     2198.    706    return true;
    +
     2199.    706}
    +
     2200.      1
    +
     2201.   3627function left_check(left, right) {
    +
     2202.   3627
    +
     2203.   3627// Warn if the left is not one of these:
    +
     2204.   3627//      e.b
    +
     2205.   3627//      e[b]
    +
     2206.   3627//      e()
    +
     2207.   3627//      ?:
    +
     2208.   3627//      identifier
    +
     2209.   3627
    +
     2210.   3627    const id = left.id;
    +
     2211.   3627    if (
    +
     2212.   3627        !left.identifier
    +
     2213.    840        && (
    +
     2214.    840            left.arity !== "ternary"
    +
     2215.    840            || (
    +
     2216.    840                !left_check(left.expression[1])
    +
     2217.    840                && !left_check(left.expression[2])
    +
     2218.    840            )
    +
     2219.    840        )
    +
     2220.    840        && (
    +
     2221.    840            left.arity !== "binary"
    +
     2222.    840            || (id !== "." && id !== "(" && id !== "[")
    +
     2223.    840        )
    +
     2224.      4    ) {
    +
     2225.      4        warn("unexpected_a", right);
    +
     2226.      4        return false;
    +
     2227.   3623    }
    +
     2228.   3623    return true;
    +
     2229.   3623}
    +
     2230.      1
    +
     2231.      1// These functions are used to specify the grammar of our language:
    +
     2232.      1
    +
     2233.    129function symbol(id, bp) {
    +
     2234.    129
    +
     2235.    129// Make a symbol if it does not already exist in the language's syntax.
    +
     2236.    129
    +
     2237.    129    let the_symbol = syntax[id];
    +
     2238.    113    if (the_symbol === undefined) {
    +
     2239.    113        the_symbol = empty();
    +
     2240.    113        the_symbol.id = id;
    +
     2241.    113        the_symbol.lbp = bp || 0;
    +
     2242.    113        syntax[id] = the_symbol;
    +
     2243.    113    }
    +
     2244.    129    return the_symbol;
    +
     2245.    129}
    +
     2246.      1
    +
     2247.     12function assignment(id) {
    +
     2248.     12
    +
     2249.     12// Make an assignment operator. The one true assignment is different because
    +
     2250.     12// its left side, when it is a variable, is not treated as an expression.
    +
     2251.     12// That case is special because that is when a variable gets initialized. The
    +
     2252.     12// other assignment operators can modify, but they cannot initialize.
    +
     2253.     12
    +
     2254.     12    const the_symbol = symbol(id, 20);
    +
     2255.    704    the_symbol.led = function (left) {
    +
     2256.    704        const the_token = token;
    +
     2257.    704        let right;
    +
     2258.    704        the_token.arity = "assignment";
    +
     2259.    704        right = expression(20 - 1);
    +
     2260.    630        if (id === "=" && left.arity === "variable") {
    +
     2261.    318            the_token.names = left;
    +
     2262.    318            the_token.expression = right;
    +
     2263.    386        } else {
    +
     2264.    386            the_token.expression = [left, right];
    +
     2265.    386        }
    +
     2266.    704        if (
    +
     2267.    704            right.arity === "assignment"
    +
     2268.    704            || right.arity === "pre"
    +
     2269.    704            || right.arity === "post"
    +
     2270.      1        ) {
    +
     2271.      1            warn("unexpected_a", right);
    +
     2272.      1        }
    +
     2273.    704        mutation_check(left);
    +
     2274.    704        return the_token;
    +
     2275.    704    };
    +
     2276.     12    return the_symbol;
    +
     2277.     12}
    +
     2278.      1
    +
     2279.     16function constant(id, type, value) {
    +
     2280.     16
    +
     2281.     16// Make a constant symbol.
    +
     2282.     16
    +
     2283.     16    const the_symbol = symbol(id);
    +
     2284.     16    the_symbol.constant = true;
    +
     2285.     16    the_symbol.nud = (
    +
     2286.     16        typeof value === "function"
    +
     2287.      7        ? value
    +
     2288.   3144        : function () {
    +
     2289.   3144            token.constant = true;
    +
     2290.    260            if (value !== undefined) {
    +
     2291.    260                token.value = value;
    +
     2292.    260            }
    +
     2293.   3144            return token;
    +
     2294.   3144        }
    +
     2295.     16    );
    +
     2296.     16    the_symbol.type = type;
    +
     2297.     16    the_symbol.value = value;
    +
     2298.     16    return the_symbol;
    +
     2299.     16}
    +
     2300.      1
    +
     2301.     30function infix(id, bp, f) {
    +
     2302.     30
    +
     2303.     30// Make an infix operator.
    +
     2304.     30
    +
     2305.     30    const the_symbol = symbol(id, bp);
    +
     2306.   5117    the_symbol.led = function (left) {
    +
     2307.   5117        const the_token = token;
    +
     2308.   5117        the_token.arity = "binary";
    +
     2309.   3677        if (f !== undefined) {
    +
     2310.   3677            return f(left);
    +
     2311.   3677        }
    +
     2312.   1440        the_token.expression = [left, expression(bp)];
    +
     2313.   1440        return the_token;
    +
     2314.   1440    };
    +
     2315.     30    return the_symbol;
    +
     2316.     30}
    +
     2317.      1
    +
     2318.      1function infixr(id, bp) {
    +
     2319.      1
    +
     2320.      1// Make a right associative infix operator.
    +
     2321.      1
    +
     2322.      1    const the_symbol = symbol(id, bp);
    +
     2323.      0    the_symbol.led = function (left) {
    +
     2324.      0        const the_token = token;
    +
     2325.      0        the_token.arity = "binary";
    +
     2326.      0        the_token.expression = [left, expression(bp - 1)];
    +
     2327.      0        return the_token;
    +
     2328.      0    };
    +
     2329.      1    return the_symbol;
    +
     2330.      1}
    +
     2331.      1
    +
     2332.      2function post(id) {
    +
     2333.      2
    +
     2334.      2// Make one of the post operators.
    +
     2335.      2
    +
     2336.      2    const the_symbol = symbol(id, 150);
    +
     2337.      3    the_symbol.led = function (left) {
    +
     2338.      3        token.expression = left;
    +
     2339.      3        token.arity = "post";
    +
     2340.      3        mutation_check(token.expression);
    +
     2341.      3        return token;
    +
     2342.      3    };
    +
     2343.      2    return the_symbol;
    +
     2344.      2}
    +
     2345.      1
    +
     2346.      2function pre(id) {
    +
     2347.      2
    +
     2348.      2// Make one of the pre operators.
    +
     2349.      2
    +
     2350.      2    const the_symbol = symbol(id);
    +
     2351.      0    the_symbol.nud = function () {
    +
     2352.      0        const the_token = token;
    +
     2353.      0        the_token.arity = "pre";
    +
     2354.      0        the_token.expression = expression(150);
    +
     2355.      0        mutation_check(the_token.expression);
    +
     2356.      0        return the_token;
    +
     2357.      0    };
    +
     2358.      2    return the_symbol;
    +
     2359.      2}
    +
     2360.      1
    +
     2361.     17function prefix(id, f) {
    +
     2362.     17
    +
     2363.     17// Make a prefix operator.
    +
     2364.     17
    +
     2365.     17    const the_symbol = symbol(id);
    +
     2366.    814    the_symbol.nud = function () {
    +
     2367.    814        const the_token = token;
    +
     2368.    814        the_token.arity = "unary";
    +
     2369.    682        if (typeof f === "function") {
    +
     2370.    682            return f();
    +
     2371.    682        }
    +
     2372.    132        the_token.expression = expression(150);
    +
     2373.    132        return the_token;
    +
     2374.    132    };
    +
     2375.     17    return the_symbol;
    +
     2376.     17}
    +
     2377.      1
    +
     2378.     22function stmt(id, f) {
    +
     2379.     22
    +
     2380.     22// Make a statement.
    +
     2381.     22
    +
     2382.     22    const the_symbol = symbol(id);
    +
     2383.   1525    the_symbol.fud = function () {
    +
     2384.   1525        token.arity = "statement";
    +
     2385.   1525        return f();
    +
     2386.   1525    };
    +
     2387.     22    return the_symbol;
    +
     2388.     22}
    +
     2389.      1
    +
     2390.      1function ternary(id1, id2) {
    +
     2391.      1
    +
     2392.      1// Make a ternary operator.
    +
     2393.      1
    +
     2394.      1    const the_symbol = symbol(id1, 30);
    +
     2395.     41    the_symbol.led = function (left) {
    +
     2396.     41        const the_token = token;
    +
     2397.     41        const second = expression(20);
    +
     2398.     41        advance(id2);
    +
     2399.     41        token.arity = "ternary";
    +
     2400.     41        the_token.arity = "ternary";
    +
     2401.     41        the_token.expression = [left, second, expression(10)];
    +
     2402.      2        if (next_token.id !== ")") {
    +
     2403.      2            warn("use_open", the_token);
    +
     2404.      2        }
    +
     2405.     41        return the_token;
    +
     2406.     41    };
    +
     2407.      1    return the_symbol;
    +
     2408.      1}
    +
     2409.      1
    +
     2410.      1// Begin defining the language.
    +
     2411.      1
    +
     2412.      1syntax = empty();
    +
     2413.      1
    +
     2414.      1symbol("}");
    +
     2415.      1symbol(")");
    +
     2416.      1symbol("]");
    +
     2417.      1symbol(",");
    +
     2418.      1symbol(";");
    +
     2419.      1symbol(":");
    +
     2420.      1symbol("*/");
    +
     2421.      1symbol("async");
    +
     2422.      1symbol("await");
    +
     2423.      1symbol("case");
    +
     2424.      1symbol("catch");
    +
     2425.      1symbol("class");
    +
     2426.      1symbol("default");
    +
     2427.      1symbol("else");
    +
     2428.      1symbol("enum");
    +
     2429.      1symbol("finally");
    +
     2430.      1symbol("implements");
    +
     2431.      1symbol("interface");
    +
     2432.      1symbol("package");
    +
     2433.      1symbol("private");
    +
     2434.      1symbol("protected");
    +
     2435.      1symbol("public");
    +
     2436.      1symbol("static");
    +
     2437.      1symbol("super");
    +
     2438.      1symbol("void");
    +
     2439.      1symbol("yield");
    +
     2440.      1
    +
     2441.      1constant("(number)", "number");
    +
     2442.      1constant("(regexp)", "regexp");
    +
     2443.      1constant("(string)", "string");
    +
     2444.      1constant("arguments", "object", function () {
    +
     2445.      1    warn("unexpected_a", token);
    +
     2446.      1    return token;
    +
     2447.      1});
    +
     2448.      2constant("eval", "function", function () {
    +
     2449.      1    if (!option.eval) {
    +
     2450.      1        warn("unexpected_a", token);
    +
     2451.      1    } else if (next_token.id !== "(") {
    +
     2452.      1        warn("expected_a_before_b", next_token, "(", artifact());
    +
     2453.      1    }
    +
     2454.      2    return token;
    +
     2455.      2});
    +
     2456.      1constant("false", "boolean", false);
    +
     2457.      3constant("Function", "function", function () {
    +
     2458.      2    if (!option.eval) {
    +
     2459.      2        warn("unexpected_a", token);
    +
     2460.      2    } else if (next_token.id !== "(") {
    +
     2461.      1        warn("expected_a_before_b", next_token, "(", artifact());
    +
     2462.      1    }
    +
     2463.      3    return token;
    +
     2464.      3});
    +
     2465.      1constant("ignore", "undefined", function () {
    +
     2466.      1    warn("unexpected_a", token);
    +
     2467.      1    return token;
    +
     2468.      1});
    +
     2469.      1constant("Infinity", "number", Infinity);
    +
     2470.      1constant("isFinite", "function", function () {
    +
     2471.      1    warn("expected_a_b", token, "Number.isFinite", "isFinite");
    +
     2472.      1    return token;
    +
     2473.      1});
    +
     2474.      1constant("isNaN", "function", function () {
    +
     2475.      1    warn("number_isNaN", token);
    +
     2476.      1    return token;
    +
     2477.      1});
    +
     2478.      1constant("NaN", "number", NaN);
    +
     2479.      1constant("null", "null", null);
    +
     2480.      1constant("this", "object", function () {
    +
     2481.      1    if (!option.this) {
    +
     2482.      1        warn("unexpected_a", token);
    +
     2483.      1    }
    +
     2484.      1    return token;
    +
     2485.      1});
    +
     2486.      1constant("true", "boolean", true);
    +
     2487.      1constant("undefined", "undefined");
    +
     2488.      1
    +
     2489.      1assignment("=");
    +
     2490.      1assignment("+=");
    +
     2491.      1assignment("-=");
    +
     2492.      1assignment("*=");
    +
     2493.      1assignment("/=");
    +
     2494.      1assignment("%=");
    +
     2495.      1assignment("&=");
    +
     2496.      1assignment("|=");
    +
     2497.      1assignment("^=");
    +
     2498.      1assignment("<<=");
    +
     2499.      1assignment(">>=");
    +
     2500.      1assignment(">>>=");
    +
     2501.      1
    +
     2502.      1infix("??", 35);
    +
     2503.      1infix("||", 40);
    +
     2504.      1infix("&&", 50);
    +
     2505.      1infix("|", 70);
    +
     2506.      1infix("^", 80);
    +
     2507.      1infix("&", 90);
    +
     2508.      1infix("==", 100);
    +
     2509.      1infix("===", 100);
    +
     2510.      1infix("!=", 100);
    +
     2511.      1infix("!==", 100);
    +
     2512.      1infix("<", 110);
    +
     2513.      1infix(">", 110);
    +
     2514.      1infix("<=", 110);
    +
     2515.      1infix(">=", 110);
    +
     2516.      1infix("in", 110);
    +
     2517.      1infix("instanceof", 110);
    +
     2518.      1infix("<<", 120);
    +
     2519.      1infix(">>", 120);
    +
     2520.      1infix(">>>", 120);
    +
     2521.      1infix("+", 130);
    +
     2522.      1infix("-", 130);
    +
     2523.      1infix("*", 140);
    +
     2524.      1infix("/", 140);
    +
     2525.      1infix("%", 140);
    +
     2526.      1infixr("**", 150);
    +
     2527.   1578infix("(", 160, function (left) {
    +
     2528.   1578    const the_paren = token;
    +
     2529.   1578    let the_argument;
    +
     2530.   1545    if (left.id !== "function") {
    +
     2531.   1545        left_check(left, the_paren);
    +
     2532.   1545    }
    +
     2533.    487    if (functionage.arity === "statement" && left.identifier) {
    +
     2534.    347        functionage.name.calls[left.id] = left;
    +
     2535.    347    }
    +
     2536.   1578    the_paren.expression = [left];
    +
     2537.   1299    if (next_token.id !== ")") {
    +
     2538.   2126        (function next() {
    +
     2539.   2126            let ellipsis;
    +
     2540.   1299            if (next_token.id === "...") {
    +
     2541.   1299                ellipsis = true;
    +
     2542.   1299                advance("...");
    +
     2543.   1299            }
    +
     2544.   2126            the_argument = expression(10);
    +
     2545.   1299            if (ellipsis) {
    +
     2546.   1299                the_argument.ellipsis = true;
    +
     2547.   1299            }
    +
     2548.   2126            the_paren.expression.push(the_argument);
    +
     2549.   1299            if (next_token.id === ",") {
    +
     2550.   1299                advance(",");
    +
     2551.   1299                return next();
    +
     2552.   1299            }
    +
     2553.   2126        }());
    +
     2554.   1299    }
    +
     2555.   1578    advance(")", the_paren);
    +
     2556.    772    if (the_paren.expression.length === 2) {
    +
     2557.    772        the_paren.free = true;
    +
     2558.    772        if (the_argument.wrapped === true) {
    +
     2559.    772            warn("unexpected_a", the_paren);
    +
     2560.    772        }
    +
     2561.    772        if (the_argument.id === "(") {
    +
     2562.    772            the_argument.wrapped = true;
    +
     2563.    772        }
    +
     2564.    806    } else {
    +
     2565.    806        the_paren.free = false;
    +
     2566.    806    }
    +
     2567.   1578    return the_paren;
    +
     2568.   1578});
    +
     2569.   1864infix(".", 170, function (left) {
    +
     2570.   1864    const the_token = token;
    +
     2571.   1864    const name = next_token;
    +
     2572.   1864    if (
    +
     2573.   1864        (
    +
     2574.   1864            left.id !== "(string)"
    +
     2575.      7            || (name.id !== "indexOf" && name.id !== "repeat")
    +
     2576.   1864        )
    +
     2577.   1857        && (
    +
     2578.   1857            left.id !== "["
    +
     2579.   1857            || (
    +
     2580.   1857                name.id !== "concat"
    +
     2581.   1857                && name.id !== "forEach"
    +
     2582.   1857                && name.id !== "join"
    +
     2583.   1857                && name.id !== "map"
    +
     2584.   1857            )
    +
     2585.   1857        )
    +
     2586.   1855        && (left.id !== "+" || name.id !== "slice")
    +
     2587.   1854        && (
    +
     2588.   1854            left.id !== "(regexp)"
    +
     2589.   1854            || (name.id !== "exec" && name.id !== "test")
    +
     2590.   1854        )
    +
     2591.   1846    ) {
    +
     2592.   1846        left_check(left, the_token);
    +
     2593.   1846    }
    +
     2594.      1    if (!name.identifier) {
    +
     2595.      1        stop("expected_identifier_a");
    +
     2596.   1863    }
    +
     2597.   1863    advance();
    +
     2598.   1863    survey(name);
    +
     2599.   1863
    +
     2600.   1863// The property name is not an expression.
    +
     2601.   1863
    +
     2602.   1863    the_token.name = name;
    +
     2603.   1863    the_token.expression = left;
    +
     2604.   1863    return the_token;
    +
     2605.   1863});
    +
     2606.      1infix("?.", 170, function (left) {
    +
     2607.      1    const the_token = token;
    +
     2608.      1    const name = next_token;
    +
     2609.      1    if (
    +
     2610.      1        (
    +
     2611.      1            left.id !== "(string)"
    +
     2612.      0            || (name.id !== "indexOf" && name.id !== "repeat")
    +
     2613.      1        )
    +
     2614.      1        && (
    +
     2615.      1            left.id !== "["
    +
     2616.      0            || (
    +
     2617.      0                name.id !== "concat"
    +
     2618.      0                && name.id !== "forEach"
    +
     2619.      0                && name.id !== "join"
    +
     2620.      0                && name.id !== "map"
    +
     2621.      0            )
    +
     2622.      1        )
    +
     2623.      0        && (left.id !== "+" || name.id !== "slice")
    +
     2624.      1        && (
    +
     2625.      1            left.id !== "(regexp)"
    +
     2626.      0            || (name.id !== "exec" && name.id !== "test")
    +
     2627.      1        )
    +
     2628.      1    ) {
    +
     2629.      1        left_check(left, the_token);
    +
     2630.      1    }
    +
     2631.      1    if (!name.identifier) {
    +
     2632.      1        stop("expected_identifier_a");
    +
     2633.      0    }
    +
     2634.      0    advance();
    +
     2635.      0    survey(name);
    +
     2636.      0
    +
     2637.      0// The property name is not an expression.
    +
     2638.      0
    +
     2639.      0    the_token.name = name;
    +
     2640.      0    the_token.expression = left;
    +
     2641.      0    return the_token;
    +
     2642.      0});
    +
     2643.    221infix("[", 170, function (left) {
    +
     2644.    221    const the_token = token;
    +
     2645.    221    const the_subscript = expression(0);
    +
     2646.    220    if (the_subscript.id === "(string)" || the_subscript.id === "`") {
    +
     2647.      2        const name = survey(the_subscript);
    +
     2648.      2        if (rx_identifier.test(name)) {
    +
     2649.      2            warn("subscript_a", the_subscript, name);
    +
     2650.      2        }
    +
     2651.      2    }
    +
     2652.    221    left_check(left, the_token);
    +
     2653.    221    the_token.expression = [left, the_subscript];
    +
     2654.    221    advance("]");
    +
     2655.    221    return the_token;
    +
     2656.    221});
    +
     2657.      1infix("=>", 170, function (left) {
    +
     2658.      1    return stop("wrap_parameter", left);
    +
     2659.      1});
    +
     2660.      1
    +
     2661.     26function do_tick() {
    +
     2662.     26    const the_tick = token;
    +
     2663.     26    the_tick.value = [];
    +
     2664.     26    the_tick.expression = [];
    +
     2665.     26    if (next_token.id !== "`") {
    +
     2666.     56        (function part() {
    +
     2667.     56            advance("(string)");
    +
     2668.     56            the_tick.value.push(token);
    +
     2669.     30            if (next_token.id === "${") {
    +
     2670.     30                advance("${");
    +
     2671.     30                the_tick.expression.push(expression(0));
    +
     2672.     30                advance("}");
    +
     2673.     30                return part();
    +
     2674.     30            }
    +
     2675.     56        }());
    +
     2676.     26    }
    +
     2677.     26    advance("`");
    +
     2678.     26    return the_tick;
    +
     2679.     26}
    +
     2680.      1
    +
     2681.     12infix("`", 160, function (left) {
    +
     2682.     12    const the_tick = do_tick();
    +
     2683.     12    left_check(left, the_tick);
    +
     2684.     12    the_tick.expression = [left].concat(the_tick.expression);
    +
     2685.     12    return the_tick;
    +
     2686.     12});
    +
     2687.      1
    +
     2688.      1post("++");
    +
     2689.      1post("--");
    +
     2690.      1pre("++");
    +
     2691.      1pre("--");
    +
     2692.      1
    +
     2693.      1prefix("+");
    +
     2694.      1prefix("-");
    +
     2695.      1prefix("~");
    +
     2696.      1prefix("!");
    +
     2697.      1prefix("!!");
    +
     2698.    158prefix("[", function () {
    +
     2699.    158    const the_token = token;
    +
     2700.    158    the_token.expression = [];
    +
     2701.    110    if (next_token.id !== "]") {
    +
     2702.    446        (function next() {
    +
     2703.    446            let element;
    +
     2704.    446            let ellipsis = false;
    +
     2705.      0            if (next_token.id === "...") {
    +
     2706.      0                ellipsis = true;
    +
     2707.      0                advance("...");
    +
     2708.      0            }
    +
     2709.    446            element = expression(10);
    +
     2710.      0            if (ellipsis) {
    +
     2711.      0                element.ellipsis = true;
    +
     2712.      0            }
    +
     2713.    446            the_token.expression.push(element);
    +
     2714.    336            if (next_token.id === ",") {
    +
     2715.    336                advance(",");
    +
     2716.    336                return next();
    +
     2717.    336            }
    +
     2718.    446        }());
    +
     2719.    110    }
    +
     2720.    158    advance("]");
    +
     2721.    158    return the_token;
    +
     2722.    158});
    +
     2723.      1prefix("/=", function () {
    +
     2724.      1    stop("expected_a_b", token, "/\\=", "/=");
    +
     2725.      1});
    +
     2726.      1prefix("=>", function () {
    +
     2727.      1    return stop("expected_a_before_b", token, "()", "=>");
    +
     2728.      1});
    +
     2729.     15prefix("new", function () {
    +
     2730.     15    const the_new = token;
    +
     2731.     15    const right = expression(160);
    +
     2732.      1    if (next_token.id !== "(") {
    +
     2733.      1        warn("expected_a_before_b", next_token, "()", artifact(next_token));
    +
     2734.      1    }
    +
     2735.     15    the_new.expression = right;
    +
     2736.     15    return the_new;
    +
     2737.     15});
    +
     2738.      1prefix("typeof");
    +
     2739.      1prefix("void", function () {
    +
     2740.      1    const the_void = token;
    +
     2741.      1    warn("unexpected_a", the_void);
    +
     2742.      1    the_void.expression = expression(0);
    +
     2743.      1    return the_void;
    +
     2744.      1});
    +
     2745.      1
    +
     2746.    336function parameter_list() {
    +
     2747.    336    const list = [];
    +
     2748.    336    let optional;
    +
     2749.    336    const signature = ["("];
    +
     2750.    198    if (next_token.id !== ")" && next_token.id !== "(end)") {
    +
     2751.    279        (function parameter() {
    +
     2752.    279            let ellipsis = false;
    +
     2753.    279            let param;
    +
     2754.    198            if (next_token.id === "{") {
    +
     2755.    198                if (optional !== undefined) {
    +
     2756.    198                    warn(
    +
     2757.    198                        "required_a_optional_b",
    +
     2758.    198                        next_token,
    +
     2759.    198                        next_token.id,
    +
     2760.    198                        optional.id
    +
     2761.    198                    );
    +
     2762.    198                }
    +
     2763.    198                param = next_token;
    +
     2764.    198                param.names = [];
    +
     2765.    198                advance("{");
    +
     2766.    198                signature.push("{");
    +
     2767.    198                (function subparameter() {
    +
     2768.    198                    let subparam = next_token;
    +
     2769.    198                    if (!subparam.identifier) {
    +
     2770.    198                        return stop("expected_identifier_a");
    +
     2771.    198                    }
    +
     2772.    198                    survey(subparam);
    +
     2773.    198                    advance();
    +
     2774.    198                    signature.push(subparam.id);
    +
     2775.    198                    if (next_token.id === ":") {
    +
     2776.    198                        advance(":");
    +
     2777.    198                        advance();
    +
     2778.    198                        token.label = subparam;
    +
     2779.    198                        subparam = token;
    +
     2780.    198                        if (!subparam.identifier) {
    +
     2781.    198                            return stop("expected_identifier_a");
    +
     2782.    198                        }
    +
     2783.    198                    }
    +
     2784.    198                    if (next_token.id === "=") {
    +
     2785.    198                        advance("=");
    +
     2786.    198                        subparam.expression = expression();
    +
     2787.    198                        param.open = true;
    +
     2788.    198                    }
    +
     2789.    198                    param.names.push(subparam);
    +
     2790.    198                    if (next_token.id === ",") {
    +
     2791.    198                        advance(",");
    +
     2792.    198                        signature.push(", ");
    +
     2793.    198                        return subparameter();
    +
     2794.    198                    }
    +
     2795.    198                }());
    +
     2796.    198                list.push(param);
    +
     2797.    198                advance("}");
    +
     2798.    198                signature.push("}");
    +
     2799.    198                if (next_token.id === ",") {
    +
     2800.    198                    advance(",");
    +
     2801.    198                    signature.push(", ");
    +
     2802.    198                    return parameter();
    +
     2803.    198                }
    +
     2804.    259            } else if (next_token.id === "[") {
    +
     2805.    259                if (optional !== undefined) {
    +
     2806.    259                    warn(
    +
     2807.    259                        "required_a_optional_b",
    +
     2808.    259                        next_token,
    +
     2809.    259                        next_token.id,
    +
     2810.    259                        optional.id
    +
     2811.    259                    );
    +
     2812.    259                }
    +
     2813.    259                param = next_token;
    +
     2814.    259                param.names = [];
    +
     2815.    259                advance("[");
    +
     2816.    259                signature.push("[]");
    +
     2817.    259                (function subparameter() {
    +
     2818.    259                    const subparam = next_token;
    +
     2819.    259                    if (!subparam.identifier) {
    +
     2820.    259                        return stop("expected_identifier_a");
    +
     2821.    259                    }
    +
     2822.    259                    advance();
    +
     2823.    259                    param.names.push(subparam);
    +
     2824.    259                    if (next_token.id === "=") {
    +
     2825.    259                        advance("=");
    +
     2826.    259                        subparam.expression = expression();
    +
     2827.    259                        param.open = true;
    +
     2828.    259                    }
    +
     2829.    259                    if (next_token.id === ",") {
    +
     2830.    259                        advance(",");
    +
     2831.    259                        return subparameter();
    +
     2832.    259                    }
    +
     2833.    259                }());
    +
     2834.    259                list.push(param);
    +
     2835.    259                advance("]");
    +
     2836.    259                if (next_token.id === ",") {
    +
     2837.    259                    advance(",");
    +
     2838.    259                    signature.push(", ");
    +
     2839.    259                    return parameter();
    +
     2840.    259                }
    +
     2841.    259            } else {
    +
     2842.    259                if (next_token.id === "...") {
    +
     2843.    259                    ellipsis = true;
    +
     2844.    259                    signature.push("...");
    +
     2845.    259                    advance("...");
    +
     2846.    259                    if (optional !== undefined) {
    +
     2847.    259                        warn(
    +
     2848.    259                            "required_a_optional_b",
    +
     2849.    259                            next_token,
    +
     2850.    259                            next_token.id,
    +
     2851.    259                            optional.id
    +
     2852.    259                        );
    +
     2853.    259                    }
    +
     2854.    259                }
    +
     2855.    259                if (!next_token.identifier) {
    +
     2856.    259                    return stop("expected_identifier_a");
    +
     2857.    259                }
    +
     2858.    259                param = next_token;
    +
     2859.    259                list.push(param);
    +
     2860.    259                advance();
    +
     2861.    259                signature.push(param.id);
    +
     2862.    259                if (ellipsis) {
    +
     2863.    259                    param.ellipsis = true;
    +
     2864.    259                } else {
    +
     2865.    259                    if (next_token.id === "=") {
    +
     2866.    259                        optional = param;
    +
     2867.    259                        advance("=");
    +
     2868.    259                        param.expression = expression(0);
    +
     2869.    259                    } else {
    +
     2870.      0                        if (optional !== undefined) {
    +
     2871.      0                            warn(
    +
     2872.      0                                "required_a_optional_b",
    +
     2873.      0                                param,
    +
     2874.      0                                param.id,
    +
     2875.      0                                optional.id
    +
     2876.      0                            );
    +
     2877.      0                        }
    +
     2878.    259                    }
    +
     2879.    259                    if (next_token.id === ",") {
    +
     2880.    259                        advance(",");
    +
     2881.    259                        signature.push(", ");
    +
     2882.    259                        return parameter();
    +
     2883.    259                    }
    +
     2884.    259                }
    +
     2885.    259            }
    +
     2886.    279        }());
    +
     2887.    330    }
    +
     2888.    330    advance(")");
    +
     2889.    330    signature.push(")");
    +
     2890.    330    return [list, signature.join("")];
    +
     2891.    330}
    +
     2892.      1
    +
     2893.    336function do_function(the_function) {
    +
     2894.    336    let name;
    +
     2895.    333    if (the_function === undefined) {
    +
     2896.    333        the_function = token;
    +
     2897.    333
    +
     2898.    333// A function statement must have a name that will be in the parent's scope.
    +
     2899.    333
    +
     2900.    333        if (the_function.arity === "statement") {
    +
     2901.    333            if (!next_token.identifier) {
    +
     2902.    333                return stop("expected_identifier_a", next_token);
    +
     2903.    333            }
    +
     2904.    333            name = next_token;
    +
     2905.    333            enroll(name, "variable", true);
    +
     2906.    333            the_function.name = name;
    +
     2907.    333            name.init = true;
    +
     2908.    333            name.calls = empty();
    +
     2909.    333            advance();
    +
     2910.    333        } else if (name === undefined) {
    +
     2911.    333
    +
     2912.    333// A function expression may have an optional name.
    +
     2913.    333
    +
     2914.    333            if (next_token.identifier) {
    +
     2915.    333                name = next_token;
    +
     2916.    333                the_function.name = name;
    +
     2917.    333                advance();
    +
     2918.    333            } else {
    +
     2919.    333                the_function.name = anon;
    +
     2920.    333            }
    +
     2921.    333        }
    +
     2922.    333    } else {
    +
     2923.      3        name = the_function.name;
    +
     2924.    335    }
    +
     2925.    335    the_function.level = functionage.level + 1;
    +
     2926.      0    if (mega_mode) {
    +
     2927.      0        warn("unexpected_a", the_function);
    +
     2928.      0    }
    +
     2929.    335
    +
     2930.    335// Don't make functions in loops. It is inefficient, and it can lead to scoping
    +
     2931.    335// errors.
    +
     2932.    335
    +
     2933.    335    if (functionage.loop > 0) {
    +
     2934.      1        warn("function_in_loop", the_function);
    +
     2935.    335    }
    +
     2936.    335
    +
     2937.    335// Give the function properties for storing its names and for observing the
    +
     2938.    335// depth of loops and switches.
    +
     2939.    335
    +
     2940.    335    the_function.context = empty();
    +
     2941.    335    the_function.finally = 0;
    +
     2942.    335    the_function.loop = 0;
    +
     2943.    335    the_function.switch = 0;
    +
     2944.    335    the_function.try = 0;
    +
     2945.    335
    +
     2946.    335// Push the current function context and establish a new one.
    +
     2947.    335
    +
     2948.    335    stack.push(functionage);
    +
     2949.    335    functions.push(the_function);
    +
     2950.    335    functionage = the_function;
    +
     2951.    335    if (the_function.arity !== "statement" && typeof name === "object") {
    +
     2952.     32        enroll(name, "function", true);
    +
     2953.     32        name.dead = false;
    +
     2954.     32        name.init = true;
    +
     2955.     32        name.used = 1;
    +
     2956.    335    }
    +
     2957.    335
    +
     2958.    335// Parse the parameter list.
    +
     2959.    335
    +
     2960.    335    advance("(");
    +
     2961.    335    token.free = false;
    +
     2962.    335    token.arity = "function";
    +
     2963.    335    [functionage.parameters, functionage.signature] = parameter_list();
    +
     2964.    335    functionage.parameters.forEach(function enroll_parameter(name) {
    +
     2965.    335        if (name.identifier) {
    +
     2966.    335            enroll(name, "parameter", false);
    +
     2967.    335        } else {
    +
     2968.    335            name.names.forEach(enroll_parameter);
    +
     2969.    335        }
    +
     2970.    335    });
    +
     2971.    335
    +
     2972.    335// The function's body is a block.
    +
     2973.    335
    +
     2974.    335    the_function.block = block("body");
    +
     2975.    335    if (
    +
     2976.    335        the_function.arity === "statement"
    +
     2977.    335        && next_token.line === token.line
    +
     2978.      2    ) {
    +
     2979.      2        return stop("unexpected_a", next_token);
    +
     2980.    327    }
    +
     2981.    327    if (
    +
     2982.    327        next_token.id === "."
    +
     2983.    327        || next_token.id === "?."
    +
     2984.    327        || next_token.id === "["
    +
     2985.      1    ) {
    +
     2986.      1        warn("unexpected_a");
    +
     2987.    327    }
    +
     2988.    327
    +
     2989.    327// Restore the previous context.
    +
     2990.    327
    +
     2991.    327    functionage = stack.pop();
    +
     2992.    327    return the_function;
    +
     2993.    327}
    +
     2994.      1
    +
     2995.     13function do_async() {
    +
     2996.     13    let the_async;
    +
     2997.     13    let the_function;
    +
     2998.     13    the_async = token;
    +
     2999.     13    advance("function");
    +
     3000.     13    the_function = token;
    +
     3001.     13    the_function.is_async = true;
    +
     3002.     13    the_function.arity = the_async.arity;
    +
     3003.     13    do_function();
    +
     3004.      1    if (!the_function.has_await) {
    +
     3005.      1        warn("missing_await_statement", the_function);
    +
     3006.     12    }
    +
     3007.     12    return the_function;
    +
     3008.     12}
    +
     3009.      1
    +
     3010.      1prefix("async", do_async);
    +
     3011.      1prefix("function", do_function);
    +
     3012.     21prefix("await", function () {
    +
     3013.     21    let the_await;
    +
     3014.     21    the_await = token;
    +
     3015.      1    if (!functionage.is_async) {
    +
     3016.      1        return stop("unexpected_a", the_await);
    +
     3017.     20    }
    +
     3018.     20    functionage.has_await = true;
    +
     3019.     20    the_await.expression = expression(150);
    +
     3020.     20    return the_await;
    +
     3021.     20});
    +
     3022.      1
    +
     3023.      2function fart(pl) {
    +
     3024.      2    advance("=>");
    +
     3025.      2    const the_fart = token;
    +
     3026.      2    the_fart.arity = "binary";
    +
     3027.      2    the_fart.name = "=>";
    +
     3028.      2    the_fart.level = functionage.level + 1;
    +
     3029.      2    functions.push(the_fart);
    +
     3030.      1    if (functionage.loop > 0) {
    +
     3031.      1        warn("function_in_loop", the_fart);
    +
     3032.      1    }
    +
     3033.      2
    +
     3034.      2// Give the function properties storing its names and for observing the depth
    +
     3035.      2// of loops and switches.
    +
     3036.      2
    +
     3037.      2    the_fart.context = empty();
    +
     3038.      2    the_fart.finally = 0;
    +
     3039.      2    the_fart.loop = 0;
    +
     3040.      2    the_fart.switch = 0;
    +
     3041.      2    the_fart.try = 0;
    +
     3042.      2
    +
     3043.      2// Push the current function context and establish a new one.
    +
     3044.      2
    +
     3045.      2    stack.push(functionage);
    +
     3046.      2    functionage = the_fart;
    +
     3047.      2    the_fart.parameters = pl[0];
    +
     3048.      2    the_fart.signature = pl[1];
    +
     3049.      1    the_fart.parameters.forEach(function (name) {
    +
     3050.      1        enroll(name, "parameter", true);
    +
     3051.      1    });
    +
     3052.      1    if (next_token.id === "{") {
    +
     3053.      1        warn("expected_a_b", the_fart, "function", "=>");
    +
     3054.      1        the_fart.block = block("body");
    +
     3055.      1    } else {
    +
     3056.      1        the_fart.expression = expression(0);
    +
     3057.      1    }
    +
     3058.      2    functionage = stack.pop();
    +
     3059.      2    return the_fart;
    +
     3060.      2}
    +
     3061.      1
    +
     3062.    230prefix("(", function () {
    +
     3063.    230    const the_paren = token;
    +
     3064.    230    let the_value;
    +
     3065.    230    const cadet = lookahead().id;
    +
     3066.    230
    +
     3067.    230// We can distinguish between a parameter list for => and a wrapped expression
    +
     3068.    230// with one token of lookahead.
    +
     3069.    230
    +
     3070.    230    if (
    +
     3071.    230        next_token.id === ")"
    +
     3072.    229        || next_token.id === "..."
    +
     3073.    229        || (next_token.identifier && (cadet === "," || cadet === "="))
    +
     3074.      1    ) {
    +
     3075.      1        the_paren.free = false;
    +
     3076.      1        return fart(parameter_list());
    +
     3077.    229    }
    +
     3078.    229    the_paren.free = true;
    +
     3079.    229    the_value = expression(0);
    +
     3080.    229    if (the_value.wrapped === true) {
    +
     3081.      1        warn("unexpected_a", the_paren);
    +
     3082.    229    }
    +
     3083.    229    the_value.wrapped = true;
    +
     3084.    229    advance(")", the_paren);
    +
     3085.    229    if (next_token.id === "=>") {
    +
     3086.      3        if (the_value.arity !== "variable") {
    +
     3087.      3            if (the_value.id === "{" || the_value.id === "[") {
    +
     3088.      3                warn("expected_a_before_b", the_paren, "function", "(");
    +
     3089.      3                return stop("expected_a_b", next_token, "{", "=>");
    +
     3090.      3            }
    +
     3091.      3            return stop("expected_identifier_a", the_value);
    +
     3092.      3        }
    +
     3093.      3        the_paren.expression = [the_value];
    +
     3094.      3        return fart([the_paren.expression, "(" + the_value.id + ")"]);
    +
     3095.    226    }
    +
     3096.    226    return the_value;
    +
     3097.    226});
    +
     3098.      1prefix("`", do_tick);
    +
     3099.     45prefix("{", function () {
    +
     3100.     45    const the_brace = token;
    +
     3101.     45    const seen = empty();
    +
     3102.     45    the_brace.expression = [];
    +
     3103.     42    if (next_token.id !== "}") {
    +
     3104.    313        (function member() {
    +
     3105.    313            let extra;
    +
     3106.    313            let full;
    +
     3107.    313            let id;
    +
     3108.    313            let name = next_token;
    +
     3109.    313            let value;
    +
     3110.    313            advance();
    +
     3111.    313            if (
    +
     3112.    312                (name.id === "get" || name.id === "set")
    +
     3113.     42                && next_token.identifier
    +
     3114.     42            ) {
    +
     3115.     42                if (!option.getset) {
    +
     3116.     42                    warn("unexpected_a", name);
    +
     3117.     42                }
    +
     3118.     42                extra = name.id;
    +
     3119.     42                full = extra + " " + next_token.id;
    +
     3120.     42                name = next_token;
    +
     3121.     42                advance();
    +
     3122.     42                id = survey(name);
    +
     3123.      0                if (seen[full] === true || seen[id] === true) {
    +
     3124.      0                    warn("duplicate_a", name);
    +
     3125.      0                }
    +
     3126.     42                seen[id] = false;
    +
     3127.     42                seen[full] = true;
    +
     3128.    310            } else {
    +
     3129.    310                id = survey(name);
    +
     3130.    310                if (typeof seen[id] === "boolean") {
    +
     3131.    310                    warn("duplicate_a", name);
    +
     3132.    310                }
    +
     3133.    310                seen[id] = true;
    +
     3134.    310            }
    +
     3135.    223            if (name.identifier) {
    +
     3136.    223                if (next_token.id === "}" || next_token.id === ",") {
    +
     3137.      0                    if (typeof extra === "string") {
    +
     3138.      0                        advance("(");
    +
     3139.      0                    }
    +
     3140.    223                    value = expression(Infinity, true);
    +
     3141.    223                } else if (next_token.id === "(") {
    +
     3142.    223                    value = do_function({
    +
     3143.    223                        arity: "unary",
    +
     3144.    223                        from: name.from,
    +
     3145.    223                        id: "function",
    +
     3146.    223                        line: name.line,
    +
     3147.    223                        name: (
    +
     3148.    223                            typeof extra === "string"
    +
     3149.    223                            ? extra
    +
     3150.      0                            : id
    +
     3151.    223                        ),
    +
     3152.    223                        thru: name.from
    +
     3153.    223                    });
    +
     3154.    223                } else {
    +
     3155.      0                    if (typeof extra === "string") {
    +
     3156.      0                        advance("(");
    +
     3157.      0                    }
    +
     3158.    223                    let the_colon = next_token;
    +
     3159.    223                    advance(":");
    +
     3160.    223                    value = expression(0);
    +
     3161.    223                    if (value.id === name.id && value.id !== "function") {
    +
     3162.    223                        warn("unexpected_a", the_colon, ": " + name.id);
    +
     3163.    223                    }
    +
     3164.    223                }
    +
     3165.    223                value.label = name;
    +
     3166.    223                if (typeof extra === "string") {
    +
     3167.    223                    value.extra = extra;
    +
     3168.    223                }
    +
     3169.    223                the_brace.expression.push(value);
    +
     3170.    223            } else {
    +
     3171.     90                advance(":");
    +
     3172.     90                value = expression(0);
    +
     3173.     90                value.label = name;
    +
     3174.     90                the_brace.expression.push(value);
    +
     3175.     90            }
    +
     3176.    271            if (next_token.id === ",") {
    +
     3177.    271                advance(",");
    +
     3178.    271                return member();
    +
     3179.    271            }
    +
     3180.    313        }());
    +
     3181.     42    }
    +
     3182.     45    advance("}");
    +
     3183.     45    return the_brace;
    +
     3184.     45});
    +
     3185.      1
    +
     3186.      2stmt(";", function () {
    +
     3187.      2    warn("unexpected_a", token);
    +
     3188.      2    return token;
    +
     3189.      2});
    +
     3190.      3stmt("{", function () {
    +
     3191.      3    warn("naked_block", token);
    +
     3192.      3    return block("naked");
    +
     3193.      3});
    +
     3194.      1stmt("async", do_async);
    +
     3195.     12stmt("break", function () {
    +
     3196.     12    const the_break = token;
    +
     3197.     12    let the_label;
    +
     3198.     12    if (
    +
     3199.      6        (functionage.loop < 1 && functionage.switch < 1)
    +
     3200.      9        || functionage.finally > 0
    +
     3201.      3    ) {
    +
     3202.      3        warn("unexpected_a", the_break);
    +
     3203.      3    }
    +
     3204.     12    the_break.disrupt = true;
    +
     3205.      2    if (next_token.identifier && token.line === next_token.line) {
    +
     3206.      2        the_label = functionage.context[next_token.id];
    +
     3207.      2        if (
    +
     3208.      2            the_label === undefined
    +
     3209.      2            || the_label.role !== "label"
    +
     3210.      0            || the_label.dead
    +
     3211.      2        ) {
    +
     3212.      2            warn(
    +
     3213.      2                (the_label !== undefined && the_label.dead)
    +
     3214.      2                ? "out_of_scope_a"
    +
     3215.      2                : "not_label_a"
    +
     3216.      2            );
    +
     3217.      0        } else {
    +
     3218.      0            the_label.used += 1;
    +
     3219.      0        }
    +
     3220.      2        the_break.label = next_token;
    +
     3221.      2        advance();
    +
     3222.      2    }
    +
     3223.     12    advance(";");
    +
     3224.     12    return the_break;
    +
     3225.     12});
    +
     3226.      1
    +
     3227.    396function do_var() {
    +
     3228.    396    const the_statement = token;
    +
     3229.    396    const is_const = the_statement.id === "const";
    +
     3230.    396    the_statement.names = [];
    +
     3231.    396
    +
     3232.    396// A program may use var or let, but not both.
    +
     3233.    396
    +
     3234.    223    if (!is_const) {
    +
     3235.    223        if (var_mode === undefined) {
    +
     3236.    223            var_mode = the_statement.id;
    +
     3237.    223        } else if (the_statement.id !== var_mode) {
    +
     3238.    223            warn(
    +
     3239.    223                "expected_a_b",
    +
     3240.    223                the_statement,
    +
     3241.    223                var_mode,
    +
     3242.    223                the_statement.id
    +
     3243.    223            );
    +
     3244.    223        }
    +
     3245.    223    }
    +
     3246.    396
    +
     3247.    396// We don't expect to see variables created in switch statements.
    +
     3248.    396
    +
     3249.      1    if (functionage.switch > 0) {
    +
     3250.      1        warn("var_switch", the_statement);
    +
     3251.      1    }
    +
     3252.      1    if (functionage.loop > 0 && the_statement.id === "var") {
    +
     3253.      1        warn("var_loop", the_statement);
    +
     3254.      1    }
    +
     3255.    396    (function next() {
    +
     3256.      1        if (next_token.id === "{" && the_statement.id !== "var") {
    +
     3257.      1            const the_brace = next_token;
    +
     3258.      1            advance("{");
    +
     3259.      2            (function pair() {
    +
     3260.      0                if (!next_token.identifier) {
    +
     3261.      0                    return stop("expected_identifier_a", next_token);
    +
     3262.      0                }
    +
     3263.      2                const name = next_token;
    +
     3264.      2                survey(name);
    +
     3265.      2                advance();
    +
     3266.      0                if (next_token.id === ":") {
    +
     3267.      0                    advance(":");
    +
     3268.      0                    if (!next_token.identifier) {
    +
     3269.      0                        return stop("expected_identifier_a", next_token);
    +
     3270.      0                    }
    +
     3271.      0                    next_token.label = name;
    +
     3272.      0                    the_statement.names.push(next_token);
    +
     3273.      0                    enroll(next_token, "variable", is_const);
    +
     3274.      0                    advance();
    +
     3275.      0                    the_brace.open = true;
    +
     3276.      0                } else {
    +
     3277.      2                    the_statement.names.push(name);
    +
     3278.      2                    enroll(name, "variable", is_const);
    +
     3279.      2                }
    +
     3280.      2                name.dead = false;
    +
     3281.      2                name.init = true;
    +
     3282.      0                if (next_token.id === "=") {
    +
     3283.      0                    advance("=");
    +
     3284.      0                    name.expression = expression();
    +
     3285.      0                    the_brace.open = true;
    +
     3286.      0                }
    +
     3287.      1                if (next_token.id === ",") {
    +
     3288.      1                    advance(",");
    +
     3289.      1                    return pair();
    +
     3290.      1                }
    +
     3291.      2            }());
    +
     3292.      1            advance("}");
    +
     3293.      1            advance("=");
    +
     3294.      1            the_statement.expression = expression(0);
    +
     3295.    395        } else if (next_token.id === "[" && the_statement.id !== "var") {
    +
     3296.    395            const the_bracket = next_token;
    +
     3297.    395            advance("[");
    +
     3298.    395            (function element() {
    +
     3299.    395                let ellipsis;
    +
     3300.      0                if (next_token.id === "...") {
    +
     3301.      0                    ellipsis = true;
    +
     3302.      0                    advance("...");
    +
     3303.      0                }
    +
     3304.    395                if (!next_token.identifier) {
    +
     3305.    395                    return stop("expected_identifier_a", next_token);
    +
     3306.    395                }
    +
     3307.    395                const name = next_token;
    +
     3308.    395                advance();
    +
     3309.    395                the_statement.names.push(name);
    +
     3310.    395                enroll(name, "variable", is_const);
    +
     3311.    395                name.dead = false;
    +
     3312.    395                name.init = true;
    +
     3313.      0                if (ellipsis) {
    +
     3314.      0                    name.ellipsis = true;
    +
     3315.      0                } else {
    +
     3316.      0                    if (next_token.id === "=") {
    +
     3317.      0                        advance("=");
    +
     3318.      0                        name.expression = expression();
    +
     3319.      0                        the_bracket.open = true;
    +
     3320.      0                    }
    +
     3321.    395                    if (next_token.id === ",") {
    +
     3322.    395                        advance(",");
    +
     3323.    395                        return element();
    +
     3324.    395                    }
    +
     3325.    395                }
    +
     3326.    395            }());
    +
     3327.    395            advance("]");
    +
     3328.    395            advance("=");
    +
     3329.    395            the_statement.expression = expression(0);
    +
     3330.    395        } else if (next_token.identifier) {
    +
     3331.    395            const name = next_token;
    +
     3332.    395            advance();
    +
     3333.    395            if (name.id === "ignore") {
    +
     3334.    395                warn("unexpected_a", name);
    +
     3335.    395            }
    +
     3336.    395            enroll(name, "variable", is_const);
    +
     3337.    395            if (next_token.id === "=" || is_const) {
    +
     3338.    395                advance("=");
    +
     3339.    395                name.dead = false;
    +
     3340.    395                name.init = true;
    +
     3341.    395                name.expression = expression(0);
    +
     3342.    395            }
    +
     3343.    395            the_statement.names.push(name);
    +
     3344.      0        } else {
    +
     3345.      0            return stop("expected_identifier_a", next_token);
    +
     3346.      0        }
    +
     3347.    396    }());
    +
     3348.    396    semicolon();
    +
     3349.    396    return the_statement;
    +
     3350.    396}
    +
     3351.      1
    +
     3352.      1stmt("const", do_var);
    +
     3353.      2stmt("continue", function () {
    +
     3354.      2    const the_continue = token;
    +
     3355.      1    if (functionage.loop < 1 || functionage.finally > 0) {
    +
     3356.      1        warn("unexpected_a", the_continue);
    +
     3357.      1    }
    +
     3358.      2    not_top_level(the_continue);
    +
     3359.      2    the_continue.disrupt = true;
    +
     3360.      2    warn("unexpected_a", the_continue);
    +
     3361.      2    advance(";");
    +
     3362.      2    return the_continue;
    +
     3363.      2});
    +
     3364.      1stmt("debugger", function () {
    +
     3365.      1    const the_debug = token;
    +
     3366.      1    if (!option.devel) {
    +
     3367.      1        warn("unexpected_a", the_debug);
    +
     3368.      1    }
    +
     3369.      1    semicolon();
    +
     3370.      1    return the_debug;
    +
     3371.      1});
    +
     3372.     13stmt("delete", function () {
    +
     3373.     13    const the_token = token;
    +
     3374.     13    const the_value = expression(0);
    +
     3375.     13    if (
    +
     3376.      1        (the_value.id !== "." && the_value.id !== "[")
    +
     3377.     13        || the_value.arity !== "binary"
    +
     3378.      1    ) {
    +
     3379.      1        stop("expected_a_b", the_value, ".", artifact(the_value));
    +
     3380.     12    }
    +
     3381.     12    the_token.expression = the_value;
    +
     3382.     12    semicolon();
    +
     3383.     12    return the_token;
    +
     3384.     12});
    +
     3385.      1stmt("do", function () {
    +
     3386.      1    const the_do = token;
    +
     3387.      1    not_top_level(the_do);
    +
     3388.      1    functionage.loop += 1;
    +
     3389.      1    the_do.block = block();
    +
     3390.      1    advance("while");
    +
     3391.      1    the_do.expression = condition();
    +
     3392.      1    semicolon();
    +
     3393.      1    if (the_do.block.disrupt === true) {
    +
     3394.      1        warn("weird_loop", the_do);
    +
     3395.      1    }
    +
     3396.      1    functionage.loop -= 1;
    +
     3397.      1    return the_do;
    +
     3398.      1});
    +
     3399.      7stmt("export", function () {
    +
     3400.      7    const the_export = token;
    +
     3401.      7    let the_id;
    +
     3402.      7    let the_name;
    +
     3403.      7    let the_thing;
    +
     3404.      7
    +
     3405.      2    function export_id() {
    +
     3406.      0        if (!next_token.identifier) {
    +
     3407.      0            stop("expected_identifier_a");
    +
     3408.      0        }
    +
     3409.      2        the_id = next_token.id;
    +
     3410.      2        the_name = global.context[the_id];
    +
     3411.      0        if (the_name === undefined) {
    +
     3412.      0            warn("unexpected_a");
    +
     3413.      0        } else {
    +
     3414.      2            the_name.used += 1;
    +
     3415.      1            if (exports[the_id] !== undefined) {
    +
     3416.      1                warn("duplicate_a");
    +
     3417.      1            }
    +
     3418.      2            exports[the_id] = the_name;
    +
     3419.      2        }
    +
     3420.      2        advance();
    +
     3421.      2        the_export.expression.push(the_thing);
    +
     3422.      2    }
    +
     3423.      7
    +
     3424.      7    the_export.expression = [];
    +
     3425.      3    if (next_token.id === "default") {
    +
     3426.      0        if (exports.default !== undefined) {
    +
     3427.      0            warn("duplicate_a");
    +
     3428.      0        }
    +
     3429.      3        advance("default");
    +
     3430.      3        the_thing = expression(0);
    +
     3431.      3        if (
    +
     3432.      3            the_thing.id !== "("
    +
     3433.      3            || the_thing.expression[0].id !== "."
    +
     3434.      3            || the_thing.expression[0].expression.id !== "Object"
    +
     3435.      3            || the_thing.expression[0].name.id !== "freeze"
    +
     3436.      3        ) {
    +
     3437.      3            warn("freeze_exports", the_thing);
    +
     3438.      3        }
    +
     3439.      3        if (next_token.id === ";") {
    +
     3440.      3            semicolon();
    +
     3441.      3        }
    +
     3442.      3        exports.default = the_thing;
    +
     3443.      3        the_export.expression.push(the_thing);
    +
     3444.      4    } else {
    +
     3445.      4        if (next_token.id === "function") {
    +
     3446.      4            warn("freeze_exports");
    +
     3447.      4            the_thing = statement();
    +
     3448.      4            the_name = the_thing.name;
    +
     3449.      4            the_id = the_name.id;
    +
     3450.      4            the_name.used += 1;
    +
     3451.      0            if (exports[the_id] !== undefined) {
    +
     3452.      0                warn("duplicate_a", the_name);
    +
     3453.      0            }
    +
     3454.      4            exports[the_id] = the_thing;
    +
     3455.      4            the_export.expression.push(the_thing);
    +
     3456.      4            the_thing.statement = false;
    +
     3457.      4            the_thing.arity = "unary";
    +
     3458.      4        } else if (
    +
     3459.      4            next_token.id === "var"
    +
     3460.      4            || next_token.id === "let"
    +
     3461.      4            || next_token.id === "const"
    +
     3462.      4        ) {
    +
     3463.      4            warn("unexpected_a", next_token);
    +
     3464.      4            statement();
    +
     3465.      4        } else if (next_token.id === "{") {
    +
     3466.      4            advance("{");
    +
     3467.      4            (function loop() {
    +
     3468.      4                export_id();
    +
     3469.      4                if (next_token.id === ",") {
    +
     3470.      4                    advance(",");
    +
     3471.      4                    return loop();
    +
     3472.      4                }
    +
     3473.      4            }());
    +
     3474.      4            advance("}");
    +
     3475.      4            semicolon();
    +
     3476.      4        } else {
    +
     3477.      4            stop("unexpected_a");
    +
     3478.      4        }
    +
     3479.      6    }
    +
     3480.      6    module_mode = true;
    +
     3481.      6    return the_export;
    +
     3482.      6});
    +
     3483.      7stmt("for", function () {
    +
     3484.      7    let first;
    +
     3485.      7    const the_for = token;
    +
     3486.      6    if (!option.for) {
    +
     3487.      6        warn("unexpected_a", the_for);
    +
     3488.      6    }
    +
     3489.      7    not_top_level(the_for);
    +
     3490.      7    functionage.loop += 1;
    +
     3491.      7    advance("(");
    +
     3492.      7    token.free = true;
    +
     3493.      1    if (next_token.id === ";") {
    +
     3494.      1        return stop("expected_a_b", the_for, "while (", "for (;");
    +
     3495.      6    }
    +
     3496.      6    if (
    +
     3497.      6        next_token.id === "var"
    +
     3498.      6        || next_token.id === "let"
    +
     3499.      6        || next_token.id === "const"
    +
     3500.      1    ) {
    +
     3501.      1        return stop("unexpected_a");
    +
     3502.      5    }
    +
     3503.      5    first = expression(0);
    +
     3504.      5    if (first.id === "in") {
    +
     3505.      3        if (first.expression[0].arity !== "variable") {
    +
     3506.      3            warn("bad_assignment_a", first.expression[0]);
    +
     3507.      3        }
    +
     3508.      3        the_for.name = first.expression[0];
    +
     3509.      3        the_for.expression = first.expression[1];
    +
     3510.      3        warn("expected_a_b", the_for, "Object.keys", "for in");
    +
     3511.      3    } else {
    +
     3512.      2        the_for.initial = first;
    +
     3513.      2        advance(";");
    +
     3514.      2        the_for.expression = expression(0);
    +
     3515.      2        advance(";");
    +
     3516.      2        the_for.inc = expression(0);
    +
     3517.      2        if (the_for.inc.id === "++") {
    +
     3518.      2            warn("expected_a_b", the_for.inc, "+= 1", "++");
    +
     3519.      2        }
    +
     3520.      5    }
    +
     3521.      5    advance(")");
    +
     3522.      5    the_for.block = block();
    +
     3523.      0    if (the_for.block.disrupt === true) {
    +
     3524.      0        warn("weird_loop", the_for);
    +
     3525.      0    }
    +
     3526.      5    functionage.loop -= 1;
    +
     3527.      5    return the_for;
    +
     3528.      5});
    +
     3529.      1stmt("function", do_function);
    +
     3530.    615stmt("if", function () {
    +
     3531.    615    let the_else;
    +
     3532.    615    const the_if = token;
    +
     3533.    615    the_if.expression = condition();
    +
     3534.    615    the_if.block = block();
    +
     3535.    146    if (next_token.id === "else") {
    +
     3536.    146        advance("else");
    +
     3537.    146        the_else = token;
    +
     3538.    146        the_if.else = (
    +
     3539.    146            next_token.id === "if"
    +
     3540.    146            ? statement()
    +
     3541.    146            : block()
    +
     3542.    146        );
    +
     3543.    146        if (the_if.block.disrupt === true) {
    +
     3544.    146            if (the_if.else.disrupt === true) {
    +
     3545.    146                the_if.disrupt = true;
    +
     3546.      0            } else {
    +
     3547.      0                warn("unexpected_a", the_else);
    +
     3548.      0            }
    +
     3549.    146        }
    +
     3550.    146    }
    +
     3551.    615    return the_if;
    +
     3552.    615});
    +
     3553.     11stmt("import", function () {
    +
     3554.     11    const the_import = token;
    +
     3555.      1    if (next_token.id === "(") {
    +
     3556.      1        the_import.arity = "unary";
    +
     3557.      1        the_import.constant = true;
    +
     3558.      1        the_import.statement = false;
    +
     3559.      1        advance("(");
    +
     3560.      1        const string = expression(0);
    +
     3561.      1        if (string.id !== "(string)") {
    +
     3562.      1            warn("expected_string_a", string);
    +
     3563.      1        }
    +
     3564.      1        froms.push(token.value);
    +
     3565.      1        advance(")");
    +
     3566.      1        advance(".");
    +
     3567.      1        advance("then");
    +
     3568.      1        advance("(");
    +
     3569.      1        the_import.expression = expression(0);
    +
     3570.      1        advance(")");
    +
     3571.      1        semicolon();
    +
     3572.      1        return the_import;
    +
     3573.     10    }
    +
     3574.     10    let name;
    +
     3575.     10    if (typeof module_mode === "object") {
    +
     3576.      1        warn("unexpected_directive_a", module_mode, module_mode.directive);
    +
     3577.     10    }
    +
     3578.     10    module_mode = true;
    +
     3579.     10    if (next_token.identifier) {
    +
     3580.      8        name = next_token;
    +
     3581.      8        advance();
    +
     3582.      8        if (name.id === "ignore") {
    +
     3583.      8            warn("unexpected_a", name);
    +
     3584.      8        }
    +
     3585.      8        enroll(name, "variable", true);
    +
     3586.      8        the_import.name = name;
    +
     3587.      8    } else {
    +
     3588.      2        const names = [];
    +
     3589.      2        advance("{");
    +
     3590.      2        if (next_token.id !== "}") {
    +
     3591.      2            while (true) {
    +
     3592.      2                if (!next_token.identifier) {
    +
     3593.      2                    stop("expected_identifier_a");
    +
     3594.      2                }
    +
     3595.      2                name = next_token;
    +
     3596.      2                advance();
    +
     3597.      2                if (name.id === "ignore") {
    +
     3598.      2                    warn("unexpected_a", name);
    +
     3599.      2                }
    +
     3600.      2                enroll(name, "variable", true);
    +
     3601.      2                names.push(name);
    +
     3602.      2                if (next_token.id !== ",") {
    +
     3603.      2                    break;
    +
     3604.      0                }
    +
     3605.      0                advance(",");
    +
     3606.      0            }
    +
     3607.      2        }
    +
     3608.      2        advance("}");
    +
     3609.      2        the_import.name = names;
    +
     3610.      9    }
    +
     3611.      9    advance("from");
    +
     3612.      9    advance("(string)");
    +
     3613.      9    the_import.import = token;
    +
     3614.      9    if (!rx_module.test(token.value)) {
    +
     3615.      1        warn("bad_module_name_a", token);
    +
     3616.      9    }
    +
     3617.      9    froms.push(token.value);
    +
     3618.      9    semicolon();
    +
     3619.      9    return the_import;
    +
     3620.      9});
    +
     3621.      1stmt("let", do_var);
    +
     3622.    284stmt("return", function () {
    +
     3623.    284    const the_return = token;
    +
     3624.    284    not_top_level(the_return);
    +
     3625.      1    if (functionage.finally > 0) {
    +
     3626.      1        warn("unexpected_a", the_return);
    +
     3627.      1    }
    +
     3628.    284    the_return.disrupt = true;
    +
     3629.    263    if (next_token.id !== ";" && the_return.line === next_token.line) {
    +
     3630.    263        the_return.expression = expression(10);
    +
     3631.    263    }
    +
     3632.    284    advance(";");
    +
     3633.    284    return the_return;
    +
     3634.    284});
    +
     3635.      5stmt("switch", function () {
    +
     3636.      5    let dups = [];
    +
     3637.      5    let last;
    +
     3638.      5    let stmts;
    +
     3639.      5    const the_cases = [];
    +
     3640.      5    let the_disrupt = true;
    +
     3641.      5    const the_switch = token;
    +
     3642.      5    not_top_level(the_switch);
    +
     3643.      1    if (functionage.finally > 0) {
    +
     3644.      1        warn("unexpected_a", the_switch);
    +
     3645.      1    }
    +
     3646.      5    functionage.switch += 1;
    +
     3647.      5    advance("(");
    +
     3648.      5    token.free = true;
    +
     3649.      5    the_switch.expression = expression(0);
    +
     3650.      5    the_switch.block = the_cases;
    +
     3651.      5    advance(")");
    +
     3652.      5    advance("{");
    +
     3653.      7    (function major() {
    +
     3654.      7        const the_case = next_token;
    +
     3655.      7        the_case.arity = "statement";
    +
     3656.      7        the_case.expression = [];
    +
     3657.     13        (function minor() {
    +
     3658.     13            advance("case");
    +
     3659.     13            token.switch = true;
    +
     3660.     13            const exp = expression(0);
    +
     3661.     19            if (dups.some(function (thing) {
    +
     3662.     19                return are_similar(thing, exp);
    +
     3663.      0            })) {
    +
     3664.      0                warn("unexpected_a", exp);
    +
     3665.      0            }
    +
     3666.     13            dups.push(exp);
    +
     3667.     13            the_case.expression.push(exp);
    +
     3668.     13            advance(":");
    +
     3669.      6            if (next_token.id === "case") {
    +
     3670.      6                return minor();
    +
     3671.      6            }
    +
     3672.     13        }());
    +
     3673.      7        stmts = statements();
    +
     3674.      1        if (stmts.length < 1) {
    +
     3675.      1            warn("expected_statements_a");
    +
     3676.      1            return;
    +
     3677.      6        }
    +
     3678.      6        the_case.block = stmts;
    +
     3679.      6        the_cases.push(the_case);
    +
     3680.      6        last = stmts[stmts.length - 1];
    +
     3681.      6        if (last.disrupt) {
    +
     3682.      5            if (last.id === "break" && last.label === undefined) {
    +
     3683.      5                the_disrupt = false;
    +
     3684.      5            }
    +
     3685.      5        } else {
    +
     3686.      1            warn(
    +
     3687.      1                "expected_a_before_b",
    +
     3688.      1                next_token,
    +
     3689.      1                "break;",
    +
     3690.      1                artifact(next_token)
    +
     3691.      1            );
    +
     3692.      6        }
    +
     3693.      6        if (next_token.id === "case") {
    +
     3694.      2            return major();
    +
     3695.      2        }
    +
     3696.      7    }());
    +
     3697.      5    dups = undefined;
    +
     3698.      3    if (next_token.id === "default") {
    +
     3699.      3        const the_default = next_token;
    +
     3700.      3        advance("default");
    +
     3701.      3        token.switch = true;
    +
     3702.      3        advance(":");
    +
     3703.      3        the_switch.else = statements();
    +
     3704.      0        if (the_switch.else.length < 1) {
    +
     3705.      0            warn("unexpected_a", the_default);
    +
     3706.      0            the_disrupt = false;
    +
     3707.      0        } else {
    +
     3708.      3            const the_last = the_switch.else[the_switch.else.length - 1];
    +
     3709.      0            if (the_last.id === "break" && the_last.label === undefined) {
    +
     3710.      0                warn("unexpected_a", the_last);
    +
     3711.      0                the_last.disrupt = false;
    +
     3712.      0            }
    +
     3713.      3            the_disrupt = the_disrupt && the_last.disrupt;
    +
     3714.      3        }
    +
     3715.      3    } else {
    +
     3716.      2        the_disrupt = false;
    +
     3717.      2    }
    +
     3718.      5    advance("}", the_switch);
    +
     3719.      5    functionage.switch -= 1;
    +
     3720.      5    the_switch.disrupt = the_disrupt;
    +
     3721.      5    return the_switch;
    +
     3722.      5});
    +
     3723.      6stmt("throw", function () {
    +
     3724.      6    const the_throw = token;
    +
     3725.      6    the_throw.disrupt = true;
    +
     3726.      6    the_throw.expression = expression(10);
    +
     3727.      6    semicolon();
    +
     3728.      0    if (functionage.try > 0) {
    +
     3729.      0        warn("unexpected_a", the_throw);
    +
     3730.      0    }
    +
     3731.      6    return the_throw;
    +
     3732.      6});
    +
     3733.      7stmt("try", function () {
    +
     3734.      7    let the_catch;
    +
     3735.      7    let the_disrupt;
    +
     3736.      7    const the_try = token;
    +
     3737.      0    if (functionage.try > 0) {
    +
     3738.      0        warn("unexpected_a", the_try);
    +
     3739.      0    }
    +
     3740.      7    functionage.try += 1;
    +
     3741.      7    the_try.block = block();
    +
     3742.      7    the_disrupt = the_try.block.disrupt;
    +
     3743.      6    if (next_token.id === "catch") {
    +
     3744.      6        let ignored = "ignore";
    +
     3745.      6        the_catch = next_token;
    +
     3746.      6        the_try.catch = the_catch;
    +
     3747.      6        advance("catch");
    +
     3748.      6        if (next_token.id === "(") {
    +
     3749.      6            advance("(");
    +
     3750.      0            if (!next_token.identifier) {
    +
     3751.      0                return stop("expected_identifier_a", next_token);
    +
     3752.      0            }
    +
     3753.      6            if (next_token.id !== "ignore") {
    +
     3754.      6                ignored = undefined;
    +
     3755.      6                the_catch.name = next_token;
    +
     3756.      6                enroll(next_token, "exception", true);
    +
     3757.      6            }
    +
     3758.      6            advance();
    +
     3759.      6            advance(")");
    +
     3760.      6        }
    +
     3761.      6        the_catch.block = block(ignored);
    +
     3762.      6        if (the_catch.block.disrupt !== true) {
    +
     3763.      6            the_disrupt = false;
    +
     3764.      6        }
    +
     3765.      6    } else {
    +
     3766.      1        warn(
    +
     3767.      1            "expected_a_before_b",
    +
     3768.      1            next_token,
    +
     3769.      1            "catch",
    +
     3770.      1            artifact(next_token)
    +
     3771.      1        );
    +
     3772.      1
    +
     3773.      1    }
    +
     3774.      4    if (next_token.id === "finally") {
    +
     3775.      4        functionage.finally += 1;
    +
     3776.      4        advance("finally");
    +
     3777.      4        the_try.else = block();
    +
     3778.      4        the_disrupt = the_try.else.disrupt;
    +
     3779.      4        functionage.finally -= 1;
    +
     3780.      4    }
    +
     3781.      7    the_try.disrupt = the_disrupt;
    +
     3782.      7    functionage.try -= 1;
    +
     3783.      7    return the_try;
    +
     3784.      7});
    +
     3785.      1stmt("var", do_var);
    +
     3786.     15stmt("while", function () {
    +
     3787.     15    const the_while = token;
    +
     3788.     15    not_top_level(the_while);
    +
     3789.     15    functionage.loop += 1;
    +
     3790.     15    the_while.expression = condition();
    +
     3791.     15    the_while.block = block();
    +
     3792.      3    if (the_while.block.disrupt === true) {
    +
     3793.      3        warn("weird_loop", the_while);
    +
     3794.      3    }
    +
     3795.     15    functionage.loop -= 1;
    +
     3796.     15    return the_while;
    +
     3797.     15});
    +
     3798.      0stmt("with", function () {
    +
     3799.      0    stop("unexpected_a", token);
    +
     3800.      0});
    +
     3801.      1
    +
     3802.      1ternary("?", ":");
    +
     3803.      1
    +
     3804.      1// Ambulation of the parse tree.
    +
     3805.      1
    +
     3806.      2function action(when) {
    +
     3807.      2
    +
     3808.      2// Produce a function that will register task functions that will be called as
    +
     3809.      2// the tree is traversed.
    +
     3810.      2
    +
     3811.     38    return function (arity, id, task) {
    +
     3812.     38        let a_set = when[arity];
    +
     3813.     38        let i_set;
    +
     3814.     38
    +
     3815.     38// The id parameter is optional. If excluded, the task will be applied to all
    +
     3816.     38// ids.
    +
     3817.     38
    +
     3818.      8        if (typeof id !== "string") {
    +
     3819.      8            task = id;
    +
     3820.      8            id = "(all)";
    +
     3821.      8        }
    +
     3822.     38
    +
     3823.     38// If this arity has no registrations yet, then create a set object to hold
    +
     3824.     38// them.
    +
     3825.     38
    +
     3826.     10        if (a_set === undefined) {
    +
     3827.     10            a_set = empty();
    +
     3828.     10            when[arity] = a_set;
    +
     3829.     10        }
    +
     3830.     38
    +
     3831.     38// If this id has no registrations yet, then create a set array to hold them.
    +
     3832.     38
    +
     3833.     38        i_set = a_set[id];
    +
     3834.     37        if (i_set === undefined) {
    +
     3835.     37            i_set = [];
    +
     3836.     37            a_set[id] = i_set;
    +
     3837.     37        }
    +
     3838.     38
    +
     3839.     38// Register the task with the arity and the id.
    +
     3840.     38
    +
     3841.     38        i_set.push(task);
    +
     3842.     38    };
    +
     3843.      2}
    +
     3844.      1
    +
     3845.      2function amble(when) {
    +
     3846.      2
    +
     3847.      2// Produce a function that will act on the tasks registered by an action
    +
     3848.      2// function while walking the tree.
    +
     3849.      2
    +
     3850.  32988    return function (the_token) {
    +
     3851.  32988
    +
     3852.  32988// Given a task set that was built by an action function, run all of the
    +
     3853.  32988// relevant tasks on the token.
    +
     3854.  32988
    +
     3855.  32988        let a_set = when[the_token.arity];
    +
     3856.  32988        let i_set;
    +
     3857.  32988
    +
     3858.  32988// If there are tasks associated with the token's arity...
    +
     3859.  32988
    +
     3860.  22312        if (a_set !== undefined) {
    +
     3861.  22312
    +
     3862.  22312// If there are tasks associated with the token's id...
    +
     3863.  22312
    +
     3864.  22312            i_set = a_set[the_token.id];
    +
     3865.  22312            if (i_set !== undefined) {
    +
     3866.  22312                i_set.forEach(function (task) {
    +
     3867.  22312                    return task(the_token);
    +
     3868.  22312                });
    +
     3869.  22312            }
    +
     3870.  22312
    +
     3871.  22312// If there are tasks for all ids.
    +
     3872.  22312
    +
     3873.  22312            i_set = a_set["(all)"];
    +
     3874.  22312            if (i_set !== undefined) {
    +
     3875.  22312                i_set.forEach(function (task) {
    +
     3876.  22312                    return task(the_token);
    +
     3877.  22312                });
    +
     3878.  22312            }
    +
     3879.  22312        }
    +
     3880.  32988    };
    +
     3881.      2}
    +
     3882.      1
    +
     3883.      1const posts = empty();
    +
     3884.      1const pres = empty();
    +
     3885.      1const preaction = action(pres);
    +
     3886.      1const postaction = action(posts);
    +
     3887.      1const preamble = amble(pres);
    +
     3888.      1const postamble = amble(posts);
    +
     3889.      1
    +
     3890.  25743function walk_expression(thing) {
    +
     3891.  16169    if (thing) {
    +
     3892.  16169        if (Array.isArray(thing)) {
    +
     3893.  16169            thing.forEach(walk_expression);
    +
     3894.  16169        } else {
    +
     3895.  16169            preamble(thing);
    +
     3896.  16169            walk_expression(thing.expression);
    +
     3897.  16169            if (thing.id === "function") {
    +
     3898.  16169                walk_statement(thing.block);
    +
     3899.  16169            }
    +
     3900.  16169            if (thing.arity === "pre" || thing.arity === "post") {
    +
     3901.  16169                warn("unexpected_a", thing);
    +
     3902.  16169            } else if (
    +
     3903.  16169                thing.arity === "statement"
    +
     3904.  16169                || thing.arity === "assignment"
    +
     3905.      0            ) {
    +
     3906.      0                warn("unexpected_statement_a", thing);
    +
     3907.      0            }
    +
     3908.  16169            postamble(thing);
    +
     3909.  16169        }
    +
     3910.  16169    }
    +
     3911.  25743}
    +
     3912.      1
    +
     3913.  11919function walk_statement(thing) {
    +
     3914.   5451    if (thing) {
    +
     3915.   5451        if (Array.isArray(thing)) {
    +
     3916.   5451            thing.forEach(walk_statement);
    +
     3917.   5451        } else {
    +
     3918.   5451            preamble(thing);
    +
     3919.   5451            walk_expression(thing.expression);
    +
     3920.   5451            if (thing.arity === "binary") {
    +
     3921.   5451                if (thing.id !== "(") {
    +
     3922.   5451                    warn("unexpected_expression_a", thing);
    +
     3923.   5451                }
    +
     3924.   5451            } else if (
    +
     3925.   5451                thing.arity !== "statement"
    +
     3926.   5451                && thing.arity !== "assignment"
    +
     3927.   5451                && thing.id !== "import"
    +
     3928.   5451                && thing.id !== "await"
    +
     3929.   5451            ) {
    +
     3930.   5451                warn("unexpected_expression_a", thing);
    +
     3931.   5451            }
    +
     3932.   5451            walk_statement(thing.block);
    +
     3933.   5451            walk_statement(thing.else);
    +
     3934.   5451            postamble(thing);
    +
     3935.   5451        }
    +
     3936.   5451    }
    +
     3937.  11919}
    +
     3938.      1
    +
     3939.   4668function lookup(thing) {
    +
     3940.   4667    if (thing.arity === "variable") {
    +
     3941.   4667
    +
     3942.   4667// Look up the variable in the current context.
    +
     3943.   4667
    +
     3944.   4667        let the_variable = functionage.context[thing.id];
    +
     3945.   4667
    +
     3946.   4667// If it isn't local, search all the other contexts. If there are name
    +
     3947.   4667// collisions, take the most recent.
    +
     3948.   4667
    +
     3949.   4667        if (the_variable === undefined) {
    +
     3950.   4667            stack.forEach(function (outer) {
    +
     3951.   4667                const a_variable = outer.context[thing.id];
    +
     3952.   4667                if (
    +
     3953.   4667                    a_variable !== undefined
    +
     3954.   4667                    && a_variable.role !== "label"
    +
     3955.   4667                ) {
    +
     3956.   4667                    the_variable = a_variable;
    +
     3957.   4667                }
    +
     3958.   4667            });
    +
     3959.   4667
    +
     3960.   4667// If it isn't in any of those either, perhaps it is a predefined global.
    +
     3961.   4667// If so, add it to the global context.
    +
     3962.   4667
    +
     3963.   4667            if (the_variable === undefined) {
    +
     3964.   4667                if (declared_globals[thing.id] === undefined) {
    +
     3965.   4667                    warn("undeclared_a", thing);
    +
     3966.   4667                    return;
    +
     3967.   4667                }
    +
     3968.   4667                the_variable = {
    +
     3969.   4667                    dead: false,
    +
     3970.   4667                    parent: global,
    +
     3971.   4667                    id: thing.id,
    +
     3972.   4667                    init: true,
    +
     3973.   4667                    role: "variable",
    +
     3974.   4667                    used: 0,
    +
     3975.   4667                    writable: false
    +
     3976.   4667                };
    +
     3977.   4667                global.context[thing.id] = the_variable;
    +
     3978.   4667            }
    +
     3979.   4667            the_variable.closure = true;
    +
     3980.   4667            functionage.context[thing.id] = the_variable;
    +
     3981.   4667        } else if (the_variable.role === "label") {
    +
     3982.   4667            warn("label_a", thing);
    +
     3983.   4667        }
    +
     3984.   4667        if (
    +
     3985.   4667            the_variable.dead
    +
     3986.   4667            && (
    +
     3987.   4667                the_variable.calls === undefined
    +
     3988.   4667                || functionage.name === undefined
    +
     3989.   4667                || the_variable.calls[functionage.name.id] === undefined
    +
     3990.   4667            )
    +
     3991.   4667        ) {
    +
     3992.   4667            warn("out_of_scope_a", thing);
    +
     3993.   4667        }
    +
     3994.   4667        return the_variable;
    +
     3995.   4667    }
    +
     3996.   4668}
    +
     3997.      1
    +
     3998.     42function subactivate(name) {
    +
     3999.     42    name.init = true;
    +
     4000.     42    name.dead = false;
    +
     4001.     42    blockage.live.push(name);
    +
     4002.     42}
    +
     4003.      1
    +
     4004.    327function preaction_function(thing) {
    +
     4005.      0    if (thing.arity === "statement" && blockage.body !== true) {
    +
     4006.      0        warn("unexpected_a", thing);
    +
     4007.      0    }
    +
     4008.    327    stack.push(functionage);
    +
     4009.    327    block_stack.push(blockage);
    +
     4010.    327    functionage = thing;
    +
     4011.    327    blockage = thing;
    +
     4012.    327    thing.live = [];
    +
     4013.    158    if (typeof thing.name === "object") {
    +
     4014.    158        thing.name.dead = false;
    +
     4015.    158        thing.name.init = true;
    +
     4016.    158    }
    +
     4017.      1    if (thing.extra === "get") {
    +
     4018.      1        if (thing.parameters.length !== 0) {
    +
     4019.      1            warn("bad_get", thing);
    +
     4020.      1        }
    +
     4021.    326    } else if (thing.extra === "set") {
    +
     4022.    326        if (thing.parameters.length !== 1) {
    +
     4023.    326            warn("bad_set", thing);
    +
     4024.    326        }
    +
     4025.    326    }
    +
     4026.    264    thing.parameters.forEach(function (name) {
    +
     4027.    264        walk_expression(name.expression);
    +
     4028.    249        if (name.id === "{" || name.id === "[") {
    +
     4029.     18            name.names.forEach(subactivate);
    +
     4030.    246        } else {
    +
     4031.    246            name.dead = false;
    +
     4032.    246            name.init = true;
    +
     4033.    246        }
    +
     4034.    264    });
    +
     4035.    327}
    +
     4036.      1
    +
     4037.   5817function bitwise_check(thing) {
    +
     4038.     91    if (!option.bitwise && bitwiseop[thing.id] === true) {
    +
     4039.      1        warn("unexpected_a", thing);
    +
     4040.      1    }
    +
     4041.   5817    if (
    +
     4042.   5817        thing.id !== "("
    +
     4043.   4239        && thing.id !== "&&"
    +
     4044.   4045        && thing.id !== "||"
    +
     4045.   3860        && thing.id !== "="
    +
     4046.   3230        && Array.isArray(thing.expression)
    +
     4047.   1365        && thing.expression.length === 2
    +
     4048.   1353        && (
    +
     4049.   1353            relationop[thing.expression[0].id] === true
    +
     4050.   1353            || relationop[thing.expression[1].id] === true
    +
     4051.   1353        )
    +
     4052.      1    ) {
    +
     4053.      1        warn("unexpected_a", thing);
    +
     4054.      1    }
    +
     4055.   5817}
    +
     4056.      1
    +
     4057.   1384function pop_block() {
    +
     4058.    337    blockage.live.forEach(function (name) {
    +
     4059.    337        name.dead = true;
    +
     4060.    337    });
    +
     4061.   1384    delete blockage.live;
    +
     4062.   1384    blockage = block_stack.pop();
    +
     4063.   1384}
    +
     4064.      1
    +
     4065.    395function activate(name) {
    +
     4066.    395    name.dead = false;
    +
     4067.    252    if (name.expression !== undefined) {
    +
     4068.    252        walk_expression(name.expression);
    +
     4069.      0        if (name.id === "{" || name.id === "[") {
    +
     4070.      0            name.names.forEach(subactivate);
    +
     4071.      0        } else {
    +
     4072.    252            name.init = true;
    +
     4073.    252        }
    +
     4074.    252    }
    +
     4075.    395    blockage.live.push(name);
    +
     4076.    395}
    +
     4077.      1
    +
     4078.    394function action_var(thing) {
    +
     4079.    394    thing.names.forEach(activate);
    +
     4080.    394}
    +
     4081.      1
    +
     4082.      1preaction("assignment", bitwise_check);
    +
     4083.      1preaction("binary", bitwise_check);
    +
     4084.   5113preaction("binary", function (thing) {
    +
     4085.    810    if (relationop[thing.id] === true) {
    +
     4086.    810        const left = thing.expression[0];
    +
     4087.    810        const right = thing.expression[1];
    +
     4088.    810        if (left.id === "NaN" || right.id === "NaN") {
    +
     4089.    810            warn("number_isNaN", thing);
    +
     4090.    810        } else if (left.id === "typeof") {
    +
     4091.    810            if (right.id !== "(string)") {
    +
     4092.    810                if (right.id !== "typeof") {
    +
     4093.    810                    warn("expected_string_a", right);
    +
     4094.    810                }
    +
     4095.    810            } else {
    +
     4096.    810                const value = right.value;
    +
     4097.    810                if (value === "null" || value === "undefined") {
    +
     4098.    810                    warn("unexpected_typeof_a", right, value);
    +
     4099.    810                } else if (
    +
     4100.    810                    value !== "boolean"
    +
     4101.    810                    && value !== "function"
    +
     4102.    810                    && value !== "number"
    +
     4103.    810                    && value !== "object"
    +
     4104.    810                    && value !== "string"
    +
     4105.    810                    && value !== "symbol"
    +
     4106.    810                ) {
    +
     4107.    810                    warn("expected_type_string_a", right, value);
    +
     4108.    810                }
    +
     4109.    810            }
    +
     4110.    810        }
    +
     4111.    810    }
    +
     4112.   5113});
    +
     4113.      2preaction("binary", "==", function (thing) {
    +
     4114.      2    warn("expected_a_b", thing, "===", "==");
    +
     4115.      2});
    +
     4116.      1preaction("binary", "!=", function (thing) {
    +
     4117.      1    warn("expected_a_b", thing, "!==", "!=");
    +
     4118.      1});
    +
     4119.      1preaction("binary", "=>", preaction_function);
    +
     4120.    185preaction("binary", "||", function (thing) {
    +
     4121.    370    thing.expression.forEach(function (thang) {
    +
     4122.     35        if (thang.id === "&&" && !thang.wrapped) {
    +
     4123.      1            warn("and", thang);
    +
     4124.      1        }
    +
     4125.    370    });
    +
     4126.    185});
    +
     4127.   1578preaction("binary", "(", function (thing) {
    +
     4128.   1578    const left = thing.expression[0];
    +
     4129.   1578    if (
    +
     4130.   1578        left.identifier
    +
     4131.   1128        && functionage.context[left.id] === undefined
    +
     4132.    562        && typeof functionage.name === "object"
    +
     4133.    306    ) {
    +
     4134.    306        const parent = functionage.name.parent;
    +
     4135.    306        if (parent) {
    +
     4136.    306            const left_variable = parent.context[left.id];
    +
     4137.    306            if (
    +
     4138.    306                left_variable !== undefined
    +
     4139.    306                && left_variable.dead
    +
     4140.    306                && left_variable.parent === parent
    +
     4141.    306                && left_variable.calls !== undefined
    +
     4142.    306                && left_variable.calls[functionage.name.id] !== undefined
    +
     4143.    306            ) {
    +
     4144.    306                left_variable.dead = false;
    +
     4145.    306            }
    +
     4146.    306        }
    +
     4147.    306    }
    +
     4148.   1578});
    +
     4149.      1preaction("binary", "in", function (thing) {
    +
     4150.      1    warn("infix_in", thing);
    +
     4151.      1});
    +
     4152.      1preaction("binary", "instanceof", function (thing) {
    +
     4153.      1    warn("unexpected_a", thing);
    +
     4154.      1});
    +
     4155.   1863preaction("binary", ".", function (thing) {
    +
     4156.      0    if (thing.expression.new) {
    +
     4157.      0        thing.new = true;
    +
     4158.      0    }
    +
     4159.   1863});
    +
     4160.   1057preaction("statement", "{", function (thing) {
    +
     4161.   1057    block_stack.push(blockage);
    +
     4162.   1057    blockage = thing;
    +
     4163.   1057    thing.live = [];
    +
     4164.   1057});
    +
     4165.      5preaction("statement", "for", function (thing) {
    +
     4166.      3    if (thing.name !== undefined) {
    +
     4167.      3        const the_variable = lookup(thing.name);
    +
     4168.      3        if (the_variable !== undefined) {
    +
     4169.      3            the_variable.init = true;
    +
     4170.      3            if (!the_variable.writable) {
    +
     4171.      3                warn("bad_assignment_a", thing.name);
    +
     4172.      3            }
    +
     4173.      3        }
    +
     4174.      3    }
    +
     4175.      5    walk_statement(thing.initial);
    +
     4176.      5});
    +
     4177.      1preaction("statement", "function", preaction_function);
    +
     4178.      1preaction("unary", "~", bitwise_check);
    +
     4179.      1preaction("unary", "function", preaction_function);
    +
     4180.   4347preaction("variable", function (thing) {
    +
     4181.   4347    const the_variable = lookup(thing);
    +
     4182.   4309    if (the_variable !== undefined) {
    +
     4183.   4309        thing.variable = the_variable;
    +
     4184.   4309        the_variable.used += 1;
    +
     4185.   4309    }
    +
     4186.   4347});
    +
     4187.      1
    +
     4188.    318function init_variable(name) {
    +
     4189.    318    const the_variable = lookup(name);
    +
     4190.    293    if (the_variable !== undefined) {
    +
     4191.    293        if (the_variable.writable) {
    +
     4192.    293            the_variable.init = true;
    +
     4193.    293            return;
    +
     4194.    293        }
    +
     4195.    293    }
    +
     4196.     25    warn("bad_assignment_a", name);
    +
     4197.     25}
    +
     4198.      1
    +
     4199.     65postaction("assignment", "+=", function (thing) {
    +
     4200.     65    let right = thing.expression[1];
    +
     4201.     38    if (right.constant) {
    +
     4202.     38        if (
    +
     4203.     38            right.value === ""
    +
     4204.     38            || (right.id === "(number)" && right.value === "0")
    +
     4205.     38            || right.id === "(boolean)"
    +
     4206.     38            || right.id === "null"
    +
     4207.     38            || right.id === "undefined"
    +
     4208.     38            || Number.isNaN(right.value)
    +
     4209.     38        ) {
    +
     4210.     38            warn("unexpected_a", right);
    +
     4211.     38        }
    +
     4212.     38    }
    +
     4213.     65});
    +
     4214.    704postaction("assignment", function (thing) {
    +
     4215.    704
    +
     4216.    704// Assignment using = sets the init property of a variable. No other assignment
    +
     4217.    704// operator can do this. A = token keeps that variable (or array of variables
    +
     4218.    704// in case of destructuring) in its name property.
    +
     4219.    704
    +
     4220.    704    const lvalue = thing.expression[0];
    +
     4221.    630    if (thing.id === "=") {
    +
     4222.    630        if (thing.names !== undefined) {
    +
     4223.      0            if (Array.isArray(thing.names)) {
    +
     4224.      0                thing.names.forEach(init_variable);
    +
     4225.      0            } else {
    +
     4226.    630                init_variable(thing.names);
    +
     4227.    630            }
    +
     4228.    630        } else {
    +
     4229.    630            if (lvalue.id === "[" || lvalue.id === "{") {
    +
     4230.    630                lvalue.expression.forEach(function (thing) {
    +
     4231.    630                    if (thing.variable) {
    +
     4232.    630                        thing.variable.init = true;
    +
     4233.    630                    }
    +
     4234.    630                });
    +
     4235.    630            } else if (
    +
     4236.    630                lvalue.id === "."
    +
     4237.    630                && thing.expression[1].id === "undefined"
    +
     4238.    630            ) {
    +
     4239.    630                warn(
    +
     4240.    630                    "expected_a_b",
    +
     4241.    630                    lvalue.expression,
    +
     4242.    630                    "delete",
    +
     4243.    630                    "undefined"
    +
     4244.    630                );
    +
     4245.    630            }
    +
     4246.    630        }
    +
     4247.    630    } else {
    +
     4248.     74        if (lvalue.arity === "variable") {
    +
     4249.     74            if (!lvalue.variable || lvalue.variable.writable !== true) {
    +
     4250.     74                warn("bad_assignment_a", lvalue);
    +
     4251.     74            }
    +
     4252.     74        }
    +
     4253.     74        const right = syntax[thing.expression[1].id];
    +
     4254.     74        if (
    +
     4255.     74            right !== undefined
    +
     4256.     74            && (
    +
     4257.     74                right.id === "function"
    +
     4258.     74                || right.id === "=>"
    +
     4259.     74                || (
    +
     4260.     74                    right.constant
    +
     4261.     74                    && right.id !== "(number)"
    +
     4262.     74                    && (right.id !== "(string)" || thing.id !== "+=")
    +
     4263.     74                )
    +
     4264.     74            )
    +
     4265.     74        ) {
    +
     4266.     74            warn("unexpected_a", thing.expression[1]);
    +
     4267.     74        }
    +
     4268.     74    }
    +
     4269.    704});
    +
     4270.      1
    +
     4271.    327function postaction_function(thing) {
    +
     4272.    327    delete functionage.finally;
    +
     4273.    327    delete functionage.loop;
    +
     4274.    327    delete functionage.switch;
    +
     4275.    327    delete functionage.try;
    +
     4276.    327    functionage = stack.pop();
    +
     4277.      1    if (thing.wrapped) {
    +
     4278.      1        warn("unexpected_parens", thing);
    +
     4279.      1    }
    +
     4280.    327    return pop_block();
    +
     4281.    327}
    +
     4282.      1
    +
     4283.   5113postaction("binary", function (thing) {
    +
     4284.   5113    let right;
    +
     4285.    810    if (relationop[thing.id]) {
    +
     4286.    810        if (
    +
     4287.    810            is_weird(thing.expression[0])
    +
     4288.    810            || is_weird(thing.expression[1])
    +
     4289.    810            || are_similar(thing.expression[0], thing.expression[1])
    +
     4290.    810            || (
    +
     4291.    810                thing.expression[0].constant === true
    +
     4292.    810                && thing.expression[1].constant === true
    +
     4293.    810            )
    +
     4294.    810        ) {
    +
     4295.    810            warn("weird_relation_a", thing);
    +
     4296.    810        }
    +
     4297.    810    }
    +
     4298.    201    if (thing.id === "+") {
    +
     4299.    201        if (!option.convert) {
    +
     4300.    201            if (thing.expression[0].value === "") {
    +
     4301.    201                warn("expected_a_b", thing, "String(...)", "\"\" +");
    +
     4302.    201            } else if (thing.expression[1].value === "") {
    +
     4303.    201                warn("expected_a_b", thing, "String(...)", "+ \"\"");
    +
     4304.    201            }
    +
     4305.    201        }
    +
     4306.   4912    } else if (thing.id === "[") {
    +
     4307.   4912        if (thing.expression[0].id === "window") {
    +
     4308.   4912            warn("weird_expression_a", thing, "window[...]");
    +
     4309.   4912        }
    +
     4310.   4912        if (thing.expression[0].id === "self") {
    +
     4311.   4912            warn("weird_expression_a", thing, "self[...]");
    +
     4312.   4912        }
    +
     4313.   4912    } else if (thing.id === "." || thing.id === "?.") {
    +
     4314.   4912        if (thing.expression.id === "RegExp") {
    +
     4315.   4912            warn("weird_expression_a", thing);
    +
     4316.   4912        }
    +
     4317.   4912    } else if (thing.id !== "=>" && thing.id !== "(") {
    +
     4318.   4912        right = thing.expression[1];
    +
     4319.   4912        if (
    +
     4320.   4912            (thing.id === "+" || thing.id === "-")
    +
     4321.   4912            && right.id === thing.id
    +
     4322.   4912            && right.arity === "unary"
    +
     4323.   4912            && !right.wrapped
    +
     4324.   4912        ) {
    +
     4325.   4912            warn("wrap_unary", right);
    +
     4326.   4912        }
    +
     4327.   4912        if (
    +
     4328.   4912            thing.expression[0].constant === true
    +
     4329.   4912            && right.constant === true
    +
     4330.   4912        ) {
    +
     4331.   4912            thing.constant = true;
    +
     4332.   4912        }
    +
     4333.   4912    }
    +
     4334.   5113});
    +
     4335.    194postaction("binary", "&&", function (thing) {
    +
     4336.    194    if (
    +
     4337.    194        is_weird(thing.expression[0])
    +
     4338.    194        || are_similar(thing.expression[0], thing.expression[1])
    +
     4339.    192        || thing.expression[0].constant === true
    +
     4340.    192        || thing.expression[1].constant === true
    +
     4341.      2    ) {
    +
     4342.      2        warn("weird_condition_a", thing);
    +
     4343.      2    }
    +
     4344.    194});
    +
     4345.    185postaction("binary", "||", function (thing) {
    +
     4346.    185    if (
    +
     4347.    185        is_weird(thing.expression[0])
    +
     4348.    185        || are_similar(thing.expression[0], thing.expression[1])
    +
     4349.    184        || thing.expression[0].constant === true
    +
     4350.      1    ) {
    +
     4351.      1        warn("weird_condition_a", thing);
    +
     4352.      1    }
    +
     4353.    185});
    +
     4354.      1postaction("binary", "=>", postaction_function);
    +
     4355.   1578postaction("binary", "(", function (thing) {
    +
     4356.   1578    let left = thing.expression[0];
    +
     4357.   1578    let the_new;
    +
     4358.   1578    let arg;
    +
     4359.     14    if (left.id === "new") {
    +
     4360.     14        the_new = left;
    +
     4361.     14        left = left.expression;
    +
     4362.     14    }
    +
     4363.     33    if (left.id === "function") {
    +
     4364.     33        if (!thing.wrapped) {
    +
     4365.     33            warn("wrap_immediate", thing);
    +
     4366.     33        }
    +
     4367.   1545    } else if (left.identifier) {
    +
     4368.   1545        if (the_new !== undefined) {
    +
     4369.   1545            if (
    +
     4370.   1545                left.id[0] > "Z"
    +
     4371.   1545                || left.id === "Boolean"
    +
     4372.   1545                || left.id === "Number"
    +
     4373.   1545                || left.id === "String"
    +
     4374.   1545                || left.id === "Symbol"
    +
     4375.   1545            ) {
    +
     4376.   1545                warn("unexpected_a", the_new);
    +
     4377.   1545            } else if (left.id === "Function") {
    +
     4378.   1545                if (!option.eval) {
    +
     4379.   1545                    warn("unexpected_a", left, "new Function");
    +
     4380.   1545                }
    +
     4381.   1545            } else if (left.id === "Array") {
    +
     4382.   1545                arg = thing.expression;
    +
     4383.   1545                if (arg.length !== 2 || arg[1].id === "(string)") {
    +
     4384.   1545                    warn("expected_a_b", left, "[]", "new Array");
    +
     4385.   1545                }
    +
     4386.   1545            } else if (left.id === "Object") {
    +
     4387.   1545                warn(
    +
     4388.   1545                    "expected_a_b",
    +
     4389.   1545                    left,
    +
     4390.   1545                    "Object.create(null)",
    +
     4391.   1545                    "new Object"
    +
     4392.   1545                );
    +
     4393.   1545            }
    +
     4394.   1545        } else {
    +
     4395.   1545            if (
    +
     4396.   1545                left.id[0] >= "A"
    +
     4397.   1545                && left.id[0] <= "Z"
    +
     4398.   1545                && left.id !== "Boolean"
    +
     4399.   1545                && left.id !== "Number"
    +
     4400.   1545                && left.id !== "String"
    +
     4401.   1545                && left.id !== "Symbol"
    +
     4402.   1545            ) {
    +
     4403.   1545                warn(
    +
     4404.   1545                    "expected_a_before_b",
    +
     4405.   1545                    left,
    +
     4406.   1545                    "new",
    +
     4407.   1545                    artifact(left)
    +
     4408.   1545                );
    +
     4409.   1545            }
    +
     4410.   1545        }
    +
     4411.   1545    } else if (left.id === ".") {
    +
     4412.   1545        let cack = the_new !== undefined;
    +
     4413.   1545        if (left.expression.id === "Date" && left.name.id === "UTC") {
    +
     4414.   1545            cack = !cack;
    +
     4415.   1545        }
    +
     4416.   1545        if (rx_cap.test(left.name.id) !== cack) {
    +
     4417.   1545            if (the_new !== undefined) {
    +
     4418.   1545                warn("unexpected_a", the_new);
    +
     4419.   1545            } else {
    +
     4420.   1545                warn(
    +
     4421.   1545                    "expected_a_before_b",
    +
     4422.   1545                    left.expression,
    +
     4423.   1545                    "new",
    +
     4424.   1545                    left.name.id
    +
     4425.   1545                );
    +
     4426.   1545            }
    +
     4427.   1545        }
    +
     4428.   1545        if (left.name.id === "getTime") {
    +
     4429.   1545            const paren = left.expression;
    +
     4430.   1545            if (paren.id === "(") {
    +
     4431.   1545                const array = paren.expression;
    +
     4432.   1545                if (array.length === 1) {
    +
     4433.   1545                    const new_date = array[0];
    +
     4434.   1545                    if (
    +
     4435.   1545                        new_date.id === "new"
    +
     4436.   1545                        && new_date.expression.id === "Date"
    +
     4437.   1545                    ) {
    +
     4438.   1545                        warn(
    +
     4439.   1545                            "expected_a_b",
    +
     4440.   1545                            new_date,
    +
     4441.   1545                            "Date.now()",
    +
     4442.   1545                            "new Date().getTime()"
    +
     4443.   1545                        );
    +
     4444.   1545                    }
    +
     4445.   1545                }
    +
     4446.   1545            }
    +
     4447.   1545        }
    +
     4448.   1545    }
    +
     4449.   1578});
    +
     4450.    221postaction("binary", "[", function (thing) {
    +
     4451.      1    if (thing.expression[0].id === "RegExp") {
    +
     4452.      1        warn("weird_expression_a", thing);
    +
     4453.      1    }
    +
     4454.      1    if (is_weird(thing.expression[1])) {
    +
     4455.      1        warn("weird_expression_a", thing.expression[1]);
    +
     4456.      1    }
    +
     4457.    221});
    +
     4458.      1postaction("statement", "{", pop_block);
    +
     4459.      1postaction("statement", "const", action_var);
    +
     4460.      1postaction("statement", "export", top_level_only);
    +
     4461.      5postaction("statement", "for", function (thing) {
    +
     4462.      5    walk_statement(thing.inc);
    +
     4463.      5});
    +
     4464.      1postaction("statement", "function", postaction_function);
    +
     4465.      9postaction("statement", "import", function (the_thing) {
    +
     4466.      9    const name = the_thing.name;
    +
     4467.      9    if (name) {
    +
     4468.      1        if (Array.isArray(name)) {
    +
     4469.      1            name.forEach(function (name) {
    +
     4470.      1                name.dead = false;
    +
     4471.      1                name.init = true;
    +
     4472.      1                blockage.live.push(name);
    +
     4473.      1            });
    +
     4474.      8        } else {
    +
     4475.      8            name.dead = false;
    +
     4476.      8            name.init = true;
    +
     4477.      8            blockage.live.push(name);
    +
     4478.      8        }
    +
     4479.      9        return top_level_only(the_thing);
    +
     4480.      9    }
    +
     4481.      9});
    +
     4482.      1postaction("statement", "let", action_var);
    +
     4483.      7postaction("statement", "try", function (thing) {
    +
     4484.      6    if (thing.catch !== undefined) {
    +
     4485.      6        const the_name = thing.catch.name;
    +
     4486.      6        if (the_name !== undefined) {
    +
     4487.      6            const the_variable = functionage.context[the_name.id];
    +
     4488.      6            the_variable.dead = false;
    +
     4489.      6            the_variable.init = true;
    +
     4490.      6        }
    +
     4491.      6        walk_statement(thing.catch.block);
    +
     4492.      6    }
    +
     4493.      7});
    +
     4494.      1postaction("statement", "var", action_var);
    +
     4495.     41postaction("ternary", function (thing) {
    +
     4496.     41    if (
    +
     4497.     41        is_weird(thing.expression[0])
    +
     4498.     41        || thing.expression[0].constant === true
    +
     4499.     38        || are_similar(thing.expression[1], thing.expression[2])
    +
     4500.      3    ) {
    +
     4501.      3        warn("unexpected_a", thing);
    +
     4502.     38    } else if (are_similar(thing.expression[0], thing.expression[1])) {
    +
     4503.     38        warn("expected_a_b", thing, "||", "?");
    +
     4504.     38    } else if (are_similar(thing.expression[0], thing.expression[2])) {
    +
     4505.     38        warn("expected_a_b", thing, "&&", "?");
    +
     4506.     38    } else if (
    +
     4507.     38        thing.expression[1].id === "true"
    +
     4508.     38        && thing.expression[2].id === "false"
    +
     4509.     38    ) {
    +
     4510.     38        warn("expected_a_b", thing, "!!", "?");
    +
     4511.     38    } else if (
    +
     4512.     38        thing.expression[1].id === "false"
    +
     4513.     38        && thing.expression[2].id === "true"
    +
     4514.     38    ) {
    +
     4515.     38        warn("expected_a_b", thing, "!", "?");
    +
     4516.     38    } else if (
    +
     4517.     38        thing.expression[0].wrapped !== true
    +
     4518.     38        && (
    +
     4519.     38            thing.expression[0].id === "||"
    +
     4520.     38            || thing.expression[0].id === "&&"
    +
     4521.     38        )
    +
     4522.     38    ) {
    +
     4523.     38        warn("wrap_condition", thing.expression[0]);
    +
     4524.     38    }
    +
     4525.     41});
    +
     4526.    583postaction("unary", function (thing) {
    +
     4527.     14    if (thing.id === "`") {
    +
     4528.     14        if (thing.expression.every(function (thing) {
    +
     4529.     14            return thing.constant;
    +
     4530.     14        })) {
    +
     4531.     14            thing.constant = true;
    +
     4532.     14        }
    +
     4533.    569    } else if (thing.id === "!") {
    +
     4534.    569        if (thing.expression.constant === true) {
    +
     4535.    569            warn("unexpected_a", thing);
    +
     4536.    569        }
    +
     4537.    569    } else if (thing.id === "!!") {
    +
     4538.    569        if (!option.convert) {
    +
     4539.    569            warn("expected_a_b", thing, "Boolean(...)", "!!");
    +
     4540.    569        }
    +
     4541.    569    } else if (
    +
     4542.    569        thing.id !== "["
    +
     4543.    569        && thing.id !== "{"
    +
     4544.    569        && thing.id !== "function"
    +
     4545.    569        && thing.id !== "new"
    +
     4546.    569    ) {
    +
     4547.    569        if (thing.expression.constant === true) {
    +
     4548.    569            thing.constant = true;
    +
     4549.    569        }
    +
     4550.    569    }
    +
     4551.    583});
    +
     4552.      1postaction("unary", "function", postaction_function);
    +
     4553.      4postaction("unary", "+", function (thing) {
    +
     4554.      4    if (!option.convert) {
    +
     4555.      4        warn("expected_a_b", thing, "Number(...)", "+");
    +
     4556.      4    }
    +
     4557.      4    const right = thing.expression;
    +
     4558.      0    if (right.id === "(" && right.expression[0].id === "new") {
    +
     4559.      0        warn("unexpected_a_before_b", thing, "+", "new");
    +
     4560.      0    } else if (
    +
     4561.      4        right.constant
    +
     4562.      1        || right.id === "{"
    +
     4563.      0        || (right.id === "[" && right.arity !== "binary")
    +
     4564.      3    ) {
    +
     4565.      3        warn("unexpected_a", thing, "+");
    +
     4566.      3    }
    +
     4567.      4});
    +
     4568.      1
    +
     4569.    313function delve(the_function) {
    +
     4570.   2059    Object.keys(the_function.context).forEach(function (id) {
    +
     4571.   2051        if (id !== "ignore") {
    +
     4572.   2051            const name = the_function.context[id];
    +
     4573.   2051            if (name.parent === the_function) {
    +
     4574.   2051                if (
    +
     4575.   2051                    name.used === 0
    +
     4576.   2051                    && (
    +
     4577.   2051                        name.role !== "function"
    +
     4578.      0                        || name.parent.arity !== "unary"
    +
     4579.   2051                    )
    +
     4580.   2051                ) {
    +
     4581.   2051                    warn("unused_a", name);
    +
     4582.   2051                } else if (!name.init) {
    +
     4583.   2051                    warn("uninitialized_a", name);
    +
     4584.   2051                }
    +
     4585.   2051            }
    +
     4586.   2051        }
    +
     4587.   2059    });
    +
     4588.    313}
    +
     4589.      1
    +
     4590.     17function uninitialized_and_unused() {
    +
     4591.     17
    +
     4592.     17// Delve into the functions looking for variables that were not initialized
    +
     4593.     17// or used. If the file imports or exports, then its global object is also
    +
     4594.     17// delved.
    +
     4595.     17
    +
     4596.     13    if (module_mode === true || option.node) {
    +
     4597.     13        delve(global);
    +
     4598.     13    }
    +
     4599.     17    functions.forEach(delve);
    +
     4600.     17}
    +
     4601.      1
    +
     4602.      1// Go through the token list, looking at usage of whitespace.
    +
     4603.      1
    +
     4604.     17function whitage() {
    +
     4605.     17    let closer = "(end)";
    +
     4606.     17    let free = false;
    +
     4607.     17    let left = global;
    +
     4608.     17    let margin = 0;
    +
     4609.     17    let nr_comments_skipped = 0;
    +
     4610.     17    let open = true;
    +
     4611.     17    let opening = true;
    +
     4612.     17    let right;
    +
     4613.     17
    +
     4614.   3722    function pop() {
    +
     4615.   3722        const previous = stack.pop();
    +
     4616.   3722        closer = previous.closer;
    +
     4617.   3722        free = previous.free;
    +
     4618.   3722        margin = previous.margin;
    +
     4619.   3722        open = previous.open;
    +
     4620.   3722        opening = previous.opening;
    +
     4621.   3722    }
    +
     4622.     17
    +
     4623.   3722    function push() {
    +
     4624.   3722        stack.push({
    +
     4625.   3722            closer,
    +
     4626.   3722            free,
    +
     4627.   3722            margin,
    +
     4628.   3722            open,
    +
     4629.   3722            opening
    +
     4630.   3722        });
    +
     4631.   3722    }
    +
     4632.     17
    +
     4633.      2    function expected_at(at) {
    +
     4634.      2        warn(
    +
     4635.      2            "expected_a_at_b_c",
    +
     4636.      2            right,
    +
     4637.      2            artifact(right),
    +
     4638.      2            fudge + at,
    +
     4639.      2            artifact_column(right)
    +
     4640.      2        );
    +
     4641.      2    }
    +
     4642.     17
    +
     4643.   5437    function at_margin(fit) {
    +
     4644.   5437        const at = margin + fit;
    +
     4645.      1        if (right.from !== at) {
    +
     4646.      1            return expected_at(at);
    +
     4647.      1        }
    +
     4648.   5437    }
    +
     4649.     17
    +
     4650.  14314    function no_space_only() {
    +
     4651.  14314        if (
    +
     4652.  14314            left.id !== "(global)"
    +
     4653.  14314            && left.nr + 1 === right.nr
    +
     4654.  14314            && (
    +
     4655.  14314                left.line !== right.line
    +
     4656.  14314                || left.thru !== right.from
    +
     4657.  14314            )
    +
     4658.      2        ) {
    +
     4659.      2            warn(
    +
     4660.      2                "unexpected_space_a_b",
    +
     4661.      2                right,
    +
     4662.      2                artifact(left),
    +
     4663.      2                artifact(right)
    +
     4664.      2            );
    +
     4665.      2        }
    +
     4666.  14314    }
    +
     4667.     17
    +
     4668.    515    function no_space() {
    +
     4669.    515        if (left.line === right.line) {
    +
     4670.      0            if (left.thru !== right.from && nr_comments_skipped === 0) {
    +
     4671.      0                warn(
    +
     4672.      0                    "unexpected_space_a_b",
    +
     4673.      0                    right,
    +
     4674.      0                    artifact(left),
    +
     4675.      0                    artifact(right)
    +
     4676.      0                );
    +
     4677.      0            }
    +
     4678.      0        } else {
    +
     4679.      0            if (open) {
    +
     4680.      0                const at = (
    +
     4681.      0                    free
    +
     4682.      0                    ? margin
    +
     4683.      0                    : margin + 8
    +
     4684.      0                );
    +
     4685.      0                if (right.from < at) {
    +
     4686.      0                    expected_at(at);
    +
     4687.      0                }
    +
     4688.      0            } else {
    +
     4689.      0                if (right.from !== margin + 8) {
    +
     4690.      0                    expected_at(margin + 8);
    +
     4691.      0                }
    +
     4692.      0            }
    +
     4693.      0        }
    +
     4694.    515    }
    +
     4695.     17
    +
     4696.   1335    function one_space_only() {
    +
     4697.      0        if (left.line !== right.line || left.thru + 1 !== right.from) {
    +
     4698.      0            warn(
    +
     4699.      0                "expected_space_a_b",
    +
     4700.      0                right,
    +
     4701.      0                artifact(left),
    +
     4702.      0                artifact(right)
    +
     4703.      0            );
    +
     4704.      0        }
    +
     4705.   1335    }
    +
     4706.     17
    +
     4707.   7533    function one_space() {
    +
     4708.   7260        if (left.line === right.line || !open) {
    +
     4709.   7260            if (left.thru + 1 !== right.from && nr_comments_skipped === 0) {
    +
     4710.   7260                warn(
    +
     4711.   7260                    "expected_space_a_b",
    +
     4712.   7260                    right,
    +
     4713.   7260                    artifact(left),
    +
     4714.   7260                    artifact(right)
    +
     4715.   7260                );
    +
     4716.   7260            }
    +
     4717.   7260        } else {
    +
     4718.    273            if (right.from !== margin) {
    +
     4719.    273                expected_at(margin);
    +
     4720.    273            }
    +
     4721.    273        }
    +
     4722.   7533    }
    +
     4723.     17
    +
     4724.     17    stack = [];
    +
     4725.  29753    tokens.forEach(function (the_token) {
    +
     4726.  29753        right = the_token;
    +
     4727.  29257        if (right.id === "(comment)" || right.id === "(end)") {
    +
     4728.    513            nr_comments_skipped += 1;
    +
     4729.  29240        } else {
    +
     4730.  29240
    +
     4731.  29240// If left is an opener and right is not the closer, then push the previous
    +
     4732.  29240// state. If the token following the opener is on the next line, then this is
    +
     4733.  29240// an open form. If the tokens are on the same line, then it is a closed form.
    +
     4734.  29240// Open form is more readable, with each item (statement, argument, parameter,
    +
     4735.  29240// etc) starting on its own line. Closed form is more compact. Statement blocks
    +
     4736.  29240// are always in open form.
    +
     4737.  29240
    +
     4738.  29240            const new_closer = opener[left.id];
    +
     4739.  29240            if (typeof new_closer === "string") {
    +
     4740.  29240                if (new_closer !== right.id) {
    +
     4741.  29240                    opening = left.open || (left.line !== right.line);
    +
     4742.  29240                    push();
    +
     4743.  29240                    closer = new_closer;
    +
     4744.  29240                    if (opening) {
    +
     4745.  29240                        free = closer === ")" && left.free;
    +
     4746.  29240                        open = true;
    +
     4747.  29240                        margin += 4;
    +
     4748.      0                        if (right.role === "label") {
    +
     4749.      0                            if (right.from !== 0) {
    +
     4750.      0                                expected_at(0);
    +
     4751.      0                            }
    +
     4752.      0                        } else if (right.switch) {
    +
     4753.  29240                            at_margin(-4);
    +
     4754.  29240                        } else {
    +
     4755.  29240                            at_margin(0);
    +
     4756.  29240                        }
    +
     4757.  29240                    } else {
    +
     4758.      0                        if (right.statement || right.role === "label") {
    +
     4759.      0                            warn(
    +
     4760.      0                                "expected_line_break_a_b",
    +
     4761.      0                                right,
    +
     4762.      0                                artifact(left),
    +
     4763.      0                                artifact(right)
    +
     4764.      0                            );
    +
     4765.      0                        }
    +
     4766.  29240                        free = false;
    +
     4767.  29240                        open = false;
    +
     4768.  29240                        no_space_only();
    +
     4769.  29240                    }
    +
     4770.  29240                } else {
    +
     4771.  29240
    +
     4772.  29240// If left and right are opener and closer, then the placement of right depends
    +
     4773.  29240// on the openness. Illegal pairs (like '{]') have already been detected.
    +
     4774.  29240
    +
     4775.  29240                    if (left.line === right.line) {
    +
     4776.  29240                        no_space();
    +
     4777.      0                    } else {
    +
     4778.      0                        at_margin(0);
    +
     4779.      0                    }
    +
     4780.  29240                }
    +
     4781.  29240            } else {
    +
     4782.  29240                if (right.statement === true) {
    +
     4783.  29240                    if (left.id === "else") {
    +
     4784.  29240                        one_space_only();
    +
     4785.  29240                    } else {
    +
     4786.  29240                        at_margin(0);
    +
     4787.  29240                        open = false;
    +
     4788.  29240                    }
    +
     4789.  29240
    +
     4790.  29240// If right is a closer, then pop the previous state.
    +
     4791.  29240
    +
     4792.  29240                } else if (right.id === closer) {
    +
     4793.  29240                    pop();
    +
     4794.  29240                    if (opening && right.id !== ";") {
    +
     4795.  29240                        at_margin(0);
    +
     4796.  29240                    } else {
    +
     4797.  29240                        no_space_only();
    +
     4798.  29240                    }
    +
     4799.  29240                } else {
    +
     4800.  29240
    +
     4801.  29240// Left is not an opener, and right is not a closer.
    +
     4802.  29240// The nature of left and right will determine the space between them.
    +
     4803.  29240
    +
     4804.  29240// If left is ',' or ';' or right is a statement then if open,
    +
     4805.  29240// right must go at the margin, or if closed, a space between.
    +
     4806.  29240
    +
     4807.  29240                    if (right.switch) {
    +
     4808.  29240                        at_margin(-4);
    +
     4809.      0                    } else if (right.role === "label") {
    +
     4810.      0                        if (right.from !== 0) {
    +
     4811.      0                            expected_at(0);
    +
     4812.      0                        }
    +
     4813.      0                    } else if (left.id === ",") {
    +
     4814.  29240                        if (!open || (
    +
     4815.  29240                            (free || closer === "]")
    +
     4816.  29240                            && left.line === right.line
    +
     4817.  29240                        )) {
    +
     4818.  29240                            one_space();
    +
     4819.  29240                        } else {
    +
     4820.  29240                            at_margin(0);
    +
     4821.  29240                        }
    +
     4822.  29240
    +
     4823.  29240// If right is a ternary operator, line it up on the margin.
    +
     4824.  29240
    +
     4825.  29240                    } else if (right.arity === "ternary") {
    +
     4826.  29240                        if (open) {
    +
     4827.  29240                            at_margin(0);
    +
     4828.      0                        } else {
    +
     4829.      0                            warn("use_open", right);
    +
     4830.      0                        }
    +
     4831.  29240                    } else if (
    +
     4832.  29240                        right.arity === "binary"
    +
     4833.  29240                        && right.id === "("
    +
     4834.  29240                        && free
    +
     4835.  29240                    ) {
    +
     4836.  29240                        no_space();
    +
     4837.  29240                    } else if (
    +
     4838.  29240                        left.id === "."
    +
     4839.  29240                        || left.id === "?."
    +
     4840.  29240                        || left.id === "..."
    +
     4841.  29240                        || right.id === ","
    +
     4842.  29240                        || right.id === ";"
    +
     4843.  29240                        || right.id === ":"
    +
     4844.  29240                        || (
    +
     4845.  29240                            right.arity === "binary"
    +
     4846.  29240                            && (right.id === "(" || right.id === "[")
    +
     4847.  29240                        )
    +
     4848.  29240                        || (
    +
     4849.  29240                            right.arity === "function"
    +
     4850.  29240                            && left.id !== "function"
    +
     4851.  29240                        )
    +
     4852.  29240                    ) {
    +
     4853.  29240                        no_space_only();
    +
     4854.  29240                    } else if (right.id === "." || right.id === "?.") {
    +
     4855.  29240                        no_space_only();
    +
     4856.      0                    } else if (left.id === ";") {
    +
     4857.      0                        if (open) {
    +
     4858.      0                            at_margin(0);
    +
     4859.      0                        }
    +
     4860.      0                    } else if (
    +
     4861.  29240                        left.arity === "ternary"
    +
     4862.  29240                        || left.id === "case"
    +
     4863.  29240                        || left.id === "catch"
    +
     4864.  29240                        || left.id === "else"
    +
     4865.  29240                        || left.id === "finally"
    +
     4866.  29240                        || left.id === "while"
    +
     4867.  29240                        || left.id === "await"
    +
     4868.  29240                        || right.id === "catch"
    +
     4869.  29240                        || right.id === "else"
    +
     4870.  29240                        || right.id === "finally"
    +
     4871.      0                        || (right.id === "while" && !right.statement)
    +
     4872.  29240                        || (left.id === ")" && right.id === "{")
    +
     4873.  29240                    ) {
    +
     4874.  29240                        one_space_only();
    +
     4875.  29240                    } else if (
    +
     4876.  29240
    +
     4877.  29240// There is a space between left and right.
    +
     4878.  29240
    +
     4879.  29240                        spaceop[left.id] === true
    +
     4880.  29240                        || spaceop[right.id] === true
    +
     4881.  29240                        || (
    +
     4882.  29240                            left.arity === "binary"
    +
     4883.  29240                            && (left.id === "+" || left.id === "-")
    +
     4884.  29240                        )
    +
     4885.  29240                        || (
    +
     4886.  29240                            right.arity === "binary"
    +
     4887.  29240                            && (right.id === "+" || right.id === "-")
    +
     4888.  29240                        )
    +
     4889.  29240                        || left.id === "function"
    +
     4890.  29240                        || left.id === ":"
    +
     4891.  29240                        || (
    +
     4892.  29240                            (
    +
     4893.  29240                                left.identifier
    +
     4894.  29240                                || left.id === "(string)"
    +
     4895.  29240                                || left.id === "(number)"
    +
     4896.  29240                            )
    +
     4897.  29240                            && (
    +
     4898.  29240                                right.identifier
    +
     4899.  29240                                || right.id === "(string)"
    +
     4900.  29240                                || right.id === "(number)"
    +
     4901.  29240                            )
    +
     4902.  29240                        )
    +
     4903.  29240                        || (left.arity === "statement" && right.id !== ";")
    +
     4904.  29240                    ) {
    +
     4905.  29240                        one_space();
    +
     4906.  29240                    } else if (left.arity === "unary" && left.id !== "`") {
    +
     4907.  29240                        no_space_only();
    +
     4908.  29240                    }
    +
     4909.  29240                }
    +
     4910.  29240            }
    +
     4911.  29240            nr_comments_skipped = 0;
    +
     4912.  29240            delete left.calls;
    +
     4913.  29240            delete left.dead;
    +
     4914.  29240            delete left.free;
    +
     4915.  29240            delete left.init;
    +
     4916.  29240            delete left.open;
    +
     4917.  29240            delete left.used;
    +
     4918.  29240            left = right;
    +
     4919.  29240        }
    +
     4920.  29753    });
    +
     4921.     17}
    +
     4922.      1
    +
     4923.      1// The jslint function itself.
    +
     4924.      1
    +
     4925.    211function jslint(
    +
     4926.    211    source = "",
    +
     4927.    211    option_object = empty(),
    +
     4928.    211    global_array = []
    +
     4929.    211) {
    +
     4930.    211    try {
    +
     4931.    211        warnings = [];
    +
     4932.    211        option = Object.assign(empty(), option_object);
    +
     4933.    211        anon = "anonymous";
    +
     4934.    211        block_stack = [];
    +
     4935.    211        declared_globals = empty();
    +
     4936.    211        directive_mode = true;
    +
     4937.    211        directives = [];
    +
     4938.    211        early_stop = true;
    +
     4939.    211        exports = empty();
    +
     4940.    211        froms = [];
    +
     4941.    211        fudge = (
    +
     4942.    211            option.fudge
    +
     4943.     12            ? 1
    +
     4944.    199            : 0
    +
     4945.    211        );
    +
     4946.    211        functions = [];
    +
     4947.    211        global = {
    +
     4948.    211            id: "(global)",
    +
     4949.    211            body: true,
    +
     4950.    211            context: empty(),
    +
     4951.    211            from: 0,
    +
     4952.    211            level: 0,
    +
     4953.    211            line: 0,
    +
     4954.    211            live: [],
    +
     4955.    211            loop: 0,
    +
     4956.    211            switch: 0,
    +
     4957.    211            thru: 0
    +
     4958.    211        };
    +
     4959.    211        blockage = global;
    +
     4960.    211        functionage = global;
    +
     4961.    211        json_mode = false;
    +
     4962.    211        mega_mode = false;
    +
     4963.    211        module_mode = false;
    +
     4964.    211        next_token = global;
    +
     4965.    211        property = empty();
    +
     4966.    211        shebang = false;
    +
     4967.    211        stack = [];
    +
     4968.    211        tenure = undefined;
    +
     4969.    211        token = global;
    +
     4970.    211        token_nr = 0;
    +
     4971.    211        var_mode = undefined;
    +
     4972.    211        populate(standard, declared_globals, false);
    +
     4973.    211        populate(global_array, declared_globals, false);
    +
     4974.     60        Object.keys(option).forEach(function (name) {
    +
     4975.     60            if (option[name] === true) {
    +
     4976.     60                const allowed = allowed_option[name];
    +
     4977.     24                if (Array.isArray(allowed)) {
    +
     4978.     24                    populate(allowed, declared_globals, false);
    +
     4979.     24                }
    +
     4980.     60            }
    +
     4981.     60        });
    +
     4982.    211        tokenize(source);
    +
     4983.    211        advance();
    +
     4984.      9        if (json_mode) {
    +
     4985.      9            tree = json_value();
    +
     4986.      9            advance("(end)");
    +
     4987.    182        } else {
    +
     4988.    182
    +
     4989.    182// Because browsers encourage combining of script files, the first token might
    +
     4990.    182// be a semicolon to defend against a missing semicolon in the preceding file.
    +
     4991.    182
    +
     4992.    182            if (option.browser) {
    +
     4993.      0                if (next_token.id === ";") {
    +
     4994.      0                    advance(";");
    +
     4995.      0                }
    +
     4996.    182            } else {
    +
     4997.    182
    +
     4998.    182// If we are not in a browser, then the file form of strict pragma may be used.
    +
     4999.    182
    +
     5000.    182                if (
    +
     5001.    182                    next_token.value === "use strict"
    +
     5002.      0                ) {
    +
     5003.      0                    advance("(string)");
    +
     5004.      0                    advance(";");
    +
     5005.      0                }
    +
     5006.    182            }
    +
     5007.    182            tree = statements();
    +
     5008.    182            advance("(end)");
    +
     5009.    182            functionage = global;
    +
     5010.    182            walk_statement(tree);
    +
     5011.    182            if (warnings.length === 0) {
    +
     5012.    182                uninitialized_and_unused();
    +
     5013.    182                if (!option.white) {
    +
     5014.    182                    whitage();
    +
     5015.    182                }
    +
     5016.    182            }
    +
     5017.    182        }
    +
     5018.    160        if (!option.browser) {
    +
     5019.    148            directives.forEach(function (comment) {
    +
     5020.    148                if (comment.directive === "global") {
    +
     5021.    148                    warn("missing_browser", comment);
    +
     5022.    148                }
    +
     5023.    148            });
    +
     5024.    160        }
    +
     5025.    160        early_stop = false;
    +
     5026.    160    } catch (e) {
    +
     5027.     51        e.column = e.column || -1;
    +
     5028.     51        e.early_stop = true;
    +
     5029.     51        e.line = e.line || -1;
    +
     5030.     51        e.message = "[JSLint was unable to finish] - " + e.message;
    +
     5031.      0        if (e.name !== "JSLintError") {
    +
     5032.      0            warnings.push(e);
    +
     5033.      0        }
    +
     5034.     51    }
    +
     5035.    211
    +
     5036.    211// sort warnings by early_stop first, line, column respectively
    +
     5037.    211
    +
     5038.    457    warnings.sort(function (a, b) {
    +
     5039.    457        return (
    +
     5040.    457            Boolean(b.early_stop) - Boolean(a.early_stop)
    +
     5041.    422            || a.line - b.line || a.column - b.column
    +
     5042.    457        );
    +
     5043.    457
    +
     5044.    457// update each warning with a formatted_message ready for use by cli
    +
     5045.    457
    +
     5046.    490    }).map(function ({
    +
     5047.    490        column = 0,
    +
     5048.    490        line = 0,
    +
     5049.    490        message = "",
    +
     5050.    490        stack_trace = ""
    +
     5051.    490    }, ii, list) {
    +
     5052.    490        column += 1;
    +
     5053.    490        line += 1;
    +
     5054.    490        list[ii].formatted_message = String(
    +
     5055.    490            String(ii + 1).padStart(3, " ") +
    +
     5056.    490            " \u001b[31m" + message + "\u001b[39m" +
    +
     5057.    490            " \u001b[90m\/\/ line " + line + ", column " + column +
    +
     5058.    490            "\u001b[39m\n" +
    +
     5059.    490            ("    " + String(lines && lines[line - 1]).trim()).slice(0, 72) +
    +
     5060.    490            "\n" + stack_trace
    +
     5061.    490        ).trim();
    +
     5062.    490    });
    +
     5063.    211    return {
    +
     5064.    211        directives,
    +
     5065.    211        edition: "v2021.5.23",
    +
     5066.    211        exports,
    +
     5067.    211        froms,
    +
     5068.    211        functions,
    +
     5069.    211        global,
    +
     5070.    211        id: "(JSLint)",
    +
     5071.    211        json: json_mode,
    +
     5072.    211        lines,
    +
     5073.    211        module: module_mode === true,
    +
     5074.     12        ok: warnings.length === 0 && !early_stop,
    +
     5075.    211        option,
    +
     5076.    211        property,
    +
     5077.    211        shebang: (
    +
     5078.    211            shebang
    +
     5079.      1            ? lines[0]
    +
     5080.    210            : undefined
    +
     5081.    211        ),
    +
     5082.    211        stop: early_stop,
    +
     5083.    211        tokens,
    +
     5084.    211        tree,
    +
     5085.    211        warnings
    +
     5086.    211    };
    +
     5087.    211}
    +
     5088.      1
    +
     5089.      1async function cli({
    +
     5090.      1    file
    +
     5091.      1}) {
    +
     5092.      1/*
    +
     5093.      1 * this function will run jslint from nodejs-cli
    +
     5094.      1 */
    +
     5095.      1    const {
    +
     5096.      1        readFile,
    +
     5097.      1        readdir
    +
     5098.      1    } = await import("fs/promises");
    +
     5099.      1    let exitCode;
    +
     5100.      7    function string_line_count(code) {
    +
     5101.      7    /*
    +
     5102.      7     * this function will count number of newlines in <code>
    +
     5103.      7     */
    +
     5104.      7        let cnt;
    +
     5105.      7        let ii;
    +
     5106.      7        // https://jsperf.com/regexp-counting-2/8
    +
     5107.      7        cnt = 0;
    +
     5108.      7        ii = 0;
    +
     5109.   2161        while (true) {
    +
     5110.   2161            ii = code.indexOf("\n", ii) + 1;
    +
     5111.   2161            if (ii === 0) {
    +
     5112.   2161                break;
    +
     5113.   2161            }
    +
     5114.   2161            cnt += 1;
    +
     5115.   2161        }
    +
     5116.      7        return cnt;
    +
     5117.      7    }
    +
     5118.     19    function jslint_from_file({
    +
     5119.     19        code,
    +
     5120.     19        file,
    +
     5121.     19        line_offset = 0,
    +
     5122.     19        warnings = []
    +
     5123.     19    }) {
    +
     5124.     19        switch ((
    +
     5125.     19            /\.\w+?$|$/m
    +
     5126.     19        ).exec(file)[0]) {
    +
     5127.      4        case ".html":
    +
     5128.      4            // recurse
    +
     5129.      4            code.replace((
    +
     5130.      4                /^<script\b[^>]*?>\n([\S\s]*?\n)<\/script>$/gm
    +
     5131.      0            ), function (ignore, match1, ii) {
    +
     5132.      0                jslint_from_file({
    +
     5133.      0                    code: match1,
    +
     5134.      0                    file: file + ".<script>.js",
    +
     5135.      0                    line_offset: string_line_count(code.slice(0, ii)) + 1
    +
     5136.      0                });
    +
     5137.      0                return "";
    +
     5138.      0            });
    +
     5139.      4            return;
    +
     5140.      2        case ".md":
    +
     5141.      2            // recurse
    +
     5142.      2            code.replace((
    +
     5143.      2                /^```javascript\n([\S\s]*?\n)```$/gm
    +
     5144.      2            ), function (ignore, match1, ii) {
    +
     5145.      2                jslint_from_file({
    +
     5146.      2                    code: match1.replace((
    +
     5147.      2                        /\u0027"\u0027"\u0027/g
    +
     5148.      2                    ), "\u0027"),
    +
     5149.      2                    file: file + ".<```javascript>.js",
    +
     5150.      2                    line_offset: string_line_count(code.slice(0, ii)) + 1
    +
     5151.      2                });
    +
     5152.      2                return "";
    +
     5153.      2            });
    +
     5154.      2            return;
    +
     5155.      1        case ".sh":
    +
     5156.      1            // recurse
    +
     5157.      1            code.replace((
    +
     5158.      1                /\bnode\u0020-e\u0020\u0027\n([\S\s]*?\n)\u0027/gm
    +
     5159.      6            ), function (ignore, match1, ii) {
    +
     5160.      6                jslint_from_file({
    +
     5161.      6                    code: match1.replace((
    +
     5162.      6                        /\u0027"\u0027"\u0027/g
    +
     5163.      6                    ), "\u0027"),
    +
     5164.      6                    file: file + ".<node -e>.js",
    +
     5165.      6                    line_offset: string_line_count(code.slice(0, ii)) + 1
    +
     5166.      6                });
    +
     5167.      6                return "";
    +
     5168.      6            });
    +
     5169.      1            return;
    +
     5170.     12        default:
    +
     5171.     12            warnings = jslint("\n".repeat(line_offset) + code, {
    +
     5172.     12                bitwise: true,
    +
     5173.     12                browser: true,
    +
     5174.     12                fudge: true,
    +
     5175.     12                node: true,
    +
     5176.     12                this: true
    +
     5177.     12            }, [
    +
     5178.     12                "global", "globalThis"
    +
     5179.     12            ]).warnings;
    +
     5180.     12        }
    +
     5181.     12        // print only first 10 warnings
    +
     5182.      0        if (warnings.length > 0) {
    +
     5183.      0            exitCode = 1;
    +
     5184.      0            // print first 10 warnings to stderr
    +
     5185.      0            console.error(
    +
     5186.      0                "\u001b[1mjslint " + file + "\u001b[22m\n" +
    +
     5187.      0                warnings.slice(0, 10).map(function ({
    +
     5188.      0                    formatted_message
    +
     5189.      0                }) {
    +
     5190.      0                    return formatted_message;
    +
     5191.      0                }).join("\n")
    +
     5192.      0            );
    +
     5193.      0        }
    +
     5194.     19    }
    +
     5195.      1    if (file === ".") {
    +
     5196.      1        file = await readdir(".");
    +
     5197.     27        await Promise.all(file.map(async function (file) {
    +
     5198.     27            let code;
    +
     5199.     27            let timeStart = Date.now();
    +
     5200.     27            switch ((
    +
     5201.     27                /\.\w+?$|$/m
    +
     5202.     27            ).exec(file)[0]) {
    +
     5203.      4            case ".html":
    +
     5204.      8            case ".js":
    +
     5205.      9            case ".json":
    +
     5206.     11            case ".md":
    +
     5207.     11            case ".mjs":
    +
     5208.     12            case ".sh":
    +
     5209.     12                break;
    +
     5210.     15            default:
    +
     5211.     15                return;
    +
     5212.     12            }
    +
     5213.     12            try {
    +
     5214.     12                code = await readFile(file, "utf8");
    +
     5215.      0            } catch (ignore) {
    +
     5216.      0                return;
    +
     5217.      0            }
    +
     5218.     12            if (!(
    +
     5219.     12                !(
    +
     5220.     12                    /\b(?:assets\.app\.js|lock|min|raw|rollup)\b/
    +
     5221.     12                ).test(file) && code && code.length < 1048576
    +
     5222.      0            )) {
    +
     5223.      0                return;
    +
     5224.      0            }
    +
     5225.     12            jslint_from_file({
    +
     5226.     12                code,
    +
     5227.     12                file
    +
     5228.     12            });
    +
     5229.     12            console.error(
    +
     5230.     12                "jslint - " + (Date.now() - timeStart) + "ms - " + file
    +
     5231.     12            );
    +
     5232.     12        }));
    +
     5233.      0    } else {
    +
     5234.      0        jslint_from_file({
    +
     5235.      0            code: await readFile(file, "utf8"),
    +
     5236.      0            file
    +
     5237.      0        });
    +
     5238.      0    }
    +
     5239.      1    return exitCode;
    +
     5240.      1}
    +
     5241.    200export default Object.freeze(function (
    +
     5242.    200    source = "",
    +
     5243.    200    option_object = empty(),
    +
     5244.    200    global_array = []
    +
     5245.    200) {
    +
     5246.      1    if (option_object.cli_mode) {
    +
     5247.      1        return cli(option_object);
    +
     5248.    199    }
    +
     5249.    199    return jslint(source, option_object, global_array);
    +
     5250.    199});
    +
     5251.      1// feature-detect nodejs-cli
    +
     5252.      1if (
    +
     5253.      1    typeof process === "object"
    +
     5254.      1    && process
    +
     5255.      1    && process.versions
    +
     5256.      1    && typeof process.versions.node === "string"
    +
     5257.      1    && process.argv
    +
     5258.      1    && (/\bjslint.m?js$/m).test(process.argv[1])
    +
     5259.      0) {
    +
     5260.      0    // run cli
    +
     5261.      0    cli({
    +
     5262.      0        file: process.argv[2]
    +
     5263.      0    }).then(process.exit);
    +
     5264.      0}
    +
     5265.      1
    + +
    +
    +
    + + diff --git a/branch.master/.build/coverage/test.js.html b/branch.master/.build/coverage/test.js.html new file mode 100644 index 000000000..5fe2db6e9 --- /dev/null +++ b/branch.master/.build/coverage/test.js.html @@ -0,0 +1,524 @@ + + + +coverage-report + + + +
    +
    coverage-report
    + + + + + + + + + + + +
    files coveredlines
    + ./ test.js
    +
    +
    +
    +
    + 98.47 %
    + 387 / 393 +
    +
    +
    +
        1.      1import jslint from "./jslint.js";
    +
        2.      1
    +
        3.      1(async function testCaseCoverage() {
    +
        4.      1/*
    +
        5.      1 * this function will try to improve coverage with no validation
    +
        6.      1 */
    +
        7.      1    await jslint("", {
    +
        8.      1        cli_mode: true,
    +
        9.      1        file: "."
    +
       10.      1    });
    +
       11.      1}());
    +
       12.      1
    +
       13.      1(function testCaseJslintWarningsValidate() {
    +
       14.      1/*
    +
       15.      1 * this function will validate each jslint <warning> is raised with given
    +
       16.      1 * malformed <code>
    +
       17.      1 */
    +
       18.      1    Object.entries({
    +
       19.      1        "and": [
    +
       20.      1            "aa && aa || aa"
    +
       21.      1        ],
    +
       22.      1        "bad_assignment_a": [
    +
       23.      1            "/*jslint for*/\nfunction aa(){for (0 in aa){}}",
    +
       24.      1            "0=0",
    +
       25.      1            "const aa=0;for(aa in aa){}"
    +
       26.      1        ],
    +
       27.      1        "bad_directive_a": [
    +
       28.      1            "/*jslint !*/"
    +
       29.      1        ],
    +
       30.      1        "bad_get": [
    +
       31.      1            "/*jslint getset*/\naa={get aa(aa){}}"
    +
       32.      1        ],
    +
       33.      1        "bad_module_name_a": [
    +
       34.      1            "import aa from \"!aa\""
    +
       35.      1        ],
    +
       36.      1        "bad_option_a": [
    +
       37.      1            "/*global aa:true*/",
    +
       38.      1            "/*jslint undefined*/"
    +
       39.      1        ],
    +
       40.      1        "bad_property_a": [
    +
       41.      1            "aa._"
    +
       42.      1        ],
    +
       43.      1        "bad_set": [
    +
       44.      1            "/*jslint getset*/\naa={set aa(){}}"
    +
       45.      1        ],
    +
       46.      1        "duplicate_a": [
    +
       47.      1            "aa={\"aa\":0,\"aa\":0}",
    +
       48.      1            "let aa;export {aa,aa}",
    +
       49.      1            "{\"aa\":0,\"aa\":0}"
    +
       50.      1        ],
    +
       51.      1        "empty_block": [
    +
       52.      1            "function aa(){}"
    +
       53.      1        ],
    +
       54.      1        "escape_mega": [],
    +
       55.      1        "expected_a": [],
    +
       56.      1        "expected_a_at_b_c": [
    +
       57.      1            "let aa={\n    aa:\n0\n};"
    +
       58.      1        ],
    +
       59.      1        "expected_a_b": [
    +
       60.      1            "!!aa",
    +
       61.      1            "([])=>0",
    +
       62.      1            "(aa)=>{}",
    +
       63.      1            "(aa?0:aa)",
    +
       64.      1            "(aa?aa:0)",
    +
       65.      1            "(aa?false:true)",
    +
       66.      1            "(aa?true:false)",
    +
       67.      1            "/=0",
    +
       68.      1            "0!=0",
    +
       69.      1            "0==0",
    +
       70.      1            ";{",
    +
       71.      1            "`${/ /}`",
    +
       72.      1            "`${`",
    +
       73.      1            "`${{`",
    +
       74.      1            "aa.aa=undefined",
    +
       75.      1            "aa=+aa",
    +
       76.      1            "aa=/[ ]/",
    +
       77.      1            "aa=/aa{/",
    +
       78.      1            "aa=0+\"\"",
    +
       79.      1            "aa=\"\"+\"\"",
    +
       80.      1            "async",
    +
       81.      1            "delete [0]",
    +
       82.      1            "for(;;){}",
    +
       83.      1            "isFinite(0)",
    +
       84.      1            "let aa;var aa;",
    +
       85.      1            "new Array(\"\")",
    +
       86.      1            "new Date().getTime()",
    +
       87.      1            "new Object()"
    +
       88.      1        ],
    +
       89.      1        "expected_a_b_from_c_d": [
    +
       90.      1            "{\"aa\":0"
    +
       91.      1        ],
    +
       92.      1        "expected_a_before_b": [
    +
       93.      1            ".0",
    +
       94.      1            "/*jslint eval*/\nFunction;eval",
    +
       95.      1            "=>0",
    +
       96.      1            "aa=/(:)/",
    +
       97.      1            "aa=/=/",
    +
       98.      1            "aa=/?/",
    +
       99.      1            "aa=/[/",
    +
      100.      1            "let Aa=Aa()",
    +
      101.      1            "let Aa=Aa.Aa()",
    +
      102.      1            "new Aa"
    +
      103.      1        ],
    +
      104.      1        "expected_a_next_at_b": [],
    +
      105.      1        "expected_digits_after_a": [
    +
      106.      1            "0x"
    +
      107.      1        ],
    +
      108.      1        "expected_four_digits": [
    +
      109.      1            "\"\\u0\""
    +
      110.      1        ],
    +
      111.      1        "expected_identifier_a": [
    +
      112.      1            "(0)=>0",
    +
      113.      1            "aa.0",
    +
      114.      1            "aa?.0",
    +
      115.      1            "function aa(0){}",
    +
      116.      1            "function aa([aa]){}\nfunction aa([aa],[aa,aa=aa],[0]){}",
    +
      117.      1            "function aa({aa}){}\nfunction aa({aa},{aa:aa,aa=aa},{aa:0}){}",
    +
      118.      1            "function(){}",
    +
      119.      1            "import {",
    +
      120.      1            "let [aa,0]=[]"
    +
      121.      1        ],
    +
      122.      1        "expected_line_break_a_b": [],
    +
      123.      1        "expected_regexp_factor_a": [
    +
      124.      1            "/ /"
    +
      125.      1        ],
    +
      126.      1        "expected_space_a_b": [
    +
      127.      1            "/**//**/",
    +
      128.      1            "let aa=0;"
    +
      129.      1        ],
    +
      130.      1        "expected_statements_a": [],
    +
      131.      1        "expected_string_a": [
    +
      132.      1            "import(aa).then(aa)",
    +
      133.      1            "typeof 0===0"
    +
      134.      1        ],
    +
      135.      1        "expected_type_string_a": [
    +
      136.      1            "typeof 0===\"aa\""
    +
      137.      1        ],
    +
      138.      1        "freeze_exports": [
    +
      139.      1            "export default Object.aa()",
    +
      140.      1            "export function aa(){}"
    +
      141.      1        ],
    +
      142.      1        "function_in_loop": [
    +
      143.      1            "function aa(){while(0){aa.map(()=>0);}}",
    +
      144.      1            "function aa(){while(0){aa.map(function(){});}}"
    +
      145.      1        ],
    +
      146.      1        "infix_in": [
    +
      147.      1            "aa in aa"
    +
      148.      1        ],
    +
      149.      1        "label_a": [
    +
      150.      1            "aa:while(0){aa;}"
    +
      151.      1        ],
    +
      152.      1        "misplaced_a": [
    +
      153.      1            "if(0){import aa from \"aa\";}"
    +
      154.      1        ],
    +
      155.      1        "misplaced_directive_a": [
    +
      156.      1            "let aa;\n/*global aa*/"
    +
      157.      1        ],
    +
      158.      1        "missing_await_statement": [
    +
      159.      1            "async function aa(){}"
    +
      160.      1        ],
    +
      161.      1        "missing_browser": [
    +
      162.      1            "/*global aa*/"
    +
      163.      1        ],
    +
      164.      1        "missing_m": [
    +
      165.      1            "aa=/$^/"
    +
      166.      1        ],
    +
      167.      1        "naked_block": [],
    +
      168.      1        "nested_comment": [
    +
      169.      1            "/*/*",
    +
      170.      1            "/*/**/"
    +
      171.      1        ],
    +
      172.      1        "not_label_a": [
    +
      173.      1            "aa:{break aa;}"
    +
      174.      1        ],
    +
      175.      1        "number_isNaN": [
    +
      176.      1            "NaN===NaN",
    +
      177.      1            "isNaN(0)"
    +
      178.      1        ],
    +
      179.      1        "out_of_scope_a": [
    +
      180.      1            "aa:{function aa(aa){break aa;}}",
    +
      181.      1            "function aa(){bb();}\nfunction bb(){}"
    +
      182.      1        ],
    +
      183.      1        "redefinition_a_b": [
    +
      184.      1            "let aa;let aa"
    +
      185.      1        ],
    +
      186.      1        "required_a_optional_b": [
    +
      187.      1            "function aa(aa=0,...){}",
    +
      188.      1            "function aa(aa=0,[]){}",
    +
      189.      1            "function aa(aa=0,{}){}"
    +
      190.      1        ],
    +
      191.      1        "reserved_a": [
    +
      192.      1            "let undefined"
    +
      193.      1        ],
    +
      194.      1        "subscript_a": [
    +
      195.      1            "aa[`aa`]"
    +
      196.      1        ],
    +
      197.      1        "todo_comment": [
    +
      198.      1            "// todo"
    +
      199.      1        ],
    +
      200.      1        "too_long": [
    +
      201.      1            "//".repeat(100)
    +
      202.      1        ],
    +
      203.      1        "too_many_digits": [
    +
      204.      1            "\"\\u{123456}\""
    +
      205.      1        ],
    +
      206.      1        "unclosed_comment": [
    +
      207.      1            "/*"
    +
      208.      1        ],
    +
      209.      1        "unclosed_mega": [
    +
      210.      1            "`aa"
    +
      211.      1        ],
    +
      212.      1        "unclosed_string": [
    +
      213.      1            "\"\\",
    +
      214.      1            "\"aa"
    +
      215.      1        ],
    +
      216.      1        "undeclared_a": [
    +
      217.      1            "aa"
    +
      218.      1        ],
    +
      219.      1        "unexpected_a": [
    +
      220.      1            "((0))",
    +
      221.      1            "(+0?+0:+0)()",
    +
      222.      1            "/*/",
    +
      223.      1            "/_/",
    +
      224.      1            "0 instanceof 0",
    +
      225.      1            "0===(0==0)",
    +
      226.      1            "0[0][0]",
    +
      227.      1            "0|0",
    +
      228.      1            ";",
    +
      229.      1            "Function",
    +
      230.      1            "[-0x0]",
    +
      231.      1            "[0x0]",
    +
      232.      1            "`${/[`]/}`",
    +
      233.      1            "`${/`/}`",
    +
      234.      1            "`${\"`\"}`",
    +
      235.      1            "aa((0))",
    +
      236.      1            "aa+=NaN",
    +
      237.      1            "aa/=0",
    +
      238.      1            "aa=/[0-]/",
    +
      239.      1            "aa=/_//",
    +
      240.      1            "aa=/_/z",
    +
      241.      1            "aa=aa++",
    +
      242.      1            "aa={aa:aa}",
    +
      243.      1            "aa={set aa(){}}",
    +
      244.      1            "arguments",
    +
      245.      1            "await",
    +
      246.      1            "debugger",
    +
      247.      1            "eval",
    +
      248.      1            "export aa",
    +
      249.      1            "export const aa=0",
    +
      250.      1            "for(aa in aa){}",
    +
      251.      1            "for(const ii=0;;){}",
    +
      252.      1            "for(ii=0;ii<0;ii++){}",
    +
      253.      1            "for(ii=0;ii<0;ii+=0){}",
    +
      254.      1            "function aa(){try{return;}catch(ignore){}finally{return;}}",
    +
      255.      1            "function aa(){try{}catch(ignore){}finally{switch(0){case 0:}}}",
    +
      256.      1            "function aa(){while(0){continue;}}",
    +
      257.      1            "function aa(){while(0){try{0;}catch(ignore){}finally{continue;}}}",
    +
      258.      1            "function aa(){}0",
    +
      259.      1            "function aa(){}\n[]",
    +
      260.      1            "function ignore(){let ignore;}",
    +
      261.      1            "ignore",
    +
      262.      1            "ignore:",
    +
      263.      1            "import ignore from \"aa\"",
    +
      264.      1            "import {ignore} from \"aa\"",
    +
      265.      1            "new Date.UTC()",
    +
      266.      1            "new Function()",
    +
      267.      1            "new Symbol()",
    +
      268.      1            "this",
    +
      269.      1            "try{}finally{break;}",
    +
      270.      1            "void 0",
    +
      271.      1            "while((0)){}",
    +
      272.      1            "while(0){}",
    +
      273.      1            "yield /_/",
    +
      274.      1            "{//\n}",
    +
      275.      1            "{0:0}",
    +
      276.      1            "{\"\\u{1234}\":0}",
    +
      277.      1            "{\"aa\":",
    +
      278.      1            "{\"aa\":'aa'}"
    +
      279.      1        ],
    +
      280.      1        "unexpected_a_after_b": [
    +
      281.      1            "0a"
    +
      282.      1        ],
    +
      283.      1        "unexpected_a_before_b": [
    +
      284.      1            "aa=\"\\a\""
    +
      285.      1        ],
    +
      286.      1        "unexpected_at_top_level_a": [],
    +
      287.      1        "unexpected_char_a": [
    +
      288.      1            "#"
    +
      289.      1        ],
    +
      290.      1        "unexpected_comment": [
    +
      291.      1            "`${//}`"
    +
      292.      1        ],
    +
      293.      1        "unexpected_directive_a": [
    +
      294.      1            "/*global aa*/\nimport aa from \"aa\""
    +
      295.      1        ],
    +
      296.      1        "unexpected_expression_a": [
    +
      297.      1            "aa++",
    +
      298.      1            "typeof 0===typeof 0"
    +
      299.      1        ],
    +
      300.      1        "unexpected_label_a": [
    +
      301.      1            "aa:aa"
    +
      302.      1        ],
    +
      303.      1        "unexpected_parens": [
    +
      304.      1            "aa=(function(){})"
    +
      305.      1        ],
    +
      306.      1        "unexpected_space_a_b": [
    +
      307.      1            "let aa=( 0 );"
    +
      308.      1        ],
    +
      309.      1        "unexpected_statement_a": [],
    +
      310.      1        "unexpected_trailing_space": [],
    +
      311.      1        "unexpected_typeof_a": [
    +
      312.      1            "typeof aa===\"undefined\""
    +
      313.      1        ],
    +
      314.      1        "uninitialized_a": [
    +
      315.      1            "/*jslint node*/\nlet aa;aa();"
    +
      316.      1        ],
    +
      317.      1        "unreachable_a": [
    +
      318.      1            "function aa(){while(0){break;0;}}"
    +
      319.      1        ],
    +
      320.      1        "unregistered_property_a": [
    +
      321.      1            "/*property aa*/\naa.bb"
    +
      322.      1        ],
    +
      323.      1        "unsafe": [],
    +
      324.      1        "unused_a": [
    +
      325.      1            "/*jslint node*/\nlet aa;"
    +
      326.      1        ],
    +
      327.      1        "use_double": [
    +
      328.      1            "''"
    +
      329.      1        ],
    +
      330.      1        "use_open": [
    +
      331.      1            "0?0:0",
    +
      332.      1            "aa=0?0:0"
    +
      333.      1        ],
    +
      334.      1        "use_spaces": [
    +
      335.      1            "\t"
    +
      336.      1        ],
    +
      337.      1        "var_loop": [
    +
      338.      1            "function aa(){while(0){var aa;}}"
    +
      339.      1        ],
    +
      340.      1        "var_switch": [
    +
      341.      1            "function aa(){switch(0){case 0:var aa;}}"
    +
      342.      1        ],
    +
      343.      1        "weird_condition_a": [
    +
      344.      1            "if(0&&0){0;}",
    +
      345.      1            "if(0||0){0;}"
    +
      346.      1        ],
    +
      347.      1        "weird_expression_a": [
    +
      348.      1            "aa=RegExp.aa",
    +
      349.      1            "aa=RegExp[0]",
    +
      350.      1            "aa[[0]]",
    +
      351.      1            "self=self[0]",
    +
      352.      1            "window=window[0]"
    +
      353.      1        ],
    +
      354.      1        "weird_loop": [
    +
      355.      1            "function aa(){do {break;}while(0);}",
    +
      356.      1            "function aa(){while(0){break;}}"
    +
      357.      1        ],
    +
      358.      1        "weird_relation_a": [
    +
      359.      1            "if(0===0){0;}"
    +
      360.      1        ],
    +
      361.      1        "wrap_condition": [
    +
      362.      1            "(aa&&!aa?0:1)"
    +
      363.      1        ],
    +
      364.      1        "wrap_immediate": [
    +
      365.      1            "aa=function(){}()"
    +
      366.      1        ],
    +
      367.      1        "wrap_parameter": [
    +
      368.      1            "aa=>0"
    +
      369.      1        ],
    +
      370.      1        "wrap_regexp": [
    +
      371.      1            "!/_/"
    +
      372.      1        ],
    +
      373.      1        "wrap_unary": [
    +
      374.      1            "0 - -0"
    +
      375.      1        ]
    +
      376.     84    }).forEach(function ([
    +
      377.     84        expectedWarning, malformedCodeList
    +
      378.     84    ]) {
    +
      379.    199        malformedCodeList.forEach(function (malformedCode) {
    +
      380.    323            if (!jslint(malformedCode).warnings.some(function ({
    +
      381.    323                code
    +
      382.    323            }) {
    +
      383.    323                return code === expectedWarning;
    +
      384.      0            })) {
    +
      385.      0                throw new Error(
    +
      386.      0                    `jslint failed to warn "${expectedWarning}" with ` +
    +
      387.      0                    `malfomed code "${malformedCode}"`
    +
      388.      0                );
    +
      389.      0            }
    +
      390.    199        });
    +
      391.     84    });
    +
      392.      1}());
    +
      393.      1
    + +
    +
    +
    + + diff --git a/branch.master/.build/screenshot.browser._2findex.html.html b/branch.master/.build/screenshot.browser._2findex.html.html new file mode 100644 index 000000000..1270d54c2 --- /dev/null +++ b/branch.master/.build/screenshot.browser._2findex.html.html @@ -0,0 +1,415 @@ + + + + + + + +JSLint: The JavaScript Code Quality Tool + + + +
    +
    Source
    +
    + + +
    +
    + Warnings +
    +
    +
    + Function Report +
    +
    +
    + Property Directive + +
    +
    + + + + +
    +
    Options +
    Assume... +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Global variables... + +
    +
    +
    + + + diff --git a/branch.master/.build/screenshot.browser._2findex.html.png b/branch.master/.build/screenshot.browser._2findex.html.png new file mode 100644 index 000000000..f3f18e917 Binary files /dev/null and b/branch.master/.build/screenshot.browser._2findex.html.png differ diff --git a/branch.master/.gitconfig b/branch.master/.gitconfig new file mode 100644 index 000000000..1cc9b799d --- /dev/null +++ b/branch.master/.gitconfig @@ -0,0 +1,23 @@ +[branch "alpha"] + merge = refs/heads/alpha + remote = origin +[branch "base"] + merge = refs/heads/base + remote = origin +[core] + # autocrlf = false + autocrlf = input + bare = false + # filemode = false + logallrefupdates = true + repositoryformatversion = 0 +[diff] + algorithm = histogram +[receive] + denyCurrentBranch = warn +[remote "origin"] + fetch = +refs/heads/*:refs/remotes/origin/* + url = https://github.com/user/jslint +[remote "upstream"] + fetch = +refs/heads/*:refs/remotes/upstream/* + url = https://github.com/jslint-org/jslint diff --git a/branch.master/.github/workflows/ci.yml b/branch.master/.github/workflows/ci.yml new file mode 100644 index 000000000..7466f8475 --- /dev/null +++ b/branch.master/.github/workflows/ci.yml @@ -0,0 +1,49 @@ +# this workflow will run nodejs coverages and tests and +# upload build-artifacts to branch-gh-pages +name: CI +on: + push: + branches: + - alpha + - beta + - master + - sandbox +# shCiBase - start +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + architecture: + # - arm64 + - x64 + # - x86 + node_version: + - 12 + - 14 + - 16 + os: + - macos-latest + - ubuntu-latest + - windows-latest + name: node . v${{ matrix.node_version }} . ${{ matrix.architecture }} . ${{ matrix.os }} + steps: + # https://github.com/actions/checkout + - uses: actions/checkout@v2 + # https://github.com/actions/setup-node + - uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node_version }} + architecture: ${{ matrix.architecture }} + # fetch ci.sh from trusted source + - run: git fetch origin alpha && git checkout origin/alpha ci.sh + # run nodejs coverages and tests + - run: sh ci.sh shCiBase +# shCiBase - end + # fetch ci.sh from trusted source + - run: git fetch origin alpha && git checkout origin/alpha ci.sh + # upload build-artifacts to branch-gh-pages + - run: sh ci.sh shCiArtifactUpload + env: + CI_NODE_VERSION_ARCH_PLATFORM: v14.x64.linux + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/branch.master/.github/workflows/on_pull_request.yml b/branch.master/.github/workflows/on_pull_request.yml new file mode 100644 index 000000000..9274873fc --- /dev/null +++ b/branch.master/.github/workflows/on_pull_request.yml @@ -0,0 +1,36 @@ +# this workflow will run nodejs coverages and tests +name: on_pull_request +on: + - pull_request +# shCiBase - start +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + architecture: + # - arm64 + - x64 + # - x86 + node_version: + - 12 + - 14 + - 16 + os: + - macos-latest + - ubuntu-latest + - windows-latest + name: node . v${{ matrix.node_version }} . ${{ matrix.architecture }} . ${{ matrix.os }} + steps: + # https://github.com/actions/checkout + - uses: actions/checkout@v2 + # https://github.com/actions/setup-node + - uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node_version }} + architecture: ${{ matrix.architecture }} + # fetch ci.sh from trusted source + - run: git fetch origin alpha && git checkout origin/alpha ci.sh + # run nodejs coverages and tests + - run: sh ci.sh shCiBase +# shCiBase - end diff --git a/branch.master/.gitignore b/branch.master/.gitignore new file mode 100644 index 000000000..da69da6f4 --- /dev/null +++ b/branch.master/.gitignore @@ -0,0 +1,18 @@ +"* +'* +*.[0123456789][0123456789] +*.lock +*.log +*.pyc +*~ +.* +_* +node_modules +package-lock.json +temp* +tmp +undefined + +!.gitconfig +!.github +!.gitignore diff --git a/branch.master/CHANGELOG.md b/branch.master/CHANGELOG.md new file mode 100644 index 000000000..5bd5f2fad --- /dev/null +++ b/branch.master/CHANGELOG.md @@ -0,0 +1,41 @@ +# Changelog + +## Todo +- app - deploy jslint as chrome-extension. +- doc - add svg package-listing. +- ci - continue addng regression tests and improve code-coverage. +- none + +## Branch beta +- none + +## v2021.5.23 +- doc - add section Changelog +- doc - update README.md with installtion instructions +- cli - merge shell-function shJslintCli into jslint.js. +- jslint - update default globals with support for "import" +- jslint - sort warnings with higher priority for early_stop +- jslint - add async/await support +- ci - make branch-beta the default branch. +- ci - validate non-http/file links in *.md files. +- ci - add shell-functions shCiBranchPromote. + +## v2021.5.21 +- this ci-release does not change any core-functionality of file jslint.js. +- doc - add file CHANGELOG.md. +- ci - begin addng regression tests and improve code-coverage. +- ci - allow pull-requests to run restricted-ci (cannot upload artifacts). +- gh-pages - fix missing assets and insecure http-links. +- gh-pages - merge file jslint.css into index.html. +- gh-pages - add files image-jslint-xxx.png. +- gh-pages - cleanup asset naming-convention. +- fix missing fonts in function.html and help.html. +- add files .gitconfig, Daley-Bold.woff2, Programma-Bold.woff2, icon-folder-open-solid.svg, icon-window-maximize-regular.svg. +- ci - fix http-links after moving to jslint-org. +- doc - migrate file README to README.md with embedded ci links and screenshots. +- ci - add macos and windows to ci-matrix. +- ci - ci now fails if jslint-check fails for any of the files in branches. +- ci - add github-workflows to generate code-coverage for jslint.js. + +## v2020.11.6 +- vestigial diff --git a/branch.master/README.md b/branch.master/README.md new file mode 100644 index 000000000..c3762aefe --- /dev/null +++ b/branch.master/README.md @@ -0,0 +1,87 @@ +# JSLint, The JavaScript Code Quality Tool + +Douglas Crockford +douglas@crockford.com + +## v2021.5.23 + +## Status +| Branch | [master
    (release)](https://github.com/kaizhu256/jslint/tree/master) | [beta
    (production)](https://github.com/kaizhu256/jslint/tree/beta) | [alpha
    (development)](https://github.com/kaizhu256/jslint/tree/alpha) | +|--:|:--:|:--:|:--:| +| CI | [![ci](https://github.com/kaizhu256/jslint/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/kaizhu256/jslint/actions?query=branch%3Amaster) | [![ci](https://github.com/kaizhu256/jslint/actions/workflows/ci.yml/badge.svg?branch=beta)](https://github.com/kaizhu256/jslint/actions?query=branch%3Abeta) | [![ci](https://github.com/kaizhu256/jslint/actions/workflows/ci.yml/badge.svg?branch=alpha)](https://github.com/kaizhu256/jslint/actions?query=branch%3Aalpha) | +| Coverage | [![coverage](https://kaizhu256.github.io/jslint/branch.master/.build/coverage/coverage-badge.svg)](https://kaizhu256.github.io/jslint/branch.master/.build/coverage/index.html) | [![coverage](https://kaizhu256.github.io/jslint/branch.beta/.build/coverage/coverage-badge.svg)](https://kaizhu256.github.io/jslint/branch.beta/.build/coverage/index.html) | [![coverage](https://kaizhu256.github.io/jslint/branch.alpha/.build/coverage/coverage-badge.svg)](https://kaizhu256.github.io/jslint/branch.alpha/.build/coverage/index.html) | +| Demo | [](https://kaizhu256.github.io/jslint/branch.master/index.html) | [](https://kaizhu256.github.io/jslint/branch.beta/index.html) | [](https://kaizhu256.github.io/jslint/branch.alpha/index.html) | +| Artifacts | [](https://github.com/kaizhu256/jslint/tree/gh-pages/branch.master/.build) | [](https://github.com/kaizhu256/jslint/tree/gh-pages/branch.beta/.build) | [](https://github.com/kaizhu256/jslint/tree/gh-pages/branch.alpha/.build) | + +## Live Web Demo +- [https://kaizhu256.github.io/jslint/index.html](https://kaizhu256.github.io/jslint/index.html) + +[![screenshot](https://kaizhu256.github.io/jslint/branch.master/.build/screenshot.browser._2findex.html.png)](https://kaizhu256.github.io/jslint/index.html) + +## Installation +1. Download [https://www.jslint.com/jslint.js](https://www.jslint.com/jslint.js) and rename to `jslint.mjs` +```shell +#!/bin/sh +curl -L https://www.jslint.com/jslint.js > jslint.mjs +``` + +2. To run `jslint.mjs` from command line: +```shell +#!/bin/sh +node jslint.mjs hello.js + +# stderr: +# jslint hello.js +# 1 Use double quotes, not single quotes. // line 1, column 14 +# console.log('hello world'); +``` + +3. To load `jslint.mjs` as es-module: +```javascript +import jslint from "./jslint.mjs"; +let code = "console.log('hello world');\n"; +let result = jslint(code); +result.warnings.forEach(function ({ + formatted_message +}) { + console.error(formatted_message); +}); + +// stderr: +// 1 Undeclared 'console'. // line 1, column 1 +// console.log('hello world'); +// 2 Use double quotes, not single quotes. // line 1, column 14 +// console.log('hello world'); +``` + +## Description +- [jslint.js](jslint.js) contains the jslint function. It parses and analyzes a source file, returning an object with information about the file. It can also take an object that sets options. + +- [index.html](index.html) runs the jslint.js function in a web page. The page also depends on `browser.js` and `report.js`. + +- [browser.js](browser.js) runs the web user interface. + +- [report.js](report.js) generates the results reports in HTML. + +- [help.html](help.html) describes JSLint's usage. Please [read it](https://kaizhu256.github.io/jslint/help.html). + +- [function.html](function.html) describes the jslint function and the results it produces. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[https://www.crockford.com/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. + +## Changelog +- [CHANGELOG.md](CHANGELOG.md) diff --git a/branch.master/browser.js b/branch.master/browser.js new file mode 100644 index 000000000..5086fed85 --- /dev/null +++ b/branch.master/browser.js @@ -0,0 +1,158 @@ +// browser.js +// 2018-06-16 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +/*jslint + browser +*/ + +/*property + checked, create, disable, display, error, focus, forEach, function, + getElementById, innerHTML, join, length, map, onchange, onclick, onscroll, + property, querySelectorAll, scrollTop, select, split, style, title, value +*/ + +import jslint from "./jslint.js"; +import report from "./report.js"; + +// This is the web script companion file for JSLint. It includes code for +// interacting with the browser and displaying the reports. + +let rx_crlf = /\n|\r\n?/; +let rx_separator = /[\s,;'"]+/; + +let fudge_unit = 0; + +const aux = document.getElementById("JSLINT_AUX"); +const boxes = document.querySelectorAll("[type=checkbox]"); +const fudge = document.getElementById("JSLINT_FUDGE"); +const global = document.getElementById("JSLINT_GLOBAL"); +const number = document.getElementById("JSLINT_NUMBER"); +const property = document.getElementById("JSLINT_PROPERTY"); +const property_fieldset = document.getElementById("JSLINT_PROPERTYFIELDSET"); +const report_field = document.getElementById("JSLINT_REPORT"); +const report_list = document.getElementById("JSLINT_REPORT_LIST"); +const source = document.getElementById("JSLINT_SOURCE"); +const select = document.getElementById("JSLINT_SELECT"); +const warnings = document.getElementById("JSLINT_WARNINGS"); +const warnings_list = document.getElementById("JSLINT_WARNINGS_LIST"); + +function show_numbers() { + number.value = source.value.split(rx_crlf).map(function (ignore, index) { + return index + fudge_unit; + }).join("\n"); +} + +function clear() { + aux.style.display = "none"; + number.value = ""; + property.value = ""; + property_fieldset.style.display = "none"; + report_field.style.display = "none"; + report_list.innerHTML = ""; + source.focus(); + source.value = ""; + warnings.style.display = "none"; + warnings_list.innerHTML = ""; +} + +function fudge_change() { + fudge_unit = Number(fudge.checked); + show_numbers(); +} + +function clear_options() { + boxes.forEach(function (node) { + node.checked = false; + }); + fudge_change(); + global.innerHTML = ""; +} + +function call_jslint() { + +// First build the option object. + + let option = Object.create(null); + boxes.forEach(function (node) { + if (node.checked) { + option[node.title] = true; + } + }); + +// Call JSLint with the source text, the options, and the predefined globals. + + let global_string = global.value; + let result = jslint( + source.value, + option, + ( + global_string === "" + ? undefined + : global_string.split(rx_separator) + ) + ); + +// Generate the reports. + + let error_html = report.error(result); + let function_html = report.function(result); + let property_text = report.property(result); + +// Display the reports. + + warnings_list.innerHTML = error_html; + warnings.style.display = ( + error_html.length === 0 + ? "none" + : "block" + ); + + report_list.innerHTML = function_html; + report_field.style.display = "block"; + if (property_text) { + property.value = property_text; + property_fieldset.style.display = "block"; + property.scrollTop = 0; + select.disable = false; + } else { + property_fieldset.style.display = "none"; + select.disable = true; + } + aux.style.display = "block"; + source.select(); +} + +fudge.onchange = fudge_change; + +source.onchange = function (ignore) { + show_numbers(); +}; + +source.onscroll = function () { + let ss = source.scrollTop; + number.scrollTop = ss; + let sn = number.scrollTop; + if (ss > sn) { + show_numbers(); + number.scrollTop = ss; + } +}; + +document.querySelectorAll("[name='JSLint']").forEach(function (node) { + node.onclick = call_jslint; +}); + +document.querySelectorAll("[name='clear']").forEach(function (node) { + node.onclick = clear; +}); + +document.getElementById("JSLINT_SELECT").onclick = function () { + property.focus(); + property.select(); +}; +document.getElementById("JSLINT_CLEAR_OPTIONS").onclick = clear_options; + +fudge_change(); +source.select(); +source.focus(); diff --git a/branch.master/ci.sh b/branch.master/ci.sh new file mode 100755 index 000000000..03b85dfbd --- /dev/null +++ b/branch.master/ci.sh @@ -0,0 +1,1057 @@ +#!/bin/sh +: ' +/* jslint utility2:true */ +' + +# sh one-liner +# head CHANGELOG.md -n20 +# git fetch origin alpha beta master && git fetch upstream alpha beta master +# sh ci.sh shCiBranchPromote origin alpha beta + +shBrowserScreenshot() {(set -e +# this function will run headless-chrome to screenshot url $1 with +# window-size $2 + node -e ' +// init debugInline +if (!globalThis.debugInline) { + let consoleError; + consoleError = console.error; + globalThis.debugInline = function (...argList) { + /* + * this function will both print to stderr and + * return [0] + */ + consoleError("\n\ndebugInline"); + consoleError(...argList); + consoleError("\n"); + return argList[0]; + }; +} +(function () { + "use strict"; + let file; + let timeStart; + let url; + if (process.platform !== "linux") { + return; + } + timeStart = Date.now(); + url = process.argv[1]; + if (!( + /^\w+?:/ + ).test(url)) { + url = require("path").resolve(url); + } + file = require("url").parse(url).pathname; + // remove prefix $PWD from file + if (String(file + "/").indexOf(process.cwd() + "/") === 0) { + file = file.replace(process.cwd(), ""); + } + file = ".build/screenshot.browser." + encodeURIComponent(file).replace(( + /%/g + ), "_").toLowerCase(); + process.on("exit", function (exitCode) { + if (typeof exitCode === "object" && exitCode) { + console.error(exitCode); + exitCode = 1; + } + console.error( + "shBrowserScreenshot" + + "\n - url - " + url + + "\n - wrote - " + file + ".html" + + "\n - wrote - " + file + ".png" + + "\n - timeElapsed - " + (Date.now() - timeStart) + " ms" + + "\n - EXIT_CODE=" + exitCode + ); + }); + [ + ".html", ".png" + ].forEach(function (extname) { + let argList; + let child; + argList = Array.from([ + "--headless", + "--ignore-certificate-errors", + "--incognito", + "--timeout=30000", + "--user-data-dir=/dev/null", + "--window-size=" + (process.argv[2] || "800x600"), + ( + extname === ".html" + ? "--dump-dom" + : "" + ), + ( + extname === ".png" + ? "--screenshot" + : "" + ), + ( + extname === ".png" + ? "-screenshot=" + file + ".png" + : "" + ), + ( + (process.getuid && process.getuid() === 0) + ? "--no-sandbox" + : "" + ), + url + ]).filter(function (elem) { + return elem; + }); + // debug argList + // console.error(argList); + child = require("child_process").spawn(( + process.platform === "darwin" + ? "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" + : process.platform === "win32" + ? "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe" + : "/usr/bin/google-chrome-stable" + ), argList, { + stdio: [ + "ignore", "pipe", 2 + ] + }); + child.stdout.pipe( + extname === ".html" + ? require("fs").createWriteStream(file + ".html") + : process.stdout + ); + }); +}()); +' "$@" # "' +)} + +shCiArtifactUpload() {(set -e +# this function will upload build-artifacts to branch-gh-pages + node -e ' +process.exit( + `${process.version.split(".")[0]}.${process.arch}.${process.platform}` !== + process.env.CI_NODE_VERSION_ARCH_PLATFORM +); +' || return 0 + local BRANCH + # init $BRANCH + BRANCH="$(git rev-parse --abbrev-ref HEAD)" + # init .git/config + git config --local user.email "github-actions@users.noreply.github.com" + git config --local user.name "github-actions" + # update README.md with $GITHUB_REPOSITORY + sed -i \ + -e "s|\bjslint-org/jslint\b|$GITHUB_REPOSITORY|g" \ + -e "s|\bjslint-org\.github\.io/jslint\b|$( + printf "$GITHUB_REPOSITORY" | sed -e "s|/|.github.io/|" + )|g" \ + README.md + # add dir .build + git add -f .build + git commit -am "add dir .build" + # checkout branch-gh-pages + git checkout -b gh-pages + git fetch origin gh-pages + git reset --hard origin/gh-pages + # update dir branch.$BRANCH + rm -rf "branch.$BRANCH" + mkdir "branch.$BRANCH" + (set -e + cd "branch.$BRANCH" + git init -b branch1 + git pull --depth=1 .. "$BRANCH" + rm -rf .git + git add -f . + ) + # update root-dir with branch-beta + if [ "$BRANCH" = beta ] + then + git rm -rf .build + git checkout beta . + fi + git status + git commit -am "update dir branch.$BRANCH" || true + # if branch-gh-pages has more than 100 commits, + # then backup and squash commits + if [ "$(git rev-list --count gh-pages)" -gt 100 ] + then + # backup + shGitCmdWithGithubToken push origin -f gh-pages:gh-pages.backup + # squash commits + git checkout --orphan squash1 + git commit --quiet -am squash || true + # reset branch-gh-pages to squashed-commit + git push . -f squash1:gh-pages + git checkout gh-pages + # force-push squashed-commit + shGitCmdWithGithubToken push origin -f gh-pages + fi + # list files + shGitLsTree + # push branch-gh-pages + shGitCmdWithGithubToken push origin gh-pages + # validate http-links + (set -e + cd "branch.$BRANCH" + sleep 15 + shDirHttplinkValidate + ) +)} + +shCiBase() {(set -e +# this function will run github-ci + # jslint all files + node jslint.js . + # run test with coverage-report + shRunWithCoverage node test.js + # screenshot live-web-demo + shBrowserScreenshot index.html +)} + +shCiBranchPromote() {(set -e +# this function will promote branch $REMOTE/$BRANCH1 to branch $REMOTE/$BRANCH2 + local BRANCH1 + local BRANCH2 + local REMOTE + REMOTE="$1" + shift + BRANCH1="$1" + shift + BRANCH2="$1" + shift + git fetch "$REMOTE" "$BRANCH1" + git push "$REMOTE" "$REMOTE/$BRANCH1:$BRANCH2" "$@" +)} + +shDirHttplinkValidate() {(set -e +# this function will validate http-links embedded in .html and .md files + node -e ' +(async function () { + "use strict"; + let dict = {}; + Array.from(await require("fs").readdir(".")).forEach(async function (file) { + if (!( + /.\.html$|.\.md$/m + ).test(file)) { + return; + } + let data = await require("fs").promises.readFile(file, "utf8"); + data.replace(( + /\bhttps?:\/\/.*?(?:[")\]]|$)/gm + ), function (url) { + url = url.slice(0, -1).replace(( + /[\u0022\u0027]/g + ), "").replace(( + /\/branch\.\w+?\//g + ), "/branch.alpha/").replace(( + /\bjslint-org\/jslint\b/g + ), process.env.GITHUB_REPOSITORY || "jslint-org/jslint").replace(( + /\bjslint-org\.github\.io\/jslint\b/g + ), String( + process.env.GITHUB_REPOSITORY || "jslint-org/jslint" + ).replace("/", ".github.io/")); + if (url.indexOf("http://") === 0) { + throw new Error( + "shDirHttplinkValidate - insecure link " + url + ); + } + // ignore duplicate-link + if (dict.hasOwnProperty(url)) { + return ""; + } + dict[url] = true; + let req = require("https").request(require("url").parse( + url + ), function (res) { + console.error( + "shDirHttplinkValidate " + res.statusCode + " " + url + ); + if (!(res.statusCode < 400)) { + throw new Error( + "shDirHttplinkValidate - " + file + + " - unreachable link " + url + ); + } + req.abort(); + res.destroy(); + }); + req.setTimeout(30000); + req.end(); + return ""; + }); + data.replace(( + /(\bhref=|\bsrc=|\burl\(|\[[^]*?\]\()("?.*?)(?:[")\]]|$)/gm + ), function (ignore, linkType, url) { + if (linkType[0] !== "[") { + url = url.slice(1); + } + // ignore duplicate-link + if (dict.hasOwnProperty(url)) { + return ""; + } + dict[url] = true; + if (!( + /^https?|^mailto:|^[#\/]/m + ).test(url)) { + require("fs").stat(url, function (ignore, exists) { + console.error( + "shDirHttplinkValidate " + Boolean(exists) + " " + url + ); + if (!exists) { + throw new Error( + "shDirHttplinkValidate - " + file + + " - unreachable link " + url + ); + } + }); + } + return ""; + }); + }); +}()); +' # "' +)} + +shGitCmdWithGithubToken() {(set -e +# this function will run git $CMD with $GITHUB_TOKEN + local CMD + local EXIT_CODE + local REMOTE + local URL + printf "shGitCmdWithGithubToken $*\n" + CMD="$1" + shift + REMOTE="$1" + shift + URL="$( + git config "remote.$REMOTE.url" | + sed -e "s|https://|https://x-access-token:$GITHUB_TOKEN@|" + )" + EXIT_CODE=0 + # hide $GITHUB_TOKEN in case of err + git "$CMD" "$URL" "$@" 2>/dev/null || EXIT_CODE="$?" + printf "EXIT_CODE=$EXIT_CODE\n" + return "$EXIT_CODE" +)} + +shGitLsTree() {(set -e +# this function will "git ls-tree" all files committed in HEAD +# example use: +# shGitLsTree | sort -rk3 # sort by date +# shGitLsTree | sort -rk4 # sort by size + node -e ' +(async function () { + "use strict"; + let result; + // get file, mode, size + result = await new Promise(function (resolve) { + let child; + child = require("child_process").spawn("git", [ + "ls-tree", "-lr", "HEAD" + ], { + encoding: "utf8", + stdio: [ + "ignore", "pipe", 2 + ] + }); + child.on("exit", function () { + resolve(child.stdout); + }); + }); + result = Array.from(result.matchAll( + /^(\S+?)\u0020+?\S+?\u0020+?\S+?\u0020+?(\S+?)\t(\S+?)$/gm + )).map(function ([ + ignore, mode, size, file + ]) { + return { + file, + mode: mode.slice(-3), + size: Number(size) + }; + }); + result = result.sort(function (aa, bb) { + return aa.file > bb.file || -1; + }); + result = result.slice(0, 1000); + result.unshift({ + file: ".", + mode: "755", + size: 0 + }); + // get date + result.forEach(function (elem) { + result[0].size += elem.size; + require("child_process").spawn("git", [ + "log", "--max-count=1", "--format=%at", elem.file + ], { + stdio: [ + "ignore", "pipe", 2 + ] + }).stdout.on("data", function (chunk) { + elem.date = new Date( + Number(chunk) * 1000 + ).toISOString().slice(0, 19) + "Z"; + }); + }); + process.on("exit", function () { + let iiPad; + let sizePad; + iiPad = String(result.length).length + 1; + sizePad = String(Math.ceil(result[0].size / 1024)).length; + process.stdout.write(result.map(function (elem, ii) { + return ( + String(ii + ".").padStart(iiPad, " ") + + " " + elem.mode + + " " + elem.date + + " " + String( + Math.ceil(elem.size / 1024) + ).padStart(sizePad, " ") + " KB" + + " " + elem.file + + "\n" + ); + }).join("")); + }); +}()); +' # "' +)} + +shRunWithCoverage() {(set -e +# this function will run nodejs command $@ with v8-coverage and +# create coverage-report .build/coverage/index.html + export DIR_COVERAGE=.build/coverage/ + rm -rf "$DIR_COVERAGE" + (export NODE_V8_COVERAGE="$DIR_COVERAGE" && "$@" || true) + node -e ' +// init debugInline +if (!globalThis.debugInline) { + let consoleError; + consoleError = console.error; + globalThis.debugInline = function (...argList) { + /* + * this function will both print to stderr and + * return [0] + */ + consoleError("\n\ndebugInline"); + consoleError(...argList); + consoleError("\n"); + return argList[0]; + }; +} +(async function () { + "use strict"; + let DIR_COVERAGE = process.env.DIR_COVERAGE; + let cwd; + let data; + let fileDict; + async function htmlRender({ + fileList, + lineList, + pathname + }) { + let html; + let padLines; + let padPathname; + let txt; + let txtBorder; + function stringHtmlSafe(str) { + /* + * this function will make html-safe + * https://stackoverflow.com/questions/7381974/ + * which-characters-need-to-be-escaped-on-html + */ + return str.replace(( + /&/gu + ), "&").replace(( + /"/gu + ), """).replace(( + /\u0027/gu + ), "'").replace(( + //gu + ), ">").replace(( + /&(amp;|apos;|gt;|lt;|quot;)/igu + ), "&$1"); + } + html = ""; + html += ` + + +coverage-report + + + +
    +
    coverage-report
    + + + + + + + +`; + if (!lineList) { + padLines = String("100.00 %").length; + padPathname = 32; + fileList.unshift({ + linesCovered: 0, + linesTotal: 0, + pathname: "./" + }); + fileList.slice(1).forEach(function ({ + linesCovered, + linesTotal, + pathname + }) { + fileList[0].linesCovered += linesCovered; + fileList[0].linesTotal += linesTotal; + padPathname = Math.max(padPathname, pathname.length + 2); + padLines = Math.max( + padLines, + String(linesCovered + " / " + linesTotal).length + ); + }); + } + txtBorder = ( + "+" + "-".repeat(padPathname + 2) + "+" + + "-".repeat(padLines + 2) + "+\n" + ); + txt = ""; + txt += "coverage-report\n"; + txt += txtBorder; + txt += ( + "| " + String("files covered").padEnd(padPathname, " ") + " | " + + String("lines").padStart(padLines, " ") + " |\n" + ); + txt += txtBorder; + fileList.forEach(function ({ + linesCovered, + linesTotal, + pathname + }, ii) { + let coverageLevel; + let coveragePct; + coveragePct = Math.floor(10000 * linesCovered / linesTotal | 0); + coverageLevel = ( + coveragePct >= 8000 + ? "coverageHigh" + : coveragePct >= 5000 + ? "coverageMedium" + : "coverageLow" + ); + coveragePct = String(coveragePct).replace(( + /..$/m + ), ".$&"); + if (!lineList && ii === 0) { + let fill = ( + // red + "#" + Math.round( + (100 - Number(coveragePct)) * 2.21 + ).toString(16).padStart(2, "0") + // green + + Math.round( + Number(coveragePct) * 2.21 + ).toString(16).padStart(2, "0") + + // blue + "00" + ); + let str1 = "coverage"; + let str2 = coveragePct + " %"; + let xx1 = 6 * str1.length + 20; + let xx2 = 6 * str2.length + 20; + // fs - write coverage-badge.svg + require("fs").promises.writeFile(( + DIR_COVERAGE + "/coverage-badge.svg" + ), String(` + + + + +${str1} +${str2} + + + `).trim() + "\n"); + pathname = ""; + } + txt += ( + "| " + + String("./" + pathname).padEnd(padPathname, " ") + " | " + + String(coveragePct + " %").padStart(padLines, " ") + " |\n" + ); + txt += ( + "| " + "*".repeat( + Math.round(0.01 * coveragePct * padPathname) + ).padEnd(padPathname, "_") + " | " + + String( + linesCovered + " / " + linesTotal + ).padStart(padLines, " ") + " |\n" + ); + txt += txtBorder; + pathname = stringHtmlSafe(pathname); + html += ` + + +`; + }); + if (lineList) { + html += ` +
    files coveredlines
    + ${( + lineList + ? ( + "./ " + + pathname + "
    " + ) + : ( + "./ " + + pathname + "
    " + ) + )} +
    +
    +
    +
    + ${coveragePct} %
    + ${linesCovered} / ${linesTotal} +
    +
    +
    +`; + lineList.forEach(function ({ + count, + holeList, + line, + startOffset + }, ii) { + let chunk; + let inHole; + let lineId; + let lineHtml; + lineHtml = ""; + lineId = "line_" + (ii + 1); + switch (count) { + case -1: + case 0: + if (holeList.length === 0) { + lineHtml += ""; + lineHtml += ""; + lineHtml += stringHtmlSafe(line); + break; + } + line = line.split("").map(function (chr) { + return { + chr, + isHole: undefined + }; + }); + holeList.forEach(function ([ + aa, bb + ]) { + aa = Math.max(aa - startOffset, 0); + bb = Math.min(bb - startOffset, line.length); + while (aa < bb) { + line[aa].isHole = true; + aa += 1; + } + }); + chunk = ""; + line.forEach(function ({ + chr, + isHole + }) { + if (inHole !== isHole) { + lineHtml += stringHtmlSafe(chunk); + lineHtml += ( + isHole + ? "" + : "" + ); + chunk = ""; + inHole = isHole; + } + chunk += chr; + }); + lineHtml += stringHtmlSafe(chunk); + break; + default: + lineHtml += stringHtmlSafe(line); + } + html += String(` +
    +
    +${String(ii + 1).padStart(5, " ")}.
    +
    +
    +${String(count).padStart(7, " ")}
    +
    +${lineHtml}
    +
    + `).replace(( + /\n/g + ), "").trim() + "\n"; + }); + } + html += ` +
    +
    +
    + +`; + html += "\n"; + await require("fs").promises.mkdir(require("path").dirname(pathname), { + recursive: true + }); + // fs - write *.html + require("fs").promises.writeFile(pathname + ".html", html); + if (lineList) { + return; + } + // fs - write coverage.txt + console.error("\n" + txt); + require("fs").promises.writeFile(( + DIR_COVERAGE + "/coverage-report.txt" + ), txt); + } + data = await require("fs").promises.readdir(DIR_COVERAGE); + await Promise.all(data.map(async function (file) { + if (( + /^coverage-.*?\.json$/ + ).test(file)) { + data = await require("fs").promises.readFile(( + DIR_COVERAGE + file + ), "utf8"); + // fs - rename to coverage-v8.json + require("fs").promises.rename( + DIR_COVERAGE + file, + DIR_COVERAGE + "coverage-v8.json" + ); + } + })); + fileDict = {}; + cwd = process.cwd().replace(( + /\\/g + ), "/") + "/"; + await Promise.all(JSON.parse(data).result.map(async function ({ + functions, + url + }) { + let lineList; + let linesCovered; + let linesTotal; + let pathname; + let src; + if (url.indexOf("file:///") !== 0) { + return; + } + pathname = url.replace(( + process.platform === "win32" + ? "file:///" + : "file://" + ), "").replace(( + /\\\\/g + ), "/"); + if ( + pathname.indexOf(cwd) !== 0 || + pathname.indexOf(cwd + "[") === 0 || + ( + process.env.npm_config_mode_coverage !== "all" && + pathname.indexOf("/node_modules/") >= 0 + ) + ) { + return; + } + pathname = pathname.replace(cwd, ""); + src = await require("fs").promises.readFile(pathname, "utf8"); + lineList = [{}]; + src.replace(( + /^.*$/gm + ), function (line, startOffset) { + lineList[lineList.length - 1].endOffset = startOffset - 1; + lineList.push({ + count: -1, + endOffset: 0, + holeList: [], + line, + startOffset + }); + return ""; + }); + lineList.shift(); + lineList[lineList.length - 1].endOffset = src.length; + functions.reverse().forEach(function ({ + ranges + }) { + ranges.reverse().forEach(function ({ + count, + endOffset, + startOffset + }, ii, list) { + lineList.forEach(function (elem) { + if (!( + ( + elem.startOffset <= startOffset && + startOffset <= elem.endOffset + ) || ( + elem.startOffset <= endOffset && + endOffset <= elem.endOffset + ) || ( + startOffset <= elem.startOffset && + elem.endOffset <= endOffset + ) + )) { + return; + } + // handle root-range + if (ii + 1 === list.length) { + if (elem.count === -1) { + elem.count = count; + } + return; + } + // handle non-root-range + if (elem.count !== 0) { + elem.count = Math.max(count, elem.count); + } + if (count === 0) { + elem.count = 0; + elem.holeList.push([ + startOffset, endOffset + ]); + } + }); + }); + }); + linesTotal = lineList.length; + linesCovered = lineList.filter(function ({ + count + }) { + return count > 0; + }).length; + await require("fs").promises.mkdir(( + require("path").dirname(DIR_COVERAGE + pathname) + ), { + recursive: true + }); + await htmlRender({ + fileList: [ + { + linesCovered, + linesTotal, + pathname + } + ], + lineList, + pathname: DIR_COVERAGE + pathname + }); + fileDict[pathname] = { + lineList, + linesCovered, + linesTotal, + pathname, + src + }; + })); + await htmlRender({ + fileList: Object.keys(fileDict).sort().map(function (pathname) { + return fileDict[pathname]; + }), + pathname: DIR_COVERAGE + "index" + }); +}()); +' # "' +)} + +shRunWithScreenshotTxt() {(set -e +# this function will run cmd $@ and screenshot text-output +# https://www.cnx-software.com/2011/09/22/how-to-convert-a-command-line-result-into-an-image-in-linux/ + local EXIT_CODE + EXIT_CODE=0 + export SCREENSHOT_SVG=.build/screenshot.svg + rm -f "$SCREENSHOT_SVG" + printf "0\n" > "$SCREENSHOT_SVG.exit_code" + shCiPrint "shRunWithScreenshotTxt - (shRun $* 2>&1)" + ( + (shRun "$@" 2>&1) || printf "$?\n" > "$SCREENSHOT_SVG.exit_code" + ) | tee /tmp/shRunWithScreenshotTxt.txt + EXIT_CODE="$(cat "$SCREENSHOT_SVG.exit_code")" + shCiPrint "shRunWithScreenshotTxt - EXIT_CODE=$EXIT_CODE" + # run shRunWithScreenshotTxtAfter + if (type shRunWithScreenshotTxtAfter > /dev/null 2>&1) + then + eval shRunWithScreenshotTxtAfter + unset shRunWithScreenshotTxtAfter + fi + # format text-output + node -e ' +(async function () { + "use strict"; + let result; + let yy; + yy = 10; + result = await require("fs/promises").readFile( + require("os").tmpdir() + "/shRunWithScreenshotTxt.txt", + "utf8" + ); + // remove ansi escape-code + result = result.replace(( + /\u001b.*?m/g + ), ""); + // format unicode + result = result.replace(( + /\\u[0-9a-f]{4}/g + ), function (match0) { + return String.fromCharCode("0x" + match0.slice(-4)); + }).trimEnd(); + // 96 column wordwrap + result = result.replace(( + /^.*?$/gm + ), function (line) { + return line.replace(( + /.{0,96}/g + ), function (line, ii) { + if (ii && !line) { + return ""; + } + yy += 16; + return "" + line.replace(( + /&/g + ), "&").replace(( + //g + ), ">") + ""; + }).replace(( + /(<\/tspan>\n" + + "\n" + + "\n" + + result + "\n\n" + ); + try { + await require("fs/promises").mkdir(( + require("path").dirname(process.argv[1]) + ), { + recursive: true + }); + } catch (ignore) {} + require("fs/promises").writeFile(process.argv[1], result); +}()); +' "$SCREENSHOT_SVG" # "' + shCiPrint "shRunWithScreenshotTxt - wrote - $SCREENSHOT_SVG" + return "$EXIT_CODE" +)} + +# run $@ +"$@" diff --git a/branch.master/font-daley-bold.woff2 b/branch.master/font-daley-bold.woff2 new file mode 100644 index 000000000..783bdd697 Binary files /dev/null and b/branch.master/font-daley-bold.woff2 differ diff --git a/branch.master/font-programma-bold.woff2 b/branch.master/font-programma-bold.woff2 new file mode 100644 index 000000000..dfb5a9753 Binary files /dev/null and b/branch.master/font-programma-bold.woff2 differ diff --git a/branch.master/function.html b/branch.master/function.html new file mode 100644 index 000000000..05c02480e --- /dev/null +++ b/branch.master/function.html @@ -0,0 +1,616 @@ + + + + + + + + +JSLint: jslint function + + + +
    JSLint
    + +
    The jslint Function
    +
    +

    JSLint

    +

    JSLint is delivered as a set of files:

    +
    + + + + + + + + + + + + + + +
    FilenameContent
    browser.jsBrowser based UI.
    function.htmlThis page that you are looking at right now.
    help.htmlUsing jslint as a single page JSLint application.
    index.htmlSingle page JSLint application that uses the + js files.
    jslint.jsThe jslint function.
    report.jsThe report object, containing report generator functions for + HTML.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    +
    + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring"(JSLint)"
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    parenttokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    parenttokenThe function in which the variable was declared.variable
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    shebangstringThe first line of text if it started with #!line
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable may be assigned to.variable
    +

    Reports

    +

    The report object contains three functions that all take a + result from the jslint function as input. + report.js has no other dependence on other files.

    + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    +
    + +
    + + + +
    + + diff --git a/branch.master/help.html b/branch.master/help.html new file mode 100644 index 000000000..1a55f6192 --- /dev/null +++ b/branch.master/help.html @@ -0,0 +1,813 @@ + + + + + + + + + +JSLint: Help + + + +
    JSLint
    + +
    Help
    +
    +
    Il semble que la perfection soit atteinte non quand il n’y a + plus rien à ajouter, mais quand il n’y a plus rien à + retrancher.
    +
    + Antoine de Saint-Exupéry +
    +
    + Terre des Hommes (1939) +
    +

    Good Parts

    +

    The Principle of the Good Parts is

    +
    If a feature is sometimes useful and sometimes dangerous and if + there is a better option then always use the better option.
    +

    JSLint is a JavaScript program that looks for problems in + JavaScript programs. It is a code quality tool.

    +

    When C was + a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    +

    As the language matured, the definition of the language was + strengthened to eliminate some insecurities, and compilers got better + at issuing warnings. lint is no longer needed.

    +

    JavaScript is a + young-for-its-age language. It was originally intended to do small tasks in + webpages, tasks for which Java was too heavy and clumsy. But JavaScript is + a surprisingly capable language, and it is now being used in larger + projects. Many of the features that were intended to make the language easy + to use are troublesome when projects become complicated. A lint + for JavaScript is needed: JSLint, a JavaScript syntax checker + and validator.

    +

    JSLint takes a JavaScript source and scans it. If it finds a + problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by the ECMAScript Programming Language + Standard (the strangely named document that governs JavaScript). + JSLint will reject most legal programs. It is a higher + standard.

    +

    JavaScript is a sloppy language, but hidden deep inside there is an elegant, + better language. JSLint helps you to program in that better + language and to avoid most of the slop. JSLint will reject + programs that browsers will accept because JSLint is concerned + with the quality of your code and browsers are not. You should gladly accept + all of JSLint's advice.

    +

    JSLint can operate on JavaScript source + or JSON text.

    +

    ECMAScript Sixth Edition

    +

    Some of + ES6’s features are good, so JSLint will recognize the good + parts of ES6.

    +

    Currently, these features are recognized:

    +
      +
    • The ... ellipsis marker in parameter + lists and argument lists, replacing the arguments object + for variadic functions.
    • +
    • The let statement, which is like the var + statement except that it respects block scope. + You may use let or var but not both.
    • +
    • The const statement is like the let statement + except that it disallows the use of assignment on the variable, although + if the value of the variable is mutable, it can still be mutated. + const is preferred to let. +
    • Destructuring of arrays and objects is allowed in parameter lists and on + the left side of let, and const, but not + var or assignment statements, and not deep destructuring + or eliding.
    • +
    • Enhanced object literals, providing shorter forms for function + declaration and properties that are initialized by variables with the + same name.
    • +
    • The fat arrow => fart functions.
    • +
    • The simplest forms of import and export.
    • +
    • `Megastring` literals, but not nested + `megastring` literals.
    • +
    • New global functions, such as Map, Set, + WeakMap, and WeakSet.
    • +
    • 0b- and 0o- number literals.
    • +
    +

    The most important new feature of ES6 is proper tail calls. This has no + new syntax, so JSLint doesn’t see it. But it makes recursion + much more attractive, which makes loops, particularly for + loops, much less attractive.

    +

    import export

    +

    The ES6 module feature will be an important improvement over JavaScript’s + global variables as a means of linking separate files together. + JSLint recognizes a small but essential subset of the module + syntax.

    +
    +

    import name from stringliteral;

    +

    import {name} from stringliteral;

    +

    import(string).then(function);

    +

    export default function () {};

    +

    export default function name() {};

    +

    export default expression;

    +

    export function name() {}

    +

    export name; // where name is const

    +

    export {name};

    +
    +

    Directives

    +

    JSLint provides three directives that may be placed in a + file to manage JSLint’s behavior. Use of these directives is + optional. If they are used, they should be placed in a source file before + the first statement. They are written in the form of a comment, where the + directive name is placed immediately after the opening of the comment before + any whitespace. The three directives are global, + jslint, and property. Directives in a file are + stronger than options selected from the UI or passed with the option object.

    +

    /*global*/

    +

    The /*global*/ directive is used to specify a set of globals + (usually functions and objects containing functions) that are available to + this file. This was commonly used in browsers to link source files together + before ES6 modules appeared. Use of global variables is strongly + discouraged, but unfortunately web browsers require their use. The + /*global*/ directive can only be used when the + Assume a browser option is selected. +

    Each of the names listed will indicate a read-only global variable. The names + are separated by , comma. This directive + should not be used if import or export is used. + This directive inhibits warnings, but it does not declare the names in the + execution environment. +

    For example: +

    /*global +
    ADSAFE, report, jslint
    +*/
    +

    instructs JSLint to not give warnings about the global + variables ADsafe, report, and jslint. + However, if any of those names are expected to be supplied by other files + and those other files fail to do so, then execution errors will result. It + is usually better to use top-level variable declarations instead: +

        var ADSAFE;
    +    var report;
    +    var jslint; 
    +

    Using var in this way allows comparing a global variable to the + undefined value to determine whether it is has been used in the global context.

    +

    /*jslint*/

    +

    The /*jslint*/ directive allows for the control of several + options. These options can also be set from the + JSLint.com user interface.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Tolerate bitwise operatorsbitwisetrue if bitwise operators should be allowed. The + bitwise operators are rarely used in JavaScript programs, so it + is usually more likely that & is a mistyping of + && than that it indicates a bitwise and. + JSLint will give warnings on the bitwise operators + unless this option is selected.
    Assume a browser browsertrue if the standard browser globals should be + predefined. This option will reject the use of + import and export. This option also + disallows the file form of the "use + strict" pragma. It does not supply + self; you will have to request that unnecessary + alias of the dreaded global object yourself. It adds the same + globals as this directive: +
    /*global +
    + clearInterval, clearTimeout, document, event, FileReader, + FormData, history, localStorage, location, name, navigator, + screen, sessionStorage, setInterval, setTimeout, Storage, + URL, window, XMLHttpRequest +
    + */
    +
    Tolerate conversion operatorsconverttrue if !!, + prefix, + and concatenation with "" are allowed. + The Boolean, Number, and + String functions are preferred
    Assume CouchDBcouchtrue if + Couch DB + globals should be predefined. It adds the same globals as this + directive: +
    /*global +
    emit, getRow, isArray, log, provides, registerType, require, + send, start, sum, toJSON
    + */
    Assume in developmentdeveltrue if browser globals that are useful in + development should be predefined, and if debugger + statements and TODO comments + should be allowed. It adds the + same globals as this directive: +
    /*global +
    alert, confirm, console, prompt
    + */
    Be sure to turn this option off before going + into production.
    Tolerate evalevaltrue if eval should be allowed. In the + past, the eval function was the most misused + feature of the language.
    Tolerate for statementfortrue if the for statement should be + allowed. It is almost always better to use the array methods + instead.
    Fudge the line and column numbersfudgetrue if the origin of a text file is line 1 column + 1. Programmers know that number systems start at zero. + Unfortunately, most text editors start numbering lines and + columns at one. JSLint will by default start + numbering at zero. Use this option to start the numbering at one + in its reports.
    Tolerate get and setgetsettrue if accessor properties are allowed in object literals.
    Tolerate long source lineslongtrue if a line can contain more than 80 characters.
    Assume Node.jsnodetrue if Node.js globals should be predefined. It + will predefine globals that are used in the Node.js environment. + It adds the same globals as this directive: +
    /*global +
    + Buffer, clearImmediate, clearInterval, clearTimeout, console, exports, + module, process, require, setImmediate, setInterval, setTimeout, URL, + URLSearchParams, __dirname, __filename +
    + */
    Tolerate single quote stringssingletrue if 'single quote + should be allowed to enclose string literals.
    Tolerate this
    thistrue if this should be allowed.
    Tolerate whitespace messwhitetrue if the whitespace rules should be ignored.
    +

    For example:

    +
    /*jslint
    +    bitwise, node
    +*/
    + +

    /*property*/

    +

    The /*property*/ directive is used to declare a list of + property identifiers that are used in the file. Each property name in the + program is looked up in this list. If a name is not found, that indicates an + error, most likely a typing error.

    +

    The list can also be used to evade some of JSLint’s naming + rules.

    +

    JSLint can build the /*property*/ list for you. At + the bottom of its report, JSLint displays a + /*property*/ directive. It contains all of the names that were + used with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. You + can copy the /*property*/ directive to the top of your + script file. JSLint will check the spelling of all property + names against the list. That way, you can have JSLint look for + misspellings for you.

    +

    For example,

    +
    /*property +
    charAt, slice, _$_
    + */
    +

    ignore

    +

    JSLint introduces a new reserved word: ignore. It + is used in parameter lists and in catch clauses to indicate a + parameter that will be ignored. Unused warnings will not be produced for + ignore. +

    function handler(ignore, value) {
    +    return do_something_useful(value);
    +}
    +

    var let const

    +

    JavaScript provides three statements for declaring variables: + var, let, and const. The + var statement suffers from a bad practice called hoisting. The + let statement does not do hoisting and respects block scope. + The const statement is like let except that it + marks the variable (but not its contents) as read only, making it an error + to attempt to assign to the variable. When given a choice, + const is the best, var is the worst.

    +

    JSLint uses the intersection of the var rules and the + let rules, and by doing so avoids the errors related to either. + A name should be declared only once in a function. It should be declared + before it is used. It should not be used outside of the block in which it is + declared. A variable should not have the same name as a variable or + parameter in an outer function. Do not mix var and + let. Declare one name per statement.

    +

    = == ===

    +

    Fortran made a terrible + mistake in using the equality operator as its assignment operator. That + mistake has been replicated in most languages since then. C compounded that + mistake by making == its equality operator. The visual + similarity is a source of errors. JavaScript compounded this further by + making == a type coercing comparison operator that produces + false positive results. This was mitigated by adding the === + operator, leaving the broken == operator in place.

    +

    JSLint attempts to minimize errors by the following rules:

    +

    == is not allowed. This avoids the false positives, and + increases the visual distance between = and ===. + Assignments are not allowed in expression position, and comparisons are not + allowed in statement position. This also reduces confusion. 

    +

    Semicolon

    +

    JavaScript uses a C-like syntax that requires the use of semicolons to + delimit certain statements. JavaScript attempts to make those semicolons + optional with an automatic semicolon insertion mechanism, but it does not + work very well. Automatic semicolon insertion was added to make + things easier for beginners. Unfortunately, it sometimes fails. Do not rely + on it unless you are a beginner.

    +

    JSLint expects that every statement will be followed by + ; except for for, function, + if, switch, try, and + while. JSLint does not expect to see unnecessary + semicolons, the empty statement, or empty blocks.

    +

    function =>

    +

    JavaScript has four syntactic forms for making function objects: function + statements, function expressions, enhanced object literals, and the + => fart operator.

    +

    The function statement creates a variable and assigns the function object to + it. It should be used in a file or function body, but not inside of a block.

    +
    function name(parameters) { +
    statements
    + }
    +

    The function expression unfortunately looks like the function statement. It + may appear anywhere that an expression may appear, but not in statement + position and not in a loop. It produces a function object but does not + create a variable in which to store it.

    +
    function (parameters) { +
    statements
    + }
    +

    The enhanced object literal provides an ES6 shorthand for creating a property + whose value is a function, saving you from having to type + : colon and function.

    +
    { +
    name(parameters) { +
    statements
    + }
    + }
    +

    Finally, ES6 provides an even shorter form of function expression that leaves + out the words function and return:

    +
    (parameters) => + expression
    +

    JSLint requires the parens around the parameters, and forbids a + { left brace after the + => fart to avoid syntactic ambiguity.

    +

    Comma

    +

    The , comma operator is unnecessary and can + mask programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as + an operator. It does not expect to see elided elements in array literals. A + comma should not appear after the last element of an array literal or object + literal.

    +

    Blocks

    +

    JSLint expects blocks with function, + if, switch, while, for, + do, and try statements and nowhere else.

    +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    +

    JavaScript allows an if to be written like this:

    +
    if (condition)
    +    statement;
    +

    That form is known to contribute to mistakes in projects where many + programmers are working on the same code. That is why JSLint + expects the use of a block:

    +
    if (condition) {
    +    statements;
    +}
    +

    Experience shows that this form is more resilient.

    +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call. All other expression statements are considered + to be errors.

    +

    for

    +

    JSLint does not recommend use of the for statement. + Use array methods like forEach instead. The for + option will suppress some warnings. The forms of for that + JSLint accepts are restricted, excluding the new ES6 forms.

    +

    for in

    +

    JSLint does not recommend use of the for + in statement. Use Object.keys instead.

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, it also + loops through all of the properties that were inherited through the + prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written + without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    for (name in object) {
    +    if (object.hasOwnProperty(name)) {
    +        ....
    +    }
    +}
    +

    Note that the above code will fail if the object contains a property + named hasOwnProperty. Use Object.keys instead.

    +

    switch

    +

    A common error in switch statements is to forget to place a + break statement after each case, resulting in unintended + fall-through. JSLint expects that the statement before the next + case or default is one of these: + break, return, or throw.

    +

    with

    +

    The with statement was intended to provide a shorthand in + accessing properties in deeply nested objects. Unfortunately, it behaves + very badly when setting new properties. Never use the with + statement.

    +

    JSLint does not expect to see a with statement.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that + labels will be distinct from vars and parameters.

    +

    Unreachable Code

    +

    JSLint expects that a return, break, + or throw statement will be followed by a + } right brace or case or + default.

    +

    Confusing Pluses and Minuses

    +

    JSLint expects that + will not be followed by + + or ++, and that - will not be + followed by - or --. A misplaced space can turn + + + into ++, an error that is difficult to see. + Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ increment and + -- decrement operators have been known to + contribute to bad code by encouraging excessive trickiness. They are second + only to faulty architecture in enabling to viruses and other security + menaces. Also, preincrement/postincrement confusion can produce off-by-one + errors that are extremely difficult to diagnose. Fortunately, they are also + complete unnecessary. There are better ways to add 1 to a variable.

    +

    It is best to avoid these operators entirely and rely on += and + -= instead.

    +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. + JSLint looks for problems that may cause portability problems. + It also attempts to resolve visual ambiguities by recommending explicit + escapement.

    +

    JavaScript’s syntax for regular expression literals overloads the + / slash character. To avoid ambiguity, + JSLint expects that the character preceding a regular + expression literal is a ( left paren or + = equal or + : colon or + , comma character.

    +

    this

    +
    Having this in the language makes it harder to talk + about the language. It is like pair programming with Abbott and Costello. +
    +

    Avoid using this. Warnings about this can be + suppressed with option.this.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the + new prefix. The new prefix creates a new object + based on the function's prototype, and binds that object to the + function's implied this parameter. If you neglect to use the + new prefix, no new object will be made and this + will be bound to the global object.

    +

    JSLint enforces the convention that constructor functions be + given names with initial uppercase. JSLint does not expect to + see a function invocation with an initial uppercase name unless it has the + new prefix. JSLint does not expect to see the + new prefix used with functions whose names do not start with + initial uppercase.

    +

    JSLint does not expect to see the wrapper forms + new Number, new String, new Boolean.

    +

    JSLint does not expect to see new Object. + Use Object.create(null) instead.

    +

    Whitespace

    +

    JSLint has a specific set of rules about the use of whitespace. + Where possible, these rules are consistent with centuries of good practice + with literary style.

    +

    The indentation increases by 4 spaces when the last token on a line is + { left brace, + [ left bracket, + ( left paren. The matching closing + token will be the first token on a line, restoring the previous + indentation.

    +

    The ternary operator can be visually confusing, so + ? question mark and + : colon always begin a line and increase + the indentation by 4 spaces.

    +
    return (
    +    (the_token.id === "(string)" || the_token.id === "(number)")
    +    ? String(the_token.value)
    +    : the_token.id
    +);
    +

    The word function is always followed with one space.

    +

    Clauses (case, catch, default, + else, finally) are not statements and so should + not be indented like statements.

    +

    Spaces are used to make things that are not invocations look less like + invocations.

    +

    Tabs and spaces should not be mixed. We should pick just one in order to + avoid the problems that come from having both. Personal preference is an + extremely unreliable criterion. Neither offers a powerful advantage over the + other. Fifty years ago, tab had the advantage of consuming less memory, but + Moore's Law has eliminated that advantage. Space has one clear advantage + over tab: there is no reliable standard for how many spaces a tab + represents, but it is universally accepted that a space occupies a space. So + use spaces. You can edit with tabs if you must, but make sure it is spaces + again before you commit. Maybe someday we will finally get a universal + standard for tabs, but until that day comes, the better choice is spaces.

    +

    Report

    +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    +
      +
    • The line number on which it starts.
    • +
    • The function’s name. In the case of anonymous functions, + JSLint will attempt to + «guess» the name.
    • +
    • The parameters.
    • +
    • Variables: The variables that are declared in the function.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Exceptions: The variables that are declared by catch + clauses of try statements.
    • +
    • Outer: Variables used by this function that are declared in + other functions.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements.

    +

    Try it

    +

    Try it. Paste your script + into the window and click the + + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    JSLint is written entirely in JavaScript, so it can run + anywhere that JavaScript (or even Java) can run.

    +

    Perfectly fine

    +

    JSLint was designed to reject code that some would consider to + be perfectly fine. The reason for this is that JSLint's + purpose is to help produce programs that are free of error. That is + difficult in any language and is especially hard in JavaScript. + JSLint attempts to help you increase the visual distance + between correct programs and incorrect programs, making the remaining errors + more obvious. JSLint will give warnings about things that are + not necessarily wrong in the current situation, but which have been observed + to mask or obscure errors. Avoid those things when there are better options + available.

    +

    It is dangerous out there. JSLint is here to help.

    +

    Warning

    +

    JSLint will hurt your feelings. Side effects may include + headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, + dry mouth, cleaner code, and a reduced error rate.

    +

    Further Reading

    + +
    +

    + Code Conventions for the JavaScript Programming Language.

    + + + +
    + + diff --git a/branch.master/image-folder-open-solid.svg b/branch.master/image-folder-open-solid.svg new file mode 100644 index 000000000..99d403c81 --- /dev/null +++ b/branch.master/image-folder-open-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/branch.master/image-github-brands.svg b/branch.master/image-github-brands.svg new file mode 100644 index 000000000..7870c06dc --- /dev/null +++ b/branch.master/image-github-brands.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/branch.master/image-jslint-128x128.png b/branch.master/image-jslint-128x128.png new file mode 100644 index 000000000..f7340e0b9 Binary files /dev/null and b/branch.master/image-jslint-128x128.png differ diff --git a/branch.master/image-jslint-256x256.png b/branch.master/image-jslint-256x256.png new file mode 100644 index 000000000..1b2c70aba Binary files /dev/null and b/branch.master/image-jslint-256x256.png differ diff --git a/branch.master/image-jslint-32x32.png b/branch.master/image-jslint-32x32.png new file mode 100644 index 000000000..0d32ee109 Binary files /dev/null and b/branch.master/image-jslint-32x32.png differ diff --git a/branch.master/image-jslint-512x512.png b/branch.master/image-jslint-512x512.png new file mode 100644 index 000000000..f1b440efe Binary files /dev/null and b/branch.master/image-jslint-512x512.png differ diff --git a/branch.master/image-jslint-64x64.png b/branch.master/image-jslint-64x64.png new file mode 100644 index 000000000..7545fffbf Binary files /dev/null and b/branch.master/image-jslint-64x64.png differ diff --git a/branch.master/image-jslint.html b/branch.master/image-jslint.html new file mode 100644 index 000000000..ba774fcb6 --- /dev/null +++ b/branch.master/image-jslint.html @@ -0,0 +1,52 @@ + + + +logo + + + +
    +
    JS
    +
    Lint
    +
    + + diff --git a/branch.master/image-json160.gif b/branch.master/image-json160.gif new file mode 100644 index 000000000..3bb55c8dd Binary files /dev/null and b/branch.master/image-json160.gif differ diff --git a/branch.master/image-window-maximize-regular.svg b/branch.master/image-window-maximize-regular.svg new file mode 100644 index 000000000..7b7e5e166 --- /dev/null +++ b/branch.master/image-window-maximize-regular.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/branch.master/index.html b/branch.master/index.html new file mode 100644 index 000000000..9b7b05a2a --- /dev/null +++ b/branch.master/index.html @@ -0,0 +1,446 @@ + + + + + + + + +JSLint: The JavaScript Code Quality Tool + + + +
    +
    Source
    +
    + + +
    +
    + Warnings +
    +
    +
    + Function Report +
    +
    +
    + Property Directive + +
    +
    + + + + +
    +
    Options +
    Assume... +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Global variables... + +
    +
    +
    + + diff --git a/branch.master/jslint.js b/branch.master/jslint.js new file mode 100755 index 000000000..de3f79d64 --- /dev/null +++ b/branch.master/jslint.js @@ -0,0 +1,5264 @@ +#!/usr/bin/env node +// jslint.js +// v2021.5.23 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// jslint(source, option_object, global_array) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze, a string or an array of strings. +// option_object An object whose keys correspond to option names. +// global_array An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all of the functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// 1. If the source is a single string, split it into an array of strings. +// 2. Turn the source into an array of tokens. +// 3. Furcate the tokens into a parse tree. +// 4. Walk the tree, traversing all of the nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// 5. Check the whitespace between the tokens. + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*property + a, all, and, argv, arity, assign, b, bad_assignment_a, bad_directive_a, + bad_get, bad_module_name_a, bad_option_a, bad_property_a, bad_set, bitwise, + block, body, browser, c, calls, catch, cli_mode, closer, closure, code, + column, concat, constant, context, convert, couch, create, d, dead, debug, + default, devel, directive, directives, disrupt, dot, duplicate_a, + early_stop, edition, ellipsis, else, empty_block, error, eval, every, exec, + exit, expected_a, expected_a_at_b_c, expected_a_b, expected_a_b_from_c_d, + expected_a_before_b, expected_a_next_at_b, expected_digits_after_a, + expected_four_digits, expected_identifier_a, expected_line_break_a_b, + expected_regexp_factor_a, expected_space_a_b, expected_statements_a, + expected_string_a, expected_type_string_a, exports, expression, extra, file, + finally, flag, for, forEach, formatted_message, free, freeze, + freeze_exports, from, froms, fud, fudge, function_in_loop, functions, g, + getset, global, has_await, i, id, identifier, import, inc, indexOf, + infix_in, init, initial, isArray, isNaN, is_async, join, json, keys, label, + label_a, lbp, led, length, level, line, line_offset, lines, live, long, + loop, m, map, margin, match, message, misplaced_a, misplaced_directive_a, + missing_await_statement, missing_browser, missing_m, module, naked_block, + name, names, nested_comment, new, node, not_label_a, now, nr, nud, + number_isNaN, ok, open, opening, option, out_of_scope_a, padStart, + parameters, parent, pop, property, push, quote, raw, readFile, readdir, + redefinition_a_b, repeat, replace, required_a_optional_b, reserved_a, role, + search, shebang, signature, single, slice, some, sort, split, stack, + stack_trace, startsWith, statement, stop, subscript_a, switch, test, then, + this, thru, todo_comment, tokens, too_long, too_many_digits, tree, trim, + try, type, u, unclosed_comment, unclosed_mega, unclosed_string, + undeclared_a, unexpected_a, unexpected_a_after_b, unexpected_a_before_b, + unexpected_at_top_level_a, unexpected_char_a, unexpected_comment, + unexpected_directive_a, unexpected_expression_a, unexpected_label_a, + unexpected_parens, unexpected_space_a_b, unexpected_statement_a, + unexpected_trailing_space, unexpected_typeof_a, uninitialized_a, + unreachable_a, unregistered_property_a, unused_a, use_double, use_open, + use_spaces, used, value, var_loop, var_switch, variable, versions, warning, + warnings, weird_condition_a, weird_expression_a, weird_loop, + weird_relation_a, white, wrap_condition, wrap_immediate, wrap_parameter, + wrap_regexp, wrap_unary, wrapped, writable, y +*/ + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than '{}' because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +function populate(array, object = empty(), value = true) { + +// Augment an object by taking property names from an array of strings. + + array.forEach(function (name) { + object[name] = value; + }); + return object; +} + +const allowed_option = { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + bitwise: true, + browser: [ + "caches", "CharacterData", "clearInterval", "clearTimeout", "document", + "DocumentType", "DOMException", "Element", "Event", "event", "fetch", + "FileReader", "FontFace", "FormData", "history", "IntersectionObserver", + "localStorage", "location", "MutationObserver", "name", "navigator", + "screen", "sessionStorage", "setInterval", "setTimeout", "Storage", + "TextDecoder", "TextEncoder", "URL", "window", "Worker", + "XMLHttpRequest" + ], + couch: [ + "emit", "getRow", "isArray", "log", "provides", "registerType", + "require", "send", "start", "sum", "toJSON" + ], + convert: true, + debug: true, + devel: [ + "alert", "confirm", "console", "prompt" + ], + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + node: [ + "Buffer", "clearImmediate", "clearInterval", "clearTimeout", + "console", "exports", "module", "process", "require", + "setImmediate", "setInterval", "setTimeout", "TextDecoder", + "TextEncoder", "URL", "URLSearchParams", "__dirname", "__filename" + ], + single: true, + this: true, + white: true +}; + +const anticondition = populate([ + "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%", + "typeof", "(number)", "(string)" +]); + +// These are the bitwise operators. + +const bitwiseop = populate([ + "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=", + ">>>", ">>>=" +]); + +const escapeable = populate([ + "\\", "/", "`", "b", "f", "n", "r", "t" +]); + +const opener = { + +// The open and close pairs. + + "(": ")", // paren + "[": "]", // bracket + "{": "}", // brace + "${": "}" // mega +}; + +// The relational operators. + +const relationop = populate([ + "!=", "!==", "==", "===", "<", "<=", ">", ">=" +]); + +// This is the set of infix operators that require a space on each side. + +const spaceop = populate([ + "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/", + "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=", + ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" +]); + +const standard = [ + +// These are the globals that are provided by the language standard. + + "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI", + "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error", + "EvalError", "Float32Array", "Float64Array", "Generator", + "GeneratorFunction", "import", "Int8Array", "Int16Array", "Int32Array", + "Intl", "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat", + "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", + "Set", "String", "Symbol", "SyntaxError", "System", "TypeError", + "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", + "URIError", "WeakMap", "WeakSet" +]; + +const bundle = { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + and: "The '&&' subexpression should be wrapped in parens.", + bad_assignment_a: "Bad assignment to '{a}'.", + bad_directive_a: "Bad directive '{a}'.", + bad_get: "A get function takes no parameters.", + bad_module_name_a: "Bad module name '{a}'.", + bad_option_a: "Bad option '{a}'.", + bad_property_a: "Bad property name '{a}'.", + bad_set: "A set function takes one parameter.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + expected_a: "Expected '{a}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: ( + "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'." + ), + expected_a_before_b: "Expected '{a}' before '{b}'.", + expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.", + expected_digits_after_a: "Expected digits after '{a}'.", + expected_four_digits: "Expected four digits after '\\u'.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.", + expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.", + expected_space_a_b: "Expected one space between '{a}' and '{b}'.", + expected_statements_a: "Expected statements before '{a}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + expected_type_string_a: "Expected a type string and instead saw '{a}'.", + freeze_exports: ( + "Expected 'Object.freeze('. All export values should be frozen." + ), + function_in_loop: "Don't make functions within a loop.", + infix_in: ( + "Unexpected 'in'. Compare with undefined, " + + "or use the hasOwnProperty method instead." + ), + label_a: "'{a}' is a statement label.", + misplaced_a: "Place '{a}' at the outermost level.", + misplaced_directive_a: ( + "Place the '/*{a}*/' directive before the first statement." + ), + missing_await_statement: "Expected await statement in async function.", + missing_browser: "/*global*/ requires the Assume a browser option.", + missing_m: "Expected 'm' flag on a multiline regular expression.", + naked_block: "Naked block.", + nested_comment: "Nested comment.", + not_label_a: "'{a}' is not a label.", + number_isNaN: "Use Number.isNaN function to compare with NaN.", + out_of_scope_a: "'{a}' is out of scope.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + required_a_optional_b: ( + "Required parameter '{a}' after optional parameter '{b}'." + ), + reserved_a: "Reserved name '{a}'.", + subscript_a: "['{a}'] is better written in dot notation.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line is longer than 80 characters.", + too_many_digits: "Too many digits.", + unclosed_comment: "Unclosed comment.", + unclosed_mega: "Unclosed mega literal.", + unclosed_string: "Unclosed string.", + undeclared_a: "Undeclared '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_a_after_b: "Unexpected '{a}' after '{b}'.", + unexpected_a_before_b: "Unexpected '{a}' before '{b}'.", + unexpected_at_top_level_a: "Expected '{a}' to be in a function.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_directive_a: "When using modules, don't use directive '/*{a}'.", + unexpected_expression_a: ( + "Unexpected expression '{a}' in statement position." + ), + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_parens: "Don't wrap function literals in parens.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_statement_a: ( + "Unexpected statement '{a}' in expression position." + ), + unexpected_trailing_space: "Unexpected trailing space.", + unexpected_typeof_a: ( + "Unexpected 'typeof'. Use '===' to compare directly with {a}." + ), + uninitialized_a: "Uninitialized '{a}'.", + unreachable_a: "Unreachable '{a}'.", + unregistered_property_a: "Unregistered property name '{a}'.", + unused_a: "Unused '{a}'.", + use_double: "Use double quotes, not single quotes.", + use_open: ( + "Wrap a ternary expression in parens, " + + "with a line break after the left paren." + ), + use_spaces: "Use spaces, not tabs.", + var_loop: "Don't declare variables in a loop.", + var_switch: "Don't declare variables in a switch.", + weird_condition_a: "Weird condition '{a}'.", + weird_expression_a: "Weird expression '{a}'.", + weird_loop: "Weird loop.", + weird_relation_a: "Weird relation '{a}'.", + wrap_condition: "Wrap the condition in parens.", + wrap_immediate: ( + "Wrap an immediate function invocation in parentheses to assist " + + "the reader in understanding that the expression is the result " + + "of a function, and not the function itself." + ), + wrap_parameter: "Wrap the parameter in parens.", + wrap_regexp: "Wrap this regexp in parens to avoid confusion.", + wrap_unary: "Wrap the unary expression in parens." +}; + +// Regular expression literals: + +function tag_regexp(strings) { + return new RegExp(strings.raw[0].replace(/\s/g, "")); +} + +// supplant {variables} +const rx_supplant = /\{([^{}]*)\}/g; +// carriage return, carriage return linefeed, or linefeed +const rx_crlf = tag_regexp ` + \n + | \r \n? +`; +// identifier +const rx_identifier = tag_regexp ` ^( + [ a-z A-Z _ $ ] + [ a-z A-Z 0-9 _ $ ]* +)$`; +const rx_module = tag_regexp ` ^ [ a-z A-Z 0-9 _ $ : . @ \- \/ ]+ $ `; +const rx_bad_property = tag_regexp ` + ^_ + | \$ + | Sync $ + | _ $ +`; +// star slash +const rx_star_slash = tag_regexp ` \* \/ `; +// slash star +const rx_slash_star = tag_regexp ` \/ \* `; +// slash star or ending slash +const rx_slash_star_or_slash = tag_regexp ` \/ \* | \/ $ `; +// uncompleted work comment +const rx_todo = tag_regexp ` \b (?: + todo + | TO \s? DO + | HACK +) \b `; +// tab +const rx_tab = /\t/g; +// directive +const rx_directive = tag_regexp ` ^ ( + jslint + | property + | global +) \s+ ( .* ) $ `; +const rx_directive_part = tag_regexp ` ^ ( + [ a-z A-Z $ _ ] [ a-z A-Z 0-9 $ _ ]* +) (?: + : \s* ( true | false ) +)? ,? \s* ( .* ) $ `; +// token +const rx_token = tag_regexp ` ^ ( + (\s+) + | ( + [ a-z A-Z _ $ ] + [ a-z A-Z 0-9 _ $ ]* + ) + | [ + ( ) { } \[ \] , : ; ' " ~ \` + ] + | \? [ ? . ]? + | = (?: + = =? + | > + )? + | \.+ + | \* [ * \/ = ]? + | \/ [ * \/ ]? + | \+ [ = + ]? + | - [ = \- ]? + | [ \^ % ] =? + | & [ & = ]? + | \| [ | = ]? + | >{1,3} =? + | < = "a" && string <= "z\uffff") + || (string >= "A" && string <= "Z\uffff") + ); +} + +function supplant(string, object) { + return string.replace(rx_supplant, function (found, filling) { + const replacement = object[filling]; + return ( + replacement !== undefined + ? replacement + : found + ); + }); +} + +let anon; // The guessed name for anonymous functions. +let blockage; // The current block. +let block_stack; // The stack of blocks. +let declared_globals; // The object containing the global declarations. +let directives; // The directive comments. +let directive_mode; // true if directives are still allowed. +let early_stop; // true if JSLint cannot finish. +let exports; // The exported names and values. +let froms; // The array collecting all import-from strings. +let fudge; // true if the natural numbers start with 1. +let functionage; // The current function. +let functions; // The array containing all of the functions. +let global; // The global object; the outermost context. +let json_mode; // true if parsing JSON. +let lines; // The array containing source lines. +let mega_mode; // true if currently parsing a megastring literal. +let module_mode; // true if import or export was used. +let next_token; // The next token to be examined in the parse. +let option; // The options parameter. +let property; // The object containing the tallied property names. +let shebang; // true if a #! was seen on the first line. +let stack; // The stack of functions. +let syntax; // The object containing the parser. +let token; // The current token being examined in the parse. +let token_nr; // The number of the next token. +let tokens; // The array of tokens. +let tenure; // The predefined property registry. +let tree; // The abstract parse tree. +let var_mode; // "var" if using var; "let" if using let. +let warnings; // The array collecting all generated warnings. + +// Error reportage functions: + +function artifact(the_token) { + +// Return a string representing an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); +} + +function artifact_line(the_token) { + +// Return the fudged line number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.line + fudge; +} + +function artifact_column(the_token) { + +// Return the fudged column number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.from + fudge; +} + +function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + const warning = { // ~~ + name: "JSLintError", + column, + line, + code + }; + if (a !== undefined) { + warning.a = a; + } + if (b !== undefined) { + warning.b = b; + } + if (c !== undefined) { + warning.c = c; + } + if (d !== undefined) { + warning.d = d; + } + warning.message = supplant(bundle[code] || code, warning); +// Include stack_trace for jslint to debug itself for errors. + if (option.debug) { + warning.stack_trace = new Error().stack; + } + warnings.push(warning); + return warning; +} + +function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); +} + +function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + if (the_token.warning === undefined) { + the_token.warning = warn_at( + code, + the_token.line, + the_token.from, + a || artifact(the_token), + b, + c, + d + ); + return the_token.warning; + } +} + +function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); +} + +// Tokenize: + +function tokenize(source) { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + +// If the source is not an array, then it is split into lines at the +// carriage return/linefeed. + + lines = ( + Array.isArray(source) + ? source + : source.split(rx_crlf) + ); + tokens = []; + + let char; // a popular character + let column = 0; // the column number of the next character + let first; // the first token + let from; // the starting column number of the token + let line = -1; // the line number of the next character + let nr = 0; // the next token number + let previous = global; // the previous token including comments + let prior = global; // the previous token excluding comments + let mega_from; // the starting column of megastring + let mega_line; // the starting line of megastring + let regexp_seen; // regular expression literal seen on this line + let snippet; // a piece of string + let source_line = ""; // the remaining line source string + let whole_line = ""; // the whole line source string + + if (lines[0].startsWith("#!")) { + line = 0; + shebang = true; + } + + function next_line() { + +// Put the next line of source in source_line. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + let at; + if ( + !option.long + && whole_line.length > 80 + && !json_mode + && first + && !regexp_seen + ) { + warn_at("too_long", line, 80); + } + column = 0; + line += 1; + regexp_seen = false; + source_line = lines[line]; + whole_line = source_line || ""; + if (source_line !== undefined) { + at = source_line.search(rx_tab); + if (at >= 0) { + if (!option.white) { + warn_at("use_spaces", line, at + 1); + } + source_line = source_line.replace(rx_tab, " "); + } + if (!option.white && source_line.slice(-1) === " ") { + warn_at( + "unexpected_trailing_space", + line, + source_line.length - 1 + ); + } + } + return source_line; + } + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions snip, next_char, back_char, +// some_digits, and escape help in the parsing of literals. + + function snip() { + +// Remove the last character from snippet. + + snippet = snippet.slice(0, -1); + } + + function next_char(match) { + +// Get the next character from the source line. Remove it from the source_line, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + return stop_at( + ( + char === "" + ? "expected_a" + : "expected_a_b" + ), + line, + column - 1, + match, + char + ); + } + if (source_line) { + char = source_line[0]; + source_line = source_line.slice(1); + snippet += char; + } else { + char = ""; + snippet += " "; + } + column += 1; + return char; + } + + function back_char() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the source_line. + + if (snippet) { + char = snippet.slice(-1); + source_line = char + source_line; + column -= 1; + snip(); + } else { + char = ""; + } + return char; + } + + function some_digits(rx, quiet) { + const digits = source_line.match(rx)[0]; + const length = digits.length; + if (!quiet && length === 0) { + warn_at("expected_digits_after_a", line, column, snippet); + } + column += length; + source_line = source_line.slice(length); + snippet += digits; + next_char(); + return length; + } + + function escape(extra) { + next_char("\\"); + if (escapeable[char] === true) { + return next_char(); + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "u") { + if (next_char("u") === "{") { + if (json_mode) { + warn_at("unexpected_a", line, column - 1, char); + } + if (some_digits(rx_hexs) > 5) { + warn_at("too_many_digits", line, column - 1); + } + if (next_char() !== "}") { + stop_at("expected_a_before_b", line, column, "}", char); + } + return next_char(); + } + back_char(); + if (some_digits(rx_hexs, true) < 4) { + warn_at("expected_four_digits", line, column - 1); + } + return; + } + if (extra && extra.indexOf(char) >= 0) { + return next_char(); + } + warn_at("unexpected_a_before_b", line, column - 2, "\\", char); + } + + function make(id, value, identifier) { + +// Make the token object and append it to the tokens list. + + const the_token = { + from, + id, + identifier: Boolean(identifier), + line, + nr, + thru: column + }; + tokens[nr] = the_token; + nr += 1; + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + directive_mode = false; + } + +// If the token is to have a value, give it one. + + if (value !== undefined) { + the_token.value = value; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option.white. + + if ( + previous.line === line + && previous.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (previous.id === "(comment)" || previous.id === "(regexp)") + ) { + warn( + "expected_space_a_b", + the_token, + artifact(previous), + artifact(the_token) + ); + } + if (previous.id === "." && id === "(number)") { + warn("expected_a_before_b", previous, "0", "."); + } + if (prior.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// The previous token is used to detect adjacency problems. + + previous = the_token; + +// The prior token is a previous token that was not a comment. The prior token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (previous.id !== "(comment)") { + prior = previous; + } + return the_token; + } + + function parse_directive(the_comment, body) { + +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + + const result = body.match(rx_directive_part); + if (result) { + let allowed; + const name = result[1]; + const value = result[2]; + if (the_comment.directive === "jslint") { + allowed = allowed_option[name]; + if ( + typeof allowed === "boolean" + || typeof allowed === "object" + ) { + if ( + value === "" + || value === "true" + || value === undefined + ) { + option[name] = true; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } else if (value === "false") { + option[name] = false; + } else { + warn("bad_option_a", the_comment, name + ":" + value); + } + } else { + warn("bad_option_a", the_comment, name); + } + } else if (the_comment.directive === "property") { + if (tenure === undefined) { + tenure = empty(); + } + tenure[name] = true; + } else if (the_comment.directive === "global") { + if (value) { + warn("bad_option_a", the_comment, name + ":" + value); + } + declared_globals[name] = false; + module_mode = the_comment; + } + return parse_directive(the_comment, result[3]); + } + if (body) { + return stop("bad_directive_a", the_comment, body); + } + } + + function comment(snippet) { + +// Make a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + + const the_comment = make("(comment)", snippet); + if (Array.isArray(snippet)) { + snippet = snippet.join(" "); + } + if (!option.devel && rx_todo.test(snippet)) { + warn("todo_comment", the_comment); + } + const result = snippet.match(rx_directive); + if (result) { + if (!directive_mode) { + warn_at("misplaced_directive_a", line, from, result[1]); + } else { + the_comment.directive = result[1]; + parse_directive(the_comment, result[2]); + } + directives.push(the_comment); + } + return the_comment; + } + + function regexp() { + +// Parse a regular expression literal. + + let multi_mode = false; + let result; + let value; + regexp_seen = true; + + function quantifier() { + +// Match an optional quantifier. + + if (char === "?" || char === "*" || char === "+") { + next_char(); + } else if (char === "{") { + if (some_digits(rx_digits, true) === 0) { + warn_at("expected_a_before_b", line, column, "0", ","); + } + if (char === ",") { + some_digits(rx_digits, true); + } + next_char("}"); + } else { + return; + } + if (char === "?") { + next_char("?"); + } + } + + function subklass() { + +// Match a character in a character class. + + if (char === "\\") { + escape("BbDdSsWw-[]^"); + return true; + } + if ( + char === "" + || char === "[" + || char === "]" + || char === "/" + || char === "^" + || char === "-" + ) { + return false; + } + if (char === " ") { + warn_at("expected_a_b", line, column, "\\u0020", " "); + } else if (char === "`" && mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char(); + return true; + } + + function ranges() { + +// Match a range of subclasses. + + if (subklass()) { + if (char === "-") { + next_char("-"); + if (!subklass()) { + return stop_at( + "unexpected_a", + line, + column - 1, + "-" + ); + } + } + return ranges(); + } + } + + function klass() { + +// Match a class. + + next_char("["); + if (char === "^") { + next_char("^"); + } + (function classy() { + ranges(); + if (char !== "]" && char !== "") { + warn_at( + "expected_a_before_b", + line, + column, + "\\", + char + ); + next_char(); + return classy(); + } + }()); + next_char("]"); + } + + function choice() { + + function group() { + +// Match a group that starts with left paren. + + next_char("("); + if (char === "?") { + next_char("?"); + if (char === "=" || char === "!") { + next_char(); + } else { + next_char(":"); + } + } else if (char === ":") { + warn_at("expected_a_before_b", line, column, "?", ":"); + } + choice(); + next_char(")"); + } + + function factor() { + if ( + char === "" + || char === "/" + || char === "]" + || char === ")" + ) { + return false; + } + if (char === "(") { + group(); + return true; + } + if (char === "[") { + klass(); + return true; + } + if (char === "\\") { + escape("BbDdSsWw^${}[]():=!.|*+?"); + return true; + } + if ( + char === "?" + || char === "+" + || char === "*" + || char === "}" + || char === "{" + ) { + warn_at( + "expected_a_before_b", + line, + column - 1, + "\\", + char + ); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column - 1, "`"); + } + } else if (char === " ") { + warn_at( + "expected_a_b", + line, + column - 1, + "\\s", + " " + ); + } else if (char === "$") { + if (source_line[0] !== "/") { + multi_mode = true; + } + } else if (char === "^") { + if (snippet !== "^") { + multi_mode = true; + } + } + next_char(); + return true; + } + + function sequence(follow) { + if (factor()) { + quantifier(); + return sequence(true); + } + if (!follow) { + warn_at("expected_regexp_factor_a", line, column, char); + } + } + +// Match a choice (a sequence that can be followed by | and another choice). + + sequence(); + if (char === "|") { + next_char("|"); + return choice(); + } + } + +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + next_char(); + if (char === "=") { + warn_at("expected_a_before_b", line, column, "\\", "="); + } + choice(); + +// Make sure there is a closing slash. + + snip(); + value = snippet; + next_char("/"); + +// Process dangling flag letters. + + const allowed = { + g: true, + i: true, + m: true, + u: true, + y: true + }; + const flag = empty(); + (function make_flag() { + if (is_letter(char)) { + if (allowed[char] !== true) { + warn_at("unexpected_a", line, column, char); + } + allowed[char] = false; + flag[char] = true; + next_char(); + return make_flag(); + } + }()); + back_char(); + if (char === "/" || char === "*") { + return stop_at("unexpected_a", line, from, char); + } + result = make("(regexp)", char); + result.flag = flag; + result.value = value; + if (multi_mode && !flag.m) { + warn_at("missing_m", line, column); + } + return result; + } + + function string(quote) { + +// Make a string token. + + let the_token; + snippet = ""; + next_char(); + + return (function next() { + if (char === quote) { + snip(); + the_token = make("(string)", snippet); + the_token.quote = quote; + return the_token; + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "\\") { + escape(quote); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char("`"); + } else { + next_char(); + } + return next(); + }()); + } + + function frack() { + if (char === ".") { + some_digits(rx_digits); + } + if (char === "E" || char === "e") { + next_char(); + if (char !== "+" && char !== "-") { + back_char(); + } + some_digits(rx_digits); + } + } + + function number() { + if (snippet === "0") { + next_char(); + if (char === ".") { + frack(); + } else if (char === "b") { + some_digits(rx_bits); + } else if (char === "o") { + some_digits(rx_octals); + } else if (char === "x") { + some_digits(rx_hexs); + } + } else { + next_char(); + frack(); + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + return stop_at( + "unexpected_a_after_b", + line, + column - 1, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + back_char(); + return make("(number)", snippet); + } + + function lex() { + let array; + let i = 0; + let j = 0; + let last; + let result; + let the_token; + +// This should properly be a tail recursive function, but sadly, conformant +// implementations of ES6 are still rare. This is the ideal code: + +// if (!source_line) { +// source_line = next_line(); +// from = 0; +// return ( +// source_line === undefined +// ? ( +// mega_mode +// ? stop_at("unclosed_mega", mega_line, mega_from) +// : make("(end)") +// ) +// : lex() +// ); +// } + +// Unfortunately, incompetent JavaScript engines will sometimes fail to execute +// it correctly. So for now, we do it the old fashioned way. + + while (!source_line) { + source_line = next_line(); + from = 0; + if (source_line === undefined) { + return ( + mega_mode + ? stop_at("unclosed_mega", mega_line, mega_from) + : make("(end)") + ); + } + } + + from = column; + result = source_line.match(rx_token); + +// result[1] token +// result[2] whitespace +// result[3] identifier +// result[4] number +// result[5] rest + + if (!result) { + return stop_at( + "unexpected_char_a", + line, + column, + source_line[0] + ); + } + + snippet = result[1]; + column += snippet.length; + source_line = result[5]; + +// Whitespace was matched. Call lex again to get more. + + if (result[2]) { + return lex(); + } + +// The token is an identifier. + + if (result[3]) { + return make(snippet, undefined, true); + } + +// The token is a number. + + if (result[4]) { + return number(snippet); + } + +// The token is a string. + + if (snippet === "\"") { + return string(snippet); + } + if (snippet === "'") { + if (!option.single) { + warn_at("use_double", line, column); + } + return string(snippet); + } + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (snippet === "`") { + if (mega_mode) { + return stop_at("expected_a_b", line, column, "}", "`"); + } + snippet = ""; + mega_from = from; + mega_line = line; + mega_mode = true; + +// Parsing a mega literal is tricky. First make a ` token. + + make("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + (function part() { + const at = source_line.search(rx_mega); + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + if (at < 0) { + snippet += source_line + "\n"; + return ( + next_line() === undefined + ? stop_at("unclosed_mega", mega_line, mega_from) + : part() + ); + } + snippet += source_line.slice(0, at); + column += at; + source_line = source_line.slice(at); + if (source_line[0] === "\\") { + snippet += source_line.slice(0, 2); + source_line = source_line.slice(2); + column += 2; + return part(); + } + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + make("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then make tokens that will become part of an expression until +// a } token is made. + + if (source_line[0] === "$") { + column += 2; + make("${"); + source_line = source_line.slice(2); + (function expr() { + const id = lex().id; + if (id === "{") { + return stop_at( + "expected_a_b", + line, + column, + "}", + "{" + ); + } + if (id !== "}") { + return expr(); + } + }()); + return part(); + } + }()); + source_line = source_line.slice(1); + column += 1; + mega_mode = false; + return make("`"); + } + +// The token is a // comment. + + if (snippet === "//") { + snippet = source_line; + source_line = ""; + the_token = comment(snippet); + if (mega_mode) { + warn("unexpected_comment", the_token, "`"); + } + return the_token; + } + +// The token is a /* comment. + + if (snippet === "/*") { + array = []; + if (source_line[0] === "/") { + warn_at("unexpected_a", line, column + i, "/"); + } + (function next() { + if (source_line > "") { + i = source_line.search(rx_star_slash); + if (i >= 0) { + return; + } + j = source_line.search(rx_slash_star); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + } + array.push(source_line); + source_line = next_line(); + if (source_line === undefined) { + return stop_at("unclosed_comment", line, column); + } + return next(); + }()); + snippet = source_line.slice(0, i); + j = snippet.search(rx_slash_star_or_slash); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + array.push(snippet); + column += i + 2; + source_line = source_line.slice(i + 2); + return comment(array); + } + +// The token is a slash. + + if (snippet === "/") { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + if (prior.identifier) { + if (!prior.dot) { + if (prior.id === "return") { + return regexp(); + } + if ( + prior.id === "(begin)" + || prior.id === "case" + || prior.id === "delete" + || prior.id === "in" + || prior.id === "instanceof" + || prior.id === "new" + || prior.id === "typeof" + || prior.id === "void" + || prior.id === "yield" + ) { + the_token = regexp(); + return stop("unexpected_a", the_token); + } + } + } else { + last = prior.id[prior.id.length - 1]; + if ("(,=:?[".indexOf(last) >= 0) { + return regexp(); + } + if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) { + the_token = regexp(); + warn("wrap_regexp", the_token); + return the_token; + } + } + if (source_line[0] === "=") { + column += 1; + source_line = source_line.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + } + return make(snippet); + } + + first = lex(); + json_mode = first.id === "{" || first.id === "["; + +// This loop will be replaced with a recursive call to lex when ES6 has been +// finished and widely deployed and adopted. + + while (true) { + if (lex().id === "(end)") { + break; + } + } +} + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + +function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!rx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!rx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property[id] === "number") { + property[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (tenure !== undefined) { + if (tenure[id] !== true) { + warn("unregistered_property_a", name); + } + } else { + if (name.identifier && rx_bad_property.test(id)) { + warn("bad_property_a", name); + } + } + property[id] = 1; + } + return id; +} + +function dispense() { + +// Deliver the next token, skipping the comments. + + const cadet = tokens[token_nr]; + token_nr += 1; + if (cadet.id === "(comment)") { + if (json_mode) { + warn("unexpected_a", cadet); + } + return dispense(); + } else { + return cadet; + } +} + +function lookahead() { + +// Look ahead one token without advancing. + + const old_token_nr = token_nr; + const cadet = dispense(true); + token_nr = old_token_nr; + return cadet; +} + +function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if (token.identifier && token.id !== "function") { + anon = token.id; + } else if (token.id === "(string)" && rx_identifier.test(token.value)) { + anon = token.value; + } + +// Attempt to match next_token with an expected id. + + if (id !== undefined && next_token.id !== id) { + return ( + match === undefined + ? stop("expected_a_b", next_token, id, artifact()) + : stop( + "expected_a_b_from_c_d", + next_token, + id, + artifact(match), + artifact_line(match), + artifact(next_token) + ) + ); + } + +// Promote the tokens, skipping comments. + + token = next_token; + next_token = dispense(); + if (next_token.id === "(end)") { + token_nr -= 1; + } +} + +// Parsing of JSON is simple: + +function json_value() { + let negative; + if (next_token.id === "{") { + return (function json_object() { + const brace = next_token; + const object = empty(); + const properties = []; + brace.expression = properties; + advance("{"); + if (next_token.id !== "}") { + (function next() { + let name; + let value; + if (next_token.quote !== "\"") { + warn( + "unexpected_a", + next_token, + next_token.quote + ); + } + name = next_token; + advance("(string)"); + if (object[token.value] !== undefined) { + warn("duplicate_a", token); + } else if (token.value === "__proto__") { + warn("bad_property_a", token); + } else { + object[token.value] = token; + } + advance(":"); + value = json_value(); + value.label = name; + properties.push(value); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("}", brace); + return brace; + }()); + } + if (next_token.id === "[") { + return (function json_array() { + const bracket = next_token; + const elements = []; + bracket.expression = elements; + advance("["); + if (next_token.id !== "]") { + (function next() { + elements.push(json_value()); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]", bracket); + return bracket; + }()); + } + if ( + next_token.id === "true" + || next_token.id === "false" + || next_token.id === "null" + ) { + advance(); + return token; + } + if (next_token.id === "(number)") { + if (!rx_JSON_number.test(next_token.value)) { + warn("unexpected_a"); + } + advance(); + return token; + } + if (next_token.id === "(string)") { + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + advance(); + return token; + } + if (next_token.id === "-") { + negative = next_token; + negative.arity = "unary"; + advance("-"); + advance("(number)"); + if (!rx_JSON_number.test(token.value)) { + warn("unexpected_a", token); + } + negative.expression = token; + return negative; + } + stop("unexpected_a"); +} + +// Now we parse JavaScript. + +function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + const id = name.id; + +// Reserved words may not be enrolled. + + if (syntax[id] !== undefined && id !== "ignore") { + warn("reserved_a", name); + } else { + +// Has the name been enrolled in this context? + + let earlier = functionage.context[id]; + if (earlier) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + +// Has the name been enrolled in an outer context? + + } else { + stack.forEach(function (value) { + const item = value.context[id]; + if (item !== undefined) { + earlier = item; + } + }); + if (earlier) { + if (id === "ignore") { + if (earlier.role === "variable") { + warn("unexpected_a", name); + } + } else { + if ( + ( + role !== "exception" + || earlier.role !== "exception" + ) + && role !== "parameter" + && role !== "function" + ) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + } + } + } + +// Enroll it. + + functionage.context[id] = name; + name.dead = true; + name.parent = functionage; + name.init = false; + name.role = role; + name.used = 0; + name.writable = !readonly; + } + } +} + +function expression(rbp, initial) { + +// This is the heart of the Pratt parser. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// nud Null denotation +// led Left denotation +// lbp Left binding power +// rbp Right binding power + +// It processes a nud (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop. It +// returns the expression's parse tree. + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement, + + if (!initial) { + advance(); + } + the_symbol = syntax[token.id]; + if (the_symbol !== undefined && the_symbol.nud !== undefined) { + left = the_symbol.nud(); + } else if (token.identifier) { + left = token; + left.arity = "variable"; + } else { + return stop("unexpected_a", token); + } + (function right() { + the_symbol = syntax[next_token.id]; + if ( + the_symbol !== undefined + && the_symbol.led !== undefined + && rbp < the_symbol.lbp + ) { + advance(); + left = the_symbol.led(left); + return right(); + } + }()); + return left; +} + +function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = next_token; + let the_value; + the_paren.free = true; + advance("("); + the_value = expression(0); + advance(")"); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (anticondition[the_value.id] === true) { + warn("unexpected_a", the_value); + } + return the_value; +} + +function is_weird(thing) { + return ( + thing.id === "(regexp)" + || thing.id === "{" + || thing.id === "=>" + || thing.id === "function" + || (thing.id === "[" && thing.arity === "unary") + ); +} + +function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + return ( + Array.isArray(b) + && a.length === b.length + && a.every(function (value, index) { + return are_similar(value, b[index]); + }) + ); + } + if (Array.isArray(b)) { + return false; + } + if (a.id === "(number)" && b.id === "(number)") { + return a.value === b.value; + } + let a_string; + let b_string; + if (a.id === "(string)") { + a_string = a.value; + } else if (a.id === "`" && a.constant) { + a_string = a.value[0]; + } + if (b.id === "(string)") { + b_string = b.value; + } else if (b.id === "`" && b.constant) { + b_string = b.value[0]; + } + if (typeof a_string === "string") { + return a_string === b_string; + } + if (is_weird(a) || is_weird(b)) { + return false; + } + if (a.arity === b.arity && a.id === b.id) { + if (a.id === ".") { + return ( + are_similar(a.expression, b.expression) + && are_similar(a.name, b.name) + ); + } + if (a.arity === "unary") { + return are_similar(a.expression, b.expression); + } + if (a.arity === "binary") { + return ( + a.id !== "(" + && are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + ); + } + if (a.arity === "ternary") { + return ( + are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + && are_similar(a.expression[2], b.expression[2]) + ); + } + if (a.arity === "function" && a.arity === "regexp") { + return false; + } + return true; + } + return false; +} + +function semicolon() { + +// Try to match a semicolon. + + if (next_token.id === ";") { + advance(";"); + } else { + warn_at( + "expected_a_b", + token.line, + token.thru, + ";", + artifact(next_token) + ); + } + anon = "anonymous"; +} + +function statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token.identifier && next_token.id === ":") { + the_label = token; + if (the_label.id === "ignore") { + warn("unexpected_a", the_label); + } + advance(":"); + if ( + next_token.id === "do" + || next_token.id === "for" + || next_token.id === "switch" + || next_token.id === "while" + ) { + enroll(the_label, "label", true); + the_label.init = true; + the_label.dead = false; + the_statement = statement(); + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token; + first.statement = true; + the_symbol = syntax[first.id]; + if (the_symbol !== undefined && the_symbol.fud !== undefined) { + the_symbol.disrupt = false; + the_symbol.statement = true; + the_statement = the_symbol.fud(); + } else { + +// It is an expression statement. + + the_statement = expression(0, true); + if (the_statement.wrapped && the_statement.id !== "(") { + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; +} + +function statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const array = []; + (function next(disrupt) { + if ( + next_token.id !== "}" + && next_token.id !== "case" + && next_token.id !== "default" + && next_token.id !== "else" + && next_token.id !== "(end)" + ) { + let a_statement = statement(); + array.push(a_statement); + if (disrupt) { + warn("unreachable_a", a_statement); + } + return next(a_statement.disrupt); + } + }(false)); + return array; +} + +function not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === global) { + warn("unexpected_at_top_level_a", thing); + } +} + +function top_level_only(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== global) { + warn("misplaced_a", the_thing); + } +} + +function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token; + the_block.arity = "statement"; + the_block.body = special === "body"; + +// Top level function bodies may include the "use strict" pragma. + + if ( + special === "body" + && stack.length === 1 + && next_token.value === "use strict" + ) { + next_token.statement = true; + advance("(string)"); + advance(";"); + } + stmts = statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option.devel && special !== "ignore") { + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; +} + +function mutation_check(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + warn("bad_assignment_a", the_thing); + return false; + } + return true; +} + +function left_check(left, right) { + +// Warn if the left is not one of these: +// e.b +// e[b] +// e() +// ?: +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !left_check(left.expression[1]) + && !left_check(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right); + return false; + } + return true; +} + +// These functions are used to specify the grammar of our language: + +function symbol(id, bp) { + +// Make a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax[id] = the_symbol; + } + return the_symbol; +} + +function assignment(id) { + +// Make an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led = function (left) { + const the_token = token; + let right; + the_token.arity = "assignment"; + right = expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "pre" + || right.arity === "post" + ) { + warn("unexpected_a", right); + } + mutation_check(left); + return the_token; + }; + return the_symbol; +} + +function constant(id, type, value) { + +// Make a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud = ( + typeof value === "function" + ? value + : function () { + token.constant = true; + if (value !== undefined) { + token.value = value; + } + return token; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; +} + +function infix(id, bp, f) { + +// Make an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, expression(bp)]; + return the_token; + }; + return the_symbol; +} + +function infixr(id, bp) { + +// Make a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + the_token.expression = [left, expression(bp - 1)]; + return the_token; + }; + return the_symbol; +} + +function post(id) { + +// Make one of the post operators. + + const the_symbol = symbol(id, 150); + the_symbol.led = function (left) { + token.expression = left; + token.arity = "post"; + mutation_check(token.expression); + return token; + }; + return the_symbol; +} + +function pre(id) { + +// Make one of the pre operators. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "pre"; + the_token.expression = expression(150); + mutation_check(the_token.expression); + return the_token; + }; + return the_symbol; +} + +function prefix(id, f) { + +// Make a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = expression(150); + return the_token; + }; + return the_symbol; +} + +function stmt(id, f) { + +// Make a statement. + + const the_symbol = symbol(id); + the_symbol.fud = function () { + token.arity = "statement"; + return f(); + }; + return the_symbol; +} + +function ternary(id1, id2) { + +// Make a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led = function (left) { + const the_token = token; + const second = expression(20); + advance(id2); + token.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, expression(10)]; + if (next_token.id !== ")") { + warn("use_open", the_token); + } + return the_token; + }; + return the_symbol; +} + +// Begin defining the language. + +syntax = empty(); + +symbol("}"); +symbol(")"); +symbol("]"); +symbol(","); +symbol(";"); +symbol(":"); +symbol("*/"); +symbol("async"); +symbol("await"); +symbol("case"); +symbol("catch"); +symbol("class"); +symbol("default"); +symbol("else"); +symbol("enum"); +symbol("finally"); +symbol("implements"); +symbol("interface"); +symbol("package"); +symbol("private"); +symbol("protected"); +symbol("public"); +symbol("static"); +symbol("super"); +symbol("void"); +symbol("yield"); + +constant("(number)", "number"); +constant("(regexp)", "regexp"); +constant("(string)", "string"); +constant("arguments", "object", function () { + warn("unexpected_a", token); + return token; +}); +constant("eval", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("false", "boolean", false); +constant("Function", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("ignore", "undefined", function () { + warn("unexpected_a", token); + return token; +}); +constant("Infinity", "number", Infinity); +constant("isFinite", "function", function () { + warn("expected_a_b", token, "Number.isFinite", "isFinite"); + return token; +}); +constant("isNaN", "function", function () { + warn("number_isNaN", token); + return token; +}); +constant("NaN", "number", NaN); +constant("null", "null", null); +constant("this", "object", function () { + if (!option.this) { + warn("unexpected_a", token); + } + return token; +}); +constant("true", "boolean", true); +constant("undefined", "undefined"); + +assignment("="); +assignment("+="); +assignment("-="); +assignment("*="); +assignment("/="); +assignment("%="); +assignment("&="); +assignment("|="); +assignment("^="); +assignment("<<="); +assignment(">>="); +assignment(">>>="); + +infix("??", 35); +infix("||", 40); +infix("&&", 50); +infix("|", 70); +infix("^", 80); +infix("&", 90); +infix("==", 100); +infix("===", 100); +infix("!=", 100); +infix("!==", 100); +infix("<", 110); +infix(">", 110); +infix("<=", 110); +infix(">=", 110); +infix("in", 110); +infix("instanceof", 110); +infix("<<", 120); +infix(">>", 120); +infix(">>>", 120); +infix("+", 130); +infix("-", 130); +infix("*", 140); +infix("/", 140); +infix("%", 140); +infixr("**", 150); +infix("(", 160, function (left) { + const the_paren = token; + let the_argument; + if (left.id !== "function") { + left_check(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (next_token.id !== ")") { + (function next() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + the_paren.free = true; + if (the_argument.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + the_paren.free = false; + } + return the_paren; +}); +infix(".", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("?.", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("[", 170, function (left) { + const the_token = token; + const the_subscript = expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + const name = survey(the_subscript); + if (rx_identifier.test(name)) { + warn("subscript_a", the_subscript, name); + } + } + left_check(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; +}); +infix("=>", 170, function (left) { + return stop("wrap_parameter", left); +}); + +function do_tick() { + const the_tick = token; + the_tick.value = []; + the_tick.expression = []; + if (next_token.id !== "`") { + (function part() { + advance("(string)"); + the_tick.value.push(token); + if (next_token.id === "${") { + advance("${"); + the_tick.expression.push(expression(0)); + advance("}"); + return part(); + } + }()); + } + advance("`"); + return the_tick; +} + +infix("`", 160, function (left) { + const the_tick = do_tick(); + left_check(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; +}); + +post("++"); +post("--"); +pre("++"); +pre("--"); + +prefix("+"); +prefix("-"); +prefix("~"); +prefix("!"); +prefix("!!"); +prefix("[", function () { + const the_token = token; + the_token.expression = []; + if (next_token.id !== "]") { + (function next() { + let element; + let ellipsis = false; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + element = expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]"); + return the_token; +}); +prefix("/=", function () { + stop("expected_a_b", token, "/\\=", "/="); +}); +prefix("=>", function () { + return stop("expected_a_before_b", token, "()", "=>"); +}); +prefix("new", function () { + const the_new = token; + const right = expression(160); + if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "()", artifact(next_token)); + } + the_new.expression = right; + return the_new; +}); +prefix("typeof"); +prefix("void", function () { + const the_void = token; + warn("unexpected_a", the_void); + the_void.expression = expression(0); + return the_void; +}); + +function parameter_list() { + const list = []; + let optional; + const signature = ["("]; + if (next_token.id !== ")" && next_token.id !== "(end)") { + (function parameter() { + let ellipsis = false; + let param; + if (next_token.id === "{") { + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("{"); + signature.push("{"); + (function subparameter() { + let subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (next_token.id === ":") { + advance(":"); + advance(); + token.label = subparam; + subparam = token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + } + if (next_token.id === "=") { + advance("="); + subparam.expression = expression(); + param.open = true; + } + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return subparameter(); + } + }()); + list.push(param); + advance("}"); + signature.push("}"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else if (next_token.id === "[") { + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("["); + signature.push("[]"); + (function subparameter() { + const subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + if (next_token.id === "=") { + advance("="); + subparam.expression = expression(); + param.open = true; + } + if (next_token.id === ",") { + advance(","); + return subparameter(); + } + }()); + list.push(param); + advance("]"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else { + if (next_token.id === "...") { + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + } + if (!next_token.identifier) { + return stop("expected_identifier_a"); + } + param = next_token; + list.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (next_token.id === "=") { + optional = param; + advance("="); + param.expression = expression(0); + } else { + if (optional !== undefined) { + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } + } + }()); + } + advance(")"); + signature.push(")"); + return [list, signature.join("")]; +} + +function do_function(the_function) { + let name; + if (the_function === undefined) { + the_function = token; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + name = next_token; + enroll(name, "variable", true); + the_function.name = name; + name.init = true; + name.calls = empty(); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + if (next_token.identifier) { + name = next_token; + the_function.name = name; + advance(); + } else { + the_function.name = anon; + } + } + } else { + name = the_function.name; + } + the_function.level = functionage.level + 1; + if (mega_mode) { + warn("unexpected_a", the_function); + } + +// Don't make functions in loops. It is inefficient, and it can lead to scoping +// errors. + + if (functionage.loop > 0) { + warn("function_in_loop", the_function); + } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + the_function.context = empty(); + the_function.finally = 0; + the_function.loop = 0; + the_function.switch = 0; + the_function.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functions.push(the_function); + functionage = the_function; + if (the_function.arity !== "statement" && typeof name === "object") { + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// Parse the parameter list. + + advance("("); + token.free = false; + token.arity = "function"; + [functionage.parameters, functionage.signature] = parameter_list(); + functionage.parameters.forEach(function enroll_parameter(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + name.names.forEach(enroll_parameter); + } + }); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && next_token.line === token.line + ) { + return stop("unexpected_a", next_token); + } + if ( + next_token.id === "." + || next_token.id === "?." + || next_token.id === "[" + ) { + warn("unexpected_a"); + } + +// Restore the previous context. + + functionage = stack.pop(); + return the_function; +} + +function do_async() { + let the_async; + let the_function; + the_async = token; + advance("function"); + the_function = token; + the_function.is_async = true; + the_function.arity = the_async.arity; + do_function(); + if (!the_function.has_await) { + warn("missing_await_statement", the_function); + } + return the_function; +} + +prefix("async", do_async); +prefix("function", do_function); +prefix("await", function () { + let the_await; + the_await = token; + if (!functionage.is_async) { + return stop("unexpected_a", the_await); + } + functionage.has_await = true; + the_await.expression = expression(150); + return the_await; +}); + +function fart(pl) { + advance("=>"); + const the_fart = token; + the_fart.arity = "binary"; + the_fart.name = "=>"; + the_fart.level = functionage.level + 1; + functions.push(the_fart); + if (functionage.loop > 0) { + warn("function_in_loop", the_fart); + } + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + the_fart.context = empty(); + the_fart.finally = 0; + the_fart.loop = 0; + the_fart.switch = 0; + the_fart.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functionage = the_fart; + the_fart.parameters = pl[0]; + the_fart.signature = pl[1]; + the_fart.parameters.forEach(function (name) { + enroll(name, "parameter", true); + }); + if (next_token.id === "{") { + warn("expected_a_b", the_fart, "function", "=>"); + the_fart.block = block("body"); + } else { + the_fart.expression = expression(0); + } + functionage = stack.pop(); + return the_fart; +} + +prefix("(", function () { + const the_paren = token; + let the_value; + const cadet = lookahead().id; + +// We can distinguish between a parameter list for => and a wrapped expression +// with one token of lookahead. + + if ( + next_token.id === ")" + || next_token.id === "..." + || (next_token.identifier && (cadet === "," || cadet === "=")) + ) { + the_paren.free = false; + return fart(parameter_list()); + } + the_paren.free = true; + the_value = expression(0); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + if (next_token.id === "=>") { + if (the_value.arity !== "variable") { + if (the_value.id === "{" || the_value.id === "[") { + warn("expected_a_before_b", the_paren, "function", "("); + return stop("expected_a_b", next_token, "{", "=>"); + } + return stop("expected_identifier_a", the_value); + } + the_paren.expression = [the_value]; + return fart([the_paren.expression, "(" + the_value.id + ")"]); + } + return the_value; +}); +prefix("`", do_tick); +prefix("{", function () { + const the_brace = token; + const seen = empty(); + the_brace.expression = []; + if (next_token.id !== "}") { + (function member() { + let extra; + let full; + let id; + let name = next_token; + let value; + advance(); + if ( + (name.id === "get" || name.id === "set") + && next_token.identifier + ) { + if (!option.getset) { + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + next_token.id; + name = next_token; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (next_token.id === "}" || next_token.id === ",") { + if (typeof extra === "string") { + advance("("); + } + value = expression(Infinity, true); + } else if (next_token.id === "(") { + value = do_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + advance("("); + } + let the_colon = next_token; + advance(":"); + value = expression(0); + if (value.id === name.id && value.id !== "function") { + warn("unexpected_a", the_colon, ": " + name.id); + } + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + advance(":"); + value = expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (next_token.id === ",") { + advance(","); + return member(); + } + }()); + } + advance("}"); + return the_brace; +}); + +stmt(";", function () { + warn("unexpected_a", token); + return token; +}); +stmt("{", function () { + warn("naked_block", token); + return block("naked"); +}); +stmt("async", do_async); +stmt("break", function () { + const the_break = token; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (next_token.identifier && token.line === next_token.line) { + the_label = functionage.context[next_token.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + warn( + (the_label !== undefined && the_label.dead) + ? "out_of_scope_a" + : "not_label_a" + ); + } else { + the_label.used += 1; + } + the_break.label = next_token; + advance(); + } + advance(";"); + return the_break; +}); + +function do_var() { + const the_statement = token; + const is_const = the_statement.id === "const"; + the_statement.names = []; + +// A program may use var or let, but not both. + + if (!is_const) { + if (var_mode === undefined) { + var_mode = the_statement.id; + } else if (the_statement.id !== var_mode) { + warn( + "expected_a_b", + the_statement, + var_mode, + the_statement.id + ); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + warn("var_switch", the_statement); + } + if (functionage.loop > 0 && the_statement.id === "var") { + warn("var_loop", the_statement); + } + (function next() { + if (next_token.id === "{" && the_statement.id !== "var") { + const the_brace = next_token; + advance("{"); + (function pair() { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + survey(name); + advance(); + if (next_token.id === ":") { + advance(":"); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + next_token.label = name; + the_statement.names.push(next_token); + enroll(next_token, "variable", is_const); + advance(); + the_brace.open = true; + } else { + the_statement.names.push(name); + enroll(name, "variable", is_const); + } + name.dead = false; + name.init = true; + if (next_token.id === "=") { + advance("="); + name.expression = expression(); + the_brace.open = true; + } + if (next_token.id === ",") { + advance(","); + return pair(); + } + }()); + advance("}"); + advance("="); + the_statement.expression = expression(0); + } else if (next_token.id === "[" && the_statement.id !== "var") { + const the_bracket = next_token; + advance("["); + (function element() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + advance(); + the_statement.names.push(name); + enroll(name, "variable", is_const); + name.dead = false; + name.init = true; + if (ellipsis) { + name.ellipsis = true; + } else { + if (next_token.id === "=") { + advance("="); + name.expression = expression(); + the_bracket.open = true; + } + if (next_token.id === ",") { + advance(","); + return element(); + } + } + }()); + advance("]"); + advance("="); + the_statement.expression = expression(0); + } else if (next_token.identifier) { + const name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", is_const); + if (next_token.id === "=" || is_const) { + advance("="); + name.dead = false; + name.init = true; + name.expression = expression(0); + } + the_statement.names.push(name); + } else { + return stop("expected_identifier_a", next_token); + } + }()); + semicolon(); + return the_statement; +} + +stmt("const", do_var); +stmt("continue", function () { + const the_continue = token; + if (functionage.loop < 1 || functionage.finally > 0) { + warn("unexpected_a", the_continue); + } + not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; +}); +stmt("debugger", function () { + const the_debug = token; + if (!option.devel) { + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; +}); +stmt("delete", function () { + const the_token = token; + const the_value = expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; +}); +stmt("do", function () { + const the_do = token; + not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; +}); +stmt("export", function () { + const the_export = token; + let the_id; + let the_name; + let the_thing; + + function export_id() { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + the_id = next_token.id; + the_name = global.context[the_id]; + if (the_name === undefined) { + warn("unexpected_a"); + } else { + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a"); + } + exports[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + } + + the_export.expression = []; + if (next_token.id === "default") { + if (exports.default !== undefined) { + warn("duplicate_a"); + } + advance("default"); + the_thing = expression(0); + if ( + the_thing.id !== "(" + || the_thing.expression[0].id !== "." + || the_thing.expression[0].expression.id !== "Object" + || the_thing.expression[0].name.id !== "freeze" + ) { + warn("freeze_exports", the_thing); + } + if (next_token.id === ";") { + semicolon(); + } + exports.default = the_thing; + the_export.expression.push(the_thing); + } else { + if (next_token.id === "function") { + warn("freeze_exports"); + the_thing = statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a", the_name); + } + exports[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + warn("unexpected_a", next_token); + statement(); + } else if (next_token.id === "{") { + advance("{"); + (function loop() { + export_id(); + if (next_token.id === ",") { + advance(","); + return loop(); + } + }()); + advance("}"); + semicolon(); + } else { + stop("unexpected_a"); + } + } + module_mode = true; + return the_export; +}); +stmt("for", function () { + let first; + const the_for = token; + if (!option.for) { + warn("unexpected_a", the_for); + } + not_top_level(the_for); + functionage.loop += 1; + advance("("); + token.free = true; + if (next_token.id === ";") { + return stop("expected_a_b", the_for, "while (", "for (;"); + } + if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + return stop("unexpected_a"); + } + first = expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = expression(0); + advance(";"); + the_for.inc = expression(0); + if (the_for.inc.id === "++") { + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; +}); +stmt("function", do_function); +stmt("if", function () { + let the_else; + const the_if = token; + the_if.expression = condition(); + the_if.block = block(); + if (next_token.id === "else") { + advance("else"); + the_else = token; + the_if.else = ( + next_token.id === "if" + ? statement() + : block() + ); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + the_if.disrupt = true; + } else { + warn("unexpected_a", the_else); + } + } + } + return the_if; +}); +stmt("import", function () { + const the_import = token; + if (next_token.id === "(") { + the_import.arity = "unary"; + the_import.constant = true; + the_import.statement = false; + advance("("); + const string = expression(0); + if (string.id !== "(string)") { + warn("expected_string_a", string); + } + froms.push(token.value); + advance(")"); + advance("."); + advance("then"); + advance("("); + the_import.expression = expression(0); + advance(")"); + semicolon(); + return the_import; + } + let name; + if (typeof module_mode === "object") { + warn("unexpected_directive_a", module_mode, module_mode.directive); + } + module_mode = true; + if (next_token.identifier) { + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + const names = []; + advance("{"); + if (next_token.id !== "}") { + while (true) { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (next_token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token; + if (!rx_module.test(token.value)) { + warn("bad_module_name_a", token); + } + froms.push(token.value); + semicolon(); + return the_import; +}); +stmt("let", do_var); +stmt("return", function () { + const the_return = token; + not_top_level(the_return); + if (functionage.finally > 0) { + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (next_token.id !== ";" && the_return.line === next_token.line) { + the_return.expression = expression(10); + } + advance(";"); + return the_return; +}); +stmt("switch", function () { + let dups = []; + let last; + let stmts; + const the_cases = []; + let the_disrupt = true; + const the_switch = token; + not_top_level(the_switch); + if (functionage.finally > 0) { + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token.free = true; + the_switch.expression = expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + (function major() { + const the_case = next_token; + the_case.arity = "statement"; + the_case.expression = []; + (function minor() { + advance("case"); + token.switch = true; + const exp = expression(0); + if (dups.some(function (thing) { + return are_similar(thing, exp); + })) { + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (next_token.id === "case") { + return minor(); + } + }()); + stmts = statements(); + if (stmts.length < 1) { + warn("expected_statements_a"); + return; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "break;", + artifact(next_token) + ); + } + if (next_token.id === "case") { + return major(); + } + }()); + dups = undefined; + if (next_token.id === "default") { + const the_default = next_token; + advance("default"); + token.switch = true; + advance(":"); + the_switch.else = statements(); + if (the_switch.else.length < 1) { + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + const the_last = the_switch.else[the_switch.else.length - 1]; + if (the_last.id === "break" && the_last.label === undefined) { + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; +}); +stmt("throw", function () { + const the_throw = token; + the_throw.disrupt = true; + the_throw.expression = expression(10); + semicolon(); + if (functionage.try > 0) { + warn("unexpected_a", the_throw); + } + return the_throw; +}); +stmt("try", function () { + let the_catch; + let the_disrupt; + const the_try = token; + if (functionage.try > 0) { + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (next_token.id === "catch") { + let ignored = "ignore"; + the_catch = next_token; + the_try.catch = the_catch; + advance("catch"); + if (next_token.id === "(") { + advance("("); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + if (next_token.id !== "ignore") { + ignored = undefined; + the_catch.name = next_token; + enroll(next_token, "exception", true); + } + advance(); + advance(")"); + } + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "catch", + artifact(next_token) + ); + + } + if (next_token.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; +}); +stmt("var", do_var); +stmt("while", function () { + const the_while = token; + not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; +}); +stmt("with", function () { + stop("unexpected_a", token); +}); + +ternary("?", ":"); + +// Ambulation of the parse tree. + +function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; +} + +function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all of the +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + } + }; +} + +const posts = empty(); +const pres = empty(); +const preaction = action(pres); +const postaction = action(posts); +const preamble = amble(pres); +const postamble = amble(posts); + +function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.id === "function") { + walk_statement(thing.block); + } + if (thing.arity === "pre" || thing.arity === "post") { + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + warn("unexpected_statement_a", thing); + } + postamble(thing); + } + } +} + +function walk_statement(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_statement); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + && thing.id !== "import" + && thing.id !== "await" + ) { + warn("unexpected_expression_a", thing); + } + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + } +} + +function lookup(thing) { + if (thing.arity === "variable") { + +// Look up the variable in the current context. + + let the_variable = functionage.context[thing.id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable === undefined) { + stack.forEach(function (outer) { + const a_variable = outer.context[thing.id]; + if ( + a_variable !== undefined + && a_variable.role !== "label" + ) { + the_variable = a_variable; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (the_variable === undefined) { + if (declared_globals[thing.id] === undefined) { + warn("undeclared_a", thing); + return; + } + the_variable = { + dead: false, + parent: global, + id: thing.id, + init: true, + role: "variable", + used: 0, + writable: false + }; + global.context[thing.id] = the_variable; + } + the_variable.closure = true; + functionage.context[thing.id] = the_variable; + } else if (the_variable.role === "label") { + warn("label_a", thing); + } + if ( + the_variable.dead + && ( + the_variable.calls === undefined + || functionage.name === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + ) { + warn("out_of_scope_a", thing); + } + return the_variable; + } +} + +function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); +} + +function preaction_function(thing) { + if (thing.arity === "statement" && blockage.body !== true) { + warn("unexpected_a", thing); + } + stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); +} + +function bitwise_check(thing) { + if (!option.bitwise && bitwiseop[thing.id] === true) { + warn("unexpected_a", thing); + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + warn("unexpected_a", thing); + } +} + +function pop_block() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); +} + +function activate(name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.init = true; + } + } + blockage.live.push(name); +} + +function action_var(thing) { + thing.names.forEach(activate); +} + +preaction("assignment", bitwise_check); +preaction("binary", bitwise_check); +preaction("binary", function (thing) { + if (relationop[thing.id] === true) { + const left = thing.expression[0]; + const right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + warn("expected_string_a", right); + } + } else { + const value = right.value; + if (value === "null" || value === "undefined") { + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + warn("expected_type_string_a", right, value); + } + } + } + } +}); +preaction("binary", "==", function (thing) { + warn("expected_a_b", thing, "===", "=="); +}); +preaction("binary", "!=", function (thing) { + warn("expected_a_b", thing, "!==", "!="); +}); +preaction("binary", "=>", preaction_function); +preaction("binary", "||", function (thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + warn("and", thang); + } + }); +}); +preaction("binary", "(", function (thing) { + const left = thing.expression[0]; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + const parent = functionage.name.parent; + if (parent) { + const left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + && left_variable.dead + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } +}); +preaction("binary", "in", function (thing) { + warn("infix_in", thing); +}); +preaction("binary", "instanceof", function (thing) { + warn("unexpected_a", thing); +}); +preaction("binary", ".", function (thing) { + if (thing.expression.new) { + thing.new = true; + } +}); +preaction("statement", "{", function (thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; +}); +preaction("statement", "for", function (thing) { + if (thing.name !== undefined) { + const the_variable = lookup(thing.name); + if (the_variable !== undefined) { + the_variable.init = true; + if (!the_variable.writable) { + warn("bad_assignment_a", thing.name); + } + } + } + walk_statement(thing.initial); +}); +preaction("statement", "function", preaction_function); +preaction("unary", "~", bitwise_check); +preaction("unary", "function", preaction_function); +preaction("variable", function (thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } +}); + +function init_variable(name) { + const the_variable = lookup(name); + if (the_variable !== undefined) { + if (the_variable.writable) { + the_variable.init = true; + return; + } + } + warn("bad_assignment_a", name); +} + +postaction("assignment", "+=", function (thing) { + let right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } +}); +postaction("assignment", function (thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + if (thing.id === "=") { + if (thing.names !== undefined) { + if (Array.isArray(thing.names)) { + thing.names.forEach(init_variable); + } else { + init_variable(thing.names); + } + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.writable !== true) { + warn("bad_assignment_a", lvalue); + } + } + const right = syntax[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + warn("unexpected_a", thing.expression[1]); + } + } +}); + +function postaction_function(thing) { + delete functionage.finally; + delete functionage.loop; + delete functionage.switch; + delete functionage.try; + functionage = stack.pop(); + if (thing.wrapped) { + warn("unexpected_parens", thing); + } + return pop_block(); +} + +postaction("binary", function (thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || are_similar(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option.convert) { + if (thing.expression[0].value === "") { + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === "." || thing.id === "?.") { + if (thing.expression.id === "RegExp") { + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } +}); +postaction("binary", "&&", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "||", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "=>", postaction_function); +postaction("binary", "(", function (thing) { + let left = thing.expression[0]; + let the_new; + let arg; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option.eval) { + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + warn( + "expected_a_before_b", + left, + "new", + artifact(left) + ); + } + } + } else if (left.id === ".") { + let cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + cack = !cack; + } + if (rx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + warn("unexpected_a", the_new); + } else { + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + const paren = left.expression; + if (paren.id === "(") { + const array = paren.expression; + if (array.length === 1) { + const new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } +}); +postaction("binary", "[", function (thing) { + if (thing.expression[0].id === "RegExp") { + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + warn("weird_expression_a", thing.expression[1]); + } +}); +postaction("statement", "{", pop_block); +postaction("statement", "const", action_var); +postaction("statement", "export", top_level_only); +postaction("statement", "for", function (thing) { + walk_statement(thing.inc); +}); +postaction("statement", "function", postaction_function); +postaction("statement", "import", function (the_thing) { + const name = the_thing.name; + if (name) { + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return top_level_only(the_thing); + } +}); +postaction("statement", "let", action_var); +postaction("statement", "try", function (thing) { + if (thing.catch !== undefined) { + const the_name = thing.catch.name; + if (the_name !== undefined) { + const the_variable = functionage.context[the_name.id]; + the_variable.dead = false; + the_variable.init = true; + } + walk_statement(thing.catch.block); + } +}); +postaction("statement", "var", action_var); +postaction("ternary", function (thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || are_similar(thing.expression[1], thing.expression[2]) + ) { + warn("unexpected_a", thing); + } else if (are_similar(thing.expression[0], thing.expression[1])) { + warn("expected_a_b", thing, "||", "?"); + } else if (are_similar(thing.expression[0], thing.expression[2])) { + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + warn("wrap_condition", thing.expression[0]); + } +}); +postaction("unary", function (thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option.convert) { + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } +}); +postaction("unary", "function", postaction_function); +postaction("unary", "+", function (thing) { + if (!option.convert) { + warn("expected_a_b", thing, "Number(...)", "+"); + } + const right = thing.expression; + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } +}); + +function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + if (id !== "ignore") { + const name = the_function.context[id]; + if (name.parent === the_function) { + if ( + name.used === 0 + && ( + name.role !== "function" + || name.parent.arity !== "unary" + ) + ) { + warn("unused_a", name); + } else if (!name.init) { + warn("uninitialized_a", name); + } + } + } + }); +} + +function uninitialized_and_unused() { + +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (module_mode === true || option.node) { + delve(global); + } + functions.forEach(delve); +} + +// Go through the token list, looking at usage of whitespace. + +function whitage() { + let closer = "(end)"; + let free = false; + let left = global; + let margin = 0; + let nr_comments_skipped = 0; + let open = true; + let opening = true; + let right; + + function pop() { + const previous = stack.pop(); + closer = previous.closer; + free = previous.free; + margin = previous.margin; + open = previous.open; + opening = previous.opening; + } + + function push() { + stack.push({ + closer, + free, + margin, + open, + opening + }); + } + + function expected_at(at) { + warn( + "expected_a_at_b_c", + right, + artifact(right), + fudge + at, + artifact_column(right) + ); + } + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function no_space() { + if (left.line === right.line) { + if (left.thru !== right.from && nr_comments_skipped === 0) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (open) { + const at = ( + free + ? margin + : margin + 8 + ); + if (right.from < at) { + expected_at(at); + } + } else { + if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line || !open) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (right.from !== margin) { + expected_at(margin); + } + } + } + + stack = []; + tokens.forEach(function (the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + + const new_closer = opener[left.id]; + if (typeof new_closer === "string") { + if (new_closer !== right.id) { + opening = left.open || (left.line !== right.line); + push(); + closer = new_closer; + if (opening) { + free = closer === ")" && left.free; + open = true; + margin += 4; + if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (right.switch) { + at_margin(-4); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + free = false; + open = false; + no_space_only(); + } + } else { + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like '{]') have already been detected. + + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } + } else { + if (right.statement === true) { + if (left.id === "else") { + one_space_only(); + } else { + at_margin(0); + open = false; + } + +// If right is a closer, then pop the previous state. + + } else if (right.id === closer) { + pop(); + if (opening && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-4); + } else if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + one_space(); + } else { + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + at_margin(0); + } else { + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + no_space(); + } else if ( + left.id === "." + || left.id === "?." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + ) { + no_space_only(); + } else if (right.id === "." || right.id === "?.") { + no_space_only(); + } else if (left.id === ";") { + if (open) { + at_margin(0); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || left.id === "await" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + one_space_only(); + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +// The jslint function itself. + +function jslint( + source = "", + option_object = empty(), + global_array = [] +) { + try { + warnings = []; + option = Object.assign(empty(), option_object); + anon = "anonymous"; + block_stack = []; + declared_globals = empty(); + directive_mode = true; + directives = []; + early_stop = true; + exports = empty(); + froms = []; + fudge = ( + option.fudge + ? 1 + : 0 + ); + functions = []; + global = { + id: "(global)", + body: true, + context: empty(), + from: 0, + level: 0, + line: 0, + live: [], + loop: 0, + switch: 0, + thru: 0 + }; + blockage = global; + functionage = global; + json_mode = false; + mega_mode = false; + module_mode = false; + next_token = global; + property = empty(); + shebang = false; + stack = []; + tenure = undefined; + token = global; + token_nr = 0; + var_mode = undefined; + populate(standard, declared_globals, false); + populate(global_array, declared_globals, false); + Object.keys(option).forEach(function (name) { + if (option[name] === true) { + const allowed = allowed_option[name]; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } + }); + tokenize(source); + advance(); + if (json_mode) { + tree = json_value(); + advance("(end)"); + } else { + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option.browser) { + if (next_token.id === ";") { + advance(";"); + } + } else { + +// If we are not in a browser, then the file form of strict pragma may be used. + + if ( + next_token.value === "use strict" + ) { + advance("(string)"); + advance(";"); + } + } + tree = statements(); + advance("(end)"); + functionage = global; + walk_statement(tree); + if (warnings.length === 0) { + uninitialized_and_unused(); + if (!option.white) { + whitage(); + } + } + } + if (!option.browser) { + directives.forEach(function (comment) { + if (comment.directive === "global") { + warn("missing_browser", comment); + } + }); + } + early_stop = false; + } catch (e) { + e.column = e.column || -1; + e.early_stop = true; + e.line = e.line || -1; + e.message = "[JSLint was unable to finish] - " + e.message; + if (e.name !== "JSLintError") { + warnings.push(e); + } + } + +// sort warnings by early_stop first, line, column respectively + + warnings.sort(function (a, b) { + return ( + Boolean(b.early_stop) - Boolean(a.early_stop) + || a.line - b.line || a.column - b.column + ); + +// update each warning with a formatted_message ready for use by cli + + }).map(function ({ + column = 0, + line = 0, + message = "", + stack_trace = "" + }, ii, list) { + column += 1; + line += 1; + list[ii].formatted_message = String( + String(ii + 1).padStart(3, " ") + + " \u001b[31m" + message + "\u001b[39m" + + " \u001b[90m\/\/ line " + line + ", column " + column + + "\u001b[39m\n" + + (" " + String(lines && lines[line - 1]).trim()).slice(0, 72) + + "\n" + stack_trace + ).trim(); + }); + return { + directives, + edition: "v2021.5.23", + exports, + froms, + functions, + global, + id: "(JSLint)", + json: json_mode, + lines, + module: module_mode === true, + ok: warnings.length === 0 && !early_stop, + option, + property, + shebang: ( + shebang + ? lines[0] + : undefined + ), + stop: early_stop, + tokens, + tree, + warnings + }; +} + +async function cli({ + file +}) { +/* + * this function will run jslint from nodejs-cli + */ + const { + readFile, + readdir + } = await import("fs/promises"); + let exitCode; + function string_line_count(code) { + /* + * this function will count number of newlines in + */ + let cnt; + let ii; + // https://jsperf.com/regexp-counting-2/8 + cnt = 0; + ii = 0; + while (true) { + ii = code.indexOf("\n", ii) + 1; + if (ii === 0) { + break; + } + cnt += 1; + } + return cnt; + } + function jslint_from_file({ + code, + file, + line_offset = 0, + warnings = [] + }) { + switch (( + /\.\w+?$|$/m + ).exec(file)[0]) { + case ".html": + // recurse + code.replace(( + /^]*?>\n([\S\s]*?\n)<\/script>$/gm + ), function (ignore, match1, ii) { + jslint_from_file({ + code: match1, + file: file + ". + diff --git a/branch.nomen/jslint.css b/branch.nomen/jslint.css new file mode 100644 index 000000000..cc3bf1696 --- /dev/null +++ b/branch.nomen/jslint.css @@ -0,0 +1,341 @@ +@font-face { + font-family: 'Programma'; + font-weight: bold; + src: url('Programma-Bold.woff2') format('woff2'); +} +@font-face { + font-family: 'Daley'; + font-weight: bold; + src: url('Daley-Bold.woff2') format('woff2'); +} + +body { + background-color: antiquewhite; + color: black; + font-family: 'Daley', sans-serif; + margin: 0; + padding: 0; +} + +#JSLINT_TITLEBOX { + font-family: 'Daley', sans-serif; + margin: 0; + margin-left: 3%; + margin-right: 3%; + padding: 0; +} + +#JSLINT_TITLEBOX ul { + float: right; + font-size: 12pt; +} + +#JSLINT_TITLEBOX div { + color: darkslategray; + float: left; + font-size: 48pt; +} + +#JSLINT_ fieldset { + background-color: gainsboro; + border: 0; + clear: both; + margin-bottom: 1em; + margin-left: 3%; + margin-right: 3%; + margin-top: 1em; + padding: 0; + width: auto; +} +#JSLINT_ legend { + background-color: darkslategray; + border: 0; + color: white; + font-size: 100%; + font-style: normal; + font-weight: normal; + margin: 0; + padding-bottom: 0.25em; + padding-left: 0; + padding-right: 0; + padding-top: 0.25em; + text-align: center; + width: 100%; +} +#JSLINT_ button { + background-color: darkslategray; + border: 0; + color: white; + cursor: pointer; + font-family: 'Daley', sans-serif; + font-size: 100%; + font-style: normal; + margin-left: 1em; + margin-right: 1em; + padding-left: 1em; + padding-right: 1em; + padding-bottom: 0.25em; + padding-top: 0.25em; + text-align: center; +} +#JSLINT_ button:hover { + background-color: slategray; + color: white; + border: 0; +} + +#JSLINT_ button:active { + border: 0; + color: black; +} + +#JSLINT_ button:disabled { + background-color: gray; + border: 0; + color: gainsboro; +} + +a:visited { + color: #69565c; +} + +a:link { + color: black; +} + +#JSLINT_ input[type="text"] { + background-color: white; + border: 0; + color: black; + margin-bottom: 0.2em; + margin-right: 0.5em; + padding-left: 0.5em; + padding-right: 0.5em; + text-align: right; + width: 3em; +} + +#JSLINT_ textarea { + border: 0; + background-color: white; + border: 0; + font-family: Programma, monospace; + font-size: 100%; + font-weight: bold; + resize: none; +} + +#JSLINT_ textarea::selection { + background-color: yellow; + color: black; +} + +#JSLINT_NUMBER { + color: darkgray; + display: inline-block; + height: 4in; + margin: 2%; + margin-right: 0; + overflow: hidden; + padding-right: 0.5%; + text-align: right; + white-space: pre; + width: 4%; +} + +#JSLINT_SOURCE { + color: black; + display: inline-block; + font-size: 100%; + height: 4in; + margin: 2%; + margin-left: 0; + margin-right: 0; + overflow: auto; + padding-left: 0.5%; + white-space: pre; + width: 91%; +} + +#JSLINT_PROPERTY { + background-color: honeydew; + border: 0; + margin: 2%; + padding: 0.5%; + white-space: pre; + width: 95%; +} + +#JSLINT_GLOBAL { + white-space: normal; + width: 95%; +} +#JSLINT_ label { + font-family: sans-serif; + font-size: 90%; + padding-left: 0.25em; +} +#JSLINT_OPTIONS>div { + float: left; + margin: 0.5em; +} + +#JSLINT_ address { + color: black; + display: block; + float: right; + font-family: serif; + font-size: 90%; + margin-left: 1em; +} +#JSLINT_ dl { + background-color: cornsilk; + color: white; + margin: 0; + padding-bottom: 2pt; + padding-left: 1em; + padding-right: 1em; + padding-top: 2pt; +} +#JSLINT_ dfn { + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + margin-bottom: 2pt; +} +#JSLINT_ dt { + color: black; + display: block; + float: left; + font-family: serif; + font-size: 75%; + font-style: italic; + margin: 0; + width: 8em; + text-align: right; +} +#JSLINT_ dd { + color: black; + display: block; + font-family: Programma, monospace; + font-weight: bold; + margin-left: 8em; + padding-bottom: 2pt; +} +#JSLINT_WARNINGS>legend { + background-color: indianred; +} +#JSLINT_WARNINGS>div { + background-color: pink; + padding: 1em; +} +#JSLINT_WARNINGS cite { + color: black; + display: block; + font-family: serif; + font-size: 100%; + font-style: normal; + margin-bottom: 4pt; + margin-left: 20pt; + margin-right: 20pt; + margin-top: 4pt; + overflow-x: hidden; +} +#JSLINT_WARNINGS samp { + background-color: lavenderblush; + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + padding: 4pt; + margin-bottom: 0; + margin-left: 16pt; + margin-right: 16pt; + margin-top: 0; + white-space: pre-wrap; +} + +#JSLINT_REPORT center { + margin-top: 1em; +} + +#JSLINT_WARNINGS dl address { + color: black; + display: inline; + float: none; + font-size: 80%; + margin: 0; +} + +#JSLINT_REPORT>div { + padding: 1em; +} + +#JSLINT_ dl.level0 { + color: black; + background-color: white; +} + +#JSLINT_ dl.level1 { + color: black; + background-color: #ffffe0; /* yellow */ + margin-left: 1em; +} + +#JSLINT_ dl.level2 { + color: black; + background-color: #e0ffe0; /* green */ + margin-left: 2em; +} + +#JSLINT_ dl.level3 { + color: black; + background-color: #D0D0ff; /* blue */ + margin-left: 3em; +} + +#JSLINT_ dl.level4 { + background-color: #ffe0ff; /* purple */ + color: black; + margin-left: 4em; +} + +#JSLINT_ dl.level5 { + background-color: #ffe0e0; /* red */ + color: black; + margin-left: 5em; +} + +#JSLINT_ dl.level6 { + background-color: #ffe390; /* orange */ + color: black; + margin-left: 6em; +} + +#JSLINT_ dl.level7 { + background-color: #e0e0e0; /* gray */ + color: black; + margin-left: 7em; +} + +#JSLINT_ dl.level8 { + color: black; + margin-left: 8em; +} + +#JSLINT_ dl.level9 { + color: black; + margin-left: 9em; +} + +.none { + display: none; +} +.center { + text-align: center; +} diff --git a/branch.nomen/jslint.js b/branch.nomen/jslint.js new file mode 100644 index 000000000..8456730e7 --- /dev/null +++ b/branch.nomen/jslint.js @@ -0,0 +1,4991 @@ +// jslint.js +// 2020-03-18 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// jslint(source, option_object, global_array) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze, a string or an array of strings. +// option_object An object whose keys correspond to option names. +// global_array An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all of the functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// 1. If the source is a single string, split it into an array of strings. +// 2. Turn the source into an array of tokens. +// 3. Furcate the tokens into a parse tree. +// 4. Walk the tree, traversing all of the nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// 5. Check the whitespace between the tokens. + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*property + a, and, arity, assign, b, bad_assignment_a, bad_directive_a, bad_get, + bad_module_name_a, bad_option_a, bad_property_a, bad_set, bitwise, block, + body, browser, c, calls, catch, charCodeAt, closer, closure, code, column, + concat, constant, context, convert, couch, create, d, dead, default, devel, + directive, directives, disrupt, dot, duplicate_a, edition, ellipsis, else, + empty_block, escape_mega, eval, every, expected_a, expected_a_at_b_c, + expected_a_b, expected_a_b_from_c_d, expected_a_before_b, + expected_a_next_at_b, expected_digits_after_a, expected_four_digits, + expected_identifier_a, expected_line_break_a_b, expected_regexp_factor_a, + expected_space_a_b, expected_statements_a, expected_string_a, + expected_type_string_a, exports, expression, extra, finally, flag, for, + forEach, free, freeze, freeze_exports, from, froms, fud, fudge, + function_in_loop, functions, g, getset, global, i, id, identifier, import, + inc, indexOf, infix_in, init, initial, isArray, isNaN, join, json, keys, + label, label_a, lbp, led, length, level, line, lines, live, long, loop, m, + margin, match, message, misplaced_a, misplaced_directive_a, missing_browser, + missing_m, module, naked_block, name, names, nested_comment, new, node, + not_label_a, nr, nud, number_isNaN, ok, open, opening, option, + out_of_scope_a, parameters, parent, pop, property, push, quote, + redefinition_a_b, replace, required_a_optional_b, reserved_a, role, search, + shebang, signature, single, slice, some, sort, split, startsWith, statement, + stop, subscript_a, switch, test, this, thru, toString, todo_comment, + tokens, too_long, too_many_digits, tree, try, type, u, unclosed_comment, + unclosed_mega, unclosed_string, undeclared_a, unexpected_a, + unexpected_a_after_b, unexpected_a_before_b, unexpected_at_top_level_a, + unexpected_char_a, unexpected_comment, unexpected_directive_a, + unexpected_expression_a, unexpected_label_a, unexpected_parens, + unexpected_space_a_b, unexpected_statement_a, unexpected_trailing_space, + unexpected_typeof_a, uninitialized_a, unreachable_a, + unregistered_property_a, unsafe, unused_a, use_double, use_open, use_spaces, + used, value, var_loop, var_switch, variable, warning, warnings, + weird_condition_a, weird_expression_a, weird_loop, weird_relation_a, white, + wrap_condition, wrap_immediate, wrap_parameter, wrap_regexp, wrap_unary, + wrapped, writable, y +*/ + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than '{}' because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +function populate(array, object = empty(), value = true) { + +// Augment an object by taking property names from an array of strings. + + array.forEach(function (name) { + object[name] = value; + }); + return object; +} + +const allowed_option = { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + bitwise: true, + browser: [ + "caches", "CharacterData", "clearInterval", "clearTimeout", "document", + "DocumentType", "DOMException", "Element", "Event", "event", "fetch", + "FileReader", "FontFace", "FormData", "history", "IntersectionObserver", + "localStorage", "location", "MutationObserver", "name", "navigator", + "screen", "sessionStorage", "setInterval", "setTimeout", "Storage", + "TextDecoder", "TextEncoder", "URL", "window", "Worker", + "XMLHttpRequest" + ], + couch: [ + "emit", "getRow", "isArray", "log", "provides", "registerType", + "require", "send", "start", "sum", "toJSON" + ], + convert: true, + devel: [ + "alert", "confirm", "console", "prompt" + ], + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + node: [ + "Buffer", "clearImmediate", "clearInterval", "clearTimeout", + "console", "exports", "module", "process", "require", + "setImmediate", "setInterval", "setTimeout", "TextDecoder", + "TextEncoder", "URL", "URLSearchParams", "__dirname", "__filename" + ], + nomen: true, + single: true, + this: true, + white: true +}; + +const anticondition = populate([ + "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%", + "typeof", "(number)", "(string)" +]); + +// These are the bitwise operators. + +const bitwiseop = populate([ + "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=", + ">>>", ">>>=" +]); + +const escapeable = populate([ + "\\", "/", "`", "b", "f", "n", "r", "t" +]); + +const opener = { + +// The open and close pairs. + + "(": ")", // paren + "[": "]", // bracket + "{": "}", // brace + "${": "}" // mega +}; + +// The relational operators. + +const relationop = populate([ + "!=", "!==", "==", "===", "<", "<=", ">", ">=" +]); + +// This is the set of infix operators that require a space on each side. + +const spaceop = populate([ + "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/", + "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=", + ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" +]); + +const standard = [ + +// These are the globals that are provided by the language standard. + + "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI", + "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error", + "EvalError", "Float32Array", "Float64Array", "Generator", + "GeneratorFunction", "Int8Array", "Int16Array", "Int32Array", "Intl", + "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat", + "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", + "Set", "String", "Symbol", "SyntaxError", "System", "TypeError", + "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", + "URIError", "WeakMap", "WeakSet" +]; + +const bundle = { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + and: "The '&&' subexpression should be wrapped in parens.", + bad_assignment_a: "Bad assignment to '{a}'.", + bad_directive_a: "Bad directive '{a}'.", + bad_get: "A get function takes no parameters.", + bad_module_name_a: "Bad module name '{a}'.", + bad_option_a: "Bad option '{a}'.", + bad_property_a: "Bad property name '{a}'.", + bad_set: "A set function takes one parameter.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + escape_mega: "Unexpected escapement in mega literal.", + expected_a: "Expected '{a}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: ( + "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'." + ), + expected_a_before_b: "Expected '{a}' before '{b}'.", + expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.", + expected_digits_after_a: "Expected digits after '{a}'.", + expected_four_digits: "Expected four digits after '\\u'.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.", + expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.", + expected_space_a_b: "Expected one space between '{a}' and '{b}'.", + expected_statements_a: "Expected statements before '{a}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + expected_type_string_a: "Expected a type string and instead saw '{a}'.", + freeze_exports: ( + "Expected 'Object.freeze('. All export values should be frozen." + ), + function_in_loop: "Don't make functions within a loop.", + infix_in: ( + "Unexpected 'in'. Compare with undefined, " + + "or use the hasOwnProperty method instead." + ), + label_a: "'{a}' is a statement label.", + misplaced_a: "Place '{a}' at the outermost level.", + misplaced_directive_a: ( + "Place the '/*{a}*/' directive before the first statement." + ), + missing_browser: "/*global*/ requires the Assume a browser option.", + missing_m: "Expected 'm' flag on a multiline regular expression.", + naked_block: "Naked block.", + nested_comment: "Nested comment.", + not_label_a: "'{a}' is not a label.", + number_isNaN: "Use Number.isNaN function to compare with NaN.", + out_of_scope_a: "'{a}' is out of scope.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + required_a_optional_b: ( + "Required parameter '{a}' after optional parameter '{b}'." + ), + reserved_a: "Reserved name '{a}'.", + subscript_a: "['{a}'] is better written in dot notation.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line is longer than 80 characters.", + too_many_digits: "Too many digits.", + unclosed_comment: "Unclosed comment.", + unclosed_mega: "Unclosed mega literal.", + unclosed_string: "Unclosed string.", + undeclared_a: "Undeclared '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_a_after_b: "Unexpected '{a}' after '{b}'.", + unexpected_a_before_b: "Unexpected '{a}' before '{b}'.", + unexpected_at_top_level_a: "Expected '{a}' to be in a function.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_directive_a: "When using modules, don't use directive '/*{a}'.", + unexpected_expression_a: ( + "Unexpected expression '{a}' in statement position." + ), + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_parens: "Don't wrap function literals in parens.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_statement_a: ( + "Unexpected statement '{a}' in expression position." + ), + unexpected_trailing_space: "Unexpected trailing space.", + unexpected_typeof_a: ( + "Unexpected 'typeof'. Use '===' to compare directly with {a}." + ), + uninitialized_a: "Uninitialized '{a}'.", + unreachable_a: "Unreachable '{a}'.", + unregistered_property_a: "Unregistered property name '{a}'.", + unsafe: "Unsafe character '{a}'.", + unused_a: "Unused '{a}'.", + use_double: "Use double quotes, not single quotes.", + use_open: ( + "Wrap a ternary expression in parens, " + + "with a line break after the left paren." + ), + use_spaces: "Use spaces, not tabs.", + var_loop: "Don't declare variables in a loop.", + var_switch: "Don't declare variables in a switch.", + weird_condition_a: "Weird condition '{a}'.", + weird_expression_a: "Weird expression '{a}'.", + weird_loop: "Weird loop.", + weird_relation_a: "Weird relation '{a}'.", + wrap_condition: "Wrap the condition in parens.", + wrap_immediate: ( + "Wrap an immediate function invocation in parentheses to assist " + + "the reader in understanding that the expression is the result " + + "of a function, and not the function itself." + ), + wrap_parameter: "Wrap the parameter in parens.", + wrap_regexp: "Wrap this regexp in parens to avoid confusion.", + wrap_unary: "Wrap the unary expression in parens." +}; + +// Regular expression literals: + +// supplant {variables} +const rx_supplant = /\{([^{}]*)\}/g; +// carriage return, carriage return linefeed, or linefeed +const rx_crlf = /\n|\r\n?/; +// unsafe characters that are silently deleted by one or more browsers +const rx_unsafe = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; +// identifier +const rx_identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; +const rx_module = /^[a-zA-Z0-9_$:.@\-\/]+$/; +const rx_bad_property = /^_|\$|Sync\$|_$/; +// star slash +const rx_star_slash = /\*\//; +// slash star +const rx_slash_star = /\/\*/; +// slash star or ending slash +const rx_slash_star_or_slash = /\/\*|\/$/; +// uncompleted work comment +const rx_todo = /\b(?:todo|TO\s?DO|HACK)\b/; +// tab +const rx_tab = /\t/g; +// directive +const rx_directive = /^(jslint|property|global)\s+(.*)$/; +const rx_directive_part = /^([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*(.*)$/; +// token (sorry it is so long) +const rx_token = /^((\s+)|([a-zA-Z_$][a-zA-Z0-9_$]*)|[(){}\[\],:;'"~`]|\?\.?|=(?:==?|>)?|\.+|[*\/][*\/=]?|\+[=+]?|-[=\-]?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<= "a" && string <= "z\uffff") + || (string >= "A" && string <= "Z\uffff") + ); +} + +function supplant(string, object) { + return string.replace(rx_supplant, function (found, filling) { + const replacement = object[filling]; + return ( + replacement !== undefined + ? replacement + : found + ); + }); +} + +let anon; // The guessed name for anonymous functions. +let blockage; // The current block. +let block_stack; // The stack of blocks. +let declared_globals; // The object containing the global declarations. +let directives; // The directive comments. +let directive_mode; // true if directives are still allowed. +let early_stop; // true if JSLint cannot finish. +let exports; // The exported names and values. +let froms; // The array collecting all import-from strings. +let fudge; // true if the natural numbers start with 1. +let functionage; // The current function. +let functions; // The array containing all of the functions. +let global; // The global object; the outermost context. +let json_mode; // true if parsing JSON. +let lines; // The array containing source lines. +let mega_mode; // true if currently parsing a megastring literal. +let module_mode; // true if import or export was used. +let next_token; // The next token to be examined in the parse. +let option; // The options parameter. +let property; // The object containing the tallied property names. +let shebang; // true if a #! was seen on the first line. +let stack; // The stack of functions. +let syntax; // The object containing the parser. +let token; // The current token being examined in the parse. +let token_nr; // The number of the next token. +let tokens; // The array of tokens. +let tenure; // The predefined property registry. +let tree; // The abstract parse tree. +let var_mode; // "var" if using var; "let" if using let. +let warnings; // The array collecting all generated warnings. + +// Error reportage functions: + +function artifact(the_token) { + +// Return a string representing an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); +} + +function artifact_line(the_token) { + +// Return the fudged line number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.line + fudge; +} + +function artifact_column(the_token) { + +// Return the fudged column number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.from + fudge; +} + +function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + const warning = { // ~~ + name: "JSLintError", + column, + line, + code + }; + if (a !== undefined) { + warning.a = a; + } + if (b !== undefined) { + warning.b = b; + } + if (c !== undefined) { + warning.c = c; + } + if (d !== undefined) { + warning.d = d; + } + warning.message = supplant(bundle[code] || code, warning); + warnings.push(warning); + return warning; +} + +function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); +} + +function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + if (the_token.warning === undefined) { + the_token.warning = warn_at( + code, + the_token.line, + the_token.from, + a || artifact(the_token), + b, + c, + d + ); + return the_token.warning; + } +} + +function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); +} + +// Tokenize: + +function tokenize(source) { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + +// If the source is not an array, then it is split into lines at the +// carriage return/linefeed. + + lines = ( + Array.isArray(source) + ? source + : source.split(rx_crlf) + ); + tokens = []; + + let char; // a popular character + let column = 0; // the column number of the next character + let first; // the first token + let from; // the starting column number of the token + let line = -1; // the line number of the next character + let nr = 0; // the next token number + let previous = global; // the previous token including comments + let prior = global; // the previous token excluding comments + let mega_from; // the starting column of megastring + let mega_line; // the starting line of megastring + let regexp_seen; // regular expression literal seen on this line + let snippet; // a piece of string + let source_line = ""; // the remaining line source string + let whole_line = ""; // the whole line source string + + if (lines[0].startsWith("#!")) { + line = 0; + shebang = true; + } + + function next_line() { + +// Put the next line of source in source_line. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + let at; + if ( + !option.long + && whole_line.length > 80 + && !json_mode + && first + && !regexp_seen + ) { + warn_at("too_long", line, 80); + } + column = 0; + line += 1; + regexp_seen = false; + source_line = lines[line]; + whole_line = source_line || ""; + if (source_line !== undefined) { + at = source_line.search(rx_tab); + if (at >= 0) { + if (!option.white) { + warn_at("use_spaces", line, at + 1); + } + source_line = source_line.replace(rx_tab, " "); + } + at = source_line.search(rx_unsafe); + if (at >= 0) { + warn_at( + "unsafe", + line, + column + at, + "U+" + source_line.charCodeAt(at).toString(16) + ); + } + if (!option.white && source_line.slice(-1) === " ") { + warn_at( + "unexpected_trailing_space", + line, + source_line.length - 1 + ); + } + } + return source_line; + } + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions snip, next_char, prev_char, +// some_digits, and escape help in the parsing of literals. + + function snip() { + +// Remove the last character from snippet. + + snippet = snippet.slice(0, -1); + } + + function next_char(match) { + +// Get the next character from the source line. Remove it from the source_line, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + return stop_at( + ( + char === "" + ? "expected_a" + : "expected_a_b" + ), + line, + column - 1, + match, + char + ); + } + if (source_line) { + char = source_line[0]; + source_line = source_line.slice(1); + snippet += char; + } else { + char = ""; + snippet += " "; + } + column += 1; + return char; + } + + function back_char() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the source_line. + + if (snippet) { + char = snippet.slice(-1); + source_line = char + source_line; + column -= 1; + snip(); + } else { + char = ""; + } + return char; + } + + function some_digits(rx, quiet) { + const result = source_line.match(rx); + if (result) { + char = result[1]; + column += char.length; + source_line = result[2]; + snippet += char; + } else { + char = ""; + if (!quiet) { + warn_at( + "expected_digits_after_a", + line, + column, + snippet + ); + } + } + return char.length; + } + + function escape(extra) { + next_char("\\"); + if (escapeable[char] === true) { + return next_char(); + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "u") { + if (next_char("u") === "{") { + if (json_mode) { + warn_at("unexpected_a", line, column - 1, char); + } + if (some_digits(rx_hexs) > 5) { + warn_at("too_many_digits", line, column - 1); + } + if (next_char() !== "}") { + stop_at("expected_a_before_b", line, column, "}", char); + } + return next_char(); + } + back_char(); + if (some_digits(rx_hexs, true) < 4) { + warn_at("expected_four_digits", line, column - 1); + } + return; + } + if (extra && extra.indexOf(char) >= 0) { + return next_char(); + } + warn_at("unexpected_a_before_b", line, column - 2, "\\", char); + } + + function make(id, value, identifier) { + +// Make the token object and append it to the tokens list. + + const the_token = { + from, + id, + identifier: Boolean(identifier), + line, + nr, + thru: column + }; + tokens[nr] = the_token; + nr += 1; + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + directive_mode = false; + } + +// If the token is to have a value, give it one. + + if (value !== undefined) { + the_token.value = value; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option.white. + + if ( + previous.line === line + && previous.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (previous.id === "(comment)" || previous.id === "(regexp)") + ) { + warn( + "expected_space_a_b", + the_token, + artifact(previous), + artifact(the_token) + ); + } + if (previous.id === "." && id === "(number)") { + warn("expected_a_before_b", previous, "0", "."); + } + if (prior.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// The previous token is used to detect adjacency problems. + + previous = the_token; + +// The prior token is a previous token that was not a comment. The prior token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (previous.id !== "(comment)") { + prior = previous; + } + return the_token; + } + + function parse_directive(the_comment, body) { + +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + + const result = body.match(rx_directive_part); + if (result) { + let allowed; + const name = result[1]; + const value = result[2]; + if (the_comment.directive === "jslint") { + allowed = allowed_option[name]; + if ( + typeof allowed === "boolean" + || typeof allowed === "object" + ) { + if ( + value === "" + || value === "true" + || value === undefined + ) { + option[name] = true; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } else if (value === "false") { + option[name] = false; + } else { + warn("bad_option_a", the_comment, name + ":" + value); + } + } else { + warn("bad_option_a", the_comment, name); + } + } else if (the_comment.directive === "property") { + if (tenure === undefined) { + tenure = empty(); + } + tenure[name] = true; + } else if (the_comment.directive === "global") { + if (value) { + warn("bad_option_a", the_comment, name + ":" + value); + } + declared_globals[name] = false; + module_mode = the_comment; + } + return parse_directive(the_comment, result[3]); + } + if (body) { + return stop("bad_directive_a", the_comment, body); + } + } + + function comment(snippet) { + +// Make a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + + const the_comment = make("(comment)", snippet); + if (Array.isArray(snippet)) { + snippet = snippet.join(" "); + } + if (!option.devel && rx_todo.test(snippet)) { + warn("todo_comment", the_comment); + } + const result = snippet.match(rx_directive); + if (result) { + if (!directive_mode) { + warn_at("misplaced_directive_a", line, from, result[1]); + } else { + the_comment.directive = result[1]; + parse_directive(the_comment, result[2]); + } + directives.push(the_comment); + } + return the_comment; + } + + function regexp() { + +// Parse a regular expression literal. + + let multi_mode = false; + let result; + let value; + regexp_seen = true; + + function quantifier() { + +// Match an optional quantifier. + + if (char === "?" || char === "*" || char === "+") { + next_char(); + } else if (char === "{") { + if (some_digits(rx_digits, true) === 0) { + warn_at("expected_a", line, column, "0"); + } + if (next_char() === ",") { + some_digits(rx_digits, true); + next_char(); + } + next_char("}"); + } else { + return; + } + if (char === "?") { + next_char("?"); + } + } + + function subklass() { + +// Match a character in a character class. + + if (char === "\\") { + escape("BbDdSsWw-[]^"); + return true; + } + if ( + char === "" + || char === "[" + || char === "]" + || char === "/" + || char === "^" + || char === "-" + ) { + return false; + } + if (char === " ") { + warn_at("expected_a_b", line, column, "\\u0020", " "); + } else if (char === "`" && mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char(); + return true; + } + + function ranges() { + +// Match a range of subclasses. + + if (subklass()) { + if (char === "-") { + next_char("-"); + if (!subklass()) { + return stop_at( + "unexpected_a", + line, + column - 1, + "-" + ); + } + } + return ranges(); + } + } + + function klass() { + +// Match a class. + + next_char("["); + if (char === "^") { + next_char("^"); + } + (function classy() { + ranges(); + if (char !== "]" && char !== "") { + warn_at( + "expected_a_before_b", + line, + column, + "\\", + char + ); + next_char(); + return classy(); + } + }()); + next_char("]"); + } + + function choice() { + + function group() { + +// Match a group that starts with left paren. + + next_char("("); + if (char === "?") { + next_char("?"); + if (char === "=" || char === "!") { + next_char(); + } else { + next_char(":"); + } + } else if (char === ":") { + warn_at("expected_a_before_b", line, column, "?", ":"); + } + choice(); + next_char(")"); + } + + function factor() { + if ( + char === "" + || char === "/" + || char === "]" + || char === ")" + ) { + return false; + } + if (char === "(") { + group(); + return true; + } + if (char === "[") { + klass(); + return true; + } + if (char === "\\") { + escape("BbDdSsWw^${}[]():=!.|*+?"); + return true; + } + if ( + char === "?" + || char === "+" + || char === "*" + || char === "}" + || char === "{" + ) { + warn_at( + "expected_a_before_b", + line, + column - 1, + "\\", + char + ); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column - 1, "`"); + } + } else if (char === " ") { + warn_at( + "expected_a_b", + line, + column - 1, + "\\s", + " " + ); + } else if (char === "$") { + if (source_line[0] !== "/") { + multi_mode = true; + } + } else if (char === "^") { + if (snippet !== "^") { + multi_mode = true; + } + } + next_char(); + return true; + } + + function sequence(follow) { + if (factor()) { + quantifier(); + return sequence(true); + } + if (!follow) { + warn_at("expected_regexp_factor_a", line, column, char); + } + } + +// Match a choice (a sequence that can be followed by | and another choice). + + sequence(); + if (char === "|") { + next_char("|"); + return choice(); + } + } + +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + next_char(); + if (char === "=") { + warn_at("expected_a_before_b", line, column, "\\", "="); + } + choice(); + +// Make sure there is a closing slash. + + snip(); + value = snippet; + next_char("/"); + +// Process dangling flag letters. + + const allowed = { + g: true, + i: true, + m: true, + u: true, + y: true + }; + const flag = empty(); + (function make_flag() { + if (is_letter(char)) { + if (allowed[char] !== true) { + warn_at("unexpected_a", line, column, char); + } + allowed[char] = false; + flag[char] = true; + next_char(); + return make_flag(); + } + }()); + back_char(); + if (char === "/" || char === "*") { + return stop_at("unexpected_a", line, from, char); + } + result = make("(regexp)", char); + result.flag = flag; + result.value = value; + if (multi_mode && !flag.m) { + warn_at("missing_m", line, column); + } + return result; + } + + function string(quote) { + +// Make a string token. + + let the_token; + snippet = ""; + next_char(); + + return (function next() { + if (char === quote) { + snip(); + the_token = make("(string)", snippet); + the_token.quote = quote; + return the_token; + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "\\") { + escape(quote); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char("`"); + } else { + next_char(); + } + return next(); + }()); + } + + function frack() { + if (char === ".") { + some_digits(rx_digits); + next_char(); + } + if (char === "E" || char === "e") { + next_char(); + if (char !== "+" && char !== "-") { + back_char(); + } + some_digits(rx_digits); + next_char(); + } + } + + function number() { + if (snippet === "0") { + next_char(); + if (char === ".") { + frack(); + } else if (char === "b") { + some_digits(rx_bits); + next_char(); + } else if (char === "o") { + some_digits(rx_octals); + next_char(); + } else if (char === "x") { + some_digits(rx_hexs); + next_char(); + } + } else { + next_char(); + frack(); + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + return stop_at( + "unexpected_a_after_b", + line, + column - 1, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + back_char(); + return make("(number)", snippet); + } + + function lex() { + let array; + let i = 0; + let j = 0; + let last; + let result; + let the_token; + +// This should properly be a tail recursive function, but sadly, conformant +// implementations of ES6 are still rare. This is the ideal code: + +// if (!source_line) { +// source_line = next_line(); +// from = 0; +// return ( +// source_line === undefined +// ? ( +// mega_mode +// ? stop_at("unclosed_mega", mega_line, mega_from) +// : make("(end)") +// ) +// : lex() +// ); +// } + +// Unfortunately, incompetent JavaScript engines will sometimes fail to execute +// it correctly. So for now, we do it the old fashioned way. + + while (!source_line) { + source_line = next_line(); + from = 0; + if (source_line === undefined) { + return ( + mega_mode + ? stop_at("unclosed_mega", mega_line, mega_from) + : make("(end)") + ); + } + } + + from = column; + result = source_line.match(rx_token); + +// result[1] token +// result[2] whitespace +// result[3] identifier +// result[4] number +// result[5] rest + + if (!result) { + return stop_at( + "unexpected_char_a", + line, + column, + source_line[0] + ); + } + + snippet = result[1]; + column += snippet.length; + source_line = result[5]; + +// Whitespace was matched. Call lex again to get more. + + if (result[2]) { + return lex(); + } + +// The token is an identifier. + + if (result[3]) { + return make(snippet, undefined, true); + } + +// The token is a number. + + if (result[4]) { + return number(snippet); + } + +// The token is a string. + + if (snippet === "\"") { + return string(snippet); + } + if (snippet === "'") { + if (!option.single) { + warn_at("use_double", line, column); + } + return string(snippet); + } + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (snippet === "`") { + if (mega_mode) { + return stop_at("expected_a_b", line, column, "}", "`"); + } + snippet = ""; + mega_from = from; + mega_line = line; + mega_mode = true; + +// Parsing a mega literal is tricky. First make a ` token. + + make("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + (function part() { + const at = source_line.search(rx_mega); + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + if (at < 0) { + snippet += source_line + "\n"; + return ( + next_line() === undefined + ? stop_at("unclosed_mega", mega_line, mega_from) + : part() + ); + } + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + snippet += source_line.slice(0, at); + column += at; + source_line = source_line.slice(at); + if (source_line[0] === "\\") { + stop_at("escape_mega", line, at); + } + make("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then make tokens that will become part of an expression until +// a } token is made. + + if (source_line[0] === "$") { + column += 2; + make("${"); + source_line = source_line.slice(2); + (function expr() { + const id = lex().id; + if (id === "{") { + return stop_at( + "expected_a_b", + line, + column, + "}", + "{" + ); + } + if (id !== "}") { + return expr(); + } + }()); + return part(); + } + }()); + source_line = source_line.slice(1); + column += 1; + mega_mode = false; + return make("`"); + } + +// The token is a // comment. + + if (snippet === "//") { + snippet = source_line; + source_line = ""; + the_token = comment(snippet); + if (mega_mode) { + warn("unexpected_comment", the_token, "`"); + } + return the_token; + } + +// The token is a /* comment. + + if (snippet === "/*") { + array = []; + if (source_line[0] === "/") { + warn_at("unexpected_a", line, column + i, "/"); + } + (function next() { + if (source_line > "") { + i = source_line.search(rx_star_slash); + if (i >= 0) { + return; + } + j = source_line.search(rx_slash_star); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + } + array.push(source_line); + source_line = next_line(); + if (source_line === undefined) { + return stop_at("unclosed_comment", line, column); + } + return next(); + }()); + snippet = source_line.slice(0, i); + j = snippet.search(rx_slash_star_or_slash); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + array.push(snippet); + column += i + 2; + source_line = source_line.slice(i + 2); + return comment(array); + } + +// The token is a slash. + + if (snippet === "/") { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + if (prior.identifier) { + if (!prior.dot) { + if (prior.id === "return") { + return regexp(); + } + if ( + prior.id === "(begin)" + || prior.id === "case" + || prior.id === "delete" + || prior.id === "in" + || prior.id === "instanceof" + || prior.id === "new" + || prior.id === "typeof" + || prior.id === "void" + || prior.id === "yield" + ) { + the_token = regexp(); + return stop("unexpected_a", the_token); + } + } + } else { + last = prior.id[prior.id.length - 1]; + if ("(,=:?[".indexOf(last) >= 0) { + return regexp(); + } + if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) { + the_token = regexp(); + warn("wrap_regexp", the_token); + return the_token; + } + } + if (source_line[0] === "/") { + column += 1; + source_line = source_line.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + } + return make(snippet); + } + + first = lex(); + json_mode = first.id === "{" || first.id === "["; + +// This loop will be replaced with a recursive call to lex when ES6 has been +// finished and widely deployed and adopted. + + while (true) { + if (lex().id === "(end)") { + break; + } + } +} + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + +function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!rx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!rx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property[id] === "number") { + property[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (tenure !== undefined) { + if (tenure[id] !== true) { + warn("unregistered_property_a", name); + } + } else { + if (!option.nomen && name.identifier && rx_bad_property.test(id)) { + warn("bad_property_a", name); + } + } + property[id] = 1; + } + return id; +} + +function dispense() { + +// Deliver the next token, skipping the comments. + + const cadet = tokens[token_nr]; + token_nr += 1; + if (cadet.id === "(comment)") { + if (json_mode) { + warn("unexpected_a", cadet); + } + return dispense(); + } else { + return cadet; + } +} + +function lookahead() { + +// Look ahead one token without advancing. + + const old_token_nr = token_nr; + const cadet = dispense(true); + token_nr = old_token_nr; + return cadet; +} + +function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if (token.identifier && token.id !== "function") { + anon = token.id; + } else if (token.id === "(string)" && rx_identifier.test(token.value)) { + anon = token.value; + } + +// Attempt to match next_token with an expected id. + + if (id !== undefined && next_token.id !== id) { + return ( + match === undefined + ? stop("expected_a_b", next_token, id, artifact()) + : stop( + "expected_a_b_from_c_d", + next_token, + id, + artifact(match), + artifact_line(match), + artifact(next_token) + ) + ); + } + +// Promote the tokens, skipping comments. + + token = next_token; + next_token = dispense(); + if (next_token.id === "(end)") { + token_nr -= 1; + } +} + +// Parsing of JSON is simple: + +function json_value() { + let negative; + if (next_token.id === "{") { + return (function json_object() { + const brace = next_token; + const object = empty(); + const properties = []; + brace.expression = properties; + advance("{"); + if (next_token.id !== "}") { + (function next() { + let name; + let value; + if (next_token.quote !== "\"") { + warn( + "unexpected_a", + next_token, + next_token.quote + ); + } + name = next_token; + advance("(string)"); + if (object[token.value] !== undefined) { + warn("duplicate_a", token); + } else if (token.value === "__proto__") { + warn("bad_property_a", token); + } else { + object[token.value] = token; + } + advance(":"); + value = json_value(); + value.label = name; + properties.push(value); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("}", brace); + return brace; + }()); + } + if (next_token.id === "[") { + return (function json_array() { + const bracket = next_token; + const elements = []; + bracket.expression = elements; + advance("["); + if (next_token.id !== "]") { + (function next() { + elements.push(json_value()); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]", bracket); + return bracket; + }()); + } + if ( + next_token.id === "true" + || next_token.id === "false" + || next_token.id === "null" + ) { + advance(); + return token; + } + if (next_token.id === "(number)") { + if (!rx_JSON_number.test(next_token.value)) { + warn("unexpected_a"); + } + advance(); + return token; + } + if (next_token.id === "(string)") { + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + advance(); + return token; + } + if (next_token.id === "-") { + negative = next_token; + negative.arity = "unary"; + advance("-"); + advance("(number)"); + negative.expression = token; + return negative; + } + stop("unexpected_a"); +} + +// Now we parse JavaScript. + +function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + const id = name.id; + +// Reserved words may not be enrolled. + + if (syntax[id] !== undefined && id !== "ignore") { + warn("reserved_a", name); + } else { + +// Has the name been enrolled in this context? + + let earlier = functionage.context[id]; + if (earlier) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + +// Has the name been enrolled in an outer context? + + } else { + stack.forEach(function (value) { + const item = value.context[id]; + if (item !== undefined) { + earlier = item; + } + }); + if (earlier) { + if (id === "ignore") { + if (earlier.role === "variable") { + warn("unexpected_a", name); + } + } else { + if ( + ( + role !== "exception" + || earlier.role !== "exception" + ) + && role !== "parameter" + && role !== "function" + ) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + } + } + } + +// Enroll it. + + functionage.context[id] = name; + name.dead = true; + name.parent = functionage; + name.init = false; + name.role = role; + name.used = 0; + name.writable = !readonly; + } + } +} + +function expression(rbp, initial) { + +// This is the heart of the Pratt parser. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// nud Null denotation +// led Left denotation +// lbp Left binding power +// rbp Right binding power + +// It processes a nud (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop. It +// returns the expression's parse tree. + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement, + + if (!initial) { + advance(); + } + the_symbol = syntax[token.id]; + if (the_symbol !== undefined && the_symbol.nud !== undefined) { + left = the_symbol.nud(); + } else if (token.identifier) { + left = token; + left.arity = "variable"; + } else { + return stop("unexpected_a", token); + } + (function right() { + the_symbol = syntax[next_token.id]; + if ( + the_symbol !== undefined + && the_symbol.led !== undefined + && rbp < the_symbol.lbp + ) { + advance(); + left = the_symbol.led(left); + return right(); + } + }()); + return left; +} + +function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = next_token; + let the_value; + the_paren.free = true; + advance("("); + the_value = expression(0); + advance(")"); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (anticondition[the_value.id] === true) { + warn("unexpected_a", the_value); + } + return the_value; +} + +function is_weird(thing) { + return ( + thing.id === "(regexp)" + || thing.id === "{" + || thing.id === "=>" + || thing.id === "function" + || (thing.id === "[" && thing.arity === "unary") + ); +} + +function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + return ( + Array.isArray(b) + && a.length === b.length + && a.every(function (value, index) { + return are_similar(value, b[index]); + }) + ); + } + if (Array.isArray(b)) { + return false; + } + if (a.id === "(number)" && b.id === "(number)") { + return a.value === b.value; + } + let a_string; + let b_string; + if (a.id === "(string)") { + a_string = a.value; + } else if (a.id === "`" && a.constant) { + a_string = a.value[0]; + } + if (b.id === "(string)") { + b_string = b.value; + } else if (b.id === "`" && b.constant) { + b_string = b.value[0]; + } + if (typeof a_string === "string") { + return a_string === b_string; + } + if (is_weird(a) || is_weird(b)) { + return false; + } + if (a.arity === b.arity && a.id === b.id) { + if (a.id === ".") { + return ( + are_similar(a.expression, b.expression) + && are_similar(a.name, b.name) + ); + } + if (a.arity === "unary") { + return are_similar(a.expression, b.expression); + } + if (a.arity === "binary") { + return ( + a.id !== "(" + && are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + ); + } + if (a.arity === "ternary") { + return ( + are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + && are_similar(a.expression[2], b.expression[2]) + ); + } + if (a.arity === "function" && a.arity === "regexp") { + return false; + } + return true; + } + return false; +} + +function semicolon() { + +// Try to match a semicolon. + + if (next_token.id === ";") { + advance(";"); + } else { + warn_at( + "expected_a_b", + token.line, + token.thru, + ";", + artifact(next_token) + ); + } + anon = "anonymous"; +} + +function statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token.identifier && next_token.id === ":") { + the_label = token; + if (the_label.id === "ignore") { + warn("unexpected_a", the_label); + } + advance(":"); + if ( + next_token.id === "do" + || next_token.id === "for" + || next_token.id === "switch" + || next_token.id === "while" + ) { + enroll(the_label, "label", true); + the_label.init = true; + the_label.dead = false; + the_statement = statement(); + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token; + first.statement = true; + the_symbol = syntax[first.id]; + if (the_symbol !== undefined && the_symbol.fud !== undefined) { + the_symbol.disrupt = false; + the_symbol.statement = true; + the_statement = the_symbol.fud(); + } else { + +// It is an expression statement. + + the_statement = expression(0, true); + if (the_statement.wrapped && the_statement.id !== "(") { + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; +} + +function statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const array = []; + (function next(disrupt) { + if ( + next_token.id !== "}" + && next_token.id !== "case" + && next_token.id !== "default" + && next_token.id !== "else" + && next_token.id !== "(end)" + ) { + let a_statement = statement(); + array.push(a_statement); + if (disrupt) { + warn("unreachable_a", a_statement); + } + return next(a_statement.disrupt); + } + }(false)); + return array; +} + +function not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === global) { + warn("unexpected_at_top_level_a", thing); + } +} + +function top_level_only(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== global) { + warn("misplaced_a", the_thing); + } +} + +function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token; + the_block.arity = "statement"; + the_block.body = special === "body"; + +// Top level function bodies may include the "use strict" pragma. + + if ( + special === "body" + && stack.length === 1 + && next_token.value === "use strict" + ) { + next_token.statement = true; + advance("(string)"); + advance(";"); + } + stmts = statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option.devel && special !== "ignore") { + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; +} + +function mutation_check(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + warn("bad_assignment_a", the_thing); + return false; + } + return true; +} + +function left_check(left, right) { + +// Warn if the left is not one of these: +// e.b +// e[b] +// e() +// ?: +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !left_check(left.expression[1]) + && !left_check(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right); + return false; + } + return true; +} + +// These functions are used to specify the grammar of our language: + +function symbol(id, bp) { + +// Make a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax[id] = the_symbol; + } + return the_symbol; +} + +function assignment(id) { + +// Make an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led = function (left) { + const the_token = token; + let right; + the_token.arity = "assignment"; + right = expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "pre" + || right.arity === "post" + ) { + warn("unexpected_a", right); + } + mutation_check(left); + return the_token; + }; + return the_symbol; +} + +function constant(id, type, value) { + +// Make a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud = ( + typeof value === "function" + ? value + : function () { + token.constant = true; + if (value !== undefined) { + token.value = value; + } + return token; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; +} + +function infix(id, bp, f) { + +// Make an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, expression(bp)]; + return the_token; + }; + return the_symbol; +} + +function infixr(id, bp) { + +// Make a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + the_token.expression = [left, expression(bp - 1)]; + return the_token; + }; + return the_symbol; +} + +function post(id) { + +// Make one of the post operators. + + const the_symbol = symbol(id, 150); + the_symbol.led = function (left) { + token.expression = left; + token.arity = "post"; + mutation_check(token.expression); + return token; + }; + return the_symbol; +} + +function pre(id) { + +// Make one of the pre operators. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "pre"; + the_token.expression = expression(150); + mutation_check(the_token.expression); + return the_token; + }; + return the_symbol; +} + +function prefix(id, f) { + +// Make a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = expression(150); + return the_token; + }; + return the_symbol; +} + +function stmt(id, f) { + +// Make a statement. + + const the_symbol = symbol(id); + the_symbol.fud = function () { + token.arity = "statement"; + return f(); + }; + return the_symbol; +} + +function ternary(id1, id2) { + +// Make a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led = function (left) { + const the_token = token; + const second = expression(20); + advance(id2); + token.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, expression(10)]; + if (next_token.id !== ")") { + warn("use_open", the_token); + } + return the_token; + }; + return the_symbol; +} + +// Begin defining the language. + +syntax = empty(); + +symbol("}"); +symbol(")"); +symbol("]"); +symbol(","); +symbol(";"); +symbol(":"); +symbol("*/"); +symbol("await"); +symbol("case"); +symbol("catch"); +symbol("class"); +symbol("default"); +symbol("else"); +symbol("enum"); +symbol("finally"); +symbol("implements"); +symbol("interface"); +symbol("package"); +symbol("private"); +symbol("protected"); +symbol("public"); +symbol("static"); +symbol("super"); +symbol("void"); +symbol("yield"); + +constant("(number)", "number"); +constant("(regexp)", "regexp"); +constant("(string)", "string"); +constant("arguments", "object", function () { + warn("unexpected_a", token); + return token; +}); +constant("eval", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("false", "boolean", false); +constant("Function", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("ignore", "undefined", function () { + warn("unexpected_a", token); + return token; +}); +constant("Infinity", "number", Infinity); +constant("isFinite", "function", function () { + warn("expected_a_b", token, "Number.isFinite", "isFinite"); + return token; +}); +constant("isNaN", "function", function () { + warn("number_isNaN", token); + return token; +}); +constant("NaN", "number", NaN); +constant("null", "null", null); +constant("this", "object", function () { + if (!option.this) { + warn("unexpected_a", token); + } + return token; +}); +constant("true", "boolean", true); +constant("undefined", "undefined"); + +assignment("="); +assignment("+="); +assignment("-="); +assignment("*="); +assignment("/="); +assignment("%="); +assignment("&="); +assignment("|="); +assignment("^="); +assignment("<<="); +assignment(">>="); +assignment(">>>="); + +infix("||", 40); +infix("&&", 50); +infix("|", 70); +infix("^", 80); +infix("&", 90); +infix("==", 100); +infix("===", 100); +infix("!=", 100); +infix("!==", 100); +infix("<", 110); +infix(">", 110); +infix("<=", 110); +infix(">=", 110); +infix("in", 110); +infix("instanceof", 110); +infix("<<", 120); +infix(">>", 120); +infix(">>>", 120); +infix("+", 130); +infix("-", 130); +infix("*", 140); +infix("/", 140); +infix("%", 140); +infixr("**", 150); +infix("(", 160, function (left) { + const the_paren = token; + let the_argument; + if (left.id !== "function") { + left_check(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (next_token.id !== ")") { + (function next() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + the_paren.free = true; + if (the_argument.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + the_paren.free = false; + } + return the_paren; +}); +infix(".", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("?.", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("[", 170, function (left) { + const the_token = token; + const the_subscript = expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + const name = survey(the_subscript); + if (rx_identifier.test(name)) { + warn("subscript_a", the_subscript, name); + } + } + left_check(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; +}); +infix("=>", 170, function (left) { + return stop("wrap_parameter", left); +}); + +function do_tick() { + const the_tick = token; + the_tick.value = []; + the_tick.expression = []; + if (next_token.id !== "`") { + (function part() { + advance("(string)"); + the_tick.value.push(token); + if (next_token.id === "${") { + advance("${"); + the_tick.expression.push(expression(0)); + advance("}"); + return part(); + } + }()); + } + advance("`"); + return the_tick; +} + +infix("`", 160, function (left) { + const the_tick = do_tick(); + left_check(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; +}); + +post("++"); +post("--"); +pre("++"); +pre("--"); + +prefix("+"); +prefix("-"); +prefix("~"); +prefix("!"); +prefix("!!"); +prefix("[", function () { + const the_token = token; + the_token.expression = []; + if (next_token.id !== "]") { + (function next() { + let element; + let ellipsis = false; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + element = expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]"); + return the_token; +}); +prefix("/=", function () { + stop("expected_a_b", token, "/\\=", "/="); +}); +prefix("=>", function () { + return stop("expected_a_before_b", token, "()", "=>"); +}); +prefix("new", function () { + const the_new = token; + const right = expression(160); + if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "()", artifact(next_token)); + } + the_new.expression = right; + return the_new; +}); +prefix("typeof"); +prefix("void", function () { + const the_void = token; + warn("unexpected_a", the_void); + the_void.expression = expression(0); + return the_void; +}); + +function parameter_list() { + const list = []; + let optional; + const signature = ["("]; + if (next_token.id !== ")" && next_token.id !== "(end)") { + (function parameter() { + let ellipsis = false; + let param; + if (next_token.id === "{") { + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("{"); + signature.push("{"); + (function subparameter() { + let subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (next_token.id === ":") { + advance(":"); + advance(); + token.label = subparam; + subparam = token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + } + if (next_token.id === "=") { + advance("="); + subparam.expression = expression(); + param.open = true; + } + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return subparameter(); + } + }()); + list.push(param); + advance("}"); + signature.push("}"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else if (next_token.id === "[") { + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("["); + signature.push("[]"); + (function subparameter() { + const subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + if (next_token.id === "=") { + advance("="); + subparam.expression = expression(); + param.open = true; + } + if (next_token.id === ",") { + advance(","); + return subparameter(); + } + }()); + list.push(param); + advance("]"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else { + if (next_token.id === "...") { + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + } + if (!next_token.identifier) { + return stop("expected_identifier_a"); + } + param = next_token; + list.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (next_token.id === "=") { + optional = param; + advance("="); + param.expression = expression(0); + } else { + if (optional !== undefined) { + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } + } + }()); + } + advance(")"); + signature.push(")"); + return [list, signature.join("")]; +} + +function do_function(the_function) { + let name; + if (the_function === undefined) { + the_function = token; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + name = next_token; + enroll(name, "variable", true); + the_function.name = name; + name.init = true; + name.calls = empty(); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + if (next_token.identifier) { + name = next_token; + the_function.name = name; + advance(); + } else { + the_function.name = anon; + } + } + } else { + name = the_function.name; + } + the_function.level = functionage.level + 1; + if (mega_mode) { + warn("unexpected_a", the_function); + } + +// Don't make functions in loops. It is inefficient, and it can lead to scoping +// errors. + + if (functionage.loop > 0) { + warn("function_in_loop", the_function); + } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + the_function.context = empty(); + the_function.finally = 0; + the_function.loop = 0; + the_function.switch = 0; + the_function.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functions.push(the_function); + functionage = the_function; + if (the_function.arity !== "statement" && typeof name === "object") { + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// Parse the parameter list. + + advance("("); + token.free = false; + token.arity = "function"; + [functionage.parameters, functionage.signature] = parameter_list(); + functionage.parameters.forEach(function enroll_parameter(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + name.names.forEach(enroll_parameter); + } + }); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && next_token.line === token.line + ) { + return stop("unexpected_a", next_token); + } + if ( + next_token.id === "." + || next_token.id === "?." + || next_token.id === "[" + ) { + warn("unexpected_a"); + } + +// Restore the previous context. + + functionage = stack.pop(); + return the_function; +} + +prefix("function", do_function); + +function fart(pl) { + advance("=>"); + const the_fart = token; + the_fart.arity = "binary"; + the_fart.name = "=>"; + the_fart.level = functionage.level + 1; + functions.push(the_fart); + if (functionage.loop > 0) { + warn("function_in_loop", the_fart); + } + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + the_fart.context = empty(); + the_fart.finally = 0; + the_fart.loop = 0; + the_fart.switch = 0; + the_fart.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functionage = the_fart; + the_fart.parameters = pl[0]; + the_fart.signature = pl[1]; + the_fart.parameters.forEach(function (name) { + enroll(name, "parameter", true); + }); + if (next_token.id === "{") { + warn("expected_a_b", the_fart, "function", "=>"); + the_fart.block = block("body"); + } else { + the_fart.expression = expression(0); + } + functionage = stack.pop(); + return the_fart; +} + +prefix("(", function () { + const the_paren = token; + let the_value; + const cadet = lookahead().id; + +// We can distinguish between a parameter list for => and a wrapped expression +// with one token of lookahead. + + if ( + next_token.id === ")" + || next_token.id === "..." + || (next_token.identifier && (cadet === "," || cadet === "=")) + ) { + the_paren.free = false; + return fart(parameter_list()); + } + the_paren.free = true; + the_value = expression(0); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + if (next_token.id === "=>") { + if (the_value.arity !== "variable") { + if (the_value.id === "{" || the_value.id === "[") { + warn("expected_a_before_b", the_paren, "function", "("); + return stop("expected_a_b", next_token, "{", "=>"); + } + return stop("expected_identifier_a", the_value); + } + the_paren.expression = [the_value]; + return fart([the_paren.expression, "(" + the_value.id + ")"]); + } + return the_value; +}); +prefix("`", do_tick); +prefix("{", function () { + const the_brace = token; + const seen = empty(); + the_brace.expression = []; + if (next_token.id !== "}") { + (function member() { + let extra; + let full; + let id; + let name = next_token; + let value; + advance(); + if ( + (name.id === "get" || name.id === "set") + && next_token.identifier + ) { + if (!option.getset) { + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + next_token.id; + name = next_token; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (next_token.id === "}" || next_token.id === ",") { + if (typeof extra === "string") { + advance("("); + } + value = expression(Infinity, true); + } else if (next_token.id === "(") { + value = do_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + advance("("); + } + let the_colon = next_token; + advance(":"); + value = expression(0); + if (value.id === name.id && value.id !== "function") { + warn("unexpected_a", the_colon, ": " + name.id); + } + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + advance(":"); + value = expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (next_token.id === ",") { + advance(","); + return member(); + } + }()); + } + advance("}"); + return the_brace; +}); + +stmt(";", function () { + warn("unexpected_a", token); + return token; +}); +stmt("{", function () { + warn("naked_block", token); + return block("naked"); +}); +stmt("break", function () { + const the_break = token; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (next_token.identifier && token.line === next_token.line) { + the_label = functionage.context[next_token.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + warn( + (the_label !== undefined && the_label.dead) + ? "out_of_scope_a" + : "not_label_a" + ); + } else { + the_label.used += 1; + } + the_break.label = next_token; + advance(); + } + advance(";"); + return the_break; +}); + +function do_var() { + const the_statement = token; + const is_const = the_statement.id === "const"; + the_statement.names = []; + +// A program may use var or let, but not both. + + if (!is_const) { + if (var_mode === undefined) { + var_mode = the_statement.id; + } else if (the_statement.id !== var_mode) { + warn( + "expected_a_b", + the_statement, + var_mode, + the_statement.id + ); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + warn("var_switch", the_statement); + } + if (functionage.loop > 0 && the_statement.id === "var") { + warn("var_loop", the_statement); + } + (function next() { + if (next_token.id === "{" && the_statement.id !== "var") { + const the_brace = next_token; + advance("{"); + (function pair() { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + survey(name); + advance(); + if (next_token.id === ":") { + advance(":"); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + next_token.label = name; + the_statement.names.push(next_token); + enroll(next_token, "variable", is_const); + advance(); + the_brace.open = true; + } else { + the_statement.names.push(name); + enroll(name, "variable", is_const); + } + name.dead = false; + name.init = true; + if (next_token.id === "=") { + advance("="); + name.expression = expression(); + the_brace.open = true; + } + if (next_token.id === ",") { + advance(","); + return pair(); + } + }()); + advance("}"); + advance("="); + the_statement.expression = expression(0); + } else if (next_token.id === "[" && the_statement.id !== "var") { + const the_bracket = next_token; + advance("["); + (function element() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + advance(); + the_statement.names.push(name); + enroll(name, "variable", is_const); + name.dead = false; + name.init = true; + if (ellipsis) { + name.ellipsis = true; + } else { + if (next_token.id === "=") { + advance("="); + name.expression = expression(); + the_bracket.open = true; + } + if (next_token.id === ",") { + advance(","); + return element(); + } + } + }()); + advance("]"); + advance("="); + the_statement.expression = expression(0); + } else if (next_token.identifier) { + const name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", is_const); + if (next_token.id === "=" || is_const) { + advance("="); + name.dead = false; + name.init = true; + name.expression = expression(0); + } + the_statement.names.push(name); + } else { + return stop("expected_identifier_a", next_token); + } + }()); + semicolon(); + return the_statement; +} + +stmt("const", do_var); +stmt("continue", function () { + const the_continue = token; + if (functionage.loop < 1 || functionage.finally > 0) { + warn("unexpected_a", the_continue); + } + not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; +}); +stmt("debugger", function () { + const the_debug = token; + if (!option.devel) { + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; +}); +stmt("delete", function () { + const the_token = token; + const the_value = expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; +}); +stmt("do", function () { + const the_do = token; + not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; +}); +stmt("export", function () { + const the_export = token; + let the_id; + let the_name; + let the_thing; + + function export_id() { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + the_id = next_token.id; + the_name = global.context[the_id]; + if (the_name === undefined) { + warn("unexpected_a"); + } else { + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a"); + } + exports[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + } + + the_export.expression = []; + if (next_token.id === "default") { + if (exports.default !== undefined) { + warn("duplicate_a"); + } + advance("default"); + the_thing = expression(0); + if ( + the_thing.id !== "(" + || the_thing.expression[0].id !== "." + || the_thing.expression[0].expression.id !== "Object" + || the_thing.expression[0].name.id !== "freeze" + ) { + warn("freeze_exports", the_thing); + } + if (next_token.id === ";") { + semicolon(); + } + exports.default = the_thing; + the_export.expression.push(the_thing); + } else { + if (next_token.id === "function") { + warn("freeze_exports"); + the_thing = statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a", the_name); + } + exports[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + warn("unexpected_a", next_token); + statement(); + } else if (next_token.id === "{") { + advance("{"); + (function loop() { + export_id(); + if (next_token.id === ",") { + advance(","); + return loop(); + } + }()); + advance("}"); + semicolon(); + } else { + stop("unexpected_a"); + } + } + module_mode = true; + return the_export; +}); +stmt("for", function () { + let first; + const the_for = token; + if (!option.for) { + warn("unexpected_a", the_for); + } + not_top_level(the_for); + functionage.loop += 1; + advance("("); + token.free = true; + if (next_token.id === ";") { + return stop("expected_a_b", the_for, "while (", "for (;"); + } + if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + return stop("unexpected_a"); + } + first = expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = expression(0); + advance(";"); + the_for.inc = expression(0); + if (the_for.inc.id === "++") { + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; +}); +stmt("function", do_function); +stmt("if", function () { + let the_else; + const the_if = token; + the_if.expression = condition(); + the_if.block = block(); + if (next_token.id === "else") { + advance("else"); + the_else = token; + the_if.else = ( + next_token.id === "if" + ? statement() + : block() + ); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + the_if.disrupt = true; + } else { + warn("unexpected_a", the_else); + } + } + } + return the_if; +}); +stmt("import", function () { + const the_import = token; + if (next_token.id === "(") { + the_import.arity = "unary"; + the_import.constant = true; + the_import.statement = false; + advance("("); + const string = expression(0); + if (string.id !== "(string)") { + warn("expected_string_a", string); + } + froms.push(token.value); + advance(")"); + advance("."); + advance("then"); + advance("("); + the_import.expression = expression(0); + advance(")"); + semicolon(); + return the_import; + } + let name; + if (typeof module_mode === "object") { + warn("unexpected_directive_a", module_mode, module_mode.directive); + } + module_mode = true; + if (next_token.identifier) { + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + const names = []; + advance("{"); + if (next_token.id !== "}") { + while (true) { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (next_token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token; + if (!rx_module.test(token.value)) { + warn("bad_module_name_a", token); + } + froms.push(token.value); + semicolon(); + return the_import; +}); +stmt("let", do_var); +stmt("return", function () { + const the_return = token; + not_top_level(the_return); + if (functionage.finally > 0) { + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (next_token.id !== ";" && the_return.line === next_token.line) { + the_return.expression = expression(10); + } + advance(";"); + return the_return; +}); +stmt("switch", function () { + let dups = []; + let last; + let stmts; + const the_cases = []; + let the_disrupt = true; + const the_switch = token; + not_top_level(the_switch); + if (functionage.finally > 0) { + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token.free = true; + the_switch.expression = expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + (function major() { + const the_case = next_token; + the_case.arity = "statement"; + the_case.expression = []; + (function minor() { + advance("case"); + token.switch = true; + const exp = expression(0); + if (dups.some(function (thing) { + return are_similar(thing, exp); + })) { + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (next_token.id === "case") { + return minor(); + } + }()); + stmts = statements(); + if (stmts.length < 1) { + warn("expected_statements_a"); + return; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "break;", + artifact(next_token) + ); + } + if (next_token.id === "case") { + return major(); + } + }()); + dups = undefined; + if (next_token.id === "default") { + const the_default = next_token; + advance("default"); + token.switch = true; + advance(":"); + the_switch.else = statements(); + if (the_switch.else.length < 1) { + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + const the_last = the_switch.else[the_switch.else.length - 1]; + if (the_last.id === "break" && the_last.label === undefined) { + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; +}); +stmt("throw", function () { + const the_throw = token; + the_throw.disrupt = true; + the_throw.expression = expression(10); + semicolon(); + if (functionage.try > 0) { + warn("unexpected_a", the_throw); + } + return the_throw; +}); +stmt("try", function () { + let the_catch; + let the_disrupt; + const the_try = token; + if (functionage.try > 0) { + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (next_token.id === "catch") { + let ignored = "ignore"; + the_catch = next_token; + the_try.catch = the_catch; + advance("catch"); + if (next_token.id === "(") { + advance("("); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + if (next_token.id !== "ignore") { + ignored = undefined; + the_catch.name = next_token; + enroll(next_token, "exception", true); + } + advance(); + advance(")"); + } + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "catch", + artifact(next_token) + ); + + } + if (next_token.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; +}); +stmt("var", do_var); +stmt("while", function () { + const the_while = token; + not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; +}); +stmt("with", function () { + stop("unexpected_a", token); +}); + +ternary("?", ":"); + +// Ambulation of the parse tree. + +function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; +} + +function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all of the +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + } + }; +} + +const posts = empty(); +const pres = empty(); +const preaction = action(pres); +const postaction = action(posts); +const preamble = amble(pres); +const postamble = amble(posts); + +function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.id === "function") { + walk_statement(thing.block); + } + if (thing.arity === "pre" || thing.arity === "post") { + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + warn("unexpected_statement_a", thing); + } + postamble(thing); + } + } +} + +function walk_statement(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_statement); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + && thing.id !== "import" + ) { + warn("unexpected_expression_a", thing); + } + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + } +} + +function lookup(thing) { + if (thing.arity === "variable") { + +// Look up the variable in the current context. + + let the_variable = functionage.context[thing.id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable === undefined) { + stack.forEach(function (outer) { + const a_variable = outer.context[thing.id]; + if ( + a_variable !== undefined + && a_variable.role !== "label" + ) { + the_variable = a_variable; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (the_variable === undefined) { + if (declared_globals[thing.id] === undefined) { + warn("undeclared_a", thing); + return; + } + the_variable = { + dead: false, + parent: global, + id: thing.id, + init: true, + role: "variable", + used: 0, + writable: false + }; + global.context[thing.id] = the_variable; + } + the_variable.closure = true; + functionage.context[thing.id] = the_variable; + } else if (the_variable.role === "label") { + warn("label_a", thing); + } + if ( + the_variable.dead + && ( + the_variable.calls === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + ) { + warn("out_of_scope_a", thing); + } + return the_variable; + } +} + +function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); +} + +function preaction_function(thing) { + if (thing.arity === "statement" && blockage.body !== true) { + warn("unexpected_a", thing); + } + stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); +} + +function bitwise_check(thing) { + if (!option.bitwise && bitwiseop[thing.id] === true) { + warn("unexpected_a", thing); + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + warn("unexpected_a", thing); + } +} + +function pop_block() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); +} + +function activate(name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.init = true; + } + } + blockage.live.push(name); +} + +function action_var(thing) { + thing.names.forEach(activate); +} + +preaction("assignment", bitwise_check); +preaction("binary", bitwise_check); +preaction("binary", function (thing) { + if (relationop[thing.id] === true) { + const left = thing.expression[0]; + const right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + warn("expected_string_a", right); + } + } else { + const value = right.value; + if (value === "null" || value === "undefined") { + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + warn("expected_type_string_a", right, value); + } + } + } + } +}); +preaction("binary", "==", function (thing) { + warn("expected_a_b", thing, "===", "=="); +}); +preaction("binary", "!=", function (thing) { + warn("expected_a_b", thing, "!==", "!="); +}); +preaction("binary", "=>", preaction_function); +preaction("binary", "||", function (thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + warn("and", thang); + } + }); +}); +preaction("binary", "(", function (thing) { + const left = thing.expression[0]; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + const parent = functionage.name.parent; + if (parent) { + const left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + && left_variable.dead + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } +}); +preaction("binary", "in", function (thing) { + warn("infix_in", thing); +}); +preaction("binary", "instanceof", function (thing) { + warn("unexpected_a", thing); +}); +preaction("binary", ".", function (thing) { + if (thing.expression.new) { + thing.new = true; + } +}); +preaction("statement", "{", function (thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; +}); +preaction("statement", "for", function (thing) { + if (thing.name !== undefined) { + const the_variable = lookup(thing.name); + if (the_variable !== undefined) { + the_variable.init = true; + if (!the_variable.writable) { + warn("bad_assignment_a", thing.name); + } + } + } + walk_statement(thing.initial); +}); +preaction("statement", "function", preaction_function); +preaction("unary", "~", bitwise_check); +preaction("unary", "function", preaction_function); +preaction("variable", function (thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } +}); + +function init_variable(name) { + const the_variable = lookup(name); + if (the_variable !== undefined) { + if (the_variable.writable) { + the_variable.init = true; + return; + } + } + warn("bad_assignment_a", name); +} + +postaction("assignment", "+=", function (thing) { + let right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } +}); +postaction("assignment", function (thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + if (thing.id === "=") { + if (thing.names !== undefined) { + if (Array.isArray(thing.names)) { + thing.names.forEach(init_variable); + } else { + init_variable(thing.names); + } + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.writable !== true) { + warn("bad_assignment_a", lvalue); + } + } + const right = syntax[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + warn("unexpected_a", thing.expression[1]); + } + } +}); + +function postaction_function(thing) { + delete functionage.finally; + delete functionage.loop; + delete functionage.switch; + delete functionage.try; + functionage = stack.pop(); + if (thing.wrapped) { + warn("unexpected_parens", thing); + } + return pop_block(); +} + +postaction("binary", function (thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || are_similar(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option.convert) { + if (thing.expression[0].value === "") { + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === "." || thing.id === "?.") { + if (thing.expression.id === "RegExp") { + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } +}); +postaction("binary", "&&", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "||", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "=>", postaction_function); +postaction("binary", "(", function (thing) { + let left = thing.expression[0]; + let the_new; + let arg; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option.eval) { + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + warn( + "expected_a_before_b", + left, + "new", + artifact(left) + ); + } + } + } else if (left.id === ".") { + let cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + cack = !cack; + } + if (rx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + warn("unexpected_a", the_new); + } else { + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + const paren = left.expression; + if (paren.id === "(") { + const array = paren.expression; + if (array.length === 1) { + const new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } +}); +postaction("binary", "[", function (thing) { + if (thing.expression[0].id === "RegExp") { + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + warn("weird_expression_a", thing.expression[1]); + } +}); +postaction("statement", "{", pop_block); +postaction("statement", "const", action_var); +postaction("statement", "export", top_level_only); +postaction("statement", "for", function (thing) { + walk_statement(thing.inc); +}); +postaction("statement", "function", postaction_function); +postaction("statement", "import", function (the_thing) { + const name = the_thing.name; + if (name) { + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return top_level_only(the_thing); + } +}); +postaction("statement", "let", action_var); +postaction("statement", "try", function (thing) { + if (thing.catch !== undefined) { + const the_name = thing.catch.name; + if (the_name !== undefined) { + const the_variable = functionage.context[the_name.id]; + the_variable.dead = false; + the_variable.init = true; + } + walk_statement(thing.catch.block); + } +}); +postaction("statement", "var", action_var); +postaction("ternary", function (thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || are_similar(thing.expression[1], thing.expression[2]) + ) { + warn("unexpected_a", thing); + } else if (are_similar(thing.expression[0], thing.expression[1])) { + warn("expected_a_b", thing, "||", "?"); + } else if (are_similar(thing.expression[0], thing.expression[2])) { + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + warn("wrap_condition", thing.expression[0]); + } +}); +postaction("unary", function (thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option.convert) { + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } +}); +postaction("unary", "function", postaction_function); +postaction("unary", "+", function (thing) { + if (!option.convert) { + warn("expected_a_b", thing, "Number(...)", "+"); + } + const right = thing.expression; + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } +}); + +function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + if (id !== "ignore") { + const name = the_function.context[id]; + if (name.parent === the_function) { + if ( + name.used === 0 + && ( + name.role !== "function" + || name.parent.arity !== "unary" + ) + ) { + warn("unused_a", name); + } else if (!name.init) { + warn("uninitialized_a", name); + } + } + } + }); +} + +function uninitialized_and_unused() { + +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (module_mode === true || option.node) { + delve(global); + } + functions.forEach(delve); +} + +// Go through the token list, looking at usage of whitespace. + +function whitage() { + let closer = "(end)"; + let free = false; + let left = global; + let margin = 0; + let nr_comments_skipped = 0; + let open = true; + let opening = true; + let right; + + function pop() { + const previous = stack.pop(); + closer = previous.closer; + free = previous.free; + margin = previous.margin; + open = previous.open; + opening = previous.opening; + } + + function push() { + stack.push({ + closer, + free, + margin, + open, + opening + }); + } + + function expected_at(at) { + warn( + "expected_a_at_b_c", + right, + artifact(right), + fudge + at, + artifact_column(right) + ); + } + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function no_space() { + if (left.line === right.line) { + if (left.thru !== right.from && nr_comments_skipped === 0) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (open) { + const at = ( + free + ? margin + : margin + 8 + ); + if (right.from < at) { + expected_at(at); + } + } else { + if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line || !open) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (right.from !== margin) { + expected_at(margin); + } + } + } + + stack = []; + tokens.forEach(function (the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + + const new_closer = opener[left.id]; + if (typeof new_closer === "string") { + if (new_closer !== right.id) { + opening = left.open || (left.line !== right.line); + push(); + closer = new_closer; + if (opening) { + free = closer === ")" && left.free; + open = true; + margin += 4; + if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (right.switch) { + at_margin(-4); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + free = false; + open = false; + no_space_only(); + } + } else { + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like '{]') have already been detected. + + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } + } else { + if (right.statement === true) { + if (left.id === "else") { + one_space_only(); + } else { + at_margin(0); + open = false; + } + +// If right is a closer, then pop the previous state. + + } else if (right.id === closer) { + pop(); + if (opening && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-4); + } else if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + one_space(); + } else { + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + at_margin(0); + } else { + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + no_space(); + } else if ( + left.id === "." + || left.id === "?." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + ) { + no_space_only(); + } else if (right.id === "." || right.id === "?.") { + no_space_only(); + } else if (left.id === ";") { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + one_space_only(); + } else if ( + left.id === "var" + || left.id === "const" + || left.id === "let" + ) { + push(); + closer = ";"; + free = false; + open = left.open; + if (open) { + margin = margin + 4; + at_margin(0); + } else { + one_space_only(); + } + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +// The jslint function itself. + +export default Object.freeze(function jslint( + source = "", + option_object = empty(), + global_array = [] +) { + try { + warnings = []; + option = Object.assign(empty(), option_object); + anon = "anonymous"; + block_stack = []; + declared_globals = empty(); + directive_mode = true; + directives = []; + early_stop = true; + exports = empty(); + froms = []; + fudge = ( + option.fudge + ? 1 + : 0 + ); + functions = []; + global = { + id: "(global)", + body: true, + context: empty(), + from: 0, + level: 0, + line: 0, + live: [], + loop: 0, + switch: 0, + thru: 0 + }; + blockage = global; + functionage = global; + json_mode = false; + mega_mode = false; + module_mode = false; + next_token = global; + property = empty(); + shebang = false; + stack = []; + tenure = undefined; + token = global; + token_nr = 0; + var_mode = undefined; + populate(standard, declared_globals, false); + populate(global_array, declared_globals, false); + Object.keys(option).forEach(function (name) { + if (option[name] === true) { + const allowed = allowed_option[name]; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } + }); + tokenize(source); + advance(); + if (json_mode) { + tree = json_value(); + advance("(end)"); + } else { + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option.browser) { + if (next_token.id === ";") { + advance(";"); + } + } else { + +// If we are not in a browser, then the file form of strict pragma may be used. + + if ( + next_token.value === "use strict" + ) { + advance("(string)"); + advance(";"); + } + } + tree = statements(); + advance("(end)"); + functionage = global; + walk_statement(tree); + if (warnings.length === 0) { + uninitialized_and_unused(); + if (!option.white) { + whitage(); + } + } + } + if (!option.browser) { + directives.forEach(function (comment) { + if (comment.directive === "global") { + warn("missing_browser", comment); + } + }); + } + early_stop = false; + } catch (e) { + if (e.name !== "JSLintError") { + warnings.push(e); + } + } + return { + directives, + edition: "2020-03-18", + exports, + froms, + functions, + global, + id: "(JSLint)", + json: json_mode, + lines, + module: module_mode === true, + ok: warnings.length === 0 && !early_stop, + option, + property, + shebang: ( + shebang + ? lines[0] + : undefined + ), + stop: early_stop, + tokens, + tree, + warnings: warnings.sort(function (a, b) { + return a.line - b.line || a.column - b.column; + }) + }; +}); diff --git a/branch.nomen/report.js b/branch.nomen/report.js new file mode 100644 index 000000000..5e487d143 --- /dev/null +++ b/branch.nomen/report.js @@ -0,0 +1,222 @@ +// report.js +// 2018-10-22 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Generate JSLint HTML reports. + +/*property + closure, column, context, edition, error, exports, filter, forEach, freeze, + froms, fudge, function, functions, global, id, isArray, join, json, keys, + length, level, line, lines, message, module, name, names, option, + parameters, parent, property, push, replace, role, signature, sort, stop, + warnings +*/ + +const rx_amp = /&/g; +const rx_gt = />/g; +const rx_lt = / with less destructive entities. + + return String( + string + ).replace( + rx_amp, + "&" + ).replace( + rx_lt, + "<" + ).replace( + rx_gt, + ">" + ); +} + +export default Object.freeze({ + error: function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + let fudge = Number(Boolean(data.option.fudge)); + let output = []; + if (data.stop) { + output.push("
    JSLint was unable to finish.
    "); + } + data.warnings.forEach(function (warning) { + output.push( + "
    ", + entityify(warning.line + fudge), + ".", + entityify(warning.column + fudge), + "
    ", + entityify(warning.message), + "
    ", + entityify(data.lines[warning.line] || ""), + "" + ); + }); + return output.join(""); + }, + + function: function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + let fudge = Number(Boolean(data.option.fudge)); + let mode = ( + data.module + ? "module" + : "global" + ); + let output = []; + + if (data.json) { + return ( + data.warnings.length === 0 + ? "
    JSON: good.
    " + : "
    JSON: bad.
    " + ); + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + let global = Object.keys(data.global.context).sort(); + let froms = data.froms.sort(); + let exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + let context = the_function.context; + let list = Object.keys(context); + output.push( + "
    ", + entityify(the_function.line + fudge), + "
    ", + ( + the_function.name === "=>" + ? entityify(the_function.signature) + " =>" + : ( + typeof the_function.name === "string" + ? "«" + entityify(the_function.name) + "»" + : "" + entityify(the_function.name.id) + "" + ) + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + let params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.parent === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.parent === the_function + ); + })); + detail("outer", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.parent !== the_function + && the_variable.parent.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].parent.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + output.push( + "
    JSLint edition ", + entityify(data.edition), + "
    " + ); + return output.join(""); + }, + + property: function property_directive(data) { + +// Produce the /*property*/ directive. + + let not_first = false; + let output = ["/*property"]; + let length = 1111; + let properties = Object.keys(data.property); + + if (properties.length > 0) { + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length >= 80) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); + } + } +}); diff --git a/branch.option_debug/README b/branch.option_debug/README new file mode 100644 index 000000000..f46e91069 --- /dev/null +++ b/branch.option_debug/README @@ -0,0 +1,42 @@ +JSLint, The JavaScript Code Quality Tool + +Douglas Crockford +douglas@crockford.com + +2018-05-28 + +jslint.js contains the jslint function. It parses and analyzes a source file, +returning an object with information about the file. It can also take an object +that sets options. + +index.html runs the jslint.js function in a web page. The page also depends +browser.js and report.js and jslint.css. + +jslint.css provides styling for index.html. + +browser.js runs the web user interface. + +report.js generates the results reports in HTML. + +help.html describes JSLint's usage. Please read it. + +function.html describes the jslint function and the results it produces. + +Please direct questions and comments to +https://plus.google.com/communities/104441363299760713736. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[http://www.crockford.com/wrrrld/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. diff --git a/branch.option_debug/browser.js b/branch.option_debug/browser.js new file mode 100644 index 000000000..bddff5a94 --- /dev/null +++ b/branch.option_debug/browser.js @@ -0,0 +1,173 @@ +// browser.js +// 2018-06-16 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +/*jslint + browser + devel +*/ + +/*property + debug, + stack, + warnings, + checked, create, disable, display, error, focus, forEach, function, + getElementById, innerHTML, join, length, map, onchange, onclick, onscroll, + property, querySelectorAll, scrollTop, select, split, style, title, value +*/ + +import jslint from "./jslint.js"; +import report from "./report.js"; + +// This is the web script companion file for JSLint. It includes code for +// interacting with the browser and displaying the reports. + +let rx_crlf = /\n|\r\n?/; +let rx_separator = /[\s,;'"]+/; + +let fudge_unit = 0; + +const aux = document.getElementById("JSLINT_AUX"); +const boxes = document.querySelectorAll("[type=checkbox]"); +const fudge = document.getElementById("JSLINT_FUDGE"); +const global = document.getElementById("JSLINT_GLOBAL"); +const number = document.getElementById("JSLINT_NUMBER"); +const property = document.getElementById("JSLINT_PROPERTY"); +const property_fieldset = document.getElementById("JSLINT_PROPERTYFIELDSET"); +const report_field = document.getElementById("JSLINT_REPORT"); +const report_list = document.getElementById("JSLINT_REPORT_LIST"); +const source = document.getElementById("JSLINT_SOURCE"); +const select = document.getElementById("JSLINT_SELECT"); +const warnings = document.getElementById("JSLINT_WARNINGS"); +const warnings_list = document.getElementById("JSLINT_WARNINGS_LIST"); + +function show_numbers() { + number.value = source.value.split(rx_crlf).map(function (ignore, index) { + return index + fudge_unit; + }).join("\n"); +} + +function clear() { + aux.style.display = "none"; + number.value = ""; + property.value = ""; + property_fieldset.style.display = "none"; + report_field.style.display = "none"; + report_list.innerHTML = ""; + source.focus(); + source.value = ""; + warnings.style.display = "none"; + warnings_list.innerHTML = ""; +} + +function fudge_change() { + fudge_unit = Number(fudge.checked); + show_numbers(); +} + +function clear_options() { + boxes.forEach(function (node) { + node.checked = false; + }); + fudge_change(); + global.innerHTML = ""; +} + +function call_jslint() { + +// First build the option object. + + let option = Object.create(null); + boxes.forEach(function (node) { + if (node.checked) { + option[node.title] = true; + } + }); + +// Enable debug + + option.debug = true; + +// Call JSLint with the source text, the options, and the predefined globals. + + let global_string = global.value; + let result = jslint( + source.value, + option, + ( + global_string === "" + ? undefined + : global_string.split(rx_separator) + ) + ); + +// Debug warnings + + result.warnings.forEach(function (warning) { + console.error(warning); + console.error(warning.stack); + }); + +// Generate the reports. + + let error_html = report.error(result); + let function_html = report.function(result); + let property_text = report.property(result); + +// Display the reports. + + warnings_list.innerHTML = error_html; + warnings.style.display = ( + error_html.length === 0 + ? "none" + : "block" + ); + + report_list.innerHTML = function_html; + report_field.style.display = "block"; + if (property_text) { + property.value = property_text; + property_fieldset.style.display = "block"; + property.scrollTop = 0; + select.disable = false; + } else { + property_fieldset.style.display = "none"; + select.disable = true; + } + aux.style.display = "block"; + source.select(); +} + +fudge.onchange = fudge_change; + +source.onchange = function (ignore) { + show_numbers(); +}; + +source.onscroll = function () { + let ss = source.scrollTop; + number.scrollTop = ss; + let sn = number.scrollTop; + if (ss > sn) { + show_numbers(); + number.scrollTop = ss; + } +}; + +document.querySelectorAll("[name='JSLint']").forEach(function (node) { + node.onclick = call_jslint; +}); + +document.querySelectorAll("[name='clear']").forEach(function (node) { + node.onclick = clear; +}); + +document.getElementById("JSLINT_SELECT").onclick = function () { + property.focus(); + property.select(); +}; +document.getElementById("JSLINT_CLEAR_OPTIONS").onclick = clear_options; + +fudge_change(); +source.select(); +source.focus(); diff --git a/branch.option_debug/cli.js b/branch.option_debug/cli.js new file mode 100755 index 000000000..a220c2547 --- /dev/null +++ b/branch.option_debug/cli.js @@ -0,0 +1,130 @@ +#!/usr/bin/env node +/* + * cli.js + * simple cli-tool to run jslint.js from command-line + * + * instruction: + * 1. save this file as cli.js + * 2. download and save in same directory, latest jslint.js from + * https://github.com/douglascrockford/JSLint/blob/master/jslint.js + * + * example usage: + * $ node cli.js ./jslint.js // jslint local-file + * $ node cli.js https://code.jquery.com/jquery-1.0.0.js // jslint remote-file + */ + + + +/*jslint node */ +/*property + argv, basename, column, concat, end, error, exit, filter, forEach, fudge, + join, jslint, line, lines, main, match, message, on, option, parse, push, + readFile, replace, request, runInNewContext, slice, trim, trimRight, + warnings +*/ +"use strict"; +let chunk_list; +let file; +let fs; +let fudge; +let local; +let modeNext; +let onNext; +let path; +let url; +let vm; +let warning_text; +onNext = function (error, data) { + if (error) { + console.error(error.message || error); + process.exit(1); + } + modeNext += 1; + switch (modeNext) { + case 1: + // run this program only in cli-mode + if (module !== require.main) { + console.error( + "jslint-cli can only be run from the command-line" + ); + return; + } + // require builtins + fs = require("fs"); + path = require("path"); + url = require("url"); + vm = require("vm"); + // init local namespace + local = {}; + if (!process.argv[2]) { + console.error( + "error: no specified\n" + + "usage: " + path.basename(__filename) + " \n" + + "example: " + path.basename(__filename) + " example.js\n" + + "example: " + path.basename(__filename) + + " https://jslint.com/jslint.js" + ); + process.exit(1); + } + // init jslint + fs.readFile(path.join(__dirname, "jslint.js"), "utf8", onNext); + break; + case 2: + // bug-workaround - nodejs does not widely support es-modules + vm.runInNewContext( + data.replace("export default function", "function"), + local + ); + // read data from file + file = process.argv[2]; + console.error("jslint " + file); + data = file.match(/^(https?):\/\//); + if (!data) { + fs.readFile(file, "utf8", onNext); + return; + } + // read data from http/https url + require(data[1]).request(url.parse(file), function (response) { + chunk_list = []; + response + .on("data", function (chunk) { + chunk_list.push(chunk); + }) + .on("end", function () { + onNext(null, String(Buffer.concat(chunk_list))); + }) + .on("error", onNext); + }).on("error", onNext).end(); + break; + case 3: + // jslint data + data = local.jslint(data + // ignore first-line shebang in nodejs scripts + .replace((/^#!/), "//")); + fudge = data.option.fudge || 0; + // init warning_text + warning_text = ""; + // print warnings to stderrr + data.warnings + .filter(function (warning) { + return warning && warning.message; + }) + // print only first 10 warnings + .slice(0, 10) + .forEach(function (warning, ii) { + warning_text += ( + (" #" + (ii + 1)).slice(-3) + + " \u001b[31m" + warning.message + "\u001b[39m\n" + + " " + String(data.lines[warning.line] || "").trim() + + "\u001b[90m \/\/ line " + + (warning.line + fudge) + + ", col " + (warning.column + fudge) + + "\u001b[39m\n" + ); + }); + console.error(warning_text.trimRight() || "ok"); + break; + } +}; +modeNext = 0; +onNext(); diff --git a/branch.option_debug/function.html b/branch.option_debug/function.html new file mode 100644 index 000000000..7d8271246 --- /dev/null +++ b/branch.option_debug/function.html @@ -0,0 +1,618 @@ + + + + + + + + +JSLint: jslint function + + + +
    JSLint
    + +
    The jslint Function
    +
    +

    JSLint

    +

    JSLint is delivered as a set of files:

    +
    + + + + + + + + + + + + + + +
    FilenameContent
    browser.jsBrowser based UI.
    function.htmlThis page that you are looking at right now.
    help.htmlUsing jslint as a single page JSLint application.
    index.htmlSingle page JSLint application that uses the + js files.
    jslint.jsThe jslint function.
    report.jsThe report object, containing report generator functions for + HTML.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    +
    + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring"(JSLint)"
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    parenttokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    parenttokenThe function in which the variable was declared.variable
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    shebangstringThe first line of text if it started with #!line
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable can be assigned to.variable
    +

    Reports

    +

    The report object contains three functions that all take a + result from the jslint function as input. + report.js has no other dependence on other files.

    + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    +
    + +
    + + + + + +
    + + diff --git a/branch.option_debug/help.html b/branch.option_debug/help.html new file mode 100644 index 000000000..94b431214 --- /dev/null +++ b/branch.option_debug/help.html @@ -0,0 +1,835 @@ + + + + + + + + + +JSLint: Help + + + +
    JSLint
    + +
    Help
    +
    +
    Il semble que la perfection soit atteinte non quand il n’y a + plus rien à ajouter, mais quand il n’y a plus rien à + retrancher.
    +
    + Antoine de Saint-Exupéry +
    +
    + Terre des Hommes (1939) +
    +

    Good Parts

    +

    The Principle of the Good Parts is

    +
    If a feature is sometimes useful and sometimes dangerous and if + there is a better option then always use the better option.
    +

    JSLint is a JavaScript program that looks for problems in + JavaScript programs. It is a code quality tool.

    +

    When C was + a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    +

    As the language matured, the definition of the language was + strengthened to eliminate some insecurities, and compilers got better + at issuing warnings. lint is no longer needed.

    +

    JavaScript is a + young-for-its-age language. It was originally intended to do small tasks in + webpages, tasks for which Java was too heavy and clumsy. But JavaScript is + a surprisingly capable language, and it is now being used in larger + projects. Many of the features that were intended to make the language easy + to use are troublesome when projects become complicated. A lint + for JavaScript is needed: JSLint, a JavaScript syntax checker + and validator.

    +

    JSLint takes a JavaScript source and scans it. If it finds a + problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by the ECMAScript Programming Language + Standard (the strangely named document that governs JavaScript). + JSLint will reject most legal programs. It is a higher + standard.

    +

    JavaScript is a sloppy language, but hidden deep inside there is an elegant, + better language. JSLint helps you to program in that better + language and to avoid most of the slop. JSLint will reject + programs that browsers will accept because JSLint is concerned + with the quality of your code and browsers are not. You should gladly accept + all of JSLint's advice.

    +

    JSLint can operate on JavaScript source + or JSON text.

    +

    ECMAScript Sixth Edition

    +

    Some of + ES6’s features are good, so JSLint will recognize the good + parts of ES6.

    +

    Currently, these features are recognized:

    +
      +
    • The ... ellipsis marker in parameter + lists and argument lists, replacing the arguments object + for variadic functions.
    • +
    • The let statement, which is like the var + statement except that it respects block scope. + You may use let or var but not both.
    • +
    • The const statement is like the let statement + except that it disallows the use of assignment on the variable, although + if the value of the variable is mutable, it can still be mutated. + const is preferred to let. +
    • Destructuring of arrays and objects is allowed in parameter lists and on + the left side of let, and const, but not + var or assignment statements, and not deep destructuring + or eliding.
    • +
    • Enhanced object literals, providing shorter forms for function + declaration and properties that are initialized by variables with the + same name.
    • +
    • The fat arrow => fart functions.
    • +
    • The simplest forms of import and export.
    • +
    • `Megastring` literals, but not nested + `megastring` literals.
    • +
    • New global functions, such as Map, Set, + WeakMap, and WeakSet.
    • +
    • 0b- and 0o- number literals.
    • +
    +

    The most important new feature of ES6 is proper tail calls. This has no + new syntax, so JSLint doesn’t see it. But it makes recursion + much more attractive, which makes loops, particularly for + loops, much less attractive.

    +

    import export

    +

    The ES6 module feature will be an important improvement over JavaScript’s + global variables as a means of linking separate files together. + JSLint recognizes a small but essential subset of the module + syntax.

    +
    +

    import name from stringliteral;

    +

    import {name} from stringliteral;

    +

    export default function () {};

    +

    export default function name() {};

    +

    export default expression;

    +

    export function name() {}

    +

    export name; // where name is const

    +

    export {name};

    +
    +

    Directives

    +

    JSLint provides three directives that may be placed in a + file to manage JSLint’s behavior. Use of these directives is + optional. If they are used, they should be placed in a source file before + the first statement. They are written in the form of a comment, where the + directive name is placed immediately after the opening of the comment before + any whitespace. The three directives are global, + jslint, and property. Directives in a file are + stronger than options selected from the UI or passed with the option object.

    +

    /*global*/

    +

    The /*global*/ directive is used to specify a set of globals + (usually functions and objects containing functions) that are available to + this file. This was commonly used in browsers to link source files together + before ES6 modules appeared. Use of global variables is strongly + discouraged, but unfortunately web browsers require their use. The + /*global*/ directive can only be used when the + Assume a browser option is selected. +

    Each of the names listed will indicate a read-only global variable. The names + are separated by , comma. This directive + should not be used if import or export is used. + This directive inhibits warnings, but it does not declare the names in the + execution environment. +

    For example: +

    /*global +
    ADSAFE, report, jslint
    +*/
    +

    instructs JSLint to not give warnings about the global + variables ADsafe, report, and jslint. + However, if any of those names are expected to be supplied by other files + and those other files fail to do so, then execution errors will result. It + is usually better to use top-level variable declarations instead: +

        var ADSAFE;
    +    var report;
    +    var jslint; 
    +

    Using var in this way allows comparing a global variable to the + undefined value to determine whether it is has been used in the global context.

    +

    /*jslint*/

    +

    The /*jslint*/ directive allows for the control of several + options. These options can also be set from the + JSLint.com user interface.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Tolerate bitwise operatorsbitwisetrue if bitwise operators should be allowed. The + bitwise operators are rarely used in JavaScript programs, so it + is usually more likely that & is a mistyping of + && than that it indicates a bitwise and. + JSLint will give warnings on the bitwise operators + unless this option is selected.
    Assume a browser browsertrue if the standard browser globals should be + predefined. This option will reject the use of + import and export. This option also + disallows the file form of the "use + strict" pragma. It does not supply + self; you will have to request that unnecessary + alias of the dreaded global object yourself. It adds the same + globals as this directive: +
    /*global +
    + clearInterval, clearTimeout, document, event, FileReader, + FormData, history, localStorage, location, name, navigator, + screen, sessionStorage, setInterval, setTimeout, Storage, + URL, window, XMLHttpRequest +
    + */
    +
    Tolerate conversion operatorsconverttrue if !!, + prefix, + and concatenation with "" are allowed. + The Boolean, Number, and + String functions are preferred
    Assume CouchDBcouchtrue if + Couch DB + globals should be predefined. It adds the same globals as this + directive: +
    /*global +
    emit, getRow, isArray, log, provides, registerType, require, + send, start, sum, toJSON
    + */
    Assume in developmentdeveltrue if browser globals that are useful in + development should be predefined, and if debugger + statements and TODO comments + should be allowed. It adds the + same globals as this directive: +
    /*global +
    alert, confirm, console, prompt
    + */
    Be sure to turn this option off before going + into production.
    Tolerate evalevaltrue if eval should be allowed. In the + past, the eval function was the most misused + feature of the language.
    Tolerate for statementfortrue if the for statement should be + allowed. It is almost always better to use the array methods + instead.
    Fudge the line and column numbersfudgetrue if the origin of a text file is line 1 column + 1. Programmers know that number systems start at zero. + Unfortunately, most text editors start numbering lines and + columns at one. JSLint will by default start + numbering at zero. Use this option to start the numbering at one + in its reports.
    Tolerate get and setgetsettrue if accessor properties are allowed in object literals.
    Tolerate long source lineslongtrue if a line can contain more than 80 characters.
    Tolerate multiple variables declarations per statementmultivartrue if a var, let, or + const statement can declare two or more variables + in a single statement.
    Assume Node.jsnodetrue if Node.js globals should be predefined. It + will predefine globals that are used in the Node.js environment. + It adds the same globals as this directive: +
    /*global +
    + Buffer, clearImmediate, clearInterval, clearTimeout, console, exports, + module, process, require, setImmediate, setInterval, setTimeout, URL, + URLSearchParams, __dirname, __filename +
    + */
    Tolerate single quote stringssingletrue if 'single quote + should be allowed to enclose string literals.
    Tolerate this
    thistrue if this should be allowed.
    Tolerate whitespace messwhitetrue if the whitespace rules should be ignored.
    +

    For example:

    +
    /*jslint
    +    bitwise, node
    +*/
    + +

    /*property*/

    +

    The /*property*/ directive is used to declare a list of + property identifiers that are used in the file. Each property name in the + program is looked up in this list. If a name is not found, that indicates an + error, most likely a typing error.

    +

    The list can also be used to evade some of JSLint’s naming + rules.

    +

    JSLint can build the /*property*/ list for you. At + the bottom of its report, JSLint displays a + /*property*/ directive. It contains all of the names that were + used with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. You + can copy the /*property*/ directive to the top of your + script file. JSLint will check the spelling of all property + names against the list. That way, you can have JSLint look for + misspellings for you.

    +

    For example,

    +
    /*property +
    charAt, slice, _$_
    + */
    +

    ignore

    +

    JSLint introduces a new reserved word: ignore. It + is used in parameter lists and in catch clauses to indicate a + parameter that will be ignored. Unused warnings will not be produced for + ignore. +

    function handler(ignore, value) {
    +    return do_something_useful(value);
    +}
    +

    var let const

    +

    JavaScript provides three statements for declaring variables: + var, let, and const. The + var statement suffers from a bad practice called hoisting. The + let statement does not do hoisting and respects block scope. + The const statement is like let except that it + marks the variable (but not its contents) as read only, making it an error + to attempt to assign to the variable. When given a choice, + const is the best, var is the worst.

    +

    JSLint uses the intersection of the var rules and the + let rules, and by doing so avoids the errors related to either. + A name should be declared only once in a function. It should be declared + before it is used. It should not be used outside of the block in which it is + declared. A variable should not have the same name as a variable or + parameter in an outer function. Do not mix var and + let. Declare one name per statement.

    +

    = == ===

    +

    Fortran made a terrible + mistake in using the equality operator as its assignment operator. That + mistake has been replicated in most languages since then. C compounded that + mistake by making == its equality operator. The visual + similarity is a source of errors. JavaScript compounded this further by + making == a type coercing comparison operator that produces + false positive results. This was mitigated by adding the === + operator, leaving the broken == operator in place.

    +

    JSLint attempts to minimize errors by the following rules:

    +

    == is not allowed. This avoids the false positives, and + increases the visual distance between = and ===. + Assignments are not allowed in expression position, and comparisons are not + allowed in statement position. This also reduces confusion. 

    +

    Semicolon

    +

    JavaScript uses a C-like syntax that requires the use of semicolons to + delimit certain statements. JavaScript attempts to make those semicolons + optional with an automatic semicolon insertion mechanism, but it does not + work very well. Automatic semicolon insertion was added to make + things easier for beginners. Unfortunately, it sometimes fails. Do not rely + on it unless you are a beginner.

    +

    JSLint expects that every statement will be followed by + ; except for for, function, + if, switch, try, and + while. JSLint does not expect to see unnecessary + semicolons, the empty statement, or empty blocks.

    +

    function =>

    +

    JavaScript has four syntactic forms for making function objects: function + statements, function expressions, enhanced object literals, and the + => fart operator.

    +

    The function statement creates a variable and assigns the function object to + it. It should be used in a file or function body, but not inside of a block.

    +
    function name(parameters) { +
    statements
    + }
    +

    The function expression unfortunately looks like the function statement. It + may appear anywhere that an expression may appear, but not in statement + position and not in a loop. It produces a function object but does not + create a variable in which to store it.

    +
    function (parameters) { +
    statements
    + }
    +

    The enhanced object literal provides an ES6 shorthand for creating a property + whose value is a function, saving you from having to type + : colon and function.

    +
    { +
    name(parameters) { +
    statements
    + }
    + }
    +

    Finally, ES6 provides an even shorter form of function expression that leaves + out the words function and return:

    +
    (parameters) => + expression
    +

    JSLint requires the parens around the parameters, and forbids a + { left brace after the + => fart to avoid syntactic ambiguity.

    +

    Comma

    +

    The , comma operator is unnecessary and can + mask programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as + an operator. It does not expect to see elided elements in array literals. A + comma should not appear after the last element of an array literal or object + literal.

    +

    Blocks

    +

    JSLint expects blocks with function, + if, switch, while, for, + do, and try statements and nowhere else.

    +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    +

    JavaScript allows an if to be written like this:

    +
    if (condition)
    +    statement;
    +

    That form is known to contribute to mistakes in projects where many + programmers are working on the same code. That is why JSLint + expects the use of a block:

    +
    if (condition) {
    +    statements;
    +}
    +

    Experience shows that this form is more resilient.

    +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call. All other expression statements are considered + to be errors.

    +

    for

    +

    JSLint does not recommend use of the for statement. + Use array methods like forEach instead. The for + option will suppress some warnings. The forms of for that + JSLint accepts are restricted, excluding the new ES6 forms.

    +

    for in

    +

    JSLint does not recommend use of the for + in statement. Use Object.keys instead.

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, it also + loops through all of the properties that were inherited through the + prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written + without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    for (name in object) {
    +    if (object.hasOwnProperty(name)) {
    +        ....
    +    }
    +}
    +

    Note that the above code will fail if the object contains a property + named hasOwnProperty. Use Object.keys instead.

    +

    switch

    +

    A common error in switch statements is to forget to place a + break statement after each case, resulting in unintended + fall-through. JSLint expects that the statement before the next + case or default is one of these: + break, return, or throw.

    +

    with

    +

    The with statement was intended to provide a shorthand in + accessing properties in deeply nested objects. Unfortunately, it behaves + very badly when setting new properties. Never use the with + statement.

    +

    JSLint does not expect to see a with statement.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that + labels will be distinct from vars and parameters.

    +

    Unreachable Code

    +

    JSLint expects that a return, break, + or throw statement will be followed by a + } right brace or case or + default.

    +

    Confusing Pluses and Minuses

    +

    JSLint expects that + will not be followed by + + or ++, and that - will not be + followed by - or --. A misplaced space can turn + + + into ++, an error that is difficult to see. + Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ increment and + -- decrement operators have been known to + contribute to bad code by encouraging excessive trickiness. They are second + only to faulty architecture in enabling to viruses and other security + menaces. Also, preincrement/postincrement confusion can produce off-by-one + errors that are extremely difficult to diagnose. Fortunately, they are also + complete unnecessary. There are better ways to add 1 to a variable.

    +

    It is best to avoid these operators entirely and rely on += and + -= instead.

    +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. + JSLint looks for problems that may cause portability problems. + It also attempts to resolve visual ambiguities by recommending explicit + escapement.

    +

    JavaScript’s syntax for regular expression literals overloads the + / slash character. To avoid ambiguity, + JSLint expects that the character preceding a regular + expression literal is a ( left paren or + = equal or + : colon or + , comma character.

    +

    this

    +
    Having this in the language makes it harder to talk + about the language. It is like pair programming with Abbott and Costello. +
    +

    Avoid using this. Warnings about this can be + suppressed with option.this.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the + new prefix. The new prefix creates a new object + based on the function's prototype, and binds that object to the + function's implied this parameter. If you neglect to use the + new prefix, no new object will be made and this + will be bound to the global object.

    +

    JSLint enforces the convention that constructor functions be + given names with initial uppercase. JSLint does not expect to + see a function invocation with an initial uppercase name unless it has the + new prefix. JSLint does not expect to see the + new prefix used with functions whose names do not start with + initial uppercase.

    +

    JSLint does not expect to see the wrapper forms + new Number, new String, new Boolean.

    +

    JSLint does not expect to see new Object. + Use Object.create(null) instead.

    +

    Whitespace

    +

    JSLint has a specific set of rules about the use of whitespace. + Where possible, these rules are consistent with centuries of good practice + with literary style.

    +

    The indentation increases by 4 spaces when the last token on a line is + { left brace, + [ left bracket, + ( left paren. The matching closing + token will be the first token on a line, restoring the previous + indentation.

    +

    The ternary operator can be visually confusing, so + ? question mark and + : colon always begin a line and increase + the indentation by 4 spaces.

    +
    return (
    +    (the_token.id === "(string)" || the_token.id === "(number)")
    +    ? String(the_token.value)
    +    : the_token.id
    +);
    +

    The word function is always followed with one space.

    +

    Clauses (case, catch, default, + else, finally) are not statements and so should + not be indented like statements.

    +

    Spaces are used to make things that are not invocations look less like + invocations.

    +

    Tabs and spaces should not be mixed. We should pick just one in order to + avoid the problems that come from having both. Personal preference is an + extremely unreliable criterion. Neither offers a powerful advantage over the + other. Fifty years ago, tab had the advantage of consuming less memory, but + Moore's Law has eliminated that advantage. Space has one clear advantage + over tab: there is no reliable standard for how many spaces a tab + represents, but it is universally accepted that a space occupies a space. So + use spaces. You can edit with tabs if you must, but make sure it is spaces + again before you commit. Maybe someday we will finally get a universal + standard for tabs, but until that day comes, the better choice is spaces.

    +

    Unsafe Characters

    +

    There are characters that are handled inconsistently in some browsers, and + so must be escaped when placed in strings.

    +
    \u0000-\u001f
    +\u007f-\u009f
    +\u00ad
    +\u0600-\u0604
    +\u070f
    +\u17b4
    +\u17b5
    +\u200c-\u200f
    +\u2028-\u202f
    +\u2060-\u206f
    +\ufeff
    +\ufff0-\uffff
    +

    Report

    +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    +
      +
    • The line number on which it starts.
    • +
    • The function’s name. In the case of anonymous functions, + JSLint will attempt to + «guess» the name.
    • +
    • The parameters.
    • +
    • Variables: The variables that are declared in the function.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Exceptions: The variables that are declared by catch + clauses of try statements.
    • +
    • Outer: Variables used by this function that are declared in + other functions.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements. Updates are announced at + https://plus.google.com/communities/104441363299760713736.

    +

    Try it

    +

    Try it. Paste your script + into the window and click the + + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    JSLint is written entirely in JavaScript, so it can run + anywhere that JavaScript (or even Java) can run.

    +

    Perfectly fine

    +

    JSLint was designed to reject code that some would consider to + be perfectly fine. The reason for this is that JSLint's + purpose is to help produce programs that are free of error. That is + difficult in any language and is especially hard in JavaScript. + JSLint attempts to help you increase the visual distance + between correct programs and incorrect programs, making the remaining errors + more obvious. JSLint will give warnings about things that are + not necessarily wrong in the current situation, but which have been observed + to mask or obscure errors. Avoid those things when there are better options + available.

    +

    It is dangerous out there. JSLint is here to help.

    +

    Warning

    +

    JSLint will hurt your feelings. Side effects may include + headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, + dry mouth, cleaner code, and a reduced error rate.

    +

    Further Reading

    + +
    +

    + Code Conventions for the JavaScript Programming Language.

    + + + + + +
    + + diff --git a/branch.option_debug/index.html b/branch.option_debug/index.html new file mode 100644 index 000000000..235f44b54 --- /dev/null +++ b/branch.option_debug/index.html @@ -0,0 +1,125 @@ + + + + + + + + + + + +JSLint: The JavaScript Code Quality Tool + + +
    +
    Source
    +
    + + +
    +
    + Warnings +
    +
    +
    + Function Report +
    +
    +
    + Property Directive + +
    +
    + + + + +
    +
    Options +
    Assume... +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Global variables... + +
    +
    +
    +
    + + + + + +
    + + diff --git a/branch.option_debug/jslint.css b/branch.option_debug/jslint.css new file mode 100644 index 000000000..9c1b1a2f9 --- /dev/null +++ b/branch.option_debug/jslint.css @@ -0,0 +1,340 @@ +@font-face { + font-family: 'Programma'; + src: url('Programma.woff') format('woff'); +} +@font-face { + font-family: 'Daley'; + font-weight: bold; + src: url('Daley-Bold.woff') format('woff'); +} + +body { + background-color: antiquewhite; + color: black; + font-family: 'Daley', sans-serif; + margin: 0; + padding: 0; +} + +#JSLINT_TITLEBOX { + font-family: 'Daley', sans-serif; + margin: 0; + margin-left: 3%; + margin-right: 3%; + padding: 0; +} + +#JSLINT_TITLEBOX ul { + float: right; + font-size: 12pt; +} + +#JSLINT_TITLEBOX div { + color: darkslategray; + float: left; + font-size: 48pt; +} + +#JSLINT_ fieldset { + background-color: gainsboro; + border: 0; + clear: both; + margin-bottom: 1em; + margin-left: 3%; + margin-right: 3%; + margin-top: 1em; + padding: 0; + width: auto; +} +#JSLINT_ legend { + background-color: darkslategray; + border: 0; + color: white; + font-size: 100%; + font-style: normal; + font-weight: normal; + margin: 0; + padding-bottom: 0.25em; + padding-left: 0; + padding-right: 0; + padding-top: 0.25em; + text-align: center; + width: 100%; +} +#JSLINT_ button { + background-color: darkslategray; + border: 0; + color: white; + cursor: pointer; + font-family: 'Daley', sans-serif; + font-size: 100%; + font-style: normal; + margin-left: 1em; + margin-right: 1em; + padding-left: 1em; + padding-right: 1em; + padding-bottom: 0.25em; + padding-top: 0.25em; + text-align: center; +} +#JSLINT_ button:hover { + background-color: slategray; + color: white; + border: 0; +} + +#JSLINT_ button:active { + border: 0; + color: black; +} + +#JSLINT_ button:disabled { + background-color: gray; + border: 0; + color: gainsboro; +} + +a:visited { + color: #69565c; +} + +a:link { + color: black; +} + +#JSLINT_ input[type="text"] { + background-color: white; + border: 0; + color: black; + margin-bottom: 0.2em; + margin-right: 0.5em; + padding-left: 0.5em; + padding-right: 0.5em; + text-align: right; + width: 3em; +} + +#JSLINT_ textarea { + border: 0; + background-color: white; + border: 0; + font-family: Programma, monospace; + font-size: 100%; + font-weight: bold; + resize: none; +} + +#JSLINT_ textarea::selection { + background-color: yellow; + color: black; +} + +#JSLINT_NUMBER { + color: darkgray; + display: inline-block; + height: 4in; + margin: 2%; + margin-right: 0; + overflow: hidden; + padding-right: 0.5%; + text-align: right; + white-space: pre; + width: 4%; +} + +#JSLINT_SOURCE { + color: black; + display: inline-block; + font-size: 100%; + height: 4in; + margin: 2%; + margin-left: 0; + margin-right: 0; + overflow: auto; + padding-left: 0.5%; + white-space: pre; + width: 91%; +} + +#JSLINT_PROPERTY { + background-color: honeydew; + border: 0; + margin: 2%; + padding: 0.5%; + white-space: pre; + width: 95%; +} + +#JSLINT_GLOBAL { + white-space: normal; + width: 95%; +} +#JSLINT_ label { + font-family: sans-serif; + font-size: 90%; + padding-left: 0.25em; +} +#JSLINT_OPTIONS>div { + float: left; + margin: 0.5em; +} + +#JSLINT_ address { + color: black; + display: block; + float: right; + font-family: serif; + font-size: 90%; + margin-left: 1em; +} +#JSLINT_ dl { + background-color: cornsilk; + color: white; + margin: 0; + padding-bottom: 2pt; + padding-left: 1em; + padding-right: 1em; + padding-top: 2pt; +} +#JSLINT_ dfn { + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + margin-bottom: 2pt; +} +#JSLINT_ dt { + color: black; + display: block; + float: left; + font-family: serif; + font-size: 75%; + font-style: italic; + margin: 0; + width: 8em; + text-align: right; +} +#JSLINT_ dd { + color: black; + display: block; + font-family: Programma, monospace; + font-weight: bold; + margin-left: 8em; + padding-bottom: 2pt; +} +#JSLINT_WARNINGS>legend { + background-color: indianred; +} +#JSLINT_WARNINGS>div { + background-color: pink; + padding: 1em; +} +#JSLINT_WARNINGS cite { + color: black; + display: block; + font-family: serif; + font-size: 100%; + font-style: normal; + margin-bottom: 4pt; + margin-left: 20pt; + margin-right: 20pt; + margin-top: 4pt; + overflow-x: hidden; +} +#JSLINT_WARNINGS samp { + background-color: lavenderblush; + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + padding: 4pt; + margin-bottom: 0; + margin-left: 16pt; + margin-right: 16pt; + margin-top: 0; + white-space: pre-wrap; +} + +#JSLINT_REPORT center { + margin-top: 1em; +} + +#JSLINT_WARNINGS dl address { + color: black; + display: inline; + float: none; + font-size: 80%; + margin: 0; +} + +#JSLINT_REPORT>div { + padding: 1em; +} + +#JSLINT_ dl.level0 { + color: black; + background-color: white; +} + +#JSLINT_ dl.level1 { + color: black; + background-color: #ffffe0; /* yellow */ + margin-left: 1em; +} + +#JSLINT_ dl.level2 { + color: black; + background-color: #e0ffe0; /* green */ + margin-left: 2em; +} + +#JSLINT_ dl.level3 { + color: black; + background-color: #e0e0ff; /* blue */ + margin-left: 3em; +} + +#JSLINT_ dl.level4 { + background-color: #ffe0ff; /* purple */ + color: black; + margin-left: 4em; +} + +#JSLINT_ dl.level5 { + background-color: #ffe0e0; /* red */ + color: black; + margin-left: 5em; +} + +#JSLINT_ dl.level6 { + background-color: #ffe390; /* orange */ + color: black; + margin-left: 6em; +} + +#JSLINT_ dl.level7 { + background-color: #e0e0e0; /* gray */ + color: black; + margin-left: 7em; +} + +#JSLINT_ dl.level8 { + color: black; + margin-left: 8em; +} + +#JSLINT_ dl.level9 { + color: black; + margin-left: 9em; +} + +.none { + display: none; +} +.center { + text-align: center; +} diff --git a/branch.option_debug/jslint.js b/branch.option_debug/jslint.js new file mode 100644 index 000000000..1048ec98b --- /dev/null +++ b/branch.option_debug/jslint.js @@ -0,0 +1,4932 @@ +// jslint.js +// 2018-09-26 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// jslint(source, option_object, global_array) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze, a string or an array of strings. +// option_object An object whose keys correspond to option names. +// global_array An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all of the functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// 1. If the source is a single string, split it into an array of strings. +// 2. Turn the source into an array of tokens. +// 3. Furcate the tokens into a parse tree. +// 4. Walk the tree, traversing all of the nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// 5. Check the whitespace between the tokens. + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*property + debug, + stack, + a, and, arity, assign, b, bad_assignment_a, bad_directive_a, bad_get, + bad_module_name_a, bad_option_a, bad_property_a, bad_set, bitwise, block, + body, browser, c, calls, catch, charCodeAt, closer, closure, code, column, + complex, concat, constant, context, convert, couch, create, d, dead, + default, devel, directive, directives, disrupt, dot, duplicate_a, edition, + ellipsis, else, empty_block, escape_mega, eval, every, expected_a, + expected_a_at_b_c, expected_a_b, expected_a_b_from_c_d, expected_a_before_b, + expected_a_next_at_b, expected_digits_after_a, expected_four_digits, + expected_identifier_a, expected_line_break_a_b, expected_regexp_factor_a, + expected_space_a_b, expected_statements_a, expected_string_a, + expected_type_string_a, exports, expression, extra, finally, flag, for, + forEach, free, from, froms, fud, fudge, function_in_loop, functions, g, + getset, global, i, id, identifier, import, inc, indexOf, infix_in, init, + initial, isArray, isNaN, join, json, keys, label, label_a, lbp, led, length, + level, line, lines, live, long, loop, m, margin, match, message, + misplaced_a, misplaced_directive_a, missing_browser, missing_m, module, + multivar, naked_block, name, names, nested_comment, new, node, not_label_a, + nr, nud, number_isNaN, ok, open, option, out_of_scope_a, parameters, parent, + pop, property, push, quote, redefinition_a_b, replace, + required_a_optional_b, reserved_a, right, role, search, shebang, signature, + single, slice, some, sort, split, startsWith, statement, stop, strict, + subscript_a, switch, test, this, thru, toString, todo_comment, tokens, + too_long, too_many_digits, tree, try, type, u, unclosed_comment, + unclosed_mega, unclosed_string, undeclared_a, unexpected_a, + unexpected_a_after_b, unexpected_a_before_b, unexpected_at_top_level_a, + unexpected_char_a, unexpected_comment, unexpected_directive_a, + unexpected_expression_a, unexpected_label_a, unexpected_parens, + unexpected_space_a_b, unexpected_statement_a, unexpected_trailing_space, + unexpected_typeof_a, uninitialized_a, unreachable_a, + unregistered_property_a, unsafe, unused_a, use_double, use_open, use_spaces, + use_strict, used, value, var_loop, var_switch, variable, warning, warnings, + weird_condition_a, weird_expression_a, weird_loop, weird_relation_a, white, + wrap_assignment, wrap_condition, wrap_immediate, wrap_parameter, + wrap_regexp, wrap_unary, wrapped, writable, y +*/ + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than {} because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +function populate(array, object = empty(), value = true) { + +// Augment an object by taking property names from an array of strings. + + array.forEach(function (name) { + object[name] = value; + }); + return object; +} + +const allowed_option = { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + bitwise: true, + browser: [ + "caches", "clearInterval", "clearTimeout", "document", "DOMException", + "Element", "Event", "event", "FileReader", "FormData", "history", + "localStorage", "location", "MutationObserver", "name", "navigator", + "screen", "sessionStorage", "setInterval", "setTimeout", "Storage", + "URL", "window", "Worker", "XMLHttpRequest" + ], + couch: [ + "emit", "getRow", "isArray", "log", "provides", "registerType", + "require", "send", "start", "sum", "toJSON" + ], + convert: true, + devel: [ + "alert", "confirm", "console", "prompt" + ], + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + multivar: true, + node: [ + "Buffer", "clearImmediate", "clearInterval", "clearTimeout", + "console", "exports", "module", "process", "require", + "setImmediate", "setInterval", "setTimeout", "URL", + "URLSearchParams", "__dirname", "__filename" + ], + single: true, + this: true, + white: true +}; + +const anticondition = populate([ + "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%", + "typeof", "(number)", "(string)" +]); + +// These are the bitwise operators. + +const bitwiseop = populate([ + "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=", + ">>>", ">>>=" +]); + +const escapeable = populate([ + "\\", "/", "`", "b", "f", "n", "r", "t" +]); + +const opener = { + +// The open and close pairs. + + "(": ")", // paren + "[": "]", // bracket + "{": "}", // brace + "${": "}" // mega +}; + +// The relational operators. + +const relationop = populate([ + "!=", "!==", "==", "===", "<", "<=", ">", ">=" +]); + +// This is the set of infix operators that require a space on each side. + +const spaceop = populate([ + "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/", + "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=", + ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" +]); + +const standard = [ + +// These are the globals that are provided by the language standard. + + "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI", + "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error", + "EvalError", "Float32Array", "Float64Array", "Generator", + "GeneratorFunction", "Int8Array", "Int16Array", "Int32Array", "Intl", + "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat", + "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", + "Set", "String", "Symbol", "SyntaxError", "System", "TypeError", + "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", + "URIError", "WeakMap", "WeakSet" +]; + +const bundle = { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + and: "The '&&' subexpression should be wrapped in parens.", + bad_assignment_a: "Bad assignment to '{a}'.", + bad_directive_a: "Bad directive '{a}'.", + bad_get: "A get function takes no parameters.", + bad_module_name_a: "Bad module name '{a}'.", + bad_option_a: "Bad option '{a}'.", + bad_property_a: "Bad property name '{a}'.", + bad_set: "A set function takes one parameter.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + escape_mega: "Unexpected escapement in mega literal.", + expected_a: "Expected '{a}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: ( + "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'." + ), + expected_a_before_b: "Expected '{a}' before '{b}'.", + expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.", + expected_digits_after_a: "Expected digits after '{a}'.", + expected_four_digits: "Expected four digits after '\\u'.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.", + expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.", + expected_space_a_b: "Expected one space between '{a}' and '{b}'.", + expected_statements_a: "Expected statements before '{a}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + expected_type_string_a: "Expected a type string and instead saw '{a}'.", + function_in_loop: "Don't make functions within a loop.", + infix_in: ( + "Unexpected 'in'. Compare with undefined, " + + "or use the hasOwnProperty method instead." + ), + label_a: "'{a}' is a statement label.", + misplaced_a: "Place '{a}' at the outermost level.", + misplaced_directive_a: ( + "Place the '/*{a}*/' directive before the first statement." + ), + missing_browser: "/*global*/ requires the Assume a browser option.", + missing_m: "Expected 'm' flag on a multiline regular expression.", + naked_block: "Naked block.", + nested_comment: "Nested comment.", + not_label_a: "'{a}' is not a label.", + number_isNaN: "Use Number.isNaN function to compare with NaN.", + out_of_scope_a: "'{a}' is out of scope.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + required_a_optional_b: ( + "Required parameter '{a}' after optional parameter '{b}'." + ), + reserved_a: "Reserved name '{a}'.", + subscript_a: "['{a}'] is better written in dot notation.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line is longer than 80 characters.", + too_many_digits: "Too many digits.", + unclosed_comment: "Unclosed comment.", + unclosed_mega: "Unclosed mega literal.", + unclosed_string: "Unclosed string.", + undeclared_a: "Undeclared '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_a_after_b: "Unexpected '{a}' after '{b}'.", + unexpected_a_before_b: "Unexpected '{a}' before '{b}'.", + unexpected_at_top_level_a: "Expected '{a}' to be in a function.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_directive_a: "When using modules, don't use directive '/*{a}'.", + unexpected_expression_a: ( + "Unexpected expression '{a}' in statement position." + ), + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_parens: "Don't wrap function literals in parens.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_statement_a: ( + "Unexpected statement '{a}' in expression position." + ), + unexpected_trailing_space: "Unexpected trailing space.", + unexpected_typeof_a: ( + "Unexpected 'typeof'. Use '===' to compare directly with {a}." + ), + uninitialized_a: "Uninitialized '{a}'.", + unreachable_a: "Unreachable '{a}'.", + unregistered_property_a: "Unregistered property name '{a}'.", + unsafe: "Unsafe character '{a}'.", + unused_a: "Unused '{a}'.", + use_double: "Use double quotes, not single quotes.", + use_open: ( + "Wrap a ternary expression in parens, " + + "with a line break after the left paren." + ), + use_spaces: "Use spaces, not tabs.", + use_strict: "This function needs a \"use strict\" pragma.", + var_loop: "Don't declare variables in a loop.", + var_switch: "Don't declare variables in a switch.", + weird_condition_a: "Weird condition '{a}'.", + weird_expression_a: "Weird expression '{a}'.", + weird_loop: "Weird loop.", + weird_relation_a: "Weird relation '{a}'.", + wrap_assignment: "Don't wrap assignment statements in parens.", + wrap_condition: "Wrap the condition in parens.", + wrap_immediate: ( + "Wrap an immediate function invocation in parentheses to assist " + + "the reader in understanding that the expression is the result " + + "of a function, and not the function itself." + ), + wrap_parameter: "Wrap the parameter in parens.", + wrap_regexp: "Wrap this regexp in parens to avoid confusion.", + wrap_unary: "Wrap the unary expression in parens." +}; + +// Regular expression literals: + +// supplant {variables} +const rx_supplant = /\{([^{}]*)\}/g; +// carriage return, carriage return linefeed, or linefeed +const rx_crlf = /\n|\r\n?/; +// unsafe characters that are silently deleted by one or more browsers +const rx_unsafe = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; +// identifier +const rx_identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; +const rx_module = /^[a-zA-Z0-9_$:.@\-\/]+$/; +const rx_bad_property = /^_|\$|Sync\$|_$/; +// star slash +const rx_star_slash = /\*\//; +// slash star +const rx_slash_star = /\/\*/; +// slash star or ending slash +const rx_slash_star_or_slash = /\/\*|\/$/; +// uncompleted work comment +const rx_todo = /\b(?:todo|TO\s?DO|HACK)\b/; +// tab +const rx_tab = /\t/g; +// directive +const rx_directive = /^(jslint|property|global)\s+(.*)$/; +const rx_directive_part = /^([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*(.*)$/; +// token (sorry it is so long) +const rx_token = /^((\s+)|([a-zA-Z_$][a-zA-Z0-9_$]*)|[(){}\[\]?,:;'"~`]|=(?:==?|>)?|\.+|[*\/][*\/=]?|\+[=+]?|-[=\-]?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<= "a" && string <= "z\uffff") + || (string >= "A" && string <= "Z\uffff") + ); +} + +function supplant(string, object) { + return string.replace(rx_supplant, function (found, filling) { + const replacement = object[filling]; + return ( + replacement !== undefined + ? replacement + : found + ); + }); +} + +let anon; // The guessed name for anonymous functions. +let blockage; // The current block. +let block_stack; // The stack of blocks. +let declared_globals; // The object containing the global declarations. +let directives; // The directive comments. +let directive_mode; // true if directives are still allowed. +let early_stop; // true if JSLint cannot finish. +let exports; // The exported names and values. +let froms; // The array collecting all import-from strings. +let fudge; // true if the natural numbers start with 1. +let functionage; // The current function. +let functions; // The array containing all of the functions. +let global; // The global object; the outermost context. +let json_mode; // true if parsing JSON. +let lines; // The array containing source lines. +let module_mode; // true if import or export was used. +let next_token; // The next token to be examined in the parse. +let option; // The options parameter. +let property; // The object containing the tallied property names. +let mega_mode; // true if currently parsing a megastring literal. +let shebang; // true if a #! was seen on the first line. +let stack; // The stack of functions. +let syntax; // The object containing the parser. +let token; // The current token being examined in the parse. +let token_nr; // The number of the next token. +let tokens; // The array of tokens. +let tenure; // The predefined property registry. +let tree; // The abstract parse tree. +let var_mode; // "var" if using var; "let" if using let. +let warnings; // The array collecting all generated warnings. + +// Error reportage functions: + +function artifact(the_token) { + +// Return a string representing an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); +} + +function artifact_line(the_token) { + +// Return the fudged line number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.line + fudge; +} + +function artifact_column(the_token) { + +// Return the fudged column number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.from + fudge; +} + +function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + const warning = { // ~~ + name: "JSLintError", + column: column, + line: line, + code: code + }; + if (a !== undefined) { + warning.a = a; + } + if (b !== undefined) { + warning.b = b; + } + if (c !== undefined) { + warning.c = c; + } + if (d !== undefined) { + warning.d = d; + } + warning.message = supplant(bundle[code] || code, warning); + +// Debug stack-trace of warning. + + if (option.debug) { + warning.stack = new Error().stack; + } + warnings.push(warning); + return warning; +} + +function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); +} + +function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + if (the_token.warning === undefined) { + the_token.warning = warn_at( + code, + the_token.line, + the_token.from, + a || artifact(the_token), + b, + c, + d + ); + return the_token.warning; + } +} + +function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); +} + +// Tokenize: + +function tokenize(source) { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + +// If the source is not an array, then it is split into lines at the +// carriage return/linefeed. + + lines = ( + Array.isArray(source) + ? source + : source.split(rx_crlf) + ); + tokens = []; + + let char; // a popular character + let column = 0; // the column number of the next character + let first; // the first token + let from; // the starting column number of the token + let line = -1; // the line number of the next character + let nr = 0; // the next token number + let previous = global; // the previous token including comments + let prior = global; // the previous token excluding comments + let mega_from; // the starting column of megastring + let mega_line; // the starting line of megastring + let regexp_seen; // regular expression literal seen on this line + let snippet; // a piece of string + let source_line = ""; // the remaining line source string + let whole_line = ""; // the whole line source string + + if (lines[0].startsWith("#!")) { + line = 0; + shebang = true; + } + + function next_line() { + +// Put the next line of source in source_line. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + let at; + if ( + !option.long + && whole_line.length > 80 + && !json_mode + && first + && !regexp_seen + ) { + warn_at("too_long", line, 80); + } + column = 0; + line += 1; + regexp_seen = false; + whole_line = lines[line]; + source_line = whole_line; + if (source_line !== undefined) { + at = source_line.search(rx_tab); + if (at >= 0) { + if (!option.white) { + warn_at("use_spaces", line, at + 1); + } + source_line = source_line.replace(rx_tab, " "); + } + at = source_line.search(rx_unsafe); + if (at >= 0) { + warn_at( + "unsafe", + line, + column + at, + "U+" + source_line.charCodeAt(at).toString(16) + ); + } + if (!option.white && source_line.slice(-1) === " ") { + warn_at( + "unexpected_trailing_space", + line, + source_line.length - 1 + ); + } + } + return source_line; + } + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions snip, next_char, prev_char, +// some_digits, and escape help in the parsing of literals. + + function snip() { + +// Remove the last character from snippet. + + snippet = snippet.slice(0, -1); + } + + function next_char(match) { + +// Get the next character from the source line. Remove it from the source_line, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + return stop_at( + ( + char === "" + ? "expected_a" + : "expected_a_b" + ), + line, + column - 1, + match, + char + ); + } + if (source_line) { + char = source_line[0]; + source_line = source_line.slice(1); + snippet += char; + } else { + char = ""; + snippet += " "; + } + column += 1; + return char; + } + + function back_char() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the source_line. + + if (snippet) { + char = snippet.slice(-1); + source_line = char + source_line; + column -= 1; + snip(); + } else { + char = ""; + } + return char; + } + + function some_digits(rx, quiet) { + const result = source_line.match(rx); + if (result) { + char = result[1]; + column += char.length; + source_line = result[2]; + snippet += char; + } else { + char = ""; + if (!quiet) { + warn_at( + "expected_digits_after_a", + line, + column, + snippet + ); + } + } + return char.length; + } + + function escape(extra) { + next_char("\\"); + if (escapeable[char] === true) { + return next_char(); + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "u") { + if (next_char("u") === "{") { + if (json_mode) { + warn_at("unexpected_a", line, column - 1, char); + } + if (some_digits(rx_hexs) > 5) { + warn_at("too_many_digits", line, column - 1); + } + if (next_char() !== "}") { + stop_at("expected_a_before_b", line, column, "}", char); + } + return next_char(); + } + back_char(); + if (some_digits(rx_hexs, true) < 4) { + warn_at("expected_four_digits", line, column - 1); + } + return; + } + if (extra && extra.indexOf(char) >= 0) { + return next_char(); + } + warn_at("unexpected_a_before_b", line, column - 2, "\\", char); + } + + function make(id, value, identifier) { + +// Make the token object and append it to the tokens list. + + const the_token = { + from: from, + id: id, + identifier: Boolean(identifier), + line: line, + nr: nr, + thru: column + }; + tokens[nr] = the_token; + nr += 1; + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + directive_mode = false; + } + +// If the token is to have a value, give it one. + + if (value !== undefined) { + the_token.value = value; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option.white. + + if ( + previous.line === line + && previous.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (previous.id === "(comment)" || previous.id === "(regexp)") + ) { + warn( + "expected_space_a_b", + the_token, + artifact(previous), + artifact(the_token) + ); + } + if (previous.id === "." && id === "(number)") { + warn("expected_a_before_b", previous, "0", "."); + } + if (prior.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// The previous token is used to detect adjacency problems. + + previous = the_token; + +// The prior token is a previous token that was not a comment. The prior token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (previous.id !== "(comment)") { + prior = previous; + } + return the_token; + } + + function parse_directive(the_comment, body) { + +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + + const result = body.match(rx_directive_part); + if (result) { + let allowed; + const name = result[1]; + const value = result[2]; + if (the_comment.directive === "jslint") { + allowed = allowed_option[name]; + if ( + typeof allowed === "boolean" + || typeof allowed === "object" + ) { + if ( + value === "" + || value === "true" + || value === undefined + ) { + option[name] = true; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } else if (value === "false") { + option[name] = false; + } else { + warn("bad_option_a", the_comment, name + ":" + value); + } + } else { + warn("bad_option_a", the_comment, name); + } + } else if (the_comment.directive === "property") { + if (tenure === undefined) { + tenure = empty(); + } + tenure[name] = true; + } else if (the_comment.directive === "global") { + if (value) { + warn("bad_option_a", the_comment, name + ":" + value); + } + declared_globals[name] = false; + module_mode = the_comment; + } + return parse_directive(the_comment, result[3]); + } + if (body) { + return stop("bad_directive_a", the_comment, body); + } + } + + function comment(snippet) { + +// Make a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + + const the_comment = make("(comment)", snippet); + if (Array.isArray(snippet)) { + snippet = snippet.join(" "); + } + if (!option.devel && rx_todo.test(snippet)) { + warn("todo_comment", the_comment); + } + const result = snippet.match(rx_directive); + if (result) { + if (!directive_mode) { + warn_at("misplaced_directive_a", line, from, result[1]); + } else { + the_comment.directive = result[1]; + parse_directive(the_comment, result[2]); + } + directives.push(the_comment); + } + return the_comment; + } + + function regexp() { + +// Parse a regular expression literal. + + let multi_mode = false; + let result; + let value; + regexp_seen = true; + + function quantifier() { + +// Match an optional quantifier. + + if (char === "?" || char === "*" || char === "+") { + next_char(); + } else if (char === "{") { + if (some_digits(rx_digits, true) === 0) { + warn_at("expected_a", line, column, "0"); + } + if (next_char() === ",") { + some_digits(rx_digits, true); + next_char(); + } + next_char("}"); + } else { + return; + } + if (char === "?") { + next_char("?"); + } + } + + function subklass() { + +// Match a character in a character class. + + if (char === "\\") { + escape("BbDdSsWw-[]^"); + return true; + } + if ( + char === "" + || char === "[" + || char === "]" + || char === "/" + || char === "^" + || char === "-" + ) { + return false; + } + if (char === " ") { + warn_at("expected_a_b", line, column, "\\u0020", " "); + } else if (char === "`" && mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char(); + return true; + } + + function ranges() { + +// Match a range of subclasses. + + if (subklass()) { + if (char === "-") { + next_char("-"); + if (!subklass()) { + return stop_at( + "unexpected_a", + line, + column - 1, + "-" + ); + } + } + return ranges(); + } + } + + function klass() { + +// Match a class. + + next_char("["); + if (char === "^") { + next_char("^"); + } + (function classy() { + ranges(); + if (char !== "]" && char !== "") { + warn_at( + "expected_a_before_b", + line, + column, + "\\", + char + ); + next_char(); + return classy(); + } + }()); + next_char("]"); + } + + function choice() { + + function group() { + +// Match a group that starts with left paren. + + next_char("("); + if (char === "?") { + next_char("?"); + if (char === "=" || char === "!") { + next_char(); + } else { + next_char(":"); + } + } else if (char === ":") { + warn_at("expected_a_before_b", line, column, "?", ":"); + } + choice(); + next_char(")"); + } + + function factor() { + if ( + char === "" + || char === "/" + || char === "]" + || char === ")" + ) { + return false; + } + if (char === "(") { + group(); + return true; + } + if (char === "[") { + klass(); + return true; + } + if (char === "\\") { + escape("BbDdSsWw^${}[]():=!.-|*+?"); + return true; + } + if ( + char === "?" + || char === "+" + || char === "*" + || char === "}" + || char === "{" + ) { + warn_at( + "expected_a_before_b", + line, + column - 1, + "\\", + char + ); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column - 1, "`"); + } + } else if (char === " ") { + warn_at( + "expected_a_b", + line, + column - 1, + "\\s", + " " + ); + } else if (char === "$") { + if (source_line[0] !== "/") { + multi_mode = true; + } + } else if (char === "^") { + if (snippet !== "^") { + multi_mode = true; + } + } + next_char(); + return true; + } + + function sequence(follow) { + if (factor()) { + quantifier(); + return sequence(true); + } + if (!follow) { + warn_at("expected_regexp_factor_a", line, column, char); + } + } + +// Match a choice (a sequence that can be followed by | and another choice). + + sequence(); + if (char === "|") { + next_char("|"); + return choice(); + } + } + +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + next_char(); + if (char === "=") { + warn_at("expected_a_before_b", line, column, "\\", "="); + } + choice(); + +// Make sure there is a closing slash. + + snip(); + value = snippet; + next_char("/"); + +// Process dangling flag letters. + + const allowed = { + g: true, + i: true, + m: true, + u: true, + y: true + }; + const flag = empty(); + (function make_flag() { + if (is_letter(char)) { + if (allowed[char] !== true) { + warn_at("unexpected_a", line, column, char); + } + allowed[char] = false; + flag[char] = true; + next_char(); + return make_flag(); + } + }()); + back_char(); + if (char === "/" || char === "*") { + return stop_at("unexpected_a", line, from, char); + } + result = make("(regexp)", char); + result.flag = flag; + result.value = value; + if (multi_mode && !flag.m) { + warn_at("missing_m", line, column); + } + return result; + } + + function string(quote) { + +// Make a string token. + + let the_token; + snippet = ""; + next_char(); + + return (function next() { + if (char === quote) { + snip(); + the_token = make("(string)", snippet); + the_token.quote = quote; + return the_token; + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "\\") { + escape(quote); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char("`"); + } else { + next_char(); + } + return next(); + }()); + } + + function frack() { + if (char === ".") { + some_digits(rx_digits); + next_char(); + } + if (char === "E" || char === "e") { + next_char(); + if (char !== "+" && char !== "-") { + back_char(); + } + some_digits(rx_digits); + next_char(); + } + } + + function number() { + if (snippet === "0") { + next_char(); + if (char === ".") { + frack(); + } else if (char === "b") { + some_digits(rx_bits); + next_char(); + } else if (char === "o") { + some_digits(rx_octals); + next_char(); + } else if (char === "x") { + some_digits(rx_hexs); + next_char(); + } + } else { + next_char(); + frack(); + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + return stop_at( + "unexpected_a_after_b", + line, + column - 1, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + back_char(); + return make("(number)", snippet); + } + + function lex() { + let array; + let i = 0; + let j = 0; + let last; + let result; + let the_token; + if (!source_line) { + source_line = next_line(); + from = 0; + return ( + source_line === undefined + ? ( + mega_mode + ? stop_at("unclosed_mega", mega_line, mega_from) + : make("(end)") + ) + : lex() + ); + } + from = column; + result = source_line.match(rx_token); + +// result[1] token +// result[2] whitespace +// result[3] identifier +// result[4] number +// result[5] rest + + if (!result) { + return stop_at( + "unexpected_char_a", + line, + column, + source_line[0] + ); + } + + snippet = result[1]; + column += snippet.length; + source_line = result[5]; + +// Whitespace was matched. Call lex again to get more. + + if (result[2]) { + return lex(); + } + +// The token is an identifier. + + if (result[3]) { + return make(snippet, undefined, true); + } + +// The token is a number. + + if (result[4]) { + return number(snippet); + } + +// The token is a string. + + if (snippet === "\"") { + return string(snippet); + } + if (snippet === "'") { + if (!option.single) { + warn_at("use_double", line, column); + } + return string(snippet); + } + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (snippet === "`") { + if (mega_mode) { + return stop_at("expected_a_b", line, column, "}", "`"); + } + snippet = ""; + mega_from = from; + mega_line = line; + mega_mode = true; + +// Parsing a mega literal is tricky. First make a ` token. + + make("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + (function part() { + const at = source_line.search(rx_mega); + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + if (at < 0) { + snippet += source_line + "\n"; + return ( + next_line() === undefined + ? stop_at("unclosed_mega", mega_line, mega_from) + : part() + ); + } + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + snippet += source_line.slice(0, at); + column += at; + source_line = source_line.slice(at); + if (source_line[0] === "\\") { + stop_at("escape_mega", line, at); + } + make("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then make tokens that will become part of an expression until +// a } token is made. + + if (source_line[0] === "$") { + column += 2; + make("${"); + source_line = source_line.slice(2); + (function expr() { + const id = lex().id; + if (id === "{") { + return stop_at( + "expected_a_b", + line, + column, + "}", + "{" + ); + } + if (id !== "}") { + return expr(); + } + }()); + return part(); + } + }()); + source_line = source_line.slice(1); + column += 1; + mega_mode = false; + return make("`"); + } + +// The token is a // comment. + + if (snippet === "//") { + snippet = source_line; + source_line = ""; + the_token = comment(snippet); + if (mega_mode) { + warn("unexpected_comment", the_token, "`"); + } + return the_token; + } + +// The token is a /* comment. + + if (snippet === "/*") { + array = []; + if (source_line[0] === "/") { + warn_at("unexpected_a", line, column + i, "/"); + } + (function next() { + if (source_line > "") { + i = source_line.search(rx_star_slash); + if (i >= 0) { + return; + } + j = source_line.search(rx_slash_star); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + } + array.push(source_line); + source_line = next_line(); + if (source_line === undefined) { + return stop_at("unclosed_comment", line, column); + } + return next(); + }()); + snippet = source_line.slice(0, i); + j = snippet.search(rx_slash_star_or_slash); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + array.push(snippet); + column += i + 2; + source_line = source_line.slice(i + 2); + return comment(array); + } + +// The token is a slash. + + if (snippet === "/") { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + if (prior.identifier) { + if (!prior.dot) { + if (prior.id === "return") { + return regexp(); + } + if ( + prior.id === "(begin)" + || prior.id === "case" + || prior.id === "delete" + || prior.id === "in" + || prior.id === "instanceof" + || prior.id === "new" + || prior.id === "typeof" + || prior.id === "void" + || prior.id === "yield" + ) { + the_token = regexp(); + return stop("unexpected_a", the_token); + } + } + } else { + last = prior.id[prior.id.length - 1]; + if ("(,=:?[".indexOf(last) >= 0) { + return regexp(); + } + if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) { + the_token = regexp(); + warn("wrap_regexp", the_token); + return the_token; + } + } + if (source_line[0] === "/") { + column += 1; + source_line = source_line.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + } + return make(snippet); + } + + first = lex(); + json_mode = first.id === "{" || first.id === "["; + +// This is the only loop in JSLint. It will turn into a recursive call to lex +// when ES6 has been finished and widely deployed and adopted. + + while (true) { + if (lex().id === "(end)") { + break; + } + } +} + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + +function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!rx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!rx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property[id] === "number") { + property[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (tenure !== undefined) { + if (tenure[id] !== true) { + warn("unregistered_property_a", name); + } + } else { + if (name.identifier && rx_bad_property.test(id)) { + warn("bad_property_a", name); + } + } + property[id] = 1; + } + return id; +} + +function dispense() { + +// Deliver the next token, skipping the comments. + + const cadet = tokens[token_nr]; + token_nr += 1; + if (cadet.id === "(comment)") { + if (json_mode) { + warn("unexpected_a", cadet); + } + return dispense(); + } else { + return cadet; + } +} + +function lookahead() { + +// Look ahead one token without advancing. + + const old_token_nr = token_nr; + const cadet = dispense(true); + token_nr = old_token_nr; + return cadet; +} + +function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if (token.identifier && token.id !== "function") { + anon = token.id; + } else if (token.id === "(string)" && rx_identifier.test(token.value)) { + anon = token.value; + } + +// Attempt to match next_token with an expected id. + + if (id !== undefined && next_token.id !== id) { + return ( + match === undefined + ? stop("expected_a_b", next_token, id, artifact()) + : stop( + "expected_a_b_from_c_d", + next_token, + id, + artifact(match), + artifact_line(match), + artifact(next_token) + ) + ); + } + +// Promote the tokens, skipping comments. + + token = next_token; + next_token = dispense(); + if (next_token.id === "(end)") { + token_nr -= 1; + } +} + +// Parsing of JSON is simple: + +function json_value() { + let negative; + if (next_token.id === "{") { + return (function json_object() { + const brace = next_token; + const object = empty(); + const properties = []; + brace.expression = properties; + advance("{"); + if (next_token.id !== "}") { + (function next() { + let name; + let value; + if (next_token.quote !== "\"") { + warn( + "unexpected_a", + next_token, + next_token.quote + ); + } + name = next_token; + advance("(string)"); + if (object[token.value] !== undefined) { + warn("duplicate_a", token); + } else if (token.value === "__proto__") { + warn("bad_property_a", token); + } else { + object[token.value] = token; + } + advance(":"); + value = json_value(); + value.label = name; + properties.push(value); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("}", brace); + return brace; + }()); + } + if (next_token.id === "[") { + return (function json_array() { + const bracket = next_token; + const elements = []; + bracket.expression = elements; + advance("["); + if (next_token.id !== "]") { + (function next() { + elements.push(json_value()); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]", bracket); + return bracket; + }()); + } + if ( + next_token.id === "true" + || next_token.id === "false" + || next_token.id === "null" + ) { + advance(); + return token; + } + if (next_token.id === "(number)") { + if (!rx_JSON_number.test(next_token.value)) { + warn("unexpected_a"); + } + advance(); + return token; + } + if (next_token.id === "(string)") { + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + advance(); + return token; + } + if (next_token.id === "-") { + negative = next_token; + negative.arity = "unary"; + advance("-"); + advance("(number)"); + negative.expression = token; + return negative; + } + stop("unexpected_a"); +} + +// Now we parse JavaScript. + +function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + const id = name.id; + +// Reserved words may not be enrolled. + + if (syntax[id] !== undefined && id !== "ignore") { + warn("reserved_a", name); + } else { + +// Has the name been enrolled in this context? + + let earlier = functionage.context[id]; + if (earlier) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + +// Has the name been enrolled in an outer context? + + } else { + stack.forEach(function (value) { + const item = value.context[id]; + if (item !== undefined) { + earlier = item; + } + }); + if (earlier) { + if (id === "ignore") { + if (earlier.role === "variable") { + warn("unexpected_a", name); + } + } else { + if ( + ( + role !== "exception" + || earlier.role !== "exception" + ) + && role !== "parameter" + && role !== "function" + ) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + } + } + } + +// Enroll it. + + functionage.context[id] = name; + name.dead = true; + name.parent = functionage; + name.init = false; + name.role = role; + name.used = 0; + name.writable = !readonly; + } + } +} + +function expression(rbp, initial) { + +// This is the heart of the Pratt parser. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// nud Null denotation +// led Left denotation +// lbp Left binding power +// rbp Right binding power + +// It processes a nud (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop. It +// returns the expression's parse tree. + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement, + + if (!initial) { + advance(); + } + the_symbol = syntax[token.id]; + if (the_symbol !== undefined && the_symbol.nud !== undefined) { + left = the_symbol.nud(); + } else if (token.identifier) { + left = token; + left.arity = "variable"; + } else { + return stop("unexpected_a", token); + } + (function right() { + the_symbol = syntax[next_token.id]; + if ( + the_symbol !== undefined + && the_symbol.led !== undefined + && rbp < the_symbol.lbp + ) { + advance(); + left = the_symbol.led(left); + return right(); + } + }()); + return left; +} + +function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = next_token; + let the_value; + the_paren.free = true; + advance("("); + the_value = expression(0); + advance(")"); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (anticondition[the_value.id] === true) { + warn("unexpected_a", the_value); + } + return the_value; +} + +function is_weird(thing) { + return ( + thing.id === "(regexp)" + || thing.id === "{" + || thing.id === "=>" + || thing.id === "function" + || (thing.id === "[" && thing.arity === "unary") + ); +} + +function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + return ( + Array.isArray(b) + && a.length === b.length + && a.every(function (value, index) { + return are_similar(value, b[index]); + }) + ); + } + if (Array.isArray(b)) { + return false; + } + if (a.id === "(number)" && b.id === "(number)") { + return a.value === b.value; + } + let a_string; + let b_string; + if (a.id === "(string)") { + a_string = a.value; + } else if (a.id === "`" && a.constant) { + a_string = a.value[0]; + } + if (b.id === "(string)") { + b_string = b.value; + } else if (b.id === "`" && b.constant) { + b_string = b.value[0]; + } + if (typeof a_string === "string") { + return a_string === b_string; + } + if (is_weird(a) || is_weird(b)) { + return false; + } + if (a.arity === b.arity && a.id === b.id) { + if (a.id === ".") { + return ( + are_similar(a.expression, b.expression) + && are_similar(a.name, b.name) + ); + } + if (a.arity === "unary") { + return are_similar(a.expression, b.expression); + } + if (a.arity === "binary") { + return ( + a.id !== "(" + && are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + ); + } + if (a.arity === "ternary") { + return ( + are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + && are_similar(a.expression[2], b.expression[2]) + ); + } + if (a.arity === "function" && a.arity === "regexp") { + return false; + } + return true; + } + return false; +} + +function semicolon() { + +// Try to match a semicolon. + + if (next_token.id === ";") { + advance(";"); + } else { + warn_at( + "expected_a_b", + token.line, + token.thru, + ";", + artifact(next_token) + ); + } + anon = "anonymous"; +} + +function statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token.identifier && next_token.id === ":") { + the_label = token; + if (the_label.id === "ignore") { + warn("unexpected_a", the_label); + } + advance(":"); + if ( + next_token.id === "do" + || next_token.id === "for" + || next_token.id === "switch" + || next_token.id === "while" + ) { + enroll(the_label, "label", true); + the_label.init = true; + the_label.dead = false; + the_statement = statement(); + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token; + first.statement = true; + the_symbol = syntax[first.id]; + if (the_symbol !== undefined && the_symbol.fud !== undefined) { + the_symbol.disrupt = false; + the_symbol.statement = true; + the_statement = the_symbol.fud(); + } else { + +// It is an expression statement. + + the_statement = expression(0, true); + if (the_statement.wrapped && the_statement.id !== "(") { + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; +} + +function statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const array = []; + (function next(disrupt) { + if ( + next_token.id !== "}" + && next_token.id !== "case" + && next_token.id !== "default" + && next_token.id !== "else" + && next_token.id !== "(end)" + ) { + let a_statement = statement(); + array.push(a_statement); + if (disrupt) { + warn("unreachable_a", a_statement); + } + return next(a_statement.disrupt); + } + }(false)); + return array; +} + +function not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === global) { + warn("unexpected_at_top_level_a", thing); + } +} + +function top_level_only(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== global) { + warn("misplaced_a", the_thing); + } +} + +function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token; + the_block.arity = "statement"; + the_block.body = special === "body"; + +// All top level function bodies should include the "use strict" pragma unless +// the whole file is strict or the file is a module or the function parameters +// use es6 syntax. + + if ( + special === "body" + && stack.length === 1 + && next_token.value === "use strict" + ) { + the_block.strict = next_token; + next_token.statement = true; + advance("(string)"); + advance(";"); + } + stmts = statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option.devel && special !== "ignore") { + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; +} + +function mutation_check(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + warn("bad_assignment_a", the_thing); + return false; + } + return true; +} + +function left_check(left, right) { + +// Warn if the left is not one of these: +// e.b +// e[b] +// e() +// ?: +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !left_check(left.expression[1]) + && !left_check(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right); + return false; + } + return true; +} + +// These functions are used to specify the grammar of our language: + +function symbol(id, bp) { + +// Make a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax[id] = the_symbol; + } + return the_symbol; +} + +function assignment(id) { + +// Make an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led = function (left) { + const the_token = token; + let right; + the_token.arity = "assignment"; + right = expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "pre" + || right.arity === "post" + ) { + warn("unexpected_a", right); + } + mutation_check(left); + return the_token; + }; + return the_symbol; +} + +function constant(id, type, value) { + +// Make a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud = ( + typeof value === "function" + ? value + : function () { + token.constant = true; + if (value !== undefined) { + token.value = value; + } + return token; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; +} + +function infix(id, bp, f) { + +// Make an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, expression(bp)]; + return the_token; + }; + return the_symbol; +} + +function infixr(id, bp) { + +// Make a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + the_token.expression = [left, expression(bp - 1)]; + return the_token; + }; + return the_symbol; +} + +function post(id) { + +// Make one of the post operators. + + const the_symbol = symbol(id, 150); + the_symbol.led = function (left) { + token.expression = left; + token.arity = "post"; + mutation_check(token.expression); + return token; + }; + return the_symbol; +} + +function pre(id) { + +// Make one of the pre operators. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "pre"; + the_token.expression = expression(150); + mutation_check(the_token.expression); + return the_token; + }; + return the_symbol; +} + +function prefix(id, f) { + +// Make a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = expression(150); + return the_token; + }; + return the_symbol; +} + +function stmt(id, f) { + +// Make a statement. + + const the_symbol = symbol(id); + the_symbol.fud = function () { + token.arity = "statement"; + return f(); + }; + return the_symbol; +} + +function ternary(id1, id2) { + +// Make a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led = function (left) { + const the_token = token; + const second = expression(20); + advance(id2); + token.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, expression(10)]; + return the_token; + }; + return the_symbol; +} + +// Begin defining the language. + +syntax = empty(); + +symbol("}"); +symbol(")"); +symbol("]"); +symbol(","); +symbol(";"); +symbol(":"); +symbol("*/"); +symbol("await"); +symbol("case"); +symbol("catch"); +symbol("class"); +symbol("default"); +symbol("else"); +symbol("enum"); +symbol("finally"); +symbol("implements"); +symbol("interface"); +symbol("package"); +symbol("private"); +symbol("protected"); +symbol("public"); +symbol("static"); +symbol("super"); +symbol("void"); +symbol("yield"); + +constant("(number)", "number"); +constant("(regexp)", "regexp"); +constant("(string)", "string"); +constant("arguments", "object", function () { + warn("unexpected_a", token); + return token; +}); +constant("eval", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("false", "boolean", false); +constant("Function", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("ignore", "undefined", function () { + warn("unexpected_a", token); + return token; +}); +constant("Infinity", "number", Infinity); +constant("isFinite", "function", function () { + warn("expected_a_b", token, "Number.isFinite", "isFinite"); + return token; +}); +constant("isNaN", "function", function () { + warn("number_isNaN", token); + return token; +}); +constant("NaN", "number", NaN); +constant("null", "null", null); +constant("this", "object", function () { + if (!option.this) { + warn("unexpected_a", token); + } + return token; +}); +constant("true", "boolean", true); +constant("undefined", "undefined"); + +assignment("="); +assignment("+="); +assignment("-="); +assignment("*="); +assignment("/="); +assignment("%="); +assignment("&="); +assignment("|="); +assignment("^="); +assignment("<<="); +assignment(">>="); +assignment(">>>="); + +infix("||", 40); +infix("&&", 50); +infix("|", 70); +infix("^", 80); +infix("&", 90); +infix("==", 100); +infix("===", 100); +infix("!=", 100); +infix("!==", 100); +infix("<", 110); +infix(">", 110); +infix("<=", 110); +infix(">=", 110); +infix("in", 110); +infix("instanceof", 110); +infix("<<", 120); +infix(">>", 120); +infix(">>>", 120); +infix("+", 130); +infix("-", 130); +infix("*", 140); +infix("/", 140); +infix("%", 140); +infixr("**", 150); +infix("(", 160, function (left) { + const the_paren = token; + let the_argument; + if (left.id !== "function") { + left_check(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (next_token.id !== ")") { + (function next() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + the_paren.free = true; + if (the_argument.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + the_paren.free = false; + } + return the_paren; +}); +infix(".", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("[", 170, function (left) { + const the_token = token; + const the_subscript = expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + const name = survey(the_subscript); + if (rx_identifier.test(name)) { + warn("subscript_a", the_subscript, name); + } + } + left_check(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; +}); +infix("=>", 170, function (left) { + return stop("wrap_parameter", left); +}); + +function do_tick() { + const the_tick = token; + the_tick.value = []; + the_tick.expression = []; + if (next_token.id !== "`") { + (function part() { + advance("(string)"); + the_tick.value.push(token); + if (next_token.id === "${") { + advance("${"); + the_tick.expression.push(expression(0)); + advance("}"); + return part(); + } + }()); + } + advance("`"); + return the_tick; +} + +infix("`", 160, function (left) { + const the_tick = do_tick(); + left_check(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; +}); + +post("++"); +post("--"); +pre("++"); +pre("--"); + +prefix("+"); +prefix("-"); +prefix("~"); +prefix("!"); +prefix("!!"); +prefix("[", function () { + const the_token = token; + the_token.expression = []; + if (next_token.id !== "]") { + (function next() { + let element; + let ellipsis = false; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + element = expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]"); + return the_token; +}); +prefix("/=", function () { + stop("expected_a_b", token, "/\\=", "/="); +}); +prefix("=>", function () { + return stop("expected_a_before_b", token, "()", "=>"); +}); +prefix("new", function () { + const the_new = token; + const right = expression(160); + if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "()", artifact(next_token)); + } + the_new.expression = right; + return the_new; +}); +prefix("typeof"); +prefix("void", function () { + const the_void = token; + warn("unexpected_a", the_void); + the_void.expression = expression(0); + return the_void; +}); + +function parameter_list() { + let complex = false; + const list = []; + let optional; + const signature = ["("]; + if (next_token.id !== ")" && next_token.id !== "(end)") { + (function parameter() { + let ellipsis = false; + let param; + if (next_token.id === "{") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("{"); + signature.push("{"); + (function subparameter() { + let subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (next_token.id === ":") { + advance(":"); + advance(); + token.label = subparam; + subparam = token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + } + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return subparameter(); + } + }()); + list.push(param); + advance("}"); + signature.push("}"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else if (next_token.id === "[") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("["); + signature.push("[]"); + (function subparameter() { + const subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + return subparameter(); + } + }()); + list.push(param); + advance("]"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else { + if (next_token.id === "...") { + complex = true; + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + } + if (!next_token.identifier) { + return stop("expected_identifier_a"); + } + param = next_token; + list.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (next_token.id === "=") { + complex = true; + optional = param; + advance("="); + param.expression = expression(0); + } else { + if (optional !== undefined) { + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } + } + }()); + } + advance(")"); + signature.push(")"); + return [list, signature.join(""), complex]; +} + +function do_function(the_function) { + let name; + if (the_function === undefined) { + the_function = token; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + name = next_token; + enroll(name, "variable", true); + the_function.name = name; + name.init = true; + name.calls = empty(); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + if (next_token.identifier) { + name = next_token; + the_function.name = name; + advance(); + } else { + the_function.name = anon; + } + } + } else { + name = the_function.name; + } + the_function.level = functionage.level + 1; + if (mega_mode) { + warn("unexpected_a", the_function); + } + +// Don't make functions in loops. It is inefficient, and it can lead to scoping +// errors. + + if (functionage.loop > 0) { + warn("function_in_loop", the_function); + } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + the_function.context = empty(); + the_function.finally = 0; + the_function.loop = 0; + the_function.switch = 0; + the_function.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functions.push(the_function); + functionage = the_function; + if (the_function.arity !== "statement" && typeof name === "object") { + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// Parse the parameter list. + + advance("("); + token.free = false; + token.arity = "function"; + const pl = parameter_list(); + functionage.parameters = pl[0]; + functionage.signature = pl[1]; + functionage.complex = pl[2]; + functionage.parameters.forEach(function enroll_parameter(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + name.names.forEach(enroll_parameter); + } + }); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && next_token.line === token.line + ) { + return stop("unexpected_a", next_token); + } + if (next_token.id === "." || next_token.id === "[") { + warn("unexpected_a"); + } + +// Restore the previous context. + + functionage = stack.pop(); + return the_function; +} + +prefix("function", do_function); + +function fart(pl) { + if (next_token.id === ";") { + stop("wrap_assignment", token); + } + advance("=>"); + const the_fart = token; + the_fart.arity = "binary"; + the_fart.name = "=>"; + the_fart.level = functionage.level + 1; + functions.push(the_fart); + if (functionage.loop > 0) { + warn("function_in_loop", the_fart); + } + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + the_fart.context = empty(); + the_fart.finally = 0; + the_fart.loop = 0; + the_fart.switch = 0; + the_fart.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functionage = the_fart; + the_fart.parameters = pl[0]; + the_fart.signature = pl[1]; + the_fart.complex = true; + the_fart.parameters.forEach(function (name) { + enroll(name, "parameter", true); + }); + if (next_token.id === "{") { + warn("expected_a_b", the_fart, "function", "=>"); + the_fart.block = block("body"); + } else { + the_fart.expression = expression(0); + } + functionage = stack.pop(); + return the_fart; +} + +prefix("(", function () { + const the_paren = token; + let the_value; + const cadet = lookahead().id; + +// We can distinguish between a parameter list for => and a wrapped expression +// with one token of lookahead. + + if ( + next_token.id === ")" + || next_token.id === "..." + || (next_token.identifier && (cadet === "," || cadet === "=")) + ) { + the_paren.free = false; + return fart(parameter_list()); + } + the_paren.free = true; + the_value = expression(0); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + if (next_token.id === "=>") { + if (the_value.arity !== "variable") { + if (the_value.id === "{" || the_value.id === "[") { + warn("expected_a_before_b", the_paren, "function", "("); + return stop("expected_a_b", next_token, "{", "=>"); + } + return stop("expected_identifier_a", the_value); + } + the_paren.expression = [the_value]; + return fart([the_paren.expression, "(" + the_value.id + ")"]); + } + return the_value; +}); +prefix("`", do_tick); +prefix("{", function () { + const the_brace = token; + const seen = empty(); + the_brace.expression = []; + if (next_token.id !== "}") { + (function member() { + let extra; + let full; + let id; + let name = next_token; + let value; + advance(); + if ( + (name.id === "get" || name.id === "set") + && next_token.identifier + ) { + if (!option.getset) { + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + next_token.id; + name = next_token; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (next_token.id === "}" || next_token.id === ",") { + if (typeof extra === "string") { + advance("("); + } + value = expression(Infinity, true); + } else if (next_token.id === "(") { + value = do_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + advance("("); + } + advance(":"); + value = expression(0); + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + advance(":"); + value = expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (next_token.id === ",") { + advance(","); + return member(); + } + }()); + } + advance("}"); + return the_brace; +}); + +stmt(";", function () { + warn("unexpected_a", token); + return token; +}); +stmt("{", function () { + warn("naked_block", token); + return block("naked"); +}); +stmt("break", function () { + const the_break = token; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (next_token.identifier && token.line === next_token.line) { + the_label = functionage.context[next_token.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + warn( + (the_label !== undefined && the_label.dead) + ? "out_of_scope_a" + : "not_label_a" + ); + } else { + the_label.used += 1; + } + the_break.label = next_token; + advance(); + } + advance(";"); + return the_break; +}); + +function do_var() { + const the_statement = token; + const is_const = the_statement.id === "const"; + the_statement.names = []; + +// A program may use var or let, but not both. + + if (!is_const) { + if (var_mode === undefined) { + var_mode = the_statement.id; + } else if (the_statement.id !== var_mode) { + warn( + "expected_a_b", + the_statement, + var_mode, + the_statement.id + ); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + warn("var_switch", the_statement); + } + if (functionage.loop > 0 && the_statement.id === "var") { + warn("var_loop", the_statement); + } + (function next() { + if (next_token.id === "{" && the_statement.id !== "var") { + const the_brace = next_token; + the_brace.names = []; + advance("{"); + (function pair() { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + survey(name); + advance(); + if (next_token.id === ":") { + advance(":"); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + next_token.label = name; + the_brace.names.push(next_token); + enroll(next_token, "variable", is_const); + advance(); + } else { + the_brace.names.push(name); + enroll(name, "variable", is_const); + } + if (next_token.id === ",") { + advance(","); + return pair(); + } + }()); + advance("}"); + advance("="); + the_brace.expression = expression(0); + the_statement.names.push(the_brace); + } else if (next_token.id === "[" && the_statement.id !== "var") { + const the_bracket = next_token; + the_bracket.names = []; + advance("["); + (function element() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + advance(); + the_bracket.names.push(name); + enroll(name, "variable", the_statement.id === "const"); + if (ellipsis) { + name.ellipsis = true; + } else if (next_token.id === ",") { + advance(","); + return element(); + } + }()); + advance("]"); + advance("="); + the_bracket.expression = expression(0); + the_statement.names.push(the_bracket); + } else if (next_token.identifier) { + const name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", is_const); + if (next_token.id === "=" || is_const) { + advance("="); + name.init = true; + name.expression = expression(0); + } + the_statement.names.push(name); + } else { + return stop("expected_identifier_a", next_token); + } + if (next_token.id === ",") { + if (!option.multivar) { + warn("expected_a_b", next_token, ";", ","); + } + advance(","); + return next(); + } + }()); + the_statement.open = ( + the_statement.names.length > 1 + && the_statement.line !== the_statement.names[1].line + ); + semicolon(); + return the_statement; +} + +stmt("const", do_var); +stmt("continue", function () { + const the_continue = token; + if (functionage.loop < 1 || functionage.finally > 0) { + warn("unexpected_a", the_continue); + } + not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; +}); +stmt("debugger", function () { + const the_debug = token; + if (!option.devel) { + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; +}); +stmt("delete", function () { + const the_token = token; + const the_value = expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; +}); +stmt("do", function () { + const the_do = token; + not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; +}); +stmt("export", function () { + const the_export = token; + let the_id; + let the_name; + let the_thing; + + function export_id() { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + the_id = next_token.id; + the_name = global.context[the_id]; + if (the_name === undefined) { + warn("unexpected_a"); + } else { + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a"); + } + exports[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + } + + the_export.expression = []; + if (next_token.id === "default") { + if (exports.default !== undefined) { + warn("duplicate_a"); + } + advance("default"); + the_thing = expression(0); + semicolon(); + exports.default = the_thing; + the_export.expression.push(the_thing); + } else { + if (next_token.id === "function") { + the_thing = statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a", the_name); + } + exports[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + warn("unexpected_a"); + } else if (next_token.id === "{") { + advance("{"); + (function loop() { + export_id(); + if (next_token.id === ",") { + advance(","); + return loop(); + } + }()); + advance("}"); + semicolon(); + } else { + export_id(); + if (the_name.writable !== true) { + warn("unexpected_a", token); + } + semicolon(); + } + } + module_mode = true; + return the_export; +}); +stmt("for", function () { + let first; + const the_for = token; + if (!option.for) { + warn("unexpected_a", the_for); + } + not_top_level(the_for); + functionage.loop += 1; + advance("("); + token.free = true; + if (next_token.id === ";") { + return stop("expected_a_b", the_for, "while (", "for (;"); + } + if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + return stop("unexpected_a"); + } + first = expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = expression(0); + advance(";"); + the_for.inc = expression(0); + if (the_for.inc.id === "++") { + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; +}); +stmt("function", do_function); +stmt("if", function () { + let the_else; + const the_if = token; + the_if.expression = condition(); + the_if.block = block(); + if (next_token.id === "else") { + advance("else"); + the_else = token; + the_if.else = ( + next_token.id === "if" + ? statement() + : block() + ); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + the_if.disrupt = true; + } else { + warn("unexpected_a", the_else); + } + } + } + return the_if; +}); +stmt("import", function () { + const the_import = token; + let name; + if (typeof module_mode === "object") { + warn("unexpected_directive_a", module_mode, module_mode.directive); + } + module_mode = true; + if (next_token.identifier) { + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + const names = []; + advance("{"); + if (next_token.id !== "}") { + while (true) { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (next_token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token; + if (!rx_module.test(token.value)) { + warn("bad_module_name_a", token); + } + froms.push(token.value); + semicolon(); + return the_import; +}); +stmt("let", do_var); +stmt("return", function () { + const the_return = token; + not_top_level(the_return); + if (functionage.finally > 0) { + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (next_token.id !== ";" && the_return.line === next_token.line) { + the_return.expression = expression(10); + } + advance(";"); + return the_return; +}); +stmt("switch", function () { + let dups = []; + let last; + let stmts; + const the_cases = []; + let the_disrupt = true; + const the_switch = token; + not_top_level(the_switch); + if (functionage.finally > 0) { + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token.free = true; + the_switch.expression = expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + (function major() { + const the_case = next_token; + the_case.arity = "statement"; + the_case.expression = []; + (function minor() { + advance("case"); + token.switch = true; + const exp = expression(0); + if (dups.some(function (thing) { + return are_similar(thing, exp); + })) { + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (next_token.id === "case") { + return minor(); + } + }()); + stmts = statements(); + if (stmts.length < 1) { + warn("expected_statements_a"); + return; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "break;", + artifact(next_token) + ); + } + if (next_token.id === "case") { + return major(); + } + }()); + dups = undefined; + if (next_token.id === "default") { + const the_default = next_token; + advance("default"); + token.switch = true; + advance(":"); + the_switch.else = statements(); + if (the_switch.else.length < 1) { + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + const the_last = the_switch.else[the_switch.else.length - 1]; + if (the_last.id === "break" && the_last.label === undefined) { + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; +}); +stmt("throw", function () { + const the_throw = token; + the_throw.disrupt = true; + the_throw.expression = expression(10); + semicolon(); + if (functionage.try > 0) { + warn("unexpected_a", the_throw); + } + return the_throw; +}); +stmt("try", function () { + let the_catch; + let the_disrupt; + const the_try = token; + if (functionage.try > 0) { + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (next_token.id === "catch") { + let ignored = "ignore"; + the_catch = next_token; + the_try.catch = the_catch; + advance("catch"); + advance("("); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + if (next_token.id !== "ignore") { + ignored = undefined; + the_catch.name = next_token; + enroll(next_token, "exception", true); + } + advance(); + advance(")"); + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "catch", + artifact(next_token) + ); + + } + if (next_token.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; +}); +stmt("var", do_var); +stmt("while", function () { + const the_while = token; + not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; +}); +stmt("with", function () { + stop("unexpected_a", token); +}); + +ternary("?", ":"); + +// Ambulation of the parse tree. + +function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; +} + +function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all of the +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + } + }; +} + +const posts = empty(); +const pres = empty(); +const preaction = action(pres); +const postaction = action(posts); +const preamble = amble(pres); +const postamble = amble(posts); + +function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.id === "function") { + walk_statement(thing.block); + } + if (thing.arity === "pre" || thing.arity === "post") { + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + warn("unexpected_statement_a", thing); + } + postamble(thing); + } + } +} + +function walk_statement(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_statement); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + ) { + warn( + (thing.id === "(string)" && thing.value === "use strict") + ? "unexpected_a" + : "unexpected_expression_a", + thing + ); + } + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + } +} + +function lookup(thing) { + if (thing.arity === "variable") { + +// Look up the variable in the current context. + + let the_variable = functionage.context[thing.id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable === undefined) { + stack.forEach(function (outer) { + const a_variable = outer.context[thing.id]; + if ( + a_variable !== undefined + && a_variable.role !== "label" + ) { + the_variable = a_variable; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (the_variable === undefined) { + if (declared_globals[thing.id] === undefined) { + warn("undeclared_a", thing); + return; + } + the_variable = { + dead: false, + parent: global, + id: thing.id, + init: true, + role: "variable", + used: 0, + writable: false + }; + global.context[thing.id] = the_variable; + } + the_variable.closure = true; + functionage.context[thing.id] = the_variable; + } else if (the_variable.role === "label") { + warn("label_a", thing); + } + if ( + the_variable.dead + && ( + the_variable.calls === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + ) { + warn("out_of_scope_a", thing); + } + return the_variable; + } +} + +function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); +} + +function preaction_function(thing) { + if (thing.arity === "statement" && blockage.body !== true) { + warn("unexpected_a", thing); + } + if (thing.level === 1) { + if ( + module_mode === true + || global.strict !== undefined + || thing.complex + ) { + if (thing.id !== "=>" && thing.block.strict !== undefined) { + warn("unexpected_a", thing.block.strict); + } + } else { + if (thing.block.strict === undefined) { + warn("use_strict", thing); + } + } + } + stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); +} + +function bitwise_check(thing) { + if (!option.bitwise && bitwiseop[thing.id] === true) { + warn("unexpected_a", thing); + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + warn("unexpected_a", thing); + } +} + +function pop_block() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); +} + +function activate(name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.init = true; + } + } + blockage.live.push(name); +} + +function action_var(thing) { + thing.names.forEach(activate); +} + +preaction("assignment", bitwise_check); +preaction("binary", bitwise_check); +preaction("binary", function (thing) { + if (relationop[thing.id] === true) { + const left = thing.expression[0]; + const right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + warn("expected_string_a", right); + } + } else { + const value = right.value; + if (value === "null" || value === "undefined") { + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + warn("expected_type_string_a", right, value); + } + } + } + } +}); +preaction("binary", "==", function (thing) { + warn("expected_a_b", thing, "===", "=="); +}); +preaction("binary", "!=", function (thing) { + warn("expected_a_b", thing, "!==", "!="); +}); +preaction("binary", "=>", preaction_function); +preaction("binary", "||", function (thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + warn("and", thang); + } + }); +}); +preaction("binary", "(", function (thing) { + const left = thing.expression[0]; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + const parent = functionage.name.parent; + if (parent) { + const left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + && left_variable.dead + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } +}); +preaction("binary", "in", function (thing) { + warn("infix_in", thing); +}); +preaction("binary", "instanceof", function (thing) { + warn("unexpected_a", thing); +}); +preaction("binary", ".", function (thing) { + if (thing.expression.new) { + thing.new = true; + } +}); +preaction("statement", "{", function (thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; +}); +preaction("statement", "for", function (thing) { + if (thing.name !== undefined) { + const the_variable = lookup(thing.name); + if (the_variable !== undefined) { + the_variable.init = true; + if (!the_variable.writable) { + warn("bad_assignment_a", thing.name); + } + } + } + walk_statement(thing.initial); +}); +preaction("statement", "function", preaction_function); +preaction("unary", "~", bitwise_check); +preaction("unary", "function", preaction_function); +preaction("variable", function (thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } +}); + +function init_variable(name) { + const the_variable = lookup(name); + if (the_variable !== undefined) { + if (the_variable.writable) { + the_variable.init = true; + return; + } + } + warn("bad_assignment_a", name); +} + +postaction("assignment", "+=", function (thing) { + let right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } +}); +postaction("assignment", function (thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + if (thing.id === "=") { + if (thing.names !== undefined) { + if (Array.isArray(thing.names)) { + thing.names.forEach(init_variable); + } else { + init_variable(thing.names); + } + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.writable !== true) { + warn("bad_assignment_a", lvalue); + } + } + const right = syntax[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + warn("unexpected_a", thing.expression[1]); + } + } +}); + +function postaction_function(thing) { + delete functionage.finally; + delete functionage.loop; + delete functionage.switch; + delete functionage.try; + functionage = stack.pop(); + if (thing.wrapped) { + warn("unexpected_parens", thing); + } + return pop_block(); +} + +postaction("binary", function (thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || are_similar(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option.convert) { + if (thing.expression[0].value === "") { + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === ".") { + if (thing.expression.id === "RegExp") { + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } +}); +postaction("binary", "&&", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "||", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "=>", postaction_function); +postaction("binary", "(", function (thing) { + let left = thing.expression[0]; + let the_new; + let arg; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option.eval) { + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + warn( + "expected_a_before_b", + left, + "new", + artifact(left) + ); + } + } + } else if (left.id === ".") { + let cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + cack = !cack; + } + if (rx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + warn("unexpected_a", the_new); + } else { + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + const paren = left.expression; + if (paren.id === "(") { + const array = paren.expression; + if (array.length === 1) { + const new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } +}); +postaction("binary", "[", function (thing) { + if (thing.expression[0].id === "RegExp") { + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + warn("weird_expression_a", thing.expression[1]); + } +}); +postaction("statement", "{", pop_block); +postaction("statement", "const", action_var); +postaction("statement", "export", top_level_only); +postaction("statement", "for", function (thing) { + walk_statement(thing.inc); +}); +postaction("statement", "function", postaction_function); +postaction("statement", "import", function (the_thing) { + const name = the_thing.name; + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return top_level_only(the_thing); +}); +postaction("statement", "let", action_var); +postaction("statement", "try", function (thing) { + if (thing.catch !== undefined) { + const the_name = thing.catch.name; + if (the_name !== undefined) { + const the_variable = functionage.context[the_name.id]; + the_variable.dead = false; + the_variable.init = true; + } + walk_statement(thing.catch.block); + } +}); +postaction("statement", "var", action_var); +postaction("ternary", function (thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || are_similar(thing.expression[1], thing.expression[2]) + ) { + warn("unexpected_a", thing); + } else if (are_similar(thing.expression[0], thing.expression[1])) { + warn("expected_a_b", thing, "||", "?"); + } else if (are_similar(thing.expression[0], thing.expression[2])) { + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + warn("wrap_condition", thing.expression[0]); + } +}); +postaction("unary", function (thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option.convert) { + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } +}); +postaction("unary", "function", postaction_function); +postaction("unary", "+", function (thing) { + if (!option.convert) { + warn("expected_a_b", thing, "Number(...)", "+"); + } + const right = thing.expression; + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } +}); + +function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + if (id !== "ignore") { + const name = the_function.context[id]; + if (name.parent === the_function) { + if ( + name.used === 0 + && ( + name.role !== "function" + || name.parent.arity !== "unary" + ) + ) { + warn("unused_a", name); + } else if (!name.init) { + warn("uninitialized_a", name); + } + } + } + }); +} + +function uninitialized_and_unused() { + +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (module_mode === true || option.node) { + delve(global); + } + functions.forEach(delve); +} + +// Go through the token list, looking at usage of whitespace. + +function whitage() { + let closer = "(end)"; + let free = false; + let left = global; + let margin = 0; + let nr_comments_skipped = 0; + let open = true; + let right; + + function expected_at(at) { + warn( + "expected_a_at_b_c", + right, + artifact(right), + fudge + at, + artifact_column(right) + ); + } + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function no_space() { + if (left.line === right.line) { + if (left.thru !== right.from && nr_comments_skipped === 0) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (open) { + const at = ( + free + ? margin + : margin + 8 + ); + if (right.from < at) { + expected_at(at); + } + } else { + if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (free) { + if (right.from < margin) { + expected_at(margin); + } + } else { + const mislaid = ( + stack.length > 0 + ? stack[stack.length - 1].right + : undefined + ); + if (!open && mislaid !== undefined) { + warn( + "expected_a_next_at_b", + mislaid, + artifact(mislaid.id), + margin + 4 + fudge + ); + } else if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + stack = []; + tokens.forEach(function (the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + + const new_closer = opener[left.id]; + if (typeof new_closer === "string") { + if (new_closer !== right.id) { + stack.push({ + closer: closer, + free: free, + margin: margin, + open: open, + right: right + }); + closer = new_closer; + if (left.line !== right.line) { + free = closer === ")" && left.free; + open = true; + margin += 4; + if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (right.switch) { + at_margin(-4); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + free = false; + open = false; + no_space_only(); + } + } else { + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like {]) have already been detected. + + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } + } else { + +// If right is a closer, then pop the previous state. + + if (right.id === closer) { + const previous = stack.pop(); + margin = previous.margin; + if (open && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + closer = previous.closer; + free = previous.free; + open = previous.open; + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-4); + } else if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + one_space(); + } else { + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + at_margin(0); + } else { + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + no_space(); + } else if ( + left.id === "." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + ) { + no_space_only(); + } else if (right.id === ".") { + no_space_only(); + } else if (left.id === ";") { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + one_space_only(); + } else if (right.statement === true) { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.id === "var" + || left.id === "const" + || left.id === "let" + ) { + stack.push({ + closer: closer, + free: free, + margin: margin, + open: open + }); + closer = ";"; + free = false; + open = left.open; + if (open) { + margin = margin + 4; + at_margin(0); + } else { + one_space_only(); + } + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +// The jslint function itself. + +export default function jslint( + source = "", + option_object = empty(), + global_array = [] +) { + try { + warnings = []; + option = Object.assign(empty(), option_object); + anon = "anonymous"; + block_stack = []; + declared_globals = empty(); + directive_mode = true; + directives = []; + early_stop = true; + exports = empty(); + froms = []; + fudge = ( + option.fudge + ? 1 + : 0 + ); + functions = []; + global = { + id: "(global)", + body: true, + context: empty(), + from: 0, + level: 0, + line: 0, + live: [], + loop: 0, + switch: 0, + thru: 0 + }; + blockage = global; + functionage = global; + json_mode = false; + mega_mode = false; + module_mode = false; + next_token = global; + property = empty(); + shebang = false; + stack = []; + tenure = undefined; + token = global; + token_nr = 0; + var_mode = undefined; + populate(standard, declared_globals, false); + populate(global_array, declared_globals, false); + Object.keys(option).forEach(function (name) { + if (option[name] === true) { + const allowed = allowed_option[name]; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } + }); + tokenize(source); + advance(); + if (json_mode) { + tree = json_value(); + advance("(end)"); + } else { + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option.browser) { + if (next_token.id === ";") { + advance(";"); + } + } else { + +// If we are not in a browser, then the file form of strict pragma may be used. + + if ( + next_token.value === "use strict" + ) { + global.strict = next_token; + advance("(string)"); + advance(";"); + } + } + tree = statements(); + advance("(end)"); + functionage = global; + walk_statement(tree); + if (module_mode && global.strict !== undefined) { + warn("unexpected_a", global.strict); + } + if (warnings.length === 0) { + uninitialized_and_unused(); + if (!option.white) { + whitage(); + } + } + } + if (!option.browser) { + directives.forEach(function (comment) { + if (comment.directive === "global") { + warn("missing_browser", comment); + } + }); + } + early_stop = false; + } catch (e) { + if (e.name !== "JSLintError") { + warnings.push(e); + } + } + return { + directives, + edition: "2018-09-26", + exports, + froms, + functions, + global, + id: "(JSLint)", + json: json_mode, + lines, + module: module_mode === true, + ok: warnings.length === 0 && !early_stop, + option, + property, + shebang: ( + shebang + ? lines[0] + : undefined + ), + stop: early_stop, + tokens, + tree, + warnings: warnings.sort(function (a, b) { + return a.line - b.line || a.column - b.column; + }) + }; +}; diff --git a/branch.option_debug/report.js b/branch.option_debug/report.js new file mode 100644 index 000000000..f123e9055 --- /dev/null +++ b/branch.option_debug/report.js @@ -0,0 +1,221 @@ +// report.js +// 2018-07-29 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Generate JSLint HTML reports. + +/*property + closure, column, context, edition, error, exports, filter, forEach, froms, + fudge, function, functions, global, id, isArray, join, json, keys, length, + level, line, lines, message, module, name, names, option, parameters, + parent, property, push, replace, role, signature, sort, stop, warnings +*/ + +const rx_amp = /&/g; +const rx_gt = />/g; +const rx_lt = / with less destructive entities. + + return String( + string + ).replace( + rx_amp, + "&" + ).replace( + rx_lt, + "<" + ).replace( + rx_gt, + ">" + ); +} + +export default { + error: function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + let fudge = Number(Boolean(data.option.fudge)); + let output = []; + if (data.stop) { + output.push("
    JSLint was unable to finish.
    "); + } + data.warnings.forEach(function (warning) { + output.push( + "
    ", + entityify(warning.line + fudge), + ".", + entityify(warning.column + fudge), + "
    ", + entityify(warning.message), + "
    ", + entityify(data.lines[warning.line] || ""), + "" + ); + }); + return output.join(""); + }, + + function: function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + let fudge = Number(Boolean(data.option.fudge)); + let mode = ( + data.module + ? "module" + : "global" + ); + let output = []; + + if (data.json) { + return ( + data.warnings.length === 0 + ? "
    JSON: good.
    " + : "
    JSON: bad.
    " + ); + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + let global = Object.keys(data.global.context).sort(); + let froms = data.froms.sort(); + let exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + let context = the_function.context; + let list = Object.keys(context); + output.push( + "
    ", + entityify(the_function.line + fudge), + "
    ", + ( + the_function.name === "=>" + ? entityify(the_function.signature) + " =>" + : ( + typeof the_function.name === "string" + ? "«" + entityify(the_function.name) + "»" + : "" + entityify(the_function.name.id) + "" + ) + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + let params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.parent === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.parent === the_function + ); + })); + detail("outer", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.parent !== the_function + && the_variable.parent.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].parent.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + output.push( + "
    JSLint edition ", + entityify(data.edition), + "
    " + ); + return output.join(""); + }, + + property: function property_directive(data) { + +// Produce the /*property*/ directive. + + let not_first = false; + let output = ["/*property"]; + let length = 1111; + let properties = Object.keys(data.property); + + if (properties.length > 0) { + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length >= 80) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); + } + } +}; diff --git a/branch.remove_deadcode_expected_a_next_at_b/README b/branch.remove_deadcode_expected_a_next_at_b/README new file mode 100644 index 000000000..f46e91069 --- /dev/null +++ b/branch.remove_deadcode_expected_a_next_at_b/README @@ -0,0 +1,42 @@ +JSLint, The JavaScript Code Quality Tool + +Douglas Crockford +douglas@crockford.com + +2018-05-28 + +jslint.js contains the jslint function. It parses and analyzes a source file, +returning an object with information about the file. It can also take an object +that sets options. + +index.html runs the jslint.js function in a web page. The page also depends +browser.js and report.js and jslint.css. + +jslint.css provides styling for index.html. + +browser.js runs the web user interface. + +report.js generates the results reports in HTML. + +help.html describes JSLint's usage. Please read it. + +function.html describes the jslint function and the results it produces. + +Please direct questions and comments to +https://plus.google.com/communities/104441363299760713736. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[http://www.crockford.com/wrrrld/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. diff --git a/branch.remove_deadcode_expected_a_next_at_b/browser.js b/branch.remove_deadcode_expected_a_next_at_b/browser.js new file mode 100644 index 000000000..5086fed85 --- /dev/null +++ b/branch.remove_deadcode_expected_a_next_at_b/browser.js @@ -0,0 +1,158 @@ +// browser.js +// 2018-06-16 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +/*jslint + browser +*/ + +/*property + checked, create, disable, display, error, focus, forEach, function, + getElementById, innerHTML, join, length, map, onchange, onclick, onscroll, + property, querySelectorAll, scrollTop, select, split, style, title, value +*/ + +import jslint from "./jslint.js"; +import report from "./report.js"; + +// This is the web script companion file for JSLint. It includes code for +// interacting with the browser and displaying the reports. + +let rx_crlf = /\n|\r\n?/; +let rx_separator = /[\s,;'"]+/; + +let fudge_unit = 0; + +const aux = document.getElementById("JSLINT_AUX"); +const boxes = document.querySelectorAll("[type=checkbox]"); +const fudge = document.getElementById("JSLINT_FUDGE"); +const global = document.getElementById("JSLINT_GLOBAL"); +const number = document.getElementById("JSLINT_NUMBER"); +const property = document.getElementById("JSLINT_PROPERTY"); +const property_fieldset = document.getElementById("JSLINT_PROPERTYFIELDSET"); +const report_field = document.getElementById("JSLINT_REPORT"); +const report_list = document.getElementById("JSLINT_REPORT_LIST"); +const source = document.getElementById("JSLINT_SOURCE"); +const select = document.getElementById("JSLINT_SELECT"); +const warnings = document.getElementById("JSLINT_WARNINGS"); +const warnings_list = document.getElementById("JSLINT_WARNINGS_LIST"); + +function show_numbers() { + number.value = source.value.split(rx_crlf).map(function (ignore, index) { + return index + fudge_unit; + }).join("\n"); +} + +function clear() { + aux.style.display = "none"; + number.value = ""; + property.value = ""; + property_fieldset.style.display = "none"; + report_field.style.display = "none"; + report_list.innerHTML = ""; + source.focus(); + source.value = ""; + warnings.style.display = "none"; + warnings_list.innerHTML = ""; +} + +function fudge_change() { + fudge_unit = Number(fudge.checked); + show_numbers(); +} + +function clear_options() { + boxes.forEach(function (node) { + node.checked = false; + }); + fudge_change(); + global.innerHTML = ""; +} + +function call_jslint() { + +// First build the option object. + + let option = Object.create(null); + boxes.forEach(function (node) { + if (node.checked) { + option[node.title] = true; + } + }); + +// Call JSLint with the source text, the options, and the predefined globals. + + let global_string = global.value; + let result = jslint( + source.value, + option, + ( + global_string === "" + ? undefined + : global_string.split(rx_separator) + ) + ); + +// Generate the reports. + + let error_html = report.error(result); + let function_html = report.function(result); + let property_text = report.property(result); + +// Display the reports. + + warnings_list.innerHTML = error_html; + warnings.style.display = ( + error_html.length === 0 + ? "none" + : "block" + ); + + report_list.innerHTML = function_html; + report_field.style.display = "block"; + if (property_text) { + property.value = property_text; + property_fieldset.style.display = "block"; + property.scrollTop = 0; + select.disable = false; + } else { + property_fieldset.style.display = "none"; + select.disable = true; + } + aux.style.display = "block"; + source.select(); +} + +fudge.onchange = fudge_change; + +source.onchange = function (ignore) { + show_numbers(); +}; + +source.onscroll = function () { + let ss = source.scrollTop; + number.scrollTop = ss; + let sn = number.scrollTop; + if (ss > sn) { + show_numbers(); + number.scrollTop = ss; + } +}; + +document.querySelectorAll("[name='JSLint']").forEach(function (node) { + node.onclick = call_jslint; +}); + +document.querySelectorAll("[name='clear']").forEach(function (node) { + node.onclick = clear; +}); + +document.getElementById("JSLINT_SELECT").onclick = function () { + property.focus(); + property.select(); +}; +document.getElementById("JSLINT_CLEAR_OPTIONS").onclick = clear_options; + +fudge_change(); +source.select(); +source.focus(); diff --git a/branch.remove_deadcode_expected_a_next_at_b/cli.js b/branch.remove_deadcode_expected_a_next_at_b/cli.js new file mode 100755 index 000000000..3f558e9f9 --- /dev/null +++ b/branch.remove_deadcode_expected_a_next_at_b/cli.js @@ -0,0 +1,134 @@ +#!/usr/bin/env node +/* + * cli.js + * simple cli-tool to run jslint.js from command-line + * + * instruction: + * 1. save this file as cli.js + * 2. download and save in same directory, latest jslint.js from + * https://github.com/douglascrockford/JSLint/blob/master/jslint.js + * + * example usage: + * $ node cli.js ./jslint.js // jslint local-file + * $ node cli.js https://code.jquery.com/jquery-1.0.0.js // jslint remote-file + */ + + + +/*jslint node */ +/*property + argv, basename, column, concat, end, error, exit, filter, forEach, fudge, + join, jslint, line, lines, main, match, message, on, option, parse, push, + readFile, replace, request, runInNewContext, slice, trim, trimRight, + warnings +*/ +"use strict"; +let chunk_list; +let file; +let fs; +let fudge; +let local; +let modeNext; +let onNext; +let path; +let url; +let vm; +let warning_text; +onNext = function (error, data) { + if (error) { + console.error(error.message || error); + process.exit(1); + } + modeNext += 1; + switch (modeNext) { + case 1: + // run this program only in cli-mode + if (module !== require.main) { + console.error( + "jslint-cli can only be run from the command-line" + ); + return; + } + // require builtins + fs = require("fs"); + path = require("path"); + url = require("url"); + vm = require("vm"); + // init local namespace + local = {}; + if (!process.argv[2]) { + console.error( + "error: no specified\n" + + "usage: " + path.basename(__filename) + " \n" + + "example: " + path.basename(__filename) + " example.js\n" + + "example: " + path.basename(__filename) + + " https://jslint.com/jslint.js" + ); + process.exit(1); + } + // init jslint + fs.readFile(path.join(__dirname, "jslint.js"), "utf8", onNext); + break; + case 2: + // bug-workaround - nodejs does not widely support es-modules + vm.runInNewContext( + data.replace("export default function", "function"), + local + ); + // read data from file + file = process.argv[2]; + console.error("jslint " + file); + data = file.match( + /^(https?):\/\// + ); + if (!data) { + fs.readFile(file, "utf8", onNext); + return; + } + // read data from http/https url + require(data[1]).request(url.parse(file), function (response) { + chunk_list = []; + response + .on("data", function (chunk) { + chunk_list.push(chunk); + }) + .on("end", function () { + onNext(null, String(Buffer.concat(chunk_list))); + }) + .on("error", onNext); + }).on("error", onNext).end(); + break; + case 3: + // jslint data + data = local.jslint(data + // ignore first-line shebang in nodejs scripts + .replace(( + /^#!/ + ), "//")); + fudge = data.option.fudge || 0; + // init warning_text + warning_text = ""; + // print warnings to stderrr + data.warnings + .filter(function (warning) { + return warning && warning.message; + }) + // print only first 10 warnings + .slice(0, 10) + .forEach(function (warning, ii) { + warning_text += ( + (" #" + (ii + 1)).slice(-3) + + " \u001b[31m" + warning.message + "\u001b[39m\n" + + " " + String(data.lines[warning.line] || "").trim() + + "\u001b[90m \/\/ line " + + (warning.line + fudge) + + ", col " + (warning.column + fudge) + + "\u001b[39m\n" + ); + }); + console.error(warning_text.trimRight() || "ok"); + break; + } +}; +modeNext = 0; +onNext(); diff --git a/branch.remove_deadcode_expected_a_next_at_b/function.html b/branch.remove_deadcode_expected_a_next_at_b/function.html new file mode 100644 index 000000000..29de3af46 --- /dev/null +++ b/branch.remove_deadcode_expected_a_next_at_b/function.html @@ -0,0 +1,617 @@ + + + + + + + + +JSLint: jslint function + + + +
    JSLint
    + +
    The jslint Function
    +
    +

    JSLint

    +

    JSLint is delivered as a set of files:

    +
    + + + + + + + + + + + + + + +
    FilenameContent
    browser.jsBrowser based UI.
    function.htmlThis page that you are looking at right now.
    help.htmlUsing jslint as a single page JSLint application.
    index.htmlSingle page JSLint application that uses the + js files.
    jslint.jsThe jslint function.
    report.jsThe report object, containing report generator functions for + HTML.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    +
    + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring"(JSLint)"
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    parenttokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    parenttokenThe function in which the variable was declared.variable
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    shebangstringThe first line of text if it started with #!line
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable can be assigned to.variable
    +

    Reports

    +

    The report object contains three functions that all take a + result from the jslint function as input. + report.js has no other dependence on other files.

    + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    +
    + +
    + + + + + +
    + + diff --git a/branch.remove_deadcode_expected_a_next_at_b/help.html b/branch.remove_deadcode_expected_a_next_at_b/help.html new file mode 100644 index 000000000..babbe3be5 --- /dev/null +++ b/branch.remove_deadcode_expected_a_next_at_b/help.html @@ -0,0 +1,836 @@ + + + + + + + + + +JSLint: Help + + + +
    JSLint
    + +
    Help
    +
    +
    Il semble que la perfection soit atteinte non quand il n’y a + plus rien à ajouter, mais quand il n’y a plus rien à + retrancher.
    +
    + Antoine de Saint-Exupéry +
    +
    + Terre des Hommes (1939) +
    +

    Good Parts

    +

    The Principle of the Good Parts is

    +
    If a feature is sometimes useful and sometimes dangerous and if + there is a better option then always use the better option.
    +

    JSLint is a JavaScript program that looks for problems in + JavaScript programs. It is a code quality tool.

    +

    When C was + a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    +

    As the language matured, the definition of the language was + strengthened to eliminate some insecurities, and compilers got better + at issuing warnings. lint is no longer needed.

    +

    JavaScript is a + young-for-its-age language. It was originally intended to do small tasks in + webpages, tasks for which Java was too heavy and clumsy. But JavaScript is + a surprisingly capable language, and it is now being used in larger + projects. Many of the features that were intended to make the language easy + to use are troublesome when projects become complicated. A lint + for JavaScript is needed: JSLint, a JavaScript syntax checker + and validator.

    +

    JSLint takes a JavaScript source and scans it. If it finds a + problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by the ECMAScript Programming Language + Standard (the strangely named document that governs JavaScript). + JSLint will reject most legal programs. It is a higher + standard.

    +

    JavaScript is a sloppy language, but hidden deep inside there is an elegant, + better language. JSLint helps you to program in that better + language and to avoid most of the slop. JSLint will reject + programs that browsers will accept because JSLint is concerned + with the quality of your code and browsers are not. You should gladly accept + all of JSLint's advice.

    +

    JSLint can operate on JavaScript source + or JSON text.

    +

    ECMAScript Sixth Edition

    +

    Some of + ES6’s features are good, so JSLint will recognize the good + parts of ES6.

    +

    Currently, these features are recognized:

    +
      +
    • The ... ellipsis marker in parameter + lists and argument lists, replacing the arguments object + for variadic functions.
    • +
    • The let statement, which is like the var + statement except that it respects block scope. + You may use let or var but not both.
    • +
    • The const statement is like the let statement + except that it disallows the use of assignment on the variable, although + if the value of the variable is mutable, it can still be mutated. + const is preferred to let. +
    • Destructuring of arrays and objects is allowed in parameter lists and on + the left side of let, and const, but not + var or assignment statements, and not deep destructuring + or eliding.
    • +
    • Enhanced object literals, providing shorter forms for function + declaration and properties that are initialized by variables with the + same name.
    • +
    • The fat arrow => fart functions.
    • +
    • The simplest forms of import and export.
    • +
    • `Megastring` literals, but not nested + `megastring` literals.
    • +
    • New global functions, such as Map, Set, + WeakMap, and WeakSet.
    • +
    • 0b- and 0o- number literals.
    • +
    +

    The most important new feature of ES6 is proper tail calls. This has no + new syntax, so JSLint doesn’t see it. But it makes recursion + much more attractive, which makes loops, particularly for + loops, much less attractive.

    +

    import export

    +

    The ES6 module feature will be an important improvement over JavaScript’s + global variables as a means of linking separate files together. + JSLint recognizes a small but essential subset of the module + syntax.

    +
    +

    import name from stringliteral;

    +

    import {name} from stringliteral;

    +

    export default function () {};

    +

    export default function name() {};

    +

    export default expression;

    +

    export function name() {}

    +

    export name; // where name is const

    +

    export {name};

    +
    +

    Directives

    +

    JSLint provides three directives that may be placed in a + file to manage JSLint’s behavior. Use of these directives is + optional. If they are used, they should be placed in a source file before + the first statement. They are written in the form of a comment, where the + directive name is placed immediately after the opening of the comment before + any whitespace. The three directives are global, + jslint, and property. Directives in a file are + stronger than options selected from the UI or passed with the option object.

    +

    /*global*/

    +

    The /*global*/ directive is used to specify a set of globals + (usually functions and objects containing functions) that are available to + this file. This was commonly used in browsers to link source files together + before ES6 modules appeared. Use of global variables is strongly + discouraged, but unfortunately web browsers require their use. The + /*global*/ directive can only be used when the + Assume a browser option is selected. +

    Each of the names listed will indicate a read-only global variable. The names + are separated by , comma. This directive + should not be used if import or export is used. + This directive inhibits warnings, but it does not declare the names in the + execution environment. +

    For example: +

    /*global +
    ADSAFE, report, jslint
    +*/
    +

    instructs JSLint to not give warnings about the global + variables ADsafe, report, and jslint. + However, if any of those names are expected to be supplied by other files + and those other files fail to do so, then execution errors will result. It + is usually better to use top-level variable declarations instead: +

        var ADSAFE;
    +    var report;
    +    var jslint; 
    +

    Using var in this way allows comparing a global variable to the + undefined value to determine whether it is has been used in the global context.

    +

    /*jslint*/

    +

    The /*jslint*/ directive allows for the control of several + options. These options can also be set from the + JSLint.com user interface.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Tolerate bitwise operatorsbitwisetrue if bitwise operators should be allowed. The + bitwise operators are rarely used in JavaScript programs, so it + is usually more likely that & is a mistyping of + && than that it indicates a bitwise and. + JSLint will give warnings on the bitwise operators + unless this option is selected.
    Assume a browser browsertrue if the standard browser globals should be + predefined. This option will reject the use of + import and export. This option also + disallows the file form of the "use + strict" pragma. It does not supply + self; you will have to request that unnecessary + alias of the dreaded global object yourself. It adds the same + globals as this directive: +
    /*global +
    + clearInterval, clearTimeout, document, event, FileReader, + FormData, history, localStorage, location, name, navigator, + screen, sessionStorage, setInterval, setTimeout, Storage, + URL, window, XMLHttpRequest +
    + */
    +
    Tolerate conversion operatorsconverttrue if !!, + prefix, + and concatenation with "" are allowed. + The Boolean, Number, and + String functions are preferred
    Assume CouchDBcouchtrue if + Couch DB + globals should be predefined. It adds the same globals as this + directive: +
    /*global +
    emit, getRow, isArray, log, provides, registerType, require, + send, start, sum, toJSON
    + */
    Assume in developmentdeveltrue if browser globals that are useful in + development should be predefined, and if debugger + statements and TODO comments + should be allowed. It adds the + same globals as this directive: +
    /*global +
    alert, confirm, console, prompt
    + */
    Be sure to turn this option off before going + into production.
    Tolerate evalevaltrue if eval should be allowed. In the + past, the eval function was the most misused + feature of the language.
    Tolerate for statementfortrue if the for statement should be + allowed. It is almost always better to use the array methods + instead.
    Fudge the line and column numbersfudgetrue if the origin of a text file is line 1 column + 1. Programmers know that number systems start at zero. + Unfortunately, most text editors start numbering lines and + columns at one. JSLint will by default start + numbering at zero. Use this option to start the numbering at one + in its reports.
    Tolerate get and setgetsettrue if accessor properties are allowed in object literals.
    Tolerate long source lineslongtrue if a line can contain more than 80 characters.
    Tolerate multiple variables declarations per statementmultivartrue if a var, let, or + const statement can declare two or more variables + in a single statement.
    Assume Node.jsnodetrue if Node.js globals should be predefined. It + will predefine globals that are used in the Node.js environment. + It adds the same globals as this directive: +
    /*global +
    + Buffer, clearImmediate, clearInterval, clearTimeout, console, exports, + module, process, require, setImmediate, setInterval, setTimeout, URL, + URLSearchParams, __dirname, __filename +
    + */
    Tolerate single quote stringssingletrue if 'single quote + should be allowed to enclose string literals.
    Tolerate this
    thistrue if this should be allowed.
    Tolerate whitespace messwhitetrue if the whitespace rules should be ignored.
    +

    For example:

    +
    /*jslint
    +    bitwise, node
    +*/
    + +

    /*property*/

    +

    The /*property*/ directive is used to declare a list of + property identifiers that are used in the file. Each property name in the + program is looked up in this list. If a name is not found, that indicates an + error, most likely a typing error.

    +

    The list can also be used to evade some of JSLint’s naming + rules.

    +

    JSLint can build the /*property*/ list for you. At + the bottom of its report, JSLint displays a + /*property*/ directive. It contains all of the names that were + used with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. You + can copy the /*property*/ directive to the top of your + script file. JSLint will check the spelling of all property + names against the list. That way, you can have JSLint look for + misspellings for you.

    +

    For example,

    +
    /*property +
    charAt, slice, _$_
    + */
    +

    ignore

    +

    JSLint introduces a new reserved word: ignore. It + is used in parameter lists and in catch clauses to indicate a + parameter that will be ignored. Unused warnings will not be produced for + ignore. +

    function handler(ignore, value) {
    +    return do_something_useful(value);
    +}
    +

    var let const

    +

    JavaScript provides three statements for declaring variables: + var, let, and const. The + var statement suffers from a bad practice called hoisting. The + let statement does not do hoisting and respects block scope. + The const statement is like let except that it + marks the variable (but not its contents) as read only, making it an error + to attempt to assign to the variable. When given a choice, + const is the best, var is the worst.

    +

    JSLint uses the intersection of the var rules and the + let rules, and by doing so avoids the errors related to either. + A name should be declared only once in a function. It should be declared + before it is used. It should not be used outside of the block in which it is + declared. A variable should not have the same name as a variable or + parameter in an outer function. Do not mix var and + let. Declare one name per statement.

    +

    = == ===

    +

    Fortran made a terrible + mistake in using the equality operator as its assignment operator. That + mistake has been replicated in most languages since then. C compounded that + mistake by making == its equality operator. The visual + similarity is a source of errors. JavaScript compounded this further by + making == a type coercing comparison operator that produces + false positive results. This was mitigated by adding the === + operator, leaving the broken == operator in place.

    +

    JSLint attempts to minimize errors by the following rules:

    +

    == is not allowed. This avoids the false positives, and + increases the visual distance between = and ===. + Assignments are not allowed in expression position, and comparisons are not + allowed in statement position. This also reduces confusion. 

    +

    Semicolon

    +

    JavaScript uses a C-like syntax that requires the use of semicolons to + delimit certain statements. JavaScript attempts to make those semicolons + optional with an automatic semicolon insertion mechanism, but it does not + work very well. Automatic semicolon insertion was added to make + things easier for beginners. Unfortunately, it sometimes fails. Do not rely + on it unless you are a beginner.

    +

    JSLint expects that every statement will be followed by + ; except for for, function, + if, switch, try, and + while. JSLint does not expect to see unnecessary + semicolons, the empty statement, or empty blocks.

    +

    function =>

    +

    JavaScript has four syntactic forms for making function objects: function + statements, function expressions, enhanced object literals, and the + => fart operator.

    +

    The function statement creates a variable and assigns the function object to + it. It should be used in a file or function body, but not inside of a block.

    +
    function name(parameters) { +
    statements
    + }
    +

    The function expression unfortunately looks like the function statement. It + may appear anywhere that an expression may appear, but not in statement + position and not in a loop. It produces a function object but does not + create a variable in which to store it.

    +
    function (parameters) { +
    statements
    + }
    +

    The enhanced object literal provides an ES6 shorthand for creating a property + whose value is a function, saving you from having to type + : colon and function.

    +
    { +
    name(parameters) { +
    statements
    + }
    + }
    +

    Finally, ES6 provides an even shorter form of function expression that leaves + out the words function and return:

    +
    (parameters) => + expression
    +

    JSLint requires the parens around the parameters, and forbids a + { left brace after the + => fart to avoid syntactic ambiguity.

    +

    Comma

    +

    The , comma operator is unnecessary and can + mask programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as + an operator. It does not expect to see elided elements in array literals. A + comma should not appear after the last element of an array literal or object + literal.

    +

    Blocks

    +

    JSLint expects blocks with function, + if, switch, while, for, + do, and try statements and nowhere else.

    +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    +

    JavaScript allows an if to be written like this:

    +
    if (condition)
    +    statement;
    +

    That form is known to contribute to mistakes in projects where many + programmers are working on the same code. That is why JSLint + expects the use of a block:

    +
    if (condition) {
    +    statements;
    +}
    +

    Experience shows that this form is more resilient.

    +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call. All other expression statements are considered + to be errors.

    +

    for

    +

    JSLint does not recommend use of the for statement. + Use array methods like forEach instead. The for + option will suppress some warnings. The forms of for that + JSLint accepts are restricted, excluding the new ES6 forms.

    +

    for in

    +

    JSLint does not recommend use of the for + in statement. Use Object.keys instead.

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, it also + loops through all of the properties that were inherited through the + prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written + without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    for (name in object) {
    +    if (object.hasOwnProperty(name)) {
    +        ....
    +    }
    +}
    +

    Note that the above code will fail if the object contains a property + named hasOwnProperty. Use Object.keys instead.

    +

    switch

    +

    A common error in switch statements is to forget to place a + break statement after each case, resulting in unintended + fall-through. JSLint expects that the statement before the next + case or default is one of these: + break, return, or throw.

    +

    with

    +

    The with statement was intended to provide a shorthand in + accessing properties in deeply nested objects. Unfortunately, it behaves + very badly when setting new properties. Never use the with + statement.

    +

    JSLint does not expect to see a with statement.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that + labels will be distinct from vars and parameters.

    +

    Unreachable Code

    +

    JSLint expects that a return, break, + or throw statement will be followed by a + } right brace or case or + default.

    +

    Confusing Pluses and Minuses

    +

    JSLint expects that + will not be followed by + + or ++, and that - will not be + followed by - or --. A misplaced space can turn + + + into ++, an error that is difficult to see. + Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ increment and + -- decrement operators have been known to + contribute to bad code by encouraging excessive trickiness. They are second + only to faulty architecture in enabling to viruses and other security + menaces. Also, preincrement/postincrement confusion can produce off-by-one + errors that are extremely difficult to diagnose. Fortunately, they are also + complete unnecessary. There are better ways to add 1 to a variable.

    +

    It is best to avoid these operators entirely and rely on += and + -= instead.

    +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. + JSLint looks for problems that may cause portability problems. + It also attempts to resolve visual ambiguities by recommending explicit + escapement.

    +

    JavaScript’s syntax for regular expression literals overloads the + / slash character. To avoid ambiguity, + JSLint expects that the character preceding a regular + expression literal is a ( left paren or + = equal or + : colon or + , comma character.

    +

    this

    +
    Having this in the language makes it harder to talk + about the language. It is like pair programming with Abbott and Costello. +
    +

    Avoid using this. Warnings about this can be + suppressed with option.this.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the + new prefix. The new prefix creates a new object + based on the function's prototype, and binds that object to the + function's implied this parameter. If you neglect to use the + new prefix, no new object will be made and this + will be bound to the global object.

    +

    JSLint enforces the convention that constructor functions be + given names with initial uppercase. JSLint does not expect to + see a function invocation with an initial uppercase name unless it has the + new prefix. JSLint does not expect to see the + new prefix used with functions whose names do not start with + initial uppercase.

    +

    JSLint does not expect to see the wrapper forms + new Number, new String, new Boolean.

    +

    JSLint does not expect to see new Object. + Use Object.create(null) instead.

    +

    Whitespace

    +

    JSLint has a specific set of rules about the use of whitespace. + Where possible, these rules are consistent with centuries of good practice + with literary style.

    +

    The indentation increases by 4 spaces when the last token on a line is + { left brace, + [ left bracket, + ( left paren. The matching closing + token will be the first token on a line, restoring the previous + indentation.

    +

    The ternary operator can be visually confusing, so + ? question mark and + : colon always begin a line and increase + the indentation by 4 spaces.

    +
    return (
    +    (the_token.id === "(string)" || the_token.id === "(number)")
    +    ? String(the_token.value)
    +    : the_token.id
    +);
    +

    The word function is always followed with one space.

    +

    Clauses (case, catch, default, + else, finally) are not statements and so should + not be indented like statements.

    +

    Spaces are used to make things that are not invocations look less like + invocations.

    +

    Tabs and spaces should not be mixed. We should pick just one in order to + avoid the problems that come from having both. Personal preference is an + extremely unreliable criterion. Neither offers a powerful advantage over the + other. Fifty years ago, tab had the advantage of consuming less memory, but + Moore's Law has eliminated that advantage. Space has one clear advantage + over tab: there is no reliable standard for how many spaces a tab + represents, but it is universally accepted that a space occupies a space. So + use spaces. You can edit with tabs if you must, but make sure it is spaces + again before you commit. Maybe someday we will finally get a universal + standard for tabs, but until that day comes, the better choice is spaces.

    +

    Unsafe Characters

    +

    There are characters that are handled inconsistently in some browsers, and + so must be escaped when placed in strings.

    +
    \u0000-\u001f
    +\u007f-\u009f
    +\u00ad
    +\u0600-\u0604
    +\u070f
    +\u17b4
    +\u17b5
    +\u200c-\u200f
    +\u2028-\u202f
    +\u2060-\u206f
    +\ufeff
    +\ufff0-\uffff
    +

    Report

    +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    +
      +
    • The line number on which it starts.
    • +
    • The function’s name. In the case of anonymous functions, + JSLint will attempt to + «guess» the name.
    • +
    • The parameters.
    • +
    • Variables: The variables that are declared in the function.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Exceptions: The variables that are declared by catch + clauses of try statements.
    • +
    • Outer: Variables used by this function that are declared in + other functions.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements. Updates are announced at + https://plus.google.com/communities/104441363299760713736.

    +

    Try it

    +

    Try it. Paste your script + into the window and click the + + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    JSLint is written entirely in JavaScript, so it can run + anywhere that JavaScript (or even Java) can run.

    +

    Perfectly fine

    +

    JSLint was designed to reject code that some would consider to + be perfectly fine. The reason for this is that JSLint's + purpose is to help produce programs that are free of error. That is + difficult in any language and is especially hard in JavaScript. + JSLint attempts to help you increase the visual distance + between correct programs and incorrect programs, making the remaining errors + more obvious. JSLint will give warnings about things that are + not necessarily wrong in the current situation, but which have been observed + to mask or obscure errors. Avoid those things when there are better options + available.

    +

    It is dangerous out there. JSLint is here to help.

    +

    Warning

    +

    JSLint will hurt your feelings. Side effects may include + headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, + dry mouth, cleaner code, and a reduced error rate.

    +

    Further Reading

    + +
    +

    + Code Conventions for the JavaScript Programming Language.

    + + + + + +
    + + diff --git a/branch.remove_deadcode_expected_a_next_at_b/index.html b/branch.remove_deadcode_expected_a_next_at_b/index.html new file mode 100644 index 000000000..3d0e4fa4f --- /dev/null +++ b/branch.remove_deadcode_expected_a_next_at_b/index.html @@ -0,0 +1,124 @@ + + + + + + + + + + + +JSLint: The JavaScript Code Quality Tool + + +
    +
    Source
    +
    + + +
    +
    + Warnings +
    +
    +
    + Function Report +
    +
    +
    + Property Directive + +
    +
    + + + + +
    +
    Options +
    Assume... +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Global variables... + +
    +
    +
    +
    + + + + + +
    + + diff --git a/branch.remove_deadcode_expected_a_next_at_b/jslint.css b/branch.remove_deadcode_expected_a_next_at_b/jslint.css new file mode 100644 index 000000000..9c1b1a2f9 --- /dev/null +++ b/branch.remove_deadcode_expected_a_next_at_b/jslint.css @@ -0,0 +1,340 @@ +@font-face { + font-family: 'Programma'; + src: url('Programma.woff') format('woff'); +} +@font-face { + font-family: 'Daley'; + font-weight: bold; + src: url('Daley-Bold.woff') format('woff'); +} + +body { + background-color: antiquewhite; + color: black; + font-family: 'Daley', sans-serif; + margin: 0; + padding: 0; +} + +#JSLINT_TITLEBOX { + font-family: 'Daley', sans-serif; + margin: 0; + margin-left: 3%; + margin-right: 3%; + padding: 0; +} + +#JSLINT_TITLEBOX ul { + float: right; + font-size: 12pt; +} + +#JSLINT_TITLEBOX div { + color: darkslategray; + float: left; + font-size: 48pt; +} + +#JSLINT_ fieldset { + background-color: gainsboro; + border: 0; + clear: both; + margin-bottom: 1em; + margin-left: 3%; + margin-right: 3%; + margin-top: 1em; + padding: 0; + width: auto; +} +#JSLINT_ legend { + background-color: darkslategray; + border: 0; + color: white; + font-size: 100%; + font-style: normal; + font-weight: normal; + margin: 0; + padding-bottom: 0.25em; + padding-left: 0; + padding-right: 0; + padding-top: 0.25em; + text-align: center; + width: 100%; +} +#JSLINT_ button { + background-color: darkslategray; + border: 0; + color: white; + cursor: pointer; + font-family: 'Daley', sans-serif; + font-size: 100%; + font-style: normal; + margin-left: 1em; + margin-right: 1em; + padding-left: 1em; + padding-right: 1em; + padding-bottom: 0.25em; + padding-top: 0.25em; + text-align: center; +} +#JSLINT_ button:hover { + background-color: slategray; + color: white; + border: 0; +} + +#JSLINT_ button:active { + border: 0; + color: black; +} + +#JSLINT_ button:disabled { + background-color: gray; + border: 0; + color: gainsboro; +} + +a:visited { + color: #69565c; +} + +a:link { + color: black; +} + +#JSLINT_ input[type="text"] { + background-color: white; + border: 0; + color: black; + margin-bottom: 0.2em; + margin-right: 0.5em; + padding-left: 0.5em; + padding-right: 0.5em; + text-align: right; + width: 3em; +} + +#JSLINT_ textarea { + border: 0; + background-color: white; + border: 0; + font-family: Programma, monospace; + font-size: 100%; + font-weight: bold; + resize: none; +} + +#JSLINT_ textarea::selection { + background-color: yellow; + color: black; +} + +#JSLINT_NUMBER { + color: darkgray; + display: inline-block; + height: 4in; + margin: 2%; + margin-right: 0; + overflow: hidden; + padding-right: 0.5%; + text-align: right; + white-space: pre; + width: 4%; +} + +#JSLINT_SOURCE { + color: black; + display: inline-block; + font-size: 100%; + height: 4in; + margin: 2%; + margin-left: 0; + margin-right: 0; + overflow: auto; + padding-left: 0.5%; + white-space: pre; + width: 91%; +} + +#JSLINT_PROPERTY { + background-color: honeydew; + border: 0; + margin: 2%; + padding: 0.5%; + white-space: pre; + width: 95%; +} + +#JSLINT_GLOBAL { + white-space: normal; + width: 95%; +} +#JSLINT_ label { + font-family: sans-serif; + font-size: 90%; + padding-left: 0.25em; +} +#JSLINT_OPTIONS>div { + float: left; + margin: 0.5em; +} + +#JSLINT_ address { + color: black; + display: block; + float: right; + font-family: serif; + font-size: 90%; + margin-left: 1em; +} +#JSLINT_ dl { + background-color: cornsilk; + color: white; + margin: 0; + padding-bottom: 2pt; + padding-left: 1em; + padding-right: 1em; + padding-top: 2pt; +} +#JSLINT_ dfn { + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + margin-bottom: 2pt; +} +#JSLINT_ dt { + color: black; + display: block; + float: left; + font-family: serif; + font-size: 75%; + font-style: italic; + margin: 0; + width: 8em; + text-align: right; +} +#JSLINT_ dd { + color: black; + display: block; + font-family: Programma, monospace; + font-weight: bold; + margin-left: 8em; + padding-bottom: 2pt; +} +#JSLINT_WARNINGS>legend { + background-color: indianred; +} +#JSLINT_WARNINGS>div { + background-color: pink; + padding: 1em; +} +#JSLINT_WARNINGS cite { + color: black; + display: block; + font-family: serif; + font-size: 100%; + font-style: normal; + margin-bottom: 4pt; + margin-left: 20pt; + margin-right: 20pt; + margin-top: 4pt; + overflow-x: hidden; +} +#JSLINT_WARNINGS samp { + background-color: lavenderblush; + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + padding: 4pt; + margin-bottom: 0; + margin-left: 16pt; + margin-right: 16pt; + margin-top: 0; + white-space: pre-wrap; +} + +#JSLINT_REPORT center { + margin-top: 1em; +} + +#JSLINT_WARNINGS dl address { + color: black; + display: inline; + float: none; + font-size: 80%; + margin: 0; +} + +#JSLINT_REPORT>div { + padding: 1em; +} + +#JSLINT_ dl.level0 { + color: black; + background-color: white; +} + +#JSLINT_ dl.level1 { + color: black; + background-color: #ffffe0; /* yellow */ + margin-left: 1em; +} + +#JSLINT_ dl.level2 { + color: black; + background-color: #e0ffe0; /* green */ + margin-left: 2em; +} + +#JSLINT_ dl.level3 { + color: black; + background-color: #e0e0ff; /* blue */ + margin-left: 3em; +} + +#JSLINT_ dl.level4 { + background-color: #ffe0ff; /* purple */ + color: black; + margin-left: 4em; +} + +#JSLINT_ dl.level5 { + background-color: #ffe0e0; /* red */ + color: black; + margin-left: 5em; +} + +#JSLINT_ dl.level6 { + background-color: #ffe390; /* orange */ + color: black; + margin-left: 6em; +} + +#JSLINT_ dl.level7 { + background-color: #e0e0e0; /* gray */ + color: black; + margin-left: 7em; +} + +#JSLINT_ dl.level8 { + color: black; + margin-left: 8em; +} + +#JSLINT_ dl.level9 { + color: black; + margin-left: 9em; +} + +.none { + display: none; +} +.center { + text-align: center; +} diff --git a/branch.remove_deadcode_expected_a_next_at_b/jslint.js b/branch.remove_deadcode_expected_a_next_at_b/jslint.js new file mode 100644 index 000000000..199bfc7d7 --- /dev/null +++ b/branch.remove_deadcode_expected_a_next_at_b/jslint.js @@ -0,0 +1,4976 @@ +// jslint.js +// 2018-10-26 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// jslint(source, option_object, global_array) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze, a string or an array of strings. +// option_object An object whose keys correspond to option names. +// global_array An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all of the functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// 1. If the source is a single string, split it into an array of strings. +// 2. Turn the source into an array of tokens. +// 3. Furcate the tokens into a parse tree. +// 4. Walk the tree, traversing all of the nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// 5. Check the whitespace between the tokens. + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*property + a, and, arity, assign, b, bad_assignment_a, bad_directive_a, bad_get, + bad_module_name_a, bad_option_a, bad_property_a, bad_set, bitwise, block, + body, browser, c, calls, catch, charCodeAt, closer, closure, code, column, + complex, concat, constant, context, convert, couch, create, d, dead, + default, devel, directive, directives, disrupt, dot, duplicate_a, edition, + ellipsis, else, empty_block, escape_mega, eval, every, expected_a, + expected_a_at_b_c, expected_a_b, expected_a_b_from_c_d, expected_a_before_b, + expected_digits_after_a, expected_four_digits, + expected_identifier_a, expected_line_break_a_b, expected_regexp_factor_a, + expected_space_a_b, expected_statements_a, expected_string_a, + expected_type_string_a, exports, expression, extra, finally, flag, for, + forEach, free, freeze, freeze_exports, from, froms, fud, fudge, + function_in_loop, functions, g, getset, global, i, id, identifier, import, + inc, indexOf, infix_in, init, initial, isArray, isNaN, join, json, keys, + label, label_a, lbp, led, length, level, line, lines, live, long, loop, m, + margin, match, message, misplaced_a, misplaced_directive_a, missing_browser, + missing_m, module, multivar, naked_block, name, names, nested_comment, new, + node, not_label_a, nr, nud, number_isNaN, ok, open, opening, option, + out_of_scope_a, parameters, parent, pop, property, push, quote, + redefinition_a_b, replace, required_a_optional_b, reserved_a, role, + search, shebang, signature, single, slice, some, sort, split, startsWith, + statement, stop, strict, subscript_a, switch, test, this, thru, toString, + todo_comment, tokens, too_long, too_many_digits, tree, try, type, u, + unclosed_comment, unclosed_mega, unclosed_string, undeclared_a, + unexpected_a, unexpected_a_after_b, unexpected_a_before_b, + unexpected_at_top_level_a, unexpected_char_a, unexpected_comment, + unexpected_directive_a, unexpected_expression_a, unexpected_label_a, + unexpected_parens, unexpected_space_a_b, unexpected_statement_a, + unexpected_trailing_space, unexpected_typeof_a, uninitialized_a, + unreachable_a, unregistered_property_a, unsafe, unused_a, use_double, + use_open, use_spaces, use_strict, used, value, var_loop, var_switch, + variable, warning, warnings, weird_condition_a, weird_expression_a, + weird_loop, weird_relation_a, white, wrap_condition, wrap_immediate, + wrap_parameter, wrap_regexp, wrap_unary, wrapped, writable, y +*/ + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than '{}' because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +function populate(array, object = empty(), value = true) { + +// Augment an object by taking property names from an array of strings. + + array.forEach(function (name) { + object[name] = value; + }); + return object; +} + +const allowed_option = { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + bitwise: true, + browser: [ + "caches", "clearInterval", "clearTimeout", "document", "DOMException", + "Element", "Event", "event", "FileReader", "FormData", "history", + "localStorage", "location", "MutationObserver", "name", "navigator", + "screen", "sessionStorage", "setInterval", "setTimeout", "Storage", + "TextDecoder", "TextEncoder", "URL", "window", "Worker", + "XMLHttpRequest" + ], + couch: [ + "emit", "getRow", "isArray", "log", "provides", "registerType", + "require", "send", "start", "sum", "toJSON" + ], + convert: true, + devel: [ + "alert", "confirm", "console", "prompt" + ], + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + multivar: true, + node: [ + "Buffer", "clearImmediate", "clearInterval", "clearTimeout", + "console", "exports", "module", "process", "require", + "setImmediate", "setInterval", "setTimeout", "TextDecoder", + "TextEncoder", "URL", "URLSearchParams", "__dirname", "__filename" + ], + single: true, + this: true, + white: true +}; + +const anticondition = populate([ + "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%", + "typeof", "(number)", "(string)" +]); + +// These are the bitwise operators. + +const bitwiseop = populate([ + "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=", + ">>>", ">>>=" +]); + +const escapeable = populate([ + "\\", "/", "`", "b", "f", "n", "r", "t" +]); + +const opener = { + +// The open and close pairs. + + "(": ")", // paren + "[": "]", // bracket + "{": "}", // brace + "${": "}" // mega +}; + +// The relational operators. + +const relationop = populate([ + "!=", "!==", "==", "===", "<", "<=", ">", ">=" +]); + +// This is the set of infix operators that require a space on each side. + +const spaceop = populate([ + "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/", + "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=", + ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" +]); + +const standard = [ + +// These are the globals that are provided by the language standard. + + "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI", + "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error", + "EvalError", "Float32Array", "Float64Array", "Generator", + "GeneratorFunction", "Int8Array", "Int16Array", "Int32Array", "Intl", + "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat", + "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", + "Set", "String", "Symbol", "SyntaxError", "System", "TypeError", + "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", + "URIError", "WeakMap", "WeakSet" +]; + +const bundle = { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + and: "The '&&' subexpression should be wrapped in parens.", + bad_assignment_a: "Bad assignment to '{a}'.", + bad_directive_a: "Bad directive '{a}'.", + bad_get: "A get function takes no parameters.", + bad_module_name_a: "Bad module name '{a}'.", + bad_option_a: "Bad option '{a}'.", + bad_property_a: "Bad property name '{a}'.", + bad_set: "A set function takes one parameter.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + escape_mega: "Unexpected escapement in mega literal.", + expected_a: "Expected '{a}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: ( + "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'." + ), + expected_a_before_b: "Expected '{a}' before '{b}'.", + expected_digits_after_a: "Expected digits after '{a}'.", + expected_four_digits: "Expected four digits after '\\u'.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.", + expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.", + expected_space_a_b: "Expected one space between '{a}' and '{b}'.", + expected_statements_a: "Expected statements before '{a}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + expected_type_string_a: "Expected a type string and instead saw '{a}'.", + freeze_exports: ( + "Expected 'Object.freeze('. All export values should be frozen." + ), + function_in_loop: "Don't make functions within a loop.", + infix_in: ( + "Unexpected 'in'. Compare with undefined, " + + "or use the hasOwnProperty method instead." + ), + label_a: "'{a}' is a statement label.", + misplaced_a: "Place '{a}' at the outermost level.", + misplaced_directive_a: ( + "Place the '/*{a}*/' directive before the first statement." + ), + missing_browser: "/*global*/ requires the Assume a browser option.", + missing_m: "Expected 'm' flag on a multiline regular expression.", + naked_block: "Naked block.", + nested_comment: "Nested comment.", + not_label_a: "'{a}' is not a label.", + number_isNaN: "Use Number.isNaN function to compare with NaN.", + out_of_scope_a: "'{a}' is out of scope.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + required_a_optional_b: ( + "Required parameter '{a}' after optional parameter '{b}'." + ), + reserved_a: "Reserved name '{a}'.", + subscript_a: "['{a}'] is better written in dot notation.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line is longer than 80 characters.", + too_many_digits: "Too many digits.", + unclosed_comment: "Unclosed comment.", + unclosed_mega: "Unclosed mega literal.", + unclosed_string: "Unclosed string.", + undeclared_a: "Undeclared '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_a_after_b: "Unexpected '{a}' after '{b}'.", + unexpected_a_before_b: "Unexpected '{a}' before '{b}'.", + unexpected_at_top_level_a: "Expected '{a}' to be in a function.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_directive_a: "When using modules, don't use directive '/*{a}'.", + unexpected_expression_a: ( + "Unexpected expression '{a}' in statement position." + ), + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_parens: "Don't wrap function literals in parens.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_statement_a: ( + "Unexpected statement '{a}' in expression position." + ), + unexpected_trailing_space: "Unexpected trailing space.", + unexpected_typeof_a: ( + "Unexpected 'typeof'. Use '===' to compare directly with {a}." + ), + uninitialized_a: "Uninitialized '{a}'.", + unreachable_a: "Unreachable '{a}'.", + unregistered_property_a: "Unregistered property name '{a}'.", + unsafe: "Unsafe character '{a}'.", + unused_a: "Unused '{a}'.", + use_double: "Use double quotes, not single quotes.", + use_open: ( + "Wrap a ternary expression in parens, " + + "with a line break after the left paren." + ), + use_spaces: "Use spaces, not tabs.", + use_strict: "This function needs a \"use strict\" pragma.", + var_loop: "Don't declare variables in a loop.", + var_switch: "Don't declare variables in a switch.", + weird_condition_a: "Weird condition '{a}'.", + weird_expression_a: "Weird expression '{a}'.", + weird_loop: "Weird loop.", + weird_relation_a: "Weird relation '{a}'.", + wrap_condition: "Wrap the condition in parens.", + wrap_immediate: ( + "Wrap an immediate function invocation in parentheses to assist " + + "the reader in understanding that the expression is the result " + + "of a function, and not the function itself." + ), + wrap_parameter: "Wrap the parameter in parens.", + wrap_regexp: "Wrap this regexp in parens to avoid confusion.", + wrap_unary: "Wrap the unary expression in parens." +}; + +// Regular expression literals: + +// supplant {variables} +const rx_supplant = /\{([^{}]*)\}/g; +// carriage return, carriage return linefeed, or linefeed +const rx_crlf = /\n|\r\n?/; +// unsafe characters that are silently deleted by one or more browsers +const rx_unsafe = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; +// identifier +const rx_identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; +const rx_module = /^[a-zA-Z0-9_$:.@\-\/]+$/; +const rx_bad_property = /^_|\$|Sync\$|_$/; +// star slash +const rx_star_slash = /\*\//; +// slash star +const rx_slash_star = /\/\*/; +// slash star or ending slash +const rx_slash_star_or_slash = /\/\*|\/$/; +// uncompleted work comment +const rx_todo = /\b(?:todo|TO\s?DO|HACK)\b/; +// tab +const rx_tab = /\t/g; +// directive +const rx_directive = /^(jslint|property|global)\s+(.*)$/; +const rx_directive_part = /^([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*(.*)$/; +// token (sorry it is so long) +const rx_token = /^((\s+)|([a-zA-Z_$][a-zA-Z0-9_$]*)|[(){}\[\],:;'"~`]|\?\.?|=(?:==?|>)?|\.+|[*\/][*\/=]?|\+[=+]?|-[=\-]?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<= "a" && string <= "z\uffff") + || (string >= "A" && string <= "Z\uffff") + ); +} + +function supplant(string, object) { + return string.replace(rx_supplant, function (found, filling) { + const replacement = object[filling]; + return ( + replacement !== undefined + ? replacement + : found + ); + }); +} + +let anon; // The guessed name for anonymous functions. +let blockage; // The current block. +let block_stack; // The stack of blocks. +let declared_globals; // The object containing the global declarations. +let directives; // The directive comments. +let directive_mode; // true if directives are still allowed. +let early_stop; // true if JSLint cannot finish. +let exports; // The exported names and values. +let froms; // The array collecting all import-from strings. +let fudge; // true if the natural numbers start with 1. +let functionage; // The current function. +let functions; // The array containing all of the functions. +let global; // The global object; the outermost context. +let json_mode; // true if parsing JSON. +let lines; // The array containing source lines. +let mega_mode; // true if currently parsing a megastring literal. +let module_mode; // true if import or export was used. +let next_token; // The next token to be examined in the parse. +let option; // The options parameter. +let property; // The object containing the tallied property names. +let shebang; // true if a #! was seen on the first line. +let stack; // The stack of functions. +let syntax; // The object containing the parser. +let token; // The current token being examined in the parse. +let token_nr; // The number of the next token. +let tokens; // The array of tokens. +let tenure; // The predefined property registry. +let tree; // The abstract parse tree. +let var_mode; // "var" if using var; "let" if using let. +let warnings; // The array collecting all generated warnings. + +// Error reportage functions: + +function artifact(the_token) { + +// Return a string representing an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); +} + +function artifact_line(the_token) { + +// Return the fudged line number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.line + fudge; +} + +function artifact_column(the_token) { + +// Return the fudged column number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.from + fudge; +} + +function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + const warning = { // ~~ + name: "JSLintError", + column: column, + line: line, + code: code + }; + if (a !== undefined) { + warning.a = a; + } + if (b !== undefined) { + warning.b = b; + } + if (c !== undefined) { + warning.c = c; + } + if (d !== undefined) { + warning.d = d; + } + warning.message = supplant(bundle[code] || code, warning); + warnings.push(warning); + return warning; +} + +function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); +} + +function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + if (the_token.warning === undefined) { + the_token.warning = warn_at( + code, + the_token.line, + the_token.from, + a || artifact(the_token), + b, + c, + d + ); + return the_token.warning; + } +} + +function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); +} + +// Tokenize: + +function tokenize(source) { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + +// If the source is not an array, then it is split into lines at the +// carriage return/linefeed. + + lines = ( + Array.isArray(source) + ? source + : source.split(rx_crlf) + ); + tokens = []; + + let char; // a popular character + let column = 0; // the column number of the next character + let first; // the first token + let from; // the starting column number of the token + let line = -1; // the line number of the next character + let nr = 0; // the next token number + let previous = global; // the previous token including comments + let prior = global; // the previous token excluding comments + let mega_from; // the starting column of megastring + let mega_line; // the starting line of megastring + let regexp_seen; // regular expression literal seen on this line + let snippet; // a piece of string + let source_line = ""; // the remaining line source string + let whole_line = ""; // the whole line source string + + if (lines[0].startsWith("#!")) { + line = 0; + shebang = true; + } + + function next_line() { + +// Put the next line of source in source_line. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + let at; + if ( + !option.long + && whole_line.length > 80 + && !json_mode + && first + && !regexp_seen + ) { + warn_at("too_long", line, 80); + } + column = 0; + line += 1; + regexp_seen = false; + source_line = lines[line]; + whole_line = source_line || ""; + if (source_line !== undefined) { + at = source_line.search(rx_tab); + if (at >= 0) { + if (!option.white) { + warn_at("use_spaces", line, at + 1); + } + source_line = source_line.replace(rx_tab, " "); + } + at = source_line.search(rx_unsafe); + if (at >= 0) { + warn_at( + "unsafe", + line, + column + at, + "U+" + source_line.charCodeAt(at).toString(16) + ); + } + if (!option.white && source_line.slice(-1) === " ") { + warn_at( + "unexpected_trailing_space", + line, + source_line.length - 1 + ); + } + } + return source_line; + } + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions snip, next_char, prev_char, +// some_digits, and escape help in the parsing of literals. + + function snip() { + +// Remove the last character from snippet. + + snippet = snippet.slice(0, -1); + } + + function next_char(match) { + +// Get the next character from the source line. Remove it from the source_line, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + return stop_at( + ( + char === "" + ? "expected_a" + : "expected_a_b" + ), + line, + column - 1, + match, + char + ); + } + if (source_line) { + char = source_line[0]; + source_line = source_line.slice(1); + snippet += char; + } else { + char = ""; + snippet += " "; + } + column += 1; + return char; + } + + function back_char() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the source_line. + + if (snippet) { + char = snippet.slice(-1); + source_line = char + source_line; + column -= 1; + snip(); + } else { + char = ""; + } + return char; + } + + function some_digits(rx, quiet) { + const result = source_line.match(rx); + if (result) { + char = result[1]; + column += char.length; + source_line = result[2]; + snippet += char; + } else { + char = ""; + if (!quiet) { + warn_at( + "expected_digits_after_a", + line, + column, + snippet + ); + } + } + return char.length; + } + + function escape(extra) { + next_char("\\"); + if (escapeable[char] === true) { + return next_char(); + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "u") { + if (next_char("u") === "{") { + if (json_mode) { + warn_at("unexpected_a", line, column - 1, char); + } + if (some_digits(rx_hexs) > 5) { + warn_at("too_many_digits", line, column - 1); + } + if (next_char() !== "}") { + stop_at("expected_a_before_b", line, column, "}", char); + } + return next_char(); + } + back_char(); + if (some_digits(rx_hexs, true) < 4) { + warn_at("expected_four_digits", line, column - 1); + } + return; + } + if (extra && extra.indexOf(char) >= 0) { + return next_char(); + } + warn_at("unexpected_a_before_b", line, column - 2, "\\", char); + } + + function make(id, value, identifier) { + +// Make the token object and append it to the tokens list. + + const the_token = { + from: from, + id: id, + identifier: Boolean(identifier), + line: line, + nr: nr, + thru: column + }; + tokens[nr] = the_token; + nr += 1; + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + directive_mode = false; + } + +// If the token is to have a value, give it one. + + if (value !== undefined) { + the_token.value = value; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option.white. + + if ( + previous.line === line + && previous.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (previous.id === "(comment)" || previous.id === "(regexp)") + ) { + warn( + "expected_space_a_b", + the_token, + artifact(previous), + artifact(the_token) + ); + } + if (previous.id === "." && id === "(number)") { + warn("expected_a_before_b", previous, "0", "."); + } + if (prior.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// The previous token is used to detect adjacency problems. + + previous = the_token; + +// The prior token is a previous token that was not a comment. The prior token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (previous.id !== "(comment)") { + prior = previous; + } + return the_token; + } + + function parse_directive(the_comment, body) { + +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + + const result = body.match(rx_directive_part); + if (result) { + let allowed; + const name = result[1]; + const value = result[2]; + if (the_comment.directive === "jslint") { + allowed = allowed_option[name]; + if ( + typeof allowed === "boolean" + || typeof allowed === "object" + ) { + if ( + value === "" + || value === "true" + || value === undefined + ) { + option[name] = true; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } else if (value === "false") { + option[name] = false; + } else { + warn("bad_option_a", the_comment, name + ":" + value); + } + } else { + warn("bad_option_a", the_comment, name); + } + } else if (the_comment.directive === "property") { + if (tenure === undefined) { + tenure = empty(); + } + tenure[name] = true; + } else if (the_comment.directive === "global") { + if (value) { + warn("bad_option_a", the_comment, name + ":" + value); + } + declared_globals[name] = false; + module_mode = the_comment; + } + return parse_directive(the_comment, result[3]); + } + if (body) { + return stop("bad_directive_a", the_comment, body); + } + } + + function comment(snippet) { + +// Make a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + + const the_comment = make("(comment)", snippet); + if (Array.isArray(snippet)) { + snippet = snippet.join(" "); + } + if (!option.devel && rx_todo.test(snippet)) { + warn("todo_comment", the_comment); + } + const result = snippet.match(rx_directive); + if (result) { + if (!directive_mode) { + warn_at("misplaced_directive_a", line, from, result[1]); + } else { + the_comment.directive = result[1]; + parse_directive(the_comment, result[2]); + } + directives.push(the_comment); + } + return the_comment; + } + + function regexp() { + +// Parse a regular expression literal. + + let multi_mode = false; + let result; + let value; + regexp_seen = true; + + function quantifier() { + +// Match an optional quantifier. + + if (char === "?" || char === "*" || char === "+") { + next_char(); + } else if (char === "{") { + if (some_digits(rx_digits, true) === 0) { + warn_at("expected_a", line, column, "0"); + } + if (next_char() === ",") { + some_digits(rx_digits, true); + next_char(); + } + next_char("}"); + } else { + return; + } + if (char === "?") { + next_char("?"); + } + } + + function subklass() { + +// Match a character in a character class. + + if (char === "\\") { + escape("BbDdSsWw-[]^"); + return true; + } + if ( + char === "" + || char === "[" + || char === "]" + || char === "/" + || char === "^" + || char === "-" + ) { + return false; + } + if (char === " ") { + warn_at("expected_a_b", line, column, "\\u0020", " "); + } else if (char === "`" && mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char(); + return true; + } + + function ranges() { + +// Match a range of subclasses. + + if (subklass()) { + if (char === "-") { + next_char("-"); + if (!subklass()) { + return stop_at( + "unexpected_a", + line, + column - 1, + "-" + ); + } + } + return ranges(); + } + } + + function klass() { + +// Match a class. + + next_char("["); + if (char === "^") { + next_char("^"); + } + (function classy() { + ranges(); + if (char !== "]" && char !== "") { + warn_at( + "expected_a_before_b", + line, + column, + "\\", + char + ); + next_char(); + return classy(); + } + }()); + next_char("]"); + } + + function choice() { + + function group() { + +// Match a group that starts with left paren. + + next_char("("); + if (char === "?") { + next_char("?"); + if (char === "=" || char === "!") { + next_char(); + } else { + next_char(":"); + } + } else if (char === ":") { + warn_at("expected_a_before_b", line, column, "?", ":"); + } + choice(); + next_char(")"); + } + + function factor() { + if ( + char === "" + || char === "/" + || char === "]" + || char === ")" + ) { + return false; + } + if (char === "(") { + group(); + return true; + } + if (char === "[") { + klass(); + return true; + } + if (char === "\\") { + escape("BbDdSsWw^${}[]():=!.-|*+?"); + return true; + } + if ( + char === "?" + || char === "+" + || char === "*" + || char === "}" + || char === "{" + ) { + warn_at( + "expected_a_before_b", + line, + column - 1, + "\\", + char + ); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column - 1, "`"); + } + } else if (char === " ") { + warn_at( + "expected_a_b", + line, + column - 1, + "\\s", + " " + ); + } else if (char === "$") { + if (source_line[0] !== "/") { + multi_mode = true; + } + } else if (char === "^") { + if (snippet !== "^") { + multi_mode = true; + } + } + next_char(); + return true; + } + + function sequence(follow) { + if (factor()) { + quantifier(); + return sequence(true); + } + if (!follow) { + warn_at("expected_regexp_factor_a", line, column, char); + } + } + +// Match a choice (a sequence that can be followed by | and another choice). + + sequence(); + if (char === "|") { + next_char("|"); + return choice(); + } + } + +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + next_char(); + if (char === "=") { + warn_at("expected_a_before_b", line, column, "\\", "="); + } + choice(); + +// Make sure there is a closing slash. + + snip(); + value = snippet; + next_char("/"); + +// Process dangling flag letters. + + const allowed = { + g: true, + i: true, + m: true, + u: true, + y: true + }; + const flag = empty(); + (function make_flag() { + if (is_letter(char)) { + if (allowed[char] !== true) { + warn_at("unexpected_a", line, column, char); + } + allowed[char] = false; + flag[char] = true; + next_char(); + return make_flag(); + } + }()); + back_char(); + if (char === "/" || char === "*") { + return stop_at("unexpected_a", line, from, char); + } + result = make("(regexp)", char); + result.flag = flag; + result.value = value; + if (multi_mode && !flag.m) { + warn_at("missing_m", line, column); + } + return result; + } + + function string(quote) { + +// Make a string token. + + let the_token; + snippet = ""; + next_char(); + + return (function next() { + if (char === quote) { + snip(); + the_token = make("(string)", snippet); + the_token.quote = quote; + return the_token; + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "\\") { + escape(quote); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char("`"); + } else { + next_char(); + } + return next(); + }()); + } + + function frack() { + if (char === ".") { + some_digits(rx_digits); + next_char(); + } + if (char === "E" || char === "e") { + next_char(); + if (char !== "+" && char !== "-") { + back_char(); + } + some_digits(rx_digits); + next_char(); + } + } + + function number() { + if (snippet === "0") { + next_char(); + if (char === ".") { + frack(); + } else if (char === "b") { + some_digits(rx_bits); + next_char(); + } else if (char === "o") { + some_digits(rx_octals); + next_char(); + } else if (char === "x") { + some_digits(rx_hexs); + next_char(); + } + } else { + next_char(); + frack(); + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + return stop_at( + "unexpected_a_after_b", + line, + column - 1, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + back_char(); + return make("(number)", snippet); + } + + function lex() { + let array; + let i = 0; + let j = 0; + let last; + let result; + let the_token; + if (!source_line) { + source_line = next_line(); + from = 0; + return ( + source_line === undefined + ? ( + mega_mode + ? stop_at("unclosed_mega", mega_line, mega_from) + : make("(end)") + ) + : lex() + ); + } + from = column; + result = source_line.match(rx_token); + +// result[1] token +// result[2] whitespace +// result[3] identifier +// result[4] number +// result[5] rest + + if (!result) { + return stop_at( + "unexpected_char_a", + line, + column, + source_line[0] + ); + } + + snippet = result[1]; + column += snippet.length; + source_line = result[5]; + +// Whitespace was matched. Call lex again to get more. + + if (result[2]) { + return lex(); + } + +// The token is an identifier. + + if (result[3]) { + return make(snippet, undefined, true); + } + +// The token is a number. + + if (result[4]) { + return number(snippet); + } + +// The token is a string. + + if (snippet === "\"") { + return string(snippet); + } + if (snippet === "'") { + if (!option.single) { + warn_at("use_double", line, column); + } + return string(snippet); + } + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (snippet === "`") { + if (mega_mode) { + return stop_at("expected_a_b", line, column, "}", "`"); + } + snippet = ""; + mega_from = from; + mega_line = line; + mega_mode = true; + +// Parsing a mega literal is tricky. First make a ` token. + + make("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + (function part() { + const at = source_line.search(rx_mega); + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + if (at < 0) { + snippet += source_line + "\n"; + return ( + next_line() === undefined + ? stop_at("unclosed_mega", mega_line, mega_from) + : part() + ); + } + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + snippet += source_line.slice(0, at); + column += at; + source_line = source_line.slice(at); + if (source_line[0] === "\\") { + stop_at("escape_mega", line, at); + } + make("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then make tokens that will become part of an expression until +// a } token is made. + + if (source_line[0] === "$") { + column += 2; + make("${"); + source_line = source_line.slice(2); + (function expr() { + const id = lex().id; + if (id === "{") { + return stop_at( + "expected_a_b", + line, + column, + "}", + "{" + ); + } + if (id !== "}") { + return expr(); + } + }()); + return part(); + } + }()); + source_line = source_line.slice(1); + column += 1; + mega_mode = false; + return make("`"); + } + +// The token is a // comment. + + if (snippet === "//") { + snippet = source_line; + source_line = ""; + the_token = comment(snippet); + if (mega_mode) { + warn("unexpected_comment", the_token, "`"); + } + return the_token; + } + +// The token is a /* comment. + + if (snippet === "/*") { + array = []; + if (source_line[0] === "/") { + warn_at("unexpected_a", line, column + i, "/"); + } + (function next() { + if (source_line > "") { + i = source_line.search(rx_star_slash); + if (i >= 0) { + return; + } + j = source_line.search(rx_slash_star); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + } + array.push(source_line); + source_line = next_line(); + if (source_line === undefined) { + return stop_at("unclosed_comment", line, column); + } + return next(); + }()); + snippet = source_line.slice(0, i); + j = snippet.search(rx_slash_star_or_slash); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + array.push(snippet); + column += i + 2; + source_line = source_line.slice(i + 2); + return comment(array); + } + +// The token is a slash. + + if (snippet === "/") { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + if (prior.identifier) { + if (!prior.dot) { + if (prior.id === "return") { + return regexp(); + } + if ( + prior.id === "(begin)" + || prior.id === "case" + || prior.id === "delete" + || prior.id === "in" + || prior.id === "instanceof" + || prior.id === "new" + || prior.id === "typeof" + || prior.id === "void" + || prior.id === "yield" + ) { + the_token = regexp(); + return stop("unexpected_a", the_token); + } + } + } else { + last = prior.id[prior.id.length - 1]; + if ("(,=:?[".indexOf(last) >= 0) { + return regexp(); + } + if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) { + the_token = regexp(); + warn("wrap_regexp", the_token); + return the_token; + } + } + if (source_line[0] === "/") { + column += 1; + source_line = source_line.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + } + return make(snippet); + } + + first = lex(); + json_mode = first.id === "{" || first.id === "["; + +// This is the only loop in JSLint. It will turn into a recursive call to lex +// when ES6 has been finished and widely deployed and adopted. + + while (true) { + if (lex().id === "(end)") { + break; + } + } +} + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + +function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!rx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!rx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property[id] === "number") { + property[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (tenure !== undefined) { + if (tenure[id] !== true) { + warn("unregistered_property_a", name); + } + } else { + if (name.identifier && rx_bad_property.test(id)) { + warn("bad_property_a", name); + } + } + property[id] = 1; + } + return id; +} + +function dispense() { + +// Deliver the next token, skipping the comments. + + const cadet = tokens[token_nr]; + token_nr += 1; + if (cadet.id === "(comment)") { + if (json_mode) { + warn("unexpected_a", cadet); + } + return dispense(); + } else { + return cadet; + } +} + +function lookahead() { + +// Look ahead one token without advancing. + + const old_token_nr = token_nr; + const cadet = dispense(true); + token_nr = old_token_nr; + return cadet; +} + +function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if (token.identifier && token.id !== "function") { + anon = token.id; + } else if (token.id === "(string)" && rx_identifier.test(token.value)) { + anon = token.value; + } + +// Attempt to match next_token with an expected id. + + if (id !== undefined && next_token.id !== id) { + return ( + match === undefined + ? stop("expected_a_b", next_token, id, artifact()) + : stop( + "expected_a_b_from_c_d", + next_token, + id, + artifact(match), + artifact_line(match), + artifact(next_token) + ) + ); + } + +// Promote the tokens, skipping comments. + + token = next_token; + next_token = dispense(); + if (next_token.id === "(end)") { + token_nr -= 1; + } +} + +// Parsing of JSON is simple: + +function json_value() { + let negative; + if (next_token.id === "{") { + return (function json_object() { + const brace = next_token; + const object = empty(); + const properties = []; + brace.expression = properties; + advance("{"); + if (next_token.id !== "}") { + (function next() { + let name; + let value; + if (next_token.quote !== "\"") { + warn( + "unexpected_a", + next_token, + next_token.quote + ); + } + name = next_token; + advance("(string)"); + if (object[token.value] !== undefined) { + warn("duplicate_a", token); + } else if (token.value === "__proto__") { + warn("bad_property_a", token); + } else { + object[token.value] = token; + } + advance(":"); + value = json_value(); + value.label = name; + properties.push(value); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("}", brace); + return brace; + }()); + } + if (next_token.id === "[") { + return (function json_array() { + const bracket = next_token; + const elements = []; + bracket.expression = elements; + advance("["); + if (next_token.id !== "]") { + (function next() { + elements.push(json_value()); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]", bracket); + return bracket; + }()); + } + if ( + next_token.id === "true" + || next_token.id === "false" + || next_token.id === "null" + ) { + advance(); + return token; + } + if (next_token.id === "(number)") { + if (!rx_JSON_number.test(next_token.value)) { + warn("unexpected_a"); + } + advance(); + return token; + } + if (next_token.id === "(string)") { + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + advance(); + return token; + } + if (next_token.id === "-") { + negative = next_token; + negative.arity = "unary"; + advance("-"); + advance("(number)"); + negative.expression = token; + return negative; + } + stop("unexpected_a"); +} + +// Now we parse JavaScript. + +function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + const id = name.id; + +// Reserved words may not be enrolled. + + if (syntax[id] !== undefined && id !== "ignore") { + warn("reserved_a", name); + } else { + +// Has the name been enrolled in this context? + + let earlier = functionage.context[id]; + if (earlier) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + +// Has the name been enrolled in an outer context? + + } else { + stack.forEach(function (value) { + const item = value.context[id]; + if (item !== undefined) { + earlier = item; + } + }); + if (earlier) { + if (id === "ignore") { + if (earlier.role === "variable") { + warn("unexpected_a", name); + } + } else { + if ( + ( + role !== "exception" + || earlier.role !== "exception" + ) + && role !== "parameter" + && role !== "function" + ) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + } + } + } + +// Enroll it. + + functionage.context[id] = name; + name.dead = true; + name.parent = functionage; + name.init = false; + name.role = role; + name.used = 0; + name.writable = !readonly; + } + } +} + +function expression(rbp, initial) { + +// This is the heart of the Pratt parser. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// nud Null denotation +// led Left denotation +// lbp Left binding power +// rbp Right binding power + +// It processes a nud (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop. It +// returns the expression's parse tree. + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement, + + if (!initial) { + advance(); + } + the_symbol = syntax[token.id]; + if (the_symbol !== undefined && the_symbol.nud !== undefined) { + left = the_symbol.nud(); + } else if (token.identifier) { + left = token; + left.arity = "variable"; + } else { + return stop("unexpected_a", token); + } + (function right() { + the_symbol = syntax[next_token.id]; + if ( + the_symbol !== undefined + && the_symbol.led !== undefined + && rbp < the_symbol.lbp + ) { + advance(); + left = the_symbol.led(left); + return right(); + } + }()); + return left; +} + +function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = next_token; + let the_value; + the_paren.free = true; + advance("("); + the_value = expression(0); + advance(")"); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (anticondition[the_value.id] === true) { + warn("unexpected_a", the_value); + } + return the_value; +} + +function is_weird(thing) { + return ( + thing.id === "(regexp)" + || thing.id === "{" + || thing.id === "=>" + || thing.id === "function" + || (thing.id === "[" && thing.arity === "unary") + ); +} + +function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + return ( + Array.isArray(b) + && a.length === b.length + && a.every(function (value, index) { + return are_similar(value, b[index]); + }) + ); + } + if (Array.isArray(b)) { + return false; + } + if (a.id === "(number)" && b.id === "(number)") { + return a.value === b.value; + } + let a_string; + let b_string; + if (a.id === "(string)") { + a_string = a.value; + } else if (a.id === "`" && a.constant) { + a_string = a.value[0]; + } + if (b.id === "(string)") { + b_string = b.value; + } else if (b.id === "`" && b.constant) { + b_string = b.value[0]; + } + if (typeof a_string === "string") { + return a_string === b_string; + } + if (is_weird(a) || is_weird(b)) { + return false; + } + if (a.arity === b.arity && a.id === b.id) { + if (a.id === ".") { + return ( + are_similar(a.expression, b.expression) + && are_similar(a.name, b.name) + ); + } + if (a.arity === "unary") { + return are_similar(a.expression, b.expression); + } + if (a.arity === "binary") { + return ( + a.id !== "(" + && are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + ); + } + if (a.arity === "ternary") { + return ( + are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + && are_similar(a.expression[2], b.expression[2]) + ); + } + if (a.arity === "function" && a.arity === "regexp") { + return false; + } + return true; + } + return false; +} + +function semicolon() { + +// Try to match a semicolon. + + if (next_token.id === ";") { + advance(";"); + } else { + warn_at( + "expected_a_b", + token.line, + token.thru, + ";", + artifact(next_token) + ); + } + anon = "anonymous"; +} + +function statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token.identifier && next_token.id === ":") { + the_label = token; + if (the_label.id === "ignore") { + warn("unexpected_a", the_label); + } + advance(":"); + if ( + next_token.id === "do" + || next_token.id === "for" + || next_token.id === "switch" + || next_token.id === "while" + ) { + enroll(the_label, "label", true); + the_label.init = true; + the_label.dead = false; + the_statement = statement(); + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token; + first.statement = true; + the_symbol = syntax[first.id]; + if (the_symbol !== undefined && the_symbol.fud !== undefined) { + the_symbol.disrupt = false; + the_symbol.statement = true; + the_statement = the_symbol.fud(); + } else { + +// It is an expression statement. + + the_statement = expression(0, true); + if (the_statement.wrapped && the_statement.id !== "(") { + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; +} + +function statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const array = []; + (function next(disrupt) { + if ( + next_token.id !== "}" + && next_token.id !== "case" + && next_token.id !== "default" + && next_token.id !== "else" + && next_token.id !== "(end)" + ) { + let a_statement = statement(); + array.push(a_statement); + if (disrupt) { + warn("unreachable_a", a_statement); + } + return next(a_statement.disrupt); + } + }(false)); + return array; +} + +function not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === global) { + warn("unexpected_at_top_level_a", thing); + } +} + +function top_level_only(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== global) { + warn("misplaced_a", the_thing); + } +} + +function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token; + the_block.arity = "statement"; + the_block.body = special === "body"; + +// All top level function bodies should include the "use strict" pragma unless +// the whole file is strict or the file is a module or the function parameters +// use es6 syntax. + + if ( + special === "body" + && stack.length === 1 + && next_token.value === "use strict" + ) { + the_block.strict = next_token; + next_token.statement = true; + advance("(string)"); + advance(";"); + } + stmts = statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option.devel && special !== "ignore") { + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; +} + +function mutation_check(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + warn("bad_assignment_a", the_thing); + return false; + } + return true; +} + +function left_check(left, right) { + +// Warn if the left is not one of these: +// e.b +// e[b] +// e() +// ?: +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !left_check(left.expression[1]) + && !left_check(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right); + return false; + } + return true; +} + +// These functions are used to specify the grammar of our language: + +function symbol(id, bp) { + +// Make a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax[id] = the_symbol; + } + return the_symbol; +} + +function assignment(id) { + +// Make an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led = function (left) { + const the_token = token; + let right; + the_token.arity = "assignment"; + right = expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "pre" + || right.arity === "post" + ) { + warn("unexpected_a", right); + } + mutation_check(left); + return the_token; + }; + return the_symbol; +} + +function constant(id, type, value) { + +// Make a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud = ( + typeof value === "function" + ? value + : function () { + token.constant = true; + if (value !== undefined) { + token.value = value; + } + return token; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; +} + +function infix(id, bp, f) { + +// Make an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, expression(bp)]; + return the_token; + }; + return the_symbol; +} + +function infixr(id, bp) { + +// Make a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + the_token.expression = [left, expression(bp - 1)]; + return the_token; + }; + return the_symbol; +} + +function post(id) { + +// Make one of the post operators. + + const the_symbol = symbol(id, 150); + the_symbol.led = function (left) { + token.expression = left; + token.arity = "post"; + mutation_check(token.expression); + return token; + }; + return the_symbol; +} + +function pre(id) { + +// Make one of the pre operators. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "pre"; + the_token.expression = expression(150); + mutation_check(the_token.expression); + return the_token; + }; + return the_symbol; +} + +function prefix(id, f) { + +// Make a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = expression(150); + return the_token; + }; + return the_symbol; +} + +function stmt(id, f) { + +// Make a statement. + + const the_symbol = symbol(id); + the_symbol.fud = function () { + token.arity = "statement"; + return f(); + }; + return the_symbol; +} + +function ternary(id1, id2) { + +// Make a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led = function (left) { + const the_token = token; + const second = expression(20); + advance(id2); + token.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, expression(10)]; + return the_token; + }; + return the_symbol; +} + +// Begin defining the language. + +syntax = empty(); + +symbol("}"); +symbol(")"); +symbol("]"); +symbol(","); +symbol(";"); +symbol(":"); +symbol("*/"); +symbol("await"); +symbol("case"); +symbol("catch"); +symbol("class"); +symbol("default"); +symbol("else"); +symbol("enum"); +symbol("finally"); +symbol("implements"); +symbol("interface"); +symbol("package"); +symbol("private"); +symbol("protected"); +symbol("public"); +symbol("static"); +symbol("super"); +symbol("void"); +symbol("yield"); + +constant("(number)", "number"); +constant("(regexp)", "regexp"); +constant("(string)", "string"); +constant("arguments", "object", function () { + warn("unexpected_a", token); + return token; +}); +constant("eval", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("false", "boolean", false); +constant("Function", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("ignore", "undefined", function () { + warn("unexpected_a", token); + return token; +}); +constant("Infinity", "number", Infinity); +constant("isFinite", "function", function () { + warn("expected_a_b", token, "Number.isFinite", "isFinite"); + return token; +}); +constant("isNaN", "function", function () { + warn("number_isNaN", token); + return token; +}); +constant("NaN", "number", NaN); +constant("null", "null", null); +constant("this", "object", function () { + if (!option.this) { + warn("unexpected_a", token); + } + return token; +}); +constant("true", "boolean", true); +constant("undefined", "undefined"); + +assignment("="); +assignment("+="); +assignment("-="); +assignment("*="); +assignment("/="); +assignment("%="); +assignment("&="); +assignment("|="); +assignment("^="); +assignment("<<="); +assignment(">>="); +assignment(">>>="); + +infix("||", 40); +infix("&&", 50); +infix("|", 70); +infix("^", 80); +infix("&", 90); +infix("==", 100); +infix("===", 100); +infix("!=", 100); +infix("!==", 100); +infix("<", 110); +infix(">", 110); +infix("<=", 110); +infix(">=", 110); +infix("in", 110); +infix("instanceof", 110); +infix("<<", 120); +infix(">>", 120); +infix(">>>", 120); +infix("+", 130); +infix("-", 130); +infix("*", 140); +infix("/", 140); +infix("%", 140); +infixr("**", 150); +infix("(", 160, function (left) { + const the_paren = token; + let the_argument; + if (left.id !== "function") { + left_check(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (next_token.id !== ")") { + (function next() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + the_paren.free = true; + if (the_argument.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + the_paren.free = false; + } + return the_paren; +}); +infix(".", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("?.", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("[", 170, function (left) { + const the_token = token; + const the_subscript = expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + const name = survey(the_subscript); + if (rx_identifier.test(name)) { + warn("subscript_a", the_subscript, name); + } + } + left_check(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; +}); +infix("=>", 170, function (left) { + return stop("wrap_parameter", left); +}); + +function do_tick() { + const the_tick = token; + the_tick.value = []; + the_tick.expression = []; + if (next_token.id !== "`") { + (function part() { + advance("(string)"); + the_tick.value.push(token); + if (next_token.id === "${") { + advance("${"); + the_tick.expression.push(expression(0)); + advance("}"); + return part(); + } + }()); + } + advance("`"); + return the_tick; +} + +infix("`", 160, function (left) { + const the_tick = do_tick(); + left_check(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; +}); + +post("++"); +post("--"); +pre("++"); +pre("--"); + +prefix("+"); +prefix("-"); +prefix("~"); +prefix("!"); +prefix("!!"); +prefix("[", function () { + const the_token = token; + the_token.expression = []; + if (next_token.id !== "]") { + (function next() { + let element; + let ellipsis = false; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + element = expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]"); + return the_token; +}); +prefix("/=", function () { + stop("expected_a_b", token, "/\\=", "/="); +}); +prefix("=>", function () { + return stop("expected_a_before_b", token, "()", "=>"); +}); +prefix("new", function () { + const the_new = token; + const right = expression(160); + if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "()", artifact(next_token)); + } + the_new.expression = right; + return the_new; +}); +prefix("typeof"); +prefix("void", function () { + const the_void = token; + warn("unexpected_a", the_void); + the_void.expression = expression(0); + return the_void; +}); + +function parameter_list() { + let complex = false; + const list = []; + let optional; + const signature = ["("]; + if (next_token.id !== ")" && next_token.id !== "(end)") { + (function parameter() { + let ellipsis = false; + let param; + if (next_token.id === "{") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("{"); + signature.push("{"); + (function subparameter() { + let subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (next_token.id === ":") { + advance(":"); + advance(); + token.label = subparam; + subparam = token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + } + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return subparameter(); + } + }()); + list.push(param); + advance("}"); + signature.push("}"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else if (next_token.id === "[") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("["); + signature.push("[]"); + (function subparameter() { + const subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + return subparameter(); + } + }()); + list.push(param); + advance("]"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else { + if (next_token.id === "...") { + complex = true; + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + } + if (!next_token.identifier) { + return stop("expected_identifier_a"); + } + param = next_token; + list.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (next_token.id === "=") { + complex = true; + optional = param; + advance("="); + param.expression = expression(0); + } else { + if (optional !== undefined) { + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } + } + }()); + } + advance(")"); + signature.push(")"); + return [list, signature.join(""), complex]; +} + +function do_function(the_function) { + let name; + if (the_function === undefined) { + the_function = token; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + name = next_token; + enroll(name, "variable", true); + the_function.name = name; + name.init = true; + name.calls = empty(); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + if (next_token.identifier) { + name = next_token; + the_function.name = name; + advance(); + } else { + the_function.name = anon; + } + } + } else { + name = the_function.name; + } + the_function.level = functionage.level + 1; + if (mega_mode) { + warn("unexpected_a", the_function); + } + +// Don't make functions in loops. It is inefficient, and it can lead to scoping +// errors. + + if (functionage.loop > 0) { + warn("function_in_loop", the_function); + } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + the_function.context = empty(); + the_function.finally = 0; + the_function.loop = 0; + the_function.switch = 0; + the_function.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functions.push(the_function); + functionage = the_function; + if (the_function.arity !== "statement" && typeof name === "object") { + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// Parse the parameter list. + + advance("("); + token.free = false; + token.arity = "function"; + const pl = parameter_list(); + functionage.parameters = pl[0]; + functionage.signature = pl[1]; + functionage.complex = pl[2]; + functionage.parameters.forEach(function enroll_parameter(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + name.names.forEach(enroll_parameter); + } + }); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && next_token.line === token.line + ) { + return stop("unexpected_a", next_token); + } + if ( + next_token.id === "." + || next_token.id === "?." + || next_token.id === "[" + ) { + warn("unexpected_a"); + } + +// Restore the previous context. + + functionage = stack.pop(); + return the_function; +} + +prefix("function", do_function); + +function fart(pl) { + advance("=>"); + const the_fart = token; + the_fart.arity = "binary"; + the_fart.name = "=>"; + the_fart.level = functionage.level + 1; + functions.push(the_fart); + if (functionage.loop > 0) { + warn("function_in_loop", the_fart); + } + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + the_fart.context = empty(); + the_fart.finally = 0; + the_fart.loop = 0; + the_fart.switch = 0; + the_fart.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functionage = the_fart; + the_fart.parameters = pl[0]; + the_fart.signature = pl[1]; + the_fart.complex = true; + the_fart.parameters.forEach(function (name) { + enroll(name, "parameter", true); + }); + if (next_token.id === "{") { + warn("expected_a_b", the_fart, "function", "=>"); + the_fart.block = block("body"); + } else { + the_fart.expression = expression(0); + } + functionage = stack.pop(); + return the_fart; +} + +prefix("(", function () { + const the_paren = token; + let the_value; + const cadet = lookahead().id; + +// We can distinguish between a parameter list for => and a wrapped expression +// with one token of lookahead. + + if ( + next_token.id === ")" + || next_token.id === "..." + || (next_token.identifier && (cadet === "," || cadet === "=")) + ) { + the_paren.free = false; + return fart(parameter_list()); + } + the_paren.free = true; + the_value = expression(0); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + if (next_token.id === "=>") { + if (the_value.arity !== "variable") { + if (the_value.id === "{" || the_value.id === "[") { + warn("expected_a_before_b", the_paren, "function", "("); + return stop("expected_a_b", next_token, "{", "=>"); + } + return stop("expected_identifier_a", the_value); + } + the_paren.expression = [the_value]; + return fart([the_paren.expression, "(" + the_value.id + ")"]); + } + return the_value; +}); +prefix("`", do_tick); +prefix("{", function () { + const the_brace = token; + const seen = empty(); + the_brace.expression = []; + if (next_token.id !== "}") { + (function member() { + let extra; + let full; + let id; + let name = next_token; + let value; + advance(); + if ( + (name.id === "get" || name.id === "set") + && next_token.identifier + ) { + if (!option.getset) { + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + next_token.id; + name = next_token; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (next_token.id === "}" || next_token.id === ",") { + if (typeof extra === "string") { + advance("("); + } + value = expression(Infinity, true); + } else if (next_token.id === "(") { + value = do_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + advance("("); + } + advance(":"); + value = expression(0); + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + advance(":"); + value = expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (next_token.id === ",") { + advance(","); + return member(); + } + }()); + } + advance("}"); + return the_brace; +}); + +stmt(";", function () { + warn("unexpected_a", token); + return token; +}); +stmt("{", function () { + warn("naked_block", token); + return block("naked"); +}); +stmt("break", function () { + const the_break = token; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (next_token.identifier && token.line === next_token.line) { + the_label = functionage.context[next_token.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + warn( + (the_label !== undefined && the_label.dead) + ? "out_of_scope_a" + : "not_label_a" + ); + } else { + the_label.used += 1; + } + the_break.label = next_token; + advance(); + } + advance(";"); + return the_break; +}); + +function do_var() { + const the_statement = token; + const is_const = the_statement.id === "const"; + the_statement.names = []; + +// A program may use var or let, but not both. + + if (!is_const) { + if (var_mode === undefined) { + var_mode = the_statement.id; + } else if (the_statement.id !== var_mode) { + warn( + "expected_a_b", + the_statement, + var_mode, + the_statement.id + ); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + warn("var_switch", the_statement); + } + if (functionage.loop > 0 && the_statement.id === "var") { + warn("var_loop", the_statement); + } + (function next() { + if (next_token.id === "{" && the_statement.id !== "var") { + const the_brace = next_token; + the_brace.names = []; + advance("{"); + (function pair() { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + survey(name); + advance(); + if (next_token.id === ":") { + advance(":"); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + next_token.label = name; + the_brace.names.push(next_token); + enroll(next_token, "variable", is_const); + advance(); + } else { + the_brace.names.push(name); + enroll(name, "variable", is_const); + } + if (next_token.id === ",") { + advance(","); + return pair(); + } + }()); + advance("}"); + advance("="); + the_brace.expression = expression(0); + the_statement.names.push(the_brace); + } else if (next_token.id === "[" && the_statement.id !== "var") { + const the_bracket = next_token; + the_bracket.names = []; + advance("["); + (function element() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + advance(); + the_bracket.names.push(name); + enroll(name, "variable", the_statement.id === "const"); + if (ellipsis) { + name.ellipsis = true; + } else if (next_token.id === ",") { + advance(","); + return element(); + } + }()); + advance("]"); + advance("="); + the_bracket.expression = expression(0); + the_statement.names.push(the_bracket); + } else if (next_token.identifier) { + const name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", is_const); + if (next_token.id === "=" || is_const) { + advance("="); + name.init = true; + name.expression = expression(0); + } + the_statement.names.push(name); + } else { + return stop("expected_identifier_a", next_token); + } + if (next_token.id === ",") { + if (!option.multivar) { + warn("expected_a_b", next_token, ";", ","); + } + advance(","); + return next(); + } + }()); + the_statement.open = ( + the_statement.names.length > 1 + && the_statement.line !== the_statement.names[1].line + ); + semicolon(); + return the_statement; +} + +stmt("const", do_var); +stmt("continue", function () { + const the_continue = token; + if (functionage.loop < 1 || functionage.finally > 0) { + warn("unexpected_a", the_continue); + } + not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; +}); +stmt("debugger", function () { + const the_debug = token; + if (!option.devel) { + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; +}); +stmt("delete", function () { + const the_token = token; + const the_value = expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; +}); +stmt("do", function () { + const the_do = token; + not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; +}); +stmt("export", function () { + const the_export = token; + let the_id; + let the_name; + let the_thing; + + function export_id() { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + the_id = next_token.id; + the_name = global.context[the_id]; + if (the_name === undefined) { + warn("unexpected_a"); + } else { + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a"); + } + exports[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + } + + the_export.expression = []; + if (next_token.id === "default") { + if (exports.default !== undefined) { + warn("duplicate_a"); + } + advance("default"); + the_thing = expression(0); + if ( + the_thing.id !== "(" + || the_thing.expression[0].id !== "." + || the_thing.expression[0].expression.id !== "Object" + || the_thing.expression[0].name.id !== "freeze" + ) { + warn("freeze_exports", the_thing); + } + if (next_token.id === ";") { + semicolon(); + } + exports.default = the_thing; + the_export.expression.push(the_thing); + } else { + if (next_token.id === "function") { + warn("freeze_exports"); + the_thing = statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a", the_name); + } + exports[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + warn("unexpected_a", next_token); + statement(); + } else if (next_token.id === "{") { + advance("{"); + (function loop() { + export_id(); + if (next_token.id === ",") { + advance(","); + return loop(); + } + }()); + advance("}"); + semicolon(); + } else { + stop("unexpected_a"); + } + } + module_mode = true; + return the_export; +}); +stmt("for", function () { + let first; + const the_for = token; + if (!option.for) { + warn("unexpected_a", the_for); + } + not_top_level(the_for); + functionage.loop += 1; + advance("("); + token.free = true; + if (next_token.id === ";") { + return stop("expected_a_b", the_for, "while (", "for (;"); + } + if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + return stop("unexpected_a"); + } + first = expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = expression(0); + advance(";"); + the_for.inc = expression(0); + if (the_for.inc.id === "++") { + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; +}); +stmt("function", do_function); +stmt("if", function () { + let the_else; + const the_if = token; + the_if.expression = condition(); + the_if.block = block(); + if (next_token.id === "else") { + advance("else"); + the_else = token; + the_if.else = ( + next_token.id === "if" + ? statement() + : block() + ); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + the_if.disrupt = true; + } else { + warn("unexpected_a", the_else); + } + } + } + return the_if; +}); +stmt("import", function () { + const the_import = token; + let name; + if (typeof module_mode === "object") { + warn("unexpected_directive_a", module_mode, module_mode.directive); + } + module_mode = true; + if (next_token.identifier) { + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + const names = []; + advance("{"); + if (next_token.id !== "}") { + while (true) { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (next_token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token; + if (!rx_module.test(token.value)) { + warn("bad_module_name_a", token); + } + froms.push(token.value); + semicolon(); + return the_import; +}); +stmt("let", do_var); +stmt("return", function () { + const the_return = token; + not_top_level(the_return); + if (functionage.finally > 0) { + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (next_token.id !== ";" && the_return.line === next_token.line) { + the_return.expression = expression(10); + } + advance(";"); + return the_return; +}); +stmt("switch", function () { + let dups = []; + let last; + let stmts; + const the_cases = []; + let the_disrupt = true; + const the_switch = token; + not_top_level(the_switch); + if (functionage.finally > 0) { + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token.free = true; + the_switch.expression = expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + (function major() { + const the_case = next_token; + the_case.arity = "statement"; + the_case.expression = []; + (function minor() { + advance("case"); + token.switch = true; + const exp = expression(0); + if (dups.some(function (thing) { + return are_similar(thing, exp); + })) { + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (next_token.id === "case") { + return minor(); + } + }()); + stmts = statements(); + if (stmts.length < 1) { + warn("expected_statements_a"); + return; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "break;", + artifact(next_token) + ); + } + if (next_token.id === "case") { + return major(); + } + }()); + dups = undefined; + if (next_token.id === "default") { + const the_default = next_token; + advance("default"); + token.switch = true; + advance(":"); + the_switch.else = statements(); + if (the_switch.else.length < 1) { + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + const the_last = the_switch.else[the_switch.else.length - 1]; + if (the_last.id === "break" && the_last.label === undefined) { + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; +}); +stmt("throw", function () { + const the_throw = token; + the_throw.disrupt = true; + the_throw.expression = expression(10); + semicolon(); + if (functionage.try > 0) { + warn("unexpected_a", the_throw); + } + return the_throw; +}); +stmt("try", function () { + let the_catch; + let the_disrupt; + const the_try = token; + if (functionage.try > 0) { + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (next_token.id === "catch") { + let ignored = "ignore"; + the_catch = next_token; + the_try.catch = the_catch; + advance("catch"); + if (next_token.id === "(") { + advance("("); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + if (next_token.id !== "ignore") { + ignored = undefined; + the_catch.name = next_token; + enroll(next_token, "exception", true); + } + advance(); + advance(")"); + } + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "catch", + artifact(next_token) + ); + + } + if (next_token.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; +}); +stmt("var", do_var); +stmt("while", function () { + const the_while = token; + not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; +}); +stmt("with", function () { + stop("unexpected_a", token); +}); + +ternary("?", ":"); + +// Ambulation of the parse tree. + +function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; +} + +function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all of the +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + } + }; +} + +const posts = empty(); +const pres = empty(); +const preaction = action(pres); +const postaction = action(posts); +const preamble = amble(pres); +const postamble = amble(posts); + +function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.id === "function") { + walk_statement(thing.block); + } + if (thing.arity === "pre" || thing.arity === "post") { + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + warn("unexpected_statement_a", thing); + } + postamble(thing); + } + } +} + +function walk_statement(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_statement); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + ) { + warn( + (thing.id === "(string)" && thing.value === "use strict") + ? "unexpected_a" + : "unexpected_expression_a", + thing + ); + } + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + } +} + +function lookup(thing) { + if (thing.arity === "variable") { + +// Look up the variable in the current context. + + let the_variable = functionage.context[thing.id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable === undefined) { + stack.forEach(function (outer) { + const a_variable = outer.context[thing.id]; + if ( + a_variable !== undefined + && a_variable.role !== "label" + ) { + the_variable = a_variable; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (the_variable === undefined) { + if (declared_globals[thing.id] === undefined) { + warn("undeclared_a", thing); + return; + } + the_variable = { + dead: false, + parent: global, + id: thing.id, + init: true, + role: "variable", + used: 0, + writable: false + }; + global.context[thing.id] = the_variable; + } + the_variable.closure = true; + functionage.context[thing.id] = the_variable; + } else if (the_variable.role === "label") { + warn("label_a", thing); + } + if ( + the_variable.dead + && ( + the_variable.calls === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + ) { + warn("out_of_scope_a", thing); + } + return the_variable; + } +} + +function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); +} + +function preaction_function(thing) { + if (thing.arity === "statement" && blockage.body !== true) { + warn("unexpected_a", thing); + } + if (thing.level === 1) { + if ( + module_mode === true + || global.strict !== undefined + || thing.complex + ) { + if (thing.id !== "=>" && thing.block.strict !== undefined) { + warn("unexpected_a", thing.block.strict); + } + } else { + if (thing.block.strict === undefined) { + warn("use_strict", thing); + } + } + } + stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); +} + +function bitwise_check(thing) { + if (!option.bitwise && bitwiseop[thing.id] === true) { + warn("unexpected_a", thing); + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + warn("unexpected_a", thing); + } +} + +function pop_block() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); +} + +function activate(name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.init = true; + } + } + blockage.live.push(name); +} + +function action_var(thing) { + thing.names.forEach(activate); +} + +preaction("assignment", bitwise_check); +preaction("binary", bitwise_check); +preaction("binary", function (thing) { + if (relationop[thing.id] === true) { + const left = thing.expression[0]; + const right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + warn("expected_string_a", right); + } + } else { + const value = right.value; + if (value === "null" || value === "undefined") { + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + warn("expected_type_string_a", right, value); + } + } + } + } +}); +preaction("binary", "==", function (thing) { + warn("expected_a_b", thing, "===", "=="); +}); +preaction("binary", "!=", function (thing) { + warn("expected_a_b", thing, "!==", "!="); +}); +preaction("binary", "=>", preaction_function); +preaction("binary", "||", function (thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + warn("and", thang); + } + }); +}); +preaction("binary", "(", function (thing) { + const left = thing.expression[0]; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + const parent = functionage.name.parent; + if (parent) { + const left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + && left_variable.dead + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } +}); +preaction("binary", "in", function (thing) { + warn("infix_in", thing); +}); +preaction("binary", "instanceof", function (thing) { + warn("unexpected_a", thing); +}); +preaction("binary", ".", function (thing) { + if (thing.expression.new) { + thing.new = true; + } +}); +preaction("statement", "{", function (thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; +}); +preaction("statement", "for", function (thing) { + if (thing.name !== undefined) { + const the_variable = lookup(thing.name); + if (the_variable !== undefined) { + the_variable.init = true; + if (!the_variable.writable) { + warn("bad_assignment_a", thing.name); + } + } + } + walk_statement(thing.initial); +}); +preaction("statement", "function", preaction_function); +preaction("unary", "~", bitwise_check); +preaction("unary", "function", preaction_function); +preaction("variable", function (thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } +}); + +function init_variable(name) { + const the_variable = lookup(name); + if (the_variable !== undefined) { + if (the_variable.writable) { + the_variable.init = true; + return; + } + } + warn("bad_assignment_a", name); +} + +postaction("assignment", "+=", function (thing) { + let right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } +}); +postaction("assignment", function (thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + if (thing.id === "=") { + if (thing.names !== undefined) { + if (Array.isArray(thing.names)) { + thing.names.forEach(init_variable); + } else { + init_variable(thing.names); + } + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.writable !== true) { + warn("bad_assignment_a", lvalue); + } + } + const right = syntax[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + warn("unexpected_a", thing.expression[1]); + } + } +}); + +function postaction_function(thing) { + delete functionage.finally; + delete functionage.loop; + delete functionage.switch; + delete functionage.try; + functionage = stack.pop(); + if (thing.wrapped) { + warn("unexpected_parens", thing); + } + return pop_block(); +} + +postaction("binary", function (thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || are_similar(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option.convert) { + if (thing.expression[0].value === "") { + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === "." || thing.id === "?.") { + if (thing.expression.id === "RegExp") { + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } +}); +postaction("binary", "&&", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "||", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "=>", postaction_function); +postaction("binary", "(", function (thing) { + let left = thing.expression[0]; + let the_new; + let arg; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option.eval) { + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + warn( + "expected_a_before_b", + left, + "new", + artifact(left) + ); + } + } + } else if (left.id === ".") { + let cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + cack = !cack; + } + if (rx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + warn("unexpected_a", the_new); + } else { + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + const paren = left.expression; + if (paren.id === "(") { + const array = paren.expression; + if (array.length === 1) { + const new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } +}); +postaction("binary", "[", function (thing) { + if (thing.expression[0].id === "RegExp") { + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + warn("weird_expression_a", thing.expression[1]); + } +}); +postaction("statement", "{", pop_block); +postaction("statement", "const", action_var); +postaction("statement", "export", top_level_only); +postaction("statement", "for", function (thing) { + walk_statement(thing.inc); +}); +postaction("statement", "function", postaction_function); +postaction("statement", "import", function (the_thing) { + const name = the_thing.name; + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return top_level_only(the_thing); +}); +postaction("statement", "let", action_var); +postaction("statement", "try", function (thing) { + if (thing.catch !== undefined) { + const the_name = thing.catch.name; + if (the_name !== undefined) { + const the_variable = functionage.context[the_name.id]; + the_variable.dead = false; + the_variable.init = true; + } + walk_statement(thing.catch.block); + } +}); +postaction("statement", "var", action_var); +postaction("ternary", function (thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || are_similar(thing.expression[1], thing.expression[2]) + ) { + warn("unexpected_a", thing); + } else if (are_similar(thing.expression[0], thing.expression[1])) { + warn("expected_a_b", thing, "||", "?"); + } else if (are_similar(thing.expression[0], thing.expression[2])) { + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + warn("wrap_condition", thing.expression[0]); + } +}); +postaction("unary", function (thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option.convert) { + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } +}); +postaction("unary", "function", postaction_function); +postaction("unary", "+", function (thing) { + if (!option.convert) { + warn("expected_a_b", thing, "Number(...)", "+"); + } + const right = thing.expression; + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } +}); + +function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + if (id !== "ignore") { + const name = the_function.context[id]; + if (name.parent === the_function) { + if ( + name.used === 0 + && ( + name.role !== "function" + || name.parent.arity !== "unary" + ) + ) { + warn("unused_a", name); + } else if (!name.init) { + warn("uninitialized_a", name); + } + } + } + }); +} + +function uninitialized_and_unused() { + +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (module_mode === true || option.node) { + delve(global); + } + functions.forEach(delve); +} + +// Go through the token list, looking at usage of whitespace. + +function whitage() { + let closer = "(end)"; + let free = false; + let left = global; + let margin = 0; + let nr_comments_skipped = 0; + let open = true; + let opening = true; + let right; + + function pop() { + const previous = stack.pop(); + closer = previous.closer; + free = previous.free; + margin = previous.margin; + open = previous.open; + opening = previous.opening; + } + + function push() { + stack.push({ + closer, + free, + margin, + open, + opening + }); + } + + function expected_at(at) { + warn( + "expected_a_at_b_c", + right, + artifact(right), + fudge + at, + artifact_column(right) + ); + } + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function no_space() { + if (left.line === right.line) { + if (left.thru !== right.from && nr_comments_skipped === 0) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (open) { + const at = ( + free + ? margin + : margin + 8 + ); + if (right.from < at) { + expected_at(at); + } + } else { + if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (free) { + if (right.from < margin) { + expected_at(margin); + } + +// remove unused warning expected_a_next_at_b, +// because mislaid / stack[stack.length - 1].right +// is always undefined +// since commmit +// https://github.com/douglascrockford/JSLint/commit/8c13537 +// #diff-01d3d81a6eb6d82af3c377b55dc4fa28L4581 + + } else if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + + stack = []; + tokens.forEach(function (the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + + const new_closer = opener[left.id]; + if (typeof new_closer === "string") { + if (new_closer !== right.id) { + opening = left.line !== right.line; + push(); + closer = new_closer; + if (opening) { + free = closer === ")" && left.free; + open = true; + margin += 4; + if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (right.switch) { + at_margin(-4); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + free = false; + open = false; + no_space_only(); + } + } else { + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like '{]') have already been detected. + + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } + } else { + if (right.statement === true) { + if (left.id === "else") { + one_space_only(); + } else { + at_margin(0); + open = false; + } + +// If right is a closer, then pop the previous state. + + } else if (right.id === closer) { + pop(); + if (opening && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-4); + } else if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + one_space(); + } else { + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + at_margin(0); + } else { + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + no_space(); + } else if ( + left.id === "." + || left.id === "?." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + ) { + no_space_only(); + } else if (right.id === "." || right.id === "?.") { + no_space_only(); + } else if (left.id === ";") { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + one_space_only(); + } else if ( + left.id === "var" + || left.id === "const" + || left.id === "let" + ) { + push(); + closer = ";"; + free = false; + open = left.open; + if (open) { + margin = margin + 4; + at_margin(0); + } else { + one_space_only(); + } + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +// The jslint function itself. + +export default Object.freeze(function jslint( + source = "", + option_object = empty(), + global_array = [] +) { + try { + warnings = []; + option = Object.assign(empty(), option_object); + anon = "anonymous"; + block_stack = []; + declared_globals = empty(); + directive_mode = true; + directives = []; + early_stop = true; + exports = empty(); + froms = []; + fudge = ( + option.fudge + ? 1 + : 0 + ); + functions = []; + global = { + id: "(global)", + body: true, + context: empty(), + from: 0, + level: 0, + line: 0, + live: [], + loop: 0, + switch: 0, + thru: 0 + }; + blockage = global; + functionage = global; + json_mode = false; + mega_mode = false; + module_mode = false; + next_token = global; + property = empty(); + shebang = false; + stack = []; + tenure = undefined; + token = global; + token_nr = 0; + var_mode = undefined; + populate(standard, declared_globals, false); + populate(global_array, declared_globals, false); + Object.keys(option).forEach(function (name) { + if (option[name] === true) { + const allowed = allowed_option[name]; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } + }); + tokenize(source); + advance(); + if (json_mode) { + tree = json_value(); + advance("(end)"); + } else { + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option.browser) { + if (next_token.id === ";") { + advance(";"); + } + } else { + +// If we are not in a browser, then the file form of strict pragma may be used. + + if ( + next_token.value === "use strict" + ) { + global.strict = next_token; + advance("(string)"); + advance(";"); + } + } + tree = statements(); + advance("(end)"); + functionage = global; + walk_statement(tree); + if (module_mode && global.strict !== undefined) { + warn("unexpected_a", global.strict); + } + if (warnings.length === 0) { + uninitialized_and_unused(); + if (!option.white) { + whitage(); + } + } + } + if (!option.browser) { + directives.forEach(function (comment) { + if (comment.directive === "global") { + warn("missing_browser", comment); + } + }); + } + early_stop = false; + } catch (e) { + if (e.name !== "JSLintError") { + warnings.push(e); + } + } + return { + directives, + edition: "2018-10-26", + exports, + froms, + functions, + global, + id: "(JSLint)", + json: json_mode, + lines, + module: module_mode === true, + ok: warnings.length === 0 && !early_stop, + option, + property, + shebang: ( + shebang + ? lines[0] + : undefined + ), + stop: early_stop, + tokens, + tree, + warnings: warnings.sort(function (a, b) { + return a.line - b.line || a.column - b.column; + }) + }; +}); diff --git a/branch.remove_deadcode_expected_a_next_at_b/report.js b/branch.remove_deadcode_expected_a_next_at_b/report.js new file mode 100644 index 000000000..5e487d143 --- /dev/null +++ b/branch.remove_deadcode_expected_a_next_at_b/report.js @@ -0,0 +1,222 @@ +// report.js +// 2018-10-22 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Generate JSLint HTML reports. + +/*property + closure, column, context, edition, error, exports, filter, forEach, freeze, + froms, fudge, function, functions, global, id, isArray, join, json, keys, + length, level, line, lines, message, module, name, names, option, + parameters, parent, property, push, replace, role, signature, sort, stop, + warnings +*/ + +const rx_amp = /&/g; +const rx_gt = />/g; +const rx_lt = / with less destructive entities. + + return String( + string + ).replace( + rx_amp, + "&" + ).replace( + rx_lt, + "<" + ).replace( + rx_gt, + ">" + ); +} + +export default Object.freeze({ + error: function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + let fudge = Number(Boolean(data.option.fudge)); + let output = []; + if (data.stop) { + output.push("
    JSLint was unable to finish.
    "); + } + data.warnings.forEach(function (warning) { + output.push( + "
    ", + entityify(warning.line + fudge), + ".", + entityify(warning.column + fudge), + "
    ", + entityify(warning.message), + "
    ", + entityify(data.lines[warning.line] || ""), + "" + ); + }); + return output.join(""); + }, + + function: function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + let fudge = Number(Boolean(data.option.fudge)); + let mode = ( + data.module + ? "module" + : "global" + ); + let output = []; + + if (data.json) { + return ( + data.warnings.length === 0 + ? "
    JSON: good.
    " + : "
    JSON: bad.
    " + ); + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + let global = Object.keys(data.global.context).sort(); + let froms = data.froms.sort(); + let exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + let context = the_function.context; + let list = Object.keys(context); + output.push( + "
    ", + entityify(the_function.line + fudge), + "
    ", + ( + the_function.name === "=>" + ? entityify(the_function.signature) + " =>" + : ( + typeof the_function.name === "string" + ? "«" + entityify(the_function.name) + "»" + : "" + entityify(the_function.name.id) + "" + ) + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + let params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.parent === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.parent === the_function + ); + })); + detail("outer", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.parent !== the_function + && the_variable.parent.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].parent.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + output.push( + "
    JSLint edition ", + entityify(data.edition), + "
    " + ); + return output.join(""); + }, + + property: function property_directive(data) { + +// Produce the /*property*/ directive. + + let not_first = false; + let output = ["/*property"]; + let length = 1111; + let properties = Object.keys(data.property); + + if (properties.length > 0) { + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length >= 80) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); + } + } +}); diff --git a/branch.remove_unnecessary_recursion/README b/branch.remove_unnecessary_recursion/README new file mode 100644 index 000000000..f46e91069 --- /dev/null +++ b/branch.remove_unnecessary_recursion/README @@ -0,0 +1,42 @@ +JSLint, The JavaScript Code Quality Tool + +Douglas Crockford +douglas@crockford.com + +2018-05-28 + +jslint.js contains the jslint function. It parses and analyzes a source file, +returning an object with information about the file. It can also take an object +that sets options. + +index.html runs the jslint.js function in a web page. The page also depends +browser.js and report.js and jslint.css. + +jslint.css provides styling for index.html. + +browser.js runs the web user interface. + +report.js generates the results reports in HTML. + +help.html describes JSLint's usage. Please read it. + +function.html describes the jslint function and the results it produces. + +Please direct questions and comments to +https://plus.google.com/communities/104441363299760713736. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[http://www.crockford.com/wrrrld/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. diff --git a/branch.remove_unnecessary_recursion/browser.js b/branch.remove_unnecessary_recursion/browser.js new file mode 100644 index 000000000..5086fed85 --- /dev/null +++ b/branch.remove_unnecessary_recursion/browser.js @@ -0,0 +1,158 @@ +// browser.js +// 2018-06-16 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +/*jslint + browser +*/ + +/*property + checked, create, disable, display, error, focus, forEach, function, + getElementById, innerHTML, join, length, map, onchange, onclick, onscroll, + property, querySelectorAll, scrollTop, select, split, style, title, value +*/ + +import jslint from "./jslint.js"; +import report from "./report.js"; + +// This is the web script companion file for JSLint. It includes code for +// interacting with the browser and displaying the reports. + +let rx_crlf = /\n|\r\n?/; +let rx_separator = /[\s,;'"]+/; + +let fudge_unit = 0; + +const aux = document.getElementById("JSLINT_AUX"); +const boxes = document.querySelectorAll("[type=checkbox]"); +const fudge = document.getElementById("JSLINT_FUDGE"); +const global = document.getElementById("JSLINT_GLOBAL"); +const number = document.getElementById("JSLINT_NUMBER"); +const property = document.getElementById("JSLINT_PROPERTY"); +const property_fieldset = document.getElementById("JSLINT_PROPERTYFIELDSET"); +const report_field = document.getElementById("JSLINT_REPORT"); +const report_list = document.getElementById("JSLINT_REPORT_LIST"); +const source = document.getElementById("JSLINT_SOURCE"); +const select = document.getElementById("JSLINT_SELECT"); +const warnings = document.getElementById("JSLINT_WARNINGS"); +const warnings_list = document.getElementById("JSLINT_WARNINGS_LIST"); + +function show_numbers() { + number.value = source.value.split(rx_crlf).map(function (ignore, index) { + return index + fudge_unit; + }).join("\n"); +} + +function clear() { + aux.style.display = "none"; + number.value = ""; + property.value = ""; + property_fieldset.style.display = "none"; + report_field.style.display = "none"; + report_list.innerHTML = ""; + source.focus(); + source.value = ""; + warnings.style.display = "none"; + warnings_list.innerHTML = ""; +} + +function fudge_change() { + fudge_unit = Number(fudge.checked); + show_numbers(); +} + +function clear_options() { + boxes.forEach(function (node) { + node.checked = false; + }); + fudge_change(); + global.innerHTML = ""; +} + +function call_jslint() { + +// First build the option object. + + let option = Object.create(null); + boxes.forEach(function (node) { + if (node.checked) { + option[node.title] = true; + } + }); + +// Call JSLint with the source text, the options, and the predefined globals. + + let global_string = global.value; + let result = jslint( + source.value, + option, + ( + global_string === "" + ? undefined + : global_string.split(rx_separator) + ) + ); + +// Generate the reports. + + let error_html = report.error(result); + let function_html = report.function(result); + let property_text = report.property(result); + +// Display the reports. + + warnings_list.innerHTML = error_html; + warnings.style.display = ( + error_html.length === 0 + ? "none" + : "block" + ); + + report_list.innerHTML = function_html; + report_field.style.display = "block"; + if (property_text) { + property.value = property_text; + property_fieldset.style.display = "block"; + property.scrollTop = 0; + select.disable = false; + } else { + property_fieldset.style.display = "none"; + select.disable = true; + } + aux.style.display = "block"; + source.select(); +} + +fudge.onchange = fudge_change; + +source.onchange = function (ignore) { + show_numbers(); +}; + +source.onscroll = function () { + let ss = source.scrollTop; + number.scrollTop = ss; + let sn = number.scrollTop; + if (ss > sn) { + show_numbers(); + number.scrollTop = ss; + } +}; + +document.querySelectorAll("[name='JSLint']").forEach(function (node) { + node.onclick = call_jslint; +}); + +document.querySelectorAll("[name='clear']").forEach(function (node) { + node.onclick = clear; +}); + +document.getElementById("JSLINT_SELECT").onclick = function () { + property.focus(); + property.select(); +}; +document.getElementById("JSLINT_CLEAR_OPTIONS").onclick = clear_options; + +fudge_change(); +source.select(); +source.focus(); diff --git a/branch.remove_unnecessary_recursion/function.html b/branch.remove_unnecessary_recursion/function.html new file mode 100644 index 000000000..29de3af46 --- /dev/null +++ b/branch.remove_unnecessary_recursion/function.html @@ -0,0 +1,617 @@ + + + + + + + + +JSLint: jslint function + + + +
    JSLint
    + +
    The jslint Function
    +
    +

    JSLint

    +

    JSLint is delivered as a set of files:

    +
    + + + + + + + + + + + + + + +
    FilenameContent
    browser.jsBrowser based UI.
    function.htmlThis page that you are looking at right now.
    help.htmlUsing jslint as a single page JSLint application.
    index.htmlSingle page JSLint application that uses the + js files.
    jslint.jsThe jslint function.
    report.jsThe report object, containing report generator functions for + HTML.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    +
    + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring"(JSLint)"
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    parenttokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    parenttokenThe function in which the variable was declared.variable
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    shebangstringThe first line of text if it started with #!line
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable can be assigned to.variable
    +

    Reports

    +

    The report object contains three functions that all take a + result from the jslint function as input. + report.js has no other dependence on other files.

    + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    +
    + +
    + + + + + +
    + + diff --git a/branch.remove_unnecessary_recursion/help.html b/branch.remove_unnecessary_recursion/help.html new file mode 100644 index 000000000..babbe3be5 --- /dev/null +++ b/branch.remove_unnecessary_recursion/help.html @@ -0,0 +1,836 @@ + + + + + + + + + +JSLint: Help + + + +
    JSLint
    + +
    Help
    +
    +
    Il semble que la perfection soit atteinte non quand il n’y a + plus rien à ajouter, mais quand il n’y a plus rien à + retrancher.
    +
    + Antoine de Saint-Exupéry +
    +
    + Terre des Hommes (1939) +
    +

    Good Parts

    +

    The Principle of the Good Parts is

    +
    If a feature is sometimes useful and sometimes dangerous and if + there is a better option then always use the better option.
    +

    JSLint is a JavaScript program that looks for problems in + JavaScript programs. It is a code quality tool.

    +

    When C was + a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    +

    As the language matured, the definition of the language was + strengthened to eliminate some insecurities, and compilers got better + at issuing warnings. lint is no longer needed.

    +

    JavaScript is a + young-for-its-age language. It was originally intended to do small tasks in + webpages, tasks for which Java was too heavy and clumsy. But JavaScript is + a surprisingly capable language, and it is now being used in larger + projects. Many of the features that were intended to make the language easy + to use are troublesome when projects become complicated. A lint + for JavaScript is needed: JSLint, a JavaScript syntax checker + and validator.

    +

    JSLint takes a JavaScript source and scans it. If it finds a + problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by the ECMAScript Programming Language + Standard (the strangely named document that governs JavaScript). + JSLint will reject most legal programs. It is a higher + standard.

    +

    JavaScript is a sloppy language, but hidden deep inside there is an elegant, + better language. JSLint helps you to program in that better + language and to avoid most of the slop. JSLint will reject + programs that browsers will accept because JSLint is concerned + with the quality of your code and browsers are not. You should gladly accept + all of JSLint's advice.

    +

    JSLint can operate on JavaScript source + or JSON text.

    +

    ECMAScript Sixth Edition

    +

    Some of + ES6’s features are good, so JSLint will recognize the good + parts of ES6.

    +

    Currently, these features are recognized:

    +
      +
    • The ... ellipsis marker in parameter + lists and argument lists, replacing the arguments object + for variadic functions.
    • +
    • The let statement, which is like the var + statement except that it respects block scope. + You may use let or var but not both.
    • +
    • The const statement is like the let statement + except that it disallows the use of assignment on the variable, although + if the value of the variable is mutable, it can still be mutated. + const is preferred to let. +
    • Destructuring of arrays and objects is allowed in parameter lists and on + the left side of let, and const, but not + var or assignment statements, and not deep destructuring + or eliding.
    • +
    • Enhanced object literals, providing shorter forms for function + declaration and properties that are initialized by variables with the + same name.
    • +
    • The fat arrow => fart functions.
    • +
    • The simplest forms of import and export.
    • +
    • `Megastring` literals, but not nested + `megastring` literals.
    • +
    • New global functions, such as Map, Set, + WeakMap, and WeakSet.
    • +
    • 0b- and 0o- number literals.
    • +
    +

    The most important new feature of ES6 is proper tail calls. This has no + new syntax, so JSLint doesn’t see it. But it makes recursion + much more attractive, which makes loops, particularly for + loops, much less attractive.

    +

    import export

    +

    The ES6 module feature will be an important improvement over JavaScript’s + global variables as a means of linking separate files together. + JSLint recognizes a small but essential subset of the module + syntax.

    +
    +

    import name from stringliteral;

    +

    import {name} from stringliteral;

    +

    export default function () {};

    +

    export default function name() {};

    +

    export default expression;

    +

    export function name() {}

    +

    export name; // where name is const

    +

    export {name};

    +
    +

    Directives

    +

    JSLint provides three directives that may be placed in a + file to manage JSLint’s behavior. Use of these directives is + optional. If they are used, they should be placed in a source file before + the first statement. They are written in the form of a comment, where the + directive name is placed immediately after the opening of the comment before + any whitespace. The three directives are global, + jslint, and property. Directives in a file are + stronger than options selected from the UI or passed with the option object.

    +

    /*global*/

    +

    The /*global*/ directive is used to specify a set of globals + (usually functions and objects containing functions) that are available to + this file. This was commonly used in browsers to link source files together + before ES6 modules appeared. Use of global variables is strongly + discouraged, but unfortunately web browsers require their use. The + /*global*/ directive can only be used when the + Assume a browser option is selected. +

    Each of the names listed will indicate a read-only global variable. The names + are separated by , comma. This directive + should not be used if import or export is used. + This directive inhibits warnings, but it does not declare the names in the + execution environment. +

    For example: +

    /*global +
    ADSAFE, report, jslint
    +*/
    +

    instructs JSLint to not give warnings about the global + variables ADsafe, report, and jslint. + However, if any of those names are expected to be supplied by other files + and those other files fail to do so, then execution errors will result. It + is usually better to use top-level variable declarations instead: +

        var ADSAFE;
    +    var report;
    +    var jslint; 
    +

    Using var in this way allows comparing a global variable to the + undefined value to determine whether it is has been used in the global context.

    +

    /*jslint*/

    +

    The /*jslint*/ directive allows for the control of several + options. These options can also be set from the + JSLint.com user interface.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Tolerate bitwise operatorsbitwisetrue if bitwise operators should be allowed. The + bitwise operators are rarely used in JavaScript programs, so it + is usually more likely that & is a mistyping of + && than that it indicates a bitwise and. + JSLint will give warnings on the bitwise operators + unless this option is selected.
    Assume a browser browsertrue if the standard browser globals should be + predefined. This option will reject the use of + import and export. This option also + disallows the file form of the "use + strict" pragma. It does not supply + self; you will have to request that unnecessary + alias of the dreaded global object yourself. It adds the same + globals as this directive: +
    /*global +
    + clearInterval, clearTimeout, document, event, FileReader, + FormData, history, localStorage, location, name, navigator, + screen, sessionStorage, setInterval, setTimeout, Storage, + URL, window, XMLHttpRequest +
    + */
    +
    Tolerate conversion operatorsconverttrue if !!, + prefix, + and concatenation with "" are allowed. + The Boolean, Number, and + String functions are preferred
    Assume CouchDBcouchtrue if + Couch DB + globals should be predefined. It adds the same globals as this + directive: +
    /*global +
    emit, getRow, isArray, log, provides, registerType, require, + send, start, sum, toJSON
    + */
    Assume in developmentdeveltrue if browser globals that are useful in + development should be predefined, and if debugger + statements and TODO comments + should be allowed. It adds the + same globals as this directive: +
    /*global +
    alert, confirm, console, prompt
    + */
    Be sure to turn this option off before going + into production.
    Tolerate evalevaltrue if eval should be allowed. In the + past, the eval function was the most misused + feature of the language.
    Tolerate for statementfortrue if the for statement should be + allowed. It is almost always better to use the array methods + instead.
    Fudge the line and column numbersfudgetrue if the origin of a text file is line 1 column + 1. Programmers know that number systems start at zero. + Unfortunately, most text editors start numbering lines and + columns at one. JSLint will by default start + numbering at zero. Use this option to start the numbering at one + in its reports.
    Tolerate get and setgetsettrue if accessor properties are allowed in object literals.
    Tolerate long source lineslongtrue if a line can contain more than 80 characters.
    Tolerate multiple variables declarations per statementmultivartrue if a var, let, or + const statement can declare two or more variables + in a single statement.
    Assume Node.jsnodetrue if Node.js globals should be predefined. It + will predefine globals that are used in the Node.js environment. + It adds the same globals as this directive: +
    /*global +
    + Buffer, clearImmediate, clearInterval, clearTimeout, console, exports, + module, process, require, setImmediate, setInterval, setTimeout, URL, + URLSearchParams, __dirname, __filename +
    + */
    Tolerate single quote stringssingletrue if 'single quote + should be allowed to enclose string literals.
    Tolerate this
    thistrue if this should be allowed.
    Tolerate whitespace messwhitetrue if the whitespace rules should be ignored.
    +

    For example:

    +
    /*jslint
    +    bitwise, node
    +*/
    + +

    /*property*/

    +

    The /*property*/ directive is used to declare a list of + property identifiers that are used in the file. Each property name in the + program is looked up in this list. If a name is not found, that indicates an + error, most likely a typing error.

    +

    The list can also be used to evade some of JSLint’s naming + rules.

    +

    JSLint can build the /*property*/ list for you. At + the bottom of its report, JSLint displays a + /*property*/ directive. It contains all of the names that were + used with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. You + can copy the /*property*/ directive to the top of your + script file. JSLint will check the spelling of all property + names against the list. That way, you can have JSLint look for + misspellings for you.

    +

    For example,

    +
    /*property +
    charAt, slice, _$_
    + */
    +

    ignore

    +

    JSLint introduces a new reserved word: ignore. It + is used in parameter lists and in catch clauses to indicate a + parameter that will be ignored. Unused warnings will not be produced for + ignore. +

    function handler(ignore, value) {
    +    return do_something_useful(value);
    +}
    +

    var let const

    +

    JavaScript provides three statements for declaring variables: + var, let, and const. The + var statement suffers from a bad practice called hoisting. The + let statement does not do hoisting and respects block scope. + The const statement is like let except that it + marks the variable (but not its contents) as read only, making it an error + to attempt to assign to the variable. When given a choice, + const is the best, var is the worst.

    +

    JSLint uses the intersection of the var rules and the + let rules, and by doing so avoids the errors related to either. + A name should be declared only once in a function. It should be declared + before it is used. It should not be used outside of the block in which it is + declared. A variable should not have the same name as a variable or + parameter in an outer function. Do not mix var and + let. Declare one name per statement.

    +

    = == ===

    +

    Fortran made a terrible + mistake in using the equality operator as its assignment operator. That + mistake has been replicated in most languages since then. C compounded that + mistake by making == its equality operator. The visual + similarity is a source of errors. JavaScript compounded this further by + making == a type coercing comparison operator that produces + false positive results. This was mitigated by adding the === + operator, leaving the broken == operator in place.

    +

    JSLint attempts to minimize errors by the following rules:

    +

    == is not allowed. This avoids the false positives, and + increases the visual distance between = and ===. + Assignments are not allowed in expression position, and comparisons are not + allowed in statement position. This also reduces confusion. 

    +

    Semicolon

    +

    JavaScript uses a C-like syntax that requires the use of semicolons to + delimit certain statements. JavaScript attempts to make those semicolons + optional with an automatic semicolon insertion mechanism, but it does not + work very well. Automatic semicolon insertion was added to make + things easier for beginners. Unfortunately, it sometimes fails. Do not rely + on it unless you are a beginner.

    +

    JSLint expects that every statement will be followed by + ; except for for, function, + if, switch, try, and + while. JSLint does not expect to see unnecessary + semicolons, the empty statement, or empty blocks.

    +

    function =>

    +

    JavaScript has four syntactic forms for making function objects: function + statements, function expressions, enhanced object literals, and the + => fart operator.

    +

    The function statement creates a variable and assigns the function object to + it. It should be used in a file or function body, but not inside of a block.

    +
    function name(parameters) { +
    statements
    + }
    +

    The function expression unfortunately looks like the function statement. It + may appear anywhere that an expression may appear, but not in statement + position and not in a loop. It produces a function object but does not + create a variable in which to store it.

    +
    function (parameters) { +
    statements
    + }
    +

    The enhanced object literal provides an ES6 shorthand for creating a property + whose value is a function, saving you from having to type + : colon and function.

    +
    { +
    name(parameters) { +
    statements
    + }
    + }
    +

    Finally, ES6 provides an even shorter form of function expression that leaves + out the words function and return:

    +
    (parameters) => + expression
    +

    JSLint requires the parens around the parameters, and forbids a + { left brace after the + => fart to avoid syntactic ambiguity.

    +

    Comma

    +

    The , comma operator is unnecessary and can + mask programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as + an operator. It does not expect to see elided elements in array literals. A + comma should not appear after the last element of an array literal or object + literal.

    +

    Blocks

    +

    JSLint expects blocks with function, + if, switch, while, for, + do, and try statements and nowhere else.

    +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    +

    JavaScript allows an if to be written like this:

    +
    if (condition)
    +    statement;
    +

    That form is known to contribute to mistakes in projects where many + programmers are working on the same code. That is why JSLint + expects the use of a block:

    +
    if (condition) {
    +    statements;
    +}
    +

    Experience shows that this form is more resilient.

    +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call. All other expression statements are considered + to be errors.

    +

    for

    +

    JSLint does not recommend use of the for statement. + Use array methods like forEach instead. The for + option will suppress some warnings. The forms of for that + JSLint accepts are restricted, excluding the new ES6 forms.

    +

    for in

    +

    JSLint does not recommend use of the for + in statement. Use Object.keys instead.

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, it also + loops through all of the properties that were inherited through the + prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written + without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    for (name in object) {
    +    if (object.hasOwnProperty(name)) {
    +        ....
    +    }
    +}
    +

    Note that the above code will fail if the object contains a property + named hasOwnProperty. Use Object.keys instead.

    +

    switch

    +

    A common error in switch statements is to forget to place a + break statement after each case, resulting in unintended + fall-through. JSLint expects that the statement before the next + case or default is one of these: + break, return, or throw.

    +

    with

    +

    The with statement was intended to provide a shorthand in + accessing properties in deeply nested objects. Unfortunately, it behaves + very badly when setting new properties. Never use the with + statement.

    +

    JSLint does not expect to see a with statement.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that + labels will be distinct from vars and parameters.

    +

    Unreachable Code

    +

    JSLint expects that a return, break, + or throw statement will be followed by a + } right brace or case or + default.

    +

    Confusing Pluses and Minuses

    +

    JSLint expects that + will not be followed by + + or ++, and that - will not be + followed by - or --. A misplaced space can turn + + + into ++, an error that is difficult to see. + Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ increment and + -- decrement operators have been known to + contribute to bad code by encouraging excessive trickiness. They are second + only to faulty architecture in enabling to viruses and other security + menaces. Also, preincrement/postincrement confusion can produce off-by-one + errors that are extremely difficult to diagnose. Fortunately, they are also + complete unnecessary. There are better ways to add 1 to a variable.

    +

    It is best to avoid these operators entirely and rely on += and + -= instead.

    +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. + JSLint looks for problems that may cause portability problems. + It also attempts to resolve visual ambiguities by recommending explicit + escapement.

    +

    JavaScript’s syntax for regular expression literals overloads the + / slash character. To avoid ambiguity, + JSLint expects that the character preceding a regular + expression literal is a ( left paren or + = equal or + : colon or + , comma character.

    +

    this

    +
    Having this in the language makes it harder to talk + about the language. It is like pair programming with Abbott and Costello. +
    +

    Avoid using this. Warnings about this can be + suppressed with option.this.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the + new prefix. The new prefix creates a new object + based on the function's prototype, and binds that object to the + function's implied this parameter. If you neglect to use the + new prefix, no new object will be made and this + will be bound to the global object.

    +

    JSLint enforces the convention that constructor functions be + given names with initial uppercase. JSLint does not expect to + see a function invocation with an initial uppercase name unless it has the + new prefix. JSLint does not expect to see the + new prefix used with functions whose names do not start with + initial uppercase.

    +

    JSLint does not expect to see the wrapper forms + new Number, new String, new Boolean.

    +

    JSLint does not expect to see new Object. + Use Object.create(null) instead.

    +

    Whitespace

    +

    JSLint has a specific set of rules about the use of whitespace. + Where possible, these rules are consistent with centuries of good practice + with literary style.

    +

    The indentation increases by 4 spaces when the last token on a line is + { left brace, + [ left bracket, + ( left paren. The matching closing + token will be the first token on a line, restoring the previous + indentation.

    +

    The ternary operator can be visually confusing, so + ? question mark and + : colon always begin a line and increase + the indentation by 4 spaces.

    +
    return (
    +    (the_token.id === "(string)" || the_token.id === "(number)")
    +    ? String(the_token.value)
    +    : the_token.id
    +);
    +

    The word function is always followed with one space.

    +

    Clauses (case, catch, default, + else, finally) are not statements and so should + not be indented like statements.

    +

    Spaces are used to make things that are not invocations look less like + invocations.

    +

    Tabs and spaces should not be mixed. We should pick just one in order to + avoid the problems that come from having both. Personal preference is an + extremely unreliable criterion. Neither offers a powerful advantage over the + other. Fifty years ago, tab had the advantage of consuming less memory, but + Moore's Law has eliminated that advantage. Space has one clear advantage + over tab: there is no reliable standard for how many spaces a tab + represents, but it is universally accepted that a space occupies a space. So + use spaces. You can edit with tabs if you must, but make sure it is spaces + again before you commit. Maybe someday we will finally get a universal + standard for tabs, but until that day comes, the better choice is spaces.

    +

    Unsafe Characters

    +

    There are characters that are handled inconsistently in some browsers, and + so must be escaped when placed in strings.

    +
    \u0000-\u001f
    +\u007f-\u009f
    +\u00ad
    +\u0600-\u0604
    +\u070f
    +\u17b4
    +\u17b5
    +\u200c-\u200f
    +\u2028-\u202f
    +\u2060-\u206f
    +\ufeff
    +\ufff0-\uffff
    +

    Report

    +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    +
      +
    • The line number on which it starts.
    • +
    • The function’s name. In the case of anonymous functions, + JSLint will attempt to + «guess» the name.
    • +
    • The parameters.
    • +
    • Variables: The variables that are declared in the function.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Exceptions: The variables that are declared by catch + clauses of try statements.
    • +
    • Outer: Variables used by this function that are declared in + other functions.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements. Updates are announced at + https://plus.google.com/communities/104441363299760713736.

    +

    Try it

    +

    Try it. Paste your script + into the window and click the + + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    JSLint is written entirely in JavaScript, so it can run + anywhere that JavaScript (or even Java) can run.

    +

    Perfectly fine

    +

    JSLint was designed to reject code that some would consider to + be perfectly fine. The reason for this is that JSLint's + purpose is to help produce programs that are free of error. That is + difficult in any language and is especially hard in JavaScript. + JSLint attempts to help you increase the visual distance + between correct programs and incorrect programs, making the remaining errors + more obvious. JSLint will give warnings about things that are + not necessarily wrong in the current situation, but which have been observed + to mask or obscure errors. Avoid those things when there are better options + available.

    +

    It is dangerous out there. JSLint is here to help.

    +

    Warning

    +

    JSLint will hurt your feelings. Side effects may include + headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, + dry mouth, cleaner code, and a reduced error rate.

    +

    Further Reading

    + +
    +

    + Code Conventions for the JavaScript Programming Language.

    + + + + + +
    + + diff --git a/branch.remove_unnecessary_recursion/index.html b/branch.remove_unnecessary_recursion/index.html new file mode 100644 index 000000000..3d0e4fa4f --- /dev/null +++ b/branch.remove_unnecessary_recursion/index.html @@ -0,0 +1,124 @@ + + + + + + + + + + + +JSLint: The JavaScript Code Quality Tool + + +
    +
    Source
    +
    + + +
    +
    + Warnings +
    +
    +
    + Function Report +
    +
    +
    + Property Directive + +
    +
    + + + + +
    +
    Options +
    Assume... +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Global variables... + +
    +
    +
    +
    + + + + + +
    + + diff --git a/branch.remove_unnecessary_recursion/jslint.css b/branch.remove_unnecessary_recursion/jslint.css new file mode 100644 index 000000000..9c1b1a2f9 --- /dev/null +++ b/branch.remove_unnecessary_recursion/jslint.css @@ -0,0 +1,340 @@ +@font-face { + font-family: 'Programma'; + src: url('Programma.woff') format('woff'); +} +@font-face { + font-family: 'Daley'; + font-weight: bold; + src: url('Daley-Bold.woff') format('woff'); +} + +body { + background-color: antiquewhite; + color: black; + font-family: 'Daley', sans-serif; + margin: 0; + padding: 0; +} + +#JSLINT_TITLEBOX { + font-family: 'Daley', sans-serif; + margin: 0; + margin-left: 3%; + margin-right: 3%; + padding: 0; +} + +#JSLINT_TITLEBOX ul { + float: right; + font-size: 12pt; +} + +#JSLINT_TITLEBOX div { + color: darkslategray; + float: left; + font-size: 48pt; +} + +#JSLINT_ fieldset { + background-color: gainsboro; + border: 0; + clear: both; + margin-bottom: 1em; + margin-left: 3%; + margin-right: 3%; + margin-top: 1em; + padding: 0; + width: auto; +} +#JSLINT_ legend { + background-color: darkslategray; + border: 0; + color: white; + font-size: 100%; + font-style: normal; + font-weight: normal; + margin: 0; + padding-bottom: 0.25em; + padding-left: 0; + padding-right: 0; + padding-top: 0.25em; + text-align: center; + width: 100%; +} +#JSLINT_ button { + background-color: darkslategray; + border: 0; + color: white; + cursor: pointer; + font-family: 'Daley', sans-serif; + font-size: 100%; + font-style: normal; + margin-left: 1em; + margin-right: 1em; + padding-left: 1em; + padding-right: 1em; + padding-bottom: 0.25em; + padding-top: 0.25em; + text-align: center; +} +#JSLINT_ button:hover { + background-color: slategray; + color: white; + border: 0; +} + +#JSLINT_ button:active { + border: 0; + color: black; +} + +#JSLINT_ button:disabled { + background-color: gray; + border: 0; + color: gainsboro; +} + +a:visited { + color: #69565c; +} + +a:link { + color: black; +} + +#JSLINT_ input[type="text"] { + background-color: white; + border: 0; + color: black; + margin-bottom: 0.2em; + margin-right: 0.5em; + padding-left: 0.5em; + padding-right: 0.5em; + text-align: right; + width: 3em; +} + +#JSLINT_ textarea { + border: 0; + background-color: white; + border: 0; + font-family: Programma, monospace; + font-size: 100%; + font-weight: bold; + resize: none; +} + +#JSLINT_ textarea::selection { + background-color: yellow; + color: black; +} + +#JSLINT_NUMBER { + color: darkgray; + display: inline-block; + height: 4in; + margin: 2%; + margin-right: 0; + overflow: hidden; + padding-right: 0.5%; + text-align: right; + white-space: pre; + width: 4%; +} + +#JSLINT_SOURCE { + color: black; + display: inline-block; + font-size: 100%; + height: 4in; + margin: 2%; + margin-left: 0; + margin-right: 0; + overflow: auto; + padding-left: 0.5%; + white-space: pre; + width: 91%; +} + +#JSLINT_PROPERTY { + background-color: honeydew; + border: 0; + margin: 2%; + padding: 0.5%; + white-space: pre; + width: 95%; +} + +#JSLINT_GLOBAL { + white-space: normal; + width: 95%; +} +#JSLINT_ label { + font-family: sans-serif; + font-size: 90%; + padding-left: 0.25em; +} +#JSLINT_OPTIONS>div { + float: left; + margin: 0.5em; +} + +#JSLINT_ address { + color: black; + display: block; + float: right; + font-family: serif; + font-size: 90%; + margin-left: 1em; +} +#JSLINT_ dl { + background-color: cornsilk; + color: white; + margin: 0; + padding-bottom: 2pt; + padding-left: 1em; + padding-right: 1em; + padding-top: 2pt; +} +#JSLINT_ dfn { + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + margin-bottom: 2pt; +} +#JSLINT_ dt { + color: black; + display: block; + float: left; + font-family: serif; + font-size: 75%; + font-style: italic; + margin: 0; + width: 8em; + text-align: right; +} +#JSLINT_ dd { + color: black; + display: block; + font-family: Programma, monospace; + font-weight: bold; + margin-left: 8em; + padding-bottom: 2pt; +} +#JSLINT_WARNINGS>legend { + background-color: indianred; +} +#JSLINT_WARNINGS>div { + background-color: pink; + padding: 1em; +} +#JSLINT_WARNINGS cite { + color: black; + display: block; + font-family: serif; + font-size: 100%; + font-style: normal; + margin-bottom: 4pt; + margin-left: 20pt; + margin-right: 20pt; + margin-top: 4pt; + overflow-x: hidden; +} +#JSLINT_WARNINGS samp { + background-color: lavenderblush; + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + padding: 4pt; + margin-bottom: 0; + margin-left: 16pt; + margin-right: 16pt; + margin-top: 0; + white-space: pre-wrap; +} + +#JSLINT_REPORT center { + margin-top: 1em; +} + +#JSLINT_WARNINGS dl address { + color: black; + display: inline; + float: none; + font-size: 80%; + margin: 0; +} + +#JSLINT_REPORT>div { + padding: 1em; +} + +#JSLINT_ dl.level0 { + color: black; + background-color: white; +} + +#JSLINT_ dl.level1 { + color: black; + background-color: #ffffe0; /* yellow */ + margin-left: 1em; +} + +#JSLINT_ dl.level2 { + color: black; + background-color: #e0ffe0; /* green */ + margin-left: 2em; +} + +#JSLINT_ dl.level3 { + color: black; + background-color: #e0e0ff; /* blue */ + margin-left: 3em; +} + +#JSLINT_ dl.level4 { + background-color: #ffe0ff; /* purple */ + color: black; + margin-left: 4em; +} + +#JSLINT_ dl.level5 { + background-color: #ffe0e0; /* red */ + color: black; + margin-left: 5em; +} + +#JSLINT_ dl.level6 { + background-color: #ffe390; /* orange */ + color: black; + margin-left: 6em; +} + +#JSLINT_ dl.level7 { + background-color: #e0e0e0; /* gray */ + color: black; + margin-left: 7em; +} + +#JSLINT_ dl.level8 { + color: black; + margin-left: 8em; +} + +#JSLINT_ dl.level9 { + color: black; + margin-left: 9em; +} + +.none { + display: none; +} +.center { + text-align: center; +} diff --git a/branch.remove_unnecessary_recursion/jslint.js b/branch.remove_unnecessary_recursion/jslint.js new file mode 100644 index 000000000..8141a8cdb --- /dev/null +++ b/branch.remove_unnecessary_recursion/jslint.js @@ -0,0 +1,4984 @@ +// jslint.js +// 2018-10-26 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// jslint(source, option_object, global_array) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze, a string or an array of strings. +// option_object An object whose keys correspond to option names. +// global_array An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all of the functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// 1. If the source is a single string, split it into an array of strings. +// 2. Turn the source into an array of tokens. +// 3. Furcate the tokens into a parse tree. +// 4. Walk the tree, traversing all of the nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// 5. Check the whitespace between the tokens. + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*property + a, and, arity, assign, b, bad_assignment_a, bad_directive_a, bad_get, + bad_module_name_a, bad_option_a, bad_property_a, bad_set, bitwise, block, + body, browser, c, calls, catch, charCodeAt, closer, closure, code, column, + complex, concat, constant, context, convert, couch, create, d, dead, + default, devel, directive, directives, disrupt, dot, duplicate_a, edition, + ellipsis, else, empty_block, escape_mega, eval, every, expected_a, + expected_a_at_b_c, expected_a_b, expected_a_b_from_c_d, expected_a_before_b, + expected_a_next_at_b, expected_digits_after_a, expected_four_digits, + expected_identifier_a, expected_line_break_a_b, expected_regexp_factor_a, + expected_space_a_b, expected_statements_a, expected_string_a, + expected_type_string_a, exports, expression, extra, finally, flag, for, + forEach, free, freeze, freeze_exports, from, froms, fud, fudge, + function_in_loop, functions, g, getset, global, i, id, identifier, import, + inc, indexOf, infix_in, init, initial, isArray, isNaN, join, json, keys, + label, label_a, lbp, led, length, level, line, lines, live, long, loop, m, + margin, match, message, misplaced_a, misplaced_directive_a, missing_browser, + missing_m, module, multivar, naked_block, name, names, nested_comment, new, + node, not_label_a, nr, nud, number_isNaN, ok, open, opening, option, + out_of_scope_a, parameters, parent, pop, property, push, quote, + redefinition_a_b, replace, required_a_optional_b, reserved_a, right, role, + search, shebang, signature, single, slice, some, sort, split, startsWith, + statement, stop, strict, subscript_a, switch, test, this, thru, toString, + todo_comment, tokens, too_long, too_many_digits, tree, try, type, u, + unclosed_comment, unclosed_mega, unclosed_string, undeclared_a, + unexpected_a, unexpected_a_after_b, unexpected_a_before_b, + unexpected_at_top_level_a, unexpected_char_a, unexpected_comment, + unexpected_directive_a, unexpected_expression_a, unexpected_label_a, + unexpected_parens, unexpected_space_a_b, unexpected_statement_a, + unexpected_trailing_space, unexpected_typeof_a, uninitialized_a, + unreachable_a, unregistered_property_a, unsafe, unused_a, use_double, + use_open, use_spaces, use_strict, used, value, var_loop, var_switch, + variable, warning, warnings, weird_condition_a, weird_expression_a, + weird_loop, weird_relation_a, white, wrap_condition, wrap_immediate, + wrap_parameter, wrap_regexp, wrap_unary, wrapped, writable, y +*/ + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than '{}' because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +function populate(array, object = empty(), value = true) { + +// Augment an object by taking property names from an array of strings. + + array.forEach(function (name) { + object[name] = value; + }); + return object; +} + +const allowed_option = { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + bitwise: true, + browser: [ + "caches", "clearInterval", "clearTimeout", "document", "DOMException", + "Element", "Event", "event", "FileReader", "FormData", "history", + "localStorage", "location", "MutationObserver", "name", "navigator", + "screen", "sessionStorage", "setInterval", "setTimeout", "Storage", + "TextDecoder", "TextEncoder", "URL", "window", "Worker", + "XMLHttpRequest" + ], + couch: [ + "emit", "getRow", "isArray", "log", "provides", "registerType", + "require", "send", "start", "sum", "toJSON" + ], + convert: true, + devel: [ + "alert", "confirm", "console", "prompt" + ], + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + multivar: true, + node: [ + "Buffer", "clearImmediate", "clearInterval", "clearTimeout", + "console", "exports", "module", "process", "require", + "setImmediate", "setInterval", "setTimeout", "TextDecoder", + "TextEncoder", "URL", "URLSearchParams", "__dirname", "__filename" + ], + single: true, + this: true, + white: true +}; + +const anticondition = populate([ + "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%", + "typeof", "(number)", "(string)" +]); + +// These are the bitwise operators. + +const bitwiseop = populate([ + "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=", + ">>>", ">>>=" +]); + +const escapeable = populate([ + "\\", "/", "`", "b", "f", "n", "r", "t" +]); + +const opener = { + +// The open and close pairs. + + "(": ")", // paren + "[": "]", // bracket + "{": "}", // brace + "${": "}" // mega +}; + +// The relational operators. + +const relationop = populate([ + "!=", "!==", "==", "===", "<", "<=", ">", ">=" +]); + +// This is the set of infix operators that require a space on each side. + +const spaceop = populate([ + "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/", + "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=", + ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" +]); + +const standard = [ + +// These are the globals that are provided by the language standard. + + "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI", + "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error", + "EvalError", "Float32Array", "Float64Array", "Generator", + "GeneratorFunction", "Int8Array", "Int16Array", "Int32Array", "Intl", + "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat", + "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", + "Set", "String", "Symbol", "SyntaxError", "System", "TypeError", + "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", + "URIError", "WeakMap", "WeakSet" +]; + +const bundle = { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + and: "The '&&' subexpression should be wrapped in parens.", + bad_assignment_a: "Bad assignment to '{a}'.", + bad_directive_a: "Bad directive '{a}'.", + bad_get: "A get function takes no parameters.", + bad_module_name_a: "Bad module name '{a}'.", + bad_option_a: "Bad option '{a}'.", + bad_property_a: "Bad property name '{a}'.", + bad_set: "A set function takes one parameter.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + escape_mega: "Unexpected escapement in mega literal.", + expected_a: "Expected '{a}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: ( + "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'." + ), + expected_a_before_b: "Expected '{a}' before '{b}'.", + expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.", + expected_digits_after_a: "Expected digits after '{a}'.", + expected_four_digits: "Expected four digits after '\\u'.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.", + expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.", + expected_space_a_b: "Expected one space between '{a}' and '{b}'.", + expected_statements_a: "Expected statements before '{a}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + expected_type_string_a: "Expected a type string and instead saw '{a}'.", + freeze_exports: ( + "Expected 'Object.freeze('. All export values should be frozen." + ), + function_in_loop: "Don't make functions within a loop.", + infix_in: ( + "Unexpected 'in'. Compare with undefined, " + + "or use the hasOwnProperty method instead." + ), + label_a: "'{a}' is a statement label.", + misplaced_a: "Place '{a}' at the outermost level.", + misplaced_directive_a: ( + "Place the '/*{a}*/' directive before the first statement." + ), + missing_browser: "/*global*/ requires the Assume a browser option.", + missing_m: "Expected 'm' flag on a multiline regular expression.", + naked_block: "Naked block.", + nested_comment: "Nested comment.", + not_label_a: "'{a}' is not a label.", + number_isNaN: "Use Number.isNaN function to compare with NaN.", + out_of_scope_a: "'{a}' is out of scope.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + required_a_optional_b: ( + "Required parameter '{a}' after optional parameter '{b}'." + ), + reserved_a: "Reserved name '{a}'.", + subscript_a: "['{a}'] is better written in dot notation.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line is longer than 80 characters.", + too_many_digits: "Too many digits.", + unclosed_comment: "Unclosed comment.", + unclosed_mega: "Unclosed mega literal.", + unclosed_string: "Unclosed string.", + undeclared_a: "Undeclared '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_a_after_b: "Unexpected '{a}' after '{b}'.", + unexpected_a_before_b: "Unexpected '{a}' before '{b}'.", + unexpected_at_top_level_a: "Expected '{a}' to be in a function.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_directive_a: "When using modules, don't use directive '/*{a}'.", + unexpected_expression_a: ( + "Unexpected expression '{a}' in statement position." + ), + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_parens: "Don't wrap function literals in parens.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_statement_a: ( + "Unexpected statement '{a}' in expression position." + ), + unexpected_trailing_space: "Unexpected trailing space.", + unexpected_typeof_a: ( + "Unexpected 'typeof'. Use '===' to compare directly with {a}." + ), + uninitialized_a: "Uninitialized '{a}'.", + unreachable_a: "Unreachable '{a}'.", + unregistered_property_a: "Unregistered property name '{a}'.", + unsafe: "Unsafe character '{a}'.", + unused_a: "Unused '{a}'.", + use_double: "Use double quotes, not single quotes.", + use_open: ( + "Wrap a ternary expression in parens, " + + "with a line break after the left paren." + ), + use_spaces: "Use spaces, not tabs.", + use_strict: "This function needs a \"use strict\" pragma.", + var_loop: "Don't declare variables in a loop.", + var_switch: "Don't declare variables in a switch.", + weird_condition_a: "Weird condition '{a}'.", + weird_expression_a: "Weird expression '{a}'.", + weird_loop: "Weird loop.", + weird_relation_a: "Weird relation '{a}'.", + wrap_condition: "Wrap the condition in parens.", + wrap_immediate: ( + "Wrap an immediate function invocation in parentheses to assist " + + "the reader in understanding that the expression is the result " + + "of a function, and not the function itself." + ), + wrap_parameter: "Wrap the parameter in parens.", + wrap_regexp: "Wrap this regexp in parens to avoid confusion.", + wrap_unary: "Wrap the unary expression in parens." +}; + +// Regular expression literals: + +// supplant {variables} +const rx_supplant = /\{([^{}]*)\}/g; +// carriage return, carriage return linefeed, or linefeed +const rx_crlf = /\n|\r\n?/; +// unsafe characters that are silently deleted by one or more browsers +const rx_unsafe = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; +// identifier +const rx_identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; +const rx_module = /^[a-zA-Z0-9_$:.@\-\/]+$/; +const rx_bad_property = /^_|\$|Sync\$|_$/; +// star slash +const rx_star_slash = /\*\//; +// slash star +const rx_slash_star = /\/\*/; +// slash star or ending slash +const rx_slash_star_or_slash = /\/\*|\/$/; +// uncompleted work comment +const rx_todo = /\b(?:todo|TO\s?DO|HACK)\b/; +// tab +const rx_tab = /\t/g; +// directive +const rx_directive = /^(jslint|property|global)\s+(.*)$/; +const rx_directive_part = /^([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*(.*)$/; +// token (sorry it is so long) +const rx_token = /^((\s+)|([a-zA-Z_$][a-zA-Z0-9_$]*)|[(){}\[\],:;'"~`]|\?\.?|=(?:==?|>)?|\.+|[*\/][*\/=]?|\+[=+]?|-[=\-]?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<= "a" && string <= "z\uffff") + || (string >= "A" && string <= "Z\uffff") + ); +} + +function supplant(string, object) { + return string.replace(rx_supplant, function (found, filling) { + const replacement = object[filling]; + return ( + replacement !== undefined + ? replacement + : found + ); + }); +} + +let anon; // The guessed name for anonymous functions. +let blockage; // The current block. +let block_stack; // The stack of blocks. +let declared_globals; // The object containing the global declarations. +let directives; // The directive comments. +let directive_mode; // true if directives are still allowed. +let early_stop; // true if JSLint cannot finish. +let exports; // The exported names and values. +let froms; // The array collecting all import-from strings. +let fudge; // true if the natural numbers start with 1. +let functionage; // The current function. +let functions; // The array containing all of the functions. +let global; // The global object; the outermost context. +let json_mode; // true if parsing JSON. +let lines; // The array containing source lines. +let mega_mode; // true if currently parsing a megastring literal. +let module_mode; // true if import or export was used. +let next_token; // The next token to be examined in the parse. +let option; // The options parameter. +let property; // The object containing the tallied property names. +let shebang; // true if a #! was seen on the first line. +let stack; // The stack of functions. +let syntax; // The object containing the parser. +let token; // The current token being examined in the parse. +let token_nr; // The number of the next token. +let tokens; // The array of tokens. +let tenure; // The predefined property registry. +let tree; // The abstract parse tree. +let var_mode; // "var" if using var; "let" if using let. +let warnings; // The array collecting all generated warnings. + +// Error reportage functions: + +function artifact(the_token) { + +// Return a string representing an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); +} + +function artifact_line(the_token) { + +// Return the fudged line number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.line + fudge; +} + +function artifact_column(the_token) { + +// Return the fudged column number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.from + fudge; +} + +function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + const warning = { // ~~ + name: "JSLintError", + column: column, + line: line, + code: code + }; + if (a !== undefined) { + warning.a = a; + } + if (b !== undefined) { + warning.b = b; + } + if (c !== undefined) { + warning.c = c; + } + if (d !== undefined) { + warning.d = d; + } + warning.message = supplant(bundle[code] || code, warning); + warnings.push(warning); + return warning; +} + +function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); +} + +function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + if (the_token.warning === undefined) { + the_token.warning = warn_at( + code, + the_token.line, + the_token.from, + a || artifact(the_token), + b, + c, + d + ); + return the_token.warning; + } +} + +function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); +} + +// Tokenize: + +function tokenize(source) { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + +// If the source is not an array, then it is split into lines at the +// carriage return/linefeed. + + lines = ( + Array.isArray(source) + ? source + : source.split(rx_crlf) + ); + tokens = []; + + let char; // a popular character + let column = 0; // the column number of the next character + let first; // the first token + let from; // the starting column number of the token + let line = -1; // the line number of the next character + let nr = 0; // the next token number + let previous = global; // the previous token including comments + let prior = global; // the previous token excluding comments + let mega_from; // the starting column of megastring + let mega_line; // the starting line of megastring + let regexp_seen; // regular expression literal seen on this line + let snippet; // a piece of string + let source_line = ""; // the remaining line source string + let whole_line = ""; // the whole line source string + + if (lines[0].startsWith("#!")) { + line = 0; + shebang = true; + } + + function next_line() { + +// Put the next line of source in source_line. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + let at; + if ( + !option.long + && whole_line.length > 80 + && !json_mode + && first + && !regexp_seen + ) { + warn_at("too_long", line, 80); + } + column = 0; + line += 1; + regexp_seen = false; + source_line = lines[line]; + whole_line = source_line || ""; + if (source_line !== undefined) { + at = source_line.search(rx_tab); + if (at >= 0) { + if (!option.white) { + warn_at("use_spaces", line, at + 1); + } + source_line = source_line.replace(rx_tab, " "); + } + at = source_line.search(rx_unsafe); + if (at >= 0) { + warn_at( + "unsafe", + line, + column + at, + "U+" + source_line.charCodeAt(at).toString(16) + ); + } + if (!option.white && source_line.slice(-1) === " ") { + warn_at( + "unexpected_trailing_space", + line, + source_line.length - 1 + ); + } + } + return source_line; + } + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions snip, next_char, prev_char, +// some_digits, and escape help in the parsing of literals. + + function snip() { + +// Remove the last character from snippet. + + snippet = snippet.slice(0, -1); + } + + function next_char(match) { + +// Get the next character from the source line. Remove it from the source_line, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + return stop_at( + ( + char === "" + ? "expected_a" + : "expected_a_b" + ), + line, + column - 1, + match, + char + ); + } + if (source_line) { + char = source_line[0]; + source_line = source_line.slice(1); + snippet += char; + } else { + char = ""; + snippet += " "; + } + column += 1; + return char; + } + + function back_char() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the source_line. + + if (snippet) { + char = snippet.slice(-1); + source_line = char + source_line; + column -= 1; + snip(); + } else { + char = ""; + } + return char; + } + + function some_digits(rx, quiet) { + const result = source_line.match(rx); + if (result) { + char = result[1]; + column += char.length; + source_line = result[2]; + snippet += char; + } else { + char = ""; + if (!quiet) { + warn_at( + "expected_digits_after_a", + line, + column, + snippet + ); + } + } + return char.length; + } + + function escape(extra) { + next_char("\\"); + if (escapeable[char] === true) { + return next_char(); + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "u") { + if (next_char("u") === "{") { + if (json_mode) { + warn_at("unexpected_a", line, column - 1, char); + } + if (some_digits(rx_hexs) > 5) { + warn_at("too_many_digits", line, column - 1); + } + if (next_char() !== "}") { + stop_at("expected_a_before_b", line, column, "}", char); + } + return next_char(); + } + back_char(); + if (some_digits(rx_hexs, true) < 4) { + warn_at("expected_four_digits", line, column - 1); + } + return; + } + if (extra && extra.indexOf(char) >= 0) { + return next_char(); + } + warn_at("unexpected_a_before_b", line, column - 2, "\\", char); + } + + function make(id, value, identifier) { + +// Make the token object and append it to the tokens list. + + const the_token = { + from: from, + id: id, + identifier: Boolean(identifier), + line: line, + nr: nr, + thru: column + }; + tokens[nr] = the_token; + nr += 1; + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + directive_mode = false; + } + +// If the token is to have a value, give it one. + + if (value !== undefined) { + the_token.value = value; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option.white. + + if ( + previous.line === line + && previous.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (previous.id === "(comment)" || previous.id === "(regexp)") + ) { + warn( + "expected_space_a_b", + the_token, + artifact(previous), + artifact(the_token) + ); + } + if (previous.id === "." && id === "(number)") { + warn("expected_a_before_b", previous, "0", "."); + } + if (prior.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// The previous token is used to detect adjacency problems. + + previous = the_token; + +// The prior token is a previous token that was not a comment. The prior token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (previous.id !== "(comment)") { + prior = previous; + } + return the_token; + } + + function parse_directive(the_comment, body) { + +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + + const result = body.match(rx_directive_part); + if (result) { + let allowed; + const name = result[1]; + const value = result[2]; + if (the_comment.directive === "jslint") { + allowed = allowed_option[name]; + if ( + typeof allowed === "boolean" + || typeof allowed === "object" + ) { + if ( + value === "" + || value === "true" + || value === undefined + ) { + option[name] = true; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } else if (value === "false") { + option[name] = false; + } else { + warn("bad_option_a", the_comment, name + ":" + value); + } + } else { + warn("bad_option_a", the_comment, name); + } + } else if (the_comment.directive === "property") { + if (tenure === undefined) { + tenure = empty(); + } + tenure[name] = true; + } else if (the_comment.directive === "global") { + if (value) { + warn("bad_option_a", the_comment, name + ":" + value); + } + declared_globals[name] = false; + module_mode = the_comment; + } + return parse_directive(the_comment, result[3]); + } + if (body) { + return stop("bad_directive_a", the_comment, body); + } + } + + function comment(snippet) { + +// Make a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + + const the_comment = make("(comment)", snippet); + if (Array.isArray(snippet)) { + snippet = snippet.join(" "); + } + if (!option.devel && rx_todo.test(snippet)) { + warn("todo_comment", the_comment); + } + const result = snippet.match(rx_directive); + if (result) { + if (!directive_mode) { + warn_at("misplaced_directive_a", line, from, result[1]); + } else { + the_comment.directive = result[1]; + parse_directive(the_comment, result[2]); + } + directives.push(the_comment); + } + return the_comment; + } + + function regexp() { + +// Parse a regular expression literal. + + let multi_mode = false; + let result; + let value; + regexp_seen = true; + + function quantifier() { + +// Match an optional quantifier. + + if (char === "?" || char === "*" || char === "+") { + next_char(); + } else if (char === "{") { + if (some_digits(rx_digits, true) === 0) { + warn_at("expected_a", line, column, "0"); + } + if (next_char() === ",") { + some_digits(rx_digits, true); + next_char(); + } + next_char("}"); + } else { + return; + } + if (char === "?") { + next_char("?"); + } + } + + function subklass() { + +// Match a character in a character class. + + if (char === "\\") { + escape("BbDdSsWw-[]^"); + return true; + } + if ( + char === "" + || char === "[" + || char === "]" + || char === "/" + || char === "^" + || char === "-" + ) { + return false; + } + if (char === " ") { + warn_at("expected_a_b", line, column, "\\u0020", " "); + } else if (char === "`" && mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char(); + return true; + } + + function ranges() { + +// Match a range of subclasses. + + if (subklass()) { + if (char === "-") { + next_char("-"); + if (!subklass()) { + return stop_at( + "unexpected_a", + line, + column - 1, + "-" + ); + } + } + return ranges(); + } + } + + function klass() { + +// Match a class. + + next_char("["); + if (char === "^") { + next_char("^"); + } + (function classy() { + ranges(); + if (char !== "]" && char !== "") { + warn_at( + "expected_a_before_b", + line, + column, + "\\", + char + ); + next_char(); + return classy(); + } + }()); + next_char("]"); + } + + function choice() { + + function group() { + +// Match a group that starts with left paren. + + next_char("("); + if (char === "?") { + next_char("?"); + if (char === "=" || char === "!") { + next_char(); + } else { + next_char(":"); + } + } else if (char === ":") { + warn_at("expected_a_before_b", line, column, "?", ":"); + } + choice(); + next_char(")"); + } + + function factor() { + if ( + char === "" + || char === "/" + || char === "]" + || char === ")" + ) { + return false; + } + if (char === "(") { + group(); + return true; + } + if (char === "[") { + klass(); + return true; + } + if (char === "\\") { + escape("BbDdSsWw^${}[]():=!.-|*+?"); + return true; + } + if ( + char === "?" + || char === "+" + || char === "*" + || char === "}" + || char === "{" + ) { + warn_at( + "expected_a_before_b", + line, + column - 1, + "\\", + char + ); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column - 1, "`"); + } + } else if (char === " ") { + warn_at( + "expected_a_b", + line, + column - 1, + "\\s", + " " + ); + } else if (char === "$") { + if (source_line[0] !== "/") { + multi_mode = true; + } + } else if (char === "^") { + if (snippet !== "^") { + multi_mode = true; + } + } + next_char(); + return true; + } + + function sequence(follow) { + if (factor()) { + quantifier(); + return sequence(true); + } + if (!follow) { + warn_at("expected_regexp_factor_a", line, column, char); + } + } + +// Match a choice (a sequence that can be followed by | and another choice). + + sequence(); + if (char === "|") { + next_char("|"); + return choice(); + } + } + +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + next_char(); + if (char === "=") { + warn_at("expected_a_before_b", line, column, "\\", "="); + } + choice(); + +// Make sure there is a closing slash. + + snip(); + value = snippet; + next_char("/"); + +// Process dangling flag letters. + + const allowed = { + g: true, + i: true, + m: true, + u: true, + y: true + }; + const flag = empty(); + (function make_flag() { + if (is_letter(char)) { + if (allowed[char] !== true) { + warn_at("unexpected_a", line, column, char); + } + allowed[char] = false; + flag[char] = true; + next_char(); + return make_flag(); + } + }()); + back_char(); + if (char === "/" || char === "*") { + return stop_at("unexpected_a", line, from, char); + } + result = make("(regexp)", char); + result.flag = flag; + result.value = value; + if (multi_mode && !flag.m) { + warn_at("missing_m", line, column); + } + return result; + } + + function string(quote) { + +// Make a string token. + + let the_token; + snippet = ""; + next_char(); + + return (function next() { + if (char === quote) { + snip(); + the_token = make("(string)", snippet); + the_token.quote = quote; + return the_token; + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "\\") { + escape(quote); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char("`"); + } else { + next_char(); + } + return next(); + }()); + } + + function frack() { + if (char === ".") { + some_digits(rx_digits); + next_char(); + } + if (char === "E" || char === "e") { + next_char(); + if (char !== "+" && char !== "-") { + back_char(); + } + some_digits(rx_digits); + next_char(); + } + } + + function number() { + if (snippet === "0") { + next_char(); + if (char === ".") { + frack(); + } else if (char === "b") { + some_digits(rx_bits); + next_char(); + } else if (char === "o") { + some_digits(rx_octals); + next_char(); + } else if (char === "x") { + some_digits(rx_hexs); + next_char(); + } + } else { + next_char(); + frack(); + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + return stop_at( + "unexpected_a_after_b", + line, + column - 1, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + back_char(); + return make("(number)", snippet); + } + + function lex() { + let array; + let i = 0; + let j = 0; + let last; + let result; + let the_token; + // bug-workaround + // mitigate v8 RangeError("Maximum call stack size exceeded"), + // by avoiding unnecessary recursion + while (!source_line) { + source_line = next_line(); + from = 0; + if (source_line === undefined) { + return ( + mega_mode + ? stop_at("unclosed_mega", mega_line, mega_from) + : make("(end)") + ); + } + } + from = column; + result = source_line.match(rx_token); + +// result[1] token +// result[2] whitespace +// result[3] identifier +// result[4] number +// result[5] rest + + if (!result) { + return stop_at( + "unexpected_char_a", + line, + column, + source_line[0] + ); + } + + snippet = result[1]; + column += snippet.length; + source_line = result[5]; + +// Whitespace was matched. Call lex again to get more. + + if (result[2]) { + return lex(); + } + +// The token is an identifier. + + if (result[3]) { + return make(snippet, undefined, true); + } + +// The token is a number. + + if (result[4]) { + return number(snippet); + } + +// The token is a string. + + if (snippet === "\"") { + return string(snippet); + } + if (snippet === "'") { + if (!option.single) { + warn_at("use_double", line, column); + } + return string(snippet); + } + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (snippet === "`") { + if (mega_mode) { + return stop_at("expected_a_b", line, column, "}", "`"); + } + snippet = ""; + mega_from = from; + mega_line = line; + mega_mode = true; + +// Parsing a mega literal is tricky. First make a ` token. + + make("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + (function part() { + const at = source_line.search(rx_mega); + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + if (at < 0) { + snippet += source_line + "\n"; + return ( + next_line() === undefined + ? stop_at("unclosed_mega", mega_line, mega_from) + : part() + ); + } + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + snippet += source_line.slice(0, at); + column += at; + source_line = source_line.slice(at); + if (source_line[0] === "\\") { + stop_at("escape_mega", line, at); + } + make("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then make tokens that will become part of an expression until +// a } token is made. + + if (source_line[0] === "$") { + column += 2; + make("${"); + source_line = source_line.slice(2); + (function expr() { + const id = lex().id; + if (id === "{") { + return stop_at( + "expected_a_b", + line, + column, + "}", + "{" + ); + } + if (id !== "}") { + return expr(); + } + }()); + return part(); + } + }()); + source_line = source_line.slice(1); + column += 1; + mega_mode = false; + return make("`"); + } + +// The token is a // comment. + + if (snippet === "//") { + snippet = source_line; + source_line = ""; + the_token = comment(snippet); + if (mega_mode) { + warn("unexpected_comment", the_token, "`"); + } + return the_token; + } + +// The token is a /* comment. + + if (snippet === "/*") { + array = []; + if (source_line[0] === "/") { + warn_at("unexpected_a", line, column + i, "/"); + } + (function next() { + if (source_line > "") { + i = source_line.search(rx_star_slash); + if (i >= 0) { + return; + } + j = source_line.search(rx_slash_star); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + } + array.push(source_line); + source_line = next_line(); + if (source_line === undefined) { + return stop_at("unclosed_comment", line, column); + } + return next(); + }()); + snippet = source_line.slice(0, i); + j = snippet.search(rx_slash_star_or_slash); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + array.push(snippet); + column += i + 2; + source_line = source_line.slice(i + 2); + return comment(array); + } + +// The token is a slash. + + if (snippet === "/") { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + if (prior.identifier) { + if (!prior.dot) { + if (prior.id === "return") { + return regexp(); + } + if ( + prior.id === "(begin)" + || prior.id === "case" + || prior.id === "delete" + || prior.id === "in" + || prior.id === "instanceof" + || prior.id === "new" + || prior.id === "typeof" + || prior.id === "void" + || prior.id === "yield" + ) { + the_token = regexp(); + return stop("unexpected_a", the_token); + } + } + } else { + last = prior.id[prior.id.length - 1]; + if ("(,=:?[".indexOf(last) >= 0) { + return regexp(); + } + if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) { + the_token = regexp(); + warn("wrap_regexp", the_token); + return the_token; + } + } + if (source_line[0] === "/") { + column += 1; + source_line = source_line.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + } + return make(snippet); + } + + first = lex(); + json_mode = first.id === "{" || first.id === "["; + +// This is the only loop in JSLint. It will turn into a recursive call to lex +// when ES6 has been finished and widely deployed and adopted. + + while (true) { + if (lex().id === "(end)") { + break; + } + } +} + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + +function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!rx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!rx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property[id] === "number") { + property[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (tenure !== undefined) { + if (tenure[id] !== true) { + warn("unregistered_property_a", name); + } + } else { + if (name.identifier && rx_bad_property.test(id)) { + warn("bad_property_a", name); + } + } + property[id] = 1; + } + return id; +} + +function dispense() { + +// Deliver the next token, skipping the comments. + + const cadet = tokens[token_nr]; + token_nr += 1; + if (cadet.id === "(comment)") { + if (json_mode) { + warn("unexpected_a", cadet); + } + return dispense(); + } else { + return cadet; + } +} + +function lookahead() { + +// Look ahead one token without advancing. + + const old_token_nr = token_nr; + const cadet = dispense(true); + token_nr = old_token_nr; + return cadet; +} + +function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if (token.identifier && token.id !== "function") { + anon = token.id; + } else if (token.id === "(string)" && rx_identifier.test(token.value)) { + anon = token.value; + } + +// Attempt to match next_token with an expected id. + + if (id !== undefined && next_token.id !== id) { + return ( + match === undefined + ? stop("expected_a_b", next_token, id, artifact()) + : stop( + "expected_a_b_from_c_d", + next_token, + id, + artifact(match), + artifact_line(match), + artifact(next_token) + ) + ); + } + +// Promote the tokens, skipping comments. + + token = next_token; + next_token = dispense(); + if (next_token.id === "(end)") { + token_nr -= 1; + } +} + +// Parsing of JSON is simple: + +function json_value() { + let negative; + if (next_token.id === "{") { + return (function json_object() { + const brace = next_token; + const object = empty(); + const properties = []; + brace.expression = properties; + advance("{"); + if (next_token.id !== "}") { + (function next() { + let name; + let value; + if (next_token.quote !== "\"") { + warn( + "unexpected_a", + next_token, + next_token.quote + ); + } + name = next_token; + advance("(string)"); + if (object[token.value] !== undefined) { + warn("duplicate_a", token); + } else if (token.value === "__proto__") { + warn("bad_property_a", token); + } else { + object[token.value] = token; + } + advance(":"); + value = json_value(); + value.label = name; + properties.push(value); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("}", brace); + return brace; + }()); + } + if (next_token.id === "[") { + return (function json_array() { + const bracket = next_token; + const elements = []; + bracket.expression = elements; + advance("["); + if (next_token.id !== "]") { + (function next() { + elements.push(json_value()); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]", bracket); + return bracket; + }()); + } + if ( + next_token.id === "true" + || next_token.id === "false" + || next_token.id === "null" + ) { + advance(); + return token; + } + if (next_token.id === "(number)") { + if (!rx_JSON_number.test(next_token.value)) { + warn("unexpected_a"); + } + advance(); + return token; + } + if (next_token.id === "(string)") { + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + advance(); + return token; + } + if (next_token.id === "-") { + negative = next_token; + negative.arity = "unary"; + advance("-"); + advance("(number)"); + negative.expression = token; + return negative; + } + stop("unexpected_a"); +} + +// Now we parse JavaScript. + +function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + const id = name.id; + +// Reserved words may not be enrolled. + + if (syntax[id] !== undefined && id !== "ignore") { + warn("reserved_a", name); + } else { + +// Has the name been enrolled in this context? + + let earlier = functionage.context[id]; + if (earlier) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + +// Has the name been enrolled in an outer context? + + } else { + stack.forEach(function (value) { + const item = value.context[id]; + if (item !== undefined) { + earlier = item; + } + }); + if (earlier) { + if (id === "ignore") { + if (earlier.role === "variable") { + warn("unexpected_a", name); + } + } else { + if ( + ( + role !== "exception" + || earlier.role !== "exception" + ) + && role !== "parameter" + && role !== "function" + ) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + } + } + } + +// Enroll it. + + functionage.context[id] = name; + name.dead = true; + name.parent = functionage; + name.init = false; + name.role = role; + name.used = 0; + name.writable = !readonly; + } + } +} + +function expression(rbp, initial) { + +// This is the heart of the Pratt parser. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// nud Null denotation +// led Left denotation +// lbp Left binding power +// rbp Right binding power + +// It processes a nud (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop. It +// returns the expression's parse tree. + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement, + + if (!initial) { + advance(); + } + the_symbol = syntax[token.id]; + if (the_symbol !== undefined && the_symbol.nud !== undefined) { + left = the_symbol.nud(); + } else if (token.identifier) { + left = token; + left.arity = "variable"; + } else { + return stop("unexpected_a", token); + } + (function right() { + the_symbol = syntax[next_token.id]; + if ( + the_symbol !== undefined + && the_symbol.led !== undefined + && rbp < the_symbol.lbp + ) { + advance(); + left = the_symbol.led(left); + return right(); + } + }()); + return left; +} + +function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = next_token; + let the_value; + the_paren.free = true; + advance("("); + the_value = expression(0); + advance(")"); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (anticondition[the_value.id] === true) { + warn("unexpected_a", the_value); + } + return the_value; +} + +function is_weird(thing) { + return ( + thing.id === "(regexp)" + || thing.id === "{" + || thing.id === "=>" + || thing.id === "function" + || (thing.id === "[" && thing.arity === "unary") + ); +} + +function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + return ( + Array.isArray(b) + && a.length === b.length + && a.every(function (value, index) { + return are_similar(value, b[index]); + }) + ); + } + if (Array.isArray(b)) { + return false; + } + if (a.id === "(number)" && b.id === "(number)") { + return a.value === b.value; + } + let a_string; + let b_string; + if (a.id === "(string)") { + a_string = a.value; + } else if (a.id === "`" && a.constant) { + a_string = a.value[0]; + } + if (b.id === "(string)") { + b_string = b.value; + } else if (b.id === "`" && b.constant) { + b_string = b.value[0]; + } + if (typeof a_string === "string") { + return a_string === b_string; + } + if (is_weird(a) || is_weird(b)) { + return false; + } + if (a.arity === b.arity && a.id === b.id) { + if (a.id === ".") { + return ( + are_similar(a.expression, b.expression) + && are_similar(a.name, b.name) + ); + } + if (a.arity === "unary") { + return are_similar(a.expression, b.expression); + } + if (a.arity === "binary") { + return ( + a.id !== "(" + && are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + ); + } + if (a.arity === "ternary") { + return ( + are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + && are_similar(a.expression[2], b.expression[2]) + ); + } + if (a.arity === "function" && a.arity === "regexp") { + return false; + } + return true; + } + return false; +} + +function semicolon() { + +// Try to match a semicolon. + + if (next_token.id === ";") { + advance(";"); + } else { + warn_at( + "expected_a_b", + token.line, + token.thru, + ";", + artifact(next_token) + ); + } + anon = "anonymous"; +} + +function statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token.identifier && next_token.id === ":") { + the_label = token; + if (the_label.id === "ignore") { + warn("unexpected_a", the_label); + } + advance(":"); + if ( + next_token.id === "do" + || next_token.id === "for" + || next_token.id === "switch" + || next_token.id === "while" + ) { + enroll(the_label, "label", true); + the_label.init = true; + the_label.dead = false; + the_statement = statement(); + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token; + first.statement = true; + the_symbol = syntax[first.id]; + if (the_symbol !== undefined && the_symbol.fud !== undefined) { + the_symbol.disrupt = false; + the_symbol.statement = true; + the_statement = the_symbol.fud(); + } else { + +// It is an expression statement. + + the_statement = expression(0, true); + if (the_statement.wrapped && the_statement.id !== "(") { + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; +} + +function statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const array = []; + (function next(disrupt) { + if ( + next_token.id !== "}" + && next_token.id !== "case" + && next_token.id !== "default" + && next_token.id !== "else" + && next_token.id !== "(end)" + ) { + let a_statement = statement(); + array.push(a_statement); + if (disrupt) { + warn("unreachable_a", a_statement); + } + return next(a_statement.disrupt); + } + }(false)); + return array; +} + +function not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === global) { + warn("unexpected_at_top_level_a", thing); + } +} + +function top_level_only(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== global) { + warn("misplaced_a", the_thing); + } +} + +function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token; + the_block.arity = "statement"; + the_block.body = special === "body"; + +// All top level function bodies should include the "use strict" pragma unless +// the whole file is strict or the file is a module or the function parameters +// use es6 syntax. + + if ( + special === "body" + && stack.length === 1 + && next_token.value === "use strict" + ) { + the_block.strict = next_token; + next_token.statement = true; + advance("(string)"); + advance(";"); + } + stmts = statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option.devel && special !== "ignore") { + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; +} + +function mutation_check(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + warn("bad_assignment_a", the_thing); + return false; + } + return true; +} + +function left_check(left, right) { + +// Warn if the left is not one of these: +// e.b +// e[b] +// e() +// ?: +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !left_check(left.expression[1]) + && !left_check(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right); + return false; + } + return true; +} + +// These functions are used to specify the grammar of our language: + +function symbol(id, bp) { + +// Make a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax[id] = the_symbol; + } + return the_symbol; +} + +function assignment(id) { + +// Make an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led = function (left) { + const the_token = token; + let right; + the_token.arity = "assignment"; + right = expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "pre" + || right.arity === "post" + ) { + warn("unexpected_a", right); + } + mutation_check(left); + return the_token; + }; + return the_symbol; +} + +function constant(id, type, value) { + +// Make a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud = ( + typeof value === "function" + ? value + : function () { + token.constant = true; + if (value !== undefined) { + token.value = value; + } + return token; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; +} + +function infix(id, bp, f) { + +// Make an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, expression(bp)]; + return the_token; + }; + return the_symbol; +} + +function infixr(id, bp) { + +// Make a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + the_token.expression = [left, expression(bp - 1)]; + return the_token; + }; + return the_symbol; +} + +function post(id) { + +// Make one of the post operators. + + const the_symbol = symbol(id, 150); + the_symbol.led = function (left) { + token.expression = left; + token.arity = "post"; + mutation_check(token.expression); + return token; + }; + return the_symbol; +} + +function pre(id) { + +// Make one of the pre operators. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "pre"; + the_token.expression = expression(150); + mutation_check(the_token.expression); + return the_token; + }; + return the_symbol; +} + +function prefix(id, f) { + +// Make a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = expression(150); + return the_token; + }; + return the_symbol; +} + +function stmt(id, f) { + +// Make a statement. + + const the_symbol = symbol(id); + the_symbol.fud = function () { + token.arity = "statement"; + return f(); + }; + return the_symbol; +} + +function ternary(id1, id2) { + +// Make a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led = function (left) { + const the_token = token; + const second = expression(20); + advance(id2); + token.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, expression(10)]; + return the_token; + }; + return the_symbol; +} + +// Begin defining the language. + +syntax = empty(); + +symbol("}"); +symbol(")"); +symbol("]"); +symbol(","); +symbol(";"); +symbol(":"); +symbol("*/"); +symbol("await"); +symbol("case"); +symbol("catch"); +symbol("class"); +symbol("default"); +symbol("else"); +symbol("enum"); +symbol("finally"); +symbol("implements"); +symbol("interface"); +symbol("package"); +symbol("private"); +symbol("protected"); +symbol("public"); +symbol("static"); +symbol("super"); +symbol("void"); +symbol("yield"); + +constant("(number)", "number"); +constant("(regexp)", "regexp"); +constant("(string)", "string"); +constant("arguments", "object", function () { + warn("unexpected_a", token); + return token; +}); +constant("eval", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("false", "boolean", false); +constant("Function", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("ignore", "undefined", function () { + warn("unexpected_a", token); + return token; +}); +constant("Infinity", "number", Infinity); +constant("isFinite", "function", function () { + warn("expected_a_b", token, "Number.isFinite", "isFinite"); + return token; +}); +constant("isNaN", "function", function () { + warn("number_isNaN", token); + return token; +}); +constant("NaN", "number", NaN); +constant("null", "null", null); +constant("this", "object", function () { + if (!option.this) { + warn("unexpected_a", token); + } + return token; +}); +constant("true", "boolean", true); +constant("undefined", "undefined"); + +assignment("="); +assignment("+="); +assignment("-="); +assignment("*="); +assignment("/="); +assignment("%="); +assignment("&="); +assignment("|="); +assignment("^="); +assignment("<<="); +assignment(">>="); +assignment(">>>="); + +infix("||", 40); +infix("&&", 50); +infix("|", 70); +infix("^", 80); +infix("&", 90); +infix("==", 100); +infix("===", 100); +infix("!=", 100); +infix("!==", 100); +infix("<", 110); +infix(">", 110); +infix("<=", 110); +infix(">=", 110); +infix("in", 110); +infix("instanceof", 110); +infix("<<", 120); +infix(">>", 120); +infix(">>>", 120); +infix("+", 130); +infix("-", 130); +infix("*", 140); +infix("/", 140); +infix("%", 140); +infixr("**", 150); +infix("(", 160, function (left) { + const the_paren = token; + let the_argument; + if (left.id !== "function") { + left_check(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (next_token.id !== ")") { + (function next() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + the_paren.free = true; + if (the_argument.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + the_paren.free = false; + } + return the_paren; +}); +infix(".", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("?.", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("[", 170, function (left) { + const the_token = token; + const the_subscript = expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + const name = survey(the_subscript); + if (rx_identifier.test(name)) { + warn("subscript_a", the_subscript, name); + } + } + left_check(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; +}); +infix("=>", 170, function (left) { + return stop("wrap_parameter", left); +}); + +function do_tick() { + const the_tick = token; + the_tick.value = []; + the_tick.expression = []; + if (next_token.id !== "`") { + (function part() { + advance("(string)"); + the_tick.value.push(token); + if (next_token.id === "${") { + advance("${"); + the_tick.expression.push(expression(0)); + advance("}"); + return part(); + } + }()); + } + advance("`"); + return the_tick; +} + +infix("`", 160, function (left) { + const the_tick = do_tick(); + left_check(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; +}); + +post("++"); +post("--"); +pre("++"); +pre("--"); + +prefix("+"); +prefix("-"); +prefix("~"); +prefix("!"); +prefix("!!"); +prefix("[", function () { + const the_token = token; + the_token.expression = []; + if (next_token.id !== "]") { + (function next() { + let element; + let ellipsis = false; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + element = expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]"); + return the_token; +}); +prefix("/=", function () { + stop("expected_a_b", token, "/\\=", "/="); +}); +prefix("=>", function () { + return stop("expected_a_before_b", token, "()", "=>"); +}); +prefix("new", function () { + const the_new = token; + const right = expression(160); + if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "()", artifact(next_token)); + } + the_new.expression = right; + return the_new; +}); +prefix("typeof"); +prefix("void", function () { + const the_void = token; + warn("unexpected_a", the_void); + the_void.expression = expression(0); + return the_void; +}); + +function parameter_list() { + let complex = false; + const list = []; + let optional; + const signature = ["("]; + if (next_token.id !== ")" && next_token.id !== "(end)") { + (function parameter() { + let ellipsis = false; + let param; + if (next_token.id === "{") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("{"); + signature.push("{"); + (function subparameter() { + let subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (next_token.id === ":") { + advance(":"); + advance(); + token.label = subparam; + subparam = token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + } + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return subparameter(); + } + }()); + list.push(param); + advance("}"); + signature.push("}"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else if (next_token.id === "[") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("["); + signature.push("[]"); + (function subparameter() { + const subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + return subparameter(); + } + }()); + list.push(param); + advance("]"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else { + if (next_token.id === "...") { + complex = true; + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + } + if (!next_token.identifier) { + return stop("expected_identifier_a"); + } + param = next_token; + list.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (next_token.id === "=") { + complex = true; + optional = param; + advance("="); + param.expression = expression(0); + } else { + if (optional !== undefined) { + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } + } + }()); + } + advance(")"); + signature.push(")"); + return [list, signature.join(""), complex]; +} + +function do_function(the_function) { + let name; + if (the_function === undefined) { + the_function = token; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + name = next_token; + enroll(name, "variable", true); + the_function.name = name; + name.init = true; + name.calls = empty(); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + if (next_token.identifier) { + name = next_token; + the_function.name = name; + advance(); + } else { + the_function.name = anon; + } + } + } else { + name = the_function.name; + } + the_function.level = functionage.level + 1; + if (mega_mode) { + warn("unexpected_a", the_function); + } + +// Don't make functions in loops. It is inefficient, and it can lead to scoping +// errors. + + if (functionage.loop > 0) { + warn("function_in_loop", the_function); + } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + the_function.context = empty(); + the_function.finally = 0; + the_function.loop = 0; + the_function.switch = 0; + the_function.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functions.push(the_function); + functionage = the_function; + if (the_function.arity !== "statement" && typeof name === "object") { + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// Parse the parameter list. + + advance("("); + token.free = false; + token.arity = "function"; + const pl = parameter_list(); + functionage.parameters = pl[0]; + functionage.signature = pl[1]; + functionage.complex = pl[2]; + functionage.parameters.forEach(function enroll_parameter(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + name.names.forEach(enroll_parameter); + } + }); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && next_token.line === token.line + ) { + return stop("unexpected_a", next_token); + } + if ( + next_token.id === "." + || next_token.id === "?." + || next_token.id === "[" + ) { + warn("unexpected_a"); + } + +// Restore the previous context. + + functionage = stack.pop(); + return the_function; +} + +prefix("function", do_function); + +function fart(pl) { + advance("=>"); + const the_fart = token; + the_fart.arity = "binary"; + the_fart.name = "=>"; + the_fart.level = functionage.level + 1; + functions.push(the_fart); + if (functionage.loop > 0) { + warn("function_in_loop", the_fart); + } + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + the_fart.context = empty(); + the_fart.finally = 0; + the_fart.loop = 0; + the_fart.switch = 0; + the_fart.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functionage = the_fart; + the_fart.parameters = pl[0]; + the_fart.signature = pl[1]; + the_fart.complex = true; + the_fart.parameters.forEach(function (name) { + enroll(name, "parameter", true); + }); + if (next_token.id === "{") { + warn("expected_a_b", the_fart, "function", "=>"); + the_fart.block = block("body"); + } else { + the_fart.expression = expression(0); + } + functionage = stack.pop(); + return the_fart; +} + +prefix("(", function () { + const the_paren = token; + let the_value; + const cadet = lookahead().id; + +// We can distinguish between a parameter list for => and a wrapped expression +// with one token of lookahead. + + if ( + next_token.id === ")" + || next_token.id === "..." + || (next_token.identifier && (cadet === "," || cadet === "=")) + ) { + the_paren.free = false; + return fart(parameter_list()); + } + the_paren.free = true; + the_value = expression(0); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + if (next_token.id === "=>") { + if (the_value.arity !== "variable") { + if (the_value.id === "{" || the_value.id === "[") { + warn("expected_a_before_b", the_paren, "function", "("); + return stop("expected_a_b", next_token, "{", "=>"); + } + return stop("expected_identifier_a", the_value); + } + the_paren.expression = [the_value]; + return fart([the_paren.expression, "(" + the_value.id + ")"]); + } + return the_value; +}); +prefix("`", do_tick); +prefix("{", function () { + const the_brace = token; + const seen = empty(); + the_brace.expression = []; + if (next_token.id !== "}") { + (function member() { + let extra; + let full; + let id; + let name = next_token; + let value; + advance(); + if ( + (name.id === "get" || name.id === "set") + && next_token.identifier + ) { + if (!option.getset) { + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + next_token.id; + name = next_token; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (next_token.id === "}" || next_token.id === ",") { + if (typeof extra === "string") { + advance("("); + } + value = expression(Infinity, true); + } else if (next_token.id === "(") { + value = do_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + advance("("); + } + advance(":"); + value = expression(0); + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + advance(":"); + value = expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (next_token.id === ",") { + advance(","); + return member(); + } + }()); + } + advance("}"); + return the_brace; +}); + +stmt(";", function () { + warn("unexpected_a", token); + return token; +}); +stmt("{", function () { + warn("naked_block", token); + return block("naked"); +}); +stmt("break", function () { + const the_break = token; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (next_token.identifier && token.line === next_token.line) { + the_label = functionage.context[next_token.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + warn( + (the_label !== undefined && the_label.dead) + ? "out_of_scope_a" + : "not_label_a" + ); + } else { + the_label.used += 1; + } + the_break.label = next_token; + advance(); + } + advance(";"); + return the_break; +}); + +function do_var() { + const the_statement = token; + const is_const = the_statement.id === "const"; + the_statement.names = []; + +// A program may use var or let, but not both. + + if (!is_const) { + if (var_mode === undefined) { + var_mode = the_statement.id; + } else if (the_statement.id !== var_mode) { + warn( + "expected_a_b", + the_statement, + var_mode, + the_statement.id + ); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + warn("var_switch", the_statement); + } + if (functionage.loop > 0 && the_statement.id === "var") { + warn("var_loop", the_statement); + } + (function next() { + if (next_token.id === "{" && the_statement.id !== "var") { + const the_brace = next_token; + the_brace.names = []; + advance("{"); + (function pair() { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + survey(name); + advance(); + if (next_token.id === ":") { + advance(":"); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + next_token.label = name; + the_brace.names.push(next_token); + enroll(next_token, "variable", is_const); + advance(); + } else { + the_brace.names.push(name); + enroll(name, "variable", is_const); + } + if (next_token.id === ",") { + advance(","); + return pair(); + } + }()); + advance("}"); + advance("="); + the_brace.expression = expression(0); + the_statement.names.push(the_brace); + } else if (next_token.id === "[" && the_statement.id !== "var") { + const the_bracket = next_token; + the_bracket.names = []; + advance("["); + (function element() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + advance(); + the_bracket.names.push(name); + enroll(name, "variable", the_statement.id === "const"); + if (ellipsis) { + name.ellipsis = true; + } else if (next_token.id === ",") { + advance(","); + return element(); + } + }()); + advance("]"); + advance("="); + the_bracket.expression = expression(0); + the_statement.names.push(the_bracket); + } else if (next_token.identifier) { + const name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", is_const); + if (next_token.id === "=" || is_const) { + advance("="); + name.init = true; + name.expression = expression(0); + } + the_statement.names.push(name); + } else { + return stop("expected_identifier_a", next_token); + } + if (next_token.id === ",") { + if (!option.multivar) { + warn("expected_a_b", next_token, ";", ","); + } + advance(","); + return next(); + } + }()); + the_statement.open = ( + the_statement.names.length > 1 + && the_statement.line !== the_statement.names[1].line + ); + semicolon(); + return the_statement; +} + +stmt("const", do_var); +stmt("continue", function () { + const the_continue = token; + if (functionage.loop < 1 || functionage.finally > 0) { + warn("unexpected_a", the_continue); + } + not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; +}); +stmt("debugger", function () { + const the_debug = token; + if (!option.devel) { + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; +}); +stmt("delete", function () { + const the_token = token; + const the_value = expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; +}); +stmt("do", function () { + const the_do = token; + not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; +}); +stmt("export", function () { + const the_export = token; + let the_id; + let the_name; + let the_thing; + + function export_id() { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + the_id = next_token.id; + the_name = global.context[the_id]; + if (the_name === undefined) { + warn("unexpected_a"); + } else { + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a"); + } + exports[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + } + + the_export.expression = []; + if (next_token.id === "default") { + if (exports.default !== undefined) { + warn("duplicate_a"); + } + advance("default"); + the_thing = expression(0); + if ( + the_thing.id !== "(" + || the_thing.expression[0].id !== "." + || the_thing.expression[0].expression.id !== "Object" + || the_thing.expression[0].name.id !== "freeze" + ) { + warn("freeze_exports", the_thing); + } + if (next_token.id === ";") { + semicolon(); + } + exports.default = the_thing; + the_export.expression.push(the_thing); + } else { + if (next_token.id === "function") { + warn("freeze_exports"); + the_thing = statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a", the_name); + } + exports[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + warn("unexpected_a", next_token); + statement(); + } else if (next_token.id === "{") { + advance("{"); + (function loop() { + export_id(); + if (next_token.id === ",") { + advance(","); + return loop(); + } + }()); + advance("}"); + semicolon(); + } else { + stop("unexpected_a"); + } + } + module_mode = true; + return the_export; +}); +stmt("for", function () { + let first; + const the_for = token; + if (!option.for) { + warn("unexpected_a", the_for); + } + not_top_level(the_for); + functionage.loop += 1; + advance("("); + token.free = true; + if (next_token.id === ";") { + return stop("expected_a_b", the_for, "while (", "for (;"); + } + if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + return stop("unexpected_a"); + } + first = expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = expression(0); + advance(";"); + the_for.inc = expression(0); + if (the_for.inc.id === "++") { + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; +}); +stmt("function", do_function); +stmt("if", function () { + let the_else; + const the_if = token; + the_if.expression = condition(); + the_if.block = block(); + if (next_token.id === "else") { + advance("else"); + the_else = token; + the_if.else = ( + next_token.id === "if" + ? statement() + : block() + ); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + the_if.disrupt = true; + } else { + warn("unexpected_a", the_else); + } + } + } + return the_if; +}); +stmt("import", function () { + const the_import = token; + let name; + if (typeof module_mode === "object") { + warn("unexpected_directive_a", module_mode, module_mode.directive); + } + module_mode = true; + if (next_token.identifier) { + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + const names = []; + advance("{"); + if (next_token.id !== "}") { + while (true) { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (next_token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token; + if (!rx_module.test(token.value)) { + warn("bad_module_name_a", token); + } + froms.push(token.value); + semicolon(); + return the_import; +}); +stmt("let", do_var); +stmt("return", function () { + const the_return = token; + not_top_level(the_return); + if (functionage.finally > 0) { + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (next_token.id !== ";" && the_return.line === next_token.line) { + the_return.expression = expression(10); + } + advance(";"); + return the_return; +}); +stmt("switch", function () { + let dups = []; + let last; + let stmts; + const the_cases = []; + let the_disrupt = true; + const the_switch = token; + not_top_level(the_switch); + if (functionage.finally > 0) { + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token.free = true; + the_switch.expression = expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + (function major() { + const the_case = next_token; + the_case.arity = "statement"; + the_case.expression = []; + (function minor() { + advance("case"); + token.switch = true; + const exp = expression(0); + if (dups.some(function (thing) { + return are_similar(thing, exp); + })) { + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (next_token.id === "case") { + return minor(); + } + }()); + stmts = statements(); + if (stmts.length < 1) { + warn("expected_statements_a"); + return; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "break;", + artifact(next_token) + ); + } + if (next_token.id === "case") { + return major(); + } + }()); + dups = undefined; + if (next_token.id === "default") { + const the_default = next_token; + advance("default"); + token.switch = true; + advance(":"); + the_switch.else = statements(); + if (the_switch.else.length < 1) { + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + const the_last = the_switch.else[the_switch.else.length - 1]; + if (the_last.id === "break" && the_last.label === undefined) { + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; +}); +stmt("throw", function () { + const the_throw = token; + the_throw.disrupt = true; + the_throw.expression = expression(10); + semicolon(); + if (functionage.try > 0) { + warn("unexpected_a", the_throw); + } + return the_throw; +}); +stmt("try", function () { + let the_catch; + let the_disrupt; + const the_try = token; + if (functionage.try > 0) { + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (next_token.id === "catch") { + let ignored = "ignore"; + the_catch = next_token; + the_try.catch = the_catch; + advance("catch"); + if (next_token.id === "(") { + advance("("); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + if (next_token.id !== "ignore") { + ignored = undefined; + the_catch.name = next_token; + enroll(next_token, "exception", true); + } + advance(); + advance(")"); + } + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "catch", + artifact(next_token) + ); + + } + if (next_token.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; +}); +stmt("var", do_var); +stmt("while", function () { + const the_while = token; + not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; +}); +stmt("with", function () { + stop("unexpected_a", token); +}); + +ternary("?", ":"); + +// Ambulation of the parse tree. + +function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; +} + +function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all of the +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + } + }; +} + +const posts = empty(); +const pres = empty(); +const preaction = action(pres); +const postaction = action(posts); +const preamble = amble(pres); +const postamble = amble(posts); + +function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.id === "function") { + walk_statement(thing.block); + } + if (thing.arity === "pre" || thing.arity === "post") { + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + warn("unexpected_statement_a", thing); + } + postamble(thing); + } + } +} + +function walk_statement(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_statement); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + ) { + warn( + (thing.id === "(string)" && thing.value === "use strict") + ? "unexpected_a" + : "unexpected_expression_a", + thing + ); + } + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + } +} + +function lookup(thing) { + if (thing.arity === "variable") { + +// Look up the variable in the current context. + + let the_variable = functionage.context[thing.id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable === undefined) { + stack.forEach(function (outer) { + const a_variable = outer.context[thing.id]; + if ( + a_variable !== undefined + && a_variable.role !== "label" + ) { + the_variable = a_variable; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (the_variable === undefined) { + if (declared_globals[thing.id] === undefined) { + warn("undeclared_a", thing); + return; + } + the_variable = { + dead: false, + parent: global, + id: thing.id, + init: true, + role: "variable", + used: 0, + writable: false + }; + global.context[thing.id] = the_variable; + } + the_variable.closure = true; + functionage.context[thing.id] = the_variable; + } else if (the_variable.role === "label") { + warn("label_a", thing); + } + if ( + the_variable.dead + && ( + the_variable.calls === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + ) { + warn("out_of_scope_a", thing); + } + return the_variable; + } +} + +function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); +} + +function preaction_function(thing) { + if (thing.arity === "statement" && blockage.body !== true) { + warn("unexpected_a", thing); + } + if (thing.level === 1) { + if ( + module_mode === true + || global.strict !== undefined + || thing.complex + ) { + if (thing.id !== "=>" && thing.block.strict !== undefined) { + warn("unexpected_a", thing.block.strict); + } + } else { + if (thing.block.strict === undefined) { + warn("use_strict", thing); + } + } + } + stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); +} + +function bitwise_check(thing) { + if (!option.bitwise && bitwiseop[thing.id] === true) { + warn("unexpected_a", thing); + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + warn("unexpected_a", thing); + } +} + +function pop_block() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); +} + +function activate(name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.init = true; + } + } + blockage.live.push(name); +} + +function action_var(thing) { + thing.names.forEach(activate); +} + +preaction("assignment", bitwise_check); +preaction("binary", bitwise_check); +preaction("binary", function (thing) { + if (relationop[thing.id] === true) { + const left = thing.expression[0]; + const right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + warn("expected_string_a", right); + } + } else { + const value = right.value; + if (value === "null" || value === "undefined") { + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + warn("expected_type_string_a", right, value); + } + } + } + } +}); +preaction("binary", "==", function (thing) { + warn("expected_a_b", thing, "===", "=="); +}); +preaction("binary", "!=", function (thing) { + warn("expected_a_b", thing, "!==", "!="); +}); +preaction("binary", "=>", preaction_function); +preaction("binary", "||", function (thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + warn("and", thang); + } + }); +}); +preaction("binary", "(", function (thing) { + const left = thing.expression[0]; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + const parent = functionage.name.parent; + if (parent) { + const left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + && left_variable.dead + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } +}); +preaction("binary", "in", function (thing) { + warn("infix_in", thing); +}); +preaction("binary", "instanceof", function (thing) { + warn("unexpected_a", thing); +}); +preaction("binary", ".", function (thing) { + if (thing.expression.new) { + thing.new = true; + } +}); +preaction("statement", "{", function (thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; +}); +preaction("statement", "for", function (thing) { + if (thing.name !== undefined) { + const the_variable = lookup(thing.name); + if (the_variable !== undefined) { + the_variable.init = true; + if (!the_variable.writable) { + warn("bad_assignment_a", thing.name); + } + } + } + walk_statement(thing.initial); +}); +preaction("statement", "function", preaction_function); +preaction("unary", "~", bitwise_check); +preaction("unary", "function", preaction_function); +preaction("variable", function (thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } +}); + +function init_variable(name) { + const the_variable = lookup(name); + if (the_variable !== undefined) { + if (the_variable.writable) { + the_variable.init = true; + return; + } + } + warn("bad_assignment_a", name); +} + +postaction("assignment", "+=", function (thing) { + let right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } +}); +postaction("assignment", function (thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + if (thing.id === "=") { + if (thing.names !== undefined) { + if (Array.isArray(thing.names)) { + thing.names.forEach(init_variable); + } else { + init_variable(thing.names); + } + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.writable !== true) { + warn("bad_assignment_a", lvalue); + } + } + const right = syntax[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + warn("unexpected_a", thing.expression[1]); + } + } +}); + +function postaction_function(thing) { + delete functionage.finally; + delete functionage.loop; + delete functionage.switch; + delete functionage.try; + functionage = stack.pop(); + if (thing.wrapped) { + warn("unexpected_parens", thing); + } + return pop_block(); +} + +postaction("binary", function (thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || are_similar(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option.convert) { + if (thing.expression[0].value === "") { + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === "." || thing.id === "?.") { + if (thing.expression.id === "RegExp") { + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } +}); +postaction("binary", "&&", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "||", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "=>", postaction_function); +postaction("binary", "(", function (thing) { + let left = thing.expression[0]; + let the_new; + let arg; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option.eval) { + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + warn( + "expected_a_before_b", + left, + "new", + artifact(left) + ); + } + } + } else if (left.id === ".") { + let cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + cack = !cack; + } + if (rx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + warn("unexpected_a", the_new); + } else { + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + const paren = left.expression; + if (paren.id === "(") { + const array = paren.expression; + if (array.length === 1) { + const new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } +}); +postaction("binary", "[", function (thing) { + if (thing.expression[0].id === "RegExp") { + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + warn("weird_expression_a", thing.expression[1]); + } +}); +postaction("statement", "{", pop_block); +postaction("statement", "const", action_var); +postaction("statement", "export", top_level_only); +postaction("statement", "for", function (thing) { + walk_statement(thing.inc); +}); +postaction("statement", "function", postaction_function); +postaction("statement", "import", function (the_thing) { + const name = the_thing.name; + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return top_level_only(the_thing); +}); +postaction("statement", "let", action_var); +postaction("statement", "try", function (thing) { + if (thing.catch !== undefined) { + const the_name = thing.catch.name; + if (the_name !== undefined) { + const the_variable = functionage.context[the_name.id]; + the_variable.dead = false; + the_variable.init = true; + } + walk_statement(thing.catch.block); + } +}); +postaction("statement", "var", action_var); +postaction("ternary", function (thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || are_similar(thing.expression[1], thing.expression[2]) + ) { + warn("unexpected_a", thing); + } else if (are_similar(thing.expression[0], thing.expression[1])) { + warn("expected_a_b", thing, "||", "?"); + } else if (are_similar(thing.expression[0], thing.expression[2])) { + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + warn("wrap_condition", thing.expression[0]); + } +}); +postaction("unary", function (thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option.convert) { + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } +}); +postaction("unary", "function", postaction_function); +postaction("unary", "+", function (thing) { + if (!option.convert) { + warn("expected_a_b", thing, "Number(...)", "+"); + } + const right = thing.expression; + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } +}); + +function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + if (id !== "ignore") { + const name = the_function.context[id]; + if (name.parent === the_function) { + if ( + name.used === 0 + && ( + name.role !== "function" + || name.parent.arity !== "unary" + ) + ) { + warn("unused_a", name); + } else if (!name.init) { + warn("uninitialized_a", name); + } + } + } + }); +} + +function uninitialized_and_unused() { + +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (module_mode === true || option.node) { + delve(global); + } + functions.forEach(delve); +} + +// Go through the token list, looking at usage of whitespace. + +function whitage() { + let closer = "(end)"; + let free = false; + let left = global; + let margin = 0; + let nr_comments_skipped = 0; + let open = true; + let opening = true; + let right; + + function pop() { + const previous = stack.pop(); + closer = previous.closer; + free = previous.free; + margin = previous.margin; + open = previous.open; + opening = previous.opening; + } + + function push() { + stack.push({ + closer, + free, + margin, + open, + opening + }); + } + + function expected_at(at) { + warn( + "expected_a_at_b_c", + right, + artifact(right), + fudge + at, + artifact_column(right) + ); + } + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function no_space() { + if (left.line === right.line) { + if (left.thru !== right.from && nr_comments_skipped === 0) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (open) { + const at = ( + free + ? margin + : margin + 8 + ); + if (right.from < at) { + expected_at(at); + } + } else { + if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (free) { + if (right.from < margin) { + expected_at(margin); + } + } else { + const mislaid = ( + stack.length > 0 + ? stack[stack.length - 1].right + : undefined + ); + if (!open && mislaid !== undefined) { + warn( + "expected_a_next_at_b", + mislaid, + artifact(mislaid.id), + margin + 4 + fudge + ); + } else if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + stack = []; + tokens.forEach(function (the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + + const new_closer = opener[left.id]; + if (typeof new_closer === "string") { + if (new_closer !== right.id) { + opening = left.line !== right.line; + push(); + closer = new_closer; + if (opening) { + free = closer === ")" && left.free; + open = true; + margin += 4; + if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (right.switch) { + at_margin(-4); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + free = false; + open = false; + no_space_only(); + } + } else { + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like '{]') have already been detected. + + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } + } else { + if (right.statement === true) { + if (left.id === "else") { + one_space_only(); + } else { + at_margin(0); + open = false; + } + +// If right is a closer, then pop the previous state. + + } else if (right.id === closer) { + pop(); + if (opening && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-4); + } else if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + one_space(); + } else { + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + at_margin(0); + } else { + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + no_space(); + } else if ( + left.id === "." + || left.id === "?." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + ) { + no_space_only(); + } else if (right.id === "." || right.id === "?.") { + no_space_only(); + } else if (left.id === ";") { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + one_space_only(); + } else if ( + left.id === "var" + || left.id === "const" + || left.id === "let" + ) { + push(); + closer = ";"; + free = false; + open = left.open; + if (open) { + margin = margin + 4; + at_margin(0); + } else { + one_space_only(); + } + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +// The jslint function itself. + +export default Object.freeze(function jslint( + source = "", + option_object = empty(), + global_array = [] +) { + try { + warnings = []; + option = Object.assign(empty(), option_object); + anon = "anonymous"; + block_stack = []; + declared_globals = empty(); + directive_mode = true; + directives = []; + early_stop = true; + exports = empty(); + froms = []; + fudge = ( + option.fudge + ? 1 + : 0 + ); + functions = []; + global = { + id: "(global)", + body: true, + context: empty(), + from: 0, + level: 0, + line: 0, + live: [], + loop: 0, + switch: 0, + thru: 0 + }; + blockage = global; + functionage = global; + json_mode = false; + mega_mode = false; + module_mode = false; + next_token = global; + property = empty(); + shebang = false; + stack = []; + tenure = undefined; + token = global; + token_nr = 0; + var_mode = undefined; + populate(standard, declared_globals, false); + populate(global_array, declared_globals, false); + Object.keys(option).forEach(function (name) { + if (option[name] === true) { + const allowed = allowed_option[name]; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } + }); + tokenize(source); + advance(); + if (json_mode) { + tree = json_value(); + advance("(end)"); + } else { + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option.browser) { + if (next_token.id === ";") { + advance(";"); + } + } else { + +// If we are not in a browser, then the file form of strict pragma may be used. + + if ( + next_token.value === "use strict" + ) { + global.strict = next_token; + advance("(string)"); + advance(";"); + } + } + tree = statements(); + advance("(end)"); + functionage = global; + walk_statement(tree); + if (module_mode && global.strict !== undefined) { + warn("unexpected_a", global.strict); + } + if (warnings.length === 0) { + uninitialized_and_unused(); + if (!option.white) { + whitage(); + } + } + } + if (!option.browser) { + directives.forEach(function (comment) { + if (comment.directive === "global") { + warn("missing_browser", comment); + } + }); + } + early_stop = false; + } catch (e) { + if (e.name !== "JSLintError") { + warnings.push(e); + } + } + return { + directives, + edition: "2018-10-26", + exports, + froms, + functions, + global, + id: "(JSLint)", + json: json_mode, + lines, + module: module_mode === true, + ok: warnings.length === 0 && !early_stop, + option, + property, + shebang: ( + shebang + ? lines[0] + : undefined + ), + stop: early_stop, + tokens, + tree, + warnings: warnings.sort(function (a, b) { + return a.line - b.line || a.column - b.column; + }) + }; +}); diff --git a/branch.remove_unnecessary_recursion/report.js b/branch.remove_unnecessary_recursion/report.js new file mode 100644 index 000000000..5e487d143 --- /dev/null +++ b/branch.remove_unnecessary_recursion/report.js @@ -0,0 +1,222 @@ +// report.js +// 2018-10-22 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Generate JSLint HTML reports. + +/*property + closure, column, context, edition, error, exports, filter, forEach, freeze, + froms, fudge, function, functions, global, id, isArray, join, json, keys, + length, level, line, lines, message, module, name, names, option, + parameters, parent, property, push, replace, role, signature, sort, stop, + warnings +*/ + +const rx_amp = /&/g; +const rx_gt = />/g; +const rx_lt = / with less destructive entities. + + return String( + string + ).replace( + rx_amp, + "&" + ).replace( + rx_lt, + "<" + ).replace( + rx_gt, + ">" + ); +} + +export default Object.freeze({ + error: function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + let fudge = Number(Boolean(data.option.fudge)); + let output = []; + if (data.stop) { + output.push("
    JSLint was unable to finish.
    "); + } + data.warnings.forEach(function (warning) { + output.push( + "
    ", + entityify(warning.line + fudge), + ".", + entityify(warning.column + fudge), + "
    ", + entityify(warning.message), + "
    ", + entityify(data.lines[warning.line] || ""), + "" + ); + }); + return output.join(""); + }, + + function: function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + let fudge = Number(Boolean(data.option.fudge)); + let mode = ( + data.module + ? "module" + : "global" + ); + let output = []; + + if (data.json) { + return ( + data.warnings.length === 0 + ? "
    JSON: good.
    " + : "
    JSON: bad.
    " + ); + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + let global = Object.keys(data.global.context).sort(); + let froms = data.froms.sort(); + let exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + let context = the_function.context; + let list = Object.keys(context); + output.push( + "
    ", + entityify(the_function.line + fudge), + "
    ", + ( + the_function.name === "=>" + ? entityify(the_function.signature) + " =>" + : ( + typeof the_function.name === "string" + ? "«" + entityify(the_function.name) + "»" + : "" + entityify(the_function.name.id) + "" + ) + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + let params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.parent === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.parent === the_function + ); + })); + detail("outer", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.parent !== the_function + && the_variable.parent.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].parent.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + output.push( + "
    JSLint edition ", + entityify(data.edition), + "
    " + ); + return output.join(""); + }, + + property: function property_directive(data) { + +// Produce the /*property*/ directive. + + let not_first = false; + let output = ["/*property"]; + let length = 1111; + let properties = Object.keys(data.property); + + if (properties.length > 0) { + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length >= 80) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); + } + } +}); diff --git a/branch.replay_bug_when_source_is_undefined/README b/branch.replay_bug_when_source_is_undefined/README new file mode 100644 index 000000000..f46e91069 --- /dev/null +++ b/branch.replay_bug_when_source_is_undefined/README @@ -0,0 +1,42 @@ +JSLint, The JavaScript Code Quality Tool + +Douglas Crockford +douglas@crockford.com + +2018-05-28 + +jslint.js contains the jslint function. It parses and analyzes a source file, +returning an object with information about the file. It can also take an object +that sets options. + +index.html runs the jslint.js function in a web page. The page also depends +browser.js and report.js and jslint.css. + +jslint.css provides styling for index.html. + +browser.js runs the web user interface. + +report.js generates the results reports in HTML. + +help.html describes JSLint's usage. Please read it. + +function.html describes the jslint function and the results it produces. + +Please direct questions and comments to +https://plus.google.com/communities/104441363299760713736. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[http://www.crockford.com/wrrrld/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. diff --git a/branch.replay_bug_when_source_is_undefined/browser.js b/branch.replay_bug_when_source_is_undefined/browser.js new file mode 100644 index 000000000..7230dea0b --- /dev/null +++ b/branch.replay_bug_when_source_is_undefined/browser.js @@ -0,0 +1,159 @@ +// browser.js +// 2018-06-16 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +/*jslint + browser +*/ + +/*property + checked, create, disable, display, error, focus, forEach, function, + getElementById, innerHTML, join, length, map, onchange, onclick, onscroll, + property, querySelectorAll, scrollTop, select, split, style, title, value +*/ + +import jslint from "./jslint.js"; +window.jslint = jslint; +import report from "./report.js"; + +// This is the web script companion file for JSLint. It includes code for +// interacting with the browser and displaying the reports. + +let rx_crlf = /\n|\r\n?/; +let rx_separator = /[\s,;'"]+/; + +let fudge_unit = 0; + +const aux = document.getElementById("JSLINT_AUX"); +const boxes = document.querySelectorAll("[type=checkbox]"); +const fudge = document.getElementById("JSLINT_FUDGE"); +const global = document.getElementById("JSLINT_GLOBAL"); +const number = document.getElementById("JSLINT_NUMBER"); +const property = document.getElementById("JSLINT_PROPERTY"); +const property_fieldset = document.getElementById("JSLINT_PROPERTYFIELDSET"); +const report_field = document.getElementById("JSLINT_REPORT"); +const report_list = document.getElementById("JSLINT_REPORT_LIST"); +const source = document.getElementById("JSLINT_SOURCE"); +const select = document.getElementById("JSLINT_SELECT"); +const warnings = document.getElementById("JSLINT_WARNINGS"); +const warnings_list = document.getElementById("JSLINT_WARNINGS_LIST"); + +function show_numbers() { + number.value = source.value.split(rx_crlf).map(function (ignore, index) { + return index + fudge_unit; + }).join("\n"); +} + +function clear() { + aux.style.display = "none"; + number.value = ""; + property.value = ""; + property_fieldset.style.display = "none"; + report_field.style.display = "none"; + report_list.innerHTML = ""; + source.focus(); + source.value = ""; + warnings.style.display = "none"; + warnings_list.innerHTML = ""; +} + +function fudge_change() { + fudge_unit = Number(fudge.checked); + show_numbers(); +} + +function clear_options() { + boxes.forEach(function (node) { + node.checked = false; + }); + fudge_change(); + global.innerHTML = ""; +} + +function call_jslint() { + +// First build the option object. + + let option = Object.create(null); + boxes.forEach(function (node) { + if (node.checked) { + option[node.title] = true; + } + }); + +// Call JSLint with the source text, the options, and the predefined globals. + + let global_string = global.value; + let result = jslint( + source.value, + option, + ( + global_string === "" + ? undefined + : global_string.split(rx_separator) + ) + ); + +// Generate the reports. + + let error_html = report.error(result); + let function_html = report.function(result); + let property_text = report.property(result); + +// Display the reports. + + warnings_list.innerHTML = error_html; + warnings.style.display = ( + error_html.length === 0 + ? "none" + : "block" + ); + + report_list.innerHTML = function_html; + report_field.style.display = "block"; + if (property_text) { + property.value = property_text; + property_fieldset.style.display = "block"; + property.scrollTop = 0; + select.disable = false; + } else { + property_fieldset.style.display = "none"; + select.disable = true; + } + aux.style.display = "block"; + source.select(); +} + +fudge.onchange = fudge_change; + +source.onchange = function (ignore) { + show_numbers(); +}; + +source.onscroll = function () { + let ss = source.scrollTop; + number.scrollTop = ss; + let sn = number.scrollTop; + if (ss > sn) { + show_numbers(); + number.scrollTop = ss; + } +}; + +document.querySelectorAll("[name='JSLint']").forEach(function (node) { + node.onclick = call_jslint; +}); + +document.querySelectorAll("[name='clear']").forEach(function (node) { + node.onclick = clear; +}); + +document.getElementById("JSLINT_SELECT").onclick = function () { + property.focus(); + property.select(); +}; +document.getElementById("JSLINT_CLEAR_OPTIONS").onclick = clear_options; + +fudge_change(); +source.select(); +source.focus(); diff --git a/branch.replay_bug_when_source_is_undefined/function.html b/branch.replay_bug_when_source_is_undefined/function.html new file mode 100644 index 000000000..8d7287cbc --- /dev/null +++ b/branch.replay_bug_when_source_is_undefined/function.html @@ -0,0 +1,612 @@ + + + + + + + + +JSLint: jslint function + + + +
    JSLint
    + +
    The jslint Function
    +
    +

    JSLint

    +

    JSLint is delivered as a set of files:

    +
    + + + + + + + + + + + + + + +
    FilenameContent
    browser.jsBrowser based UI.
    function.htmlThis page that you are looking at right now.
    help.htmlUsing jslint as a single page JSLint application.
    index.htmlSingle page JSLint application that uses the + js files.
    jslint.jsThe jslint function.
    report.jsThe report object, containing report generator functions for + HTML.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    +
    + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring"(JSLint)"
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    parenttokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    parenttokenThe function in which the variable was declared.variable
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable can be assigned to.variable
    +

    Reports

    +

    The report object contains three functions that all take a + result from the jslint function as input. + report.js has no other dependence on other files.

    + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    +
    + +
    + + + + + +
    + + diff --git a/branch.replay_bug_when_source_is_undefined/help.html b/branch.replay_bug_when_source_is_undefined/help.html new file mode 100644 index 000000000..ee236ff95 --- /dev/null +++ b/branch.replay_bug_when_source_is_undefined/help.html @@ -0,0 +1,841 @@ + + + + + + + + + +JSLint: Help + + + +
    JSLint
    + +
    Help
    +
    +
    Il semble que la perfection soit atteinte non quand il n’y a + plus rien à ajouter, mais quand il n’y a plus rien à + retrancher.
    +
    + Antoine de Saint-Exupéry +
    +
    + Terre des Hommes (1939) +
    +

    Good Parts

    +

    The Principle of the Good Parts is

    +
    If a feature is sometimes useful and sometimes dangerous and if + there is a better option then always use the better option.
    +

    JSLint is a JavaScript program that looks for problems in + JavaScript programs. It is a code quality tool.

    +

    When C was + a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    +

    As the language matured, the definition of the language was + strengthened to eliminate some insecurities, and compilers got better + at issuing warnings. lint is no longer needed.

    +

    JavaScript is a + young-for-its-age language. It was originally intended to do small tasks in + webpages, tasks for which Java was too heavy and clumsy. But JavaScript is + a surprisingly capable language, and it is now being used in larger + projects. Many of the features that were intended to make the language easy + to use are troublesome when projects become complicated. A lint + for JavaScript is needed: JSLint, a JavaScript syntax checker + and validator.

    +

    JSLint takes a JavaScript source and scans it. If it finds a + problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by the ECMAScript Programming Language + Standard (the strangely named document that governs JavaScript). + JSLint will reject most legal programs. It is a higher + standard.

    +

    JavaScript is a sloppy language, but hidden deep inside there is an elegant, + better language. JSLint helps you to program in that better + language and to avoid most of the slop. JSLint will reject + programs that browsers will accept because JSLint is concerned + with the quality of your code and browsers are not. You should gladly accept + all of JSLint's advice.

    +

    JSLint can operate on JavaScript source + or JSON text.

    +

    ECMAScript Sixth Edition

    +

    Some of + ES6’s features are good, so JSLint will recognize the good + parts of ES6.

    +

    Currently, these features are recognized:

    +
      +
    • The ... ellipsis marker in parameter + lists and argument lists, replacing the arguments object + for variadic functions.
    • +
    • The let statement, which is like the var + statement except that it respects block scope. + You may use let or var but not both.
    • +
    • The const statement is like the let statement + except that it disallows the use of assignment on the variable, although + if the value of the variable is mutable, it can still be mutated. + const is preferred to let. +
    • Destructuring of arrays and objects is allowed in parameter lists and on + the left side of let, and const, but not + var or assignment statements, and not deep destructuring + or eliding.
    • +
    • Enhanced object literals, providing shorter forms for function + declaration and properties that are initialized by variables with the + same name.
    • +
    • The fat arrow => fart functions.
    • +
    • The simplest forms of import and export.
    • +
    • `Megastring` literals, but not nested + `megastring` literals.
    • +
    • New global functions, such as Map, Set, + WeakMap, and WeakSet.
    • +
    • 0b- and 0o- number literals.
    • +
    +

    The most important new feature of ES6 is proper tail calls. This has no + new syntax, so JSLint doesn’t see it. But it makes recursion + much more attractive, which makes loops, particularly for + loops, much less attractive.

    +

    import export

    +

    The ES6 module feature will be an important improvement over JavaScript’s + global variables as a means of linking separate files together. + JSLint recognizes a small but essential subset of the module + syntax.

    +
    +

    import name from stringliteral;

    +

    import {name} from stringliteral;

    +

    export default function () {};

    +

    export default function name() {};

    +

    export default expression;

    +

    export function name() {}

    +

    export name; // where name is const

    +

    export {name};

    +
    +

    Directives

    +

    JSLint provides three directives that may be placed in a + file to manage JSLint’s behavior. Use of these directives is + optional. If they are used, they should be placed in a source file before + the first statement. They are written in the form of a comment, where the + directive name is placed immediately after the opening of the comment before + any whitespace. The three directives are global, + jslint, and property. Directives in a file are + stronger than options selected from the UI or passed with the option object.

    +

    /*global*/

    +

    The /*global*/ directive is used to specify a set of globals + (usually functions and objects containing functions) that are available to + this file. This was commonly used in browsers to link source files together + before ES6 modules appeared. Use of global variables is strongly + discouraged, but unfortunately web browsers require their use. The + /*global*/ directive can only be used when the + Assume a browser option is selected. +

    Each of the names listed will indicate a read-only global variable. The names + are separated by , comma. This directive + should not be used if import or export is used. + This directive inhibits warnings, but it does not declare the names in the + execution environment. +

    For example: +

    /*global +
    ADSAFE, report, jslint
    +*/
    +

    instructs JSLint to not give warnings about the global + variables ADsafe, report, and jslint. + However, if any of those names are expected to be supplied by other files + and those other files fail to do so, then execution errors will result. It + is usually better to use top-level variable declarations instead: +

        var ADSAFE;
    +    var report;
    +    var jslint; 
    +

    Using var in this way allows comparing a global variable to the + undefined value to determine whether it is has been used in the global context.

    +

    /*jslint*/

    +

    The /*jslint*/ directive allows for the control of several + options. These options can also be set from the + JSLint.com user interface.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Tolerate bitwise operatorsbitwisetrue if bitwise operators should be allowed. The + bitwise operators are rarely used in JavaScript programs, so it + is usually more likely that & is a mistyping of + && than that it indicates a bitwise and. + JSLint will give warnings on the bitwise operators + unless this option is selected.
    Assume a browser browsertrue if the standard browser globals should be + predefined. This option will reject the use of + import and export. This option also + disallows the file form of the "use + strict" pragma. It does not supply + self; you will have to request that unnecessary + alias of the dreaded global object yourself. It adds the same + globals as this directive: +
    /*global +
    + clearInterval, clearTimeout, document, event, FileReader, + FormData, history, localStorage, location, name, navigator, + screen, sessionStorage, setInterval, setTimeout, Storage, + URL, window, XMLHttpRequest +
    + */
    +
    Tolerate conversion operatorsconverttrue if !!, + prefix, + and concatenation with "" are allowed. + The Boolean, Number, and + String functions are preferred
    Assume CouchDBcouchtrue if + Couch DB + globals should be predefined. It adds the same globals as this + directive: +
    /*global +
    emit, getRow, isArray, log, provides, registerType, require, + send, start, sum, toJSON
    + */
    Assume in developmentdeveltrue if browser globals that are useful in + development should be predefined, and if debugger + statements and TODO comments + should be allowed. It adds the + same globals as this directive: +
    /*global +
    alert, confirm, console, prompt
    + */
    Be sure to turn this option off before going + into production.
    Tolerate evalevaltrue if eval should be allowed. In the + past, the eval function was the most misused + feature of the language.
    Tolerate for statementfortrue if the for statement should be + allowed. It is almost always better to use the array methods + instead.
    Fudge the line and column numbersfudgetrue if the origin of a text file is line 1 column + 1. Programmers know that number systems start at zero. + Unfortunately, most text editors start numbering lines and + columns at one. JSLint will by default start + numbering at zero. Use this option to start the numbering at one + in its reports.
    Tolerate get and setgetsettrue if accessor properties are allowed in object literals.
    Tolerate long source lineslongtrue if a line can contain more than 80 characters.
    Tolerate multiple variables declarations per statementmultivartrue if a var, let, or + const statement can declare two or more variables + in a single statement.
    Assume Node.jsnodetrue if Node.js globals should be predefined. It + will predefine globals that are used in the Node.js environment. + It adds the same globals as this directive: +
    /*global +
    + Buffer, clearImmediate, clearInterval, clearTimeout, console, exports, + module, process, require, setImmediate, setInterval, setTimeout, URL, + URLSearchParams, __dirname, __filename +
    + */
    Tolerate single quote stringssingletrue if 'single quote + should be allowed to enclose string literals.
    Tolerate this
    thistrue if this should be allowed.
    Tolerate whitespace messwhitetrue if the whitespace rules should be ignored.
    +

    For example:

    +
    /*jslint
    +    bitwise, node
    +*/
    + +

    /*property*/

    +

    The /*property*/ directive is used to declare a list of + property identifiers that are used in the file. Each property name in the + program is looked up in this list. If a name is not found, that indicates an + error, most likely a typing error.

    +

    The list can also be used to evade some of JSLint’s naming + rules.

    +

    JSLint can build the /*property*/ list for you. At + the bottom of its report, JSLint displays a + /*property*/ directive. It contains all of the names that were + used with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. You + can copy the /*property*/ directive to the top of your + script file. JSLint will check the spelling of all property + names against the list. That way, you can have JSLint look for + misspellings for you.

    +

    For example,

    +
    /*property +
    charAt, slice, _$_
    + */
    +

    ignore

    +

    JSLint introduces a new reserved word: ignore. It + is used in parameter lists and in catch clauses to indicate a + parameter that will be ignored. Unused warnings will not be produced for + ignore. +

    function handler(ignore, value) {
    +    return do_something_useful(value);
    +}
    +

    var let const

    +

    JavaScript provides three statements for declaring variables: + var, let, and const. The + var statement suffers from a bad practice called hoisting. The + let statement does not do hoisting and respects block scope. + The const statement is like let except that it + marks the variable (but not its contents) as read only, making it an error + to attempt to assign to the variable. When given a choice, + const is the best, var is the worst.

    +

    JSLint uses the intersection of the var rules and the + let rules, and by doing so avoids the errors related to either. + A name should be declared only once in a function. It should be declared + before it is used. It should not be used outside of the block in which it is + declared. A variable should not have the same name as a variable or + parameter in an outer function. Do not mix var and + let. Declare one name per statement.

    +

    = == ===

    +

    Fortran made a terrible + mistake in using the equality operator as its assignment operator. That + mistake has been replicated in most languages since then. C compounded that + mistake by making == its equality operator. The visual + similarity is a source of errors. JavaScript compounded this further by + making == a type coercing comparison operator that produces + false positive results. This was mitigated by adding the === + operator, leaving the broken == operator in place.

    +

    JSLint attempts to minimize errors by the following rules:

    +

    == is not allowed. This avoids the false positives, and + increases the visual distance between = and ===. + Assignments are not allowed in expression position, and comparisons are not + allowed in statement position. This also reduces confusion. 

    +

    Semicolon

    +

    JavaScript uses a C-like syntax that requires the use of semicolons to + delimit certain statements. JavaScript attempts to make those semicolons + optional with an automatic semicolon insertion mechanism, but it does not + work very well. Automatic semicolon insertion was added to make + things easier for beginners. Unfortunately, it sometimes fails. Do not rely + on it unless you are a beginner.

    +

    JSLint expects that every statement will be followed by + ; except for for, function, + if, switch, try, and + while. JSLint does not expect to see unnecessary + semicolons, the empty statement, or empty blocks.

    +

    function =>

    +

    JavaScript has four syntactic forms for making function objects: function + statements, function expressions, enhanced object literals, and the + => fart operator.

    +

    The function statement creates a variable and assigns the function object to + it. It should be used in a file or function body, but not inside of a block.

    +
    function name(parameters) { +
    statements
    + }
    +

    The function expression unfortunately looks like the function statement. It + may appear anywhere that an expression may appear, but not in statement + position and not in a loop. It produces a function object but does not + create a variable in which to store it.

    +
    function (parameters) { +
    statements
    + }
    +

    The enhanced object literal provides an ES6 shorthand for creating a property + whose value is a function, saving you from having to type + : colon and function.

    +
    { +
    name(parameters) { +
    statements
    + }
    + }
    +

    Finally, ES6 provides an even shorter form of function expression that leaves + out the words function and return:

    +
    (parameters) => + expression
    +

    JSLint requires the parens around the parameters, and forbids a + { left brace after the + => fart to avoid syntactic ambiguity.

    +

    Comma

    +

    The , comma operator is unnecessary and can + mask programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as + an operator. It does not expect to see elided elements in array literals. A + comma should not appear after the last element of an array literal or object + literal.

    +

    Blocks

    +

    JSLint expects blocks with function, + if, switch, while, for, + do, and try statements and nowhere else.

    +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    +

    JavaScript allows an if to be written like this:

    +
    if (condition)
    +    statement;
    +

    That form is known to contribute to mistakes in projects where many + programmers are working on the same code. That is why JSLint + expects the use of a block:

    +
    if (condition) {
    +    statements;
    +}
    +

    Experience shows that this form is more resilient.

    +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call. All other expression statements are considered + to be errors.

    +

    for

    +

    JSLint does not recommend use of the for statement. + Use array methods like forEach instead. The for + option will suppress some warnings. The forms of for that + JSLint accepts are restricted, excluding the new ES6 forms.

    +

    for in

    +

    JSLint does not recommend use of the for + in statement. Use Object.keys instead.

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, it also + loops through all of the properties that were inherited through the + prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written + without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    for (name in object) {
    +    if (object.hasOwnProperty(name)) {
    +        ....
    +    }
    +}
    +

    Note that the above code will fail if the object contains a property + named hasOwnProperty. Use Object.keys instead.

    +

    switch

    +

    A common error in switch statements is to forget to place a + break statement after each case, resulting in unintended + fall-through. JSLint expects that the statement before the next + case or default is one of these: + break, return, or throw.

    +

    with

    +

    The with statement was intended to provide a shorthand in + accessing properties in deeply nested objects. Unfortunately, it behaves + very badly when setting new properties. Never use the with + statement.

    +

    JSLint does not expect to see a with statement.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that + labels will be distinct from vars and parameters.

    +

    Unreachable Code

    +

    JSLint expects that a return, break, + or throw statement will be followed by a + } right brace or case or + default.

    +

    Confusing Pluses and Minuses

    +

    JSLint expects that + will not be followed by + + or ++, and that - will not be + followed by - or --. A misplaced space can turn + + + into ++, an error that is difficult to see. + Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ increment and + -- decrement operators have been known to + contribute to bad code by encouraging excessive trickiness. They are second + only to faulty architecture in enabling to viruses and other security + menaces. Also, preincrement/postincrement confusion can produce off-by-one + errors that are extremely difficult to diagnose. Fortunately, they are also + complete unnecessary. There are better ways to add 1 to a variable.

    +

    It is best to avoid these operators entirely and rely on += and + -= instead.

    +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. + JSLint looks for problems that may cause portability problems. + It also attempts to resolve visual ambiguities by recommending explicit + escapement.

    +

    JavaScript’s syntax for regular expression literals overloads the + / slash character. To avoid ambiguity, + JSLint expects that the character preceding a regular + expression literal is a ( left paren or + = equal or + : colon or + , comma character.

    +

    this

    +
    Having this in the language makes it harder to talk + about the language. It is like pair programming with Abbott and Costello. +
    +

    Avoid using this. Warnings about this can be + suppressed with option.this.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the + new prefix. The new prefix creates a new object + based on the function's prototype, and binds that object to the + function's implied this parameter. If you neglect to use the + new prefix, no new object will be made and this + will be bound to the global object.

    +

    JSLint enforces the convention that constructor functions be + given names with initial uppercase. JSLint does not expect to + see a function invocation with an initial uppercase name unless it has the + new prefix. JSLint does not expect to see the + new prefix used with functions whose names do not start with + initial uppercase.

    +

    JSLint does not expect to see the wrapper forms + new Number, new String, new Boolean.

    +

    JSLint does not expect to see new Object. + Use Object.create(null) instead.

    +

    Whitespace

    +

    JSLint has a specific set of rules about the use of whitespace. + Where possible, these rules are consistent with centuries of good practice + with literary style.

    +

    The indentation increases by 4 spaces when the last token on a line is + { left brace, + [ left bracket, + ( left paren. The matching closing + token will be the first token on a line, restoring the previous + indentation.

    +

    The ternary operator can be visually confusing, so + ? question mark and + : colon always begin a line and increase + the indentation by 4 spaces.

    +
    return (the_token.id === "(string)" || the_token.id === "(number)")
    +    ? String(the_token.value)
    +    : the_token.id;
    +

    If . period is the first character on a line, the + indentation is increased by 4 spaces.

    +

    A var, let, or const statement will + also cause an indentation if it declares two or more variables, and if the + second variable is not on the same line as the start of the statement.

    +

    Long lines can be continued on the next line with 8 spaces added to the + current indentation. Those 8 spaces do not change the current indentation. + It is 8 to avoid confusion with ordinary indentation.

    +

    The word function is always followed with one space.

    +

    Clauses (case, catch, default, + else, finally) are not statements and so should + not be indented like statements.

    +

    Spaces are used to make things that are not invocations look less like + invocations.

    +

    Tabs and spaces should not be mixed. We should pick just one in order to + avoid the problems that come from having both. Personal preference is an + extremely unreliable criterion. Neither offers a powerful advantage over the + other. Fifty years ago, tab had the advantage of consuming less memory, but + Moore's Law has eliminated that advantage. Space has one clear advantage + over tab: there is no reliable standard for how many spaces a tab + represents, but it is universally accepted that a space occupies a space. So + use spaces. You can edit with tabs if you must, but make sure it is spaces + again before you commit. Maybe someday we will finally get a universal + standard for tabs, but until that day comes, the better choice is spaces.

    +

    Unsafe Characters

    +

    There are characters that are handled inconsistently in some browsers, and + so must be escaped when placed in strings.

    +
    \u0000-\u001f
    +\u007f-\u009f
    +\u00ad
    +\u0600-\u0604
    +\u070f
    +\u17b4
    +\u17b5
    +\u200c-\u200f
    +\u2028-\u202f
    +\u2060-\u206f
    +\ufeff
    +\ufff0-\uffff
    +

    Report

    +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    +
      +
    • The line number on which it starts.
    • +
    • The function’s name. In the case of anonymous functions, + JSLint will attempt to + «guess» the name.
    • +
    • The parameters.
    • +
    • Variables: The variables that are declared in the function.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Exceptions: The variables that are declared by catch + clauses of try statements.
    • +
    • Outer: Variables used by this function that are declared in + other functions.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements. Updates are announced at + https://plus.google.com/communities/104441363299760713736.

    +

    Try it

    +

    Try it. Paste your script + into the window and click the + + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    JSLint is written entirely in JavaScript, so it can run + anywhere that JavaScript (or even Java) can run.

    +

    Perfectly fine

    +

    JSLint was designed to reject code that some would consider to + be perfectly fine. The reason for this is that JSLint's + purpose is to help produce programs that are free of error. That is + difficult in any language and is especially hard in JavaScript. + JSLint attempts to help you increase the visual distance + between correct programs and incorrect programs, making the remaining errors + more obvious. JSLint will give warnings about things that are + not necessarily wrong in the current situation, but which have been observed + to mask or obscure errors. Avoid those things when there are better options + available.

    +

    It is dangerous out there. JSLint is here to help.

    +

    Warning

    +

    JSLint will hurt your feelings. Side effects may include + headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, + dry mouth, cleaner code, and a reduced error rate.

    +

    Further Reading

    + +
    +

    + Code Conventions for the JavaScript Programming Language.

    + + + + + +
    + + diff --git a/branch.replay_bug_when_source_is_undefined/index.html b/branch.replay_bug_when_source_is_undefined/index.html new file mode 100644 index 000000000..235f44b54 --- /dev/null +++ b/branch.replay_bug_when_source_is_undefined/index.html @@ -0,0 +1,125 @@ + + + + + + + + + + + +JSLint: The JavaScript Code Quality Tool + + +
    +
    Source
    +
    + + +
    +
    + Warnings +
    +
    +
    + Function Report +
    +
    +
    + Property Directive + +
    +
    + + + + +
    +
    Options +
    Assume... +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Global variables... + +
    +
    +
    +
    + + + + + +
    + + diff --git a/branch.replay_bug_when_source_is_undefined/jslint.css b/branch.replay_bug_when_source_is_undefined/jslint.css new file mode 100644 index 000000000..cbcf4c042 --- /dev/null +++ b/branch.replay_bug_when_source_is_undefined/jslint.css @@ -0,0 +1,341 @@ +@font-face { + font-family: 'Programma'; + font-weight: bold; + src: url('Programma-Bold.woff') format('woff'); +} +@font-face { + font-family: 'Daley'; + font-weight: bold; + src: url('Daley-Bold.woff') format('woff'); +} + +body { + background-color: antiquewhite; + color: black; + font-family: 'Daley', sans-serif; + margin: 0; + padding: 0; +} + +#JSLINT_TITLEBOX { + font-family: 'Daley', sans-serif; + margin: 0; + margin-left: 3%; + margin-right: 3%; + padding: 0; +} + +#JSLINT_TITLEBOX ul { + float: right; + font-size: 12pt; +} + +#JSLINT_TITLEBOX div { + color: darkslategray; + float: left; + font-size: 48pt; +} + +#JSLINT_ fieldset { + background-color: gainsboro; + border: 0; + clear: both; + margin-bottom: 1em; + margin-left: 3%; + margin-right: 3%; + margin-top: 1em; + padding: 0; + width: auto; +} +#JSLINT_ legend { + background-color: darkslategray; + border: 0; + color: white; + font-size: 100%; + font-style: normal; + font-weight: normal; + margin: 0; + padding-bottom: 0.25em; + padding-left: 0; + padding-right: 0; + padding-top: 0.25em; + text-align: center; + width: 100%; +} +#JSLINT_ button { + background-color: darkslategray; + border: 0; + color: white; + cursor: pointer; + font-family: 'Daley', sans-serif; + font-size: 100%; + font-style: normal; + margin-left: 1em; + margin-right: 1em; + padding-left: 1em; + padding-right: 1em; + padding-bottom: 0.25em; + padding-top: 0.25em; + text-align: center; +} +#JSLINT_ button:hover { + background-color: slategray; + color: white; + border: 0; +} + +#JSLINT_ button:active { + border: 0; + color: black; +} + +#JSLINT_ button:disabled { + background-color: gray; + border: 0; + color: gainsboro; +} + +a:visited { + color: #69565c; +} + +a:link { + color: black; +} + +#JSLINT_ input[type="text"] { + background-color: white; + border: 0; + color: black; + margin-bottom: 0.2em; + margin-right: 0.5em; + padding-left: 0.5em; + padding-right: 0.5em; + text-align: right; + width: 3em; +} + +#JSLINT_ textarea { + border: 0; + background-color: white; + border: 0; + font-family: Programma, monospace; + font-size: 100%; + font-weight: bold; + resize: none; +} + +#JSLINT_ textarea::selection { + background-color: yellow; + color: black; +} + +#JSLINT_NUMBER { + color: darkgray; + display: inline-block; + height: 4in; + margin: 2%; + margin-right: 0; + overflow: hidden; + padding-right: 0.5%; + text-align: right; + white-space: pre; + width: 4%; +} + +#JSLINT_SOURCE { + color: black; + display: inline-block; + font-size: 100%; + height: 4in; + margin: 2%; + margin-left: 0; + margin-right: 0; + overflow: auto; + padding-left: 0.5%; + white-space: pre; + width: 91%; +} + +#JSLINT_PROPERTY { + background-color: honeydew; + border: 0; + margin: 2%; + padding: 0.5%; + white-space: pre; + width: 95%; +} + +#JSLINT_GLOBAL { + white-space: normal; + width: 95%; +} +#JSLINT_ label { + font-family: sans-serif; + font-size: 90%; + padding-left: 0.25em; +} +#JSLINT_OPTIONS>div { + float: left; + margin: 0.5em; +} + +#JSLINT_ address { + color: black; + display: block; + float: right; + font-family: serif; + font-size: 90%; + margin-left: 1em; +} +#JSLINT_ dl { + background-color: cornsilk; + color: white; + margin: 0; + padding-bottom: 2pt; + padding-left: 1em; + padding-right: 1em; + padding-top: 2pt; +} +#JSLINT_ dfn { + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + margin-bottom: 2pt; +} +#JSLINT_ dt { + color: black; + display: block; + float: left; + font-family: serif; + font-size: 75%; + font-style: italic; + margin: 0; + width: 8em; + text-align: right; +} +#JSLINT_ dd { + color: black; + display: block; + font-family: Programma, monospace; + font-weight: bold; + margin-left: 8em; + padding-bottom: 2pt; +} +#JSLINT_WARNINGS>legend { + background-color: indianred; +} +#JSLINT_WARNINGS>div { + background-color: pink; + padding: 1em; +} +#JSLINT_WARNINGS cite { + color: black; + display: block; + font-family: serif; + font-size: 100%; + font-style: normal; + margin-bottom: 4pt; + margin-left: 20pt; + margin-right: 20pt; + margin-top: 4pt; + overflow-x: hidden; +} +#JSLINT_WARNINGS samp { + background-color: lavenderblush; + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + padding: 4pt; + margin-bottom: 0; + margin-left: 16pt; + margin-right: 16pt; + margin-top: 0; + white-space: pre-wrap; +} + +#JSLINT_REPORT center { + margin-top: 1em; +} + +#JSLINT_WARNINGS dl address { + color: black; + display: inline; + float: none; + font-size: 80%; + margin: 0; +} + +#JSLINT_REPORT>div { + padding: 1em; +} + +#JSLINT_ dl.level0 { + color: black; + background-color: white; +} + +#JSLINT_ dl.level1 { + color: black; + background-color: #ffffe0; /* yellow */ + margin-left: 1em; +} + +#JSLINT_ dl.level2 { + color: black; + background-color: #e0ffe0; /* green */ + margin-left: 2em; +} + +#JSLINT_ dl.level3 { + color: black; + background-color: #e0e0ff; /* blue */ + margin-left: 3em; +} + +#JSLINT_ dl.level4 { + background-color: #ffe0ff; /* purple */ + color: black; + margin-left: 4em; +} + +#JSLINT_ dl.level5 { + background-color: #ffe0e0; /* red */ + color: black; + margin-left: 5em; +} + +#JSLINT_ dl.level6 { + background-color: #ffe390; /* orange */ + color: black; + margin-left: 6em; +} + +#JSLINT_ dl.level7 { + background-color: #e0e0e0; /* gray */ + color: black; + margin-left: 7em; +} + +#JSLINT_ dl.level8 { + color: black; + margin-left: 8em; +} + +#JSLINT_ dl.level9 { + color: black; + margin-left: 9em; +} + +.none { + display: none; +} +.center { + text-align: center; +} diff --git a/branch.replay_bug_when_source_is_undefined/jslint.js b/branch.replay_bug_when_source_is_undefined/jslint.js new file mode 100644 index 000000000..95ae10c12 --- /dev/null +++ b/branch.replay_bug_when_source_is_undefined/jslint.js @@ -0,0 +1,4912 @@ +// jslint.js +// 2018-08-11 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// jslint(source, option_object, global_array) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze, a string or an array of strings. +// option_object An object whose keys correspond to option names. +// global_array An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all of the functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// 1. If the source is a single string, split it into an array of strings. +// 2. Turn the source into an array of tokens. +// 3. Furcate the tokens into a parse tree. +// 4. Walk the tree, traversing all of the nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// 5. Check the whitespace between the tokens. + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*jslint long */ + +/*property + a, and, arity, assign, b, bad_assignment_a, bad_directive_a, + bad_get, bad_module_name_a, bad_option_a, bad_property_a, bad_set, + bitwise, block, body, browser, c, calls, catch, charCodeAt, closer, + closure, code, column, complex, concat, constant, context, convert, + couch, create, d, dead, default, devel, directive, directives, + disrupt, dot, duplicate_a, edition, ellipsis, else, empty_block, + escape_mega, eval, every, expected_a, expected_a_at_b_c, + expected_a_b, expected_a_b_from_c_d, expected_a_before_b, + expected_a_next_at_b, expected_digits_after_a, expected_four_digits, + expected_identifier_a, expected_line_break_a_b, + expected_regexp_factor_a, expected_space_a_b, expected_statements_a, + expected_string_a, expected_type_string_a, exports, expression, + extra, finally, flag, for, forEach, free, from, froms, fud, fudge, + function_in_loop, functions, g, getset, global, i, id, identifier, + import, inc, indexOf, infix_in, init, initial, isArray, isNaN, join, + json, keys, label, label_a, lbp, led, length, level, line, lines, + live, long, loop, m, margin, match, message, misplaced_a, + misplaced_directive_a, missing_browser, missing_m, module, multivar, + naked_block, name, names, nested_comment, new, node, not_label_a, + nr, nud, number_isNaN, ok, open, option, out_of_scope_a, parameters, + parent, pop, property, push, quote, redefinition_a_b, replace, + required_a_optional_b, reserved_a, right, role, search, signature, + single, slice, some, sort, split, statement, stop, strict, + subscript_a, switch, test, this, thru, toString, todo_comment, + tokens, too_long, too_many_digits, tree, try, type, u, + unclosed_comment, unclosed_mega, unclosed_string, undeclared_a, + unexpected_a, unexpected_a_after_b, unexpected_a_before_b, + unexpected_at_top_level_a, unexpected_char_a, unexpected_comment, + unexpected_directive_a, unexpected_expression_a, unexpected_label_a, + unexpected_parens, unexpected_space_a_b, unexpected_statement_a, + unexpected_trailing_space, unexpected_typeof_a, uninitialized_a, + unreachable_a, unregistered_property_a, unsafe, unused_a, + use_double, use_open, use_spaces, use_strict, used, value, var_loop, + var_switch, variable, warning, warnings, weird_condition_a, + weird_expression_a, weird_loop, weird_relation_a, white, + wrap_assignment, wrap_condition, wrap_immediate, wrap_parameter, + wrap_regexp, wrap_unary, wrapped, writable, y +*/ + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than {} because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +function populate(array, object = empty(), value = true) { + +// Augment an object by taking property names from an array of strings. + + array.forEach(function (name) { + object[name] = value; + }); + return object; +} + +const allowed_option = { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + bitwise: true, + browser: [ + "caches", "clearInterval", "clearTimeout", "document", "DOMException", + "Element", "Event", "event", "FileReader", "FormData", "history", + "localStorage", "location", "MutationObserver", "name", "navigator", + "screen", "sessionStorage", "setInterval", "setTimeout", "Storage", + "URL", "window", "Worker", "XMLHttpRequest" + ], + couch: [ + "emit", "getRow", "isArray", "log", "provides", "registerType", + "require", "send", "start", "sum", "toJSON" + ], + convert: true, + devel: [ + "alert", "confirm", "console", "prompt" + ], + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + multivar: true, + node: [ + "Buffer", "clearImmediate", "clearInterval", "clearTimeout", + "console", "exports", "module", "process", "require", + "setImmediate", "setInterval", "setTimeout", "URL", + "URLSearchParams", "__dirname", "__filename" + ], + single: true, + this: true, + white: true +}; + +const anticondition = populate([ + "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%", + "typeof", "(number)", "(string)" +]); + +// These are the bitwise operators. + +const bitwiseop = populate([ + "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=", + ">>>", ">>>=" +]); + +const escapeable = populate([ + "\\", "/", "`", "b", "f", "n", "r", "t" +]); + +const opener = { + +// The open and close pairs. + + "(": ")", // paren + "[": "]", // bracket + "{": "}", // brace + "${": "}" // mega +}; + +// The relational operators. + +const relationop = populate([ + "!=", "!==", "==", "===", "<", "<=", ">", ">=" +]); + +// This is the set of infix operators that require a space on each side. + +const spaceop = populate([ + "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/", + "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=", + ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" +]); + +const standard = [ + +// These are the globals that are provided by the language standard. + + "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI", + "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error", + "EvalError", "Float32Array", "Float64Array", "Generator", + "GeneratorFunction", "Int8Array", "Int16Array", "Int32Array", "Intl", + "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat", + "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", + "Set", "String", "Symbol", "SyntaxError", "System", "TypeError", + "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", + "URIError", "WeakMap", "WeakSet" +]; + +const bundle = { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + and: "The '&&' subexpression should be wrapped in parens.", + bad_assignment_a: "Bad assignment to '{a}'.", + bad_directive_a: "Bad directive '{a}'.", + bad_get: "A get function takes no parameters.", + bad_module_name_a: "Bad module name '{a}'.", + bad_option_a: "Bad option '{a}'.", + bad_property_a: "Bad property name '{a}'.", + bad_set: "A set function takes one parameter.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + escape_mega: "Unexpected escapement in mega literal.", + expected_a: "Expected '{a}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: ( + "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'." + ), + expected_a_before_b: "Expected '{a}' before '{b}'.", + expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.", + expected_digits_after_a: "Expected digits after '{a}'.", + expected_four_digits: "Expected four digits after '\\u'.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.", + expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.", + expected_space_a_b: "Expected one space between '{a}' and '{b}'.", + expected_statements_a: "Expected statements before '{a}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + expected_type_string_a: "Expected a type string and instead saw '{a}'.", + function_in_loop: "Don't make functions within a loop.", + infix_in: ( + "Unexpected 'in'. Compare with undefined, " + + "or use the hasOwnProperty method instead." + ), + label_a: "'{a}' is a statement label.", + misplaced_a: "Place '{a}' at the outermost level.", + misplaced_directive_a: ( + "Place the '/*{a}*/' directive before the first statement." + ), + missing_browser: "/*global*/ requires the Assume a browser option.", + missing_m: "Expected 'm' flag on a multiline regular expression.", + naked_block: "Naked block.", + nested_comment: "Nested comment.", + not_label_a: "'{a}' is not a label.", + number_isNaN: "Use Number.isNaN function to compare with NaN.", + out_of_scope_a: "'{a}' is out of scope.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + required_a_optional_b: ( + "Required parameter '{a}' after optional parameter '{b}'." + ), + reserved_a: "Reserved name '{a}'.", + subscript_a: "['{a}'] is better written in dot notation.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line is longer than 80 characters.", + too_many_digits: "Too many digits.", + unclosed_comment: "Unclosed comment.", + unclosed_mega: "Unclosed mega literal.", + unclosed_string: "Unclosed string.", + undeclared_a: "Undeclared '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_a_after_b: "Unexpected '{a}' after '{b}'.", + unexpected_a_before_b: "Unexpected '{a}' before '{b}'.", + unexpected_at_top_level_a: "Expected '{a}' to be in a function.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_directive_a: "When using modules, don't use directive '/*{a}'.", + unexpected_expression_a: ( + "Unexpected expression '{a}' in statement position." + ), + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_parens: "Don't wrap function literals in parens.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_statement_a: ( + "Unexpected statement '{a}' in expression position." + ), + unexpected_trailing_space: "Unexpected trailing space.", + unexpected_typeof_a: ( + "Unexpected 'typeof'. Use '===' to compare directly with {a}." + ), + uninitialized_a: "Uninitialized '{a}'.", + unreachable_a: "Unreachable '{a}'.", + unregistered_property_a: "Unregistered property name '{a}'.", + unsafe: "Unsafe character '{a}'.", + unused_a: "Unused '{a}'.", + use_double: "Use double quotes, not single quotes.", + use_open: ( + "Wrap a ternary expression in parens, " + + "with a line break after the left paren." + ), + use_spaces: "Use spaces, not tabs.", + use_strict: "This function needs a \"use strict\" pragma.", + var_loop: "Don't declare variables in a loop.", + var_switch: "Don't declare variables in a switch.", + weird_condition_a: "Weird condition '{a}'.", + weird_expression_a: "Weird expression '{a}'.", + weird_loop: "Weird loop.", + weird_relation_a: "Weird relation '{a}'.", + wrap_assignment: "Don't wrap assignment statements in parens.", + wrap_condition: "Wrap the condition in parens.", + wrap_immediate: ( + "Wrap an immediate function invocation in parentheses to assist " + + "the reader in understanding that the expression is the result " + + "of a function, and not the function itself." + ), + wrap_parameter: "Wrap the parameter in parens.", + wrap_regexp: "Wrap this regexp in parens to avoid confusion.", + wrap_unary: "Wrap the unary expression in parens." +}; + +// Regular expression literals: + +// supplant {variables} +const rx_supplant = /\{([^{}]*)\}/g; +// carriage return, carriage return linefeed, or linefeed +const rx_crlf = /\n|\r\n?/; +// unsafe characters that are silently deleted by one or more browsers +const rx_unsafe = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; +// identifier +const rx_identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; +const rx_module = /^[a-zA-Z0-9_$:.@\-\/]+$/; +const rx_bad_property = /^_|\$|Sync\$|_$/; +// star slash +const rx_star_slash = /\*\//; +// slash star +const rx_slash_star = /\/\*/; +// slash star or ending slash +const rx_slash_star_or_slash = /\/\*|\/$/; +// uncompleted work comment +const rx_todo = /\b(?:todo|TO\s?DO|HACK)\b/; +// tab +const rx_tab = /\t/g; +// directive +const rx_directive = /^(jslint|property|global)\s+(.*)$/; +const rx_directive_part = /^([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*(.*)$/; +// token (sorry it is so long) +const rx_token = /^((\s+)|([a-zA-Z_$][a-zA-Z0-9_$]*)|[(){}\[\]?,:;'"~`]|=(?:==?|>)?|\.+|[*\/][*\/=]?|\+[=+]?|-[=\-]?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<= "a" && string <= "z\uffff") + || (string >= "A" && string <= "Z\uffff") + ); +} + +function supplant(string, object) { + return string.replace(rx_supplant, function (found, filling) { + const replacement = object[filling]; + return ( + replacement !== undefined + ? replacement + : found + ); + }); +} + +let anon; // The guessed name for anonymous functions. +let blockage; // The current block. +let block_stack; // The stack of blocks. +let declared_globals; // The object containing the global declarations. +let directives; // The directive comments. +let directive_mode; // true if directives are still allowed. +let early_stop; // true if JSLint cannot finish. +let exports; // The exported names and values. +let froms; // The array collecting all import-from strings. +let fudge; // true if the natural numbers start with 1. +let functionage; // The current function. +let functions; // The array containing all of the functions. +let global; // The global object; the outermost context. +let json_mode; // true if parsing JSON. +let lines; // The array containing source lines. +let module_mode; // true if import or export was used. +let next_token; // The next token to be examined in the parse. +let option; // The options parameter. +let property; // The object containing the tallied property names. +let mega_mode; // true if currently parsing a megastring literal. +let stack; // The stack of functions. +let syntax; // The object containing the parser. +let token; // The current token being examined in the parse. +let token_nr; // The number of the next token. +let tokens; // The array of tokens. +let tenure; // The predefined property registry. +let tree; // The abstract parse tree. +let var_mode; // "var" if using var; "let" if using let. +let warnings; // The array collecting all generated warnings. + +// Error reportage functions: + +function artifact(the_token) { + +// Return a string representing an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); +} + +function artifact_line(the_token) { + +// Return the fudged line number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.line + fudge; +} + +function artifact_column(the_token) { + +// Return the fudged column number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.from + fudge; +} + +function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + const warning = { // ~~ + name: "JSLintError", + column: column, + line: line, + code: code + }; + if (a !== undefined) { + warning.a = a; + } + if (b !== undefined) { + warning.b = b; + } + if (c !== undefined) { + warning.c = c; + } + if (d !== undefined) { + warning.d = d; + } + warning.message = supplant(bundle[code] || code, warning); + warnings.push(warning); + return warning; +} + +function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); +} + +function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + if (the_token.warning === undefined) { + the_token.warning = warn_at( + code, + the_token.line, + the_token.from, + a || artifact(the_token), + b, + c, + d + ); + return the_token.warning; + } +} + +function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); +} + +// Tokenize: + +function tokenize(source) { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + +// If the source is not an array, then it is split into lines at the +// carriage return/linefeed. + + lines = ( + Array.isArray(source) + ? source + : source.split(rx_crlf) + ); + tokens = []; + + let char; // a popular character + let column = 0; // the column number of the next character + let first; // the first token + let from; // the starting column number of the token + let line = -1; // the line number of the next character + let nr = 0; // the next token number + let previous = global; // the previous token including comments + let prior = global; // the previous token excluding comments + let mega_from; // the starting column of megastring + let mega_line; // the starting line of megastring + let snippet; // a piece of string + let source_line; // the current line source string + + function next_line() { + +// Put the next line of source in source_line. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + let at; + column = 0; + line += 1; + source_line = lines[line]; + if (source_line !== undefined) { + at = source_line.search(rx_tab); + if (at >= 0) { + if (!option.white) { + warn_at("use_spaces", line, at + 1); + } + source_line = source_line.replace(rx_tab, " "); + } + at = source_line.search(rx_unsafe); + if (at >= 0) { + warn_at( + "unsafe", + line, + column + at, + "U+" + source_line.charCodeAt(at).toString(16) + ); + } + if (!option.white && source_line.slice(-1) === " ") { + warn_at( + "unexpected_trailing_space", + line, + source_line.length - 1 + ); + } + if (!option.long && source_line.length > 80) { + warn_at("too_long", line, 80); + } + } + return source_line; + } + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions snip, next_char, prev_char, +// some_digits, and escape help in the parsing of literals. + + function snip() { + +// Remove the last character from snippet. + + snippet = snippet.slice(0, -1); + } + + function next_char(match) { + +// Get the next character from the source line. Remove it from the source_line, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + return stop_at( + ( + char === "" + ? "expected_a" + : "expected_a_b" + ), + line, + column - 1, + match, + char + ); + } + if (source_line) { + char = source_line[0]; + source_line = source_line.slice(1); + snippet += char; + } else { + char = ""; + snippet += " "; + } + column += 1; + return char; + } + + function back_char() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the source_line. + + if (snippet) { + char = snippet.slice(-1); + source_line = char + source_line; + column -= 1; + snip(); + } else { + char = ""; + } + return char; + } + + function some_digits(rx, quiet) { + const result = source_line.match(rx); + if (result) { + char = result[1]; + column += char.length; + source_line = result[2]; + snippet += char; + } else { + char = ""; + if (!quiet) { + warn_at( + "expected_digits_after_a", + line, + column, + snippet + ); + } + } + return char.length; + } + + function escape(extra) { + next_char("\\"); + if (escapeable[char] === true) { + return next_char(); + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "u") { + if (next_char("u") === "{") { + if (json_mode) { + warn_at("unexpected_a", line, column - 1, char); + } + if (some_digits(rx_hexs) > 5) { + warn_at("too_many_digits", line, column - 1); + } + if (next_char() !== "}") { + stop_at("expected_a_before_b", line, column, "}", char); + } + return next_char(); + } + back_char(); + if (some_digits(rx_hexs, true) < 4) { + warn_at("expected_four_digits", line, column - 1); + } + return; + } + if (extra && extra.indexOf(char) >= 0) { + return next_char(); + } + warn_at("unexpected_a_before_b", line, column - 2, "\\", char); + } + + function make(id, value, identifier) { + +// Make the token object and append it to the tokens list. + + const the_token = { + from: from, + id: id, + identifier: Boolean(identifier), + line: line, + nr: nr, + thru: column + }; + tokens[nr] = the_token; + nr += 1; + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + directive_mode = false; + } + +// If the token is to have a value, give it one. + + if (value !== undefined) { + the_token.value = value; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option.white. + + if ( + previous.line === line + && previous.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (previous.id === "(comment)" || previous.id === "(regexp)") + ) { + warn( + "expected_space_a_b", + the_token, + artifact(previous), + artifact(the_token) + ); + } + if (previous.id === "." && id === "(number)") { + warn("expected_a_before_b", previous, "0", "."); + } + if (prior.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// The previous token is used to detect adjacency problems. + + previous = the_token; + +// The prior token is a previous token that was not a comment. The prior token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (previous.id !== "(comment)") { + prior = previous; + } + return the_token; + } + + function parse_directive(the_comment, body) { + +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + + const result = body.match(rx_directive_part); + if (result) { + let allowed; + const name = result[1]; + const value = result[2]; + if (the_comment.directive === "jslint") { + allowed = allowed_option[name]; + if ( + typeof allowed === "boolean" + || typeof allowed === "object" + ) { + if ( + value === "" + || value === "true" + || value === undefined + ) { + option[name] = true; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } else if (value === "false") { + option[name] = false; + } else { + warn("bad_option_a", the_comment, name + ":" + value); + } + } else { + warn("bad_option_a", the_comment, name); + } + } else if (the_comment.directive === "property") { + if (tenure === undefined) { + tenure = empty(); + } + tenure[name] = true; + } else if (the_comment.directive === "global") { + if (value) { + warn("bad_option_a", the_comment, name + ":" + value); + } + declared_globals[name] = false; + module_mode = the_comment; + } + return parse_directive(the_comment, result[3]); + } + if (body) { + return stop("bad_directive_a", the_comment, body); + } + } + + function comment(snippet) { + +// Make a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + + const the_comment = make("(comment)", snippet); + if (Array.isArray(snippet)) { + snippet = snippet.join(" "); + } + if (!option.devel && rx_todo.test(snippet)) { + warn("todo_comment", the_comment); + } + const result = snippet.match(rx_directive); + if (result) { + if (!directive_mode) { + warn_at("misplaced_directive_a", line, from, result[1]); + } else { + the_comment.directive = result[1]; + parse_directive(the_comment, result[2]); + } + directives.push(the_comment); + } + return the_comment; + } + + function regexp() { + +// Parse a regular expression literal. + + let multi_mode = false; + let result; + let value; + + function quantifier() { + +// Match an optional quantifier. + + if (char === "?" || char === "*" || char === "+") { + next_char(); + } else if (char === "{") { + if (some_digits(rx_digits, true) === 0) { + warn_at("expected_a", line, column, "0"); + } + if (next_char() === ",") { + some_digits(rx_digits, true); + next_char(); + } + next_char("}"); + } else { + return; + } + if (char === "?") { + next_char("?"); + } + } + + function subklass() { + +// Match a character in a character class. + + if (char === "\\") { + escape("BbDdSsWw-[]^"); + return true; + } + if ( + char === "" + || char === "[" + || char === "]" + || char === "/" + || char === "^" + || char === "-" + ) { + return false; + } + if (char === " ") { + warn_at("expected_a_b", line, column, "\\u0020", " "); + } else if (char === "`" && mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char(); + return true; + } + + function ranges() { + +// Match a range of subclasses. + + if (subklass()) { + if (char === "-") { + next_char("-"); + if (!subklass()) { + return stop_at( + "unexpected_a", + line, + column - 1, + "-" + ); + } + } + return ranges(); + } + } + + function klass() { + +// Match a class. + + next_char("["); + if (char === "^") { + next_char("^"); + } + (function classy() { + ranges(); + if (char !== "]" && char !== "") { + warn_at( + "expected_a_before_b", + line, + column, + "\\", + char + ); + next_char(); + return classy(); + } + }()); + next_char("]"); + } + + function choice() { + + function group() { + +// Match a group that starts with left paren. + + next_char("("); + if (char === "?") { + next_char("?"); + if (char === "=" || char === "!") { + next_char(); + } else { + next_char(":"); + } + } else if (char === ":") { + warn_at("expected_a_before_b", line, column, "?", ":"); + } + choice(); + next_char(")"); + } + + function factor() { + if ( + char === "" + || char === "/" + || char === "]" + || char === ")" + ) { + return false; + } + if (char === "(") { + group(); + return true; + } + if (char === "[") { + klass(); + return true; + } + if (char === "\\") { + escape("BbDdSsWw^${}[]():=!.-|*+?"); + return true; + } + if ( + char === "?" + || char === "+" + || char === "*" + || char === "}" + || char === "{" + ) { + warn_at( + "expected_a_before_b", + line, + column - 1, + "\\", + char + ); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column - 1, "`"); + } + } else if (char === " ") { + warn_at( + "expected_a_b", + line, + column - 1, + "\\s", + " " + ); + } else if (char === "$") { + if (source_line[0] !== "/") { + multi_mode = true; + } + } else if (char === "^") { + if (snippet !== "^") { + multi_mode = true; + } + } + next_char(); + return true; + } + + function sequence(follow) { + if (factor()) { + quantifier(); + return sequence(true); + } + if (!follow) { + warn_at("expected_regexp_factor_a", line, column, char); + } + } + +// Match a choice (a sequence that can be followed by | and another choice). + + sequence(); + if (char === "|") { + next_char("|"); + return choice(); + } + } + +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + next_char(); + if (char === "=") { + warn_at("expected_a_before_b", line, column, "\\", "="); + } + choice(); + +// Make sure there is a closing slash. + + snip(); + value = snippet; + next_char("/"); + +// Process dangling flag letters. + + const allowed = { + g: true, + i: true, + m: true, + u: true, + y: true + }; + const flag = empty(); + (function make_flag() { + if (is_letter(char)) { + if (allowed[char] !== true) { + warn_at("unexpected_a", line, column, char); + } + allowed[char] = false; + flag[char] = true; + next_char(); + return make_flag(); + } + }()); + back_char(); + if (char === "/" || char === "*") { + return stop_at("unexpected_a", line, from, char); + } + result = make("(regexp)", char); + result.flag = flag; + result.value = value; + if (multi_mode && !flag.m) { + warn_at("missing_m", line, column); + } + return result; + } + + function string(quote) { + +// Make a string token. + + let the_token; + snippet = ""; + next_char(); + + return (function next() { + if (char === quote) { + snip(); + the_token = make("(string)", snippet); + the_token.quote = quote; + return the_token; + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "\\") { + escape(quote); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char("`"); + } else { + next_char(); + } + return next(); + }()); + } + + function frack() { + if (char === ".") { + some_digits(rx_digits); + next_char(); + } + if (char === "E" || char === "e") { + next_char(); + if (char !== "+" && char !== "-") { + back_char(); + } + some_digits(rx_digits); + next_char(); + } + } + + function number() { + if (snippet === "0") { + next_char(); + if (char === ".") { + frack(); + } else if (char === "b") { + some_digits(rx_bits); + next_char(); + } else if (char === "o") { + some_digits(rx_octals); + next_char(); + } else if (char === "x") { + some_digits(rx_hexs); + next_char(); + } + } else { + next_char(); + frack(); + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + return stop_at( + "unexpected_a_after_b", + line, + column - 1, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + back_char(); + return make("(number)", snippet); + } + + function lex() { + let array; + let i = 0; + let j = 0; + let last; + let result; + let the_token; + if (!source_line) { + source_line = next_line(); + from = 0; + return ( + source_line === undefined + ? ( + mega_mode + ? stop_at("unclosed_mega", mega_line, mega_from) + : make("(end)") + ) + : lex() + ); + } + from = column; + result = source_line.match(rx_token); + +// result[1] token +// result[2] whitespace +// result[3] identifier +// result[4] number +// result[5] rest + + if (!result) { + return stop_at( + "unexpected_char_a", + line, + column, + source_line[0] + ); + } + + snippet = result[1]; + column += snippet.length; + source_line = result[5]; + +// Whitespace was matched. Call lex again to get more. + + if (result[2]) { + return lex(); + } + +// The token is an identifier. + + if (result[3]) { + return make(snippet, undefined, true); + } + +// The token is a number. + + if (result[4]) { + return number(snippet); + } + +// The token is a string. + + if (snippet === "\"") { + return string(snippet); + } + if (snippet === "'") { + if (!option.single) { + warn_at("use_double", line, column); + } + return string(snippet); + } + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (snippet === "`") { + if (mega_mode) { + return stop_at("expected_a_b", line, column, "}", "`"); + } + snippet = ""; + mega_from = from; + mega_line = line; + mega_mode = true; + +// Parsing a mega literal is tricky. First make a ` token. + + make("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + (function part() { + const at = source_line.search(rx_mega); + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + if (at < 0) { + snippet += source_line + "\n"; + return ( + next_line() === undefined + ? stop_at("unclosed_mega", mega_line, mega_from) + : part() + ); + } + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + snippet += source_line.slice(0, at); + column += at; + source_line = source_line.slice(at); + if (source_line[0] === "\\") { + stop_at("escape_mega", line, at); + } + make("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then make tokens that will become part of an expression until +// a } token is made. + + if (source_line[0] === "$") { + column += 2; + make("${"); + source_line = source_line.slice(2); + (function expr() { + const id = lex().id; + if (id === "{") { + return stop_at( + "expected_a_b", + line, + column, + "}", + "{" + ); + } + if (id !== "}") { + return expr(); + } + }()); + return part(); + } + }()); + source_line = source_line.slice(1); + column += 1; + mega_mode = false; + return make("`"); + } + +// The token is a // comment. + + if (snippet === "//") { + snippet = source_line; + source_line = ""; + the_token = comment(snippet); + if (mega_mode) { + warn("unexpected_comment", the_token, "`"); + } + return the_token; + } + +// The token is a /* comment. + + if (snippet === "/*") { + array = []; + if (source_line[0] === "/") { + warn_at("unexpected_a", line, column + i, "/"); + } + (function next() { + if (source_line > "") { + i = source_line.search(rx_star_slash); + if (i >= 0) { + return; + } + j = source_line.search(rx_slash_star); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + } + array.push(source_line); + source_line = next_line(); + if (source_line === undefined) { + return stop_at("unclosed_comment", line, column); + } + return next(); + }()); + snippet = source_line.slice(0, i); + j = snippet.search(rx_slash_star_or_slash); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + array.push(snippet); + column += i + 2; + source_line = source_line.slice(i + 2); + return comment(array); + } + +// The token is a slash. + + if (snippet === "/") { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + if (prior.identifier) { + if (!prior.dot) { + if (prior.id === "return") { + return regexp(); + } + if ( + prior.id === "(begin)" + || prior.id === "case" + || prior.id === "delete" + || prior.id === "in" + || prior.id === "instanceof" + || prior.id === "new" + || prior.id === "typeof" + || prior.id === "void" + || prior.id === "yield" + ) { + the_token = regexp(); + return stop("unexpected_a", the_token); + } + } + } else { + last = prior.id[prior.id.length - 1]; + if ("(,=:?[".indexOf(last) >= 0) { + return regexp(); + } + if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) { + the_token = regexp(); + warn("wrap_regexp", the_token); + return the_token; + } + } + if (source_line[0] === "/") { + column += 1; + source_line = source_line.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + } + return make(snippet); + } + + first = lex(); + json_mode = first.id === "{" || first.id === "["; + +// This is the only loop in JSLint. It will turn into a recursive call to lex +// when ES6 has been finished and widely deployed and adopted. + + while (true) { + if (lex().id === "(end)") { + break; + } + } +} + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + +function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!rx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!rx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property[id] === "number") { + property[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (tenure !== undefined) { + if (tenure[id] !== true) { + warn("unregistered_property_a", name); + } + } else { + if (name.identifier && rx_bad_property.test(id)) { + warn("bad_property_a", name); + } + } + property[id] = 1; + } + return id; +} + +function dispense() { + +// Deliver the next token, skipping the comments. + + const cadet = tokens[token_nr]; + token_nr += 1; + if (cadet.id === "(comment)") { + if (json_mode) { + warn("unexpected_a", cadet); + } + return dispense(); + } else { + return cadet; + } +} + +function lookahead() { + +// Look ahead one token without advancing. + + const old_token_nr = token_nr; + const cadet = dispense(true); + token_nr = old_token_nr; + return cadet; +} + +function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if (token.identifier && token.id !== "function") { + anon = token.id; + } else if (token.id === "(string)" && rx_identifier.test(token.value)) { + anon = token.value; + } + +// Attempt to match next_token with an expected id. + + if (id !== undefined && next_token.id !== id) { + return ( + match === undefined + ? stop("expected_a_b", next_token, id, artifact()) + : stop( + "expected_a_b_from_c_d", + next_token, + id, + artifact(match), + artifact_line(match), + artifact(next_token) + ) + ); + } + +// Promote the tokens, skipping comments. + + token = next_token; + next_token = dispense(); + if (next_token.id === "(end)") { + token_nr -= 1; + } +} + +// Parsing of JSON is simple: + +function json_value() { + let negative; + if (next_token.id === "{") { + return (function json_object() { + const brace = next_token; + const object = empty(); + const properties = []; + brace.expression = properties; + advance("{"); + if (next_token.id !== "}") { + (function next() { + let name; + let value; + if (next_token.quote !== "\"") { + warn( + "unexpected_a", + next_token, + next_token.quote + ); + } + name = next_token; + advance("(string)"); + if (object[token.value] !== undefined) { + warn("duplicate_a", token); + } else if (token.value === "__proto__") { + warn("bad_property_a", token); + } else { + object[token.value] = token; + } + advance(":"); + value = json_value(); + value.label = name; + properties.push(value); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("}", brace); + return brace; + }()); + } + if (next_token.id === "[") { + return (function json_array() { + const bracket = next_token; + const elements = []; + bracket.expression = elements; + advance("["); + if (next_token.id !== "]") { + (function next() { + elements.push(json_value()); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]", bracket); + return bracket; + }()); + } + if ( + next_token.id === "true" + || next_token.id === "false" + || next_token.id === "null" + ) { + advance(); + return token; + } + if (next_token.id === "(number)") { + if (!rx_JSON_number.test(next_token.value)) { + warn("unexpected_a"); + } + advance(); + return token; + } + if (next_token.id === "(string)") { + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + advance(); + return token; + } + if (next_token.id === "-") { + negative = next_token; + negative.arity = "unary"; + advance("-"); + advance("(number)"); + negative.expression = token; + return negative; + } + stop("unexpected_a"); +} + +// Now we parse JavaScript. + +function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + const id = name.id; + +// Reserved words may not be enrolled. + + if (syntax[id] !== undefined && id !== "ignore") { + warn("reserved_a", name); + } else { + +// Has the name been enrolled in this context? + + let earlier = functionage.context[id]; + if (earlier) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + +// Has the name been enrolled in an outer context? + + } else { + stack.forEach(function (value) { + const item = value.context[id]; + if (item !== undefined) { + earlier = item; + } + }); + if (earlier) { + if (id === "ignore") { + if (earlier.role === "variable") { + warn("unexpected_a", name); + } + } else { + if ( + ( + role !== "exception" + || earlier.role !== "exception" + ) + && role !== "parameter" + && role !== "function" + ) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + } + } + } + +// Enroll it. + + functionage.context[id] = name; + name.dead = true; + name.parent = functionage; + name.init = false; + name.role = role; + name.used = 0; + name.writable = !readonly; + } + } +} + +function expression(rbp, initial) { + +// This is the heart of the Pratt parser. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// nud Null denotation +// led Left denotation +// lbp Left binding power +// rbp Right binding power + +// It processes a nud (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop. It +// returns the expression's parse tree. + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement, + + if (!initial) { + advance(); + } + the_symbol = syntax[token.id]; + if (the_symbol !== undefined && the_symbol.nud !== undefined) { + left = the_symbol.nud(); + } else if (token.identifier) { + left = token; + left.arity = "variable"; + } else { + return stop("unexpected_a", token); + } + (function right() { + the_symbol = syntax[next_token.id]; + if ( + the_symbol !== undefined + && the_symbol.led !== undefined + && rbp < the_symbol.lbp + ) { + advance(); + left = the_symbol.led(left); + return right(); + } + }()); + return left; +} + +function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = next_token; + let the_value; + the_paren.free = true; + advance("("); + the_value = expression(0); + advance(")"); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (anticondition[the_value.id] === true) { + warn("unexpected_a", the_value); + } + return the_value; +} + +function is_weird(thing) { + return ( + thing.id === "(regexp)" + || thing.id === "{" + || thing.id === "=>" + || thing.id === "function" + || (thing.id === "[" && thing.arity === "unary") + ); +} + +function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + return ( + Array.isArray(b) + && a.length === b.length + && a.every(function (value, index) { + return are_similar(value, b[index]); + }) + ); + } + if (Array.isArray(b)) { + return false; + } + if (a.id === "(number)" && b.id === "(number)") { + return a.value === b.value; + } + let a_string; + let b_string; + if (a.id === "(string)") { + a_string = a.value; + } else if (a.id === "`" && a.constant) { + a_string = a.value[0]; + } + if (b.id === "(string)") { + b_string = b.value; + } else if (b.id === "`" && b.constant) { + b_string = b.value[0]; + } + if (typeof a_string === "string") { + return a_string === b_string; + } + if (is_weird(a) || is_weird(b)) { + return false; + } + if (a.arity === b.arity && a.id === b.id) { + if (a.id === ".") { + return ( + are_similar(a.expression, b.expression) + && are_similar(a.name, b.name) + ); + } + if (a.arity === "unary") { + return are_similar(a.expression, b.expression); + } + if (a.arity === "binary") { + return ( + a.id !== "(" + && are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + ); + } + if (a.arity === "ternary") { + return ( + are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + && are_similar(a.expression[2], b.expression[2]) + ); + } + if (a.arity === "function" && a.arity === "regexp") { + return false; + } + return true; + } + return false; +} + +function semicolon() { + +// Try to match a semicolon. + + if (next_token.id === ";") { + advance(";"); + } else { + warn_at( + "expected_a_b", + token.line, + token.thru, + ";", + artifact(next_token) + ); + } + anon = "anonymous"; +} + +function statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token.identifier && next_token.id === ":") { + the_label = token; + if (the_label.id === "ignore") { + warn("unexpected_a", the_label); + } + advance(":"); + if ( + next_token.id === "do" + || next_token.id === "for" + || next_token.id === "switch" + || next_token.id === "while" + ) { + enroll(the_label, "label", true); + the_label.init = true; + the_label.dead = false; + the_statement = statement(); + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token; + first.statement = true; + the_symbol = syntax[first.id]; + if (the_symbol !== undefined && the_symbol.fud !== undefined) { + the_symbol.disrupt = false; + the_symbol.statement = true; + the_statement = the_symbol.fud(); + } else { + +// It is an expression statement. + + the_statement = expression(0, true); + if (the_statement.wrapped && the_statement.id !== "(") { + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; +} + +function statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const array = []; + (function next(disrupt) { + if ( + next_token.id !== "}" + && next_token.id !== "case" + && next_token.id !== "default" + && next_token.id !== "else" + && next_token.id !== "(end)" + ) { + let a_statement = statement(); + array.push(a_statement); + if (disrupt) { + warn("unreachable_a", a_statement); + } + return next(a_statement.disrupt); + } + }(false)); + return array; +} + +function not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === global) { + warn("unexpected_at_top_level_a", thing); + } +} + +function top_level_only(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== global) { + warn("misplaced_a", the_thing); + } +} + +function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token; + the_block.arity = "statement"; + the_block.body = special === "body"; + +// All top level function bodies should include the "use strict" pragma unless +// the whole file is strict or the file is a module or the function parameters +// use es6 syntax. + + if ( + special === "body" + && stack.length === 1 + && next_token.value === "use strict" + ) { + the_block.strict = next_token; + next_token.statement = true; + advance("(string)"); + advance(";"); + } + stmts = statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option.devel && special !== "ignore") { + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; +} + +function mutation_check(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + warn("bad_assignment_a", the_thing); + return false; + } + return true; +} + +function left_check(left, right) { + +// Warn if the left is not one of these: +// e.b +// e[b] +// e() +// ?: +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !left_check(left.expression[1]) + && !left_check(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right); + return false; + } + return true; +} + +// These functions are used to specify the grammar of our language: + +function symbol(id, bp) { + +// Make a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax[id] = the_symbol; + } + return the_symbol; +} + +function assignment(id) { + +// Make an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led = function (left) { + const the_token = token; + let right; + the_token.arity = "assignment"; + right = expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "pre" + || right.arity === "post" + ) { + warn("unexpected_a", right); + } + mutation_check(left); + return the_token; + }; + return the_symbol; +} + +function constant(id, type, value) { + +// Make a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud = ( + typeof value === "function" + ? value + : function () { + token.constant = true; + if (value !== undefined) { + token.value = value; + } + return token; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; +} + +function infix(id, bp, f) { + +// Make an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, expression(bp)]; + return the_token; + }; + return the_symbol; +} + +function infixr(id, bp) { + +// Make a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + the_token.expression = [left, expression(bp - 1)]; + return the_token; + }; + return the_symbol; +} + +function post(id) { + +// Make one of the post operators. + + const the_symbol = symbol(id, 150); + the_symbol.led = function (left) { + token.expression = left; + token.arity = "post"; + mutation_check(token.expression); + return token; + }; + return the_symbol; +} + +function pre(id) { + +// Make one of the pre operators. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "pre"; + the_token.expression = expression(150); + mutation_check(the_token.expression); + return the_token; + }; + return the_symbol; +} + +function prefix(id, f) { + +// Make a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = expression(150); + return the_token; + }; + return the_symbol; +} + +function stmt(id, f) { + +// Make a statement. + + const the_symbol = symbol(id); + the_symbol.fud = function () { + token.arity = "statement"; + return f(); + }; + return the_symbol; +} + +function ternary(id1, id2) { + +// Make a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led = function (left) { + const the_token = token; + const second = expression(20); + advance(id2); + token.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, expression(10)]; + return the_token; + }; + return the_symbol; +} + +// Begin defining the language. + +syntax = empty(); + +symbol("}"); +symbol(")"); +symbol("]"); +symbol(","); +symbol(";"); +symbol(":"); +symbol("*/"); +symbol("await"); +symbol("case"); +symbol("catch"); +symbol("class"); +symbol("default"); +symbol("else"); +symbol("enum"); +symbol("finally"); +symbol("implements"); +symbol("interface"); +symbol("package"); +symbol("private"); +symbol("protected"); +symbol("public"); +symbol("static"); +symbol("super"); +symbol("void"); +symbol("yield"); + +constant("(number)", "number"); +constant("(regexp)", "regexp"); +constant("(string)", "string"); +constant("arguments", "object", function () { + warn("unexpected_a", token); + return token; +}); +constant("eval", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("false", "boolean", false); +constant("Function", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("ignore", "undefined", function () { + warn("unexpected_a", token); + return token; +}); +constant("Infinity", "number", Infinity); +constant("isFinite", "function", function () { + warn("expected_a_b", token, "Number.isFinite", "isFinite"); + return token; +}); +constant("isNaN", "function", function () { + warn("number_isNaN", token); + return token; +}); +constant("NaN", "number", NaN); +constant("null", "null", null); +constant("this", "object", function () { + if (!option.this) { + warn("unexpected_a", token); + } + return token; +}); +constant("true", "boolean", true); +constant("undefined", "undefined"); + +assignment("="); +assignment("+="); +assignment("-="); +assignment("*="); +assignment("/="); +assignment("%="); +assignment("&="); +assignment("|="); +assignment("^="); +assignment("<<="); +assignment(">>="); +assignment(">>>="); + +infix("||", 40); +infix("&&", 50); +infix("|", 70); +infix("^", 80); +infix("&", 90); +infix("==", 100); +infix("===", 100); +infix("!=", 100); +infix("!==", 100); +infix("<", 110); +infix(">", 110); +infix("<=", 110); +infix(">=", 110); +infix("in", 110); +infix("instanceof", 110); +infix("<<", 120); +infix(">>", 120); +infix(">>>", 120); +infix("+", 130); +infix("-", 130); +infix("*", 140); +infix("/", 140); +infix("%", 140); +infixr("**", 150); +infix("(", 160, function (left) { + const the_paren = token; + let the_argument; + if (left.id !== "function") { + left_check(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (next_token.id !== ")") { + (function next() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + the_paren.free = true; + if (the_argument.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + the_paren.free = false; + } + return the_paren; +}); +infix(".", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("[", 170, function (left) { + const the_token = token; + const the_subscript = expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + const name = survey(the_subscript); + if (rx_identifier.test(name)) { + warn("subscript_a", the_subscript, name); + } + } + left_check(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; +}); +infix("=>", 170, function (left) { + return stop("wrap_parameter", left); +}); + +function do_tick() { + const the_tick = token; + the_tick.value = []; + the_tick.expression = []; + if (next_token.id !== "`") { + (function part() { + advance("(string)"); + the_tick.value.push(token); + if (next_token.id === "${") { + advance("${"); + the_tick.expression.push(expression(0)); + advance("}"); + return part(); + } + }()); + } + advance("`"); + return the_tick; +} + +infix("`", 160, function (left) { + const the_tick = do_tick(); + left_check(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; +}); + +post("++"); +post("--"); +pre("++"); +pre("--"); + +prefix("+"); +prefix("-"); +prefix("~"); +prefix("!"); +prefix("!!"); +prefix("[", function () { + const the_token = token; + the_token.expression = []; + if (next_token.id !== "]") { + (function next() { + let element; + let ellipsis = false; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + element = expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]"); + return the_token; +}); +prefix("/=", function () { + stop("expected_a_b", token, "/\\=", "/="); +}); +prefix("=>", function () { + return stop("expected_a_before_b", token, "()", "=>"); +}); +prefix("new", function () { + const the_new = token; + const right = expression(160); + if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "()", artifact(next_token)); + } + the_new.expression = right; + return the_new; +}); +prefix("typeof"); +prefix("void", function () { + const the_void = token; + warn("unexpected_a", the_void); + the_void.expression = expression(0); + return the_void; +}); + +function parameter_list() { + let complex = false; + const list = []; + let optional; + const signature = ["("]; + if (next_token.id !== ")" && next_token.id !== "(end)") { + (function parameter() { + let ellipsis = false; + let param; + if (next_token.id === "{") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("{"); + signature.push("{"); + (function subparameter() { + let subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (next_token.id === ":") { + advance(":"); + advance(); + token.label = subparam; + subparam = token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + } + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return subparameter(); + } + }()); + list.push(param); + advance("}"); + signature.push("}"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else if (next_token.id === "[") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("["); + signature.push("[]"); + (function subparameter() { + const subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + return subparameter(); + } + }()); + list.push(param); + advance("]"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else { + if (next_token.id === "...") { + complex = true; + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + } + if (!next_token.identifier) { + return stop("expected_identifier_a"); + } + param = next_token; + list.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (next_token.id === "=") { + complex = true; + optional = param; + advance("="); + param.expression = expression(0); + } else { + if (optional !== undefined) { + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } + } + }()); + } + advance(")"); + signature.push(")"); + return [list, signature.join(""), complex]; +} + +function do_function(the_function) { + let name; + if (the_function === undefined) { + the_function = token; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + name = next_token; + enroll(name, "variable", true); + the_function.name = name; + name.init = true; + name.calls = empty(); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + if (next_token.identifier) { + name = next_token; + the_function.name = name; + advance(); + } else { + the_function.name = anon; + } + } + } else { + name = the_function.name; + } + the_function.level = functionage.level + 1; + if (mega_mode) { + warn("unexpected_a", the_function); + } + +// Don't make functions in loops. It is inefficient, and it can lead to scoping +// errors. + + if (functionage.loop > 0) { + warn("function_in_loop", the_function); + } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + the_function.context = empty(); + the_function.finally = 0; + the_function.loop = 0; + the_function.switch = 0; + the_function.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functions.push(the_function); + functionage = the_function; + if (the_function.arity !== "statement" && typeof name === "object") { + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// Parse the parameter list. + + advance("("); + token.free = false; + token.arity = "function"; + const pl = parameter_list(); + functionage.parameters = pl[0]; + functionage.signature = pl[1]; + functionage.complex = pl[2]; + functionage.parameters.forEach(function enroll_parameter(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + name.names.forEach(enroll_parameter); + } + }); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && next_token.line === token.line + ) { + return stop("unexpected_a", next_token); + } + if (next_token.id === "." || next_token.id === "[") { + warn("unexpected_a"); + } + +// Restore the previous context. + + functionage = stack.pop(); + return the_function; +} + +prefix("function", do_function); + +function fart(pl) { + if (next_token.id === ";") { + stop("wrap_assignment", token); + } + advance("=>"); + const the_fart = token; + the_fart.arity = "binary"; + the_fart.name = "=>"; + the_fart.level = functionage.level + 1; + functions.push(the_fart); + if (functionage.loop > 0) { + warn("function_in_loop", the_fart); + } + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + the_fart.context = empty(); + the_fart.finally = 0; + the_fart.loop = 0; + the_fart.switch = 0; + the_fart.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functionage = the_fart; + the_fart.parameters = pl[0]; + the_fart.signature = pl[1]; + the_fart.complex = true; + the_fart.parameters.forEach(function (name) { + enroll(name, "parameter", true); + }); + if (next_token.id === "{") { + warn("expected_a_b", the_fart, "function", "=>"); + the_fart.block = block("body"); + } else { + the_fart.expression = expression(0); + } + functionage = stack.pop(); + return the_fart; +} + +prefix("(", function () { + const the_paren = token; + let the_value; + const cadet = lookahead().id; + +// We can distinguish between a parameter list for => and a wrapped expression +// with one token of lookahead. + + if ( + next_token.id === ")" + || next_token.id === "..." + || (next_token.identifier && (cadet === "," || cadet === "=")) + ) { + the_paren.free = false; + return fart(parameter_list()); + } + the_paren.free = true; + the_value = expression(0); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + if (next_token.id === "=>") { + if (the_value.arity !== "variable") { + if (the_value.id === "{" || the_value.id === "[") { + warn("expected_a_before_b", the_paren, "function", "("); + return stop("expected_a_b", next_token, "{", "=>"); + } + return stop("expected_identifier_a", the_value); + } + the_paren.expression = [the_value]; + return fart([the_paren.expression, "(" + the_value.id + ")"]); + } + return the_value; +}); +prefix("`", do_tick); +prefix("{", function () { + const the_brace = token; + const seen = empty(); + the_brace.expression = []; + if (next_token.id !== "}") { + (function member() { + let extra; + let full; + let id; + let name = next_token; + let value; + advance(); + if ( + (name.id === "get" || name.id === "set") + && next_token.identifier + ) { + if (!option.getset) { + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + next_token.id; + name = next_token; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (next_token.id === "}" || next_token.id === ",") { + if (typeof extra === "string") { + advance("("); + } + value = expression(Infinity, true); + } else if (next_token.id === "(") { + value = do_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + advance("("); + } + advance(":"); + value = expression(0); + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + advance(":"); + value = expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (next_token.id === ",") { + advance(","); + return member(); + } + }()); + } + advance("}"); + return the_brace; +}); + +stmt(";", function () { + warn("unexpected_a", token); + return token; +}); +stmt("{", function () { + warn("naked_block", token); + return block("naked"); +}); +stmt("break", function () { + const the_break = token; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (next_token.identifier && token.line === next_token.line) { + the_label = functionage.context[next_token.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + warn( + (the_label !== undefined && the_label.dead) + ? "out_of_scope_a" + : "not_label_a" + ); + } else { + the_label.used += 1; + } + the_break.label = next_token; + advance(); + } + advance(";"); + return the_break; +}); + +function do_var() { + const the_statement = token; + const is_const = the_statement.id === "const"; + the_statement.names = []; + +// A program may use var or let, but not both. + + if (!is_const) { + if (var_mode === undefined) { + var_mode = the_statement.id; + } else if (the_statement.id !== var_mode) { + warn( + "expected_a_b", + the_statement, + var_mode, + the_statement.id + ); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + warn("var_switch", the_statement); + } + if (functionage.loop > 0 && the_statement.id === "var") { + warn("var_loop", the_statement); + } + (function next() { + if (next_token.id === "{" && the_statement.id !== "var") { + const the_brace = next_token; + the_brace.names = []; + advance("{"); + (function pair() { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + survey(name); + advance(); + if (next_token.id === ":") { + advance(":"); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + next_token.label = name; + the_brace.names.push(next_token); + enroll(next_token, "variable", is_const); + advance(); + } else { + the_brace.names.push(name); + enroll(name, "variable", is_const); + } + if (next_token.id === ",") { + advance(","); + return pair(); + } + }()); + advance("}"); + advance("="); + the_brace.expression = expression(0); + the_statement.names.push(the_brace); + } else if (next_token.id === "[" && the_statement.id !== "var") { + const the_bracket = next_token; + the_bracket.names = []; + advance("["); + (function element() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + advance(); + the_bracket.names.push(name); + enroll(name, "variable", the_statement.id === "const"); + if (ellipsis) { + name.ellipsis = true; + } else if (next_token.id === ",") { + advance(","); + return element(); + } + }()); + advance("]"); + advance("="); + the_bracket.expression = expression(0); + the_statement.names.push(the_bracket); + } else if (next_token.identifier) { + const name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", is_const); + if (next_token.id === "=" || is_const) { + advance("="); + name.init = true; + name.expression = expression(0); + } + the_statement.names.push(name); + } else { + return stop("expected_identifier_a", next_token); + } + if (next_token.id === ",") { + if (!option.multivar) { + warn("expected_a_b", next_token, ";", ","); + } + advance(","); + return next(); + } + }()); + the_statement.open = ( + the_statement.names.length > 1 + && the_statement.line !== the_statement.names[1].line + ); + semicolon(); + return the_statement; +} + +stmt("const", do_var); +stmt("continue", function () { + const the_continue = token; + if (functionage.loop < 1 || functionage.finally > 0) { + warn("unexpected_a", the_continue); + } + not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; +}); +stmt("debugger", function () { + const the_debug = token; + if (!option.devel) { + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; +}); +stmt("delete", function () { + const the_token = token; + const the_value = expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; +}); +stmt("do", function () { + const the_do = token; + not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; +}); +stmt("export", function () { + const the_export = token; + let the_id; + let the_name; + let the_thing; + + function export_id() { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + the_id = next_token.id; + the_name = global.context[the_id]; + if (the_name === undefined) { + warn("unexpected_a"); + } else { + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a"); + } + exports[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + } + + the_export.expression = []; + if (next_token.id === "default") { + if (exports.default !== undefined) { + warn("duplicate_a"); + } + advance("default"); + the_thing = expression(0); + semicolon(); + exports.default = the_thing; + the_export.expression.push(the_thing); + } else { + if (next_token.id === "function") { + the_thing = statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a", the_name); + } + exports[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + warn("unexpected_a"); + } else if (next_token.id === "{") { + advance("{"); + (function loop() { + export_id(); + if (next_token.id === ",") { + advance(","); + return loop(); + } + }()); + advance("}"); + semicolon(); + } else { + export_id(); + if (the_name.writable !== true) { + warn("unexpected_a", token); + } + semicolon(); + } + } + module_mode = true; + return the_export; +}); +stmt("for", function () { + let first; + const the_for = token; + if (!option.for) { + warn("unexpected_a", the_for); + } + not_top_level(the_for); + functionage.loop += 1; + advance("("); + token.free = true; + if (next_token.id === ";") { + return stop("expected_a_b", the_for, "while (", "for (;"); + } + if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + return stop("unexpected_a"); + } + first = expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = expression(0); + advance(";"); + the_for.inc = expression(0); + if (the_for.inc.id === "++") { + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; +}); +stmt("function", do_function); +stmt("if", function () { + let the_else; + const the_if = token; + the_if.expression = condition(); + the_if.block = block(); + if (next_token.id === "else") { + advance("else"); + the_else = token; + the_if.else = ( + next_token.id === "if" + ? statement() + : block() + ); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + the_if.disrupt = true; + } else { + warn("unexpected_a", the_else); + } + } + } + return the_if; +}); +stmt("import", function () { + const the_import = token; + let name; + if (typeof module_mode === "object") { + warn("unexpected_directive_a", module_mode, module_mode.directive); + } + module_mode = true; + if (next_token.identifier) { + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + const names = []; + advance("{"); + if (next_token.id !== "}") { + while (true) { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (next_token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token; + if (!rx_module.test(token.value)) { + warn("bad_module_name_a", token); + } + froms.push(token.value); + semicolon(); + return the_import; +}); +stmt("let", do_var); +stmt("return", function () { + const the_return = token; + not_top_level(the_return); + if (functionage.finally > 0) { + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (next_token.id !== ";" && the_return.line === next_token.line) { + the_return.expression = expression(10); + } + advance(";"); + return the_return; +}); +stmt("switch", function () { + let dups = []; + let last; + let stmts; + const the_cases = []; + let the_disrupt = true; + const the_switch = token; + not_top_level(the_switch); + if (functionage.finally > 0) { + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token.free = true; + the_switch.expression = expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + (function major() { + const the_case = next_token; + the_case.arity = "statement"; + the_case.expression = []; + (function minor() { + advance("case"); + token.switch = true; + const exp = expression(0); + if (dups.some(function (thing) { + return are_similar(thing, exp); + })) { + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (next_token.id === "case") { + return minor(); + } + }()); + stmts = statements(); + if (stmts.length < 1) { + warn("expected_statements_a"); + return; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "break;", + artifact(next_token) + ); + } + if (next_token.id === "case") { + return major(); + } + }()); + dups = undefined; + if (next_token.id === "default") { + const the_default = next_token; + advance("default"); + token.switch = true; + advance(":"); + the_switch.else = statements(); + if (the_switch.else.length < 1) { + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + const the_last = the_switch.else[the_switch.else.length - 1]; + if (the_last.id === "break" && the_last.label === undefined) { + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; +}); +stmt("throw", function () { + const the_throw = token; + the_throw.disrupt = true; + the_throw.expression = expression(10); + semicolon(); + if (functionage.try > 0) { + warn("unexpected_a", the_throw); + } + return the_throw; +}); +stmt("try", function () { + let the_catch; + let the_disrupt; + const the_try = token; + if (functionage.try > 0) { + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (next_token.id === "catch") { + let ignored = "ignore"; + the_catch = next_token; + the_try.catch = the_catch; + advance("catch"); + advance("("); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + if (next_token.id !== "ignore") { + ignored = undefined; + the_catch.name = next_token; + enroll(next_token, "exception", true); + } + advance(); + advance(")"); + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "catch", + artifact(next_token) + ); + + } + if (next_token.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; +}); +stmt("var", do_var); +stmt("while", function () { + const the_while = token; + not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; +}); +stmt("with", function () { + stop("unexpected_a", token); +}); + +ternary("?", ":"); + +// Ambulation of the parse tree. + +function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; +} + +function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all of the +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + } + }; +} + +const posts = empty(); +const pres = empty(); +const preaction = action(pres); +const postaction = action(posts); +const preamble = amble(pres); +const postamble = amble(posts); + +function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.id === "function") { + walk_statement(thing.block); + } + if (thing.arity === "pre" || thing.arity === "post") { + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + warn("unexpected_statement_a", thing); + } + postamble(thing); + } + } +} + +function walk_statement(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_statement); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + ) { + warn( + (thing.id === "(string)" && thing.value === "use strict") + ? "unexpected_a" + : "unexpected_expression_a", + thing + ); + } + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + } +} + +function lookup(thing) { + if (thing.arity === "variable") { + +// Look up the variable in the current context. + + let the_variable = functionage.context[thing.id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable === undefined) { + stack.forEach(function (outer) { + const a_variable = outer.context[thing.id]; + if ( + a_variable !== undefined + && a_variable.role !== "label" + ) { + the_variable = a_variable; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (the_variable === undefined) { + if (declared_globals[thing.id] === undefined) { + warn("undeclared_a", thing); + return; + } + the_variable = { + dead: false, + parent: global, + id: thing.id, + init: true, + role: "variable", + used: 0, + writable: false + }; + global.context[thing.id] = the_variable; + } + the_variable.closure = true; + functionage.context[thing.id] = the_variable; + } else if (the_variable.role === "label") { + warn("label_a", thing); + } + if ( + the_variable.dead + && ( + the_variable.calls === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + ) { + warn("out_of_scope_a", thing); + } + return the_variable; + } +} + +function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); +} + +function preaction_function(thing) { + if (thing.arity === "statement" && blockage.body !== true) { + warn("unexpected_a", thing); + } + if (thing.level === 1) { + if ( + module_mode === true + || global.strict !== undefined + || thing.complex + ) { + if (thing.id !== "=>" && thing.block.strict !== undefined) { + warn("unexpected_a", thing.block.strict); + } + } else { + if (thing.block.strict === undefined) { + warn("use_strict", thing); + } + } + } + stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); +} + +function bitwise_check(thing) { + if (!option.bitwise && bitwiseop[thing.id] === true) { + warn("unexpected_a", thing); + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + warn("unexpected_a", thing); + } +} + +function pop_block() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); +} + +function activate(name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.init = true; + } + } + blockage.live.push(name); +} + +function action_var(thing) { + thing.names.forEach(activate); +} + +preaction("assignment", bitwise_check); +preaction("binary", bitwise_check); +preaction("binary", function (thing) { + if (relationop[thing.id] === true) { + const left = thing.expression[0]; + const right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + warn("expected_string_a", right); + } + } else { + const value = right.value; + if (value === "null" || value === "undefined") { + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + warn("expected_type_string_a", right, value); + } + } + } + } +}); +preaction("binary", "==", function (thing) { + warn("expected_a_b", thing, "===", "=="); +}); +preaction("binary", "!=", function (thing) { + warn("expected_a_b", thing, "!==", "!="); +}); +preaction("binary", "=>", preaction_function); +preaction("binary", "||", function (thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + warn("and", thang); + } + }); +}); +preaction("binary", "(", function (thing) { + const left = thing.expression[0]; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + const parent = functionage.name.parent; + if (parent) { + const left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + && left_variable.dead + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } +}); +preaction("binary", "in", function (thing) { + warn("infix_in", thing); +}); +preaction("binary", "instanceof", function (thing) { + warn("unexpected_a", thing); +}); +preaction("binary", ".", function (thing) { + if (thing.expression.new) { + thing.new = true; + } +}); +preaction("statement", "{", function (thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; +}); +preaction("statement", "for", function (thing) { + if (thing.name !== undefined) { + const the_variable = lookup(thing.name); + if (the_variable !== undefined) { + the_variable.init = true; + if (!the_variable.writable) { + warn("bad_assignment_a", thing.name); + } + } + } + walk_statement(thing.initial); +}); +preaction("statement", "function", preaction_function); +preaction("unary", "~", bitwise_check); +preaction("unary", "function", preaction_function); +preaction("variable", function (thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } +}); + +function init_variable(name) { + const the_variable = lookup(name); + if (the_variable !== undefined) { + if (the_variable.writable) { + the_variable.init = true; + return; + } + } + warn("bad_assignment_a", name); +} + +postaction("assignment", "+=", function (thing) { + let right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } +}); +postaction("assignment", function (thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + if (thing.id === "=") { + if (thing.names !== undefined) { + if (Array.isArray(thing.names)) { + thing.names.forEach(init_variable); + } else { + init_variable(thing.names); + } + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.writable !== true) { + warn("bad_assignment_a", lvalue); + } + } + const right = syntax[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + warn("unexpected_a", thing.expression[1]); + } + } +}); + +function postaction_function(thing) { + delete functionage.finally; + delete functionage.loop; + delete functionage.switch; + delete functionage.try; + functionage = stack.pop(); + if (thing.wrapped) { + warn("unexpected_parens", thing); + } + return pop_block(); +} + +postaction("binary", function (thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || are_similar(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option.convert) { + if (thing.expression[0].value === "") { + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === ".") { + if (thing.expression.id === "RegExp") { + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } +}); +postaction("binary", "&&", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "||", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "=>", postaction_function); +postaction("binary", "(", function (thing) { + let left = thing.expression[0]; + let the_new; + let arg; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option.eval) { + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + warn( + "expected_a_before_b", + left, + "new", + artifact(left) + ); + } + } + } else if (left.id === ".") { + let cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + cack = !cack; + } + if (rx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + warn("unexpected_a", the_new); + } else { + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + const paren = left.expression; + if (paren.id === "(") { + const array = paren.expression; + if (array.length === 1) { + const new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } +}); +postaction("binary", "[", function (thing) { + if (thing.expression[0].id === "RegExp") { + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + warn("weird_expression_a", thing.expression[1]); + } +}); +postaction("statement", "{", pop_block); +postaction("statement", "const", action_var); +postaction("statement", "export", top_level_only); +postaction("statement", "for", function (thing) { + walk_statement(thing.inc); +}); +postaction("statement", "function", postaction_function); +postaction("statement", "import", function (the_thing) { + const name = the_thing.name; + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return top_level_only(the_thing); +}); +postaction("statement", "let", action_var); +postaction("statement", "try", function (thing) { + if (thing.catch !== undefined) { + const the_name = thing.catch.name; + if (the_name !== undefined) { + const the_variable = functionage.context[the_name.id]; + the_variable.dead = false; + the_variable.init = true; + } + walk_statement(thing.catch.block); + } +}); +postaction("statement", "var", action_var); +postaction("ternary", function (thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || are_similar(thing.expression[1], thing.expression[2]) + ) { + warn("unexpected_a", thing); + } else if (are_similar(thing.expression[0], thing.expression[1])) { + warn("expected_a_b", thing, "||", "?"); + } else if (are_similar(thing.expression[0], thing.expression[2])) { + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + warn("wrap_condition", thing.expression[0]); + } +}); +postaction("unary", function (thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option.convert) { + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } +}); +postaction("unary", "function", postaction_function); +postaction("unary", "+", function (thing) { + if (!option.convert) { + warn("expected_a_b", thing, "Number(...)", "+"); + } + const right = thing.expression; + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } +}); + +function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + if (id !== "ignore") { + const name = the_function.context[id]; + if (name.parent === the_function) { + if ( + name.used === 0 + && ( + name.role !== "function" + || name.parent.arity !== "unary" + ) + ) { + warn("unused_a", name); + } else if (!name.init) { + warn("uninitialized_a", name); + } + } + } + }); +} + +function uninitialized_and_unused() { + +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (module_mode === true || option.node) { + delve(global); + } + functions.forEach(delve); +} + +// Go through the token list, looking at usage of whitespace. + +function whitage() { + let closer = "(end)"; + let free = false; + let left = global; + let margin = 0; + let nr_comments_skipped = 0; + let open = true; + let right; + + function expected_at(at) { + warn( + "expected_a_at_b_c", + right, + artifact(right), + fudge + at, + artifact_column(right) + ); + } + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function no_space() { + if (left.line === right.line) { + if (left.thru !== right.from && nr_comments_skipped === 0) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (open) { + const at = ( + free + ? margin + : margin + 8 + ); + if (right.from < at) { + expected_at(at); + } + } else { + if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (free) { + if (right.from < margin) { + expected_at(margin); + } + } else { + const mislaid = ( + stack.length > 0 + ? stack[stack.length - 1].right + : undefined + ); + if (!open && mislaid !== undefined) { + warn( + "expected_a_next_at_b", + mislaid, + artifact(mislaid.id), + margin + 4 + fudge + ); + } else if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + stack = []; + tokens.forEach(function (the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + + const new_closer = opener[left.id]; + if (typeof new_closer === "string") { + if (new_closer !== right.id) { + stack.push({ + closer: closer, + free: free, + margin: margin, + open: open, + right: right + }); + closer = new_closer; + if (left.line !== right.line) { + free = closer === ")" && left.free; + open = true; + margin += 4; + if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (right.switch) { + at_margin(-4); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + free = false; + open = false; + no_space_only(); + } + } else { + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like {]) have already been detected. + + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } + } else { + +// If right is a closer, then pop the previous state. + + if (right.id === closer) { + const previous = stack.pop(); + margin = previous.margin; + if (open && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + closer = previous.closer; + free = previous.free; + open = previous.open; + } else { + +// Left is not an opener, and right is not a closer. The nature of left and +// right will determine the space between them. + +// If left is , or ; or right is a statement then if open, right must go at the +// margin, or if closed, a space between. + + + if (right.switch) { + at_margin(-4); + } else if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + one_space(); + } else { + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + at_margin(0); + } else { + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + no_space(); + } else if ( + left.id === "." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + ) { + no_space_only(); + } else if (right.id === ".") { + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } else if (left.id === ";") { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + one_space_only(); + } else if (right.statement === true) { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.id === "var" + || left.id === "const" + || left.id === "let" + ) { + stack.push({ + closer: closer, + free: free, + margin: margin, + open: open + }); + closer = ";"; + free = false; + open = left.open; + if (open) { + margin = margin + 4; + at_margin(0); + } else { + one_space_only(); + } + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +// The jslint function itself. + +export default function jslint(source, option_object, global_array) { + try { + if (typeof source !== "string") { + source = String(source || ""); + } + warnings = []; + option = Object.assign(empty(), option_object); + anon = "anonymous"; + block_stack = []; + declared_globals = empty(); + directive_mode = true; + directives = []; + early_stop = true; + exports = empty(); + froms = []; + fudge = ( + option.fudge + ? 1 + : 0 + ); + functions = []; + global = { + id: "(global)", + body: true, + context: empty(), + from: 0, + level: 0, + line: 0, + live: [], + loop: 0, + switch: 0, + thru: 0 + }; + blockage = global; + functionage = global; + json_mode = false; + mega_mode = false; + module_mode = false; + next_token = global; + property = empty(); + stack = []; + tenure = undefined; + token = global; + token_nr = 0; + var_mode = undefined; + populate(standard, declared_globals, false); + if (global_array !== undefined) { + populate(global_array, declared_globals, false); + } + Object.keys(option).forEach(function (name) { + if (option[name] === true) { + const allowed = allowed_option[name]; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } + }); + tokenize(source); + advance(); + if (json_mode) { + tree = json_value(); + advance("(end)"); + } else { + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option.browser) { + if (next_token.id === ";") { + advance(";"); + } + } else { + +// If we are not in a browser, then the file form of strict pragma may be used. + + if ( + next_token.value === "use strict" + ) { + global.strict = next_token; + advance("(string)"); + advance(";"); + } + } + tree = statements(); + advance("(end)"); + functionage = global; + walk_statement(tree); + if (module_mode && global.strict !== undefined) { + warn("unexpected_a", global.strict); + } + if (warnings.length === 0) { + uninitialized_and_unused(); + if (!option.white) { + whitage(); + } + } + } + if (!option.browser) { + directives.forEach(function (comment) { + if (comment.directive === "global") { + warn("missing_browser", comment); + } + }); + } + early_stop = false; + } catch (e) { + if (e.name !== "JSLintError") { + warnings.push(e); + } + } + return { + directives: directives, + edition: "2018-08-11", + exports: exports, + froms: froms, + functions: functions, + global: global, + id: "(JSLint)", + json: json_mode, + lines: lines, + module: module_mode === true, + ok: warnings.length === 0 && !early_stop, + option: option, + property: property, + stop: early_stop, + tokens: tokens, + tree: tree, + warnings: warnings.sort(function (a, b) { + return a.line - b.line || a.column - b.column; + }) + }; +}; diff --git a/branch.replay_bug_when_source_is_undefined/report.js b/branch.replay_bug_when_source_is_undefined/report.js new file mode 100644 index 000000000..f123e9055 --- /dev/null +++ b/branch.replay_bug_when_source_is_undefined/report.js @@ -0,0 +1,221 @@ +// report.js +// 2018-07-29 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Generate JSLint HTML reports. + +/*property + closure, column, context, edition, error, exports, filter, forEach, froms, + fudge, function, functions, global, id, isArray, join, json, keys, length, + level, line, lines, message, module, name, names, option, parameters, + parent, property, push, replace, role, signature, sort, stop, warnings +*/ + +const rx_amp = /&/g; +const rx_gt = />/g; +const rx_lt = / with less destructive entities. + + return String( + string + ).replace( + rx_amp, + "&" + ).replace( + rx_lt, + "<" + ).replace( + rx_gt, + ">" + ); +} + +export default { + error: function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + let fudge = Number(Boolean(data.option.fudge)); + let output = []; + if (data.stop) { + output.push("
    JSLint was unable to finish.
    "); + } + data.warnings.forEach(function (warning) { + output.push( + "
    ", + entityify(warning.line + fudge), + ".", + entityify(warning.column + fudge), + "
    ", + entityify(warning.message), + "
    ", + entityify(data.lines[warning.line] || ""), + "" + ); + }); + return output.join(""); + }, + + function: function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + let fudge = Number(Boolean(data.option.fudge)); + let mode = ( + data.module + ? "module" + : "global" + ); + let output = []; + + if (data.json) { + return ( + data.warnings.length === 0 + ? "
    JSON: good.
    " + : "
    JSON: bad.
    " + ); + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + let global = Object.keys(data.global.context).sort(); + let froms = data.froms.sort(); + let exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + let context = the_function.context; + let list = Object.keys(context); + output.push( + "
    ", + entityify(the_function.line + fudge), + "
    ", + ( + the_function.name === "=>" + ? entityify(the_function.signature) + " =>" + : ( + typeof the_function.name === "string" + ? "«" + entityify(the_function.name) + "»" + : "" + entityify(the_function.name.id) + "" + ) + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + let params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.parent === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.parent === the_function + ); + })); + detail("outer", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.parent !== the_function + && the_variable.parent.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].parent.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + output.push( + "
    JSLint edition ", + entityify(data.edition), + "
    " + ); + return output.join(""); + }, + + property: function property_directive(data) { + +// Produce the /*property*/ directive. + + let not_first = false; + let output = ["/*property"]; + let length = 1111; + let properties = Object.keys(data.property); + + if (properties.length > 0) { + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length >= 80) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); + } + } +}; diff --git a/branch.revert_regexp_single_whitespace/README b/branch.revert_regexp_single_whitespace/README new file mode 100644 index 000000000..f46e91069 --- /dev/null +++ b/branch.revert_regexp_single_whitespace/README @@ -0,0 +1,42 @@ +JSLint, The JavaScript Code Quality Tool + +Douglas Crockford +douglas@crockford.com + +2018-05-28 + +jslint.js contains the jslint function. It parses and analyzes a source file, +returning an object with information about the file. It can also take an object +that sets options. + +index.html runs the jslint.js function in a web page. The page also depends +browser.js and report.js and jslint.css. + +jslint.css provides styling for index.html. + +browser.js runs the web user interface. + +report.js generates the results reports in HTML. + +help.html describes JSLint's usage. Please read it. + +function.html describes the jslint function and the results it produces. + +Please direct questions and comments to +https://plus.google.com/communities/104441363299760713736. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[http://www.crockford.com/wrrrld/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. diff --git a/branch.revert_regexp_single_whitespace/browser.js b/branch.revert_regexp_single_whitespace/browser.js new file mode 100644 index 000000000..5086fed85 --- /dev/null +++ b/branch.revert_regexp_single_whitespace/browser.js @@ -0,0 +1,158 @@ +// browser.js +// 2018-06-16 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +/*jslint + browser +*/ + +/*property + checked, create, disable, display, error, focus, forEach, function, + getElementById, innerHTML, join, length, map, onchange, onclick, onscroll, + property, querySelectorAll, scrollTop, select, split, style, title, value +*/ + +import jslint from "./jslint.js"; +import report from "./report.js"; + +// This is the web script companion file for JSLint. It includes code for +// interacting with the browser and displaying the reports. + +let rx_crlf = /\n|\r\n?/; +let rx_separator = /[\s,;'"]+/; + +let fudge_unit = 0; + +const aux = document.getElementById("JSLINT_AUX"); +const boxes = document.querySelectorAll("[type=checkbox]"); +const fudge = document.getElementById("JSLINT_FUDGE"); +const global = document.getElementById("JSLINT_GLOBAL"); +const number = document.getElementById("JSLINT_NUMBER"); +const property = document.getElementById("JSLINT_PROPERTY"); +const property_fieldset = document.getElementById("JSLINT_PROPERTYFIELDSET"); +const report_field = document.getElementById("JSLINT_REPORT"); +const report_list = document.getElementById("JSLINT_REPORT_LIST"); +const source = document.getElementById("JSLINT_SOURCE"); +const select = document.getElementById("JSLINT_SELECT"); +const warnings = document.getElementById("JSLINT_WARNINGS"); +const warnings_list = document.getElementById("JSLINT_WARNINGS_LIST"); + +function show_numbers() { + number.value = source.value.split(rx_crlf).map(function (ignore, index) { + return index + fudge_unit; + }).join("\n"); +} + +function clear() { + aux.style.display = "none"; + number.value = ""; + property.value = ""; + property_fieldset.style.display = "none"; + report_field.style.display = "none"; + report_list.innerHTML = ""; + source.focus(); + source.value = ""; + warnings.style.display = "none"; + warnings_list.innerHTML = ""; +} + +function fudge_change() { + fudge_unit = Number(fudge.checked); + show_numbers(); +} + +function clear_options() { + boxes.forEach(function (node) { + node.checked = false; + }); + fudge_change(); + global.innerHTML = ""; +} + +function call_jslint() { + +// First build the option object. + + let option = Object.create(null); + boxes.forEach(function (node) { + if (node.checked) { + option[node.title] = true; + } + }); + +// Call JSLint with the source text, the options, and the predefined globals. + + let global_string = global.value; + let result = jslint( + source.value, + option, + ( + global_string === "" + ? undefined + : global_string.split(rx_separator) + ) + ); + +// Generate the reports. + + let error_html = report.error(result); + let function_html = report.function(result); + let property_text = report.property(result); + +// Display the reports. + + warnings_list.innerHTML = error_html; + warnings.style.display = ( + error_html.length === 0 + ? "none" + : "block" + ); + + report_list.innerHTML = function_html; + report_field.style.display = "block"; + if (property_text) { + property.value = property_text; + property_fieldset.style.display = "block"; + property.scrollTop = 0; + select.disable = false; + } else { + property_fieldset.style.display = "none"; + select.disable = true; + } + aux.style.display = "block"; + source.select(); +} + +fudge.onchange = fudge_change; + +source.onchange = function (ignore) { + show_numbers(); +}; + +source.onscroll = function () { + let ss = source.scrollTop; + number.scrollTop = ss; + let sn = number.scrollTop; + if (ss > sn) { + show_numbers(); + number.scrollTop = ss; + } +}; + +document.querySelectorAll("[name='JSLint']").forEach(function (node) { + node.onclick = call_jslint; +}); + +document.querySelectorAll("[name='clear']").forEach(function (node) { + node.onclick = clear; +}); + +document.getElementById("JSLINT_SELECT").onclick = function () { + property.focus(); + property.select(); +}; +document.getElementById("JSLINT_CLEAR_OPTIONS").onclick = clear_options; + +fudge_change(); +source.select(); +source.focus(); diff --git a/branch.revert_regexp_single_whitespace/function.html b/branch.revert_regexp_single_whitespace/function.html new file mode 100644 index 000000000..7d8271246 --- /dev/null +++ b/branch.revert_regexp_single_whitespace/function.html @@ -0,0 +1,618 @@ + + + + + + + + +JSLint: jslint function + + + +
    JSLint
    + +
    The jslint Function
    +
    +

    JSLint

    +

    JSLint is delivered as a set of files:

    +
    + + + + + + + + + + + + + + +
    FilenameContent
    browser.jsBrowser based UI.
    function.htmlThis page that you are looking at right now.
    help.htmlUsing jslint as a single page JSLint application.
    index.htmlSingle page JSLint application that uses the + js files.
    jslint.jsThe jslint function.
    report.jsThe report object, containing report generator functions for + HTML.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    +
    + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring"(JSLint)"
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    parenttokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    parenttokenThe function in which the variable was declared.variable
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    shebangstringThe first line of text if it started with #!line
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable can be assigned to.variable
    +

    Reports

    +

    The report object contains three functions that all take a + result from the jslint function as input. + report.js has no other dependence on other files.

    + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    +
    + +
    + + + + + +
    + + diff --git a/branch.revert_regexp_single_whitespace/help.html b/branch.revert_regexp_single_whitespace/help.html new file mode 100644 index 000000000..ee236ff95 --- /dev/null +++ b/branch.revert_regexp_single_whitespace/help.html @@ -0,0 +1,841 @@ + + + + + + + + + +JSLint: Help + + + +
    JSLint
    + +
    Help
    +
    +
    Il semble que la perfection soit atteinte non quand il n’y a + plus rien à ajouter, mais quand il n’y a plus rien à + retrancher.
    +
    + Antoine de Saint-Exupéry +
    +
    + Terre des Hommes (1939) +
    +

    Good Parts

    +

    The Principle of the Good Parts is

    +
    If a feature is sometimes useful and sometimes dangerous and if + there is a better option then always use the better option.
    +

    JSLint is a JavaScript program that looks for problems in + JavaScript programs. It is a code quality tool.

    +

    When C was + a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    +

    As the language matured, the definition of the language was + strengthened to eliminate some insecurities, and compilers got better + at issuing warnings. lint is no longer needed.

    +

    JavaScript is a + young-for-its-age language. It was originally intended to do small tasks in + webpages, tasks for which Java was too heavy and clumsy. But JavaScript is + a surprisingly capable language, and it is now being used in larger + projects. Many of the features that were intended to make the language easy + to use are troublesome when projects become complicated. A lint + for JavaScript is needed: JSLint, a JavaScript syntax checker + and validator.

    +

    JSLint takes a JavaScript source and scans it. If it finds a + problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by the ECMAScript Programming Language + Standard (the strangely named document that governs JavaScript). + JSLint will reject most legal programs. It is a higher + standard.

    +

    JavaScript is a sloppy language, but hidden deep inside there is an elegant, + better language. JSLint helps you to program in that better + language and to avoid most of the slop. JSLint will reject + programs that browsers will accept because JSLint is concerned + with the quality of your code and browsers are not. You should gladly accept + all of JSLint's advice.

    +

    JSLint can operate on JavaScript source + or JSON text.

    +

    ECMAScript Sixth Edition

    +

    Some of + ES6’s features are good, so JSLint will recognize the good + parts of ES6.

    +

    Currently, these features are recognized:

    +
      +
    • The ... ellipsis marker in parameter + lists and argument lists, replacing the arguments object + for variadic functions.
    • +
    • The let statement, which is like the var + statement except that it respects block scope. + You may use let or var but not both.
    • +
    • The const statement is like the let statement + except that it disallows the use of assignment on the variable, although + if the value of the variable is mutable, it can still be mutated. + const is preferred to let. +
    • Destructuring of arrays and objects is allowed in parameter lists and on + the left side of let, and const, but not + var or assignment statements, and not deep destructuring + or eliding.
    • +
    • Enhanced object literals, providing shorter forms for function + declaration and properties that are initialized by variables with the + same name.
    • +
    • The fat arrow => fart functions.
    • +
    • The simplest forms of import and export.
    • +
    • `Megastring` literals, but not nested + `megastring` literals.
    • +
    • New global functions, such as Map, Set, + WeakMap, and WeakSet.
    • +
    • 0b- and 0o- number literals.
    • +
    +

    The most important new feature of ES6 is proper tail calls. This has no + new syntax, so JSLint doesn’t see it. But it makes recursion + much more attractive, which makes loops, particularly for + loops, much less attractive.

    +

    import export

    +

    The ES6 module feature will be an important improvement over JavaScript’s + global variables as a means of linking separate files together. + JSLint recognizes a small but essential subset of the module + syntax.

    +
    +

    import name from stringliteral;

    +

    import {name} from stringliteral;

    +

    export default function () {};

    +

    export default function name() {};

    +

    export default expression;

    +

    export function name() {}

    +

    export name; // where name is const

    +

    export {name};

    +
    +

    Directives

    +

    JSLint provides three directives that may be placed in a + file to manage JSLint’s behavior. Use of these directives is + optional. If they are used, they should be placed in a source file before + the first statement. They are written in the form of a comment, where the + directive name is placed immediately after the opening of the comment before + any whitespace. The three directives are global, + jslint, and property. Directives in a file are + stronger than options selected from the UI or passed with the option object.

    +

    /*global*/

    +

    The /*global*/ directive is used to specify a set of globals + (usually functions and objects containing functions) that are available to + this file. This was commonly used in browsers to link source files together + before ES6 modules appeared. Use of global variables is strongly + discouraged, but unfortunately web browsers require their use. The + /*global*/ directive can only be used when the + Assume a browser option is selected. +

    Each of the names listed will indicate a read-only global variable. The names + are separated by , comma. This directive + should not be used if import or export is used. + This directive inhibits warnings, but it does not declare the names in the + execution environment. +

    For example: +

    /*global +
    ADSAFE, report, jslint
    +*/
    +

    instructs JSLint to not give warnings about the global + variables ADsafe, report, and jslint. + However, if any of those names are expected to be supplied by other files + and those other files fail to do so, then execution errors will result. It + is usually better to use top-level variable declarations instead: +

        var ADSAFE;
    +    var report;
    +    var jslint; 
    +

    Using var in this way allows comparing a global variable to the + undefined value to determine whether it is has been used in the global context.

    +

    /*jslint*/

    +

    The /*jslint*/ directive allows for the control of several + options. These options can also be set from the + JSLint.com user interface.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Tolerate bitwise operatorsbitwisetrue if bitwise operators should be allowed. The + bitwise operators are rarely used in JavaScript programs, so it + is usually more likely that & is a mistyping of + && than that it indicates a bitwise and. + JSLint will give warnings on the bitwise operators + unless this option is selected.
    Assume a browser browsertrue if the standard browser globals should be + predefined. This option will reject the use of + import and export. This option also + disallows the file form of the "use + strict" pragma. It does not supply + self; you will have to request that unnecessary + alias of the dreaded global object yourself. It adds the same + globals as this directive: +
    /*global +
    + clearInterval, clearTimeout, document, event, FileReader, + FormData, history, localStorage, location, name, navigator, + screen, sessionStorage, setInterval, setTimeout, Storage, + URL, window, XMLHttpRequest +
    + */
    +
    Tolerate conversion operatorsconverttrue if !!, + prefix, + and concatenation with "" are allowed. + The Boolean, Number, and + String functions are preferred
    Assume CouchDBcouchtrue if + Couch DB + globals should be predefined. It adds the same globals as this + directive: +
    /*global +
    emit, getRow, isArray, log, provides, registerType, require, + send, start, sum, toJSON
    + */
    Assume in developmentdeveltrue if browser globals that are useful in + development should be predefined, and if debugger + statements and TODO comments + should be allowed. It adds the + same globals as this directive: +
    /*global +
    alert, confirm, console, prompt
    + */
    Be sure to turn this option off before going + into production.
    Tolerate evalevaltrue if eval should be allowed. In the + past, the eval function was the most misused + feature of the language.
    Tolerate for statementfortrue if the for statement should be + allowed. It is almost always better to use the array methods + instead.
    Fudge the line and column numbersfudgetrue if the origin of a text file is line 1 column + 1. Programmers know that number systems start at zero. + Unfortunately, most text editors start numbering lines and + columns at one. JSLint will by default start + numbering at zero. Use this option to start the numbering at one + in its reports.
    Tolerate get and setgetsettrue if accessor properties are allowed in object literals.
    Tolerate long source lineslongtrue if a line can contain more than 80 characters.
    Tolerate multiple variables declarations per statementmultivartrue if a var, let, or + const statement can declare two or more variables + in a single statement.
    Assume Node.jsnodetrue if Node.js globals should be predefined. It + will predefine globals that are used in the Node.js environment. + It adds the same globals as this directive: +
    /*global +
    + Buffer, clearImmediate, clearInterval, clearTimeout, console, exports, + module, process, require, setImmediate, setInterval, setTimeout, URL, + URLSearchParams, __dirname, __filename +
    + */
    Tolerate single quote stringssingletrue if 'single quote + should be allowed to enclose string literals.
    Tolerate this
    thistrue if this should be allowed.
    Tolerate whitespace messwhitetrue if the whitespace rules should be ignored.
    +

    For example:

    +
    /*jslint
    +    bitwise, node
    +*/
    + +

    /*property*/

    +

    The /*property*/ directive is used to declare a list of + property identifiers that are used in the file. Each property name in the + program is looked up in this list. If a name is not found, that indicates an + error, most likely a typing error.

    +

    The list can also be used to evade some of JSLint’s naming + rules.

    +

    JSLint can build the /*property*/ list for you. At + the bottom of its report, JSLint displays a + /*property*/ directive. It contains all of the names that were + used with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. You + can copy the /*property*/ directive to the top of your + script file. JSLint will check the spelling of all property + names against the list. That way, you can have JSLint look for + misspellings for you.

    +

    For example,

    +
    /*property +
    charAt, slice, _$_
    + */
    +

    ignore

    +

    JSLint introduces a new reserved word: ignore. It + is used in parameter lists and in catch clauses to indicate a + parameter that will be ignored. Unused warnings will not be produced for + ignore. +

    function handler(ignore, value) {
    +    return do_something_useful(value);
    +}
    +

    var let const

    +

    JavaScript provides three statements for declaring variables: + var, let, and const. The + var statement suffers from a bad practice called hoisting. The + let statement does not do hoisting and respects block scope. + The const statement is like let except that it + marks the variable (but not its contents) as read only, making it an error + to attempt to assign to the variable. When given a choice, + const is the best, var is the worst.

    +

    JSLint uses the intersection of the var rules and the + let rules, and by doing so avoids the errors related to either. + A name should be declared only once in a function. It should be declared + before it is used. It should not be used outside of the block in which it is + declared. A variable should not have the same name as a variable or + parameter in an outer function. Do not mix var and + let. Declare one name per statement.

    +

    = == ===

    +

    Fortran made a terrible + mistake in using the equality operator as its assignment operator. That + mistake has been replicated in most languages since then. C compounded that + mistake by making == its equality operator. The visual + similarity is a source of errors. JavaScript compounded this further by + making == a type coercing comparison operator that produces + false positive results. This was mitigated by adding the === + operator, leaving the broken == operator in place.

    +

    JSLint attempts to minimize errors by the following rules:

    +

    == is not allowed. This avoids the false positives, and + increases the visual distance between = and ===. + Assignments are not allowed in expression position, and comparisons are not + allowed in statement position. This also reduces confusion. 

    +

    Semicolon

    +

    JavaScript uses a C-like syntax that requires the use of semicolons to + delimit certain statements. JavaScript attempts to make those semicolons + optional with an automatic semicolon insertion mechanism, but it does not + work very well. Automatic semicolon insertion was added to make + things easier for beginners. Unfortunately, it sometimes fails. Do not rely + on it unless you are a beginner.

    +

    JSLint expects that every statement will be followed by + ; except for for, function, + if, switch, try, and + while. JSLint does not expect to see unnecessary + semicolons, the empty statement, or empty blocks.

    +

    function =>

    +

    JavaScript has four syntactic forms for making function objects: function + statements, function expressions, enhanced object literals, and the + => fart operator.

    +

    The function statement creates a variable and assigns the function object to + it. It should be used in a file or function body, but not inside of a block.

    +
    function name(parameters) { +
    statements
    + }
    +

    The function expression unfortunately looks like the function statement. It + may appear anywhere that an expression may appear, but not in statement + position and not in a loop. It produces a function object but does not + create a variable in which to store it.

    +
    function (parameters) { +
    statements
    + }
    +

    The enhanced object literal provides an ES6 shorthand for creating a property + whose value is a function, saving you from having to type + : colon and function.

    +
    { +
    name(parameters) { +
    statements
    + }
    + }
    +

    Finally, ES6 provides an even shorter form of function expression that leaves + out the words function and return:

    +
    (parameters) => + expression
    +

    JSLint requires the parens around the parameters, and forbids a + { left brace after the + => fart to avoid syntactic ambiguity.

    +

    Comma

    +

    The , comma operator is unnecessary and can + mask programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as + an operator. It does not expect to see elided elements in array literals. A + comma should not appear after the last element of an array literal or object + literal.

    +

    Blocks

    +

    JSLint expects blocks with function, + if, switch, while, for, + do, and try statements and nowhere else.

    +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    +

    JavaScript allows an if to be written like this:

    +
    if (condition)
    +    statement;
    +

    That form is known to contribute to mistakes in projects where many + programmers are working on the same code. That is why JSLint + expects the use of a block:

    +
    if (condition) {
    +    statements;
    +}
    +

    Experience shows that this form is more resilient.

    +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call. All other expression statements are considered + to be errors.

    +

    for

    +

    JSLint does not recommend use of the for statement. + Use array methods like forEach instead. The for + option will suppress some warnings. The forms of for that + JSLint accepts are restricted, excluding the new ES6 forms.

    +

    for in

    +

    JSLint does not recommend use of the for + in statement. Use Object.keys instead.

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, it also + loops through all of the properties that were inherited through the + prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written + without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    for (name in object) {
    +    if (object.hasOwnProperty(name)) {
    +        ....
    +    }
    +}
    +

    Note that the above code will fail if the object contains a property + named hasOwnProperty. Use Object.keys instead.

    +

    switch

    +

    A common error in switch statements is to forget to place a + break statement after each case, resulting in unintended + fall-through. JSLint expects that the statement before the next + case or default is one of these: + break, return, or throw.

    +

    with

    +

    The with statement was intended to provide a shorthand in + accessing properties in deeply nested objects. Unfortunately, it behaves + very badly when setting new properties. Never use the with + statement.

    +

    JSLint does not expect to see a with statement.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that + labels will be distinct from vars and parameters.

    +

    Unreachable Code

    +

    JSLint expects that a return, break, + or throw statement will be followed by a + } right brace or case or + default.

    +

    Confusing Pluses and Minuses

    +

    JSLint expects that + will not be followed by + + or ++, and that - will not be + followed by - or --. A misplaced space can turn + + + into ++, an error that is difficult to see. + Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ increment and + -- decrement operators have been known to + contribute to bad code by encouraging excessive trickiness. They are second + only to faulty architecture in enabling to viruses and other security + menaces. Also, preincrement/postincrement confusion can produce off-by-one + errors that are extremely difficult to diagnose. Fortunately, they are also + complete unnecessary. There are better ways to add 1 to a variable.

    +

    It is best to avoid these operators entirely and rely on += and + -= instead.

    +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. + JSLint looks for problems that may cause portability problems. + It also attempts to resolve visual ambiguities by recommending explicit + escapement.

    +

    JavaScript’s syntax for regular expression literals overloads the + / slash character. To avoid ambiguity, + JSLint expects that the character preceding a regular + expression literal is a ( left paren or + = equal or + : colon or + , comma character.

    +

    this

    +
    Having this in the language makes it harder to talk + about the language. It is like pair programming with Abbott and Costello. +
    +

    Avoid using this. Warnings about this can be + suppressed with option.this.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the + new prefix. The new prefix creates a new object + based on the function's prototype, and binds that object to the + function's implied this parameter. If you neglect to use the + new prefix, no new object will be made and this + will be bound to the global object.

    +

    JSLint enforces the convention that constructor functions be + given names with initial uppercase. JSLint does not expect to + see a function invocation with an initial uppercase name unless it has the + new prefix. JSLint does not expect to see the + new prefix used with functions whose names do not start with + initial uppercase.

    +

    JSLint does not expect to see the wrapper forms + new Number, new String, new Boolean.

    +

    JSLint does not expect to see new Object. + Use Object.create(null) instead.

    +

    Whitespace

    +

    JSLint has a specific set of rules about the use of whitespace. + Where possible, these rules are consistent with centuries of good practice + with literary style.

    +

    The indentation increases by 4 spaces when the last token on a line is + { left brace, + [ left bracket, + ( left paren. The matching closing + token will be the first token on a line, restoring the previous + indentation.

    +

    The ternary operator can be visually confusing, so + ? question mark and + : colon always begin a line and increase + the indentation by 4 spaces.

    +
    return (the_token.id === "(string)" || the_token.id === "(number)")
    +    ? String(the_token.value)
    +    : the_token.id;
    +

    If . period is the first character on a line, the + indentation is increased by 4 spaces.

    +

    A var, let, or const statement will + also cause an indentation if it declares two or more variables, and if the + second variable is not on the same line as the start of the statement.

    +

    Long lines can be continued on the next line with 8 spaces added to the + current indentation. Those 8 spaces do not change the current indentation. + It is 8 to avoid confusion with ordinary indentation.

    +

    The word function is always followed with one space.

    +

    Clauses (case, catch, default, + else, finally) are not statements and so should + not be indented like statements.

    +

    Spaces are used to make things that are not invocations look less like + invocations.

    +

    Tabs and spaces should not be mixed. We should pick just one in order to + avoid the problems that come from having both. Personal preference is an + extremely unreliable criterion. Neither offers a powerful advantage over the + other. Fifty years ago, tab had the advantage of consuming less memory, but + Moore's Law has eliminated that advantage. Space has one clear advantage + over tab: there is no reliable standard for how many spaces a tab + represents, but it is universally accepted that a space occupies a space. So + use spaces. You can edit with tabs if you must, but make sure it is spaces + again before you commit. Maybe someday we will finally get a universal + standard for tabs, but until that day comes, the better choice is spaces.

    +

    Unsafe Characters

    +

    There are characters that are handled inconsistently in some browsers, and + so must be escaped when placed in strings.

    +
    \u0000-\u001f
    +\u007f-\u009f
    +\u00ad
    +\u0600-\u0604
    +\u070f
    +\u17b4
    +\u17b5
    +\u200c-\u200f
    +\u2028-\u202f
    +\u2060-\u206f
    +\ufeff
    +\ufff0-\uffff
    +

    Report

    +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    +
      +
    • The line number on which it starts.
    • +
    • The function’s name. In the case of anonymous functions, + JSLint will attempt to + «guess» the name.
    • +
    • The parameters.
    • +
    • Variables: The variables that are declared in the function.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Exceptions: The variables that are declared by catch + clauses of try statements.
    • +
    • Outer: Variables used by this function that are declared in + other functions.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements. Updates are announced at + https://plus.google.com/communities/104441363299760713736.

    +

    Try it

    +

    Try it. Paste your script + into the window and click the + + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    JSLint is written entirely in JavaScript, so it can run + anywhere that JavaScript (or even Java) can run.

    +

    Perfectly fine

    +

    JSLint was designed to reject code that some would consider to + be perfectly fine. The reason for this is that JSLint's + purpose is to help produce programs that are free of error. That is + difficult in any language and is especially hard in JavaScript. + JSLint attempts to help you increase the visual distance + between correct programs and incorrect programs, making the remaining errors + more obvious. JSLint will give warnings about things that are + not necessarily wrong in the current situation, but which have been observed + to mask or obscure errors. Avoid those things when there are better options + available.

    +

    It is dangerous out there. JSLint is here to help.

    +

    Warning

    +

    JSLint will hurt your feelings. Side effects may include + headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, + dry mouth, cleaner code, and a reduced error rate.

    +

    Further Reading

    + +
    +

    + Code Conventions for the JavaScript Programming Language.

    + + + + + +
    + + diff --git a/branch.revert_regexp_single_whitespace/index.html b/branch.revert_regexp_single_whitespace/index.html new file mode 100644 index 000000000..235f44b54 --- /dev/null +++ b/branch.revert_regexp_single_whitespace/index.html @@ -0,0 +1,125 @@ + + + + + + + + + + + +JSLint: The JavaScript Code Quality Tool + + +
    +
    Source
    +
    + + +
    +
    + Warnings +
    +
    +
    + Function Report +
    +
    +
    + Property Directive + +
    +
    + + + + +
    +
    Options +
    Assume... +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Global variables... + +
    +
    +
    +
    + + + + + +
    + + diff --git a/branch.revert_regexp_single_whitespace/jslint.css b/branch.revert_regexp_single_whitespace/jslint.css new file mode 100644 index 000000000..9c1b1a2f9 --- /dev/null +++ b/branch.revert_regexp_single_whitespace/jslint.css @@ -0,0 +1,340 @@ +@font-face { + font-family: 'Programma'; + src: url('Programma.woff') format('woff'); +} +@font-face { + font-family: 'Daley'; + font-weight: bold; + src: url('Daley-Bold.woff') format('woff'); +} + +body { + background-color: antiquewhite; + color: black; + font-family: 'Daley', sans-serif; + margin: 0; + padding: 0; +} + +#JSLINT_TITLEBOX { + font-family: 'Daley', sans-serif; + margin: 0; + margin-left: 3%; + margin-right: 3%; + padding: 0; +} + +#JSLINT_TITLEBOX ul { + float: right; + font-size: 12pt; +} + +#JSLINT_TITLEBOX div { + color: darkslategray; + float: left; + font-size: 48pt; +} + +#JSLINT_ fieldset { + background-color: gainsboro; + border: 0; + clear: both; + margin-bottom: 1em; + margin-left: 3%; + margin-right: 3%; + margin-top: 1em; + padding: 0; + width: auto; +} +#JSLINT_ legend { + background-color: darkslategray; + border: 0; + color: white; + font-size: 100%; + font-style: normal; + font-weight: normal; + margin: 0; + padding-bottom: 0.25em; + padding-left: 0; + padding-right: 0; + padding-top: 0.25em; + text-align: center; + width: 100%; +} +#JSLINT_ button { + background-color: darkslategray; + border: 0; + color: white; + cursor: pointer; + font-family: 'Daley', sans-serif; + font-size: 100%; + font-style: normal; + margin-left: 1em; + margin-right: 1em; + padding-left: 1em; + padding-right: 1em; + padding-bottom: 0.25em; + padding-top: 0.25em; + text-align: center; +} +#JSLINT_ button:hover { + background-color: slategray; + color: white; + border: 0; +} + +#JSLINT_ button:active { + border: 0; + color: black; +} + +#JSLINT_ button:disabled { + background-color: gray; + border: 0; + color: gainsboro; +} + +a:visited { + color: #69565c; +} + +a:link { + color: black; +} + +#JSLINT_ input[type="text"] { + background-color: white; + border: 0; + color: black; + margin-bottom: 0.2em; + margin-right: 0.5em; + padding-left: 0.5em; + padding-right: 0.5em; + text-align: right; + width: 3em; +} + +#JSLINT_ textarea { + border: 0; + background-color: white; + border: 0; + font-family: Programma, monospace; + font-size: 100%; + font-weight: bold; + resize: none; +} + +#JSLINT_ textarea::selection { + background-color: yellow; + color: black; +} + +#JSLINT_NUMBER { + color: darkgray; + display: inline-block; + height: 4in; + margin: 2%; + margin-right: 0; + overflow: hidden; + padding-right: 0.5%; + text-align: right; + white-space: pre; + width: 4%; +} + +#JSLINT_SOURCE { + color: black; + display: inline-block; + font-size: 100%; + height: 4in; + margin: 2%; + margin-left: 0; + margin-right: 0; + overflow: auto; + padding-left: 0.5%; + white-space: pre; + width: 91%; +} + +#JSLINT_PROPERTY { + background-color: honeydew; + border: 0; + margin: 2%; + padding: 0.5%; + white-space: pre; + width: 95%; +} + +#JSLINT_GLOBAL { + white-space: normal; + width: 95%; +} +#JSLINT_ label { + font-family: sans-serif; + font-size: 90%; + padding-left: 0.25em; +} +#JSLINT_OPTIONS>div { + float: left; + margin: 0.5em; +} + +#JSLINT_ address { + color: black; + display: block; + float: right; + font-family: serif; + font-size: 90%; + margin-left: 1em; +} +#JSLINT_ dl { + background-color: cornsilk; + color: white; + margin: 0; + padding-bottom: 2pt; + padding-left: 1em; + padding-right: 1em; + padding-top: 2pt; +} +#JSLINT_ dfn { + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + margin-bottom: 2pt; +} +#JSLINT_ dt { + color: black; + display: block; + float: left; + font-family: serif; + font-size: 75%; + font-style: italic; + margin: 0; + width: 8em; + text-align: right; +} +#JSLINT_ dd { + color: black; + display: block; + font-family: Programma, monospace; + font-weight: bold; + margin-left: 8em; + padding-bottom: 2pt; +} +#JSLINT_WARNINGS>legend { + background-color: indianred; +} +#JSLINT_WARNINGS>div { + background-color: pink; + padding: 1em; +} +#JSLINT_WARNINGS cite { + color: black; + display: block; + font-family: serif; + font-size: 100%; + font-style: normal; + margin-bottom: 4pt; + margin-left: 20pt; + margin-right: 20pt; + margin-top: 4pt; + overflow-x: hidden; +} +#JSLINT_WARNINGS samp { + background-color: lavenderblush; + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + padding: 4pt; + margin-bottom: 0; + margin-left: 16pt; + margin-right: 16pt; + margin-top: 0; + white-space: pre-wrap; +} + +#JSLINT_REPORT center { + margin-top: 1em; +} + +#JSLINT_WARNINGS dl address { + color: black; + display: inline; + float: none; + font-size: 80%; + margin: 0; +} + +#JSLINT_REPORT>div { + padding: 1em; +} + +#JSLINT_ dl.level0 { + color: black; + background-color: white; +} + +#JSLINT_ dl.level1 { + color: black; + background-color: #ffffe0; /* yellow */ + margin-left: 1em; +} + +#JSLINT_ dl.level2 { + color: black; + background-color: #e0ffe0; /* green */ + margin-left: 2em; +} + +#JSLINT_ dl.level3 { + color: black; + background-color: #e0e0ff; /* blue */ + margin-left: 3em; +} + +#JSLINT_ dl.level4 { + background-color: #ffe0ff; /* purple */ + color: black; + margin-left: 4em; +} + +#JSLINT_ dl.level5 { + background-color: #ffe0e0; /* red */ + color: black; + margin-left: 5em; +} + +#JSLINT_ dl.level6 { + background-color: #ffe390; /* orange */ + color: black; + margin-left: 6em; +} + +#JSLINT_ dl.level7 { + background-color: #e0e0e0; /* gray */ + color: black; + margin-left: 7em; +} + +#JSLINT_ dl.level8 { + color: black; + margin-left: 8em; +} + +#JSLINT_ dl.level9 { + color: black; + margin-left: 9em; +} + +.none { + display: none; +} +.center { + text-align: center; +} diff --git a/branch.revert_regexp_single_whitespace/jslint.js b/branch.revert_regexp_single_whitespace/jslint.js new file mode 100644 index 000000000..39d8bc80a --- /dev/null +++ b/branch.revert_regexp_single_whitespace/jslint.js @@ -0,0 +1,4924 @@ +// jslint.js +// 2018-09-24 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// jslint(source, option_object, global_array) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze, a string or an array of strings. +// option_object An object whose keys correspond to option names. +// global_array An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all of the functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// 1. If the source is a single string, split it into an array of strings. +// 2. Turn the source into an array of tokens. +// 3. Furcate the tokens into a parse tree. +// 4. Walk the tree, traversing all of the nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// 5. Check the whitespace between the tokens. + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*jslint long */ + +/*property + a, and, arity, assign, b, bad_assignment_a, bad_directive_a, bad_get, + bad_module_name_a, bad_option_a, bad_property_a, bad_set, bitwise, block, + body, browser, c, calls, catch, charCodeAt, closer, closure, code, column, + complex, concat, constant, context, convert, couch, create, d, dead, + default, devel, directive, directives, disrupt, dot, duplicate_a, edition, + ellipsis, else, empty_block, escape_mega, eval, every, expected_a, + expected_a_at_b_c, expected_a_b, expected_a_b_from_c_d, expected_a_before_b, + expected_a_next_at_b, expected_digits_after_a, expected_four_digits, + expected_identifier_a, expected_line_break_a_b, expected_regexp_factor_a, + expected_space_a_b, expected_statements_a, expected_string_a, + expected_type_string_a, exports, expression, extra, finally, flag, for, + forEach, free, from, froms, fud, fudge, function_in_loop, functions, g, + getset, global, i, id, identifier, import, inc, indexOf, infix_in, init, + initial, isArray, isNaN, join, json, keys, label, label_a, lbp, led, length, + level, line, lines, live, long, loop, m, margin, match, message, + misplaced_a, misplaced_directive_a, missing_browser, missing_m, module, + multivar, naked_block, name, names, nested_comment, new, node, not_label_a, + nr, nud, number_isNaN, ok, open, option, out_of_scope_a, parameters, parent, + pop, property, push, quote, redefinition_a_b, replace, + required_a_optional_b, reserved_a, right, role, search, shebang, signature, + single, slice, some, sort, split, startsWith, statement, stop, strict, + subscript_a, switch, test, this, thru, toString, todo_comment, tokens, + too_long, too_many_digits, tree, try, type, u, unclosed_comment, + unclosed_mega, unclosed_string, undeclared_a, unexpected_a, + unexpected_a_after_b, unexpected_a_before_b, unexpected_at_top_level_a, + unexpected_char_a, unexpected_comment, unexpected_directive_a, + unexpected_expression_a, unexpected_label_a, unexpected_parens, + unexpected_space_a_b, unexpected_statement_a, unexpected_trailing_space, + unexpected_typeof_a, uninitialized_a, unreachable_a, + unregistered_property_a, unsafe, unused_a, use_double, use_open, use_spaces, + use_strict, used, value, var_loop, var_switch, variable, warning, warnings, + weird_condition_a, weird_expression_a, weird_loop, weird_relation_a, white, + wrap_assignment, wrap_condition, wrap_immediate, wrap_parameter, + wrap_regexp, wrap_unary, wrapped, writable, y +*/ + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than {} because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +function populate(array, object = empty(), value = true) { + +// Augment an object by taking property names from an array of strings. + + array.forEach(function (name) { + object[name] = value; + }); + return object; +} + +const allowed_option = { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + bitwise: true, + browser: [ + "caches", "clearInterval", "clearTimeout", "document", "DOMException", + "Element", "Event", "event", "FileReader", "FormData", "history", + "localStorage", "location", "MutationObserver", "name", "navigator", + "screen", "sessionStorage", "setInterval", "setTimeout", "Storage", + "URL", "window", "Worker", "XMLHttpRequest" + ], + couch: [ + "emit", "getRow", "isArray", "log", "provides", "registerType", + "require", "send", "start", "sum", "toJSON" + ], + convert: true, + devel: [ + "alert", "confirm", "console", "prompt" + ], + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + multivar: true, + node: [ + "Buffer", "clearImmediate", "clearInterval", "clearTimeout", + "console", "exports", "module", "process", "require", + "setImmediate", "setInterval", "setTimeout", "URL", + "URLSearchParams", "__dirname", "__filename" + ], + single: true, + this: true, + white: true +}; + +const anticondition = populate([ + "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%", + "typeof", "(number)", "(string)" +]); + +// These are the bitwise operators. + +const bitwiseop = populate([ + "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=", + ">>>", ">>>=" +]); + +const escapeable = populate([ + "\\", "/", "`", "b", "f", "n", "r", "t" +]); + +const opener = { + +// The open and close pairs. + + "(": ")", // paren + "[": "]", // bracket + "{": "}", // brace + "${": "}" // mega +}; + +// The relational operators. + +const relationop = populate([ + "!=", "!==", "==", "===", "<", "<=", ">", ">=" +]); + +// This is the set of infix operators that require a space on each side. + +const spaceop = populate([ + "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/", + "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=", + ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" +]); + +const standard = [ + +// These are the globals that are provided by the language standard. + + "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI", + "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error", + "EvalError", "Float32Array", "Float64Array", "Generator", + "GeneratorFunction", "Int8Array", "Int16Array", "Int32Array", "Intl", + "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat", + "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", + "Set", "String", "Symbol", "SyntaxError", "System", "TypeError", + "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", + "URIError", "WeakMap", "WeakSet" +]; + +const bundle = { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + and: "The '&&' subexpression should be wrapped in parens.", + bad_assignment_a: "Bad assignment to '{a}'.", + bad_directive_a: "Bad directive '{a}'.", + bad_get: "A get function takes no parameters.", + bad_module_name_a: "Bad module name '{a}'.", + bad_option_a: "Bad option '{a}'.", + bad_property_a: "Bad property name '{a}'.", + bad_set: "A set function takes one parameter.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + escape_mega: "Unexpected escapement in mega literal.", + expected_a: "Expected '{a}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: ( + "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'." + ), + expected_a_before_b: "Expected '{a}' before '{b}'.", + expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.", + expected_digits_after_a: "Expected digits after '{a}'.", + expected_four_digits: "Expected four digits after '\\u'.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.", + expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.", + expected_space_a_b: "Expected one space between '{a}' and '{b}'.", + expected_statements_a: "Expected statements before '{a}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + expected_type_string_a: "Expected a type string and instead saw '{a}'.", + function_in_loop: "Don't make functions within a loop.", + infix_in: ( + "Unexpected 'in'. Compare with undefined, " + + "or use the hasOwnProperty method instead." + ), + label_a: "'{a}' is a statement label.", + misplaced_a: "Place '{a}' at the outermost level.", + misplaced_directive_a: ( + "Place the '/*{a}*/' directive before the first statement." + ), + missing_browser: "/*global*/ requires the Assume a browser option.", + missing_m: "Expected 'm' flag on a multiline regular expression.", + naked_block: "Naked block.", + nested_comment: "Nested comment.", + not_label_a: "'{a}' is not a label.", + number_isNaN: "Use Number.isNaN function to compare with NaN.", + out_of_scope_a: "'{a}' is out of scope.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + required_a_optional_b: ( + "Required parameter '{a}' after optional parameter '{b}'." + ), + reserved_a: "Reserved name '{a}'.", + subscript_a: "['{a}'] is better written in dot notation.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line is longer than 80 characters.", + too_many_digits: "Too many digits.", + unclosed_comment: "Unclosed comment.", + unclosed_mega: "Unclosed mega literal.", + unclosed_string: "Unclosed string.", + undeclared_a: "Undeclared '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_a_after_b: "Unexpected '{a}' after '{b}'.", + unexpected_a_before_b: "Unexpected '{a}' before '{b}'.", + unexpected_at_top_level_a: "Expected '{a}' to be in a function.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_directive_a: "When using modules, don't use directive '/*{a}'.", + unexpected_expression_a: ( + "Unexpected expression '{a}' in statement position." + ), + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_parens: "Don't wrap function literals in parens.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_statement_a: ( + "Unexpected statement '{a}' in expression position." + ), + unexpected_trailing_space: "Unexpected trailing space.", + unexpected_typeof_a: ( + "Unexpected 'typeof'. Use '===' to compare directly with {a}." + ), + uninitialized_a: "Uninitialized '{a}'.", + unreachable_a: "Unreachable '{a}'.", + unregistered_property_a: "Unregistered property name '{a}'.", + unsafe: "Unsafe character '{a}'.", + unused_a: "Unused '{a}'.", + use_double: "Use double quotes, not single quotes.", + use_open: ( + "Wrap a ternary expression in parens, " + + "with a line break after the left paren." + ), + use_spaces: "Use spaces, not tabs.", + use_strict: "This function needs a \"use strict\" pragma.", + var_loop: "Don't declare variables in a loop.", + var_switch: "Don't declare variables in a switch.", + weird_condition_a: "Weird condition '{a}'.", + weird_expression_a: "Weird expression '{a}'.", + weird_loop: "Weird loop.", + weird_relation_a: "Weird relation '{a}'.", + wrap_assignment: "Don't wrap assignment statements in parens.", + wrap_condition: "Wrap the condition in parens.", + wrap_immediate: ( + "Wrap an immediate function invocation in parentheses to assist " + + "the reader in understanding that the expression is the result " + + "of a function, and not the function itself." + ), + wrap_parameter: "Wrap the parameter in parens.", + wrap_regexp: "Wrap this regexp in parens to avoid confusion.", + wrap_unary: "Wrap the unary expression in parens." +}; + +// Regular expression literals: + +// supplant {variables} +const rx_supplant = /\{([^{}]*)\}/g; +// carriage return, carriage return linefeed, or linefeed +const rx_crlf = /\n|\r\n?/; +// unsafe characters that are silently deleted by one or more browsers +const rx_unsafe = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; +// identifier +const rx_identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; +const rx_module = /^[a-zA-Z0-9_$:.@\-\/]+$/; +const rx_bad_property = /^_|\$|Sync\$|_$/; +// star slash +const rx_star_slash = /\*\//; +// slash star +const rx_slash_star = /\/\*/; +// slash star or ending slash +const rx_slash_star_or_slash = /\/\*|\/$/; +// uncompleted work comment +const rx_todo = /\b(?:todo|TO\s?DO|HACK)\b/; +// tab +const rx_tab = /\t/g; +// directive +const rx_directive = /^(jslint|property|global)\s+(.*)$/; +const rx_directive_part = /^([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*(.*)$/; +// token (sorry it is so long) +const rx_token = /^((\s+)|([a-zA-Z_$][a-zA-Z0-9_$]*)|[(){}\[\]?,:;'"~`]|=(?:==?|>)?|\.+|[*\/][*\/=]?|\+[=+]?|-[=\-]?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<= "a" && string <= "z\uffff") + || (string >= "A" && string <= "Z\uffff") + ); +} + +function supplant(string, object) { + return string.replace(rx_supplant, function (found, filling) { + const replacement = object[filling]; + return ( + replacement !== undefined + ? replacement + : found + ); + }); +} + +let anon; // The guessed name for anonymous functions. +let blockage; // The current block. +let block_stack; // The stack of blocks. +let declared_globals; // The object containing the global declarations. +let directives; // The directive comments. +let directive_mode; // true if directives are still allowed. +let early_stop; // true if JSLint cannot finish. +let exports; // The exported names and values. +let froms; // The array collecting all import-from strings. +let fudge; // true if the natural numbers start with 1. +let functionage; // The current function. +let functions; // The array containing all of the functions. +let global; // The global object; the outermost context. +let json_mode; // true if parsing JSON. +let lines; // The array containing source lines. +let module_mode; // true if import or export was used. +let next_token; // The next token to be examined in the parse. +let option; // The options parameter. +let property; // The object containing the tallied property names. +let mega_mode; // true if currently parsing a megastring literal. +let shebang; // true if a #! was seen on the first line. +let stack; // The stack of functions. +let syntax; // The object containing the parser. +let token; // The current token being examined in the parse. +let token_nr; // The number of the next token. +let tokens; // The array of tokens. +let tenure; // The predefined property registry. +let tree; // The abstract parse tree. +let var_mode; // "var" if using var; "let" if using let. +let warnings; // The array collecting all generated warnings. + +// Error reportage functions: + +function artifact(the_token) { + +// Return a string representing an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); +} + +function artifact_line(the_token) { + +// Return the fudged line number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.line + fudge; +} + +function artifact_column(the_token) { + +// Return the fudged column number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.from + fudge; +} + +function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + const warning = { // ~~ + name: "JSLintError", + column: column, + line: line, + code: code + }; + if (a !== undefined) { + warning.a = a; + } + if (b !== undefined) { + warning.b = b; + } + if (c !== undefined) { + warning.c = c; + } + if (d !== undefined) { + warning.d = d; + } + warning.message = supplant(bundle[code] || code, warning); + warnings.push(warning); + return warning; +} + +function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); +} + +function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + if (the_token.warning === undefined) { + the_token.warning = warn_at( + code, + the_token.line, + the_token.from, + a || artifact(the_token), + b, + c, + d + ); + return the_token.warning; + } +} + +function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); +} + +// Tokenize: + +function tokenize(source) { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + +// If the source is not an array, then it is split into lines at the +// carriage return/linefeed. + + lines = ( + Array.isArray(source) + ? source + : source.split(rx_crlf) + ); + tokens = []; + + let char; // a popular character + let column = 0; // the column number of the next character + let first; // the first token + let from; // the starting column number of the token + let line = -1; // the line number of the next character + let nr = 0; // the next token number + let previous = global; // the previous token including comments + let prior = global; // the previous token excluding comments + let mega_from; // the starting column of megastring + let mega_line; // the starting line of megastring + let regexp_seen; // regular expression literal seen on this line + let snippet; // a piece of string + let source_line = ""; // the remaining line source string + let whole_line = ""; // the whole line source string + + if (lines[0].startsWith("#!")) { + line = 0; + shebang = true; + } + + function next_line() { + +// Put the next line of source in source_line. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + let at; + if ( + !option.long + && whole_line.length > 80 + && !json_mode + && first + && !regexp_seen + ) { + warn_at("too_long", line, 80); + } + column = 0; + line += 1; + regexp_seen = false; + whole_line = lines[line]; + source_line = whole_line; + if (source_line !== undefined) { + at = source_line.search(rx_tab); + if (at >= 0) { + if (!option.white) { + warn_at("use_spaces", line, at + 1); + } + source_line = source_line.replace(rx_tab, " "); + } + at = source_line.search(rx_unsafe); + if (at >= 0) { + warn_at( + "unsafe", + line, + column + at, + "U+" + source_line.charCodeAt(at).toString(16) + ); + } + if (!option.white && source_line.slice(-1) === " ") { + warn_at( + "unexpected_trailing_space", + line, + source_line.length - 1 + ); + } + } + return source_line; + } + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions snip, next_char, prev_char, +// some_digits, and escape help in the parsing of literals. + + function snip() { + +// Remove the last character from snippet. + + snippet = snippet.slice(0, -1); + } + + function next_char(match) { + +// Get the next character from the source line. Remove it from the source_line, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + return stop_at( + ( + char === "" + ? "expected_a" + : "expected_a_b" + ), + line, + column - 1, + match, + char + ); + } + if (source_line) { + char = source_line[0]; + source_line = source_line.slice(1); + snippet += char; + } else { + char = ""; + snippet += " "; + } + column += 1; + return char; + } + + function back_char() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the source_line. + + if (snippet) { + char = snippet.slice(-1); + source_line = char + source_line; + column -= 1; + snip(); + } else { + char = ""; + } + return char; + } + + function some_digits(rx, quiet) { + const result = source_line.match(rx); + if (result) { + char = result[1]; + column += char.length; + source_line = result[2]; + snippet += char; + } else { + char = ""; + if (!quiet) { + warn_at( + "expected_digits_after_a", + line, + column, + snippet + ); + } + } + return char.length; + } + + function escape(extra) { + next_char("\\"); + if (escapeable[char] === true) { + return next_char(); + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "u") { + if (next_char("u") === "{") { + if (json_mode) { + warn_at("unexpected_a", line, column - 1, char); + } + if (some_digits(rx_hexs) > 5) { + warn_at("too_many_digits", line, column - 1); + } + if (next_char() !== "}") { + stop_at("expected_a_before_b", line, column, "}", char); + } + return next_char(); + } + back_char(); + if (some_digits(rx_hexs, true) < 4) { + warn_at("expected_four_digits", line, column - 1); + } + return; + } + if (extra && extra.indexOf(char) >= 0) { + return next_char(); + } + warn_at("unexpected_a_before_b", line, column - 2, "\\", char); + } + + function make(id, value, identifier) { + +// Make the token object and append it to the tokens list. + + const the_token = { + from: from, + id: id, + identifier: Boolean(identifier), + line: line, + nr: nr, + thru: column + }; + tokens[nr] = the_token; + nr += 1; + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + directive_mode = false; + } + +// If the token is to have a value, give it one. + + if (value !== undefined) { + the_token.value = value; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option.white. + + if ( + previous.line === line + && previous.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (previous.id === "(comment)" || previous.id === "(regexp)") + ) { + warn( + "expected_space_a_b", + the_token, + artifact(previous), + artifact(the_token) + ); + } + if (previous.id === "." && id === "(number)") { + warn("expected_a_before_b", previous, "0", "."); + } + if (prior.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// The previous token is used to detect adjacency problems. + + previous = the_token; + +// The prior token is a previous token that was not a comment. The prior token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (previous.id !== "(comment)") { + prior = previous; + } + return the_token; + } + + function parse_directive(the_comment, body) { + +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + + const result = body.match(rx_directive_part); + if (result) { + let allowed; + const name = result[1]; + const value = result[2]; + if (the_comment.directive === "jslint") { + allowed = allowed_option[name]; + if ( + typeof allowed === "boolean" + || typeof allowed === "object" + ) { + if ( + value === "" + || value === "true" + || value === undefined + ) { + option[name] = true; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } else if (value === "false") { + option[name] = false; + } else { + warn("bad_option_a", the_comment, name + ":" + value); + } + } else { + warn("bad_option_a", the_comment, name); + } + } else if (the_comment.directive === "property") { + if (tenure === undefined) { + tenure = empty(); + } + tenure[name] = true; + } else if (the_comment.directive === "global") { + if (value) { + warn("bad_option_a", the_comment, name + ":" + value); + } + declared_globals[name] = false; + module_mode = the_comment; + } + return parse_directive(the_comment, result[3]); + } + if (body) { + return stop("bad_directive_a", the_comment, body); + } + } + + function comment(snippet) { + +// Make a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + + const the_comment = make("(comment)", snippet); + if (Array.isArray(snippet)) { + snippet = snippet.join(" "); + } + if (!option.devel && rx_todo.test(snippet)) { + warn("todo_comment", the_comment); + } + const result = snippet.match(rx_directive); + if (result) { + if (!directive_mode) { + warn_at("misplaced_directive_a", line, from, result[1]); + } else { + the_comment.directive = result[1]; + parse_directive(the_comment, result[2]); + } + directives.push(the_comment); + } + return the_comment; + } + + function regexp() { + +// Parse a regular expression literal. + + let multi_mode = false; + let result; + let value; + regexp_seen = true; + + function quantifier() { + +// Match an optional quantifier. + + if (char === "?" || char === "*" || char === "+") { + next_char(); + } else if (char === "{") { + if (some_digits(rx_digits, true) === 0) { + warn_at("expected_a", line, column, "0"); + } + if (next_char() === ",") { + some_digits(rx_digits, true); + next_char(); + } + next_char("}"); + } else { + return; + } + if (char === "?") { + next_char("?"); + } + } + + function subklass() { + +// Match a character in a character class. + + if (char === "\\") { + escape("BbDdSsWw-[]^"); + return true; + } + if ( + char === "" + || char === "[" + || char === "]" + || char === "/" + || char === "^" + || char === "-" + ) { + return false; + } + if (char === " ") { + warn_at("expected_a_b", line, column, "\\u0020", " "); + } else if (char === "`" && mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char(); + return true; + } + + function ranges() { + +// Match a range of subclasses. + + if (subklass()) { + if (char === "-") { + next_char("-"); + if (!subklass()) { + return stop_at( + "unexpected_a", + line, + column - 1, + "-" + ); + } + } + return ranges(); + } + } + + function klass() { + +// Match a class. + + next_char("["); + if (char === "^") { + next_char("^"); + } + (function classy() { + ranges(); + if (char !== "]" && char !== "") { + warn_at( + "expected_a_before_b", + line, + column, + "\\", + char + ); + next_char(); + return classy(); + } + }()); + next_char("]"); + } + + function choice() { + + function group() { + +// Match a group that starts with left paren. + + next_char("("); + if (char === "?") { + next_char("?"); + if (char === "=" || char === "!") { + next_char(); + } else { + next_char(":"); + } + } else if (char === ":") { + warn_at("expected_a_before_b", line, column, "?", ":"); + } + choice(); + next_char(")"); + } + + function factor() { + if ( + char === "" + || char === "/" + || char === "]" + || char === ")" + ) { + return false; + } + if (char === "(") { + group(); + return true; + } + if (char === "[") { + klass(); + return true; + } + if (char === "\\") { + escape("BbDdSsWw^${}[]():=!.- |*+?"); + return true; + } + if ( + char === "?" + || char === "+" + || char === "*" + || char === "}" + || char === "{" + ) { + warn_at( + "expected_a_before_b", + line, + column - 1, + "\\", + char + ); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column - 1, "`"); + } + } else if (char === " ") { + warn_at("expected_a_before_b", line, column, "\\", " "); + } else if (char === "$") { + if (source_line[0] !== "/") { + multi_mode = true; + } + } else if (char === "^") { + if (snippet !== "^") { + multi_mode = true; + } + } + next_char(); + return true; + } + + function sequence(follow) { + if (factor()) { + quantifier(); + return sequence(true); + } + if (!follow) { + warn_at("expected_regexp_factor_a", line, column, char); + } + } + +// Match a choice (a sequence that can be followed by | and another choice). + + sequence(); + if (char === "|") { + next_char("|"); + return choice(); + } + } + +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + next_char(); + if (char === "=") { + warn_at("expected_a_before_b", line, column, "\\", "="); + } + choice(); + +// Make sure there is a closing slash. + + snip(); + value = snippet; + next_char("/"); + +// Process dangling flag letters. + + const allowed = { + g: true, + i: true, + m: true, + u: true, + y: true + }; + const flag = empty(); + (function make_flag() { + if (is_letter(char)) { + if (allowed[char] !== true) { + warn_at("unexpected_a", line, column, char); + } + allowed[char] = false; + flag[char] = true; + next_char(); + return make_flag(); + } + }()); + back_char(); + if (char === "/" || char === "*") { + return stop_at("unexpected_a", line, from, char); + } + result = make("(regexp)", char); + result.flag = flag; + result.value = value; + if (multi_mode && !flag.m) { + warn_at("missing_m", line, column); + } + return result; + } + + function string(quote) { + +// Make a string token. + + let the_token; + snippet = ""; + next_char(); + + return (function next() { + if (char === quote) { + snip(); + the_token = make("(string)", snippet); + the_token.quote = quote; + return the_token; + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "\\") { + escape(quote); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char("`"); + } else { + next_char(); + } + return next(); + }()); + } + + function frack() { + if (char === ".") { + some_digits(rx_digits); + next_char(); + } + if (char === "E" || char === "e") { + next_char(); + if (char !== "+" && char !== "-") { + back_char(); + } + some_digits(rx_digits); + next_char(); + } + } + + function number() { + if (snippet === "0") { + next_char(); + if (char === ".") { + frack(); + } else if (char === "b") { + some_digits(rx_bits); + next_char(); + } else if (char === "o") { + some_digits(rx_octals); + next_char(); + } else if (char === "x") { + some_digits(rx_hexs); + next_char(); + } + } else { + next_char(); + frack(); + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + return stop_at( + "unexpected_a_after_b", + line, + column - 1, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + back_char(); + return make("(number)", snippet); + } + + function lex() { + let array; + let i = 0; + let j = 0; + let last; + let result; + let the_token; + if (!source_line) { + source_line = next_line(); + from = 0; + return ( + source_line === undefined + ? ( + mega_mode + ? stop_at("unclosed_mega", mega_line, mega_from) + : make("(end)") + ) + : lex() + ); + } + from = column; + result = source_line.match(rx_token); + +// result[1] token +// result[2] whitespace +// result[3] identifier +// result[4] number +// result[5] rest + + if (!result) { + return stop_at( + "unexpected_char_a", + line, + column, + source_line[0] + ); + } + + snippet = result[1]; + column += snippet.length; + source_line = result[5]; + +// Whitespace was matched. Call lex again to get more. + + if (result[2]) { + return lex(); + } + +// The token is an identifier. + + if (result[3]) { + return make(snippet, undefined, true); + } + +// The token is a number. + + if (result[4]) { + return number(snippet); + } + +// The token is a string. + + if (snippet === "\"") { + return string(snippet); + } + if (snippet === "'") { + if (!option.single) { + warn_at("use_double", line, column); + } + return string(snippet); + } + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (snippet === "`") { + if (mega_mode) { + return stop_at("expected_a_b", line, column, "}", "`"); + } + snippet = ""; + mega_from = from; + mega_line = line; + mega_mode = true; + +// Parsing a mega literal is tricky. First make a ` token. + + make("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + (function part() { + const at = source_line.search(rx_mega); + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + if (at < 0) { + snippet += source_line + "\n"; + return ( + next_line() === undefined + ? stop_at("unclosed_mega", mega_line, mega_from) + : part() + ); + } + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + snippet += source_line.slice(0, at); + column += at; + source_line = source_line.slice(at); + if (source_line[0] === "\\") { + stop_at("escape_mega", line, at); + } + make("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then make tokens that will become part of an expression until +// a } token is made. + + if (source_line[0] === "$") { + column += 2; + make("${"); + source_line = source_line.slice(2); + (function expr() { + const id = lex().id; + if (id === "{") { + return stop_at( + "expected_a_b", + line, + column, + "}", + "{" + ); + } + if (id !== "}") { + return expr(); + } + }()); + return part(); + } + }()); + source_line = source_line.slice(1); + column += 1; + mega_mode = false; + return make("`"); + } + +// The token is a // comment. + + if (snippet === "//") { + snippet = source_line; + source_line = ""; + the_token = comment(snippet); + if (mega_mode) { + warn("unexpected_comment", the_token, "`"); + } + return the_token; + } + +// The token is a /* comment. + + if (snippet === "/*") { + array = []; + if (source_line[0] === "/") { + warn_at("unexpected_a", line, column + i, "/"); + } + (function next() { + if (source_line > "") { + i = source_line.search(rx_star_slash); + if (i >= 0) { + return; + } + j = source_line.search(rx_slash_star); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + } + array.push(source_line); + source_line = next_line(); + if (source_line === undefined) { + return stop_at("unclosed_comment", line, column); + } + return next(); + }()); + snippet = source_line.slice(0, i); + j = snippet.search(rx_slash_star_or_slash); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + array.push(snippet); + column += i + 2; + source_line = source_line.slice(i + 2); + return comment(array); + } + +// The token is a slash. + + if (snippet === "/") { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + if (prior.identifier) { + if (!prior.dot) { + if (prior.id === "return") { + return regexp(); + } + if ( + prior.id === "(begin)" + || prior.id === "case" + || prior.id === "delete" + || prior.id === "in" + || prior.id === "instanceof" + || prior.id === "new" + || prior.id === "typeof" + || prior.id === "void" + || prior.id === "yield" + ) { + the_token = regexp(); + return stop("unexpected_a", the_token); + } + } + } else { + last = prior.id[prior.id.length - 1]; + if ("(,=:?[".indexOf(last) >= 0) { + return regexp(); + } + if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) { + the_token = regexp(); + warn("wrap_regexp", the_token); + return the_token; + } + } + if (source_line[0] === "/") { + column += 1; + source_line = source_line.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + } + return make(snippet); + } + + first = lex(); + json_mode = first.id === "{" || first.id === "["; + +// This is the only loop in JSLint. It will turn into a recursive call to lex +// when ES6 has been finished and widely deployed and adopted. + + while (true) { + if (lex().id === "(end)") { + break; + } + } +} + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + +function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!rx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!rx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property[id] === "number") { + property[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (tenure !== undefined) { + if (tenure[id] !== true) { + warn("unregistered_property_a", name); + } + } else { + if (name.identifier && rx_bad_property.test(id)) { + warn("bad_property_a", name); + } + } + property[id] = 1; + } + return id; +} + +function dispense() { + +// Deliver the next token, skipping the comments. + + const cadet = tokens[token_nr]; + token_nr += 1; + if (cadet.id === "(comment)") { + if (json_mode) { + warn("unexpected_a", cadet); + } + return dispense(); + } else { + return cadet; + } +} + +function lookahead() { + +// Look ahead one token without advancing. + + const old_token_nr = token_nr; + const cadet = dispense(true); + token_nr = old_token_nr; + return cadet; +} + +function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if (token.identifier && token.id !== "function") { + anon = token.id; + } else if (token.id === "(string)" && rx_identifier.test(token.value)) { + anon = token.value; + } + +// Attempt to match next_token with an expected id. + + if (id !== undefined && next_token.id !== id) { + return ( + match === undefined + ? stop("expected_a_b", next_token, id, artifact()) + : stop( + "expected_a_b_from_c_d", + next_token, + id, + artifact(match), + artifact_line(match), + artifact(next_token) + ) + ); + } + +// Promote the tokens, skipping comments. + + token = next_token; + next_token = dispense(); + if (next_token.id === "(end)") { + token_nr -= 1; + } +} + +// Parsing of JSON is simple: + +function json_value() { + let negative; + if (next_token.id === "{") { + return (function json_object() { + const brace = next_token; + const object = empty(); + const properties = []; + brace.expression = properties; + advance("{"); + if (next_token.id !== "}") { + (function next() { + let name; + let value; + if (next_token.quote !== "\"") { + warn( + "unexpected_a", + next_token, + next_token.quote + ); + } + name = next_token; + advance("(string)"); + if (object[token.value] !== undefined) { + warn("duplicate_a", token); + } else if (token.value === "__proto__") { + warn("bad_property_a", token); + } else { + object[token.value] = token; + } + advance(":"); + value = json_value(); + value.label = name; + properties.push(value); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("}", brace); + return brace; + }()); + } + if (next_token.id === "[") { + return (function json_array() { + const bracket = next_token; + const elements = []; + bracket.expression = elements; + advance("["); + if (next_token.id !== "]") { + (function next() { + elements.push(json_value()); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]", bracket); + return bracket; + }()); + } + if ( + next_token.id === "true" + || next_token.id === "false" + || next_token.id === "null" + ) { + advance(); + return token; + } + if (next_token.id === "(number)") { + if (!rx_JSON_number.test(next_token.value)) { + warn("unexpected_a"); + } + advance(); + return token; + } + if (next_token.id === "(string)") { + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + advance(); + return token; + } + if (next_token.id === "-") { + negative = next_token; + negative.arity = "unary"; + advance("-"); + advance("(number)"); + negative.expression = token; + return negative; + } + stop("unexpected_a"); +} + +// Now we parse JavaScript. + +function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + const id = name.id; + +// Reserved words may not be enrolled. + + if (syntax[id] !== undefined && id !== "ignore") { + warn("reserved_a", name); + } else { + +// Has the name been enrolled in this context? + + let earlier = functionage.context[id]; + if (earlier) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + +// Has the name been enrolled in an outer context? + + } else { + stack.forEach(function (value) { + const item = value.context[id]; + if (item !== undefined) { + earlier = item; + } + }); + if (earlier) { + if (id === "ignore") { + if (earlier.role === "variable") { + warn("unexpected_a", name); + } + } else { + if ( + ( + role !== "exception" + || earlier.role !== "exception" + ) + && role !== "parameter" + && role !== "function" + ) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + } + } + } + +// Enroll it. + + functionage.context[id] = name; + name.dead = true; + name.parent = functionage; + name.init = false; + name.role = role; + name.used = 0; + name.writable = !readonly; + } + } +} + +function expression(rbp, initial) { + +// This is the heart of the Pratt parser. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// nud Null denotation +// led Left denotation +// lbp Left binding power +// rbp Right binding power + +// It processes a nud (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop. It +// returns the expression's parse tree. + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement, + + if (!initial) { + advance(); + } + the_symbol = syntax[token.id]; + if (the_symbol !== undefined && the_symbol.nud !== undefined) { + left = the_symbol.nud(); + } else if (token.identifier) { + left = token; + left.arity = "variable"; + } else { + return stop("unexpected_a", token); + } + (function right() { + the_symbol = syntax[next_token.id]; + if ( + the_symbol !== undefined + && the_symbol.led !== undefined + && rbp < the_symbol.lbp + ) { + advance(); + left = the_symbol.led(left); + return right(); + } + }()); + return left; +} + +function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = next_token; + let the_value; + the_paren.free = true; + advance("("); + the_value = expression(0); + advance(")"); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (anticondition[the_value.id] === true) { + warn("unexpected_a", the_value); + } + return the_value; +} + +function is_weird(thing) { + return ( + thing.id === "(regexp)" + || thing.id === "{" + || thing.id === "=>" + || thing.id === "function" + || (thing.id === "[" && thing.arity === "unary") + ); +} + +function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + return ( + Array.isArray(b) + && a.length === b.length + && a.every(function (value, index) { + return are_similar(value, b[index]); + }) + ); + } + if (Array.isArray(b)) { + return false; + } + if (a.id === "(number)" && b.id === "(number)") { + return a.value === b.value; + } + let a_string; + let b_string; + if (a.id === "(string)") { + a_string = a.value; + } else if (a.id === "`" && a.constant) { + a_string = a.value[0]; + } + if (b.id === "(string)") { + b_string = b.value; + } else if (b.id === "`" && b.constant) { + b_string = b.value[0]; + } + if (typeof a_string === "string") { + return a_string === b_string; + } + if (is_weird(a) || is_weird(b)) { + return false; + } + if (a.arity === b.arity && a.id === b.id) { + if (a.id === ".") { + return ( + are_similar(a.expression, b.expression) + && are_similar(a.name, b.name) + ); + } + if (a.arity === "unary") { + return are_similar(a.expression, b.expression); + } + if (a.arity === "binary") { + return ( + a.id !== "(" + && are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + ); + } + if (a.arity === "ternary") { + return ( + are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + && are_similar(a.expression[2], b.expression[2]) + ); + } + if (a.arity === "function" && a.arity === "regexp") { + return false; + } + return true; + } + return false; +} + +function semicolon() { + +// Try to match a semicolon. + + if (next_token.id === ";") { + advance(";"); + } else { + warn_at( + "expected_a_b", + token.line, + token.thru, + ";", + artifact(next_token) + ); + } + anon = "anonymous"; +} + +function statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token.identifier && next_token.id === ":") { + the_label = token; + if (the_label.id === "ignore") { + warn("unexpected_a", the_label); + } + advance(":"); + if ( + next_token.id === "do" + || next_token.id === "for" + || next_token.id === "switch" + || next_token.id === "while" + ) { + enroll(the_label, "label", true); + the_label.init = true; + the_label.dead = false; + the_statement = statement(); + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token; + first.statement = true; + the_symbol = syntax[first.id]; + if (the_symbol !== undefined && the_symbol.fud !== undefined) { + the_symbol.disrupt = false; + the_symbol.statement = true; + the_statement = the_symbol.fud(); + } else { + +// It is an expression statement. + + the_statement = expression(0, true); + if (the_statement.wrapped && the_statement.id !== "(") { + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; +} + +function statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const array = []; + (function next(disrupt) { + if ( + next_token.id !== "}" + && next_token.id !== "case" + && next_token.id !== "default" + && next_token.id !== "else" + && next_token.id !== "(end)" + ) { + let a_statement = statement(); + array.push(a_statement); + if (disrupt) { + warn("unreachable_a", a_statement); + } + return next(a_statement.disrupt); + } + }(false)); + return array; +} + +function not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === global) { + warn("unexpected_at_top_level_a", thing); + } +} + +function top_level_only(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== global) { + warn("misplaced_a", the_thing); + } +} + +function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token; + the_block.arity = "statement"; + the_block.body = special === "body"; + +// All top level function bodies should include the "use strict" pragma unless +// the whole file is strict or the file is a module or the function parameters +// use es6 syntax. + + if ( + special === "body" + && stack.length === 1 + && next_token.value === "use strict" + ) { + the_block.strict = next_token; + next_token.statement = true; + advance("(string)"); + advance(";"); + } + stmts = statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option.devel && special !== "ignore") { + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; +} + +function mutation_check(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + warn("bad_assignment_a", the_thing); + return false; + } + return true; +} + +function left_check(left, right) { + +// Warn if the left is not one of these: +// e.b +// e[b] +// e() +// ?: +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !left_check(left.expression[1]) + && !left_check(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right); + return false; + } + return true; +} + +// These functions are used to specify the grammar of our language: + +function symbol(id, bp) { + +// Make a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax[id] = the_symbol; + } + return the_symbol; +} + +function assignment(id) { + +// Make an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led = function (left) { + const the_token = token; + let right; + the_token.arity = "assignment"; + right = expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "pre" + || right.arity === "post" + ) { + warn("unexpected_a", right); + } + mutation_check(left); + return the_token; + }; + return the_symbol; +} + +function constant(id, type, value) { + +// Make a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud = ( + typeof value === "function" + ? value + : function () { + token.constant = true; + if (value !== undefined) { + token.value = value; + } + return token; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; +} + +function infix(id, bp, f) { + +// Make an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, expression(bp)]; + return the_token; + }; + return the_symbol; +} + +function infixr(id, bp) { + +// Make a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + the_token.expression = [left, expression(bp - 1)]; + return the_token; + }; + return the_symbol; +} + +function post(id) { + +// Make one of the post operators. + + const the_symbol = symbol(id, 150); + the_symbol.led = function (left) { + token.expression = left; + token.arity = "post"; + mutation_check(token.expression); + return token; + }; + return the_symbol; +} + +function pre(id) { + +// Make one of the pre operators. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "pre"; + the_token.expression = expression(150); + mutation_check(the_token.expression); + return the_token; + }; + return the_symbol; +} + +function prefix(id, f) { + +// Make a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = expression(150); + return the_token; + }; + return the_symbol; +} + +function stmt(id, f) { + +// Make a statement. + + const the_symbol = symbol(id); + the_symbol.fud = function () { + token.arity = "statement"; + return f(); + }; + return the_symbol; +} + +function ternary(id1, id2) { + +// Make a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led = function (left) { + const the_token = token; + const second = expression(20); + advance(id2); + token.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, expression(10)]; + return the_token; + }; + return the_symbol; +} + +// Begin defining the language. + +syntax = empty(); + +symbol("}"); +symbol(")"); +symbol("]"); +symbol(","); +symbol(";"); +symbol(":"); +symbol("*/"); +symbol("await"); +symbol("case"); +symbol("catch"); +symbol("class"); +symbol("default"); +symbol("else"); +symbol("enum"); +symbol("finally"); +symbol("implements"); +symbol("interface"); +symbol("package"); +symbol("private"); +symbol("protected"); +symbol("public"); +symbol("static"); +symbol("super"); +symbol("void"); +symbol("yield"); + +constant("(number)", "number"); +constant("(regexp)", "regexp"); +constant("(string)", "string"); +constant("arguments", "object", function () { + warn("unexpected_a", token); + return token; +}); +constant("eval", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("false", "boolean", false); +constant("Function", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("ignore", "undefined", function () { + warn("unexpected_a", token); + return token; +}); +constant("Infinity", "number", Infinity); +constant("isFinite", "function", function () { + warn("expected_a_b", token, "Number.isFinite", "isFinite"); + return token; +}); +constant("isNaN", "function", function () { + warn("number_isNaN", token); + return token; +}); +constant("NaN", "number", NaN); +constant("null", "null", null); +constant("this", "object", function () { + if (!option.this) { + warn("unexpected_a", token); + } + return token; +}); +constant("true", "boolean", true); +constant("undefined", "undefined"); + +assignment("="); +assignment("+="); +assignment("-="); +assignment("*="); +assignment("/="); +assignment("%="); +assignment("&="); +assignment("|="); +assignment("^="); +assignment("<<="); +assignment(">>="); +assignment(">>>="); + +infix("||", 40); +infix("&&", 50); +infix("|", 70); +infix("^", 80); +infix("&", 90); +infix("==", 100); +infix("===", 100); +infix("!=", 100); +infix("!==", 100); +infix("<", 110); +infix(">", 110); +infix("<=", 110); +infix(">=", 110); +infix("in", 110); +infix("instanceof", 110); +infix("<<", 120); +infix(">>", 120); +infix(">>>", 120); +infix("+", 130); +infix("-", 130); +infix("*", 140); +infix("/", 140); +infix("%", 140); +infixr("**", 150); +infix("(", 160, function (left) { + const the_paren = token; + let the_argument; + if (left.id !== "function") { + left_check(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (next_token.id !== ")") { + (function next() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + the_paren.free = true; + if (the_argument.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + the_paren.free = false; + } + return the_paren; +}); +infix(".", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("[", 170, function (left) { + const the_token = token; + const the_subscript = expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + const name = survey(the_subscript); + if (rx_identifier.test(name)) { + warn("subscript_a", the_subscript, name); + } + } + left_check(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; +}); +infix("=>", 170, function (left) { + return stop("wrap_parameter", left); +}); + +function do_tick() { + const the_tick = token; + the_tick.value = []; + the_tick.expression = []; + if (next_token.id !== "`") { + (function part() { + advance("(string)"); + the_tick.value.push(token); + if (next_token.id === "${") { + advance("${"); + the_tick.expression.push(expression(0)); + advance("}"); + return part(); + } + }()); + } + advance("`"); + return the_tick; +} + +infix("`", 160, function (left) { + const the_tick = do_tick(); + left_check(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; +}); + +post("++"); +post("--"); +pre("++"); +pre("--"); + +prefix("+"); +prefix("-"); +prefix("~"); +prefix("!"); +prefix("!!"); +prefix("[", function () { + const the_token = token; + the_token.expression = []; + if (next_token.id !== "]") { + (function next() { + let element; + let ellipsis = false; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + element = expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]"); + return the_token; +}); +prefix("/=", function () { + stop("expected_a_b", token, "/\\=", "/="); +}); +prefix("=>", function () { + return stop("expected_a_before_b", token, "()", "=>"); +}); +prefix("new", function () { + const the_new = token; + const right = expression(160); + if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "()", artifact(next_token)); + } + the_new.expression = right; + return the_new; +}); +prefix("typeof"); +prefix("void", function () { + const the_void = token; + warn("unexpected_a", the_void); + the_void.expression = expression(0); + return the_void; +}); + +function parameter_list() { + let complex = false; + const list = []; + let optional; + const signature = ["("]; + if (next_token.id !== ")" && next_token.id !== "(end)") { + (function parameter() { + let ellipsis = false; + let param; + if (next_token.id === "{") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("{"); + signature.push("{"); + (function subparameter() { + let subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (next_token.id === ":") { + advance(":"); + advance(); + token.label = subparam; + subparam = token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + } + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return subparameter(); + } + }()); + list.push(param); + advance("}"); + signature.push("}"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else if (next_token.id === "[") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("["); + signature.push("[]"); + (function subparameter() { + const subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + return subparameter(); + } + }()); + list.push(param); + advance("]"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else { + if (next_token.id === "...") { + complex = true; + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + } + if (!next_token.identifier) { + return stop("expected_identifier_a"); + } + param = next_token; + list.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (next_token.id === "=") { + complex = true; + optional = param; + advance("="); + param.expression = expression(0); + } else { + if (optional !== undefined) { + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } + } + }()); + } + advance(")"); + signature.push(")"); + return [list, signature.join(""), complex]; +} + +function do_function(the_function) { + let name; + if (the_function === undefined) { + the_function = token; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + name = next_token; + enroll(name, "variable", true); + the_function.name = name; + name.init = true; + name.calls = empty(); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + if (next_token.identifier) { + name = next_token; + the_function.name = name; + advance(); + } else { + the_function.name = anon; + } + } + } else { + name = the_function.name; + } + the_function.level = functionage.level + 1; + if (mega_mode) { + warn("unexpected_a", the_function); + } + +// Don't make functions in loops. It is inefficient, and it can lead to scoping +// errors. + + if (functionage.loop > 0) { + warn("function_in_loop", the_function); + } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + the_function.context = empty(); + the_function.finally = 0; + the_function.loop = 0; + the_function.switch = 0; + the_function.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functions.push(the_function); + functionage = the_function; + if (the_function.arity !== "statement" && typeof name === "object") { + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// Parse the parameter list. + + advance("("); + token.free = false; + token.arity = "function"; + const pl = parameter_list(); + functionage.parameters = pl[0]; + functionage.signature = pl[1]; + functionage.complex = pl[2]; + functionage.parameters.forEach(function enroll_parameter(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + name.names.forEach(enroll_parameter); + } + }); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && next_token.line === token.line + ) { + return stop("unexpected_a", next_token); + } + if (next_token.id === "." || next_token.id === "[") { + warn("unexpected_a"); + } + +// Restore the previous context. + + functionage = stack.pop(); + return the_function; +} + +prefix("function", do_function); + +function fart(pl) { + if (next_token.id === ";") { + stop("wrap_assignment", token); + } + advance("=>"); + const the_fart = token; + the_fart.arity = "binary"; + the_fart.name = "=>"; + the_fart.level = functionage.level + 1; + functions.push(the_fart); + if (functionage.loop > 0) { + warn("function_in_loop", the_fart); + } + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + the_fart.context = empty(); + the_fart.finally = 0; + the_fart.loop = 0; + the_fart.switch = 0; + the_fart.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functionage = the_fart; + the_fart.parameters = pl[0]; + the_fart.signature = pl[1]; + the_fart.complex = true; + the_fart.parameters.forEach(function (name) { + enroll(name, "parameter", true); + }); + if (next_token.id === "{") { + warn("expected_a_b", the_fart, "function", "=>"); + the_fart.block = block("body"); + } else { + the_fart.expression = expression(0); + } + functionage = stack.pop(); + return the_fart; +} + +prefix("(", function () { + const the_paren = token; + let the_value; + const cadet = lookahead().id; + +// We can distinguish between a parameter list for => and a wrapped expression +// with one token of lookahead. + + if ( + next_token.id === ")" + || next_token.id === "..." + || (next_token.identifier && (cadet === "," || cadet === "=")) + ) { + the_paren.free = false; + return fart(parameter_list()); + } + the_paren.free = true; + the_value = expression(0); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + if (next_token.id === "=>") { + if (the_value.arity !== "variable") { + if (the_value.id === "{" || the_value.id === "[") { + warn("expected_a_before_b", the_paren, "function", "("); + return stop("expected_a_b", next_token, "{", "=>"); + } + return stop("expected_identifier_a", the_value); + } + the_paren.expression = [the_value]; + return fart([the_paren.expression, "(" + the_value.id + ")"]); + } + return the_value; +}); +prefix("`", do_tick); +prefix("{", function () { + const the_brace = token; + const seen = empty(); + the_brace.expression = []; + if (next_token.id !== "}") { + (function member() { + let extra; + let full; + let id; + let name = next_token; + let value; + advance(); + if ( + (name.id === "get" || name.id === "set") + && next_token.identifier + ) { + if (!option.getset) { + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + next_token.id; + name = next_token; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (next_token.id === "}" || next_token.id === ",") { + if (typeof extra === "string") { + advance("("); + } + value = expression(Infinity, true); + } else if (next_token.id === "(") { + value = do_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + advance("("); + } + advance(":"); + value = expression(0); + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + advance(":"); + value = expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (next_token.id === ",") { + advance(","); + return member(); + } + }()); + } + advance("}"); + return the_brace; +}); + +stmt(";", function () { + warn("unexpected_a", token); + return token; +}); +stmt("{", function () { + warn("naked_block", token); + return block("naked"); +}); +stmt("break", function () { + const the_break = token; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (next_token.identifier && token.line === next_token.line) { + the_label = functionage.context[next_token.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + warn( + (the_label !== undefined && the_label.dead) + ? "out_of_scope_a" + : "not_label_a" + ); + } else { + the_label.used += 1; + } + the_break.label = next_token; + advance(); + } + advance(";"); + return the_break; +}); + +function do_var() { + const the_statement = token; + const is_const = the_statement.id === "const"; + the_statement.names = []; + +// A program may use var or let, but not both. + + if (!is_const) { + if (var_mode === undefined) { + var_mode = the_statement.id; + } else if (the_statement.id !== var_mode) { + warn( + "expected_a_b", + the_statement, + var_mode, + the_statement.id + ); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + warn("var_switch", the_statement); + } + if (functionage.loop > 0 && the_statement.id === "var") { + warn("var_loop", the_statement); + } + (function next() { + if (next_token.id === "{" && the_statement.id !== "var") { + const the_brace = next_token; + the_brace.names = []; + advance("{"); + (function pair() { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + survey(name); + advance(); + if (next_token.id === ":") { + advance(":"); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + next_token.label = name; + the_brace.names.push(next_token); + enroll(next_token, "variable", is_const); + advance(); + } else { + the_brace.names.push(name); + enroll(name, "variable", is_const); + } + if (next_token.id === ",") { + advance(","); + return pair(); + } + }()); + advance("}"); + advance("="); + the_brace.expression = expression(0); + the_statement.names.push(the_brace); + } else if (next_token.id === "[" && the_statement.id !== "var") { + const the_bracket = next_token; + the_bracket.names = []; + advance("["); + (function element() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + advance(); + the_bracket.names.push(name); + enroll(name, "variable", the_statement.id === "const"); + if (ellipsis) { + name.ellipsis = true; + } else if (next_token.id === ",") { + advance(","); + return element(); + } + }()); + advance("]"); + advance("="); + the_bracket.expression = expression(0); + the_statement.names.push(the_bracket); + } else if (next_token.identifier) { + const name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", is_const); + if (next_token.id === "=" || is_const) { + advance("="); + name.init = true; + name.expression = expression(0); + } + the_statement.names.push(name); + } else { + return stop("expected_identifier_a", next_token); + } + if (next_token.id === ",") { + if (!option.multivar) { + warn("expected_a_b", next_token, ";", ","); + } + advance(","); + return next(); + } + }()); + the_statement.open = ( + the_statement.names.length > 1 + && the_statement.line !== the_statement.names[1].line + ); + semicolon(); + return the_statement; +} + +stmt("const", do_var); +stmt("continue", function () { + const the_continue = token; + if (functionage.loop < 1 || functionage.finally > 0) { + warn("unexpected_a", the_continue); + } + not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; +}); +stmt("debugger", function () { + const the_debug = token; + if (!option.devel) { + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; +}); +stmt("delete", function () { + const the_token = token; + const the_value = expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; +}); +stmt("do", function () { + const the_do = token; + not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; +}); +stmt("export", function () { + const the_export = token; + let the_id; + let the_name; + let the_thing; + + function export_id() { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + the_id = next_token.id; + the_name = global.context[the_id]; + if (the_name === undefined) { + warn("unexpected_a"); + } else { + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a"); + } + exports[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + } + + the_export.expression = []; + if (next_token.id === "default") { + if (exports.default !== undefined) { + warn("duplicate_a"); + } + advance("default"); + the_thing = expression(0); + semicolon(); + exports.default = the_thing; + the_export.expression.push(the_thing); + } else { + if (next_token.id === "function") { + the_thing = statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a", the_name); + } + exports[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + warn("unexpected_a"); + } else if (next_token.id === "{") { + advance("{"); + (function loop() { + export_id(); + if (next_token.id === ",") { + advance(","); + return loop(); + } + }()); + advance("}"); + semicolon(); + } else { + export_id(); + if (the_name.writable !== true) { + warn("unexpected_a", token); + } + semicolon(); + } + } + module_mode = true; + return the_export; +}); +stmt("for", function () { + let first; + const the_for = token; + if (!option.for) { + warn("unexpected_a", the_for); + } + not_top_level(the_for); + functionage.loop += 1; + advance("("); + token.free = true; + if (next_token.id === ";") { + return stop("expected_a_b", the_for, "while (", "for (;"); + } + if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + return stop("unexpected_a"); + } + first = expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = expression(0); + advance(";"); + the_for.inc = expression(0); + if (the_for.inc.id === "++") { + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; +}); +stmt("function", do_function); +stmt("if", function () { + let the_else; + const the_if = token; + the_if.expression = condition(); + the_if.block = block(); + if (next_token.id === "else") { + advance("else"); + the_else = token; + the_if.else = ( + next_token.id === "if" + ? statement() + : block() + ); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + the_if.disrupt = true; + } else { + warn("unexpected_a", the_else); + } + } + } + return the_if; +}); +stmt("import", function () { + const the_import = token; + let name; + if (typeof module_mode === "object") { + warn("unexpected_directive_a", module_mode, module_mode.directive); + } + module_mode = true; + if (next_token.identifier) { + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + const names = []; + advance("{"); + if (next_token.id !== "}") { + while (true) { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (next_token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token; + if (!rx_module.test(token.value)) { + warn("bad_module_name_a", token); + } + froms.push(token.value); + semicolon(); + return the_import; +}); +stmt("let", do_var); +stmt("return", function () { + const the_return = token; + not_top_level(the_return); + if (functionage.finally > 0) { + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (next_token.id !== ";" && the_return.line === next_token.line) { + the_return.expression = expression(10); + } + advance(";"); + return the_return; +}); +stmt("switch", function () { + let dups = []; + let last; + let stmts; + const the_cases = []; + let the_disrupt = true; + const the_switch = token; + not_top_level(the_switch); + if (functionage.finally > 0) { + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token.free = true; + the_switch.expression = expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + (function major() { + const the_case = next_token; + the_case.arity = "statement"; + the_case.expression = []; + (function minor() { + advance("case"); + token.switch = true; + const exp = expression(0); + if (dups.some(function (thing) { + return are_similar(thing, exp); + })) { + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (next_token.id === "case") { + return minor(); + } + }()); + stmts = statements(); + if (stmts.length < 1) { + warn("expected_statements_a"); + return; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "break;", + artifact(next_token) + ); + } + if (next_token.id === "case") { + return major(); + } + }()); + dups = undefined; + if (next_token.id === "default") { + const the_default = next_token; + advance("default"); + token.switch = true; + advance(":"); + the_switch.else = statements(); + if (the_switch.else.length < 1) { + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + const the_last = the_switch.else[the_switch.else.length - 1]; + if (the_last.id === "break" && the_last.label === undefined) { + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; +}); +stmt("throw", function () { + const the_throw = token; + the_throw.disrupt = true; + the_throw.expression = expression(10); + semicolon(); + if (functionage.try > 0) { + warn("unexpected_a", the_throw); + } + return the_throw; +}); +stmt("try", function () { + let the_catch; + let the_disrupt; + const the_try = token; + if (functionage.try > 0) { + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (next_token.id === "catch") { + let ignored = "ignore"; + the_catch = next_token; + the_try.catch = the_catch; + advance("catch"); + advance("("); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + if (next_token.id !== "ignore") { + ignored = undefined; + the_catch.name = next_token; + enroll(next_token, "exception", true); + } + advance(); + advance(")"); + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "catch", + artifact(next_token) + ); + + } + if (next_token.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; +}); +stmt("var", do_var); +stmt("while", function () { + const the_while = token; + not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; +}); +stmt("with", function () { + stop("unexpected_a", token); +}); + +ternary("?", ":"); + +// Ambulation of the parse tree. + +function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; +} + +function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all of the +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + } + }; +} + +const posts = empty(); +const pres = empty(); +const preaction = action(pres); +const postaction = action(posts); +const preamble = amble(pres); +const postamble = amble(posts); + +function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.id === "function") { + walk_statement(thing.block); + } + if (thing.arity === "pre" || thing.arity === "post") { + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + warn("unexpected_statement_a", thing); + } + postamble(thing); + } + } +} + +function walk_statement(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_statement); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + ) { + warn( + (thing.id === "(string)" && thing.value === "use strict") + ? "unexpected_a" + : "unexpected_expression_a", + thing + ); + } + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + } +} + +function lookup(thing) { + if (thing.arity === "variable") { + +// Look up the variable in the current context. + + let the_variable = functionage.context[thing.id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable === undefined) { + stack.forEach(function (outer) { + const a_variable = outer.context[thing.id]; + if ( + a_variable !== undefined + && a_variable.role !== "label" + ) { + the_variable = a_variable; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (the_variable === undefined) { + if (declared_globals[thing.id] === undefined) { + warn("undeclared_a", thing); + return; + } + the_variable = { + dead: false, + parent: global, + id: thing.id, + init: true, + role: "variable", + used: 0, + writable: false + }; + global.context[thing.id] = the_variable; + } + the_variable.closure = true; + functionage.context[thing.id] = the_variable; + } else if (the_variable.role === "label") { + warn("label_a", thing); + } + if ( + the_variable.dead + && ( + the_variable.calls === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + ) { + warn("out_of_scope_a", thing); + } + return the_variable; + } +} + +function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); +} + +function preaction_function(thing) { + if (thing.arity === "statement" && blockage.body !== true) { + warn("unexpected_a", thing); + } + if (thing.level === 1) { + if ( + module_mode === true + || global.strict !== undefined + || thing.complex + ) { + if (thing.id !== "=>" && thing.block.strict !== undefined) { + warn("unexpected_a", thing.block.strict); + } + } else { + if (thing.block.strict === undefined) { + warn("use_strict", thing); + } + } + } + stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); +} + +function bitwise_check(thing) { + if (!option.bitwise && bitwiseop[thing.id] === true) { + warn("unexpected_a", thing); + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + warn("unexpected_a", thing); + } +} + +function pop_block() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); +} + +function activate(name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.init = true; + } + } + blockage.live.push(name); +} + +function action_var(thing) { + thing.names.forEach(activate); +} + +preaction("assignment", bitwise_check); +preaction("binary", bitwise_check); +preaction("binary", function (thing) { + if (relationop[thing.id] === true) { + const left = thing.expression[0]; + const right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + warn("expected_string_a", right); + } + } else { + const value = right.value; + if (value === "null" || value === "undefined") { + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + warn("expected_type_string_a", right, value); + } + } + } + } +}); +preaction("binary", "==", function (thing) { + warn("expected_a_b", thing, "===", "=="); +}); +preaction("binary", "!=", function (thing) { + warn("expected_a_b", thing, "!==", "!="); +}); +preaction("binary", "=>", preaction_function); +preaction("binary", "||", function (thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + warn("and", thang); + } + }); +}); +preaction("binary", "(", function (thing) { + const left = thing.expression[0]; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + const parent = functionage.name.parent; + if (parent) { + const left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + && left_variable.dead + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } +}); +preaction("binary", "in", function (thing) { + warn("infix_in", thing); +}); +preaction("binary", "instanceof", function (thing) { + warn("unexpected_a", thing); +}); +preaction("binary", ".", function (thing) { + if (thing.expression.new) { + thing.new = true; + } +}); +preaction("statement", "{", function (thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; +}); +preaction("statement", "for", function (thing) { + if (thing.name !== undefined) { + const the_variable = lookup(thing.name); + if (the_variable !== undefined) { + the_variable.init = true; + if (!the_variable.writable) { + warn("bad_assignment_a", thing.name); + } + } + } + walk_statement(thing.initial); +}); +preaction("statement", "function", preaction_function); +preaction("unary", "~", bitwise_check); +preaction("unary", "function", preaction_function); +preaction("variable", function (thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } +}); + +function init_variable(name) { + const the_variable = lookup(name); + if (the_variable !== undefined) { + if (the_variable.writable) { + the_variable.init = true; + return; + } + } + warn("bad_assignment_a", name); +} + +postaction("assignment", "+=", function (thing) { + let right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } +}); +postaction("assignment", function (thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + if (thing.id === "=") { + if (thing.names !== undefined) { + if (Array.isArray(thing.names)) { + thing.names.forEach(init_variable); + } else { + init_variable(thing.names); + } + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.writable !== true) { + warn("bad_assignment_a", lvalue); + } + } + const right = syntax[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + warn("unexpected_a", thing.expression[1]); + } + } +}); + +function postaction_function(thing) { + delete functionage.finally; + delete functionage.loop; + delete functionage.switch; + delete functionage.try; + functionage = stack.pop(); + if (thing.wrapped) { + warn("unexpected_parens", thing); + } + return pop_block(); +} + +postaction("binary", function (thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || are_similar(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option.convert) { + if (thing.expression[0].value === "") { + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === ".") { + if (thing.expression.id === "RegExp") { + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } +}); +postaction("binary", "&&", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "||", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "=>", postaction_function); +postaction("binary", "(", function (thing) { + let left = thing.expression[0]; + let the_new; + let arg; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option.eval) { + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + warn( + "expected_a_before_b", + left, + "new", + artifact(left) + ); + } + } + } else if (left.id === ".") { + let cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + cack = !cack; + } + if (rx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + warn("unexpected_a", the_new); + } else { + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + const paren = left.expression; + if (paren.id === "(") { + const array = paren.expression; + if (array.length === 1) { + const new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } +}); +postaction("binary", "[", function (thing) { + if (thing.expression[0].id === "RegExp") { + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + warn("weird_expression_a", thing.expression[1]); + } +}); +postaction("statement", "{", pop_block); +postaction("statement", "const", action_var); +postaction("statement", "export", top_level_only); +postaction("statement", "for", function (thing) { + walk_statement(thing.inc); +}); +postaction("statement", "function", postaction_function); +postaction("statement", "import", function (the_thing) { + const name = the_thing.name; + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return top_level_only(the_thing); +}); +postaction("statement", "let", action_var); +postaction("statement", "try", function (thing) { + if (thing.catch !== undefined) { + const the_name = thing.catch.name; + if (the_name !== undefined) { + const the_variable = functionage.context[the_name.id]; + the_variable.dead = false; + the_variable.init = true; + } + walk_statement(thing.catch.block); + } +}); +postaction("statement", "var", action_var); +postaction("ternary", function (thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || are_similar(thing.expression[1], thing.expression[2]) + ) { + warn("unexpected_a", thing); + } else if (are_similar(thing.expression[0], thing.expression[1])) { + warn("expected_a_b", thing, "||", "?"); + } else if (are_similar(thing.expression[0], thing.expression[2])) { + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + warn("wrap_condition", thing.expression[0]); + } +}); +postaction("unary", function (thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option.convert) { + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } +}); +postaction("unary", "function", postaction_function); +postaction("unary", "+", function (thing) { + if (!option.convert) { + warn("expected_a_b", thing, "Number(...)", "+"); + } + const right = thing.expression; + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } +}); + +function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + if (id !== "ignore") { + const name = the_function.context[id]; + if (name.parent === the_function) { + if ( + name.used === 0 + && ( + name.role !== "function" + || name.parent.arity !== "unary" + ) + ) { + warn("unused_a", name); + } else if (!name.init) { + warn("uninitialized_a", name); + } + } + } + }); +} + +function uninitialized_and_unused() { + +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (module_mode === true || option.node) { + delve(global); + } + functions.forEach(delve); +} + +// Go through the token list, looking at usage of whitespace. + +function whitage() { + let closer = "(end)"; + let free = false; + let left = global; + let margin = 0; + let nr_comments_skipped = 0; + let open = true; + let right; + + function expected_at(at) { + warn( + "expected_a_at_b_c", + right, + artifact(right), + fudge + at, + artifact_column(right) + ); + } + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function no_space() { + if (left.line === right.line) { + if (left.thru !== right.from && nr_comments_skipped === 0) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (open) { + const at = ( + free + ? margin + : margin + 8 + ); + if (right.from < at) { + expected_at(at); + } + } else { + if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (free) { + if (right.from < margin) { + expected_at(margin); + } + } else { + const mislaid = ( + stack.length > 0 + ? stack[stack.length - 1].right + : undefined + ); + if (!open && mislaid !== undefined) { + warn( + "expected_a_next_at_b", + mislaid, + artifact(mislaid.id), + margin + 4 + fudge + ); + } else if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + stack = []; + tokens.forEach(function (the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + + const new_closer = opener[left.id]; + if (typeof new_closer === "string") { + if (new_closer !== right.id) { + stack.push({ + closer: closer, + free: free, + margin: margin, + open: open, + right: right + }); + closer = new_closer; + if (left.line !== right.line) { + free = closer === ")" && left.free; + open = true; + margin += 4; + if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (right.switch) { + at_margin(-4); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + free = false; + open = false; + no_space_only(); + } + } else { + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like {]) have already been detected. + + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } + } else { + +// If right is a closer, then pop the previous state. + + if (right.id === closer) { + const previous = stack.pop(); + margin = previous.margin; + if (open && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + closer = previous.closer; + free = previous.free; + open = previous.open; + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-4); + } else if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + one_space(); + } else { + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + at_margin(0); + } else { + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + no_space(); + } else if ( + left.id === "." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + ) { + no_space_only(); + } else if (right.id === ".") { + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } else if (left.id === ";") { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + one_space_only(); + } else if (right.statement === true) { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.id === "var" + || left.id === "const" + || left.id === "let" + ) { + stack.push({ + closer: closer, + free: free, + margin: margin, + open: open + }); + closer = ";"; + free = false; + open = left.open; + if (open) { + margin = margin + 4; + at_margin(0); + } else { + one_space_only(); + } + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +// The jslint function itself. + +export default function jslint( + source = "", + option_object = empty(), + global_array = [] +) { + try { + warnings = []; + option = Object.assign(empty(), option_object); + anon = "anonymous"; + block_stack = []; + declared_globals = empty(); + directive_mode = true; + directives = []; + early_stop = true; + exports = empty(); + froms = []; + fudge = ( + option.fudge + ? 1 + : 0 + ); + functions = []; + global = { + id: "(global)", + body: true, + context: empty(), + from: 0, + level: 0, + line: 0, + live: [], + loop: 0, + switch: 0, + thru: 0 + }; + blockage = global; + functionage = global; + json_mode = false; + mega_mode = false; + module_mode = false; + next_token = global; + property = empty(); + shebang = false; + stack = []; + tenure = undefined; + token = global; + token_nr = 0; + var_mode = undefined; + populate(standard, declared_globals, false); + populate(global_array, declared_globals, false); + Object.keys(option).forEach(function (name) { + if (option[name] === true) { + const allowed = allowed_option[name]; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } + }); + tokenize(source); + advance(); + if (json_mode) { + tree = json_value(); + advance("(end)"); + } else { + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option.browser) { + if (next_token.id === ";") { + advance(";"); + } + } else { + +// If we are not in a browser, then the file form of strict pragma may be used. + + if ( + next_token.value === "use strict" + ) { + global.strict = next_token; + advance("(string)"); + advance(";"); + } + } + tree = statements(); + advance("(end)"); + functionage = global; + walk_statement(tree); + if (module_mode && global.strict !== undefined) { + warn("unexpected_a", global.strict); + } + if (warnings.length === 0) { + uninitialized_and_unused(); + if (!option.white) { + whitage(); + } + } + } + if (!option.browser) { + directives.forEach(function (comment) { + if (comment.directive === "global") { + warn("missing_browser", comment); + } + }); + } + early_stop = false; + } catch (e) { + if (e.name !== "JSLintError") { + warnings.push(e); + } + } + return { + directives, + edition: "2018-09-24", + exports, + froms, + functions, + global, + id: "(JSLint)", + json: json_mode, + lines, + module: module_mode === true, + ok: warnings.length === 0 && !early_stop, + option, + property, + shebang: ( + shebang + ? lines[0] + : undefined + ), + stop: early_stop, + tokens, + tree, + warnings: warnings.sort(function (a, b) { + return a.line - b.line || a.column - b.column; + }) + }; +}; diff --git a/branch.revert_regexp_single_whitespace/report.js b/branch.revert_regexp_single_whitespace/report.js new file mode 100644 index 000000000..f123e9055 --- /dev/null +++ b/branch.revert_regexp_single_whitespace/report.js @@ -0,0 +1,221 @@ +// report.js +// 2018-07-29 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Generate JSLint HTML reports. + +/*property + closure, column, context, edition, error, exports, filter, forEach, froms, + fudge, function, functions, global, id, isArray, join, json, keys, length, + level, line, lines, message, module, name, names, option, parameters, + parent, property, push, replace, role, signature, sort, stop, warnings +*/ + +const rx_amp = /&/g; +const rx_gt = />/g; +const rx_lt = / with less destructive entities. + + return String( + string + ).replace( + rx_amp, + "&" + ).replace( + rx_lt, + "<" + ).replace( + rx_gt, + ">" + ); +} + +export default { + error: function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + let fudge = Number(Boolean(data.option.fudge)); + let output = []; + if (data.stop) { + output.push("
    JSLint was unable to finish.
    "); + } + data.warnings.forEach(function (warning) { + output.push( + "
    ", + entityify(warning.line + fudge), + ".", + entityify(warning.column + fudge), + "
    ", + entityify(warning.message), + "
    ", + entityify(data.lines[warning.line] || ""), + "" + ); + }); + return output.join(""); + }, + + function: function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + let fudge = Number(Boolean(data.option.fudge)); + let mode = ( + data.module + ? "module" + : "global" + ); + let output = []; + + if (data.json) { + return ( + data.warnings.length === 0 + ? "
    JSON: good.
    " + : "
    JSON: bad.
    " + ); + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + let global = Object.keys(data.global.context).sort(); + let froms = data.froms.sort(); + let exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + let context = the_function.context; + let list = Object.keys(context); + output.push( + "
    ", + entityify(the_function.line + fudge), + "
    ", + ( + the_function.name === "=>" + ? entityify(the_function.signature) + " =>" + : ( + typeof the_function.name === "string" + ? "«" + entityify(the_function.name) + "»" + : "" + entityify(the_function.name.id) + "" + ) + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + let params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.parent === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.parent === the_function + ); + })); + detail("outer", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.parent !== the_function + && the_variable.parent.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].parent.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + output.push( + "
    JSLint edition ", + entityify(data.edition), + "
    " + ); + return output.join(""); + }, + + property: function property_directive(data) { + +// Produce the /*property*/ directive. + + let not_first = false; + let output = ["/*property"]; + let length = 1111; + let properties = Object.keys(data.property); + + if (properties.length > 0) { + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length >= 80) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); + } + } +}; diff --git a/branch.rx_token/README b/branch.rx_token/README new file mode 100644 index 000000000..cd6b06c8b --- /dev/null +++ b/branch.rx_token/README @@ -0,0 +1,39 @@ +JSLint, The JavaScript Code Quality Tool + +Douglas Crockford +douglas@crockford.com + +2019-03-15 + +jslint.js contains the jslint function. It parses and analyzes a source file, +returning an object with information about the file. It can also take an object +that sets options. + +index.html runs the jslint.js function in a web page. The page also depends +browser.js and report.js and jslint.css. + +jslint.css provides styling for index.html. + +browser.js runs the web user interface. + +report.js generates the results reports in HTML. + +help.html describes JSLint's usage. Please read it. + +function.html describes the jslint function and the results it produces. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[http://www.crockford.com/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. diff --git a/branch.rx_token/browser.js b/branch.rx_token/browser.js new file mode 100644 index 000000000..5086fed85 --- /dev/null +++ b/branch.rx_token/browser.js @@ -0,0 +1,158 @@ +// browser.js +// 2018-06-16 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +/*jslint + browser +*/ + +/*property + checked, create, disable, display, error, focus, forEach, function, + getElementById, innerHTML, join, length, map, onchange, onclick, onscroll, + property, querySelectorAll, scrollTop, select, split, style, title, value +*/ + +import jslint from "./jslint.js"; +import report from "./report.js"; + +// This is the web script companion file for JSLint. It includes code for +// interacting with the browser and displaying the reports. + +let rx_crlf = /\n|\r\n?/; +let rx_separator = /[\s,;'"]+/; + +let fudge_unit = 0; + +const aux = document.getElementById("JSLINT_AUX"); +const boxes = document.querySelectorAll("[type=checkbox]"); +const fudge = document.getElementById("JSLINT_FUDGE"); +const global = document.getElementById("JSLINT_GLOBAL"); +const number = document.getElementById("JSLINT_NUMBER"); +const property = document.getElementById("JSLINT_PROPERTY"); +const property_fieldset = document.getElementById("JSLINT_PROPERTYFIELDSET"); +const report_field = document.getElementById("JSLINT_REPORT"); +const report_list = document.getElementById("JSLINT_REPORT_LIST"); +const source = document.getElementById("JSLINT_SOURCE"); +const select = document.getElementById("JSLINT_SELECT"); +const warnings = document.getElementById("JSLINT_WARNINGS"); +const warnings_list = document.getElementById("JSLINT_WARNINGS_LIST"); + +function show_numbers() { + number.value = source.value.split(rx_crlf).map(function (ignore, index) { + return index + fudge_unit; + }).join("\n"); +} + +function clear() { + aux.style.display = "none"; + number.value = ""; + property.value = ""; + property_fieldset.style.display = "none"; + report_field.style.display = "none"; + report_list.innerHTML = ""; + source.focus(); + source.value = ""; + warnings.style.display = "none"; + warnings_list.innerHTML = ""; +} + +function fudge_change() { + fudge_unit = Number(fudge.checked); + show_numbers(); +} + +function clear_options() { + boxes.forEach(function (node) { + node.checked = false; + }); + fudge_change(); + global.innerHTML = ""; +} + +function call_jslint() { + +// First build the option object. + + let option = Object.create(null); + boxes.forEach(function (node) { + if (node.checked) { + option[node.title] = true; + } + }); + +// Call JSLint with the source text, the options, and the predefined globals. + + let global_string = global.value; + let result = jslint( + source.value, + option, + ( + global_string === "" + ? undefined + : global_string.split(rx_separator) + ) + ); + +// Generate the reports. + + let error_html = report.error(result); + let function_html = report.function(result); + let property_text = report.property(result); + +// Display the reports. + + warnings_list.innerHTML = error_html; + warnings.style.display = ( + error_html.length === 0 + ? "none" + : "block" + ); + + report_list.innerHTML = function_html; + report_field.style.display = "block"; + if (property_text) { + property.value = property_text; + property_fieldset.style.display = "block"; + property.scrollTop = 0; + select.disable = false; + } else { + property_fieldset.style.display = "none"; + select.disable = true; + } + aux.style.display = "block"; + source.select(); +} + +fudge.onchange = fudge_change; + +source.onchange = function (ignore) { + show_numbers(); +}; + +source.onscroll = function () { + let ss = source.scrollTop; + number.scrollTop = ss; + let sn = number.scrollTop; + if (ss > sn) { + show_numbers(); + number.scrollTop = ss; + } +}; + +document.querySelectorAll("[name='JSLint']").forEach(function (node) { + node.onclick = call_jslint; +}); + +document.querySelectorAll("[name='clear']").forEach(function (node) { + node.onclick = clear; +}); + +document.getElementById("JSLINT_SELECT").onclick = function () { + property.focus(); + property.select(); +}; +document.getElementById("JSLINT_CLEAR_OPTIONS").onclick = clear_options; + +fudge_change(); +source.select(); +source.focus(); diff --git a/branch.rx_token/function.html b/branch.rx_token/function.html new file mode 100644 index 000000000..7097c62e9 --- /dev/null +++ b/branch.rx_token/function.html @@ -0,0 +1,615 @@ + + + + + + + + +JSLint: jslint function + + + +
    JSLint
    + +
    The jslint Function
    +
    +

    JSLint

    +

    JSLint is delivered as a set of files:

    +
    + + + + + + + + + + + + + + +
    FilenameContent
    browser.jsBrowser based UI.
    function.htmlThis page that you are looking at right now.
    help.htmlUsing jslint as a single page JSLint application.
    index.htmlSingle page JSLint application that uses the + js files.
    jslint.jsThe jslint function.
    report.jsThe report object, containing report generator functions for + HTML.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    +
    + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring"(JSLint)"
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    parenttokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    parenttokenThe function in which the variable was declared.variable
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    shebangstringThe first line of text if it started with #!line
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable may be assigned to.variable
    +

    Reports

    +

    The report object contains three functions that all take a + result from the jslint function as input. + report.js has no other dependence on other files.

    + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    +
    + +
    + + + +
    + + diff --git a/branch.rx_token/help.html b/branch.rx_token/help.html new file mode 100644 index 000000000..71d9daa5d --- /dev/null +++ b/branch.rx_token/help.html @@ -0,0 +1,827 @@ + + + + + + + + + +JSLint: Help + + + +
    JSLint
    + +
    Help
    +
    +
    Il semble que la perfection soit atteinte non quand il n’y a + plus rien à ajouter, mais quand il n’y a plus rien à + retrancher.
    +
    + Antoine de Saint-Exupéry +
    +
    + Terre des Hommes (1939) +
    +

    Good Parts

    +

    The Principle of the Good Parts is

    +
    If a feature is sometimes useful and sometimes dangerous and if + there is a better option then always use the better option.
    +

    JSLint is a JavaScript program that looks for problems in + JavaScript programs. It is a code quality tool.

    +

    When C was + a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    +

    As the language matured, the definition of the language was + strengthened to eliminate some insecurities, and compilers got better + at issuing warnings. lint is no longer needed.

    +

    JavaScript is a + young-for-its-age language. It was originally intended to do small tasks in + webpages, tasks for which Java was too heavy and clumsy. But JavaScript is + a surprisingly capable language, and it is now being used in larger + projects. Many of the features that were intended to make the language easy + to use are troublesome when projects become complicated. A lint + for JavaScript is needed: JSLint, a JavaScript syntax checker + and validator.

    +

    JSLint takes a JavaScript source and scans it. If it finds a + problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by the ECMAScript Programming Language + Standard (the strangely named document that governs JavaScript). + JSLint will reject most legal programs. It is a higher + standard.

    +

    JavaScript is a sloppy language, but hidden deep inside there is an elegant, + better language. JSLint helps you to program in that better + language and to avoid most of the slop. JSLint will reject + programs that browsers will accept because JSLint is concerned + with the quality of your code and browsers are not. You should gladly accept + all of JSLint's advice.

    +

    JSLint can operate on JavaScript source + or JSON text.

    +

    ECMAScript Sixth Edition

    +

    Some of + ES6’s features are good, so JSLint will recognize the good + parts of ES6.

    +

    Currently, these features are recognized:

    +
      +
    • The ... ellipsis marker in parameter + lists and argument lists, replacing the arguments object + for variadic functions.
    • +
    • The let statement, which is like the var + statement except that it respects block scope. + You may use let or var but not both.
    • +
    • The const statement is like the let statement + except that it disallows the use of assignment on the variable, although + if the value of the variable is mutable, it can still be mutated. + const is preferred to let. +
    • Destructuring of arrays and objects is allowed in parameter lists and on + the left side of let, and const, but not + var or assignment statements, and not deep destructuring + or eliding.
    • +
    • Enhanced object literals, providing shorter forms for function + declaration and properties that are initialized by variables with the + same name.
    • +
    • The fat arrow => fart functions.
    • +
    • The simplest forms of import and export.
    • +
    • `Megastring` literals, but not nested + `megastring` literals.
    • +
    • New global functions, such as Map, Set, + WeakMap, and WeakSet.
    • +
    • 0b- and 0o- number literals.
    • +
    +

    The most important new feature of ES6 is proper tail calls. This has no + new syntax, so JSLint doesn’t see it. But it makes recursion + much more attractive, which makes loops, particularly for + loops, much less attractive.

    +

    import export

    +

    The ES6 module feature will be an important improvement over JavaScript’s + global variables as a means of linking separate files together. + JSLint recognizes a small but essential subset of the module + syntax.

    +
    +

    import name from stringliteral;

    +

    import {name} from stringliteral;

    +

    import(string).then(function);

    +

    export default function () {};

    +

    export default function name() {};

    +

    export default expression;

    +

    export function name() {}

    +

    export name; // where name is const

    +

    export {name};

    +
    +

    Directives

    +

    JSLint provides three directives that may be placed in a + file to manage JSLint’s behavior. Use of these directives is + optional. If they are used, they should be placed in a source file before + the first statement. They are written in the form of a comment, where the + directive name is placed immediately after the opening of the comment before + any whitespace. The three directives are global, + jslint, and property. Directives in a file are + stronger than options selected from the UI or passed with the option object.

    +

    /*global*/

    +

    The /*global*/ directive is used to specify a set of globals + (usually functions and objects containing functions) that are available to + this file. This was commonly used in browsers to link source files together + before ES6 modules appeared. Use of global variables is strongly + discouraged, but unfortunately web browsers require their use. The + /*global*/ directive can only be used when the + Assume a browser option is selected. +

    Each of the names listed will indicate a read-only global variable. The names + are separated by , comma. This directive + should not be used if import or export is used. + This directive inhibits warnings, but it does not declare the names in the + execution environment. +

    For example: +

    /*global +
    ADSAFE, report, jslint
    +*/
    +

    instructs JSLint to not give warnings about the global + variables ADsafe, report, and jslint. + However, if any of those names are expected to be supplied by other files + and those other files fail to do so, then execution errors will result. It + is usually better to use top-level variable declarations instead: +

        var ADSAFE;
    +    var report;
    +    var jslint; 
    +

    Using var in this way allows comparing a global variable to the + undefined value to determine whether it is has been used in the global context.

    +

    /*jslint*/

    +

    The /*jslint*/ directive allows for the control of several + options. These options can also be set from the + JSLint.com user interface.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Tolerate bitwise operatorsbitwisetrue if bitwise operators should be allowed. The + bitwise operators are rarely used in JavaScript programs, so it + is usually more likely that & is a mistyping of + && than that it indicates a bitwise and. + JSLint will give warnings on the bitwise operators + unless this option is selected.
    Assume a browser browsertrue if the standard browser globals should be + predefined. This option will reject the use of + import and export. This option also + disallows the file form of the "use + strict" pragma. It does not supply + self; you will have to request that unnecessary + alias of the dreaded global object yourself. It adds the same + globals as this directive: +
    /*global +
    + clearInterval, clearTimeout, document, event, FileReader, + FormData, history, localStorage, location, name, navigator, + screen, sessionStorage, setInterval, setTimeout, Storage, + URL, window, XMLHttpRequest +
    + */
    +
    Tolerate conversion operatorsconverttrue if !!, + prefix, + and concatenation with "" are allowed. + The Boolean, Number, and + String functions are preferred
    Assume CouchDBcouchtrue if + Couch DB + globals should be predefined. It adds the same globals as this + directive: +
    /*global +
    emit, getRow, isArray, log, provides, registerType, require, + send, start, sum, toJSON
    + */
    Assume in developmentdeveltrue if browser globals that are useful in + development should be predefined, and if debugger + statements and TODO comments + should be allowed. It adds the + same globals as this directive: +
    /*global +
    alert, confirm, console, prompt
    + */
    Be sure to turn this option off before going + into production.
    Tolerate evalevaltrue if eval should be allowed. In the + past, the eval function was the most misused + feature of the language.
    Tolerate for statementfortrue if the for statement should be + allowed. It is almost always better to use the array methods + instead.
    Fudge the line and column numbersfudgetrue if the origin of a text file is line 1 column + 1. Programmers know that number systems start at zero. + Unfortunately, most text editors start numbering lines and + columns at one. JSLint will by default start + numbering at zero. Use this option to start the numbering at one + in its reports.
    Tolerate get and setgetsettrue if accessor properties are allowed in object literals.
    Tolerate long source lineslongtrue if a line can contain more than 80 characters.
    Assume Node.jsnodetrue if Node.js globals should be predefined. It + will predefine globals that are used in the Node.js environment. + It adds the same globals as this directive: +
    /*global +
    + Buffer, clearImmediate, clearInterval, clearTimeout, console, exports, + module, process, require, setImmediate, setInterval, setTimeout, URL, + URLSearchParams, __dirname, __filename +
    + */
    Tolerate single quote stringssingletrue if 'single quote + should be allowed to enclose string literals.
    Tolerate this
    thistrue if this should be allowed.
    Tolerate whitespace messwhitetrue if the whitespace rules should be ignored.
    +

    For example:

    +
    /*jslint
    +    bitwise, node
    +*/
    + +

    /*property*/

    +

    The /*property*/ directive is used to declare a list of + property identifiers that are used in the file. Each property name in the + program is looked up in this list. If a name is not found, that indicates an + error, most likely a typing error.

    +

    The list can also be used to evade some of JSLint’s naming + rules.

    +

    JSLint can build the /*property*/ list for you. At + the bottom of its report, JSLint displays a + /*property*/ directive. It contains all of the names that were + used with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. You + can copy the /*property*/ directive to the top of your + script file. JSLint will check the spelling of all property + names against the list. That way, you can have JSLint look for + misspellings for you.

    +

    For example,

    +
    /*property +
    charAt, slice, _$_
    + */
    +

    ignore

    +

    JSLint introduces a new reserved word: ignore. It + is used in parameter lists and in catch clauses to indicate a + parameter that will be ignored. Unused warnings will not be produced for + ignore. +

    function handler(ignore, value) {
    +    return do_something_useful(value);
    +}
    +

    var let const

    +

    JavaScript provides three statements for declaring variables: + var, let, and const. The + var statement suffers from a bad practice called hoisting. The + let statement does not do hoisting and respects block scope. + The const statement is like let except that it + marks the variable (but not its contents) as read only, making it an error + to attempt to assign to the variable. When given a choice, + const is the best, var is the worst.

    +

    JSLint uses the intersection of the var rules and the + let rules, and by doing so avoids the errors related to either. + A name should be declared only once in a function. It should be declared + before it is used. It should not be used outside of the block in which it is + declared. A variable should not have the same name as a variable or + parameter in an outer function. Do not mix var and + let. Declare one name per statement.

    +

    = == ===

    +

    Fortran made a terrible + mistake in using the equality operator as its assignment operator. That + mistake has been replicated in most languages since then. C compounded that + mistake by making == its equality operator. The visual + similarity is a source of errors. JavaScript compounded this further by + making == a type coercing comparison operator that produces + false positive results. This was mitigated by adding the === + operator, leaving the broken == operator in place.

    +

    JSLint attempts to minimize errors by the following rules:

    +

    == is not allowed. This avoids the false positives, and + increases the visual distance between = and ===. + Assignments are not allowed in expression position, and comparisons are not + allowed in statement position. This also reduces confusion. 

    +

    Semicolon

    +

    JavaScript uses a C-like syntax that requires the use of semicolons to + delimit certain statements. JavaScript attempts to make those semicolons + optional with an automatic semicolon insertion mechanism, but it does not + work very well. Automatic semicolon insertion was added to make + things easier for beginners. Unfortunately, it sometimes fails. Do not rely + on it unless you are a beginner.

    +

    JSLint expects that every statement will be followed by + ; except for for, function, + if, switch, try, and + while. JSLint does not expect to see unnecessary + semicolons, the empty statement, or empty blocks.

    +

    function =>

    +

    JavaScript has four syntactic forms for making function objects: function + statements, function expressions, enhanced object literals, and the + => fart operator.

    +

    The function statement creates a variable and assigns the function object to + it. It should be used in a file or function body, but not inside of a block.

    +
    function name(parameters) { +
    statements
    + }
    +

    The function expression unfortunately looks like the function statement. It + may appear anywhere that an expression may appear, but not in statement + position and not in a loop. It produces a function object but does not + create a variable in which to store it.

    +
    function (parameters) { +
    statements
    + }
    +

    The enhanced object literal provides an ES6 shorthand for creating a property + whose value is a function, saving you from having to type + : colon and function.

    +
    { +
    name(parameters) { +
    statements
    + }
    + }
    +

    Finally, ES6 provides an even shorter form of function expression that leaves + out the words function and return:

    +
    (parameters) => + expression
    +

    JSLint requires the parens around the parameters, and forbids a + { left brace after the + => fart to avoid syntactic ambiguity.

    +

    Comma

    +

    The , comma operator is unnecessary and can + mask programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as + an operator. It does not expect to see elided elements in array literals. A + comma should not appear after the last element of an array literal or object + literal.

    +

    Blocks

    +

    JSLint expects blocks with function, + if, switch, while, for, + do, and try statements and nowhere else.

    +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    +

    JavaScript allows an if to be written like this:

    +
    if (condition)
    +    statement;
    +

    That form is known to contribute to mistakes in projects where many + programmers are working on the same code. That is why JSLint + expects the use of a block:

    +
    if (condition) {
    +    statements;
    +}
    +

    Experience shows that this form is more resilient.

    +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call. All other expression statements are considered + to be errors.

    +

    for

    +

    JSLint does not recommend use of the for statement. + Use array methods like forEach instead. The for + option will suppress some warnings. The forms of for that + JSLint accepts are restricted, excluding the new ES6 forms.

    +

    for in

    +

    JSLint does not recommend use of the for + in statement. Use Object.keys instead.

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, it also + loops through all of the properties that were inherited through the + prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written + without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    for (name in object) {
    +    if (object.hasOwnProperty(name)) {
    +        ....
    +    }
    +}
    +

    Note that the above code will fail if the object contains a property + named hasOwnProperty. Use Object.keys instead.

    +

    switch

    +

    A common error in switch statements is to forget to place a + break statement after each case, resulting in unintended + fall-through. JSLint expects that the statement before the next + case or default is one of these: + break, return, or throw.

    +

    with

    +

    The with statement was intended to provide a shorthand in + accessing properties in deeply nested objects. Unfortunately, it behaves + very badly when setting new properties. Never use the with + statement.

    +

    JSLint does not expect to see a with statement.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that + labels will be distinct from vars and parameters.

    +

    Unreachable Code

    +

    JSLint expects that a return, break, + or throw statement will be followed by a + } right brace or case or + default.

    +

    Confusing Pluses and Minuses

    +

    JSLint expects that + will not be followed by + + or ++, and that - will not be + followed by - or --. A misplaced space can turn + + + into ++, an error that is difficult to see. + Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ increment and + -- decrement operators have been known to + contribute to bad code by encouraging excessive trickiness. They are second + only to faulty architecture in enabling to viruses and other security + menaces. Also, preincrement/postincrement confusion can produce off-by-one + errors that are extremely difficult to diagnose. Fortunately, they are also + complete unnecessary. There are better ways to add 1 to a variable.

    +

    It is best to avoid these operators entirely and rely on += and + -= instead.

    +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. + JSLint looks for problems that may cause portability problems. + It also attempts to resolve visual ambiguities by recommending explicit + escapement.

    +

    JavaScript’s syntax for regular expression literals overloads the + / slash character. To avoid ambiguity, + JSLint expects that the character preceding a regular + expression literal is a ( left paren or + = equal or + : colon or + , comma character.

    +

    this

    +
    Having this in the language makes it harder to talk + about the language. It is like pair programming with Abbott and Costello. +
    +

    Avoid using this. Warnings about this can be + suppressed with option.this.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the + new prefix. The new prefix creates a new object + based on the function's prototype, and binds that object to the + function's implied this parameter. If you neglect to use the + new prefix, no new object will be made and this + will be bound to the global object.

    +

    JSLint enforces the convention that constructor functions be + given names with initial uppercase. JSLint does not expect to + see a function invocation with an initial uppercase name unless it has the + new prefix. JSLint does not expect to see the + new prefix used with functions whose names do not start with + initial uppercase.

    +

    JSLint does not expect to see the wrapper forms + new Number, new String, new Boolean.

    +

    JSLint does not expect to see new Object. + Use Object.create(null) instead.

    +

    Whitespace

    +

    JSLint has a specific set of rules about the use of whitespace. + Where possible, these rules are consistent with centuries of good practice + with literary style.

    +

    The indentation increases by 4 spaces when the last token on a line is + { left brace, + [ left bracket, + ( left paren. The matching closing + token will be the first token on a line, restoring the previous + indentation.

    +

    The ternary operator can be visually confusing, so + ? question mark and + : colon always begin a line and increase + the indentation by 4 spaces.

    +
    return (
    +    (the_token.id === "(string)" || the_token.id === "(number)")
    +    ? String(the_token.value)
    +    : the_token.id
    +);
    +

    The word function is always followed with one space.

    +

    Clauses (case, catch, default, + else, finally) are not statements and so should + not be indented like statements.

    +

    Spaces are used to make things that are not invocations look less like + invocations.

    +

    Tabs and spaces should not be mixed. We should pick just one in order to + avoid the problems that come from having both. Personal preference is an + extremely unreliable criterion. Neither offers a powerful advantage over the + other. Fifty years ago, tab had the advantage of consuming less memory, but + Moore's Law has eliminated that advantage. Space has one clear advantage + over tab: there is no reliable standard for how many spaces a tab + represents, but it is universally accepted that a space occupies a space. So + use spaces. You can edit with tabs if you must, but make sure it is spaces + again before you commit. Maybe someday we will finally get a universal + standard for tabs, but until that day comes, the better choice is spaces.

    +

    Unsafe Characters

    +

    There are characters that are handled inconsistently in some browsers, and + so must be escaped when placed in strings.

    +
    \u0000-\u001f
    +\u007f-\u009f
    +\u00ad
    +\u0600-\u0604
    +\u070f
    +\u17b4
    +\u17b5
    +\u200c-\u200f
    +\u2028-\u202f
    +\u2060-\u206f
    +\ufeff
    +\ufff0-\uffff
    +

    Report

    +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    +
      +
    • The line number on which it starts.
    • +
    • The function’s name. In the case of anonymous functions, + JSLint will attempt to + «guess» the name.
    • +
    • The parameters.
    • +
    • Variables: The variables that are declared in the function.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Exceptions: The variables that are declared by catch + clauses of try statements.
    • +
    • Outer: Variables used by this function that are declared in + other functions.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements.

    +

    Try it

    +

    Try it. Paste your script + into the window and click the + + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    JSLint is written entirely in JavaScript, so it can run + anywhere that JavaScript (or even Java) can run.

    +

    Perfectly fine

    +

    JSLint was designed to reject code that some would consider to + be perfectly fine. The reason for this is that JSLint's + purpose is to help produce programs that are free of error. That is + difficult in any language and is especially hard in JavaScript. + JSLint attempts to help you increase the visual distance + between correct programs and incorrect programs, making the remaining errors + more obvious. JSLint will give warnings about things that are + not necessarily wrong in the current situation, but which have been observed + to mask or obscure errors. Avoid those things when there are better options + available.

    +

    It is dangerous out there. JSLint is here to help.

    +

    Warning

    +

    JSLint will hurt your feelings. Side effects may include + headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, + dry mouth, cleaner code, and a reduced error rate.

    +

    Further Reading

    + +
    +

    + Code Conventions for the JavaScript Programming Language.

    + + + +
    + + diff --git a/branch.rx_token/index.html b/branch.rx_token/index.html new file mode 100644 index 000000000..3c82070df --- /dev/null +++ b/branch.rx_token/index.html @@ -0,0 +1,114 @@ + + + + + + + + + +JSLint: The JavaScript Code Quality Tool + + +
    +
    Source
    +
    + + +
    +
    + Warnings +
    +
    +
    + Function Report +
    +
    +
    + Property Directive + +
    +
    + + + + +
    +
    Options +
    Assume... +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Global variables... + +
    +
    +
    +
    + + + + +
    + + diff --git a/branch.rx_token/jslint.css b/branch.rx_token/jslint.css new file mode 100644 index 000000000..cc3bf1696 --- /dev/null +++ b/branch.rx_token/jslint.css @@ -0,0 +1,341 @@ +@font-face { + font-family: 'Programma'; + font-weight: bold; + src: url('Programma-Bold.woff2') format('woff2'); +} +@font-face { + font-family: 'Daley'; + font-weight: bold; + src: url('Daley-Bold.woff2') format('woff2'); +} + +body { + background-color: antiquewhite; + color: black; + font-family: 'Daley', sans-serif; + margin: 0; + padding: 0; +} + +#JSLINT_TITLEBOX { + font-family: 'Daley', sans-serif; + margin: 0; + margin-left: 3%; + margin-right: 3%; + padding: 0; +} + +#JSLINT_TITLEBOX ul { + float: right; + font-size: 12pt; +} + +#JSLINT_TITLEBOX div { + color: darkslategray; + float: left; + font-size: 48pt; +} + +#JSLINT_ fieldset { + background-color: gainsboro; + border: 0; + clear: both; + margin-bottom: 1em; + margin-left: 3%; + margin-right: 3%; + margin-top: 1em; + padding: 0; + width: auto; +} +#JSLINT_ legend { + background-color: darkslategray; + border: 0; + color: white; + font-size: 100%; + font-style: normal; + font-weight: normal; + margin: 0; + padding-bottom: 0.25em; + padding-left: 0; + padding-right: 0; + padding-top: 0.25em; + text-align: center; + width: 100%; +} +#JSLINT_ button { + background-color: darkslategray; + border: 0; + color: white; + cursor: pointer; + font-family: 'Daley', sans-serif; + font-size: 100%; + font-style: normal; + margin-left: 1em; + margin-right: 1em; + padding-left: 1em; + padding-right: 1em; + padding-bottom: 0.25em; + padding-top: 0.25em; + text-align: center; +} +#JSLINT_ button:hover { + background-color: slategray; + color: white; + border: 0; +} + +#JSLINT_ button:active { + border: 0; + color: black; +} + +#JSLINT_ button:disabled { + background-color: gray; + border: 0; + color: gainsboro; +} + +a:visited { + color: #69565c; +} + +a:link { + color: black; +} + +#JSLINT_ input[type="text"] { + background-color: white; + border: 0; + color: black; + margin-bottom: 0.2em; + margin-right: 0.5em; + padding-left: 0.5em; + padding-right: 0.5em; + text-align: right; + width: 3em; +} + +#JSLINT_ textarea { + border: 0; + background-color: white; + border: 0; + font-family: Programma, monospace; + font-size: 100%; + font-weight: bold; + resize: none; +} + +#JSLINT_ textarea::selection { + background-color: yellow; + color: black; +} + +#JSLINT_NUMBER { + color: darkgray; + display: inline-block; + height: 4in; + margin: 2%; + margin-right: 0; + overflow: hidden; + padding-right: 0.5%; + text-align: right; + white-space: pre; + width: 4%; +} + +#JSLINT_SOURCE { + color: black; + display: inline-block; + font-size: 100%; + height: 4in; + margin: 2%; + margin-left: 0; + margin-right: 0; + overflow: auto; + padding-left: 0.5%; + white-space: pre; + width: 91%; +} + +#JSLINT_PROPERTY { + background-color: honeydew; + border: 0; + margin: 2%; + padding: 0.5%; + white-space: pre; + width: 95%; +} + +#JSLINT_GLOBAL { + white-space: normal; + width: 95%; +} +#JSLINT_ label { + font-family: sans-serif; + font-size: 90%; + padding-left: 0.25em; +} +#JSLINT_OPTIONS>div { + float: left; + margin: 0.5em; +} + +#JSLINT_ address { + color: black; + display: block; + float: right; + font-family: serif; + font-size: 90%; + margin-left: 1em; +} +#JSLINT_ dl { + background-color: cornsilk; + color: white; + margin: 0; + padding-bottom: 2pt; + padding-left: 1em; + padding-right: 1em; + padding-top: 2pt; +} +#JSLINT_ dfn { + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + margin-bottom: 2pt; +} +#JSLINT_ dt { + color: black; + display: block; + float: left; + font-family: serif; + font-size: 75%; + font-style: italic; + margin: 0; + width: 8em; + text-align: right; +} +#JSLINT_ dd { + color: black; + display: block; + font-family: Programma, monospace; + font-weight: bold; + margin-left: 8em; + padding-bottom: 2pt; +} +#JSLINT_WARNINGS>legend { + background-color: indianred; +} +#JSLINT_WARNINGS>div { + background-color: pink; + padding: 1em; +} +#JSLINT_WARNINGS cite { + color: black; + display: block; + font-family: serif; + font-size: 100%; + font-style: normal; + margin-bottom: 4pt; + margin-left: 20pt; + margin-right: 20pt; + margin-top: 4pt; + overflow-x: hidden; +} +#JSLINT_WARNINGS samp { + background-color: lavenderblush; + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + padding: 4pt; + margin-bottom: 0; + margin-left: 16pt; + margin-right: 16pt; + margin-top: 0; + white-space: pre-wrap; +} + +#JSLINT_REPORT center { + margin-top: 1em; +} + +#JSLINT_WARNINGS dl address { + color: black; + display: inline; + float: none; + font-size: 80%; + margin: 0; +} + +#JSLINT_REPORT>div { + padding: 1em; +} + +#JSLINT_ dl.level0 { + color: black; + background-color: white; +} + +#JSLINT_ dl.level1 { + color: black; + background-color: #ffffe0; /* yellow */ + margin-left: 1em; +} + +#JSLINT_ dl.level2 { + color: black; + background-color: #e0ffe0; /* green */ + margin-left: 2em; +} + +#JSLINT_ dl.level3 { + color: black; + background-color: #D0D0ff; /* blue */ + margin-left: 3em; +} + +#JSLINT_ dl.level4 { + background-color: #ffe0ff; /* purple */ + color: black; + margin-left: 4em; +} + +#JSLINT_ dl.level5 { + background-color: #ffe0e0; /* red */ + color: black; + margin-left: 5em; +} + +#JSLINT_ dl.level6 { + background-color: #ffe390; /* orange */ + color: black; + margin-left: 6em; +} + +#JSLINT_ dl.level7 { + background-color: #e0e0e0; /* gray */ + color: black; + margin-left: 7em; +} + +#JSLINT_ dl.level8 { + color: black; + margin-left: 8em; +} + +#JSLINT_ dl.level9 { + color: black; + margin-left: 9em; +} + +.none { + display: none; +} +.center { + text-align: center; +} diff --git a/branch.rx_token/jslint.js b/branch.rx_token/jslint.js new file mode 100644 index 000000000..b45f5183e --- /dev/null +++ b/branch.rx_token/jslint.js @@ -0,0 +1,5013 @@ +// jslint.js +// 2020-07-02 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// jslint(source, option_object, global_array) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze, a string or an array of strings. +// option_object An object whose keys correspond to option names. +// global_array An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all of the functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// 1. If the source is a single string, split it into an array of strings. +// 2. Turn the source into an array of tokens. +// 3. Furcate the tokens into a parse tree. +// 4. Walk the tree, traversing all of the nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// 5. Check the whitespace between the tokens. + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*property + a, and, arity, assign, b, bad_assignment_a, bad_directive_a, bad_get, + bad_module_name_a, bad_option_a, bad_property_a, bad_set, bitwise, block, + body, browser, c, calls, catch, charCodeAt, closer, closure, code, column, + concat, constant, context, convert, couch, create, d, dead, default, devel, + directive, directives, disrupt, dot, duplicate_a, edition, ellipsis, else, + empty_block, escape_mega, eval, every, expected_a, expected_a_at_b_c, + expected_a_b, expected_a_b_from_c_d, expected_a_before_b, + expected_a_next_at_b, expected_digits_after_a, expected_four_digits, + expected_identifier_a, expected_line_break_a_b, expected_regexp_factor_a, + expected_space_a_b, expected_statements_a, expected_string_a, + expected_type_string_a, exports, expression, extra, finally, flag, for, + forEach, free, freeze, freeze_exports, from, froms, fud, fudge, + function_in_loop, functions, g, getset, global, i, id, identifier, import, + inc, indexOf, infix_in, init, initial, isArray, isNaN, join, json, keys, + label, label_a, lbp, led, length, level, line, lines, live, long, loop, m, + margin, match, message, misplaced_a, misplaced_directive_a, missing_browser, + missing_m, module, naked_block, name, names, nested_comment, new, node, + not_label_a, nr, nud, number_isNaN, ok, open, opening, option, + out_of_scope_a, parameters, parent, pop, property, push, quote, + redefinition_a_b, replace, required_a_optional_b, reserved_a, role, search, + shebang, signature, single, slice, some, sort, split, startsWith, statement, + stop, subscript_a, switch, test, this, thru, toString, todo_comment, + tokens, too_long, too_many_digits, tree, try, type, u, unclosed_comment, + unclosed_mega, unclosed_string, undeclared_a, unexpected_a, + unexpected_a_after_b, unexpected_a_before_b, unexpected_at_top_level_a, + unexpected_char_a, unexpected_comment, unexpected_directive_a, + unexpected_expression_a, unexpected_label_a, unexpected_parens, + unexpected_space_a_b, unexpected_statement_a, unexpected_trailing_space, + unexpected_typeof_a, uninitialized_a, unreachable_a, + unregistered_property_a, unsafe, unused_a, use_double, use_open, use_spaces, + used, value, var_loop, var_switch, variable, warning, warnings, + weird_condition_a, weird_expression_a, weird_loop, weird_relation_a, white, + wrap_condition, wrap_immediate, wrap_parameter, wrap_regexp, wrap_unary, + wrapped, writable, y +*/ + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than '{}' because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +function populate(array, object = empty(), value = true) { + +// Augment an object by taking property names from an array of strings. + + array.forEach(function (name) { + object[name] = value; + }); + return object; +} + +const allowed_option = { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + bitwise: true, + browser: [ + "caches", "CharacterData", "clearInterval", "clearTimeout", "document", + "DocumentType", "DOMException", "Element", "Event", "event", "fetch", + "FileReader", "FontFace", "FormData", "history", "IntersectionObserver", + "localStorage", "location", "MutationObserver", "name", "navigator", + "screen", "sessionStorage", "setInterval", "setTimeout", "Storage", + "TextDecoder", "TextEncoder", "URL", "window", "Worker", + "XMLHttpRequest" + ], + couch: [ + "emit", "getRow", "isArray", "log", "provides", "registerType", + "require", "send", "start", "sum", "toJSON" + ], + convert: true, + devel: [ + "alert", "confirm", "console", "prompt" + ], + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + node: [ + "Buffer", "clearImmediate", "clearInterval", "clearTimeout", + "console", "exports", "module", "process", "require", + "setImmediate", "setInterval", "setTimeout", "TextDecoder", + "TextEncoder", "URL", "URLSearchParams", "__dirname", "__filename" + ], + single: true, + this: true, + white: true +}; + +const anticondition = populate([ + "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%", + "typeof", "(number)", "(string)" +]); + +// These are the bitwise operators. + +const bitwiseop = populate([ + "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=", + ">>>", ">>>=" +]); + +const escapeable = populate([ + "\\", "/", "`", "b", "f", "n", "r", "t" +]); + +const opener = { + +// The open and close pairs. + + "(": ")", // paren + "[": "]", // bracket + "{": "}", // brace + "${": "}" // mega +}; + +// The relational operators. + +const relationop = populate([ + "!=", "!==", "==", "===", "<", "<=", ">", ">=" +]); + +// This is the set of infix operators that require a space on each side. + +const spaceop = populate([ + "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/", + "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=", + ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" +]); + +const standard = [ + +// These are the globals that are provided by the language standard. + + "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI", + "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error", + "EvalError", "Float32Array", "Float64Array", "Generator", + "GeneratorFunction", "Int8Array", "Int16Array", "Int32Array", "Intl", + "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat", + "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", + "Set", "String", "Symbol", "SyntaxError", "System", "TypeError", + "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", + "URIError", "WeakMap", "WeakSet" +]; + +const bundle = { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + and: "The '&&' subexpression should be wrapped in parens.", + bad_assignment_a: "Bad assignment to '{a}'.", + bad_directive_a: "Bad directive '{a}'.", + bad_get: "A get function takes no parameters.", + bad_module_name_a: "Bad module name '{a}'.", + bad_option_a: "Bad option '{a}'.", + bad_property_a: "Bad property name '{a}'.", + bad_set: "A set function takes one parameter.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + escape_mega: "Unexpected escapement in mega literal.", + expected_a: "Expected '{a}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: ( + "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'." + ), + expected_a_before_b: "Expected '{a}' before '{b}'.", + expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.", + expected_digits_after_a: "Expected digits after '{a}'.", + expected_four_digits: "Expected four digits after '\\u'.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.", + expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.", + expected_space_a_b: "Expected one space between '{a}' and '{b}'.", + expected_statements_a: "Expected statements before '{a}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + expected_type_string_a: "Expected a type string and instead saw '{a}'.", + freeze_exports: ( + "Expected 'Object.freeze('. All export values should be frozen." + ), + function_in_loop: "Don't make functions within a loop.", + infix_in: ( + "Unexpected 'in'. Compare with undefined, " + + "or use the hasOwnProperty method instead." + ), + label_a: "'{a}' is a statement label.", + misplaced_a: "Place '{a}' at the outermost level.", + misplaced_directive_a: ( + "Place the '/*{a}*/' directive before the first statement." + ), + missing_browser: "/*global*/ requires the Assume a browser option.", + missing_m: "Expected 'm' flag on a multiline regular expression.", + naked_block: "Naked block.", + nested_comment: "Nested comment.", + not_label_a: "'{a}' is not a label.", + number_isNaN: "Use Number.isNaN function to compare with NaN.", + out_of_scope_a: "'{a}' is out of scope.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + required_a_optional_b: ( + "Required parameter '{a}' after optional parameter '{b}'." + ), + reserved_a: "Reserved name '{a}'.", + subscript_a: "['{a}'] is better written in dot notation.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line is longer than 80 characters.", + too_many_digits: "Too many digits.", + unclosed_comment: "Unclosed comment.", + unclosed_mega: "Unclosed mega literal.", + unclosed_string: "Unclosed string.", + undeclared_a: "Undeclared '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_a_after_b: "Unexpected '{a}' after '{b}'.", + unexpected_a_before_b: "Unexpected '{a}' before '{b}'.", + unexpected_at_top_level_a: "Expected '{a}' to be in a function.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_directive_a: "When using modules, don't use directive '/*{a}'.", + unexpected_expression_a: ( + "Unexpected expression '{a}' in statement position." + ), + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_parens: "Don't wrap function literals in parens.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_statement_a: ( + "Unexpected statement '{a}' in expression position." + ), + unexpected_trailing_space: "Unexpected trailing space.", + unexpected_typeof_a: ( + "Unexpected 'typeof'. Use '===' to compare directly with {a}." + ), + uninitialized_a: "Uninitialized '{a}'.", + unreachable_a: "Unreachable '{a}'.", + unregistered_property_a: "Unregistered property name '{a}'.", + unsafe: "Unsafe character '{a}'.", + unused_a: "Unused '{a}'.", + use_double: "Use double quotes, not single quotes.", + use_open: ( + "Wrap a ternary expression in parens, " + + "with a line break after the left paren." + ), + use_spaces: "Use spaces, not tabs.", + var_loop: "Don't declare variables in a loop.", + var_switch: "Don't declare variables in a switch.", + weird_condition_a: "Weird condition '{a}'.", + weird_expression_a: "Weird expression '{a}'.", + weird_loop: "Weird loop.", + weird_relation_a: "Weird relation '{a}'.", + wrap_condition: "Wrap the condition in parens.", + wrap_immediate: ( + "Wrap an immediate function invocation in parentheses to assist " + + "the reader in understanding that the expression is the result " + + "of a function, and not the function itself." + ), + wrap_parameter: "Wrap the parameter in parens.", + wrap_regexp: "Wrap this regexp in parens to avoid confusion.", + wrap_unary: "Wrap the unary expression in parens." +}; + +// Regular expression literals: + +// supplant {variables} +const rx_supplant = /\{([^{}]*)\}/g; +// carriage return, carriage return linefeed, or linefeed +const rx_crlf = /\n|\r\n?/; +// unsafe characters that are silently deleted by one or more browsers +const rx_unsafe = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; +// identifier +const rx_identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; +const rx_module = /^[a-zA-Z0-9_$:.@\-\/]+$/; +const rx_bad_property = /^_|\$|Sync\$|_$/; +// star slash +const rx_star_slash = /\*\//; +// slash star +const rx_slash_star = /\/\*/; +// slash star or ending slash +const rx_slash_star_or_slash = /\/\*|\/$/; +// uncompleted work comment +const rx_todo = /\b(?:todo|TO\s?DO|HACK)\b/; +// tab +const rx_tab = /\t/g; +// directive +const rx_directive = /^(jslint|property|global)\s+(.*)$/; +const rx_directive_part = /^([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*(.*)$/; +// token +const rx_token = new RegExp( + "^(" + + "(\\s+)" + + "|([a-zA-Z_$][a-zA-Z0-9_$]*)" + + "|[(){}\\[\\],:;'\"~`]" + + "|\\?\\.?" + + "|=(?:==?|>)?" + + "|\\.+" + + "|\\*[*\\/=]?" + + "|\\/[*\\/]?" + + "|\\+[=+]?" + + "|-[=\\-]?" + + "|[\\^%]=?" + + "|&[&=]?" + + "|\\" + + "|[|=]?" + + "|>{1,3}=?" + + "|<= "a" && string <= "z\uffff") + || (string >= "A" && string <= "Z\uffff") + ); +} + +function supplant(string, object) { + return string.replace(rx_supplant, function (found, filling) { + const replacement = object[filling]; + return ( + replacement !== undefined + ? replacement + : found + ); + }); +} + +let anon; // The guessed name for anonymous functions. +let blockage; // The current block. +let block_stack; // The stack of blocks. +let declared_globals; // The object containing the global declarations. +let directives; // The directive comments. +let directive_mode; // true if directives are still allowed. +let early_stop; // true if JSLint cannot finish. +let exports; // The exported names and values. +let froms; // The array collecting all import-from strings. +let fudge; // true if the natural numbers start with 1. +let functionage; // The current function. +let functions; // The array containing all of the functions. +let global; // The global object; the outermost context. +let json_mode; // true if parsing JSON. +let lines; // The array containing source lines. +let mega_mode; // true if currently parsing a megastring literal. +let module_mode; // true if import or export was used. +let next_token; // The next token to be examined in the parse. +let option; // The options parameter. +let property; // The object containing the tallied property names. +let shebang; // true if a #! was seen on the first line. +let stack; // The stack of functions. +let syntax; // The object containing the parser. +let token; // The current token being examined in the parse. +let token_nr; // The number of the next token. +let tokens; // The array of tokens. +let tenure; // The predefined property registry. +let tree; // The abstract parse tree. +let var_mode; // "var" if using var; "let" if using let. +let warnings; // The array collecting all generated warnings. + +// Error reportage functions: + +function artifact(the_token) { + +// Return a string representing an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); +} + +function artifact_line(the_token) { + +// Return the fudged line number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.line + fudge; +} + +function artifact_column(the_token) { + +// Return the fudged column number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.from + fudge; +} + +function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + const warning = { // ~~ + name: "JSLintError", + column, + line, + code + }; + if (a !== undefined) { + warning.a = a; + } + if (b !== undefined) { + warning.b = b; + } + if (c !== undefined) { + warning.c = c; + } + if (d !== undefined) { + warning.d = d; + } + warning.message = supplant(bundle[code] || code, warning); + warnings.push(warning); + return warning; +} + +function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); +} + +function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + if (the_token.warning === undefined) { + the_token.warning = warn_at( + code, + the_token.line, + the_token.from, + a || artifact(the_token), + b, + c, + d + ); + return the_token.warning; + } +} + +function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); +} + +// Tokenize: + +function tokenize(source) { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + +// If the source is not an array, then it is split into lines at the +// carriage return/linefeed. + + lines = ( + Array.isArray(source) + ? source + : source.split(rx_crlf) + ); + tokens = []; + + let char; // a popular character + let column = 0; // the column number of the next character + let first; // the first token + let from; // the starting column number of the token + let line = -1; // the line number of the next character + let nr = 0; // the next token number + let previous = global; // the previous token including comments + let prior = global; // the previous token excluding comments + let mega_from; // the starting column of megastring + let mega_line; // the starting line of megastring + let regexp_seen; // regular expression literal seen on this line + let snippet; // a piece of string + let source_line = ""; // the remaining line source string + let whole_line = ""; // the whole line source string + + if (lines[0].startsWith("#!")) { + line = 0; + shebang = true; + } + + function next_line() { + +// Put the next line of source in source_line. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + let at; + if ( + !option.long + && whole_line.length > 80 + && !json_mode + && first + && !regexp_seen + ) { + warn_at("too_long", line, 80); + } + column = 0; + line += 1; + regexp_seen = false; + source_line = lines[line]; + whole_line = source_line || ""; + if (source_line !== undefined) { + at = source_line.search(rx_tab); + if (at >= 0) { + if (!option.white) { + warn_at("use_spaces", line, at + 1); + } + source_line = source_line.replace(rx_tab, " "); + } + at = source_line.search(rx_unsafe); + if (at >= 0) { + warn_at( + "unsafe", + line, + column + at, + "U+" + source_line.charCodeAt(at).toString(16) + ); + } + if (!option.white && source_line.slice(-1) === " ") { + warn_at( + "unexpected_trailing_space", + line, + source_line.length - 1 + ); + } + } + return source_line; + } + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions snip, next_char, prev_char, +// some_digits, and escape help in the parsing of literals. + + function snip() { + +// Remove the last character from snippet. + + snippet = snippet.slice(0, -1); + } + + function next_char(match) { + +// Get the next character from the source line. Remove it from the source_line, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + return stop_at( + ( + char === "" + ? "expected_a" + : "expected_a_b" + ), + line, + column - 1, + match, + char + ); + } + if (source_line) { + char = source_line[0]; + source_line = source_line.slice(1); + snippet += char; + } else { + char = ""; + snippet += " "; + } + column += 1; + return char; + } + + function back_char() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the source_line. + + if (snippet) { + char = snippet.slice(-1); + source_line = char + source_line; + column -= 1; + snip(); + } else { + char = ""; + } + return char; + } + + function some_digits(rx, quiet) { + const result = source_line.match(rx); + if (result) { + char = result[1]; + column += char.length; + source_line = result[2]; + snippet += char; + } else { + char = ""; + if (!quiet) { + warn_at( + "expected_digits_after_a", + line, + column, + snippet + ); + } + } + return char.length; + } + + function escape(extra) { + next_char("\\"); + if (escapeable[char] === true) { + return next_char(); + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "u") { + if (next_char("u") === "{") { + if (json_mode) { + warn_at("unexpected_a", line, column - 1, char); + } + if (some_digits(rx_hexs) > 5) { + warn_at("too_many_digits", line, column - 1); + } + if (next_char() !== "}") { + stop_at("expected_a_before_b", line, column, "}", char); + } + return next_char(); + } + back_char(); + if (some_digits(rx_hexs, true) < 4) { + warn_at("expected_four_digits", line, column - 1); + } + return; + } + if (extra && extra.indexOf(char) >= 0) { + return next_char(); + } + warn_at("unexpected_a_before_b", line, column - 2, "\\", char); + } + + function make(id, value, identifier) { + +// Make the token object and append it to the tokens list. + + const the_token = { + from, + id, + identifier: Boolean(identifier), + line, + nr, + thru: column + }; + tokens[nr] = the_token; + nr += 1; + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + directive_mode = false; + } + +// If the token is to have a value, give it one. + + if (value !== undefined) { + the_token.value = value; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option.white. + + if ( + previous.line === line + && previous.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (previous.id === "(comment)" || previous.id === "(regexp)") + ) { + warn( + "expected_space_a_b", + the_token, + artifact(previous), + artifact(the_token) + ); + } + if (previous.id === "." && id === "(number)") { + warn("expected_a_before_b", previous, "0", "."); + } + if (prior.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// The previous token is used to detect adjacency problems. + + previous = the_token; + +// The prior token is a previous token that was not a comment. The prior token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (previous.id !== "(comment)") { + prior = previous; + } + return the_token; + } + + function parse_directive(the_comment, body) { + +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + + const result = body.match(rx_directive_part); + if (result) { + let allowed; + const name = result[1]; + const value = result[2]; + if (the_comment.directive === "jslint") { + allowed = allowed_option[name]; + if ( + typeof allowed === "boolean" + || typeof allowed === "object" + ) { + if ( + value === "" + || value === "true" + || value === undefined + ) { + option[name] = true; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } else if (value === "false") { + option[name] = false; + } else { + warn("bad_option_a", the_comment, name + ":" + value); + } + } else { + warn("bad_option_a", the_comment, name); + } + } else if (the_comment.directive === "property") { + if (tenure === undefined) { + tenure = empty(); + } + tenure[name] = true; + } else if (the_comment.directive === "global") { + if (value) { + warn("bad_option_a", the_comment, name + ":" + value); + } + declared_globals[name] = false; + module_mode = the_comment; + } + return parse_directive(the_comment, result[3]); + } + if (body) { + return stop("bad_directive_a", the_comment, body); + } + } + + function comment(snippet) { + +// Make a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + + const the_comment = make("(comment)", snippet); + if (Array.isArray(snippet)) { + snippet = snippet.join(" "); + } + if (!option.devel && rx_todo.test(snippet)) { + warn("todo_comment", the_comment); + } + const result = snippet.match(rx_directive); + if (result) { + if (!directive_mode) { + warn_at("misplaced_directive_a", line, from, result[1]); + } else { + the_comment.directive = result[1]; + parse_directive(the_comment, result[2]); + } + directives.push(the_comment); + } + return the_comment; + } + + function regexp() { + +// Parse a regular expression literal. + + let multi_mode = false; + let result; + let value; + regexp_seen = true; + + function quantifier() { + +// Match an optional quantifier. + + if (char === "?" || char === "*" || char === "+") { + next_char(); + } else if (char === "{") { + if (some_digits(rx_digits, true) === 0) { + warn_at("expected_a", line, column, "0"); + } + if (next_char() === ",") { + some_digits(rx_digits, true); + next_char(); + } + next_char("}"); + } else { + return; + } + if (char === "?") { + next_char("?"); + } + } + + function subklass() { + +// Match a character in a character class. + + if (char === "\\") { + escape("BbDdSsWw-[]^"); + return true; + } + if ( + char === "" + || char === "[" + || char === "]" + || char === "/" + || char === "^" + || char === "-" + ) { + return false; + } + if (char === " ") { + warn_at("expected_a_b", line, column, "\\u0020", " "); + } else if (char === "`" && mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char(); + return true; + } + + function ranges() { + +// Match a range of subclasses. + + if (subklass()) { + if (char === "-") { + next_char("-"); + if (!subklass()) { + return stop_at( + "unexpected_a", + line, + column - 1, + "-" + ); + } + } + return ranges(); + } + } + + function klass() { + +// Match a class. + + next_char("["); + if (char === "^") { + next_char("^"); + } + (function classy() { + ranges(); + if (char !== "]" && char !== "") { + warn_at( + "expected_a_before_b", + line, + column, + "\\", + char + ); + next_char(); + return classy(); + } + }()); + next_char("]"); + } + + function choice() { + + function group() { + +// Match a group that starts with left paren. + + next_char("("); + if (char === "?") { + next_char("?"); + if (char === "=" || char === "!") { + next_char(); + } else { + next_char(":"); + } + } else if (char === ":") { + warn_at("expected_a_before_b", line, column, "?", ":"); + } + choice(); + next_char(")"); + } + + function factor() { + if ( + char === "" + || char === "/" + || char === "]" + || char === ")" + ) { + return false; + } + if (char === "(") { + group(); + return true; + } + if (char === "[") { + klass(); + return true; + } + if (char === "\\") { + escape("BbDdSsWw^${}[]():=!.|*+?"); + return true; + } + if ( + char === "?" + || char === "+" + || char === "*" + || char === "}" + || char === "{" + ) { + warn_at( + "expected_a_before_b", + line, + column - 1, + "\\", + char + ); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column - 1, "`"); + } + } else if (char === " ") { + warn_at( + "expected_a_b", + line, + column - 1, + "\\s", + " " + ); + } else if (char === "$") { + if (source_line[0] !== "/") { + multi_mode = true; + } + } else if (char === "^") { + if (snippet !== "^") { + multi_mode = true; + } + } + next_char(); + return true; + } + + function sequence(follow) { + if (factor()) { + quantifier(); + return sequence(true); + } + if (!follow) { + warn_at("expected_regexp_factor_a", line, column, char); + } + } + +// Match a choice (a sequence that can be followed by | and another choice). + + sequence(); + if (char === "|") { + next_char("|"); + return choice(); + } + } + +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + next_char(); + if (char === "=") { + warn_at("expected_a_before_b", line, column, "\\", "="); + } + choice(); + +// Make sure there is a closing slash. + + snip(); + value = snippet; + next_char("/"); + +// Process dangling flag letters. + + const allowed = { + g: true, + i: true, + m: true, + u: true, + y: true + }; + const flag = empty(); + (function make_flag() { + if (is_letter(char)) { + if (allowed[char] !== true) { + warn_at("unexpected_a", line, column, char); + } + allowed[char] = false; + flag[char] = true; + next_char(); + return make_flag(); + } + }()); + back_char(); + if (char === "/" || char === "*") { + return stop_at("unexpected_a", line, from, char); + } + result = make("(regexp)", char); + result.flag = flag; + result.value = value; + if (multi_mode && !flag.m) { + warn_at("missing_m", line, column); + } + return result; + } + + function string(quote) { + +// Make a string token. + + let the_token; + snippet = ""; + next_char(); + + return (function next() { + if (char === quote) { + snip(); + the_token = make("(string)", snippet); + the_token.quote = quote; + return the_token; + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "\\") { + escape(quote); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char("`"); + } else { + next_char(); + } + return next(); + }()); + } + + function frack() { + if (char === ".") { + some_digits(rx_digits); + next_char(); + } + if (char === "E" || char === "e") { + next_char(); + if (char !== "+" && char !== "-") { + back_char(); + } + some_digits(rx_digits); + next_char(); + } + } + + function number() { + if (snippet === "0") { + next_char(); + if (char === ".") { + frack(); + } else if (char === "b") { + some_digits(rx_bits); + next_char(); + } else if (char === "o") { + some_digits(rx_octals); + next_char(); + } else if (char === "x") { + some_digits(rx_hexs); + next_char(); + } + } else { + next_char(); + frack(); + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + return stop_at( + "unexpected_a_after_b", + line, + column - 1, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + back_char(); + return make("(number)", snippet); + } + + function lex() { + let array; + let i = 0; + let j = 0; + let last; + let result; + let the_token; + +// This should properly be a tail recursive function, but sadly, conformant +// implementations of ES6 are still rare. This is the ideal code: + +// if (!source_line) { +// source_line = next_line(); +// from = 0; +// return ( +// source_line === undefined +// ? ( +// mega_mode +// ? stop_at("unclosed_mega", mega_line, mega_from) +// : make("(end)") +// ) +// : lex() +// ); +// } + +// Unfortunately, incompetent JavaScript engines will sometimes fail to execute +// it correctly. So for now, we do it the old fashioned way. + + while (!source_line) { + source_line = next_line(); + from = 0; + if (source_line === undefined) { + return ( + mega_mode + ? stop_at("unclosed_mega", mega_line, mega_from) + : make("(end)") + ); + } + } + + from = column; + result = source_line.match(rx_token); + +// result[1] token +// result[2] whitespace +// result[3] identifier +// result[4] number +// result[5] rest + + if (!result) { + return stop_at( + "unexpected_char_a", + line, + column, + source_line[0] + ); + } + + snippet = result[1]; + column += snippet.length; + source_line = result[5]; + +// Whitespace was matched. Call lex again to get more. + + if (result[2]) { + return lex(); + } + +// The token is an identifier. + + if (result[3]) { + return make(snippet, undefined, true); + } + +// The token is a number. + + if (result[4]) { + return number(snippet); + } + +// The token is a string. + + if (snippet === "\"") { + return string(snippet); + } + if (snippet === "'") { + if (!option.single) { + warn_at("use_double", line, column); + } + return string(snippet); + } + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (snippet === "`") { + if (mega_mode) { + return stop_at("expected_a_b", line, column, "}", "`"); + } + snippet = ""; + mega_from = from; + mega_line = line; + mega_mode = true; + +// Parsing a mega literal is tricky. First make a ` token. + + make("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + (function part() { + const at = source_line.search(rx_mega); + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + if (at < 0) { + snippet += source_line + "\n"; + return ( + next_line() === undefined + ? stop_at("unclosed_mega", mega_line, mega_from) + : part() + ); + } + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + snippet += source_line.slice(0, at); + column += at; + source_line = source_line.slice(at); + if (source_line[0] === "\\") { + stop_at("escape_mega", line, at); + } + make("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then make tokens that will become part of an expression until +// a } token is made. + + if (source_line[0] === "$") { + column += 2; + make("${"); + source_line = source_line.slice(2); + (function expr() { + const id = lex().id; + if (id === "{") { + return stop_at( + "expected_a_b", + line, + column, + "}", + "{" + ); + } + if (id !== "}") { + return expr(); + } + }()); + return part(); + } + }()); + source_line = source_line.slice(1); + column += 1; + mega_mode = false; + return make("`"); + } + +// The token is a // comment. + + if (snippet === "//") { + snippet = source_line; + source_line = ""; + the_token = comment(snippet); + if (mega_mode) { + warn("unexpected_comment", the_token, "`"); + } + return the_token; + } + +// The token is a /* comment. + + if (snippet === "/*") { + array = []; + if (source_line[0] === "/") { + warn_at("unexpected_a", line, column + i, "/"); + } + (function next() { + if (source_line > "") { + i = source_line.search(rx_star_slash); + if (i >= 0) { + return; + } + j = source_line.search(rx_slash_star); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + } + array.push(source_line); + source_line = next_line(); + if (source_line === undefined) { + return stop_at("unclosed_comment", line, column); + } + return next(); + }()); + snippet = source_line.slice(0, i); + j = snippet.search(rx_slash_star_or_slash); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + array.push(snippet); + column += i + 2; + source_line = source_line.slice(i + 2); + return comment(array); + } + +// The token is a slash. + + if (snippet === "/") { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + if (prior.identifier) { + if (!prior.dot) { + if (prior.id === "return") { + return regexp(); + } + if ( + prior.id === "(begin)" + || prior.id === "case" + || prior.id === "delete" + || prior.id === "in" + || prior.id === "instanceof" + || prior.id === "new" + || prior.id === "typeof" + || prior.id === "void" + || prior.id === "yield" + ) { + the_token = regexp(); + return stop("unexpected_a", the_token); + } + } + } else { + last = prior.id[prior.id.length - 1]; + if ("(,=:?[".indexOf(last) >= 0) { + return regexp(); + } + if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) { + the_token = regexp(); + warn("wrap_regexp", the_token); + return the_token; + } + } + if (source_line[0] === "=") { + column += 1; + source_line = source_line.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + } + return make(snippet); + } + + first = lex(); + json_mode = first.id === "{" || first.id === "["; + +// This loop will be replaced with a recursive call to lex when ES6 has been +// finished and widely deployed and adopted. + + while (true) { + if (lex().id === "(end)") { + break; + } + } +} + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + +function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!rx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!rx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property[id] === "number") { + property[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (tenure !== undefined) { + if (tenure[id] !== true) { + warn("unregistered_property_a", name); + } + } else { + if (name.identifier && rx_bad_property.test(id)) { + warn("bad_property_a", name); + } + } + property[id] = 1; + } + return id; +} + +function dispense() { + +// Deliver the next token, skipping the comments. + + const cadet = tokens[token_nr]; + token_nr += 1; + if (cadet.id === "(comment)") { + if (json_mode) { + warn("unexpected_a", cadet); + } + return dispense(); + } else { + return cadet; + } +} + +function lookahead() { + +// Look ahead one token without advancing. + + const old_token_nr = token_nr; + const cadet = dispense(true); + token_nr = old_token_nr; + return cadet; +} + +function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if (token.identifier && token.id !== "function") { + anon = token.id; + } else if (token.id === "(string)" && rx_identifier.test(token.value)) { + anon = token.value; + } + +// Attempt to match next_token with an expected id. + + if (id !== undefined && next_token.id !== id) { + return ( + match === undefined + ? stop("expected_a_b", next_token, id, artifact()) + : stop( + "expected_a_b_from_c_d", + next_token, + id, + artifact(match), + artifact_line(match), + artifact(next_token) + ) + ); + } + +// Promote the tokens, skipping comments. + + token = next_token; + next_token = dispense(); + if (next_token.id === "(end)") { + token_nr -= 1; + } +} + +// Parsing of JSON is simple: + +function json_value() { + let negative; + if (next_token.id === "{") { + return (function json_object() { + const brace = next_token; + const object = empty(); + const properties = []; + brace.expression = properties; + advance("{"); + if (next_token.id !== "}") { + (function next() { + let name; + let value; + if (next_token.quote !== "\"") { + warn( + "unexpected_a", + next_token, + next_token.quote + ); + } + name = next_token; + advance("(string)"); + if (object[token.value] !== undefined) { + warn("duplicate_a", token); + } else if (token.value === "__proto__") { + warn("bad_property_a", token); + } else { + object[token.value] = token; + } + advance(":"); + value = json_value(); + value.label = name; + properties.push(value); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("}", brace); + return brace; + }()); + } + if (next_token.id === "[") { + return (function json_array() { + const bracket = next_token; + const elements = []; + bracket.expression = elements; + advance("["); + if (next_token.id !== "]") { + (function next() { + elements.push(json_value()); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]", bracket); + return bracket; + }()); + } + if ( + next_token.id === "true" + || next_token.id === "false" + || next_token.id === "null" + ) { + advance(); + return token; + } + if (next_token.id === "(number)") { + if (!rx_JSON_number.test(next_token.value)) { + warn("unexpected_a"); + } + advance(); + return token; + } + if (next_token.id === "(string)") { + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + advance(); + return token; + } + if (next_token.id === "-") { + negative = next_token; + negative.arity = "unary"; + advance("-"); + advance("(number)"); + if (!rx_JSON_number.test(token.value)) { + warn("unexpected_a", token); + } + negative.expression = token; + return negative; + } + stop("unexpected_a"); +} + +// Now we parse JavaScript. + +function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + const id = name.id; + +// Reserved words may not be enrolled. + + if (syntax[id] !== undefined && id !== "ignore") { + warn("reserved_a", name); + } else { + +// Has the name been enrolled in this context? + + let earlier = functionage.context[id]; + if (earlier) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + +// Has the name been enrolled in an outer context? + + } else { + stack.forEach(function (value) { + const item = value.context[id]; + if (item !== undefined) { + earlier = item; + } + }); + if (earlier) { + if (id === "ignore") { + if (earlier.role === "variable") { + warn("unexpected_a", name); + } + } else { + if ( + ( + role !== "exception" + || earlier.role !== "exception" + ) + && role !== "parameter" + && role !== "function" + ) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + } + } + } + +// Enroll it. + + functionage.context[id] = name; + name.dead = true; + name.parent = functionage; + name.init = false; + name.role = role; + name.used = 0; + name.writable = !readonly; + } + } +} + +function expression(rbp, initial) { + +// This is the heart of the Pratt parser. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// nud Null denotation +// led Left denotation +// lbp Left binding power +// rbp Right binding power + +// It processes a nud (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop. It +// returns the expression's parse tree. + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement, + + if (!initial) { + advance(); + } + the_symbol = syntax[token.id]; + if (the_symbol !== undefined && the_symbol.nud !== undefined) { + left = the_symbol.nud(); + } else if (token.identifier) { + left = token; + left.arity = "variable"; + } else { + return stop("unexpected_a", token); + } + (function right() { + the_symbol = syntax[next_token.id]; + if ( + the_symbol !== undefined + && the_symbol.led !== undefined + && rbp < the_symbol.lbp + ) { + advance(); + left = the_symbol.led(left); + return right(); + } + }()); + return left; +} + +function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = next_token; + let the_value; + the_paren.free = true; + advance("("); + the_value = expression(0); + advance(")"); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (anticondition[the_value.id] === true) { + warn("unexpected_a", the_value); + } + return the_value; +} + +function is_weird(thing) { + return ( + thing.id === "(regexp)" + || thing.id === "{" + || thing.id === "=>" + || thing.id === "function" + || (thing.id === "[" && thing.arity === "unary") + ); +} + +function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + return ( + Array.isArray(b) + && a.length === b.length + && a.every(function (value, index) { + return are_similar(value, b[index]); + }) + ); + } + if (Array.isArray(b)) { + return false; + } + if (a.id === "(number)" && b.id === "(number)") { + return a.value === b.value; + } + let a_string; + let b_string; + if (a.id === "(string)") { + a_string = a.value; + } else if (a.id === "`" && a.constant) { + a_string = a.value[0]; + } + if (b.id === "(string)") { + b_string = b.value; + } else if (b.id === "`" && b.constant) { + b_string = b.value[0]; + } + if (typeof a_string === "string") { + return a_string === b_string; + } + if (is_weird(a) || is_weird(b)) { + return false; + } + if (a.arity === b.arity && a.id === b.id) { + if (a.id === ".") { + return ( + are_similar(a.expression, b.expression) + && are_similar(a.name, b.name) + ); + } + if (a.arity === "unary") { + return are_similar(a.expression, b.expression); + } + if (a.arity === "binary") { + return ( + a.id !== "(" + && are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + ); + } + if (a.arity === "ternary") { + return ( + are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + && are_similar(a.expression[2], b.expression[2]) + ); + } + if (a.arity === "function" && a.arity === "regexp") { + return false; + } + return true; + } + return false; +} + +function semicolon() { + +// Try to match a semicolon. + + if (next_token.id === ";") { + advance(";"); + } else { + warn_at( + "expected_a_b", + token.line, + token.thru, + ";", + artifact(next_token) + ); + } + anon = "anonymous"; +} + +function statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token.identifier && next_token.id === ":") { + the_label = token; + if (the_label.id === "ignore") { + warn("unexpected_a", the_label); + } + advance(":"); + if ( + next_token.id === "do" + || next_token.id === "for" + || next_token.id === "switch" + || next_token.id === "while" + ) { + enroll(the_label, "label", true); + the_label.init = true; + the_label.dead = false; + the_statement = statement(); + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token; + first.statement = true; + the_symbol = syntax[first.id]; + if (the_symbol !== undefined && the_symbol.fud !== undefined) { + the_symbol.disrupt = false; + the_symbol.statement = true; + the_statement = the_symbol.fud(); + } else { + +// It is an expression statement. + + the_statement = expression(0, true); + if (the_statement.wrapped && the_statement.id !== "(") { + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; +} + +function statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const array = []; + (function next(disrupt) { + if ( + next_token.id !== "}" + && next_token.id !== "case" + && next_token.id !== "default" + && next_token.id !== "else" + && next_token.id !== "(end)" + ) { + let a_statement = statement(); + array.push(a_statement); + if (disrupt) { + warn("unreachable_a", a_statement); + } + return next(a_statement.disrupt); + } + }(false)); + return array; +} + +function not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === global) { + warn("unexpected_at_top_level_a", thing); + } +} + +function top_level_only(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== global) { + warn("misplaced_a", the_thing); + } +} + +function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token; + the_block.arity = "statement"; + the_block.body = special === "body"; + +// Top level function bodies may include the "use strict" pragma. + + if ( + special === "body" + && stack.length === 1 + && next_token.value === "use strict" + ) { + next_token.statement = true; + advance("(string)"); + advance(";"); + } + stmts = statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option.devel && special !== "ignore") { + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; +} + +function mutation_check(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + warn("bad_assignment_a", the_thing); + return false; + } + return true; +} + +function left_check(left, right) { + +// Warn if the left is not one of these: +// e.b +// e[b] +// e() +// ?: +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !left_check(left.expression[1]) + && !left_check(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right); + return false; + } + return true; +} + +// These functions are used to specify the grammar of our language: + +function symbol(id, bp) { + +// Make a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax[id] = the_symbol; + } + return the_symbol; +} + +function assignment(id) { + +// Make an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led = function (left) { + const the_token = token; + let right; + the_token.arity = "assignment"; + right = expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "pre" + || right.arity === "post" + ) { + warn("unexpected_a", right); + } + mutation_check(left); + return the_token; + }; + return the_symbol; +} + +function constant(id, type, value) { + +// Make a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud = ( + typeof value === "function" + ? value + : function () { + token.constant = true; + if (value !== undefined) { + token.value = value; + } + return token; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; +} + +function infix(id, bp, f) { + +// Make an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, expression(bp)]; + return the_token; + }; + return the_symbol; +} + +function infixr(id, bp) { + +// Make a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + the_token.expression = [left, expression(bp - 1)]; + return the_token; + }; + return the_symbol; +} + +function post(id) { + +// Make one of the post operators. + + const the_symbol = symbol(id, 150); + the_symbol.led = function (left) { + token.expression = left; + token.arity = "post"; + mutation_check(token.expression); + return token; + }; + return the_symbol; +} + +function pre(id) { + +// Make one of the pre operators. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "pre"; + the_token.expression = expression(150); + mutation_check(the_token.expression); + return the_token; + }; + return the_symbol; +} + +function prefix(id, f) { + +// Make a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = expression(150); + return the_token; + }; + return the_symbol; +} + +function stmt(id, f) { + +// Make a statement. + + const the_symbol = symbol(id); + the_symbol.fud = function () { + token.arity = "statement"; + return f(); + }; + return the_symbol; +} + +function ternary(id1, id2) { + +// Make a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led = function (left) { + const the_token = token; + const second = expression(20); + advance(id2); + token.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, expression(10)]; + if (next_token.id !== ")") { + warn("use_open", the_token); + } + return the_token; + }; + return the_symbol; +} + +// Begin defining the language. + +syntax = empty(); + +symbol("}"); +symbol(")"); +symbol("]"); +symbol(","); +symbol(";"); +symbol(":"); +symbol("*/"); +symbol("await"); +symbol("case"); +symbol("catch"); +symbol("class"); +symbol("default"); +symbol("else"); +symbol("enum"); +symbol("finally"); +symbol("implements"); +symbol("interface"); +symbol("package"); +symbol("private"); +symbol("protected"); +symbol("public"); +symbol("static"); +symbol("super"); +symbol("void"); +symbol("yield"); + +constant("(number)", "number"); +constant("(regexp)", "regexp"); +constant("(string)", "string"); +constant("arguments", "object", function () { + warn("unexpected_a", token); + return token; +}); +constant("eval", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("false", "boolean", false); +constant("Function", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("ignore", "undefined", function () { + warn("unexpected_a", token); + return token; +}); +constant("Infinity", "number", Infinity); +constant("isFinite", "function", function () { + warn("expected_a_b", token, "Number.isFinite", "isFinite"); + return token; +}); +constant("isNaN", "function", function () { + warn("number_isNaN", token); + return token; +}); +constant("NaN", "number", NaN); +constant("null", "null", null); +constant("this", "object", function () { + if (!option.this) { + warn("unexpected_a", token); + } + return token; +}); +constant("true", "boolean", true); +constant("undefined", "undefined"); + +assignment("="); +assignment("+="); +assignment("-="); +assignment("*="); +assignment("/="); +assignment("%="); +assignment("&="); +assignment("|="); +assignment("^="); +assignment("<<="); +assignment(">>="); +assignment(">>>="); + +infix("||", 40); +infix("&&", 50); +infix("|", 70); +infix("^", 80); +infix("&", 90); +infix("==", 100); +infix("===", 100); +infix("!=", 100); +infix("!==", 100); +infix("<", 110); +infix(">", 110); +infix("<=", 110); +infix(">=", 110); +infix("in", 110); +infix("instanceof", 110); +infix("<<", 120); +infix(">>", 120); +infix(">>>", 120); +infix("+", 130); +infix("-", 130); +infix("*", 140); +infix("/", 140); +infix("%", 140); +infixr("**", 150); +infix("(", 160, function (left) { + const the_paren = token; + let the_argument; + if (left.id !== "function") { + left_check(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (next_token.id !== ")") { + (function next() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + the_paren.free = true; + if (the_argument.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + the_paren.free = false; + } + return the_paren; +}); +infix(".", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("?.", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("[", 170, function (left) { + const the_token = token; + const the_subscript = expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + const name = survey(the_subscript); + if (rx_identifier.test(name)) { + warn("subscript_a", the_subscript, name); + } + } + left_check(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; +}); +infix("=>", 170, function (left) { + return stop("wrap_parameter", left); +}); + +function do_tick() { + const the_tick = token; + the_tick.value = []; + the_tick.expression = []; + if (next_token.id !== "`") { + (function part() { + advance("(string)"); + the_tick.value.push(token); + if (next_token.id === "${") { + advance("${"); + the_tick.expression.push(expression(0)); + advance("}"); + return part(); + } + }()); + } + advance("`"); + return the_tick; +} + +infix("`", 160, function (left) { + const the_tick = do_tick(); + left_check(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; +}); + +post("++"); +post("--"); +pre("++"); +pre("--"); + +prefix("+"); +prefix("-"); +prefix("~"); +prefix("!"); +prefix("!!"); +prefix("[", function () { + const the_token = token; + the_token.expression = []; + if (next_token.id !== "]") { + (function next() { + let element; + let ellipsis = false; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + element = expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]"); + return the_token; +}); +prefix("/=", function () { + stop("expected_a_b", token, "/\\=", "/="); +}); +prefix("=>", function () { + return stop("expected_a_before_b", token, "()", "=>"); +}); +prefix("new", function () { + const the_new = token; + const right = expression(160); + if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "()", artifact(next_token)); + } + the_new.expression = right; + return the_new; +}); +prefix("typeof"); +prefix("void", function () { + const the_void = token; + warn("unexpected_a", the_void); + the_void.expression = expression(0); + return the_void; +}); + +function parameter_list() { + const list = []; + let optional; + const signature = ["("]; + if (next_token.id !== ")" && next_token.id !== "(end)") { + (function parameter() { + let ellipsis = false; + let param; + if (next_token.id === "{") { + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("{"); + signature.push("{"); + (function subparameter() { + let subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (next_token.id === ":") { + advance(":"); + advance(); + token.label = subparam; + subparam = token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + } + if (next_token.id === "=") { + advance("="); + subparam.expression = expression(); + param.open = true; + } + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return subparameter(); + } + }()); + list.push(param); + advance("}"); + signature.push("}"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else if (next_token.id === "[") { + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("["); + signature.push("[]"); + (function subparameter() { + const subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + if (next_token.id === "=") { + advance("="); + subparam.expression = expression(); + param.open = true; + } + if (next_token.id === ",") { + advance(","); + return subparameter(); + } + }()); + list.push(param); + advance("]"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else { + if (next_token.id === "...") { + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + } + if (!next_token.identifier) { + return stop("expected_identifier_a"); + } + param = next_token; + list.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (next_token.id === "=") { + optional = param; + advance("="); + param.expression = expression(0); + } else { + if (optional !== undefined) { + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } + } + }()); + } + advance(")"); + signature.push(")"); + return [list, signature.join("")]; +} + +function do_function(the_function) { + let name; + if (the_function === undefined) { + the_function = token; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + name = next_token; + enroll(name, "variable", true); + the_function.name = name; + name.init = true; + name.calls = empty(); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + if (next_token.identifier) { + name = next_token; + the_function.name = name; + advance(); + } else { + the_function.name = anon; + } + } + } else { + name = the_function.name; + } + the_function.level = functionage.level + 1; + if (mega_mode) { + warn("unexpected_a", the_function); + } + +// Don't make functions in loops. It is inefficient, and it can lead to scoping +// errors. + + if (functionage.loop > 0) { + warn("function_in_loop", the_function); + } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + the_function.context = empty(); + the_function.finally = 0; + the_function.loop = 0; + the_function.switch = 0; + the_function.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functions.push(the_function); + functionage = the_function; + if (the_function.arity !== "statement" && typeof name === "object") { + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// Parse the parameter list. + + advance("("); + token.free = false; + token.arity = "function"; + [functionage.parameters, functionage.signature] = parameter_list(); + functionage.parameters.forEach(function enroll_parameter(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + name.names.forEach(enroll_parameter); + } + }); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && next_token.line === token.line + ) { + return stop("unexpected_a", next_token); + } + if ( + next_token.id === "." + || next_token.id === "?." + || next_token.id === "[" + ) { + warn("unexpected_a"); + } + +// Restore the previous context. + + functionage = stack.pop(); + return the_function; +} + +prefix("function", do_function); + +function fart(pl) { + advance("=>"); + const the_fart = token; + the_fart.arity = "binary"; + the_fart.name = "=>"; + the_fart.level = functionage.level + 1; + functions.push(the_fart); + if (functionage.loop > 0) { + warn("function_in_loop", the_fart); + } + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + the_fart.context = empty(); + the_fart.finally = 0; + the_fart.loop = 0; + the_fart.switch = 0; + the_fart.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functionage = the_fart; + the_fart.parameters = pl[0]; + the_fart.signature = pl[1]; + the_fart.parameters.forEach(function (name) { + enroll(name, "parameter", true); + }); + if (next_token.id === "{") { + warn("expected_a_b", the_fart, "function", "=>"); + the_fart.block = block("body"); + } else { + the_fart.expression = expression(0); + } + functionage = stack.pop(); + return the_fart; +} + +prefix("(", function () { + const the_paren = token; + let the_value; + const cadet = lookahead().id; + +// We can distinguish between a parameter list for => and a wrapped expression +// with one token of lookahead. + + if ( + next_token.id === ")" + || next_token.id === "..." + || (next_token.identifier && (cadet === "," || cadet === "=")) + ) { + the_paren.free = false; + return fart(parameter_list()); + } + the_paren.free = true; + the_value = expression(0); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + if (next_token.id === "=>") { + if (the_value.arity !== "variable") { + if (the_value.id === "{" || the_value.id === "[") { + warn("expected_a_before_b", the_paren, "function", "("); + return stop("expected_a_b", next_token, "{", "=>"); + } + return stop("expected_identifier_a", the_value); + } + the_paren.expression = [the_value]; + return fart([the_paren.expression, "(" + the_value.id + ")"]); + } + return the_value; +}); +prefix("`", do_tick); +prefix("{", function () { + const the_brace = token; + const seen = empty(); + the_brace.expression = []; + if (next_token.id !== "}") { + (function member() { + let extra; + let full; + let id; + let name = next_token; + let value; + advance(); + if ( + (name.id === "get" || name.id === "set") + && next_token.identifier + ) { + if (!option.getset) { + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + next_token.id; + name = next_token; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (next_token.id === "}" || next_token.id === ",") { + if (typeof extra === "string") { + advance("("); + } + value = expression(Infinity, true); + } else if (next_token.id === "(") { + value = do_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + advance("("); + } + let the_colon = next_token; + advance(":"); + value = expression(0); + if (value.id === name.id && value.id !== "function") { + warn("unexpected_a", the_colon, ": " + name.id); + } + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + advance(":"); + value = expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (next_token.id === ",") { + advance(","); + return member(); + } + }()); + } + advance("}"); + return the_brace; +}); + +stmt(";", function () { + warn("unexpected_a", token); + return token; +}); +stmt("{", function () { + warn("naked_block", token); + return block("naked"); +}); +stmt("break", function () { + const the_break = token; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (next_token.identifier && token.line === next_token.line) { + the_label = functionage.context[next_token.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + warn( + (the_label !== undefined && the_label.dead) + ? "out_of_scope_a" + : "not_label_a" + ); + } else { + the_label.used += 1; + } + the_break.label = next_token; + advance(); + } + advance(";"); + return the_break; +}); + +function do_var() { + const the_statement = token; + const is_const = the_statement.id === "const"; + the_statement.names = []; + +// A program may use var or let, but not both. + + if (!is_const) { + if (var_mode === undefined) { + var_mode = the_statement.id; + } else if (the_statement.id !== var_mode) { + warn( + "expected_a_b", + the_statement, + var_mode, + the_statement.id + ); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + warn("var_switch", the_statement); + } + if (functionage.loop > 0 && the_statement.id === "var") { + warn("var_loop", the_statement); + } + (function next() { + if (next_token.id === "{" && the_statement.id !== "var") { + const the_brace = next_token; + advance("{"); + (function pair() { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + survey(name); + advance(); + if (next_token.id === ":") { + advance(":"); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + next_token.label = name; + the_statement.names.push(next_token); + enroll(next_token, "variable", is_const); + advance(); + the_brace.open = true; + } else { + the_statement.names.push(name); + enroll(name, "variable", is_const); + } + name.dead = false; + name.init = true; + if (next_token.id === "=") { + advance("="); + name.expression = expression(); + the_brace.open = true; + } + if (next_token.id === ",") { + advance(","); + return pair(); + } + }()); + advance("}"); + advance("="); + the_statement.expression = expression(0); + } else if (next_token.id === "[" && the_statement.id !== "var") { + const the_bracket = next_token; + advance("["); + (function element() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + advance(); + the_statement.names.push(name); + enroll(name, "variable", is_const); + name.dead = false; + name.init = true; + if (ellipsis) { + name.ellipsis = true; + } else { + if (next_token.id === "=") { + advance("="); + name.expression = expression(); + the_bracket.open = true; + } + if (next_token.id === ",") { + advance(","); + return element(); + } + } + }()); + advance("]"); + advance("="); + the_statement.expression = expression(0); + } else if (next_token.identifier) { + const name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", is_const); + if (next_token.id === "=" || is_const) { + advance("="); + name.dead = false; + name.init = true; + name.expression = expression(0); + } + the_statement.names.push(name); + } else { + return stop("expected_identifier_a", next_token); + } + }()); + semicolon(); + return the_statement; +} + +stmt("const", do_var); +stmt("continue", function () { + const the_continue = token; + if (functionage.loop < 1 || functionage.finally > 0) { + warn("unexpected_a", the_continue); + } + not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; +}); +stmt("debugger", function () { + const the_debug = token; + if (!option.devel) { + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; +}); +stmt("delete", function () { + const the_token = token; + const the_value = expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; +}); +stmt("do", function () { + const the_do = token; + not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; +}); +stmt("export", function () { + const the_export = token; + let the_id; + let the_name; + let the_thing; + + function export_id() { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + the_id = next_token.id; + the_name = global.context[the_id]; + if (the_name === undefined) { + warn("unexpected_a"); + } else { + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a"); + } + exports[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + } + + the_export.expression = []; + if (next_token.id === "default") { + if (exports.default !== undefined) { + warn("duplicate_a"); + } + advance("default"); + the_thing = expression(0); + if ( + the_thing.id !== "(" + || the_thing.expression[0].id !== "." + || the_thing.expression[0].expression.id !== "Object" + || the_thing.expression[0].name.id !== "freeze" + ) { + warn("freeze_exports", the_thing); + } + if (next_token.id === ";") { + semicolon(); + } + exports.default = the_thing; + the_export.expression.push(the_thing); + } else { + if (next_token.id === "function") { + warn("freeze_exports"); + the_thing = statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a", the_name); + } + exports[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + warn("unexpected_a", next_token); + statement(); + } else if (next_token.id === "{") { + advance("{"); + (function loop() { + export_id(); + if (next_token.id === ",") { + advance(","); + return loop(); + } + }()); + advance("}"); + semicolon(); + } else { + stop("unexpected_a"); + } + } + module_mode = true; + return the_export; +}); +stmt("for", function () { + let first; + const the_for = token; + if (!option.for) { + warn("unexpected_a", the_for); + } + not_top_level(the_for); + functionage.loop += 1; + advance("("); + token.free = true; + if (next_token.id === ";") { + return stop("expected_a_b", the_for, "while (", "for (;"); + } + if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + return stop("unexpected_a"); + } + first = expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = expression(0); + advance(";"); + the_for.inc = expression(0); + if (the_for.inc.id === "++") { + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; +}); +stmt("function", do_function); +stmt("if", function () { + let the_else; + const the_if = token; + the_if.expression = condition(); + the_if.block = block(); + if (next_token.id === "else") { + advance("else"); + the_else = token; + the_if.else = ( + next_token.id === "if" + ? statement() + : block() + ); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + the_if.disrupt = true; + } else { + warn("unexpected_a", the_else); + } + } + } + return the_if; +}); +stmt("import", function () { + const the_import = token; + if (next_token.id === "(") { + the_import.arity = "unary"; + the_import.constant = true; + the_import.statement = false; + advance("("); + const string = expression(0); + if (string.id !== "(string)") { + warn("expected_string_a", string); + } + froms.push(token.value); + advance(")"); + advance("."); + advance("then"); + advance("("); + the_import.expression = expression(0); + advance(")"); + semicolon(); + return the_import; + } + let name; + if (typeof module_mode === "object") { + warn("unexpected_directive_a", module_mode, module_mode.directive); + } + module_mode = true; + if (next_token.identifier) { + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + const names = []; + advance("{"); + if (next_token.id !== "}") { + while (true) { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (next_token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token; + if (!rx_module.test(token.value)) { + warn("bad_module_name_a", token); + } + froms.push(token.value); + semicolon(); + return the_import; +}); +stmt("let", do_var); +stmt("return", function () { + const the_return = token; + not_top_level(the_return); + if (functionage.finally > 0) { + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (next_token.id !== ";" && the_return.line === next_token.line) { + the_return.expression = expression(10); + } + advance(";"); + return the_return; +}); +stmt("switch", function () { + let dups = []; + let last; + let stmts; + const the_cases = []; + let the_disrupt = true; + const the_switch = token; + not_top_level(the_switch); + if (functionage.finally > 0) { + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token.free = true; + the_switch.expression = expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + (function major() { + const the_case = next_token; + the_case.arity = "statement"; + the_case.expression = []; + (function minor() { + advance("case"); + token.switch = true; + const exp = expression(0); + if (dups.some(function (thing) { + return are_similar(thing, exp); + })) { + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (next_token.id === "case") { + return minor(); + } + }()); + stmts = statements(); + if (stmts.length < 1) { + warn("expected_statements_a"); + return; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "break;", + artifact(next_token) + ); + } + if (next_token.id === "case") { + return major(); + } + }()); + dups = undefined; + if (next_token.id === "default") { + const the_default = next_token; + advance("default"); + token.switch = true; + advance(":"); + the_switch.else = statements(); + if (the_switch.else.length < 1) { + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + const the_last = the_switch.else[the_switch.else.length - 1]; + if (the_last.id === "break" && the_last.label === undefined) { + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; +}); +stmt("throw", function () { + const the_throw = token; + the_throw.disrupt = true; + the_throw.expression = expression(10); + semicolon(); + if (functionage.try > 0) { + warn("unexpected_a", the_throw); + } + return the_throw; +}); +stmt("try", function () { + let the_catch; + let the_disrupt; + const the_try = token; + if (functionage.try > 0) { + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (next_token.id === "catch") { + let ignored = "ignore"; + the_catch = next_token; + the_try.catch = the_catch; + advance("catch"); + if (next_token.id === "(") { + advance("("); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + if (next_token.id !== "ignore") { + ignored = undefined; + the_catch.name = next_token; + enroll(next_token, "exception", true); + } + advance(); + advance(")"); + } + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "catch", + artifact(next_token) + ); + + } + if (next_token.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; +}); +stmt("var", do_var); +stmt("while", function () { + const the_while = token; + not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; +}); +stmt("with", function () { + stop("unexpected_a", token); +}); + +ternary("?", ":"); + +// Ambulation of the parse tree. + +function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; +} + +function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all of the +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + } + }; +} + +const posts = empty(); +const pres = empty(); +const preaction = action(pres); +const postaction = action(posts); +const preamble = amble(pres); +const postamble = amble(posts); + +function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.id === "function") { + walk_statement(thing.block); + } + if (thing.arity === "pre" || thing.arity === "post") { + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + warn("unexpected_statement_a", thing); + } + postamble(thing); + } + } +} + +function walk_statement(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_statement); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + && thing.id !== "import" + ) { + warn("unexpected_expression_a", thing); + } + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + } +} + +function lookup(thing) { + if (thing.arity === "variable") { + +// Look up the variable in the current context. + + let the_variable = functionage.context[thing.id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable === undefined) { + stack.forEach(function (outer) { + const a_variable = outer.context[thing.id]; + if ( + a_variable !== undefined + && a_variable.role !== "label" + ) { + the_variable = a_variable; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (the_variable === undefined) { + if (declared_globals[thing.id] === undefined) { + warn("undeclared_a", thing); + return; + } + the_variable = { + dead: false, + parent: global, + id: thing.id, + init: true, + role: "variable", + used: 0, + writable: false + }; + global.context[thing.id] = the_variable; + } + the_variable.closure = true; + functionage.context[thing.id] = the_variable; + } else if (the_variable.role === "label") { + warn("label_a", thing); + } + if ( + the_variable.dead + && ( + the_variable.calls === undefined + || functionage.name === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + ) { + warn("out_of_scope_a", thing); + } + return the_variable; + } +} + +function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); +} + +function preaction_function(thing) { + if (thing.arity === "statement" && blockage.body !== true) { + warn("unexpected_a", thing); + } + stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); +} + +function bitwise_check(thing) { + if (!option.bitwise && bitwiseop[thing.id] === true) { + warn("unexpected_a", thing); + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + warn("unexpected_a", thing); + } +} + +function pop_block() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); +} + +function activate(name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.init = true; + } + } + blockage.live.push(name); +} + +function action_var(thing) { + thing.names.forEach(activate); +} + +preaction("assignment", bitwise_check); +preaction("binary", bitwise_check); +preaction("binary", function (thing) { + if (relationop[thing.id] === true) { + const left = thing.expression[0]; + const right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + warn("expected_string_a", right); + } + } else { + const value = right.value; + if (value === "null" || value === "undefined") { + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + warn("expected_type_string_a", right, value); + } + } + } + } +}); +preaction("binary", "==", function (thing) { + warn("expected_a_b", thing, "===", "=="); +}); +preaction("binary", "!=", function (thing) { + warn("expected_a_b", thing, "!==", "!="); +}); +preaction("binary", "=>", preaction_function); +preaction("binary", "||", function (thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + warn("and", thang); + } + }); +}); +preaction("binary", "(", function (thing) { + const left = thing.expression[0]; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + const parent = functionage.name.parent; + if (parent) { + const left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + && left_variable.dead + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } +}); +preaction("binary", "in", function (thing) { + warn("infix_in", thing); +}); +preaction("binary", "instanceof", function (thing) { + warn("unexpected_a", thing); +}); +preaction("binary", ".", function (thing) { + if (thing.expression.new) { + thing.new = true; + } +}); +preaction("statement", "{", function (thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; +}); +preaction("statement", "for", function (thing) { + if (thing.name !== undefined) { + const the_variable = lookup(thing.name); + if (the_variable !== undefined) { + the_variable.init = true; + if (!the_variable.writable) { + warn("bad_assignment_a", thing.name); + } + } + } + walk_statement(thing.initial); +}); +preaction("statement", "function", preaction_function); +preaction("unary", "~", bitwise_check); +preaction("unary", "function", preaction_function); +preaction("variable", function (thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } +}); + +function init_variable(name) { + const the_variable = lookup(name); + if (the_variable !== undefined) { + if (the_variable.writable) { + the_variable.init = true; + return; + } + } + warn("bad_assignment_a", name); +} + +postaction("assignment", "+=", function (thing) { + let right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } +}); +postaction("assignment", function (thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + if (thing.id === "=") { + if (thing.names !== undefined) { + if (Array.isArray(thing.names)) { + thing.names.forEach(init_variable); + } else { + init_variable(thing.names); + } + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.writable !== true) { + warn("bad_assignment_a", lvalue); + } + } + const right = syntax[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + warn("unexpected_a", thing.expression[1]); + } + } +}); + +function postaction_function(thing) { + delete functionage.finally; + delete functionage.loop; + delete functionage.switch; + delete functionage.try; + functionage = stack.pop(); + if (thing.wrapped) { + warn("unexpected_parens", thing); + } + return pop_block(); +} + +postaction("binary", function (thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || are_similar(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option.convert) { + if (thing.expression[0].value === "") { + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === "." || thing.id === "?.") { + if (thing.expression.id === "RegExp") { + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } +}); +postaction("binary", "&&", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "||", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "=>", postaction_function); +postaction("binary", "(", function (thing) { + let left = thing.expression[0]; + let the_new; + let arg; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option.eval) { + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + warn( + "expected_a_before_b", + left, + "new", + artifact(left) + ); + } + } + } else if (left.id === ".") { + let cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + cack = !cack; + } + if (rx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + warn("unexpected_a", the_new); + } else { + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + const paren = left.expression; + if (paren.id === "(") { + const array = paren.expression; + if (array.length === 1) { + const new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } +}); +postaction("binary", "[", function (thing) { + if (thing.expression[0].id === "RegExp") { + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + warn("weird_expression_a", thing.expression[1]); + } +}); +postaction("statement", "{", pop_block); +postaction("statement", "const", action_var); +postaction("statement", "export", top_level_only); +postaction("statement", "for", function (thing) { + walk_statement(thing.inc); +}); +postaction("statement", "function", postaction_function); +postaction("statement", "import", function (the_thing) { + const name = the_thing.name; + if (name) { + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return top_level_only(the_thing); + } +}); +postaction("statement", "let", action_var); +postaction("statement", "try", function (thing) { + if (thing.catch !== undefined) { + const the_name = thing.catch.name; + if (the_name !== undefined) { + const the_variable = functionage.context[the_name.id]; + the_variable.dead = false; + the_variable.init = true; + } + walk_statement(thing.catch.block); + } +}); +postaction("statement", "var", action_var); +postaction("ternary", function (thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || are_similar(thing.expression[1], thing.expression[2]) + ) { + warn("unexpected_a", thing); + } else if (are_similar(thing.expression[0], thing.expression[1])) { + warn("expected_a_b", thing, "||", "?"); + } else if (are_similar(thing.expression[0], thing.expression[2])) { + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + warn("wrap_condition", thing.expression[0]); + } +}); +postaction("unary", function (thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option.convert) { + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } +}); +postaction("unary", "function", postaction_function); +postaction("unary", "+", function (thing) { + if (!option.convert) { + warn("expected_a_b", thing, "Number(...)", "+"); + } + const right = thing.expression; + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } +}); + +function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + if (id !== "ignore") { + const name = the_function.context[id]; + if (name.parent === the_function) { + if ( + name.used === 0 + && ( + name.role !== "function" + || name.parent.arity !== "unary" + ) + ) { + warn("unused_a", name); + } else if (!name.init) { + warn("uninitialized_a", name); + } + } + } + }); +} + +function uninitialized_and_unused() { + +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (module_mode === true || option.node) { + delve(global); + } + functions.forEach(delve); +} + +// Go through the token list, looking at usage of whitespace. + +function whitage() { + let closer = "(end)"; + let free = false; + let left = global; + let margin = 0; + let nr_comments_skipped = 0; + let open = true; + let opening = true; + let right; + + function pop() { + const previous = stack.pop(); + closer = previous.closer; + free = previous.free; + margin = previous.margin; + open = previous.open; + opening = previous.opening; + } + + function push() { + stack.push({ + closer, + free, + margin, + open, + opening + }); + } + + function expected_at(at) { + warn( + "expected_a_at_b_c", + right, + artifact(right), + fudge + at, + artifact_column(right) + ); + } + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function no_space() { + if (left.line === right.line) { + if (left.thru !== right.from && nr_comments_skipped === 0) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (open) { + const at = ( + free + ? margin + : margin + 8 + ); + if (right.from < at) { + expected_at(at); + } + } else { + if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line || !open) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (right.from !== margin) { + expected_at(margin); + } + } + } + + stack = []; + tokens.forEach(function (the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + + const new_closer = opener[left.id]; + if (typeof new_closer === "string") { + if (new_closer !== right.id) { + opening = left.open || (left.line !== right.line); + push(); + closer = new_closer; + if (opening) { + free = closer === ")" && left.free; + open = true; + margin += 4; + if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (right.switch) { + at_margin(-4); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + free = false; + open = false; + no_space_only(); + } + } else { + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like '{]') have already been detected. + + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } + } else { + if (right.statement === true) { + if (left.id === "else") { + one_space_only(); + } else { + at_margin(0); + open = false; + } + +// If right is a closer, then pop the previous state. + + } else if (right.id === closer) { + pop(); + if (opening && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-4); + } else if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + one_space(); + } else { + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + at_margin(0); + } else { + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + no_space(); + } else if ( + left.id === "." + || left.id === "?." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + ) { + no_space_only(); + } else if (right.id === "." || right.id === "?.") { + no_space_only(); + } else if (left.id === ";") { + if (open) { + at_margin(0); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + one_space_only(); + } else if ( + left.id === "var" + || left.id === "const" + || left.id === "let" + ) { + push(); + closer = ";"; + free = false; + open = left.open; + if (open) { + margin = margin + 4; + at_margin(0); + } else { + one_space_only(); + } + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +// The jslint function itself. + +export default Object.freeze(function jslint( + source = "", + option_object = empty(), + global_array = [] +) { + try { + warnings = []; + option = Object.assign(empty(), option_object); + anon = "anonymous"; + block_stack = []; + declared_globals = empty(); + directive_mode = true; + directives = []; + early_stop = true; + exports = empty(); + froms = []; + fudge = ( + option.fudge + ? 1 + : 0 + ); + functions = []; + global = { + id: "(global)", + body: true, + context: empty(), + from: 0, + level: 0, + line: 0, + live: [], + loop: 0, + switch: 0, + thru: 0 + }; + blockage = global; + functionage = global; + json_mode = false; + mega_mode = false; + module_mode = false; + next_token = global; + property = empty(); + shebang = false; + stack = []; + tenure = undefined; + token = global; + token_nr = 0; + var_mode = undefined; + populate(standard, declared_globals, false); + populate(global_array, declared_globals, false); + Object.keys(option).forEach(function (name) { + if (option[name] === true) { + const allowed = allowed_option[name]; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } + }); + tokenize(source); + advance(); + if (json_mode) { + tree = json_value(); + advance("(end)"); + } else { + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option.browser) { + if (next_token.id === ";") { + advance(";"); + } + } else { + +// If we are not in a browser, then the file form of strict pragma may be used. + + if ( + next_token.value === "use strict" + ) { + advance("(string)"); + advance(";"); + } + } + tree = statements(); + advance("(end)"); + functionage = global; + walk_statement(tree); + if (warnings.length === 0) { + uninitialized_and_unused(); + if (!option.white) { + whitage(); + } + } + } + if (!option.browser) { + directives.forEach(function (comment) { + if (comment.directive === "global") { + warn("missing_browser", comment); + } + }); + } + early_stop = false; + } catch (e) { + if (e.name !== "JSLintError") { + warnings.push(e); + } + } + return { + directives, + edition: "2020-07-02", + exports, + froms, + functions, + global, + id: "(JSLint)", + json: json_mode, + lines, + module: module_mode === true, + ok: warnings.length === 0 && !early_stop, + option, + property, + shebang: ( + shebang + ? lines[0] + : undefined + ), + stop: early_stop, + tokens, + tree, + warnings: warnings.sort(function (a, b) { + return a.line - b.line || a.column - b.column; + }) + }; +}); diff --git a/branch.rx_token/report.js b/branch.rx_token/report.js new file mode 100644 index 000000000..5e487d143 --- /dev/null +++ b/branch.rx_token/report.js @@ -0,0 +1,222 @@ +// report.js +// 2018-10-22 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Generate JSLint HTML reports. + +/*property + closure, column, context, edition, error, exports, filter, forEach, freeze, + froms, fudge, function, functions, global, id, isArray, join, json, keys, + length, level, line, lines, message, module, name, names, option, + parameters, parent, property, push, replace, role, signature, sort, stop, + warnings +*/ + +const rx_amp = /&/g; +const rx_gt = />/g; +const rx_lt = / with less destructive entities. + + return String( + string + ).replace( + rx_amp, + "&" + ).replace( + rx_lt, + "<" + ).replace( + rx_gt, + ">" + ); +} + +export default Object.freeze({ + error: function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + let fudge = Number(Boolean(data.option.fudge)); + let output = []; + if (data.stop) { + output.push("
    JSLint was unable to finish.
    "); + } + data.warnings.forEach(function (warning) { + output.push( + "
    ", + entityify(warning.line + fudge), + ".", + entityify(warning.column + fudge), + "
    ", + entityify(warning.message), + "
    ", + entityify(data.lines[warning.line] || ""), + "" + ); + }); + return output.join(""); + }, + + function: function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + let fudge = Number(Boolean(data.option.fudge)); + let mode = ( + data.module + ? "module" + : "global" + ); + let output = []; + + if (data.json) { + return ( + data.warnings.length === 0 + ? "
    JSON: good.
    " + : "
    JSON: bad.
    " + ); + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + let global = Object.keys(data.global.context).sort(); + let froms = data.froms.sort(); + let exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + let context = the_function.context; + let list = Object.keys(context); + output.push( + "
    ", + entityify(the_function.line + fudge), + "
    ", + ( + the_function.name === "=>" + ? entityify(the_function.signature) + " =>" + : ( + typeof the_function.name === "string" + ? "«" + entityify(the_function.name) + "»" + : "" + entityify(the_function.name.id) + "" + ) + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + let params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.parent === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.parent === the_function + ); + })); + detail("outer", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.parent !== the_function + && the_variable.parent.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].parent.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + output.push( + "
    JSLint edition ", + entityify(data.edition), + "
    " + ); + return output.join(""); + }, + + property: function property_directive(data) { + +// Produce the /*property*/ directive. + + let not_first = false; + let output = ["/*property"]; + let length = 1111; + let properties = Object.keys(data.property); + + if (properties.length > 0) { + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length >= 80) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); + } + } +}); diff --git a/branch.sort_const_let_var/README b/branch.sort_const_let_var/README new file mode 100644 index 000000000..f46e91069 --- /dev/null +++ b/branch.sort_const_let_var/README @@ -0,0 +1,42 @@ +JSLint, The JavaScript Code Quality Tool + +Douglas Crockford +douglas@crockford.com + +2018-05-28 + +jslint.js contains the jslint function. It parses and analyzes a source file, +returning an object with information about the file. It can also take an object +that sets options. + +index.html runs the jslint.js function in a web page. The page also depends +browser.js and report.js and jslint.css. + +jslint.css provides styling for index.html. + +browser.js runs the web user interface. + +report.js generates the results reports in HTML. + +help.html describes JSLint's usage. Please read it. + +function.html describes the jslint function and the results it produces. + +Please direct questions and comments to +https://plus.google.com/communities/104441363299760713736. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[http://www.crockford.com/wrrrld/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. diff --git a/branch.sort_const_let_var/browser.js b/branch.sort_const_let_var/browser.js new file mode 100644 index 000000000..5086fed85 --- /dev/null +++ b/branch.sort_const_let_var/browser.js @@ -0,0 +1,158 @@ +// browser.js +// 2018-06-16 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +/*jslint + browser +*/ + +/*property + checked, create, disable, display, error, focus, forEach, function, + getElementById, innerHTML, join, length, map, onchange, onclick, onscroll, + property, querySelectorAll, scrollTop, select, split, style, title, value +*/ + +import jslint from "./jslint.js"; +import report from "./report.js"; + +// This is the web script companion file for JSLint. It includes code for +// interacting with the browser and displaying the reports. + +let rx_crlf = /\n|\r\n?/; +let rx_separator = /[\s,;'"]+/; + +let fudge_unit = 0; + +const aux = document.getElementById("JSLINT_AUX"); +const boxes = document.querySelectorAll("[type=checkbox]"); +const fudge = document.getElementById("JSLINT_FUDGE"); +const global = document.getElementById("JSLINT_GLOBAL"); +const number = document.getElementById("JSLINT_NUMBER"); +const property = document.getElementById("JSLINT_PROPERTY"); +const property_fieldset = document.getElementById("JSLINT_PROPERTYFIELDSET"); +const report_field = document.getElementById("JSLINT_REPORT"); +const report_list = document.getElementById("JSLINT_REPORT_LIST"); +const source = document.getElementById("JSLINT_SOURCE"); +const select = document.getElementById("JSLINT_SELECT"); +const warnings = document.getElementById("JSLINT_WARNINGS"); +const warnings_list = document.getElementById("JSLINT_WARNINGS_LIST"); + +function show_numbers() { + number.value = source.value.split(rx_crlf).map(function (ignore, index) { + return index + fudge_unit; + }).join("\n"); +} + +function clear() { + aux.style.display = "none"; + number.value = ""; + property.value = ""; + property_fieldset.style.display = "none"; + report_field.style.display = "none"; + report_list.innerHTML = ""; + source.focus(); + source.value = ""; + warnings.style.display = "none"; + warnings_list.innerHTML = ""; +} + +function fudge_change() { + fudge_unit = Number(fudge.checked); + show_numbers(); +} + +function clear_options() { + boxes.forEach(function (node) { + node.checked = false; + }); + fudge_change(); + global.innerHTML = ""; +} + +function call_jslint() { + +// First build the option object. + + let option = Object.create(null); + boxes.forEach(function (node) { + if (node.checked) { + option[node.title] = true; + } + }); + +// Call JSLint with the source text, the options, and the predefined globals. + + let global_string = global.value; + let result = jslint( + source.value, + option, + ( + global_string === "" + ? undefined + : global_string.split(rx_separator) + ) + ); + +// Generate the reports. + + let error_html = report.error(result); + let function_html = report.function(result); + let property_text = report.property(result); + +// Display the reports. + + warnings_list.innerHTML = error_html; + warnings.style.display = ( + error_html.length === 0 + ? "none" + : "block" + ); + + report_list.innerHTML = function_html; + report_field.style.display = "block"; + if (property_text) { + property.value = property_text; + property_fieldset.style.display = "block"; + property.scrollTop = 0; + select.disable = false; + } else { + property_fieldset.style.display = "none"; + select.disable = true; + } + aux.style.display = "block"; + source.select(); +} + +fudge.onchange = fudge_change; + +source.onchange = function (ignore) { + show_numbers(); +}; + +source.onscroll = function () { + let ss = source.scrollTop; + number.scrollTop = ss; + let sn = number.scrollTop; + if (ss > sn) { + show_numbers(); + number.scrollTop = ss; + } +}; + +document.querySelectorAll("[name='JSLint']").forEach(function (node) { + node.onclick = call_jslint; +}); + +document.querySelectorAll("[name='clear']").forEach(function (node) { + node.onclick = clear; +}); + +document.getElementById("JSLINT_SELECT").onclick = function () { + property.focus(); + property.select(); +}; +document.getElementById("JSLINT_CLEAR_OPTIONS").onclick = clear_options; + +fudge_change(); +source.select(); +source.focus(); diff --git a/branch.sort_const_let_var/cli.js b/branch.sort_const_let_var/cli.js new file mode 100755 index 000000000..a220c2547 --- /dev/null +++ b/branch.sort_const_let_var/cli.js @@ -0,0 +1,130 @@ +#!/usr/bin/env node +/* + * cli.js + * simple cli-tool to run jslint.js from command-line + * + * instruction: + * 1. save this file as cli.js + * 2. download and save in same directory, latest jslint.js from + * https://github.com/douglascrockford/JSLint/blob/master/jslint.js + * + * example usage: + * $ node cli.js ./jslint.js // jslint local-file + * $ node cli.js https://code.jquery.com/jquery-1.0.0.js // jslint remote-file + */ + + + +/*jslint node */ +/*property + argv, basename, column, concat, end, error, exit, filter, forEach, fudge, + join, jslint, line, lines, main, match, message, on, option, parse, push, + readFile, replace, request, runInNewContext, slice, trim, trimRight, + warnings +*/ +"use strict"; +let chunk_list; +let file; +let fs; +let fudge; +let local; +let modeNext; +let onNext; +let path; +let url; +let vm; +let warning_text; +onNext = function (error, data) { + if (error) { + console.error(error.message || error); + process.exit(1); + } + modeNext += 1; + switch (modeNext) { + case 1: + // run this program only in cli-mode + if (module !== require.main) { + console.error( + "jslint-cli can only be run from the command-line" + ); + return; + } + // require builtins + fs = require("fs"); + path = require("path"); + url = require("url"); + vm = require("vm"); + // init local namespace + local = {}; + if (!process.argv[2]) { + console.error( + "error: no specified\n" + + "usage: " + path.basename(__filename) + " \n" + + "example: " + path.basename(__filename) + " example.js\n" + + "example: " + path.basename(__filename) + + " https://jslint.com/jslint.js" + ); + process.exit(1); + } + // init jslint + fs.readFile(path.join(__dirname, "jslint.js"), "utf8", onNext); + break; + case 2: + // bug-workaround - nodejs does not widely support es-modules + vm.runInNewContext( + data.replace("export default function", "function"), + local + ); + // read data from file + file = process.argv[2]; + console.error("jslint " + file); + data = file.match(/^(https?):\/\//); + if (!data) { + fs.readFile(file, "utf8", onNext); + return; + } + // read data from http/https url + require(data[1]).request(url.parse(file), function (response) { + chunk_list = []; + response + .on("data", function (chunk) { + chunk_list.push(chunk); + }) + .on("end", function () { + onNext(null, String(Buffer.concat(chunk_list))); + }) + .on("error", onNext); + }).on("error", onNext).end(); + break; + case 3: + // jslint data + data = local.jslint(data + // ignore first-line shebang in nodejs scripts + .replace((/^#!/), "//")); + fudge = data.option.fudge || 0; + // init warning_text + warning_text = ""; + // print warnings to stderrr + data.warnings + .filter(function (warning) { + return warning && warning.message; + }) + // print only first 10 warnings + .slice(0, 10) + .forEach(function (warning, ii) { + warning_text += ( + (" #" + (ii + 1)).slice(-3) + + " \u001b[31m" + warning.message + "\u001b[39m\n" + + " " + String(data.lines[warning.line] || "").trim() + + "\u001b[90m \/\/ line " + + (warning.line + fudge) + + ", col " + (warning.column + fudge) + + "\u001b[39m\n" + ); + }); + console.error(warning_text.trimRight() || "ok"); + break; + } +}; +modeNext = 0; +onNext(); diff --git a/branch.sort_const_let_var/function.html b/branch.sort_const_let_var/function.html new file mode 100644 index 000000000..7d8271246 --- /dev/null +++ b/branch.sort_const_let_var/function.html @@ -0,0 +1,618 @@ + + + + + + + + +JSLint: jslint function + + + +
    JSLint
    + +
    The jslint Function
    +
    +

    JSLint

    +

    JSLint is delivered as a set of files:

    +
    + + + + + + + + + + + + + + +
    FilenameContent
    browser.jsBrowser based UI.
    function.htmlThis page that you are looking at right now.
    help.htmlUsing jslint as a single page JSLint application.
    index.htmlSingle page JSLint application that uses the + js files.
    jslint.jsThe jslint function.
    report.jsThe report object, containing report generator functions for + HTML.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    +
    + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring"(JSLint)"
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    parenttokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    parenttokenThe function in which the variable was declared.variable
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    shebangstringThe first line of text if it started with #!line
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable can be assigned to.variable
    +

    Reports

    +

    The report object contains three functions that all take a + result from the jslint function as input. + report.js has no other dependence on other files.

    + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    +
    + +
    + + + + + +
    + + diff --git a/branch.sort_const_let_var/help.html b/branch.sort_const_let_var/help.html new file mode 100644 index 000000000..94b431214 --- /dev/null +++ b/branch.sort_const_let_var/help.html @@ -0,0 +1,835 @@ + + + + + + + + + +JSLint: Help + + + +
    JSLint
    + +
    Help
    +
    +
    Il semble que la perfection soit atteinte non quand il n’y a + plus rien à ajouter, mais quand il n’y a plus rien à + retrancher.
    +
    + Antoine de Saint-Exupéry +
    +
    + Terre des Hommes (1939) +
    +

    Good Parts

    +

    The Principle of the Good Parts is

    +
    If a feature is sometimes useful and sometimes dangerous and if + there is a better option then always use the better option.
    +

    JSLint is a JavaScript program that looks for problems in + JavaScript programs. It is a code quality tool.

    +

    When C was + a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    +

    As the language matured, the definition of the language was + strengthened to eliminate some insecurities, and compilers got better + at issuing warnings. lint is no longer needed.

    +

    JavaScript is a + young-for-its-age language. It was originally intended to do small tasks in + webpages, tasks for which Java was too heavy and clumsy. But JavaScript is + a surprisingly capable language, and it is now being used in larger + projects. Many of the features that were intended to make the language easy + to use are troublesome when projects become complicated. A lint + for JavaScript is needed: JSLint, a JavaScript syntax checker + and validator.

    +

    JSLint takes a JavaScript source and scans it. If it finds a + problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by the ECMAScript Programming Language + Standard (the strangely named document that governs JavaScript). + JSLint will reject most legal programs. It is a higher + standard.

    +

    JavaScript is a sloppy language, but hidden deep inside there is an elegant, + better language. JSLint helps you to program in that better + language and to avoid most of the slop. JSLint will reject + programs that browsers will accept because JSLint is concerned + with the quality of your code and browsers are not. You should gladly accept + all of JSLint's advice.

    +

    JSLint can operate on JavaScript source + or JSON text.

    +

    ECMAScript Sixth Edition

    +

    Some of + ES6’s features are good, so JSLint will recognize the good + parts of ES6.

    +

    Currently, these features are recognized:

    +
      +
    • The ... ellipsis marker in parameter + lists and argument lists, replacing the arguments object + for variadic functions.
    • +
    • The let statement, which is like the var + statement except that it respects block scope. + You may use let or var but not both.
    • +
    • The const statement is like the let statement + except that it disallows the use of assignment on the variable, although + if the value of the variable is mutable, it can still be mutated. + const is preferred to let. +
    • Destructuring of arrays and objects is allowed in parameter lists and on + the left side of let, and const, but not + var or assignment statements, and not deep destructuring + or eliding.
    • +
    • Enhanced object literals, providing shorter forms for function + declaration and properties that are initialized by variables with the + same name.
    • +
    • The fat arrow => fart functions.
    • +
    • The simplest forms of import and export.
    • +
    • `Megastring` literals, but not nested + `megastring` literals.
    • +
    • New global functions, such as Map, Set, + WeakMap, and WeakSet.
    • +
    • 0b- and 0o- number literals.
    • +
    +

    The most important new feature of ES6 is proper tail calls. This has no + new syntax, so JSLint doesn’t see it. But it makes recursion + much more attractive, which makes loops, particularly for + loops, much less attractive.

    +

    import export

    +

    The ES6 module feature will be an important improvement over JavaScript’s + global variables as a means of linking separate files together. + JSLint recognizes a small but essential subset of the module + syntax.

    +
    +

    import name from stringliteral;

    +

    import {name} from stringliteral;

    +

    export default function () {};

    +

    export default function name() {};

    +

    export default expression;

    +

    export function name() {}

    +

    export name; // where name is const

    +

    export {name};

    +
    +

    Directives

    +

    JSLint provides three directives that may be placed in a + file to manage JSLint’s behavior. Use of these directives is + optional. If they are used, they should be placed in a source file before + the first statement. They are written in the form of a comment, where the + directive name is placed immediately after the opening of the comment before + any whitespace. The three directives are global, + jslint, and property. Directives in a file are + stronger than options selected from the UI or passed with the option object.

    +

    /*global*/

    +

    The /*global*/ directive is used to specify a set of globals + (usually functions and objects containing functions) that are available to + this file. This was commonly used in browsers to link source files together + before ES6 modules appeared. Use of global variables is strongly + discouraged, but unfortunately web browsers require their use. The + /*global*/ directive can only be used when the + Assume a browser option is selected. +

    Each of the names listed will indicate a read-only global variable. The names + are separated by , comma. This directive + should not be used if import or export is used. + This directive inhibits warnings, but it does not declare the names in the + execution environment. +

    For example: +

    /*global +
    ADSAFE, report, jslint
    +*/
    +

    instructs JSLint to not give warnings about the global + variables ADsafe, report, and jslint. + However, if any of those names are expected to be supplied by other files + and those other files fail to do so, then execution errors will result. It + is usually better to use top-level variable declarations instead: +

        var ADSAFE;
    +    var report;
    +    var jslint; 
    +

    Using var in this way allows comparing a global variable to the + undefined value to determine whether it is has been used in the global context.

    +

    /*jslint*/

    +

    The /*jslint*/ directive allows for the control of several + options. These options can also be set from the + JSLint.com user interface.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Tolerate bitwise operatorsbitwisetrue if bitwise operators should be allowed. The + bitwise operators are rarely used in JavaScript programs, so it + is usually more likely that & is a mistyping of + && than that it indicates a bitwise and. + JSLint will give warnings on the bitwise operators + unless this option is selected.
    Assume a browser browsertrue if the standard browser globals should be + predefined. This option will reject the use of + import and export. This option also + disallows the file form of the "use + strict" pragma. It does not supply + self; you will have to request that unnecessary + alias of the dreaded global object yourself. It adds the same + globals as this directive: +
    /*global +
    + clearInterval, clearTimeout, document, event, FileReader, + FormData, history, localStorage, location, name, navigator, + screen, sessionStorage, setInterval, setTimeout, Storage, + URL, window, XMLHttpRequest +
    + */
    +
    Tolerate conversion operatorsconverttrue if !!, + prefix, + and concatenation with "" are allowed. + The Boolean, Number, and + String functions are preferred
    Assume CouchDBcouchtrue if + Couch DB + globals should be predefined. It adds the same globals as this + directive: +
    /*global +
    emit, getRow, isArray, log, provides, registerType, require, + send, start, sum, toJSON
    + */
    Assume in developmentdeveltrue if browser globals that are useful in + development should be predefined, and if debugger + statements and TODO comments + should be allowed. It adds the + same globals as this directive: +
    /*global +
    alert, confirm, console, prompt
    + */
    Be sure to turn this option off before going + into production.
    Tolerate evalevaltrue if eval should be allowed. In the + past, the eval function was the most misused + feature of the language.
    Tolerate for statementfortrue if the for statement should be + allowed. It is almost always better to use the array methods + instead.
    Fudge the line and column numbersfudgetrue if the origin of a text file is line 1 column + 1. Programmers know that number systems start at zero. + Unfortunately, most text editors start numbering lines and + columns at one. JSLint will by default start + numbering at zero. Use this option to start the numbering at one + in its reports.
    Tolerate get and setgetsettrue if accessor properties are allowed in object literals.
    Tolerate long source lineslongtrue if a line can contain more than 80 characters.
    Tolerate multiple variables declarations per statementmultivartrue if a var, let, or + const statement can declare two or more variables + in a single statement.
    Assume Node.jsnodetrue if Node.js globals should be predefined. It + will predefine globals that are used in the Node.js environment. + It adds the same globals as this directive: +
    /*global +
    + Buffer, clearImmediate, clearInterval, clearTimeout, console, exports, + module, process, require, setImmediate, setInterval, setTimeout, URL, + URLSearchParams, __dirname, __filename +
    + */
    Tolerate single quote stringssingletrue if 'single quote + should be allowed to enclose string literals.
    Tolerate this
    thistrue if this should be allowed.
    Tolerate whitespace messwhitetrue if the whitespace rules should be ignored.
    +

    For example:

    +
    /*jslint
    +    bitwise, node
    +*/
    + +

    /*property*/

    +

    The /*property*/ directive is used to declare a list of + property identifiers that are used in the file. Each property name in the + program is looked up in this list. If a name is not found, that indicates an + error, most likely a typing error.

    +

    The list can also be used to evade some of JSLint’s naming + rules.

    +

    JSLint can build the /*property*/ list for you. At + the bottom of its report, JSLint displays a + /*property*/ directive. It contains all of the names that were + used with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. You + can copy the /*property*/ directive to the top of your + script file. JSLint will check the spelling of all property + names against the list. That way, you can have JSLint look for + misspellings for you.

    +

    For example,

    +
    /*property +
    charAt, slice, _$_
    + */
    +

    ignore

    +

    JSLint introduces a new reserved word: ignore. It + is used in parameter lists and in catch clauses to indicate a + parameter that will be ignored. Unused warnings will not be produced for + ignore. +

    function handler(ignore, value) {
    +    return do_something_useful(value);
    +}
    +

    var let const

    +

    JavaScript provides three statements for declaring variables: + var, let, and const. The + var statement suffers from a bad practice called hoisting. The + let statement does not do hoisting and respects block scope. + The const statement is like let except that it + marks the variable (but not its contents) as read only, making it an error + to attempt to assign to the variable. When given a choice, + const is the best, var is the worst.

    +

    JSLint uses the intersection of the var rules and the + let rules, and by doing so avoids the errors related to either. + A name should be declared only once in a function. It should be declared + before it is used. It should not be used outside of the block in which it is + declared. A variable should not have the same name as a variable or + parameter in an outer function. Do not mix var and + let. Declare one name per statement.

    +

    = == ===

    +

    Fortran made a terrible + mistake in using the equality operator as its assignment operator. That + mistake has been replicated in most languages since then. C compounded that + mistake by making == its equality operator. The visual + similarity is a source of errors. JavaScript compounded this further by + making == a type coercing comparison operator that produces + false positive results. This was mitigated by adding the === + operator, leaving the broken == operator in place.

    +

    JSLint attempts to minimize errors by the following rules:

    +

    == is not allowed. This avoids the false positives, and + increases the visual distance between = and ===. + Assignments are not allowed in expression position, and comparisons are not + allowed in statement position. This also reduces confusion. 

    +

    Semicolon

    +

    JavaScript uses a C-like syntax that requires the use of semicolons to + delimit certain statements. JavaScript attempts to make those semicolons + optional with an automatic semicolon insertion mechanism, but it does not + work very well. Automatic semicolon insertion was added to make + things easier for beginners. Unfortunately, it sometimes fails. Do not rely + on it unless you are a beginner.

    +

    JSLint expects that every statement will be followed by + ; except for for, function, + if, switch, try, and + while. JSLint does not expect to see unnecessary + semicolons, the empty statement, or empty blocks.

    +

    function =>

    +

    JavaScript has four syntactic forms for making function objects: function + statements, function expressions, enhanced object literals, and the + => fart operator.

    +

    The function statement creates a variable and assigns the function object to + it. It should be used in a file or function body, but not inside of a block.

    +
    function name(parameters) { +
    statements
    + }
    +

    The function expression unfortunately looks like the function statement. It + may appear anywhere that an expression may appear, but not in statement + position and not in a loop. It produces a function object but does not + create a variable in which to store it.

    +
    function (parameters) { +
    statements
    + }
    +

    The enhanced object literal provides an ES6 shorthand for creating a property + whose value is a function, saving you from having to type + : colon and function.

    +
    { +
    name(parameters) { +
    statements
    + }
    + }
    +

    Finally, ES6 provides an even shorter form of function expression that leaves + out the words function and return:

    +
    (parameters) => + expression
    +

    JSLint requires the parens around the parameters, and forbids a + { left brace after the + => fart to avoid syntactic ambiguity.

    +

    Comma

    +

    The , comma operator is unnecessary and can + mask programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as + an operator. It does not expect to see elided elements in array literals. A + comma should not appear after the last element of an array literal or object + literal.

    +

    Blocks

    +

    JSLint expects blocks with function, + if, switch, while, for, + do, and try statements and nowhere else.

    +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    +

    JavaScript allows an if to be written like this:

    +
    if (condition)
    +    statement;
    +

    That form is known to contribute to mistakes in projects where many + programmers are working on the same code. That is why JSLint + expects the use of a block:

    +
    if (condition) {
    +    statements;
    +}
    +

    Experience shows that this form is more resilient.

    +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call. All other expression statements are considered + to be errors.

    +

    for

    +

    JSLint does not recommend use of the for statement. + Use array methods like forEach instead. The for + option will suppress some warnings. The forms of for that + JSLint accepts are restricted, excluding the new ES6 forms.

    +

    for in

    +

    JSLint does not recommend use of the for + in statement. Use Object.keys instead.

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, it also + loops through all of the properties that were inherited through the + prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written + without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    for (name in object) {
    +    if (object.hasOwnProperty(name)) {
    +        ....
    +    }
    +}
    +

    Note that the above code will fail if the object contains a property + named hasOwnProperty. Use Object.keys instead.

    +

    switch

    +

    A common error in switch statements is to forget to place a + break statement after each case, resulting in unintended + fall-through. JSLint expects that the statement before the next + case or default is one of these: + break, return, or throw.

    +

    with

    +

    The with statement was intended to provide a shorthand in + accessing properties in deeply nested objects. Unfortunately, it behaves + very badly when setting new properties. Never use the with + statement.

    +

    JSLint does not expect to see a with statement.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that + labels will be distinct from vars and parameters.

    +

    Unreachable Code

    +

    JSLint expects that a return, break, + or throw statement will be followed by a + } right brace or case or + default.

    +

    Confusing Pluses and Minuses

    +

    JSLint expects that + will not be followed by + + or ++, and that - will not be + followed by - or --. A misplaced space can turn + + + into ++, an error that is difficult to see. + Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ increment and + -- decrement operators have been known to + contribute to bad code by encouraging excessive trickiness. They are second + only to faulty architecture in enabling to viruses and other security + menaces. Also, preincrement/postincrement confusion can produce off-by-one + errors that are extremely difficult to diagnose. Fortunately, they are also + complete unnecessary. There are better ways to add 1 to a variable.

    +

    It is best to avoid these operators entirely and rely on += and + -= instead.

    +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. + JSLint looks for problems that may cause portability problems. + It also attempts to resolve visual ambiguities by recommending explicit + escapement.

    +

    JavaScript’s syntax for regular expression literals overloads the + / slash character. To avoid ambiguity, + JSLint expects that the character preceding a regular + expression literal is a ( left paren or + = equal or + : colon or + , comma character.

    +

    this

    +
    Having this in the language makes it harder to talk + about the language. It is like pair programming with Abbott and Costello. +
    +

    Avoid using this. Warnings about this can be + suppressed with option.this.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the + new prefix. The new prefix creates a new object + based on the function's prototype, and binds that object to the + function's implied this parameter. If you neglect to use the + new prefix, no new object will be made and this + will be bound to the global object.

    +

    JSLint enforces the convention that constructor functions be + given names with initial uppercase. JSLint does not expect to + see a function invocation with an initial uppercase name unless it has the + new prefix. JSLint does not expect to see the + new prefix used with functions whose names do not start with + initial uppercase.

    +

    JSLint does not expect to see the wrapper forms + new Number, new String, new Boolean.

    +

    JSLint does not expect to see new Object. + Use Object.create(null) instead.

    +

    Whitespace

    +

    JSLint has a specific set of rules about the use of whitespace. + Where possible, these rules are consistent with centuries of good practice + with literary style.

    +

    The indentation increases by 4 spaces when the last token on a line is + { left brace, + [ left bracket, + ( left paren. The matching closing + token will be the first token on a line, restoring the previous + indentation.

    +

    The ternary operator can be visually confusing, so + ? question mark and + : colon always begin a line and increase + the indentation by 4 spaces.

    +
    return (
    +    (the_token.id === "(string)" || the_token.id === "(number)")
    +    ? String(the_token.value)
    +    : the_token.id
    +);
    +

    The word function is always followed with one space.

    +

    Clauses (case, catch, default, + else, finally) are not statements and so should + not be indented like statements.

    +

    Spaces are used to make things that are not invocations look less like + invocations.

    +

    Tabs and spaces should not be mixed. We should pick just one in order to + avoid the problems that come from having both. Personal preference is an + extremely unreliable criterion. Neither offers a powerful advantage over the + other. Fifty years ago, tab had the advantage of consuming less memory, but + Moore's Law has eliminated that advantage. Space has one clear advantage + over tab: there is no reliable standard for how many spaces a tab + represents, but it is universally accepted that a space occupies a space. So + use spaces. You can edit with tabs if you must, but make sure it is spaces + again before you commit. Maybe someday we will finally get a universal + standard for tabs, but until that day comes, the better choice is spaces.

    +

    Unsafe Characters

    +

    There are characters that are handled inconsistently in some browsers, and + so must be escaped when placed in strings.

    +
    \u0000-\u001f
    +\u007f-\u009f
    +\u00ad
    +\u0600-\u0604
    +\u070f
    +\u17b4
    +\u17b5
    +\u200c-\u200f
    +\u2028-\u202f
    +\u2060-\u206f
    +\ufeff
    +\ufff0-\uffff
    +

    Report

    +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    +
      +
    • The line number on which it starts.
    • +
    • The function’s name. In the case of anonymous functions, + JSLint will attempt to + «guess» the name.
    • +
    • The parameters.
    • +
    • Variables: The variables that are declared in the function.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Exceptions: The variables that are declared by catch + clauses of try statements.
    • +
    • Outer: Variables used by this function that are declared in + other functions.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements. Updates are announced at + https://plus.google.com/communities/104441363299760713736.

    +

    Try it

    +

    Try it. Paste your script + into the window and click the + + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    JSLint is written entirely in JavaScript, so it can run + anywhere that JavaScript (or even Java) can run.

    +

    Perfectly fine

    +

    JSLint was designed to reject code that some would consider to + be perfectly fine. The reason for this is that JSLint's + purpose is to help produce programs that are free of error. That is + difficult in any language and is especially hard in JavaScript. + JSLint attempts to help you increase the visual distance + between correct programs and incorrect programs, making the remaining errors + more obvious. JSLint will give warnings about things that are + not necessarily wrong in the current situation, but which have been observed + to mask or obscure errors. Avoid those things when there are better options + available.

    +

    It is dangerous out there. JSLint is here to help.

    +

    Warning

    +

    JSLint will hurt your feelings. Side effects may include + headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, + dry mouth, cleaner code, and a reduced error rate.

    +

    Further Reading

    + +
    +

    + Code Conventions for the JavaScript Programming Language.

    + + + + + +
    + + diff --git a/branch.sort_const_let_var/index.html b/branch.sort_const_let_var/index.html new file mode 100644 index 000000000..235f44b54 --- /dev/null +++ b/branch.sort_const_let_var/index.html @@ -0,0 +1,125 @@ + + + + + + + + + + + +JSLint: The JavaScript Code Quality Tool + + +
    +
    Source
    +
    + + +
    +
    + Warnings +
    +
    +
    + Function Report +
    +
    +
    + Property Directive + +
    +
    + + + + +
    +
    Options +
    Assume... +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Global variables... + +
    +
    +
    +
    + + + + + +
    + + diff --git a/branch.sort_const_let_var/jslint.css b/branch.sort_const_let_var/jslint.css new file mode 100644 index 000000000..9c1b1a2f9 --- /dev/null +++ b/branch.sort_const_let_var/jslint.css @@ -0,0 +1,340 @@ +@font-face { + font-family: 'Programma'; + src: url('Programma.woff') format('woff'); +} +@font-face { + font-family: 'Daley'; + font-weight: bold; + src: url('Daley-Bold.woff') format('woff'); +} + +body { + background-color: antiquewhite; + color: black; + font-family: 'Daley', sans-serif; + margin: 0; + padding: 0; +} + +#JSLINT_TITLEBOX { + font-family: 'Daley', sans-serif; + margin: 0; + margin-left: 3%; + margin-right: 3%; + padding: 0; +} + +#JSLINT_TITLEBOX ul { + float: right; + font-size: 12pt; +} + +#JSLINT_TITLEBOX div { + color: darkslategray; + float: left; + font-size: 48pt; +} + +#JSLINT_ fieldset { + background-color: gainsboro; + border: 0; + clear: both; + margin-bottom: 1em; + margin-left: 3%; + margin-right: 3%; + margin-top: 1em; + padding: 0; + width: auto; +} +#JSLINT_ legend { + background-color: darkslategray; + border: 0; + color: white; + font-size: 100%; + font-style: normal; + font-weight: normal; + margin: 0; + padding-bottom: 0.25em; + padding-left: 0; + padding-right: 0; + padding-top: 0.25em; + text-align: center; + width: 100%; +} +#JSLINT_ button { + background-color: darkslategray; + border: 0; + color: white; + cursor: pointer; + font-family: 'Daley', sans-serif; + font-size: 100%; + font-style: normal; + margin-left: 1em; + margin-right: 1em; + padding-left: 1em; + padding-right: 1em; + padding-bottom: 0.25em; + padding-top: 0.25em; + text-align: center; +} +#JSLINT_ button:hover { + background-color: slategray; + color: white; + border: 0; +} + +#JSLINT_ button:active { + border: 0; + color: black; +} + +#JSLINT_ button:disabled { + background-color: gray; + border: 0; + color: gainsboro; +} + +a:visited { + color: #69565c; +} + +a:link { + color: black; +} + +#JSLINT_ input[type="text"] { + background-color: white; + border: 0; + color: black; + margin-bottom: 0.2em; + margin-right: 0.5em; + padding-left: 0.5em; + padding-right: 0.5em; + text-align: right; + width: 3em; +} + +#JSLINT_ textarea { + border: 0; + background-color: white; + border: 0; + font-family: Programma, monospace; + font-size: 100%; + font-weight: bold; + resize: none; +} + +#JSLINT_ textarea::selection { + background-color: yellow; + color: black; +} + +#JSLINT_NUMBER { + color: darkgray; + display: inline-block; + height: 4in; + margin: 2%; + margin-right: 0; + overflow: hidden; + padding-right: 0.5%; + text-align: right; + white-space: pre; + width: 4%; +} + +#JSLINT_SOURCE { + color: black; + display: inline-block; + font-size: 100%; + height: 4in; + margin: 2%; + margin-left: 0; + margin-right: 0; + overflow: auto; + padding-left: 0.5%; + white-space: pre; + width: 91%; +} + +#JSLINT_PROPERTY { + background-color: honeydew; + border: 0; + margin: 2%; + padding: 0.5%; + white-space: pre; + width: 95%; +} + +#JSLINT_GLOBAL { + white-space: normal; + width: 95%; +} +#JSLINT_ label { + font-family: sans-serif; + font-size: 90%; + padding-left: 0.25em; +} +#JSLINT_OPTIONS>div { + float: left; + margin: 0.5em; +} + +#JSLINT_ address { + color: black; + display: block; + float: right; + font-family: serif; + font-size: 90%; + margin-left: 1em; +} +#JSLINT_ dl { + background-color: cornsilk; + color: white; + margin: 0; + padding-bottom: 2pt; + padding-left: 1em; + padding-right: 1em; + padding-top: 2pt; +} +#JSLINT_ dfn { + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + margin-bottom: 2pt; +} +#JSLINT_ dt { + color: black; + display: block; + float: left; + font-family: serif; + font-size: 75%; + font-style: italic; + margin: 0; + width: 8em; + text-align: right; +} +#JSLINT_ dd { + color: black; + display: block; + font-family: Programma, monospace; + font-weight: bold; + margin-left: 8em; + padding-bottom: 2pt; +} +#JSLINT_WARNINGS>legend { + background-color: indianred; +} +#JSLINT_WARNINGS>div { + background-color: pink; + padding: 1em; +} +#JSLINT_WARNINGS cite { + color: black; + display: block; + font-family: serif; + font-size: 100%; + font-style: normal; + margin-bottom: 4pt; + margin-left: 20pt; + margin-right: 20pt; + margin-top: 4pt; + overflow-x: hidden; +} +#JSLINT_WARNINGS samp { + background-color: lavenderblush; + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + padding: 4pt; + margin-bottom: 0; + margin-left: 16pt; + margin-right: 16pt; + margin-top: 0; + white-space: pre-wrap; +} + +#JSLINT_REPORT center { + margin-top: 1em; +} + +#JSLINT_WARNINGS dl address { + color: black; + display: inline; + float: none; + font-size: 80%; + margin: 0; +} + +#JSLINT_REPORT>div { + padding: 1em; +} + +#JSLINT_ dl.level0 { + color: black; + background-color: white; +} + +#JSLINT_ dl.level1 { + color: black; + background-color: #ffffe0; /* yellow */ + margin-left: 1em; +} + +#JSLINT_ dl.level2 { + color: black; + background-color: #e0ffe0; /* green */ + margin-left: 2em; +} + +#JSLINT_ dl.level3 { + color: black; + background-color: #e0e0ff; /* blue */ + margin-left: 3em; +} + +#JSLINT_ dl.level4 { + background-color: #ffe0ff; /* purple */ + color: black; + margin-left: 4em; +} + +#JSLINT_ dl.level5 { + background-color: #ffe0e0; /* red */ + color: black; + margin-left: 5em; +} + +#JSLINT_ dl.level6 { + background-color: #ffe390; /* orange */ + color: black; + margin-left: 6em; +} + +#JSLINT_ dl.level7 { + background-color: #e0e0e0; /* gray */ + color: black; + margin-left: 7em; +} + +#JSLINT_ dl.level8 { + color: black; + margin-left: 8em; +} + +#JSLINT_ dl.level9 { + color: black; + margin-left: 9em; +} + +.none { + display: none; +} +.center { + text-align: center; +} diff --git a/branch.sort_const_let_var/jslint.js b/branch.sort_const_let_var/jslint.js new file mode 100644 index 000000000..9282ea50d --- /dev/null +++ b/branch.sort_const_let_var/jslint.js @@ -0,0 +1,4931 @@ +// jslint.js +// 2018-10-04 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// jslint(source, option_object, global_array) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze, a string or an array of strings. +// option_object An object whose keys correspond to option names. +// global_array An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all of the functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// 1. If the source is a single string, split it into an array of strings. +// 2. Turn the source into an array of tokens. +// 3. Furcate the tokens into a parse tree. +// 4. Walk the tree, traversing all of the nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// 5. Check the whitespace between the tokens. + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*property + a, and, arity, assign, b, bad_assignment_a, bad_directive_a, bad_get, + bad_module_name_a, bad_option_a, bad_property_a, bad_set, bitwise, block, + body, browser, c, calls, catch, charCodeAt, closer, closure, code, column, + complex, concat, constant, context, convert, couch, create, d, dead, + default, devel, directive, directives, disrupt, dot, duplicate_a, edition, + ellipsis, else, empty_block, escape_mega, eval, every, expected_a, + expected_a_at_b_c, expected_a_b, expected_a_b_from_c_d, expected_a_before_b, + expected_a_next_at_b, expected_digits_after_a, expected_four_digits, + expected_identifier_a, expected_line_break_a_b, expected_regexp_factor_a, + expected_space_a_b, expected_statements_a, expected_string_a, + expected_type_string_a, exports, expression, extra, finally, flag, for, + forEach, free, from, froms, fud, fudge, function_in_loop, functions, g, + getset, global, i, id, identifier, import, inc, indexOf, infix_in, init, + initial, isArray, isNaN, join, json, keys, label, label_a, lbp, led, length, + level, line, lines, live, long, loop, m, margin, match, message, + misplaced_a, misplaced_directive_a, missing_browser, missing_m, module, + multivar, naked_block, name, names, nested_comment, new, node, not_label_a, + nr, nud, number_isNaN, ok, open, opening, option, out_of_scope_a, + parameters, parent, pop, property, push, quote, redefinition_a_b, replace, + required_a_optional_b, reserved_a, right, role, search, shebang, signature, + single, slice, some, sort, split, startsWith, statement, stop, strict, + subscript_a, switch, test, this, thru, toString, todo_comment, tokens, + too_long, too_many_digits, tree, try, type, u, unclosed_comment, + unclosed_mega, unclosed_string, undeclared_a, unexpected_a, + unexpected_a_after_b, unexpected_a_before_b, unexpected_at_top_level_a, + unexpected_char_a, unexpected_comment, unexpected_directive_a, + unexpected_expression_a, unexpected_label_a, unexpected_parens, + unexpected_space_a_b, unexpected_statement_a, unexpected_trailing_space, + unexpected_typeof_a, uninitialized_a, unreachable_a, + unregistered_property_a, unsafe, unused_a, use_double, use_open, use_spaces, + use_strict, used, value, var_loop, var_switch, variable, warning, warnings, + weird_condition_a, weird_expression_a, weird_loop, weird_relation_a, white, + wrap_condition, wrap_immediate, wrap_parameter, wrap_regexp, wrap_unary, + wrapped, writable, y +*/ + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than '{}' because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +function populate(array, object = empty(), value = true) { + +// Augment an object by taking property names from an array of strings. + + array.forEach(function (name) { + object[name] = value; + }); + return object; +} + +const allowed_option = { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + bitwise: true, + browser: [ + "caches", "clearInterval", "clearTimeout", "document", "DOMException", + "Element", "Event", "event", "FileReader", "FormData", "history", + "localStorage", "location", "MutationObserver", "name", "navigator", + "screen", "sessionStorage", "setInterval", "setTimeout", "Storage", + "URL", "window", "Worker", "XMLHttpRequest" + ], + couch: [ + "emit", "getRow", "isArray", "log", "provides", "registerType", + "require", "send", "start", "sum", "toJSON" + ], + convert: true, + devel: [ + "alert", "confirm", "console", "prompt" + ], + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + multivar: true, + node: [ + "Buffer", "clearImmediate", "clearInterval", "clearTimeout", + "console", "exports", "module", "process", "require", + "setImmediate", "setInterval", "setTimeout", "URL", + "URLSearchParams", "__dirname", "__filename" + ], + single: true, + this: true, + white: true +}; + +const anticondition = populate([ + "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%", + "typeof", "(number)", "(string)" +]); + +// These are the bitwise operators. + +const bitwiseop = populate([ + "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=", + ">>>", ">>>=" +]); + +const escapeable = populate([ + "\\", "/", "`", "b", "f", "n", "r", "t" +]); + +const opener = { + +// The open and close pairs. + + "(": ")", // paren + "[": "]", // bracket + "{": "}", // brace + "${": "}" // mega +}; + +// The relational operators. + +const relationop = populate([ + "!=", "!==", "==", "===", "<", "<=", ">", ">=" +]); + +// This is the set of infix operators that require a space on each side. + +const spaceop = populate([ + "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/", + "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=", + ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" +]); + +const standard = [ + +// These are the globals that are provided by the language standard. + + "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI", + "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error", + "EvalError", "Float32Array", "Float64Array", "Generator", + "GeneratorFunction", "Int8Array", "Int16Array", "Int32Array", "Intl", + "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat", + "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", + "Set", "String", "Symbol", "SyntaxError", "System", "TypeError", + "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", + "URIError", "WeakMap", "WeakSet" +]; + +const bundle = { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + and: "The '&&' subexpression should be wrapped in parens.", + bad_assignment_a: "Bad assignment to '{a}'.", + bad_directive_a: "Bad directive '{a}'.", + bad_get: "A get function takes no parameters.", + bad_module_name_a: "Bad module name '{a}'.", + bad_option_a: "Bad option '{a}'.", + bad_property_a: "Bad property name '{a}'.", + bad_set: "A set function takes one parameter.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + escape_mega: "Unexpected escapement in mega literal.", + expected_a: "Expected '{a}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: ( + "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'." + ), + expected_a_before_b: "Expected '{a}' before '{b}'.", + expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.", + expected_digits_after_a: "Expected digits after '{a}'.", + expected_four_digits: "Expected four digits after '\\u'.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.", + expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.", + expected_space_a_b: "Expected one space between '{a}' and '{b}'.", + expected_statements_a: "Expected statements before '{a}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + expected_type_string_a: "Expected a type string and instead saw '{a}'.", + function_in_loop: "Don't make functions within a loop.", + infix_in: ( + "Unexpected 'in'. Compare with undefined, " + + "or use the hasOwnProperty method instead." + ), + label_a: "'{a}' is a statement label.", + misplaced_a: "Place '{a}' at the outermost level.", + misplaced_directive_a: ( + "Place the '/*{a}*/' directive before the first statement." + ), + missing_browser: "/*global*/ requires the Assume a browser option.", + missing_m: "Expected 'm' flag on a multiline regular expression.", + naked_block: "Naked block.", + nested_comment: "Nested comment.", + not_label_a: "'{a}' is not a label.", + number_isNaN: "Use Number.isNaN function to compare with NaN.", + out_of_scope_a: "'{a}' is out of scope.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + required_a_optional_b: ( + "Required parameter '{a}' after optional parameter '{b}'." + ), + reserved_a: "Reserved name '{a}'.", + subscript_a: "['{a}'] is better written in dot notation.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line is longer than 80 characters.", + too_many_digits: "Too many digits.", + unclosed_comment: "Unclosed comment.", + unclosed_mega: "Unclosed mega literal.", + unclosed_string: "Unclosed string.", + undeclared_a: "Undeclared '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_a_after_b: "Unexpected '{a}' after '{b}'.", + unexpected_a_before_b: "Unexpected '{a}' before '{b}'.", + unexpected_at_top_level_a: "Expected '{a}' to be in a function.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_directive_a: "When using modules, don't use directive '/*{a}'.", + unexpected_expression_a: ( + "Unexpected expression '{a}' in statement position." + ), + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_parens: "Don't wrap function literals in parens.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_statement_a: ( + "Unexpected statement '{a}' in expression position." + ), + unexpected_trailing_space: "Unexpected trailing space.", + unexpected_typeof_a: ( + "Unexpected 'typeof'. Use '===' to compare directly with {a}." + ), + uninitialized_a: "Uninitialized '{a}'.", + unreachable_a: "Unreachable '{a}'.", + unregistered_property_a: "Unregistered property name '{a}'.", + unsafe: "Unsafe character '{a}'.", + unused_a: "Unused '{a}'.", + use_double: "Use double quotes, not single quotes.", + use_open: ( + "Wrap a ternary expression in parens, " + + "with a line break after the left paren." + ), + use_spaces: "Use spaces, not tabs.", + use_strict: "This function needs a \"use strict\" pragma.", + var_loop: "Don't declare variables in a loop.", + var_switch: "Don't declare variables in a switch.", + weird_condition_a: "Weird condition '{a}'.", + weird_expression_a: "Weird expression '{a}'.", + weird_loop: "Weird loop.", + weird_relation_a: "Weird relation '{a}'.", + wrap_condition: "Wrap the condition in parens.", + wrap_immediate: ( + "Wrap an immediate function invocation in parentheses to assist " + + "the reader in understanding that the expression is the result " + + "of a function, and not the function itself." + ), + wrap_parameter: "Wrap the parameter in parens.", + wrap_regexp: "Wrap this regexp in parens to avoid confusion.", + wrap_unary: "Wrap the unary expression in parens." +}; + +// Regular expression literals: + +// supplant {variables} +const rx_supplant = /\{([^{}]*)\}/g; +// carriage return, carriage return linefeed, or linefeed +const rx_crlf = /\n|\r\n?/; +// unsafe characters that are silently deleted by one or more browsers +const rx_unsafe = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; +// identifier +const rx_identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; +const rx_module = /^[a-zA-Z0-9_$:.@\-\/]+$/; +const rx_bad_property = /^_|\$|Sync\$|_$/; +// star slash +const rx_star_slash = /\*\//; +// slash star +const rx_slash_star = /\/\*/; +// slash star or ending slash +const rx_slash_star_or_slash = /\/\*|\/$/; +// uncompleted work comment +const rx_todo = /\b(?:todo|TO\s?DO|HACK)\b/; +// tab +const rx_tab = /\t/g; +// directive +const rx_directive = /^(jslint|property|global)\s+(.*)$/; +const rx_directive_part = /^([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*(.*)$/; +// token (sorry it is so long) +const rx_token = /^((\s+)|([a-zA-Z_$][a-zA-Z0-9_$]*)|[(){}\[\]?,:;'"~`]|=(?:==?|>)?|\.+|[*\/][*\/=]?|\+[=+]?|-[=\-]?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<= "a" && string <= "z\uffff") + || (string >= "A" && string <= "Z\uffff") + ); +} + +function supplant(string, object) { + return string.replace(rx_supplant, function (found, filling) { + const replacement = object[filling]; + return ( + replacement !== undefined + ? replacement + : found + ); + }); +} + +let anon; // The guessed name for anonymous functions. +let block_stack; // The stack of blocks. +let blockage; // The current block. +let declared_globals; // The object containing the global declarations. +let directive_mode; // true if directives are still allowed. +let directives; // The directive comments. +let early_stop; // true if JSLint cannot finish. +let exports; // The exported names and values. +let froms; // The array collecting all import-from strings. +let fudge; // true if the natural numbers start with 1. +let functionage; // The current function. +let functions; // The array containing all of the functions. +let global; // The global object; the outermost context. +let json_mode; // true if parsing JSON. +let lines; // The array containing source lines. +let mega_mode; // true if currently parsing a megastring literal. +let module_mode; // true if import or export was used. +let next_token; // The next token to be examined in the parse. +let option; // The options parameter. +let property; // The object containing the tallied property names. +let shebang; // true if a #! was seen on the first line. +let stack; // The stack of functions. +let syntax; // The object containing the parser. +let tenure; // The predefined property registry. +let token; // The current token being examined in the parse. +let token_nr; // The number of the next token. +let tokens; // The array of tokens. +let tree; // The abstract parse tree. +let var_mode; // "var" if using var; "let" if using let. +let warnings; // The array collecting all generated warnings. + +// Error reportage functions: + +function artifact(the_token) { + +// Return a string representing an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); +} + +function artifact_line(the_token) { + +// Return the fudged line number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.line + fudge; +} + +function artifact_column(the_token) { + +// Return the fudged column number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.from + fudge; +} + +function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + const warning = { // ~~ + name: "JSLintError", + column: column, + line: line, + code: code + }; + if (a !== undefined) { + warning.a = a; + } + if (b !== undefined) { + warning.b = b; + } + if (c !== undefined) { + warning.c = c; + } + if (d !== undefined) { + warning.d = d; + } + warning.message = supplant(bundle[code] || code, warning); + warnings.push(warning); + return warning; +} + +function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); +} + +function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + if (the_token.warning === undefined) { + the_token.warning = warn_at( + code, + the_token.line, + the_token.from, + a || artifact(the_token), + b, + c, + d + ); + return the_token.warning; + } +} + +function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); +} + +// Tokenize: + +function tokenize(source) { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + +// If the source is not an array, then it is split into lines at the +// carriage return/linefeed. + + lines = ( + Array.isArray(source) + ? source + : source.split(rx_crlf) + ); + tokens = []; + + let char; // a popular character + let column = 0; // the column number of the next character + let first; // the first token + let from; // the starting column number of the token + let line = -1; // the line number of the next character + let nr = 0; // the next token number + let previous = global; // the previous token including comments + let prior = global; // the previous token excluding comments + let mega_from; // the starting column of megastring + let mega_line; // the starting line of megastring + let regexp_seen; // regular expression literal seen on this line + let snippet; // a piece of string + let source_line = ""; // the remaining line source string + let whole_line = ""; // the whole line source string + + if (lines[0].startsWith("#!")) { + line = 0; + shebang = true; + } + + function next_line() { + +// Put the next line of source in source_line. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + let at; + if ( + !option.long + && whole_line.length > 80 + && !json_mode + && first + && !regexp_seen + ) { + warn_at("too_long", line, 80); + } + column = 0; + line += 1; + regexp_seen = false; + source_line = lines[line]; + whole_line = source_line || ""; + if (source_line !== undefined) { + at = source_line.search(rx_tab); + if (at >= 0) { + if (!option.white) { + warn_at("use_spaces", line, at + 1); + } + source_line = source_line.replace(rx_tab, " "); + } + at = source_line.search(rx_unsafe); + if (at >= 0) { + warn_at( + "unsafe", + line, + column + at, + "U+" + source_line.charCodeAt(at).toString(16) + ); + } + if (!option.white && source_line.slice(-1) === " ") { + warn_at( + "unexpected_trailing_space", + line, + source_line.length - 1 + ); + } + } + return source_line; + } + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions snip, next_char, prev_char, +// some_digits, and escape help in the parsing of literals. + + function snip() { + +// Remove the last character from snippet. + + snippet = snippet.slice(0, -1); + } + + function next_char(match) { + +// Get the next character from the source line. Remove it from the source_line, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + return stop_at( + ( + char === "" + ? "expected_a" + : "expected_a_b" + ), + line, + column - 1, + match, + char + ); + } + if (source_line) { + char = source_line[0]; + source_line = source_line.slice(1); + snippet += char; + } else { + char = ""; + snippet += " "; + } + column += 1; + return char; + } + + function back_char() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the source_line. + + if (snippet) { + char = snippet.slice(-1); + source_line = char + source_line; + column -= 1; + snip(); + } else { + char = ""; + } + return char; + } + + function some_digits(rx, quiet) { + const result = source_line.match(rx); + if (result) { + char = result[1]; + column += char.length; + source_line = result[2]; + snippet += char; + } else { + char = ""; + if (!quiet) { + warn_at( + "expected_digits_after_a", + line, + column, + snippet + ); + } + } + return char.length; + } + + function escape(extra) { + next_char("\\"); + if (escapeable[char] === true) { + return next_char(); + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "u") { + if (next_char("u") === "{") { + if (json_mode) { + warn_at("unexpected_a", line, column - 1, char); + } + if (some_digits(rx_hexs) > 5) { + warn_at("too_many_digits", line, column - 1); + } + if (next_char() !== "}") { + stop_at("expected_a_before_b", line, column, "}", char); + } + return next_char(); + } + back_char(); + if (some_digits(rx_hexs, true) < 4) { + warn_at("expected_four_digits", line, column - 1); + } + return; + } + if (extra && extra.indexOf(char) >= 0) { + return next_char(); + } + warn_at("unexpected_a_before_b", line, column - 2, "\\", char); + } + + function make(id, value, identifier) { + +// Make the token object and append it to the tokens list. + + const the_token = { + from: from, + id: id, + identifier: Boolean(identifier), + line: line, + nr: nr, + thru: column + }; + tokens[nr] = the_token; + nr += 1; + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + directive_mode = false; + } + +// If the token is to have a value, give it one. + + if (value !== undefined) { + the_token.value = value; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option.white. + + if ( + previous.line === line + && previous.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (previous.id === "(comment)" || previous.id === "(regexp)") + ) { + warn( + "expected_space_a_b", + the_token, + artifact(previous), + artifact(the_token) + ); + } + if (previous.id === "." && id === "(number)") { + warn("expected_a_before_b", previous, "0", "."); + } + if (prior.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// The previous token is used to detect adjacency problems. + + previous = the_token; + +// The prior token is a previous token that was not a comment. The prior token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (previous.id !== "(comment)") { + prior = previous; + } + return the_token; + } + + function parse_directive(the_comment, body) { + +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + + const result = body.match(rx_directive_part); + if (result) { + let allowed; + const name = result[1]; + const value = result[2]; + if (the_comment.directive === "jslint") { + allowed = allowed_option[name]; + if ( + typeof allowed === "boolean" + || typeof allowed === "object" + ) { + if ( + value === "" + || value === "true" + || value === undefined + ) { + option[name] = true; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } else if (value === "false") { + option[name] = false; + } else { + warn("bad_option_a", the_comment, name + ":" + value); + } + } else { + warn("bad_option_a", the_comment, name); + } + } else if (the_comment.directive === "property") { + if (tenure === undefined) { + tenure = empty(); + } + tenure[name] = true; + } else if (the_comment.directive === "global") { + if (value) { + warn("bad_option_a", the_comment, name + ":" + value); + } + declared_globals[name] = false; + module_mode = the_comment; + } + return parse_directive(the_comment, result[3]); + } + if (body) { + return stop("bad_directive_a", the_comment, body); + } + } + + function comment(snippet) { + +// Make a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + + const the_comment = make("(comment)", snippet); + if (Array.isArray(snippet)) { + snippet = snippet.join(" "); + } + if (!option.devel && rx_todo.test(snippet)) { + warn("todo_comment", the_comment); + } + const result = snippet.match(rx_directive); + if (result) { + if (!directive_mode) { + warn_at("misplaced_directive_a", line, from, result[1]); + } else { + the_comment.directive = result[1]; + parse_directive(the_comment, result[2]); + } + directives.push(the_comment); + } + return the_comment; + } + + function regexp() { + +// Parse a regular expression literal. + + let multi_mode = false; + let result; + let value; + regexp_seen = true; + + function quantifier() { + +// Match an optional quantifier. + + if (char === "?" || char === "*" || char === "+") { + next_char(); + } else if (char === "{") { + if (some_digits(rx_digits, true) === 0) { + warn_at("expected_a", line, column, "0"); + } + if (next_char() === ",") { + some_digits(rx_digits, true); + next_char(); + } + next_char("}"); + } else { + return; + } + if (char === "?") { + next_char("?"); + } + } + + function subklass() { + +// Match a character in a character class. + + if (char === "\\") { + escape("BbDdSsWw-[]^"); + return true; + } + if ( + char === "" + || char === "[" + || char === "]" + || char === "/" + || char === "^" + || char === "-" + ) { + return false; + } + if (char === " ") { + warn_at("expected_a_b", line, column, "\\u0020", " "); + } else if (char === "`" && mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char(); + return true; + } + + function ranges() { + +// Match a range of subclasses. + + if (subklass()) { + if (char === "-") { + next_char("-"); + if (!subklass()) { + return stop_at( + "unexpected_a", + line, + column - 1, + "-" + ); + } + } + return ranges(); + } + } + + function klass() { + +// Match a class. + + next_char("["); + if (char === "^") { + next_char("^"); + } + (function classy() { + ranges(); + if (char !== "]" && char !== "") { + warn_at( + "expected_a_before_b", + line, + column, + "\\", + char + ); + next_char(); + return classy(); + } + }()); + next_char("]"); + } + + function choice() { + + function group() { + +// Match a group that starts with left paren. + + next_char("("); + if (char === "?") { + next_char("?"); + if (char === "=" || char === "!") { + next_char(); + } else { + next_char(":"); + } + } else if (char === ":") { + warn_at("expected_a_before_b", line, column, "?", ":"); + } + choice(); + next_char(")"); + } + + function factor() { + if ( + char === "" + || char === "/" + || char === "]" + || char === ")" + ) { + return false; + } + if (char === "(") { + group(); + return true; + } + if (char === "[") { + klass(); + return true; + } + if (char === "\\") { + escape("BbDdSsWw^${}[]():=!.-|*+?"); + return true; + } + if ( + char === "?" + || char === "+" + || char === "*" + || char === "}" + || char === "{" + ) { + warn_at( + "expected_a_before_b", + line, + column - 1, + "\\", + char + ); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column - 1, "`"); + } + } else if (char === " ") { + warn_at( + "expected_a_b", + line, + column - 1, + "\\s", + " " + ); + } else if (char === "$") { + if (source_line[0] !== "/") { + multi_mode = true; + } + } else if (char === "^") { + if (snippet !== "^") { + multi_mode = true; + } + } + next_char(); + return true; + } + + function sequence(follow) { + if (factor()) { + quantifier(); + return sequence(true); + } + if (!follow) { + warn_at("expected_regexp_factor_a", line, column, char); + } + } + +// Match a choice (a sequence that can be followed by | and another choice). + + sequence(); + if (char === "|") { + next_char("|"); + return choice(); + } + } + +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + next_char(); + if (char === "=") { + warn_at("expected_a_before_b", line, column, "\\", "="); + } + choice(); + +// Make sure there is a closing slash. + + snip(); + value = snippet; + next_char("/"); + +// Process dangling flag letters. + + const allowed = { + g: true, + i: true, + m: true, + u: true, + y: true + }; + const flag = empty(); + (function make_flag() { + if (is_letter(char)) { + if (allowed[char] !== true) { + warn_at("unexpected_a", line, column, char); + } + allowed[char] = false; + flag[char] = true; + next_char(); + return make_flag(); + } + }()); + back_char(); + if (char === "/" || char === "*") { + return stop_at("unexpected_a", line, from, char); + } + result = make("(regexp)", char); + result.flag = flag; + result.value = value; + if (multi_mode && !flag.m) { + warn_at("missing_m", line, column); + } + return result; + } + + function string(quote) { + +// Make a string token. + + let the_token; + snippet = ""; + next_char(); + + return (function next() { + if (char === quote) { + snip(); + the_token = make("(string)", snippet); + the_token.quote = quote; + return the_token; + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "\\") { + escape(quote); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char("`"); + } else { + next_char(); + } + return next(); + }()); + } + + function frack() { + if (char === ".") { + some_digits(rx_digits); + next_char(); + } + if (char === "E" || char === "e") { + next_char(); + if (char !== "+" && char !== "-") { + back_char(); + } + some_digits(rx_digits); + next_char(); + } + } + + function number() { + if (snippet === "0") { + next_char(); + if (char === ".") { + frack(); + } else if (char === "b") { + some_digits(rx_bits); + next_char(); + } else if (char === "o") { + some_digits(rx_octals); + next_char(); + } else if (char === "x") { + some_digits(rx_hexs); + next_char(); + } + } else { + next_char(); + frack(); + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + return stop_at( + "unexpected_a_after_b", + line, + column - 1, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + back_char(); + return make("(number)", snippet); + } + + function lex() { + let array; + let i = 0; + let j = 0; + let last; + let result; + let the_token; + if (!source_line) { + source_line = next_line(); + from = 0; + return ( + source_line === undefined + ? ( + mega_mode + ? stop_at("unclosed_mega", mega_line, mega_from) + : make("(end)") + ) + : lex() + ); + } + from = column; + result = source_line.match(rx_token); + +// result[1] token +// result[2] whitespace +// result[3] identifier +// result[4] number +// result[5] rest + + if (!result) { + return stop_at( + "unexpected_char_a", + line, + column, + source_line[0] + ); + } + + snippet = result[1]; + column += snippet.length; + source_line = result[5]; + +// Whitespace was matched. Call lex again to get more. + + if (result[2]) { + return lex(); + } + +// The token is an identifier. + + if (result[3]) { + return make(snippet, undefined, true); + } + +// The token is a number. + + if (result[4]) { + return number(snippet); + } + +// The token is a string. + + if (snippet === "\"") { + return string(snippet); + } + if (snippet === "'") { + if (!option.single) { + warn_at("use_double", line, column); + } + return string(snippet); + } + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (snippet === "`") { + if (mega_mode) { + return stop_at("expected_a_b", line, column, "}", "`"); + } + snippet = ""; + mega_from = from; + mega_line = line; + mega_mode = true; + +// Parsing a mega literal is tricky. First make a ` token. + + make("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + (function part() { + const at = source_line.search(rx_mega); + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + if (at < 0) { + snippet += source_line + "\n"; + return ( + next_line() === undefined + ? stop_at("unclosed_mega", mega_line, mega_from) + : part() + ); + } + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + snippet += source_line.slice(0, at); + column += at; + source_line = source_line.slice(at); + if (source_line[0] === "\\") { + stop_at("escape_mega", line, at); + } + make("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then make tokens that will become part of an expression until +// a } token is made. + + if (source_line[0] === "$") { + column += 2; + make("${"); + source_line = source_line.slice(2); + (function expr() { + const id = lex().id; + if (id === "{") { + return stop_at( + "expected_a_b", + line, + column, + "}", + "{" + ); + } + if (id !== "}") { + return expr(); + } + }()); + return part(); + } + }()); + source_line = source_line.slice(1); + column += 1; + mega_mode = false; + return make("`"); + } + +// The token is a // comment. + + if (snippet === "//") { + snippet = source_line; + source_line = ""; + the_token = comment(snippet); + if (mega_mode) { + warn("unexpected_comment", the_token, "`"); + } + return the_token; + } + +// The token is a /* comment. + + if (snippet === "/*") { + array = []; + if (source_line[0] === "/") { + warn_at("unexpected_a", line, column + i, "/"); + } + (function next() { + if (source_line > "") { + i = source_line.search(rx_star_slash); + if (i >= 0) { + return; + } + j = source_line.search(rx_slash_star); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + } + array.push(source_line); + source_line = next_line(); + if (source_line === undefined) { + return stop_at("unclosed_comment", line, column); + } + return next(); + }()); + snippet = source_line.slice(0, i); + j = snippet.search(rx_slash_star_or_slash); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + array.push(snippet); + column += i + 2; + source_line = source_line.slice(i + 2); + return comment(array); + } + +// The token is a slash. + + if (snippet === "/") { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + if (prior.identifier) { + if (!prior.dot) { + if (prior.id === "return") { + return regexp(); + } + if ( + prior.id === "(begin)" + || prior.id === "case" + || prior.id === "delete" + || prior.id === "in" + || prior.id === "instanceof" + || prior.id === "new" + || prior.id === "typeof" + || prior.id === "void" + || prior.id === "yield" + ) { + the_token = regexp(); + return stop("unexpected_a", the_token); + } + } + } else { + last = prior.id[prior.id.length - 1]; + if ("(,=:?[".indexOf(last) >= 0) { + return regexp(); + } + if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) { + the_token = regexp(); + warn("wrap_regexp", the_token); + return the_token; + } + } + if (source_line[0] === "/") { + column += 1; + source_line = source_line.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + } + return make(snippet); + } + + first = lex(); + json_mode = first.id === "{" || first.id === "["; + +// This is the only loop in JSLint. It will turn into a recursive call to lex +// when ES6 has been finished and widely deployed and adopted. + + while (true) { + if (lex().id === "(end)") { + break; + } + } +} + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + +function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!rx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!rx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property[id] === "number") { + property[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (tenure !== undefined) { + if (tenure[id] !== true) { + warn("unregistered_property_a", name); + } + } else { + if (name.identifier && rx_bad_property.test(id)) { + warn("bad_property_a", name); + } + } + property[id] = 1; + } + return id; +} + +function dispense() { + +// Deliver the next token, skipping the comments. + + const cadet = tokens[token_nr]; + token_nr += 1; + if (cadet.id === "(comment)") { + if (json_mode) { + warn("unexpected_a", cadet); + } + return dispense(); + } else { + return cadet; + } +} + +function lookahead() { + +// Look ahead one token without advancing. + + const old_token_nr = token_nr; + const cadet = dispense(true); + token_nr = old_token_nr; + return cadet; +} + +function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if (token.identifier && token.id !== "function") { + anon = token.id; + } else if (token.id === "(string)" && rx_identifier.test(token.value)) { + anon = token.value; + } + +// Attempt to match next_token with an expected id. + + if (id !== undefined && next_token.id !== id) { + return ( + match === undefined + ? stop("expected_a_b", next_token, id, artifact()) + : stop( + "expected_a_b_from_c_d", + next_token, + id, + artifact(match), + artifact_line(match), + artifact(next_token) + ) + ); + } + +// Promote the tokens, skipping comments. + + token = next_token; + next_token = dispense(); + if (next_token.id === "(end)") { + token_nr -= 1; + } +} + +// Parsing of JSON is simple: + +function json_value() { + let negative; + if (next_token.id === "{") { + return (function json_object() { + const brace = next_token; + const object = empty(); + const properties = []; + brace.expression = properties; + advance("{"); + if (next_token.id !== "}") { + (function next() { + let name; + let value; + if (next_token.quote !== "\"") { + warn( + "unexpected_a", + next_token, + next_token.quote + ); + } + name = next_token; + advance("(string)"); + if (object[token.value] !== undefined) { + warn("duplicate_a", token); + } else if (token.value === "__proto__") { + warn("bad_property_a", token); + } else { + object[token.value] = token; + } + advance(":"); + value = json_value(); + value.label = name; + properties.push(value); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("}", brace); + return brace; + }()); + } + if (next_token.id === "[") { + return (function json_array() { + const bracket = next_token; + const elements = []; + bracket.expression = elements; + advance("["); + if (next_token.id !== "]") { + (function next() { + elements.push(json_value()); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]", bracket); + return bracket; + }()); + } + if ( + next_token.id === "true" + || next_token.id === "false" + || next_token.id === "null" + ) { + advance(); + return token; + } + if (next_token.id === "(number)") { + if (!rx_JSON_number.test(next_token.value)) { + warn("unexpected_a"); + } + advance(); + return token; + } + if (next_token.id === "(string)") { + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + advance(); + return token; + } + if (next_token.id === "-") { + negative = next_token; + negative.arity = "unary"; + advance("-"); + advance("(number)"); + negative.expression = token; + return negative; + } + stop("unexpected_a"); +} + +// Now we parse JavaScript. + +function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + const id = name.id; + +// Reserved words may not be enrolled. + + if (syntax[id] !== undefined && id !== "ignore") { + warn("reserved_a", name); + } else { + +// Has the name been enrolled in this context? + + let earlier = functionage.context[id]; + if (earlier) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + +// Has the name been enrolled in an outer context? + + } else { + stack.forEach(function (value) { + const item = value.context[id]; + if (item !== undefined) { + earlier = item; + } + }); + if (earlier) { + if (id === "ignore") { + if (earlier.role === "variable") { + warn("unexpected_a", name); + } + } else { + if ( + ( + role !== "exception" + || earlier.role !== "exception" + ) + && role !== "parameter" + && role !== "function" + ) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + } + } + } + +// Enroll it. + + functionage.context[id] = name; + name.dead = true; + name.parent = functionage; + name.init = false; + name.role = role; + name.used = 0; + name.writable = !readonly; + } + } +} + +function expression(rbp, initial) { + +// This is the heart of the Pratt parser. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// nud Null denotation +// led Left denotation +// lbp Left binding power +// rbp Right binding power + +// It processes a nud (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop. It +// returns the expression's parse tree. + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement, + + if (!initial) { + advance(); + } + the_symbol = syntax[token.id]; + if (the_symbol !== undefined && the_symbol.nud !== undefined) { + left = the_symbol.nud(); + } else if (token.identifier) { + left = token; + left.arity = "variable"; + } else { + return stop("unexpected_a", token); + } + (function right() { + the_symbol = syntax[next_token.id]; + if ( + the_symbol !== undefined + && the_symbol.led !== undefined + && rbp < the_symbol.lbp + ) { + advance(); + left = the_symbol.led(left); + return right(); + } + }()); + return left; +} + +function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = next_token; + let the_value; + the_paren.free = true; + advance("("); + the_value = expression(0); + advance(")"); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (anticondition[the_value.id] === true) { + warn("unexpected_a", the_value); + } + return the_value; +} + +function is_weird(thing) { + return ( + thing.id === "(regexp)" + || thing.id === "{" + || thing.id === "=>" + || thing.id === "function" + || (thing.id === "[" && thing.arity === "unary") + ); +} + +function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + return ( + Array.isArray(b) + && a.length === b.length + && a.every(function (value, index) { + return are_similar(value, b[index]); + }) + ); + } + if (Array.isArray(b)) { + return false; + } + if (a.id === "(number)" && b.id === "(number)") { + return a.value === b.value; + } + let a_string; + let b_string; + if (a.id === "(string)") { + a_string = a.value; + } else if (a.id === "`" && a.constant) { + a_string = a.value[0]; + } + if (b.id === "(string)") { + b_string = b.value; + } else if (b.id === "`" && b.constant) { + b_string = b.value[0]; + } + if (typeof a_string === "string") { + return a_string === b_string; + } + if (is_weird(a) || is_weird(b)) { + return false; + } + if (a.arity === b.arity && a.id === b.id) { + if (a.id === ".") { + return ( + are_similar(a.expression, b.expression) + && are_similar(a.name, b.name) + ); + } + if (a.arity === "unary") { + return are_similar(a.expression, b.expression); + } + if (a.arity === "binary") { + return ( + a.id !== "(" + && are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + ); + } + if (a.arity === "ternary") { + return ( + are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + && are_similar(a.expression[2], b.expression[2]) + ); + } + if (a.arity === "function" && a.arity === "regexp") { + return false; + } + return true; + } + return false; +} + +function semicolon() { + +// Try to match a semicolon. + + if (next_token.id === ";") { + advance(";"); + } else { + warn_at( + "expected_a_b", + token.line, + token.thru, + ";", + artifact(next_token) + ); + } + anon = "anonymous"; +} + +function statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token.identifier && next_token.id === ":") { + the_label = token; + if (the_label.id === "ignore") { + warn("unexpected_a", the_label); + } + advance(":"); + if ( + next_token.id === "do" + || next_token.id === "for" + || next_token.id === "switch" + || next_token.id === "while" + ) { + enroll(the_label, "label", true); + the_label.init = true; + the_label.dead = false; + the_statement = statement(); + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token; + first.statement = true; + the_symbol = syntax[first.id]; + if (the_symbol !== undefined && the_symbol.fud !== undefined) { + the_symbol.disrupt = false; + the_symbol.statement = true; + the_statement = the_symbol.fud(); + } else { + +// It is an expression statement. + + the_statement = expression(0, true); + if (the_statement.wrapped && the_statement.id !== "(") { + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; +} + +function statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const array = []; + (function next(disrupt) { + if ( + next_token.id !== "}" + && next_token.id !== "case" + && next_token.id !== "default" + && next_token.id !== "else" + && next_token.id !== "(end)" + ) { + let a_statement = statement(); + array.push(a_statement); + if (disrupt) { + warn("unreachable_a", a_statement); + } + return next(a_statement.disrupt); + } + }(false)); + return array; +} + +function not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === global) { + warn("unexpected_at_top_level_a", thing); + } +} + +function top_level_only(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== global) { + warn("misplaced_a", the_thing); + } +} + +function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token; + the_block.arity = "statement"; + the_block.body = special === "body"; + +// All top level function bodies should include the "use strict" pragma unless +// the whole file is strict or the file is a module or the function parameters +// use es6 syntax. + + if ( + special === "body" + && stack.length === 1 + && next_token.value === "use strict" + ) { + the_block.strict = next_token; + next_token.statement = true; + advance("(string)"); + advance(";"); + } + stmts = statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option.devel && special !== "ignore") { + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; +} + +function mutation_check(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + warn("bad_assignment_a", the_thing); + return false; + } + return true; +} + +function left_check(left, right) { + +// Warn if the left is not one of these: +// e.b +// e[b] +// e() +// ?: +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !left_check(left.expression[1]) + && !left_check(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right); + return false; + } + return true; +} + +// These functions are used to specify the grammar of our language: + +function symbol(id, bp) { + +// Make a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax[id] = the_symbol; + } + return the_symbol; +} + +function assignment(id) { + +// Make an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led = function (left) { + const the_token = token; + let right; + the_token.arity = "assignment"; + right = expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "pre" + || right.arity === "post" + ) { + warn("unexpected_a", right); + } + mutation_check(left); + return the_token; + }; + return the_symbol; +} + +function constant(id, type, value) { + +// Make a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud = ( + typeof value === "function" + ? value + : function () { + token.constant = true; + if (value !== undefined) { + token.value = value; + } + return token; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; +} + +function infix(id, bp, f) { + +// Make an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, expression(bp)]; + return the_token; + }; + return the_symbol; +} + +function infixr(id, bp) { + +// Make a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + the_token.expression = [left, expression(bp - 1)]; + return the_token; + }; + return the_symbol; +} + +function post(id) { + +// Make one of the post operators. + + const the_symbol = symbol(id, 150); + the_symbol.led = function (left) { + token.expression = left; + token.arity = "post"; + mutation_check(token.expression); + return token; + }; + return the_symbol; +} + +function pre(id) { + +// Make one of the pre operators. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "pre"; + the_token.expression = expression(150); + mutation_check(the_token.expression); + return the_token; + }; + return the_symbol; +} + +function prefix(id, f) { + +// Make a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = expression(150); + return the_token; + }; + return the_symbol; +} + +function stmt(id, f) { + +// Make a statement. + + const the_symbol = symbol(id); + the_symbol.fud = function () { + token.arity = "statement"; + return f(); + }; + return the_symbol; +} + +function ternary(id1, id2) { + +// Make a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led = function (left) { + const the_token = token; + const second = expression(20); + advance(id2); + token.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, expression(10)]; + return the_token; + }; + return the_symbol; +} + +// Begin defining the language. + +syntax = empty(); + +symbol("}"); +symbol(")"); +symbol("]"); +symbol(","); +symbol(";"); +symbol(":"); +symbol("*/"); +symbol("await"); +symbol("case"); +symbol("catch"); +symbol("class"); +symbol("default"); +symbol("else"); +symbol("enum"); +symbol("finally"); +symbol("implements"); +symbol("interface"); +symbol("package"); +symbol("private"); +symbol("protected"); +symbol("public"); +symbol("static"); +symbol("super"); +symbol("void"); +symbol("yield"); + +constant("(number)", "number"); +constant("(regexp)", "regexp"); +constant("(string)", "string"); +constant("arguments", "object", function () { + warn("unexpected_a", token); + return token; +}); +constant("eval", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("false", "boolean", false); +constant("Function", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("ignore", "undefined", function () { + warn("unexpected_a", token); + return token; +}); +constant("Infinity", "number", Infinity); +constant("isFinite", "function", function () { + warn("expected_a_b", token, "Number.isFinite", "isFinite"); + return token; +}); +constant("isNaN", "function", function () { + warn("number_isNaN", token); + return token; +}); +constant("NaN", "number", NaN); +constant("null", "null", null); +constant("this", "object", function () { + if (!option.this) { + warn("unexpected_a", token); + } + return token; +}); +constant("true", "boolean", true); +constant("undefined", "undefined"); + +assignment("="); +assignment("+="); +assignment("-="); +assignment("*="); +assignment("/="); +assignment("%="); +assignment("&="); +assignment("|="); +assignment("^="); +assignment("<<="); +assignment(">>="); +assignment(">>>="); + +infix("||", 40); +infix("&&", 50); +infix("|", 70); +infix("^", 80); +infix("&", 90); +infix("==", 100); +infix("===", 100); +infix("!=", 100); +infix("!==", 100); +infix("<", 110); +infix(">", 110); +infix("<=", 110); +infix(">=", 110); +infix("in", 110); +infix("instanceof", 110); +infix("<<", 120); +infix(">>", 120); +infix(">>>", 120); +infix("+", 130); +infix("-", 130); +infix("*", 140); +infix("/", 140); +infix("%", 140); +infixr("**", 150); +infix("(", 160, function (left) { + const the_paren = token; + let the_argument; + if (left.id !== "function") { + left_check(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (next_token.id !== ")") { + (function next() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + the_paren.free = true; + if (the_argument.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + the_paren.free = false; + } + return the_paren; +}); +infix(".", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("[", 170, function (left) { + const the_token = token; + const the_subscript = expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + const name = survey(the_subscript); + if (rx_identifier.test(name)) { + warn("subscript_a", the_subscript, name); + } + } + left_check(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; +}); +infix("=>", 170, function (left) { + return stop("wrap_parameter", left); +}); + +function do_tick() { + const the_tick = token; + the_tick.value = []; + the_tick.expression = []; + if (next_token.id !== "`") { + (function part() { + advance("(string)"); + the_tick.value.push(token); + if (next_token.id === "${") { + advance("${"); + the_tick.expression.push(expression(0)); + advance("}"); + return part(); + } + }()); + } + advance("`"); + return the_tick; +} + +infix("`", 160, function (left) { + const the_tick = do_tick(); + left_check(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; +}); + +post("++"); +post("--"); +pre("++"); +pre("--"); + +prefix("+"); +prefix("-"); +prefix("~"); +prefix("!"); +prefix("!!"); +prefix("[", function () { + const the_token = token; + the_token.expression = []; + if (next_token.id !== "]") { + (function next() { + let element; + let ellipsis = false; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + element = expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]"); + return the_token; +}); +prefix("/=", function () { + stop("expected_a_b", token, "/\\=", "/="); +}); +prefix("=>", function () { + return stop("expected_a_before_b", token, "()", "=>"); +}); +prefix("new", function () { + const the_new = token; + const right = expression(160); + if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "()", artifact(next_token)); + } + the_new.expression = right; + return the_new; +}); +prefix("typeof"); +prefix("void", function () { + const the_void = token; + warn("unexpected_a", the_void); + the_void.expression = expression(0); + return the_void; +}); + +function parameter_list() { + let complex = false; + const list = []; + let optional; + const signature = ["("]; + if (next_token.id !== ")" && next_token.id !== "(end)") { + (function parameter() { + let ellipsis = false; + let param; + if (next_token.id === "{") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("{"); + signature.push("{"); + (function subparameter() { + let subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (next_token.id === ":") { + advance(":"); + advance(); + token.label = subparam; + subparam = token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + } + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return subparameter(); + } + }()); + list.push(param); + advance("}"); + signature.push("}"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else if (next_token.id === "[") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("["); + signature.push("[]"); + (function subparameter() { + const subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + return subparameter(); + } + }()); + list.push(param); + advance("]"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else { + if (next_token.id === "...") { + complex = true; + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + } + if (!next_token.identifier) { + return stop("expected_identifier_a"); + } + param = next_token; + list.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (next_token.id === "=") { + complex = true; + optional = param; + advance("="); + param.expression = expression(0); + } else { + if (optional !== undefined) { + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } + } + }()); + } + advance(")"); + signature.push(")"); + return [list, signature.join(""), complex]; +} + +function do_function(the_function) { + let name; + if (the_function === undefined) { + the_function = token; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + name = next_token; + enroll(name, "variable", true); + the_function.name = name; + name.init = true; + name.calls = empty(); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + if (next_token.identifier) { + name = next_token; + the_function.name = name; + advance(); + } else { + the_function.name = anon; + } + } + } else { + name = the_function.name; + } + the_function.level = functionage.level + 1; + if (mega_mode) { + warn("unexpected_a", the_function); + } + +// Don't make functions in loops. It is inefficient, and it can lead to scoping +// errors. + + if (functionage.loop > 0) { + warn("function_in_loop", the_function); + } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + the_function.context = empty(); + the_function.finally = 0; + the_function.loop = 0; + the_function.switch = 0; + the_function.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functions.push(the_function); + functionage = the_function; + if (the_function.arity !== "statement" && typeof name === "object") { + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// Parse the parameter list. + + advance("("); + token.free = false; + token.arity = "function"; + const pl = parameter_list(); + functionage.parameters = pl[0]; + functionage.signature = pl[1]; + functionage.complex = pl[2]; + functionage.parameters.forEach(function enroll_parameter(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + name.names.forEach(enroll_parameter); + } + }); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && next_token.line === token.line + ) { + return stop("unexpected_a", next_token); + } + if (next_token.id === "." || next_token.id === "[") { + warn("unexpected_a"); + } + +// Restore the previous context. + + functionage = stack.pop(); + return the_function; +} + +prefix("function", do_function); + +function fart(pl) { + advance("=>"); + const the_fart = token; + the_fart.arity = "binary"; + the_fart.name = "=>"; + the_fart.level = functionage.level + 1; + functions.push(the_fart); + if (functionage.loop > 0) { + warn("function_in_loop", the_fart); + } + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + the_fart.context = empty(); + the_fart.finally = 0; + the_fart.loop = 0; + the_fart.switch = 0; + the_fart.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functionage = the_fart; + the_fart.parameters = pl[0]; + the_fart.signature = pl[1]; + the_fart.complex = true; + the_fart.parameters.forEach(function (name) { + enroll(name, "parameter", true); + }); + if (next_token.id === "{") { + warn("expected_a_b", the_fart, "function", "=>"); + the_fart.block = block("body"); + } else { + the_fart.expression = expression(0); + } + functionage = stack.pop(); + return the_fart; +} + +prefix("(", function () { + const the_paren = token; + let the_value; + const cadet = lookahead().id; + +// We can distinguish between a parameter list for => and a wrapped expression +// with one token of lookahead. + + if ( + next_token.id === ")" + || next_token.id === "..." + || (next_token.identifier && (cadet === "," || cadet === "=")) + ) { + the_paren.free = false; + return fart(parameter_list()); + } + the_paren.free = true; + the_value = expression(0); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + if (next_token.id === "=>") { + if (the_value.arity !== "variable") { + if (the_value.id === "{" || the_value.id === "[") { + warn("expected_a_before_b", the_paren, "function", "("); + return stop("expected_a_b", next_token, "{", "=>"); + } + return stop("expected_identifier_a", the_value); + } + the_paren.expression = [the_value]; + return fart([the_paren.expression, "(" + the_value.id + ")"]); + } + return the_value; +}); +prefix("`", do_tick); +prefix("{", function () { + const the_brace = token; + const seen = empty(); + the_brace.expression = []; + if (next_token.id !== "}") { + (function member() { + let extra; + let full; + let id; + let name = next_token; + let value; + advance(); + if ( + (name.id === "get" || name.id === "set") + && next_token.identifier + ) { + if (!option.getset) { + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + next_token.id; + name = next_token; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (next_token.id === "}" || next_token.id === ",") { + if (typeof extra === "string") { + advance("("); + } + value = expression(Infinity, true); + } else if (next_token.id === "(") { + value = do_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + advance("("); + } + advance(":"); + value = expression(0); + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + advance(":"); + value = expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (next_token.id === ",") { + advance(","); + return member(); + } + }()); + } + advance("}"); + return the_brace; +}); + +stmt(";", function () { + warn("unexpected_a", token); + return token; +}); +stmt("{", function () { + warn("naked_block", token); + return block("naked"); +}); +stmt("break", function () { + const the_break = token; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (next_token.identifier && token.line === next_token.line) { + the_label = functionage.context[next_token.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + warn( + (the_label !== undefined && the_label.dead) + ? "out_of_scope_a" + : "not_label_a" + ); + } else { + the_label.used += 1; + } + the_break.label = next_token; + advance(); + } + advance(";"); + return the_break; +}); + +function do_var() { + const the_statement = token; + const is_const = the_statement.id === "const"; + the_statement.names = []; + +// A program may use var or let, but not both. + + if (!is_const) { + if (var_mode === undefined) { + var_mode = the_statement.id; + } else if (the_statement.id !== var_mode) { + warn( + "expected_a_b", + the_statement, + var_mode, + the_statement.id + ); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + warn("var_switch", the_statement); + } + if (functionage.loop > 0 && the_statement.id === "var") { + warn("var_loop", the_statement); + } + (function next() { + if (next_token.id === "{" && the_statement.id !== "var") { + const the_brace = next_token; + the_brace.names = []; + advance("{"); + (function pair() { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + survey(name); + advance(); + if (next_token.id === ":") { + advance(":"); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + next_token.label = name; + the_brace.names.push(next_token); + enroll(next_token, "variable", is_const); + advance(); + } else { + the_brace.names.push(name); + enroll(name, "variable", is_const); + } + if (next_token.id === ",") { + advance(","); + return pair(); + } + }()); + advance("}"); + advance("="); + the_brace.expression = expression(0); + the_statement.names.push(the_brace); + } else if (next_token.id === "[" && the_statement.id !== "var") { + const the_bracket = next_token; + the_bracket.names = []; + advance("["); + (function element() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + advance(); + the_bracket.names.push(name); + enroll(name, "variable", the_statement.id === "const"); + if (ellipsis) { + name.ellipsis = true; + } else if (next_token.id === ",") { + advance(","); + return element(); + } + }()); + advance("]"); + advance("="); + the_bracket.expression = expression(0); + the_statement.names.push(the_bracket); + } else if (next_token.identifier) { + const name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", is_const); + if (next_token.id === "=" || is_const) { + advance("="); + name.init = true; + name.expression = expression(0); + } + the_statement.names.push(name); + } else { + return stop("expected_identifier_a", next_token); + } + if (next_token.id === ",") { + if (!option.multivar) { + warn("expected_a_b", next_token, ";", ","); + } + advance(","); + return next(); + } + }()); + the_statement.open = ( + the_statement.names.length > 1 + && the_statement.line !== the_statement.names[1].line + ); + semicolon(); + return the_statement; +} + +stmt("const", do_var); +stmt("continue", function () { + const the_continue = token; + if (functionage.loop < 1 || functionage.finally > 0) { + warn("unexpected_a", the_continue); + } + not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; +}); +stmt("debugger", function () { + const the_debug = token; + if (!option.devel) { + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; +}); +stmt("delete", function () { + const the_token = token; + const the_value = expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; +}); +stmt("do", function () { + const the_do = token; + not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; +}); +stmt("export", function () { + const the_export = token; + let the_id; + let the_name; + let the_thing; + + function export_id() { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + the_id = next_token.id; + the_name = global.context[the_id]; + if (the_name === undefined) { + warn("unexpected_a"); + } else { + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a"); + } + exports[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + } + + the_export.expression = []; + if (next_token.id === "default") { + if (exports.default !== undefined) { + warn("duplicate_a"); + } + advance("default"); + the_thing = expression(0); + if (next_token.id === ";") { + semicolon(); + } + exports.default = the_thing; + the_export.expression.push(the_thing); + } else { + if (next_token.id === "function") { + the_thing = statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a", the_name); + } + exports[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + warn("unexpected_a"); + } else if (next_token.id === "{") { + advance("{"); + (function loop() { + export_id(); + if (next_token.id === ",") { + advance(","); + return loop(); + } + }()); + advance("}"); + semicolon(); + } else { + export_id(); + if (the_name.writable !== true) { + warn("unexpected_a", token); + } + semicolon(); + } + } + module_mode = true; + return the_export; +}); +stmt("for", function () { + let first; + const the_for = token; + if (!option.for) { + warn("unexpected_a", the_for); + } + not_top_level(the_for); + functionage.loop += 1; + advance("("); + token.free = true; + if (next_token.id === ";") { + return stop("expected_a_b", the_for, "while (", "for (;"); + } + if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + return stop("unexpected_a"); + } + first = expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = expression(0); + advance(";"); + the_for.inc = expression(0); + if (the_for.inc.id === "++") { + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; +}); +stmt("function", do_function); +stmt("if", function () { + let the_else; + const the_if = token; + the_if.expression = condition(); + the_if.block = block(); + if (next_token.id === "else") { + advance("else"); + the_else = token; + the_if.else = ( + next_token.id === "if" + ? statement() + : block() + ); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + the_if.disrupt = true; + } else { + warn("unexpected_a", the_else); + } + } + } + return the_if; +}); +stmt("import", function () { + const the_import = token; + let name; + if (typeof module_mode === "object") { + warn("unexpected_directive_a", module_mode, module_mode.directive); + } + module_mode = true; + if (next_token.identifier) { + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + const names = []; + advance("{"); + if (next_token.id !== "}") { + while (true) { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (next_token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token; + if (!rx_module.test(token.value)) { + warn("bad_module_name_a", token); + } + froms.push(token.value); + semicolon(); + return the_import; +}); +stmt("let", do_var); +stmt("return", function () { + const the_return = token; + not_top_level(the_return); + if (functionage.finally > 0) { + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (next_token.id !== ";" && the_return.line === next_token.line) { + the_return.expression = expression(10); + } + advance(";"); + return the_return; +}); +stmt("switch", function () { + let dups = []; + let last; + let stmts; + const the_cases = []; + let the_disrupt = true; + const the_switch = token; + not_top_level(the_switch); + if (functionage.finally > 0) { + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token.free = true; + the_switch.expression = expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + (function major() { + const the_case = next_token; + the_case.arity = "statement"; + the_case.expression = []; + (function minor() { + advance("case"); + token.switch = true; + const exp = expression(0); + if (dups.some(function (thing) { + return are_similar(thing, exp); + })) { + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (next_token.id === "case") { + return minor(); + } + }()); + stmts = statements(); + if (stmts.length < 1) { + warn("expected_statements_a"); + return; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "break;", + artifact(next_token) + ); + } + if (next_token.id === "case") { + return major(); + } + }()); + dups = undefined; + if (next_token.id === "default") { + const the_default = next_token; + advance("default"); + token.switch = true; + advance(":"); + the_switch.else = statements(); + if (the_switch.else.length < 1) { + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + const the_last = the_switch.else[the_switch.else.length - 1]; + if (the_last.id === "break" && the_last.label === undefined) { + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; +}); +stmt("throw", function () { + const the_throw = token; + the_throw.disrupt = true; + the_throw.expression = expression(10); + semicolon(); + if (functionage.try > 0) { + warn("unexpected_a", the_throw); + } + return the_throw; +}); +stmt("try", function () { + let the_catch; + let the_disrupt; + const the_try = token; + if (functionage.try > 0) { + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (next_token.id === "catch") { + let ignored = "ignore"; + the_catch = next_token; + the_try.catch = the_catch; + advance("catch"); + if (next_token.id === "(") { + advance("("); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + if (next_token.id !== "ignore") { + ignored = undefined; + the_catch.name = next_token; + enroll(next_token, "exception", true); + } + advance(); + advance(")"); + } + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "catch", + artifact(next_token) + ); + + } + if (next_token.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; +}); +stmt("var", do_var); +stmt("while", function () { + const the_while = token; + not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; +}); +stmt("with", function () { + stop("unexpected_a", token); +}); + +ternary("?", ":"); + +// Ambulation of the parse tree. + +function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; +} + +function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all of the +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + } + }; +} + +const posts = empty(); +const pres = empty(); +const preaction = action(pres); +const postaction = action(posts); +const preamble = amble(pres); +const postamble = amble(posts); + +function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.id === "function") { + walk_statement(thing.block); + } + if (thing.arity === "pre" || thing.arity === "post") { + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + warn("unexpected_statement_a", thing); + } + postamble(thing); + } + } +} + +function walk_statement(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_statement); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + ) { + warn( + (thing.id === "(string)" && thing.value === "use strict") + ? "unexpected_a" + : "unexpected_expression_a", + thing + ); + } + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + } +} + +function lookup(thing) { + if (thing.arity === "variable") { + +// Look up the variable in the current context. + + let the_variable = functionage.context[thing.id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable === undefined) { + stack.forEach(function (outer) { + const a_variable = outer.context[thing.id]; + if ( + a_variable !== undefined + && a_variable.role !== "label" + ) { + the_variable = a_variable; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (the_variable === undefined) { + if (declared_globals[thing.id] === undefined) { + warn("undeclared_a", thing); + return; + } + the_variable = { + dead: false, + parent: global, + id: thing.id, + init: true, + role: "variable", + used: 0, + writable: false + }; + global.context[thing.id] = the_variable; + } + the_variable.closure = true; + functionage.context[thing.id] = the_variable; + } else if (the_variable.role === "label") { + warn("label_a", thing); + } + if ( + the_variable.dead + && ( + the_variable.calls === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + ) { + warn("out_of_scope_a", thing); + } + return the_variable; + } +} + +function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); +} + +function preaction_function(thing) { + if (thing.arity === "statement" && blockage.body !== true) { + warn("unexpected_a", thing); + } + if (thing.level === 1) { + if ( + module_mode === true + || global.strict !== undefined + || thing.complex + ) { + if (thing.id !== "=>" && thing.block.strict !== undefined) { + warn("unexpected_a", thing.block.strict); + } + } else { + if (thing.block.strict === undefined) { + warn("use_strict", thing); + } + } + } + stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); +} + +function bitwise_check(thing) { + if (!option.bitwise && bitwiseop[thing.id] === true) { + warn("unexpected_a", thing); + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + warn("unexpected_a", thing); + } +} + +function pop_block() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); +} + +function activate(name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.init = true; + } + } + blockage.live.push(name); +} + +function action_var(thing) { + thing.names.forEach(activate); +} + +preaction("assignment", bitwise_check); +preaction("binary", bitwise_check); +preaction("binary", function (thing) { + if (relationop[thing.id] === true) { + const left = thing.expression[0]; + const right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + warn("expected_string_a", right); + } + } else { + const value = right.value; + if (value === "null" || value === "undefined") { + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + warn("expected_type_string_a", right, value); + } + } + } + } +}); +preaction("binary", "==", function (thing) { + warn("expected_a_b", thing, "===", "=="); +}); +preaction("binary", "!=", function (thing) { + warn("expected_a_b", thing, "!==", "!="); +}); +preaction("binary", "=>", preaction_function); +preaction("binary", "||", function (thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + warn("and", thang); + } + }); +}); +preaction("binary", "(", function (thing) { + const left = thing.expression[0]; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + const parent = functionage.name.parent; + if (parent) { + const left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + && left_variable.dead + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } +}); +preaction("binary", "in", function (thing) { + warn("infix_in", thing); +}); +preaction("binary", "instanceof", function (thing) { + warn("unexpected_a", thing); +}); +preaction("binary", ".", function (thing) { + if (thing.expression.new) { + thing.new = true; + } +}); +preaction("statement", "{", function (thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; +}); +preaction("statement", "for", function (thing) { + if (thing.name !== undefined) { + const the_variable = lookup(thing.name); + if (the_variable !== undefined) { + the_variable.init = true; + if (!the_variable.writable) { + warn("bad_assignment_a", thing.name); + } + } + } + walk_statement(thing.initial); +}); +preaction("statement", "function", preaction_function); +preaction("unary", "~", bitwise_check); +preaction("unary", "function", preaction_function); +preaction("variable", function (thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } +}); + +function init_variable(name) { + const the_variable = lookup(name); + if (the_variable !== undefined) { + if (the_variable.writable) { + the_variable.init = true; + return; + } + } + warn("bad_assignment_a", name); +} + +postaction("assignment", "+=", function (thing) { + let right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } +}); +postaction("assignment", function (thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + if (thing.id === "=") { + if (thing.names !== undefined) { + if (Array.isArray(thing.names)) { + thing.names.forEach(init_variable); + } else { + init_variable(thing.names); + } + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.writable !== true) { + warn("bad_assignment_a", lvalue); + } + } + const right = syntax[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + warn("unexpected_a", thing.expression[1]); + } + } +}); + +function postaction_function(thing) { + delete functionage.finally; + delete functionage.loop; + delete functionage.switch; + delete functionage.try; + functionage = stack.pop(); + if (thing.wrapped) { + warn("unexpected_parens", thing); + } + return pop_block(); +} + +postaction("binary", function (thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || are_similar(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option.convert) { + if (thing.expression[0].value === "") { + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === ".") { + if (thing.expression.id === "RegExp") { + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } +}); +postaction("binary", "&&", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "||", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "=>", postaction_function); +postaction("binary", "(", function (thing) { + let left = thing.expression[0]; + let arg; + let the_new; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option.eval) { + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + warn( + "expected_a_before_b", + left, + "new", + artifact(left) + ); + } + } + } else if (left.id === ".") { + let cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + cack = !cack; + } + if (rx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + warn("unexpected_a", the_new); + } else { + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + const paren = left.expression; + if (paren.id === "(") { + const array = paren.expression; + if (array.length === 1) { + const new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } +}); +postaction("binary", "[", function (thing) { + if (thing.expression[0].id === "RegExp") { + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + warn("weird_expression_a", thing.expression[1]); + } +}); +postaction("statement", "{", pop_block); +postaction("statement", "const", action_var); +postaction("statement", "export", top_level_only); +postaction("statement", "for", function (thing) { + walk_statement(thing.inc); +}); +postaction("statement", "function", postaction_function); +postaction("statement", "import", function (the_thing) { + const name = the_thing.name; + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return top_level_only(the_thing); +}); +postaction("statement", "let", action_var); +postaction("statement", "try", function (thing) { + if (thing.catch !== undefined) { + const the_name = thing.catch.name; + if (the_name !== undefined) { + const the_variable = functionage.context[the_name.id]; + the_variable.dead = false; + the_variable.init = true; + } + walk_statement(thing.catch.block); + } +}); +postaction("statement", "var", action_var); +postaction("ternary", function (thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || are_similar(thing.expression[1], thing.expression[2]) + ) { + warn("unexpected_a", thing); + } else if (are_similar(thing.expression[0], thing.expression[1])) { + warn("expected_a_b", thing, "||", "?"); + } else if (are_similar(thing.expression[0], thing.expression[2])) { + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + warn("wrap_condition", thing.expression[0]); + } +}); +postaction("unary", function (thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option.convert) { + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } +}); +postaction("unary", "function", postaction_function); +postaction("unary", "+", function (thing) { + if (!option.convert) { + warn("expected_a_b", thing, "Number(...)", "+"); + } + const right = thing.expression; + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } +}); + +function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + if (id !== "ignore") { + const name = the_function.context[id]; + if (name.parent === the_function) { + if ( + name.used === 0 + && ( + name.role !== "function" + || name.parent.arity !== "unary" + ) + ) { + warn("unused_a", name); + } else if (!name.init) { + warn("uninitialized_a", name); + } + } + } + }); +} + +function uninitialized_and_unused() { + +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (module_mode === true || option.node) { + delve(global); + } + functions.forEach(delve); +} + +// Go through the token list, looking at usage of whitespace. + +function whitage() { + let closer = "(end)"; + let free = false; + let left = global; + let margin = 0; + let nr_comments_skipped = 0; + let open = true; + let opening = true; + let right; + + function pop() { + const previous = stack.pop(); + closer = previous.closer; + free = previous.free; + margin = previous.margin; + open = previous.open; + opening = previous.opening; + } + + function push() { + stack.push({ + closer, + free, + margin, + open, + opening + }); + } + + function expected_at(at) { + warn( + "expected_a_at_b_c", + right, + artifact(right), + fudge + at, + artifact_column(right) + ); + } + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function no_space() { + if (left.line === right.line) { + if (left.thru !== right.from && nr_comments_skipped === 0) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (open) { + const at = ( + free + ? margin + : margin + 8 + ); + if (right.from < at) { + expected_at(at); + } + } else { + if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (free) { + if (right.from < margin) { + expected_at(margin); + } + } else { + const mislaid = ( + stack.length > 0 + ? stack[stack.length - 1].right + : undefined + ); + if (!open && mislaid !== undefined) { + warn( + "expected_a_next_at_b", + mislaid, + artifact(mislaid.id), + margin + 4 + fudge + ); + } else if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + stack = []; + tokens.forEach(function (the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + + const new_closer = opener[left.id]; + if (typeof new_closer === "string") { + if (new_closer !== right.id) { + opening = left.line !== right.line; + push(); + closer = new_closer; + if (opening) { + free = closer === ")" && left.free; + open = true; + margin += 4; + if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (right.switch) { + at_margin(-4); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + free = false; + open = false; + no_space_only(); + } + } else { + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like '{]') have already been detected. + + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } + } else { + if (right.statement === true) { + if (left.id === "else") { + one_space_only(); + } else { + at_margin(0); + open = false; + } + +// If right is a closer, then pop the previous state. + + } else if (right.id === closer) { + pop(); + if (opening && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-4); + } else if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + one_space(); + } else { + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + at_margin(0); + } else { + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + no_space(); + } else if ( + left.id === "." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + ) { + no_space_only(); + } else if (right.id === ".") { + no_space_only(); + } else if (left.id === ";") { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + one_space_only(); + } else if ( + left.id === "var" + || left.id === "const" + || left.id === "let" + ) { + push(); + closer = ";"; + free = false; + open = left.open; + if (open) { + margin = margin + 4; + at_margin(0); + } else { + one_space_only(); + } + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +// The jslint function itself. + +export default function jslint( + source = "", + option_object = empty(), + global_array = [] +) { + try { + warnings = []; + option = Object.assign(empty(), option_object); + anon = "anonymous"; + block_stack = []; + declared_globals = empty(); + directive_mode = true; + directives = []; + early_stop = true; + exports = empty(); + froms = []; + fudge = ( + option.fudge + ? 1 + : 0 + ); + functions = []; + global = { + id: "(global)", + body: true, + context: empty(), + from: 0, + level: 0, + line: 0, + live: [], + loop: 0, + switch: 0, + thru: 0 + }; + blockage = global; + functionage = global; + json_mode = false; + mega_mode = false; + module_mode = false; + next_token = global; + property = empty(); + shebang = false; + stack = []; + tenure = undefined; + token = global; + token_nr = 0; + var_mode = undefined; + populate(standard, declared_globals, false); + populate(global_array, declared_globals, false); + Object.keys(option).forEach(function (name) { + if (option[name] === true) { + const allowed = allowed_option[name]; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } + }); + tokenize(source); + advance(); + if (json_mode) { + tree = json_value(); + advance("(end)"); + } else { + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option.browser) { + if (next_token.id === ";") { + advance(";"); + } + } else { + +// If we are not in a browser, then the file form of strict pragma may be used. + + if ( + next_token.value === "use strict" + ) { + global.strict = next_token; + advance("(string)"); + advance(";"); + } + } + tree = statements(); + advance("(end)"); + functionage = global; + walk_statement(tree); + if (module_mode && global.strict !== undefined) { + warn("unexpected_a", global.strict); + } + if (warnings.length === 0) { + uninitialized_and_unused(); + if (!option.white) { + whitage(); + } + } + } + if (!option.browser) { + directives.forEach(function (comment) { + if (comment.directive === "global") { + warn("missing_browser", comment); + } + }); + } + early_stop = false; + } catch (e) { + if (e.name !== "JSLintError") { + warnings.push(e); + } + } + return { + directives, + edition: "2018-10-04", + exports, + froms, + functions, + global, + id: "(JSLint)", + json: json_mode, + lines, + module: module_mode === true, + ok: warnings.length === 0 && !early_stop, + option, + property, + shebang: ( + shebang + ? lines[0] + : undefined + ), + stop: early_stop, + tokens, + tree, + warnings: warnings.sort(function (a, b) { + return a.line - b.line || a.column - b.column; + }) + }; +}; diff --git a/branch.sort_const_let_var/report.js b/branch.sort_const_let_var/report.js new file mode 100644 index 000000000..f123e9055 --- /dev/null +++ b/branch.sort_const_let_var/report.js @@ -0,0 +1,221 @@ +// report.js +// 2018-07-29 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Generate JSLint HTML reports. + +/*property + closure, column, context, edition, error, exports, filter, forEach, froms, + fudge, function, functions, global, id, isArray, join, json, keys, length, + level, line, lines, message, module, name, names, option, parameters, + parent, property, push, replace, role, signature, sort, stop, warnings +*/ + +const rx_amp = /&/g; +const rx_gt = />/g; +const rx_lt = / with less destructive entities. + + return String( + string + ).replace( + rx_amp, + "&" + ).replace( + rx_lt, + "<" + ).replace( + rx_gt, + ">" + ); +} + +export default { + error: function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + let fudge = Number(Boolean(data.option.fudge)); + let output = []; + if (data.stop) { + output.push("
    JSLint was unable to finish.
    "); + } + data.warnings.forEach(function (warning) { + output.push( + "
    ", + entityify(warning.line + fudge), + ".", + entityify(warning.column + fudge), + "
    ", + entityify(warning.message), + "
    ", + entityify(data.lines[warning.line] || ""), + "" + ); + }); + return output.join(""); + }, + + function: function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + let fudge = Number(Boolean(data.option.fudge)); + let mode = ( + data.module + ? "module" + : "global" + ); + let output = []; + + if (data.json) { + return ( + data.warnings.length === 0 + ? "
    JSON: good.
    " + : "
    JSON: bad.
    " + ); + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + let global = Object.keys(data.global.context).sort(); + let froms = data.froms.sort(); + let exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + let context = the_function.context; + let list = Object.keys(context); + output.push( + "
    ", + entityify(the_function.line + fudge), + "
    ", + ( + the_function.name === "=>" + ? entityify(the_function.signature) + " =>" + : ( + typeof the_function.name === "string" + ? "«" + entityify(the_function.name) + "»" + : "" + entityify(the_function.name.id) + "" + ) + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + let params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.parent === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.parent === the_function + ); + })); + detail("outer", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.parent !== the_function + && the_variable.parent.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].parent.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + output.push( + "
    JSLint edition ", + entityify(data.edition), + "
    " + ); + return output.join(""); + }, + + property: function property_directive(data) { + +// Produce the /*property*/ directive. + + let not_first = false; + let output = ["/*property"]; + let length = 1111; + let properties = Object.keys(data.property); + + if (properties.length > 0) { + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length >= 80) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); + } + } +}; diff --git a/branch.sort_object_keys/README b/branch.sort_object_keys/README new file mode 100644 index 000000000..cd6b06c8b --- /dev/null +++ b/branch.sort_object_keys/README @@ -0,0 +1,39 @@ +JSLint, The JavaScript Code Quality Tool + +Douglas Crockford +douglas@crockford.com + +2019-03-15 + +jslint.js contains the jslint function. It parses and analyzes a source file, +returning an object with information about the file. It can also take an object +that sets options. + +index.html runs the jslint.js function in a web page. The page also depends +browser.js and report.js and jslint.css. + +jslint.css provides styling for index.html. + +browser.js runs the web user interface. + +report.js generates the results reports in HTML. + +help.html describes JSLint's usage. Please read it. + +function.html describes the jslint function and the results it produces. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[http://www.crockford.com/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. diff --git a/branch.sort_object_keys/browser.js b/branch.sort_object_keys/browser.js new file mode 100644 index 000000000..5086fed85 --- /dev/null +++ b/branch.sort_object_keys/browser.js @@ -0,0 +1,158 @@ +// browser.js +// 2018-06-16 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +/*jslint + browser +*/ + +/*property + checked, create, disable, display, error, focus, forEach, function, + getElementById, innerHTML, join, length, map, onchange, onclick, onscroll, + property, querySelectorAll, scrollTop, select, split, style, title, value +*/ + +import jslint from "./jslint.js"; +import report from "./report.js"; + +// This is the web script companion file for JSLint. It includes code for +// interacting with the browser and displaying the reports. + +let rx_crlf = /\n|\r\n?/; +let rx_separator = /[\s,;'"]+/; + +let fudge_unit = 0; + +const aux = document.getElementById("JSLINT_AUX"); +const boxes = document.querySelectorAll("[type=checkbox]"); +const fudge = document.getElementById("JSLINT_FUDGE"); +const global = document.getElementById("JSLINT_GLOBAL"); +const number = document.getElementById("JSLINT_NUMBER"); +const property = document.getElementById("JSLINT_PROPERTY"); +const property_fieldset = document.getElementById("JSLINT_PROPERTYFIELDSET"); +const report_field = document.getElementById("JSLINT_REPORT"); +const report_list = document.getElementById("JSLINT_REPORT_LIST"); +const source = document.getElementById("JSLINT_SOURCE"); +const select = document.getElementById("JSLINT_SELECT"); +const warnings = document.getElementById("JSLINT_WARNINGS"); +const warnings_list = document.getElementById("JSLINT_WARNINGS_LIST"); + +function show_numbers() { + number.value = source.value.split(rx_crlf).map(function (ignore, index) { + return index + fudge_unit; + }).join("\n"); +} + +function clear() { + aux.style.display = "none"; + number.value = ""; + property.value = ""; + property_fieldset.style.display = "none"; + report_field.style.display = "none"; + report_list.innerHTML = ""; + source.focus(); + source.value = ""; + warnings.style.display = "none"; + warnings_list.innerHTML = ""; +} + +function fudge_change() { + fudge_unit = Number(fudge.checked); + show_numbers(); +} + +function clear_options() { + boxes.forEach(function (node) { + node.checked = false; + }); + fudge_change(); + global.innerHTML = ""; +} + +function call_jslint() { + +// First build the option object. + + let option = Object.create(null); + boxes.forEach(function (node) { + if (node.checked) { + option[node.title] = true; + } + }); + +// Call JSLint with the source text, the options, and the predefined globals. + + let global_string = global.value; + let result = jslint( + source.value, + option, + ( + global_string === "" + ? undefined + : global_string.split(rx_separator) + ) + ); + +// Generate the reports. + + let error_html = report.error(result); + let function_html = report.function(result); + let property_text = report.property(result); + +// Display the reports. + + warnings_list.innerHTML = error_html; + warnings.style.display = ( + error_html.length === 0 + ? "none" + : "block" + ); + + report_list.innerHTML = function_html; + report_field.style.display = "block"; + if (property_text) { + property.value = property_text; + property_fieldset.style.display = "block"; + property.scrollTop = 0; + select.disable = false; + } else { + property_fieldset.style.display = "none"; + select.disable = true; + } + aux.style.display = "block"; + source.select(); +} + +fudge.onchange = fudge_change; + +source.onchange = function (ignore) { + show_numbers(); +}; + +source.onscroll = function () { + let ss = source.scrollTop; + number.scrollTop = ss; + let sn = number.scrollTop; + if (ss > sn) { + show_numbers(); + number.scrollTop = ss; + } +}; + +document.querySelectorAll("[name='JSLint']").forEach(function (node) { + node.onclick = call_jslint; +}); + +document.querySelectorAll("[name='clear']").forEach(function (node) { + node.onclick = clear; +}); + +document.getElementById("JSLINT_SELECT").onclick = function () { + property.focus(); + property.select(); +}; +document.getElementById("JSLINT_CLEAR_OPTIONS").onclick = clear_options; + +fudge_change(); +source.select(); +source.focus(); diff --git a/branch.sort_object_keys/function.html b/branch.sort_object_keys/function.html new file mode 100644 index 000000000..75bf39360 --- /dev/null +++ b/branch.sort_object_keys/function.html @@ -0,0 +1,615 @@ + + + + + + + + +JSLint: jslint function + + + +
    JSLint
    + +
    The jslint Function
    +
    +

    JSLint

    +

    JSLint is delivered as a set of files:

    +
    + + + + + + + + + + + + + + +
    FilenameContent
    browser.jsBrowser based UI.
    function.htmlThis page that you are looking at right now.
    help.htmlUsing jslint as a single page JSLint application.
    index.htmlSingle page JSLint application that uses the + js files.
    jslint.jsThe jslint function.
    report.jsThe report object, containing report generator functions for + HTML.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    +
    + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring"(JSLint)"
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    parenttokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    parenttokenThe function in which the variable was declared.variable
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    shebangstringThe first line of text if it started with #!line
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable may be assigned to.variable
    +

    Reports

    +

    The report object contains three functions that all take a + result from the jslint function as input. + report.js has no other dependence on other files.

    + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    +
    + +
    + + + +
    + + diff --git a/branch.sort_object_keys/help.html b/branch.sort_object_keys/help.html new file mode 100644 index 000000000..358a35baa --- /dev/null +++ b/branch.sort_object_keys/help.html @@ -0,0 +1,812 @@ + + + + + + + + + +JSLint: Help + + + +
    JSLint
    + +
    Help
    +
    +
    Il semble que la perfection soit atteinte non quand il n’y a + plus rien à ajouter, mais quand il n’y a plus rien à + retrancher.
    +
    + Antoine de Saint-Exupéry +
    +
    + Terre des Hommes (1939) +
    +

    Good Parts

    +

    The Principle of the Good Parts is

    +
    If a feature is sometimes useful and sometimes dangerous and if + there is a better option then always use the better option.
    +

    JSLint is a JavaScript program that looks for problems in + JavaScript programs. It is a code quality tool.

    +

    When C was + a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    +

    As the language matured, the definition of the language was + strengthened to eliminate some insecurities, and compilers got better + at issuing warnings. lint is no longer needed.

    +

    JavaScript is a + young-for-its-age language. It was originally intended to do small tasks in + webpages, tasks for which Java was too heavy and clumsy. But JavaScript is + a surprisingly capable language, and it is now being used in larger + projects. Many of the features that were intended to make the language easy + to use are troublesome when projects become complicated. A lint + for JavaScript is needed: JSLint, a JavaScript syntax checker + and validator.

    +

    JSLint takes a JavaScript source and scans it. If it finds a + problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by the ECMAScript Programming Language + Standard (the strangely named document that governs JavaScript). + JSLint will reject most legal programs. It is a higher + standard.

    +

    JavaScript is a sloppy language, but hidden deep inside there is an elegant, + better language. JSLint helps you to program in that better + language and to avoid most of the slop. JSLint will reject + programs that browsers will accept because JSLint is concerned + with the quality of your code and browsers are not. You should gladly accept + all of JSLint's advice.

    +

    JSLint can operate on JavaScript source + or JSON text.

    +

    ECMAScript Sixth Edition

    +

    Some of + ES6’s features are good, so JSLint will recognize the good + parts of ES6.

    +

    Currently, these features are recognized:

    +
      +
    • The ... ellipsis marker in parameter + lists and argument lists, replacing the arguments object + for variadic functions.
    • +
    • The let statement, which is like the var + statement except that it respects block scope. + You may use let or var but not both.
    • +
    • The const statement is like the let statement + except that it disallows the use of assignment on the variable, although + if the value of the variable is mutable, it can still be mutated. + const is preferred to let. +
    • Destructuring of arrays and objects is allowed in parameter lists and on + the left side of let, and const, but not + var or assignment statements, and not deep destructuring + or eliding.
    • +
    • Enhanced object literals, providing shorter forms for function + declaration and properties that are initialized by variables with the + same name.
    • +
    • The fat arrow => fart functions.
    • +
    • The simplest forms of import and export.
    • +
    • `Megastring` literals, but not nested + `megastring` literals.
    • +
    • New global functions, such as Map, Set, + WeakMap, and WeakSet.
    • +
    • 0b- and 0o- number literals.
    • +
    +

    The most important new feature of ES6 is proper tail calls. This has no + new syntax, so JSLint doesn’t see it. But it makes recursion + much more attractive, which makes loops, particularly for + loops, much less attractive.

    +

    import export

    +

    The ES6 module feature will be an important improvement over JavaScript’s + global variables as a means of linking separate files together. + JSLint recognizes a small but essential subset of the module + syntax.

    +
    +

    import name from stringliteral;

    +

    import {name} from stringliteral;

    +

    import(string).then(function);

    +

    export default function () {};

    +

    export default function name() {};

    +

    export default expression;

    +

    export function name() {}

    +

    export name; // where name is const

    +

    export {name};

    +
    +

    Directives

    +

    JSLint provides three directives that may be placed in a + file to manage JSLint’s behavior. Use of these directives is + optional. If they are used, they should be placed in a source file before + the first statement. They are written in the form of a comment, where the + directive name is placed immediately after the opening of the comment before + any whitespace. The three directives are global, + jslint, and property. Directives in a file are + stronger than options selected from the UI or passed with the option object.

    +

    /*global*/

    +

    The /*global*/ directive is used to specify a set of globals + (usually functions and objects containing functions) that are available to + this file. This was commonly used in browsers to link source files together + before ES6 modules appeared. Use of global variables is strongly + discouraged, but unfortunately web browsers require their use. The + /*global*/ directive can only be used when the + Assume a browser option is selected. +

    Each of the names listed will indicate a read-only global variable. The names + are separated by , comma. This directive + should not be used if import or export is used. + This directive inhibits warnings, but it does not declare the names in the + execution environment. +

    For example: +

    /*global +
    ADSAFE, report, jslint
    +*/
    +

    instructs JSLint to not give warnings about the global + variables ADsafe, report, and jslint. + However, if any of those names are expected to be supplied by other files + and those other files fail to do so, then execution errors will result. It + is usually better to use top-level variable declarations instead: +

        var ADSAFE;
    +    var report;
    +    var jslint; 
    +

    Using var in this way allows comparing a global variable to the + undefined value to determine whether it is has been used in the global context.

    +

    /*jslint*/

    +

    The /*jslint*/ directive allows for the control of several + options. These options can also be set from the + JSLint.com user interface.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Tolerate bitwise operatorsbitwisetrue if bitwise operators should be allowed. The + bitwise operators are rarely used in JavaScript programs, so it + is usually more likely that & is a mistyping of + && than that it indicates a bitwise and. + JSLint will give warnings on the bitwise operators + unless this option is selected.
    Assume a browser browsertrue if the standard browser globals should be + predefined. This option will reject the use of + import and export. This option also + disallows the file form of the "use + strict" pragma. It does not supply + self; you will have to request that unnecessary + alias of the dreaded global object yourself. It adds the same + globals as this directive: +
    /*global +
    + clearInterval, clearTimeout, document, event, FileReader, + FormData, history, localStorage, location, name, navigator, + screen, sessionStorage, setInterval, setTimeout, Storage, + URL, window, XMLHttpRequest +
    + */
    +
    Tolerate conversion operatorsconverttrue if !!, + prefix, + and concatenation with "" are allowed. + The Boolean, Number, and + String functions are preferred
    Assume CouchDBcouchtrue if + Couch DB + globals should be predefined. It adds the same globals as this + directive: +
    /*global +
    emit, getRow, isArray, log, provides, registerType, require, + send, start, sum, toJSON
    + */
    Assume in developmentdeveltrue if browser globals that are useful in + development should be predefined, and if debugger + statements and TODO comments + should be allowed. It adds the + same globals as this directive: +
    /*global +
    alert, confirm, console, prompt
    + */
    Be sure to turn this option off before going + into production.
    Tolerate evalevaltrue if eval should be allowed. In the + past, the eval function was the most misused + feature of the language.
    Tolerate for statementfortrue if the for statement should be + allowed. It is almost always better to use the array methods + instead.
    Fudge the line and column numbersfudgetrue if the origin of a text file is line 1 column + 1. Programmers know that number systems start at zero. + Unfortunately, most text editors start numbering lines and + columns at one. JSLint will by default start + numbering at zero. Use this option to start the numbering at one + in its reports.
    Tolerate get and setgetsettrue if accessor properties are allowed in object literals.
    Tolerate long source lineslongtrue if a line can contain more than 80 characters.
    Assume Node.jsnodetrue if Node.js globals should be predefined. It + will predefine globals that are used in the Node.js environment. + It adds the same globals as this directive: +
    /*global +
    + Buffer, clearImmediate, clearInterval, clearTimeout, console, exports, + module, process, require, setImmediate, setInterval, setTimeout, URL, + URLSearchParams, __dirname, __filename +
    + */
    Tolerate single quote stringssingletrue if 'single quote + should be allowed to enclose string literals.
    Tolerate this
    thistrue if this should be allowed.
    Tolerate whitespace messwhitetrue if the whitespace rules should be ignored.
    +

    For example:

    +
    /*jslint
    +    bitwise, node
    +*/
    + +

    /*property*/

    +

    The /*property*/ directive is used to declare a list of + property identifiers that are used in the file. Each property name in the + program is looked up in this list. If a name is not found, that indicates an + error, most likely a typing error.

    +

    The list can also be used to evade some of JSLint’s naming + rules.

    +

    JSLint can build the /*property*/ list for you. At + the bottom of its report, JSLint displays a + /*property*/ directive. It contains all of the names that were + used with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. You + can copy the /*property*/ directive to the top of your + script file. JSLint will check the spelling of all property + names against the list. That way, you can have JSLint look for + misspellings for you.

    +

    For example,

    +
    /*property +
    charAt, slice, _$_
    + */
    +

    ignore

    +

    JSLint introduces a new reserved word: ignore. It + is used in parameter lists and in catch clauses to indicate a + parameter that will be ignored. Unused warnings will not be produced for + ignore. +

    function handler(ignore, value) {
    +    return do_something_useful(value);
    +}
    +

    var let const

    +

    JavaScript provides three statements for declaring variables: + var, let, and const. The + var statement suffers from a bad practice called hoisting. The + let statement does not do hoisting and respects block scope. + The const statement is like let except that it + marks the variable (but not its contents) as read only, making it an error + to attempt to assign to the variable. When given a choice, + const is the best, var is the worst.

    +

    JSLint uses the intersection of the var rules and the + let rules, and by doing so avoids the errors related to either. + A name should be declared only once in a function. It should be declared + before it is used. It should not be used outside of the block in which it is + declared. A variable should not have the same name as a variable or + parameter in an outer function. Do not mix var and + let. Declare one name per statement.

    +

    = == ===

    +

    Fortran made a terrible + mistake in using the equality operator as its assignment operator. That + mistake has been replicated in most languages since then. C compounded that + mistake by making == its equality operator. The visual + similarity is a source of errors. JavaScript compounded this further by + making == a type coercing comparison operator that produces + false positive results. This was mitigated by adding the === + operator, leaving the broken == operator in place.

    +

    JSLint attempts to minimize errors by the following rules:

    +

    == is not allowed. This avoids the false positives, and + increases the visual distance between = and ===. + Assignments are not allowed in expression position, and comparisons are not + allowed in statement position. This also reduces confusion. 

    +

    Semicolon

    +

    JavaScript uses a C-like syntax that requires the use of semicolons to + delimit certain statements. JavaScript attempts to make those semicolons + optional with an automatic semicolon insertion mechanism, but it does not + work very well. Automatic semicolon insertion was added to make + things easier for beginners. Unfortunately, it sometimes fails. Do not rely + on it unless you are a beginner.

    +

    JSLint expects that every statement will be followed by + ; except for for, function, + if, switch, try, and + while. JSLint does not expect to see unnecessary + semicolons, the empty statement, or empty blocks.

    +

    function =>

    +

    JavaScript has four syntactic forms for making function objects: function + statements, function expressions, enhanced object literals, and the + => fart operator.

    +

    The function statement creates a variable and assigns the function object to + it. It should be used in a file or function body, but not inside of a block.

    +
    function name(parameters) { +
    statements
    + }
    +

    The function expression unfortunately looks like the function statement. It + may appear anywhere that an expression may appear, but not in statement + position and not in a loop. It produces a function object but does not + create a variable in which to store it.

    +
    function (parameters) { +
    statements
    + }
    +

    The enhanced object literal provides an ES6 shorthand for creating a property + whose value is a function, saving you from having to type + : colon and function.

    +
    { +
    name(parameters) { +
    statements
    + }
    + }
    +

    Finally, ES6 provides an even shorter form of function expression that leaves + out the words function and return:

    +
    (parameters) => + expression
    +

    JSLint requires the parens around the parameters, and forbids a + { left brace after the + => fart to avoid syntactic ambiguity.

    +

    Comma

    +

    The , comma operator is unnecessary and can + mask programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as + an operator. It does not expect to see elided elements in array literals. A + comma should not appear after the last element of an array literal or object + literal.

    +

    Blocks

    +

    JSLint expects blocks with function, + if, switch, while, for, + do, and try statements and nowhere else.

    +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    +

    JavaScript allows an if to be written like this:

    +
    if (condition)
    +    statement;
    +

    That form is known to contribute to mistakes in projects where many + programmers are working on the same code. That is why JSLint + expects the use of a block:

    +
    if (condition) {
    +    statements;
    +}
    +

    Experience shows that this form is more resilient.

    +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call. All other expression statements are considered + to be errors.

    +

    for

    +

    JSLint does not recommend use of the for statement. + Use array methods like forEach instead. The for + option will suppress some warnings. The forms of for that + JSLint accepts are restricted, excluding the new ES6 forms.

    +

    for in

    +

    JSLint does not recommend use of the for + in statement. Use Object.keys instead.

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, it also + loops through all of the properties that were inherited through the + prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written + without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    for (name in object) {
    +    if (object.hasOwnProperty(name)) {
    +        ....
    +    }
    +}
    +

    Note that the above code will fail if the object contains a property + named hasOwnProperty. Use Object.keys instead.

    +

    switch

    +

    A common error in switch statements is to forget to place a + break statement after each case, resulting in unintended + fall-through. JSLint expects that the statement before the next + case or default is one of these: + break, return, or throw.

    +

    with

    +

    The with statement was intended to provide a shorthand in + accessing properties in deeply nested objects. Unfortunately, it behaves + very badly when setting new properties. Never use the with + statement.

    +

    JSLint does not expect to see a with statement.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that + labels will be distinct from vars and parameters.

    +

    Unreachable Code

    +

    JSLint expects that a return, break, + or throw statement will be followed by a + } right brace or case or + default.

    +

    Confusing Pluses and Minuses

    +

    JSLint expects that + will not be followed by + + or ++, and that - will not be + followed by - or --. A misplaced space can turn + + + into ++, an error that is difficult to see. + Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ increment and + -- decrement operators have been known to + contribute to bad code by encouraging excessive trickiness. They are second + only to faulty architecture in enabling to viruses and other security + menaces. Also, preincrement/postincrement confusion can produce off-by-one + errors that are extremely difficult to diagnose. Fortunately, they are also + complete unnecessary. There are better ways to add 1 to a variable.

    +

    It is best to avoid these operators entirely and rely on += and + -= instead.

    +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. + JSLint looks for problems that may cause portability problems. + It also attempts to resolve visual ambiguities by recommending explicit + escapement.

    +

    JavaScript’s syntax for regular expression literals overloads the + / slash character. To avoid ambiguity, + JSLint expects that the character preceding a regular + expression literal is a ( left paren or + = equal or + : colon or + , comma character.

    +

    this

    +
    Having this in the language makes it harder to talk + about the language. It is like pair programming with Abbott and Costello. +
    +

    Avoid using this. Warnings about this can be + suppressed with option.this.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the + new prefix. The new prefix creates a new object + based on the function's prototype, and binds that object to the + function's implied this parameter. If you neglect to use the + new prefix, no new object will be made and this + will be bound to the global object.

    +

    JSLint enforces the convention that constructor functions be + given names with initial uppercase. JSLint does not expect to + see a function invocation with an initial uppercase name unless it has the + new prefix. JSLint does not expect to see the + new prefix used with functions whose names do not start with + initial uppercase.

    +

    JSLint does not expect to see the wrapper forms + new Number, new String, new Boolean.

    +

    JSLint does not expect to see new Object. + Use Object.create(null) instead.

    +

    Whitespace

    +

    JSLint has a specific set of rules about the use of whitespace. + Where possible, these rules are consistent with centuries of good practice + with literary style.

    +

    The indentation increases by 4 spaces when the last token on a line is + { left brace, + [ left bracket, + ( left paren. The matching closing + token will be the first token on a line, restoring the previous + indentation.

    +

    The ternary operator can be visually confusing, so + ? question mark and + : colon always begin a line and increase + the indentation by 4 spaces.

    +
    return (
    +    (the_token.id === "(string)" || the_token.id === "(number)")
    +    ? String(the_token.value)
    +    : the_token.id
    +);
    +

    The word function is always followed with one space.

    +

    Clauses (case, catch, default, + else, finally) are not statements and so should + not be indented like statements.

    +

    Spaces are used to make things that are not invocations look less like + invocations.

    +

    Tabs and spaces should not be mixed. We should pick just one in order to + avoid the problems that come from having both. Personal preference is an + extremely unreliable criterion. Neither offers a powerful advantage over the + other. Fifty years ago, tab had the advantage of consuming less memory, but + Moore's Law has eliminated that advantage. Space has one clear advantage + over tab: there is no reliable standard for how many spaces a tab + represents, but it is universally accepted that a space occupies a space. So + use spaces. You can edit with tabs if you must, but make sure it is spaces + again before you commit. Maybe someday we will finally get a universal + standard for tabs, but until that day comes, the better choice is spaces.

    +

    Report

    +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    +
      +
    • The line number on which it starts.
    • +
    • The function’s name. In the case of anonymous functions, + JSLint will attempt to + «guess» the name.
    • +
    • The parameters.
    • +
    • Variables: The variables that are declared in the function.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Exceptions: The variables that are declared by catch + clauses of try statements.
    • +
    • Outer: Variables used by this function that are declared in + other functions.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements.

    +

    Try it

    +

    Try it. Paste your script + into the window and click the + + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    JSLint is written entirely in JavaScript, so it can run + anywhere that JavaScript (or even Java) can run.

    +

    Perfectly fine

    +

    JSLint was designed to reject code that some would consider to + be perfectly fine. The reason for this is that JSLint's + purpose is to help produce programs that are free of error. That is + difficult in any language and is especially hard in JavaScript. + JSLint attempts to help you increase the visual distance + between correct programs and incorrect programs, making the remaining errors + more obvious. JSLint will give warnings about things that are + not necessarily wrong in the current situation, but which have been observed + to mask or obscure errors. Avoid those things when there are better options + available.

    +

    It is dangerous out there. JSLint is here to help.

    +

    Warning

    +

    JSLint will hurt your feelings. Side effects may include + headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, + dry mouth, cleaner code, and a reduced error rate.

    +

    Further Reading

    + +
    +

    + Code Conventions for the JavaScript Programming Language.

    + + + +
    + + diff --git a/branch.sort_object_keys/index.html b/branch.sort_object_keys/index.html new file mode 100644 index 000000000..822acaad6 --- /dev/null +++ b/branch.sort_object_keys/index.html @@ -0,0 +1,108 @@ + + + + + + + + + +JSLint: The JavaScript Code Quality Tool + + +
    +
    Source
    +
    + + +
    +
    + Warnings +
    +
    +
    + Function Report +
    +
    +
    + Property Directive + +
    +
    + + + + +
    +
    Options +
    Assume... +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Global variables... + +
    +
    +
    + + diff --git a/branch.sort_object_keys/jslint.css b/branch.sort_object_keys/jslint.css new file mode 100644 index 000000000..cc3bf1696 --- /dev/null +++ b/branch.sort_object_keys/jslint.css @@ -0,0 +1,341 @@ +@font-face { + font-family: 'Programma'; + font-weight: bold; + src: url('Programma-Bold.woff2') format('woff2'); +} +@font-face { + font-family: 'Daley'; + font-weight: bold; + src: url('Daley-Bold.woff2') format('woff2'); +} + +body { + background-color: antiquewhite; + color: black; + font-family: 'Daley', sans-serif; + margin: 0; + padding: 0; +} + +#JSLINT_TITLEBOX { + font-family: 'Daley', sans-serif; + margin: 0; + margin-left: 3%; + margin-right: 3%; + padding: 0; +} + +#JSLINT_TITLEBOX ul { + float: right; + font-size: 12pt; +} + +#JSLINT_TITLEBOX div { + color: darkslategray; + float: left; + font-size: 48pt; +} + +#JSLINT_ fieldset { + background-color: gainsboro; + border: 0; + clear: both; + margin-bottom: 1em; + margin-left: 3%; + margin-right: 3%; + margin-top: 1em; + padding: 0; + width: auto; +} +#JSLINT_ legend { + background-color: darkslategray; + border: 0; + color: white; + font-size: 100%; + font-style: normal; + font-weight: normal; + margin: 0; + padding-bottom: 0.25em; + padding-left: 0; + padding-right: 0; + padding-top: 0.25em; + text-align: center; + width: 100%; +} +#JSLINT_ button { + background-color: darkslategray; + border: 0; + color: white; + cursor: pointer; + font-family: 'Daley', sans-serif; + font-size: 100%; + font-style: normal; + margin-left: 1em; + margin-right: 1em; + padding-left: 1em; + padding-right: 1em; + padding-bottom: 0.25em; + padding-top: 0.25em; + text-align: center; +} +#JSLINT_ button:hover { + background-color: slategray; + color: white; + border: 0; +} + +#JSLINT_ button:active { + border: 0; + color: black; +} + +#JSLINT_ button:disabled { + background-color: gray; + border: 0; + color: gainsboro; +} + +a:visited { + color: #69565c; +} + +a:link { + color: black; +} + +#JSLINT_ input[type="text"] { + background-color: white; + border: 0; + color: black; + margin-bottom: 0.2em; + margin-right: 0.5em; + padding-left: 0.5em; + padding-right: 0.5em; + text-align: right; + width: 3em; +} + +#JSLINT_ textarea { + border: 0; + background-color: white; + border: 0; + font-family: Programma, monospace; + font-size: 100%; + font-weight: bold; + resize: none; +} + +#JSLINT_ textarea::selection { + background-color: yellow; + color: black; +} + +#JSLINT_NUMBER { + color: darkgray; + display: inline-block; + height: 4in; + margin: 2%; + margin-right: 0; + overflow: hidden; + padding-right: 0.5%; + text-align: right; + white-space: pre; + width: 4%; +} + +#JSLINT_SOURCE { + color: black; + display: inline-block; + font-size: 100%; + height: 4in; + margin: 2%; + margin-left: 0; + margin-right: 0; + overflow: auto; + padding-left: 0.5%; + white-space: pre; + width: 91%; +} + +#JSLINT_PROPERTY { + background-color: honeydew; + border: 0; + margin: 2%; + padding: 0.5%; + white-space: pre; + width: 95%; +} + +#JSLINT_GLOBAL { + white-space: normal; + width: 95%; +} +#JSLINT_ label { + font-family: sans-serif; + font-size: 90%; + padding-left: 0.25em; +} +#JSLINT_OPTIONS>div { + float: left; + margin: 0.5em; +} + +#JSLINT_ address { + color: black; + display: block; + float: right; + font-family: serif; + font-size: 90%; + margin-left: 1em; +} +#JSLINT_ dl { + background-color: cornsilk; + color: white; + margin: 0; + padding-bottom: 2pt; + padding-left: 1em; + padding-right: 1em; + padding-top: 2pt; +} +#JSLINT_ dfn { + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + margin-bottom: 2pt; +} +#JSLINT_ dt { + color: black; + display: block; + float: left; + font-family: serif; + font-size: 75%; + font-style: italic; + margin: 0; + width: 8em; + text-align: right; +} +#JSLINT_ dd { + color: black; + display: block; + font-family: Programma, monospace; + font-weight: bold; + margin-left: 8em; + padding-bottom: 2pt; +} +#JSLINT_WARNINGS>legend { + background-color: indianred; +} +#JSLINT_WARNINGS>div { + background-color: pink; + padding: 1em; +} +#JSLINT_WARNINGS cite { + color: black; + display: block; + font-family: serif; + font-size: 100%; + font-style: normal; + margin-bottom: 4pt; + margin-left: 20pt; + margin-right: 20pt; + margin-top: 4pt; + overflow-x: hidden; +} +#JSLINT_WARNINGS samp { + background-color: lavenderblush; + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + padding: 4pt; + margin-bottom: 0; + margin-left: 16pt; + margin-right: 16pt; + margin-top: 0; + white-space: pre-wrap; +} + +#JSLINT_REPORT center { + margin-top: 1em; +} + +#JSLINT_WARNINGS dl address { + color: black; + display: inline; + float: none; + font-size: 80%; + margin: 0; +} + +#JSLINT_REPORT>div { + padding: 1em; +} + +#JSLINT_ dl.level0 { + color: black; + background-color: white; +} + +#JSLINT_ dl.level1 { + color: black; + background-color: #ffffe0; /* yellow */ + margin-left: 1em; +} + +#JSLINT_ dl.level2 { + color: black; + background-color: #e0ffe0; /* green */ + margin-left: 2em; +} + +#JSLINT_ dl.level3 { + color: black; + background-color: #D0D0ff; /* blue */ + margin-left: 3em; +} + +#JSLINT_ dl.level4 { + background-color: #ffe0ff; /* purple */ + color: black; + margin-left: 4em; +} + +#JSLINT_ dl.level5 { + background-color: #ffe0e0; /* red */ + color: black; + margin-left: 5em; +} + +#JSLINT_ dl.level6 { + background-color: #ffe390; /* orange */ + color: black; + margin-left: 6em; +} + +#JSLINT_ dl.level7 { + background-color: #e0e0e0; /* gray */ + color: black; + margin-left: 7em; +} + +#JSLINT_ dl.level8 { + color: black; + margin-left: 8em; +} + +#JSLINT_ dl.level9 { + color: black; + margin-left: 9em; +} + +.none { + display: none; +} +.center { + text-align: center; +} diff --git a/branch.sort_object_keys/jslint.js b/branch.sort_object_keys/jslint.js new file mode 100644 index 000000000..8d189c006 --- /dev/null +++ b/branch.sort_object_keys/jslint.js @@ -0,0 +1,5045 @@ +// jslint.js +// 2021-05-17 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// jslint(source, option_object, global_array) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze, a string or an array of strings. +// option_object An object whose keys correspond to option names. +// global_array An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all of the functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// 1. If the source is a single string, split it into an array of strings. +// 2. Turn the source into an array of tokens. +// 3. Furcate the tokens into a parse tree. +// 4. Walk the tree, traversing all of the nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// 5. Check the whitespace between the tokens. + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*property + a, and, arity, assign, b, bad_assignment_a, bad_directive_a, bad_get, + bad_module_name_a, bad_option_a, bad_property_a, bad_set, bitwise, block, + body, browser, c, calls, catch, charCodeAt, closer, closure, code, column, + concat, constant, context, convert, couch, create, d, dead, default, devel, + directive, directives, disrupt, dot, duplicate_a, edition, ellipsis, else, + empty_block, eval, every, expected_a, expected_a_at_b_c, + expected_a_b, expected_a_b_from_c_d, expected_a_before_b, + expected_a_next_at_b, expected_digits_after_a, expected_four_digits, + expected_identifier_a, expected_line_break_a_b, expected_regexp_factor_a, + expected_space_a_b, expected_statements_a, expected_string_a, + expected_type_string_a, exports, expression, extra, finally, flag, for, + forEach, free, freeze, freeze_exports, from, froms, fud, fudge, + function_in_loop, functions, g, getset, global, i, id, identifier, import, + inc, indexOf, infix_in, init, initial, isArray, isNaN, join, json, keys, + label, label_a, lbp, led, length, level, line, lines, live, long, loop, m, + margin, match, message, misplaced_a, misplaced_directive_a, missing_browser, + missing_m, module, naked_block, name, names, nested_comment, new, node, + not_label_a, nr, nud, number_isNaN, ok, open, opening, option, + out_of_scope_a, parameters, parent, pop, property, push, quote, raw, + redefinition_a_b, replace, required_a_optional_b, reserved_a, role, search, + shebang, signature, single, slice, some, sort, split, startsWith, statement, + stop, subscript_a, switch, test, this, thru, toString, todo_comment, + tokens, too_long, too_many_digits, tree, try, type, u, unclosed_comment, + unclosed_mega, unclosed_string, undeclared_a, unexpected_a, + unexpected_a_after_b, unexpected_a_before_b, unexpected_at_top_level_a, + unexpected_char_a, unexpected_comment, unexpected_directive_a, + unexpected_expression_a, unexpected_label_a, unexpected_parens, + unexpected_space_a_b, unexpected_statement_a, unexpected_trailing_space, + unexpected_typeof_a, uninitialized_a, unordered_param_a, + unordered_property_a, unreachable_a, unregistered_property_a, unused_a, + use_double, use_open, use_spaces, used, value, var_loop, var_switch, + variable, warning, warnings, weird_condition_a, weird_expression_a, + weird_loop, weird_relation_a, white, wrap_condition, wrap_immediate, + wrap_parameter, wrap_regexp, wrap_unary, wrapped, writable, y +*/ + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than '{}' because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +function populate(array, object = empty(), value = true) { + +// Augment an object by taking property names from an array of strings. + + array.forEach(function (name) { + object[name] = value; + }); + return object; +} + +const allowed_option = { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + bitwise: true, + browser: [ + "caches", "CharacterData", "clearInterval", "clearTimeout", "document", + "DocumentType", "DOMException", "Element", "Event", "event", "fetch", + "FileReader", "FontFace", "FormData", "history", "IntersectionObserver", + "localStorage", "location", "MutationObserver", "name", "navigator", + "screen", "sessionStorage", "setInterval", "setTimeout", "Storage", + "TextDecoder", "TextEncoder", "URL", "window", "Worker", + "XMLHttpRequest" + ], + convert: true, + couch: [ + "emit", "getRow", "isArray", "log", "provides", "registerType", + "require", "send", "start", "sum", "toJSON" + ], + devel: [ + "alert", "confirm", "console", "prompt" + ], + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + node: [ + "Buffer", "clearImmediate", "clearInterval", "clearTimeout", + "console", "exports", "module", "process", "require", + "setImmediate", "setInterval", "setTimeout", "TextDecoder", + "TextEncoder", "URL", "URLSearchParams", "__dirname", "__filename" + ], + single: true, + this: true, + white: true +}; + +const anticondition = populate([ + "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%", + "typeof", "(number)", "(string)" +]); + +// These are the bitwise operators. + +const bitwiseop = populate([ + "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=", + ">>>", ">>>=" +]); + +const escapeable = populate([ + "\\", "/", "`", "b", "f", "n", "r", "t" +]); + +const opener = { + +// The open and close pairs. + + "${": "}", // mega + "(": ")", // paren + "[": "]", // bracket + "{": "}" // brace +}; + +// The relational operators. + +const relationop = populate([ + "!=", "!==", "==", "===", "<", "<=", ">", ">=" +]); + +// This is the set of infix operators that require a space on each side. + +const spaceop = populate([ + "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/", + "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=", + ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" +]); + +const standard = [ + +// These are the globals that are provided by the language standard. + + "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI", + "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error", + "EvalError", "Float32Array", "Float64Array", "Generator", + "GeneratorFunction", "Int8Array", "Int16Array", "Int32Array", "Intl", + "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat", + "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", + "Set", "String", "Symbol", "SyntaxError", "System", "TypeError", + "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", + "URIError", "WeakMap", "WeakSet" +]; + +const bundle = { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + and: "The '&&' subexpression should be wrapped in parens.", + bad_assignment_a: "Bad assignment to '{a}'.", + bad_directive_a: "Bad directive '{a}'.", + bad_get: "A get function takes no parameters.", + bad_module_name_a: "Bad module name '{a}'.", + bad_option_a: "Bad option '{a}'.", + bad_property_a: "Bad property name '{a}'.", + bad_set: "A set function takes one parameter.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + expected_a: "Expected '{a}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: ( + "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'." + ), + expected_a_before_b: "Expected '{a}' before '{b}'.", + expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.", + expected_digits_after_a: "Expected digits after '{a}'.", + expected_four_digits: "Expected four digits after '\\u'.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.", + expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.", + expected_space_a_b: "Expected one space between '{a}' and '{b}'.", + expected_statements_a: "Expected statements before '{a}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + expected_type_string_a: "Expected a type string and instead saw '{a}'.", + freeze_exports: ( + "Expected 'Object.freeze('. All export values should be frozen." + ), + function_in_loop: "Don't make functions within a loop.", + infix_in: ( + "Unexpected 'in'. Compare with undefined, " + + "or use the hasOwnProperty method instead." + ), + label_a: "'{a}' is a statement label.", + misplaced_a: "Place '{a}' at the outermost level.", + misplaced_directive_a: ( + "Place the '/*{a}*/' directive before the first statement." + ), + missing_browser: "/*global*/ requires the Assume a browser option.", + missing_m: "Expected 'm' flag on a multiline regular expression.", + naked_block: "Naked block.", + nested_comment: "Nested comment.", + not_label_a: "'{a}' is not a label.", + number_isNaN: "Use Number.isNaN function to compare with NaN.", + out_of_scope_a: "'{a}' is out of scope.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + required_a_optional_b: ( + "Required parameter '{a}' after optional parameter '{b}'." + ), + reserved_a: "Reserved name '{a}'.", + subscript_a: "['{a}'] is better written in dot notation.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line is longer than 80 characters.", + too_many_digits: "Too many digits.", + unclosed_comment: "Unclosed comment.", + unclosed_mega: "Unclosed mega literal.", + unclosed_string: "Unclosed string.", + undeclared_a: "Undeclared '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_a_after_b: "Unexpected '{a}' after '{b}'.", + unexpected_a_before_b: "Unexpected '{a}' before '{b}'.", + unexpected_at_top_level_a: "Expected '{a}' to be in a function.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_directive_a: "When using modules, don't use directive '/*{a}'.", + unexpected_expression_a: ( + "Unexpected expression '{a}' in statement position." + ), + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_parens: "Don't wrap function literals in parens.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_statement_a: ( + "Unexpected statement '{a}' in expression position." + ), + unexpected_trailing_space: "Unexpected trailing space.", + unexpected_typeof_a: ( + "Unexpected 'typeof'. Use '===' to compare directly with {a}." + ), + uninitialized_a: "Uninitialized '{a}'.", + unordered_param_a: ( + "Parameter '{a}' not listed in alphabetical order." + ), + unordered_property_a: ( + "Property name '{a}' not listed in alphabetical order." + ), + unreachable_a: "Unreachable '{a}'.", + unregistered_property_a: "Unregistered property name '{a}'.", + unused_a: "Unused '{a}'.", + use_double: "Use double quotes, not single quotes.", + use_open: ( + "Wrap a ternary expression in parens, " + + "with a line break after the left paren." + ), + use_spaces: "Use spaces, not tabs.", + var_loop: "Don't declare variables in a loop.", + var_switch: "Don't declare variables in a switch.", + weird_condition_a: "Weird condition '{a}'.", + weird_expression_a: "Weird expression '{a}'.", + weird_loop: "Weird loop.", + weird_relation_a: "Weird relation '{a}'.", + wrap_condition: "Wrap the condition in parens.", + wrap_immediate: ( + "Wrap an immediate function invocation in parentheses to assist " + + "the reader in understanding that the expression is the result " + + "of a function, and not the function itself." + ), + wrap_parameter: "Wrap the parameter in parens.", + wrap_regexp: "Wrap this regexp in parens to avoid confusion.", + wrap_unary: "Wrap the unary expression in parens." +}; + +// Regular expression literals: + +function tag_regexp(strings) { + return new RegExp(strings.raw[0].replace(/\s/g, "")); +} + +// supplant {variables} +const rx_supplant = /\{([^{}]*)\}/g; +// carriage return, carriage return linefeed, or linefeed +const rx_crlf = tag_regexp ` + \n + | \r \n? +`; +// identifier +const rx_identifier = tag_regexp ` ^( + [ a-z A-Z _ $ ] + [ a-z A-Z 0-9 _ $ ]* +)$`; +const rx_module = tag_regexp ` ^ [ a-z A-Z 0-9 _ $ : . @ \- \/ ]+ $ `; +const rx_bad_property = tag_regexp ` + ^_ + | \$ + | Sync $ + | _ $ +`; +// star slash +const rx_star_slash = tag_regexp ` \* \/ `; +// slash star +const rx_slash_star = tag_regexp ` \/ \* `; +// slash star or ending slash +const rx_slash_star_or_slash = tag_regexp ` \/ \* | \/ $ `; +// uncompleted work comment +const rx_todo = tag_regexp ` \b (?: + todo + | TO \s? DO + | HACK +) \b `; +// tab +const rx_tab = /\t/g; +// directive +const rx_directive = tag_regexp ` ^ ( + jslint + | property + | global +) \s+ ( .* ) $ `; +const rx_directive_part = tag_regexp ` ^ ( + [ a-z A-Z $ _ ] [ a-z A-Z 0-9 $ _ ]* +) (?: + : \s* ( true | false ) +)? ,? \s* ( .* ) $ `; +// token +const rx_token = tag_regexp ` ^ ( + (\s+) + | ( + [ a-z A-Z _ $ ] + [ a-z A-Z 0-9 _ $ ]* + ) + | [ + ( ) { } \[ \] , : ; ' " ~ \` + ] + | \? [ ? . ]? + | = (?: + = =? + | > + )? + | \.+ + | \* [ * \/ = ]? + | \/ [ * \/ ]? + | \+ [ = + ]? + | - [ = \- ]? + | [ \^ % ] =? + | & [ & = ]? + | \| [ | = ]? + | >{1,3} =? + | < = "a" && string <= "z\uffff") + || (string >= "A" && string <= "Z\uffff") + ); +} + +function supplant(string, object) { + return string.replace(rx_supplant, function (found, filling) { + const replacement = object[filling]; + return ( + replacement !== undefined + ? replacement + : found + ); + }); +} + +let anon; // The guessed name for anonymous functions. +let blockage; // The current block. +let block_stack; // The stack of blocks. +let declared_globals; // The object containing the global declarations. +let directives; // The directive comments. +let directive_mode; // true if directives are still allowed. +let early_stop; // true if JSLint cannot finish. +let exports; // The exported names and values. +let froms; // The array collecting all import-from strings. +let fudge; // true if the natural numbers start with 1. +let functionage; // The current function. +let functions; // The array containing all of the functions. +let global; // The global object; the outermost context. +let json_mode; // true if parsing JSON. +let lines; // The array containing source lines. +let mega_mode; // true if currently parsing a megastring literal. +let module_mode; // true if import or export was used. +let next_token; // The next token to be examined in the parse. +let option; // The options parameter. +let property; // The object containing the tallied property names. +let shebang; // true if a #! was seen on the first line. +let stack; // The stack of functions. +let syntax; // The object containing the parser. +let token; // The current token being examined in the parse. +let token_nr; // The number of the next token. +let tokens; // The array of tokens. +let tenure; // The predefined property registry. +let tree; // The abstract parse tree. +let var_mode; // "var" if using var; "let" if using let. +let warnings; // The array collecting all generated warnings. + +// Error reportage functions: + +function artifact(the_token) { + +// Return a string representing an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); +} + +function artifact_line(the_token) { + +// Return the fudged line number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.line + fudge; +} + +function artifact_column(the_token) { + +// Return the fudged column number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.from + fudge; +} + +function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + const warning = { // ~~ + code, + column, + line, + name: "JSLintError" + }; + if (a !== undefined) { + warning.a = a; + } + if (b !== undefined) { + warning.b = b; + } + if (c !== undefined) { + warning.c = c; + } + if (d !== undefined) { + warning.d = d; + } + warning.message = supplant(bundle[code] || code, warning); + warnings.push(warning); + return warning; +} + +function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); +} + +function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + if (the_token.warning === undefined) { + the_token.warning = warn_at( + code, + the_token.line, + the_token.from, + a || artifact(the_token), + b, + c, + d + ); + return the_token.warning; + } +} + +function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); +} + +// Tokenize: + +function tokenize(source) { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + +// If the source is not an array, then it is split into lines at the +// carriage return/linefeed. + + lines = ( + Array.isArray(source) + ? source + : source.split(rx_crlf) + ); + tokens = []; + + let char; // a popular character + let column = 0; // the column number of the next character + let first; // the first token + let from; // the starting column number of the token + let line = -1; // the line number of the next character + let nr = 0; // the next token number + let previous = global; // the previous token including comments + let prior = global; // the previous token excluding comments + let mega_from; // the starting column of megastring + let mega_line; // the starting line of megastring + let regexp_seen; // regular expression literal seen on this line + let snippet; // a piece of string + let source_line = ""; // the remaining line source string + let whole_line = ""; // the whole line source string + + if (lines[0].startsWith("#!")) { + line = 0; + shebang = true; + } + + function next_line() { + +// Put the next line of source in source_line. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + let at; + if ( + !option.long + && whole_line.length > 80 + && !json_mode + && first + && !regexp_seen + ) { + warn_at("too_long", line, 80); + } + column = 0; + line += 1; + regexp_seen = false; + source_line = lines[line]; + whole_line = source_line || ""; + if (source_line !== undefined) { + at = source_line.search(rx_tab); + if (at >= 0) { + if (!option.white) { + warn_at("use_spaces", line, at + 1); + } + source_line = source_line.replace(rx_tab, " "); + } + if (!option.white && source_line.slice(-1) === " ") { + warn_at( + "unexpected_trailing_space", + line, + source_line.length - 1 + ); + } + } + return source_line; + } + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions snip, next_char, back_char, +// some_digits, and escape help in the parsing of literals. + + function snip() { + +// Remove the last character from snippet. + + snippet = snippet.slice(0, -1); + } + + function next_char(match) { + +// Get the next character from the source line. Remove it from the source_line, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + return stop_at( + ( + char === "" + ? "expected_a" + : "expected_a_b" + ), + line, + column - 1, + match, + char + ); + } + if (source_line) { + char = source_line[0]; + source_line = source_line.slice(1); + snippet += char; + } else { + char = ""; + snippet += " "; + } + column += 1; + return char; + } + + function back_char() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the source_line. + + if (snippet) { + char = snippet.slice(-1); + source_line = char + source_line; + column -= 1; + snip(); + } else { + char = ""; + } + return char; + } + + function some_digits(rx, quiet) { + const digits = source_line.match(rx)[0]; + const length = digits.length; + if (!quiet && length === 0) { + warn_at("expected_digits_after_a", line, column, snippet); + } + column += length; + source_line = source_line.slice(length); + snippet += digits; + next_char(); + return length; + } + + function escape(extra) { + next_char("\\"); + if (escapeable[char] === true) { + return next_char(); + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "u") { + if (next_char("u") === "{") { + if (json_mode) { + warn_at("unexpected_a", line, column - 1, char); + } + if (some_digits(rx_hexs) > 5) { + warn_at("too_many_digits", line, column - 1); + } + if (next_char() !== "}") { + stop_at("expected_a_before_b", line, column, "}", char); + } + return next_char(); + } + back_char(); + if (some_digits(rx_hexs, true) < 4) { + warn_at("expected_four_digits", line, column - 1); + } + return; + } + if (extra && extra.indexOf(char) >= 0) { + return next_char(); + } + warn_at("unexpected_a_before_b", line, column - 2, "\\", char); + } + + function make(id, value, identifier) { + +// Make the token object and append it to the tokens list. + + const the_token = { + from, + id, + identifier: Boolean(identifier), + line, + nr, + thru: column + }; + tokens[nr] = the_token; + nr += 1; + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + directive_mode = false; + } + +// If the token is to have a value, give it one. + + if (value !== undefined) { + the_token.value = value; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option.white. + + if ( + previous.line === line + && previous.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (previous.id === "(comment)" || previous.id === "(regexp)") + ) { + warn( + "expected_space_a_b", + the_token, + artifact(previous), + artifact(the_token) + ); + } + if (previous.id === "." && id === "(number)") { + warn("expected_a_before_b", previous, "0", "."); + } + if (prior.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// The previous token is used to detect adjacency problems. + + previous = the_token; + +// The prior token is a previous token that was not a comment. The prior token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (previous.id !== "(comment)") { + prior = previous; + } + return the_token; + } + + function parse_directive(the_comment, body) { + +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + + const result = body.match(rx_directive_part); + if (result) { + let allowed; + const name = result[1]; + const value = result[2]; + if (the_comment.directive === "jslint") { + allowed = allowed_option[name]; + if ( + typeof allowed === "boolean" + || typeof allowed === "object" + ) { + if ( + value === "" + || value === "true" + || value === undefined + ) { + option[name] = true; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } else if (value === "false") { + option[name] = false; + } else { + warn("bad_option_a", the_comment, name + ":" + value); + } + } else { + warn("bad_option_a", the_comment, name); + } + } else if (the_comment.directive === "property") { + if (tenure === undefined) { + tenure = empty(); + } + tenure[name] = true; + } else if (the_comment.directive === "global") { + if (value) { + warn("bad_option_a", the_comment, name + ":" + value); + } + declared_globals[name] = false; + module_mode = the_comment; + } + return parse_directive(the_comment, result[3]); + } + if (body) { + return stop("bad_directive_a", the_comment, body); + } + } + + function comment(snippet) { + +// Make a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + + const the_comment = make("(comment)", snippet); + if (Array.isArray(snippet)) { + snippet = snippet.join(" "); + } + if (!option.devel && rx_todo.test(snippet)) { + warn("todo_comment", the_comment); + } + const result = snippet.match(rx_directive); + if (result) { + if (!directive_mode) { + warn_at("misplaced_directive_a", line, from, result[1]); + } else { + the_comment.directive = result[1]; + parse_directive(the_comment, result[2]); + } + directives.push(the_comment); + } + return the_comment; + } + + function regexp() { + +// Parse a regular expression literal. + + let multi_mode = false; + let result; + let value; + regexp_seen = true; + + function quantifier() { + +// Match an optional quantifier. + + if (char === "?" || char === "*" || char === "+") { + next_char(); + } else if (char === "{") { + if (some_digits(rx_digits, true) === 0) { + warn_at("expected_a_before_b", line, column, "0", ","); + } + if (char === ",") { + some_digits(rx_digits, true); + } + next_char("}"); + } else { + return; + } + if (char === "?") { + next_char("?"); + } + } + + function subklass() { + +// Match a character in a character class. + + if (char === "\\") { + escape("BbDdSsWw-[]^"); + return true; + } + if ( + char === "" + || char === "[" + || char === "]" + || char === "/" + || char === "^" + || char === "-" + ) { + return false; + } + if (char === " ") { + warn_at("expected_a_b", line, column, "\\u0020", " "); + } else if (char === "`" && mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char(); + return true; + } + + function ranges() { + +// Match a range of subclasses. + + if (subklass()) { + if (char === "-") { + next_char("-"); + if (!subklass()) { + return stop_at( + "unexpected_a", + line, + column - 1, + "-" + ); + } + } + return ranges(); + } + } + + function klass() { + +// Match a class. + + next_char("["); + if (char === "^") { + next_char("^"); + } + (function classy() { + ranges(); + if (char !== "]" && char !== "") { + warn_at( + "expected_a_before_b", + line, + column, + "\\", + char + ); + next_char(); + return classy(); + } + }()); + next_char("]"); + } + + function choice() { + + function group() { + +// Match a group that starts with left paren. + + next_char("("); + if (char === "?") { + next_char("?"); + if (char === "=" || char === "!") { + next_char(); + } else { + next_char(":"); + } + } else if (char === ":") { + warn_at("expected_a_before_b", line, column, "?", ":"); + } + choice(); + next_char(")"); + } + + function factor() { + if ( + char === "" + || char === "/" + || char === "]" + || char === ")" + ) { + return false; + } + if (char === "(") { + group(); + return true; + } + if (char === "[") { + klass(); + return true; + } + if (char === "\\") { + escape("BbDdSsWw^${}[]():=!.|*+?"); + return true; + } + if ( + char === "?" + || char === "+" + || char === "*" + || char === "}" + || char === "{" + ) { + warn_at( + "expected_a_before_b", + line, + column - 1, + "\\", + char + ); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column - 1, "`"); + } + } else if (char === " ") { + warn_at( + "expected_a_b", + line, + column - 1, + "\\s", + " " + ); + } else if (char === "$") { + if (source_line[0] !== "/") { + multi_mode = true; + } + } else if (char === "^") { + if (snippet !== "^") { + multi_mode = true; + } + } + next_char(); + return true; + } + + function sequence(follow) { + if (factor()) { + quantifier(); + return sequence(true); + } + if (!follow) { + warn_at("expected_regexp_factor_a", line, column, char); + } + } + +// Match a choice (a sequence that can be followed by | and another choice). + + sequence(); + if (char === "|") { + next_char("|"); + return choice(); + } + } + +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + next_char(); + if (char === "=") { + warn_at("expected_a_before_b", line, column, "\\", "="); + } + choice(); + +// Make sure there is a closing slash. + + snip(); + value = snippet; + next_char("/"); + +// Process dangling flag letters. + + const allowed = { + g: true, + i: true, + m: true, + u: true, + y: true + }; + const flag = empty(); + (function make_flag() { + if (is_letter(char)) { + if (allowed[char] !== true) { + warn_at("unexpected_a", line, column, char); + } + allowed[char] = false; + flag[char] = true; + next_char(); + return make_flag(); + } + }()); + back_char(); + if (char === "/" || char === "*") { + return stop_at("unexpected_a", line, from, char); + } + result = make("(regexp)", char); + result.flag = flag; + result.value = value; + if (multi_mode && !flag.m) { + warn_at("missing_m", line, column); + } + return result; + } + + function string(quote) { + +// Make a string token. + + let the_token; + snippet = ""; + next_char(); + + return (function next() { + if (char === quote) { + snip(); + the_token = make("(string)", snippet); + the_token.quote = quote; + return the_token; + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "\\") { + escape(quote); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char("`"); + } else { + next_char(); + } + return next(); + }()); + } + + function frack() { + if (char === ".") { + some_digits(rx_digits); + } + if (char === "E" || char === "e") { + next_char(); + if (char !== "+" && char !== "-") { + back_char(); + } + some_digits(rx_digits); + } + } + + function number() { + if (snippet === "0") { + next_char(); + if (char === ".") { + frack(); + } else if (char === "b") { + some_digits(rx_bits); + } else if (char === "o") { + some_digits(rx_octals); + } else if (char === "x") { + some_digits(rx_hexs); + } + } else { + next_char(); + frack(); + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + return stop_at( + "unexpected_a_after_b", + line, + column - 1, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + back_char(); + return make("(number)", snippet); + } + + function lex() { + let array; + let i = 0; + let j = 0; + let last; + let result; + let the_token; + +// This should properly be a tail recursive function, but sadly, conformant +// implementations of ES6 are still rare. This is the ideal code: + +// if (!source_line) { +// source_line = next_line(); +// from = 0; +// return ( +// source_line === undefined +// ? ( +// mega_mode +// ? stop_at("unclosed_mega", mega_line, mega_from) +// : make("(end)") +// ) +// : lex() +// ); +// } + +// Unfortunately, incompetent JavaScript engines will sometimes fail to execute +// it correctly. So for now, we do it the old fashioned way. + + while (!source_line) { + source_line = next_line(); + from = 0; + if (source_line === undefined) { + return ( + mega_mode + ? stop_at("unclosed_mega", mega_line, mega_from) + : make("(end)") + ); + } + } + + from = column; + result = source_line.match(rx_token); + +// result[1] token +// result[2] whitespace +// result[3] identifier +// result[4] number +// result[5] rest + + if (!result) { + return stop_at( + "unexpected_char_a", + line, + column, + source_line[0] + ); + } + + snippet = result[1]; + column += snippet.length; + source_line = result[5]; + +// Whitespace was matched. Call lex again to get more. + + if (result[2]) { + return lex(); + } + +// The token is an identifier. + + if (result[3]) { + return make(snippet, undefined, true); + } + +// The token is a number. + + if (result[4]) { + return number(snippet); + } + +// The token is a string. + + if (snippet === "\"") { + return string(snippet); + } + if (snippet === "'") { + if (!option.single) { + warn_at("use_double", line, column); + } + return string(snippet); + } + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (snippet === "`") { + if (mega_mode) { + return stop_at("expected_a_b", line, column, "}", "`"); + } + snippet = ""; + mega_from = from; + mega_line = line; + mega_mode = true; + +// Parsing a mega literal is tricky. First make a ` token. + + make("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + (function part() { + const at = source_line.search(rx_mega); + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + if (at < 0) { + snippet += source_line + "\n"; + return ( + next_line() === undefined + ? stop_at("unclosed_mega", mega_line, mega_from) + : part() + ); + } + snippet += source_line.slice(0, at); + column += at; + source_line = source_line.slice(at); + if (source_line[0] === "\\") { + snippet += source_line.slice(0, 2); + source_line = source_line.slice(2); + column += 2; + return part(); + } + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + make("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then make tokens that will become part of an expression until +// a } token is made. + + if (source_line[0] === "$") { + column += 2; + make("${"); + source_line = source_line.slice(2); + (function expr() { + const id = lex().id; + if (id === "{") { + return stop_at( + "expected_a_b", + line, + column, + "}", + "{" + ); + } + if (id !== "}") { + return expr(); + } + }()); + return part(); + } + }()); + source_line = source_line.slice(1); + column += 1; + mega_mode = false; + return make("`"); + } + +// The token is a // comment. + + if (snippet === "//") { + snippet = source_line; + source_line = ""; + the_token = comment(snippet); + if (mega_mode) { + warn("unexpected_comment", the_token, "`"); + } + return the_token; + } + +// The token is a /* comment. + + if (snippet === "/*") { + array = []; + if (source_line[0] === "/") { + warn_at("unexpected_a", line, column + i, "/"); + } + (function next() { + if (source_line > "") { + i = source_line.search(rx_star_slash); + if (i >= 0) { + return; + } + j = source_line.search(rx_slash_star); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + } + array.push(source_line); + source_line = next_line(); + if (source_line === undefined) { + return stop_at("unclosed_comment", line, column); + } + return next(); + }()); + snippet = source_line.slice(0, i); + j = snippet.search(rx_slash_star_or_slash); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + array.push(snippet); + column += i + 2; + source_line = source_line.slice(i + 2); + return comment(array); + } + +// The token is a slash. + + if (snippet === "/") { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + if (prior.identifier) { + if (!prior.dot) { + if (prior.id === "return") { + return regexp(); + } + if ( + prior.id === "(begin)" + || prior.id === "case" + || prior.id === "delete" + || prior.id === "in" + || prior.id === "instanceof" + || prior.id === "new" + || prior.id === "typeof" + || prior.id === "void" + || prior.id === "yield" + ) { + the_token = regexp(); + return stop("unexpected_a", the_token); + } + } + } else { + last = prior.id[prior.id.length - 1]; + if ("(,=:?[".indexOf(last) >= 0) { + return regexp(); + } + if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) { + the_token = regexp(); + warn("wrap_regexp", the_token); + return the_token; + } + } + if (source_line[0] === "=") { + column += 1; + source_line = source_line.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + } + return make(snippet); + } + + first = lex(); + json_mode = first.id === "{" || first.id === "["; + +// This loop will be replaced with a recursive call to lex when ES6 has been +// finished and widely deployed and adopted. + + while (true) { + if (lex().id === "(end)") { + break; + } + } +} + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + +function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!rx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!rx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property[id] === "number") { + property[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (tenure !== undefined) { + if (tenure[id] !== true) { + warn("unregistered_property_a", name); + } + } else { + if (name.identifier && rx_bad_property.test(id)) { + warn("bad_property_a", name); + } + } + property[id] = 1; + } + return id; +} + +function dispense() { + +// Deliver the next token, skipping the comments. + + const cadet = tokens[token_nr]; + token_nr += 1; + if (cadet.id === "(comment)") { + if (json_mode) { + warn("unexpected_a", cadet); + } + return dispense(); + } else { + return cadet; + } +} + +function lookahead() { + +// Look ahead one token without advancing. + + const old_token_nr = token_nr; + const cadet = dispense(true); + token_nr = old_token_nr; + return cadet; +} + +function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if (token.identifier && token.id !== "function") { + anon = token.id; + } else if (token.id === "(string)" && rx_identifier.test(token.value)) { + anon = token.value; + } + +// Attempt to match next_token with an expected id. + + if (id !== undefined && next_token.id !== id) { + return ( + match === undefined + ? stop("expected_a_b", next_token, id, artifact()) + : stop( + "expected_a_b_from_c_d", + next_token, + id, + artifact(match), + artifact_line(match), + artifact(next_token) + ) + ); + } + +// Promote the tokens, skipping comments. + + token = next_token; + next_token = dispense(); + if (next_token.id === "(end)") { + token_nr -= 1; + } +} + +// Parsing of JSON is simple: + +function json_value() { + let negative; + if (next_token.id === "{") { + return (function json_object() { + const brace = next_token; + const object = empty(); + const properties = []; + brace.expression = properties; + advance("{"); + if (next_token.id !== "}") { + (function next() { + let name; + let value; + if (next_token.quote !== "\"") { + warn( + "unexpected_a", + next_token, + next_token.quote + ); + } + name = next_token; + advance("(string)"); + if (object[token.value] !== undefined) { + warn("duplicate_a", token); + } else if (token.value === "__proto__") { + warn("bad_property_a", token); + } else { + object[token.value] = token; + } + advance(":"); + value = json_value(); + value.label = name; + properties.push(value); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("}", brace); + return brace; + }()); + } + if (next_token.id === "[") { + return (function json_array() { + const bracket = next_token; + const elements = []; + bracket.expression = elements; + advance("["); + if (next_token.id !== "]") { + (function next() { + elements.push(json_value()); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]", bracket); + return bracket; + }()); + } + if ( + next_token.id === "true" + || next_token.id === "false" + || next_token.id === "null" + ) { + advance(); + return token; + } + if (next_token.id === "(number)") { + if (!rx_JSON_number.test(next_token.value)) { + warn("unexpected_a"); + } + advance(); + return token; + } + if (next_token.id === "(string)") { + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + advance(); + return token; + } + if (next_token.id === "-") { + negative = next_token; + negative.arity = "unary"; + advance("-"); + advance("(number)"); + if (!rx_JSON_number.test(token.value)) { + warn("unexpected_a", token); + } + negative.expression = token; + return negative; + } + stop("unexpected_a"); +} + +// Now we parse JavaScript. + +function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + const id = name.id; + +// Reserved words may not be enrolled. + + if (syntax[id] !== undefined && id !== "ignore") { + warn("reserved_a", name); + } else { + +// Has the name been enrolled in this context? + + let earlier = functionage.context[id]; + if (earlier) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + +// Has the name been enrolled in an outer context? + + } else { + stack.forEach(function (value) { + const item = value.context[id]; + if (item !== undefined) { + earlier = item; + } + }); + if (earlier) { + if (id === "ignore") { + if (earlier.role === "variable") { + warn("unexpected_a", name); + } + } else { + if ( + ( + role !== "exception" + || earlier.role !== "exception" + ) + && role !== "parameter" + && role !== "function" + ) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + } + } + } + +// Enroll it. + + functionage.context[id] = name; + name.dead = true; + name.parent = functionage; + name.init = false; + name.role = role; + name.used = 0; + name.writable = !readonly; + } + } +} + +function expression(rbp, initial) { + +// This is the heart of the Pratt parser. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// nud Null denotation +// led Left denotation +// lbp Left binding power +// rbp Right binding power + +// It processes a nud (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop. It +// returns the expression's parse tree. + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement, + + if (!initial) { + advance(); + } + the_symbol = syntax[token.id]; + if (the_symbol !== undefined && the_symbol.nud !== undefined) { + left = the_symbol.nud(); + } else if (token.identifier) { + left = token; + left.arity = "variable"; + } else { + return stop("unexpected_a", token); + } + (function right() { + the_symbol = syntax[next_token.id]; + if ( + the_symbol !== undefined + && the_symbol.led !== undefined + && rbp < the_symbol.lbp + ) { + advance(); + left = the_symbol.led(left); + return right(); + } + }()); + return left; +} + +function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = next_token; + let the_value; + the_paren.free = true; + advance("("); + the_value = expression(0); + advance(")"); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (anticondition[the_value.id] === true) { + warn("unexpected_a", the_value); + } + return the_value; +} + +function is_weird(thing) { + return ( + thing.id === "(regexp)" + || thing.id === "{" + || thing.id === "=>" + || thing.id === "function" + || (thing.id === "[" && thing.arity === "unary") + ); +} + +function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + return ( + Array.isArray(b) + && a.length === b.length + && a.every(function (value, index) { + return are_similar(value, b[index]); + }) + ); + } + if (Array.isArray(b)) { + return false; + } + if (a.id === "(number)" && b.id === "(number)") { + return a.value === b.value; + } + let a_string; + let b_string; + if (a.id === "(string)") { + a_string = a.value; + } else if (a.id === "`" && a.constant) { + a_string = a.value[0]; + } + if (b.id === "(string)") { + b_string = b.value; + } else if (b.id === "`" && b.constant) { + b_string = b.value[0]; + } + if (typeof a_string === "string") { + return a_string === b_string; + } + if (is_weird(a) || is_weird(b)) { + return false; + } + if (a.arity === b.arity && a.id === b.id) { + if (a.id === ".") { + return ( + are_similar(a.expression, b.expression) + && are_similar(a.name, b.name) + ); + } + if (a.arity === "unary") { + return are_similar(a.expression, b.expression); + } + if (a.arity === "binary") { + return ( + a.id !== "(" + && are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + ); + } + if (a.arity === "ternary") { + return ( + are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + && are_similar(a.expression[2], b.expression[2]) + ); + } + if (a.arity === "function" && a.arity === "regexp") { + return false; + } + return true; + } + return false; +} + +function semicolon() { + +// Try to match a semicolon. + + if (next_token.id === ";") { + advance(";"); + } else { + warn_at( + "expected_a_b", + token.line, + token.thru, + ";", + artifact(next_token) + ); + } + anon = "anonymous"; +} + +function statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token.identifier && next_token.id === ":") { + the_label = token; + if (the_label.id === "ignore") { + warn("unexpected_a", the_label); + } + advance(":"); + if ( + next_token.id === "do" + || next_token.id === "for" + || next_token.id === "switch" + || next_token.id === "while" + ) { + enroll(the_label, "label", true); + the_label.init = true; + the_label.dead = false; + the_statement = statement(); + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token; + first.statement = true; + the_symbol = syntax[first.id]; + if (the_symbol !== undefined && the_symbol.fud !== undefined) { + the_symbol.disrupt = false; + the_symbol.statement = true; + the_statement = the_symbol.fud(); + } else { + +// It is an expression statement. + + the_statement = expression(0, true); + if (the_statement.wrapped && the_statement.id !== "(") { + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; +} + +function statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const array = []; + (function next(disrupt) { + if ( + next_token.id !== "}" + && next_token.id !== "case" + && next_token.id !== "default" + && next_token.id !== "else" + && next_token.id !== "(end)" + ) { + let a_statement = statement(); + array.push(a_statement); + if (disrupt) { + warn("unreachable_a", a_statement); + } + return next(a_statement.disrupt); + } + }(false)); + return array; +} + +function not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === global) { + warn("unexpected_at_top_level_a", thing); + } +} + +function top_level_only(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== global) { + warn("misplaced_a", the_thing); + } +} + +function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token; + the_block.arity = "statement"; + the_block.body = special === "body"; + +// Top level function bodies may include the "use strict" pragma. + + if ( + special === "body" + && stack.length === 1 + && next_token.value === "use strict" + ) { + next_token.statement = true; + advance("(string)"); + advance(";"); + } + stmts = statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option.devel && special !== "ignore") { + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; +} + +function mutation_check(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + warn("bad_assignment_a", the_thing); + return false; + } + return true; +} + +function left_check(left, right) { + +// Warn if the left is not one of these: +// e.b +// e[b] +// e() +// ?: +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !left_check(left.expression[1]) + && !left_check(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right); + return false; + } + return true; +} + +// These functions are used to specify the grammar of our language: + +function symbol(id, bp) { + +// Make a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax[id] = the_symbol; + } + return the_symbol; +} + +function assignment(id) { + +// Make an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led = function (left) { + const the_token = token; + let right; + the_token.arity = "assignment"; + right = expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "pre" + || right.arity === "post" + ) { + warn("unexpected_a", right); + } + mutation_check(left); + return the_token; + }; + return the_symbol; +} + +function constant(id, type, value) { + +// Make a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud = ( + typeof value === "function" + ? value + : function () { + token.constant = true; + if (value !== undefined) { + token.value = value; + } + return token; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; +} + +function infix(id, bp, f) { + +// Make an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, expression(bp)]; + return the_token; + }; + return the_symbol; +} + +function infixr(id, bp) { + +// Make a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + the_token.expression = [left, expression(bp - 1)]; + return the_token; + }; + return the_symbol; +} + +function post(id) { + +// Make one of the post operators. + + const the_symbol = symbol(id, 150); + the_symbol.led = function (left) { + token.expression = left; + token.arity = "post"; + mutation_check(token.expression); + return token; + }; + return the_symbol; +} + +function pre(id) { + +// Make one of the pre operators. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "pre"; + the_token.expression = expression(150); + mutation_check(the_token.expression); + return the_token; + }; + return the_symbol; +} + +function prefix(id, f) { + +// Make a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = expression(150); + return the_token; + }; + return the_symbol; +} + +function stmt(id, f) { + +// Make a statement. + + const the_symbol = symbol(id); + the_symbol.fud = function () { + token.arity = "statement"; + return f(); + }; + return the_symbol; +} + +function ternary(id1, id2) { + +// Make a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led = function (left) { + const the_token = token; + const second = expression(20); + advance(id2); + token.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, expression(10)]; + if (next_token.id !== ")") { + warn("use_open", the_token); + } + return the_token; + }; + return the_symbol; +} + +// Begin defining the language. + +syntax = empty(); + +symbol("}"); +symbol(")"); +symbol("]"); +symbol(","); +symbol(";"); +symbol(":"); +symbol("*/"); +symbol("await"); +symbol("case"); +symbol("catch"); +symbol("class"); +symbol("default"); +symbol("else"); +symbol("enum"); +symbol("finally"); +symbol("implements"); +symbol("interface"); +symbol("package"); +symbol("private"); +symbol("protected"); +symbol("public"); +symbol("static"); +symbol("super"); +symbol("void"); +symbol("yield"); + +constant("(number)", "number"); +constant("(regexp)", "regexp"); +constant("(string)", "string"); +constant("arguments", "object", function () { + warn("unexpected_a", token); + return token; +}); +constant("eval", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("false", "boolean", false); +constant("Function", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("ignore", "undefined", function () { + warn("unexpected_a", token); + return token; +}); +constant("Infinity", "number", Infinity); +constant("isFinite", "function", function () { + warn("expected_a_b", token, "Number.isFinite", "isFinite"); + return token; +}); +constant("isNaN", "function", function () { + warn("number_isNaN", token); + return token; +}); +constant("NaN", "number", NaN); +constant("null", "null", null); +constant("this", "object", function () { + if (!option.this) { + warn("unexpected_a", token); + } + return token; +}); +constant("true", "boolean", true); +constant("undefined", "undefined"); + +assignment("="); +assignment("+="); +assignment("-="); +assignment("*="); +assignment("/="); +assignment("%="); +assignment("&="); +assignment("|="); +assignment("^="); +assignment("<<="); +assignment(">>="); +assignment(">>>="); + +infix("??", 35); +infix("||", 40); +infix("&&", 50); +infix("|", 70); +infix("^", 80); +infix("&", 90); +infix("==", 100); +infix("===", 100); +infix("!=", 100); +infix("!==", 100); +infix("<", 110); +infix(">", 110); +infix("<=", 110); +infix(">=", 110); +infix("in", 110); +infix("instanceof", 110); +infix("<<", 120); +infix(">>", 120); +infix(">>>", 120); +infix("+", 130); +infix("-", 130); +infix("*", 140); +infix("/", 140); +infix("%", 140); +infixr("**", 150); +infix("(", 160, function (left) { + const the_paren = token; + let the_argument; + if (left.id !== "function") { + left_check(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (next_token.id !== ")") { + (function next() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + the_paren.free = true; + if (the_argument.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + the_paren.free = false; + } + return the_paren; +}); +infix(".", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("?.", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("[", 170, function (left) { + const the_token = token; + const the_subscript = expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + const name = survey(the_subscript); + if (rx_identifier.test(name)) { + warn("subscript_a", the_subscript, name); + } + } + left_check(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; +}); +infix("=>", 170, function (left) { + return stop("wrap_parameter", left); +}); + +function do_tick() { + const the_tick = token; + the_tick.value = []; + the_tick.expression = []; + if (next_token.id !== "`") { + (function part() { + advance("(string)"); + the_tick.value.push(token); + if (next_token.id === "${") { + advance("${"); + the_tick.expression.push(expression(0)); + advance("}"); + return part(); + } + }()); + } + advance("`"); + return the_tick; +} + +infix("`", 160, function (left) { + const the_tick = do_tick(); + left_check(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; +}); + +post("++"); +post("--"); +pre("++"); +pre("--"); + +prefix("+"); +prefix("-"); +prefix("~"); +prefix("!"); +prefix("!!"); +prefix("[", function () { + const the_token = token; + the_token.expression = []; + if (next_token.id !== "]") { + (function next() { + let element; + let ellipsis = false; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + element = expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]"); + return the_token; +}); +prefix("/=", function () { + stop("expected_a_b", token, "/\\=", "/="); +}); +prefix("=>", function () { + return stop("expected_a_before_b", token, "()", "=>"); +}); +prefix("new", function () { + const the_new = token; + const right = expression(160); + if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "()", artifact(next_token)); + } + the_new.expression = right; + return the_new; +}); +prefix("typeof"); +prefix("void", function () { + const the_void = token; + warn("unexpected_a", the_void); + the_void.expression = expression(0); + return the_void; +}); + +function parameter_list() { + const list = []; + let optional; + const signature = ["("]; + if (next_token.id !== ")" && next_token.id !== "(end)") { + (function parameter() { + let ellipsis = false; + let param; + if (next_token.id === "{") { + let a; + let b = ""; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("{"); + signature.push("{"); + (function subparameter() { + let subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + survey(subparam); + a = b; + b = String(subparam.value || subparam.id); + if (a > b) { + warn("unordered_param_a", subparam); + } + advance(); + signature.push(subparam.id); + if (next_token.id === ":") { + advance(":"); + advance(); + token.label = subparam; + subparam = token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + } + if (next_token.id === "=") { + advance("="); + subparam.expression = expression(); + param.open = true; + } + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return subparameter(); + } + }()); + list.push(param); + advance("}"); + signature.push("}"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else if (next_token.id === "[") { + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("["); + signature.push("[]"); + (function subparameter() { + const subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + if (next_token.id === "=") { + advance("="); + subparam.expression = expression(); + param.open = true; + } + if (next_token.id === ",") { + advance(","); + return subparameter(); + } + }()); + list.push(param); + advance("]"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else { + if (next_token.id === "...") { + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + } + if (!next_token.identifier) { + return stop("expected_identifier_a"); + } + param = next_token; + list.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (next_token.id === "=") { + optional = param; + advance("="); + param.expression = expression(0); + } else { + if (optional !== undefined) { + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } + } + }()); + } + advance(")"); + signature.push(")"); + return [list, signature.join("")]; +} + +function do_function(the_function) { + let name; + if (the_function === undefined) { + the_function = token; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + name = next_token; + enroll(name, "variable", true); + the_function.name = name; + name.init = true; + name.calls = empty(); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + if (next_token.identifier) { + name = next_token; + the_function.name = name; + advance(); + } else { + the_function.name = anon; + } + } + } else { + name = the_function.name; + } + the_function.level = functionage.level + 1; + if (mega_mode) { + warn("unexpected_a", the_function); + } + +// Don't make functions in loops. It is inefficient, and it can lead to scoping +// errors. + + if (functionage.loop > 0) { + warn("function_in_loop", the_function); + } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + the_function.context = empty(); + the_function.finally = 0; + the_function.loop = 0; + the_function.switch = 0; + the_function.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functions.push(the_function); + functionage = the_function; + if (the_function.arity !== "statement" && typeof name === "object") { + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// Parse the parameter list. + + advance("("); + token.free = false; + token.arity = "function"; + [functionage.parameters, functionage.signature] = parameter_list(); + functionage.parameters.forEach(function enroll_parameter(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + name.names.forEach(enroll_parameter); + } + }); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && next_token.line === token.line + ) { + return stop("unexpected_a", next_token); + } + if ( + next_token.id === "." + || next_token.id === "?." + || next_token.id === "[" + ) { + warn("unexpected_a"); + } + +// Restore the previous context. + + functionage = stack.pop(); + return the_function; +} + +prefix("function", do_function); + +function fart(pl) { + advance("=>"); + const the_fart = token; + the_fart.arity = "binary"; + the_fart.name = "=>"; + the_fart.level = functionage.level + 1; + functions.push(the_fart); + if (functionage.loop > 0) { + warn("function_in_loop", the_fart); + } + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + the_fart.context = empty(); + the_fart.finally = 0; + the_fart.loop = 0; + the_fart.switch = 0; + the_fart.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functionage = the_fart; + the_fart.parameters = pl[0]; + the_fart.signature = pl[1]; + the_fart.parameters.forEach(function (name) { + enroll(name, "parameter", true); + }); + if (next_token.id === "{") { + warn("expected_a_b", the_fart, "function", "=>"); + the_fart.block = block("body"); + } else { + the_fart.expression = expression(0); + } + functionage = stack.pop(); + return the_fart; +} + +prefix("(", function () { + const the_paren = token; + let the_value; + const cadet = lookahead().id; + +// We can distinguish between a parameter list for => and a wrapped expression +// with one token of lookahead. + + if ( + next_token.id === ")" + || next_token.id === "..." + || (next_token.identifier && (cadet === "," || cadet === "=")) + ) { + the_paren.free = false; + return fart(parameter_list()); + } + the_paren.free = true; + the_value = expression(0); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + if (next_token.id === "=>") { + if (the_value.arity !== "variable") { + if (the_value.id === "{" || the_value.id === "[") { + warn("expected_a_before_b", the_paren, "function", "("); + return stop("expected_a_b", next_token, "{", "=>"); + } + return stop("expected_identifier_a", the_value); + } + the_paren.expression = [the_value]; + return fart([the_paren.expression, "(" + the_value.id + ")"]); + } + return the_value; +}); +prefix("`", do_tick); +prefix("{", function () { + let a; + let b = ""; + const the_brace = token; + const seen = empty(); + the_brace.expression = []; + if (next_token.id !== "}") { + (function member() { + let extra; + let full; + let id; + let name = next_token; + let value; + a = b; + b = String(name.value || name.id); + if (a > b) { + warn("unordered_property_a", name); + } + advance(); + if ( + (name.id === "get" || name.id === "set") + && next_token.identifier + ) { + if (!option.getset) { + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + next_token.id; + name = next_token; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (next_token.id === "}" || next_token.id === ",") { + if (typeof extra === "string") { + advance("("); + } + value = expression(Infinity, true); + } else if (next_token.id === "(") { + value = do_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + advance("("); + } + let the_colon = next_token; + advance(":"); + value = expression(0); + if (value.id === name.id && value.id !== "function") { + warn("unexpected_a", the_colon, ": " + name.id); + } + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + advance(":"); + value = expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (next_token.id === ",") { + advance(","); + return member(); + } + }()); + } + advance("}"); + return the_brace; +}); + +stmt(";", function () { + warn("unexpected_a", token); + return token; +}); +stmt("{", function () { + warn("naked_block", token); + return block("naked"); +}); +stmt("break", function () { + const the_break = token; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (next_token.identifier && token.line === next_token.line) { + the_label = functionage.context[next_token.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + warn( + (the_label !== undefined && the_label.dead) + ? "out_of_scope_a" + : "not_label_a" + ); + } else { + the_label.used += 1; + } + the_break.label = next_token; + advance(); + } + advance(";"); + return the_break; +}); + +function do_var() { + const the_statement = token; + const is_const = the_statement.id === "const"; + the_statement.names = []; + +// A program may use var or let, but not both. + + if (!is_const) { + if (var_mode === undefined) { + var_mode = the_statement.id; + } else if (the_statement.id !== var_mode) { + warn( + "expected_a_b", + the_statement, + var_mode, + the_statement.id + ); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + warn("var_switch", the_statement); + } + if (functionage.loop > 0 && the_statement.id === "var") { + warn("var_loop", the_statement); + } + (function next() { + if (next_token.id === "{" && the_statement.id !== "var") { + let a; + let b = ""; + const the_brace = next_token; + advance("{"); + (function pair() { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + survey(name); + a = b; + b = String(name.value || name.id); + if (a > b) { + warn("unordered_param_a", name); + } + advance(); + if (next_token.id === ":") { + advance(":"); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + next_token.label = name; + the_statement.names.push(next_token); + enroll(next_token, "variable", is_const); + advance(); + the_brace.open = true; + } else { + the_statement.names.push(name); + enroll(name, "variable", is_const); + } + name.dead = false; + name.init = true; + if (next_token.id === "=") { + advance("="); + name.expression = expression(); + the_brace.open = true; + } + if (next_token.id === ",") { + advance(","); + return pair(); + } + }()); + advance("}"); + advance("="); + the_statement.expression = expression(0); + } else if (next_token.id === "[" && the_statement.id !== "var") { + const the_bracket = next_token; + advance("["); + (function element() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + advance(); + the_statement.names.push(name); + enroll(name, "variable", is_const); + name.dead = false; + name.init = true; + if (ellipsis) { + name.ellipsis = true; + } else { + if (next_token.id === "=") { + advance("="); + name.expression = expression(); + the_bracket.open = true; + } + if (next_token.id === ",") { + advance(","); + return element(); + } + } + }()); + advance("]"); + advance("="); + the_statement.expression = expression(0); + } else if (next_token.identifier) { + const name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", is_const); + if (next_token.id === "=" || is_const) { + advance("="); + name.dead = false; + name.init = true; + name.expression = expression(0); + } + the_statement.names.push(name); + } else { + return stop("expected_identifier_a", next_token); + } + }()); + semicolon(); + return the_statement; +} + +stmt("const", do_var); +stmt("continue", function () { + const the_continue = token; + if (functionage.loop < 1 || functionage.finally > 0) { + warn("unexpected_a", the_continue); + } + not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; +}); +stmt("debugger", function () { + const the_debug = token; + if (!option.devel) { + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; +}); +stmt("delete", function () { + const the_token = token; + const the_value = expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; +}); +stmt("do", function () { + const the_do = token; + not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; +}); +stmt("export", function () { + const the_export = token; + let the_id; + let the_name; + let the_thing; + + function export_id() { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + the_id = next_token.id; + the_name = global.context[the_id]; + if (the_name === undefined) { + warn("unexpected_a"); + } else { + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a"); + } + exports[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + } + + the_export.expression = []; + if (next_token.id === "default") { + if (exports.default !== undefined) { + warn("duplicate_a"); + } + advance("default"); + the_thing = expression(0); + if ( + the_thing.id !== "(" + || the_thing.expression[0].id !== "." + || the_thing.expression[0].expression.id !== "Object" + || the_thing.expression[0].name.id !== "freeze" + ) { + warn("freeze_exports", the_thing); + } + if (next_token.id === ";") { + semicolon(); + } + exports.default = the_thing; + the_export.expression.push(the_thing); + } else { + if (next_token.id === "function") { + warn("freeze_exports"); + the_thing = statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a", the_name); + } + exports[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + warn("unexpected_a", next_token); + statement(); + } else if (next_token.id === "{") { + advance("{"); + (function loop() { + export_id(); + if (next_token.id === ",") { + advance(","); + return loop(); + } + }()); + advance("}"); + semicolon(); + } else { + stop("unexpected_a"); + } + } + module_mode = true; + return the_export; +}); +stmt("for", function () { + let first; + const the_for = token; + if (!option.for) { + warn("unexpected_a", the_for); + } + not_top_level(the_for); + functionage.loop += 1; + advance("("); + token.free = true; + if (next_token.id === ";") { + return stop("expected_a_b", the_for, "while (", "for (;"); + } + if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + return stop("unexpected_a"); + } + first = expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = expression(0); + advance(";"); + the_for.inc = expression(0); + if (the_for.inc.id === "++") { + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; +}); +stmt("function", do_function); +stmt("if", function () { + let the_else; + const the_if = token; + the_if.expression = condition(); + the_if.block = block(); + if (next_token.id === "else") { + advance("else"); + the_else = token; + the_if.else = ( + next_token.id === "if" + ? statement() + : block() + ); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + the_if.disrupt = true; + } else { + warn("unexpected_a", the_else); + } + } + } + return the_if; +}); +stmt("import", function () { + const the_import = token; + if (next_token.id === "(") { + the_import.arity = "unary"; + the_import.constant = true; + the_import.statement = false; + advance("("); + const string = expression(0); + if (string.id !== "(string)") { + warn("expected_string_a", string); + } + froms.push(token.value); + advance(")"); + advance("."); + advance("then"); + advance("("); + the_import.expression = expression(0); + advance(")"); + semicolon(); + return the_import; + } + let name; + if (typeof module_mode === "object") { + warn("unexpected_directive_a", module_mode, module_mode.directive); + } + module_mode = true; + if (next_token.identifier) { + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + const names = []; + advance("{"); + if (next_token.id !== "}") { + while (true) { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (next_token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token; + if (!rx_module.test(token.value)) { + warn("bad_module_name_a", token); + } + froms.push(token.value); + semicolon(); + return the_import; +}); +stmt("let", do_var); +stmt("return", function () { + const the_return = token; + not_top_level(the_return); + if (functionage.finally > 0) { + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (next_token.id !== ";" && the_return.line === next_token.line) { + the_return.expression = expression(10); + } + advance(";"); + return the_return; +}); +stmt("switch", function () { + let dups = []; + let last; + let stmts; + const the_cases = []; + let the_disrupt = true; + const the_switch = token; + not_top_level(the_switch); + if (functionage.finally > 0) { + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token.free = true; + the_switch.expression = expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + (function major() { + const the_case = next_token; + the_case.arity = "statement"; + the_case.expression = []; + (function minor() { + advance("case"); + token.switch = true; + const exp = expression(0); + if (dups.some(function (thing) { + return are_similar(thing, exp); + })) { + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (next_token.id === "case") { + return minor(); + } + }()); + stmts = statements(); + if (stmts.length < 1) { + warn("expected_statements_a"); + return; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "break;", + artifact(next_token) + ); + } + if (next_token.id === "case") { + return major(); + } + }()); + dups = undefined; + if (next_token.id === "default") { + const the_default = next_token; + advance("default"); + token.switch = true; + advance(":"); + the_switch.else = statements(); + if (the_switch.else.length < 1) { + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + const the_last = the_switch.else[the_switch.else.length - 1]; + if (the_last.id === "break" && the_last.label === undefined) { + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; +}); +stmt("throw", function () { + const the_throw = token; + the_throw.disrupt = true; + the_throw.expression = expression(10); + semicolon(); + if (functionage.try > 0) { + warn("unexpected_a", the_throw); + } + return the_throw; +}); +stmt("try", function () { + let the_catch; + let the_disrupt; + const the_try = token; + if (functionage.try > 0) { + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (next_token.id === "catch") { + let ignored = "ignore"; + the_catch = next_token; + the_try.catch = the_catch; + advance("catch"); + if (next_token.id === "(") { + advance("("); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + if (next_token.id !== "ignore") { + ignored = undefined; + the_catch.name = next_token; + enroll(next_token, "exception", true); + } + advance(); + advance(")"); + } + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "catch", + artifact(next_token) + ); + + } + if (next_token.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; +}); +stmt("var", do_var); +stmt("while", function () { + const the_while = token; + not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; +}); +stmt("with", function () { + stop("unexpected_a", token); +}); + +ternary("?", ":"); + +// Ambulation of the parse tree. + +function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; +} + +function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all of the +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + } + }; +} + +const posts = empty(); +const pres = empty(); +const preaction = action(pres); +const postaction = action(posts); +const preamble = amble(pres); +const postamble = amble(posts); + +function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.id === "function") { + walk_statement(thing.block); + } + if (thing.arity === "pre" || thing.arity === "post") { + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + warn("unexpected_statement_a", thing); + } + postamble(thing); + } + } +} + +function walk_statement(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_statement); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + && thing.id !== "import" + ) { + warn("unexpected_expression_a", thing); + } + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + } +} + +function lookup(thing) { + if (thing.arity === "variable") { + +// Look up the variable in the current context. + + let the_variable = functionage.context[thing.id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable === undefined) { + stack.forEach(function (outer) { + const a_variable = outer.context[thing.id]; + if ( + a_variable !== undefined + && a_variable.role !== "label" + ) { + the_variable = a_variable; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (the_variable === undefined) { + if (declared_globals[thing.id] === undefined) { + warn("undeclared_a", thing); + return; + } + the_variable = { + dead: false, + id: thing.id, + init: true, + parent: global, + role: "variable", + used: 0, + writable: false + }; + global.context[thing.id] = the_variable; + } + the_variable.closure = true; + functionage.context[thing.id] = the_variable; + } else if (the_variable.role === "label") { + warn("label_a", thing); + } + if ( + the_variable.dead + && ( + the_variable.calls === undefined + || functionage.name === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + ) { + warn("out_of_scope_a", thing); + } + return the_variable; + } +} + +function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); +} + +function preaction_function(thing) { + if (thing.arity === "statement" && blockage.body !== true) { + warn("unexpected_a", thing); + } + stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); +} + +function bitwise_check(thing) { + if (!option.bitwise && bitwiseop[thing.id] === true) { + warn("unexpected_a", thing); + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + warn("unexpected_a", thing); + } +} + +function pop_block() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); +} + +function activate(name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.init = true; + } + } + blockage.live.push(name); +} + +function action_var(thing) { + thing.names.forEach(activate); +} + +preaction("assignment", bitwise_check); +preaction("binary", bitwise_check); +preaction("binary", function (thing) { + if (relationop[thing.id] === true) { + const left = thing.expression[0]; + const right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + warn("expected_string_a", right); + } + } else { + const value = right.value; + if (value === "null" || value === "undefined") { + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + warn("expected_type_string_a", right, value); + } + } + } + } +}); +preaction("binary", "==", function (thing) { + warn("expected_a_b", thing, "===", "=="); +}); +preaction("binary", "!=", function (thing) { + warn("expected_a_b", thing, "!==", "!="); +}); +preaction("binary", "=>", preaction_function); +preaction("binary", "||", function (thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + warn("and", thang); + } + }); +}); +preaction("binary", "(", function (thing) { + const left = thing.expression[0]; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + const parent = functionage.name.parent; + if (parent) { + const left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + && left_variable.dead + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } +}); +preaction("binary", "in", function (thing) { + warn("infix_in", thing); +}); +preaction("binary", "instanceof", function (thing) { + warn("unexpected_a", thing); +}); +preaction("binary", ".", function (thing) { + if (thing.expression.new) { + thing.new = true; + } +}); +preaction("statement", "{", function (thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; +}); +preaction("statement", "for", function (thing) { + if (thing.name !== undefined) { + const the_variable = lookup(thing.name); + if (the_variable !== undefined) { + the_variable.init = true; + if (!the_variable.writable) { + warn("bad_assignment_a", thing.name); + } + } + } + walk_statement(thing.initial); +}); +preaction("statement", "function", preaction_function); +preaction("unary", "~", bitwise_check); +preaction("unary", "function", preaction_function); +preaction("variable", function (thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } +}); + +function init_variable(name) { + const the_variable = lookup(name); + if (the_variable !== undefined) { + if (the_variable.writable) { + the_variable.init = true; + return; + } + } + warn("bad_assignment_a", name); +} + +postaction("assignment", "+=", function (thing) { + let right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } +}); +postaction("assignment", function (thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + if (thing.id === "=") { + if (thing.names !== undefined) { + if (Array.isArray(thing.names)) { + thing.names.forEach(init_variable); + } else { + init_variable(thing.names); + } + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.writable !== true) { + warn("bad_assignment_a", lvalue); + } + } + const right = syntax[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + warn("unexpected_a", thing.expression[1]); + } + } +}); + +function postaction_function(thing) { + delete functionage.finally; + delete functionage.loop; + delete functionage.switch; + delete functionage.try; + functionage = stack.pop(); + if (thing.wrapped) { + warn("unexpected_parens", thing); + } + return pop_block(); +} + +postaction("binary", function (thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || are_similar(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option.convert) { + if (thing.expression[0].value === "") { + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === "." || thing.id === "?.") { + if (thing.expression.id === "RegExp") { + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } +}); +postaction("binary", "&&", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "||", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "=>", postaction_function); +postaction("binary", "(", function (thing) { + let left = thing.expression[0]; + let the_new; + let arg; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option.eval) { + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + warn( + "expected_a_before_b", + left, + "new", + artifact(left) + ); + } + } + } else if (left.id === ".") { + let cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + cack = !cack; + } + if (rx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + warn("unexpected_a", the_new); + } else { + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + const paren = left.expression; + if (paren.id === "(") { + const array = paren.expression; + if (array.length === 1) { + const new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } +}); +postaction("binary", "[", function (thing) { + if (thing.expression[0].id === "RegExp") { + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + warn("weird_expression_a", thing.expression[1]); + } +}); +postaction("statement", "{", pop_block); +postaction("statement", "const", action_var); +postaction("statement", "export", top_level_only); +postaction("statement", "for", function (thing) { + walk_statement(thing.inc); +}); +postaction("statement", "function", postaction_function); +postaction("statement", "import", function (the_thing) { + const name = the_thing.name; + if (name) { + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return top_level_only(the_thing); + } +}); +postaction("statement", "let", action_var); +postaction("statement", "try", function (thing) { + if (thing.catch !== undefined) { + const the_name = thing.catch.name; + if (the_name !== undefined) { + const the_variable = functionage.context[the_name.id]; + the_variable.dead = false; + the_variable.init = true; + } + walk_statement(thing.catch.block); + } +}); +postaction("statement", "var", action_var); +postaction("ternary", function (thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || are_similar(thing.expression[1], thing.expression[2]) + ) { + warn("unexpected_a", thing); + } else if (are_similar(thing.expression[0], thing.expression[1])) { + warn("expected_a_b", thing, "||", "?"); + } else if (are_similar(thing.expression[0], thing.expression[2])) { + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + warn("wrap_condition", thing.expression[0]); + } +}); +postaction("unary", function (thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option.convert) { + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } +}); +postaction("unary", "function", postaction_function); +postaction("unary", "+", function (thing) { + if (!option.convert) { + warn("expected_a_b", thing, "Number(...)", "+"); + } + const right = thing.expression; + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } +}); + +function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + if (id !== "ignore") { + const name = the_function.context[id]; + if (name.parent === the_function) { + if ( + name.used === 0 + && ( + name.role !== "function" + || name.parent.arity !== "unary" + ) + ) { + warn("unused_a", name); + } else if (!name.init) { + warn("uninitialized_a", name); + } + } + } + }); +} + +function uninitialized_and_unused() { + +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (module_mode === true || option.node) { + delve(global); + } + functions.forEach(delve); +} + +// Go through the token list, looking at usage of whitespace. + +function whitage() { + let closer = "(end)"; + let free = false; + let left = global; + let margin = 0; + let nr_comments_skipped = 0; + let open = true; + let opening = true; + let right; + + function pop() { + const previous = stack.pop(); + closer = previous.closer; + free = previous.free; + margin = previous.margin; + open = previous.open; + opening = previous.opening; + } + + function push() { + stack.push({ + closer, + free, + margin, + open, + opening + }); + } + + function expected_at(at) { + warn( + "expected_a_at_b_c", + right, + artifact(right), + fudge + at, + artifact_column(right) + ); + } + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function no_space() { + if (left.line === right.line) { + if (left.thru !== right.from && nr_comments_skipped === 0) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (open) { + const at = ( + free + ? margin + : margin + 8 + ); + if (right.from < at) { + expected_at(at); + } + } else { + if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line || !open) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (right.from !== margin) { + expected_at(margin); + } + } + } + + stack = []; + tokens.forEach(function (the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + + const new_closer = opener[left.id]; + if (typeof new_closer === "string") { + if (new_closer !== right.id) { + opening = left.open || (left.line !== right.line); + push(); + closer = new_closer; + if (opening) { + free = closer === ")" && left.free; + open = true; + margin += 4; + if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (right.switch) { + at_margin(-4); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + free = false; + open = false; + no_space_only(); + } + } else { + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like '{]') have already been detected. + + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } + } else { + if (right.statement === true) { + if (left.id === "else") { + one_space_only(); + } else { + at_margin(0); + open = false; + } + +// If right is a closer, then pop the previous state. + + } else if (right.id === closer) { + pop(); + if (opening && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-4); + } else if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + one_space(); + } else { + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + at_margin(0); + } else { + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + no_space(); + } else if ( + left.id === "." + || left.id === "?." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + ) { + no_space_only(); + } else if (right.id === "." || right.id === "?.") { + no_space_only(); + } else if (left.id === ";") { + if (open) { + at_margin(0); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + one_space_only(); + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +// The jslint function itself. + +export default Object.freeze(function jslint( + source = "", + option_object = empty(), + global_array = [] +) { + try { + warnings = []; + option = Object.assign(empty(), option_object); + anon = "anonymous"; + block_stack = []; + declared_globals = empty(); + directive_mode = true; + directives = []; + early_stop = true; + exports = empty(); + froms = []; + fudge = ( + option.fudge + ? 1 + : 0 + ); + functions = []; + global = { + body: true, + context: empty(), + from: 0, + id: "(global)", + level: 0, + line: 0, + live: [], + loop: 0, + switch: 0, + thru: 0 + }; + blockage = global; + functionage = global; + json_mode = false; + mega_mode = false; + module_mode = false; + next_token = global; + property = empty(); + shebang = false; + stack = []; + tenure = undefined; + token = global; + token_nr = 0; + var_mode = undefined; + populate(standard, declared_globals, false); + populate(global_array, declared_globals, false); + Object.keys(option).forEach(function (name) { + if (option[name] === true) { + const allowed = allowed_option[name]; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } + }); + tokenize(source); + advance(); + if (json_mode) { + tree = json_value(); + advance("(end)"); + } else { + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option.browser) { + if (next_token.id === ";") { + advance(";"); + } + } else { + +// If we are not in a browser, then the file form of strict pragma may be used. + + if ( + next_token.value === "use strict" + ) { + advance("(string)"); + advance(";"); + } + } + tree = statements(); + advance("(end)"); + functionage = global; + walk_statement(tree); + if (warnings.length === 0) { + uninitialized_and_unused(); + if (!option.white) { + whitage(); + } + } + } + if (!option.browser) { + directives.forEach(function (comment) { + if (comment.directive === "global") { + warn("missing_browser", comment); + } + }); + } + early_stop = false; + } catch (e) { + if (e.name !== "JSLintError") { + warnings.push(e); + } + } + return { + directives, + edition: "2021-05-17", + exports, + froms, + functions, + global, + id: "(JSLint)", + json: json_mode, + lines, + module: module_mode === true, + ok: warnings.length === 0 && !early_stop, + option, + property, + shebang: ( + shebang + ? lines[0] + : undefined + ), + stop: early_stop, + tokens, + tree, + warnings: warnings.sort(function (a, b) { + return a.line - b.line || a.column - b.column; + }) + }; +}); diff --git a/branch.sort_object_keys/report.js b/branch.sort_object_keys/report.js new file mode 100644 index 000000000..5e487d143 --- /dev/null +++ b/branch.sort_object_keys/report.js @@ -0,0 +1,222 @@ +// report.js +// 2018-10-22 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Generate JSLint HTML reports. + +/*property + closure, column, context, edition, error, exports, filter, forEach, freeze, + froms, fudge, function, functions, global, id, isArray, join, json, keys, + length, level, line, lines, message, module, name, names, option, + parameters, parent, property, push, replace, role, signature, sort, stop, + warnings +*/ + +const rx_amp = /&/g; +const rx_gt = />/g; +const rx_lt = / with less destructive entities. + + return String( + string + ).replace( + rx_amp, + "&" + ).replace( + rx_lt, + "<" + ).replace( + rx_gt, + ">" + ); +} + +export default Object.freeze({ + error: function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + let fudge = Number(Boolean(data.option.fudge)); + let output = []; + if (data.stop) { + output.push("
    JSLint was unable to finish.
    "); + } + data.warnings.forEach(function (warning) { + output.push( + "
    ", + entityify(warning.line + fudge), + ".", + entityify(warning.column + fudge), + "
    ", + entityify(warning.message), + "
    ", + entityify(data.lines[warning.line] || ""), + "" + ); + }); + return output.join(""); + }, + + function: function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + let fudge = Number(Boolean(data.option.fudge)); + let mode = ( + data.module + ? "module" + : "global" + ); + let output = []; + + if (data.json) { + return ( + data.warnings.length === 0 + ? "
    JSON: good.
    " + : "
    JSON: bad.
    " + ); + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + let global = Object.keys(data.global.context).sort(); + let froms = data.froms.sort(); + let exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + let context = the_function.context; + let list = Object.keys(context); + output.push( + "
    ", + entityify(the_function.line + fudge), + "
    ", + ( + the_function.name === "=>" + ? entityify(the_function.signature) + " =>" + : ( + typeof the_function.name === "string" + ? "«" + entityify(the_function.name) + "»" + : "" + entityify(the_function.name.id) + "" + ) + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + let params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.parent === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.parent === the_function + ); + })); + detail("outer", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.parent !== the_function + && the_variable.parent.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].parent.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + output.push( + "
    JSLint edition ", + entityify(data.edition), + "
    " + ); + return output.join(""); + }, + + property: function property_directive(data) { + +// Produce the /*property*/ directive. + + let not_first = false; + let output = ["/*property"]; + let length = 1111; + let properties = Object.keys(data.property); + + if (properties.length > 0) { + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length >= 80) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); + } + } +}); diff --git a/branch.sort_warnings_early_stop/README b/branch.sort_warnings_early_stop/README new file mode 100644 index 000000000..cd6b06c8b --- /dev/null +++ b/branch.sort_warnings_early_stop/README @@ -0,0 +1,39 @@ +JSLint, The JavaScript Code Quality Tool + +Douglas Crockford +douglas@crockford.com + +2019-03-15 + +jslint.js contains the jslint function. It parses and analyzes a source file, +returning an object with information about the file. It can also take an object +that sets options. + +index.html runs the jslint.js function in a web page. The page also depends +browser.js and report.js and jslint.css. + +jslint.css provides styling for index.html. + +browser.js runs the web user interface. + +report.js generates the results reports in HTML. + +help.html describes JSLint's usage. Please read it. + +function.html describes the jslint function and the results it produces. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[http://www.crockford.com/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. diff --git a/branch.sort_warnings_early_stop/browser.js b/branch.sort_warnings_early_stop/browser.js new file mode 100644 index 000000000..5086fed85 --- /dev/null +++ b/branch.sort_warnings_early_stop/browser.js @@ -0,0 +1,158 @@ +// browser.js +// 2018-06-16 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +/*jslint + browser +*/ + +/*property + checked, create, disable, display, error, focus, forEach, function, + getElementById, innerHTML, join, length, map, onchange, onclick, onscroll, + property, querySelectorAll, scrollTop, select, split, style, title, value +*/ + +import jslint from "./jslint.js"; +import report from "./report.js"; + +// This is the web script companion file for JSLint. It includes code for +// interacting with the browser and displaying the reports. + +let rx_crlf = /\n|\r\n?/; +let rx_separator = /[\s,;'"]+/; + +let fudge_unit = 0; + +const aux = document.getElementById("JSLINT_AUX"); +const boxes = document.querySelectorAll("[type=checkbox]"); +const fudge = document.getElementById("JSLINT_FUDGE"); +const global = document.getElementById("JSLINT_GLOBAL"); +const number = document.getElementById("JSLINT_NUMBER"); +const property = document.getElementById("JSLINT_PROPERTY"); +const property_fieldset = document.getElementById("JSLINT_PROPERTYFIELDSET"); +const report_field = document.getElementById("JSLINT_REPORT"); +const report_list = document.getElementById("JSLINT_REPORT_LIST"); +const source = document.getElementById("JSLINT_SOURCE"); +const select = document.getElementById("JSLINT_SELECT"); +const warnings = document.getElementById("JSLINT_WARNINGS"); +const warnings_list = document.getElementById("JSLINT_WARNINGS_LIST"); + +function show_numbers() { + number.value = source.value.split(rx_crlf).map(function (ignore, index) { + return index + fudge_unit; + }).join("\n"); +} + +function clear() { + aux.style.display = "none"; + number.value = ""; + property.value = ""; + property_fieldset.style.display = "none"; + report_field.style.display = "none"; + report_list.innerHTML = ""; + source.focus(); + source.value = ""; + warnings.style.display = "none"; + warnings_list.innerHTML = ""; +} + +function fudge_change() { + fudge_unit = Number(fudge.checked); + show_numbers(); +} + +function clear_options() { + boxes.forEach(function (node) { + node.checked = false; + }); + fudge_change(); + global.innerHTML = ""; +} + +function call_jslint() { + +// First build the option object. + + let option = Object.create(null); + boxes.forEach(function (node) { + if (node.checked) { + option[node.title] = true; + } + }); + +// Call JSLint with the source text, the options, and the predefined globals. + + let global_string = global.value; + let result = jslint( + source.value, + option, + ( + global_string === "" + ? undefined + : global_string.split(rx_separator) + ) + ); + +// Generate the reports. + + let error_html = report.error(result); + let function_html = report.function(result); + let property_text = report.property(result); + +// Display the reports. + + warnings_list.innerHTML = error_html; + warnings.style.display = ( + error_html.length === 0 + ? "none" + : "block" + ); + + report_list.innerHTML = function_html; + report_field.style.display = "block"; + if (property_text) { + property.value = property_text; + property_fieldset.style.display = "block"; + property.scrollTop = 0; + select.disable = false; + } else { + property_fieldset.style.display = "none"; + select.disable = true; + } + aux.style.display = "block"; + source.select(); +} + +fudge.onchange = fudge_change; + +source.onchange = function (ignore) { + show_numbers(); +}; + +source.onscroll = function () { + let ss = source.scrollTop; + number.scrollTop = ss; + let sn = number.scrollTop; + if (ss > sn) { + show_numbers(); + number.scrollTop = ss; + } +}; + +document.querySelectorAll("[name='JSLint']").forEach(function (node) { + node.onclick = call_jslint; +}); + +document.querySelectorAll("[name='clear']").forEach(function (node) { + node.onclick = clear; +}); + +document.getElementById("JSLINT_SELECT").onclick = function () { + property.focus(); + property.select(); +}; +document.getElementById("JSLINT_CLEAR_OPTIONS").onclick = clear_options; + +fudge_change(); +source.select(); +source.focus(); diff --git a/branch.sort_warnings_early_stop/function.html b/branch.sort_warnings_early_stop/function.html new file mode 100644 index 000000000..75bf39360 --- /dev/null +++ b/branch.sort_warnings_early_stop/function.html @@ -0,0 +1,615 @@ + + + + + + + + +JSLint: jslint function + + + +
    JSLint
    + +
    The jslint Function
    +
    +

    JSLint

    +

    JSLint is delivered as a set of files:

    +
    + + + + + + + + + + + + + + +
    FilenameContent
    browser.jsBrowser based UI.
    function.htmlThis page that you are looking at right now.
    help.htmlUsing jslint as a single page JSLint application.
    index.htmlSingle page JSLint application that uses the + js files.
    jslint.jsThe jslint function.
    report.jsThe report object, containing report generator functions for + HTML.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    +
    + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring"(JSLint)"
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    parenttokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    parenttokenThe function in which the variable was declared.variable
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    shebangstringThe first line of text if it started with #!line
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable may be assigned to.variable
    +

    Reports

    +

    The report object contains three functions that all take a + result from the jslint function as input. + report.js has no other dependence on other files.

    + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    +
    + +
    + + + +
    + + diff --git a/branch.sort_warnings_early_stop/help.html b/branch.sort_warnings_early_stop/help.html new file mode 100644 index 000000000..358a35baa --- /dev/null +++ b/branch.sort_warnings_early_stop/help.html @@ -0,0 +1,812 @@ + + + + + + + + + +JSLint: Help + + + +
    JSLint
    + +
    Help
    +
    +
    Il semble que la perfection soit atteinte non quand il n’y a + plus rien à ajouter, mais quand il n’y a plus rien à + retrancher.
    +
    + Antoine de Saint-Exupéry +
    +
    + Terre des Hommes (1939) +
    +

    Good Parts

    +

    The Principle of the Good Parts is

    +
    If a feature is sometimes useful and sometimes dangerous and if + there is a better option then always use the better option.
    +

    JSLint is a JavaScript program that looks for problems in + JavaScript programs. It is a code quality tool.

    +

    When C was + a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    +

    As the language matured, the definition of the language was + strengthened to eliminate some insecurities, and compilers got better + at issuing warnings. lint is no longer needed.

    +

    JavaScript is a + young-for-its-age language. It was originally intended to do small tasks in + webpages, tasks for which Java was too heavy and clumsy. But JavaScript is + a surprisingly capable language, and it is now being used in larger + projects. Many of the features that were intended to make the language easy + to use are troublesome when projects become complicated. A lint + for JavaScript is needed: JSLint, a JavaScript syntax checker + and validator.

    +

    JSLint takes a JavaScript source and scans it. If it finds a + problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by the ECMAScript Programming Language + Standard (the strangely named document that governs JavaScript). + JSLint will reject most legal programs. It is a higher + standard.

    +

    JavaScript is a sloppy language, but hidden deep inside there is an elegant, + better language. JSLint helps you to program in that better + language and to avoid most of the slop. JSLint will reject + programs that browsers will accept because JSLint is concerned + with the quality of your code and browsers are not. You should gladly accept + all of JSLint's advice.

    +

    JSLint can operate on JavaScript source + or JSON text.

    +

    ECMAScript Sixth Edition

    +

    Some of + ES6’s features are good, so JSLint will recognize the good + parts of ES6.

    +

    Currently, these features are recognized:

    +
      +
    • The ... ellipsis marker in parameter + lists and argument lists, replacing the arguments object + for variadic functions.
    • +
    • The let statement, which is like the var + statement except that it respects block scope. + You may use let or var but not both.
    • +
    • The const statement is like the let statement + except that it disallows the use of assignment on the variable, although + if the value of the variable is mutable, it can still be mutated. + const is preferred to let. +
    • Destructuring of arrays and objects is allowed in parameter lists and on + the left side of let, and const, but not + var or assignment statements, and not deep destructuring + or eliding.
    • +
    • Enhanced object literals, providing shorter forms for function + declaration and properties that are initialized by variables with the + same name.
    • +
    • The fat arrow => fart functions.
    • +
    • The simplest forms of import and export.
    • +
    • `Megastring` literals, but not nested + `megastring` literals.
    • +
    • New global functions, such as Map, Set, + WeakMap, and WeakSet.
    • +
    • 0b- and 0o- number literals.
    • +
    +

    The most important new feature of ES6 is proper tail calls. This has no + new syntax, so JSLint doesn’t see it. But it makes recursion + much more attractive, which makes loops, particularly for + loops, much less attractive.

    +

    import export

    +

    The ES6 module feature will be an important improvement over JavaScript’s + global variables as a means of linking separate files together. + JSLint recognizes a small but essential subset of the module + syntax.

    +
    +

    import name from stringliteral;

    +

    import {name} from stringliteral;

    +

    import(string).then(function);

    +

    export default function () {};

    +

    export default function name() {};

    +

    export default expression;

    +

    export function name() {}

    +

    export name; // where name is const

    +

    export {name};

    +
    +

    Directives

    +

    JSLint provides three directives that may be placed in a + file to manage JSLint’s behavior. Use of these directives is + optional. If they are used, they should be placed in a source file before + the first statement. They are written in the form of a comment, where the + directive name is placed immediately after the opening of the comment before + any whitespace. The three directives are global, + jslint, and property. Directives in a file are + stronger than options selected from the UI or passed with the option object.

    +

    /*global*/

    +

    The /*global*/ directive is used to specify a set of globals + (usually functions and objects containing functions) that are available to + this file. This was commonly used in browsers to link source files together + before ES6 modules appeared. Use of global variables is strongly + discouraged, but unfortunately web browsers require their use. The + /*global*/ directive can only be used when the + Assume a browser option is selected. +

    Each of the names listed will indicate a read-only global variable. The names + are separated by , comma. This directive + should not be used if import or export is used. + This directive inhibits warnings, but it does not declare the names in the + execution environment. +

    For example: +

    /*global +
    ADSAFE, report, jslint
    +*/
    +

    instructs JSLint to not give warnings about the global + variables ADsafe, report, and jslint. + However, if any of those names are expected to be supplied by other files + and those other files fail to do so, then execution errors will result. It + is usually better to use top-level variable declarations instead: +

        var ADSAFE;
    +    var report;
    +    var jslint; 
    +

    Using var in this way allows comparing a global variable to the + undefined value to determine whether it is has been used in the global context.

    +

    /*jslint*/

    +

    The /*jslint*/ directive allows for the control of several + options. These options can also be set from the + JSLint.com user interface.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Tolerate bitwise operatorsbitwisetrue if bitwise operators should be allowed. The + bitwise operators are rarely used in JavaScript programs, so it + is usually more likely that & is a mistyping of + && than that it indicates a bitwise and. + JSLint will give warnings on the bitwise operators + unless this option is selected.
    Assume a browser browsertrue if the standard browser globals should be + predefined. This option will reject the use of + import and export. This option also + disallows the file form of the "use + strict" pragma. It does not supply + self; you will have to request that unnecessary + alias of the dreaded global object yourself. It adds the same + globals as this directive: +
    /*global +
    + clearInterval, clearTimeout, document, event, FileReader, + FormData, history, localStorage, location, name, navigator, + screen, sessionStorage, setInterval, setTimeout, Storage, + URL, window, XMLHttpRequest +
    + */
    +
    Tolerate conversion operatorsconverttrue if !!, + prefix, + and concatenation with "" are allowed. + The Boolean, Number, and + String functions are preferred
    Assume CouchDBcouchtrue if + Couch DB + globals should be predefined. It adds the same globals as this + directive: +
    /*global +
    emit, getRow, isArray, log, provides, registerType, require, + send, start, sum, toJSON
    + */
    Assume in developmentdeveltrue if browser globals that are useful in + development should be predefined, and if debugger + statements and TODO comments + should be allowed. It adds the + same globals as this directive: +
    /*global +
    alert, confirm, console, prompt
    + */
    Be sure to turn this option off before going + into production.
    Tolerate evalevaltrue if eval should be allowed. In the + past, the eval function was the most misused + feature of the language.
    Tolerate for statementfortrue if the for statement should be + allowed. It is almost always better to use the array methods + instead.
    Fudge the line and column numbersfudgetrue if the origin of a text file is line 1 column + 1. Programmers know that number systems start at zero. + Unfortunately, most text editors start numbering lines and + columns at one. JSLint will by default start + numbering at zero. Use this option to start the numbering at one + in its reports.
    Tolerate get and setgetsettrue if accessor properties are allowed in object literals.
    Tolerate long source lineslongtrue if a line can contain more than 80 characters.
    Assume Node.jsnodetrue if Node.js globals should be predefined. It + will predefine globals that are used in the Node.js environment. + It adds the same globals as this directive: +
    /*global +
    + Buffer, clearImmediate, clearInterval, clearTimeout, console, exports, + module, process, require, setImmediate, setInterval, setTimeout, URL, + URLSearchParams, __dirname, __filename +
    + */
    Tolerate single quote stringssingletrue if 'single quote + should be allowed to enclose string literals.
    Tolerate this
    thistrue if this should be allowed.
    Tolerate whitespace messwhitetrue if the whitespace rules should be ignored.
    +

    For example:

    +
    /*jslint
    +    bitwise, node
    +*/
    + +

    /*property*/

    +

    The /*property*/ directive is used to declare a list of + property identifiers that are used in the file. Each property name in the + program is looked up in this list. If a name is not found, that indicates an + error, most likely a typing error.

    +

    The list can also be used to evade some of JSLint’s naming + rules.

    +

    JSLint can build the /*property*/ list for you. At + the bottom of its report, JSLint displays a + /*property*/ directive. It contains all of the names that were + used with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. You + can copy the /*property*/ directive to the top of your + script file. JSLint will check the spelling of all property + names against the list. That way, you can have JSLint look for + misspellings for you.

    +

    For example,

    +
    /*property +
    charAt, slice, _$_
    + */
    +

    ignore

    +

    JSLint introduces a new reserved word: ignore. It + is used in parameter lists and in catch clauses to indicate a + parameter that will be ignored. Unused warnings will not be produced for + ignore. +

    function handler(ignore, value) {
    +    return do_something_useful(value);
    +}
    +

    var let const

    +

    JavaScript provides three statements for declaring variables: + var, let, and const. The + var statement suffers from a bad practice called hoisting. The + let statement does not do hoisting and respects block scope. + The const statement is like let except that it + marks the variable (but not its contents) as read only, making it an error + to attempt to assign to the variable. When given a choice, + const is the best, var is the worst.

    +

    JSLint uses the intersection of the var rules and the + let rules, and by doing so avoids the errors related to either. + A name should be declared only once in a function. It should be declared + before it is used. It should not be used outside of the block in which it is + declared. A variable should not have the same name as a variable or + parameter in an outer function. Do not mix var and + let. Declare one name per statement.

    +

    = == ===

    +

    Fortran made a terrible + mistake in using the equality operator as its assignment operator. That + mistake has been replicated in most languages since then. C compounded that + mistake by making == its equality operator. The visual + similarity is a source of errors. JavaScript compounded this further by + making == a type coercing comparison operator that produces + false positive results. This was mitigated by adding the === + operator, leaving the broken == operator in place.

    +

    JSLint attempts to minimize errors by the following rules:

    +

    == is not allowed. This avoids the false positives, and + increases the visual distance between = and ===. + Assignments are not allowed in expression position, and comparisons are not + allowed in statement position. This also reduces confusion. 

    +

    Semicolon

    +

    JavaScript uses a C-like syntax that requires the use of semicolons to + delimit certain statements. JavaScript attempts to make those semicolons + optional with an automatic semicolon insertion mechanism, but it does not + work very well. Automatic semicolon insertion was added to make + things easier for beginners. Unfortunately, it sometimes fails. Do not rely + on it unless you are a beginner.

    +

    JSLint expects that every statement will be followed by + ; except for for, function, + if, switch, try, and + while. JSLint does not expect to see unnecessary + semicolons, the empty statement, or empty blocks.

    +

    function =>

    +

    JavaScript has four syntactic forms for making function objects: function + statements, function expressions, enhanced object literals, and the + => fart operator.

    +

    The function statement creates a variable and assigns the function object to + it. It should be used in a file or function body, but not inside of a block.

    +
    function name(parameters) { +
    statements
    + }
    +

    The function expression unfortunately looks like the function statement. It + may appear anywhere that an expression may appear, but not in statement + position and not in a loop. It produces a function object but does not + create a variable in which to store it.

    +
    function (parameters) { +
    statements
    + }
    +

    The enhanced object literal provides an ES6 shorthand for creating a property + whose value is a function, saving you from having to type + : colon and function.

    +
    { +
    name(parameters) { +
    statements
    + }
    + }
    +

    Finally, ES6 provides an even shorter form of function expression that leaves + out the words function and return:

    +
    (parameters) => + expression
    +

    JSLint requires the parens around the parameters, and forbids a + { left brace after the + => fart to avoid syntactic ambiguity.

    +

    Comma

    +

    The , comma operator is unnecessary and can + mask programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as + an operator. It does not expect to see elided elements in array literals. A + comma should not appear after the last element of an array literal or object + literal.

    +

    Blocks

    +

    JSLint expects blocks with function, + if, switch, while, for, + do, and try statements and nowhere else.

    +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    +

    JavaScript allows an if to be written like this:

    +
    if (condition)
    +    statement;
    +

    That form is known to contribute to mistakes in projects where many + programmers are working on the same code. That is why JSLint + expects the use of a block:

    +
    if (condition) {
    +    statements;
    +}
    +

    Experience shows that this form is more resilient.

    +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call. All other expression statements are considered + to be errors.

    +

    for

    +

    JSLint does not recommend use of the for statement. + Use array methods like forEach instead. The for + option will suppress some warnings. The forms of for that + JSLint accepts are restricted, excluding the new ES6 forms.

    +

    for in

    +

    JSLint does not recommend use of the for + in statement. Use Object.keys instead.

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, it also + loops through all of the properties that were inherited through the + prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written + without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    for (name in object) {
    +    if (object.hasOwnProperty(name)) {
    +        ....
    +    }
    +}
    +

    Note that the above code will fail if the object contains a property + named hasOwnProperty. Use Object.keys instead.

    +

    switch

    +

    A common error in switch statements is to forget to place a + break statement after each case, resulting in unintended + fall-through. JSLint expects that the statement before the next + case or default is one of these: + break, return, or throw.

    +

    with

    +

    The with statement was intended to provide a shorthand in + accessing properties in deeply nested objects. Unfortunately, it behaves + very badly when setting new properties. Never use the with + statement.

    +

    JSLint does not expect to see a with statement.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that + labels will be distinct from vars and parameters.

    +

    Unreachable Code

    +

    JSLint expects that a return, break, + or throw statement will be followed by a + } right brace or case or + default.

    +

    Confusing Pluses and Minuses

    +

    JSLint expects that + will not be followed by + + or ++, and that - will not be + followed by - or --. A misplaced space can turn + + + into ++, an error that is difficult to see. + Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ increment and + -- decrement operators have been known to + contribute to bad code by encouraging excessive trickiness. They are second + only to faulty architecture in enabling to viruses and other security + menaces. Also, preincrement/postincrement confusion can produce off-by-one + errors that are extremely difficult to diagnose. Fortunately, they are also + complete unnecessary. There are better ways to add 1 to a variable.

    +

    It is best to avoid these operators entirely and rely on += and + -= instead.

    +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. + JSLint looks for problems that may cause portability problems. + It also attempts to resolve visual ambiguities by recommending explicit + escapement.

    +

    JavaScript’s syntax for regular expression literals overloads the + / slash character. To avoid ambiguity, + JSLint expects that the character preceding a regular + expression literal is a ( left paren or + = equal or + : colon or + , comma character.

    +

    this

    +
    Having this in the language makes it harder to talk + about the language. It is like pair programming with Abbott and Costello. +
    +

    Avoid using this. Warnings about this can be + suppressed with option.this.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the + new prefix. The new prefix creates a new object + based on the function's prototype, and binds that object to the + function's implied this parameter. If you neglect to use the + new prefix, no new object will be made and this + will be bound to the global object.

    +

    JSLint enforces the convention that constructor functions be + given names with initial uppercase. JSLint does not expect to + see a function invocation with an initial uppercase name unless it has the + new prefix. JSLint does not expect to see the + new prefix used with functions whose names do not start with + initial uppercase.

    +

    JSLint does not expect to see the wrapper forms + new Number, new String, new Boolean.

    +

    JSLint does not expect to see new Object. + Use Object.create(null) instead.

    +

    Whitespace

    +

    JSLint has a specific set of rules about the use of whitespace. + Where possible, these rules are consistent with centuries of good practice + with literary style.

    +

    The indentation increases by 4 spaces when the last token on a line is + { left brace, + [ left bracket, + ( left paren. The matching closing + token will be the first token on a line, restoring the previous + indentation.

    +

    The ternary operator can be visually confusing, so + ? question mark and + : colon always begin a line and increase + the indentation by 4 spaces.

    +
    return (
    +    (the_token.id === "(string)" || the_token.id === "(number)")
    +    ? String(the_token.value)
    +    : the_token.id
    +);
    +

    The word function is always followed with one space.

    +

    Clauses (case, catch, default, + else, finally) are not statements and so should + not be indented like statements.

    +

    Spaces are used to make things that are not invocations look less like + invocations.

    +

    Tabs and spaces should not be mixed. We should pick just one in order to + avoid the problems that come from having both. Personal preference is an + extremely unreliable criterion. Neither offers a powerful advantage over the + other. Fifty years ago, tab had the advantage of consuming less memory, but + Moore's Law has eliminated that advantage. Space has one clear advantage + over tab: there is no reliable standard for how many spaces a tab + represents, but it is universally accepted that a space occupies a space. So + use spaces. You can edit with tabs if you must, but make sure it is spaces + again before you commit. Maybe someday we will finally get a universal + standard for tabs, but until that day comes, the better choice is spaces.

    +

    Report

    +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    +
      +
    • The line number on which it starts.
    • +
    • The function’s name. In the case of anonymous functions, + JSLint will attempt to + «guess» the name.
    • +
    • The parameters.
    • +
    • Variables: The variables that are declared in the function.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Exceptions: The variables that are declared by catch + clauses of try statements.
    • +
    • Outer: Variables used by this function that are declared in + other functions.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements.

    +

    Try it

    +

    Try it. Paste your script + into the window and click the + + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    JSLint is written entirely in JavaScript, so it can run + anywhere that JavaScript (or even Java) can run.

    +

    Perfectly fine

    +

    JSLint was designed to reject code that some would consider to + be perfectly fine. The reason for this is that JSLint's + purpose is to help produce programs that are free of error. That is + difficult in any language and is especially hard in JavaScript. + JSLint attempts to help you increase the visual distance + between correct programs and incorrect programs, making the remaining errors + more obvious. JSLint will give warnings about things that are + not necessarily wrong in the current situation, but which have been observed + to mask or obscure errors. Avoid those things when there are better options + available.

    +

    It is dangerous out there. JSLint is here to help.

    +

    Warning

    +

    JSLint will hurt your feelings. Side effects may include + headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, + dry mouth, cleaner code, and a reduced error rate.

    +

    Further Reading

    + +
    +

    + Code Conventions for the JavaScript Programming Language.

    + + + +
    + + diff --git a/branch.sort_warnings_early_stop/index.html b/branch.sort_warnings_early_stop/index.html new file mode 100644 index 000000000..822acaad6 --- /dev/null +++ b/branch.sort_warnings_early_stop/index.html @@ -0,0 +1,108 @@ + + + + + + + + + +JSLint: The JavaScript Code Quality Tool + + +
    +
    Source
    +
    + + +
    +
    + Warnings +
    +
    +
    + Function Report +
    +
    +
    + Property Directive + +
    +
    + + + + +
    +
    Options +
    Assume... +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Global variables... + +
    +
    +
    + + diff --git a/branch.sort_warnings_early_stop/jslint.css b/branch.sort_warnings_early_stop/jslint.css new file mode 100644 index 000000000..cc3bf1696 --- /dev/null +++ b/branch.sort_warnings_early_stop/jslint.css @@ -0,0 +1,341 @@ +@font-face { + font-family: 'Programma'; + font-weight: bold; + src: url('Programma-Bold.woff2') format('woff2'); +} +@font-face { + font-family: 'Daley'; + font-weight: bold; + src: url('Daley-Bold.woff2') format('woff2'); +} + +body { + background-color: antiquewhite; + color: black; + font-family: 'Daley', sans-serif; + margin: 0; + padding: 0; +} + +#JSLINT_TITLEBOX { + font-family: 'Daley', sans-serif; + margin: 0; + margin-left: 3%; + margin-right: 3%; + padding: 0; +} + +#JSLINT_TITLEBOX ul { + float: right; + font-size: 12pt; +} + +#JSLINT_TITLEBOX div { + color: darkslategray; + float: left; + font-size: 48pt; +} + +#JSLINT_ fieldset { + background-color: gainsboro; + border: 0; + clear: both; + margin-bottom: 1em; + margin-left: 3%; + margin-right: 3%; + margin-top: 1em; + padding: 0; + width: auto; +} +#JSLINT_ legend { + background-color: darkslategray; + border: 0; + color: white; + font-size: 100%; + font-style: normal; + font-weight: normal; + margin: 0; + padding-bottom: 0.25em; + padding-left: 0; + padding-right: 0; + padding-top: 0.25em; + text-align: center; + width: 100%; +} +#JSLINT_ button { + background-color: darkslategray; + border: 0; + color: white; + cursor: pointer; + font-family: 'Daley', sans-serif; + font-size: 100%; + font-style: normal; + margin-left: 1em; + margin-right: 1em; + padding-left: 1em; + padding-right: 1em; + padding-bottom: 0.25em; + padding-top: 0.25em; + text-align: center; +} +#JSLINT_ button:hover { + background-color: slategray; + color: white; + border: 0; +} + +#JSLINT_ button:active { + border: 0; + color: black; +} + +#JSLINT_ button:disabled { + background-color: gray; + border: 0; + color: gainsboro; +} + +a:visited { + color: #69565c; +} + +a:link { + color: black; +} + +#JSLINT_ input[type="text"] { + background-color: white; + border: 0; + color: black; + margin-bottom: 0.2em; + margin-right: 0.5em; + padding-left: 0.5em; + padding-right: 0.5em; + text-align: right; + width: 3em; +} + +#JSLINT_ textarea { + border: 0; + background-color: white; + border: 0; + font-family: Programma, monospace; + font-size: 100%; + font-weight: bold; + resize: none; +} + +#JSLINT_ textarea::selection { + background-color: yellow; + color: black; +} + +#JSLINT_NUMBER { + color: darkgray; + display: inline-block; + height: 4in; + margin: 2%; + margin-right: 0; + overflow: hidden; + padding-right: 0.5%; + text-align: right; + white-space: pre; + width: 4%; +} + +#JSLINT_SOURCE { + color: black; + display: inline-block; + font-size: 100%; + height: 4in; + margin: 2%; + margin-left: 0; + margin-right: 0; + overflow: auto; + padding-left: 0.5%; + white-space: pre; + width: 91%; +} + +#JSLINT_PROPERTY { + background-color: honeydew; + border: 0; + margin: 2%; + padding: 0.5%; + white-space: pre; + width: 95%; +} + +#JSLINT_GLOBAL { + white-space: normal; + width: 95%; +} +#JSLINT_ label { + font-family: sans-serif; + font-size: 90%; + padding-left: 0.25em; +} +#JSLINT_OPTIONS>div { + float: left; + margin: 0.5em; +} + +#JSLINT_ address { + color: black; + display: block; + float: right; + font-family: serif; + font-size: 90%; + margin-left: 1em; +} +#JSLINT_ dl { + background-color: cornsilk; + color: white; + margin: 0; + padding-bottom: 2pt; + padding-left: 1em; + padding-right: 1em; + padding-top: 2pt; +} +#JSLINT_ dfn { + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + margin-bottom: 2pt; +} +#JSLINT_ dt { + color: black; + display: block; + float: left; + font-family: serif; + font-size: 75%; + font-style: italic; + margin: 0; + width: 8em; + text-align: right; +} +#JSLINT_ dd { + color: black; + display: block; + font-family: Programma, monospace; + font-weight: bold; + margin-left: 8em; + padding-bottom: 2pt; +} +#JSLINT_WARNINGS>legend { + background-color: indianred; +} +#JSLINT_WARNINGS>div { + background-color: pink; + padding: 1em; +} +#JSLINT_WARNINGS cite { + color: black; + display: block; + font-family: serif; + font-size: 100%; + font-style: normal; + margin-bottom: 4pt; + margin-left: 20pt; + margin-right: 20pt; + margin-top: 4pt; + overflow-x: hidden; +} +#JSLINT_WARNINGS samp { + background-color: lavenderblush; + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + padding: 4pt; + margin-bottom: 0; + margin-left: 16pt; + margin-right: 16pt; + margin-top: 0; + white-space: pre-wrap; +} + +#JSLINT_REPORT center { + margin-top: 1em; +} + +#JSLINT_WARNINGS dl address { + color: black; + display: inline; + float: none; + font-size: 80%; + margin: 0; +} + +#JSLINT_REPORT>div { + padding: 1em; +} + +#JSLINT_ dl.level0 { + color: black; + background-color: white; +} + +#JSLINT_ dl.level1 { + color: black; + background-color: #ffffe0; /* yellow */ + margin-left: 1em; +} + +#JSLINT_ dl.level2 { + color: black; + background-color: #e0ffe0; /* green */ + margin-left: 2em; +} + +#JSLINT_ dl.level3 { + color: black; + background-color: #D0D0ff; /* blue */ + margin-left: 3em; +} + +#JSLINT_ dl.level4 { + background-color: #ffe0ff; /* purple */ + color: black; + margin-left: 4em; +} + +#JSLINT_ dl.level5 { + background-color: #ffe0e0; /* red */ + color: black; + margin-left: 5em; +} + +#JSLINT_ dl.level6 { + background-color: #ffe390; /* orange */ + color: black; + margin-left: 6em; +} + +#JSLINT_ dl.level7 { + background-color: #e0e0e0; /* gray */ + color: black; + margin-left: 7em; +} + +#JSLINT_ dl.level8 { + color: black; + margin-left: 8em; +} + +#JSLINT_ dl.level9 { + color: black; + margin-left: 9em; +} + +.none { + display: none; +} +.center { + text-align: center; +} diff --git a/branch.sort_warnings_early_stop/jslint.js b/branch.sort_warnings_early_stop/jslint.js new file mode 100644 index 000000000..f11581c95 --- /dev/null +++ b/branch.sort_warnings_early_stop/jslint.js @@ -0,0 +1,5041 @@ +// jslint.js +// 2020-10-29 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// jslint(source, option_object, global_array) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze, a string or an array of strings. +// option_object An object whose keys correspond to option names. +// global_array An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all of the functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// 1. If the source is a single string, split it into an array of strings. +// 2. Turn the source into an array of tokens. +// 3. Furcate the tokens into a parse tree. +// 4. Walk the tree, traversing all of the nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// 5. Check the whitespace between the tokens. + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*property + a, and, arity, assign, b, bad_assignment_a, bad_directive_a, bad_get, + bad_module_name_a, bad_option_a, bad_property_a, bad_set, bitwise, block, + body, browser, c, calls, catch, charCodeAt, closer, closure, code, column, + concat, constant, context, convert, couch, create, d, dead, default, devel, + directive, directives, disrupt, dot, duplicate_a, early_stop, edition, + ellipsis, else, empty_block, eval, every, expected_a, expected_a_at_b_c, + expected_a_b, expected_a_b_from_c_d, expected_a_before_b, + expected_a_next_at_b, expected_digits_after_a, expected_four_digits, + expected_identifier_a, expected_line_break_a_b, expected_regexp_factor_a, + expected_space_a_b, expected_statements_a, expected_string_a, + expected_type_string_a, exports, expression, extra, finally, flag, for, + forEach, free, freeze, freeze_exports, from, froms, fud, fudge, + function_in_loop, functions, g, getset, global, i, id, identifier, import, + inc, indexOf, infix_in, init, initial, isArray, isNaN, join, json, keys, + label, label_a, lbp, led, length, level, line, lines, live, long, loop, m, + margin, match, message, misplaced_a, misplaced_directive_a, missing_browser, + missing_m, module, naked_block, name, names, nested_comment, new, node, + not_label_a, nr, nud, number_isNaN, ok, open, opening, option, + out_of_scope_a, parameters, parent, pop, property, push, quote, raw, + redefinition_a_b, replace, required_a_optional_b, reserved_a, role, search, + shebang, signature, single, slice, some, sort, split, startsWith, statement, + stop, subscript_a, switch, test, this, thru, toString, todo_comment, + tokens, too_long, too_many_digits, tree, try, type, u, unclosed_comment, + unclosed_mega, unclosed_string, undeclared_a, unexpected_a, + unexpected_a_after_b, unexpected_a_before_b, unexpected_at_top_level_a, + unexpected_char_a, unexpected_comment, unexpected_directive_a, + unexpected_expression_a, unexpected_label_a, unexpected_parens, + unexpected_space_a_b, unexpected_statement_a, unexpected_trailing_space, + unexpected_typeof_a, uninitialized_a, unreachable_a, + unregistered_property_a, unused_a, use_double, use_open, use_spaces, + used, value, var_loop, var_switch, variable, warning, warnings, + weird_condition_a, weird_expression_a, weird_loop, weird_relation_a, white, + wrap_condition, wrap_immediate, wrap_parameter, wrap_regexp, wrap_unary, + wrapped, writable, y +*/ + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than '{}' because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +function populate(array, object = empty(), value = true) { + +// Augment an object by taking property names from an array of strings. + + array.forEach(function (name) { + object[name] = value; + }); + return object; +} + +const allowed_option = { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + bitwise: true, + browser: [ + "caches", "CharacterData", "clearInterval", "clearTimeout", "document", + "DocumentType", "DOMException", "Element", "Event", "event", "fetch", + "FileReader", "FontFace", "FormData", "history", "IntersectionObserver", + "localStorage", "location", "MutationObserver", "name", "navigator", + "screen", "sessionStorage", "setInterval", "setTimeout", "Storage", + "TextDecoder", "TextEncoder", "URL", "window", "Worker", + "XMLHttpRequest" + ], + couch: [ + "emit", "getRow", "isArray", "log", "provides", "registerType", + "require", "send", "start", "sum", "toJSON" + ], + convert: true, + devel: [ + "alert", "confirm", "console", "prompt" + ], + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + node: [ + "Buffer", "clearImmediate", "clearInterval", "clearTimeout", + "console", "exports", "module", "process", "require", + "setImmediate", "setInterval", "setTimeout", "TextDecoder", + "TextEncoder", "URL", "URLSearchParams", "__dirname", "__filename" + ], + single: true, + this: true, + white: true +}; + +const anticondition = populate([ + "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%", + "typeof", "(number)", "(string)" +]); + +// These are the bitwise operators. + +const bitwiseop = populate([ + "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=", + ">>>", ">>>=" +]); + +const escapeable = populate([ + "\\", "/", "`", "b", "f", "n", "r", "t" +]); + +const opener = { + +// The open and close pairs. + + "(": ")", // paren + "[": "]", // bracket + "{": "}", // brace + "${": "}" // mega +}; + +// The relational operators. + +const relationop = populate([ + "!=", "!==", "==", "===", "<", "<=", ">", ">=" +]); + +// This is the set of infix operators that require a space on each side. + +const spaceop = populate([ + "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/", + "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=", + ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" +]); + +const standard = [ + +// These are the globals that are provided by the language standard. + + "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI", + "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error", + "EvalError", "Float32Array", "Float64Array", "Generator", + "GeneratorFunction", "Int8Array", "Int16Array", "Int32Array", "Intl", + "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat", + "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", + "Set", "String", "Symbol", "SyntaxError", "System", "TypeError", + "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", + "URIError", "WeakMap", "WeakSet" +]; + +const bundle = { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + and: "The '&&' subexpression should be wrapped in parens.", + bad_assignment_a: "Bad assignment to '{a}'.", + bad_directive_a: "Bad directive '{a}'.", + bad_get: "A get function takes no parameters.", + bad_module_name_a: "Bad module name '{a}'.", + bad_option_a: "Bad option '{a}'.", + bad_property_a: "Bad property name '{a}'.", + bad_set: "A set function takes one parameter.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + expected_a: "Expected '{a}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: ( + "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'." + ), + expected_a_before_b: "Expected '{a}' before '{b}'.", + expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.", + expected_digits_after_a: "Expected digits after '{a}'.", + expected_four_digits: "Expected four digits after '\\u'.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.", + expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.", + expected_space_a_b: "Expected one space between '{a}' and '{b}'.", + expected_statements_a: "Expected statements before '{a}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + expected_type_string_a: "Expected a type string and instead saw '{a}'.", + freeze_exports: ( + "Expected 'Object.freeze('. All export values should be frozen." + ), + function_in_loop: "Don't make functions within a loop.", + infix_in: ( + "Unexpected 'in'. Compare with undefined, " + + "or use the hasOwnProperty method instead." + ), + label_a: "'{a}' is a statement label.", + misplaced_a: "Place '{a}' at the outermost level.", + misplaced_directive_a: ( + "Place the '/*{a}*/' directive before the first statement." + ), + missing_browser: "/*global*/ requires the Assume a browser option.", + missing_m: "Expected 'm' flag on a multiline regular expression.", + naked_block: "Naked block.", + nested_comment: "Nested comment.", + not_label_a: "'{a}' is not a label.", + number_isNaN: "Use Number.isNaN function to compare with NaN.", + out_of_scope_a: "'{a}' is out of scope.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + required_a_optional_b: ( + "Required parameter '{a}' after optional parameter '{b}'." + ), + reserved_a: "Reserved name '{a}'.", + subscript_a: "['{a}'] is better written in dot notation.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line is longer than 80 characters.", + too_many_digits: "Too many digits.", + unclosed_comment: "Unclosed comment.", + unclosed_mega: "Unclosed mega literal.", + unclosed_string: "Unclosed string.", + undeclared_a: "Undeclared '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_a_after_b: "Unexpected '{a}' after '{b}'.", + unexpected_a_before_b: "Unexpected '{a}' before '{b}'.", + unexpected_at_top_level_a: "Expected '{a}' to be in a function.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_directive_a: "When using modules, don't use directive '/*{a}'.", + unexpected_expression_a: ( + "Unexpected expression '{a}' in statement position." + ), + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_parens: "Don't wrap function literals in parens.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_statement_a: ( + "Unexpected statement '{a}' in expression position." + ), + unexpected_trailing_space: "Unexpected trailing space.", + unexpected_typeof_a: ( + "Unexpected 'typeof'. Use '===' to compare directly with {a}." + ), + uninitialized_a: "Uninitialized '{a}'.", + unreachable_a: "Unreachable '{a}'.", + unregistered_property_a: "Unregistered property name '{a}'.", + unused_a: "Unused '{a}'.", + use_double: "Use double quotes, not single quotes.", + use_open: ( + "Wrap a ternary expression in parens, " + + "with a line break after the left paren." + ), + use_spaces: "Use spaces, not tabs.", + var_loop: "Don't declare variables in a loop.", + var_switch: "Don't declare variables in a switch.", + weird_condition_a: "Weird condition '{a}'.", + weird_expression_a: "Weird expression '{a}'.", + weird_loop: "Weird loop.", + weird_relation_a: "Weird relation '{a}'.", + wrap_condition: "Wrap the condition in parens.", + wrap_immediate: ( + "Wrap an immediate function invocation in parentheses to assist " + + "the reader in understanding that the expression is the result " + + "of a function, and not the function itself." + ), + wrap_parameter: "Wrap the parameter in parens.", + wrap_regexp: "Wrap this regexp in parens to avoid confusion.", + wrap_unary: "Wrap the unary expression in parens." +}; + +// Regular expression literals: + +function tag_regexp(strings) { + return new RegExp(strings.raw[0].replace(/\s/g, "")); +} + +// supplant {variables} +const rx_supplant = /\{([^{}]*)\}/g; +// carriage return, carriage return linefeed, or linefeed +const rx_crlf = tag_regexp ` + \n + | \r \n? +`; +// identifier +const rx_identifier = tag_regexp ` ^( + [ a-z A-Z _ $ ] + [ a-z A-Z 0-9 _ $ ]* +)$`; +const rx_module = tag_regexp ` ^ [ a-z A-Z 0-9 _ $ : . @ \- \/ ]+ $ `; +const rx_bad_property = tag_regexp ` + ^_ + | \$ + | Sync $ + | _ $ +`; +// star slash +const rx_star_slash = tag_regexp ` \* \/ `; +// slash star +const rx_slash_star = tag_regexp ` \/ \* `; +// slash star or ending slash +const rx_slash_star_or_slash = tag_regexp ` \/ \* | \/ $ `; +// uncompleted work comment +const rx_todo = tag_regexp ` \b (?: + todo + | TO \s? DO + | HACK +) \b `; +// tab +const rx_tab = /\t/g; +// directive +const rx_directive = tag_regexp ` ^ ( + jslint + | property + | global +) \s+ ( .* ) $ `; +const rx_directive_part = tag_regexp ` ^ ( + [ a-z A-Z $ _ ] [ a-z A-Z 0-9 $ _ ]* +) (?: + : \s* ( true | false ) +)? ,? \s* ( .* ) $ `; +// token +const rx_token = tag_regexp ` ^ ( + (\s+) + | ( + [ a-z A-Z _ $ ] + [ a-z A-Z 0-9 _ $ ]* + ) + | [ + ( ) { } \[ \] , : ; ' " ~ \` + ] + | \? [ ? . ]? + | = (?: + = =? + | > + )? + | \.+ + | \* [ * \/ = ]? + | \/ [ * \/ ]? + | \+ [ = + ]? + | - [ = \- ]? + | [ \^ % ] =? + | & [ & = ]? + | \| [ | = ]? + | >{1,3} =? + | < = "a" && string <= "z\uffff") + || (string >= "A" && string <= "Z\uffff") + ); +} + +function supplant(string, object) { + return string.replace(rx_supplant, function (found, filling) { + const replacement = object[filling]; + return ( + replacement !== undefined + ? replacement + : found + ); + }); +} + +let anon; // The guessed name for anonymous functions. +let blockage; // The current block. +let block_stack; // The stack of blocks. +let declared_globals; // The object containing the global declarations. +let directives; // The directive comments. +let directive_mode; // true if directives are still allowed. +let early_stop; // true if JSLint cannot finish. +let exports; // The exported names and values. +let froms; // The array collecting all import-from strings. +let fudge; // true if the natural numbers start with 1. +let functionage; // The current function. +let functions; // The array containing all of the functions. +let global; // The global object; the outermost context. +let json_mode; // true if parsing JSON. +let lines; // The array containing source lines. +let mega_mode; // true if currently parsing a megastring literal. +let module_mode; // true if import or export was used. +let next_token; // The next token to be examined in the parse. +let option; // The options parameter. +let property; // The object containing the tallied property names. +let shebang; // true if a #! was seen on the first line. +let stack; // The stack of functions. +let syntax; // The object containing the parser. +let token; // The current token being examined in the parse. +let token_nr; // The number of the next token. +let tokens; // The array of tokens. +let tenure; // The predefined property registry. +let tree; // The abstract parse tree. +let var_mode; // "var" if using var; "let" if using let. +let warnings; // The array collecting all generated warnings. + +// Error reportage functions: + +function artifact(the_token) { + +// Return a string representing an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); +} + +function artifact_line(the_token) { + +// Return the fudged line number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.line + fudge; +} + +function artifact_column(the_token) { + +// Return the fudged column number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.from + fudge; +} + +function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + const warning = { // ~~ + name: "JSLintError", + column, + line, + code + }; + if (a !== undefined) { + warning.a = a; + } + if (b !== undefined) { + warning.b = b; + } + if (c !== undefined) { + warning.c = c; + } + if (d !== undefined) { + warning.d = d; + } + warning.message = supplant(bundle[code] || code, warning); + warnings.push(warning); + return warning; +} + +function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); +} + +function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + if (the_token.warning === undefined) { + the_token.warning = warn_at( + code, + the_token.line, + the_token.from, + a || artifact(the_token), + b, + c, + d + ); + return the_token.warning; + } +} + +function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); +} + +// Tokenize: + +function tokenize(source) { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + +// If the source is not an array, then it is split into lines at the +// carriage return/linefeed. + + lines = ( + Array.isArray(source) + ? source + : source.split(rx_crlf) + ); + tokens = []; + + let char; // a popular character + let column = 0; // the column number of the next character + let first; // the first token + let from; // the starting column number of the token + let line = -1; // the line number of the next character + let nr = 0; // the next token number + let previous = global; // the previous token including comments + let prior = global; // the previous token excluding comments + let mega_from; // the starting column of megastring + let mega_line; // the starting line of megastring + let regexp_seen; // regular expression literal seen on this line + let snippet; // a piece of string + let source_line = ""; // the remaining line source string + let whole_line = ""; // the whole line source string + + if (lines[0].startsWith("#!")) { + line = 0; + shebang = true; + } + + function next_line() { + +// Put the next line of source in source_line. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + let at; + if ( + !option.long + && whole_line.length > 80 + && !json_mode + && first + && !regexp_seen + ) { + warn_at("too_long", line, 80); + } + column = 0; + line += 1; + regexp_seen = false; + source_line = lines[line]; + whole_line = source_line || ""; + if (source_line !== undefined) { + at = source_line.search(rx_tab); + if (at >= 0) { + if (!option.white) { + warn_at("use_spaces", line, at + 1); + } + source_line = source_line.replace(rx_tab, " "); + } + if (!option.white && source_line.slice(-1) === " ") { + warn_at( + "unexpected_trailing_space", + line, + source_line.length - 1 + ); + } + } + return source_line; + } + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions snip, next_char, back_char, +// some_digits, and escape help in the parsing of literals. + + function snip() { + +// Remove the last character from snippet. + + snippet = snippet.slice(0, -1); + } + + function next_char(match) { + +// Get the next character from the source line. Remove it from the source_line, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + return stop_at( + ( + char === "" + ? "expected_a" + : "expected_a_b" + ), + line, + column - 1, + match, + char + ); + } + if (source_line) { + char = source_line[0]; + source_line = source_line.slice(1); + snippet += char; + } else { + char = ""; + snippet += " "; + } + column += 1; + return char; + } + + function back_char() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the source_line. + + if (snippet) { + char = snippet.slice(-1); + source_line = char + source_line; + column -= 1; + snip(); + } else { + char = ""; + } + return char; + } + + function some_digits(rx, quiet) { + const digits = source_line.match(rx)[0]; + const length = digits.length; + if (!quiet && length === 0) { + warn_at("expected_digits_after_a", line, column, snippet); + } + column += length; + source_line = source_line.slice(length); + snippet += digits; + next_char(); + return length; + } + + function escape(extra) { + next_char("\\"); + if (escapeable[char] === true) { + return next_char(); + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "u") { + if (next_char("u") === "{") { + if (json_mode) { + warn_at("unexpected_a", line, column - 1, char); + } + if (some_digits(rx_hexs) > 5) { + warn_at("too_many_digits", line, column - 1); + } + if (next_char() !== "}") { + stop_at("expected_a_before_b", line, column, "}", char); + } + return next_char(); + } + back_char(); + if (some_digits(rx_hexs, true) < 4) { + warn_at("expected_four_digits", line, column - 1); + } + return; + } + if (extra && extra.indexOf(char) >= 0) { + return next_char(); + } + warn_at("unexpected_a_before_b", line, column - 2, "\\", char); + } + + function make(id, value, identifier) { + +// Make the token object and append it to the tokens list. + + const the_token = { + from, + id, + identifier: Boolean(identifier), + line, + nr, + thru: column + }; + tokens[nr] = the_token; + nr += 1; + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + directive_mode = false; + } + +// If the token is to have a value, give it one. + + if (value !== undefined) { + the_token.value = value; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option.white. + + if ( + previous.line === line + && previous.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (previous.id === "(comment)" || previous.id === "(regexp)") + ) { + warn( + "expected_space_a_b", + the_token, + artifact(previous), + artifact(the_token) + ); + } + if (previous.id === "." && id === "(number)") { + warn("expected_a_before_b", previous, "0", "."); + } + if (prior.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// The previous token is used to detect adjacency problems. + + previous = the_token; + +// The prior token is a previous token that was not a comment. The prior token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (previous.id !== "(comment)") { + prior = previous; + } + return the_token; + } + + function parse_directive(the_comment, body) { + +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + + const result = body.match(rx_directive_part); + if (result) { + let allowed; + const name = result[1]; + const value = result[2]; + if (the_comment.directive === "jslint") { + allowed = allowed_option[name]; + if ( + typeof allowed === "boolean" + || typeof allowed === "object" + ) { + if ( + value === "" + || value === "true" + || value === undefined + ) { + option[name] = true; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } else if (value === "false") { + option[name] = false; + } else { + warn("bad_option_a", the_comment, name + ":" + value); + } + } else { + warn("bad_option_a", the_comment, name); + } + } else if (the_comment.directive === "property") { + if (tenure === undefined) { + tenure = empty(); + } + tenure[name] = true; + } else if (the_comment.directive === "global") { + if (value) { + warn("bad_option_a", the_comment, name + ":" + value); + } + declared_globals[name] = false; + module_mode = the_comment; + } + return parse_directive(the_comment, result[3]); + } + if (body) { + return stop("bad_directive_a", the_comment, body); + } + } + + function comment(snippet) { + +// Make a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + + const the_comment = make("(comment)", snippet); + if (Array.isArray(snippet)) { + snippet = snippet.join(" "); + } + if (!option.devel && rx_todo.test(snippet)) { + warn("todo_comment", the_comment); + } + const result = snippet.match(rx_directive); + if (result) { + if (!directive_mode) { + warn_at("misplaced_directive_a", line, from, result[1]); + } else { + the_comment.directive = result[1]; + parse_directive(the_comment, result[2]); + } + directives.push(the_comment); + } + return the_comment; + } + + function regexp() { + +// Parse a regular expression literal. + + let multi_mode = false; + let result; + let value; + regexp_seen = true; + + function quantifier() { + +// Match an optional quantifier. + + if (char === "?" || char === "*" || char === "+") { + next_char(); + } else if (char === "{") { + if (some_digits(rx_digits, true) === 0) { + warn_at("expected_a_before_b", line, column, "0", ","); + } + if (char === ",") { + some_digits(rx_digits, true); + } + next_char("}"); + } else { + return; + } + if (char === "?") { + next_char("?"); + } + } + + function subklass() { + +// Match a character in a character class. + + if (char === "\\") { + escape("BbDdSsWw-[]^"); + return true; + } + if ( + char === "" + || char === "[" + || char === "]" + || char === "/" + || char === "^" + || char === "-" + ) { + return false; + } + if (char === " ") { + warn_at("expected_a_b", line, column, "\\u0020", " "); + } else if (char === "`" && mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char(); + return true; + } + + function ranges() { + +// Match a range of subclasses. + + if (subklass()) { + if (char === "-") { + next_char("-"); + if (!subklass()) { + return stop_at( + "unexpected_a", + line, + column - 1, + "-" + ); + } + } + return ranges(); + } + } + + function klass() { + +// Match a class. + + next_char("["); + if (char === "^") { + next_char("^"); + } + (function classy() { + ranges(); + if (char !== "]" && char !== "") { + warn_at( + "expected_a_before_b", + line, + column, + "\\", + char + ); + next_char(); + return classy(); + } + }()); + next_char("]"); + } + + function choice() { + + function group() { + +// Match a group that starts with left paren. + + next_char("("); + if (char === "?") { + next_char("?"); + if (char === "=" || char === "!") { + next_char(); + } else { + next_char(":"); + } + } else if (char === ":") { + warn_at("expected_a_before_b", line, column, "?", ":"); + } + choice(); + next_char(")"); + } + + function factor() { + if ( + char === "" + || char === "/" + || char === "]" + || char === ")" + ) { + return false; + } + if (char === "(") { + group(); + return true; + } + if (char === "[") { + klass(); + return true; + } + if (char === "\\") { + escape("BbDdSsWw^${}[]():=!.|*+?"); + return true; + } + if ( + char === "?" + || char === "+" + || char === "*" + || char === "}" + || char === "{" + ) { + warn_at( + "expected_a_before_b", + line, + column - 1, + "\\", + char + ); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column - 1, "`"); + } + } else if (char === " ") { + warn_at( + "expected_a_b", + line, + column - 1, + "\\s", + " " + ); + } else if (char === "$") { + if (source_line[0] !== "/") { + multi_mode = true; + } + } else if (char === "^") { + if (snippet !== "^") { + multi_mode = true; + } + } + next_char(); + return true; + } + + function sequence(follow) { + if (factor()) { + quantifier(); + return sequence(true); + } + if (!follow) { + warn_at("expected_regexp_factor_a", line, column, char); + } + } + +// Match a choice (a sequence that can be followed by | and another choice). + + sequence(); + if (char === "|") { + next_char("|"); + return choice(); + } + } + +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + next_char(); + if (char === "=") { + warn_at("expected_a_before_b", line, column, "\\", "="); + } + choice(); + +// Make sure there is a closing slash. + + snip(); + value = snippet; + next_char("/"); + +// Process dangling flag letters. + + const allowed = { + g: true, + i: true, + m: true, + u: true, + y: true + }; + const flag = empty(); + (function make_flag() { + if (is_letter(char)) { + if (allowed[char] !== true) { + warn_at("unexpected_a", line, column, char); + } + allowed[char] = false; + flag[char] = true; + next_char(); + return make_flag(); + } + }()); + back_char(); + if (char === "/" || char === "*") { + return stop_at("unexpected_a", line, from, char); + } + result = make("(regexp)", char); + result.flag = flag; + result.value = value; + if (multi_mode && !flag.m) { + warn_at("missing_m", line, column); + } + return result; + } + + function string(quote) { + +// Make a string token. + + let the_token; + snippet = ""; + next_char(); + + return (function next() { + if (char === quote) { + snip(); + the_token = make("(string)", snippet); + the_token.quote = quote; + return the_token; + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "\\") { + escape(quote); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char("`"); + } else { + next_char(); + } + return next(); + }()); + } + + function frack() { + if (char === ".") { + some_digits(rx_digits); + } + if (char === "E" || char === "e") { + next_char(); + if (char !== "+" && char !== "-") { + back_char(); + } + some_digits(rx_digits); + } + } + + function number() { + if (snippet === "0") { + next_char(); + if (char === ".") { + frack(); + } else if (char === "b") { + some_digits(rx_bits); + } else if (char === "o") { + some_digits(rx_octals); + } else if (char === "x") { + some_digits(rx_hexs); + } + } else { + next_char(); + frack(); + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + return stop_at( + "unexpected_a_after_b", + line, + column - 1, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + back_char(); + return make("(number)", snippet); + } + + function lex() { + let array; + let i = 0; + let j = 0; + let last; + let result; + let the_token; + +// This should properly be a tail recursive function, but sadly, conformant +// implementations of ES6 are still rare. This is the ideal code: + +// if (!source_line) { +// source_line = next_line(); +// from = 0; +// return ( +// source_line === undefined +// ? ( +// mega_mode +// ? stop_at("unclosed_mega", mega_line, mega_from) +// : make("(end)") +// ) +// : lex() +// ); +// } + +// Unfortunately, incompetent JavaScript engines will sometimes fail to execute +// it correctly. So for now, we do it the old fashioned way. + + while (!source_line) { + source_line = next_line(); + from = 0; + if (source_line === undefined) { + return ( + mega_mode + ? stop_at("unclosed_mega", mega_line, mega_from) + : make("(end)") + ); + } + } + + from = column; + result = source_line.match(rx_token); + +// result[1] token +// result[2] whitespace +// result[3] identifier +// result[4] number +// result[5] rest + + if (!result) { + return stop_at( + "unexpected_char_a", + line, + column, + source_line[0] + ); + } + + snippet = result[1]; + column += snippet.length; + source_line = result[5]; + +// Whitespace was matched. Call lex again to get more. + + if (result[2]) { + return lex(); + } + +// The token is an identifier. + + if (result[3]) { + return make(snippet, undefined, true); + } + +// The token is a number. + + if (result[4]) { + return number(snippet); + } + +// The token is a string. + + if (snippet === "\"") { + return string(snippet); + } + if (snippet === "'") { + if (!option.single) { + warn_at("use_double", line, column); + } + return string(snippet); + } + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (snippet === "`") { + if (mega_mode) { + return stop_at("expected_a_b", line, column, "}", "`"); + } + snippet = ""; + mega_from = from; + mega_line = line; + mega_mode = true; + +// Parsing a mega literal is tricky. First make a ` token. + + make("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + (function part() { + const at = source_line.search(rx_mega); + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + if (at < 0) { + snippet += source_line + "\n"; + return ( + next_line() === undefined + ? stop_at("unclosed_mega", mega_line, mega_from) + : part() + ); + } + snippet += source_line.slice(0, at); + column += at; + source_line = source_line.slice(at); + if (source_line[0] === "\\") { + snippet += source_line.slice(0, 2); + source_line = source_line.slice(2); + column += 2; + return part(); + } + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + make("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then make tokens that will become part of an expression until +// a } token is made. + + if (source_line[0] === "$") { + column += 2; + make("${"); + source_line = source_line.slice(2); + (function expr() { + const id = lex().id; + if (id === "{") { + return stop_at( + "expected_a_b", + line, + column, + "}", + "{" + ); + } + if (id !== "}") { + return expr(); + } + }()); + return part(); + } + }()); + source_line = source_line.slice(1); + column += 1; + mega_mode = false; + return make("`"); + } + +// The token is a // comment. + + if (snippet === "//") { + snippet = source_line; + source_line = ""; + the_token = comment(snippet); + if (mega_mode) { + warn("unexpected_comment", the_token, "`"); + } + return the_token; + } + +// The token is a /* comment. + + if (snippet === "/*") { + array = []; + if (source_line[0] === "/") { + warn_at("unexpected_a", line, column + i, "/"); + } + (function next() { + if (source_line > "") { + i = source_line.search(rx_star_slash); + if (i >= 0) { + return; + } + j = source_line.search(rx_slash_star); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + } + array.push(source_line); + source_line = next_line(); + if (source_line === undefined) { + return stop_at("unclosed_comment", line, column); + } + return next(); + }()); + snippet = source_line.slice(0, i); + j = snippet.search(rx_slash_star_or_slash); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + array.push(snippet); + column += i + 2; + source_line = source_line.slice(i + 2); + return comment(array); + } + +// The token is a slash. + + if (snippet === "/") { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + if (prior.identifier) { + if (!prior.dot) { + if (prior.id === "return") { + return regexp(); + } + if ( + prior.id === "(begin)" + || prior.id === "case" + || prior.id === "delete" + || prior.id === "in" + || prior.id === "instanceof" + || prior.id === "new" + || prior.id === "typeof" + || prior.id === "void" + || prior.id === "yield" + ) { + the_token = regexp(); + return stop("unexpected_a", the_token); + } + } + } else { + last = prior.id[prior.id.length - 1]; + if ("(,=:?[".indexOf(last) >= 0) { + return regexp(); + } + if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) { + the_token = regexp(); + warn("wrap_regexp", the_token); + return the_token; + } + } + if (source_line[0] === "=") { + column += 1; + source_line = source_line.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + } + return make(snippet); + } + + first = lex(); + json_mode = first.id === "{" || first.id === "["; + +// This loop will be replaced with a recursive call to lex when ES6 has been +// finished and widely deployed and adopted. + + while (true) { + if (lex().id === "(end)") { + break; + } + } +} + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + +function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!rx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!rx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property[id] === "number") { + property[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (tenure !== undefined) { + if (tenure[id] !== true) { + warn("unregistered_property_a", name); + } + } else { + if (name.identifier && rx_bad_property.test(id)) { + warn("bad_property_a", name); + } + } + property[id] = 1; + } + return id; +} + +function dispense() { + +// Deliver the next token, skipping the comments. + + const cadet = tokens[token_nr]; + token_nr += 1; + if (cadet.id === "(comment)") { + if (json_mode) { + warn("unexpected_a", cadet); + } + return dispense(); + } else { + return cadet; + } +} + +function lookahead() { + +// Look ahead one token without advancing. + + const old_token_nr = token_nr; + const cadet = dispense(true); + token_nr = old_token_nr; + return cadet; +} + +function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if (token.identifier && token.id !== "function") { + anon = token.id; + } else if (token.id === "(string)" && rx_identifier.test(token.value)) { + anon = token.value; + } + +// Attempt to match next_token with an expected id. + + if (id !== undefined && next_token.id !== id) { + return ( + match === undefined + ? stop("expected_a_b", next_token, id, artifact()) + : stop( + "expected_a_b_from_c_d", + next_token, + id, + artifact(match), + artifact_line(match), + artifact(next_token) + ) + ); + } + +// Promote the tokens, skipping comments. + + token = next_token; + next_token = dispense(); + if (next_token.id === "(end)") { + token_nr -= 1; + } +} + +// Parsing of JSON is simple: + +function json_value() { + let negative; + if (next_token.id === "{") { + return (function json_object() { + const brace = next_token; + const object = empty(); + const properties = []; + brace.expression = properties; + advance("{"); + if (next_token.id !== "}") { + (function next() { + let name; + let value; + if (next_token.quote !== "\"") { + warn( + "unexpected_a", + next_token, + next_token.quote + ); + } + name = next_token; + advance("(string)"); + if (object[token.value] !== undefined) { + warn("duplicate_a", token); + } else if (token.value === "__proto__") { + warn("bad_property_a", token); + } else { + object[token.value] = token; + } + advance(":"); + value = json_value(); + value.label = name; + properties.push(value); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("}", brace); + return brace; + }()); + } + if (next_token.id === "[") { + return (function json_array() { + const bracket = next_token; + const elements = []; + bracket.expression = elements; + advance("["); + if (next_token.id !== "]") { + (function next() { + elements.push(json_value()); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]", bracket); + return bracket; + }()); + } + if ( + next_token.id === "true" + || next_token.id === "false" + || next_token.id === "null" + ) { + advance(); + return token; + } + if (next_token.id === "(number)") { + if (!rx_JSON_number.test(next_token.value)) { + warn("unexpected_a"); + } + advance(); + return token; + } + if (next_token.id === "(string)") { + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + advance(); + return token; + } + if (next_token.id === "-") { + negative = next_token; + negative.arity = "unary"; + advance("-"); + advance("(number)"); + if (!rx_JSON_number.test(token.value)) { + warn("unexpected_a", token); + } + negative.expression = token; + return negative; + } + stop("unexpected_a"); +} + +// Now we parse JavaScript. + +function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + const id = name.id; + +// Reserved words may not be enrolled. + + if (syntax[id] !== undefined && id !== "ignore") { + warn("reserved_a", name); + } else { + +// Has the name been enrolled in this context? + + let earlier = functionage.context[id]; + if (earlier) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + +// Has the name been enrolled in an outer context? + + } else { + stack.forEach(function (value) { + const item = value.context[id]; + if (item !== undefined) { + earlier = item; + } + }); + if (earlier) { + if (id === "ignore") { + if (earlier.role === "variable") { + warn("unexpected_a", name); + } + } else { + if ( + ( + role !== "exception" + || earlier.role !== "exception" + ) + && role !== "parameter" + && role !== "function" + ) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + } + } + } + +// Enroll it. + + functionage.context[id] = name; + name.dead = true; + name.parent = functionage; + name.init = false; + name.role = role; + name.used = 0; + name.writable = !readonly; + } + } +} + +function expression(rbp, initial) { + +// This is the heart of the Pratt parser. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// nud Null denotation +// led Left denotation +// lbp Left binding power +// rbp Right binding power + +// It processes a nud (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop. It +// returns the expression's parse tree. + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement, + + if (!initial) { + advance(); + } + the_symbol = syntax[token.id]; + if (the_symbol !== undefined && the_symbol.nud !== undefined) { + left = the_symbol.nud(); + } else if (token.identifier) { + left = token; + left.arity = "variable"; + } else { + return stop("unexpected_a", token); + } + (function right() { + the_symbol = syntax[next_token.id]; + if ( + the_symbol !== undefined + && the_symbol.led !== undefined + && rbp < the_symbol.lbp + ) { + advance(); + left = the_symbol.led(left); + return right(); + } + }()); + return left; +} + +function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = next_token; + let the_value; + the_paren.free = true; + advance("("); + the_value = expression(0); + advance(")"); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (anticondition[the_value.id] === true) { + warn("unexpected_a", the_value); + } + return the_value; +} + +function is_weird(thing) { + return ( + thing.id === "(regexp)" + || thing.id === "{" + || thing.id === "=>" + || thing.id === "function" + || (thing.id === "[" && thing.arity === "unary") + ); +} + +function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + return ( + Array.isArray(b) + && a.length === b.length + && a.every(function (value, index) { + return are_similar(value, b[index]); + }) + ); + } + if (Array.isArray(b)) { + return false; + } + if (a.id === "(number)" && b.id === "(number)") { + return a.value === b.value; + } + let a_string; + let b_string; + if (a.id === "(string)") { + a_string = a.value; + } else if (a.id === "`" && a.constant) { + a_string = a.value[0]; + } + if (b.id === "(string)") { + b_string = b.value; + } else if (b.id === "`" && b.constant) { + b_string = b.value[0]; + } + if (typeof a_string === "string") { + return a_string === b_string; + } + if (is_weird(a) || is_weird(b)) { + return false; + } + if (a.arity === b.arity && a.id === b.id) { + if (a.id === ".") { + return ( + are_similar(a.expression, b.expression) + && are_similar(a.name, b.name) + ); + } + if (a.arity === "unary") { + return are_similar(a.expression, b.expression); + } + if (a.arity === "binary") { + return ( + a.id !== "(" + && are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + ); + } + if (a.arity === "ternary") { + return ( + are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + && are_similar(a.expression[2], b.expression[2]) + ); + } + if (a.arity === "function" && a.arity === "regexp") { + return false; + } + return true; + } + return false; +} + +function semicolon() { + +// Try to match a semicolon. + + if (next_token.id === ";") { + advance(";"); + } else { + warn_at( + "expected_a_b", + token.line, + token.thru, + ";", + artifact(next_token) + ); + } + anon = "anonymous"; +} + +function statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token.identifier && next_token.id === ":") { + the_label = token; + if (the_label.id === "ignore") { + warn("unexpected_a", the_label); + } + advance(":"); + if ( + next_token.id === "do" + || next_token.id === "for" + || next_token.id === "switch" + || next_token.id === "while" + ) { + enroll(the_label, "label", true); + the_label.init = true; + the_label.dead = false; + the_statement = statement(); + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token; + first.statement = true; + the_symbol = syntax[first.id]; + if (the_symbol !== undefined && the_symbol.fud !== undefined) { + the_symbol.disrupt = false; + the_symbol.statement = true; + the_statement = the_symbol.fud(); + } else { + +// It is an expression statement. + + the_statement = expression(0, true); + if (the_statement.wrapped && the_statement.id !== "(") { + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; +} + +function statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const array = []; + (function next(disrupt) { + if ( + next_token.id !== "}" + && next_token.id !== "case" + && next_token.id !== "default" + && next_token.id !== "else" + && next_token.id !== "(end)" + ) { + let a_statement = statement(); + array.push(a_statement); + if (disrupt) { + warn("unreachable_a", a_statement); + } + return next(a_statement.disrupt); + } + }(false)); + return array; +} + +function not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === global) { + warn("unexpected_at_top_level_a", thing); + } +} + +function top_level_only(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== global) { + warn("misplaced_a", the_thing); + } +} + +function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token; + the_block.arity = "statement"; + the_block.body = special === "body"; + +// Top level function bodies may include the "use strict" pragma. + + if ( + special === "body" + && stack.length === 1 + && next_token.value === "use strict" + ) { + next_token.statement = true; + advance("(string)"); + advance(";"); + } + stmts = statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option.devel && special !== "ignore") { + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; +} + +function mutation_check(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + warn("bad_assignment_a", the_thing); + return false; + } + return true; +} + +function left_check(left, right) { + +// Warn if the left is not one of these: +// e.b +// e[b] +// e() +// ?: +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !left_check(left.expression[1]) + && !left_check(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right); + return false; + } + return true; +} + +// These functions are used to specify the grammar of our language: + +function symbol(id, bp) { + +// Make a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax[id] = the_symbol; + } + return the_symbol; +} + +function assignment(id) { + +// Make an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led = function (left) { + const the_token = token; + let right; + the_token.arity = "assignment"; + right = expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "pre" + || right.arity === "post" + ) { + warn("unexpected_a", right); + } + mutation_check(left); + return the_token; + }; + return the_symbol; +} + +function constant(id, type, value) { + +// Make a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud = ( + typeof value === "function" + ? value + : function () { + token.constant = true; + if (value !== undefined) { + token.value = value; + } + return token; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; +} + +function infix(id, bp, f) { + +// Make an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, expression(bp)]; + return the_token; + }; + return the_symbol; +} + +function infixr(id, bp) { + +// Make a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + the_token.expression = [left, expression(bp - 1)]; + return the_token; + }; + return the_symbol; +} + +function post(id) { + +// Make one of the post operators. + + const the_symbol = symbol(id, 150); + the_symbol.led = function (left) { + token.expression = left; + token.arity = "post"; + mutation_check(token.expression); + return token; + }; + return the_symbol; +} + +function pre(id) { + +// Make one of the pre operators. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "pre"; + the_token.expression = expression(150); + mutation_check(the_token.expression); + return the_token; + }; + return the_symbol; +} + +function prefix(id, f) { + +// Make a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = expression(150); + return the_token; + }; + return the_symbol; +} + +function stmt(id, f) { + +// Make a statement. + + const the_symbol = symbol(id); + the_symbol.fud = function () { + token.arity = "statement"; + return f(); + }; + return the_symbol; +} + +function ternary(id1, id2) { + +// Make a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led = function (left) { + const the_token = token; + const second = expression(20); + advance(id2); + token.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, expression(10)]; + if (next_token.id !== ")") { + warn("use_open", the_token); + } + return the_token; + }; + return the_symbol; +} + +// Begin defining the language. + +syntax = empty(); + +symbol("}"); +symbol(")"); +symbol("]"); +symbol(","); +symbol(";"); +symbol(":"); +symbol("*/"); +symbol("await"); +symbol("case"); +symbol("catch"); +symbol("class"); +symbol("default"); +symbol("else"); +symbol("enum"); +symbol("finally"); +symbol("implements"); +symbol("interface"); +symbol("package"); +symbol("private"); +symbol("protected"); +symbol("public"); +symbol("static"); +symbol("super"); +symbol("void"); +symbol("yield"); + +constant("(number)", "number"); +constant("(regexp)", "regexp"); +constant("(string)", "string"); +constant("arguments", "object", function () { + warn("unexpected_a", token); + return token; +}); +constant("eval", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("false", "boolean", false); +constant("Function", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("ignore", "undefined", function () { + warn("unexpected_a", token); + return token; +}); +constant("Infinity", "number", Infinity); +constant("isFinite", "function", function () { + warn("expected_a_b", token, "Number.isFinite", "isFinite"); + return token; +}); +constant("isNaN", "function", function () { + warn("number_isNaN", token); + return token; +}); +constant("NaN", "number", NaN); +constant("null", "null", null); +constant("this", "object", function () { + if (!option.this) { + warn("unexpected_a", token); + } + return token; +}); +constant("true", "boolean", true); +constant("undefined", "undefined"); + +assignment("="); +assignment("+="); +assignment("-="); +assignment("*="); +assignment("/="); +assignment("%="); +assignment("&="); +assignment("|="); +assignment("^="); +assignment("<<="); +assignment(">>="); +assignment(">>>="); + +infix("??", 35); +infix("||", 40); +infix("&&", 50); +infix("|", 70); +infix("^", 80); +infix("&", 90); +infix("==", 100); +infix("===", 100); +infix("!=", 100); +infix("!==", 100); +infix("<", 110); +infix(">", 110); +infix("<=", 110); +infix(">=", 110); +infix("in", 110); +infix("instanceof", 110); +infix("<<", 120); +infix(">>", 120); +infix(">>>", 120); +infix("+", 130); +infix("-", 130); +infix("*", 140); +infix("/", 140); +infix("%", 140); +infixr("**", 150); +infix("(", 160, function (left) { + const the_paren = token; + let the_argument; + if (left.id !== "function") { + left_check(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (next_token.id !== ")") { + (function next() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + the_paren.free = true; + if (the_argument.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + the_paren.free = false; + } + return the_paren; +}); +infix(".", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("?.", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("[", 170, function (left) { + const the_token = token; + const the_subscript = expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + const name = survey(the_subscript); + if (rx_identifier.test(name)) { + warn("subscript_a", the_subscript, name); + } + } + left_check(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; +}); +infix("=>", 170, function (left) { + return stop("wrap_parameter", left); +}); + +function do_tick() { + const the_tick = token; + the_tick.value = []; + the_tick.expression = []; + if (next_token.id !== "`") { + (function part() { + advance("(string)"); + the_tick.value.push(token); + if (next_token.id === "${") { + advance("${"); + the_tick.expression.push(expression(0)); + advance("}"); + return part(); + } + }()); + } + advance("`"); + return the_tick; +} + +infix("`", 160, function (left) { + const the_tick = do_tick(); + left_check(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; +}); + +post("++"); +post("--"); +pre("++"); +pre("--"); + +prefix("+"); +prefix("-"); +prefix("~"); +prefix("!"); +prefix("!!"); +prefix("[", function () { + const the_token = token; + the_token.expression = []; + if (next_token.id !== "]") { + (function next() { + let element; + let ellipsis = false; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + element = expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]"); + return the_token; +}); +prefix("/=", function () { + stop("expected_a_b", token, "/\\=", "/="); +}); +prefix("=>", function () { + return stop("expected_a_before_b", token, "()", "=>"); +}); +prefix("new", function () { + const the_new = token; + const right = expression(160); + if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "()", artifact(next_token)); + } + the_new.expression = right; + return the_new; +}); +prefix("typeof"); +prefix("void", function () { + const the_void = token; + warn("unexpected_a", the_void); + the_void.expression = expression(0); + return the_void; +}); + +function parameter_list() { + const list = []; + let optional; + const signature = ["("]; + if (next_token.id !== ")" && next_token.id !== "(end)") { + (function parameter() { + let ellipsis = false; + let param; + if (next_token.id === "{") { + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("{"); + signature.push("{"); + (function subparameter() { + let subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (next_token.id === ":") { + advance(":"); + advance(); + token.label = subparam; + subparam = token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + } + if (next_token.id === "=") { + advance("="); + subparam.expression = expression(); + param.open = true; + } + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return subparameter(); + } + }()); + list.push(param); + advance("}"); + signature.push("}"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else if (next_token.id === "[") { + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("["); + signature.push("[]"); + (function subparameter() { + const subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + if (next_token.id === "=") { + advance("="); + subparam.expression = expression(); + param.open = true; + } + if (next_token.id === ",") { + advance(","); + return subparameter(); + } + }()); + list.push(param); + advance("]"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else { + if (next_token.id === "...") { + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + } + if (!next_token.identifier) { + return stop("expected_identifier_a"); + } + param = next_token; + list.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (next_token.id === "=") { + optional = param; + advance("="); + param.expression = expression(0); + } else { + if (optional !== undefined) { + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } + } + }()); + } + advance(")"); + signature.push(")"); + return [list, signature.join("")]; +} + +function do_function(the_function) { + let name; + if (the_function === undefined) { + the_function = token; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + name = next_token; + enroll(name, "variable", true); + the_function.name = name; + name.init = true; + name.calls = empty(); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + if (next_token.identifier) { + name = next_token; + the_function.name = name; + advance(); + } else { + the_function.name = anon; + } + } + } else { + name = the_function.name; + } + the_function.level = functionage.level + 1; + if (mega_mode) { + warn("unexpected_a", the_function); + } + +// Don't make functions in loops. It is inefficient, and it can lead to scoping +// errors. + + if (functionage.loop > 0) { + warn("function_in_loop", the_function); + } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + the_function.context = empty(); + the_function.finally = 0; + the_function.loop = 0; + the_function.switch = 0; + the_function.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functions.push(the_function); + functionage = the_function; + if (the_function.arity !== "statement" && typeof name === "object") { + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// Parse the parameter list. + + advance("("); + token.free = false; + token.arity = "function"; + [functionage.parameters, functionage.signature] = parameter_list(); + functionage.parameters.forEach(function enroll_parameter(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + name.names.forEach(enroll_parameter); + } + }); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && next_token.line === token.line + ) { + return stop("unexpected_a", next_token); + } + if ( + next_token.id === "." + || next_token.id === "?." + || next_token.id === "[" + ) { + warn("unexpected_a"); + } + +// Restore the previous context. + + functionage = stack.pop(); + return the_function; +} + +prefix("function", do_function); + +function fart(pl) { + advance("=>"); + const the_fart = token; + the_fart.arity = "binary"; + the_fart.name = "=>"; + the_fart.level = functionage.level + 1; + functions.push(the_fart); + if (functionage.loop > 0) { + warn("function_in_loop", the_fart); + } + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + the_fart.context = empty(); + the_fart.finally = 0; + the_fart.loop = 0; + the_fart.switch = 0; + the_fart.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functionage = the_fart; + the_fart.parameters = pl[0]; + the_fart.signature = pl[1]; + the_fart.parameters.forEach(function (name) { + enroll(name, "parameter", true); + }); + if (next_token.id === "{") { + warn("expected_a_b", the_fart, "function", "=>"); + the_fart.block = block("body"); + } else { + the_fart.expression = expression(0); + } + functionage = stack.pop(); + return the_fart; +} + +prefix("(", function () { + const the_paren = token; + let the_value; + const cadet = lookahead().id; + +// We can distinguish between a parameter list for => and a wrapped expression +// with one token of lookahead. + + if ( + next_token.id === ")" + || next_token.id === "..." + || (next_token.identifier && (cadet === "," || cadet === "=")) + ) { + the_paren.free = false; + return fart(parameter_list()); + } + the_paren.free = true; + the_value = expression(0); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + if (next_token.id === "=>") { + if (the_value.arity !== "variable") { + if (the_value.id === "{" || the_value.id === "[") { + warn("expected_a_before_b", the_paren, "function", "("); + return stop("expected_a_b", next_token, "{", "=>"); + } + return stop("expected_identifier_a", the_value); + } + the_paren.expression = [the_value]; + return fart([the_paren.expression, "(" + the_value.id + ")"]); + } + return the_value; +}); +prefix("`", do_tick); +prefix("{", function () { + const the_brace = token; + const seen = empty(); + the_brace.expression = []; + if (next_token.id !== "}") { + (function member() { + let extra; + let full; + let id; + let name = next_token; + let value; + advance(); + if ( + (name.id === "get" || name.id === "set") + && next_token.identifier + ) { + if (!option.getset) { + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + next_token.id; + name = next_token; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (next_token.id === "}" || next_token.id === ",") { + if (typeof extra === "string") { + advance("("); + } + value = expression(Infinity, true); + } else if (next_token.id === "(") { + value = do_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + advance("("); + } + let the_colon = next_token; + advance(":"); + value = expression(0); + if (value.id === name.id && value.id !== "function") { + warn("unexpected_a", the_colon, ": " + name.id); + } + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + advance(":"); + value = expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (next_token.id === ",") { + advance(","); + return member(); + } + }()); + } + advance("}"); + return the_brace; +}); + +stmt(";", function () { + warn("unexpected_a", token); + return token; +}); +stmt("{", function () { + warn("naked_block", token); + return block("naked"); +}); +stmt("break", function () { + const the_break = token; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (next_token.identifier && token.line === next_token.line) { + the_label = functionage.context[next_token.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + warn( + (the_label !== undefined && the_label.dead) + ? "out_of_scope_a" + : "not_label_a" + ); + } else { + the_label.used += 1; + } + the_break.label = next_token; + advance(); + } + advance(";"); + return the_break; +}); + +function do_var() { + const the_statement = token; + const is_const = the_statement.id === "const"; + the_statement.names = []; + +// A program may use var or let, but not both. + + if (!is_const) { + if (var_mode === undefined) { + var_mode = the_statement.id; + } else if (the_statement.id !== var_mode) { + warn( + "expected_a_b", + the_statement, + var_mode, + the_statement.id + ); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + warn("var_switch", the_statement); + } + if (functionage.loop > 0 && the_statement.id === "var") { + warn("var_loop", the_statement); + } + (function next() { + if (next_token.id === "{" && the_statement.id !== "var") { + const the_brace = next_token; + advance("{"); + (function pair() { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + survey(name); + advance(); + if (next_token.id === ":") { + advance(":"); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + next_token.label = name; + the_statement.names.push(next_token); + enroll(next_token, "variable", is_const); + advance(); + the_brace.open = true; + } else { + the_statement.names.push(name); + enroll(name, "variable", is_const); + } + name.dead = false; + name.init = true; + if (next_token.id === "=") { + advance("="); + name.expression = expression(); + the_brace.open = true; + } + if (next_token.id === ",") { + advance(","); + return pair(); + } + }()); + advance("}"); + advance("="); + the_statement.expression = expression(0); + } else if (next_token.id === "[" && the_statement.id !== "var") { + const the_bracket = next_token; + advance("["); + (function element() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + advance(); + the_statement.names.push(name); + enroll(name, "variable", is_const); + name.dead = false; + name.init = true; + if (ellipsis) { + name.ellipsis = true; + } else { + if (next_token.id === "=") { + advance("="); + name.expression = expression(); + the_bracket.open = true; + } + if (next_token.id === ",") { + advance(","); + return element(); + } + } + }()); + advance("]"); + advance("="); + the_statement.expression = expression(0); + } else if (next_token.identifier) { + const name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", is_const); + if (next_token.id === "=" || is_const) { + advance("="); + name.dead = false; + name.init = true; + name.expression = expression(0); + } + the_statement.names.push(name); + } else { + return stop("expected_identifier_a", next_token); + } + }()); + semicolon(); + return the_statement; +} + +stmt("const", do_var); +stmt("continue", function () { + const the_continue = token; + if (functionage.loop < 1 || functionage.finally > 0) { + warn("unexpected_a", the_continue); + } + not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; +}); +stmt("debugger", function () { + const the_debug = token; + if (!option.devel) { + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; +}); +stmt("delete", function () { + const the_token = token; + const the_value = expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; +}); +stmt("do", function () { + const the_do = token; + not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; +}); +stmt("export", function () { + const the_export = token; + let the_id; + let the_name; + let the_thing; + + function export_id() { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + the_id = next_token.id; + the_name = global.context[the_id]; + if (the_name === undefined) { + warn("unexpected_a"); + } else { + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a"); + } + exports[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + } + + the_export.expression = []; + if (next_token.id === "default") { + if (exports.default !== undefined) { + warn("duplicate_a"); + } + advance("default"); + the_thing = expression(0); + if ( + the_thing.id !== "(" + || the_thing.expression[0].id !== "." + || the_thing.expression[0].expression.id !== "Object" + || the_thing.expression[0].name.id !== "freeze" + ) { + warn("freeze_exports", the_thing); + } + if (next_token.id === ";") { + semicolon(); + } + exports.default = the_thing; + the_export.expression.push(the_thing); + } else { + if (next_token.id === "function") { + warn("freeze_exports"); + the_thing = statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a", the_name); + } + exports[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + warn("unexpected_a", next_token); + statement(); + } else if (next_token.id === "{") { + advance("{"); + (function loop() { + export_id(); + if (next_token.id === ",") { + advance(","); + return loop(); + } + }()); + advance("}"); + semicolon(); + } else { + stop("unexpected_a"); + } + } + module_mode = true; + return the_export; +}); +stmt("for", function () { + let first; + const the_for = token; + if (!option.for) { + warn("unexpected_a", the_for); + } + not_top_level(the_for); + functionage.loop += 1; + advance("("); + token.free = true; + if (next_token.id === ";") { + return stop("expected_a_b", the_for, "while (", "for (;"); + } + if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + return stop("unexpected_a"); + } + first = expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = expression(0); + advance(";"); + the_for.inc = expression(0); + if (the_for.inc.id === "++") { + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; +}); +stmt("function", do_function); +stmt("if", function () { + let the_else; + const the_if = token; + the_if.expression = condition(); + the_if.block = block(); + if (next_token.id === "else") { + advance("else"); + the_else = token; + the_if.else = ( + next_token.id === "if" + ? statement() + : block() + ); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + the_if.disrupt = true; + } else { + warn("unexpected_a", the_else); + } + } + } + return the_if; +}); +stmt("import", function () { + const the_import = token; + if (next_token.id === "(") { + the_import.arity = "unary"; + the_import.constant = true; + the_import.statement = false; + advance("("); + const string = expression(0); + if (string.id !== "(string)") { + warn("expected_string_a", string); + } + froms.push(token.value); + advance(")"); + advance("."); + advance("then"); + advance("("); + the_import.expression = expression(0); + advance(")"); + semicolon(); + return the_import; + } + let name; + if (typeof module_mode === "object") { + warn("unexpected_directive_a", module_mode, module_mode.directive); + } + module_mode = true; + if (next_token.identifier) { + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + const names = []; + advance("{"); + if (next_token.id !== "}") { + while (true) { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (next_token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token; + if (!rx_module.test(token.value)) { + warn("bad_module_name_a", token); + } + froms.push(token.value); + semicolon(); + return the_import; +}); +stmt("let", do_var); +stmt("return", function () { + const the_return = token; + not_top_level(the_return); + if (functionage.finally > 0) { + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (next_token.id !== ";" && the_return.line === next_token.line) { + the_return.expression = expression(10); + } + advance(";"); + return the_return; +}); +stmt("switch", function () { + let dups = []; + let last; + let stmts; + const the_cases = []; + let the_disrupt = true; + const the_switch = token; + not_top_level(the_switch); + if (functionage.finally > 0) { + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token.free = true; + the_switch.expression = expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + (function major() { + const the_case = next_token; + the_case.arity = "statement"; + the_case.expression = []; + (function minor() { + advance("case"); + token.switch = true; + const exp = expression(0); + if (dups.some(function (thing) { + return are_similar(thing, exp); + })) { + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (next_token.id === "case") { + return minor(); + } + }()); + stmts = statements(); + if (stmts.length < 1) { + warn("expected_statements_a"); + return; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "break;", + artifact(next_token) + ); + } + if (next_token.id === "case") { + return major(); + } + }()); + dups = undefined; + if (next_token.id === "default") { + const the_default = next_token; + advance("default"); + token.switch = true; + advance(":"); + the_switch.else = statements(); + if (the_switch.else.length < 1) { + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + const the_last = the_switch.else[the_switch.else.length - 1]; + if (the_last.id === "break" && the_last.label === undefined) { + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; +}); +stmt("throw", function () { + const the_throw = token; + the_throw.disrupt = true; + the_throw.expression = expression(10); + semicolon(); + if (functionage.try > 0) { + warn("unexpected_a", the_throw); + } + return the_throw; +}); +stmt("try", function () { + let the_catch; + let the_disrupt; + const the_try = token; + if (functionage.try > 0) { + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (next_token.id === "catch") { + let ignored = "ignore"; + the_catch = next_token; + the_try.catch = the_catch; + advance("catch"); + if (next_token.id === "(") { + advance("("); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + if (next_token.id !== "ignore") { + ignored = undefined; + the_catch.name = next_token; + enroll(next_token, "exception", true); + } + advance(); + advance(")"); + } + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "catch", + artifact(next_token) + ); + + } + if (next_token.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; +}); +stmt("var", do_var); +stmt("while", function () { + const the_while = token; + not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; +}); +stmt("with", function () { + stop("unexpected_a", token); +}); + +ternary("?", ":"); + +// Ambulation of the parse tree. + +function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; +} + +function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all of the +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + } + }; +} + +const posts = empty(); +const pres = empty(); +const preaction = action(pres); +const postaction = action(posts); +const preamble = amble(pres); +const postamble = amble(posts); + +function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.id === "function") { + walk_statement(thing.block); + } + if (thing.arity === "pre" || thing.arity === "post") { + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + warn("unexpected_statement_a", thing); + } + postamble(thing); + } + } +} + +function walk_statement(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_statement); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + && thing.id !== "import" + ) { + warn("unexpected_expression_a", thing); + } + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + } +} + +function lookup(thing) { + if (thing.arity === "variable") { + +// Look up the variable in the current context. + + let the_variable = functionage.context[thing.id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable === undefined) { + stack.forEach(function (outer) { + const a_variable = outer.context[thing.id]; + if ( + a_variable !== undefined + && a_variable.role !== "label" + ) { + the_variable = a_variable; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (the_variable === undefined) { + if (declared_globals[thing.id] === undefined) { + warn("undeclared_a", thing); + return; + } + the_variable = { + dead: false, + parent: global, + id: thing.id, + init: true, + role: "variable", + used: 0, + writable: false + }; + global.context[thing.id] = the_variable; + } + the_variable.closure = true; + functionage.context[thing.id] = the_variable; + } else if (the_variable.role === "label") { + warn("label_a", thing); + } + if ( + the_variable.dead + && ( + the_variable.calls === undefined + || functionage.name === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + ) { + warn("out_of_scope_a", thing); + } + return the_variable; + } +} + +function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); +} + +function preaction_function(thing) { + if (thing.arity === "statement" && blockage.body !== true) { + warn("unexpected_a", thing); + } + stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); +} + +function bitwise_check(thing) { + if (!option.bitwise && bitwiseop[thing.id] === true) { + warn("unexpected_a", thing); + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + warn("unexpected_a", thing); + } +} + +function pop_block() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); +} + +function activate(name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.init = true; + } + } + blockage.live.push(name); +} + +function action_var(thing) { + thing.names.forEach(activate); +} + +preaction("assignment", bitwise_check); +preaction("binary", bitwise_check); +preaction("binary", function (thing) { + if (relationop[thing.id] === true) { + const left = thing.expression[0]; + const right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + warn("expected_string_a", right); + } + } else { + const value = right.value; + if (value === "null" || value === "undefined") { + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + warn("expected_type_string_a", right, value); + } + } + } + } +}); +preaction("binary", "==", function (thing) { + warn("expected_a_b", thing, "===", "=="); +}); +preaction("binary", "!=", function (thing) { + warn("expected_a_b", thing, "!==", "!="); +}); +preaction("binary", "=>", preaction_function); +preaction("binary", "||", function (thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + warn("and", thang); + } + }); +}); +preaction("binary", "(", function (thing) { + const left = thing.expression[0]; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + const parent = functionage.name.parent; + if (parent) { + const left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + && left_variable.dead + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } +}); +preaction("binary", "in", function (thing) { + warn("infix_in", thing); +}); +preaction("binary", "instanceof", function (thing) { + warn("unexpected_a", thing); +}); +preaction("binary", ".", function (thing) { + if (thing.expression.new) { + thing.new = true; + } +}); +preaction("statement", "{", function (thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; +}); +preaction("statement", "for", function (thing) { + if (thing.name !== undefined) { + const the_variable = lookup(thing.name); + if (the_variable !== undefined) { + the_variable.init = true; + if (!the_variable.writable) { + warn("bad_assignment_a", thing.name); + } + } + } + walk_statement(thing.initial); +}); +preaction("statement", "function", preaction_function); +preaction("unary", "~", bitwise_check); +preaction("unary", "function", preaction_function); +preaction("variable", function (thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } +}); + +function init_variable(name) { + const the_variable = lookup(name); + if (the_variable !== undefined) { + if (the_variable.writable) { + the_variable.init = true; + return; + } + } + warn("bad_assignment_a", name); +} + +postaction("assignment", "+=", function (thing) { + let right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } +}); +postaction("assignment", function (thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + if (thing.id === "=") { + if (thing.names !== undefined) { + if (Array.isArray(thing.names)) { + thing.names.forEach(init_variable); + } else { + init_variable(thing.names); + } + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.writable !== true) { + warn("bad_assignment_a", lvalue); + } + } + const right = syntax[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + warn("unexpected_a", thing.expression[1]); + } + } +}); + +function postaction_function(thing) { + delete functionage.finally; + delete functionage.loop; + delete functionage.switch; + delete functionage.try; + functionage = stack.pop(); + if (thing.wrapped) { + warn("unexpected_parens", thing); + } + return pop_block(); +} + +postaction("binary", function (thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || are_similar(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option.convert) { + if (thing.expression[0].value === "") { + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === "." || thing.id === "?.") { + if (thing.expression.id === "RegExp") { + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } +}); +postaction("binary", "&&", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "||", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "=>", postaction_function); +postaction("binary", "(", function (thing) { + let left = thing.expression[0]; + let the_new; + let arg; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option.eval) { + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + warn( + "expected_a_before_b", + left, + "new", + artifact(left) + ); + } + } + } else if (left.id === ".") { + let cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + cack = !cack; + } + if (rx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + warn("unexpected_a", the_new); + } else { + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + const paren = left.expression; + if (paren.id === "(") { + const array = paren.expression; + if (array.length === 1) { + const new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } +}); +postaction("binary", "[", function (thing) { + if (thing.expression[0].id === "RegExp") { + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + warn("weird_expression_a", thing.expression[1]); + } +}); +postaction("statement", "{", pop_block); +postaction("statement", "const", action_var); +postaction("statement", "export", top_level_only); +postaction("statement", "for", function (thing) { + walk_statement(thing.inc); +}); +postaction("statement", "function", postaction_function); +postaction("statement", "import", function (the_thing) { + const name = the_thing.name; + if (name) { + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return top_level_only(the_thing); + } +}); +postaction("statement", "let", action_var); +postaction("statement", "try", function (thing) { + if (thing.catch !== undefined) { + const the_name = thing.catch.name; + if (the_name !== undefined) { + const the_variable = functionage.context[the_name.id]; + the_variable.dead = false; + the_variable.init = true; + } + walk_statement(thing.catch.block); + } +}); +postaction("statement", "var", action_var); +postaction("ternary", function (thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || are_similar(thing.expression[1], thing.expression[2]) + ) { + warn("unexpected_a", thing); + } else if (are_similar(thing.expression[0], thing.expression[1])) { + warn("expected_a_b", thing, "||", "?"); + } else if (are_similar(thing.expression[0], thing.expression[2])) { + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + warn("wrap_condition", thing.expression[0]); + } +}); +postaction("unary", function (thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option.convert) { + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } +}); +postaction("unary", "function", postaction_function); +postaction("unary", "+", function (thing) { + if (!option.convert) { + warn("expected_a_b", thing, "Number(...)", "+"); + } + const right = thing.expression; + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } +}); + +function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + if (id !== "ignore") { + const name = the_function.context[id]; + if (name.parent === the_function) { + if ( + name.used === 0 + && ( + name.role !== "function" + || name.parent.arity !== "unary" + ) + ) { + warn("unused_a", name); + } else if (!name.init) { + warn("uninitialized_a", name); + } + } + } + }); +} + +function uninitialized_and_unused() { + +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (module_mode === true || option.node) { + delve(global); + } + functions.forEach(delve); +} + +// Go through the token list, looking at usage of whitespace. + +function whitage() { + let closer = "(end)"; + let free = false; + let left = global; + let margin = 0; + let nr_comments_skipped = 0; + let open = true; + let opening = true; + let right; + + function pop() { + const previous = stack.pop(); + closer = previous.closer; + free = previous.free; + margin = previous.margin; + open = previous.open; + opening = previous.opening; + } + + function push() { + stack.push({ + closer, + free, + margin, + open, + opening + }); + } + + function expected_at(at) { + warn( + "expected_a_at_b_c", + right, + artifact(right), + fudge + at, + artifact_column(right) + ); + } + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function no_space() { + if (left.line === right.line) { + if (left.thru !== right.from && nr_comments_skipped === 0) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (open) { + const at = ( + free + ? margin + : margin + 8 + ); + if (right.from < at) { + expected_at(at); + } + } else { + if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line || !open) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (right.from !== margin) { + expected_at(margin); + } + } + } + + stack = []; + tokens.forEach(function (the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + + const new_closer = opener[left.id]; + if (typeof new_closer === "string") { + if (new_closer !== right.id) { + opening = left.open || (left.line !== right.line); + push(); + closer = new_closer; + if (opening) { + free = closer === ")" && left.free; + open = true; + margin += 4; + if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (right.switch) { + at_margin(-4); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + free = false; + open = false; + no_space_only(); + } + } else { + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like '{]') have already been detected. + + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } + } else { + if (right.statement === true) { + if (left.id === "else") { + one_space_only(); + } else { + at_margin(0); + open = false; + } + +// If right is a closer, then pop the previous state. + + } else if (right.id === closer) { + pop(); + if (opening && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-4); + } else if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + one_space(); + } else { + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + at_margin(0); + } else { + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + no_space(); + } else if ( + left.id === "." + || left.id === "?." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + ) { + no_space_only(); + } else if (right.id === "." || right.id === "?.") { + no_space_only(); + } else if (left.id === ";") { + if (open) { + at_margin(0); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + one_space_only(); + } else if ( + left.id === "var" + || left.id === "const" + || left.id === "let" + ) { + push(); + closer = ";"; + free = false; + open = left.open; + if (open) { + margin = margin + 4; + at_margin(0); + } else { + one_space_only(); + } + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +// The jslint function itself. + +export default Object.freeze(function jslint( + source = "", + option_object = empty(), + global_array = [] +) { + try { + warnings = []; + option = Object.assign(empty(), option_object); + anon = "anonymous"; + block_stack = []; + declared_globals = empty(); + directive_mode = true; + directives = []; + early_stop = true; + exports = empty(); + froms = []; + fudge = ( + option.fudge + ? 1 + : 0 + ); + functions = []; + global = { + id: "(global)", + body: true, + context: empty(), + from: 0, + level: 0, + line: 0, + live: [], + loop: 0, + switch: 0, + thru: 0 + }; + blockage = global; + functionage = global; + json_mode = false; + mega_mode = false; + module_mode = false; + next_token = global; + property = empty(); + shebang = false; + stack = []; + tenure = undefined; + token = global; + token_nr = 0; + var_mode = undefined; + populate(standard, declared_globals, false); + populate(global_array, declared_globals, false); + Object.keys(option).forEach(function (name) { + if (option[name] === true) { + const allowed = allowed_option[name]; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } + }); + tokenize(source); + advance(); + if (json_mode) { + tree = json_value(); + advance("(end)"); + } else { + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option.browser) { + if (next_token.id === ";") { + advance(";"); + } + } else { + +// If we are not in a browser, then the file form of strict pragma may be used. + + if ( + next_token.value === "use strict" + ) { + advance("(string)"); + advance(";"); + } + } + tree = statements(); + advance("(end)"); + functionage = global; + walk_statement(tree); + if (warnings.length === 0) { + uninitialized_and_unused(); + if (!option.white) { + whitage(); + } + } + } + if (!option.browser) { + directives.forEach(function (comment) { + if (comment.directive === "global") { + warn("missing_browser", comment); + } + }); + } + early_stop = false; + } catch (e) { + e.early_stop = early_stop; + e.message += " [JSLint was unable to finish]"; + if (e.name !== "JSLintError") { + warnings.push(e); + } + } + return { + directives, + edition: "2020-10-29", + exports, + froms, + functions, + global, + id: "(JSLint)", + json: json_mode, + lines, + module: module_mode === true, + ok: warnings.length === 0 && !early_stop, + option, + property, + shebang: ( + shebang + ? lines[0] + : undefined + ), + stop: early_stop, + tokens, + tree, + warnings: warnings.sort(function (a, b) { + return ( + a.early_stop + ? -1 + : b.early_stop + ? 1 + : a.line - b.line || a.column - b.column + ); + }) + }; +}); diff --git a/branch.sort_warnings_early_stop/report.js b/branch.sort_warnings_early_stop/report.js new file mode 100644 index 000000000..5e487d143 --- /dev/null +++ b/branch.sort_warnings_early_stop/report.js @@ -0,0 +1,222 @@ +// report.js +// 2018-10-22 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Generate JSLint HTML reports. + +/*property + closure, column, context, edition, error, exports, filter, forEach, freeze, + froms, fudge, function, functions, global, id, isArray, join, json, keys, + length, level, line, lines, message, module, name, names, option, + parameters, parent, property, push, replace, role, signature, sort, stop, + warnings +*/ + +const rx_amp = /&/g; +const rx_gt = />/g; +const rx_lt = / with less destructive entities. + + return String( + string + ).replace( + rx_amp, + "&" + ).replace( + rx_lt, + "<" + ).replace( + rx_gt, + ">" + ); +} + +export default Object.freeze({ + error: function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + let fudge = Number(Boolean(data.option.fudge)); + let output = []; + if (data.stop) { + output.push("
    JSLint was unable to finish.
    "); + } + data.warnings.forEach(function (warning) { + output.push( + "
    ", + entityify(warning.line + fudge), + ".", + entityify(warning.column + fudge), + "
    ", + entityify(warning.message), + "
    ", + entityify(data.lines[warning.line] || ""), + "" + ); + }); + return output.join(""); + }, + + function: function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + let fudge = Number(Boolean(data.option.fudge)); + let mode = ( + data.module + ? "module" + : "global" + ); + let output = []; + + if (data.json) { + return ( + data.warnings.length === 0 + ? "
    JSON: good.
    " + : "
    JSON: bad.
    " + ); + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + let global = Object.keys(data.global.context).sort(); + let froms = data.froms.sort(); + let exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + let context = the_function.context; + let list = Object.keys(context); + output.push( + "
    ", + entityify(the_function.line + fudge), + "
    ", + ( + the_function.name === "=>" + ? entityify(the_function.signature) + " =>" + : ( + typeof the_function.name === "string" + ? "«" + entityify(the_function.name) + "»" + : "" + entityify(the_function.name.id) + "" + ) + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + let params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.parent === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.parent === the_function + ); + })); + detail("outer", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.parent !== the_function + && the_variable.parent.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].parent.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + output.push( + "
    JSLint edition ", + entityify(data.edition), + "
    " + ); + return output.join(""); + }, + + property: function property_directive(data) { + +// Produce the /*property*/ directive. + + let not_first = false; + let output = ["/*property"]; + let length = 1111; + let properties = Object.keys(data.property); + + if (properties.length > 0) { + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length >= 80) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); + } + } +}); diff --git a/branch.unexpected_empty_lines/README b/branch.unexpected_empty_lines/README new file mode 100644 index 000000000..f46e91069 --- /dev/null +++ b/branch.unexpected_empty_lines/README @@ -0,0 +1,42 @@ +JSLint, The JavaScript Code Quality Tool + +Douglas Crockford +douglas@crockford.com + +2018-05-28 + +jslint.js contains the jslint function. It parses and analyzes a source file, +returning an object with information about the file. It can also take an object +that sets options. + +index.html runs the jslint.js function in a web page. The page also depends +browser.js and report.js and jslint.css. + +jslint.css provides styling for index.html. + +browser.js runs the web user interface. + +report.js generates the results reports in HTML. + +help.html describes JSLint's usage. Please read it. + +function.html describes the jslint function and the results it produces. + +Please direct questions and comments to +https://plus.google.com/communities/104441363299760713736. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[http://www.crockford.com/wrrrld/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. diff --git a/branch.unexpected_empty_lines/browser.js b/branch.unexpected_empty_lines/browser.js new file mode 100644 index 000000000..5086fed85 --- /dev/null +++ b/branch.unexpected_empty_lines/browser.js @@ -0,0 +1,158 @@ +// browser.js +// 2018-06-16 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +/*jslint + browser +*/ + +/*property + checked, create, disable, display, error, focus, forEach, function, + getElementById, innerHTML, join, length, map, onchange, onclick, onscroll, + property, querySelectorAll, scrollTop, select, split, style, title, value +*/ + +import jslint from "./jslint.js"; +import report from "./report.js"; + +// This is the web script companion file for JSLint. It includes code for +// interacting with the browser and displaying the reports. + +let rx_crlf = /\n|\r\n?/; +let rx_separator = /[\s,;'"]+/; + +let fudge_unit = 0; + +const aux = document.getElementById("JSLINT_AUX"); +const boxes = document.querySelectorAll("[type=checkbox]"); +const fudge = document.getElementById("JSLINT_FUDGE"); +const global = document.getElementById("JSLINT_GLOBAL"); +const number = document.getElementById("JSLINT_NUMBER"); +const property = document.getElementById("JSLINT_PROPERTY"); +const property_fieldset = document.getElementById("JSLINT_PROPERTYFIELDSET"); +const report_field = document.getElementById("JSLINT_REPORT"); +const report_list = document.getElementById("JSLINT_REPORT_LIST"); +const source = document.getElementById("JSLINT_SOURCE"); +const select = document.getElementById("JSLINT_SELECT"); +const warnings = document.getElementById("JSLINT_WARNINGS"); +const warnings_list = document.getElementById("JSLINT_WARNINGS_LIST"); + +function show_numbers() { + number.value = source.value.split(rx_crlf).map(function (ignore, index) { + return index + fudge_unit; + }).join("\n"); +} + +function clear() { + aux.style.display = "none"; + number.value = ""; + property.value = ""; + property_fieldset.style.display = "none"; + report_field.style.display = "none"; + report_list.innerHTML = ""; + source.focus(); + source.value = ""; + warnings.style.display = "none"; + warnings_list.innerHTML = ""; +} + +function fudge_change() { + fudge_unit = Number(fudge.checked); + show_numbers(); +} + +function clear_options() { + boxes.forEach(function (node) { + node.checked = false; + }); + fudge_change(); + global.innerHTML = ""; +} + +function call_jslint() { + +// First build the option object. + + let option = Object.create(null); + boxes.forEach(function (node) { + if (node.checked) { + option[node.title] = true; + } + }); + +// Call JSLint with the source text, the options, and the predefined globals. + + let global_string = global.value; + let result = jslint( + source.value, + option, + ( + global_string === "" + ? undefined + : global_string.split(rx_separator) + ) + ); + +// Generate the reports. + + let error_html = report.error(result); + let function_html = report.function(result); + let property_text = report.property(result); + +// Display the reports. + + warnings_list.innerHTML = error_html; + warnings.style.display = ( + error_html.length === 0 + ? "none" + : "block" + ); + + report_list.innerHTML = function_html; + report_field.style.display = "block"; + if (property_text) { + property.value = property_text; + property_fieldset.style.display = "block"; + property.scrollTop = 0; + select.disable = false; + } else { + property_fieldset.style.display = "none"; + select.disable = true; + } + aux.style.display = "block"; + source.select(); +} + +fudge.onchange = fudge_change; + +source.onchange = function (ignore) { + show_numbers(); +}; + +source.onscroll = function () { + let ss = source.scrollTop; + number.scrollTop = ss; + let sn = number.scrollTop; + if (ss > sn) { + show_numbers(); + number.scrollTop = ss; + } +}; + +document.querySelectorAll("[name='JSLint']").forEach(function (node) { + node.onclick = call_jslint; +}); + +document.querySelectorAll("[name='clear']").forEach(function (node) { + node.onclick = clear; +}); + +document.getElementById("JSLINT_SELECT").onclick = function () { + property.focus(); + property.select(); +}; +document.getElementById("JSLINT_CLEAR_OPTIONS").onclick = clear_options; + +fudge_change(); +source.select(); +source.focus(); diff --git a/branch.unexpected_empty_lines/function.html b/branch.unexpected_empty_lines/function.html new file mode 100644 index 000000000..8d7287cbc --- /dev/null +++ b/branch.unexpected_empty_lines/function.html @@ -0,0 +1,612 @@ + + + + + + + + +JSLint: jslint function + + + +
    JSLint
    + +
    The jslint Function
    +
    +

    JSLint

    +

    JSLint is delivered as a set of files:

    +
    + + + + + + + + + + + + + + +
    FilenameContent
    browser.jsBrowser based UI.
    function.htmlThis page that you are looking at right now.
    help.htmlUsing jslint as a single page JSLint application.
    index.htmlSingle page JSLint application that uses the + js files.
    jslint.jsThe jslint function.
    report.jsThe report object, containing report generator functions for + HTML.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    +
    + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring"(JSLint)"
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    parenttokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    parenttokenThe function in which the variable was declared.variable
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable can be assigned to.variable
    +

    Reports

    +

    The report object contains three functions that all take a + result from the jslint function as input. + report.js has no other dependence on other files.

    + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    +
    + +
    + + + + + +
    + + diff --git a/branch.unexpected_empty_lines/help.html b/branch.unexpected_empty_lines/help.html new file mode 100644 index 000000000..ee236ff95 --- /dev/null +++ b/branch.unexpected_empty_lines/help.html @@ -0,0 +1,841 @@ + + + + + + + + + +JSLint: Help + + + +
    JSLint
    + +
    Help
    +
    +
    Il semble que la perfection soit atteinte non quand il n’y a + plus rien à ajouter, mais quand il n’y a plus rien à + retrancher.
    +
    + Antoine de Saint-Exupéry +
    +
    + Terre des Hommes (1939) +
    +

    Good Parts

    +

    The Principle of the Good Parts is

    +
    If a feature is sometimes useful and sometimes dangerous and if + there is a better option then always use the better option.
    +

    JSLint is a JavaScript program that looks for problems in + JavaScript programs. It is a code quality tool.

    +

    When C was + a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    +

    As the language matured, the definition of the language was + strengthened to eliminate some insecurities, and compilers got better + at issuing warnings. lint is no longer needed.

    +

    JavaScript is a + young-for-its-age language. It was originally intended to do small tasks in + webpages, tasks for which Java was too heavy and clumsy. But JavaScript is + a surprisingly capable language, and it is now being used in larger + projects. Many of the features that were intended to make the language easy + to use are troublesome when projects become complicated. A lint + for JavaScript is needed: JSLint, a JavaScript syntax checker + and validator.

    +

    JSLint takes a JavaScript source and scans it. If it finds a + problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by the ECMAScript Programming Language + Standard (the strangely named document that governs JavaScript). + JSLint will reject most legal programs. It is a higher + standard.

    +

    JavaScript is a sloppy language, but hidden deep inside there is an elegant, + better language. JSLint helps you to program in that better + language and to avoid most of the slop. JSLint will reject + programs that browsers will accept because JSLint is concerned + with the quality of your code and browsers are not. You should gladly accept + all of JSLint's advice.

    +

    JSLint can operate on JavaScript source + or JSON text.

    +

    ECMAScript Sixth Edition

    +

    Some of + ES6’s features are good, so JSLint will recognize the good + parts of ES6.

    +

    Currently, these features are recognized:

    +
      +
    • The ... ellipsis marker in parameter + lists and argument lists, replacing the arguments object + for variadic functions.
    • +
    • The let statement, which is like the var + statement except that it respects block scope. + You may use let or var but not both.
    • +
    • The const statement is like the let statement + except that it disallows the use of assignment on the variable, although + if the value of the variable is mutable, it can still be mutated. + const is preferred to let. +
    • Destructuring of arrays and objects is allowed in parameter lists and on + the left side of let, and const, but not + var or assignment statements, and not deep destructuring + or eliding.
    • +
    • Enhanced object literals, providing shorter forms for function + declaration and properties that are initialized by variables with the + same name.
    • +
    • The fat arrow => fart functions.
    • +
    • The simplest forms of import and export.
    • +
    • `Megastring` literals, but not nested + `megastring` literals.
    • +
    • New global functions, such as Map, Set, + WeakMap, and WeakSet.
    • +
    • 0b- and 0o- number literals.
    • +
    +

    The most important new feature of ES6 is proper tail calls. This has no + new syntax, so JSLint doesn’t see it. But it makes recursion + much more attractive, which makes loops, particularly for + loops, much less attractive.

    +

    import export

    +

    The ES6 module feature will be an important improvement over JavaScript’s + global variables as a means of linking separate files together. + JSLint recognizes a small but essential subset of the module + syntax.

    +
    +

    import name from stringliteral;

    +

    import {name} from stringliteral;

    +

    export default function () {};

    +

    export default function name() {};

    +

    export default expression;

    +

    export function name() {}

    +

    export name; // where name is const

    +

    export {name};

    +
    +

    Directives

    +

    JSLint provides three directives that may be placed in a + file to manage JSLint’s behavior. Use of these directives is + optional. If they are used, they should be placed in a source file before + the first statement. They are written in the form of a comment, where the + directive name is placed immediately after the opening of the comment before + any whitespace. The three directives are global, + jslint, and property. Directives in a file are + stronger than options selected from the UI or passed with the option object.

    +

    /*global*/

    +

    The /*global*/ directive is used to specify a set of globals + (usually functions and objects containing functions) that are available to + this file. This was commonly used in browsers to link source files together + before ES6 modules appeared. Use of global variables is strongly + discouraged, but unfortunately web browsers require their use. The + /*global*/ directive can only be used when the + Assume a browser option is selected. +

    Each of the names listed will indicate a read-only global variable. The names + are separated by , comma. This directive + should not be used if import or export is used. + This directive inhibits warnings, but it does not declare the names in the + execution environment. +

    For example: +

    /*global +
    ADSAFE, report, jslint
    +*/
    +

    instructs JSLint to not give warnings about the global + variables ADsafe, report, and jslint. + However, if any of those names are expected to be supplied by other files + and those other files fail to do so, then execution errors will result. It + is usually better to use top-level variable declarations instead: +

        var ADSAFE;
    +    var report;
    +    var jslint; 
    +

    Using var in this way allows comparing a global variable to the + undefined value to determine whether it is has been used in the global context.

    +

    /*jslint*/

    +

    The /*jslint*/ directive allows for the control of several + options. These options can also be set from the + JSLint.com user interface.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Tolerate bitwise operatorsbitwisetrue if bitwise operators should be allowed. The + bitwise operators are rarely used in JavaScript programs, so it + is usually more likely that & is a mistyping of + && than that it indicates a bitwise and. + JSLint will give warnings on the bitwise operators + unless this option is selected.
    Assume a browser browsertrue if the standard browser globals should be + predefined. This option will reject the use of + import and export. This option also + disallows the file form of the "use + strict" pragma. It does not supply + self; you will have to request that unnecessary + alias of the dreaded global object yourself. It adds the same + globals as this directive: +
    /*global +
    + clearInterval, clearTimeout, document, event, FileReader, + FormData, history, localStorage, location, name, navigator, + screen, sessionStorage, setInterval, setTimeout, Storage, + URL, window, XMLHttpRequest +
    + */
    +
    Tolerate conversion operatorsconverttrue if !!, + prefix, + and concatenation with "" are allowed. + The Boolean, Number, and + String functions are preferred
    Assume CouchDBcouchtrue if + Couch DB + globals should be predefined. It adds the same globals as this + directive: +
    /*global +
    emit, getRow, isArray, log, provides, registerType, require, + send, start, sum, toJSON
    + */
    Assume in developmentdeveltrue if browser globals that are useful in + development should be predefined, and if debugger + statements and TODO comments + should be allowed. It adds the + same globals as this directive: +
    /*global +
    alert, confirm, console, prompt
    + */
    Be sure to turn this option off before going + into production.
    Tolerate evalevaltrue if eval should be allowed. In the + past, the eval function was the most misused + feature of the language.
    Tolerate for statementfortrue if the for statement should be + allowed. It is almost always better to use the array methods + instead.
    Fudge the line and column numbersfudgetrue if the origin of a text file is line 1 column + 1. Programmers know that number systems start at zero. + Unfortunately, most text editors start numbering lines and + columns at one. JSLint will by default start + numbering at zero. Use this option to start the numbering at one + in its reports.
    Tolerate get and setgetsettrue if accessor properties are allowed in object literals.
    Tolerate long source lineslongtrue if a line can contain more than 80 characters.
    Tolerate multiple variables declarations per statementmultivartrue if a var, let, or + const statement can declare two or more variables + in a single statement.
    Assume Node.jsnodetrue if Node.js globals should be predefined. It + will predefine globals that are used in the Node.js environment. + It adds the same globals as this directive: +
    /*global +
    + Buffer, clearImmediate, clearInterval, clearTimeout, console, exports, + module, process, require, setImmediate, setInterval, setTimeout, URL, + URLSearchParams, __dirname, __filename +
    + */
    Tolerate single quote stringssingletrue if 'single quote + should be allowed to enclose string literals.
    Tolerate this
    thistrue if this should be allowed.
    Tolerate whitespace messwhitetrue if the whitespace rules should be ignored.
    +

    For example:

    +
    /*jslint
    +    bitwise, node
    +*/
    + +

    /*property*/

    +

    The /*property*/ directive is used to declare a list of + property identifiers that are used in the file. Each property name in the + program is looked up in this list. If a name is not found, that indicates an + error, most likely a typing error.

    +

    The list can also be used to evade some of JSLint’s naming + rules.

    +

    JSLint can build the /*property*/ list for you. At + the bottom of its report, JSLint displays a + /*property*/ directive. It contains all of the names that were + used with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. You + can copy the /*property*/ directive to the top of your + script file. JSLint will check the spelling of all property + names against the list. That way, you can have JSLint look for + misspellings for you.

    +

    For example,

    +
    /*property +
    charAt, slice, _$_
    + */
    +

    ignore

    +

    JSLint introduces a new reserved word: ignore. It + is used in parameter lists and in catch clauses to indicate a + parameter that will be ignored. Unused warnings will not be produced for + ignore. +

    function handler(ignore, value) {
    +    return do_something_useful(value);
    +}
    +

    var let const

    +

    JavaScript provides three statements for declaring variables: + var, let, and const. The + var statement suffers from a bad practice called hoisting. The + let statement does not do hoisting and respects block scope. + The const statement is like let except that it + marks the variable (but not its contents) as read only, making it an error + to attempt to assign to the variable. When given a choice, + const is the best, var is the worst.

    +

    JSLint uses the intersection of the var rules and the + let rules, and by doing so avoids the errors related to either. + A name should be declared only once in a function. It should be declared + before it is used. It should not be used outside of the block in which it is + declared. A variable should not have the same name as a variable or + parameter in an outer function. Do not mix var and + let. Declare one name per statement.

    +

    = == ===

    +

    Fortran made a terrible + mistake in using the equality operator as its assignment operator. That + mistake has been replicated in most languages since then. C compounded that + mistake by making == its equality operator. The visual + similarity is a source of errors. JavaScript compounded this further by + making == a type coercing comparison operator that produces + false positive results. This was mitigated by adding the === + operator, leaving the broken == operator in place.

    +

    JSLint attempts to minimize errors by the following rules:

    +

    == is not allowed. This avoids the false positives, and + increases the visual distance between = and ===. + Assignments are not allowed in expression position, and comparisons are not + allowed in statement position. This also reduces confusion. 

    +

    Semicolon

    +

    JavaScript uses a C-like syntax that requires the use of semicolons to + delimit certain statements. JavaScript attempts to make those semicolons + optional with an automatic semicolon insertion mechanism, but it does not + work very well. Automatic semicolon insertion was added to make + things easier for beginners. Unfortunately, it sometimes fails. Do not rely + on it unless you are a beginner.

    +

    JSLint expects that every statement will be followed by + ; except for for, function, + if, switch, try, and + while. JSLint does not expect to see unnecessary + semicolons, the empty statement, or empty blocks.

    +

    function =>

    +

    JavaScript has four syntactic forms for making function objects: function + statements, function expressions, enhanced object literals, and the + => fart operator.

    +

    The function statement creates a variable and assigns the function object to + it. It should be used in a file or function body, but not inside of a block.

    +
    function name(parameters) { +
    statements
    + }
    +

    The function expression unfortunately looks like the function statement. It + may appear anywhere that an expression may appear, but not in statement + position and not in a loop. It produces a function object but does not + create a variable in which to store it.

    +
    function (parameters) { +
    statements
    + }
    +

    The enhanced object literal provides an ES6 shorthand for creating a property + whose value is a function, saving you from having to type + : colon and function.

    +
    { +
    name(parameters) { +
    statements
    + }
    + }
    +

    Finally, ES6 provides an even shorter form of function expression that leaves + out the words function and return:

    +
    (parameters) => + expression
    +

    JSLint requires the parens around the parameters, and forbids a + { left brace after the + => fart to avoid syntactic ambiguity.

    +

    Comma

    +

    The , comma operator is unnecessary and can + mask programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as + an operator. It does not expect to see elided elements in array literals. A + comma should not appear after the last element of an array literal or object + literal.

    +

    Blocks

    +

    JSLint expects blocks with function, + if, switch, while, for, + do, and try statements and nowhere else.

    +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    +

    JavaScript allows an if to be written like this:

    +
    if (condition)
    +    statement;
    +

    That form is known to contribute to mistakes in projects where many + programmers are working on the same code. That is why JSLint + expects the use of a block:

    +
    if (condition) {
    +    statements;
    +}
    +

    Experience shows that this form is more resilient.

    +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call. All other expression statements are considered + to be errors.

    +

    for

    +

    JSLint does not recommend use of the for statement. + Use array methods like forEach instead. The for + option will suppress some warnings. The forms of for that + JSLint accepts are restricted, excluding the new ES6 forms.

    +

    for in

    +

    JSLint does not recommend use of the for + in statement. Use Object.keys instead.

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, it also + loops through all of the properties that were inherited through the + prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written + without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    for (name in object) {
    +    if (object.hasOwnProperty(name)) {
    +        ....
    +    }
    +}
    +

    Note that the above code will fail if the object contains a property + named hasOwnProperty. Use Object.keys instead.

    +

    switch

    +

    A common error in switch statements is to forget to place a + break statement after each case, resulting in unintended + fall-through. JSLint expects that the statement before the next + case or default is one of these: + break, return, or throw.

    +

    with

    +

    The with statement was intended to provide a shorthand in + accessing properties in deeply nested objects. Unfortunately, it behaves + very badly when setting new properties. Never use the with + statement.

    +

    JSLint does not expect to see a with statement.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that + labels will be distinct from vars and parameters.

    +

    Unreachable Code

    +

    JSLint expects that a return, break, + or throw statement will be followed by a + } right brace or case or + default.

    +

    Confusing Pluses and Minuses

    +

    JSLint expects that + will not be followed by + + or ++, and that - will not be + followed by - or --. A misplaced space can turn + + + into ++, an error that is difficult to see. + Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ increment and + -- decrement operators have been known to + contribute to bad code by encouraging excessive trickiness. They are second + only to faulty architecture in enabling to viruses and other security + menaces. Also, preincrement/postincrement confusion can produce off-by-one + errors that are extremely difficult to diagnose. Fortunately, they are also + complete unnecessary. There are better ways to add 1 to a variable.

    +

    It is best to avoid these operators entirely and rely on += and + -= instead.

    +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. + JSLint looks for problems that may cause portability problems. + It also attempts to resolve visual ambiguities by recommending explicit + escapement.

    +

    JavaScript’s syntax for regular expression literals overloads the + / slash character. To avoid ambiguity, + JSLint expects that the character preceding a regular + expression literal is a ( left paren or + = equal or + : colon or + , comma character.

    +

    this

    +
    Having this in the language makes it harder to talk + about the language. It is like pair programming with Abbott and Costello. +
    +

    Avoid using this. Warnings about this can be + suppressed with option.this.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the + new prefix. The new prefix creates a new object + based on the function's prototype, and binds that object to the + function's implied this parameter. If you neglect to use the + new prefix, no new object will be made and this + will be bound to the global object.

    +

    JSLint enforces the convention that constructor functions be + given names with initial uppercase. JSLint does not expect to + see a function invocation with an initial uppercase name unless it has the + new prefix. JSLint does not expect to see the + new prefix used with functions whose names do not start with + initial uppercase.

    +

    JSLint does not expect to see the wrapper forms + new Number, new String, new Boolean.

    +

    JSLint does not expect to see new Object. + Use Object.create(null) instead.

    +

    Whitespace

    +

    JSLint has a specific set of rules about the use of whitespace. + Where possible, these rules are consistent with centuries of good practice + with literary style.

    +

    The indentation increases by 4 spaces when the last token on a line is + { left brace, + [ left bracket, + ( left paren. The matching closing + token will be the first token on a line, restoring the previous + indentation.

    +

    The ternary operator can be visually confusing, so + ? question mark and + : colon always begin a line and increase + the indentation by 4 spaces.

    +
    return (the_token.id === "(string)" || the_token.id === "(number)")
    +    ? String(the_token.value)
    +    : the_token.id;
    +

    If . period is the first character on a line, the + indentation is increased by 4 spaces.

    +

    A var, let, or const statement will + also cause an indentation if it declares two or more variables, and if the + second variable is not on the same line as the start of the statement.

    +

    Long lines can be continued on the next line with 8 spaces added to the + current indentation. Those 8 spaces do not change the current indentation. + It is 8 to avoid confusion with ordinary indentation.

    +

    The word function is always followed with one space.

    +

    Clauses (case, catch, default, + else, finally) are not statements and so should + not be indented like statements.

    +

    Spaces are used to make things that are not invocations look less like + invocations.

    +

    Tabs and spaces should not be mixed. We should pick just one in order to + avoid the problems that come from having both. Personal preference is an + extremely unreliable criterion. Neither offers a powerful advantage over the + other. Fifty years ago, tab had the advantage of consuming less memory, but + Moore's Law has eliminated that advantage. Space has one clear advantage + over tab: there is no reliable standard for how many spaces a tab + represents, but it is universally accepted that a space occupies a space. So + use spaces. You can edit with tabs if you must, but make sure it is spaces + again before you commit. Maybe someday we will finally get a universal + standard for tabs, but until that day comes, the better choice is spaces.

    +

    Unsafe Characters

    +

    There are characters that are handled inconsistently in some browsers, and + so must be escaped when placed in strings.

    +
    \u0000-\u001f
    +\u007f-\u009f
    +\u00ad
    +\u0600-\u0604
    +\u070f
    +\u17b4
    +\u17b5
    +\u200c-\u200f
    +\u2028-\u202f
    +\u2060-\u206f
    +\ufeff
    +\ufff0-\uffff
    +

    Report

    +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    +
      +
    • The line number on which it starts.
    • +
    • The function’s name. In the case of anonymous functions, + JSLint will attempt to + «guess» the name.
    • +
    • The parameters.
    • +
    • Variables: The variables that are declared in the function.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Exceptions: The variables that are declared by catch + clauses of try statements.
    • +
    • Outer: Variables used by this function that are declared in + other functions.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements. Updates are announced at + https://plus.google.com/communities/104441363299760713736.

    +

    Try it

    +

    Try it. Paste your script + into the window and click the + + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    JSLint is written entirely in JavaScript, so it can run + anywhere that JavaScript (or even Java) can run.

    +

    Perfectly fine

    +

    JSLint was designed to reject code that some would consider to + be perfectly fine. The reason for this is that JSLint's + purpose is to help produce programs that are free of error. That is + difficult in any language and is especially hard in JavaScript. + JSLint attempts to help you increase the visual distance + between correct programs and incorrect programs, making the remaining errors + more obvious. JSLint will give warnings about things that are + not necessarily wrong in the current situation, but which have been observed + to mask or obscure errors. Avoid those things when there are better options + available.

    +

    It is dangerous out there. JSLint is here to help.

    +

    Warning

    +

    JSLint will hurt your feelings. Side effects may include + headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, + dry mouth, cleaner code, and a reduced error rate.

    +

    Further Reading

    + +
    +

    + Code Conventions for the JavaScript Programming Language.

    + + + + + +
    + + diff --git a/branch.unexpected_empty_lines/index.html b/branch.unexpected_empty_lines/index.html new file mode 100644 index 000000000..235f44b54 --- /dev/null +++ b/branch.unexpected_empty_lines/index.html @@ -0,0 +1,125 @@ + + + + + + + + + + + +JSLint: The JavaScript Code Quality Tool + + +
    +
    Source
    +
    + + +
    +
    + Warnings +
    +
    +
    + Function Report +
    +
    +
    + Property Directive + +
    +
    + + + + +
    +
    Options +
    Assume... +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Global variables... + +
    +
    +
    +
    + + + + + +
    + + diff --git a/branch.unexpected_empty_lines/jslint.css b/branch.unexpected_empty_lines/jslint.css new file mode 100644 index 000000000..9c1b1a2f9 --- /dev/null +++ b/branch.unexpected_empty_lines/jslint.css @@ -0,0 +1,340 @@ +@font-face { + font-family: 'Programma'; + src: url('Programma.woff') format('woff'); +} +@font-face { + font-family: 'Daley'; + font-weight: bold; + src: url('Daley-Bold.woff') format('woff'); +} + +body { + background-color: antiquewhite; + color: black; + font-family: 'Daley', sans-serif; + margin: 0; + padding: 0; +} + +#JSLINT_TITLEBOX { + font-family: 'Daley', sans-serif; + margin: 0; + margin-left: 3%; + margin-right: 3%; + padding: 0; +} + +#JSLINT_TITLEBOX ul { + float: right; + font-size: 12pt; +} + +#JSLINT_TITLEBOX div { + color: darkslategray; + float: left; + font-size: 48pt; +} + +#JSLINT_ fieldset { + background-color: gainsboro; + border: 0; + clear: both; + margin-bottom: 1em; + margin-left: 3%; + margin-right: 3%; + margin-top: 1em; + padding: 0; + width: auto; +} +#JSLINT_ legend { + background-color: darkslategray; + border: 0; + color: white; + font-size: 100%; + font-style: normal; + font-weight: normal; + margin: 0; + padding-bottom: 0.25em; + padding-left: 0; + padding-right: 0; + padding-top: 0.25em; + text-align: center; + width: 100%; +} +#JSLINT_ button { + background-color: darkslategray; + border: 0; + color: white; + cursor: pointer; + font-family: 'Daley', sans-serif; + font-size: 100%; + font-style: normal; + margin-left: 1em; + margin-right: 1em; + padding-left: 1em; + padding-right: 1em; + padding-bottom: 0.25em; + padding-top: 0.25em; + text-align: center; +} +#JSLINT_ button:hover { + background-color: slategray; + color: white; + border: 0; +} + +#JSLINT_ button:active { + border: 0; + color: black; +} + +#JSLINT_ button:disabled { + background-color: gray; + border: 0; + color: gainsboro; +} + +a:visited { + color: #69565c; +} + +a:link { + color: black; +} + +#JSLINT_ input[type="text"] { + background-color: white; + border: 0; + color: black; + margin-bottom: 0.2em; + margin-right: 0.5em; + padding-left: 0.5em; + padding-right: 0.5em; + text-align: right; + width: 3em; +} + +#JSLINT_ textarea { + border: 0; + background-color: white; + border: 0; + font-family: Programma, monospace; + font-size: 100%; + font-weight: bold; + resize: none; +} + +#JSLINT_ textarea::selection { + background-color: yellow; + color: black; +} + +#JSLINT_NUMBER { + color: darkgray; + display: inline-block; + height: 4in; + margin: 2%; + margin-right: 0; + overflow: hidden; + padding-right: 0.5%; + text-align: right; + white-space: pre; + width: 4%; +} + +#JSLINT_SOURCE { + color: black; + display: inline-block; + font-size: 100%; + height: 4in; + margin: 2%; + margin-left: 0; + margin-right: 0; + overflow: auto; + padding-left: 0.5%; + white-space: pre; + width: 91%; +} + +#JSLINT_PROPERTY { + background-color: honeydew; + border: 0; + margin: 2%; + padding: 0.5%; + white-space: pre; + width: 95%; +} + +#JSLINT_GLOBAL { + white-space: normal; + width: 95%; +} +#JSLINT_ label { + font-family: sans-serif; + font-size: 90%; + padding-left: 0.25em; +} +#JSLINT_OPTIONS>div { + float: left; + margin: 0.5em; +} + +#JSLINT_ address { + color: black; + display: block; + float: right; + font-family: serif; + font-size: 90%; + margin-left: 1em; +} +#JSLINT_ dl { + background-color: cornsilk; + color: white; + margin: 0; + padding-bottom: 2pt; + padding-left: 1em; + padding-right: 1em; + padding-top: 2pt; +} +#JSLINT_ dfn { + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + margin-bottom: 2pt; +} +#JSLINT_ dt { + color: black; + display: block; + float: left; + font-family: serif; + font-size: 75%; + font-style: italic; + margin: 0; + width: 8em; + text-align: right; +} +#JSLINT_ dd { + color: black; + display: block; + font-family: Programma, monospace; + font-weight: bold; + margin-left: 8em; + padding-bottom: 2pt; +} +#JSLINT_WARNINGS>legend { + background-color: indianred; +} +#JSLINT_WARNINGS>div { + background-color: pink; + padding: 1em; +} +#JSLINT_WARNINGS cite { + color: black; + display: block; + font-family: serif; + font-size: 100%; + font-style: normal; + margin-bottom: 4pt; + margin-left: 20pt; + margin-right: 20pt; + margin-top: 4pt; + overflow-x: hidden; +} +#JSLINT_WARNINGS samp { + background-color: lavenderblush; + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + padding: 4pt; + margin-bottom: 0; + margin-left: 16pt; + margin-right: 16pt; + margin-top: 0; + white-space: pre-wrap; +} + +#JSLINT_REPORT center { + margin-top: 1em; +} + +#JSLINT_WARNINGS dl address { + color: black; + display: inline; + float: none; + font-size: 80%; + margin: 0; +} + +#JSLINT_REPORT>div { + padding: 1em; +} + +#JSLINT_ dl.level0 { + color: black; + background-color: white; +} + +#JSLINT_ dl.level1 { + color: black; + background-color: #ffffe0; /* yellow */ + margin-left: 1em; +} + +#JSLINT_ dl.level2 { + color: black; + background-color: #e0ffe0; /* green */ + margin-left: 2em; +} + +#JSLINT_ dl.level3 { + color: black; + background-color: #e0e0ff; /* blue */ + margin-left: 3em; +} + +#JSLINT_ dl.level4 { + background-color: #ffe0ff; /* purple */ + color: black; + margin-left: 4em; +} + +#JSLINT_ dl.level5 { + background-color: #ffe0e0; /* red */ + color: black; + margin-left: 5em; +} + +#JSLINT_ dl.level6 { + background-color: #ffe390; /* orange */ + color: black; + margin-left: 6em; +} + +#JSLINT_ dl.level7 { + background-color: #e0e0e0; /* gray */ + color: black; + margin-left: 7em; +} + +#JSLINT_ dl.level8 { + color: black; + margin-left: 8em; +} + +#JSLINT_ dl.level9 { + color: black; + margin-left: 9em; +} + +.none { + display: none; +} +.center { + text-align: center; +} diff --git a/branch.unexpected_empty_lines/jslint.js b/branch.unexpected_empty_lines/jslint.js new file mode 100644 index 000000000..ee046eb72 --- /dev/null +++ b/branch.unexpected_empty_lines/jslint.js @@ -0,0 +1,4939 @@ +// jslint.js +// 2018-09-13 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// jslint(source, option_object, global_array) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze, a string or an array of strings. +// option_object An object whose keys correspond to option names. +// global_array An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all of the functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// 1. If the source is a single string, split it into an array of strings. +// 2. Turn the source into an array of tokens. +// 3. Furcate the tokens into a parse tree. +// 4. Walk the tree, traversing all of the nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// 5. Check the whitespace between the tokens. + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*jslint long */ + +/*property + a, and, arity, assign, b, bad_assignment_a, bad_directive_a, + bad_get, bad_module_name_a, bad_option_a, bad_property_a, bad_set, + bitwise, block, body, browser, c, calls, catch, charCodeAt, closer, + closure, code, column, complex, concat, constant, context, convert, + couch, create, d, dead, default, devel, directive, directives, + disrupt, dot, duplicate_a, edition, ellipsis, else, empty_block, + escape_mega, eval, every, expected_a, expected_a_at_b_c, + expected_a_b, expected_a_b_from_c_d, expected_a_before_b, + expected_a_next_at_b, expected_digits_after_a, expected_four_digits, + expected_identifier_a, expected_line_break_a_b, + expected_newlines, + expected_regexp_factor_a, expected_space_a_b, expected_statements_a, + expected_string_a, expected_type_string_a, exports, expression, + extra, finally, flag, for, forEach, free, from, froms, fud, fudge, + function_in_loop, functions, g, getset, global, i, id, identifier, + import, inc, indexOf, infix_in, init, initial, isArray, isNaN, join, + json, keys, label, label_a, lbp, led, length, level, line, lines, + live, long, loop, m, margin, match, message, misplaced_a, + misplaced_directive_a, missing_browser, missing_m, module, multivar, + naked_block, name, names, nested_comment, new, node, not_label_a, + nr, nud, number_isNaN, ok, open, option, out_of_scope_a, parameters, + parent, pop, property, push, quote, redefinition_a_b, replace, + required_a_optional_b, reserved_a, right, role, search, signature, + single, slice, some, sort, split, statement, stop, strict, + subscript_a, switch, test, this, thru, toString, todo_comment, + tokens, too_long, too_many_digits, tree, try, type, u, + unclosed_comment, unclosed_mega, unclosed_string, undeclared_a, + unexpected_a, unexpected_a_after_b, unexpected_a_before_b, + unexpected_at_top_level_a, unexpected_char_a, unexpected_comment, + unexpected_directive_a, unexpected_expression_a, unexpected_label_a, + unexpected_parens, unexpected_space_a_b, unexpected_statement_a, + unexpected_trailing_space, unexpected_typeof_a, uninitialized_a, + unreachable_a, unregistered_property_a, unsafe, unused_a, + use_double, use_open, use_spaces, use_strict, used, value, var_loop, + var_switch, variable, warning, warnings, weird_condition_a, + weird_expression_a, weird_loop, weird_relation_a, white, + wrap_assignment, wrap_condition, wrap_immediate, wrap_parameter, + wrap_regexp, wrap_unary, wrapped, writable, y +*/ + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than {} because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +function populate(array, object = empty(), value = true) { + +// Augment an object by taking property names from an array of strings. + + array.forEach(function (name) { + object[name] = value; + }); + return object; +} + +const allowed_option = { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + bitwise: true, + browser: [ + "caches", "clearInterval", "clearTimeout", "document", "DOMException", + "Element", "Event", "event", "FileReader", "FormData", "history", + "localStorage", "location", "MutationObserver", "name", "navigator", + "screen", "sessionStorage", "setInterval", "setTimeout", "Storage", + "URL", "window", "Worker", "XMLHttpRequest" + ], + couch: [ + "emit", "getRow", "isArray", "log", "provides", "registerType", + "require", "send", "start", "sum", "toJSON" + ], + convert: true, + devel: [ + "alert", "confirm", "console", "prompt" + ], + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + multivar: true, + node: [ + "Buffer", "clearImmediate", "clearInterval", "clearTimeout", + "console", "exports", "module", "process", "require", + "setImmediate", "setInterval", "setTimeout", "URL", + "URLSearchParams", "__dirname", "__filename" + ], + single: true, + this: true, + white: true +}; + +const anticondition = populate([ + "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%", + "typeof", "(number)", "(string)" +]); + +// These are the bitwise operators. + +const bitwiseop = populate([ + "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=", + ">>>", ">>>=" +]); + +const escapeable = populate([ + "\\", "/", "`", "b", "f", "n", "r", "t" +]); + +const opener = { + +// The open and close pairs. + + "(": ")", // paren + "[": "]", // bracket + "{": "}", // brace + "${": "}" // mega +}; + +// The relational operators. + +const relationop = populate([ + "!=", "!==", "==", "===", "<", "<=", ">", ">=" +]); + +// This is the set of infix operators that require a space on each side. + +const spaceop = populate([ + "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/", + "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=", + ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" +]); + +const standard = [ + +// These are the globals that are provided by the language standard. + + "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI", + "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error", + "EvalError", "Float32Array", "Float64Array", "Generator", + "GeneratorFunction", "Int8Array", "Int16Array", "Int32Array", "Intl", + "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat", + "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", + "Set", "String", "Symbol", "SyntaxError", "System", "TypeError", + "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", + "URIError", "WeakMap", "WeakSet" +]; + +const bundle = { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + and: "The '&&' subexpression should be wrapped in parens.", + bad_assignment_a: "Bad assignment to '{a}'.", + bad_directive_a: "Bad directive '{a}'.", + bad_get: "A get function takes no parameters.", + bad_module_name_a: "Bad module name '{a}'.", + bad_option_a: "Bad option '{a}'.", + bad_property_a: "Bad property name '{a}'.", + bad_set: "A set function takes one parameter.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + escape_mega: "Unexpected escapement in mega literal.", + expected_a: "Expected '{a}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: ( + "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'." + ), + expected_a_before_b: "Expected '{a}' before '{b}'.", + expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.", + expected_digits_after_a: "Expected digits after '{a}'.", + expected_four_digits: "Expected four digits after '\\u'.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.", + expected_newlines: ( + "Expected 2 or 4 newlines between code-sections instead of {a}." + ), + expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.", + expected_space_a_b: "Expected one space between '{a}' and '{b}'.", + expected_statements_a: "Expected statements before '{a}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + expected_type_string_a: "Expected a type string and instead saw '{a}'.", + function_in_loop: "Don't make functions within a loop.", + infix_in: ( + "Unexpected 'in'. Compare with undefined, " + + "or use the hasOwnProperty method instead." + ), + label_a: "'{a}' is a statement label.", + misplaced_a: "Place '{a}' at the outermost level.", + misplaced_directive_a: ( + "Place the '/*{a}*/' directive before the first statement." + ), + missing_browser: "/*global*/ requires the Assume a browser option.", + missing_m: "Expected 'm' flag on a multiline regular expression.", + naked_block: "Naked block.", + nested_comment: "Nested comment.", + not_label_a: "'{a}' is not a label.", + number_isNaN: "Use Number.isNaN function to compare with NaN.", + out_of_scope_a: "'{a}' is out of scope.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + required_a_optional_b: ( + "Required parameter '{a}' after optional parameter '{b}'." + ), + reserved_a: "Reserved name '{a}'.", + subscript_a: "['{a}'] is better written in dot notation.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line is longer than 80 characters.", + too_many_digits: "Too many digits.", + unclosed_comment: "Unclosed comment.", + unclosed_mega: "Unclosed mega literal.", + unclosed_string: "Unclosed string.", + undeclared_a: "Undeclared '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_a_after_b: "Unexpected '{a}' after '{b}'.", + unexpected_a_before_b: "Unexpected '{a}' before '{b}'.", + unexpected_at_top_level_a: "Expected '{a}' to be in a function.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_directive_a: "When using modules, don't use directive '/*{a}'.", + unexpected_expression_a: ( + "Unexpected expression '{a}' in statement position." + ), + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_parens: "Don't wrap function literals in parens.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_statement_a: ( + "Unexpected statement '{a}' in expression position." + ), + unexpected_trailing_space: "Unexpected trailing space.", + unexpected_typeof_a: ( + "Unexpected 'typeof'. Use '===' to compare directly with {a}." + ), + uninitialized_a: "Uninitialized '{a}'.", + unreachable_a: "Unreachable '{a}'.", + unregistered_property_a: "Unregistered property name '{a}'.", + unsafe: "Unsafe character '{a}'.", + unused_a: "Unused '{a}'.", + use_double: "Use double quotes, not single quotes.", + use_open: ( + "Wrap a ternary expression in parens, " + + "with a line break after the left paren." + ), + use_spaces: "Use spaces, not tabs.", + use_strict: "This function needs a \"use strict\" pragma.", + var_loop: "Don't declare variables in a loop.", + var_switch: "Don't declare variables in a switch.", + weird_condition_a: "Weird condition '{a}'.", + weird_expression_a: "Weird expression '{a}'.", + weird_loop: "Weird loop.", + weird_relation_a: "Weird relation '{a}'.", + wrap_assignment: "Don't wrap assignment statements in parens.", + wrap_condition: "Wrap the condition in parens.", + wrap_immediate: ( + "Wrap an immediate function invocation in parentheses to assist " + + "the reader in understanding that the expression is the result " + + "of a function, and not the function itself." + ), + wrap_parameter: "Wrap the parameter in parens.", + wrap_regexp: "Wrap this regexp in parens to avoid confusion.", + wrap_unary: "Wrap the unary expression in parens." +}; + +// Regular expression literals: + +// supplant {variables} +const rx_supplant = /\{([^{}]*)\}/g; +// carriage return, carriage return linefeed, or linefeed +const rx_crlf = /\n|\r\n?/; +// unsafe characters that are silently deleted by one or more browsers +const rx_unsafe = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; +// identifier +const rx_identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; +const rx_module = /^[a-zA-Z0-9_$:.@\-\/]+$/; +const rx_bad_property = /^_|\$|Sync\$|_$/; +// star slash +const rx_star_slash = /\*\//; +// slash star +const rx_slash_star = /\/\*/; +// slash star or ending slash +const rx_slash_star_or_slash = /\/\*|\/$/; +// uncompleted work comment +const rx_todo = /\b(?:todo|TO\s?DO|HACK)\b/; +// tab +const rx_tab = /\t/g; +// directive +const rx_directive = /^(jslint|property|global)\s+(.*)$/; +const rx_directive_part = /^([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*(.*)$/; +// token (sorry it is so long) +const rx_token = /^((\s+)|([a-zA-Z_$][a-zA-Z0-9_$]*)|[(){}\[\]?,:;'"~`]|=(?:==?|>)?|\.+|[*\/][*\/=]?|\+[=+]?|-[=\-]?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<= "a" && string <= "z\uffff") + || (string >= "A" && string <= "Z\uffff") + ); +} + +function supplant(string, object) { + return string.replace(rx_supplant, function (found, filling) { + const replacement = object[filling]; + return ( + replacement !== undefined + ? replacement + : found + ); + }); +} + +let anon; // The guessed name for anonymous functions. +let blockage; // The current block. +let block_stack; // The stack of blocks. +let newlines_streak; // The current streak of newlines in function next_line() +let declared_globals; // The object containing the global declarations. +let directives; // The directive comments. +let directive_mode; // true if directives are still allowed. +let early_stop; // true if JSLint cannot finish. +let exports; // The exported names and values. +let froms; // The array collecting all import-from strings. +let fudge; // true if the natural numbers start with 1. +let functionage; // The current function. +let functions; // The array containing all of the functions. +let global; // The global object; the outermost context. +let json_mode; // true if parsing JSON. +let lines; // The array containing source lines. +let module_mode; // true if import or export was used. +let next_token; // The next token to be examined in the parse. +let option; // The options parameter. +let property; // The object containing the tallied property names. +let mega_mode; // true if currently parsing a megastring literal. +let stack; // The stack of functions. +let syntax; // The object containing the parser. +let token; // The current token being examined in the parse. +let token_nr; // The number of the next token. +let tokens; // The array of tokens. +let tenure; // The predefined property registry. +let tree; // The abstract parse tree. +let var_mode; // "var" if using var; "let" if using let. +let warnings; // The array collecting all generated warnings. + +// Error reportage functions: + +function artifact(the_token) { + +// Return a string representing an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); +} + +function artifact_line(the_token) { + +// Return the fudged line number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.line + fudge; +} + +function artifact_column(the_token) { + +// Return the fudged column number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.from + fudge; +} + +function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + const warning = { // ~~ + name: "JSLintError", + column: column, + line: line, + code: code + }; + if (a !== undefined) { + warning.a = a; + } + if (b !== undefined) { + warning.b = b; + } + if (c !== undefined) { + warning.c = c; + } + if (d !== undefined) { + warning.d = d; + } + warning.message = supplant(bundle[code] || code, warning); + warnings.push(warning); + return warning; +} + +function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); +} + +function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + if (the_token.warning === undefined) { + the_token.warning = warn_at( + code, + the_token.line, + the_token.from, + a || artifact(the_token), + b, + c, + d + ); + return the_token.warning; + } +} + +function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); +} + +// Tokenize: + +function tokenize(source) { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + +// If the source is not an array, then it is split into lines at the +// carriage return/linefeed. + + lines = ( + Array.isArray(source) + ? source + : source.split(rx_crlf) + ); + tokens = []; + + let char; // a popular character + let column = 0; // the column number of the next character + let first; // the first token + let from; // the starting column number of the token + let line = -1; // the line number of the next character + let nr = 0; // the next token number + let previous = global; // the previous token including comments + let prior = global; // the previous token excluding comments + let mega_from; // the starting column of megastring + let mega_line; // the starting line of megastring + let snippet; // a piece of string + let source_line; // the current line source string + + function next_line() { + +// Put the next line of source in source_line. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + let at; + column = 0; + line += 1; + source_line = lines[line]; + if (source_line !== undefined) { + newlines_streak += 1; + if (source_line.length > 0) { + if ( + newlines_streak !== 1 + && newlines_streak !== 2 + && newlines_streak !== 4 + ) { + warn_at( + "expected_newlines", + line >= newlines_streak + ? line - newlines_streak + : 0, + 0, + newlines_streak + ); + } + newlines_streak = 0; + } + at = source_line.search(rx_tab); + if (at >= 0) { + if (!option.white) { + warn_at("use_spaces", line, at + 1); + } + source_line = source_line.replace(rx_tab, " "); + } + at = source_line.search(rx_unsafe); + if (at >= 0) { + warn_at( + "unsafe", + line, + column + at, + "U+" + source_line.charCodeAt(at).toString(16) + ); + } + if (!option.white && source_line.slice(-1) === " ") { + warn_at( + "unexpected_trailing_space", + line, + source_line.length - 1 + ); + } + if ( + !option.long + && source_line.length > 80 + && !json_mode + && first + ) { + warn_at("too_long", line, 80); + } + } + return source_line; + } + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions snip, next_char, prev_char, +// some_digits, and escape help in the parsing of literals. + + function snip() { + +// Remove the last character from snippet. + + snippet = snippet.slice(0, -1); + } + + function next_char(match) { + +// Get the next character from the source line. Remove it from the source_line, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + return stop_at( + ( + char === "" + ? "expected_a" + : "expected_a_b" + ), + line, + column - 1, + match, + char + ); + } + if (source_line) { + char = source_line[0]; + source_line = source_line.slice(1); + snippet += char; + } else { + char = ""; + snippet += " "; + } + column += 1; + return char; + } + + function back_char() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the source_line. + + if (snippet) { + char = snippet.slice(-1); + source_line = char + source_line; + column -= 1; + snip(); + } else { + char = ""; + } + return char; + } + + function some_digits(rx, quiet) { + const result = source_line.match(rx); + if (result) { + char = result[1]; + column += char.length; + source_line = result[2]; + snippet += char; + } else { + char = ""; + if (!quiet) { + warn_at( + "expected_digits_after_a", + line, + column, + snippet + ); + } + } + return char.length; + } + + function escape(extra) { + next_char("\\"); + if (escapeable[char] === true) { + return next_char(); + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "u") { + if (next_char("u") === "{") { + if (json_mode) { + warn_at("unexpected_a", line, column - 1, char); + } + if (some_digits(rx_hexs) > 5) { + warn_at("too_many_digits", line, column - 1); + } + if (next_char() !== "}") { + stop_at("expected_a_before_b", line, column, "}", char); + } + return next_char(); + } + back_char(); + if (some_digits(rx_hexs, true) < 4) { + warn_at("expected_four_digits", line, column - 1); + } + return; + } + if (extra && extra.indexOf(char) >= 0) { + return next_char(); + } + warn_at("unexpected_a_before_b", line, column - 2, "\\", char); + } + + function make(id, value, identifier) { + +// Make the token object and append it to the tokens list. + + const the_token = { + from: from, + id: id, + identifier: Boolean(identifier), + line: line, + nr: nr, + thru: column + }; + tokens[nr] = the_token; + nr += 1; + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + directive_mode = false; + } + +// If the token is to have a value, give it one. + + if (value !== undefined) { + the_token.value = value; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option.white. + + if ( + previous.line === line + && previous.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (previous.id === "(comment)" || previous.id === "(regexp)") + ) { + warn( + "expected_space_a_b", + the_token, + artifact(previous), + artifact(the_token) + ); + } + if (previous.id === "." && id === "(number)") { + warn("expected_a_before_b", previous, "0", "."); + } + if (prior.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// The previous token is used to detect adjacency problems. + + previous = the_token; + +// The prior token is a previous token that was not a comment. The prior token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (previous.id !== "(comment)") { + prior = previous; + } + return the_token; + } + + function parse_directive(the_comment, body) { + +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + + const result = body.match(rx_directive_part); + if (result) { + let allowed; + const name = result[1]; + const value = result[2]; + if (the_comment.directive === "jslint") { + allowed = allowed_option[name]; + if ( + typeof allowed === "boolean" + || typeof allowed === "object" + ) { + if ( + value === "" + || value === "true" + || value === undefined + ) { + option[name] = true; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } else if (value === "false") { + option[name] = false; + } else { + warn("bad_option_a", the_comment, name + ":" + value); + } + } else { + warn("bad_option_a", the_comment, name); + } + } else if (the_comment.directive === "property") { + if (tenure === undefined) { + tenure = empty(); + } + tenure[name] = true; + } else if (the_comment.directive === "global") { + if (value) { + warn("bad_option_a", the_comment, name + ":" + value); + } + declared_globals[name] = false; + module_mode = the_comment; + } + return parse_directive(the_comment, result[3]); + } + if (body) { + return stop("bad_directive_a", the_comment, body); + } + } + + function comment(snippet) { + +// Make a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + + const the_comment = make("(comment)", snippet); + if (Array.isArray(snippet)) { + snippet = snippet.join(" "); + } + if (!option.devel && rx_todo.test(snippet)) { + warn("todo_comment", the_comment); + } + const result = snippet.match(rx_directive); + if (result) { + if (!directive_mode) { + warn_at("misplaced_directive_a", line, from, result[1]); + } else { + the_comment.directive = result[1]; + parse_directive(the_comment, result[2]); + } + directives.push(the_comment); + } + return the_comment; + } + + function regexp() { + +// Parse a regular expression literal. + + let multi_mode = false; + let result; + let value; + + function quantifier() { + +// Match an optional quantifier. + + if (char === "?" || char === "*" || char === "+") { + next_char(); + } else if (char === "{") { + if (some_digits(rx_digits, true) === 0) { + warn_at("expected_a", line, column, "0"); + } + if (next_char() === ",") { + some_digits(rx_digits, true); + next_char(); + } + next_char("}"); + } else { + return; + } + if (char === "?") { + next_char("?"); + } + } + + function subklass() { + +// Match a character in a character class. + + if (char === "\\") { + escape("BbDdSsWw-[]^"); + return true; + } + if ( + char === "" + || char === "[" + || char === "]" + || char === "/" + || char === "^" + || char === "-" + ) { + return false; + } + if (char === " ") { + warn_at("expected_a_b", line, column, "\\u0020", " "); + } else if (char === "`" && mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char(); + return true; + } + + function ranges() { + +// Match a range of subclasses. + + if (subklass()) { + if (char === "-") { + next_char("-"); + if (!subklass()) { + return stop_at( + "unexpected_a", + line, + column - 1, + "-" + ); + } + } + return ranges(); + } + } + + function klass() { + +// Match a class. + + next_char("["); + if (char === "^") { + next_char("^"); + } + (function classy() { + ranges(); + if (char !== "]" && char !== "") { + warn_at( + "expected_a_before_b", + line, + column, + "\\", + char + ); + next_char(); + return classy(); + } + }()); + next_char("]"); + } + + function choice() { + + function group() { + +// Match a group that starts with left paren. + + next_char("("); + if (char === "?") { + next_char("?"); + if (char === "=" || char === "!") { + next_char(); + } else { + next_char(":"); + } + } else if (char === ":") { + warn_at("expected_a_before_b", line, column, "?", ":"); + } + choice(); + next_char(")"); + } + + function factor() { + if ( + char === "" + || char === "/" + || char === "]" + || char === ")" + ) { + return false; + } + if (char === "(") { + group(); + return true; + } + if (char === "[") { + klass(); + return true; + } + if (char === "\\") { + escape("BbDdSsWw^${}[]():=!.-|*+?"); + return true; + } + if ( + char === "?" + || char === "+" + || char === "*" + || char === "}" + || char === "{" + ) { + warn_at( + "expected_a_before_b", + line, + column - 1, + "\\", + char + ); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column - 1, "`"); + } + } else if (char === " ") { + warn_at( + "expected_a_b", + line, + column - 1, + "\\s", + " " + ); + } else if (char === "$") { + if (source_line[0] !== "/") { + multi_mode = true; + } + } else if (char === "^") { + if (snippet !== "^") { + multi_mode = true; + } + } + next_char(); + return true; + } + + function sequence(follow) { + if (factor()) { + quantifier(); + return sequence(true); + } + if (!follow) { + warn_at("expected_regexp_factor_a", line, column, char); + } + } + +// Match a choice (a sequence that can be followed by | and another choice). + + sequence(); + if (char === "|") { + next_char("|"); + return choice(); + } + } + +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + next_char(); + if (char === "=") { + warn_at("expected_a_before_b", line, column, "\\", "="); + } + choice(); + +// Make sure there is a closing slash. + + snip(); + value = snippet; + next_char("/"); + +// Process dangling flag letters. + + const allowed = { + g: true, + i: true, + m: true, + u: true, + y: true + }; + const flag = empty(); + (function make_flag() { + if (is_letter(char)) { + if (allowed[char] !== true) { + warn_at("unexpected_a", line, column, char); + } + allowed[char] = false; + flag[char] = true; + next_char(); + return make_flag(); + } + }()); + back_char(); + if (char === "/" || char === "*") { + return stop_at("unexpected_a", line, from, char); + } + result = make("(regexp)", char); + result.flag = flag; + result.value = value; + if (multi_mode && !flag.m) { + warn_at("missing_m", line, column); + } + return result; + } + + function string(quote) { + +// Make a string token. + + let the_token; + snippet = ""; + next_char(); + + return (function next() { + if (char === quote) { + snip(); + the_token = make("(string)", snippet); + the_token.quote = quote; + return the_token; + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "\\") { + escape(quote); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char("`"); + } else { + next_char(); + } + return next(); + }()); + } + + function frack() { + if (char === ".") { + some_digits(rx_digits); + next_char(); + } + if (char === "E" || char === "e") { + next_char(); + if (char !== "+" && char !== "-") { + back_char(); + } + some_digits(rx_digits); + next_char(); + } + } + + function number() { + if (snippet === "0") { + next_char(); + if (char === ".") { + frack(); + } else if (char === "b") { + some_digits(rx_bits); + next_char(); + } else if (char === "o") { + some_digits(rx_octals); + next_char(); + } else if (char === "x") { + some_digits(rx_hexs); + next_char(); + } + } else { + next_char(); + frack(); + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + return stop_at( + "unexpected_a_after_b", + line, + column - 1, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + back_char(); + return make("(number)", snippet); + } + + function lex() { + let array; + let i = 0; + let j = 0; + let last; + let result; + let the_token; + if (!source_line) { + source_line = next_line(); + from = 0; + return ( + source_line === undefined + ? ( + mega_mode + ? stop_at("unclosed_mega", mega_line, mega_from) + : make("(end)") + ) + : lex() + ); + } + from = column; + result = source_line.match(rx_token); + +// result[1] token +// result[2] whitespace +// result[3] identifier +// result[4] number +// result[5] rest + + if (!result) { + return stop_at( + "unexpected_char_a", + line, + column, + source_line[0] + ); + } + + snippet = result[1]; + column += snippet.length; + source_line = result[5]; + +// Whitespace was matched. Call lex again to get more. + + if (result[2]) { + return lex(); + } + +// The token is an identifier. + + if (result[3]) { + return make(snippet, undefined, true); + } + +// The token is a number. + + if (result[4]) { + return number(snippet); + } + +// The token is a string. + + if (snippet === "\"") { + return string(snippet); + } + if (snippet === "'") { + if (!option.single) { + warn_at("use_double", line, column); + } + return string(snippet); + } + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (snippet === "`") { + if (mega_mode) { + return stop_at("expected_a_b", line, column, "}", "`"); + } + snippet = ""; + mega_from = from; + mega_line = line; + mega_mode = true; + +// Parsing a mega literal is tricky. First make a ` token. + + make("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + (function part() { + const at = source_line.search(rx_mega); + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + if (at < 0) { + snippet += source_line + "\n"; + return ( + next_line() === undefined + ? stop_at("unclosed_mega", mega_line, mega_from) + : part() + ); + } + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + snippet += source_line.slice(0, at); + column += at; + source_line = source_line.slice(at); + if (source_line[0] === "\\") { + stop_at("escape_mega", line, at); + } + make("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then make tokens that will become part of an expression until +// a } token is made. + + if (source_line[0] === "$") { + column += 2; + make("${"); + source_line = source_line.slice(2); + (function expr() { + const id = lex().id; + if (id === "{") { + return stop_at( + "expected_a_b", + line, + column, + "}", + "{" + ); + } + if (id !== "}") { + return expr(); + } + }()); + return part(); + } + }()); + source_line = source_line.slice(1); + column += 1; + mega_mode = false; + return make("`"); + } + +// The token is a // comment. + + if (snippet === "//") { + snippet = source_line; + source_line = ""; + the_token = comment(snippet); + if (mega_mode) { + warn("unexpected_comment", the_token, "`"); + } + return the_token; + } + +// The token is a /* comment. + + if (snippet === "/*") { + array = []; + if (source_line[0] === "/") { + warn_at("unexpected_a", line, column + i, "/"); + } + (function next() { + if (source_line > "") { + i = source_line.search(rx_star_slash); + if (i >= 0) { + return; + } + j = source_line.search(rx_slash_star); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + } + array.push(source_line); + source_line = next_line(); + if (source_line === undefined) { + return stop_at("unclosed_comment", line, column); + } + return next(); + }()); + snippet = source_line.slice(0, i); + j = snippet.search(rx_slash_star_or_slash); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + array.push(snippet); + column += i + 2; + source_line = source_line.slice(i + 2); + return comment(array); + } + +// The token is a slash. + + if (snippet === "/") { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + if (prior.identifier) { + if (!prior.dot) { + if (prior.id === "return") { + return regexp(); + } + if ( + prior.id === "(begin)" + || prior.id === "case" + || prior.id === "delete" + || prior.id === "in" + || prior.id === "instanceof" + || prior.id === "new" + || prior.id === "typeof" + || prior.id === "void" + || prior.id === "yield" + ) { + the_token = regexp(); + return stop("unexpected_a", the_token); + } + } + } else { + last = prior.id[prior.id.length - 1]; + if ("(,=:?[".indexOf(last) >= 0) { + return regexp(); + } + if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) { + the_token = regexp(); + warn("wrap_regexp", the_token); + return the_token; + } + } + if (source_line[0] === "/") { + column += 1; + source_line = source_line.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + } + return make(snippet); + } + + first = lex(); + json_mode = first.id === "{" || first.id === "["; + +// This is the only loop in JSLint. It will turn into a recursive call to lex +// when ES6 has been finished and widely deployed and adopted. + + while (true) { + if (lex().id === "(end)") { + break; + } + } +} + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + +function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!rx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!rx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property[id] === "number") { + property[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (tenure !== undefined) { + if (tenure[id] !== true) { + warn("unregistered_property_a", name); + } + } else { + if (name.identifier && rx_bad_property.test(id)) { + warn("bad_property_a", name); + } + } + property[id] = 1; + } + return id; +} + +function dispense() { + +// Deliver the next token, skipping the comments. + + const cadet = tokens[token_nr]; + token_nr += 1; + if (cadet.id === "(comment)") { + if (json_mode) { + warn("unexpected_a", cadet); + } + return dispense(); + } else { + return cadet; + } +} + +function lookahead() { + +// Look ahead one token without advancing. + + const old_token_nr = token_nr; + const cadet = dispense(true); + token_nr = old_token_nr; + return cadet; +} + +function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if (token.identifier && token.id !== "function") { + anon = token.id; + } else if (token.id === "(string)" && rx_identifier.test(token.value)) { + anon = token.value; + } + +// Attempt to match next_token with an expected id. + + if (id !== undefined && next_token.id !== id) { + return ( + match === undefined + ? stop("expected_a_b", next_token, id, artifact()) + : stop( + "expected_a_b_from_c_d", + next_token, + id, + artifact(match), + artifact_line(match), + artifact(next_token) + ) + ); + } + +// Promote the tokens, skipping comments. + + token = next_token; + next_token = dispense(); + if (next_token.id === "(end)") { + token_nr -= 1; + } +} + +// Parsing of JSON is simple: + +function json_value() { + let negative; + if (next_token.id === "{") { + return (function json_object() { + const brace = next_token; + const object = empty(); + const properties = []; + brace.expression = properties; + advance("{"); + if (next_token.id !== "}") { + (function next() { + let name; + let value; + if (next_token.quote !== "\"") { + warn( + "unexpected_a", + next_token, + next_token.quote + ); + } + name = next_token; + advance("(string)"); + if (object[token.value] !== undefined) { + warn("duplicate_a", token); + } else if (token.value === "__proto__") { + warn("bad_property_a", token); + } else { + object[token.value] = token; + } + advance(":"); + value = json_value(); + value.label = name; + properties.push(value); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("}", brace); + return brace; + }()); + } + if (next_token.id === "[") { + return (function json_array() { + const bracket = next_token; + const elements = []; + bracket.expression = elements; + advance("["); + if (next_token.id !== "]") { + (function next() { + elements.push(json_value()); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]", bracket); + return bracket; + }()); + } + if ( + next_token.id === "true" + || next_token.id === "false" + || next_token.id === "null" + ) { + advance(); + return token; + } + if (next_token.id === "(number)") { + if (!rx_JSON_number.test(next_token.value)) { + warn("unexpected_a"); + } + advance(); + return token; + } + if (next_token.id === "(string)") { + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + advance(); + return token; + } + if (next_token.id === "-") { + negative = next_token; + negative.arity = "unary"; + advance("-"); + advance("(number)"); + negative.expression = token; + return negative; + } + stop("unexpected_a"); +} + +// Now we parse JavaScript. + +function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + const id = name.id; + +// Reserved words may not be enrolled. + + if (syntax[id] !== undefined && id !== "ignore") { + warn("reserved_a", name); + } else { + +// Has the name been enrolled in this context? + + let earlier = functionage.context[id]; + if (earlier) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + +// Has the name been enrolled in an outer context? + + } else { + stack.forEach(function (value) { + const item = value.context[id]; + if (item !== undefined) { + earlier = item; + } + }); + if (earlier) { + if (id === "ignore") { + if (earlier.role === "variable") { + warn("unexpected_a", name); + } + } else { + if ( + ( + role !== "exception" + || earlier.role !== "exception" + ) + && role !== "parameter" + && role !== "function" + ) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + } + } + } + +// Enroll it. + + functionage.context[id] = name; + name.dead = true; + name.parent = functionage; + name.init = false; + name.role = role; + name.used = 0; + name.writable = !readonly; + } + } +} + +function expression(rbp, initial) { + +// This is the heart of the Pratt parser. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// nud Null denotation +// led Left denotation +// lbp Left binding power +// rbp Right binding power + +// It processes a nud (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop. It +// returns the expression's parse tree. + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement, + + if (!initial) { + advance(); + } + the_symbol = syntax[token.id]; + if (the_symbol !== undefined && the_symbol.nud !== undefined) { + left = the_symbol.nud(); + } else if (token.identifier) { + left = token; + left.arity = "variable"; + } else { + return stop("unexpected_a", token); + } + (function right() { + the_symbol = syntax[next_token.id]; + if ( + the_symbol !== undefined + && the_symbol.led !== undefined + && rbp < the_symbol.lbp + ) { + advance(); + left = the_symbol.led(left); + return right(); + } + }()); + return left; +} + +function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = next_token; + let the_value; + the_paren.free = true; + advance("("); + the_value = expression(0); + advance(")"); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (anticondition[the_value.id] === true) { + warn("unexpected_a", the_value); + } + return the_value; +} + +function is_weird(thing) { + return ( + thing.id === "(regexp)" + || thing.id === "{" + || thing.id === "=>" + || thing.id === "function" + || (thing.id === "[" && thing.arity === "unary") + ); +} + +function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + return ( + Array.isArray(b) + && a.length === b.length + && a.every(function (value, index) { + return are_similar(value, b[index]); + }) + ); + } + if (Array.isArray(b)) { + return false; + } + if (a.id === "(number)" && b.id === "(number)") { + return a.value === b.value; + } + let a_string; + let b_string; + if (a.id === "(string)") { + a_string = a.value; + } else if (a.id === "`" && a.constant) { + a_string = a.value[0]; + } + if (b.id === "(string)") { + b_string = b.value; + } else if (b.id === "`" && b.constant) { + b_string = b.value[0]; + } + if (typeof a_string === "string") { + return a_string === b_string; + } + if (is_weird(a) || is_weird(b)) { + return false; + } + if (a.arity === b.arity && a.id === b.id) { + if (a.id === ".") { + return ( + are_similar(a.expression, b.expression) + && are_similar(a.name, b.name) + ); + } + if (a.arity === "unary") { + return are_similar(a.expression, b.expression); + } + if (a.arity === "binary") { + return ( + a.id !== "(" + && are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + ); + } + if (a.arity === "ternary") { + return ( + are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + && are_similar(a.expression[2], b.expression[2]) + ); + } + if (a.arity === "function" && a.arity === "regexp") { + return false; + } + return true; + } + return false; +} + +function semicolon() { + +// Try to match a semicolon. + + if (next_token.id === ";") { + advance(";"); + } else { + warn_at( + "expected_a_b", + token.line, + token.thru, + ";", + artifact(next_token) + ); + } + anon = "anonymous"; +} + +function statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token.identifier && next_token.id === ":") { + the_label = token; + if (the_label.id === "ignore") { + warn("unexpected_a", the_label); + } + advance(":"); + if ( + next_token.id === "do" + || next_token.id === "for" + || next_token.id === "switch" + || next_token.id === "while" + ) { + enroll(the_label, "label", true); + the_label.init = true; + the_label.dead = false; + the_statement = statement(); + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token; + first.statement = true; + the_symbol = syntax[first.id]; + if (the_symbol !== undefined && the_symbol.fud !== undefined) { + the_symbol.disrupt = false; + the_symbol.statement = true; + the_statement = the_symbol.fud(); + } else { + +// It is an expression statement. + + the_statement = expression(0, true); + if (the_statement.wrapped && the_statement.id !== "(") { + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; +} + +function statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const array = []; + (function next(disrupt) { + if ( + next_token.id !== "}" + && next_token.id !== "case" + && next_token.id !== "default" + && next_token.id !== "else" + && next_token.id !== "(end)" + ) { + let a_statement = statement(); + array.push(a_statement); + if (disrupt) { + warn("unreachable_a", a_statement); + } + return next(a_statement.disrupt); + } + }(false)); + return array; +} + +function not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === global) { + warn("unexpected_at_top_level_a", thing); + } +} + +function top_level_only(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== global) { + warn("misplaced_a", the_thing); + } +} + +function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token; + the_block.arity = "statement"; + the_block.body = special === "body"; + +// All top level function bodies should include the "use strict" pragma unless +// the whole file is strict or the file is a module or the function parameters +// use es6 syntax. + + if ( + special === "body" + && stack.length === 1 + && next_token.value === "use strict" + ) { + the_block.strict = next_token; + next_token.statement = true; + advance("(string)"); + advance(";"); + } + stmts = statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option.devel && special !== "ignore") { + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; +} + +function mutation_check(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + warn("bad_assignment_a", the_thing); + return false; + } + return true; +} + +function left_check(left, right) { + +// Warn if the left is not one of these: +// e.b +// e[b] +// e() +// ?: +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !left_check(left.expression[1]) + && !left_check(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right); + return false; + } + return true; +} + +// These functions are used to specify the grammar of our language: + +function symbol(id, bp) { + +// Make a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax[id] = the_symbol; + } + return the_symbol; +} + +function assignment(id) { + +// Make an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led = function (left) { + const the_token = token; + let right; + the_token.arity = "assignment"; + right = expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "pre" + || right.arity === "post" + ) { + warn("unexpected_a", right); + } + mutation_check(left); + return the_token; + }; + return the_symbol; +} + +function constant(id, type, value) { + +// Make a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud = ( + typeof value === "function" + ? value + : function () { + token.constant = true; + if (value !== undefined) { + token.value = value; + } + return token; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; +} + +function infix(id, bp, f) { + +// Make an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, expression(bp)]; + return the_token; + }; + return the_symbol; +} + +function infixr(id, bp) { + +// Make a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + the_token.expression = [left, expression(bp - 1)]; + return the_token; + }; + return the_symbol; +} + +function post(id) { + +// Make one of the post operators. + + const the_symbol = symbol(id, 150); + the_symbol.led = function (left) { + token.expression = left; + token.arity = "post"; + mutation_check(token.expression); + return token; + }; + return the_symbol; +} + +function pre(id) { + +// Make one of the pre operators. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "pre"; + the_token.expression = expression(150); + mutation_check(the_token.expression); + return the_token; + }; + return the_symbol; +} + +function prefix(id, f) { + +// Make a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = expression(150); + return the_token; + }; + return the_symbol; +} + +function stmt(id, f) { + +// Make a statement. + + const the_symbol = symbol(id); + the_symbol.fud = function () { + token.arity = "statement"; + return f(); + }; + return the_symbol; +} + +function ternary(id1, id2) { + +// Make a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led = function (left) { + const the_token = token; + const second = expression(20); + advance(id2); + token.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, expression(10)]; + return the_token; + }; + return the_symbol; +} + +// Begin defining the language. + +syntax = empty(); + +symbol("}"); +symbol(")"); +symbol("]"); +symbol(","); +symbol(";"); +symbol(":"); +symbol("*/"); +symbol("await"); +symbol("case"); +symbol("catch"); +symbol("class"); +symbol("default"); +symbol("else"); +symbol("enum"); +symbol("finally"); +symbol("implements"); +symbol("interface"); +symbol("package"); +symbol("private"); +symbol("protected"); +symbol("public"); +symbol("static"); +symbol("super"); +symbol("void"); +symbol("yield"); + +constant("(number)", "number"); +constant("(regexp)", "regexp"); +constant("(string)", "string"); +constant("arguments", "object", function () { + warn("unexpected_a", token); + return token; +}); +constant("eval", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("false", "boolean", false); +constant("Function", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("ignore", "undefined", function () { + warn("unexpected_a", token); + return token; +}); +constant("Infinity", "number", Infinity); +constant("isFinite", "function", function () { + warn("expected_a_b", token, "Number.isFinite", "isFinite"); + return token; +}); +constant("isNaN", "function", function () { + warn("number_isNaN", token); + return token; +}); +constant("NaN", "number", NaN); +constant("null", "null", null); +constant("this", "object", function () { + if (!option.this) { + warn("unexpected_a", token); + } + return token; +}); +constant("true", "boolean", true); +constant("undefined", "undefined"); + +assignment("="); +assignment("+="); +assignment("-="); +assignment("*="); +assignment("/="); +assignment("%="); +assignment("&="); +assignment("|="); +assignment("^="); +assignment("<<="); +assignment(">>="); +assignment(">>>="); + +infix("||", 40); +infix("&&", 50); +infix("|", 70); +infix("^", 80); +infix("&", 90); +infix("==", 100); +infix("===", 100); +infix("!=", 100); +infix("!==", 100); +infix("<", 110); +infix(">", 110); +infix("<=", 110); +infix(">=", 110); +infix("in", 110); +infix("instanceof", 110); +infix("<<", 120); +infix(">>", 120); +infix(">>>", 120); +infix("+", 130); +infix("-", 130); +infix("*", 140); +infix("/", 140); +infix("%", 140); +infixr("**", 150); +infix("(", 160, function (left) { + const the_paren = token; + let the_argument; + if (left.id !== "function") { + left_check(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (next_token.id !== ")") { + (function next() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + the_paren.free = true; + if (the_argument.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + the_paren.free = false; + } + return the_paren; +}); +infix(".", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("[", 170, function (left) { + const the_token = token; + const the_subscript = expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + const name = survey(the_subscript); + if (rx_identifier.test(name)) { + warn("subscript_a", the_subscript, name); + } + } + left_check(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; +}); +infix("=>", 170, function (left) { + return stop("wrap_parameter", left); +}); + +function do_tick() { + const the_tick = token; + the_tick.value = []; + the_tick.expression = []; + if (next_token.id !== "`") { + (function part() { + advance("(string)"); + the_tick.value.push(token); + if (next_token.id === "${") { + advance("${"); + the_tick.expression.push(expression(0)); + advance("}"); + return part(); + } + }()); + } + advance("`"); + return the_tick; +} + +infix("`", 160, function (left) { + const the_tick = do_tick(); + left_check(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; +}); + +post("++"); +post("--"); +pre("++"); +pre("--"); + +prefix("+"); +prefix("-"); +prefix("~"); +prefix("!"); +prefix("!!"); +prefix("[", function () { + const the_token = token; + the_token.expression = []; + if (next_token.id !== "]") { + (function next() { + let element; + let ellipsis = false; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + element = expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]"); + return the_token; +}); +prefix("/=", function () { + stop("expected_a_b", token, "/\\=", "/="); +}); +prefix("=>", function () { + return stop("expected_a_before_b", token, "()", "=>"); +}); +prefix("new", function () { + const the_new = token; + const right = expression(160); + if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "()", artifact(next_token)); + } + the_new.expression = right; + return the_new; +}); +prefix("typeof"); +prefix("void", function () { + const the_void = token; + warn("unexpected_a", the_void); + the_void.expression = expression(0); + return the_void; +}); + +function parameter_list() { + let complex = false; + const list = []; + let optional; + const signature = ["("]; + if (next_token.id !== ")" && next_token.id !== "(end)") { + (function parameter() { + let ellipsis = false; + let param; + if (next_token.id === "{") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("{"); + signature.push("{"); + (function subparameter() { + let subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (next_token.id === ":") { + advance(":"); + advance(); + token.label = subparam; + subparam = token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + } + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return subparameter(); + } + }()); + list.push(param); + advance("}"); + signature.push("}"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else if (next_token.id === "[") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("["); + signature.push("[]"); + (function subparameter() { + const subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + return subparameter(); + } + }()); + list.push(param); + advance("]"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else { + if (next_token.id === "...") { + complex = true; + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + } + if (!next_token.identifier) { + return stop("expected_identifier_a"); + } + param = next_token; + list.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (next_token.id === "=") { + complex = true; + optional = param; + advance("="); + param.expression = expression(0); + } else { + if (optional !== undefined) { + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } + } + }()); + } + advance(")"); + signature.push(")"); + return [list, signature.join(""), complex]; +} + +function do_function(the_function) { + let name; + if (the_function === undefined) { + the_function = token; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + name = next_token; + enroll(name, "variable", true); + the_function.name = name; + name.init = true; + name.calls = empty(); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + if (next_token.identifier) { + name = next_token; + the_function.name = name; + advance(); + } else { + the_function.name = anon; + } + } + } else { + name = the_function.name; + } + the_function.level = functionage.level + 1; + if (mega_mode) { + warn("unexpected_a", the_function); + } + +// Don't make functions in loops. It is inefficient, and it can lead to scoping +// errors. + + if (functionage.loop > 0) { + warn("function_in_loop", the_function); + } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + the_function.context = empty(); + the_function.finally = 0; + the_function.loop = 0; + the_function.switch = 0; + the_function.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functions.push(the_function); + functionage = the_function; + if (the_function.arity !== "statement" && typeof name === "object") { + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// Parse the parameter list. + + advance("("); + token.free = false; + token.arity = "function"; + const pl = parameter_list(); + functionage.parameters = pl[0]; + functionage.signature = pl[1]; + functionage.complex = pl[2]; + functionage.parameters.forEach(function enroll_parameter(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + name.names.forEach(enroll_parameter); + } + }); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && next_token.line === token.line + ) { + return stop("unexpected_a", next_token); + } + if (next_token.id === "." || next_token.id === "[") { + warn("unexpected_a"); + } + +// Restore the previous context. + + functionage = stack.pop(); + return the_function; +} + +prefix("function", do_function); + +function fart(pl) { + if (next_token.id === ";") { + stop("wrap_assignment", token); + } + advance("=>"); + const the_fart = token; + the_fart.arity = "binary"; + the_fart.name = "=>"; + the_fart.level = functionage.level + 1; + functions.push(the_fart); + if (functionage.loop > 0) { + warn("function_in_loop", the_fart); + } + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + the_fart.context = empty(); + the_fart.finally = 0; + the_fart.loop = 0; + the_fart.switch = 0; + the_fart.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functionage = the_fart; + the_fart.parameters = pl[0]; + the_fart.signature = pl[1]; + the_fart.complex = true; + the_fart.parameters.forEach(function (name) { + enroll(name, "parameter", true); + }); + if (next_token.id === "{") { + warn("expected_a_b", the_fart, "function", "=>"); + the_fart.block = block("body"); + } else { + the_fart.expression = expression(0); + } + functionage = stack.pop(); + return the_fart; +} + +prefix("(", function () { + const the_paren = token; + let the_value; + const cadet = lookahead().id; + +// We can distinguish between a parameter list for => and a wrapped expression +// with one token of lookahead. + + if ( + next_token.id === ")" + || next_token.id === "..." + || (next_token.identifier && (cadet === "," || cadet === "=")) + ) { + the_paren.free = false; + return fart(parameter_list()); + } + the_paren.free = true; + the_value = expression(0); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + if (next_token.id === "=>") { + if (the_value.arity !== "variable") { + if (the_value.id === "{" || the_value.id === "[") { + warn("expected_a_before_b", the_paren, "function", "("); + return stop("expected_a_b", next_token, "{", "=>"); + } + return stop("expected_identifier_a", the_value); + } + the_paren.expression = [the_value]; + return fart([the_paren.expression, "(" + the_value.id + ")"]); + } + return the_value; +}); +prefix("`", do_tick); +prefix("{", function () { + const the_brace = token; + const seen = empty(); + the_brace.expression = []; + if (next_token.id !== "}") { + (function member() { + let extra; + let full; + let id; + let name = next_token; + let value; + advance(); + if ( + (name.id === "get" || name.id === "set") + && next_token.identifier + ) { + if (!option.getset) { + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + next_token.id; + name = next_token; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (next_token.id === "}" || next_token.id === ",") { + if (typeof extra === "string") { + advance("("); + } + value = expression(Infinity, true); + } else if (next_token.id === "(") { + value = do_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + advance("("); + } + advance(":"); + value = expression(0); + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + advance(":"); + value = expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (next_token.id === ",") { + advance(","); + return member(); + } + }()); + } + advance("}"); + return the_brace; +}); + +stmt(";", function () { + warn("unexpected_a", token); + return token; +}); +stmt("{", function () { + warn("naked_block", token); + return block("naked"); +}); +stmt("break", function () { + const the_break = token; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (next_token.identifier && token.line === next_token.line) { + the_label = functionage.context[next_token.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + warn( + (the_label !== undefined && the_label.dead) + ? "out_of_scope_a" + : "not_label_a" + ); + } else { + the_label.used += 1; + } + the_break.label = next_token; + advance(); + } + advance(";"); + return the_break; +}); + +function do_var() { + const the_statement = token; + const is_const = the_statement.id === "const"; + the_statement.names = []; + +// A program may use var or let, but not both. + + if (!is_const) { + if (var_mode === undefined) { + var_mode = the_statement.id; + } else if (the_statement.id !== var_mode) { + warn( + "expected_a_b", + the_statement, + var_mode, + the_statement.id + ); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + warn("var_switch", the_statement); + } + if (functionage.loop > 0 && the_statement.id === "var") { + warn("var_loop", the_statement); + } + (function next() { + if (next_token.id === "{" && the_statement.id !== "var") { + const the_brace = next_token; + the_brace.names = []; + advance("{"); + (function pair() { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + survey(name); + advance(); + if (next_token.id === ":") { + advance(":"); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + next_token.label = name; + the_brace.names.push(next_token); + enroll(next_token, "variable", is_const); + advance(); + } else { + the_brace.names.push(name); + enroll(name, "variable", is_const); + } + if (next_token.id === ",") { + advance(","); + return pair(); + } + }()); + advance("}"); + advance("="); + the_brace.expression = expression(0); + the_statement.names.push(the_brace); + } else if (next_token.id === "[" && the_statement.id !== "var") { + const the_bracket = next_token; + the_bracket.names = []; + advance("["); + (function element() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + advance(); + the_bracket.names.push(name); + enroll(name, "variable", the_statement.id === "const"); + if (ellipsis) { + name.ellipsis = true; + } else if (next_token.id === ",") { + advance(","); + return element(); + } + }()); + advance("]"); + advance("="); + the_bracket.expression = expression(0); + the_statement.names.push(the_bracket); + } else if (next_token.identifier) { + const name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", is_const); + if (next_token.id === "=" || is_const) { + advance("="); + name.init = true; + name.expression = expression(0); + } + the_statement.names.push(name); + } else { + return stop("expected_identifier_a", next_token); + } + if (next_token.id === ",") { + if (!option.multivar) { + warn("expected_a_b", next_token, ";", ","); + } + advance(","); + return next(); + } + }()); + the_statement.open = ( + the_statement.names.length > 1 + && the_statement.line !== the_statement.names[1].line + ); + semicolon(); + return the_statement; +} + +stmt("const", do_var); +stmt("continue", function () { + const the_continue = token; + if (functionage.loop < 1 || functionage.finally > 0) { + warn("unexpected_a", the_continue); + } + not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; +}); +stmt("debugger", function () { + const the_debug = token; + if (!option.devel) { + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; +}); +stmt("delete", function () { + const the_token = token; + const the_value = expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; +}); +stmt("do", function () { + const the_do = token; + not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; +}); +stmt("export", function () { + const the_export = token; + let the_id; + let the_name; + let the_thing; + + function export_id() { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + the_id = next_token.id; + the_name = global.context[the_id]; + if (the_name === undefined) { + warn("unexpected_a"); + } else { + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a"); + } + exports[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + } + + the_export.expression = []; + if (next_token.id === "default") { + if (exports.default !== undefined) { + warn("duplicate_a"); + } + advance("default"); + the_thing = expression(0); + semicolon(); + exports.default = the_thing; + the_export.expression.push(the_thing); + } else { + if (next_token.id === "function") { + the_thing = statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a", the_name); + } + exports[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + warn("unexpected_a"); + } else if (next_token.id === "{") { + advance("{"); + (function loop() { + export_id(); + if (next_token.id === ",") { + advance(","); + return loop(); + } + }()); + advance("}"); + semicolon(); + } else { + export_id(); + if (the_name.writable !== true) { + warn("unexpected_a", token); + } + semicolon(); + } + } + module_mode = true; + return the_export; +}); +stmt("for", function () { + let first; + const the_for = token; + if (!option.for) { + warn("unexpected_a", the_for); + } + not_top_level(the_for); + functionage.loop += 1; + advance("("); + token.free = true; + if (next_token.id === ";") { + return stop("expected_a_b", the_for, "while (", "for (;"); + } + if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + return stop("unexpected_a"); + } + first = expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = expression(0); + advance(";"); + the_for.inc = expression(0); + if (the_for.inc.id === "++") { + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; +}); +stmt("function", do_function); +stmt("if", function () { + let the_else; + const the_if = token; + the_if.expression = condition(); + the_if.block = block(); + if (next_token.id === "else") { + advance("else"); + the_else = token; + the_if.else = ( + next_token.id === "if" + ? statement() + : block() + ); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + the_if.disrupt = true; + } else { + warn("unexpected_a", the_else); + } + } + } + return the_if; +}); +stmt("import", function () { + const the_import = token; + let name; + if (typeof module_mode === "object") { + warn("unexpected_directive_a", module_mode, module_mode.directive); + } + module_mode = true; + if (next_token.identifier) { + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + const names = []; + advance("{"); + if (next_token.id !== "}") { + while (true) { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (next_token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token; + if (!rx_module.test(token.value)) { + warn("bad_module_name_a", token); + } + froms.push(token.value); + semicolon(); + return the_import; +}); +stmt("let", do_var); +stmt("return", function () { + const the_return = token; + not_top_level(the_return); + if (functionage.finally > 0) { + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (next_token.id !== ";" && the_return.line === next_token.line) { + the_return.expression = expression(10); + } + advance(";"); + return the_return; +}); +stmt("switch", function () { + let dups = []; + let last; + let stmts; + const the_cases = []; + let the_disrupt = true; + const the_switch = token; + not_top_level(the_switch); + if (functionage.finally > 0) { + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token.free = true; + the_switch.expression = expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + (function major() { + const the_case = next_token; + the_case.arity = "statement"; + the_case.expression = []; + (function minor() { + advance("case"); + token.switch = true; + const exp = expression(0); + if (dups.some(function (thing) { + return are_similar(thing, exp); + })) { + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (next_token.id === "case") { + return minor(); + } + }()); + stmts = statements(); + if (stmts.length < 1) { + warn("expected_statements_a"); + return; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "break;", + artifact(next_token) + ); + } + if (next_token.id === "case") { + return major(); + } + }()); + dups = undefined; + if (next_token.id === "default") { + const the_default = next_token; + advance("default"); + token.switch = true; + advance(":"); + the_switch.else = statements(); + if (the_switch.else.length < 1) { + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + const the_last = the_switch.else[the_switch.else.length - 1]; + if (the_last.id === "break" && the_last.label === undefined) { + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; +}); +stmt("throw", function () { + const the_throw = token; + the_throw.disrupt = true; + the_throw.expression = expression(10); + semicolon(); + if (functionage.try > 0) { + warn("unexpected_a", the_throw); + } + return the_throw; +}); +stmt("try", function () { + let the_catch; + let the_disrupt; + const the_try = token; + if (functionage.try > 0) { + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (next_token.id === "catch") { + let ignored = "ignore"; + the_catch = next_token; + the_try.catch = the_catch; + advance("catch"); + advance("("); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + if (next_token.id !== "ignore") { + ignored = undefined; + the_catch.name = next_token; + enroll(next_token, "exception", true); + } + advance(); + advance(")"); + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "catch", + artifact(next_token) + ); + + } + if (next_token.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; +}); +stmt("var", do_var); +stmt("while", function () { + const the_while = token; + not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; +}); +stmt("with", function () { + stop("unexpected_a", token); +}); + +ternary("?", ":"); + +// Ambulation of the parse tree. + +function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; +} + +function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all of the +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + } + }; +} + +const posts = empty(); +const pres = empty(); +const preaction = action(pres); +const postaction = action(posts); +const preamble = amble(pres); +const postamble = amble(posts); + +function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.id === "function") { + walk_statement(thing.block); + } + if (thing.arity === "pre" || thing.arity === "post") { + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + warn("unexpected_statement_a", thing); + } + postamble(thing); + } + } +} + +function walk_statement(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_statement); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + ) { + warn( + (thing.id === "(string)" && thing.value === "use strict") + ? "unexpected_a" + : "unexpected_expression_a", + thing + ); + } + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + } +} + +function lookup(thing) { + if (thing.arity === "variable") { + +// Look up the variable in the current context. + + let the_variable = functionage.context[thing.id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable === undefined) { + stack.forEach(function (outer) { + const a_variable = outer.context[thing.id]; + if ( + a_variable !== undefined + && a_variable.role !== "label" + ) { + the_variable = a_variable; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (the_variable === undefined) { + if (declared_globals[thing.id] === undefined) { + warn("undeclared_a", thing); + return; + } + the_variable = { + dead: false, + parent: global, + id: thing.id, + init: true, + role: "variable", + used: 0, + writable: false + }; + global.context[thing.id] = the_variable; + } + the_variable.closure = true; + functionage.context[thing.id] = the_variable; + } else if (the_variable.role === "label") { + warn("label_a", thing); + } + if ( + the_variable.dead + && ( + the_variable.calls === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + ) { + warn("out_of_scope_a", thing); + } + return the_variable; + } +} + +function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); +} + +function preaction_function(thing) { + if (thing.arity === "statement" && blockage.body !== true) { + warn("unexpected_a", thing); + } + if (thing.level === 1) { + if ( + module_mode === true + || global.strict !== undefined + || thing.complex + ) { + if (thing.id !== "=>" && thing.block.strict !== undefined) { + warn("unexpected_a", thing.block.strict); + } + } else { + if (thing.block.strict === undefined) { + warn("use_strict", thing); + } + } + } + stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); +} + +function bitwise_check(thing) { + if (!option.bitwise && bitwiseop[thing.id] === true) { + warn("unexpected_a", thing); + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + warn("unexpected_a", thing); + } +} + +function pop_block() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); +} + +function activate(name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.init = true; + } + } + blockage.live.push(name); +} + +function action_var(thing) { + thing.names.forEach(activate); +} + +preaction("assignment", bitwise_check); +preaction("binary", bitwise_check); +preaction("binary", function (thing) { + if (relationop[thing.id] === true) { + const left = thing.expression[0]; + const right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + warn("expected_string_a", right); + } + } else { + const value = right.value; + if (value === "null" || value === "undefined") { + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + warn("expected_type_string_a", right, value); + } + } + } + } +}); +preaction("binary", "==", function (thing) { + warn("expected_a_b", thing, "===", "=="); +}); +preaction("binary", "!=", function (thing) { + warn("expected_a_b", thing, "!==", "!="); +}); +preaction("binary", "=>", preaction_function); +preaction("binary", "||", function (thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + warn("and", thang); + } + }); +}); +preaction("binary", "(", function (thing) { + const left = thing.expression[0]; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + const parent = functionage.name.parent; + if (parent) { + const left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + && left_variable.dead + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } +}); +preaction("binary", "in", function (thing) { + warn("infix_in", thing); +}); +preaction("binary", "instanceof", function (thing) { + warn("unexpected_a", thing); +}); +preaction("binary", ".", function (thing) { + if (thing.expression.new) { + thing.new = true; + } +}); +preaction("statement", "{", function (thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; +}); +preaction("statement", "for", function (thing) { + if (thing.name !== undefined) { + const the_variable = lookup(thing.name); + if (the_variable !== undefined) { + the_variable.init = true; + if (!the_variable.writable) { + warn("bad_assignment_a", thing.name); + } + } + } + walk_statement(thing.initial); +}); +preaction("statement", "function", preaction_function); +preaction("unary", "~", bitwise_check); +preaction("unary", "function", preaction_function); +preaction("variable", function (thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } +}); + +function init_variable(name) { + const the_variable = lookup(name); + if (the_variable !== undefined) { + if (the_variable.writable) { + the_variable.init = true; + return; + } + } + warn("bad_assignment_a", name); +} + +postaction("assignment", "+=", function (thing) { + let right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } +}); +postaction("assignment", function (thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + if (thing.id === "=") { + if (thing.names !== undefined) { + if (Array.isArray(thing.names)) { + thing.names.forEach(init_variable); + } else { + init_variable(thing.names); + } + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.writable !== true) { + warn("bad_assignment_a", lvalue); + } + } + const right = syntax[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + warn("unexpected_a", thing.expression[1]); + } + } +}); + +function postaction_function(thing) { + delete functionage.finally; + delete functionage.loop; + delete functionage.switch; + delete functionage.try; + functionage = stack.pop(); + if (thing.wrapped) { + warn("unexpected_parens", thing); + } + return pop_block(); +} + +postaction("binary", function (thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || are_similar(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option.convert) { + if (thing.expression[0].value === "") { + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === ".") { + if (thing.expression.id === "RegExp") { + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } +}); +postaction("binary", "&&", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "||", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "=>", postaction_function); +postaction("binary", "(", function (thing) { + let left = thing.expression[0]; + let the_new; + let arg; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option.eval) { + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + warn( + "expected_a_before_b", + left, + "new", + artifact(left) + ); + } + } + } else if (left.id === ".") { + let cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + cack = !cack; + } + if (rx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + warn("unexpected_a", the_new); + } else { + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + const paren = left.expression; + if (paren.id === "(") { + const array = paren.expression; + if (array.length === 1) { + const new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } +}); +postaction("binary", "[", function (thing) { + if (thing.expression[0].id === "RegExp") { + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + warn("weird_expression_a", thing.expression[1]); + } +}); +postaction("statement", "{", pop_block); +postaction("statement", "const", action_var); +postaction("statement", "export", top_level_only); +postaction("statement", "for", function (thing) { + walk_statement(thing.inc); +}); +postaction("statement", "function", postaction_function); +postaction("statement", "import", function (the_thing) { + const name = the_thing.name; + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return top_level_only(the_thing); +}); +postaction("statement", "let", action_var); +postaction("statement", "try", function (thing) { + if (thing.catch !== undefined) { + const the_name = thing.catch.name; + if (the_name !== undefined) { + const the_variable = functionage.context[the_name.id]; + the_variable.dead = false; + the_variable.init = true; + } + walk_statement(thing.catch.block); + } +}); +postaction("statement", "var", action_var); +postaction("ternary", function (thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || are_similar(thing.expression[1], thing.expression[2]) + ) { + warn("unexpected_a", thing); + } else if (are_similar(thing.expression[0], thing.expression[1])) { + warn("expected_a_b", thing, "||", "?"); + } else if (are_similar(thing.expression[0], thing.expression[2])) { + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + warn("wrap_condition", thing.expression[0]); + } +}); +postaction("unary", function (thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option.convert) { + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } +}); +postaction("unary", "function", postaction_function); +postaction("unary", "+", function (thing) { + if (!option.convert) { + warn("expected_a_b", thing, "Number(...)", "+"); + } + const right = thing.expression; + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } +}); + +function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + if (id !== "ignore") { + const name = the_function.context[id]; + if (name.parent === the_function) { + if ( + name.used === 0 + && ( + name.role !== "function" + || name.parent.arity !== "unary" + ) + ) { + warn("unused_a", name); + } else if (!name.init) { + warn("uninitialized_a", name); + } + } + } + }); +} + +function uninitialized_and_unused() { + +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (module_mode === true || option.node) { + delve(global); + } + functions.forEach(delve); +} + +// Go through the token list, looking at usage of whitespace. + +function whitage() { + let closer = "(end)"; + let free = false; + let left = global; + let margin = 0; + let nr_comments_skipped = 0; + let open = true; + let right; + + function expected_at(at) { + warn( + "expected_a_at_b_c", + right, + artifact(right), + fudge + at, + artifact_column(right) + ); + } + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function no_space() { + if (left.line === right.line) { + if (left.thru !== right.from && nr_comments_skipped === 0) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (open) { + const at = ( + free + ? margin + : margin + 8 + ); + if (right.from < at) { + expected_at(at); + } + } else { + if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (free) { + if (right.from < margin) { + expected_at(margin); + } + } else { + const mislaid = ( + stack.length > 0 + ? stack[stack.length - 1].right + : undefined + ); + if (!open && mislaid !== undefined) { + warn( + "expected_a_next_at_b", + mislaid, + artifact(mislaid.id), + margin + 4 + fudge + ); + } else if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + stack = []; + tokens.forEach(function (the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + + const new_closer = opener[left.id]; + if (typeof new_closer === "string") { + if (new_closer !== right.id) { + stack.push({ + closer: closer, + free: free, + margin: margin, + open: open, + right: right + }); + closer = new_closer; + if (left.line !== right.line) { + free = closer === ")" && left.free; + open = true; + margin += 4; + if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (right.switch) { + at_margin(-4); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + free = false; + open = false; + no_space_only(); + } + } else { + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like {]) have already been detected. + + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } + } else { + +// If right is a closer, then pop the previous state. + + if (right.id === closer) { + const previous = stack.pop(); + margin = previous.margin; + if (open && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + closer = previous.closer; + free = previous.free; + open = previous.open; + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-4); + } else if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + one_space(); + } else { + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + at_margin(0); + } else { + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + no_space(); + } else if ( + left.id === "." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + ) { + no_space_only(); + } else if (right.id === ".") { + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } else if (left.id === ";") { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + one_space_only(); + } else if (right.statement === true) { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.id === "var" + || left.id === "const" + || left.id === "let" + ) { + stack.push({ + closer: closer, + free: free, + margin: margin, + open: open + }); + closer = ";"; + free = false; + open = left.open; + if (open) { + margin = margin + 4; + at_margin(0); + } else { + one_space_only(); + } + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +// The jslint function itself. + +export default function jslint( + source = "", + option_object = empty(), + global_array = [] +) { + try { + warnings = []; + option = Object.assign(empty(), option_object); + anon = "anonymous"; + block_stack = []; + declared_globals = empty(); + directive_mode = true; + directives = []; + early_stop = true; + exports = empty(); + froms = []; + fudge = ( + option.fudge + ? 1 + : 0 + ); + functions = []; + global = { + id: "(global)", + body: true, + context: empty(), + from: 0, + level: 0, + line: 0, + live: [], + loop: 0, + switch: 0, + thru: 0 + }; + blockage = global; + functionage = global; + json_mode = false; + mega_mode = false; + module_mode = false; + next_token = global; + property = empty(); + stack = []; + tenure = undefined; + token = global; + token_nr = 0; + var_mode = undefined; + newlines_streak = 0; + populate(standard, declared_globals, false); + populate(global_array, declared_globals, false); + Object.keys(option).forEach(function (name) { + if (option[name] === true) { + const allowed = allowed_option[name]; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } + }); + tokenize(source); + advance(); + if (json_mode) { + tree = json_value(); + advance("(end)"); + } else { + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option.browser) { + if (next_token.id === ";") { + advance(";"); + } + } else { + +// If we are not in a browser, then the file form of strict pragma may be used. + + if ( + next_token.value === "use strict" + ) { + global.strict = next_token; + advance("(string)"); + advance(";"); + } + } + tree = statements(); + advance("(end)"); + functionage = global; + walk_statement(tree); + if (module_mode && global.strict !== undefined) { + warn("unexpected_a", global.strict); + } + if (warnings.length === 0) { + uninitialized_and_unused(); + if (!option.white) { + whitage(); + } + } + } + if (!option.browser) { + directives.forEach(function (comment) { + if (comment.directive === "global") { + warn("missing_browser", comment); + } + }); + } + early_stop = false; + } catch (e) { + if (e.name !== "JSLintError") { + warnings.push(e); + } + } + return { + directives, + edition: "2018-09-13", + exports, + froms, + functions, + global, + id: "(JSLint)", + json: json_mode, + lines, + module: module_mode === true, + ok: warnings.length === 0 && !early_stop, + option, + property, + stop: early_stop, + tokens, + tree, + warnings: warnings.sort(function (a, b) { + return a.line - b.line || a.column - b.column; + }) + }; +}; diff --git a/branch.unexpected_empty_lines/report.js b/branch.unexpected_empty_lines/report.js new file mode 100644 index 000000000..f123e9055 --- /dev/null +++ b/branch.unexpected_empty_lines/report.js @@ -0,0 +1,221 @@ +// report.js +// 2018-07-29 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Generate JSLint HTML reports. + +/*property + closure, column, context, edition, error, exports, filter, forEach, froms, + fudge, function, functions, global, id, isArray, join, json, keys, length, + level, line, lines, message, module, name, names, option, parameters, + parent, property, push, replace, role, signature, sort, stop, warnings +*/ + +const rx_amp = /&/g; +const rx_gt = />/g; +const rx_lt = / with less destructive entities. + + return String( + string + ).replace( + rx_amp, + "&" + ).replace( + rx_lt, + "<" + ).replace( + rx_gt, + ">" + ); +} + +export default { + error: function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + let fudge = Number(Boolean(data.option.fudge)); + let output = []; + if (data.stop) { + output.push("
    JSLint was unable to finish.
    "); + } + data.warnings.forEach(function (warning) { + output.push( + "
    ", + entityify(warning.line + fudge), + ".", + entityify(warning.column + fudge), + "
    ", + entityify(warning.message), + "
    ", + entityify(data.lines[warning.line] || ""), + "" + ); + }); + return output.join(""); + }, + + function: function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + let fudge = Number(Boolean(data.option.fudge)); + let mode = ( + data.module + ? "module" + : "global" + ); + let output = []; + + if (data.json) { + return ( + data.warnings.length === 0 + ? "
    JSON: good.
    " + : "
    JSON: bad.
    " + ); + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + let global = Object.keys(data.global.context).sort(); + let froms = data.froms.sort(); + let exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + let context = the_function.context; + let list = Object.keys(context); + output.push( + "
    ", + entityify(the_function.line + fudge), + "
    ", + ( + the_function.name === "=>" + ? entityify(the_function.signature) + " =>" + : ( + typeof the_function.name === "string" + ? "«" + entityify(the_function.name) + "»" + : "" + entityify(the_function.name.id) + "" + ) + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + let params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.parent === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.parent === the_function + ); + })); + detail("outer", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.parent !== the_function + && the_variable.parent.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].parent.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + output.push( + "
    JSLint edition ", + entityify(data.edition), + "
    " + ); + return output.join(""); + }, + + property: function property_directive(data) { + +// Produce the /*property*/ directive. + + let not_first = false; + let output = ["/*property"]; + let length = 1111; + let properties = Object.keys(data.property); + + if (properties.length > 0) { + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length >= 80) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); + } + } +}; diff --git a/branch.warning_early_stop/README b/branch.warning_early_stop/README new file mode 100644 index 000000000..f46e91069 --- /dev/null +++ b/branch.warning_early_stop/README @@ -0,0 +1,42 @@ +JSLint, The JavaScript Code Quality Tool + +Douglas Crockford +douglas@crockford.com + +2018-05-28 + +jslint.js contains the jslint function. It parses and analyzes a source file, +returning an object with information about the file. It can also take an object +that sets options. + +index.html runs the jslint.js function in a web page. The page also depends +browser.js and report.js and jslint.css. + +jslint.css provides styling for index.html. + +browser.js runs the web user interface. + +report.js generates the results reports in HTML. + +help.html describes JSLint's usage. Please read it. + +function.html describes the jslint function and the results it produces. + +Please direct questions and comments to +https://plus.google.com/communities/104441363299760713736. + +JSLint can be run anywhere that JavaScript (or Java) can run. + +The place to express yourself in programming is in the quality of your ideas and +the efficiency of their execution. The role of style in programming is the same +as in literature: It makes for better reading. A great writer doesn't express +herself by putting the spaces before her commas instead of after, or by putting +extra spaces inside her parentheses. A great writer will slavishly conform to +some rules of style, and that in no way constrains her power to express herself +creatively. See for example William Strunk's The Elements of Style +[http://www.crockford.com/wrrrld/style.html]. + +This applies to programming as well. Conforming to a consistent style improves +readability, and frees you to express yourself in ways that matter. JSLint here +plays the part of a stern but benevolent editor, helping you to get the style +right so that you can focus your creative energy where it is most needed. diff --git a/branch.warning_early_stop/browser.js b/branch.warning_early_stop/browser.js new file mode 100644 index 000000000..5086fed85 --- /dev/null +++ b/branch.warning_early_stop/browser.js @@ -0,0 +1,158 @@ +// browser.js +// 2018-06-16 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +/*jslint + browser +*/ + +/*property + checked, create, disable, display, error, focus, forEach, function, + getElementById, innerHTML, join, length, map, onchange, onclick, onscroll, + property, querySelectorAll, scrollTop, select, split, style, title, value +*/ + +import jslint from "./jslint.js"; +import report from "./report.js"; + +// This is the web script companion file for JSLint. It includes code for +// interacting with the browser and displaying the reports. + +let rx_crlf = /\n|\r\n?/; +let rx_separator = /[\s,;'"]+/; + +let fudge_unit = 0; + +const aux = document.getElementById("JSLINT_AUX"); +const boxes = document.querySelectorAll("[type=checkbox]"); +const fudge = document.getElementById("JSLINT_FUDGE"); +const global = document.getElementById("JSLINT_GLOBAL"); +const number = document.getElementById("JSLINT_NUMBER"); +const property = document.getElementById("JSLINT_PROPERTY"); +const property_fieldset = document.getElementById("JSLINT_PROPERTYFIELDSET"); +const report_field = document.getElementById("JSLINT_REPORT"); +const report_list = document.getElementById("JSLINT_REPORT_LIST"); +const source = document.getElementById("JSLINT_SOURCE"); +const select = document.getElementById("JSLINT_SELECT"); +const warnings = document.getElementById("JSLINT_WARNINGS"); +const warnings_list = document.getElementById("JSLINT_WARNINGS_LIST"); + +function show_numbers() { + number.value = source.value.split(rx_crlf).map(function (ignore, index) { + return index + fudge_unit; + }).join("\n"); +} + +function clear() { + aux.style.display = "none"; + number.value = ""; + property.value = ""; + property_fieldset.style.display = "none"; + report_field.style.display = "none"; + report_list.innerHTML = ""; + source.focus(); + source.value = ""; + warnings.style.display = "none"; + warnings_list.innerHTML = ""; +} + +function fudge_change() { + fudge_unit = Number(fudge.checked); + show_numbers(); +} + +function clear_options() { + boxes.forEach(function (node) { + node.checked = false; + }); + fudge_change(); + global.innerHTML = ""; +} + +function call_jslint() { + +// First build the option object. + + let option = Object.create(null); + boxes.forEach(function (node) { + if (node.checked) { + option[node.title] = true; + } + }); + +// Call JSLint with the source text, the options, and the predefined globals. + + let global_string = global.value; + let result = jslint( + source.value, + option, + ( + global_string === "" + ? undefined + : global_string.split(rx_separator) + ) + ); + +// Generate the reports. + + let error_html = report.error(result); + let function_html = report.function(result); + let property_text = report.property(result); + +// Display the reports. + + warnings_list.innerHTML = error_html; + warnings.style.display = ( + error_html.length === 0 + ? "none" + : "block" + ); + + report_list.innerHTML = function_html; + report_field.style.display = "block"; + if (property_text) { + property.value = property_text; + property_fieldset.style.display = "block"; + property.scrollTop = 0; + select.disable = false; + } else { + property_fieldset.style.display = "none"; + select.disable = true; + } + aux.style.display = "block"; + source.select(); +} + +fudge.onchange = fudge_change; + +source.onchange = function (ignore) { + show_numbers(); +}; + +source.onscroll = function () { + let ss = source.scrollTop; + number.scrollTop = ss; + let sn = number.scrollTop; + if (ss > sn) { + show_numbers(); + number.scrollTop = ss; + } +}; + +document.querySelectorAll("[name='JSLint']").forEach(function (node) { + node.onclick = call_jslint; +}); + +document.querySelectorAll("[name='clear']").forEach(function (node) { + node.onclick = clear; +}); + +document.getElementById("JSLINT_SELECT").onclick = function () { + property.focus(); + property.select(); +}; +document.getElementById("JSLINT_CLEAR_OPTIONS").onclick = clear_options; + +fudge_change(); +source.select(); +source.focus(); diff --git a/branch.warning_early_stop/cli.js b/branch.warning_early_stop/cli.js new file mode 100755 index 000000000..3ac2b3044 --- /dev/null +++ b/branch.warning_early_stop/cli.js @@ -0,0 +1,130 @@ +#!/usr/bin/env node +/* + * cli.js + * simple cli-tool to run jslint.js from command-line + * + * instruction: + * 1. save this file as cli.js + * 2. download and save in same directory, latest jslint.js from + * https://github.com/douglascrockford/JSLint/blob/master/jslint.js + * + * example usage: + * $ node cli.js ./jslint.js // jslint local-file + * $ node cli.js https://code.jquery.com/jquery-1.0.0.js // jslint remote-file + */ + + + +/*jslint node */ +/*property + argv, basename, column, concat, end, error, exit, filter, forEach, fudge, + join, jslint, line, lines, main, match, message, on, option, parse, push, + readFile, replace, request, runInNewContext, slice, trim, trimRight, + warnings +*/ +"use strict"; +let chunk_list; +let file; +let fs; +let fudge; +let local; +let modeNext; +let onNext; +let path; +let url; +let vm; +let warning_text; +onNext = function (error, data) { + if (error) { + console.error(error.message || error); + process.exit(1); + } + modeNext += 1; + switch (modeNext) { + case 1: + // run this program only in cli-mode + if (module !== require.main) { + console.error( + "jslint-cli can only be run from the command-line" + ); + return; + } + // require builtins + fs = require("fs"); + path = require("path"); + url = require("url"); + vm = require("vm"); + // init local namespace + local = {}; + if (!process.argv[2]) { + console.error( + "error: no specified\n" + + "usage: " + path.basename(__filename) + " \n" + + "example: " + path.basename(__filename) + " example.js\n" + + "example: " + path.basename(__filename) + + " https://jslint.com/jslint.js" + ); + process.exit(1); + } + // init jslint + fs.readFile(path.join(__dirname, "jslint.js"), "utf8", onNext); + break; + case 2: + // bug-workaround - nodejs does not widely support es-modules + vm.runInThisContext( + data.replace("export default function", "function") + ); + local.jslint = jslint; + // read data from file + file = process.argv[2]; + console.error("jslint " + file); + data = file.match(/^(https?):\/\//); + if (!data) { + fs.readFile(file, "utf8", onNext); + return; + } + // read data from http/https url + require(data[1]).request(url.parse(file), function (response) { + chunk_list = []; + response + .on("data", function (chunk) { + chunk_list.push(chunk); + }) + .on("end", function () { + onNext(null, String(Buffer.concat(chunk_list))); + }) + .on("error", onNext); + }).on("error", onNext).end(); + break; + case 3: + // jslint data + data = local.jslint(data + // ignore first-line shebang in nodejs scripts + .replace((/^#!/), "//")); + fudge = data.option.fudge || 0; + // init warning_text + warning_text = ""; + // print warnings to stderrr + data.warnings + .filter(function (warning) { + return warning && warning.message; + }) + // print only first 10 warnings + .slice(0, 10) + .forEach(function (warning, ii) { + warning_text += ( + (" #" + (ii + 1)).slice(-3) + + " \u001b[31m" + warning.message + "\u001b[39m\n" + + " " + String(data.lines[warning.line] || "").trim() + + "\u001b[90m \/\/ line " + + (warning.line + fudge) + + ", col " + (warning.column + fudge) + + "\u001b[39m\n" + ); + }); + console.error(warning_text.trimRight() || "ok"); + break; + } +}; +modeNext = 0; +onNext(); diff --git a/branch.warning_early_stop/function.html b/branch.warning_early_stop/function.html new file mode 100644 index 000000000..7d8271246 --- /dev/null +++ b/branch.warning_early_stop/function.html @@ -0,0 +1,618 @@ + + + + + + + + +JSLint: jslint function + + + +
    JSLint
    + +
    The jslint Function
    +
    +

    JSLint

    +

    JSLint is delivered as a set of files:

    +
    + + + + + + + + + + + + + + +
    FilenameContent
    browser.jsBrowser based UI.
    function.htmlThis page that you are looking at right now.
    help.htmlUsing jslint as a single page JSLint application.
    index.htmlSingle page JSLint application that uses the + js files.
    jslint.jsThe jslint function.
    report.jsThe report object, containing report generator functions for + HTML.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    +
    + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring"(JSLint)"
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    parenttokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    parenttokenThe function in which the variable was declared.variable
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    shebangstringThe first line of text if it started with #!line
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable can be assigned to.variable
    +

    Reports

    +

    The report object contains three functions that all take a + result from the jslint function as input. + report.js has no other dependence on other files.

    + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    +
    + +
    + + + + + +
    + + diff --git a/branch.warning_early_stop/help.html b/branch.warning_early_stop/help.html new file mode 100644 index 000000000..94b431214 --- /dev/null +++ b/branch.warning_early_stop/help.html @@ -0,0 +1,835 @@ + + + + + + + + + +JSLint: Help + + + +
    JSLint
    + +
    Help
    +
    +
    Il semble que la perfection soit atteinte non quand il n’y a + plus rien à ajouter, mais quand il n’y a plus rien à + retrancher.
    +
    + Antoine de Saint-Exupéry +
    +
    + Terre des Hommes (1939) +
    +

    Good Parts

    +

    The Principle of the Good Parts is

    +
    If a feature is sometimes useful and sometimes dangerous and if + there is a better option then always use the better option.
    +

    JSLint is a JavaScript program that looks for problems in + JavaScript programs. It is a code quality tool.

    +

    When C was + a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    +

    As the language matured, the definition of the language was + strengthened to eliminate some insecurities, and compilers got better + at issuing warnings. lint is no longer needed.

    +

    JavaScript is a + young-for-its-age language. It was originally intended to do small tasks in + webpages, tasks for which Java was too heavy and clumsy. But JavaScript is + a surprisingly capable language, and it is now being used in larger + projects. Many of the features that were intended to make the language easy + to use are troublesome when projects become complicated. A lint + for JavaScript is needed: JSLint, a JavaScript syntax checker + and validator.

    +

    JSLint takes a JavaScript source and scans it. If it finds a + problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by the ECMAScript Programming Language + Standard (the strangely named document that governs JavaScript). + JSLint will reject most legal programs. It is a higher + standard.

    +

    JavaScript is a sloppy language, but hidden deep inside there is an elegant, + better language. JSLint helps you to program in that better + language and to avoid most of the slop. JSLint will reject + programs that browsers will accept because JSLint is concerned + with the quality of your code and browsers are not. You should gladly accept + all of JSLint's advice.

    +

    JSLint can operate on JavaScript source + or JSON text.

    +

    ECMAScript Sixth Edition

    +

    Some of + ES6’s features are good, so JSLint will recognize the good + parts of ES6.

    +

    Currently, these features are recognized:

    +
      +
    • The ... ellipsis marker in parameter + lists and argument lists, replacing the arguments object + for variadic functions.
    • +
    • The let statement, which is like the var + statement except that it respects block scope. + You may use let or var but not both.
    • +
    • The const statement is like the let statement + except that it disallows the use of assignment on the variable, although + if the value of the variable is mutable, it can still be mutated. + const is preferred to let. +
    • Destructuring of arrays and objects is allowed in parameter lists and on + the left side of let, and const, but not + var or assignment statements, and not deep destructuring + or eliding.
    • +
    • Enhanced object literals, providing shorter forms for function + declaration and properties that are initialized by variables with the + same name.
    • +
    • The fat arrow => fart functions.
    • +
    • The simplest forms of import and export.
    • +
    • `Megastring` literals, but not nested + `megastring` literals.
    • +
    • New global functions, such as Map, Set, + WeakMap, and WeakSet.
    • +
    • 0b- and 0o- number literals.
    • +
    +

    The most important new feature of ES6 is proper tail calls. This has no + new syntax, so JSLint doesn’t see it. But it makes recursion + much more attractive, which makes loops, particularly for + loops, much less attractive.

    +

    import export

    +

    The ES6 module feature will be an important improvement over JavaScript’s + global variables as a means of linking separate files together. + JSLint recognizes a small but essential subset of the module + syntax.

    +
    +

    import name from stringliteral;

    +

    import {name} from stringliteral;

    +

    export default function () {};

    +

    export default function name() {};

    +

    export default expression;

    +

    export function name() {}

    +

    export name; // where name is const

    +

    export {name};

    +
    +

    Directives

    +

    JSLint provides three directives that may be placed in a + file to manage JSLint’s behavior. Use of these directives is + optional. If they are used, they should be placed in a source file before + the first statement. They are written in the form of a comment, where the + directive name is placed immediately after the opening of the comment before + any whitespace. The three directives are global, + jslint, and property. Directives in a file are + stronger than options selected from the UI or passed with the option object.

    +

    /*global*/

    +

    The /*global*/ directive is used to specify a set of globals + (usually functions and objects containing functions) that are available to + this file. This was commonly used in browsers to link source files together + before ES6 modules appeared. Use of global variables is strongly + discouraged, but unfortunately web browsers require their use. The + /*global*/ directive can only be used when the + Assume a browser option is selected. +

    Each of the names listed will indicate a read-only global variable. The names + are separated by , comma. This directive + should not be used if import or export is used. + This directive inhibits warnings, but it does not declare the names in the + execution environment. +

    For example: +

    /*global +
    ADSAFE, report, jslint
    +*/
    +

    instructs JSLint to not give warnings about the global + variables ADsafe, report, and jslint. + However, if any of those names are expected to be supplied by other files + and those other files fail to do so, then execution errors will result. It + is usually better to use top-level variable declarations instead: +

        var ADSAFE;
    +    var report;
    +    var jslint; 
    +

    Using var in this way allows comparing a global variable to the + undefined value to determine whether it is has been used in the global context.

    +

    /*jslint*/

    +

    The /*jslint*/ directive allows for the control of several + options. These options can also be set from the + JSLint.com user interface.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Tolerate bitwise operatorsbitwisetrue if bitwise operators should be allowed. The + bitwise operators are rarely used in JavaScript programs, so it + is usually more likely that & is a mistyping of + && than that it indicates a bitwise and. + JSLint will give warnings on the bitwise operators + unless this option is selected.
    Assume a browser browsertrue if the standard browser globals should be + predefined. This option will reject the use of + import and export. This option also + disallows the file form of the "use + strict" pragma. It does not supply + self; you will have to request that unnecessary + alias of the dreaded global object yourself. It adds the same + globals as this directive: +
    /*global +
    + clearInterval, clearTimeout, document, event, FileReader, + FormData, history, localStorage, location, name, navigator, + screen, sessionStorage, setInterval, setTimeout, Storage, + URL, window, XMLHttpRequest +
    + */
    +
    Tolerate conversion operatorsconverttrue if !!, + prefix, + and concatenation with "" are allowed. + The Boolean, Number, and + String functions are preferred
    Assume CouchDBcouchtrue if + Couch DB + globals should be predefined. It adds the same globals as this + directive: +
    /*global +
    emit, getRow, isArray, log, provides, registerType, require, + send, start, sum, toJSON
    + */
    Assume in developmentdeveltrue if browser globals that are useful in + development should be predefined, and if debugger + statements and TODO comments + should be allowed. It adds the + same globals as this directive: +
    /*global +
    alert, confirm, console, prompt
    + */
    Be sure to turn this option off before going + into production.
    Tolerate evalevaltrue if eval should be allowed. In the + past, the eval function was the most misused + feature of the language.
    Tolerate for statementfortrue if the for statement should be + allowed. It is almost always better to use the array methods + instead.
    Fudge the line and column numbersfudgetrue if the origin of a text file is line 1 column + 1. Programmers know that number systems start at zero. + Unfortunately, most text editors start numbering lines and + columns at one. JSLint will by default start + numbering at zero. Use this option to start the numbering at one + in its reports.
    Tolerate get and setgetsettrue if accessor properties are allowed in object literals.
    Tolerate long source lineslongtrue if a line can contain more than 80 characters.
    Tolerate multiple variables declarations per statementmultivartrue if a var, let, or + const statement can declare two or more variables + in a single statement.
    Assume Node.jsnodetrue if Node.js globals should be predefined. It + will predefine globals that are used in the Node.js environment. + It adds the same globals as this directive: +
    /*global +
    + Buffer, clearImmediate, clearInterval, clearTimeout, console, exports, + module, process, require, setImmediate, setInterval, setTimeout, URL, + URLSearchParams, __dirname, __filename +
    + */
    Tolerate single quote stringssingletrue if 'single quote + should be allowed to enclose string literals.
    Tolerate this
    thistrue if this should be allowed.
    Tolerate whitespace messwhitetrue if the whitespace rules should be ignored.
    +

    For example:

    +
    /*jslint
    +    bitwise, node
    +*/
    + +

    /*property*/

    +

    The /*property*/ directive is used to declare a list of + property identifiers that are used in the file. Each property name in the + program is looked up in this list. If a name is not found, that indicates an + error, most likely a typing error.

    +

    The list can also be used to evade some of JSLint’s naming + rules.

    +

    JSLint can build the /*property*/ list for you. At + the bottom of its report, JSLint displays a + /*property*/ directive. It contains all of the names that were + used with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. You + can copy the /*property*/ directive to the top of your + script file. JSLint will check the spelling of all property + names against the list. That way, you can have JSLint look for + misspellings for you.

    +

    For example,

    +
    /*property +
    charAt, slice, _$_
    + */
    +

    ignore

    +

    JSLint introduces a new reserved word: ignore. It + is used in parameter lists and in catch clauses to indicate a + parameter that will be ignored. Unused warnings will not be produced for + ignore. +

    function handler(ignore, value) {
    +    return do_something_useful(value);
    +}
    +

    var let const

    +

    JavaScript provides three statements for declaring variables: + var, let, and const. The + var statement suffers from a bad practice called hoisting. The + let statement does not do hoisting and respects block scope. + The const statement is like let except that it + marks the variable (but not its contents) as read only, making it an error + to attempt to assign to the variable. When given a choice, + const is the best, var is the worst.

    +

    JSLint uses the intersection of the var rules and the + let rules, and by doing so avoids the errors related to either. + A name should be declared only once in a function. It should be declared + before it is used. It should not be used outside of the block in which it is + declared. A variable should not have the same name as a variable or + parameter in an outer function. Do not mix var and + let. Declare one name per statement.

    +

    = == ===

    +

    Fortran made a terrible + mistake in using the equality operator as its assignment operator. That + mistake has been replicated in most languages since then. C compounded that + mistake by making == its equality operator. The visual + similarity is a source of errors. JavaScript compounded this further by + making == a type coercing comparison operator that produces + false positive results. This was mitigated by adding the === + operator, leaving the broken == operator in place.

    +

    JSLint attempts to minimize errors by the following rules:

    +

    == is not allowed. This avoids the false positives, and + increases the visual distance between = and ===. + Assignments are not allowed in expression position, and comparisons are not + allowed in statement position. This also reduces confusion. 

    +

    Semicolon

    +

    JavaScript uses a C-like syntax that requires the use of semicolons to + delimit certain statements. JavaScript attempts to make those semicolons + optional with an automatic semicolon insertion mechanism, but it does not + work very well. Automatic semicolon insertion was added to make + things easier for beginners. Unfortunately, it sometimes fails. Do not rely + on it unless you are a beginner.

    +

    JSLint expects that every statement will be followed by + ; except for for, function, + if, switch, try, and + while. JSLint does not expect to see unnecessary + semicolons, the empty statement, or empty blocks.

    +

    function =>

    +

    JavaScript has four syntactic forms for making function objects: function + statements, function expressions, enhanced object literals, and the + => fart operator.

    +

    The function statement creates a variable and assigns the function object to + it. It should be used in a file or function body, but not inside of a block.

    +
    function name(parameters) { +
    statements
    + }
    +

    The function expression unfortunately looks like the function statement. It + may appear anywhere that an expression may appear, but not in statement + position and not in a loop. It produces a function object but does not + create a variable in which to store it.

    +
    function (parameters) { +
    statements
    + }
    +

    The enhanced object literal provides an ES6 shorthand for creating a property + whose value is a function, saving you from having to type + : colon and function.

    +
    { +
    name(parameters) { +
    statements
    + }
    + }
    +

    Finally, ES6 provides an even shorter form of function expression that leaves + out the words function and return:

    +
    (parameters) => + expression
    +

    JSLint requires the parens around the parameters, and forbids a + { left brace after the + => fart to avoid syntactic ambiguity.

    +

    Comma

    +

    The , comma operator is unnecessary and can + mask programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as + an operator. It does not expect to see elided elements in array literals. A + comma should not appear after the last element of an array literal or object + literal.

    +

    Blocks

    +

    JSLint expects blocks with function, + if, switch, while, for, + do, and try statements and nowhere else.

    +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    +

    JavaScript allows an if to be written like this:

    +
    if (condition)
    +    statement;
    +

    That form is known to contribute to mistakes in projects where many + programmers are working on the same code. That is why JSLint + expects the use of a block:

    +
    if (condition) {
    +    statements;
    +}
    +

    Experience shows that this form is more resilient.

    +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call. All other expression statements are considered + to be errors.

    +

    for

    +

    JSLint does not recommend use of the for statement. + Use array methods like forEach instead. The for + option will suppress some warnings. The forms of for that + JSLint accepts are restricted, excluding the new ES6 forms.

    +

    for in

    +

    JSLint does not recommend use of the for + in statement. Use Object.keys instead.

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, it also + loops through all of the properties that were inherited through the + prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written + without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    for (name in object) {
    +    if (object.hasOwnProperty(name)) {
    +        ....
    +    }
    +}
    +

    Note that the above code will fail if the object contains a property + named hasOwnProperty. Use Object.keys instead.

    +

    switch

    +

    A common error in switch statements is to forget to place a + break statement after each case, resulting in unintended + fall-through. JSLint expects that the statement before the next + case or default is one of these: + break, return, or throw.

    +

    with

    +

    The with statement was intended to provide a shorthand in + accessing properties in deeply nested objects. Unfortunately, it behaves + very badly when setting new properties. Never use the with + statement.

    +

    JSLint does not expect to see a with statement.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that + labels will be distinct from vars and parameters.

    +

    Unreachable Code

    +

    JSLint expects that a return, break, + or throw statement will be followed by a + } right brace or case or + default.

    +

    Confusing Pluses and Minuses

    +

    JSLint expects that + will not be followed by + + or ++, and that - will not be + followed by - or --. A misplaced space can turn + + + into ++, an error that is difficult to see. + Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ increment and + -- decrement operators have been known to + contribute to bad code by encouraging excessive trickiness. They are second + only to faulty architecture in enabling to viruses and other security + menaces. Also, preincrement/postincrement confusion can produce off-by-one + errors that are extremely difficult to diagnose. Fortunately, they are also + complete unnecessary. There are better ways to add 1 to a variable.

    +

    It is best to avoid these operators entirely and rely on += and + -= instead.

    +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. + JSLint looks for problems that may cause portability problems. + It also attempts to resolve visual ambiguities by recommending explicit + escapement.

    +

    JavaScript’s syntax for regular expression literals overloads the + / slash character. To avoid ambiguity, + JSLint expects that the character preceding a regular + expression literal is a ( left paren or + = equal or + : colon or + , comma character.

    +

    this

    +
    Having this in the language makes it harder to talk + about the language. It is like pair programming with Abbott and Costello. +
    +

    Avoid using this. Warnings about this can be + suppressed with option.this.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the + new prefix. The new prefix creates a new object + based on the function's prototype, and binds that object to the + function's implied this parameter. If you neglect to use the + new prefix, no new object will be made and this + will be bound to the global object.

    +

    JSLint enforces the convention that constructor functions be + given names with initial uppercase. JSLint does not expect to + see a function invocation with an initial uppercase name unless it has the + new prefix. JSLint does not expect to see the + new prefix used with functions whose names do not start with + initial uppercase.

    +

    JSLint does not expect to see the wrapper forms + new Number, new String, new Boolean.

    +

    JSLint does not expect to see new Object. + Use Object.create(null) instead.

    +

    Whitespace

    +

    JSLint has a specific set of rules about the use of whitespace. + Where possible, these rules are consistent with centuries of good practice + with literary style.

    +

    The indentation increases by 4 spaces when the last token on a line is + { left brace, + [ left bracket, + ( left paren. The matching closing + token will be the first token on a line, restoring the previous + indentation.

    +

    The ternary operator can be visually confusing, so + ? question mark and + : colon always begin a line and increase + the indentation by 4 spaces.

    +
    return (
    +    (the_token.id === "(string)" || the_token.id === "(number)")
    +    ? String(the_token.value)
    +    : the_token.id
    +);
    +

    The word function is always followed with one space.

    +

    Clauses (case, catch, default, + else, finally) are not statements and so should + not be indented like statements.

    +

    Spaces are used to make things that are not invocations look less like + invocations.

    +

    Tabs and spaces should not be mixed. We should pick just one in order to + avoid the problems that come from having both. Personal preference is an + extremely unreliable criterion. Neither offers a powerful advantage over the + other. Fifty years ago, tab had the advantage of consuming less memory, but + Moore's Law has eliminated that advantage. Space has one clear advantage + over tab: there is no reliable standard for how many spaces a tab + represents, but it is universally accepted that a space occupies a space. So + use spaces. You can edit with tabs if you must, but make sure it is spaces + again before you commit. Maybe someday we will finally get a universal + standard for tabs, but until that day comes, the better choice is spaces.

    +

    Unsafe Characters

    +

    There are characters that are handled inconsistently in some browsers, and + so must be escaped when placed in strings.

    +
    \u0000-\u001f
    +\u007f-\u009f
    +\u00ad
    +\u0600-\u0604
    +\u070f
    +\u17b4
    +\u17b5
    +\u200c-\u200f
    +\u2028-\u202f
    +\u2060-\u206f
    +\ufeff
    +\ufff0-\uffff
    +

    Report

    +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    +
      +
    • The line number on which it starts.
    • +
    • The function’s name. In the case of anonymous functions, + JSLint will attempt to + «guess» the name.
    • +
    • The parameters.
    • +
    • Variables: The variables that are declared in the function.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Exceptions: The variables that are declared by catch + clauses of try statements.
    • +
    • Outer: Variables used by this function that are declared in + other functions.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements. Updates are announced at + https://plus.google.com/communities/104441363299760713736.

    +

    Try it

    +

    Try it. Paste your script + into the window and click the + + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    JSLint is written entirely in JavaScript, so it can run + anywhere that JavaScript (or even Java) can run.

    +

    Perfectly fine

    +

    JSLint was designed to reject code that some would consider to + be perfectly fine. The reason for this is that JSLint's + purpose is to help produce programs that are free of error. That is + difficult in any language and is especially hard in JavaScript. + JSLint attempts to help you increase the visual distance + between correct programs and incorrect programs, making the remaining errors + more obvious. JSLint will give warnings about things that are + not necessarily wrong in the current situation, but which have been observed + to mask or obscure errors. Avoid those things when there are better options + available.

    +

    It is dangerous out there. JSLint is here to help.

    +

    Warning

    +

    JSLint will hurt your feelings. Side effects may include + headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, + dry mouth, cleaner code, and a reduced error rate.

    +

    Further Reading

    + +
    +

    + Code Conventions for the JavaScript Programming Language.

    + + + + + +
    + + diff --git a/branch.warning_early_stop/index.html b/branch.warning_early_stop/index.html new file mode 100644 index 000000000..235f44b54 --- /dev/null +++ b/branch.warning_early_stop/index.html @@ -0,0 +1,125 @@ + + + + + + + + + + + +JSLint: The JavaScript Code Quality Tool + + +
    +
    Source
    +
    + + +
    +
    + Warnings +
    +
    +
    + Function Report +
    +
    +
    + Property Directive + +
    +
    + + + + +
    +
    Options +
    Assume... +
    +
    +
    +
    +
    Fudge... +
    +
    +
    Tolerate... +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Global variables... + +
    +
    +
    +
    + + + + + +
    + + diff --git a/branch.warning_early_stop/jslint.css b/branch.warning_early_stop/jslint.css new file mode 100644 index 000000000..9c1b1a2f9 --- /dev/null +++ b/branch.warning_early_stop/jslint.css @@ -0,0 +1,340 @@ +@font-face { + font-family: 'Programma'; + src: url('Programma.woff') format('woff'); +} +@font-face { + font-family: 'Daley'; + font-weight: bold; + src: url('Daley-Bold.woff') format('woff'); +} + +body { + background-color: antiquewhite; + color: black; + font-family: 'Daley', sans-serif; + margin: 0; + padding: 0; +} + +#JSLINT_TITLEBOX { + font-family: 'Daley', sans-serif; + margin: 0; + margin-left: 3%; + margin-right: 3%; + padding: 0; +} + +#JSLINT_TITLEBOX ul { + float: right; + font-size: 12pt; +} + +#JSLINT_TITLEBOX div { + color: darkslategray; + float: left; + font-size: 48pt; +} + +#JSLINT_ fieldset { + background-color: gainsboro; + border: 0; + clear: both; + margin-bottom: 1em; + margin-left: 3%; + margin-right: 3%; + margin-top: 1em; + padding: 0; + width: auto; +} +#JSLINT_ legend { + background-color: darkslategray; + border: 0; + color: white; + font-size: 100%; + font-style: normal; + font-weight: normal; + margin: 0; + padding-bottom: 0.25em; + padding-left: 0; + padding-right: 0; + padding-top: 0.25em; + text-align: center; + width: 100%; +} +#JSLINT_ button { + background-color: darkslategray; + border: 0; + color: white; + cursor: pointer; + font-family: 'Daley', sans-serif; + font-size: 100%; + font-style: normal; + margin-left: 1em; + margin-right: 1em; + padding-left: 1em; + padding-right: 1em; + padding-bottom: 0.25em; + padding-top: 0.25em; + text-align: center; +} +#JSLINT_ button:hover { + background-color: slategray; + color: white; + border: 0; +} + +#JSLINT_ button:active { + border: 0; + color: black; +} + +#JSLINT_ button:disabled { + background-color: gray; + border: 0; + color: gainsboro; +} + +a:visited { + color: #69565c; +} + +a:link { + color: black; +} + +#JSLINT_ input[type="text"] { + background-color: white; + border: 0; + color: black; + margin-bottom: 0.2em; + margin-right: 0.5em; + padding-left: 0.5em; + padding-right: 0.5em; + text-align: right; + width: 3em; +} + +#JSLINT_ textarea { + border: 0; + background-color: white; + border: 0; + font-family: Programma, monospace; + font-size: 100%; + font-weight: bold; + resize: none; +} + +#JSLINT_ textarea::selection { + background-color: yellow; + color: black; +} + +#JSLINT_NUMBER { + color: darkgray; + display: inline-block; + height: 4in; + margin: 2%; + margin-right: 0; + overflow: hidden; + padding-right: 0.5%; + text-align: right; + white-space: pre; + width: 4%; +} + +#JSLINT_SOURCE { + color: black; + display: inline-block; + font-size: 100%; + height: 4in; + margin: 2%; + margin-left: 0; + margin-right: 0; + overflow: auto; + padding-left: 0.5%; + white-space: pre; + width: 91%; +} + +#JSLINT_PROPERTY { + background-color: honeydew; + border: 0; + margin: 2%; + padding: 0.5%; + white-space: pre; + width: 95%; +} + +#JSLINT_GLOBAL { + white-space: normal; + width: 95%; +} +#JSLINT_ label { + font-family: sans-serif; + font-size: 90%; + padding-left: 0.25em; +} +#JSLINT_OPTIONS>div { + float: left; + margin: 0.5em; +} + +#JSLINT_ address { + color: black; + display: block; + float: right; + font-family: serif; + font-size: 90%; + margin-left: 1em; +} +#JSLINT_ dl { + background-color: cornsilk; + color: white; + margin: 0; + padding-bottom: 2pt; + padding-left: 1em; + padding-right: 1em; + padding-top: 2pt; +} +#JSLINT_ dfn { + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + margin-bottom: 2pt; +} +#JSLINT_ dt { + color: black; + display: block; + float: left; + font-family: serif; + font-size: 75%; + font-style: italic; + margin: 0; + width: 8em; + text-align: right; +} +#JSLINT_ dd { + color: black; + display: block; + font-family: Programma, monospace; + font-weight: bold; + margin-left: 8em; + padding-bottom: 2pt; +} +#JSLINT_WARNINGS>legend { + background-color: indianred; +} +#JSLINT_WARNINGS>div { + background-color: pink; + padding: 1em; +} +#JSLINT_WARNINGS cite { + color: black; + display: block; + font-family: serif; + font-size: 100%; + font-style: normal; + margin-bottom: 4pt; + margin-left: 20pt; + margin-right: 20pt; + margin-top: 4pt; + overflow-x: hidden; +} +#JSLINT_WARNINGS samp { + background-color: lavenderblush; + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + padding: 4pt; + margin-bottom: 0; + margin-left: 16pt; + margin-right: 16pt; + margin-top: 0; + white-space: pre-wrap; +} + +#JSLINT_REPORT center { + margin-top: 1em; +} + +#JSLINT_WARNINGS dl address { + color: black; + display: inline; + float: none; + font-size: 80%; + margin: 0; +} + +#JSLINT_REPORT>div { + padding: 1em; +} + +#JSLINT_ dl.level0 { + color: black; + background-color: white; +} + +#JSLINT_ dl.level1 { + color: black; + background-color: #ffffe0; /* yellow */ + margin-left: 1em; +} + +#JSLINT_ dl.level2 { + color: black; + background-color: #e0ffe0; /* green */ + margin-left: 2em; +} + +#JSLINT_ dl.level3 { + color: black; + background-color: #e0e0ff; /* blue */ + margin-left: 3em; +} + +#JSLINT_ dl.level4 { + background-color: #ffe0ff; /* purple */ + color: black; + margin-left: 4em; +} + +#JSLINT_ dl.level5 { + background-color: #ffe0e0; /* red */ + color: black; + margin-left: 5em; +} + +#JSLINT_ dl.level6 { + background-color: #ffe390; /* orange */ + color: black; + margin-left: 6em; +} + +#JSLINT_ dl.level7 { + background-color: #e0e0e0; /* gray */ + color: black; + margin-left: 7em; +} + +#JSLINT_ dl.level8 { + color: black; + margin-left: 8em; +} + +#JSLINT_ dl.level9 { + color: black; + margin-left: 9em; +} + +.none { + display: none; +} +.center { + text-align: center; +} diff --git a/branch.warning_early_stop/jslint.js b/branch.warning_early_stop/jslint.js new file mode 100644 index 000000000..9d9dd4403 --- /dev/null +++ b/branch.warning_early_stop/jslint.js @@ -0,0 +1,4932 @@ +// jslint.js +// 2018-09-26 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// jslint(source, option_object, global_array) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze, a string or an array of strings. +// option_object An object whose keys correspond to option names. +// global_array An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all of the functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// 1. If the source is a single string, split it into an array of strings. +// 2. Turn the source into an array of tokens. +// 3. Furcate the tokens into a parse tree. +// 4. Walk the tree, traversing all of the nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// 5. Check the whitespace between the tokens. + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*property + a, and, arity, assign, b, bad_assignment_a, bad_directive_a, bad_get, + bad_module_name_a, bad_option_a, bad_property_a, bad_set, bitwise, block, + body, browser, c, calls, catch, charCodeAt, closer, closure, code, column, + complex, concat, constant, context, convert, couch, create, d, dead, + default, devel, directive, directives, disrupt, dot, duplicate_a, + early_stop, + edition, + ellipsis, else, empty_block, escape_mega, eval, every, expected_a, + expected_a_at_b_c, expected_a_b, expected_a_b_from_c_d, expected_a_before_b, + expected_a_next_at_b, expected_digits_after_a, expected_four_digits, + expected_identifier_a, expected_line_break_a_b, expected_regexp_factor_a, + expected_space_a_b, expected_statements_a, expected_string_a, + expected_type_string_a, exports, expression, extra, finally, flag, for, + forEach, free, from, froms, fud, fudge, function_in_loop, functions, g, + getset, global, i, id, identifier, import, inc, indexOf, infix_in, init, + initial, isArray, isNaN, join, json, keys, label, label_a, lbp, led, length, + level, line, lines, live, long, loop, m, margin, match, message, + misplaced_a, misplaced_directive_a, missing_browser, missing_m, module, + multivar, naked_block, name, names, nested_comment, new, node, not_label_a, + nr, nud, number_isNaN, ok, open, option, out_of_scope_a, parameters, parent, + pop, property, push, quote, redefinition_a_b, replace, + required_a_optional_b, reserved_a, right, role, search, shebang, signature, + single, slice, some, sort, split, startsWith, statement, stop, strict, + subscript_a, switch, test, this, thru, toString, todo_comment, tokens, + too_long, too_many_digits, tree, try, type, u, unclosed_comment, + unclosed_mega, unclosed_string, undeclared_a, unexpected_a, + unexpected_a_after_b, unexpected_a_before_b, unexpected_at_top_level_a, + unexpected_char_a, unexpected_comment, unexpected_directive_a, + unexpected_expression_a, unexpected_label_a, unexpected_parens, + unexpected_space_a_b, unexpected_statement_a, unexpected_trailing_space, + unexpected_typeof_a, uninitialized_a, unreachable_a, + unregistered_property_a, unsafe, unused_a, use_double, use_open, use_spaces, + use_strict, used, value, var_loop, var_switch, variable, warning, warnings, + weird_condition_a, weird_expression_a, weird_loop, weird_relation_a, white, + wrap_assignment, wrap_condition, wrap_immediate, wrap_parameter, + wrap_regexp, wrap_unary, wrapped, writable, y +*/ + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than {} because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +function populate(array, object = empty(), value = true) { + +// Augment an object by taking property names from an array of strings. + + array.forEach(function (name) { + object[name] = value; + }); + return object; +} + +const allowed_option = { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + bitwise: true, + browser: [ + "caches", "clearInterval", "clearTimeout", "document", "DOMException", + "Element", "Event", "event", "FileReader", "FormData", "history", + "localStorage", "location", "MutationObserver", "name", "navigator", + "screen", "sessionStorage", "setInterval", "setTimeout", "Storage", + "URL", "window", "Worker", "XMLHttpRequest" + ], + couch: [ + "emit", "getRow", "isArray", "log", "provides", "registerType", + "require", "send", "start", "sum", "toJSON" + ], + convert: true, + devel: [ + "alert", "confirm", "console", "prompt" + ], + eval: true, + for: true, + fudge: true, + getset: true, + long: true, + multivar: true, + node: [ + "Buffer", "clearImmediate", "clearInterval", "clearTimeout", + "console", "exports", "module", "process", "require", + "setImmediate", "setInterval", "setTimeout", "URL", + "URLSearchParams", "__dirname", "__filename" + ], + single: true, + this: true, + white: true +}; + +const anticondition = populate([ + "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%", + "typeof", "(number)", "(string)" +]); + +// These are the bitwise operators. + +const bitwiseop = populate([ + "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=", + ">>>", ">>>=" +]); + +const escapeable = populate([ + "\\", "/", "`", "b", "f", "n", "r", "t" +]); + +const opener = { + +// The open and close pairs. + + "(": ")", // paren + "[": "]", // bracket + "{": "}", // brace + "${": "}" // mega +}; + +// The relational operators. + +const relationop = populate([ + "!=", "!==", "==", "===", "<", "<=", ">", ">=" +]); + +// This is the set of infix operators that require a space on each side. + +const spaceop = populate([ + "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/", + "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=", + ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" +]); + +const standard = [ + +// These are the globals that are provided by the language standard. + + "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI", + "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error", + "EvalError", "Float32Array", "Float64Array", "Generator", + "GeneratorFunction", "Int8Array", "Int16Array", "Int32Array", "Intl", + "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat", + "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp", + "Set", "String", "Symbol", "SyntaxError", "System", "TypeError", + "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", + "URIError", "WeakMap", "WeakSet" +]; + +const bundle = { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + and: "The '&&' subexpression should be wrapped in parens.", + bad_assignment_a: "Bad assignment to '{a}'.", + bad_directive_a: "Bad directive '{a}'.", + bad_get: "A get function takes no parameters.", + bad_module_name_a: "Bad module name '{a}'.", + bad_option_a: "Bad option '{a}'.", + bad_property_a: "Bad property name '{a}'.", + bad_set: "A set function takes one parameter.", + duplicate_a: "Duplicate '{a}'.", + early_stop: "JSLint was unable to finish.", + empty_block: "Empty block.", + escape_mega: "Unexpected escapement in mega literal.", + expected_a: "Expected '{a}'.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: ( + "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'." + ), + expected_a_before_b: "Expected '{a}' before '{b}'.", + expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.", + expected_digits_after_a: "Expected digits after '{a}'.", + expected_four_digits: "Expected four digits after '\\u'.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.", + expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.", + expected_space_a_b: "Expected one space between '{a}' and '{b}'.", + expected_statements_a: "Expected statements before '{a}'.", + expected_string_a: "Expected a string and instead saw '{a}'.", + expected_type_string_a: "Expected a type string and instead saw '{a}'.", + function_in_loop: "Don't make functions within a loop.", + infix_in: ( + "Unexpected 'in'. Compare with undefined, " + + "or use the hasOwnProperty method instead." + ), + label_a: "'{a}' is a statement label.", + misplaced_a: "Place '{a}' at the outermost level.", + misplaced_directive_a: ( + "Place the '/*{a}*/' directive before the first statement." + ), + missing_browser: "/*global*/ requires the Assume a browser option.", + missing_m: "Expected 'm' flag on a multiline regular expression.", + naked_block: "Naked block.", + nested_comment: "Nested comment.", + not_label_a: "'{a}' is not a label.", + number_isNaN: "Use Number.isNaN function to compare with NaN.", + out_of_scope_a: "'{a}' is out of scope.", + redefinition_a_b: "Redefinition of '{a}' from line {b}.", + required_a_optional_b: ( + "Required parameter '{a}' after optional parameter '{b}'." + ), + reserved_a: "Reserved name '{a}'.", + subscript_a: "['{a}'] is better written in dot notation.", + todo_comment: "Unexpected TODO comment.", + too_long: "Line is longer than 80 characters.", + too_many_digits: "Too many digits.", + unclosed_comment: "Unclosed comment.", + unclosed_mega: "Unclosed mega literal.", + unclosed_string: "Unclosed string.", + undeclared_a: "Undeclared '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_a_after_b: "Unexpected '{a}' after '{b}'.", + unexpected_a_before_b: "Unexpected '{a}' before '{b}'.", + unexpected_at_top_level_a: "Expected '{a}' to be in a function.", + unexpected_char_a: "Unexpected character '{a}'.", + unexpected_comment: "Unexpected comment.", + unexpected_directive_a: "When using modules, don't use directive '/*{a}'.", + unexpected_expression_a: ( + "Unexpected expression '{a}' in statement position." + ), + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_parens: "Don't wrap function literals in parens.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unexpected_statement_a: ( + "Unexpected statement '{a}' in expression position." + ), + unexpected_trailing_space: "Unexpected trailing space.", + unexpected_typeof_a: ( + "Unexpected 'typeof'. Use '===' to compare directly with {a}." + ), + uninitialized_a: "Uninitialized '{a}'.", + unreachable_a: "Unreachable '{a}'.", + unregistered_property_a: "Unregistered property name '{a}'.", + unsafe: "Unsafe character '{a}'.", + unused_a: "Unused '{a}'.", + use_double: "Use double quotes, not single quotes.", + use_open: ( + "Wrap a ternary expression in parens, " + + "with a line break after the left paren." + ), + use_spaces: "Use spaces, not tabs.", + use_strict: "This function needs a \"use strict\" pragma.", + var_loop: "Don't declare variables in a loop.", + var_switch: "Don't declare variables in a switch.", + weird_condition_a: "Weird condition '{a}'.", + weird_expression_a: "Weird expression '{a}'.", + weird_loop: "Weird loop.", + weird_relation_a: "Weird relation '{a}'.", + wrap_assignment: "Don't wrap assignment statements in parens.", + wrap_condition: "Wrap the condition in parens.", + wrap_immediate: ( + "Wrap an immediate function invocation in parentheses to assist " + + "the reader in understanding that the expression is the result " + + "of a function, and not the function itself." + ), + wrap_parameter: "Wrap the parameter in parens.", + wrap_regexp: "Wrap this regexp in parens to avoid confusion.", + wrap_unary: "Wrap the unary expression in parens." +}; + +// Regular expression literals: + +// supplant {variables} +const rx_supplant = /\{([^{}]*)\}/g; +// carriage return, carriage return linefeed, or linefeed +const rx_crlf = /\n|\r\n?/; +// unsafe characters that are silently deleted by one or more browsers +const rx_unsafe = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; +// identifier +const rx_identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; +const rx_module = /^[a-zA-Z0-9_$:.@\-\/]+$/; +const rx_bad_property = /^_|\$|Sync\$|_$/; +// star slash +const rx_star_slash = /\*\//; +// slash star +const rx_slash_star = /\/\*/; +// slash star or ending slash +const rx_slash_star_or_slash = /\/\*|\/$/; +// uncompleted work comment +const rx_todo = /\b(?:todo|TO\s?DO|HACK)\b/; +// tab +const rx_tab = /\t/g; +// directive +const rx_directive = /^(jslint|property|global)\s+(.*)$/; +const rx_directive_part = /^([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*(.*)$/; +// token (sorry it is so long) +const rx_token = /^((\s+)|([a-zA-Z_$][a-zA-Z0-9_$]*)|[(){}\[\]?,:;'"~`]|=(?:==?|>)?|\.+|[*\/][*\/=]?|\+[=+]?|-[=\-]?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<= "a" && string <= "z\uffff") + || (string >= "A" && string <= "Z\uffff") + ); +} + +function supplant(string, object) { + return string.replace(rx_supplant, function (found, filling) { + const replacement = object[filling]; + return ( + replacement !== undefined + ? replacement + : found + ); + }); +} + +let anon; // The guessed name for anonymous functions. +let blockage; // The current block. +let block_stack; // The stack of blocks. +let declared_globals; // The object containing the global declarations. +let directives; // The directive comments. +let directive_mode; // true if directives are still allowed. +let early_stop; // true if JSLint cannot finish. +let exports; // The exported names and values. +let froms; // The array collecting all import-from strings. +let fudge; // true if the natural numbers start with 1. +let functionage; // The current function. +let functions; // The array containing all of the functions. +let global; // The global object; the outermost context. +let json_mode; // true if parsing JSON. +let lines; // The array containing source lines. +let module_mode; // true if import or export was used. +let next_token; // The next token to be examined in the parse. +let option; // The options parameter. +let property; // The object containing the tallied property names. +let mega_mode; // true if currently parsing a megastring literal. +let shebang; // true if a #! was seen on the first line. +let stack; // The stack of functions. +let syntax; // The object containing the parser. +let token; // The current token being examined in the parse. +let token_nr; // The number of the next token. +let tokens; // The array of tokens. +let tenure; // The predefined property registry. +let tree; // The abstract parse tree. +let var_mode; // "var" if using var; "let" if using let. +let warnings; // The array collecting all generated warnings. + +// Error reportage functions: + +function artifact(the_token) { + +// Return a string representing an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); +} + +function artifact_line(the_token) { + +// Return the fudged line number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.line + fudge; +} + +function artifact_column(the_token) { + +// Return the fudged column number of an artifact. + + if (the_token === undefined) { + the_token = next_token; + } + return the_token.from + fudge; +} + +function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + const warning = { // ~~ + name: "JSLintError", + column: column, + line: line, + code: code + }; + if (a !== undefined) { + warning.a = a; + } + if (b !== undefined) { + warning.b = b; + } + if (c !== undefined) { + warning.c = c; + } + if (d !== undefined) { + warning.d = d; + } + warning.message = supplant(bundle[code] || code, warning); + warnings.push(warning); + return warning; +} + +function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); +} + +function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + if (the_token.warning === undefined) { + the_token.warning = warn_at( + code, + the_token.line, + the_token.from, + a || artifact(the_token), + b, + c, + d + ); + return the_token.warning; + } +} + +function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + if (the_token === undefined) { + the_token = next_token; + } + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); +} + +// Tokenize: + +function tokenize(source) { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + +// If the source is not an array, then it is split into lines at the +// carriage return/linefeed. + + lines = ( + Array.isArray(source) + ? source + : source.split(rx_crlf) + ); + tokens = []; + + let char; // a popular character + let column = 0; // the column number of the next character + let first; // the first token + let from; // the starting column number of the token + let line = -1; // the line number of the next character + let nr = 0; // the next token number + let previous = global; // the previous token including comments + let prior = global; // the previous token excluding comments + let mega_from; // the starting column of megastring + let mega_line; // the starting line of megastring + let regexp_seen; // regular expression literal seen on this line + let snippet; // a piece of string + let source_line = ""; // the remaining line source string + let whole_line = ""; // the whole line source string + + if (lines[0].startsWith("#!")) { + line = 0; + shebang = true; + } + + function next_line() { + +// Put the next line of source in source_line. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + let at; + if ( + !option.long + && whole_line.length > 80 + && !json_mode + && first + && !regexp_seen + ) { + warn_at("too_long", line, 80); + } + column = 0; + line += 1; + regexp_seen = false; + whole_line = lines[line]; + source_line = whole_line; + if (source_line !== undefined) { + at = source_line.search(rx_tab); + if (at >= 0) { + if (!option.white) { + warn_at("use_spaces", line, at + 1); + } + source_line = source_line.replace(rx_tab, " "); + } + at = source_line.search(rx_unsafe); + if (at >= 0) { + warn_at( + "unsafe", + line, + column + at, + "U+" + source_line.charCodeAt(at).toString(16) + ); + } + if (!option.white && source_line.slice(-1) === " ") { + warn_at( + "unexpected_trailing_space", + line, + source_line.length - 1 + ); + } + } + return source_line; + } + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions snip, next_char, prev_char, +// some_digits, and escape help in the parsing of literals. + + function snip() { + +// Remove the last character from snippet. + + snippet = snippet.slice(0, -1); + } + + function next_char(match) { + +// Get the next character from the source line. Remove it from the source_line, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + return stop_at( + ( + char === "" + ? "expected_a" + : "expected_a_b" + ), + line, + column - 1, + match, + char + ); + } + if (source_line) { + char = source_line[0]; + source_line = source_line.slice(1); + snippet += char; + } else { + char = ""; + snippet += " "; + } + column += 1; + return char; + } + + function back_char() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the source_line. + + if (snippet) { + char = snippet.slice(-1); + source_line = char + source_line; + column -= 1; + snip(); + } else { + char = ""; + } + return char; + } + + function some_digits(rx, quiet) { + const result = source_line.match(rx); + if (result) { + char = result[1]; + column += char.length; + source_line = result[2]; + snippet += char; + } else { + char = ""; + if (!quiet) { + warn_at( + "expected_digits_after_a", + line, + column, + snippet + ); + } + } + return char.length; + } + + function escape(extra) { + next_char("\\"); + if (escapeable[char] === true) { + return next_char(); + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "u") { + if (next_char("u") === "{") { + if (json_mode) { + warn_at("unexpected_a", line, column - 1, char); + } + if (some_digits(rx_hexs) > 5) { + warn_at("too_many_digits", line, column - 1); + } + if (next_char() !== "}") { + stop_at("expected_a_before_b", line, column, "}", char); + } + return next_char(); + } + back_char(); + if (some_digits(rx_hexs, true) < 4) { + warn_at("expected_four_digits", line, column - 1); + } + return; + } + if (extra && extra.indexOf(char) >= 0) { + return next_char(); + } + warn_at("unexpected_a_before_b", line, column - 2, "\\", char); + } + + function make(id, value, identifier) { + +// Make the token object and append it to the tokens list. + + const the_token = { + from: from, + id: id, + identifier: Boolean(identifier), + line: line, + nr: nr, + thru: column + }; + tokens[nr] = the_token; + nr += 1; + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + directive_mode = false; + } + +// If the token is to have a value, give it one. + + if (value !== undefined) { + the_token.value = value; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option.white. + + if ( + previous.line === line + && previous.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (previous.id === "(comment)" || previous.id === "(regexp)") + ) { + warn( + "expected_space_a_b", + the_token, + artifact(previous), + artifact(the_token) + ); + } + if (previous.id === "." && id === "(number)") { + warn("expected_a_before_b", previous, "0", "."); + } + if (prior.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// The previous token is used to detect adjacency problems. + + previous = the_token; + +// The prior token is a previous token that was not a comment. The prior token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (previous.id !== "(comment)") { + prior = previous; + } + return the_token; + } + + function parse_directive(the_comment, body) { + +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + + const result = body.match(rx_directive_part); + if (result) { + let allowed; + const name = result[1]; + const value = result[2]; + if (the_comment.directive === "jslint") { + allowed = allowed_option[name]; + if ( + typeof allowed === "boolean" + || typeof allowed === "object" + ) { + if ( + value === "" + || value === "true" + || value === undefined + ) { + option[name] = true; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } else if (value === "false") { + option[name] = false; + } else { + warn("bad_option_a", the_comment, name + ":" + value); + } + } else { + warn("bad_option_a", the_comment, name); + } + } else if (the_comment.directive === "property") { + if (tenure === undefined) { + tenure = empty(); + } + tenure[name] = true; + } else if (the_comment.directive === "global") { + if (value) { + warn("bad_option_a", the_comment, name + ":" + value); + } + declared_globals[name] = false; + module_mode = the_comment; + } + return parse_directive(the_comment, result[3]); + } + if (body) { + return stop("bad_directive_a", the_comment, body); + } + } + + function comment(snippet) { + +// Make a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + + const the_comment = make("(comment)", snippet); + if (Array.isArray(snippet)) { + snippet = snippet.join(" "); + } + if (!option.devel && rx_todo.test(snippet)) { + warn("todo_comment", the_comment); + } + const result = snippet.match(rx_directive); + if (result) { + if (!directive_mode) { + warn_at("misplaced_directive_a", line, from, result[1]); + } else { + the_comment.directive = result[1]; + parse_directive(the_comment, result[2]); + } + directives.push(the_comment); + } + return the_comment; + } + + function regexp() { + +// Parse a regular expression literal. + + let multi_mode = false; + let result; + let value; + regexp_seen = true; + + function quantifier() { + +// Match an optional quantifier. + + if (char === "?" || char === "*" || char === "+") { + next_char(); + } else if (char === "{") { + if (some_digits(rx_digits, true) === 0) { + warn_at("expected_a", line, column, "0"); + } + if (next_char() === ",") { + some_digits(rx_digits, true); + next_char(); + } + next_char("}"); + } else { + return; + } + if (char === "?") { + next_char("?"); + } + } + + function subklass() { + +// Match a character in a character class. + + if (char === "\\") { + escape("BbDdSsWw-[]^"); + return true; + } + if ( + char === "" + || char === "[" + || char === "]" + || char === "/" + || char === "^" + || char === "-" + ) { + return false; + } + if (char === " ") { + warn_at("expected_a_b", line, column, "\\u0020", " "); + } else if (char === "`" && mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char(); + return true; + } + + function ranges() { + +// Match a range of subclasses. + + if (subklass()) { + if (char === "-") { + next_char("-"); + if (!subklass()) { + return stop_at( + "unexpected_a", + line, + column - 1, + "-" + ); + } + } + return ranges(); + } + } + + function klass() { + +// Match a class. + + next_char("["); + if (char === "^") { + next_char("^"); + } + (function classy() { + ranges(); + if (char !== "]" && char !== "") { + warn_at( + "expected_a_before_b", + line, + column, + "\\", + char + ); + next_char(); + return classy(); + } + }()); + next_char("]"); + } + + function choice() { + + function group() { + +// Match a group that starts with left paren. + + next_char("("); + if (char === "?") { + next_char("?"); + if (char === "=" || char === "!") { + next_char(); + } else { + next_char(":"); + } + } else if (char === ":") { + warn_at("expected_a_before_b", line, column, "?", ":"); + } + choice(); + next_char(")"); + } + + function factor() { + if ( + char === "" + || char === "/" + || char === "]" + || char === ")" + ) { + return false; + } + if (char === "(") { + group(); + return true; + } + if (char === "[") { + klass(); + return true; + } + if (char === "\\") { + escape("BbDdSsWw^${}[]():=!.-|*+?"); + return true; + } + if ( + char === "?" + || char === "+" + || char === "*" + || char === "}" + || char === "{" + ) { + warn_at( + "expected_a_before_b", + line, + column - 1, + "\\", + char + ); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column - 1, "`"); + } + } else if (char === " ") { + warn_at( + "expected_a_b", + line, + column - 1, + "\\s", + " " + ); + } else if (char === "$") { + if (source_line[0] !== "/") { + multi_mode = true; + } + } else if (char === "^") { + if (snippet !== "^") { + multi_mode = true; + } + } + next_char(); + return true; + } + + function sequence(follow) { + if (factor()) { + quantifier(); + return sequence(true); + } + if (!follow) { + warn_at("expected_regexp_factor_a", line, column, char); + } + } + +// Match a choice (a sequence that can be followed by | and another choice). + + sequence(); + if (char === "|") { + next_char("|"); + return choice(); + } + } + +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + next_char(); + if (char === "=") { + warn_at("expected_a_before_b", line, column, "\\", "="); + } + choice(); + +// Make sure there is a closing slash. + + snip(); + value = snippet; + next_char("/"); + +// Process dangling flag letters. + + const allowed = { + g: true, + i: true, + m: true, + u: true, + y: true + }; + const flag = empty(); + (function make_flag() { + if (is_letter(char)) { + if (allowed[char] !== true) { + warn_at("unexpected_a", line, column, char); + } + allowed[char] = false; + flag[char] = true; + next_char(); + return make_flag(); + } + }()); + back_char(); + if (char === "/" || char === "*") { + return stop_at("unexpected_a", line, from, char); + } + result = make("(regexp)", char); + result.flag = flag; + result.value = value; + if (multi_mode && !flag.m) { + warn_at("missing_m", line, column); + } + return result; + } + + function string(quote) { + +// Make a string token. + + let the_token; + snippet = ""; + next_char(); + + return (function next() { + if (char === quote) { + snip(); + the_token = make("(string)", snippet); + the_token.quote = quote; + return the_token; + } + if (char === "") { + return stop_at("unclosed_string", line, column); + } + if (char === "\\") { + escape(quote); + } else if (char === "`") { + if (mega_mode) { + warn_at("unexpected_a", line, column, "`"); + } + next_char("`"); + } else { + next_char(); + } + return next(); + }()); + } + + function frack() { + if (char === ".") { + some_digits(rx_digits); + next_char(); + } + if (char === "E" || char === "e") { + next_char(); + if (char !== "+" && char !== "-") { + back_char(); + } + some_digits(rx_digits); + next_char(); + } + } + + function number() { + if (snippet === "0") { + next_char(); + if (char === ".") { + frack(); + } else if (char === "b") { + some_digits(rx_bits); + next_char(); + } else if (char === "o") { + some_digits(rx_octals); + next_char(); + } else if (char === "x") { + some_digits(rx_hexs); + next_char(); + } + } else { + next_char(); + frack(); + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + return stop_at( + "unexpected_a_after_b", + line, + column - 1, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + back_char(); + return make("(number)", snippet); + } + + function lex() { + let array; + let i = 0; + let j = 0; + let last; + let result; + let the_token; + if (!source_line) { + source_line = next_line(); + from = 0; + return ( + source_line === undefined + ? ( + mega_mode + ? stop_at("unclosed_mega", mega_line, mega_from) + : make("(end)") + ) + : lex() + ); + } + from = column; + result = source_line.match(rx_token); + +// result[1] token +// result[2] whitespace +// result[3] identifier +// result[4] number +// result[5] rest + + if (!result) { + return stop_at( + "unexpected_char_a", + line, + column, + source_line[0] + ); + } + + snippet = result[1]; + column += snippet.length; + source_line = result[5]; + +// Whitespace was matched. Call lex again to get more. + + if (result[2]) { + return lex(); + } + +// The token is an identifier. + + if (result[3]) { + return make(snippet, undefined, true); + } + +// The token is a number. + + if (result[4]) { + return number(snippet); + } + +// The token is a string. + + if (snippet === "\"") { + return string(snippet); + } + if (snippet === "'") { + if (!option.single) { + warn_at("use_double", line, column); + } + return string(snippet); + } + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (snippet === "`") { + if (mega_mode) { + return stop_at("expected_a_b", line, column, "}", "`"); + } + snippet = ""; + mega_from = from; + mega_line = line; + mega_mode = true; + +// Parsing a mega literal is tricky. First make a ` token. + + make("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + (function part() { + const at = source_line.search(rx_mega); + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + if (at < 0) { + snippet += source_line + "\n"; + return ( + next_line() === undefined + ? stop_at("unclosed_mega", mega_line, mega_from) + : part() + ); + } + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + snippet += source_line.slice(0, at); + column += at; + source_line = source_line.slice(at); + if (source_line[0] === "\\") { + stop_at("escape_mega", line, at); + } + make("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then make tokens that will become part of an expression until +// a } token is made. + + if (source_line[0] === "$") { + column += 2; + make("${"); + source_line = source_line.slice(2); + (function expr() { + const id = lex().id; + if (id === "{") { + return stop_at( + "expected_a_b", + line, + column, + "}", + "{" + ); + } + if (id !== "}") { + return expr(); + } + }()); + return part(); + } + }()); + source_line = source_line.slice(1); + column += 1; + mega_mode = false; + return make("`"); + } + +// The token is a // comment. + + if (snippet === "//") { + snippet = source_line; + source_line = ""; + the_token = comment(snippet); + if (mega_mode) { + warn("unexpected_comment", the_token, "`"); + } + return the_token; + } + +// The token is a /* comment. + + if (snippet === "/*") { + array = []; + if (source_line[0] === "/") { + warn_at("unexpected_a", line, column + i, "/"); + } + (function next() { + if (source_line > "") { + i = source_line.search(rx_star_slash); + if (i >= 0) { + return; + } + j = source_line.search(rx_slash_star); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + } + array.push(source_line); + source_line = next_line(); + if (source_line === undefined) { + return stop_at("unclosed_comment", line, column); + } + return next(); + }()); + snippet = source_line.slice(0, i); + j = snippet.search(rx_slash_star_or_slash); + if (j >= 0) { + warn_at("nested_comment", line, column + j); + } + array.push(snippet); + column += i + 2; + source_line = source_line.slice(i + 2); + return comment(array); + } + +// The token is a slash. + + if (snippet === "/") { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + if (prior.identifier) { + if (!prior.dot) { + if (prior.id === "return") { + return regexp(); + } + if ( + prior.id === "(begin)" + || prior.id === "case" + || prior.id === "delete" + || prior.id === "in" + || prior.id === "instanceof" + || prior.id === "new" + || prior.id === "typeof" + || prior.id === "void" + || prior.id === "yield" + ) { + the_token = regexp(); + return stop("unexpected_a", the_token); + } + } + } else { + last = prior.id[prior.id.length - 1]; + if ("(,=:?[".indexOf(last) >= 0) { + return regexp(); + } + if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) { + the_token = regexp(); + warn("wrap_regexp", the_token); + return the_token; + } + } + if (source_line[0] === "/") { + column += 1; + source_line = source_line.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + } + return make(snippet); + } + + first = lex(); + json_mode = first.id === "{" || first.id === "["; + +// This is the only loop in JSLint. It will turn into a recursive call to lex +// when ES6 has been finished and widely deployed and adopted. + + while (true) { + if (lex().id === "(end)") { + break; + } + } +} + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + +function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!rx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!rx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property[id] === "number") { + property[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (tenure !== undefined) { + if (tenure[id] !== true) { + warn("unregistered_property_a", name); + } + } else { + if (name.identifier && rx_bad_property.test(id)) { + warn("bad_property_a", name); + } + } + property[id] = 1; + } + return id; +} + +function dispense() { + +// Deliver the next token, skipping the comments. + + const cadet = tokens[token_nr]; + token_nr += 1; + if (cadet.id === "(comment)") { + if (json_mode) { + warn("unexpected_a", cadet); + } + return dispense(); + } else { + return cadet; + } +} + +function lookahead() { + +// Look ahead one token without advancing. + + const old_token_nr = token_nr; + const cadet = dispense(true); + token_nr = old_token_nr; + return cadet; +} + +function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if (token.identifier && token.id !== "function") { + anon = token.id; + } else if (token.id === "(string)" && rx_identifier.test(token.value)) { + anon = token.value; + } + +// Attempt to match next_token with an expected id. + + if (id !== undefined && next_token.id !== id) { + return ( + match === undefined + ? stop("expected_a_b", next_token, id, artifact()) + : stop( + "expected_a_b_from_c_d", + next_token, + id, + artifact(match), + artifact_line(match), + artifact(next_token) + ) + ); + } + +// Promote the tokens, skipping comments. + + token = next_token; + next_token = dispense(); + if (next_token.id === "(end)") { + token_nr -= 1; + } +} + +// Parsing of JSON is simple: + +function json_value() { + let negative; + if (next_token.id === "{") { + return (function json_object() { + const brace = next_token; + const object = empty(); + const properties = []; + brace.expression = properties; + advance("{"); + if (next_token.id !== "}") { + (function next() { + let name; + let value; + if (next_token.quote !== "\"") { + warn( + "unexpected_a", + next_token, + next_token.quote + ); + } + name = next_token; + advance("(string)"); + if (object[token.value] !== undefined) { + warn("duplicate_a", token); + } else if (token.value === "__proto__") { + warn("bad_property_a", token); + } else { + object[token.value] = token; + } + advance(":"); + value = json_value(); + value.label = name; + properties.push(value); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("}", brace); + return brace; + }()); + } + if (next_token.id === "[") { + return (function json_array() { + const bracket = next_token; + const elements = []; + bracket.expression = elements; + advance("["); + if (next_token.id !== "]") { + (function next() { + elements.push(json_value()); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]", bracket); + return bracket; + }()); + } + if ( + next_token.id === "true" + || next_token.id === "false" + || next_token.id === "null" + ) { + advance(); + return token; + } + if (next_token.id === "(number)") { + if (!rx_JSON_number.test(next_token.value)) { + warn("unexpected_a"); + } + advance(); + return token; + } + if (next_token.id === "(string)") { + if (next_token.quote !== "\"") { + warn("unexpected_a", next_token, next_token.quote); + } + advance(); + return token; + } + if (next_token.id === "-") { + negative = next_token; + negative.arity = "unary"; + advance("-"); + advance("(number)"); + negative.expression = token; + return negative; + } + stop("unexpected_a"); +} + +// Now we parse JavaScript. + +function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + const id = name.id; + +// Reserved words may not be enrolled. + + if (syntax[id] !== undefined && id !== "ignore") { + warn("reserved_a", name); + } else { + +// Has the name been enrolled in this context? + + let earlier = functionage.context[id]; + if (earlier) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + +// Has the name been enrolled in an outer context? + + } else { + stack.forEach(function (value) { + const item = value.context[id]; + if (item !== undefined) { + earlier = item; + } + }); + if (earlier) { + if (id === "ignore") { + if (earlier.role === "variable") { + warn("unexpected_a", name); + } + } else { + if ( + ( + role !== "exception" + || earlier.role !== "exception" + ) + && role !== "parameter" + && role !== "function" + ) { + warn( + "redefinition_a_b", + name, + name.id, + earlier.line + fudge + ); + } + } + } + +// Enroll it. + + functionage.context[id] = name; + name.dead = true; + name.parent = functionage; + name.init = false; + name.role = role; + name.used = 0; + name.writable = !readonly; + } + } +} + +function expression(rbp, initial) { + +// This is the heart of the Pratt parser. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// nud Null denotation +// led Left denotation +// lbp Left binding power +// rbp Right binding power + +// It processes a nud (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop. It +// returns the expression's parse tree. + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement, + + if (!initial) { + advance(); + } + the_symbol = syntax[token.id]; + if (the_symbol !== undefined && the_symbol.nud !== undefined) { + left = the_symbol.nud(); + } else if (token.identifier) { + left = token; + left.arity = "variable"; + } else { + return stop("unexpected_a", token); + } + (function right() { + the_symbol = syntax[next_token.id]; + if ( + the_symbol !== undefined + && the_symbol.led !== undefined + && rbp < the_symbol.lbp + ) { + advance(); + left = the_symbol.led(left); + return right(); + } + }()); + return left; +} + +function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = next_token; + let the_value; + the_paren.free = true; + advance("("); + the_value = expression(0); + advance(")"); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (anticondition[the_value.id] === true) { + warn("unexpected_a", the_value); + } + return the_value; +} + +function is_weird(thing) { + return ( + thing.id === "(regexp)" + || thing.id === "{" + || thing.id === "=>" + || thing.id === "function" + || (thing.id === "[" && thing.arity === "unary") + ); +} + +function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + return ( + Array.isArray(b) + && a.length === b.length + && a.every(function (value, index) { + return are_similar(value, b[index]); + }) + ); + } + if (Array.isArray(b)) { + return false; + } + if (a.id === "(number)" && b.id === "(number)") { + return a.value === b.value; + } + let a_string; + let b_string; + if (a.id === "(string)") { + a_string = a.value; + } else if (a.id === "`" && a.constant) { + a_string = a.value[0]; + } + if (b.id === "(string)") { + b_string = b.value; + } else if (b.id === "`" && b.constant) { + b_string = b.value[0]; + } + if (typeof a_string === "string") { + return a_string === b_string; + } + if (is_weird(a) || is_weird(b)) { + return false; + } + if (a.arity === b.arity && a.id === b.id) { + if (a.id === ".") { + return ( + are_similar(a.expression, b.expression) + && are_similar(a.name, b.name) + ); + } + if (a.arity === "unary") { + return are_similar(a.expression, b.expression); + } + if (a.arity === "binary") { + return ( + a.id !== "(" + && are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + ); + } + if (a.arity === "ternary") { + return ( + are_similar(a.expression[0], b.expression[0]) + && are_similar(a.expression[1], b.expression[1]) + && are_similar(a.expression[2], b.expression[2]) + ); + } + if (a.arity === "function" && a.arity === "regexp") { + return false; + } + return true; + } + return false; +} + +function semicolon() { + +// Try to match a semicolon. + + if (next_token.id === ";") { + advance(";"); + } else { + warn_at( + "expected_a_b", + token.line, + token.thru, + ";", + artifact(next_token) + ); + } + anon = "anonymous"; +} + +function statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token.identifier && next_token.id === ":") { + the_label = token; + if (the_label.id === "ignore") { + warn("unexpected_a", the_label); + } + advance(":"); + if ( + next_token.id === "do" + || next_token.id === "for" + || next_token.id === "switch" + || next_token.id === "while" + ) { + enroll(the_label, "label", true); + the_label.init = true; + the_label.dead = false; + the_statement = statement(); + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token; + first.statement = true; + the_symbol = syntax[first.id]; + if (the_symbol !== undefined && the_symbol.fud !== undefined) { + the_symbol.disrupt = false; + the_symbol.statement = true; + the_statement = the_symbol.fud(); + } else { + +// It is an expression statement. + + the_statement = expression(0, true); + if (the_statement.wrapped && the_statement.id !== "(") { + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; +} + +function statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const array = []; + (function next(disrupt) { + if ( + next_token.id !== "}" + && next_token.id !== "case" + && next_token.id !== "default" + && next_token.id !== "else" + && next_token.id !== "(end)" + ) { + let a_statement = statement(); + array.push(a_statement); + if (disrupt) { + warn("unreachable_a", a_statement); + } + return next(a_statement.disrupt); + } + }(false)); + return array; +} + +function not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === global) { + warn("unexpected_at_top_level_a", thing); + } +} + +function top_level_only(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== global) { + warn("misplaced_a", the_thing); + } +} + +function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token; + the_block.arity = "statement"; + the_block.body = special === "body"; + +// All top level function bodies should include the "use strict" pragma unless +// the whole file is strict or the file is a module or the function parameters +// use es6 syntax. + + if ( + special === "body" + && stack.length === 1 + && next_token.value === "use strict" + ) { + the_block.strict = next_token; + next_token.statement = true; + advance("(string)"); + advance(";"); + } + stmts = statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option.devel && special !== "ignore") { + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; +} + +function mutation_check(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + warn("bad_assignment_a", the_thing); + return false; + } + return true; +} + +function left_check(left, right) { + +// Warn if the left is not one of these: +// e.b +// e[b] +// e() +// ?: +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !left_check(left.expression[1]) + && !left_check(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right); + return false; + } + return true; +} + +// These functions are used to specify the grammar of our language: + +function symbol(id, bp) { + +// Make a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax[id] = the_symbol; + } + return the_symbol; +} + +function assignment(id) { + +// Make an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led = function (left) { + const the_token = token; + let right; + the_token.arity = "assignment"; + right = expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "pre" + || right.arity === "post" + ) { + warn("unexpected_a", right); + } + mutation_check(left); + return the_token; + }; + return the_symbol; +} + +function constant(id, type, value) { + +// Make a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud = ( + typeof value === "function" + ? value + : function () { + token.constant = true; + if (value !== undefined) { + token.value = value; + } + return token; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; +} + +function infix(id, bp, f) { + +// Make an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, expression(bp)]; + return the_token; + }; + return the_symbol; +} + +function infixr(id, bp) { + +// Make a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led = function (left) { + const the_token = token; + the_token.arity = "binary"; + the_token.expression = [left, expression(bp - 1)]; + return the_token; + }; + return the_symbol; +} + +function post(id) { + +// Make one of the post operators. + + const the_symbol = symbol(id, 150); + the_symbol.led = function (left) { + token.expression = left; + token.arity = "post"; + mutation_check(token.expression); + return token; + }; + return the_symbol; +} + +function pre(id) { + +// Make one of the pre operators. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "pre"; + the_token.expression = expression(150); + mutation_check(the_token.expression); + return the_token; + }; + return the_symbol; +} + +function prefix(id, f) { + +// Make a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud = function () { + const the_token = token; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = expression(150); + return the_token; + }; + return the_symbol; +} + +function stmt(id, f) { + +// Make a statement. + + const the_symbol = symbol(id); + the_symbol.fud = function () { + token.arity = "statement"; + return f(); + }; + return the_symbol; +} + +function ternary(id1, id2) { + +// Make a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led = function (left) { + const the_token = token; + const second = expression(20); + advance(id2); + token.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, expression(10)]; + return the_token; + }; + return the_symbol; +} + +// Begin defining the language. + +syntax = empty(); + +symbol("}"); +symbol(")"); +symbol("]"); +symbol(","); +symbol(";"); +symbol(":"); +symbol("*/"); +symbol("await"); +symbol("case"); +symbol("catch"); +symbol("class"); +symbol("default"); +symbol("else"); +symbol("enum"); +symbol("finally"); +symbol("implements"); +symbol("interface"); +symbol("package"); +symbol("private"); +symbol("protected"); +symbol("public"); +symbol("static"); +symbol("super"); +symbol("void"); +symbol("yield"); + +constant("(number)", "number"); +constant("(regexp)", "regexp"); +constant("(string)", "string"); +constant("arguments", "object", function () { + warn("unexpected_a", token); + return token; +}); +constant("eval", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("false", "boolean", false); +constant("Function", "function", function () { + if (!option.eval) { + warn("unexpected_a", token); + } else if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "(", artifact()); + } + return token; +}); +constant("ignore", "undefined", function () { + warn("unexpected_a", token); + return token; +}); +constant("Infinity", "number", Infinity); +constant("isFinite", "function", function () { + warn("expected_a_b", token, "Number.isFinite", "isFinite"); + return token; +}); +constant("isNaN", "function", function () { + warn("number_isNaN", token); + return token; +}); +constant("NaN", "number", NaN); +constant("null", "null", null); +constant("this", "object", function () { + if (!option.this) { + warn("unexpected_a", token); + } + return token; +}); +constant("true", "boolean", true); +constant("undefined", "undefined"); + +assignment("="); +assignment("+="); +assignment("-="); +assignment("*="); +assignment("/="); +assignment("%="); +assignment("&="); +assignment("|="); +assignment("^="); +assignment("<<="); +assignment(">>="); +assignment(">>>="); + +infix("||", 40); +infix("&&", 50); +infix("|", 70); +infix("^", 80); +infix("&", 90); +infix("==", 100); +infix("===", 100); +infix("!=", 100); +infix("!==", 100); +infix("<", 110); +infix(">", 110); +infix("<=", 110); +infix(">=", 110); +infix("in", 110); +infix("instanceof", 110); +infix("<<", 120); +infix(">>", 120); +infix(">>>", 120); +infix("+", 130); +infix("-", 130); +infix("*", 140); +infix("/", 140); +infix("%", 140); +infixr("**", 150); +infix("(", 160, function (left) { + const the_paren = token; + let the_argument; + if (left.id !== "function") { + left_check(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (next_token.id !== ")") { + (function next() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + the_paren.free = true; + if (the_argument.wrapped === true) { + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + the_paren.free = false; + } + return the_paren; +}); +infix(".", 170, function (left) { + const the_token = token; + const name = next_token; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + left_check(left, the_token); + } + if (!name.identifier) { + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; +}); +infix("[", 170, function (left) { + const the_token = token; + const the_subscript = expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + const name = survey(the_subscript); + if (rx_identifier.test(name)) { + warn("subscript_a", the_subscript, name); + } + } + left_check(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; +}); +infix("=>", 170, function (left) { + return stop("wrap_parameter", left); +}); + +function do_tick() { + const the_tick = token; + the_tick.value = []; + the_tick.expression = []; + if (next_token.id !== "`") { + (function part() { + advance("(string)"); + the_tick.value.push(token); + if (next_token.id === "${") { + advance("${"); + the_tick.expression.push(expression(0)); + advance("}"); + return part(); + } + }()); + } + advance("`"); + return the_tick; +} + +infix("`", 160, function (left) { + const the_tick = do_tick(); + left_check(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; +}); + +post("++"); +post("--"); +pre("++"); +pre("--"); + +prefix("+"); +prefix("-"); +prefix("~"); +prefix("!"); +prefix("!!"); +prefix("[", function () { + const the_token = token; + the_token.expression = []; + if (next_token.id !== "]") { + (function next() { + let element; + let ellipsis = false; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + element = expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (next_token.id === ",") { + advance(","); + return next(); + } + }()); + } + advance("]"); + return the_token; +}); +prefix("/=", function () { + stop("expected_a_b", token, "/\\=", "/="); +}); +prefix("=>", function () { + return stop("expected_a_before_b", token, "()", "=>"); +}); +prefix("new", function () { + const the_new = token; + const right = expression(160); + if (next_token.id !== "(") { + warn("expected_a_before_b", next_token, "()", artifact(next_token)); + } + the_new.expression = right; + return the_new; +}); +prefix("typeof"); +prefix("void", function () { + const the_void = token; + warn("unexpected_a", the_void); + the_void.expression = expression(0); + return the_void; +}); + +function parameter_list() { + let complex = false; + const list = []; + let optional; + const signature = ["("]; + if (next_token.id !== ")" && next_token.id !== "(end)") { + (function parameter() { + let ellipsis = false; + let param; + if (next_token.id === "{") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("{"); + signature.push("{"); + (function subparameter() { + let subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (next_token.id === ":") { + advance(":"); + advance(); + token.label = subparam; + subparam = token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + } + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return subparameter(); + } + }()); + list.push(param); + advance("}"); + signature.push("}"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else if (next_token.id === "[") { + complex = true; + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + param = next_token; + param.names = []; + advance("["); + signature.push("[]"); + (function subparameter() { + const subparam = next_token; + if (!subparam.identifier) { + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + if (next_token.id === ",") { + advance(","); + return subparameter(); + } + }()); + list.push(param); + advance("]"); + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } else { + if (next_token.id === "...") { + complex = true; + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + warn( + "required_a_optional_b", + next_token, + next_token.id, + optional.id + ); + } + } + if (!next_token.identifier) { + return stop("expected_identifier_a"); + } + param = next_token; + list.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (next_token.id === "=") { + complex = true; + optional = param; + advance("="); + param.expression = expression(0); + } else { + if (optional !== undefined) { + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (next_token.id === ",") { + advance(","); + signature.push(", "); + return parameter(); + } + } + } + }()); + } + advance(")"); + signature.push(")"); + return [list, signature.join(""), complex]; +} + +function do_function(the_function) { + let name; + if (the_function === undefined) { + the_function = token; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + name = next_token; + enroll(name, "variable", true); + the_function.name = name; + name.init = true; + name.calls = empty(); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + if (next_token.identifier) { + name = next_token; + the_function.name = name; + advance(); + } else { + the_function.name = anon; + } + } + } else { + name = the_function.name; + } + the_function.level = functionage.level + 1; + if (mega_mode) { + warn("unexpected_a", the_function); + } + +// Don't make functions in loops. It is inefficient, and it can lead to scoping +// errors. + + if (functionage.loop > 0) { + warn("function_in_loop", the_function); + } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + the_function.context = empty(); + the_function.finally = 0; + the_function.loop = 0; + the_function.switch = 0; + the_function.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functions.push(the_function); + functionage = the_function; + if (the_function.arity !== "statement" && typeof name === "object") { + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// Parse the parameter list. + + advance("("); + token.free = false; + token.arity = "function"; + const pl = parameter_list(); + functionage.parameters = pl[0]; + functionage.signature = pl[1]; + functionage.complex = pl[2]; + functionage.parameters.forEach(function enroll_parameter(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + name.names.forEach(enroll_parameter); + } + }); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && next_token.line === token.line + ) { + return stop("unexpected_a", next_token); + } + if (next_token.id === "." || next_token.id === "[") { + warn("unexpected_a"); + } + +// Restore the previous context. + + functionage = stack.pop(); + return the_function; +} + +prefix("function", do_function); + +function fart(pl) { + if (next_token.id === ";") { + stop("wrap_assignment", token); + } + advance("=>"); + const the_fart = token; + the_fart.arity = "binary"; + the_fart.name = "=>"; + the_fart.level = functionage.level + 1; + functions.push(the_fart); + if (functionage.loop > 0) { + warn("function_in_loop", the_fart); + } + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + the_fart.context = empty(); + the_fart.finally = 0; + the_fart.loop = 0; + the_fart.switch = 0; + the_fart.try = 0; + +// Push the current function context and establish a new one. + + stack.push(functionage); + functionage = the_fart; + the_fart.parameters = pl[0]; + the_fart.signature = pl[1]; + the_fart.complex = true; + the_fart.parameters.forEach(function (name) { + enroll(name, "parameter", true); + }); + if (next_token.id === "{") { + warn("expected_a_b", the_fart, "function", "=>"); + the_fart.block = block("body"); + } else { + the_fart.expression = expression(0); + } + functionage = stack.pop(); + return the_fart; +} + +prefix("(", function () { + const the_paren = token; + let the_value; + const cadet = lookahead().id; + +// We can distinguish between a parameter list for => and a wrapped expression +// with one token of lookahead. + + if ( + next_token.id === ")" + || next_token.id === "..." + || (next_token.identifier && (cadet === "," || cadet === "=")) + ) { + the_paren.free = false; + return fart(parameter_list()); + } + the_paren.free = true; + the_value = expression(0); + if (the_value.wrapped === true) { + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + if (next_token.id === "=>") { + if (the_value.arity !== "variable") { + if (the_value.id === "{" || the_value.id === "[") { + warn("expected_a_before_b", the_paren, "function", "("); + return stop("expected_a_b", next_token, "{", "=>"); + } + return stop("expected_identifier_a", the_value); + } + the_paren.expression = [the_value]; + return fart([the_paren.expression, "(" + the_value.id + ")"]); + } + return the_value; +}); +prefix("`", do_tick); +prefix("{", function () { + const the_brace = token; + const seen = empty(); + the_brace.expression = []; + if (next_token.id !== "}") { + (function member() { + let extra; + let full; + let id; + let name = next_token; + let value; + advance(); + if ( + (name.id === "get" || name.id === "set") + && next_token.identifier + ) { + if (!option.getset) { + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + next_token.id; + name = next_token; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (next_token.id === "}" || next_token.id === ",") { + if (typeof extra === "string") { + advance("("); + } + value = expression(Infinity, true); + } else if (next_token.id === "(") { + value = do_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + advance("("); + } + advance(":"); + value = expression(0); + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + advance(":"); + value = expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (next_token.id === ",") { + advance(","); + return member(); + } + }()); + } + advance("}"); + return the_brace; +}); + +stmt(";", function () { + warn("unexpected_a", token); + return token; +}); +stmt("{", function () { + warn("naked_block", token); + return block("naked"); +}); +stmt("break", function () { + const the_break = token; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (next_token.identifier && token.line === next_token.line) { + the_label = functionage.context[next_token.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + warn( + (the_label !== undefined && the_label.dead) + ? "out_of_scope_a" + : "not_label_a" + ); + } else { + the_label.used += 1; + } + the_break.label = next_token; + advance(); + } + advance(";"); + return the_break; +}); + +function do_var() { + const the_statement = token; + const is_const = the_statement.id === "const"; + the_statement.names = []; + +// A program may use var or let, but not both. + + if (!is_const) { + if (var_mode === undefined) { + var_mode = the_statement.id; + } else if (the_statement.id !== var_mode) { + warn( + "expected_a_b", + the_statement, + var_mode, + the_statement.id + ); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + warn("var_switch", the_statement); + } + if (functionage.loop > 0 && the_statement.id === "var") { + warn("var_loop", the_statement); + } + (function next() { + if (next_token.id === "{" && the_statement.id !== "var") { + const the_brace = next_token; + the_brace.names = []; + advance("{"); + (function pair() { + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + survey(name); + advance(); + if (next_token.id === ":") { + advance(":"); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + next_token.label = name; + the_brace.names.push(next_token); + enroll(next_token, "variable", is_const); + advance(); + } else { + the_brace.names.push(name); + enroll(name, "variable", is_const); + } + if (next_token.id === ",") { + advance(","); + return pair(); + } + }()); + advance("}"); + advance("="); + the_brace.expression = expression(0); + the_statement.names.push(the_brace); + } else if (next_token.id === "[" && the_statement.id !== "var") { + const the_bracket = next_token; + the_bracket.names = []; + advance("["); + (function element() { + let ellipsis; + if (next_token.id === "...") { + ellipsis = true; + advance("..."); + } + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + const name = next_token; + advance(); + the_bracket.names.push(name); + enroll(name, "variable", the_statement.id === "const"); + if (ellipsis) { + name.ellipsis = true; + } else if (next_token.id === ",") { + advance(","); + return element(); + } + }()); + advance("]"); + advance("="); + the_bracket.expression = expression(0); + the_statement.names.push(the_bracket); + } else if (next_token.identifier) { + const name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", is_const); + if (next_token.id === "=" || is_const) { + advance("="); + name.init = true; + name.expression = expression(0); + } + the_statement.names.push(name); + } else { + return stop("expected_identifier_a", next_token); + } + if (next_token.id === ",") { + if (!option.multivar) { + warn("expected_a_b", next_token, ";", ","); + } + advance(","); + return next(); + } + }()); + the_statement.open = ( + the_statement.names.length > 1 + && the_statement.line !== the_statement.names[1].line + ); + semicolon(); + return the_statement; +} + +stmt("const", do_var); +stmt("continue", function () { + const the_continue = token; + if (functionage.loop < 1 || functionage.finally > 0) { + warn("unexpected_a", the_continue); + } + not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; +}); +stmt("debugger", function () { + const the_debug = token; + if (!option.devel) { + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; +}); +stmt("delete", function () { + const the_token = token; + const the_value = expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; +}); +stmt("do", function () { + const the_do = token; + not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; +}); +stmt("export", function () { + const the_export = token; + let the_id; + let the_name; + let the_thing; + + function export_id() { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + the_id = next_token.id; + the_name = global.context[the_id]; + if (the_name === undefined) { + warn("unexpected_a"); + } else { + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a"); + } + exports[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + } + + the_export.expression = []; + if (next_token.id === "default") { + if (exports.default !== undefined) { + warn("duplicate_a"); + } + advance("default"); + the_thing = expression(0); + semicolon(); + exports.default = the_thing; + the_export.expression.push(the_thing); + } else { + if (next_token.id === "function") { + the_thing = statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (exports[the_id] !== undefined) { + warn("duplicate_a", the_name); + } + exports[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + warn("unexpected_a"); + } else if (next_token.id === "{") { + advance("{"); + (function loop() { + export_id(); + if (next_token.id === ",") { + advance(","); + return loop(); + } + }()); + advance("}"); + semicolon(); + } else { + export_id(); + if (the_name.writable !== true) { + warn("unexpected_a", token); + } + semicolon(); + } + } + module_mode = true; + return the_export; +}); +stmt("for", function () { + let first; + const the_for = token; + if (!option.for) { + warn("unexpected_a", the_for); + } + not_top_level(the_for); + functionage.loop += 1; + advance("("); + token.free = true; + if (next_token.id === ";") { + return stop("expected_a_b", the_for, "while (", "for (;"); + } + if ( + next_token.id === "var" + || next_token.id === "let" + || next_token.id === "const" + ) { + return stop("unexpected_a"); + } + first = expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = expression(0); + advance(";"); + the_for.inc = expression(0); + if (the_for.inc.id === "++") { + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; +}); +stmt("function", do_function); +stmt("if", function () { + let the_else; + const the_if = token; + the_if.expression = condition(); + the_if.block = block(); + if (next_token.id === "else") { + advance("else"); + the_else = token; + the_if.else = ( + next_token.id === "if" + ? statement() + : block() + ); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + the_if.disrupt = true; + } else { + warn("unexpected_a", the_else); + } + } + } + return the_if; +}); +stmt("import", function () { + const the_import = token; + let name; + if (typeof module_mode === "object") { + warn("unexpected_directive_a", module_mode, module_mode.directive); + } + module_mode = true; + if (next_token.identifier) { + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + const names = []; + advance("{"); + if (next_token.id !== "}") { + while (true) { + if (!next_token.identifier) { + stop("expected_identifier_a"); + } + name = next_token; + advance(); + if (name.id === "ignore") { + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (next_token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token; + if (!rx_module.test(token.value)) { + warn("bad_module_name_a", token); + } + froms.push(token.value); + semicolon(); + return the_import; +}); +stmt("let", do_var); +stmt("return", function () { + const the_return = token; + not_top_level(the_return); + if (functionage.finally > 0) { + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (next_token.id !== ";" && the_return.line === next_token.line) { + the_return.expression = expression(10); + } + advance(";"); + return the_return; +}); +stmt("switch", function () { + let dups = []; + let last; + let stmts; + const the_cases = []; + let the_disrupt = true; + const the_switch = token; + not_top_level(the_switch); + if (functionage.finally > 0) { + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token.free = true; + the_switch.expression = expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + (function major() { + const the_case = next_token; + the_case.arity = "statement"; + the_case.expression = []; + (function minor() { + advance("case"); + token.switch = true; + const exp = expression(0); + if (dups.some(function (thing) { + return are_similar(thing, exp); + })) { + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (next_token.id === "case") { + return minor(); + } + }()); + stmts = statements(); + if (stmts.length < 1) { + warn("expected_statements_a"); + return; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "break;", + artifact(next_token) + ); + } + if (next_token.id === "case") { + return major(); + } + }()); + dups = undefined; + if (next_token.id === "default") { + const the_default = next_token; + advance("default"); + token.switch = true; + advance(":"); + the_switch.else = statements(); + if (the_switch.else.length < 1) { + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + const the_last = the_switch.else[the_switch.else.length - 1]; + if (the_last.id === "break" && the_last.label === undefined) { + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; +}); +stmt("throw", function () { + const the_throw = token; + the_throw.disrupt = true; + the_throw.expression = expression(10); + semicolon(); + if (functionage.try > 0) { + warn("unexpected_a", the_throw); + } + return the_throw; +}); +stmt("try", function () { + let the_catch; + let the_disrupt; + const the_try = token; + if (functionage.try > 0) { + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (next_token.id === "catch") { + let ignored = "ignore"; + the_catch = next_token; + the_try.catch = the_catch; + advance("catch"); + advance("("); + if (!next_token.identifier) { + return stop("expected_identifier_a", next_token); + } + if (next_token.id !== "ignore") { + ignored = undefined; + the_catch.name = next_token; + enroll(next_token, "exception", true); + } + advance(); + advance(")"); + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + } else { + warn( + "expected_a_before_b", + next_token, + "catch", + artifact(next_token) + ); + + } + if (next_token.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; +}); +stmt("var", do_var); +stmt("while", function () { + const the_while = token; + not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; +}); +stmt("with", function () { + stop("unexpected_a", token); +}); + +ternary("?", ":"); + +// Ambulation of the parse tree. + +function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; +} + +function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all of the +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + return task(the_token); + }); + } + } + }; +} + +const posts = empty(); +const pres = empty(); +const preaction = action(pres); +const postaction = action(posts); +const preamble = amble(pres); +const postamble = amble(posts); + +function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.id === "function") { + walk_statement(thing.block); + } + if (thing.arity === "pre" || thing.arity === "post") { + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + warn("unexpected_statement_a", thing); + } + postamble(thing); + } + } +} + +function walk_statement(thing) { + if (thing) { + if (Array.isArray(thing)) { + thing.forEach(walk_statement); + } else { + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + ) { + warn( + (thing.id === "(string)" && thing.value === "use strict") + ? "unexpected_a" + : "unexpected_expression_a", + thing + ); + } + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + } +} + +function lookup(thing) { + if (thing.arity === "variable") { + +// Look up the variable in the current context. + + let the_variable = functionage.context[thing.id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable === undefined) { + stack.forEach(function (outer) { + const a_variable = outer.context[thing.id]; + if ( + a_variable !== undefined + && a_variable.role !== "label" + ) { + the_variable = a_variable; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (the_variable === undefined) { + if (declared_globals[thing.id] === undefined) { + warn("undeclared_a", thing); + return; + } + the_variable = { + dead: false, + parent: global, + id: thing.id, + init: true, + role: "variable", + used: 0, + writable: false + }; + global.context[thing.id] = the_variable; + } + the_variable.closure = true; + functionage.context[thing.id] = the_variable; + } else if (the_variable.role === "label") { + warn("label_a", thing); + } + if ( + the_variable.dead + && ( + the_variable.calls === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + ) { + warn("out_of_scope_a", thing); + } + return the_variable; + } +} + +function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); +} + +function preaction_function(thing) { + if (thing.arity === "statement" && blockage.body !== true) { + warn("unexpected_a", thing); + } + if (thing.level === 1) { + if ( + module_mode === true + || global.strict !== undefined + || thing.complex + ) { + if (thing.id !== "=>" && thing.block.strict !== undefined) { + warn("unexpected_a", thing.block.strict); + } + } else { + if (thing.block.strict === undefined) { + warn("use_strict", thing); + } + } + } + stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); +} + +function bitwise_check(thing) { + if (!option.bitwise && bitwiseop[thing.id] === true) { + warn("unexpected_a", thing); + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + warn("unexpected_a", thing); + } +} + +function pop_block() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); +} + +function activate(name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.init = true; + } + } + blockage.live.push(name); +} + +function action_var(thing) { + thing.names.forEach(activate); +} + +preaction("assignment", bitwise_check); +preaction("binary", bitwise_check); +preaction("binary", function (thing) { + if (relationop[thing.id] === true) { + const left = thing.expression[0]; + const right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + warn("expected_string_a", right); + } + } else { + const value = right.value; + if (value === "null" || value === "undefined") { + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + warn("expected_type_string_a", right, value); + } + } + } + } +}); +preaction("binary", "==", function (thing) { + warn("expected_a_b", thing, "===", "=="); +}); +preaction("binary", "!=", function (thing) { + warn("expected_a_b", thing, "!==", "!="); +}); +preaction("binary", "=>", preaction_function); +preaction("binary", "||", function (thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + warn("and", thang); + } + }); +}); +preaction("binary", "(", function (thing) { + const left = thing.expression[0]; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + const parent = functionage.name.parent; + if (parent) { + const left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + && left_variable.dead + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } +}); +preaction("binary", "in", function (thing) { + warn("infix_in", thing); +}); +preaction("binary", "instanceof", function (thing) { + warn("unexpected_a", thing); +}); +preaction("binary", ".", function (thing) { + if (thing.expression.new) { + thing.new = true; + } +}); +preaction("statement", "{", function (thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; +}); +preaction("statement", "for", function (thing) { + if (thing.name !== undefined) { + const the_variable = lookup(thing.name); + if (the_variable !== undefined) { + the_variable.init = true; + if (!the_variable.writable) { + warn("bad_assignment_a", thing.name); + } + } + } + walk_statement(thing.initial); +}); +preaction("statement", "function", preaction_function); +preaction("unary", "~", bitwise_check); +preaction("unary", "function", preaction_function); +preaction("variable", function (thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } +}); + +function init_variable(name) { + const the_variable = lookup(name); + if (the_variable !== undefined) { + if (the_variable.writable) { + the_variable.init = true; + return; + } + } + warn("bad_assignment_a", name); +} + +postaction("assignment", "+=", function (thing) { + let right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } +}); +postaction("assignment", function (thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + if (thing.id === "=") { + if (thing.names !== undefined) { + if (Array.isArray(thing.names)) { + thing.names.forEach(init_variable); + } else { + init_variable(thing.names); + } + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.writable !== true) { + warn("bad_assignment_a", lvalue); + } + } + const right = syntax[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + warn("unexpected_a", thing.expression[1]); + } + } +}); + +function postaction_function(thing) { + delete functionage.finally; + delete functionage.loop; + delete functionage.switch; + delete functionage.try; + functionage = stack.pop(); + if (thing.wrapped) { + warn("unexpected_parens", thing); + } + return pop_block(); +} + +postaction("binary", function (thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || are_similar(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option.convert) { + if (thing.expression[0].value === "") { + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === ".") { + if (thing.expression.id === "RegExp") { + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } +}); +postaction("binary", "&&", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "||", function (thing) { + if ( + is_weird(thing.expression[0]) + || are_similar(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + warn("weird_condition_a", thing); + } +}); +postaction("binary", "=>", postaction_function); +postaction("binary", "(", function (thing) { + let left = thing.expression[0]; + let the_new; + let arg; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option.eval) { + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + warn( + "expected_a_before_b", + left, + "new", + artifact(left) + ); + } + } + } else if (left.id === ".") { + let cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + cack = !cack; + } + if (rx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + warn("unexpected_a", the_new); + } else { + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + const paren = left.expression; + if (paren.id === "(") { + const array = paren.expression; + if (array.length === 1) { + const new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } +}); +postaction("binary", "[", function (thing) { + if (thing.expression[0].id === "RegExp") { + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + warn("weird_expression_a", thing.expression[1]); + } +}); +postaction("statement", "{", pop_block); +postaction("statement", "const", action_var); +postaction("statement", "export", top_level_only); +postaction("statement", "for", function (thing) { + walk_statement(thing.inc); +}); +postaction("statement", "function", postaction_function); +postaction("statement", "import", function (the_thing) { + const name = the_thing.name; + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return top_level_only(the_thing); +}); +postaction("statement", "let", action_var); +postaction("statement", "try", function (thing) { + if (thing.catch !== undefined) { + const the_name = thing.catch.name; + if (the_name !== undefined) { + const the_variable = functionage.context[the_name.id]; + the_variable.dead = false; + the_variable.init = true; + } + walk_statement(thing.catch.block); + } +}); +postaction("statement", "var", action_var); +postaction("ternary", function (thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || are_similar(thing.expression[1], thing.expression[2]) + ) { + warn("unexpected_a", thing); + } else if (are_similar(thing.expression[0], thing.expression[1])) { + warn("expected_a_b", thing, "||", "?"); + } else if (are_similar(thing.expression[0], thing.expression[2])) { + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + warn("wrap_condition", thing.expression[0]); + } +}); +postaction("unary", function (thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option.convert) { + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } +}); +postaction("unary", "function", postaction_function); +postaction("unary", "+", function (thing) { + if (!option.convert) { + warn("expected_a_b", thing, "Number(...)", "+"); + } + const right = thing.expression; + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } +}); + +function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + if (id !== "ignore") { + const name = the_function.context[id]; + if (name.parent === the_function) { + if ( + name.used === 0 + && ( + name.role !== "function" + || name.parent.arity !== "unary" + ) + ) { + warn("unused_a", name); + } else if (!name.init) { + warn("uninitialized_a", name); + } + } + } + }); +} + +function uninitialized_and_unused() { + +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (module_mode === true || option.node) { + delve(global); + } + functions.forEach(delve); +} + +// Go through the token list, looking at usage of whitespace. + +function whitage() { + let closer = "(end)"; + let free = false; + let left = global; + let margin = 0; + let nr_comments_skipped = 0; + let open = true; + let right; + + function expected_at(at) { + warn( + "expected_a_at_b_c", + right, + artifact(right), + fudge + at, + artifact_column(right) + ); + } + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function no_space() { + if (left.line === right.line) { + if (left.thru !== right.from && nr_comments_skipped === 0) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (open) { + const at = ( + free + ? margin + : margin + 8 + ); + if (right.from < at) { + expected_at(at); + } + } else { + if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (free) { + if (right.from < margin) { + expected_at(margin); + } + } else { + const mislaid = ( + stack.length > 0 + ? stack[stack.length - 1].right + : undefined + ); + if (!open && mislaid !== undefined) { + warn( + "expected_a_next_at_b", + mislaid, + artifact(mislaid.id), + margin + 4 + fudge + ); + } else if (right.from !== margin + 8) { + expected_at(margin + 8); + } + } + } + } + + stack = []; + tokens.forEach(function (the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + + const new_closer = opener[left.id]; + if (typeof new_closer === "string") { + if (new_closer !== right.id) { + stack.push({ + closer: closer, + free: free, + margin: margin, + open: open, + right: right + }); + closer = new_closer; + if (left.line !== right.line) { + free = closer === ")" && left.free; + open = true; + margin += 4; + if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (right.switch) { + at_margin(-4); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + free = false; + open = false; + no_space_only(); + } + } else { + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like {]) have already been detected. + + if (left.line === right.line) { + no_space(); + } else { + at_margin(0); + } + } + } else { + +// If right is a closer, then pop the previous state. + + if (right.id === closer) { + const previous = stack.pop(); + margin = previous.margin; + if (open && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + closer = previous.closer; + free = previous.free; + open = previous.open; + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-4); + } else if (right.role === "label") { + if (right.from !== 0) { + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + one_space(); + } else { + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + at_margin(0); + } else { + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + no_space(); + } else if ( + left.id === "." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + ) { + no_space_only(); + } else if (right.id === ".") { + no_space_only(); + } else if (left.id === ";") { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + one_space_only(); + } else if (right.statement === true) { + if (open) { + at_margin(0); + } else { + one_space(); + } + } else if ( + left.id === "var" + || left.id === "const" + || left.id === "let" + ) { + stack.push({ + closer: closer, + free: free, + margin: margin, + open: open + }); + closer = ";"; + free = false; + open = left.open; + if (open) { + margin = margin + 4; + at_margin(0); + } else { + one_space_only(); + } + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +// The jslint function itself. + +export default function jslint( + source = "", + option_object = empty(), + global_array = [] +) { + try { + warnings = []; + option = Object.assign(empty(), option_object); + anon = "anonymous"; + block_stack = []; + declared_globals = empty(); + directive_mode = true; + directives = []; + early_stop = true; + exports = empty(); + froms = []; + fudge = ( + option.fudge + ? 1 + : 0 + ); + functions = []; + global = { + id: "(global)", + body: true, + context: empty(), + from: 0, + level: 0, + line: 0, + live: [], + loop: 0, + switch: 0, + thru: 0 + }; + blockage = global; + functionage = global; + json_mode = false; + mega_mode = false; + module_mode = false; + next_token = global; + property = empty(); + shebang = false; + stack = []; + tenure = undefined; + token = global; + token_nr = 0; + var_mode = undefined; + populate(standard, declared_globals, false); + populate(global_array, declared_globals, false); + Object.keys(option).forEach(function (name) { + if (option[name] === true) { + const allowed = allowed_option[name]; + if (Array.isArray(allowed)) { + populate(allowed, declared_globals, false); + } + } + }); + tokenize(source); + advance(); + if (json_mode) { + tree = json_value(); + advance("(end)"); + } else { + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option.browser) { + if (next_token.id === ";") { + advance(";"); + } + } else { + +// If we are not in a browser, then the file form of strict pragma may be used. + + if ( + next_token.value === "use strict" + ) { + global.strict = next_token; + advance("(string)"); + advance(";"); + } + } + tree = statements(); + advance("(end)"); + functionage = global; + walk_statement(tree); + if (module_mode && global.strict !== undefined) { + warn("unexpected_a", global.strict); + } + if (warnings.length === 0) { + uninitialized_and_unused(); + if (!option.white) { + whitage(); + } + } + } + if (!option.browser) { + directives.forEach(function (comment) { + if (comment.directive === "global") { + warn("missing_browser", comment); + } + }); + } + early_stop = false; + } catch (e) { + if (e.name !== "JSLintError") { + warnings.push(e); + } + warn_at(bundle.early_stop, e.line || 0, e.column || 0); + } + return { + directives, + edition: "2018-09-26", + exports, + froms, + functions, + global, + id: "(JSLint)", + json: json_mode, + lines, + module: module_mode === true, + ok: warnings.length === 0 && !early_stop, + option, + property, + shebang: ( + shebang + ? lines[0] + : undefined + ), + stop: early_stop, + tokens, + tree, + warnings: warnings.sort(function (a, b) { + return a.code === bundle.early_stop + ? -1 + : b.code === bundle.early_stop + ? 1 + : a.line - b.line || a.column - b.column; + }) + }; +}; diff --git a/branch.warning_early_stop/report.js b/branch.warning_early_stop/report.js new file mode 100644 index 000000000..87ae83e43 --- /dev/null +++ b/branch.warning_early_stop/report.js @@ -0,0 +1,218 @@ +// report.js +// 2018-07-29 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Generate JSLint HTML reports. + +/*property + closure, column, context, edition, error, exports, filter, forEach, froms, + fudge, function, functions, global, id, isArray, join, json, keys, length, + level, line, lines, message, module, name, names, option, parameters, + parent, property, push, replace, role, signature, sort, warnings +*/ + +const rx_amp = /&/g; +const rx_gt = />/g; +const rx_lt = / with less destructive entities. + + return String( + string + ).replace( + rx_amp, + "&" + ).replace( + rx_lt, + "<" + ).replace( + rx_gt, + ">" + ); +} + +export default { + error: function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + let fudge = Number(Boolean(data.option.fudge)); + let output = []; + data.warnings.forEach(function (warning) { + output.push( + "
    ", + entityify(warning.line + fudge), + ".", + entityify(warning.column + fudge), + "
    ", + entityify(warning.message), + "
    ", + entityify(data.lines[warning.line] || ""), + "" + ); + }); + return output.join(""); + }, + + function: function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + let fudge = Number(Boolean(data.option.fudge)); + let mode = ( + data.module + ? "module" + : "global" + ); + let output = []; + + if (data.json) { + return ( + data.warnings.length === 0 + ? "
    JSON: good.
    " + : "
    JSON: bad.
    " + ); + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + let global = Object.keys(data.global.context).sort(); + let froms = data.froms.sort(); + let exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + let context = the_function.context; + let list = Object.keys(context); + output.push( + "
    ", + entityify(the_function.line + fudge), + "
    ", + ( + the_function.name === "=>" + ? entityify(the_function.signature) + " =>" + : ( + typeof the_function.name === "string" + ? "«" + entityify(the_function.name) + "»" + : "" + entityify(the_function.name.id) + "" + ) + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + let params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.parent === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.parent === the_function + ); + })); + detail("outer", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.parent !== the_function + && the_variable.parent.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].parent.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + output.push( + "
    JSLint edition ", + entityify(data.edition), + "
    " + ); + return output.join(""); + }, + + property: function property_directive(data) { + +// Produce the /*property*/ directive. + + let not_first = false; + let output = ["/*property"]; + let length = 1111; + let properties = Object.keys(data.property); + + if (properties.length > 0) { + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length >= 80) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); + } + } +}; diff --git a/browser.js b/browser.js new file mode 100644 index 000000000..73f197b24 --- /dev/null +++ b/browser.js @@ -0,0 +1,406 @@ +// browser.js +// 2018-06-16 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +/*jslint + beta + browser +*/ + +/*property + click, + CodeMirror, Tab, addEventListener, checked, closure, column, context, + create, ctrlKey, display, edition, exports, extraKeys, filter, forEach, + fromTextArea, froms, functions, getElementById, getValue, global, id, + indentUnit, indentWithTabs, innerHTML, isArray, join, jslint_result, json, + key, keys, length, level, line, lineNumbers, lineWrapping, line_source, + matchBrackets, message, metaKey, mode, module, name, names, onclick, + parameters, parent, property, push, querySelector, querySelectorAll, + replace, replaceSelection, role, scrollTop, setValue, showTrailingSpace, + signature, sort, split, stack_trace, stop, style, textContent, title, value, + warnings +*/ + +import jslint from "./jslint.js?cc=ab6s"; + +// This is the web script companion file for JSLint. It includes code for +// interacting with the browser and displaying the reports. + +let editor; + +function entityify(string) { + +// Replace & < > with less destructive entities. + + return String(string).replace(( + /&/g + ), "&").replace(( + //g + ), ">"); +} + +function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + let output = []; + if (data.stop) { + output.push("
    JSLint was unable to finish.
    "); + } + data.warnings.forEach(function ({ + column, + line, + line_source, + message, + stack_trace = "" + }) { + output.push( + "
    ", + entityify(line), + ".", + entityify(column), + "
    ", + entityify(message), + "
    ", + entityify(line_source + "\n" + stack_trace), + "" + ); + }); + if (output.length === 0) { + output.push("
    There are no warnings.
    "); + } + return output.join(""); +} + +function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + let exports; + let froms; + let global; + let mode = ( + data.module + ? "module" + : "global" + ); + let output = []; + + if (data.json) { + return ( + data.warnings.length === 0 + ? "
    JSON: good.
    " + : "
    JSON: bad.
    " + ); + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + global = Object.keys(data.global.context).sort(); + froms = data.froms.sort(); + exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + let context = the_function.context; + let list = Object.keys(context); + let params; + output.push( + "
    ", + entityify(the_function.line), + "
    ", + ( + the_function.name === "=>" + ? entityify(the_function.signature) + " =>" + : ( + typeof the_function.name === "string" + ? ( + "\u00ab" + entityify(the_function.name) + + "\u00bb" + ) + : "" + entityify(the_function.name.id) + "" + ) + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.parent === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.parent === the_function + ); + })); + detail("outer", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.parent !== the_function + && the_variable.parent.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].parent.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + return output.join(""); +} + +function property_directive(data) { + +// Produce the /*property*/ directive. + + let length = 1111; + let not_first = false; + let output = ["/*property"]; + let properties = Object.keys(data.property); + + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length >= 80) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); +} + +function call_jslint() { + let global_string; + let option; + let result; + +// Show ui-loader-animation. + + document.getElementById("uiLoader1").style.display = "flex"; + +// First build the option object. + + option = Object.create(null); + document.querySelectorAll("input[type=checkbox]").forEach(function (elem) { + if (elem.checked) { + option[elem.title] = true; + } + }); + +// Call JSLint with the source text, the options, and the predefined globals. + + global_string = document.getElementById("JSLINT_GLOBAL").value; + result = jslint( + editor.getValue(), + option, + ( + global_string === "" + ? undefined + : global_string.split( + /[\s,;'"]+/ + ) + ) + ); + +// Debug result. + + globalThis.jslint_result = result; + +// Generate the reports. +// Display the reports. + + document.getElementById( + "JSLINT_WARNINGS_LIST" + ).innerHTML = error_report(result); + document.getElementById( + "JSLINT_REPORT_LIST" + ).innerHTML = function_report(result); + document.getElementById( + "JSLINT_PROPERTY" + ).value = property_directive(result); + document.getElementById("JSLINT_PROPERTY").scrollTop = 0; + +// Hide ui-loader-animation. + + setTimeout(function () { + document.getElementById("uiLoader1").style.display = "none"; + }, 500); +} + +(function () { + +// Init edition. + + document.getElementById("JSLINT_EDITION").textContent = ( + `Edition: ${jslint.edition}` + ); + +// Init event-handling. + + document.addEventListener("keydown", function (evt) { + if ((evt.ctrlKey || evt.metaKey) && evt.key === "Enter") { + call_jslint(); + } + }); + document.querySelector("button[name='JSLint']").onclick = call_jslint; + document.querySelector( + "button[name='clear_source']" + ).onclick = function () { + editor.setValue(""); + }; + document.querySelector( + "button[name='clear_options']" + ).onclick = function () { + document.querySelectorAll( + "input[type=checkbox]" + ).forEach(function (elem) { + elem.checked = false; + }); + document.getElementById("JSLINT_GLOBAL").value = ""; + }; + +// Init CodeMirror editor. + + editor = globalThis.CodeMirror.fromTextArea(document.getElementById( + "JSLINT_SOURCE" + ), { + extraKeys: { + Tab: function (editor) { + editor.replaceSelection(" "); + } + }, + indentUnit: 4, + indentWithTabs: false, + lineNumbers: true, + lineWrapping: true, + matchBrackets: true, + mode: "text/javascript", + showTrailingSpace: true + }); + editor.setValue(`#!/usr/bin/env node + +/*jslint beta node*/ + +import jslint from \u0022./jslint.mjs\u0022; +import https from "https"; + +// Optional directives. +// .... /*jslint beta*/ .......... Enable extra warnings currently in beta. +// .... /*jslint bitwise*/ ....... Allow bitwise operators. +// .... /*jslint browser*/ ....... Assume browser environment. +// .... /*jslint convert*/ ....... Allow conversion operators. +// .... /*jslint couch*/ ......... Assume CouchDb environment. +// .... /*jslint debug*/ ......... Include jslint stack-trace in warnings. +// .... /*jslint devel*/ ......... Allow console.log() and friends. +// .... /*jslint eval*/ .......... Allow eval(). +// .... /*jslint for*/ ........... Allow for-statement. +// .... /*jslint getset*/ ........ Allow get() and set(). +// .... /*jslint long*/ .......... Allow long lines. +// .... /*jslint name*/ .......... Allow weird property names. +// .... /*jslint node*/ .......... Assume Node.js environment. +// .... /*jslint single*/ ........ Allow single-quote strings. +// .... /*jslint test_internal_error*/ ... Test jslint's internal-error +// ........................................... handling-ability. +// .... /*jslint this*/ .......... Allow 'this'. +// .... /*jslint unordered*/ ..... Allow unordered cases, params, properties, +// ................................... and variables. +// .... /*jslint variable*/ ...... Allow unordered const and let declarations +// ................................... that are not at top of function-scope. +// .... /*jslint white: true...... Allow messy whitespace. + +/*jslint-disable*/ +// TODO: jslint this code-block in future. +console.log('hello world'); +/*jslint-enable*/ + +// Suppress warnings on next-line. +eval( //jslint-quiet + "console.log('hello world');" +); + +(async function () { + let result; + result = await new Promise(function (resolve) { + https.request("https://www.jslint.com/jslint.js", function (res) { + result = ""; + res.on("data", function (chunk) { + result += chunk; + }).on("end", function () { + resolve(result); + }).setEncoding("utf8"); + }).end(); + }); + result = jslint(result); + result.warnings.forEach(function ({ + formatted_message + }) { + console.error(formatted_message); + }); +}()); +`); + document.querySelector("button[name='JSLint']").click(); +}()); diff --git a/browser.mjs b/browser.mjs new file mode 100644 index 000000000..2e25c6a4f --- /dev/null +++ b/browser.mjs @@ -0,0 +1,946 @@ +// browser.mjs +// Original Author: Douglas Crockford (https://www.jslint.com). + +// This is free and unencumbered software released into the public domain. + +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. + +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +// For more information, please refer to + + +/*jslint beta, browser*/ + +/*property + dom_style_report_unmatched, + focus, + indentSelection, + slice, somethingSelected, + CodeMirror, Pos, Tab, addEventListener, checked, click, closest, closure, + column, context, ctrlKey, currentTarget, dispatchEvent, display, edition, + editor, error, exports, extraKeys, filter, forEach, from, fromTextArea, + froms, functions, global, globals, gutters, id, indentUnit, indentWithTabs, + innerHTML, isArray, join, json, key, keys, length, level, line, lineNumbers, + lineWrapping, line_source, lint, lintOnChange, map, matchBrackets, message, + metaKey, mode, mode_stop, module, name, names, offsetWidth, onclick, + onkeyup, outerHTML, parameters, parent, performLint, preventDefault, + property, push, querySelector, querySelectorAll, registerHelper, replace, + replaceSelection, result, reverse, role, search, setSize, setValue, + severity, showTrailingSpace, signature, sort, split, stack_trace, stop, + stopPropagation, style, target, test, textContent, to, trim, value, + warnings, width +*/ + +import jslint from "./jslint.mjs?cc=0z2f"; + +// This is the web script companion file for JSLint. It includes code for +// interacting with the browser and displaying the reports. + +let editor; +let jslint_option_dict = { + lintOnChange: false +}; +let mode_debug; + +function dom_style_report_unmatched() { + +// Debug css-style. + + let style_list = []; + Array.from(document.querySelectorAll("style")).forEach(function (elem) { + elem.innerHTML.replace(( + /\/\*[\S\s]*?\*\/|;|\}/g + ), "\n").replace(( + /^([^\n\u0020@].*?)[,{:].*?$/gm + ), function (match0, match1) { + let ii; + try { + ii = document.querySelectorAll(match1).length; + } catch (err) { + console.error(match1 + "\n" + err); //jslint-quiet + } + if (ii <= 1 && !( + /^0\u0020(?:(body\u0020>\u0020)?(?:\.button|\.readonly|\.styleColorError|\.textarea|\.uiAnimateSlide|a|base64|body|code|div|input|pre|textarea)(?:,|\u0020\{))|^[1-9]\d*?\u0020#/m + ).test(ii + " " + match0)) { + style_list.push(ii + " " + match0); + } + return ""; + }); + }); + style_list.sort().reverse().forEach(function (elem, ii, list) { + console.error( //jslint-quiet + "dom_style_report_unmatched " + (list.length - ii) + ". " + elem + ); + }); +} + +function jslint_plugin_codemirror(CodeMirror) { + +// This function will integrate jslint with CodeMirror's lint addon. +// Requires CodeMirror and jslint. + + CodeMirror.registerHelper("lint", "javascript", function (text, options) { + options.result = jslint(text, options, options.globals); + return options.result.warnings.map(function ({ + column, + line, + message, + mode_stop + }) { + return { + from: CodeMirror.Pos(line - 1, column - 1), //jslint-quiet + message, + severity: ( + mode_stop + ? "error" + : "warning" + ), + to: CodeMirror.Pos(line - 1, column) //jslint-quiet + }; + }); + }); +} + +function jslint_report_html({ + exports, + froms, + functions, + global, + json, + module, + property, + stop, + warnings +}) { + +// This function will create html-reports for warnings, properties, and +// functions from jslint's results. +// example usage: +// let result = jslint("console.log('hello world')"); +// let html = jslint_report_html(result); + + let html = ""; + let length_80 = 1111; + + function detail(title, list) { + return ( + (Array.isArray(list) && list.length > 0) + ? ( + +// Google Lighthouse Accessibility -
    's do not contain only properly-ordered +//
    and
    groups, + `).trim() + "\n"; + html += "\n"; + return html; +} + +async function jslint_ui_call() { +// This function will run jslint in browser and create html-reports. + +// Show ui-loader-animation. + + document.querySelector("#uiLoader1 > div").textContent = "Linting"; + document.querySelector("#uiLoader1").style.display = "flex"; + try { + +// Wait awhile before running cpu-intensive linter so ui-loader doesn't jank. + + await new Promise(function (resolve) { + setTimeout(resolve); + }); + +// Update jslint_option_dict from ui-inputs. + + document.querySelectorAll( + "#JSLINT_OPTIONS input[type=checkbox]" + ).forEach(function (elem) { + jslint_option_dict[elem.value] = elem.checked; + }); + +// Execute linter. + + editor.performLint(); + +// Generate the reports. +// Display the reports. + + document.querySelector( + "#JSLINT_REPORT_HTML" + ).outerHTML = jslint_report_html(jslint_option_dict.result); + jslint_ui_onresize(); + } catch (err) { + console.error(err); //jslint-quiet + } + +// Hide ui-loader-animation. + + setTimeout(function () { + document.querySelector("#uiLoader1").style.display = "none"; + }, 500); +} + +function jslint_ui_onresize() { + let content_width = document.querySelector( + "#JSLINT_OPTIONS" + ).offsetWidth; + +// Set explicit content-width for overflow to work properly. + + document.querySelectorAll( + ".JSLINT_ fieldset > div" + ).forEach(function (elem) { + if (!elem.closest("#JSLINT_OPTIONS")) { + elem.style.width = content_width + "px"; + } + }); + editor.setSize(content_width); +} + +(function () { + let CodeMirror = window.CodeMirror; + +// Init edition. + + document.querySelector("#JSLINT_EDITION").textContent = ( + "Edition: " + jslint.edition + ); + +// Init mode_debug. + mode_debug = ( + /\bdebug=1\b/ + ).test(location.search); + +// Init CodeMirror editor. + + editor = CodeMirror.fromTextArea(document.querySelector( + "#JSLINT_SOURCE textarea" + ), { + extraKeys: { + "Shift-Tab": "indentLess", + Tab: function (cm) { + if (cm.somethingSelected()) { + cm.indentSelection("add"); + return; + } + cm.replaceSelection(" "); + } + }, + gutters: ["CodeMirror-lint-markers"], + indentUnit: 4, + indentWithTabs: false, + lineNumbers: true, + lineWrapping: true, + lint: jslint_option_dict, + matchBrackets: true, + mode: "text/javascript", + showTrailingSpace: true + }); + window.editor = editor; + +// Init CodeMirror linter. + + jslint_plugin_codemirror(CodeMirror); + +// Init event-handling. + + document.addEventListener("keydown", function (evt) { + switch ((evt.ctrlKey || evt.metaKey) && evt.key) { + case "Enter": + case "e": + evt.preventDefault(); + evt.stopPropagation(); + jslint_ui_call(); + break; + } + }); + document.querySelector( + "#JSLINT_BUTTONS" + ).onclick = function ({ + target + }) { + switch (target.name) { + case "JSLint": + jslint_ui_call(); + break; + case "clear_options": + document.querySelectorAll( + "#JSLINT_OPTIONS input[type=checkbox]" + ).forEach(function (elem) { + elem.checked = false; + }); + document.querySelector("#JSLINT_GLOBALS").value = ""; + document.querySelector("#JSLINT_OPTIONS").click(); + document.querySelector("#JSLINT_GLOBALS").dispatchEvent( + new Event("keyup") + ); + break; + case "clear_source": + editor.setValue(""); + editor.focus(); + break; + } + }; + document.querySelector( + "#JSLINT_GLOBALS" + ).onkeyup = function ({ + currentTarget + }) { + jslint_option_dict.globals = currentTarget.value.trim().split( + /[\s,;'"]+/ + ); + }; + document.querySelector( + "#JSLINT_OPTIONS" + ).onclick = function (evt) { + let elem; + elem = evt.target.closest( + "#JSLINT_OPTIONS div[title]" + ); + elem = elem && elem.querySelector("input[type=checkbox]"); + if (elem && elem !== evt.target) { + evt.preventDefault(); + evt.stopPropagation(); + elem.checked = !elem.checked; + } + }; + window.addEventListener("load", jslint_ui_onresize); + window.addEventListener("resize", jslint_ui_onresize); + if (!mode_debug) { + editor.setValue(String(` +#!/usr/bin/env node +/*jslint browser, node*/ +/*global caches, indexedDb*/ +import https from "https"; +import jslint from \u0022./jslint.mjs\u0022; + +/*jslint-disable*/ + Syntax error.\u0020\u0020\u0020\u0020 +/*jslint-enable*/ + +eval("console.log(\\"hello world\\");"); //jslint-quiet + +eval("console.log(\\"hello world\\");"); + +// Optional directives. +// .... /*jslint beta*/ .......... Enable experimental warnings. +// .... /*jslint bitwise*/ ....... Allow bitwise operators. +// .... /*jslint browser*/ ....... Assume browser environment. +// .... /*jslint convert*/ ....... Allow conversion operators. +// .... /*jslint devel*/ ......... Allow console.log() and friends. +// .... /*jslint for*/ ........... Allow for-statement. +// .... /*jslint getset*/ ........ Allow get() and set(). +// .... /*jslint indent2*/ ....... Use 2-space indent. +// .... /*jslint long*/ .......... Allow long lines. +// .... /*jslint name*/ .......... Allow weird property names. +// .... /*jslint node*/ .......... Assume Node.js environment. +// .... /*jslint single*/ ........ Allow single-quote strings. +// .... /*jslint this*/ .......... Allow 'this'. +// .... /*jslint trace*/ ......... Include jslint stack-trace in warnings. +// .... /*jslint unordered*/ ..... Allow unordered cases, params, properties, +// ................................... and variables. +// .... /*jslint variable*/ ...... Allow unordered const and let declarations +// ................................... that are not at top of function-scope. +// .... /*jslint white*/ ......... Allow messy whitespace. + +(async function () { + let result = await new Promise(function (resolve) { + https.request("https://www.jslint.com/jslint.mjs", function (res) { + result = ""; + res.on("data", function (chunk) { + result += chunk; + }).on("end", function () { + resolve(result); + }).setEncoding("utf8"); + }).end(); + }); + result = jslint(result); + result.warnings.forEach(function ({ + formatted_message + }) { + console.error(formatted_message); + }); +}()); + `).trim() + "\n"); + } + if (mode_debug) { + document.querySelector( + "#JSLINT_OPTIONS input[value=debug]" + ).click(); + } + document.querySelector("button[name='JSLint']").click(); + +// Debug css-style. + + window.dom_style_report_unmatched = dom_style_report_unmatched; +}()); diff --git a/ci.sh b/ci.sh new file mode 100755 index 000000000..c1d675d5b --- /dev/null +++ b/ci.sh @@ -0,0 +1,2023 @@ +#!/bin/sh + +# sh one-liner +# +# git branch -d -r origin/aa +# git config --global diff.algorithm histogram +# git fetch origin alpha beta master && git fetch upstream alpha beta master +# git fetch origin alpha beta master --tags +# git fetch upstream "refs/tags/*:refs/tags/*" +# git ls-files --stage | sort +# git ls-remote --heads origin +# git update-index --chmod=+x aa.js +# head CHANGELOG.md -n50 +# ln -f jslint.mjs ~/jslint.mjs +# openssl rand -base64 32 # random key +# sh ci.sh shCiBranchPromote origin alpha beta +# sh ci.sh shRunWithScreenshotTxt .build/screenshot-changelog.svg head -n50 CHANGELOG.md +# vim rgx-lowercase \L\1\e + +shBrowserScreenshot() {(set -e +# this function will run headless-chrome to screenshot url $1 with +# window-size $2 + node --input-type=module -e ' +import moduleChildProcess from "child_process"; +import modulePath from "path"; +import moduleUrl from "url"; +// init debugInline +(function () { + var consoleError = console.error; + globalThis.debugInline = globalThis.debugInline || function (...argList) { + +// this function will both print to stderr and return [0] + + consoleError("\n\ndebugInline"); + consoleError(...argList); + consoleError("\n"); + return argList[0]; + }; +}()); +(function () { + var file; + var timeStart; + var url; + if (process.platform !== "linux") { + return; + } + timeStart = Date.now(); + url = process.argv[1]; + if (!( + /^\w+?:/ + ).test(url)) { + url = modulePath.resolve(url); + } + file = moduleUrl.parse(url).pathname; + // remove prefix $PWD from file + if (String(file + "/").startsWith(process.cwd() + "/")) { + file = file.replace(process.cwd(), ""); + } + file = ".build/screenshot-browser-" + encodeURIComponent(file).replace(( + /%/g + ), "_").toLowerCase() + ".png"; + moduleChildProcess.spawn( + ( + process.platform === "darwin" + ? "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" + : process.platform === "win32" + ? "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe" + : "/usr/bin/google-chrome-stable" + ), + [ + "--headless", + "--ignore-certificate-errors", + "--incognito", + "--screenshot", + "--timeout=30000", + "--user-data-dir=/dev/null", + "--window-size=800x600", + "-screenshot=" + file, + ( + (process.getuid && process.getuid() === 0) + ? "--no-sandbox" + : "" + ), + url + ].concat(process.argv.filter(function (elem) { + return elem.startsWith("-"); + })).filter(function (elem) { + return elem; + }), + { + stdio: [ + "ignore", 1, 2 + ] + } + ).on("exit", function (exitCode) { + console.error( + "shBrowserScreenshot" + + "\n - url - " + url + + "\n - wrote - " + file + + "\n - timeElapsed - " + (Date.now() - timeStart) + " ms" + + "\n - EXIT_CODE=" + exitCode + ); + }); +}()); +' "$@" # "' +)} + +shCiArtifactUpload() {(set -e +# this function will upload build-artifacts to branch-gh-pages + local BRANCH + local SIZE + node --input-type=module -e ' +process.exit(Number( + `${process.version.split(".")[0]}.${process.arch}.${process.platform}` !== + process.env.CI_NODE_VERSION_ARCH_PLATFORM +)); +' || return 0 + # init $BRANCH + BRANCH="$(git rev-parse --abbrev-ref HEAD)" + git pull --unshallow origin "$BRANCH" + # init .git/config + git config --local user.email "github-actions@users.noreply.github.com" + git config --local user.name "github-actions" + # screenshot asset-image-jslint + shImageJslintCreate & + # screenshot web-demo + shBrowserScreenshot \ + https://jslint-org.github.io/jslint/branch-beta/index.html + node --input-type=module -e ' +import moduleFs from "fs"; +import moduleChildProcess from "child_process"; +(async function () { + var screenshotCurl = await moduleFs.promises.stat("jslint.mjs"); + screenshotCurl = String(` +echo "\ +% Total % Received % Xferd Average Speed Time Time Time Current + Dload Upload Total Spent Left Speed +100 250k 100 250k 0 0 250k 0 0:00:01 --:--:-- 0:00:01 250k\ +" + `).trim().replace(( + /250/g + ), Math.floor(screenshotCurl.size / 1024)); + [ + // parallel-task - screenshot files + [ + "shRunWithScreenshotTxt", + ".build/screenshot-files.svg", + "shGitLsTree" + ], + // parallel-task - screenshot changelog + [ + "shRunWithScreenshotTxt", + ".build/screenshot-changelog.svg", + "head", + "-n50", + "CHANGELOG.md" + ] + ].forEach(function (argList) { + moduleChildProcess.spawn("./ci.sh", argList, { + stdio: [ + "ignore", 1, 2 + ] + }).on("exit", function (exitCode) { + if (exitCode) { + process.exit(exitCode); + } + }); + }); + // parallel-task - screenshot example-shell-commands in README.md + Array.from(String( + await moduleFs.promises.readFile("README.md", "utf8") + ).matchAll( + /\n```shell\u0020\n([\S\s]*?\n)```\n/g + )).forEach(async function ([ + ignore, file, script + ]) { + await moduleFs.promises.writeFile(file + ".sh", ( + "printf \u0027" + + script.trim().replace(( + /[%\\]/gm + ), "$&$&").replace(( + /\u0027/g + ), "\u0027\"\u0027\"\u0027").replace(( + /^/gm + ), "> ") + + "\n\n\n\u0027\n" + + script.replace( + "curl -L https://www.jslint.com/jslint.mjs > jslint.mjs", + screenshotCurl + ) + )); + moduleChildProcess.spawn( + "./ci.sh", + [ + "shRunWithScreenshotTxt", + file, + "sh", + file + ".sh" + ], + { + stdio: [ + "ignore", 1, 2 + ] + } + ); + }); +}()); +' # ' + # seo - inline css-assets and invalidate cached-assets + node --input-type=module -e ' +import moduleFs from "fs"; +(async function () { + var cacheKey = Math.random().toString(36).slice(-4); + var fileDict = {}; + await Promise.all([ + "asset-codemirror-rollup.css", + "browser.mjs", + "index.html" + ].map(async function (file) { + fileDict[file] = await moduleFs.promises.readFile(file, "utf8"); + })); + +// Inline css-assets. + + fileDict["index.html"] = fileDict["index.html"].replace(( + "\n\n" + ), function () { + return ( + "\n\n" + ); + }); + fileDict["index.html"] = fileDict["index.html"].replace(( + "\n\n" + ), function () { + return fileDict["browser.mjs"].match( + /\n\n[\S\s]*?\n<\/style>\n/ + )[0]; + }); + +// Invalidate cached-assets. + + fileDict["browser.mjs"] = fileDict["browser.mjs"].replace(( + /^import\u0020.+?\u0020from\u0020".+?\.(?:js|mjs)\b/gm + ), function (match0) { + return `${match0}?cc=${cacheKey}`; + }); + fileDict["index.html"] = fileDict["index.html"].replace(( + /\b(?:href|src)=".+?\.(?:css|js|mjs)\b/g + ), function (match0) { + return `${match0}?cc=${cacheKey}`; + }); + +// Write file. + + await Promise.all(Object.entries(fileDict).map(function ([ + file, data + ]) { + moduleFs.promises.writeFile(file, data); + })); +}()); +' # ' + # add dir .build + git add -f .build jslint.cjs jslint.js + git commit -am "add dir .build" + # checkout branch-gh-pages + git checkout -b gh-pages + git fetch origin gh-pages + git reset --hard origin/gh-pages + # update dir branch-$BRANCH + rm -rf "branch-$BRANCH" + mkdir "branch-$BRANCH" + (set -e + cd "branch-$BRANCH" + git init -b branch1 + git pull --depth=1 .. "$BRANCH" + rm -rf .git + git add -f . + ) + # update root-dir with branch-beta + if [ "$BRANCH" = beta ] + then + git rm -rf .build + git checkout beta . + fi + # update README.md with branch-$BRANCH and $GITHUB_REPOSITORY + sed -i \ + -e "s|/branch-[0-9A-Z_a-z]*/|/branch-$BRANCH/|g" \ + -e "s|\bjslint-org/jslint\b|$GITHUB_REPOSITORY|g" \ + -e "s|\bjslint-org\.github\.io/jslint\b|$( + printf "$GITHUB_REPOSITORY" | sed -e "s|/|.github.io/|" + )|g" \ + "branch-$BRANCH/README.md" + git status + git commit -am "update dir branch-$BRANCH" || true + # if branch-gh-pages has more than 100 commits, + # then backup and squash commits + if [ "$(git rev-list --count gh-pages)" -gt 100 ] + then + # backup + shGitCmdWithGithubToken push origin -f gh-pages:gh-pages-backup + # squash commits + git checkout --orphan squash1 + git commit --quiet -am squash || true + # reset branch-gh-pages to squashed-commit + git push . -f squash1:gh-pages + git checkout gh-pages + # force-push squashed-commit + shGitCmdWithGithubToken push origin -f gh-pages + fi + # list files + shGitLsTree + # push branch-gh-pages + shGitCmdWithGithubToken push origin gh-pages + # validate http-links + (set -e + cd "branch-$BRANCH" + sleep 15 + shDirHttplinkValidate + ) +)} + +shCiBase() {(set -e +# this function will run base-ci + # create jslint.cjs + cp jslint.mjs jslint.js + cat jslint.mjs | sed \ + -e "s|^// module.exports = |module.exports = |" \ + -e "s|^export default Object.freeze(|// &|" \ + -e "s|^jslint_import_meta_url = |// &|" \ + > jslint.cjs + # run test with coverage-report + # coverage-hack - test jslint's invalid-file handling-behavior + mkdir -p .test-dir.js + # test jslint's cli handling-behavior + printf "node jslint.cjs .\n" + node jslint.cjs . + printf "node jslint.mjs .\n" + node jslint.mjs . + printf "node test.mjs\n" + (set -e + # coverage-hack - test jslint's cli handling-behavior + export JSLINT_BETA=1 + shRunWithCoverage node test.mjs + ) + # update version from CHANGELOG.md + node --input-type=module -e ' +import moduleFs from "fs"; +(async function () { + +// Update edition in README.md, jslint.mjs from CHANGELOG.md + + var dict; + var versionBeta; + var versionMaster; + dict = {}; + await Promise.all([ + "CHANGELOG.md", + "README.md", + "jslint.mjs" + ].map(async function (file) { + dict[file] = await moduleFs.promises.readFile(file, "utf8"); + })); + Array.from(dict["CHANGELOG.md"].matchAll( + /\n\n#\u0020(v\d\d\d\d\.\d\d?\.\d\d?(.*?)?)\n/g + )).slice(0, 2).forEach(function ([ + ignore, version, isBeta + ]) { + versionBeta = versionBeta || version; + versionMaster = versionMaster || (!isBeta && version); + }); + [ + { + file: "README.md", + src: dict["README.md"].replace(( + /\bv\d\d\d\d\.\d\d?\.\d\d?\b/m + ), versionMaster), + src0: dict["README.md"] + }, { + file: "jslint.mjs", + src: dict["jslint.mjs"].replace(( + /^let\u0020jslint_edition\u0020=\u0020".*?";$/m + ), `let jslint_edition = "${versionBeta}";`), + src0: dict["jslint.mjs"] + } + ].forEach(function ({ + file, + src, + src0 + }) { + if (src !== src0) { + console.error(`update file ${file}`); + moduleFs.promises.writeFile(file, src); + } + }); +}()); +' # ' +)} + +shCiBranchPromote() {(set -e +# this function will promote branch $REMOTE/$BRANCH1 to branch $REMOTE/$BRANCH2 + local BRANCH1 + local BRANCH2 + local REMOTE + REMOTE="$1" + shift + BRANCH1="$1" + shift + BRANCH2="$1" + shift + git fetch "$REMOTE" "$BRANCH1" + git push "$REMOTE" "$REMOTE/$BRANCH1:$BRANCH2" "$@" +)} + +shDirHttplinkValidate() {(set -e +# this function will validate http-links embedded in .html and .md files + node --input-type=module -e ' +import moduleFs from "fs"; +import moduleHttps from "https"; +import moduleUrl from "url"; +(async function () { + var dict = {}; + Array.from( + await moduleFs.promises.readdir(".") + ).forEach(async function (file) { + var data; + if (!( + /.\.html$|.\.md$/m + ).test(file)) { + return; + } + data = await moduleFs.promises.readFile(file, "utf8"); + data.replace(( + /\bhttps?:\/\/.*?(?:[\s")\]]|\W?$)/gm + ), function (url) { + var req; + url = url.slice(0, -1).replace(( + /[\u0022\u0027]/g + ), "").replace(( + /\/branch-\w+?\//g + ), "/branch-alpha/").replace(( + /\bjslint-org\/jslint\b/g + ), process.env.GITHUB_REPOSITORY || "jslint-org/jslint").replace(( + /\bjslint-org\.github\.io\/jslint\b/g + ), String( + process.env.GITHUB_REPOSITORY || "jslint-org/jslint" + ).replace("/", ".github.io/")); + if (url.startsWith("http://")) { + throw new Error("shDirHttplinkValidate - insecure link " + url); + } + // ignore duplicate-link + if (dict.hasOwnProperty(url)) { + return ""; + } + dict[url] = true; + req = moduleHttps.request(moduleUrl.parse( + url + ), function (res) { + console.error( + "shDirHttplinkValidate " + res.statusCode + " " + url + ); + if (!(res.statusCode < 400)) { + throw new Error( + "shDirHttplinkValidate - " + file + + " - unreachable link " + url + ); + } + req.abort(); + res.destroy(); + }); + req.setTimeout(30000); + req.end(); + return ""; + }); + data.replace(( + /(\bhref=|\bsrc=|\burl\(|\[[^]*?\]\()("?.*?)(?:[")\]]|$)/gm + ), function (ignore, linkType, url) { + if (!linkType.startsWith("[")) { + url = url.slice(1); + } + if (url.length === 0 || url.startsWith("data:")) { + return; + } + // ignore duplicate-link + if (dict.hasOwnProperty(url)) { + return ""; + } + dict[url] = true; + if (!( + /^https?|^mailto:|^[#\/]/m + ).test(url)) { + moduleFs.stat(url.split("?")[0], function (ignore, exists) { + console.error( + "shDirHttplinkValidate " + Boolean(exists) + " " + url + ); + if (!exists) { + throw new Error( + "shDirHttplinkValidate - " + file + + " - unreachable link " + url + ); + } + }); + } + return ""; + }); + }); +}()); +' # "' +)} + +shGitCmdWithGithubToken() {(set -e +# this function will run git $CMD with $GITHUB_TOKEN + local CMD + local EXIT_CODE + local REMOTE + local URL + printf "shGitCmdWithGithubToken $*\n" + CMD="$1" + shift + REMOTE="$1" + shift + URL="$( + git config "remote.$REMOTE.url" \ + | sed -e "s|https://|https://x-access-token:$GITHUB_TOKEN@|" + )" + EXIT_CODE=0 + # hide $GITHUB_TOKEN in case of err + git "$CMD" "$URL" "$@" 2>/dev/null || EXIT_CODE="$?" + printf "shGitCmdWithGithubToken - EXIT_CODE=$EXIT_CODE\n" 1>&2 + return "$EXIT_CODE" +)} + +shGitLsTree() {(set -e +# this function will "git ls-tree" all files committed in HEAD +# example use: +# shGitLsTree | sort -rk3 # sort by date +# shGitLsTree | sort -rk4 # sort by size + node --input-type=module -e ' +import moduleChildProcess from "child_process"; +(async function () { + var result; + // get file, mode, size + result = await new Promise(function (resolve) { + result = ""; + moduleChildProcess.spawn("git", [ + "ls-tree", "-lr", "HEAD" + ], { + encoding: "utf8", + stdio: [ + "ignore", "pipe", 2 + ] + }).on("exit", function () { + resolve(result); + }).stdout.on("data", function (chunk) { + result += chunk; + }).setEncoding("utf8"); + }); + result = Array.from(result.matchAll( + /^(\S+?)\u0020+?\S+?\u0020+?\S+?\u0020+?(\S+?)\t(\S+?)$/gm + )).map(function ([ + ignore, mode, size, file + ]) { + return { + file, + mode: mode.slice(-3), + size: Number(size) + }; + }); + result = result.sort(function (aa, bb) { + return aa.file > bb.file || -1; + }); + result = result.slice(0, 1000); + result.unshift({ + file: ".", + mode: "755", + size: 0 + }); + // get date + result.forEach(function (elem) { + result[0].size += elem.size; + moduleChildProcess.spawn("git", [ + "log", "--max-count=1", "--format=%at", elem.file + ], { + stdio: [ + "ignore", "pipe", 2 + ] + }).stdout.on("data", function (chunk) { + elem.date = new Date( + Number(chunk) * 1000 + ).toISOString().slice(0, 19) + "Z"; + }); + }); + process.on("exit", function () { + var iiPad; + var sizePad; + iiPad = String(result.length).length + 1; + sizePad = String(Math.ceil(result[0].size / 1024)).length; + process.stdout.write(result.map(function (elem, ii) { + return ( + String(ii + ".").padStart(iiPad, " ") + + " " + elem.mode + + " " + elem.date + + " " + String( + Math.ceil(elem.size / 1024) + ).padStart(sizePad, " ") + " KB" + + " " + elem.file + + "\n" + ); + }).join("")); + }); +}()); +' # "' +)} + +shHttpFileServer() {(set -e +# this function will run simple node http-file-server on port $PORT + if [ ! "$npm_config_mode_auto_restart" ] + then + local EXIT_CODE + EXIT_CODE=0 + export npm_config_mode_auto_restart=1 + while true + do + printf "\n" + git diff --color 2>/dev/null | cat || true + printf "\nshHttpFileServer - (re)starting $*\n" + (shHttpFileServer "$@") || EXIT_CODE="$?" + printf "process exited with code $EXIT_CODE\n" + # if $EXIT_CODE != 77, then exit process + # http://en.wikipedia.org/wiki/Unix_signal + if [ "$EXIT_CODE" != 77 ] + then + break + fi + # else restart process after 1 second + sleep 1 + done + return + fi + node --input-type=module -e ' +import moduleChildProcess from "child_process"; +import moduleFs from "fs"; +import moduleHttp from "http"; +import modulePath from "path"; +import moduleRepl from "repl"; +import moduleUrl from "url"; +// init debugInline +(function () { + var consoleError = console.error; + globalThis.debugInline = globalThis.debugInline || function (...argList) { + +// this function will both print to stderr and return [0] + + consoleError("\n\ndebugInline"); + consoleError(...argList); + consoleError("\n"); + return argList[0]; + }; +}()); +(async function httpFileServer() { +/* + * this function will start http-file-server + */ + var contentTypeDict = { + ".bmp": "image/bmp", + ".cjs": "application/javascript; charset=utf-8", + ".css": "text/css; charset=utf-8", + ".gif": "image/gif", + ".htm": "text/html; charset=utf-8", + ".html": "text/html; charset=utf-8", + ".jpe": "image/jpeg", + ".jpeg": "image/jpeg", + ".jpg": "image/jpeg", + ".js": "application/javascript; charset=utf-8", + ".json": "application/json; charset=utf-8", + ".md": "text/markdown; charset=utf-8", + ".mjs": "application/javascript; charset=utf-8", + ".pdf": "application/pdf", + ".png": "image/png", + ".svg": "image/svg+xml; charset=utf-8", + ".txt": "text/plain; charset=utf-8", + ".wasm": "application/wasm", + ".woff": "font/woff", + ".woff2": "font/woff2", + ".xml": "application/xml; charset=utf-8", + "/": "text/html; charset=utf-8" + }; + if (process.argv[1]) { + await import("file://" + modulePath.resolve(process.argv[1])); + } + process.env.PORT = process.env.PORT || "8080"; + console.error("http-file-server listening on port " + process.env.PORT); + moduleHttp.createServer(function (req, res) { + var file; + var pathname; + var timeStart; + // init timeStart + timeStart = Date.now(); + // init pathname + pathname = moduleUrl.parse(req.url).pathname; + // debug - serverLog + res.on("close", function () { + if (pathname === "/favicon.ico") { + return; + } + console.error( + "serverLog - " + + new Date(timeStart).toISOString() + " - " + + (Date.now() - timeStart) + "ms - " + + (res.statusCode || 0) + " " + req.method + " " + pathname + ); + }); + // debug - echo request + if (pathname === "/echo") { + res.write(JSON.stringify(req.headers, undefined, 4) + "\n"); + req.pipe(res); + return; + } + // replace trailing "/" with "/index.html" + file = pathname.slice(1).replace(( + /\/$/ + ), "/index.html"); + // resolve file + file = modulePath.resolve(file); + // security - disable parent-directory lookup + if (!file.startsWith(process.cwd() + modulePath.sep)) { + res.statusCode = 404; + res.end(); + return; + } + moduleFs.readFile(file, function (err, data) { + var contentType; + if (err) { + res.statusCode = 404; + res.end(); + return; + } + contentType = contentTypeDict[( + /^\/$|\.[^.]*?$|$/m + ).exec(file)[0]]; + if (contentType) { + res.setHeader("content-type", contentType); + } + res.end(data); + }); + }).listen(process.env.PORT); +}()); +(function jslintDir() { +/* + * this function will jslint current-directory + */ + moduleFs.stat(( + process.env.HOME + "/jslint.mjs" + ), function (ignore, exists) { + if (exists) { + moduleChildProcess.spawn("node", [ + process.env.HOME + "/jslint.mjs", "." + ], { + stdio: [ + "ignore", 1, 2 + ] + }); + } + }); +}()); +(function replStart() { +/* + * this function will start repl-debugger + */ + var that; + // start repl + that = moduleRepl.start({ + useGlobal: true + }); + // init history + that.setupHistory(modulePath.resolve( + process.env.HOME + "/.node_repl_history" + ), function () { + return; + }); + // save eval-function + that.evalDefault = that.eval; + // hook custom-eval-function + that.eval = function (script, context, file, onError) { + script.replace(( + /^(\S+)\u0020(.*?)\n/ + ), function (ignore, match1, match2) { + switch (match1) { + // syntax-sugar - run shell-cmd + case "$": + switch (match2.split(" ").slice(0, 2).join(" ")) { + // syntax-sugar - run git diff + case "git diff": + match2 += " --color"; + break; + // syntax-sugar - run git log + case "git log": + match2 += " -n 10"; + break; + // syntax-sugar - run ll + case "ll": + match2 = "ls -Fal"; + break; + } + match2 = match2.replace(( + /^git\u0020/ + ), "git --no-pager "); + // run shell-cmd + console.error("$ " + match2); + moduleChildProcess.spawn(match2, { + shell: true, + stdio: [ + "ignore", 1, 2 + ] + // print exitCode + }).on("exit", function (exitCode) { + console.error("$ EXIT_CODE=" + exitCode); + that.evalDefault("\n", context, file, onError); + }); + script = "\n"; + break; + // syntax-sugar - map text with charCodeAt + case "charCode": + console.error( + match2.split("").map(function (chr) { + return ( + "\\u" + + chr.charCodeAt(0).toString(16).padStart(4, 0) + ); + }).join("") + ); + script = "\n"; + break; + // syntax-sugar - sort chr + case "charSort": + console.error(JSON.stringify(match2.split("").sort().join(""))); + script = "\n"; + break; + // syntax-sugar - list obj-keys, sorted by item-type + // console.error(Object.keys(global).map(function(key){return(typeof global[key]===\u0027object\u0027&&global[key]&&global[key]===global[key]?\u0027global\u0027:typeof global[key])+\u0027 \u0027+key;}).sort().join(\u0027\n\u0027)) //jslint-quiet + case "keys": + script = ( + "console.error(Object.keys(" + match2 + + ").map(function(key){return(" + + "typeof " + match2 + "[key]===\u0027object\u0027&&" + + match2 + "[key]&&" + + match2 + "[key]===global[key]" + + "?\u0027global\u0027" + + ":typeof " + match2 + "[key]" + + ")+\u0027 \u0027+key;" + + "}).sort().join(\u0027\\n\u0027))\n" + ); + break; + // syntax-sugar - print String(val) + case "print": + script = "console.error(String(" + match2 + "))\n"; + break; + } + }); + // eval script + that.evalDefault(script, context, file, onError); + }; +}()); +(function watchDir() { +/* + * this function will watch current-directory for changes + */ + moduleFs.readdir(".", function (ignore, fileList) { + fileList.forEach(function (file) { + if (file[0] === ".") { + return; + } + moduleFs.stat(file, function (ignore, stats) { + if (!(stats && stats.isFile())) { + return; + } + moduleFs.watchFile(file, { + interval: 1000, + persistent: false + }, function () { + console.error("watchFile - modified - " + file); + setTimeout(process.exit.bind(undefined, 77), 1000); + }); + }); + }); + }); +}()); +' "$@" # ' +)} + +shImageJslintCreate() {(set -e +# this function will create .png logo of jslint + echo ' + + + +logo + + + +
    +
    JS
    +
    Lint
    +
    + + +' > .build/asset-image-jslint-512.html + cp asset-font-daley-bold.woff2 .build + # screenshot asset-image-jslint-512.png + shBrowserScreenshot .build/asset-image-jslint-512.html \ + --window-size=512x512 \ + -screenshot=.build/asset-image-jslint-512.png + # create various smaller thumbnails + for SIZE in 32 64 128 256 + do + convert -resize "${SIZE}x${SIZE}" .build/asset-image-jslint-512.png \ + ".build/asset-image-jslint-$SIZE.png" + printf \ +"shImageJslintCreate - wrote - .build/asset-image-jslint-$SIZE.png\n" 1>&2 + done + # convert to svg @ https://convertio.co/png-svg/ +)} + +shImageToDataUri() {(set -e +# this function will convert image $1 to data-uri string + node --input-type=module -e ' +import moduleFs from "fs"; +import moduleHttps from "https"; +(async function () { + let file; + let result; + file = process.argv[1]; + if (( + /^https:\/\// + ).test(file)) { + result = await new Promise(function (resolve) { + moduleHttps.get(file, function (res) { + let chunkList; + chunkList = []; + res.on("data", function (chunk) { + chunkList.push(chunk); + }).on("end", function () { + resolve(Buffer.concat(chunkList)); + }); + }); + }); + } else { + result = await moduleFs.promises.readFile(file); + } + result = String( + "data:image/" + file.match( + /\.[^.]*?$|$/m + )[0].slice(1) + ";base64," + result.toString("base64") + ).replace(( + /.{72}/g + ), "$&\\\n"); + console.log(result); +}()); +' "$@" # ' +)} + +shJsonNormalize() {(set -e +# this function will +# 1. read json-data from file $1 +# 2. normalize json-data +# 3. write normalized json-data back to file $1 + node --input-type=module -e ' +import moduleFs from "fs"; +(async function () { + function identity(val) { + +// This function will return . + + return val; + } + function objectDeepCopyWithKeysSorted(obj) { + +// this function will recursively deep-copy with keys sorted + + var sorted; + if (typeof obj !== "object" || !obj) { + return obj; + } + +// recursively deep-copy list with child-keys sorted + + if (Array.isArray(obj)) { + return obj.map(objectDeepCopyWithKeysSorted); + } + +// recursively deep-copy obj with keys sorted + + sorted = {}; + Object.keys(obj).sort().forEach(function (key) { + sorted[key] = objectDeepCopyWithKeysSorted(obj[key]); + }); + return sorted; + } + console.error("shJsonNormalize - " + process.argv[1]); + moduleFs.promises.writeFile( + process.argv[1], + JSON.stringify( + objectDeepCopyWithKeysSorted( + JSON.parse( + identity( + await moduleFs.promises.readFile( + process.argv[1], + "utf8" + ) + ).replace(( + /^\ufeff/ + ), "") + ) + ), + undefined, + 4 + ) + "\n" + ); +}()); +' "$@" # ' +)} + +shRawLibFetch() {(set -e +# this function will fetch raw-lib from $1 + node --input-type=module -e ' +import moduleChildProcess from "child_process"; +import moduleFs from "fs"; +import moduleHttps from "https"; +import modulePath from "path"; +// init debugInline +(function () { + var consoleError = console.error; + globalThis.debugInline = globalThis.debugInline || function (...argList) { + +// this function will both print to stderr and return [0] + + consoleError("\n\ndebugInline"); + consoleError(...argList); + consoleError("\n"); + return argList[0]; + }; +}()); +(async function () { + var fetchList; + var matchObj; + var replaceList; + var repoDict; + function pipeToBuffer(res, dict, key) { + /* + * this function will concat data from to [] + */ + var data; + data = []; + res.on("data", function (chunk) { + data.push(chunk); + }).on("end", function () { + dict[key] = Buffer.concat(data); + }); + } + // init matchObj + matchObj = ( + /^\/\*jslint-disable\*\/\n\/\*\nshRawLibFetch\n(\{\n[\S\s]*?\n\})([\S\s]*?)\n\*\/\n/m + ).exec(await moduleFs.promises.readFile(process.argv[1], "utf8")); + // JSON.parse match1 with comment + fetchList = JSON.parse(matchObj[1]).fetchList; + replaceList = JSON.parse(matchObj[1]).replaceList || []; + // init repoDict, fetchList + repoDict = {}; + fetchList.forEach(function (elem) { + if (!elem.url) { + return; + } + elem.prefix = elem.url.split("/").slice(0, 7).join("/"); + // fetch dateCommitted + if (!repoDict.hasOwnProperty(elem.prefix)) { + repoDict[elem.prefix] = true; + moduleHttps.request(elem.prefix.replace( + "/blob/", + "/commits/" + ), function (res) { + pipeToBuffer(res, elem, "dateCommitted"); + }).end(); + } + // fetch file + if (elem.node) { + pipeToBuffer(moduleChildProcess.spawn("node", [ + "-e", elem.node + ], { + stdio: [ + "ignore", "pipe", 2 + ] + }).stdout, elem, "data"); + return; + } + if (elem.sh) { + pipeToBuffer(moduleChildProcess.spawn(elem.sh, { + shell: true, + stdio: [ + "ignore", "pipe", 2 + ] + }).stdout, elem, "data"); + return; + } + moduleHttps.get(elem.url2 || elem.url.replace( + "https://github.com/", + "https://raw.githubusercontent.com/" + ).replace("/blob/", "/"), function (res) { + // http-redirect + if (res.statusCode === 302) { + moduleHttps.get(res.headers.location, function (res) { + pipeToBuffer(res, elem, "data"); + }); + return; + } + pipeToBuffer(res, elem, "data"); + }); + }); + // parse fetched data + process.on("exit", function () { + var header; + var result; + var result0; + result = ""; + fetchList.forEach(function (elem, ii, list) { + var prefix; + if (!elem.url) { + return; + } + // init prefix + prefix = "exports_" + modulePath.dirname(elem.url).replace( + "https://github.com/", + "" + ).replace(( + /\/blob\/[^\/]*/ + ), "/").replace(( + /\W/g + ), "_").replace(( + /(_)_+|_+$/g + ), "$1"); + list[ii].exports = prefix + "_" + modulePath.basename( + elem.url + ).replace(( + /\.js$/ + ), "").replace(( + /\W/g + ), "_"); + if (elem.dataUriType) { + return; + } + if (elem.dateCommitted) { + result += ( + "\n\n\n/*\n" + + "repo " + elem.prefix.replace("/blob/", "/tree/") + "\n" + + "committed " + ( + /\b\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ\b/ + ).exec(elem.dateCommitted.toString())[0] + "\n" + + "*/" + ); + } + // mangle module.exports + result += ( + "\n\n\n/*\nfile " + elem.url + "\n*/\n" + + elem.data.toString().trim() + ); + }); + result = ( + "\n" + result.trim() + + "\n\n\n/*\nfile none\n*/\n/*jslint-enable*/\n" + ); + // comment #! + result = result.replace(( + /^#!/gm + ), "// $&"); + // normalize newline + result = result.replace(( + /\r\n|\r/g + ), "\n"); + // remove trailing-whitespace + result = result.replace(( + /[\t\u0020]+$/gm + ), ""); + // remove leading-newline before ket + result = result.replace(( + /\n+?(\n\u0020*?\})/g + ), "$1"); + // eslint - no-multiple-empty-lines + // https://github.com/eslint/eslint/blob/v7.2.0/docs/rules/no-multiple-empty-lines.md //jslint-quiet + result = result.replace(( + /\n{4,}/g + ), "\n\n\n"); + // replace from replaceList + replaceList.forEach(function ({ + aa, + bb, + flags + }) { + result0 = result; + result = result.replace(new RegExp(aa, flags), bb); + if (result0 === result) { + throw new Error( + "shRawLibFetch - cannot find-and-replace snippet " + + JSON.stringify(aa) + ); + } + }); + // init header + header = ( + matchObj.input.slice(0, matchObj.index) + + "/*jslint-disable*/\n/*\nshRawLibFetch\n" + + JSON.stringify(JSON.parse(matchObj[1]), undefined, 4) + "\n" + + matchObj[2].split("\n\n").filter(function (elem) { + return elem.trim(); + }).map(function (elem) { + return elem.trim().replace(( + /\*\//g + ), "*\\\\/").replace(( + /\/\*/g + ), "/\\\\*") + "\n"; + }).sort().join("\n") + "*/\n\n" + ); + // replace from header-diff + header.replace(( + /((?:^-.*?\n)+?)((?:^\+.*?\n)+)/gm + ), function (ignore, aa, bb) { + aa = "\n" + aa.replace(( + /^-/gm + ), "").replace(( + /\*\\\\\//g + ), "*/").replace(( + /\/\\\\\*/g + ), "/*"); + bb = "\n" + bb.replace(( + /^\+/gm + ), "").replace(( + /\*\\\\\//g + ), "*/").replace(( + /\/\\\\\*/g + ), "/*"); + result0 = result; + // disable $-escape in replacement-string + result = result.replace(aa, function () { + return bb; + }); + if (result0 === result) { + throw new Error( + "shRawLibFetch - cannot find-and-replace snippet " + + JSON.stringify(aa) + ); + } + return ""; + }); + // inline dataUri + fetchList.forEach(function ({ + data, + dataUriType, + exports + }) { + if (!dataUriType) { + return; + } + data = ( + "data:" + dataUriType + ";base64," + + data.toString("base64") + ); + result0 = result; + result = result.replace( + new RegExp("^" + exports + "$", "gm"), + // disable $-escape in replacement-string + function () { + return data; + } + ); + if (result0 === result) { + throw new Error( + "shRawLibFetch - cannot find-and-replace snippet " + + JSON.stringify(exports) + ); + } + }); + // init footer + result = header + result; + matchObj.input.replace(( + /\n\/\*\nfile\u0020none\n\*\/\n\/\*jslint-enable\*\/\n([\S\s]+)/ + ), function (ignore, match1) { + result += "\n\n" + match1.trim() + "\n"; + }); + // write to file + moduleFs.writeFileSync(process.argv[1], result); //jslint-quiet + }); +}()); +' "$@" # ' + git diff 2>/dev/null || true +)} + +shRunWithCoverage() {(set -e +# this function will run nodejs command $@ with v8-coverage +# and create coverage-report .build/coverage/index.html + local EXIT_CODE + EXIT_CODE=0 + export DIR_COVERAGE=.build/coverage/ + rm -rf "$DIR_COVERAGE" + (set -e + export NODE_V8_COVERAGE="$DIR_COVERAGE" + "$@" + ) || EXIT_CODE="$?" + if [ "$EXIT_CODE" = 0 ] + then + node --input-type=module -e ' +import moduleFs from "fs"; +import modulePath from "path"; +// init debugInline +(function () { + var consoleError = console.error; + globalThis.debugInline = globalThis.debugInline || function (...argList) { + +// this function will both print to stderr and return [0] + + consoleError("\n\ndebugInline"); + consoleError(...argList); + consoleError("\n"); + return argList[0]; + }; +}()); +(async function () { + var DIR_COVERAGE = process.env.DIR_COVERAGE; + var cwd; + var data; + var fileDict; + async function htmlRender({ + fileList, + lineList, + pathname + }) { + var html; + var padLines; + var padPathname; + var txt; + var txtBorder; + function stringHtmlSafe(str) { + /* + * this function will make html-safe + * https://stackoverflow.com/questions/7381974/which-characters-need-to-be-escaped-on-html //jslint-quiet + */ + return str.replace(( + /&/gu + ), "&").replace(( + /"/gu + ), """).replace(( + /\u0027/gu + ), "'").replace(( + //gu + ), ">").replace(( + /&(amp;|apos;|gt;|lt;|quot;)/igu + ), "&$1"); + } + html = ""; + html += ` + + +coverage-report + + + +
    +
    coverage-report
    + + + + + + + +`; + if (!lineList) { + padLines = String("100.00 %").length; + padPathname = 32; + fileList.unshift({ + linesCovered: 0, + linesTotal: 0, + pathname: "./" + }); + fileList.slice(1).forEach(function ({ + linesCovered, + linesTotal, + pathname + }) { + fileList[0].linesCovered += linesCovered; + fileList[0].linesTotal += linesTotal; + padPathname = Math.max(padPathname, pathname.length + 2); + padLines = Math.max( + padLines, + String(linesCovered + " / " + linesTotal).length + ); + }); + } + txtBorder = ( + "+" + "-".repeat(padPathname + 2) + "+" + + "-".repeat(padLines + 2) + "+\n" + ); + txt = ""; + txt += "coverage-report\n"; + txt += txtBorder; + txt += ( + "| " + String("files covered").padEnd(padPathname, " ") + " | " + + String("lines").padStart(padLines, " ") + " |\n" + ); + txt += txtBorder; + fileList.forEach(function ({ + linesCovered, + linesTotal, + pathname + }, ii) { + var coverageLevel; + var coveragePct; + var fill; + var str1; + var str2; + var xx1; + var xx2; + coveragePct = Math.floor(10000 * linesCovered / linesTotal || 0); + coverageLevel = ( + coveragePct >= 8000 + ? "coverageHigh" + : coveragePct >= 5000 + ? "coverageMedium" + : "coverageLow" + ); + coveragePct = String(coveragePct).replace(( + /..$/m + ), ".$&"); + if (!lineList && ii === 0) { + fill = ( + // red + "#" + Math.round( + (100 - Number(coveragePct)) * 2.21 + ).toString(16).padStart(2, "0") + // green + + Math.round( + Number(coveragePct) * 2.21 + ).toString(16).padStart(2, "0") + + // blue + "00" + ); + str1 = "coverage"; + str2 = coveragePct + " %"; + xx1 = 6 * str1.length + 20; + xx2 = 6 * str2.length + 20; + // fs - write coverage-badge.svg + moduleFs.promises.writeFile(( + DIR_COVERAGE + "/coverage-badge.svg" + ), String(` + + + + +${str1} +${str2} + + + `).trim() + "\n"); + pathname = ""; + } + txt += ( + "| " + + String("./" + pathname).padEnd(padPathname, " ") + " | " + + String(coveragePct + " %").padStart(padLines, " ") + " |\n" + ); + txt += ( + "| " + "*".repeat( + Math.round(0.01 * coveragePct * padPathname) + ).padEnd(padPathname, "_") + " | " + + String( + linesCovered + " / " + linesTotal + ).padStart(padLines, " ") + " |\n" + ); + txt += txtBorder; + pathname = stringHtmlSafe(pathname); + html += ` + + +`; + }); + if (lineList) { + html += ` +
    files coveredlines
    + ${( + lineList + ? ( + "./ " + + pathname + "
    " + ) + : ( + "./ " + + pathname + "
    " + ) + )} +
    +
    +
    +
    + ${coveragePct} %
    + ${linesCovered} / ${linesTotal} +
    +
    +
    +`; + lineList.forEach(function ({ + count, + holeList, + line, + startOffset + }, ii) { + var chunk; + var inHole; + var lineHtml; + var lineId; + lineHtml = ""; + lineId = "line_" + (ii + 1); + switch (count) { + case -1: + case 0: + if (holeList.length === 0) { + lineHtml += ""; + lineHtml += ""; + lineHtml += stringHtmlSafe(line); + break; + } + line = line.split("").map(function (chr) { + return { + chr, + isHole: undefined + }; + }); + holeList.forEach(function ([ + aa, bb + ]) { + aa = Math.max(aa - startOffset, 0); + bb = Math.min(bb - startOffset, line.length); + while (aa < bb) { + line[aa].isHole = true; + aa += 1; + } + }); + chunk = ""; + line.forEach(function ({ + chr, + isHole + }) { + if (inHole !== isHole) { + lineHtml += stringHtmlSafe(chunk); + lineHtml += ( + isHole + ? "" + : "" + ); + chunk = ""; + inHole = isHole; + } + chunk += chr; + }); + lineHtml += stringHtmlSafe(chunk); + break; + default: + lineHtml += stringHtmlSafe(line); + } + html += String(` +
    +
    +${String(ii + 1).padStart(5, " ")}.
    +
    +
    +${String(count).padStart(7, " ")}
    +
    +${lineHtml}
    +
    + `).replace(( + /\n/g + ), "").trim() + "\n"; + }); + } + html += ` +
    +
    +
    + +`; + html += "\n"; + await moduleFs.promises.mkdir(modulePath.dirname(pathname), { + recursive: true + }); + // fs - write *.html + moduleFs.promises.writeFile(pathname + ".html", html); + if (lineList) { + return; + } + // fs - write coverage.txt + console.error("\n" + txt); + moduleFs.promises.writeFile(( + DIR_COVERAGE + "/coverage-report.txt" + ), txt); + } + data = await moduleFs.promises.readdir(DIR_COVERAGE); + await Promise.all(data.map(async function (file) { + if (( + /^coverage-.*?\.json$/ + ).test(file)) { + data = await moduleFs.promises.readFile(( + DIR_COVERAGE + file + ), "utf8"); + // fs - rename to coverage-v8.json + moduleFs.promises.rename( + DIR_COVERAGE + file, + DIR_COVERAGE + "coverage-v8.json" + ); + } + })); + fileDict = {}; + cwd = process.cwd().replace(( + /\\/g + ), "/") + "/"; + await Promise.all(JSON.parse(data).result.map(async function ({ + functions, + url + }) { + var lineList; + var linesCovered; + var linesTotal; + var pathname; + var src; + if (!url.startsWith("file:///")) { + return; + } + pathname = url.replace(( + process.platform === "win32" + ? "file:///" + : "file://" + ), "").replace(( + /\\\\/g + ), "/"); + if ( + !pathname.startsWith(cwd) + || pathname.startsWith(cwd + "[") + || ( + process.env.npm_config_mode_coverage !== "all" + && pathname.indexOf("/node_modules/") >= 0 + ) + ) { + return; + } + pathname = pathname.replace(cwd, ""); + src = await moduleFs.promises.readFile(pathname, "utf8"); + lineList = [{}]; + src.replace(( + /^.*$/gm + ), function (line, startOffset) { + lineList[lineList.length - 1].endOffset = startOffset - 1; + lineList.push({ + count: -1, + endOffset: 0, + holeList: [], + line, + startOffset + }); + return ""; + }); + lineList.shift(); + lineList[lineList.length - 1].endOffset = src.length; + functions.reverse().forEach(function ({ + ranges + }) { + ranges.reverse().forEach(function ({ + count, + endOffset, + startOffset + }, ii, list) { + lineList.forEach(function (elem) { + if (!( + ( + elem.startOffset <= startOffset + && startOffset <= elem.endOffset + ) || ( + elem.startOffset <= endOffset + && endOffset <= elem.endOffset + ) || ( + startOffset <= elem.startOffset + && elem.endOffset <= endOffset + ) + )) { + return; + } + // handle root-range + if (ii + 1 === list.length) { + if (elem.count === -1) { + elem.count = count; + } + return; + } + // handle non-root-range + if (elem.count !== 0) { + elem.count = Math.max(count, elem.count); + } + if (count === 0) { + elem.count = 0; + elem.holeList.push([ + startOffset, endOffset + ]); + } + }); + }); + }); + linesTotal = lineList.length; + linesCovered = lineList.filter(function ({ + count + }) { + return count > 0; + }).length; + await moduleFs.promises.mkdir(( + modulePath.dirname(DIR_COVERAGE + pathname) + ), { + recursive: true + }); + await htmlRender({ + fileList: [ + { + linesCovered, + linesTotal, + pathname + } + ], + lineList, + pathname: DIR_COVERAGE + pathname + }); + fileDict[pathname] = { + lineList, + linesCovered, + linesTotal, + pathname, + src + }; + })); + await htmlRender({ + fileList: Object.keys(fileDict).sort().map(function (pathname) { + return fileDict[pathname]; + }), + pathname: DIR_COVERAGE + "index" + }); +}()); +' # "' + find "$DIR_COVERAGE" + fi + printf "shRunWithCoverage - EXIT_CODE=$EXIT_CODE\n" 1>&2 + return "$EXIT_CODE" +)} + +shRunWithScreenshotTxt() {(set -e +# this function will run cmd $@ and screenshot text-output +# https://www.cnx-software.com/2011/09/22/how-to-convert-a-command-line-result-into-an-image-in-linux/ + local EXIT_CODE + EXIT_CODE=0 + export SCREENSHOT_SVG="$1" + shift + printf "0\n" > "$SCREENSHOT_SVG.exit_code" + printf "shRunWithScreenshotTxt - ($* 2>&1)\n" 1>&2 + # run "$@" with screenshot + (set -e + "$@" 2>&1 || printf "$?\n" > "$SCREENSHOT_SVG.exit_code" + ) | tee "$SCREENSHOT_SVG.txt" + EXIT_CODE="$(cat "$SCREENSHOT_SVG.exit_code")" + printf "shRunWithScreenshotTxt - EXIT_CODE=$EXIT_CODE\n" 1>&2 + # format text-output + node --input-type=module -e ' +import moduleFs from "fs"; +(async function () { + var result = await moduleFs.promises.readFile( + process.argv[1] + ".txt", + "utf8" + ); + var yy = 10; + // remove ansi escape-code + result = result.replace(( + /\u001b.*?m/g + ), ""); + /* + // format unicode + result = result.replace(( + /\\u[0-9a-f]{4}/g + ), function (match0) { + return String.fromCharCode("0x" + match0.slice(-4)); + }); + */ + // normalize "\r\n" + result = result.replace(( + /\r\n?/ + ), "\n").trimEnd(); + // 96-column wordwrap + result = result.split("\n").map(function (line) { + var wordwrap = line.slice(0, 96).padEnd(96, " "); + line = line.slice(96); + while (line) { + wordwrap += "\\\n " + line.slice(0, 96 - 2).padEnd(96 - 2, " "); + line = line.slice(96 - 2); + } + return wordwrap + " "; + }).join("\n"); + // html-escape + result = result.replace(( + /&/g + ), "&").replace(( + //g + ), ">"); + // convert text to svg-tspan + result = result.split("\n").map(function (line) { + yy += 22; + return `${line}\n`; + }).join(""); + result = String(` + + + +${result} + + + `).trim() + "\n"; + moduleFs.promises.writeFile(process.argv[1], result); +}()); +' "$SCREENSHOT_SVG" # "' + printf "shRunWithScreenshotTxt - wrote - $SCREENSHOT_SVG\n" + # cleanup + rm "$SCREENSHOT_SVG.exit_code" "$SCREENSHOT_SVG.txt" + return "$EXIT_CODE" +)} + +# run $@ +(set -e + export NODE_OPTIONS="--unhandled-rejections=strict" + case "$(uname)" in + MSYS*) + if [ "$IS_WINPTY" ] || [ "$1" = shHttpFileServer ] + then + "$@" + else + export IS_WINPTY=1 + winpty -Xallow-non-tty -Xplain sh ci.sh "$@" + fi + ;; + *) + "$@" + ;; + esac +) diff --git a/folder-solid.svg b/folder-solid.svg new file mode 100644 index 000000000..0169c71bd --- /dev/null +++ b/folder-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/font-daley-bold.woff2 b/font-daley-bold.woff2 new file mode 100644 index 000000000..783bdd697 Binary files /dev/null and b/font-daley-bold.woff2 differ diff --git a/font-programma-bold.woff2 b/font-programma-bold.woff2 new file mode 100644 index 000000000..dfb5a9753 Binary files /dev/null and b/font-programma-bold.woff2 differ diff --git a/function.html b/function.html new file mode 100644 index 000000000..4f1016450 --- /dev/null +++ b/function.html @@ -0,0 +1,611 @@ + + + + + + + + +JSLint: jslint function + + + +
    JSLint
    + +
    The jslint Function
    +
    +

    JSLint

    +

    JSLint is delivered as a set of files:

    +
    + + + + + + + + + +
    FilenameContent
    function.htmlThis page that you are looking at right now.
    help.htmlUsing jslint as a single page JSLint application.
    index.htmlSingle page JSLint application that uses the + js files.
    jslint.mjsThe jslint function.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    +
    + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring"(JSLint)"
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    parenttokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    parenttokenThe function in which the variable was declared.variable
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    shebangstringThe first line of text if it started with #!line
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable may be assigned to.variable
    +

    Reports

    +

    The report object contains three functions that all take a + result from the jslint function as input. + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    +
    + +
    + + + +
    + + diff --git a/glyphicons_144_folder_open.png b/glyphicons_144_folder_open.png new file mode 100644 index 000000000..2834d378b Binary files /dev/null and b/glyphicons_144_folder_open.png differ diff --git a/help.html b/help.html new file mode 100644 index 000000000..c4ad563e5 --- /dev/null +++ b/help.html @@ -0,0 +1,1336 @@ + + + + + + + + + +JSLint: Help + + + +
    JSLint
    + +
    Help
    +
    +
    Il semble que la perfection soit atteinte non quand il n’y a + plus rien à ajouter, mais quand il n’y a plus rien à + retrancher.
    +
    + Antoine de Saint-Exupéry +
    +
    + Terre des Hommes (1939) +
    +

    Good Parts

    +

    The Principle of the Good Parts is

    +
    If a feature is sometimes useful and sometimes dangerous and if + there is a better option then always use the better option.
    +

    JSLint is a JavaScript program that looks for problems in + JavaScript programs. It is a code quality tool.

    +

    When C was + a young + programming language, there were several common programming errors that + were not caught by the primitive compilers, so an accessory program called + lint + was developed that would scan a source file, looking for problems.

    +

    As the language matured, the definition of the language was + strengthened to eliminate some insecurities, and compilers got better + at issuing warnings. lint is no longer needed.

    +

    JavaScript is a + young-for-its-age language. It was originally intended to do small tasks in + webpages, tasks for which Java was too heavy and clumsy. But JavaScript is + a surprisingly capable language, and it is now being used in larger + projects. Many of the features that were intended to make the language easy + to use are troublesome when projects become complicated. A lint + for JavaScript is needed: JSLint, a JavaScript syntax checker + and validator.

    +

    JSLint takes a JavaScript source and scans it. If it finds a + problem, it returns a message describing the problem and an approximate + location within the source. The problem is not necessarily a syntax error, + although it often is. JSLint looks at some style conventions + as well as structural problems. It does not prove that your program is + correct. It just provides another set of eyes to help spot problems.

    +

    JSLint defines a professional subset of JavaScript, a stricter + language than that defined by the ECMAScript Programming Language + Standard (the strangely named document that governs JavaScript). + JSLint will reject most legal programs. It is a higher + standard.

    +

    JavaScript is a sloppy language, but hidden deep inside there is an elegant, + better language. JSLint helps you to program in that better + language and to avoid most of the slop. JSLint will reject + programs that browsers will accept because JSLint is concerned + with the quality of your code and browsers are not. You should gladly accept + all of JSLint's advice.

    +

    JSLint can operate on JavaScript source + or JSON text.

    +

    ECMAScript Sixth Edition

    +

    Some of + ES6’s features are good, so JSLint will recognize the good + parts of ES6.

    +

    Currently, these features are recognized:

    +
      +
    • The ... ellipsis marker in parameter + lists and argument lists, replacing the arguments object + for variadic functions.
    • +
    • The let statement, which is like the var + statement except that it respects block scope. + You may use let or var but not both.
    • +
    • The const statement is like the let statement + except that it disallows the use of assignment on the variable, although + if the value of the variable is mutable, it can still be mutated. + const is preferred to let. +
    • Destructuring of arrays and objects is allowed in parameter lists and on + the left side of let, and const, but not + var or assignment statements, and not deep destructuring + or eliding.
    • +
    • Enhanced object literals, providing shorter forms for function + declaration and properties that are initialized by variables with the + same name.
    • +
    • The fat arrow => fart functions.
    • +
    • The simplest forms of import and export.
    • +
    • `Megastring` literals, but not nested + `megastring` literals.
    • +
    • New global functions, such as Map, Set, + WeakMap, and WeakSet.
    • +
    • 0b- and 0o- number literals.
    • +
    +

    The most important new feature of ES6 is proper tail calls. This has no + new syntax, so JSLint doesn’t see it. But it makes recursion + much more attractive, which makes loops, particularly for + loops, much less attractive.

    +

    import export

    +

    The ES6 module feature will be an important improvement over JavaScript’s + global variables as a means of linking separate files together. + JSLint recognizes a small but essential subset of the module + syntax.

    +
    +

    import name from stringliteral;

    +

    import {name} from stringliteral;

    +

    import(string).then(function);

    +

    export default function () {};

    +

    export default function name() {};

    +

    export default expression;

    +

    export function name() {}

    +

    export name; // where name is const

    +

    export {name};

    +
    +

    Directives

    +

    JSLint provides three directives that may be placed in a + file to manage JSLint’s behavior. Use of these directives is + optional. If they are used, they should be placed in a source file before + the first statement. They are written in the form of a comment, where the + directive name is placed immediately after the opening of the comment before + any whitespace. The three directives are global, + jslint, and property. Directives in a file are + stronger than options selected from the UI or passed with the option object.

    +

    /*global*/

    +

    The /*global*/ directive is used to specify a set of globals + (usually functions and objects containing functions) that are available to + this file. This was commonly used in browsers to link source files together + before ES6 modules appeared. Use of global variables is strongly + discouraged, but unfortunately web browsers require their use. The + /*global*/ directive can only be used when the + Assume a browser option is selected. +

    Each of the names listed will indicate a read-only global variable. The names + are separated by , comma. This directive + should not be used if import or export is used. + This directive inhibits warnings, but it does not declare the names in the + execution environment. +

    For example: +

    /*global +
    ADSAFE, report, jslint
    +*/
    +

    instructs JSLint to not give warnings about the global + variables ADsafe, report, and jslint. + However, if any of those names are expected to be supplied by other files + and those other files fail to do so, then execution errors will result. It + is usually better to use top-level variable declarations instead: +

        var ADSAFE;
    +    var report;
    +    var jslint; 
    +

    Using var in this way allows comparing a global variable to the + undefined value to determine whether it is has been used in the global context.

    +

    /*jslint*/

    +

    The /*jslint*/ directive allows for the control of several + options. These options can also be set from the + jslint.com user interface.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DescriptionoptionMeaning
    Enable experimental warnings.betatrue will enable the following additional warnings: +
      +
    • Warn if global variables are redefined.
    • +
    • Warn if const / let statements are not declared + at top of function or script, + similar to var statements.
    • +
    • Warn if const / let / var statements + are not declared in ascii-order.
    • +
    • Warn if named-functions + are not declared in ascii-order.
    • +
    +
    Allow bitwise operator.bitwisetrue if bitwise operators should be allowed. The + bitwise operators are rarely used in JavaScript programs, so it + is usually more likely that & is a mistyping of + && than that it indicates a bitwise + and. + JSLint will give warnings on the bitwise operators + unless this option is selected.
    Assume browser environment.browsertrue if the standard browser globals should be + predefined. This option will reject the use of + import and export. This option also + disallows the file form of the "use + strict" pragma. It does not supply + self; you will have to request that unnecessary + alias of the dreaded global object yourself. It adds the same + globals as this directive: +
    /*global +
    +AbortController, +Blob, +Event, +EventTarget, +FileReader, +FormData, +IntersectionObserver, +MessageChannel, +MessageEvent, +MessagePort, +MutationObserver, +TextDecoder, +TextEncoder, +URL, +URLSearchParams, +WebAssembly, +Worker, +XMLHttpRequest, +clearInterval, +clearTimeout, +document, +fetch, +importScripts, +indexedDb, +localStorage, +location, +navigator, +performance, +postMessage, +queueMicrotask, +sessionStorage, +setInterval, +setTimeout, +structuredClone, +window +
    + */
    +
    Allow conversion operator.converttrue if !!, + prefix, + and concatenation with "" are allowed. + The Boolean, Number, and + String functions are preferred
    Assume + CouchDB environment.couchtrue if + Couch DB + globals should be predefined. It adds the same globals as this + directive: +
    /*global +
    emit, getRow, isArray, log, provides, registerType, require, + send, start, sum, toJSON
    + */
    Assume in development.develtrue if browser globals that are useful in + development should be predefined, and if debugger + statements and TODO comments + should be allowed. It adds the + same globals as this directive: +
    /*global +
    alert, confirm, console, prompt
    + */
    Be sure to turn this option off before going + into production.
    Allow eval.evaltrue if eval should be allowed. In the + past, the eval function was the most misused + feature of the language.
    Allow complex fat-arrow.farttrue if complex fat-arrows are allowed.
    Allow for statement.fortrue if the for statement should be + allowed. It is almost always better to use the array methods + instead.
    Allow get and set.getsettrue if accessor properties are allowed in object + literals.
    Use 2-space indent.indent2true if using 2-space indent.
    Allow long lines.longtrue if a line can contain more than 80 characters. +
    Assume Node.js + environment.nodetrue if Node.js globals should be predefined. It + will predefine globals that are used in the Node.js environment. + It adds the same globals as this directive: +
    /*global +
    +AbortController, +Buffer, +Event, +EventTarget, +MessageChannel, +MessageEvent, +MessagePort, +TextDecoder, +TextEncoder, +URL, +URLSearchParams, +WebAssembly, +__dirname, +__filename, +clearImmediate, +clearInterval, +clearTimeout, +console, +exports, +global, +module, +performance, +process, +queueMicrotask, +require, +setImmediate, +setInterval, +setTimeout +
    + */
    Allow weird property name.nomen + true if weird property names like + $, _foo, fooSync, foo_ + are allowed. +
    Allow single quote strings.singletrue if 'single quote + should be allowed to enclose string literals.
    Allow identifier in subscript-notation.subscripttrue to allow identifier in + subscript-notation, e.g.: foo["bar"] = 1;. + This allows linting of scripts targeting Google Closure Compiler, + where subscript-notation is commonly used to prevent renaming of + properties.
    Allow this.
    thistrue if this should be allowed.
    Include jslint stack-trace in warnings.
    tracetrue is used by developers to debug JSLint.
    Allow unordered cases, params, properties, + and variables.unorderedtrue if objects and functions are allowed + to declare properties and params in non-ascii order.
    Allow messy whitespace.whitetrue if the whitespace rules should be ignored. +
    + +

    For example:

    +
    /*jslint
    +    bitwise, node
    +*/
    + +

    /*property*/

    +

    The /*property*/ directive is used to declare a list of + property identifiers that are used in the file. Each property name in the + program is looked up in this list. If a name is not found, that indicates an + error, most likely a typing error.

    +

    The list can also be used to evade some of JSLint’s naming + rules.

    +

    JSLint can build the /*property*/ list for you. At + the bottom of its report, JSLint displays a + /*property*/ directive. It contains all of the names that were + used with dot notation, subscript notation, and object literals to name the + properties of objects. You can look through the list for misspellings. You + can copy the /*property*/ directive to the top of your + script file. JSLint will check the spelling of all property + names against the list. That way, you can have JSLint look for + misspellings for you.

    +

    For example,

    +
    /*property
    +     charAt, slice, _$_
    + */
    +

    ignore

    +

    JSLint introduces a new reserved word: ignore. It + is used in parameter lists and in catch clauses to indicate a + parameter that will be ignored. Unused warnings will not be produced for + ignore. +

    function handler(ignore, value) {
    +    return do_something_useful(value);
    +}
    +

    var let const

    +

    JavaScript provides three statements for declaring variables: + var, let, and const. The + var statement suffers from a bad practice called hoisting. The + let statement does not do hoisting and respects block scope. + The const statement is like let except that it + marks the variable (but not its contents) as read only, making it an error + to attempt to assign to the variable. When given a choice, + const is the best, var is the worst.

    +

    JSLint uses the intersection of the var rules and the + let rules, and by doing so avoids the errors related to either. + A name should be declared only once in a function. It should be declared + before it is used. It should not be used outside of the block in which it is + declared. A variable should not have the same name as a variable or + parameter in an outer function. Do not mix var and + let. Declare one name per statement.

    +

    = == ===

    +

    FORTRAN made a terrible + mistake in using the equality operator as its assignment operator. That + mistake has been replicated in most languages since then. C compounded that + mistake by making == its equality operator. The visual + similarity is a source of errors. JavaScript compounded this further by + making == a type coercing comparison operator that produces + false positive results. This was mitigated by adding the === + operator, leaving the broken == operator in place.

    +

    JSLint attempts to minimize errors by the following rules:

    +

    == is not allowed. This avoids the false positives, and + increases the visual distance between = and ===. + Assignments are not allowed in expression position, and comparisons are not + allowed in statement position. This also reduces confusion. 

    +

    Semicolon

    +

    JavaScript uses a C-like syntax that requires the use of semicolons to + delimit certain statements. JavaScript attempts to make those semicolons + optional with an automatic semicolon insertion mechanism, but it does not + work very well. Automatic semicolon insertion was added to make + things easier for beginners. Unfortunately, it sometimes fails. Do not rely + on it unless you are a beginner.

    +

    JSLint expects that every statement will be followed by + ; except for for, function, + if, switch, try, and + while. JSLint does not expect to see unnecessary + semicolons, the empty statement, or empty blocks.

    +

    function =>

    +

    JavaScript has four syntactic forms for making function objects: function + statements, function expressions, enhanced object literals, and the + => fart operator.

    +

    The function statement creates a variable and assigns the function object to + it. It should be used in a file or function body, but not inside of a block.

    +
    function name(parameters) { +
    statements
    + }
    +

    The function expression unfortunately looks like the function statement. It + may appear anywhere that an expression may appear, but not in statement + position and not in a loop. It produces a function object but does not + create a variable in which to store it.

    +
    function (parameters) { +
    statements
    + }
    +

    The enhanced object literal provides an ES6 shorthand for creating a property + whose value is a function, saving you from having to type + : colon and function.

    +
    { +
    name(parameters) { +
    statements
    + }
    + }
    +

    Finally, ES6 provides an even shorter form of function expression that leaves + out the words function and return:

    +
    (parameters) => + expression
    +

    JSLint requires the parens around the parameters, and forbids a + { left brace after the + => fart to avoid syntactic ambiguity.

    +

    Comma

    +

    The , comma operator is unnecessary and can + mask programming errors.

    +

    JSLint expects to see the comma used as a separator, but not as + an operator. It does not expect to see elided elements in array literals. A + comma should not appear after the last element of an array literal or object + literal.

    +

    Blocks

    +

    JSLint expects blocks with function, + if, switch, while, for, + do, and try statements and nowhere else.

    +

    JSLint expects that if, while, + do and for statements will be made with blocks + {that is, with statements enclosed in braces}.

    +

    JavaScript allows an if to be written like this:

    +
    if (condition)
    +    statement;
    +

    That form is known to contribute to mistakes in projects where many + programmers are working on the same code. That is why JSLint + expects the use of a block:

    +
    if (condition) {
    +    statements;
    +}
    +

    Experience shows that this form is more resilient.

    +

    Expression Statements

    +

    An expression statement is expected to be an assignment or a function/method + call. All other expression statements are considered + to be errors.

    +

    for

    +

    JSLint does not recommend use of the for statement. + Use array methods like forEach instead. The for + option will suppress some warnings. The forms of for that + JSLint accepts are restricted, excluding the new ES6 forms.

    +

    for in

    +

    JSLint does not recommend use of the for + in statement. Use Object.keys instead.

    +

    The for in statement allows for looping through + the names of all of the properties of an object. Unfortunately, it also + loops through all of the properties that were inherited through the + prototype chain. This has the bad side effect of serving up method + functions when the interest is in data properties. If a program is written + without awareness of this situation, then it can fail.

    +

    The body of every for in statement should be + wrapped in an if statement that does filtering. It can select + for a particular type or range of values, or it can exclude functions, + or it can exclude properties from the prototype. For example,

    +
    for (name in object) {
    +    if (object.hasOwnProperty(name)) {
    +        ....
    +    }
    +}
    +

    Note that the above code will fail if the object contains a property + named hasOwnProperty. Use Object.keys instead.

    +

    switch

    +

    A common error in switch statements is to forget to place a + break statement after each case, resulting in unintended + fall-through. JSLint expects that the statement before the next + case or default is one of these: + break, return, or throw.

    +

    with

    +

    The with statement was intended to provide a shorthand in + accessing properties in deeply nested objects. Unfortunately, it behaves + very badly when setting new properties. Never use the with + statement.

    +

    JSLint does not expect to see a with statement.

    +

    Labels

    +

    JavaScript allows any statement to have a label, and labels have a + separate name space. JSLint is more strict.

    +

    JSLint expects labels only on statements that interact + with break: switch, while, + do, and for. JSLint expects that + labels will be distinct from vars and parameters.

    +

    Unreachable Code

    +

    JSLint expects that a return, break, + or throw statement will be followed by a + } right brace or case or + default.

    +

    Confusing Pluses and Minuses

    +

    JSLint expects that + will not be followed by + + or ++, and that - will not be + followed by - or --. A misplaced space can turn + + + into ++, an error that is difficult to see. + Use parens to avoid confusion.

    +

    ++ and --

    +

    The ++ increment and + -- decrement operators have been known to + contribute to bad code by encouraging excessive trickiness. They are second + only to faulty architecture in enabling to viruses and other security + menaces. Also, preincrement/postincrement confusion can produce off-by-one + errors that are extremely difficult to diagnose. Fortunately, they are also + complete unnecessary. There are better ways to add 1 to a variable.

    +

    It is best to avoid these operators entirely and rely on += and + -= instead.

    +

    void

    +

    In most C-like languages, void is a type. In + JavaScript, void is a prefix operator that always + returns undefined. JSLint does not expect to + see void because it is confusing and not very useful.

    +

    Regular Expressions

    +

    Regular expressions are written in a terse and cryptic notation. + JSLint looks for problems that may cause portability problems. + It also attempts to resolve visual ambiguities by recommending explicit + escapement.

    +

    JavaScript’s syntax for regular expression literals overloads the + / slash character. To avoid ambiguity, + JSLint expects that the character preceding a regular + expression literal is a ( left paren or + = equal or + : colon or + , comma character.

    +

    this

    +
    Having this in the language makes it harder to talk + about the language. It is like pair programming with Abbott and Costello. +
    +

    Avoid using this. Warnings about this can be + suppressed with option.this.

    +

    Constructors and new

    +

    Constructors are functions that are designed to be used with the + new prefix. The new prefix creates a new object + based on the function's prototype, and binds that object to the + function's implied this parameter. If you neglect to use the + new prefix, no new object will be made and this + will be bound to the global object.

    +

    JSLint enforces the convention that constructor functions be + given names with initial uppercase. JSLint does not expect to + see a function invocation with an initial uppercase name unless it has the + new prefix. JSLint does not expect to see the + new prefix used with functions whose names do not start with + initial uppercase.

    +

    JSLint does not expect to see the wrapper forms + new Number, new String, new Boolean.

    +

    JSLint does not expect to see new Object. + Use Object.create(null) instead.

    +

    Whitespace

    +

    JSLint has a specific set of rules about the use of whitespace. + Where possible, these rules are consistent with centuries of good practice + with literary style.

    +

    The indentation increases by 4 spaces when the last token on a line is + { left brace, + [ left bracket, + ( left paren. The matching closing + token will be the first token on a line, restoring the previous + indentation.

    +

    The ternary operator can be visually confusing, so + ? question mark and + : colon always begin a line and increase + the indentation by 4 spaces.

    +
    return (
    +    (the_token.id === "(string)" || the_token.id === "(number)")
    +    ? String(the_token.value)
    +    : the_token.id
    +);
    +

    The word function is always followed with one space.

    +

    Clauses (case, catch, default, + else, finally) are not statements and so should + not be indented like statements.

    +

    Spaces are used to make things that are not invocations look less like + invocations.

    +

    Tabs and spaces should not be mixed. We should pick just one in order to + avoid the problems that come from having both. Personal preference is an + extremely unreliable criterion. Neither offers a powerful advantage over the + other. Fifty years ago, tab had the advantage of consuming less memory, but + Moore's Law has eliminated that advantage. Space has one clear advantage + over tab: there is no reliable standard for how many spaces a tab + represents, but it is universally accepted that a space occupies a space. So + use spaces. You can edit with tabs if you must, but make sure it is spaces + again before you commit. Maybe someday we will finally get a universal + standard for tabs, but until that day comes, the better choice is spaces.

    +

    Report

    +

    If JSLint is able to complete its scan, it generates a function + report. It lists for each function:

    +
      +
    • The line number on which it starts.
    • +
    • The function’s name. In the case of anonymous functions, + JSLint will attempt to + «guess» the name.
    • +
    • The parameters.
    • +
    • Variables: The variables that are declared in the function.
    • +
    • Closure: The variables and parameters that are declared in + the function that are used by its inner functions.
    • +
    • Exceptions: The variables that are declared by catch + clauses of try statements.
    • +
    • Outer: Variables used by this function that are declared in + other functions.
    • +
    • Global: Global variables that are used by this function. Keep + these to a minimum.
    • +
    • Label: Statement labels that are used by this function.
    • +
    +

    The report will also include a list of all of the property + names that were used.

    +

    Feedback

    +

    Please let me know if JSLint is useful for you. Is it too + strict? Is there a check or a report that could help you to improve the + quality of your programs? + douglas@crockford.com. + But please don't ask me to dumb JSLint down or to make it more + forgiving of bad practices. You would only be disappointed.

    +

    I intend to continue to adapt JSLint based on your comments. + Keep watching for improvements.

    +

    Try it

    +

    Try it. Paste your script + into the window and click the + JSLint + button. The analysis is done by a script running on your machine. + Your script is not sent over the network. You can set the options used.

    +

    JSLint is written entirely in JavaScript, so it can run + anywhere that JavaScript (or even Java) can run.

    +

    Perfectly fine

    +

    JSLint was designed to reject code that some would consider to + be perfectly fine. The reason for this is that JSLint's + purpose is to help produce programs that are free of error. That is + difficult in any language and is especially hard in JavaScript. + JSLint attempts to help you increase the visual distance + between correct programs and incorrect programs, making the remaining errors + more obvious. JSLint will give warnings about things that are + not necessarily wrong in the current situation, but which have been observed + to mask or obscure errors. Avoid those things when there are better options + available.

    +

    It is dangerous out there. JSLint is here to help.

    +

    Warning

    +

    JSLint will hurt your feelings. Side effects may include + headache, irritability, dizziness, snarkiness, stomach pain, defensiveness, + dry mouth, cleaner code, and a reduced error rate.

    + +

    JSLint

    +

    JSLint is delivered as a set of files:

    + + + + + + + + +
    FilenameContent
    help.htmlUsing jslint as a single page JSLint application.
    index.htmlSingle page JSLint application that uses the + js files.
    jslint.mjsThe jslint function.
    +

    function jslint

    +

    The jslint function is written in ES6 JavaScript. It has no + dependence on other files.

    +

    The jslint function has three arguments:

    + + + + + + + + + + +
    Parameter nameTypeVolitionDescription
    sourcestring or + arrayrequiredThis is the source of the + program to be analyzed. It can be either a string containing + \n, \r, or \r\n line breaks, or an + array of strings, one element per line.
    option_objectobjectoptionalThis object contains the chosen options for this + analysis. The keys are the names of the options described on the + help page. The values are either the boolean + true or a small number.
    global_arrayarrayoptionalThis is an array of strings. Each string names one global variable that + may be used by the program. Listing names here will silence warnings + about the names. It will not introduce the names into the runtime + environment.
    +

    The jslint function will not modify any of its arguments.

    +

    The jslint function will return a result object contain the + following properties:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeContent
    directivesarrayAn array of comments containing directives.
    editionstringThe verison of jslint that produced the result.
    exportsobjectAll of the exported names and values.
    fromsarrayAll of the strings named in import statements.
    functionsarrayAn array of function objects.
    globalobjectThe global object, a body that contains the outermost statements and + variables.
    idstring"(JSLint)"
    jsonbooleantrue if the source was a + JSON text.
    linesarrayAn array of strings, one for each line of text in the source.
    modulebooleantrue if the file contains an import or + export.
    okbooleantrue if no warnings were found.
    optionobjectThe option object that was passed in, or an empty substitute.
    propertyobjectThe names are the names of properties used in the source. The values are + the number of times each name occurred.
    stopbooleantrue if JSLint was not able to process the + entire file.
    tokensarrayAll of the token objects in source order.
    treearrayThe token objects that represent the outermost statements. Those will be + linked to other tokens, forming an abstract parse tree.
    warningsarrayThe warning objects.
    +

    Tokens

    +

    A source file is composed of tokens. Each identifier, each operator, each + punctuator, each literal, and each comment is a token. The whitespace + between tokens is not a token.

    +

    An object is made for each token. The properties in each token will vary + according to the type of the token. More properties will be added to the + tokens during the analysis to indicate the token's purpose or + relationship with other tokens. The tokens will be woven together to + form a tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Property nameTypeDescriptionWhere
    aritystringunary, binary, ternary, + assignment, statement, + variable, function, pre, + postnon-literals
    blocktoken or array of tokensThis is the contents of a block or compound statement. Each + token represents a statement.statement
    bodybooleantrue if the block is a function body.block
    catchtokenThe catch clause.try
    closurebooleantrue if accessed by inner functionsvariable
    complexbooleantrue if one or more parameters use new ES6 syntaxfunction
    constantbooleantrue if the thing is a compile-time constanttoken
    contextobjectThe container of variables, parameters, labels, and exception + variables declared in a function.function
    directivebooleanjslint, global, propertycomment
    disruptbooleantrue if a disruptive statement, or a block ending + with a distruption. An if will disrupt if both of + its branches disrupt.statement
    dotbooleantrue if the previous token was a dot.identifier
    ellipsisbooleantrue if the parameter or argument is preceded by + the ellipsis.token
    elsearray of tokensAlternate block in + if (else), + switch (default), + try (finally)statement
    expressiontoken or array of tokensOne or more expressions.operator or statement
    extrastringget, setproperties
    flagobjectAn object containing the properties g, + i, m, u, or + y.regexp
    fromnumberThe starting position of the token within its line of source + code. A token at the extreme left margin has a from + of 0.token
    parenttokenThe function in which the variable was declared.variable
    idstringThe id of the token. For most tokens, this is the + token text itself.
    + For comments, id is "(comment)".
    For + number literals, id is "(number)".
    + For regular expression literals, id is + "(regexp)".
    For string literals, id is + "(string)".
    + The end of the file has an id of "(end)".
    + The global object has an id of "(global)".
    token
    identifierbooleantrue if the token is an identifier.token
    importstringThe import from string literal.import
    inctokenThe increment clause of a for statement.for
    initialtokenThe initialization clause of a for statement.for
    labeltokenThe label of a statement, or the name of a property in an object + literal.statement or object literal
    levelnumberThe function nesting level. The global context is 0. The + outermost functions are 1. Their inner functions are 2.function
    linenumberThe line number on which the token was found. A token on the + first line will have a line of 0. If the token + spans multiple lines (such as a long comment) line + will be the number of the last line it occupied. token
    nametoken or stringThe name of a functionfunction
    namestoken or array of tokensParameters or variables on the left of =.= or (
    nrnumberThe token sequence number.token
    parametersarray of tokensThe parameter list.function
    parenttokenThe function in which the variable was declared.variable
    quotestringThe quote character.string literal
    rolestringexception, function, + label, parameter, variableidentifier
    shebangstringThe first line of text if it started with #!line
    statementbooleantrue if the token is the first token of a statement.statement
    stricttokenThe "use strict" pragma.block
    thrunumberThe ending position of the token within its line of source code. + It is usually from plus the length of the token.token
    valuestring or array of stringsThe text of the token. For a long comment or megastring, this + could be an array of strings.literals
    variabletokenThis links a variable to its definition.variable
    warningobjectA warning object triggered by this token.token
    wrappedbooleantrue if the expression was wrapped in parens.expression
    writablebooleantrue if the variable may be assigned to.variable
    +

    Reports

    +

    The report object contains three functions that all take a + result from the jslint function as input. + + + + + + + + + + + + + + + + + +
    Function nameDescription
    errorThis function takes a result and returns an HTML text fragment + from the warnings that are found.
    functionThis function takes a result and returns an HTML text fragment + detailing the declarations of every function.
    propertyThis function takes a result and returns a JSLint + property directive. This directive could be pasted into a file.
    +

     

    + +

    Further Reading

    + + + diff --git a/heroku-logo.75x25.png b/heroku-logo.75x25.png new file mode 100644 index 000000000..981f0dc10 Binary files /dev/null and b/heroku-logo.75x25.png differ diff --git a/icon-folder-open-regular.svg b/icon-folder-open-regular.svg new file mode 100644 index 000000000..26e06fa2c --- /dev/null +++ b/icon-folder-open-regular.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon-folder-open-solid.svg b/icon-folder-open-solid.svg new file mode 100644 index 000000000..99d403c81 --- /dev/null +++ b/icon-folder-open-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon-folder-regular.svg b/icon-folder-regular.svg new file mode 100644 index 000000000..1e0ce26bc --- /dev/null +++ b/icon-folder-regular.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon-folder-solid.svg b/icon-folder-solid.svg new file mode 100644 index 000000000..0169c71bd --- /dev/null +++ b/icon-folder-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon-folder.svg b/icon-folder.svg new file mode 100644 index 000000000..44bda9b0f --- /dev/null +++ b/icon-folder.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icon-window-maximize-regular.svg b/icon-window-maximize-regular.svg new file mode 100644 index 000000000..7b7e5e166 --- /dev/null +++ b/icon-window-maximize-regular.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/image-folder-open-solid.svg b/image-folder-open-solid.svg new file mode 100644 index 000000000..99d403c81 --- /dev/null +++ b/image-folder-open-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/image-github-brands.svg b/image-github-brands.svg new file mode 100644 index 000000000..7870c06dc --- /dev/null +++ b/image-github-brands.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/image-jslint-128x128.png b/image-jslint-128x128.png new file mode 100644 index 000000000..f7340e0b9 Binary files /dev/null and b/image-jslint-128x128.png differ diff --git a/image-jslint-256x256.png b/image-jslint-256x256.png new file mode 100644 index 000000000..1b2c70aba Binary files /dev/null and b/image-jslint-256x256.png differ diff --git a/image-jslint-32x32.png b/image-jslint-32x32.png new file mode 100644 index 000000000..0d32ee109 Binary files /dev/null and b/image-jslint-32x32.png differ diff --git a/image-jslint-512.svg b/image-jslint-512.svg new file mode 100644 index 000000000..149257cdd --- /dev/null +++ b/image-jslint-512.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + diff --git a/image-jslint-512x512.png b/image-jslint-512x512.png new file mode 100644 index 000000000..f1b440efe Binary files /dev/null and b/image-jslint-512x512.png differ diff --git a/image-jslint-64x64.png b/image-jslint-64x64.png new file mode 100644 index 000000000..7545fffbf Binary files /dev/null and b/image-jslint-64x64.png differ diff --git a/image-jslint.html b/image-jslint.html new file mode 100644 index 000000000..ba774fcb6 --- /dev/null +++ b/image-jslint.html @@ -0,0 +1,52 @@ + + + +logo + + + +
    +
    JS
    +
    Lint
    +
    + + diff --git a/image-json160.gif b/image-json160.gif new file mode 100644 index 000000000..3bb55c8dd Binary files /dev/null and b/image-json160.gif differ diff --git a/image-window-maximize-regular.svg b/image-window-maximize-regular.svg new file mode 100644 index 000000000..7b7e5e166 --- /dev/null +++ b/image-window-maximize-regular.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 000000000..b3f6bc9b3 --- /dev/null +++ b/index.html @@ -0,0 +1,1649 @@ + + + + + + + + + JSLint: The JavaScript Code Quality and Coverage Tool + + + + + + + + + + + + +
    +
    Loading modules
    +   + + . + . + . + +
    +
    + +
    + Source + +
    +
    + + + +
    +
    + Options +
    +
    + Env... +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    + Allow... +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
      +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
      +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
      +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    +
    + + diff --git a/jslint.cjs b/jslint.cjs new file mode 100644 index 000000000..569e46264 --- /dev/null +++ b/jslint.cjs @@ -0,0 +1,19 @@ +/*jslint beta, node*/ +/*property + module, readFileSync, replace, runInNewContext +*/ +require("vm").runInNewContext( + ( + "\"use strict\";" + + require("fs").readFileSync(__dirname + "/jslint.mjs", "utf8").replace( + "\nexport default Object.freeze(jslint_export);", + "\nmodule.exports = jslint_export;" + ).replace( + "\njslint_import_meta_url = import.meta.url;", + "\n// jslint_import_meta_url = import.meta.url;" + ) + ), + { + module + } +); diff --git a/jslint.css b/jslint.css new file mode 100644 index 000000000..cc3bf1696 --- /dev/null +++ b/jslint.css @@ -0,0 +1,341 @@ +@font-face { + font-family: 'Programma'; + font-weight: bold; + src: url('Programma-Bold.woff2') format('woff2'); +} +@font-face { + font-family: 'Daley'; + font-weight: bold; + src: url('Daley-Bold.woff2') format('woff2'); +} + +body { + background-color: antiquewhite; + color: black; + font-family: 'Daley', sans-serif; + margin: 0; + padding: 0; +} + +#JSLINT_TITLEBOX { + font-family: 'Daley', sans-serif; + margin: 0; + margin-left: 3%; + margin-right: 3%; + padding: 0; +} + +#JSLINT_TITLEBOX ul { + float: right; + font-size: 12pt; +} + +#JSLINT_TITLEBOX div { + color: darkslategray; + float: left; + font-size: 48pt; +} + +#JSLINT_ fieldset { + background-color: gainsboro; + border: 0; + clear: both; + margin-bottom: 1em; + margin-left: 3%; + margin-right: 3%; + margin-top: 1em; + padding: 0; + width: auto; +} +#JSLINT_ legend { + background-color: darkslategray; + border: 0; + color: white; + font-size: 100%; + font-style: normal; + font-weight: normal; + margin: 0; + padding-bottom: 0.25em; + padding-left: 0; + padding-right: 0; + padding-top: 0.25em; + text-align: center; + width: 100%; +} +#JSLINT_ button { + background-color: darkslategray; + border: 0; + color: white; + cursor: pointer; + font-family: 'Daley', sans-serif; + font-size: 100%; + font-style: normal; + margin-left: 1em; + margin-right: 1em; + padding-left: 1em; + padding-right: 1em; + padding-bottom: 0.25em; + padding-top: 0.25em; + text-align: center; +} +#JSLINT_ button:hover { + background-color: slategray; + color: white; + border: 0; +} + +#JSLINT_ button:active { + border: 0; + color: black; +} + +#JSLINT_ button:disabled { + background-color: gray; + border: 0; + color: gainsboro; +} + +a:visited { + color: #69565c; +} + +a:link { + color: black; +} + +#JSLINT_ input[type="text"] { + background-color: white; + border: 0; + color: black; + margin-bottom: 0.2em; + margin-right: 0.5em; + padding-left: 0.5em; + padding-right: 0.5em; + text-align: right; + width: 3em; +} + +#JSLINT_ textarea { + border: 0; + background-color: white; + border: 0; + font-family: Programma, monospace; + font-size: 100%; + font-weight: bold; + resize: none; +} + +#JSLINT_ textarea::selection { + background-color: yellow; + color: black; +} + +#JSLINT_NUMBER { + color: darkgray; + display: inline-block; + height: 4in; + margin: 2%; + margin-right: 0; + overflow: hidden; + padding-right: 0.5%; + text-align: right; + white-space: pre; + width: 4%; +} + +#JSLINT_SOURCE { + color: black; + display: inline-block; + font-size: 100%; + height: 4in; + margin: 2%; + margin-left: 0; + margin-right: 0; + overflow: auto; + padding-left: 0.5%; + white-space: pre; + width: 91%; +} + +#JSLINT_PROPERTY { + background-color: honeydew; + border: 0; + margin: 2%; + padding: 0.5%; + white-space: pre; + width: 95%; +} + +#JSLINT_GLOBAL { + white-space: normal; + width: 95%; +} +#JSLINT_ label { + font-family: sans-serif; + font-size: 90%; + padding-left: 0.25em; +} +#JSLINT_OPTIONS>div { + float: left; + margin: 0.5em; +} + +#JSLINT_ address { + color: black; + display: block; + float: right; + font-family: serif; + font-size: 90%; + margin-left: 1em; +} +#JSLINT_ dl { + background-color: cornsilk; + color: white; + margin: 0; + padding-bottom: 2pt; + padding-left: 1em; + padding-right: 1em; + padding-top: 2pt; +} +#JSLINT_ dfn { + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + margin-bottom: 2pt; +} +#JSLINT_ dt { + color: black; + display: block; + float: left; + font-family: serif; + font-size: 75%; + font-style: italic; + margin: 0; + width: 8em; + text-align: right; +} +#JSLINT_ dd { + color: black; + display: block; + font-family: Programma, monospace; + font-weight: bold; + margin-left: 8em; + padding-bottom: 2pt; +} +#JSLINT_WARNINGS>legend { + background-color: indianred; +} +#JSLINT_WARNINGS>div { + background-color: pink; + padding: 1em; +} +#JSLINT_WARNINGS cite { + color: black; + display: block; + font-family: serif; + font-size: 100%; + font-style: normal; + margin-bottom: 4pt; + margin-left: 20pt; + margin-right: 20pt; + margin-top: 4pt; + overflow-x: hidden; +} +#JSLINT_WARNINGS samp { + background-color: lavenderblush; + color: black; + display: block; + font-family: Programma, monospace; + font-size: 100%; + font-style: normal; + font-weight: bold; + padding: 4pt; + margin-bottom: 0; + margin-left: 16pt; + margin-right: 16pt; + margin-top: 0; + white-space: pre-wrap; +} + +#JSLINT_REPORT center { + margin-top: 1em; +} + +#JSLINT_WARNINGS dl address { + color: black; + display: inline; + float: none; + font-size: 80%; + margin: 0; +} + +#JSLINT_REPORT>div { + padding: 1em; +} + +#JSLINT_ dl.level0 { + color: black; + background-color: white; +} + +#JSLINT_ dl.level1 { + color: black; + background-color: #ffffe0; /* yellow */ + margin-left: 1em; +} + +#JSLINT_ dl.level2 { + color: black; + background-color: #e0ffe0; /* green */ + margin-left: 2em; +} + +#JSLINT_ dl.level3 { + color: black; + background-color: #D0D0ff; /* blue */ + margin-left: 3em; +} + +#JSLINT_ dl.level4 { + background-color: #ffe0ff; /* purple */ + color: black; + margin-left: 4em; +} + +#JSLINT_ dl.level5 { + background-color: #ffe0e0; /* red */ + color: black; + margin-left: 5em; +} + +#JSLINT_ dl.level6 { + background-color: #ffe390; /* orange */ + color: black; + margin-left: 6em; +} + +#JSLINT_ dl.level7 { + background-color: #e0e0e0; /* gray */ + color: black; + margin-left: 7em; +} + +#JSLINT_ dl.level8 { + color: black; + margin-left: 8em; +} + +#JSLINT_ dl.level9 { + color: black; + margin-left: 9em; +} + +.none { + display: none; +} +.center { + text-align: center; +} diff --git a/jslint.js b/jslint.js new file mode 100644 index 000000000..b486c1ce2 --- /dev/null +++ b/jslint.js @@ -0,0 +1,11573 @@ +// #!/usr/bin/env node +// JSLint + +// The Unlicense +// +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + + +// jslint(source, option_dict, global_list) is a function that takes 3 +// arguments. The second two arguments are optional. + +// source A text to analyze. +// option_dict An object whose keys correspond to option names. +// global_list An array of strings containing global variables that +// the file is allowed readonly access. + +// jslint returns an object containing its results. The object contains a lot +// of valuable information. It can be used to generate reports. The object +// contains: + +// directives: an array of directive comment tokens. +// edition: the version of JSLint that did the analysis. +// exports: the names exported from the module. +// froms: an array of strings representing each of the imports. +// functions: an array of objects that represent all functions +// declared in the file. +// global: an object representing the global object. Its .context property +// is an object containing a property for each global variable. +// id: "(JSLint)" +// json: true if the file is a JSON text. +// lines: an array of strings, the source. +// module: true if an import or export statement was used. +// ok: true if no warnings were generated. This is what you want. +// option: the option argument. +// property: a property object. +// stop: true if JSLint was unable to finish. You don't want this. +// tokens: an array of objects representing the tokens in the file. +// tree: the token objects arranged in a tree. +// warnings: an array of warning objects. A warning object can contain: +// name: "JSLintError" +// column: A column number in the file. +// line: A line number in the file. +// code: A warning code string. +// message: The warning message string. +// a: Exhibit A. +// b: Exhibit B. +// c: Exhibit C. +// d: Exhibit D. + +// jslint works in several phases. In any of these phases, errors might be +// found. Sometimes JSLint is able to recover from an error and continue +// parsing. In some cases, it cannot and will stop early. If that should happen, +// repair your code and try again. + +// Phases: + +// PHASE 1. Split by newlines into . +// PHASE 2. Lex into . +// PHASE 3. Parse into using the Pratt-parser. +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). +// PHASE 5. Check whitespace between tokens in . + +// jslint can also examine JSON text. It decides that a file is JSON text if +// the first token is "[" or "{". Processing of JSON text is much simpler than +// the processing of JavaScript programs. Only the first three phases are +// required. + +// WARNING: JSLint will hurt your feelings. + +/*jslint beta, node*/ +/*property + JSLINT_BETA, NODE_V8_COVERAGE, a, all, argv, arity, artifact, + assertErrorThrownAsync, assertJsonEqual, assertOrThrow, assign, async, b, + beta, bitwise, block, body, browser, c, calls, catch, catch_list, + catch_stack, causes, char, children, clear, closer, closure, code, column, + concat, consoleError, console_error, console_log, constant, context, + convert, count, coverageDir, create, cwd, d, dead, debugInline, default, + delta, devel, directive, directive_ignore_line, directive_list, directives, + dirname, disrupt, dot, edition, elem_list, ellipsis, else, end, endOffset, + endsWith, entries, env, error, eval, every, example_list, excludeList, exec, + execArgv, exit, exitCode, export_dict, exports, expression, extra, fart, + file, fileList, fileURLToPath, filter, finally, flag, floor, for, forEach, + formatted_message, free, freeze, from, froms, fsWriteFileWithParents, + fud_stmt, functionName, function_list, function_stack, functions, get, + getset, github_repo, globExclude, global, global_dict, global_list, + holeList, htmlEscape, id, identifier, import, import_list, import_meta_url, + inc, includeList, indent2, index, indexOf, init, initial, isArray, + isBlockCoverage, isHole, isNaN, is_equal, is_weird, join, jslint, + jslint_apidoc, jslint_assert, jslint_charset_ascii, jslint_cli, + jslint_edition, jslint_phase1_split, jslint_phase2_lex, jslint_phase3_parse, + jslint_phase4_walk, jslint_phase5_whitage, jslint_report, json, + jstestDescribe, jstestIt, jstestOnExit, keys, label, lbp, led_infix, length, + level, line, lineList, line_list, line_offset, line_source, lines, + linesCovered, linesTotal, live, log, long, loop, m, map, margin, match, max, + message, meta, min, mkdir, modeCoverageIgnoreFile, modeIndex, mode_cli, + mode_conditional, mode_json, mode_module, mode_noop, mode_property, + mode_shebang, mode_stop, module, moduleFsInit, moduleName, module_list, + name, names, node, nomen, noop, now, nr, nud_prefix, + objectDeepCopyWithKeysSorted, ok, on, open, opening, option, option_dict, + order, package_name, padEnd, padStart, parameters, parent, parentIi, parse, + pathname, pathnameList, platform, pop, processArgv, process_argv, + process_env, process_exit, promises, property, property_dict, push, quote, + ranges, readFile, readdir, readonly, recursive, reduce, repeat, replace, + resolve, result, reverse, role, round, scriptId, search, set, shebang, + shell, shift, signature, single, slice, some, sort, source, spawn, splice, + split, stack, stack_trace, start, startOffset, startsWith, statement, + statement_prv, stdio, stop, stop_at, stringify, subscript, switch, + syntax_dict, tenure, test, test_cause, test_internal_error, this, thru, + toLocaleString, toString, token, token_global, token_list, token_nxt, + token_tree, tokens, trace, tree, trim, trimEnd, trimRight, try, type, + unlink, unordered, unshift, url, used, v8CoverageListMerge, + v8CoverageReportCreate, value, variable, version, versions, warn, warn_at, + warning, warning_list, warnings, white, wrapped, writeFile +*/ + +// init debugInline +let debugInline = (function () { + let __consoleError = function () { + return; + }; + function debug(...argv) { + +// This function will print to stderr and then return [0]. + + __consoleError("\n\ndebugInline"); + __consoleError(...argv); + __consoleError("\n"); + return argv[0]; + } + debug(); // Coverage-hack. + __consoleError = console.error; //jslint-ignore-line + return debug; +}()); +let jslint_charset_ascii = ( + "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007" + + "\b\t\n\u000b\f\r\u000e\u000f" + + "\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017" + + "\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f" + + " !\"#$%&'()*+,-./0123456789:;<=>?" + + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + + "`abcdefghijklmnopqrstuvwxyz{|}~\u007f" +); +let jslint_edition = "v2024.11.24"; +let jslint_export; // The jslint object to be exported. +let jslint_fudge = 1; // Fudge starting line and starting + // ... column to 1. +let jslint_import_meta_url = ""; // import.meta.url used by cli. +let jslint_rgx_cap = ( + /^[A-Z]/ +); +let jslint_rgx_crlf = ( + /\n|\r\n?/ +); +let jslint_rgx_digits_bits = ( + /^[01_]*/ +); +let jslint_rgx_digits_decimals = ( + /^[0-9_]*/ +); +let jslint_rgx_digits_hexs = ( + /^[0-9A-F_]*/i +); +let jslint_rgx_digits_octals = ( + /^[0-7_]*/ +); +let jslint_rgx_directive = ( + /^(jslint|property|global)\s+(.*)$/ +); +let jslint_rgx_directive_part = ( + /([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*|$/g +); +let jslint_rgx_identifier = ( + /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/ +); +let jslint_rgx_json_number = ( + +// https://datatracker.ietf.org/doc/html/rfc7159#section-6 +// number = [ minus ] int [ frac ] [ exp ] + + /^-?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][\-+]?\d+)?$/ +); +let jslint_rgx_mega = ( + +// Vim-hack - vim-editor has trouble parsing naked '`' in regexp + + /[\u0060\\]|\$\{/ +); +let jslint_rgx_module = ( + /^[a-zA-Z0-9_$:.@\-\/]+$/ +); +let jslint_rgx_numeric_separator_illegal = ( + /__|_$|_n$/m +); +let jslint_rgx_slash_star_or_slash = ( + /\/\*|\/$/ +); +let jslint_rgx_tab = ( + /\t/g +); +let jslint_rgx_todo = ( + /\b(?:todo|TO\s?DO|HACK)\b/ +); +let jslint_rgx_token = new RegExp( + "^(" + + "(\\s+)" + + "|([a-zA-Z_$][a-zA-Z0-9_$]*)" + + "|[(){}\\[\\],:;'\"~\\`]" + + "|\\?[?.]?" + + "|=(?:==?|>)?" + + "|\\.+" + + "|\\*[*\\/=]?" + + "|\\/[*\\/]?" + + "|\\+[=+]?" + + "|-[=\\-]?" + + "|[\\^%]=?" + + "|&[&=]?" + + "|\\" + + "|[|=]?" + + "|>{1,3}=?" + + "|< throws an error. + + let err; + try { + await asyncFunc(); + } catch (errCaught) { + err = errCaught; + } + assertOrThrow(err, "No error thrown."); + assertOrThrow( + !regexp || new RegExp(regexp).test(err.message), + err + ); +} + +function assertJsonEqual(aa, bb, message) { + +// This function will assert JSON.stringify() === JSON.stringify(). + + aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa), undefined, 1); + bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb), undefined, 1); + if (aa !== bb) { + throw new Error( + "\n" + aa + "\n!==\n" + bb + + ( + typeof message === "string" + ? " - " + message + : message + ? " - " + JSON.stringify(message) + : "" + ) + ); + } +} + +function assertOrThrow(condition, message) { + +// This function will throw if is falsy. + + if (!condition) { + throw ( + (!message || typeof message === "string") + ? new Error(String(message).slice(0, 2048)) + : message + ); + } +} + +function empty() { + +// The empty function produces a new empty object that inherits nothing. This is +// much better than '{}' because confusions around accidental method names like +// 'constructor' are completely avoided. + + return Object.create(null); +} + +async function fsWriteFileWithParents(pathname, data) { + +// This function will write to and lazy-mkdirp if necessary. + + await moduleFsInit(); + +// Try writing to pathname. + + try { + await moduleFs.promises.writeFile(pathname, data); + } catch (ignore) { + +// Lazy mkdirp. + + await moduleFs.promises.mkdir(modulePath.dirname(pathname), { + recursive: true + }); + +// Retry writing to pathname. + + await moduleFs.promises.writeFile(pathname, data); + } + console.error("wrote file " + pathname); +} + +function globExclude({ + excludeList = [], + includeList = [], + pathnameList = [] +}) { + +// This function will +// 1. Exclude pathnames in that don't match glob-patterns in +// . +// 2. Exclude pathnames in that match glob-patterns in +// . + + function globAssertNotWeird(list, name) { + +// This function will check if of strings contain weird characters. + + [ + [ + "\n", ( + /^.*?([\u0000-\u0007\r]).*/gm + ) + ], + [ + "\r", ( + /^.*?([\n]).*/gm + ) + ] + ].forEach(function ([ + separator, rgx + ]) { + list.join(separator).replace(rgx, function (match0, char) { + throw new Error( + "Weird character " + + JSON.stringify(char) + + " found in " + name + " " + + JSON.stringify(match0) + ); + }); + }); + } + + function globToRegexp(pattern) { + +// This function will translate glob to javascript-regexp, +// which javascript can then use to "glob" pathnames. + + let ii = 0; + let isClass = false; + let strClass = ""; + let strRegex = ""; + pattern = pattern.replace(( + /\/\/+/g + ), "/"); + pattern = pattern.replace(( + /\*\*\*+/g + ), "**"); + pattern.replace(( + /\\\\|\\\[|\\\]|\[|\]|./g + ), function (match0) { + switch (match0) { + case "[": + if (isClass) { + strClass += "["; + return; + } + strClass += "\u0000"; + strRegex += "\u0000"; + isClass = true; + return; + case "]": + if (isClass) { + isClass = false; + return; + } + strRegex += "]"; + return; + default: + if (isClass) { + strClass += match0; + return; + } + strRegex += match0; + } + return ""; + }); + strClass += "\u0000"; + +// An expression "[!...]" matches a single character, namely any character that +// is not matched by the expression obtained by removing the first '!' from it. +// (Thus, "[!a-]" matches any single character except 'a', and '-'.) + + strClass = strClass.replace(( + /\u0000!/g + ), "\u0000^"); + +// One may include '-' in its literal meaning by making it the first or last +// character between the brackets. + + strClass = strClass.replace(( + /\u0000-/g + ), "\u0000\\-"); + strClass = strClass.replace(( + /-\u0000/g + ), "\\-\u0000"); + +// Escape brackets '[', ']' in character class. + + strClass = strClass.replace(( + /[\[\]]/g + ), "\\$&"); + +// https://stackoverflow.com/questions/3561493 +// /is-there-a-regexp-escape-function-in-javascript +// $()*+-./?[\]^{|} + + strRegex = strRegex.replace(( + +// Ignore [-/]. + + /[$()*+.?\[\\\]\^{|}]/g + ), "\\$&"); + +// Expand wildcard '**/*'. + + strRegex = strRegex.replace(( + /\\\*\\\*\/(?:\\\*)+/g + ), ".*?"); + +// Expand wildcard '**'. + + strRegex = strRegex.replace(( + /(^|\/)\\\*\\\*(\/|$)/gm + ), "$1.*?$2"); + +// Expand wildcard '*'. + + strRegex = strRegex.replace(( + /(?:\\\*)+/g + ), "[^\\/]*?"); + +// Expand wildcard '?'. + + strRegex = strRegex.replace(( + /\\\?/g + ), "[^\\/]"); + +// Expand directory-with-trailing-slash '.../'. + + strRegex = strRegex.replace(( + /\/$/gm + ), "\\/.*?"); + +// Merge strClass into strRegex. + + ii = 0; + strClass = strClass.split("\u0000"); + strRegex = strRegex.replace(( + /\u0000/g + ), function () { + ii += 1; + if (strClass[ii] === "") { + return ""; + } + return "[" + strClass[ii] + "]"; + }); + +// Change strRegex from string to regexp. + + strRegex = new RegExp("^" + strRegex + "$", "gm"); + return strRegex; + } + +// Validate excludeList, includeList, pathnameList. + + globAssertNotWeird(excludeList, "pattern"); + globAssertNotWeird(includeList, "pattern"); + globAssertNotWeird(pathnameList, "pathname"); + +// Optimization +// Concat pathnames into a single, newline-separated string, +// whose pathnames can all be filtered with a single, regexp-pass. + + pathnameList = pathnameList.join("\n"); + +// 1. Exclude pathnames in that don't match glob-patterns in +// . + + if (includeList.length > 0) { + includeList = includeList.map(globToRegexp); + includeList.forEach(function (pattern) { + pathnameList = pathnameList.replace(pattern, "\u0000$&"); + }); + pathnameList = pathnameList.replace(( + /^[^\u0000].*/gm + ), ""); + pathnameList = pathnameList.replace(( + /^\u0000+/gm + ), ""); + } + +// 2. Exclude pathnames in that match glob-patterns in +// . + + excludeList = excludeList.map(globToRegexp); + excludeList.forEach(function (pattern) { + pathnameList = pathnameList.replace(pattern, ""); + }); + +// Split newline-separated pathnames back to list. + + pathnameList = pathnameList.split("\n").filter(function (elem) { + return elem; + }); + return { + excludeList, + includeList, + pathnameList + }; +} + +function htmlEscape(str) { + +// This function will make html-safe by escaping & < >. + + return String(str).replace(( + /&/g + ), "&").replace(( + //g + ), ">"); +} + +function jslint( + source = "", // A text to analyze. + option_dict = empty(), // An object whose keys correspond to option + // ... names. + global_list = [] // An array of strings containing global + // ... variables that the file is allowed + // ... readonly access. +) { + +// The jslint function itself. + + let catch_list = []; // The array containing all catch-blocks. + let catch_stack = [ // The stack of catch-blocks. + { + context: empty() + } + ]; + let cause_dict = empty(); // The object of test-causes. + let directive_list = []; // The directive comments. + let export_dict = empty(); // The exported names and values. + let function_list = []; // The array containing all functions. + let function_stack = []; // The stack of functions. + let global_dict = empty(); // The object containing the global + // ... declarations. + let import_list = []; // The array collecting all import-from strings. + let line_list = String( // The array containing source lines. + "\n" + source + ).split(jslint_rgx_crlf).map(function (line_source) { + return { + line_source + }; + }); + let mode_stop = false; // true if JSLint cannot finish. + let property_dict = empty(); // The object containing the tallied + // ... property names. + let state = empty(); // jslint state-object to be passed between + // jslint functions. + let syntax_dict = empty(); // The object containing the parser. + let tenure = empty(); // The predefined property registry. + let token_global = { // The global object; the outermost context. + async: 0, + body: true, + context: empty(), + finally: 0, + from: 0, + id: "(global)", + level: 0, + line: jslint_fudge, + live: [], + loop: 0, + switch: 0, + thru: 0, + try: 0 + }; + let token_list = []; // The array of tokens. + let warning_list = []; // The array collecting all generated warnings. + +// Error reportage functions: + + function artifact(the_token) { + +// Return a string representing an artifact. + + the_token = the_token || state.token_nxt; + return ( + (the_token.id === "(string)" || the_token.id === "(number)") + ? String(the_token.value) + : the_token.id + ); + } + + function is_equal(aa, bb) { + +// test_cause: +// ["0&&0", "is_equal", "", "", 0] + + test_cause(""); + +// Probably deadcode. +// if (aa === bb) { +// return true; +// } + + jslint_assert(!(aa === bb), `Expected !(aa === bb).`); + if (Array.isArray(aa)) { + return ( + Array.isArray(bb) + && aa.length === bb.length + && aa.every(function (value, index) { + +// test_cause: +// ["`${0}`&&`${0}`", "is_equal", "recurse_isArray", "", 0] +// ["`${0}`&&`${1}`", "is_equal", "recurse_isArray", "", 0] + + test_cause("recurse_isArray"); + return is_equal(value, bb[index]); + }) + ); + } + +// Probably deadcode. +// if (Array.isArray(bb)) { +// return false; +// } + + jslint_assert(!Array.isArray(bb), `Expected !Array.isArray(bb).`); + switch (aa.id === bb.id && aa.id) { + case "(number)": + case "(string)": + return aa.value === bb.value; + +// PR-394 - Bugfix +// Fix jslint falsely believing megastring literals `0` and `1` are similar. + + case "`": + if (!is_equal(aa.value, bb.value)) { + return false; + } + break; + } + if (is_weird(aa) || is_weird(bb)) { + +// test_cause: +// ["aa(/./)||{}", "is_equal", "false", "", 0] + + test_cause("false"); + return false; + } + if (aa.arity === bb.arity && aa.id === bb.id) { + if (aa.id === "." || aa.id === "?.") { + +// test_cause: +// ["aa.bb&&aa.bb", "is_equal", "recurse_arity_id", "", 0] +// ["aa?.bb&&aa?.bb", "is_equal", "recurse_arity_id", "", 0] + + test_cause("recurse_arity_id"); + return ( + is_equal(aa.expression, bb.expression) + && is_equal(aa.name, bb.name) + ); + } + if (aa.arity === "unary") { + +// test_cause: +// ["+0&&+0", "is_equal", "recurse_unary", "", 0] + + test_cause("recurse_unary"); + return is_equal(aa.expression, bb.expression); + } + if (aa.arity === "binary") { + +// test_cause: +// ["aa[0]&&aa[0]", "is_equal", "recurse_binary", "", 0] + + test_cause("recurse_binary"); + return ( + aa.id !== "(" + && is_equal(aa.expression[0], bb.expression[0]) + && is_equal(aa.expression[1], bb.expression[1]) + ); + } + if (aa.arity === "ternary") { + +// test_cause: +// ["aa=(``?``:``)&&(``?``:``)", "is_equal", "recurse_ternary", "", 0] + + test_cause("recurse_ternary"); + return ( + is_equal(aa.expression[0], bb.expression[0]) + && is_equal(aa.expression[1], bb.expression[1]) + && is_equal(aa.expression[2], bb.expression[2]) + ); + } + +// Probably deadcode. +// if (aa.arity === "function" || aa.arity === "regexp") { +// return false; +// } + + jslint_assert( + !(aa.arity === "function" || aa.arity === "regexp"), + `Expected !(aa.arity === "function" || aa.arity === "regexp").` + ); + +// test_cause: +// ["undefined&&undefined", "is_equal", "true", "", 0] + + test_cause("true"); + return true; + } + +// test_cause: +// ["null&&undefined", "is_equal", "false", "", 0] + + test_cause("false"); + return false; + } + + function is_weird(thing) { + switch (thing.id) { + case "(regexp)": + return true; + case "=>": + return true; + case "[": + return thing.arity === "unary"; + case "function": + return true; + case "{": + return true; + default: + return false; + } + } + + function stop(code, the_token, a, b, c, d) { + +// Similar to warn and stop_at. If the token already had a warning, that +// warning will be replaced with this new one. It is likely that the stopping +// warning will be the more meaningful. + + the_token = the_token || state.token_nxt; + delete the_token.warning; + throw warn(code, the_token, a, b, c, d); + } + + function stop_at(code, line, column, a, b, c, d) { + +// Same as warn_at, except that it stops the analysis. + + throw warn_at(code, line, column, a, b, c, d); + } + + function test_cause(code, aa, column) { + +// This function will instrument to for test-purposes. + + if (option_dict.test_cause) { + cause_dict[JSON.stringify([ + String(new Error().stack).replace(( + /^ at (?:file|stop|stop_at|test_cause|warn|warn_at)\b.*?\n/gm + ), "").match( + /\n at ((?:Object\.\w+?_)?\w+?) / + )[1].replace(( + /^Object\./ + ), ""), + code, + String( + (aa === undefined || aa === token_global) + ? "" + : aa + ), + column || 0 + ])] = true; + } + } + + function warn(code, the_token, a, b, c, d) { + +// Same as warn_at, except the warning will be associated with a specific token. +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + let the_warning; + the_token = the_token || state.token_nxt; + the_warning = warn_at( + code, + the_token.line, + (the_token.from || 0) + jslint_fudge, + a || artifact(the_token), + b, + c, + d + ); + +// Issue #408 +// Warnings that should be ignored sometimes suppress legitimate warnings. + + if (the_warning.directive_ignore_line) { + return the_warning; + } + +// If there is already a warning on this token, suppress the new one. It is +// likely that the first warning will be the most meaningful. + + if (the_token.warning) { + warning_list.pop(); + return the_warning; + } + the_token.warning = the_warning; + return the_warning; + } + + function warn_at(code, line, column, a, b, c, d) { + +// Report an error at some line and column of the program. The warning object +// resembles an exception. + + let mm; + let warning = Object.assign(empty(), { + a, + b, + c, + code, + +// Fudge column numbers in warning message. + + column: column || jslint_fudge, + d, + line, + line_source: "", + name: "JSLintError" + }, line_list[line]); + warning.column = Math.max( + Math.min(warning.column, warning.line_source.length), + jslint_fudge + ); + test_cause(code, b || a, warning.column); + switch (code) { + +// The bundle contains the raw text messages that are generated by jslint. It +// seems that they are all error messages and warnings. There are no "Atta +// boy!" or "You are so awesome!" messages. There is no positive reinforcement +// or encouragement. This relentless negativity can undermine self-esteem and +// wound the inner child. But if you accept it as sound advice rather than as +// personal criticism, it can make your programs better. + + case "and": + mm = `The '&&' subexpression should be wrapped in parens.`; + break; + case "bad_assignment_a": + mm = `Bad assignment to '${a}'.`; + break; + case "bad_directive_a": + mm = `Bad directive '${a}'.`; + break; + case "bad_get": + mm = `A get function takes no parameters.`; + break; + case "bad_module_name_a": + mm = `Bad module name '${a}'.`; + break; + case "bad_option_a": + mm = `Bad option '${a}'.`; + break; + case "bad_set": + mm = `A set function takes one parameter.`; + break; + case "duplicate_a": + mm = `Duplicate '${a}'.`; + break; + case "empty_block": + mm = `Empty block.`; + break; + case "expected_a": + mm = `Expected '${a}'.`; + break; + case "expected_a_at_b_c": + mm = `Expected '${a}' at column ${b}, not column ${c}.`; + break; + case "expected_a_b": + mm = `Expected '${a}' and instead saw '${b}'.`; + break; + case "expected_a_b_before_c_d": + mm = `Expected ${a} '${b}' to be ordered before ${c} '${d}'.`; + break; + case "expected_a_b_from_c_d": + mm = ( + `Expected '${a}' to match '${b}' from line ${c}` + + ` and instead saw '${d}'.` + ); + break; + case "expected_a_before_b": + mm = `Expected '${a}' before '${b}'.`; + break; + case "expected_digits_after_a": + mm = `Expected digits after '${a}'.`; + break; + case "expected_four_digits": + mm = `Expected four digits after '\\u'.`; + break; + case "expected_identifier_a": + mm = `Expected an identifier and instead saw '${a}'.`; + break; + case "expected_line_break_a_b": + mm = `Expected a line break between '${a}' and '${b}'.`; + break; + case "expected_regexp_factor_a": + mm = `Expected a regexp factor and instead saw '${a}'.`; + break; + case "expected_space_a_b": + mm = `Expected one space between '${a}' and '${b}'.`; + break; + case "expected_statements_a": + mm = `Expected statements before '${a}'.`; + break; + case "expected_string_a": + mm = `Expected a string and instead saw '${a}'.`; + break; + case "expected_type_string_a": + mm = `Expected a type string and instead saw '${a}'.`; + break; + case "freeze_exports": + mm = ( + `Expected 'Object.freeze('. All export values should be frozen.` + ); + break; + +// PR-378 - Relax warning "function_in_loop". +// +// case "function_in_loop": +// mm = `Don't create functions within a loop.`; +// break; + +// PR-390 - Add numeric-separator check. + + case "illegal_num_separator": + mm = `Illegal numeric separator '_' at column ${column}.`; + break; + case "infix_in": + mm = ( + `Unexpected 'in'. Compare with undefined,` + + ` or use the hasOwnProperty method instead.` + ); + break; + case "label_a": + mm = `'${a}' is a statement label.`; + break; + case "misplaced_a": + mm = `Place '${a}' at the outermost level.`; + break; + case "misplaced_directive_a": + mm = `Place the '/*${a}*/' directive before the first statement.`; + break; + case "missing_await_statement": + mm = `Expected await statement in async function.`; + break; + +// PR-347 - Disable warning "missing_browser". +// +// case "missing_browser": +// mm = `/*global*/ requires the Assume a browser option.`; +// break; + + case "missing_m": + mm = `Expected 'm' flag on a multiline regular expression.`; + break; + case "naked_block": + mm = `Naked block.`; + break; + case "nested_comment": + mm = `Nested comment.`; + break; + case "not_label_a": + mm = `'${a}' is not a label.`; + break; + case "number_isNaN": + mm = `Use Number.isNaN function to compare with NaN.`; + break; + case "out_of_scope_a": + mm = `'${a}' is out of scope.`; + break; + case "redefinition_a_b": + mm = `Redefinition of '${a}' from line ${b}.`; + break; + case "redefinition_global_a_b": + mm = `Redefinition of global ${a} variable '${b}'.`; + break; + case "required_a_optional_b": + mm = `Required parameter '${a}' after optional parameter '${b}'.`; + break; + case "reserved_a": + mm = `Reserved name '${a}'.`; + break; + case "subscript_a": + mm = `['${a}'] is better written in dot notation.`; + break; + case "todo_comment": + mm = `Unexpected TODO comment.`; + break; + case "too_long": + mm = `Line is longer than 80 characters.`; + break; + case "too_many_digits": + mm = `Too many digits.`; + break; + case "unclosed_comment": + mm = `Unclosed comment.`; + break; + case "unclosed_disable": + mm = ( + `Directive '/*jslint-disable*/' was not closed` + + ` with '/*jslint-enable*/'.` + ); + break; + case "unclosed_mega": + mm = `Unclosed mega literal.`; + break; + case "unclosed_string": + mm = `Unclosed string.`; + break; + case "undeclared_a": + mm = `Undeclared '${a}'.`; + break; + case "unexpected_a": + mm = `Unexpected '${a}'.`; + break; + case "unexpected_a_after_b": + mm = `Unexpected '${a}' after '${b}'.`; + break; + case "unexpected_a_before_b": + mm = `Unexpected '${a}' before '${b}'.`; + break; + case "unexpected_at_top_level_a": + mm = `Expected '${a}' to be in a function.`; + break; + case "unexpected_char_a": + mm = `Unexpected character '${a}'.`; + break; + case "unexpected_comment": + mm = `Unexpected comment.`; + break; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// case "unexpected_directive_a": +// mm = `When using modules, don't use directive '/\u002a${a}'.`; +// break; + + case "unexpected_expression_a": + mm = `Unexpected expression '${a}' in statement position.`; + break; + case "unexpected_label_a": + mm = `Unexpected label '${a}'.`; + break; + case "unexpected_parens": + mm = `Don't wrap function literals in parens.`; + break; + case "unexpected_space_a_b": + mm = `Unexpected space between '${a}' and '${b}'.`; + break; + case "unexpected_statement_a": + mm = `Unexpected statement '${a}' in expression position.`; + break; + case "unexpected_trailing_space": + mm = `Unexpected trailing space.`; + break; + case "unexpected_typeof_a": + mm = ( + `Unexpected 'typeof'. Use '===' to compare directly with ${a}.` + ); + break; + case "uninitialized_a": + mm = `Uninitialized '${a}'.`; + break; + case "unopened_enable": + mm = ( + `Directive '/*jslint-enable*/' was not opened` + + ` with '/*jslint-disable*/'.` + ); + break; + case "unreachable_a": + mm = `Unreachable '${a}'.`; + break; + case "unregistered_property_a": + mm = `Unregistered property name '${a}'.`; + break; + case "unused_a": + mm = `Unused '${a}'.`; + break; + case "use_double": + mm = `Use double quotes, not single quotes.`; + break; + +// PR-386 - Fix issue #382 - Make fart-related warnings more readable. + + case "use_function_not_fart": + mm = ( + `Use 'function (...)', not '(...) =>' when arrow functions` + + ` become too complex.` + ); + break; + case "use_open": + mm = ( + `Wrap a ternary expression in parens,` + + ` with a line break after the left paren.` + ); + break; + case "use_spaces": + mm = `Use spaces, not tabs.`; + break; + case "var_on_top": + mm = `Move variable declaration to top of function or script.`; + break; + case "var_switch": + mm = `Don't declare variables in a switch.`; + break; + case "weird_condition_a": + mm = `Weird condition '${a}'.`; + break; + case "weird_expression_a": + mm = `Weird expression '${a}'.`; + break; + case "weird_loop": + mm = `Weird loop.`; + break; + case "weird_property_a": + mm = `Weird property name '${a}'.`; + break; + case "weird_relation_a": + mm = `Weird relation '${a}'.`; + break; + case "wrap_condition": + mm = `Wrap the condition in parens.`; + break; + +// PR-386 - Fix issue #382 - Make fart-related warnings more readable. + + case "wrap_fart_parameter": + mm = `Wrap the parameter before '=>' in parens.`; + break; + case "wrap_immediate": + mm = ( + `Wrap an immediate function invocation in parentheses to assist` + + ` the reader in understanding that the expression is the` + + ` result of a function, and not the function itself.` + ); + break; + case "wrap_regexp": + mm = `Wrap this regexp in parens to avoid confusion.`; + break; + case "wrap_unary": + mm = `Wrap the unary expression in parens.`; + break; + } + +// Validate mm. + + jslint_assert(mm, code); + warning.message = mm; + +// PR-242 - Include stack_trace for jslint to debug itself for errors. + + if (option_dict.trace) { + warning.stack_trace = new Error().stack; + } + if (warning.directive_ignore_line) { + +// test_cause: +// ["0 //jslint-ignore-line", "semicolon", "directive_ignore_line", "", 0] + + test_cause("directive_ignore_line"); + return warning; + } + warning_list.push(warning); + return warning; + } + + try { + +// tokenize takes a source and produces from it an array of token objects. +// JavaScript is notoriously difficult to tokenize because of the horrible +// interactions between automatic semicolon insertion, regular expression +// literals, and now megastring literals. JSLint benefits from eliminating +// automatic semicolon insertion and nested megastring literals, which allows +// full tokenization to precede parsing. + + option_dict = Object.assign(empty(), option_dict); + Object.assign(state, { + artifact, + catch_list, + catch_stack, + directive_list, + export_dict, + function_list, + function_stack, + global_dict, + global_list, + import_list, + is_equal, + is_weird, + line_list, + mode_json: false, // true if parsing JSON. + mode_module: false, // true if import or export was used. + mode_property: false, // true if directive /*property*/ is + // ... used. + mode_shebang: false, // true if #! is seen on the first line. + option_dict, + property_dict, + source, + stop, + stop_at, + syntax_dict, + tenure, + test_cause, + token_global, + token_list, + token_nxt: token_global, + warn, + warn_at, + warning_list + }); + +// PHASE 1. Split by newlines into . + + jslint_phase1_split(state); + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 2. Lex into . + + jslint_phase2_lex(state); + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 3. Parse into using the Pratt-parser. + + jslint_phase3_parse(state); + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). + + if (!state.mode_json) { + jslint_phase4_walk(state); + } + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PHASE 5. Check whitespace between tokens in . + + if (!state.mode_json && warning_list.length === 0) { + jslint_phase5_whitage(state); + } + jslint_assert(catch_stack.length === 1, `catch_stack.length === 1.`); + jslint_assert( + function_stack.length === 0, + `function_stack.length === 0.` + ); + +// PR-347 - Disable warning "missing_browser". +// +// if (!option_dict.browser) { +// directive_list.forEach(function (comment) { +// if (comment.directive === "global") { +// +// // test_cause: +// // ["/*global aa*/", "jslint", "missing_browser", "(comment)", 1] +// +// warn("missing_browser", comment); +// } +// }); +// } + + if (option_dict.test_internal_error) { + jslint_assert(undefined, "test_internal_error"); + } + } catch (err) { + mode_stop = true; + err.message = "[JSLint was unable to finish] " + err.message; + err.mode_stop = true; + if (err.name !== "JSLintError") { + Object.assign(err, { + column: jslint_fudge, + line: jslint_fudge, + line_source: "", + stack_trace: err.stack + }); + } + if (warning_list.indexOf(err) === -1) { + warning_list.push(err); + } + } + +// Sort warning_list by mode_stop first, line, column respectively. + + warning_list.sort(function (aa, bb) { + return ( + Boolean(bb.mode_stop) - Boolean(aa.mode_stop) + || aa.line - bb.line + || aa.column - bb.column + ); + +// Update each warning with formatted_message ready-for-use by jslint_cli. + + }).map(function ({ + column, + line, + line_source, + message, + stack_trace = "" + }, ii, list) { + list[ii].formatted_message = String( + String(ii + 1).padStart(2, " ") + + ". \u001b[31m" + message + "\u001b[39m" + + " \u001b[90m\/\/ line " + line + ", column " + column + + "\u001b[39m\n" + + (" " + line_source.trim()).slice(0, 72) + "\n" + + stack_trace + ).trimRight(); + }); + + return { + causes: cause_dict, + directives: directive_list, + edition: jslint_edition, + exports: export_dict, + froms: import_list, + functions: function_list, + global: token_global, + id: "(JSLint)", + json: state.mode_json, + lines: line_list, + module: state.mode_module === true, + ok: warning_list.length === 0 && !mode_stop, + option: option_dict, + property: property_dict, + shebang: ( + state.mode_shebang + ? line_list[jslint_fudge].line_source + : undefined + ), + stop: mode_stop, + tokens: token_list, + tree: state.token_tree, + warnings: warning_list + }; +} + +// PR-362 - Add API Doc. + +async function jslint_apidoc({ + example_list, + github_repo, + module_list, + package_name, + pathname, + version +}) { + +// This function will create API Doc from . + + let elem_ii = 0; + let html; + + function elem_create(moduleObj, key, moduleName) { + +// This function will create a sub API Doc from elem []. + + let example = "N/A"; + let id = encodeURIComponent("apidoc.elem." + moduleName + "." + key); + let name; + let signature; + let source; + name = htmlEscape((typeof moduleObj[key]) + " " + key); + if (typeof moduleObj[key] !== "function") { + return { + name, + signature: (` + +${name} + + `), + source: (` +
  • +

    + + ${name} + +

    +
  • + `) + }; + } + // init source + source = htmlEscape(trim_start(moduleObj[key].toString())); + // init signature + source = source.replace(( + /(\([\S\s]*?\)) \{/ + ), function (match0, match1) { + signature = htmlEscape( + match1.replace(( + / *?\/\*[\S\s]*?\*\/ */g + ), "").replace(( + / *?\/\/.*/g + ), "").replace(( + /\n{2,}/g + ), "\n") + ); + return match0; + }); + // init comment + source = source.replace(( + /\n(?:\/\/.*?\n)+\n/ + ), "$&"); + // init example + example_list.some(function (example2) { + example2.replace( + new RegExp(( + "((?:\\n.*?){8}(function )?)\\b" + + key + + "(\\((?:.*?\\n){8})" + ), "g"), + function (ignore, header, isDeclaration, footer) { + if (!isDeclaration) { + example = "..." + trim_start( + htmlEscape(header) + + "" + + htmlEscape(key) + + "" + + htmlEscape(footer) + ).trimEnd() + "\n..."; + } + return ""; + } + ); + return example !== "N/A"; + }); + if (source.length > 2048) { + source = source.slice(0, 2048) + "...\n}\n"; + } + return { + name, + signature: (` + +${name}${signature} + + `), + source: (` +
  • +

    + + ${name}${signature} + +

    +
  • +
  • Description and source-code:
    ${source}
  • +
  • Example usage:
    ${example}
  • + `) + }; + } + + function trim_start(str) { + +// This function will normalize whitespace before . + + let whitespace = ""; + str.trim().replace(( + /^ */gm + ), function (match0) { + if (whitespace === "" || match0.length < whitespace.length) { + whitespace = match0; + } + return ""; + }); + str = str.replace(new RegExp("^" + whitespace, "gm"), ""); + return str; + } + await moduleFsInit(); + +// Html-escape params. + + github_repo = htmlEscape(github_repo); + package_name = htmlEscape(package_name); + version = htmlEscape(version); + +// Init example_list. + + example_list = await Promise.all(example_list.map(async function (file) { + +// This function will read example from given file. + + let result = await moduleFs.promises.readFile(file, "utf8"); + result = ( + "\n\n\n\n\n\n\n\n" + // bug-workaround - truncate example to manageable size + + result.slice(0, 524288) + + "\n\n\n\n\n\n\n\n" + ); + result = result.replace(( + /\r\n*/g + ), "\n"); + return result; + })); + +// Init module_list. + + module_list = await Promise.all(module_list.map(async function ({ + pathname + }) { + let moduleName = htmlEscape(JSON.stringify(pathname)); + let moduleObj = await import(pathname); + if (moduleObj.default) { + moduleObj = moduleObj.default; + } + return { + elem_list: Object.keys(moduleObj).map(function (key) { + return elem_create(moduleObj, key, moduleName); + }).sort(function (aa, bb) { + return ( + aa.name < bb.name + ? -1 + : 1 + ); + }).map(function (elem) { + elem_ii += 1; + elem.signature = elem.signature.replace( + ">", + ">" + elem_ii + ". " + ); + elem.source = elem.source.replace( + "\">", + "\">" + elem_ii + ". " + ); + return elem; + }), + id: encodeURIComponent("apidoc.module." + moduleName), + moduleName + }; + })); + html = (` + + + + + + +${package_name} apidoc + + + +
    +

    API Doc for ${package_name} (${version})

    +
    + +

    Table of Contents

    +
    +
      + `) + module_list.map(function ({ + elem_list, + id, + moduleName + }) { + return ( + (` +
    • + Module ${moduleName} +
        + `) + + elem_list.map(function ({ + signature + }) { + return "
      • \n" + signature + "\n
      • \n"; + }).join("") + + (` +
      +
    • + `) + ); + }).join("") + (` +
    +
    + `) + module_list.map(function ({ + elem_list, + id, + moduleName + }) { + return ( + (` +
    +

    Module ${moduleName}

    +
      + `) + + elem_list.map(function ({ + source + }) { + return source; + }).join("") + + (` +
    +
    + `) + ); + }).join("") + (` +
    + [ + This document was created with + JSLint + ] +
    +
    + + + `); + html = html.trim().replace(( + / +?$/gm + ), "") + "\n"; + await fsWriteFileWithParents(pathname, html); +} + +function jslint_assert(condition, message) { + +// This function will throw if is falsy. + + if (condition) { + return condition; + } + throw new Error( + `This was caused by a bug in JSLint. +Please open an issue with this stack-trace (and possible example-code) at +https://github.com/jslint-org/jslint/issues. +edition = "${jslint_edition}"; +${String(message).slice(0, 2000)}` + ); +} + +async function jslint_cli({ + console_error, + console_log, + file, + import_meta_url, + mode_cli, + mode_noop, + option, + process_argv, + process_env, + process_exit, + source +}) { + +// This function will run jslint from nodejs-cli. + + let command; + let data; + let exit_code = 0; + let mode_report; + let mode_wrapper_vim; + let result; + + function jslint_from_file({ + code, + file, + line_offset = 0, + mode_conditional, + option = empty() + }) { + let result_from_file; + if ( + mode_conditional + && !( + /^\/\*jslint\b/m + ).test(code.slice(0, 65536)) + ) { + return; + } + option = Object.assign(empty(), option, { + file + }); + switch (( + /\.\w+?$|$/m + ).exec(file)[0]) { + case ".html": + +// Recursively jslint embedded "". + + code.replace(( + /^]*?>\n([\S\s]*?\n)<\/script>$/gm + ), function (ignore, match1, ii) { + jslint_from_file({ + code: match1, + file: file + ". + + import_meta_url = import_meta_url || jslint_import_meta_url; + if ( + jslint_rgx_url_search_window_jslint.test(import_meta_url) + && (typeof globalThis === "object" && globalThis) + ) { + globalThis.jslint = jslint; + } + +// Feature-detect nodejs. + + if (!( + (typeof process === "object" && process) + && process.versions + && typeof process.versions.node === "string" + && !mode_noop + )) { + return exit_code; + } + console_error = console_error || console.error; + console_log = console_log || console.log; + process_argv = process_argv || process.argv; + process_env = process_env || process.env; + process_exit = process_exit || process.exit; + await moduleFsInit(); + if ( + !( + +// Feature-detect nodejs-cli. + + process.execArgv.indexOf("--eval") === -1 + && process.execArgv.indexOf("-e") === -1 + && ( + ( + /[\/|\\]jslint(?:\.[cm]?js)?$/m + ).test(process_argv[1]) + || mode_cli + ) + && ( + moduleUrl.fileURLToPath(import_meta_url) + === + modulePath.resolve(process_argv[1]) + ) + ) + && !mode_cli + ) { + return exit_code; + } + +// init commmand + + command = String(process_argv[2]).split("="); + command[1] = command.slice(1).join("="); + + switch (command[0]) { + +// PR-362 - Add API Doc. + + case "jslint_apidoc": + await jslint_apidoc(Object.assign(JSON.parse(process_argv[3]), { + pathname: command[1] + })); + return; + +// PR-363 - Add command jslint_report. + + case "jslint_report": + mode_report = command[1]; + process_argv = process_argv.slice(1); + break; + +// COMMIT-b26d6df2 - Add command jslint_wrapper_vim. + + case "jslint_wrapper_vim": + mode_wrapper_vim = true; + process_argv = process_argv.slice(1); + break; + +// PR-364 - Add command v8_coverage_report. + + case "v8_coverage_report": + await v8CoverageReportCreate({ + consoleError: console_error, + coverageDir: command[1], + processArgv: process_argv.slice(3) + }); + return; + } + +// Normalize file relative to process.cwd(). + + process_argv.slice(2).some(function (arg) { + if (!arg.startsWith("-")) { + file = file || arg; + return true; + } + }); + if (!file) { + return; + } + file = modulePath.resolve(file) + "/"; + if (file.startsWith(process.cwd() + "/")) { + file = file.replace(process.cwd() + "/", "").slice(0, -1) || "."; + } + file = file.replace(( + /\\/g + ), "/").replace(( + /\/$/g + ), ""); + if (source) { + data = source; + } else { + +// jslint_cli - jslint directory. + + try { + data = await moduleFs.promises.readdir(file, "utf8"); + } catch (ignore) {} + if (data) { + await Promise.all(data.map(async function (file2) { + let code; + let time_start = Date.now(); + file2 = file + "/" + file2; + switch (( + /\.\w+?$|$/m + ).exec(file2)[0]) { + case ".cjs": + case ".html": + case ".js": + case ".json": + case ".md": + case ".mjs": + case ".sh": + break; + default: + return; + } + try { + code = await moduleFs.promises.readFile(file2, "utf8"); + } catch (ignore) { + return; + } + if ( + ( + /(?:\b|_)(?:lock|min|raw|rollup)(?:\b|_)/ + ).test(file2) + || !(code && code.length < 1048576) + ) { + return; + } + jslint_from_file({ + code, + file: file2, + option + }); + console_error( + "jslint - " + (Date.now() - time_start) + "ms - " + file2 + ); + })); + process_exit(exit_code); + return exit_code; + } + +// jslint_cli - jslint file. + + try { + data = await moduleFs.promises.readFile(file, "utf8"); + } catch (err) { + console_error(err); + exit_code = 1; + process_exit(exit_code); + return exit_code; + } + } + result = jslint_from_file({ + code: data, + file, + option + }); + if (mode_report) { + result = jslint.jslint_report(result); + result = `\n${result}\n`; + await fsWriteFileWithParents(mode_report, result); + } + process_exit(exit_code); + return exit_code; +} + +function jslint_phase1_split() { + +// PHASE 1. Split by newlines into . + + return; +} + +function jslint_phase2_lex(state) { + +// PHASE 2. Lex into . + + let { + artifact, + directive_list, + global_dict, + global_list, + line_list, + option_dict, + stop, + stop_at, + tenure, + test_cause, + token_global, + token_list, + warn, + warn_at + } = state; + let char; // The current character being lexed. + let column = 0; // The column number of the next character. + let from; // The starting column number of the token. + let from_mega; // The starting column of megastring. + let line = 0; // The line number of the next character. + let line_disable; // The starting line of "/*jslint-disable*/". + let line_mega; // The starting line of megastring. + let line_source = ""; // The remaining line source string. + let line_whole = ""; // The whole line source string. + let mode_digits_empty_string = 1; + let mode_digits_numeric_separator = 2; + let mode_directive = true; // true if directives are still allowed. + let mode_mega = false; // true if currently parsing a megastring + // ... literal. + let mode_regexp; // true if regular expression literal seen on + // ... this line. + let paren_backtrack_list = []; // List of most recent "(" tokens at any + // ... paren-depth. + let paren_depth = 0; // Keeps track of current paren-depth. + let snippet = ""; // A piece of string. + let token_1; // The first token. + let token_prv = token_global; // The previous token including + // ... comments. + let token_prv_expr = token_global; // The previous token excluding + // ... comments. + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions char_after, char_before, +// read_digits, and char_after_escape help in the parsing of literals. + + function char_after(match) { + +// Get the next character from the source line. Remove it from the line_source, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + +// test_cause: +// ["aa=/[", "char_after", "expected_a", "]", 5] +// ["aa=/aa{/", "char_after", "expected_a_b", "/", 8] + + return ( + char === "" + ? stop_at("expected_a", line, column - 1, match) + : stop_at("expected_a_b", line, column, match, char) + ); + } + char = line_source.slice(0, 1); + line_source = line_source.slice(1); + snippet += char || " "; + column += 1; + return char; + } + + function char_after_escape(extra) { + +// Validate char after escape "\\". + + char_after("\\"); + switch (char) { + case "": + +// test_cause: +// ["\"\\", "char_after_escape", "unclosed_string", "", 2] + + return stop_at("unclosed_string", line, column); + case "/": + return char_after(); + case "\\": + return char_after(); + case "`": + return char_after(); + case "b": + return char_after(); + case "f": + return char_after(); + case "n": + return char_after(); + case "r": + return char_after(); + case "t": + +// test_cause: +// ["\"\\/\\\\\\`\\b\\f\\n\\r\\t\"", "char_after_escape", "char_after", "", 0] + + test_cause("char_after"); + return char_after(); + case "u": + if (char_after("u") === "{") { + if (state.mode_json) { + +// test_cause: +// ["[\"\\u{12345}\"]", "char_after_escape", "unexpected_a", "{", 5] + + warn_at("unexpected_a", line, column, char); + } + if (read_digits("x", undefined) > 5) { + +// test_cause: +// ["\"\\u{123456}\"", "char_after_escape", "too_many_digits", "", 11] + + warn_at("too_many_digits", line, column); + } + if (char !== "}") { + +// test_cause: +// ["\"\\u{12345\"", "char_after_escape", "expected_a_before_b", "\"", 10] + + stop_at("expected_a_before_b", line, column, "}", char); + } + return char_after(); + } + char_before(); + if (read_digits("x", mode_digits_empty_string) < 4) { + +// test_cause: +// ["\"\\u0\"", "char_after_escape", "expected_four_digits", "", 5] + + warn_at("expected_four_digits", line, column); + } + return; + default: + if (extra && extra.indexOf(char) >= 0) { + return char_after(); + } + +// test_cause: +// ["\"\\0\"", "char_after_escape", "unexpected_a_before_b", "0", 3] + + warn_at("unexpected_a_before_b", line, column, "\\", char); + } + } + + function char_before() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the line_source. + + char = snippet.slice(-1); + line_source = char + line_source; + column -= char.length; + +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + return char; + } + + function check_numeric_separator(digits, column) { + +// This function will check for illegal numeric-separator in . + + digits.replace(( + jslint_rgx_numeric_separator_illegal + ), function (ignore, ii) { + +// test_cause: +// ["0x0_0_;", "check_numeric_separator", "illegal_num_separator", "", 6] +// ["0x0_0__0;", "check_numeric_separator", "illegal_num_separator", "", 6] +// ["aa=1_2_;", "check_numeric_separator", "illegal_num_separator", "", 7] +// ["aa=1_2__3;", "check_numeric_separator", "illegal_num_separator", "", 7] +// ["aa=1_2_n;", "check_numeric_separator", "illegal_num_separator", "", 7] + + warn_at("illegal_num_separator", line, column + ii + 1); + return ""; + }); + } + + function lex_comment() { + let body; + let ii = 0; + let jj = 0; + let the_comment; + +// Create a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + +// Create token from comment //.... + + if (snippet === "//") { + snippet = line_source; + line_source = ""; + the_comment = token_create("(comment)", snippet); + if (mode_mega) { + +// test_cause: +// ["`${//}`", "lex_comment", "unexpected_comment", "`", 4] + + warn("unexpected_comment", the_comment, "`"); + } + +// Create token from comment /*...*/. + + } else { + snippet = []; + if (line_source[0] === "/") { + +// test_cause: +// ["/*/", "lex_comment", "unexpected_a", "/", 2] + + warn_at("unexpected_a", line, column + ii, "/"); + } + +// Lex/loop through each line until "*/". + + while (true) { + // jslint_rgx_star_slash + ii = line_source.indexOf("*/"); + if (ii >= 0) { + break; + } + // jslint_rgx_slash_star + ii = line_source.indexOf("/*"); + if (ii >= 0) { + +// test_cause: +// ["/*/*", "lex_comment", "nested_comment", "", 2] + + warn_at("nested_comment", line, column + ii); + } + snippet.push(line_source); + line_source = read_line(); + if (line_source === undefined) { + +// test_cause: +// ["/*", "lex_comment", "unclosed_comment", "", 1] + + return stop_at("unclosed_comment", line, column); + } + } + jj = line_source.slice(0, ii).search( + jslint_rgx_slash_star_or_slash + ); + if (jj >= 0) { + +// test_cause: +// ["/*/**/", "lex_comment", "nested_comment", "", 2] + + warn_at("nested_comment", line, column + jj); + } + snippet.push(line_source.slice(0, ii)); + snippet = snippet.join(" "); + column += ii + 2; + line_source = line_source.slice(ii + 2); + the_comment = token_create("(comment)", snippet); + } + +// Uncompleted work comment. + + if (!option_dict.devel && jslint_rgx_todo.test(snippet)) { + +// test_cause: +// ["//todo", "lex_comment", "todo_comment", "(comment)", 1] //jslint-ignore-line + + warn("todo_comment", the_comment); + } + +// Lex directives in comment. + + [ + the_comment.directive, body + ] = Array.from(snippet.match(jslint_rgx_directive) || []).slice(1); + if (the_comment.directive === undefined) { + return the_comment; + } + directive_list.push(the_comment); + if (!mode_directive) { + +// test_cause: +// ["0\n/*global aa*/", "lex_comment", "misplaced_directive_a", "global", 1] + + warn_at("misplaced_directive_a", line, from, the_comment.directive); + return the_comment; + } + +// lex_directive(); +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + +// Lex/loop through each directive in /*...*/ + + ii = 0; + body.replace(jslint_rgx_directive_part, function ( + match0, + key, + val, + jj + ) { + if (ii !== jj) { + +// test_cause: +// ["/*jslint !*/", "lex_comment", "bad_directive_a", "!", 1] + + return stop("bad_directive_a", the_comment, body.slice(ii)); + } + if (match0 === "") { + return ""; + } + ii += match0.length; + switch (the_comment.directive) { + case "global": + if (val) { + +// test_cause: +// ["/*global aa:false*/", "lex_comment", "bad_option_a", "aa:false", 1] + + warn("bad_option_a", the_comment, key + ":" + val); + } + global_dict[key] = "user-defined"; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// state.mode_module = the_comment; + + break; + case "jslint": + if (!option_set_item(key, val !== "false")) { + +// test_cause: +// ["/*jslint undefined*/", "lex_comment", "bad_option_a", "undefined", 1] + + warn("bad_option_a", the_comment, key); + } + break; + case "property": + state.mode_property = true; + tenure[key] = true; + break; + } + return ""; + }); + return the_comment; + } + + function lex_megastring() { + let id; + let match; + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (mode_mega) { + +// test_cause: +// ["`${`", "lex_megastring", "expected_a_b", "`", 4] + + return stop_at("expected_a_b", line, column, "}", "`"); + } + from_mega = from; + line_mega = line; + mode_mega = true; + snippet = ""; + +// Parsing a mega literal is tricky. First create a ` token. + + token_create("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + while (true) { + match = line_source.match(jslint_rgx_mega) || { + "0": "", + index: 0 + }; + snippet += line_source.slice(0, match.index); + column += match.index; + line_source = line_source.slice(match.index); + match = match[0]; + switch (match) { + case "${": + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + token_create("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then create tokens that will become part of an expression until +// a } token is made. + + column += 2; + token_create("${"); + line_source = line_source.slice(2); + +// Lex/loop through each token inside megastring-expression `${...}`. + + while (true) { + id = lex_token().id; + if (id === "{") { + +// test_cause: +// ["`${{", "lex_megastring", "expected_a_b", "{", 4] + + return stop_at("expected_a_b", line, column, "}", "{"); + } + if (id === "}") { + break; + } + } + break; + case "\\": + snippet += line_source.slice(0, 2); + line_source = line_source.slice(2); + column += 2; + break; + case "`": + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + token_create("(string)", snippet).quote = "`"; + snippet = ""; + +// Terminate megastring with `. + + line_source = line_source.slice(1); + column += 1; + mode_mega = false; + return token_create("`"); + default: + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + snippet += line_source + "\n"; + if (read_line() === undefined) { + +// test_cause: +// ["`", "lex_megastring", "unclosed_mega", "", 1] + + return stop_at("unclosed_mega", line_mega, from_mega); + } + } + } + } + + function lex_number() { + let prefix = snippet; + +// PR-390 - Add numeric-separator check. + + check_numeric_separator(prefix, column - prefix.length); + char_after(); + switch (prefix === "0" && char) { + case "b": + case "o": + case "x": + read_digits(char, mode_digits_numeric_separator); + +// PR-351 - Ignore BigInt suffix 'n'. + + if (char === "n") { + char_after("n"); + } + break; + default: + if (char === ".") { + read_digits("d", mode_digits_numeric_separator); + } + if (char === "E" || char === "e") { + char_after(char); + if (char !== "+" && char !== "-") { + char_before(); + } + read_digits("d", mode_digits_numeric_separator); + } + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + +// test_cause: +// ["0a", "lex_number", "unexpected_a_after_b", "0", 2] + + return stop_at( + "unexpected_a_after_b", + line, + column, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + char_before(); + return token_create("(number)", snippet); + } + + function lex_regexp() { + +// Regexp +// Lex a regular expression literal. + + let flag; + let mode_regexp_multiline; + let result; + let value; + mode_regexp = true; + + function lex_regexp_bracketed() { + let mode_regexp_range; + +// RegExp +// Match a class. + + char_after("["); + if (char === "^") { + char_after("^"); + } + while (true) { + +// RegExp +// Match a character in a character class. + + switch (char) { + case "": + case "]": + +// test_cause: +// ["aa=/[", "lex_regexp_bracketed", "closer", "", 0] +// ["aa=/[]/", "lex_regexp_bracketed", "closer", "", 0] + + test_cause("closer"); + if (mode_regexp_range) { + +// test_cause: +// ["aa=/[0-]/", "lex_regexp_bracketed", "unexpected_a", "-", 7] + + warn_at("unexpected_a", line, column - 1, "-"); + } + return char_after("]"); + +// PR-362 - Relax regexp-warning against using . +// +// case " ": +// +// // test_cause: +// // ["aa=/[ ]/", "lex_regexp_bracketed", "expected_a_b", " ", 6] +// +// warn_at("expected_a_b", line, column, "\\u0020", " "); +// break; + + case "-": + case "/": + case "[": + case "^": + +// test_cause: +// ["aa=/[-]/", "lex_regexp_bracketed", "expected_a_before_b", "-", 6] +// ["aa=/[.^]/", "lex_regexp_bracketed", "expected_a_before_b", "^", 7] +// ["aa=/[/", "lex_regexp_bracketed", "expected_a_before_b", "/", 6] +// ["aa=/[\\\\/]/", "lex_regexp_bracketed", "expected_a_before_b", "/", 8] +// ["aa=/[\\\\[]/", "lex_regexp_bracketed", "expected_a_before_b", "[", 8] + + warn_at("expected_a_before_b", line, column, "\\", char); + break; + case "\\": + char_after_escape("BbDdSsWw-[]^"); + char_before(); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${/[`]/}`", "lex_regexp_bracketed", "unexpected_a", "`", 6] + + warn_at("unexpected_a", line, column, "`"); + } + break; + } + char_after(); + mode_regexp_range = false; + if (char === "-") { + +// RegExp +// Match a range of subclasses. + + mode_regexp_range = true; + char_after("-"); + } + } + } + + function lex_regexp_group() { + +// RegExp +// Lex sequence of characters in regexp. + + switch (char) { + case "": + warn_at("expected_regexp_factor_a", line, column, char); + break; + case ")": + warn_at("expected_regexp_factor_a", line, column, char); + break; + case "]": + +// test_cause: +// ["/ /", "lex_regexp_group", "expected_regexp_factor_a", "", 3] +// ["aa=/)", "lex_regexp_group", "expected_regexp_factor_a", ")", 5] +// ["aa=/]", "lex_regexp_group", "expected_regexp_factor_a", "]", 5] + + warn_at("expected_regexp_factor_a", line, column, char); + break; + } + while (true) { + switch (char) { + case "": + case ")": + case "/": + case "]": + return; + +// PR-362 - Relax regexp-warning against using . +// +// case " ": +// +// // test_cause: +// // ["aa=/ /", "lex_regexp_group", "expected_a_b", " ", 5] +// +// warn_at("expected_a_b", line, column, "\\s", " "); +// char_after(); +// break; + + case "$": + if (line_source[0] !== "/") { + mode_regexp_multiline = true; + } + char_after(); + break; + case "(": + +// RegExp +// Match a group that starts with left paren. + + char_after("("); + switch (char) { + case ":": + +// test_cause: +// ["aa=/(:)/", "lex_regexp_group", "expected_a_before_b", ":", 6] +// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5] + + warn_at("expected_a_before_b", line, column, "?", ":"); + break; + case "?": + char_after("?"); + switch (char) { + case "!": + +// PR-437 - Add grammar for regexp-named-capture-group. + + case "<": + case "=": + char_after(); + break; + default: + char_after(":"); + } + break; + } + +// RegExp +// Recurse lex_regexp_group(). + + lex_regexp_group(); + char_after(")"); + break; + case "*": + case "+": + case "?": + case "{": + case "}": + +// test_cause: +// ["aa=/+/", "lex_regexp_group", "expected_a_before_b", "+", 5] +// ["aa=/.**/", "lex_regexp_group", "expected_a_before_b", "*", 7] +// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5] +// ["aa=/{/", "lex_regexp_group", "expected_a_before_b", "{", 5] +// ["aa=/}/", "lex_regexp_group", "expected_a_before_b", "}", 5] + + warn_at("expected_a_before_b", line, column, "\\", char); + char_after(); + break; + case "[": + lex_regexp_bracketed(); + break; + case "\\": + +// test_cause: +// ["aa=/\\/", "lex_regexp_group", "escape", "", 0] + + test_cause("escape"); + +// PR-437 - Add grammar for regexp-named-backreference. + + char_after_escape("BbDdSsWw^${}[]():=!.|*+?k"); + break; + case "^": + if (snippet !== "^") { + mode_regexp_multiline = true; + } + char_after(); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${/`/}`", "lex_regexp_group", "unexpected_a", "`", 5] + + warn_at("unexpected_a", line, column, "`"); + } + char_after(); + break; + default: + char_after(); + } + +// RegExp +// Match an optional quantifier. + + switch (char) { + case "*": + case "+": + if (char_after(char) === "?") { + +// test_cause: +// ["aa=/.*?/", "lex_regexp_group", "?", "", 0] +// ["aa=/.+?/", "lex_regexp_group", "?", "", 0] + + test_cause("?"); + char_after("?"); + } + break; + case "?": + if (char_after("?") === "?") { + +// test_cause: +// ["aa=/.??/", "lex_regexp_group", "unexpected_a", "?", 7] + + warn_at("unexpected_a", line, column, char); + char_after("?"); + } + break; + case "{": + if (read_digits("d", mode_digits_empty_string) === 0) { + +// test_cause: +// ["aa=/aa{/", "lex_regexp_group", "expected_a_before_b", ",", 8] + + warn_at("expected_a_before_b", line, column, "0", ","); + } + if (char === ",") { + +// test_cause: +// ["aa=/.{,/", "lex_regexp_group", "comma", "", 0] + + test_cause("comma"); + read_digits("d", mode_digits_empty_string); + } + if (char_after("}") === "?") { + +// test_cause: +// ["aa=/.{0}?/", "lex_regexp_group", "unexpected_a", "?", 9] + + warn_at("unexpected_a", line, column, char); + char_after("?"); + } + break; + } + } + } + +// RegExp +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + char_after(); + if (char === "=") { + +// test_cause: +// ["aa=/=/", "lex_regexp", "expected_a_before_b", "=", 5] + + warn_at("expected_a_before_b", line, column, "\\", "="); + } + lex_regexp_group(); + +// RegExp +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + +// RegExp +// Make sure there is a closing slash. + + value = snippet; + char_after("/"); + +// RegExp +// Create flag. + + flag = empty(); + while ( + +// Regexp +// char is a letter. + + (char >= "a" && char <= "z\uffff") + || (char >= "A" && char <= "Z\uffff") + ) { + +// RegExp +// Process dangling flag letters. + + switch (!flag[char] && char) { + case "g": + break; + case "i": + break; + case "m": + break; + case "u": + break; + case "y": + +// test_cause: +// ["aa=/./gimuy", "lex_regexp", "flag", "", 0] + + test_cause("flag"); + break; + default: + +// test_cause: +// ["aa=/./gg", "lex_regexp", "unexpected_a", "g", 8] +// ["aa=/./z", "lex_regexp", "unexpected_a", "z", 7] + + warn_at("unexpected_a", line, column, char); + } + flag[char] = true; + char_after(); + } + char_before(); + if (char === "/" || char === "*") { + +// test_cause: +// ["aa=/.//", "lex_regexp", "unexpected_a", "/", 3] + + return stop_at("unexpected_a", line, from, char); + } + result = token_create("(regexp)", char); + result.flag = flag; + result.value = value; + if (mode_regexp_multiline && !flag.m) { + +// test_cause: +// ["aa=/$^/", "lex_regexp", "missing_m", "", 7] + + warn_at("missing_m", line, column); + } + return result; + } + + function lex_slash_or_regexp() { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + let the_token; + switch ( + token_prv_expr.identifier + && !token_prv_expr.dot + && token_prv_expr.id + ) { + case "case": + case "delete": + case "in": + case "instanceof": + case "new": + case "typeof": + case "void": + case "yield": + the_token = lex_regexp(); + +// test_cause: +// ["case /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6] +// ["delete /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8] +// ["in /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 4] +// ["instanceof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 12] +// ["new /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 5] +// ["typeof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8] +// ["void /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6] +// ["yield /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 7] + + return stop("unexpected_a", the_token); + case "return": + return lex_regexp(); + } + switch (!token_prv_expr.identifier && token_prv_expr.id.slice(-1)) { + case "!": + case "%": + case "&": + case "*": + case "+": + case "-": + case "/": + case ";": + case "<": + case ">": + case "^": + case "{": + case "|": + case "}": + case "~": + the_token = lex_regexp(); + +// test_cause: +// ["!/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["%/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["&/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["+/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["-/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["0 * /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5] +// ["0 / /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5] +// [";/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["^/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["{/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["|/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["}/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["~/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] + + warn("wrap_regexp", the_token); + return the_token; + case "(": + case ",": + case ":": + case "=": + case "?": + case "[": + +// test_cause: +// ["(/./", "lex_slash_or_regexp", "recurse", "", 0] +// [",/./", "lex_slash_or_regexp", "recurse", "", 0] +// [":/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["=/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["?/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["aa[/./", "lex_slash_or_regexp", "recurse", "", 0] + + test_cause("recurse"); + return lex_regexp(); + } + if (line_source[0] === "=") { + column += 1; + line_source = line_source.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + return token_create(snippet); + } + + function lex_string(quote) { + +// Create a string token. + + let the_token; + if (!option_dict.single && quote === "'") { + +// test_cause: +// ["''", "lex_string", "use_double", "", 1] + + warn_at("use_double", line, column); + } + snippet = ""; + char_after(); + +// Lex/loop through each character in "...". + + while (true) { + switch (char) { + case "": + +// test_cause: +// ["\"", "lex_string", "unclosed_string", "", 1] + + return stop_at("unclosed_string", line, column); + case "\\": + char_after_escape(quote); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${\"`\"}`", "lex_string", "unexpected_a", "`", 5] + + warn_at("unexpected_a", line, column, "`"); + } + char_after("`"); + break; + case quote: + +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + the_token = token_create("(string)", snippet); + the_token.quote = quote; + return the_token; + default: + char_after(); + } + } + } + + function lex_token() { + let match; + +// Lex/loop through each whitespace. + + while (true) { + +// Lex/loop through each blank-line. + + while (!line_source) { + line_source = read_line(); + from = 0; + if (line_source === undefined) { + return ( + mode_mega + +// test_cause: +// ["`${//}`", "lex_token", "unclosed_mega", "", 1] + + ? stop_at("unclosed_mega", line_mega, from_mega) + : line_disable !== undefined + +// test_cause: +// ["/*jslint-disable*/", "lex_token", "unclosed_disable", "", 1] + + ? stop_at("unclosed_disable", line_disable) + : token_create("(end)") + ); + } + } + from = column; + match = line_source.match(jslint_rgx_token); + +// match[1] token +// match[2] whitespace +// match[3] identifier +// match[4] number +// match[5] rest + + if (!match) { + +// test_cause: +// ["#", "lex_token", "unexpected_char_a", "#", 1] + + return stop_at( + "unexpected_char_a", + line, + column, + line_source[0] + ); + } + snippet = match[1]; + column += snippet.length; + line_source = match[5]; + if (!match[2]) { + break; + } + } + +// The token is an identifier. + + if (match[3]) { + return token_create(snippet, undefined, true); + } + +// Create token from number. + + if (match[4]) { + return lex_number(); + } + +// Create token from string "..." or '...'. + + if (snippet === "\"" || snippet === "'") { + return lex_string(snippet); + } + +// Create token from megastring `...`. + + if (snippet === "`") { + return lex_megastring(); + } + +// Create token from comment /*...*/ or //.... + + if (snippet === "/*" || snippet === "//") { + return lex_comment(); + } + +// Create token from slash /. + + if (snippet === "/") { + return lex_slash_or_regexp(); + } + return token_create(snippet); + } + + function option_set_item(key, val) { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + switch (key) { + case "beta": // Enable experimental warnings. + case "bitwise": // Allow bitwise operator. + case "browser": // Assume browser environment. + case "convert": // Allow conversion operator. + case "couch": // Assume CouchDb environment. + case "devel": // Allow console.log() and friends. + case "ecma": // Assume ECMAScript environment. + case "eval": // Allow eval(). + case "fart": // Allow complex fat-arrow. + case "for": // Allow for-statement. + case "getset": // Allow get() and set(). + case "indent2": // Use 2-space indent. + case "long": // Allow long lines. + case "node": // Assume Node.js environment. + case "nomen": // Allow weird property name. + case "single": // Allow single-quote strings. + case "subscript": // Allow identifier in subscript-notation. + case "test_cause": // Test jslint's causes. + case "test_internal_error": // Test jslint's internal-error + // ... handling-ability. + case "this": // Allow 'this'. + case "trace": // Include jslint stack-trace in warnings. + case "unordered": // Allow unordered cases, params, properties, + // ... variables, and exports. + case "variable": // Allow unordered const and let declarations + // ... that are not at top of function-scope. + case "white": // Allow messy whitespace. + option_dict[key] = val; + break; + +// PR-404 - Alias "evil" to jslint-directive "eval" for backwards-compat. + + case "evil": + return option_set_item("eval", val); + +// PR-404 - Alias "nomen" to jslint-directive "name" for backwards-compat. + + case "name": + return option_set_item("nomen", val); + default: + return false; + } + +// Initialize global-variables. + + switch (val && key) { + +// Assign global browser variables to global_dict. +/* +// /\*jslint beta, browser, devel*\/ +console.log(JSON.stringify(Object.keys(window).sort(), undefined, 4)); +*/ + + case "browser": + object_assign_from_list(global_dict, [ + +// Shared with Node.js. + + "AbortController", + // "Buffer", + // "Crypto", + // "CryptoKey", + "Event", + "EventTarget", + "MessageChannel", + "MessageEvent", + "MessagePort", + // "Request", + // "Response", + // "SubtleCrypto", + "TextDecoder", + "TextEncoder", + "URL", + "URLSearchParams", + "WebAssembly", + // "__dirname", + // "__filename", + // "atob", + // "btoa", + // "clearImmediate", + "clearInterval", + "clearTimeout", + // "console", + // "crypto", + // "exports", + // "fetch", + // "global", + // "module", + "performance", + // "process", + "queueMicrotask", + // "require", + // "setImmediate", + "setInterval", + "setTimeout", + +// Web worker only. +// https://github.com/mdn/content/blob/main/files/en-us/web/api +// /workerglobalscope/index.md + + "importScripts", + +// Window. + + "Blob", + // "CharacterData", + // "DocumentType", + // "Element", + // "Event", + "FileReader", + // "FontFace", + "FormData", + "IntersectionObserver", + "MutationObserver", + // "Storage", + // "TextDecoder", + // "TextEncoder", + // "URL", + "Worker", + "XMLHttpRequest", + // "caches", + // "clearInterval", + // "clearTimeout", + "document", + // "event", + "fetch", + // "history", + "indexedDb", + "localStorage", + "location", + // "name", + "navigator", + "postMessage", + // "screen", + "sessionStorage", + // "setInterval", + // "setTimeout", + "structuredClone", + "window" + ], "browser"); + break; + +// https://docs.couchdb.org/en/stable/query-server/javascript.html#javascript + + case "couch": + object_assign_from_list(global_dict, [ + "emit", + "getRow", + "isArray", + "log", + "provides", + "registerType", + "require", + "send", + "start", + "sum", + "toJSON" + ], "CouchDb"); + break; + case "devel": + object_assign_from_list(global_dict, [ + "alert", "confirm", "console", "prompt" + ], "development"); + break; + +// These are the globals that are provided by the language standard. +// Assign global ECMAScript variables to global_dict. +/* +node --input-type=module --eval ' +// /\*jslint beta, node*\/ +import https from "https"; +(async function () { + let dict = {import: true}; + let result = ""; + await new Promise(function (resolve) { + https.get(( + "https://raw.githubusercontent.com/mdn/content/main/files" + + "/en-us/web/javascript/reference/global_objects/index.md" + ), function (res) { + res.on("data", function (chunk) { + result += chunk; + }).on("end", resolve).setEncoding("utf8"); + }); + }); + result.replace(( + /\n- \{\{JSxRef\("(?:Global_Objects\/)?([^"\/]+?)"/g + ), function (ignore, key) { + if (globalThis.hasOwnProperty(key)) { + dict[key] = true; + } + return ""; + }); + console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4)); +}()); +' +*/ + + case "ecma": + object_assign_from_list(global_dict, [ + "AggregateError", + "Array", + "ArrayBuffer", + "Atomics", + "BigInt", + "BigInt64Array", + "BigUint64Array", + "Boolean", + "DataView", + "Date", + "Error", + "EvalError", + "Float32Array", + "Float64Array", + "Function", + "Infinity", + "Int16Array", + "Int32Array", + "Int8Array", + "Intl", + "JSON", + "Map", + "Math", + "NaN", + "Number", + "Object", + "Promise", + "Proxy", + "RangeError", + "ReferenceError", + "Reflect", + "RegExp", + "Set", + "SharedArrayBuffer", + "String", + "Symbol", + "SyntaxError", + "TypeError", + "URIError", + "Uint16Array", + "Uint32Array", + "Uint8Array", + "Uint8ClampedArray", + "WeakMap", + "WeakSet", + "WebAssembly", + "decodeURI", + "decodeURIComponent", + "encodeURI", + "encodeURIComponent", + "eval", + "globalThis", + "import", + "isFinite", + "isNaN", + "parseFloat", + "parseInt", + "undefined" + ], "ECMAScript"); + break; + +// Assign global Node.js variables to global_dict. +/* +node --input-type=module --eval ' +// /\*jslint beta, node*\/ +import moduleHttps from "https"; +(async function () { + let dict = Object.create(null); + let result = ""; + await new Promise(function (resolve) { + moduleHttps.get(( + "https://raw.githubusercontent.com/nodejs/node/v16.x/doc/api" + + "/globals.md" + ), function (res) { + res.on("data", function (chunk) { + result += chunk; + }).on("end", resolve).setEncoding("utf8"); + }); + }); + result.replace(( + /\n(?:\* \[`|## |## Class: )`\w+/g + ), function (match0) { + dict[match0.split("`")[1]] = true; + return ""; + }); + console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4)); +}()); +' +*/ + + case "node": + object_assign_from_list(global_dict, [ + "AbortController", + "Buffer", + // "Crypto", + // "CryptoKey", + "Event", + "EventTarget", + "MessageChannel", + "MessageEvent", + "MessagePort", + // "Request", + // "Response", + // "SubtleCrypto", + "TextDecoder", + "TextEncoder", + "URL", + "URLSearchParams", + "WebAssembly", + "__dirname", + "__filename", + // "atob", + // "btoa", + "clearImmediate", + "clearInterval", + "clearTimeout", + "console", + // "crypto", + "exports", + // "fetch", + "global", + "module", + "performance", + "process", + "queueMicrotask", + "require", + "setImmediate", + "setInterval", + "setTimeout" + ], "Node.js"); + break; + } + return true; + } + + function read_digits(base, mode) { + let digits = line_source.match( + base === "b" + ? jslint_rgx_digits_bits + : base === "o" + ? jslint_rgx_digits_octals + : base === "x" + ? jslint_rgx_digits_hexs + : jslint_rgx_digits_decimals + )[0]; + if ( + (mode !== mode_digits_empty_string && digits.length === 0) + || digits[0] === "_" + ) { + +// test_cause: +// ["0x", "read_digits", "expected_digits_after_a", "0x", 2] +// ["0x_", "read_digits", "expected_digits_after_a", "0x", 2] + + warn_at("expected_digits_after_a", line, column, snippet); + } + +// PR-390 - Add numeric-separator check. + + if (mode === mode_digits_numeric_separator) { + check_numeric_separator(digits, column); + } else if (digits.indexOf("_") >= 0) { + +// test_cause: +// ["\"\\u{1_2}\"", "read_digits", "illegal_num_separator", "", 6] + + warn_at( + "illegal_num_separator", + line, + column + digits.indexOf("_") + 1 + ); + } + column += digits.length; + line_source = line_source.slice(digits.length); + snippet += digits; + char_after(); + return digits.length; + } + + function read_line() { + +// Put the next line of source in line_source. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + if ( + !option_dict.long + && line_whole.length > 80 + && line_disable === undefined + && !state.mode_json + && token_1 + && !mode_regexp + ) { + +// test_cause: +// ["/////////////////////////////////////////////////////////////////////////////////", "read_line", "too_long", "", 1] //jslint-ignore-line + + warn_at("too_long", line); + } + column = 0; + line += 1; + mode_regexp = false; + line_source = undefined; + line_whole = ""; + if (line_list[line] === undefined) { + return line_source; + } + line_source = line_list[line].line_source; + line_whole = line_source; + +// Scan each line for following ignore-directives: +// "/*jslint-disable*/" +// "/*jslint-enable*/" +// "//jslint-ignore-line" + + if (line_source === "/*jslint-disable*/") { + +// test_cause: +// ["/*jslint-disable*/", "read_line", "jslint_disable", "", 0] + + test_cause("jslint_disable"); + line_disable = line; + } else if (line_source === "/*jslint-enable*/") { + if (line_disable === undefined) { + +// test_cause: +// ["/*jslint-enable*/", "read_line", "unopened_enable", "", 1] + + stop_at("unopened_enable", line); + } + line_disable = undefined; + } else if ( + line_source.endsWith(" //jslint-ignore-line") + || line_source.endsWith(" //jslint-quiet") + ) { + +// test_cause: +// ["0 //jslint-ignore-line", "read_line", "jslint_ignore_line", "", 0] + + test_cause("jslint_ignore_line"); + line_list[line].directive_ignore_line = true; + } + if (line_disable !== undefined) { + +// test_cause: +// ["/*jslint-disable*/\n0", "read_line", "line_disable", "", 0] + + test_cause("line_disable"); + line_source = ""; + } + // jslint_rgx_tab + if (line_source.indexOf("\t") >= 0) { + if (!option_dict.white) { + +// test_cause: +// ["\t", "read_line", "use_spaces", "", 1] + + warn_at("use_spaces", line, line_source.indexOf("\t") + 1); + } + line_source = line_source.replace(jslint_rgx_tab, " "); + } + if (!option_dict.white && line_source.endsWith(" ")) { + +// test_cause: +// [" ", "read_line", "unexpected_trailing_space", "", 1] + + warn_at("unexpected_trailing_space", line, line_source.length - 1); + } + return line_source; + } + + function token_create(id, value, identifier) { + +// Create the token object and append it to token_list. + + let the_token = { + from, + id, + identifier: Boolean(identifier), + line, + nr: token_list.length, + thru: column, + value + }; + token_list.push(the_token); + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + mode_directive = false; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option_dict.white. + + if ( + token_prv.line === line + && token_prv.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (token_prv.id === "(comment)" || token_prv.id === "(regexp)") + ) { + +// test_cause: +// ["/**//**/", "token_create", "expected_space_a_b", "(comment)", 5] + + warn( + "expected_space_a_b", + the_token, + artifact(token_prv), + artifact(the_token) + ); + } + if (token_prv.id === "." && id === "(number)") { + +// test_cause: +// [".0", "token_create", "expected_a_before_b", ".", 1] + + warn("expected_a_before_b", token_prv, "0", "."); + } + if (token_prv_expr.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart. +// Farts are now detected by keeping a list of most recent "(" tokens at any +// given depth. When a "=>" token is encountered, the most recent "(" token at +// current depth is marked as a fart. + + switch (id) { + case "(": + paren_backtrack_list[paren_depth] = the_token; + paren_depth += 1; + break; + case ")": + paren_depth -= 1; + break; + case "=>": + if ( + token_prv_expr.id === ")" + && paren_backtrack_list[paren_depth] + ) { + paren_backtrack_list[paren_depth].fart = the_token; + } + break; + } + +// The previous token is used to detect adjacency problems. + + token_prv = the_token; + +// The token_prv_expr token is a previous token that was not a comment. +// The token_prv_expr token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (token_prv.id !== "(comment)") { + token_prv_expr = token_prv; + } + return the_token; + } + +// Init global_dict and option_dict. + + option_set_item("ecma", true); + Object.keys(option_dict).sort().forEach(function (key) { + option_set_item(key, option_dict[key] === true); + }); + object_assign_from_list(global_dict, global_list, "user-defined"); + +// Scan first line for "#!" and ignore it. + + if (line_list[jslint_fudge].line_source.startsWith("#!")) { + line += 1; + state.mode_shebang = true; + } + token_1 = lex_token(); + state.mode_json = token_1.id === "{" || token_1.id === "["; + +// Lex/loop through each token until (end). + + while (true) { + if (lex_token().id === "(end)") { + break; + } + } +} + +function jslint_phase3_parse(state) { + +// PHASE 3. Parse into using the Pratt-parser. + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + + let anon = "anonymous"; // The guessed name for anonymous functions. + let { + artifact, + catch_list, + catch_stack, + export_dict, + function_list, + function_stack, + global_dict, + import_list, + is_equal, + option_dict, + property_dict, + stop, + syntax_dict, + tenure, + test_cause, + token_global, + token_list, + warn, + warn_at + } = state; + let catchage = catch_stack[0]; // The current catch-block. + let functionage = token_global; // The current function. + let mode_var; // "var" if using var; "let" if using let. + let token_ii = 0; // The number of the next token. + let token_now = token_global; // The current token being examined in + // ... the parse. + let token_nxt = token_global; // The next token to be examined in + // ... . + + function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if ( + token_now.identifier + && token_now.id !== "function" + && token_now.id !== "async" + ) { + anon = token_now.id; + } else if ( + token_now.id === "(string)" + && jslint_rgx_identifier.test(token_now.value) + ) { + anon = token_now.value; + } + +// Attempt to match token_nxt with an expected id. + + if (id !== undefined && token_nxt.id !== id) { + return ( + match === undefined + +// test_cause: +// ["{0:0}", "advance", "expected_a_b", "0", 2] + + ? stop("expected_a_b", token_nxt, id, artifact()) + +// test_cause: +// ["{\"aa\":0", "advance", "expected_a_b_from_c_d", "{", 1] + + : stop( + "expected_a_b_from_c_d", + token_nxt, + id, + artifact(match), + match.line, + artifact() + ) + ); + } + +// Promote the tokens, skipping comments. + + token_now = token_nxt; + while (true) { + token_nxt = token_list[token_ii]; + state.token_nxt = token_nxt; + token_ii += 1; + if (token_nxt.id !== "(comment)") { + if (token_nxt.id === "(end)") { + token_ii -= 1; + } + break; + } + if (state.mode_json) { + +// test_cause: +// ["[//]", "advance", "unexpected_a", "(comment)", 2] + + warn("unexpected_a"); + } + } + } + + function assignment(id) { + +// Create an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led_infix = function (left) { + const the_token = token_now; + let right; + the_token.arity = "assignment"; + right = parse_expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "preassign" + || right.arity === "postassign" + ) { + warn("unexpected_a", right); + } + check_mutation(left); + return the_token; + }; + return the_symbol; + } + + function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token_now; + if (special !== "body") { + functionage.statement_prv = the_block; + } + the_block.arity = "statement"; + the_block.body = special === "body"; + +// Top level function bodies may include the "use strict" pragma. + + if ( + special === "body" + && function_stack.length === 1 + && token_nxt.value === "use strict" + ) { + token_nxt.statement = true; + advance("(string)"); + advance(";"); + } + stmts = parse_statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option_dict.devel && special !== "ignore") { + +// test_cause: +// ["function aa(){}", "block", "empty_block", "{", 14] + + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; + } + + function check_left(left, right) { + +// Warn if the left is not one of these: +// ?. +// ?: +// e() +// e.b +// e[b] +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !check_left(left.expression[1]) + && !check_left(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "?." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right || token_nxt); + return false; + } + return true; + } + + function check_mutation(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + +// test_cause: +// ["0=0", "check_mutation", "bad_assignment_a", "0", 1] + + warn("bad_assignment_a", the_thing); + return false; + } + return true; + } + + function check_not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === token_global) { + +// test_cause: +// [" +// while(0){} +// ", "check_not_top_level", "unexpected_at_top_level_a", "while", 1] + + warn("unexpected_at_top_level_a", thing); + } + } + + function check_ordered(type, token_list) { + +// This function will warn if is unordered. + + token_list.reduce(function (aa, token) { + const bb = artifact(token); + if (!option_dict.unordered && aa > bb) { + warn("expected_a_b_before_c_d", token, type, bb, type, aa); + } + return bb; + }, ""); + } + + function check_ordered_case(case_list) { + +// This function will warn if is unordered. + + case_list.filter(noop).map(function (token) { + switch (token.identifier || token.id) { + case "(number)": + return { + order: 1, + token, + type: "number", + value: Number(artifact(token)) + }; + case "(string)": + return { + order: 2, + token, + type: "string", + value: artifact(token) + }; + case true: + return { + order: 3, + token, + type: "identifier", + value: artifact(token) + }; + } + }).reduce(function (aa, bb) { + if ( + +// PR-419 - Hide warning about unordered case-statements behind beta-flag. + + option_dict.beta + && !option_dict.unordered + && aa && bb + && ( + aa.order > bb.order + || (aa.order === bb.order && aa.value > bb.value) + ) + ) { + warn( + "expected_a_b_before_c_d", + bb.token, + `case-${bb.type}`, + bb.value, + `case-${aa.type}`, + aa.value + ); + } + return bb; + }, undefined); + } + + function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = token_nxt; + let the_value; + +// test_cause: +// ["do{}while()", "condition", "", "", 0] +// ["if(){}", "condition", "", "", 0] +// ["while(){}", "condition", "", "", 0] + + test_cause(""); + the_paren.free = true; + advance("("); + the_value = parse_expression(0); + advance(")"); + if (the_value.wrapped === true) { + +// test_cause: +// ["while((0)){}", "condition", "unexpected_a", "(", 6] + + warn("unexpected_a", the_paren); + } + +// Check for anticondition. + + switch (the_value.id) { + case "%": + warn("unexpected_a", the_value); + break; + case "&": + warn("unexpected_a", the_value); + break; + case "(number)": + warn("unexpected_a", the_value); + break; + case "(string)": + warn("unexpected_a", the_value); + break; + case "*": + warn("unexpected_a", the_value); + break; + case "+": + warn("unexpected_a", the_value); + break; + case "-": + warn("unexpected_a", the_value); + break; + case "/": + warn("unexpected_a", the_value); + break; + case "<<": + warn("unexpected_a", the_value); + break; + case ">>": + warn("unexpected_a", the_value); + break; + case ">>>": + warn("unexpected_a", the_value); + break; + case "?": + warn("unexpected_a", the_value); + break; + case "^": + warn("unexpected_a", the_value); + break; + case "typeof": + warn("unexpected_a", the_value); + break; + case "|": + warn("unexpected_a", the_value); + break; + case "~": + +// test_cause: +// ["if(0%0){}", "condition", "unexpected_a", "%", 5] +// ["if(0&0){}", "condition", "unexpected_a", "&", 5] +// ["if(0){}", "condition", "unexpected_a", "0", 4] +// ["if(0*0){}", "condition", "unexpected_a", "*", 5] +// ["if(0+0){}", "condition", "unexpected_a", "+", 5] +// ["if(0-0){}", "condition", "unexpected_a", "-", 5] +// ["if(0/0){}", "condition", "unexpected_a", "/", 5] +// ["if(0<<0){}", "condition", "unexpected_a", "<<", 5] +// ["if(0>>0){}", "condition", "unexpected_a", ">>", 5] +// ["if(0>>>0){}", "condition", "unexpected_a", ">>>", 5] +// ["if(0?0:0){}", "condition", "unexpected_a", "?", 5] +// ["if(0^0){}", "condition", "unexpected_a", "^", 5] +// ["if(0|0){}", "condition", "unexpected_a", "|", 5] +// ["if(\"aa\"){}", "condition", "unexpected_a", "aa", 4] +// ["if(typeof 0){}", "condition", "unexpected_a", "typeof", 4] +// ["if(~0){}", "condition", "unexpected_a", "~", 4] + + warn("unexpected_a", the_value); + break; + } + return the_value; + } + + function constant(id, type, value) { + +// Create a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud_prefix = ( + typeof value === "function" + ? value + : function () { + token_now.constant = true; + if (value !== undefined) { + token_now.value = value; + } + return token_now; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; + } + + function constant_Function() { + if (!option_dict.eval) { + +// test_cause: +// ["Function", "constant_Function", "unexpected_a", "Function", 1] + + warn("unexpected_a", token_now); + } else if (token_nxt.id !== "(") { + +// test_cause: +// [" +// /*jslint eval*/ +// Function +// ", "constant_Function", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "(", artifact()); + } + return token_now; + } + + function constant_arguments() { + +// test_cause: +// ["arguments", "constant_arguments", "unexpected_a", "arguments", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function constant_eval() { + if (!option_dict.eval) { + +// test_cause: +// ["eval", "constant_eval", "unexpected_a", "eval", 1] + + warn("unexpected_a", token_now); + } else if (token_nxt.id !== "(") { + +// test_cause: +// ["/*jslint eval*/\neval", "constant_eval", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "(", artifact()); + } + return token_now; + } + + function constant_ignore() { + +// test_cause: +// ["ignore", "constant_ignore", "unexpected_a", "ignore", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function constant_isInfinite() { + +// test_cause: +// ["isFinite", "constant_isInfinite", "expected_a_b", "isFinite", 1] + + warn("expected_a_b", token_now, "Number.isFinite", "isFinite"); + return token_now; + } + + function constant_isNaN() { + +// test_cause: +// ["isNaN(0)", "constant_isNaN", "number_isNaN", "isNaN", 1] + + warn("number_isNaN", token_now); + return token_now; + } + + function constant_this() { + if (!option_dict.this) { + +// test_cause: +// ["this", "constant_this", "unexpected_a", "this", 1] + + warn("unexpected_a", token_now); + } + return token_now; + } + + function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + let earlier; + let id = name.id; + +// Reserved words may not be enrolled. + + if (syntax_dict[id] !== undefined && id !== "ignore") { + +// test_cause: +// ["let undefined", "enroll", "reserved_a", "undefined", 5] + + warn("reserved_a", name); + return; + } + +// Has the name been enrolled in this context? + + earlier = functionage.context[id] || catchage.context[id]; + if (earlier) { + +// test_cause: +// ["let aa;let aa", "enroll", "redefinition_a_b", "1", 12] + + warn("redefinition_a_b", name, id, earlier.line); + return; + } + +// Has the name been enrolled in an outer context? + + function_stack.forEach(function ({ + context + }) { + earlier = context[id] || earlier; + }); + if (earlier && id === "ignore") { + if (earlier.role === "variable") { + +// test_cause: +// ["let ignore;function aa(ignore){}", "enroll", "redefinition_a_b", "1", 24] + + warn("redefinition_a_b", name, id, earlier.line); + } + } else if ( + earlier + && role !== "parameter" && role !== "function" + && (role !== "exception" || earlier.role !== "exception") + ) { + +// test_cause: +// [" +// function aa(){try{aa();}catch(aa){aa();}} +// ", "enroll", "redefinition_a_b", "1", 31] +// ["function aa(){var aa;}", "enroll", "redefinition_a_b", "1", 19] + + warn("redefinition_a_b", name, id, earlier.line); + } else if ( + option_dict.beta + && global_dict[id] + && role !== "parameter" + ) { + +// test_cause: +// ["let Array", "enroll", "redefinition_global_a_b", "Array", 5] + + warn("redefinition_global_a_b", name, global_dict[id], id); + } + +// Enroll it. + + Object.assign(name, { + dead: true, + init: false, + parent: ( + role === "exception" + ? catchage + : functionage + ), + readonly, + role, + used: 0 + }); + name.parent.context[id] = name; + } + + function infix(bp, id, f) { + +// Create an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led_infix = function (left) { + const the_token = token_now; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, parse_expression(bp)]; + return the_token; + }; + return the_symbol; + } + + function infix_dot(left) { + const the_token = token_now; + let name = token_nxt; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "flat" + && name.id !== "flatMap" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + +// test_cause: +// ["\"\".aa", "check_left", "unexpected_a", ".", 3] + + check_left(left, the_token); + } + if (!name.identifier) { + +// test_cause: +// ["aa.0", "infix_dot", "expected_identifier_a", "0", 4] + + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; + } + + function infix_fart_unwrapped() { + +// test_cause: +// ["aa=>0", "infix_fart_unwrapped", "wrap_fart_parameter", "=>", 3] + + return stop("wrap_fart_parameter", token_now); + } + + function infix_grave(left) { + const the_tick = prefix_tick(); + +// test_cause: +// ["0``", "check_left", "unexpected_a", "`", 2] + + check_left(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; + } + + function infix_lbracket(left) { + const the_token = token_now; + let name; + let the_subscript = parse_expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + name = survey(the_subscript); + +// PR-404 - Add new directive "subscript" to play nice with Google Closure. + + if (!option_dict.subscript && jslint_rgx_identifier.test(name)) { + +// test_cause: +// ["aa[`aa`]", "infix_lbracket", "subscript_a", "aa", 4] + + warn("subscript_a", the_subscript, name); + } + } + +// test_cause: +// ["0[0]", "check_left", "unexpected_a", "[", 2] + + check_left(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; + } + + function infix_lparen(left) { + const the_paren = token_now; + let ellipsis; + let the_argument; + if (left.id !== "function") { + +// test_cause: +// ["(0?0:0)()", "check_left", "unexpected_a", "(", 8] +// ["0()", "check_left", "unexpected_a", "(", 2] + + check_left(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (token_nxt.id !== ")") { + +// Parse/loop through each token in expression (...). + + while (true) { + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = parse_expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + +// test_cause: +// ["aa(0)", "infix_lparen", "free", "", 0] + + test_cause("free"); + the_paren.free = true; + if (the_argument.wrapped === true) { + +// test_cause: +// ["aa((0))", "infix_lparen", "unexpected_a", "(", 3] + + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + +// test_cause: +// ["aa()", "infix_lparen", "not_free", "", 0] +// ["aa(0,0)", "infix_lparen", "not_free", "", 0] + + test_cause("not_free"); + the_paren.free = false; + } + return the_paren; + } + + function infix_option_chain(left) { + const the_token = token_now; + let name = token_nxt; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + +// test_cause: +// ["(0+0)?.0", "infix_option_chain", "check_left", "", 0] + + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + test_cause("check_left"); + +// test_cause: +// ["(/./)?.0", "check_left", "unexpected_a", "?.", 6] +// ["\"aa\"?.0", "check_left", "unexpected_a", "?.", 5] +// ["aa=[]?.aa", "check_left", "unexpected_a", "?.", 6] + + check_left(left, the_token); + } + +// Issue #468 - Fix optional dynamic-property/function-call not recognized. + + if (name.id === "[" || name.id === "(") { + test_cause("dyn_prop_or_call"); + +// test_cause: +// ["aa?.(bb)", "infix_option_chain", "dyn_prop_or_call", "", 0] +// ["aa?.[bb]", "infix_option_chain", "dyn_prop_or_call", "", 0] + + return left; + } + if (!name.identifier) { + +// test_cause: +// ["aa?.0", "infix_option_chain", "expected_identifier_a", "0", 5] + + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; + } + + function infixr(bp, id) { + +// Create a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led_infix = function parse_infixr_led(left) { + const the_token = token_now; + +// test_cause: +// ["0**0", "parse_infixr_led", "led_infix", "", 0] + + test_cause("led_infix"); + the_token.arity = "binary"; + the_token.expression = [left, parse_expression(bp - 1)]; + return the_token; + }; + return the_symbol; + } + + function parse_expression(rbp, initial) { + +// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it +// is looking for ad hoc lint patterns. We add .fud_stmt to Pratt's model, which +// is like .nud_prefix except that it is only used on the first token of a +// statement. Having .fud_stmt makes it much easier to define statement-oriented +// languages like JavaScript. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// .nud_prefix Null denotation. The prefix handler. +// .fud_stmt First null denotation. The statement handler. +// .led_infix Left denotation. The infix/postfix handler. +// lbp Left binding power of infix operator. It tells us how strongly +// the operator binds to the argument at its left. +// rbp Right binding power. + +// It processes a nud_prefix (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop (it +// consumes tokens until it meets a token whose lbp <= rbp). Specifically, it +// means that it collects all tokens that bind together before returning to the +// operator that called it. It returns the expression's parse tree. + +// For example, "3 + 1 * 2 * 4 + 5" +// parses into +// { +// "id": "+", +// "expression": [ +// { +// "id": "+", +// "expression": [ +// { +// "id": "(number)", +// "value": "3" +// }, +// { +// "id": "*", +// "expression": [ +// { +// "id": "*", +// "expression": [ +// { +// "id": "(number)", +// "value": "1" +// }, +// { +// "id": "(number)", +// "value": "2" +// } +// ] +// }, +// { +// "id": "(number)", +// "value": "4" +// } +// ] +// } +// ] +// }, +// { +// "id": "(number)", +// "value": "5" +// } +// ] +// } + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement. + + if (!initial) { + advance(); + } + the_symbol = syntax_dict[token_now.id]; + if (the_symbol !== undefined && the_symbol.nud_prefix !== undefined) { + +// test_cause: +// ["0", "parse_expression", "symbol", "", 0] + + test_cause("symbol"); + left = the_symbol.nud_prefix(); + } else if (token_now.identifier) { + +// test_cause: +// ["aa", "parse_expression", "identifier", "", 0] + + test_cause("identifier"); + left = token_now; + left.arity = "variable"; + } else { + +// test_cause: +// ["!", "parse_expression", "unexpected_a", "(end)", 1] +// ["/./", "parse_expression", "unexpected_a", "/", 1] +// ["let aa=`${}`;", "parse_expression", "unexpected_a", "}", 11] + + return stop("unexpected_a", token_now); + } + +// Parse/loop through each symbol in expression. + + while (true) { + the_symbol = syntax_dict[token_nxt.id]; + if ( + the_symbol === undefined + || the_symbol.led_infix === undefined + || the_symbol.lbp <= rbp + ) { + break; + } + advance(); + left = the_symbol.led_infix(left); + } + return left; + } + + function parse_fart(the_fart) { + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + Object.assign(the_fart, { + arity: "binary", + context: empty(), + finally: 0, + level: functionage.level + 1, + loop: 0, + name: anon, + switch: 0, + try: 0 + }); + +// PR-384 - Relax warning "function_in_loop". +// +// if (functionage.loop > 0) { + +// // test_cause: +// // ["while(0){aa.map(()=>0);}", "parse_fart", "function_in_loop", "=>", 19] +// +// warn("function_in_loop", the_fart); +// } + +// Push the current function context and establish a new one. + + function_list.push(the_fart); + function_stack.push(functionage); + functionage = the_fart; + +// Parse the parameter list. + + prefix_function_parameter(the_fart); + advance("=>"); + +// The function's body is a block. + + if (token_nxt.id === "{") { + if (!option_dict.fart) { + +// test_cause: +// ["()=>{}", "parse_fart", "use_function_not_fart", "=>", 3] + + warn("use_function_not_fart", the_fart); + } + the_fart.block = block("body"); + } else if ( + syntax_dict[token_nxt.id] !== undefined + && syntax_dict[token_nxt.id].fud_stmt !== undefined + ) { + +// PR-384 - Bugfix - Fixes issue #379 - warn against naked-statement in fart. + +// test_cause: +// ["()=>delete aa", "parse_fart", "unexpected_a_after_b", "=>", 5] + + stop("unexpected_a_after_b", token_nxt, token_nxt.id, "=>"); + +// The function's body is an expression. + + } else { + the_fart.expression = parse_expression(0); + } + +// Restore the previous context. + + functionage = function_stack.pop(); + return the_fart; + } + + function parse_json() { + let container; + let is_dup; + let name; + let negative; + switch (token_nxt.id) { + case "(number)": + if (!jslint_rgx_json_number.test(token_nxt.value)) { + +// test_cause: +// ["[-.0]", "parse_json", "unexpected_a", ".", 3] +// ["[-0x0]", "parse_json", "unexpected_a", "0x0", 3] +// ["[.0]", "parse_json", "unexpected_a", ".", 2] +// ["[0x0]", "parse_json", "unexpected_a", "0x0", 2] + + warn("unexpected_a"); + } + advance("(number)"); + return token_now; + case "(string)": + if (token_nxt.quote !== "\"") { + +// test_cause: +// ["['']", "parse_json", "unexpected_a", "'", 2] + + warn("unexpected_a", token_nxt, token_nxt.quote); + } + advance("(string)"); + return token_now; + case "-": + negative = token_nxt; + negative.arity = "unary"; + advance("-"); + +// Recurse parse_json(). + + negative.expression = parse_json(); + return negative; + case "[": + +// test_cause: +// ["[]", "parse_json", "bracket", "", 0] + + test_cause("bracket"); + container = token_nxt; + container.expression = []; + advance("["); + if (token_nxt.id !== "]") { + while (true) { + +// Recurse parse_json(). + + container.expression.push(parse_json()); + if (token_nxt.id !== ",") { + +// test_cause: +// ["[0,0]", "parse_json", "comma", "", 0] + + test_cause("comma"); + break; + } + advance(","); + } + } + advance("]", container); + return container; + case "false": + case "null": + case "true": + +// test_cause: +// ["[false]", "parse_json", "constant", "", 0] +// ["[null]", "parse_json", "constant", "", 0] +// ["[true]", "parse_json", "constant", "", 0] + + test_cause("constant"); + advance(); + return token_now; + case "{": + +// test_cause: +// ["{}", "parse_json", "brace", "", 0] + + test_cause("brace"); + container = token_nxt; + +// Explicit empty-object required to detect "__proto__". + + is_dup = empty(); + container.expression = []; + advance("{"); + if (token_nxt.id !== "}") { + +// JSON +// Parse/loop through each property in {...}. + + while (true) { + if (token_nxt.quote !== "\"") { + +// test_cause: +// ["{0:0}", "parse_json", "unexpected_a", "0", 2] + + warn( + "unexpected_a", + token_nxt, + token_nxt.quote + ); + } + name = token_nxt; + advance("(string)"); + if (is_dup[token_now.value] !== undefined) { + +// test_cause: +// ["{\"aa\":0,\"aa\":0}", "parse_json", "duplicate_a", "aa", 9] + + warn("duplicate_a", token_now); + } else if (token_now.value === "__proto__") { + +// test_cause: +// ["{\"__proto__\":0}", "parse_json", "weird_property_a", "__proto__", 2] + + warn("weird_property_a", token_now); + } else { + is_dup[token_now.value] = token_now; + } + advance(":"); + container.expression.push( + +// Recurse parse_json(). + + Object.assign(parse_json(), { + label: name + }) + ); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance("}", container); + return container; + default: + +// test_cause: +// ["[undefined]", "parse_json", "unexpected_a", "undefined", 2] + + stop("unexpected_a"); + } + } + + function parse_statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token_now.identifier && token_nxt.id === ":") { + the_label = token_now; + if (the_label.id === "ignore") { + +// test_cause: +// ["ignore:", "parse_statement", "unexpected_a", "ignore", 1] + + warn("unexpected_a", the_label); + } + advance(":"); + switch (token_nxt.id) { + case "do": + case "for": + case "switch": + case "while": + +// test_cause: +// ["aa:do{}", "parse_statement", "the_statement_label", "do", 0] +// ["aa:for{}", "parse_statement", "the_statement_label", "for", 0] +// ["aa:switch{}", "parse_statement", "the_statement_label", "switch", 0] +// ["aa:while{}", "parse_statement", "the_statement_label", "while", 0] + + test_cause("the_statement_label", token_nxt.id); + enroll(the_label, "label", true); + the_label.dead = false; + the_label.init = true; + the_statement = parse_statement(); + functionage.statement_prv = the_statement; + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + +// test_cause: +// ["aa:", "parse_statement", "unexpected_label_a", "aa", 1] + + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token_now; + first.statement = true; + the_symbol = syntax_dict[first.id]; + if ( + the_symbol !== undefined + && the_symbol.fud_stmt !== undefined + +// PR-318 - Bugfix - Fixes issues #316, #317 - dynamic-import(). + + && !(the_symbol.id === "import" && token_nxt.id === "(") + ) { + the_symbol.disrupt = false; + the_symbol.statement = true; + token_now.arity = "statement"; + the_statement = the_symbol.fud_stmt(); + functionage.statement_prv = the_statement; + } else { + +// It is an expression statement. + + the_statement = parse_expression(0, true); + functionage.statement_prv = the_statement; + if (the_statement.wrapped && the_statement.id !== "(") { + +// test_cause: +// ["(0)", "parse_statement", "unexpected_a", "(", 1] + + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; + } + + function parse_statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const statement_list = []; + let a_statement; + let disrupt = false; + +// Parse/loop each statement until a statement-terminator is reached. + + while (true) { + switch (token_nxt.id) { + case "(end)": + case "case": + case "default": + case "else": + case "}": + +// test_cause: +// [";", "parse_statements", "closer", "", 0] +// ["case", "parse_statements", "closer", "", 0] +// ["default", "parse_statements", "closer", "", 0] +// ["else", "parse_statements", "closer", "", 0] +// ["}", "parse_statements", "closer", "", 0] + + test_cause("closer"); + return statement_list; + } + a_statement = parse_statement(); + statement_list.push(a_statement); + if (disrupt) { + +// test_cause: +// ["while(0){break;0;}", "parse_statements", "unreachable_a", "0", 16] + + warn("unreachable_a", a_statement); + } + disrupt = a_statement.disrupt; + } + } + + function postassign(id) { + +// Create one of the postassign operators. + + const the_symbol = symbol(id, 150); + the_symbol.led_infix = function (left) { + token_now.expression = left; + token_now.arity = "postassign"; + check_mutation(token_now.expression); + return token_now; + }; + return the_symbol; + } + + function preassign(id) { + +// Create one of the preassign operators. + + const the_symbol = symbol(id); + the_symbol.nud_prefix = function () { + const the_token = token_now; + the_token.arity = "preassign"; + the_token.expression = parse_expression(150); + check_mutation(the_token.expression); + return the_token; + }; + return the_symbol; + } + + function prefix(id, f) { + +// Create a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud_prefix = function () { + const the_token = token_now; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = parse_expression(150); + return the_token; + }; + return the_symbol; + } + + function prefix_assign_divide() { + +// test_cause: +// ["/=", "prefix_assign_divide", "expected_a_b", "/=", 1] + + stop("expected_a_b", token_now, "/\\=", "/="); + } + + function prefix_async() { + let the_async = token_now; + let the_function; + token_nxt.arity = the_async.arity; + +// PR-414 - Parse async fart. + + if (token_nxt.fart) { + advance("("); + the_function = Object.assign(token_now.fart, { + async: 1 + }); + if (!option_dict.fart) { + +// test_cause: +// ["async()=>0", "prefix_async", "use_function_not_fart", "=>", 8] + + warn("use_function_not_fart", the_function); + } + prefix_lparen(); + +// Parse async function. + + } else { + advance("function"); + the_function = Object.assign(token_now, { + async: 1 + }); + prefix_function(); + } + if (the_function.async === 1) { + +// test_cause: +// [" +// async function aa(){} +// ", "prefix_async", "missing_await_statement", "function", 7] + + warn("missing_await_statement", the_function); + } + return the_function; + } + + function prefix_await() { + const the_await = token_now; + +// PR-370 - Add top-level-await support. + + if (functionage.async === 0 && functionage !== token_global) { + +// test_cause: +// ["function aa(){aa=await 0;}", "prefix_await", "unexpected_a", "await", 18] +// ["function aa(){await 0;}", "prefix_await", "unexpected_a", "await", 15] + + warn("unexpected_a", the_await); + } else { + functionage.async += 1; + } + if (the_await.arity === "statement") { + +// PR-405 - Bugfix - fix expression after "await" mis-identified as statement. + + the_await.expression = parse_expression(150); + semicolon(); + } else { + the_await.expression = parse_expression(150); + } + return the_await; + } + + function prefix_fart() { + +// test_cause: +// ["=>0", "prefix_fart", "expected_a_before_b", "=>", 1] + + return stop("expected_a_before_b", token_now, "()", "=>"); + } + + function prefix_function(the_function) { + let name = the_function && the_function.name; + if (the_function === undefined) { + the_function = token_now; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!token_nxt.identifier) { + +// test_cause: +// ["function(){}", "prefix_function", "expected_identifier_a", "(", 9] +// ["function*aa(){}", "prefix_function", "expected_identifier_a", "*", 9] + + return stop("expected_identifier_a"); + } + name = token_nxt; + enroll(name, "variable", true); + the_function.name = Object.assign(name, { + calls: empty(), + +// PR-331 - Bugfix - Fixes issue #272 - function hoisting not allowed. + + dead: false, + init: true + }); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + the_function.name = anon; + if (token_nxt.identifier) { + name = token_nxt; + the_function.name = name; + advance(); + } + } + } + +// Probably deadcode. +// if (mode_mega) { +// warn("unexpected_a", the_function); +// } +// jslint_assert(!mode_mega, `Expected !mode_mega.`); + +// PR-378 - Relax warning "function_in_loop". +// +// // Don't create functions in loops. It is inefficient, and it can lead to +// // scoping errors. +// +// if (functionage.loop > 0) { +// +// // test_cause: +// // [" +// // while(0){aa.map(function(){});} +// // ", "prefix_function", "function_in_loop", "function", 17] +// +// warn("function_in_loop", the_function); +// } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + Object.assign(the_function, { + async: the_function.async || 0, + context: empty(), + finally: 0, + level: functionage.level + 1, + loop: 0, + statement_prv: undefined, + switch: 0, + try: 0 + }); + if (the_function.arity !== "statement" && typeof name === "object") { + +// test_cause: +// ["let aa=function bb(){return;};", "prefix_function", "expression", "bb", 0] + + test_cause("expression", name.id); + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// PR-334 - Bugfix - fix function-redefinition not warned inside function-call. +// Push the current function context and establish a new one. + + function_list.push(the_function); + function_stack.push(functionage); + functionage = the_function; + +// Parse the parameter list. + + advance("("); + token_now.arity = "function"; + prefix_function_parameter(the_function); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && token_nxt.line === token_now.line + ) { + +// test_cause: +// ["function aa(){}0", "prefix_function", "unexpected_a", "0", 16] + + return stop("unexpected_a"); + } + if ( + token_nxt.id === "." + || token_nxt.id === "?." + +// PR-459 - Allow destructuring-assignment after function-definition. + + // || token_nxt.id === "[" + ) { + +// test_cause: +// ["function aa(){}\n.aa", "prefix_function", "unexpected_a", ".", 1] +// ["function aa(){}\n?.aa", "prefix_function", "unexpected_a", "?.", 1] + + warn("unexpected_a"); + } + +// Check functions are ordered. + + check_ordered( + "function", + function_list.slice( + function_list.indexOf(the_function) + 1 + ).map(function ({ + level, + name + }) { + return (level === the_function.level + 1) && name; + }).filter(function (name) { + return option_dict.beta && name && name.id; + }) + ); + +// Restore the previous context. + + functionage = function_stack.pop(); + return the_function; + } + + function prefix_function_parameter(the_function) { + +// This function will parse input at beginning of + + let optional; + let parameters = []; + let signature = ["("]; + let subparam; + function param_enroll(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + +// test_cause: +// ["([aa])=>0", "param_enroll", "use_function_not_fart", "=>", 7] +// ["({aa})=>0", "param_enroll", "use_function_not_fart", "=>", 7] + + if (the_function.id === "=>" && !option_dict.fart) { + warn("use_function_not_fart", the_function); + } + +// Recurse param_enroll(). + + name.names.forEach(param_enroll); + } + } + function param_parse() { + let ellipsis = false; + let param; + if (token_nxt.id === "{") { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,{}){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + param = token_nxt; + param.names = []; + advance("{"); + signature.push("{"); + while (true) { + subparam = token_nxt; + if (!subparam.identifier) { + +// test_cause: +// ["function aa(aa=0,{}){}", "param_parse", "expected_identifier_a", "}", 19] +// ["function aa({0}){}", "param_parse", "expected_identifier_a", "0", 14] + + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (token_nxt.id === ":") { + advance(":"); + advance(); + token_now.label = subparam; + subparam = token_now; + if (!subparam.identifier) { + +// test_cause: +// ["function aa({aa:0}){}", "param_parse", "expected_identifier_a", "}", 18] + + return stop( + "expected_identifier_a", + token_nxt + ); + } + } + +// test_cause: +// ["function aa({aa=aa},aa){}", "param_parse", "equal", "", 0] + + test_cause("equal"); + if (token_nxt.id === "=") { + advance("="); + subparam.expression = parse_expression(); + param.open = true; + } + param.names.push(subparam); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + } else { + break; + } + } + parameters.push(param); + +// test_cause: +// [" +// function aa({bb,aa}){} +// ", "check_ordered", "expected_a_b_before_c_d", "aa", 17] + + check_ordered("parameter", param.names); + advance("}"); + signature.push("}"); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } else if (token_nxt.id === "[") { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,[]){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + param = token_nxt; + param.names = []; + advance("["); + signature.push("[]"); + while (true) { + subparam = token_nxt; + if (!subparam.identifier) { + +// test_cause: +// ["function aa(aa=0,[]){}", "param_parse", "expected_identifier_a", "]", 19] + + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + +// test_cause: +// ["function aa([aa=aa],aa){}", "param_parse", "id", "", 0] + + test_cause("id"); + if (token_nxt.id === "=") { + advance("="); + subparam.expression = parse_expression(); + param.open = true; + } + if (token_nxt.id === ",") { + advance(","); + } else { + break; + } + } + parameters.push(param); + advance("]"); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } else { + if (token_nxt.id === "...") { + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,...){}", "param_parse", "required_a_optional_b", "aa", 21] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + } + if (!token_nxt.identifier) { + +// test_cause: +// ["function aa(0){}", "param_parse", "expected_identifier_a", "0", 13] + + return stop("expected_identifier_a"); + } + param = token_nxt; + parameters.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (token_nxt.id === "=") { + optional = param; + advance("="); + param.expression = parse_expression(0); + } else { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,bb){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } + } + } + +// test_cause: +// ["function aa(){}", "prefix_function_parameter", "opener", "(", 0] + + test_cause("opener", token_now.id); + token_now.free = false; + if (token_nxt.id !== ")" && token_nxt.id !== "(end)") { + param_parse(); + } + advance(")"); + signature.push(")"); + parameters.forEach(param_enroll); + the_function.parameters = parameters; + the_function.signature = signature.join(""); + } + + function prefix_lbrace() { + const seen = empty(); + const the_brace = token_now; + let extra; + let full; + let id; + let name; + let the_colon; + let value; + the_brace.expression = []; + if (token_nxt.id !== "}") { + +// Parse/loop through each property in {...}. + + while (true) { + name = token_nxt; + advance(); + if ( + (name.id === "get" || name.id === "set") + && token_nxt.identifier + ) { + if (!option_dict.getset) { + +// test_cause: +// ["aa={get aa(){}}", "prefix_lbrace", "unexpected_a", "get", 5] + + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + token_nxt.id; + name = token_nxt; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + +// test_cause: +// ["aa={get aa(){},get aa(){}}", "prefix_lbrace", "duplicate_a", "aa", 20] + + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else if (name.id === "`") { + +// test_cause: +// ["aa={`aa`:0}", "prefix_lbrace", "unexpected_a", "`", 5] + + stop("unexpected_a", name); + + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + +// test_cause: +// ["aa={aa,aa}", "prefix_lbrace", "duplicate_a", "aa", 8] + + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (token_nxt.id === "}" || token_nxt.id === ",") { + if (typeof extra === "string") { + +// test_cause: +// ["aa={get aa}", "prefix_lbrace", "closer", "", 0] + + test_cause("closer"); + advance("("); + } + value = parse_expression(Infinity, true); + } else if (token_nxt.id === "(") { + +// test_cause: +// ["aa={aa()}", "prefix_lbrace", "paren", "", 0] +// ["aa={get aa(){}}", "prefix_lbrace", "paren", "", 0] + + test_cause("paren"); + value = prefix_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + +// test_cause: +// ["aa={get aa.aa}", "prefix_lbrace", "paren", "", 0] + + test_cause("paren"); + advance("("); + } + the_colon = token_nxt; + advance(":"); + value = parse_expression(0); + if ( + value.id === name.id + && value.id !== "function" + ) { + +// test_cause: +// ["aa={aa:aa}", "prefix_lbrace", "unexpected_a", ": aa", 7] + + warn("unexpected_a", the_colon, ": " + name.id); + } + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + +// test_cause: +// ["aa={\"aa\":0}", "prefix_lbrace", "colon", "", 0] + + test_cause("colon"); + advance(":"); + value = parse_expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (token_nxt.id !== ",") { + break; + } + +// test_cause: +// ["aa={\"aa\":0,\"bb\":0}", "prefix_lbrace", "comma", "", 0] + + test_cause("comma"); + advance(","); + if (token_nxt.id === "}") { + +// test_cause: +// ["let aa={aa:0,}", "prefix_lbrace", "unexpected_a", ",", 13] + + warn("unexpected_a", token_now); + break; + } + } + } + +// test_cause: +// ["aa={bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8] + + check_ordered( + "property", + the_brace.expression.map(function ({ + label + }) { + return label; + }) + ); + advance("}"); + return the_brace; + } + + function prefix_lbracket() { + const the_token = token_now; + let element; + let ellipsis; + the_token.expression = []; + if (token_nxt.id !== "]") { + +// Parse/loop through each element in [...]. + + while (true) { + ellipsis = false; + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + element = parse_expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (token_nxt.id !== ",") { + break; + } + advance(","); + if (token_nxt.id === "]") { + +// test_cause: +// ["let aa=[0,]", "prefix_lbracket", "unexpected_a", ",", 10] + + warn("unexpected_a", token_now); + break; + } + } + } + advance("]"); + return the_token; + } + + function prefix_lparen() { + let the_paren = token_now; + let the_value; + +// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart. + + if (token_now.fart) { + return parse_fart(token_now.fart); + } + +// test_cause: +// ["(0)", "prefix_lparen", "expr", "", 0] + + test_cause("expr"); + the_paren.free = true; + the_value = parse_expression(0); + if (the_value.wrapped === true) { + +// test_cause: +// ["((0))", "prefix_lparen", "unexpected_a", "(", 1] + + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + return the_value; + } + + function prefix_new() { + const the_new = token_now; + let right; + right = parse_expression(160); + if (token_nxt.id !== "(") { + +// test_cause: +// ["new aa", "prefix_new", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "()", artifact()); + } + the_new.expression = right; + return the_new; + } + + function prefix_tick() { + const the_tick = token_now; + the_tick.value = []; + the_tick.expression = []; + if (token_nxt.id !== "`") { + +// Parse/loop through each token in `${...}`. + + while (true) { + advance("(string)"); + the_tick.value.push(token_now); + if (token_nxt.id !== "${") { + break; + } + advance("${"); + +// test_cause: +// ["let aa=`${}`;", "prefix_tick", "${", "", 0] + + test_cause("${"); + the_tick.expression.push(parse_expression(0)); + advance("}"); + } + } + advance("`"); + return the_tick; + } + + function prefix_void() { + const the_void = token_now; + +// test_cause: +// ["void 0", "prefix_void", "unexpected_a", "void", 1] +// ["void", "prefix_void", "unexpected_a", "void", 1] + + warn("unexpected_a", the_void); + the_void.expression = parse_expression(0); + return the_void; + } + + function semicolon() { + +// Try to match a semicolon. + + if (token_nxt.id === ";") { + advance(";"); + } else { + +// test_cause: +// ["0", "semicolon", "expected_a_b", "(end)", 1] + + warn_at( + "expected_a_b", + token_now.line, + token_now.thru + 1, + ";", + artifact() + ); + } + anon = "anonymous"; + } + + function stmt(id, fud_stmt) { + +// Create a statement. + + const the_symbol = symbol(id); + the_symbol.fud_stmt = fud_stmt; + return the_symbol; + } + + function stmt_break() { + const the_break = token_now; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + +// test_cause: +// ["break", "stmt_break", "unexpected_a", "break", 1] + + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (token_nxt.identifier && token_now.line === token_nxt.line) { + the_label = functionage.context[token_nxt.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + if (the_label !== undefined && the_label.dead) { + +// test_cause: +// ["aa:{function aa(aa){break aa;}}", "stmt_break", "out_of_scope_a", "aa", 27] + + warn("out_of_scope_a"); + } else { + +// test_cause: +// ["aa:{break aa;}", "stmt_break", "not_label_a", "aa", 11] + + warn("not_label_a"); + } + } else { + the_label.used += 1; + } + the_break.label = token_nxt; + advance(); + } + advance(";"); + return the_break; + } + + function stmt_continue() { + const the_continue = token_now; + if (functionage.loop < 1 || functionage.finally > 0) { + +// test_cause: +// ["continue", "stmt_continue", "unexpected_a", "continue", 1] +// [" +// function aa(){while(0){try{}finally{continue}}} +// ", "stmt_continue", "unexpected_a", "continue", 37] + + warn("unexpected_a", the_continue); + } + check_not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; + } + + function stmt_debugger() { + const the_debug = token_now; + if (!option_dict.devel) { + +// test_cause: +// ["debugger", "stmt_debugger", "unexpected_a", "debugger", 1] + + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; + } + + function stmt_delete() { + const the_token = token_now; + const the_value = parse_expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + +// test_cause: +// ["delete 0", "stmt_delete", "expected_a_b", "0", 8] + + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; + } + + function stmt_do() { + const the_do = token_now; + check_not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + +// test_cause: +// ["function aa(){do{break;}while(0)}", "stmt_do", "weird_loop", "do", 15] + + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; + } + + function stmt_export() { + let export_list = []; + let the_export = token_now; + let the_id; + let the_name; + let the_thing; + + the_export.expression = []; + if (token_nxt.id === "default") { + if (export_dict.default !== undefined) { + +// test_cause: +// [" +// export default 0;export default 0 +// ", "stmt_export", "duplicate_a", "default", 25] + + warn("duplicate_a"); + } + advance("default"); + the_thing = parse_expression(0); + if ( + the_thing.id !== "(" + || the_thing.expression[0].id !== "." + || the_thing.expression[0].expression.id !== "Object" + || the_thing.expression[0].name.id !== "freeze" + ) { + +// test_cause: +// ["export default {}", "stmt_export", "freeze_exports", "{", 16] + + warn("freeze_exports", the_thing); + +// PR-301 - Bugfix - Fixes issues #282 - optional-semicolon. + + } else { + +// test_cause: +// [" +// export default Object.freeze({}) +// ", "semicolon", "expected_a_b", "(end)", 32] + + semicolon(); + } + export_dict.default = the_thing; + the_export.expression.push(the_thing); + } else { + +// PR-439 - Add grammar for "export async function ...". + + if (token_nxt.id === "function" || token_nxt.id === "async") { + +// test_cause: +// ["export async function aa(){}", "stmt_export", "freeze_exports", "async", 8] +// ["export function aa(){}", "stmt_export", "freeze_exports", "function", 8] + + warn("freeze_exports"); + the_thing = parse_statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (export_dict[the_id] !== undefined) { + +// test_cause: +// [" +// let aa;export{aa};export function aa(){} +// ", "stmt_export", "duplicate_a", "aa", 35] + + warn("duplicate_a", the_name); + } + export_dict[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + token_nxt.id === "var" + || token_nxt.id === "let" + || token_nxt.id === "const" + ) { + +// test_cause: +// ["export const", "stmt_export", "unexpected_a", "const", 8] +// ["export let", "stmt_export", "unexpected_a", "let", 8] +// ["export var", "stmt_export", "unexpected_a", "var", 8] + + warn("unexpected_a"); + parse_statement(); + } else if (token_nxt.id === "{") { + +// test_cause: +// ["export {}", "stmt_export", "advance{", "", 0] + + test_cause("advance{"); + advance("{"); + while (true) { + if (!token_nxt.identifier) { + +// test_cause: +// ["export {}", "stmt_export", "expected_identifier_a", "}", 9] + + stop("expected_identifier_a"); + } + the_id = token_nxt.id; + export_list.push(token_nxt); + the_name = token_global.context[the_id]; + if (the_name === undefined) { + +// test_cause: +// ["export {aa}", "stmt_export", "unexpected_a", "aa", 9] + + warn("unexpected_a"); + } else { + the_name.used += 1; + if (export_dict[the_id] !== undefined) { + +// test_cause: +// ["let aa;export{aa,aa}", "stmt_export", "duplicate_a", "aa", 18] + + warn("duplicate_a"); + } + export_dict[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + if (token_nxt.id === ",") { + advance(","); + } else { + break; + } + } + +// PR-439 - Check exported properties are ordered. + +// test_cause: +// ["export {bb, aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 13] + + check_ordered("export", export_list); + advance("}"); + semicolon(); + } else { + +// test_cause: +// ["export", "stmt_export", "unexpected_a", "(end)", 1] + + stop("unexpected_a"); + } + } + state.mode_module = true; + return the_export; + } + + function stmt_for() { + let first; + let the_for = token_now; + if (!option_dict.for) { + +// test_cause: +// ["for", "stmt_for", "unexpected_a", "for", 1] + + warn("unexpected_a", the_for); + } + check_not_top_level(the_for); + functionage.loop += 1; + advance("("); + token_now.free = true; + if (token_nxt.id === ";") { + +// test_cause: +// ["for(;;){}", "stmt_for", "expected_a_b", "for (;", 1] + + return stop("expected_a_b", the_for, "while (", "for (;"); + } + switch (token_nxt.id) { + case "const": + case "let": + case "var": + +// test_cause: +// ["for(const aa in aa){}", "stmt_for", "unexpected_a", "const", 5] + + return stop("unexpected_a"); + } + first = parse_expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + +// test_cause: +// ["for(0 in aa){}", "stmt_for", "bad_assignment_a", "0", 5] + + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = parse_expression(0); + advance(";"); + the_for.inc = parse_expression(0); + if (the_for.inc.id === "++") { + +// test_cause: +// ["for(aa;aa;aa++){}", "stmt_for", "expected_a_b", "++", 13] + + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + +// test_cause: +// [" +// /*jslint for*/ +// function aa(bb,cc){for(0;0;0){break;}} +// ", "stmt_for", "weird_loop", "for", 20] + + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; + } + + function stmt_if() { + const the_if = token_now; + let the_else; + the_if.expression = condition(); + the_if.block = block(); + if (token_nxt.id === "else") { + advance("else"); + the_else = token_now; + the_if.else = ( + token_nxt.id === "if" + ? parse_statement() + : block() + ); + +// test_cause: +// ["if(0){0}else if(0){0}", "stmt_if", "else", "", 0] +// ["if(0){0}else{0}", "stmt_if", "else", "", 0] + + test_cause("else"); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + +// test_cause: +// ["if(0){break;}else{break;}", "stmt_if", "disrupt", "", 0] + + test_cause("disrupt"); + the_if.disrupt = true; + } else { + +// test_cause: +// ["if(0){break;}else{}", "stmt_if", "unexpected_a", "else", 14] + + warn("unexpected_a", the_else); + } + } + } + return the_if; + } + + function stmt_import() { + const the_import = token_now; + let name; + let names; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// if (typeof state.mode_module === "object") { +// +// // test_cause: +// // [" +// // /*global aa*/ +// // import aa from "aa" +// // ", "stmt_import", "unexpected_directive_a", "global", 1] +// +// warn( +// "unexpected_directive_a", +// state.mode_module, +// state.mode_module.directive +// ); +// } + + state.mode_module = true; + +// PR-436 - Add grammar for side-effect import-statement. + + if (token_nxt.id === "(string)") { + +// test_cause: +// ["import \"./aa.mjs\";", "stmt_import", "import_side_effect", "", 0] + + test_cause("import_side_effect"); + warn("expected_a_b", token_nxt, "{", artifact()); + advance(); + semicolon(); + return the_import; + } + if (token_nxt.identifier) { + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// ["import ignore from \"aa\"", "stmt_import", "unexpected_a", "ignore", 8] + + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + names = []; + advance("{"); + if (token_nxt.id !== "}") { + while (true) { + if (!token_nxt.identifier) { + +// test_cause: +// ["import {", "stmt_import", "expected_identifier_a", "(end)", 1] + + stop("expected_identifier_a"); + } + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// ["import {ignore} from \"aa\"", "stmt_import", "unexpected_a", "ignore", 9] + + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token_now; + if (!jslint_rgx_module.test(token_now.value)) { + +// test_cause: +// ["import aa from \"!aa\"", "stmt_import", "bad_module_name_a", "!aa", 16] + + warn("bad_module_name_a", token_now); + } + import_list.push(token_now.value); + semicolon(); + return the_import; + } + + function stmt_lbrace() { + +// test_cause: +// [";{}", "stmt_lbrace", "naked_block", "{", 2] +// ["class aa{}", "stmt_lbrace", "naked_block", "{", 9] + + warn("naked_block", token_now); + return block("naked"); + } + + function stmt_return() { + const the_return = token_now; + check_not_top_level(the_return); + if (functionage.finally > 0) { + +// test_cause: +// [" +// function aa(){try{}finally{return;}} +// ", "stmt_return", "unexpected_a", "return", 28] + + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (token_nxt.id !== ";" && the_return.line === token_nxt.line) { + the_return.expression = parse_expression(10); + } + advance(";"); + return the_return; + } + + function stmt_semicolon() { + +// test_cause: +// [";", "stmt_semicolon", "unexpected_a", ";", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function stmt_switch() { + const the_cases = []; + const the_switch = token_now; + let dups = []; + let exp; + let last; + let stmts; + let the_case; + let the_default; + let the_disrupt = true; + let the_last; + function is_dup(thing) { + return is_equal(thing, exp); + } + check_not_top_level(the_switch); + if (functionage.finally > 0) { + +// test_cause: +// [" +// function aa(){try{}finally{switch(0){}}} +// ", "stmt_switch", "unexpected_a", "switch", 28] + + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token_now.free = true; + the_switch.expression = parse_expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + while (true) { + +// Loop through cases with breaks. + + the_case = token_nxt; + the_case.arity = "statement"; + the_case.expression = []; + while (true) { + +// Loop through fallthrough cases. + + advance("case"); + token_now.switch = true; + exp = parse_expression(0); + if (dups.some(is_dup)) { + +// test_cause: +// [" +// switch(0){case 0:break;case 0:break} +// ", "stmt_switch", "unexpected_a", "0", 29] + + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (token_nxt.id !== "case") { + break; + } + } + +// test_cause: +// [" +// switch(0){case 1:case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 23] +// [" +// switch(0){case "aa":case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 26] +// [" +// switch(0){case "bb":case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 26] +// [" +// switch(0){case aa:case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24] +// [" +// switch(0){case bb:case aa:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24] + + check_ordered_case(the_case.expression); + stmts = parse_statements(); + if (stmts.length < 1) { + +// test_cause: +// [" +// switch(0){case 0:default:} +// ", "stmt_switch", "expected_statements_a", "default", 18] +// ["switch(0){case 0:}", "stmt_switch", "expected_statements_a", "}", 18] + + warn("expected_statements_a"); + +// PR-359 - Bugfix - Fixes issue #358 - switch-statement crashes jslint. + + break; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn("expected_a_before_b", token_nxt, "break;", artifact()); + } + if (token_nxt.id !== "case") { + break; + } + } + +// test_cause: +// [" +// switch(0){case 1:break;case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 29] +// [" +// switch(0){case "aa":break;case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 32] +// [" +// switch(0){case "bb":break;case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 32] +// [" +// switch(0){case aa:break;case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30] +// [" +// switch(0){case bb:break;case aa:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30] + + check_ordered_case(the_cases.map(function ({ + expression + }) { + return expression[0]; + })); + dups = undefined; + if (token_nxt.id === "default") { + the_default = token_nxt; + advance("default"); + token_now.switch = true; + advance(":"); + the_switch.else = parse_statements(); + if (the_switch.else.length < 1) { + +// test_cause: +// [" +// switch(0){case 0:break;default:} +// ", "stmt_switch", "unexpected_a", "default", 24] + + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + the_last = the_switch.else[ + the_switch.else.length - 1 + ]; + if ( + the_last.id === "break" + && the_last.label === undefined + ) { + +// test_cause: +// [" +// switch(0){case 0:break;default:break;} +// ", "stmt_switch", "unexpected_a", "break", 32] + + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; + } + + function stmt_throw() { + const the_throw = token_now; + the_throw.disrupt = true; + the_throw.expression = parse_expression(10); + semicolon(); + if (functionage.try > 0) { + +// test_cause: +// ["try{throw 0}catch(){}", "stmt_throw", "unexpected_a", "throw", 5] + + warn("unexpected_a", the_throw); + } + return the_throw; + } + + function stmt_try() { + const the_try = token_now; + let ignored; + let the_catch; + let the_disrupt; + if (functionage.try > 0) { + +// test_cause: +// ["try{try{}catch(){}}catch(){}", "stmt_try", "unexpected_a", "try", 5] + + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (token_nxt.id === "catch") { + ignored = "ignore"; + the_catch = token_nxt; + the_try.catch = the_catch; + advance("catch"); + +// Create new catch-scope for catch-parameter. + + catch_stack.push(catchage); + catchage = the_catch; + catch_list.push(catchage); + the_catch.context = empty(); + if (token_nxt.id === "(") { + advance("("); + if (!token_nxt.identifier) { + +// test_cause: +// ["try{}catch(){}", "stmt_try", "expected_identifier_a", ")", 12] + + return stop("expected_identifier_a"); + } + if (token_nxt.id !== "ignore") { + ignored = undefined; + the_catch.name = token_nxt; + enroll(token_nxt, "exception", true); + } + advance(); + advance(")"); + } + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + +// Restore previous catch-scope after catch-block. + + catchage = catch_stack.pop(); + +// PR-404 - Relax warning about missing `catch` in `try...finally` statement. +// +// } else { +// +// // test_cause: +// // ["try{}finally{break;}", "stmt_try", "expected_a_before_b", "finally", 6] +// +// warn("expected_a_before_b", token_nxt, "catch", artifact()); + + } + if (token_nxt.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; + } + + function stmt_var() { + let ellipsis; + let mode_const; + let name; + let the_brace; + let the_bracket; + let the_variable = token_now; + let variable_prv; + mode_const = the_variable.id === "const"; + the_variable.names = []; + +// A program may use var or let, but not both. + + if (!mode_const) { + if (mode_var === undefined) { + mode_var = the_variable.id; + } else if (the_variable.id !== mode_var) { + +// test_cause: +// ["let aa;var aa", "stmt_var", "expected_a_b", "var", 8] + + warn("expected_a_b", the_variable, mode_var, the_variable.id); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + +// test_cause: +// ["switch(0){case 0:var aa}", "stmt_var", "var_switch", "var", 18] + + warn("var_switch", the_variable); + } + switch ( + Boolean(functionage.statement_prv) + && functionage.statement_prv.id + ) { + case "const": + case "let": + case "var": + +// test_cause: +// ["const aa=0;const bb=0;", "stmt_var", "var_prv", "const", 0] +// ["let aa=0;let bb=0;", "stmt_var", "var_prv", "let", 0] +// ["var aa=0;var bb=0;", "stmt_var", "var_prv", "var", 0] + + test_cause("var_prv", functionage.statement_prv.id); + variable_prv = functionage.statement_prv; + break; + case "import": + +// test_cause: +// ["import aa from \"aa\";\nlet bb=0;", "stmt_var", "import_prv", "", 0] + + test_cause("import_prv"); + break; + case false: + break; + default: + if ( + (option_dict.beta && !option_dict.variable) + || the_variable.id === "var" + ) { + +// test_cause: +// ["console.log();let aa=0;", "stmt_var", "var_on_top", "let", 15] +// ["console.log();var aa=0;", "stmt_var", "var_on_top", "var", 15] +// ["try{aa();}catch(aa){var aa=0;}", "stmt_var", "var_on_top", "var", 21] +// ["while(0){var aa;}", "stmt_var", "var_on_top", "var", 10] + + warn("var_on_top", token_now); + } + } + while (true) { + if (token_nxt.id === "{") { + if (the_variable.id === "var") { + +// test_cause: +// ["var{aa}=0", "stmt_var", "unexpected_a", "var", 1] + + warn("unexpected_a", the_variable); + } + the_brace = token_nxt; + advance("{"); + while (true) { + name = token_nxt; + if (!name.identifier) { + +// test_cause: +// ["let {0}", "stmt_var", "expected_identifier_a", "0", 6] + + return stop("expected_identifier_a"); + } + survey(name); + advance(); + if (token_nxt.id === ":") { + advance(":"); + if (!token_nxt.identifier) { + +// test_cause: +// ["let {aa:0}", "stmt_var", "expected_identifier_a", "0", 9] +// ["let {aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 9] + + return stop("expected_identifier_a"); + } + +// PR-363 - Bugfix +// Add test against false-warning in code +// '/*jslint node*/\nlet {aa:bb} = {}; bb();'. +// +// token_nxt.label = name; +// the_variable.names.push(token_nxt); +// enroll(token_nxt, "variable", mode_const); + + name = token_nxt; + the_variable.names.push(name); + survey(name); + enroll(name, "variable", mode_const); + + advance(); + the_brace.open = true; + } else { + the_variable.names.push(name); + enroll(name, "variable", mode_const); + } + name.dead = false; + name.init = true; + if (token_nxt.id === "=") { + +// test_cause: +// ["let {aa=0}", "stmt_var", "assign", "", 0] + + test_cause("assign"); + advance("="); + name.expression = parse_expression(); + the_brace.open = true; + } + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + +// test_cause: +// ["let{bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8] + + check_ordered(the_variable.id, the_variable.names); + advance("}"); + advance("="); + the_variable.expression = parse_expression(0); + } else if (token_nxt.id === "[") { + if (the_variable.id === "var") { + +// test_cause: +// ["var[aa]=0", "stmt_var", "unexpected_a", "var", 1] + + warn("unexpected_a", the_variable); + } + the_bracket = token_nxt; + advance("["); + while (true) { + ellipsis = false; + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + if (!token_nxt.identifier) { + +// test_cause: +// ["let[]", "stmt_var", "expected_identifier_a", "]", 5] + + return stop("expected_identifier_a"); + } + name = token_nxt; + advance(); + the_variable.names.push(name); + enroll(name, "variable", mode_const); + name.dead = false; + name.init = true; + if (ellipsis) { + name.ellipsis = true; + break; + } + if (token_nxt.id === "=") { + advance("="); + name.expression = parse_expression(); + the_bracket.open = true; + } + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + advance("]"); + advance("="); + the_variable.expression = parse_expression(0); + } else if (token_nxt.identifier) { + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// [" +// let ignore;function aa(ignore) {} +// ", "stmt_var", "unexpected_a", "ignore", 5] + + warn("unexpected_a", name); + } + enroll(name, "variable", mode_const); + if (token_nxt.id === "=" || mode_const) { + advance("="); + name.dead = false; + name.init = true; + name.expression = parse_expression(0); + } + the_variable.names.push(name); + } else { + +// test_cause: +// ["let 0", "stmt_var", "expected_identifier_a", "0", 5] +// ["var{aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 8] + + return stop("expected_identifier_a"); + } + if (token_nxt.id !== ",") { + break; + } + +// test_cause: +// ["let aa,bb;", "stmt_var", "expected_a_b", ",", 7] + + warn("expected_a_b", token_nxt, ";", ","); + advance(","); + } + +// Warn if variable declarations are unordered. + + if ( + option_dict.beta + && !option_dict.unordered + && !option_dict.variable + && variable_prv + && ( + variable_prv.id + " " + variable_prv.names[0].id + > the_variable.id + " " + the_variable.names[0].id + ) + ) { + +// test_cause: +// ["const bb=0;const aa=0;", "stmt_var", "expected_a_b_before_c_d", "aa", 12] +// ["let bb;let aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8] +// ["var bb;var aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8] + + warn( + "expected_a_b_before_c_d", + the_variable, + the_variable.id, + the_variable.names[0].id, + variable_prv.id, + variable_prv.names[0].id + ); + } + semicolon(); + return the_variable; + } + + function stmt_while() { + const the_while = token_now; + check_not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + +// test_cause: +// ["function aa(){while(0){break;}}", "stmt_while", "weird_loop", "while", 15] + + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; + } + + function stmt_with() { + +// test_cause: +// ["with", "stmt_with", "unexpected_a", "with", 1] + + stop("unexpected_a", token_now); + } + + function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!jslint_rgx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!jslint_rgx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + +// test_cause: +// ["let aa={0:0}", "survey", "expected_identifier_a", "0", 9] + + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property_dict[id] === "number") { + property_dict[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (state.mode_property) { + if (tenure[id] !== true) { + +// test_cause: +// ["/*property aa*/\naa.bb", "survey", "unregistered_property_a", "bb", 4] + + warn("unregistered_property_a", name); + } + } else if ( + !option_dict.nomen + && name.identifier + && jslint_rgx_weird_property.test(id) + ) { + +// test_cause: +// ["aa.$", "survey", "weird_property_a", "$", 4] +// ["aa._", "survey", "weird_property_a", "_", 4] +// ["aa._aa", "survey", "weird_property_a", "_aa", 4] +// ["aa.aaSync", "survey", "weird_property_a", "aaSync", 4] +// ["aa.aa_", "survey", "weird_property_a", "aa_", 4] + + warn("weird_property_a", name); + } + property_dict[id] = 1; + } + return id; + } + +// These functions are used to specify the grammar of our language: + + function symbol(id, bp) { + +// Create a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax_dict[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax_dict[id] = the_symbol; + } + return the_symbol; + } + + function ternary(id1, id2) { + +// Create a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led_infix = function parse_ternary_led(left) { + const the_token = token_now; + let second; + second = parse_expression(20); + advance(id2); + token_now.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, parse_expression(10)]; + if (token_nxt.id !== ")") { + +// test_cause: +// ["0?0:0", "parse_ternary_led", "use_open", "?", 2] + + warn("use_open", the_token); + } + return the_token; + }; + return the_symbol; + } + +// Now we parse JavaScript. +// Begin defining the language. + + assignment("%="); + assignment("&="); + assignment("*="); + assignment("+="); + assignment("-="); + assignment("/="); + assignment("<<="); + assignment("="); + assignment(">>="); + assignment(">>>="); + assignment("^="); + assignment("|="); + constant("(number)", "number"); + constant("(regexp)", "regexp"); + constant("(string)", "string"); + constant("Function", "function", constant_Function); + constant("Infinity", "number", Infinity); + constant("NaN", "number", NaN); + constant("arguments", "object", constant_arguments); + constant("eval", "function", constant_eval); + constant("false", "boolean", false); + constant("ignore", "undefined", constant_ignore); + constant("isFinite", "function", constant_isInfinite); + constant("isNaN", "function", constant_isNaN); + constant("null", "null", null); + constant("this", "object", constant_this); + constant("true", "boolean", true); + constant("undefined", "undefined"); + infix(100, "!="); + infix(100, "!=="); + infix(100, "=="); + infix(100, "==="); + infix(110, "<"); + infix(110, "<="); + infix(110, ">"); + infix(110, ">="); + infix(110, "in"); + infix(110, "instanceof"); + infix(120, "<<"); + infix(120, ">>"); + infix(120, ">>>"); + infix(130, "+"); + infix(130, "-"); + infix(140, "%"); + infix(140, "*"); + infix(140, "/"); + infix(160, "(", infix_lparen); + infix(160, "`", infix_grave); + infix(170, ".", infix_dot); + infix(170, "=>", infix_fart_unwrapped); + infix(170, "?.", infix_option_chain); + infix(170, "[", infix_lbracket); + infix(35, "??"); + infix(40, "||"); + infix(50, "&&"); + infix(70, "|"); + infix(80, "^"); + infix(90, "&"); + infixr(150, "**"); + postassign("++"); + postassign("--"); + preassign("++"); + preassign("--"); + prefix("!!"); + prefix("!"); + prefix("(", prefix_lparen); + prefix("+"); + prefix("-"); + prefix("/=", prefix_assign_divide); + prefix("=>", prefix_fart); + prefix("[", prefix_lbracket); + prefix("`", prefix_tick); + prefix("async", prefix_async); + prefix("await", prefix_await); + prefix("function", prefix_function); + prefix("new", prefix_new); + prefix("typeof"); + prefix("void", prefix_void); + prefix("{", prefix_lbrace); + prefix("~"); + stmt(";", stmt_semicolon); + stmt("async", prefix_async); + stmt("await", prefix_await); + stmt("break", stmt_break); + stmt("const", stmt_var); + stmt("continue", stmt_continue); + stmt("debugger", stmt_debugger); + stmt("delete", stmt_delete); + stmt("do", stmt_do); + stmt("export", stmt_export); + stmt("for", stmt_for); + stmt("function", prefix_function); + stmt("if", stmt_if); + stmt("import", stmt_import); + stmt("let", stmt_var); + stmt("return", stmt_return); + stmt("switch", stmt_switch); + stmt("throw", stmt_throw); + stmt("try", stmt_try); + stmt("var", stmt_var); + stmt("while", stmt_while); + stmt("with", stmt_with); + stmt("{", stmt_lbrace); + symbol(")"); + symbol("*/"); + symbol(","); + symbol(":"); + symbol(";"); + symbol("]"); + symbol("async"); + symbol("await"); + symbol("case"); + symbol("catch"); + symbol("class"); + symbol("default"); + symbol("else"); + symbol("enum"); + symbol("finally"); + symbol("implements"); + symbol("interface"); + symbol("package"); + symbol("private"); + symbol("protected"); + symbol("public"); + symbol("static"); + symbol("super"); + symbol("void"); + symbol("yield"); + symbol("}"); + ternary("?", ":"); + +// Init token_nxt. + + advance(); + +// Parsing of JSON is simple: + + if (state.mode_json) { + state.token_tree = parse_json(); + advance("(end)"); + return; + } + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option_dict.browser) { + if (token_nxt.id === ";") { + advance(";"); + } + +// If we are not in a browser, then the file form of strict pragma may be used. + + } else if (token_nxt.value === "use strict") { + advance("(string)"); + advance(";"); + } + state.token_tree = parse_statements(); + advance("(end)"); + +// Check global functions are ordered. + + check_ordered( + "function", + function_list.map(function ({ + level, + name + }) { + return (level === 1) && name; + }).filter(function (name) { + return option_dict.beta && name && name.id; + }) + ); +} + +function jslint_phase4_walk(state) { + +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). + + let { + artifact, + catch_stack, + function_stack, + global_dict, + is_equal, + is_weird, + option_dict, + syntax_dict, + test_cause, + token_global, + warn + } = state; + let block_stack = []; // The stack of blocks. + let blockage = token_global; // The current block. + let catchage = catch_stack[0]; // The current catch-block. + let functionage = token_global; // The current function. + let postaction; + let postamble; + let posts = empty(); + let preaction; + let preamble; + let pres = empty(); + +// The relational operators. + + let relationop = object_assign_from_list(empty(), [ + "!=", "!==", "<", "<=", "==", "===", ">", ">=" + ], true); + +// Ambulation of the parse tree. + + function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; + } + + function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + task(the_token); + }); + } + } + }; + } + + function init_variable(name) { + let the_variable = lookup(name); + if (!the_variable || the_variable.readonly) { + warn("bad_assignment_a", name); + return; + } + the_variable.init = true; + } + + function lookup(thing) { + let id = thing.id; + let the_variable; + if (thing.arity !== "variable") { + return; + } + +// Look up the variable in the current context. + + the_variable = functionage.context[id] || catchage.context[id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable && the_variable.role === "label") { + +// test_cause: +// ["aa:while(0){aa;}", "lookup", "label_a", "aa", 13] + + warn("label_a", thing); + return the_variable; + } + if (!the_variable) { + function_stack.forEach(function ({ + context + }) { + if (context[id] && context[id].role !== "label") { + the_variable = context[id]; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (!the_variable && global_dict[id] === undefined) { + +// test_cause: +// ["aa", "lookup", "undeclared_a", "aa", 1] +// ["class aa{}", "lookup", "undeclared_a", "aa", 7] +// [" +// let aa=0;try{aa();}catch(bb){bb();}bb(); +// ", "lookup", "undeclared_a", "bb", 36] +// [" +// let aa=0;try{aa();}catch(ignore){bb();} +// ", "lookup", "undeclared_a", "bb", 34] + + warn("undeclared_a", thing); + return; + } + if (!the_variable) { + the_variable = { + dead: false, + id, + init: true, + parent: token_global, + readonly: true, + role: "variable", + used: 0 + }; + token_global.context[id] = the_variable; + } + the_variable.closure = true; + functionage.context[id] = the_variable; + } + if ( + ( + the_variable.calls === undefined + || functionage.name === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + && the_variable.dead + ) { + +// test_cause: +// ["let aa;if(aa){let bb;}bb;", "lookup", "out_of_scope_a", "bb", 23] + + warn("out_of_scope_a", thing); + } + return the_variable; + } + + function post_a(thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + let right; + if (thing.id === "=") { + if (thing.names !== undefined) { + +// test_cause: +// ["if(0){aa=0}", "post_a", "=", "", 0] + + test_cause("="); + +// Probably deadcode. +// if (Array.isArray(thing.names)) { +// thing.names.forEach(init_variable); +// } else { +// init_variable(thing.names); +// } + + jslint_assert( + !Array.isArray(thing.names), + `Expected !Array.isArray(thing.names).` + ); + init_variable(thing.names); + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + +// test_cause: +// ["aa.aa=undefined", "post_a", "expected_a_b", "undefined", 1] + + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.readonly) { + warn("bad_assignment_a", lvalue); + } + } + right = syntax_dict[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + +// test_cause: +// ["aa+=undefined", "post_a", "unexpected_a", "undefined", 5] + + warn("unexpected_a", thing.expression[1]); + } + } + } + + function post_a_pluseq(thing) { + const right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } + } + + function post_b(thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || is_equal(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + +// test_cause: +// ["if(0===0){0}", "post_b", "weird_relation_a", "===", 5] + + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option_dict.convert) { + if (thing.expression[0].value === "") { + +// test_cause: +// ["\"\"+0", "post_b", "expected_a_b", "\"\" +", 3] + + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + +// test_cause: +// ["0+\"\"", "post_b", "expected_a_b", "+ \"\"", 2] + + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + +// test_cause: +// ["aa=window[0]", "post_b", "weird_expression_a", "window[...]", 10] + + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + +// test_cause: +// ["aa=self[0]", "post_b", "weird_expression_a", "self[...]", 8] + + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === "." || thing.id === "?.") { + if (thing.expression.id === "RegExp") { + +// test_cause: +// ["aa=RegExp.aa", "post_b", "weird_expression_a", ".", 10] + + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + +// test_cause: +// ["0- -0", "post_b", "wrap_unary", "-", 4] + + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } + } + + function post_b_and(thing) { + if ( + is_weird(thing.expression[0]) + || is_equal(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + +// test_cause: +// ["aa=(()=>0)&&(()=>0)", "post_b_and", "weird_condition_a", "&&", 11] +// ["aa=(``?``:``)&&(``?``:``)", "post_b_and", "weird_condition_a", "&&", 14] +// ["aa=/./&&/./", "post_b_and", "weird_condition_a", "&&", 7] +// ["aa=0&&0", "post_b_and", "weird_condition_a", "&&", 5] +// ["aa=[]&&[]", "post_b_and", "weird_condition_a", "&&", 6] +// ["aa=`${0}`&&`${0}`", "post_b_and", "weird_condition_a", "&&", 10] +// [" +// aa=function aa(){}&&function aa(){} +// ", "post_b_and", "weird_condition_a", "&&", 19] +// ["aa={}&&{}", "post_b_and", "weird_condition_a", "&&", 6] + + warn("weird_condition_a", thing); + } + } + + function post_b_lbracket(thing) { + if (thing.expression[0].id === "RegExp") { + +// test_cause: +// ["aa=RegExp[0]", "post_b_lbracket", "weird_expression_a", "[", 10] + + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + +// test_cause: +// ["aa[[0]]", "post_b_lbracket", "weird_expression_a", "[", 4] + + warn("weird_expression_a", thing.expression[1]); + } + } + + function post_b_lparen(thing) { + let arg; + let array; + let cack; + let left = thing.expression[0]; + let new_date; + let paren; + let the_new; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + +// test_cause: +// ["aa=function(){}()", "post_b_lparen", "wrap_immediate", "(", 16] + + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "BigInt" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + +// test_cause: +// ["new BigInt()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Boolean()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Number()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new String()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Symbol()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new aa()", "post_b_lparen", "unexpected_a", "new", 1] + + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option_dict.eval) { + +// test_cause: +// ["new Function()", "post_b_lparen", "unexpected_a", "new Function", 5] + + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + +// test_cause: +// ["new Array()", "post_b_lparen", "expected_a_b", "new Array", 5] + + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + +// test_cause: +// ["new Object()", "post_b_lparen", "expected_a_b", "new Object", 5] + + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "BigInt" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + +// test_cause: +// ["let Aa=Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8] + + warn("expected_a_before_b", left, "new", artifact(left)); + } + } + } else if (left.id === ".") { + cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + +// test_cause: +// ["new Date.UTC()", "post_b_lparen", "cack", "", 0] + + test_cause("cack"); + cack = !cack; + } + if (jslint_rgx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + +// test_cause: +// ["new Date.UTC()", "post_b_lparen", "unexpected_a", "new", 1] + + warn("unexpected_a", the_new); + } else { + +// test_cause: +// ["let Aa=Aa.Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8] + + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + paren = left.expression; + if (paren.id === "(") { + array = paren.expression; + if (array.length === 1) { + new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + +// test_cause: +// [" +// new Date().getTime() +// ", "post_b_lparen", "expected_a_b", "new Date().getTime()", 1] + + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } + } + + function post_b_or(thing) { + if ( + is_weird(thing.expression[0]) + || is_equal(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + +// test_cause: +// ["aa=0||0", "post_b_or", "weird_condition_a", "||", 5] + + warn("weird_condition_a", thing); + } + } + + function post_s_export(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== token_global) { + +// test_cause: +// [" +// if(0){import aa from "aa";} +// ", "post_s_export", "misplaced_a", "import", 7] + + warn("misplaced_a", the_thing); + } + } + + function post_s_for(thing) { + +// Recurse walk_statement(). + + walk_statement(thing.inc); + } + + function post_s_function(thing) { + delete functionage.async; + delete functionage.finally; + delete functionage.loop; + delete functionage.statement_prv; + delete functionage.switch; + delete functionage.try; + functionage = function_stack.pop(); + if (thing.wrapped) { + +// test_cause: +// ["aa=(function(){})", "post_s_function", "unexpected_parens", "function", 5] + + warn("unexpected_parens", thing); + } + return post_s_lbrace(); + } + + function post_s_import(the_thing) { + const name = the_thing.name; + if (name) { + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return post_s_export(the_thing); + } + } + + function post_s_lbrace() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); + } + + function post_s_try(thing) { + if (thing.catch) { + if (thing.catch.name) { + Object.assign(catchage.context[thing.catch.name.id], { + dead: false, + init: true + }); + } + +// Recurse walk_statement(). + + walk_statement(thing.catch.block); + +// Restore previous catch-scope after catch-block. + + catchage = catch_stack.pop(); + } + } + + function post_s_var(thing) { + thing.names.forEach(function (name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + +// Probably deadcode. +// if (name.id === "{" || name.id === "[") { +// name.names.forEach(subactivate); +// } else { +// name.init = true; +// } + + jslint_assert( + !(name.id === "{" || name.id === "["), + `Expected !(name.id === "{" || name.id === "[").` + ); + name.init = true; + } + blockage.live.push(name); + }); + } + + function post_t(thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || is_equal(thing.expression[1], thing.expression[2]) + ) { + +// test_cause: +// ["let aa=(aa?`${0}`:`${0}`);", "post_t", "unexpected_a", "?", 11] +// ["let aa=(aa?`0`:`0`);", "post_t", "unexpected_a", "?", 11] + + warn("unexpected_a", thing); + } else if (is_equal(thing.expression[0], thing.expression[1])) { + +// test_cause: +// ["aa?aa:0", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "||", "?"); + } else if (is_equal(thing.expression[0], thing.expression[2])) { + +// test_cause: +// ["aa?0:aa", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + +// test_cause: +// ["aa?true:false", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + +// test_cause: +// ["aa?false:true", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + +// test_cause: +// ["(aa&&!aa?0:1)", "post_t", "wrap_condition", "&&", 4] + + warn("wrap_condition", thing.expression[0]); + } + } + + function post_u(thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option_dict.convert) { + +// test_cause: +// ["!!0", "post_u", "expected_a_b", "!!", 1] + + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } + } + + function post_u_plus(thing) { + const right = thing.expression; + if (!option_dict.convert) { + +// test_cause: +// ["aa=+0", "post_u_plus", "expected_a_b", "+", 4] + + warn("expected_a_b", thing, "Number(...)", "+"); + } + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } + } + + function pre_a_bitwise(thing) { + +// These are the bitwise operators. + + switch (!option_dict.bitwise && thing.id) { + case "&": + case "&=": + case "<<": + case "<<=": + case ">>": + case ">>=": + case ">>>": + case ">>>=": + case "^": + case "^=": + case "|": + case "|=": + case "~": + +// test_cause: +// ["0&0", "pre_a_bitwise", "unexpected_a", "&", 2] +// ["0&=0", "pre_a_bitwise", "unexpected_a", "&=", 2] +// ["0<<0", "pre_a_bitwise", "unexpected_a", "<<", 2] +// ["0<<=0", "pre_a_bitwise", "unexpected_a", "<<=", 2] +// ["0>>0", "pre_a_bitwise", "unexpected_a", ">>", 2] +// ["0>>=0", "pre_a_bitwise", "unexpected_a", ">>=", 2] +// ["0>>>0", "pre_a_bitwise", "unexpected_a", ">>>", 2] +// ["0>>>=0", "pre_a_bitwise", "unexpected_a", ">>>=", 2] +// ["0^0", "pre_a_bitwise", "unexpected_a", "^", 2] +// ["0^=0", "pre_a_bitwise", "unexpected_a", "^=", 2] +// ["0|0", "pre_a_bitwise", "unexpected_a", "|", 2] +// ["0|=0", "pre_a_bitwise", "unexpected_a", "|=", 2] +// ["~0", "pre_a_bitwise", "unexpected_a", "~", 1] + + warn("unexpected_a", thing); + break; + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + +// test_cause: +// ["0<0<0", "pre_a_bitwise", "unexpected_a", "<", 4] + + warn("unexpected_a", thing); + } + } + + function pre_b(thing) { + let left; + let right; + let value; + if (relationop[thing.id] === true) { + left = thing.expression[0]; + right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + +// test_cause: +// ["NaN===NaN", "pre_b", "number_isNaN", "===", 4] + + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + +// test_cause: +// ["typeof 0===0", "pre_b", "expected_string_a", "0", 12] + + warn("expected_string_a", right); + } + } else { + value = right.value; + if (value === "null" || value === "undefined") { + +// test_cause: +// [" +// typeof aa==="undefined" +// ", "pre_b", "unexpected_typeof_a", "undefined", 13] + + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "bigint" + && value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + +// test_cause: +// ["typeof 0===\"aa\"", "pre_b", "expected_type_string_a", "aa", 12] + + warn("expected_type_string_a", right, value); + } + } + } + } + } + + function pre_b_eqeq(thing) { + +// test_cause: +// ["0==0", "pre_b_eqeq", "expected_a_b", "==", 2] + + warn("expected_a_b", thing, "===", "=="); + } + + function pre_b_in(thing) { + +// test_cause: +// ["aa in aa", "pre_b_in", "infix_in", "in", 4] + + warn("infix_in", thing); + } + + function pre_b_instanceof(thing) { + +// test_cause: +// ["0 instanceof 0", "pre_b_instanceof", "unexpected_a", "instanceof", 3] + + warn("unexpected_a", thing); + } + + function pre_b_lparen(thing) { + const left = thing.expression[0]; + let left_variable; + let parent; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + parent = functionage.name.parent; + if (parent) { + left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + +// Probably deadcode. +// && left_variable.dead + + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } + } + + function pre_b_noteq(thing) { + +// test_cause: +// ["0!=0", "pre_b_noteq", "expected_a_b", "!=", 2] + + warn("expected_a_b", thing, "!==", "!="); + } + + function pre_b_or(thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + +// test_cause: +// ["0&&0||0", "pre_b_or", "and", "&&", 2] + + warn("and", thang); + } + }); + } + + function pre_s_for(thing) { + let the_variable; + if (thing.name !== undefined) { + thing.name.dead = false; + the_variable = lookup(thing.name); + if (the_variable !== undefined) { + if (the_variable.init && the_variable.readonly) { + +// test_cause: +// ["const aa=0;for(aa in aa){}", "pre_s_for", "bad_assignment_a", "aa", 16] + + warn("bad_assignment_a", thing.name); + } + the_variable.init = true; + } + } + +// Recurse walk_statement(). + + walk_statement(thing.initial); + } + + function pre_s_function(thing) { + +// test_cause: +// ["()=>0", "pre_s_function", "", "", 0] +// ["(function (){}())", "pre_s_function", "", "", 0] +// ["function aa(){}", "pre_s_function", "", "", 0] + + test_cause(""); + if (thing.arity === "statement" && blockage.body !== true) { + +// test_cause: +// ["if(0){function aa(){}\n}", "pre_s_function", "unexpected_a", "function", 7] + + warn("unexpected_a", thing); + } + function_stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + +// test_cause: +// [" +// /*jslint getset*/ +// aa={get aa(aa){}} +// ", "pre_s_function", "bad_get", "function", 9] + + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + +// test_cause: +// [" +// /*jslint getset*/ +// aa={set aa(){}} +// ", "pre_s_function", "bad_set", "function", 9] + + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); + } + + function pre_s_lbrace(thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; + } + + function pre_try(thing) { + if (thing.catch !== undefined) { + +// Create new catch-scope for catch-parameter. + + catch_stack.push(catchage); + catchage = thing.catch; + } + } + + function pre_v(thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } + } + + function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); + } + + function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + +// test_cause: +// ["(function(){}())", "walk_expression", "isArray", "", 0] +// ["0&&0", "walk_expression", "isArray", "", 0] + + test_cause("isArray"); + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + +// PR-414 - Bugfix - fix fart-body not being walked. + + if (thing.id === "function" || thing.id === "=>") { + +// test_cause: +// ["aa=()=>0", "walk_expression", "function", "=>", 0] +// ["aa=function(){}", "walk_expression", "function", "function", 0] + + test_cause("function", thing.id); + +// Recurse walk_statement(). + + walk_statement(thing.block); + } + if ( + thing.arity === "preassign" || thing.arity === "postassign" + ) { + +// test_cause: +// ["aa=++aa", "walk_expression", "unexpected_a", "++", 4] +// ["aa=--aa", "walk_expression", "unexpected_a", "--", 4] + + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + +// test_cause: +// ["aa[aa=0]", "walk_expression", "unexpected_statement_a", "=", 6] + + warn("unexpected_statement_a", thing); + } + +// test_cause: +// ["aa=0", "walk_expression", "default", "", 0] + + test_cause("default"); + postamble(thing); + } + } + } + + function walk_statement(thing) { + if (!thing) { + return; + } + if (Array.isArray(thing)) { + +// test_cause: +// ["+[]", "walk_statement", "isArray", "", 0] + + test_cause("isArray"); + +// Recurse walk_statement(). + + thing.forEach(walk_statement); + return; + } + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + +// test_cause: +// ["0&&0", "walk_statement", "unexpected_expression_a", "&&", 2] + + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + && thing.id !== "import" + ) { + +// test_cause: +// ["!0", "walk_statement", "unexpected_expression_a", "!", 1] +// ["+[]", "walk_statement", "unexpected_expression_a", "+", 1] +// ["+new aa()", "walk_statement", "unexpected_expression_a", "+", 1] +// ["0", "walk_statement", "unexpected_expression_a", "0", 1] +// ["typeof 0", "walk_statement", "unexpected_expression_a", "typeof", 1] + + warn("unexpected_expression_a", thing); + } + +// Recurse walk_statement(). + + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + + postaction = action(posts); + postamble = amble(posts); + preaction = action(pres); + preamble = amble(pres); + postaction("assignment", "+=", post_a_pluseq); + postaction("assignment", post_a); + postaction("binary", "&&", post_b_and); + postaction("binary", "(", post_b_lparen); + postaction("binary", "=>", post_s_function); + postaction("binary", "[", post_b_lbracket); + postaction("binary", "||", post_b_or); + postaction("binary", post_b); + postaction("statement", "const", post_s_var); + postaction("statement", "export", post_s_export); + postaction("statement", "for", post_s_for); + postaction("statement", "function", post_s_function); + postaction("statement", "import", post_s_import); + postaction("statement", "let", post_s_var); + postaction("statement", "try", post_s_try); + postaction("statement", "var", post_s_var); + postaction("statement", "{", post_s_lbrace); + postaction("ternary", post_t); + postaction("unary", "+", post_u_plus); + postaction("unary", "function", post_s_function); + postaction("unary", post_u); + preaction("assignment", pre_a_bitwise); + preaction("binary", "!=", pre_b_noteq); + preaction("binary", "(", pre_b_lparen); + preaction("binary", "==", pre_b_eqeq); + preaction("binary", "=>", pre_s_function); + preaction("binary", "in", pre_b_in); + preaction("binary", "instanceof", pre_b_instanceof); + preaction("binary", "||", pre_b_or); + preaction("binary", pre_b); + preaction("binary", pre_a_bitwise); + preaction("statement", "for", pre_s_for); + preaction("statement", "function", pre_s_function); + preaction("statement", "try", pre_try); + preaction("statement", "{", pre_s_lbrace); + preaction("unary", "function", pre_s_function); + preaction("unary", "~", pre_a_bitwise); + preaction("variable", pre_v); + + walk_statement(state.token_tree); +} + +function jslint_phase5_whitage(state) { + +// PHASE 5. Check whitespace between tokens in . + + let { + artifact, + catch_list, + function_list, + function_stack, + option_dict, + test_cause, + token_global, + token_list, + warn + } = state; + let closer = "(end)"; + let free = false; + +// free = false + +// cause: +// "()=>0" +// "aa()" +// "aa(0,0)" +// "function(){}" + +// free = true + +// cause: +// "(0)" +// "(aa)" +// "aa(0)" +// "do{}while()" +// "for(){}" +// "if(){}" +// "switch(){}" +// "while(){}" + + let left = token_global; + let margin = 0; + let mode_indent = ( + +// PR-330 - Allow 2-space indent. + + option_dict.indent2 + ? 2 + : 4 + ); + let nr_comments_skipped = 0; + let open = true; + let opening = true; + let right; + +// This is the set of infix operators that require a space on each side. + + let spaceop = object_assign_from_list(empty(), [ + "!=", "!==", "%", "%=", "&", "&&", "&=", "*", "*=", "+=", "-=", "/", + "/=", "<", "<<", "<<=", "<=", "=", "==", "===", "=>", ">", ">=", ">>", + ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" + ], true); + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + const name = the_function.context[id]; + if (id !== "ignore" && name.parent === the_function) { + +// test_cause: +// ["function aa(aa) {return aa;}", "delve", "id", "", 0] + + test_cause("id"); + if ( + name.used === 0 + +// Probably deadcode. +// && ( +// name.role !== "function" +// || name.parent.arity !== "unary" +// ) + + && jslint_assert( + name.role !== "function", + `Expected name.role !== "function".` + ) + ) { + +// test_cause: +// ["/*jslint node*/\nlet aa;", "delve", "unused_a", "aa", 5] +// ["function aa(aa){return;}", "delve", "unused_a", "aa", 13] +// ["let aa=0;try{aa();}catch(bb){aa();}", "delve", "unused_a", "bb", 26] + + warn("unused_a", name); + } else if (!name.init) { + +// test_cause: +// ["/*jslint node*/\nlet aa;aa();", "delve", "uninitialized_a", "aa", 5] + + warn("uninitialized_a", name); + } + } + }); + } + + function expected_at(at) { + +// Probably deadcode. +// if (right === undefined) { +// right = token_nxt; +// } + + jslint_assert( + !(right === undefined), + `Expected !(right === undefined).` + ); + warn( + "expected_a_at_b_c", + right, + artifact(right), + +// Fudge column numbers in warning message. + + at + jslint_fudge, + right.from + jslint_fudge + ); + } + + function no_space() { + if (left.line === right.line) { + +// from: +// if (left.line === right.line) { +// no_space(); +// } else { + + if (left.thru !== right.from && nr_comments_skipped === 0) { + +// test_cause: +// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14] + + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + +// from: +// } else if ( +// right.arity === "binary" +// && right.id === "(" +// && free +// ) { +// no_space(); +// } else if ( + +// Probably deadcode. +// if (open) { +// const at = ( +// free +// ? margin +// : margin + 8 +// ); +// if (right.from < at) { +// expected_at(at); +// } +// } else { +// if (right.from !== margin + 8) { +// expected_at(margin + 8); +// } +// } + + jslint_assert(open, `Expected open.`); + jslint_assert(free, `Expected free.`); + if (right.from < margin) { + +// test_cause: +// ["let aa = aa(\naa\n()\n);", "expected_at", "expected_a_at_b_c", "5", 1] + + expected_at(margin); + } + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line || !open) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (right.from !== margin) { + expected_at(margin); + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn("expected_space_a_b", right, artifact(left), artifact(right)); + } + } + + function pop() { + const previous = function_stack.pop(); + closer = previous.closer; + free = previous.free; + margin = previous.margin; + open = previous.open; + opening = previous.opening; + } + + function push() { + function_stack.push({ + closer, + free, + margin, + open, + opening + }); + } + +// uninitialized_and_unused(); +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (state.mode_module === true || option_dict.node) { + delve(token_global); + } + catch_list.forEach(delve); + function_list.forEach(delve); + + if (option_dict.white) { + return; + } + +// whitage(); +// Go through the token list, looking at usage of whitespace. + + token_list.forEach(function whitage(the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + +// The open and close pairs. + + switch (left.id) { + case "${": + case "(": + case "[": + case "{": + +// test_cause: +// ["let aa=[];", "whitage", "opener", "", 0] +// ["let aa=`${0}`;", "whitage", "opener", "", 0] +// ["let aa=aa();", "whitage", "opener", "", 0] +// ["let aa={};", "whitage", "opener", "", 0] + + test_cause("opener"); + +// Probably deadcode. +// case "${}": + + jslint_assert( + !(left.id + right.id === "${}"), + "Expected !(left.id + right.id === \"${}\")." + ); + switch (left.id + right.id) { + case "()": + case "[]": + case "{}": + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like '{]') have already been detected. + +// test_cause: +// ["let aa=[];", "whitage", "opener_closer", "", 0] +// ["let aa=aa();", "whitage", "opener_closer", "", 0] +// ["let aa={};", "whitage", "opener_closer", "", 0] + + test_cause("opener_closer"); + if (left.line === right.line) { + +// test_cause: +// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14] + + no_space(); + } else { + +// test_cause: +// ["let aa = aa(\n );", "expected_at", "expected_a_at_b_c", "1", 2] + + at_margin(0); + } + break; + default: + +// test_cause: +// ["let aa=(0);", "whitage", "opener_operand", "", 0] +// ["let aa=[0];", "whitage", "opener_operand", "", 0] +// ["let aa=`${0}`;", "whitage", "opener_operand", "", 0] +// ["let aa=aa(0);", "whitage", "opener_operand", "", 0] +// ["let aa={aa:0};", "whitage", "opener_operand", "", 0] + + test_cause("opener_operand"); + opening = left.open || (left.line !== right.line); + push(); + switch (left.id) { + case "${": + closer = "}"; + break; + case "(": + closer = ")"; + break; + case "[": + closer = "]"; + break; + case "{": + closer = "}"; + break; + } + if (opening) { + +// test_cause: +// ["function aa(){\nreturn;\n}", "whitage", "opening", "", 0] +// ["let aa=(\n0\n);", "whitage", "opening", "", 0] +// ["let aa=[\n0\n];", "whitage", "opening", "", 0] +// ["let aa=`${\n0\n}`;", "whitage", "opening", "", 0] +// ["let aa={\naa:0\n};", "whitage", "opening", "", 0] + + test_cause("opening"); + free = closer === ")" && left.free; + open = true; + margin += mode_indent; + if (right.role === "label") { + if (right.from !== 0) { + +// test_cause: +// [" +// function aa() { +// bb: +// while (aa) { +// if (aa) { +// break bb; +// } +// } +// } +// ", "expected_at", "expected_a_at_b_c", "1", 2] + + expected_at(0); + } + } else if (right.switch) { + at_margin(-mode_indent); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + +// test_cause: +// [" +// function aa() {bb: +// while (aa) { +// aa(); +// } +// } +// ", "whitage", "expected_line_break_a_b", "bb", 16] + + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + +// test_cause: +// ["let aa=(0);", "whitage", "not_free", "", 0] +// ["let aa=[0];", "whitage", "not_free", "", 0] +// ["let aa=`${0}`;", "whitage", "not_free", "", 0] +// ["let aa={aa:0};", "whitage", "not_free", "", 0] + + test_cause("not_free"); + free = false; + open = false; + +// test_cause: +// ["let aa = ( 0 );", "no_space_only", "unexpected_space_a_b", "0", 12] + + no_space_only(); + } + } + break; + default: + if (right.statement === true) { + if (left.id === "else") { + +// test_cause: +// [" +// let aa = 0; +// if (aa) { +// aa(); +// } else if (aa) { +// aa(); +// } +// ", "one_space_only", "expected_space_a_b", "if", 9] + + one_space_only(); + } else { + +// test_cause: +// [" let aa = 0;", "expected_at", "expected_a_at_b_c", "1", 2] + + at_margin(0); + open = false; + } + +// If right is a closer, then pop the previous state. + + } else if (right.id === closer) { + pop(); + if (opening && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-mode_indent); + } else if (right.role === "label") { + if (right.from !== 0) { + +// test_cause: +// [" +// function aa() { +// aa();cc: +// while (aa) { +// if (aa) { +// break cc; +// } +// } +// } +// ", "expected_at", "expected_a_at_b_c", "1", 10] + + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + +// test_cause: +// ["let {aa,bb} = 0;", "one_space", "expected_space_a_b", "bb", 9] + + one_space(); + } else { + +// test_cause: +// [" +// function aa() { +// aa( +// 0,0 +// ); +// } +// ", "expected_at", "expected_a_at_b_c", "9", 11] + + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + +// test_cause: +// [" +// let aa = ( +// aa +// ? 0 +// : 1 +// ); +// ", "expected_at", "expected_a_at_b_c", "5", 1] + + at_margin(0); + } else { + +// test_cause: +// ["let aa = (aa ? 0 : 1);", "whitage", "use_open", "?", 14] + + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + +// test_cause: +// ["let aa = aa(\naa ()\n);", "no_space", "unexpected_space_a_b", "(", 4] + + no_space(); + } else if ( + left.id === "." + || left.id === "?." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + || (right.id === "." || right.id === "?.") + ) { + +// test_cause: +// ["let aa = 0 ;", "no_space_only", "unexpected_space_a_b", ";", 12] +// ["let aa = aa ?.aa;", "no_space_only", "unexpected_space_a_b", "?.", 13] + + no_space_only(); + } else if (left.id === ";") { + +// test_cause: +// [" +// /*jslint for*/ +// function aa() { +// for ( +// aa(); +// aa; +// aa() +// ) { +// aa(); +// } +// } +// ", "expected_at", "expected_a_at_b_c", "9", 1] + + if (open) { + at_margin(0); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || left.id === "await" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + +// test_cause: +// [" +// function aa() { +// do { +// aa(); +// } while(aa()); +// } +// ", "one_space_only", "expected_space_a_b", "(", 12] + + one_space_only(); + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || left.id === "async" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + +// test_cause: +// ["let aa=0;", "one_space", "expected_space_a_b", "0", 8] +// ["let aa={\naa:\n0\n};", "expected_at", "expected_a_at_b_c", "5", 1] + + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +function jslint_report({ + exports, + froms, + functions, + global, + json, + module, + property, + stop, + warnings +}) { + +// This function will create human-readable, html-report +// for warnings, properties, and functions from jslint-result-object. +// +// Example usage: +// let result = jslint("console.log('hello world')"); +// let html = jslint_report(result); + + let html = ""; + let length_80 = 1111; + + function address(line = 1, column = 1) { + +// This function will create HTML address element from and + + return `
    ${Number(line)}: ${Number(column)}
    `; + + } + + function detail(title, list) { + return ( + (Array.isArray(list) && list.length > 0) + ? ( + +// Google Lighthouse Accessibility -
    's do not contain only properly-ordered +//
    and
    groups, ". + + code.replace(( + /^]*?>\n([\S\s]*?\n)<\/script>$/gm + ), function (ignore, match1, ii) { + jslint_from_file({ + code: match1, + file: file + ". + + import_meta_url = import_meta_url || jslint_import_meta_url; + if ( + jslint_rgx_url_search_window_jslint.test(import_meta_url) + && (typeof globalThis === "object" && globalThis) + ) { + globalThis.jslint = jslint; + } + +// Feature-detect nodejs. + + if (!( + (typeof process === "object" && process) + && process.versions + && typeof process.versions.node === "string" + && !mode_noop + )) { + return exit_code; + } + console_error = console_error || console.error; + console_log = console_log || console.log; + process_argv = process_argv || process.argv; + process_env = process_env || process.env; + process_exit = process_exit || process.exit; + await moduleFsInit(); + if ( + !( + +// Feature-detect nodejs-cli. + + process.execArgv.indexOf("--eval") === -1 + && process.execArgv.indexOf("-e") === -1 + && ( + ( + /[\/|\\]jslint(?:\.[cm]?js)?$/m + ).test(process_argv[1]) + || mode_cli + ) + && ( + moduleUrl.fileURLToPath(import_meta_url) + === + modulePath.resolve(process_argv[1]) + ) + ) + && !mode_cli + ) { + return exit_code; + } + +// init commmand + + command = String(process_argv[2]).split("="); + command[1] = command.slice(1).join("="); + + switch (command[0]) { + +// PR-362 - Add API Doc. + + case "jslint_apidoc": + await jslint_apidoc(Object.assign(JSON.parse(process_argv[3]), { + pathname: command[1] + })); + return; + +// PR-363 - Add command jslint_report. + + case "jslint_report": + mode_report = command[1]; + process_argv = process_argv.slice(1); + break; + +// COMMIT-b26d6df2 - Add command jslint_wrapper_vim. + + case "jslint_wrapper_vim": + mode_wrapper_vim = true; + process_argv = process_argv.slice(1); + break; + +// PR-364 - Add command v8_coverage_report. + + case "v8_coverage_report": + await v8CoverageReportCreate({ + consoleError: console_error, + coverageDir: command[1], + processArgv: process_argv.slice(3) + }); + return; + } + +// Normalize file relative to process.cwd(). + + process_argv.slice(2).some(function (arg) { + if (!arg.startsWith("-")) { + file = file || arg; + return true; + } + }); + if (!file) { + return; + } + file = modulePath.resolve(file) + "/"; + if (file.startsWith(process.cwd() + "/")) { + file = file.replace(process.cwd() + "/", "").slice(0, -1) || "."; + } + file = file.replace(( + /\\/g + ), "/").replace(( + /\/$/g + ), ""); + if (source) { + data = source; + } else { + +// jslint_cli - jslint directory. + + try { + data = await moduleFs.promises.readdir(file, "utf8"); + } catch (ignore) {} + if (data) { + await Promise.all(data.map(async function (file2) { + let code; + let time_start = Date.now(); + file2 = file + "/" + file2; + switch (( + /\.\w+?$|$/m + ).exec(file2)[0]) { + case ".cjs": + case ".html": + case ".js": + case ".json": + case ".md": + case ".mjs": + case ".sh": + break; + default: + return; + } + try { + code = await moduleFs.promises.readFile(file2, "utf8"); + } catch (ignore) { + return; + } + if ( + ( + /(?:\b|_)(?:lock|min|raw|rollup)(?:\b|_)/ + ).test(file2) + || !(code && code.length < 1048576) + ) { + return; + } + jslint_from_file({ + code, + file: file2, + option + }); + console_error( + "jslint - " + (Date.now() - time_start) + "ms - " + file2 + ); + })); + process_exit(exit_code); + return exit_code; + } + +// jslint_cli - jslint file. + + try { + data = await moduleFs.promises.readFile(file, "utf8"); + } catch (err) { + console_error(err); + exit_code = 1; + process_exit(exit_code); + return exit_code; + } + } + result = jslint_from_file({ + code: data, + file, + option + }); + if (mode_report) { + result = jslint.jslint_report(result); + result = `\n${result}\n`; + await fsWriteFileWithParents(mode_report, result); + } + process_exit(exit_code); + return exit_code; +} + +function jslint_phase1_split() { + +// PHASE 1. Split by newlines into . + + return; +} + +function jslint_phase2_lex(state) { + +// PHASE 2. Lex into . + + let { + artifact, + directive_list, + global_dict, + global_list, + line_list, + option_dict, + stop, + stop_at, + tenure, + test_cause, + token_global, + token_list, + warn, + warn_at + } = state; + let char; // The current character being lexed. + let column = 0; // The column number of the next character. + let from; // The starting column number of the token. + let from_mega; // The starting column of megastring. + let line = 0; // The line number of the next character. + let line_disable; // The starting line of "/*jslint-disable*/". + let line_mega; // The starting line of megastring. + let line_source = ""; // The remaining line source string. + let line_whole = ""; // The whole line source string. + let mode_digits_empty_string = 1; + let mode_digits_numeric_separator = 2; + let mode_directive = true; // true if directives are still allowed. + let mode_mega = false; // true if currently parsing a megastring + // ... literal. + let mode_regexp; // true if regular expression literal seen on + // ... this line. + let paren_backtrack_list = []; // List of most recent "(" tokens at any + // ... paren-depth. + let paren_depth = 0; // Keeps track of current paren-depth. + let snippet = ""; // A piece of string. + let token_1; // The first token. + let token_prv = token_global; // The previous token including + // ... comments. + let token_prv_expr = token_global; // The previous token excluding + // ... comments. + +// Most tokens, including the identifiers, operators, and punctuators, can be +// found with a regular expression. Regular expressions cannot correctly match +// regular expression literals, so we will match those the hard way. String +// literals and number literals can be matched by regular expressions, but they +// don't provide good warnings. The functions char_after, char_before, +// read_digits, and char_after_escape help in the parsing of literals. + + function char_after(match) { + +// Get the next character from the source line. Remove it from the line_source, +// and append it to the snippet. Optionally check that the previous character +// matched an expected value. + + if (match !== undefined && char !== match) { + +// test_cause: +// ["aa=/[", "char_after", "expected_a", "]", 5] +// ["aa=/aa{/", "char_after", "expected_a_b", "/", 8] + + return ( + char === "" + ? stop_at("expected_a", line, column - 1, match) + : stop_at("expected_a_b", line, column, match, char) + ); + } + char = line_source.slice(0, 1); + line_source = line_source.slice(1); + snippet += char || " "; + column += 1; + return char; + } + + function char_after_escape(extra) { + +// Validate char after escape "\\". + + char_after("\\"); + switch (char) { + case "": + +// test_cause: +// ["\"\\", "char_after_escape", "unclosed_string", "", 2] + + return stop_at("unclosed_string", line, column); + case "/": + return char_after(); + case "\\": + return char_after(); + case "`": + return char_after(); + case "b": + return char_after(); + case "f": + return char_after(); + case "n": + return char_after(); + case "r": + return char_after(); + case "t": + +// test_cause: +// ["\"\\/\\\\\\`\\b\\f\\n\\r\\t\"", "char_after_escape", "char_after", "", 0] + + test_cause("char_after"); + return char_after(); + case "u": + if (char_after("u") === "{") { + if (state.mode_json) { + +// test_cause: +// ["[\"\\u{12345}\"]", "char_after_escape", "unexpected_a", "{", 5] + + warn_at("unexpected_a", line, column, char); + } + if (read_digits("x", undefined) > 5) { + +// test_cause: +// ["\"\\u{123456}\"", "char_after_escape", "too_many_digits", "", 11] + + warn_at("too_many_digits", line, column); + } + if (char !== "}") { + +// test_cause: +// ["\"\\u{12345\"", "char_after_escape", "expected_a_before_b", "\"", 10] + + stop_at("expected_a_before_b", line, column, "}", char); + } + return char_after(); + } + char_before(); + if (read_digits("x", mode_digits_empty_string) < 4) { + +// test_cause: +// ["\"\\u0\"", "char_after_escape", "expected_four_digits", "", 5] + + warn_at("expected_four_digits", line, column); + } + return; + default: + if (extra && extra.indexOf(char) >= 0) { + return char_after(); + } + +// test_cause: +// ["\"\\0\"", "char_after_escape", "unexpected_a_before_b", "0", 3] + + warn_at("unexpected_a_before_b", line, column, "\\", char); + } + } + + function char_before() { + +// Back up one character by moving a character from the end of the snippet to +// the front of the line_source. + + char = snippet.slice(-1); + line_source = char + line_source; + column -= char.length; + +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + return char; + } + + function check_numeric_separator(digits, column) { + +// This function will check for illegal numeric-separator in . + + digits.replace(( + jslint_rgx_numeric_separator_illegal + ), function (ignore, ii) { + +// test_cause: +// ["0x0_0_;", "check_numeric_separator", "illegal_num_separator", "", 6] +// ["0x0_0__0;", "check_numeric_separator", "illegal_num_separator", "", 6] +// ["aa=1_2_;", "check_numeric_separator", "illegal_num_separator", "", 7] +// ["aa=1_2__3;", "check_numeric_separator", "illegal_num_separator", "", 7] +// ["aa=1_2_n;", "check_numeric_separator", "illegal_num_separator", "", 7] + + warn_at("illegal_num_separator", line, column + ii + 1); + return ""; + }); + } + + function lex_comment() { + let body; + let ii = 0; + let jj = 0; + let the_comment; + +// Create a comment object. Comments are not allowed in JSON text. Comments can +// include directives and notices of incompletion. + +// Create token from comment //.... + + if (snippet === "//") { + snippet = line_source; + line_source = ""; + the_comment = token_create("(comment)", snippet); + if (mode_mega) { + +// test_cause: +// ["`${//}`", "lex_comment", "unexpected_comment", "`", 4] + + warn("unexpected_comment", the_comment, "`"); + } + +// Create token from comment /*...*/. + + } else { + snippet = []; + if (line_source[0] === "/") { + +// test_cause: +// ["/*/", "lex_comment", "unexpected_a", "/", 2] + + warn_at("unexpected_a", line, column + ii, "/"); + } + +// Lex/loop through each line until "*/". + + while (true) { + // jslint_rgx_star_slash + ii = line_source.indexOf("*/"); + if (ii >= 0) { + break; + } + // jslint_rgx_slash_star + ii = line_source.indexOf("/*"); + if (ii >= 0) { + +// test_cause: +// ["/*/*", "lex_comment", "nested_comment", "", 2] + + warn_at("nested_comment", line, column + ii); + } + snippet.push(line_source); + line_source = read_line(); + if (line_source === undefined) { + +// test_cause: +// ["/*", "lex_comment", "unclosed_comment", "", 1] + + return stop_at("unclosed_comment", line, column); + } + } + jj = line_source.slice(0, ii).search( + jslint_rgx_slash_star_or_slash + ); + if (jj >= 0) { + +// test_cause: +// ["/*/**/", "lex_comment", "nested_comment", "", 2] + + warn_at("nested_comment", line, column + jj); + } + snippet.push(line_source.slice(0, ii)); + snippet = snippet.join(" "); + column += ii + 2; + line_source = line_source.slice(ii + 2); + the_comment = token_create("(comment)", snippet); + } + +// Uncompleted work comment. + + if (!option_dict.devel && jslint_rgx_todo.test(snippet)) { + +// test_cause: +// ["//todo", "lex_comment", "todo_comment", "(comment)", 1] //jslint-ignore-line + + warn("todo_comment", the_comment); + } + +// Lex directives in comment. + + [ + the_comment.directive, body + ] = Array.from(snippet.match(jslint_rgx_directive) || []).slice(1); + if (the_comment.directive === undefined) { + return the_comment; + } + directive_list.push(the_comment); + if (!mode_directive) { + +// test_cause: +// ["0\n/*global aa*/", "lex_comment", "misplaced_directive_a", "global", 1] + + warn_at("misplaced_directive_a", line, from, the_comment.directive); + return the_comment; + } + +// lex_directive(); +// JSLint recognizes three directives that can be encoded in comments. This +// function processes one item, and calls itself recursively to process the +// next one. + +// Lex/loop through each directive in /*...*/ + + ii = 0; + body.replace(jslint_rgx_directive_part, function ( + match0, + key, + val, + jj + ) { + if (ii !== jj) { + +// test_cause: +// ["/*jslint !*/", "lex_comment", "bad_directive_a", "!", 1] + + return stop("bad_directive_a", the_comment, body.slice(ii)); + } + if (match0 === "") { + return ""; + } + ii += match0.length; + switch (the_comment.directive) { + case "global": + if (val) { + +// test_cause: +// ["/*global aa:false*/", "lex_comment", "bad_option_a", "aa:false", 1] + + warn("bad_option_a", the_comment, key + ":" + val); + } + global_dict[key] = "user-defined"; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// state.mode_module = the_comment; + + break; + case "jslint": + if (!option_set_item(key, val !== "false")) { + +// test_cause: +// ["/*jslint undefined*/", "lex_comment", "bad_option_a", "undefined", 1] + + warn("bad_option_a", the_comment, key); + } + break; + case "property": + state.mode_property = true; + tenure[key] = true; + break; + } + return ""; + }); + return the_comment; + } + + function lex_megastring() { + let id; + let match; + +// The token is a megastring. We don't allow any kind of mega nesting. + + if (mode_mega) { + +// test_cause: +// ["`${`", "lex_megastring", "expected_a_b", "`", 4] + + return stop_at("expected_a_b", line, column, "}", "`"); + } + from_mega = from; + line_mega = line; + mode_mega = true; + snippet = ""; + +// Parsing a mega literal is tricky. First create a ` token. + + token_create("`"); + from += 1; + +// Then loop, building up a string, possibly from many lines, until seeing +// the end of file, a closing `, or a ${ indicting an expression within the +// string. + + while (true) { + match = line_source.match(jslint_rgx_mega) || { + "0": "", + index: 0 + }; + snippet += line_source.slice(0, match.index); + column += match.index; + line_source = line_source.slice(match.index); + match = match[0]; + switch (match) { + case "${": + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + token_create("(string)", snippet).quote = "`"; + snippet = ""; + +// If ${, then create tokens that will become part of an expression until +// a } token is made. + + column += 2; + token_create("${"); + line_source = line_source.slice(2); + +// Lex/loop through each token inside megastring-expression `${...}`. + + while (true) { + id = lex_token().id; + if (id === "{") { + +// test_cause: +// ["`${{", "lex_megastring", "expected_a_b", "{", 4] + + return stop_at("expected_a_b", line, column, "}", "{"); + } + if (id === "}") { + break; + } + } + break; + case "\\": + snippet += line_source.slice(0, 2); + line_source = line_source.slice(2); + column += 2; + break; + case "`": + +// if either ` or ${ was found, then the preceding joins the snippet to become +// a string token. + + token_create("(string)", snippet).quote = "`"; + snippet = ""; + +// Terminate megastring with `. + + line_source = line_source.slice(1); + column += 1; + mode_mega = false; + return token_create("`"); + default: + +// If neither ` nor ${ is seen, then the whole line joins the snippet. + + snippet += line_source + "\n"; + if (read_line() === undefined) { + +// test_cause: +// ["`", "lex_megastring", "unclosed_mega", "", 1] + + return stop_at("unclosed_mega", line_mega, from_mega); + } + } + } + } + + function lex_number() { + let prefix = snippet; + +// PR-390 - Add numeric-separator check. + + check_numeric_separator(prefix, column - prefix.length); + char_after(); + switch (prefix === "0" && char) { + case "b": + case "o": + case "x": + read_digits(char, mode_digits_numeric_separator); + +// PR-351 - Ignore BigInt suffix 'n'. + + if (char === "n") { + char_after("n"); + } + break; + default: + if (char === ".") { + read_digits("d", mode_digits_numeric_separator); + } + if (char === "E" || char === "e") { + char_after(char); + if (char !== "+" && char !== "-") { + char_before(); + } + read_digits("d", mode_digits_numeric_separator); + } + } + +// If the next character after a number is a digit or letter, then something +// unexpected is going on. + + if ( + (char >= "0" && char <= "9") + || (char >= "a" && char <= "z") + || (char >= "A" && char <= "Z") + ) { + +// test_cause: +// ["0a", "lex_number", "unexpected_a_after_b", "0", 2] + + return stop_at( + "unexpected_a_after_b", + line, + column, + snippet.slice(-1), + snippet.slice(0, -1) + ); + } + char_before(); + return token_create("(number)", snippet); + } + + function lex_regexp() { + +// Regexp +// Lex a regular expression literal. + + let flag; + let mode_regexp_multiline; + let result; + let value; + mode_regexp = true; + + function lex_regexp_bracketed() { + let mode_regexp_range; + +// RegExp +// Match a class. + + char_after("["); + if (char === "^") { + char_after("^"); + } + while (true) { + +// RegExp +// Match a character in a character class. + + switch (char) { + case "": + case "]": + +// test_cause: +// ["aa=/[", "lex_regexp_bracketed", "closer", "", 0] +// ["aa=/[]/", "lex_regexp_bracketed", "closer", "", 0] + + test_cause("closer"); + if (mode_regexp_range) { + +// test_cause: +// ["aa=/[0-]/", "lex_regexp_bracketed", "unexpected_a", "-", 7] + + warn_at("unexpected_a", line, column - 1, "-"); + } + return char_after("]"); + +// PR-362 - Relax regexp-warning against using . +// +// case " ": +// +// // test_cause: +// // ["aa=/[ ]/", "lex_regexp_bracketed", "expected_a_b", " ", 6] +// +// warn_at("expected_a_b", line, column, "\\u0020", " "); +// break; + + case "-": + case "/": + case "[": + case "^": + +// test_cause: +// ["aa=/[-]/", "lex_regexp_bracketed", "expected_a_before_b", "-", 6] +// ["aa=/[.^]/", "lex_regexp_bracketed", "expected_a_before_b", "^", 7] +// ["aa=/[/", "lex_regexp_bracketed", "expected_a_before_b", "/", 6] +// ["aa=/[\\\\/]/", "lex_regexp_bracketed", "expected_a_before_b", "/", 8] +// ["aa=/[\\\\[]/", "lex_regexp_bracketed", "expected_a_before_b", "[", 8] + + warn_at("expected_a_before_b", line, column, "\\", char); + break; + case "\\": + char_after_escape("BbDdSsWw-[]^"); + char_before(); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${/[`]/}`", "lex_regexp_bracketed", "unexpected_a", "`", 6] + + warn_at("unexpected_a", line, column, "`"); + } + break; + } + char_after(); + mode_regexp_range = false; + if (char === "-") { + +// RegExp +// Match a range of subclasses. + + mode_regexp_range = true; + char_after("-"); + } + } + } + + function lex_regexp_group() { + +// RegExp +// Lex sequence of characters in regexp. + + switch (char) { + case "": + warn_at("expected_regexp_factor_a", line, column, char); + break; + case ")": + warn_at("expected_regexp_factor_a", line, column, char); + break; + case "]": + +// test_cause: +// ["/ /", "lex_regexp_group", "expected_regexp_factor_a", "", 3] +// ["aa=/)", "lex_regexp_group", "expected_regexp_factor_a", ")", 5] +// ["aa=/]", "lex_regexp_group", "expected_regexp_factor_a", "]", 5] + + warn_at("expected_regexp_factor_a", line, column, char); + break; + } + while (true) { + switch (char) { + case "": + case ")": + case "/": + case "]": + return; + +// PR-362 - Relax regexp-warning against using . +// +// case " ": +// +// // test_cause: +// // ["aa=/ /", "lex_regexp_group", "expected_a_b", " ", 5] +// +// warn_at("expected_a_b", line, column, "\\s", " "); +// char_after(); +// break; + + case "$": + if (line_source[0] !== "/") { + mode_regexp_multiline = true; + } + char_after(); + break; + case "(": + +// RegExp +// Match a group that starts with left paren. + + char_after("("); + switch (char) { + case ":": + +// test_cause: +// ["aa=/(:)/", "lex_regexp_group", "expected_a_before_b", ":", 6] +// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5] + + warn_at("expected_a_before_b", line, column, "?", ":"); + break; + case "?": + char_after("?"); + switch (char) { + case "!": + +// PR-437 - Add grammar for regexp-named-capture-group. + + case "<": + case "=": + char_after(); + break; + default: + char_after(":"); + } + break; + } + +// RegExp +// Recurse lex_regexp_group(). + + lex_regexp_group(); + char_after(")"); + break; + case "*": + case "+": + case "?": + case "{": + case "}": + +// test_cause: +// ["aa=/+/", "lex_regexp_group", "expected_a_before_b", "+", 5] +// ["aa=/.**/", "lex_regexp_group", "expected_a_before_b", "*", 7] +// ["aa=/?/", "lex_regexp_group", "expected_a_before_b", "?", 5] +// ["aa=/{/", "lex_regexp_group", "expected_a_before_b", "{", 5] +// ["aa=/}/", "lex_regexp_group", "expected_a_before_b", "}", 5] + + warn_at("expected_a_before_b", line, column, "\\", char); + char_after(); + break; + case "[": + lex_regexp_bracketed(); + break; + case "\\": + +// test_cause: +// ["aa=/\\/", "lex_regexp_group", "escape", "", 0] + + test_cause("escape"); + +// PR-437 - Add grammar for regexp-named-backreference. + + char_after_escape("BbDdSsWw^${}[]():=!.|*+?k"); + break; + case "^": + if (snippet !== "^") { + mode_regexp_multiline = true; + } + char_after(); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${/`/}`", "lex_regexp_group", "unexpected_a", "`", 5] + + warn_at("unexpected_a", line, column, "`"); + } + char_after(); + break; + default: + char_after(); + } + +// RegExp +// Match an optional quantifier. + + switch (char) { + case "*": + case "+": + if (char_after(char) === "?") { + +// test_cause: +// ["aa=/.*?/", "lex_regexp_group", "?", "", 0] +// ["aa=/.+?/", "lex_regexp_group", "?", "", 0] + + test_cause("?"); + char_after("?"); + } + break; + case "?": + if (char_after("?") === "?") { + +// test_cause: +// ["aa=/.??/", "lex_regexp_group", "unexpected_a", "?", 7] + + warn_at("unexpected_a", line, column, char); + char_after("?"); + } + break; + case "{": + if (read_digits("d", mode_digits_empty_string) === 0) { + +// test_cause: +// ["aa=/aa{/", "lex_regexp_group", "expected_a_before_b", ",", 8] + + warn_at("expected_a_before_b", line, column, "0", ","); + } + if (char === ",") { + +// test_cause: +// ["aa=/.{,/", "lex_regexp_group", "comma", "", 0] + + test_cause("comma"); + read_digits("d", mode_digits_empty_string); + } + if (char_after("}") === "?") { + +// test_cause: +// ["aa=/.{0}?/", "lex_regexp_group", "unexpected_a", "?", 9] + + warn_at("unexpected_a", line, column, char); + char_after("?"); + } + break; + } + } + } + +// RegExp +// Scan the regexp literal. Give a warning if the first character is = because +// /= looks like a division assignment operator. + + snippet = ""; + char_after(); + if (char === "=") { + +// test_cause: +// ["aa=/=/", "lex_regexp", "expected_a_before_b", "=", 5] + + warn_at("expected_a_before_b", line, column, "\\", "="); + } + lex_regexp_group(); + +// RegExp +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + +// RegExp +// Make sure there is a closing slash. + + value = snippet; + char_after("/"); + +// RegExp +// Create flag. + + flag = empty(); + while ( + +// Regexp +// char is a letter. + + (char >= "a" && char <= "z\uffff") + || (char >= "A" && char <= "Z\uffff") + ) { + +// RegExp +// Process dangling flag letters. + + switch (!flag[char] && char) { + case "g": + break; + case "i": + break; + case "m": + break; + case "u": + break; + case "y": + +// test_cause: +// ["aa=/./gimuy", "lex_regexp", "flag", "", 0] + + test_cause("flag"); + break; + default: + +// test_cause: +// ["aa=/./gg", "lex_regexp", "unexpected_a", "g", 8] +// ["aa=/./z", "lex_regexp", "unexpected_a", "z", 7] + + warn_at("unexpected_a", line, column, char); + } + flag[char] = true; + char_after(); + } + char_before(); + if (char === "/" || char === "*") { + +// test_cause: +// ["aa=/.//", "lex_regexp", "unexpected_a", "/", 3] + + return stop_at("unexpected_a", line, from, char); + } + result = token_create("(regexp)", char); + result.flag = flag; + result.value = value; + if (mode_regexp_multiline && !flag.m) { + +// test_cause: +// ["aa=/$^/", "lex_regexp", "missing_m", "", 7] + + warn_at("missing_m", line, column); + } + return result; + } + + function lex_slash_or_regexp() { + +// The / can be a division operator or the beginning of a regular expression +// literal. It is not possible to know which without doing a complete parse. +// We want to complete the tokenization before we begin to parse, so we will +// estimate. This estimator can fail in some cases. For example, it cannot +// know if "}" is ending a block or ending an object literal, so it can +// behave incorrectly in that case; it is not meaningful to divide an +// object, so it is likely that we can get away with it. We avoided the worst +// cases by eliminating automatic semicolon insertion. + + let the_token; + switch ( + token_prv_expr.identifier + && !token_prv_expr.dot + && token_prv_expr.id + ) { + case "case": + case "delete": + case "in": + case "instanceof": + case "new": + case "typeof": + case "void": + case "yield": + the_token = lex_regexp(); + +// test_cause: +// ["case /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6] +// ["delete /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8] +// ["in /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 4] +// ["instanceof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 12] +// ["new /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 5] +// ["typeof /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 8] +// ["void /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 6] +// ["yield /./", "lex_slash_or_regexp", "unexpected_a", "(regexp)", 7] + + return stop("unexpected_a", the_token); + case "return": + return lex_regexp(); + } + switch (!token_prv_expr.identifier && token_prv_expr.id.slice(-1)) { + case "!": + case "%": + case "&": + case "*": + case "+": + case "-": + case "/": + case ";": + case "<": + case ">": + case "^": + case "{": + case "|": + case "}": + case "~": + the_token = lex_regexp(); + +// test_cause: +// ["!/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["%/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["&/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["+/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["-/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["0 * /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5] +// ["0 / /./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 5] +// [";/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["^/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["{/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["|/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["}/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] +// ["~/./", "lex_slash_or_regexp", "wrap_regexp", "(regexp)", 2] + + warn("wrap_regexp", the_token); + return the_token; + case "(": + case ",": + case ":": + case "=": + case "?": + case "[": + +// test_cause: +// ["(/./", "lex_slash_or_regexp", "recurse", "", 0] +// [",/./", "lex_slash_or_regexp", "recurse", "", 0] +// [":/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["=/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["?/./", "lex_slash_or_regexp", "recurse", "", 0] +// ["aa[/./", "lex_slash_or_regexp", "recurse", "", 0] + + test_cause("recurse"); + return lex_regexp(); + } + if (line_source[0] === "=") { + column += 1; + line_source = line_source.slice(1); + snippet = "/="; + warn_at("unexpected_a", line, column, "/="); + } + return token_create(snippet); + } + + function lex_string(quote) { + +// Create a string token. + + let the_token; + if (!option_dict.single && quote === "'") { + +// test_cause: +// ["''", "lex_string", "use_double", "", 1] + + warn_at("use_double", line, column); + } + snippet = ""; + char_after(); + +// Lex/loop through each character in "...". + + while (true) { + switch (char) { + case "": + +// test_cause: +// ["\"", "lex_string", "unclosed_string", "", 1] + + return stop_at("unclosed_string", line, column); + case "\\": + char_after_escape(quote); + break; + case "`": + if (mode_mega) { + +// test_cause: +// ["`${\"`\"}`", "lex_string", "unexpected_a", "`", 5] + + warn_at("unexpected_a", line, column, "`"); + } + char_after("`"); + break; + case quote: + +// Remove last character from snippet. + + snippet = snippet.slice(0, -1); + the_token = token_create("(string)", snippet); + the_token.quote = quote; + return the_token; + default: + char_after(); + } + } + } + + function lex_token() { + let match; + +// Lex/loop through each whitespace. + + while (true) { + +// Lex/loop through each blank-line. + + while (!line_source) { + line_source = read_line(); + from = 0; + if (line_source === undefined) { + return ( + mode_mega + +// test_cause: +// ["`${//}`", "lex_token", "unclosed_mega", "", 1] + + ? stop_at("unclosed_mega", line_mega, from_mega) + : line_disable !== undefined + +// test_cause: +// ["/*jslint-disable*/", "lex_token", "unclosed_disable", "", 1] + + ? stop_at("unclosed_disable", line_disable) + : token_create("(end)") + ); + } + } + from = column; + match = line_source.match(jslint_rgx_token); + +// match[1] token +// match[2] whitespace +// match[3] identifier +// match[4] number +// match[5] rest + + if (!match) { + +// test_cause: +// ["#", "lex_token", "unexpected_char_a", "#", 1] + + return stop_at( + "unexpected_char_a", + line, + column, + line_source[0] + ); + } + snippet = match[1]; + column += snippet.length; + line_source = match[5]; + if (!match[2]) { + break; + } + } + +// The token is an identifier. + + if (match[3]) { + return token_create(snippet, undefined, true); + } + +// Create token from number. + + if (match[4]) { + return lex_number(); + } + +// Create token from string "..." or '...'. + + if (snippet === "\"" || snippet === "'") { + return lex_string(snippet); + } + +// Create token from megastring `...`. + + if (snippet === "`") { + return lex_megastring(); + } + +// Create token from comment /*...*/ or //.... + + if (snippet === "/*" || snippet === "//") { + return lex_comment(); + } + +// Create token from slash /. + + if (snippet === "/") { + return lex_slash_or_regexp(); + } + return token_create(snippet); + } + + function option_set_item(key, val) { + +// These are the options that are recognized in the option object or that may +// appear in a /*jslint*/ directive. Most options will have a boolean value, +// usually true. Some options will also predefine some number of global +// variables. + + switch (key) { + case "beta": // Enable experimental warnings. + case "bitwise": // Allow bitwise operator. + case "browser": // Assume browser environment. + case "convert": // Allow conversion operator. + case "couch": // Assume CouchDb environment. + case "devel": // Allow console.log() and friends. + case "ecma": // Assume ECMAScript environment. + case "eval": // Allow eval(). + case "fart": // Allow complex fat-arrow. + case "for": // Allow for-statement. + case "getset": // Allow get() and set(). + case "indent2": // Use 2-space indent. + case "long": // Allow long lines. + case "node": // Assume Node.js environment. + case "nomen": // Allow weird property name. + case "single": // Allow single-quote strings. + case "subscript": // Allow identifier in subscript-notation. + case "test_cause": // Test jslint's causes. + case "test_internal_error": // Test jslint's internal-error + // ... handling-ability. + case "this": // Allow 'this'. + case "trace": // Include jslint stack-trace in warnings. + case "unordered": // Allow unordered cases, params, properties, + // ... variables, and exports. + case "variable": // Allow unordered const and let declarations + // ... that are not at top of function-scope. + case "white": // Allow messy whitespace. + option_dict[key] = val; + break; + +// PR-404 - Alias "evil" to jslint-directive "eval" for backwards-compat. + + case "evil": + return option_set_item("eval", val); + +// PR-404 - Alias "nomen" to jslint-directive "name" for backwards-compat. + + case "name": + return option_set_item("nomen", val); + default: + return false; + } + +// Initialize global-variables. + + switch (val && key) { + +// Assign global browser variables to global_dict. +/* +// /\*jslint beta, browser, devel*\/ +console.log(JSON.stringify(Object.keys(window).sort(), undefined, 4)); +*/ + + case "browser": + object_assign_from_list(global_dict, [ + +// Shared with Node.js. + + "AbortController", + // "Buffer", + // "Crypto", + // "CryptoKey", + "Event", + "EventTarget", + "MessageChannel", + "MessageEvent", + "MessagePort", + // "Request", + // "Response", + // "SubtleCrypto", + "TextDecoder", + "TextEncoder", + "URL", + "URLSearchParams", + "WebAssembly", + // "__dirname", + // "__filename", + // "atob", + // "btoa", + // "clearImmediate", + "clearInterval", + "clearTimeout", + // "console", + // "crypto", + // "exports", + // "fetch", + // "global", + // "module", + "performance", + // "process", + "queueMicrotask", + // "require", + // "setImmediate", + "setInterval", + "setTimeout", + +// Web worker only. +// https://github.com/mdn/content/blob/main/files/en-us/web/api +// /workerglobalscope/index.md + + "importScripts", + +// Window. + + "Blob", + // "CharacterData", + // "DocumentType", + // "Element", + // "Event", + "FileReader", + // "FontFace", + "FormData", + "IntersectionObserver", + "MutationObserver", + // "Storage", + // "TextDecoder", + // "TextEncoder", + // "URL", + "Worker", + "XMLHttpRequest", + // "caches", + // "clearInterval", + // "clearTimeout", + "document", + // "event", + "fetch", + // "history", + "indexedDb", + "localStorage", + "location", + // "name", + "navigator", + "postMessage", + // "screen", + "sessionStorage", + // "setInterval", + // "setTimeout", + "structuredClone", + "window" + ], "browser"); + break; + +// https://docs.couchdb.org/en/stable/query-server/javascript.html#javascript + + case "couch": + object_assign_from_list(global_dict, [ + "emit", + "getRow", + "isArray", + "log", + "provides", + "registerType", + "require", + "send", + "start", + "sum", + "toJSON" + ], "CouchDb"); + break; + case "devel": + object_assign_from_list(global_dict, [ + "alert", "confirm", "console", "prompt" + ], "development"); + break; + +// These are the globals that are provided by the language standard. +// Assign global ECMAScript variables to global_dict. +/* +node --input-type=module --eval ' +// /\*jslint beta, node*\/ +import https from "https"; +(async function () { + let dict = {import: true}; + let result = ""; + await new Promise(function (resolve) { + https.get(( + "https://raw.githubusercontent.com/mdn/content/main/files" + + "/en-us/web/javascript/reference/global_objects/index.md" + ), function (res) { + res.on("data", function (chunk) { + result += chunk; + }).on("end", resolve).setEncoding("utf8"); + }); + }); + result.replace(( + /\n- \{\{JSxRef\("(?:Global_Objects\/)?([^"\/]+?)"/g + ), function (ignore, key) { + if (globalThis.hasOwnProperty(key)) { + dict[key] = true; + } + return ""; + }); + console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4)); +}()); +' +*/ + + case "ecma": + object_assign_from_list(global_dict, [ + "AggregateError", + "Array", + "ArrayBuffer", + "Atomics", + "BigInt", + "BigInt64Array", + "BigUint64Array", + "Boolean", + "DataView", + "Date", + "Error", + "EvalError", + "Float32Array", + "Float64Array", + "Function", + "Infinity", + "Int16Array", + "Int32Array", + "Int8Array", + "Intl", + "JSON", + "Map", + "Math", + "NaN", + "Number", + "Object", + "Promise", + "Proxy", + "RangeError", + "ReferenceError", + "Reflect", + "RegExp", + "Set", + "SharedArrayBuffer", + "String", + "Symbol", + "SyntaxError", + "TypeError", + "URIError", + "Uint16Array", + "Uint32Array", + "Uint8Array", + "Uint8ClampedArray", + "WeakMap", + "WeakSet", + "WebAssembly", + "decodeURI", + "decodeURIComponent", + "encodeURI", + "encodeURIComponent", + "eval", + "globalThis", + "import", + "isFinite", + "isNaN", + "parseFloat", + "parseInt", + "undefined" + ], "ECMAScript"); + break; + +// Assign global Node.js variables to global_dict. +/* +node --input-type=module --eval ' +// /\*jslint beta, node*\/ +import moduleHttps from "https"; +(async function () { + let dict = Object.create(null); + let result = ""; + await new Promise(function (resolve) { + moduleHttps.get(( + "https://raw.githubusercontent.com/nodejs/node/v16.x/doc/api" + + "/globals.md" + ), function (res) { + res.on("data", function (chunk) { + result += chunk; + }).on("end", resolve).setEncoding("utf8"); + }); + }); + result.replace(( + /\n(?:\* \[`|## |## Class: )`\w+/g + ), function (match0) { + dict[match0.split("`")[1]] = true; + return ""; + }); + console.log(JSON.stringify(Object.keys(dict).sort(), undefined, 4)); +}()); +' +*/ + + case "node": + object_assign_from_list(global_dict, [ + "AbortController", + "Buffer", + // "Crypto", + // "CryptoKey", + "Event", + "EventTarget", + "MessageChannel", + "MessageEvent", + "MessagePort", + // "Request", + // "Response", + // "SubtleCrypto", + "TextDecoder", + "TextEncoder", + "URL", + "URLSearchParams", + "WebAssembly", + "__dirname", + "__filename", + // "atob", + // "btoa", + "clearImmediate", + "clearInterval", + "clearTimeout", + "console", + // "crypto", + "exports", + // "fetch", + "global", + "module", + "performance", + "process", + "queueMicrotask", + "require", + "setImmediate", + "setInterval", + "setTimeout" + ], "Node.js"); + break; + } + return true; + } + + function read_digits(base, mode) { + let digits = line_source.match( + base === "b" + ? jslint_rgx_digits_bits + : base === "o" + ? jslint_rgx_digits_octals + : base === "x" + ? jslint_rgx_digits_hexs + : jslint_rgx_digits_decimals + )[0]; + if ( + (mode !== mode_digits_empty_string && digits.length === 0) + || digits[0] === "_" + ) { + +// test_cause: +// ["0x", "read_digits", "expected_digits_after_a", "0x", 2] +// ["0x_", "read_digits", "expected_digits_after_a", "0x", 2] + + warn_at("expected_digits_after_a", line, column, snippet); + } + +// PR-390 - Add numeric-separator check. + + if (mode === mode_digits_numeric_separator) { + check_numeric_separator(digits, column); + } else if (digits.indexOf("_") >= 0) { + +// test_cause: +// ["\"\\u{1_2}\"", "read_digits", "illegal_num_separator", "", 6] + + warn_at( + "illegal_num_separator", + line, + column + digits.indexOf("_") + 1 + ); + } + column += digits.length; + line_source = line_source.slice(digits.length); + snippet += digits; + char_after(); + return digits.length; + } + + function read_line() { + +// Put the next line of source in line_source. If the line contains tabs, +// replace them with spaces and give a warning. Also warn if the line contains +// unsafe characters or is too damn long. + + if ( + !option_dict.long + && line_whole.length > 80 + && line_disable === undefined + && !state.mode_json + && token_1 + && !mode_regexp + ) { + +// test_cause: +// ["/////////////////////////////////////////////////////////////////////////////////", "read_line", "too_long", "", 1] //jslint-ignore-line + + warn_at("too_long", line); + } + column = 0; + line += 1; + mode_regexp = false; + line_source = undefined; + line_whole = ""; + if (line_list[line] === undefined) { + return line_source; + } + line_source = line_list[line].line_source; + line_whole = line_source; + +// Scan each line for following ignore-directives: +// "/*jslint-disable*/" +// "/*jslint-enable*/" +// "//jslint-ignore-line" + + if (line_source === "/*jslint-disable*/") { + +// test_cause: +// ["/*jslint-disable*/", "read_line", "jslint_disable", "", 0] + + test_cause("jslint_disable"); + line_disable = line; + } else if (line_source === "/*jslint-enable*/") { + if (line_disable === undefined) { + +// test_cause: +// ["/*jslint-enable*/", "read_line", "unopened_enable", "", 1] + + stop_at("unopened_enable", line); + } + line_disable = undefined; + } else if ( + line_source.endsWith(" //jslint-ignore-line") + || line_source.endsWith(" //jslint-quiet") + ) { + +// test_cause: +// ["0 //jslint-ignore-line", "read_line", "jslint_ignore_line", "", 0] + + test_cause("jslint_ignore_line"); + line_list[line].directive_ignore_line = true; + } + if (line_disable !== undefined) { + +// test_cause: +// ["/*jslint-disable*/\n0", "read_line", "line_disable", "", 0] + + test_cause("line_disable"); + line_source = ""; + } + // jslint_rgx_tab + if (line_source.indexOf("\t") >= 0) { + if (!option_dict.white) { + +// test_cause: +// ["\t", "read_line", "use_spaces", "", 1] + + warn_at("use_spaces", line, line_source.indexOf("\t") + 1); + } + line_source = line_source.replace(jslint_rgx_tab, " "); + } + if (!option_dict.white && line_source.endsWith(" ")) { + +// test_cause: +// [" ", "read_line", "unexpected_trailing_space", "", 1] + + warn_at("unexpected_trailing_space", line, line_source.length - 1); + } + return line_source; + } + + function token_create(id, value, identifier) { + +// Create the token object and append it to token_list. + + let the_token = { + from, + id, + identifier: Boolean(identifier), + line, + nr: token_list.length, + thru: column, + value + }; + token_list.push(the_token); + +// Directives must appear before the first statement. + + if (id !== "(comment)" && id !== ";") { + mode_directive = false; + } + +// If this token is an identifier that touches a preceding number, or +// a "/", comment, or regular expression literal that touches a preceding +// comment or regular expression literal, then give a missing space warning. +// This warning is not suppressed by option_dict.white. + + if ( + token_prv.line === line + && token_prv.thru === from + && (id === "(comment)" || id === "(regexp)" || id === "/") + && (token_prv.id === "(comment)" || token_prv.id === "(regexp)") + ) { + +// test_cause: +// ["/**//**/", "token_create", "expected_space_a_b", "(comment)", 5] + + warn( + "expected_space_a_b", + the_token, + artifact(token_prv), + artifact(the_token) + ); + } + if (token_prv.id === "." && id === "(number)") { + +// test_cause: +// [".0", "token_create", "expected_a_before_b", ".", 1] + + warn("expected_a_before_b", token_prv, "0", "."); + } + if (token_prv_expr.id === "." && the_token.identifier) { + the_token.dot = true; + } + +// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart. +// Farts are now detected by keeping a list of most recent "(" tokens at any +// given depth. When a "=>" token is encountered, the most recent "(" token at +// current depth is marked as a fart. + + switch (id) { + case "(": + paren_backtrack_list[paren_depth] = the_token; + paren_depth += 1; + break; + case ")": + paren_depth -= 1; + break; + case "=>": + if ( + token_prv_expr.id === ")" + && paren_backtrack_list[paren_depth] + ) { + paren_backtrack_list[paren_depth].fart = the_token; + } + break; + } + +// The previous token is used to detect adjacency problems. + + token_prv = the_token; + +// The token_prv_expr token is a previous token that was not a comment. +// The token_prv_expr token +// is used to disambiguate "/", which can mean division or regular expression +// literal. + + if (token_prv.id !== "(comment)") { + token_prv_expr = token_prv; + } + return the_token; + } + +// Init global_dict and option_dict. + + option_set_item("ecma", true); + Object.keys(option_dict).sort().forEach(function (key) { + option_set_item(key, option_dict[key] === true); + }); + object_assign_from_list(global_dict, global_list, "user-defined"); + +// Scan first line for "#!" and ignore it. + + if (line_list[jslint_fudge].line_source.startsWith("#!")) { + line += 1; + state.mode_shebang = true; + } + token_1 = lex_token(); + state.mode_json = token_1.id === "{" || token_1.id === "["; + +// Lex/loop through each token until (end). + + while (true) { + if (lex_token().id === "(end)") { + break; + } + } +} + +function jslint_phase3_parse(state) { + +// PHASE 3. Parse into using the Pratt-parser. + +// Parsing: + +// Parsing weaves the tokens into an abstract syntax tree. During that process, +// a token may be given any of these properties: + +// arity string +// label identifier +// name identifier +// expression expressions +// block statements +// else statements (else, default, catch) + +// Specialized tokens may have additional properties. + + let anon = "anonymous"; // The guessed name for anonymous functions. + let { + artifact, + catch_list, + catch_stack, + export_dict, + function_list, + function_stack, + global_dict, + import_list, + is_equal, + option_dict, + property_dict, + stop, + syntax_dict, + tenure, + test_cause, + token_global, + token_list, + warn, + warn_at + } = state; + let catchage = catch_stack[0]; // The current catch-block. + let functionage = token_global; // The current function. + let mode_var; // "var" if using var; "let" if using let. + let token_ii = 0; // The number of the next token. + let token_now = token_global; // The current token being examined in + // ... the parse. + let token_nxt = token_global; // The next token to be examined in + // ... . + + function advance(id, match) { + +// Produce the next token. + +// Attempt to give helpful names to anonymous functions. + + if ( + token_now.identifier + && token_now.id !== "function" + && token_now.id !== "async" + ) { + anon = token_now.id; + } else if ( + token_now.id === "(string)" + && jslint_rgx_identifier.test(token_now.value) + ) { + anon = token_now.value; + } + +// Attempt to match token_nxt with an expected id. + + if (id !== undefined && token_nxt.id !== id) { + return ( + match === undefined + +// test_cause: +// ["{0:0}", "advance", "expected_a_b", "0", 2] + + ? stop("expected_a_b", token_nxt, id, artifact()) + +// test_cause: +// ["{\"aa\":0", "advance", "expected_a_b_from_c_d", "{", 1] + + : stop( + "expected_a_b_from_c_d", + token_nxt, + id, + artifact(match), + match.line, + artifact() + ) + ); + } + +// Promote the tokens, skipping comments. + + token_now = token_nxt; + while (true) { + token_nxt = token_list[token_ii]; + state.token_nxt = token_nxt; + token_ii += 1; + if (token_nxt.id !== "(comment)") { + if (token_nxt.id === "(end)") { + token_ii -= 1; + } + break; + } + if (state.mode_json) { + +// test_cause: +// ["[//]", "advance", "unexpected_a", "(comment)", 2] + + warn("unexpected_a"); + } + } + } + + function assignment(id) { + +// Create an assignment operator. The one true assignment is different because +// its left side, when it is a variable, is not treated as an expression. +// That case is special because that is when a variable gets initialized. The +// other assignment operators can modify, but they cannot initialize. + + const the_symbol = symbol(id, 20); + the_symbol.led_infix = function (left) { + const the_token = token_now; + let right; + the_token.arity = "assignment"; + right = parse_expression(20 - 1); + if (id === "=" && left.arity === "variable") { + the_token.names = left; + the_token.expression = right; + } else { + the_token.expression = [left, right]; + } + if ( + right.arity === "assignment" + || right.arity === "preassign" + || right.arity === "postassign" + ) { + warn("unexpected_a", right); + } + check_mutation(left); + return the_token; + }; + return the_symbol; + } + + function block(special) { + +// Parse a block, a sequence of statements wrapped in braces. +// special "body" The block is a function body. +// "ignore" No warning on an empty block. +// "naked" No advance. +// undefined An ordinary block. + + let stmts; + let the_block; + if (special !== "naked") { + advance("{"); + } + the_block = token_now; + if (special !== "body") { + functionage.statement_prv = the_block; + } + the_block.arity = "statement"; + the_block.body = special === "body"; + +// Top level function bodies may include the "use strict" pragma. + + if ( + special === "body" + && function_stack.length === 1 + && token_nxt.value === "use strict" + ) { + token_nxt.statement = true; + advance("(string)"); + advance(";"); + } + stmts = parse_statements(); + the_block.block = stmts; + if (stmts.length === 0) { + if (!option_dict.devel && special !== "ignore") { + +// test_cause: +// ["function aa(){}", "block", "empty_block", "{", 14] + + warn("empty_block", the_block); + } + the_block.disrupt = false; + } else { + the_block.disrupt = stmts[stmts.length - 1].disrupt; + } + advance("}"); + return the_block; + } + + function check_left(left, right) { + +// Warn if the left is not one of these: +// ?. +// ?: +// e() +// e.b +// e[b] +// identifier + + const id = left.id; + if ( + !left.identifier + && ( + left.arity !== "ternary" + || ( + !check_left(left.expression[1]) + && !check_left(left.expression[2]) + ) + ) + && ( + left.arity !== "binary" + || (id !== "." && id !== "?." && id !== "(" && id !== "[") + ) + ) { + warn("unexpected_a", right || token_nxt); + return false; + } + return true; + } + + function check_mutation(the_thing) { + +// The only expressions that may be assigned to are +// e.b +// e[b] +// v +// [destructure] +// {destructure} + + if ( + the_thing.arity !== "variable" + && the_thing.id !== "." + && the_thing.id !== "[" + && the_thing.id !== "{" + ) { + +// test_cause: +// ["0=0", "check_mutation", "bad_assignment_a", "0", 1] + + warn("bad_assignment_a", the_thing); + return false; + } + return true; + } + + function check_not_top_level(thing) { + +// Some features should not be at the outermost level. + + if (functionage === token_global) { + +// test_cause: +// [" +// while(0){} +// ", "check_not_top_level", "unexpected_at_top_level_a", "while", 1] + + warn("unexpected_at_top_level_a", thing); + } + } + + function check_ordered(type, token_list) { + +// This function will warn if is unordered. + + token_list.reduce(function (aa, token) { + const bb = artifact(token); + if (!option_dict.unordered && aa > bb) { + warn("expected_a_b_before_c_d", token, type, bb, type, aa); + } + return bb; + }, ""); + } + + function check_ordered_case(case_list) { + +// This function will warn if is unordered. + + case_list.filter(noop).map(function (token) { + switch (token.identifier || token.id) { + case "(number)": + return { + order: 1, + token, + type: "number", + value: Number(artifact(token)) + }; + case "(string)": + return { + order: 2, + token, + type: "string", + value: artifact(token) + }; + case true: + return { + order: 3, + token, + type: "identifier", + value: artifact(token) + }; + } + }).reduce(function (aa, bb) { + if ( + +// PR-419 - Hide warning about unordered case-statements behind beta-flag. + + option_dict.beta + && !option_dict.unordered + && aa && bb + && ( + aa.order > bb.order + || (aa.order === bb.order && aa.value > bb.value) + ) + ) { + warn( + "expected_a_b_before_c_d", + bb.token, + `case-${bb.type}`, + bb.value, + `case-${aa.type}`, + aa.value + ); + } + return bb; + }, undefined); + } + + function condition() { + +// Parse the condition part of a do, if, while. + + const the_paren = token_nxt; + let the_value; + +// test_cause: +// ["do{}while()", "condition", "", "", 0] +// ["if(){}", "condition", "", "", 0] +// ["while(){}", "condition", "", "", 0] + + test_cause(""); + the_paren.free = true; + advance("("); + the_value = parse_expression(0); + advance(")"); + if (the_value.wrapped === true) { + +// test_cause: +// ["while((0)){}", "condition", "unexpected_a", "(", 6] + + warn("unexpected_a", the_paren); + } + +// Check for anticondition. + + switch (the_value.id) { + case "%": + warn("unexpected_a", the_value); + break; + case "&": + warn("unexpected_a", the_value); + break; + case "(number)": + warn("unexpected_a", the_value); + break; + case "(string)": + warn("unexpected_a", the_value); + break; + case "*": + warn("unexpected_a", the_value); + break; + case "+": + warn("unexpected_a", the_value); + break; + case "-": + warn("unexpected_a", the_value); + break; + case "/": + warn("unexpected_a", the_value); + break; + case "<<": + warn("unexpected_a", the_value); + break; + case ">>": + warn("unexpected_a", the_value); + break; + case ">>>": + warn("unexpected_a", the_value); + break; + case "?": + warn("unexpected_a", the_value); + break; + case "^": + warn("unexpected_a", the_value); + break; + case "typeof": + warn("unexpected_a", the_value); + break; + case "|": + warn("unexpected_a", the_value); + break; + case "~": + +// test_cause: +// ["if(0%0){}", "condition", "unexpected_a", "%", 5] +// ["if(0&0){}", "condition", "unexpected_a", "&", 5] +// ["if(0){}", "condition", "unexpected_a", "0", 4] +// ["if(0*0){}", "condition", "unexpected_a", "*", 5] +// ["if(0+0){}", "condition", "unexpected_a", "+", 5] +// ["if(0-0){}", "condition", "unexpected_a", "-", 5] +// ["if(0/0){}", "condition", "unexpected_a", "/", 5] +// ["if(0<<0){}", "condition", "unexpected_a", "<<", 5] +// ["if(0>>0){}", "condition", "unexpected_a", ">>", 5] +// ["if(0>>>0){}", "condition", "unexpected_a", ">>>", 5] +// ["if(0?0:0){}", "condition", "unexpected_a", "?", 5] +// ["if(0^0){}", "condition", "unexpected_a", "^", 5] +// ["if(0|0){}", "condition", "unexpected_a", "|", 5] +// ["if(\"aa\"){}", "condition", "unexpected_a", "aa", 4] +// ["if(typeof 0){}", "condition", "unexpected_a", "typeof", 4] +// ["if(~0){}", "condition", "unexpected_a", "~", 4] + + warn("unexpected_a", the_value); + break; + } + return the_value; + } + + function constant(id, type, value) { + +// Create a constant symbol. + + const the_symbol = symbol(id); + the_symbol.constant = true; + the_symbol.nud_prefix = ( + typeof value === "function" + ? value + : function () { + token_now.constant = true; + if (value !== undefined) { + token_now.value = value; + } + return token_now; + } + ); + the_symbol.type = type; + the_symbol.value = value; + return the_symbol; + } + + function constant_Function() { + if (!option_dict.eval) { + +// test_cause: +// ["Function", "constant_Function", "unexpected_a", "Function", 1] + + warn("unexpected_a", token_now); + } else if (token_nxt.id !== "(") { + +// test_cause: +// [" +// /*jslint eval*/ +// Function +// ", "constant_Function", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "(", artifact()); + } + return token_now; + } + + function constant_arguments() { + +// test_cause: +// ["arguments", "constant_arguments", "unexpected_a", "arguments", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function constant_eval() { + if (!option_dict.eval) { + +// test_cause: +// ["eval", "constant_eval", "unexpected_a", "eval", 1] + + warn("unexpected_a", token_now); + } else if (token_nxt.id !== "(") { + +// test_cause: +// ["/*jslint eval*/\neval", "constant_eval", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "(", artifact()); + } + return token_now; + } + + function constant_ignore() { + +// test_cause: +// ["ignore", "constant_ignore", "unexpected_a", "ignore", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function constant_isInfinite() { + +// test_cause: +// ["isFinite", "constant_isInfinite", "expected_a_b", "isFinite", 1] + + warn("expected_a_b", token_now, "Number.isFinite", "isFinite"); + return token_now; + } + + function constant_isNaN() { + +// test_cause: +// ["isNaN(0)", "constant_isNaN", "number_isNaN", "isNaN", 1] + + warn("number_isNaN", token_now); + return token_now; + } + + function constant_this() { + if (!option_dict.this) { + +// test_cause: +// ["this", "constant_this", "unexpected_a", "this", 1] + + warn("unexpected_a", token_now); + } + return token_now; + } + + function enroll(name, role, readonly) { + +// Enroll a name into the current function context. The role can be exception, +// function, label, parameter, or variable. We look for variable redefinition +// because it causes confusion. + + let earlier; + let id = name.id; + +// Reserved words may not be enrolled. + + if (syntax_dict[id] !== undefined && id !== "ignore") { + +// test_cause: +// ["let undefined", "enroll", "reserved_a", "undefined", 5] + + warn("reserved_a", name); + return; + } + +// Has the name been enrolled in this context? + + earlier = functionage.context[id] || catchage.context[id]; + if (earlier) { + +// test_cause: +// ["let aa;let aa", "enroll", "redefinition_a_b", "1", 12] + + warn("redefinition_a_b", name, id, earlier.line); + return; + } + +// Has the name been enrolled in an outer context? + + function_stack.forEach(function ({ + context + }) { + earlier = context[id] || earlier; + }); + if (earlier && id === "ignore") { + if (earlier.role === "variable") { + +// test_cause: +// ["let ignore;function aa(ignore){}", "enroll", "redefinition_a_b", "1", 24] + + warn("redefinition_a_b", name, id, earlier.line); + } + } else if ( + earlier + && role !== "parameter" && role !== "function" + && (role !== "exception" || earlier.role !== "exception") + ) { + +// test_cause: +// [" +// function aa(){try{aa();}catch(aa){aa();}} +// ", "enroll", "redefinition_a_b", "1", 31] +// ["function aa(){var aa;}", "enroll", "redefinition_a_b", "1", 19] + + warn("redefinition_a_b", name, id, earlier.line); + } else if ( + option_dict.beta + && global_dict[id] + && role !== "parameter" + ) { + +// test_cause: +// ["let Array", "enroll", "redefinition_global_a_b", "Array", 5] + + warn("redefinition_global_a_b", name, global_dict[id], id); + } + +// Enroll it. + + Object.assign(name, { + dead: true, + init: false, + parent: ( + role === "exception" + ? catchage + : functionage + ), + readonly, + role, + used: 0 + }); + name.parent.context[id] = name; + } + + function infix(bp, id, f) { + +// Create an infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led_infix = function (left) { + const the_token = token_now; + the_token.arity = "binary"; + if (f !== undefined) { + return f(left); + } + the_token.expression = [left, parse_expression(bp)]; + return the_token; + }; + return the_symbol; + } + + function infix_dot(left) { + const the_token = token_now; + let name = token_nxt; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "flat" + && name.id !== "flatMap" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + +// test_cause: +// ["\"\".aa", "check_left", "unexpected_a", ".", 3] + + check_left(left, the_token); + } + if (!name.identifier) { + +// test_cause: +// ["aa.0", "infix_dot", "expected_identifier_a", "0", 4] + + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; + } + + function infix_fart_unwrapped() { + +// test_cause: +// ["aa=>0", "infix_fart_unwrapped", "wrap_fart_parameter", "=>", 3] + + return stop("wrap_fart_parameter", token_now); + } + + function infix_grave(left) { + const the_tick = prefix_tick(); + +// test_cause: +// ["0``", "check_left", "unexpected_a", "`", 2] + + check_left(left, the_tick); + the_tick.expression = [left].concat(the_tick.expression); + return the_tick; + } + + function infix_lbracket(left) { + const the_token = token_now; + let name; + let the_subscript = parse_expression(0); + if (the_subscript.id === "(string)" || the_subscript.id === "`") { + name = survey(the_subscript); + +// PR-404 - Add new directive "subscript" to play nice with Google Closure. + + if (!option_dict.subscript && jslint_rgx_identifier.test(name)) { + +// test_cause: +// ["aa[`aa`]", "infix_lbracket", "subscript_a", "aa", 4] + + warn("subscript_a", the_subscript, name); + } + } + +// test_cause: +// ["0[0]", "check_left", "unexpected_a", "[", 2] + + check_left(left, the_token); + the_token.expression = [left, the_subscript]; + advance("]"); + return the_token; + } + + function infix_lparen(left) { + const the_paren = token_now; + let ellipsis; + let the_argument; + if (left.id !== "function") { + +// test_cause: +// ["(0?0:0)()", "check_left", "unexpected_a", "(", 8] +// ["0()", "check_left", "unexpected_a", "(", 2] + + check_left(left, the_paren); + } + if (functionage.arity === "statement" && left.identifier) { + functionage.name.calls[left.id] = left; + } + the_paren.expression = [left]; + if (token_nxt.id !== ")") { + +// Parse/loop through each token in expression (...). + + while (true) { + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + the_argument = parse_expression(10); + if (ellipsis) { + the_argument.ellipsis = true; + } + the_paren.expression.push(the_argument); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance(")", the_paren); + if (the_paren.expression.length === 2) { + +// test_cause: +// ["aa(0)", "infix_lparen", "free", "", 0] + + test_cause("free"); + the_paren.free = true; + if (the_argument.wrapped === true) { + +// test_cause: +// ["aa((0))", "infix_lparen", "unexpected_a", "(", 3] + + warn("unexpected_a", the_paren); + } + if (the_argument.id === "(") { + the_argument.wrapped = true; + } + } else { + +// test_cause: +// ["aa()", "infix_lparen", "not_free", "", 0] +// ["aa(0,0)", "infix_lparen", "not_free", "", 0] + + test_cause("not_free"); + the_paren.free = false; + } + return the_paren; + } + + function infix_option_chain(left) { + const the_token = token_now; + let name = token_nxt; + if ( + ( + left.id !== "(string)" + || (name.id !== "indexOf" && name.id !== "repeat") + ) + && ( + left.id !== "[" + || ( + name.id !== "concat" + && name.id !== "forEach" + && name.id !== "join" + && name.id !== "map" + ) + ) + +// test_cause: +// ["(0+0)?.0", "infix_option_chain", "check_left", "", 0] + + && (left.id !== "+" || name.id !== "slice") + && ( + left.id !== "(regexp)" + || (name.id !== "exec" && name.id !== "test") + ) + ) { + test_cause("check_left"); + +// test_cause: +// ["(/./)?.0", "check_left", "unexpected_a", "?.", 6] +// ["\"aa\"?.0", "check_left", "unexpected_a", "?.", 5] +// ["aa=[]?.aa", "check_left", "unexpected_a", "?.", 6] + + check_left(left, the_token); + } + +// Issue #468 - Fix optional dynamic-property/function-call not recognized. + + if (name.id === "[" || name.id === "(") { + test_cause("dyn_prop_or_call"); + +// test_cause: +// ["aa?.(bb)", "infix_option_chain", "dyn_prop_or_call", "", 0] +// ["aa?.[bb]", "infix_option_chain", "dyn_prop_or_call", "", 0] + + return left; + } + if (!name.identifier) { + +// test_cause: +// ["aa?.0", "infix_option_chain", "expected_identifier_a", "0", 5] + + stop("expected_identifier_a"); + } + advance(); + survey(name); + +// The property name is not an expression. + + the_token.name = name; + the_token.expression = left; + return the_token; + } + + function infixr(bp, id) { + +// Create a right associative infix operator. + + const the_symbol = symbol(id, bp); + the_symbol.led_infix = function parse_infixr_led(left) { + const the_token = token_now; + +// test_cause: +// ["0**0", "parse_infixr_led", "led_infix", "", 0] + + test_cause("led_infix"); + the_token.arity = "binary"; + the_token.expression = [left, parse_expression(bp - 1)]; + return the_token; + }; + return the_symbol; + } + + function parse_expression(rbp, initial) { + +// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it +// is looking for ad hoc lint patterns. We add .fud_stmt to Pratt's model, which +// is like .nud_prefix except that it is only used on the first token of a +// statement. Having .fud_stmt makes it much easier to define statement-oriented +// languages like JavaScript. I retained Pratt's nomenclature. +// They are elements of the parsing method called Top Down Operator Precedence. + +// .nud_prefix Null denotation. The prefix handler. +// .fud_stmt First null denotation. The statement handler. +// .led_infix Left denotation. The infix/postfix handler. +// lbp Left binding power of infix operator. It tells us how strongly +// the operator binds to the argument at its left. +// rbp Right binding power. + +// It processes a nud_prefix (variable, constant, prefix operator). It will then +// process leds (infix operators) until the bind powers cause it to stop (it +// consumes tokens until it meets a token whose lbp <= rbp). Specifically, it +// means that it collects all tokens that bind together before returning to the +// operator that called it. It returns the expression's parse tree. + +// For example, "3 + 1 * 2 * 4 + 5" +// parses into +// { +// "id": "+", +// "expression": [ +// { +// "id": "+", +// "expression": [ +// { +// "id": "(number)", +// "value": "3" +// }, +// { +// "id": "*", +// "expression": [ +// { +// "id": "*", +// "expression": [ +// { +// "id": "(number)", +// "value": "1" +// }, +// { +// "id": "(number)", +// "value": "2" +// } +// ] +// }, +// { +// "id": "(number)", +// "value": "4" +// } +// ] +// } +// ] +// }, +// { +// "id": "(number)", +// "value": "5" +// } +// ] +// } + + let left; + let the_symbol; + +// Statements will have already advanced, so advance now only if the token is +// not the first of a statement. + + if (!initial) { + advance(); + } + the_symbol = syntax_dict[token_now.id]; + if (the_symbol !== undefined && the_symbol.nud_prefix !== undefined) { + +// test_cause: +// ["0", "parse_expression", "symbol", "", 0] + + test_cause("symbol"); + left = the_symbol.nud_prefix(); + } else if (token_now.identifier) { + +// test_cause: +// ["aa", "parse_expression", "identifier", "", 0] + + test_cause("identifier"); + left = token_now; + left.arity = "variable"; + } else { + +// test_cause: +// ["!", "parse_expression", "unexpected_a", "(end)", 1] +// ["/./", "parse_expression", "unexpected_a", "/", 1] +// ["let aa=`${}`;", "parse_expression", "unexpected_a", "}", 11] + + return stop("unexpected_a", token_now); + } + +// Parse/loop through each symbol in expression. + + while (true) { + the_symbol = syntax_dict[token_nxt.id]; + if ( + the_symbol === undefined + || the_symbol.led_infix === undefined + || the_symbol.lbp <= rbp + ) { + break; + } + advance(); + left = the_symbol.led_infix(left); + } + return left; + } + + function parse_fart(the_fart) { + +// Give the function properties storing its names and for observing the depth +// of loops and switches. + + Object.assign(the_fart, { + arity: "binary", + context: empty(), + finally: 0, + level: functionage.level + 1, + loop: 0, + name: anon, + switch: 0, + try: 0 + }); + +// PR-384 - Relax warning "function_in_loop". +// +// if (functionage.loop > 0) { + +// // test_cause: +// // ["while(0){aa.map(()=>0);}", "parse_fart", "function_in_loop", "=>", 19] +// +// warn("function_in_loop", the_fart); +// } + +// Push the current function context and establish a new one. + + function_list.push(the_fart); + function_stack.push(functionage); + functionage = the_fart; + +// Parse the parameter list. + + prefix_function_parameter(the_fart); + advance("=>"); + +// The function's body is a block. + + if (token_nxt.id === "{") { + if (!option_dict.fart) { + +// test_cause: +// ["()=>{}", "parse_fart", "use_function_not_fart", "=>", 3] + + warn("use_function_not_fart", the_fart); + } + the_fart.block = block("body"); + } else if ( + syntax_dict[token_nxt.id] !== undefined + && syntax_dict[token_nxt.id].fud_stmt !== undefined + ) { + +// PR-384 - Bugfix - Fixes issue #379 - warn against naked-statement in fart. + +// test_cause: +// ["()=>delete aa", "parse_fart", "unexpected_a_after_b", "=>", 5] + + stop("unexpected_a_after_b", token_nxt, token_nxt.id, "=>"); + +// The function's body is an expression. + + } else { + the_fart.expression = parse_expression(0); + } + +// Restore the previous context. + + functionage = function_stack.pop(); + return the_fart; + } + + function parse_json() { + let container; + let is_dup; + let name; + let negative; + switch (token_nxt.id) { + case "(number)": + if (!jslint_rgx_json_number.test(token_nxt.value)) { + +// test_cause: +// ["[-.0]", "parse_json", "unexpected_a", ".", 3] +// ["[-0x0]", "parse_json", "unexpected_a", "0x0", 3] +// ["[.0]", "parse_json", "unexpected_a", ".", 2] +// ["[0x0]", "parse_json", "unexpected_a", "0x0", 2] + + warn("unexpected_a"); + } + advance("(number)"); + return token_now; + case "(string)": + if (token_nxt.quote !== "\"") { + +// test_cause: +// ["['']", "parse_json", "unexpected_a", "'", 2] + + warn("unexpected_a", token_nxt, token_nxt.quote); + } + advance("(string)"); + return token_now; + case "-": + negative = token_nxt; + negative.arity = "unary"; + advance("-"); + +// Recurse parse_json(). + + negative.expression = parse_json(); + return negative; + case "[": + +// test_cause: +// ["[]", "parse_json", "bracket", "", 0] + + test_cause("bracket"); + container = token_nxt; + container.expression = []; + advance("["); + if (token_nxt.id !== "]") { + while (true) { + +// Recurse parse_json(). + + container.expression.push(parse_json()); + if (token_nxt.id !== ",") { + +// test_cause: +// ["[0,0]", "parse_json", "comma", "", 0] + + test_cause("comma"); + break; + } + advance(","); + } + } + advance("]", container); + return container; + case "false": + case "null": + case "true": + +// test_cause: +// ["[false]", "parse_json", "constant", "", 0] +// ["[null]", "parse_json", "constant", "", 0] +// ["[true]", "parse_json", "constant", "", 0] + + test_cause("constant"); + advance(); + return token_now; + case "{": + +// test_cause: +// ["{}", "parse_json", "brace", "", 0] + + test_cause("brace"); + container = token_nxt; + +// Explicit empty-object required to detect "__proto__". + + is_dup = empty(); + container.expression = []; + advance("{"); + if (token_nxt.id !== "}") { + +// JSON +// Parse/loop through each property in {...}. + + while (true) { + if (token_nxt.quote !== "\"") { + +// test_cause: +// ["{0:0}", "parse_json", "unexpected_a", "0", 2] + + warn( + "unexpected_a", + token_nxt, + token_nxt.quote + ); + } + name = token_nxt; + advance("(string)"); + if (is_dup[token_now.value] !== undefined) { + +// test_cause: +// ["{\"aa\":0,\"aa\":0}", "parse_json", "duplicate_a", "aa", 9] + + warn("duplicate_a", token_now); + } else if (token_now.value === "__proto__") { + +// test_cause: +// ["{\"__proto__\":0}", "parse_json", "weird_property_a", "__proto__", 2] + + warn("weird_property_a", token_now); + } else { + is_dup[token_now.value] = token_now; + } + advance(":"); + container.expression.push( + +// Recurse parse_json(). + + Object.assign(parse_json(), { + label: name + }) + ); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance("}", container); + return container; + default: + +// test_cause: +// ["[undefined]", "parse_json", "unexpected_a", "undefined", 2] + + stop("unexpected_a"); + } + } + + function parse_statement() { + +// Parse a statement. Any statement may have a label, but only four statements +// have use for one. A statement can be one of the standard statements, or +// an assignment expression, or an invocation expression. + + let first; + let the_label; + let the_statement; + let the_symbol; + advance(); + if (token_now.identifier && token_nxt.id === ":") { + the_label = token_now; + if (the_label.id === "ignore") { + +// test_cause: +// ["ignore:", "parse_statement", "unexpected_a", "ignore", 1] + + warn("unexpected_a", the_label); + } + advance(":"); + switch (token_nxt.id) { + case "do": + case "for": + case "switch": + case "while": + +// test_cause: +// ["aa:do{}", "parse_statement", "the_statement_label", "do", 0] +// ["aa:for{}", "parse_statement", "the_statement_label", "for", 0] +// ["aa:switch{}", "parse_statement", "the_statement_label", "switch", 0] +// ["aa:while{}", "parse_statement", "the_statement_label", "while", 0] + + test_cause("the_statement_label", token_nxt.id); + enroll(the_label, "label", true); + the_label.dead = false; + the_label.init = true; + the_statement = parse_statement(); + functionage.statement_prv = the_statement; + the_statement.label = the_label; + the_statement.statement = true; + return the_statement; + } + advance(); + +// test_cause: +// ["aa:", "parse_statement", "unexpected_label_a", "aa", 1] + + warn("unexpected_label_a", the_label); + } + +// Parse the statement. + + first = token_now; + first.statement = true; + the_symbol = syntax_dict[first.id]; + if ( + the_symbol !== undefined + && the_symbol.fud_stmt !== undefined + +// PR-318 - Bugfix - Fixes issues #316, #317 - dynamic-import(). + + && !(the_symbol.id === "import" && token_nxt.id === "(") + ) { + the_symbol.disrupt = false; + the_symbol.statement = true; + token_now.arity = "statement"; + the_statement = the_symbol.fud_stmt(); + functionage.statement_prv = the_statement; + } else { + +// It is an expression statement. + + the_statement = parse_expression(0, true); + functionage.statement_prv = the_statement; + if (the_statement.wrapped && the_statement.id !== "(") { + +// test_cause: +// ["(0)", "parse_statement", "unexpected_a", "(", 1] + + warn("unexpected_a", first); + } + semicolon(); + } + if (the_label !== undefined) { + the_label.dead = true; + } + return the_statement; + } + + function parse_statements() { + +// Parse a list of statements. Give a warning if an unreachable statement +// follows a disruptive statement. + + const statement_list = []; + let a_statement; + let disrupt = false; + +// Parse/loop each statement until a statement-terminator is reached. + + while (true) { + switch (token_nxt.id) { + case "(end)": + case "case": + case "default": + case "else": + case "}": + +// test_cause: +// [";", "parse_statements", "closer", "", 0] +// ["case", "parse_statements", "closer", "", 0] +// ["default", "parse_statements", "closer", "", 0] +// ["else", "parse_statements", "closer", "", 0] +// ["}", "parse_statements", "closer", "", 0] + + test_cause("closer"); + return statement_list; + } + a_statement = parse_statement(); + statement_list.push(a_statement); + if (disrupt) { + +// test_cause: +// ["while(0){break;0;}", "parse_statements", "unreachable_a", "0", 16] + + warn("unreachable_a", a_statement); + } + disrupt = a_statement.disrupt; + } + } + + function postassign(id) { + +// Create one of the postassign operators. + + const the_symbol = symbol(id, 150); + the_symbol.led_infix = function (left) { + token_now.expression = left; + token_now.arity = "postassign"; + check_mutation(token_now.expression); + return token_now; + }; + return the_symbol; + } + + function preassign(id) { + +// Create one of the preassign operators. + + const the_symbol = symbol(id); + the_symbol.nud_prefix = function () { + const the_token = token_now; + the_token.arity = "preassign"; + the_token.expression = parse_expression(150); + check_mutation(the_token.expression); + return the_token; + }; + return the_symbol; + } + + function prefix(id, f) { + +// Create a prefix operator. + + const the_symbol = symbol(id); + the_symbol.nud_prefix = function () { + const the_token = token_now; + the_token.arity = "unary"; + if (typeof f === "function") { + return f(); + } + the_token.expression = parse_expression(150); + return the_token; + }; + return the_symbol; + } + + function prefix_assign_divide() { + +// test_cause: +// ["/=", "prefix_assign_divide", "expected_a_b", "/=", 1] + + stop("expected_a_b", token_now, "/\\=", "/="); + } + + function prefix_async() { + let the_async = token_now; + let the_function; + token_nxt.arity = the_async.arity; + +// PR-414 - Parse async fart. + + if (token_nxt.fart) { + advance("("); + the_function = Object.assign(token_now.fart, { + async: 1 + }); + if (!option_dict.fart) { + +// test_cause: +// ["async()=>0", "prefix_async", "use_function_not_fart", "=>", 8] + + warn("use_function_not_fart", the_function); + } + prefix_lparen(); + +// Parse async function. + + } else { + advance("function"); + the_function = Object.assign(token_now, { + async: 1 + }); + prefix_function(); + } + if (the_function.async === 1) { + +// test_cause: +// [" +// async function aa(){} +// ", "prefix_async", "missing_await_statement", "function", 7] + + warn("missing_await_statement", the_function); + } + return the_function; + } + + function prefix_await() { + const the_await = token_now; + +// PR-370 - Add top-level-await support. + + if (functionage.async === 0 && functionage !== token_global) { + +// test_cause: +// ["function aa(){aa=await 0;}", "prefix_await", "unexpected_a", "await", 18] +// ["function aa(){await 0;}", "prefix_await", "unexpected_a", "await", 15] + + warn("unexpected_a", the_await); + } else { + functionage.async += 1; + } + if (the_await.arity === "statement") { + +// PR-405 - Bugfix - fix expression after "await" mis-identified as statement. + + the_await.expression = parse_expression(150); + semicolon(); + } else { + the_await.expression = parse_expression(150); + } + return the_await; + } + + function prefix_fart() { + +// test_cause: +// ["=>0", "prefix_fart", "expected_a_before_b", "=>", 1] + + return stop("expected_a_before_b", token_now, "()", "=>"); + } + + function prefix_function(the_function) { + let name = the_function && the_function.name; + if (the_function === undefined) { + the_function = token_now; + +// A function statement must have a name that will be in the parent's scope. + + if (the_function.arity === "statement") { + if (!token_nxt.identifier) { + +// test_cause: +// ["function(){}", "prefix_function", "expected_identifier_a", "(", 9] +// ["function*aa(){}", "prefix_function", "expected_identifier_a", "*", 9] + + return stop("expected_identifier_a"); + } + name = token_nxt; + enroll(name, "variable", true); + the_function.name = Object.assign(name, { + calls: empty(), + +// PR-331 - Bugfix - Fixes issue #272 - function hoisting not allowed. + + dead: false, + init: true + }); + advance(); + } else if (name === undefined) { + +// A function expression may have an optional name. + + the_function.name = anon; + if (token_nxt.identifier) { + name = token_nxt; + the_function.name = name; + advance(); + } + } + } + +// Probably deadcode. +// if (mode_mega) { +// warn("unexpected_a", the_function); +// } +// jslint_assert(!mode_mega, `Expected !mode_mega.`); + +// PR-378 - Relax warning "function_in_loop". +// +// // Don't create functions in loops. It is inefficient, and it can lead to +// // scoping errors. +// +// if (functionage.loop > 0) { +// +// // test_cause: +// // [" +// // while(0){aa.map(function(){});} +// // ", "prefix_function", "function_in_loop", "function", 17] +// +// warn("function_in_loop", the_function); +// } + +// Give the function properties for storing its names and for observing the +// depth of loops and switches. + + Object.assign(the_function, { + async: the_function.async || 0, + context: empty(), + finally: 0, + level: functionage.level + 1, + loop: 0, + statement_prv: undefined, + switch: 0, + try: 0 + }); + if (the_function.arity !== "statement" && typeof name === "object") { + +// test_cause: +// ["let aa=function bb(){return;};", "prefix_function", "expression", "bb", 0] + + test_cause("expression", name.id); + enroll(name, "function", true); + name.dead = false; + name.init = true; + name.used = 1; + } + +// PR-334 - Bugfix - fix function-redefinition not warned inside function-call. +// Push the current function context and establish a new one. + + function_list.push(the_function); + function_stack.push(functionage); + functionage = the_function; + +// Parse the parameter list. + + advance("("); + token_now.arity = "function"; + prefix_function_parameter(the_function); + +// The function's body is a block. + + the_function.block = block("body"); + if ( + the_function.arity === "statement" + && token_nxt.line === token_now.line + ) { + +// test_cause: +// ["function aa(){}0", "prefix_function", "unexpected_a", "0", 16] + + return stop("unexpected_a"); + } + if ( + token_nxt.id === "." + || token_nxt.id === "?." + +// PR-459 - Allow destructuring-assignment after function-definition. + + // || token_nxt.id === "[" + ) { + +// test_cause: +// ["function aa(){}\n.aa", "prefix_function", "unexpected_a", ".", 1] +// ["function aa(){}\n?.aa", "prefix_function", "unexpected_a", "?.", 1] + + warn("unexpected_a"); + } + +// Check functions are ordered. + + check_ordered( + "function", + function_list.slice( + function_list.indexOf(the_function) + 1 + ).map(function ({ + level, + name + }) { + return (level === the_function.level + 1) && name; + }).filter(function (name) { + return option_dict.beta && name && name.id; + }) + ); + +// Restore the previous context. + + functionage = function_stack.pop(); + return the_function; + } + + function prefix_function_parameter(the_function) { + +// This function will parse input at beginning of + + let optional; + let parameters = []; + let signature = ["("]; + let subparam; + function param_enroll(name) { + if (name.identifier) { + enroll(name, "parameter", false); + } else { + +// test_cause: +// ["([aa])=>0", "param_enroll", "use_function_not_fart", "=>", 7] +// ["({aa})=>0", "param_enroll", "use_function_not_fart", "=>", 7] + + if (the_function.id === "=>" && !option_dict.fart) { + warn("use_function_not_fart", the_function); + } + +// Recurse param_enroll(). + + name.names.forEach(param_enroll); + } + } + function param_parse() { + let ellipsis = false; + let param; + if (token_nxt.id === "{") { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,{}){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + param = token_nxt; + param.names = []; + advance("{"); + signature.push("{"); + while (true) { + subparam = token_nxt; + if (!subparam.identifier) { + +// test_cause: +// ["function aa(aa=0,{}){}", "param_parse", "expected_identifier_a", "}", 19] +// ["function aa({0}){}", "param_parse", "expected_identifier_a", "0", 14] + + return stop("expected_identifier_a"); + } + survey(subparam); + advance(); + signature.push(subparam.id); + if (token_nxt.id === ":") { + advance(":"); + advance(); + token_now.label = subparam; + subparam = token_now; + if (!subparam.identifier) { + +// test_cause: +// ["function aa({aa:0}){}", "param_parse", "expected_identifier_a", "}", 18] + + return stop( + "expected_identifier_a", + token_nxt + ); + } + } + +// test_cause: +// ["function aa({aa=aa},aa){}", "param_parse", "equal", "", 0] + + test_cause("equal"); + if (token_nxt.id === "=") { + advance("="); + subparam.expression = parse_expression(); + param.open = true; + } + param.names.push(subparam); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + } else { + break; + } + } + parameters.push(param); + +// test_cause: +// [" +// function aa({bb,aa}){} +// ", "check_ordered", "expected_a_b_before_c_d", "aa", 17] + + check_ordered("parameter", param.names); + advance("}"); + signature.push("}"); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } else if (token_nxt.id === "[") { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,[]){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + param = token_nxt; + param.names = []; + advance("["); + signature.push("[]"); + while (true) { + subparam = token_nxt; + if (!subparam.identifier) { + +// test_cause: +// ["function aa(aa=0,[]){}", "param_parse", "expected_identifier_a", "]", 19] + + return stop("expected_identifier_a"); + } + advance(); + param.names.push(subparam); + +// test_cause: +// ["function aa([aa=aa],aa){}", "param_parse", "id", "", 0] + + test_cause("id"); + if (token_nxt.id === "=") { + advance("="); + subparam.expression = parse_expression(); + param.open = true; + } + if (token_nxt.id === ",") { + advance(","); + } else { + break; + } + } + parameters.push(param); + advance("]"); + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } else { + if (token_nxt.id === "...") { + ellipsis = true; + signature.push("..."); + advance("..."); + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,...){}", "param_parse", "required_a_optional_b", "aa", 21] + + warn( + "required_a_optional_b", + token_nxt, + token_nxt.id, + optional.id + ); + } + } + if (!token_nxt.identifier) { + +// test_cause: +// ["function aa(0){}", "param_parse", "expected_identifier_a", "0", 13] + + return stop("expected_identifier_a"); + } + param = token_nxt; + parameters.push(param); + advance(); + signature.push(param.id); + if (ellipsis) { + param.ellipsis = true; + } else { + if (token_nxt.id === "=") { + optional = param; + advance("="); + param.expression = parse_expression(0); + } else { + if (optional !== undefined) { + +// test_cause: +// ["function aa(aa=0,bb){}", "param_parse", "required_a_optional_b", "aa", 18] + + warn( + "required_a_optional_b", + param, + param.id, + optional.id + ); + } + } + if (token_nxt.id === ",") { + advance(","); + signature.push(", "); + param_parse(); + return; + } + } + } + } + +// test_cause: +// ["function aa(){}", "prefix_function_parameter", "opener", "(", 0] + + test_cause("opener", token_now.id); + token_now.free = false; + if (token_nxt.id !== ")" && token_nxt.id !== "(end)") { + param_parse(); + } + advance(")"); + signature.push(")"); + parameters.forEach(param_enroll); + the_function.parameters = parameters; + the_function.signature = signature.join(""); + } + + function prefix_lbrace() { + const seen = empty(); + const the_brace = token_now; + let extra; + let full; + let id; + let name; + let the_colon; + let value; + the_brace.expression = []; + if (token_nxt.id !== "}") { + +// Parse/loop through each property in {...}. + + while (true) { + name = token_nxt; + advance(); + if ( + (name.id === "get" || name.id === "set") + && token_nxt.identifier + ) { + if (!option_dict.getset) { + +// test_cause: +// ["aa={get aa(){}}", "prefix_lbrace", "unexpected_a", "get", 5] + + warn("unexpected_a", name); + } + extra = name.id; + full = extra + " " + token_nxt.id; + name = token_nxt; + advance(); + id = survey(name); + if (seen[full] === true || seen[id] === true) { + +// test_cause: +// ["aa={get aa(){},get aa(){}}", "prefix_lbrace", "duplicate_a", "aa", 20] + + warn("duplicate_a", name); + } + seen[id] = false; + seen[full] = true; + } else if (name.id === "`") { + +// test_cause: +// ["aa={`aa`:0}", "prefix_lbrace", "unexpected_a", "`", 5] + + stop("unexpected_a", name); + + } else { + id = survey(name); + if (typeof seen[id] === "boolean") { + +// test_cause: +// ["aa={aa,aa}", "prefix_lbrace", "duplicate_a", "aa", 8] + + warn("duplicate_a", name); + } + seen[id] = true; + } + if (name.identifier) { + if (token_nxt.id === "}" || token_nxt.id === ",") { + if (typeof extra === "string") { + +// test_cause: +// ["aa={get aa}", "prefix_lbrace", "closer", "", 0] + + test_cause("closer"); + advance("("); + } + value = parse_expression(Infinity, true); + } else if (token_nxt.id === "(") { + +// test_cause: +// ["aa={aa()}", "prefix_lbrace", "paren", "", 0] +// ["aa={get aa(){}}", "prefix_lbrace", "paren", "", 0] + + test_cause("paren"); + value = prefix_function({ + arity: "unary", + from: name.from, + id: "function", + line: name.line, + name: ( + typeof extra === "string" + ? extra + : id + ), + thru: name.from + }); + } else { + if (typeof extra === "string") { + +// test_cause: +// ["aa={get aa.aa}", "prefix_lbrace", "paren", "", 0] + + test_cause("paren"); + advance("("); + } + the_colon = token_nxt; + advance(":"); + value = parse_expression(0); + if ( + value.id === name.id + && value.id !== "function" + ) { + +// test_cause: +// ["aa={aa:aa}", "prefix_lbrace", "unexpected_a", ": aa", 7] + + warn("unexpected_a", the_colon, ": " + name.id); + } + } + value.label = name; + if (typeof extra === "string") { + value.extra = extra; + } + the_brace.expression.push(value); + } else { + +// test_cause: +// ["aa={\"aa\":0}", "prefix_lbrace", "colon", "", 0] + + test_cause("colon"); + advance(":"); + value = parse_expression(0); + value.label = name; + the_brace.expression.push(value); + } + if (token_nxt.id !== ",") { + break; + } + +// test_cause: +// ["aa={\"aa\":0,\"bb\":0}", "prefix_lbrace", "comma", "", 0] + + test_cause("comma"); + advance(","); + if (token_nxt.id === "}") { + +// test_cause: +// ["let aa={aa:0,}", "prefix_lbrace", "unexpected_a", ",", 13] + + warn("unexpected_a", token_now); + break; + } + } + } + +// test_cause: +// ["aa={bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8] + + check_ordered( + "property", + the_brace.expression.map(function ({ + label + }) { + return label; + }) + ); + advance("}"); + return the_brace; + } + + function prefix_lbracket() { + const the_token = token_now; + let element; + let ellipsis; + the_token.expression = []; + if (token_nxt.id !== "]") { + +// Parse/loop through each element in [...]. + + while (true) { + ellipsis = false; + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + element = parse_expression(10); + if (ellipsis) { + element.ellipsis = true; + } + the_token.expression.push(element); + if (token_nxt.id !== ",") { + break; + } + advance(","); + if (token_nxt.id === "]") { + +// test_cause: +// ["let aa=[0,]", "prefix_lbracket", "unexpected_a", ",", 10] + + warn("unexpected_a", token_now); + break; + } + } + } + advance("]"); + return the_token; + } + + function prefix_lparen() { + let the_paren = token_now; + let the_value; + +// PR-385 - Bugfix - Fixes issue #382 - failure to detect destructured fart. + + if (token_now.fart) { + return parse_fart(token_now.fart); + } + +// test_cause: +// ["(0)", "prefix_lparen", "expr", "", 0] + + test_cause("expr"); + the_paren.free = true; + the_value = parse_expression(0); + if (the_value.wrapped === true) { + +// test_cause: +// ["((0))", "prefix_lparen", "unexpected_a", "(", 1] + + warn("unexpected_a", the_paren); + } + the_value.wrapped = true; + advance(")", the_paren); + return the_value; + } + + function prefix_new() { + const the_new = token_now; + let right; + right = parse_expression(160); + if (token_nxt.id !== "(") { + +// test_cause: +// ["new aa", "prefix_new", "expected_a_before_b", "(end)", 1] + + warn("expected_a_before_b", token_nxt, "()", artifact()); + } + the_new.expression = right; + return the_new; + } + + function prefix_tick() { + const the_tick = token_now; + the_tick.value = []; + the_tick.expression = []; + if (token_nxt.id !== "`") { + +// Parse/loop through each token in `${...}`. + + while (true) { + advance("(string)"); + the_tick.value.push(token_now); + if (token_nxt.id !== "${") { + break; + } + advance("${"); + +// test_cause: +// ["let aa=`${}`;", "prefix_tick", "${", "", 0] + + test_cause("${"); + the_tick.expression.push(parse_expression(0)); + advance("}"); + } + } + advance("`"); + return the_tick; + } + + function prefix_void() { + const the_void = token_now; + +// test_cause: +// ["void 0", "prefix_void", "unexpected_a", "void", 1] +// ["void", "prefix_void", "unexpected_a", "void", 1] + + warn("unexpected_a", the_void); + the_void.expression = parse_expression(0); + return the_void; + } + + function semicolon() { + +// Try to match a semicolon. + + if (token_nxt.id === ";") { + advance(";"); + } else { + +// test_cause: +// ["0", "semicolon", "expected_a_b", "(end)", 1] + + warn_at( + "expected_a_b", + token_now.line, + token_now.thru + 1, + ";", + artifact() + ); + } + anon = "anonymous"; + } + + function stmt(id, fud_stmt) { + +// Create a statement. + + const the_symbol = symbol(id); + the_symbol.fud_stmt = fud_stmt; + return the_symbol; + } + + function stmt_break() { + const the_break = token_now; + let the_label; + if ( + (functionage.loop < 1 && functionage.switch < 1) + || functionage.finally > 0 + ) { + +// test_cause: +// ["break", "stmt_break", "unexpected_a", "break", 1] + + warn("unexpected_a", the_break); + } + the_break.disrupt = true; + if (token_nxt.identifier && token_now.line === token_nxt.line) { + the_label = functionage.context[token_nxt.id]; + if ( + the_label === undefined + || the_label.role !== "label" + || the_label.dead + ) { + if (the_label !== undefined && the_label.dead) { + +// test_cause: +// ["aa:{function aa(aa){break aa;}}", "stmt_break", "out_of_scope_a", "aa", 27] + + warn("out_of_scope_a"); + } else { + +// test_cause: +// ["aa:{break aa;}", "stmt_break", "not_label_a", "aa", 11] + + warn("not_label_a"); + } + } else { + the_label.used += 1; + } + the_break.label = token_nxt; + advance(); + } + advance(";"); + return the_break; + } + + function stmt_continue() { + const the_continue = token_now; + if (functionage.loop < 1 || functionage.finally > 0) { + +// test_cause: +// ["continue", "stmt_continue", "unexpected_a", "continue", 1] +// [" +// function aa(){while(0){try{}finally{continue}}} +// ", "stmt_continue", "unexpected_a", "continue", 37] + + warn("unexpected_a", the_continue); + } + check_not_top_level(the_continue); + the_continue.disrupt = true; + warn("unexpected_a", the_continue); + advance(";"); + return the_continue; + } + + function stmt_debugger() { + const the_debug = token_now; + if (!option_dict.devel) { + +// test_cause: +// ["debugger", "stmt_debugger", "unexpected_a", "debugger", 1] + + warn("unexpected_a", the_debug); + } + semicolon(); + return the_debug; + } + + function stmt_delete() { + const the_token = token_now; + const the_value = parse_expression(0); + if ( + (the_value.id !== "." && the_value.id !== "[") + || the_value.arity !== "binary" + ) { + +// test_cause: +// ["delete 0", "stmt_delete", "expected_a_b", "0", 8] + + stop("expected_a_b", the_value, ".", artifact(the_value)); + } + the_token.expression = the_value; + semicolon(); + return the_token; + } + + function stmt_do() { + const the_do = token_now; + check_not_top_level(the_do); + functionage.loop += 1; + the_do.block = block(); + advance("while"); + the_do.expression = condition(); + semicolon(); + if (the_do.block.disrupt === true) { + +// test_cause: +// ["function aa(){do{break;}while(0)}", "stmt_do", "weird_loop", "do", 15] + + warn("weird_loop", the_do); + } + functionage.loop -= 1; + return the_do; + } + + function stmt_export() { + let export_list = []; + let the_export = token_now; + let the_id; + let the_name; + let the_thing; + + the_export.expression = []; + if (token_nxt.id === "default") { + if (export_dict.default !== undefined) { + +// test_cause: +// [" +// export default 0;export default 0 +// ", "stmt_export", "duplicate_a", "default", 25] + + warn("duplicate_a"); + } + advance("default"); + the_thing = parse_expression(0); + if ( + the_thing.id !== "(" + || the_thing.expression[0].id !== "." + || the_thing.expression[0].expression.id !== "Object" + || the_thing.expression[0].name.id !== "freeze" + ) { + +// test_cause: +// ["export default {}", "stmt_export", "freeze_exports", "{", 16] + + warn("freeze_exports", the_thing); + +// PR-301 - Bugfix - Fixes issues #282 - optional-semicolon. + + } else { + +// test_cause: +// [" +// export default Object.freeze({}) +// ", "semicolon", "expected_a_b", "(end)", 32] + + semicolon(); + } + export_dict.default = the_thing; + the_export.expression.push(the_thing); + } else { + +// PR-439 - Add grammar for "export async function ...". + + if (token_nxt.id === "function" || token_nxt.id === "async") { + +// test_cause: +// ["export async function aa(){}", "stmt_export", "freeze_exports", "async", 8] +// ["export function aa(){}", "stmt_export", "freeze_exports", "function", 8] + + warn("freeze_exports"); + the_thing = parse_statement(); + the_name = the_thing.name; + the_id = the_name.id; + the_name.used += 1; + if (export_dict[the_id] !== undefined) { + +// test_cause: +// [" +// let aa;export{aa};export function aa(){} +// ", "stmt_export", "duplicate_a", "aa", 35] + + warn("duplicate_a", the_name); + } + export_dict[the_id] = the_thing; + the_export.expression.push(the_thing); + the_thing.statement = false; + the_thing.arity = "unary"; + } else if ( + token_nxt.id === "var" + || token_nxt.id === "let" + || token_nxt.id === "const" + ) { + +// test_cause: +// ["export const", "stmt_export", "unexpected_a", "const", 8] +// ["export let", "stmt_export", "unexpected_a", "let", 8] +// ["export var", "stmt_export", "unexpected_a", "var", 8] + + warn("unexpected_a"); + parse_statement(); + } else if (token_nxt.id === "{") { + +// test_cause: +// ["export {}", "stmt_export", "advance{", "", 0] + + test_cause("advance{"); + advance("{"); + while (true) { + if (!token_nxt.identifier) { + +// test_cause: +// ["export {}", "stmt_export", "expected_identifier_a", "}", 9] + + stop("expected_identifier_a"); + } + the_id = token_nxt.id; + export_list.push(token_nxt); + the_name = token_global.context[the_id]; + if (the_name === undefined) { + +// test_cause: +// ["export {aa}", "stmt_export", "unexpected_a", "aa", 9] + + warn("unexpected_a"); + } else { + the_name.used += 1; + if (export_dict[the_id] !== undefined) { + +// test_cause: +// ["let aa;export{aa,aa}", "stmt_export", "duplicate_a", "aa", 18] + + warn("duplicate_a"); + } + export_dict[the_id] = the_name; + } + advance(); + the_export.expression.push(the_thing); + if (token_nxt.id === ",") { + advance(","); + } else { + break; + } + } + +// PR-439 - Check exported properties are ordered. + +// test_cause: +// ["export {bb, aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 13] + + check_ordered("export", export_list); + advance("}"); + semicolon(); + } else { + +// test_cause: +// ["export", "stmt_export", "unexpected_a", "(end)", 1] + + stop("unexpected_a"); + } + } + state.mode_module = true; + return the_export; + } + + function stmt_for() { + let first; + let the_for = token_now; + if (!option_dict.for) { + +// test_cause: +// ["for", "stmt_for", "unexpected_a", "for", 1] + + warn("unexpected_a", the_for); + } + check_not_top_level(the_for); + functionage.loop += 1; + advance("("); + token_now.free = true; + if (token_nxt.id === ";") { + +// test_cause: +// ["for(;;){}", "stmt_for", "expected_a_b", "for (;", 1] + + return stop("expected_a_b", the_for, "while (", "for (;"); + } + switch (token_nxt.id) { + case "const": + case "let": + case "var": + +// test_cause: +// ["for(const aa in aa){}", "stmt_for", "unexpected_a", "const", 5] + + return stop("unexpected_a"); + } + first = parse_expression(0); + if (first.id === "in") { + if (first.expression[0].arity !== "variable") { + +// test_cause: +// ["for(0 in aa){}", "stmt_for", "bad_assignment_a", "0", 5] + + warn("bad_assignment_a", first.expression[0]); + } + the_for.name = first.expression[0]; + the_for.expression = first.expression[1]; + warn("expected_a_b", the_for, "Object.keys", "for in"); + } else { + the_for.initial = first; + advance(";"); + the_for.expression = parse_expression(0); + advance(";"); + the_for.inc = parse_expression(0); + if (the_for.inc.id === "++") { + +// test_cause: +// ["for(aa;aa;aa++){}", "stmt_for", "expected_a_b", "++", 13] + + warn("expected_a_b", the_for.inc, "+= 1", "++"); + } + } + advance(")"); + the_for.block = block(); + if (the_for.block.disrupt === true) { + +// test_cause: +// [" +// /*jslint for*/ +// function aa(bb,cc){for(0;0;0){break;}} +// ", "stmt_for", "weird_loop", "for", 20] + + warn("weird_loop", the_for); + } + functionage.loop -= 1; + return the_for; + } + + function stmt_if() { + const the_if = token_now; + let the_else; + the_if.expression = condition(); + the_if.block = block(); + if (token_nxt.id === "else") { + advance("else"); + the_else = token_now; + the_if.else = ( + token_nxt.id === "if" + ? parse_statement() + : block() + ); + +// test_cause: +// ["if(0){0}else if(0){0}", "stmt_if", "else", "", 0] +// ["if(0){0}else{0}", "stmt_if", "else", "", 0] + + test_cause("else"); + if (the_if.block.disrupt === true) { + if (the_if.else.disrupt === true) { + +// test_cause: +// ["if(0){break;}else{break;}", "stmt_if", "disrupt", "", 0] + + test_cause("disrupt"); + the_if.disrupt = true; + } else { + +// test_cause: +// ["if(0){break;}else{}", "stmt_if", "unexpected_a", "else", 14] + + warn("unexpected_a", the_else); + } + } + } + return the_if; + } + + function stmt_import() { + const the_import = token_now; + let name; + let names; + +// PR-347 - Disable warning "unexpected_directive_a". +// +// if (typeof state.mode_module === "object") { +// +// // test_cause: +// // [" +// // /*global aa*/ +// // import aa from "aa" +// // ", "stmt_import", "unexpected_directive_a", "global", 1] +// +// warn( +// "unexpected_directive_a", +// state.mode_module, +// state.mode_module.directive +// ); +// } + + state.mode_module = true; + +// PR-436 - Add grammar for side-effect import-statement. + + if (token_nxt.id === "(string)") { + +// test_cause: +// ["import \"./aa.mjs\";", "stmt_import", "import_side_effect", "", 0] + + test_cause("import_side_effect"); + warn("expected_a_b", token_nxt, "{", artifact()); + advance(); + semicolon(); + return the_import; + } + if (token_nxt.identifier) { + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// ["import ignore from \"aa\"", "stmt_import", "unexpected_a", "ignore", 8] + + warn("unexpected_a", name); + } + enroll(name, "variable", true); + the_import.name = name; + } else { + names = []; + advance("{"); + if (token_nxt.id !== "}") { + while (true) { + if (!token_nxt.identifier) { + +// test_cause: +// ["import {", "stmt_import", "expected_identifier_a", "(end)", 1] + + stop("expected_identifier_a"); + } + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// ["import {ignore} from \"aa\"", "stmt_import", "unexpected_a", "ignore", 9] + + warn("unexpected_a", name); + } + enroll(name, "variable", true); + names.push(name); + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + the_import.name = names; + } + advance("from"); + advance("(string)"); + the_import.import = token_now; + if (!jslint_rgx_module.test(token_now.value)) { + +// test_cause: +// ["import aa from \"!aa\"", "stmt_import", "bad_module_name_a", "!aa", 16] + + warn("bad_module_name_a", token_now); + } + import_list.push(token_now.value); + semicolon(); + return the_import; + } + + function stmt_lbrace() { + +// test_cause: +// [";{}", "stmt_lbrace", "naked_block", "{", 2] +// ["class aa{}", "stmt_lbrace", "naked_block", "{", 9] + + warn("naked_block", token_now); + return block("naked"); + } + + function stmt_return() { + const the_return = token_now; + check_not_top_level(the_return); + if (functionage.finally > 0) { + +// test_cause: +// [" +// function aa(){try{}finally{return;}} +// ", "stmt_return", "unexpected_a", "return", 28] + + warn("unexpected_a", the_return); + } + the_return.disrupt = true; + if (token_nxt.id !== ";" && the_return.line === token_nxt.line) { + the_return.expression = parse_expression(10); + } + advance(";"); + return the_return; + } + + function stmt_semicolon() { + +// test_cause: +// [";", "stmt_semicolon", "unexpected_a", ";", 1] + + warn("unexpected_a", token_now); + return token_now; + } + + function stmt_switch() { + const the_cases = []; + const the_switch = token_now; + let dups = []; + let exp; + let last; + let stmts; + let the_case; + let the_default; + let the_disrupt = true; + let the_last; + function is_dup(thing) { + return is_equal(thing, exp); + } + check_not_top_level(the_switch); + if (functionage.finally > 0) { + +// test_cause: +// [" +// function aa(){try{}finally{switch(0){}}} +// ", "stmt_switch", "unexpected_a", "switch", 28] + + warn("unexpected_a", the_switch); + } + functionage.switch += 1; + advance("("); + token_now.free = true; + the_switch.expression = parse_expression(0); + the_switch.block = the_cases; + advance(")"); + advance("{"); + while (true) { + +// Loop through cases with breaks. + + the_case = token_nxt; + the_case.arity = "statement"; + the_case.expression = []; + while (true) { + +// Loop through fallthrough cases. + + advance("case"); + token_now.switch = true; + exp = parse_expression(0); + if (dups.some(is_dup)) { + +// test_cause: +// [" +// switch(0){case 0:break;case 0:break} +// ", "stmt_switch", "unexpected_a", "0", 29] + + warn("unexpected_a", exp); + } + dups.push(exp); + the_case.expression.push(exp); + advance(":"); + if (token_nxt.id !== "case") { + break; + } + } + +// test_cause: +// [" +// switch(0){case 1:case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 23] +// [" +// switch(0){case "aa":case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 26] +// [" +// switch(0){case "bb":case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 26] +// [" +// switch(0){case aa:case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24] +// [" +// switch(0){case bb:case aa:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 24] + + check_ordered_case(the_case.expression); + stmts = parse_statements(); + if (stmts.length < 1) { + +// test_cause: +// [" +// switch(0){case 0:default:} +// ", "stmt_switch", "expected_statements_a", "default", 18] +// ["switch(0){case 0:}", "stmt_switch", "expected_statements_a", "}", 18] + + warn("expected_statements_a"); + +// PR-359 - Bugfix - Fixes issue #358 - switch-statement crashes jslint. + + break; + } + the_case.block = stmts; + the_cases.push(the_case); + last = stmts[stmts.length - 1]; + if (last.disrupt) { + if (last.id === "break" && last.label === undefined) { + the_disrupt = false; + } + } else { + warn("expected_a_before_b", token_nxt, "break;", artifact()); + } + if (token_nxt.id !== "case") { + break; + } + } + +// test_cause: +// [" +// switch(0){case 1:break;case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 29] +// [" +// switch(0){case "aa":break;case 0:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "case-number", 32] +// [" +// switch(0){case "bb":break;case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 32] +// [" +// switch(0){case aa:break;case "aa":break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30] +// [" +// switch(0){case bb:break;case aa:break;} +// ", "check_ordered_case", "expected_a_b_before_c_d", "aa", 30] + + check_ordered_case(the_cases.map(function ({ + expression + }) { + return expression[0]; + })); + dups = undefined; + if (token_nxt.id === "default") { + the_default = token_nxt; + advance("default"); + token_now.switch = true; + advance(":"); + the_switch.else = parse_statements(); + if (the_switch.else.length < 1) { + +// test_cause: +// [" +// switch(0){case 0:break;default:} +// ", "stmt_switch", "unexpected_a", "default", 24] + + warn("unexpected_a", the_default); + the_disrupt = false; + } else { + the_last = the_switch.else[ + the_switch.else.length - 1 + ]; + if ( + the_last.id === "break" + && the_last.label === undefined + ) { + +// test_cause: +// [" +// switch(0){case 0:break;default:break;} +// ", "stmt_switch", "unexpected_a", "break", 32] + + warn("unexpected_a", the_last); + the_last.disrupt = false; + } + the_disrupt = the_disrupt && the_last.disrupt; + } + } else { + the_disrupt = false; + } + advance("}", the_switch); + functionage.switch -= 1; + the_switch.disrupt = the_disrupt; + return the_switch; + } + + function stmt_throw() { + const the_throw = token_now; + the_throw.disrupt = true; + the_throw.expression = parse_expression(10); + semicolon(); + if (functionage.try > 0) { + +// test_cause: +// ["try{throw 0}catch(){}", "stmt_throw", "unexpected_a", "throw", 5] + + warn("unexpected_a", the_throw); + } + return the_throw; + } + + function stmt_try() { + const the_try = token_now; + let ignored; + let the_catch; + let the_disrupt; + if (functionage.try > 0) { + +// test_cause: +// ["try{try{}catch(){}}catch(){}", "stmt_try", "unexpected_a", "try", 5] + + warn("unexpected_a", the_try); + } + functionage.try += 1; + the_try.block = block(); + the_disrupt = the_try.block.disrupt; + if (token_nxt.id === "catch") { + ignored = "ignore"; + the_catch = token_nxt; + the_try.catch = the_catch; + advance("catch"); + +// Create new catch-scope for catch-parameter. + + catch_stack.push(catchage); + catchage = the_catch; + catch_list.push(catchage); + the_catch.context = empty(); + if (token_nxt.id === "(") { + advance("("); + if (!token_nxt.identifier) { + +// test_cause: +// ["try{}catch(){}", "stmt_try", "expected_identifier_a", ")", 12] + + return stop("expected_identifier_a"); + } + if (token_nxt.id !== "ignore") { + ignored = undefined; + the_catch.name = token_nxt; + enroll(token_nxt, "exception", true); + } + advance(); + advance(")"); + } + the_catch.block = block(ignored); + if (the_catch.block.disrupt !== true) { + the_disrupt = false; + } + +// Restore previous catch-scope after catch-block. + + catchage = catch_stack.pop(); + +// PR-404 - Relax warning about missing `catch` in `try...finally` statement. +// +// } else { +// +// // test_cause: +// // ["try{}finally{break;}", "stmt_try", "expected_a_before_b", "finally", 6] +// +// warn("expected_a_before_b", token_nxt, "catch", artifact()); + + } + if (token_nxt.id === "finally") { + functionage.finally += 1; + advance("finally"); + the_try.else = block(); + the_disrupt = the_try.else.disrupt; + functionage.finally -= 1; + } + the_try.disrupt = the_disrupt; + functionage.try -= 1; + return the_try; + } + + function stmt_var() { + let ellipsis; + let mode_const; + let name; + let the_brace; + let the_bracket; + let the_variable = token_now; + let variable_prv; + mode_const = the_variable.id === "const"; + the_variable.names = []; + +// A program may use var or let, but not both. + + if (!mode_const) { + if (mode_var === undefined) { + mode_var = the_variable.id; + } else if (the_variable.id !== mode_var) { + +// test_cause: +// ["let aa;var aa", "stmt_var", "expected_a_b", "var", 8] + + warn("expected_a_b", the_variable, mode_var, the_variable.id); + } + } + +// We don't expect to see variables created in switch statements. + + if (functionage.switch > 0) { + +// test_cause: +// ["switch(0){case 0:var aa}", "stmt_var", "var_switch", "var", 18] + + warn("var_switch", the_variable); + } + switch ( + Boolean(functionage.statement_prv) + && functionage.statement_prv.id + ) { + case "const": + case "let": + case "var": + +// test_cause: +// ["const aa=0;const bb=0;", "stmt_var", "var_prv", "const", 0] +// ["let aa=0;let bb=0;", "stmt_var", "var_prv", "let", 0] +// ["var aa=0;var bb=0;", "stmt_var", "var_prv", "var", 0] + + test_cause("var_prv", functionage.statement_prv.id); + variable_prv = functionage.statement_prv; + break; + case "import": + +// test_cause: +// ["import aa from \"aa\";\nlet bb=0;", "stmt_var", "import_prv", "", 0] + + test_cause("import_prv"); + break; + case false: + break; + default: + if ( + (option_dict.beta && !option_dict.variable) + || the_variable.id === "var" + ) { + +// test_cause: +// ["console.log();let aa=0;", "stmt_var", "var_on_top", "let", 15] +// ["console.log();var aa=0;", "stmt_var", "var_on_top", "var", 15] +// ["try{aa();}catch(aa){var aa=0;}", "stmt_var", "var_on_top", "var", 21] +// ["while(0){var aa;}", "stmt_var", "var_on_top", "var", 10] + + warn("var_on_top", token_now); + } + } + while (true) { + if (token_nxt.id === "{") { + if (the_variable.id === "var") { + +// test_cause: +// ["var{aa}=0", "stmt_var", "unexpected_a", "var", 1] + + warn("unexpected_a", the_variable); + } + the_brace = token_nxt; + advance("{"); + while (true) { + name = token_nxt; + if (!name.identifier) { + +// test_cause: +// ["let {0}", "stmt_var", "expected_identifier_a", "0", 6] + + return stop("expected_identifier_a"); + } + survey(name); + advance(); + if (token_nxt.id === ":") { + advance(":"); + if (!token_nxt.identifier) { + +// test_cause: +// ["let {aa:0}", "stmt_var", "expected_identifier_a", "0", 9] +// ["let {aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 9] + + return stop("expected_identifier_a"); + } + +// PR-363 - Bugfix +// Add test against false-warning in code +// '/*jslint node*/\nlet {aa:bb} = {}; bb();'. +// +// token_nxt.label = name; +// the_variable.names.push(token_nxt); +// enroll(token_nxt, "variable", mode_const); + + name = token_nxt; + the_variable.names.push(name); + survey(name); + enroll(name, "variable", mode_const); + + advance(); + the_brace.open = true; + } else { + the_variable.names.push(name); + enroll(name, "variable", mode_const); + } + name.dead = false; + name.init = true; + if (token_nxt.id === "=") { + +// test_cause: +// ["let {aa=0}", "stmt_var", "assign", "", 0] + + test_cause("assign"); + advance("="); + name.expression = parse_expression(); + the_brace.open = true; + } + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + +// test_cause: +// ["let{bb,aa}", "check_ordered", "expected_a_b_before_c_d", "aa", 8] + + check_ordered(the_variable.id, the_variable.names); + advance("}"); + advance("="); + the_variable.expression = parse_expression(0); + } else if (token_nxt.id === "[") { + if (the_variable.id === "var") { + +// test_cause: +// ["var[aa]=0", "stmt_var", "unexpected_a", "var", 1] + + warn("unexpected_a", the_variable); + } + the_bracket = token_nxt; + advance("["); + while (true) { + ellipsis = false; + if (token_nxt.id === "...") { + ellipsis = true; + advance("..."); + } + if (!token_nxt.identifier) { + +// test_cause: +// ["let[]", "stmt_var", "expected_identifier_a", "]", 5] + + return stop("expected_identifier_a"); + } + name = token_nxt; + advance(); + the_variable.names.push(name); + enroll(name, "variable", mode_const); + name.dead = false; + name.init = true; + if (ellipsis) { + name.ellipsis = true; + break; + } + if (token_nxt.id === "=") { + advance("="); + name.expression = parse_expression(); + the_bracket.open = true; + } + if (token_nxt.id !== ",") { + break; + } + advance(","); + } + advance("]"); + advance("="); + the_variable.expression = parse_expression(0); + } else if (token_nxt.identifier) { + name = token_nxt; + advance(); + if (name.id === "ignore") { + +// test_cause: +// [" +// let ignore;function aa(ignore) {} +// ", "stmt_var", "unexpected_a", "ignore", 5] + + warn("unexpected_a", name); + } + enroll(name, "variable", mode_const); + if (token_nxt.id === "=" || mode_const) { + advance("="); + name.dead = false; + name.init = true; + name.expression = parse_expression(0); + } + the_variable.names.push(name); + } else { + +// test_cause: +// ["let 0", "stmt_var", "expected_identifier_a", "0", 5] +// ["var{aa:{aa}}", "stmt_var", "expected_identifier_a", "{", 8] + + return stop("expected_identifier_a"); + } + if (token_nxt.id !== ",") { + break; + } + +// test_cause: +// ["let aa,bb;", "stmt_var", "expected_a_b", ",", 7] + + warn("expected_a_b", token_nxt, ";", ","); + advance(","); + } + +// Warn if variable declarations are unordered. + + if ( + option_dict.beta + && !option_dict.unordered + && !option_dict.variable + && variable_prv + && ( + variable_prv.id + " " + variable_prv.names[0].id + > the_variable.id + " " + the_variable.names[0].id + ) + ) { + +// test_cause: +// ["const bb=0;const aa=0;", "stmt_var", "expected_a_b_before_c_d", "aa", 12] +// ["let bb;let aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8] +// ["var bb;var aa;", "stmt_var", "expected_a_b_before_c_d", "aa", 8] + + warn( + "expected_a_b_before_c_d", + the_variable, + the_variable.id, + the_variable.names[0].id, + variable_prv.id, + variable_prv.names[0].id + ); + } + semicolon(); + return the_variable; + } + + function stmt_while() { + const the_while = token_now; + check_not_top_level(the_while); + functionage.loop += 1; + the_while.expression = condition(); + the_while.block = block(); + if (the_while.block.disrupt === true) { + +// test_cause: +// ["function aa(){while(0){break;}}", "stmt_while", "weird_loop", "while", 15] + + warn("weird_loop", the_while); + } + functionage.loop -= 1; + return the_while; + } + + function stmt_with() { + +// test_cause: +// ["with", "stmt_with", "unexpected_a", "with", 1] + + stop("unexpected_a", token_now); + } + + function survey(name) { + let id = name.id; + +// Tally the property name. If it is a string, only tally strings that conform +// to the identifier rules. + + if (id === "(string)") { + id = name.value; + if (!jslint_rgx_identifier.test(id)) { + return id; + } + } else if (id === "`") { + if (name.value.length === 1) { + id = name.value[0].value; + if (!jslint_rgx_identifier.test(id)) { + return id; + } + } + } else if (!name.identifier) { + +// test_cause: +// ["let aa={0:0}", "survey", "expected_identifier_a", "0", 9] + + return stop("expected_identifier_a", name); + } + +// If we have seen this name before, increment its count. + + if (typeof property_dict[id] === "number") { + property_dict[id] += 1; + +// If this is the first time seeing this property name, and if there is a +// tenure list, then it must be on the list. Otherwise, it must conform to +// the rules for good property names. + + } else { + if (state.mode_property) { + if (tenure[id] !== true) { + +// test_cause: +// ["/*property aa*/\naa.bb", "survey", "unregistered_property_a", "bb", 4] + + warn("unregistered_property_a", name); + } + } else if ( + !option_dict.nomen + && name.identifier + && jslint_rgx_weird_property.test(id) + ) { + +// test_cause: +// ["aa.$", "survey", "weird_property_a", "$", 4] +// ["aa._", "survey", "weird_property_a", "_", 4] +// ["aa._aa", "survey", "weird_property_a", "_aa", 4] +// ["aa.aaSync", "survey", "weird_property_a", "aaSync", 4] +// ["aa.aa_", "survey", "weird_property_a", "aa_", 4] + + warn("weird_property_a", name); + } + property_dict[id] = 1; + } + return id; + } + +// These functions are used to specify the grammar of our language: + + function symbol(id, bp) { + +// Create a symbol if it does not already exist in the language's syntax. + + let the_symbol = syntax_dict[id]; + if (the_symbol === undefined) { + the_symbol = empty(); + the_symbol.id = id; + the_symbol.lbp = bp || 0; + syntax_dict[id] = the_symbol; + } + return the_symbol; + } + + function ternary(id1, id2) { + +// Create a ternary operator. + + const the_symbol = symbol(id1, 30); + the_symbol.led_infix = function parse_ternary_led(left) { + const the_token = token_now; + let second; + second = parse_expression(20); + advance(id2); + token_now.arity = "ternary"; + the_token.arity = "ternary"; + the_token.expression = [left, second, parse_expression(10)]; + if (token_nxt.id !== ")") { + +// test_cause: +// ["0?0:0", "parse_ternary_led", "use_open", "?", 2] + + warn("use_open", the_token); + } + return the_token; + }; + return the_symbol; + } + +// Now we parse JavaScript. +// Begin defining the language. + + assignment("%="); + assignment("&="); + assignment("*="); + assignment("+="); + assignment("-="); + assignment("/="); + assignment("<<="); + assignment("="); + assignment(">>="); + assignment(">>>="); + assignment("^="); + assignment("|="); + constant("(number)", "number"); + constant("(regexp)", "regexp"); + constant("(string)", "string"); + constant("Function", "function", constant_Function); + constant("Infinity", "number", Infinity); + constant("NaN", "number", NaN); + constant("arguments", "object", constant_arguments); + constant("eval", "function", constant_eval); + constant("false", "boolean", false); + constant("ignore", "undefined", constant_ignore); + constant("isFinite", "function", constant_isInfinite); + constant("isNaN", "function", constant_isNaN); + constant("null", "null", null); + constant("this", "object", constant_this); + constant("true", "boolean", true); + constant("undefined", "undefined"); + infix(100, "!="); + infix(100, "!=="); + infix(100, "=="); + infix(100, "==="); + infix(110, "<"); + infix(110, "<="); + infix(110, ">"); + infix(110, ">="); + infix(110, "in"); + infix(110, "instanceof"); + infix(120, "<<"); + infix(120, ">>"); + infix(120, ">>>"); + infix(130, "+"); + infix(130, "-"); + infix(140, "%"); + infix(140, "*"); + infix(140, "/"); + infix(160, "(", infix_lparen); + infix(160, "`", infix_grave); + infix(170, ".", infix_dot); + infix(170, "=>", infix_fart_unwrapped); + infix(170, "?.", infix_option_chain); + infix(170, "[", infix_lbracket); + infix(35, "??"); + infix(40, "||"); + infix(50, "&&"); + infix(70, "|"); + infix(80, "^"); + infix(90, "&"); + infixr(150, "**"); + postassign("++"); + postassign("--"); + preassign("++"); + preassign("--"); + prefix("!!"); + prefix("!"); + prefix("(", prefix_lparen); + prefix("+"); + prefix("-"); + prefix("/=", prefix_assign_divide); + prefix("=>", prefix_fart); + prefix("[", prefix_lbracket); + prefix("`", prefix_tick); + prefix("async", prefix_async); + prefix("await", prefix_await); + prefix("function", prefix_function); + prefix("new", prefix_new); + prefix("typeof"); + prefix("void", prefix_void); + prefix("{", prefix_lbrace); + prefix("~"); + stmt(";", stmt_semicolon); + stmt("async", prefix_async); + stmt("await", prefix_await); + stmt("break", stmt_break); + stmt("const", stmt_var); + stmt("continue", stmt_continue); + stmt("debugger", stmt_debugger); + stmt("delete", stmt_delete); + stmt("do", stmt_do); + stmt("export", stmt_export); + stmt("for", stmt_for); + stmt("function", prefix_function); + stmt("if", stmt_if); + stmt("import", stmt_import); + stmt("let", stmt_var); + stmt("return", stmt_return); + stmt("switch", stmt_switch); + stmt("throw", stmt_throw); + stmt("try", stmt_try); + stmt("var", stmt_var); + stmt("while", stmt_while); + stmt("with", stmt_with); + stmt("{", stmt_lbrace); + symbol(")"); + symbol("*/"); + symbol(","); + symbol(":"); + symbol(";"); + symbol("]"); + symbol("async"); + symbol("await"); + symbol("case"); + symbol("catch"); + symbol("class"); + symbol("default"); + symbol("else"); + symbol("enum"); + symbol("finally"); + symbol("implements"); + symbol("interface"); + symbol("package"); + symbol("private"); + symbol("protected"); + symbol("public"); + symbol("static"); + symbol("super"); + symbol("void"); + symbol("yield"); + symbol("}"); + ternary("?", ":"); + +// Init token_nxt. + + advance(); + +// Parsing of JSON is simple: + + if (state.mode_json) { + state.token_tree = parse_json(); + advance("(end)"); + return; + } + +// Because browsers encourage combining of script files, the first token might +// be a semicolon to defend against a missing semicolon in the preceding file. + + if (option_dict.browser) { + if (token_nxt.id === ";") { + advance(";"); + } + +// If we are not in a browser, then the file form of strict pragma may be used. + + } else if (token_nxt.value === "use strict") { + advance("(string)"); + advance(";"); + } + state.token_tree = parse_statements(); + advance("(end)"); + +// Check global functions are ordered. + + check_ordered( + "function", + function_list.map(function ({ + level, + name + }) { + return (level === 1) && name; + }).filter(function (name) { + return option_dict.beta && name && name.id; + }) + ); +} + +function jslint_phase4_walk(state) { + +// PHASE 4. Walk , traversing all nodes of the tree. It is a +// recursive traversal. Each node may be processed on the way down +// (preaction) and on the way up (postaction). + + let { + artifact, + catch_stack, + function_stack, + global_dict, + is_equal, + is_weird, + option_dict, + syntax_dict, + test_cause, + token_global, + warn + } = state; + let block_stack = []; // The stack of blocks. + let blockage = token_global; // The current block. + let catchage = catch_stack[0]; // The current catch-block. + let functionage = token_global; // The current function. + let postaction; + let postamble; + let posts = empty(); + let preaction; + let preamble; + let pres = empty(); + +// The relational operators. + + let relationop = object_assign_from_list(empty(), [ + "!=", "!==", "<", "<=", "==", "===", ">", ">=" + ], true); + +// Ambulation of the parse tree. + + function action(when) { + +// Produce a function that will register task functions that will be called as +// the tree is traversed. + + return function (arity, id, task) { + let a_set = when[arity]; + let i_set; + +// The id parameter is optional. If excluded, the task will be applied to all +// ids. + + if (typeof id !== "string") { + task = id; + id = "(all)"; + } + +// If this arity has no registrations yet, then create a set object to hold +// them. + + if (a_set === undefined) { + a_set = empty(); + when[arity] = a_set; + } + +// If this id has no registrations yet, then create a set array to hold them. + + i_set = a_set[id]; + if (i_set === undefined) { + i_set = []; + a_set[id] = i_set; + } + +// Register the task with the arity and the id. + + i_set.push(task); + }; + } + + function amble(when) { + +// Produce a function that will act on the tasks registered by an action +// function while walking the tree. + + return function (the_token) { + +// Given a task set that was built by an action function, run all +// relevant tasks on the token. + + let a_set = when[the_token.arity]; + let i_set; + +// If there are tasks associated with the token's arity... + + if (a_set !== undefined) { + +// If there are tasks associated with the token's id... + + i_set = a_set[the_token.id]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + task(the_token); + }); + } + +// If there are tasks for all ids. + + i_set = a_set["(all)"]; + if (i_set !== undefined) { + i_set.forEach(function (task) { + task(the_token); + }); + } + } + }; + } + + function init_variable(name) { + let the_variable = lookup(name); + if (!the_variable || the_variable.readonly) { + warn("bad_assignment_a", name); + return; + } + the_variable.init = true; + } + + function lookup(thing) { + let id = thing.id; + let the_variable; + if (thing.arity !== "variable") { + return; + } + +// Look up the variable in the current context. + + the_variable = functionage.context[id] || catchage.context[id]; + +// If it isn't local, search all the other contexts. If there are name +// collisions, take the most recent. + + if (the_variable && the_variable.role === "label") { + +// test_cause: +// ["aa:while(0){aa;}", "lookup", "label_a", "aa", 13] + + warn("label_a", thing); + return the_variable; + } + if (!the_variable) { + function_stack.forEach(function ({ + context + }) { + if (context[id] && context[id].role !== "label") { + the_variable = context[id]; + } + }); + +// If it isn't in any of those either, perhaps it is a predefined global. +// If so, add it to the global context. + + if (!the_variable && global_dict[id] === undefined) { + +// test_cause: +// ["aa", "lookup", "undeclared_a", "aa", 1] +// ["class aa{}", "lookup", "undeclared_a", "aa", 7] +// [" +// let aa=0;try{aa();}catch(bb){bb();}bb(); +// ", "lookup", "undeclared_a", "bb", 36] +// [" +// let aa=0;try{aa();}catch(ignore){bb();} +// ", "lookup", "undeclared_a", "bb", 34] + + warn("undeclared_a", thing); + return; + } + if (!the_variable) { + the_variable = { + dead: false, + id, + init: true, + parent: token_global, + readonly: true, + role: "variable", + used: 0 + }; + token_global.context[id] = the_variable; + } + the_variable.closure = true; + functionage.context[id] = the_variable; + } + if ( + ( + the_variable.calls === undefined + || functionage.name === undefined + || the_variable.calls[functionage.name.id] === undefined + ) + && the_variable.dead + ) { + +// test_cause: +// ["let aa;if(aa){let bb;}bb;", "lookup", "out_of_scope_a", "bb", 23] + + warn("out_of_scope_a", thing); + } + return the_variable; + } + + function post_a(thing) { + +// Assignment using = sets the init property of a variable. No other assignment +// operator can do this. A = token keeps that variable (or array of variables +// in case of destructuring) in its name property. + + const lvalue = thing.expression[0]; + let right; + if (thing.id === "=") { + if (thing.names !== undefined) { + +// test_cause: +// ["if(0){aa=0}", "post_a", "=", "", 0] + + test_cause("="); + +// Probably deadcode. +// if (Array.isArray(thing.names)) { +// thing.names.forEach(init_variable); +// } else { +// init_variable(thing.names); +// } + + jslint_assert( + !Array.isArray(thing.names), + `Expected !Array.isArray(thing.names).` + ); + init_variable(thing.names); + } else { + if (lvalue.id === "[" || lvalue.id === "{") { + lvalue.expression.forEach(function (thing) { + if (thing.variable) { + thing.variable.init = true; + } + }); + } else if ( + lvalue.id === "." + && thing.expression[1].id === "undefined" + ) { + +// test_cause: +// ["aa.aa=undefined", "post_a", "expected_a_b", "undefined", 1] + + warn( + "expected_a_b", + lvalue.expression, + "delete", + "undefined" + ); + } + } + } else { + if (lvalue.arity === "variable") { + if (!lvalue.variable || lvalue.variable.readonly) { + warn("bad_assignment_a", lvalue); + } + } + right = syntax_dict[thing.expression[1].id]; + if ( + right !== undefined + && ( + right.id === "function" + || right.id === "=>" + || ( + right.constant + && right.id !== "(number)" + && (right.id !== "(string)" || thing.id !== "+=") + ) + ) + ) { + +// test_cause: +// ["aa+=undefined", "post_a", "unexpected_a", "undefined", 5] + + warn("unexpected_a", thing.expression[1]); + } + } + } + + function post_a_pluseq(thing) { + const right = thing.expression[1]; + if (right.constant) { + if ( + right.value === "" + || (right.id === "(number)" && right.value === "0") + || right.id === "(boolean)" + || right.id === "null" + || right.id === "undefined" + || Number.isNaN(right.value) + ) { + warn("unexpected_a", right); + } + } + } + + function post_b(thing) { + let right; + if (relationop[thing.id]) { + if ( + is_weird(thing.expression[0]) + || is_weird(thing.expression[1]) + || is_equal(thing.expression[0], thing.expression[1]) + || ( + thing.expression[0].constant === true + && thing.expression[1].constant === true + ) + ) { + +// test_cause: +// ["if(0===0){0}", "post_b", "weird_relation_a", "===", 5] + + warn("weird_relation_a", thing); + } + } + if (thing.id === "+") { + if (!option_dict.convert) { + if (thing.expression[0].value === "") { + +// test_cause: +// ["\"\"+0", "post_b", "expected_a_b", "\"\" +", 3] + + warn("expected_a_b", thing, "String(...)", "\"\" +"); + } else if (thing.expression[1].value === "") { + +// test_cause: +// ["0+\"\"", "post_b", "expected_a_b", "+ \"\"", 2] + + warn("expected_a_b", thing, "String(...)", "+ \"\""); + } + } + } else if (thing.id === "[") { + if (thing.expression[0].id === "window") { + +// test_cause: +// ["aa=window[0]", "post_b", "weird_expression_a", "window[...]", 10] + + warn("weird_expression_a", thing, "window[...]"); + } + if (thing.expression[0].id === "self") { + +// test_cause: +// ["aa=self[0]", "post_b", "weird_expression_a", "self[...]", 8] + + warn("weird_expression_a", thing, "self[...]"); + } + } else if (thing.id === "." || thing.id === "?.") { + if (thing.expression.id === "RegExp") { + +// test_cause: +// ["aa=RegExp.aa", "post_b", "weird_expression_a", ".", 10] + + warn("weird_expression_a", thing); + } + } else if (thing.id !== "=>" && thing.id !== "(") { + right = thing.expression[1]; + if ( + (thing.id === "+" || thing.id === "-") + && right.id === thing.id + && right.arity === "unary" + && !right.wrapped + ) { + +// test_cause: +// ["0- -0", "post_b", "wrap_unary", "-", 4] + + warn("wrap_unary", right); + } + if ( + thing.expression[0].constant === true + && right.constant === true + ) { + thing.constant = true; + } + } + } + + function post_b_and(thing) { + if ( + is_weird(thing.expression[0]) + || is_equal(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + || thing.expression[1].constant === true + ) { + +// test_cause: +// ["aa=(()=>0)&&(()=>0)", "post_b_and", "weird_condition_a", "&&", 11] +// ["aa=(``?``:``)&&(``?``:``)", "post_b_and", "weird_condition_a", "&&", 14] +// ["aa=/./&&/./", "post_b_and", "weird_condition_a", "&&", 7] +// ["aa=0&&0", "post_b_and", "weird_condition_a", "&&", 5] +// ["aa=[]&&[]", "post_b_and", "weird_condition_a", "&&", 6] +// ["aa=`${0}`&&`${0}`", "post_b_and", "weird_condition_a", "&&", 10] +// [" +// aa=function aa(){}&&function aa(){} +// ", "post_b_and", "weird_condition_a", "&&", 19] +// ["aa={}&&{}", "post_b_and", "weird_condition_a", "&&", 6] + + warn("weird_condition_a", thing); + } + } + + function post_b_lbracket(thing) { + if (thing.expression[0].id === "RegExp") { + +// test_cause: +// ["aa=RegExp[0]", "post_b_lbracket", "weird_expression_a", "[", 10] + + warn("weird_expression_a", thing); + } + if (is_weird(thing.expression[1])) { + +// test_cause: +// ["aa[[0]]", "post_b_lbracket", "weird_expression_a", "[", 4] + + warn("weird_expression_a", thing.expression[1]); + } + } + + function post_b_lparen(thing) { + let arg; + let array; + let cack; + let left = thing.expression[0]; + let new_date; + let paren; + let the_new; + if (left.id === "new") { + the_new = left; + left = left.expression; + } + if (left.id === "function") { + if (!thing.wrapped) { + +// test_cause: +// ["aa=function(){}()", "post_b_lparen", "wrap_immediate", "(", 16] + + warn("wrap_immediate", thing); + } + } else if (left.identifier) { + if (the_new !== undefined) { + if ( + left.id[0] > "Z" + || left.id === "BigInt" + || left.id === "Boolean" + || left.id === "Number" + || left.id === "String" + || left.id === "Symbol" + ) { + +// test_cause: +// ["new BigInt()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Boolean()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Number()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new String()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new Symbol()", "post_b_lparen", "unexpected_a", "new", 1] +// ["new aa()", "post_b_lparen", "unexpected_a", "new", 1] + + warn("unexpected_a", the_new); + } else if (left.id === "Function") { + if (!option_dict.eval) { + +// test_cause: +// ["new Function()", "post_b_lparen", "unexpected_a", "new Function", 5] + + warn("unexpected_a", left, "new Function"); + } + } else if (left.id === "Array") { + arg = thing.expression; + if (arg.length !== 2 || arg[1].id === "(string)") { + +// test_cause: +// ["new Array()", "post_b_lparen", "expected_a_b", "new Array", 5] + + warn("expected_a_b", left, "[]", "new Array"); + } + } else if (left.id === "Object") { + +// test_cause: +// ["new Object()", "post_b_lparen", "expected_a_b", "new Object", 5] + + warn( + "expected_a_b", + left, + "Object.create(null)", + "new Object" + ); + } + } else { + if ( + left.id[0] >= "A" + && left.id[0] <= "Z" + && left.id !== "BigInt" + && left.id !== "Boolean" + && left.id !== "Number" + && left.id !== "String" + && left.id !== "Symbol" + ) { + +// test_cause: +// ["let Aa=Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8] + + warn("expected_a_before_b", left, "new", artifact(left)); + } + } + } else if (left.id === ".") { + cack = the_new !== undefined; + if (left.expression.id === "Date" && left.name.id === "UTC") { + +// test_cause: +// ["new Date.UTC()", "post_b_lparen", "cack", "", 0] + + test_cause("cack"); + cack = !cack; + } + if (jslint_rgx_cap.test(left.name.id) !== cack) { + if (the_new !== undefined) { + +// test_cause: +// ["new Date.UTC()", "post_b_lparen", "unexpected_a", "new", 1] + + warn("unexpected_a", the_new); + } else { + +// test_cause: +// ["let Aa=Aa.Aa()", "post_b_lparen", "expected_a_before_b", "Aa", 8] + + warn( + "expected_a_before_b", + left.expression, + "new", + left.name.id + ); + } + } + if (left.name.id === "getTime") { + paren = left.expression; + if (paren.id === "(") { + array = paren.expression; + if (array.length === 1) { + new_date = array[0]; + if ( + new_date.id === "new" + && new_date.expression.id === "Date" + ) { + +// test_cause: +// [" +// new Date().getTime() +// ", "post_b_lparen", "expected_a_b", "new Date().getTime()", 1] + + warn( + "expected_a_b", + new_date, + "Date.now()", + "new Date().getTime()" + ); + } + } + } + } + } + } + + function post_b_or(thing) { + if ( + is_weird(thing.expression[0]) + || is_equal(thing.expression[0], thing.expression[1]) + || thing.expression[0].constant === true + ) { + +// test_cause: +// ["aa=0||0", "post_b_or", "weird_condition_a", "||", 5] + + warn("weird_condition_a", thing); + } + } + + function post_s_export(the_thing) { + +// Some features must be at the most outermost level. + + if (blockage !== token_global) { + +// test_cause: +// [" +// if(0){import aa from "aa";} +// ", "post_s_export", "misplaced_a", "import", 7] + + warn("misplaced_a", the_thing); + } + } + + function post_s_for(thing) { + +// Recurse walk_statement(). + + walk_statement(thing.inc); + } + + function post_s_function(thing) { + delete functionage.async; + delete functionage.finally; + delete functionage.loop; + delete functionage.statement_prv; + delete functionage.switch; + delete functionage.try; + functionage = function_stack.pop(); + if (thing.wrapped) { + +// test_cause: +// ["aa=(function(){})", "post_s_function", "unexpected_parens", "function", 5] + + warn("unexpected_parens", thing); + } + return post_s_lbrace(); + } + + function post_s_import(the_thing) { + const name = the_thing.name; + if (name) { + if (Array.isArray(name)) { + name.forEach(function (name) { + name.dead = false; + name.init = true; + blockage.live.push(name); + }); + } else { + name.dead = false; + name.init = true; + blockage.live.push(name); + } + return post_s_export(the_thing); + } + } + + function post_s_lbrace() { + blockage.live.forEach(function (name) { + name.dead = true; + }); + delete blockage.live; + blockage = block_stack.pop(); + } + + function post_s_try(thing) { + if (thing.catch) { + if (thing.catch.name) { + Object.assign(catchage.context[thing.catch.name.id], { + dead: false, + init: true + }); + } + +// Recurse walk_statement(). + + walk_statement(thing.catch.block); + +// Restore previous catch-scope after catch-block. + + catchage = catch_stack.pop(); + } + } + + function post_s_var(thing) { + thing.names.forEach(function (name) { + name.dead = false; + if (name.expression !== undefined) { + walk_expression(name.expression); + +// Probably deadcode. +// if (name.id === "{" || name.id === "[") { +// name.names.forEach(subactivate); +// } else { +// name.init = true; +// } + + jslint_assert( + !(name.id === "{" || name.id === "["), + `Expected !(name.id === "{" || name.id === "[").` + ); + name.init = true; + } + blockage.live.push(name); + }); + } + + function post_t(thing) { + if ( + is_weird(thing.expression[0]) + || thing.expression[0].constant === true + || is_equal(thing.expression[1], thing.expression[2]) + ) { + +// test_cause: +// ["let aa=(aa?`${0}`:`${0}`);", "post_t", "unexpected_a", "?", 11] +// ["let aa=(aa?`0`:`0`);", "post_t", "unexpected_a", "?", 11] + + warn("unexpected_a", thing); + } else if (is_equal(thing.expression[0], thing.expression[1])) { + +// test_cause: +// ["aa?aa:0", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "||", "?"); + } else if (is_equal(thing.expression[0], thing.expression[2])) { + +// test_cause: +// ["aa?0:aa", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "&&", "?"); + } else if ( + thing.expression[1].id === "true" + && thing.expression[2].id === "false" + ) { + +// test_cause: +// ["aa?true:false", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "!!", "?"); + } else if ( + thing.expression[1].id === "false" + && thing.expression[2].id === "true" + ) { + +// test_cause: +// ["aa?false:true", "post_t", "expected_a_b", "?", 3] + + warn("expected_a_b", thing, "!", "?"); + } else if ( + thing.expression[0].wrapped !== true + && ( + thing.expression[0].id === "||" + || thing.expression[0].id === "&&" + ) + ) { + +// test_cause: +// ["(aa&&!aa?0:1)", "post_t", "wrap_condition", "&&", 4] + + warn("wrap_condition", thing.expression[0]); + } + } + + function post_u(thing) { + if (thing.id === "`") { + if (thing.expression.every(function (thing) { + return thing.constant; + })) { + thing.constant = true; + } + } else if (thing.id === "!") { + if (thing.expression.constant === true) { + warn("unexpected_a", thing); + } + } else if (thing.id === "!!") { + if (!option_dict.convert) { + +// test_cause: +// ["!!0", "post_u", "expected_a_b", "!!", 1] + + warn("expected_a_b", thing, "Boolean(...)", "!!"); + } + } else if ( + thing.id !== "[" + && thing.id !== "{" + && thing.id !== "function" + && thing.id !== "new" + ) { + if (thing.expression.constant === true) { + thing.constant = true; + } + } + } + + function post_u_plus(thing) { + const right = thing.expression; + if (!option_dict.convert) { + +// test_cause: +// ["aa=+0", "post_u_plus", "expected_a_b", "+", 4] + + warn("expected_a_b", thing, "Number(...)", "+"); + } + if (right.id === "(" && right.expression[0].id === "new") { + warn("unexpected_a_before_b", thing, "+", "new"); + } else if ( + right.constant + || right.id === "{" + || (right.id === "[" && right.arity !== "binary") + ) { + warn("unexpected_a", thing, "+"); + } + } + + function pre_a_bitwise(thing) { + +// These are the bitwise operators. + + switch (!option_dict.bitwise && thing.id) { + case "&": + case "&=": + case "<<": + case "<<=": + case ">>": + case ">>=": + case ">>>": + case ">>>=": + case "^": + case "^=": + case "|": + case "|=": + case "~": + +// test_cause: +// ["0&0", "pre_a_bitwise", "unexpected_a", "&", 2] +// ["0&=0", "pre_a_bitwise", "unexpected_a", "&=", 2] +// ["0<<0", "pre_a_bitwise", "unexpected_a", "<<", 2] +// ["0<<=0", "pre_a_bitwise", "unexpected_a", "<<=", 2] +// ["0>>0", "pre_a_bitwise", "unexpected_a", ">>", 2] +// ["0>>=0", "pre_a_bitwise", "unexpected_a", ">>=", 2] +// ["0>>>0", "pre_a_bitwise", "unexpected_a", ">>>", 2] +// ["0>>>=0", "pre_a_bitwise", "unexpected_a", ">>>=", 2] +// ["0^0", "pre_a_bitwise", "unexpected_a", "^", 2] +// ["0^=0", "pre_a_bitwise", "unexpected_a", "^=", 2] +// ["0|0", "pre_a_bitwise", "unexpected_a", "|", 2] +// ["0|=0", "pre_a_bitwise", "unexpected_a", "|=", 2] +// ["~0", "pre_a_bitwise", "unexpected_a", "~", 1] + + warn("unexpected_a", thing); + break; + } + if ( + thing.id !== "(" + && thing.id !== "&&" + && thing.id !== "||" + && thing.id !== "=" + && Array.isArray(thing.expression) + && thing.expression.length === 2 + && ( + relationop[thing.expression[0].id] === true + || relationop[thing.expression[1].id] === true + ) + ) { + +// test_cause: +// ["0<0<0", "pre_a_bitwise", "unexpected_a", "<", 4] + + warn("unexpected_a", thing); + } + } + + function pre_b(thing) { + let left; + let right; + let value; + if (relationop[thing.id] === true) { + left = thing.expression[0]; + right = thing.expression[1]; + if (left.id === "NaN" || right.id === "NaN") { + +// test_cause: +// ["NaN===NaN", "pre_b", "number_isNaN", "===", 4] + + warn("number_isNaN", thing); + } else if (left.id === "typeof") { + if (right.id !== "(string)") { + if (right.id !== "typeof") { + +// test_cause: +// ["typeof 0===0", "pre_b", "expected_string_a", "0", 12] + + warn("expected_string_a", right); + } + } else { + value = right.value; + if (value === "null" || value === "undefined") { + +// test_cause: +// [" +// typeof aa==="undefined" +// ", "pre_b", "unexpected_typeof_a", "undefined", 13] + + warn("unexpected_typeof_a", right, value); + } else if ( + value !== "bigint" + && value !== "boolean" + && value !== "function" + && value !== "number" + && value !== "object" + && value !== "string" + && value !== "symbol" + ) { + +// test_cause: +// ["typeof 0===\"aa\"", "pre_b", "expected_type_string_a", "aa", 12] + + warn("expected_type_string_a", right, value); + } + } + } + } + } + + function pre_b_eqeq(thing) { + +// test_cause: +// ["0==0", "pre_b_eqeq", "expected_a_b", "==", 2] + + warn("expected_a_b", thing, "===", "=="); + } + + function pre_b_in(thing) { + +// test_cause: +// ["aa in aa", "pre_b_in", "infix_in", "in", 4] + + warn("infix_in", thing); + } + + function pre_b_instanceof(thing) { + +// test_cause: +// ["0 instanceof 0", "pre_b_instanceof", "unexpected_a", "instanceof", 3] + + warn("unexpected_a", thing); + } + + function pre_b_lparen(thing) { + const left = thing.expression[0]; + let left_variable; + let parent; + if ( + left.identifier + && functionage.context[left.id] === undefined + && typeof functionage.name === "object" + ) { + parent = functionage.name.parent; + if (parent) { + left_variable = parent.context[left.id]; + if ( + left_variable !== undefined + +// Probably deadcode. +// && left_variable.dead + + && left_variable.parent === parent + && left_variable.calls !== undefined + && left_variable.calls[functionage.name.id] !== undefined + ) { + left_variable.dead = false; + } + } + } + } + + function pre_b_noteq(thing) { + +// test_cause: +// ["0!=0", "pre_b_noteq", "expected_a_b", "!=", 2] + + warn("expected_a_b", thing, "!==", "!="); + } + + function pre_b_or(thing) { + thing.expression.forEach(function (thang) { + if (thang.id === "&&" && !thang.wrapped) { + +// test_cause: +// ["0&&0||0", "pre_b_or", "and", "&&", 2] + + warn("and", thang); + } + }); + } + + function pre_s_for(thing) { + let the_variable; + if (thing.name !== undefined) { + thing.name.dead = false; + the_variable = lookup(thing.name); + if (the_variable !== undefined) { + if (the_variable.init && the_variable.readonly) { + +// test_cause: +// ["const aa=0;for(aa in aa){}", "pre_s_for", "bad_assignment_a", "aa", 16] + + warn("bad_assignment_a", thing.name); + } + the_variable.init = true; + } + } + +// Recurse walk_statement(). + + walk_statement(thing.initial); + } + + function pre_s_function(thing) { + +// test_cause: +// ["()=>0", "pre_s_function", "", "", 0] +// ["(function (){}())", "pre_s_function", "", "", 0] +// ["function aa(){}", "pre_s_function", "", "", 0] + + test_cause(""); + if (thing.arity === "statement" && blockage.body !== true) { + +// test_cause: +// ["if(0){function aa(){}\n}", "pre_s_function", "unexpected_a", "function", 7] + + warn("unexpected_a", thing); + } + function_stack.push(functionage); + block_stack.push(blockage); + functionage = thing; + blockage = thing; + thing.live = []; + if (typeof thing.name === "object") { + thing.name.dead = false; + thing.name.init = true; + } + if (thing.extra === "get") { + if (thing.parameters.length !== 0) { + +// test_cause: +// [" +// /*jslint getset*/ +// aa={get aa(aa){}} +// ", "pre_s_function", "bad_get", "function", 9] + + warn("bad_get", thing); + } + } else if (thing.extra === "set") { + if (thing.parameters.length !== 1) { + +// test_cause: +// [" +// /*jslint getset*/ +// aa={set aa(){}} +// ", "pre_s_function", "bad_set", "function", 9] + + warn("bad_set", thing); + } + } + thing.parameters.forEach(function (name) { + walk_expression(name.expression); + if (name.id === "{" || name.id === "[") { + name.names.forEach(subactivate); + } else { + name.dead = false; + name.init = true; + } + }); + } + + function pre_s_lbrace(thing) { + block_stack.push(blockage); + blockage = thing; + thing.live = []; + } + + function pre_try(thing) { + if (thing.catch !== undefined) { + +// Create new catch-scope for catch-parameter. + + catch_stack.push(catchage); + catchage = thing.catch; + } + } + + function pre_v(thing) { + const the_variable = lookup(thing); + if (the_variable !== undefined) { + thing.variable = the_variable; + the_variable.used += 1; + } + } + + function subactivate(name) { + name.init = true; + name.dead = false; + blockage.live.push(name); + } + + function walk_expression(thing) { + if (thing) { + if (Array.isArray(thing)) { + +// test_cause: +// ["(function(){}())", "walk_expression", "isArray", "", 0] +// ["0&&0", "walk_expression", "isArray", "", 0] + + test_cause("isArray"); + thing.forEach(walk_expression); + } else { + preamble(thing); + walk_expression(thing.expression); + +// PR-414 - Bugfix - fix fart-body not being walked. + + if (thing.id === "function" || thing.id === "=>") { + +// test_cause: +// ["aa=()=>0", "walk_expression", "function", "=>", 0] +// ["aa=function(){}", "walk_expression", "function", "function", 0] + + test_cause("function", thing.id); + +// Recurse walk_statement(). + + walk_statement(thing.block); + } + if ( + thing.arity === "preassign" || thing.arity === "postassign" + ) { + +// test_cause: +// ["aa=++aa", "walk_expression", "unexpected_a", "++", 4] +// ["aa=--aa", "walk_expression", "unexpected_a", "--", 4] + + warn("unexpected_a", thing); + } else if ( + thing.arity === "statement" + || thing.arity === "assignment" + ) { + +// test_cause: +// ["aa[aa=0]", "walk_expression", "unexpected_statement_a", "=", 6] + + warn("unexpected_statement_a", thing); + } + +// test_cause: +// ["aa=0", "walk_expression", "default", "", 0] + + test_cause("default"); + postamble(thing); + } + } + } + + function walk_statement(thing) { + if (!thing) { + return; + } + if (Array.isArray(thing)) { + +// test_cause: +// ["+[]", "walk_statement", "isArray", "", 0] + + test_cause("isArray"); + +// Recurse walk_statement(). + + thing.forEach(walk_statement); + return; + } + preamble(thing); + walk_expression(thing.expression); + if (thing.arity === "binary") { + if (thing.id !== "(") { + +// test_cause: +// ["0&&0", "walk_statement", "unexpected_expression_a", "&&", 2] + + warn("unexpected_expression_a", thing); + } + } else if ( + thing.arity !== "statement" + && thing.arity !== "assignment" + && thing.id !== "import" + ) { + +// test_cause: +// ["!0", "walk_statement", "unexpected_expression_a", "!", 1] +// ["+[]", "walk_statement", "unexpected_expression_a", "+", 1] +// ["+new aa()", "walk_statement", "unexpected_expression_a", "+", 1] +// ["0", "walk_statement", "unexpected_expression_a", "0", 1] +// ["typeof 0", "walk_statement", "unexpected_expression_a", "typeof", 1] + + warn("unexpected_expression_a", thing); + } + +// Recurse walk_statement(). + + walk_statement(thing.block); + walk_statement(thing.else); + postamble(thing); + } + + postaction = action(posts); + postamble = amble(posts); + preaction = action(pres); + preamble = amble(pres); + postaction("assignment", "+=", post_a_pluseq); + postaction("assignment", post_a); + postaction("binary", "&&", post_b_and); + postaction("binary", "(", post_b_lparen); + postaction("binary", "=>", post_s_function); + postaction("binary", "[", post_b_lbracket); + postaction("binary", "||", post_b_or); + postaction("binary", post_b); + postaction("statement", "const", post_s_var); + postaction("statement", "export", post_s_export); + postaction("statement", "for", post_s_for); + postaction("statement", "function", post_s_function); + postaction("statement", "import", post_s_import); + postaction("statement", "let", post_s_var); + postaction("statement", "try", post_s_try); + postaction("statement", "var", post_s_var); + postaction("statement", "{", post_s_lbrace); + postaction("ternary", post_t); + postaction("unary", "+", post_u_plus); + postaction("unary", "function", post_s_function); + postaction("unary", post_u); + preaction("assignment", pre_a_bitwise); + preaction("binary", "!=", pre_b_noteq); + preaction("binary", "(", pre_b_lparen); + preaction("binary", "==", pre_b_eqeq); + preaction("binary", "=>", pre_s_function); + preaction("binary", "in", pre_b_in); + preaction("binary", "instanceof", pre_b_instanceof); + preaction("binary", "||", pre_b_or); + preaction("binary", pre_b); + preaction("binary", pre_a_bitwise); + preaction("statement", "for", pre_s_for); + preaction("statement", "function", pre_s_function); + preaction("statement", "try", pre_try); + preaction("statement", "{", pre_s_lbrace); + preaction("unary", "function", pre_s_function); + preaction("unary", "~", pre_a_bitwise); + preaction("variable", pre_v); + + walk_statement(state.token_tree); +} + +function jslint_phase5_whitage(state) { + +// PHASE 5. Check whitespace between tokens in . + + let { + artifact, + catch_list, + function_list, + function_stack, + option_dict, + test_cause, + token_global, + token_list, + warn + } = state; + let closer = "(end)"; + let free = false; + +// free = false + +// cause: +// "()=>0" +// "aa()" +// "aa(0,0)" +// "function(){}" + +// free = true + +// cause: +// "(0)" +// "(aa)" +// "aa(0)" +// "do{}while()" +// "for(){}" +// "if(){}" +// "switch(){}" +// "while(){}" + + let left = token_global; + let margin = 0; + let mode_indent = ( + +// PR-330 - Allow 2-space indent. + + option_dict.indent2 + ? 2 + : 4 + ); + let nr_comments_skipped = 0; + let open = true; + let opening = true; + let right; + +// This is the set of infix operators that require a space on each side. + + let spaceop = object_assign_from_list(empty(), [ + "!=", "!==", "%", "%=", "&", "&&", "&=", "*", "*=", "+=", "-=", "/", + "/=", "<", "<<", "<<=", "<=", "=", "==", "===", "=>", ">", ">=", ">>", + ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||" + ], true); + + function at_margin(fit) { + const at = margin + fit; + if (right.from !== at) { + return expected_at(at); + } + } + + function delve(the_function) { + Object.keys(the_function.context).forEach(function (id) { + const name = the_function.context[id]; + if (id !== "ignore" && name.parent === the_function) { + +// test_cause: +// ["function aa(aa) {return aa;}", "delve", "id", "", 0] + + test_cause("id"); + if ( + name.used === 0 + +// Probably deadcode. +// && ( +// name.role !== "function" +// || name.parent.arity !== "unary" +// ) + + && jslint_assert( + name.role !== "function", + `Expected name.role !== "function".` + ) + ) { + +// test_cause: +// ["/*jslint node*/\nlet aa;", "delve", "unused_a", "aa", 5] +// ["function aa(aa){return;}", "delve", "unused_a", "aa", 13] +// ["let aa=0;try{aa();}catch(bb){aa();}", "delve", "unused_a", "bb", 26] + + warn("unused_a", name); + } else if (!name.init) { + +// test_cause: +// ["/*jslint node*/\nlet aa;aa();", "delve", "uninitialized_a", "aa", 5] + + warn("uninitialized_a", name); + } + } + }); + } + + function expected_at(at) { + +// Probably deadcode. +// if (right === undefined) { +// right = token_nxt; +// } + + jslint_assert( + !(right === undefined), + `Expected !(right === undefined).` + ); + warn( + "expected_a_at_b_c", + right, + artifact(right), + +// Fudge column numbers in warning message. + + at + jslint_fudge, + right.from + jslint_fudge + ); + } + + function no_space() { + if (left.line === right.line) { + +// from: +// if (left.line === right.line) { +// no_space(); +// } else { + + if (left.thru !== right.from && nr_comments_skipped === 0) { + +// test_cause: +// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14] + + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + +// from: +// } else if ( +// right.arity === "binary" +// && right.id === "(" +// && free +// ) { +// no_space(); +// } else if ( + +// Probably deadcode. +// if (open) { +// const at = ( +// free +// ? margin +// : margin + 8 +// ); +// if (right.from < at) { +// expected_at(at); +// } +// } else { +// if (right.from !== margin + 8) { +// expected_at(margin + 8); +// } +// } + + jslint_assert(open, `Expected open.`); + jslint_assert(free, `Expected free.`); + if (right.from < margin) { + +// test_cause: +// ["let aa = aa(\naa\n()\n);", "expected_at", "expected_a_at_b_c", "5", 1] + + expected_at(margin); + } + } + } + + function no_space_only() { + if ( + left.id !== "(global)" + && left.nr + 1 === right.nr + && ( + left.line !== right.line + || left.thru !== right.from + ) + ) { + warn( + "unexpected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } + + function one_space() { + if (left.line === right.line || !open) { + if (left.thru + 1 !== right.from && nr_comments_skipped === 0) { + warn( + "expected_space_a_b", + right, + artifact(left), + artifact(right) + ); + } + } else { + if (right.from !== margin) { + expected_at(margin); + } + } + } + + function one_space_only() { + if (left.line !== right.line || left.thru + 1 !== right.from) { + warn("expected_space_a_b", right, artifact(left), artifact(right)); + } + } + + function pop() { + const previous = function_stack.pop(); + closer = previous.closer; + free = previous.free; + margin = previous.margin; + open = previous.open; + opening = previous.opening; + } + + function push() { + function_stack.push({ + closer, + free, + margin, + open, + opening + }); + } + +// uninitialized_and_unused(); +// Delve into the functions looking for variables that were not initialized +// or used. If the file imports or exports, then its global object is also +// delved. + + if (state.mode_module === true || option_dict.node) { + delve(token_global); + } + catch_list.forEach(delve); + function_list.forEach(delve); + + if (option_dict.white) { + return; + } + +// whitage(); +// Go through the token list, looking at usage of whitespace. + + token_list.forEach(function whitage(the_token) { + right = the_token; + if (right.id === "(comment)" || right.id === "(end)") { + nr_comments_skipped += 1; + } else { + +// If left is an opener and right is not the closer, then push the previous +// state. If the token following the opener is on the next line, then this is +// an open form. If the tokens are on the same line, then it is a closed form. +// Open form is more readable, with each item (statement, argument, parameter, +// etc) starting on its own line. Closed form is more compact. Statement blocks +// are always in open form. + +// The open and close pairs. + + switch (left.id) { + case "${": + case "(": + case "[": + case "{": + +// test_cause: +// ["let aa=[];", "whitage", "opener", "", 0] +// ["let aa=`${0}`;", "whitage", "opener", "", 0] +// ["let aa=aa();", "whitage", "opener", "", 0] +// ["let aa={};", "whitage", "opener", "", 0] + + test_cause("opener"); + +// Probably deadcode. +// case "${}": + + jslint_assert( + !(left.id + right.id === "${}"), + "Expected !(left.id + right.id === \"${}\")." + ); + switch (left.id + right.id) { + case "()": + case "[]": + case "{}": + +// If left and right are opener and closer, then the placement of right depends +// on the openness. Illegal pairs (like '{]') have already been detected. + +// test_cause: +// ["let aa=[];", "whitage", "opener_closer", "", 0] +// ["let aa=aa();", "whitage", "opener_closer", "", 0] +// ["let aa={};", "whitage", "opener_closer", "", 0] + + test_cause("opener_closer"); + if (left.line === right.line) { + +// test_cause: +// ["let aa = aa( );", "no_space", "unexpected_space_a_b", ")", 14] + + no_space(); + } else { + +// test_cause: +// ["let aa = aa(\n );", "expected_at", "expected_a_at_b_c", "1", 2] + + at_margin(0); + } + break; + default: + +// test_cause: +// ["let aa=(0);", "whitage", "opener_operand", "", 0] +// ["let aa=[0];", "whitage", "opener_operand", "", 0] +// ["let aa=`${0}`;", "whitage", "opener_operand", "", 0] +// ["let aa=aa(0);", "whitage", "opener_operand", "", 0] +// ["let aa={aa:0};", "whitage", "opener_operand", "", 0] + + test_cause("opener_operand"); + opening = left.open || (left.line !== right.line); + push(); + switch (left.id) { + case "${": + closer = "}"; + break; + case "(": + closer = ")"; + break; + case "[": + closer = "]"; + break; + case "{": + closer = "}"; + break; + } + if (opening) { + +// test_cause: +// ["function aa(){\nreturn;\n}", "whitage", "opening", "", 0] +// ["let aa=(\n0\n);", "whitage", "opening", "", 0] +// ["let aa=[\n0\n];", "whitage", "opening", "", 0] +// ["let aa=`${\n0\n}`;", "whitage", "opening", "", 0] +// ["let aa={\naa:0\n};", "whitage", "opening", "", 0] + + test_cause("opening"); + free = closer === ")" && left.free; + open = true; + margin += mode_indent; + if (right.role === "label") { + if (right.from !== 0) { + +// test_cause: +// [" +// function aa() { +// bb: +// while (aa) { +// if (aa) { +// break bb; +// } +// } +// } +// ", "expected_at", "expected_a_at_b_c", "1", 2] + + expected_at(0); + } + } else if (right.switch) { + at_margin(-mode_indent); + } else { + at_margin(0); + } + } else { + if (right.statement || right.role === "label") { + +// test_cause: +// [" +// function aa() {bb: +// while (aa) { +// aa(); +// } +// } +// ", "whitage", "expected_line_break_a_b", "bb", 16] + + warn( + "expected_line_break_a_b", + right, + artifact(left), + artifact(right) + ); + } + +// test_cause: +// ["let aa=(0);", "whitage", "not_free", "", 0] +// ["let aa=[0];", "whitage", "not_free", "", 0] +// ["let aa=`${0}`;", "whitage", "not_free", "", 0] +// ["let aa={aa:0};", "whitage", "not_free", "", 0] + + test_cause("not_free"); + free = false; + open = false; + +// test_cause: +// ["let aa = ( 0 );", "no_space_only", "unexpected_space_a_b", "0", 12] + + no_space_only(); + } + } + break; + default: + if (right.statement === true) { + if (left.id === "else") { + +// test_cause: +// [" +// let aa = 0; +// if (aa) { +// aa(); +// } else if (aa) { +// aa(); +// } +// ", "one_space_only", "expected_space_a_b", "if", 9] + + one_space_only(); + } else { + +// test_cause: +// [" let aa = 0;", "expected_at", "expected_a_at_b_c", "1", 2] + + at_margin(0); + open = false; + } + +// If right is a closer, then pop the previous state. + + } else if (right.id === closer) { + pop(); + if (opening && right.id !== ";") { + at_margin(0); + } else { + no_space_only(); + } + } else { + +// Left is not an opener, and right is not a closer. +// The nature of left and right will determine the space between them. + +// If left is ',' or ';' or right is a statement then if open, +// right must go at the margin, or if closed, a space between. + + if (right.switch) { + at_margin(-mode_indent); + } else if (right.role === "label") { + if (right.from !== 0) { + +// test_cause: +// [" +// function aa() { +// aa();cc: +// while (aa) { +// if (aa) { +// break cc; +// } +// } +// } +// ", "expected_at", "expected_a_at_b_c", "1", 10] + + expected_at(0); + } + } else if (left.id === ",") { + if (!open || ( + (free || closer === "]") + && left.line === right.line + )) { + +// test_cause: +// ["let {aa,bb} = 0;", "one_space", "expected_space_a_b", "bb", 9] + + one_space(); + } else { + +// test_cause: +// [" +// function aa() { +// aa( +// 0,0 +// ); +// } +// ", "expected_at", "expected_a_at_b_c", "9", 11] + + at_margin(0); + } + +// If right is a ternary operator, line it up on the margin. + + } else if (right.arity === "ternary") { + if (open) { + +// test_cause: +// [" +// let aa = ( +// aa +// ? 0 +// : 1 +// ); +// ", "expected_at", "expected_a_at_b_c", "5", 1] + + at_margin(0); + } else { + +// test_cause: +// ["let aa = (aa ? 0 : 1);", "whitage", "use_open", "?", 14] + + warn("use_open", right); + } + } else if ( + right.arity === "binary" + && right.id === "(" + && free + ) { + +// test_cause: +// ["let aa = aa(\naa ()\n);", "no_space", "unexpected_space_a_b", "(", 4] + + no_space(); + } else if ( + left.id === "." + || left.id === "?." + || left.id === "..." + || right.id === "," + || right.id === ";" + || right.id === ":" + || ( + right.arity === "binary" + && (right.id === "(" || right.id === "[") + ) + || ( + right.arity === "function" + && left.id !== "function" + ) + || (right.id === "." || right.id === "?.") + ) { + +// test_cause: +// ["let aa = 0 ;", "no_space_only", "unexpected_space_a_b", ";", 12] +// ["let aa = aa ?.aa;", "no_space_only", "unexpected_space_a_b", "?.", 13] + + no_space_only(); + } else if (left.id === ";") { + +// test_cause: +// [" +// /*jslint for*/ +// function aa() { +// for ( +// aa(); +// aa; +// aa() +// ) { +// aa(); +// } +// } +// ", "expected_at", "expected_a_at_b_c", "9", 1] + + if (open) { + at_margin(0); + } + } else if ( + left.arity === "ternary" + || left.id === "case" + || left.id === "catch" + || left.id === "else" + || left.id === "finally" + || left.id === "while" + || left.id === "await" + || right.id === "catch" + || right.id === "else" + || right.id === "finally" + || (right.id === "while" && !right.statement) + || (left.id === ")" && right.id === "{") + ) { + +// test_cause: +// [" +// function aa() { +// do { +// aa(); +// } while(aa()); +// } +// ", "one_space_only", "expected_space_a_b", "(", 12] + + one_space_only(); + } else if ( + +// There is a space between left and right. + + spaceop[left.id] === true + || spaceop[right.id] === true + || ( + left.arity === "binary" + && (left.id === "+" || left.id === "-") + ) + || ( + right.arity === "binary" + && (right.id === "+" || right.id === "-") + ) + || left.id === "function" + || left.id === ":" + || left.id === "async" + || ( + ( + left.identifier + || left.id === "(string)" + || left.id === "(number)" + ) + && ( + right.identifier + || right.id === "(string)" + || right.id === "(number)" + ) + ) + || (left.arity === "statement" && right.id !== ";") + ) { + +// test_cause: +// ["let aa=0;", "one_space", "expected_space_a_b", "0", 8] +// ["let aa={\naa:\n0\n};", "expected_at", "expected_a_at_b_c", "5", 1] + + one_space(); + } else if (left.arity === "unary" && left.id !== "`") { + no_space_only(); + } + } + } + nr_comments_skipped = 0; + delete left.calls; + delete left.dead; + delete left.free; + delete left.init; + delete left.open; + delete left.used; + left = right; + } + }); +} + +function jslint_report({ + exports, + froms, + functions, + global, + json, + module, + property, + stop, + warnings +}) { + +// This function will create human-readable, html-report +// for warnings, properties, and functions from jslint-result-object. +// +// Example usage: +// let result = jslint("console.log('hello world')"); +// let html = jslint_report(result); + + let html = ""; + let length_80 = 1111; + + function address(line = 1, column = 1) { + +// This function will create HTML address element from and + + return `
    ${Number(line)}: ${Number(column)}
    `; + + } + + function detail(title, list) { + return ( + (Array.isArray(list) && list.length > 0) + ? ( + +// Google Lighthouse Accessibility -
    's do not contain only properly-ordered +//
    and
    groups, + + + + + + + + + + + + +

    CodeMirror: JSLint Demo

    +

    +This demo will auto-lint the code below, and auto-generate a report as you type. +

    + + + + + + + +
    + + + + + diff --git a/jslint_wrapper_codemirror.js b/jslint_wrapper_codemirror.js new file mode 100644 index 000000000..0cdc0950c --- /dev/null +++ b/jslint_wrapper_codemirror.js @@ -0,0 +1,146 @@ +// The Unlicense +// +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + + +// This wrapper will integrate JSLint with CodeMirror's lint.js addon. +// Requires CodeMirror and JSLint. +// +// Example usage: +/* + + + + + + + + + + + +*/ + +/*jslint browser, devel*/ +/*global CodeMirror define exports jslint module require*/ +/*property + Pos, amd, column, error, from, globals, jslint, line, map, message, + mode_stop, registerHelper, result, severity, signal, source, to, warnings +*/ + +(function (mod) { + "use strict"; + +// CommonJS + + if (typeof exports === "object" && typeof module === "object") { + mod(require("../../lib/codemirror")); + +// AMD + + } else if (typeof define === "function" && define.amd) { + define(["../../lib/codemirror"], mod); + +// Plain browser env + + } else { + mod(CodeMirror); + } +}(function (CodeMirror) { + "use strict"; + if (!window.jslint) { + console.error( + "Error: window.jslint not defined," + + " CodeMirror JavaScript linting cannot run." + ); + return []; + } + CodeMirror.registerHelper("lint", "javascript", function ( + source, + options, + editor + ) { + let result; + +// Emit before linter is run, so it can be modified +// before passing to jslint. +// +// Example usage: +// editor.on("lintJslintBefore", function (options) { +// options.browser = true; +// options.node = true; +// options.globals = ["caches", "indexedDb"]; +// }); + + options.source = source; + CodeMirror.signal(editor, "lintJslintBefore", options); + +// Run jslint. + + result = jslint.jslint(source, options, options.globals); + +// Emit after linter is run, so it can be used to generate reports. +// +// Example usage: +// editor.on("lintJslintAfter", function (options) { +// divReport.innerHTML = jslint.jslint_report(options.result); +// }); + + options.result = result; + CodeMirror.signal(editor, "lintJslintAfter", options); + +// Return warnings. + + return result.warnings.map(function ({ + column, + line, + message, + mode_stop + }) { + return { + from: CodeMirror.Pos(line - 1, column - 1), //jslint-ignore-line + message, + severity: ( + mode_stop + ? "error" + : "warning" + ), + to: CodeMirror.Pos(line - 1, column) //jslint-ignore-line + }; + }); + }); +})); diff --git a/jslint_wrapper_vim.vim b/jslint_wrapper_vim.vim new file mode 100644 index 000000000..ce6700c1c --- /dev/null +++ b/jslint_wrapper_vim.vim @@ -0,0 +1,59 @@ +"" The Unlicense +"" +"" This is free and unencumbered software released into the public domain. +"" +"" Anyone is free to copy, modify, publish, use, compile, sell, or +"" distribute this software, either in source code form or as a compiled +"" binary, for any purpose, commercial or non-commercial, and by any +"" means. +"" +"" In jurisdictions that recognize copyright laws, the author or authors +"" of this software dedicate any and all copyright interest in the +"" software to the public domain. We make this dedication for the benefit +"" of the public at large and to the detriment of our heirs and +"" successors. We intend this dedication to be an overt act of +"" relinquishment in perpetuity of all present and future rights to this +"" software under copyright law. +"" +"" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +"" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +"" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +"" IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +"" OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +"" ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +"" OTHER DEALINGS IN THE SOFTWARE. + + +"" jslint_wrapper_vim.vim +"" +"" jslint wrapper for vim +"" +"" 1. Save this file and "jslint.mjs" to directory "~/.vim/" +"" 2. Add vim-command ":source ~/.vim/jslint_wrapper_vim.vim" to file "~/.vimrc" +"" 3. Vim can now jslint files (via nodejs): +"" - with vim-command ":SaveAndJslint" +"" - with vim-key-combo " " + +let s:dir = expand(":p:h") + +"" this function will save current file and jslint it (via nodejs) +function! SaveAndJslint(bang) + "" save file + if a:bang == "!" | write! | else | write | endif + "" jslint file (via nodejs) + let &l:errorformat = + \ "%f..js:%n:%l:%c:%m," . + \ "%f:%n:%l:%c:%m" + let &l:makeprg = "node" + \ . " \"" . s:dir . "/jslint.mjs\"" + \ . " jslint_wrapper_vim" + \ . " \"" . fnamemodify(bufname("%"), ":p") . "\"" + silent make! | cwindow | redraw! +endfunction + +"" create vim-command ":SaveAndJslint" +command! -nargs=* -bang SaveAndJslint call SaveAndJslint("") + +"" map vim-key-combo " " to ":SaveAndJslint" +inoremap :SaveAndJslint +nnoremap :SaveAndJslint diff --git a/jslint_wrapper_vscode.js b/jslint_wrapper_vscode.js new file mode 100644 index 000000000..3ecf2a54e --- /dev/null +++ b/jslint_wrapper_vscode.js @@ -0,0 +1,243 @@ +// The Unlicense +// +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + + +/*jslint beta, node*/ +/*property + Diagnostic, DiagnosticSeverity, ProgressLocation, Warning, Window, activate, + cancellable, character, clear, column, commands, createDiagnosticCollection, + document, end, endsWith, exports, fsPath, getText, increment, insert, + isEmpty, jslint, languages, line, lineAt, location, map, message, module, + promises, push, range, rangeIncludingLineBreak, readFileSync, + registerTextEditorCommand, replace, report, runInNewContext, selection, set, + slice, start, subscriptions, title, uri, warnings, window, withProgress, + writeFile +*/ + +"use strict"; + +function activate({ + subscriptions +}) { +/** + * @param {vscode.ExtensionContext} context + */ +// This method is called when your extension is activated. +// Your extension is activated the very first time the command is executed. + +// Directly print diagnostic without language-server. +// https://stackoverflow.com/questions/35581332 +// /create-diagnostics-entries-without-language-server-in-visual-studio-code + + let diagnosticCollection; + let jslint; + let vscode; + + function jslintClear() { + return vscode.window.withProgress({ + cancellable: false, + location: vscode.ProgressLocation.Window, + title: "JSLint clear warnings ..." + }, async function (progress) { + progress.report({ + increment: 0 + }); + +// Clear "Problems" tab. + + diagnosticCollection.clear(); + +// Wait awhile for flicker to give user visual confirmation "Problems" tab +// is refreshed. + + await new Promise(function (resolve) { + setTimeout(resolve, 500); + }); + + progress.report({ + increment: 100 + }); + }); + } + + function jslintDisableRegion({ + document, + selection + }, edit) { + let range; + let text; + edit.insert({ + character: 0, + line: selection.start.line + }, "/*jslint-disable*/\n"); + range = document.lineAt(selection.end).rangeIncludingLineBreak; + text = document.getText(range); + +// If selection-end is EOL without preceding line-break, +// then prepend line-break before directive. + + if (!text.endsWith("\n")) { + text += "\n/*jslint-enable*/"; + +// If selection-end is start of a new line, then prepend directive before it. + + } else if (!selection.isEmpty && selection.end.character === 0) { + text = "/*jslint-enable*/\n" + text; + +// Append directive to selection-end. + + } else { + text += "/*jslint-enable*/\n"; + } + edit.replace(range, text); + } + + function jslintIgnoreLine({ + document, + selection + }, edit) { + edit.insert({ + character: document.lineAt(selection.end).range.end.character, + line: selection.end.line + }, " //jslint-ignore-line"); + } + + function jslintLint({ + document + }) { + return vscode.window.withProgress({ + cancellable: false, + location: vscode.ProgressLocation.Window, + title: "JSLint lint file ..." + }, async function (progress) { + let result; + progress.report({ + increment: 0 + }); + +// Clear "Problems" tab. + + diagnosticCollection.clear(); + result = document.getText(); + result = jslint.jslint(result); + result = result.warnings.slice(0, 100).map(function ({ + column, + line, + // line_source, + message + }) { + return new vscode.Diagnostic( + // code: line_source, + { + end: { + character: column - 1, + line: line - 1 + }, + start: { + character: column - 1, + line: line - 1 + } + }, + `JSLint - ${message}`, + vscode.DiagnosticSeverity.Warning + ); + }); + +// Wait awhile for flicker to give user visual confirmation "Problems" tab +// is refreshed. + + await new Promise(function (resolve) { + setTimeout(resolve, 100); + }); + +// Update "Problems" tab. + + diagnosticCollection.set(document.uri, result); + progress.report({ + increment: 100 + }); + }); + } + +// PR-429 - Add manual lint-on-save command. + + async function jslintLintAndSave({ + document + }) { + jslintLint({ + document + }); + await require("fs").promises.writeFile( + document.uri.fsPath, + document.getText() + ); + } + +// Initialize vscode and jslint. + + vscode = require("vscode"); + diagnosticCollection = vscode.languages.createDiagnosticCollection( + "jslint" + ); + require("vm").runInNewContext( + ( + "\"use strict\";" + + require("fs").readFileSync( //jslint-ignore-line + __dirname + "/jslint.mjs", + "utf8" + ).replace( + "\nexport default Object.freeze(jslint_export);", + "\nmodule.exports = jslint_export;" + ).replace( + "\njslint_import_meta_url = import.meta.url;", + "\n// jslint_import_meta_url = import.meta.url;" + ) + ), + { + module + } + ); + jslint = module.exports; + +// Register extension commands. + + subscriptions.push(vscode.commands.registerTextEditorCommand(( + "jslint.clear" + ), jslintClear)); + subscriptions.push(vscode.commands.registerTextEditorCommand(( + "jslint.disableRegion" + ), jslintDisableRegion)); + subscriptions.push(vscode.commands.registerTextEditorCommand(( + "jslint.ignoreLine" + ), jslintIgnoreLine)); + subscriptions.push(vscode.commands.registerTextEditorCommand(( + "jslint.lint" + ), jslintLint)); + subscriptions.push(vscode.commands.registerTextEditorCommand(( + "jslint.lintAndSave" + ), jslintLintAndSave)); +} + +exports.activate = activate; diff --git a/package.json b/package.json new file mode 100644 index 000000000..d1f5d2207 --- /dev/null +++ b/package.json @@ -0,0 +1,39 @@ +{ + "bin": { + "jslint": "jslint.mjs" + }, + "bugs": { + "url": "https://github.com/jslint-org/jslint/issues" + }, + "counter": 0, + "description": "JSLint, The JavaScript Code Quality and Coverage Tool", + "exports": { + "default": "./jslint_wrapper_cjs.cjs", + "import": "./jslint.mjs" + }, + "fileCount": 34, + "keywords": [ + "coverage-report", + "javascript", + "jslint", + "linter", + "zero-config", + "zero-dependency" + ], + "license": "UNLICENSE", + "main": "./jslint_wrapper_cjs.cjs", + "module": "./jslint.mjs", + "name": "@jslint-org/jslint", + "repository": { + "type": "git", + "url": "git+https://github.com/jslint-org/jslint.git" + }, + "scripts": { + "test": "node jslint.mjs v8_coverage_report=.artifact/coverage node test.mjs", + "test2": "sh jslint_ci.sh shCiBase" + }, + "shCiArtifactUpload": 1, + "shCiPublishNpm": 1, + "type": "module", + "version": "2024.11.24" +} diff --git a/report.js b/report.js new file mode 100644 index 000000000..5e487d143 --- /dev/null +++ b/report.js @@ -0,0 +1,222 @@ +// report.js +// 2018-10-22 +// Copyright (c) 2015 Douglas Crockford (www.JSLint.com) + +// Generate JSLint HTML reports. + +/*property + closure, column, context, edition, error, exports, filter, forEach, freeze, + froms, fudge, function, functions, global, id, isArray, join, json, keys, + length, level, line, lines, message, module, name, names, option, + parameters, parent, property, push, replace, role, signature, sort, stop, + warnings +*/ + +const rx_amp = /&/g; +const rx_gt = />/g; +const rx_lt = / with less destructive entities. + + return String( + string + ).replace( + rx_amp, + "&" + ).replace( + rx_lt, + "<" + ).replace( + rx_gt, + ">" + ); +} + +export default Object.freeze({ + error: function error_report(data) { + +// Produce the HTML Error Report. + +//
    LINE_NUMBER
    MESSAGE
    +// EVIDENCE + + let fudge = Number(Boolean(data.option.fudge)); + let output = []; + if (data.stop) { + output.push("
    JSLint was unable to finish.
    "); + } + data.warnings.forEach(function (warning) { + output.push( + "
    ", + entityify(warning.line + fudge), + ".", + entityify(warning.column + fudge), + "
    ", + entityify(warning.message), + "
    ", + entityify(data.lines[warning.line] || ""), + "" + ); + }); + return output.join(""); + }, + + function: function function_report(data) { + +// Produce the HTML Function Report. + +//
    LINE_NUMBER
    FUNCTION_NAME_AND_SIGNATURE +//
    DETAIL
    NAMES
    +//
    + + let fudge = Number(Boolean(data.option.fudge)); + let mode = ( + data.module + ? "module" + : "global" + ); + let output = []; + + if (data.json) { + return ( + data.warnings.length === 0 + ? "
    JSON: good.
    " + : "
    JSON: bad.
    " + ); + } + + function detail(title, array) { + if (Array.isArray(array) && array.length > 0) { + output.push( + "
    ", + entityify(title), + "
    ", + array.join(", "), + "
    " + ); + } + } + + if (data.functions.length === 0) { + output.push("
    There are no functions.
    "); + } + let global = Object.keys(data.global.context).sort(); + let froms = data.froms.sort(); + let exports = Object.keys(data.exports).sort(); + if (global.length + froms.length + exports.length > 0) { + output.push("
    "); + detail(mode, global); + detail("import from", froms); + detail("export", exports); + output.push("
    "); + } + + if (data.functions.length > 0) { + data.functions.forEach(function (the_function) { + let context = the_function.context; + let list = Object.keys(context); + output.push( + "
    ", + entityify(the_function.line + fudge), + "
    ", + ( + the_function.name === "=>" + ? entityify(the_function.signature) + " =>" + : ( + typeof the_function.name === "string" + ? "«" + entityify(the_function.name) + "»" + : "" + entityify(the_function.name.id) + "" + ) + ) + entityify(the_function.signature), + "" + ); + if (Array.isArray(the_function.parameters)) { + let params = []; + the_function.parameters.forEach(function extract(name) { + if (name.id === "{" || name.id === "[") { + name.names.forEach(extract); + } else { + if (name.id !== "ignore") { + params.push(name.id); + } + } + }); + detail( + "parameter", + params.sort() + ); + } + list.sort(); + detail("variable", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.role === "variable" + && the_variable.parent === the_function + ); + })); + detail("exception", list.filter(function (id) { + return context[id].role === "exception"; + })); + detail("closure", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.closure === true + && the_variable.parent === the_function + ); + })); + detail("outer", list.filter(function (id) { + let the_variable = context[id]; + return ( + the_variable.parent !== the_function + && the_variable.parent.id !== "(global)" + ); + })); + detail(mode, list.filter(function (id) { + return context[id].parent.id === "(global)"; + })); + detail("label", list.filter(function (id) { + return context[id].role === "label"; + })); + output.push("
    "); + }); + } + output.push( + "
    JSLint edition ", + entityify(data.edition), + "
    " + ); + return output.join(""); + }, + + property: function property_directive(data) { + +// Produce the /*property*/ directive. + + let not_first = false; + let output = ["/*property"]; + let length = 1111; + let properties = Object.keys(data.property); + + if (properties.length > 0) { + properties.sort().forEach(function (key) { + if (not_first) { + output.push(","); + length += 2; + } + not_first = true; + if (length + key.length >= 80) { + length = 4; + output.push("\n "); + } + output.push(" ", key); + length += key.length; + }); + output.push("\n*/\n"); + return output.join(""); + } + } +}); diff --git a/test.js b/test.js new file mode 100644 index 000000000..4e2bb4a51 --- /dev/null +++ b/test.js @@ -0,0 +1,402 @@ +/*jslint node*/ +import fs from "fs"; +import jslint from "./jslint.js"; + +function assertOrThrow(passed, msg) { +/* + * this function will throw if is falsy + */ + if (!passed) { + throw new Error(msg); + } +} + +function noop() { +/* + * this function will do nothing + */ + return; +} + +(function testCaseJslintCli() { +/* + * this function will test jslint's cli handling-behavior + */ + process.exit = function (exitCode) { + assertOrThrow(!exitCode, exitCode); + }; + jslint.cli({ + file: "jslint.js" + }); + jslint.cli({ + // suppress error + console_error: noop, + file: "undefined" + }); + jslint.cli({ + // suppress error + console_error: noop, + file: "syntax_error.js", + option: { + debug: true + }, + source: "syntax error" + }); + jslint.cli({ + file: "aa.html", + source: "\n" + }); +}()); + +(function testCaseJslintMisc() { +/* + * this function will test jslint's misc handling-behavior + */ + // test assertOrThrow's throw handling-behavior + try { + assertOrThrow(undefined, new Error()); + } catch (ignore) {} +}()); + +(function testCaseJslintOption() { +/* + * this function will test jslint's option handling-behavior + */ + [ + [ + "let aa = aa | 0;", {bitwise: true}, [] + ], [ + ";\naa(new XMLHttpRequest());", {browser: true}, ["aa"] + ], [ + "let aa = \"aa\" + 0;", {convert: true}, [] + ], [ + "registerType();", {couch: true}, [] + ], [ + "", {debug: true}, [] + ], [ + "debugger;", {devel: true}, [] + ], [ + "new Function();\neval();", {eval: true}, [] + ], [ + ( + "function aa(aa) {\n" + + " for (aa = 0; aa < 0; aa += 1) {\n" + + " aa();\n" + + " }\n" + + "}\n" + ), {for: true}, [] + ], [ + "let aa = {get aa() {\n return;\n}};", {getset: true}, [] + ], [ + "let aa = {set aa(aa) {\n return aa;\n}};", {getset: true}, [] + ], [ + "/".repeat(100), {long: true}, [] + ], [ + "let aa = aa._;", {name: true}, [] + ], [ + "require();", {node: true}, [] + ], [ + "let aa = 'aa';", {single: true}, [] + ], [ + "let aa = this;", {this: true}, [] + ], [ + ( + "function aa({bb, aa}) {\n" + + " switch (aa) {\n" + + " case 1:\n" + + " break;\n" + + " case 0:\n" + + " break;\n" + + " default:\n" + + " return {bb, aa};\n" + + " }\n" + + "}\n" + ), {unordered: true}, [] + ], [ + "let {bb, aa} = 0;", {unordered: true}, [] + ], [ + "let bb = 0;\nlet aa = 0;", {beta: true, variable: true}, [] + ], [ + ( + "function aa() {\n" + + " if (aa) {\n" + + " let bb = 0;\n" + + " return bb;\n" + + " }\n" + + "}\n" + ), {beta: true, variable: true}, [] + ], [ + "\t", {white: true}, [] + ] + ].forEach(function ([ + source, option_dict, global_list + ]) { + // test jslint's option handling-behavior + assertOrThrow( + jslint(source, option_dict, global_list).warnings.length === 0, + "jslint(" + JSON.stringify([ + source, option_dict, global_list + ]) + ")" + ); + // test jslint's directive handling-behavior + source = ( + "/*jslint\n" + + JSON.stringify(option_dict).slice(1, -1).replace(( + /"/g + ), "") + "\n" + + "*/\n" + + ( + global_list.length === 0 + ? "" + : ( + "/*global\n" + + global_list.join(",") + "\n" + + "*/\n" + ) + ) + + source + ); + assertOrThrow(jslint(source).warnings.length === 0, source); + }); + assertOrThrow(jslint("", { + test_internal_error: true + }).warnings.length === 1); +}()); + +(function testCaseJslintCodeValidate() { +/* + * this function will validate each code is valid in jslint + */ + Object.values({ + array: [ + "new Array(0);" + ], + async_await: [ + "async function aa() {\n await aa();\n}", + "async function aa() {\n" + + " try {\n" + + " aa();\n" + + " } catch (err) {\n" + + " await err();\n" + + " }\n" + + "}\n", + "async function aa() {\n" + + " try {\n" + + " await aa();\n" + + " } catch (err) {\n" + + " await err();\n" + + " }\n" + + "}\n" + ], + date: [ + "Date.getTime();", + "let aa = aa().getTime();", + "let aa = aa.aa().getTime();" + ], + directive: [ + "#!\n/*jslint browser:false, node*/\n\"use strict\";", + "/*property aa bb*/" + ], + fart: [ + "function aa() {\n return () => 0;\n}" + ], + jslint_disable: [ + "/*jslint-disable*/\n0\n/*jslint-enable*/" + ], + jslint_quiet: [ + "0 //jslint-quiet" + ], + json: [ + "{\"aa\":[[],-0,null]}" + ], + label: [ + "function aa() {\n" + + "bb:\n" + + " while (true) {\n" + + " if (true) {\n" + + " break bb;\n" + + " }\n" + + " }\n" + + "}\n" + ], + loop: [ + "function aa() {\n do {\n aa();\n } while (aa());\n}" + ], + module: [ + "export default Object.freeze();", + "import {aa, bb} from \"aa\";\naa(bb);", + "import {} from \"aa\";", + "import(\"aa\").then(function () {\n return;\n});", + "let aa = 0;\nimport(aa).then(aa).then(aa).catch(aa).finally(aa);" + ], + number: [ + "let aa = 0.0e0;", + "let aa = 0b0;", + "let aa = 0o0;", + "let aa = 0x0;" + ], + optional_chaining: [ + "let aa = aa?.bb?.cc;" + ], + param: [ + "function aa({aa, bb}) {\n" + + " return {aa, bb};\n" + + "}\n", + "function aa({constructor}) {\n" + + " return {constructor};\n" + + "}\n" + ], + property: [ + "let aa = aa[`!`];" + ], + regexp: [ + "function aa() {\n return /./;\n}", + "let aa = /(?!.)(?:.)(?=.)/;", + "let aa = /./gimuy;" + ], + ternary: [ + "let aa = (\n aa()\n ? 0\n : 1\n) " + + "&& (\n aa()\n ? 0\n : 1\n);" + ], + try_catch: [ + "let aa = 0;\n" + + "try {\n" + + " aa();\n" + + "} catch (err) {\n" + + " aa = err;\n" + + "}\n" + + "try {\n" + + " aa();\n" + + "} catch (err) {\n" + + " aa = err;\n" + + "}\n" + + "aa();\n" + ], + var: [ + "\"use strict\";\nvar aa = 0;", + "let [\n aa, bb = 0\n] = 0;", + "let [...aa] = [...aa];", + "let constructor = 0;", + "let {\n aa: bb\n} = 0;", + "let {aa, bb} = 0;", + "let {constructor} = 0;" + ] + }).forEach(function (codeList) { + let code0 = ""; + codeList.forEach(function (code) { + let warnings; + // Assert codeList is sorted. + assertOrThrow(code0 < code, JSON.stringify([ + code0, code + ], undefined, 4)); + code0 = code; + warnings = jslint(code).warnings; + assertOrThrow( + warnings.length === 0, + JSON.stringify([code, warnings]) + ); + }); + }); +}()); + +(async function testCaseJslintWarningsValidate() { +/* + * this function will validate each jslint is raised with given + * malformed + */ + Array.from(String( + await fs.promises.readFile("jslint.js", "utf8") + ).matchAll(new RegExp(( + "\\s*?" + + "(\\/\\/\\s*?cause:.*?\\n(?:\\/\\/.*?\\n)*?)" + + "(\\s*?^[^\\/].*?(?:\\n\\s*?\".*?)?$)" + ), "gm"))).forEach(function ([ + match0, causeList, warning + ]) { + let cause0 = ""; + let expectedWarningCode; + let fnc; + // debug match0 + // console.error(match0.trim().replace((/\n\n/g), "\n")); + assertOrThrow( + match0.indexOf("\n\n" + causeList + "\n ") === 0, + JSON.stringify([ + match0, causeList + ], undefined, 4) + ); + warning = warning.match( + "(" + + "at_margin" + + "|expected_at" + + "|left_check" + + "|no_space_only" + + "|one_space" + + "|one_space_only" + + "|semicolon" + + "|stop" + + "|stop_at" + + "|warn" + + "|warn_at" + + "|warn_if_unordered" + + "|warn_if_unordered_case_statement" + + ")" + + "\\\u0028\\s*?\"?" + + "(\\S[^\n\"]+)" + ); + if (warning) { + expectedWarningCode = warning[2]; + fnc = warning[1]; + switch (fnc) { + case "at_margin": + case "expected_at": + expectedWarningCode = "expected_a_at_b_c"; + break; + case "left_check": + expectedWarningCode = "unexpected_a"; + break; + case "no_space_only": + expectedWarningCode = "unexpected_space_a_b"; + break; + case "one_space": + case "one_space_only": + expectedWarningCode = "expected_space_a_b"; + break; + case "semicolon": + expectedWarningCode = "expected_a_b"; + break; + case "warn_if_unordered": + case "warn_if_unordered_case_statement": + expectedWarningCode = "expected_a_b_before_c_d"; + break; + } + } + causeList.split( + /\/\/\u0020cause:[\n|\u0020]/ + ).slice(1).forEach(function (cause) { + assertOrThrow(cause === cause.trim() + "\n", JSON.stringify(cause)); + cause = ( + expectedWarningCode === "too_long" + ? "//".repeat(100) + : cause[0] === "\"" + ? JSON.parse(cause) + : cause.replace(( + /^\/\/\u0020/gm + ), "") + ); + // Assert causeList is sorted. + assertOrThrow(cause0 < cause, JSON.stringify([ + cause0, cause + ], undefined, 4)); + cause0 = cause; + // Assert expectedWarningCode from cause. + assertOrThrow( + jslint(cause).warnings.some(function ({ + code + }) { + return code === expectedWarningCode; + }) || !expectedWarningCode, + "\n" + cause.trim() + ); + }); + }); +}()); diff --git a/test.mjs b/test.mjs new file mode 100644 index 000000000..24967f887 --- /dev/null +++ b/test.mjs @@ -0,0 +1,1575 @@ +/*jslint beta, node*/ +import jslint from "./jslint.mjs"; +import jslintCjs from "./jslint_wrapper_cjs.cjs"; +import moduleFs from "fs"; +import modulePath from "path"; + +let { + assertErrorThrownAsync, + assertJsonEqual, + assertOrThrow, + debugInline, + fsWriteFileWithParents, + globExclude, + jstestDescribe, + jstestIt, + jstestOnExit, + moduleFsInit, + noop, + v8CoverageListMerge, + v8CoverageReportCreate +} = jslint; +let sourceJslintMjs; +let testCoverageMergeData; + +await (async function init() { + +// Coverage-hack - Ugly-hack to get test-coverage for all initialization-states. + + moduleFsInit(); + moduleFsInit(); + +// Cleanup directory .tmp + + await moduleFs.promises.rm(".tmp", { + recursive: true + }).catch(noop); + +// init sourceJslintMjs + + sourceJslintMjs = await moduleFs.promises.readFile("jslint.mjs", "utf8"); + +// init testCoverageMergeData + + testCoverageMergeData = JSON.parse( + await moduleFs.promises.readFile( + "test_coverage_merge_data.json", + "utf8" + ) + ); +}()); + +jstestDescribe(( + "test fsXxx handling-behavior" +), function testBehaviorFsXxx() { + jstestIt(( + "test fsWriteFileWithParents handling-behavior" + ), async function () { + await Promise.all([ + 1, 2, 3, 4 + ].map(async function () { + await fsWriteFileWithParents( + ".tmp/fsWriteFileWithParents/aa/bb/cc", + "aa" + ); + })); + assertJsonEqual( + await moduleFs.promises.readFile( + ".tmp/fsWriteFileWithParents/aa/bb/cc", + "utf8" + ), + "aa" + ); + }); +}); + +jstestDescribe(( + "test globXxx handling-behavior" +), function testBehaviorGlobXxx() { + jstestIt(( + "test globAssertNotWeird-error handling-behavior" + ), async function () { + await Promise.all([ + "\n", + "\r", + "\u0000" + ].map(async function (char) { + await assertErrorThrownAsync(function () { + return globExclude({ + pathnameList: [ + "aa", + `cc/${char}/dd`, + "bb" + ] + }); + }, ( + "Weird character " + + JSON.stringify(char).replace("\\", "\\\\") + + " found in " + )); + })); + }); + jstestIt(( + "test globExclude handling-behavior" + ), function () { + let pathnameList = [ + ".dockerignore", + ".eslintrc.js", + ".gitignore", + ".npmignore", + ".travis.yml", + "/node_modules/aa/bb/cc.js", + "/node_modules/aa/bb/dd.js", + "CHANGELOG.md", + "CONTRIBUTING.md", + "Dockerfile", + "LICENSE", + "Makefile", + "README.md", + "appveyor.yml", + "benchmark/insert-transaction.sql", + "benchmark/insert.js", + "binding.gyp", + "cloudformation/ci.template.js", + "deps/common-sqlite.gypi", + "deps/extract.py", + "deps/sqlite-autoconf-3340000.tar.gz", + "deps/sqlite3.gyp", + "examples/simple-chaining.js", + "lib/index.js", + "lib/sqlite3-binding.js", + "lib/sqlite3.js", + "lib/trace.js", + "node_modules/aa/bb/cc.js", + "node_modules/aa/bb/dd.js", + "package.json", + "scripts/build-appveyor.bat", + "scripts/build-local.bat", + "scripts/build_against_electron.sh", + "scripts/build_against_node.sh", + "scripts/build_against_node_webkit.sh", + "scripts/build_for_node_webkit.cmd", + "scripts/install_node.sh", + "scripts/validate_tag.sh", + "sqlite3.js", + "src/async.h", + "src/backup.cc", + "src/backup.h", + "src/database.cc", + "src/database.h", + "src/gcc-preinclude.h", + "src/macros.h", + "src/node_sqlite3.cc", + "src/statement.cc", + "src/statement.h", + "src/threading.h", + "test/affected.test.js", + "test/backup.test.js", + "test/blob.test.js", + "test/cache.test.js", + "test/constants.test.js", + "test/database_fail.test.js", + "test/each.test.js", + "test/exec.test.js", + "test/extension.test.js", + "test/fts-content.test.js", + "test/interrupt.test.js", + "test/issue-108.test.js", + "test/json.test.js", + "test/map.test.js", + "test/named_columns.test.js", + "test/named_params.test.js", + "test/null_error.test.js", + "test/nw/.gitignore", + "test/nw/Makefile", + "test/nw/index.html", + "test/nw/package.json", + "test/open_close.test.js", + "test/other_objects.test.js", + "test/parallel_insert.test.js", + "test/prepare.test.js", + "test/profile.test.js", + "test/rerun.test.js", + "test/scheduling.test.js", + "test/serialization.test.js", + "test/support/createdb-electron.js", + "test/support/createdb.js", + "test/support/elmo.png", + "test/support/helper.js", + "test/support/prepare.db", + "test/support/script.sql", + "test/trace.test.js", + "test/unicode.test.js", + "test/upsert.test.js", + "test/verbose.test.js", + "tools/docker/architecture/linux-arm/Dockerfile", + "tools/docker/architecture/linux-arm/run.sh", + "tools/docker/architecture/linux-arm64/Dockerfile", + "tools/docker/architecture/linux-arm64/run.sh" + ]; + [ + "tes?/", + "tes[-t-]/", + "tes[-t]/", + "tes[0-9A-Z_a-z-]/", + "tes[t-]/", + "test/**/*.js" + ].forEach(function (aa) { + [ + "li*/*.js", + "li?/*.js", + "lib/", + "lib/*", + "lib/**/*.js", + "lib/*.js" + ].forEach(function (bb) { + [ + "", + "**/node_modules/", + "node_modules/" + ].forEach(function (cc) { + assertJsonEqual( + globExclude({ + excludeList: [ + "tes[!0-9A-Z_a-z-]/", + "tes[^0-9A-Z_a-z-]/", + "test/suppor*/*elper.js", + "test/suppor?/?elper.js", + "test/support/helper.js" + ].concat(aa, cc), + includeList: [ + "**/*.cjs", + "**/*.js", + "**/*.mjs", + "lib/sqlite3.js" + ].concat(bb), + pathnameList + }).pathnameList, + [ + ".eslintrc.js", + "benchmark/insert.js", + "cloudformation/ci.template.js", + "examples/simple-chaining.js", + "lib/index.js", + "lib/sqlite3-binding.js", + "lib/sqlite3.js", + "lib/trace.js", + "sqlite3.js" + ].concat( + cc === "**/node_modules/" + ? [ + "node_modules/aa/bb/cc.js", + "node_modules/aa/bb/dd.js" + ] + : cc === "node_modules/" + ? [ + "/node_modules/aa/bb/cc.js", + "/node_modules/aa/bb/dd.js" + ] + : [ + "/node_modules/aa/bb/cc.js", + "/node_modules/aa/bb/dd.js", + "node_modules/aa/bb/cc.js", + "node_modules/aa/bb/dd.js" + ] + ).sort() + ); + }); + }); + }); + }); + jstestIt(( + "test globToRegexp handling-behavior" + ), function () { + Object.entries({ + "*": ( + /^[^\/]*?$/gm + ), + "**": ( + /^.*?$/gm + ), + "***": ( + /^.*?$/gm + ), + "****": ( + /^.*?$/gm + ), + "****////****": ( + /^.*?$/gm + ), + "***///***": ( + /^.*?$/gm + ), + "**/*": ( + /^.*?$/gm + ), + "**/node_modules/": ( + /^.*?\/node_modules\/.*?$/gm + ), + "**/node_modules/**/*": ( + /^.*?\/node_modules\/.*?$/gm + ), + "?": ( + /^[^\/]$/gm + ), + "[!0-9A-Za-z-]": ( + /^[^0-9A-Za-z\-]$/gm + ), + "[0-9A-Za-z-]": ( + /^[0-9A-Za-z\-]$/gm + ), + "[[]] ]][[": ( + /^[\[]\] \]\][\[]$/gm + ), + "[]": ( + /^$/gm + ), + "[^0-9A-Za-z-]": ( + /^[^0-9A-Za-z\-]$/gm + ), + "aa/bb/cc": ( + /^aa\/bb\/cc$/gm + ), + "aa/bb/cc/": ( + /^aa\/bb\/cc\/.*?$/gm + ), + "li*/*": ( + /^li[^\/]*?\/[^\/]*?$/gm + ), + "li?/*": ( + /^li[^\/]\/[^\/]*?$/gm + ), + "lib/": ( + /^lib\/.*?$/gm + ), + "lib/*": ( + /^lib\/[^\/]*?$/gm + ), + "lib/**/*.js": ( + /^lib\/.*?\.js$/gm + ), + "lib/*.js": ( + /^lib\/[^\/]*?\.js$/gm + ), + "node_modules/": ( + /^node_modules\/.*?$/gm + ), + "node_modules/**/*": ( + /^node_modules\/.*?$/gm + ), + "tes[!0-9A-Z_a-z-]/**/*": ( + /^tes[^0-9A-Z_a-z\-]\/.*?$/gm + ), + "tes[0-9A-Z_a-z-]/**/*": ( + /^tes[0-9A-Z_a-z\-]\/.*?$/gm + ), + "tes[^0-9A-Z_a-z-]/**/*": ( + /^tes[^0-9A-Z_a-z\-]\/.*?$/gm + ), + "test/**/*": ( + /^test\/.*?$/gm + ), + "test/**/*.js": ( + /^test\/.*?\.js$/gm + ), + "test/suppor*/*elper.js": ( + /^test\/suppor[^\/]*?\/[^\/]*?elper\.js$/gm + ), + "test/suppor?/?elper.js": ( + /^test\/suppor[^\/]\/[^\/]elper\.js$/gm + ), + "test/support/helper.js": ( + /^test\/support\/helper\.js$/gm + ) + }).forEach(function ([ + pattern, rgx + ]) { + assertJsonEqual( + globExclude({ + excludeList: [ + pattern + ] + }).excludeList[0].source, + rgx.source + ); + assertJsonEqual( + globExclude({ + includeList: [ + pattern + ] + }).includeList[0].source, + rgx.source + ); + }); + }); +}); + +jstestDescribe(( + "test jslint's cli handling-behavior" +), function testBehaviorJslintCli() { + function processExit0(exitCode) { + assertOrThrow(exitCode === 0, exitCode); + } + function processExit1(exitCode) { + assertOrThrow(exitCode === 1, exitCode); + } + jstestIt(( + "test cli-null-case handling-behavior" + ), function () { + jslint.jslint_cli({ + mode_noop: true, + process_exit: processExit0 + }); + }); + jstestIt(( + "test cli-window-jslint handling-behavior" + ), function () { + [ + "&window_jslint=", + "&window_jslint=12", + "&window_jslint=1?", + "&window_jslint=?", + "?window_jslint=", + "?window_jslint=12", + "?window_jslint=1?", + "?window_jslint=?", + "window_jslint=1", + "window_jslint=1&", + "window_jslint=12", + "window_jslint=1?" + ].forEach(function (import_meta_url) { + jslint.jslint_cli({ + import_meta_url + }); + assertOrThrow(globalThis.jslint === undefined); + }); + [ + "&window_jslint=1", + "&window_jslint=1&", + "?window_jslint=1", + "?window_jslint=1&" + ].forEach(function (import_meta_url) { + jslint.jslint_cli({ + import_meta_url + }); + assertOrThrow(globalThis.jslint === jslint); + delete globalThis.jslint; + }); + }); + jstestIt(( + "test cli-cjs-and-invalid-file handling-behavior" + ), async function () { + await fsWriteFileWithParents(".test_dir.cjs/touch.txt", ""); + [ + ".", // test dir handling-behavior + "jslint.mjs", // test file handling-behavior + undefined // test file-undefined handling-behavior + ].forEach(function (file) { + jslint.jslint_cli({ + file, + mode_cli: true, + process_env: { + JSLINT_BETA: "1" + }, + process_exit: processExit0 + }); + }); + }); + jstestIt(( + "test cli-apidoc handling-behavior" + ), function () { + jslint.jslint_cli({ + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_apidoc=.artifact/apidoc.html", + JSON.stringify({ + example_list: [ + "README.md", + "test.mjs", + "jslint.mjs" + ], + github_repo: "https://github.com/jslint-org/jslint", + module_list: [ + { + pathname: "./jslint.mjs" + } + ], + package_name: "JSLint", + version: jslint.jslint_edition + }) + ], + process_exit: processExit0 + }); + }); + jstestIt(( + "test cli-file-error handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + file: "undefined", + mode_cli: true, + process_exit: processExit1 + }); + }); + jstestIt(( + "test cli-syntax-error handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + file: "syntax-error.js", + mode_cli: true, + option: { + trace: true + }, + process_exit: processExit1, + source: "syntax error" + }); + }); + jstestIt(( + "test cli-report handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_report=.tmp/jslint_report.html", + "jslint.mjs" + ], + process_exit: processExit0 + }); + }); + jstestIt(( + "test cli-report-error handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_report=.tmp/jslint_report.html", + "syntax-error.js" + ], + process_exit: processExit1, + source: "syntax error" + }); + }); + jstestIt(( + "test cli-report-json handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_report=.tmp/jslint_report.html", + "aa.json" + ], + process_exit: processExit0, + source: "[]" + }); + }); + jstestIt(( + "test cli-report-json-error handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_report=.tmp/jslint_report.html", + "aa.json" + ], + process_exit: processExit1, + source: "[" + }); + }); + jstestIt(( + "test cli-report-misc handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_report=.tmp/jslint_report.html", + "aa.js" + ], + process_exit: processExit0, + source: "let aa = 0;" + }); + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_report=.tmp/jslint_report.html", + "aa.js" + ], + process_exit: processExit1, + source: "(aa)=>aa; function aa([aa]){}" + }); + }); + jstestIt(( + "test cli-jslint-wrapper-vim handling-behavior" + ), function () { + jslint.jslint_cli({ + // suppress error + console_error: noop, + mode_cli: true, + process_argv: [ + "node", + "jslint.mjs", + "jslint_wrapper_vim", + "syntax-error.js" + ], + process_exit: processExit1, + source: "syntax error" + }); + }); +}); + +jstestDescribe(( + "test jslint's no-warnings handling-behavior" +), function testBehaviorJslintNoWarnings() { + jstestIt(( + "test jslint's no-warnings handling-behavior" + ), function () { + Object.values({ + array: [ + "new Array(0);" + ], + async_await: [ + "async function aa() {\n await aa();\n}", + +// PR-405 - Bugfix - fix expression after "await" mis-identified as statement. + + "async function aa() {\n await aa;\n}", + ( + "async function aa() {\n" + + " try {\n" + + " aa();\n" + + " } catch (err) {\n" + + " await err();\n" + + " }\n" + + "}\n" + ), + ( + "async function aa() {\n" + + " try {\n" + + " await aa();\n" + + " } catch (err) {\n" + + " await err();\n" + + " }\n" + + "}\n" + ), + +// PR-370 - Add top-level-await support. + + "await String();\n" + ], + +// PR-351 - Add BigInt support. + + bigint: [ + "let aa = 0b0n;\n", + "let aa = 0o0n;\n", + "let aa = 0x0n;\n", + "let aa = BigInt(0n);\n", + "let aa = typeof aa === \"bigint\";\n" + ], + date: [ + "Date.getTime();", + "let aa = aa().getTime();", + "let aa = aa.aa().getTime();" + ], + directive: [ + "#!\n/*jslint browser:false, node*/\n\"use strict\";", + "/*property aa bb*/" + ], + for: [ + ( + "/*jslint for*/\n" + + "function aa(bb) {\n" + + " for (bb = 0; bb < 0; bb += 1) {\n" + + " bb();\n" + + " }\n" + + "}\n" + ) + ], + jslint_disable: [ + "/*jslint-disable*/\n0\n/*jslint-enable*/" + ], + jslint_ignore_line: [ + "0 //jslint-ignore-line" + ], + json: [ + "{\"aa\":[[],-0,null]}" + ], + label: [ + ( + "function aa() {\n" + + "bb:\n" + + " while (true) {\n" + + " if (true) {\n" + + " break bb;\n" + + " }\n" + + " }\n" + + "}\n" + ) + ], + loop: [ + ( + "function aa() {\n" + + " do {\n" + + " aa();\n" + + " } while (aa());\n" + + "}\n" + ), + +// PR-378 - Relax warning "function_in_loop". + + ( + "function aa() {\n" + + " while (true) {\n" + + " (function () {\n" + + " return;\n" + + " }());\n" + + " }\n" + + "}\n" + ) + ], + module: [ + "export default Object.freeze();", + +// PR-439 - Add grammar for "export async function ...". + + ( + "export default Object.freeze(async function () {\n" + + " return await 0;\n" + + "});\n" + ), + "import {aa, bb} from \"aa\";\naa(bb);", + "import {} from \"aa\";", + "import(\"aa\").then(function () {\n return;\n});", + ( + "let aa = 0;\n" + + "import(aa).then(aa).then(aa)" + + ".catch(aa).finally(aa);\n" + ) + ], + number: [ + "let aa = 0.0e0;", + "let aa = 0b0;", + "let aa = 0o0;", + "let aa = 0x0;" + ], + +// PR-390 - Add numeric-separator support. + + numeric_separator: [ + "let aa = 0.0_0_0;", + "let aa = 0b0_1111_1111n;\n", + "let aa = 0o0_1234_1234n;\n", + "let aa = 0x0_1234_1234n;\n", + "let aa = 1_234_234.1_234_234E1_234_234;" + ], + optional_chaining: [ + "let aa = aa?.bb?.cc;" + ], + param: [ + "function aa({aa, bb}) {\n return {aa, bb};\n}\n", + ( + "function aa({constructor}) {\n" + + " return {constructor};\n" + + "}\n" + ) + ], + property: [ + "let aa = aa[`!`];" + ], + regexp: [ + "function aa() {\n return /./;\n}", + "let aa = /(?!.)(?:.)(?=.)/;", + "let aa = /./gimuy;", + "let aa = /[\\--\\-]/;" + ], + ternary: [ + ( + "let aa = (\n" + + " aa()\n" + + " ? 0\n" + + " : 1\n" + + ") " + + "&& (\n" + + " aa()\n" + + " ? 0\n" + + " : 1\n" + + ");" + ), + ( + "let aa = (\n" + + " aa()\n" + + " ? `${0}`\n" + + " : `${1}`\n" + + ");" + ), + +// PR-394 - Bugfix +// Fix jslint falsely believing megastring literals `0` and `1` are similar. + + ( + "let aa = (\n" + + " aa()\n" + + " ? `0`\n" + + " : `1`\n" + + ");" + ) + ], + try_catch: [ + ( + "let aa = 0;\n" + + "try {\n" + + " aa();\n" + + "} catch (err) {\n" + + " aa = err;\n" + + "}\n" + + "try {\n" + + " aa();\n" + + "} catch (err) {\n" + + " aa = err;\n" + + "}\n" + + "aa();\n" + ) + ], + try_finally: [ + ( + "let aa = 0;\n" + + "try {\n" + + " aa();\n" + + "} finally {\n" + + " aa();\n" + + "}\n" + ) + ], + use_strict: [ + ( + "\"use strict\";\n" + + "let aa = 0;\n" + + "function bb() {\n" + + " \"use strict\";\n" + + " return aa;\n" + + "}\n" + ) + ], + var: [ + +// PR-363 - Bugfix +// Add test against false-warning in code +// '/*jslint node*/\nlet {aa:bb} = {}; bb();'. + + "/*jslint node*/\n", + "" + ].map(function (directive) { + return [ + "let [\n aa, bb = 0\n] = 0;\naa();\nbb();", + "let aa = 0;\nlet [...bb] = [...aa];\nbb();", + +// PR-459 - Allow destructuring-assignment after function-definition. + + ( + "let aa;\n" + + "let bb;\n" + + "function cc() {\n" + + " return;\n" + + "}\n" + + "[aa, bb] = cc();\n" + ), + "let constructor = 0;\nconstructor();", + "let {\n aa: bb\n} = 0;\nbb();", + "let {\n aa: bb,\n bb: cc\n} = 0;\nbb();\ncc();", + "let {aa, bb} = 0;\naa();\nbb();", + "let {constructor} = 0;\nconstructor();" + ].map(function (code) { + return directive + code; + }); + }).flat() + }).forEach(function (codeList) { + let elemPrv = ""; + codeList.forEach(function (code) { + let warnings; + // Assert codeList is sorted. + assertOrThrow(elemPrv < code, JSON.stringify([ + elemPrv, code + ], undefined, 4)); + elemPrv = code; + [ + jslint.jslint, + jslintCjs.jslint + ].forEach(function (jslint) { + warnings = jslint(code, { + beta: true + }).warnings; + assertOrThrow( + warnings.length === 0, + JSON.stringify([code, warnings]) + ); + }); + }); + }); + }); +}); + +jstestDescribe(( + "test jslint's option handling-behavior" +), function testBehaviorJslintOption() { + let elemPrv = ""; + [ + [ + "let aa = aa | 0;", {bitwise: true}, [] + ], [ + ";\naa(new XMLHttpRequest());", {browser: true}, ["aa"] + ], [ + "let aa = \"aa\" + 0;", {convert: true}, [] + ], [ + "registerType();", {couch: true}, [] + ], [ + "debugger;", {devel: true}, [] + ], [ + +// PR-404 - Alias "evil" to jslint-directive "eval" for backwards-compat. + + "new Function();\neval();", {eval: true, evil: true}, [] + ], [ + "let aa = () => 0;", {fart: true}, [] + ], [ + ( + "let aa = async (bb, {cc, dd}, [ee, ff], ...gg) => {\n" + + " bb += 1;\n" + + " return await (bb + cc + dd + ee + ff + gg);\n" + + "};\n" + ), {fart: true}, [] + ], [ + ( + "function aa(aa) {\n" + + " for (aa = 0; aa < 0; aa += 1) {\n" + + " aa();\n" + + " }\n" + + "}\n" + ), {for: true}, [] + ], [ + "let aa = {get aa() {\n return;\n}};", {getset: true}, [] + ], [ + "let aa = {set aa(aa) {\n return aa;\n}};", {getset: true}, [] + ], [ + sourceJslintMjs.replace(( + / /g + ), " "), {indent2: true}, [] + ], [ + "function aa() {\n return;\n}", {indent2: true}, [] + ], [ + "/".repeat(100), {long: true}, [] + ], [ + +// PR-404 - Alias "nomen" to jslint-directive "name" for backwards-compat. + + "let aa = aa._;", {name: true, nomen: true}, [] + ], [ + "require();", {node: true}, [] + ], [ + "let aa = 'aa';", {single: true}, [] + ], [ + +// PR-404 - Add new directive "subscript" to play nice with Google Closure. + + "aa[\"aa\"] = 1;", {subscript: true}, ["aa"] + ], [ + "", {test_internal_error: true}, [] + ], [ + "let aa = this;", {this: true}, [] + ], [ + "", {trace: true}, [] + ], [ + ( + "function aa({bb, aa}) {\n" + + " switch (aa) {\n" + + " case 1:\n" + + " break;\n" + + " case 0:\n" + + " break;\n" + + " default:\n" + + " return {bb, aa};\n" + + " }\n" + + "}\n" + ), {unordered: true}, [] + ], [ + "let {bb, aa} = 0;", {unordered: true}, [] + ], [ + ( + "function aa() {\n" + + " if (aa) {\n" + + " let bb = 0;\n" + + " return bb;\n" + + " }\n" + + "}\n" + ), {variable: true}, [] + ], [ + "let bb = 0;\nlet aa = 0;", {variable: true}, [] + ], [ + "\t", {white: true}, [] + ] + ].forEach(function ([ + source, option_dict, global_list + ]) { + jstestIt(( + `test option=${JSON.stringify(option_dict)} handling-behavior` + ), function () { + let elemNow = JSON.stringify([ + option_dict, source, global_list + ]); + let warningsLength = ( + option_dict.test_internal_error + ? 1 + : 0 + ); + // Assert list is sorted. + assertOrThrow(elemPrv < elemNow, JSON.stringify([ + elemPrv, elemNow + ], undefined, 4)); + elemPrv = elemNow; + option_dict.beta = true; + [ + jslint.jslint, + jslintCjs.jslint + ].forEach(function (jslint) { + // test jslint's option handling-behavior + assertOrThrow( + jslint( + source, + option_dict, + global_list + ).warnings.length === warningsLength, + "jslint.jslint(" + JSON.stringify([ + source, option_dict, global_list + ]) + ")" + ); + // test jslint's directive handling-behavior + source = ( + "/*jslint " + JSON.stringify( + option_dict + ).slice(1, -1).replace(( + /"/g + ), "") + "*/\n" + + ( + global_list.length === 0 + ? "" + : "/*global " + global_list.join(",") + "*/\n" + ) + + source.replace(( + /^#!/ + ), "//") + ); + assertOrThrow( + jslint(source).warnings.length === warningsLength, + source + ); + }); + }); + }); +}); + +jstestDescribe(( + "test jslint's warnings handling-behavior" +), function testBehaviorJslintWarnings() { + jstestIt(( + "test jslint's warning handling-behavior" + ), function () { + +// this function will validate each jslint is raised with given +// malformed + + sourceJslintMjs.replace(( + /(\n\s*?\/\/\s*?test_cause:\s*?)(\S[\S\s]*?\S)(\n\n\s*?) *?\S/g + ), function (match0, header, causeList, footer) { + let tmp; + // console.error(match0); + // Validate header. + assertOrThrow(header === "\n\n// test_cause:\n", match0); + // Validate footer. + assertOrThrow(footer === "\n\n", match0); + // Validate causeList. + causeList = causeList.replace(( + /^\/\/ /gm + ), "").replace(( + /^\["\n([\S\s]*?)\n"(,.*?)$/gm + ), function (ignore, source, param) { + source = "[" + JSON.stringify(source) + param; + assertOrThrow(source.length > (80 - 3), source); + return source; + }).replace(( + / \/\/jslint-ignore-line$/gm + ), ""); + tmp = causeList.split("\n").map(function (cause) { + return ( + "[" + + JSON.parse(cause).map(function (elem) { + return JSON.stringify(elem); + }).join(", ") + + "]" + ); + }).sort().join("\n"); + assertOrThrow( + causeList === tmp, + "\n" + causeList + "\n\n" + tmp + ); + causeList.split("\n").forEach(function (cause) { + cause = JSON.parse(cause); + tmp = jslint.jslint(cause[0], { + beta: true, + test_cause: true + }).causes; + // Validate cause. + assertOrThrow( + tmp[JSON.stringify(cause.slice(1))], + ( + "\n" + JSON.stringify(cause) + "\n\n" + + Object.keys(tmp).sort().join("\n") + ) + ); + }); + return ""; + }); + }); +}); + +jstestDescribe(( + "test jstestXxx handling-behavior" +), function testBehaviorJstestXxx() { + jstestIt(( + "test jstestDescribe error handling-behavior" + ), function () { + throw new Error(); + }, "pass"); + jstestIt(( + "test jstestOnExit tests-failed handling-behavior" + ), function () { + jstestOnExit(undefined, "testsFailed"); + }); +}); + +jstestDescribe(( + "test misc handling-behavior" +), function testBehaviorMisc() { + jstestIt(( + "test misc handling-behavior" + ), async function () { + // test debugInline handling-behavior + noop(debugInline); + // test assertErrorThrownAsync error handling-behavior + await assertErrorThrownAsync(function () { + return assertErrorThrownAsync(noop); + }); + // test assertJsonEqual error handling-behavior + await assertErrorThrownAsync(function () { + assertJsonEqual(1, 2); + }); + await assertErrorThrownAsync(function () { + assertJsonEqual(1, 2, "undefined"); + }); + await assertErrorThrownAsync(function () { + assertJsonEqual(1, 2, {}); + }); + // test assertOrThrow error handling-behavior + await assertErrorThrownAsync(function () { + assertOrThrow(undefined, "undefined"); + }); + await assertErrorThrownAsync(function () { + assertOrThrow(undefined, new Error()); + }); + }); +}); + +jstestDescribe(( + "test v8CoverageListMerge handling-behavior" +), function testBehaviorV8CoverageListMerge() { + let functionsInput = JSON.stringify([ + { + functionName: "test", + isBlockCoverage: true, + ranges: [ + { + count: 2, + endOffset: 4, + startOffset: 0 + }, + { + count: 1, + endOffset: 2, + startOffset: 1 + }, + { + count: 1, + endOffset: 3, + startOffset: 2 + } + ] + } + ]); + jstestIt(( + "accepts empty arrays for `v8CoverageListMerge`" + ), function () { + assertJsonEqual(v8CoverageListMerge([]), { + result: [] + }); + }); + jstestIt(( + "funcCovs.length === 1" + ), function () { + assertJsonEqual(v8CoverageListMerge([ + { + result: [ + { + functions: [ + { + functionName: "test", + isBlockCoverage: true, + ranges: [ + { + count: 2, + endOffset: 4, + startOffset: 0 + } + ] + } + ], + moduleUrl: "/lib.js", + scriptId: "1" + } + ] + }, + { + result: [ + { + functions: [], + moduleUrl: "/lib.js", + scriptId: "2" + } + ] + } + ]), { + result: [ + { + functions: [ + { + functionName: "test", + isBlockCoverage: true, + ranges: [ + { + count: 2, + endOffset: 4, + startOffset: 0 + } + ] + } + ], + scriptId: "0" + } + ] + }); + }); + jstestIt(( + "accepts arrays with a single item for `v8CoverageListMerge`" + ), function () { + assertJsonEqual(v8CoverageListMerge([ + { + result: [ + { + functions: JSON.parse(functionsInput), + moduleUrl: "/lib.js", + scriptId: "123" + } + ] + } + ]), { + result: [ + { + functions: [ + { + functionName: "test", + isBlockCoverage: true, + ranges: [ + { + count: 2, + endOffset: 4, + startOffset: 0 + }, + { + count: 1, + endOffset: 3, + startOffset: 1 + } + ] + } + ], + moduleUrl: "/lib.js", + scriptId: "0" + } + ] + }); + }); + jstestIt(( + "accepts arrays with two identical items for" + + " `v8CoverageListMerge`" + ), function () { + assertJsonEqual(v8CoverageListMerge([ + { + result: [ + { + functions: JSON.parse(functionsInput), + scriptId: "123", + url: "/lib.js" + }, { + functions: JSON.parse(functionsInput), + scriptId: "123", + url: "/lib.js" + } + ] + } + ]), { + result: [ + { + functions: [ + { + functionName: "test", + isBlockCoverage: true, + ranges: [ + { + count: 4, + endOffset: 4, + startOffset: 0 + }, + { + count: 2, + endOffset: 3, + startOffset: 1 + } + ] + } + ], + scriptId: "0", + url: "/lib.js" + } + ] + }); + }); + [ + "test_coverage_merge_is_block_coverage_test.json", + "test_coverage_merge_issue_2_mixed_is_block_coverage_test.json", + "test_coverage_merge_node_10_internal_errors_one_of_test.json", + "test_coverage_merge_reduced_test.json", + "test_coverage_merge_simple_test.json", + "test_coverage_merge_various_test.json" + ].forEach(function (file) { + jstestIt(file, function () { + file = testCoverageMergeData[file]; + file.forEach(function ({ + expected, + inputs + }) { + assertJsonEqual(v8CoverageListMerge(inputs), expected); + }); + }); + }); + jstestIt(( + "merge multiple node-sqlite coverage files" + ), function () { + let data1 = [ + "test_v8_coverage_node_sqlite_9884_1633662346346_0.json", + "test_v8_coverage_node_sqlite_13216_1633662333140_0.json" + ].map(function (file) { + return testCoverageMergeData[file]; + }); + let data2 = testCoverageMergeData[ + "test_v8_coverage_node_sqlite_merged.json" + ]; + data1 = v8CoverageListMerge(data1); + data1 = v8CoverageListMerge([data1]); + +// Debug data1. +// await moduleFs.promises.writeFile( +// ".test_v8_coverage_node_sqlite_merged.json", +// JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n" +// ); + + assertJsonEqual(data1, data2); + }); +}); + +jstestDescribe(( + "test v8CoverageReportCreate handling-behavior" +), function testBehaviorV8CoverageReportCreate() { + jstestIt(( + "test null-case handling-behavior" + ), async function () { + await assertErrorThrownAsync(function () { + return v8CoverageReportCreate({}); + }, "invalid coverageDir"); + }); + jstestIt(( + "test coverage-report jslint.mjs handling-behavior" + ), async function () { + // test remove-old-coverage handling-behavior + await fsWriteFileWithParents( + ".tmp/coverage_jslint/coverage-0-0-0.json", + "" + ); + await jslint.jslint_cli({ + console_error: noop, // comment to debug + mode_cli: true, + process_argv: [ + "node", "jslint.mjs", + "v8_coverage_report=.tmp/coverage_jslint", + "--exclude=aa.js", + "--include-node-modules=1", + "--include=jslint.mjs", + "node", "jslint.mjs" + ] + }); + }); + [ + [ + "v8CoverageReportCreate_high.js", ( + "switch(0){\n" + + "case 0:break;\n" + + "}\n" + ) + ], [ + "v8CoverageReportCreate_ignore.js", ( + "/*coverage-ignore-file*/\n" + + "switch(0){\n" + + "case 0:break;\n" + + "}\n" + ) + ], [ + "v8CoverageReportCreate_low.js", ( + "switch(0){\n" + + "case 1:break;\n" + + "case 2:break;\n" + + "case 3:break;\n" + + "case 4:break;\n" + + "}\n" + ) + ], [ + "v8CoverageReportCreate_medium.js", ( + "switch(0){\n" + + "case 0:break;\n" + + "case 1:break;\n" + + "case 2:break;\n" + + "}\n" + ) + ] + ].forEach(function ([ + file, data + ], ii) { + jstestIt(file, async function () { + let dir = ".tmp/coverage_" + ii + "/"; + file = dir + file; + await fsWriteFileWithParents(file, data); + await jslint.jslint_cli({ + console_error: noop, // comment to debug + mode_cli: true, + process_argv: [ + "node", "jslint.mjs", + "v8_coverage_report=" + dir, + "node", + file + ] + }); + }); + }); + jstestIt(( + "test npm handling-behavior" + ), async function () { + await jslint.jslint_cli({ + console_error: noop, // comment to debug + mode_cli: true, + process_argv: [ + "node", "jslint.mjs", + "v8_coverage_report=.tmp/coverage_npm", + "npm", "--version" + ] + }); + }); + jstestIt(( + "test misc handling-behavior" + ), async function () { + await Promise.all([ + [ + ".tmp/coverage_misc/aa.js", "\n".repeat(0x100) + ], [ + ".tmp/coverage_misc/coverage-0-0-0.json", JSON.stringify({ + "result": [ + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 0xf0, + "startOffset": 0x10 + }, + { + "count": 1, + "endOffset": 0x40, + "startOffset": 0x20 + }, + { + "count": 1, + "endOffset": 0x80, + "startOffset": 0x60 + }, + { + "count": 0, + "endOffset": 0x45, + "startOffset": 0x25 + }, + { + "count": 0, + "endOffset": 0x85, + "startOffset": 0x65 + } + ] + } + ], + "scriptId": "0", + "url": "file:///" + modulePath.resolve( + ".tmp/coverage_misc/aa.js" + ) + } + ] + }, undefined, 4) + ] + ].map(async function ([ + file, data + ]) { + await fsWriteFileWithParents(file, data); + })); + await jslint.jslint_cli({ + console_error: noop, // comment to debug + mode_cli: true, + process_argv: [ + "node", "jslint.mjs", + "v8_coverage_report=.tmp/coverage_misc" + // "node", ".tmp/coverage_misc/aa.js" + ] + }); + }); +}); diff --git a/test_coverage_merge_data.json b/test_coverage_merge_data.json new file mode 100644 index 000000000..bc0bb0e48 --- /dev/null +++ b/test_coverage_merge_data.json @@ -0,0 +1,17877 @@ +{ + "test_coverage_merge_is_block_coverage_test.json": [ + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Identity preserves 'isBlockCoverage: true'", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Identity preserves 'isBlockCoverage: false'", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a flat block-coverage (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a flat block-coverage (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a flat block-coverage (permutation 2)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a flat block-coverage (permutation 3)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a flat block-coverage (permutation 4)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a flat block-coverage (permutation 5)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a block-coverage with a child (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a block-coverage with a child (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a block-coverage with a child (permutation 2)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a block-coverage with a child (permutation 3)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a block-coverage with a child (permutation 4)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge two non-block coverages and a block-coverage with a child (permutation 5)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage and two flat block-coverages (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage and two flat block-coverages (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage and two flat block-coverages (permutation 2)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage and two flat block-coverages (permutation 3)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage and two flat block-coverages (permutation 4)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage and two flat block-coverages (permutation 5)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 24, + "endOffset": 6, + "startOffset": 2 + }, + { + "count": 6, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 42, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverages and two block-coverages with children (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 24, + "endOffset": 6, + "startOffset": 2 + }, + { + "count": 6, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 42, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverages and two block-coverages with children (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 24, + "endOffset": 6, + "startOffset": 2 + }, + { + "count": 6, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 42, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverages and two block-coverages with children (permutation 2)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 24, + "endOffset": 6, + "startOffset": 2 + }, + { + "count": 6, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 42, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverages and two block-coverages with children (permutation 3)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 24, + "endOffset": 6, + "startOffset": 2 + }, + { + "count": 6, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 42, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverages and two block-coverages with children (permutation 4)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 70, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 60, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 24, + "endOffset": 6, + "startOffset": 2 + }, + { + "count": 6, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 42, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverages and two block-coverages with children (permutation 5)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 50, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "count": 40, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage with a flat block-coverage with count (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 50, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "count": 40, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Merge a non-block coverage with a flat block-coverage with count (permutation 1)", + "status": "run" + } + ], + "test_coverage_merge_issue_2_mixed_is_block_coverage_test.json": [ + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 23, + "functionName": "realpathSync", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 42951, + "startOffset": 39013 + }, + { + "count": 15, + "endOffset": 39088, + "startOffset": 39069 + }, + { + "count": 5, + "endOffset": 39140, + "startOffset": 39088 + }, + { + "count": 0, + "endOffset": 39214, + "startOffset": 39196 + }, + { + "count": 5, + "endOffset": 39356, + "startOffset": 39341 + }, + { + "count": 1, + "endOffset": 39418, + "startOffset": 39383 + }, + { + "count": 19, + "endOffset": 39991, + "startOffset": 39418 + }, + { + "count": 0, + "endOffset": 40010, + "startOffset": 39991 + }, + { + "count": 0, + "endOffset": 40187, + "startOffset": 40012 + }, + { + "count": 19, + "endOffset": 40324, + "startOffset": 40187 + }, + { + "count": 427, + "endOffset": 42868, + "startOffset": 40324 + }, + { + "count": 34, + "endOffset": 40551, + "startOffset": 40436 + }, + { + "count": 393, + "endOffset": 40677, + "startOffset": 40551 + }, + { + "count": 187, + "endOffset": 40798, + "startOffset": 40760 + }, + { + "count": 28, + "endOffset": 40797, + "startOffset": 40770 + }, + { + "count": 257, + "endOffset": 40937, + "startOffset": 40800 + }, + { + "count": 0, + "endOffset": 40915, + "startOffset": 40891 + }, + { + "count": 170, + "endOffset": 40999, + "startOffset": 40937 + }, + { + "count": 11, + "endOffset": 41017, + "startOffset": 40999 + }, + { + "count": 0, + "endOffset": 41097, + "startOffset": 41048 + }, + { + "count": 170, + "endOffset": 42382, + "startOffset": 41097 + }, + { + "count": 135, + "endOffset": 41551, + "startOffset": 41450 + }, + { + "count": 11, + "endOffset": 41525, + "startOffset": 41503 + }, + { + "count": 35, + "endOffset": 41964, + "startOffset": 41551 + }, + { + "count": 13, + "endOffset": 41924, + "startOffset": 41875 + }, + { + "count": 22, + "endOffset": 42214, + "startOffset": 41964 + }, + { + "count": 34, + "endOffset": 42296, + "startOffset": 42214 + }, + { + "count": 0, + "endOffset": 42326, + "startOffset": 42296 + }, + { + "count": 34, + "endOffset": 42376, + "startOffset": 42326 + }, + { + "count": 34, + "endOffset": 42658, + "startOffset": 42382 + }, + { + "count": 0, + "endOffset": 42677, + "startOffset": 42658 + }, + { + "count": 0, + "endOffset": 42864, + "startOffset": 42679 + }, + { + "count": 18, + "endOffset": 42883, + "startOffset": 42868 + }, + { + "count": 4, + "endOffset": 42906, + "startOffset": 42883 + }, + { + "count": 18, + "endOffset": 42950, + "startOffset": 42906 + } + ] + } + ], + "scriptId": "0", + "url": "fs.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "realpathSync", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 42951, + "startOffset": 39013 + }, + { + "count": 15, + "endOffset": 39088, + "startOffset": 39069 + }, + { + "count": 5, + "endOffset": 39140, + "startOffset": 39088 + }, + { + "count": 0, + "endOffset": 39214, + "startOffset": 39196 + }, + { + "count": 5, + "endOffset": 39356, + "startOffset": 39341 + }, + { + "count": 1, + "endOffset": 39418, + "startOffset": 39383 + }, + { + "count": 19, + "endOffset": 39991, + "startOffset": 39418 + }, + { + "count": 0, + "endOffset": 40010, + "startOffset": 39991 + }, + { + "count": 0, + "endOffset": 40187, + "startOffset": 40012 + }, + { + "count": 19, + "endOffset": 40324, + "startOffset": 40187 + }, + { + "count": 427, + "endOffset": 42868, + "startOffset": 40324 + }, + { + "count": 34, + "endOffset": 40551, + "startOffset": 40436 + }, + { + "count": 393, + "endOffset": 40677, + "startOffset": 40551 + }, + { + "count": 187, + "endOffset": 40798, + "startOffset": 40760 + }, + { + "count": 28, + "endOffset": 40797, + "startOffset": 40770 + }, + { + "count": 257, + "endOffset": 40937, + "startOffset": 40800 + }, + { + "count": 0, + "endOffset": 40915, + "startOffset": 40891 + }, + { + "count": 170, + "endOffset": 40999, + "startOffset": 40937 + }, + { + "count": 11, + "endOffset": 41017, + "startOffset": 40999 + }, + { + "count": 0, + "endOffset": 41097, + "startOffset": 41048 + }, + { + "count": 170, + "endOffset": 42382, + "startOffset": 41097 + }, + { + "count": 135, + "endOffset": 41551, + "startOffset": 41450 + }, + { + "count": 11, + "endOffset": 41525, + "startOffset": 41503 + }, + { + "count": 35, + "endOffset": 41932, + "startOffset": 41551 + }, + { + "count": 13, + "endOffset": 41924, + "startOffset": 41875 + }, + { + "count": 35, + "endOffset": 41964, + "startOffset": 41932 + }, + { + "count": 22, + "endOffset": 42214, + "startOffset": 41964 + }, + { + "count": 34, + "endOffset": 42296, + "startOffset": 42214 + }, + { + "count": 0, + "endOffset": 42326, + "startOffset": 42296 + }, + { + "count": 34, + "endOffset": 42376, + "startOffset": 42326 + }, + { + "count": 34, + "endOffset": 42658, + "startOffset": 42382 + }, + { + "count": 0, + "endOffset": 42677, + "startOffset": 42658 + }, + { + "count": 0, + "endOffset": 42864, + "startOffset": 42679 + }, + { + "count": 18, + "endOffset": 42883, + "startOffset": 42868 + }, + { + "count": 4, + "endOffset": 42906, + "startOffset": 42883 + }, + { + "count": 18, + "endOffset": 42950, + "startOffset": 42906 + } + ] + } + ], + "scriptId": "48", + "url": "fs.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "realpathSync", + "isBlockCoverage": false, + "ranges": [ + { + "count": 3, + "endOffset": 42951, + "startOffset": 39013 + } + ] + } + ], + "scriptId": "48", + "url": "fs.js" + } + ] + } + ], + "name": "Issue 2: Mixed isBlockCoverage (https://github.com/demurgos/v8-coverage/issues/2)", + "status": "run" + } + ], + "test_coverage_merge_node_10_internal_errors_one_of_test.json": [ + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2516, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 719, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 718, + "endOffset": 148299, + "startOffset": 147892 + }, + { + "count": 537, + "endOffset": 148155, + "startOffset": 147892 + }, + { + "count": 530, + "endOffset": 148061, + "startOffset": 147892 + }, + { + "count": 2, + "endOffset": 148061, + "startOffset": 148055 + }, + { + "count": 2, + "endOffset": 148155, + "startOffset": 148061 + }, + { + "count": 188, + "endOffset": 148299, + "startOffset": 148155 + }, + { + "count": 2, + "endOffset": 148299, + "startOffset": 148291 + }, + { + "count": 38, + "endOffset": 148437, + "startOffset": 148299 + }, + { + "count": 5, + "endOffset": 148433, + "startOffset": 148299 + }, + { + "count": 4, + "endOffset": 148427, + "startOffset": 148299 + }, + { + "count": 2, + "endOffset": 148433, + "startOffset": 148427 + }, + { + "count": 2, + "endOffset": 148437, + "startOffset": 148433 + }, + { + "count": 2019, + "endOffset": 148555, + "startOffset": 148437 + }, + { + "count": 1799, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 2, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 148556, + "startOffset": 147273 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1453, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 4, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 1, + "endOffset": 148061, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148061, + "startOffset": 148055 + }, + { + "count": 2, + "endOffset": 148433, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148155, + "startOffset": 148061 + }, + { + "count": 1, + "endOffset": 148299, + "startOffset": 148155 + }, + { + "count": 0, + "endOffset": 148299, + "startOffset": 148291 + }, + { + "count": 1, + "endOffset": 148427, + "startOffset": 148299 + }, + { + "count": 0, + "endOffset": 148433, + "startOffset": 148427 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 148433 + }, + { + "count": 1, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 28, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 2, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 1, + "endOffset": 148061, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148061, + "startOffset": 148055 + }, + { + "count": 1, + "endOffset": 148433, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148155, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148433, + "startOffset": 148291 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 148433 + }, + { + "count": 26, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 1, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 0, + "endOffset": 148299, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 148427 + }, + { + "count": 1, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 173, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 33, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 0, + "endOffset": 148155, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 148291 + }, + { + "count": 140, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 313, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 154, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 148055 + }, + { + "count": 159, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 43, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 26, + "endOffset": 148437, + "startOffset": 147492 + }, + { + "count": 24, + "endOffset": 148061, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148061, + "startOffset": 148055 + }, + { + "count": 2, + "endOffset": 148433, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148155, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148433, + "startOffset": 148291 + }, + { + "count": 0, + "endOffset": 148437, + "startOffset": 148433 + }, + { + "count": 17, + "endOffset": 148554, + "startOffset": 148437 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148554 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 148, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 0, + "endOffset": 148155, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148291 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 2, + "endOffset": 148061, + "startOffset": 147892 + }, + { + "count": 0, + "endOffset": 148061, + "startOffset": 148055 + }, + { + "count": 1, + "endOffset": 148433, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148155, + "startOffset": 148061 + }, + { + "count": 0, + "endOffset": 148433, + "startOffset": 148291 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148433 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 346, + "endOffset": 148556, + "startOffset": 147273 + }, + { + "count": 0, + "endOffset": 148555, + "startOffset": 148055 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "node@10.11.0: internal/errors.js#oneOf", + "status": "run" + } + ], + "test_coverage_merge_reduced_test.json": [ + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1962, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 332, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 331, + "endOffset": 4, + "startOffset": 2 + }, + { + "count": 329, + "endOffset": 4, + "startOffset": 3 + }, + { + "count": 30, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 4 + }, + { + "count": 29, + "endOffset": 6, + "startOffset": 5 + }, + { + "count": 1814, + "endOffset": 7, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1811, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 181, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 180, + "endOffset": 4, + "startOffset": 2 + }, + { + "count": 27, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 4 + }, + { + "count": 26, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 148, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 0, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "node@10.11.0: internal/errors.js#oneOf: reduced", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1962, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 332, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 331, + "endOffset": 4, + "startOffset": 2 + }, + { + "count": 329, + "endOffset": 4, + "startOffset": 3 + }, + { + "count": 30, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 4 + }, + { + "count": 29, + "endOffset": 6, + "startOffset": 5 + }, + { + "count": 1814, + "endOffset": 7, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1811, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 181, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 180, + "endOffset": 4, + "startOffset": 2 + }, + { + "count": 27, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 4 + }, + { + "count": 26, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 148, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 0, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "node@10.11.0: internal/errors.js#oneOf: reduced with hint", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 66, + "endOffset": 4, + "startOffset": 2 + }, + { + "count": 30, + "endOffset": 4, + "startOffset": 3 + }, + { + "count": 43, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 4 + }, + { + "count": 52, + "endOffset": 7, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 6, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "node@10.11.0: internal/errors.js#oneOf: minimal", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 66, + "endOffset": 4, + "startOffset": 2 + }, + { + "count": 30, + "endOffset": 4, + "startOffset": 3 + }, + { + "count": 43, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 4 + }, + { + "count": 52, + "endOffset": 7, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 6, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 8, + "startOffset": 0 + }, + { + "count": 40, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "node@10.11.0: internal/errors.js#oneOf: minimal with hint", + "status": "run" + } + ], + "test_coverage_merge_simple_test.json": [ + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "No children", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "One child", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Equal", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 3, + "startOffset": 1 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 3, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (BeforeAdjacent)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 2, + "startOffset": 1 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 2, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (BeforeStrict)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 12, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (AfterAdjacent)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 12, + "endOffset": 8, + "startOffset": 7 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 7 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (AfterStrict)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Nested (OverlapStrict)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 6, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Nested (OverlapEqualEnd)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 8, + "startOffset": 3 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Nested (OverlapEqualStart)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Nested (ContainedEqualStart)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 6, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Nested (ContainedEqualEnd)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Nested (ContainedStrict)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Partial overlap (PartialOverlapStart)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 3, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 12, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Partial overlap (PartialOverlapEnd)", + "status": "run" + } + ], + "test_coverage_merge_various_test.json": [ + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three empty trees", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 3, + "endOffset": 3, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 3, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 3, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Equal children, same startOffset as parents", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 3, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Equal children, same endOffset as parents", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 3, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 3, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (strict) children, offsets matching parents (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 3, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 9, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 3, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (strict) children, offsets matching parents (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 4, + "startOffset": 1 + }, + { + "count": 12, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (adjacent) children (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 4, + "startOffset": 1 + }, + { + "count": 12, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 7, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Disjoint (adjacent) children (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 2 left & 1 right (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 2 left & 1 right (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 2 left & 1 right (permutation 2)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 2 left & 1 right (permutation 3)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 2 left & 1 right (permutation 4)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 5, + "startOffset": 3 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 2 left & 1 right (permutation 5)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 7, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 16, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 1 left & 2 right (permutation 0)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 7, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 16, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 1 left & 2 right (permutation 1)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 7, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 16, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 1 left & 2 right (permutation 2)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 7, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 16, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 1 left & 2 right (permutation 3)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 7, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 16, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 1 left & 2 right (permutation 4)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 70, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 61, + "endOffset": 6, + "startOffset": 3 + }, + { + "count": 7, + "endOffset": 6, + "startOffset": 4 + }, + { + "count": 16, + "endOffset": 8, + "startOffset": 6 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 8, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Three trees: Partial overlap: 1 left & 2 right (permutation 5)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 13, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 10, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 9, + "endOffset": 8, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 6, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 7, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 8, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Normalization: two trees with complementary children (not summing to the same count)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 14, + "endOffset": 8, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 6, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 5, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 8, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 8, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 9, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 7, + "endOffset": 8, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Normalization: two trees with complementary children (summing to the same count, same as parent)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 10, + "endOffset": 8, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 6, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 9, + "startOffset": 0 + }, + { + "count": 7, + "endOffset": 8, + "startOffset": 1 + }, + { + "count": 4, + "endOffset": 8, + "startOffset": 5 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "Normalization: two trees with complementary children (summing to the same count, different from parent)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 5, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 4, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: a+b", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 5, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 4, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: b+a", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 6, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: a+c", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: b+c", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 5, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 4, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: (a+b)+c", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 6, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: (a+c)+b", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 11, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: a+(b+c)", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 6, + "startOffset": 0 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 1 + }, + { + "count": 3, + "endOffset": 4, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: a+b+c", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "count": 40, + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 31, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 22, + "endOffset": 6, + "startOffset": 1 + }, + { + "count": 13, + "endOffset": 5, + "startOffset": 2 + }, + { + "count": 4, + "endOffset": 4, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 6, + "startOffset": 2 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 7, + "startOffset": 3 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a similar sliding chain: a+b+c+d", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 4, + "startOffset": 0 + }, + { + "count": 3, + "endOffset": 4, + "startOffset": 1 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a tagged sliding chain: a+b", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 30, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 21, + "endOffset": 4, + "startOffset": 0 + }, + { + "count": 3, + "endOffset": 4, + "startOffset": 1 + }, + { + "count": 12, + "endOffset": 5, + "startOffset": 4 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 20, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 5, + "startOffset": 1 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 7, + "startOffset": 0 + }, + { + "count": 1, + "endOffset": 4, + "startOffset": 0 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges a tagged sliding chain: a+b", + "status": "run" + }, + { + "expected": { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 15, + "endOffset": 16, + "startOffset": 0 + }, + { + "count": 20, + "endOffset": 4, + "startOffset": 0 + }, + { + "count": 18, + "endOffset": 1, + "startOffset": 0 + }, + { + "count": 16, + "endOffset": 2, + "startOffset": 1 + }, + { + "count": 17, + "endOffset": 4, + "startOffset": 3 + }, + { + "count": 12, + "endOffset": 11, + "startOffset": 4 + }, + { + "count": 13, + "endOffset": 8, + "startOffset": 5 + }, + { + "count": 16, + "endOffset": 7, + "startOffset": 6 + }, + { + "count": 9, + "endOffset": 8, + "startOffset": 7 + }, + { + "count": 11, + "endOffset": 11, + "startOffset": 9 + }, + { + "count": 19, + "endOffset": 11, + "startOffset": 10 + }, + { + "count": 23, + "endOffset": 15, + "startOffset": 11 + }, + { + "count": 22, + "endOffset": 12, + "startOffset": 11 + }, + { + "count": 16, + "endOffset": 14, + "startOffset": 13 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + "inputs": [ + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 16, + "startOffset": 0 + }, + { + "count": 6, + "endOffset": 4, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 2, + "startOffset": 1 + }, + { + "count": 4, + "endOffset": 7, + "startOffset": 6 + }, + { + "count": 9, + "endOffset": 15, + "startOffset": 10 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 16, + "startOffset": 0 + }, + { + "count": 2, + "endOffset": 1, + "startOffset": 0 + }, + { + "count": 5, + "endOffset": 8, + "startOffset": 5 + }, + { + "count": 3, + "endOffset": 12, + "startOffset": 9 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + }, + { + "result": [ + { + "functions": [ + { + "functionName": "test", + "isBlockCoverage": true, + "ranges": [ + { + "count": 10, + "endOffset": 16, + "startOffset": 0 + }, + { + "count": 7, + "endOffset": 11, + "startOffset": 3 + }, + { + "count": 3, + "endOffset": 8, + "startOffset": 7 + }, + { + "count": 3, + "endOffset": 14, + "startOffset": 13 + } + ] + } + ], + "scriptId": "0", + "url": "/lib.js" + } + ] + } + ], + "name": "merges three trees with a complex relation (chains, nesting)", + "status": "run" + } + ], + "test_v8_coverage_node_sqlite_13216_1633662333140_0.json": { + "result": [ + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 20604, + "startOffset": 0 + } + ] + }, + { + "functionName": "Console", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4925, + "startOffset": 2686 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5200, + "startOffset": 5144 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5669, + "startOffset": 5458 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 6489, + "startOffset": 5878 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 6165, + "startOffset": 6067 + }, + { + "count": 1, + "endOffset": 6124, + "startOffset": 6101 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6208, + "startOffset": 6178 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6417, + "startOffset": 6315 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6460, + "startOffset": 6430 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 7776, + "startOffset": 6563 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9334, + "startOffset": 7850 + }, + { + "count": 0, + "endOffset": 8095, + "startOffset": 8081 + }, + { + "count": 0, + "endOffset": 8197, + "startOffset": 8171 + }, + { + "count": 0, + "endOffset": 8432, + "startOffset": 8238 + }, + { + "count": 0, + "endOffset": 8521, + "startOffset": 8493 + }, + { + "count": 0, + "endOffset": 9261, + "startOffset": 8971 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9918, + "startOffset": 9411 + }, + { + "count": 0, + "endOffset": 9635, + "startOffset": 9629 + }, + { + "count": 0, + "endOffset": 9840, + "startOffset": 9714 + }, + { + "count": 0, + "endOffset": 9910, + "startOffset": 9886 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 10124, + "startOffset": 9993 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10330, + "startOffset": 10199 + } + ] + }, + { + "functionName": "createWriteErrorHandler", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 11331, + "startOffset": 10424 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 11327, + "startOffset": 10493 + }, + { + "count": 0, + "endOffset": 10786, + "startOffset": 10768 + } + ] + }, + { + "functionName": "log", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 11452, + "startOffset": 11363 + } + ] + }, + { + "functionName": "warn", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11551, + "startOffset": 11461 + } + ] + }, + { + "functionName": "dir", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11751, + "startOffset": 11560 + } + ] + }, + { + "functionName": "time", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12125, + "startOffset": 11758 + } + ] + }, + { + "functionName": "timeEnd", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12429, + "startOffset": 12132 + } + ] + }, + { + "functionName": "timeLog", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12679, + "startOffset": 12436 + } + ] + }, + { + "functionName": "trace", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12886, + "startOffset": 12693 + } + ] + }, + { + "functionName": "assert", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 13116, + "startOffset": 12893 + } + ] + }, + { + "functionName": "clear", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 13620, + "startOffset": 13180 + } + ] + }, + { + "functionName": "count", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14153, + "startOffset": 13684 + } + ] + }, + { + "functionName": "countReset", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14518, + "startOffset": 14222 + } + ] + }, + { + "functionName": "group", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14699, + "startOffset": 14525 + } + ] + }, + { + "functionName": "groupEnd", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14880, + "startOffset": 14706 + } + ] + }, + { + "functionName": "table", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18454, + "startOffset": 14932 + } + ] + }, + { + "functionName": "timeLogImpl", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19012, + "startOffset": 18499 + } + ] + }, + { + "functionName": "pad", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19095, + "startOffset": 19016 + } + ] + }, + { + "functionName": "formatTime", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19892, + "startOffset": 19099 + } + ] + }, + { + "functionName": "isArray", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20089, + "startOffset": 20033 + } + ] + }, + { + "functionName": "noop", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20112, + "startOffset": 20094 + } + ] + } + ], + "scriptId": "28", + "url": "internal/console/constructor.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1638, + "startOffset": 0 + } + ] + } + ], + "scriptId": "29", + "url": "internal/constants.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 2283, + "startOffset": 0 + } + ] + }, + { + "functionName": "sendInspectorCommand", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 453, + "startOffset": 100 + } + ] + }, + { + "functionName": "installConsoleExtensions", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1094, + "startOffset": 530 + } + ] + }, + { + "functionName": "wrapConsole", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 1984, + "startOffset": 1176 + }, + { + "count": 23, + "endOffset": 1981, + "startOffset": 1336 + }, + { + "count": 19, + "endOffset": 1855, + "startOffset": 1555 + }, + { + "count": 4, + "endOffset": 1976, + "startOffset": 1855 + } + ] + }, + { + "functionName": "get consoleFromVM", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2217, + "startOffset": 2164 + } + ] + }, + { + "functionName": "set consoleFromVM", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 2277, + "startOffset": 2222 + } + ] + } + ], + "scriptId": "30", + "url": "internal/util/inspector.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 42795, + "startOffset": 0 + } + ] + }, + { + "functionName": "toUSVString", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2627, + "startOffset": 2323 + } + ] + }, + { + "functionName": "serializeTupleOrigin", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2965, + "startOffset": 2845 + } + ] + }, + { + "functionName": "URLContext", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 3611, + "startOffset": 3378 + } + ] + }, + { + "functionName": "URLSearchParams", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 6333, + "startOffset": 3909 + }, + { + "count": 0, + "endOffset": 6267, + "startOffset": 4027 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7439, + "startOffset": 6339 + } + ] + }, + { + "functionName": "onParseComplete", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 8156, + "startOffset": 7446 + }, + { + "count": 0, + "endOffset": 7716, + "startOffset": 7706 + }, + { + "count": 0, + "endOffset": 7790, + "startOffset": 7780 + }, + { + "count": 0, + "endOffset": 7877, + "startOffset": 7873 + } + ] + }, + { + "functionName": "onParseError", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8237, + "startOffset": 8160 + } + ] + }, + { + "functionName": "onParseProtocolComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8596, + "startOffset": 8241 + } + ] + }, + { + "functionName": "onParseHostnameComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8956, + "startOffset": 8600 + } + ] + }, + { + "functionName": "onParsePortComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9125, + "startOffset": 8960 + } + ] + }, + { + "functionName": "onParseHostComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9440, + "startOffset": 9129 + } + ] + }, + { + "functionName": "onParsePathComplete", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 9954, + "startOffset": 9444 + }, + { + "count": 0, + "endOffset": 9787, + "startOffset": 9716 + } + ] + }, + { + "functionName": "onParseSearchComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10129, + "startOffset": 9958 + } + ] + }, + { + "functionName": "onParseHashComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10306, + "startOffset": 10133 + } + ] + }, + { + "functionName": "URL", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 10663, + "startOffset": 10325 + }, + { + "count": 0, + "endOffset": 10518, + "startOffset": 10464 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10752, + "startOffset": 10669 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 327, + "endOffset": 10853, + "startOffset": 10758 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11136, + "startOffset": 10931 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12107, + "startOffset": 11142 + } + ] + }, + { + "functionName": "format", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 13502, + "startOffset": 12284 + }, + { + "count": 0, + "endOffset": 12432, + "startOffset": 12371 + }, + { + "count": 0, + "endOffset": 13023, + "startOffset": 12856 + }, + { + "count": 0, + "endOffset": 13094, + "startOffset": 13056 + }, + { + "count": 0, + "endOffset": 13172, + "startOffset": 13150 + }, + { + "count": 0, + "endOffset": 13247, + "startOffset": 13181 + }, + { + "count": 0, + "endOffset": 13386, + "startOffset": 13363 + }, + { + "count": 0, + "endOffset": 13476, + "startOffset": 13450 + } + ] + }, + { + "functionName": "toString", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 13849, + "startOffset": 13788 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 13967, + "startOffset": 13920 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14159, + "startOffset": 13974 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14895, + "startOffset": 14245 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15020, + "startOffset": 14970 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15408, + "startOffset": 15027 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15535, + "startOffset": 15483 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15953, + "startOffset": 15542 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16080, + "startOffset": 16028 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16498, + "startOffset": 16087 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16731, + "startOffset": 16569 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17038, + "startOffset": 16738 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17167, + "startOffset": 17113 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17482, + "startOffset": 17174 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17656, + "startOffset": 17553 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17991, + "startOffset": 17663 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": true, + "ranges": [ + { + "count": 218, + "endOffset": 18268, + "startOffset": 18066 + }, + { + "count": 0, + "endOffset": 18167, + "startOffset": 18148 + }, + { + "count": 0, + "endOffset": 18221, + "startOffset": 18211 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 18501, + "startOffset": 18275 + }, + { + "count": 0, + "endOffset": 18396, + "startOffset": 18389 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18718, + "startOffset": 18574 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19232, + "startOffset": 18725 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19372, + "startOffset": 19324 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19599, + "startOffset": 19443 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20037, + "startOffset": 19606 + } + ] + }, + { + "functionName": "toJSON", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20249, + "startOffset": 20190 + } + ] + }, + { + "functionName": "update", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20581, + "startOffset": 20263 + } + ] + }, + { + "functionName": "initSearchParams", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 20731, + "startOffset": 20585 + }, + { + "count": 0, + "endOffset": 20730, + "startOffset": 20686 + } + ] + }, + { + "functionName": "parseParams", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 23241, + "startOffset": 20844 + } + ] + }, + { + "functionName": "serializeParams", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 24820, + "startOffset": 24244 + } + ] + }, + { + "functionName": "defineIDLClass", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 25592, + "startOffset": 24878 + }, + { + "count": 13, + "endOffset": 25379, + "startOffset": 25226 + }, + { + "count": 1, + "endOffset": 25589, + "startOffset": 25435 + } + ] + }, + { + "functionName": "merge", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 26271, + "startOffset": 25615 + } + ] + }, + { + "functionName": "append", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 26740, + "startOffset": 26341 + } + ] + }, + { + "functionName": "delete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27265, + "startOffset": 26747 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27722, + "startOffset": 27272 + } + ] + }, + { + "functionName": "getAll", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 28214, + "startOffset": 27729 + } + ] + }, + { + "functionName": "has", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 28665, + "startOffset": 28221 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 29736, + "startOffset": 28672 + } + ] + }, + { + "functionName": "sort", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 30962, + "startOffset": 29743 + } + ] + }, + { + "functionName": "entries", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31345, + "startOffset": 31130 + } + ] + }, + { + "functionName": "forEach", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31943, + "startOffset": 31352 + } + ] + }, + { + "functionName": "keys", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 32207, + "startOffset": 32001 + } + ] + }, + { + "functionName": "values", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 32424, + "startOffset": 32214 + } + ] + }, + { + "functionName": "toString", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 32767, + "startOffset": 32561 + } + ] + }, + { + "functionName": "createSearchParamsIterator", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33269, + "startOffset": 33058 + } + ] + }, + { + "functionName": "next", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 34289, + "startOffset": 33499 + } + ] + }, + { + "functionName": "defineIDLClass", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 35544, + "startOffset": 34294 + } + ] + }, + { + "functionName": "domainToASCII", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 35734, + "startOffset": 35553 + } + ] + }, + { + "functionName": "domainToUnicode", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 35923, + "startOffset": 35738 + } + ] + }, + { + "functionName": "urlToOptions", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36633, + "startOffset": 36071 + } + ] + }, + { + "functionName": "getPathFromURLWin32", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 38099, + "startOffset": 36673 + } + ] + }, + { + "functionName": "getPathFromURLPosix", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 38623, + "startOffset": 38103 + } + ] + }, + { + "functionName": "fileURLToPath", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 38982, + "startOffset": 38627 + } + ] + }, + { + "functionName": "encodePathChars", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 40330, + "startOffset": 39761 + }, + { + "count": 0, + "endOffset": 39883, + "startOffset": 39834 + }, + { + "count": 0, + "endOffset": 39985, + "startOffset": 39959 + }, + { + "count": 0, + "endOffset": 40043, + "startOffset": 39992 + }, + { + "count": 0, + "endOffset": 40130, + "startOffset": 40081 + }, + { + "count": 0, + "endOffset": 40224, + "startOffset": 40168 + }, + { + "count": 0, + "endOffset": 40307, + "startOffset": 40262 + } + ] + }, + { + "functionName": "pathToFileURL", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 41482, + "startOffset": 40334 + }, + { + "count": 0, + "endOffset": 41025, + "startOffset": 40456 + }, + { + "count": 0, + "endOffset": 41381, + "startOffset": 41327 + }, + { + "count": 0, + "endOffset": 41406, + "startOffset": 41390 + } + ] + }, + { + "functionName": "isURLInstance", + "isBlockCoverage": true, + "ranges": [ + { + "count": 277, + "endOffset": 41607, + "startOffset": 41486 + }, + { + "count": 0, + "endOffset": 41603, + "startOffset": 41580 + } + ] + }, + { + "functionName": "toPathIfFileURL", + "isBlockCoverage": true, + "ranges": [ + { + "count": 277, + "endOffset": 41760, + "startOffset": 41611 + }, + { + "count": 0, + "endOffset": 41759, + "startOffset": 41717 + } + ] + }, + { + "functionName": "constructUrl", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 42484, + "startOffset": 41764 + } + ] + } + ], + "scriptId": "31", + "url": "internal/url.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 3134, + "startOffset": 0 + } + ] + }, + { + "functionName": "encodeStr", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3065, + "startOffset": 1374 + } + ] + } + ], + "scriptId": "32", + "url": "internal/querystring.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 44873, + "startOffset": 0 + } + ] + }, + { + "functionName": "isPathSeparator", + "isBlockCoverage": true, + "ranges": [ + { + "count": 144876, + "endOffset": 1635, + "startOffset": 1529 + }, + { + "count": 144646, + "endOffset": 1631, + "startOffset": 1600 + } + ] + }, + { + "functionName": "isPosixPathSeparator", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1718, + "startOffset": 1639 + } + ] + }, + { + "functionName": "isWindowsDeviceRoot", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5087, + "endOffset": 1895, + "startOffset": 1722 + }, + { + "count": 4801, + "endOffset": 1822, + "startOffset": 1795 + }, + { + "count": 1027, + "endOffset": 1891, + "startOffset": 1824 + }, + { + "count": 741, + "endOffset": 1890, + "startOffset": 1863 + } + ] + }, + { + "functionName": "normalizeString", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2284, + "endOffset": 3826, + "startOffset": 1961 + }, + { + "count": 131113, + "endOffset": 3808, + "startOffset": 2184 + }, + { + "count": 128829, + "endOffset": 2245, + "startOffset": 2219 + }, + { + "count": 2284, + "endOffset": 2340, + "startOffset": 2245 + }, + { + "count": 2279, + "endOffset": 2296, + "startOffset": 2290 + }, + { + "count": 5, + "endOffset": 2340, + "startOffset": 2296 + }, + { + "count": 128834, + "endOffset": 2375, + "startOffset": 2340 + }, + { + "count": 12268, + "endOffset": 3704, + "startOffset": 2375 + }, + { + "count": 11644, + "endOffset": 2421, + "startOffset": 2408 + }, + { + "count": 724, + "endOffset": 2450, + "startOffset": 2423 + }, + { + "count": 11544, + "endOffset": 3658, + "startOffset": 2450 + }, + { + "count": 31, + "endOffset": 3439, + "startOffset": 2472 + }, + { + "count": 0, + "endOffset": 2588, + "startOffset": 2529 + }, + { + "count": 0, + "endOffset": 2648, + "startOffset": 2589 + }, + { + "count": 0, + "endOffset": 2868, + "startOffset": 2789 + }, + { + "count": 0, + "endOffset": 3283, + "startOffset": 3108 + }, + { + "count": 0, + "endOffset": 3430, + "startOffset": 3294 + }, + { + "count": 11513, + "endOffset": 3658, + "startOffset": 3439 + }, + { + "count": 9341, + "endOffset": 3540, + "startOffset": 3487 + }, + { + "count": 2172, + "endOffset": 3601, + "startOffset": 3540 + }, + { + "count": 12237, + "endOffset": 3704, + "startOffset": 3658 + }, + { + "count": 116566, + "endOffset": 3803, + "startOffset": 3704 + }, + { + "count": 1642, + "endOffset": 3746, + "startOffset": 3732 + }, + { + "count": 165, + "endOffset": 3771, + "startOffset": 3748 + }, + { + "count": 116401, + "endOffset": 3803, + "startOffset": 3771 + } + ] + }, + { + "functionName": "_format", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4453, + "startOffset": 4017 + } + ] + }, + { + "functionName": "resolve", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2279, + "endOffset": 9115, + "startOffset": 4579 + }, + { + "count": 3106, + "endOffset": 8648, + "startOffset": 4742 + }, + { + "count": 3105, + "endOffset": 4953, + "startOffset": 4780 + }, + { + "count": 0, + "endOffset": 4944, + "startOffset": 4911 + }, + { + "count": 1, + "endOffset": 5845, + "startOffset": 4953 + }, + { + "count": 0, + "endOffset": 5845, + "startOffset": 5033 + }, + { + "count": 0, + "endOffset": 6218, + "startOffset": 6053 + }, + { + "count": 0, + "endOffset": 7598, + "startOffset": 6251 + }, + { + "count": 2975, + "endOffset": 7689, + "startOffset": 7634 + }, + { + "count": 2279, + "endOffset": 8015, + "startOffset": 7691 + }, + { + "count": 2279, + "endOffset": 8333, + "startOffset": 8048 + }, + { + "count": 0, + "endOffset": 8270, + "startOffset": 8090 + }, + { + "count": 0, + "endOffset": 8433, + "startOffset": 8365 + }, + { + "count": 2279, + "endOffset": 8600, + "startOffset": 8572 + }, + { + "count": 2279, + "endOffset": 8632, + "startOffset": 8602 + }, + { + "count": 0, + "endOffset": 9109, + "startOffset": 9059 + } + ] + }, + { + "functionName": "normalize", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 12023, + "startOffset": 9187 + }, + { + "count": 0, + "endOffset": 9308, + "startOffset": 9297 + }, + { + "count": 0, + "endOffset": 9622, + "startOffset": 9465 + }, + { + "count": 0, + "endOffset": 11204, + "startOffset": 9655 + }, + { + "count": 4, + "endOffset": 11582, + "startOffset": 11278 + }, + { + "count": 0, + "endOffset": 11708, + "startOffset": 11697 + }, + { + "count": 0, + "endOffset": 11751, + "startOffset": 11737 + }, + { + "count": 0, + "endOffset": 11771, + "startOffset": 11760 + }, + { + "count": 0, + "endOffset": 11863, + "startOffset": 11850 + }, + { + "count": 1, + "endOffset": 11950, + "startOffset": 11895 + }, + { + "count": 0, + "endOffset": 11935, + "startOffset": 11922 + }, + { + "count": 4, + "endOffset": 11996, + "startOffset": 11950 + }, + { + "count": 0, + "endOffset": 12017, + "startOffset": 11997 + } + ] + }, + { + "functionName": "isAbsolute", + "isBlockCoverage": true, + "ranges": [ + { + "count": 312, + "endOffset": 12477, + "startOffset": 12096 + }, + { + "count": 0, + "endOffset": 12220, + "startOffset": 12207 + }, + { + "count": 311, + "endOffset": 12380, + "startOffset": 12345 + }, + { + "count": 156, + "endOffset": 12424, + "startOffset": 12381 + }, + { + "count": 112, + "endOffset": 12470, + "startOffset": 12425 + } + ] + }, + { + "functionName": "join", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 14671, + "startOffset": 12552 + }, + { + "count": 0, + "endOffset": 12614, + "startOffset": 12603 + }, + { + "count": 11, + "endOffset": 12926, + "startOffset": 12697 + }, + { + "count": 5, + "endOffset": 12863, + "startOffset": 12838 + }, + { + "count": 6, + "endOffset": 12910, + "startOffset": 12863 + }, + { + "count": 0, + "endOffset": 12978, + "startOffset": 12967 + }, + { + "count": 0, + "endOffset": 14271, + "startOffset": 13851 + }, + { + "count": 0, + "endOffset": 14499, + "startOffset": 14466 + }, + { + "count": 0, + "endOffset": 14620, + "startOffset": 14579 + } + ] + }, + { + "functionName": "relative", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18564, + "startOffset": 14979 + } + ] + }, + { + "functionName": "toNamespacedPath", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1064, + "endOffset": 19630, + "startOffset": 18571 + }, + { + "count": 0, + "endOffset": 18702, + "startOffset": 18690 + }, + { + "count": 0, + "endOffset": 18759, + "startOffset": 18733 + }, + { + "count": 0, + "endOffset": 18865, + "startOffset": 18853 + }, + { + "count": 0, + "endOffset": 19300, + "startOffset": 18929 + }, + { + "count": 0, + "endOffset": 19629, + "startOffset": 19605 + } + ] + }, + { + "functionName": "dirname", + "isBlockCoverage": true, + "ranges": [ + { + "count": 489, + "endOffset": 22114, + "startOffset": 19702 + }, + { + "count": 0, + "endOffset": 19821, + "startOffset": 19810 + }, + { + "count": 0, + "endOffset": 20090, + "startOffset": 19926 + }, + { + "count": 0, + "endOffset": 21455, + "startOffset": 20153 + }, + { + "count": 0, + "endOffset": 21602, + "startOffset": 21599 + }, + { + "count": 5638, + "endOffset": 21973, + "startOffset": 21732 + }, + { + "count": 489, + "endOffset": 21871, + "startOffset": 21782 + }, + { + "count": 5149, + "endOffset": 21966, + "startOffset": 21871 + }, + { + "count": 0, + "endOffset": 22077, + "startOffset": 21997 + } + ] + }, + { + "functionName": "basename", + "isBlockCoverage": true, + "ranges": [ + { + "count": 112, + "endOffset": 24930, + "startOffset": 22214 + }, + { + "count": 0, + "endOffset": 22298, + "startOffset": 22271 + }, + { + "count": 0, + "endOffset": 22778, + "startOffset": 22761 + }, + { + "count": 0, + "endOffset": 22807, + "startOffset": 22779 + }, + { + "count": 0, + "endOffset": 24318, + "startOffset": 22809 + }, + { + "count": 1309, + "endOffset": 24848, + "startOffset": 24371 + }, + { + "count": 112, + "endOffset": 24652, + "startOffset": 24421 + }, + { + "count": 1197, + "endOffset": 24841, + "startOffset": 24652 + }, + { + "count": 112, + "endOffset": 24841, + "startOffset": 24674 + }, + { + "count": 0, + "endOffset": 24889, + "startOffset": 24879 + } + ] + }, + { + "functionName": "extname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27073, + "startOffset": 25002 + } + ] + }, + { + "functionName": "parse", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31903, + "startOffset": 27291 + } + ] + }, + { + "functionName": "resolve", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33025, + "startOffset": 32100 + } + ] + }, + { + "functionName": "normalize", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33694, + "startOffset": 33097 + } + ] + }, + { + "functionName": "isAbsolute", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33899, + "startOffset": 33767 + } + ] + }, + { + "functionName": "join", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 34405, + "startOffset": 33974 + } + ] + }, + { + "functionName": "relative", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36780, + "startOffset": 34502 + } + ] + }, + { + "functionName": "toNamespacedPath", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36866, + "startOffset": 36787 + } + ] + }, + { + "functionName": "dirname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 37590, + "startOffset": 36938 + } + ] + }, + { + "functionName": "basename", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 40083, + "startOffset": 37690 + } + ] + }, + { + "functionName": "extname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 41863, + "startOffset": 40155 + } + ] + }, + { + "functionName": "parse", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 44518, + "startOffset": 42086 + } + ] + } + ], + "scriptId": "33", + "url": "path.js" + } + ], + "timestamp": 967695.999409 + }, + "test_v8_coverage_node_sqlite_9884_1633662346346_0.json": { + "result": [ + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 3134, + "startOffset": 0 + } + ] + }, + { + "functionName": "encodeStr", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3065, + "startOffset": 1374 + } + ] + } + ], + "scriptId": "32", + "url": "internal/querystring.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 44873, + "startOffset": 0 + } + ] + }, + { + "functionName": "isPathSeparator", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2111, + "endOffset": 1635, + "startOffset": 1529 + }, + { + "count": 2108, + "endOffset": 1631, + "startOffset": 1600 + } + ] + }, + { + "functionName": "isPosixPathSeparator", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1718, + "startOffset": 1639 + } + ] + }, + { + "functionName": "isWindowsDeviceRoot", + "isBlockCoverage": true, + "ranges": [ + { + "count": 86, + "endOffset": 1895, + "startOffset": 1722 + }, + { + "count": 81, + "endOffset": 1822, + "startOffset": 1795 + }, + { + "count": 14, + "endOffset": 1891, + "startOffset": 1824 + }, + { + "count": 9, + "endOffset": 1890, + "startOffset": 1863 + } + ] + }, + { + "functionName": "normalizeString", + "isBlockCoverage": true, + "ranges": [ + { + "count": 41, + "endOffset": 3826, + "startOffset": 1961 + }, + { + "count": 1915, + "endOffset": 3808, + "startOffset": 2184 + }, + { + "count": 1874, + "endOffset": 2245, + "startOffset": 2219 + }, + { + "count": 41, + "endOffset": 2340, + "startOffset": 2245 + }, + { + "count": 40, + "endOffset": 2296, + "startOffset": 2290 + }, + { + "count": 1, + "endOffset": 2340, + "startOffset": 2296 + }, + { + "count": 1875, + "endOffset": 2375, + "startOffset": 2340 + }, + { + "count": 209, + "endOffset": 3704, + "startOffset": 2375 + }, + { + "count": 200, + "endOffset": 2421, + "startOffset": 2408 + }, + { + "count": 10, + "endOffset": 2450, + "startOffset": 2423 + }, + { + "count": 199, + "endOffset": 3658, + "startOffset": 2450 + }, + { + "count": 2, + "endOffset": 3439, + "startOffset": 2472 + }, + { + "count": 0, + "endOffset": 2588, + "startOffset": 2529 + }, + { + "count": 0, + "endOffset": 2648, + "startOffset": 2589 + }, + { + "count": 0, + "endOffset": 2868, + "startOffset": 2789 + }, + { + "count": 0, + "endOffset": 3283, + "startOffset": 3108 + }, + { + "count": 0, + "endOffset": 3430, + "startOffset": 3294 + }, + { + "count": 197, + "endOffset": 3658, + "startOffset": 3439 + }, + { + "count": 158, + "endOffset": 3540, + "startOffset": 3487 + }, + { + "count": 39, + "endOffset": 3601, + "startOffset": 3540 + }, + { + "count": 207, + "endOffset": 3704, + "startOffset": 3658 + }, + { + "count": 1666, + "endOffset": 3803, + "startOffset": 3704 + }, + { + "count": 24, + "endOffset": 3746, + "startOffset": 3732 + }, + { + "count": 9, + "endOffset": 3771, + "startOffset": 3748 + }, + { + "count": 1657, + "endOffset": 3803, + "startOffset": 3771 + } + ] + }, + { + "functionName": "_format", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4453, + "startOffset": 4017 + } + ] + }, + { + "functionName": "resolve", + "isBlockCoverage": true, + "ranges": [ + { + "count": 40, + "endOffset": 9115, + "startOffset": 4579 + }, + { + "count": 53, + "endOffset": 8648, + "startOffset": 4742 + }, + { + "count": 52, + "endOffset": 4953, + "startOffset": 4780 + }, + { + "count": 0, + "endOffset": 4944, + "startOffset": 4911 + }, + { + "count": 1, + "endOffset": 5845, + "startOffset": 4953 + }, + { + "count": 0, + "endOffset": 5845, + "startOffset": 5033 + }, + { + "count": 0, + "endOffset": 6218, + "startOffset": 6053 + }, + { + "count": 0, + "endOffset": 7598, + "startOffset": 6251 + }, + { + "count": 49, + "endOffset": 7689, + "startOffset": 7634 + }, + { + "count": 40, + "endOffset": 8015, + "startOffset": 7691 + }, + { + "count": 40, + "endOffset": 8333, + "startOffset": 8048 + }, + { + "count": 0, + "endOffset": 8270, + "startOffset": 8090 + }, + { + "count": 0, + "endOffset": 8433, + "startOffset": 8365 + }, + { + "count": 40, + "endOffset": 8600, + "startOffset": 8572 + }, + { + "count": 40, + "endOffset": 8632, + "startOffset": 8602 + }, + { + "count": 0, + "endOffset": 9109, + "startOffset": 9059 + } + ] + }, + { + "functionName": "normalize", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 12023, + "startOffset": 9187 + }, + { + "count": 0, + "endOffset": 9308, + "startOffset": 9297 + }, + { + "count": 0, + "endOffset": 9622, + "startOffset": 9465 + }, + { + "count": 0, + "endOffset": 11204, + "startOffset": 9655 + }, + { + "count": 0, + "endOffset": 11708, + "startOffset": 11697 + }, + { + "count": 0, + "endOffset": 11751, + "startOffset": 11737 + }, + { + "count": 0, + "endOffset": 11771, + "startOffset": 11760 + }, + { + "count": 0, + "endOffset": 11863, + "startOffset": 11850 + }, + { + "count": 0, + "endOffset": 11950, + "startOffset": 11895 + }, + { + "count": 0, + "endOffset": 12017, + "startOffset": 11997 + } + ] + }, + { + "functionName": "isAbsolute", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5, + "endOffset": 12477, + "startOffset": 12096 + }, + { + "count": 0, + "endOffset": 12220, + "startOffset": 12207 + }, + { + "count": 4, + "endOffset": 12424, + "startOffset": 12381 + }, + { + "count": 4, + "endOffset": 12470, + "startOffset": 12425 + } + ] + }, + { + "functionName": "join", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 14671, + "startOffset": 12552 + }, + { + "count": 0, + "endOffset": 12614, + "startOffset": 12603 + }, + { + "count": 2, + "endOffset": 12926, + "startOffset": 12697 + }, + { + "count": 1, + "endOffset": 12910, + "startOffset": 12838 + }, + { + "count": 0, + "endOffset": 12978, + "startOffset": 12967 + }, + { + "count": 0, + "endOffset": 14271, + "startOffset": 13851 + }, + { + "count": 0, + "endOffset": 14499, + "startOffset": 14466 + }, + { + "count": 0, + "endOffset": 14620, + "startOffset": 14579 + } + ] + }, + { + "functionName": "relative", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18564, + "startOffset": 14979 + } + ] + }, + { + "functionName": "toNamespacedPath", + "isBlockCoverage": true, + "ranges": [ + { + "count": 18, + "endOffset": 19630, + "startOffset": 18571 + }, + { + "count": 0, + "endOffset": 18702, + "startOffset": 18690 + }, + { + "count": 0, + "endOffset": 18759, + "startOffset": 18733 + }, + { + "count": 0, + "endOffset": 18865, + "startOffset": 18853 + }, + { + "count": 0, + "endOffset": 19300, + "startOffset": 18929 + }, + { + "count": 0, + "endOffset": 19629, + "startOffset": 19605 + } + ] + }, + { + "functionName": "dirname", + "isBlockCoverage": true, + "ranges": [ + { + "count": 7, + "endOffset": 22114, + "startOffset": 19702 + }, + { + "count": 0, + "endOffset": 19821, + "startOffset": 19810 + }, + { + "count": 0, + "endOffset": 20090, + "startOffset": 19926 + }, + { + "count": 0, + "endOffset": 21455, + "startOffset": 20153 + }, + { + "count": 0, + "endOffset": 21602, + "startOffset": 21599 + }, + { + "count": 57, + "endOffset": 21973, + "startOffset": 21732 + }, + { + "count": 7, + "endOffset": 21871, + "startOffset": 21782 + }, + { + "count": 50, + "endOffset": 21966, + "startOffset": 21871 + }, + { + "count": 0, + "endOffset": 22077, + "startOffset": 21997 + } + ] + }, + { + "functionName": "basename", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 24930, + "startOffset": 22214 + }, + { + "count": 0, + "endOffset": 22298, + "startOffset": 22271 + }, + { + "count": 0, + "endOffset": 22778, + "startOffset": 22761 + }, + { + "count": 0, + "endOffset": 22807, + "startOffset": 22779 + }, + { + "count": 0, + "endOffset": 24318, + "startOffset": 22809 + }, + { + "count": 17, + "endOffset": 24848, + "startOffset": 24371 + }, + { + "count": 2, + "endOffset": 24652, + "startOffset": 24421 + }, + { + "count": 15, + "endOffset": 24841, + "startOffset": 24652 + }, + { + "count": 2, + "endOffset": 24841, + "startOffset": 24674 + }, + { + "count": 0, + "endOffset": 24889, + "startOffset": 24879 + } + ] + }, + { + "functionName": "extname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27073, + "startOffset": 25002 + } + ] + }, + { + "functionName": "parse", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31903, + "startOffset": 27291 + } + ] + }, + { + "functionName": "resolve", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33025, + "startOffset": 32100 + } + ] + }, + { + "functionName": "normalize", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33694, + "startOffset": 33097 + } + ] + }, + { + "functionName": "isAbsolute", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33899, + "startOffset": 33767 + } + ] + }, + { + "functionName": "join", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 34405, + "startOffset": 33974 + } + ] + }, + { + "functionName": "relative", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36780, + "startOffset": 34502 + } + ] + }, + { + "functionName": "toNamespacedPath", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36866, + "startOffset": 36787 + } + ] + }, + { + "functionName": "dirname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 37590, + "startOffset": 36938 + } + ] + }, + { + "functionName": "basename", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 40083, + "startOffset": 37690 + } + ] + }, + { + "functionName": "extname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 41863, + "startOffset": 40155 + } + ] + }, + { + "functionName": "parse", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 44518, + "startOffset": 42086 + } + ] + } + ], + "scriptId": "33", + "url": "path.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 16488, + "startOffset": 0 + } + ] + }, + { + "functionName": "lazyBuffer", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1119, + "startOffset": 1006 + } + ] + }, + { + "functionName": "validateEncoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1251, + "startOffset": 1123 + } + ] + }, + { + "functionName": "validateDecoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1383, + "startOffset": 1255 + } + ] + }, + { + "functionName": "validateArgument", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1601, + "startOffset": 1387 + } + ] + }, + { + "functionName": "trimAsciiWhitespace", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9068, + "startOffset": 8566 + } + ] + }, + { + "functionName": "getEncodingFromLabel", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9255, + "startOffset": 9072 + } + ] + }, + { + "functionName": "TextEncoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9379, + "startOffset": 9331 + } + ] + }, + { + "functionName": "get encoding", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9455, + "startOffset": 9385 + } + ] + }, + { + "functionName": "encode", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9556, + "startOffset": 9461 + } + ] + }, + { + "functionName": "encodeInto", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9879, + "startOffset": 9562 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10291, + "startOffset": 9885 + } + ] + }, + { + "functionName": "makeTextDecoderICU", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 12397, + "startOffset": 10680 + } + ] + }, + { + "functionName": "TextDecoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11574, + "startOffset": 10825 + } + ] + }, + { + "functionName": "decode", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12364, + "startOffset": 11584 + } + ] + }, + { + "functionName": "makeTextDecoderJS", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15025, + "startOffset": 12401 + } + ] + }, + { + "functionName": "get encoding", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15240, + "startOffset": 15156 + } + ] + }, + { + "functionName": "get fatal", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15379, + "startOffset": 15249 + } + ] + }, + { + "functionName": "get ignoreBOM", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15547, + "startOffset": 15388 + } + ] + }, + { + "functionName": "ObjectGetOwnPropertyDescriptors", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16186, + "startOffset": 15556 + } + ] + } + ], + "scriptId": "34", + "url": "internal/encoding.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 8643, + "startOffset": 0 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1882, + "startOffset": 1855 + } + ] + }, + { + "functionName": "unenroll", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3615, + "startOffset": 2396 + } + ] + }, + { + "functionName": "enroll", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4089, + "startOffset": 3827 + } + ] + }, + { + "functionName": "setTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4800, + "startOffset": 4127 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5019, + "startOffset": 4881 + } + ] + }, + { + "functionName": "clearTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5407, + "startOffset": 5028 + } + ] + }, + { + "functionName": "setInterval", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6086, + "startOffset": 5411 + } + ] + }, + { + "functionName": "clearInterval", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6393, + "startOffset": 6090 + } + ] + }, + { + "functionName": "Timeout.close", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6477, + "startOffset": 6423 + } + ] + }, + { + "functionName": "Timeout.", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6689, + "startOffset": 6521 + } + ] + }, + { + "functionName": "setImmediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7259, + "startOffset": 6694 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7482, + "startOffset": 7342 + } + ] + }, + { + "functionName": "clearImmediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7977, + "startOffset": 7493 + } + ] + } + ], + "scriptId": "35", + "url": "timers.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1166, + "startOffset": 0 + } + ] + }, + { + "functionName": "init", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 93, + "startOffset": 17 + } + ] + }, + { + "functionName": "peek", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 221, + "startOffset": 126 + } + ] + }, + { + "functionName": "remove", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 497, + "startOffset": 259 + } + ] + }, + { + "functionName": "append", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1021, + "startOffset": 556 + } + ] + }, + { + "functionName": "isEmpty", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1087, + "startOffset": 1025 + } + ] + } + ], + "scriptId": "36", + "url": "internal/linkedlist.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 19158, + "startOffset": 0 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4543, + "startOffset": 4516 + } + ] + }, + { + "functionName": "initAsyncResource", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5802, + "startOffset": 5518 + } + ] + }, + { + "functionName": "Timeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6918, + "startOffset": 5891 + } + ] + }, + { + "functionName": "Timeout.", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7213, + "startOffset": 7034 + } + ] + }, + { + "functionName": "Timeout.refresh", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7351, + "startOffset": 7246 + } + ] + }, + { + "functionName": "Timeout.unref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7517, + "startOffset": 7382 + } + ] + }, + { + "functionName": "Timeout.ref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7681, + "startOffset": 7546 + } + ] + }, + { + "functionName": "Timeout.hasRef", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7752, + "startOffset": 7713 + } + ] + }, + { + "functionName": "TimersList", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8064, + "startOffset": 7757 + } + ] + }, + { + "functionName": "TimersList.", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8362, + "startOffset": 8183 + } + ] + }, + { + "functionName": "ImmediateList", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 8494, + "startOffset": 8423 + } + ] + }, + { + "functionName": "ImmediateList.append", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8852, + "startOffset": 8677 + } + ] + }, + { + "functionName": "ImmediateList.remove", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9387, + "startOffset": 9034 + } + ] + }, + { + "functionName": "incRefCount", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9471, + "startOffset": 9392 + } + ] + }, + { + "functionName": "decRefCount", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9555, + "startOffset": 9475 + } + ] + }, + { + "functionName": "active", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9698, + "startOffset": 9642 + } + ] + }, + { + "functionName": "unrefActive", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9911, + "startOffset": 9849 + } + ] + }, + { + "functionName": "insertGuarded", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10677, + "startOffset": 10138 + } + ] + }, + { + "functionName": "insert", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11352, + "startOffset": 10681 + } + ] + }, + { + "functionName": "setUnrefTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11672, + "startOffset": 11356 + } + ] + }, + { + "functionName": "getTimerDuration", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12279, + "startOffset": 11742 + } + ] + }, + { + "functionName": "compareTimersLists", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12497, + "startOffset": 12283 + } + ] + }, + { + "functionName": "setPosition", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12574, + "startOffset": 12501 + } + ] + }, + { + "functionName": "getTimerCallbacks", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 17857, + "startOffset": 12578 + } + ] + }, + { + "functionName": "processImmediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14758, + "startOffset": 12896 + } + ] + }, + { + "functionName": "processTimers", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15258, + "startOffset": 14766 + } + ] + }, + { + "functionName": "listOnTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17792, + "startOffset": 15264 + } + ] + }, + { + "functionName": "Immediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18218, + "startOffset": 17882 + } + ] + }, + { + "functionName": "ref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18404, + "startOffset": 18224 + } + ] + }, + { + "functionName": "unref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18593, + "startOffset": 18410 + } + ] + }, + { + "functionName": "hasRef", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18642, + "startOffset": 18599 + } + ] + } + ], + "scriptId": "37", + "url": "internal/timers.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 3052, + "startOffset": 0 + } + ] + }, + { + "functionName": "PriorityQueue", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 838, + "startOffset": 589 + } + ] + }, + { + "functionName": "module.exports", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 886, + "startOffset": 844 + } + ] + }, + { + "functionName": "insert", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1086, + "startOffset": 892 + } + ] + }, + { + "functionName": "peek", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1133, + "startOffset": 1092 + } + ] + }, + { + "functionName": "percolateDown", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1829, + "startOffset": 1139 + } + ] + }, + { + "functionName": "percolateUp", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2344, + "startOffset": 1835 + } + ] + }, + { + "functionName": "removeAt", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2695, + "startOffset": 2350 + } + ] + }, + { + "functionName": "remove", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2876, + "startOffset": 2701 + } + ] + }, + { + "functionName": "shift", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3046, + "startOffset": 2882 + } + ] + } + ], + "scriptId": "38", + "url": "internal/priority_queue.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 2918, + "startOffset": 0 + } + ] + }, + { + "functionName": "initializeDebugEnv", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 904, + "startOffset": 521 + }, + { + "count": 0, + "endOffset": 819, + "startOffset": 614 + } + ] + }, + { + "functionName": "emitWarningIfNeeded", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1314, + "startOffset": 982 + } + ] + }, + { + "functionName": "noop", + "isBlockCoverage": true, + "ranges": [ + { + "count": 13, + "endOffset": 1336, + "startOffset": 1318 + } + ] + }, + { + "functionName": "debuglogImpl", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 1921, + "startOffset": 1340 + }, + { + "count": 2, + "endOffset": 1891, + "startOffset": 1416 + }, + { + "count": 0, + "endOffset": 1841, + "startOffset": 1436 + } + ] + }, + { + "functionName": "debug", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1833, + "startOffset": 1528 + } + ] + }, + { + "functionName": "debuglog", + "isBlockCoverage": true, + "ranges": [ + { + "count": 12, + "endOffset": 2855, + "startOffset": 2147 + } + ] + }, + { + "functionName": "init", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 2278, + "startOffset": 2179 + } + ] + }, + { + "functionName": "debug", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 2539, + "startOffset": 2294 + } + ] + }, + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2644, + "startOffset": 2571 + } + ] + }, + { + "functionName": "logger", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 2691, + "startOffset": 2664 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2779, + "startOffset": 2743 + } + ] + } + ], + "scriptId": "39", + "url": "internal/util/debuglog.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 7161, + "startOffset": 0 + } + ] + }, + { + "functionName": "tryGetCwd", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 890, + "startOffset": 551 + }, + { + "count": 0, + "endOffset": 887, + "startOffset": 615 + } + ] + }, + { + "functionName": "evalModule", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1347, + "startOffset": 894 + } + ] + }, + { + "functionName": "evalScript", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2774, + "startOffset": 1351 + } + ] + }, + { + "functionName": "setUncaughtExceptionCaptureCallback", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3570, + "startOffset": 2858 + } + ] + }, + { + "functionName": "hasUncaughtExceptionCaptureCallback", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3677, + "startOffset": 3574 + } + ] + }, + { + "functionName": "noop", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3699, + "startOffset": 3681 + } + ] + }, + { + "functionName": "createOnGlobalUncaughtException", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 6711, + "startOffset": 4252 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6707, + "startOffset": 4525 + } + ] + }, + { + "functionName": "readStdin", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6931, + "startOffset": 6715 + } + ] + } + ], + "scriptId": "40", + "url": "internal/process/execution.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 5002, + "startOffset": 0 + } + ] + }, + { + "functionName": "lazyOption", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 864, + "startOffset": 295 + } + ] + }, + { + "functionName": "writeOut", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1097, + "startOffset": 970 + } + ] + }, + { + "functionName": "writeToFile", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1504, + "startOffset": 1101 + } + ] + }, + { + "functionName": "doEmitWarning", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1581, + "startOffset": 1508 + } + ] + }, + { + "functionName": "onWarning", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2833, + "startOffset": 1623 + } + ] + }, + { + "functionName": "emitWarning", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4139, + "startOffset": 2961 + } + ] + }, + { + "functionName": "emitWarningSync", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4239, + "startOffset": 4143 + } + ] + }, + { + "functionName": "createWarningObject", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4924, + "startOffset": 4243 + } + ] + } + ], + "scriptId": "41", + "url": "internal/process/warning.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 6632, + "startOffset": 0 + } + ] + }, + { + "functionName": "process._startProfilerIdleNotifier", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 533, + "startOffset": 525 + } + ] + }, + { + "functionName": "process._stopProfilerIdleNotifier", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 580, + "startOffset": 572 + } + ] + }, + { + "functionName": "defineStream", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 738, + "startOffset": 585 + } + ] + }, + { + "functionName": "createWritableStdioStream", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2943, + "startOffset": 1318 + } + ] + }, + { + "functionName": "dummyDestroy", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3343, + "startOffset": 2947 + } + ] + }, + { + "functionName": "getStdout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3728, + "startOffset": 3387 + } + ] + }, + { + "functionName": "getStderr", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4073, + "startOffset": 3732 + } + ] + }, + { + "functionName": "getStdin", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6480, + "startOffset": 4077 + } + ] + }, + { + "functionName": "rawMethods.resetStdioForTesting", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6629, + "startOffset": 6546 + } + ] + } + ], + "scriptId": "42", + "url": "internal/bootstrap/switches/is_main_thread.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1177, + "startOffset": 0 + } + ] + }, + { + "functionName": "isSignal", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 318, + "startOffset": 220 + } + ] + }, + { + "functionName": "startListeningIfSignal", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 892, + "startOffset": 385 + }, + { + "count": 1, + "endOffset": 472, + "startOffset": 447 + }, + { + "count": 1, + "endOffset": 889, + "startOffset": 474 + }, + { + "count": 0, + "endOffset": 848, + "startOffset": 766 + } + ] + }, + { + "functionName": "stopListeningIfSignal", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1097, + "startOffset": 896 + } + ] + } + ], + "scriptId": "43", + "url": "internal/process/signal.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 3655, + "startOffset": 0 + }, + { + "count": 0, + "endOffset": 598, + "startOffset": 298 + } + ] + }, + { + "functionName": "wrapPosixCredentialSetters", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3064, + "startOffset": 846 + } + ] + }, + { + "functionName": "wrappedChdir", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3397, + "startOffset": 3221 + } + ] + }, + { + "functionName": "wrappedUmask", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3542, + "startOffset": 3401 + } + ] + }, + { + "functionName": "wrappedCwd", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 3653, + "startOffset": 3546 + }, + { + "count": 1, + "endOffset": 3629, + "startOffset": 3600 + } + ] + } + ], + "scriptId": "44", + "url": "internal/bootstrap/switches/does_own_process_state.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 649, + "startOffset": 0 + } + ] + } + ], + "scriptId": "45", + "url": "internal/main/run_main_module.js" + } + ], + "timestamp": 967709.209879 + }, + "test_v8_coverage_node_sqlite_merged.json": { + "result": [ + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 3655, + "startOffset": 0 + }, + { + "count": 0, + "endOffset": 598, + "startOffset": 298 + } + ] + }, + { + "functionName": "wrapPosixCredentialSetters", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3064, + "startOffset": 846 + } + ] + }, + { + "functionName": "wrappedChdir", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3397, + "startOffset": 3221 + } + ] + }, + { + "functionName": "wrappedUmask", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3542, + "startOffset": 3401 + } + ] + }, + { + "functionName": "wrappedCwd", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 3653, + "startOffset": 3546 + }, + { + "count": 1, + "endOffset": 3629, + "startOffset": 3600 + } + ] + } + ], + "scriptId": "0", + "url": "internal/bootstrap/switches/does_own_process_state.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 6632, + "startOffset": 0 + } + ] + }, + { + "functionName": "process._startProfilerIdleNotifier", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 533, + "startOffset": 525 + } + ] + }, + { + "functionName": "process._stopProfilerIdleNotifier", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 580, + "startOffset": 572 + } + ] + }, + { + "functionName": "defineStream", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 738, + "startOffset": 585 + } + ] + }, + { + "functionName": "createWritableStdioStream", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2943, + "startOffset": 1318 + } + ] + }, + { + "functionName": "dummyDestroy", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3343, + "startOffset": 2947 + } + ] + }, + { + "functionName": "getStdout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3728, + "startOffset": 3387 + } + ] + }, + { + "functionName": "getStderr", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4073, + "startOffset": 3732 + } + ] + }, + { + "functionName": "getStdin", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6480, + "startOffset": 4077 + } + ] + }, + { + "functionName": "rawMethods.resetStdioForTesting", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6629, + "startOffset": 6546 + } + ] + } + ], + "scriptId": "1", + "url": "internal/bootstrap/switches/is_main_thread.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 20604, + "startOffset": 0 + } + ] + }, + { + "functionName": "Console", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4925, + "startOffset": 2686 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5200, + "startOffset": 5144 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5669, + "startOffset": 5458 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 6489, + "startOffset": 5878 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 6165, + "startOffset": 6067 + }, + { + "count": 1, + "endOffset": 6124, + "startOffset": 6101 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6208, + "startOffset": 6178 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6417, + "startOffset": 6315 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6460, + "startOffset": 6430 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 7776, + "startOffset": 6563 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9334, + "startOffset": 7850 + }, + { + "count": 0, + "endOffset": 8095, + "startOffset": 8081 + }, + { + "count": 0, + "endOffset": 8197, + "startOffset": 8171 + }, + { + "count": 0, + "endOffset": 8432, + "startOffset": 8238 + }, + { + "count": 0, + "endOffset": 8521, + "startOffset": 8493 + }, + { + "count": 0, + "endOffset": 9261, + "startOffset": 8971 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 9918, + "startOffset": 9411 + }, + { + "count": 0, + "endOffset": 9635, + "startOffset": 9629 + }, + { + "count": 0, + "endOffset": 9840, + "startOffset": 9714 + }, + { + "count": 0, + "endOffset": 9910, + "startOffset": 9886 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 10124, + "startOffset": 9993 + } + ] + }, + { + "functionName": "value", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10330, + "startOffset": 10199 + } + ] + }, + { + "functionName": "createWriteErrorHandler", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 11331, + "startOffset": 10424 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 11327, + "startOffset": 10493 + }, + { + "count": 0, + "endOffset": 10786, + "startOffset": 10768 + } + ] + }, + { + "functionName": "log", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 11452, + "startOffset": 11363 + } + ] + }, + { + "functionName": "warn", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11551, + "startOffset": 11461 + } + ] + }, + { + "functionName": "dir", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11751, + "startOffset": 11560 + } + ] + }, + { + "functionName": "time", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12125, + "startOffset": 11758 + } + ] + }, + { + "functionName": "timeEnd", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12429, + "startOffset": 12132 + } + ] + }, + { + "functionName": "timeLog", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12679, + "startOffset": 12436 + } + ] + }, + { + "functionName": "trace", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12886, + "startOffset": 12693 + } + ] + }, + { + "functionName": "assert", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 13116, + "startOffset": 12893 + } + ] + }, + { + "functionName": "clear", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 13620, + "startOffset": 13180 + } + ] + }, + { + "functionName": "count", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14153, + "startOffset": 13684 + } + ] + }, + { + "functionName": "countReset", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14518, + "startOffset": 14222 + } + ] + }, + { + "functionName": "group", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14699, + "startOffset": 14525 + } + ] + }, + { + "functionName": "groupEnd", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14880, + "startOffset": 14706 + } + ] + }, + { + "functionName": "table", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18454, + "startOffset": 14932 + } + ] + }, + { + "functionName": "timeLogImpl", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19012, + "startOffset": 18499 + } + ] + }, + { + "functionName": "pad", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19095, + "startOffset": 19016 + } + ] + }, + { + "functionName": "formatTime", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19892, + "startOffset": 19099 + } + ] + }, + { + "functionName": "isArray", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20089, + "startOffset": 20033 + } + ] + }, + { + "functionName": "noop", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20112, + "startOffset": 20094 + } + ] + } + ], + "scriptId": "2", + "url": "internal/console/constructor.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1638, + "startOffset": 0 + } + ] + } + ], + "scriptId": "3", + "url": "internal/constants.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 16488, + "startOffset": 0 + } + ] + }, + { + "functionName": "lazyBuffer", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1119, + "startOffset": 1006 + } + ] + }, + { + "functionName": "validateEncoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1251, + "startOffset": 1123 + } + ] + }, + { + "functionName": "validateDecoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1383, + "startOffset": 1255 + } + ] + }, + { + "functionName": "validateArgument", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1601, + "startOffset": 1387 + } + ] + }, + { + "functionName": "trimAsciiWhitespace", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9068, + "startOffset": 8566 + } + ] + }, + { + "functionName": "getEncodingFromLabel", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9255, + "startOffset": 9072 + } + ] + }, + { + "functionName": "TextEncoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9379, + "startOffset": 9331 + } + ] + }, + { + "functionName": "get encoding", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9455, + "startOffset": 9385 + } + ] + }, + { + "functionName": "encode", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9556, + "startOffset": 9461 + } + ] + }, + { + "functionName": "encodeInto", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9879, + "startOffset": 9562 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10291, + "startOffset": 9885 + } + ] + }, + { + "functionName": "makeTextDecoderICU", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 12397, + "startOffset": 10680 + } + ] + }, + { + "functionName": "TextDecoder", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11574, + "startOffset": 10825 + } + ] + }, + { + "functionName": "decode", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12364, + "startOffset": 11584 + } + ] + }, + { + "functionName": "makeTextDecoderJS", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15025, + "startOffset": 12401 + } + ] + }, + { + "functionName": "get encoding", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15240, + "startOffset": 15156 + } + ] + }, + { + "functionName": "get fatal", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15379, + "startOffset": 15249 + } + ] + }, + { + "functionName": "get ignoreBOM", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15547, + "startOffset": 15388 + } + ] + }, + { + "functionName": "ObjectGetOwnPropertyDescriptors", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16186, + "startOffset": 15556 + } + ] + } + ], + "scriptId": "4", + "url": "internal/encoding.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1166, + "startOffset": 0 + } + ] + }, + { + "functionName": "init", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 93, + "startOffset": 17 + } + ] + }, + { + "functionName": "peek", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 221, + "startOffset": 126 + } + ] + }, + { + "functionName": "remove", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 497, + "startOffset": 259 + } + ] + }, + { + "functionName": "append", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1021, + "startOffset": 556 + } + ] + }, + { + "functionName": "isEmpty", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1087, + "startOffset": 1025 + } + ] + } + ], + "scriptId": "5", + "url": "internal/linkedlist.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 649, + "startOffset": 0 + } + ] + } + ], + "scriptId": "6", + "url": "internal/main/run_main_module.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 3052, + "startOffset": 0 + } + ] + }, + { + "functionName": "PriorityQueue", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 838, + "startOffset": 589 + } + ] + }, + { + "functionName": "module.exports", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 886, + "startOffset": 844 + } + ] + }, + { + "functionName": "insert", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1086, + "startOffset": 892 + } + ] + }, + { + "functionName": "peek", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1133, + "startOffset": 1092 + } + ] + }, + { + "functionName": "percolateDown", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1829, + "startOffset": 1139 + } + ] + }, + { + "functionName": "percolateUp", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2344, + "startOffset": 1835 + } + ] + }, + { + "functionName": "removeAt", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2695, + "startOffset": 2350 + } + ] + }, + { + "functionName": "remove", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2876, + "startOffset": 2701 + } + ] + }, + { + "functionName": "shift", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3046, + "startOffset": 2882 + } + ] + } + ], + "scriptId": "7", + "url": "internal/priority_queue.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 7161, + "startOffset": 0 + } + ] + }, + { + "functionName": "tryGetCwd", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 890, + "startOffset": 551 + }, + { + "count": 0, + "endOffset": 887, + "startOffset": 615 + } + ] + }, + { + "functionName": "evalModule", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1347, + "startOffset": 894 + } + ] + }, + { + "functionName": "evalScript", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2774, + "startOffset": 1351 + } + ] + }, + { + "functionName": "setUncaughtExceptionCaptureCallback", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3570, + "startOffset": 2858 + } + ] + }, + { + "functionName": "hasUncaughtExceptionCaptureCallback", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3677, + "startOffset": 3574 + } + ] + }, + { + "functionName": "noop", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3699, + "startOffset": 3681 + } + ] + }, + { + "functionName": "createOnGlobalUncaughtException", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 6711, + "startOffset": 4252 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6707, + "startOffset": 4525 + } + ] + }, + { + "functionName": "readStdin", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6931, + "startOffset": 6715 + } + ] + } + ], + "scriptId": "8", + "url": "internal/process/execution.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1177, + "startOffset": 0 + } + ] + }, + { + "functionName": "isSignal", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 318, + "startOffset": 220 + } + ] + }, + { + "functionName": "startListeningIfSignal", + "isBlockCoverage": true, + "ranges": [ + { + "count": 4, + "endOffset": 892, + "startOffset": 385 + }, + { + "count": 1, + "endOffset": 472, + "startOffset": 447 + }, + { + "count": 1, + "endOffset": 889, + "startOffset": 474 + }, + { + "count": 0, + "endOffset": 848, + "startOffset": 766 + } + ] + }, + { + "functionName": "stopListeningIfSignal", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1097, + "startOffset": 896 + } + ] + } + ], + "scriptId": "9", + "url": "internal/process/signal.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 5002, + "startOffset": 0 + } + ] + }, + { + "functionName": "lazyOption", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 864, + "startOffset": 295 + } + ] + }, + { + "functionName": "writeOut", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1097, + "startOffset": 970 + } + ] + }, + { + "functionName": "writeToFile", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1504, + "startOffset": 1101 + } + ] + }, + { + "functionName": "doEmitWarning", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1581, + "startOffset": 1508 + } + ] + }, + { + "functionName": "onWarning", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2833, + "startOffset": 1623 + } + ] + }, + { + "functionName": "emitWarning", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4139, + "startOffset": 2961 + } + ] + }, + { + "functionName": "emitWarningSync", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4239, + "startOffset": 4143 + } + ] + }, + { + "functionName": "createWarningObject", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4924, + "startOffset": 4243 + } + ] + } + ], + "scriptId": "10", + "url": "internal/process/warning.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 2, + "endOffset": 3134, + "startOffset": 0 + } + ] + }, + { + "functionName": "encodeStr", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3065, + "startOffset": 1374 + } + ] + } + ], + "scriptId": "11", + "url": "internal/querystring.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 19158, + "startOffset": 0 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4543, + "startOffset": 4516 + } + ] + }, + { + "functionName": "initAsyncResource", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5802, + "startOffset": 5518 + } + ] + }, + { + "functionName": "Timeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6918, + "startOffset": 5891 + } + ] + }, + { + "functionName": "Timeout.", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7213, + "startOffset": 7034 + } + ] + }, + { + "functionName": "Timeout.refresh", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7351, + "startOffset": 7246 + } + ] + }, + { + "functionName": "Timeout.unref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7517, + "startOffset": 7382 + } + ] + }, + { + "functionName": "Timeout.ref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7681, + "startOffset": 7546 + } + ] + }, + { + "functionName": "Timeout.hasRef", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7752, + "startOffset": 7713 + } + ] + }, + { + "functionName": "TimersList", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8064, + "startOffset": 7757 + } + ] + }, + { + "functionName": "TimersList.", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8362, + "startOffset": 8183 + } + ] + }, + { + "functionName": "ImmediateList", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 8494, + "startOffset": 8423 + } + ] + }, + { + "functionName": "ImmediateList.append", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8852, + "startOffset": 8677 + } + ] + }, + { + "functionName": "ImmediateList.remove", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9387, + "startOffset": 9034 + } + ] + }, + { + "functionName": "incRefCount", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9471, + "startOffset": 9392 + } + ] + }, + { + "functionName": "decRefCount", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9555, + "startOffset": 9475 + } + ] + }, + { + "functionName": "active", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9698, + "startOffset": 9642 + } + ] + }, + { + "functionName": "unrefActive", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9911, + "startOffset": 9849 + } + ] + }, + { + "functionName": "insertGuarded", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10677, + "startOffset": 10138 + } + ] + }, + { + "functionName": "insert", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11352, + "startOffset": 10681 + } + ] + }, + { + "functionName": "setUnrefTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11672, + "startOffset": 11356 + } + ] + }, + { + "functionName": "getTimerDuration", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12279, + "startOffset": 11742 + } + ] + }, + { + "functionName": "compareTimersLists", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12497, + "startOffset": 12283 + } + ] + }, + { + "functionName": "setPosition", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12574, + "startOffset": 12501 + } + ] + }, + { + "functionName": "getTimerCallbacks", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 17857, + "startOffset": 12578 + } + ] + }, + { + "functionName": "processImmediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14758, + "startOffset": 12896 + } + ] + }, + { + "functionName": "processTimers", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15258, + "startOffset": 14766 + } + ] + }, + { + "functionName": "listOnTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17792, + "startOffset": 15264 + } + ] + }, + { + "functionName": "Immediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18218, + "startOffset": 17882 + } + ] + }, + { + "functionName": "ref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18404, + "startOffset": 18224 + } + ] + }, + { + "functionName": "unref", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18593, + "startOffset": 18410 + } + ] + }, + { + "functionName": "hasRef", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18642, + "startOffset": 18599 + } + ] + } + ], + "scriptId": "12", + "url": "internal/timers.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 42795, + "startOffset": 0 + } + ] + }, + { + "functionName": "toUSVString", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2627, + "startOffset": 2323 + } + ] + }, + { + "functionName": "serializeTupleOrigin", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2965, + "startOffset": 2845 + } + ] + }, + { + "functionName": "URLContext", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 3611, + "startOffset": 3378 + } + ] + }, + { + "functionName": "URLSearchParams", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 6333, + "startOffset": 3909 + }, + { + "count": 0, + "endOffset": 6267, + "startOffset": 4027 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7439, + "startOffset": 6339 + } + ] + }, + { + "functionName": "onParseComplete", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 8156, + "startOffset": 7446 + }, + { + "count": 0, + "endOffset": 7716, + "startOffset": 7706 + }, + { + "count": 0, + "endOffset": 7790, + "startOffset": 7780 + }, + { + "count": 0, + "endOffset": 7877, + "startOffset": 7873 + } + ] + }, + { + "functionName": "onParseError", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8237, + "startOffset": 8160 + } + ] + }, + { + "functionName": "onParseProtocolComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8596, + "startOffset": 8241 + } + ] + }, + { + "functionName": "onParseHostnameComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 8956, + "startOffset": 8600 + } + ] + }, + { + "functionName": "onParsePortComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9125, + "startOffset": 8960 + } + ] + }, + { + "functionName": "onParseHostComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 9440, + "startOffset": 9129 + } + ] + }, + { + "functionName": "onParsePathComplete", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 9954, + "startOffset": 9444 + }, + { + "count": 0, + "endOffset": 9787, + "startOffset": 9716 + } + ] + }, + { + "functionName": "onParseSearchComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10129, + "startOffset": 9958 + } + ] + }, + { + "functionName": "onParseHashComplete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10306, + "startOffset": 10133 + } + ] + }, + { + "functionName": "URL", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 10663, + "startOffset": 10325 + }, + { + "count": 0, + "endOffset": 10518, + "startOffset": 10464 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 10752, + "startOffset": 10669 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": true, + "ranges": [ + { + "count": 327, + "endOffset": 10853, + "startOffset": 10758 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 11136, + "startOffset": 10931 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 12107, + "startOffset": 11142 + } + ] + }, + { + "functionName": "format", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 13502, + "startOffset": 12284 + }, + { + "count": 0, + "endOffset": 12432, + "startOffset": 12371 + }, + { + "count": 0, + "endOffset": 13023, + "startOffset": 12856 + }, + { + "count": 0, + "endOffset": 13094, + "startOffset": 13056 + }, + { + "count": 0, + "endOffset": 13172, + "startOffset": 13150 + }, + { + "count": 0, + "endOffset": 13247, + "startOffset": 13181 + }, + { + "count": 0, + "endOffset": 13386, + "startOffset": 13363 + }, + { + "count": 0, + "endOffset": 13476, + "startOffset": 13450 + } + ] + }, + { + "functionName": "toString", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 13849, + "startOffset": 13788 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 13967, + "startOffset": 13920 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14159, + "startOffset": 13974 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 14895, + "startOffset": 14245 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15020, + "startOffset": 14970 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15408, + "startOffset": 15027 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15535, + "startOffset": 15483 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 15953, + "startOffset": 15542 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16080, + "startOffset": 16028 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16498, + "startOffset": 16087 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 16731, + "startOffset": 16569 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17038, + "startOffset": 16738 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17167, + "startOffset": 17113 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17482, + "startOffset": 17174 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17656, + "startOffset": 17553 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 17991, + "startOffset": 17663 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": true, + "ranges": [ + { + "count": 218, + "endOffset": 18268, + "startOffset": 18066 + }, + { + "count": 0, + "endOffset": 18167, + "startOffset": 18148 + }, + { + "count": 0, + "endOffset": 18221, + "startOffset": 18211 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 18501, + "startOffset": 18275 + }, + { + "count": 0, + "endOffset": 18396, + "startOffset": 18389 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18718, + "startOffset": 18574 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19232, + "startOffset": 18725 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19372, + "startOffset": 19324 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 19599, + "startOffset": 19443 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20037, + "startOffset": 19606 + } + ] + }, + { + "functionName": "toJSON", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20249, + "startOffset": 20190 + } + ] + }, + { + "functionName": "update", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 20581, + "startOffset": 20263 + } + ] + }, + { + "functionName": "initSearchParams", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 20731, + "startOffset": 20585 + }, + { + "count": 0, + "endOffset": 20730, + "startOffset": 20686 + } + ] + }, + { + "functionName": "parseParams", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 23241, + "startOffset": 20844 + } + ] + }, + { + "functionName": "serializeParams", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 24820, + "startOffset": 24244 + } + ] + }, + { + "functionName": "defineIDLClass", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2, + "endOffset": 25592, + "startOffset": 24878 + }, + { + "count": 13, + "endOffset": 25379, + "startOffset": 25226 + }, + { + "count": 1, + "endOffset": 25589, + "startOffset": 25435 + } + ] + }, + { + "functionName": "merge", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 26271, + "startOffset": 25615 + } + ] + }, + { + "functionName": "append", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 26740, + "startOffset": 26341 + } + ] + }, + { + "functionName": "delete", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27265, + "startOffset": 26747 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27722, + "startOffset": 27272 + } + ] + }, + { + "functionName": "getAll", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 28214, + "startOffset": 27729 + } + ] + }, + { + "functionName": "has", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 28665, + "startOffset": 28221 + } + ] + }, + { + "functionName": "set", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 29736, + "startOffset": 28672 + } + ] + }, + { + "functionName": "sort", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 30962, + "startOffset": 29743 + } + ] + }, + { + "functionName": "entries", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31345, + "startOffset": 31130 + } + ] + }, + { + "functionName": "forEach", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31943, + "startOffset": 31352 + } + ] + }, + { + "functionName": "keys", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 32207, + "startOffset": 32001 + } + ] + }, + { + "functionName": "values", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 32424, + "startOffset": 32214 + } + ] + }, + { + "functionName": "toString", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 32767, + "startOffset": 32561 + } + ] + }, + { + "functionName": "createSearchParamsIterator", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33269, + "startOffset": 33058 + } + ] + }, + { + "functionName": "next", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 34289, + "startOffset": 33499 + } + ] + }, + { + "functionName": "defineIDLClass", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 35544, + "startOffset": 34294 + } + ] + }, + { + "functionName": "domainToASCII", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 35734, + "startOffset": 35553 + } + ] + }, + { + "functionName": "domainToUnicode", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 35923, + "startOffset": 35738 + } + ] + }, + { + "functionName": "urlToOptions", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36633, + "startOffset": 36071 + } + ] + }, + { + "functionName": "getPathFromURLWin32", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 38099, + "startOffset": 36673 + } + ] + }, + { + "functionName": "getPathFromURLPosix", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 38623, + "startOffset": 38103 + } + ] + }, + { + "functionName": "fileURLToPath", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 38982, + "startOffset": 38627 + } + ] + }, + { + "functionName": "encodePathChars", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 40330, + "startOffset": 39761 + }, + { + "count": 0, + "endOffset": 39883, + "startOffset": 39834 + }, + { + "count": 0, + "endOffset": 39985, + "startOffset": 39959 + }, + { + "count": 0, + "endOffset": 40043, + "startOffset": 39992 + }, + { + "count": 0, + "endOffset": 40130, + "startOffset": 40081 + }, + { + "count": 0, + "endOffset": 40224, + "startOffset": 40168 + }, + { + "count": 0, + "endOffset": 40307, + "startOffset": 40262 + } + ] + }, + { + "functionName": "pathToFileURL", + "isBlockCoverage": true, + "ranges": [ + { + "count": 109, + "endOffset": 41482, + "startOffset": 40334 + }, + { + "count": 0, + "endOffset": 41025, + "startOffset": 40456 + }, + { + "count": 0, + "endOffset": 41381, + "startOffset": 41327 + }, + { + "count": 0, + "endOffset": 41406, + "startOffset": 41390 + } + ] + }, + { + "functionName": "isURLInstance", + "isBlockCoverage": true, + "ranges": [ + { + "count": 277, + "endOffset": 41607, + "startOffset": 41486 + }, + { + "count": 0, + "endOffset": 41603, + "startOffset": 41580 + } + ] + }, + { + "functionName": "toPathIfFileURL", + "isBlockCoverage": true, + "ranges": [ + { + "count": 277, + "endOffset": 41760, + "startOffset": 41611 + }, + { + "count": 0, + "endOffset": 41759, + "startOffset": 41717 + } + ] + }, + { + "functionName": "constructUrl", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 42484, + "startOffset": 41764 + } + ] + } + ], + "scriptId": "13", + "url": "internal/url.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 2918, + "startOffset": 0 + } + ] + }, + { + "functionName": "initializeDebugEnv", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 904, + "startOffset": 521 + }, + { + "count": 0, + "endOffset": 819, + "startOffset": 614 + } + ] + }, + { + "functionName": "emitWarningIfNeeded", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1314, + "startOffset": 982 + } + ] + }, + { + "functionName": "noop", + "isBlockCoverage": true, + "ranges": [ + { + "count": 13, + "endOffset": 1336, + "startOffset": 1318 + } + ] + }, + { + "functionName": "debuglogImpl", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 1921, + "startOffset": 1340 + }, + { + "count": 2, + "endOffset": 1891, + "startOffset": 1416 + }, + { + "count": 0, + "endOffset": 1841, + "startOffset": 1436 + } + ] + }, + { + "functionName": "debug", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1833, + "startOffset": 1528 + } + ] + }, + { + "functionName": "debuglog", + "isBlockCoverage": true, + "ranges": [ + { + "count": 12, + "endOffset": 2855, + "startOffset": 2147 + } + ] + }, + { + "functionName": "init", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 2278, + "startOffset": 2179 + } + ] + }, + { + "functionName": "debug", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 2539, + "startOffset": 2294 + } + ] + }, + { + "functionName": "test", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2644, + "startOffset": 2571 + } + ] + }, + { + "functionName": "logger", + "isBlockCoverage": true, + "ranges": [ + { + "count": 3, + "endOffset": 2691, + "startOffset": 2664 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2779, + "startOffset": 2743 + } + ] + } + ], + "scriptId": "14", + "url": "internal/util/debuglog.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 2283, + "startOffset": 0 + } + ] + }, + { + "functionName": "sendInspectorCommand", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 453, + "startOffset": 100 + } + ] + }, + { + "functionName": "installConsoleExtensions", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1094, + "startOffset": 530 + } + ] + }, + { + "functionName": "wrapConsole", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 1984, + "startOffset": 1176 + }, + { + "count": 23, + "endOffset": 1981, + "startOffset": 1336 + }, + { + "count": 19, + "endOffset": 1855, + "startOffset": 1555 + }, + { + "count": 4, + "endOffset": 1976, + "startOffset": 1855 + } + ] + }, + { + "functionName": "get consoleFromVM", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 2217, + "startOffset": 2164 + } + ] + }, + { + "functionName": "set consoleFromVM", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 2277, + "startOffset": 2222 + } + ] + } + ], + "scriptId": "15", + "url": "internal/util/inspector.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 2, + "endOffset": 44873, + "startOffset": 0 + } + ] + }, + { + "functionName": "isPathSeparator", + "isBlockCoverage": true, + "ranges": [ + { + "count": 146987, + "endOffset": 1635, + "startOffset": 1529 + }, + { + "count": 146754, + "endOffset": 1631, + "startOffset": 1600 + } + ] + }, + { + "functionName": "isPosixPathSeparator", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1718, + "startOffset": 1639 + } + ] + }, + { + "functionName": "isWindowsDeviceRoot", + "isBlockCoverage": true, + "ranges": [ + { + "count": 5173, + "endOffset": 1895, + "startOffset": 1722 + }, + { + "count": 4882, + "endOffset": 1822, + "startOffset": 1795 + }, + { + "count": 1041, + "endOffset": 1891, + "startOffset": 1824 + }, + { + "count": 750, + "endOffset": 1890, + "startOffset": 1863 + } + ] + }, + { + "functionName": "normalizeString", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2325, + "endOffset": 3826, + "startOffset": 1961 + }, + { + "count": 133028, + "endOffset": 3808, + "startOffset": 2184 + }, + { + "count": 130703, + "endOffset": 2245, + "startOffset": 2219 + }, + { + "count": 2325, + "endOffset": 2340, + "startOffset": 2245 + }, + { + "count": 2319, + "endOffset": 2296, + "startOffset": 2290 + }, + { + "count": 6, + "endOffset": 2340, + "startOffset": 2296 + }, + { + "count": 130709, + "endOffset": 2375, + "startOffset": 2340 + }, + { + "count": 12477, + "endOffset": 3704, + "startOffset": 2375 + }, + { + "count": 11844, + "endOffset": 2421, + "startOffset": 2408 + }, + { + "count": 734, + "endOffset": 2450, + "startOffset": 2423 + }, + { + "count": 11743, + "endOffset": 3658, + "startOffset": 2450 + }, + { + "count": 33, + "endOffset": 3439, + "startOffset": 2472 + }, + { + "count": 0, + "endOffset": 2588, + "startOffset": 2529 + }, + { + "count": 0, + "endOffset": 2648, + "startOffset": 2589 + }, + { + "count": 0, + "endOffset": 2868, + "startOffset": 2789 + }, + { + "count": 0, + "endOffset": 3283, + "startOffset": 3108 + }, + { + "count": 0, + "endOffset": 3430, + "startOffset": 3294 + }, + { + "count": 11710, + "endOffset": 3658, + "startOffset": 3439 + }, + { + "count": 9499, + "endOffset": 3540, + "startOffset": 3487 + }, + { + "count": 2211, + "endOffset": 3601, + "startOffset": 3540 + }, + { + "count": 12444, + "endOffset": 3704, + "startOffset": 3658 + }, + { + "count": 118232, + "endOffset": 3803, + "startOffset": 3704 + }, + { + "count": 1666, + "endOffset": 3746, + "startOffset": 3732 + }, + { + "count": 174, + "endOffset": 3771, + "startOffset": 3748 + }, + { + "count": 118058, + "endOffset": 3803, + "startOffset": 3771 + } + ] + }, + { + "functionName": "_format", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4453, + "startOffset": 4017 + } + ] + }, + { + "functionName": "resolve", + "isBlockCoverage": true, + "ranges": [ + { + "count": 2319, + "endOffset": 9115, + "startOffset": 4579 + }, + { + "count": 3159, + "endOffset": 8648, + "startOffset": 4742 + }, + { + "count": 3157, + "endOffset": 4953, + "startOffset": 4780 + }, + { + "count": 0, + "endOffset": 4944, + "startOffset": 4911 + }, + { + "count": 2, + "endOffset": 5845, + "startOffset": 4953 + }, + { + "count": 0, + "endOffset": 5845, + "startOffset": 5033 + }, + { + "count": 0, + "endOffset": 6218, + "startOffset": 6053 + }, + { + "count": 0, + "endOffset": 7598, + "startOffset": 6251 + }, + { + "count": 3024, + "endOffset": 7689, + "startOffset": 7634 + }, + { + "count": 2319, + "endOffset": 8015, + "startOffset": 7691 + }, + { + "count": 2319, + "endOffset": 8333, + "startOffset": 8048 + }, + { + "count": 0, + "endOffset": 8270, + "startOffset": 8090 + }, + { + "count": 0, + "endOffset": 8433, + "startOffset": 8365 + }, + { + "count": 2319, + "endOffset": 8600, + "startOffset": 8572 + }, + { + "count": 2319, + "endOffset": 8632, + "startOffset": 8602 + }, + { + "count": 0, + "endOffset": 9109, + "startOffset": 9059 + } + ] + }, + { + "functionName": "normalize", + "isBlockCoverage": true, + "ranges": [ + { + "count": 6, + "endOffset": 12023, + "startOffset": 9187 + }, + { + "count": 0, + "endOffset": 9308, + "startOffset": 9297 + }, + { + "count": 0, + "endOffset": 9622, + "startOffset": 9465 + }, + { + "count": 0, + "endOffset": 11204, + "startOffset": 9655 + }, + { + "count": 5, + "endOffset": 11582, + "startOffset": 11278 + }, + { + "count": 0, + "endOffset": 11708, + "startOffset": 11697 + }, + { + "count": 0, + "endOffset": 11751, + "startOffset": 11737 + }, + { + "count": 0, + "endOffset": 11771, + "startOffset": 11760 + }, + { + "count": 0, + "endOffset": 11863, + "startOffset": 11850 + }, + { + "count": 1, + "endOffset": 11950, + "startOffset": 11895 + }, + { + "count": 0, + "endOffset": 11935, + "startOffset": 11922 + }, + { + "count": 5, + "endOffset": 11996, + "startOffset": 11950 + }, + { + "count": 0, + "endOffset": 12017, + "startOffset": 11997 + } + ] + }, + { + "functionName": "isAbsolute", + "isBlockCoverage": true, + "ranges": [ + { + "count": 317, + "endOffset": 12477, + "startOffset": 12096 + }, + { + "count": 0, + "endOffset": 12220, + "startOffset": 12207 + }, + { + "count": 316, + "endOffset": 12380, + "startOffset": 12345 + }, + { + "count": 160, + "endOffset": 12424, + "startOffset": 12381 + }, + { + "count": 116, + "endOffset": 12470, + "startOffset": 12425 + } + ] + }, + { + "functionName": "join", + "isBlockCoverage": true, + "ranges": [ + { + "count": 6, + "endOffset": 14671, + "startOffset": 12552 + }, + { + "count": 0, + "endOffset": 12614, + "startOffset": 12603 + }, + { + "count": 13, + "endOffset": 12926, + "startOffset": 12697 + }, + { + "count": 12, + "endOffset": 12910, + "startOffset": 12838 + }, + { + "count": 6, + "endOffset": 12863, + "startOffset": 12838 + }, + { + "count": 7, + "endOffset": 12910, + "startOffset": 12863 + }, + { + "count": 0, + "endOffset": 12978, + "startOffset": 12967 + }, + { + "count": 0, + "endOffset": 14271, + "startOffset": 13851 + }, + { + "count": 0, + "endOffset": 14499, + "startOffset": 14466 + }, + { + "count": 0, + "endOffset": 14620, + "startOffset": 14579 + } + ] + }, + { + "functionName": "relative", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 18564, + "startOffset": 14979 + } + ] + }, + { + "functionName": "toNamespacedPath", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1082, + "endOffset": 19630, + "startOffset": 18571 + }, + { + "count": 0, + "endOffset": 18702, + "startOffset": 18690 + }, + { + "count": 0, + "endOffset": 18759, + "startOffset": 18733 + }, + { + "count": 0, + "endOffset": 18865, + "startOffset": 18853 + }, + { + "count": 0, + "endOffset": 19300, + "startOffset": 18929 + }, + { + "count": 0, + "endOffset": 19629, + "startOffset": 19605 + } + ] + }, + { + "functionName": "dirname", + "isBlockCoverage": true, + "ranges": [ + { + "count": 496, + "endOffset": 22114, + "startOffset": 19702 + }, + { + "count": 0, + "endOffset": 19821, + "startOffset": 19810 + }, + { + "count": 0, + "endOffset": 20090, + "startOffset": 19926 + }, + { + "count": 0, + "endOffset": 21455, + "startOffset": 20153 + }, + { + "count": 0, + "endOffset": 21602, + "startOffset": 21599 + }, + { + "count": 5695, + "endOffset": 21973, + "startOffset": 21732 + }, + { + "count": 496, + "endOffset": 21871, + "startOffset": 21782 + }, + { + "count": 5199, + "endOffset": 21966, + "startOffset": 21871 + }, + { + "count": 0, + "endOffset": 22077, + "startOffset": 21997 + } + ] + }, + { + "functionName": "basename", + "isBlockCoverage": true, + "ranges": [ + { + "count": 114, + "endOffset": 24930, + "startOffset": 22214 + }, + { + "count": 0, + "endOffset": 22298, + "startOffset": 22271 + }, + { + "count": 0, + "endOffset": 22778, + "startOffset": 22761 + }, + { + "count": 0, + "endOffset": 22807, + "startOffset": 22779 + }, + { + "count": 0, + "endOffset": 24318, + "startOffset": 22809 + }, + { + "count": 1326, + "endOffset": 24848, + "startOffset": 24371 + }, + { + "count": 114, + "endOffset": 24652, + "startOffset": 24421 + }, + { + "count": 1212, + "endOffset": 24841, + "startOffset": 24652 + }, + { + "count": 114, + "endOffset": 24841, + "startOffset": 24674 + }, + { + "count": 0, + "endOffset": 24889, + "startOffset": 24879 + } + ] + }, + { + "functionName": "extname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 27073, + "startOffset": 25002 + } + ] + }, + { + "functionName": "parse", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 31903, + "startOffset": 27291 + } + ] + }, + { + "functionName": "resolve", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33025, + "startOffset": 32100 + } + ] + }, + { + "functionName": "normalize", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33694, + "startOffset": 33097 + } + ] + }, + { + "functionName": "isAbsolute", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 33899, + "startOffset": 33767 + } + ] + }, + { + "functionName": "join", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 34405, + "startOffset": 33974 + } + ] + }, + { + "functionName": "relative", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36780, + "startOffset": 34502 + } + ] + }, + { + "functionName": "toNamespacedPath", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 36866, + "startOffset": 36787 + } + ] + }, + { + "functionName": "dirname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 37590, + "startOffset": 36938 + } + ] + }, + { + "functionName": "basename", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 40083, + "startOffset": 37690 + } + ] + }, + { + "functionName": "extname", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 41863, + "startOffset": 40155 + } + ] + }, + { + "functionName": "parse", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 44518, + "startOffset": 42086 + } + ] + } + ], + "scriptId": "16", + "url": "path.js" + }, + { + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 8643, + "startOffset": 0 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1882, + "startOffset": 1855 + } + ] + }, + { + "functionName": "unenroll", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 3615, + "startOffset": 2396 + } + ] + }, + { + "functionName": "enroll", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4089, + "startOffset": 3827 + } + ] + }, + { + "functionName": "setTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 4800, + "startOffset": 4127 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5019, + "startOffset": 4881 + } + ] + }, + { + "functionName": "clearTimeout", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 5407, + "startOffset": 5028 + } + ] + }, + { + "functionName": "setInterval", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6086, + "startOffset": 5411 + } + ] + }, + { + "functionName": "clearInterval", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6393, + "startOffset": 6090 + } + ] + }, + { + "functionName": "Timeout.close", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6477, + "startOffset": 6423 + } + ] + }, + { + "functionName": "Timeout.", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 6689, + "startOffset": 6521 + } + ] + }, + { + "functionName": "setImmediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7259, + "startOffset": 6694 + } + ] + }, + { + "functionName": "get", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7482, + "startOffset": 7342 + } + ] + }, + { + "functionName": "clearImmediate", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 7977, + "startOffset": 7493 + } + ] + } + ], + "scriptId": "17", + "url": "timers.js" + } + ] + } +} diff --git a/window-maximize-regular.svg b/window-maximize-regular.svg new file mode 100644 index 000000000..7b7e5e166 --- /dev/null +++ b/window-maximize-regular.svg @@ -0,0 +1 @@ + \ No newline at end of file